summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore9091
-rw-r--r--Jenkinsfile63
-rw-r--r--boot.manifest53
-rw-r--r--exception_lists/check_rtime1
-rw-r--r--exception_lists/copyright25
-rw-r--r--exception_lists/cstyle32
-rw-r--r--exception_lists/hdrchk12
-rw-r--r--exception_lists/keywords2
-rw-r--r--exception_lists/packaging1
-rw-r--r--exception_lists/wscheck88
-rw-r--r--mancheck.conf57
-rw-r--r--manifest21671
-rw-r--r--usr/src/Makefile27
-rw-r--r--usr/src/Makefile.master18
-rw-r--r--usr/src/Makefile.master.641
-rw-r--r--usr/src/Makefile.psm.targ3
-rw-r--r--usr/src/Makefile.testarchive36
-rw-r--r--usr/src/Targetdirs35
-rw-r--r--usr/src/boot/common/console.c13
-rw-r--r--usr/src/boot/efi/loader/Makefile.com10
-rw-r--r--usr/src/boot/efi/loader/conf.c8
-rw-r--r--usr/src/boot/forth/Makefile11
-rw-r--r--usr/src/boot/forth/brand-smartos.4th38
-rw-r--r--usr/src/boot/forth/loader.4th18
-rw-r--r--usr/src/boot/forth/loader.rc10
-rw-r--r--usr/src/boot/forth/logo-smartos.4th41
-rw-r--r--usr/src/boot/forth/menu-commands.4th114
-rw-r--r--usr/src/boot/forth/menu.4th44
-rw-r--r--usr/src/boot/forth/support.4th15
-rw-r--r--usr/src/boot/forth/triton-logo.pngbin0 -> 13720 bytes
-rw-r--r--usr/src/boot/forth/triton.menu.rc109
-rw-r--r--usr/src/boot/forth/triton.pngbin0 -> 2561 bytes
-rw-r--r--usr/src/boot/i386/libi386/i386_copy.c20
-rw-r--r--usr/src/boot/i386/pmbr/pmbr.s4
-rw-r--r--usr/src/cmd/Adm/group1
-rw-r--r--usr/src/cmd/Adm/sun/Makefile29
-rw-r--r--usr/src/cmd/Adm/sun/ftpusers1
-rw-r--r--usr/src/cmd/Adm/sun/issue.in14
-rw-r--r--usr/src/cmd/Adm/sun/passwd1
-rw-r--r--usr/src/cmd/Adm/sun/shadow1
-rw-r--r--usr/src/cmd/Makefile27
-rw-r--r--usr/src/cmd/Makefile.check4
-rw-r--r--usr/src/cmd/adbgen/common/adbsub.c16
-rw-r--r--usr/src/cmd/ast/libast/amd64/FEATURE/aso28
-rw-r--r--usr/src/cmd/auditd/auditd.xml6
-rw-r--r--usr/src/cmd/auditrecord/Makefile8
-rw-r--r--usr/src/cmd/bhyve/Makefile25
-rw-r--r--usr/src/cmd/bhyve/acpi.c8
-rw-r--r--usr/src/cmd/bhyve/bhyverun.c40
-rw-r--r--usr/src/cmd/bhyve/rfb.c1
-rw-r--r--usr/src/cmd/bhyve/test/tests/mevent/lists_delete.c2
-rw-r--r--usr/src/cmd/bhyve/test/tests/mevent/testlib.c3
-rw-r--r--usr/src/cmd/bhyve/test/tests/mevent/testlib.h2
-rw-r--r--usr/src/cmd/bhyve/zhyve.c167
-rw-r--r--usr/src/cmd/cmd-crypto/etc/pkcs11.conf2
-rw-r--r--usr/src/cmd/cmd-inet/etc/services703
-rw-r--r--usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel4
-rw-r--r--usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile4
-rw-r--r--usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c37
-rw-r--r--usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h25
-rw-r--r--usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c76
-rw-r--r--usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_path.c84
-rw-r--r--usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c38
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c36
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c22
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile3
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/Makefile1
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/arp.c7
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/ndp.c9
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/route.c90
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c30
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile7
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c12
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h3
-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_rport.c9
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_svp.c557
-rw-r--r--usr/src/cmd/column/Makefile34
-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.c329
-rw-r--r--usr/src/cmd/connstat/connstat_tcp.c5
-rw-r--r--usr/src/cmd/coreadm/coreadm.c6
-rw-r--r--usr/src/cmd/coreadm/coreadm.xml8
-rw-r--r--usr/src/cmd/cron/Makefile53
-rw-r--r--usr/src/cmd/cron/cron.c154
-rw-r--r--usr/src/cmd/cron/cron.h4
-rw-r--r--usr/src/cmd/cron/crontab.c40
-rw-r--r--usr/src/cmd/cron/crontab.root18
-rw-r--r--usr/src/cmd/cron/svc-cron42
-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.c81
-rw-r--r--usr/src/cmd/devfsadm/misc_link.c44
-rw-r--r--usr/src/cmd/dispadmin/Makefile32
-rw-r--r--usr/src/cmd/dispadmin/dispadmin.conf1
-rw-r--r--usr/src/cmd/dladm/Makefile3
-rw-r--r--usr/src/cmd/dladm/dladm.c132
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_db.c122
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_door.c21
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_impl.h17
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_main.c64
-rw-r--r--usr/src/cmd/dlmgmtd/dlmgmt_util.c94
-rw-r--r--usr/src/cmd/dlstat/dlstat.c69
-rw-r--r--usr/src/cmd/dtrace/demo/Makefile10
-rw-r--r--usr/src/cmd/dtrace/test/README9
-rw-r--r--usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile2
-rw-r--r--usr/src/cmd/dtrace/test/cmd/scripts/dtest.pl2
-rw-r--r--usr/src/cmd/dtrace/test/tst/Makefile.com2
-rw-r--r--usr/src/cmd/dtrace/test/tst/common/Makefile8
-rw-r--r--usr/src/cmd/dtrace/test/tst/common/java_api/Makefile2
-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/dumpadm/Makefile8
-rw-r--r--usr/src/cmd/dumpadm/dconf.c39
-rw-r--r--usr/src/cmd/dumpadm/dconf.h2
-rw-r--r--usr/src/cmd/dumpadm/dumpadm.conf11
-rw-r--r--usr/src/cmd/dumpadm/main.c14
-rw-r--r--usr/src/cmd/dumpadm/svc-dumpadm14
-rw-r--r--usr/src/cmd/flowadm/flowadm.c50
-rw-r--r--usr/src/cmd/flowstat/flowstat.c20
-rw-r--r--usr/src/cmd/fm/fmdump/common/nvlrender.c2
-rw-r--r--usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c1
-rw-r--r--usr/src/cmd/fs.d/Makefile4
-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/mount.c11
-rw-r--r--usr/src/cmd/fs.d/nfs/lib/nfs_sec.c8
-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/lockd/lockd.c15
-rw-r--r--usr/src/cmd/fs.d/nfs/mount/Makefile13
-rw-r--r--usr/src/cmd/fs.d/nfs/mount/mount.c2
-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/init.c63
-rw-r--r--usr/src/cmd/init/init.dfl3
-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/etc/Makefile27
-rw-r--r--usr/src/cmd/ipf/etc/smartos_version1
-rw-r--r--usr/src/cmd/ipf/lib/common/printfr.c18
-rw-r--r--usr/src/cmd/ipf/svc/ipfilter20
-rw-r--r--usr/src/cmd/ipf/svc/ipfilter.xml7
-rw-r--r--usr/src/cmd/ipf/tools/Makefile.tools17
-rw-r--r--usr/src/cmd/ipf/tools/ipf_y.y17
-rw-r--r--usr/src/cmd/ipf/tools/ipfstat.c4
-rw-r--r--usr/src/cmd/ipf/tools/ipmon_y.y5
-rw-r--r--usr/src/cmd/ipf/tools/ipnat_y.y4
-rw-r--r--usr/src/cmd/ipf/tools/ippool_y.y4
-rw-r--r--usr/src/cmd/ipf/tools/lexer.c37
-rw-r--r--usr/src/cmd/ipf/tools/lexer.h5
-rw-r--r--usr/src/cmd/iscsid/iscsi-initiator20
-rw-r--r--usr/src/cmd/iscsid/iscsi-initiator.xml17
-rw-r--r--usr/src/cmd/localedef/Makefile10
-rw-r--r--usr/src/cmd/localedef/UTF-8.x114
-rw-r--r--usr/src/cmd/lofiadm/main.c27
-rw-r--r--usr/src/cmd/logadm/Makefile2
-rw-r--r--usr/src/cmd/logadm/conf.c5
-rw-r--r--usr/src/cmd/logadm/glob.c6
-rw-r--r--usr/src/cmd/logadm/logadm.conf11
-rw-r--r--usr/src/cmd/logadm/main.c24
-rw-r--r--usr/src/cmd/login/login.dfl17
-rw-r--r--usr/src/cmd/machid/Makefile80
-rw-r--r--usr/src/cmd/machid/machid.c137
-rw-r--r--usr/src/cmd/man/Makefile.com23
-rw-r--r--usr/src/cmd/mdb/Makefile.common1
-rw-r--r--usr/src/cmd/mdb/Makefile.module2
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_cmds.c43
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_ctf.c58
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_main.c11
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_modapi.h19
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_print.c98
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_tab.c24
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/Makefile.files1
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/ctxop.c3
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/genunix.c105
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/refhash.c61
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/refhash.h35
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/zone.c46
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/zone.h1
-rw-r--r--usr/src/cmd/mdb/common/modules/ipc/ipc.c5
-rw-r--r--usr/src/cmd/mdb/common/modules/libc/libc.c28
-rw-r--r--usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c67
-rw-r--r--usr/src/cmd/mdb/common/modules/random/random.c16
-rw-r--r--usr/src/cmd/mdb/common/modules/xhci/xhci.c893
-rw-r--r--usr/src/cmd/mdb/intel/amd64/libpython/Makefile1
-rw-r--r--usr/src/cmd/mdb/intel/amd64/libumem/Makefile1
-rw-r--r--usr/src/cmd/mdb/intel/amd64/xhci/Makefile27
-rw-r--r--usr/src/cmd/mdb/intel/ia32/libumem/Makefile1
-rw-r--r--usr/src/cmd/mdb/intel/ia32/xhci/Makefile25
-rw-r--r--usr/src/cmd/netfiles/Makefile4
-rw-r--r--usr/src/cmd/netfiles/nsswitch.conf4
-rw-r--r--usr/src/cmd/netfiles/resolv.conf0
-rw-r--r--usr/src/cmd/nicstat/Makefile31
-rw-r--r--usr/src/cmd/nicstat/nicstat.pl424
-rw-r--r--usr/src/cmd/nsadmin/Makefile93
-rwxr-xr-xusr/src/cmd/nsadmin/bash/bash_completion9417
-rw-r--r--usr/src/cmd/nsadmin/bash/bash_completion.d/dladm34
-rw-r--r--usr/src/cmd/nsadmin/bash/bash_completion.d/machines19
-rw-r--r--usr/src/cmd/nsadmin/bash/bash_completion.d/vms62
-rw-r--r--usr/src/cmd/nsadmin/bash/bash_completion.d/zone_alias42
-rw-r--r--usr/src/cmd/nsadmin/bash/bash_completion.d/zones50
-rw-r--r--usr/src/cmd/nsadmin/bashrc.sh5
-rw-r--r--usr/src/cmd/nsadmin/dot-bash_profile.sh12
-rw-r--r--usr/src/cmd/nsadmin/dot-bashrc.sh57
-rw-r--r--usr/src/cmd/nsadmin/dot-profile.sh25
-rw-r--r--usr/src/cmd/nsadmin/etc-profile.sh18
-rw-r--r--usr/src/cmd/nsadmin/etc-skel-bashrc.sh7
-rw-r--r--usr/src/cmd/nsadmin/system55
-rw-r--r--usr/src/cmd/nsadmin/zshrc31
-rw-r--r--usr/src/cmd/nscd/Makefile1
-rw-r--r--usr/src/cmd/passwd/Makefile2
-rw-r--r--usr/src/cmd/pgrep/Makefile9
-rw-r--r--usr/src/cmd/pgrep/idtab.c7
-rw-r--r--usr/src/cmd/pgrep/pgrep.c112
-rw-r--r--usr/src/cmd/pgrep/psexp.c14
-rw-r--r--usr/src/cmd/pgrep/psexp.h15
-rw-r--r--usr/src/cmd/prstat/prstat.c40
-rw-r--r--usr/src/cmd/prstat/prstat.h1
-rw-r--r--usr/src/cmd/prtconf/prtconf.c5
-rw-r--r--usr/src/cmd/ps/ps.c201
-rw-r--r--usr/src/cmd/ps/ucbps.c219
-rw-r--r--usr/src/cmd/ptools/Makefile.bld37
-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.c3
-rw-r--r--usr/src/cmd/ptools/pfiles/pfiles.c11
-rw-r--r--usr/src/cmd/ptools/pflags/pflags.c5
-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.c9
-rw-r--r--usr/src/cmd/ptools/ptime/ptime.c7
-rw-r--r--usr/src/cmd/ptools/pwait/pwait.c7
-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/Makefile4
-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/Makefile4
-rw-r--r--usr/src/cmd/rcap/rcapstat/rcapstat.c7
-rw-r--r--usr/src/cmd/savecore/Makefile.com13
-rw-r--r--usr/src/cmd/savecore/savecore.c172
-rw-r--r--usr/src/cmd/sed/main.c27
-rw-r--r--usr/src/cmd/sendmail/src/Makefile3
-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/include/conv.h2
-rw-r--r--usr/src/cmd/sgs/lex/common/main.c45
-rw-r--r--usr/src/cmd/sgs/lex/common/once.h10
-rw-r--r--usr/src/cmd/sgs/libconv/common/corenote.c15
-rw-r--r--usr/src/cmd/sgs/libconv/common/corenote.msg7
-rw-r--r--usr/src/cmd/sgs/libconv/common/phdr.c46
-rw-r--r--usr/src/cmd/sgs/libconv/common/phdr.msg30
-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.c63
-rw-r--r--usr/src/cmd/sgs/rtld/common/globals.c3
-rw-r--r--usr/src/cmd/sgs/rtld/common/rtld.msg6
-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/common/dextern.h17
-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/smbios/Makefile4
-rw-r--r--usr/src/cmd/smbios/smbios.c26
-rw-r--r--usr/src/cmd/ssh/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/cmd/ssh/doc/LICENCE194
-rw-r--r--usr/src/cmd/ssh/etc/ssh.xml177
-rw-r--r--usr/src/cmd/ssh/etc/ssh_config31
-rw-r--r--usr/src/cmd/ssh/etc/sshd127
-rw-r--r--usr/src/cmd/ssh/etc/sshd_config145
-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/common/notify_params.c2
-rw-r--r--usr/src/cmd/svc/configd/backend.c20
-rw-r--r--usr/src/cmd/svc/configd/rc_node.c3
-rw-r--r--usr/src/cmd/svc/milestone/Makefile19
-rw-r--r--usr/src/cmd/svc/milestone/console-login14
-rwxr-xr-xusr/src/cmd/svc/milestone/fs-joyent312
-rw-r--r--usr/src/cmd/svc/milestone/fs-root98
-rw-r--r--usr/src/cmd/svc/milestone/fs-usr162
-rw-r--r--usr/src/cmd/svc/milestone/identity-node150
-rw-r--r--usr/src/cmd/svc/milestone/joyent-fs.xml93
-rw-r--r--usr/src/cmd/svc/milestone/make-console-login-xml39
-rw-r--r--usr/src/cmd/svc/milestone/manifest-import31
-rwxr-xr-xusr/src/cmd/svc/milestone/mdata-execute53
-rwxr-xr-xusr/src/cmd/svc/milestone/mdata-fetch477
-rw-r--r--usr/src/cmd/svc/milestone/mdata.xml39
-rw-r--r--usr/src/cmd/svc/milestone/minimal-fs.xml4
-rw-r--r--usr/src/cmd/svc/milestone/net-early-admin260
-rw-r--r--usr/src/cmd/svc/milestone/net-physical1293
-rw-r--r--usr/src/cmd/svc/milestone/net-routing-setup35
-rw-r--r--usr/src/cmd/svc/milestone/network-early-admin.xml73
-rw-r--r--usr/src/cmd/svc/milestone/network-location.xml8
-rw-r--r--usr/src/cmd/svc/milestone/network-physical.xml102
-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
-rwxr-xr-xusr/src/cmd/svc/milestone/smartdc-config211
-rw-r--r--usr/src/cmd/svc/milestone/smartdc-config.xml145
-rwxr-xr-xusr/src/cmd/svc/milestone/smartdc-init246
-rw-r--r--usr/src/cmd/svc/milestone/smartdc-init.xml124
-rwxr-xr-xusr/src/cmd/svc/milestone/smartdc-ur79
-rw-r--r--usr/src/cmd/svc/milestone/smartdc-ur.xml62
-rwxr-xr-xusr/src/cmd/svc/milestone/sysidtool-net5
-rwxr-xr-xusr/src/cmd/svc/milestone/sysidtool-system5
-rw-r--r--usr/src/cmd/svc/profile/Makefile6
-rw-r--r--usr/src/cmd/svc/profile/generic.xml397
-rw-r--r--usr/src/cmd/svc/shell/mfsthistory1
-rw-r--r--usr/src/cmd/svc/shell/smf_include.sh1
-rw-r--r--usr/src/cmd/svc/startd/graph.c27
-rw-r--r--usr/src/cmd/svc/startd/method.c75
-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/Makefile10
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_libscf.c4
-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.c84
-rw-r--r--usr/src/cmd/syslogd/syslog.conf4
-rw-r--r--usr/src/cmd/syslogd/system-log101
-rw-r--r--usr/src/cmd/tail/Makefile5
-rw-r--r--usr/src/cmd/truss/codes.c5
-rw-r--r--usr/src/cmd/truss/print.c23
-rw-r--r--usr/src/cmd/truss/systable.c8
-rw-r--r--usr/src/cmd/varpd/Makefile3
-rw-r--r--usr/src/cmd/varpd/varpd.c2
-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/Makefile67
-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.ksh298
-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.c262
-rw-r--r--usr/src/cmd/zlogin/zlogin.c590
-rw-r--r--usr/src/cmd/zoneadm/Makefile11
-rw-r--r--usr/src/cmd/zoneadm/svc-resource-mgmt14
-rw-r--r--usr/src/cmd/zoneadm/svc-zones139
-rw-r--r--usr/src/cmd/zoneadm/zfs.c16
-rw-r--r--usr/src/cmd/zoneadm/zoneadm.c490
-rw-r--r--usr/src/cmd/zoneadm/zones.xml23
-rw-r--r--usr/src/cmd/zoneadmd/Makefile38
-rw-r--r--usr/src/cmd/zoneadmd/Makefile.com72
-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/log.c1027
-rw-r--r--usr/src/cmd/zoneadmd/vplat.c684
-rw-r--r--usr/src/cmd/zoneadmd/zcons.c189
-rw-r--r--usr/src/cmd/zoneadmd/zfd.c1238
-rw-r--r--usr/src/cmd/zoneadmd/zoneadmd.c984
-rw-r--r--usr/src/cmd/zoneadmd/zoneadmd.h44
-rw-r--r--usr/src/cmd/zonecfg/Makefile4
-rw-r--r--usr/src/cmd/zonecfg/zonecfg.c918
-rw-r--r--usr/src/cmd/zonecfg/zonecfg.h17
-rw-r--r--usr/src/cmd/zonecfg/zonecfg_grammar.y91
-rw-r--r--usr/src/cmd/zonecfg/zonecfg_lex.l31
-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.c3
-rw-r--r--usr/src/common/brand/lx/lx_auxv.c96
-rw-r--r--usr/src/common/brand/lx/lx_auxv.h32
-rw-r--r--usr/src/common/brand/lx/lx_errno.c206
-rw-r--r--usr/src/common/brand/lx/lx_errno.h29
-rw-r--r--usr/src/common/brand/lx/lx_signum.c339
-rw-r--r--usr/src/common/brand/lx/lx_signum.h114
-rw-r--r--usr/src/common/brand/lx/lx_syscall.h123
-rw-r--r--usr/src/common/brand/lx/tools/Makefile47
-rw-r--r--usr/src/common/brand/lx/tools/README.md39
-rw-r--r--usr/src/common/brand/lx/tools/gen_errno.c444
-rw-r--r--usr/src/common/crypto/chacha/chacha.c24
-rw-r--r--usr/src/common/crypto/chacha/chacha.h6
-rw-r--r--usr/src/common/net/dhcp/octet.c3
-rw-r--r--usr/src/common/pnglite/pnglite.c1
-rw-r--r--usr/src/common/refhash/refhash.c231
-rw-r--r--usr/src/common/zfs/zfs_prop.c17
-rw-r--r--usr/src/common/zfs/zfs_prop.h2
-rw-r--r--usr/src/grub/Makefile6
-rw-r--r--usr/src/grub/Makefile.grub1
-rw-r--r--usr/src/grub/grub-0.97/stage2/boot.c13
-rw-r--r--usr/src/grub/grub-0.97/stage2/cmdline.c23
-rw-r--r--usr/src/head/Makefile6
-rw-r--r--usr/src/head/libzonecfg.h63
-rw-r--r--usr/src/head/regexp.h12
-rw-r--r--usr/src/head/resolv_joy.h472
-rw-r--r--usr/src/head/zdoor.h74
-rw-r--r--usr/src/head/zone.h9
-rw-r--r--usr/src/lib/Makefile27
-rw-r--r--usr/src/lib/Makefile.lib23
-rw-r--r--usr/src/lib/Makefile.targ2
-rw-r--r--usr/src/lib/Makefile.usdt28
-rw-r--r--usr/src/lib/brand/Makefile13
-rw-r--r--usr/src/lib/brand/bhyve/Makefile32
-rw-r--r--usr/src/lib/brand/bhyve/Makefile.bhyve17
-rw-r--r--usr/src/lib/brand/bhyve/zone/Makefile45
-rw-r--r--usr/src/lib/brand/bhyve/zone/SYSbhyve.xml9
-rwxr-xr-xusr/src/lib/brand/bhyve/zone/attach26
-rw-r--r--usr/src/lib/brand/bhyve/zone/bhhwcompat.c98
-rw-r--r--usr/src/lib/brand/bhyve/zone/boot.c926
-rw-r--r--usr/src/lib/brand/bhyve/zone/config.xml69
-rwxr-xr-xusr/src/lib/brand/bhyve/zone/detach19
-rw-r--r--usr/src/lib/brand/bhyve/zone/platform.xml68
-rw-r--r--usr/src/lib/brand/bhyve/zone/statechange22
-rwxr-xr-xusr/src/lib/brand/bhyve/zone/uninstall23
-rw-r--r--usr/src/lib/brand/ipkg/zone/Makefile4
-rw-r--r--usr/src/lib/brand/jcommon/Makefile28
-rw-r--r--usr/src/lib/brand/jcommon/README.smf107
-rw-r--r--usr/src/lib/brand/jcommon/cattach63
-rw-r--r--usr/src/lib/brand/jcommon/cdetach50
-rw-r--r--usr/src/lib/brand/jcommon/cinstall183
-rw-r--r--usr/src/lib/brand/jcommon/cuninstall74
-rw-r--r--usr/src/lib/brand/jcommon/libhooks.ksh86
-rw-r--r--usr/src/lib/brand/jcommon/poststate34
-rw-r--r--usr/src/lib/brand/jcommon/prestate34
-rwxr-xr-xusr/src/lib/brand/jcommon/query52
-rw-r--r--usr/src/lib/brand/jcommon/statechange906
-rw-r--r--usr/src/lib/brand/joyent-minimal/Makefile31
-rw-r--r--usr/src/lib/brand/joyent-minimal/zone/Makefile37
-rw-r--r--usr/src/lib/brand/joyent-minimal/zone/config.xml115
-rwxr-xr-xusr/src/lib/brand/joyent-minimal/zone/jattach26
-rwxr-xr-xusr/src/lib/brand/joyent-minimal/zone/jdetach19
-rwxr-xr-xusr/src/lib/brand/joyent-minimal/zone/jinstall32
-rwxr-xr-xusr/src/lib/brand/joyent-minimal/zone/juninstall23
-rw-r--r--usr/src/lib/brand/joyent-minimal/zone/manifests126
-rw-r--r--usr/src/lib/brand/joyent-minimal/zone/platform.xml172
-rwxr-xr-xusr/src/lib/brand/joyent-minimal/zone/poststate19
-rwxr-xr-xusr/src/lib/brand/joyent-minimal/zone/prestate19
-rwxr-xr-xusr/src/lib/brand/joyent-minimal/zone/statechange42
-rw-r--r--usr/src/lib/brand/joyent/Makefile31
-rw-r--r--usr/src/lib/brand/joyent/zone/Joyent.xml32
-rw-r--r--usr/src/lib/brand/joyent/zone/Makefile39
-rw-r--r--usr/src/lib/brand/joyent/zone/SUNWdefault.xml (renamed from usr/src/lib/brand/ipkg/zone/SUNWdefault.xml)2
-rw-r--r--usr/src/lib/brand/joyent/zone/config.xml115
-rwxr-xr-xusr/src/lib/brand/joyent/zone/jattach26
-rwxr-xr-xusr/src/lib/brand/joyent/zone/jdetach19
-rwxr-xr-xusr/src/lib/brand/joyent/zone/jinstall31
-rwxr-xr-xusr/src/lib/brand/joyent/zone/juninstall23
-rw-r--r--usr/src/lib/brand/joyent/zone/manifests175
-rw-r--r--usr/src/lib/brand/joyent/zone/platform.xml169
-rwxr-xr-xusr/src/lib/brand/joyent/zone/poststate19
-rwxr-xr-xusr/src/lib/brand/joyent/zone/prestate19
-rwxr-xr-xusr/src/lib/brand/joyent/zone/statechange42
-rw-r--r--usr/src/lib/brand/kvm/Makefile31
-rw-r--r--usr/src/lib/brand/kvm/zone/Makefile35
-rw-r--r--usr/src/lib/brand/kvm/zone/config.xml77
-rwxr-xr-xusr/src/lib/brand/kvm/zone/kattach26
-rwxr-xr-xusr/src/lib/brand/kvm/zone/kdetach19
-rwxr-xr-xusr/src/lib/brand/kvm/zone/kinstall31
-rwxr-xr-xusr/src/lib/brand/kvm/zone/kuninstall23
-rw-r--r--usr/src/lib/brand/kvm/zone/platform.xml145
-rwxr-xr-xusr/src/lib/brand/kvm/zone/poststate19
-rwxr-xr-xusr/src/lib/brand/kvm/zone/prestate19
-rwxr-xr-xusr/src/lib/brand/kvm/zone/statechange36
-rw-r--r--usr/src/lib/brand/lx/Makefile55
-rw-r--r--usr/src/lib/brand/lx/Makefile.lx34
-rw-r--r--usr/src/lib/brand/lx/librtld_db/Makefile (renamed from usr/src/cmd/ssh/Makefile)34
-rw-r--r--usr/src/lib/brand/lx/librtld_db/Makefile.com84
-rw-r--r--usr/src/lib/brand/lx/librtld_db/amd64/Makefile36
-rw-r--r--usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c853
-rw-r--r--usr/src/lib/brand/lx/librtld_db/i386/Makefile33
-rw-r--r--usr/src/lib/brand/lx/lx_brand/Makefile49
-rw-r--r--usr/src/lib/brand/lx/lx_brand/Makefile.com103
-rw-r--r--usr/src/lib/brand/lx/lx_brand/amd64/Makefile42
-rw-r--r--usr/src/lib/brand/lx/lx_brand/amd64/lx_crt.s62
-rw-r--r--usr/src/lib/brand/lx/lx_brand/amd64/lx_handler.s53
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/capabilities.c516
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/clock.c192
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/clone.c724
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/debug.c171
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/dir.c82
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/file.c347
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/fork.c186
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/lx_brand.c1709
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/lx_provider.d39
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/mapfile-vers50
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/misc.c359
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/module.c90
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/mount.c540
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/mount_nfs.c1506
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/ptrace.c105
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/sendfile.c168
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/signal.c2431
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/stack.c337
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/statfs.c348
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/sysctl.c137
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c987
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/time.c113
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/truncate.c173
-rw-r--r--usr/src/lib/brand/lx/lx_brand/i386/Makefile48
-rw-r--r--usr/src/lib/brand/lx/lx_brand/i386/lx_crt.s65
-rw-r--r--usr/src/lib/brand/lx/lx_brand/i386/lx_handler.s91
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h54
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h179
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_mount.h163
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_poll.h65
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h289
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_sigstack.h78
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_socket.h147
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_statfs.h85
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h194
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_sysv_ipc.h222
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h80
-rw-r--r--usr/src/lib/brand/lx/lx_init/Makefile74
-rw-r--r--usr/src/lib/brand/lx/lx_init/lxinit.c1025
-rw-r--r--usr/src/lib/brand/lx/lx_init/pipe_stream.c326
-rw-r--r--usr/src/lib/brand/lx/lx_init/pipe_stream.h48
-rw-r--r--usr/src/lib/brand/lx/lx_init/run_command.c282
-rw-r--r--usr/src/lib/brand/lx/lx_init/run_command.h32
-rw-r--r--usr/src/lib/brand/lx/lx_lockd/Makefile65
-rw-r--r--usr/src/lib/brand/lx/lx_lockd/lockd.c568
-rw-r--r--usr/src/lib/brand/lx/lx_lockd/nfs_tbind.c1832
-rw-r--r--usr/src/lib/brand/lx/lx_support/Makefile (renamed from usr/src/cmd/ssh/etc/Makefile)45
-rw-r--r--usr/src/lib/brand/lx/lx_support/lx_support.c392
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/Makefile39
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/Makefile.com85
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/amd64/Makefile42
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/amd64/vdso_subr.s131
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/common/mapfile-vers59
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/common/vdso_defs.h42
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/common/vdso_main.c139
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/i386/Makefile43
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/i386/vdso_subr.s92
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/tools/Makefile45
-rw-r--r--usr/src/lib/brand/lx/lx_vdso/tools/vdso_tool.c456
-rw-r--r--usr/src/lib/brand/lx/netfiles/Makefile47
-rw-r--r--usr/src/lib/brand/lx/testing/Makefile36
-rw-r--r--usr/src/lib/brand/lx/testing/Readme_ltp295
-rw-r--r--usr/src/lib/brand/lx/testing/ltp_skiplist272
-rw-r--r--usr/src/lib/brand/lx/testing/ltp_tests1
-rw-r--r--usr/src/lib/brand/lx/testing/pts_ignorelist366
-rw-r--r--usr/src/lib/brand/lx/zone/Makefile72
-rw-r--r--usr/src/lib/brand/lx/zone/SUNWlx.xml34
-rw-r--r--usr/src/lib/brand/lx/zone/SUNWlx26.xml35
-rw-r--r--usr/src/lib/brand/lx/zone/config.xml110
-rw-r--r--usr/src/lib/brand/lx/zone/lx_boot.ksh106
-rw-r--r--usr/src/lib/brand/lx/zone/lx_boot_zone_busybox.ksh182
-rw-r--r--usr/src/lib/brand/lx/zone/lx_boot_zone_debian.ksh175
-rwxr-xr-xusr/src/lib/brand/lx/zone/lx_boot_zone_docker.ksh17
-rw-r--r--usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh357
-rw-r--r--usr/src/lib/brand/lx/zone/lx_boot_zone_suse.ksh213
-rw-r--r--usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu.ksh142
-rwxr-xr-xusr/src/lib/brand/lx/zone/lx_boot_zone_void.ksh17
-rw-r--r--usr/src/lib/brand/lx/zone/lx_install.ksh62
-rwxr-xr-xusr/src/lib/brand/lx/zone/lx_uninstall.ksh23
-rw-r--r--usr/src/lib/brand/lx/zone/platform.xml163
-rwxr-xr-xusr/src/lib/brand/lx/zone/poststate.ksh19
-rwxr-xr-xusr/src/lib/brand/lx/zone/prestate.ksh19
-rwxr-xr-xusr/src/lib/brand/lx/zone/statechange.ksh41
-rw-r--r--usr/src/lib/brand/shared/zone/common.ksh98
-rw-r--r--usr/src/lib/brand/shared/zone/uninstall.ksh50
-rw-r--r--usr/src/lib/fm/libfmd_adm/common/fmd_adm.c6
-rw-r--r--usr/src/lib/fm/libfmd_snmp/Makefile.com2
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/libtopo.h2
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/mapfile-vers4
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_hc.h21
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_mod.h2
-rw-r--r--usr/src/lib/fm/topo/libtopo/common/topo_string.c59
-rwxr-xr-x[-rw-r--r--]usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Joyent-Compute-Platform-1101-disk-hc-topology.xmlgenksh0
-rw-r--r--usr/src/lib/fm/topo/maps/Joyent,Joyent-Storage-Platform-7001/Joyent-Storage-Platform-7001-slot-hc-topology.xml4
-rw-r--r--usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-slot-hc-topology.xml4
-rw-r--r--usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml5
-rw-r--r--usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c65
-rw-r--r--usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c51
-rw-r--r--usr/src/lib/fm/topo/modules/common/ipmi/Makefile5
-rw-r--r--usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c297
-rw-r--r--usr/src/lib/fm/topo/modules/common/ses/ses.c43
-rw-r--r--usr/src/lib/json_nvlist/json_nvlist.c2
-rw-r--r--usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com3
-rw-r--r--usr/src/lib/libbe/common/be_list.c1
-rw-r--r--usr/src/lib/libbrand/common/libbrand.c15
-rw-r--r--usr/src/lib/libbsm/common/adt.c102
-rw-r--r--usr/src/lib/libbunyan/Makefile42
-rw-r--r--usr/src/lib/libbunyan/Makefile.com36
-rw-r--r--usr/src/lib/libbunyan/amd64/Makefile19
-rw-r--r--usr/src/lib/libbunyan/common/bunyan.c913
-rw-r--r--usr/src/lib/libbunyan/common/bunyan.h88
-rw-r--r--usr/src/lib/libbunyan/common/bunyan_provider.d32
-rw-r--r--usr/src/lib/libbunyan/common/llib-lbunyan19
-rw-r--r--usr/src/lib/libbunyan/common/mapfile-vers53
-rw-r--r--usr/src/lib/libbunyan/i386/Makefile18
-rw-r--r--usr/src/lib/libbunyan/sparc/Makefile18
-rw-r--r--usr/src/lib/libbunyan/sparcv9/Makefile19
-rw-r--r--usr/src/lib/libc/Makefile4
-rw-r--r--usr/src/lib/libc/Makefile.targ21
-rw-r--r--usr/src/lib/libc/amd64/Makefile14
-rw-r--r--usr/src/lib/libc/amd64/gen/siginfolst.c2
-rw-r--r--usr/src/lib/libc/i386/Makefile.com17
-rw-r--r--usr/src/lib/libc/i386/gen/siginfolst.c2
-rw-r--r--usr/src/lib/libc/inc/thr_inlines.h12
-rw-r--r--usr/src/lib/libc/inc/thr_uberdata.h5
-rw-r--r--usr/src/lib/libc/port/gen/arc4random.c4
-rw-r--r--usr/src/lib/libc/port/gen/getauxv.c15
-rw-r--r--usr/src/lib/libc/port/gen/sh_locks.c2
-rw-r--r--usr/src/lib/libc/port/gen/siglist.c2
-rw-r--r--usr/src/lib/libc/port/gen/str2sig.c3
-rw-r--r--usr/src/lib/libc/port/mapfile-vers7
-rw-r--r--usr/src/lib/libc/port/stdio/system.c11
-rw-r--r--usr/src/lib/libc/port/sys/inotify.c142
-rw-r--r--usr/src/lib/libc/port/sys/zone.c13
-rw-r--r--usr/src/lib/libc/port/threads/sigaction.c74
-rw-r--r--usr/src/lib/libc/port/threads/thr.c13
-rw-r--r--usr/src/lib/libc/sparc/Makefile.com3
-rw-r--r--usr/src/lib/libc/sparc/gen/siginfolst.c2
-rw-r--r--usr/src/lib/libc/sparcv9/Makefile.com3
-rw-r--r--usr/src/lib/libc/sparcv9/gen/siginfolst.c2
-rw-r--r--usr/src/lib/libctf/common/ctf_convert.c2
-rw-r--r--usr/src/lib/libctf/common/ctf_lib.c22
-rw-r--r--usr/src/lib/libctf/common/libctf.h2
-rw-r--r--usr/src/lib/libctf/common/mapfile-vers1
-rw-r--r--usr/src/lib/libcurses/screen/setupterm.c7
-rw-r--r--usr/src/lib/libdevid/Makefile.com2
-rw-r--r--usr/src/lib/libdhcpagent/common/dhcpagent_util.c9
-rw-r--r--usr/src/lib/libdiskmgt/common/findevs.c6
-rw-r--r--usr/src/lib/libdiskmgt/common/libdiskmgt.h2
-rw-r--r--usr/src/lib/libdladm/Makefile1
-rw-r--r--usr/src/lib/libdladm/Makefile.com2
-rw-r--r--usr/src/lib/libdladm/common/libdladm.c40
-rw-r--r--usr/src/lib/libdladm/common/libdladm.h9
-rw-r--r--usr/src/lib/libdladm/common/libdladm_impl.h10
-rw-r--r--usr/src/lib/libdladm/common/libdllink.c28
-rw-r--r--usr/src/lib/libdladm/common/libdllink.h6
-rw-r--r--usr/src/lib/libdladm/common/libdlmgmt.c25
-rw-r--r--usr/src/lib/libdladm/common/libdlvnic.c27
-rw-r--r--usr/src/lib/libdladm/common/linkprop.c118
-rw-r--r--usr/src/lib/libdladm/common/mapfile-vers1
-rw-r--r--usr/src/lib/libdlpi/common/libdlpi.c71
-rw-r--r--usr/src/lib/libdlpi/common/libdlpi.h2
-rw-r--r--usr/src/lib/libdlpi/common/libdlpi_impl.h3
-rw-r--r--usr/src/lib/libdlpi/common/mapfile-vers5
-rw-r--r--usr/src/lib/libdtrace/Makefile.com10
-rw-r--r--usr/src/lib/libdtrace/common/dt_impl.h1
-rw-r--r--usr/src/lib/libdtrace/common/dt_module.c92
-rw-r--r--usr/src/lib/libdtrace/common/dt_open.c27
-rw-r--r--usr/src/lib/libdtrace/common/dt_options.c73
-rw-r--r--usr/src/lib/libdtrace/common/dt_program.c9
-rw-r--r--usr/src/lib/libdtrace/common/dt_work.c32
-rw-r--r--usr/src/lib/libdtrace/common/dtrace.h1
-rw-r--r--usr/src/lib/libdtrace/common/mac.d.in66
-rw-r--r--usr/src/lib/libdtrace/common/mac.sed.in45
-rw-r--r--usr/src/lib/libdtrace/common/vnd.d28
-rw-r--r--usr/src/lib/libefi/common/crc32_efi.c5
-rw-r--r--usr/src/lib/libefi/common/rdwr_efi.c112
-rw-r--r--usr/src/lib/libfakekernel/common/kstat.c6
-rw-r--r--usr/src/lib/libfakekernel/common/mapfile-vers2
-rw-r--r--usr/src/lib/libfakekernel/common/taskq.c1
-rw-r--r--usr/src/lib/libgrubmgmt/common/libgrub_fs.c2
-rw-r--r--usr/src/lib/libidspace/Makefile.com1
-rw-r--r--usr/src/lib/libidspace/common/mapfile-vers10
-rw-r--r--usr/src/lib/libinetutil/common/mapfile-vers1
-rw-r--r--usr/src/lib/libipadm/common/libipadm.c27
-rw-r--r--usr/src/lib/libipmi/common/ipmi_lancfg.c245
-rw-r--r--usr/src/lib/libipmi/common/libipmi.h12
-rw-r--r--usr/src/lib/libjedec/common/llib-ljedec19
-rw-r--r--usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com6
-rw-r--r--usr/src/lib/libnisdb/db_mindex3.cc4
-rw-r--r--usr/src/lib/libnisdb/db_table.cc4
-rw-r--r--usr/src/lib/libnisdb/nis_db.cc8
-rw-r--r--usr/src/lib/libnsl/common/mapfile-vers1
-rw-r--r--usr/src/lib/libnsl/netselect/netselect.c27
-rw-r--r--usr/src/lib/libnvpair/libnvpair.h2
-rw-r--r--usr/src/lib/libnvpair/mapfile-vers2
-rw-r--r--usr/src/lib/libnvpair/nvpair_json.c284
-rw-r--r--usr/src/lib/libppt/Makefile13
-rw-r--r--usr/src/lib/libppt/common/llib-lppt19
-rw-r--r--usr/src/lib/libppt/ppt_matches1
-rw-r--r--usr/src/lib/libproc/common/Pcontrol.c11
-rw-r--r--usr/src/lib/libproc/common/Pcontrol.h1
-rw-r--r--usr/src/lib/libproc/common/Psymtab.c64
-rw-r--r--usr/src/lib/librefhash/Makefile41
-rw-r--r--usr/src/lib/librefhash/Makefile.com43
-rw-r--r--usr/src/lib/librefhash/amd64/Makefile19
-rw-r--r--usr/src/lib/librefhash/common/mapfile-vers48
-rw-r--r--usr/src/lib/librefhash/i386/Makefile18
-rw-r--r--usr/src/lib/librefhash/sparc/Makefile18
-rw-r--r--usr/src/lib/librefhash/sparcv9/Makefile19
-rw-r--r--usr/src/lib/libresolv2_joy/Makefile76
-rw-r--r--usr/src/lib/libresolv2_joy/Makefile.com165
-rw-r--r--usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE185
-rw-r--r--usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/lib/libresolv2_joy/amd64/Makefile31
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/daemon.c81
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/ftruncate.c60
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/gettimeofday.c58
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/mktemp.c151
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/putenv.c23
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/readv.c35
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/setenv.c146
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/setitimer.c25
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/strcasecmp.c119
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/strdup.c20
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/strerror.c89
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/strpbrk.c65
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/strsep.c83
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/strtoul.c114
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/utimes.c40
-rw-r--r--usr/src/lib/libresolv2_joy/common/bsd/writev.c85
-rw-r--r--usr/src/lib/libresolv2_joy/common/dst/dst_api.c1044
-rw-r--r--usr/src/lib/libresolv2_joy/common/dst/dst_internal.h155
-rw-r--r--usr/src/lib/libresolv2_joy/common/dst/hmac_link.c488
-rw-r--r--usr/src/lib/libresolv2_joy/common/dst/support.c339
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/inet_cidr_ntop.c263
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/inet_cidr_pton.c277
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/inet_data.c46
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/inet_lnaof.c65
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/inet_makeaddr.c68
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/inet_net_ntop.c279
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/inet_net_pton.c407
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/inet_neta.c89
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/inet_netof.c64
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/inet_network.c106
-rw-r--r--usr/src/lib/libresolv2_joy/common/inet/nsap_addr.c111
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/dns.c150
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/dns_ho.c1139
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/dns_nw.c587
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/dns_p.h52
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/dns_pr.c264
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/dns_sv.c296
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/gai_strerror.c105
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/gen.c455
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/gen_ho.c387
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/gen_ng.c170
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/gen_nw.c260
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/gen_p.h113
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/gen_pr.c224
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/gen_sv.c225
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/getaddrinfo.c1253
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/gethostent.c1092
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/gethostent_r.c274
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/getnameinfo.c334
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/getnetent.c341
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/getnetent_r.c230
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/getnetgrent.c156
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/getnetgrent_r.c215
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/getprotoent.c172
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/getprotoent_r.c219
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/getservent.c175
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/getservent_r.c238
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/hesiod.c501
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/hesiod_p.h48
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irp.c577
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irp_ho.c401
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irp_ng.c249
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irp_nw.c340
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irp_p.h60
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irp_pr.c321
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irp_sv.c340
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irpmarshall.c2297
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irs_data.c250
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irs_data.h63
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/irs_p.h51
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/lcl.c136
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/lcl_ho.c574
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/lcl_ng.c442
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/lcl_nw.c370
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/lcl_p.h51
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/lcl_pr.c288
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/lcl_sv.c426
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/nis.c150
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/nis_p.h47
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/nul_ng.c123
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/pathnames.h52
-rw-r--r--usr/src/lib/libresolv2_joy/common/irs/util.c105
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/assertions.c90
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/base64.c329
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/bitncmp.c64
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/ctl_clnt.c614
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/ctl_p.c184
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/ctl_p.h28
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/ctl_srvr.c781
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/ev_connects.c363
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/ev_files.c273
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/ev_streams.c304
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/ev_timers.c493
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/ev_waits.c243
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/eventlib.c932
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/eventlib_p.h281
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/heap.c232
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/hex.c119
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/logging.c712
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/logging_p.h61
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/memcluster.c583
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/movefile.c43
-rw-r--r--usr/src/lib/libresolv2_joy/common/isc/tree.c530
-rw-r--r--usr/src/lib/libresolv2_joy/common/llib-lresolv_joy59
-rw-r--r--usr/src/lib/libresolv2_joy/common/mapfile-vers60
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_date.c123
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_name.c1149
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_netint.c55
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_newmsg.c269
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_parse.c269
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_print.c1239
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_rdata.c296
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_samedomain.c203
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_sign.c381
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_ttl.c161
-rw-r--r--usr/src/lib/libresolv2_joy/common/nameser/ns_verify.c478
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/herror.c124
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/mtctxres.c135
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_comp.c282
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_data.c318
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_debug.c1247
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_debug.h35
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_findzonecut.c718
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_init.c953
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_mkquery.c381
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.c1159
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.h25
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_private.h22
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_query.c435
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_send.c1115
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_sendsigned.c170
-rw-r--r--usr/src/lib/libresolv2_joy/common/resolv/res_update.c209
-rw-r--r--usr/src/lib/libresolv2_joy/common/sunw/sunw_mtctxres.c126
-rw-r--r--usr/src/lib/libresolv2_joy/common/sunw/sunw_updrec.c249
-rw-r--r--usr/src/lib/libresolv2_joy/common/sunw/sunw_wrappers.c23
-rw-r--r--usr/src/lib/libresolv2_joy/i386/Makefile30
-rw-r--r--usr/src/lib/libresolv2_joy/include/Makefile60
-rw-r--r--usr/src/lib/libresolv2_joy/include/arpa/port_inet.h41
-rw-r--r--usr/src/lib/libresolv2_joy/include/arpa/port_nameser.h40
-rw-r--r--usr/src/lib/libresolv2_joy/include/conf/sunoptions.h30
-rw-r--r--usr/src/lib/libresolv2_joy/include/config.h75
-rw-r--r--usr/src/lib/libresolv2_joy/include/err.h62
-rw-r--r--usr/src/lib/libresolv2_joy/include/fd_setsize.h10
-rw-r--r--usr/src/lib/libresolv2_joy/include/hesiod.h39
-rw-r--r--usr/src/lib/libresolv2_joy/include/irp.h107
-rw-r--r--usr/src/lib/libresolv2_joy/include/irs.h348
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/assertions.h123
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/ctl.h112
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/dst.h168
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/eventlib.h206
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/heap.h49
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/irpmarshall.h112
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/list.h118
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/logging.h113
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/memcluster.h50
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/misc.h44
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/platform.h42
-rw-r--r--usr/src/lib/libresolv2_joy/include/isc/tree.h59
-rwxr-xr-xusr/src/lib/libresolv2_joy/include/make_os_version34
-rw-r--r--usr/src/lib/libresolv2_joy/include/make_os_version.sh34
-rw-r--r--usr/src/lib/libresolv2_joy/include/port_after.h539
-rw-r--r--usr/src/lib/libresolv2_joy/include/port_before.h201
-rw-r--r--usr/src/lib/libresolv2_joy/include/port_netdb.h188
-rw-r--r--usr/src/lib/libresolv2_joy/include/port_resolv.h42
-rwxr-xr-xusr/src/lib/libresolv2_joy/include/probe_ipv673
-rw-r--r--usr/src/lib/libresolv2_joy/include/probe_ipv6.sh73
-rw-r--r--usr/src/lib/libresolv2_joy/include/res_update.h88
-rw-r--r--usr/src/lib/libresolv2_joy/include/resolv_mt.h47
-rw-r--r--usr/src/lib/libresolv2_joy/include/sunw_port_after.h123
-rw-r--r--usr/src/lib/libresolv2_joy/include/sunw_port_before.h43
-rw-r--r--usr/src/lib/libresolv2_joy/include/sys/bitypes.h37
-rw-r--r--usr/src/lib/libresolv2_joy/include/sys/cdefs.h144
-rw-r--r--usr/src/lib/libresolv2_joy/sparc/Makefile30
-rw-r--r--usr/src/lib/libresolv2_joy/sparcv9/Makefile38
-rw-r--r--usr/src/lib/libscf/inc/libscf.h1
-rw-r--r--usr/src/lib/libsecdb/user_attr.txt8
-rw-r--r--usr/src/lib/libshare/nfs/libshare_nfs.c14
-rw-r--r--usr/src/lib/libsmartsshd/Makefile48
-rw-r--r--usr/src/lib/libsmartsshd/Makefile.com46
-rw-r--r--usr/src/lib/libsmartsshd/amd64/Makefile32
-rw-r--r--usr/src/lib/libsmartsshd/common/llib-lsmartsshd32
-rw-r--r--usr/src/lib/libsmartsshd/common/mapfile-vers48
-rw-r--r--usr/src/lib/libsmartsshd/common/sshd-plugin.c193
-rw-r--r--usr/src/lib/libsmartsshd/i386/Makefile31
-rw-r--r--usr/src/lib/libsmedia/library/Makefile.com2
-rw-r--r--usr/src/lib/libumem/amd64/umem_genasm.c1
-rw-r--r--usr/src/lib/libvnd/Makefile50
-rw-r--r--usr/src/lib/libvnd/Makefile.com42
-rw-r--r--usr/src/lib/libvnd/amd64/Makefile19
-rw-r--r--usr/src/lib/libvnd/common/libvnd.c550
-rw-r--r--usr/src/lib/libvnd/common/libvnd.h84
-rw-r--r--usr/src/lib/libvnd/common/llib-lvnd19
-rw-r--r--usr/src/lib/libvnd/common/mapfile-vers55
-rw-r--r--usr/src/lib/libvnd/i386/Makefile18
-rw-r--r--usr/src/lib/libzdoor/Makefile48
-rw-r--r--usr/src/lib/libzdoor/Makefile.com50
-rw-r--r--usr/src/lib/libzdoor/amd64/Makefile32
-rw-r--r--usr/src/lib/libzdoor/common/llib-lzdoor34
-rw-r--r--usr/src/lib/libzdoor/common/mapfile-vers48
-rw-r--r--usr/src/lib/libzdoor/common/zdoor-int.c324
-rw-r--r--usr/src/lib/libzdoor/common/zdoor-int.h64
-rw-r--r--usr/src/lib/libzdoor/common/zdoor.c431
-rw-r--r--usr/src/lib/libzdoor/common/zerror.c117
-rw-r--r--usr/src/lib/libzdoor/common/zerror.h48
-rw-r--r--usr/src/lib/libzdoor/common/ztree.c343
-rw-r--r--usr/src/lib/libzdoor/common/ztree.h88
-rw-r--r--usr/src/lib/libzdoor/i386/Makefile31
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h2
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c20
-rw-r--r--usr/src/lib/libzfs/common/libzfs_impl.h3
-rw-r--r--usr/src/lib/libzfs/common/libzfs_iter.c15
-rw-r--r--usr/src/lib/libzfs/common/libzfs_pool.c8
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c40
-rw-r--r--usr/src/lib/libzfs/common/mapfile-vers3
-rw-r--r--usr/src/lib/libzonecfg/Makefile.com17
-rw-r--r--usr/src/lib/libzonecfg/common/getzoneent.c145
-rw-r--r--usr/src/lib/libzonecfg/common/libzonecfg.c861
-rw-r--r--usr/src/lib/libzonecfg/common/mapfile-vers14
-rw-r--r--usr/src/lib/libzonecfg/dtd/zonecfg.dtd.115
-rw-r--r--usr/src/lib/libzpool/Makefile.com5
-rw-r--r--usr/src/lib/nsswitch/dns/Makefile.com4
-rw-r--r--usr/src/lib/nsswitch/dns/common/dns_common.h4
-rw-r--r--usr/src/lib/nsswitch/dns/common/dns_mt.c155
-rw-r--r--usr/src/lib/nsswitch/dns/common/gethostent.c8
-rw-r--r--usr/src/lib/nsswitch/dns/common/gethostent6.c4
-rw-r--r--usr/src/lib/pysolaris/Makefile.com1
-rw-r--r--usr/src/lib/varpd/Makefile4
-rw-r--r--usr/src/lib/varpd/direct/Makefile.com2
-rw-r--r--usr/src/lib/varpd/direct/common/llib-lvarpd_direct18
-rw-r--r--usr/src/lib/varpd/direct/sparc/Makefile18
-rw-r--r--usr/src/lib/varpd/direct/sparcv9/Makefile19
-rw-r--r--usr/src/lib/varpd/files/Makefile2
-rw-r--r--usr/src/lib/varpd/files/Makefile.com2
-rw-r--r--usr/src/lib/varpd/files/common/llib-lvarpd_files18
-rw-r--r--usr/src/lib/varpd/files/sparc/Makefile18
-rw-r--r--usr/src/lib/varpd/files/sparcv9/Makefile19
-rw-r--r--usr/src/lib/varpd/libvarpd/Makefile2
-rw-r--r--usr/src/lib/varpd/libvarpd/Makefile.com5
-rw-r--r--usr/src/lib/varpd/libvarpd/common/libvarpd.c15
-rw-r--r--usr/src/lib/varpd/libvarpd/common/libvarpd_impl.h1
-rw-r--r--usr/src/lib/varpd/libvarpd/common/libvarpd_overlay.c2
-rw-r--r--usr/src/lib/varpd/libvarpd/common/libvarpd_plugin.c53
-rw-r--r--usr/src/lib/varpd/libvarpd/common/libvarpd_provider.h6
-rw-r--r--usr/src/lib/varpd/libvarpd/common/llib-lvarpd19
-rw-r--r--usr/src/lib/varpd/libvarpd/sparc/Makefile18
-rw-r--r--usr/src/lib/varpd/libvarpd/sparcv9/Makefile19
-rw-r--r--usr/src/lib/varpd/svp/Makefile42
-rw-r--r--usr/src/lib/varpd/svp/Makefile.com52
-rw-r--r--usr/src/lib/varpd/svp/amd64/Makefile19
-rw-r--r--usr/src/lib/varpd/svp/common/libvarpd_svp.c1140
-rw-r--r--usr/src/lib/varpd/svp/common/libvarpd_svp.h357
-rw-r--r--usr/src/lib/varpd/svp/common/libvarpd_svp_conn.c1030
-rw-r--r--usr/src/lib/varpd/svp/common/libvarpd_svp_crc.c53
-rw-r--r--usr/src/lib/varpd/svp/common/libvarpd_svp_host.c171
-rw-r--r--usr/src/lib/varpd/svp/common/libvarpd_svp_loop.c210
-rw-r--r--usr/src/lib/varpd/svp/common/libvarpd_svp_prot.h236
-rw-r--r--usr/src/lib/varpd/svp/common/libvarpd_svp_remote.c821
-rw-r--r--usr/src/lib/varpd/svp/common/libvarpd_svp_shootdown.c474
-rw-r--r--usr/src/lib/varpd/svp/common/libvarpd_svp_timer.c150
-rw-r--r--usr/src/lib/varpd/svp/common/llib-lvarpd_svp18
-rw-r--r--usr/src/lib/varpd/svp/common/mapfile-vers35
-rw-r--r--usr/src/lib/varpd/svp/i386/Makefile18
-rw-r--r--usr/src/lib/varpd/svp/sparc/Makefile18
-rw-r--r--usr/src/lib/varpd/svp/sparcv9/Makefile19
-rw-r--r--usr/src/lib/xml/os_dtd.c238
-rw-r--r--usr/src/lib/xml/os_dtd.h49
-rw-r--r--usr/src/man/Makefile1
-rw-r--r--usr/src/man/man1/Makefile14
-rw-r--r--usr/src/man/man1/column.1129
-rw-r--r--usr/src/man/man1/crontab.149
-rw-r--r--usr/src/man/man1/diff.11
-rw-r--r--usr/src/man/man1/hostname.117
-rw-r--r--usr/src/man/man1/ld.so.1.119
-rw-r--r--usr/src/man/man1/machid.125
-rw-r--r--usr/src/man/man1/pgrep.170
-rw-r--r--usr/src/man/man1/ps.130
-rw-r--r--usr/src/man/man1/sed.120
-rw-r--r--usr/src/man/man1/zlogin.181
-rw-r--r--usr/src/man/man1b/ps.1b14
-rw-r--r--usr/src/man/man3c/Makefile3
-rw-r--r--usr/src/man/man3c/inotify_add_watch.3c120
-rw-r--r--usr/src/man/man3c/inotify_init.3c107
-rw-r--r--usr/src/man/man3c/inotify_rm_watch.3c81
-rw-r--r--usr/src/man/man3c/psignal.3c2
-rw-r--r--usr/src/man/man3dlpi/Makefile2
-rw-r--r--usr/src/man/man3dlpi/dlpi_open.3dlpi31
-rw-r--r--usr/src/man/man3lib/Makefile1
-rw-r--r--usr/src/man/man3lib/libvnd.3lib690
-rw-r--r--usr/src/man/man3vnd/Makefile70
-rw-r--r--usr/src/man/man3vnd/vnd_create.3vnd280
-rw-r--r--usr/src/man/man3vnd/vnd_errno.3vnd170
-rw-r--r--usr/src/man/man3vnd/vnd_frameio_read.3vnd705
-rw-r--r--usr/src/man/man3vnd/vnd_pollfd.3vnd155
-rw-r--r--usr/src/man/man3vnd/vnd_prop_get.3vnd242
-rw-r--r--usr/src/man/man3vnd/vnd_prop_iter.3vnd148
-rw-r--r--usr/src/man/man3vnd/vnd_prop_writeable.3vnd101
-rw-r--r--usr/src/man/man3vnd/vnd_walk.3vnd155
-rw-r--r--usr/src/man/man3xnet/Makefile4
-rw-r--r--usr/src/man/man3xnet/htonl.3xnet26
-rw-r--r--usr/src/man/man4/Makefile3
-rw-r--r--usr/src/man/man4/swap.492
-rw-r--r--usr/src/man/man4d/Makefile4
-rw-r--r--usr/src/man/man4d/cpuid.4d5
-rw-r--r--usr/src/man/man4d/vnd.4d118
-rw-r--r--usr/src/man/man4d/zfd.4d81
-rw-r--r--usr/src/man/man4fs/Makefile30
-rw-r--r--usr/src/man/man4fs/hyprlofs.4fs62
-rw-r--r--usr/src/man/man4fs/lxproc.4fs115
-rw-r--r--usr/src/man/man4i/Makefile4
-rw-r--r--usr/src/man/man4m/Makefile2
-rw-r--r--usr/src/man/man4m/datafilt.4m48
-rw-r--r--usr/src/man/man4p/vxlan.4p17
-rw-r--r--usr/src/man/man5/proc.536
-rw-r--r--usr/src/man/man5/process.516
-rw-r--r--usr/src/man/man7/Makefile6
-rw-r--r--usr/src/man/man7/inotify.7305
-rw-r--r--usr/src/man/man7/lx.7113
-rw-r--r--usr/src/man/man7/privileges.720
-rw-r--r--usr/src/man/man7/resource_controls.774
-rw-r--r--usr/src/man/man8/Makefile3
-rw-r--r--usr/src/man/man8/connstat.86
-rw-r--r--usr/src/man/man8/dladm.8233
-rw-r--r--usr/src/man/man8/dumpadm.840
-rw-r--r--usr/src/man/man8/flowadm.851
-rw-r--r--usr/src/man/man8/prstat.810
-rw-r--r--usr/src/man/man8/reboot.83
-rw-r--r--usr/src/man/man8/route.822
-rw-r--r--usr/src/man/man8/savecore.826
-rw-r--r--usr/src/man/man8/smbios.816
-rw-r--r--usr/src/man/man8/snoop.819
-rw-r--r--usr/src/man/man8/svc.startd.87
-rw-r--r--usr/src/man/man8/tunefs.87
-rw-r--r--usr/src/man/man8/vfsstat.8213
-rw-r--r--usr/src/man/man8/vndadm.8650
-rw-r--r--usr/src/man/man8/vndstat.8163
-rw-r--r--usr/src/man/man8/zfs.896
-rw-r--r--usr/src/man/man8/zoneadm.828
-rw-r--r--usr/src/man/man8/zonecfg.895
-rw-r--r--usr/src/man/man9e/ddi_ufm.9e1
-rw-r--r--usr/src/man/man9f/Makefile4
-rw-r--r--usr/src/man/man9f/bzero.9f11
-rw-r--r--usr/src/man/man9f/ddi_fm_init.9f315
-rw-r--r--usr/src/pkg/manifests/developer-build-make.p5m10
-rw-r--r--usr/src/pkg/manifests/driver-crypto-nfp.p5m32
-rw-r--r--usr/src/pkg/manifests/service-fault-management.p5m54
-rw-r--r--usr/src/pkg/manifests/system-bhyve-tests.p5m4
-rw-r--r--usr/src/pkg/manifests/system-dtrace-tests.p5m2
-rw-r--r--usr/src/pkg/manifests/system-header.p5m1
-rw-r--r--usr/src/pkg/manifests/system-kernel.man9f.inc2
-rw-r--r--usr/src/pkg/manifests/system-library-bhyve.p5m2
-rw-r--r--usr/src/pkg/manifests/system-test-libctest.p5m2
-rw-r--r--usr/src/pkg/manifests/system-test-nettest.p5m1
-rw-r--r--usr/src/pkg/manifests/system-test-ostest.p5m8
-rw-r--r--usr/src/pkg/manifests/system-test-smartostest.p5m50
-rw-r--r--usr/src/pkg/manifests/system-test-utiltest.p5m12
-rw-r--r--usr/src/pkg/manifests/system-test-vndtest.p5m82
-rw-r--r--usr/src/pkg/manifests/system-test-zfstest.p5m9
-rw-r--r--usr/src/pkg/manifests/system-zones-brand-joyent.p5m54
-rw-r--r--usr/src/pkg/manifests/system-zones-brand-lx.p5m101
-rw-r--r--usr/src/req.flg1
-rw-r--r--usr/src/stand/lib/fs/hsfs/hsfsops.c1
-rw-r--r--usr/src/test/Makefile1
-rw-r--r--usr/src/test/bhyve-tests/runfiles/default.run5
-rw-r--r--usr/src/test/bhyve-tests/tests/vmm/Makefile6
-rwxr-xr-xusr/src/test/bhyve-tests/tests/vmm/vmm_drv_test_fini23
-rwxr-xr-xusr/src/test/bhyve-tests/tests/vmm/vmm_drv_test_init24
-rw-r--r--usr/src/test/libc-tests/runfiles/default.run2
-rw-r--r--usr/src/test/libc-tests/tests/Makefile1
-rw-r--r--usr/src/test/libc-tests/tests/env-OS-4089.c73
-rw-r--r--usr/src/test/libc-tests/tests/random/Makefile10
-rw-r--r--usr/src/test/libc-tests/tests/random/chacha_tv.c4
-rw-r--r--usr/src/test/os-tests/runfiles/default.run9
-rw-r--r--usr/src/test/os-tests/tests/Makefile5
-rw-r--r--usr/src/test/os-tests/tests/OS-6097.c74
-rw-r--r--usr/src/test/os-tests/tests/file-locking/Makefile16
-rw-r--r--usr/src/test/os-tests/tests/poll/Makefile4
-rw-r--r--usr/src/test/os-tests/tests/secflags/secflags_zonecfg.sh10
-rw-r--r--usr/src/test/os-tests/tests/tmpfs/Makefile52
-rw-r--r--usr/src/test/os-tests/tests/tmpfs/tmpfs_badmount.ksh114
-rw-r--r--usr/src/test/os-tests/tests/tmpfs/tmpfs_enospc.ksh74
-rw-r--r--usr/src/test/os-tests/tests/tmpfs/tmpfs_full.c94
-rw-r--r--usr/src/test/smartos-test/Makefile49
-rw-r--r--usr/src/test/smartos-test/README103
-rwxr-xr-xusr/src/test/smartos-test/smartos-test.sh444
-rw-r--r--usr/src/test/test-runner/cmd/run67
-rw-r--r--usr/src/test/util-tests/runfiles/default.run6
-rw-r--r--usr/src/test/util-tests/tests/Makefile1
-rw-r--r--usr/src/test/util-tests/tests/bunyan/Makefile68
-rw-r--r--usr/src/test/util-tests/tests/bunyan/btest.c316
-rw-r--r--usr/src/test/util-tests/tests/bunyan/bunyan.ksh26
-rw-r--r--usr/src/test/util-tests/tests/dis/distest.ksh23
-rw-r--r--usr/src/test/util-tests/tests/dladm/Makefile2
-rw-r--r--usr/src/test/util-tests/tests/dladm/allowed-cids.ksh95
-rw-r--r--usr/src/test/util-tests/tests/dladm/allowed-ips.ksh43
-rw-r--r--usr/src/test/util-tests/tests/dladm/common.ksh57
-rw-r--r--usr/src/test/util-tests/tests/dladm/dynamic-methods.ksh45
-rw-r--r--usr/src/test/util-tests/tests/dladm/vnic-mtu.ksh116
-rw-r--r--usr/src/test/util-tests/tests/grep_xpg4/grep_test.ksh2
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/Makefile19
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/json_08_large_data.ksh37
-rw-r--r--usr/src/test/util-tests/tests/libnvpair_json/print_json.c2
-rw-r--r--usr/src/test/util-tests/tests/mdb/typedef/tst.dellist.mdb.out0
-rw-r--r--usr/src/test/util-tests/tests/mdb/typedef/tst.emptylist.mdb.out0
-rw-r--r--usr/src/test/zfs-tests/cmd/scripts/zfstest.ksh16
-rw-r--r--usr/src/test/zfs-tests/cmd/watch_dir/Makefile19
-rw-r--r--usr/src/test/zfs-tests/cmd/watch_dir/watch_dir.c151
-rw-r--r--usr/src/test/zfs-tests/include/commands.cfg3
-rw-r--r--usr/src/test/zfs-tests/runfiles/common.run6
-rw-r--r--usr/src/test/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_dryrun.ksh169
-rw-r--r--usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_inotify.ksh74
-rw-r--r--usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_portfs.ksh74
-rw-r--r--usr/src/test/zfs-tests/tests/functional/mmp/mmp.cfg1
-rw-r--r--usr/src/test/zfs-tests/tests/functional/nopwrite/nopwrite_sync.ksh5
-rw-r--r--usr/src/test/zfs-tests/tests/functional/slog/slog.cfg5
-rw-r--r--usr/src/test/zfs-tests/tests/functional/slog/slog_014_pos.ksh1
-rw-r--r--usr/src/tools/Makefile27
-rw-r--r--usr/src/tools/README.tools3
-rw-r--r--usr/src/tools/cpcgen/cpcgen.c4
-rw-r--r--usr/src/tools/ctf/Makefile25
-rw-r--r--usr/src/tools/ctf/Makefile.ctf2
-rw-r--r--usr/src/tools/ctf/common/utils.h3
-rw-r--r--usr/src/tools/ctf/dwarf/Makefile.com4
-rw-r--r--usr/src/tools/cw/Makefile2
-rw-r--r--usr/src/tools/env/illumos.sh6
-rw-r--r--usr/src/tools/findunref/exception_list.git39
-rw-r--r--usr/src/tools/findunref/exception_list.unknown0
-rw-r--r--usr/src/tools/man/Makefile37
-rw-r--r--usr/src/tools/mandoc/Makefile8
-rw-r--r--usr/src/tools/onbld/Checks/Cddl.py2
-rw-r--r--usr/src/tools/onbld/Checks/CmtBlk.py2
-rw-r--r--usr/src/tools/onbld/Checks/Comments.py4
-rw-r--r--usr/src/tools/onbld/Checks/Copyright.py10
-rw-r--r--usr/src/tools/onbld/Checks/DbLookups.py35
-rw-r--r--usr/src/tools/onbld/Checks/HdrChk.py2
-rw-r--r--usr/src/tools/onbld/Checks/Keywords.py2
-rw-r--r--usr/src/tools/onbld/Checks/Mapfile.py2
-rw-r--r--usr/src/tools/onbld/Checks/__init__.py2
-rw-r--r--usr/src/tools/onbld/Scm/__init__.py4
-rw-r--r--usr/src/tools/protocmp/stdusers.c2
-rw-r--r--usr/src/tools/scripts/webrev.sh7
-rw-r--r--usr/src/tools/smatch/Makefile9
-rw-r--r--usr/src/tools/vtfontcvt/Makefile9
-rw-r--r--usr/src/ucbhead/Makefile68
-rw-r--r--usr/src/ucbhead/sys/file.h4
-rw-r--r--usr/src/ucblib/libucb/port/sys/flock.c2
-rw-r--r--usr/src/uts/Makefile.targ1
-rw-r--r--usr/src/uts/Makefile.uts9
-rw-r--r--usr/src/uts/common/Makefile.files41
-rw-r--r--usr/src/uts/common/Makefile.rules34
-rw-r--r--usr/src/uts/common/brand/lx/autofs/lx_autofs.c3177
-rw-r--r--usr/src/uts/common/brand/lx/autofs/lxautofs.conf14
-rw-r--r--usr/src/uts/common/brand/lx/cgroups/cgrps.h222
-rw-r--r--usr/src/uts/common/brand/lx/cgroups/cgrps_node.c1023
-rw-r--r--usr/src/uts/common/brand/lx/cgroups/cgrps_vfsops.c1071
-rw-r--r--usr/src/uts/common/brand/lx/cgroups/cgrps_vnops.c1552
-rw-r--r--usr/src/uts/common/brand/lx/devfs/lxd.h244
-rw-r--r--usr/src/uts/common/brand/lx/devfs/lxd_attrdb.c368
-rw-r--r--usr/src/uts/common/brand/lx/devfs/lxd_node.c1012
-rw-r--r--usr/src/uts/common/brand/lx/devfs/lxd_vfsops.c860
-rw-r--r--usr/src/uts/common/brand/lx/devfs/lxd_vnops.c1520
-rw-r--r--usr/src/uts/common/brand/lx/dtrace/lx_systrace.c499
-rw-r--r--usr/src/uts/common/brand/lx/dtrace/lx_systrace.conf27
-rw-r--r--usr/src/uts/common/brand/lx/io/lx_netlink.c2234
-rw-r--r--usr/src/uts/common/brand/lx/io/lx_ptm.c1188
-rw-r--r--usr/src/uts/common/brand/lx/io/lx_ptm.conf27
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_acct.c198
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_acl.c213
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_audit.c1604
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_brand.c2728
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_lockd.c351
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_misc.c1136
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_pid.c498
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_ptrace.c2710
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_signal.c50
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_syscall.c1229
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_proc.h394
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_prsubr.c1065
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_prvfsops.c377
-rw-r--r--usr/src/uts/common/brand/lx/procfs/lx_prvnops.c8553
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_acl.h45
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_audit.h38
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_autofs.h511
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h162
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_brand.h772
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_fcntl.h161
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_futex.h143
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_impl.h52
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_ldt.h91
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_misc.h135
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_ptm.h44
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_siginfo.h190
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_signal.h32
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_socket.h444
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_syscalls.h341
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_types.h144
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_userhz.h64
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_access.c223
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_aio.c1345
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_brk.c55
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_chmod.c107
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_chown.c180
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_clone.c513
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_close.c30
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_cpu.c36
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_dup.c53
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_epoll.c303
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_eventfd.c126
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_fadvise.c103
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_fallocate.c251
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_fcntl.c701
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_futex.c1728
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_getcwd.c52
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_getdents.c416
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_getpid.c75
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_getrandom.c33
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_id.c509
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_ioctl.c1901
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_ioprio.c66
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_kill.c408
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_link.c200
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_lseek.c82
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_mem.c1118
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_miscsys.c495
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_mkdir.c38
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_modify_ldt.c121
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_mount.c744
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_open.c224
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_personality.c112
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_pgrp.c189
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_pipe.c309
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_poll.c786
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_prctl.c351
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_priority.c192
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_rename.c39
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_rlimit.c597
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_rw.c956
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_sched.c1161
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_socket.c4832
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_splice.c491
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_stat.c481
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_sync.c86
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_sysinfo.c207
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_thread_area.c194
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_time.c72
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_timer.c637
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_umask.c52
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_uname.c82
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_wait.c377
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_xattr.c519
-rw-r--r--usr/src/uts/common/brand/lx/sysfs/lx_sysfs.h198
-rw-r--r--usr/src/uts/common/brand/lx/sysfs/lx_syssubr.c443
-rw-r--r--usr/src/uts/common/brand/lx/sysfs/lx_sysvfsops.c365
-rw-r--r--usr/src/uts/common/brand/lx/sysfs/lx_sysvnops.c2165
-rw-r--r--usr/src/uts/common/brand/sn1/sn1_brand.c103
-rw-r--r--usr/src/uts/common/brand/sn1/sn1_brand.h7
-rw-r--r--usr/src/uts/common/brand/solaris10/s10_brand.c103
-rw-r--r--usr/src/uts/common/brand/solaris10/s10_brand.h6
-rw-r--r--usr/src/uts/common/conf/param.c29
-rw-r--r--usr/src/uts/common/contract/process.c23
-rw-r--r--usr/src/uts/common/crypto/api/kcf_random.c1
-rw-r--r--usr/src/uts/common/crypto/core/kcf_sched.c6
-rw-r--r--usr/src/uts/common/disp/cmt.c8
-rw-r--r--usr/src/uts/common/disp/cpucaps.c285
-rw-r--r--usr/src/uts/common/disp/disp.c2
-rw-r--r--usr/src/uts/common/disp/fx.c12
-rw-r--r--usr/src/uts/common/disp/priocntl.c4
-rw-r--r--usr/src/uts/common/disp/rt.c9
-rw-r--r--usr/src/uts/common/disp/rt_dptbl.c2
-rw-r--r--usr/src/uts/common/disp/thread.c118
-rw-r--r--usr/src/uts/common/dtrace/dtrace.c23
-rw-r--r--usr/src/uts/common/dtrace/sdt_subr.c33
-rw-r--r--usr/src/uts/common/exec/elf/elf.c473
-rw-r--r--usr/src/uts/common/exec/elf/elf_notes.c2
-rw-r--r--usr/src/uts/common/exec/intp/intp.c36
-rw-r--r--usr/src/uts/common/exec/java/java.c9
-rw-r--r--usr/src/uts/common/exec/shbin/shbin.c9
-rw-r--r--usr/src/uts/common/fs/dev/sdev_netops.c259
-rw-r--r--usr/src/uts/common/fs/dev/sdev_vnops.c8
-rw-r--r--usr/src/uts/common/fs/dev/sdev_zvolops.c6
-rw-r--r--usr/src/uts/common/fs/fem.c688
-rw-r--r--usr/src/uts/common/fs/fifofs/fifosubr.c19
-rw-r--r--usr/src/uts/common/fs/fifofs/fifovnops.c40
-rw-r--r--usr/src/uts/common/fs/hyprlofs/hyprlofs_dir.c640
-rw-r--r--usr/src/uts/common/fs/hyprlofs/hyprlofs_subr.c127
-rw-r--r--usr/src/uts/common/fs/hyprlofs/hyprlofs_vfsops.c613
-rw-r--r--usr/src/uts/common/fs/hyprlofs/hyprlofs_vnops.c1450
-rw-r--r--usr/src/uts/common/fs/lookup.c4
-rw-r--r--usr/src/uts/common/fs/lxproc/lxpr_subr.c524
-rw-r--r--usr/src/uts/common/fs/lxproc/lxpr_vfsops.c367
-rw-r--r--usr/src/uts/common/fs/lxproc/lxpr_vnops.c3105
-rw-r--r--usr/src/uts/common/fs/lxproc/lxproc.h278
-rw-r--r--usr/src/uts/common/fs/nfs/nfs3_vfsops.c1
-rw-r--r--usr/src/uts/common/fs/nfs/nfs3_vnops.c14
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_vfsops.c1
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4_vnops.c48
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_sys.c3
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_vfsops.c1
-rw-r--r--usr/src/uts/common/fs/nfs/nfs_vnops.c24
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_dir.c5
-rw-r--r--usr/src/uts/common/fs/pcfs/pc_vnops.c5
-rw-r--r--usr/src/uts/common/fs/portfs/port_fop.c83
-rw-r--r--usr/src/uts/common/fs/proc/prargv.c530
-rw-r--r--usr/src/uts/common/fs/proc/prcontrol.c16
-rw-r--r--usr/src/uts/common/fs/proc/prdata.h7
-rw-r--r--usr/src/uts/common/fs/proc/prsubr.c7
-rw-r--r--usr/src/uts/common/fs/proc/prvnops.c203
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_kshare.c1
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_server.c16
-rw-r--r--usr/src/uts/common/fs/sockfs/sockcommon_sops.c16
-rw-r--r--usr/src/uts/common/fs/sockfs/sockcommon_subr.c19
-rw-r--r--usr/src/uts/common/fs/sockfs/sockfilter.c24
-rw-r--r--usr/src/uts/common/fs/sockfs/sockfilter_impl.h2
-rw-r--r--usr/src/uts/common/fs/sockfs/socksubr.c5
-rw-r--r--usr/src/uts/common/fs/sockfs/socktpi_impl.h3
-rw-r--r--usr/src/uts/common/fs/swapfs/swap_subr.c6
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_dir.c61
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_subr.c136
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_tnode.c70
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_vfsops.c278
-rw-r--r--usr/src/uts/common/fs/tmpfs/tmp_vnops.c99
-rw-r--r--usr/src/uts/common/fs/udfs/udf_dir.c6
-rw-r--r--usr/src/uts/common/fs/udfs/udf_vnops.c14
-rw-r--r--usr/src/uts/common/fs/ufs/ufs_vnops.c25
-rw-r--r--usr/src/uts/common/fs/vfs.c4
-rw-r--r--usr/src/uts/common/fs/vnode.c173
-rw-r--r--usr/src/uts/common/fs/zfs/abd.c5
-rw-r--r--usr/src/uts/common/fs/zfs/arc.c15
-rw-r--r--usr/src/uts/common/fs/zfs/dbuf.c13
-rw-r--r--usr/src/uts/common/fs/zfs/dmu_send.c2
-rw-r--r--usr/src/uts/common/fs/zfs/dmu_tx.c4
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_dir.c3
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_pool.c3
-rw-r--r--usr/src/uts/common/fs/zfs/metaslab.c54
-rw-r--r--usr/src/uts/common/fs/zfs/spa.c17
-rw-r--r--usr/src/uts/common/fs/zfs/sys/metaslab_impl.h10
-rw-r--r--usr/src/uts/common/fs/zfs/sys/vdev_impl.h1
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_zone.h63
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zio.h12
-rw-r--r--usr/src/uts/common/fs/zfs/txg.c3
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_disk.c8
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_queue.c14
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_ioctl.c163
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vfsops.c15
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vnops.c43
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_zone.c1419
-rw-r--r--usr/src/uts/common/fs/zfs/zil.c37
-rw-r--r--usr/src/uts/common/fs/zfs/zio.c29
-rw-r--r--usr/src/uts/common/fs/zfs/zvol.c99
-rw-r--r--usr/src/uts/common/inet/bpf.h49
-rw-r--r--usr/src/uts/common/inet/bpf_filter.c (renamed from usr/src/uts/common/io/bpf/bpf_filter.c)46
-rw-r--r--usr/src/uts/common/inet/ip.h15
-rw-r--r--usr/src/uts/common/inet/ip/conn_opt.c22
-rw-r--r--usr/src/uts/common/inet/ip/icmp.c117
-rw-r--r--usr/src/uts/common/inet/ip/icmp_opt_data.c6
-rw-r--r--usr/src/uts/common/inet/ip/ip.c94
-rw-r--r--usr/src/uts/common/inet/ip/ip_if.c177
-rw-r--r--usr/src/uts/common/inet/ip/ip_squeue.c2
-rw-r--r--usr/src/uts/common/inet/ip/ipclassifier.c165
-rw-r--r--usr/src/uts/common/inet/ipclassifier.h4
-rw-r--r--usr/src/uts/common/inet/ipd/ipd.c6
-rw-r--r--usr/src/uts/common/inet/ipf/cfw.c659
-rw-r--r--usr/src/uts/common/inet/ipf/fil.c5
-rw-r--r--usr/src/uts/common/inet/ipf/ip_fil_solaris.c304
-rw-r--r--usr/src/uts/common/inet/ipf/ip_log.c4
-rw-r--r--usr/src/uts/common/inet/ipf/ip_state.c19
-rw-r--r--usr/src/uts/common/inet/ipf/ipf.conf5
-rw-r--r--usr/src/uts/common/inet/ipf/netinet/Makefile7
-rw-r--r--usr/src/uts/common/inet/ipf/netinet/ip_fil.h46
-rw-r--r--usr/src/uts/common/inet/ipf/netinet/ip_state.h4
-rw-r--r--usr/src/uts/common/inet/ipf/netinet/ipf_cfw.h69
-rw-r--r--usr/src/uts/common/inet/ipf/netinet/ipf_stack.h15
-rw-r--r--usr/src/uts/common/inet/ipf/solaris.c10
-rw-r--r--usr/src/uts/common/inet/mib2.h3
-rw-r--r--usr/src/uts/common/inet/rawip_impl.h6
-rw-r--r--usr/src/uts/common/inet/sockmods/datafilt.c116
-rw-r--r--usr/src/uts/common/inet/sockmods/sockmod_pfp.c11
-rw-r--r--usr/src/uts/common/inet/squeue.c104
-rw-r--r--usr/src/uts/common/inet/tcp.h10
-rw-r--r--usr/src/uts/common/inet/tcp/tcp.c39
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_bind.c226
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_input.c19
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_opt_data.c110
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_socket.c10
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_stats.c9
-rw-r--r--usr/src/uts/common/inet/tcp/tcp_timers.c2
-rw-r--r--usr/src/uts/common/inet/tcp_impl.h26
-rw-r--r--usr/src/uts/common/inet/udp/udp.c20
-rw-r--r--usr/src/uts/common/inet/udp/udp_opt_data.c4
-rw-r--r--usr/src/uts/common/inet/udp_impl.h4
-rw-r--r--usr/src/uts/common/io/bpf/bpf_wrap.c35
-rw-r--r--usr/src/uts/common/io/dld/dld_drv.c47
-rw-r--r--usr/src/uts/common/io/dld/dld_proto.c108
-rw-r--r--usr/src/uts/common/io/dld/dld_str.c108
-rw-r--r--usr/src/uts/common/io/dls/dls.c59
-rw-r--r--usr/src/uts/common/io/dls/dls_link.c1
-rw-r--r--usr/src/uts/common/io/dls/dls_mgmt.c423
-rw-r--r--usr/src/uts/common/io/dls/dls_stat.c172
-rw-r--r--usr/src/uts/common/io/dump.c30
-rw-r--r--usr/src/uts/common/io/eventfd.c88
-rw-r--r--usr/src/uts/common/io/gsqueue/gsqueue.c608
-rw-r--r--usr/src/uts/common/io/inotify.c1559
-rw-r--r--usr/src/uts/common/io/inotify.conf16
-rw-r--r--usr/src/uts/common/io/ixgbe/ixgbe_main.c8
-rw-r--r--usr/src/uts/common/io/ksocket/ksocket.c2
-rw-r--r--usr/src/uts/common/io/ksyms.c9
-rw-r--r--usr/src/uts/common/io/mac/mac.c6
-rw-r--r--usr/src/uts/common/io/mac/mac_client.c35
-rw-r--r--usr/src/uts/common/io/mac/mac_protect.c118
-rw-r--r--usr/src/uts/common/io/mac/mac_stat.c8
-rw-r--r--usr/src/uts/common/io/mem.c11
-rw-r--r--usr/src/uts/common/io/mr_sas/mr_sas.conf8
-rw-r--r--usr/src/uts/common/io/nfp/THIRDPARTYLICENSE19
-rw-r--r--usr/src/uts/common/io/nfp/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/uts/common/io/nfp/autoversion.h21
-rw-r--r--usr/src/uts/common/io/nfp/drvlist.c19
-rw-r--r--usr/src/uts/common/io/nfp/hostif.c1192
-rw-r--r--usr/src/uts/common/io/nfp/i21285.c310
-rw-r--r--usr/src/uts/common/io/nfp/i21285.h43
-rw-r--r--usr/src/uts/common/io/nfp/i21555.c423
-rw-r--r--usr/src/uts/common/io/nfp/i21555.h51
-rw-r--r--usr/src/uts/common/io/nfp/i21555d.c28
-rw-r--r--usr/src/uts/common/io/nfp/nfdev-common.h141
-rw-r--r--usr/src/uts/common/io/nfp/nfdev-solaris.h37
-rw-r--r--usr/src/uts/common/io/nfp/nfp.h113
-rw-r--r--usr/src/uts/common/io/nfp/nfp_cmd.h68
-rw-r--r--usr/src/uts/common/io/nfp/nfp_common.h68
-rw-r--r--usr/src/uts/common/io/nfp/nfp_error.h48
-rw-r--r--usr/src/uts/common/io/nfp/nfp_hostif.h54
-rw-r--r--usr/src/uts/common/io/nfp/nfp_ifvers.c51
-rw-r--r--usr/src/uts/common/io/nfp/nfp_osif.h105
-rw-r--r--usr/src/uts/common/io/nfp/nfpci.h171
-rw-r--r--usr/src/uts/common/io/nfp/osif.c184
-rw-r--r--usr/src/uts/common/io/overlay/overlay.c3
-rw-r--r--usr/src/uts/common/io/overlay/overlay_mux.c10
-rw-r--r--usr/src/uts/common/io/physmem.c8
-rw-r--r--usr/src/uts/common/io/pseudo.conf9
-rw-r--r--usr/src/uts/common/io/ptm.c47
-rw-r--r--usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/iwarp_sm.jpgbin0 -> 86314 bytes
-rw-r--r--usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-full-36.jpgbin0 -> 37055 bytes
-rw-r--r--usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-full.pngbin0 -> 9054 bytes
-rw-r--r--usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-logo.pngbin0 -> 9907 bytes
-rw-r--r--usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/reg_access.jpgbin0 -> 46722 bytes
-rw-r--r--usr/src/uts/common/io/qede/579xx/drivers/ecore/ecore_init_values.binbin0 -> 1177408 bytes
-rw-r--r--usr/src/uts/common/io/qede/579xx/drivers/ecore/ecore_init_values_zipped.binbin0 -> 528720 bytes
-rw-r--r--usr/src/uts/common/io/qede/qede_list.h1
-rw-r--r--usr/src/uts/common/io/qede/qede_version.h1
-rw-r--r--usr/src/uts/common/io/random.c3
-rw-r--r--usr/src/uts/common/io/rsm/rsm.c2
-rw-r--r--usr/src/uts/common/io/sata/adapters/ahci/ahci.c1
-rw-r--r--usr/src/uts/common/io/scsi/adapters/mpt_sas/mpt_sas.conf7
-rw-r--r--usr/src/uts/common/io/scsi/targets/sd.c107
-rw-r--r--usr/src/uts/common/io/signalfd.c4
-rw-r--r--usr/src/uts/common/io/vnd/frameio.c465
-rw-r--r--usr/src/uts/common/io/vnd/vnd.c5857
-rw-r--r--usr/src/uts/common/io/vnd/vnd.conf16
-rw-r--r--usr/src/uts/common/io/zfd.c1157
-rw-r--r--usr/src/uts/common/klm/klmmod.c5
-rw-r--r--usr/src/uts/common/klm/mapfile-mod6
-rw-r--r--usr/src/uts/common/klm/nlm_dispatch.c11
-rw-r--r--usr/src/uts/common/klm/nlm_impl.c37
-rw-r--r--usr/src/uts/common/klm/nlm_impl.h2
-rw-r--r--usr/src/uts/common/mapfiles/ddi.mapfile3
-rw-r--r--usr/src/uts/common/netinet/udp.h2
-rw-r--r--usr/src/uts/common/nfs/nfssys.h12
-rw-r--r--usr/src/uts/common/os/acct.c19
-rw-r--r--usr/src/uts/common/os/brand.c186
-rw-r--r--usr/src/uts/common/os/contract.c6
-rw-r--r--usr/src/uts/common/os/core.c2
-rw-r--r--usr/src/uts/common/os/cpu.c2
-rw-r--r--usr/src/uts/common/os/cred.c8
-rw-r--r--usr/src/uts/common/os/ddi_intr_irm.c2
-rw-r--r--usr/src/uts/common/os/dumpsubr.c107
-rw-r--r--usr/src/uts/common/os/exec.c160
-rw-r--r--usr/src/uts/common/os/exit.c210
-rw-r--r--usr/src/uts/common/os/fio.c24
-rw-r--r--usr/src/uts/common/os/fork.c52
-rw-r--r--usr/src/uts/common/os/grow.c34
-rw-r--r--usr/src/uts/common/os/ipc.c26
-rw-r--r--usr/src/uts/common/os/kmem.c1
-rw-r--r--usr/src/uts/common/os/kstat_fr.c11
-rw-r--r--usr/src/uts/common/os/logsubr.c9
-rw-r--r--usr/src/uts/common/os/lwp.c129
-rw-r--r--usr/src/uts/common/os/main.c12
-rw-r--r--usr/src/uts/common/os/mem_config.c3
-rw-r--r--usr/src/uts/common/os/mmapobj.c13
-rw-r--r--usr/src/uts/common/os/pid.c25
-rw-r--r--usr/src/uts/common/os/policy.c32
-rw-r--r--usr/src/uts/common/os/priv_defs8
-rw-r--r--usr/src/uts/common/os/rctl.c32
-rw-r--r--usr/src/uts/common/os/rctl_proc.c28
-rw-r--r--usr/src/uts/common/os/sched.c15
-rw-r--r--usr/src/uts/common/os/shm.c41
-rw-r--r--usr/src/uts/common/os/sig.c100
-rw-r--r--usr/src/uts/common/os/streamio.c2
-rw-r--r--usr/src/uts/common/os/strsubr.c1
-rw-r--r--usr/src/uts/common/os/sunddi.c14
-rw-r--r--usr/src/uts/common/os/sysent.c37
-rw-r--r--usr/src/uts/common/os/timer.c281
-rw-r--r--usr/src/uts/common/os/vm_pageout.c579
-rw-r--r--usr/src/uts/common/os/vmem.c2
-rw-r--r--usr/src/uts/common/os/zone.c1167
-rw-r--r--usr/src/uts/common/sys/Makefile12
-rw-r--r--usr/src/uts/common/sys/acct.h3
-rw-r--r--usr/src/uts/common/sys/auxv.h30
-rw-r--r--usr/src/uts/common/sys/brand.h102
-rw-r--r--usr/src/uts/common/sys/buf.h8
-rw-r--r--usr/src/uts/common/sys/contract/process.h6
-rw-r--r--usr/src/uts/common/sys/cpucaps.h5
-rw-r--r--usr/src/uts/common/sys/cpucaps_impl.h6
-rw-r--r--usr/src/uts/common/sys/cpuvar.h3
-rw-r--r--usr/src/uts/common/sys/cred.h1
-rw-r--r--usr/src/uts/common/sys/dktp/dadk.h2
-rw-r--r--usr/src/uts/common/sys/dld.h12
-rw-r--r--usr/src/uts/common/sys/dld_impl.h5
-rw-r--r--usr/src/uts/common/sys/dlpi.h11
-rw-r--r--usr/src/uts/common/sys/dls.h10
-rw-r--r--usr/src/uts/common/sys/dls_impl.h6
-rw-r--r--usr/src/uts/common/sys/dls_mgmt.h9
-rw-r--r--usr/src/uts/common/sys/dumpadm.h3
-rw-r--r--usr/src/uts/common/sys/dumphdr.h29
-rw-r--r--usr/src/uts/common/sys/elf.h44
-rw-r--r--usr/src/uts/common/sys/eventfd.h10
-rw-r--r--usr/src/uts/common/sys/exec.h20
-rw-r--r--usr/src/uts/common/sys/file.h11
-rw-r--r--usr/src/uts/common/sys/frameio.h107
-rw-r--r--usr/src/uts/common/sys/fs/fifonode.h16
-rw-r--r--usr/src/uts/common/sys/fs/hyprlofs.h91
-rw-r--r--usr/src/uts/common/sys/fs/hyprlofs_info.h174
-rw-r--r--usr/src/uts/common/sys/fs/sdev_impl.h1
-rw-r--r--usr/src/uts/common/sys/fs/tmp.h54
-rw-r--r--usr/src/uts/common/sys/fss.h1
-rw-r--r--usr/src/uts/common/sys/fx.h10
-rw-r--r--usr/src/uts/common/sys/gsqueue.h59
-rw-r--r--usr/src/uts/common/sys/ia.h1
-rw-r--r--usr/src/uts/common/sys/inotify.h153
-rw-r--r--usr/src/uts/common/sys/ipc_impl.h2
-rw-r--r--usr/src/uts/common/sys/ipd.h4
-rw-r--r--usr/src/uts/common/sys/iso/signal_iso.h3
-rw-r--r--usr/src/uts/common/sys/klwp.h11
-rw-r--r--usr/src/uts/common/sys/mac_client.h1
-rw-r--r--usr/src/uts/common/sys/mac_client_impl.h1
-rw-r--r--usr/src/uts/common/sys/mac_client_priv.h3
-rw-r--r--usr/src/uts/common/sys/mac_flow.h22
-rw-r--r--usr/src/uts/common/sys/mman.h1
-rw-r--r--usr/src/uts/common/sys/mntent.h2
-rw-r--r--usr/src/uts/common/sys/netconfig.h1
-rw-r--r--usr/src/uts/common/sys/neti.h2
-rw-r--r--usr/src/uts/common/sys/netstack.h3
-rw-r--r--usr/src/uts/common/sys/param.h2
-rw-r--r--usr/src/uts/common/sys/policy.h2
-rw-r--r--usr/src/uts/common/sys/poll_impl.h1
-rw-r--r--usr/src/uts/common/sys/proc.h10
-rw-r--r--usr/src/uts/common/sys/procfs.h7
-rw-r--r--usr/src/uts/common/sys/ptms.h19
-rw-r--r--usr/src/uts/common/sys/refhash.h8
-rw-r--r--usr/src/uts/common/sys/resource.h1
-rw-r--r--usr/src/uts/common/sys/rt.h11
-rw-r--r--usr/src/uts/common/sys/scsi/adapters/mpt_sas/mpi/mpi2_pci.h147
-rw-r--r--usr/src/uts/common/sys/scsi/generic/inquiry.h7
-rw-r--r--usr/src/uts/common/sys/scsi/targets/sddef.h10
-rw-r--r--usr/src/uts/common/sys/shm.h5
-rw-r--r--usr/src/uts/common/sys/shm_impl.h17
-rw-r--r--usr/src/uts/common/sys/signal.h5
-rw-r--r--usr/src/uts/common/sys/socket.h8
-rw-r--r--usr/src/uts/common/sys/socketvar.h27
-rw-r--r--usr/src/uts/common/sys/sockfilter.h10
-rw-r--r--usr/src/uts/common/sys/squeue.h14
-rw-r--r--usr/src/uts/common/sys/squeue_impl.h2
-rw-r--r--usr/src/uts/common/sys/stream.h1
-rw-r--r--usr/src/uts/common/sys/sunddi.h6
-rw-r--r--usr/src/uts/common/sys/systrace.h13
-rw-r--r--usr/src/uts/common/sys/termios.h18
-rw-r--r--usr/src/uts/common/sys/thread.h9
-rw-r--r--usr/src/uts/common/sys/time.h9
-rw-r--r--usr/src/uts/common/sys/timer.h26
-rw-r--r--usr/src/uts/common/sys/ts.h2
-rw-r--r--usr/src/uts/common/sys/uadmin.h3
-rw-r--r--usr/src/uts/common/sys/uio.h12
-rw-r--r--usr/src/uts/common/sys/user.h10
-rw-r--r--usr/src/uts/common/sys/vm.h3
-rw-r--r--usr/src/uts/common/sys/vm_usage.h7
-rw-r--r--usr/src/uts/common/sys/vmsystm.h8
-rw-r--r--usr/src/uts/common/sys/vnd.h141
-rw-r--r--usr/src/uts/common/sys/vnd_errno.h72
-rw-r--r--usr/src/uts/common/sys/vnode.h56
-rw-r--r--usr/src/uts/common/sys/zfd.h78
-rw-r--r--usr/src/uts/common/sys/zone.h201
-rw-r--r--usr/src/uts/common/syscall/brandsys.c8
-rw-r--r--usr/src/uts/common/syscall/chdir.c29
-rw-r--r--usr/src/uts/common/syscall/fcntl.c3
-rw-r--r--usr/src/uts/common/syscall/memcntl.c8
-rw-r--r--usr/src/uts/common/syscall/open.c8
-rw-r--r--usr/src/uts/common/syscall/poll.c358
-rw-r--r--usr/src/uts/common/syscall/rusagesys.c1
-rw-r--r--usr/src/uts/common/syscall/stat.c2
-rw-r--r--usr/src/uts/common/syscall/sysconfig.c49
-rw-r--r--usr/src/uts/common/syscall/uadmin.c10
-rw-r--r--usr/src/uts/common/syscall/umount.c11
-rw-r--r--usr/src/uts/common/vm/hat.h10
-rw-r--r--usr/src/uts/common/vm/page.h7
-rw-r--r--usr/src/uts/common/vm/seg_vn.c11
-rw-r--r--usr/src/uts/common/vm/vm_as.c19
-rw-r--r--usr/src/uts/common/vm/vm_page.c11
-rw-r--r--usr/src/uts/common/vm/vm_pvn.c28
-rw-r--r--usr/src/uts/common/vm/vm_usage.c252
-rw-r--r--usr/src/uts/i86pc/Makefile.i86pc1
-rw-r--r--usr/src/uts/i86pc/dboot/dboot_printf.c4
-rw-r--r--usr/src/uts/i86pc/dboot/dboot_printf.h13
-rw-r--r--usr/src/uts/i86pc/dboot/dboot_startkern.c422
-rw-r--r--usr/src/uts/i86pc/dboot/dboot_xboot.h12
-rw-r--r--usr/src/uts/i86pc/io/mp_platform_common.c1
-rw-r--r--usr/src/uts/i86pc/io/psm/psm_common.c1
-rw-r--r--usr/src/uts/i86pc/ml/kpti_trampolines.s2
-rw-r--r--usr/src/uts/i86pc/ml/offsets.in1
-rw-r--r--usr/src/uts/i86pc/ml/syscall_asm_amd64.s166
-rw-r--r--usr/src/uts/i86pc/os/cpr_impl.c19
-rw-r--r--usr/src/uts/i86pc/os/ibft.c6
-rw-r--r--usr/src/uts/i86pc/os/lgrpplat.c14
-rw-r--r--usr/src/uts/i86pc/os/startup.c1
-rw-r--r--usr/src/uts/i86pc/os/trap.c12
-rw-r--r--usr/src/uts/i86pc/sys/apic.h2
-rw-r--r--usr/src/uts/i86pc/sys/comm_page.h1
-rw-r--r--usr/src/uts/i86pc/sys/vm_machparam.h6
-rw-r--r--usr/src/uts/i86pc/vm/hat_i86.c78
-rw-r--r--usr/src/uts/i86pc/vm/hment.c7
-rw-r--r--usr/src/uts/i86pc/vm/vm_machdep.c8
-rw-r--r--usr/src/uts/intel/Makefile6
-rw-r--r--usr/src/uts/intel/Makefile.files92
-rw-r--r--usr/src/uts/intel/Makefile.intel22
-rw-r--r--usr/src/uts/intel/Makefile.rules4
-rw-r--r--usr/src/uts/intel/amd64/krtld/kobj_reloc.c3
-rw-r--r--usr/src/uts/intel/bpf/Makefile2
-rw-r--r--usr/src/uts/intel/brand/lx/lx_archdep.c1720
-rw-r--r--usr/src/uts/intel/core_pcbe/Makefile4
-rw-r--r--usr/src/uts/intel/datafilt/Makefile63
-rw-r--r--usr/src/uts/intel/dld/Makefile1
-rw-r--r--usr/src/uts/intel/dls/Makefile1
-rw-r--r--usr/src/uts/intel/dtrace/fasttrap_isa.c15
-rw-r--r--usr/src/uts/intel/genassym/Makefile83
-rw-r--r--usr/src/uts/intel/genassym/offsets.in43
-rw-r--r--usr/src/uts/intel/gsqueue/Makefile41
-rw-r--r--usr/src/uts/intel/hyprlofs/Makefile74
-rw-r--r--usr/src/uts/intel/icmp/Makefile2
-rw-r--r--usr/src/uts/intel/inotify/Makefile61
-rw-r--r--usr/src/uts/intel/io/dktp/dcdev/dadk.c48
-rw-r--r--usr/src/uts/intel/io/ipmi/ipmivars.h1
-rw-r--r--usr/src/uts/intel/io/pci/pci_boot.c2
-rw-r--r--usr/src/uts/intel/io/scsi/targets/sd.conf33
-rw-r--r--usr/src/uts/intel/io/vmxnet/buildNumber.h12
-rw-r--r--usr/src/uts/intel/io/vmxnet/includeCheck.h159
-rw-r--r--usr/src/uts/intel/io/vmxnet/net.h220
-rw-r--r--usr/src/uts/intel/io/vmxnet/net_sg.h84
-rw-r--r--usr/src/uts/intel/io/vmxnet/vm_basic_types.h1037
-rw-r--r--usr/src/uts/intel/io/vmxnet/vm_device_version.h246
-rw-r--r--usr/src/uts/intel/io/vmxnet/vmnet_def.h91
-rw-r--r--usr/src/uts/intel/io/vmxnet/vmxnet.c2442
-rw-r--r--usr/src/uts/intel/io/vmxnet/vmxnet.conf24
-rw-r--r--usr/src/uts/intel/io/vmxnet/vmxnet2_def.h436
-rw-r--r--usr/src/uts/intel/io/vmxnet/vmxnet_def.h184
-rw-r--r--usr/src/uts/intel/ipf/Makefile3
-rw-r--r--usr/src/uts/intel/ipf/ipf.global-objs.debug6423
-rw-r--r--usr/src/uts/intel/iptun/Makefile1
-rw-r--r--usr/src/uts/intel/lx_brand/Makefile92
-rw-r--r--usr/src/uts/intel/lx_brand/Makefile.rules82
-rw-r--r--usr/src/uts/intel/lx_cgroup/Makefile52
-rw-r--r--usr/src/uts/intel/lx_cgroup/Makefile.rules18
-rw-r--r--usr/src/uts/intel/lx_devfs/Makefile52
-rw-r--r--usr/src/uts/intel/lx_devfs/Makefile.rules18
-rw-r--r--usr/src/uts/intel/lx_netlink/Makefile67
-rw-r--r--usr/src/uts/intel/lx_proc/Makefile101
-rw-r--r--usr/src/uts/intel/lx_proc/Makefile.rules32
-rw-r--r--usr/src/uts/intel/lx_ptm/Makefile80
-rw-r--r--usr/src/uts/intel/lx_sysfs/Makefile49
-rw-r--r--usr/src/uts/intel/lx_sysfs/Makefile.rules18
-rw-r--r--usr/src/uts/intel/lx_systrace/Makefile62
-rw-r--r--usr/src/uts/intel/lxautofs/Makefile102
-rw-r--r--usr/src/uts/intel/lxautofs/Makefile.rules32
-rw-r--r--usr/src/uts/intel/lxprocfs/Makefile80
-rw-r--r--usr/src/uts/intel/mac/Makefile3
-rw-r--r--usr/src/uts/intel/mac_ether/Makefile1
-rw-r--r--usr/src/uts/intel/mac_ib/Makefile1
-rw-r--r--usr/src/uts/intel/mac_wifi/Makefile1
-rw-r--r--usr/src/uts/intel/ml/modstubs.s10
-rw-r--r--usr/src/uts/intel/ml/swtch.s38
-rw-r--r--usr/src/uts/intel/nfp/Makefile76
-rw-r--r--usr/src/uts/intel/opteron_pcbe/Makefile2
-rw-r--r--usr/src/uts/intel/os/archdep.c24
-rw-r--r--usr/src/uts/intel/os/comm_page_util.c4
-rw-r--r--usr/src/uts/intel/os/cpuid.c6
-rw-r--r--usr/src/uts/intel/os/desctbls.c28
-rw-r--r--usr/src/uts/intel/os/device_policy19
-rw-r--r--usr/src/uts/intel/os/driver_aliases1850
-rw-r--r--usr/src/uts/intel/os/driver_classes29
-rw-r--r--usr/src/uts/intel/os/minor_perm270
-rw-r--r--usr/src/uts/intel/os/name_to_major313
-rw-r--r--usr/src/uts/intel/os/path_to_inst4
-rw-r--r--usr/src/uts/intel/os/sendsig.c53
-rw-r--r--usr/src/uts/intel/p4_pcbe/Makefile2
-rw-r--r--usr/src/uts/intel/pcbe/core_pcbe.c2
-rw-r--r--usr/src/uts/intel/sys/machbrand.h12
-rw-r--r--usr/src/uts/intel/sys/segments.h4
-rw-r--r--usr/src/uts/intel/sys/ucontext.h15
-rw-r--r--usr/src/uts/intel/syscall/getcontext.c72
-rw-r--r--usr/src/uts/intel/vmxnet/Makefile85
-rw-r--r--usr/src/uts/intel/vnd/Makefile52
-rw-r--r--usr/src/uts/intel/zfd/Makefile40
-rw-r--r--usr/src/uts/sfmmu/vm/hat_sfmmu.c5
-rw-r--r--usr/src/uts/sparc/Makefile.sparc2
-rw-r--r--usr/src/uts/sparc/bpf/Makefile3
-rw-r--r--usr/src/uts/sparc/datafilt/Makefile62
-rw-r--r--usr/src/uts/sparc/dld/Makefile1
-rw-r--r--usr/src/uts/sparc/dls/Makefile1
-rw-r--r--usr/src/uts/sparc/icmp/Makefile1
-rw-r--r--usr/src/uts/sparc/inotify/Makefile61
-rw-r--r--usr/src/uts/sparc/ipf/ipf.global-objs.debug6419
-rw-r--r--usr/src/uts/sparc/mac_ether/Makefile1
-rw-r--r--usr/src/uts/sparc/mac_ib/Makefile1
-rw-r--r--usr/src/uts/sparc/mac_wifi/Makefile1
-rw-r--r--usr/src/uts/sparc/syscall/getcontext.c31
-rw-r--r--usr/src/uts/sparc/zfd/Makefile42
-rw-r--r--usr/src/uts/sun4u/io/pci/pcisch.c5
1784 files changed, 277986 insertions, 8626 deletions
diff --git a/.gitignore b/.gitignore
index 617842941e..c4a7e96451 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,19 +7,9086 @@ ncscope.out
cscope.in.out
cscope.po.out
.make.state*
+*.o
+*.po
+*.so
+*.so.1
+*.a
+*.pyc
+/closed
+/illumos.sh
/log
+/nightly
+/on-closed-bins-nd.i386.tar.bz2
+/on-closed-bins.i386.tar.bz2
/packages
/proto
/webrev
-/usr/src/.build.tstamp
-/usr/src/ELF-data.*
-/usr/src/check-*.out
-/usr/src/clobber-*.out
-/usr/src/install-*.out
-/usr/src/lint-*.out
-/usr/src/lint-noise-*.out
-/usr/src/lint-noise-*.ref
-/usr/src/noise-*.out
-/usr/src/noise-*.ref
-/usr/src/tools/clobber-*.out
-/usr/src/tools/install-*.out
+.make.state
+.build.tstamp
+usr/src/ELF-data.*
+usr/src/check-i386.out
+usr/src/cmd/Adm/sun/motd
+usr/src/cmd/Adm/sun/smbpasswd
+usr/src/cmd/abi/appcert/etc/etc.alt
+usr/src/cmd/abi/appcert/etc/etc.scoped
+usr/src/cmd/abi/appcert/etc/etc.tweaks
+usr/src/cmd/abi/appcert/etc/etc.warn
+usr/src/cmd/abi/appcert/scripts/abi_index
+usr/src/cmd/abi/appcert/scripts/appcert
+usr/src/cmd/abi/appcert/scripts/symcheck
+usr/src/cmd/abi/appcert/scripts/symprof
+usr/src/cmd/abi/appcert/scripts/symreport
+usr/src/cmd/abi/appcert/static_prof/i386/static_prof
+usr/src/cmd/abi/apptracecmd/apptrace
+usr/src/cmd/abi/spectrans/spec2map/i386/spec2map
+usr/src/cmd/abi/spectrans/spec2trace/i386/spec2trace
+usr/src/cmd/acct/acct
+usr/src/cmd/acct/acctcms
+usr/src/cmd/acct/acctcom
+usr/src/cmd/acct/acctcon
+usr/src/cmd/acct/acctcon1
+usr/src/cmd/acct/acctcon2
+usr/src/cmd/acct/acctdisk
+usr/src/cmd/acct/acctdusg
+usr/src/cmd/acct/acctmerg
+usr/src/cmd/acct/accton
+usr/src/cmd/acct/acctprc
+usr/src/cmd/acct/acctprc1
+usr/src/cmd/acct/acctprc2
+usr/src/cmd/acct/acctwtmp
+usr/src/cmd/acct/chargefee
+usr/src/cmd/acct/ckpacct
+usr/src/cmd/acct/closewtmp
+usr/src/cmd/acct/dodisk
+usr/src/cmd/acct/fwtmp
+usr/src/cmd/acct/holidays
+usr/src/cmd/acct/lastlogin
+usr/src/cmd/acct/monacct
+usr/src/cmd/acct/nulladm
+usr/src/cmd/acct/prctmp
+usr/src/cmd/acct/prdaily
+usr/src/cmd/acct/prtacct
+usr/src/cmd/acct/remove
+usr/src/cmd/acct/runacct
+usr/src/cmd/acct/shutacct
+usr/src/cmd/acct/startup
+usr/src/cmd/acct/turnacct
+usr/src/cmd/acct/utmp2wtmp
+usr/src/cmd/acct/wtmpfix
+usr/src/cmd/acctadm/acctadm
+usr/src/cmd/acpihpd/THIRDPARTYLICENSE
+usr/src/cmd/acpihpd/i386/acpihpd
+usr/src/cmd/adbgen/i386/adbgen
+usr/src/cmd/adbgen/i386/adbgen1
+usr/src/cmd/adbgen/i386/adbgen3
+usr/src/cmd/adbgen/i386/adbgen4
+usr/src/cmd/addbadsec/addbadsec
+usr/src/cmd/allocate/add_allocatable
+usr/src/cmd/allocate/allocate
+usr/src/cmd/allocate/allscripts.sh
+usr/src/cmd/allocate/audio
+usr/src/cmd/allocate/audio_clean
+usr/src/cmd/allocate/disk_clean
+usr/src/cmd/allocate/dminfo
+usr/src/cmd/allocate/fd0
+usr/src/cmd/allocate/mkdevalloc
+usr/src/cmd/allocate/sr0
+usr/src/cmd/allocate/st0
+obj32
+obj64
+debug32
+debug64
+usr/src/cmd/allocate/st1
+usr/src/cmd/allocate/st_clean
+usr/src/cmd/amt/amd64/amt
+usr/src/cmd/amt/amt
+usr/src/cmd/amt/i386/amt
+usr/src/cmd/arch/arch
+usr/src/cmd/asa/asa
+usr/src/cmd/ast/msgcc/msgcc
+usr/src/cmd/ast/msgcc/msgcpp
+usr/src/cmd/ast/msgcc/msgcvt
+usr/src/cmd/ast/msgcc/msggen
+usr/src/cmd/ast/msgcc/msgget
+usr/src/cmd/audio/audioconvert/audioconvert
+usr/src/cmd/audio/audioctl/audioctl
+usr/src/cmd/audio/audioplay/audioplay
+usr/src/cmd/audio/audiorecord/audiorecord
+usr/src/cmd/audio/audiotest/audiotest
+usr/src/cmd/audit/audit
+usr/src/cmd/audit_warn/audit_warn
+usr/src/cmd/auditconfig/auditconfig
+usr/src/cmd/auditd/auditd
+usr/src/cmd/auditrecord/audit_record_attr
+usr/src/cmd/auditrecord/auditrecord
+usr/src/cmd/auditrecord/filter_txt
+usr/src/cmd/auditrecord/mkmsg
+usr/src/cmd/auditreduce/auditreduce
+usr/src/cmd/auditset/svc-auditset
+usr/src/cmd/auditstat/auditstat
+usr/src/cmd/auths/auths
+usr/src/cmd/autopush/autopush
+usr/src/cmd/availdevs/availdevs
+usr/src/cmd/avs/dsbitmap/dsbitmap
+usr/src/cmd/avs/dscfg/dscfg
+usr/src/cmd/avs/dscfg/dscfgadm
+usr/src/cmd/avs/dscfglockd/dscfgcli
+usr/src/cmd/avs/dscfglockd/dscfglockd
+usr/src/cmd/avs/dsstat/dsstat
+usr/src/cmd/avs/dsw/etc/ii
+usr/src/cmd/avs/dsw/etc/ii.cluster
+usr/src/cmd/avs/dsw/iiadm
+usr/src/cmd/avs/dsw/iiboot
+usr/src/cmd/avs/dsw/iicpbmp
+usr/src/cmd/avs/dsw/iicpshd
+usr/src/cmd/avs/errgen/errgen
+usr/src/cmd/avs/ncall/ncalladm
+usr/src/cmd/avs/nsctl/nscadm
+usr/src/cmd/avs/nsctl/nskernd
+usr/src/cmd/avs/rdc/etc/rdc
+usr/src/cmd/avs/rdc/etc/rdc.cluster
+usr/src/cmd/avs/rdc/etc/rdcfinish
+usr/src/cmd/avs/rdc/sndradm
+usr/src/cmd/avs/rdc/sndrboot
+usr/src/cmd/avs/rdc/sndrd
+usr/src/cmd/avs/rdc/sndrsyncd
+usr/src/cmd/avs/sdbc/etc/dscfg_reconfigure.cluster
+usr/src/cmd/avs/sdbc/etc/scm
+usr/src/cmd/avs/sdbc/scmadm
+usr/src/cmd/avs/sdbc/sd_diag
+usr/src/cmd/avs/sdbc/sd_stats
+usr/src/cmd/avs/sv/etc/sv
+usr/src/cmd/avs/sv/etc/sv.cluster
+usr/src/cmd/avs/sv/svadm
+usr/src/cmd/avs/sv/svboot
+usr/src/cmd/avs/svc/nws_ii.xml
+usr/src/cmd/avs/svc/nws_rdc.xml
+usr/src/cmd/avs/svc/nws_rdcsyncd.xml
+usr/src/cmd/avs/svc/nws_scm.xml
+usr/src/cmd/avs/svc/nws_sv.xml
+usr/src/cmd/awk/awk.g.c
+usr/src/cmd/awk/awk.lx.c
+usr/src/cmd/awk/maketab
+usr/src/cmd/awk/nawk
+usr/src/cmd/awk/proctab.c
+usr/src/cmd/awk/y.tab.h
+usr/src/cmd/awk_xpg4/awk.c
+usr/src/cmd/awk_xpg4/awk.xpg4
+usr/src/cmd/awk_xpg4/y.tab.h
+usr/src/cmd/backup/dump/dumpdates
+usr/src/cmd/backup/dump/ufsdump
+usr/src/cmd/backup/lib/getdate.c
+usr/src/cmd/backup/restore/ufsrestore
+usr/src/cmd/banner/banner
+usr/src/cmd/bart/bart
+usr/src/cmd/basename/basename
+usr/src/cmd/basename/basename.xpg4
+usr/src/cmd/bc/bc
+usr/src/cmd/bc/bc.xpg6
+usr/src/cmd/bc/lib.b
+usr/src/cmd/bdiff/bdiff
+usr/src/cmd/beadm/beadm
+usr/src/cmd/bfs/bfs
+usr/src/cmd/biosdev/biosdev
+usr/src/cmd/bnu/bnuconvert
+usr/src/cmd/bnu/bnuconvert.cat
+usr/src/cmd/bnu/ct
+usr/src/cmd/bnu/ct.cat
+usr/src/cmd/bnu/cu
+usr/src/cmd/bnu/cu.cat
+usr/src/cmd/bnu/in.uucpd
+usr/src/cmd/bnu/in.uucpd.cat
+usr/src/cmd/bnu/remote.unknown
+usr/src/cmd/bnu/remote.unknown.cat
+usr/src/cmd/bnu/uucheck
+usr/src/cmd/bnu/uucheck.cat
+usr/src/cmd/bnu/uucico
+usr/src/cmd/bnu/uucico.cat
+usr/src/cmd/bnu/uucleanup
+usr/src/cmd/bnu/uucleanup.cat
+usr/src/cmd/bnu/uucp
+usr/src/cmd/bnu/uucp.cat
+usr/src/cmd/bnu/uudecode
+usr/src/cmd/bnu/uudecode.cat
+usr/src/cmd/bnu/uuencode
+usr/src/cmd/bnu/uuencode.cat
+usr/src/cmd/bnu/uuglist
+usr/src/cmd/bnu/uuglist.cat
+usr/src/cmd/bnu/uuname
+usr/src/cmd/bnu/uuname.cat
+usr/src/cmd/bnu/uusched
+usr/src/cmd/bnu/uusched.cat
+usr/src/cmd/bnu/uustat
+usr/src/cmd/bnu/uustat.cat
+usr/src/cmd/bnu/uux
+usr/src/cmd/bnu/uux.cat
+usr/src/cmd/bnu/uuxqt
+usr/src/cmd/bnu/uuxqt.cat
+usr/src/cmd/boot/bootadm/bootadm
+usr/src/cmd/boot/fiocompress/fiocompress
+usr/src/cmd/boot/installgrub/installgrub
+usr/src/cmd/boot/mbr/mbr
+usr/src/cmd/boot/scripts/boot-archive-update
+usr/src/cmd/boot/scripts/create_diskmap
+usr/src/cmd/boot/scripts/create_ramdisk
+usr/src/cmd/boot/scripts/extract_boot_filelist
+usr/src/cmd/boot/scripts/root_archive
+usr/src/cmd/boot/scripts/update_grub
+usr/src/cmd/boot/symdef/symdef
+usr/src/cmd/busstat/busstat
+usr/src/cmd/cal/cal
+usr/src/cmd/cal/cal.dc
+usr/src/cmd/calendar/calendar
+usr/src/cmd/calendar/calprog
+usr/src/cmd/captoinfo/captoinfo
+usr/src/cmd/cat/cat
+usr/src/cmd/cdrw/cdrw
+usr/src/cmd/cfgadm/cfgadm
+usr/src/cmd/cfgadm/cfgadm.dc
+usr/src/cmd/checkeq/checkeq
+usr/src/cmd/checknr/checknr
+usr/src/cmd/chgrp/chgrp
+usr/src/cmd/chgrp/chgrp.xpg4
+usr/src/cmd/chmod/chmod
+usr/src/cmd/chown/chown
+usr/src/cmd/chown/chown.xpg4
+usr/src/cmd/chroot/chroot
+usr/src/cmd/clear/clear
+usr/src/cmd/clinfo/clinfo
+usr/src/cmd/cmd-crypto/cryptoadm/cryptoadm
+usr/src/cmd/cmd-crypto/decrypt/amd64/decrypt
+usr/src/cmd/cmd-crypto/decrypt/i386/decrypt
+usr/src/cmd/cmd-crypto/digest/amd64/digest
+usr/src/cmd/cmd-crypto/digest/i386/digest
+usr/src/cmd/cmd-crypto/elfsign/elfsign
+usr/src/cmd/cmd-crypto/etc/certs/SUNWObjectCA
+usr/src/cmd/cmd-crypto/kmfcfg/kmfcfg
+usr/src/cmd/cmd-crypto/kmscfg/kmscfg
+usr/src/cmd/cmd-crypto/pktool/pktool
+usr/src/cmd/cmd-crypto/tpmadm/tpmadm
+usr/src/cmd/cmd-inet/etc/datemsk.ndpd
+usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmtd
+usr/src/cmd/cmd-inet/lib/netcfgd/netcfgd
+usr/src/cmd/cmd-inet/lib/nwamd/nwamd
+usr/src/cmd/cmd-inet/sbin/dhcpagent/dhcpagent
+usr/src/cmd/cmd-inet/sbin/dhcpinfo/dhcpinfo
+usr/src/cmd/cmd-inet/sbin/ifparse/ifparse
+usr/src/cmd/cmd-inet/sbin/netstrategy/netstrategy
+usr/src/cmd/cmd-inet/usr.bin/chat/chat
+usr/src/cmd/cmd-inet/usr.bin/dns-sd
+usr/src/cmd/cmd-inet/usr.bin/finger
+usr/src/cmd/cmd-inet/usr.bin/ftp/ftp
+usr/src/cmd/cmd-inet/usr.bin/nc/nc
+usr/src/cmd/cmd-inet/usr.bin/nca/ncab2clf
+usr/src/cmd/cmd-inet/usr.bin/netstat/netstat
+usr/src/cmd/cmd-inet/usr.bin/pppd/pppd
+usr/src/cmd/cmd-inet/usr.bin/pppdump/THIRDPARTYLICENSE
+usr/src/cmd/cmd-inet/usr.bin/pppdump/pppdump
+usr/src/cmd/cmd-inet/usr.bin/pppstats/pppstats
+usr/src/cmd/cmd-inet/usr.bin/rcp
+usr/src/cmd/cmd-inet/usr.bin/rdate
+usr/src/cmd/cmd-inet/usr.bin/rdist/rdist
+usr/src/cmd/cmd-inet/usr.bin/rlogin
+usr/src/cmd/cmd-inet/usr.bin/rsh
+usr/src/cmd/cmd-inet/usr.bin/ruptime
+usr/src/cmd/cmd-inet/usr.bin/rwho
+usr/src/cmd/cmd-inet/usr.bin/talk/talk
+usr/src/cmd/cmd-inet/usr.bin/telnet/telnet
+usr/src/cmd/cmd-inet/usr.bin/tftp/tftp
+usr/src/cmd/cmd-inet/usr.bin/whois
+usr/src/cmd/cmd-inet/usr.lib/bridged/bridged
+usr/src/cmd/cmd-inet/usr.lib/dhcp/dhcpconfig
+usr/src/cmd/cmd-inet/usr.lib/dhcp/dhtadm
+usr/src/cmd/cmd-inet/usr.lib/dhcp/pntadm
+usr/src/cmd/cmd-inet/usr.lib/dsvclockd/dsvclockd
+usr/src/cmd/cmd-inet/usr.lib/ilbd/ilb/ilb_probe
+usr/src/cmd/cmd-inet/usr.lib/ilbd/ilbd
+usr/src/cmd/cmd-inet/usr.lib/in.chargend/in.chargend
+usr/src/cmd/cmd-inet/usr.lib/in.daytimed/in.daytimed
+usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/in.dhcpd
+usr/src/cmd/cmd-inet/usr.lib/in.discardd/in.discardd
+usr/src/cmd/cmd-inet/usr.lib/in.echod/in.echod
+usr/src/cmd/cmd-inet/usr.lib/in.mpathd/in.mpathd
+usr/src/cmd/cmd-inet/usr.lib/in.ndpd/in.ndpd
+usr/src/cmd/cmd-inet/usr.lib/in.ripngd/in.ripngd
+usr/src/cmd/cmd-inet/usr.lib/in.timed/in.timed
+usr/src/cmd/cmd-inet/usr.lib/inetd/inetd
+usr/src/cmd/cmd-inet/usr.lib/inetd/inetd-upgrade
+usr/src/cmd/cmd-inet/usr.lib/mdnsd/mdnsd
+usr/src/cmd/cmd-inet/usr.lib/ncaconfd/ncaconfd
+usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoec
+usr/src/cmd/cmd-inet/usr.lib/pppoe/pppoed
+usr/src/cmd/cmd-inet/usr.lib/slpd/slpd
+usr/src/cmd/cmd-inet/usr.lib/vrrpd/vrrpd
+usr/src/cmd/cmd-inet/usr.lib/wanboot/encr/encr
+usr/src/cmd/cmd-inet/usr.lib/wanboot/hmac/hmac
+usr/src/cmd/cmd-inet/usr.lib/wanboot/ickey/ickey
+usr/src/cmd/cmd-inet/usr.lib/wanboot/keygen/keygen
+usr/src/cmd/cmd-inet/usr.lib/wanboot/keymgmt/keymgmt
+usr/src/cmd/cmd-inet/usr.lib/wanboot/netbootinfo/netbootinfo
+usr/src/cmd/cmd-inet/usr.lib/wanboot/p12split/p12split
+usr/src/cmd/cmd-inet/usr.lib/wanboot/wanboot-cgi/wanboot-cgi
+usr/src/cmd/cmd-inet/usr.lib/wpad/wpad
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/SUNWbinfiles.jar
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/SUNWbinfiles.manifest
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/SUNWfiles.jar
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/SUNWfiles.manifest
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/bin/dhcpmgr
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/Bridge.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/BridgeException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/DsymException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ExistsException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/HostExistsException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/InvalidPathException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/InvalidRsrcException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoDefaultsException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoEntryException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoHostsEntryException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NoTableException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/NotRunningException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/TableExistsException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/WordexpException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/bridge/com_sun_dhcpmgr_bridge_Bridge.h
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Console.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliFunction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliOption.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliOptions.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliPrint.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/DhcpCliProgram.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Format.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/GetOpt.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/GetSubOpt.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/common/Util.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/DhcpBatch.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/DhcpCommand.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpbatch/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureBootp.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureDhcp.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureNetwork.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService$Action.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService$ActionDisable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService$ActionEnable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService$ActionImpl.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService$ActionQuery.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService$ActionReenable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConfigureService.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ConvertDataStore.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/DhcpCfg.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/DhcpCfgFunction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ExportData.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/IPAddressList.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ImportData.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter$Action.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter$ActionDelete.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter$ActionGet.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter$ActionGetAll.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter$ActionImpl.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter$ActionSet.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/ServerParameter.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhcpconfig/UnconfigureDhcp.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/AddEntry.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/CreateTable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DeleteEntry.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdm.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdmBatch.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DhtAdmFunction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/DisplayTable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ModifyEntry.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/RemoveTable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/dhtadm/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/AddClientEntry.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/CreateNetworkTable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/DeleteClientEntry.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/DisplayNetworkTable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ListNetworkTables.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ModifyClientEntry.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdm.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdmBatch.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/PntAdmFunction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/RemoveNetworkTable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/cli/pntadm/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$10.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$11.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$4.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$5.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$6$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$6.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$7$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$7.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$8.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$9.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$AddressLoader$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$AddressLoader.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$AddressTableCellRenderer.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$AddressTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$DialogListener.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView$NetworkListModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressView.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$2$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$Address.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$ConfigureStep$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$ConfigureStep$MacroListModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$ConfigureStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$ConfirmStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$LeaseStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$NumberStep$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$NumberStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$ReviewStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$ServerStep$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$ServerStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard$WizardTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/AddressWizard.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$DnsStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$LeaseStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$NetTypeStep$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$NetTypeStep$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$NetTypeStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$NetworkStep$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$NetworkStep$NetworkComboBoxEditor.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$NetworkStep$NetworkListModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$NetworkStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$NisStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard$ReviewStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigWizard.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigureChoiceDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConfigureRelayDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConvertWizard$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConvertWizard$2$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConvertWizard$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConvertWizard$ReviewStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConvertWizard$SaveTablesStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ConvertWizard.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateAddressDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateAddressDialog$MacroListModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateAddressDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateMacroDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateMacroDialog$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateMacroDialog$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateMacroDialog$4.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateMacroDialog$MacroTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateMacroDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog$4.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog$5.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog$6.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog$ClassListModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/CreateOptionDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModule.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModuleEvent.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSModuleListener.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard$DSConf.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard$DSConfButton.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard$DSConfList.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard$DatastoreModuleStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard$DatastoreStep$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard$DatastoreStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DSWizard.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DataManager.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteAddressDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteAddressDialog$AddressTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteAddressDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteMacroDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog$4.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog$5.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog$NetworkListModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteNetworksDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DeleteOptionDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$ConversionTransition.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$ConvertAction$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$ConvertAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$DisableAction$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$DisableAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$EnableAction$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$EnableAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$ExportAction$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$ExportAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$HowToAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$ImportAction$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$ImportAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$IndexAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$ModifyServiceAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$OverviewAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$RestartAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$ServiceAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$StartAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$StopAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$UnconfigureServiceAction$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$UnconfigureServiceAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet$WizardTransition.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrApplet.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcpmgrDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcptabNameDocument.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DhcptabNameField.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DialogActions.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/DisableServiceDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ErrorTable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ErrorTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$ErrorDisplay.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$FileStep$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$FileStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$MacroStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$NetworkStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$OptionStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$OverwritePrompter.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$ReviewStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard$WarningDisplay.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ExportWizard.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard$ErrorDisplay.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard$LocationStep$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard$LocationStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard$ReviewStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ImportWizard.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroLoader$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroLoader.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroNameField$MacroNameDocument.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroNameField.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView$4.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView$DialogListener.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView$MacroTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView$MacroTreeModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView$MacroTreeNode.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView$MacroTreeRootNode.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MacroView.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ModifyAddressesDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ModifyAddressesDialog$MacroListModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ModifyAddressesDialog$ServerListModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ModifyAddressesDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MultipleOperationDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MultipleOperationDialog$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/MultipleOperationDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionNameField$OptionNameDocument.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionNameField.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView$4.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView$5.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView$DialogListener.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView$OptionLoader$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView$OptionLoader.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView$OptionTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/OptionView.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/RelayView.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ReleaseAddressDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ReleaseAddressDialog$AddressTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ReleaseAddressDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWModule$PathListener.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWModule.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWbinfiles/SUNWbinfiles.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SUNWfiles/SUNWfiles.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SelectOptionDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SelectOptionDialog$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SelectOptionDialog$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SelectOptionDialog$OptionTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/SelectOptionDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog$4.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog$5.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog$6.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog$7.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog$8.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog$InterfaceTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ServerOptionsDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/UnconfigureDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/UnconfigureDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ViewMacroDialog$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ViewMacroDialog$MacroTableModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/client/ViewMacroDialog.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ExportController.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Exporter.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ImportController.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/Importer.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/common/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ActionError.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/AsciiOptionValue.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/BogusOptionValue.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/BooleanOptionValue.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientFlagType.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientFlagTypes.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpClientRecord.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpConfigOpts.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpConfigOpts.java
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpDatastore.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpResource.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcpdOptions.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/DhcptabRecord.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ExportHeader.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPAddress.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPInterface.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IPOptionValue.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/IncludeOptionValue.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Macro.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Network.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/NumberOptionValue.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/NumberValue.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OctetOptionValue.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/Option.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionContext.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionType.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionValue.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionValueFactory.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/OptionsTable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/StandardOptions$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/StandardOptions.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/ValidationException.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/Qualifier.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierAnd.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierArray.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierBoolean.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierEnum.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierFQDN.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIPv4.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIPv6.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierImpl.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierInteger.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIntegerEnum.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierIntegerRange.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierOr.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierRange.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierString.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierStringEnum.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierType.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/data/qualifier/QualifierTypeImpl.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpMgr.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpMgrImpl.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpNetMgr.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpNetMgrImpl.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpServiceMgr.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcpServiceMgrImpl.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcptabMgr.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/server/DhcptabMgrImpl.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ALIGNMENT.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/AutosizingTable.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonLayout.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanel$ButtonAdaptor.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ButtonPanelListener.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/DownButton.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ExtendedCellRenderer.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/FieldLayout$Row.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/FieldLayout.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/FixedSelectionModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HelpIds.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HostnameField$HostnameDocument.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/HostnameField.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressField$IPAddressDocument.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressField.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressList$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressList$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressList$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressList$IPAddressListModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IPAddressList.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ImageButton.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IntegerField$IntegerDocument.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/IntegerField.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/LeftButton.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ListPair$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ListPair$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ListPair$3.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ListPair$4.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ListPair.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ListPairLayout.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame$CreateAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame$DeleteAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame$DuplicateAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame$ExitAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame$FindPanel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame$PropertiesAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame$StatusBar.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame$UpdateAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame$ViewSelectionListener.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MainFrame.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Mnemonic.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/MnemonicAction.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NextButton.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NoSpaceField$NoSpaceDocument.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/NoSpaceField.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/OurListModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/PreviousButton.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProgressManager$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProgressManager.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ProportionalLayout.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/ResourceStrings.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/RightButton.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SelectionListener.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SortedHeaderRenderer.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SwingWorker$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SwingWorker$2.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/SwingWorker.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableMap.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableSorter$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/TableSorter.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/UpButton.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/VerticalButtonLayout.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/View.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/WizButtonListener.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard$1.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard$1MyTextArea.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard$WizButtonPanel$ButtonAdaptor.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard$WizButtonPanel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard$WizContentsPanel$ContentsModel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard$WizContentsPanel$ContentsRenderer.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard$WizContentsPanel$MyList.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard$WizContentsPanel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard$WizStepPanel.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/Wizard.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/com/sun/dhcpmgr/ui/WizardStep.class
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/dhcpcli.jar
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/dhcpcommon.jar
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/dhcpmgr.jar
+usr/src/cmd/cmd-inet/usr.sadm/dhcpmgr/dhcpsvc.jar
+usr/src/cmd/cmd-inet/usr.sbin/6to4relay
+usr/src/cmd/cmd-inet/usr.sbin/THIRDPARTYLICENSE.arp
+usr/src/cmd/cmd-inet/usr.sbin/arp
+usr/src/cmd/cmd-inet/usr.sbin/bootconfchk/bootconfchk
+usr/src/cmd/cmd-inet/usr.sbin/dhcpconfig
+usr/src/cmd/cmd-inet/usr.sbin/dhtadm
+usr/src/cmd/cmd-inet/usr.sbin/gettable
+usr/src/cmd/cmd-inet/usr.sbin/hostconfig
+usr/src/cmd/cmd-inet/usr.sbin/htable/htable
+usr/src/cmd/cmd-inet/usr.sbin/htable/y.tab.h
+usr/src/cmd/cmd-inet/usr.sbin/if_mpadm
+usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig
+usr/src/cmd/cmd-inet/usr.sbin/ilbadm/ilbadm
+usr/src/cmd/cmd-inet/usr.sbin/in.comsat
+usr/src/cmd/cmd-inet/usr.sbin/in.fingerd
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ckconfig
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpaddhost
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcmd.c
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpconfig
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpcount
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftprestart
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpshut
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/ftpwho
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/in.ftpd
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privatepw
+usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/y.tab.h
+usr/src/cmd/cmd-inet/usr.sbin/in.rarpd
+usr/src/cmd/cmd-inet/usr.sbin/in.rdisc/in.rdisc
+usr/src/cmd/cmd-inet/usr.sbin/in.rexecd
+usr/src/cmd/cmd-inet/usr.sbin/in.rlogind
+usr/src/cmd/cmd-inet/usr.sbin/in.routed/in.routed
+usr/src/cmd/cmd-inet/usr.sbin/in.routed/rtquery
+usr/src/cmd/cmd-inet/usr.sbin/in.rshd
+usr/src/cmd/cmd-inet/usr.sbin/in.rwhod
+usr/src/cmd/cmd-inet/usr.sbin/in.talkd/in.talkd
+usr/src/cmd/cmd-inet/usr.sbin/in.telnetd
+usr/src/cmd/cmd-inet/usr.sbin/in.tftpd
+usr/src/cmd/cmd-inet/usr.sbin/inetadm/inetadm
+usr/src/cmd/cmd-inet/usr.sbin/inetconv/inetconv
+usr/src/cmd/cmd-inet/usr.sbin/ipaddrsel
+usr/src/cmd/cmd-inet/usr.sbin/ipadm/ipadm
+usr/src/cmd/cmd-inet/usr.sbin/ipmpstat/ipmpstat
+usr/src/cmd/cmd-inet/usr.sbin/ipqosconf/ipqosconf
+usr/src/cmd/cmd-inet/usr.sbin/ipsecutils/ikeadm
+usr/src/cmd/cmd-inet/usr.sbin/ipsecutils/ikecert
+usr/src/cmd/cmd-inet/usr.sbin/ipsecutils/ipsecalgs
+usr/src/cmd/cmd-inet/usr.sbin/ipsecutils/ipsecconf
+usr/src/cmd/cmd-inet/usr.sbin/ipsecutils/ipseckey
+usr/src/cmd/cmd-inet/usr.sbin/ndd
+usr/src/cmd/cmd-inet/usr.sbin/nwamadm/nwamadm
+usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg
+usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.tab.c
+usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_grammar.tab.h
+usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg_lex.c
+usr/src/cmd/cmd-inet/usr.sbin/ping/ping
+usr/src/cmd/cmd-inet/usr.sbin/pntadm
+usr/src/cmd/cmd-inet/usr.sbin/route
+usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm
+usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop
+usr/src/cmd/cmd-inet/usr.sbin/soconfig
+usr/src/cmd/cmd-inet/usr.sbin/sppptun/sppptun
+usr/src/cmd/cmd-inet/usr.sbin/syncinit
+usr/src/cmd/cmd-inet/usr.sbin/syncloop
+usr/src/cmd/cmd-inet/usr.sbin/syncstat
+usr/src/cmd/cmd-inet/usr.sbin/traceroute/traceroute
+usr/src/cmd/cmd-inet/usr.sbin/wanbootutil
+usr/src/cmd/cmd-inet/usr.sbin/wificonfig/wificonfig
+usr/src/cmd/col/col
+usr/src/cmd/compress/compress
+usr/src/cmd/consadm/i386/consadm
+usr/src/cmd/coreadm/coreadm
+usr/src/cmd/cpc/cpustat/cpustat
+usr/src/cmd/cpc/cputrack/amd64/cputrack
+usr/src/cmd/cpc/cputrack/i386/cputrack
+usr/src/cmd/cpio/cpio
+usr/src/cmd/cron/at
+usr/src/cmd/cron/at.xpg4
+usr/src/cmd/cron/atq
+usr/src/cmd/cron/atrm
+usr/src/cmd/cron/att1.c
+usr/src/cmd/cron/att1.h
+usr/src/cmd/cron/att2.c
+usr/src/cmd/cron/batch
+usr/src/cmd/cron/batch.xpg4
+usr/src/cmd/cron/cron
+usr/src/cmd/cron/crontab
+usr/src/cmd/cron/crontab.xpg4
+usr/src/cmd/cron/crontab.xpg6
+usr/src/cmd/crypt/crypt
+usr/src/cmd/csh/i386/csh
+usr/src/cmd/csh/sh.tconst.h
+usr/src/cmd/csplit/csplit
+usr/src/cmd/ctrun/ctrun
+usr/src/cmd/ctstat/ctstat
+usr/src/cmd/ctwatch/ctwatch
+usr/src/cmd/datadm/datadm
+usr/src/cmd/date/date
+usr/src/cmd/date/date.xpg4
+usr/src/cmd/dc/dc
+usr/src/cmd/dc/dc.xpg6
+usr/src/cmd/dd/dd
+usr/src/cmd/deroff/deroff
+usr/src/cmd/devctl/devctl
+usr/src/cmd/devfsadm/i386/devfsadm
+usr/src/cmd/devfsadm/i386/devlink.tab
+usr/src/cmd/devfsadm/plcysubr.c
+usr/src/cmd/devinfo/devinfo
+usr/src/cmd/devmgmt/cmds/devattr
+usr/src/cmd/devmgmt/cmds/devfree
+usr/src/cmd/devmgmt/cmds/devreserv
+usr/src/cmd/devmgmt/cmds/getdev
+usr/src/cmd/devmgmt/cmds/getdgrp
+usr/src/cmd/devmgmt/cmds/getvol
+usr/src/cmd/devmgmt/cmds/listdgrp
+usr/src/cmd/devmgmt/cmds/putdev
+usr/src/cmd/devmgmt/cmds/putdgrp
+usr/src/cmd/devmgmt/mkdtab/mkdtab
+usr/src/cmd/devprop/devprop
+usr/src/cmd/dfs.cmds/dfshares/dfshares
+usr/src/cmd/dfs.cmds/general/unshare
+usr/src/cmd/dfs.cmds/shareall/shareall
+usr/src/cmd/dfs.cmds/sharectl/i386/sharectl
+usr/src/cmd/dfs.cmds/sharectl/i386/shareutil.c
+usr/src/cmd/dfs.cmds/sharemgr/i386/sharemgr
+usr/src/cmd/dfs.cmds/unshareall/unshareall
+usr/src/cmd/diff/diff
+usr/src/cmd/diff/diff.dc
+usr/src/cmd/diff/diffh
+usr/src/cmd/diff3/diff3
+usr/src/cmd/diff3/diff3prog
+usr/src/cmd/diffmk/diffmk
+usr/src/cmd/dircmp/dircmp
+usr/src/cmd/dirname/dirname
+usr/src/cmd/dis/dis
+usr/src/cmd/diskscan/diskscan
+usr/src/cmd/dispadmin/FSSdispadmin
+usr/src/cmd/dispadmin/FXdispadmin
+usr/src/cmd/dispadmin/IAdispadmin
+usr/src/cmd/dispadmin/RTdispadmin
+usr/src/cmd/dispadmin/SDCdispadmin
+usr/src/cmd/dispadmin/TSdispadmin
+usr/src/cmd/dispadmin/dispadmin
+usr/src/cmd/dladm/dladm
+usr/src/cmd/dlmgmtd/dlmgmtd
+usr/src/cmd/dlstat/dlstat
+usr/src/cmd/dmesg/dmesg
+usr/src/cmd/dtrace/amd64/dtrace
+usr/src/cmd/dtrace/demo/applicat.d
+usr/src/cmd/dtrace/demo/badopen.d
+usr/src/cmd/dtrace/demo/begin.d
+usr/src/cmd/dtrace/demo/callout.d
+usr/src/cmd/dtrace/demo/clause.d
+usr/src/cmd/dtrace/demo/clear.d
+usr/src/cmd/dtrace/demo/countdown.d
+usr/src/cmd/dtrace/demo/counter.d
+usr/src/cmd/dtrace/demo/dateprof.d
+usr/src/cmd/dtrace/demo/delay.d
+usr/src/cmd/dtrace/demo/denorm.d
+usr/src/cmd/dtrace/demo/end.d
+usr/src/cmd/dtrace/demo/error.d
+usr/src/cmd/dtrace/demo/errorpath.d
+usr/src/cmd/dtrace/demo/find.d
+usr/src/cmd/dtrace/demo/firebird.d
+usr/src/cmd/dtrace/demo/hello.d
+usr/src/cmd/dtrace/demo/howlong.d
+usr/src/cmd/dtrace/demo/index.html
+usr/src/cmd/dtrace/demo/interp.d
+usr/src/cmd/dtrace/demo/interval.d
+usr/src/cmd/dtrace/demo/intr.d
+usr/src/cmd/dtrace/demo/iocpu.d
+usr/src/cmd/dtrace/demo/iosnoop.d
+usr/src/cmd/dtrace/demo/iothrough.d
+usr/src/cmd/dtrace/demo/iotime.d
+usr/src/cmd/dtrace/demo/ipio.d
+usr/src/cmd/dtrace/demo/ipproto.d
+usr/src/cmd/dtrace/demo/iprb.d
+usr/src/cmd/dtrace/demo/kstat.d
+usr/src/cmd/dtrace/demo/ksyms.d
+usr/src/cmd/dtrace/demo/libc.d
+usr/src/cmd/dtrace/demo/lquantize.d
+usr/src/cmd/dtrace/demo/lwptime.d
+usr/src/cmd/dtrace/demo/mkdemo
+usr/src/cmd/dtrace/demo/normalize.d
+usr/src/cmd/dtrace/demo/nscd.d
+usr/src/cmd/dtrace/demo/pri.d
+usr/src/cmd/dtrace/demo/printa.d
+usr/src/cmd/dtrace/demo/pritime.d
+usr/src/cmd/dtrace/demo/prof.d
+usr/src/cmd/dtrace/demo/profpri.d
+usr/src/cmd/dtrace/demo/progtime.d
+usr/src/cmd/dtrace/demo/putnext.d
+usr/src/cmd/dtrace/demo/qlen.d
+usr/src/cmd/dtrace/demo/qtime.d
+usr/src/cmd/dtrace/demo/renormalize.d
+usr/src/cmd/dtrace/demo/restest.d
+usr/src/cmd/dtrace/demo/ring.d
+usr/src/cmd/dtrace/demo/rtime.d
+usr/src/cmd/dtrace/demo/rwinfo.d
+usr/src/cmd/dtrace/demo/rwtime.d
+usr/src/cmd/dtrace/demo/sig.d
+usr/src/cmd/dtrace/demo/soffice.d
+usr/src/cmd/dtrace/demo/spec.d
+usr/src/cmd/dtrace/demo/specopen.d
+usr/src/cmd/dtrace/demo/ssd.d
+usr/src/cmd/dtrace/demo/syscall.d
+usr/src/cmd/dtrace/demo/tcp1stbyte.d
+usr/src/cmd/dtrace/demo/tcpbytes.d
+usr/src/cmd/dtrace/demo/tcpbytesstat.d
+usr/src/cmd/dtrace/demo/tcpconnlat.d
+usr/src/cmd/dtrace/demo/tcpio.d
+usr/src/cmd/dtrace/demo/tcpioflags.d
+usr/src/cmd/dtrace/demo/tcprst.d
+usr/src/cmd/dtrace/demo/tcpsnoop.d
+usr/src/cmd/dtrace/demo/tcpstate.d
+usr/src/cmd/dtrace/demo/tcptop.d
+usr/src/cmd/dtrace/demo/tick.d
+usr/src/cmd/dtrace/demo/ticktime.d
+usr/src/cmd/dtrace/demo/time.d
+usr/src/cmd/dtrace/demo/tracewrite.d
+usr/src/cmd/dtrace/demo/trunc.d
+usr/src/cmd/dtrace/demo/trussrw.d
+usr/src/cmd/dtrace/demo/udpbytes.d
+usr/src/cmd/dtrace/demo/udpbytesstat.d
+usr/src/cmd/dtrace/demo/udpio.d
+usr/src/cmd/dtrace/demo/udpsnoop.d
+usr/src/cmd/dtrace/demo/udptop.d
+usr/src/cmd/dtrace/demo/userfunc.d
+usr/src/cmd/dtrace/demo/whatfor.d
+usr/src/cmd/dtrace/demo/whatlock.d
+usr/src/cmd/dtrace/demo/where.d
+usr/src/cmd/dtrace/demo/whererun.d
+usr/src/cmd/dtrace/demo/whoexec.d
+usr/src/cmd/dtrace/demo/whofor.d
+usr/src/cmd/dtrace/demo/whoio.d
+usr/src/cmd/dtrace/demo/whopreempt.d
+usr/src/cmd/dtrace/demo/whoqueue.d
+usr/src/cmd/dtrace/demo/whosteal.d
+usr/src/cmd/dtrace/demo/whowrite.d
+usr/src/cmd/dtrace/demo/writes.d
+usr/src/cmd/dtrace/demo/writesbycmd.d
+usr/src/cmd/dtrace/demo/writesbycmdfd.d
+usr/src/cmd/dtrace/demo/writetime.d
+usr/src/cmd/dtrace/demo/writetimeq.d
+usr/src/cmd/dtrace/demo/xioctl.d
+usr/src/cmd/dtrace/demo/xterm.d
+usr/src/cmd/dtrace/demo/xwork.d
+usr/src/cmd/dtrace/i386/dtrace
+usr/src/cmd/dtrace/test/cmd/baddof/baddof
+usr/src/cmd/dtrace/test/cmd/badioctl/badioctl
+usr/src/cmd/dtrace/test/cmd/chkargs/amd64/chkargs
+usr/src/cmd/dtrace/test/cmd/chkargs/i386/chkargs
+usr/src/cmd/dtrace/test/cmd/jdtrace/classes/
+usr/src/cmd/dtrace/test/cmd/jdtrace/jdtrace
+usr/src/cmd/dtrace/test/cmd/jdtrace/lib/
+usr/src/cmd/dtrace/test/tst/common/io/tst.fds.exe
+usr/src/cmd/dtrace/test/tst/common/java_api/classes/
+usr/src/cmd/dtrace/test/tst/common/java_api/test.jar
+usr/src/cmd/dtrace/test/tst/common/java_api/tst.FunctionLookup.exe
+usr/src/cmd/dtrace/test/tst/common/java_api/tst.ProbeData.exe
+usr/src/cmd/dtrace/test/tst/common/nfs/tst.call.exe
+usr/src/cmd/dtrace/test/tst/common/nfs/tst.call3.exe
+usr/src/cmd/dtrace/test/tst/common/pid/tst.args1.exe
+usr/src/cmd/dtrace/test/tst/common/pid/tst.float.exe
+usr/src/cmd/dtrace/test/tst/common/pid/tst.fork.exe
+usr/src/cmd/dtrace/test/tst/common/pid/tst.gcc.exe
+usr/src/cmd/dtrace/test/tst/common/pid/tst.ret1.exe
+usr/src/cmd/dtrace/test/tst/common/pid/tst.ret2.exe
+usr/src/cmd/dtrace/test/tst/common/pid/tst.vfork.exe
+usr/src/cmd/dtrace/test/tst/common/pid/tst.weak1.exe
+usr/src/cmd/dtrace/test/tst/common/pid/tst.weak2.exe
+usr/src/cmd/dtrace/test/tst/common/proc/tst.sigwait.exe
+usr/src/cmd/dtrace/test/tst/common/profile-n/tst.ufuncsort.exe
+usr/src/cmd/dtrace/test/tst/common/raise/tst.raise1.exe
+usr/src/cmd/dtrace/test/tst/common/raise/tst.raise2.exe
+usr/src/cmd/dtrace/test/tst/common/raise/tst.raise3.exe
+usr/src/cmd/dtrace/test/tst/common/sdt/tst.sdtargs.exe
+usr/src/cmd/dtrace/test/tst/common/stop/tst.stop1.exe
+usr/src/cmd/dtrace/test/tst/common/stop/tst.stop2.exe
+usr/src/cmd/dtrace/test/tst/common/syscall/tst.args.exe
+usr/src/cmd/dtrace/test/tst/common/sysevent/tst.post.exe
+usr/src/cmd/dtrace/test/tst/common/sysevent/tst.post_chan.exe
+usr/src/cmd/dtrace/test/tst/common/usdt/forker.h
+usr/src/cmd/dtrace/test/tst/common/usdt/tst.argmap.exe
+usr/src/cmd/dtrace/test/tst/common/usdt/tst.args.exe
+usr/src/cmd/dtrace/test/tst/common/usdt/tst.forker.exe
+usr/src/cmd/dtrace/test/tst/common/ustack/tst.bigstack.exe
+usr/src/cmd/dtrace/test/tst/common/ustack/tst.spin.exe
+usr/src/cmd/dtrace/test/tst/i386/pid/tst.badinstr.exe
+usr/src/cmd/dtrace/test/tst/i386/pid/tst.branch.exe
+usr/src/cmd/dtrace/test/tst/i386/pid/tst.embedded.exe
+usr/src/cmd/dtrace/test/tst/i386/pid/tst.ret.exe
+usr/src/cmd/dtrace/test/tst/i386/pid/tst.retlist.exe
+usr/src/cmd/dtrace/test/tst/i386/ustack/tst.annotated.exe
+usr/src/cmd/dtrace/test/tst/i386/ustack/tst.circstack.exe
+usr/src/cmd/dtrace/test/tst/i386/ustack/tst.helper.exe
+usr/src/cmd/du/du
+usr/src/cmd/du/du.po.xpg4
+usr/src/cmd/du/du.xpg4
+usr/src/cmd/dumpadm/dumpadm
+usr/src/cmd/dumpcs/dumpcs
+usr/src/cmd/echo/echo
+usr/src/cmd/ed/compile.c.i
+usr/src/cmd/ed/ed
+usr/src/cmd/ed/ed.xpg4
+usr/src/cmd/ed/ed.xpg6
+usr/src/cmd/eeprom/i386/eeprom
+usr/src/cmd/egrep/egrep
+usr/src/cmd/egrep/egrep.c
+usr/src/cmd/eject/eject
+usr/src/cmd/emul64ioctl/emul64ioctl
+usr/src/cmd/enhance/enhance
+usr/src/cmd/env/env
+usr/src/cmd/env/env.xpg4
+usr/src/cmd/eqn/eqn.d/e.c
+usr/src/cmd/eqn/eqn.d/e.def
+usr/src/cmd/eqn/eqn.d/eqn
+usr/src/cmd/eqn/neqn.d/e.c
+usr/src/cmd/eqn/neqn.d/e.def
+usr/src/cmd/eqn/neqn.d/neqn
+usr/src/cmd/expand/expand
+usr/src/cmd/expand/expand_cmd.c
+usr/src/cmd/expand/unexpand
+usr/src/cmd/expr/expr
+usr/src/cmd/expr/expr.xpg4
+usr/src/cmd/expr/expr.xpg6
+usr/src/cmd/exstr/exstr
+usr/src/cmd/factor/factor
+usr/src/cmd/false/false
+usr/src/cmd/fcinfo/fcinfo
+usr/src/cmd/fcoesvc/svc-fcoei
+usr/src/cmd/fcoesvc/svc-fcoet
+usr/src/cmd/fdetach/fdetach
+usr/src/cmd/fdformat/fdformat
+usr/src/cmd/fdisk/fdisk
+usr/src/cmd/fgrep/fgrep
+usr/src/cmd/file/file
+usr/src/cmd/file/file.xpg4
+usr/src/cmd/filebench/amd64/go_filebench
+usr/src/cmd/filebench/amd64/parser_gram.c
+usr/src/cmd/filebench/amd64/parser_gram.h
+usr/src/cmd/filebench/amd64/parser_lex.c
+usr/src/cmd/filebench/fbscript/filebench
+usr/src/cmd/filebench/i386/go_filebench
+usr/src/cmd/filebench/i386/parser_gram.c
+usr/src/cmd/filebench/i386/parser_gram.h
+usr/src/cmd/filebench/i386/parser_lex.c
+usr/src/cmd/filebench/scripts/filebench_compare
+usr/src/cmd/filebench/scripts/fs_flush
+usr/src/cmd/filesync/filesync
+usr/src/cmd/find/find
+usr/src/cmd/find/find.xpg4
+usr/src/cmd/flowadm/flowadm
+usr/src/cmd/flowstat/flowstat
+usr/src/cmd/fm/dicts/AMD.mo
+usr/src/cmd/fm/dicts/DISK.mo
+usr/src/cmd/fm/dicts/FMD.mo
+usr/src/cmd/fm/dicts/FMNOTIFY.mo
+usr/src/cmd/fm/dicts/GMCA.mo
+usr/src/cmd/fm/dicts/INTEL.mo
+usr/src/cmd/fm/dicts/NXGE.mo
+usr/src/cmd/fm/dicts/PCI.mo
+usr/src/cmd/fm/dicts/PCIEX.mo
+usr/src/cmd/fm/dicts/SCA1000.mo
+usr/src/cmd/fm/dicts/SCA500.mo
+usr/src/cmd/fm/dicts/SENSOR.mo
+usr/src/cmd/fm/dicts/SMF.mo
+usr/src/cmd/fm/dicts/STORAGE.mo
+usr/src/cmd/fm/dicts/SUNOS.mo
+usr/src/cmd/fm/dicts/TEST.mo
+usr/src/cmd/fm/dicts/ZFS.mo
+usr/src/cmd/fm/eversholt/eftinfo/i386/eftinfo
+usr/src/cmd/fm/eversholt/eftinfo/i386/y.output
+usr/src/cmd/fm/eversholt/eftinfo/i386/y.tab.c
+usr/src/cmd/fm/eversholt/eftinfo/i386/y.tab.h
+usr/src/cmd/fm/eversholt/esc/i386/esc
+usr/src/cmd/fm/eversholt/esc/i386/y.output
+usr/src/cmd/fm/eversholt/esc/i386/y.tab.c
+usr/src/cmd/fm/eversholt/esc/i386/y.tab.h
+usr/src/cmd/fm/eversholt/files/i386/disk.eft
+usr/src/cmd/fm/eversholt/files/i386/i86pc/amd64.eft
+usr/src/cmd/fm/eversholt/files/i386/i86pc/gcpu.eft
+usr/src/cmd/fm/eversholt/files/i386/i86pc/gcpu_amd.eft
+usr/src/cmd/fm/eversholt/files/i386/i86pc/intel.eft
+usr/src/cmd/fm/eversholt/files/i386/neptune_xaui.eft
+usr/src/cmd/fm/eversholt/files/i386/neptune_xfp.eft
+usr/src/cmd/fm/eversholt/files/i386/pci.eft
+usr/src/cmd/fm/eversholt/files/i386/pciex.eft
+usr/src/cmd/fm/eversholt/files/i386/pciexrc.eft
+usr/src/cmd/fm/eversholt/files/i386/sca1000.eft
+usr/src/cmd/fm/eversholt/files/i386/sca500.eft
+usr/src/cmd/fm/eversholt/files/i386/sensor.eft
+usr/src/cmd/fm/eversholt/files/i386/storage.eft
+usr/src/cmd/fm/fmadm/i386/fmadm
+usr/src/cmd/fm/fmd/common/fmd_error.c
+usr/src/cmd/fm/fmd/common/fmd_rpc_adm.h
+usr/src/cmd/fm/fmd/common/fmd_rpc_api.h
+usr/src/cmd/fm/fmd/i386/fmd
+usr/src/cmd/fm/fmd/i386/fmd_svc_adm.c
+usr/src/cmd/fm/fmd/i386/fmd_svc_api.c
+usr/src/cmd/fm/fmd/i386/fmd_xdr_adm.c
+usr/src/cmd/fm/fmd/i386/fmd_xdr_api.c
+usr/src/cmd/fm/fmdump/i386/fmdump
+usr/src/cmd/fm/fminject/i386/fminject
+usr/src/cmd/fm/fminject/i386/inj_grammar.c
+usr/src/cmd/fm/fminject/i386/inj_grammar.h
+usr/src/cmd/fm/fminject/i386/inj_lex.c
+usr/src/cmd/fm/fmstat/i386/fmstat
+usr/src/cmd/fm/fmtopo/i386/fmtopo
+usr/src/cmd/fm/ipmitopo/i386/ipmitopo
+usr/src/cmd/fm/modules/common/eversholt/y.tab.c
+usr/src/cmd/fm/modules/common/eversholt/y.tab.h
+usr/src/cmd/fm/notify/smtp-notify/i386/process_msg_template.sh
+usr/src/cmd/fm/notify/smtp-notify/i386/smtp-notify
+usr/src/cmd/fm/notify/snmp-notify/i386/snmp-notify
+usr/src/cmd/fm/scripts/buildcode
+usr/src/cmd/fm/scripts/bustcode
+usr/src/cmd/fm/scripts/dictck
+usr/src/cmd/fm/scripts/fmsim
+usr/src/cmd/fmt/fmt
+usr/src/cmd/fmthard/fmthard
+usr/src/cmd/fmtmsg/fmtmsg
+usr/src/cmd/fold/fold
+usr/src/cmd/format/format
+usr/src/cmd/fs.d/autofs/automount
+usr/src/cmd/fs.d/autofs/automountd
+usr/src/cmd/fs.d/autofs/dfshares
+usr/src/cmd/fs.d/autofs/mount
+usr/src/cmd/fs.d/autofs/share
+usr/src/cmd/fs.d/autofs/unshare
+usr/src/cmd/fs.d/autofs/webnfs.h
+usr/src/cmd/fs.d/autofs/webnfs.x
+usr/src/cmd/fs.d/autofs/webnfs_client.c
+usr/src/cmd/fs.d/autofs/webnfs_xdr.c
+usr/src/cmd/fs.d/cachefs/cachefslog/cachefslog
+usr/src/cmd/fs.d/cachefs/cachefspack/cachefspack
+usr/src/cmd/fs.d/cachefs/cachefsstat/cachefsstat
+usr/src/cmd/fs.d/cachefs/cachefswssize/cachefswssize
+usr/src/cmd/fs.d/cachefs/cfsadmin/cfsadmin
+usr/src/cmd/fs.d/cachefs/cfsd/cachefsd
+usr/src/cmd/fs.d/cachefs/cfsd/cachefsd_tbl.i
+usr/src/cmd/fs.d/cachefs/cfsfstype/cfsfstype
+usr/src/cmd/fs.d/cachefs/cfstagchk/cfstagchk
+usr/src/cmd/fs.d/cachefs/common/cachefsd.h
+usr/src/cmd/fs.d/cachefs/common/cachefsd_clnt.c
+usr/src/cmd/fs.d/cachefs/common/cachefsd_xdr.c
+usr/src/cmd/fs.d/cachefs/dfshares/dfshares
+usr/src/cmd/fs.d/cachefs/fsck/fsck
+usr/src/cmd/fs.d/cachefs/mount/mount
+usr/src/cmd/fs.d/cachefs/share/share
+usr/src/cmd/fs.d/cachefs/umount/umount
+usr/src/cmd/fs.d/cachefs/unshare/unshare
+usr/src/cmd/fs.d/clri
+usr/src/cmd/fs.d/ctfs/mount
+usr/src/cmd/fs.d/dev/mount
+usr/src/cmd/fs.d/df
+usr/src/cmd/fs.d/df.po.xpg4
+usr/src/cmd/fs.d/df.xpg4
+usr/src/cmd/fs.d/fd/mount
+usr/src/cmd/fs.d/ff
+usr/src/cmd/fs.d/fs.dfl
+usr/src/cmd/fs.d/fsck
+usr/src/cmd/fs.d/hsfs/labelit/labelit
+usr/src/cmd/fs.d/hsfs/mount/mount
+usr/src/cmd/fs.d/lofs/mount/mount
+usr/src/cmd/fs.d/mntfs/mount
+usr/src/cmd/fs.d/mnttab
+usr/src/cmd/fs.d/mount
+usr/src/cmd/fs.d/nfs/clear_locks/clear_locks
+usr/src/cmd/fs.d/nfs/dfmounts/dfmounts
+usr/src/cmd/fs.d/nfs/dfshares/dfshares
+usr/src/cmd/fs.d/nfs/exportfs/exportfs
+usr/src/cmd/fs.d/nfs/mount/mount
+usr/src/cmd/fs.d/nfs/mount/webnfs.h
+usr/src/cmd/fs.d/nfs/mount/webnfs.x
+usr/src/cmd/fs.d/nfs/mount/webnfs_client.c
+usr/src/cmd/fs.d/nfs/mount/webnfs_xdr.c
+usr/src/cmd/fs.d/nfs/mountd/mountd
+usr/src/cmd/fs.d/nfs/nfs4cbd/nfs4cbd
+usr/src/cmd/fs.d/nfs/nfsd/nfsd
+usr/src/cmd/fs.d/nfs/nfsfind/nfsfind
+usr/src/cmd/fs.d/nfs/nfslog/nfslogd
+usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid
+usr/src/cmd/fs.d/nfs/nfsmapid/nfsmapid_test
+usr/src/cmd/fs.d/nfs/nfsref/nfsref
+usr/src/cmd/fs.d/nfs/nfsstat/nfsstat
+usr/src/cmd/fs.d/nfs/rquotad/rquota_xdr.c
+usr/src/cmd/fs.d/nfs/rquotad/rquotad
+usr/src/cmd/fs.d/nfs/share/sharetab
+usr/src/cmd/fs.d/nfs/showmount/showmount
+usr/src/cmd/fs.d/nfs/statd/statd
+usr/src/cmd/fs.d/nfs/umount/umount
+usr/src/cmd/fs.d/objfs/mount
+usr/src/cmd/fs.d/pcfs/fsck/fsck
+usr/src/cmd/fs.d/pcfs/mkfs/mkfs
+usr/src/cmd/fs.d/pcfs/mount/mount
+usr/src/cmd/fs.d/proc/mount
+usr/src/cmd/fs.d/reparsed/reparsed
+usr/src/cmd/fs.d/sharefs/mount
+usr/src/cmd/fs.d/smbclnt/chacl/chacl
+usr/src/cmd/fs.d/smbclnt/lsacl/lsacl
+usr/src/cmd/fs.d/smbclnt/mount/mount
+usr/src/cmd/fs.d/smbclnt/share/dfshares
+usr/src/cmd/fs.d/smbclnt/share/share
+usr/src/cmd/fs.d/smbclnt/share/unshare
+usr/src/cmd/fs.d/smbclnt/smbiod-svc/smbiod-svc
+usr/src/cmd/fs.d/smbclnt/smbiod/smbiod
+usr/src/cmd/fs.d/smbclnt/smbutil/smbutil
+usr/src/cmd/fs.d/smbclnt/umount/umount
+usr/src/cmd/fs.d/tmpfs/mount
+usr/src/cmd/fs.d/udfs/fsck/fsck
+usr/src/cmd/fs.d/udfs/fsdb/fsdb
+usr/src/cmd/fs.d/udfs/fsdb/lex.yy.c
+usr/src/cmd/fs.d/udfs/fsdb/ud_lib.c
+usr/src/cmd/fs.d/udfs/fsdb/ud_lib.h
+usr/src/cmd/fs.d/udfs/fsdb/y.tab.c
+usr/src/cmd/fs.d/udfs/fsdb/y.tab.h
+usr/src/cmd/fs.d/udfs/labelit/labelit
+usr/src/cmd/fs.d/udfs/labelit/ud_lib.c
+usr/src/cmd/fs.d/udfs/labelit/ud_lib.h
+usr/src/cmd/fs.d/udfs/mkfs/mkfs
+usr/src/cmd/fs.d/udfs/mount/mount
+usr/src/cmd/fs.d/ufs/clri/clri
+usr/src/cmd/fs.d/ufs/df/df
+usr/src/cmd/fs.d/ufs/edquota/edquota
+usr/src/cmd/fs.d/ufs/ff/ff
+usr/src/cmd/fs.d/ufs/fsck/fsck
+usr/src/cmd/fs.d/ufs/fsckall/fsckall
+usr/src/cmd/fs.d/ufs/fsdb/fsdb
+usr/src/cmd/fs.d/ufs/fsirand/fsirand
+usr/src/cmd/fs.d/ufs/fssnap/fssnap
+usr/src/cmd/fs.d/ufs/labelit/labelit
+usr/src/cmd/fs.d/ufs/lockfs/lockfs
+usr/src/cmd/fs.d/ufs/mkfs/mkfs
+usr/src/cmd/fs.d/ufs/mount/mount
+usr/src/cmd/fs.d/ufs/ncheck/ncheck
+usr/src/cmd/fs.d/ufs/newfs/newfs
+usr/src/cmd/fs.d/ufs/quot/quot
+usr/src/cmd/fs.d/ufs/quota/quota
+usr/src/cmd/fs.d/ufs/quota/rquota_xdr.c
+usr/src/cmd/fs.d/ufs/quotacheck/quotacheck
+usr/src/cmd/fs.d/ufs/quotaon/quotaon
+usr/src/cmd/fs.d/ufs/repquota/repquota
+usr/src/cmd/fs.d/ufs/tunefs/tunefs
+usr/src/cmd/fs.d/ufs/volcopy/volcopy
+usr/src/cmd/fs.d/umount
+usr/src/cmd/fs.d/volcopy
+usr/src/cmd/fs.d/zfs/bootinstall/bootinstall
+usr/src/cmd/fstyp/fstyp
+usr/src/cmd/fuser/fuser
+usr/src/cmd/fwflash/i386/fwflash
+usr/src/cmd/gcore/amd64/gcore
+usr/src/cmd/gcore/i386/gcore
+usr/src/cmd/gencat/gencat
+usr/src/cmd/geniconvtbl/i386/geniconvtbl
+usr/src/cmd/geniconvtbl/i386/lex.yy.c
+usr/src/cmd/geniconvtbl/i386/y.output
+usr/src/cmd/geniconvtbl/i386/y.tab.c
+usr/src/cmd/geniconvtbl/i386/y.tab.h
+usr/src/cmd/geniconvtbl/itm_comp.c
+usr/src/cmd/geniconvtbl/native/geniconvtbl
+usr/src/cmd/geniconvtbl/native/lex.yy.c
+usr/src/cmd/geniconvtbl/native/y.output
+usr/src/cmd/geniconvtbl/native/y.tab.c
+usr/src/cmd/geniconvtbl/native/y.tab.h
+usr/src/cmd/geniconvtbl/samples/ISO646%ISO8859-1.bt
+usr/src/cmd/geniconvtbl/samples/ISO8859-1%ISO646.bt
+usr/src/cmd/genmsg/genmsg
+usr/src/cmd/genmsg/genmsg.c
+usr/src/cmd/genmsg/lex.yy.c
+usr/src/cmd/genmsg/y.tab.h
+usr/src/cmd/getconf/getconf
+usr/src/cmd/getconf/getconf.xpg4
+usr/src/cmd/getconf/getconf.xpg6
+usr/src/cmd/getdevpolicy/getdevpolicy
+usr/src/cmd/getent/getent
+usr/src/cmd/getfacl/getfacl
+usr/src/cmd/getmajor/getmajor
+usr/src/cmd/getopt/getopt
+usr/src/cmd/getopt/getoptcvt
+usr/src/cmd/gettext/gettext
+usr/src/cmd/gettxt/gettxt
+usr/src/cmd/grep/grep
+usr/src/cmd/grep_xpg4/grep.xpg4
+usr/src/cmd/groups/groups
+usr/src/cmd/grpck/grpck
+usr/src/cmd/gss/etc/dummy_mech_token.conf
+usr/src/cmd/gss/gsscred/gsscred
+usr/src/cmd/gss/gsscred_clean/gsscred_clean
+usr/src/cmd/gss/gssd/gssd
+usr/src/cmd/gss/gssd/gssd.h
+usr/src/cmd/gss/gssd/gssd.x
+usr/src/cmd/gss/gssd/gssd_clnt.c
+usr/src/cmd/gss/gssd/gssd_svc.c
+usr/src/cmd/gss/gssd/gssd_xdr.c
+usr/src/cmd/gss/gssd/gssdtest
+usr/src/cmd/hal/addons/acpi/hald-addon-acpi
+usr/src/cmd/hal/addons/cpufreq/hald-addon-cpufreq
+usr/src/cmd/hal/addons/network-devices/hald-addon-network-discovery
+usr/src/cmd/hal/addons/storage/hald-addon-storage
+usr/src/cmd/hal/hal.conf
+usr/src/cmd/hal/hald-runner/hald-runner
+usr/src/cmd/hal/hald/hald
+usr/src/cmd/hal/hald/hald_marshal.c
+usr/src/cmd/hal/hald/hald_marshal.h
+usr/src/cmd/hal/probing/acpi/hald-probe-acpi
+usr/src/cmd/hal/probing/network-printer/hald-probe-network-printer
+usr/src/cmd/hal/probing/printer/hald-probe-printer
+usr/src/cmd/hal/probing/storage/hald-probe-storage
+usr/src/cmd/hal/probing/volume/hald-probe-volume
+usr/src/cmd/hal/probing/xkb/hald-probe-xkb
+usr/src/cmd/hal/tools/hal-device
+usr/src/cmd/hal/tools/hal-fdi-validate
+usr/src/cmd/hal/tools/hal-find-by-capability
+usr/src/cmd/hal/tools/hal-find-by-property
+usr/src/cmd/hal/tools/hal-functions
+usr/src/cmd/hal/tools/hal-get-property
+usr/src/cmd/hal/tools/hal-is-caller-privileged
+usr/src/cmd/hal/tools/hal-set-property
+usr/src/cmd/hal/tools/hal-storage-cleanup-all-mountpoints
+usr/src/cmd/hal/tools/hal-storage-cleanup-mountpoint
+usr/src/cmd/hal/tools/hal-storage-closetray
+usr/src/cmd/hal/tools/hal-storage-eject
+usr/src/cmd/hal/tools/hal-storage-mount
+usr/src/cmd/hal/tools/hal-storage-unmount
+usr/src/cmd/hal/tools/hal-storage-zpool-export
+usr/src/cmd/hal/tools/hal-storage-zpool-import
+usr/src/cmd/hal/tools/hal-system-lcd-get-brightness
+usr/src/cmd/hal/tools/hal-system-lcd-set-brightness
+usr/src/cmd/hal/tools/hal-system-power-hibernate
+usr/src/cmd/hal/tools/hal-system-power-reboot
+usr/src/cmd/hal/tools/hal-system-power-shutdown
+usr/src/cmd/hal/tools/hal-system-power-suspend
+usr/src/cmd/hal/tools/lshal
+usr/src/cmd/hal/tools/sunos/hal-system-lcd-get-brightness-sunos
+usr/src/cmd/hal/tools/sunos/hal-system-lcd-set-brightness-sunos
+usr/src/cmd/hal/tools/sunos/hal-system-power-hibernate-sunos
+usr/src/cmd/hal/tools/sunos/hal-system-power-reboot-sunos
+usr/src/cmd/hal/tools/sunos/hal-system-power-shutdown-sunos
+usr/src/cmd/hal/tools/sunos/hal-system-power-suspend-sunos
+usr/src/cmd/halt/halt
+usr/src/cmd/head/head
+usr/src/cmd/hostid/hostid
+usr/src/cmd/hostname/hostname
+usr/src/cmd/hotplug/hotplug
+usr/src/cmd/hotplugd/hotplugd
+usr/src/cmd/hwdata/THIRDPARTYLICENSE.pciids
+usr/src/cmd/ibd_upgrade/ibd_delete_link
+usr/src/cmd/ibd_upgrade/ibd_upgrade
+usr/src/cmd/id/id
+usr/src/cmd/id/id.xpg4
+usr/src/cmd/idmap/idmap/idmap
+usr/src/cmd/idmap/idmapd/idmapd
+usr/src/cmd/infocmp/infocmp
+usr/src/cmd/init/init
+usr/src/cmd/initpkg/dfstab
+usr/src/cmd/initpkg/mountall
+usr/src/cmd/initpkg/rc0
+usr/src/cmd/initpkg/rc1
+usr/src/cmd/initpkg/rc2
+usr/src/cmd/initpkg/rc3
+usr/src/cmd/initpkg/rcS
+usr/src/cmd/initpkg/shutdown
+usr/src/cmd/initpkg/swapadd
+usr/src/cmd/initpkg/umountall
+usr/src/cmd/initpkg/vfstab
+usr/src/cmd/install.d/install.d
+usr/src/cmd/intrd/intrd
+usr/src/cmd/intrstat/amd64/intrstat
+usr/src/cmd/intrstat/i386/intrstat
+usr/src/cmd/ipcrm/ipcrm
+usr/src/cmd/ipcs/ipcs
+usr/src/cmd/ipf/examples/mkfilters
+usr/src/cmd/ipf/svc/svc.ipfd
+usr/src/cmd/ipf/tools/amd64/ipf
+usr/src/cmd/ipf/tools/amd64/ipf.tab.c
+usr/src/cmd/ipf/tools/amd64/ipf.tab.h
+usr/src/cmd/ipf/tools/amd64/ipfs
+usr/src/cmd/ipf/tools/amd64/ipfstat
+usr/src/cmd/ipf/tools/amd64/ipftest
+usr/src/cmd/ipf/tools/amd64/ipmon
+usr/src/cmd/ipf/tools/amd64/ipmon.tab.c
+usr/src/cmd/ipf/tools/amd64/ipmon.tab.h
+usr/src/cmd/ipf/tools/amd64/ipnat
+usr/src/cmd/ipf/tools/amd64/ipnat.tab.c
+usr/src/cmd/ipf/tools/amd64/ipnat.tab.h
+usr/src/cmd/ipf/tools/amd64/ippool
+usr/src/cmd/ipf/tools/amd64/ippool.tab.c
+usr/src/cmd/ipf/tools/amd64/ippool.tab.h
+usr/src/cmd/ipf/tools/i386/ipf
+usr/src/cmd/ipf/tools/i386/ipf.tab.c
+usr/src/cmd/ipf/tools/i386/ipf.tab.h
+usr/src/cmd/ipf/tools/i386/ipfs
+usr/src/cmd/ipf/tools/i386/ipfstat
+usr/src/cmd/ipf/tools/i386/ipftest
+usr/src/cmd/ipf/tools/i386/ipmon
+usr/src/cmd/ipf/tools/i386/ipmon.tab.c
+usr/src/cmd/ipf/tools/i386/ipmon.tab.h
+usr/src/cmd/ipf/tools/i386/ipnat
+usr/src/cmd/ipf/tools/i386/ipnat.tab.c
+usr/src/cmd/ipf/tools/i386/ipnat.tab.h
+usr/src/cmd/ipf/tools/i386/ippool
+usr/src/cmd/ipf/tools/i386/ippool.tab.c
+usr/src/cmd/ipf/tools/i386/ippool.tab.h
+usr/src/cmd/ipf/tools/ipf_l.c
+usr/src/cmd/ipf/tools/ipf_l.h
+usr/src/cmd/ipf/tools/ipf_y.c
+usr/src/cmd/ipf/tools/ipf_y.h
+usr/src/cmd/ipf/tools/ipmon_l.c
+usr/src/cmd/ipf/tools/ipmon_l.h
+usr/src/cmd/ipf/tools/ipmon_y.c
+usr/src/cmd/ipf/tools/ipmon_y.h
+usr/src/cmd/ipf/tools/ipnat_l.c
+usr/src/cmd/ipf/tools/ipnat_l.h
+usr/src/cmd/ipf/tools/ipnat_y.c
+usr/src/cmd/ipf/tools/ipnat_y.h
+usr/src/cmd/ipf/tools/ippool_l.c
+usr/src/cmd/ipf/tools/ippool_l.h
+usr/src/cmd/ipf/tools/ippool_y.c
+usr/src/cmd/ipf/tools/ippool_y.h
+usr/src/cmd/isaexec/isaexec
+usr/src/cmd/isainfo/isainfo
+usr/src/cmd/isalist/isalist
+usr/src/cmd/isalist/optisa
+usr/src/cmd/iscsiadm/iscsiadm
+usr/src/cmd/iscsid/iscsid
+usr/src/cmd/iscsitsvc/iscsi-target
+usr/src/cmd/isns/isnsadm/isnsadm
+usr/src/cmd/isns/isnsd/isns
+usr/src/cmd/isns/isnsd/isns_provider.h
+usr/src/cmd/itadm/itadm
+usr/src/cmd/itutools/itu
+usr/src/cmd/itutools/mkbootmedia
+usr/src/cmd/itutools/pkg2du
+usr/src/cmd/itutools/updatemedia
+usr/src/cmd/kbd/kbd
+usr/src/cmd/keyserv/chkey
+usr/src/cmd/keyserv/domainname
+usr/src/cmd/keyserv/keylogin
+usr/src/cmd/keyserv/keylogout
+usr/src/cmd/keyserv/keyserv
+usr/src/cmd/keyserv/newkey
+usr/src/cmd/killall/killall
+usr/src/cmd/krb5/kadmin/cli/k5srvutil
+usr/src/cmd/krb5/kadmin/cli/kadmin
+usr/src/cmd/krb5/kadmin/cli/kadmin.local
+usr/src/cmd/krb5/kadmin/dbutil/iprop.h
+usr/src/cmd/krb5/kadmin/dbutil/kdb5_util
+usr/src/cmd/krb5/kadmin/kclient/kclient
+usr/src/cmd/krb5/kadmin/kclient/kconf
+usr/src/cmd/krb5/kadmin/kclient/kdyndns
+usr/src/cmd/krb5/kadmin/kclient/ksetpw
+usr/src/cmd/krb5/kadmin/kclient/ksmb
+usr/src/cmd/krb5/kadmin/kdcmgr/kdcmgr
+usr/src/cmd/krb5/kadmin/kdcmgr/klookup
+usr/src/cmd/krb5/kadmin/kpasswd/kpasswd
+usr/src/cmd/krb5/kadmin/ktutil/ktutil
+usr/src/cmd/krb5/kadmin/server/iprop.h
+usr/src/cmd/krb5/kadmin/server/iprop_xdr.c
+usr/src/cmd/krb5/kadmin/server/kadmind
+usr/src/cmd/krb5/kdestroy/kdestroy
+usr/src/cmd/krb5/kinit/kinit
+usr/src/cmd/krb5/klist/klist
+usr/src/cmd/krb5/kproplog/iprop.h
+usr/src/cmd/krb5/kproplog/iprop_xdr.c
+usr/src/cmd/krb5/kproplog/kproplog
+usr/src/cmd/krb5/krb5-config/krb5-config
+usr/src/cmd/krb5/krb5kdc/krb5kdc
+usr/src/cmd/krb5/kwarn/ktkt_warnd
+usr/src/cmd/krb5/kwarn/kwarnd.h
+usr/src/cmd/krb5/kwarn/kwarnd_svc.c
+usr/src/cmd/krb5/kwarn/kwarnd_xdr.c
+usr/src/cmd/krb5/ldap_util/kdb5_ldap_util
+usr/src/cmd/krb5/slave/iprop.h
+usr/src/cmd/krb5/slave/iprop_xdr.c
+usr/src/cmd/krb5/slave/kprop
+usr/src/cmd/krb5/slave/kprop_script
+usr/src/cmd/krb5/slave/kpropd
+usr/src/cmd/ksh/amd64/ksh
+usr/src/cmd/ksh/amd64/ksh93
+usr/src/cmd/ksh/builtins/alias
+usr/src/cmd/ksh/i386/ksh
+usr/src/cmd/ksh/i386/ksh93
+usr/src/cmd/kstat/kstat
+usr/src/cmd/kvmstat/kvmstat
+usr/src/cmd/last/last
+usr/src/cmd/last/wtmpx
+usr/src/cmd/lastcomm/lastcomm
+usr/src/cmd/ldap/i386/idsconfig
+usr/src/cmd/ldap/i386/ldapaddent
+usr/src/cmd/ldap/i386/ldapclient
+usr/src/cmd/ldap/i386/ldapdelete
+usr/src/cmd/ldap/i386/ldaplist
+usr/src/cmd/ldap/i386/ldapmodify
+usr/src/cmd/ldap/i386/ldapmodrdn
+usr/src/cmd/ldap/i386/ldapsearch
+usr/src/cmd/ldapcachemgr/ldap_cachemgr
+usr/src/cmd/lgrpinfo/lgrpinfo
+usr/src/cmd/line/line
+usr/src/cmd/link/link
+usr/src/cmd/link/link.xpg4
+usr/src/cmd/listen/listen
+usr/src/cmd/listen/nlps_server
+usr/src/cmd/lms/lms
+usr/src/cmd/loadkeys/dumpkeys
+usr/src/cmd/loadkeys/loadkeys
+usr/src/cmd/loadkeys/loadkeys.c
+usr/src/cmd/locale/amd64/locale
+usr/src/cmd/locale/i386/locale
+usr/src/cmd/localedef/8859-1.cm
+usr/src/cmd/localedef/8859-11.cm
+usr/src/cmd/localedef/8859-13.cm
+usr/src/cmd/localedef/8859-15.cm
+usr/src/cmd/localedef/8859-2.cm
+usr/src/cmd/localedef/8859-5.cm
+usr/src/cmd/localedef/8859-6.cm
+usr/src/cmd/localedef/8859-7.cm
+usr/src/cmd/localedef/8859-9.cm
+usr/src/cmd/localedef/KOI8-R.cm
+usr/src/cmd/localedef/UTF-8.cm
+usr/src/cmd/localedef/locale/
+usr/src/cmd/localedef/localedef
+usr/src/cmd/localedef/parser.tab.c
+usr/src/cmd/localedef/parser.tab.h
+usr/src/cmd/locator/locator
+usr/src/cmd/lockstat/amd64/lockstat
+usr/src/cmd/lockstat/i386/lockstat
+usr/src/cmd/lofiadm/lofiadm
+usr/src/cmd/logadm/logadm
+usr/src/cmd/logger/logger
+usr/src/cmd/login/login
+usr/src/cmd/login/logindevperm
+usr/src/cmd/logins/logins
+usr/src/cmd/look/look
+usr/src/cmd/ls/amd64/ls
+usr/src/cmd/ls/amd64/ls.xpg4
+usr/src/cmd/ls/amd64/ls.xpg6
+usr/src/cmd/ls/i386/ls
+usr/src/cmd/ls/i386/ls.xpg4
+usr/src/cmd/ls/i386/ls.xpg6
+usr/src/cmd/ls/ls.dc
+usr/src/cmd/luxadm/luxadm
+usr/src/cmd/mach/mach
+usr/src/cmd/machid/machid
+usr/src/cmd/mail/mail
+usr/src/cmd/mailx/mailx
+usr/src/cmd/mailx/mailx.help
+usr/src/cmd/mailx/mailx.help.~
+usr/src/cmd/makekey/makekey
+usr/src/cmd/man/src/apropos
+usr/src/cmd/man/src/catman
+usr/src/cmd/man/src/getNAME
+usr/src/cmd/man/src/makewhatis
+usr/src/cmd/man/src/man
+usr/src/cmd/man/src/util/instant
+usr/src/cmd/man/src/util/instant.src/THIRDPARTYLICENSE
+usr/src/cmd/man/src/util/instant.src/instant
+usr/src/cmd/man/src/util/nsgmls
+usr/src/cmd/man/src/util/nsgmls.src/lib/Makefile.dep
+usr/src/cmd/man/src/util/nsgmls.src/lib/app_inst.cxx
+usr/src/cmd/man/src/util/nsgmls.src/lib/arc_inst.cxx
+usr/src/cmd/man/src/util/nsgmls.src/lib/entmgr_inst.cxx
+usr/src/cmd/man/src/util/nsgmls.src/lib/parser_inst.cxx
+usr/src/cmd/man/src/util/nsgmls.src/lib/xentmgr_inst.cxx
+usr/src/cmd/man/src/util/nsgmls.src/nsgmls/Makefile.dep
+usr/src/cmd/man/src/util/nsgmls.src/nsgmls/nsgmls
+usr/src/cmd/man/src/util/nsgmls.src/nsgmls/nsgmls_inst.cxx
+usr/src/cmd/man/src/util/sgml2roff
+usr/src/cmd/man/src/util/solbookv2/THIRDPARTYLICENSE
+usr/src/cmd/man/src/whatis
+usr/src/cmd/mdb/i86pc/modules/apix/amd64/kmod/
+usr/src/cmd/mdb/i86pc/modules/apix/ia32/kmod/
+usr/src/cmd/mdb/i86pc/modules/pcplusmp/amd64/kmod/
+usr/src/cmd/mdb/i86pc/modules/pcplusmp/ia32/kmod/
+usr/src/cmd/mdb/i86pc/modules/unix/amd64/kmod/
+usr/src/cmd/mdb/i86pc/modules/unix/ia32/kmod/
+usr/src/cmd/mdb/i86pc/modules/uppc/amd64/kmod/
+usr/src/cmd/mdb/i86pc/modules/uppc/ia32/kmod/
+usr/src/cmd/mdb/i86xpv/modules/unix/amd64/kmod/
+usr/src/cmd/mdb/i86xpv/modules/unix/ia32/kmod/
+usr/src/cmd/mdb/i86xpv/modules/xpv_psm/amd64/kmod/
+usr/src/cmd/mdb/i86xpv/modules/xpv_psm/ia32/kmod/
+usr/src/cmd/mdb/i86xpv/modules/xpv_uppc/amd64/kmod/
+usr/src/cmd/mdb/i86xpv/modules/xpv_uppc/ia32/kmod/
+usr/src/cmd/mdb/intel/amd64/arp/kmod/
+usr/src/cmd/mdb/intel/amd64/cpc/kmod/
+usr/src/cmd/mdb/intel/amd64/crypto/kmod/
+usr/src/cmd/mdb/intel/amd64/dof/dof_names.c
+usr/src/cmd/mdb/intel/amd64/dtrace/dof_names.c
+usr/src/cmd/mdb/intel/amd64/emlxs/kmod/
+usr/src/cmd/mdb/intel/amd64/fcip/kmod/
+usr/src/cmd/mdb/intel/amd64/fcp/kmod/
+usr/src/cmd/mdb/intel/amd64/fctl/kmod/
+usr/src/cmd/mdb/intel/amd64/genunix/kmod/
+usr/src/cmd/mdb/intel/amd64/hook/kmod/
+usr/src/cmd/mdb/intel/amd64/idm/kmod/
+usr/src/cmd/mdb/intel/amd64/ii/kmod/
+usr/src/cmd/mdb/intel/amd64/ip/kmod/
+usr/src/cmd/mdb/intel/amd64/ipc/kmod/
+usr/src/cmd/mdb/intel/amd64/ipp/kmod/
+usr/src/cmd/mdb/intel/amd64/kmdb/kmdb_context_off.h
+usr/src/cmd/mdb/intel/amd64/kmdb/kmdb_modlinktest.c
+usr/src/cmd/mdb/intel/amd64/kmdb/kmdb_terminfo.c
+usr/src/cmd/mdb/intel/amd64/kmdb/kmdbmod
+usr/src/cmd/mdb/intel/amd64/kmdb/kmdbmod.core
+usr/src/cmd/mdb/intel/amd64/kmdb/mapfile
+usr/src/cmd/mdb/intel/amd64/kmdb/mapfile.i
+usr/src/cmd/mdb/intel/amd64/kmdb/mdb_grammar.c
+usr/src/cmd/mdb/intel/amd64/kmdb/mdb_grammar.h
+usr/src/cmd/mdb/intel/amd64/kmdb/mdb_lex.c
+usr/src/cmd/mdb/intel/amd64/kmdb/y.output
+usr/src/cmd/mdb/intel/amd64/krtld/kmod/
+usr/src/cmd/mdb/intel/amd64/lofs/kmod/
+usr/src/cmd/mdb/intel/amd64/logindmux/kmod/
+usr/src/cmd/mdb/intel/amd64/mac/kmod/
+usr/src/cmd/mdb/intel/amd64/md/kmod/
+usr/src/cmd/mdb/intel/amd64/mdb/mdb
+usr/src/cmd/mdb/intel/amd64/mdb/mdb_grammar.c
+usr/src/cmd/mdb/intel/amd64/mdb/mdb_grammar.h
+usr/src/cmd/mdb/intel/amd64/mdb/mdb_lex.c
+usr/src/cmd/mdb/intel/amd64/mdb/y.output
+usr/src/cmd/mdb/intel/amd64/mdb_ds/kmod/
+usr/src/cmd/mdb/intel/amd64/mdb_ks/kmod/
+usr/src/cmd/mdb/intel/amd64/mpt_sas/kmod/
+usr/src/cmd/mdb/intel/amd64/mr_sas/kmod/
+usr/src/cmd/mdb/intel/amd64/nca/kmod/
+usr/src/cmd/mdb/intel/amd64/neti/kmod/
+usr/src/cmd/mdb/intel/amd64/nsctl/kmod/
+usr/src/cmd/mdb/intel/amd64/nsmb/kmod/
+usr/src/cmd/mdb/intel/amd64/pmcs/kmod/
+usr/src/cmd/mdb/intel/amd64/ptm/kmod/
+usr/src/cmd/mdb/intel/amd64/qlc/kmod/
+usr/src/cmd/mdb/intel/amd64/random/kmod/
+usr/src/cmd/mdb/intel/amd64/rdc/kmod/
+usr/src/cmd/mdb/intel/amd64/s1394/kmod/
+usr/src/cmd/mdb/intel/amd64/scsi_vhci/kmod/
+usr/src/cmd/mdb/intel/amd64/sctp/kmod/
+usr/src/cmd/mdb/intel/amd64/sd/kmod/
+usr/src/cmd/mdb/intel/amd64/sdbc/kmod/
+usr/src/cmd/mdb/intel/amd64/smbfs/kmod/
+usr/src/cmd/mdb/intel/amd64/smbsrv/kmod/
+usr/src/cmd/mdb/intel/amd64/sockfs/kmod/
+usr/src/cmd/mdb/intel/amd64/specfs/kmod/
+usr/src/cmd/mdb/intel/amd64/sppp/kmod/
+usr/src/cmd/mdb/intel/amd64/srpt/kmod/
+usr/src/cmd/mdb/intel/amd64/stmf/kmod/
+usr/src/cmd/mdb/intel/amd64/stmf_sbd/kmod/
+usr/src/cmd/mdb/intel/amd64/sv/kmod/
+usr/src/cmd/mdb/intel/amd64/ufs/kmod/
+usr/src/cmd/mdb/intel/amd64/uhci/kmod/
+usr/src/cmd/mdb/intel/amd64/usba/kmod/
+usr/src/cmd/mdb/intel/amd64/zfs/kmod/
+usr/src/cmd/mdb/intel/ia32/arp/kmod/
+usr/src/cmd/mdb/intel/ia32/cpc/kmod/
+usr/src/cmd/mdb/intel/ia32/crypto/kmod/
+usr/src/cmd/mdb/intel/ia32/dof/dof_names.c
+usr/src/cmd/mdb/intel/ia32/dtrace/dof_names.c
+usr/src/cmd/mdb/intel/ia32/emlxs/kmod/
+usr/src/cmd/mdb/intel/ia32/fcip/kmod/
+usr/src/cmd/mdb/intel/ia32/fcp/kmod/
+usr/src/cmd/mdb/intel/ia32/fctl/kmod/
+usr/src/cmd/mdb/intel/ia32/genunix/kmod/
+usr/src/cmd/mdb/intel/ia32/hook/kmod/
+usr/src/cmd/mdb/intel/ia32/idm/kmod/
+usr/src/cmd/mdb/intel/ia32/ii/kmod/
+usr/src/cmd/mdb/intel/ia32/ip/kmod/
+usr/src/cmd/mdb/intel/ia32/ipc/kmod/
+usr/src/cmd/mdb/intel/ia32/ipp/kmod/
+usr/src/cmd/mdb/intel/ia32/kmdb/kmdb_context_off.h
+usr/src/cmd/mdb/intel/ia32/kmdb/kmdb_modlinktest.c
+usr/src/cmd/mdb/intel/ia32/kmdb/kmdb_terminfo.c
+usr/src/cmd/mdb/intel/ia32/kmdb/kmdbmod
+usr/src/cmd/mdb/intel/ia32/kmdb/kmdbmod.core
+usr/src/cmd/mdb/intel/ia32/kmdb/mapfile
+usr/src/cmd/mdb/intel/ia32/kmdb/mapfile.i
+usr/src/cmd/mdb/intel/ia32/kmdb/mdb_grammar.c
+usr/src/cmd/mdb/intel/ia32/kmdb/mdb_grammar.h
+usr/src/cmd/mdb/intel/ia32/kmdb/mdb_lex.c
+usr/src/cmd/mdb/intel/ia32/kmdb/y.output
+usr/src/cmd/mdb/intel/ia32/krtld/kmod/
+usr/src/cmd/mdb/intel/ia32/lofs/kmod/
+usr/src/cmd/mdb/intel/ia32/logindmux/kmod/
+usr/src/cmd/mdb/intel/ia32/mac/kmod/
+usr/src/cmd/mdb/intel/ia32/md/kmod/
+usr/src/cmd/mdb/intel/ia32/mdb/mdb
+usr/src/cmd/mdb/intel/ia32/mdb/mdb_grammar.c
+usr/src/cmd/mdb/intel/ia32/mdb/mdb_grammar.h
+usr/src/cmd/mdb/intel/ia32/mdb/mdb_lex.c
+usr/src/cmd/mdb/intel/ia32/mdb/y.output
+usr/src/cmd/mdb/intel/ia32/mdb_ds/kmod/
+usr/src/cmd/mdb/intel/ia32/mdb_ks/kmod/
+usr/src/cmd/mdb/intel/ia32/mpt_sas/kmod/
+usr/src/cmd/mdb/intel/ia32/mr_sas/kmod/
+usr/src/cmd/mdb/intel/ia32/nca/kmod/
+usr/src/cmd/mdb/intel/ia32/neti/kmod/
+usr/src/cmd/mdb/intel/ia32/nsctl/kmod/
+usr/src/cmd/mdb/intel/ia32/nsmb/kmod/
+usr/src/cmd/mdb/intel/ia32/pmcs/kmod/
+usr/src/cmd/mdb/intel/ia32/ptm/kmod/
+usr/src/cmd/mdb/intel/ia32/qlc/kmod/
+usr/src/cmd/mdb/intel/ia32/random/kmod/
+usr/src/cmd/mdb/intel/ia32/rdc/kmod/
+usr/src/cmd/mdb/intel/ia32/s1394/kmod/
+usr/src/cmd/mdb/intel/ia32/scsi_vhci/kmod/
+usr/src/cmd/mdb/intel/ia32/sctp/kmod/
+usr/src/cmd/mdb/intel/ia32/sd/kmod/
+usr/src/cmd/mdb/intel/ia32/sdbc/kmod/
+usr/src/cmd/mdb/intel/ia32/smbfs/kmod/
+usr/src/cmd/mdb/intel/ia32/smbsrv/kmod/
+usr/src/cmd/mdb/intel/ia32/sockfs/kmod/
+usr/src/cmd/mdb/intel/ia32/specfs/kmod/
+usr/src/cmd/mdb/intel/ia32/sppp/kmod/
+usr/src/cmd/mdb/intel/ia32/srpt/kmod/
+usr/src/cmd/mdb/intel/ia32/stmf/kmod/
+usr/src/cmd/mdb/intel/ia32/stmf_sbd/kmod/
+usr/src/cmd/mdb/intel/ia32/sv/kmod/
+usr/src/cmd/mdb/intel/ia32/ufs/kmod/
+usr/src/cmd/mdb/intel/ia32/uhci/kmod/
+usr/src/cmd/mdb/intel/ia32/usba/kmod/
+usr/src/cmd/mdb/intel/ia32/zfs/kmod/
+usr/src/cmd/mdb/intel/modules/amd_opteron/amd64/kmod/
+usr/src/cmd/mdb/intel/modules/amd_opteron/ia32/kmod/
+usr/src/cmd/mdb/intel/modules/generic_cpu/amd64/kmod/
+usr/src/cmd/mdb/intel/modules/generic_cpu/ia32/kmod/
+usr/src/cmd/mdb/intel/modules/sata/amd64/kmod/
+usr/src/cmd/mdb/intel/modules/sata/ia32/kmod/
+usr/src/cmd/mdb/tools/scripts/hdr2map
+usr/src/cmd/mdb/tools/scripts/map2linktest
+usr/src/cmd/mdb/tools/scripts/mdb
+usr/src/cmd/mdb/tools/scripts/mkmodules
+usr/src/cmd/mdb/tools/scripts/tigen
+usr/src/cmd/mdb/tools/setdynflag/i386/setdynflag
+usr/src/cmd/mesg/mesg
+usr/src/cmd/mkdir/mkdir
+usr/src/cmd/mkfifo/mkfifo
+usr/src/cmd/mkfile/mkfile
+usr/src/cmd/mkmsgs/mkmsgs
+usr/src/cmd/mknod/mknod
+usr/src/cmd/mkpwdict/mkpwdict
+usr/src/cmd/mktemp/mktemp
+usr/src/cmd/modload/amd64/add_drv
+usr/src/cmd/modload/amd64/modinfo
+usr/src/cmd/modload/amd64/modload
+usr/src/cmd/modload/amd64/modunload
+usr/src/cmd/modload/amd64/rem_drv
+usr/src/cmd/modload/amd64/update_drv
+usr/src/cmd/modload/i386/add_drv
+usr/src/cmd/modload/i386/modinfo
+usr/src/cmd/modload/i386/modload
+usr/src/cmd/modload/i386/modunload
+usr/src/cmd/modload/i386/rem_drv
+usr/src/cmd/modload/i386/update_drv
+usr/src/cmd/more/more
+usr/src/cmd/mpathadm/mpathadm
+usr/src/cmd/msgfmt/gmsgfmt
+usr/src/cmd/msgfmt/gmsgfmt_rev
+usr/src/cmd/msgfmt/gnu_po.c
+usr/src/cmd/msgfmt/msgfmt
+usr/src/cmd/msgfmt/xgettext
+usr/src/cmd/msgfmt/y.tab.h
+usr/src/cmd/msgid/msgid
+usr/src/cmd/mt/mt
+usr/src/cmd/mv/cp
+usr/src/cmd/mv/ln
+usr/src/cmd/mv/mv
+usr/src/cmd/mv/mv.xpg4
+usr/src/cmd/mvdir/mvdir
+usr/src/cmd/ndmpadm/ndmpadm
+usr/src/cmd/ndmpd/ndmp/ndmp.h
+usr/src/cmd/ndmpd/ndmp/ndmp_xdr.c
+usr/src/cmd/ndmpd/ndmp/ndmp_xdr.h
+usr/src/cmd/ndmpd/ndmpd
+usr/src/cmd/ndmpstat/ndmpstat
+usr/src/cmd/netadm/_pmtab
+usr/src/cmd/netadm/_sactab
+usr/src/cmd/netadm/_sysconfig
+usr/src/cmd/netadm/etc/ttydefs.cleanup
+usr/src/cmd/netadm/iu.ap
+usr/src/cmd/netadm/log
+usr/src/cmd/netadm/ttydefs
+usr/src/cmd/newform/newform
+usr/src/cmd/newgrp/newgrp
+usr/src/cmd/news/news
+usr/src/cmd/newtask/amd64/newtask
+usr/src/cmd/newtask/i386/newtask
+usr/src/cmd/nice/nice
+usr/src/cmd/nice/nice.xpg4
+usr/src/cmd/nl/nl
+usr/src/cmd/nl/nl.xpg4
+usr/src/cmd/nlsadmin/nlsadmin
+usr/src/cmd/nohup/amd64/nohup
+usr/src/cmd/nohup/amd64/nohup.xpg4
+usr/src/cmd/nohup/i386/nohup
+usr/src/cmd/nohup/i386/nohup.xpg4
+usr/src/cmd/nsadmin/.bashrc
+usr/src/cmd/nsadmin/.login
+usr/src/cmd/nsadmin/.profile
+usr/src/cmd/nsadmin/profile
+usr/src/cmd/nscd/nscd
+usr/src/cmd/ntfsprogs/THIRDPARTYLICENSE
+usr/src/cmd/ntfsprogs/mkntfs
+usr/src/cmd/ntfsprogs/ntfscat
+usr/src/cmd/ntfsprogs/ntfsclone
+usr/src/cmd/ntfsprogs/ntfscluster
+usr/src/cmd/ntfsprogs/ntfscmp
+usr/src/cmd/ntfsprogs/ntfscp
+usr/src/cmd/ntfsprogs/ntfsfix
+usr/src/cmd/ntfsprogs/ntfsinfo
+usr/src/cmd/ntfsprogs/ntfslabel
+usr/src/cmd/ntfsprogs/ntfsls
+usr/src/cmd/ntfsprogs/ntfsresize
+usr/src/cmd/ntfsprogs/ntfsundelete
+usr/src/cmd/oamuser/group/groupadd
+usr/src/cmd/oamuser/group/groupdel
+usr/src/cmd/oamuser/group/groupmod
+usr/src/cmd/oamuser/lib/ugdates
+usr/src/cmd/oamuser/user/useradd
+usr/src/cmd/oamuser/user/userdel
+usr/src/cmd/oamuser/user/usermod
+usr/src/cmd/oawk/awk.g.c
+usr/src/cmd/oawk/awk.h
+usr/src/cmd/oawk/awk.lx.c
+usr/src/cmd/oawk/native/
+usr/src/cmd/oawk/oawk
+usr/src/cmd/oawk/proctab.c
+usr/src/cmd/oawk/temp
+usr/src/cmd/oawk/tmptoken.c
+usr/src/cmd/od/od
+usr/src/cmd/pack/pack
+usr/src/cmd/pagesize/pagesize
+usr/src/cmd/parted/THIRDPARTYLICENSE
+usr/src/cmd/parted/parted
+usr/src/cmd/passmgmt/datemsk
+usr/src/cmd/passmgmt/passmgmt
+usr/src/cmd/passwd/passwd
+usr/src/cmd/pathchk/pathchk
+usr/src/cmd/pbind/amd64/pbind
+usr/src/cmd/pbind/i386/pbind
+usr/src/cmd/pcidr/pcidr
+usr/src/cmd/pcitool/i386/pcitool
+usr/src/cmd/perl/5.12/
+usr/src/cmd/pfexec/pfexec
+usr/src/cmd/pfexecd/pfexecd
+usr/src/cmd/pg/pg
+usr/src/cmd/pginfo/pginfo
+usr/src/cmd/pgrep/pgrep
+usr/src/cmd/pgrep/pkill
+usr/src/cmd/pgstat/pgstat
+usr/src/cmd/picl/picld/picld
+usr/src/cmd/picl/plugins/lib/picld_pluginutil/i386/lint.out
+usr/src/cmd/picl/plugins/lib/picld_pluginutil/i386/llib-lpicld_pluginutil.ln
+usr/src/cmd/picl/prtpicl/prtpicl
+usr/src/cmd/platexec/platexec
+usr/src/cmd/plimit/amd64/plimit
+usr/src/cmd/plimit/i386/plimit
+usr/src/cmd/plockstat/amd64/plockstat
+usr/src/cmd/plockstat/i386/plockstat
+usr/src/cmd/policykit/polkit-is-privileged
+usr/src/cmd/pools/pooladm/pooladm
+usr/src/cmd/pools/poolbind/poolbind
+usr/src/cmd/pools/poolcfg/poolcfg
+usr/src/cmd/pools/poolcfg/poolcfg_grammar.c
+usr/src/cmd/pools/poolcfg/poolcfg_grammar.h
+usr/src/cmd/pools/poolcfg/poolcfg_lex.c
+usr/src/cmd/pools/poold/JPool.jar
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/AbstractObjective.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/AbstractStatistic.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/AggregateStatistic.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/ComponentMove.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/ConfigurationException.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/DRM.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/DecisionHistory$1.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/DecisionHistory$ComponentMoveDecision.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/DecisionHistory$Decision.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/DecisionHistory.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/DoubleStatistic.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Expression.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/IllegalOFValueException.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/KExpression.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/KVExpression.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/KVOpExpression.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/LGroupData.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/LocalityObjective.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/LogDRM.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/LongStatistic.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Monitor.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Move.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Objective.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/PSETData.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Poold$1.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Poold$logHelper.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Poold$utility.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Poold.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/PooldException.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/QuantityMove.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/ResourceMonitor.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/ResultTuple.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Solver.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/StaleMonitorException.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/Statistic.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/StatisticEvent.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/StatisticList.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/StatisticListener.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/StatisticOperations.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/SystemMonitor.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/SystemSolver$ScoreMove.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/SystemSolver.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/UnsignedInt64Statistic.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/UtilizationObjective.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/WeightedLoadObjective$Calculation.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/WeightedLoadObjective.class
+usr/src/cmd/pools/poold/com/sun/solaris/domain/pools/WorkloadDependentObjective.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/exception/SuccinctStackTraceFormatter.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/kstat/Kstat.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/kstat/KstatChainUpdateException.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/kstat/KstatCtl.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/kstat/KstatException.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/kstat/KstatReadException.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/kstat/KstatTypeNotSupportedException.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/locality/LocalityDomain.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/locality/LocalityGroup.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/logging/Facility.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/logging/Severity.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/logging/SyslogHandler.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/logging/SysloglikeFormatter$1.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/logging/SysloglikeFormatter.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/Component.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/Configuration.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/Element.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/HRTime.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/Pool.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/PoolInternal.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/PoolsException.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/Property.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/PropertyWalk.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/Resource.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/UnsignedInt64.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/pools/Value.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/timer/RecurringEventTimer.class
+usr/src/cmd/pools/poold/com/sun/solaris/service/timer/SimpleRecurringEventTimer.class
+usr/src/cmd/pools/poold/libjkstat/jkstat.h
+usr/src/cmd/pools/poold/libjlgrp/jlgrp.h
+usr/src/cmd/pools/poold/libjpool/jpool.h
+usr/src/cmd/pools/poold/libjsyslog/jsyslog.h
+usr/src/cmd/pools/poold/poold
+usr/src/cmd/pools/poolstat/poolstat
+usr/src/cmd/power/pmconfig
+usr/src/cmd/power/power.conf
+usr/src/cmd/power/powerd
+usr/src/cmd/power/sys-suspend
+usr/src/cmd/power/sysidpm
+usr/src/cmd/powertop/amd64/powertop
+usr/src/cmd/powertop/i386/powertop
+usr/src/cmd/ppgsz/amd64/ppgsz
+usr/src/cmd/ppgsz/i386/ppgsz
+usr/src/cmd/pr/pr
+usr/src/cmd/pr/pr.dc
+usr/src/cmd/pr/pr.xpg4
+usr/src/cmd/praudit/praudit
+usr/src/cmd/prctl/amd64/prctl
+usr/src/cmd/prctl/i386/prctl
+usr/src/cmd/printf/printf
+usr/src/cmd/priocntl/FSSpriocntl
+usr/src/cmd/priocntl/FXpriocntl
+usr/src/cmd/priocntl/IApriocntl
+usr/src/cmd/priocntl/RTpriocntl
+usr/src/cmd/priocntl/SDCpriocntl
+usr/src/cmd/priocntl/TSpriocntl
+usr/src/cmd/priocntl/priocntl
+usr/src/cmd/profiles/profiles
+usr/src/cmd/projadd/projadd
+usr/src/cmd/projadd/projdel
+usr/src/cmd/projadd/projmod
+usr/src/cmd/projects/projects
+usr/src/cmd/prstat/amd64/prstat
+usr/src/cmd/prstat/i386/prstat
+usr/src/cmd/prtconf/amd64/prtconf
+usr/src/cmd/prtconf/i386/prtconf
+usr/src/cmd/prtdiag/i386/prtdiag
+usr/src/cmd/prtvtoc/prtvtoc
+usr/src/cmd/ps/amd64/ps
+usr/src/cmd/ps/i386/ps
+usr/src/cmd/ps/ps.dc
+usr/src/cmd/psradm/psradm
+usr/src/cmd/psrinfo/psrinfo
+usr/src/cmd/psrset/amd64/psrset
+usr/src/cmd/psrset/i386/psrset
+usr/src/cmd/ptools/pargs/amd64/
+usr/src/cmd/ptools/pargs/i386/
+usr/src/cmd/ptools/pcred/amd64/
+usr/src/cmd/ptools/pcred/i386/
+usr/src/cmd/ptools/pfiles/amd64/
+usr/src/cmd/ptools/pfiles/i386/
+usr/src/cmd/ptools/pflags/amd64/
+usr/src/cmd/ptools/pflags/i386/
+usr/src/cmd/ptools/pldd/amd64/
+usr/src/cmd/ptools/pldd/i386/
+usr/src/cmd/ptools/plgrp/amd64/
+usr/src/cmd/ptools/plgrp/i386/
+usr/src/cmd/ptools/pmadvise/amd64/
+usr/src/cmd/ptools/pmadvise/i386/
+usr/src/cmd/ptools/pmap/amd64/
+usr/src/cmd/ptools/pmap/i386/
+usr/src/cmd/ptools/ppriv/amd64/
+usr/src/cmd/ptools/ppriv/i386/
+usr/src/cmd/ptools/preap/amd64/
+usr/src/cmd/ptools/preap/i386/
+usr/src/cmd/ptools/prun/amd64/
+usr/src/cmd/ptools/prun/i386/
+usr/src/cmd/ptools/psig/amd64/
+usr/src/cmd/ptools/psig/i386/
+usr/src/cmd/ptools/pstack/amd64/
+usr/src/cmd/ptools/pstack/i386/
+usr/src/cmd/ptools/pstop/amd64/
+usr/src/cmd/ptools/pstop/i386/
+usr/src/cmd/ptools/ptime/amd64/
+usr/src/cmd/ptools/ptime/i386/
+usr/src/cmd/ptools/ptree/amd64/
+usr/src/cmd/ptools/ptree/i386/
+usr/src/cmd/ptools/pwait/amd64/
+usr/src/cmd/ptools/pwait/i386/
+usr/src/cmd/ptools/pwdx/amd64/
+usr/src/cmd/ptools/pwdx/i386/
+usr/src/cmd/pwck/pwck
+usr/src/cmd/pwconv/pwconv
+usr/src/cmd/pwd/pwd
+usr/src/cmd/raidctl/raidctl
+usr/src/cmd/ramdiskadm/ramdiskadm
+usr/src/cmd/rcap/rcapadm/rcapadm
+usr/src/cmd/rcap/rcapd/amd64/rcapd
+usr/src/cmd/rcap/rcapd/i386/rcapd
+usr/src/cmd/rcap/rcapstat/rcapstat
+usr/src/cmd/rcm_daemon/i386/rcm_daemon
+usr/src/cmd/rctladm/rctladm
+usr/src/cmd/refer/addbib
+usr/src/cmd/refer/hunt
+usr/src/cmd/refer/indxbib
+usr/src/cmd/refer/inv
+usr/src/cmd/refer/lookbib
+usr/src/cmd/refer/mkey
+usr/src/cmd/refer/refer
+usr/src/cmd/refer/roffbib
+usr/src/cmd/refer/sortbib
+usr/src/cmd/regcmp/regcmp
+usr/src/cmd/renice/renice
+usr/src/cmd/rexd/on
+usr/src/cmd/rexd/rpc.rexd
+usr/src/cmd/rm/rm
+usr/src/cmd/rm/rm.xpg4
+usr/src/cmd/rmdir/rmdir
+usr/src/cmd/rmformat/rmformat
+usr/src/cmd/rmmount/rmmount
+usr/src/cmd/rmt/rmt
+usr/src/cmd/rmvolmgr/rmvolmgr
+usr/src/cmd/roles/roles
+usr/src/cmd/rpcbind/rpc-bind
+usr/src/cmd/rpcbind/rpcbind
+usr/src/cmd/rpcgen/rpcgen
+usr/src/cmd/rpcinfo/rpcinfo
+usr/src/cmd/rpcsvc/rpc.bootparamd/rpc.bootparamd
+usr/src/cmd/rpcsvc/rpc.rstatd
+usr/src/cmd/rpcsvc/rpc.rusersd
+usr/src/cmd/rpcsvc/rpc.rwalld
+usr/src/cmd/rpcsvc/rpc.sprayd
+usr/src/cmd/rpcsvc/rstat.h
+usr/src/cmd/rpcsvc/rstat.x
+usr/src/cmd/rpcsvc/rstat_svc.c
+usr/src/cmd/rpcsvc/rstat_v2.h
+usr/src/cmd/rpcsvc/rstat_v2_svc.c
+usr/src/cmd/rpcsvc/rstat_v2_xdr.c
+usr/src/cmd/rpcsvc/rup
+usr/src/cmd/rpcsvc/rusers
+usr/src/cmd/rpcsvc/rwall
+usr/src/cmd/rpcsvc/rwall.h
+usr/src/cmd/rpcsvc/rwall.x
+usr/src/cmd/rpcsvc/rwall_clnt.c
+usr/src/cmd/rpcsvc/rwall_svc.c
+usr/src/cmd/rpcsvc/spray
+usr/src/cmd/rpcsvc/spray.h
+usr/src/cmd/rpcsvc/spray.x
+usr/src/cmd/rpcsvc/spray_clnt.c
+usr/src/cmd/rpcsvc/spray_svc.c
+usr/src/cmd/rtc/rtc
+usr/src/cmd/runat/runat
+usr/src/cmd/sa/sa1
+usr/src/cmd/sa/sa2
+usr/src/cmd/sa/sadc
+usr/src/cmd/sa/sar
+usr/src/cmd/sa/timex
+usr/src/cmd/saf/pmadm
+usr/src/cmd/saf/sac
+usr/src/cmd/saf/sacadm
+usr/src/cmd/sasinfo/sasinfo
+usr/src/cmd/savecore/amd64/savecore
+usr/src/cmd/savecore/i386/savecore
+usr/src/cmd/sbdadm/sbdadm
+usr/src/cmd/script/script
+usr/src/cmd/scsi/sestopo/i386/sestopo
+usr/src/cmd/scsi/smp/i386/smp
+usr/src/cmd/sdiff/sdiff
+usr/src/cmd/sdpadm/sdpadm
+usr/src/cmd/sed/sed
+usr/src/cmd/sendmail/THIRDPARTYLICENSE
+usr/src/cmd/sendmail/aux/editmap
+usr/src/cmd/sendmail/aux/etrn
+usr/src/cmd/sendmail/aux/mail.local
+usr/src/cmd/sendmail/aux/mailcompat
+usr/src/cmd/sendmail/aux/mailq
+usr/src/cmd/sendmail/aux/mailstats
+usr/src/cmd/sendmail/aux/makemap
+usr/src/cmd/sendmail/aux/mconnect
+usr/src/cmd/sendmail/aux/praliases
+usr/src/cmd/sendmail/aux/smrsh
+usr/src/cmd/sendmail/aux/vacation
+usr/src/cmd/sendmail/cf/cf/sendmail.cf
+usr/src/cmd/sendmail/cf/cf/submit.cf
+usr/src/cmd/sendmail/cf/sendmail.cf
+usr/src/cmd/sendmail/cf/submit.cf
+usr/src/cmd/sendmail/libmilter/i386/lint.out
+usr/src/cmd/sendmail/libmilter/i386/llib-lmilter.ln
+usr/src/cmd/sendmail/src/sendmail
+usr/src/cmd/setfacl/setfacl
+usr/src/cmd/setmnt/setmnt
+usr/src/cmd/setpgrp/setpgrp
+usr/src/cmd/setuname/amd64/setuname
+usr/src/cmd/setuname/i386/setuname
+usr/src/cmd/sgs/0@0/.make.state.i386
+usr/src/cmd/sgs/ar/.make.state.i386
+usr/src/cmd/sgs/ar/amd64/.make.state.i386
+usr/src/cmd/sgs/ar/amd64/ar
+usr/src/cmd/sgs/ar/amd64/msg.c
+usr/src/cmd/sgs/ar/amd64/msg.h
+usr/src/cmd/sgs/ar/i386/.make.state.i386
+usr/src/cmd/sgs/ar/i386/ar
+usr/src/cmd/sgs/ar/i386/ar.xpg4
+usr/src/cmd/sgs/ar/i386/msg.c
+usr/src/cmd/sgs/ar/i386/msg.h
+usr/src/cmd/sgs/crle/.make.state.i386
+usr/src/cmd/sgs/crle/amd64/.make.state.i386
+usr/src/cmd/sgs/crle/amd64/crle
+usr/src/cmd/sgs/crle/amd64/msg.c
+usr/src/cmd/sgs/crle/amd64/msg.h
+usr/src/cmd/sgs/crle/i386/.make.state.i386
+usr/src/cmd/sgs/crle/i386/crle
+usr/src/cmd/sgs/crle/i386/msg.c
+usr/src/cmd/sgs/crle/i386/msg.h
+usr/src/cmd/sgs/dump/.make.state.i386
+usr/src/cmd/sgs/dump/amd64/.make.state.i386
+usr/src/cmd/sgs/dump/amd64/dump
+usr/src/cmd/sgs/dump/i386/.make.state.i386
+usr/src/cmd/sgs/dump/i386/dump
+usr/src/cmd/sgs/elfdump/.make.state.i386
+usr/src/cmd/sgs/elfdump/amd64/.make.state.i386
+usr/src/cmd/sgs/elfdump/amd64/elfdump
+usr/src/cmd/sgs/elfdump/amd64/gen_struct_layout
+usr/src/cmd/sgs/elfdump/amd64/msg.c
+usr/src/cmd/sgs/elfdump/amd64/msg.h
+usr/src/cmd/sgs/elfdump/i386/.make.state.i386
+usr/src/cmd/sgs/elfdump/i386/elfdump
+usr/src/cmd/sgs/elfdump/i386/gen_struct_layout
+usr/src/cmd/sgs/elfdump/i386/msg.c
+usr/src/cmd/sgs/elfdump/i386/msg.h
+usr/src/cmd/sgs/elfedit/.make.state.i386
+usr/src/cmd/sgs/elfedit/amd64/.make.state.i386
+usr/src/cmd/sgs/elfedit/amd64/elfedit
+usr/src/cmd/sgs/elfedit/amd64/msg.c
+usr/src/cmd/sgs/elfedit/amd64/msg.h
+usr/src/cmd/sgs/elfedit/i386/.make.state.i386
+usr/src/cmd/sgs/elfedit/i386/elfedit
+usr/src/cmd/sgs/elfedit/i386/msg.c
+usr/src/cmd/sgs/elfedit/i386/msg.h
+usr/src/cmd/sgs/elfedit/modules/amd64/.make.state.i386
+usr/src/cmd/sgs/elfedit/modules/amd64/cap_msg.c
+usr/src/cmd/sgs/elfedit/modules/amd64/cap_msg.h
+usr/src/cmd/sgs/elfedit/modules/amd64/dyn_msg.c
+usr/src/cmd/sgs/elfedit/modules/amd64/dyn_msg.h
+usr/src/cmd/sgs/elfedit/modules/amd64/ehdr_msg.c
+usr/src/cmd/sgs/elfedit/modules/amd64/ehdr_msg.h
+usr/src/cmd/sgs/elfedit/modules/amd64/phdr_msg.c
+usr/src/cmd/sgs/elfedit/modules/amd64/phdr_msg.h
+usr/src/cmd/sgs/elfedit/modules/amd64/shdr_msg.c
+usr/src/cmd/sgs/elfedit/modules/amd64/shdr_msg.h
+usr/src/cmd/sgs/elfedit/modules/amd64/str_msg.c
+usr/src/cmd/sgs/elfedit/modules/amd64/str_msg.h
+usr/src/cmd/sgs/elfedit/modules/amd64/sym_msg.c
+usr/src/cmd/sgs/elfedit/modules/amd64/sym_msg.h
+usr/src/cmd/sgs/elfedit/modules/amd64/syminfo_msg.c
+usr/src/cmd/sgs/elfedit/modules/amd64/syminfo_msg.h
+usr/src/cmd/sgs/elfedit/modules/i386/.make.state.i386
+usr/src/cmd/sgs/elfedit/modules/i386/cap_msg.c
+usr/src/cmd/sgs/elfedit/modules/i386/cap_msg.h
+usr/src/cmd/sgs/elfedit/modules/i386/dyn_msg.c
+usr/src/cmd/sgs/elfedit/modules/i386/dyn_msg.h
+usr/src/cmd/sgs/elfedit/modules/i386/ehdr_msg.c
+usr/src/cmd/sgs/elfedit/modules/i386/ehdr_msg.h
+usr/src/cmd/sgs/elfedit/modules/i386/phdr_msg.c
+usr/src/cmd/sgs/elfedit/modules/i386/phdr_msg.h
+usr/src/cmd/sgs/elfedit/modules/i386/shdr_msg.c
+usr/src/cmd/sgs/elfedit/modules/i386/shdr_msg.h
+usr/src/cmd/sgs/elfedit/modules/i386/str_msg.c
+usr/src/cmd/sgs/elfedit/modules/i386/str_msg.h
+usr/src/cmd/sgs/elfedit/modules/i386/sym_msg.c
+usr/src/cmd/sgs/elfedit/modules/i386/sym_msg.h
+usr/src/cmd/sgs/elfedit/modules/i386/syminfo_msg.c
+usr/src/cmd/sgs/elfedit/modules/i386/syminfo_msg.h
+usr/src/cmd/sgs/elfwrap/.make.state.i386
+usr/src/cmd/sgs/elfwrap/amd64/.make.state.i386
+usr/src/cmd/sgs/elfwrap/amd64/elfwrap
+usr/src/cmd/sgs/elfwrap/amd64/msg.c
+usr/src/cmd/sgs/elfwrap/amd64/msg.h
+usr/src/cmd/sgs/elfwrap/i386/.make.state.i386
+usr/src/cmd/sgs/elfwrap/i386/elfwrap
+usr/src/cmd/sgs/elfwrap/i386/msg.c
+usr/src/cmd/sgs/elfwrap/i386/msg.h
+usr/src/cmd/sgs/error/i386/error
+usr/src/cmd/sgs/gprof/i386/.make.state.i386
+usr/src/cmd/sgs/gprof/i386/gprof
+usr/src/cmd/sgs/lari/.make.state.i386
+usr/src/cmd/sgs/lari/lari
+usr/src/cmd/sgs/ld/.make.state.i386
+usr/src/cmd/sgs/ld/amd64/.make.state.i386
+usr/src/cmd/sgs/ld/amd64/ld
+usr/src/cmd/sgs/ld/amd64/msg.c
+usr/src/cmd/sgs/ld/amd64/msg.h
+usr/src/cmd/sgs/ld/i386/.make.state.i386
+usr/src/cmd/sgs/ld/i386/ld
+usr/src/cmd/sgs/ld/i386/msg.c
+usr/src/cmd/sgs/ld/i386/msg.h
+usr/src/cmd/sgs/ldd/.make.state.i386
+usr/src/cmd/sgs/ldd/amd64/.make.state.i386
+usr/src/cmd/sgs/ldd/amd64/ldd
+usr/src/cmd/sgs/ldd/amd64/msg.c
+usr/src/cmd/sgs/ldd/amd64/msg.h
+usr/src/cmd/sgs/ldd/i386/.make.state.i386
+usr/src/cmd/sgs/ldd/i386/ldd
+usr/src/cmd/sgs/ldd/i386/msg.c
+usr/src/cmd/sgs/ldd/i386/msg.h
+usr/src/cmd/sgs/lddstub/.make.state.i386
+usr/src/cmd/sgs/lddstub/amd64/.make.state.i386
+usr/src/cmd/sgs/lddstub/amd64/lddstub
+usr/src/cmd/sgs/lddstub/i386/.make.state.i386
+usr/src/cmd/sgs/lddstub/i386/lddstub
+usr/src/cmd/sgs/ldprof/.make.state.i386
+usr/src/cmd/sgs/ldprof/amd64/.make.state.i386
+usr/src/cmd/sgs/ldprof/amd64/msg.c
+usr/src/cmd/sgs/ldprof/amd64/msg.h
+usr/src/cmd/sgs/ldprof/i386/.make.state.i386
+usr/src/cmd/sgs/ldprof/i386/msg.c
+usr/src/cmd/sgs/ldprof/i386/msg.h
+usr/src/cmd/sgs/lex/amd64/llib-ll.ln
+usr/src/cmd/sgs/lex/common/parser.c
+usr/src/cmd/sgs/lex/i386/lex
+usr/src/cmd/sgs/lex/i386/llib-ll.ln
+usr/src/cmd/sgs/libconv/.make.state.i386
+usr/src/cmd/sgs/libconv/amd64/.make.state.i386
+usr/src/cmd/sgs/libconv/amd64/arch_msg.c
+usr/src/cmd/sgs/libconv/amd64/arch_msg.h
+usr/src/cmd/sgs/libconv/amd64/audit_msg.c
+usr/src/cmd/sgs/libconv/amd64/audit_msg.h
+usr/src/cmd/sgs/libconv/amd64/bld_vernote
+usr/src/cmd/sgs/libconv/amd64/c_literal_msg.c
+usr/src/cmd/sgs/libconv/amd64/c_literal_msg.h
+usr/src/cmd/sgs/libconv/amd64/cap_msg.c
+usr/src/cmd/sgs/libconv/amd64/cap_msg.h
+usr/src/cmd/sgs/libconv/amd64/config_msg.c
+usr/src/cmd/sgs/libconv/amd64/config_msg.h
+usr/src/cmd/sgs/libconv/amd64/corenote_msg.c
+usr/src/cmd/sgs/libconv/amd64/corenote_msg.h
+usr/src/cmd/sgs/libconv/amd64/data_msg.c
+usr/src/cmd/sgs/libconv/amd64/data_msg.h
+usr/src/cmd/sgs/libconv/amd64/deftag_msg.c
+usr/src/cmd/sgs/libconv/amd64/deftag_msg.h
+usr/src/cmd/sgs/libconv/amd64/demangle_msg.c
+usr/src/cmd/sgs/libconv/amd64/demangle_msg.h
+usr/src/cmd/sgs/libconv/amd64/dl_msg.c
+usr/src/cmd/sgs/libconv/amd64/dl_msg.h
+usr/src/cmd/sgs/libconv/amd64/dwarf_ehe_msg.c
+usr/src/cmd/sgs/libconv/amd64/dwarf_ehe_msg.h
+usr/src/cmd/sgs/libconv/amd64/dwarf_msg.c
+usr/src/cmd/sgs/libconv/amd64/dwarf_msg.h
+usr/src/cmd/sgs/libconv/amd64/dynamic_msg.c
+usr/src/cmd/sgs/libconv/amd64/dynamic_msg.h
+usr/src/cmd/sgs/libconv/amd64/elf_msg.c
+usr/src/cmd/sgs/libconv/amd64/elf_msg.h
+usr/src/cmd/sgs/libconv/amd64/entry_msg.c
+usr/src/cmd/sgs/libconv/amd64/entry_msg.h
+usr/src/cmd/sgs/libconv/amd64/globals_msg.c
+usr/src/cmd/sgs/libconv/amd64/globals_msg.h
+usr/src/cmd/sgs/libconv/amd64/group_msg.c
+usr/src/cmd/sgs/libconv/amd64/group_msg.h
+usr/src/cmd/sgs/libconv/amd64/lddstub_msg.c
+usr/src/cmd/sgs/libconv/amd64/lddstub_msg.h
+usr/src/cmd/sgs/libconv/amd64/map_msg.c
+usr/src/cmd/sgs/libconv/amd64/map_msg.h
+usr/src/cmd/sgs/libconv/amd64/phdr_msg.c
+usr/src/cmd/sgs/libconv/amd64/phdr_msg.h
+usr/src/cmd/sgs/libconv/amd64/relocate_amd64_msg.c
+usr/src/cmd/sgs/libconv/amd64/relocate_amd64_msg.h
+usr/src/cmd/sgs/libconv/amd64/relocate_i386_msg.c
+usr/src/cmd/sgs/libconv/amd64/relocate_i386_msg.h
+usr/src/cmd/sgs/libconv/amd64/relocate_sparc_msg.c
+usr/src/cmd/sgs/libconv/amd64/relocate_sparc_msg.h
+usr/src/cmd/sgs/libconv/amd64/report_bufsize.h
+usr/src/cmd/sgs/libconv/amd64/sections_msg.c
+usr/src/cmd/sgs/libconv/amd64/sections_msg.h
+usr/src/cmd/sgs/libconv/amd64/segments_msg.c
+usr/src/cmd/sgs/libconv/amd64/segments_msg.h
+usr/src/cmd/sgs/libconv/amd64/symbols_msg.c
+usr/src/cmd/sgs/libconv/amd64/symbols_msg.h
+usr/src/cmd/sgs/libconv/amd64/symbols_sparc_msg.c
+usr/src/cmd/sgs/libconv/amd64/symbols_sparc_msg.h
+usr/src/cmd/sgs/libconv/amd64/syminfo_msg.c
+usr/src/cmd/sgs/libconv/amd64/syminfo_msg.h
+usr/src/cmd/sgs/libconv/amd64/time_msg.c
+usr/src/cmd/sgs/libconv/amd64/time_msg.h
+usr/src/cmd/sgs/libconv/amd64/vernote.s
+usr/src/cmd/sgs/libconv/amd64/version_msg.c
+usr/src/cmd/sgs/libconv/amd64/version_msg.h
+usr/src/cmd/sgs/libconv/i386/.make.state.i386
+usr/src/cmd/sgs/libconv/i386/arch_msg.c
+usr/src/cmd/sgs/libconv/i386/arch_msg.h
+usr/src/cmd/sgs/libconv/i386/audit_msg.c
+usr/src/cmd/sgs/libconv/i386/audit_msg.h
+usr/src/cmd/sgs/libconv/i386/bld_vernote
+usr/src/cmd/sgs/libconv/i386/c_literal_msg.c
+usr/src/cmd/sgs/libconv/i386/c_literal_msg.h
+usr/src/cmd/sgs/libconv/i386/cap_msg.c
+usr/src/cmd/sgs/libconv/i386/cap_msg.h
+usr/src/cmd/sgs/libconv/i386/config_msg.c
+usr/src/cmd/sgs/libconv/i386/config_msg.h
+usr/src/cmd/sgs/libconv/i386/corenote_msg.c
+usr/src/cmd/sgs/libconv/i386/corenote_msg.h
+usr/src/cmd/sgs/libconv/i386/data_msg.c
+usr/src/cmd/sgs/libconv/i386/data_msg.h
+usr/src/cmd/sgs/libconv/i386/deftag_msg.c
+usr/src/cmd/sgs/libconv/i386/deftag_msg.h
+usr/src/cmd/sgs/libconv/i386/demangle_msg.c
+usr/src/cmd/sgs/libconv/i386/demangle_msg.h
+usr/src/cmd/sgs/libconv/i386/dl_msg.c
+usr/src/cmd/sgs/libconv/i386/dl_msg.h
+usr/src/cmd/sgs/libconv/i386/dwarf_ehe_msg.c
+usr/src/cmd/sgs/libconv/i386/dwarf_ehe_msg.h
+usr/src/cmd/sgs/libconv/i386/dwarf_msg.c
+usr/src/cmd/sgs/libconv/i386/dwarf_msg.h
+usr/src/cmd/sgs/libconv/i386/dynamic_msg.c
+usr/src/cmd/sgs/libconv/i386/dynamic_msg.h
+usr/src/cmd/sgs/libconv/i386/elf_msg.c
+usr/src/cmd/sgs/libconv/i386/elf_msg.h
+usr/src/cmd/sgs/libconv/i386/entry_msg.c
+usr/src/cmd/sgs/libconv/i386/entry_msg.h
+usr/src/cmd/sgs/libconv/i386/globals_msg.c
+usr/src/cmd/sgs/libconv/i386/globals_msg.h
+usr/src/cmd/sgs/libconv/i386/group_msg.c
+usr/src/cmd/sgs/libconv/i386/group_msg.h
+usr/src/cmd/sgs/libconv/i386/lddstub_msg.c
+usr/src/cmd/sgs/libconv/i386/lddstub_msg.h
+usr/src/cmd/sgs/libconv/i386/map_msg.c
+usr/src/cmd/sgs/libconv/i386/map_msg.h
+usr/src/cmd/sgs/libconv/i386/phdr_msg.c
+usr/src/cmd/sgs/libconv/i386/phdr_msg.h
+usr/src/cmd/sgs/libconv/i386/relocate_amd64_msg.c
+usr/src/cmd/sgs/libconv/i386/relocate_amd64_msg.h
+usr/src/cmd/sgs/libconv/i386/relocate_i386_msg.c
+usr/src/cmd/sgs/libconv/i386/relocate_i386_msg.h
+usr/src/cmd/sgs/libconv/i386/relocate_sparc_msg.c
+usr/src/cmd/sgs/libconv/i386/relocate_sparc_msg.h
+usr/src/cmd/sgs/libconv/i386/report_bufsize.h
+usr/src/cmd/sgs/libconv/i386/sections_msg.c
+usr/src/cmd/sgs/libconv/i386/sections_msg.h
+usr/src/cmd/sgs/libconv/i386/segments_msg.c
+usr/src/cmd/sgs/libconv/i386/segments_msg.h
+usr/src/cmd/sgs/libconv/i386/symbols_msg.c
+usr/src/cmd/sgs/libconv/i386/symbols_msg.h
+usr/src/cmd/sgs/libconv/i386/symbols_sparc_msg.c
+usr/src/cmd/sgs/libconv/i386/symbols_sparc_msg.h
+usr/src/cmd/sgs/libconv/i386/syminfo_msg.c
+usr/src/cmd/sgs/libconv/i386/syminfo_msg.h
+usr/src/cmd/sgs/libconv/i386/time_msg.c
+usr/src/cmd/sgs/libconv/i386/time_msg.h
+usr/src/cmd/sgs/libconv/i386/vernote.s
+usr/src/cmd/sgs/libconv/i386/version_msg.c
+usr/src/cmd/sgs/libconv/i386/version_msg.h
+usr/src/cmd/sgs/libcrle/.make.state.i386
+usr/src/cmd/sgs/libcrle/amd64/.make.state.i386
+usr/src/cmd/sgs/libcrle/amd64/msg.c
+usr/src/cmd/sgs/libcrle/amd64/msg.h
+usr/src/cmd/sgs/libcrle/i386/.make.state.i386
+usr/src/cmd/sgs/libcrle/i386/msg.c
+usr/src/cmd/sgs/libcrle/i386/msg.h
+usr/src/cmd/sgs/libdl/.make.state.i386
+usr/src/cmd/sgs/libdl/amd64/lint.out
+usr/src/cmd/sgs/libdl/amd64/llib-ldl.ln
+usr/src/cmd/sgs/libdl/i386/lint.out
+usr/src/cmd/sgs/libdl/i386/llib-ldl.ln
+usr/src/cmd/sgs/libelf/.make.state.i386
+usr/src/cmd/sgs/libelf/amd64/.make.state.i386
+usr/src/cmd/sgs/libelf/amd64/lint.out.1
+usr/src/cmd/sgs/libelf/amd64/llib-lelf.ln
+usr/src/cmd/sgs/libelf/amd64/msg.c
+usr/src/cmd/sgs/libelf/amd64/msg.h
+usr/src/cmd/sgs/libelf/amd64/xlate.c
+usr/src/cmd/sgs/libelf/amd64/xlate64.c
+usr/src/cmd/sgs/libelf/i386/.make.state.i386
+usr/src/cmd/sgs/libelf/i386/lint.out.1
+usr/src/cmd/sgs/libelf/i386/llib-lelf.ln
+usr/src/cmd/sgs/libelf/i386/msg.c
+usr/src/cmd/sgs/libelf/i386/msg.h
+usr/src/cmd/sgs/libelf/i386/xlate.c
+usr/src/cmd/sgs/libelf/i386/xlate64.c
+usr/src/cmd/sgs/libld/.make.state.i386
+usr/src/cmd/sgs/libld/amd64/.make.state.i386
+usr/src/cmd/sgs/libld/amd64/libld.so.4
+usr/src/cmd/sgs/libld/amd64/msg.c
+usr/src/cmd/sgs/libld/amd64/msg.h
+usr/src/cmd/sgs/libld/i386/.make.state.i386
+usr/src/cmd/sgs/libld/i386/libld.so.4
+usr/src/cmd/sgs/libld/i386/msg.c
+usr/src/cmd/sgs/libld/i386/msg.h
+usr/src/cmd/sgs/liblddbg/.make.state.i386
+usr/src/cmd/sgs/liblddbg/amd64/.make.state.i386
+usr/src/cmd/sgs/liblddbg/amd64/liblddbg.so.4
+usr/src/cmd/sgs/liblddbg/amd64/msg.c
+usr/src/cmd/sgs/liblddbg/amd64/msg.h
+usr/src/cmd/sgs/liblddbg/i386/.make.state.i386
+usr/src/cmd/sgs/liblddbg/i386/liblddbg.so.4
+usr/src/cmd/sgs/liblddbg/i386/msg.c
+usr/src/cmd/sgs/liblddbg/i386/msg.h
+usr/src/cmd/sgs/libldmake/.make.state.i386
+usr/src/cmd/sgs/libldmake/amd64/.make.state.i386
+usr/src/cmd/sgs/libldmake/i386/.make.state.i386
+usr/src/cmd/sgs/libldstab/.make.state.i386
+usr/src/cmd/sgs/libldstab/amd64/.make.state.i386
+usr/src/cmd/sgs/libldstab/i386/.make.state.i386
+usr/src/cmd/sgs/librtld/.make.state.i386
+usr/src/cmd/sgs/librtld/amd64/.make.state.i386
+usr/src/cmd/sgs/librtld/amd64/msg.c
+usr/src/cmd/sgs/librtld/amd64/msg.h
+usr/src/cmd/sgs/librtld/i386/.make.state.i386
+usr/src/cmd/sgs/librtld/i386/msg.c
+usr/src/cmd/sgs/librtld/i386/msg.h
+usr/src/cmd/sgs/librtld_db/.make.state.i386
+usr/src/cmd/sgs/librtld_db/amd64/.make.state.i386
+usr/src/cmd/sgs/librtld_db/amd64/lint.out.1
+usr/src/cmd/sgs/librtld_db/amd64/llib-lrtld_db.ln
+usr/src/cmd/sgs/librtld_db/amd64/msg.c
+usr/src/cmd/sgs/librtld_db/amd64/msg.h
+usr/src/cmd/sgs/librtld_db/demo/Makefile.com.demo
+usr/src/cmd/sgs/librtld_db/demo/Makefile.targ.demo
+usr/src/cmd/sgs/librtld_db/demo/amd64/Makefile.demo
+usr/src/cmd/sgs/librtld_db/demo/i386/Makefile.demo
+usr/src/cmd/sgs/librtld_db/demo/sparc/Makefile.demo
+usr/src/cmd/sgs/librtld_db/demo/sparcv9/Makefile.demo
+usr/src/cmd/sgs/librtld_db/i386/.make.state.i386
+usr/src/cmd/sgs/librtld_db/i386/lint.out.1
+usr/src/cmd/sgs/librtld_db/i386/llib-lrtld_db.ln
+usr/src/cmd/sgs/librtld_db/i386/msg.c
+usr/src/cmd/sgs/librtld_db/i386/msg.h
+usr/src/cmd/sgs/link_audit/amd64/.make.state.i386
+usr/src/cmd/sgs/link_audit/amd64/perfcnt
+usr/src/cmd/sgs/link_audit/amd64/symbindrep
+usr/src/cmd/sgs/link_audit/i386/.make.state.i386
+usr/src/cmd/sgs/link_audit/i386/dumpbind
+usr/src/cmd/sgs/link_audit/i386/perfcnt
+usr/src/cmd/sgs/link_audit/i386/sotruss
+usr/src/cmd/sgs/link_audit/i386/symbindrep
+usr/src/cmd/sgs/link_audit/i386/whocalls
+usr/src/cmd/sgs/lorder/.make.state.i386
+usr/src/cmd/sgs/lorder/lorder
+usr/src/cmd/sgs/m4/common/m4y.c
+usr/src/cmd/sgs/m4/common/m4y_xpg4.c
+usr/src/cmd/sgs/m4/i386/m4
+usr/src/cmd/sgs/m4/i386/m4.xpg4
+usr/src/cmd/sgs/mcs/.make.state.i386
+usr/src/cmd/sgs/mcs/amd64/.make.state.i386
+usr/src/cmd/sgs/mcs/amd64/mcs
+usr/src/cmd/sgs/mcs/i386/.make.state.i386
+usr/src/cmd/sgs/mcs/i386/mcs
+usr/src/cmd/sgs/messages/.make.state.i386
+usr/src/cmd/sgs/messages/i386/.make.state.i386
+usr/src/cmd/sgs/messages/i386/ar
+usr/src/cmd/sgs/messages/i386/crle
+usr/src/cmd/sgs/messages/i386/elfdump
+usr/src/cmd/sgs/messages/i386/elfedit
+usr/src/cmd/sgs/messages/i386/elfedit_cap
+usr/src/cmd/sgs/messages/i386/elfedit_dyn
+usr/src/cmd/sgs/messages/i386/elfedit_ehdr
+usr/src/cmd/sgs/messages/i386/elfedit_phdr
+usr/src/cmd/sgs/messages/i386/elfedit_shdr
+usr/src/cmd/sgs/messages/i386/elfedit_str
+usr/src/cmd/sgs/messages/i386/elfedit_sym
+usr/src/cmd/sgs/messages/i386/elfedit_syminfo
+usr/src/cmd/sgs/messages/i386/elfwrap
+usr/src/cmd/sgs/messages/i386/lari
+usr/src/cmd/sgs/messages/i386/ld
+usr/src/cmd/sgs/messages/i386/ldd
+usr/src/cmd/sgs/messages/i386/ldprof
+usr/src/cmd/sgs/messages/i386/libcrle
+usr/src/cmd/sgs/messages/i386/libelf
+usr/src/cmd/sgs/messages/i386/libld
+usr/src/cmd/sgs/messages/i386/liblddbg
+usr/src/cmd/sgs/messages/i386/librtld
+usr/src/cmd/sgs/messages/i386/moe
+usr/src/cmd/sgs/messages/i386/pvs
+usr/src/cmd/sgs/messages/i386/rtld
+usr/src/cmd/sgs/moe/.make.state.i386
+usr/src/cmd/sgs/moe/amd64/.make.state.i386
+usr/src/cmd/sgs/moe/amd64/moe
+usr/src/cmd/sgs/moe/amd64/msg.c
+usr/src/cmd/sgs/moe/amd64/msg.h
+usr/src/cmd/sgs/moe/i386/.make.state.i386
+usr/src/cmd/sgs/moe/i386/moe
+usr/src/cmd/sgs/moe/i386/msg.c
+usr/src/cmd/sgs/moe/i386/msg.h
+usr/src/cmd/sgs/nm/amd64/.make.state.i386
+usr/src/cmd/sgs/nm/amd64/nm
+usr/src/cmd/sgs/nm/i386/.make.state.i386
+usr/src/cmd/sgs/nm/i386/nm
+usr/src/cmd/sgs/nm/i386/nm.xpg4
+usr/src/cmd/sgs/prof/i386/.make.state.i386
+usr/src/cmd/sgs/prof/i386/prof
+usr/src/cmd/sgs/proto/
+usr/src/cmd/sgs/pvs/.make.state.i386
+usr/src/cmd/sgs/pvs/amd64/.make.state.i386
+usr/src/cmd/sgs/pvs/amd64/msg.c
+usr/src/cmd/sgs/pvs/amd64/msg.h
+usr/src/cmd/sgs/pvs/amd64/pvs
+usr/src/cmd/sgs/pvs/i386/.make.state.i386
+usr/src/cmd/sgs/pvs/i386/msg.c
+usr/src/cmd/sgs/pvs/i386/msg.h
+usr/src/cmd/sgs/pvs/i386/pvs
+usr/src/cmd/sgs/ranlib/ranlib
+usr/src/cmd/sgs/rtld/.make.state.i386
+usr/src/cmd/sgs/rtld/amd64/.make.state.i386
+usr/src/cmd/sgs/rtld/amd64/msg.c
+usr/src/cmd/sgs/rtld/amd64/msg.h
+usr/src/cmd/sgs/rtld/i386/.make.state.i386
+usr/src/cmd/sgs/rtld/i386/msg.c
+usr/src/cmd/sgs/rtld/i386/msg.h
+usr/src/cmd/sgs/rtld/mdbmod/.make.state.i386
+usr/src/cmd/sgs/rtld/mdbmod/amd64/.make.state.i386
+usr/src/cmd/sgs/rtld/mdbmod/amd64/msg.c
+usr/src/cmd/sgs/rtld/mdbmod/amd64/msg.h
+usr/src/cmd/sgs/rtld/mdbmod/i386/.make.state.i386
+usr/src/cmd/sgs/rtld/mdbmod/i386/msg.c
+usr/src/cmd/sgs/rtld/mdbmod/i386/msg.h
+usr/src/cmd/sgs/size/amd64/.make.state.i386
+usr/src/cmd/sgs/size/amd64/size
+usr/src/cmd/sgs/size/i386/.make.state.i386
+usr/src/cmd/sgs/size/i386/size
+usr/src/cmd/sgs/symorder/symorder
+usr/src/cmd/sgs/tools/i386/.make.state.i386
+usr/src/cmd/sgs/tools/i386/piglatin
+usr/src/cmd/sgs/tools/i386/sgsmsg
+usr/src/cmd/sgs/tsort/i386/tsort
+usr/src/cmd/sgs/unifdef/i386/unifdef
+usr/src/cmd/sgs/yacc/amd64/llib-ly.ln
+usr/src/cmd/sgs/yacc/i386/llib-ly.ln
+usr/src/cmd/sgs/yacc/i386/yacc
+usr/src/cmd/sh/sh
+usr/src/cmd/shcomp/shcomp
+usr/src/cmd/smbios/smbios
+usr/src/cmd/smserverd/rpc.smserverd
+usr/src/cmd/smserverd/smed.h
+usr/src/cmd/smserverd/smed_svc.c
+usr/src/cmd/smserverd/smed_xdr.c
+usr/src/cmd/soelim/soelim
+usr/src/cmd/sort/amd64/sort
+usr/src/cmd/sort/amd64/sort.xpg4
+usr/src/cmd/sort/i386/sort
+usr/src/cmd/sort/i386/sort.xpg4
+usr/src/cmd/spell/compress
+usr/src/cmd/spell/hashcheck
+usr/src/cmd/spell/hashmake
+usr/src/cmd/spell/hashmk1
+usr/src/cmd/spell/hlista
+usr/src/cmd/spell/hlistb
+usr/src/cmd/spell/hstop
+usr/src/cmd/spell/htemp1
+usr/src/cmd/spell/spell
+usr/src/cmd/spell/spellhist
+usr/src/cmd/spell/spellin
+usr/src/cmd/spell/spellin1
+usr/src/cmd/spell/spellprog
+usr/src/cmd/split/split
+usr/src/cmd/sqlite/sqlite
+usr/src/cmd/srchtxt/srchtxt
+usr/src/cmd/srptadm/srptadm
+usr/src/cmd/srptsvc/svc-srpt
+usr/src/cmd/ssh/THIRDPARTYLICENSE
+usr/src/cmd/ssh/libopenbsd-compat/i386/lint.out
+usr/src/cmd/ssh/libopenbsd-compat/i386/llib-lopenbsd-compat.ln
+usr/src/cmd/ssh/libssh/i386/lint.out
+usr/src/cmd/ssh/libssh/i386/llib-lssh.ln
+usr/src/cmd/ssh/scp/scp
+usr/src/cmd/ssh/sftp-server/sftp-server
+usr/src/cmd/ssh/sftp/sftp
+usr/src/cmd/ssh/ssh-add/ssh-add
+usr/src/cmd/ssh/ssh-agent/ssh-agent
+usr/src/cmd/ssh/ssh-http-proxy-connect/ssh-http-proxy-connect
+usr/src/cmd/ssh/ssh-keygen/ssh-keygen
+usr/src/cmd/ssh/ssh-keyscan/ssh-keyscan
+usr/src/cmd/ssh/ssh-keysign/ssh-keysign
+usr/src/cmd/ssh/ssh-socks5-proxy-connect/ssh-socks5-proxy-connect
+usr/src/cmd/ssh/ssh/ssh
+usr/src/cmd/ssh/sshd/sshd
+usr/src/cmd/stat/arcstat/arcstat
+usr/src/cmd/stat/fsstat/fsstat
+usr/src/cmd/stat/iostat/iostat
+usr/src/cmd/stat/mpstat/mpstat
+usr/src/cmd/stat/vfsstat/vfsstat
+usr/src/cmd/stat/vmstat/vmstat
+usr/src/cmd/stat/ziostat/ziostat
+usr/src/cmd/stmfadm/stmfadm
+usr/src/cmd/stmfproxy/aluaadm/aluaadm
+usr/src/cmd/stmfproxy/stmfproxy/svc-stmfproxy
+usr/src/cmd/stmfsvc/svc-stmf
+usr/src/cmd/stmsboot/stmsboot
+usr/src/cmd/stmsboot/stmsboot_util
+usr/src/cmd/streams/log/strace
+usr/src/cmd/streams/log/strclean
+usr/src/cmd/streams/log/strerr
+usr/src/cmd/streams/strcmd/strchg
+usr/src/cmd/streams/strcmd/strconf
+usr/src/cmd/strings/amd64/strings
+usr/src/cmd/strings/i386/strings
+usr/src/cmd/su/embedded_su
+usr/src/cmd/su/su
+usr/src/cmd/su/su.static
+usr/src/cmd/sulogin/sulogin
+usr/src/cmd/sunpc/other/dos2unix
+usr/src/cmd/sunpc/other/unix2dos
+usr/src/cmd/svc/configd/svc.configd
+usr/src/cmd/svc/configd/svc.configd-native
+usr/src/cmd/svc/lsvcrun/lsvcrun
+usr/src/cmd/svc/mfstscan/mfstscan
+usr/src/cmd/svc/milestone/console-login.xml
+usr/src/cmd/svc/profile/check_limited
+usr/src/cmd/svc/profile/check_limited.all
+usr/src/cmd/svc/profile/check_limited.enabled
+usr/src/cmd/svc/profile/check_limited.notcovered
+usr/src/cmd/svc/profile/check_open
+usr/src/cmd/svc/profile/check_open.all
+usr/src/cmd/svc/profile/check_open.enabled
+usr/src/cmd/svc/profile/check_open.notcovered
+usr/src/cmd/svc/seed/common.db
+usr/src/cmd/svc/seed/global.db
+usr/src/cmd/svc/seed/miniroot.db
+usr/src/cmd/svc/seed/nonglobal.db
+usr/src/cmd/svc/servinfo/servinfo
+usr/src/cmd/svc/startd/svc.startd
+usr/src/cmd/svc/svcadm/svcadm
+usr/src/cmd/svc/svccfg/svccfg
+usr/src/cmd/svc/svccfg/svccfg-native
+usr/src/cmd/svc/svccfg/svccfg_grammar.c
+usr/src/cmd/svc/svccfg/svccfg_grammar.h
+usr/src/cmd/svc/svccfg/svccfg_lex.c
+usr/src/cmd/svc/svcprop/svcprop
+usr/src/cmd/svc/svcs/svcs
+usr/src/cmd/svr4pkg/installf/dofinal.i
+usr/src/cmd/svr4pkg/installf/installf
+usr/src/cmd/svr4pkg/installf/installf.i
+usr/src/cmd/svr4pkg/installf/main.i
+usr/src/cmd/svr4pkg/installf/removef.i
+usr/src/cmd/svr4pkg/libinst/copyf.i
+usr/src/cmd/svr4pkg/libinst/cvtpath.i
+usr/src/cmd/svr4pkg/libinst/depchk.i
+usr/src/cmd/svr4pkg/libinst/dockdeps.i
+usr/src/cmd/svr4pkg/libinst/doulimit.i
+usr/src/cmd/svr4pkg/libinst/dryrun.i
+usr/src/cmd/svr4pkg/libinst/echo.i
+usr/src/cmd/svr4pkg/libinst/eptstat.i
+usr/src/cmd/svr4pkg/libinst/finalck.i
+usr/src/cmd/svr4pkg/libinst/findscripts.i
+usr/src/cmd/svr4pkg/libinst/fixpath.i
+usr/src/cmd/svr4pkg/libinst/flex_dev.i
+usr/src/cmd/svr4pkg/libinst/is_local_host.i
+usr/src/cmd/svr4pkg/libinst/isreloc.i
+usr/src/cmd/svr4pkg/libinst/listmgr.i
+usr/src/cmd/svr4pkg/libinst/lockinst.i
+usr/src/cmd/svr4pkg/libinst/log.i
+usr/src/cmd/svr4pkg/libinst/mntinfo.i
+usr/src/cmd/svr4pkg/libinst/nblk.i
+usr/src/cmd/svr4pkg/libinst/ocfile.i
+usr/src/cmd/svr4pkg/libinst/open_package_datastream.i
+usr/src/cmd/svr4pkg/libinst/pathdup.i
+usr/src/cmd/svr4pkg/libinst/pkgdbmerg.i
+usr/src/cmd/svr4pkg/libinst/pkgobjmap.i
+usr/src/cmd/svr4pkg/libinst/pkgops.i
+usr/src/cmd/svr4pkg/libinst/procmap.i
+usr/src/cmd/svr4pkg/libinst/ptext.i
+usr/src/cmd/svr4pkg/libinst/putparam.i
+usr/src/cmd/svr4pkg/libinst/qreason.i
+usr/src/cmd/svr4pkg/libinst/qstrdup.i
+usr/src/cmd/svr4pkg/libinst/scriptvfy.i
+usr/src/cmd/svr4pkg/libinst/setadmin.i
+usr/src/cmd/svr4pkg/libinst/setlist.i
+usr/src/cmd/svr4pkg/libinst/setup_temporary_directory.i
+usr/src/cmd/svr4pkg/libinst/sml.i
+usr/src/cmd/svr4pkg/libinst/srcpath.i
+usr/src/cmd/svr4pkg/libinst/stub.i
+usr/src/cmd/svr4pkg/libinst/unpack_package_from_stream.i
+usr/src/cmd/svr4pkg/pkgadd/check.i
+usr/src/cmd/svr4pkg/pkgadd/main.i
+usr/src/cmd/svr4pkg/pkgadd/pkgadd
+usr/src/cmd/svr4pkg/pkgadd/quit.i
+usr/src/cmd/svr4pkg/pkgadm/addcert.i
+usr/src/cmd/svr4pkg/pkgadm/certs.i
+usr/src/cmd/svr4pkg/pkgadm/listcert.i
+usr/src/cmd/svr4pkg/pkgadm/lock.i
+usr/src/cmd/svr4pkg/pkgadm/main.i
+usr/src/cmd/svr4pkg/pkgadm/pkgadm
+usr/src/cmd/svr4pkg/pkgadm/removecert.i
+usr/src/cmd/svr4pkg/pkgchk/checkmap.i
+usr/src/cmd/svr4pkg/pkgchk/ckentry.i
+usr/src/cmd/svr4pkg/pkgchk/main.i
+usr/src/cmd/svr4pkg/pkgchk/pkgchk
+usr/src/cmd/svr4pkg/pkgcond/main.i
+usr/src/cmd/svr4pkg/pkgcond/pkgcond
+usr/src/cmd/svr4pkg/pkginfo/pkginfo
+usr/src/cmd/svr4pkg/pkginfo/pkginfo.i
+usr/src/cmd/svr4pkg/pkginstall/backup.i
+usr/src/cmd/svr4pkg/pkginstall/check.i
+usr/src/cmd/svr4pkg/pkginstall/cppath.i
+usr/src/cmd/svr4pkg/pkginstall/dockspace.i
+usr/src/cmd/svr4pkg/pkginstall/getinst.i
+usr/src/cmd/svr4pkg/pkginstall/instvol.i
+usr/src/cmd/svr4pkg/pkginstall/main.i
+usr/src/cmd/svr4pkg/pkginstall/merginfo.i
+usr/src/cmd/svr4pkg/pkginstall/pkgenv.i
+usr/src/cmd/svr4pkg/pkginstall/pkginstall
+usr/src/cmd/svr4pkg/pkginstall/pkgvolume.i
+usr/src/cmd/svr4pkg/pkginstall/quit.i
+usr/src/cmd/svr4pkg/pkginstall/reqexec.i
+usr/src/cmd/svr4pkg/pkginstall/sortmap.i
+usr/src/cmd/svr4pkg/pkgmk/main.i
+usr/src/cmd/svr4pkg/pkgmk/mkpkgmap.i
+usr/src/cmd/svr4pkg/pkgmk/pkgmk
+usr/src/cmd/svr4pkg/pkgmk/quit.i
+usr/src/cmd/svr4pkg/pkgmk/splpkgmap.i
+usr/src/cmd/svr4pkg/pkgname/pkgname
+usr/src/cmd/svr4pkg/pkgname/pkgname.i
+usr/src/cmd/svr4pkg/pkgparam/pkgparam
+usr/src/cmd/svr4pkg/pkgparam/pkgparam.i
+usr/src/cmd/svr4pkg/pkgproto/main.i
+usr/src/cmd/svr4pkg/pkgproto/pkgproto
+usr/src/cmd/svr4pkg/pkgremove/check.i
+usr/src/cmd/svr4pkg/pkgremove/delmap.i
+usr/src/cmd/svr4pkg/pkgremove/main.i
+usr/src/cmd/svr4pkg/pkgremove/pkgremove
+usr/src/cmd/svr4pkg/pkgremove/quit.i
+usr/src/cmd/svr4pkg/pkgremove/special.i
+usr/src/cmd/svr4pkg/pkgrm/check.i
+usr/src/cmd/svr4pkg/pkgrm/main.i
+usr/src/cmd/svr4pkg/pkgrm/pkgrm
+usr/src/cmd/svr4pkg/pkgrm/quit.i
+usr/src/cmd/svr4pkg/pkgscripts/cmdexec
+usr/src/cmd/svr4pkg/pkgscripts/cmdexec.i
+usr/src/cmd/svr4pkg/pkgscripts/i.CompCpio
+usr/src/cmd/svr4pkg/pkgscripts/i.awk
+usr/src/cmd/svr4pkg/pkgscripts/i.build
+usr/src/cmd/svr4pkg/pkgscripts/i.preserve
+usr/src/cmd/svr4pkg/pkgscripts/i.sed
+usr/src/cmd/svr4pkg/pkgscripts/r.awk
+usr/src/cmd/svr4pkg/pkgscripts/r.build
+usr/src/cmd/svr4pkg/pkgscripts/r.sed
+usr/src/cmd/svr4pkg/pkgserv/pkgserv
+usr/src/cmd/svr4pkg/pkgserv/pkgserv.i
+usr/src/cmd/svr4pkg/pkgtrans/main.i
+usr/src/cmd/svr4pkg/pkgtrans/pkgtrans
+usr/src/cmd/swap/amd64/swap
+usr/src/cmd/swap/i386/swap
+usr/src/cmd/sync/sync
+usr/src/cmd/sysdef/amd64/sysdef
+usr/src/cmd/sysdef/i386/sysdef
+usr/src/cmd/syseventadm/syseventadm
+usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd
+usr/src/cmd/syseventd/daemons/syseventd/syseventd
+usr/src/cmd/syslogd/i386/authlog
+usr/src/cmd/syslogd/i386/syslog
+usr/src/cmd/syslogd/i386/syslogd
+usr/src/cmd/tabs/tabs
+usr/src/cmd/tail/tail
+usr/src/cmd/tar/tar
+usr/src/cmd/tar/tar.dc
+usr/src/cmd/tbl/tbl
+usr/src/cmd/tcopy/tcopy
+usr/src/cmd/tcpd/THIRDPARTYLICENSE
+usr/src/cmd/tcpd/safe_finger
+usr/src/cmd/tcpd/sunman/
+usr/src/cmd/tcpd/tcpd
+usr/src/cmd/tcpd/tcpdchk
+usr/src/cmd/tcpd/tcpdmatch
+usr/src/cmd/tcpd/try-from
+usr/src/cmd/terminfo/errs
+usr/src/cmd/terminfo/terminfo.src
+usr/src/cmd/th_tools/th_define
+usr/src/cmd/th_tools/th_manage
+usr/src/cmd/th_tools/th_script
+usr/src/cmd/tic/tic
+usr/src/cmd/time/time
+usr/src/cmd/tip/aculog
+usr/src/cmd/tip/tip
+usr/src/cmd/tnf/prex/amd64/prex
+usr/src/cmd/tnf/prex/amd64/prexgram.c
+usr/src/cmd/tnf/prex/amd64/prexlex.c
+usr/src/cmd/tnf/prex/amd64/y.tab.h
+usr/src/cmd/tnf/prex/i386/prex
+usr/src/cmd/tnf/prex/i386/prexgram.c
+usr/src/cmd/tnf/prex/i386/prexlex.c
+usr/src/cmd/tnf/prex/i386/y.tab.h
+usr/src/cmd/tnf/tnfdump/tnfdump
+usr/src/cmd/tnf/tnfxtract/amd64/tnfxtract
+usr/src/cmd/tnf/tnfxtract/i386/tnfxtract
+usr/src/cmd/touch/touch
+usr/src/cmd/tput/tput
+usr/src/cmd/tr/tr
+usr/src/cmd/troff/nroff.d/nroff
+usr/src/cmd/troff/nroff.d/terms.d/tab.2631
+usr/src/cmd/troff/nroff.d/terms.d/tab.2631-c
+usr/src/cmd/troff/nroff.d/terms.d/tab.2631-e
+usr/src/cmd/troff/nroff.d/terms.d/tab.300
+usr/src/cmd/troff/nroff.d/terms.d/tab.300-12
+usr/src/cmd/troff/nroff.d/terms.d/tab.300s
+usr/src/cmd/troff/nroff.d/terms.d/tab.300s-12
+usr/src/cmd/troff/nroff.d/terms.d/tab.37
+usr/src/cmd/troff/nroff.d/terms.d/tab.382
+usr/src/cmd/troff/nroff.d/terms.d/tab.4000a
+usr/src/cmd/troff/nroff.d/terms.d/tab.450
+usr/src/cmd/troff/nroff.d/terms.d/tab.450-12
+usr/src/cmd/troff/nroff.d/terms.d/tab.832
+usr/src/cmd/troff/nroff.d/terms.d/tab.8510
+usr/src/cmd/troff/nroff.d/terms.d/tab.X
+usr/src/cmd/troff/nroff.d/terms.d/tab.lp
+usr/src/cmd/troff/nroff.d/terms.d/tab.tn300
+usr/src/cmd/troff/troff.d/makedev
+usr/src/cmd/troff/troff.d/ta
+usr/src/cmd/troff/troff.d/troff
+usr/src/cmd/true/true
+usr/src/cmd/truss/amd64/truss
+usr/src/cmd/truss/i386/truss
+usr/src/cmd/tsol/atohexlabel/atohexlabel
+usr/src/cmd/tsol/getlabel/getlabel
+usr/src/cmd/tsol/getzonepath/getzonepath
+usr/src/cmd/tsol/hextoalabel/hextoalabel
+usr/src/cmd/tsol/lslabels/lslabels
+usr/src/cmd/tsol/misc/relabel
+usr/src/cmd/tsol/misc/txzonemgr
+usr/src/cmd/tsol/plabel/plabel
+usr/src/cmd/tsol/setlabel/setlabel
+usr/src/cmd/tsol/tnchkdb/tnchkdb
+usr/src/cmd/tsol/tnctl/tnctl
+usr/src/cmd/tsol/tnd/tnd
+usr/src/cmd/tsol/tninfo/tninfo
+usr/src/cmd/tsol/updatehome/updatehome
+usr/src/cmd/tsol/zones/zoneshare
+usr/src/cmd/tsol/zones/zoneunshare
+usr/src/cmd/tty/tty
+usr/src/cmd/ttymon/stty
+usr/src/cmd/ttymon/stty.xpg4
+usr/src/cmd/ttymon/sttydefs
+usr/src/cmd/ttymon/ttyadm
+usr/src/cmd/ttymon/ttymon
+usr/src/cmd/tzreload/tzreload
+usr/src/cmd/uadmin/uadmin
+usr/src/cmd/ucodeadm/ucode_errno.c
+usr/src/cmd/ucodeadm/ucodeadm
+usr/src/cmd/ul/ul
+usr/src/cmd/uname/uname
+usr/src/cmd/units/units
+usr/src/cmd/unlink/unlink
+usr/src/cmd/unpack/unpack
+usr/src/cmd/userattr/userattr
+usr/src/cmd/users/listusers
+usr/src/cmd/utmp_update/utmp_update
+usr/src/cmd/utmpd/utmpd
+usr/src/cmd/valtools/ckdate
+usr/src/cmd/valtools/ckgid
+usr/src/cmd/valtools/ckint
+usr/src/cmd/valtools/ckitem
+usr/src/cmd/valtools/ckkeywd
+usr/src/cmd/valtools/ckpath
+usr/src/cmd/valtools/ckrange
+usr/src/cmd/valtools/ckstr
+usr/src/cmd/valtools/cktime
+usr/src/cmd/valtools/ckuid
+usr/src/cmd/valtools/ckyorn
+usr/src/cmd/valtools/puttext
+usr/src/cmd/vgrind/vfontedpr
+usr/src/cmd/vgrind/vgrind
+usr/src/cmd/vgrind/vgrindefs
+usr/src/cmd/vi/misc/ctags
+usr/src/cmd/vi/misc/ctags.xpg4
+usr/src/cmd/vi/misc/cxref
+usr/src/cmd/vi/misc/fold
+usr/src/cmd/vi/port/ex
+usr/src/cmd/vi/port/ex.xpg4
+usr/src/cmd/vi/port/ex.xpg6
+usr/src/cmd/vi/port/expreserve
+usr/src/cmd/vi/port/exrecover
+usr/src/cmd/volcheck/volcheck
+usr/src/cmd/volrmmount/volrmmount
+usr/src/cmd/vrrpadm/vrrpadm
+usr/src/cmd/vscan/vscanadm/vscanadm
+usr/src/cmd/vscan/vscand/vscand
+usr/src/cmd/vt/vtdaemon
+usr/src/cmd/vt/vtinfo
+usr/src/cmd/vt/vtxlock
+usr/src/cmd/w/amd64/w
+usr/src/cmd/w/i386/w
+usr/src/cmd/w/w.dc
+usr/src/cmd/wall/wall
+usr/src/cmd/which/which
+usr/src/cmd/who/utmpx
+usr/src/cmd/who/who
+usr/src/cmd/who/who.dc
+usr/src/cmd/who/who.xpg4
+usr/src/cmd/whodo/amd64/whodo
+usr/src/cmd/whodo/i386/whodo
+usr/src/cmd/whodo/whodo.dc
+usr/src/cmd/wracct/wracct
+usr/src/cmd/write/write
+usr/src/cmd/write/write.dc
+usr/src/cmd/wusbadm/wusbadm
+usr/src/cmd/xargs/xargs
+usr/src/cmd/xstr/xstr
+usr/src/cmd/xvm/ipagent/ipagent
+usr/src/cmd/yes/yes
+usr/src/cmd/ypcmd/makedbm
+usr/src/cmd/ypcmd/mkalias
+usr/src/cmd/ypcmd/mknetid/mknetid
+usr/src/cmd/ypcmd/multi
+usr/src/cmd/ypcmd/multi.awk
+usr/src/cmd/ypcmd/revnetgroup/revnetgroup
+usr/src/cmd/ypcmd/stdethers
+usr/src/cmd/ypcmd/stdhosts
+usr/src/cmd/ypcmd/udpublickey
+usr/src/cmd/ypcmd/yp
+usr/src/cmd/ypcmd/yp2lscripts/inityp2l
+usr/src/cmd/ypcmd/yp2lscripts/ypmap2src
+usr/src/cmd/ypcmd/ypalias
+usr/src/cmd/ypcmd/ypbind
+usr/src/cmd/ypcmd/ypcat
+usr/src/cmd/ypcmd/ypinit
+usr/src/cmd/ypcmd/ypmatch
+usr/src/cmd/ypcmd/yppasswd/rpc.yppasswdd
+usr/src/cmd/ypcmd/yppoll
+usr/src/cmd/ypcmd/yppush
+usr/src/cmd/ypcmd/ypserv
+usr/src/cmd/ypcmd/ypset
+usr/src/cmd/ypcmd/ypstart
+usr/src/cmd/ypcmd/ypstop
+usr/src/cmd/ypcmd/ypupdated/rpc.ypupdated
+usr/src/cmd/ypcmd/ypupdated/ypupdated_prot.h
+usr/src/cmd/ypcmd/ypwhich
+usr/src/cmd/ypcmd/ypxfr
+usr/src/cmd/ypcmd/ypxfr_1perday
+usr/src/cmd/ypcmd/ypxfr_1perhour
+usr/src/cmd/ypcmd/ypxfr_2perday
+usr/src/cmd/ypcmd/ypxfrd
+usr/src/cmd/ypcmd/ypxfrd.h
+usr/src/cmd/ypcmd/ypxfrd_xdr.c
+usr/src/cmd/yppasswd/yppasswd
+usr/src/cmd/zdb/amd64/zdb
+usr/src/cmd/zdb/i386/zdb
+usr/src/cmd/zdump/zdump
+usr/src/cmd/zfs/zfs
+usr/src/cmd/zic/native/
+usr/src/cmd/zic/tzselect
+usr/src/cmd/zic/zic
+usr/src/cmd/zinject/amd64/zinject
+usr/src/cmd/zinject/i386/zinject
+usr/src/cmd/zlogin/zlogin
+usr/src/cmd/zlook/amd64/zlook
+usr/src/cmd/zlook/i386/zlook
+usr/src/cmd/zoneadm/zoneadm
+usr/src/cmd/zoneadm/zonemon
+usr/src/cmd/zoneadmd/zoneadmd
+usr/src/cmd/zonecfg/zonecfg
+usr/src/cmd/zonecfg/zonecfg_grammar.tab.c
+usr/src/cmd/zonecfg/zonecfg_grammar.tab.h
+usr/src/cmd/zonecfg/zonecfg_lex.c
+usr/src/cmd/zonename/zonename
+usr/src/cmd/zonestat/zonestat/zonestat
+usr/src/cmd/zonestat/zonestatd/zonestatd
+usr/src/cmd/zpool/zpool
+usr/src/cmd/zstreamdump/zstreamdump
+usr/src/cmd/ztest/amd64/ztest
+usr/src/cmd/ztest/i386/ztest
+usr/src/common/mapfiles/gen/amd64_cc_map.noexeglobs
+usr/src/common/mapfiles/gen/amd64_gcc_map.noexeglobs
+usr/src/common/mapfiles/gen/i386_cc_map.noexeglobs
+usr/src/common/mapfiles/gen/i386_gcc_map.noexeglobs
+usr/src/grub/grub-0.97/grub/grub
+usr/src/grub/grub-0.97/stage1/stage1
+usr/src/grub/grub-0.97/stage1/stage1.exec
+usr/src/grub/grub-0.97/stage2/diskless
+usr/src/grub/grub-0.97/stage2/diskless.exec
+usr/src/grub/grub-0.97/stage2/diskless_size.h
+usr/src/grub/grub-0.97/stage2/e2fs_stage1_5
+usr/src/grub/grub-0.97/stage2/e2fs_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/fat_stage1_5
+usr/src/grub/grub-0.97/stage2/fat_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/ffs_stage1_5
+usr/src/grub/grub-0.97/stage2/ffs_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/iso9660_stage1_5
+usr/src/grub/grub-0.97/stage2/iso9660_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/jfs_stage1_5
+usr/src/grub/grub-0.97/stage2/jfs_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/minix_stage1_5
+usr/src/grub/grub-0.97/stage2/minix_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/nbgrub
+usr/src/grub/grub-0.97/stage2/nbloader
+usr/src/grub/grub-0.97/stage2/nbloader.exec
+usr/src/grub/grub-0.97/stage2/pre_stage2
+usr/src/grub/grub-0.97/stage2/pre_stage2.exec
+usr/src/grub/grub-0.97/stage2/pxegrub
+usr/src/grub/grub-0.97/stage2/pxeloader
+usr/src/grub/grub-0.97/stage2/pxeloader.exec
+usr/src/grub/grub-0.97/stage2/reiserfs_stage1_5
+usr/src/grub/grub-0.97/stage2/reiserfs_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/stage2
+usr/src/grub/grub-0.97/stage2/stage2_eltorito
+usr/src/grub/grub-0.97/stage2/stage2_size.h
+usr/src/grub/grub-0.97/stage2/start
+usr/src/grub/grub-0.97/stage2/start.exec
+usr/src/grub/grub-0.97/stage2/start_eltorito
+usr/src/grub/grub-0.97/stage2/start_eltorito.exec
+usr/src/grub/grub-0.97/stage2/ufs2_stage1_5
+usr/src/grub/grub-0.97/stage2/ufs2_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/ufs_stage1_5
+usr/src/grub/grub-0.97/stage2/ufs_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/vstafs_stage1_5
+usr/src/grub/grub-0.97/stage2/vstafs_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/xfs_stage1_5
+usr/src/grub/grub-0.97/stage2/xfs_stage1_5.exec
+usr/src/grub/grub-0.97/stage2/zfs_stage1_5
+usr/src/grub/grub-0.97/stage2/zfs_stage1_5.exec
+usr/src/head/mdiox.h
+usr/src/head/meta_basic.h
+usr/src/head/metacl.h
+usr/src/head/metad.h
+usr/src/head/metamed.h
+usr/src/head/metamhd.h
+usr/src/head/mhdx.h
+usr/src/head/rpcsvc/bootparam_prot.h
+usr/src/head/rpcsvc/mount.h
+usr/src/head/rpcsvc/nfs4_prot.h
+usr/src/head/rpcsvc/nfs_acl.h
+usr/src/head/rpcsvc/nfs_prot.c
+usr/src/head/rpcsvc/nfs_prot.h
+usr/src/head/rpcsvc/nis.h
+usr/src/head/rpcsvc/nlm_prot.h
+usr/src/head/rpcsvc/rex.h
+usr/src/head/rpcsvc/rquota.h
+usr/src/head/rpcsvc/rstat.h
+usr/src/head/rpcsvc/rusers.h
+usr/src/head/rpcsvc/rwall.h
+usr/src/head/rpcsvc/spray.h
+usr/src/head/rpcsvc/ufs_prot.h
+usr/src/install-nd-i386.out
+usr/src/lib/abi/apptrace/common/abienv.i
+usr/src/lib/abi/apptrace/common/apptrace.i
+usr/src/lib/brand/lx/cmd/ifconfig
+usr/src/lib/brand/lx/cmd/lx_isaexec_wrapper
+usr/src/lib/brand/lx/cmd/lx_lockd
+usr/src/lib/brand/lx/cmd/lx_native
+usr/src/lib/brand/lx/cmd/lx_statd
+usr/src/lib/brand/lx/cmd/lx_thunk
+usr/src/lib/brand/lx/lx_brand/amd64/assym.h
+usr/src/lib/brand/lx/lx_brand/amd64/lx_provider_impl.h
+usr/src/lib/brand/lx/lx_brand/i386/assym.h
+usr/src/lib/brand/lx/lx_brand/i386/lx_provider_impl.h
+usr/src/lib/brand/lx/lx_init/lxinit
+usr/src/lib/brand/lx/lx_support/lx_support
+usr/src/lib/brand/lx/lx_vdso/tools/vdso_tool
+usr/src/lib/brand/lx/netfiles/etc_default_nfs
+usr/src/lib/brand/lx/zone/lx_boot
+usr/src/lib/brand/lx/zone/lx_boot_zone_busybox
+usr/src/lib/brand/lx/zone/lx_boot_zone_debian
+usr/src/lib/brand/lx/zone/lx_boot_zone_redhat
+usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu
+usr/src/lib/brand/lx/zone/lx_distro_install
+usr/src/lib/brand/lx/zone/lx_install
+usr/src/lib/brand/lx/zone/lx_networking
+usr/src/lib/brand/shared/brand/amd64/assym.h
+usr/src/lib/brand/shared/brand/i386/assym.h
+usr/src/lib/brand/shared/zone/query
+usr/src/lib/brand/sn1/zone/sn1_boot
+usr/src/lib/brand/solaris10/cmd/s10_isaexec_wrapper
+usr/src/lib/brand/solaris10/cmd/s10_native
+usr/src/lib/brand/solaris10/cmd/s10_net_physical
+usr/src/lib/brand/solaris10/cmd/s10_python_wrapper
+usr/src/lib/brand/solaris10/s10_replacefile/s10_replacefile
+usr/src/lib/brand/solaris10/s10_support/s10_support
+usr/src/lib/brand/solaris10/zone/attach
+usr/src/lib/brand/solaris10/zone/clone
+usr/src/lib/brand/solaris10/zone/detach
+usr/src/lib/brand/solaris10/zone/image_install
+usr/src/lib/brand/solaris10/zone/p2v
+usr/src/lib/brand/solaris10/zone/postattach
+usr/src/lib/brand/solaris10/zone/poststate
+usr/src/lib/brand/solaris10/zone/prestate
+usr/src/lib/brand/solaris10/zone/preuninstall
+usr/src/lib/brand/solaris10/zone/s10_boot
+usr/src/lib/brand/solaris10/zone/uninstall
+usr/src/lib/c_synonyms/amd64/mapfile-vers
+usr/src/lib/c_synonyms/amd64/synonym_list
+usr/src/lib/c_synonyms/i386/mapfile-vers
+usr/src/lib/c_synonyms/i386/synonym_list
+usr/src/lib/cfgadm_plugins/sbd/common/ap_err.c
+usr/src/lib/cfgadm_plugins/sbd/sbdgenerr
+usr/src/lib/fm/libdiagcode/amd64/lint.out
+usr/src/lib/fm/libdiagcode/amd64/llib-ldiagcode.ln
+usr/src/lib/fm/libdiagcode/i386/lint.out
+usr/src/lib/fm/libdiagcode/i386/llib-ldiagcode.ln
+usr/src/lib/fm/libdiskstatus/amd64/lint.out
+usr/src/lib/fm/libdiskstatus/amd64/llib-ldiskstatus.ln
+usr/src/lib/fm/libdiskstatus/i386/lint.out
+usr/src/lib/fm/libdiskstatus/i386/llib-ldiskstatus.ln
+usr/src/lib/fm/libfmd_adm/amd64/fmd_rpc.c
+usr/src/lib/fm/libfmd_adm/amd64/fmd_rpc_adm.h
+usr/src/lib/fm/libfmd_adm/amd64/fmd_rpc_adm.x
+usr/src/lib/fm/libfmd_adm/amd64/fmd_xdr.c
+usr/src/lib/fm/libfmd_adm/amd64/lint.out
+usr/src/lib/fm/libfmd_adm/amd64/llib-lfmd_adm.ln
+usr/src/lib/fm/libfmd_adm/i386/fmd_rpc.c
+usr/src/lib/fm/libfmd_adm/i386/fmd_rpc_adm.h
+usr/src/lib/fm/libfmd_adm/i386/fmd_rpc_adm.x
+usr/src/lib/fm/libfmd_adm/i386/fmd_xdr.c
+usr/src/lib/fm/libfmd_adm/i386/lint.out
+usr/src/lib/fm/libfmd_adm/i386/llib-lfmd_adm.ln
+usr/src/lib/fm/libfmd_agent/amd64/lint.out
+usr/src/lib/fm/libfmd_agent/amd64/llib-lfmd_agent.ln
+usr/src/lib/fm/libfmd_agent/i386/lint.out
+usr/src/lib/fm/libfmd_agent/i386/llib-lfmd_agent.ln
+usr/src/lib/fm/libfmd_log/amd64/lint.out
+usr/src/lib/fm/libfmd_log/amd64/llib-lfmd_log.ln
+usr/src/lib/fm/libfmd_log/i386/lint.out
+usr/src/lib/fm/libfmd_log/i386/llib-lfmd_log.ln
+usr/src/lib/fm/libfmd_msg/amd64/lint.out
+usr/src/lib/fm/libfmd_msg/amd64/llib-lfmd_msg.ln
+usr/src/lib/fm/libfmd_msg/i386/lint.out
+usr/src/lib/fm/libfmd_msg/i386/llib-lfmd_msg.ln
+usr/src/lib/fm/libfmd_snmp/amd64/lint.out
+usr/src/lib/fm/libfmd_snmp/amd64/llib-lfmd_snmp.ln
+usr/src/lib/fm/libfmd_snmp/i386/lint.out
+usr/src/lib/fm/libfmd_snmp/i386/llib-lfmd_snmp.ln
+usr/src/lib/fm/libfmevent/amd64/lint.out
+usr/src/lib/fm/libfmevent/amd64/llib-lfmevent.ln
+usr/src/lib/fm/libfmevent/common/fmev_errstring.c
+usr/src/lib/fm/libfmevent/i386/lint.out
+usr/src/lib/fm/libfmevent/i386/llib-lfmevent.ln
+usr/src/lib/fm/libfmnotify/amd64/lint.out
+usr/src/lib/fm/libfmnotify/amd64/llib-lfmnotify.ln
+usr/src/lib/fm/libfmnotify/i386/lint.out
+usr/src/lib/fm/libfmnotify/i386/llib-lfmnotify.ln
+usr/src/lib/fm/libseslog/amd64/lint.out
+usr/src/lib/fm/libseslog/amd64/llib-lseslog.ln
+usr/src/lib/fm/libseslog/i386/lint.out
+usr/src/lib/fm/libseslog/i386/llib-lseslog.ln
+usr/src/lib/fm/topo/libtopo/amd64/lint.out
+usr/src/lib/fm/topo/libtopo/amd64/llib-ltopo.ln
+usr/src/lib/fm/topo/libtopo/common/topo_error.c
+usr/src/lib/fm/topo/libtopo/common/topo_tables.c
+usr/src/lib/fm/topo/libtopo/i386/lint.out
+usr/src/lib/fm/topo/libtopo/i386/llib-ltopo.ln
+usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-disk-hc-topology.xml
+usr/src/lib/fm/topo/maps/SUNW,Netra-X4200-M2/Netra-X4200-M2-disk-hc-topology.xml
+usr/src/lib/fm/topo/maps/SUNW,Sun-Fire-X4200-M2/Sun-Fire-X4200-M2-disk-hc-topology.xml
+usr/src/lib/fm/topo/maps/SUNW,Sun-Fire-X4200-Server/Sun-Fire-X4200-Server-disk-hc-topology.xml
+usr/src/lib/fm/topo/maps/SUNW,Sun-Fire-X4500/Sun-Fire-X4500-disk-hc-topology.xml
+usr/src/lib/fm/topo/maps/SUNW,Sun-Fire-X4540/Sun-Fire-X4540-disk-hc-topology.xml
+usr/src/lib/fm/topo/maps/SUNW,Sun-Fire-X4600-M2/Sun-Fire-X4600-M2-disk-hc-topology.xml
+usr/src/lib/fm/topo/maps/i386/xfp-hc-topology.xml
+usr/src/lib/fm/topo/maps/i86pc/fan-hc-topology.xml
+usr/src/lib/gss_mechs/mech_dh/backend/mech/dhmech_prot.h
+usr/src/lib/gss_mechs/mech_dh/backend/mech/xdr_token.c
+usr/src/lib/gss_mechs/mech_krb5/amd64/kwarnd.h
+usr/src/lib/gss_mechs/mech_krb5/amd64/kwarnd_clnt.c
+usr/src/lib/gss_mechs/mech_krb5/amd64/kwarnd_clnt_stubs.c
+usr/src/lib/gss_mechs/mech_krb5/amd64/kwarnd_handle.c
+usr/src/lib/gss_mechs/mech_krb5/amd64/kwarnd_xdr.c
+usr/src/lib/gss_mechs/mech_krb5/i386/kwarnd.h
+usr/src/lib/gss_mechs/mech_krb5/i386/kwarnd_clnt.c
+usr/src/lib/gss_mechs/mech_krb5/i386/kwarnd_clnt_stubs.c
+usr/src/lib/gss_mechs/mech_krb5/i386/kwarnd_handle.c
+usr/src/lib/gss_mechs/mech_krb5/i386/kwarnd_xdr.c
+usr/src/lib/hal/libhal-storage/amd64/hal-storage.pc
+usr/src/lib/hal/libhal-storage/amd64/libhal-storage.so.1.0.0
+usr/src/lib/hal/libhal-storage/amd64/lint.out
+usr/src/lib/hal/libhal-storage/amd64/llib-lhal-storage.ln
+usr/src/lib/hal/libhal-storage/i386/hal-storage.pc
+usr/src/lib/hal/libhal-storage/i386/libhal-storage.so.1.0.0
+usr/src/lib/hal/libhal-storage/i386/lint.out
+usr/src/lib/hal/libhal-storage/i386/llib-lhal-storage.ln
+usr/src/lib/hal/libhal/amd64/hal.pc
+usr/src/lib/hal/libhal/amd64/libhal.so.1.0.0
+usr/src/lib/hal/libhal/amd64/lint.out
+usr/src/lib/hal/libhal/amd64/llib-lhal.ln
+usr/src/lib/hal/libhal/i386/hal.pc
+usr/src/lib/hal/libhal/i386/libhal.so.1.0.0
+usr/src/lib/hal/libhal/i386/lint.out
+usr/src/lib/hal/libhal/i386/llib-lhal.ln
+usr/src/lib/hbaapi/amd64/lint.out
+usr/src/lib/hbaapi/amd64/llib-lHBAAPI.ln
+usr/src/lib/hbaapi/i386/lint.out
+usr/src/lib/hbaapi/i386/llib-lHBAAPI.ln
+usr/src/lib/krb5/kadm5/clnt/iprop.h
+usr/src/lib/krb5/kdb/iprop.h
+usr/src/lib/krb5/kdb/iprop_xdr.c
+usr/src/lib/libadm/amd64/lint.out
+usr/src/lib/libadm/amd64/llib-ladm.ln
+usr/src/lib/libadm/i386/lint.out
+usr/src/lib/libadm/i386/llib-ladm.ln
+usr/src/lib/libadt_jni/amd64/lint.out
+usr/src/lib/libadt_jni/amd64/llib-ladt_jni.ln
+usr/src/lib/libadt_jni/com/sun/audit/Audit.jar
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_admin_authenticate.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_admin_authenticate.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_filesystem_add.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_filesystem_add.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_filesystem_delete.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_filesystem_delete.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_filesystem_modify.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_filesystem_modify.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_ilb_delete_rule.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_ilb_delete_rule.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_ilb_disable_rule.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_ilb_disable_rule.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_ilb_enable_rule.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_ilb_enable_rule.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_logout.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_logout.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_network_add.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_network_add.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_network_delete.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_network_delete.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_network_modify.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_network_modify.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_printer_add.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_printer_add.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_printer_delete.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_printer_delete.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_printer_modify.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_printer_modify.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_role_login.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_role_login.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_scheduledjob_add.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_scheduledjob_add.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_scheduledjob_delete.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_scheduledjob_delete.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_scheduledjob_modify.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_scheduledjob_modify.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_serialport_add.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_serialport_add.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_serialport_delete.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_serialport_delete.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_serialport_modify.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_serialport_modify.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_uauth.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_uauth.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_usermgr_add.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_usermgr_add.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_usermgr_delete.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_usermgr_delete.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_usermgr_modify.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditEvent_usermgr_modify.java
+usr/src/lib/libadt_jni/com/sun/audit/AuditSession.class
+usr/src/lib/libadt_jni/com/sun/audit/AuditSession.h
+usr/src/lib/libadt_jni/common/adt_jni_event.c
+usr/src/lib/libadt_jni/common/mapfile-vers
+usr/src/lib/libadt_jni/i386/lint.out
+usr/src/lib/libadt_jni/i386/llib-ladt_jni.ln
+usr/src/lib/libadutils/amd64/lint.out
+usr/src/lib/libadutils/amd64/llib-ladutils.ln
+usr/src/lib/libadutils/i386/lint.out
+usr/src/lib/libadutils/i386/llib-ladutils.ln
+usr/src/lib/libaio/amd64/lint.out
+usr/src/lib/libaio/amd64/llib-laio.ln
+usr/src/lib/libaio/i386/lint.out
+usr/src/lib/libaio/i386/llib-laio.ln
+usr/src/lib/libast/amd64/libast.msg
+usr/src/lib/libast/amd64/lint.out
+usr/src/lib/libast/amd64/llib-last.ln
+usr/src/lib/libast/amd64/msgcc.out
+usr/src/lib/libast/amd64/msgs/
+usr/src/lib/libast/i386/libast.msg
+usr/src/lib/libast/i386/lint.out
+usr/src/lib/libast/i386/llib-last.ln
+usr/src/lib/libast/i386/msgcc.out
+usr/src/lib/libast/i386/msgs/
+usr/src/lib/libast/tmpastinclude/
+usr/src/lib/libavl/amd64/lint.out
+usr/src/lib/libavl/amd64/llib-lavl.ln
+usr/src/lib/libavl/i386/lint.out
+usr/src/lib/libavl/i386/llib-lavl.ln
+usr/src/lib/libbe/i386/lint.out
+usr/src/lib/libbe/i386/llib-lbe.ln
+usr/src/lib/libbrand/amd64/lint.out
+usr/src/lib/libbrand/amd64/llib-lbrand.ln
+usr/src/lib/libbrand/i386/lint.out
+usr/src/lib/libbrand/i386/llib-lbrand.ln
+usr/src/lib/libbsdmalloc/amd64/lint.out
+usr/src/lib/libbsdmalloc/amd64/llib-lbsdmalloc.ln
+usr/src/lib/libbsdmalloc/i386/lint.out
+usr/src/lib/libbsdmalloc/i386/llib-lbsdmalloc.ln
+usr/src/lib/libbsm/amd64/lint.out
+usr/src/lib/libbsm/amd64/llib-lbsm.ln
+usr/src/lib/libbsm/audit_uevents.h
+usr/src/lib/libbsm/common/adt_event.h
+usr/src/lib/libbsm/common/adt_xlate.c
+usr/src/lib/libbsm/i386/lint.out
+usr/src/lib/libbsm/i386/llib-lbsm.ln
+usr/src/lib/libc/THIRDPARTYLICENSE
+usr/src/lib/libc/amd64/assym.h
+usr/src/lib/libc/amd64/genassym
+usr/src/lib/libc/amd64/lint.out
+usr/src/lib/libc/amd64/llib-lc.ln
+usr/src/lib/libc/extract-copyright
+usr/src/lib/libc/i386/assym.h
+usr/src/lib/libc/i386/crt/_rtbootld.s
+usr/src/lib/libc/i386/etc/caplib
+usr/src/lib/libc/i386/genassym
+usr/src/lib/libc/i386/lint.out
+usr/src/lib/libc/i386/llib-lc.ln
+usr/src/lib/libc/i386_hwcap1/assym.h
+usr/src/lib/libc/i386_hwcap1/genassym
+usr/src/lib/libc/i386_hwcap2/assym.h
+usr/src/lib/libc/i386_hwcap2/genassym
+usr/src/lib/libc/i386_hwcap3/assym.h
+usr/src/lib/libc/i386_hwcap3/genassym
+usr/src/lib/libc/port/gen/errlst.c
+usr/src/lib/libc/port/gen/new_list.c
+usr/src/lib/libc_db/amd64/lint.out
+usr/src/lib/libc_db/amd64/llib-lc_db.ln
+usr/src/lib/libc_db/i386/lint.out
+usr/src/lib/libc_db/i386/llib-lc_db.ln
+usr/src/lib/libcfgadm/amd64/lint.out
+usr/src/lib/libcfgadm/amd64/llib-lcfgadm.ln
+usr/src/lib/libcfgadm/i386/lint.out
+usr/src/lib/libcfgadm/i386/llib-lcfgadm.ln
+usr/src/lib/libcmd/amd64/libcmd.msg
+usr/src/lib/libcmd/amd64/lint.out
+usr/src/lib/libcmd/amd64/llib-lcmd.ln
+usr/src/lib/libcmd/amd64/msgcc.out
+usr/src/lib/libcmd/amd64/msgs/
+usr/src/lib/libcmd/i386/libcmd.msg
+usr/src/lib/libcmd/i386/lint.out
+usr/src/lib/libcmd/i386/llib-lcmd.ln
+usr/src/lib/libcmd/i386/msgcc.out
+usr/src/lib/libcmd/i386/msgs/
+usr/src/lib/libcmd/tmpastinclude/
+usr/src/lib/libcmdutils/amd64/lint.out
+usr/src/lib/libcmdutils/amd64/llib-lcmdutils.ln
+usr/src/lib/libcmdutils/i386/lint.out
+usr/src/lib/libcmdutils/i386/llib-lcmdutils.ln
+usr/src/lib/libcommputil/amd64/lint.out
+usr/src/lib/libcommputil/amd64/llib-lcommputil.ln
+usr/src/lib/libcommputil/i386/lint.out
+usr/src/lib/libcommputil/i386/llib-lcommputil.ln
+usr/src/lib/libcontract/amd64/lint.out
+usr/src/lib/libcontract/amd64/llib-lcontract.ln
+usr/src/lib/libcontract/i386/lint.out
+usr/src/lib/libcontract/i386/llib-lcontract.ln
+usr/src/lib/libcpc/amd64/lint.out
+usr/src/lib/libcpc/amd64/llib-lcpc.ln
+usr/src/lib/libcpc/i386/lint.out
+usr/src/lib/libcpc/i386/llib-lcpc.ln
+usr/src/lib/libcrypt/amd64/lint.out
+usr/src/lib/libcrypt/amd64/llib-lcrypt.ln
+usr/src/lib/libcrypt/i386/lint.out
+usr/src/lib/libcrypt/i386/llib-lcrypt.ln
+usr/src/lib/libcryptoutil/amd64/lint.out
+usr/src/lib/libcryptoutil/amd64/llib-lcryptoutil.ln
+usr/src/lib/libcryptoutil/i386/lint.out
+usr/src/lib/libcryptoutil/i386/llib-lcryptoutil.ln
+usr/src/lib/libctf/amd64/lint.out
+usr/src/lib/libctf/amd64/llib-lctf.ln
+usr/src/lib/libctf/i386/lint.out
+usr/src/lib/libctf/i386/llib-lctf.ln
+usr/src/lib/libcurses/amd64/lint.out
+usr/src/lib/libcurses/amd64/llib-lcurses.ln
+usr/src/lib/libcurses/i386/lint.out
+usr/src/lib/libcurses/i386/llib-lcurses.ln
+usr/src/lib/libcurses/screen/curses.h
+usr/src/lib/libcurses/screen/keycaps
+usr/src/lib/libcurses/screen/keyname.c
+usr/src/lib/libcurses/screen/term.h
+usr/src/lib/libcurses/screen/termcap.c
+usr/src/lib/libcurses/screen/tifget.c
+usr/src/lib/libcurses/screen/tifnames.c
+usr/src/lib/libcurses/screen/tiget.c
+usr/src/lib/libcurses/screen/tinames.c
+usr/src/lib/libcurses/screen/tmp/
+usr/src/lib/libcurses/screen/tnames.c
+usr/src/lib/libdevice/amd64/lint.out
+usr/src/lib/libdevice/amd64/llib-ldevice.ln
+usr/src/lib/libdevice/i386/lint.out
+usr/src/lib/libdevice/i386/llib-ldevice.ln
+usr/src/lib/libdevid/amd64/lint.out
+usr/src/lib/libdevid/amd64/llib-ldevid.ln
+usr/src/lib/libdevid/i386/lint.out
+usr/src/lib/libdevid/i386/llib-ldevid.ln
+usr/src/lib/libdevinfo/amd64/lint.out
+usr/src/lib/libdevinfo/amd64/llib-ldevinfo.ln
+usr/src/lib/libdevinfo/i386/lint.out
+usr/src/lib/libdevinfo/i386/llib-ldevinfo.ln
+usr/src/lib/libdhcpagent/i386/lint.out
+usr/src/lib/libdhcpagent/i386/llib-ldhcpagent.ln
+usr/src/lib/libdhcpsvc/head/dhcp_svc_confkey.h
+usr/src/lib/libdhcpsvc/modules/files0/i386/ds_SUNWfiles.so.0
+usr/src/lib/libdhcpsvc/private/i386/lint.out
+usr/src/lib/libdhcpsvc/private/i386/llib-ldhcpsvc.ln
+usr/src/lib/libdhcputil/i386/lint.out
+usr/src/lib/libdhcputil/i386/llib-ldhcputil.ln
+usr/src/lib/libdisasm/amd64/lint.out
+usr/src/lib/libdisasm/amd64/llib-ldisasm.ln
+usr/src/lib/libdisasm/i386/lint.out
+usr/src/lib/libdisasm/i386/llib-ldisasm.ln
+usr/src/lib/libdiskmgt/amd64/lint.out
+usr/src/lib/libdiskmgt/amd64/llib-ldiskmgt.ln
+usr/src/lib/libdiskmgt/i386/lint.out
+usr/src/lib/libdiskmgt/i386/llib-ldiskmgt.ln
+usr/src/lib/libdladm/amd64/lint.out
+usr/src/lib/libdladm/amd64/llib-ldladm.ln
+usr/src/lib/libdladm/i386/lint.out
+usr/src/lib/libdladm/i386/llib-ldladm.ln
+usr/src/lib/libdll/amd64/libdll.msg
+usr/src/lib/libdll/amd64/lint.out
+usr/src/lib/libdll/amd64/llib-ldll.ln
+usr/src/lib/libdll/amd64/msgcc.out
+usr/src/lib/libdll/amd64/msgs/
+usr/src/lib/libdll/i386/libdll.msg
+usr/src/lib/libdll/i386/lint.out
+usr/src/lib/libdll/i386/llib-ldll.ln
+usr/src/lib/libdll/i386/msgcc.out
+usr/src/lib/libdll/i386/msgs/
+usr/src/lib/libdll/tmpastinclude/
+usr/src/lib/libdlpi/amd64/lint.out
+usr/src/lib/libdlpi/amd64/llib-ldlpi.ln
+usr/src/lib/libdlpi/i386/lint.out
+usr/src/lib/libdlpi/i386/llib-ldlpi.ln
+usr/src/lib/libdns_sd/amd64/lint.out
+usr/src/lib/libdns_sd/amd64/llib-ldns_sd.ln
+usr/src/lib/libdns_sd/i386/lint.out
+usr/src/lib/libdns_sd/i386/llib-ldns_sd.ln
+usr/src/lib/libdns_sd/java/com/apple/dnssd/AppleBrowser.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/AppleDNSRecord.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/AppleDNSSD.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/AppleDNSSDException.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/AppleDomainEnum.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/AppleQuery.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/AppleRecordRegistrar.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/AppleRegistration.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/AppleResolver.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/AppleService.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java.h
+usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/dnssd.jar
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/BrowserApp$1.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/BrowserApp.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/BrowserApp.jar
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/BrowserListModel$BrowserListElem.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/BrowserListModel.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/DomainListModel.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/ListenerThread.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/ServicesBrowserListModel.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/SimpleChat$1.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/SimpleChat.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/SimpleChat.jar
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/SwingBrowseListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/SwingDomainListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/SwingQueryListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/SwingResolveListener.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/TargetListElem.class
+usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/TargetListModel.class
+usr/src/lib/libdoor/amd64/lint.out
+usr/src/lib/libdoor/amd64/llib-ldoor.ln
+usr/src/lib/libdoor/i386/lint.out
+usr/src/lib/libdoor/i386/llib-ldoor.ln
+usr/src/lib/libdscfg/i386/lint.out
+usr/src/lib/libdscfg/i386/llib-ldscfg.ln
+usr/src/lib/libdtrace/amd64/dt_grammar.c
+usr/src/lib/libdtrace/amd64/dt_grammar.h
+usr/src/lib/libdtrace/amd64/dt_lex.c
+usr/src/lib/libdtrace/amd64/lint.out
+usr/src/lib/libdtrace/amd64/llib-ldtrace.ln
+usr/src/lib/libdtrace/amd64/y.output
+usr/src/lib/libdtrace/common/dt_errtags.c
+usr/src/lib/libdtrace/common/dt_names.c
+usr/src/lib/libdtrace/common/errno.d
+usr/src/lib/libdtrace/common/io.d
+usr/src/lib/libdtrace/common/io.sed
+usr/src/lib/libdtrace/common/ip.d
+usr/src/lib/libdtrace/common/ip.sed
+usr/src/lib/libdtrace/common/net.d
+usr/src/lib/libdtrace/common/net.sed
+usr/src/lib/libdtrace/common/procfs.d
+usr/src/lib/libdtrace/common/procfs.sed
+usr/src/lib/libdtrace/common/signal.d
+usr/src/lib/libdtrace/common/sysevent.d
+usr/src/lib/libdtrace/common/sysevent.sed
+usr/src/lib/libdtrace/common/tcp.d
+usr/src/lib/libdtrace/common/tcp.sed
+usr/src/lib/libdtrace/common/udp.d
+usr/src/lib/libdtrace/common/udp.sed
+usr/src/lib/libdtrace/i386/dt_grammar.c
+usr/src/lib/libdtrace/i386/dt_grammar.h
+usr/src/lib/libdtrace/i386/dt_lex.c
+usr/src/lib/libdtrace/i386/lint.out
+usr/src/lib/libdtrace/i386/llib-ldtrace.ln
+usr/src/lib/libdtrace/i386/regs.d
+usr/src/lib/libdtrace/i386/regs.sed
+usr/src/lib/libdtrace/i386/y.output
+usr/src/lib/libdtrace_jni/java/classes/
+usr/src/lib/libdtrace_jni/java/lib/
+usr/src/lib/libdtrace_jni/java/native/
+usr/src/lib/libefi/amd64/lint.out
+usr/src/lib/libefi/amd64/llib-lefi.ln
+usr/src/lib/libefi/i386/lint.out
+usr/src/lib/libefi/i386/llib-lefi.ln
+usr/src/lib/libelfsign/i386/lint.out
+usr/src/lib/libelfsign/i386/llib-lelfsign.ln
+usr/src/lib/libeti/form/amd64/lint.out
+usr/src/lib/libeti/form/amd64/llib-lform.ln
+usr/src/lib/libeti/form/i386/lint.out
+usr/src/lib/libeti/form/i386/llib-lform.ln
+usr/src/lib/libeti/menu/amd64/lint.out
+usr/src/lib/libeti/menu/amd64/llib-lmenu.ln
+usr/src/lib/libeti/menu/i386/lint.out
+usr/src/lib/libeti/menu/i386/llib-lmenu.ln
+usr/src/lib/libeti/panel/amd64/lint.out
+usr/src/lib/libeti/panel/amd64/llib-lpanel.ln
+usr/src/lib/libeti/panel/i386/lint.out
+usr/src/lib/libeti/panel/i386/llib-lpanel.ln
+usr/src/lib/libexacct/amd64/lint.out
+usr/src/lib/libexacct/amd64/llib-lexacct.ln
+usr/src/lib/libexacct/demo/exdump
+usr/src/lib/libexacct/i386/lint.out
+usr/src/lib/libexacct/i386/llib-lexacct.ln
+usr/src/lib/libfcoe/amd64/lint.out
+usr/src/lib/libfcoe/amd64/llib-lfcoe.ln
+usr/src/lib/libfcoe/i386/lint.out
+usr/src/lib/libfcoe/i386/llib-lfcoe.ln
+usr/src/lib/libfdisk/amd64/lint.out
+usr/src/lib/libfdisk/amd64/llib-lfdisk.ln
+usr/src/lib/libfdisk/i386/lint.out
+usr/src/lib/libfdisk/i386/llib-lfdisk.ln
+usr/src/lib/libfru/amd64/pics/
+usr/src/lib/libfru/i386/pics/
+usr/src/lib/libfsmgt/common/nfs_sec.c
+usr/src/lib/libfsmgt/common/replica.c
+usr/src/lib/libfsmgt/common/sharetab.c
+usr/src/lib/libfsmgt/i386/lint.out
+usr/src/lib/libfsmgt/i386/llib-lfsmgt.ln
+usr/src/lib/libfstyp/i386/lint.out
+usr/src/lib/libfstyp/i386/llib-lfstyp.ln
+usr/src/lib/libgen/amd64/lint.out
+usr/src/lib/libgen/amd64/llib-lgen.ln
+usr/src/lib/libgen/i386/lint.out
+usr/src/lib/libgen/i386/llib-lgen.ln
+usr/src/lib/libgrubmgmt/i386/lint.out
+usr/src/lib/libgrubmgmt/i386/llib-lgrubmgmt.ln
+usr/src/lib/libgss/amd64/lint.out
+usr/src/lib/libgss/amd64/llib-lgss.ln
+usr/src/lib/libgss/i386/lint.out
+usr/src/lib/libgss/i386/llib-lgss.ln
+usr/src/lib/libhotplug/amd64/lint.out
+usr/src/lib/libhotplug/amd64/llib-lhotplug.ln
+usr/src/lib/libhotplug/i386/lint.out
+usr/src/lib/libhotplug/i386/llib-lhotplug.ln
+usr/src/lib/libidmap/amd64/idmap_xdr.c
+usr/src/lib/libidmap/amd64/lint.out
+usr/src/lib/libidmap/amd64/llib-lidmap.ln
+usr/src/lib/libidmap/i386/idmap_xdr.c
+usr/src/lib/libidmap/i386/lint.out
+usr/src/lib/libidmap/i386/llib-lidmap.ln
+usr/src/lib/libilb/amd64/lint.out
+usr/src/lib/libilb/amd64/llib-lilb.ln
+usr/src/lib/libilb/i386/lint.out
+usr/src/lib/libilb/i386/llib-lilb.ln
+usr/src/lib/libima/amd64/lint.out
+usr/src/lib/libima/amd64/llib-lima.ln
+usr/src/lib/libima/i386/lint.out
+usr/src/lib/libima/i386/llib-lima.ln
+usr/src/lib/libinetsvc/i386/lint.out
+usr/src/lib/libinetsvc/i386/llib-linetsvc.ln
+usr/src/lib/libinetutil/amd64/lint.out
+usr/src/lib/libinetutil/amd64/llib-linetutil.ln
+usr/src/lib/libinetutil/i386/lint.out
+usr/src/lib/libinetutil/i386/llib-linetutil.ln
+usr/src/lib/libinstzones/common/zones.i
+usr/src/lib/libinstzones/common/zones_args.i
+usr/src/lib/libinstzones/common/zones_exec.i
+usr/src/lib/libinstzones/common/zones_locks.i
+usr/src/lib/libinstzones/common/zones_lofs.i
+usr/src/lib/libinstzones/common/zones_paths.i
+usr/src/lib/libinstzones/common/zones_states.i
+usr/src/lib/libinstzones/common/zones_str.i
+usr/src/lib/libinstzones/common/zones_utils.i
+usr/src/lib/libinstzones/i386/lint.out
+usr/src/lib/libinstzones/i386/llib-linstzones.ln
+usr/src/lib/libintl/amd64/lint.out
+usr/src/lib/libintl/amd64/llib-lintl.ln
+usr/src/lib/libintl/i386/lint.out
+usr/src/lib/libintl/i386/llib-lintl.ln
+usr/src/lib/libipadm/i386/lint.out
+usr/src/lib/libipadm/i386/llib-lipadm.ln
+usr/src/lib/libipmi/amd64/lint.out
+usr/src/lib/libipmi/amd64/llib-lipmi.ln
+usr/src/lib/libipmi/common/ipmi_tables.c
+usr/src/lib/libipmi/i386/lint.out
+usr/src/lib/libipmi/i386/llib-lipmi.ln
+usr/src/lib/libipmp/i386/lint.out
+usr/src/lib/libipmp/i386/llib-lipmp.ln
+usr/src/lib/libipp/amd64/lint.out
+usr/src/lib/libipp/amd64/llib-lipp.ln
+usr/src/lib/libipp/i386/lint.out
+usr/src/lib/libipp/i386/llib-lipp.ln
+usr/src/lib/libipsecutil/amd64/lint.out
+usr/src/lib/libipsecutil/amd64/llib-lipsecutil.ln
+usr/src/lib/libipsecutil/i386/lint.out
+usr/src/lib/libipsecutil/i386/llib-lipsecutil.ln
+usr/src/lib/libiscsit/amd64/lint.out
+usr/src/lib/libiscsit/amd64/llib-liscsit.ln
+usr/src/lib/libiscsit/i386/lint.out
+usr/src/lib/libiscsit/i386/llib-liscsit.ln
+usr/src/lib/libkmf/THIRDPARTYLICENSE
+usr/src/lib/libkmf/ber_der/amd64/lint.out
+usr/src/lib/libkmf/ber_der/amd64/llib-lkmfberder.ln
+usr/src/lib/libkmf/ber_der/i386/lint.out
+usr/src/lib/libkmf/ber_der/i386/llib-lkmfberder.ln
+usr/src/lib/libkmf/libkmf/amd64/lint.out
+usr/src/lib/libkmf/libkmf/amd64/llib-lkmf.ln
+usr/src/lib/libkmf/libkmf/i386/lint.out
+usr/src/lib/libkmf/libkmf/i386/llib-lkmf.ln
+usr/src/lib/libkstat/amd64/lint.out
+usr/src/lib/libkstat/amd64/llib-lkstat.ln
+usr/src/lib/libkstat/i386/lint.out
+usr/src/lib/libkstat/i386/llib-lkstat.ln
+usr/src/lib/libkvm/amd64/lint.out
+usr/src/lib/libkvm/amd64/llib-lkvm.ln
+usr/src/lib/libkvm/i386/lint.out
+usr/src/lib/libkvm/i386/llib-lkvm.ln
+usr/src/lib/libldap4/amd64/libldap.so.4
+usr/src/lib/libldap4/i386/libldap.so.4
+usr/src/lib/libldap5/amd64/libldap.so.5
+usr/src/lib/libldap5/amd64/lint.out
+usr/src/lib/libldap5/amd64/llib-lldap.ln
+usr/src/lib/libldap5/i386/libldap.so.5
+usr/src/lib/libldap5/i386/lint.out
+usr/src/lib/libldap5/i386/llib-lldap.ln
+usr/src/lib/liblgrp/amd64/lint.out
+usr/src/lib/liblgrp/amd64/llib-llgrp.ln
+usr/src/lib/liblgrp/i386/lint.out
+usr/src/lib/liblgrp/i386/llib-llgrp.ln
+usr/src/lib/libmail/amd64/lint.out
+usr/src/lib/libmail/amd64/llib-lmail.ln
+usr/src/lib/libmail/i386/lint.out
+usr/src/lib/libmail/i386/llib-lmail.ln
+usr/src/lib/libmalloc/amd64/lint.out
+usr/src/lib/libmalloc/amd64/llib-lmalloc.ln
+usr/src/lib/libmalloc/i386/lint.out
+usr/src/lib/libmalloc/i386/llib-lmalloc.ln
+usr/src/lib/libmapid/i386/lint.out
+usr/src/lib/libmapid/i386/llib-lmapid.ln
+usr/src/lib/libmapmalloc/amd64/lint.out
+usr/src/lib/libmapmalloc/amd64/llib-lmapmalloc.ln
+usr/src/lib/libmapmalloc/i386/lint.out
+usr/src/lib/libmapmalloc/i386/llib-lmapmalloc.ln
+usr/src/lib/libmd/amd64/lint.out
+usr/src/lib/libmd/amd64/llib-lmd.ln
+usr/src/lib/libmd/amd64/md5_amd64.s
+usr/src/lib/libmd/amd64/sha1-x86_64.s
+usr/src/lib/libmd/amd64/sha256-x86_64.s
+usr/src/lib/libmd/amd64/sha512-x86_64.s
+usr/src/lib/libmd/i386/lint.out
+usr/src/lib/libmd/i386/llib-lmd.ln
+usr/src/lib/libmd5/amd64/lint.out
+usr/src/lib/libmd5/amd64/llib-lmd5.ln
+usr/src/lib/libmd5/i386/lint.out
+usr/src/lib/libmd5/i386/llib-lmd5.ln
+usr/src/lib/libmp/amd64/libmp.so.2
+usr/src/lib/libmp/i386/libmp.so.2
+usr/src/lib/libmtmalloc/amd64/lint.out
+usr/src/lib/libmtmalloc/amd64/llib-lmtmalloc.ln
+usr/src/lib/libmtmalloc/i386/lint.out
+usr/src/lib/libmtmalloc/i386/llib-lmtmalloc.ln
+usr/src/lib/libndmp/amd64/lint.out
+usr/src/lib/libndmp/amd64/llib-lndmp.ln
+usr/src/lib/libndmp/i386/lint.out
+usr/src/lib/libndmp/i386/llib-lndmp.ln
+usr/src/lib/libnisdb/db.h
+usr/src/lib/libnisdb/db_c.h
+usr/src/lib/libnisdb/db_c_xdr.c
+usr/src/lib/libnisdb/db_dictionary.h
+usr/src/lib/libnisdb/db_dictionary_c.h
+usr/src/lib/libnisdb/db_dictionary_c_xdr.c
+usr/src/lib/libnisdb/db_dictlog.h
+usr/src/lib/libnisdb/db_dictlog_c.h
+usr/src/lib/libnisdb/db_dictlog_c_xdr.c
+usr/src/lib/libnisdb/db_entry.h
+usr/src/lib/libnisdb/db_entry_c.h
+usr/src/lib/libnisdb/db_entry_c_xdr.c
+usr/src/lib/libnisdb/db_index.h
+usr/src/lib/libnisdb/db_index_c.h
+usr/src/lib/libnisdb/db_index_c_xdr.c
+usr/src/lib/libnisdb/db_index_entry.h
+usr/src/lib/libnisdb/db_index_entry_c.h
+usr/src/lib/libnisdb/db_index_entry_c_xdr.c
+usr/src/lib/libnisdb/db_item.h
+usr/src/lib/libnisdb/db_item_c.h
+usr/src/lib/libnisdb/db_item_c_xdr.c
+usr/src/lib/libnisdb/db_log.h
+usr/src/lib/libnisdb/db_log_c.h
+usr/src/lib/libnisdb/db_log_c_xdr.c
+usr/src/lib/libnisdb/db_log_entry.h
+usr/src/lib/libnisdb/db_log_entry_c.h
+usr/src/lib/libnisdb/db_log_entry_c_xdr.c
+usr/src/lib/libnisdb/db_mindex.h
+usr/src/lib/libnisdb/db_mindex_c.h
+usr/src/lib/libnisdb/db_mindex_c_xdr.c
+usr/src/lib/libnisdb/db_query.h
+usr/src/lib/libnisdb/db_query_c.h
+usr/src/lib/libnisdb/db_query_c_xdr.c
+usr/src/lib/libnisdb/db_scheme.h
+usr/src/lib/libnisdb/db_scheme_c.h
+usr/src/lib/libnisdb/db_scheme_c_xdr.c
+usr/src/lib/libnisdb/db_table.h
+usr/src/lib/libnisdb/db_table_c.h
+usr/src/lib/libnisdb/db_table_c_xdr.c
+usr/src/lib/libnisdb/db_vers.h
+usr/src/lib/libnisdb/db_vers_c.h
+usr/src/lib/libnisdb/db_vers_c_xdr.c
+usr/src/lib/libnisdb/libnisdb.so.2
+usr/src/lib/libnls/amd64/lint.out
+usr/src/lib/libnls/amd64/llib-lnls.ln
+usr/src/lib/libnls/i386/lint.out
+usr/src/lib/libnls/i386/llib-lnls.ln
+usr/src/lib/libnsctl/i386/lint.out
+usr/src/lib/libnsctl/i386/llib-lnsctl.ln
+usr/src/lib/libnsl/amd64/lint.out
+usr/src/lib/libnsl/amd64/llib-lnsl.ln
+usr/src/lib/libnsl/i386/lint.out
+usr/src/lib/libnsl/i386/llib-lnsl.ln
+usr/src/lib/libnsl/nis/gen/nis_clnt.h
+usr/src/lib/libntfs/THIRDPARTYLICENSE
+usr/src/lib/libntfs/i386/libntfs.so.10
+usr/src/lib/libnvpair/amd64/lint.out
+usr/src/lib/libnvpair/amd64/llib-lnvpair.ln
+usr/src/lib/libnvpair/i386/lint.out
+usr/src/lib/libnvpair/i386/llib-lnvpair.ln
+usr/src/lib/libnwam/i386/lint.out
+usr/src/lib/libnwam/i386/llib-lnwam.ln
+usr/src/lib/libpam/amd64/lint.out
+usr/src/lib/libpam/amd64/llib-lpam.ln
+usr/src/lib/libpam/i386/lint.out
+usr/src/lib/libpam/i386/llib-lpam.ln
+usr/src/lib/libparted/THIRDPARTYLICENSE
+usr/src/lib/libparted/i386/libparted.so.8
+usr/src/lib/libpctx/amd64/lint.out
+usr/src/lib/libpctx/amd64/llib-lpctx.ln
+usr/src/lib/libpctx/i386/lint.out
+usr/src/lib/libpctx/i386/llib-lpctx.ln
+usr/src/lib/libpicl/amd64/lint.out
+usr/src/lib/libpicl/amd64/llib-lpicl.ln
+usr/src/lib/libpicl/i386/lint.out
+usr/src/lib/libpicl/i386/llib-lpicl.ln
+usr/src/lib/libpicltree/i386/lint.out
+usr/src/lib/libpicltree/i386/llib-lpicltree.ln
+usr/src/lib/libpkg/common/canonize.i
+usr/src/lib/libpkg/common/ckparam.i
+usr/src/lib/libpkg/common/ckvolseq.i
+usr/src/lib/libpkg/common/devtype.i
+usr/src/lib/libpkg/common/dstream.i
+usr/src/lib/libpkg/common/fmkdir.i
+usr/src/lib/libpkg/common/gpkglist.i
+usr/src/lib/libpkg/common/gpkgmap.i
+usr/src/lib/libpkg/common/handlelocalfs.i
+usr/src/lib/libpkg/common/isdir.i
+usr/src/lib/libpkg/common/keystore.i
+usr/src/lib/libpkg/common/logerr.i
+usr/src/lib/libpkg/common/mappath.i
+usr/src/lib/libpkg/common/ncgrpw.i
+usr/src/lib/libpkg/common/nhash.i
+usr/src/lib/libpkg/common/p12lib.i
+usr/src/lib/libpkg/common/pkgerr.i
+usr/src/lib/libpkg/common/pkgexecl.i
+usr/src/lib/libpkg/common/pkgexecv.i
+usr/src/lib/libpkg/common/pkgmount.i
+usr/src/lib/libpkg/common/pkgserv.i
+usr/src/lib/libpkg/common/pkgstr.i
+usr/src/lib/libpkg/common/pkgtrans.i
+usr/src/lib/libpkg/common/pkgweb.i
+usr/src/lib/libpkg/common/ppkgmap.i
+usr/src/lib/libpkg/common/progerr.i
+usr/src/lib/libpkg/common/putcfile.i
+usr/src/lib/libpkg/common/rrmdir.i
+usr/src/lib/libpkg/common/runcmd.i
+usr/src/lib/libpkg/common/security.i
+usr/src/lib/libpkg/common/srchcfile.i
+usr/src/lib/libpkg/common/tputcfent.i
+usr/src/lib/libpkg/common/verify.i
+usr/src/lib/libpkg/common/vfpops.i
+usr/src/lib/libpkg/i386/lint.out
+usr/src/lib/libpkg/i386/llib-lpkg.ln
+usr/src/lib/libpool/amd64/lint.out
+usr/src/lib/libpool/amd64/llib-lpool.ln
+usr/src/lib/libpool/i386/lint.out
+usr/src/lib/libpool/i386/llib-lpool.ln
+usr/src/lib/libpp/i386/libpp.msg
+usr/src/lib/libpp/i386/lint.out
+usr/src/lib/libpp/i386/llib-lpp.ln
+usr/src/lib/libpp/i386/msgcc.out
+usr/src/lib/libpp/i386/msgs/
+usr/src/lib/libpp/tmpastinclude/
+usr/src/lib/libproc/amd64/lint.out
+usr/src/lib/libproc/amd64/llib-lproc.ln
+usr/src/lib/libproc/i386/lint.out
+usr/src/lib/libproc/i386/llib-lproc.ln
+usr/src/lib/libproject/amd64/lint.out
+usr/src/lib/libproject/amd64/llib-lproject.ln
+usr/src/lib/libproject/i386/lint.out
+usr/src/lib/libproject/i386/llib-lproject.ln
+usr/src/lib/libpthread/amd64/lint.out
+usr/src/lib/libpthread/amd64/llib-lpthread.ln
+usr/src/lib/libpthread/i386/lint.out
+usr/src/lib/libpthread/i386/llib-lpthread.ln
+usr/src/lib/libraidcfg/amd64/lint.out
+usr/src/lib/libraidcfg/amd64/llib-lraidcfg.ln
+usr/src/lib/libraidcfg/i386/lint.out
+usr/src/lib/libraidcfg/i386/llib-lraidcfg.ln
+usr/src/lib/librcm/amd64/lint.out
+usr/src/lib/librcm/amd64/llib-lrcm.ln
+usr/src/lib/librcm/i386/lint.out
+usr/src/lib/librcm/i386/llib-lrcm.ln
+usr/src/lib/librdc/i386/lint.out
+usr/src/lib/librdc/i386/llib-lrdc.ln
+usr/src/lib/libreparse/amd64/lint.out
+usr/src/lib/libreparse/amd64/llib-lreparse.ln
+usr/src/lib/libreparse/i386/lint.out
+usr/src/lib/libreparse/i386/llib-lreparse.ln
+usr/src/lib/libresolv2/amd64/libresolv.so.2
+usr/src/lib/libresolv2/amd64/lint.out
+usr/src/lib/libresolv2/amd64/llib-lresolv.ln
+usr/src/lib/libresolv2/i386/libresolv.so.2
+usr/src/lib/libresolv2/i386/lint.out
+usr/src/lib/libresolv2/i386/llib-lresolv.ln
+usr/src/lib/libresolv2/include/make_os_version
+usr/src/lib/libresolv2/include/os_version.h
+usr/src/lib/libresolv2/include/port_ipv6.h
+usr/src/lib/libresolv2/include/probe_ipv6
+usr/src/lib/libresolv2_joy/amd64/libresolv_joy.so.2
+usr/src/lib/libresolv2_joy/amd64/lint.out
+usr/src/lib/libresolv2_joy/amd64/llib-lresolv_joy.ln
+usr/src/lib/libresolv2_joy/i386/libresolv_joy.so.2
+usr/src/lib/libresolv2_joy/i386/lint.out
+usr/src/lib/libresolv2_joy/i386/llib-lresolv_joy.ln
+usr/src/lib/libresolv2_joy/include/make_os_version
+usr/src/lib/libresolv2_joy/include/os_version.h
+usr/src/lib/libresolv2_joy/include/port_ipv6.h
+usr/src/lib/libresolv2_joy/include/probe_ipv6
+usr/src/lib/librestart/amd64/lint.out
+usr/src/lib/librestart/amd64/llib-lrestart.ln
+usr/src/lib/librestart/i386/lint.out
+usr/src/lib/librestart/i386/llib-lrestart.ln
+usr/src/lib/librpcsvc/amd64/lint.out
+usr/src/lib/librpcsvc/amd64/llib-lrpcsvc.ln
+usr/src/lib/librpcsvc/common/bootparam_prot_xdr.c
+usr/src/lib/librpcsvc/common/mount_xdr.c
+usr/src/lib/librpcsvc/common/nlm_prot.c
+usr/src/lib/librpcsvc/common/nsm_addr_xdr.c
+usr/src/lib/librpcsvc/common/rpc_sztypes.c
+usr/src/lib/librpcsvc/common/rstat_xdr.c
+usr/src/lib/librpcsvc/common/rusers_xdr.c
+usr/src/lib/librpcsvc/common/sm_inter_xdr.c
+usr/src/lib/librpcsvc/common/spray_xdr.c
+usr/src/lib/librpcsvc/i386/lint.out
+usr/src/lib/librpcsvc/i386/llib-lrpcsvc.ln
+usr/src/lib/librsm/amd64/librsm.so.2
+usr/src/lib/librsm/i386/librsm.so.2
+usr/src/lib/librstp/i386/lint.out
+usr/src/lib/librstp/i386/llib-lrstp.ln
+usr/src/lib/librt/amd64/lint.out
+usr/src/lib/librt/amd64/llib-lrt.ln
+usr/src/lib/librt/i386/lint.out
+usr/src/lib/librt/i386/llib-lrt.ln
+usr/src/lib/libsasl/THIRDPARTYLICENSE
+usr/src/lib/libsasl/amd64/lint.out
+usr/src/lib/libsasl/amd64/llib-lsasl.ln
+usr/src/lib/libsasl/i386/lint.out
+usr/src/lib/libsasl/i386/llib-lsasl.ln
+usr/src/lib/libscf/amd64/lint.out
+usr/src/lib/libscf/amd64/llib-lscf.ln
+usr/src/lib/libscf/i386/lint.out
+usr/src/lib/libscf/i386/llib-lscf.ln
+usr/src/lib/libsched/amd64/lint.out
+usr/src/lib/libsched/amd64/llib-lsched.ln
+usr/src/lib/libsched/i386/lint.out
+usr/src/lib/libsched/i386/llib-lsched.ln
+usr/src/lib/libsctp/amd64/lint.out
+usr/src/lib/libsctp/amd64/llib-lsctp.ln
+usr/src/lib/libsctp/i386/lint.out
+usr/src/lib/libsctp/i386/llib-lsctp.ln
+usr/src/lib/libsec/amd64/acl.output
+usr/src/lib/libsec/amd64/acl.tab.c
+usr/src/lib/libsec/amd64/acl.tab.h
+usr/src/lib/libsec/amd64/acl_lex.c
+usr/src/lib/libsec/amd64/lint.out
+usr/src/lib/libsec/amd64/llib-lsec.ln
+usr/src/lib/libsec/i386/acl.output
+usr/src/lib/libsec/i386/acl.tab.c
+usr/src/lib/libsec/i386/acl.tab.h
+usr/src/lib/libsec/i386/acl_lex.c
+usr/src/lib/libsec/i386/lint.out
+usr/src/lib/libsec/i386/llib-lsec.ln
+usr/src/lib/libsecdb/amd64/lint.out
+usr/src/lib/libsecdb/amd64/llib-lsecdb.ln
+usr/src/lib/libsecdb/auth_attr
+usr/src/lib/libsecdb/exec_attr
+usr/src/lib/libsecdb/i386/lint.out
+usr/src/lib/libsecdb/i386/llib-lsecdb.ln
+usr/src/lib/libsecdb/prof_attr
+usr/src/lib/libsecdb/user_attr
+usr/src/lib/libsendfile/amd64/lint.out
+usr/src/lib/libsendfile/amd64/llib-lsendfile.ln
+usr/src/lib/libsendfile/i386/lint.out
+usr/src/lib/libsendfile/i386/llib-lsendfile.ln
+usr/src/lib/libshare/amd64/lint.out
+usr/src/lib/libshare/amd64/llib-lshare.ln
+usr/src/lib/libshare/i386/lint.out
+usr/src/lib/libshare/i386/llib-lshare.ln
+usr/src/lib/libshell/amd64/libshell.msg
+usr/src/lib/libshell/amd64/lint.out
+usr/src/lib/libshell/amd64/llib-lshell.ln
+usr/src/lib/libshell/amd64/msgcc.out
+usr/src/lib/libshell/amd64/msgs/
+usr/src/lib/libshell/i386/libshell.msg
+usr/src/lib/libshell/i386/lint.out
+usr/src/lib/libshell/i386/llib-lshell.ln
+usr/src/lib/libshell/i386/msgcc.out
+usr/src/lib/libshell/i386/msgs/
+usr/src/lib/libshell/tmpastinclude/
+usr/src/lib/libshell/xsltproc.log
+usr/src/lib/libsip/amd64/lint.out
+usr/src/lib/libsip/amd64/llib-lsip.ln
+usr/src/lib/libsip/i386/lint.out
+usr/src/lib/libsip/i386/llib-lsip.ln
+usr/src/lib/libsldap/amd64/lint.out
+usr/src/lib/libsldap/amd64/llib-lsldap.ln
+usr/src/lib/libsldap/i386/lint.out
+usr/src/lib/libsldap/i386/llib-lsldap.ln
+usr/src/lib/libslp/amd64/lint.out
+usr/src/lib/libslp/amd64/llib-lslp.ln
+usr/src/lib/libslp/classes/
+usr/src/lib/libslp/i386/lint.out
+usr/src/lib/libslp/i386/llib-lslp.ln
+usr/src/lib/libsmartsshd/amd64/lint.out
+usr/src/lib/libsmartsshd/amd64/llib-lsmartsshd.ln
+usr/src/lib/libsmartsshd/i386/lint.out
+usr/src/lib/libsmartsshd/i386/llib-lsmartsshd.ln
+usr/src/lib/libsmbfs/amd64/lint.out
+usr/src/lib/libsmbfs/amd64/llib-lsmbfs.ln
+usr/src/lib/libsmbfs/i386/lint.out
+usr/src/lib/libsmbfs/i386/llib-lsmbfs.ln
+usr/src/lib/libsmbios/amd64/lint.out
+usr/src/lib/libsmbios/amd64/llib-lsmbios.ln
+usr/src/lib/libsmbios/common/smb_tables.c
+usr/src/lib/libsmbios/i386/lint.out
+usr/src/lib/libsmbios/i386/llib-lsmbios.ln
+usr/src/lib/libsmedia/library/amd64/lint.out
+usr/src/lib/libsmedia/library/amd64/llib-lsmedia.ln
+usr/src/lib/libsmedia/library/common/smed.h
+usr/src/lib/libsmedia/library/common/smed_clnt.c
+usr/src/lib/libsmedia/library/common/smed_xdr.c
+usr/src/lib/libsmedia/library/i386/lint.out
+usr/src/lib/libsmedia/library/i386/llib-lsmedia.ln
+usr/src/lib/libsocket/amd64/lint.out
+usr/src/lib/libsocket/amd64/llib-lsocket.ln
+usr/src/lib/libsocket/i386/lint.out
+usr/src/lib/libsocket/i386/llib-lsocket.ln
+usr/src/lib/libsqlite/i386/lemon
+usr/src/lib/libsqlite/i386/lemon-build
+usr/src/lib/libsqlite/i386/lempar.c
+usr/src/lib/libsqlite/i386/lint.out
+usr/src/lib/libsqlite/i386/llib-lsqlite.ln
+usr/src/lib/libsqlite/i386/opcodes.c
+usr/src/lib/libsqlite/i386/opcodes.h
+usr/src/lib/libsqlite/i386/parse.c
+usr/src/lib/libsqlite/i386/parse.h
+usr/src/lib/libsqlite/i386/parse_tmp.c
+usr/src/lib/libsqlite/i386/parse_tmp.h
+usr/src/lib/libsqlite/i386/parse_tmp.out
+usr/src/lib/libsqlite/i386/parse_tmp.y
+usr/src/lib/libsqlite/sqlite.h
+usr/src/lib/libsrpt/amd64/lint.out
+usr/src/lib/libsrpt/amd64/llib-lsrpt.ln
+usr/src/lib/libsrpt/i386/lint.out
+usr/src/lib/libsrpt/i386/llib-lsrpt.ln
+usr/src/lib/libstmf/amd64/lint.out
+usr/src/lib/libstmf/amd64/llib-lstmf.ln
+usr/src/lib/libstmf/i386/lint.out
+usr/src/lib/libstmf/i386/llib-lstmf.ln
+usr/src/lib/libstmfproxy/amd64/lint.out
+usr/src/lib/libstmfproxy/amd64/llib-lstmfproxy.ln
+usr/src/lib/libstmfproxy/i386/lint.out
+usr/src/lib/libstmfproxy/i386/llib-lstmfproxy.ln
+usr/src/lib/libsum/amd64/libsum.msg
+usr/src/lib/libsum/amd64/lint.out
+usr/src/lib/libsum/amd64/llib-lsum.ln
+usr/src/lib/libsum/amd64/msgcc.out
+usr/src/lib/libsum/amd64/msgs/
+usr/src/lib/libsum/i386/libsum.msg
+usr/src/lib/libsum/i386/lint.out
+usr/src/lib/libsum/i386/llib-lsum.ln
+usr/src/lib/libsum/i386/msgcc.out
+usr/src/lib/libsum/i386/msgs/
+usr/src/lib/libsum/tmpastinclude/
+usr/src/lib/libsun_ima/amd64/lint.out
+usr/src/lib/libsun_ima/amd64/llib-lsun_ima.ln
+usr/src/lib/libsun_ima/i386/lint.out
+usr/src/lib/libsun_ima/i386/llib-lsun_ima.ln
+usr/src/lib/libsys/i386/libsys.c
+usr/src/lib/libsysevent/amd64/lint.out
+usr/src/lib/libsysevent/amd64/llib-lsysevent.ln
+usr/src/lib/libsysevent/i386/lint.out
+usr/src/lib/libsysevent/i386/llib-lsysevent.ln
+usr/src/lib/libtecla/amd64/lint.out
+usr/src/lib/libtecla/amd64/llib-ltecla.ln
+usr/src/lib/libtecla/i386/lint.out
+usr/src/lib/libtecla/i386/llib-ltecla.ln
+usr/src/lib/libthread/amd64/lint.out
+usr/src/lib/libthread/amd64/llib-lthread.ln
+usr/src/lib/libthread/i386/lint.out
+usr/src/lib/libthread/i386/llib-lthread.ln
+usr/src/lib/libtsnet/amd64/lint.out
+usr/src/lib/libtsnet/amd64/llib-ltsnet.ln
+usr/src/lib/libtsnet/i386/lint.out
+usr/src/lib/libtsnet/i386/llib-ltsnet.ln
+usr/src/lib/libtsol/amd64/libtsol.so.2
+usr/src/lib/libtsol/amd64/lint.out
+usr/src/lib/libtsol/amd64/llib-ltsol.ln
+usr/src/lib/libtsol/i386/libtsol.so.2
+usr/src/lib/libtsol/i386/lint.out
+usr/src/lib/libtsol/i386/llib-ltsol.ln
+usr/src/lib/libumem/amd64/lint.out
+usr/src/lib/libumem/amd64/llib-lumem.ln
+usr/src/lib/libumem/i386/lint.out
+usr/src/lib/libumem/i386/llib-lumem.ln
+usr/src/lib/libunistat/common/dsw/dsw.dtrnk
+usr/src/lib/libunistat/common/dsw/dsw.edef
+usr/src/lib/libunistat/common/dsw/dsw.msg
+usr/src/lib/libunistat/common/dsw/dsw.trnk
+usr/src/lib/libunistat/common/rdc/rdc.dtrnk
+usr/src/lib/libunistat/common/rdc/rdc.edef
+usr/src/lib/libunistat/common/rdc/rdc.msg
+usr/src/lib/libunistat/common/rdc/rdc.trnk
+usr/src/lib/libunistat/common/sdbc/sdbc.dtrnk
+usr/src/lib/libunistat/common/sdbc/sdbc.edef
+usr/src/lib/libunistat/common/sdbc/sdbc.msg
+usr/src/lib/libunistat/common/sdbc/sdbc.trnk
+usr/src/lib/libunistat/common/solaris/solaris.dtrnk
+usr/src/lib/libunistat/common/solaris/solaris.edef
+usr/src/lib/libunistat/common/solaris/solaris.msg
+usr/src/lib/libunistat/common/solaris/solaris.trnk
+usr/src/lib/libunistat/common/spcs/spcs.dtrnk
+usr/src/lib/libunistat/common/spcs/spcs.edef
+usr/src/lib/libunistat/common/spcs/spcs.msg
+usr/src/lib/libunistat/common/spcs/spcs.trnk
+usr/src/lib/libunistat/common/sv/sv.dtrnk
+usr/src/lib/libunistat/common/sv/sv.edef
+usr/src/lib/libunistat/common/sv/sv.msg
+usr/src/lib/libunistat/common/sv/sv.trnk
+usr/src/lib/libunistat/i386/lint.out
+usr/src/lib/libunistat/i386/llib-lunistat.ln
+usr/src/lib/libunistat/i386/spcs_dtrinkets.h
+usr/src/lib/libunistat/i386/spcs_errors.h
+usr/src/lib/libunistat/i386/spcs_etext.h
+usr/src/lib/libunistat/i386/spcs_etrinkets.h
+usr/src/lib/libuuid/amd64/lint.out
+usr/src/lib/libuuid/amd64/llib-luuid.ln
+usr/src/lib/libuuid/i386/lint.out
+usr/src/lib/libuuid/i386/llib-luuid.ln
+usr/src/lib/libuutil/amd64/lint.out
+usr/src/lib/libuutil/amd64/llib-luutil.ln
+usr/src/lib/libuutil/i386/lint.out
+usr/src/lib/libuutil/i386/llib-luutil.ln
+usr/src/lib/libuutil/native/sys/
+usr/src/lib/libvolmgt/amd64/lint.out
+usr/src/lib/libvolmgt/amd64/llib-lvolmgt.ln
+usr/src/lib/libvolmgt/i386/lint.out
+usr/src/lib/libvolmgt/i386/llib-lvolmgt.ln
+usr/src/lib/libvrrpadm/amd64/lint.out
+usr/src/lib/libvrrpadm/amd64/llib-lvrrpadm.ln
+usr/src/lib/libvrrpadm/i386/lint.out
+usr/src/lib/libvrrpadm/i386/llib-lvrrpadm.ln
+usr/src/lib/libvscan/i386/lint.out
+usr/src/lib/libvscan/i386/llib-lvscan.ln
+usr/src/lib/libwanboot/i386/lint.out
+usr/src/lib/libwanboot/i386/llib-lwanboot.ln
+usr/src/lib/libwanbootutil/common/key_xdr.c
+usr/src/lib/libwanbootutil/common/key_xdr.h
+usr/src/lib/libwanbootutil/i386/lint.out
+usr/src/lib/libwanbootutil/i386/llib-lwanbootutil.ln
+usr/src/lib/libwrap/THIRDPARTYLICENSE
+usr/src/lib/libwrap/i386/libwrap.so.1.0
+usr/src/lib/libwrap/i386/lint.out
+usr/src/lib/libwrap/i386/llib-lwrap.ln
+usr/src/lib/libxcurses/h/term.h
+usr/src/lib/libxcurses/src/libc/xcurses/boolcode.c
+usr/src/lib/libxcurses/src/libc/xcurses/boolfnam.c
+usr/src/lib/libxcurses/src/libc/xcurses/boolname.c
+usr/src/lib/libxcurses/src/libc/xcurses/keyindex.c
+usr/src/lib/libxcurses/src/libc/xcurses/numcode.c
+usr/src/lib/libxcurses/src/libc/xcurses/numfnam.c
+usr/src/lib/libxcurses/src/libc/xcurses/numname.c
+usr/src/lib/libxcurses/src/libc/xcurses/strcode.c
+usr/src/lib/libxcurses/src/libc/xcurses/strfnam.c
+usr/src/lib/libxcurses/src/libc/xcurses/strname.c
+usr/src/lib/libxcurses2/amd64/libcurses.so.2
+usr/src/lib/libxcurses2/amd64/lint.out
+usr/src/lib/libxcurses2/amd64/llib-lcurses.ln
+usr/src/lib/libxcurses2/h/term.h
+usr/src/lib/libxcurses2/i386/libcurses.so.2
+usr/src/lib/libxcurses2/i386/lint.out
+usr/src/lib/libxcurses2/i386/llib-lcurses.ln
+usr/src/lib/libxcurses2/src/libc/xcurses/boolcode.c
+usr/src/lib/libxcurses2/src/libc/xcurses/boolfnam.c
+usr/src/lib/libxcurses2/src/libc/xcurses/boolname.c
+usr/src/lib/libxcurses2/src/libc/xcurses/keyindex.c
+usr/src/lib/libxcurses2/src/libc/xcurses/numcode.c
+usr/src/lib/libxcurses2/src/libc/xcurses/numfnam.c
+usr/src/lib/libxcurses2/src/libc/xcurses/numname.c
+usr/src/lib/libxcurses2/src/libc/xcurses/strcode.c
+usr/src/lib/libxcurses2/src/libc/xcurses/strfnam.c
+usr/src/lib/libxcurses2/src/libc/xcurses/strname.c
+usr/src/lib/libxnet/amd64/lint.out
+usr/src/lib/libxnet/amd64/llib-lxnet.ln
+usr/src/lib/libxnet/i386/lint.out
+usr/src/lib/libxnet/i386/llib-lxnet.ln
+usr/src/lib/libzdoor/amd64/lint.out
+usr/src/lib/libzdoor/amd64/llib-lzdoor.ln
+usr/src/lib/libzdoor/i386/lint.out
+usr/src/lib/libzdoor/i386/llib-lzdoor.ln
+usr/src/lib/libzfs/amd64/lint.out
+usr/src/lib/libzfs/amd64/llib-lzfs.ln
+usr/src/lib/libzfs/i386/lint.out
+usr/src/lib/libzfs/i386/llib-lzfs.ln
+usr/src/lib/libzfs_jni/amd64/lint.out
+usr/src/lib/libzfs_jni/amd64/llib-lzfs_jni.ln
+usr/src/lib/libzfs_jni/i386/lint.out
+usr/src/lib/libzfs_jni/i386/llib-lzfs_jni.ln
+usr/src/lib/libzonecfg/amd64/lint.out
+usr/src/lib/libzonecfg/amd64/llib-lzonecfg.ln
+usr/src/lib/libzonecfg/i386/lint.out
+usr/src/lib/libzonecfg/i386/llib-lzonecfg.ln
+usr/src/lib/libzoneinfo/i386/lint.out
+usr/src/lib/libzoneinfo/i386/llib-lzoneinfo.ln
+usr/src/lib/libzonestat/amd64/lint.out
+usr/src/lib/libzonestat/amd64/llib-lzonestat.ln
+usr/src/lib/libzonestat/i386/lint.out
+usr/src/lib/libzonestat/i386/llib-lzonestat.ln
+usr/src/lib/libzpool/amd64/lint.out
+usr/src/lib/libzpool/amd64/llib-lzpool.ln
+usr/src/lib/libzpool/i386/lint.out
+usr/src/lib/libzpool/i386/llib-lzpool.ln
+usr/src/lib/mpapi/libmpapi/amd64/lint.out
+usr/src/lib/mpapi/libmpapi/amd64/llib-lMPAPI.ln
+usr/src/lib/mpapi/libmpapi/i386/lint.out
+usr/src/lib/mpapi/libmpapi/i386/llib-lMPAPI.ln
+usr/src/lib/nametoaddr/straddr/amd64/straddr.so.2
+usr/src/lib/nametoaddr/straddr/i386/straddr.so.2
+usr/src/lib/passwdutil/amd64/lint.out
+usr/src/lib/passwdutil/amd64/llib-lpasswdutil.ln
+usr/src/lib/passwdutil/i386/lint.out
+usr/src/lib/passwdutil/i386/llib-lpasswdutil.ln
+usr/src/lib/pkcs11/libkcfd/i386/lint.out
+usr/src/lib/pkcs11/libkcfd/i386/llib-lkcfd.ln
+usr/src/lib/pkcs11/libpkcs11/amd64/lint.out
+usr/src/lib/pkcs11/libpkcs11/amd64/llib-lpkcs11.ln
+usr/src/lib/pkcs11/libpkcs11/i386/lint.out
+usr/src/lib/pkcs11/libpkcs11/i386/llib-lpkcs11.ln
+usr/src/lib/pkcs11/libsoftcrypto/amd64/arcfour-x86_64.s
+usr/src/lib/pkcs11/libsoftcrypto/amd64/lint.out
+usr/src/lib/pkcs11/libsoftcrypto/amd64/llib-lsoftcrypto.ln
+usr/src/lib/pkcs11/libsoftcrypto/i386/lint.out
+usr/src/lib/pkcs11/libsoftcrypto/i386/llib-lsoftcrypto.ln
+usr/src/lib/policykit/libpolkit/i386/libpolkit.so.0.0.0
+usr/src/lib/policykit/libpolkit/i386/lint.out
+usr/src/lib/policykit/libpolkit/i386/llib-lpolkit.ln
+usr/src/lib/policykit/libpolkit/i386/polkit.pc
+usr/src/lib/sasl_plugins/gssapi/THIRDPARTYLICENSE
+usr/src/lib/scsi/libscsi/amd64/lint.out
+usr/src/lib/scsi/libscsi/amd64/llib-lscsi.ln
+usr/src/lib/scsi/libscsi/common/scsi_errno.c
+usr/src/lib/scsi/libscsi/i386/lint.out
+usr/src/lib/scsi/libscsi/i386/llib-lscsi.ln
+usr/src/lib/scsi/libses/amd64/lint.out
+usr/src/lib/scsi/libses/amd64/llib-lses.ln
+usr/src/lib/scsi/libses/common/ses_errno.c
+usr/src/lib/scsi/libses/i386/lint.out
+usr/src/lib/scsi/libses/i386/llib-lses.ln
+usr/src/lib/scsi/libsmp/amd64/lint.out
+usr/src/lib/scsi/libsmp/amd64/llib-lsmp.ln
+usr/src/lib/scsi/libsmp/common/smp_errno.c
+usr/src/lib/scsi/libsmp/i386/lint.out
+usr/src/lib/scsi/libsmp/i386/llib-lsmp.ln
+usr/src/lib/scsi/plugins/ses/libses/common/libses_elemtype.c
+usr/src/lib/smbsrv/libmlrpc/amd64/lint.out
+usr/src/lib/smbsrv/libmlrpc/amd64/llib-lmlrpc.ln
+usr/src/lib/smbsrv/libmlrpc/amd64/rpcpdu_ndr.c
+usr/src/lib/smbsrv/libmlrpc/i386/lint.out
+usr/src/lib/smbsrv/libmlrpc/i386/llib-lmlrpc.ln
+usr/src/lib/smbsrv/libmlrpc/i386/rpcpdu_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/dssetup_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/eventlog_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/lint.out
+usr/src/lib/smbsrv/libmlsvc/amd64/llib-lmlsvc.ln
+usr/src/lib/smbsrv/libmlsvc/amd64/lsarpc_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/msgsvc_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/netdfs_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/netlogon_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/samrpc_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/spoolss_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/srvsvc_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/svcctl_ndr.c
+usr/src/lib/smbsrv/libmlsvc/amd64/winreg_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/dssetup_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/eventlog_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/lint.out
+usr/src/lib/smbsrv/libmlsvc/i386/llib-lmlsvc.ln
+usr/src/lib/smbsrv/libmlsvc/i386/lsarpc_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/msgsvc_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/netdfs_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/netlogon_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/samrpc_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/spoolss_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/srvsvc_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/svcctl_ndr.c
+usr/src/lib/smbsrv/libmlsvc/i386/winreg_ndr.c
+usr/src/lib/smbsrv/libsmb/amd64/lint.out
+usr/src/lib/smbsrv/libsmb/amd64/llib-lsmb.ln
+usr/src/lib/smbsrv/libsmb/common/smb_status_tbl.h
+usr/src/lib/smbsrv/libsmb/i386/lint.out
+usr/src/lib/smbsrv/libsmb/i386/llib-lsmb.ln
+usr/src/lib/smbsrv/libsmbns/amd64/lint.out
+usr/src/lib/smbsrv/libsmbns/amd64/llib-lsmbns.ln
+usr/src/lib/smbsrv/libsmbns/i386/lint.out
+usr/src/lib/smbsrv/libsmbns/i386/llib-lsmbns.ln
+usr/src/lib/smbsrv/libsmbrdr/amd64/lint.out
+usr/src/lib/smbsrv/libsmbrdr/amd64/llib-lsmbrdr.ln
+usr/src/lib/smbsrv/libsmbrdr/i386/lint.out
+usr/src/lib/smbsrv/libsmbrdr/i386/llib-lsmbrdr.ln
+usr/src/lib/smbsrv/libsmbrp/amd64/lint.out
+usr/src/lib/smbsrv/libsmbrp/amd64/llib-lreparse_smb.ln
+usr/src/lib/smbsrv/libsmbrp/i386/lint.out
+usr/src/lib/smbsrv/libsmbrp/i386/llib-lreparse_smb.ln
+usr/src/lib/smhba/amd64/lint.out
+usr/src/lib/smhba/amd64/llib-lSMHBAAPI.ln
+usr/src/lib/smhba/i386/lint.out
+usr/src/lib/smhba/i386/llib-lSMHBAAPI.ln
+usr/src/lib/udapl/libdat/amd64/lint.out
+usr/src/lib/udapl/libdat/amd64/llib-ldat.ln
+usr/src/lib/udapl/libdat/i386/lint.out
+usr/src/lib/udapl/libdat/i386/llib-ldat.ln
+usr/src/man/man1/batch.1
+usr/src/man/man1/bg.1
+usr/src/man/man1/case.1
+usr/src/man/man1/chdir.1
+usr/src/man/man1/checkeq.1
+usr/src/man/man1/continue.1
+usr/src/man/man1/decrypt.1
+usr/src/man/man1/dirname.1
+usr/src/man/man1/dirs.1
+usr/src/man/man1/disable.1
+usr/src/man/man1/dumpkeys.1
+usr/src/man/man1/edit.1
+usr/src/man/man1/errange.1
+usr/src/man/man1/errdate.1
+usr/src/man/man1/errgid.1
+usr/src/man/man1/errint.1
+usr/src/man/man1/erritem.1
+usr/src/man/man1/errpath.1
+usr/src/man/man1/errstr.1
+usr/src/man/man1/errtime.1
+usr/src/man/man1/erruid.1
+usr/src/man/man1/erryorn.1
+usr/src/man/man1/eval.1
+usr/src/man/man1/export.1
+usr/src/man/man1/false.1
+usr/src/man/man1/fc.1
+usr/src/man/man1/fg.1
+usr/src/man/man1/for.1
+usr/src/man/man1/foreach.1
+usr/src/man/man1/function.1
+usr/src/man/man1/goto.1
+usr/src/man/man1/hashcheck.1
+usr/src/man/man1/hashmake.1
+usr/src/man/man1/hashstat.1
+usr/src/man/man1/helpdate.1
+usr/src/man/man1/helpgid.1
+usr/src/man/man1/helpint.1
+usr/src/man/man1/helpitem.1
+usr/src/man/man1/helppath.1
+usr/src/man/man1/helprange.1
+usr/src/man/man1/helpstr.1
+usr/src/man/man1/helptime.1
+usr/src/man/man1/helpuid.1
+usr/src/man/man1/helpyorn.1
+usr/src/man/man1/hist.1
+usr/src/man/man1/i286.1
+usr/src/man/man1/i386.1
+usr/src/man/man1/i486.1
+usr/src/man/man1/i860.1
+usr/src/man/man1/iAPX286.1
+usr/src/man/man1/if.1
+usr/src/man/man1/intro.1
+usr/src/man/man1/jsh.1
+usr/src/man/man1/ksh.1
+usr/src/man/man1/ldapadd.1
+usr/src/man/man1/neqn.1
+usr/src/man/man1/notify.1
+usr/src/man/man1/onintr.1
+usr/src/man/man1/page.1
+usr/src/man/man1/pcat.1
+usr/src/man/man1/pcred.1
+usr/src/man/man1/pdp11.1
+usr/src/man/man1/pfcsh.1
+usr/src/man/man1/pfiles.1
+usr/src/man/man1/pfksh.1
+usr/src/man/man1/pflags.1
+usr/src/man/man1/pfsh.1
+usr/src/man/man1/pkill.1
+usr/src/man/man1/pldd.1
+usr/src/man/man1/popd.1
+usr/src/man/man1/prun.1
+usr/src/man/man1/psig.1
+usr/src/man/man1/pstack.1
+usr/src/man/man1/pstop.1
+usr/src/man/man1/ptime.1
+usr/src/man/man1/pushd.1
+usr/src/man/man1/pwait.1
+usr/src/man/man1/pwdx.1
+usr/src/man/man1/red.1
+usr/src/man/man1/rehash.1
+usr/src/man/man1/remote_shell.1
+usr/src/man/man1/remsh.1
+usr/src/man/man1/repeat.1
+usr/src/man/man1/return.1
+usr/src/man/man1/rksh.1
+usr/src/man/man1/rksh93.1
+usr/src/man/man1/rmail.1
+usr/src/man/man1/rmdir.1
+usr/src/man/man1/rmumount.1
+usr/src/man/man1/select.1
+usr/src/man/man1/setenv.1
+usr/src/man/man1/settime.1
+usr/src/man/man1/sh.1
+usr/src/man/man1/snca.1
+usr/src/man/man1/source.1
+usr/src/man/man1/sparc.1
+usr/src/man/man1/spellin.1
+usr/src/man/man1/stop.1
+usr/src/man/man1/strconf.1
+usr/src/man/man1/sun.1
+usr/src/man/man1/switch.1
+usr/src/man/man1/t300.1
+usr/src/man/man1/t300s.1
+usr/src/man/man1/t4014.1
+usr/src/man/man1/t450.1
+usr/src/man/man1/tek.1
+usr/src/man/man1/u370.1
+usr/src/man/man1/u3b.1
+usr/src/man/man1/u3b15.1
+usr/src/man/man1/u3b2.1
+usr/src/man/man1/u3b5.1
+usr/src/man/man1/ulimit.1
+usr/src/man/man1/unalias.1
+usr/src/man/man1/uncompress.1
+usr/src/man/man1/unexpand.1
+usr/src/man/man1/unhash.1
+usr/src/man/man1/unlimit.1
+usr/src/man/man1/unpack.1
+usr/src/man/man1/unset.1
+usr/src/man/man1/unsetenv.1
+usr/src/man/man1/until.1
+usr/src/man/man1/valdate.1
+usr/src/man/man1/valgid.1
+usr/src/man/man1/valint.1
+usr/src/man/man1/valpath.1
+usr/src/man/man1/valrange.1
+usr/src/man/man1/valstr.1
+usr/src/man/man1/valtime.1
+usr/src/man/man1/valuid.1
+usr/src/man/man1/valyorn.1
+usr/src/man/man1/vax.1
+usr/src/man/man1/vedit.1
+usr/src/man/man1/ver.1
+usr/src/man/man1/whence.1
+usr/src/man/man1/while.1
+usr/src/man/man1/zcat.1
+usr/src/man/man1b/Mail.1b
+usr/src/man/man1b/fasthalt.1b
+usr/src/man/man1b/reset.1b
+usr/src/man/man1c/uudecode.1c
+usr/src/man/man1c/uulog.1c
+usr/src/man/man1c/uuname.1c
+usr/src/man/man1c/uupick.1c
+usr/src/man/man1m/acctcon1.1m
+usr/src/man/man1m/acctcon2.1m
+usr/src/man/man1m/acctdisk.1m
+usr/src/man/man1m/acctdusg.1m
+usr/src/man/man1m/accton.1m
+usr/src/man/man1m/acctprc1.1m
+usr/src/man/man1m/acctprc2.1m
+usr/src/man/man1m/acctwtmp.1m
+usr/src/man/man1m/bootparamd.1m
+usr/src/man/man1m/chargefee.1m
+usr/src/man/man1m/ckpacct.1m
+usr/src/man/man1m/closewtmp.1m
+usr/src/man/man1m/comsat.1m
+usr/src/man/man1m/dcopy.1m
+usr/src/man/man1m/devfsadmd.1m
+usr/src/man/man1m/dodisk.1m
+usr/src/man/man1m/fcadm.1m
+usr/src/man/man1m/fingerd.1m
+usr/src/man/man1m/ftpd.1m
+usr/src/man/man1m/grpck.1m
+usr/src/man/man1m/hal-find-by-capability.1m
+usr/src/man/man1m/hal-find-by-property.1m
+usr/src/man/man1m/hal-set-property.1m
+usr/src/man/man1m/intro.1m
+usr/src/man/man1m/kadmin.local.1m
+usr/src/man/man1m/lastlogin.1m
+usr/src/man/man1m/metadetach.1m
+usr/src/man/man1m/metaonline.1m
+usr/src/man/man1m/monacct.1m
+usr/src/man/man1m/nulladm.1m
+usr/src/man/man1m/poweroff.1m
+usr/src/man/man1m/prctmp.1m
+usr/src/man/man1m/prdaily.1m
+usr/src/man/man1m/prtacct.1m
+usr/src/man/man1m/quotaoff.1m
+usr/src/man/man1m/rarpd.1m
+usr/src/man/man1m/rdisc.1m
+usr/src/man/man1m/reject.1m
+usr/src/man/man1m/restricted_shell.1m
+usr/src/man/man1m/rexd.1m
+usr/src/man/man1m/rexecd.1m
+usr/src/man/man1m/rlogind.1m
+usr/src/man/man1m/routed.1m
+usr/src/man/man1m/rshd.1m
+usr/src/man/man1m/rstatd.1m
+usr/src/man/man1m/rusersd.1m
+usr/src/man/man1m/rwalld.1m
+usr/src/man/man1m/rwhod.1m
+usr/src/man/man1m/sa1.1m
+usr/src/man/man1m/sa2.1m
+usr/src/man/man1m/sadc.1m
+usr/src/man/man1m/shutacct.1m
+usr/src/man/man1m/sprayd.1m
+usr/src/man/man1m/startup.1m
+usr/src/man/man1m/talkd.1m
+usr/src/man/man1m/telinit.1m
+usr/src/man/man1m/telnetd.1m
+usr/src/man/man1m/tftpd.1m
+usr/src/man/man1m/turnacct.1m
+usr/src/man/man1m/umount.1m
+usr/src/man/man1m/umount_smbfs.1m
+usr/src/man/man1m/umountall.1m
+usr/src/man/man1m/unlink.1m
+usr/src/man/man1m/unshareall.1m
+usr/src/man/man1m/utmp2wtmp.1m
+usr/src/man/man1m/uucpd.1m
+usr/src/man/man1m/uutry.1m
+usr/src/man/man1m/wtmpfix.1m
+usr/src/man/man1m/yppasswdd.1m
+usr/src/man/man1m/ypstop.1m
+usr/src/man/man1m/ypupdated.1m
+usr/src/man/man1m/ypxfr_1perday.1m
+usr/src/man/man1m/ypxfr_1perhour.1m
+usr/src/man/man1m/ypxfr_2perday.1m
+usr/src/man/man1m/ypxfrd.1m
+usr/src/man/man2/_Exit.2
+usr/src/man/man2/_exit.2
+usr/src/man/man2/_lwp_cond_broadcast.2
+usr/src/man/man2/_lwp_cond_reltimedwait.2
+usr/src/man/man2/_lwp_cond_timedwait.2
+usr/src/man/man2/_lwp_continue.2
+usr/src/man/man2/_lwp_mutex_trylock.2
+usr/src/man/man2/_lwp_mutex_unlock.2
+usr/src/man/man2/_lwp_sema_init.2
+usr/src/man/man2/_lwp_sema_post.2
+usr/src/man/man2/_lwp_sema_trywait.2
+usr/src/man/man2/execl.2
+usr/src/man/man2/execle.2
+usr/src/man/man2/execlp.2
+usr/src/man/man2/execv.2
+usr/src/man/man2/execve.2
+usr/src/man/man2/execvp.2
+usr/src/man/man2/faccessat.2
+usr/src/man/man2/facl.2
+usr/src/man/man2/fchdir.2
+usr/src/man/man2/fchmod.2
+usr/src/man/man2/fchown.2
+usr/src/man/man2/fchownat.2
+usr/src/man/man2/fchroot.2
+usr/src/man/man2/fgetlabel.2
+usr/src/man/man2/fork1.2
+usr/src/man/man2/forkall.2
+usr/src/man/man2/forkallx.2
+usr/src/man/man2/forkx.2
+usr/src/man/man2/fstat.2
+usr/src/man/man2/fstatat.2
+usr/src/man/man2/fstatvfs.2
+usr/src/man/man2/futimesat.2
+usr/src/man/man2/getaudit_addr.2
+usr/src/man/man2/getegid.2
+usr/src/man/man2/geteuid.2
+usr/src/man/man2/getgid.2
+usr/src/man/man2/getpgid.2
+usr/src/man/man2/getpgrp.2
+usr/src/man/man2/getpmsg.2
+usr/src/man/man2/getppid.2
+usr/src/man/man2/getprojid.2
+usr/src/man/man2/getrctl.2
+usr/src/man/man2/gettaskid.2
+usr/src/man/man2/intro.2
+usr/src/man/man2/lchown.2
+usr/src/man/man2/lstat.2
+usr/src/man/man2/openat.2
+usr/src/man/man2/pathconf.2
+usr/src/man/man2/pread.2
+usr/src/man/man2/pset_assign.2
+usr/src/man/man2/pset_destroy.2
+usr/src/man/man2/pset_getattr.2
+usr/src/man/man2/putacct.2
+usr/src/man/man2/putpmsg.2
+usr/src/man/man2/pwrite.2
+usr/src/man/man2/readv.2
+usr/src/man/man2/renameat.2
+usr/src/man/man2/sbrk.2
+usr/src/man/man2/semtimedop.2
+usr/src/man/man2/setaudit.2
+usr/src/man/man2/setaudit_addr.2
+usr/src/man/man2/setauid.2
+usr/src/man/man2/setcontext.2
+usr/src/man/man2/setegid.2
+usr/src/man/man2/seteuid.2
+usr/src/man/man2/setgid.2
+usr/src/man/man2/setgroups.2
+usr/src/man/man2/setitimer.2
+usr/src/man/man2/setpflags.2
+usr/src/man/man2/setppriv.2
+usr/src/man/man2/setrlimit.2
+usr/src/man/man2/setustack.2
+usr/src/man/man2/shmat.2
+usr/src/man/man2/shmdt.2
+usr/src/man/man2/sigsendset.2
+usr/src/man/man2/umount2.2
+usr/src/man/man2/unlinkat.2
+usr/src/man/man2/vforkx.2
+usr/src/man/man2/wracct.2
+usr/src/man/man2/writev.2
+usr/src/man/man3/intro.3
+usr/src/man/man3bsm/au_close.3bsm
+usr/src/man/man3bsm/au_to_arg.3bsm
+usr/src/man/man3bsm/au_to_arg32.3bsm
+usr/src/man/man3bsm/au_to_arg64.3bsm
+usr/src/man/man3bsm/au_to_attr.3bsm
+usr/src/man/man3bsm/au_to_cmd.3bsm
+usr/src/man/man3bsm/au_to_data.3bsm
+usr/src/man/man3bsm/au_to_groups.3bsm
+usr/src/man/man3bsm/au_to_in_addr.3bsm
+usr/src/man/man3bsm/au_to_ipc.3bsm
+usr/src/man/man3bsm/au_to_iport.3bsm
+usr/src/man/man3bsm/au_to_me.3bsm
+usr/src/man/man3bsm/au_to_newgroups.3bsm
+usr/src/man/man3bsm/au_to_opaque.3bsm
+usr/src/man/man3bsm/au_to_path.3bsm
+usr/src/man/man3bsm/au_to_process.3bsm
+usr/src/man/man3bsm/au_to_process_ex.3bsm
+usr/src/man/man3bsm/au_to_return.3bsm
+usr/src/man/man3bsm/au_to_return32.3bsm
+usr/src/man/man3bsm/au_to_return64.3bsm
+usr/src/man/man3bsm/au_to_socket.3bsm
+usr/src/man/man3bsm/au_to_subject.3bsm
+usr/src/man/man3bsm/au_to_subject_ex.3bsm
+usr/src/man/man3bsm/au_to_text.3bsm
+usr/src/man/man3bsm/au_write.3bsm
+usr/src/man/man3bsm/endac.3bsm
+usr/src/man/man3bsm/endauclass.3bsm
+usr/src/man/man3bsm/endauevent.3bsm
+usr/src/man/man3bsm/endauuser.3bsm
+usr/src/man/man3bsm/endddent.3bsm
+usr/src/man/man3bsm/getacdir.3bsm
+usr/src/man/man3bsm/getacflg.3bsm
+usr/src/man/man3bsm/getacmin.3bsm
+usr/src/man/man3bsm/getacna.3bsm
+usr/src/man/man3bsm/getauclassent_r.3bsm
+usr/src/man/man3bsm/getauclassnam.3bsm
+usr/src/man/man3bsm/getauclassnam_r.3bsm
+usr/src/man/man3bsm/getauditflagsbin.3bsm
+usr/src/man/man3bsm/getauditflagschar.3bsm
+usr/src/man/man3bsm/getauevent_r.3bsm
+usr/src/man/man3bsm/getauevnam.3bsm
+usr/src/man/man3bsm/getauevnam_r.3bsm
+usr/src/man/man3bsm/getauevnonam.3bsm
+usr/src/man/man3bsm/getauevnum.3bsm
+usr/src/man/man3bsm/getauevnum_r.3bsm
+usr/src/man/man3bsm/getauuserent.3bsm
+usr/src/man/man3bsm/getauuserent_r.3bsm
+usr/src/man/man3bsm/getauusernam_r.3bsm
+usr/src/man/man3bsm/getddnam.3bsm
+usr/src/man/man3bsm/setac.3bsm
+usr/src/man/man3bsm/setauclass.3bsm
+usr/src/man/man3bsm/setauevent.3bsm
+usr/src/man/man3bsm/setauuser.3bsm
+usr/src/man/man3bsm/setddent.3bsm
+usr/src/man/man3bsm/setddfile.3bsm
+usr/src/man/man3c/FD_CLR.3c
+usr/src/man/man3c/FD_ISSET.3c
+usr/src/man/man3c/FD_SET.3c
+usr/src/man/man3c/FD_ZERO.3c
+usr/src/man/man3c/__flbf.3c
+usr/src/man/man3c/__fpending.3c
+usr/src/man/man3c/__fpurge.3c
+usr/src/man/man3c/__freadable.3c
+usr/src/man/man3c/__freading.3c
+usr/src/man/man3c/__fsetlocking.3c
+usr/src/man/man3c/__fwritable.3c
+usr/src/man/man3c/__fwriting.3c
+usr/src/man/man3c/_edata.3c
+usr/src/man/man3c/_end.3c
+usr/src/man/man3c/_etext.3c
+usr/src/man/man3c/_exithandle.3c
+usr/src/man/man3c/_flushlbf.3c
+usr/src/man/man3c/_setjmp.3c
+usr/src/man/man3c/addrtosymstr.3c
+usr/src/man/man3c/aiowrite.3c
+usr/src/man/man3c/alloca.3c
+usr/src/man/man3c/alphasort.3c
+usr/src/man/man3c/ascftime.3c
+usr/src/man/man3c/asctime.3c
+usr/src/man/man3c/asctime_r.3c
+usr/src/man/man3c/asprintf.3c
+usr/src/man/man3c/atof.3c
+usr/src/man/man3c/atoi.3c
+usr/src/man/man3c/atol.3c
+usr/src/man/man3c/atoll.3c
+usr/src/man/man3c/atomic_add_16.3c
+usr/src/man/man3c/atomic_add_16_nv.3c
+usr/src/man/man3c/atomic_add_32.3c
+usr/src/man/man3c/atomic_add_32_nv.3c
+usr/src/man/man3c/atomic_add_64.3c
+usr/src/man/man3c/atomic_add_64_nv.3c
+usr/src/man/man3c/atomic_add_8.3c
+usr/src/man/man3c/atomic_add_8_nv.3c
+usr/src/man/man3c/atomic_add_char.3c
+usr/src/man/man3c/atomic_add_char_nv.3c
+usr/src/man/man3c/atomic_add_int.3c
+usr/src/man/man3c/atomic_add_int_nv.3c
+usr/src/man/man3c/atomic_add_long.3c
+usr/src/man/man3c/atomic_add_long_nv.3c
+usr/src/man/man3c/atomic_add_ptr.3c
+usr/src/man/man3c/atomic_add_ptr_nv.3c
+usr/src/man/man3c/atomic_add_short.3c
+usr/src/man/man3c/atomic_add_short_nv.3c
+usr/src/man/man3c/atomic_and_16.3c
+usr/src/man/man3c/atomic_and_16_nv.3c
+usr/src/man/man3c/atomic_and_32.3c
+usr/src/man/man3c/atomic_and_32_nv.3c
+usr/src/man/man3c/atomic_and_64.3c
+usr/src/man/man3c/atomic_and_64_nv.3c
+usr/src/man/man3c/atomic_and_8.3c
+usr/src/man/man3c/atomic_and_8_nv.3c
+usr/src/man/man3c/atomic_and_uchar.3c
+usr/src/man/man3c/atomic_and_uchar_nv.3c
+usr/src/man/man3c/atomic_and_uint.3c
+usr/src/man/man3c/atomic_and_uint_nv.3c
+usr/src/man/man3c/atomic_and_ulong.3c
+usr/src/man/man3c/atomic_and_ulong_nv.3c
+usr/src/man/man3c/atomic_and_ushort.3c
+usr/src/man/man3c/atomic_and_ushort_nv.3c
+usr/src/man/man3c/atomic_cas_16.3c
+usr/src/man/man3c/atomic_cas_32.3c
+usr/src/man/man3c/atomic_cas_64.3c
+usr/src/man/man3c/atomic_cas_8.3c
+usr/src/man/man3c/atomic_cas_ptr.3c
+usr/src/man/man3c/atomic_cas_uchar.3c
+usr/src/man/man3c/atomic_cas_uint.3c
+usr/src/man/man3c/atomic_cas_ulong.3c
+usr/src/man/man3c/atomic_cas_ushort.3c
+usr/src/man/man3c/atomic_clear_long_excl.3c
+usr/src/man/man3c/atomic_dec_16.3c
+usr/src/man/man3c/atomic_dec_16_nv.3c
+usr/src/man/man3c/atomic_dec_32.3c
+usr/src/man/man3c/atomic_dec_32_nv.3c
+usr/src/man/man3c/atomic_dec_64.3c
+usr/src/man/man3c/atomic_dec_64_nv.3c
+usr/src/man/man3c/atomic_dec_8.3c
+usr/src/man/man3c/atomic_dec_8_nv.3c
+usr/src/man/man3c/atomic_dec_ptr.3c
+usr/src/man/man3c/atomic_dec_ptr_nv.3c
+usr/src/man/man3c/atomic_dec_uchar.3c
+usr/src/man/man3c/atomic_dec_uchar_nv.3c
+usr/src/man/man3c/atomic_dec_uint.3c
+usr/src/man/man3c/atomic_dec_uint_nv.3c
+usr/src/man/man3c/atomic_dec_ulong.3c
+usr/src/man/man3c/atomic_dec_ulong_nv.3c
+usr/src/man/man3c/atomic_dec_ushort.3c
+usr/src/man/man3c/atomic_dec_ushort_nv.3c
+usr/src/man/man3c/atomic_inc_16.3c
+usr/src/man/man3c/atomic_inc_16_nv.3c
+usr/src/man/man3c/atomic_inc_32.3c
+usr/src/man/man3c/atomic_inc_32_nv.3c
+usr/src/man/man3c/atomic_inc_64.3c
+usr/src/man/man3c/atomic_inc_64_nv.3c
+usr/src/man/man3c/atomic_inc_8.3c
+usr/src/man/man3c/atomic_inc_8_nv.3c
+usr/src/man/man3c/atomic_inc_ptr.3c
+usr/src/man/man3c/atomic_inc_ptr_nv.3c
+usr/src/man/man3c/atomic_inc_uchar.3c
+usr/src/man/man3c/atomic_inc_uchar_nv.3c
+usr/src/man/man3c/atomic_inc_uint.3c
+usr/src/man/man3c/atomic_inc_uint_nv.3c
+usr/src/man/man3c/atomic_inc_ulong.3c
+usr/src/man/man3c/atomic_inc_ulong_nv.3c
+usr/src/man/man3c/atomic_inc_ushort.3c
+usr/src/man/man3c/atomic_inc_ushort_nv.3c
+usr/src/man/man3c/atomic_or_16.3c
+usr/src/man/man3c/atomic_or_16_nv.3c
+usr/src/man/man3c/atomic_or_32.3c
+usr/src/man/man3c/atomic_or_32_nv.3c
+usr/src/man/man3c/atomic_or_64.3c
+usr/src/man/man3c/atomic_or_64_nv.3c
+usr/src/man/man3c/atomic_or_8.3c
+usr/src/man/man3c/atomic_or_8_nv.3c
+usr/src/man/man3c/atomic_or_uchar.3c
+usr/src/man/man3c/atomic_or_uchar_nv.3c
+usr/src/man/man3c/atomic_or_uint.3c
+usr/src/man/man3c/atomic_or_uint_nv.3c
+usr/src/man/man3c/atomic_or_ulong.3c
+usr/src/man/man3c/atomic_or_ulong_nv.3c
+usr/src/man/man3c/atomic_or_ushort.3c
+usr/src/man/man3c/atomic_or_ushort_nv.3c
+usr/src/man/man3c/atomic_set_long_excl.3c
+usr/src/man/man3c/atomic_swap_16.3c
+usr/src/man/man3c/atomic_swap_32.3c
+usr/src/man/man3c/atomic_swap_64.3c
+usr/src/man/man3c/atomic_swap_8.3c
+usr/src/man/man3c/atomic_swap_ptr.3c
+usr/src/man/man3c/atomic_swap_uchar.3c
+usr/src/man/man3c/atomic_swap_uint.3c
+usr/src/man/man3c/atomic_swap_ulong.3c
+usr/src/man/man3c/atomic_swap_ushort.3c
+usr/src/man/man3c/backtrace.3c
+usr/src/man/man3c/backtrace_symbols.3c
+usr/src/man/man3c/backtrace_symbols_fd.3c
+usr/src/man/man3c/bcmp.3c
+usr/src/man/man3c/bcopy.3c
+usr/src/man/man3c/bind_textdomain_codeset.3c
+usr/src/man/man3c/bindtextdomain.3c
+usr/src/man/man3c/bzero.3c
+usr/src/man/man3c/calloc.3c
+usr/src/man/man3c/catclose.3c
+usr/src/man/man3c/cfgetospeed.3c
+usr/src/man/man3c/cfsetospeed.3c
+usr/src/man/man3c/cftime.3c
+usr/src/man/man3c/clearerr.3c
+usr/src/man/man3c/clock_getres.3c
+usr/src/man/man3c/clock_gettime.3c
+usr/src/man/man3c/closelog.3c
+usr/src/man/man3c/cond_broadcast.3c
+usr/src/man/man3c/cond_destroy.3c
+usr/src/man/man3c/cond_reltimedwait.3c
+usr/src/man/man3c/cond_signal.3c
+usr/src/man/man3c/cond_timedwait.3c
+usr/src/man/man3c/cond_wait.3c
+usr/src/man/man3c/csetcol.3c
+usr/src/man/man3c/csetlen.3c
+usr/src/man/man3c/csetno.3c
+usr/src/man/man3c/ctermid_r.3c
+usr/src/man/man3c/ctime_r.3c
+usr/src/man/man3c/dbm_clearerr.3c
+usr/src/man/man3c/dbm_close.3c
+usr/src/man/man3c/dbm_delete.3c
+usr/src/man/man3c/dbm_error.3c
+usr/src/man/man3c/dbm_fetch.3c
+usr/src/man/man3c/dbm_firstkey.3c
+usr/src/man/man3c/dbm_nextkey.3c
+usr/src/man/man3c/dbm_open.3c
+usr/src/man/man3c/dbm_store.3c
+usr/src/man/man3c/dcgettext.3c
+usr/src/man/man3c/dcngettext.3c
+usr/src/man/man3c/decimal_to_double.3c
+usr/src/man/man3c/decimal_to_extended.3c
+usr/src/man/man3c/decimal_to_quadruple.3c
+usr/src/man/man3c/decimal_to_single.3c
+usr/src/man/man3c/dgettext.3c
+usr/src/man/man3c/dladdr1.3c
+usr/src/man/man3c/dlmopen.3c
+usr/src/man/man3c/dngettext.3c
+usr/src/man/man3c/door_setparam.3c
+usr/src/man/man3c/door_unbind.3c
+usr/src/man/man3c/double_to_decimal.3c
+usr/src/man/man3c/edata.3c
+usr/src/man/man3c/endgrent.3c
+usr/src/man/man3c/endnetgrent.3c
+usr/src/man/man3c/endpwent.3c
+usr/src/man/man3c/endspent.3c
+usr/src/man/man3c/endusershell.3c
+usr/src/man/man3c/endutent.3c
+usr/src/man/man3c/endutxent.3c
+usr/src/man/man3c/erand48.3c
+usr/src/man/man3c/errno.3c
+usr/src/man/man3c/errx.3c
+usr/src/man/man3c/etext.3c
+usr/src/man/man3c/euccol.3c
+usr/src/man/man3c/eucscol.3c
+usr/src/man/man3c/extended_to_decimal.3c
+usr/src/man/man3c/fconvert.3c
+usr/src/man/man3c/fcvt.3c
+usr/src/man/man3c/fdopendir.3c
+usr/src/man/man3c/fdwalk.3c
+usr/src/man/man3c/feof.3c
+usr/src/man/man3c/fgetgrent.3c
+usr/src/man/man3c/fgetgrent_r.3c
+usr/src/man/man3c/fgetpwent.3c
+usr/src/man/man3c/fgetpwent_r.3c
+usr/src/man/man3c/fgets.3c
+usr/src/man/man3c/fgetspent.3c
+usr/src/man/man3c/fgetspent_r.3c
+usr/src/man/man3c/fgetws.3c
+usr/src/man/man3c/file_to_decimal.3c
+usr/src/man/man3c/fileno.3c
+usr/src/man/man3c/finite.3c
+usr/src/man/man3c/fpclass.3c
+usr/src/man/man3c/fpgetmask.3c
+usr/src/man/man3c/fpgetsticky.3c
+usr/src/man/man3c/fprintf.3c
+usr/src/man/man3c/fpsetmask.3c
+usr/src/man/man3c/fpsetround.3c
+usr/src/man/man3c/fpsetsticky.3c
+usr/src/man/man3c/fputs.3c
+usr/src/man/man3c/free.3c
+usr/src/man/man3c/fscanf.3c
+usr/src/man/man3c/fseeko.3c
+usr/src/man/man3c/fsetattr.3c
+usr/src/man/man3c/ftello.3c
+usr/src/man/man3c/ftruncate.3c
+usr/src/man/man3c/ftrylockfile.3c
+usr/src/man/man3c/func_to_decimal.3c
+usr/src/man/man3c/funlockfile.3c
+usr/src/man/man3c/gconvert.3c
+usr/src/man/man3c/gcvt.3c
+usr/src/man/man3c/getattrat.3c
+usr/src/man/man3c/getc.3c
+usr/src/man/man3c/getc_unlocked.3c
+usr/src/man/man3c/getchar.3c
+usr/src/man/man3c/getchar_unlocked.3c
+usr/src/man/man3c/getextmntent.3c
+usr/src/man/man3c/getgrent.3c
+usr/src/man/man3c/getgrent_r.3c
+usr/src/man/man3c/getgrgid.3c
+usr/src/man/man3c/getgrgid_r.3c
+usr/src/man/man3c/getgrnam_r.3c
+usr/src/man/man3c/gethomelgroup.3c
+usr/src/man/man3c/gethrvtime.3c
+usr/src/man/man3c/getlogin_r.3c
+usr/src/man/man3c/getmntany.3c
+usr/src/man/man3c/getnetgrent_r.3c
+usr/src/man/man3c/getpassphrase.3c
+usr/src/man/man3c/getpwent.3c
+usr/src/man/man3c/getpwent_r.3c
+usr/src/man/man3c/getpwnam_r.3c
+usr/src/man/man3c/getpwuid.3c
+usr/src/man/man3c/getpwuid_r.3c
+usr/src/man/man3c/getspent.3c
+usr/src/man/man3c/getspent_r.3c
+usr/src/man/man3c/getspnam_r.3c
+usr/src/man/man3c/getutid.3c
+usr/src/man/man3c/getutline.3c
+usr/src/man/man3c/getutmp.3c
+usr/src/man/man3c/getutmpx.3c
+usr/src/man/man3c/getutxid.3c
+usr/src/man/man3c/getutxline.3c
+usr/src/man/man3c/getvfsany.3c
+usr/src/man/man3c/getvfsfile.3c
+usr/src/man/man3c/getvfsspec.3c
+usr/src/man/man3c/getw.3c
+usr/src/man/man3c/getzoneidbyname.3c
+usr/src/man/man3c/getzonenamebyid.3c
+usr/src/man/man3c/globfree.3c
+usr/src/man/man3c/gmtime.3c
+usr/src/man/man3c/gmtime_r.3c
+usr/src/man/man3c/gsignal.3c
+usr/src/man/man3c/hasmntopt.3c
+usr/src/man/man3c/hcreate.3c
+usr/src/man/man3c/hdestroy.3c
+usr/src/man/man3c/initstate.3c
+usr/src/man/man3c/innetgr.3c
+usr/src/man/man3c/isalnum.3c
+usr/src/man/man3c/isalpha.3c
+usr/src/man/man3c/isascii.3c
+usr/src/man/man3c/isblank.3c
+usr/src/man/man3c/iscntrl.3c
+usr/src/man/man3c/isdigit.3c
+usr/src/man/man3c/isenglish.3c
+usr/src/man/man3c/isgraph.3c
+usr/src/man/man3c/isideogram.3c
+usr/src/man/man3c/islower.3c
+usr/src/man/man3c/isnanf.3c
+usr/src/man/man3c/isnumber.3c
+usr/src/man/man3c/isphonogram.3c
+usr/src/man/man3c/isprint.3c
+usr/src/man/man3c/ispunct.3c
+usr/src/man/man3c/isspace.3c
+usr/src/man/man3c/isspecial.3c
+usr/src/man/man3c/isupper.3c
+usr/src/man/man3c/iswalnum.3c
+usr/src/man/man3c/iswascii.3c
+usr/src/man/man3c/iswblank.3c
+usr/src/man/man3c/iswcntrl.3c
+usr/src/man/man3c/iswdigit.3c
+usr/src/man/man3c/iswgraph.3c
+usr/src/man/man3c/iswlower.3c
+usr/src/man/man3c/iswprint.3c
+usr/src/man/man3c/iswpunct.3c
+usr/src/man/man3c/iswspace.3c
+usr/src/man/man3c/iswupper.3c
+usr/src/man/man3c/iswxdigit.3c
+usr/src/man/man3c/isxdigit.3c
+usr/src/man/man3c/jrand48.3c
+usr/src/man/man3c/l64a.3c
+usr/src/man/man3c/labs.3c
+usr/src/man/man3c/lcong48.3c
+usr/src/man/man3c/ldiv.3c
+usr/src/man/man3c/lfind.3c
+usr/src/man/man3c/llabs.3c
+usr/src/man/man3c/lldiv.3c
+usr/src/man/man3c/lltostr.3c
+usr/src/man/man3c/localtime.3c
+usr/src/man/man3c/localtime_r.3c
+usr/src/man/man3c/longjmp.3c
+usr/src/man/man3c/lrand48.3c
+usr/src/man/man3c/major.3c
+usr/src/man/man3c/memalign.3c
+usr/src/man/man3c/membar_consumer.3c
+usr/src/man/man3c/membar_enter.3c
+usr/src/man/man3c/membar_exit.3c
+usr/src/man/man3c/membar_producer.3c
+usr/src/man/man3c/memccpy.3c
+usr/src/man/man3c/memchr.3c
+usr/src/man/man3c/memcmp.3c
+usr/src/man/man3c/memcpy.3c
+usr/src/man/man3c/memmove.3c
+usr/src/man/man3c/memset.3c
+usr/src/man/man3c/minor.3c
+usr/src/man/man3c/mkdtemp.3c
+usr/src/man/man3c/mkstemps.3c
+usr/src/man/man3c/mq_reltimedreceive_np.3c
+usr/src/man/man3c/mq_reltimedsend_np.3c
+usr/src/man/man3c/mq_timedreceive.3c
+usr/src/man/man3c/mq_timedsend.3c
+usr/src/man/man3c/mrand48.3c
+usr/src/man/man3c/munlock.3c
+usr/src/man/man3c/munlockall.3c
+usr/src/man/man3c/mutex_consistent.3c
+usr/src/man/man3c/mutex_destroy.3c
+usr/src/man/man3c/mutex_lock.3c
+usr/src/man/man3c/mutex_trylock.3c
+usr/src/man/man3c/mutex_unlock.3c
+usr/src/man/man3c/nftw.3c
+usr/src/man/man3c/ngettext.3c
+usr/src/man/man3c/nrand48.3c
+usr/src/man/man3c/openlog.3c
+usr/src/man/man3c/pclose.3c
+usr/src/man/man3c/port_dissociate.3c
+usr/src/man/man3c/port_getn.3c
+usr/src/man/man3c/port_sendn.3c
+usr/src/man/man3c/posix_spawn_file_actions_addopen.3c
+usr/src/man/man3c/posix_spawn_file_actions_init.3c
+usr/src/man/man3c/posix_spawnattr_init.3c
+usr/src/man/man3c/posix_spawnattr_setflags.3c
+usr/src/man/man3c/posix_spawnattr_setpgroup.3c
+usr/src/man/man3c/posix_spawnattr_setschedparam.3c
+usr/src/man/man3c/posix_spawnattr_setschedpolicy.3c
+usr/src/man/man3c/posix_spawnattr_setsigdefault.3c
+usr/src/man/man3c/posix_spawnattr_setsigignore_np.3c
+usr/src/man/man3c/posix_spawnattr_setsigmask.3c
+usr/src/man/man3c/posix_spawnp.3c
+usr/src/man/man3c/printstack.3c
+usr/src/man/man3c/priv_allocset.3c
+usr/src/man/man3c/priv_basicset.3c
+usr/src/man/man3c/priv_copyset.3c
+usr/src/man/man3c/priv_delset.3c
+usr/src/man/man3c/priv_emptyset.3c
+usr/src/man/man3c/priv_fillset.3c
+usr/src/man/man3c/priv_freeset.3c
+usr/src/man/man3c/priv_getbyname.3c
+usr/src/man/man3c/priv_getbynum.3c
+usr/src/man/man3c/priv_getsetbyname.3c
+usr/src/man/man3c/priv_getsetbynum.3c
+usr/src/man/man3c/priv_gettext.3c
+usr/src/man/man3c/priv_ineffect.3c
+usr/src/man/man3c/priv_intersect.3c
+usr/src/man/man3c/priv_inverse.3c
+usr/src/man/man3c/priv_isemptyset.3c
+usr/src/man/man3c/priv_isequalset.3c
+usr/src/man/man3c/priv_isfullset.3c
+usr/src/man/man3c/priv_ismember.3c
+usr/src/man/man3c/priv_issubset.3c
+usr/src/man/man3c/priv_set_to_str.3c
+usr/src/man/man3c/priv_union.3c
+usr/src/man/man3c/pselect.3c
+usr/src/man/man3c/psiginfo.3c
+usr/src/man/man3c/pthread_attr_destroy.3c
+usr/src/man/man3c/pthread_attr_setdetachstate.3c
+usr/src/man/man3c/pthread_attr_setguardsize.3c
+usr/src/man/man3c/pthread_attr_setinheritsched.3c
+usr/src/man/man3c/pthread_attr_setschedparam.3c
+usr/src/man/man3c/pthread_attr_setschedpolicy.3c
+usr/src/man/man3c/pthread_attr_setscope.3c
+usr/src/man/man3c/pthread_attr_setstack.3c
+usr/src/man/man3c/pthread_attr_setstackaddr.3c
+usr/src/man/man3c/pthread_attr_setstacksize.3c
+usr/src/man/man3c/pthread_barrier_init.3c
+usr/src/man/man3c/pthread_barrierattr_init.3c
+usr/src/man/man3c/pthread_barrierattr_setpshared.3c
+usr/src/man/man3c/pthread_cond_broadcast.3c
+usr/src/man/man3c/pthread_cond_destroy.3c
+usr/src/man/man3c/pthread_cond_reltimedwait_np.3c
+usr/src/man/man3c/pthread_cond_timedwait.3c
+usr/src/man/man3c/pthread_condattr_destroy.3c
+usr/src/man/man3c/pthread_condattr_setclock.3c
+usr/src/man/man3c/pthread_condattr_setpshared.3c
+usr/src/man/man3c/pthread_key_create_once_np.3c
+usr/src/man/man3c/pthread_mutex_destroy.3c
+usr/src/man/man3c/pthread_mutex_reltimedlock_np.3c
+usr/src/man/man3c/pthread_mutex_setprioceiling.3c
+usr/src/man/man3c/pthread_mutex_trylock.3c
+usr/src/man/man3c/pthread_mutex_unlock.3c
+usr/src/man/man3c/pthread_mutexattr_destroy.3c
+usr/src/man/man3c/pthread_mutexattr_setprioceiling.3c
+usr/src/man/man3c/pthread_mutexattr_setprotocol.3c
+usr/src/man/man3c/pthread_mutexattr_setpshared.3c
+usr/src/man/man3c/pthread_mutexattr_settype.3c
+usr/src/man/man3c/pthread_rwlock_destroy.3c
+usr/src/man/man3c/pthread_rwlock_reltimedrdlock_np.3c
+usr/src/man/man3c/pthread_rwlock_reltimedwrlock_np.3c
+usr/src/man/man3c/pthread_rwlock_tryrdlock.3c
+usr/src/man/man3c/pthread_rwlock_trywrlock.3c
+usr/src/man/man3c/pthread_rwlockattr_destroy.3c
+usr/src/man/man3c/pthread_rwlockattr_setpshared.3c
+usr/src/man/man3c/pthread_setconcurrency.3c
+usr/src/man/man3c/pthread_setschedparam.3c
+usr/src/man/man3c/pthread_setspecific.3c
+usr/src/man/man3c/pthread_spin_init.3c
+usr/src/man/man3c/pthread_spin_trylock.3c
+usr/src/man/man3c/putc.3c
+usr/src/man/man3c/putc_unlocked.3c
+usr/src/man/man3c/putchar.3c
+usr/src/man/man3c/putchar_unlocked.3c
+usr/src/man/man3c/putmntent.3c
+usr/src/man/man3c/pututline.3c
+usr/src/man/man3c/pututxline.3c
+usr/src/man/man3c/putw.3c
+usr/src/man/man3c/putwc.3c
+usr/src/man/man3c/putwchar.3c
+usr/src/man/man3c/qeconvert.3c
+usr/src/man/man3c/qfconvert.3c
+usr/src/man/man3c/qgconvert.3c
+usr/src/man/man3c/quadruple_to_decimal.3c
+usr/src/man/man3c/rand_r.3c
+usr/src/man/man3c/rctlblk_get_enforced_value.3c
+usr/src/man/man3c/rctlblk_get_firing_time.3c
+usr/src/man/man3c/rctlblk_get_global_action.3c
+usr/src/man/man3c/rctlblk_get_global_flags.3c
+usr/src/man/man3c/rctlblk_get_local_action.3c
+usr/src/man/man3c/rctlblk_get_local_flags.3c
+usr/src/man/man3c/rctlblk_get_privilege.3c
+usr/src/man/man3c/rctlblk_get_recipient_pid.3c
+usr/src/man/man3c/rctlblk_get_value.3c
+usr/src/man/man3c/rctlblk_set_local_action.3c
+usr/src/man/man3c/rctlblk_set_local_flags.3c
+usr/src/man/man3c/rctlblk_set_privilege.3c
+usr/src/man/man3c/rctlblk_set_recipient_pid.3c
+usr/src/man/man3c/rctlblk_size.3c
+usr/src/man/man3c/re_exec.3c
+usr/src/man/man3c/readdir_r.3c
+usr/src/man/man3c/realloc.3c
+usr/src/man/man3c/regerror.3c
+usr/src/man/man3c/regex.3c
+usr/src/man/man3c/regexec.3c
+usr/src/man/man3c/regfree.3c
+usr/src/man/man3c/remque.3c
+usr/src/man/man3c/resetmnttab.3c
+usr/src/man/man3c/rindex.3c
+usr/src/man/man3c/rw_rdlock.3c
+usr/src/man/man3c/rw_tryrdlock.3c
+usr/src/man/man3c/rw_trywrlock.3c
+usr/src/man/man3c/rw_unlock.3c
+usr/src/man/man3c/rw_wrlock.3c
+usr/src/man/man3c/rwlock_destroy.3c
+usr/src/man/man3c/rwlock_init.3c
+usr/src/man/man3c/sched_get_priority_min.3c
+usr/src/man/man3c/schedctl_exit.3c
+usr/src/man/man3c/schedctl_lookup.3c
+usr/src/man/man3c/schedctl_start.3c
+usr/src/man/man3c/schedctl_stop.3c
+usr/src/man/man3c/seconvert.3c
+usr/src/man/man3c/seed48.3c
+usr/src/man/man3c/sem_reltimedwait_np.3c
+usr/src/man/man3c/sem_trywait.3c
+usr/src/man/man3c/sema_destroy.3c
+usr/src/man/man3c/sema_init.3c
+usr/src/man/man3c/sema_post.3c
+usr/src/man/man3c/sema_trywait.3c
+usr/src/man/man3c/sema_wait.3c
+usr/src/man/man3c/setattrat.3c
+usr/src/man/man3c/setgrent.3c
+usr/src/man/man3c/sethostname.3c
+usr/src/man/man3c/setlinebuf.3c
+usr/src/man/man3c/setlogmask.3c
+usr/src/man/man3c/setnetgrent.3c
+usr/src/man/man3c/setpriority.3c
+usr/src/man/man3c/setpwent.3c
+usr/src/man/man3c/setspent.3c
+usr/src/man/man3c/setstate.3c
+usr/src/man/man3c/settimeofday.3c
+usr/src/man/man3c/setusershell.3c
+usr/src/man/man3c/setutent.3c
+usr/src/man/man3c/setutxent.3c
+usr/src/man/man3c/setvbuf.3c
+usr/src/man/man3c/sfconvert.3c
+usr/src/man/man3c/sgconvert.3c
+usr/src/man/man3c/sig2str.3c
+usr/src/man/man3c/sigaddset.3c
+usr/src/man/man3c/sigdelset.3c
+usr/src/man/man3c/sigemptyset.3c
+usr/src/man/man3c/sigfillset.3c
+usr/src/man/man3c/sighold.3c
+usr/src/man/man3c/sigignore.3c
+usr/src/man/man3c/sigismember.3c
+usr/src/man/man3c/siglongjmp.3c
+usr/src/man/man3c/sigpause.3c
+usr/src/man/man3c/sigrelse.3c
+usr/src/man/man3c/sigset.3c
+usr/src/man/man3c/sigsetjmp.3c
+usr/src/man/man3c/sigtimedwait.3c
+usr/src/man/man3c/single_to_decimal.3c
+usr/src/man/man3c/snprintf.3c
+usr/src/man/man3c/sprintf.3c
+usr/src/man/man3c/srand.3c
+usr/src/man/man3c/srand48.3c
+usr/src/man/man3c/srandom.3c
+usr/src/man/man3c/sscanf.3c
+usr/src/man/man3c/strcasecmp.3c
+usr/src/man/man3c/strcat.3c
+usr/src/man/man3c/strchr.3c
+usr/src/man/man3c/strcmp.3c
+usr/src/man/man3c/strcpy.3c
+usr/src/man/man3c/strcspn.3c
+usr/src/man/man3c/strdup.3c
+usr/src/man/man3c/strerror_r.3c
+usr/src/man/man3c/strlcat.3c
+usr/src/man/man3c/strlcpy.3c
+usr/src/man/man3c/strlen.3c
+usr/src/man/man3c/strncasecmp.3c
+usr/src/man/man3c/strncat.3c
+usr/src/man/man3c/strncmp.3c
+usr/src/man/man3c/strncpy.3c
+usr/src/man/man3c/strnlen.3c
+usr/src/man/man3c/strpbrk.3c
+usr/src/man/man3c/strrchr.3c
+usr/src/man/man3c/strsep.3c
+usr/src/man/man3c/strspn.3c
+usr/src/man/man3c/strstr.3c
+usr/src/man/man3c/strtof.3c
+usr/src/man/man3c/strtok.3c
+usr/src/man/man3c/strtok_r.3c
+usr/src/man/man3c/strtold.3c
+usr/src/man/man3c/strtoll.3c
+usr/src/man/man3c/strtoull.3c
+usr/src/man/man3c/strtoumax.3c
+usr/src/man/man3c/swapcontext.3c
+usr/src/man/man3c/swprintf.3c
+usr/src/man/man3c/swscanf.3c
+usr/src/man/man3c/tdelete.3c
+usr/src/man/man3c/tempnam.3c
+usr/src/man/man3c/textdomain.3c
+usr/src/man/man3c/tfind.3c
+usr/src/man/man3c/thr_continue.3c
+usr/src/man/man3c/thr_getspecific.3c
+usr/src/man/man3c/thr_keycreate_once.3c
+usr/src/man/man3c/thr_setconcurrency.3c
+usr/src/man/man3c/thr_setprio.3c
+usr/src/man/man3c/thr_setspecific.3c
+usr/src/man/man3c/timer_getoverrun.3c
+usr/src/man/man3c/timer_gettime.3c
+usr/src/man/man3c/timerclear.3c
+usr/src/man/man3c/timercmp.3c
+usr/src/man/man3c/timerisset.3c
+usr/src/man/man3c/timersub.3c
+usr/src/man/man3c/tmpnam_r.3c
+usr/src/man/man3c/ttyname_r.3c
+usr/src/man/man3c/twalk.3c
+usr/src/man/man3c/tzset.3c
+usr/src/man/man3c/uconv_u16tou8.3c
+usr/src/man/man3c/uconv_u32tou16.3c
+usr/src/man/man3c/uconv_u32tou8.3c
+usr/src/man/man3c/uconv_u8tou16.3c
+usr/src/man/man3c/uconv_u8tou32.3c
+usr/src/man/man3c/ucred_free.3c
+usr/src/man/man3c/ucred_getegid.3c
+usr/src/man/man3c/ucred_geteuid.3c
+usr/src/man/man3c/ucred_getgroups.3c
+usr/src/man/man3c/ucred_getlabel.3c
+usr/src/man/man3c/ucred_getpflags.3c
+usr/src/man/man3c/ucred_getpid.3c
+usr/src/man/man3c/ucred_getprivset.3c
+usr/src/man/man3c/ucred_getprojid.3c
+usr/src/man/man3c/ucred_getrgid.3c
+usr/src/man/man3c/ucred_getruid.3c
+usr/src/man/man3c/ucred_getsgid.3c
+usr/src/man/man3c/ucred_getsuid.3c
+usr/src/man/man3c/ucred_getzoneid.3c
+usr/src/man/man3c/ucred_size.3c
+usr/src/man/man3c/ulckpwdf.3c
+usr/src/man/man3c/ulltostr.3c
+usr/src/man/man3c/unordered.3c
+usr/src/man/man3c/updwtmp.3c
+usr/src/man/man3c/updwtmpx.3c
+usr/src/man/man3c/utmpname.3c
+usr/src/man/man3c/utmpxname.3c
+usr/src/man/man3c/valloc.3c
+usr/src/man/man3c/vasprintf.3c
+usr/src/man/man3c/verr.3c
+usr/src/man/man3c/verrx.3c
+usr/src/man/man3c/vfprintf.3c
+usr/src/man/man3c/vfscanf.3c
+usr/src/man/man3c/vfwscanf.3c
+usr/src/man/man3c/vscanf.3c
+usr/src/man/man3c/vsnprintf.3c
+usr/src/man/man3c/vsprintf.3c
+usr/src/man/man3c/vsscanf.3c
+usr/src/man/man3c/vswprintf.3c
+usr/src/man/man3c/vswscanf.3c
+usr/src/man/man3c/vwarn.3c
+usr/src/man/man3c/vwarnx.3c
+usr/src/man/man3c/vwprintf.3c
+usr/src/man/man3c/vwscanf.3c
+usr/src/man/man3c/wait4.3c
+usr/src/man/man3c/warn.3c
+usr/src/man/man3c/warnx.3c
+usr/src/man/man3c/watof.3c
+usr/src/man/man3c/watoi.3c
+usr/src/man/man3c/watol.3c
+usr/src/man/man3c/watoll.3c
+usr/src/man/man3c/wcscat.3c
+usr/src/man/man3c/wcschr.3c
+usr/src/man/man3c/wcscmp.3c
+usr/src/man/man3c/wcscpy.3c
+usr/src/man/man3c/wcscspn.3c
+usr/src/man/man3c/wcsetno.3c
+usr/src/man/man3c/wcslen.3c
+usr/src/man/man3c/wcsncat.3c
+usr/src/man/man3c/wcsncmp.3c
+usr/src/man/man3c/wcsncpy.3c
+usr/src/man/man3c/wcspbrk.3c
+usr/src/man/man3c/wcsrchr.3c
+usr/src/man/man3c/wcsspn.3c
+usr/src/man/man3c/wcstof.3c
+usr/src/man/man3c/wcstok.3c
+usr/src/man/man3c/wcstold.3c
+usr/src/man/man3c/wcstoll.3c
+usr/src/man/man3c/wcstoull.3c
+usr/src/man/man3c/wcstoumax.3c
+usr/src/man/man3c/wcswcs.3c
+usr/src/man/man3c/windex.3c
+usr/src/man/man3c/wordfree.3c
+usr/src/man/man3c/wprintf.3c
+usr/src/man/man3c/wrindex.3c
+usr/src/man/man3c/wscanf.3c
+usr/src/man/man3c/wscasecmp.3c
+usr/src/man/man3c/wscat.3c
+usr/src/man/man3c/wschr.3c
+usr/src/man/man3c/wscmp.3c
+usr/src/man/man3c/wscol.3c
+usr/src/man/man3c/wscoll.3c
+usr/src/man/man3c/wscpy.3c
+usr/src/man/man3c/wscspn.3c
+usr/src/man/man3c/wsdup.3c
+usr/src/man/man3c/wslen.3c
+usr/src/man/man3c/wsncasecmp.3c
+usr/src/man/man3c/wsncat.3c
+usr/src/man/man3c/wsncmp.3c
+usr/src/man/man3c/wsncpy.3c
+usr/src/man/man3c/wspbrk.3c
+usr/src/man/man3c/wsrchr.3c
+usr/src/man/man3c/wsspn.3c
+usr/src/man/man3c/wstod.3c
+usr/src/man/man3c/wstok.3c
+usr/src/man/man3c/wstol.3c
+usr/src/man/man3c/wstostr.3c
+usr/src/man/man3c/wsxfrm.3c
+usr/src/man/man3c_db/td_event_addset.3c_db
+usr/src/man/man3c_db/td_event_delset.3c_db
+usr/src/man/man3c_db/td_event_emptyset.3c_db
+usr/src/man/man3c_db/td_event_fillset.3c_db
+usr/src/man/man3c_db/td_eventisempty.3c_db
+usr/src/man/man3c_db/td_eventismember.3c_db
+usr/src/man/man3c_db/td_sync_get_stats.3c_db
+usr/src/man/man3c_db/td_sync_setstate.3c_db
+usr/src/man/man3c_db/td_sync_waiters.3c_db
+usr/src/man/man3c_db/td_ta_clear_event.3c_db
+usr/src/man/man3c_db/td_ta_delete.3c_db
+usr/src/man/man3c_db/td_ta_event_getmsg.3c_db
+usr/src/man/man3c_db/td_ta_get_ph.3c_db
+usr/src/man/man3c_db/td_ta_get_stats.3c_db
+usr/src/man/man3c_db/td_ta_map_lwp2thr.3c_db
+usr/src/man/man3c_db/td_ta_reset_stats.3c_db
+usr/src/man/man3c_db/td_ta_set_event.3c_db
+usr/src/man/man3c_db/td_ta_sync_tracking_enable.3c_db
+usr/src/man/man3c_db/td_ta_thr_iter.3c_db
+usr/src/man/man3c_db/td_ta_tsd_iter.3c_db
+usr/src/man/man3c_db/td_thr_clear_event.3c_db
+usr/src/man/man3c_db/td_thr_dbresume.3c_db
+usr/src/man/man3c_db/td_thr_event_enable.3c_db
+usr/src/man/man3c_db/td_thr_event_getmsg.3c_db
+usr/src/man/man3c_db/td_thr_getfpregs.3c_db
+usr/src/man/man3c_db/td_thr_getxregs.3c_db
+usr/src/man/man3c_db/td_thr_getxregsize.3c_db
+usr/src/man/man3c_db/td_thr_set_event.3c_db
+usr/src/man/man3c_db/td_thr_setfpregs.3c_db
+usr/src/man/man3c_db/td_thr_setgregs.3c_db
+usr/src/man/man3c_db/td_thr_setxregs.3c_db
+usr/src/man/man3c_db/td_thr_sigsetmask.3c_db
+usr/src/man/man3cfgadm/config_ap_id_cmp.3cfgadm
+usr/src/man/man3cfgadm/config_change_state.3cfgadm
+usr/src/man/man3cfgadm/config_list.3cfgadm
+usr/src/man/man3cfgadm/config_list_ext.3cfgadm
+usr/src/man/man3cfgadm/config_private_func.3cfgadm
+usr/src/man/man3cfgadm/config_stat.3cfgadm
+usr/src/man/man3cfgadm/config_strerror.3cfgadm
+usr/src/man/man3cfgadm/config_test.3cfgadm
+usr/src/man/man3cfgadm/config_unload_libs.3cfgadm
+usr/src/man/man3commputil/sdp_add_attribute.3commputil
+usr/src/man/man3commputil/sdp_add_bandwidth.3commputil
+usr/src/man/man3commputil/sdp_add_connection.3commputil
+usr/src/man/man3commputil/sdp_add_email.3commputil
+usr/src/man/man3commputil/sdp_add_information.3commputil
+usr/src/man/man3commputil/sdp_add_key.3commputil
+usr/src/man/man3commputil/sdp_add_media.3commputil
+usr/src/man/man3commputil/sdp_add_name.3commputil
+usr/src/man/man3commputil/sdp_add_phone.3commputil
+usr/src/man/man3commputil/sdp_add_repeat.3commputil
+usr/src/man/man3commputil/sdp_add_time.3commputil
+usr/src/man/man3commputil/sdp_add_uri.3commputil
+usr/src/man/man3commputil/sdp_add_zone.3commputil
+usr/src/man/man3commputil/sdp_delete_all_media_field.3commputil
+usr/src/man/man3commputil/sdp_delete_attribute.3commputil
+usr/src/man/man3commputil/sdp_free_session.3commputil
+usr/src/man/man3contract/ct_ctl_abandon.3contract
+usr/src/man/man3contract/ct_ctl_ack.3contract
+usr/src/man/man3contract/ct_ctl_nack.3contract
+usr/src/man/man3contract/ct_ctl_newct.3contract
+usr/src/man/man3contract/ct_ctl_qack.3contract
+usr/src/man/man3contract/ct_dev_status_get_aset.3contract
+usr/src/man/man3contract/ct_dev_status_get_minor.3contract
+usr/src/man/man3contract/ct_dev_status_get_noneg.3contract
+usr/src/man/man3contract/ct_dev_tmpl_clear_noneg.3contract
+usr/src/man/man3contract/ct_dev_tmpl_get_aset.3contract
+usr/src/man/man3contract/ct_dev_tmpl_get_minor.3contract
+usr/src/man/man3contract/ct_dev_tmpl_get_noneg.3contract
+usr/src/man/man3contract/ct_dev_tmpl_set_minor.3contract
+usr/src/man/man3contract/ct_dev_tmpl_set_noneg.3contract
+usr/src/man/man3contract/ct_event_free.3contract
+usr/src/man/man3contract/ct_event_get_ctid.3contract
+usr/src/man/man3contract/ct_event_get_evid.3contract
+usr/src/man/man3contract/ct_event_get_flags.3contract
+usr/src/man/man3contract/ct_event_get_nevid.3contract
+usr/src/man/man3contract/ct_event_get_newct.3contract
+usr/src/man/man3contract/ct_event_get_type.3contract
+usr/src/man/man3contract/ct_event_read_critical.3contract
+usr/src/man/man3contract/ct_event_reliable.3contract
+usr/src/man/man3contract/ct_event_reset.3contract
+usr/src/man/man3contract/ct_pr_event_get_exitstatus.3contract
+usr/src/man/man3contract/ct_pr_event_get_gcorefile.3contract
+usr/src/man/man3contract/ct_pr_event_get_pcorefile.3contract
+usr/src/man/man3contract/ct_pr_event_get_ppid.3contract
+usr/src/man/man3contract/ct_pr_event_get_sender.3contract
+usr/src/man/man3contract/ct_pr_event_get_senderct.3contract
+usr/src/man/man3contract/ct_pr_event_get_signal.3contract
+usr/src/man/man3contract/ct_pr_event_get_zcorefile.3contract
+usr/src/man/man3contract/ct_pr_status_get_contracts.3contract
+usr/src/man/man3contract/ct_pr_status_get_fatal.3contract
+usr/src/man/man3contract/ct_pr_status_get_members.3contract
+usr/src/man/man3contract/ct_pr_status_get_svc_aux.3contract
+usr/src/man/man3contract/ct_pr_status_get_svc_creator.3contract
+usr/src/man/man3contract/ct_pr_status_get_svc_ctid.3contract
+usr/src/man/man3contract/ct_pr_status_get_svc_fmri.3contract
+usr/src/man/man3contract/ct_pr_tmpl_get_fatal.3contract
+usr/src/man/man3contract/ct_pr_tmpl_get_param.3contract
+usr/src/man/man3contract/ct_pr_tmpl_get_svc_aux.3contract
+usr/src/man/man3contract/ct_pr_tmpl_get_svc_fmri.3contract
+usr/src/man/man3contract/ct_pr_tmpl_get_transfer.3contract
+usr/src/man/man3contract/ct_pr_tmpl_set_fatal.3contract
+usr/src/man/man3contract/ct_pr_tmpl_set_param.3contract
+usr/src/man/man3contract/ct_pr_tmpl_set_svc_aux.3contract
+usr/src/man/man3contract/ct_pr_tmpl_set_svc_fmri.3contract
+usr/src/man/man3contract/ct_status_free.3contract
+usr/src/man/man3contract/ct_status_get_cookie.3contract
+usr/src/man/man3contract/ct_status_get_critical.3contract
+usr/src/man/man3contract/ct_status_get_holder.3contract
+usr/src/man/man3contract/ct_status_get_id.3contract
+usr/src/man/man3contract/ct_status_get_informative.3contract
+usr/src/man/man3contract/ct_status_get_nevents.3contract
+usr/src/man/man3contract/ct_status_get_nevid.3contract
+usr/src/man/man3contract/ct_status_get_ntime.3contract
+usr/src/man/man3contract/ct_status_get_qtime.3contract
+usr/src/man/man3contract/ct_status_get_state.3contract
+usr/src/man/man3contract/ct_status_get_type.3contract
+usr/src/man/man3contract/ct_status_get_zoneid.3contract
+usr/src/man/man3contract/ct_tmpl_clear.3contract
+usr/src/man/man3contract/ct_tmpl_create.3contract
+usr/src/man/man3contract/ct_tmpl_get_cookie.3contract
+usr/src/man/man3contract/ct_tmpl_get_critical.3contract
+usr/src/man/man3contract/ct_tmpl_get_informative.3contract
+usr/src/man/man3contract/ct_tmpl_set_cookie.3contract
+usr/src/man/man3contract/ct_tmpl_set_critical.3contract
+usr/src/man/man3contract/ct_tmpl_set_informative.3contract
+usr/src/man/man3cpc/cpc_bind_cpu.3cpc
+usr/src/man/man3cpc/cpc_bind_pctx.3cpc
+usr/src/man/man3cpc/cpc_buf_add.3cpc
+usr/src/man/man3cpc/cpc_buf_copy.3cpc
+usr/src/man/man3cpc/cpc_buf_destroy.3cpc
+usr/src/man/man3cpc/cpc_buf_get.3cpc
+usr/src/man/man3cpc/cpc_buf_hrtime.3cpc
+usr/src/man/man3cpc/cpc_buf_set.3cpc
+usr/src/man/man3cpc/cpc_buf_sub.3cpc
+usr/src/man/man3cpc/cpc_buf_tick.3cpc
+usr/src/man/man3cpc/cpc_buf_zero.3cpc
+usr/src/man/man3cpc/cpc_caps.3cpc
+usr/src/man/man3cpc/cpc_cciname.3cpc
+usr/src/man/man3cpc/cpc_close.3cpc
+usr/src/man/man3cpc/cpc_count_sys_events.3cpc
+usr/src/man/man3cpc/cpc_cpuref.3cpc
+usr/src/man/man3cpc/cpc_disable.3cpc
+usr/src/man/man3cpc/cpc_event_accum.3cpc
+usr/src/man/man3cpc/cpc_eventtostr.3cpc
+usr/src/man/man3cpc/cpc_getcciname.3cpc
+usr/src/man/man3cpc/cpc_getcpuref.3cpc
+usr/src/man/man3cpc/cpc_getnpic.3cpc
+usr/src/man/man3cpc/cpc_getusage.3cpc
+usr/src/man/man3cpc/cpc_pctx_invalidate.3cpc
+usr/src/man/man3cpc/cpc_pctx_rele.3cpc
+usr/src/man/man3cpc/cpc_pctx_take_sample.3cpc
+usr/src/man/man3cpc/cpc_rele.3cpc
+usr/src/man/man3cpc/cpc_request_preset.3cpc
+usr/src/man/man3cpc/cpc_set_add_request.3cpc
+usr/src/man/man3cpc/cpc_set_destroy.3cpc
+usr/src/man/man3cpc/cpc_set_restart.3cpc
+usr/src/man/man3cpc/cpc_set_sample.3cpc
+usr/src/man/man3cpc/cpc_shared_bind_event.3cpc
+usr/src/man/man3cpc/cpc_shared_close.3cpc
+usr/src/man/man3cpc/cpc_shared_rele.3cpc
+usr/src/man/man3cpc/cpc_shared_take_sample.3cpc
+usr/src/man/man3cpc/cpc_take_sample.3cpc
+usr/src/man/man3cpc/cpc_unbind.3cpc
+usr/src/man/man3cpc/cpc_walk_attrs.3cpc
+usr/src/man/man3cpc/cpc_walk_events_all.3cpc
+usr/src/man/man3cpc/cpc_walk_events_pic.3cpc
+usr/src/man/man3cpc/cpc_walk_generic_events_all.3cpc
+usr/src/man/man3cpc/cpc_walk_generic_events_pic.3cpc
+usr/src/man/man3cpc/cpc_walk_names.3cpc
+usr/src/man/man3cpc/cpc_walk_requests.3cpc
+usr/src/man/man3cpc/pctx_create.3cpc
+usr/src/man/man3cpc/pctx_release.3cpc
+usr/src/man/man3cpc/pctx_run.3cpc
+usr/src/man/man3curses/addch.3curses
+usr/src/man/man3curses/addchnstr.3curses
+usr/src/man/man3curses/addchstr.3curses
+usr/src/man/man3curses/addnstr.3curses
+usr/src/man/man3curses/addnwstr.3curses
+usr/src/man/man3curses/addstr.3curses
+usr/src/man/man3curses/addwch.3curses
+usr/src/man/man3curses/addwchnstr.3curses
+usr/src/man/man3curses/addwchstr.3curses
+usr/src/man/man3curses/addwstr.3curses
+usr/src/man/man3curses/adjcurspos.3curses
+usr/src/man/man3curses/attroff.3curses
+usr/src/man/man3curses/attron.3curses
+usr/src/man/man3curses/attrset.3curses
+usr/src/man/man3curses/baudrate.3curses
+usr/src/man/man3curses/beep.3curses
+usr/src/man/man3curses/bkgd.3curses
+usr/src/man/man3curses/bkgdset.3curses
+usr/src/man/man3curses/border.3curses
+usr/src/man/man3curses/bottom_panel.3curses
+usr/src/man/man3curses/box.3curses
+usr/src/man/man3curses/can_change_color.3curses
+usr/src/man/man3curses/cbreak.3curses
+usr/src/man/man3curses/clear.3curses
+usr/src/man/man3curses/clearok.3curses
+usr/src/man/man3curses/clrtobot.3curses
+usr/src/man/man3curses/clrtoeol.3curses
+usr/src/man/man3curses/color_content.3curses
+usr/src/man/man3curses/copywin.3curses
+usr/src/man/man3curses/current_field.3curses
+usr/src/man/man3curses/current_item.3curses
+usr/src/man/man3curses/curs_set.3curses
+usr/src/man/man3curses/data_ahead.3curses
+usr/src/man/man3curses/data_behind.3curses
+usr/src/man/man3curses/def_prog_mode.3curses
+usr/src/man/man3curses/def_shell_mode.3curses
+usr/src/man/man3curses/del_curterm.3curses
+usr/src/man/man3curses/del_panel.3curses
+usr/src/man/man3curses/delay_output.3curses
+usr/src/man/man3curses/delch.3curses
+usr/src/man/man3curses/deleteln.3curses
+usr/src/man/man3curses/delscreen.3curses
+usr/src/man/man3curses/delwin.3curses
+usr/src/man/man3curses/derwin.3curses
+usr/src/man/man3curses/doupdate.3curses
+usr/src/man/man3curses/dup_field.3curses
+usr/src/man/man3curses/dupwin.3curses
+usr/src/man/man3curses/dynamic_field_info.3curses
+usr/src/man/man3curses/echo.3curses
+usr/src/man/man3curses/echochar.3curses
+usr/src/man/man3curses/echowchar.3curses
+usr/src/man/man3curses/endwin.3curses
+usr/src/man/man3curses/erase.3curses
+usr/src/man/man3curses/erasechar.3curses
+usr/src/man/man3curses/field_arg.3curses
+usr/src/man/man3curses/field_back.3curses
+usr/src/man/man3curses/field_buffer.3curses
+usr/src/man/man3curses/field_count.3curses
+usr/src/man/man3curses/field_fore.3curses
+usr/src/man/man3curses/field_index.3curses
+usr/src/man/man3curses/field_info.3curses
+usr/src/man/man3curses/field_init.3curses
+usr/src/man/man3curses/field_just.3curses
+usr/src/man/man3curses/field_opts.3curses
+usr/src/man/man3curses/field_opts_off.3curses
+usr/src/man/man3curses/field_opts_on.3curses
+usr/src/man/man3curses/field_pad.3curses
+usr/src/man/man3curses/field_status.3curses
+usr/src/man/man3curses/field_term.3curses
+usr/src/man/man3curses/field_type.3curses
+usr/src/man/man3curses/field_userptr.3curses
+usr/src/man/man3curses/filter.3curses
+usr/src/man/man3curses/flash.3curses
+usr/src/man/man3curses/flushinp.3curses
+usr/src/man/man3curses/form_fields.3curses
+usr/src/man/man3curses/form_init.3curses
+usr/src/man/man3curses/form_opts_off.3curses
+usr/src/man/man3curses/form_opts_on.3curses
+usr/src/man/man3curses/form_sub.3curses
+usr/src/man/man3curses/form_term.3curses
+usr/src/man/man3curses/free_field.3curses
+usr/src/man/man3curses/free_fieldtype.3curses
+usr/src/man/man3curses/free_form.3curses
+usr/src/man/man3curses/free_item.3curses
+usr/src/man/man3curses/free_menu.3curses
+usr/src/man/man3curses/getbegyx.3curses
+usr/src/man/man3curses/getch.3curses
+usr/src/man/man3curses/getmaxyx.3curses
+usr/src/man/man3curses/getnwstr.3curses
+usr/src/man/man3curses/getparyx.3curses
+usr/src/man/man3curses/getstr.3curses
+usr/src/man/man3curses/getsyx.3curses
+usr/src/man/man3curses/getwch.3curses
+usr/src/man/man3curses/getwin.3curses
+usr/src/man/man3curses/getwstr.3curses
+usr/src/man/man3curses/getyx.3curses
+usr/src/man/man3curses/halfdelay.3curses
+usr/src/man/man3curses/has_colors.3curses
+usr/src/man/man3curses/has_ic.3curses
+usr/src/man/man3curses/has_il.3curses
+usr/src/man/man3curses/hide_panel.3curses
+usr/src/man/man3curses/idcok.3curses
+usr/src/man/man3curses/idlok.3curses
+usr/src/man/man3curses/immedok.3curses
+usr/src/man/man3curses/inch.3curses
+usr/src/man/man3curses/inchnstr.3curses
+usr/src/man/man3curses/inchstr.3curses
+usr/src/man/man3curses/init_color.3curses
+usr/src/man/man3curses/init_pair.3curses
+usr/src/man/man3curses/initscr.3curses
+usr/src/man/man3curses/innstr.3curses
+usr/src/man/man3curses/innwstr.3curses
+usr/src/man/man3curses/insch.3curses
+usr/src/man/man3curses/insdelln.3curses
+usr/src/man/man3curses/insertln.3curses
+usr/src/man/man3curses/insnstr.3curses
+usr/src/man/man3curses/insnwstr.3curses
+usr/src/man/man3curses/insstr.3curses
+usr/src/man/man3curses/instr.3curses
+usr/src/man/man3curses/inswch.3curses
+usr/src/man/man3curses/inswstr.3curses
+usr/src/man/man3curses/intrflush.3curses
+usr/src/man/man3curses/inwch.3curses
+usr/src/man/man3curses/inwchnstr.3curses
+usr/src/man/man3curses/inwchstr.3curses
+usr/src/man/man3curses/inwstr.3curses
+usr/src/man/man3curses/is_linetouched.3curses
+usr/src/man/man3curses/is_wintouched.3curses
+usr/src/man/man3curses/isendwin.3curses
+usr/src/man/man3curses/item_count.3curses
+usr/src/man/man3curses/item_description.3curses
+usr/src/man/man3curses/item_index.3curses
+usr/src/man/man3curses/item_init.3curses
+usr/src/man/man3curses/item_name.3curses
+usr/src/man/man3curses/item_opts.3curses
+usr/src/man/man3curses/item_opts_off.3curses
+usr/src/man/man3curses/item_opts_on.3curses
+usr/src/man/man3curses/item_term.3curses
+usr/src/man/man3curses/item_userptr.3curses
+usr/src/man/man3curses/item_value.3curses
+usr/src/man/man3curses/item_visible.3curses
+usr/src/man/man3curses/keyname.3curses
+usr/src/man/man3curses/keypad.3curses
+usr/src/man/man3curses/killchar.3curses
+usr/src/man/man3curses/leaveok.3curses
+usr/src/man/man3curses/link_field.3curses
+usr/src/man/man3curses/link_fieldtype.3curses
+usr/src/man/man3curses/longname.3curses
+usr/src/man/man3curses/menu_back.3curses
+usr/src/man/man3curses/menu_fore.3curses
+usr/src/man/man3curses/menu_grey.3curses
+usr/src/man/man3curses/menu_init.3curses
+usr/src/man/man3curses/menu_opts_off.3curses
+usr/src/man/man3curses/menu_opts_on.3curses
+usr/src/man/man3curses/menu_pad.3curses
+usr/src/man/man3curses/menu_sub.3curses
+usr/src/man/man3curses/menu_term.3curses
+usr/src/man/man3curses/meta.3curses
+usr/src/man/man3curses/move.3curses
+usr/src/man/man3curses/move_field.3curses
+usr/src/man/man3curses/move_panel.3curses
+usr/src/man/man3curses/movenextch.3curses
+usr/src/man/man3curses/moveprevch.3curses
+usr/src/man/man3curses/mvaddch.3curses
+usr/src/man/man3curses/mvaddchnstr.3curses
+usr/src/man/man3curses/mvaddchstr.3curses
+usr/src/man/man3curses/mvaddnstr.3curses
+usr/src/man/man3curses/mvaddnwstr.3curses
+usr/src/man/man3curses/mvaddstr.3curses
+usr/src/man/man3curses/mvaddwch.3curses
+usr/src/man/man3curses/mvaddwchnstr.3curses
+usr/src/man/man3curses/mvaddwchstr.3curses
+usr/src/man/man3curses/mvaddwstr.3curses
+usr/src/man/man3curses/mvcur.3curses
+usr/src/man/man3curses/mvdelch.3curses
+usr/src/man/man3curses/mvderwin.3curses
+usr/src/man/man3curses/mvgetch.3curses
+usr/src/man/man3curses/mvgetnwstr.3curses
+usr/src/man/man3curses/mvgetstr.3curses
+usr/src/man/man3curses/mvgetwch.3curses
+usr/src/man/man3curses/mvgetwstr.3curses
+usr/src/man/man3curses/mvinch.3curses
+usr/src/man/man3curses/mvinchnstr.3curses
+usr/src/man/man3curses/mvinchstr.3curses
+usr/src/man/man3curses/mvinnstr.3curses
+usr/src/man/man3curses/mvinnwstr.3curses
+usr/src/man/man3curses/mvinsch.3curses
+usr/src/man/man3curses/mvinsnstr.3curses
+usr/src/man/man3curses/mvinsnwstr.3curses
+usr/src/man/man3curses/mvinsstr.3curses
+usr/src/man/man3curses/mvinstr.3curses
+usr/src/man/man3curses/mvinswch.3curses
+usr/src/man/man3curses/mvinswstr.3curses
+usr/src/man/man3curses/mvinwch.3curses
+usr/src/man/man3curses/mvinwchnstr.3curses
+usr/src/man/man3curses/mvinwchstr.3curses
+usr/src/man/man3curses/mvinwstr.3curses
+usr/src/man/man3curses/mvprintw.3curses
+usr/src/man/man3curses/mvscanw.3curses
+usr/src/man/man3curses/mvwaddch.3curses
+usr/src/man/man3curses/mvwaddchnstr.3curses
+usr/src/man/man3curses/mvwaddchstr.3curses
+usr/src/man/man3curses/mvwaddnstr.3curses
+usr/src/man/man3curses/mvwaddnwstr.3curses
+usr/src/man/man3curses/mvwaddstr.3curses
+usr/src/man/man3curses/mvwaddwch.3curses
+usr/src/man/man3curses/mvwaddwchnstr.3curses
+usr/src/man/man3curses/mvwaddwchstr.3curses
+usr/src/man/man3curses/mvwaddwstr.3curses
+usr/src/man/man3curses/mvwdelch.3curses
+usr/src/man/man3curses/mvwgetch.3curses
+usr/src/man/man3curses/mvwgetnwstr.3curses
+usr/src/man/man3curses/mvwgetstr.3curses
+usr/src/man/man3curses/mvwgetwch.3curses
+usr/src/man/man3curses/mvwgetwstr.3curses
+usr/src/man/man3curses/mvwin.3curses
+usr/src/man/man3curses/mvwinch.3curses
+usr/src/man/man3curses/mvwinchnstr.3curses
+usr/src/man/man3curses/mvwinchstr.3curses
+usr/src/man/man3curses/mvwinnstr.3curses
+usr/src/man/man3curses/mvwinnwstr.3curses
+usr/src/man/man3curses/mvwinsch.3curses
+usr/src/man/man3curses/mvwinsnstr.3curses
+usr/src/man/man3curses/mvwinsnwstr.3curses
+usr/src/man/man3curses/mvwinsstr.3curses
+usr/src/man/man3curses/mvwinstr.3curses
+usr/src/man/man3curses/mvwinswch.3curses
+usr/src/man/man3curses/mvwinswstr.3curses
+usr/src/man/man3curses/mvwinwch.3curses
+usr/src/man/man3curses/mvwinwchnstr.3curses
+usr/src/man/man3curses/mvwinwchstr.3curses
+usr/src/man/man3curses/mvwinwstr.3curses
+usr/src/man/man3curses/mvwprintw.3curses
+usr/src/man/man3curses/mvwscanw.3curses
+usr/src/man/man3curses/napms.3curses
+usr/src/man/man3curses/new_field.3curses
+usr/src/man/man3curses/new_fieldtype.3curses
+usr/src/man/man3curses/new_form.3curses
+usr/src/man/man3curses/new_item.3curses
+usr/src/man/man3curses/new_menu.3curses
+usr/src/man/man3curses/new_page.3curses
+usr/src/man/man3curses/new_panel.3curses
+usr/src/man/man3curses/newpad.3curses
+usr/src/man/man3curses/newterm.3curses
+usr/src/man/man3curses/newwin.3curses
+usr/src/man/man3curses/nl.3curses
+usr/src/man/man3curses/nocbreak.3curses
+usr/src/man/man3curses/nodelay.3curses
+usr/src/man/man3curses/noecho.3curses
+usr/src/man/man3curses/nonl.3curses
+usr/src/man/man3curses/noqiflush.3curses
+usr/src/man/man3curses/noraw.3curses
+usr/src/man/man3curses/notimeout.3curses
+usr/src/man/man3curses/overlay.3curses
+usr/src/man/man3curses/overwrite.3curses
+usr/src/man/man3curses/pair_content.3curses
+usr/src/man/man3curses/panel_below.3curses
+usr/src/man/man3curses/panel_hidden.3curses
+usr/src/man/man3curses/pechochar.3curses
+usr/src/man/man3curses/pechowchar.3curses
+usr/src/man/man3curses/pnoutrefresh.3curses
+usr/src/man/man3curses/pos_form_cursor.3curses
+usr/src/man/man3curses/pos_menu_cursor.3curses
+usr/src/man/man3curses/post_form.3curses
+usr/src/man/man3curses/post_menu.3curses
+usr/src/man/man3curses/prefresh.3curses
+usr/src/man/man3curses/printw.3curses
+usr/src/man/man3curses/putp.3curses
+usr/src/man/man3curses/putwin.3curses
+usr/src/man/man3curses/qiflush.3curses
+usr/src/man/man3curses/raw.3curses
+usr/src/man/man3curses/redrawwin.3curses
+usr/src/man/man3curses/refresh.3curses
+usr/src/man/man3curses/replace_panel.3curses
+usr/src/man/man3curses/reset_prog_mode.3curses
+usr/src/man/man3curses/reset_shell_mode.3curses
+usr/src/man/man3curses/resetty.3curses
+usr/src/man/man3curses/restartterm.3curses
+usr/src/man/man3curses/ripoffline.3curses
+usr/src/man/man3curses/savetty.3curses
+usr/src/man/man3curses/scale_form.3curses
+usr/src/man/man3curses/scale_menu.3curses
+usr/src/man/man3curses/scanw.3curses
+usr/src/man/man3curses/scr_dump.3curses
+usr/src/man/man3curses/scr_init.3curses
+usr/src/man/man3curses/scr_restore.3curses
+usr/src/man/man3curses/scr_set.3curses
+usr/src/man/man3curses/scrl.3curses
+usr/src/man/man3curses/scroll.3curses
+usr/src/man/man3curses/scrollok.3curses
+usr/src/man/man3curses/set_current_field.3curses
+usr/src/man/man3curses/set_current_item.3curses
+usr/src/man/man3curses/set_curterm.3curses
+usr/src/man/man3curses/set_field_back.3curses
+usr/src/man/man3curses/set_field_buffer.3curses
+usr/src/man/man3curses/set_field_fore.3curses
+usr/src/man/man3curses/set_field_init.3curses
+usr/src/man/man3curses/set_field_just.3curses
+usr/src/man/man3curses/set_field_opts.3curses
+usr/src/man/man3curses/set_field_pad.3curses
+usr/src/man/man3curses/set_field_status.3curses
+usr/src/man/man3curses/set_field_term.3curses
+usr/src/man/man3curses/set_field_type.3curses
+usr/src/man/man3curses/set_field_userptr.3curses
+usr/src/man/man3curses/set_fieldtype_arg.3curses
+usr/src/man/man3curses/set_fieldtype_choice.3curses
+usr/src/man/man3curses/set_form_fields.3curses
+usr/src/man/man3curses/set_form_init.3curses
+usr/src/man/man3curses/set_form_opts.3curses
+usr/src/man/man3curses/set_form_page.3curses
+usr/src/man/man3curses/set_form_sub.3curses
+usr/src/man/man3curses/set_form_term.3curses
+usr/src/man/man3curses/set_form_userptr.3curses
+usr/src/man/man3curses/set_form_win.3curses
+usr/src/man/man3curses/set_item_init.3curses
+usr/src/man/man3curses/set_item_opts.3curses
+usr/src/man/man3curses/set_item_term.3curses
+usr/src/man/man3curses/set_item_userptr.3curses
+usr/src/man/man3curses/set_item_value.3curses
+usr/src/man/man3curses/set_max_field.3curses
+usr/src/man/man3curses/set_menu_back.3curses
+usr/src/man/man3curses/set_menu_fore.3curses
+usr/src/man/man3curses/set_menu_format.3curses
+usr/src/man/man3curses/set_menu_grey.3curses
+usr/src/man/man3curses/set_menu_init.3curses
+usr/src/man/man3curses/set_menu_items.3curses
+usr/src/man/man3curses/set_menu_mark.3curses
+usr/src/man/man3curses/set_menu_opts.3curses
+usr/src/man/man3curses/set_menu_pad.3curses
+usr/src/man/man3curses/set_menu_pattern.3curses
+usr/src/man/man3curses/set_menu_sub.3curses
+usr/src/man/man3curses/set_menu_term.3curses
+usr/src/man/man3curses/set_menu_userptr.3curses
+usr/src/man/man3curses/set_menu_win.3curses
+usr/src/man/man3curses/set_new_page.3curses
+usr/src/man/man3curses/set_panel_userptr.3curses
+usr/src/man/man3curses/set_term.3curses
+usr/src/man/man3curses/set_top_row.3curses
+usr/src/man/man3curses/setscrreg.3curses
+usr/src/man/man3curses/setsyx.3curses
+usr/src/man/man3curses/setterm.3curses
+usr/src/man/man3curses/setupterm.3curses
+usr/src/man/man3curses/show_panel.3curses
+usr/src/man/man3curses/slk_attroff.3curses
+usr/src/man/man3curses/slk_attron.3curses
+usr/src/man/man3curses/slk_attrset.3curses
+usr/src/man/man3curses/slk_clear.3curses
+usr/src/man/man3curses/slk_init.3curses
+usr/src/man/man3curses/slk_label.3curses
+usr/src/man/man3curses/slk_noutrefresh.3curses
+usr/src/man/man3curses/slk_refresh.3curses
+usr/src/man/man3curses/slk_restore.3curses
+usr/src/man/man3curses/slk_set.3curses
+usr/src/man/man3curses/slk_touch.3curses
+usr/src/man/man3curses/standend.3curses
+usr/src/man/man3curses/standout.3curses
+usr/src/man/man3curses/start_color.3curses
+usr/src/man/man3curses/subpad.3curses
+usr/src/man/man3curses/subwin.3curses
+usr/src/man/man3curses/syncok.3curses
+usr/src/man/man3curses/termattrs.3curses
+usr/src/man/man3curses/termname.3curses
+usr/src/man/man3curses/tgetent.3curses
+usr/src/man/man3curses/tgetflag.3curses
+usr/src/man/man3curses/tgetnum.3curses
+usr/src/man/man3curses/tgetstr.3curses
+usr/src/man/man3curses/tgoto.3curses
+usr/src/man/man3curses/tigetflag.3curses
+usr/src/man/man3curses/tigetnum.3curses
+usr/src/man/man3curses/tigetstr.3curses
+usr/src/man/man3curses/timeout.3curses
+usr/src/man/man3curses/top_panel.3curses
+usr/src/man/man3curses/top_row.3curses
+usr/src/man/man3curses/touchline.3curses
+usr/src/man/man3curses/touchwin.3curses
+usr/src/man/man3curses/tparm.3curses
+usr/src/man/man3curses/tputs.3curses
+usr/src/man/man3curses/typeahead.3curses
+usr/src/man/man3curses/unctrl.3curses
+usr/src/man/man3curses/ungetch.3curses
+usr/src/man/man3curses/ungetwch.3curses
+usr/src/man/man3curses/unpost_form.3curses
+usr/src/man/man3curses/unpost_menu.3curses
+usr/src/man/man3curses/untouchwin.3curses
+usr/src/man/man3curses/update_panels.3curses
+usr/src/man/man3curses/use_env.3curses
+usr/src/man/man3curses/vidattr.3curses
+usr/src/man/man3curses/vidputs.3curses
+usr/src/man/man3curses/vwprintw.3curses
+usr/src/man/man3curses/vwscanw.3curses
+usr/src/man/man3curses/waddch.3curses
+usr/src/man/man3curses/waddchnstr.3curses
+usr/src/man/man3curses/waddchstr.3curses
+usr/src/man/man3curses/waddnstr.3curses
+usr/src/man/man3curses/waddnwstr.3curses
+usr/src/man/man3curses/waddstr.3curses
+usr/src/man/man3curses/waddwch.3curses
+usr/src/man/man3curses/waddwchnstr.3curses
+usr/src/man/man3curses/waddwchstr.3curses
+usr/src/man/man3curses/waddwstr.3curses
+usr/src/man/man3curses/wadjcurspos.3curses
+usr/src/man/man3curses/wattroff.3curses
+usr/src/man/man3curses/wattron.3curses
+usr/src/man/man3curses/wattrset.3curses
+usr/src/man/man3curses/wbkgd.3curses
+usr/src/man/man3curses/wbkgdset.3curses
+usr/src/man/man3curses/wborder.3curses
+usr/src/man/man3curses/wclear.3curses
+usr/src/man/man3curses/wclrtobot.3curses
+usr/src/man/man3curses/wclrtoeol.3curses
+usr/src/man/man3curses/wcursyncup.3curses
+usr/src/man/man3curses/wdelch.3curses
+usr/src/man/man3curses/wdeleteln.3curses
+usr/src/man/man3curses/wechochar.3curses
+usr/src/man/man3curses/wechowchar.3curses
+usr/src/man/man3curses/werase.3curses
+usr/src/man/man3curses/wgetch.3curses
+usr/src/man/man3curses/wgetnstr.3curses
+usr/src/man/man3curses/wgetnwstr.3curses
+usr/src/man/man3curses/wgetstr.3curses
+usr/src/man/man3curses/wgetwch.3curses
+usr/src/man/man3curses/wgetwstr.3curses
+usr/src/man/man3curses/whline.3curses
+usr/src/man/man3curses/winch.3curses
+usr/src/man/man3curses/winchnstr.3curses
+usr/src/man/man3curses/winchstr.3curses
+usr/src/man/man3curses/winnstr.3curses
+usr/src/man/man3curses/winnwstr.3curses
+usr/src/man/man3curses/winsch.3curses
+usr/src/man/man3curses/winsdelln.3curses
+usr/src/man/man3curses/winsertln.3curses
+usr/src/man/man3curses/winsnstr.3curses
+usr/src/man/man3curses/winsnwstr.3curses
+usr/src/man/man3curses/winsstr.3curses
+usr/src/man/man3curses/winstr.3curses
+usr/src/man/man3curses/winswch.3curses
+usr/src/man/man3curses/winswstr.3curses
+usr/src/man/man3curses/winwch.3curses
+usr/src/man/man3curses/winwchnstr.3curses
+usr/src/man/man3curses/winwchstr.3curses
+usr/src/man/man3curses/winwstr.3curses
+usr/src/man/man3curses/wmove.3curses
+usr/src/man/man3curses/wmovenextch.3curses
+usr/src/man/man3curses/wmoveprevch.3curses
+usr/src/man/man3curses/wnoutrefresh.3curses
+usr/src/man/man3curses/wprintw.3curses
+usr/src/man/man3curses/wredrawln.3curses
+usr/src/man/man3curses/wrefresh.3curses
+usr/src/man/man3curses/wscanw.3curses
+usr/src/man/man3curses/wscrl.3curses
+usr/src/man/man3curses/wsetscrreg.3curses
+usr/src/man/man3curses/wstandend.3curses
+usr/src/man/man3curses/wstandout.3curses
+usr/src/man/man3curses/wsyncdown.3curses
+usr/src/man/man3curses/wsyncup.3curses
+usr/src/man/man3curses/wtimeout.3curses
+usr/src/man/man3curses/wtouchln.3curses
+usr/src/man/man3curses/wvline.3curses
+usr/src/man/man3devid/devid_compare.3devid
+usr/src/man/man3devid/devid_deviceid_to_nmlist.3devid
+usr/src/man/man3devid/devid_free.3devid
+usr/src/man/man3devid/devid_free_nmlist.3devid
+usr/src/man/man3devid/devid_get_minor_name.3devid
+usr/src/man/man3devid/devid_sizeof.3devid
+usr/src/man/man3devid/devid_str_decode.3devid
+usr/src/man/man3devid/devid_str_encode.3devid
+usr/src/man/man3devid/devid_str_free.3devid
+usr/src/man/man3devid/devid_valid.3devid
+usr/src/man/man3devinfo/di_bus_addr.3devinfo
+usr/src/man/man3devinfo/di_compatible_names.3devinfo
+usr/src/man/man3devinfo/di_devfs_minor_path.3devinfo
+usr/src/man/man3devinfo/di_devfs_path_free.3devinfo
+usr/src/man/man3devinfo/di_devid.3devinfo
+usr/src/man/man3devinfo/di_devlink_content.3devinfo
+usr/src/man/man3devinfo/di_devlink_fini.3devinfo
+usr/src/man/man3devinfo/di_devlink_free.3devinfo
+usr/src/man/man3devinfo/di_devlink_type.3devinfo
+usr/src/man/man3devinfo/di_driver_major.3devinfo
+usr/src/man/man3devinfo/di_driver_name.3devinfo
+usr/src/man/man3devinfo/di_driver_ops.3devinfo
+usr/src/man/man3devinfo/di_drv_first_node.3devinfo
+usr/src/man/man3devinfo/di_drv_next_node.3devinfo
+usr/src/man/man3devinfo/di_fini.3devinfo
+usr/src/man/man3devinfo/di_instance.3devinfo
+usr/src/man/man3devinfo/di_link_next_by_lnode.3devinfo
+usr/src/man/man3devinfo/di_link_private_get.3devinfo
+usr/src/man/man3devinfo/di_link_private_set.3devinfo
+usr/src/man/man3devinfo/di_link_to_lnode.3devinfo
+usr/src/man/man3devinfo/di_lnode_devinfo.3devinfo
+usr/src/man/man3devinfo/di_lnode_devt.3devinfo
+usr/src/man/man3devinfo/di_lnode_private_get.3devinfo
+usr/src/man/man3devinfo/di_lnode_private_set.3devinfo
+usr/src/man/man3devinfo/di_minor_name.3devinfo
+usr/src/man/man3devinfo/di_minor_nodetype.3devinfo
+usr/src/man/man3devinfo/di_minor_private_get.3devinfo
+usr/src/man/man3devinfo/di_minor_private_set.3devinfo
+usr/src/man/man3devinfo/di_minor_spectype.3devinfo
+usr/src/man/man3devinfo/di_node_name.3devinfo
+usr/src/man/man3devinfo/di_node_private_get.3devinfo
+usr/src/man/man3devinfo/di_nodeid.3devinfo
+usr/src/man/man3devinfo/di_parent_node.3devinfo
+usr/src/man/man3devinfo/di_path_client_devfs_path.3devinfo
+usr/src/man/man3devinfo/di_path_client_node.3devinfo
+usr/src/man/man3devinfo/di_path_devfs_path.3devinfo
+usr/src/man/man3devinfo/di_path_instance.3devinfo
+usr/src/man/man3devinfo/di_path_node_name.3devinfo
+usr/src/man/man3devinfo/di_path_phci_next_path.3devinfo
+usr/src/man/man3devinfo/di_path_phci_node.3devinfo
+usr/src/man/man3devinfo/di_path_private_get.3devinfo
+usr/src/man/man3devinfo/di_path_private_set.3devinfo
+usr/src/man/man3devinfo/di_path_prop_int64s.3devinfo
+usr/src/man/man3devinfo/di_path_prop_ints.3devinfo
+usr/src/man/man3devinfo/di_path_prop_lookup_int64s.3devinfo
+usr/src/man/man3devinfo/di_path_prop_lookup_ints.3devinfo
+usr/src/man/man3devinfo/di_path_prop_lookup_strings.3devinfo
+usr/src/man/man3devinfo/di_path_prop_name.3devinfo
+usr/src/man/man3devinfo/di_path_prop_strings.3devinfo
+usr/src/man/man3devinfo/di_path_prop_type.3devinfo
+usr/src/man/man3devinfo/di_path_state.3devinfo
+usr/src/man/man3devinfo/di_prom_fini.3devinfo
+usr/src/man/man3devinfo/di_prom_prop_lookup_ints.3devinfo
+usr/src/man/man3devinfo/di_prom_prop_lookup_strings.3devinfo
+usr/src/man/man3devinfo/di_prom_prop_name.3devinfo
+usr/src/man/man3devinfo/di_prom_prop_next.3devinfo
+usr/src/man/man3devinfo/di_prop_devt.3devinfo
+usr/src/man/man3devinfo/di_prop_int64.3devinfo
+usr/src/man/man3devinfo/di_prop_ints.3devinfo
+usr/src/man/man3devinfo/di_prop_lookup_int64.3devinfo
+usr/src/man/man3devinfo/di_prop_lookup_ints.3devinfo
+usr/src/man/man3devinfo/di_prop_lookup_strings.3devinfo
+usr/src/man/man3devinfo/di_prop_name.3devinfo
+usr/src/man/man3devinfo/di_prop_strings.3devinfo
+usr/src/man/man3devinfo/di_prop_type.3devinfo
+usr/src/man/man3devinfo/di_sibling_node.3devinfo
+usr/src/man/man3dlpi/dlpi_disabmulti.3dlpi
+usr/src/man/man3dlpi/dlpi_promiscoff.3dlpi
+usr/src/man/man3dns_sd/DNSServiceAddRecord.3dns_sd
+usr/src/man/man3dns_sd/DNSServiceRegisterRecord.3dns_sd
+usr/src/man/man3dns_sd/DNSServiceRemoveRecord.3dns_sd
+usr/src/man/man3dns_sd/DNSServiceUpdateRecord.3dns_sd
+usr/src/man/man3dns_sd/TXTRecordContainsKey.3dns_sd
+usr/src/man/man3dns_sd/TXTRecordDeallocate.3dns_sd
+usr/src/man/man3dns_sd/TXTRecordGetBytesPtr.3dns_sd
+usr/src/man/man3dns_sd/TXTRecordGetCount.3dns_sd
+usr/src/man/man3dns_sd/TXTRecordGetItemAtIndex.3dns_sd
+usr/src/man/man3dns_sd/TXTRecordGetLength.3dns_sd
+usr/src/man/man3dns_sd/TXTRecordGetValuePtr.3dns_sd
+usr/src/man/man3dns_sd/TXTRecordRemoveValue.3dns_sd
+usr/src/man/man3dns_sd/TXTRecordSetValue.3dns_sd
+usr/src/man/man3elf/elf32_newehdr.3elf
+usr/src/man/man3elf/elf32_newphdr.3elf
+usr/src/man/man3elf/elf32_xlatetom.3elf
+usr/src/man/man3elf/elf64_checksum.3elf
+usr/src/man/man3elf/elf64_fsize.3elf
+usr/src/man/man3elf/elf64_getehdr.3elf
+usr/src/man/man3elf/elf64_getphdr.3elf
+usr/src/man/man3elf/elf64_getshdr.3elf
+usr/src/man/man3elf/elf64_newehdr.3elf
+usr/src/man/man3elf/elf64_newphdr.3elf
+usr/src/man/man3elf/elf64_xlatetof.3elf
+usr/src/man/man3elf/elf64_xlatetom.3elf
+usr/src/man/man3elf/elf_end.3elf
+usr/src/man/man3elf/elf_errno.3elf
+usr/src/man/man3elf/elf_flagehdr.3elf
+usr/src/man/man3elf/elf_flagelf.3elf
+usr/src/man/man3elf/elf_flagphdr.3elf
+usr/src/man/man3elf/elf_flagscn.3elf
+usr/src/man/man3elf/elf_flagshdr.3elf
+usr/src/man/man3elf/elf_getphdrnum.3elf
+usr/src/man/man3elf/elf_getphnum.3elf
+usr/src/man/man3elf/elf_getshdrnum.3elf
+usr/src/man/man3elf/elf_getshdrstrndx.3elf
+usr/src/man/man3elf/elf_getshnum.3elf
+usr/src/man/man3elf/elf_getshstrndx.3elf
+usr/src/man/man3elf/elf_memory.3elf
+usr/src/man/man3elf/elf_ndxscn.3elf
+usr/src/man/man3elf/elf_newdata.3elf
+usr/src/man/man3elf/elf_newscn.3elf
+usr/src/man/man3elf/elf_next.3elf
+usr/src/man/man3elf/elf_nextscn.3elf
+usr/src/man/man3elf/elf_rand.3elf
+usr/src/man/man3elf/elf_rawdata.3elf
+usr/src/man/man3elf/gelf_checksum.3elf
+usr/src/man/man3elf/gelf_fsize.3elf
+usr/src/man/man3elf/gelf_getcap.3elf
+usr/src/man/man3elf/gelf_getclass.3elf
+usr/src/man/man3elf/gelf_getdyn.3elf
+usr/src/man/man3elf/gelf_getehdr.3elf
+usr/src/man/man3elf/gelf_getmove.3elf
+usr/src/man/man3elf/gelf_getphdr.3elf
+usr/src/man/man3elf/gelf_getrel.3elf
+usr/src/man/man3elf/gelf_getrela.3elf
+usr/src/man/man3elf/gelf_getshdr.3elf
+usr/src/man/man3elf/gelf_getsym.3elf
+usr/src/man/man3elf/gelf_getsyminfo.3elf
+usr/src/man/man3elf/gelf_getsymshndx.3elf
+usr/src/man/man3elf/gelf_newehdr.3elf
+usr/src/man/man3elf/gelf_newphdr.3elf
+usr/src/man/man3elf/gelf_update_cap.3elf
+usr/src/man/man3elf/gelf_update_dyn.3elf
+usr/src/man/man3elf/gelf_update_ehdr.3elf
+usr/src/man/man3elf/gelf_update_getmove.3elf
+usr/src/man/man3elf/gelf_update_move.3elf
+usr/src/man/man3elf/gelf_update_phdr.3elf
+usr/src/man/man3elf/gelf_update_rel.3elf
+usr/src/man/man3elf/gelf_update_rela.3elf
+usr/src/man/man3elf/gelf_update_shdr.3elf
+usr/src/man/man3elf/gelf_update_sym.3elf
+usr/src/man/man3elf/gelf_update_syminfo.3elf
+usr/src/man/man3elf/gelf_update_symshndx.3elf
+usr/src/man/man3elf/gelf_xlatetof.3elf
+usr/src/man/man3elf/gelf_xlatetom.3elf
+usr/src/man/man3exacct/ea_alloc.3exacct
+usr/src/man/man3exacct/ea_attach_to_group.3exacct
+usr/src/man/man3exacct/ea_attach_to_object.3exacct
+usr/src/man/man3exacct/ea_close.3exacct
+usr/src/man/man3exacct/ea_copy_object.3exacct
+usr/src/man/man3exacct/ea_copy_object_tree.3exacct
+usr/src/man/man3exacct/ea_free.3exacct
+usr/src/man/man3exacct/ea_free_item.3exacct
+usr/src/man/man3exacct/ea_free_object.3exacct
+usr/src/man/man3exacct/ea_get_creator.3exacct
+usr/src/man/man3exacct/ea_get_hostname.3exacct
+usr/src/man/man3exacct/ea_get_object.3exacct
+usr/src/man/man3exacct/ea_get_object_tree.3exacct
+usr/src/man/man3exacct/ea_match_object_catalog.3exacct
+usr/src/man/man3exacct/ea_next_object.3exacct
+usr/src/man/man3exacct/ea_previous_object.3exacct
+usr/src/man/man3exacct/ea_set_group.3exacct
+usr/src/man/man3exacct/ea_strdup.3exacct
+usr/src/man/man3exacct/ea_strfree.3exacct
+usr/src/man/man3exacct/ea_unpack_object.3exacct
+usr/src/man/man3exacct/ea_write_object.3exacct
+usr/src/man/man3ext/DES_FAILED.3ext
+usr/src/man/man3ext/MD4Final.3ext
+usr/src/man/man3ext/MD4Init.3ext
+usr/src/man/man3ext/MD4Update.3ext
+usr/src/man/man3ext/MD5Final.3ext
+usr/src/man/man3ext/MD5Init.3ext
+usr/src/man/man3ext/MD5Update.3ext
+usr/src/man/man3ext/SHA1Final.3ext
+usr/src/man/man3ext/SHA1Init.3ext
+usr/src/man/man3ext/SHA1Update.3ext
+usr/src/man/man3ext/SHA256Final.3ext
+usr/src/man/man3ext/SHA256Init.3ext
+usr/src/man/man3ext/SHA256Update.3ext
+usr/src/man/man3ext/SHA2Final.3ext
+usr/src/man/man3ext/SHA2Init.3ext
+usr/src/man/man3ext/SHA2Update.3ext
+usr/src/man/man3ext/SHA384Final.3ext
+usr/src/man/man3ext/SHA384Init.3ext
+usr/src/man/man3ext/SHA384Update.3ext
+usr/src/man/man3ext/SHA512Final.3ext
+usr/src/man/man3ext/SHA512Init.3ext
+usr/src/man/man3ext/SHA512Update.3ext
+usr/src/man/man3ext/SUNW_C_KeyToObject.3ext
+usr/src/man/man3ext/_NOTE.3ext
+usr/src/man/man3ext/auto_ef_file.3ext
+usr/src/man/man3ext/auto_ef_free.3ext
+usr/src/man/man3ext/auto_ef_get_encoding.3ext
+usr/src/man/man3ext/auto_ef_get_score.3ext
+usr/src/man/man3ext/auto_ef_str.3ext
+usr/src/man/man3ext/cbc_crypt.3ext
+usr/src/man/man3ext/cplus_demangle.3ext
+usr/src/man/man3ext/crypt_close.3ext
+usr/src/man/man3ext/des_crypt.3ext
+usr/src/man/man3ext/des_encrypt.3ext
+usr/src/man/man3ext/des_setkey.3ext
+usr/src/man/man3ext/des_setparity.3ext
+usr/src/man/man3ext/efi_alloc_and_read.3ext
+usr/src/man/man3ext/efi_free.3ext
+usr/src/man/man3ext/efi_use_whole_disk.3ext
+usr/src/man/man3ext/efi_write.3ext
+usr/src/man/man3ext/encrypt.3ext
+usr/src/man/man3ext/la_activity.3ext
+usr/src/man/man3ext/la_amd64_pltenter.3ext
+usr/src/man/man3ext/la_i86_pltenter.3ext
+usr/src/man/man3ext/la_objfilter.3ext
+usr/src/man/man3ext/la_objopen.3ext
+usr/src/man/man3ext/la_objsearch.3ext
+usr/src/man/man3ext/la_pltexit.3ext
+usr/src/man/man3ext/la_pltexit64.3ext
+usr/src/man/man3ext/la_preinit.3ext
+usr/src/man/man3ext/la_sparcv8_pltenter.3ext
+usr/src/man/man3ext/la_sparcv9_pltenter.3ext
+usr/src/man/man3ext/la_symbind32.3ext
+usr/src/man/man3ext/la_symbind64.3ext
+usr/src/man/man3ext/la_version.3ext
+usr/src/man/man3ext/ld_atexit.3ext
+usr/src/man/man3ext/ld_atexit64.3ext
+usr/src/man/man3ext/ld_file.3ext
+usr/src/man/man3ext/ld_file64.3ext
+usr/src/man/man3ext/ld_input_done.3ext
+usr/src/man/man3ext/ld_input_section.3ext
+usr/src/man/man3ext/ld_input_section64.3ext
+usr/src/man/man3ext/ld_open.3ext
+usr/src/man/man3ext/ld_open64.3ext
+usr/src/man/man3ext/ld_section.3ext
+usr/src/man/man3ext/ld_section64.3ext
+usr/src/man/man3ext/ld_start.3ext
+usr/src/man/man3ext/ld_start64.3ext
+usr/src/man/man3ext/ld_version.3ext
+usr/src/man/man3ext/md5_calc.3ext
+usr/src/man/man3ext/rd_delete.3ext
+usr/src/man/man3ext/rd_errstr.3ext
+usr/src/man/man3ext/rd_event_addr.3ext
+usr/src/man/man3ext/rd_event_enable.3ext
+usr/src/man/man3ext/rd_event_getmsg.3ext
+usr/src/man/man3ext/rd_init.3ext
+usr/src/man/man3ext/rd_loadobj_iter.3ext
+usr/src/man/man3ext/rd_log.3ext
+usr/src/man/man3ext/rd_new.3ext
+usr/src/man/man3ext/rd_objpad_enable.3ext
+usr/src/man/man3ext/rd_plt_resolution.3ext
+usr/src/man/man3ext/rd_reset.3ext
+usr/src/man/man3ext/run_crypt.3ext
+usr/src/man/man3ext/run_setkey.3ext
+usr/src/man/man3ext/setkey.3ext
+usr/src/man/man3ext/tsalarm_set.3ext
+usr/src/man/man3ext/write_vtoc.3ext
+usr/src/man/man3fstyp/fstyp_fini.3fstyp
+usr/src/man/man3fstyp/fstyp_mod_dump.3fstyp
+usr/src/man/man3fstyp/fstyp_mod_fini.3fstyp
+usr/src/man/man3fstyp/fstyp_mod_get_attr.3fstyp
+usr/src/man/man3fstyp/fstyp_mod_ident.3fstyp
+usr/src/man/man3gen/advance.3gen
+usr/src/man/man3gen/compile.3gen
+usr/src/man/man3gen/p2close.3gen
+usr/src/man/man3gen/rmdirp.3gen
+usr/src/man/man3gen/step.3gen
+usr/src/man/man3gen/str.3gen
+usr/src/man/man3gen/strcadd.3gen
+usr/src/man/man3gen/streadd.3gen
+usr/src/man/man3gen/strecpy.3gen
+usr/src/man/man3gen/strrspn.3gen
+usr/src/man/man3gen/strtrns.3gen
+usr/src/man/man3head/acct.3head
+usr/src/man/man3head/aio.3head
+usr/src/man/man3head/ar.3head
+usr/src/man/man3head/archives.3head
+usr/src/man/man3head/assert.3head
+usr/src/man/man3head/complex.3head
+usr/src/man/man3head/cpio.3head
+usr/src/man/man3head/dirent.3head
+usr/src/man/man3head/errno.3head
+usr/src/man/man3head/fcntl.3head
+usr/src/man/man3head/fenv.3head
+usr/src/man/man3head/float.3head
+usr/src/man/man3head/floatingpoint.3head
+usr/src/man/man3head/fmtmsg.3head
+usr/src/man/man3head/fnmatch.3head
+usr/src/man/man3head/ftw.3head
+usr/src/man/man3head/glob.3head
+usr/src/man/man3head/grp.3head
+usr/src/man/man3head/iconv.3head
+usr/src/man/man3head/if.3head
+usr/src/man/man3head/in.3head
+usr/src/man/man3head/inet.3head
+usr/src/man/man3head/inttypes.3head
+usr/src/man/man3head/ipc.3head
+usr/src/man/man3head/iso646.3head
+usr/src/man/man3head/langinfo.3head
+usr/src/man/man3head/libgen.3head
+usr/src/man/man3head/libintl.3head
+usr/src/man/man3head/limits.3head
+usr/src/man/man3head/locale.3head
+usr/src/man/man3head/math.3head
+usr/src/man/man3head/mman.3head
+usr/src/man/man3head/monetary.3head
+usr/src/man/man3head/mqueue.3head
+usr/src/man/man3head/msg.3head
+usr/src/man/man3head/ndbm.3head
+usr/src/man/man3head/netdb.3head
+usr/src/man/man3head/nl_types.3head
+usr/src/man/man3head/poll.3head
+usr/src/man/man3head/pthread.3head
+usr/src/man/man3head/pwd.3head
+usr/src/man/man3head/regex.3head
+usr/src/man/man3head/resource.3head
+usr/src/man/man3head/sched.3head
+usr/src/man/man3head/search.3head
+usr/src/man/man3head/select.3head
+usr/src/man/man3head/sem.3head
+usr/src/man/man3head/semaphore.3head
+usr/src/man/man3head/setjmp.3head
+usr/src/man/man3head/shm.3head
+usr/src/man/man3head/siginfo.3head
+usr/src/man/man3head/signal.3head
+usr/src/man/man3head/socket.3head
+usr/src/man/man3head/spawn.3head
+usr/src/man/man3head/stat.3head
+usr/src/man/man3head/statvfs.3head
+usr/src/man/man3head/stdbool.3head
+usr/src/man/man3head/stddef.3head
+usr/src/man/man3head/stdint.3head
+usr/src/man/man3head/stdio.3head
+usr/src/man/man3head/stdlib.3head
+usr/src/man/man3head/string.3head
+usr/src/man/man3head/strings.3head
+usr/src/man/man3head/stropts.3head
+usr/src/man/man3head/syslog.3head
+usr/src/man/man3head/tar.3head
+usr/src/man/man3head/tcp.3head
+usr/src/man/man3head/termios.3head
+usr/src/man/man3head/tgmath.3head
+usr/src/man/man3head/time.3head
+usr/src/man/man3head/timeb.3head
+usr/src/man/man3head/times.3head
+usr/src/man/man3head/types.3head
+usr/src/man/man3head/types32.3head
+usr/src/man/man3head/ucontext.3head
+usr/src/man/man3head/uio.3head
+usr/src/man/man3head/ulimit.3head
+usr/src/man/man3head/un.3head
+usr/src/man/man3head/unistd.3head
+usr/src/man/man3head/utime.3head
+usr/src/man/man3head/utmpx.3head
+usr/src/man/man3head/utsname.3head
+usr/src/man/man3head/values.3head
+usr/src/man/man3head/wait.3head
+usr/src/man/man3head/wchar.3head
+usr/src/man/man3head/wctype.3head
+usr/src/man/man3head/wordexp.3head
+usr/src/man/man3iscsit/it_config_commit.3iscsit
+usr/src/man/man3iscsit/it_config_free.3iscsit
+usr/src/man/man3iscsit/it_config_setprop.3iscsit
+usr/src/man/man3iscsit/it_ini_delete.3iscsit
+usr/src/man/man3iscsit/it_ini_free.3iscsit
+usr/src/man/man3iscsit/it_ini_setprop.3iscsit
+usr/src/man/man3iscsit/it_portal_delete.3iscsit
+usr/src/man/man3iscsit/it_tgt_delete.3iscsit
+usr/src/man/man3iscsit/it_tgt_free.3iscsit
+usr/src/man/man3iscsit/it_tgt_setprop.3iscsit
+usr/src/man/man3iscsit/it_tpg_delete.3iscsit
+usr/src/man/man3iscsit/it_tpg_free.3iscsit
+usr/src/man/man3iscsit/it_tpgt_create.3iscsit
+usr/src/man/man3iscsit/it_tpgt_delete.3iscsit
+usr/src/man/man3iscsit/it_tpgt_free.3iscsit
+usr/src/man/man3kstat/kstat_close.3kstat
+usr/src/man/man3kstat/kstat_data_lookup.3kstat
+usr/src/man/man3kstat/kstat_write.3kstat
+usr/src/man/man3kvm/kvm_close.3kvm
+usr/src/man/man3kvm/kvm_getcmd.3kvm
+usr/src/man/man3kvm/kvm_getproc.3kvm
+usr/src/man/man3kvm/kvm_kwrite.3kvm
+usr/src/man/man3kvm/kvm_setproc.3kvm
+usr/src/man/man3kvm/kvm_uread.3kvm
+usr/src/man/man3kvm/kvm_uwrite.3kvm
+usr/src/man/man3kvm/kvm_write.3kvm
+usr/src/man/man3ldap/ber_alloc.3ldap
+usr/src/man/man3ldap/ber_alloc_t.3ldap
+usr/src/man/man3ldap/ber_bvdup.3ldap
+usr/src/man/man3ldap/ber_bvecfree.3ldap
+usr/src/man/man3ldap/ber_bvfree.3ldap
+usr/src/man/man3ldap/ber_first_element.3ldap
+usr/src/man/man3ldap/ber_flatten.3ldap
+usr/src/man/man3ldap/ber_free.3ldap
+usr/src/man/man3ldap/ber_get_bitstring.3ldap
+usr/src/man/man3ldap/ber_get_boolean.3ldap
+usr/src/man/man3ldap/ber_get_int.3ldap
+usr/src/man/man3ldap/ber_get_next.3ldap
+usr/src/man/man3ldap/ber_get_null.3ldap
+usr/src/man/man3ldap/ber_get_stringa.3ldap
+usr/src/man/man3ldap/ber_get_stringal.3ldap
+usr/src/man/man3ldap/ber_get_stringb.3ldap
+usr/src/man/man3ldap/ber_init.3ldap
+usr/src/man/man3ldap/ber_next_element.3ldap
+usr/src/man/man3ldap/ber_peek_tag.3ldap
+usr/src/man/man3ldap/ber_printf.3ldap
+usr/src/man/man3ldap/ber_put_bitstring.3ldap
+usr/src/man/man3ldap/ber_put_boolean.3ldap
+usr/src/man/man3ldap/ber_put_int.3ldap
+usr/src/man/man3ldap/ber_put_null.3ldap
+usr/src/man/man3ldap/ber_put_ostring.3ldap
+usr/src/man/man3ldap/ber_put_seq.3ldap
+usr/src/man/man3ldap/ber_put_set.3ldap
+usr/src/man/man3ldap/ber_put_string.3ldap
+usr/src/man/man3ldap/ber_scanf.3ldap
+usr/src/man/man3ldap/ber_skip_tag.3ldap
+usr/src/man/man3ldap/ber_start_seq.3ldap
+usr/src/man/man3ldap/ber_start_set.3ldap
+usr/src/man/man3ldap/ldap_8859_to_t61.3ldap
+usr/src/man/man3ldap/ldap_add_ext.3ldap
+usr/src/man/man3ldap/ldap_add_ext_s.3ldap
+usr/src/man/man3ldap/ldap_add_s.3ldap
+usr/src/man/man3ldap/ldap_bind_s.3ldap
+usr/src/man/man3ldap/ldap_build_filter.3ldap
+usr/src/man/man3ldap/ldap_compare_ext.3ldap
+usr/src/man/man3ldap/ldap_compare_ext_s.3ldap
+usr/src/man/man3ldap/ldap_compare_s.3ldap
+usr/src/man/man3ldap/ldap_controls_free.3ldap
+usr/src/man/man3ldap/ldap_count_entries.3ldap
+usr/src/man/man3ldap/ldap_count_messages.3ldap
+usr/src/man/man3ldap/ldap_count_references.3ldap
+usr/src/man/man3ldap/ldap_count_values.3ldap
+usr/src/man/man3ldap/ldap_count_values_len.3ldap
+usr/src/man/man3ldap/ldap_delete_ext.3ldap
+usr/src/man/man3ldap/ldap_delete_ext_s.3ldap
+usr/src/man/man3ldap/ldap_delete_s.3ldap
+usr/src/man/man3ldap/ldap_dn2ufn.3ldap
+usr/src/man/man3ldap/ldap_dn_to_url.3ldap
+usr/src/man/man3ldap/ldap_dns_to_dn.3ldap
+usr/src/man/man3ldap/ldap_dns_to_url.3ldap
+usr/src/man/man3ldap/ldap_enable_translation.3ldap
+usr/src/man/man3ldap/ldap_entry2html.3ldap
+usr/src/man/man3ldap/ldap_entry2html_search.3ldap
+usr/src/man/man3ldap/ldap_entry2text_search.3ldap
+usr/src/man/man3ldap/ldap_err2string.3ldap
+usr/src/man/man3ldap/ldap_explode_dn.3ldap
+usr/src/man/man3ldap/ldap_explode_dns.3ldap
+usr/src/man/man3ldap/ldap_first_disptmpl.3ldap
+usr/src/man/man3ldap/ldap_first_reference.3ldap
+usr/src/man/man3ldap/ldap_first_searchobj.3ldap
+usr/src/man/man3ldap/ldap_first_tmplcol.3ldap
+usr/src/man/man3ldap/ldap_first_tmplrow.3ldap
+usr/src/man/man3ldap/ldap_free_friendlymap.3ldap
+usr/src/man/man3ldap/ldap_free_searchprefs.3ldap
+usr/src/man/man3ldap/ldap_free_templates.3ldap
+usr/src/man/man3ldap/ldap_free_urldesc.3ldap
+usr/src/man/man3ldap/ldap_friendly_name.3ldap
+usr/src/man/man3ldap/ldap_get_lang_values_len.3ldap
+usr/src/man/man3ldap/ldap_get_values_len.3ldap
+usr/src/man/man3ldap/ldap_getfilter_free.3ldap
+usr/src/man/man3ldap/ldap_getfirstfilter.3ldap
+usr/src/man/man3ldap/ldap_getnextfilter.3ldap
+usr/src/man/man3ldap/ldap_init.3ldap
+usr/src/man/man3ldap/ldap_init_getfilter.3ldap
+usr/src/man/man3ldap/ldap_init_getfilter_buf.3ldap
+usr/src/man/man3ldap/ldap_init_searchprefs.3ldap
+usr/src/man/man3ldap/ldap_init_searchprefs_buf.3ldap
+usr/src/man/man3ldap/ldap_init_templates.3ldap
+usr/src/man/man3ldap/ldap_init_templates_buf.3ldap
+usr/src/man/man3ldap/ldap_is_dns_dn.3ldap
+usr/src/man/man3ldap/ldap_is_ldap_url.3ldap
+usr/src/man/man3ldap/ldap_memcache_destroy.3ldap
+usr/src/man/man3ldap/ldap_memcache_flush.3ldap
+usr/src/man/man3ldap/ldap_memcache_get.3ldap
+usr/src/man/man3ldap/ldap_memcache_init.3ldap
+usr/src/man/man3ldap/ldap_memcache_set.3ldap
+usr/src/man/man3ldap/ldap_memcache_update.3ldap
+usr/src/man/man3ldap/ldap_modify_ext.3ldap
+usr/src/man/man3ldap/ldap_modify_ext_s.3ldap
+usr/src/man/man3ldap/ldap_modify_s.3ldap
+usr/src/man/man3ldap/ldap_modrdn2.3ldap
+usr/src/man/man3ldap/ldap_modrdn2_s.3ldap
+usr/src/man/man3ldap/ldap_modrdn_s.3ldap
+usr/src/man/man3ldap/ldap_mods_free.3ldap
+usr/src/man/man3ldap/ldap_msgfree.3ldap
+usr/src/man/man3ldap/ldap_msgtype.3ldap
+usr/src/man/man3ldap/ldap_name2template.3ldap
+usr/src/man/man3ldap/ldap_next_attribute.3ldap
+usr/src/man/man3ldap/ldap_next_disptmpl.3ldap
+usr/src/man/man3ldap/ldap_next_entry.3ldap
+usr/src/man/man3ldap/ldap_next_message.3ldap
+usr/src/man/man3ldap/ldap_next_reference.3ldap
+usr/src/man/man3ldap/ldap_next_searchobj.3ldap
+usr/src/man/man3ldap/ldap_next_tmplcol.3ldap
+usr/src/man/man3ldap/ldap_next_tmplrow.3ldap
+usr/src/man/man3ldap/ldap_oc2template.3ldap
+usr/src/man/man3ldap/ldap_parse_extended_result.3ldap
+usr/src/man/man3ldap/ldap_parse_sasl_bind_result.3ldap
+usr/src/man/man3ldap/ldap_perror.3ldap
+usr/src/man/man3ldap/ldap_rename.3ldap
+usr/src/man/man3ldap/ldap_rename_s.3ldap
+usr/src/man/man3ldap/ldap_result2error.3ldap
+usr/src/man/man3ldap/ldap_sasl_bind.3ldap
+usr/src/man/man3ldap/ldap_sasl_bind_s.3ldap
+usr/src/man/man3ldap/ldap_sasl_interactive_bind_s.3ldap
+usr/src/man/man3ldap/ldap_search_ext.3ldap
+usr/src/man/man3ldap/ldap_search_ext_s.3ldap
+usr/src/man/man3ldap/ldap_search_s.3ldap
+usr/src/man/man3ldap/ldap_search_st.3ldap
+usr/src/man/man3ldap/ldap_set_option.3ldap
+usr/src/man/man3ldap/ldap_set_rebind_proc.3ldap
+usr/src/man/man3ldap/ldap_set_string_translators.3ldap
+usr/src/man/man3ldap/ldap_setfilteraffixes.3ldap
+usr/src/man/man3ldap/ldap_simple_bind.3ldap
+usr/src/man/man3ldap/ldap_simple_bind_s.3ldap
+usr/src/man/man3ldap/ldap_sort_entries.3ldap
+usr/src/man/man3ldap/ldap_sort_strcasecmp.3ldap
+usr/src/man/man3ldap/ldap_sort_values.3ldap
+usr/src/man/man3ldap/ldap_t61_to_8859.3ldap
+usr/src/man/man3ldap/ldap_tmplattrs.3ldap
+usr/src/man/man3ldap/ldap_translate_from_t61.3ldap
+usr/src/man/man3ldap/ldap_translate_to_t61.3ldap
+usr/src/man/man3ldap/ldap_ufn_search_c.3ldap
+usr/src/man/man3ldap/ldap_ufn_search_ct.3ldap
+usr/src/man/man3ldap/ldap_ufn_search_s.3ldap
+usr/src/man/man3ldap/ldap_ufn_setfilter.3ldap
+usr/src/man/man3ldap/ldap_ufn_setprefix.3ldap
+usr/src/man/man3ldap/ldap_ufn_timeout.3ldap
+usr/src/man/man3ldap/ldap_unbind.3ldap
+usr/src/man/man3ldap/ldap_unbind_ext.3ldap
+usr/src/man/man3ldap/ldap_unbind_s.3ldap
+usr/src/man/man3ldap/ldap_url_parse.3ldap
+usr/src/man/man3ldap/ldap_url_parse_nodn.3ldap
+usr/src/man/man3ldap/ldap_url_search.3ldap
+usr/src/man/man3ldap/ldap_url_search_s.3ldap
+usr/src/man/man3ldap/ldap_url_search_st.3ldap
+usr/src/man/man3ldap/ldap_vals2html.3ldap
+usr/src/man/man3ldap/ldap_vals2text.3ldap
+usr/src/man/man3ldap/ldap_value_free.3ldap
+usr/src/man/man3ldap/ldap_value_free_len.3ldap
+usr/src/man/man3lgrp/lgrp_affinity_set.3lgrp
+usr/src/man/man3lgrp/lgrp_latency_cookie.3lgrp
+usr/src/man/man3lib/SMHBA_GetAdapterAttributes.3lib
+usr/src/man/man3lib/SMHBA_GetAdapterPortAttributes.3lib
+usr/src/man/man3lib/SMHBA_GetBindingCapability.3lib
+usr/src/man/man3lib/SMHBA_GetBindingSupport.3lib
+usr/src/man/man3lib/SMHBA_GetDiscoveredPortAttributes.3lib
+usr/src/man/man3lib/SMHBA_GetFCPhyAttributes.3lib
+usr/src/man/man3lib/SMHBA_GetLUNStatistics.3lib
+usr/src/man/man3lib/SMHBA_GetNumberofPorts.3lib
+usr/src/man/man3lib/SMHBA_GetPersistentBinding.3lib
+usr/src/man/man3lib/SMHBA_GetPhyStatistics.3lib
+usr/src/man/man3lib/SMHBA_GetPortAttributesByWWN.3lib
+usr/src/man/man3lib/SMHBA_GetPortType.3lib
+usr/src/man/man3lib/SMHBA_GetProtocolStatistics.3lib
+usr/src/man/man3lib/SMHBA_GetSASPhyAttributes.3lib
+usr/src/man/man3lib/SMHBA_GetTargetMapping.3lib
+usr/src/man/man3lib/SMHBA_GetVendorLibraryAttributes.3lib
+usr/src/man/man3lib/SMHBA_GetVersion.3lib
+usr/src/man/man3lib/SMHBA_GetWrapperLibraryAttributes.3lib
+usr/src/man/man3lib/SMHBA_RegisterForAdapterAddEvents.3lib
+usr/src/man/man3lib/SMHBA_RegisterForAdapterEvents.3lib
+usr/src/man/man3lib/SMHBA_RegisterForAdapterPhyStatEvents.3lib
+usr/src/man/man3lib/SMHBA_RegisterForAdapterPortEvents.3lib
+usr/src/man/man3lib/SMHBA_RegisterForAdapterPortStatEvents.3lib
+usr/src/man/man3lib/SMHBA_RegisterForTargetEvents.3lib
+usr/src/man/man3lib/SMHBA_RegisterLibrary.3lib
+usr/src/man/man3lib/SMHBA_RemoveAllPersistentBindings.3lib
+usr/src/man/man3lib/SMHBA_RemovePersistentBinding.3lib
+usr/src/man/man3lib/SMHBA_ScsiInquiry.3lib
+usr/src/man/man3lib/SMHBA_ScsiReadCapacity.3lib
+usr/src/man/man3lib/SMHBA_ScsiReportLuns.3lib
+usr/src/man/man3lib/SMHBA_SendECHO.3lib
+usr/src/man/man3lib/SMHBA_SendSMPPassThru.3lib
+usr/src/man/man3lib/SMHBA_SendTEST.3lib
+usr/src/man/man3lib/SMHBA_SetBindingSupport.3lib
+usr/src/man/man3lib/SMHBA_SetPersistentBinding.3lib
+usr/src/man/man3lib/libmpapi.3lib
+usr/src/man/man3lib/libposix4.3lib
+usr/src/man/man3lib/libsmhbaapi.3lib
+usr/src/man/man3lib/libtermcap.3lib
+usr/src/man/man3lib/libtermlib.3lib
+usr/src/man/man3mail/mailunlock.3mail
+usr/src/man/man3mail/touchlock.3mail
+usr/src/man/man3malloc/calloc.3malloc
+usr/src/man/man3malloc/free.3malloc
+usr/src/man/man3malloc/mallinfo.3malloc
+usr/src/man/man3malloc/mallocctl.3malloc
+usr/src/man/man3malloc/mallopt.3malloc
+usr/src/man/man3malloc/memalign.3malloc
+usr/src/man/man3malloc/realloc.3malloc
+usr/src/man/man3malloc/umem_cache_alloc.3malloc
+usr/src/man/man3malloc/umem_cache_destroy.3malloc
+usr/src/man/man3malloc/umem_cache_free.3malloc
+usr/src/man/man3malloc/umem_free.3malloc
+usr/src/man/man3malloc/umem_nofail_callback.3malloc
+usr/src/man/man3malloc/umem_zalloc.3malloc
+usr/src/man/man3malloc/valloc.3malloc
+usr/src/man/man3mp/mp_gcd.3mp
+usr/src/man/man3mp/mp_itom.3mp
+usr/src/man/man3mp/mp_madd.3mp
+usr/src/man/man3mp/mp_mcmp.3mp
+usr/src/man/man3mp/mp_mdiv.3mp
+usr/src/man/man3mp/mp_mfree.3mp
+usr/src/man/man3mp/mp_min.3mp
+usr/src/man/man3mp/mp_mout.3mp
+usr/src/man/man3mp/mp_msqrt.3mp
+usr/src/man/man3mp/mp_msub.3mp
+usr/src/man/man3mp/mp_mtox.3mp
+usr/src/man/man3mp/mp_mult.3mp
+usr/src/man/man3mp/mp_pow.3mp
+usr/src/man/man3mp/mp_rpow.3mp
+usr/src/man/man3mp/mp_sdiv.3mp
+usr/src/man/man3mp/mp_xtom.3mp
+usr/src/man/man3nsl/auth_destroy.3nsl
+usr/src/man/man3nsl/authdes_create.3nsl
+usr/src/man/man3nsl/authdes_getucred.3nsl
+usr/src/man/man3nsl/authdes_seccreate.3nsl
+usr/src/man/man3nsl/authnone_create.3nsl
+usr/src/man/man3nsl/authsys_create.3nsl
+usr/src/man/man3nsl/authsys_create_default.3nsl
+usr/src/man/man3nsl/authunix_create.3nsl
+usr/src/man/man3nsl/authunix_create_default.3nsl
+usr/src/man/man3nsl/callrpc.3nsl
+usr/src/man/man3nsl/clnt_broadcast.3nsl
+usr/src/man/man3nsl/clnt_call.3nsl
+usr/src/man/man3nsl/clnt_control.3nsl
+usr/src/man/man3nsl/clnt_create.3nsl
+usr/src/man/man3nsl/clnt_create_timed.3nsl
+usr/src/man/man3nsl/clnt_create_vers.3nsl
+usr/src/man/man3nsl/clnt_create_vers_timed.3nsl
+usr/src/man/man3nsl/clnt_destroy.3nsl
+usr/src/man/man3nsl/clnt_dg_create.3nsl
+usr/src/man/man3nsl/clnt_door_create.3nsl
+usr/src/man/man3nsl/clnt_freeres.3nsl
+usr/src/man/man3nsl/clnt_geterr.3nsl
+usr/src/man/man3nsl/clnt_pcreateerror.3nsl
+usr/src/man/man3nsl/clnt_perrno.3nsl
+usr/src/man/man3nsl/clnt_perror.3nsl
+usr/src/man/man3nsl/clnt_raw_create.3nsl
+usr/src/man/man3nsl/clnt_send.3nsl
+usr/src/man/man3nsl/clnt_spcreateerror.3nsl
+usr/src/man/man3nsl/clnt_sperrno.3nsl
+usr/src/man/man3nsl/clnt_sperror.3nsl
+usr/src/man/man3nsl/clnt_tli_create.3nsl
+usr/src/man/man3nsl/clnt_tp_create.3nsl
+usr/src/man/man3nsl/clnt_tp_create_timed.3nsl
+usr/src/man/man3nsl/clnt_vc_create.3nsl
+usr/src/man/man3nsl/clntraw_create.3nsl
+usr/src/man/man3nsl/clnttcp_create.3nsl
+usr/src/man/man3nsl/clntudp_bufcreate.3nsl
+usr/src/man/man3nsl/clntudp_create.3nsl
+usr/src/man/man3nsl/endhostent.3nsl
+usr/src/man/man3nsl/endnetconfig.3nsl
+usr/src/man/man3nsl/endnetpath.3nsl
+usr/src/man/man3nsl/endrpcent.3nsl
+usr/src/man/man3nsl/freeipsecalgent.3nsl
+usr/src/man/man3nsl/freenetconfigent.3nsl
+usr/src/man/man3nsl/get_myaddress.3nsl
+usr/src/man/man3nsl/gethostbyaddr.3nsl
+usr/src/man/man3nsl/gethostbyaddr_r.3nsl
+usr/src/man/man3nsl/gethostbyname_r.3nsl
+usr/src/man/man3nsl/gethostent.3nsl
+usr/src/man/man3nsl/gethostent_r.3nsl
+usr/src/man/man3nsl/getipsecalgbynum.3nsl
+usr/src/man/man3nsl/getipsecprotobynum.3nsl
+usr/src/man/man3nsl/getnetconfigent.3nsl
+usr/src/man/man3nsl/getnetname.3nsl
+usr/src/man/man3nsl/getrpcbyname_r.3nsl
+usr/src/man/man3nsl/getrpcbynumber.3nsl
+usr/src/man/man3nsl/getrpcbynumber_r.3nsl
+usr/src/man/man3nsl/getrpcent.3nsl
+usr/src/man/man3nsl/getrpcent_r.3nsl
+usr/src/man/man3nsl/getrpcport.3nsl
+usr/src/man/man3nsl/getsecretkey.3nsl
+usr/src/man/man3nsl/host2netname.3nsl
+usr/src/man/man3nsl/key_decryptsession.3nsl
+usr/src/man/man3nsl/key_encryptsession.3nsl
+usr/src/man/man3nsl/key_gendes.3nsl
+usr/src/man/man3nsl/key_secretkey_is_set.3nsl
+usr/src/man/man3nsl/key_setsecret.3nsl
+usr/src/man/man3nsl/nc_perror.3nsl
+usr/src/man/man3nsl/nc_sperror.3nsl
+usr/src/man/man3nsl/netdir_free.3nsl
+usr/src/man/man3nsl/netdir_getbyaddr.3nsl
+usr/src/man/man3nsl/netdir_getbyname.3nsl
+usr/src/man/man3nsl/netdir_mergeaddr.3nsl
+usr/src/man/man3nsl/netdir_options.3nsl
+usr/src/man/man3nsl/netdir_perror.3nsl
+usr/src/man/man3nsl/netdir_sperror.3nsl
+usr/src/man/man3nsl/netname2host.3nsl
+usr/src/man/man3nsl/netname2user.3nsl
+usr/src/man/man3nsl/pmap_getmaps.3nsl
+usr/src/man/man3nsl/pmap_getport.3nsl
+usr/src/man/man3nsl/pmap_rmtcall.3nsl
+usr/src/man/man3nsl/pmap_set.3nsl
+usr/src/man/man3nsl/pmap_unset.3nsl
+usr/src/man/man3nsl/publickey.3nsl
+usr/src/man/man3nsl/registerrpc.3nsl
+usr/src/man/man3nsl/rpc_broadcast.3nsl
+usr/src/man/man3nsl/rpc_broadcast_exp.3nsl
+usr/src/man/man3nsl/rpc_call.3nsl
+usr/src/man/man3nsl/rpc_createerr.3nsl
+usr/src/man/man3nsl/rpc_gss_get_mech_info.3nsl
+usr/src/man/man3nsl/rpc_gss_get_versions.3nsl
+usr/src/man/man3nsl/rpc_gss_is_installed.3nsl
+usr/src/man/man3nsl/rpc_gss_qop_to_num.3nsl
+usr/src/man/man3nsl/rpc_gss_svc_max_data_length.3nsl
+usr/src/man/man3nsl/rpc_reg.3nsl
+usr/src/man/man3nsl/rpcb_getaddr.3nsl
+usr/src/man/man3nsl/rpcb_getmaps.3nsl
+usr/src/man/man3nsl/rpcb_gettime.3nsl
+usr/src/man/man3nsl/rpcb_rmtcall.3nsl
+usr/src/man/man3nsl/rpcb_set.3nsl
+usr/src/man/man3nsl/rpcb_unset.3nsl
+usr/src/man/man3nsl/sethostent.3nsl
+usr/src/man/man3nsl/setnetconfig.3nsl
+usr/src/man/man3nsl/setnetpath.3nsl
+usr/src/man/man3nsl/setrpcent.3nsl
+usr/src/man/man3nsl/svc_add_input.3nsl
+usr/src/man/man3nsl/svc_auth_reg.3nsl
+usr/src/man/man3nsl/svc_control.3nsl
+usr/src/man/man3nsl/svc_create.3nsl
+usr/src/man/man3nsl/svc_destroy.3nsl
+usr/src/man/man3nsl/svc_dg_create.3nsl
+usr/src/man/man3nsl/svc_dg_enablecache.3nsl
+usr/src/man/man3nsl/svc_done.3nsl
+usr/src/man/man3nsl/svc_door_create.3nsl
+usr/src/man/man3nsl/svc_exit.3nsl
+usr/src/man/man3nsl/svc_fd_create.3nsl
+usr/src/man/man3nsl/svc_fd_negotiate_ucred.3nsl
+usr/src/man/man3nsl/svc_fds.3nsl
+usr/src/man/man3nsl/svc_fdset.3nsl
+usr/src/man/man3nsl/svc_freeargs.3nsl
+usr/src/man/man3nsl/svc_getargs.3nsl
+usr/src/man/man3nsl/svc_getcaller.3nsl
+usr/src/man/man3nsl/svc_getcallerucred.3nsl
+usr/src/man/man3nsl/svc_getreq.3nsl
+usr/src/man/man3nsl/svc_getreq_common.3nsl
+usr/src/man/man3nsl/svc_getreq_poll.3nsl
+usr/src/man/man3nsl/svc_getreqset.3nsl
+usr/src/man/man3nsl/svc_getrpccaller.3nsl
+usr/src/man/man3nsl/svc_max_pollfd.3nsl
+usr/src/man/man3nsl/svc_pollfd.3nsl
+usr/src/man/man3nsl/svc_raw_create.3nsl
+usr/src/man/man3nsl/svc_reg.3nsl
+usr/src/man/man3nsl/svc_register.3nsl
+usr/src/man/man3nsl/svc_remove_input.3nsl
+usr/src/man/man3nsl/svc_run.3nsl
+usr/src/man/man3nsl/svc_sendreply.3nsl
+usr/src/man/man3nsl/svc_tli_create.3nsl
+usr/src/man/man3nsl/svc_tp_create.3nsl
+usr/src/man/man3nsl/svc_unreg.3nsl
+usr/src/man/man3nsl/svc_unregister.3nsl
+usr/src/man/man3nsl/svc_vc_create.3nsl
+usr/src/man/man3nsl/svcerr_auth.3nsl
+usr/src/man/man3nsl/svcerr_decode.3nsl
+usr/src/man/man3nsl/svcerr_noproc.3nsl
+usr/src/man/man3nsl/svcerr_noprog.3nsl
+usr/src/man/man3nsl/svcerr_progvers.3nsl
+usr/src/man/man3nsl/svcerr_systemerr.3nsl
+usr/src/man/man3nsl/svcerr_weakauth.3nsl
+usr/src/man/man3nsl/svcfd_create.3nsl
+usr/src/man/man3nsl/svcraw_create.3nsl
+usr/src/man/man3nsl/svctcp_create.3nsl
+usr/src/man/man3nsl/svcudp_bufcreate.3nsl
+usr/src/man/man3nsl/svcudp_create.3nsl
+usr/src/man/man3nsl/taddr2uaddr.3nsl
+usr/src/man/man3nsl/uaddr2taddr.3nsl
+usr/src/man/man3nsl/undial.3nsl
+usr/src/man/man3nsl/user2netname.3nsl
+usr/src/man/man3nsl/xdr_accepted_reply.3nsl
+usr/src/man/man3nsl/xdr_array.3nsl
+usr/src/man/man3nsl/xdr_authsys_parms.3nsl
+usr/src/man/man3nsl/xdr_authunix_parms.3nsl
+usr/src/man/man3nsl/xdr_bool.3nsl
+usr/src/man/man3nsl/xdr_bytes.3nsl
+usr/src/man/man3nsl/xdr_callhdr.3nsl
+usr/src/man/man3nsl/xdr_callmsg.3nsl
+usr/src/man/man3nsl/xdr_char.3nsl
+usr/src/man/man3nsl/xdr_control.3nsl
+usr/src/man/man3nsl/xdr_destroy.3nsl
+usr/src/man/man3nsl/xdr_double.3nsl
+usr/src/man/man3nsl/xdr_enum.3nsl
+usr/src/man/man3nsl/xdr_float.3nsl
+usr/src/man/man3nsl/xdr_free.3nsl
+usr/src/man/man3nsl/xdr_getpos.3nsl
+usr/src/man/man3nsl/xdr_hyper.3nsl
+usr/src/man/man3nsl/xdr_inline.3nsl
+usr/src/man/man3nsl/xdr_int.3nsl
+usr/src/man/man3nsl/xdr_long.3nsl
+usr/src/man/man3nsl/xdr_longlong_t.3nsl
+usr/src/man/man3nsl/xdr_opaque.3nsl
+usr/src/man/man3nsl/xdr_opaque_auth.3nsl
+usr/src/man/man3nsl/xdr_pointer.3nsl
+usr/src/man/man3nsl/xdr_quadruple.3nsl
+usr/src/man/man3nsl/xdr_reference.3nsl
+usr/src/man/man3nsl/xdr_rejected_reply.3nsl
+usr/src/man/man3nsl/xdr_replymsg.3nsl
+usr/src/man/man3nsl/xdr_setpos.3nsl
+usr/src/man/man3nsl/xdr_short.3nsl
+usr/src/man/man3nsl/xdr_sizeof.3nsl
+usr/src/man/man3nsl/xdr_string.3nsl
+usr/src/man/man3nsl/xdr_u_char.3nsl
+usr/src/man/man3nsl/xdr_u_hyper.3nsl
+usr/src/man/man3nsl/xdr_u_int.3nsl
+usr/src/man/man3nsl/xdr_u_long.3nsl
+usr/src/man/man3nsl/xdr_u_longlong_t.3nsl
+usr/src/man/man3nsl/xdr_u_short.3nsl
+usr/src/man/man3nsl/xdr_union.3nsl
+usr/src/man/man3nsl/xdr_vector.3nsl
+usr/src/man/man3nsl/xdr_void.3nsl
+usr/src/man/man3nsl/xdr_wrapstring.3nsl
+usr/src/man/man3nsl/xdrmem_create.3nsl
+usr/src/man/man3nsl/xdrrec_create.3nsl
+usr/src/man/man3nsl/xdrrec_endofrecord.3nsl
+usr/src/man/man3nsl/xdrrec_eof.3nsl
+usr/src/man/man3nsl/xdrrec_readbytes.3nsl
+usr/src/man/man3nsl/xdrrec_skiprecord.3nsl
+usr/src/man/man3nsl/xdrstdio_create.3nsl
+usr/src/man/man3nsl/xprt_register.3nsl
+usr/src/man/man3nsl/xprt_unregister.3nsl
+usr/src/man/man3nsl/yp_all.3nsl
+usr/src/man/man3nsl/yp_bind.3nsl
+usr/src/man/man3nsl/yp_first.3nsl
+usr/src/man/man3nsl/yp_get_default_domain.3nsl
+usr/src/man/man3nsl/yp_master.3nsl
+usr/src/man/man3nsl/yp_match.3nsl
+usr/src/man/man3nsl/yp_next.3nsl
+usr/src/man/man3nsl/yp_order.3nsl
+usr/src/man/man3nsl/yp_unbind.3nsl
+usr/src/man/man3nsl/yperr_string.3nsl
+usr/src/man/man3nsl/ypprot_err.3nsl
+usr/src/man/man3nvpair/nv_alloc_fini.3nvpair
+usr/src/man/man3nvpair/nv_alloc_init.3nvpair
+usr/src/man/man3nvpair/nv_alloc_reset.3nvpair
+usr/src/man/man3nvpair/nvlist_add_boolean_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_boolean_value.3nvpair
+usr/src/man/man3nvpair/nvlist_add_byte.3nvpair
+usr/src/man/man3nvpair/nvlist_add_byte_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_double.3nvpair
+usr/src/man/man3nvpair/nvlist_add_int16.3nvpair
+usr/src/man/man3nvpair/nvlist_add_int16_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_int32.3nvpair
+usr/src/man/man3nvpair/nvlist_add_int32_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_int64.3nvpair
+usr/src/man/man3nvpair/nvlist_add_int64_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_int8.3nvpair
+usr/src/man/man3nvpair/nvlist_add_int8_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_nvlist.3nvpair
+usr/src/man/man3nvpair/nvlist_add_nvlist_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_nvpair.3nvpair
+usr/src/man/man3nvpair/nvlist_add_string.3nvpair
+usr/src/man/man3nvpair/nvlist_add_string_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_uint16.3nvpair
+usr/src/man/man3nvpair/nvlist_add_uint16_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_uint32.3nvpair
+usr/src/man/man3nvpair/nvlist_add_uint32_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_uint64.3nvpair
+usr/src/man/man3nvpair/nvlist_add_uint64_array.3nvpair
+usr/src/man/man3nvpair/nvlist_add_uint8.3nvpair
+usr/src/man/man3nvpair/nvlist_add_uint8_array.3nvpair
+usr/src/man/man3nvpair/nvlist_dup.3nvpair
+usr/src/man/man3nvpair/nvlist_exists.3nvpair
+usr/src/man/man3nvpair/nvlist_free.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_boolean_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_boolean_value.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_byte.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_byte_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_double.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_int16.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_int16_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_int32.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_int32_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_int64.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_int64_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_int8.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_int8_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_nv_alloc.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_nvlist.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_nvlist_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_pairs.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_string.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_string_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_uint16.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_uint16_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_uint32.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_uint32_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_uint64.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_uint64_array.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_uint8.3nvpair
+usr/src/man/man3nvpair/nvlist_lookup_uint8_array.3nvpair
+usr/src/man/man3nvpair/nvlist_merge.3nvpair
+usr/src/man/man3nvpair/nvlist_pack.3nvpair
+usr/src/man/man3nvpair/nvlist_remove_all.3nvpair
+usr/src/man/man3nvpair/nvlist_size.3nvpair
+usr/src/man/man3nvpair/nvlist_unpack.3nvpair
+usr/src/man/man3nvpair/nvlist_xalloc.3nvpair
+usr/src/man/man3nvpair/nvlist_xdup.3nvpair
+usr/src/man/man3nvpair/nvlist_xpack.3nvpair
+usr/src/man/man3nvpair/nvlist_xunpack.3nvpair
+usr/src/man/man3nvpair/nvpair_name.3nvpair
+usr/src/man/man3nvpair/nvpair_type.3nvpair
+usr/src/man/man3nvpair/nvpair_value_boolean_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_boolean_value.3nvpair
+usr/src/man/man3nvpair/nvpair_value_byte_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_double.3nvpair
+usr/src/man/man3nvpair/nvpair_value_int16.3nvpair
+usr/src/man/man3nvpair/nvpair_value_int16_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_int32.3nvpair
+usr/src/man/man3nvpair/nvpair_value_int32_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_int64.3nvpair
+usr/src/man/man3nvpair/nvpair_value_int64_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_int8.3nvpair
+usr/src/man/man3nvpair/nvpair_value_int8_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_nvlist.3nvpair
+usr/src/man/man3nvpair/nvpair_value_nvlist_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_string.3nvpair
+usr/src/man/man3nvpair/nvpair_value_string_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_uint16.3nvpair
+usr/src/man/man3nvpair/nvpair_value_uint16_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_uint32.3nvpair
+usr/src/man/man3nvpair/nvpair_value_uint32_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_uint64.3nvpair
+usr/src/man/man3nvpair/nvpair_value_uint64_array.3nvpair
+usr/src/man/man3nvpair/nvpair_value_uint8.3nvpair
+usr/src/man/man3nvpair/nvpair_value_uint8_array.3nvpair
+usr/src/man/man3pam/pam_close_session.3pam
+usr/src/man/man3pam/pam_end.3pam
+usr/src/man/man3pam/pam_get_data.3pam
+usr/src/man/man3pam/pam_get_item.3pam
+usr/src/man/man3pam/pam_sm_close_session.3pam
+usr/src/man/man3papi/papiAttributeListAddBoolean.3papi
+usr/src/man/man3papi/papiAttributeListAddCollection.3papi
+usr/src/man/man3papi/papiAttributeListAddDatetime.3papi
+usr/src/man/man3papi/papiAttributeListAddInteger.3papi
+usr/src/man/man3papi/papiAttributeListAddMetadata.3papi
+usr/src/man/man3papi/papiAttributeListAddRange.3papi
+usr/src/man/man3papi/papiAttributeListAddResolution.3papi
+usr/src/man/man3papi/papiAttributeListAddString.3papi
+usr/src/man/man3papi/papiAttributeListDelete.3papi
+usr/src/man/man3papi/papiAttributeListFind.3papi
+usr/src/man/man3papi/papiAttributeListFree.3papi
+usr/src/man/man3papi/papiAttributeListFromString.3papi
+usr/src/man/man3papi/papiAttributeListGetBoolean.3papi
+usr/src/man/man3papi/papiAttributeListGetCollection.3papi
+usr/src/man/man3papi/papiAttributeListGetDatetime.3papi
+usr/src/man/man3papi/papiAttributeListGetInteger.3papi
+usr/src/man/man3papi/papiAttributeListGetMetadata.3papi
+usr/src/man/man3papi/papiAttributeListGetNext.3papi
+usr/src/man/man3papi/papiAttributeListGetRange.3papi
+usr/src/man/man3papi/papiAttributeListGetResolution.3papi
+usr/src/man/man3papi/papiAttributeListGetString.3papi
+usr/src/man/man3papi/papiAttributeListGetValue.3papi
+usr/src/man/man3papi/papiAttributeListToString.3papi
+usr/src/man/man3papi/papiJobCancel.3papi
+usr/src/man/man3papi/papiJobFree.3papi
+usr/src/man/man3papi/papiJobGetAttributeList.3papi
+usr/src/man/man3papi/papiJobGetId.3papi
+usr/src/man/man3papi/papiJobGetJobTicket.3papi
+usr/src/man/man3papi/papiJobGetPrinterName.3papi
+usr/src/man/man3papi/papiJobHold.3papi
+usr/src/man/man3papi/papiJobListFree.3papi
+usr/src/man/man3papi/papiJobModify.3papi
+usr/src/man/man3papi/papiJobMove.3papi
+usr/src/man/man3papi/papiJobPromote.3papi
+usr/src/man/man3papi/papiJobQuery.3papi
+usr/src/man/man3papi/papiJobRelease.3papi
+usr/src/man/man3papi/papiJobRestart.3papi
+usr/src/man/man3papi/papiJobStreamClose.3papi
+usr/src/man/man3papi/papiJobStreamOpen.3papi
+usr/src/man/man3papi/papiJobStreamWrite.3papi
+usr/src/man/man3papi/papiJobSubmitByReference.3papi
+usr/src/man/man3papi/papiJobValidate.3papi
+usr/src/man/man3papi/papiLibrarySupportedCalls.3papi
+usr/src/man/man3papi/papiPrinterAdd.3papi
+usr/src/man/man3papi/papiPrinterDisable.3papi
+usr/src/man/man3papi/papiPrinterEnable.3papi
+usr/src/man/man3papi/papiPrinterFree.3papi
+usr/src/man/man3papi/papiPrinterGetAttributeList.3papi
+usr/src/man/man3papi/papiPrinterListFree.3papi
+usr/src/man/man3papi/papiPrinterListJobs.3papi
+usr/src/man/man3papi/papiPrinterModify.3papi
+usr/src/man/man3papi/papiPrinterPause.3papi
+usr/src/man/man3papi/papiPrinterPurgeJobs.3papi
+usr/src/man/man3papi/papiPrinterQuery.3papi
+usr/src/man/man3papi/papiPrinterRemove.3papi
+usr/src/man/man3papi/papiPrinterResume.3papi
+usr/src/man/man3papi/papiServiceDestroy.3papi
+usr/src/man/man3papi/papiServiceGetAppData.3papi
+usr/src/man/man3papi/papiServiceGetAttributeList.3papi
+usr/src/man/man3papi/papiServiceGetEncryption.3papi
+usr/src/man/man3papi/papiServiceGetPassword.3papi
+usr/src/man/man3papi/papiServiceGetServiceName.3papi
+usr/src/man/man3papi/papiServiceGetStatusMessage.3papi
+usr/src/man/man3papi/papiServiceGetUserName.3papi
+usr/src/man/man3papi/papiServiceSetAppData.3papi
+usr/src/man/man3papi/papiServiceSetAuthCB.3papi
+usr/src/man/man3papi/papiServiceSetEncryption.3papi
+usr/src/man/man3papi/papiServiceSetPassword.3papi
+usr/src/man/man3papi/papiServiceSetUserName.3papi
+usr/src/man/man3picl/picl_get_next_by_col.3picl
+usr/src/man/man3picl/picl_get_next_prop.3picl
+usr/src/man/man3picl/picl_get_propval_by_name.3picl
+usr/src/man/man3picl/picl_set_propval_by_name.3picl
+usr/src/man/man3picltree/ptree_add_row_to_table.3picltree
+usr/src/man/man3picltree/ptree_delete_node.3picltree
+usr/src/man/man3picltree/ptree_delete_prop.3picltree
+usr/src/man/man3picltree/ptree_destroy_node.3picltree
+usr/src/man/man3picltree/ptree_destroy_prop.3picltree
+usr/src/man/man3picltree/ptree_get_next_by_col.3picltree
+usr/src/man/man3picltree/ptree_get_next_prop.3picltree
+usr/src/man/man3picltree/ptree_get_propval_by_name.3picltree
+usr/src/man/man3picltree/ptree_update_propval_by_name.3picltree
+usr/src/man/man3pool/pool_conf_close.3pool
+usr/src/man/man3pool/pool_conf_commit.3pool
+usr/src/man/man3pool/pool_conf_export.3pool
+usr/src/man/man3pool/pool_conf_free.3pool
+usr/src/man/man3pool/pool_conf_info.3pool
+usr/src/man/man3pool/pool_conf_location.3pool
+usr/src/man/man3pool/pool_conf_open.3pool
+usr/src/man/man3pool/pool_conf_remove.3pool
+usr/src/man/man3pool/pool_conf_rollback.3pool
+usr/src/man/man3pool/pool_conf_status.3pool
+usr/src/man/man3pool/pool_conf_to_elem.3pool
+usr/src/man/man3pool/pool_conf_update.3pool
+usr/src/man/man3pool/pool_conf_validate.3pool
+usr/src/man/man3pool/pool_create.3pool
+usr/src/man/man3pool/pool_destroy.3pool
+usr/src/man/man3pool/pool_dissociate.3pool
+usr/src/man/man3pool/pool_get_owning_resource.3pool
+usr/src/man/man3pool/pool_get_resource.3pool
+usr/src/man/man3pool/pool_get_resource_binding.3pool
+usr/src/man/man3pool/pool_get_status.3pool
+usr/src/man/man3pool/pool_info.3pool
+usr/src/man/man3pool/pool_put_property.3pool
+usr/src/man/man3pool/pool_query_components.3pool
+usr/src/man/man3pool/pool_query_pool_resources.3pool
+usr/src/man/man3pool/pool_query_pools.3pool
+usr/src/man/man3pool/pool_query_resource_components.3pool
+usr/src/man/man3pool/pool_query_resources.3pool
+usr/src/man/man3pool/pool_resource_destroy.3pool
+usr/src/man/man3pool/pool_resource_info.3pool
+usr/src/man/man3pool/pool_resource_to_elem.3pool
+usr/src/man/man3pool/pool_resource_transfer.3pool
+usr/src/man/man3pool/pool_resource_type_list.3pool
+usr/src/man/man3pool/pool_resource_xtransfer.3pool
+usr/src/man/man3pool/pool_rm_property.3pool
+usr/src/man/man3pool/pool_set_binding.3pool
+usr/src/man/man3pool/pool_set_status.3pool
+usr/src/man/man3pool/pool_static_location.3pool
+usr/src/man/man3pool/pool_strerror.3pool
+usr/src/man/man3pool/pool_to_elem.3pool
+usr/src/man/man3pool/pool_value_free.3pool
+usr/src/man/man3pool/pool_value_get_bool.3pool
+usr/src/man/man3pool/pool_value_get_double.3pool
+usr/src/man/man3pool/pool_value_get_int64.3pool
+usr/src/man/man3pool/pool_value_get_name.3pool
+usr/src/man/man3pool/pool_value_get_string.3pool
+usr/src/man/man3pool/pool_value_get_type.3pool
+usr/src/man/man3pool/pool_value_get_uint64.3pool
+usr/src/man/man3pool/pool_value_set_bool.3pool
+usr/src/man/man3pool/pool_value_set_double.3pool
+usr/src/man/man3pool/pool_value_set_int64.3pool
+usr/src/man/man3pool/pool_value_set_name.3pool
+usr/src/man/man3pool/pool_value_set_string.3pool
+usr/src/man/man3pool/pool_value_set_uint64.3pool
+usr/src/man/man3pool/pool_version.3pool
+usr/src/man/man3pool/pool_walk_pools.3pool
+usr/src/man/man3pool/pool_walk_properties.3pool
+usr/src/man/man3pool/pool_walk_resources.3pool
+usr/src/man/man3proc/ps_kill.3proc
+usr/src/man/man3proc/ps_lcontinue.3proc
+usr/src/man/man3proc/ps_lgetfpregs.3proc
+usr/src/man/man3proc/ps_lgetxregs.3proc
+usr/src/man/man3proc/ps_lgetxregsize.3proc
+usr/src/man/man3proc/ps_lrolltoaddr.3proc
+usr/src/man/man3proc/ps_lsetfpregs.3proc
+usr/src/man/man3proc/ps_lsetregs.3proc
+usr/src/man/man3proc/ps_lsetxregs.3proc
+usr/src/man/man3proc/ps_lstop.3proc
+usr/src/man/man3proc/ps_pcontinue.3proc
+usr/src/man/man3proc/ps_pdread.3proc
+usr/src/man/man3proc/ps_pdwrite.3proc
+usr/src/man/man3proc/ps_pglobal_sym.3proc
+usr/src/man/man3proc/ps_ptread.3proc
+usr/src/man/man3proc/ps_ptwrite.3proc
+usr/src/man/man3proc/ps_pwrite.3proc
+usr/src/man/man3project/endprojent.3project
+usr/src/man/man3project/fgetprojent.3project
+usr/src/man/man3project/getdefaultproj.3project
+usr/src/man/man3project/getprojbyid.3project
+usr/src/man/man3project/getprojbyname.3project
+usr/src/man/man3project/getprojidbyname.3project
+usr/src/man/man3project/inproj.3project
+usr/src/man/man3project/setprojent.3project
+usr/src/man/man3resolv/dn_comp.3resolv
+usr/src/man/man3resolv/dn_expand.3resolv
+usr/src/man/man3resolv/fp_resstat.3resolv
+usr/src/man/man3resolv/herror.3resolv
+usr/src/man/man3resolv/hstrerror.3resolv
+usr/src/man/man3resolv/res_getservers.3resolv
+usr/src/man/man3resolv/res_hostalias.3resolv
+usr/src/man/man3resolv/res_init.3resolv
+usr/src/man/man3resolv/res_mkquery.3resolv
+usr/src/man/man3resolv/res_nclose.3resolv
+usr/src/man/man3resolv/res_ndestroy.3resolv
+usr/src/man/man3resolv/res_ninit.3resolv
+usr/src/man/man3resolv/res_nmkquery.3resolv
+usr/src/man/man3resolv/res_nquery.3resolv
+usr/src/man/man3resolv/res_nquerydomain.3resolv
+usr/src/man/man3resolv/res_nsearch.3resolv
+usr/src/man/man3resolv/res_nsend.3resolv
+usr/src/man/man3resolv/res_nsendsigned.3resolv
+usr/src/man/man3resolv/res_query.3resolv
+usr/src/man/man3resolv/res_search.3resolv
+usr/src/man/man3resolv/res_send.3resolv
+usr/src/man/man3resolv/res_setservers.3resolv
+usr/src/man/man3rpc/havedisk.3rpc
+usr/src/man/man3rpc/rnusers.3rpc
+usr/src/man/man3rsm/rsm_free_interconnect_topology.3rsm
+usr/src/man/man3rsm/rsm_free_localmemory_handle.3rsm
+usr/src/man/man3rsm/rsm_get_controller_attr.3rsm
+usr/src/man/man3rsm/rsm_intr_signal_wait.3rsm
+usr/src/man/man3rsm/rsm_memseg_export_destroy.3rsm
+usr/src/man/man3rsm/rsm_memseg_export_rebind.3rsm
+usr/src/man/man3rsm/rsm_memseg_export_republish.3rsm
+usr/src/man/man3rsm/rsm_memseg_export_unpublish.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_close_barrier.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_destroy_barrier.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_disconnect.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_get16.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_get32.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_get64.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_get8.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_get_mode.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_getv.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_order_barrier.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_put16.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_put32.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_put64.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_put8.3rsm
+usr/src/man/man3rsm/rsm_memseg_import_unmap.3rsm
+usr/src/man/man3rsm/rsm_memseg_release_pollfd.3rsm
+usr/src/man/man3rsm/rsm_release_controller.3rsm
+usr/src/man/man3sasl/prop_clear.3sasl
+usr/src/man/man3sasl/prop_dispose.3sasl
+usr/src/man/man3sasl/prop_dup.3sasl
+usr/src/man/man3sasl/prop_erase.3sasl
+usr/src/man/man3sasl/prop_format.3sasl
+usr/src/man/man3sasl/prop_get.3sasl
+usr/src/man/man3sasl/prop_getnames.3sasl
+usr/src/man/man3sasl/prop_new.3sasl
+usr/src/man/man3sasl/prop_request.3sasl
+usr/src/man/man3sasl/prop_set.3sasl
+usr/src/man/man3sasl/prop_setvals.3sasl
+usr/src/man/man3sasl/sasl_encodev.3sasl
+usr/src/man/man3scf/scf_count_ranges_destroy.3scf
+usr/src/man/man3scf/scf_entry_add_value.3scf
+usr/src/man/man3scf/scf_entry_destroy.3scf
+usr/src/man/man3scf/scf_entry_destroy_children.3scf
+usr/src/man/man3scf/scf_entry_handle.3scf
+usr/src/man/man3scf/scf_entry_reset.3scf
+usr/src/man/man3scf/scf_handle_bind.3scf
+usr/src/man/man3scf/scf_handle_decorate.3scf
+usr/src/man/man3scf/scf_handle_destroy.3scf
+usr/src/man/man3scf/scf_handle_get_scope.3scf
+usr/src/man/man3scf/scf_handle_unbind.3scf
+usr/src/man/man3scf/scf_instance_add_pg.3scf
+usr/src/man/man3scf/scf_instance_delete.3scf
+usr/src/man/man3scf/scf_instance_destroy.3scf
+usr/src/man/man3scf/scf_instance_get_name.3scf
+usr/src/man/man3scf/scf_instance_get_parent.3scf
+usr/src/man/man3scf/scf_instance_get_pg.3scf
+usr/src/man/man3scf/scf_instance_get_pg_composed.3scf
+usr/src/man/man3scf/scf_instance_get_snapshot.3scf
+usr/src/man/man3scf/scf_instance_handle.3scf
+usr/src/man/man3scf/scf_instance_to_fmri.3scf
+usr/src/man/man3scf/scf_int_ranges_destroy.3scf
+usr/src/man/man3scf/scf_iter_destroy.3scf
+usr/src/man/man3scf/scf_iter_handle.3scf
+usr/src/man/man3scf/scf_iter_handle_scopes.3scf
+usr/src/man/man3scf/scf_iter_instance_pgs.3scf
+usr/src/man/man3scf/scf_iter_instance_pgs_composed.3scf
+usr/src/man/man3scf/scf_iter_instance_pgs_typed.3scf
+usr/src/man/man3scf/scf_iter_instance_pgs_typed_composed.3scf
+usr/src/man/man3scf/scf_iter_instance_snapshots.3scf
+usr/src/man/man3scf/scf_iter_next_instance.3scf
+usr/src/man/man3scf/scf_iter_next_pg.3scf
+usr/src/man/man3scf/scf_iter_next_property.3scf
+usr/src/man/man3scf/scf_iter_next_scope.3scf
+usr/src/man/man3scf/scf_iter_next_service.3scf
+usr/src/man/man3scf/scf_iter_next_snapshot.3scf
+usr/src/man/man3scf/scf_iter_next_value.3scf
+usr/src/man/man3scf/scf_iter_pg_properties.3scf
+usr/src/man/man3scf/scf_iter_property_values.3scf
+usr/src/man/man3scf/scf_iter_reset.3scf
+usr/src/man/man3scf/scf_iter_scope_services.3scf
+usr/src/man/man3scf/scf_iter_service_instances.3scf
+usr/src/man/man3scf/scf_iter_service_pgs.3scf
+usr/src/man/man3scf/scf_iter_service_pgs_typed.3scf
+usr/src/man/man3scf/scf_iter_snaplevel_pgs.3scf
+usr/src/man/man3scf/scf_iter_snaplevel_pgs_typed.3scf
+usr/src/man/man3scf/scf_myname.3scf
+usr/src/man/man3scf/scf_pg_delete.3scf
+usr/src/man/man3scf/scf_pg_destroy.3scf
+usr/src/man/man3scf/scf_pg_get_flags.3scf
+usr/src/man/man3scf/scf_pg_get_name.3scf
+usr/src/man/man3scf/scf_pg_get_parent_instance.3scf
+usr/src/man/man3scf/scf_pg_get_parent_service.3scf
+usr/src/man/man3scf/scf_pg_get_parent_snaplevel.3scf
+usr/src/man/man3scf/scf_pg_get_property.3scf
+usr/src/man/man3scf/scf_pg_get_type.3scf
+usr/src/man/man3scf/scf_pg_get_underlying_pg.3scf
+usr/src/man/man3scf/scf_pg_handle.3scf
+usr/src/man/man3scf/scf_pg_to_fmri.3scf
+usr/src/man/man3scf/scf_pg_update.3scf
+usr/src/man/man3scf/scf_property_destroy.3scf
+usr/src/man/man3scf/scf_property_get_name.3scf
+usr/src/man/man3scf/scf_property_get_value.3scf
+usr/src/man/man3scf/scf_property_handle.3scf
+usr/src/man/man3scf/scf_property_is_type.3scf
+usr/src/man/man3scf/scf_property_to_fmri.3scf
+usr/src/man/man3scf/scf_property_type.3scf
+usr/src/man/man3scf/scf_scope_add_service.3scf
+usr/src/man/man3scf/scf_scope_destroy.3scf
+usr/src/man/man3scf/scf_scope_get_name.3scf
+usr/src/man/man3scf/scf_scope_get_service.3scf
+usr/src/man/man3scf/scf_scope_handle.3scf
+usr/src/man/man3scf/scf_scope_to_fmri.3scf
+usr/src/man/man3scf/scf_service_add_instance.3scf
+usr/src/man/man3scf/scf_service_add_pg.3scf
+usr/src/man/man3scf/scf_service_delete.3scf
+usr/src/man/man3scf/scf_service_destroy.3scf
+usr/src/man/man3scf/scf_service_get_instance.3scf
+usr/src/man/man3scf/scf_service_get_name.3scf
+usr/src/man/man3scf/scf_service_get_parent.3scf
+usr/src/man/man3scf/scf_service_get_pg.3scf
+usr/src/man/man3scf/scf_service_handle.3scf
+usr/src/man/man3scf/scf_service_to_fmri.3scf
+usr/src/man/man3scf/scf_simple_app_props_free.3scf
+usr/src/man/man3scf/scf_simple_app_props_get.3scf
+usr/src/man/man3scf/scf_simple_app_props_next.3scf
+usr/src/man/man3scf/scf_simple_app_props_search.3scf
+usr/src/man/man3scf/scf_simple_prop_free.3scf
+usr/src/man/man3scf/scf_simple_prop_name.3scf
+usr/src/man/man3scf/scf_simple_prop_next_astring.3scf
+usr/src/man/man3scf/scf_simple_prop_next_boolean.3scf
+usr/src/man/man3scf/scf_simple_prop_next_count.3scf
+usr/src/man/man3scf/scf_simple_prop_next_integer.3scf
+usr/src/man/man3scf/scf_simple_prop_next_opaque.3scf
+usr/src/man/man3scf/scf_simple_prop_next_reset.3scf
+usr/src/man/man3scf/scf_simple_prop_next_time.3scf
+usr/src/man/man3scf/scf_simple_prop_next_ustring.3scf
+usr/src/man/man3scf/scf_simple_prop_numvalues.3scf
+usr/src/man/man3scf/scf_simple_prop_pgname.3scf
+usr/src/man/man3scf/scf_simple_prop_type.3scf
+usr/src/man/man3scf/scf_snaplevel_destroy.3scf
+usr/src/man/man3scf/scf_snaplevel_get_instance_name.3scf
+usr/src/man/man3scf/scf_snaplevel_get_next_snaplevel.3scf
+usr/src/man/man3scf/scf_snaplevel_get_parent.3scf
+usr/src/man/man3scf/scf_snaplevel_get_pg.3scf
+usr/src/man/man3scf/scf_snaplevel_get_scope_name.3scf
+usr/src/man/man3scf/scf_snaplevel_get_service_name.3scf
+usr/src/man/man3scf/scf_snaplevel_handle.3scf
+usr/src/man/man3scf/scf_snapshot_destroy.3scf
+usr/src/man/man3scf/scf_snapshot_get_base_snaplevel.3scf
+usr/src/man/man3scf/scf_snapshot_get_name.3scf
+usr/src/man/man3scf/scf_snapshot_get_parent.3scf
+usr/src/man/man3scf/scf_snapshot_handle.3scf
+usr/src/man/man3scf/scf_snapshot_update.3scf
+usr/src/man/man3scf/scf_strerror.3scf
+usr/src/man/man3scf/scf_string_to_type.3scf
+usr/src/man/man3scf/scf_tmpl_error_pg.3scf
+usr/src/man/man3scf/scf_tmpl_error_pg_tmpl.3scf
+usr/src/man/man3scf/scf_tmpl_error_prop.3scf
+usr/src/man/man3scf/scf_tmpl_error_prop_tmpl.3scf
+usr/src/man/man3scf/scf_tmpl_error_source_fmri.3scf
+usr/src/man/man3scf/scf_tmpl_error_type.3scf
+usr/src/man/man3scf/scf_tmpl_error_value.3scf
+usr/src/man/man3scf/scf_tmpl_errors_destroy.3scf
+usr/src/man/man3scf/scf_tmpl_get_by_pg.3scf
+usr/src/man/man3scf/scf_tmpl_get_by_pg_name.3scf
+usr/src/man/man3scf/scf_tmpl_get_by_prop.3scf
+usr/src/man/man3scf/scf_tmpl_iter_pgs.3scf
+usr/src/man/man3scf/scf_tmpl_iter_props.3scf
+usr/src/man/man3scf/scf_tmpl_next_error.3scf
+usr/src/man/man3scf/scf_tmpl_pg_common_name.3scf
+usr/src/man/man3scf/scf_tmpl_pg_description.3scf
+usr/src/man/man3scf/scf_tmpl_pg_destroy.3scf
+usr/src/man/man3scf/scf_tmpl_pg_required.3scf
+usr/src/man/man3scf/scf_tmpl_pg_reset.3scf
+usr/src/man/man3scf/scf_tmpl_pg_target.3scf
+usr/src/man/man3scf/scf_tmpl_pg_type.3scf
+usr/src/man/man3scf/scf_tmpl_prop_cardinality.3scf
+usr/src/man/man3scf/scf_tmpl_prop_common_name.3scf
+usr/src/man/man3scf/scf_tmpl_prop_description.3scf
+usr/src/man/man3scf/scf_tmpl_prop_destroy.3scf
+usr/src/man/man3scf/scf_tmpl_prop_internal_seps.3scf
+usr/src/man/man3scf/scf_tmpl_prop_required.3scf
+usr/src/man/man3scf/scf_tmpl_prop_reset.3scf
+usr/src/man/man3scf/scf_tmpl_prop_type.3scf
+usr/src/man/man3scf/scf_tmpl_prop_units.3scf
+usr/src/man/man3scf/scf_tmpl_prop_visibility.3scf
+usr/src/man/man3scf/scf_tmpl_reset_errors.3scf
+usr/src/man/man3scf/scf_tmpl_strerror.3scf
+usr/src/man/man3scf/scf_tmpl_value_common_name.3scf
+usr/src/man/man3scf/scf_tmpl_value_count_range_choices.3scf
+usr/src/man/man3scf/scf_tmpl_value_count_range_constraints.3scf
+usr/src/man/man3scf/scf_tmpl_value_description.3scf
+usr/src/man/man3scf/scf_tmpl_value_in_constraint.3scf
+usr/src/man/man3scf/scf_tmpl_value_int_range_choices.3scf
+usr/src/man/man3scf/scf_tmpl_value_int_range_constraints.3scf
+usr/src/man/man3scf/scf_tmpl_value_name_choices.3scf
+usr/src/man/man3scf/scf_tmpl_value_name_constraints.3scf
+usr/src/man/man3scf/scf_tmpl_visibility_to_string.3scf
+usr/src/man/man3scf/scf_transaction_commit.3scf
+usr/src/man/man3scf/scf_transaction_destroy.3scf
+usr/src/man/man3scf/scf_transaction_destroy_children.3scf
+usr/src/man/man3scf/scf_transaction_handle.3scf
+usr/src/man/man3scf/scf_transaction_property_change.3scf
+usr/src/man/man3scf/scf_transaction_property_change_type.3scf
+usr/src/man/man3scf/scf_transaction_property_delete.3scf
+usr/src/man/man3scf/scf_transaction_property_new.3scf
+usr/src/man/man3scf/scf_transaction_reset.3scf
+usr/src/man/man3scf/scf_transaction_reset_all.3scf
+usr/src/man/man3scf/scf_transaction_start.3scf
+usr/src/man/man3scf/scf_type_base_type.3scf
+usr/src/man/man3scf/scf_type_to_string.3scf
+usr/src/man/man3scf/scf_value_base_type.3scf
+usr/src/man/man3scf/scf_value_destroy.3scf
+usr/src/man/man3scf/scf_value_get_as_string.3scf
+usr/src/man/man3scf/scf_value_get_as_string_typed.3scf
+usr/src/man/man3scf/scf_value_get_astring.3scf
+usr/src/man/man3scf/scf_value_get_boolean.3scf
+usr/src/man/man3scf/scf_value_get_count.3scf
+usr/src/man/man3scf/scf_value_get_integer.3scf
+usr/src/man/man3scf/scf_value_get_opaque.3scf
+usr/src/man/man3scf/scf_value_get_time.3scf
+usr/src/man/man3scf/scf_value_get_ustring.3scf
+usr/src/man/man3scf/scf_value_handle.3scf
+usr/src/man/man3scf/scf_value_is_type.3scf
+usr/src/man/man3scf/scf_value_reset.3scf
+usr/src/man/man3scf/scf_value_set_astring.3scf
+usr/src/man/man3scf/scf_value_set_boolean.3scf
+usr/src/man/man3scf/scf_value_set_count.3scf
+usr/src/man/man3scf/scf_value_set_from_string.3scf
+usr/src/man/man3scf/scf_value_set_integer.3scf
+usr/src/man/man3scf/scf_value_set_opaque.3scf
+usr/src/man/man3scf/scf_value_set_time.3scf
+usr/src/man/man3scf/scf_value_set_ustring.3scf
+usr/src/man/man3scf/scf_value_type.3scf
+usr/src/man/man3scf/scf_values_destroy.3scf
+usr/src/man/man3scf/smf_degrade_instance.3scf
+usr/src/man/man3scf/smf_disable_instance.3scf
+usr/src/man/man3scf/smf_get_state.3scf
+usr/src/man/man3scf/smf_maintain_instance.3scf
+usr/src/man/man3scf/smf_refresh_instance.3scf
+usr/src/man/man3scf/smf_restart_instance.3scf
+usr/src/man/man3scf/smf_restore_instance.3scf
+usr/src/man/man3sec/acl_fromtext.3sec
+usr/src/man/man3sec/acl_set.3sec
+usr/src/man/man3sec/aclfrommode.3sec
+usr/src/man/man3sec/aclfromtext.3sec
+usr/src/man/man3sec/facl_get.3sec
+usr/src/man/man3sec/facl_set.3sec
+usr/src/man/man3secdb/chkauthattr.3secdb
+usr/src/man/man3secdb/endauthattr.3secdb
+usr/src/man/man3secdb/endexecattr.3secdb
+usr/src/man/man3secdb/endprofattr.3secdb
+usr/src/man/man3secdb/enduserattr.3secdb
+usr/src/man/man3secdb/fgetuserattr.3secdb
+usr/src/man/man3secdb/free_authattr.3secdb
+usr/src/man/man3secdb/free_execattr.3secdb
+usr/src/man/man3secdb/free_profattr.3secdb
+usr/src/man/man3secdb/free_proflist.3secdb
+usr/src/man/man3secdb/free_userattr.3secdb
+usr/src/man/man3secdb/getauthnam.3secdb
+usr/src/man/man3secdb/getexecprof.3secdb
+usr/src/man/man3secdb/getexecuser.3secdb
+usr/src/man/man3secdb/getproflist.3secdb
+usr/src/man/man3secdb/getprofnam.3secdb
+usr/src/man/man3secdb/getusernam.3secdb
+usr/src/man/man3secdb/getuseruid.3secdb
+usr/src/man/man3secdb/match_execattr.3secdb
+usr/src/man/man3secdb/setauthattr.3secdb
+usr/src/man/man3secdb/setexecattr.3secdb
+usr/src/man/man3secdb/setprofattr.3secdb
+usr/src/man/man3secdb/setuserattr.3secdb
+usr/src/man/man3sip/sip_add_accept.3sip
+usr/src/man/man3sip/sip_add_accept_enc.3sip
+usr/src/man/man3sip/sip_add_accept_lang.3sip
+usr/src/man/man3sip/sip_add_alert_info.3sip
+usr/src/man/man3sip/sip_add_allow.3sip
+usr/src/man/man3sip/sip_add_allow_events.3sip
+usr/src/man/man3sip/sip_add_authen_info.3sip
+usr/src/man/man3sip/sip_add_author.3sip
+usr/src/man/man3sip/sip_add_call_info.3sip
+usr/src/man/man3sip/sip_add_callid.3sip
+usr/src/man/man3sip/sip_add_contact.3sip
+usr/src/man/man3sip/sip_add_content.3sip
+usr/src/man/man3sip/sip_add_content_disp.3sip
+usr/src/man/man3sip/sip_add_content_enc.3sip
+usr/src/man/man3sip/sip_add_content_lang.3sip
+usr/src/man/man3sip/sip_add_content_type.3sip
+usr/src/man/man3sip/sip_add_cseq.3sip
+usr/src/man/man3sip/sip_add_date.3sip
+usr/src/man/man3sip/sip_add_error_info.3sip
+usr/src/man/man3sip/sip_add_event.3sip
+usr/src/man/man3sip/sip_add_expires.3sip
+usr/src/man/man3sip/sip_add_in_reply_to.3sip
+usr/src/man/man3sip/sip_add_maxforward.3sip
+usr/src/man/man3sip/sip_add_mime_version.3sip
+usr/src/man/man3sip/sip_add_min_expires.3sip
+usr/src/man/man3sip/sip_add_org.3sip
+usr/src/man/man3sip/sip_add_passertedid.3sip
+usr/src/man/man3sip/sip_add_ppreferredid.3sip
+usr/src/man/man3sip/sip_add_priority.3sip
+usr/src/man/man3sip/sip_add_privacy.3sip
+usr/src/man/man3sip/sip_add_proxy_authen.3sip
+usr/src/man/man3sip/sip_add_proxy_author.3sip
+usr/src/man/man3sip/sip_add_proxy_require.3sip
+usr/src/man/man3sip/sip_add_rack.3sip
+usr/src/man/man3sip/sip_add_record_route.3sip
+usr/src/man/man3sip/sip_add_reply_to.3sip
+usr/src/man/man3sip/sip_add_require.3sip
+usr/src/man/man3sip/sip_add_response_line.3sip
+usr/src/man/man3sip/sip_add_retry_after.3sip
+usr/src/man/man3sip/sip_add_route.3sip
+usr/src/man/man3sip/sip_add_rseq.3sip
+usr/src/man/man3sip/sip_add_server.3sip
+usr/src/man/man3sip/sip_add_subject.3sip
+usr/src/man/man3sip/sip_add_substate.3sip
+usr/src/man/man3sip/sip_add_supported.3sip
+usr/src/man/man3sip/sip_add_to.3sip
+usr/src/man/man3sip/sip_add_tstamp.3sip
+usr/src/man/man3sip/sip_add_unsupported.3sip
+usr/src/man/man3sip/sip_add_user_agent.3sip
+usr/src/man/man3sip/sip_add_via.3sip
+usr/src/man/man3sip/sip_add_warning.3sip
+usr/src/man/man3sip/sip_add_www_authen.3sip
+usr/src/man/man3sip/sip_clear_stale_data.3sip
+usr/src/man/man3sip/sip_conn_destroyed.3sip
+usr/src/man/man3sip/sip_copy_all_headers.3sip
+usr/src/man/man3sip/sip_copy_header.3sip
+usr/src/man/man3sip/sip_copy_header_by_name.3sip
+usr/src/man/man3sip/sip_create_dialog_req_nocontact.3sip
+usr/src/man/man3sip/sip_delete_header.3sip
+usr/src/man/man3sip/sip_delete_header_by_name.3sip
+usr/src/man/man3sip/sip_delete_value.3sip
+usr/src/man/man3sip/sip_disable_counters.3sip
+usr/src/man/man3sip/sip_disable_dialog_logging.3sip
+usr/src/man/man3sip/sip_disable_trans_logging.3sip
+usr/src/man/man3sip/sip_enable_dialog_logging.3sip
+usr/src/man/man3sip/sip_free_msg.3sip
+usr/src/man/man3sip/sip_free_parsed_uri.3sip
+usr/src/man/man3sip/sip_get_accept_enc.3sip
+usr/src/man/man3sip/sip_get_accept_lang.3sip
+usr/src/man/man3sip/sip_get_accept_sub_type.3sip
+usr/src/man/man3sip/sip_get_accept_type.3sip
+usr/src/man/man3sip/sip_get_alert_info_uri.3sip
+usr/src/man/man3sip/sip_get_allow_events.3sip
+usr/src/man/man3sip/sip_get_allow_method.3sip
+usr/src/man/man3sip/sip_get_authen_info.3sip
+usr/src/man/man3sip/sip_get_author_param.3sip
+usr/src/man/man3sip/sip_get_author_scheme.3sip
+usr/src/man/man3sip/sip_get_branchid.3sip
+usr/src/man/man3sip/sip_get_call_info_uri.3sip
+usr/src/man/man3sip/sip_get_callid.3sip
+usr/src/man/man3sip/sip_get_callseq_method.3sip
+usr/src/man/man3sip/sip_get_callseq_num.3sip
+usr/src/man/man3sip/sip_get_contact_uri_str.3sip
+usr/src/man/man3sip/sip_get_content.3sip
+usr/src/man/man3sip/sip_get_content_disp.3sip
+usr/src/man/man3sip/sip_get_content_enc.3sip
+usr/src/man/man3sip/sip_get_content_lang.3sip
+usr/src/man/man3sip/sip_get_content_length.3sip
+usr/src/man/man3sip/sip_get_content_sub_type.3sip
+usr/src/man/man3sip/sip_get_content_type.3sip
+usr/src/man/man3sip/sip_get_counter_value.3sip
+usr/src/man/man3sip/sip_get_date_day.3sip
+usr/src/man/man3sip/sip_get_date_month.3sip
+usr/src/man/man3sip/sip_get_date_time.3sip
+usr/src/man/man3sip/sip_get_date_timezone.3sip
+usr/src/man/man3sip/sip_get_date_wkday.3sip
+usr/src/man/man3sip/sip_get_date_year.3sip
+usr/src/man/man3sip/sip_get_dialog_callid.3sip
+usr/src/man/man3sip/sip_get_dialog_local_contact_uri.3sip
+usr/src/man/man3sip/sip_get_dialog_local_cseq.3sip
+usr/src/man/man3sip/sip_get_dialog_local_tag.3sip
+usr/src/man/man3sip/sip_get_dialog_local_uri.3sip
+usr/src/man/man3sip/sip_get_dialog_method.3sip
+usr/src/man/man3sip/sip_get_dialog_msgcnt.3sip
+usr/src/man/man3sip/sip_get_dialog_remote_cseq.3sip
+usr/src/man/man3sip/sip_get_dialog_remote_tag.3sip
+usr/src/man/man3sip/sip_get_dialog_remote_target_uri.3sip
+usr/src/man/man3sip/sip_get_dialog_remote_uri.3sip
+usr/src/man/man3sip/sip_get_dialog_route_set.3sip
+usr/src/man/man3sip/sip_get_dialog_type.3sip
+usr/src/man/man3sip/sip_get_error_info_uri.3sip
+usr/src/man/man3sip/sip_get_event.3sip
+usr/src/man/man3sip/sip_get_expires.3sip
+usr/src/man/man3sip/sip_get_from_display_name.3sip
+usr/src/man/man3sip/sip_get_from_tag.3sip
+usr/src/man/man3sip/sip_get_from_uri_str.3sip
+usr/src/man/man3sip/sip_get_in_reply_to.3sip
+usr/src/man/man3sip/sip_get_maxforward.3sip
+usr/src/man/man3sip/sip_get_mime_version.3sip
+usr/src/man/man3sip/sip_get_min_expires.3sip
+usr/src/man/man3sip/sip_get_next_value.3sip
+usr/src/man/man3sip/sip_get_org.3sip
+usr/src/man/man3sip/sip_get_params.3sip
+usr/src/man/man3sip/sip_get_passertedid_display_name.3sip
+usr/src/man/man3sip/sip_get_passertedid_uri_str.3sip
+usr/src/man/man3sip/sip_get_ppreferredid_display_name.3sip
+usr/src/man/man3sip/sip_get_ppreferredid_uri_str.3sip
+usr/src/man/man3sip/sip_get_priority.3sip
+usr/src/man/man3sip/sip_get_priv_value.3sip
+usr/src/man/man3sip/sip_get_proxy_authen_param.3sip
+usr/src/man/man3sip/sip_get_proxy_authen_scheme.3sip
+usr/src/man/man3sip/sip_get_proxy_author_param.3sip
+usr/src/man/man3sip/sip_get_proxy_author_scheme.3sip
+usr/src/man/man3sip/sip_get_proxy_require.3sip
+usr/src/man/man3sip/sip_get_rack_cseq_num.3sip
+usr/src/man/man3sip/sip_get_rack_method.3sip
+usr/src/man/man3sip/sip_get_rack_resp_num.3sip
+usr/src/man/man3sip/sip_get_replyto_display_name.3sip
+usr/src/man/man3sip/sip_get_replyto_uri_str.3sip
+usr/src/man/man3sip/sip_get_require.3sip
+usr/src/man/man3sip/sip_get_response_code.3sip
+usr/src/man/man3sip/sip_get_response_phrase.3sip
+usr/src/man/man3sip/sip_get_retry_after_cmts.3sip
+usr/src/man/man3sip/sip_get_retry_after_time.3sip
+usr/src/man/man3sip/sip_get_route_display_name.3sip
+usr/src/man/man3sip/sip_get_route_uri_str.3sip
+usr/src/man/man3sip/sip_get_rseq.3sip
+usr/src/man/man3sip/sip_get_rseq_resp_num.3sip
+usr/src/man/man3sip/sip_get_server.3sip
+usr/src/man/man3sip/sip_get_sip_version.3sip
+usr/src/man/man3sip/sip_get_subject.3sip
+usr/src/man/man3sip/sip_get_substate.3sip
+usr/src/man/man3sip/sip_get_supported.3sip
+usr/src/man/man3sip/sip_get_to_display_name.3sip
+usr/src/man/man3sip/sip_get_to_tag.3sip
+usr/src/man/man3sip/sip_get_to_uri_str.3sip
+usr/src/man/man3sip/sip_get_trans_branchid.3sip
+usr/src/man/man3sip/sip_get_trans_conn_obj.3sip
+usr/src/man/man3sip/sip_get_trans_orig_msg.3sip
+usr/src/man/man3sip/sip_get_trans_resp_msg.3sip
+usr/src/man/man3sip/sip_get_trans_state.3sip
+usr/src/man/man3sip/sip_get_tstamp_delay.3sip
+usr/src/man/man3sip/sip_get_tstamp_value.3sip
+usr/src/man/man3sip/sip_get_unsupported.3sip
+usr/src/man/man3sip/sip_get_uri_errflags.3sip
+usr/src/man/man3sip/sip_get_uri_headers.3sip
+usr/src/man/man3sip/sip_get_uri_host.3sip
+usr/src/man/man3sip/sip_get_uri_opaque.3sip
+usr/src/man/man3sip/sip_get_uri_params.3sip
+usr/src/man/man3sip/sip_get_uri_password.3sip
+usr/src/man/man3sip/sip_get_uri_path.3sip
+usr/src/man/man3sip/sip_get_uri_port.3sip
+usr/src/man/man3sip/sip_get_uri_query.3sip
+usr/src/man/man3sip/sip_get_uri_regname.3sip
+usr/src/man/man3sip/sip_get_uri_scheme.3sip
+usr/src/man/man3sip/sip_get_uri_user.3sip
+usr/src/man/man3sip/sip_get_user_agent.3sip
+usr/src/man/man3sip/sip_get_via_sent_by_host.3sip
+usr/src/man/man3sip/sip_get_via_sent_by_port.3sip
+usr/src/man/man3sip/sip_get_via_sent_protocol_name.3sip
+usr/src/man/man3sip/sip_get_via_sent_protocol_version.3sip
+usr/src/man/man3sip/sip_get_via_sent_transport.3sip
+usr/src/man/man3sip/sip_get_warning_agent.3sip
+usr/src/man/man3sip/sip_get_warning_code.3sip
+usr/src/man/man3sip/sip_get_warning_text.3sip
+usr/src/man/man3sip/sip_get_www_authen_param.3sip
+usr/src/man/man3sip/sip_get_www_authen_scheme.3sip
+usr/src/man/man3sip/sip_hdr_to_str.3sip
+usr/src/man/man3sip/sip_is_dialog_secure.3sip
+usr/src/man/man3sip/sip_is_param_present.3sip
+usr/src/man/man3sip/sip_is_uri_teluser.3sip
+usr/src/man/man3sip/sip_message_is_response.3sip
+usr/src/man/man3sip/sip_release_dialog.3sip
+usr/src/man/man3sip/sip_release_trans.3sip
+usr/src/man/man3sip/sip_reqline_to_str.3sip
+usr/src/man/man3sip/sip_respline_to_str.3sip
+usr/src/man/man3sip/sip_sent_by_to_str.3sip
+usr/src/man/man3sip/sip_unregister_all_sent_by.3sip
+usr/src/man/man3sip/sip_unregister_sent_by.3sip
+usr/src/man/man3sip/sip_uri_errflags_to_str.3sip
+usr/src/man/man3socket/endnetent.3socket
+usr/src/man/man3socket/endprotoent.3socket
+usr/src/man/man3socket/endservent.3socket
+usr/src/man/man3socket/ether_aton.3socket
+usr/src/man/man3socket/ether_hostton.3socket
+usr/src/man/man3socket/ether_line.3socket
+usr/src/man/man3socket/ether_ntoa.3socket
+usr/src/man/man3socket/ether_ntohost.3socket
+usr/src/man/man3socket/freeaddrinfo.3socket
+usr/src/man/man3socket/freehostent.3socket
+usr/src/man/man3socket/gai_strerror.3socket
+usr/src/man/man3socket/getipnodebyaddr.3socket
+usr/src/man/man3socket/getipv4sourcefilter.3socket
+usr/src/man/man3socket/getnameinfo.3socket
+usr/src/man/man3socket/getnetbyaddr.3socket
+usr/src/man/man3socket/getnetbyaddr_r.3socket
+usr/src/man/man3socket/getnetbyname_r.3socket
+usr/src/man/man3socket/getnetent.3socket
+usr/src/man/man3socket/getnetent_r.3socket
+usr/src/man/man3socket/getprotobyname_r.3socket
+usr/src/man/man3socket/getprotobynumber.3socket
+usr/src/man/man3socket/getprotobynumber_r.3socket
+usr/src/man/man3socket/getprotoent.3socket
+usr/src/man/man3socket/getprotoent_r.3socket
+usr/src/man/man3socket/getservbyname_r.3socket
+usr/src/man/man3socket/getservbyport.3socket
+usr/src/man/man3socket/getservbyport_r.3socket
+usr/src/man/man3socket/getservent.3socket
+usr/src/man/man3socket/getservent_r.3socket
+usr/src/man/man3socket/htonl.3socket
+usr/src/man/man3socket/htonll.3socket
+usr/src/man/man3socket/htons.3socket
+usr/src/man/man3socket/if_freenameindex.3socket
+usr/src/man/man3socket/if_indextoname.3socket
+usr/src/man/man3socket/if_nameindex.3socket
+usr/src/man/man3socket/inet6.3socket
+usr/src/man/man3socket/inet6_opt_append.3socket
+usr/src/man/man3socket/inet6_opt_find.3socket
+usr/src/man/man3socket/inet6_opt_finish.3socket
+usr/src/man/man3socket/inet6_opt_get_val.3socket
+usr/src/man/man3socket/inet6_opt_init.3socket
+usr/src/man/man3socket/inet6_opt_next.3socket
+usr/src/man/man3socket/inet6_opt_set_val.3socket
+usr/src/man/man3socket/inet6_rth_add.3socket
+usr/src/man/man3socket/inet6_rth_getaddr.3socket
+usr/src/man/man3socket/inet6_rth_init.3socket
+usr/src/man/man3socket/inet6_rth_reverse.3socket
+usr/src/man/man3socket/inet6_rth_segments.3socket
+usr/src/man/man3socket/inet6_rth_space.3socket
+usr/src/man/man3socket/inet_addr.3socket
+usr/src/man/man3socket/inet_aton.3socket
+usr/src/man/man3socket/inet_lnaof.3socket
+usr/src/man/man3socket/inet_makeaddr.3socket
+usr/src/man/man3socket/inet_netof.3socket
+usr/src/man/man3socket/inet_network.3socket
+usr/src/man/man3socket/inet_ntoa.3socket
+usr/src/man/man3socket/inet_ntop.3socket
+usr/src/man/man3socket/inet_pton.3socket
+usr/src/man/man3socket/ntohl.3socket
+usr/src/man/man3socket/ntohll.3socket
+usr/src/man/man3socket/ntohs.3socket
+usr/src/man/man3socket/rcmd_af.3socket
+usr/src/man/man3socket/recvfrom.3socket
+usr/src/man/man3socket/recvmsg.3socket
+usr/src/man/man3socket/rexec_af.3socket
+usr/src/man/man3socket/rresvport.3socket
+usr/src/man/man3socket/rresvport_af.3socket
+usr/src/man/man3socket/ruserok.3socket
+usr/src/man/man3socket/sctp_freeladdrs.3socket
+usr/src/man/man3socket/sctp_freepaddrs.3socket
+usr/src/man/man3socket/sendmsg.3socket
+usr/src/man/man3socket/sendto.3socket
+usr/src/man/man3socket/setipv4sourcefilter.3socket
+usr/src/man/man3socket/setnetent.3socket
+usr/src/man/man3socket/setprotoent.3socket
+usr/src/man/man3socket/setservent.3socket
+usr/src/man/man3socket/setsockopt.3socket
+usr/src/man/man3socket/setsourcefilter.3socket
+usr/src/man/man3stmf/stmfGetLuProp.3stmf
+usr/src/man/man3stmf/stmfModifyLuByFname.3stmf
+usr/src/man/man3sysevent/sysevent_get_pid.3sysevent
+usr/src/man/man3sysevent/sysevent_get_pub_name.3sysevent
+usr/src/man/man3sysevent/sysevent_get_seq.3sysevent
+usr/src/man/man3sysevent/sysevent_get_size.3sysevent
+usr/src/man/man3sysevent/sysevent_get_subclass_name.3sysevent
+usr/src/man/man3sysevent/sysevent_get_time.3sysevent
+usr/src/man/man3sysevent/sysevent_unbind_handle.3sysevent
+usr/src/man/man3sysevent/sysevent_unsubscribe_event.3sysevent
+usr/src/man/man3tecla/cfc_file_start.3tecla
+usr/src/man/man3tecla/cfc_literal_escapes.3tecla
+usr/src/man/man3tecla/cfc_set_check_fn.3tecla
+usr/src/man/man3tecla/cpl_add_completion.3tecla
+usr/src/man/man3tecla/cpl_check_exe.3tecla
+usr/src/man/man3tecla/cpl_file_completions.3tecla
+usr/src/man/man3tecla/cpl_last_error.3tecla
+usr/src/man/man3tecla/cpl_list_completions.3tecla
+usr/src/man/man3tecla/cpl_recall_matches.3tecla
+usr/src/man/man3tecla/cpl_record_error.3tecla
+usr/src/man/man3tecla/del_CplFileConf.3tecla
+usr/src/man/man3tecla/del_ExpandFile.3tecla
+usr/src/man/man3tecla/del_GetLine.3tecla
+usr/src/man/man3tecla/del_PathCache.3tecla
+usr/src/man/man3tecla/del_PcaPathConf.3tecla
+usr/src/man/man3tecla/del_WordCompletion.3tecla
+usr/src/man/man3tecla/ef_last_error.3tecla
+usr/src/man/man3tecla/ef_list_expansions.3tecla
+usr/src/man/man3tecla/gl_abandon_line.3tecla
+usr/src/man/man3tecla/gl_append_history.3tecla
+usr/src/man/man3tecla/gl_automatic_history.3tecla
+usr/src/man/man3tecla/gl_bind_keyseq.3tecla
+usr/src/man/man3tecla/gl_catch_blocked.3tecla
+usr/src/man/man3tecla/gl_change_terminal.3tecla
+usr/src/man/man3tecla/gl_clear_history.3tecla
+usr/src/man/man3tecla/gl_completion_action.3tecla
+usr/src/man/man3tecla/gl_configure_getline.3tecla
+usr/src/man/man3tecla/gl_customize_completion.3tecla
+usr/src/man/man3tecla/gl_display_text.3tecla
+usr/src/man/man3tecla/gl_echo_mode.3tecla
+usr/src/man/man3tecla/gl_erase_terminal.3tecla
+usr/src/man/man3tecla/gl_error_message.3tecla
+usr/src/man/man3tecla/gl_group_history.3tecla
+usr/src/man/man3tecla/gl_handle_signal.3tecla
+usr/src/man/man3tecla/gl_ignore_signal.3tecla
+usr/src/man/man3tecla/gl_inactivity_timeout.3tecla
+usr/src/man/man3tecla/gl_last_signal.3tecla
+usr/src/man/man3tecla/gl_limit_history.3tecla
+usr/src/man/man3tecla/gl_list_signals.3tecla
+usr/src/man/man3tecla/gl_load_history.3tecla
+usr/src/man/man3tecla/gl_lookup_history.3tecla
+usr/src/man/man3tecla/gl_normal_io.3tecla
+usr/src/man/man3tecla/gl_pending_io.3tecla
+usr/src/man/man3tecla/gl_prompt_style.3tecla
+usr/src/man/man3tecla/gl_query_char.3tecla
+usr/src/man/man3tecla/gl_range_of_history.3tecla
+usr/src/man/man3tecla/gl_raw_io.3tecla
+usr/src/man/man3tecla/gl_read_char.3tecla
+usr/src/man/man3tecla/gl_register_action.3tecla
+usr/src/man/man3tecla/gl_replace_prompt.3tecla
+usr/src/man/man3tecla/gl_resize_history.3tecla
+usr/src/man/man3tecla/gl_return_status.3tecla
+usr/src/man/man3tecla/gl_save_history.3tecla
+usr/src/man/man3tecla/gl_set_term_size.3tecla
+usr/src/man/man3tecla/gl_show_history.3tecla
+usr/src/man/man3tecla/gl_size_of_history.3tecla
+usr/src/man/man3tecla/gl_state_of_history.3tecla
+usr/src/man/man3tecla/gl_terminal_size.3tecla
+usr/src/man/man3tecla/gl_toggle_history.3tecla
+usr/src/man/man3tecla/gl_trap_signal.3tecla
+usr/src/man/man3tecla/gl_tty_signals.3tecla
+usr/src/man/man3tecla/gl_watch_fd.3tecla
+usr/src/man/man3tecla/new_CplFileConf.3tecla
+usr/src/man/man3tecla/new_ExpandFile.3tecla
+usr/src/man/man3tecla/new_GetLine.3tecla
+usr/src/man/man3tecla/new_PathCache.3tecla
+usr/src/man/man3tecla/new_PcaPathConf.3tecla
+usr/src/man/man3tecla/new_WordCompletion.3tecla
+usr/src/man/man3tecla/pca_last_error.3tecla
+usr/src/man/man3tecla/pca_path_completions.3tecla
+usr/src/man/man3tecla/pca_scan_path.3tecla
+usr/src/man/man3tecla/pca_set_check_fn.3tecla
+usr/src/man/man3tecla/ppc_file_start.3tecla
+usr/src/man/man3tecla/ppc_literal_escapes.3tecla
+usr/src/man/man3tnf/TNF_DEBUG.3tnf
+usr/src/man/man3tnf/TNF_DEFINE_RECORD_1.3tnf
+usr/src/man/man3tnf/TNF_DEFINE_RECORD_2.3tnf
+usr/src/man/man3tnf/TNF_DEFINE_RECORD_3.3tnf
+usr/src/man/man3tnf/TNF_DEFINE_RECORD_4.3tnf
+usr/src/man/man3tnf/TNF_DEFINE_RECORD_5.3tnf
+usr/src/man/man3tnf/TNF_PROBE_0.3tnf
+usr/src/man/man3tnf/TNF_PROBE_0_DEBUG.3tnf
+usr/src/man/man3tnf/TNF_PROBE_1.3tnf
+usr/src/man/man3tnf/TNF_PROBE_1_DEBUG.3tnf
+usr/src/man/man3tnf/TNF_PROBE_2.3tnf
+usr/src/man/man3tnf/TNF_PROBE_2_DEBUG.3tnf
+usr/src/man/man3tnf/TNF_PROBE_3.3tnf
+usr/src/man/man3tnf/TNF_PROBE_3_DEBUG.3tnf
+usr/src/man/man3tnf/TNF_PROBE_4.3tnf
+usr/src/man/man3tnf/TNF_PROBE_4_DEBUG.3tnf
+usr/src/man/man3tnf/TNF_PROBE_5.3tnf
+usr/src/man/man3tnf/TNF_PROBE_5_DEBUG.3tnf
+usr/src/man/man3tnf/tnf_process_enable.3tnf
+usr/src/man/man3tnf/tnf_thread_disable.3tnf
+usr/src/man/man3tnf/tnf_thread_enable.3tnf
+usr/src/man/man3tnf/tnfctl_buffer_dealloc.3tnf
+usr/src/man/man3tnf/tnfctl_check_libs.3tnf
+usr/src/man/man3tnf/tnfctl_continue.3tnf
+usr/src/man/man3tnf/tnfctl_exec_open.3tnf
+usr/src/man/man3tnf/tnfctl_filter_list_add.3tnf
+usr/src/man/man3tnf/tnfctl_filter_list_delete.3tnf
+usr/src/man/man3tnf/tnfctl_filter_list_get.3tnf
+usr/src/man/man3tnf/tnfctl_filter_state_set.3tnf
+usr/src/man/man3tnf/tnfctl_probe_apply_ids.3tnf
+usr/src/man/man3tnf/tnfctl_probe_connect.3tnf
+usr/src/man/man3tnf/tnfctl_probe_disable.3tnf
+usr/src/man/man3tnf/tnfctl_probe_disconnect_all.3tnf
+usr/src/man/man3tnf/tnfctl_probe_enable.3tnf
+usr/src/man/man3tnf/tnfctl_probe_trace.3tnf
+usr/src/man/man3tnf/tnfctl_probe_untrace.3tnf
+usr/src/man/man3tsol/Xbcleartos.3tsol
+usr/src/man/man3tsol/Xbsltos.3tsol
+usr/src/man/man3tsol/bcleartoh.3tsol
+usr/src/man/man3tsol/bcleartoh_r.3tsol
+usr/src/man/man3tsol/bcleartos.3tsol
+usr/src/man/man3tsol/bldominates.3tsol
+usr/src/man/man3tsol/blequal.3tsol
+usr/src/man/man3tsol/blinrange.3tsol
+usr/src/man/man3tsol/blmaximum.3tsol
+usr/src/man/man3tsol/blminimum.3tsol
+usr/src/man/man3tsol/blstrictdom.3tsol
+usr/src/man/man3tsol/bltocolor_r.3tsol
+usr/src/man/man3tsol/bsltoh.3tsol
+usr/src/man/man3tsol/bsltoh_r.3tsol
+usr/src/man/man3tsol/bsltos.3tsol
+usr/src/man/man3tsol/getzoneidbylabel.3tsol
+usr/src/man/man3tsol/getzonelabelbyname.3tsol
+usr/src/man/man3tsol/getzonerootbylabel.3tsol
+usr/src/man/man3tsol/getzonerootbyname.3tsol
+usr/src/man/man3tsol/h_alloc.3tsol
+usr/src/man/man3tsol/h_free.3tsol
+usr/src/man/man3tsol/htobclear.3tsol
+usr/src/man/man3tsol/htobsl.3tsol
+usr/src/man/man3tsol/m_label_alloc.3tsol
+usr/src/man/man3tsol/m_label_dup.3tsol
+usr/src/man/man3tsol/m_label_free.3tsol
+usr/src/man/man3tsol/sbcleartos.3tsol
+usr/src/man/man3tsol/sbsltos.3tsol
+usr/src/man/man3tsol/stobclear.3tsol
+usr/src/man/man3tsol/stobsl.3tsol
+usr/src/man/man3uuid/uuid_compare.3uuid
+usr/src/man/man3uuid/uuid_copy.3uuid
+usr/src/man/man3uuid/uuid_generate.3uuid
+usr/src/man/man3uuid/uuid_generate_random.3uuid
+usr/src/man/man3uuid/uuid_generate_time.3uuid
+usr/src/man/man3uuid/uuid_is_null.3uuid
+usr/src/man/man3uuid/uuid_parse.3uuid
+usr/src/man/man3uuid/uuid_time.3uuid
+usr/src/man/man3uuid/uuid_unparse.3uuid
+usr/src/man/man3volmgt/media_setattr.3volmgt
+usr/src/man/man3volmgt/volmgt_symdev.3volmgt
+usr/src/man/man3xcurses/COLORS.3xcurses
+usr/src/man/man3xcurses/COLOR_PAIR.3xcurses
+usr/src/man/man3xcurses/COLOR_PAIRS.3xcurses
+usr/src/man/man3xcurses/PAIR_NUMBER.3xcurses
+usr/src/man/man3xcurses/add_wchstr.3xcurses
+usr/src/man/man3xcurses/addchnstr.3xcurses
+usr/src/man/man3xcurses/addstr.3xcurses
+usr/src/man/man3xcurses/addwstr.3xcurses
+usr/src/man/man3xcurses/attr_off.3xcurses
+usr/src/man/man3xcurses/attr_on.3xcurses
+usr/src/man/man3xcurses/attr_set.3xcurses
+usr/src/man/man3xcurses/attron.3xcurses
+usr/src/man/man3xcurses/attrset.3xcurses
+usr/src/man/man3xcurses/bkgdset.3xcurses
+usr/src/man/man3xcurses/bkgrndset.3xcurses
+usr/src/man/man3xcurses/box.3xcurses
+usr/src/man/man3xcurses/box_set.3xcurses
+usr/src/man/man3xcurses/color_content.3xcurses
+usr/src/man/man3xcurses/color_set.3xcurses
+usr/src/man/man3xcurses/def_shell_mode.3xcurses
+usr/src/man/man3xcurses/erase.3xcurses
+usr/src/man/man3xcurses/erasewchar.3xcurses
+usr/src/man/man3xcurses/flash.3xcurses
+usr/src/man/man3xcurses/get_wstr.3xcurses
+usr/src/man/man3xcurses/getbkgd.3xcurses
+usr/src/man/man3xcurses/getbkgrnd.3xcurses
+usr/src/man/man3xcurses/getmaxyx.3xcurses
+usr/src/man/man3xcurses/getparyx.3xcurses
+usr/src/man/man3xcurses/getstr.3xcurses
+usr/src/man/man3xcurses/getyx.3xcurses
+usr/src/man/man3xcurses/has_colors.3xcurses
+usr/src/man/man3xcurses/has_il.3xcurses
+usr/src/man/man3xcurses/idlok.3xcurses
+usr/src/man/man3xcurses/in_wchstr.3xcurses
+usr/src/man/man3xcurses/inchstr.3xcurses
+usr/src/man/man3xcurses/init_color.3xcurses
+usr/src/man/man3xcurses/init_pair.3xcurses
+usr/src/man/man3xcurses/ins_wstr.3xcurses
+usr/src/man/man3xcurses/insstr.3xcurses
+usr/src/man/man3xcurses/instr.3xcurses
+usr/src/man/man3xcurses/inwstr.3xcurses
+usr/src/man/man3xcurses/is_wintouched.3xcurses
+usr/src/man/man3xcurses/isendwin.3xcurses
+usr/src/man/man3xcurses/key_name.3xcurses
+usr/src/man/man3xcurses/killchar.3xcurses
+usr/src/man/man3xcurses/killwchar.3xcurses
+usr/src/man/man3xcurses/leaveok.3xcurses
+usr/src/man/man3xcurses/mvadd_wch.3xcurses
+usr/src/man/man3xcurses/mvadd_wchnstr.3xcurses
+usr/src/man/man3xcurses/mvadd_wchstr.3xcurses
+usr/src/man/man3xcurses/mvaddch.3xcurses
+usr/src/man/man3xcurses/mvaddchnstr.3xcurses
+usr/src/man/man3xcurses/mvaddchstr.3xcurses
+usr/src/man/man3xcurses/mvaddnstr.3xcurses
+usr/src/man/man3xcurses/mvaddnwstr.3xcurses
+usr/src/man/man3xcurses/mvaddstr.3xcurses
+usr/src/man/man3xcurses/mvaddwstr.3xcurses
+usr/src/man/man3xcurses/mvchgat.3xcurses
+usr/src/man/man3xcurses/mvdelch.3xcurses
+usr/src/man/man3xcurses/mvget_wch.3xcurses
+usr/src/man/man3xcurses/mvget_wstr.3xcurses
+usr/src/man/man3xcurses/mvgetch.3xcurses
+usr/src/man/man3xcurses/mvgetn_wstr.3xcurses
+usr/src/man/man3xcurses/mvgetnstr.3xcurses
+usr/src/man/man3xcurses/mvgetstr.3xcurses
+usr/src/man/man3xcurses/mvhline.3xcurses
+usr/src/man/man3xcurses/mvhline_set.3xcurses
+usr/src/man/man3xcurses/mvin_wch.3xcurses
+usr/src/man/man3xcurses/mvin_wchnstr.3xcurses
+usr/src/man/man3xcurses/mvin_wchstr.3xcurses
+usr/src/man/man3xcurses/mvinch.3xcurses
+usr/src/man/man3xcurses/mvinchnstr.3xcurses
+usr/src/man/man3xcurses/mvinchstr.3xcurses
+usr/src/man/man3xcurses/mvinnstr.3xcurses
+usr/src/man/man3xcurses/mvinnwstr.3xcurses
+usr/src/man/man3xcurses/mvins_nwstr.3xcurses
+usr/src/man/man3xcurses/mvins_wch.3xcurses
+usr/src/man/man3xcurses/mvins_wstr.3xcurses
+usr/src/man/man3xcurses/mvinsch.3xcurses
+usr/src/man/man3xcurses/mvinsnstr.3xcurses
+usr/src/man/man3xcurses/mvinsstr.3xcurses
+usr/src/man/man3xcurses/mvinstr.3xcurses
+usr/src/man/man3xcurses/mvinwstr.3xcurses
+usr/src/man/man3xcurses/mvvline.3xcurses
+usr/src/man/man3xcurses/mvvline_set.3xcurses
+usr/src/man/man3xcurses/mvwadd_wch.3xcurses
+usr/src/man/man3xcurses/mvwadd_wchnstr.3xcurses
+usr/src/man/man3xcurses/mvwadd_wchstr.3xcurses
+usr/src/man/man3xcurses/mvwaddch.3xcurses
+usr/src/man/man3xcurses/mvwaddchnstr.3xcurses
+usr/src/man/man3xcurses/mvwaddchstr.3xcurses
+usr/src/man/man3xcurses/mvwaddnstr.3xcurses
+usr/src/man/man3xcurses/mvwaddnwstr.3xcurses
+usr/src/man/man3xcurses/mvwaddstr.3xcurses
+usr/src/man/man3xcurses/mvwaddwstr.3xcurses
+usr/src/man/man3xcurses/mvwchgat.3xcurses
+usr/src/man/man3xcurses/mvwdelch.3xcurses
+usr/src/man/man3xcurses/mvwget_wch.3xcurses
+usr/src/man/man3xcurses/mvwget_wstr.3xcurses
+usr/src/man/man3xcurses/mvwgetch.3xcurses
+usr/src/man/man3xcurses/mvwgetn_wstr.3xcurses
+usr/src/man/man3xcurses/mvwgetnstr.3xcurses
+usr/src/man/man3xcurses/mvwgetstr.3xcurses
+usr/src/man/man3xcurses/mvwhline.3xcurses
+usr/src/man/man3xcurses/mvwhline_set.3xcurses
+usr/src/man/man3xcurses/mvwin_wch.3xcurses
+usr/src/man/man3xcurses/mvwin_wchnstr.3xcurses
+usr/src/man/man3xcurses/mvwin_wchstr.3xcurses
+usr/src/man/man3xcurses/mvwinch.3xcurses
+usr/src/man/man3xcurses/mvwinchnstr.3xcurses
+usr/src/man/man3xcurses/mvwinchstr.3xcurses
+usr/src/man/man3xcurses/mvwinnstr.3xcurses
+usr/src/man/man3xcurses/mvwinnwstr.3xcurses
+usr/src/man/man3xcurses/mvwins_nwstr.3xcurses
+usr/src/man/man3xcurses/mvwins_wch.3xcurses
+usr/src/man/man3xcurses/mvwins_wstr.3xcurses
+usr/src/man/man3xcurses/mvwinsch.3xcurses
+usr/src/man/man3xcurses/mvwinsnstr.3xcurses
+usr/src/man/man3xcurses/mvwinsstr.3xcurses
+usr/src/man/man3xcurses/mvwinstr.3xcurses
+usr/src/man/man3xcurses/mvwinwstr.3xcurses
+usr/src/man/man3xcurses/mvwprintw.3xcurses
+usr/src/man/man3xcurses/mvwscanw.3xcurses
+usr/src/man/man3xcurses/mvwvline.3xcurses
+usr/src/man/man3xcurses/mvwvline_set.3xcurses
+usr/src/man/man3xcurses/newterm.3xcurses
+usr/src/man/man3xcurses/newwin.3xcurses
+usr/src/man/man3xcurses/nocbreak.3xcurses
+usr/src/man/man3xcurses/noecho.3xcurses
+usr/src/man/man3xcurses/nonl.3xcurses
+usr/src/man/man3xcurses/noraw.3xcurses
+usr/src/man/man3xcurses/overwrite.3xcurses
+usr/src/man/man3xcurses/pair_content.3xcurses
+usr/src/man/man3xcurses/pecho_wchar.3xcurses
+usr/src/man/man3xcurses/pnoutrefresh.3xcurses
+usr/src/man/man3xcurses/prefresh.3xcurses
+usr/src/man/man3xcurses/printw.3xcurses
+usr/src/man/man3xcurses/putwin.3xcurses
+usr/src/man/man3xcurses/qiflush.3xcurses
+usr/src/man/man3xcurses/raw.3xcurses
+usr/src/man/man3xcurses/refresh.3xcurses
+usr/src/man/man3xcurses/reset_prog_mode.3xcurses
+usr/src/man/man3xcurses/reset_shell_mode.3xcurses
+usr/src/man/man3xcurses/restartterm.3xcurses
+usr/src/man/man3xcurses/savetty.3xcurses
+usr/src/man/man3xcurses/scanw.3xcurses
+usr/src/man/man3xcurses/scr_init.3xcurses
+usr/src/man/man3xcurses/scr_restore.3xcurses
+usr/src/man/man3xcurses/scr_set.3xcurses
+usr/src/man/man3xcurses/scroll.3xcurses
+usr/src/man/man3xcurses/scrollok.3xcurses
+usr/src/man/man3xcurses/set_curterm.3xcurses
+usr/src/man/man3xcurses/setscrreg.3xcurses
+usr/src/man/man3xcurses/setupterm.3xcurses
+usr/src/man/man3xcurses/slk_attr_off.3xcurses
+usr/src/man/man3xcurses/slk_attr_on.3xcurses
+usr/src/man/man3xcurses/slk_attr_set.3xcurses
+usr/src/man/man3xcurses/slk_attron.3xcurses
+usr/src/man/man3xcurses/slk_attrset.3xcurses
+usr/src/man/man3xcurses/slk_clear.3xcurses
+usr/src/man/man3xcurses/slk_color.3xcurses
+usr/src/man/man3xcurses/slk_init.3xcurses
+usr/src/man/man3xcurses/slk_label.3xcurses
+usr/src/man/man3xcurses/slk_noutrefresh.3xcurses
+usr/src/man/man3xcurses/slk_refresh.3xcurses
+usr/src/man/man3xcurses/slk_restore.3xcurses
+usr/src/man/man3xcurses/slk_set.3xcurses
+usr/src/man/man3xcurses/slk_touch.3xcurses
+usr/src/man/man3xcurses/slk_wset.3xcurses
+usr/src/man/man3xcurses/standout.3xcurses
+usr/src/man/man3xcurses/start_color.3xcurses
+usr/src/man/man3xcurses/subpad.3xcurses
+usr/src/man/man3xcurses/subwin.3xcurses
+usr/src/man/man3xcurses/term_attrs.3xcurses
+usr/src/man/man3xcurses/tgetflag.3xcurses
+usr/src/man/man3xcurses/tgetnum.3xcurses
+usr/src/man/man3xcurses/tgetstr.3xcurses
+usr/src/man/man3xcurses/tgoto.3xcurses
+usr/src/man/man3xcurses/tigetnum.3xcurses
+usr/src/man/man3xcurses/tigetstr.3xcurses
+usr/src/man/man3xcurses/timeout.3xcurses
+usr/src/man/man3xcurses/touchline.3xcurses
+usr/src/man/man3xcurses/touchwin.3xcurses
+usr/src/man/man3xcurses/tparm.3xcurses
+usr/src/man/man3xcurses/tputs.3xcurses
+usr/src/man/man3xcurses/unget_wch.3xcurses
+usr/src/man/man3xcurses/untouchwin.3xcurses
+usr/src/man/man3xcurses/vid_attr.3xcurses
+usr/src/man/man3xcurses/vid_puts.3xcurses
+usr/src/man/man3xcurses/vidputs.3xcurses
+usr/src/man/man3xcurses/vline.3xcurses
+usr/src/man/man3xcurses/vline_set.3xcurses
+usr/src/man/man3xcurses/wadd_wch.3xcurses
+usr/src/man/man3xcurses/wadd_wchnstr.3xcurses
+usr/src/man/man3xcurses/wadd_wchstr.3xcurses
+usr/src/man/man3xcurses/waddch.3xcurses
+usr/src/man/man3xcurses/waddchnstr.3xcurses
+usr/src/man/man3xcurses/waddchstr.3xcurses
+usr/src/man/man3xcurses/waddnstr.3xcurses
+usr/src/man/man3xcurses/waddnwstr.3xcurses
+usr/src/man/man3xcurses/waddstr.3xcurses
+usr/src/man/man3xcurses/waddwstr.3xcurses
+usr/src/man/man3xcurses/wattr_get.3xcurses
+usr/src/man/man3xcurses/wattr_off.3xcurses
+usr/src/man/man3xcurses/wattr_on.3xcurses
+usr/src/man/man3xcurses/wattr_set.3xcurses
+usr/src/man/man3xcurses/wattroff.3xcurses
+usr/src/man/man3xcurses/wattron.3xcurses
+usr/src/man/man3xcurses/wattrset.3xcurses
+usr/src/man/man3xcurses/wbkgd.3xcurses
+usr/src/man/man3xcurses/wbkgdset.3xcurses
+usr/src/man/man3xcurses/wbkgrnd.3xcurses
+usr/src/man/man3xcurses/wbkgrndset.3xcurses
+usr/src/man/man3xcurses/wborder.3xcurses
+usr/src/man/man3xcurses/wborder_set.3xcurses
+usr/src/man/man3xcurses/wchgat.3xcurses
+usr/src/man/man3xcurses/wclear.3xcurses
+usr/src/man/man3xcurses/wclrtobot.3xcurses
+usr/src/man/man3xcurses/wclrtoeol.3xcurses
+usr/src/man/man3xcurses/wcolor_set.3xcurses
+usr/src/man/man3xcurses/wcursyncup.3xcurses
+usr/src/man/man3xcurses/wdelch.3xcurses
+usr/src/man/man3xcurses/wdeleteln.3xcurses
+usr/src/man/man3xcurses/wecho_wchar.3xcurses
+usr/src/man/man3xcurses/wechochar.3xcurses
+usr/src/man/man3xcurses/werase.3xcurses
+usr/src/man/man3xcurses/wget_wch.3xcurses
+usr/src/man/man3xcurses/wget_wstr.3xcurses
+usr/src/man/man3xcurses/wgetbkgrnd.3xcurses
+usr/src/man/man3xcurses/wgetch.3xcurses
+usr/src/man/man3xcurses/wgetn_wstr.3xcurses
+usr/src/man/man3xcurses/wgetnstr.3xcurses
+usr/src/man/man3xcurses/wgetstr.3xcurses
+usr/src/man/man3xcurses/whline.3xcurses
+usr/src/man/man3xcurses/whline_set.3xcurses
+usr/src/man/man3xcurses/win_wch.3xcurses
+usr/src/man/man3xcurses/win_wchnstr.3xcurses
+usr/src/man/man3xcurses/win_wchstr.3xcurses
+usr/src/man/man3xcurses/winch.3xcurses
+usr/src/man/man3xcurses/winchnstr.3xcurses
+usr/src/man/man3xcurses/winchstr.3xcurses
+usr/src/man/man3xcurses/winnstr.3xcurses
+usr/src/man/man3xcurses/winnwstr.3xcurses
+usr/src/man/man3xcurses/wins_nwstr.3xcurses
+usr/src/man/man3xcurses/wins_wch.3xcurses
+usr/src/man/man3xcurses/wins_wstr.3xcurses
+usr/src/man/man3xcurses/winsch.3xcurses
+usr/src/man/man3xcurses/winsdelln.3xcurses
+usr/src/man/man3xcurses/winsertln.3xcurses
+usr/src/man/man3xcurses/winsnstr.3xcurses
+usr/src/man/man3xcurses/winsstr.3xcurses
+usr/src/man/man3xcurses/winstr.3xcurses
+usr/src/man/man3xcurses/winwstr.3xcurses
+usr/src/man/man3xcurses/wmove.3xcurses
+usr/src/man/man3xcurses/wnoutrefresh.3xcurses
+usr/src/man/man3xcurses/wprintw.3xcurses
+usr/src/man/man3xcurses/wredrawln.3xcurses
+usr/src/man/man3xcurses/wrefresh.3xcurses
+usr/src/man/man3xcurses/wscanw.3xcurses
+usr/src/man/man3xcurses/wscrl.3xcurses
+usr/src/man/man3xcurses/wsetscrreg.3xcurses
+usr/src/man/man3xcurses/wstandend.3xcurses
+usr/src/man/man3xcurses/wstandout.3xcurses
+usr/src/man/man3xcurses/wsyncdown.3xcurses
+usr/src/man/man3xcurses/wsyncup.3xcurses
+usr/src/man/man3xcurses/wtimeout.3xcurses
+usr/src/man/man3xcurses/wtouchln.3xcurses
+usr/src/man/man3xcurses/wvline.3xcurses
+usr/src/man/man3xcurses/wvline_set.3xcurses
+usr/src/man/man3xnet/getaddrinfo.3xnet
+usr/src/man/man3xnet/gethostbyaddr.3xnet
+usr/src/man/man3xnet/gethostbyname.3xnet
+usr/src/man/man3xnet/gethostent.3xnet
+usr/src/man/man3xnet/getnetbyaddr.3xnet
+usr/src/man/man3xnet/getnetbyname.3xnet
+usr/src/man/man3xnet/getnetent.3xnet
+usr/src/man/man3xnet/getprotobyname.3xnet
+usr/src/man/man3xnet/getprotobynumber.3xnet
+usr/src/man/man3xnet/getprotoent.3xnet
+usr/src/man/man3xnet/getservbyname.3xnet
+usr/src/man/man3xnet/getservbyport.3xnet
+usr/src/man/man3xnet/getservent.3xnet
+usr/src/man/man3xnet/htons.3xnet
+usr/src/man/man3xnet/if_freenameindex.3xnet
+usr/src/man/man3xnet/if_indextoname.3xnet
+usr/src/man/man3xnet/if_nameindex.3xnet
+usr/src/man/man3xnet/inet_lnaof.3xnet
+usr/src/man/man3xnet/inet_makeaddr.3xnet
+usr/src/man/man3xnet/inet_netof.3xnet
+usr/src/man/man3xnet/inet_network.3xnet
+usr/src/man/man3xnet/inet_ntoa.3xnet
+usr/src/man/man3xnet/inet_pton.3xnet
+usr/src/man/man3xnet/ntohl.3xnet
+usr/src/man/man3xnet/ntohs.3xnet
+usr/src/man/man3xnet/sethostent.3xnet
+usr/src/man/man3xnet/setnetent.3xnet
+usr/src/man/man3xnet/setprotoent.3xnet
+usr/src/man/man3xnet/setservent.3xnet
+usr/src/man/man4/addresses.4
+usr/src/man/man4/devid_cache.4
+usr/src/man/man4/devname_cache.4
+usr/src/man/man4/dir.4
+usr/src/man/man4/dumpdates.4
+usr/src/man/man4/fbtab.4
+usr/src/man/man4/forward.4
+usr/src/man/man4/fs.4
+usr/src/man/man4/intro.4
+usr/src/man/man4/isa.4
+usr/src/man/man4/md.cf.4
+usr/src/man/man4/mdi_ib_cache.4
+usr/src/man/man4/mdi_scsi_vhci_cache.4
+usr/src/man/man4/pci_unitaddr_persistent.4
+usr/src/man/man4/pcie.4
+usr/src/man/man4/qop.4
+usr/src/man/man4/rhosts.4
+usr/src/man/man4/sendmail.cf.4
+usr/src/man/man4/snapshot_cache.4
+usr/src/man/man4/submit.cf.4
+usr/src/man/man4/volume-defaults.4
+usr/src/man/man4/wtmp.4
+usr/src/man/man4/wtmpx.4
+usr/src/man/man5/ANSI.5
+usr/src/man/man5/C++.5
+usr/src/man/man5/C.5
+usr/src/man/man5/CSI.5
+usr/src/man/man5/ISO.5
+usr/src/man/man5/MT-Level.5
+usr/src/man/man5/POSIX.1.5
+usr/src/man/man5/POSIX.2.5
+usr/src/man/man5/POSIX.5
+usr/src/man/man5/RBAC.5
+usr/src/man/man5/SUS.5
+usr/src/man/man5/SUSv2.5
+usr/src/man/man5/SUSv3.5
+usr/src/man/man5/SVID.5
+usr/src/man/man5/SVID3.5
+usr/src/man/man5/XNS.5
+usr/src/man/man5/XNS4.5
+usr/src/man/man5/XNS5.5
+usr/src/man/man5/XPG.5
+usr/src/man/man5/XPG3.5
+usr/src/man/man5/XPG4.5
+usr/src/man/man5/XPG4v2.5
+usr/src/man/man5/advance.5
+usr/src/man/man5/architecture.5
+usr/src/man/man5/availability.5
+usr/src/man/man5/compile.5
+usr/src/man/man5/intro.5
+usr/src/man/man5/pthreads.5
+usr/src/man/man5/stability.5
+usr/src/man/man5/standard.5
+usr/src/man/man5/step.5
+usr/src/man/man5/teclarc.5
+usr/src/man/man7/intro.7
+usr/src/man/man7d/1394.7d
+usr/src/man/man7d/allkmem.7d
+usr/src/man/man7d/bscbus.7d
+usr/src/man/man7d/fdc.7d
+usr/src/man/man7d/firewire.7d
+usr/src/man/man7d/hwa1480_fw.7d
+usr/src/man/man7d/i2bsc.7d
+usr/src/man/man7d/kmem.7d
+usr/src/man/man7d/lo0.7d
+usr/src/man/man7d/ticots.7d
+usr/src/man/man7d/ticotsord.7d
+usr/src/man/man7d/urandom.7d
+usr/src/man/man7d/usb.7d
+usr/src/man/man7d/uwb.7d
+usr/src/man/man7m/vuid2ps2.7m
+usr/src/man/man7m/vuid3ps2.7m
+usr/src/man/man7m/vuidm3p.7m
+usr/src/man/man7m/vuidm4p.7m
+usr/src/man/man7m/vuidm5p.7m
+usr/src/man/man7p/AH.7p
+usr/src/man/man7p/ARP.7p
+usr/src/man/man7p/ESP.7p
+usr/src/man/man7p/ICMP.7p
+usr/src/man/man7p/IP.7p
+usr/src/man/man7p/RARP.7p
+usr/src/man/man7p/SCTP.7p
+usr/src/man/man7p/TCP.7p
+usr/src/man/man7p/UDP.7p
+usr/src/man/man7p/if.7p
+usr/src/man/man9/Intro.9
+usr/src/man/man9/intro.9
+usr/src/man/man9e/_info.9e
+usr/src/man/man9e/_init.9e
+usr/src/man/man9e/gldm_get_stats.9e
+usr/src/man/man9e/gldm_intr.9e
+usr/src/man/man9e/gldm_ioctl.9e
+usr/src/man/man9e/gldm_reset.9e
+usr/src/man/man9e/gldm_send.9e
+usr/src/man/man9e/gldm_set_mac_addr.9e
+usr/src/man/man9e/gldm_set_multicast.9e
+usr/src/man/man9e/gldm_set_promiscuous.9e
+usr/src/man/man9e/gldm_start.9e
+usr/src/man/man9e/gldm_stop.9e
+usr/src/man/man9e/intro.9e
+usr/src/man/man9e/tran_destroy_pkt.9e
+usr/src/man/man9e/tran_pkt_constructor.9e
+usr/src/man/man9e/tran_pkt_destructor.9e
+usr/src/man/man9e/tran_setcap.9e
+usr/src/man/man9e/tran_teardown_pkt.9e
+usr/src/man/man9e/tran_unquiesce.9e
+usr/src/man/man9f/SIZEOF_PTR.9f
+usr/src/man/man9f/SIZEOF_STRUCT.9f
+usr/src/man/man9f/STRUCT_BUF.9f
+usr/src/man/man9f/STRUCT_FADDR.9f
+usr/src/man/man9f/STRUCT_FGET.9f
+usr/src/man/man9f/STRUCT_FGETP.9f
+usr/src/man/man9f/STRUCT_FSET.9f
+usr/src/man/man9f/STRUCT_FSETP.9f
+usr/src/man/man9f/STRUCT_HANDLE.9f
+usr/src/man/man9f/STRUCT_INIT.9f
+usr/src/man/man9f/STRUCT_SET_HANDLE.9f
+usr/src/man/man9f/STRUCT_SIZE.9f
+usr/src/man/man9f/assert.9f
+usr/src/man/man9f/atomic_add_16.9f
+usr/src/man/man9f/atomic_add_16_nv.9f
+usr/src/man/man9f/atomic_add_32.9f
+usr/src/man/man9f/atomic_add_32_nv.9f
+usr/src/man/man9f/atomic_add_64.9f
+usr/src/man/man9f/atomic_add_64_nv.9f
+usr/src/man/man9f/atomic_add_8.9f
+usr/src/man/man9f/atomic_add_8_nv.9f
+usr/src/man/man9f/atomic_add_char.9f
+usr/src/man/man9f/atomic_add_char_nv.9f
+usr/src/man/man9f/atomic_add_int.9f
+usr/src/man/man9f/atomic_add_int_nv.9f
+usr/src/man/man9f/atomic_add_long.9f
+usr/src/man/man9f/atomic_add_long_nv.9f
+usr/src/man/man9f/atomic_add_ptr.9f
+usr/src/man/man9f/atomic_add_ptr_nv.9f
+usr/src/man/man9f/atomic_add_short.9f
+usr/src/man/man9f/atomic_add_short_nv.9f
+usr/src/man/man9f/atomic_and_16.9f
+usr/src/man/man9f/atomic_and_16_nv.9f
+usr/src/man/man9f/atomic_and_32.9f
+usr/src/man/man9f/atomic_and_32_nv.9f
+usr/src/man/man9f/atomic_and_64.9f
+usr/src/man/man9f/atomic_and_64_nv.9f
+usr/src/man/man9f/atomic_and_8.9f
+usr/src/man/man9f/atomic_and_8_nv.9f
+usr/src/man/man9f/atomic_and_uchar.9f
+usr/src/man/man9f/atomic_and_uchar_nv.9f
+usr/src/man/man9f/atomic_and_uint.9f
+usr/src/man/man9f/atomic_and_uint_nv.9f
+usr/src/man/man9f/atomic_and_ulong.9f
+usr/src/man/man9f/atomic_and_ulong_nv.9f
+usr/src/man/man9f/atomic_and_ushort.9f
+usr/src/man/man9f/atomic_and_ushort_nv.9f
+usr/src/man/man9f/atomic_cas_16.9f
+usr/src/man/man9f/atomic_cas_32.9f
+usr/src/man/man9f/atomic_cas_64.9f
+usr/src/man/man9f/atomic_cas_8.9f
+usr/src/man/man9f/atomic_cas_ptr.9f
+usr/src/man/man9f/atomic_cas_uchar.9f
+usr/src/man/man9f/atomic_cas_uint.9f
+usr/src/man/man9f/atomic_cas_ulong.9f
+usr/src/man/man9f/atomic_cas_ushort.9f
+usr/src/man/man9f/atomic_clear_long_excl.9f
+usr/src/man/man9f/atomic_dec_16.9f
+usr/src/man/man9f/atomic_dec_16_nv.9f
+usr/src/man/man9f/atomic_dec_32.9f
+usr/src/man/man9f/atomic_dec_32_nv.9f
+usr/src/man/man9f/atomic_dec_64.9f
+usr/src/man/man9f/atomic_dec_64_nv.9f
+usr/src/man/man9f/atomic_dec_8.9f
+usr/src/man/man9f/atomic_dec_8_nv.9f
+usr/src/man/man9f/atomic_dec_ptr.9f
+usr/src/man/man9f/atomic_dec_ptr_nv.9f
+usr/src/man/man9f/atomic_dec_uchar.9f
+usr/src/man/man9f/atomic_dec_uchar_nv.9f
+usr/src/man/man9f/atomic_dec_uint.9f
+usr/src/man/man9f/atomic_dec_uint_nv.9f
+usr/src/man/man9f/atomic_dec_ulong.9f
+usr/src/man/man9f/atomic_dec_ulong_nv.9f
+usr/src/man/man9f/atomic_dec_ushort.9f
+usr/src/man/man9f/atomic_dec_ushort_nv.9f
+usr/src/man/man9f/atomic_inc_16.9f
+usr/src/man/man9f/atomic_inc_16_nv.9f
+usr/src/man/man9f/atomic_inc_32.9f
+usr/src/man/man9f/atomic_inc_32_nv.9f
+usr/src/man/man9f/atomic_inc_64.9f
+usr/src/man/man9f/atomic_inc_64_nv.9f
+usr/src/man/man9f/atomic_inc_8.9f
+usr/src/man/man9f/atomic_inc_8_nv.9f
+usr/src/man/man9f/atomic_inc_ptr.9f
+usr/src/man/man9f/atomic_inc_ptr_nv.9f
+usr/src/man/man9f/atomic_inc_uchar.9f
+usr/src/man/man9f/atomic_inc_uchar_nv.9f
+usr/src/man/man9f/atomic_inc_uint.9f
+usr/src/man/man9f/atomic_inc_uint_nv.9f
+usr/src/man/man9f/atomic_inc_ulong.9f
+usr/src/man/man9f/atomic_inc_ulong_nv.9f
+usr/src/man/man9f/atomic_inc_ushort.9f
+usr/src/man/man9f/atomic_inc_ushort_nv.9f
+usr/src/man/man9f/atomic_or_16.9f
+usr/src/man/man9f/atomic_or_16_nv.9f
+usr/src/man/man9f/atomic_or_32.9f
+usr/src/man/man9f/atomic_or_32_nv.9f
+usr/src/man/man9f/atomic_or_64.9f
+usr/src/man/man9f/atomic_or_64_nv.9f
+usr/src/man/man9f/atomic_or_8.9f
+usr/src/man/man9f/atomic_or_8_nv.9f
+usr/src/man/man9f/atomic_or_uchar.9f
+usr/src/man/man9f/atomic_or_uchar_nv.9f
+usr/src/man/man9f/atomic_or_uint.9f
+usr/src/man/man9f/atomic_or_uint_nv.9f
+usr/src/man/man9f/atomic_or_ulong.9f
+usr/src/man/man9f/atomic_or_ulong_nv.9f
+usr/src/man/man9f/atomic_or_ushort.9f
+usr/src/man/man9f/atomic_or_ushort_nv.9f
+usr/src/man/man9f/atomic_set_long_excl.9f
+usr/src/man/man9f/atomic_swap_16.9f
+usr/src/man/man9f/atomic_swap_32.9f
+usr/src/man/man9f/atomic_swap_64.9f
+usr/src/man/man9f/atomic_swap_8.9f
+usr/src/man/man9f/atomic_swap_ptr.9f
+usr/src/man/man9f/atomic_swap_uchar.9f
+usr/src/man/man9f/atomic_swap_uint.9f
+usr/src/man/man9f/atomic_swap_ulong.9f
+usr/src/man/man9f/atomic_swap_ushort.9f
+usr/src/man/man9f/crgetgid.9f
+usr/src/man/man9f/crgetgroups.9f
+usr/src/man/man9f/crgetngroups.9f
+usr/src/man/man9f/crgetrgid.9f
+usr/src/man/man9f/crgetruid.9f
+usr/src/man/man9f/crgetsgid.9f
+usr/src/man/man9f/crgetsuid.9f
+usr/src/man/man9f/crgetuid.9f
+usr/src/man/man9f/crgetzoneid.9f
+usr/src/man/man9f/csx_Get16.9f
+usr/src/man/man9f/csx_Get32.9f
+usr/src/man/man9f/csx_Get64.9f
+usr/src/man/man9f/csx_GetEventMask.9f
+usr/src/man/man9f/csx_GetNextClient.9f
+usr/src/man/man9f/csx_GetNextTuple.9f
+usr/src/man/man9f/csx_Parse_CISTPL_DEVICE_A.9f
+usr/src/man/man9f/csx_Parse_CISTPL_DEVICE_OA.9f
+usr/src/man/man9f/csx_Parse_CISTPL_DEVICE_OC.9f
+usr/src/man/man9f/csx_Parse_CISTPL_JEDEC_A.9f
+usr/src/man/man9f/csx_Parse_CISTPL_LONGLINK_C.9f
+usr/src/man/man9f/csx_Put16.9f
+usr/src/man/man9f/csx_Put32.9f
+usr/src/man/man9f/csx_Put64.9f
+usr/src/man/man9f/csx_ReleaseIO.9f
+usr/src/man/man9f/csx_ReleaseIRQ.9f
+usr/src/man/man9f/csx_ReleaseSocketMask.9f
+usr/src/man/man9f/csx_ReleaseWindow.9f
+usr/src/man/man9f/csx_RemoveDeviceNode.9f
+usr/src/man/man9f/csx_RepGet16.9f
+usr/src/man/man9f/csx_RepGet32.9f
+usr/src/man/man9f/csx_RepGet64.9f
+usr/src/man/man9f/csx_RepPut16.9f
+usr/src/man/man9f/csx_RepPut32.9f
+usr/src/man/man9f/csx_RepPut64.9f
+usr/src/man/man9f/cv_broadcast.9f
+usr/src/man/man9f/cv_destroy.9f
+usr/src/man/man9f/cv_init.9f
+usr/src/man/man9f/cv_reltimedwait.9f
+usr/src/man/man9f/cv_reltimedwait_sig.9f
+usr/src/man/man9f/cv_signal.9f
+usr/src/man/man9f/cv_timedwait.9f
+usr/src/man/man9f/cv_timedwait_sig.9f
+usr/src/man/man9f/cv_wait.9f
+usr/src/man/man9f/cv_wait_sig.9f
+usr/src/man/man9f/ddi_btopr.9f
+usr/src/man/man9f/ddi_cb_unregister.9f
+usr/src/man/man9f/ddi_check_dma_handle.9f
+usr/src/man/man9f/ddi_devid_free.9f
+usr/src/man/man9f/ddi_devid_get.9f
+usr/src/man/man9f/ddi_devid_init.9f
+usr/src/man/man9f/ddi_devid_register.9f
+usr/src/man/man9f/ddi_devid_sizeof.9f
+usr/src/man/man9f/ddi_devid_str_decode.9f
+usr/src/man/man9f/ddi_devid_str_encode.9f
+usr/src/man/man9f/ddi_devid_str_free.9f
+usr/src/man/man9f/ddi_devid_unregister.9f
+usr/src/man/man9f/ddi_devid_valid.9f
+usr/src/man/man9f/ddi_devmap_segmap.9f
+usr/src/man/man9f/ddi_dmae_1stparty.9f
+usr/src/man/man9f/ddi_dmae_alloc.9f
+usr/src/man/man9f/ddi_dmae_disable.9f
+usr/src/man/man9f/ddi_dmae_enable.9f
+usr/src/man/man9f/ddi_dmae_getattr.9f
+usr/src/man/man9f/ddi_dmae_getcnt.9f
+usr/src/man/man9f/ddi_dmae_getlim.9f
+usr/src/man/man9f/ddi_dmae_prog.9f
+usr/src/man/man9f/ddi_dmae_release.9f
+usr/src/man/man9f/ddi_dmae_stop.9f
+usr/src/man/man9f/ddi_exit_critical.9f
+usr/src/man/man9f/ddi_fls.9f
+usr/src/man/man9f/ddi_fm_capable.9f
+usr/src/man/man9f/ddi_fm_dma_err_clear.9f
+usr/src/man/man9f/ddi_fm_dma_err_get.9f
+usr/src/man/man9f/ddi_fm_fini.9f
+usr/src/man/man9f/ddi_fm_handler_unregister.9f
+usr/src/man/man9f/ddi_get16.9f
+usr/src/man/man9f/ddi_get32.9f
+usr/src/man/man9f/ddi_get64.9f
+usr/src/man/man9f/ddi_get_iblock_cookie.9f
+usr/src/man/man9f/ddi_get_lbolt64.9f
+usr/src/man/man9f/ddi_get_name.9f
+usr/src/man/man9f/ddi_get_soft_iblock_cookie.9f
+usr/src/man/man9f/ddi_get_soft_state.9f
+usr/src/man/man9f/ddi_getb.9f
+usr/src/man/man9f/ddi_getl.9f
+usr/src/man/man9f/ddi_getll.9f
+usr/src/man/man9f/ddi_getlongprop.9f
+usr/src/man/man9f/ddi_getlongprop_buf.9f
+usr/src/man/man9f/ddi_getprop.9f
+usr/src/man/man9f/ddi_getproplen.9f
+usr/src/man/man9f/ddi_getw.9f
+usr/src/man/man9f/ddi_intr_block_disable.9f
+usr/src/man/man9f/ddi_intr_block_enable.9f
+usr/src/man/man9f/ddi_intr_clr_mask.9f
+usr/src/man/man9f/ddi_intr_disable.9f
+usr/src/man/man9f/ddi_intr_free.9f
+usr/src/man/man9f/ddi_intr_get_navail.9f
+usr/src/man/man9f/ddi_intr_get_softint_pri.9f
+usr/src/man/man9f/ddi_intr_remove_handler.9f
+usr/src/man/man9f/ddi_intr_remove_softint.9f
+usr/src/man/man9f/ddi_intr_set_cap.9f
+usr/src/man/man9f/ddi_intr_set_pri.9f
+usr/src/man/man9f/ddi_intr_set_softint_pri.9f
+usr/src/man/man9f/ddi_intr_trigger_softint.9f
+usr/src/man/man9f/ddi_io_get16.9f
+usr/src/man/man9f/ddi_io_get32.9f
+usr/src/man/man9f/ddi_io_getb.9f
+usr/src/man/man9f/ddi_io_getl.9f
+usr/src/man/man9f/ddi_io_getw.9f
+usr/src/man/man9f/ddi_io_put16.9f
+usr/src/man/man9f/ddi_io_put32.9f
+usr/src/man/man9f/ddi_io_putb.9f
+usr/src/man/man9f/ddi_io_putl.9f
+usr/src/man/man9f/ddi_io_putw.9f
+usr/src/man/man9f/ddi_io_rep_get16.9f
+usr/src/man/man9f/ddi_io_rep_get32.9f
+usr/src/man/man9f/ddi_io_rep_getb.9f
+usr/src/man/man9f/ddi_io_rep_getl.9f
+usr/src/man/man9f/ddi_io_rep_getw.9f
+usr/src/man/man9f/ddi_io_rep_put16.9f
+usr/src/man/man9f/ddi_io_rep_put32.9f
+usr/src/man/man9f/ddi_io_rep_putb.9f
+usr/src/man/man9f/ddi_io_rep_putl.9f
+usr/src/man/man9f/ddi_io_rep_putw.9f
+usr/src/man/man9f/ddi_iopb_free.9f
+usr/src/man/man9f/ddi_mem_free.9f
+usr/src/man/man9f/ddi_mem_get16.9f
+usr/src/man/man9f/ddi_mem_get32.9f
+usr/src/man/man9f/ddi_mem_get64.9f
+usr/src/man/man9f/ddi_mem_getb.9f
+usr/src/man/man9f/ddi_mem_getl.9f
+usr/src/man/man9f/ddi_mem_getll.9f
+usr/src/man/man9f/ddi_mem_getw.9f
+usr/src/man/man9f/ddi_mem_put16.9f
+usr/src/man/man9f/ddi_mem_put32.9f
+usr/src/man/man9f/ddi_mem_put64.9f
+usr/src/man/man9f/ddi_mem_putb.9f
+usr/src/man/man9f/ddi_mem_putl.9f
+usr/src/man/man9f/ddi_mem_putll.9f
+usr/src/man/man9f/ddi_mem_putw.9f
+usr/src/man/man9f/ddi_mem_rep_get16.9f
+usr/src/man/man9f/ddi_mem_rep_get32.9f
+usr/src/man/man9f/ddi_mem_rep_get64.9f
+usr/src/man/man9f/ddi_mem_rep_getb.9f
+usr/src/man/man9f/ddi_mem_rep_getl.9f
+usr/src/man/man9f/ddi_mem_rep_getll.9f
+usr/src/man/man9f/ddi_mem_rep_getw.9f
+usr/src/man/man9f/ddi_mem_rep_put16.9f
+usr/src/man/man9f/ddi_mem_rep_put32.9f
+usr/src/man/man9f/ddi_mem_rep_put64.9f
+usr/src/man/man9f/ddi_mem_rep_putb.9f
+usr/src/man/man9f/ddi_mem_rep_putl.9f
+usr/src/man/man9f/ddi_mem_rep_putll.9f
+usr/src/man/man9f/ddi_mem_rep_putw.9f
+usr/src/man/man9f/ddi_modclose.9f
+usr/src/man/man9f/ddi_modsym.9f
+usr/src/man/man9f/ddi_peek16.9f
+usr/src/man/man9f/ddi_peek32.9f
+usr/src/man/man9f/ddi_peek64.9f
+usr/src/man/man9f/ddi_peek8.9f
+usr/src/man/man9f/ddi_peekc.9f
+usr/src/man/man9f/ddi_peekd.9f
+usr/src/man/man9f/ddi_peekl.9f
+usr/src/man/man9f/ddi_peeks.9f
+usr/src/man/man9f/ddi_poke16.9f
+usr/src/man/man9f/ddi_poke32.9f
+usr/src/man/man9f/ddi_poke64.9f
+usr/src/man/man9f/ddi_poke8.9f
+usr/src/man/man9f/ddi_pokec.9f
+usr/src/man/man9f/ddi_poked.9f
+usr/src/man/man9f/ddi_pokel.9f
+usr/src/man/man9f/ddi_pokes.9f
+usr/src/man/man9f/ddi_prop_free.9f
+usr/src/man/man9f/ddi_prop_get_int64.9f
+usr/src/man/man9f/ddi_prop_lookup_byte_array.9f
+usr/src/man/man9f/ddi_prop_lookup_int64_array.9f
+usr/src/man/man9f/ddi_prop_lookup_int_array.9f
+usr/src/man/man9f/ddi_prop_lookup_string.9f
+usr/src/man/man9f/ddi_prop_lookup_string_array.9f
+usr/src/man/man9f/ddi_prop_modify.9f
+usr/src/man/man9f/ddi_prop_remove.9f
+usr/src/man/man9f/ddi_prop_remove_all.9f
+usr/src/man/man9f/ddi_prop_undefine.9f
+usr/src/man/man9f/ddi_prop_update_byte_array.9f
+usr/src/man/man9f/ddi_prop_update_int.9f
+usr/src/man/man9f/ddi_prop_update_int64.9f
+usr/src/man/man9f/ddi_prop_update_int64_array.9f
+usr/src/man/man9f/ddi_prop_update_int_array.9f
+usr/src/man/man9f/ddi_prop_update_string.9f
+usr/src/man/man9f/ddi_prop_update_string_array.9f
+usr/src/man/man9f/ddi_ptob.9f
+usr/src/man/man9f/ddi_put16.9f
+usr/src/man/man9f/ddi_put32.9f
+usr/src/man/man9f/ddi_put64.9f
+usr/src/man/man9f/ddi_putb.9f
+usr/src/man/man9f/ddi_putl.9f
+usr/src/man/man9f/ddi_putll.9f
+usr/src/man/man9f/ddi_putw.9f
+usr/src/man/man9f/ddi_remove_intr.9f
+usr/src/man/man9f/ddi_remove_softintr.9f
+usr/src/man/man9f/ddi_rep_get16.9f
+usr/src/man/man9f/ddi_rep_get32.9f
+usr/src/man/man9f/ddi_rep_get64.9f
+usr/src/man/man9f/ddi_rep_getb.9f
+usr/src/man/man9f/ddi_rep_getl.9f
+usr/src/man/man9f/ddi_rep_getll.9f
+usr/src/man/man9f/ddi_rep_getw.9f
+usr/src/man/man9f/ddi_rep_put16.9f
+usr/src/man/man9f/ddi_rep_put32.9f
+usr/src/man/man9f/ddi_rep_put64.9f
+usr/src/man/man9f/ddi_rep_putb.9f
+usr/src/man/man9f/ddi_rep_putl.9f
+usr/src/man/man9f/ddi_rep_putll.9f
+usr/src/man/man9f/ddi_rep_putw.9f
+usr/src/man/man9f/ddi_segmap_setup.9f
+usr/src/man/man9f/ddi_set_driver_private.9f
+usr/src/man/man9f/ddi_soft_state_fini.9f
+usr/src/man/man9f/ddi_soft_state_free.9f
+usr/src/man/man9f/ddi_soft_state_init.9f
+usr/src/man/man9f/ddi_soft_state_zalloc.9f
+usr/src/man/man9f/ddi_strdup.9f
+usr/src/man/man9f/ddi_strtoull.9f
+usr/src/man/man9f/ddi_taskq_create.9f
+usr/src/man/man9f/ddi_taskq_destroy.9f
+usr/src/man/man9f/ddi_taskq_dispatch.9f
+usr/src/man/man9f/ddi_taskq_resume.9f
+usr/src/man/man9f/ddi_taskq_suspend.9f
+usr/src/man/man9f/ddi_taskq_wait.9f
+usr/src/man/man9f/ddi_trigger_softintr.9f
+usr/src/man/man9f/ddi_umem_free.9f
+usr/src/man/man9f/ddi_umem_unlock.9f
+usr/src/man/man9f/ddi_unmap_regs.9f
+usr/src/man/man9f/desballoc.9f
+usr/src/man/man9f/devmap_load.9f
+usr/src/man/man9f/devmap_umem_setup.9f
+usr/src/man/man9f/dlerrorack.9f
+usr/src/man/man9f/dlokack.9f
+usr/src/man/man9f/dlphysaddrack.9f
+usr/src/man/man9f/dluderrorind.9f
+usr/src/man/man9f/free_pktiopb.9f
+usr/src/man/man9f/gld_intr.9f
+usr/src/man/man9f/gld_mac_alloc.9f
+usr/src/man/man9f/gld_mac_free.9f
+usr/src/man/man9f/gld_recv.9f
+usr/src/man/man9f/gld_register.9f
+usr/src/man/man9f/gld_sched.9f
+usr/src/man/man9f/gld_unregister.9f
+usr/src/man/man9f/id32_free.9f
+usr/src/man/man9f/id32_lookup.9f
+usr/src/man/man9f/inl.9f
+usr/src/man/man9f/intro.9f
+usr/src/man/man9f/inw.9f
+usr/src/man/man9f/kmem_cache_alloc.9f
+usr/src/man/man9f/kmem_cache_destroy.9f
+usr/src/man/man9f/kmem_cache_free.9f
+usr/src/man/man9f/kmem_cache_set_move.9f
+usr/src/man/man9f/kmem_free.9f
+usr/src/man/man9f/kmem_zalloc.9f
+usr/src/man/man9f/kstat_named_setstr.9f
+usr/src/man/man9f/kstat_runq_back_to_waitq.9f
+usr/src/man/man9f/kstat_runq_enter.9f
+usr/src/man/man9f/kstat_runq_exit.9f
+usr/src/man/man9f/kstat_waitq_enter.9f
+usr/src/man/man9f/kstat_waitq_exit.9f
+usr/src/man/man9f/kstat_waitq_to_runq.9f
+usr/src/man/man9f/ldi_awrite.9f
+usr/src/man/man9f/ldi_close.9f
+usr/src/man/man9f/ldi_get_devid.9f
+usr/src/man/man9f/ldi_get_minor_name.9f
+usr/src/man/man9f/ldi_get_otyp.9f
+usr/src/man/man9f/ldi_getmsg.9f
+usr/src/man/man9f/ldi_ident_from_dip.9f
+usr/src/man/man9f/ldi_ident_from_stream.9f
+usr/src/man/man9f/ldi_ident_release.9f
+usr/src/man/man9f/ldi_open_by_devid.9f
+usr/src/man/man9f/ldi_open_by_name.9f
+usr/src/man/man9f/ldi_prop_get_int64.9f
+usr/src/man/man9f/ldi_prop_lookup_byte_array.9f
+usr/src/man/man9f/ldi_prop_lookup_int64_array.9f
+usr/src/man/man9f/ldi_prop_lookup_string.9f
+usr/src/man/man9f/ldi_prop_lookup_string_array.9f
+usr/src/man/man9f/ldi_write.9f
+usr/src/man/man9f/list_destroy.9f
+usr/src/man/man9f/list_head.9f
+usr/src/man/man9f/list_insert_after.9f
+usr/src/man/man9f/list_insert_before.9f
+usr/src/man/man9f/list_insert_head.9f
+usr/src/man/man9f/list_insert_tail.9f
+usr/src/man/man9f/list_is_empty.9f
+usr/src/man/man9f/list_link_active.9f
+usr/src/man/man9f/list_link_init.9f
+usr/src/man/man9f/list_link_replace.9f
+usr/src/man/man9f/list_move_tail.9f
+usr/src/man/man9f/list_next.9f
+usr/src/man/man9f/list_prev.9f
+usr/src/man/man9f/list_remove.9f
+usr/src/man/man9f/list_remove_head.9f
+usr/src/man/man9f/list_remove_tail.9f
+usr/src/man/man9f/list_tail.9f
+usr/src/man/man9f/makecom_g0.9f
+usr/src/man/man9f/makecom_g0_s.9f
+usr/src/man/man9f/makecom_g1.9f
+usr/src/man/man9f/makecom_g5.9f
+usr/src/man/man9f/membar_consumer.9f
+usr/src/man/man9f/membar_enter.9f
+usr/src/man/man9f/membar_exit.9f
+usr/src/man/man9f/membar_producer.9f
+usr/src/man/man9f/memcmp.9f
+usr/src/man/man9f/memcpy.9f
+usr/src/man/man9f/memmove.9f
+usr/src/man/man9f/memset.9f
+usr/src/man/man9f/minphys.9f
+usr/src/man/man9f/mod_info.9f
+usr/src/man/man9f/mod_modname.9f
+usr/src/man/man9f/mod_remove.9f
+usr/src/man/man9f/mutex_destroy.9f
+usr/src/man/man9f/mutex_enter.9f
+usr/src/man/man9f/mutex_exit.9f
+usr/src/man/man9f/mutex_init.9f
+usr/src/man/man9f/mutex_owned.9f
+usr/src/man/man9f/mutex_tryenter.9f
+usr/src/man/man9f/net_event_notify_unregister.9f
+usr/src/man/man9f/net_instance_notify_unregister.9f
+usr/src/man/man9f/net_instance_protocol_unregister.9f
+usr/src/man/man9f/numtos.9f
+usr/src/man/man9f/nv_alloc_fini.9f
+usr/src/man/man9f/nv_alloc_init.9f
+usr/src/man/man9f/nvlist_add_boolean_array.9f
+usr/src/man/man9f/nvlist_add_boolean_value.9f
+usr/src/man/man9f/nvlist_add_byte.9f
+usr/src/man/man9f/nvlist_add_byte_array.9f
+usr/src/man/man9f/nvlist_add_int16.9f
+usr/src/man/man9f/nvlist_add_int16_array.9f
+usr/src/man/man9f/nvlist_add_int32.9f
+usr/src/man/man9f/nvlist_add_int32_array.9f
+usr/src/man/man9f/nvlist_add_int64.9f
+usr/src/man/man9f/nvlist_add_int64_array.9f
+usr/src/man/man9f/nvlist_add_int8.9f
+usr/src/man/man9f/nvlist_add_int8_array.9f
+usr/src/man/man9f/nvlist_add_nvlist.9f
+usr/src/man/man9f/nvlist_add_nvlist_array.9f
+usr/src/man/man9f/nvlist_add_nvpair.9f
+usr/src/man/man9f/nvlist_add_string.9f
+usr/src/man/man9f/nvlist_add_string_array.9f
+usr/src/man/man9f/nvlist_add_uint16.9f
+usr/src/man/man9f/nvlist_add_uint16_array.9f
+usr/src/man/man9f/nvlist_add_uint32.9f
+usr/src/man/man9f/nvlist_add_uint32_array.9f
+usr/src/man/man9f/nvlist_add_uint64.9f
+usr/src/man/man9f/nvlist_add_uint64_array.9f
+usr/src/man/man9f/nvlist_add_uint8.9f
+usr/src/man/man9f/nvlist_add_uint8_array.9f
+usr/src/man/man9f/nvlist_dup.9f
+usr/src/man/man9f/nvlist_exists.9f
+usr/src/man/man9f/nvlist_free.9f
+usr/src/man/man9f/nvlist_lookup_boolean_array.9f
+usr/src/man/man9f/nvlist_lookup_boolean_value.9f
+usr/src/man/man9f/nvlist_lookup_byte.9f
+usr/src/man/man9f/nvlist_lookup_byte_array.9f
+usr/src/man/man9f/nvlist_lookup_int16.9f
+usr/src/man/man9f/nvlist_lookup_int16_array.9f
+usr/src/man/man9f/nvlist_lookup_int32.9f
+usr/src/man/man9f/nvlist_lookup_int32_array.9f
+usr/src/man/man9f/nvlist_lookup_int64.9f
+usr/src/man/man9f/nvlist_lookup_int64_array.9f
+usr/src/man/man9f/nvlist_lookup_int8.9f
+usr/src/man/man9f/nvlist_lookup_int8_array.9f
+usr/src/man/man9f/nvlist_lookup_nvlist.9f
+usr/src/man/man9f/nvlist_lookup_nvlist_array.9f
+usr/src/man/man9f/nvlist_lookup_pairs.9f
+usr/src/man/man9f/nvlist_lookup_string.9f
+usr/src/man/man9f/nvlist_lookup_string_array.9f
+usr/src/man/man9f/nvlist_lookup_uint16.9f
+usr/src/man/man9f/nvlist_lookup_uint16_array.9f
+usr/src/man/man9f/nvlist_lookup_uint32.9f
+usr/src/man/man9f/nvlist_lookup_uint32_array.9f
+usr/src/man/man9f/nvlist_lookup_uint64.9f
+usr/src/man/man9f/nvlist_lookup_uint64_array.9f
+usr/src/man/man9f/nvlist_lookup_uint8.9f
+usr/src/man/man9f/nvlist_lookup_uint8_array.9f
+usr/src/man/man9f/nvlist_merge.9f
+usr/src/man/man9f/nvlist_pack.9f
+usr/src/man/man9f/nvlist_remove_all.9f
+usr/src/man/man9f/nvlist_size.9f
+usr/src/man/man9f/nvlist_t.9f
+usr/src/man/man9f/nvlist_unpack.9f
+usr/src/man/man9f/nvlist_xalloc.9f
+usr/src/man/man9f/nvlist_xdup.9f
+usr/src/man/man9f/nvlist_xpack.9f
+usr/src/man/man9f/nvlist_xunpack.9f
+usr/src/man/man9f/nvpair_name.9f
+usr/src/man/man9f/nvpair_type.9f
+usr/src/man/man9f/nvpair_value_boolean_array.9f
+usr/src/man/man9f/nvpair_value_byte_array.9f
+usr/src/man/man9f/nvpair_value_int16.9f
+usr/src/man/man9f/nvpair_value_int16_array.9f
+usr/src/man/man9f/nvpair_value_int32.9f
+usr/src/man/man9f/nvpair_value_int32_array.9f
+usr/src/man/man9f/nvpair_value_int64.9f
+usr/src/man/man9f/nvpair_value_int64_array.9f
+usr/src/man/man9f/nvpair_value_int8.9f
+usr/src/man/man9f/nvpair_value_int8_array.9f
+usr/src/man/man9f/nvpair_value_nvlist.9f
+usr/src/man/man9f/nvpair_value_nvlist_array.9f
+usr/src/man/man9f/nvpair_value_string.9f
+usr/src/man/man9f/nvpair_value_string_array.9f
+usr/src/man/man9f/nvpair_value_uint16.9f
+usr/src/man/man9f/nvpair_value_uint16_array.9f
+usr/src/man/man9f/nvpair_value_uint32.9f
+usr/src/man/man9f/nvpair_value_uint32_array.9f
+usr/src/man/man9f/nvpair_value_uint64.9f
+usr/src/man/man9f/nvpair_value_uint64_array.9f
+usr/src/man/man9f/nvpair_value_uint8.9f
+usr/src/man/man9f/nvpair_value_uint8_array.9f
+usr/src/man/man9f/otherq.9f
+usr/src/man/man9f/outl.9f
+usr/src/man/man9f/outw.9f
+usr/src/man/man9f/pci_config_get16.9f
+usr/src/man/man9f/pci_config_get32.9f
+usr/src/man/man9f/pci_config_get64.9f
+usr/src/man/man9f/pci_config_getb.9f
+usr/src/man/man9f/pci_config_getl.9f
+usr/src/man/man9f/pci_config_getll.9f
+usr/src/man/man9f/pci_config_getw.9f
+usr/src/man/man9f/pci_config_put16.9f
+usr/src/man/man9f/pci_config_put32.9f
+usr/src/man/man9f/pci_config_put64.9f
+usr/src/man/man9f/pci_config_put8.9f
+usr/src/man/man9f/pci_config_putb.9f
+usr/src/man/man9f/pci_config_putl.9f
+usr/src/man/man9f/pci_config_putll.9f
+usr/src/man/man9f/pci_config_putw.9f
+usr/src/man/man9f/pci_config_teardown.9f
+usr/src/man/man9f/pci_ereport_post.9f
+usr/src/man/man9f/pci_ereport_teardown.9f
+usr/src/man/man9f/pci_restore_config_regs.9f
+usr/src/man/man9f/pm_idle_component.9f
+usr/src/man/man9f/pm_lower_power.9f
+usr/src/man/man9f/priv_policy_choice.9f
+usr/src/man/man9f/priv_policy_only.9f
+usr/src/man/man9f/proc_ref.9f
+usr/src/man/man9f/proc_unref.9f
+usr/src/man/man9f/qprocsoff.9f
+usr/src/man/man9f/qwait_sig.9f
+usr/src/man/man9f/rd.9f
+usr/src/man/man9f/repinsb.9f
+usr/src/man/man9f/repinsd.9f
+usr/src/man/man9f/repinsw.9f
+usr/src/man/man9f/repoutsb.9f
+usr/src/man/man9f/repoutsd.9f
+usr/src/man/man9f/repoutsw.9f
+usr/src/man/man9f/rmallocmap_wait.9f
+usr/src/man/man9f/rmfreemap.9f
+usr/src/man/man9f/rw_destroy.9f
+usr/src/man/man9f/rw_downgrade.9f
+usr/src/man/man9f/rw_enter.9f
+usr/src/man/man9f/rw_exit.9f
+usr/src/man/man9f/rw_init.9f
+usr/src/man/man9f/rw_read_locked.9f
+usr/src/man/man9f/rw_tryenter.9f
+usr/src/man/man9f/rw_tryupgrade.9f
+usr/src/man/man9f/samestr.9f
+usr/src/man/man9f/scsi_dmafree.9f
+usr/src/man/man9f/scsi_dname.9f
+usr/src/man/man9f/scsi_hba_attach.9f
+usr/src/man/man9f/scsi_hba_detach.9f
+usr/src/man/man9f/scsi_hba_fini.9f
+usr/src/man/man9f/scsi_hba_pkt_free.9f
+usr/src/man/man9f/scsi_hba_tran_free.9f
+usr/src/man/man9f/scsi_ifsetcap.9f
+usr/src/man/man9f/scsi_mname.9f
+usr/src/man/man9f/scsi_pktfree.9f
+usr/src/man/man9f/scsi_resalloc.9f
+usr/src/man/man9f/scsi_resfree.9f
+usr/src/man/man9f/scsi_rname.9f
+usr/src/man/man9f/scsi_sense_asc.9f
+usr/src/man/man9f/scsi_sense_ascq.9f
+usr/src/man/man9f/scsi_sense_cmdspecific_uint64.9f
+usr/src/man/man9f/scsi_sense_info_uint64.9f
+usr/src/man/man9f/scsi_sname.9f
+usr/src/man/man9f/scsi_unslave.9f
+usr/src/man/man9f/sema_destroy.9f
+usr/src/man/man9f/sema_init.9f
+usr/src/man/man9f/sema_p.9f
+usr/src/man/man9f/sema_p_sig.9f
+usr/src/man/man9f/sema_tryp.9f
+usr/src/man/man9f/sema_v.9f
+usr/src/man/man9f/strcasecmp.9f
+usr/src/man/man9f/strchr.9f
+usr/src/man/man9f/strcmp.9f
+usr/src/man/man9f/strcpy.9f
+usr/src/man/man9f/strdup.9f
+usr/src/man/man9f/strfree.9f
+usr/src/man/man9f/strlcat.9f
+usr/src/man/man9f/strlcpy.9f
+usr/src/man/man9f/strlen.9f
+usr/src/man/man9f/strncasecmp.9f
+usr/src/man/man9f/strncat.9f
+usr/src/man/man9f/strncmp.9f
+usr/src/man/man9f/strncpy.9f
+usr/src/man/man9f/strnlen.9f
+usr/src/man/man9f/strrchr.9f
+usr/src/man/man9f/strspn.9f
+usr/src/man/man9f/taskq_suspended.9f
+usr/src/man/man9f/uconv_u16tou8.9f
+usr/src/man/man9f/uconv_u32tou16.9f
+usr/src/man/man9f/uconv_u32tou8.9f
+usr/src/man/man9f/uconv_u8tou16.9f
+usr/src/man/man9f/uconv_u8tou32.9f
+usr/src/man/man9f/unfreezestr.9f
+usr/src/man/man9f/usb_alloc_bulk_req.9f
+usr/src/man/man9f/usb_alloc_ctrl_req.9f
+usr/src/man/man9f/usb_alloc_intr_req.9f
+usr/src/man/man9f/usb_alloc_isoc_req.9f
+usr/src/man/man9f/usb_client_detach.9f
+usr/src/man/man9f/usb_free_bulk_req.9f
+usr/src/man/man9f/usb_free_ctrl_req.9f
+usr/src/man/man9f/usb_free_descr_tree.9f
+usr/src/man/man9f/usb_free_dev_data.9f
+usr/src/man/man9f/usb_free_intr_req.9f
+usr/src/man/man9f/usb_free_isoc_req.9f
+usr/src/man/man9f/usb_get_if_number.9f
+usr/src/man/man9f/usb_owns_device.9f
+usr/src/man/man9f/usb_pipe_ctrl_xfer_wait.9f
+usr/src/man/man9f/usb_pipe_get_private.9f
+usr/src/man/man9f/usb_pipe_stop_intr_polling.9f
+usr/src/man/man9f/usb_pipe_stop_isoc_polling.9f
+usr/src/man/man9f/usb_print_descr_tree.9f
+usr/src/man/man9f/usb_set_alt_if.9f
+usr/src/man/man9f/usb_set_cfg.9f
+usr/src/man/man9f/usb_unregister_hotplug_cbs.9f
+usr/src/man/man9f/va_copy.9f
+usr/src/man/man9f/va_end.9f
+usr/src/man/man9f/va_start.9f
+usr/src/man/man9f/vcmn_err.9f
+usr/src/man/man9f/wr.9f
+usr/src/man/man9f/zcmn_err.9f
+usr/src/man/man9p/Nblock.9p
+usr/src/man/man9p/blksize.9p
+usr/src/man/man9p/ddi-no-autodetach.9p
+usr/src/man/man9p/device-blksize.9p
+usr/src/man/man9p/device-nblocks.9p
+usr/src/man/man9p/inquiry-product-id.9p
+usr/src/man/man9p/inquiry-revision-id.9p
+usr/src/man/man9p/inquiry-serial-no.9p
+usr/src/man/man9p/inquiry-vendor-id.9p
+usr/src/man/man9s/dblk.9s
+usr/src/man/man9s/ddi_dma_lim.9s
+usr/src/man/man9s/intro.9s
+usr/src/man/man9s/mblk.9s
+usr/src/msg/magic
+usr/src/msg/mailx.help
+usr/src/msg/more.help
+usr/src/msg/priv_names
+usr/src/pkg/license-list
+usr/src/pkg/packages.i386/
+usr/src/psm/stand/bootblks/ufs/i386/installboot
+usr/src/psm/stand/bootblks/ufs/i386/mboot
+usr/src/psm/stand/bootblks/ufs/i386/mboot.exec
+usr/src/tools/aw/aw
+usr/src/tools/codereview/codereview
+usr/src/tools/codesign/findcrypto
+usr/src/tools/codesign/signit
+usr/src/tools/codesign/signproto
+usr/src/tools/cscope-fast/cscope-fast
+usr/src/tools/ctf/cvt/i386/ctfconvert
+usr/src/tools/ctf/cvt/i386/ctfmerge
+usr/src/tools/ctf/dump/i386/ctfdump
+usr/src/tools/ctf/scripts/ctfcvtptbl
+usr/src/tools/ctf/scripts/ctffindmod
+usr/src/tools/ctf/stabs/i386/ctfstabs
+usr/src/tools/cw/cw
+usr/src/tools/elfextract/elfextract
+usr/src/tools/elfsign/elfsign
+usr/src/tools/elfsign/elfsigncmp
+usr/src/tools/env/developer
+usr/src/tools/env/gatekeeper
+usr/src/tools/env/illumos
+usr/src/tools/findunref/exception_list
+usr/src/tools/findunref/findunref
+usr/src/tools/gk/cshrc
+usr/src/tools/gk/gen_make.machines
+usr/src/tools/gk/login
+usr/src/tools/install-i386.out
+usr/src/tools/install.bin/install.bin
+usr/src/tools/lintdump/lintdump
+usr/src/tools/mbh_patch/mbh_patch
+usr/src/tools/ndrgen/ndrgen
+usr/src/tools/ndrgen/ndrgen1
+usr/src/tools/ndrgen/y.tab.c
+usr/src/tools/ndrgen/y.tab.h
+usr/src/tools/pmodes/pmodes
+usr/src/tools/proto/
+usr/src/tools/protocmp/protocmp
+usr/src/tools/protolist/protolist
+usr/src/tools/scripts/Install
+usr/src/tools/scripts/bindrop
+usr/src/tools/scripts/bldenv
+usr/src/tools/scripts/bldenv.1
+usr/src/tools/scripts/bringovercheck
+usr/src/tools/scripts/build_cscope
+usr/src/tools/scripts/cddlchk
+usr/src/tools/scripts/check_rtime
+usr/src/tools/scripts/checkpaths
+usr/src/tools/scripts/checkproto
+usr/src/tools/scripts/copyrightchk
+usr/src/tools/scripts/cryptodrop
+usr/src/tools/scripts/cstyle
+usr/src/tools/scripts/elfcmp
+usr/src/tools/scripts/find_elf
+usr/src/tools/scripts/flg.flp
+usr/src/tools/scripts/genoffsets
+usr/src/tools/scripts/git-active
+usr/src/tools/scripts/hdrchk
+usr/src/tools/scripts/hg-active
+usr/src/tools/scripts/hgsetup
+usr/src/tools/scripts/interface_check
+usr/src/tools/scripts/interface_cmp
+usr/src/tools/scripts/jstyle
+usr/src/tools/scripts/mapfilechk
+usr/src/tools/scripts/mkreadme_osol
+usr/src/tools/scripts/mktpl
+usr/src/tools/scripts/nightly
+usr/src/tools/scripts/onu
+usr/src/tools/scripts/protocmp.terse
+usr/src/tools/scripts/sccscheck
+usr/src/tools/scripts/validate_flg
+usr/src/tools/scripts/validate_paths
+usr/src/tools/scripts/validate_pkg
+usr/src/tools/scripts/wdiff
+usr/src/tools/scripts/webrev
+usr/src/tools/scripts/which_scm
+usr/src/tools/scripts/ws
+usr/src/tools/scripts/wsdiff
+usr/src/tools/scripts/xref
+usr/src/ucbcmd/basename/basename
+usr/src/ucbcmd/biff/biff
+usr/src/ucbcmd/chown/chown
+usr/src/ucbcmd/df/df
+usr/src/ucbcmd/du/du
+usr/src/ucbcmd/echo/echo
+usr/src/ucbcmd/expr/expr
+usr/src/ucbcmd/fastboot/fastboot
+usr/src/ucbcmd/fasthalt/fasthalt
+usr/src/ucbcmd/file/file
+usr/src/ucbcmd/from/from
+usr/src/ucbcmd/groups/groups
+usr/src/ucbcmd/install.d/installcmd
+usr/src/ucbcmd/ln/ln
+usr/src/ucbcmd/ls/ls
+usr/src/ucbcmd/mkstr/mkstr
+usr/src/ucbcmd/printenv/printenv
+usr/src/ucbcmd/rusage/rusage
+usr/src/ucbcmd/sed/sed
+usr/src/ucbcmd/shutdown/shutdown
+usr/src/ucbcmd/stty/stty
+usr/src/ucbcmd/sum/sum
+usr/src/ucbcmd/test/test
+usr/src/ucbcmd/touch/touch
+usr/src/ucbcmd/tr/tr
+usr/src/ucbcmd/tset/tset
+usr/src/ucbcmd/users/users
+usr/src/ucbcmd/vipw/vipw
+usr/src/ucbcmd/whereis/whereis
+usr/src/ucbcmd/whoami/whoami
+usr/src/uts/common/avs/ns/rdc/rdc_prot.h
+usr/src/uts/common/gssapi/gssd_prot.c
+usr/src/uts/common/gssapi/gssd_prot.h
+usr/src/uts/common/gssapi/gssd_xdr.c
+usr/src/uts/common/idmap/idmap_xdr.c
+usr/src/uts/common/io/fibre-channel/fca/qlc/ql_fw_table.c
+usr/src/uts/common/io/lvm/md/meta_basic_xdr.c
+usr/src/uts/common/io/lvm/md/metamed_xdr.c
+usr/src/uts/common/io/usb/clients/hwa1480_fw/i1480/hwa1480_fw
+usr/src/uts/common/os/priv_const.c
+usr/src/uts/common/rpc/key_prot.h
+usr/src/uts/common/rpc/pmap_prot.h
+usr/src/uts/common/rpc/rpc_sztypes.h
+usr/src/uts/common/rpc/rpcb_prot.h
+usr/src/uts/common/rpcsvc/autofs_prot.h
+usr/src/uts/common/rpcsvc/idmap_prot.h
+usr/src/uts/common/rpcsvc/nsm_addr.h
+usr/src/uts/common/rpcsvc/sm_inter.h
+usr/src/uts/common/sys/lvm/md_basic.h
+usr/src/uts/common/sys/lvm/md_mdiox.h
+usr/src/uts/common/sys/lvm/md_mhdx.h
+usr/src/uts/common/sys/lvm/mdmed.h
+usr/src/uts/common/sys/lvm/mdmn_commd.h
+usr/src/uts/common/sys/priv_const.h
+usr/src/uts/common/sys/priv_names.h
+usr/src/uts/common/sys/usb/usbdevs.h
+usr/src/uts/i86pc/cpu/scripts/ao_gendisp
+usr/src/uts/i86pc/io/acpi/drmach_acpi/drmach_err.c
+usr/src/uts/i86pc/io/acpi/drmach_acpi/sbdgenerr
+usr/src/uts/i86pc/io/dr/dr_err.c
+usr/src/uts/i86pc/io/dr/sbdgenerr
+usr/src/uts/i86pc/unix/multiboot
+usr/src/uts/intel/arcfour/arcfour-x86_64.s
+usr/src/uts/intel/genunix/debug32/
+usr/src/uts/intel/genunix/debug64/
+usr/src/uts/intel/ip/ip.symbols.obj64.diff
+usr/src/uts/intel/ip/ip.symbols.obj64.tmp
+usr/src/uts/intel/ip/ip.symbols.obj64.tmp.new
+usr/src/uts/intel/md5/md5_amd64.s
+usr/src/uts/intel/mwlfw/mw88W8363fw
+usr/src/uts/intel/mwlfw/mwlboot
+usr/src/uts/intel/os/priv_names
+usr/src/uts/intel/sha1/sha1-x86_64.s
+usr/src/uts/intel/sha2/sha256-x86_64.s
+usr/src/uts/intel/sha2/sha512-x86_64.s
+usr/src/uts/intel/sockfs/nl7ctokgen.h
+usr/src/uts/intel/uathfw/uathbin
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000000..3d942e46ca
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,63 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * Copyright 2021 Joyent, Inc.
+ * Copyright 2022 MNX Cloud, Inc.
+ */
+
+@Library('jenkins-joylib@v1.0.8') _
+
+pipeline {
+
+ agent {
+ label 'platform:true && image_ver:21.4.0 && pkgsrc_arch:x86_64 && ' +
+ 'dram:16gb && !virt:kvm && fs:pcfs && fs:ufs && jenkins_agent:3'
+ }
+
+ options {
+ buildDiscarder(logRotator(numToKeepStr: '30'))
+ timestamps()
+ parallelsAlwaysFailFast()
+ }
+
+ stages {
+ stage('trigger smartos-live') {
+ when {
+ anyOf {
+ branch 'master'
+ triggeredBy cause: 'UserIdCause'
+ }
+ // Prevent a user from starting a UserIdCause build on a
+ // release branch, since that would trigger the smartos-live
+ // 'master' branch build below, which is not what we want.
+ not {
+ branch pattern: 'release-\\d+', comparator: 'REGEXP'
+ }
+ }
+ steps {
+ build(job:'TritonDataCenter/smartos-live/master',
+ wait: false,
+ parameters: [
+ text(name: 'CONFIGURE_PROJECTS',
+ value:
+ "illumos-extra: master: origin\n" +
+ 'illumos: $BRANCH_NAME: origin\n' +
+ 'local/kbmd: master: origin\n' +
+ 'local/kvm-cmd: master: origin\n' +
+ 'local/kvm: master: origin\n' +
+ 'local/mdata-client: master: origin\n' +
+ 'local/ur-agent: master: origin')
+ ])
+ }
+ }
+ }
+ post {
+ always {
+ joySlackNotifications(channel: 'jenkins')
+ }
+ }
+}
diff --git a/boot.manifest b/boot.manifest
new file mode 100644
index 0000000000..4ccf8e489d
--- /dev/null
+++ b/boot.manifest
@@ -0,0 +1,53 @@
+#
+# 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 2019 Joyent, Inc.
+# Copyright 2022 MNX Cloud, Inc.
+#
+
+#
+# This lists the boot-package files that illumos-joyent delivers as part of
+# a build. These are not part of the platform image but rather the bootable
+# image itself.
+#
+d boot 0755 root sys
+d boot/defaults 0755 root sys
+f boot/defaults/loader.conf 0444 root sys
+d boot/forth 0755 root sys
+f boot/forth/beadm.4th 0444 root sys
+f boot/forth/beastie.4th 0444 root sys
+f boot/forth/brand-smartos.4th 0444 root sys
+f boot/forth/brand.4th 0444 root sys
+f boot/forth/check-password.4th 0444 root sys
+f boot/forth/color.4th 0444 root sys
+f boot/forth/delay.4th 0444 root sys
+f boot/forth/efi.4th 0444 root sys
+f boot/forth/frames.4th 0444 root sys
+f boot/forth/loader.4th 0444 root sys
+f boot/forth/logo-smartos.4th 0444 root sys
+f boot/forth/menu-commands.4th 0444 root sys
+f boot/forth/menu.4th 0444 root sys
+f boot/forth/menu.rc 0444 root sys
+f boot/forth/menusets.4th 0444 root sys
+f boot/forth/pcibios.4th 0444 root sys
+f boot/forth/screen.4th 0444 root sys
+f boot/forth/shortcuts.4th 0444 root sys
+f boot/forth/support.4th 0444 root sys
+f boot/forth/version.4th 0444 root sys
+f boot/gptzfsboot 0444 root sys
+f boot/loader 0444 root sys
+f boot/loader64.efi 0555 root sys
+f boot/loader.help 0444 root sys
+f boot/loader.rc 0444 root sys
+f boot/pmbr 0444 root sys
+f boot/triton.png 0444 root sys
+f boot/triton-logo.png 0444 root sys
diff --git a/exception_lists/check_rtime b/exception_lists/check_rtime
index eb69ff4335..2d8fa710e8 100644
--- a/exception_lists/check_rtime
+++ b/exception_lists/check_rtime
@@ -22,6 +22,7 @@
#
# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
+# Copyright 2018 Joyent, Inc.
# Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
# Copyright 2019 Peter Tribble.
# Copyright 2018 Joyent, Inc.
diff --git a/exception_lists/copyright b/exception_lists/copyright
index 2688984285..23ed313491 100644
--- a/exception_lists/copyright
+++ b/exception_lists/copyright
@@ -33,6 +33,30 @@ exception_lists/closed-bins
exception_lists/copyright
exception_lists/cstyle
exception_lists/hdrchk
+usr/src/cmd/acpi/acpidump/acpidump.h
+usr/src/cmd/acpi/acpidump/apdump.c
+usr/src/cmd/acpi/acpidump/apfiles.c
+usr/src/cmd/acpi/acpidump/apmain.c
+usr/src/cmd/acpi/acpidump/osillumostbl.c
+usr/src/cmd/acpi/acpidump/osunixdir.c
+usr/src/cmd/acpi/acpidump/tbprint.c
+usr/src/cmd/acpi/acpidump/tbxfroot.c
+usr/src/cmd/acpi/acpidump/utbuffer.c
+usr/src/cmd/acpi/acpixtract/acpixtract.c
+usr/src/cmd/acpi/acpixtract/acpixtract.h
+usr/src/cmd/acpi/acpixtract/axmain.c
+usr/src/cmd/acpi/acpixtract/axmain.h
+usr/src/cmd/acpi/acpixtract/axutils.c
+usr/src/cmd/acpi/acpixtract/axutils.h
+usr/src/cmd/acpi/common/getopt.c
+usr/src/cmd/acpi/common/utascii.c
+usr/src/cmd/acpi/common/utdebug.c
+usr/src/cmd/acpi/common/utexcep.c
+usr/src/cmd/acpi/common/utglobal.c
+usr/src/cmd/acpi/common/utmath.c
+usr/src/cmd/acpi/common/utnonansi.c
+usr/src/cmd/acpi/common/utprint.c
+usr/src/cmd/acpi/common/utxferror.c
usr/src/boot/*
usr/src/cmd/acpi/acpidump/acpidump.h
usr/src/cmd/acpi/acpidump/apdump.c
@@ -391,6 +415,7 @@ usr/src/test/crypto-tests/tests/digest/data/README
usr/src/test/libc-tests/tests/err/data/*
usr/src/test/libmlrpc-tests/tests/netrlogon/krb5_pac_tests/krb5_pac.bin
usr/src/test/util-tests/tests/dis/*/*.out
+usr/src/test/util-tests/tests/libsff/*.out
usr/src/test/util-tests/tests/grep_xpg4/files/gout*
usr/src/test/util-tests/tests/grep_xpg4/files/test*
usr/src/test/util-tests/tests/head/*.in
diff --git a/exception_lists/cstyle b/exception_lists/cstyle
index 814d5534cc..f0f641904b 100644
--- a/exception_lists/cstyle
+++ b/exception_lists/cstyle
@@ -647,6 +647,7 @@ usr/src/lib/krb5/ss/std_rqs.c
usr/src/lib/krb5/ss/utils.c
usr/src/lib/libgss/g_glue.c
usr/src/lib/libresolv2/common
+usr/src/lib/libresolv2_joy/common
usr/src/lib/librstp/common/base.h
usr/src/lib/librstp/common/choose.h
usr/src/lib/librstp/common/edge.c
@@ -814,6 +815,7 @@ usr/src/uts/common/gssapi/mechs/krb5/mech/util_validate.c
usr/src/uts/common/gssapi/mechs/krb5/mech/val_cred.c
usr/src/uts/common/gssapi/mechs/krb5/mech/verify.c
usr/src/uts/common/gssapi/mechs/krb5/mech/wrap_size_limit.c
+usr/src/uts/common/inet/ipf/ip_fil_solaris.c
usr/src/uts/common/inet/cc.h
usr/src/uts/common/inet/cc/cc_cubic.c
usr/src/uts/common/inet/cc/cc_module.h
@@ -1073,6 +1075,26 @@ usr/src/uts/common/io/ixgbe/core/ixgbe_x540.c
usr/src/uts/common/io/ixgbe/core/ixgbe_x540.h
usr/src/uts/common/io/ixgbe/core/ixgbe_x550.c
usr/src/uts/common/io/ixgbe/core/ixgbe_x550.h
+usr/src/uts/common/io/nfp/
+usr/src/uts/common/io/nfp/osif.c
+usr/src/uts/common/io/nfp/nfp.h
+usr/src/uts/common/io/nfp/drvlist.c
+usr/src/uts/common/io/nfp/nfp_ifvers.c
+usr/src/uts/common/io/nfp/i21285.c
+usr/src/uts/common/io/nfp/nfpci.h
+usr/src/uts/common/io/nfp/nfp_error.h
+usr/src/uts/common/io/nfp/i21555.h
+usr/src/uts/common/io/nfp/i21555d.c
+usr/src/uts/common/io/nfp/hostif.c
+usr/src/uts/common/io/nfp/nfp_common.h
+usr/src/uts/common/io/nfp/nfp_osif.h
+usr/src/uts/common/io/nfp/nfdev-solaris.h
+usr/src/uts/common/io/nfp/i21555.c
+usr/src/uts/common/io/nfp/nfp_cmd.h
+usr/src/uts/common/io/nfp/nfp_hostif.h
+usr/src/uts/common/io/nfp/nfdev-common.h
+usr/src/uts/common/io/nfp/autoversion.h
+usr/src/uts/common/io/nfp/i21285.h
usr/src/uts/common/io/qede/*
usr/src/uts/common/io/sfxge/common/ef10_ev.c
usr/src/uts/common/io/sfxge/common/ef10_filter.c
@@ -1269,6 +1291,16 @@ usr/src/common/acpica/utilities/utstate.c
usr/src/common/acpica/utilities/uttrack.c
usr/src/common/acpica/utilities/utxface.c
usr/src/common/acpica/utilities/utxferror.c
+usr/src/uts/intel/io/vmxnet/buildNumber.h
+usr/src/uts/intel/io/vmxnet/includeCheck.h
+usr/src/uts/intel/io/vmxnet/net.h
+usr/src/uts/intel/io/vmxnet/net_sg.h
+usr/src/uts/intel/io/vmxnet/vm_basic_types.h
+usr/src/uts/intel/io/vmxnet/vm_device_version.h
+usr/src/uts/intel/io/vmxnet/vmnet_def.h
+usr/src/uts/intel/io/vmxnet/vmxnet.c
+usr/src/uts/intel/io/vmxnet/vmxnet2_def.h
+usr/src/uts/intel/io/vmxnet/vmxnet_def.h
usr/src/uts/intel/sys/acpi/acapps.h
usr/src/uts/intel/sys/acpi/accommon.h
usr/src/uts/intel/sys/acpi/acconfig.h
diff --git a/exception_lists/hdrchk b/exception_lists/hdrchk
index 8985ced0e0..7c0313395e 100644
--- a/exception_lists/hdrchk
+++ b/exception_lists/hdrchk
@@ -366,6 +366,18 @@ usr/src/uts/common/io/e1000api/e1000_regs.h
usr/src/uts/common/io/ixgbe/core/*
usr/src/uts/common/io/i40e/core/i40e_adminq_cmd.h
usr/src/uts/common/io/qede/*
+usr/src/uts/common/io/nfp/nfp.h
+usr/src/uts/common/io/nfp/nfpci.h
+usr/src/uts/common/io/nfp/nfp_error.h
+usr/src/uts/common/io/nfp/i21555.h
+usr/src/uts/common/io/nfp/nfp_common.h
+usr/src/uts/common/io/nfp/nfp_osif.h
+usr/src/uts/common/io/nfp/nfdev-solaris.h
+usr/src/uts/common/io/nfp/nfp_cmd.h
+usr/src/uts/common/io/nfp/nfp_hostif.h
+usr/src/uts/common/io/nfp/nfdev-common.h
+usr/src/uts/common/io/nfp/autoversion.h
+usr/src/uts/common/io/nfp/i21285.h
usr/src/uts/common/io/udmf/dm9601reg.h
usr/src/uts/common/io/upf/adm8511reg.h
usr/src/uts/common/io/urf/rtl8150reg.h
diff --git a/exception_lists/keywords b/exception_lists/keywords
index 5c44c20350..4db53981e3 100644
--- a/exception_lists/keywords
+++ b/exception_lists/keywords
@@ -22,6 +22,7 @@
# Copyright 2017 Nexenta Systems, Inc.
# Copyright (c) 2013 by Delphix. All rights reserved.
# Copyright 2016 Toomas Soome <tsoome@me.com>
+# Copyright 2018 Joyent, Inc.
# Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
#
@@ -36,6 +37,7 @@ usr/src/data/locale/data/zh_SG.UTF-8.src
usr/src/data/locale/data/zh_TW.UTF-8.src
usr/src/data/terminfo/termcap.src
usr/src/data/terminfo/terminfo.src
+usr/src/data/ucode/amd/*
usr/src/data/ucode/intel/*
usr/src/test/zfs-tests/tests/functional/cli_root/zpool_upgrade/blockfiles/zfs-pool-v15.dat.bz2
usr/src/test/zfs-tests/tests/functional/delegate/delegate_common.kshlib
diff --git a/exception_lists/packaging b/exception_lists/packaging
index e3ee4960d5..8925008038 100644
--- a/exception_lists/packaging
+++ b/exception_lists/packaging
@@ -735,6 +735,7 @@ usr/lib/libsff.so
#
lib/amd64/libvmm.so i386
lib/amd64/libvmmapi.so i386
+usr/include/bhyve.h i386
usr/include/libppt.h i386
usr/include/libvmm.h i386
usr/include/vmmapi.h i386
diff --git a/exception_lists/wscheck b/exception_lists/wscheck
index 41e2272227..638acb3f52 100644
--- a/exception_lists/wscheck
+++ b/exception_lists/wscheck
@@ -14,13 +14,100 @@
#
syntax: glob
+usr/src/data/ucode/amd/*
+usr/src/data/ucode/intel/*
+usr/src/lib/libresolv2/common/*
+usr/src/lib/libresolv2_joy/common/*
+
+# bhyve sources
+usr/src/cmd/bhyve/acpi.[ch]
+usr/src/cmd/bhyve/ahci.h
+usr/src/cmd/bhyve/atkbdc.[ch]
+usr/src/cmd/bhyve/bhyvegc.[ch]
+usr/src/cmd/bhyve/bhyverun.[ch]
+usr/src/cmd/bhyve/block_if.[ch]
+usr/src/cmd/bhyve/bootrom.[ch]
+usr/src/cmd/bhyve/console.[ch]
+usr/src/cmd/bhyve/consport.c
+usr/src/cmd/bhyve/dbgport.[ch]
+usr/src/cmd/bhyve/fwctl.[ch]
+usr/src/cmd/bhyve/gdb.[ch]
+usr/src/cmd/bhyve/inout.[ch]
+usr/src/cmd/bhyve/ioapic.[ch]
+usr/src/cmd/bhyve/mem.[ch]
+usr/src/cmd/bhyve/mevent.[ch]
+usr/src/cmd/bhyve/mevent_test.c
+usr/src/cmd/bhyve/mptbl.[ch]
+usr/src/cmd/bhyve/pci_ahci.c
+usr/src/cmd/bhyve/pci_e82545.c
+usr/src/cmd/bhyve/pci_emul.[ch]
+usr/src/cmd/bhyve/pci_fbuf.c
+usr/src/cmd/bhyve/pci_hostbridge.c
+usr/src/cmd/bhyve/pci_irq.[ch]
+usr/src/cmd/bhyve/pci_lpc.[ch]
+usr/src/cmd/bhyve/pci_nvme.c
+usr/src/cmd/bhyve/pci_passthru.c
+usr/src/cmd/bhyve/pci_uart.c
+usr/src/cmd/bhyve/pci_virtio_block.c
+usr/src/cmd/bhyve/pci_virtio_console.c
+usr/src/cmd/bhyve/pci_virtio_net.c
+usr/src/cmd/bhyve/pci_virtio_rnd.c
+usr/src/cmd/bhyve/pci_virtio_scsi.c
+usr/src/cmd/bhyve/pci_xhci.[ch]
+usr/src/cmd/bhyve/pm.c
+usr/src/cmd/bhyve/pmtmr.c
+usr/src/cmd/bhyve/post.c
+usr/src/cmd/bhyve/ps2kbd.[ch]
+usr/src/cmd/bhyve/ps2mouse.[ch]
+usr/src/cmd/bhyve/rfb.[ch]
+usr/src/cmd/bhyve/rtc.[ch]
+usr/src/cmd/bhyve/smbiostbl.[ch]
+usr/src/cmd/bhyve/sockstream.[ch]
+usr/src/cmd/bhyve/spinup_ap.[ch]
+usr/src/cmd/bhyve/task_switch.c
+usr/src/cmd/bhyve/uart_emul.[ch]
+usr/src/cmd/bhyve/usb_emul.[ch]
+usr/src/cmd/bhyve/usb_mouse.c
+usr/src/cmd/bhyve/vga.[ch]
+usr/src/cmd/bhyve/virtio.[ch]
+usr/src/cmd/bhyve/xmsr.[ch]
+usr/src/cmd/bhyveconsole/bhyveconsole.c
+usr/src/cmd/bhyvectl/bhyvectl.c
usr/src/cmd/cxgbetool/*
usr/src/cmd/smbsrv/testoplock/case*.ref
+usr/src/compat/freebsd/*.h
+usr/src/compat/freebsd/*/*.h
+usr/src/compat/freebsd/amd64/machine/*.h
usr/src/data/hwdata/pci.ids
usr/src/data/hwdata/usb.ids
usr/src/data/perfmon/readme.txt
usr/src/data/ucode/amd/*
usr/src/data/ucode/intel/*
+usr/contrib/freebsd/*/*.h
+usr/contrib/freebsd/*/*/*.h
+usr/contrib/freebsd/lib/libutil/*.c
+usr/src/lib/libvmmapi/common/vmmapi.[ch]
+usr/src/uts/i86pc/io/vmm/amd/*.[ch]
+usr/src/uts/i86pc/io/vmm/intel/*.[chs]
+usr/src/uts/i86pc/io/vmm/io/*.[ch]
+usr/src/uts/i86pc/io/vmm/vmm.c
+usr/src/uts/i86pc/io/vmm/vmm_host.[ch]
+usr/src/uts/i86pc/io/vmm/vmm_instruction_emul.c
+usr/src/uts/i86pc/io/vmm/vmm_ioport.[ch]
+usr/src/uts/i86pc/io/vmm/vmm_ipi.h
+usr/src/uts/i86pc/io/vmm/vmm_ktr.h
+usr/src/uts/i86pc/io/vmm/vmm_lapic.[ch]
+usr/src/uts/i86pc/io/vmm/vmm_mem.[ch]
+usr/src/uts/i86pc/io/vmm/vmm_sol_glue.c
+usr/src/uts/i86pc/io/vmm/vmm_stat.[ch]
+usr/src/uts/i86pc/io/vmm/vmm_util.[ch]
+usr/src/uts/i86pc/io/vmm/vmx_assym.s
+usr/src/uts/i86pc/io/vmm/x86.[ch]
+usr/src/uts/i86pc/sys/vmm.h
+usr/src/uts/i86pc/sys/vmm_dev.h
+usr/src/uts/i86pc/sys/vmm_instruction_emul.h
+usr/src/uts/common/io/qede/*
+usr/src/uts/common/io/i40e/core/*
usr/src/test/crypto-tests/tests/digest/data/*.rsp
usr/src/test/os-tests/tests/definit/init.data
usr/src/test/util-tests/tests/dis/i386/*.out
@@ -33,6 +120,7 @@ usr/src/uts/common/io/i40e/core/*
usr/src/uts/common/io/ixgbe/core/*
usr/src/contrib/*
usr/src/cmd/ast/libshell/misc/shell_styleguide.docbook
+usr/src/test/util-tests/tests/dis/i386/*.out
# bhyve sources
usr/src/cmd/bhyve/acpi.[ch]
diff --git a/mancheck.conf b/mancheck.conf
new file mode 100644
index 0000000000..99770a4f1e
--- /dev/null
+++ b/mancheck.conf
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+/*
+ * WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+ *
+ * mancheck configuration files must comply with the rules detailed in:
+ *
+ * smartos-live.git :: tools/mancheck/README.md
+ *
+ * You should not be making modifications here until you've read the most
+ * current copy of that file. If you need help, contact a gatekeeper for
+ * guidance.
+ */
+
++section "2*";
+-page "__sparc_utrap_install.2";
+
++section "3*";
+-section "3iscsit";
+-section "3papi";
+-section "3tsol";
+-section "3tnf";
+-section "3perl";
+-section "3rsm";
+
+-page "libtsol.3lib";
+-page "libtsnet.3lib";
+-page "librsm.3lib";
+-page "libpapi.3lib";
+
++section "4*";
+-section "4d";
+
+/*
+ * These pages are for interfaces we do not really ship or
+ * support:
+ */
+-page "dsp.4i";
+-page "mixer.4i";
+-page "audio.4i";
+-page "agpgart_io.4i";
+-page "ibmf.4";
+
++section "5*";
+-page "volume-defaults.5";
+-page "md.cf.5";
+-page "wanboot.conf.5";
+
++section "7*";
+-page "pkcs11_tpm.7";
+-page "hal.7";
+-page "pam_tsol_account.7";
+-page "trusted_extensions.7";
+
++section "9*";
diff --git a/manifest b/manifest
new file mode 100644
index 0000000000..d2161378e6
--- /dev/null
+++ b/manifest
@@ -0,0 +1,21671 @@
+#
+# 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 2022 MNX Cloud, Inc.
+# Copyright 2020 Joyent, Inc.
+# Copyright 2017 ASS-Einrichtungssysteme GmbH
+#
+# This lists all the files that illumos-joyent delivers as part of a build.
+# Keep this sorted as by LC_ALL=C sort -u -k2.
+#
+s bin=usr/bin
+d boot 0755 root sys
+d boot/acpi 0755 root sys
+d boot/acpi/tables 0755 root sys
+d boot/fonts 0755 root sys
+f boot/fonts/10x18.fnt 0444 root bin
+f boot/fonts/10x18b.fnt 0444 root bin
+f boot/fonts/10x20.fnt 0444 root bin
+f boot/fonts/10x20b.fnt 0444 root bin
+f boot/fonts/11x22.fnt 0444 root bin
+f boot/fonts/11x22b.fnt 0444 root bin
+f boot/fonts/12x24.fnt 0444 root bin
+f boot/fonts/12x24b.fnt 0444 root bin
+f boot/fonts/14x28.fnt 0444 root bin
+f boot/fonts/14x28b.fnt 0444 root bin
+f boot/fonts/16x32.fnt 0444 root bin
+f boot/fonts/16x32b.fnt 0444 root bin
+f boot/fonts/6x12.fnt 0444 root bin
+f boot/fonts/6x12b.fnt 0444 root bin
+f boot/fonts/8x14.fnt 0444 root bin
+f boot/fonts/8x14b.fnt 0444 root bin
+f boot/fonts/8x14v.fnt 0444 root bin
+f boot/fonts/8x16.fnt 0444 root bin
+f boot/fonts/8x16b.fnt 0444 root bin
+f boot/fonts/8x16v.fnt 0444 root bin
+f boot/fonts/Gallant19.fnt 0444 root bin
+f boot/fonts/fonts.dir 0444 root bin
+d boot/forth 0755 root root
+f boot/forth/menu.rc 0444 root root
+d boot/solaris 0755 root sys
+f boot/solaris/bootenv.rc 0644 root root
+d dev 0755 root sys
+s dev/allkmem=../devices/pseudo/mm@0:allkmem
+s dev/arp=../devices/pseudo/arp@0:arp
+s dev/bl=../devices/pseudo/bl@0:bl
+s dev/bpf=../devices/pseudo/bpf@0:bpf
+s dev/bridgectl=../devices/pseudo/clone@0:bridge
+s dev/conslog=../devices/pseudo/log@0:conslog
+s dev/console=../devices/pseudo/cn@0:console
+d dev/cpu 0755 root root
+d dev/cpu/self 0755 root root
+s dev/cpu/self/cpuid=../../../devices/pseudo/cpuid@0:self
+s dev/crypto=../devices/pseudo/crypto@0:crypto
+s dev/cryptoadm=../devices/pseudo/cryptoadm@0:cryptoadm
+s dev/dld=../devices/pseudo/dld@0:ctl
+d dev/dtrace 0755 root root
+d dev/dtrace/provider 0755 root root
+s dev/dtrace/provider/dcpc=../../../devices/pseudo/dcpc@0:dcpc
+s dev/dtrace/provider/fasttrap=../../../devices/pseudo/fasttrap@0:fasttrap
+s dev/dtrace/provider/fbt=../../../devices/pseudo/fbt@0:fbt
+s dev/dtrace/provider/lockstat=../../../devices/pseudo/lockstat@0:lockstat
+s dev/dtrace/provider/profile=../../../devices/pseudo/profile@0:profile
+s dev/dtrace/provider/sdt=../../../devices/pseudo/sdt@0:sdt
+s dev/dtrace/provider/systrace=../../../devices/pseudo/systrace@0:systrace
+s dev/dump=../devices/pseudo/dump@0:dump
+d dev/fd 0555 root root
+s dev/fm=../devices/pseudo/fm@0:fm
+s dev/fssnapctl=../devices/pseudo/fssnap@0:ctl
+s dev/full=../devices/pseudo/mm@0:full
+s dev/icmp6=../devices/pseudo/icmp6@0:icmp6
+s dev/icmp=../devices/pseudo/icmp@0:icmp
+s dev/igb=../devices/pseudo/clone@0:igb
+s dev/ip6=../devices/pseudo/ip6@0:ip6
+s dev/ip=../devices/pseudo/ip@0:ip
+s dev/ipauth=../devices/pseudo/ipf@0:ipauth
+s dev/ipf=../devices/pseudo/ipf@0:ipf
+s dev/ipfev=../devices/pseudo/ipf@0:ipfev
+s dev/ipl=../devices/pseudo/ipf@0:ipf
+s dev/iplookup=../devices/pseudo/ipf@0:iplookup
+s dev/ipmpstub=../devices/pseudo/dlpistub@0:ipmpstub
+s dev/ipnat=../devices/pseudo/ipf@0:ipnat
+s dev/ipscan=../devices/pseudo/ipf@0:ipscan
+s dev/ipsecah=../devices/pseudo/ipsecah@0:ipsecah
+s dev/ipsecesp=../devices/pseudo/ipsecesp@0:ipsecesp
+s dev/ipstate=../devices/pseudo/ipf@0:ipstate
+s dev/ipsync=../devices/pseudo/ipf@0:ipsync
+s dev/kbd=../devices/pseudo/conskbd@0:kbd
+s dev/keysock=../devices/pseudo/keysock@0:keysock
+s dev/kmdb=../devices/pseudo/kmdb@0:kmdb
+s dev/kmem=../devices/pseudo/mm@0:kmem
+s dev/kstat=../devices/pseudo/kstat@0:kstat
+s dev/ksyms=../devices/pseudo/ksyms@0:ksyms
+s dev/llc1=../devices/pseudo/clone@0:llc1
+s dev/lo0=../devices/pseudo/ipnet@0:lo0
+s dev/lockstat=../devices/pseudo/lockstat@0:lockstat
+s dev/lofictl=../devices/pseudo/lofi@0:ctl
+s dev/log=../devices/pseudo/log@0:log
+s dev/logindmux=../devices/pseudo/clone@0:logindmux
+s dev/mem=../devices/pseudo/mm@0:mem
+s dev/mouse=../devices/pseudo/consms@0:mouse
+s dev/msglog=../devices/pseudo/sysmsg@0:msglog
+s dev/nsmb=../devices/pseudo/nsmb@0:nsmb
+s dev/null=../devices/pseudo/mm@0:null
+s dev/openprom=../devices/pseudo/openeepr@0:openprom
+s dev/physmem=../devices/pseudo/physmem@0:physmem
+s dev/pm=../devices/pseudo/pm@0:pm
+s dev/poll=../devices/pseudo/poll@0:poll
+s dev/pool=../devices/pseudo/pool@0:pool
+s dev/poolctl=../devices/pseudo/pool@0:poolctl
+s dev/power_button=../devices/pseudo/power@0:power_button
+s dev/ppm=../devices/pseudo/ppm@0:ppm
+s dev/ptmajor=../devices/pseudo/ptm@0:ptmajor
+s dev/ptmx=../devices/pseudo/clone@0:ptm
+s dev/ptyp0=../devices/pseudo/ptc@0:ptyp0
+s dev/ptyp1=../devices/pseudo/ptc@0:ptyp1
+s dev/ptyp2=../devices/pseudo/ptc@0:ptyp2
+s dev/ptyp3=../devices/pseudo/ptc@0:ptyp3
+s dev/ptyp4=../devices/pseudo/ptc@0:ptyp4
+s dev/ptyp5=../devices/pseudo/ptc@0:ptyp5
+s dev/ptyp6=../devices/pseudo/ptc@0:ptyp6
+s dev/ptyp7=../devices/pseudo/ptc@0:ptyp7
+s dev/ptyp8=../devices/pseudo/ptc@0:ptyp8
+s dev/ptyp9=../devices/pseudo/ptc@0:ptyp9
+s dev/ptypa=../devices/pseudo/ptc@0:ptypa
+s dev/ptypb=../devices/pseudo/ptc@0:ptypb
+s dev/ptypc=../devices/pseudo/ptc@0:ptypc
+s dev/ptypd=../devices/pseudo/ptc@0:ptypd
+s dev/ptype=../devices/pseudo/ptc@0:ptype
+s dev/ptypf=../devices/pseudo/ptc@0:ptypf
+s dev/ptyq0=../devices/pseudo/ptc@0:ptyq0
+s dev/ptyq1=../devices/pseudo/ptc@0:ptyq1
+s dev/ptyq2=../devices/pseudo/ptc@0:ptyq2
+s dev/ptyq3=../devices/pseudo/ptc@0:ptyq3
+s dev/ptyq4=../devices/pseudo/ptc@0:ptyq4
+s dev/ptyq5=../devices/pseudo/ptc@0:ptyq5
+s dev/ptyq6=../devices/pseudo/ptc@0:ptyq6
+s dev/ptyq7=../devices/pseudo/ptc@0:ptyq7
+s dev/ptyq8=../devices/pseudo/ptc@0:ptyq8
+s dev/ptyq9=../devices/pseudo/ptc@0:ptyq9
+s dev/ptyqa=../devices/pseudo/ptc@0:ptyqa
+s dev/ptyqb=../devices/pseudo/ptc@0:ptyqb
+s dev/ptyqc=../devices/pseudo/ptc@0:ptyqc
+s dev/ptyqd=../devices/pseudo/ptc@0:ptyqd
+s dev/ptyqe=../devices/pseudo/ptc@0:ptyqe
+s dev/ptyqf=../devices/pseudo/ptc@0:ptyqf
+s dev/ptyr0=../devices/pseudo/ptc@0:ptyr0
+s dev/ptyr1=../devices/pseudo/ptc@0:ptyr1
+s dev/ptyr2=../devices/pseudo/ptc@0:ptyr2
+s dev/ptyr3=../devices/pseudo/ptc@0:ptyr3
+s dev/ptyr4=../devices/pseudo/ptc@0:ptyr4
+s dev/ptyr5=../devices/pseudo/ptc@0:ptyr5
+s dev/ptyr6=../devices/pseudo/ptc@0:ptyr6
+s dev/ptyr7=../devices/pseudo/ptc@0:ptyr7
+s dev/ptyr8=../devices/pseudo/ptc@0:ptyr8
+s dev/ptyr9=../devices/pseudo/ptc@0:ptyr9
+s dev/ptyra=../devices/pseudo/ptc@0:ptyra
+s dev/ptyrb=../devices/pseudo/ptc@0:ptyrb
+s dev/ptyrc=../devices/pseudo/ptc@0:ptyrc
+s dev/ptyrd=../devices/pseudo/ptc@0:ptyrd
+s dev/ptyre=../devices/pseudo/ptc@0:ptyre
+s dev/ptyrf=../devices/pseudo/ptc@0:ptyrf
+s dev/ramdiskctl=../devices/pseudo/ramdisk@1024:ctl
+s dev/random=../devices/pseudo/random@0:random
+s dev/rawip6=../devices/pseudo/icmp6@0:icmp6
+s dev/rawip=../devices/pseudo/icmp@0:icmp
+s dev/rts=../devices/pseudo/rts@0:rts
+d dev/sad 0755 root root
+s dev/sad/admin=../../devices/pseudo/sad@0:admin
+s dev/sad/user=../../devices/pseudo/sad@0:user
+s dev/sdp=../devices/pseudo/sdp@0:sdp
+s dev/smbios=../devices/pseudo/smbios@0:smbios
+s dev/spdsock=../devices/pseudo/spdsock@0:spdsock
+s dev/sppp=../devices/pseudo/clone@0:sppp
+s dev/sppptun=../devices/pseudo/clone@0:sppptun
+s dev/srn=../devices/pseudo/srn@0:srn
+s dev/stderr=fd/2
+s dev/stdin=fd/0
+s dev/stdout=fd/1
+s dev/syscon=../devices/pseudo/cn@0:syscon
+s dev/sysevent=../devices/pseudo/sysevent@0:sysevent
+s dev/sysmsg=../devices/pseudo/sysmsg@0:sysmsg
+s dev/systty=../devices/pseudo/cn@0:systty
+s dev/tcp6=../devices/pseudo/tcp6@0:tcp6
+s dev/tcp=../devices/pseudo/tcp@0:tcp
+s dev/ticlts=../devices/pseudo/tl@0:ticlts
+s dev/ticots=../devices/pseudo/tl@0:ticots
+s dev/ticotsord=../devices/pseudo/tl@0:ticotsord
+s dev/tty=../devices/pseudo/sy@0:tty
+s dev/ttyp0=../devices/pseudo/ptsl@0:ttyp0
+s dev/ttyp1=../devices/pseudo/ptsl@0:ttyp1
+s dev/ttyp2=../devices/pseudo/ptsl@0:ttyp2
+s dev/ttyp3=../devices/pseudo/ptsl@0:ttyp3
+s dev/ttyp4=../devices/pseudo/ptsl@0:ttyp4
+s dev/ttyp5=../devices/pseudo/ptsl@0:ttyp5
+s dev/ttyp6=../devices/pseudo/ptsl@0:ttyp6
+s dev/ttyp7=../devices/pseudo/ptsl@0:ttyp7
+s dev/ttyp8=../devices/pseudo/ptsl@0:ttyp8
+s dev/ttyp9=../devices/pseudo/ptsl@0:ttyp9
+s dev/ttypa=../devices/pseudo/ptsl@0:ttypa
+s dev/ttypb=../devices/pseudo/ptsl@0:ttypb
+s dev/ttypc=../devices/pseudo/ptsl@0:ttypc
+s dev/ttypd=../devices/pseudo/ptsl@0:ttypd
+s dev/ttype=../devices/pseudo/ptsl@0:ttype
+s dev/ttypf=../devices/pseudo/ptsl@0:ttypf
+s dev/ttyq0=../devices/pseudo/ptsl@0:ttyq0
+s dev/ttyq1=../devices/pseudo/ptsl@0:ttyq1
+s dev/ttyq2=../devices/pseudo/ptsl@0:ttyq2
+s dev/ttyq3=../devices/pseudo/ptsl@0:ttyq3
+s dev/ttyq4=../devices/pseudo/ptsl@0:ttyq4
+s dev/ttyq5=../devices/pseudo/ptsl@0:ttyq5
+s dev/ttyq6=../devices/pseudo/ptsl@0:ttyq6
+s dev/ttyq7=../devices/pseudo/ptsl@0:ttyq7
+s dev/ttyq8=../devices/pseudo/ptsl@0:ttyq8
+s dev/ttyq9=../devices/pseudo/ptsl@0:ttyq9
+s dev/ttyqa=../devices/pseudo/ptsl@0:ttyqa
+s dev/ttyqb=../devices/pseudo/ptsl@0:ttyqb
+s dev/ttyqc=../devices/pseudo/ptsl@0:ttyqc
+s dev/ttyqd=../devices/pseudo/ptsl@0:ttyqd
+s dev/ttyqe=../devices/pseudo/ptsl@0:ttyqe
+s dev/ttyqf=../devices/pseudo/ptsl@0:ttyqf
+s dev/ttyr0=../devices/pseudo/ptsl@0:ttyr0
+s dev/ttyr1=../devices/pseudo/ptsl@0:ttyr1
+s dev/ttyr2=../devices/pseudo/ptsl@0:ttyr2
+s dev/ttyr3=../devices/pseudo/ptsl@0:ttyr3
+s dev/ttyr4=../devices/pseudo/ptsl@0:ttyr4
+s dev/ttyr5=../devices/pseudo/ptsl@0:ttyr5
+s dev/ttyr6=../devices/pseudo/ptsl@0:ttyr6
+s dev/ttyr7=../devices/pseudo/ptsl@0:ttyr7
+s dev/ttyr8=../devices/pseudo/ptsl@0:ttyr8
+s dev/ttyr9=../devices/pseudo/ptsl@0:ttyr9
+s dev/ttyra=../devices/pseudo/ptsl@0:ttyra
+s dev/ttyrb=../devices/pseudo/ptsl@0:ttyrb
+s dev/ttyrc=../devices/pseudo/ptsl@0:ttyrc
+s dev/ttyrd=../devices/pseudo/ptsl@0:ttyrd
+s dev/ttyre=../devices/pseudo/ptsl@0:ttyre
+s dev/ttyrf=../devices/pseudo/ptsl@0:ttyrf
+s dev/ucode=../devices/pseudo/ucode@0:ucode
+s dev/udp6=../devices/pseudo/udp6@0:udp6
+s dev/udp=../devices/pseudo/udp@0:udp
+s dev/urandom=../devices/pseudo/random@0:urandom
+s dev/vni=../devices/pseudo/dlpistub@0:vni
+s dev/winlock=../devices/pseudo/winlock@0:winlock
+s dev/wscons=../devices/pseudo/wc@0:wscons
+s dev/zero=../devices/pseudo/mm@0:zero
+s dev/zfs=../devices/pseudo/zfs@0:zfs
+d devices 0755 root sys
+d etc 0755 root sys
+f etc/.login 0644 root sys
+s etc/.syslog_door=../var/run/syslog_door
+s etc/TIMEZONE=default/init
+d etc/acct 0755 adm adm
+f etc/acct/holidays 0644 root bin
+s etc/aliases=./mail/aliases
+d etc/amd64 0755 root sys
+f etc/auto_home 0644 root bin
+f etc/auto_master 0644 root bin
+d etc/bash 0755 root sys
+f etc/bash/bash_completion 0644 root sys
+d etc/bash/bash_completion.d 0755 root sys
+f etc/bash/bash_completion.d/dladm 0644 root sys
+f etc/bash/bash_completion.d/zone_alias 0644 root sys
+f etc/bash/bash_completion.d/zones 0644 root sys
+d etc/brand 0755 root sys
+d etc/certs 0755 root sys
+d etc/cron.d 0755 root sys
+f etc/cron.d/.proto 0744 root sys
+f etc/cron.d/at.deny 0644 root sys
+f etc/cron.d/cron.deny 0644 root sys
+d etc/cron.d/crontabs 0755 root sys
+f etc/cron.d/crontabs/root 0600 root root
+f etc/cron.d/queuedefs 0644 root sys
+d etc/crypto 0755 root sys
+d etc/crypto/certs 0755 root sys
+d etc/crypto/crls 0755 root sys
+f etc/crypto/kcf.conf 0644 root sys
+f etc/crypto/kmf.conf 0644 root sys
+f etc/crypto/pkcs11.conf 0644 root sys
+f etc/dacf.conf 0644 root sys
+d etc/dat 0755 root sys
+f etc/dat/dat.conf 0644 root sys
+f etc/datemsk 0444 root sys
+d etc/default 0755 root sys
+f etc/default/autofs 0644 root sys
+f etc/default/cron 0644 root sys
+f etc/default/devfsadm 0644 root sys
+f etc/default/dhcpagent 0644 root sys
+f etc/default/fs 0644 root sys
+f etc/default/ftp 0644 root sys
+f etc/default/inetinit 0644 root sys
+f etc/default/init 0644 root sys
+f etc/default/ipsec 0644 root sys
+f etc/default/keyserv 0644 root sys
+f etc/default/login 0644 root sys
+f etc/default/mpathd 0644 root sys
+f etc/default/nfs 0644 root sys
+f etc/default/nfslogd 0644 root sys
+f etc/default/nss 0644 root sys
+f etc/default/passwd 0644 root sys
+f etc/default/power 0644 root sys
+f etc/default/su 0644 root sys
+f etc/default/syslogd 0644 root sys
+f etc/default/tar 0644 root sys
+f etc/default/telnetd 0644 root sys
+f etc/default/utmpd 0644 root sys
+d etc/dev 0755 root sys
+f etc/device.tab 0444 root root
+d etc/devices 0755 root sys
+d etc/devices/dli 0755 root root
+f etc/devlink.tab 0644 root sys
+d etc/dfs 0755 root sys
+f etc/dfs/dfstab 0644 root sys
+f etc/dfs/fstypes 0644 root root
+f etc/dfs/sharetab 0444 root root
+f etc/dgroup.tab 0444 root sys
+d etc/dhcp 0755 root sys
+f etc/dhcp/inittab 0644 root sys
+f etc/dhcp/inittab6 0644 root sys
+f etc/dispadmin.conf 0644 root sys
+d etc/dladm 0755 dladm netadm
+f etc/dladm/datalink.conf 0644 dladm sys
+f etc/dladm/flowadm.conf 0644 dladm netadm
+f etc/dladm/flowprop.conf 0644 dladm netadm
+f etc/dladm/secobj.conf 0660 dladm netadm
+f etc/driver_aliases 0644 root sys
+f etc/driver_classes 0644 root sys
+f etc/dumpadm.conf 0644 root other
+f etc/dumpdates 0664 root sys
+d etc/fm 0755 root sys
+d etc/fm/fmd 0755 root sys
+f etc/format.dat 0644 root sys
+d etc/fs 0755 root sys
+d etc/fs/dev 0755 root sys
+f etc/fs/dev/mount 0555 root bin
+d etc/fs/hsfs 0755 root sys
+f etc/fs/hsfs/mount 0555 root bin
+d etc/fs/nfs 0755 root sys
+s etc/fs/nfs/mount=../../../usr/lib/fs/nfs/mount
+d etc/fs/ufs 0755 root sys
+f etc/fs/ufs/mount 0555 root bin
+d etc/fs/zfs 0755 root sys
+s etc/fs/zfs/mount=../../../sbin/zfs
+s etc/fs/zfs/umount=../../../sbin/zfs
+f etc/group 0644 root sys
+d etc/gss 0755 root sys
+f etc/gss/gsscred.conf 0644 root sys
+f etc/gss/mech 0644 root sys
+f etc/gss/qop 0644 root sys
+f etc/hba.conf 0644 root bin
+s etc/hosts=inet/hosts
+f etc/ima.conf 0644 root bin
+d etc/inet 0755 root sys
+f etc/inet/datemsk.ndpd 0444 root sys
+f etc/inet/hosts 0644 root sys
+d etc/inet/ike 0755 root sys
+f etc/inet/ike/config.sample 0444 root sys
+d etc/inet/ike/crls 0755 root sys
+d etc/inet/ike/publickeys 0755 root sys
+f etc/inet/inetd.conf 0644 root sys
+f etc/inet/ipaddrsel.conf 0644 root sys
+s etc/inet/ipnodes=hosts
+f etc/inet/ipsecalgs 0644 root sys
+f etc/inet/ipsecinit.sample 0444 root sys
+f etc/inet/netmasks 0644 root sys
+f etc/inet/networks 0644 root sys
+f etc/inet/ntp.conf 0644 root sys
+f etc/inet/protocols 0644 root sys
+d etc/inet/secret 0700 root sys
+f etc/inet/secret/ike.preshared 0600 root sys
+d etc/inet/secret/ike.privatekeys 0700 root sys
+f etc/inet/secret/ipseckeys.sample 0600 root sys
+f etc/inet/services 0644 root sys
+f etc/inet/slp.conf.example 0644 root sys
+s etc/inetd.conf=inet/inetd.conf
+d etc/init.d 0755 root sys
+f etc/init.d/PRESERVE 0744 root sys
+f etc/init.d/README 0644 root sys
+f etc/init.d/acct 0744 root sys
+f etc/init.d/dodatadm.udaplt 0744 root sys
+f etc/init.d/pppd 0744 root sys
+f etc/init.d/sysetup 0744 root sys
+f etc/inittab 0644 root sys
+f etc/ioctl.syscon 0644 root sys
+d etc/ipadm 0755 netadm netadm
+f etc/ipadm/ipadm.conf 0644 netadm netadm
+d etc/ipf 0755 root sys
+f etc/ipf/ipf.conf 0644 root sys
+f etc/ipf/smartos_version 0444 root sys
+f etc/issue 0644 root sys
+f etc/iu.ap 0644 root sys
+d etc/krb5 0755 root sys
+f etc/krb5/krb5.conf 0644 root sys
+f etc/krb5/warn.conf 0644 root sys
+f etc/ksh.kshrc 0644 root sys
+s etc/log=../var/adm/log
+f etc/logadm.conf 0644 root sys
+d etc/logadm.d 0755 root sys
+f etc/logadm.d/fmd.logadm.conf 0444 root sys
+f etc/logindevperm 0644 root sys
+f etc/mach 0644 root sys
+f etc/magic 0444 root bin
+d etc/mail 0755 root mail
+f etc/mail/Mail.rc 0644 root mail
+f etc/mail/aliases 0644 root mail
+d etc/mail/cf 0755 root mail
+f etc/mail/helpfile 0644 root mail
+f etc/mail/local-host-names 0644 root mail
+f etc/mail/mailx.rc 0644 root mail
+s etc/mail/main.cf=sendmail.cf
+f etc/mail/sendmail.cf 0444 root mail
+s etc/mail/sendmail.hf=helpfile
+f etc/mail/submit.cf 0444 root mail
+s etc/mail/subsidiary.cf=sendmail.cf
+f etc/mail/trusted-users 0644 root mail
+f etc/mailcap 0644 root bin
+f etc/mailer.conf 0644 root sys
+f etc/mime.types 0644 root bin
+f etc/minor_perm 0644 root sys
+f etc/mnttab 0444 root root
+f etc/motd 0644 root sys
+f etc/mpapi.conf 0644 root bin
+f etc/name_to_major 0644 root sys
+f etc/name_to_sysnum 0644 root sys
+d etc/net 0755 root sys
+d etc/net/ticlts 0755 root sys
+f etc/net/ticlts/hosts 0644 root sys
+f etc/net/ticlts/services 0644 root sys
+d etc/net/ticots 0755 root sys
+f etc/net/ticots/hosts 0644 root sys
+f etc/net/ticots/services 0644 root sys
+d etc/net/ticotsord 0755 root sys
+f etc/net/ticotsord/hosts 0644 root sys
+f etc/net/ticotsord/services 0644 root sys
+f etc/netconfig 0644 root sys
+s etc/netmasks=inet/netmasks
+s etc/networks=inet/networks
+d etc/nfs 0755 root sys
+f etc/nfs/nfslog.conf 0644 root sys
+f etc/nfssec.conf 0644 root sys
+f etc/nscd.conf 0644 root sys
+f etc/nsswitch.ad 0644 root sys
+f etc/nsswitch.conf 0644 root sys
+f etc/nsswitch.dns 0644 root sys
+f etc/nsswitch.files 0644 root sys
+f etc/nsswitch.ldap 0644 root sys
+f etc/nsswitch.nis 0644 root sys
+d etc/opt 0755 root sys
+f etc/pam.conf 0644 root sys
+f etc/passwd 0644 root sys
+f etc/path_to_inst 0444 root root
+f etc/power.conf 0644 root sys
+d etc/ppp 0755 root sys
+f etc/ppp/chap-secrets 0600 root sys
+f etc/ppp/myisp-chat.tmpl 0644 root sys
+f etc/ppp/options.tmpl 0644 root sys
+f etc/ppp/options.ttya.tmpl 0644 root sys
+f etc/ppp/pap-secrets 0600 root sys
+d etc/ppp/peers 0755 root sys
+f etc/ppp/peers/myisp.tmpl 0644 root sys
+d etc/ppp/plugins 0755 root sys
+f etc/ppt_matches 0444 root root
+f etc/profile 0644 root sys
+f etc/project 0644 root sys
+s etc/protocols=inet/protocols
+f etc/publickey 0644 root bin
+d etc/rc0.d 0755 root sys
+h etc/rc0.d/K50pppd=etc/init.d/pppd
+d etc/rc1.d 0755 root sys
+h etc/rc1.d/K50pppd=etc/init.d/pppd
+d etc/rc2.d 0755 root sys
+f etc/rc2.d/README 0644 root sys
+f etc/rc2.d/S20sysetup 0744 root sys
+h etc/rc2.d/S47pppd=etc/init.d/pppd
+f etc/rc2.d/S81dodatadm.udaplt 0744 root sys
+f etc/rc2.d/S89PRESERVE 0744 root sys
+d etc/rc3.d 0755 root sys
+f etc/rc3.d/README 0644 root sys
+d etc/rcS.d 0755 root sys
+f etc/rcS.d/README 0644 root sys
+d etc/rcm 0755 root sys
+d etc/rcm/scripts 0755 root sys
+f etc/remote 0644 root bin
+f etc/resolv.conf 0644 netadm netadm
+f etc/rpc 0644 root sys
+d etc/rpcsec 0755 root sys
+d etc/saf 0755 root bin
+f etc/saf/_sactab 0644 root sys
+f etc/saf/_sysconfig 0644 root sys
+d etc/saf/zsmon 0755 root sys
+d etc/sasl 0755 root sys
+f etc/sdp.conf 0644 root sys
+d etc/security 0755 root sys
+d etc/security/audit 0755 root sys
+d etc/security/audit/localhost 0755 root sys
+s etc/security/audit/localhost/files=../../../../var/audit
+f etc/security/audit_class 0644 root sys
+f etc/security/audit_event 0644 root sys
+f etc/security/audit_warn 0740 root sys
+f etc/security/auth_attr 0644 root sys
+d etc/security/auth_attr.d 0755 root sys
+f etc/security/auth_attr.d/SUNWcs 0644 root sys
+f etc/security/crypt.conf 0644 root sys
+d etc/security/dev 0755 root sys
+f etc/security/dev/audio 0400 root bin
+f etc/security/dev/fd0 0400 root bin
+f etc/security/dev/sr0 0400 root bin
+f etc/security/dev/st0 0400 root bin
+f etc/security/dev/st1 0400 root bin
+f etc/security/device_policy 0644 root sys
+f etc/security/exec_attr 0644 root sys
+d etc/security/exec_attr.d 0755 root sys
+f etc/security/exec_attr.d/SUNWcs 0644 root sys
+f etc/security/exec_attr.d/SUNWsmbfs 0644 root sys
+f etc/security/extra_privs 0644 root sys
+f etc/security/kmfpolicy.xml 0644 root bin
+d etc/security/lib 0755 root sys
+f etc/security/lib/audio_clean 0555 root sys
+f etc/security/lib/fd_clean 0555 root sys
+f etc/security/lib/sr_clean 0555 root sys
+f etc/security/lib/st_clean 0555 root sys
+f etc/security/policy.conf 0644 root sys
+f etc/security/priv_names 0644 root sys
+f etc/security/prof_attr 0644 root sys
+d etc/security/prof_attr.d 0755 root sys
+f etc/security/prof_attr.d/SUNWcs 0644 root sys
+s etc/services=inet/services
+f etc/shadow 0400 root sys
+d etc/skel 0755 root sys
+f etc/skel/.bashrc 0644 root other
+f etc/skel/.kshrc 0644 root other
+f etc/skel/.profile 0644 root other
+f etc/smhba.conf 0644 root sys
+d etc/sock2path.d 0755 root sys
+f etc/sock2path.d/driver%2Fnetwork%2Fbpf 0644 root sys
+f etc/sock2path.d/driver%2Fnetwork%2Fsdp 0644 root sys
+f etc/sock2path.d/system%2Fkernel 0644 root sys
+s etc/sulogin=../sbin/sulogin
+d etc/svc 0755 root sys
+d etc/svc/profile 0755 root sys
+f etc/svc/profile/generic.xml 0444 root sys
+f etc/svc/profile/platform.xml 0444 root sys
+d etc/svc/volatile 0755 root sys
+d etc/sysconfig 0755 root root
+d etc/sysevent 0755 root sys
+d etc/sysevent/config 0755 root sys
+d etc/sysevent/devfsadm_event_channel 0755 root root
+f etc/syslog.conf 0644 root sys
+f etc/system 0644 root sys
+s etc/termcap=../usr/share/lib/termcap
+d etc/tm 0755 root sys
+f etc/ttydefs 0644 root sys
+f etc/ttysrch 0644 root sys
+d etc/usb 0755 root sys
+f etc/usb/config_map.conf 0644 root sys
+f etc/user_attr 0644 root sys
+d etc/user_attr.d 0755 root sys
+f etc/user_attr.d/SUNWcs 0644 root sys
+s etc/utmpx=../var/adm/utmpx
+d etc/versions 0755 root sys
+f etc/versions/build 0644 root other
+f etc/vfstab 0644 root sys
+s etc/wtmpx=../var/adm/wtmpx
+d etc/xdg 0755 root sys
+d etc/xdg/autostart 0755 root sys
+d etc/zfs 0755 root sys
+d etc/zones 0755 root sys
+f etc/zones/Joyent.xml 0444 root bin
+f etc/zones/SUNWblank.xml 0444 root bin
+f etc/zones/SUNWdefault.xml 0444 root bin
+f etc/zones/SYSbhyve.xml 0444 root bin
+f etc/zones/index 0644 root sys
+s etc/zprofile=profile
+f etc/zshrc 0644 root bin
+d kernel 0755 root sys
+d kernel/amd64 0755 root sys
+f kernel/amd64/genunix 0755 root sys
+d kernel/cc 0755 root sys
+d kernel/cc/amd64 0755 root sys
+f kernel/cc/amd64/cc_cubic 0755 root sys
+f kernel/cc/amd64/cc_newreno 0755 root sys
+f kernel/cc/amd64/cc_sunreno 0755 root sys
+d kernel/crypto 0755 root sys
+d kernel/crypto/amd64 0755 root sys
+f kernel/crypto/amd64/aes 0755 root sys
+f kernel/crypto/amd64/arcfour 0755 root sys
+f kernel/crypto/amd64/blowfish 0755 root sys
+f kernel/crypto/amd64/des 0755 root sys
+s kernel/crypto/amd64/dprov=../../../kernel/drv/amd64/dprov
+f kernel/crypto/amd64/ecc 0755 root sys
+f kernel/crypto/amd64/edonr 0755 root sys
+f kernel/crypto/amd64/md4 0755 root sys
+f kernel/crypto/amd64/md5 0755 root sys
+f kernel/crypto/amd64/rsa 0755 root sys
+f kernel/crypto/amd64/sha1 0755 root sys
+f kernel/crypto/amd64/sha2 0755 root sys
+f kernel/crypto/amd64/skein 0755 root sys
+f kernel/crypto/amd64/swrand 0755 root sys
+d kernel/dacf 0755 root sys
+d kernel/dacf/amd64 0755 root sys
+f kernel/dacf/amd64/net_dacf 0755 root sys
+d kernel/drv 0755 root sys
+f kernel/drv/aac.conf 0644 root sys
+f kernel/drv/acpi_drv.conf 0644 root sys
+f kernel/drv/adpu320.conf 0644 root sys
+f kernel/drv/aggr.conf 0644 root sys
+f kernel/drv/amdzen.conf 0644 root sys
+d kernel/drv/amd64 0755 root sys
+f kernel/drv/amd64/aac 0755 root sys
+f kernel/drv/amd64/acpi_drv 0755 root sys
+f kernel/drv/amd64/adpu320 0755 root sys
+f kernel/drv/amd64/afe 0755 root sys
+f kernel/drv/amd64/aggr 0755 root sys
+f kernel/drv/amd64/ahci 0755 root sys
+f kernel/drv/amd64/amd8111s 0755 root sys
+f kernel/drv/amd64/amdzen 0755 root sys
+f kernel/drv/amd64/amdzen_stub 0755 root sys
+f kernel/drv/amd64/amdnbtemp 0755 root sys
+f kernel/drv/amd64/amr 0755 root sys
+f kernel/drv/amd64/arcmsr 0755 root sys
+f kernel/drv/amd64/arp 0755 root sys
+f kernel/drv/amd64/asy 0755 root sys
+f kernel/drv/amd64/ata 0755 root sys
+f kernel/drv/amd64/atge 0755 root sys
+f kernel/drv/amd64/audio 0755 root sys
+f kernel/drv/amd64/axf 0755 root sys
+f kernel/drv/amd64/bcm_sata 0755 root sys
+f kernel/drv/amd64/bfe 0755 root sys
+f kernel/drv/amd64/bge 0755 root sys
+f kernel/drv/amd64/blkdev 0755 root sys
+f kernel/drv/amd64/bnx 0755 root sys
+f kernel/drv/amd64/bnxe 0755 root sys
+f kernel/drv/amd64/bridge 0755 root sys
+f kernel/drv/amd64/ccid 0755 root sys
+f kernel/drv/amd64/chxge 0755 root sys
+f kernel/drv/amd64/clone 0755 root sys
+f kernel/drv/amd64/cmdk 0755 root sys
+f kernel/drv/amd64/cn 0755 root sys
+f kernel/drv/amd64/conskbd 0755 root sys
+f kernel/drv/amd64/consms 0755 root sys
+f kernel/drv/amd64/coretemp 0755 root sys
+f kernel/drv/amd64/cpqary3 0755 root sys
+f kernel/drv/amd64/cpuid 0755 root sys
+f kernel/drv/amd64/cpunex 0755 root sys
+f kernel/drv/amd64/crypto 0755 root sys
+f kernel/drv/amd64/cryptoadm 0755 root sys
+f kernel/drv/amd64/cxgbe 0755 root sys
+f kernel/drv/amd64/dcpc 0755 root sys
+f kernel/drv/amd64/devinfo 0755 root sys
+f kernel/drv/amd64/dld 0755 root sys
+f kernel/drv/amd64/dlpistub 0755 root sys
+f kernel/drv/amd64/dmfe 0755 root sys
+f kernel/drv/amd64/dnet 0755 root sys
+f kernel/drv/amd64/dprov 0755 root sys
+f kernel/drv/amd64/dtrace 0755 root sys
+f kernel/drv/amd64/e1000g 0755 root sys
+f kernel/drv/amd64/ehci 0755 root sys
+f kernel/drv/amd64/eibnx 0755 root sys
+f kernel/drv/amd64/ena 0755 root sys
+f kernel/drv/amd64/fasttrap 0755 root sys
+f kernel/drv/amd64/fbt 0755 root sys
+f kernel/drv/amd64/fcp 0755 root sys
+f kernel/drv/amd64/fcsm 0755 root sys
+f kernel/drv/amd64/fm 0755 root sys
+f kernel/drv/amd64/fp 0755 root sys
+f kernel/drv/amd64/hid 0755 root sys
+f kernel/drv/amd64/hubd 0755 root sys
+f kernel/drv/amd64/hxge 0755 root sys
+f kernel/drv/amd64/i40e 0755 root sys
+f kernel/drv/amd64/i8042 0755 root sys
+f kernel/drv/amd64/icmp 0755 root sys
+f kernel/drv/amd64/icmp6 0755 root sys
+f kernel/drv/amd64/igb 0755 root sys
+f kernel/drv/amd64/imc 0755 root sys
+f kernel/drv/amd64/imcstub 0755 root sys
+f kernel/drv/amd64/intel_nb5000 0755 root sys
+f kernel/drv/amd64/intel_nhm 0755 root sys
+f kernel/drv/amd64/intel_nhmex 0755 root sys
+f kernel/drv/amd64/ip 0755 root sys
+f kernel/drv/amd64/ip6 0755 root sys
+f kernel/drv/amd64/ipd 0755 root sys
+f kernel/drv/amd64/ipnet 0755 root sys
+f kernel/drv/amd64/ippctl 0755 root sys
+f kernel/drv/amd64/iprb 0755 root sys
+f kernel/drv/amd64/ipsecah 0755 root sys
+f kernel/drv/amd64/ipsecesp 0755 root sys
+f kernel/drv/amd64/iptun 0755 root sys
+f kernel/drv/amd64/iscsi 0755 root sys
+f kernel/drv/amd64/iscsit 0755 root sys
+f kernel/drv/amd64/iwn 0755 root sys
+f kernel/drv/amd64/iwscn 0755 root sys
+f kernel/drv/amd64/ixgb 0755 root sys
+f kernel/drv/amd64/ixgbe 0755 root sys
+f kernel/drv/amd64/kb8042 0755 root sys
+f kernel/drv/amd64/keysock 0755 root sys
+f kernel/drv/amd64/ksensor 0755 root sys
+f kernel/drv/amd64/kmdb 0755 root sys
+f kernel/drv/amd64/llc1 0755 root sys
+f kernel/drv/amd64/lockstat 0755 root sys
+f kernel/drv/amd64/lofi 0755 root sys
+f kernel/drv/amd64/log 0755 root sys
+f kernel/drv/amd64/lsimega 0755 root sys
+f kernel/drv/amd64/marvell88sx 0755 root sys
+f kernel/drv/amd64/mega_sas 0755 root sys
+f kernel/drv/amd64/mlxcx 0755 root sys
+f kernel/drv/amd64/mm 0755 root sys
+f kernel/drv/amd64/mpt 0755 root sys
+f kernel/drv/amd64/mpt_sas 0755 root sys
+f kernel/drv/amd64/mr_sas 0755 root sys
+f kernel/drv/amd64/myri10ge 0755 root sys
+f kernel/drv/amd64/nfp 0755 root sys
+f kernel/drv/amd64/nge 0755 root sys
+f kernel/drv/amd64/ntxn 0755 root sys
+f kernel/drv/amd64/nulldriver 0755 root sys
+f kernel/drv/amd64/nv_sata 0755 root sys
+f kernel/drv/amd64/nvme 0755 root sys
+f kernel/drv/amd64/nxge 0755 root sys
+f kernel/drv/amd64/oce 0755 root sys
+f kernel/drv/amd64/ohci 0755 root sys
+f kernel/drv/amd64/openeepr 0755 root sys
+f kernel/drv/amd64/options 0755 root sys
+f kernel/drv/amd64/overlay 0755 root sys
+f kernel/drv/amd64/pchtemp 0755 root sys
+f kernel/drv/amd64/pci_pci 0755 root sys
+f kernel/drv/amd64/pcieb 0755 root sys
+f kernel/drv/amd64/physmem 0755 root sys
+f kernel/drv/amd64/pmcs 0755 root sys
+f kernel/drv/amd64/poll 0755 root sys
+f kernel/drv/amd64/power 0755 root sys
+f kernel/drv/amd64/profile 0755 root sys
+f kernel/drv/amd64/pseudo 0755 root sys
+f kernel/drv/amd64/ptc 0755 root sys
+f kernel/drv/amd64/ptsl 0755 root sys
+f kernel/drv/amd64/pvscsi 0755 root sys
+f kernel/drv/amd64/qede 0755 root sys
+f kernel/drv/amd64/qlc 0755 root sys
+f kernel/drv/amd64/qlge 0755 root sys
+f kernel/drv/amd64/ramdisk 0755 root sys
+f kernel/drv/amd64/random 0755 root sys
+f kernel/drv/amd64/rge 0755 root sys
+f kernel/drv/amd64/rtls 0755 root sys
+f kernel/drv/amd64/rts 0755 root sys
+f kernel/drv/amd64/sad 0755 root sys
+f kernel/drv/amd64/scsa2usb 0755 root sys
+f kernel/drv/amd64/scsi_vhci 0755 root sys
+f kernel/drv/amd64/sd 0755 root sys
+f kernel/drv/amd64/sdhost 0755 root sys
+f kernel/drv/amd64/sdt 0755 root sys
+f kernel/drv/amd64/ses 0755 root sys
+f kernel/drv/amd64/sfe 0755 root sys
+f kernel/drv/amd64/sfxge 0755 root sys
+f kernel/drv/amd64/sgen 0755 root sys
+f kernel/drv/amd64/si3124 0755 root sys
+f kernel/drv/amd64/simnet 0755 root sys
+f kernel/drv/amd64/skd 0755 root sys
+f kernel/drv/amd64/smbios 0755 root sys
+f kernel/drv/amd64/smntemp 0755 root sys
+f kernel/drv/amd64/smp 0755 root sys
+f kernel/drv/amd64/smrt 0755 root sys
+f kernel/drv/amd64/softmac 0755 root sys
+f kernel/drv/amd64/spdsock 0755 root sys
+f kernel/drv/amd64/srn 0755 root sys
+f kernel/drv/amd64/st 0755 root sys
+f kernel/drv/amd64/stmf 0755 root sys
+f kernel/drv/amd64/stmf_sbd 0755 root sys
+f kernel/drv/amd64/sy 0755 root sys
+f kernel/drv/amd64/sysevent 0755 root sys
+f kernel/drv/amd64/sysmsg 0755 root sys
+f kernel/drv/amd64/systrace 0755 root sys
+f kernel/drv/amd64/t4nex 0755 root sys
+f kernel/drv/amd64/tcp 0755 root sys
+f kernel/drv/amd64/tcp6 0755 root sys
+f kernel/drv/amd64/tl 0755 root sys
+f kernel/drv/amd64/tzmon 0755 root sys
+f kernel/drv/amd64/ucode 0755 root sys
+f kernel/drv/amd64/udmf 0755 root sys
+f kernel/drv/amd64/udp 0755 root sys
+f kernel/drv/amd64/udp6 0755 root sys
+f kernel/drv/amd64/ufm 0755 root sys
+f kernel/drv/amd64/ugen 0755 root sys
+f kernel/drv/amd64/uhci 0755 root sys
+f kernel/drv/amd64/upf 0755 root sys
+f kernel/drv/amd64/urf 0755 root sys
+f kernel/drv/amd64/usb_ac 0755 root sys
+f kernel/drv/amd64/usb_as 0755 root sys
+f kernel/drv/amd64/usb_ia 0755 root sys
+f kernel/drv/amd64/usb_mid 0755 root sys
+f kernel/drv/amd64/usbecm 0755 root sys
+f kernel/drv/amd64/usbftdi 0755 root sys
+f kernel/drv/amd64/usbsacm 0755 root sys
+f kernel/drv/amd64/usbser_edge 0755 root sys
+f kernel/drv/amd64/usbskel 0755 root sys
+f kernel/drv/amd64/usbsksp 0755 root sys
+f kernel/drv/amd64/usbsprl 0755 root sys
+f kernel/drv/amd64/vgatext 0755 root sys
+f kernel/drv/amd64/vio9p 0755 root sys
+f kernel/drv/amd64/vioblk 0755 root sys
+f kernel/drv/amd64/vioif 0755 root sys
+f kernel/drv/amd64/vioscsi 0755 root sys
+f kernel/drv/amd64/vmxnet 0755 root sys
+f kernel/drv/amd64/vmxnet3s 0755 root sys
+f kernel/drv/amd64/vnd 0755 root sys
+f kernel/drv/amd64/vnic 0755 root sys
+f kernel/drv/amd64/vr 0755 root sys
+f kernel/drv/amd64/vscan 0755 root sys
+f kernel/drv/amd64/wc 0755 root sys
+f kernel/drv/amd64/xge 0755 root sys
+f kernel/drv/amd64/xhci 0755 root sys
+f kernel/drv/amd64/yge 0755 root sys
+f kernel/drv/amd64/zen_umc 0755 root sys
+f kernel/drv/amd64/zfs 0755 root sys
+f kernel/drv/amr.conf 0644 root sys
+f kernel/drv/arcmsr.conf 0644 root sys
+f kernel/drv/arp.conf 0644 root sys
+f kernel/drv/asy.conf 0644 root sys
+f kernel/drv/ata.conf 0644 root sys
+f kernel/drv/audio.conf 0644 root sys
+f kernel/drv/bge.conf 0644 root sys
+f kernel/drv/bl.conf 0644 root sys
+f kernel/drv/bnx.conf 0644 root sys
+f kernel/drv/bnxe.conf 0644 root sys
+f kernel/drv/bridge.conf 0644 root sys
+f kernel/drv/clone.conf 0644 root sys
+f kernel/drv/cmdk.conf 0644 root sys
+f kernel/drv/cn.conf 0644 root sys
+f kernel/drv/conskbd.conf 0644 root sys
+f kernel/drv/consms.conf 0644 root sys
+f kernel/drv/coretemp.conf 0644 root sys
+f kernel/drv/cpqary3.conf 0644 root sys
+f kernel/drv/cpuid.conf 0644 root sys
+f kernel/drv/crypto.conf 0644 root sys
+f kernel/drv/cryptoadm.conf 0644 root sys
+f kernel/drv/dcpc.conf 0644 root sys
+f kernel/drv/devinfo.conf 0644 root sys
+f kernel/drv/dld.conf 0644 root sys
+f kernel/drv/dlpistub.conf 0644 root sys
+f kernel/drv/dprov.conf 0644 root sys
+f kernel/drv/dtrace.conf 0644 root sys
+f kernel/drv/e1000g.conf 0644 root sys
+f kernel/drv/ehci.conf 0644 root sys
+f kernel/drv/eibnx.conf 0644 root sys
+f kernel/drv/ena.conf 0644 root sys
+f kernel/drv/fasttrap.conf 0644 root sys
+f kernel/drv/fbt.conf 0644 root sys
+f kernel/drv/fcp.conf 0644 root sys
+f kernel/drv/fcsm.conf 0644 root sys
+f kernel/drv/fm.conf 0644 root sys
+f kernel/drv/fp.conf 0644 root sys
+f kernel/drv/hxge.conf 0644 root sys
+f kernel/drv/i40e.conf 0644 root sys
+f kernel/drv/icmp.conf 0644 root sys
+f kernel/drv/icmp6.conf 0644 root sys
+f kernel/drv/igb.conf 0644 root sys
+f kernel/drv/imc.conf 0644 root sys
+f kernel/drv/intel_nb5000.conf 0644 root sys
+f kernel/drv/intel_nhm.conf 0644 root sys
+f kernel/drv/intel_nhmex.conf 0644 root sys
+f kernel/drv/ip.conf 0644 root sys
+f kernel/drv/ip6.conf 0644 root sys
+f kernel/drv/ipd.conf 0644 root sys
+f kernel/drv/ipnet.conf 0644 root sys
+f kernel/drv/ippctl.conf 0644 root sys
+f kernel/drv/ipsecah.conf 0644 root sys
+f kernel/drv/ipsecesp.conf 0644 root sys
+f kernel/drv/iptun.conf 0644 root sys
+f kernel/drv/iscsi.conf 0644 root sys
+f kernel/drv/iscsit.conf 0644 root sys
+f kernel/drv/iwscn.conf 0644 root sys
+f kernel/drv/ixgbe.conf 0644 root sys
+f kernel/drv/keysock.conf 0644 root sys
+f kernel/drv/kmdb.conf 0644 root sys
+f kernel/drv/ksensor.conf 0644 root sys
+f kernel/drv/llc1.conf 0644 root sys
+f kernel/drv/lockstat.conf 0644 root sys
+f kernel/drv/lofi.conf 0644 root sys
+f kernel/drv/log.conf 0644 root sys
+f kernel/drv/lsimega.conf 0644 root sys
+f kernel/drv/mega_sas.conf 0644 root sys
+f kernel/drv/mlxcx.conf 0644 root sys
+f kernel/drv/mm.conf 0644 root sys
+f kernel/drv/mpt.conf 0644 root sys
+f kernel/drv/mpt_sas.conf 0644 root sys
+f kernel/drv/mr_sas.conf 0644 root sys
+f kernel/drv/nge.conf 0644 root sys
+f kernel/drv/ntxn.conf 0644 root sys
+f kernel/drv/nv_sata.conf 0644 root sys
+f kernel/drv/nvme.conf 0644 root sys
+f kernel/drv/nxge.conf 0644 root sys
+f kernel/drv/ohci.conf 0644 root sys
+f kernel/drv/openeepr.conf 0644 root sys
+f kernel/drv/options.conf 0644 root sys
+f kernel/drv/overlay.conf 0644 root sys
+f kernel/drv/pcieb.conf 0644 root sys
+f kernel/drv/physmem.conf 0644 root sys
+f kernel/drv/pmcs.conf 0644 root sys
+f kernel/drv/poll.conf 0644 root sys
+f kernel/drv/power.conf 0644 root sys
+f kernel/drv/profile.conf 0644 root sys
+f kernel/drv/pseudo.conf 0644 root sys
+f kernel/drv/ptc.conf 0644 root sys
+f kernel/drv/ptsl.conf 0644 root sys
+f kernel/drv/qede.conf 0644 root sys
+f kernel/drv/qlc.conf 0644 root sys
+f kernel/drv/ramdisk.conf 0644 root sys
+f kernel/drv/random.conf 0644 root sys
+f kernel/drv/sad.conf 0644 root sys
+f kernel/drv/scsa2usb.conf 0644 root sys
+f kernel/drv/scsi_vhci.conf 0644 root sys
+f kernel/drv/sd.conf 0644 root sys
+f kernel/drv/sdt.conf 0644 root sys
+f kernel/drv/ses.conf 0644 root sys
+f kernel/drv/sgen.conf 0644 root sys
+f kernel/drv/simnet.conf 0644 root sys
+f kernel/drv/skd.conf 0644 root sys
+f kernel/drv/smbios.conf 0644 root sys
+f kernel/drv/smp.conf 0644 root sys
+f kernel/drv/smrt.conf 0644 root sys
+f kernel/drv/softmac.conf 0644 root sys
+f kernel/drv/spdsock.conf 0644 root sys
+f kernel/drv/srn.conf 0644 root sys
+f kernel/drv/st.conf 0644 root sys
+f kernel/drv/stmf.conf 0644 root sys
+f kernel/drv/stmf_sbd.conf 0644 root sys
+f kernel/drv/sy.conf 0644 root sys
+f kernel/drv/sysevent.conf 0644 root sys
+f kernel/drv/sysmsg.conf 0644 root sys
+f kernel/drv/systrace.conf 0644 root sys
+f kernel/drv/tcp.conf 0644 root sys
+f kernel/drv/tcp6.conf 0644 root sys
+f kernel/drv/tl.conf 0644 root sys
+f kernel/drv/tzmon.conf 0644 root sys
+f kernel/drv/ucode.conf 0644 root sys
+f kernel/drv/udp.conf 0644 root sys
+f kernel/drv/udp6.conf 0644 root sys
+f kernel/drv/ufm.conf 0644 root sys
+f kernel/drv/uhci.conf 0644 root sys
+f kernel/drv/usbftdi.conf 0644 root sys
+f kernel/drv/usbser_edge.conf 0644 root sys
+f kernel/drv/usbskel.conf 0644 root sys
+f kernel/drv/vmxnet.conf 0644 root sys
+f kernel/drv/vmxnet3s.conf 0644 root sys
+f kernel/drv/vnd.conf 0644 root sys
+f kernel/drv/vnic.conf 0644 root sys
+f kernel/drv/vscan.conf 0644 root sys
+f kernel/drv/wc.conf 0644 root sys
+f kernel/drv/xhci.conf 0644 root sys
+f kernel/drv/zfs.conf 0644 root sys
+d kernel/dtrace 0755 root sys
+d kernel/dtrace/amd64 0755 root sys
+f kernel/dtrace/amd64/dcpc 0755 root sys
+f kernel/dtrace/amd64/fasttrap 0755 root sys
+f kernel/dtrace/amd64/fbt 0755 root sys
+f kernel/dtrace/amd64/lockstat 0755 root sys
+f kernel/dtrace/amd64/profile 0755 root sys
+f kernel/dtrace/amd64/sdt 0755 root sys
+f kernel/dtrace/amd64/systrace 0755 root sys
+d kernel/exec 0755 root sys
+d kernel/exec/amd64 0755 root sys
+f kernel/exec/amd64/elfexec 0755 root sys
+f kernel/exec/amd64/intpexec 0755 root sys
+d kernel/firmware 0755 root sys
+d kernel/firmware/iwn 0755 root sys
+f kernel/firmware/iwn/iwlwifi-100-5.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-1000-3.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-105-6.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-135-6.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-2000-6.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-2030-6.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-4965-2.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-5000-2.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-5150-2.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-6000-4.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-6000g2a-6.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-6000g2b-6.ucode 0666 root sys
+f kernel/firmware/iwn/iwlwifi-6050-5.ucode 0666 root sys
+d kernel/fs 0755 root sys
+d kernel/fs/amd64 0755 root sys
+f kernel/fs/amd64/autofs 0755 root sys
+f kernel/fs/amd64/bootfs 0755 root sys
+f kernel/fs/amd64/ctfs 0755 root sys
+f kernel/fs/amd64/dcfs 0755 root sys
+f kernel/fs/amd64/dev 0755 root sys
+f kernel/fs/amd64/devfs 0755 root sys
+f kernel/fs/amd64/fifofs 0755 root sys
+f kernel/fs/amd64/hsfs 0755 root sys
+f kernel/fs/amd64/hyprlofs 0755 root sys
+f kernel/fs/amd64/lofs 0755 root sys
+f kernel/fs/amd64/lxprocfs 0755 root sys
+f kernel/fs/amd64/mntfs 0755 root sys
+f kernel/fs/amd64/namefs 0755 root sys
+f kernel/fs/amd64/nfs 0755 root sys
+f kernel/fs/amd64/objfs 0755 root sys
+f kernel/fs/amd64/procfs 0755 root sys
+f kernel/fs/amd64/sharefs 0755 root sys
+f kernel/fs/amd64/sockfs 0755 root sys
+f kernel/fs/amd64/specfs 0755 root sys
+f kernel/fs/amd64/tmpfs 0755 root sys
+f kernel/fs/amd64/ufs 0755 root sys
+h kernel/fs/amd64/zfs=kernel/drv/amd64/zfs
+d kernel/ipp 0755 root sys
+d kernel/ipp/amd64 0755 root sys
+f kernel/ipp/amd64/dlcosmk 0755 root sys
+f kernel/ipp/amd64/dscpmk 0755 root sys
+f kernel/ipp/amd64/flowacct 0755 root sys
+f kernel/ipp/amd64/ipgpc 0755 root sys
+f kernel/ipp/amd64/tokenmt 0755 root sys
+f kernel/ipp/amd64/tswtclmt 0755 root sys
+d kernel/kiconv 0755 root sys
+d kernel/kiconv/amd64 0755 root sys
+f kernel/kiconv/amd64/kiconv_emea 0755 root sys
+f kernel/kiconv/amd64/kiconv_ja 0755 root sys
+f kernel/kiconv/amd64/kiconv_ko 0755 root sys
+f kernel/kiconv/amd64/kiconv_sc 0755 root sys
+f kernel/kiconv/amd64/kiconv_tc 0755 root sys
+d kernel/kmdb 0755 root sys
+d kernel/kmdb/amd64 0755 root sys
+f kernel/kmdb/amd64/arp 0555 root sys
+f kernel/kmdb/amd64/cpc 0555 root sys
+f kernel/kmdb/amd64/cpu.generic 0555 root sys
+f kernel/kmdb/amd64/cpu_ms.AuthenticAMD.15 0555 root sys
+f kernel/kmdb/amd64/crypto 0555 root sys
+f kernel/kmdb/amd64/emlxs 0555 root sys
+f kernel/kmdb/amd64/fcp 0555 root sys
+f kernel/kmdb/amd64/fctl 0555 root sys
+f kernel/kmdb/amd64/genunix 0555 root sys
+f kernel/kmdb/amd64/hook 0555 root sys
+f kernel/kmdb/amd64/i40e 0555 root sys
+f kernel/kmdb/amd64/idm 0555 root sys
+f kernel/kmdb/amd64/ip 0555 root sys
+f kernel/kmdb/amd64/ipc 0555 root sys
+f kernel/kmdb/amd64/ipp 0555 root sys
+f kernel/kmdb/amd64/krtld 0555 root sys
+f kernel/kmdb/amd64/lofs 0555 root sys
+f kernel/kmdb/amd64/logindmux 0555 root sys
+f kernel/kmdb/amd64/mac 0555 root sys
+f kernel/kmdb/amd64/mdb_ds 0555 root sys
+f kernel/kmdb/amd64/mm 0555 root sys
+f kernel/kmdb/amd64/mpt 0555 root sys
+f kernel/kmdb/amd64/mpt_sas 0555 root sys
+f kernel/kmdb/amd64/mr_sas 0555 root sys
+f kernel/kmdb/amd64/neti 0555 root sys
+f kernel/kmdb/amd64/nfs 0555 root sys
+f kernel/kmdb/amd64/ptm 0555 root sys
+f kernel/kmdb/amd64/qlc 0555 root sys
+f kernel/kmdb/amd64/random 0555 root sys
+f kernel/kmdb/amd64/s1394 0555 root sys
+f kernel/kmdb/amd64/sata 0555 root sys
+f kernel/kmdb/amd64/scsi_vhci 0555 root sys
+f kernel/kmdb/amd64/sctp 0555 root sys
+f kernel/kmdb/amd64/sd 0555 root sys
+f kernel/kmdb/amd64/sockfs 0555 root sys
+f kernel/kmdb/amd64/specfs 0555 root sys
+f kernel/kmdb/amd64/sppp 0555 root sys
+f kernel/kmdb/amd64/srpt 0555 root sys
+f kernel/kmdb/amd64/stmf 0555 root sys
+f kernel/kmdb/amd64/stmf_sbd 0555 root sys
+f kernel/kmdb/amd64/ufs 0555 root sys
+f kernel/kmdb/amd64/uhci 0555 root sys
+f kernel/kmdb/amd64/usba 0555 root sys
+f kernel/kmdb/amd64/xhci 0555 root sys
+f kernel/kmdb/amd64/zfs 0555 root sys
+d kernel/mac 0755 root sys
+d kernel/mac/amd64 0755 root sys
+f kernel/mac/amd64/mac_6to4 0755 root sys
+f kernel/mac/amd64/mac_ether 0755 root sys
+f kernel/mac/amd64/mac_ib 0755 root sys
+f kernel/mac/amd64/mac_ipv4 0755 root sys
+f kernel/mac/amd64/mac_ipv6 0755 root sys
+f kernel/mac/amd64/mac_wifi 0755 root sys
+d kernel/misc 0755 root sys
+d kernel/misc/amd64 0755 root sys
+f kernel/misc/amd64/ac97 0755 root sys
+f kernel/misc/amd64/acpica 0755 root sys
+f kernel/misc/amd64/bignum 0755 root sys
+f kernel/misc/amd64/bootdev 0755 root sys
+f kernel/misc/amd64/busra 0755 root sys
+f kernel/misc/amd64/cc 0755 root sys
+f kernel/misc/amd64/cmlb 0755 root sys
+f kernel/misc/amd64/consconfig 0755 root sys
+f kernel/misc/amd64/ctf 0755 root sys
+f kernel/misc/amd64/dadk 0755 root sys
+f kernel/misc/amd64/dcopy 0755 root sys
+s kernel/misc/amd64/des=../../../kernel/crypto/amd64/des
+f kernel/misc/amd64/dls 0755 root sys
+f kernel/misc/amd64/edonr 0755 root sys
+f kernel/misc/amd64/fctl 0755 root sys
+f kernel/misc/amd64/fssnap_if 0755 root sys
+f kernel/misc/amd64/gda 0755 root sys
+f kernel/misc/amd64/gld 0755 root sys
+f kernel/misc/amd64/gsqueue 0755 root sys
+f kernel/misc/amd64/hidparser 0755 root sys
+f kernel/misc/amd64/hook 0755 root sys
+f kernel/misc/amd64/hpcsvc 0755 root sys
+f kernel/misc/amd64/idm 0755 root sys
+f kernel/misc/amd64/idmap 0755 root sys
+f kernel/misc/amd64/iommulib 0755 root sys
+f kernel/misc/amd64/ipc 0755 root sys
+f kernel/misc/amd64/kbtrans 0755 root sys
+f kernel/misc/amd64/kcf 0755 root sys
+f kernel/misc/amd64/kgssapi 0755 root sys
+f kernel/misc/amd64/klmmod 0755 root sys
+f kernel/misc/amd64/klmops 0755 root sys
+f kernel/misc/amd64/kmdbmod 0755 root sys
+f kernel/misc/amd64/ksocket 0755 root sys
+f kernel/misc/amd64/mac 0755 root sys
+f kernel/misc/amd64/md5 0755 root sys
+f kernel/misc/amd64/mii 0755 root sys
+f kernel/misc/amd64/mwlfw 0755 root sys
+f kernel/misc/amd64/net80211 0755 root sys
+f kernel/misc/amd64/neti 0755 root sys
+f kernel/misc/amd64/nfs_dlboot 0755 root sys
+f kernel/misc/amd64/nfssrv 0755 root sys
+f kernel/misc/amd64/pci_autoconfig 0755 root sys
+f kernel/misc/amd64/pcicfg 0755 root sys
+f kernel/misc/amd64/pcie 0755 root sys
+f kernel/misc/amd64/pcihp 0755 root sys
+f kernel/misc/amd64/pcmcia 0755 root sys
+f kernel/misc/amd64/rpcsec 0755 root sys
+f kernel/misc/amd64/rpcsec_gss 0755 root sys
+f kernel/misc/amd64/rsmops 0755 root sys
+f kernel/misc/amd64/s1394 0755 root sys
+f kernel/misc/amd64/sata 0755 root sys
+f kernel/misc/amd64/sbp2 0755 root sys
+f kernel/misc/amd64/scsi 0755 root sys
+f kernel/misc/amd64/sda 0755 root sys
+f kernel/misc/amd64/sha1 0755 root sys
+f kernel/misc/amd64/sha2 0755 root sys
+f kernel/misc/amd64/skein 0755 root sys
+f kernel/misc/amd64/strategy 0755 root sys
+f kernel/misc/amd64/strplumb 0755 root sys
+f kernel/misc/amd64/tem 0755 root sys
+f kernel/misc/amd64/tlimod 0755 root sys
+f kernel/misc/amd64/usba 0755 root sys
+f kernel/misc/amd64/usba10 0755 root sys
+f kernel/misc/amd64/usbgem 0755 root sys
+f kernel/misc/amd64/usbs49_fw 0755 root sys
+f kernel/misc/amd64/usbser 0755 root sys
+f kernel/misc/amd64/virtio 0755 root sys
+d kernel/misc/kgss 0755 root sys
+d kernel/misc/kgss/amd64 0755 root sys
+f kernel/misc/kgss/amd64/kmech_krb5 0755 root sys
+d kernel/misc/pmcs 0755 root sys
+d kernel/misc/pmcs/amd64 0755 root sys
+f kernel/misc/pmcs/amd64/pmcs8001fw 0755 root sys
+d kernel/misc/qlc 0755 root sys
+d kernel/misc/qlc/amd64 0755 root sys
+f kernel/misc/qlc/amd64/qlc_fw_2200 0755 root sys
+f kernel/misc/qlc/amd64/qlc_fw_2300 0755 root sys
+f kernel/misc/qlc/amd64/qlc_fw_2400 0755 root sys
+f kernel/misc/qlc/amd64/qlc_fw_2500 0755 root sys
+f kernel/misc/qlc/amd64/qlc_fw_6322 0755 root sys
+f kernel/misc/qlc/amd64/qlc_fw_8100 0755 root sys
+d kernel/misc/scsi_vhci 0755 root sys
+d kernel/misc/scsi_vhci/amd64 0755 root sys
+f kernel/misc/scsi_vhci/amd64/scsi_vhci_f_asym_emc 0755 root sys
+f kernel/misc/scsi_vhci/amd64/scsi_vhci_f_asym_lsi 0755 root sys
+f kernel/misc/scsi_vhci/amd64/scsi_vhci_f_sym 0755 root sys
+f kernel/misc/scsi_vhci/amd64/scsi_vhci_f_sym_emc 0755 root sys
+f kernel/misc/scsi_vhci/amd64/scsi_vhci_f_sym_hds 0755 root sys
+f kernel/misc/scsi_vhci/amd64/scsi_vhci_f_tape 0755 root sys
+f kernel/misc/scsi_vhci/amd64/scsi_vhci_f_tpgs 0755 root sys
+f kernel/misc/scsi_vhci/amd64/scsi_vhci_f_tpgs_tape 0755 root sys
+d kernel/overlay 0755 root sys
+d kernel/overlay/amd64 0755 root sys
+f kernel/overlay/amd64/vxlan 0755 root sys
+d kernel/sched 0755 root sys
+d kernel/sched/amd64 0755 root sys
+f kernel/sched/amd64/SDC 0755 root sys
+f kernel/sched/amd64/TS 0755 root sys
+f kernel/sched/amd64/TS_DPTBL 0755 root sys
+d kernel/socketmod 0755 root sys
+d kernel/socketmod/amd64 0755 root sys
+f kernel/socketmod/amd64/datafilt 0755 root sys
+h kernel/socketmod/amd64/icmp=kernel/drv/amd64/icmp
+f kernel/socketmod/amd64/lx_netlink 0755 root sys
+f kernel/socketmod/amd64/rts 0755 root sys
+f kernel/socketmod/amd64/sockrds 0755 root sys
+f kernel/socketmod/amd64/socksctp 0755 root sys
+f kernel/socketmod/amd64/socksdp 0755 root sys
+h kernel/socketmod/amd64/tcp=kernel/drv/amd64/tcp
+f kernel/socketmod/amd64/trill 0755 root sys
+h kernel/socketmod/amd64/udp=kernel/drv/amd64/udp
+d kernel/strmod 0755 root sys
+d kernel/strmod/amd64 0755 root sys
+f kernel/strmod/amd64/arp 0755 root sys
+f kernel/strmod/amd64/bufmod 0755 root sys
+f kernel/strmod/amd64/connld 0755 root sys
+f kernel/strmod/amd64/dedump 0755 root sys
+f kernel/strmod/amd64/drcompat 0755 root sys
+h kernel/strmod/amd64/icmp=kernel/drv/amd64/icmp
+f kernel/strmod/amd64/ip 0755 root sys
+f kernel/strmod/amd64/ipsecah 0755 root sys
+f kernel/strmod/amd64/ipsecesp 0755 root sys
+f kernel/strmod/amd64/keysock 0755 root sys
+f kernel/strmod/amd64/ldterm 0755 root sys
+f kernel/strmod/amd64/pckt 0755 root sys
+f kernel/strmod/amd64/pfmod 0755 root sys
+f kernel/strmod/amd64/pipemod 0755 root sys
+f kernel/strmod/amd64/ptem 0755 root sys
+f kernel/strmod/amd64/redirmod 0755 root sys
+f kernel/strmod/amd64/rpcmod 0755 root sys
+f kernel/strmod/amd64/sdpib 0755 root sys
+h kernel/strmod/amd64/tcp=kernel/drv/amd64/tcp
+f kernel/strmod/amd64/timod 0755 root sys
+f kernel/strmod/amd64/tirdwr 0755 root sys
+f kernel/strmod/amd64/ttcompat 0755 root sys
+h kernel/strmod/amd64/udp=kernel/drv/amd64/udp
+f kernel/strmod/amd64/usb_ah 0755 root sys
+f kernel/strmod/amd64/usbkbm 0755 root sys
+f kernel/strmod/amd64/usbms 0755 root sys
+f kernel/strmod/amd64/usbwcm 0755 root sys
+f kernel/strmod/amd64/vuid2ps2 0755 root sys
+f kernel/strmod/amd64/vuid3ps2 0755 root sys
+f kernel/strmod/amd64/vuidm3p 0755 root sys
+f kernel/strmod/amd64/vuidm4p 0755 root sys
+f kernel/strmod/amd64/vuidm5p 0755 root sys
+d kernel/sys 0755 root sys
+d kernel/sys/amd64 0755 root sys
+f kernel/sys/amd64/autofs 0755 root sys
+f kernel/sys/amd64/c2audit 0755 root sys
+f kernel/sys/amd64/doorfs 0755 root sys
+f kernel/sys/amd64/inst_sync 0755 root sys
+f kernel/sys/amd64/kaio 0755 root sys
+f kernel/sys/amd64/msgsys 0755 root sys
+f kernel/sys/amd64/nfs 0755 root sys
+f kernel/sys/amd64/pipe 0755 root sys
+f kernel/sys/amd64/portfs 0755 root sys
+f kernel/sys/amd64/pset 0755 root sys
+f kernel/sys/amd64/rpcmod 0755 root sys
+f kernel/sys/amd64/semsys 0755 root sys
+f kernel/sys/amd64/shmsys 0755 root sys
+d lib 0755 root bin
+s lib/32=.
+s lib/64=amd64
+d lib/amd64 0755 root bin
+f lib/amd64/c_synonyms.so.1 0755 root bin
+f lib/amd64/ld.so.1 0755 root bin
+f lib/amd64/libMPAPI.so.1 0755 root bin
+s lib/amd64/libMPAPI.so=libMPAPI.so.1
+f lib/amd64/libadm.so.1 0755 root bin
+s lib/amd64/libadm.so=libadm.so.1
+f lib/amd64/libaio.so.1 0755 root bin
+s lib/amd64/libaio.so=libaio.so.1
+f lib/amd64/libavl.so.1 0755 root bin
+s lib/amd64/libavl.so=libavl.so.1
+f lib/amd64/libbsm.so.1 0755 root bin
+s lib/amd64/libbsm.so=libbsm.so.1
+f lib/amd64/libc.so.1 0755 root bin
+s lib/amd64/libc.so=libc.so.1
+f lib/amd64/libc_db.so.1 0755 root bin
+s lib/amd64/libc_db.so=libc_db.so.1
+f lib/amd64/libcmdutils.so.1 0755 root bin
+s lib/amd64/libcmdutils.so=libcmdutils.so.1
+f lib/amd64/libcontract.so.1 0755 root bin
+s lib/amd64/libcontract.so=libcontract.so.1
+f lib/amd64/libcryptoutil.so.1 0755 root bin
+s lib/amd64/libcryptoutil.so=libcryptoutil.so.1
+f lib/amd64/libctf.so.1 0755 root bin
+s lib/amd64/libctf.so=libctf.so.1
+f lib/amd64/libcurses.so.1 0755 root bin
+s lib/amd64/libcurses.so=libcurses.so.1
+f lib/amd64/libcustr.so.1 0755 root bin
+f lib/amd64/libdevice.so.1 0755 root bin
+s lib/amd64/libdevice.so=libdevice.so.1
+f lib/amd64/libdevid.so.1 0755 root bin
+s lib/amd64/libdevid.so=libdevid.so.1
+f lib/amd64/libdevinfo.so.1 0755 root bin
+s lib/amd64/libdevinfo.so=libdevinfo.so.1
+f lib/amd64/libdhcpagent.so.1 0755 root bin
+s lib/amd64/libdhcpagent.so=libdhcpagent.so.1
+f lib/amd64/libdl.so.1 0755 root bin
+s lib/amd64/libdl.so=libdl.so.1
+f lib/amd64/libdladm.so.1 0755 root bin
+s lib/amd64/libdladm.so=libdladm.so.1
+f lib/amd64/libdlpi.so.1 0755 root bin
+s lib/amd64/libdlpi.so=libdlpi.so.1
+f lib/amd64/libdoor.so.1 0755 root bin
+s lib/amd64/libdoor.so=libdoor.so.1
+f lib/amd64/libdwarf.so.1 0755 root bin
+f lib/amd64/libefi.so.1 0755 root bin
+s lib/amd64/libefi.so=libefi.so.1
+f lib/amd64/libelf.so.1 0755 root bin
+s lib/amd64/libelf.so=libelf.so.1
+f lib/amd64/libfakekernel.so.1 0755 root bin
+f lib/amd64/libfdisk.so.1 0755 root bin
+s lib/amd64/libfdisk.so=libfdisk.so.1
+f lib/amd64/libgen.so.1 0755 root bin
+s lib/amd64/libgen.so=libgen.so.1
+f lib/amd64/libinetutil.so.1 0755 root bin
+s lib/amd64/libinetutil.so=libinetutil.so.1
+f lib/amd64/libintl.so.1 0755 root bin
+s lib/amd64/libintl.so=libintl.so.1
+f lib/amd64/libipadm.so.1 0755 root bin
+s lib/amd64/libipadm.so=libipadm.so.1
+f lib/amd64/libipmp.so.1 0755 root bin
+s lib/amd64/libipmp.so=libipmp.so.1
+f lib/amd64/libkmf.so.1 0755 root bin
+s lib/amd64/libkmf.so=libkmf.so.1
+f lib/amd64/libkmfberder.so.1 0755 root bin
+s lib/amd64/libkmfberder.so=libkmfberder.so.1
+f lib/amd64/libkstat.so.1 0755 root bin
+s lib/amd64/libkstat.so=libkstat.so.1
+f lib/amd64/libld.so.4 0755 root bin
+f lib/amd64/liblddbg.so.4 0755 root bin
+f lib/amd64/libm.so.1 0755 root bin
+f lib/amd64/libm.so.2 0755 root bin
+s lib/amd64/libm.so=libm.so.2
+f lib/amd64/libmd.so.1 0755 root bin
+s lib/amd64/libmd.so=libmd.so.1
+f lib/amd64/libmd5.so.1 0755 root bin
+s lib/amd64/libmd5.so=libmd5.so.1
+f lib/amd64/libmp.so.2 0755 root bin
+s lib/amd64/libmp.so=libmp.so.2
+f lib/amd64/libmpscsi_vhci.so.1 0755 root bin
+s lib/amd64/libmpscsi_vhci.so=libmpscsi_vhci.so.1
+f lib/amd64/libmvec.so.1 0755 root bin
+s lib/amd64/libmvec.so=libmvec.so.1
+f lib/amd64/libnsl.so.1 0755 root bin
+s lib/amd64/libnsl.so=libnsl.so.1
+f lib/amd64/libnvpair.so.1 0755 root bin
+s lib/amd64/libnvpair.so=libnvpair.so.1
+f lib/amd64/libofmt.so.1 0755 root bin
+s lib/amd64/libofmt.so=libofmt.so.1
+f lib/amd64/libpam.so.1 0755 root bin
+s lib/amd64/libpam.so=libpam.so.1
+s lib/amd64/libposix4.so.1=librt.so.1
+s lib/amd64/libposix4.so=libposix4.so.1
+f lib/amd64/libproc.so.1 0755 root bin
+s lib/amd64/libproc.so=libproc.so.1
+f lib/amd64/libpthread.so.1 0755 root bin
+s lib/amd64/libpthread.so=libpthread.so.1
+f lib/amd64/librcm.so.1 0755 root bin
+s lib/amd64/librcm.so=librcm.so.1
+f lib/amd64/libresolv.so.2 0755 root bin
+s lib/amd64/libresolv.so=libresolv.so.2
+f lib/amd64/libresolv_joy.so.2 0755 root bin
+f lib/amd64/librestart.so.1 0755 root bin
+s lib/amd64/librestart.so=librestart.so.1
+f lib/amd64/librpcsvc.so.1 0755 root bin
+s lib/amd64/librpcsvc.so=librpcsvc.so.1
+f lib/amd64/librt.so.1 0755 root bin
+s lib/amd64/librt.so=librt.so.1
+f lib/amd64/librtld.so.1 0755 root bin
+f lib/amd64/librtld_db.so.1 0755 root bin
+s lib/amd64/librtld_db.so=librtld_db.so.1
+f lib/amd64/libscf.so.1 0755 root bin
+s lib/amd64/libscf.so=libscf.so.1
+f lib/amd64/libsec.so.1 0755 root bin
+s lib/amd64/libsec.so=libsec.so.1
+f lib/amd64/libsecdb.so.1 0755 root bin
+s lib/amd64/libsecdb.so=libsecdb.so.1
+f lib/amd64/libsendfile.so.1 0755 root bin
+s lib/amd64/libsendfile.so=libsendfile.so.1
+f lib/amd64/libsmartsshd.so.1 0755 root bin
+s lib/amd64/libsmartsshd.so=libsmartsshd.so.1
+f lib/amd64/libsocket.so.1 0755 root bin
+s lib/amd64/libsocket.so=libsocket.so.1
+f lib/amd64/libsysevent.so.1 0755 root bin
+s lib/amd64/libsysevent.so=libsysevent.so.1
+f lib/amd64/libtermcap.so.1 0755 root bin
+s lib/amd64/libtermcap.so=libtermcap.so.1
+s lib/amd64/libtermlib.so.1=libcurses.so.1
+s lib/amd64/libtermlib.so=libtermlib.so.1
+f lib/amd64/libthread.so.1 0755 root bin
+s lib/amd64/libthread.so=libthread.so.1
+s lib/amd64/libthread_db.so.1=libc_db.so.1
+s lib/amd64/libthread_db.so=libc_db.so.1
+f lib/amd64/libtsnet.so.1 0755 root bin
+s lib/amd64/libtsnet.so=libtsnet.so.1
+f lib/amd64/libtsol.so.2 0755 root bin
+s lib/amd64/libtsol.so=libtsol.so.2
+f lib/amd64/libumem.so.1 0755 root bin
+s lib/amd64/libumem.so=libumem.so.1
+f lib/amd64/libuuid.so.1 0755 root bin
+s lib/amd64/libuuid.so=libuuid.so.1
+f lib/amd64/libuutil.so.1 0755 root bin
+s lib/amd64/libuutil.so=libuutil.so.1
+f lib/amd64/libvarpd.so.1 0755 root bin
+f lib/amd64/libvmm.so.1 0755 root bin
+f lib/amd64/libvmmapi.so.1 0755 root bin
+f lib/amd64/libw.so.1 0755 root bin
+s lib/amd64/libw.so=libw.so.1
+f lib/amd64/libxnet.so.1 0755 root bin
+s lib/amd64/libxnet.so=libxnet.so.1
+f lib/amd64/libzdoor.so.1 0755 root bin
+s lib/amd64/libzdoor.so=libzdoor.so.1
+f lib/amd64/libzfs.so.1 0755 root bin
+s lib/amd64/libzfs.so=libzfs.so.1
+s lib/amd64/libzfs_core.so.1=../../usr/lib/amd64/libzfs_core.so.1
+s lib/amd64/libzfs_core.so=../../usr/lib/amd64/libzfs_core.so
+f lib/amd64/libzutil.so.1 0755 root bin
+s lib/amd64/libzutil.so=libzutil.so.1
+f lib/amd64/nss_compat.so.1 0755 root bin
+f lib/amd64/nss_dns.so.1 0755 root bin
+f lib/amd64/nss_files.so.1 0755 root bin
+f lib/amd64/nss_nis.so.1 0755 root bin
+f lib/amd64/nss_user.so.1 0755 root bin
+f lib/c_synonyms.so.1 0755 root bin
+d lib/crypto 0755 root bin
+s lib/crypto/32=.
+s lib/crypto/64=amd64
+d lib/crypto/amd64 0755 root bin
+f lib/crypto/amd64/kmf_mapper_cn.so.1 0755 root bin
+f lib/crypto/amd64/kmf_nss.so.1 0755 root bin
+f lib/crypto/amd64/kmf_openssl.so.1 0755 root bin
+f lib/crypto/amd64/kmf_pkcs11.so.1 0755 root bin
+f lib/crypto/kmf_mapper_cn.so.1 0755 root bin
+f lib/crypto/kmf_nss.so.1 0755 root bin
+f lib/crypto/kmf_openssl.so.1 0755 root bin
+f lib/crypto/kmf_pkcs11.so.1 0755 root bin
+d lib/fm 0755 root bin
+d lib/fm/amd64 0755 root bin
+f lib/fm/amd64/libfmevent.so.1 0755 root bin
+s lib/fm/amd64/libfmevent.so=libfmevent.so.1
+f lib/fm/libfmevent.so.1 0755 root bin
+s lib/fm/libfmevent.so=libfmevent.so.1
+d lib/fs 0755 root sys
+d lib/fs/dev 0755 root sys
+h lib/fs/dev/mount=etc/fs/dev/mount
+d lib/fs/hsfs 0755 root sys
+h lib/fs/hsfs/mount=etc/fs/hsfs/mount
+d lib/fs/nfs 0755 root sys
+s lib/fs/nfs/mount=../../../usr/lib/fs/nfs/mount
+d lib/fs/ufs 0755 root sys
+h lib/fs/ufs/mount=etc/fs/ufs/mount
+d lib/fs/zfs 0755 root sys
+h lib/fs/zfs/mount=etc/fs/zfs/mount
+h lib/fs/zfs/umount=etc/fs/zfs/umount
+d lib/inet 0755 root bin
+f lib/inet/in.mpathd 0555 root bin
+f lib/inet/ipmgmtd 0555 root bin
+f lib/inet/netcfgd 0555 root bin
+f lib/inet/nwamd 0555 root bin
+f lib/ld.so.1 0755 root bin
+f lib/libMPAPI.so.1 0755 root bin
+s lib/libMPAPI.so=libMPAPI.so.1
+f lib/libadm.so.1 0755 root bin
+s lib/libadm.so=libadm.so.1
+f lib/libaio.so.1 0755 root bin
+s lib/libaio.so=libaio.so.1
+f lib/libavl.so.1 0755 root bin
+s lib/libavl.so=libavl.so.1
+f lib/libbsm.so.1 0755 root bin
+s lib/libbsm.so=libbsm.so.1
+f lib/libc.so.1 0755 root bin
+s lib/libc.so=libc.so.1
+f lib/libc_db.so.1 0755 root bin
+s lib/libc_db.so=libc_db.so.1
+f lib/libcmdutils.so.1 0755 root bin
+s lib/libcmdutils.so=libcmdutils.so.1
+f lib/libcontract.so.1 0755 root bin
+s lib/libcontract.so=libcontract.so.1
+f lib/libcryptoutil.so.1 0755 root bin
+s lib/libcryptoutil.so=libcryptoutil.so.1
+f lib/libctf.so.1 0755 root bin
+s lib/libctf.so=libctf.so.1
+f lib/libcurses.so.1 0755 root bin
+s lib/libcurses.so=libcurses.so.1
+f lib/libcustr.so.1 0755 root bin
+f lib/libdevice.so.1 0755 root bin
+s lib/libdevice.so=libdevice.so.1
+f lib/libdevid.so.1 0755 root bin
+s lib/libdevid.so=libdevid.so.1
+f lib/libdevinfo.so.1 0755 root bin
+s lib/libdevinfo.so=libdevinfo.so.1
+f lib/libdhcpagent.so.1 0755 root bin
+f lib/libdhcputil.so.1 0755 root bin
+f lib/libdl.so.1 0755 root bin
+s lib/libdl.so=libdl.so.1
+f lib/libdladm.so.1 0755 root bin
+s lib/libdladm.so=libdladm.so.1
+f lib/libdlpi.so.1 0755 root bin
+s lib/libdlpi.so=libdlpi.so.1
+f lib/libdoor.so.1 0755 root bin
+s lib/libdoor.so=libdoor.so.1
+f lib/libdwarf.so.1 0755 root bin
+f lib/libefi.so.1 0755 root bin
+s lib/libefi.so=libefi.so.1
+f lib/libelf.so.1 0755 root bin
+s lib/libelf.so=libelf.so.1
+f lib/libelfsign.so.1 0755 root bin
+s lib/libelfsign.so=libelfsign.so.1
+f lib/libfakekernel.so.1 0755 root bin
+f lib/libfdisk.so.1 0755 root bin
+s lib/libfdisk.so=libfdisk.so.1
+f lib/libgen.so.1 0755 root bin
+s lib/libgen.so=libgen.so.1
+f lib/libgrubmgmt.so.1 0755 root bin
+s lib/libgrubmgmt.so=libgrubmgmt.so.1
+f lib/libinetutil.so.1 0755 root bin
+s lib/libinetutil.so=libinetutil.so.1
+f lib/libintl.so.1 0755 root bin
+s lib/libintl.so=libintl.so.1
+f lib/libipadm.so.1 0755 root bin
+s lib/libipadm.so=libipadm.so.1
+f lib/libipmp.so.1 0755 root bin
+s lib/libipmp.so=libipmp.so.1
+f lib/libkcfd.so.1 0755 root bin
+s lib/libkcfd.so=libkcfd.so.1
+f lib/libkmf.so.1 0755 root bin
+s lib/libkmf.so=libkmf.so.1
+f lib/libkmfberder.so.1 0755 root bin
+s lib/libkmfberder.so=libkmfberder.so.1
+f lib/libkstat.so.1 0755 root bin
+s lib/libkstat.so=libkstat.so.1
+f lib/libld.so.4 0755 root bin
+f lib/liblddbg.so.4 0755 root bin
+f lib/libm.so.1 0755 root bin
+f lib/libm.so.2 0755 root bin
+s lib/libm.so=libm.so.2
+f lib/libmd.so.1 0755 root bin
+s lib/libmd.so=libmd.so.1
+f lib/libmd5.so.1 0755 root bin
+s lib/libmd5.so=libmd5.so.1
+f lib/libmp.so.1 0755 root bin
+f lib/libmp.so.2 0755 root bin
+s lib/libmp.so=libmp.so.2
+f lib/libmpscsi_vhci.so.1 0755 root bin
+s lib/libmpscsi_vhci.so=libmpscsi_vhci.so.1
+d lib/libmvec 0755 root bin
+f lib/libmvec.so.1 0755 root bin
+s lib/libmvec.so=libmvec.so.1
+f lib/libmvec/libmvec_hwcap1.so.1 0755 root bin
+f lib/libnsl.so.1 0755 root bin
+s lib/libnsl.so=libnsl.so.1
+f lib/libnvpair.so.1 0755 root bin
+s lib/libnvpair.so=libnvpair.so.1
+f lib/libnwam.so.1 0755 root bin
+s lib/libnwam.so=libnwam.so.1
+f lib/libofmt.so.1 0755 root bin
+s lib/libofmt.so=libofmt.so.1
+f lib/libpam.so.1 0755 root bin
+s lib/libpam.so=libpam.so.1
+s lib/libposix4.so.1=librt.so.1
+s lib/libposix4.so=libposix4.so.1
+f lib/libproc.so.1 0755 root bin
+s lib/libproc.so=libproc.so.1
+f lib/libpthread.so.1 0755 root bin
+s lib/libpthread.so=libpthread.so.1
+f lib/librcm.so.1 0755 root bin
+s lib/librcm.so=librcm.so.1
+f lib/libresolv.so.1 0755 root bin
+f lib/libresolv.so.2 0755 root bin
+s lib/libresolv.so=libresolv.so.2
+f lib/libresolv_joy.so.2 0755 root bin
+f lib/librestart.so.1 0755 root bin
+s lib/librestart.so=librestart.so.1
+f lib/librpcsvc.so.1 0755 root bin
+s lib/librpcsvc.so=librpcsvc.so.1
+f lib/librt.so.1 0755 root bin
+s lib/librt.so=librt.so.1
+f lib/librtld.so.1 0755 root bin
+f lib/librtld_db.so.1 0755 root bin
+s lib/librtld_db.so=librtld_db.so.1
+f lib/libscf.so.1 0755 root bin
+s lib/libscf.so=libscf.so.1
+f lib/libsec.so.1 0755 root bin
+s lib/libsec.so=libsec.so.1
+f lib/libsecdb.so.1 0755 root bin
+s lib/libsecdb.so=libsecdb.so.1
+f lib/libsendfile.so.1 0755 root bin
+s lib/libsendfile.so=libsendfile.so.1
+f lib/libsmartsshd.so.1 0755 root bin
+s lib/libsmartsshd.so=libsmartsshd.so.1
+f lib/libsocket.so.1 0755 root bin
+s lib/libsocket.so=libsocket.so.1
+f lib/libsqlite-sys.so.2.8.15 0755 root bin
+f lib/libsysevent.so.1 0755 root bin
+s lib/libsysevent.so=libsysevent.so.1
+f lib/libtermcap.so.1 0755 root bin
+s lib/libtermcap.so=libtermcap.so.1
+s lib/libtermlib.so.1=libcurses.so.1
+s lib/libtermlib.so=libtermlib.so.1
+f lib/libthread.so.1 0755 root bin
+s lib/libthread.so=libthread.so.1
+s lib/libthread_db.so.1=libc_db.so.1
+s lib/libthread_db.so=libc_db.so.1
+f lib/libtsnet.so.1 0755 root bin
+s lib/libtsnet.so=libtsnet.so.1
+f lib/libtsol.so.2 0755 root bin
+s lib/libtsol.so=libtsol.so.2
+f lib/libumem.so.1 0755 root bin
+s lib/libumem.so=libumem.so.1
+f lib/libuuid.so.1 0755 root bin
+s lib/libuuid.so=libuuid.so.1
+f lib/libuutil.so.1 0755 root bin
+s lib/libuutil.so=libuutil.so.1
+f lib/libvarpd.so.1 0755 root bin
+f lib/libw.so.1 0755 root bin
+s lib/libw.so=libw.so.1
+f lib/libxnet.so.1 0755 root bin
+s lib/libxnet.so=libxnet.so.1
+f lib/libzdoor.so.1 0755 root bin
+s lib/libzdoor.so=libzdoor.so.1
+f lib/libzfs.so.1 0755 root bin
+s lib/libzfs.so=libzfs.so.1
+s lib/libzfs_core.so.1=../usr/lib/libzfs_core.so
+s lib/libzfs_core.so=../usr/lib/libzfs_core.so
+f lib/libzutil.so.1 0755 root bin
+s lib/libzutil.so=libzutil.so.1
+d lib/mpxio 0755 root bin
+f lib/mpxio/stmsboot_util 0555 root bin
+f lib/nss_compat.so.1 0755 root bin
+f lib/nss_dns.so.1 0755 root bin
+f lib/nss_files.so.1 0755 root bin
+f lib/nss_nis.so.1 0755 root bin
+f lib/nss_user.so.1 0755 root bin
+d lib/secure 0755 root bin
+s lib/secure/32=.
+s lib/secure/64=amd64
+d lib/secure/amd64 0755 root bin
+d lib/svc 0755 root bin
+d lib/svc/bin 0755 root bin
+f lib/svc/bin/lsvcrun 0555 root sys
+f lib/svc/bin/mfstscan 0555 root sys
+f lib/svc/bin/restore_repository 0555 root sys
+f lib/svc/bin/sqlite 0555 root sys
+f lib/svc/bin/svc.configd 0555 root sys
+f lib/svc/bin/svc.ipfd 0555 root sys
+f lib/svc/bin/svc.startd 0555 root sys
+d lib/svc/capture 0755 root bin
+d lib/svc/manifest 0755 root sys
+d lib/svc/manifest/application 0755 root sys
+d lib/svc/manifest/application/security 0755 root sys
+d lib/svc/manifest/device 0755 root sys
+d lib/svc/manifest/milestone 0755 root sys
+f lib/svc/manifest/milestone/multi-user-server.xml 0444 root sys
+f lib/svc/manifest/milestone/multi-user.xml 0444 root sys
+f lib/svc/manifest/milestone/name-services.xml 0444 root sys
+f lib/svc/manifest/milestone/network.xml 0444 root sys
+f lib/svc/manifest/milestone/single-user.xml 0444 root sys
+f lib/svc/manifest/milestone/sysconfig.xml 0444 root sys
+d lib/svc/manifest/network 0755 root sys
+f lib/svc/manifest/network/bridge.xml 0444 root sys
+f lib/svc/manifest/network/dlmgmt.xml 0444 root sys
+d lib/svc/manifest/network/dns 0755 root sys
+f lib/svc/manifest/network/dns/client.xml 0444 root sys
+f lib/svc/manifest/network/dns/install.xml 0444 root sys
+f lib/svc/manifest/network/dns/multicast.xml 0444 root sys
+f lib/svc/manifest/network/forwarding.xml 0444 root sys
+f lib/svc/manifest/network/ibd-post-upgrade.xml 0444 root sys
+f lib/svc/manifest/network/inetd-upgrade.xml 0444 root sys
+f lib/svc/manifest/network/inetd.xml 0444 root sys
+f lib/svc/manifest/network/ipfilter.xml 0444 root sys
+d lib/svc/manifest/network/ipsec 0755 root sys
+f lib/svc/manifest/network/ipsec/ike.xml 0444 root sys
+f lib/svc/manifest/network/ipsec/ipsecalgs.xml 0444 root sys
+f lib/svc/manifest/network/ipsec/manual-key.xml 0444 root sys
+f lib/svc/manifest/network/ipsec/policy.xml 0444 root sys
+d lib/svc/manifest/network/iscsi 0755 root sys
+f lib/svc/manifest/network/iscsi/iscsi-initiator.xml 0444 root sys
+f lib/svc/manifest/network/iscsi/iscsi-target.xml 0444 root sys
+f lib/svc/manifest/network/isns_server.xml 0444 root sys
+d lib/svc/manifest/network/ldap 0755 root sys
+f lib/svc/manifest/network/ldap/client.xml 0444 root sys
+d lib/svc/manifest/network/loadbalancer 0755 root sys
+f lib/svc/manifest/network/loadbalancer/ilbd.xml 0444 root sys
+f lib/svc/manifest/network/login.xml 0444 root sys
+f lib/svc/manifest/network/network-early-admin.xml 0444 root sys
+f lib/svc/manifest/network/network-initial.xml 0444 root sys
+f lib/svc/manifest/network/network-install.xml 0444 root sys
+f lib/svc/manifest/network/network-ipmgmt.xml 0444 root sys
+f lib/svc/manifest/network/network-ipqos.xml 0444 root sys
+f lib/svc/manifest/network/network-iptun.xml 0444 root sys
+f lib/svc/manifest/network/network-location.xml 0444 root sys
+f lib/svc/manifest/network/network-loopback.xml 0444 root sys
+f lib/svc/manifest/network/network-netmask.xml 0444 root sys
+f lib/svc/manifest/network/network-physical.xml 0444 root sys
+f lib/svc/manifest/network/network-routing-setup.xml 0444 root sys
+f lib/svc/manifest/network/network-service.xml 0444 root sys
+d lib/svc/manifest/network/nfs 0755 root sys
+f lib/svc/manifest/network/nfs/client.xml 0444 root sys
+f lib/svc/manifest/network/nfs/mapid.xml 0444 root sys
+f lib/svc/manifest/network/nfs/nlockmgr.xml 0444 root sys
+f lib/svc/manifest/network/nfs/server.xml 0444 root sys
+f lib/svc/manifest/network/nfs/status.xml 0444 root sys
+d lib/svc/manifest/network/nis 0755 root sys
+f lib/svc/manifest/network/nis/client.xml 0444 root sys
+f lib/svc/manifest/network/npiv_config.xml 0444 root sys
+f lib/svc/manifest/network/rarp.xml 0444 root sys
+f lib/svc/manifest/network/rexec.xml 0444 root sys
+d lib/svc/manifest/network/routing 0755 root sys
+f lib/svc/manifest/network/routing/legacy-routing.xml 0444 root sys
+f lib/svc/manifest/network/routing/ndp.xml 0444 root sys
+f lib/svc/manifest/network/routing/rdisc.xml 0444 root sys
+f lib/svc/manifest/network/routing/ripng.xml 0444 root sys
+f lib/svc/manifest/network/routing/route.xml 0444 root sys
+d lib/svc/manifest/network/rpc 0755 root sys
+f lib/svc/manifest/network/rpc/bind.xml 0444 root sys
+f lib/svc/manifest/network/rpc/gss.xml 0444 root sys
+d lib/svc/manifest/network/security 0755 root sys
+f lib/svc/manifest/network/sendmail-client.xml 0444 root sys
+d lib/svc/manifest/network/shares 0755 root sys
+f lib/svc/manifest/network/shares/group.xml 0444 root sys
+f lib/svc/manifest/network/shares/reparsed.xml 0444 root sys
+f lib/svc/manifest/network/shell.xml 0444 root sys
+f lib/svc/manifest/network/slp.xml 0444 root sys
+d lib/svc/manifest/network/smb 0755 root sys
+f lib/svc/manifest/network/smb/client.xml 0444 root sys
+f lib/svc/manifest/network/smb/server.xml 0444 root sys
+f lib/svc/manifest/network/smtp-sendmail.xml 0444 root sys
+f lib/svc/manifest/network/tftp-udp6.xml 0444 root sys
+f lib/svc/manifest/network/time.xml 0444 root sys
+f lib/svc/manifest/network/varpd.xml 0444 root sys
+f lib/svc/manifest/network/vrrp.xml 0444 root sys
+d lib/svc/manifest/platform 0755 root sys
+d lib/svc/manifest/platform/i86pc 0755 root sys
+f lib/svc/manifest/platform/i86pc/acpihpd.xml 0444 root sys
+d lib/svc/manifest/platform/sun4u 0755 root sys
+d lib/svc/manifest/platform/sun4v 0755 root sys
+d lib/svc/manifest/site 0755 root sys
+d lib/svc/manifest/system 0755 root sys
+f lib/svc/manifest/system/auditd.xml 0444 root sys
+f lib/svc/manifest/system/auditset.xml 0444 root sys
+f lib/svc/manifest/system/boot-archive-update.xml 0444 root sys
+f lib/svc/manifest/system/boot-archive.xml 0444 root sys
+f lib/svc/manifest/system/boot-config.xml 0444 root sys
+f lib/svc/manifest/system/consadm.xml 0444 root sys
+f lib/svc/manifest/system/console-login.xml 0444 root sys
+f lib/svc/manifest/system/coreadm.xml 0444 root sys
+f lib/svc/manifest/system/cron.xml 0444 root sys
+f lib/svc/manifest/system/cryptosvc.xml 0444 root sys
+d lib/svc/manifest/system/device 0755 root sys
+f lib/svc/manifest/system/device/allocate.xml 0444 root sys
+f lib/svc/manifest/system/device/devices-local.xml 0444 root sys
+f lib/svc/manifest/system/device/mpxio-upgrade.xml 0444 root sys
+f lib/svc/manifest/system/dumpadm.xml 0444 root sys
+f lib/svc/manifest/system/early-manifest-import.xml 0444 root sys
+f lib/svc/manifest/system/extended-accounting.xml 0444 root sys
+d lib/svc/manifest/system/filesystem 0755 root sys
+f lib/svc/manifest/system/filesystem/autofs.xml 0444 root sys
+f lib/svc/manifest/system/filesystem/joyent-fs.xml 0444 root sys
+f lib/svc/manifest/system/filesystem/local-fs.xml 0444 root sys
+f lib/svc/manifest/system/filesystem/minimal-fs.xml 0444 root sys
+f lib/svc/manifest/system/filesystem/root-fs.xml 0444 root sys
+f lib/svc/manifest/system/filesystem/usr-fs.xml 0444 root sys
+f lib/svc/manifest/system/filesystem/vscan.xml 0444 root sys
+d lib/svc/manifest/system/fm 0755 root sys
+f lib/svc/manifest/system/fm/notify-params.xml 0444 root sys
+f lib/svc/manifest/system/fm/smtp-notify.xml 0444 root sys
+f lib/svc/manifest/system/fmd.xml 0444 root sys
+f lib/svc/manifest/system/hostid.xml 0444 root sys
+f lib/svc/manifest/system/hotplug.xml 0444 root sys
+d lib/svc/manifest/system/ibsrp 0755 root sys
+f lib/svc/manifest/system/ibsrp/target.xml 0444 root sys
+f lib/svc/manifest/system/identity.xml 0444 root sys
+f lib/svc/manifest/system/idmap.xml 0444 root sys
+f lib/svc/manifest/system/intrd.xml 0444 root sys
+f lib/svc/manifest/system/keymap.xml 0444 root sys
+f lib/svc/manifest/system/labeld.xml 0444 root sys
+f lib/svc/manifest/system/logadm-upgrade.xml 0444 root sys
+f lib/svc/manifest/system/manifest-import.xml 0444 root sys
+f lib/svc/manifest/system/mdata.xml 0444 root sys
+f lib/svc/manifest/system/name-service-cache.xml 0444 root sys
+f lib/svc/manifest/system/ndmp.xml 0444 root sys
+f lib/svc/manifest/system/pfexecd.xml 0444 root sys
+f lib/svc/manifest/system/picl.xml 0444 root sys
+f lib/svc/manifest/system/power.xml 0444 root sys
+f lib/svc/manifest/system/process-security.xml 0444 root sys
+f lib/svc/manifest/system/rbac.xml 0444 root sys
+f lib/svc/manifest/system/rcap.xml 0444 root sys
+f lib/svc/manifest/system/resource-mgmt.xml 0444 root sys
+f lib/svc/manifest/system/rmtmpfiles.xml 0444 root sys
+f lib/svc/manifest/system/sac.xml 0444 root sys
+f lib/svc/manifest/system/sar.xml 0444 root sys
+f lib/svc/manifest/system/scheduler.xml 0444 root sys
+d lib/svc/manifest/system/security 0755 root sys
+f lib/svc/manifest/system/smartdc-config.xml 0444 root sys
+f lib/svc/manifest/system/smartdc-init.xml 0444 root sys
+f lib/svc/manifest/system/smartdc-ur.xml 0444 root sys
+f lib/svc/manifest/system/stmf.xml 0444 root sys
+d lib/svc/manifest/system/svc 0755 root sys
+f lib/svc/manifest/system/svc/global.xml 0444 root sys
+f lib/svc/manifest/system/svc/restarter.xml 0444 root sys
+f lib/svc/manifest/system/sysevent.xml 0444 root sys
+f lib/svc/manifest/system/system-log.xml 0444 root sys
+f lib/svc/manifest/system/update-man-index.xml 0444 root sys
+f lib/svc/manifest/system/utmp.xml 0444 root sys
+f lib/svc/manifest/system/vtdaemon.xml 0444 root sys
+f lib/svc/manifest/system/zones.xml 0444 root sys
+d lib/svc/method 0755 root bin
+f lib/svc/method/boot-archive 0555 root bin
+f lib/svc/method/boot-archive-update 0555 root bin
+f lib/svc/method/console-login 0555 root bin
+f lib/svc/method/devices-audio 0555 root bin
+f lib/svc/method/devices-local 0555 root bin
+f lib/svc/method/dns-install 0555 root bin
+f lib/svc/method/fc-fabric 0555 root sys
+f lib/svc/method/fs-joyent 0555 root sys
+f lib/svc/method/fs-local 0555 root bin
+f lib/svc/method/fs-minimal 0555 root bin
+f lib/svc/method/fs-root 0555 root bin
+f lib/svc/method/fs-usr 0555 root bin
+f lib/svc/method/ibd-post-upgrade 0555 root sys
+f lib/svc/method/identity-domain 0555 root bin
+f lib/svc/method/identity-node 0555 root bin
+f lib/svc/method/inetd-upgrade 0555 root bin
+f lib/svc/method/ipfilter 0555 root sys
+f lib/svc/method/iscsi-initiator 0555 root sys
+f lib/svc/method/iscsi-target 0555 root sys
+f lib/svc/method/iscsid 0555 root sys
+f lib/svc/method/keymap 0555 root bin
+f lib/svc/method/ldap-client 0555 root bin
+f lib/svc/method/logadm-upgrade 0555 root bin
+f lib/svc/method/manifest-import 0555 root bin
+f lib/svc/method/mdata-execute 0555 root bin
+f lib/svc/method/mdata-fetch 0555 root bin
+f lib/svc/method/mpxio-upgrade 0555 root bin
+f lib/svc/method/net-early-admin 0555 root bin
+f lib/svc/method/net-init 0555 root bin
+f lib/svc/method/net-install 0555 root bin
+f lib/svc/method/net-ipmgmt 0555 root bin
+f lib/svc/method/net-ipqos 0555 root bin
+f lib/svc/method/net-iptun 0555 root bin
+f lib/svc/method/net-loc 0555 root bin
+f lib/svc/method/net-loopback 0555 root bin
+f lib/svc/method/net-netmask 0555 root bin
+f lib/svc/method/net-physical 0555 root sys
+f lib/svc/method/net-routing-setup 0555 root bin
+f lib/svc/method/net-svc 0555 root bin
+f lib/svc/method/nfs-client 0555 root bin
+f lib/svc/method/nfs-server 0555 root sys
+f lib/svc/method/nlockmgr 0555 root bin
+f lib/svc/method/npivconfig 0555 root sys
+f lib/svc/method/rmtmpfiles 0555 root bin
+f lib/svc/method/rpc-bind 0555 root bin
+f lib/svc/method/sendmail-client 0555 root sys
+f lib/svc/method/slp 0555 root sys
+f lib/svc/method/smartdc-config 0555 root bin
+f lib/svc/method/smartdc-init 0555 root sys
+f lib/svc/method/smartdc-ur 0555 root bin
+f lib/svc/method/smb-client 0555 root sys
+f lib/svc/method/smtp-sendmail 0555 root sys
+f lib/svc/method/sshd 0555 root bin
+f lib/svc/method/svc-acpihpd 0555 root sys
+f lib/svc/method/svc-allocate 0555 root bin
+f lib/svc/method/svc-auditd 0555 root bin
+f lib/svc/method/svc-auditset 0555 root bin
+f lib/svc/method/svc-autofs 0555 root bin
+f lib/svc/method/svc-boot-config 0555 root bin
+f lib/svc/method/svc-consadm 0555 root bin
+f lib/svc/method/svc-cron 0555 root bin
+f lib/svc/method/svc-dlmgmtd 0555 root bin
+f lib/svc/method/svc-dumpadm 0555 root sys
+f lib/svc/method/svc-fcoei 0555 root sys
+f lib/svc/method/svc-fcoet 0555 root sys
+f lib/svc/method/svc-forwarding 0555 root bin
+f lib/svc/method/svc-hostid 0555 root bin
+f lib/svc/method/svc-hotplug 0555 root bin
+f lib/svc/method/svc-intrd 0555 root sys
+f lib/svc/method/svc-labeld 0555 root sys
+f lib/svc/method/svc-legacy-routing 0555 root bin
+f lib/svc/method/svc-ndmp 0555 root sys
+f lib/svc/method/svc-ndp 0555 root bin
+f lib/svc/method/svc-nscd 0555 root bin
+f lib/svc/method/svc-power 0555 root sys
+f lib/svc/method/svc-rbac 0555 root bin
+f lib/svc/method/svc-rdisc 0555 root bin
+f lib/svc/method/svc-resource-mgmt 0555 root sys
+f lib/svc/method/svc-ripng 0555 root bin
+f lib/svc/method/svc-route 0555 root bin
+f lib/svc/method/svc-scheduler 0555 root sys
+f lib/svc/method/svc-sockfilter 0555 root bin
+f lib/svc/method/svc-srpt 0555 root sys
+f lib/svc/method/svc-stmf 0555 root sys
+f lib/svc/method/svc-syseventd 0555 root sys
+f lib/svc/method/svc-tnd 0555 root sys
+f lib/svc/method/svc-utmpd 0555 root bin
+f lib/svc/method/svc-vrrp 0555 root sys
+f lib/svc/method/svc-zones 0555 root sys
+f lib/svc/method/sysidtool-net 0555 root bin
+f lib/svc/method/sysidtool-system 0555 root bin
+f lib/svc/method/system-log 0555 root bin
+f lib/svc/method/update-man-index 0555 root bin
+f lib/svc/method/vtdaemon 0555 root bin
+f lib/svc/method/yp 0555 root bin
+d lib/svc/monitor 0755 root bin
+d lib/svc/share 0755 root bin
+f lib/svc/share/README 0444 root bin
+f lib/svc/share/fs_include.sh 0444 root bin
+f lib/svc/share/ipf_include.sh 0444 root bin
+f lib/svc/share/mfsthistory 0444 root bin
+f lib/svc/share/net_include.sh 0444 root bin
+f lib/svc/share/routing_include.sh 0444 root bin
+f lib/svc/share/sendmail_include.sh 0444 root sys
+f lib/svc/share/smf_include.sh 0444 root bin
+d mnt 0755 root sys
+d opt 0755 root sys
+d platform 0755 root sys
+d platform/i86pc 0755 root sys
+d platform/i86pc/amd64 0755 root sys
+d platform/i86pc/kernel 0755 root sys
+d platform/i86pc/kernel/amd64 0755 root sys
+f platform/i86pc/kernel/amd64/unix 0755 root sys
+d platform/i86pc/kernel/cpu 0755 root sys
+d platform/i86pc/kernel/cpu/amd64 0755 root sys
+f platform/i86pc/kernel/cpu/amd64/cpu.generic 0755 root sys
+f platform/i86pc/kernel/cpu/amd64/cpu_ms.AuthenticAMD 0755 root sys
+f platform/i86pc/kernel/cpu/amd64/cpu_ms.AuthenticAMD.15 0755 root sys
+f platform/i86pc/kernel/cpu/amd64/cpu_ms.GenuineIntel 0755 root sys
+f platform/i86pc/kernel/cpu/amd64/cpu_ms.GenuineIntel.6.46 0755 root sys
+f platform/i86pc/kernel/cpu/amd64/cpu_ms.GenuineIntel.6.47 0755 root sys
+d platform/i86pc/kernel/dacf 0755 root sys
+d platform/i86pc/kernel/dacf/amd64 0755 root sys
+f platform/i86pc/kernel/dacf/amd64/consconfig_dacf 0755 root sys
+d platform/i86pc/kernel/drv 0755 root sys
+f platform/i86pc/kernel/drv/acpippm.conf 0644 root sys
+d platform/i86pc/kernel/drv/amd64 0755 root sys
+f platform/i86pc/kernel/drv/amd64/acpinex 0755 root sys
+f platform/i86pc/kernel/drv/amd64/acpippm 0755 root sys
+f platform/i86pc/kernel/drv/amd64/amd_iommu 0755 root sys
+f platform/i86pc/kernel/drv/amd64/cpc 0755 root sys
+f platform/i86pc/kernel/drv/amd64/cpudrv 0755 root sys
+f platform/i86pc/kernel/drv/amd64/dr 0755 root sys
+f platform/i86pc/kernel/drv/amd64/fipe 0755 root sys
+f platform/i86pc/kernel/drv/amd64/ioat 0755 root sys
+f platform/i86pc/kernel/drv/amd64/isa 0755 root sys
+f platform/i86pc/kernel/drv/amd64/npe 0755 root sys
+f platform/i86pc/kernel/drv/amd64/pci 0755 root sys
+f platform/i86pc/kernel/drv/amd64/pci-ide 0755 root sys
+f platform/i86pc/kernel/drv/amd64/pit_beep 0755 root sys
+f platform/i86pc/kernel/drv/amd64/ppm 0755 root sys
+f platform/i86pc/kernel/drv/amd64/rootnex 0755 root sys
+f platform/i86pc/kernel/drv/amd_iommu.conf 0644 root sys
+f platform/i86pc/kernel/drv/cpc.conf 0644 root sys
+f platform/i86pc/kernel/drv/dr.conf 0644 root sys
+f platform/i86pc/kernel/drv/fipe.conf 0644 root sys
+f platform/i86pc/kernel/drv/ioat.conf 0644 root sys
+f platform/i86pc/kernel/drv/pci-ide.conf 0644 root sys
+f platform/i86pc/kernel/drv/pit_beep.conf 0644 root sys
+f platform/i86pc/kernel/drv/ppm.conf 0644 root sys
+f platform/i86pc/kernel/drv/rootnex.conf 0644 root sys
+d platform/i86pc/kernel/kmdb 0755 root sys
+d platform/i86pc/kernel/kmdb/amd64 0755 root sys
+f platform/i86pc/kernel/kmdb/amd64/apix 0555 root sys
+f platform/i86pc/kernel/kmdb/amd64/pcplusmp 0555 root sys
+f platform/i86pc/kernel/kmdb/amd64/unix 0555 root sys
+f platform/i86pc/kernel/kmdb/amd64/uppc 0555 root sys
+d platform/i86pc/kernel/mach 0755 root sys
+d platform/i86pc/kernel/mach/amd64 0755 root sys
+f platform/i86pc/kernel/mach/amd64/apix 0755 root sys
+f platform/i86pc/kernel/mach/amd64/pcplusmp 0755 root sys
+f platform/i86pc/kernel/mach/amd64/uppc 0755 root sys
+d platform/i86pc/kernel/misc 0755 root sys
+d platform/i86pc/kernel/misc/amd64 0755 root sys
+f platform/i86pc/kernel/misc/amd64/acpidev 0755 root sys
+f platform/i86pc/kernel/misc/amd64/cpr 0755 root sys
+f platform/i86pc/kernel/misc/amd64/drmach_acpi 0755 root sys
+f platform/i86pc/kernel/misc/amd64/gfx_private 0755 root sys
+f platform/i86pc/kernel/misc/amd64/pci_prd 0755 root sys
+d platform/i86pc/kernel/pcbe 0755 root sys
+d platform/i86pc/kernel/pcbe/amd64 0755 root sys
+f platform/i86pc/kernel/pcbe/amd64/pcbe.AuthenticAMD 0755 root sys
+f platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.15 0755 root sys
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.122=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.126=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.134=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.140=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.141=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.142=pcbe.GenuineIntel.6.15
+f platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.15 0755 root sys
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.158=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.165=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.166=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.23=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.26=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.28=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.29=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.30=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.31=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.37=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.38=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.39=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.42=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.44=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.45=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.46=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.47=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.53=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.54=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.55=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.58=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.60=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.61=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.62=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.63=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.69=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.70=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.71=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.76=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.77=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.78=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.79=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.85=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.86=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.92=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.94=pcbe.GenuineIntel.6.15
+s platform/i86pc/kernel/pcbe/amd64/pcbe.GenuineIntel.6.95=pcbe.GenuineIntel.6.15
+d platform/i86pc/kernel/sys 0755 root sys
+d platform/i86pc/kernel/sys/amd64 0755 root sys
+f platform/i86pc/kernel/sys/amd64/cpc 0755 root sys
+d platform/i86pc/ucode 0755 root sys
+d platform/i86pc/ucode/AuthenticAMD 0755 root sys
+f platform/i86pc/ucode/AuthenticAMD/1020-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/1022-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/1041-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/1043-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/1062-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/1080-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/1081-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/10A0-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/2031-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/3010-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/5010-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/5020-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/6012-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/6020-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/6101-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/7001-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/8012-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/8082-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/8310-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/A010-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/A011-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/A012-00 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/container 0444 root sys
+f platform/i86pc/ucode/AuthenticAMD/equivalence-table 0444 root sys
+d platform/i86pc/ucode/GenuineIntel 0755 root sys
+f platform/i86pc/ucode/GenuineIntel/00000650-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000650-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000650-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000651-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000652-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000652-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000652-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000653-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000653-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000653-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000653-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000660-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000665-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0000066A-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0000066A-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0000066A-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0000066D-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0000066D-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0000066D-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000671-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000672-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000673-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000681-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000681-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000681-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000681-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000681-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000683-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000683-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000686-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000686-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000686-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000686-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000686-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0000068A-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0000068A-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0000068A-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000695-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000695-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000695-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006A0-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006A1-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006B1-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006B1-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006B4-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006B4-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006D6-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006E8-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006EC-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006EC-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006F2-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006F2-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006F6-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006F6-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006F6-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006F7-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006F7-40 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FA-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FB-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FB-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FB-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FB-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FB-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FB-40 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FB-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FD-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FD-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000006FD-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F07-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F07-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F0A-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F0A-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F0A-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F12-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F24-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F24-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F24-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F25-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F25-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F25-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F25-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F26-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F27-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F27-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F27-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F29-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F29-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F29-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F32-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F32-04=platform/i86pc/ucode/GenuineIntel/00000F32-01
+h platform/i86pc/ucode/GenuineIntel/00000F32-08=platform/i86pc/ucode/GenuineIntel/00000F32-01
+f platform/i86pc/ucode/GenuineIntel/00000F33-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F33-04=platform/i86pc/ucode/GenuineIntel/00000F33-01
+h platform/i86pc/ucode/GenuineIntel/00000F33-08=platform/i86pc/ucode/GenuineIntel/00000F33-01
+f platform/i86pc/ucode/GenuineIntel/00000F34-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F34-04=platform/i86pc/ucode/GenuineIntel/00000F34-01
+h platform/i86pc/ucode/GenuineIntel/00000F34-08=platform/i86pc/ucode/GenuineIntel/00000F34-01
+h platform/i86pc/ucode/GenuineIntel/00000F34-10=platform/i86pc/ucode/GenuineIntel/00000F34-01
+f platform/i86pc/ucode/GenuineIntel/00000F41-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F41-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F41-04=platform/i86pc/ucode/GenuineIntel/00000F41-01
+h platform/i86pc/ucode/GenuineIntel/00000F41-08=platform/i86pc/ucode/GenuineIntel/00000F41-01
+h platform/i86pc/ucode/GenuineIntel/00000F41-10=platform/i86pc/ucode/GenuineIntel/00000F41-01
+h platform/i86pc/ucode/GenuineIntel/00000F41-20=platform/i86pc/ucode/GenuineIntel/00000F41-01
+h platform/i86pc/ucode/GenuineIntel/00000F41-80=platform/i86pc/ucode/GenuineIntel/00000F41-01
+f platform/i86pc/ucode/GenuineIntel/00000F43-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F43-04=platform/i86pc/ucode/GenuineIntel/00000F43-01
+h platform/i86pc/ucode/GenuineIntel/00000F43-08=platform/i86pc/ucode/GenuineIntel/00000F43-01
+h platform/i86pc/ucode/GenuineIntel/00000F43-10=platform/i86pc/ucode/GenuineIntel/00000F43-01
+h platform/i86pc/ucode/GenuineIntel/00000F43-80=platform/i86pc/ucode/GenuineIntel/00000F43-01
+f platform/i86pc/ucode/GenuineIntel/00000F44-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F44-04=platform/i86pc/ucode/GenuineIntel/00000F44-01
+h platform/i86pc/ucode/GenuineIntel/00000F44-08=platform/i86pc/ucode/GenuineIntel/00000F44-01
+h platform/i86pc/ucode/GenuineIntel/00000F44-10=platform/i86pc/ucode/GenuineIntel/00000F44-01
+h platform/i86pc/ucode/GenuineIntel/00000F44-80=platform/i86pc/ucode/GenuineIntel/00000F44-01
+f platform/i86pc/ucode/GenuineIntel/00000F47-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F47-04=platform/i86pc/ucode/GenuineIntel/00000F47-01
+h platform/i86pc/ucode/GenuineIntel/00000F47-08=platform/i86pc/ucode/GenuineIntel/00000F47-01
+h platform/i86pc/ucode/GenuineIntel/00000F47-10=platform/i86pc/ucode/GenuineIntel/00000F47-01
+h platform/i86pc/ucode/GenuineIntel/00000F47-80=platform/i86pc/ucode/GenuineIntel/00000F47-01
+f platform/i86pc/ucode/GenuineIntel/00000F48-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F48-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F48-04 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F48-08=platform/i86pc/ucode/GenuineIntel/00000F48-04
+h platform/i86pc/ucode/GenuineIntel/00000F48-10=platform/i86pc/ucode/GenuineIntel/00000F48-04
+h platform/i86pc/ucode/GenuineIntel/00000F48-40=platform/i86pc/ucode/GenuineIntel/00000F48-04
+f platform/i86pc/ucode/GenuineIntel/00000F49-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F49-04=platform/i86pc/ucode/GenuineIntel/00000F49-01
+h platform/i86pc/ucode/GenuineIntel/00000F49-08=platform/i86pc/ucode/GenuineIntel/00000F49-01
+h platform/i86pc/ucode/GenuineIntel/00000F49-10=platform/i86pc/ucode/GenuineIntel/00000F49-01
+h platform/i86pc/ucode/GenuineIntel/00000F49-20=platform/i86pc/ucode/GenuineIntel/00000F49-01
+h platform/i86pc/ucode/GenuineIntel/00000F49-80=platform/i86pc/ucode/GenuineIntel/00000F49-01
+f platform/i86pc/ucode/GenuineIntel/00000F4A-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F4A-04 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F4A-08=platform/i86pc/ucode/GenuineIntel/00000F4A-04
+h platform/i86pc/ucode/GenuineIntel/00000F4A-10=platform/i86pc/ucode/GenuineIntel/00000F4A-04
+h platform/i86pc/ucode/GenuineIntel/00000F4A-40=platform/i86pc/ucode/GenuineIntel/00000F4A-04
+f platform/i86pc/ucode/GenuineIntel/00000F62-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F64-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F64-04 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F64-10=platform/i86pc/ucode/GenuineIntel/00000F64-04
+h platform/i86pc/ucode/GenuineIntel/00000F64-20=platform/i86pc/ucode/GenuineIntel/00000F64-04
+f platform/i86pc/ucode/GenuineIntel/00000F65-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00000F68-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00000F68-20=platform/i86pc/ucode/GenuineIntel/00000F68-02
+f platform/i86pc/ucode/GenuineIntel/00001632-00 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00010661-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00010661-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00010661-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00010676-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00010676-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00010676-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00010676-40 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00010676-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00010677-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0001067A-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/0001067A-04 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/0001067A-10=platform/i86pc/ucode/GenuineIntel/0001067A-01
+f platform/i86pc/ucode/GenuineIntel/0001067A-20 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/0001067A-40=platform/i86pc/ucode/GenuineIntel/0001067A-04
+h platform/i86pc/ucode/GenuineIntel/0001067A-80=platform/i86pc/ucode/GenuineIntel/0001067A-20
+f platform/i86pc/ucode/GenuineIntel/000106A4-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000106A4-02=platform/i86pc/ucode/GenuineIntel/000106A4-01
+f platform/i86pc/ucode/GenuineIntel/000106A5-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000106A5-02=platform/i86pc/ucode/GenuineIntel/000106A5-01
+f platform/i86pc/ucode/GenuineIntel/000106C2-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000106C2-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000106C2-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000106CA-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000106CA-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000106CA-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000106CA-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000106D1-08 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000106E5-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000106E5-02=platform/i86pc/ucode/GenuineIntel/000106E5-01
+h platform/i86pc/ucode/GenuineIntel/000106E5-10=platform/i86pc/ucode/GenuineIntel/000106E5-01
+f platform/i86pc/ucode/GenuineIntel/00020652-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00020652-10=platform/i86pc/ucode/GenuineIntel/00020652-02
+f platform/i86pc/ucode/GenuineIntel/00020655-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00020655-10=platform/i86pc/ucode/GenuineIntel/00020655-02
+h platform/i86pc/ucode/GenuineIntel/00020655-80=platform/i86pc/ucode/GenuineIntel/00020655-02
+f platform/i86pc/ucode/GenuineIntel/00020661-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00020661-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000206A7-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000206A7-10=platform/i86pc/ucode/GenuineIntel/000206A7-02
+f platform/i86pc/ucode/GenuineIntel/000206C2-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000206C2-02=platform/i86pc/ucode/GenuineIntel/000206C2-01
+f platform/i86pc/ucode/GenuineIntel/000206D6-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000206D6-04=platform/i86pc/ucode/GenuineIntel/000206D6-01
+h platform/i86pc/ucode/GenuineIntel/000206D6-08=platform/i86pc/ucode/GenuineIntel/000206D6-01
+h platform/i86pc/ucode/GenuineIntel/000206D6-20=platform/i86pc/ucode/GenuineIntel/000206D6-01
+h platform/i86pc/ucode/GenuineIntel/000206D6-40=platform/i86pc/ucode/GenuineIntel/000206D6-01
+f platform/i86pc/ucode/GenuineIntel/000206D7-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000206D7-04=platform/i86pc/ucode/GenuineIntel/000206D7-01
+h platform/i86pc/ucode/GenuineIntel/000206D7-08=platform/i86pc/ucode/GenuineIntel/000206D7-01
+h platform/i86pc/ucode/GenuineIntel/000206D7-20=platform/i86pc/ucode/GenuineIntel/000206D7-01
+h platform/i86pc/ucode/GenuineIntel/000206D7-40=platform/i86pc/ucode/GenuineIntel/000206D7-01
+f platform/i86pc/ucode/GenuineIntel/000206E6-04 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000206F2-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000206F2-04=platform/i86pc/ucode/GenuineIntel/000206F2-01
+f platform/i86pc/ucode/GenuineIntel/00030678-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00030678-04 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00030678-08=platform/i86pc/ucode/GenuineIntel/00030678-04
+f platform/i86pc/ucode/GenuineIntel/00030679-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00030679-02=platform/i86pc/ucode/GenuineIntel/00030679-01
+h platform/i86pc/ucode/GenuineIntel/00030679-04=platform/i86pc/ucode/GenuineIntel/00030679-01
+h platform/i86pc/ucode/GenuineIntel/00030679-08=platform/i86pc/ucode/GenuineIntel/00030679-01
+f platform/i86pc/ucode/GenuineIntel/000306A9-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000306A9-10=platform/i86pc/ucode/GenuineIntel/000306A9-02
+f platform/i86pc/ucode/GenuineIntel/000306C3-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000306C3-10=platform/i86pc/ucode/GenuineIntel/000306C3-02
+h platform/i86pc/ucode/GenuineIntel/000306C3-20=platform/i86pc/ucode/GenuineIntel/000306C3-02
+f platform/i86pc/ucode/GenuineIntel/000306D4-40 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000306D4-80=platform/i86pc/ucode/GenuineIntel/000306D4-40
+f platform/i86pc/ucode/GenuineIntel/000306E4-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000306E4-04=platform/i86pc/ucode/GenuineIntel/000306E4-01
+h platform/i86pc/ucode/GenuineIntel/000306E4-08=platform/i86pc/ucode/GenuineIntel/000306E4-01
+h platform/i86pc/ucode/GenuineIntel/000306E4-20=platform/i86pc/ucode/GenuineIntel/000306E4-01
+h platform/i86pc/ucode/GenuineIntel/000306E4-40=platform/i86pc/ucode/GenuineIntel/000306E4-01
+h platform/i86pc/ucode/GenuineIntel/000306E4-80=platform/i86pc/ucode/GenuineIntel/000306E4-01
+f platform/i86pc/ucode/GenuineIntel/000306E6-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000306E6-04=platform/i86pc/ucode/GenuineIntel/000306E6-01
+h platform/i86pc/ucode/GenuineIntel/000306E6-08=platform/i86pc/ucode/GenuineIntel/000306E6-01
+h platform/i86pc/ucode/GenuineIntel/000306E6-20=platform/i86pc/ucode/GenuineIntel/000306E6-01
+h platform/i86pc/ucode/GenuineIntel/000306E6-40=platform/i86pc/ucode/GenuineIntel/000306E6-01
+h platform/i86pc/ucode/GenuineIntel/000306E6-80=platform/i86pc/ucode/GenuineIntel/000306E6-01
+f platform/i86pc/ucode/GenuineIntel/000306E7-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000306E7-04=platform/i86pc/ucode/GenuineIntel/000306E7-01
+h platform/i86pc/ucode/GenuineIntel/000306E7-08=platform/i86pc/ucode/GenuineIntel/000306E7-01
+h platform/i86pc/ucode/GenuineIntel/000306E7-20=platform/i86pc/ucode/GenuineIntel/000306E7-01
+h platform/i86pc/ucode/GenuineIntel/000306E7-40=platform/i86pc/ucode/GenuineIntel/000306E7-01
+h platform/i86pc/ucode/GenuineIntel/000306E7-80=platform/i86pc/ucode/GenuineIntel/000306E7-01
+f platform/i86pc/ucode/GenuineIntel/000306F2-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000306F2-02=platform/i86pc/ucode/GenuineIntel/000306F2-01
+h platform/i86pc/ucode/GenuineIntel/000306F2-04=platform/i86pc/ucode/GenuineIntel/000306F2-01
+h platform/i86pc/ucode/GenuineIntel/000306F2-08=platform/i86pc/ucode/GenuineIntel/000306F2-01
+h platform/i86pc/ucode/GenuineIntel/000306F2-20=platform/i86pc/ucode/GenuineIntel/000306F2-01
+h platform/i86pc/ucode/GenuineIntel/000306F2-40=platform/i86pc/ucode/GenuineIntel/000306F2-01
+f platform/i86pc/ucode/GenuineIntel/000306F4-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00040651-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00040651-10=platform/i86pc/ucode/GenuineIntel/00040651-02
+h platform/i86pc/ucode/GenuineIntel/00040651-20=platform/i86pc/ucode/GenuineIntel/00040651-02
+h platform/i86pc/ucode/GenuineIntel/00040651-40=platform/i86pc/ucode/GenuineIntel/00040651-02
+f platform/i86pc/ucode/GenuineIntel/00040661-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00040661-10=platform/i86pc/ucode/GenuineIntel/00040661-02
+h platform/i86pc/ucode/GenuineIntel/00040661-20=platform/i86pc/ucode/GenuineIntel/00040661-02
+f platform/i86pc/ucode/GenuineIntel/00040671-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00040671-20=platform/i86pc/ucode/GenuineIntel/00040671-02
+f platform/i86pc/ucode/GenuineIntel/000406C3-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000406C4-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000406D8-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000406E3-40 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000406E3-80=platform/i86pc/ucode/GenuineIntel/000406E3-40
+f platform/i86pc/ucode/GenuineIntel/000406F1-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000406F1-02=platform/i86pc/ucode/GenuineIntel/000406F1-01
+h platform/i86pc/ucode/GenuineIntel/000406F1-04=platform/i86pc/ucode/GenuineIntel/000406F1-01
+h platform/i86pc/ucode/GenuineIntel/000406F1-08=platform/i86pc/ucode/GenuineIntel/000406F1-01
+h platform/i86pc/ucode/GenuineIntel/000406F1-20=platform/i86pc/ucode/GenuineIntel/000406F1-01
+h platform/i86pc/ucode/GenuineIntel/000406F1-40=platform/i86pc/ucode/GenuineIntel/000406F1-01
+h platform/i86pc/ucode/GenuineIntel/000406F1-80=platform/i86pc/ucode/GenuineIntel/000406F1-01
+f platform/i86pc/ucode/GenuineIntel/00050653-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00050653-02=platform/i86pc/ucode/GenuineIntel/00050653-01
+h platform/i86pc/ucode/GenuineIntel/00050653-04=platform/i86pc/ucode/GenuineIntel/00050653-01
+h platform/i86pc/ucode/GenuineIntel/00050653-10=platform/i86pc/ucode/GenuineIntel/00050653-01
+h platform/i86pc/ucode/GenuineIntel/00050653-80=platform/i86pc/ucode/GenuineIntel/00050653-01
+f platform/i86pc/ucode/GenuineIntel/00050654-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00050654-02=platform/i86pc/ucode/GenuineIntel/00050654-01
+h platform/i86pc/ucode/GenuineIntel/00050654-04=platform/i86pc/ucode/GenuineIntel/00050654-01
+h platform/i86pc/ucode/GenuineIntel/00050654-10=platform/i86pc/ucode/GenuineIntel/00050654-01
+h platform/i86pc/ucode/GenuineIntel/00050654-20=platform/i86pc/ucode/GenuineIntel/00050654-01
+h platform/i86pc/ucode/GenuineIntel/00050654-80=platform/i86pc/ucode/GenuineIntel/00050654-01
+f platform/i86pc/ucode/GenuineIntel/00050655-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00050655-02=platform/i86pc/ucode/GenuineIntel/00050655-01
+h platform/i86pc/ucode/GenuineIntel/00050655-04=platform/i86pc/ucode/GenuineIntel/00050655-01
+h platform/i86pc/ucode/GenuineIntel/00050655-10=platform/i86pc/ucode/GenuineIntel/00050655-01
+h platform/i86pc/ucode/GenuineIntel/00050655-20=platform/i86pc/ucode/GenuineIntel/00050655-01
+h platform/i86pc/ucode/GenuineIntel/00050655-80=platform/i86pc/ucode/GenuineIntel/00050655-01
+f platform/i86pc/ucode/GenuineIntel/00050656-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00050656-02=platform/i86pc/ucode/GenuineIntel/00050656-01
+h platform/i86pc/ucode/GenuineIntel/00050656-04=platform/i86pc/ucode/GenuineIntel/00050656-01
+h platform/i86pc/ucode/GenuineIntel/00050656-08=platform/i86pc/ucode/GenuineIntel/00050656-01
+h platform/i86pc/ucode/GenuineIntel/00050656-10=platform/i86pc/ucode/GenuineIntel/00050656-01
+h platform/i86pc/ucode/GenuineIntel/00050656-20=platform/i86pc/ucode/GenuineIntel/00050656-01
+h platform/i86pc/ucode/GenuineIntel/00050656-80=platform/i86pc/ucode/GenuineIntel/00050656-01
+f platform/i86pc/ucode/GenuineIntel/00050657-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00050657-02=platform/i86pc/ucode/GenuineIntel/00050657-01
+h platform/i86pc/ucode/GenuineIntel/00050657-04=platform/i86pc/ucode/GenuineIntel/00050657-01
+h platform/i86pc/ucode/GenuineIntel/00050657-08=platform/i86pc/ucode/GenuineIntel/00050657-01
+h platform/i86pc/ucode/GenuineIntel/00050657-10=platform/i86pc/ucode/GenuineIntel/00050657-01
+h platform/i86pc/ucode/GenuineIntel/00050657-20=platform/i86pc/ucode/GenuineIntel/00050657-01
+h platform/i86pc/ucode/GenuineIntel/00050657-80=platform/i86pc/ucode/GenuineIntel/00050657-01
+f platform/i86pc/ucode/GenuineIntel/0005065B-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/0005065B-02=platform/i86pc/ucode/GenuineIntel/0005065B-01
+h platform/i86pc/ucode/GenuineIntel/0005065B-04=platform/i86pc/ucode/GenuineIntel/0005065B-01
+h platform/i86pc/ucode/GenuineIntel/0005065B-08=platform/i86pc/ucode/GenuineIntel/0005065B-01
+h platform/i86pc/ucode/GenuineIntel/0005065B-10=platform/i86pc/ucode/GenuineIntel/0005065B-01
+h platform/i86pc/ucode/GenuineIntel/0005065B-20=platform/i86pc/ucode/GenuineIntel/0005065B-01
+h platform/i86pc/ucode/GenuineIntel/0005065B-80=platform/i86pc/ucode/GenuineIntel/0005065B-01
+f platform/i86pc/ucode/GenuineIntel/00050662-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00050663-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00050664-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00050665-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000506C2-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000506C9-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000506C9-02=platform/i86pc/ucode/GenuineIntel/000506C9-01
+f platform/i86pc/ucode/GenuineIntel/000506CA-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000506CA-02=platform/i86pc/ucode/GenuineIntel/000506CA-01
+f platform/i86pc/ucode/GenuineIntel/000506E3-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000506E3-04=platform/i86pc/ucode/GenuineIntel/000506E3-02
+h platform/i86pc/ucode/GenuineIntel/000506E3-10=platform/i86pc/ucode/GenuineIntel/000506E3-02
+h platform/i86pc/ucode/GenuineIntel/000506E3-20=platform/i86pc/ucode/GenuineIntel/000506E3-02
+f platform/i86pc/ucode/GenuineIntel/000506F1-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00060663-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000606A5-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000606A5-02=platform/i86pc/ucode/GenuineIntel/000606A5-01
+h platform/i86pc/ucode/GenuineIntel/000606A5-04=platform/i86pc/ucode/GenuineIntel/000606A5-01
+h platform/i86pc/ucode/GenuineIntel/000606A5-80=platform/i86pc/ucode/GenuineIntel/000606A5-01
+f platform/i86pc/ucode/GenuineIntel/000606A6-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000606A6-02=platform/i86pc/ucode/GenuineIntel/000606A6-01
+h platform/i86pc/ucode/GenuineIntel/000606A6-04=platform/i86pc/ucode/GenuineIntel/000606A6-01
+h platform/i86pc/ucode/GenuineIntel/000606A6-80=platform/i86pc/ucode/GenuineIntel/000606A6-01
+f platform/i86pc/ucode/GenuineIntel/000606C1-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000706A1-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000706A8-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000706E5-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000806A1-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000806C1-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000806C2-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000806C2-40=platform/i86pc/ucode/GenuineIntel/000806C2-02
+h platform/i86pc/ucode/GenuineIntel/000806C2-80=platform/i86pc/ucode/GenuineIntel/000806C2-02
+f platform/i86pc/ucode/GenuineIntel/000806D1-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000806D1-40=platform/i86pc/ucode/GenuineIntel/000806D1-02
+h platform/i86pc/ucode/GenuineIntel/000806D1-80=platform/i86pc/ucode/GenuineIntel/000806D1-02
+f platform/i86pc/ucode/GenuineIntel/000806E9-10 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000806E9-40 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000806E9-80=platform/i86pc/ucode/GenuineIntel/000806E9-40
+f platform/i86pc/ucode/GenuineIntel/000806EA-40 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000806EA-80=platform/i86pc/ucode/GenuineIntel/000806EA-40
+f platform/i86pc/ucode/GenuineIntel/000806EB-10 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000806EB-40=platform/i86pc/ucode/GenuineIntel/000806EB-10
+h platform/i86pc/ucode/GenuineIntel/000806EB-80=platform/i86pc/ucode/GenuineIntel/000806EB-10
+f platform/i86pc/ucode/GenuineIntel/000806EC-04 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000806EC-10=platform/i86pc/ucode/GenuineIntel/000806EC-04
+h platform/i86pc/ucode/GenuineIntel/000806EC-80=platform/i86pc/ucode/GenuineIntel/000806EC-04
+f platform/i86pc/ucode/GenuineIntel/00090661-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/00090672-01 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/00090672-02=platform/i86pc/ucode/GenuineIntel/00090672-01
+h platform/i86pc/ucode/GenuineIntel/00090672-04=platform/i86pc/ucode/GenuineIntel/00090672-01
+h platform/i86pc/ucode/GenuineIntel/00090675-01=platform/i86pc/ucode/GenuineIntel/00090672-01
+h platform/i86pc/ucode/GenuineIntel/00090675-02=platform/i86pc/ucode/GenuineIntel/00090672-01
+h platform/i86pc/ucode/GenuineIntel/00090675-04=platform/i86pc/ucode/GenuineIntel/00090672-01
+f platform/i86pc/ucode/GenuineIntel/000906A3-80 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000906A4-80=platform/i86pc/ucode/GenuineIntel/000906A3-80
+f platform/i86pc/ucode/GenuineIntel/000906C0-01 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000906E9-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000906E9-08=platform/i86pc/ucode/GenuineIntel/000906E9-02
+h platform/i86pc/ucode/GenuineIntel/000906E9-20=platform/i86pc/ucode/GenuineIntel/000906E9-02
+f platform/i86pc/ucode/GenuineIntel/000906EA-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000906EA-20=platform/i86pc/ucode/GenuineIntel/000906EA-02
+f platform/i86pc/ucode/GenuineIntel/000906EB-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000906EC-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000906EC-20=platform/i86pc/ucode/GenuineIntel/000906EC-02
+f platform/i86pc/ucode/GenuineIntel/000906ED-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000906ED-20=platform/i86pc/ucode/GenuineIntel/000906ED-02
+f platform/i86pc/ucode/GenuineIntel/000A0652-20 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000A0653-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000A0653-20=platform/i86pc/ucode/GenuineIntel/000A0653-02
+f platform/i86pc/ucode/GenuineIntel/000A0655-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000A0655-20=platform/i86pc/ucode/GenuineIntel/000A0653-02
+f platform/i86pc/ucode/GenuineIntel/000A0660-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000A0661-80 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000A0671-02 0444 root sys
+f platform/i86pc/ucode/GenuineIntel/000B0671-02 0444 root sys
+h platform/i86pc/ucode/GenuineIntel/000B0671-10=platform/i86pc/ucode/GenuineIntel/000B0671-02
+h platform/i86pc/ucode/GenuineIntel/000B0671-20=platform/i86pc/ucode/GenuineIntel/000B0671-02
+h platform/i86pc/ucode/GenuineIntel/000B06F2-01=platform/i86pc/ucode/GenuineIntel/00090672-01
+h platform/i86pc/ucode/GenuineIntel/000B06F2-02=platform/i86pc/ucode/GenuineIntel/00090672-01
+h platform/i86pc/ucode/GenuineIntel/000B06F2-04=platform/i86pc/ucode/GenuineIntel/00090672-01
+h platform/i86pc/ucode/GenuineIntel/000B06F5-01=platform/i86pc/ucode/GenuineIntel/00090672-01
+h platform/i86pc/ucode/GenuineIntel/000B06F5-02=platform/i86pc/ucode/GenuineIntel/00090672-01
+h platform/i86pc/ucode/GenuineIntel/000B06F5-04=platform/i86pc/ucode/GenuineIntel/00090672-01
+d proc 0755 root root
+d root 0755 root root
+f root/.bash_profile 0644 root root
+f root/.bashrc 0644 root root
+f root/.profile 0644 root root
+d sbin 0755 root sys
+f sbin/autopush 0555 root bin
+f sbin/biosdev 0555 root bin
+f sbin/bootadm 0555 root bin
+f sbin/cryptoadm 0555 root bin
+f sbin/devprop 0555 root bin
+f sbin/dhcpagent 0555 root bin
+f sbin/dhcpinfo 0555 root bin
+f sbin/dladm 0555 root bin
+f sbin/dlmgmtd 0555 root bin
+f sbin/dlstat 0555 root bin
+f sbin/fdisk 0555 root bin
+f sbin/fiocompress 0555 root bin
+f sbin/flowadm 0555 root bin
+f sbin/flowstat 0555 root bin
+f sbin/hostconfig 0555 root bin
+f sbin/ibd_delete_link 0555 root bin
+f sbin/ibd_upgrade 0555 root bin
+f sbin/ifconfig 0555 root bin
+f sbin/ifparse 0555 root bin
+s sbin/in.mpathd=../lib/inet/in.mpathd
+f sbin/init 0555 root sys
+f sbin/installgrub 0555 root sys
+f sbin/ipadm 0555 root bin
+f sbin/ipmpstat 0555 root bin
+s sbin/jsh=../usr/bin/ksh93
+f sbin/mount 0555 root bin
+f sbin/mountall 0555 root sys
+f sbin/netstrategy 0555 root bin
+s sbin/pfsh=../usr/bin/pfexec
+f sbin/rc0 0744 root sys
+f sbin/rc1 0744 root sys
+f sbin/rc2 0744 root sys
+f sbin/rc3 0744 root sys
+h sbin/rc5=sbin/rc0
+h sbin/rc6=sbin/rc0
+f sbin/rcS 0744 root sys
+f sbin/route 0555 root bin
+f sbin/routeadm 0555 root bin
+s sbin/sh=../usr/bin/i86/ksh93
+f sbin/soconfig 0555 root bin
+f sbin/su.static 0555 root sys
+s sbin/su=../usr/bin/su
+f sbin/sulogin 0555 root bin
+f sbin/svcadm 0555 root bin
+f sbin/swapadd 0744 root sys
+f sbin/sync 0555 root bin
+f sbin/tzreload 0555 root bin
+f sbin/uadmin 0555 root sys
+f sbin/umount 0555 root bin
+f sbin/umountall 0555 root sys
+f sbin/uname 0555 root bin
+f sbin/zfs 0555 root bin
+f sbin/zonename 0555 root bin
+f sbin/zpool 0555 root bin
+d system 0755 root root
+d system/boot 0755 root root
+d system/contract 0555 root root
+d system/object 0555 root root
+d tmp 1777 root root
+d usr 0755 root sys
+s usr/adm=../var/adm
+d usr/bin 0755 root bin
+h usr/bin/[=usr/bin/alias
+f usr/bin/addbadsec 0555 root bin
+f usr/bin/alias 0555 root bin
+d usr/bin/amd64 0755 root bin
+f usr/bin/amd64/amt 0555 root bin
+f usr/bin/amd64/cputrack 0555 root bin
+f usr/bin/amd64/crle 0555 root bin
+h usr/bin/amd64/decrypt=usr/bin/amd64/encrypt
+h usr/bin/amd64/digest=usr/bin/amd64/mac
+s usr/bin/amd64/dump=../dump
+s usr/bin/amd64/elfdump=../elfdump
+f usr/bin/amd64/elfedit 0555 root bin
+f usr/bin/amd64/elfwrap 0555 root bin
+f usr/bin/amd64/encrypt 0555 root bin
+f usr/bin/amd64/ficl-sys 0555 root bin
+f usr/bin/amd64/gcore 0555 root bin
+f usr/bin/amd64/ksh93 0555 root bin
+h usr/bin/amd64/ksh=usr/bin/amd64/ksh93
+s usr/bin/amd64/ld=../ld
+s usr/bin/amd64/ldd=../ldd
+f usr/bin/amd64/ls 0555 root bin
+f usr/bin/amd64/mac 0555 root bin
+f usr/bin/amd64/mcs 0555 root bin
+f usr/bin/amd64/mdb 0555 root bin
+f usr/bin/amd64/moe 0555 root bin
+f usr/bin/amd64/newtask 4555 root sys
+f usr/bin/amd64/nohup 0555 root bin
+f usr/bin/amd64/pargs 0555 root bin
+h usr/bin/amd64/pauxv=usr/bin/amd64/pargs
+f usr/bin/amd64/pcred 0555 root bin
+h usr/bin/amd64/penv=usr/bin/amd64/pargs
+f usr/bin/amd64/pfiles 0555 root bin
+f usr/bin/amd64/pflags 0555 root bin
+f usr/bin/amd64/pldd 0555 root bin
+f usr/bin/amd64/plgrp 0555 root bin
+f usr/bin/amd64/plimit 0555 root bin
+f usr/bin/amd64/pmadvise 0555 root bin
+f usr/bin/amd64/pmap 0555 root bin
+f usr/bin/amd64/ppgsz 0555 root bin
+f usr/bin/amd64/ppriv 0555 root bin
+f usr/bin/amd64/prctl 0555 root bin
+f usr/bin/amd64/preap 0555 root bin
+f usr/bin/amd64/prstat 0555 root bin
+f usr/bin/amd64/prun 0555 root bin
+f usr/bin/amd64/psecflags 0555 root bin
+f usr/bin/amd64/psig 0555 root bin
+f usr/bin/amd64/pstack 0555 root bin
+f usr/bin/amd64/pstop 0555 root bin
+f usr/bin/amd64/ptime 0555 root bin
+f usr/bin/amd64/ptree 0555 root bin
+s usr/bin/amd64/pvs=../pvs
+f usr/bin/amd64/pwait 0555 root bin
+f usr/bin/amd64/pwdx 0555 root bin
+h usr/bin/amd64/rksh93=usr/bin/amd64/ksh93
+h usr/bin/amd64/rksh=usr/bin/amd64/ksh93
+f usr/bin/amd64/savecore 0555 root bin
+s usr/bin/amd64/size=../size
+f usr/bin/amd64/sort 0555 root bin
+f usr/bin/amd64/strings 0555 root bin
+f usr/bin/amd64/truss 0555 root bin
+f usr/bin/amt 0555 root bin
+h usr/bin/apropos=usr/bin/man
+f usr/bin/ar 0555 root bin
+f usr/bin/arch 0555 root bin
+f usr/bin/arcstat 0555 root bin
+f usr/bin/asa 0555 root bin
+f usr/bin/at 4755 root sys
+f usr/bin/atq 4755 root sys
+f usr/bin/atrm 4755 root sys
+f usr/bin/auths 0555 root bin
+h usr/bin/awk=usr/bin/nawk
+f usr/bin/banner 0555 root bin
+f usr/bin/bart 0555 root bin
+f usr/bin/basename 0555 root bin
+f usr/bin/batch 0555 root bin
+f usr/bin/bc 0555 root bin
+f usr/bin/bdiff 0755 root bin
+h usr/bin/bg=usr/bin/alias
+f usr/bin/busstat 0555 root bin
+f usr/bin/cal 0555 root bin
+f usr/bin/captoinfo 0555 root bin
+f usr/bin/cat 0555 root bin
+h usr/bin/catman=usr/bin/man
+h usr/bin/cd=usr/bin/alias
+f usr/bin/checkeq 0555 root bin
+f usr/bin/checknr 0555 root bin
+f usr/bin/chgrp 0555 root bin
+f usr/bin/chkey 4555 root sys
+f usr/bin/chmod 0555 root bin
+f usr/bin/chown 0555 root bin
+f usr/bin/ckdate 0555 root bin
+f usr/bin/ckgid 0555 root bin
+f usr/bin/ckint 0555 root bin
+f usr/bin/ckitem 0555 root bin
+f usr/bin/ckkeywd 0555 root bin
+f usr/bin/ckpath 0555 root bin
+f usr/bin/ckrange 0555 root bin
+f usr/bin/ckstr 0555 root bin
+h usr/bin/cksum=usr/bin/alias
+f usr/bin/cktime 0555 root bin
+f usr/bin/ckuid 0555 root bin
+f usr/bin/ckyorn 0555 root bin
+f usr/bin/clear 0555 root bin
+h usr/bin/cmp=usr/bin/alias
+f usr/bin/col 0555 root bin
+f usr/bin/column 0555 root bin
+h usr/bin/comm=usr/bin/alias
+h usr/bin/command=usr/bin/alias
+f usr/bin/compress 0555 root bin
+f usr/bin/connstat 0555 root bin
+f usr/bin/coreadm 0555 root bin
+f usr/bin/cp 0555 root bin
+f usr/bin/cpio 0555 root bin
+h usr/bin/cputrack=usr/lib/isaexec
+f usr/bin/crle 0555 root bin
+f usr/bin/crontab 4555 root bin
+f usr/bin/crypt 0555 root bin
+f usr/bin/csh 0555 root bin
+f usr/bin/csplit 0555 root bin
+f usr/bin/ctfdiff 0555 root bin
+f usr/bin/ctfdump 0555 root bin
+f usr/bin/ctrun 0555 root bin
+f usr/bin/ctstat 0555 root bin
+f usr/bin/ctwatch 0555 root bin
+h usr/bin/cut=usr/bin/alias
+f usr/bin/date 0555 root bin
+f usr/bin/dc 0555 root bin
+f usr/bin/dd 0555 root bin
+h usr/bin/decrypt=usr/lib/isaexec
+f usr/bin/demangle 0555 root bin
+f usr/bin/deroff 0555 root bin
+f usr/bin/devattr 0555 root bin
+f usr/bin/devfree 0555 root bin
+f usr/bin/devreserv 0555 root bin
+s usr/bin/df=../sbin/df
+f usr/bin/diff 0555 root bin
+f usr/bin/diff3 0555 root bin
+f usr/bin/diffmk 0555 root bin
+h usr/bin/digest=usr/lib/isaexec
+f usr/bin/dircmp 0555 root bin
+f usr/bin/dirname 0555 root bin
+f usr/bin/dis 0555 root bin
+f usr/bin/diskinfo 0555 root bin
+f usr/bin/diskscan 0555 root bin
+h usr/bin/dispgid=usr/bin/ckgid
+h usr/bin/dispuid=usr/bin/ckuid
+s usr/bin/dmesg=../sbin/dmesg
+f usr/bin/dns-sd 0555 root bin
+f usr/bin/domainname 0555 root bin
+f usr/bin/dos2unix 0555 root bin
+f usr/bin/du 0555 root bin
+f usr/bin/dump 0555 root bin
+f usr/bin/dumpcs 0555 root bin
+f usr/bin/dumpkeys 0555 root bin
+f usr/bin/echo 0555 root bin
+f usr/bin/ed 0555 root bin
+f usr/bin/edit 0555 root bin
+h usr/bin/egrep=usr/bin/grep
+f usr/bin/eject 0555 root bin
+f usr/bin/elfdump 0555 root bin
+f usr/bin/elfedit 0555 root bin
+f usr/bin/elfsign 0555 root bin
+f usr/bin/elfwrap 0555 root bin
+h usr/bin/encrypt=usr/lib/isaexec
+f usr/bin/enhance 0555 root bin
+f usr/bin/env 0555 root bin
+f usr/bin/eqn 0555 root bin
+f usr/bin/etdump 0555 root bin
+f usr/bin/expand 0555 root bin
+f usr/bin/expr 0555 root bin
+f usr/bin/exstr 0555 root bin
+f usr/bin/factor 0555 root bin
+f usr/bin/false 0555 root bin
+h usr/bin/fc=usr/bin/alias
+f usr/bin/fdetach 0555 root bin
+f usr/bin/fdformat 4555 root bin
+h usr/bin/fg=usr/bin/alias
+h usr/bin/fgrep=usr/bin/grep
+h usr/bin/ficl-sys=usr/lib/isaexec
+f usr/bin/file 0555 root bin
+f usr/bin/filesync 0555 root bin
+f usr/bin/find 0555 root bin
+f usr/bin/fmt 0555 root bin
+f usr/bin/fmtmsg 0555 root bin
+f usr/bin/fold 0555 root bin
+f usr/bin/fsstat 0555 root bin
+f usr/bin/ftp 0555 root bin
+h usr/bin/gcore=usr/lib/isaexec
+f usr/bin/gencat 0555 root bin
+f usr/bin/geniconvtbl 0555 root bin
+f usr/bin/genmsg 0555 root bin
+f usr/bin/getconf 0555 root bin
+f usr/bin/getdev 0555 root bin
+f usr/bin/getdgrp 0555 root bin
+f usr/bin/getent 0555 root bin
+f usr/bin/getfacl 0555 root bin
+f usr/bin/getopt 0555 root bin
+h usr/bin/getopts=usr/bin/alias
+f usr/bin/gettext 0555 root bin
+f usr/bin/gettxt 0555 root bin
+f usr/bin/getvol 0555 root bin
+f usr/bin/grep 0555 root bin
+f usr/bin/groups 0555 root bin
+h usr/bin/hash=usr/bin/alias
+f usr/bin/head 0555 root bin
+f usr/bin/hostid 0555 root bin
+f usr/bin/hostname 0555 root bin
+h usr/bin/i286=usr/bin/sun
+h usr/bin/i386=usr/bin/sun
+h usr/bin/i486=usr/bin/sun
+d usr/bin/i86 0755 root bin
+f usr/bin/i86/ksh93 0555 root bin
+f usr/bin/i86/mdb 0555 root bin
+h usr/bin/i86pc=usr/bin/sun
+f usr/bin/iconv 0555 root bin
+f usr/bin/id 0555 root bin
+f usr/bin/infocmp 0555 root bin
+f usr/bin/iostat 0555 root bin
+f usr/bin/ipcrm 0555 root bin
+f usr/bin/ipcs 0555 root bin
+f usr/bin/isainfo 0555 root bin
+f usr/bin/isalist 0555 root bin
+h usr/bin/jobs=usr/bin/alias
+h usr/bin/join=usr/bin/alias
+s usr/bin/jsh=ksh93
+f usr/bin/kbd 0555 root bin
+f usr/bin/kdestroy 0555 root bin
+f usr/bin/keylogin 0555 root bin
+f usr/bin/keylogout 0555 root bin
+h usr/bin/kill=usr/bin/alias
+f usr/bin/kinit 0555 root bin
+f usr/bin/klist 0555 root bin
+f usr/bin/kmfcfg 0555 root bin
+f usr/bin/kpasswd 0555 root bin
+f usr/bin/krb5-config 0555 root bin
+h usr/bin/ksh93=usr/lib/isaexec
+h usr/bin/ksh=usr/lib/isaexec
+f usr/bin/kstat 0555 root bin
+f usr/bin/ktutil 0555 root bin
+f usr/bin/kvmstat 0555 root bin
+f usr/bin/last 0555 root bin
+f usr/bin/lastcomm 0555 root bin
+f usr/bin/ld 0755 root bin
+f usr/bin/ldapadd 0555 root bin
+f usr/bin/ldapdelete 0555 root bin
+f usr/bin/ldaplist 0555 root bin
+h usr/bin/ldapmodify=usr/bin/ldapadd
+f usr/bin/ldapmodrdn 0555 root bin
+f usr/bin/ldapsearch 0555 root bin
+f usr/bin/ldd 0555 root bin
+f usr/bin/lgrpinfo 0555 root bin
+f usr/bin/line 0555 root bin
+f usr/bin/listdgrp 0555 root bin
+f usr/bin/listusers 0555 root bin
+h usr/bin/ln=usr/bin/cp
+f usr/bin/loadkeys 0555 root bin
+f usr/bin/locale 0555 root bin
+f usr/bin/logger 0555 root bin
+f usr/bin/login 4555 root bin
+f usr/bin/logins 0750 root bin
+f usr/bin/logname 0555 root bin
+f usr/bin/look 0755 root bin
+f usr/bin/lorder 0555 root bin
+# We want ls to be the 32bit binary (not a link to isaexec) so we don't blow
+# out memory sorting entries - we just die if we need too much memory. This
+# also matches the setup on a stock solaris box.
+f usr/bin/ls 0555 root bin
+f usr/bin/m4 0555 root bin
+h usr/bin/mac=usr/lib/isaexec
+f usr/bin/mach 0555 root bin
+f usr/bin/mail 2511 root mail
+s usr/bin/mailq=../lib/mailwrapper
+f usr/bin/mailx 2511 root mail
+f usr/bin/makedev 0555 root bin
+f usr/bin/man 0555 root bin
+f usr/bin/mandoc 0555 root bin
+f usr/bin/mcs 0555 root bin
+h usr/bin/mdb=usr/lib/isaexec
+f usr/bin/mesg 0555 root bin
+f usr/bin/mkdir 0555 root bin
+f usr/bin/mkfifo 0555 root bin
+f usr/bin/mkmsgs 0555 root root
+f usr/bin/mkpwdict 0555 root bin
+f usr/bin/mktemp 0555 root bin
+f usr/bin/moe 0555 root bin
+f usr/bin/more 0555 root bin
+f usr/bin/mpstat 0555 root bin
+f usr/bin/msgfmt 0555 root bin
+f usr/bin/mt 0555 root bin
+h usr/bin/mv=usr/bin/cp
+f usr/bin/nawk 0555 root bin
+f usr/bin/nc 0555 root bin
+f usr/bin/neqn 0555 root bin
+f usr/bin/netstat 0555 root bin
+f usr/bin/newform 0555 root bin
+f usr/bin/newgrp 4755 root sys
+f usr/bin/news 0555 root bin
+h usr/bin/newtask=usr/lib/isaexec
+f usr/bin/nfsstat 0555 root bin
+f usr/bin/nice 0555 root bin
+f usr/bin/nicstat 0555 root bin
+f usr/bin/nl 0555 root bin
+f usr/bin/nm 0555 root bin
+h usr/bin/nohup=usr/lib/isaexec
+f usr/bin/nroff 0555 root bin
+f usr/bin/od 0555 root bin
+f usr/bin/optisa 0555 root bin
+f usr/bin/pack 0555 root bin
+h usr/bin/page=usr/bin/more
+f usr/bin/pagesize 0555 root bin
+h usr/bin/pargs=usr/lib/isaexec
+f usr/bin/passwd 6555 root sys
+h usr/bin/paste=usr/bin/alias
+f usr/bin/pathchk 0555 root bin
+h usr/bin/pauxv=usr/lib/isaexec
+f usr/bin/pax 0555 root bin
+s usr/bin/pcat=unpack
+h usr/bin/pcred=usr/lib/isaexec
+h usr/bin/penv=usr/lib/isaexec
+s usr/bin/perl=/opt/local/bin/perl
+h usr/bin/pfbash=usr/bin/pfexec
+h usr/bin/pfcsh=usr/bin/pfexec
+f usr/bin/pfexec 0555 root bin
+h usr/bin/pfiles=usr/lib/isaexec
+h usr/bin/pfksh93=usr/bin/pfexec
+h usr/bin/pfksh=usr/bin/pfexec
+h usr/bin/pflags=usr/lib/isaexec
+h usr/bin/pfrksh93=usr/bin/pfexec
+h usr/bin/pfrksh=usr/bin/pfexec
+h usr/bin/pfsh=usr/bin/pfexec
+h usr/bin/pftcsh=usr/bin/pfexec
+h usr/bin/pfzsh=usr/bin/pfexec
+f usr/bin/pg 0555 root bin
+f usr/bin/pginfo 0555 root bin
+f usr/bin/pgrep 0555 root bin
+f usr/bin/pgstat 0555 root bin
+f usr/bin/pkgtrans 0555 root bin
+h usr/bin/pkill=usr/bin/pgrep
+f usr/bin/pktool 0555 root bin
+h usr/bin/pldd=usr/lib/isaexec
+h usr/bin/plgrp=usr/lib/isaexec
+h usr/bin/plimit=usr/lib/isaexec
+h usr/bin/pmadvise=usr/lib/isaexec
+h usr/bin/pmap=usr/lib/isaexec
+h usr/bin/ppgsz=usr/lib/isaexec
+h usr/bin/ppriv=usr/lib/isaexec
+f usr/bin/pr 0555 root bin
+h usr/bin/prctl=usr/lib/isaexec
+h usr/bin/preap=usr/lib/isaexec
+h usr/bin/print=usr/bin/alias
+f usr/bin/printf 0555 root bin
+f usr/bin/priocntl 0555 root bin
+f usr/bin/profiles 0555 root bin
+f usr/bin/projects 0555 root bin
+h usr/bin/prstat=usr/lib/isaexec
+h usr/bin/prun=usr/lib/isaexec
+f usr/bin/ps 0555 root bin
+h usr/bin/psecflags=usr/lib/isaexec
+h usr/bin/psig=usr/lib/isaexec
+h usr/bin/pstack=usr/lib/isaexec
+h usr/bin/pstop=usr/lib/isaexec
+h usr/bin/ptime=usr/lib/isaexec
+h usr/bin/ptree=usr/lib/isaexec
+f usr/bin/putdev 0555 root bin
+f usr/bin/putdgrp 0555 root bin
+f usr/bin/pvs 0555 root bin
+h usr/bin/pwait=usr/lib/isaexec
+s usr/bin/pwconv=../sbin/pwconv
+f usr/bin/pwd 0555 root bin
+h usr/bin/pwdx=usr/lib/isaexec
+f usr/bin/rcapstat 0555 root bin
+f usr/bin/rcp 4555 root bin
+f usr/bin/rdate 0555 root bin
+f usr/bin/rdist 4555 root bin
+h usr/bin/read=usr/bin/alias
+f usr/bin/red 0555 root bin
+f usr/bin/refer 0555 root bin
+f usr/bin/regcmp 0555 root bin
+s usr/bin/remsh=rsh
+f usr/bin/renice 0555 root bin
+h usr/bin/rev=usr/bin/alias
+h usr/bin/rksh93=usr/lib/isaexec
+h usr/bin/rksh=usr/lib/isaexec
+f usr/bin/rlogin 4555 root bin
+f usr/bin/rm 0555 root bin
+s usr/bin/rmail=mail
+f usr/bin/rmdir 0555 root bin
+f usr/bin/rmformat 0555 root bin
+f usr/bin/roles 0555 root bin
+f usr/bin/rpcinfo 0555 root bin
+f usr/bin/rsh 4555 root bin
+f usr/bin/runat 0555 root bin
+f usr/bin/rup 0555 root bin
+f usr/bin/ruptime 0555 root bin
+f usr/bin/rusers 0555 root bin
+h usr/bin/savecore=usr/lib/isaexec
+f usr/bin/scp 0555 root bin
+f usr/bin/script 0555 root bin
+f usr/bin/sdiff 0555 root bin
+f usr/bin/sed 0555 root bin
+f usr/bin/setfacl 0555 root bin
+f usr/bin/setpgrp 0555 root sys
+h usr/bin/settime=usr/bin/touch
+s usr/bin/sh=ksh93
+f usr/bin/shcomp 0555 root bin
+f usr/bin/size 0555 root bin
+f usr/bin/sleep 0555 root bin
+f usr/bin/smbutil 0555 root bin
+f usr/bin/soelim 0555 root bin
+h usr/bin/sort=usr/lib/isaexec
+f usr/bin/sotruss 0755 root bin
+h usr/bin/sparc=usr/bin/sun
+f usr/bin/spell 0555 root bin
+f usr/bin/split 0555 root bin
+f usr/bin/srchtxt 0555 root bin
+f usr/bin/strchg 0555 root root
+s usr/bin/strclean=../sbin/strclean
+f usr/bin/strconf 0555 root root
+s usr/bin/strerr=../sbin/strerr
+h usr/bin/strings=usr/lib/isaexec
+f usr/bin/strip 0555 root bin
+f usr/bin/stty 0555 root bin
+f usr/bin/su 4555 root sys
+h usr/bin/sum=usr/bin/alias
+f usr/bin/sun 0555 root bin
+h usr/bin/sun2=usr/bin/sun
+h usr/bin/sun3=usr/bin/sun
+h usr/bin/sun3x=usr/bin/sun
+h usr/bin/sun4=usr/bin/sun
+h usr/bin/sun4c=usr/bin/sun
+h usr/bin/sun4d=usr/bin/sun
+h usr/bin/sun4e=usr/bin/sun
+h usr/bin/sun4m=usr/bin/sun
+f usr/bin/svcprop 0555 root bin
+f usr/bin/svcs 0555 root bin
+s usr/bin/sync=../../sbin/sync
+f usr/bin/ta 0555 root bin
+f usr/bin/tabs 0555 root bin
+f usr/bin/tail 0555 root bin
+s usr/bin/tar=../sbin/tar
+f usr/bin/tbl 0555 root bin
+f usr/bin/tcopy 0555 root bin
+h usr/bin/tee=usr/bin/alias
+f usr/bin/telnet 0555 root bin
+h usr/bin/test=usr/bin/alias
+f usr/bin/tic 0555 root bin
+f usr/bin/time 0555 root bin
+f usr/bin/timex 0555 root bin
+f usr/bin/tip 4511 uucp bin
+f usr/bin/touch 0555 root bin
+f usr/bin/tput 0555 root bin
+f usr/bin/tr 0555 root bin
+f usr/bin/troff 0555 root bin
+f usr/bin/true 0555 root bin
+h usr/bin/truss=usr/lib/isaexec
+f usr/bin/tsort 0555 root bin
+f usr/bin/tty 0555 root bin
+h usr/bin/type=usr/bin/alias
+f usr/bin/tzselect 0555 root bin
+f usr/bin/ul 0555 root bin
+h usr/bin/ulimit=usr/bin/alias
+h usr/bin/umask=usr/bin/alias
+h usr/bin/unalias=usr/bin/alias
+s usr/bin/uname=../../sbin/uname
+f usr/bin/uncompress 0555 root bin
+f usr/bin/unexpand 0555 root bin
+h usr/bin/uniq=usr/bin/alias
+f usr/bin/units 0555 root bin
+f usr/bin/unix2dos 0555 root bin
+f usr/bin/unpack 0555 root bin
+h usr/bin/uptime=usr/bin/w
+f usr/bin/userattr 0555 root bin
+f usr/bin/uudecode 0555 root uucp
+f usr/bin/uuencode 0555 root uucp
+f usr/bin/uuidgen 0555 root bin
+h usr/bin/vedit=usr/bin/edit
+f usr/bin/vfsstat 0555 root bin
+f usr/bin/vgrind 0555 root bin
+f usr/bin/vmstat 0555 root bin
+f usr/bin/vndstat 0555 root bin
+f usr/bin/vtfontcvt 0555 root bin
+f usr/bin/w 4555 root bin
+h usr/bin/wait=usr/bin/alias
+h usr/bin/wc=usr/bin/alias
+h usr/bin/whatis=usr/bin/man
+f usr/bin/which 0555 root bin
+f usr/bin/who 0555 root bin
+f usr/bin/whocalls 0755 root bin
+f usr/bin/whois 0555 root bin
+f usr/bin/wracct 0555 root bin
+f usr/bin/write 2555 root tty
+s usr/bin/wtf=man
+f usr/bin/xargs 0555 root bin
+f usr/bin/xgettext 0555 root bin
+f usr/bin/xstr 0555 root bin
+f usr/bin/yes 0555 root bin
+f usr/bin/ypcat 0555 root other
+f usr/bin/ypmatch 0555 root other
+f usr/bin/yppasswd 0555 root bin
+f usr/bin/ypwhich 0555 root other
+f usr/bin/zcat 0555 root bin
+f usr/bin/ziostat 0555 root bin
+s usr/bin/zonename=../../sbin/zonename
+f usr/bin/ztest 0555 root bin
+d usr/ccs 0755 root bin
+d usr/ccs/bin 0755 root bin
+d usr/ccs/bin/amd64 0755 root bin
+s usr/ccs/bin/amd64/ld=../../../bin/amd64/ld
+s usr/ccs/bin/ar=../../bin/ar
+s usr/ccs/bin/dump=../../bin/dump
+s usr/ccs/bin/ld=../../bin/ld
+s usr/ccs/bin/m4=../../bin/m4
+s usr/ccs/bin/regcmp=../../bin/regcmp
+d usr/ccs/lib 0755 root bin
+d usr/ccs/lib/amd64 0755 root bin
+s usr/ccs/lib/amd64/libcurses.so.1=../../../../lib/amd64/libcurses.so.1
+s usr/ccs/lib/amd64/libform.so.1=../../../lib/amd64/libform.so.1
+s usr/ccs/lib/amd64/libgen.so.1=../../../../lib/amd64/libgen.so.1
+s usr/ccs/lib/amd64/libl.so.1=../../../lib/amd64/libl.so.1
+s usr/ccs/lib/amd64/libmalloc.so.1=../../../lib/amd64/libmalloc.so.1
+s usr/ccs/lib/amd64/libmenu.so.1=../../../lib/amd64/libmenu.so.1
+s usr/ccs/lib/amd64/libpanel.so.1=../../../lib/amd64/libpanel.so.1
+s usr/ccs/lib/amd64/libtermcap.so.1=../../../../lib/amd64/libtermcap.so.1
+s usr/ccs/lib/amd64/libtermlib.so.1=../../../../lib/amd64/libcurses.so.1
+s usr/ccs/lib/amd64/liby.so.1=../../../lib/amd64/liby.so.1
+s usr/ccs/lib/libcurses.so.1=../../../lib/libcurses.so.1
+s usr/ccs/lib/libform.so.1=../../lib/libform.so.1
+s usr/ccs/lib/libgen.so.1=../../../lib/libgen.so.1
+s usr/ccs/lib/libl.so.1=../../lib/libl.so.1
+s usr/ccs/lib/libmalloc.so.1=../../lib/libmalloc.so.1
+s usr/ccs/lib/libmenu.so.1=../../lib/libmenu.so.1
+s usr/ccs/lib/libpanel.so.1=../../lib/libpanel.so.1
+s usr/ccs/lib/libtermcap.so.1=../../../lib/libtermcap.so.1
+s usr/ccs/lib/libtermlib.so.1=../../../lib/libcurses.so.1
+s usr/ccs/lib/liby.so.1=../../lib/liby.so.1
+s usr/ccs/lib/link_audit=../../lib/link_audit
+s usr/dict=share/lib/dict
+d usr/gnu 0755 root bin
+d usr/gnu/bin 0755 root bin
+d usr/gnu/lib 0755 root bin
+d usr/gnu/share 0755 root sys
+d usr/gnu/share/man 0755 root bin
+d usr/gnu/share/man/man1 0755 root bin
+d usr/gnu/share/man/man3 0755 root bin
+d usr/gnu/share/man/man5 0755 root bin
+d usr/gnu/share/man/man7 0755 root bin
+d usr/has 0755 root bin
+d usr/has/man 0755 root bin
+d usr/has/man/man1has 0755 root bin
+f usr/has/man/man1has/edit.1has 0444 root bin
+f usr/has/man/man1has/vi.1has 0444 root bin
+d usr/include 0755 root bin
+f usr/include/aclutils.h 0644 root bin
+d usr/include/ads 0755 root bin
+f usr/include/ads/dsgetdc.h 0644 root bin
+f usr/include/aio.h 0644 root bin
+f usr/include/alloca.h 0644 root bin
+d usr/include/amd64 0755 root bin
+d usr/include/amd64/sys 0755 root bin
+f usr/include/amd64/sys/kdi_regs.h 0644 root bin
+f usr/include/amd64/sys/privmregs.h 0644 root bin
+f usr/include/amd64/sys/privregs.h 0644 root bin
+f usr/include/ar.h 0644 root bin
+f usr/include/archives.h 0644 root bin
+d usr/include/arpa 0755 root bin
+f usr/include/arpa/ftp.h 0644 root bin
+f usr/include/arpa/inet.h 0644 root bin
+f usr/include/arpa/nameser.h 0644 root bin
+f usr/include/arpa/nameser_compat.h 0644 root bin
+f usr/include/arpa/telnet.h 0644 root bin
+f usr/include/arpa/tftp.h 0644 root bin
+d usr/include/asm 0755 root bin
+f usr/include/asm/atomic.h 0644 root bin
+f usr/include/asm/bitmap.h 0644 root bin
+f usr/include/asm/byteorder.h 0644 root bin
+f usr/include/asm/clock.h 0644 root bin
+f usr/include/asm/cpu.h 0644 root bin
+f usr/include/asm/cpuvar.h 0644 root bin
+f usr/include/asm/htable.h 0644 root bin
+f usr/include/asm/mmu.h 0644 root bin
+f usr/include/asm/sunddi.h 0644 root bin
+f usr/include/asm/thread.h 0644 root bin
+f usr/include/assert.h 0644 root bin
+d usr/include/ast 0755 root bin
+f usr/include/ast/align.h 0644 root bin
+f usr/include/ast/aso.h 0644 root bin
+f usr/include/ast/ast.h 0644 root bin
+f usr/include/ast/ast_api.h 0644 root bin
+f usr/include/ast/ast_botch.h 0644 root bin
+f usr/include/ast/ast_ccode.h 0644 root bin
+f usr/include/ast/ast_common.h 0644 root bin
+f usr/include/ast/ast_dir.h 0644 root bin
+f usr/include/ast/ast_dirent.h 0644 root bin
+f usr/include/ast/ast_fcntl.h 0644 root bin
+f usr/include/ast/ast_float.h 0644 root bin
+f usr/include/ast/ast_fs.h 0644 root bin
+f usr/include/ast/ast_getopt.h 0644 root bin
+f usr/include/ast/ast_iconv.h 0644 root bin
+f usr/include/ast/ast_lib.h 0644 root bin
+f usr/include/ast/ast_limits.h 0644 root bin
+f usr/include/ast/ast_map.h 0644 root bin
+f usr/include/ast/ast_mmap.h 0644 root bin
+f usr/include/ast/ast_mode.h 0644 root bin
+f usr/include/ast/ast_namval.h 0644 root bin
+f usr/include/ast/ast_ndbm.h 0644 root bin
+f usr/include/ast/ast_nl_types.h 0644 root bin
+f usr/include/ast/ast_param.h 0644 root bin
+f usr/include/ast/ast_sizeof.h 0644 root bin
+f usr/include/ast/ast_standards.h 0644 root bin
+f usr/include/ast/ast_std.h 0644 root bin
+f usr/include/ast/ast_stdio.h 0644 root bin
+f usr/include/ast/ast_sys.h 0644 root bin
+f usr/include/ast/ast_time.h 0644 root bin
+f usr/include/ast/ast_tty.h 0644 root bin
+f usr/include/ast/ast_vfork.h 0644 root bin
+f usr/include/ast/ast_wait.h 0644 root bin
+f usr/include/ast/ast_wchar.h 0644 root bin
+f usr/include/ast/ast_wctype.h 0644 root bin
+f usr/include/ast/ast_windows.h 0644 root bin
+f usr/include/ast/bytesex.h 0644 root bin
+f usr/include/ast/ccode.h 0644 root bin
+f usr/include/ast/cdt.h 0644 root bin
+f usr/include/ast/cdtlib.h 0644 root bin
+f usr/include/ast/cmd.h 0644 root bin
+f usr/include/ast/cmdarg.h 0644 root bin
+f usr/include/ast/cmdext.h 0644 root bin
+f usr/include/ast/debug.h 0644 root bin
+f usr/include/ast/dirent.h 0644 root bin
+f usr/include/ast/dlldefs.h 0644 root bin
+f usr/include/ast/dt.h 0644 root bin
+f usr/include/ast/endian.h 0644 root bin
+f usr/include/ast/error.h 0644 root bin
+f usr/include/ast/find.h 0644 root bin
+f usr/include/ast/fnmatch.h 0644 root bin
+f usr/include/ast/fnv.h 0644 root bin
+f usr/include/ast/fs3d.h 0644 root bin
+f usr/include/ast/fts.h 0644 root bin
+f usr/include/ast/ftw.h 0644 root bin
+f usr/include/ast/ftwalk.h 0644 root bin
+f usr/include/ast/getopt.h 0644 root bin
+f usr/include/ast/glob.h 0644 root bin
+f usr/include/ast/hash.h 0644 root bin
+f usr/include/ast/hashkey.h 0644 root bin
+f usr/include/ast/hashpart.h 0644 root bin
+f usr/include/ast/history.h 0644 root bin
+f usr/include/ast/iconv.h 0644 root bin
+f usr/include/ast/ip6.h 0644 root bin
+f usr/include/ast/lc.h 0644 root bin
+f usr/include/ast/ls.h 0644 root bin
+f usr/include/ast/magic.h 0644 root bin
+f usr/include/ast/magicid.h 0644 root bin
+f usr/include/ast/mc.h 0644 root bin
+f usr/include/ast/mime.h 0644 root bin
+f usr/include/ast/mnt.h 0644 root bin
+f usr/include/ast/modecanon.h 0644 root bin
+f usr/include/ast/modex.h 0644 root bin
+f usr/include/ast/namval.h 0644 root bin
+f usr/include/ast/nl_types.h 0644 root bin
+f usr/include/ast/nval.h 0644 root bin
+f usr/include/ast/option.h 0644 root bin
+f usr/include/ast/pp.h 0644 root bin
+f usr/include/ast/ppkey.h 0644 root bin
+f usr/include/ast/preroot.h 0644 root bin
+f usr/include/ast/proc.h 0644 root bin
+f usr/include/ast/prototyped.h 0644 root bin
+f usr/include/ast/re_comp.h 0644 root bin
+f usr/include/ast/recfmt.h 0644 root bin
+f usr/include/ast/regex.h 0644 root bin
+f usr/include/ast/regexp.h 0644 root bin
+f usr/include/ast/sfdisc.h 0644 root bin
+f usr/include/ast/sfio.h 0644 root bin
+f usr/include/ast/sfio_s.h 0644 root bin
+f usr/include/ast/sfio_t.h 0644 root bin
+f usr/include/ast/shcmd.h 0644 root bin
+f usr/include/ast/shell.h 0644 root bin
+f usr/include/ast/sig.h 0644 root bin
+f usr/include/ast/stack.h 0644 root bin
+f usr/include/ast/stak.h 0644 root bin
+f usr/include/ast/stdio.h 0644 root bin
+f usr/include/ast/stk.h 0644 root bin
+f usr/include/ast/sum.h 0644 root bin
+f usr/include/ast/swap.h 0644 root bin
+f usr/include/ast/tar.h 0644 root bin
+f usr/include/ast/times.h 0644 root bin
+f usr/include/ast/tm.h 0644 root bin
+f usr/include/ast/tmx.h 0644 root bin
+f usr/include/ast/tok.h 0644 root bin
+f usr/include/ast/tv.h 0644 root bin
+f usr/include/ast/usage.h 0644 root bin
+f usr/include/ast/vdb.h 0644 root bin
+f usr/include/ast/vecargs.h 0644 root bin
+f usr/include/ast/vmalloc.h 0644 root bin
+f usr/include/ast/wait.h 0644 root bin
+f usr/include/ast/wchar.h 0644 root bin
+f usr/include/ast/wctype.h 0644 root bin
+f usr/include/ast/wordexp.h 0644 root bin
+f usr/include/atomic.h 0644 root bin
+f usr/include/attr.h 0644 root bin
+d usr/include/audio 0755 root bin
+f usr/include/audio/au.h 0644 root bin
+f usr/include/auth_attr.h 0644 root bin
+f usr/include/auth_list.h 0644 root bin
+d usr/include/bsm 0755 root bin
+f usr/include/bsm/adt.h 0644 root bin
+f usr/include/bsm/adt_event.h 0644 root bin
+f usr/include/bsm/audit.h 0644 root bin
+f usr/include/bsm/audit_door_infc.h 0644 root bin
+f usr/include/bsm/audit_kernel.h 0644 root bin
+f usr/include/bsm/audit_kevents.h 0644 root bin
+f usr/include/bsm/audit_private.h 0644 root bin
+f usr/include/bsm/audit_record.h 0644 root bin
+f usr/include/bsm/audit_uevents.h 0644 root bin
+f usr/include/bsm/devalloc.h 0644 root bin
+f usr/include/bsm/devices.h 0644 root bin
+f usr/include/bsm/libbsm.h 0644 root bin
+f usr/include/complex.h 0644 root bin
+f usr/include/config_admin.h 0644 root bin
+f usr/include/cpio.h 0644 root bin
+f usr/include/crypt.h 0644 root bin
+f usr/include/cryptoutil.h 0644 root bin
+f usr/include/ctype.h 0644 root bin
+f usr/include/curses.h 0644 root bin
+d usr/include/dat 0755 root bin
+f usr/include/dat/dat.h 0644 root bin
+f usr/include/dat/dat_error.h 0644 root bin
+f usr/include/dat/dat_platform_specific.h 0644 root bin
+f usr/include/dat/dat_redirection.h 0644 root bin
+f usr/include/dat/dat_registry.h 0644 root bin
+f usr/include/dat/dat_vendor_specific.h 0644 root bin
+f usr/include/dat/udat.h 0644 root bin
+f usr/include/dat/udat_config.h 0644 root bin
+f usr/include/dat/udat_redirection.h 0644 root bin
+f usr/include/dat/udat_vendor_specific.h 0644 root bin
+f usr/include/deflt.h 0644 root bin
+f usr/include/demangle-sys.h 0644 root bin
+d usr/include/des 0755 root bin
+f usr/include/des/des.h 0644 root bin
+f usr/include/des/desdata.h 0644 root bin
+f usr/include/des/softdes.h 0644 root bin
+f usr/include/devfsadm.h 0644 root bin
+f usr/include/device_info.h 0644 root bin
+f usr/include/devid.h 0644 root bin
+f usr/include/devmgmt.h 0644 root bin
+f usr/include/devpoll.h 0644 root bin
+f usr/include/dial.h 0644 root bin
+f usr/include/directory.h 0644 root bin
+f usr/include/dirent.h 0644 root bin
+f usr/include/dlfcn.h 0644 root bin
+f usr/include/dns_sd.h 0644 root bin
+f usr/include/door.h 0644 root bin
+f usr/include/dtrace.h 0644 root bin
+f usr/include/elf.h 0644 root bin
+f usr/include/endian.h 0644 root bin
+f usr/include/err.h 0644 root bin
+f usr/include/errfp.h 0644 root bin
+f usr/include/errno.h 0644 root bin
+f usr/include/eti.h 0644 root bin
+f usr/include/euc.h 0644 root bin
+f usr/include/exacct.h 0644 root bin
+f usr/include/exacct_impl.h 0644 root bin
+f usr/include/exec_attr.h 0644 root bin
+f usr/include/execinfo.h 0644 root bin
+f usr/include/fatal.h 0644 root bin
+f usr/include/fcntl.h 0644 root bin
+f usr/include/fenv.h 0644 root bin
+f usr/include/float.h 0644 root bin
+f usr/include/floatingpoint.h 0644 root bin
+d usr/include/fm 0755 root bin
+f usr/include/fm/diagcode.h 0644 root bin
+f usr/include/fm/fmd_adm.h 0644 root bin
+f usr/include/fm/fmd_agent.h 0644 root bin
+f usr/include/fm/fmd_api.h 0644 root bin
+f usr/include/fm/fmd_fmri.h 0644 root bin
+f usr/include/fm/fmd_log.h 0644 root bin
+f usr/include/fm/fmd_msg.h 0644 root bin
+f usr/include/fm/fmd_snmp.h 0644 root bin
+f usr/include/fm/libdiskstatus.h 0644 root bin
+f usr/include/fm/libfmevent.h 0644 root bin
+f usr/include/fm/libfmevent_ruleset.h 0644 root bin
+f usr/include/fm/libseslog.h 0644 root bin
+f usr/include/fm/libtopo.h 0644 root bin
+f usr/include/fm/topo_hc.h 0644 root bin
+f usr/include/fm/topo_list.h 0644 root bin
+f usr/include/fm/topo_method.h 0644 root bin
+f usr/include/fm/topo_mod.h 0644 root bin
+f usr/include/fmtmsg.h 0644 root bin
+f usr/include/fnmatch.h 0644 root bin
+f usr/include/form.h 0644 root bin
+f usr/include/fts.h 0644 root bin
+f usr/include/ftw.h 0644 root bin
+d usr/include/fwflash 0755 root bin
+f usr/include/fwflash/fwflash.h 0644 root bin
+f usr/include/gelf.h 0644 root bin
+f usr/include/getopt.h 0644 root bin
+f usr/include/getwidth.h 0644 root bin
+f usr/include/getxby_door.h 0644 root bin
+f usr/include/glob.h 0644 root bin
+f usr/include/grp.h 0644 root bin
+d usr/include/gssapi 0755 root bin
+f usr/include/gssapi/gssapi.h 0644 root bin
+f usr/include/gssapi/gssapi_ext.h 0644 root bin
+f usr/include/hbaapi.h 0644 root bin
+d usr/include/ia32 0755 root bin
+d usr/include/ia32/sys 0755 root bin
+s usr/include/ia32/sys/asm_linkage.h=../../sys/asm_linkage.h
+f usr/include/ia32/sys/kdi_regs.h 0644 root bin
+s usr/include/ia32/sys/machtypes.h=../../sys/machtypes.h
+f usr/include/ia32/sys/privmregs.h 0644 root bin
+f usr/include/ia32/sys/privregs.h 0644 root bin
+s usr/include/ia32/sys/psw.h=../../sys/psw.h
+s usr/include/ia32/sys/pte.h=../../sys/pte.h
+s usr/include/ia32/sys/reg.h=../../sys/reg.h
+s usr/include/ia32/sys/stack.h=../../sys/stack.h
+s usr/include/ia32/sys/trap.h=../../sys/trap.h
+s usr/include/ia32/sys/traptrace.h=../../sys/traptrace.h
+f usr/include/iconv.h 0644 root bin
+f usr/include/idmap.h 0644 root bin
+f usr/include/ieeefp.h 0644 root bin
+f usr/include/ifaddrs.h 0644 root bin
+f usr/include/ikedoor.h 0644 root bin
+f usr/include/ima.h 0644 root bin
+d usr/include/inet 0755 root bin
+f usr/include/inet/arp.h 0644 root bin
+f usr/include/inet/cc.h 0644 root bin
+f usr/include/inet/common.h 0644 root bin
+f usr/include/inet/ip.h 0644 root bin
+f usr/include/inet/ip2mac.h 0644 root bin
+f usr/include/inet/ip2mac_impl.h 0644 root bin
+f usr/include/inet/ip6.h 0644 root bin
+f usr/include/inet/ip6_asp.h 0644 root bin
+f usr/include/inet/ip_arp.h 0644 root bin
+f usr/include/inet/ip_ftable.h 0644 root bin
+f usr/include/inet/ip_if.h 0644 root bin
+f usr/include/inet/ip_impl.h 0644 root bin
+f usr/include/inet/ip_ire.h 0644 root bin
+f usr/include/inet/ip_multi.h 0644 root bin
+f usr/include/inet/ip_ndp.h 0644 root bin
+f usr/include/inet/ip_netinfo.h 0644 root bin
+f usr/include/inet/ip_rts.h 0644 root bin
+f usr/include/inet/ip_stack.h 0644 root bin
+f usr/include/inet/ipclassifier.h 0644 root bin
+f usr/include/inet/ipdrop.h 0644 root bin
+f usr/include/inet/ipnet.h 0644 root bin
+f usr/include/inet/ipp_common.h 0644 root bin
+f usr/include/inet/ipsec_impl.h 0644 root bin
+f usr/include/inet/ipsec_info.h 0644 root bin
+f usr/include/inet/ipsecah.h 0644 root bin
+f usr/include/inet/ipsecesp.h 0644 root bin
+f usr/include/inet/iptun.h 0644 root bin
+f usr/include/inet/keysock.h 0644 root bin
+f usr/include/inet/led.h 0644 root bin
+f usr/include/inet/mi.h 0644 root bin
+f usr/include/inet/mib2.h 0644 root bin
+f usr/include/inet/nd.h 0644 root bin
+f usr/include/inet/optcom.h 0644 root bin
+f usr/include/inet/rawip_impl.h 0644 root bin
+f usr/include/inet/sadb.h 0644 root bin
+f usr/include/inet/sctp_itf.h 0644 root bin
+f usr/include/inet/snmpcom.h 0644 root bin
+f usr/include/inet/tcp.h 0644 root bin
+f usr/include/inet/tcp_impl.h 0644 root bin
+f usr/include/inet/tcp_sack.h 0644 root bin
+f usr/include/inet/tcp_stack.h 0644 root bin
+f usr/include/inet/tcp_stats.h 0644 root bin
+f usr/include/inet/tunables.h 0644 root bin
+f usr/include/inet/udp_impl.h 0644 root bin
+f usr/include/inet/wifi_ioctl.h 0644 root bin
+f usr/include/inetsvc.h 0644 root bin
+f usr/include/instzones_api.h 0644 root bin
+f usr/include/inttypes.h 0644 root bin
+f usr/include/ipadm_ipmgmt.h 0644 root bin
+f usr/include/ipadm_ndpd.h 0644 root bin
+f usr/include/ipmp.h 0644 root bin
+f usr/include/ipmp_admin.h 0644 root bin
+f usr/include/ipmp_mpathd.h 0644 root bin
+f usr/include/ipmp_query.h 0644 root bin
+f usr/include/ipmp_query_impl.h 0644 root bin
+d usr/include/ipp 0755 root bin
+d usr/include/ipp/dlcosmk 0755 root bin
+f usr/include/ipp/dlcosmk/dlcosmk.h 0644 root bin
+d usr/include/ipp/dscpmk 0755 root bin
+f usr/include/ipp/dscpmk/dscpmk.h 0644 root bin
+d usr/include/ipp/flowacct 0755 root bin
+f usr/include/ipp/flowacct/flowacct.h 0644 root bin
+d usr/include/ipp/ipgpc 0755 root bin
+f usr/include/ipp/ipgpc/ipgpc.h 0644 root bin
+f usr/include/ipp/ipp.h 0644 root bin
+f usr/include/ipp/ipp_config.h 0644 root bin
+f usr/include/ipp/ipp_impl.h 0644 root bin
+f usr/include/ipp/ippctl.h 0644 root bin
+d usr/include/ipp/meters 0755 root bin
+f usr/include/ipp/meters/meter.h 0644 root bin
+f usr/include/ipsec_util.h 0644 root bin
+d usr/include/iso 0755 root bin
+s usr/include/iso/assert_iso.h=../assert.h
+f usr/include/iso/ctype_iso.h 0644 root bin
+s usr/include/iso/errno_iso.h=../errno.h
+s usr/include/iso/float_iso.h=../float.h
+s usr/include/iso/iso646_iso.h=../iso646.h
+f usr/include/iso/limits_iso.h 0644 root bin
+f usr/include/iso/locale_iso.h 0644 root bin
+f usr/include/iso/math_c99.h 0644 root bin
+f usr/include/iso/math_iso.h 0644 root bin
+f usr/include/iso/setjmp_iso.h 0644 root bin
+f usr/include/iso/signal_iso.h 0644 root bin
+f usr/include/iso/stdarg_c99.h 0644 root bin
+f usr/include/iso/stdarg_iso.h 0644 root bin
+f usr/include/iso/stddef_iso.h 0644 root bin
+f usr/include/iso/stdio_c99.h 0644 root bin
+f usr/include/iso/stdio_iso.h 0644 root bin
+f usr/include/iso/stdlib_c11.h 0644 root bin
+f usr/include/iso/stdlib_c99.h 0644 root bin
+f usr/include/iso/stdlib_iso.h 0644 root bin
+f usr/include/iso/string_iso.h 0644 root bin
+f usr/include/iso/time_iso.h 0644 root bin
+f usr/include/iso/wchar_c99.h 0644 root bin
+f usr/include/iso/wchar_iso.h 0644 root bin
+f usr/include/iso/wctype_iso.h 0644 root bin
+f usr/include/iso646.h 0644 root bin
+d usr/include/kerberosv5 0755 root bin
+f usr/include/kerberosv5/com_err.h 0644 root bin
+f usr/include/kerberosv5/krb5.h 0644 root bin
+f usr/include/kerberosv5/locate_plugin.h 0644 root bin
+f usr/include/kerberosv5/mit-sipb-copyright.h 0644 root bin
+f usr/include/kerberosv5/mit_copyright.h 0644 root bin
+f usr/include/klpd.h 0644 root bin
+f usr/include/kmfapi.h 0644 root bin
+f usr/include/kmftypes.h 0644 root bin
+f usr/include/kstat.h 0644 root bin
+f usr/include/kvm.h 0644 root bin
+f usr/include/langinfo.h 0644 root bin
+f usr/include/lastlog.h 0644 root bin
+f usr/include/lber.h 0644 root bin
+f usr/include/ldap.h 0644 root bin
+f usr/include/libbe.h 0644 root bin
+f usr/include/libbe_priv.h 0644 root bin
+f usr/include/libbrand.h 0644 root bin
+f usr/include/libcmdutils.h 0644 root bin
+f usr/include/libcontract.h 0644 root bin
+f usr/include/libcontract_priv.h 0644 root bin
+f usr/include/libcpc.h 0644 root bin
+f usr/include/libctf.h 0644 root bin
+f usr/include/libdevice.h 0644 root bin
+f usr/include/libdevinfo.h 0644 root bin
+f usr/include/libdisasm.h 0644 root bin
+f usr/include/libdiskmgt.h 0644 root bin
+f usr/include/libdladm.h 0644 root bin
+f usr/include/libdladm_impl.h 0644 root bin
+f usr/include/libdlaggr.h 0644 root bin
+f usr/include/libdlbridge.h 0644 root bin
+f usr/include/libdlether.h 0644 root bin
+f usr/include/libdlflow.h 0644 root bin
+f usr/include/libdlflow_impl.h 0644 root bin
+f usr/include/libdlib.h 0644 root bin
+f usr/include/libdliptun.h 0644 root bin
+f usr/include/libdllink.h 0644 root bin
+f usr/include/libdlmgmt.h 0644 root bin
+f usr/include/libdlpi.h 0644 root bin
+f usr/include/libdlsim.h 0644 root bin
+f usr/include/libdlstat.h 0644 root bin
+f usr/include/libdlvlan.h 0644 root bin
+f usr/include/libdlvnic.h 0644 root bin
+f usr/include/libdlwlan.h 0644 root bin
+f usr/include/libdlwlan_impl.h 0644 root bin
+f usr/include/libelf.h 0644 root bin
+f usr/include/libelfsign.h 0644 root bin
+f usr/include/libfcoe.h 0644 root bin
+f usr/include/libfdisk.h 0644 root bin
+f usr/include/libfstyp.h 0644 root bin
+f usr/include/libfstyp_module.h 0644 root bin
+f usr/include/libgen.h 0644 root bin
+f usr/include/libgrubmgmt.h 0644 root bin
+f usr/include/libilb.h 0644 root bin
+f usr/include/libinetutil.h 0644 root bin
+f usr/include/libintl.h 0644 root bin
+f usr/include/libipadm.h 0644 root bin
+f usr/include/libipd.h 0644 root bin
+f usr/include/libipmi.h 0644 root bin
+f usr/include/libipp.h 0644 root bin
+f usr/include/libiscsit.h 0644 root bin
+f usr/include/libmail.h 0644 root bin
+d usr/include/libmilter 0755 root bin
+f usr/include/libmilter/README 0644 root bin
+f usr/include/libmilter/mfapi.h 0644 root bin
+f usr/include/libmilter/mfdef.h 0644 root bin
+f usr/include/libndmp.h 0644 root bin
+f usr/include/libnvpair.h 0644 root bin
+f usr/include/libnwam.h 0644 root bin
+f usr/include/libnwam_priv.h 0644 root bin
+f usr/include/libpctx.h 0644 root bin
+f usr/include/libproc.h 0644 root bin
+f usr/include/librcm.h 0644 root bin
+f usr/include/librcm_event.h 0644 root bin
+f usr/include/librcm_impl.h 0644 root bin
+f usr/include/librestart.h 0644 root bin
+f usr/include/librestart_priv.h 0644 root bin
+f usr/include/libscf.h 0644 root bin
+f usr/include/libscf_priv.h 0644 root bin
+f usr/include/libshare.h 0644 root bin
+f usr/include/libshare_impl.h 0644 root bin
+f usr/include/libsocket_priv.h 0644 root bin
+f usr/include/libsrpt.h 0644 root bin
+f usr/include/libstmf.h 0644 root bin
+f usr/include/libstmfproxy.h 0644 root bin
+f usr/include/libsun_ima.h 0644 root bin
+f usr/include/libsysevent.h 0644 root bin
+f usr/include/libsysevent_impl.h 0644 root bin
+f usr/include/libtecla.h 0644 root bin
+f usr/include/libuutil.h 0644 root bin
+f usr/include/libuutil_impl.h 0644 root bin
+f usr/include/libvnd.h 0644 root bin
+f usr/include/libvrrpadm.h 0644 root bin
+f usr/include/libvscan.h 0644 root bin
+f usr/include/libw.h 0644 root bin
+f usr/include/libzfs.h 0644 root bin
+f usr/include/libzfs_core.h 0644 root bin
+f usr/include/libzfs_jni_dataset.h 0644 root bin
+f usr/include/libzfs_jni_disk.h 0644 root bin
+f usr/include/libzfs_jni_diskmgt.h 0644 root bin
+f usr/include/libzfs_jni_ipool.h 0644 root bin
+f usr/include/libzfs_jni_main.h 0644 root bin
+f usr/include/libzfs_jni_pool.h 0644 root bin
+f usr/include/libzfs_jni_property.h 0644 root bin
+f usr/include/libzfs_jni_util.h 0644 root bin
+f usr/include/libzonecfg.h 0644 root bin
+f usr/include/libzoneinfo.h 0644 root bin
+f usr/include/limits.h 0644 root bin
+f usr/include/linenum.h 0644 root bin
+f usr/include/link.h 0644 root bin
+f usr/include/listen.h 0644 root bin
+f usr/include/locale.h 0644 root bin
+f usr/include/macros.h 0644 root bin
+f usr/include/maillock.h 0644 root bin
+f usr/include/malloc.h 0644 root bin
+f usr/include/math.h 0644 root bin
+f usr/include/md4.h 0644 root bin
+f usr/include/md5.h 0644 root bin
+f usr/include/memory.h 0644 root bin
+f usr/include/menu.h 0644 root bin
+f usr/include/mon.h 0644 root bin
+f usr/include/monetary.h 0644 root bin
+f usr/include/mp.h 0644 root bin
+f usr/include/mpapi-sun.h 0644 root bin
+f usr/include/mpapi.h 0644 root bin
+f usr/include/mqueue.h 0644 root bin
+f usr/include/mtmalloc.h 0644 root bin
+f usr/include/nan.h 0644 root bin
+f usr/include/ndbm.h 0644 root bin
+f usr/include/ndpd.h 0644 root bin
+d usr/include/net 0755 root bin
+f usr/include/net/af.h 0644 root bin
+f usr/include/net/bpf.h 0644 root bin
+f usr/include/net/bpfdesc.h 0644 root bin
+f usr/include/net/bridge.h 0644 root bin
+f usr/include/net/bridge_impl.h 0644 root bin
+f usr/include/net/dlt.h 0644 root bin
+f usr/include/net/if.h 0644 root bin
+f usr/include/net/if_arp.h 0644 root bin
+f usr/include/net/if_dl.h 0644 root bin
+f usr/include/net/if_types.h 0644 root bin
+f usr/include/net/pfkeyv2.h 0644 root bin
+f usr/include/net/pfpolicy.h 0644 root bin
+f usr/include/net/ppp-comp.h 0644 root bin
+f usr/include/net/ppp_defs.h 0644 root bin
+f usr/include/net/pppio.h 0644 root bin
+f usr/include/net/pppoe.h 0644 root bin
+f usr/include/net/radix.h 0644 root bin
+f usr/include/net/route.h 0644 root bin
+f usr/include/net/simnet.h 0644 root bin
+f usr/include/net/sppptun.h 0644 root bin
+f usr/include/net/trill.h 0644 root bin
+f usr/include/net/vjcompress.h 0644 root bin
+f usr/include/net/wpa.h 0644 root bin
+f usr/include/netconfig.h 0644 root bin
+f usr/include/netdb.h 0644 root bin
+f usr/include/netdir.h 0644 root bin
+d usr/include/netinet 0755 root bin
+f usr/include/netinet/arp.h 0644 root bin
+f usr/include/netinet/dhcp.h 0644 root bin
+f usr/include/netinet/dhcp6.h 0644 root bin
+f usr/include/netinet/icmp6.h 0644 root bin
+f usr/include/netinet/icmp_var.h 0644 root bin
+f usr/include/netinet/if_ether.h 0644 root bin
+f usr/include/netinet/igmp.h 0644 root bin
+f usr/include/netinet/igmp_var.h 0644 root bin
+f usr/include/netinet/in.h 0644 root bin
+f usr/include/netinet/in_pcb.h 0644 root bin
+f usr/include/netinet/in_systm.h 0644 root bin
+f usr/include/netinet/in_var.h 0644 root bin
+f usr/include/netinet/inetutil.h 0644 root bin
+f usr/include/netinet/ip.h 0644 root bin
+f usr/include/netinet/ip6.h 0644 root bin
+f usr/include/netinet/ip_auth.h 0644 root bin
+f usr/include/netinet/ip_compat.h 0644 root bin
+f usr/include/netinet/ip_fil.h 0644 root bin
+f usr/include/netinet/ip_frag.h 0644 root bin
+f usr/include/netinet/ip_htable.h 0644 root bin
+f usr/include/netinet/ip_icmp.h 0644 root bin
+f usr/include/netinet/ip_lookup.h 0644 root bin
+f usr/include/netinet/ip_mroute.h 0644 root bin
+f usr/include/netinet/ip_nat.h 0644 root bin
+f usr/include/netinet/ip_pool.h 0644 root bin
+f usr/include/netinet/ip_proxy.h 0644 root bin
+f usr/include/netinet/ip_state.h 0644 root bin
+f usr/include/netinet/ip_var.h 0644 root bin
+f usr/include/netinet/ipf_stack.h 0644 root bin
+f usr/include/netinet/ipl.h 0644 root bin
+f usr/include/netinet/pim.h 0644 root bin
+f usr/include/netinet/sctp.h 0644 root bin
+f usr/include/netinet/tcp.h 0644 root bin
+f usr/include/netinet/tcp_debug.h 0644 root bin
+f usr/include/netinet/tcp_fsm.h 0644 root bin
+f usr/include/netinet/tcp_seq.h 0644 root bin
+f usr/include/netinet/tcp_timer.h 0644 root bin
+f usr/include/netinet/tcp_var.h 0644 root bin
+f usr/include/netinet/tcpip.h 0644 root bin
+f usr/include/netinet/udp.h 0644 root bin
+f usr/include/netinet/udp_var.h 0644 root bin
+f usr/include/netinet/vrrp.h 0644 root bin
+d usr/include/netpacket 0755 root bin
+f usr/include/netpacket/packet.h 0644 root bin
+d usr/include/netsmb 0755 root bin
+f usr/include/netsmb/smbfs_acl.h 0644 root bin
+f usr/include/netsmb/smbfs_api.h 0644 root bin
+d usr/include/nfs 0755 root bin
+f usr/include/nfs/auth.h 0644 root bin
+f usr/include/nfs/export.h 0644 root bin
+f usr/include/nfs/lm.h 0644 root bin
+f usr/include/nfs/mapid.h 0644 root bin
+f usr/include/nfs/mount.h 0644 root bin
+f usr/include/nfs/nfs.h 0644 root bin
+f usr/include/nfs/nfs4.h 0644 root bin
+f usr/include/nfs/nfs4_attr.h 0644 root bin
+f usr/include/nfs/nfs4_clnt.h 0644 root bin
+f usr/include/nfs/nfs4_db_impl.h 0644 root bin
+f usr/include/nfs/nfs4_idmap_impl.h 0644 root bin
+f usr/include/nfs/nfs4_kprot.h 0644 root bin
+f usr/include/nfs/nfs_acl.h 0644 root bin
+f usr/include/nfs/nfs_clnt.h 0644 root bin
+f usr/include/nfs/nfs_cmd.h 0644 root bin
+f usr/include/nfs/nfs_log.h 0644 root bin
+f usr/include/nfs/nfs_sec.h 0644 root bin
+f usr/include/nfs/nfsid_map.h 0644 root bin
+f usr/include/nfs/nfssys.h 0644 root bin
+f usr/include/nfs/rnode.h 0644 root bin
+f usr/include/nfs/rnode4.h 0644 root bin
+f usr/include/nl_types.h 0644 root bin
+f usr/include/nlist.h 0644 root bin
+f usr/include/note.h 0644 root bin
+f usr/include/nss.h 0644 root bin
+f usr/include/nss_common.h 0644 root bin
+f usr/include/nss_dbdefs.h 0644 root bin
+f usr/include/nss_netdir.h 0644 root bin
+f usr/include/nsswitch.h 0644 root bin
+f usr/include/nvfru.h 0644 root bin
+f usr/include/ofmt.h 0644 root bin
+f usr/include/panel.h 0644 root bin
+f usr/include/passwdutil.h 0644 root bin
+f usr/include/paths.h 0644 root bin
+f usr/include/pcsample.h 0644 root bin
+f usr/include/pfmt.h 0644 root bin
+f usr/include/picl.h 0644 root bin
+f usr/include/picltree.h 0644 root bin
+f usr/include/pkgdev.h 0644 root bin
+f usr/include/pkginfo.h 0644 root bin
+f usr/include/pkglocs.h 0644 root bin
+f usr/include/pkgstrct.h 0644 root bin
+f usr/include/pkgtrans.h 0644 root bin
+f usr/include/poll.h 0644 root bin
+f usr/include/pool.h 0644 root bin
+f usr/include/port.h 0644 root bin
+f usr/include/priv.h 0644 root bin
+f usr/include/priv_utils.h 0644 root bin
+f usr/include/proc_service.h 0644 root bin
+f usr/include/procfs.h 0644 root bin
+f usr/include/prof.h 0644 root bin
+f usr/include/prof_attr.h 0644 root bin
+f usr/include/project.h 0644 root bin
+d usr/include/protocols 0755 root bin
+f usr/include/protocols/dumprestore.h 0644 root bin
+f usr/include/protocols/ripngd.h 0644 root bin
+f usr/include/protocols/routed.h 0644 root bin
+f usr/include/protocols/rwhod.h 0644 root bin
+f usr/include/protocols/timed.h 0644 root bin
+f usr/include/pthread.h 0644 root bin
+f usr/include/pw.h 0644 root bin
+f usr/include/pwd.h 0644 root bin
+f usr/include/raidcfg.h 0644 root bin
+f usr/include/raidcfg_spi.h 0644 root bin
+f usr/include/rcm_module.h 0644 root bin
+f usr/include/rctl.h 0644 root bin
+f usr/include/re_comp.h 0644 root bin
+f usr/include/regex.h 0644 root bin
+f usr/include/regexp.h 0644 root bin
+f usr/include/regexpr.h 0644 root bin
+f usr/include/resolv.h 0644 root bin
+f usr/include/rje.h 0644 root bin
+f usr/include/rp_plugin.h 0644 root bin
+d usr/include/rpc 0755 root bin
+f usr/include/rpc/auth.h 0644 root bin
+f usr/include/rpc/auth_des.h 0644 root bin
+f usr/include/rpc/auth_sys.h 0644 root bin
+f usr/include/rpc/auth_unix.h 0644 root bin
+f usr/include/rpc/bootparam.h 0644 root bin
+f usr/include/rpc/clnt.h 0644 root bin
+f usr/include/rpc/clnt_soc.h 0644 root bin
+f usr/include/rpc/clnt_stat.h 0644 root bin
+f usr/include/rpc/des_crypt.h 0644 root bin
+f usr/include/rpc/key_prot.h 0644 root bin
+f usr/include/rpc/nettype.h 0644 root bin
+f usr/include/rpc/pmap_clnt.h 0644 root bin
+f usr/include/rpc/pmap_prot.h 0644 root bin
+f usr/include/rpc/pmap_prot.x 0644 root bin
+f usr/include/rpc/pmap_rmt.h 0644 root bin
+f usr/include/rpc/rpc.h 0644 root bin
+f usr/include/rpc/rpc_com.h 0644 root bin
+f usr/include/rpc/rpc_msg.h 0644 root bin
+f usr/include/rpc/rpc_rdma.h 0644 root bin
+f usr/include/rpc/rpc_sztypes.h 0644 root bin
+f usr/include/rpc/rpcb_clnt.h 0644 root bin
+f usr/include/rpc/rpcb_prot.h 0644 root bin
+f usr/include/rpc/rpcb_prot.x 0644 root bin
+f usr/include/rpc/rpcent.h 0644 root bin
+f usr/include/rpc/rpcsec_gss.h 0644 root bin
+f usr/include/rpc/rpcsys.h 0644 root bin
+f usr/include/rpc/svc.h 0644 root bin
+f usr/include/rpc/svc_auth.h 0644 root bin
+f usr/include/rpc/svc_mt.h 0644 root bin
+f usr/include/rpc/svc_soc.h 0644 root bin
+f usr/include/rpc/types.h 0644 root bin
+f usr/include/rpc/xdr.h 0644 root bin
+d usr/include/rpcsvc 0755 root bin
+f usr/include/rpcsvc/autofs_prot.h 0644 root bin
+f usr/include/rpcsvc/autofs_prot.x 0644 root bin
+f usr/include/rpcsvc/bootparam.h 0644 root bin
+f usr/include/rpcsvc/bootparam_prot.h 0644 root bin
+f usr/include/rpcsvc/bootparam_prot.x 0644 root bin
+f usr/include/rpcsvc/daemon_utils.h 0644 root bin
+f usr/include/rpcsvc/dbm.h 0644 root bin
+f usr/include/rpcsvc/idmap_prot.h 0644 root bin
+f usr/include/rpcsvc/idmap_prot.x 0644 root bin
+f usr/include/rpcsvc/key_prot.x 0644 root bin
+f usr/include/rpcsvc/mount.h 0644 root bin
+f usr/include/rpcsvc/mount.x 0644 root bin
+f usr/include/rpcsvc/nfs4_prot.h 0644 root bin
+f usr/include/rpcsvc/nfs4_prot.x 0644 root bin
+f usr/include/rpcsvc/nfs_acl.h 0644 root bin
+f usr/include/rpcsvc/nfs_acl.x 0644 root bin
+f usr/include/rpcsvc/nfs_prot.h 0644 root bin
+f usr/include/rpcsvc/nfs_prot.x 0644 root bin
+f usr/include/rpcsvc/nis.h 0644 root bin
+f usr/include/rpcsvc/nis.x 0644 root bin
+f usr/include/rpcsvc/nis_db.h 0644 root bin
+f usr/include/rpcsvc/nis_object.x 0644 root bin
+f usr/include/rpcsvc/nislib.h 0644 root bin
+f usr/include/rpcsvc/nlm_prot.h 0644 root bin
+f usr/include/rpcsvc/nlm_prot.x 0644 root bin
+f usr/include/rpcsvc/nsm_addr.h 0644 root bin
+f usr/include/rpcsvc/nsm_addr.x 0644 root bin
+f usr/include/rpcsvc/rpc_sztypes.h 0644 root bin
+f usr/include/rpcsvc/rpc_sztypes.x 0644 root bin
+f usr/include/rpcsvc/rquota.h 0644 root bin
+f usr/include/rpcsvc/rquota.x 0644 root bin
+f usr/include/rpcsvc/rstat.h 0644 root bin
+f usr/include/rpcsvc/rstat.x 0644 root bin
+f usr/include/rpcsvc/rusers.h 0644 root bin
+f usr/include/rpcsvc/rusers.x 0644 root bin
+f usr/include/rpcsvc/rwall.h 0644 root bin
+f usr/include/rpcsvc/rwall.x 0644 root bin
+f usr/include/rpcsvc/sm_inter.h 0644 root bin
+f usr/include/rpcsvc/sm_inter.x 0644 root bin
+f usr/include/rpcsvc/spray.h 0644 root bin
+f usr/include/rpcsvc/spray.x 0644 root bin
+f usr/include/rpcsvc/svc_dg_priv.h 0644 root bin
+f usr/include/rpcsvc/ufs_prot.h 0644 root bin
+f usr/include/rpcsvc/ufs_prot.x 0644 root bin
+f usr/include/rpcsvc/yp.x 0644 root bin
+f usr/include/rpcsvc/yp_prot.h 0644 root bin
+f usr/include/rpcsvc/ypclnt.h 0644 root bin
+f usr/include/rpcsvc/yppasswd.h 0644 root bin
+f usr/include/rpcsvc/ypupd.h 0644 root bin
+f usr/include/rsmapi.h 0644 root bin
+f usr/include/rtld_db.h 0644 root bin
+f usr/include/s_string.h 0644 root bin
+f usr/include/sac.h 0644 root bin
+d usr/include/sasl 0755 root bin
+f usr/include/sasl/prop.h 0644 root bin
+f usr/include/sasl/sasl.h 0644 root bin
+f usr/include/sasl/saslplug.h 0644 root bin
+f usr/include/sasl/saslutil.h 0644 root bin
+f usr/include/scfutil.h 0644 root bin
+f usr/include/sched.h 0644 root bin
+f usr/include/schedctl.h 0644 root bin
+d usr/include/scsi 0755 root bin
+f usr/include/scsi/libscsi.h 0644 root bin
+f usr/include/scsi/libses.h 0644 root bin
+f usr/include/scsi/libses_plugin.h 0644 root bin
+f usr/include/scsi/libsmp.h 0644 root bin
+f usr/include/scsi/libsmp_plugin.h 0644 root bin
+d usr/include/scsi/plugins 0755 root bin
+d usr/include/scsi/plugins/ses 0755 root bin
+d usr/include/scsi/plugins/ses/framework 0755 root bin
+f usr/include/scsi/plugins/ses/framework/libses.h 0644 root bin
+f usr/include/scsi/plugins/ses/framework/ses2.h 0644 root bin
+f usr/include/scsi/plugins/ses/framework/ses2_impl.h 0644 root bin
+d usr/include/scsi/plugins/ses/vendor 0755 root bin
+f usr/include/scsi/plugins/ses/vendor/sun.h 0644 root bin
+f usr/include/scsi/plugins/ses/vendor/sun_impl.h 0644 root bin
+d usr/include/scsi/plugins/smp 0755 root bin
+d usr/include/scsi/plugins/smp/engine 0755 root bin
+d usr/include/scsi/plugins/smp/framework 0755 root bin
+f usr/include/sdp.h 0644 root bin
+f usr/include/search.h 0644 root bin
+f usr/include/secdb.h 0644 root bin
+d usr/include/security 0755 root bin
+f usr/include/security/auditd.h 0644 root bin
+f usr/include/security/cryptoki.h 0644 root bin
+f usr/include/security/pam_appl.h 0644 root bin
+f usr/include/security/pam_impl.h 0644 root bin
+f usr/include/security/pam_modules.h 0644 root bin
+f usr/include/security/pkcs11.h 0644 root bin
+f usr/include/security/pkcs11f.h 0644 root bin
+f usr/include/security/pkcs11t.h 0644 root bin
+f usr/include/semaphore.h 0644 root bin
+f usr/include/setjmp.h 0644 root bin
+f usr/include/sgtty.h 0644 root bin
+f usr/include/sha1.h 0644 root bin
+f usr/include/sha2.h 0644 root bin
+f usr/include/shadow.h 0644 root bin
+d usr/include/sharefs 0755 root bin
+f usr/include/sharefs/share.h 0644 root bin
+f usr/include/sharefs/sharefs.h 0644 root bin
+f usr/include/sharefs/sharetab.h 0644 root bin
+f usr/include/siginfo.h 0644 root bin
+f usr/include/signal.h 0644 root bin
+f usr/include/sip.h 0644 root bin
+f usr/include/skein.h 0644 root bin
+f usr/include/slp.h 0644 root bin
+d usr/include/smb 0755 root bin
+f usr/include/smb/doserror.h 0644 root bin
+f usr/include/smb/lmerr.h 0644 root bin
+f usr/include/smb/nterror.h 0644 root bin
+f usr/include/smb/ntstatus.h 0644 root bin
+f usr/include/smbios.h 0644 root bin
+d usr/include/smbsrv 0755 root bin
+f usr/include/smbsrv/alloc.h 0644 root bin
+f usr/include/smbsrv/cp_unicode.h 0644 root bin
+f usr/include/smbsrv/cp_usascii.h 0644 root bin
+f usr/include/smbsrv/hash_table.h 0644 root bin
+f usr/include/smbsrv/libmlsvc.h 0644 root bin
+f usr/include/smbsrv/libsmb.h 0644 root bin
+f usr/include/smbsrv/libsmbns.h 0644 root bin
+f usr/include/smbsrv/mac_cifs.h 0644 root bin
+f usr/include/smbsrv/mailslot.h 0644 root bin
+f usr/include/smbsrv/mbuf.h 0644 root bin
+f usr/include/smbsrv/msgbuf.h 0644 root bin
+d usr/include/smbsrv/ndl 0755 root bin
+f usr/include/smbsrv/ndl/dssetup.ndl 0644 root bin
+f usr/include/smbsrv/ndl/eventlog.ndl 0644 root bin
+f usr/include/smbsrv/ndl/llsrpc.ndl 0644 root bin
+f usr/include/smbsrv/ndl/lsarpc.ndl 0644 root bin
+f usr/include/smbsrv/ndl/msgsvc.ndl 0644 root bin
+f usr/include/smbsrv/ndl/netdfs.ndl 0644 root bin
+f usr/include/smbsrv/ndl/netlogon.ndl 0644 root bin
+f usr/include/smbsrv/ndl/samrpc.ndl 0644 root bin
+f usr/include/smbsrv/ndl/security.ndl 0644 root bin
+f usr/include/smbsrv/ndl/spoolss.ndl 0644 root bin
+f usr/include/smbsrv/ndl/srvsvc.ndl 0644 root bin
+f usr/include/smbsrv/ndl/svcctl.ndl 0644 root bin
+f usr/include/smbsrv/ndl/winreg.ndl 0644 root bin
+f usr/include/smbsrv/netbios.h 0644 root bin
+f usr/include/smbsrv/netrauth.h 0644 root bin
+f usr/include/smbsrv/nmpipes.h 0644 root bin
+f usr/include/smbsrv/ntifs.h 0644 root bin
+f usr/include/smbsrv/ntlocale.h 0644 root bin
+f usr/include/smbsrv/smb.h 0644 root bin
+f usr/include/smbsrv/smb_dfs.h 0644 root bin
+f usr/include/smbsrv/smb_door.h 0644 root bin
+f usr/include/smbsrv/smb_fsops.h 0644 root bin
+f usr/include/smbsrv/smb_idmap.h 0644 root bin
+f usr/include/smbsrv/smb_inet.h 0644 root bin
+f usr/include/smbsrv/smb_ioctl.h 0644 root bin
+f usr/include/smbsrv/smb_kproto.h 0644 root bin
+f usr/include/smbsrv/smb_kstat.h 0644 root bin
+f usr/include/smbsrv/smb_ktypes.h 0644 root bin
+f usr/include/smbsrv/smb_privilege.h 0644 root bin
+f usr/include/smbsrv/smb_share.h 0644 root bin
+f usr/include/smbsrv/smb_sid.h 0644 root bin
+f usr/include/smbsrv/smb_token.h 0644 root bin
+f usr/include/smbsrv/smb_vops.h 0644 root bin
+f usr/include/smbsrv/smb_xdr.h 0644 root bin
+f usr/include/smbsrv/smbinfo.h 0644 root bin
+f usr/include/smbsrv/string.h 0644 root bin
+f usr/include/smbsrv/svrapi.h 0644 root bin
+f usr/include/smbsrv/winsvc.h 0644 root bin
+f usr/include/smhbaapi.h 0644 root bin
+f usr/include/spawn.h 0644 root bin
+f usr/include/stack_unwind.h 0644 root bin
+f usr/include/stdalign.h 0644 root bin
+f usr/include/stdarg.h 0644 root bin
+f usr/include/stdbool.h 0644 root bin
+f usr/include/stddef.h 0644 root bin
+f usr/include/stdint.h 0644 root bin
+f usr/include/stdio.h 0644 root bin
+f usr/include/stdio_ext.h 0644 root bin
+f usr/include/stdio_impl.h 0644 root bin
+f usr/include/stdio_tag.h 0644 root bin
+f usr/include/stdlib.h 0644 root bin
+f usr/include/stdnoreturn.h 0644 root bin
+f usr/include/storclass.h 0644 root bin
+f usr/include/stp_bpdu.h 0644 root bin
+f usr/include/stp_in.h 0644 root bin
+f usr/include/stp_vectors.h 0644 root bin
+f usr/include/string.h 0644 root bin
+f usr/include/strings.h 0644 root bin
+f usr/include/stropts.h 0644 root bin
+f usr/include/syms.h 0644 root bin
+f usr/include/synch.h 0644 root bin
+d usr/include/sys 0755 root bin
+d usr/include/sys/1394 0755 root bin
+f usr/include/sys/1394/cmd1394.h 0644 root bin
+f usr/include/sys/1394/id1394.h 0644 root bin
+f usr/include/sys/1394/ieee1212.h 0644 root bin
+f usr/include/sys/1394/ieee1394.h 0644 root bin
+f usr/include/sys/1394/ixl1394.h 0644 root bin
+f usr/include/sys/1394/s1394_impl.h 0644 root bin
+f usr/include/sys/1394/t1394.h 0644 root bin
+f usr/include/sys/acct.h 0644 root bin
+f usr/include/sys/acctctl.h 0644 root bin
+f usr/include/sys/acl.h 0644 root bin
+f usr/include/sys/acl_impl.h 0644 root bin
+f usr/include/sys/acpi_drv.h 0644 root bin
+f usr/include/sys/aggr.h 0644 root bin
+f usr/include/sys/aggr_impl.h 0644 root bin
+f usr/include/sys/aio.h 0644 root bin
+f usr/include/sys/aio_impl.h 0644 root bin
+f usr/include/sys/aio_req.h 0644 root bin
+f usr/include/sys/aiocb.h 0644 root bin
+f usr/include/sys/archsystm.h 0644 root bin
+f usr/include/sys/ascii.h 0644 root bin
+f usr/include/sys/asm_linkage.h 0644 root bin
+f usr/include/sys/asy.h 0644 root bin
+f usr/include/sys/asynch.h 0644 root bin
+f usr/include/sys/atomic.h 0644 root bin
+f usr/include/sys/attr.h 0644 root bin
+d usr/include/sys/audio 0755 root bin
+f usr/include/sys/audio.h 0644 root bin
+f usr/include/sys/audio/ac97.h 0644 root bin
+f usr/include/sys/audio/audio_common.h 0644 root bin
+f usr/include/sys/audio/audio_driver.h 0644 root bin
+f usr/include/sys/audio/audio_oss.h 0644 root bin
+f usr/include/sys/audio/g711.h 0644 root bin
+f usr/include/sys/audioio.h 0644 root bin
+f usr/include/sys/autoconf.h 0644 root bin
+f usr/include/sys/auxv.h 0644 root bin
+f usr/include/sys/auxv_386.h 0644 root bin
+f usr/include/sys/auxv_SPARC.h 0644 root bin
+d usr/include/sys/av 0755 root bin
+f usr/include/sys/av/iec61883.h 0644 root bin
+f usr/include/sys/avintr.h 0644 root bin
+f usr/include/sys/avl.h 0644 root bin
+f usr/include/sys/avl_impl.h 0644 root bin
+f usr/include/sys/bitext.h 0644 root bin
+f usr/include/sys/bitmap.h 0644 root bin
+f usr/include/sys/bitset.h 0644 root bin
+f usr/include/sys/bl.h 0644 root bin
+f usr/include/sys/blkdev.h 0644 root bin
+f usr/include/sys/bofi.h 0644 root bin
+f usr/include/sys/bofi_impl.h 0644 root bin
+f usr/include/sys/bootconf.h 0644 root bin
+f usr/include/sys/bootregs.h 0644 root bin
+f usr/include/sys/bootstat.h 0644 root bin
+f usr/include/sys/bootsvcs.h 0644 root bin
+f usr/include/sys/bpp_io.h 0644 root bin
+f usr/include/sys/brand.h 0644 root bin
+f usr/include/sys/buf.h 0644 root bin
+f usr/include/sys/bufmod.h 0644 root bin
+f usr/include/sys/bustypes.h 0644 root bin
+f usr/include/sys/byteorder.h 0644 root bin
+f usr/include/sys/callb.h 0644 root bin
+f usr/include/sys/callo.h 0644 root bin
+f usr/include/sys/cap_util.h 0644 root bin
+f usr/include/sys/ccompile.h 0644 root bin
+f usr/include/sys/cdio.h 0644 root bin
+f usr/include/sys/cis.h 0644 root bin
+f usr/include/sys/cis_handlers.h 0644 root bin
+f usr/include/sys/cis_protos.h 0644 root bin
+f usr/include/sys/cladm.h 0644 root bin
+f usr/include/sys/class.h 0644 root bin
+f usr/include/sys/clconf.h 0644 root bin
+f usr/include/sys/clock_impl.h 0644 root bin
+f usr/include/sys/cmlb.h 0644 root bin
+f usr/include/sys/cmn_err.h 0644 root bin
+f usr/include/sys/compress.h 0644 root bin
+f usr/include/sys/condvar.h 0644 root bin
+f usr/include/sys/condvar_impl.h 0644 root bin
+f usr/include/sys/conf.h 0644 root bin
+f usr/include/sys/consdev.h 0644 root bin
+f usr/include/sys/console.h 0644 root bin
+f usr/include/sys/consplat.h 0644 root bin
+f usr/include/sys/containerof.h 0644 root bin
+d usr/include/sys/contract 0755 root bin
+f usr/include/sys/contract.h 0644 root bin
+f usr/include/sys/contract/device.h 0644 root bin
+f usr/include/sys/contract/device_impl.h 0644 root bin
+f usr/include/sys/contract/process.h 0644 root bin
+f usr/include/sys/contract/process_impl.h 0644 root bin
+f usr/include/sys/contract_impl.h 0644 root bin
+f usr/include/sys/controlregs.h 0644 root bin
+f usr/include/sys/copyops.h 0644 root bin
+f usr/include/sys/core.h 0644 root bin
+f usr/include/sys/corectl.h 0644 root bin
+f usr/include/sys/cpc_impl.h 0644 root bin
+f usr/include/sys/cpc_pcbe.h 0644 root bin
+f usr/include/sys/cpr.h 0644 root bin
+f usr/include/sys/cpu.h 0644 root bin
+f usr/include/sys/cpu_uarray.h 0644 root bin
+f usr/include/sys/cpucaps.h 0644 root bin
+f usr/include/sys/cpucaps_impl.h 0644 root bin
+f usr/include/sys/cpupart.h 0644 root bin
+f usr/include/sys/cpuvar.h 0644 root bin
+f usr/include/sys/crc32.h 0644 root bin
+f usr/include/sys/cred.h 0644 root bin
+f usr/include/sys/cred_impl.h 0644 root bin
+f usr/include/sys/crtctl.h 0644 root bin
+f usr/include/sys/cryptmod.h 0644 root bin
+d usr/include/sys/crypto 0755 root bin
+f usr/include/sys/crypto/api.h 0644 root bin
+f usr/include/sys/crypto/common.h 0644 root bin
+f usr/include/sys/crypto/elfsign.h 0644 root bin
+f usr/include/sys/crypto/impl.h 0644 root bin
+f usr/include/sys/crypto/ioctl.h 0644 root bin
+f usr/include/sys/crypto/ioctladmin.h 0644 root bin
+f usr/include/sys/crypto/ops_impl.h 0644 root bin
+f usr/include/sys/crypto/sched_impl.h 0644 root bin
+f usr/include/sys/crypto/spi.h 0644 root bin
+f usr/include/sys/cs.h 0644 root bin
+f usr/include/sys/cs_priv.h 0644 root bin
+f usr/include/sys/cs_strings.h 0644 root bin
+f usr/include/sys/cs_stubs.h 0644 root bin
+f usr/include/sys/cs_types.h 0644 root bin
+f usr/include/sys/csiioctl.h 0644 root bin
+f usr/include/sys/ctf.h 0644 root bin
+f usr/include/sys/ctf_api.h 0644 root bin
+f usr/include/sys/ctfs.h 0644 root bin
+f usr/include/sys/ctfs_impl.h 0644 root bin
+f usr/include/sys/ctype.h 0644 root bin
+f usr/include/sys/cyclic.h 0644 root bin
+f usr/include/sys/cyclic_impl.h 0644 root bin
+f usr/include/sys/dacf.h 0644 root bin
+f usr/include/sys/dacf_impl.h 0644 root bin
+f usr/include/sys/damap.h 0644 root bin
+f usr/include/sys/damap_impl.h 0644 root bin
+f usr/include/sys/dc_ki.h 0644 root bin
+d usr/include/sys/dcam 0755 root bin
+f usr/include/sys/dcam/dcam1394_io.h 0644 root bin
+f usr/include/sys/ddi.h 0644 root bin
+f usr/include/sys/ddi_hp.h 0644 root bin
+f usr/include/sys/ddi_hp_impl.h 0644 root bin
+f usr/include/sys/ddi_impldefs.h 0644 root bin
+f usr/include/sys/ddi_implfuncs.h 0644 root bin
+f usr/include/sys/ddi_intr.h 0644 root bin
+f usr/include/sys/ddi_intr_impl.h 0644 root bin
+f usr/include/sys/ddi_isa.h 0644 root bin
+f usr/include/sys/ddi_obsolete.h 0644 root bin
+f usr/include/sys/ddi_periodic.h 0644 root bin
+f usr/include/sys/ddi_ufm.h 0644 root bin
+f usr/include/sys/ddi_ufm_impl.h 0644 root bin
+f usr/include/sys/ddidevmap.h 0644 root bin
+f usr/include/sys/ddidmareq.h 0644 root bin
+f usr/include/sys/ddifm.h 0644 root bin
+f usr/include/sys/ddifm_impl.h 0644 root bin
+f usr/include/sys/ddimapreq.h 0644 root bin
+f usr/include/sys/ddipropdefs.h 0644 root bin
+f usr/include/sys/dditypes.h 0644 root bin
+f usr/include/sys/debug.h 0644 root bin
+f usr/include/sys/debugreg.h 0644 root bin
+f usr/include/sys/des.h 0644 root bin
+f usr/include/sys/devcache.h 0644 root bin
+f usr/include/sys/devcache_impl.h 0644 root bin
+f usr/include/sys/devctl.h 0644 root bin
+f usr/include/sys/devfm.h 0644 root bin
+f usr/include/sys/devid_cache.h 0644 root bin
+f usr/include/sys/devinfo_impl.h 0644 root bin
+f usr/include/sys/devops.h 0644 root bin
+f usr/include/sys/devpolicy.h 0644 root bin
+f usr/include/sys/devpoll.h 0644 root bin
+f usr/include/sys/dirent.h 0644 root bin
+f usr/include/sys/disp.h 0644 root bin
+f usr/include/sys/dkbad.h 0644 root bin
+f usr/include/sys/dkio.h 0644 root bin
+f usr/include/sys/dklabel.h 0644 root bin
+d usr/include/sys/dktp 0755 root bin
+f usr/include/sys/dktp/altsctr.h 0644 root bin
+f usr/include/sys/dktp/bbh.h 0644 root bin
+f usr/include/sys/dktp/cm.h 0644 root bin
+f usr/include/sys/dktp/cmdev.h 0644 root bin
+f usr/include/sys/dktp/cmdk.h 0644 root bin
+f usr/include/sys/dktp/cmpkt.h 0644 root bin
+f usr/include/sys/dktp/controller.h 0644 root bin
+f usr/include/sys/dktp/dadev.h 0644 root bin
+f usr/include/sys/dktp/dadk.h 0644 root bin
+f usr/include/sys/dktp/dadkio.h 0644 root bin
+f usr/include/sys/dktp/fctypes.h 0644 root bin
+f usr/include/sys/dktp/fdisk.h 0644 root bin
+f usr/include/sys/dktp/flowctrl.h 0644 root bin
+f usr/include/sys/dktp/gda.h 0644 root bin
+f usr/include/sys/dktp/quetypes.h 0644 root bin
+f usr/include/sys/dktp/queue.h 0644 root bin
+f usr/include/sys/dktp/tgcom.h 0644 root bin
+f usr/include/sys/dktp/tgdk.h 0644 root bin
+f usr/include/sys/dl.h 0644 root bin
+f usr/include/sys/dld.h 0644 root bin
+f usr/include/sys/dld_impl.h 0644 root bin
+f usr/include/sys/dld_ioc.h 0644 root bin
+f usr/include/sys/dlpi.h 0644 root bin
+f usr/include/sys/dls.h 0644 root bin
+f usr/include/sys/dls_impl.h 0644 root bin
+f usr/include/sys/dls_mgmt.h 0644 root bin
+f usr/include/sys/dma_engine.h 0644 root bin
+f usr/include/sys/dma_i8237A.h 0644 root bin
+f usr/include/sys/dnlc.h 0644 root bin
+f usr/include/sys/door.h 0644 root bin
+f usr/include/sys/door_data.h 0644 root bin
+f usr/include/sys/door_impl.h 0644 root bin
+f usr/include/sys/dtrace.h 0644 root bin
+f usr/include/sys/dtrace_impl.h 0644 root bin
+f usr/include/sys/dumpadm.h 0644 root bin
+f usr/include/sys/dumphdr.h 0644 root bin
+f usr/include/sys/ecppio.h 0644 root bin
+f usr/include/sys/ecppreg.h 0644 root bin
+f usr/include/sys/ecppsys.h 0644 root bin
+f usr/include/sys/ecppvar.h 0644 root bin
+f usr/include/sys/edonr.h 0644 root bin
+f usr/include/sys/efi_partition.h 0644 root bin
+f usr/include/sys/elf.h 0644 root bin
+f usr/include/sys/elf_386.h 0644 root bin
+f usr/include/sys/elf_SPARC.h 0644 root bin
+f usr/include/sys/elf_amd64.h 0644 root bin
+f usr/include/sys/elf_notes.h 0644 root bin
+f usr/include/sys/elftypes.h 0644 root bin
+f usr/include/sys/emul64.h 0644 root bin
+f usr/include/sys/emul64cmd.h 0644 root bin
+f usr/include/sys/emul64var.h 0644 root bin
+f usr/include/sys/epm.h 0644 root bin
+f usr/include/sys/epoll.h 0644 root bin
+f usr/include/sys/errno.h 0644 root bin
+f usr/include/sys/errorq.h 0644 root bin
+f usr/include/sys/errorq_impl.h 0644 root bin
+f usr/include/sys/esunddi.h 0644 root bin
+f usr/include/sys/ethernet.h 0644 root bin
+f usr/include/sys/euc.h 0644 root bin
+f usr/include/sys/eucioctl.h 0644 root bin
+f usr/include/sys/eventfd.h 0644 root bin
+f usr/include/sys/exacct.h 0644 root bin
+f usr/include/sys/exacct_catalog.h 0644 root bin
+f usr/include/sys/exacct_impl.h 0644 root bin
+f usr/include/sys/exec.h 0644 root bin
+f usr/include/sys/exechdr.h 0644 root bin
+f usr/include/sys/extdirent.h 0644 root bin
+f usr/include/sys/fasttrap.h 0644 root bin
+f usr/include/sys/fasttrap_impl.h 0644 root bin
+f usr/include/sys/fasttrap_isa.h 0644 root bin
+f usr/include/sys/fault.h 0644 root bin
+f usr/include/sys/fbio.h 0644 root bin
+f usr/include/sys/fbuf.h 0644 root bin
+d usr/include/sys/fc4 0755 root bin
+f usr/include/sys/fc4/fc.h 0644 root bin
+f usr/include/sys/fc4/fc_transport.h 0644 root bin
+f usr/include/sys/fc4/fcal.h 0644 root bin
+f usr/include/sys/fc4/fcal_linkapp.h 0644 root bin
+f usr/include/sys/fc4/fcal_transport.h 0644 root bin
+f usr/include/sys/fc4/fcio.h 0644 root bin
+f usr/include/sys/fc4/fcp.h 0644 root bin
+f usr/include/sys/fc4/linkapp.h 0644 root bin
+f usr/include/sys/fcntl.h 0644 root bin
+f usr/include/sys/fct.h 0644 root bin
+f usr/include/sys/fct_defines.h 0644 root bin
+f usr/include/sys/fctio.h 0644 root bin
+f usr/include/sys/fd_debug.h 0644 root bin
+f usr/include/sys/fdbuffer.h 0644 root bin
+f usr/include/sys/fdc.h 0644 root bin
+f usr/include/sys/fdio.h 0644 root bin
+f usr/include/sys/fdmedia.h 0644 root bin
+f usr/include/sys/feature_tests.h 0644 root bin
+f usr/include/sys/fem.h 0644 root bin
+d usr/include/sys/fibre-channel 0755 root bin
+f usr/include/sys/fibre-channel/fc.h 0644 root bin
+f usr/include/sys/fibre-channel/fc_appif.h 0644 root bin
+f usr/include/sys/fibre-channel/fc_types.h 0644 root bin
+f usr/include/sys/fibre-channel/fcio.h 0644 root bin
+d usr/include/sys/fibre-channel/impl 0755 root bin
+f usr/include/sys/fibre-channel/impl/fc_error.h 0644 root bin
+f usr/include/sys/fibre-channel/impl/fcph.h 0644 root bin
+d usr/include/sys/fibre-channel/ulp 0755 root bin
+f usr/include/sys/fibre-channel/ulp/fcp_util.h 0644 root bin
+f usr/include/sys/fibre-channel/ulp/fcsm.h 0644 root bin
+f usr/include/sys/file.h 0644 root bin
+f usr/include/sys/filio.h 0644 root bin
+f usr/include/sys/firmload.h 0644 root bin
+f usr/include/sys/flock.h 0644 root bin
+f usr/include/sys/flock_impl.h 0644 root bin
+d usr/include/sys/fm 0755 root bin
+d usr/include/sys/fm/cpu 0755 root bin
+d usr/include/sys/fm/fs 0755 root bin
+f usr/include/sys/fm/fs/zfs.h 0644 root bin
+d usr/include/sys/fm/io 0755 root bin
+f usr/include/sys/fm/io/ddi.h 0644 root bin
+f usr/include/sys/fm/io/disk.h 0644 root bin
+f usr/include/sys/fm/io/opl_mc_fm.h 0644 root bin
+f usr/include/sys/fm/io/pci.h 0644 root bin
+f usr/include/sys/fm/io/scsi.h 0644 root bin
+f usr/include/sys/fm/io/sun4upci.h 0644 root bin
+f usr/include/sys/fm/protocol.h 0644 root bin
+f usr/include/sys/fm/util.h 0644 root bin
+f usr/include/sys/fork.h 0644 root bin
+f usr/include/sys/fp.h 0644 root bin
+f usr/include/sys/frame.h 0644 root bin
+f usr/include/sys/frameio.h 0644 root bin
+d usr/include/sys/fs 0755 root bin
+f usr/include/sys/fs/autofs.h 0644 root bin
+f usr/include/sys/fs/decomp.h 0644 root bin
+f usr/include/sys/fs/dv_node.h 0644 root bin
+f usr/include/sys/fs/fifonode.h 0644 root bin
+f usr/include/sys/fs/hsfs_isospec.h 0644 root bin
+f usr/include/sys/fs/hsfs_node.h 0644 root bin
+f usr/include/sys/fs/hsfs_rrip.h 0644 root bin
+f usr/include/sys/fs/hsfs_spec.h 0644 root bin
+f usr/include/sys/fs/hsfs_susp.h 0644 root bin
+f usr/include/sys/fs/hyprlofs.h 0644 root bin
+f usr/include/sys/fs/hyprlofs_info.h 0644 root bin
+f usr/include/sys/fs/lofs_info.h 0644 root bin
+f usr/include/sys/fs/lofs_node.h 0644 root bin
+f usr/include/sys/fs/mntdata.h 0644 root bin
+f usr/include/sys/fs/namenode.h 0644 root bin
+f usr/include/sys/fs/pc_dir.h 0644 root bin
+f usr/include/sys/fs/pc_fs.h 0644 root bin
+f usr/include/sys/fs/pc_label.h 0644 root bin
+f usr/include/sys/fs/pc_node.h 0644 root bin
+f usr/include/sys/fs/pxfs_ki.h 0644 root bin
+f usr/include/sys/fs/sdev_impl.h 0644 root bin
+f usr/include/sys/fs/sdev_plugin.h 0644 root bin
+f usr/include/sys/fs/snode.h 0644 root bin
+f usr/include/sys/fs/swapnode.h 0644 root bin
+f usr/include/sys/fs/tmp.h 0644 root bin
+f usr/include/sys/fs/tmpnode.h 0644 root bin
+f usr/include/sys/fs/udf_inode.h 0644 root bin
+f usr/include/sys/fs/udf_volume.h 0644 root bin
+f usr/include/sys/fs/ufs_acl.h 0644 root bin
+f usr/include/sys/fs/ufs_bio.h 0644 root bin
+f usr/include/sys/fs/ufs_filio.h 0644 root bin
+f usr/include/sys/fs/ufs_fs.h 0644 root bin
+f usr/include/sys/fs/ufs_fsdir.h 0644 root bin
+f usr/include/sys/fs/ufs_inode.h 0644 root bin
+f usr/include/sys/fs/ufs_lockfs.h 0644 root bin
+f usr/include/sys/fs/ufs_log.h 0644 root bin
+f usr/include/sys/fs/ufs_mount.h 0644 root bin
+f usr/include/sys/fs/ufs_panic.h 0644 root bin
+f usr/include/sys/fs/ufs_prot.h 0644 root bin
+f usr/include/sys/fs/ufs_quota.h 0644 root bin
+f usr/include/sys/fs/ufs_snap.h 0644 root bin
+f usr/include/sys/fs/ufs_trans.h 0644 root bin
+f usr/include/sys/fs/zfs.h 0644 root bin
+f usr/include/sys/fs/zut.h 0644 root bin
+f usr/include/sys/fs_reparse.h 0644 root bin
+f usr/include/sys/fs_subr.h 0644 root bin
+f usr/include/sys/fsid.h 0644 root bin
+f usr/include/sys/fss.h 0644 root bin
+f usr/include/sys/fssnap.h 0644 root bin
+f usr/include/sys/fssnap_if.h 0644 root bin
+f usr/include/sys/fsspriocntl.h 0644 root bin
+f usr/include/sys/fstyp.h 0644 root bin
+f usr/include/sys/ftrace.h 0644 root bin
+f usr/include/sys/fx.h 0644 root bin
+f usr/include/sys/fxpriocntl.h 0644 root bin
+f usr/include/sys/gfs.h 0644 root bin
+f usr/include/sys/gfx_private.h 0644 root bin
+f usr/include/sys/gld.h 0644 root bin
+f usr/include/sys/gldpriv.h 0644 root bin
+f usr/include/sys/group.h 0644 root bin
+f usr/include/sys/hdio.h 0644 root bin
+f usr/include/sys/hook.h 0644 root bin
+f usr/include/sys/hook_event.h 0644 root bin
+f usr/include/sys/hook_impl.h 0644 root bin
+d usr/include/sys/hotplug 0755 root bin
+f usr/include/sys/hotplug/hpcsvc.h 0644 root bin
+f usr/include/sys/hotplug/hpctrl.h 0644 root bin
+d usr/include/sys/hotplug/pci 0755 root bin
+f usr/include/sys/hotplug/pci/pcicfg.h 0644 root bin
+f usr/include/sys/hotplug/pci/pcihp.h 0644 root bin
+f usr/include/sys/hwconf.h 0644 root bin
+f usr/include/sys/hypervisor.h 0644 root bin
+f usr/include/sys/i8272A.h 0644 root bin
+f usr/include/sys/ia.h 0644 root bin
+f usr/include/sys/iapriocntl.h 0644 root bin
+d usr/include/sys/ib 0755 root bin
+d usr/include/sys/ib/adapters 0755 root bin
+d usr/include/sys/ib/adapters/hermon 0755 root bin
+f usr/include/sys/ib/adapters/hermon/hermon_ioctl.h 0644 root bin
+f usr/include/sys/ib/adapters/mlnx_umap.h 0644 root bin
+d usr/include/sys/ib/adapters/tavor 0755 root bin
+f usr/include/sys/ib/adapters/tavor/tavor_ioctl.h 0644 root bin
+d usr/include/sys/ib/clients 0755 root bin
+d usr/include/sys/ib/clients/ibd 0755 root bin
+f usr/include/sys/ib/clients/ibd/ibd.h 0644 root bin
+d usr/include/sys/ib/clients/of 0755 root bin
+f usr/include/sys/ib/clients/of/ofa_solaris.h 0644 root bin
+f usr/include/sys/ib/clients/of/ofed_kernel.h 0644 root bin
+d usr/include/sys/ib/clients/of/rdma 0755 root bin
+f usr/include/sys/ib/clients/of/rdma/ib_addr.h 0644 root bin
+f usr/include/sys/ib/clients/of/rdma/ib_user_mad.h 0644 root bin
+f usr/include/sys/ib/clients/of/rdma/ib_user_sa.h 0644 root bin
+f usr/include/sys/ib/clients/of/rdma/ib_user_verbs.h 0644 root bin
+f usr/include/sys/ib/clients/of/rdma/ib_verbs.h 0644 root bin
+f usr/include/sys/ib/clients/of/rdma/rdma_cm.h 0644 root bin
+f usr/include/sys/ib/clients/of/rdma/rdma_user_cm.h 0644 root bin
+d usr/include/sys/ib/clients/of/sol_ofs 0755 root bin
+f usr/include/sys/ib/clients/of/sol_ofs/sol_cma.h 0644 root bin
+f usr/include/sys/ib/clients/of/sol_ofs/sol_ib_cma.h 0644 root bin
+f usr/include/sys/ib/clients/of/sol_ofs/sol_kverb_impl.h 0644 root bin
+f usr/include/sys/ib/clients/of/sol_ofs/sol_ofs_common.h 0644 root bin
+d usr/include/sys/ib/clients/of/sol_ucma 0755 root bin
+f usr/include/sys/ib/clients/of/sol_ucma/sol_rdma_user_cm.h 0644 root bin
+f usr/include/sys/ib/clients/of/sol_ucma/sol_ucma.h 0644 root bin
+d usr/include/sys/ib/clients/of/sol_umad 0755 root bin
+f usr/include/sys/ib/clients/of/sol_umad/sol_umad.h 0644 root bin
+d usr/include/sys/ib/clients/of/sol_uverbs 0755 root bin
+f usr/include/sys/ib/clients/of/sol_uverbs/sol_uverbs.h 0644 root bin
+f usr/include/sys/ib/clients/of/sol_uverbs/sol_uverbs2ucma.h 0644 root bin
+f usr/include/sys/ib/clients/of/sol_uverbs/sol_uverbs_comp.h 0644 root bin
+f usr/include/sys/ib/clients/of/sol_uverbs/sol_uverbs_event.h 0644 root bin
+f usr/include/sys/ib/clients/of/sol_uverbs/sol_uverbs_hca.h 0644 root bin
+f usr/include/sys/ib/clients/of/sol_uverbs/sol_uverbs_qp.h 0644 root bin
+f usr/include/sys/ib/ib_pkt_hdrs.h 0644 root bin
+f usr/include/sys/ib/ib_types.h 0644 root bin
+d usr/include/sys/ib/ibnex 0755 root bin
+f usr/include/sys/ib/ibnex/ibnex_devctl.h 0644 root bin
+d usr/include/sys/ib/ibtl 0755 root bin
+f usr/include/sys/ib/ibtl/ibci.h 0644 root bin
+f usr/include/sys/ib/ibtl/ibti.h 0644 root bin
+f usr/include/sys/ib/ibtl/ibti_cm.h 0644 root bin
+f usr/include/sys/ib/ibtl/ibti_common.h 0644 root bin
+f usr/include/sys/ib/ibtl/ibtl_ci_types.h 0644 root bin
+f usr/include/sys/ib/ibtl/ibtl_status.h 0644 root bin
+f usr/include/sys/ib/ibtl/ibtl_types.h 0644 root bin
+f usr/include/sys/ib/ibtl/ibvti.h 0644 root bin
+d usr/include/sys/ib/ibtl/impl 0755 root bin
+f usr/include/sys/ib/ibtl/impl/ibtl_util.h 0644 root bin
+d usr/include/sys/ib/mgt 0755 root bin
+f usr/include/sys/ib/mgt/ib_dm_attr.h 0644 root bin
+f usr/include/sys/ib/mgt/ib_mad.h 0644 root bin
+d usr/include/sys/ib/mgt/ibmf 0755 root bin
+f usr/include/sys/ib/mgt/ibmf/ibmf.h 0644 root bin
+f usr/include/sys/ib/mgt/ibmf/ibmf_msg.h 0644 root bin
+f usr/include/sys/ib/mgt/ibmf/ibmf_saa.h 0644 root bin
+f usr/include/sys/ib/mgt/ibmf/ibmf_utils.h 0644 root bin
+f usr/include/sys/ib/mgt/sa_recs.h 0644 root bin
+f usr/include/sys/ib/mgt/sm_attr.h 0644 root bin
+f usr/include/sys/ibpart.h 0644 root bin
+f usr/include/sys/id32.h 0644 root bin
+f usr/include/sys/id_space.h 0644 root bin
+d usr/include/sys/idm 0755 root bin
+f usr/include/sys/idm/idm.h 0644 root bin
+f usr/include/sys/idm/idm_conn_sm.h 0644 root bin
+f usr/include/sys/idm/idm_impl.h 0644 root bin
+f usr/include/sys/idm/idm_so.h 0644 root bin
+f usr/include/sys/idm/idm_text.h 0644 root bin
+f usr/include/sys/idm/idm_transport.h 0644 root bin
+f usr/include/sys/idmap.h 0644 root bin
+f usr/include/sys/ieeefp.h 0644 root bin
+f usr/include/sys/inline.h 0644 root bin
+f usr/include/sys/inotify.h 0644 root bin
+f usr/include/sys/instance.h 0644 root bin
+f usr/include/sys/int_const.h 0644 root bin
+f usr/include/sys/int_fmtio.h 0644 root bin
+f usr/include/sys/int_limits.h 0644 root bin
+f usr/include/sys/int_types.h 0644 root bin
+f usr/include/sys/inttypes.h 0644 root bin
+f usr/include/sys/ioccom.h 0644 root bin
+f usr/include/sys/ioctl.h 0644 root bin
+f usr/include/sys/iommulib.h 0644 root bin
+f usr/include/sys/ipc.h 0644 root bin
+f usr/include/sys/ipc_impl.h 0644 root bin
+f usr/include/sys/ipc_rctl.h 0644 root bin
+f usr/include/sys/ipmi.h 0644 root bin
+f usr/include/sys/isa_defs.h 0644 root bin
+f usr/include/sys/iscsi_authclient.h 0644 root bin
+f usr/include/sys/iscsi_authclientglue.h 0644 root bin
+f usr/include/sys/iscsi_protocol.h 0644 root bin
+d usr/include/sys/iscsit 0755 root bin
+f usr/include/sys/iscsit/chap.h 0644 root bin
+f usr/include/sys/iscsit/iscsi_if.h 0644 root bin
+f usr/include/sys/iscsit/iscsit_common.h 0644 root bin
+f usr/include/sys/iscsit/isns_protocol.h 0644 root bin
+f usr/include/sys/iscsit/radius_packet.h 0644 root bin
+f usr/include/sys/iscsit/radius_protocol.h 0644 root bin
+d usr/include/sys/iso 0755 root bin
+f usr/include/sys/iso/signal_iso.h 0644 root bin
+f usr/include/sys/jioctl.h 0644 root bin
+f usr/include/sys/kbd.h 0644 root bin
+f usr/include/sys/kbdreg.h 0644 root bin
+f usr/include/sys/kbio.h 0644 root bin
+f usr/include/sys/kcpc.h 0644 root bin
+f usr/include/sys/kd.h 0644 root bin
+f usr/include/sys/kdi.h 0644 root bin
+f usr/include/sys/kdi_impl.h 0644 root bin
+f usr/include/sys/kdi_machimpl.h 0644 root bin
+f usr/include/sys/kdi_regs.h 0644 root bin
+f usr/include/sys/kiconv.h 0644 root bin
+f usr/include/sys/kiconv_big5_utf8.h 0644 root bin
+f usr/include/sys/kiconv_cck_common.h 0644 root bin
+f usr/include/sys/kiconv_cp950hkscs_utf8.h 0644 root bin
+f usr/include/sys/kiconv_emea1.h 0644 root bin
+f usr/include/sys/kiconv_emea2.h 0644 root bin
+f usr/include/sys/kiconv_euckr_utf8.h 0644 root bin
+f usr/include/sys/kiconv_euctw_utf8.h 0644 root bin
+f usr/include/sys/kiconv_gb18030_utf8.h 0644 root bin
+f usr/include/sys/kiconv_gb2312_utf8.h 0644 root bin
+f usr/include/sys/kiconv_hkscs_utf8.h 0644 root bin
+f usr/include/sys/kiconv_ja.h 0644 root bin
+f usr/include/sys/kiconv_ja_jis_to_unicode.h 0644 root bin
+f usr/include/sys/kiconv_ja_unicode_to_jis.h 0644 root bin
+f usr/include/sys/kiconv_ko.h 0644 root bin
+f usr/include/sys/kiconv_latin1.h 0644 root bin
+f usr/include/sys/kiconv_sc.h 0644 root bin
+f usr/include/sys/kiconv_tc.h 0644 root bin
+f usr/include/sys/kiconv_uhc_utf8.h 0644 root bin
+f usr/include/sys/kiconv_utf8_big5.h 0644 root bin
+f usr/include/sys/kiconv_utf8_cp950hkscs.h 0644 root bin
+f usr/include/sys/kiconv_utf8_euckr.h 0644 root bin
+f usr/include/sys/kiconv_utf8_euctw.h 0644 root bin
+f usr/include/sys/kiconv_utf8_gb18030.h 0644 root bin
+f usr/include/sys/kiconv_utf8_gb2312.h 0644 root bin
+f usr/include/sys/kiconv_utf8_hkscs.h 0644 root bin
+f usr/include/sys/kiconv_utf8_uhc.h 0644 root bin
+f usr/include/sys/kidmap.h 0644 root bin
+f usr/include/sys/klpd.h 0644 root bin
+f usr/include/sys/klwp.h 0644 root bin
+f usr/include/sys/kmdb.h 0644 root bin
+f usr/include/sys/kmem.h 0644 root bin
+f usr/include/sys/kmem_impl.h 0644 root bin
+f usr/include/sys/kobj.h 0644 root bin
+f usr/include/sys/kobj_impl.h 0644 root bin
+f usr/include/sys/ksocket.h 0644 root bin
+f usr/include/sys/kstat.h 0644 root bin
+f usr/include/sys/kstr.h 0644 root bin
+f usr/include/sys/ksyms.h 0644 root bin
+f usr/include/sys/ksynch.h 0644 root bin
+f usr/include/sys/ldterm.h 0644 root bin
+f usr/include/sys/lgrp.h 0644 root bin
+f usr/include/sys/lgrp_user.h 0644 root bin
+f usr/include/sys/libc_kernel.h 0644 root bin
+f usr/include/sys/libdevid.h 0644 root bin
+f usr/include/sys/limits.h 0644 root bin
+f usr/include/sys/link.h 0644 root bin
+f usr/include/sys/linker_set.h 0644 root bin
+f usr/include/sys/list.h 0644 root bin
+f usr/include/sys/list_impl.h 0644 root bin
+f usr/include/sys/llc1.h 0644 root bin
+f usr/include/sys/loadavg.h 0644 root bin
+f usr/include/sys/lock.h 0644 root bin
+f usr/include/sys/lockfs.h 0644 root bin
+f usr/include/sys/lockstat.h 0644 root bin
+f usr/include/sys/lofi.h 0644 root bin
+f usr/include/sys/log.h 0644 root bin
+f usr/include/sys/logindmux.h 0644 root bin
+f usr/include/sys/logindmux_impl.h 0644 root bin
+f usr/include/sys/lpif.h 0644 root bin
+f usr/include/sys/lwp.h 0644 root bin
+f usr/include/sys/lwp_timer_impl.h 0644 root bin
+f usr/include/sys/lwp_upimutex_impl.h 0644 root bin
+f usr/include/sys/mac.h 0644 root bin
+f usr/include/sys/mac_client.h 0644 root bin
+f usr/include/sys/mac_client_impl.h 0644 root bin
+f usr/include/sys/mac_ether.h 0644 root bin
+f usr/include/sys/mac_flow.h 0644 root bin
+f usr/include/sys/mac_flow_impl.h 0644 root bin
+f usr/include/sys/mac_impl.h 0644 root bin
+f usr/include/sys/mac_provider.h 0644 root bin
+f usr/include/sys/mac_soft_ring.h 0644 root bin
+f usr/include/sys/mac_stat.h 0644 root bin
+f usr/include/sys/machelf.h 0644 root bin
+f usr/include/sys/machlock.h 0644 root bin
+f usr/include/sys/machsig.h 0644 root bin
+f usr/include/sys/machtypes.h 0644 root bin
+f usr/include/sys/map.h 0644 root bin
+f usr/include/sys/mc.h 0644 root bin
+f usr/include/sys/mc_amd.h 0644 root bin
+f usr/include/sys/mc_intel.h 0644 root bin
+f usr/include/sys/mca_amd.h 0644 root bin
+f usr/include/sys/mca_x86.h 0644 root bin
+f usr/include/sys/mcontext.h 0644 root bin
+f usr/include/sys/md4.h 0644 root bin
+f usr/include/sys/md5.h 0644 root bin
+f usr/include/sys/md5_consts.h 0644 root bin
+f usr/include/sys/mdb_modapi.h 0644 root bin
+f usr/include/sys/mdi_impldefs.h 0644 root bin
+f usr/include/sys/mem.h 0644 root bin
+f usr/include/sys/mem_config.h 0644 root bin
+f usr/include/sys/memlist.h 0644 root bin
+f usr/include/sys/mhd.h 0644 root bin
+f usr/include/sys/mii.h 0644 root bin
+f usr/include/sys/miiregs.h 0644 root bin
+f usr/include/sys/mixer.h 0644 root bin
+f usr/include/sys/mkdev.h 0644 root bin
+f usr/include/sys/mman.h 0644 root bin
+f usr/include/sys/mmapobj.h 0644 root bin
+f usr/include/sys/mntent.h 0644 root bin
+f usr/include/sys/mntio.h 0644 root bin
+f usr/include/sys/mnttab.h 0644 root bin
+f usr/include/sys/modctl.h 0644 root bin
+f usr/include/sys/mode.h 0644 root bin
+f usr/include/sys/model.h 0644 root bin
+f usr/include/sys/modhash.h 0644 root bin
+f usr/include/sys/modhash_impl.h 0644 root bin
+f usr/include/sys/mount.h 0644 root bin
+f usr/include/sys/mouse.h 0644 root bin
+f usr/include/sys/msacct.h 0644 root bin
+f usr/include/sys/msg.h 0644 root bin
+f usr/include/sys/msg_impl.h 0644 root bin
+f usr/include/sys/msio.h 0644 root bin
+f usr/include/sys/msreg.h 0644 root bin
+f usr/include/sys/mtio.h 0644 root bin
+f usr/include/sys/mutex.h 0644 root bin
+f usr/include/sys/mutex_impl.h 0644 root bin
+f usr/include/sys/nbmlock.h 0644 root bin
+f usr/include/sys/ndi_impldefs.h 0644 root bin
+f usr/include/sys/ndifm.h 0644 root bin
+f usr/include/sys/net80211.h 0644 root bin
+f usr/include/sys/net80211_crypto.h 0644 root bin
+f usr/include/sys/net80211_ht.h 0644 root bin
+f usr/include/sys/net80211_proto.h 0644 root bin
+f usr/include/sys/netconfig.h 0644 root bin
+f usr/include/sys/neti.h 0644 root bin
+f usr/include/sys/netstack.h 0644 root bin
+f usr/include/sys/nexusdefs.h 0644 root bin
+f usr/include/sys/note.h 0644 root bin
+f usr/include/sys/null.h 0644 root bin
+f usr/include/sys/nvpair.h 0644 root bin
+f usr/include/sys/nvpair_impl.h 0644 root bin
+f usr/include/sys/objfs.h 0644 root bin
+f usr/include/sys/objfs_impl.h 0644 root bin
+f usr/include/sys/obpdefs.h 0644 root bin
+f usr/include/sys/old_procfs.h 0644 root bin
+f usr/include/sys/ontrap.h 0644 root bin
+f usr/include/sys/open.h 0644 root bin
+f usr/include/sys/openpromio.h 0644 root bin
+f usr/include/sys/panic.h 0644 root bin
+f usr/include/sys/param.h 0644 root bin
+f usr/include/sys/pathconf.h 0644 root bin
+f usr/include/sys/pathname.h 0644 root bin
+f usr/include/sys/pattr.h 0644 root bin
+f usr/include/sys/pbio.h 0644 root bin
+f usr/include/sys/pcb.h 0644 root bin
+f usr/include/sys/pccard.h 0644 root bin
+f usr/include/sys/pci.h 0644 root bin
+f usr/include/sys/pci_impl.h 0644 root bin
+f usr/include/sys/pci_tools.h 0644 root bin
+f usr/include/sys/pcic_reg.h 0644 root bin
+f usr/include/sys/pcic_var.h 0644 root bin
+f usr/include/sys/pcie.h 0644 root bin
+f usr/include/sys/pcmcia.h 0644 root bin
+f usr/include/sys/pctypes.h 0644 root bin
+f usr/include/sys/pfmod.h 0644 root bin
+f usr/include/sys/pg.h 0644 root bin
+f usr/include/sys/pghw.h 0644 root bin
+f usr/include/sys/physmem.h 0644 root bin
+f usr/include/sys/pic.h 0644 root bin
+f usr/include/sys/pit.h 0644 root bin
+f usr/include/sys/pkp_hash.h 0644 root bin
+f usr/include/sys/pm.h 0644 root bin
+f usr/include/sys/pmem.h 0644 root bin
+f usr/include/sys/policy.h 0644 root bin
+f usr/include/sys/poll.h 0644 root bin
+f usr/include/sys/poll_impl.h 0644 root bin
+f usr/include/sys/pool.h 0644 root bin
+f usr/include/sys/pool_impl.h 0644 root bin
+f usr/include/sys/pool_pset.h 0644 root bin
+f usr/include/sys/port.h 0644 root bin
+f usr/include/sys/port_impl.h 0644 root bin
+f usr/include/sys/port_kernel.h 0644 root bin
+f usr/include/sys/portif.h 0644 root bin
+f usr/include/sys/ppmio.h 0644 root bin
+f usr/include/sys/pppt_ic_if.h 0644 root bin
+f usr/include/sys/pppt_ioctl.h 0644 root bin
+f usr/include/sys/priocntl.h 0644 root bin
+f usr/include/sys/priv.h 0644 root bin
+f usr/include/sys/priv_const.h 0644 root bin
+f usr/include/sys/priv_impl.h 0644 root bin
+f usr/include/sys/priv_names.h 0644 root bin
+f usr/include/sys/privmregs.h 0644 root bin
+f usr/include/sys/privregs.h 0644 root bin
+f usr/include/sys/prnio.h 0644 root bin
+d usr/include/sys/proc 0755 root bin
+f usr/include/sys/proc.h 0644 root bin
+f usr/include/sys/proc/prdata.h 0644 root bin
+f usr/include/sys/processor.h 0644 root bin
+f usr/include/sys/procfs.h 0644 root bin
+f usr/include/sys/procfs_isa.h 0644 root bin
+f usr/include/sys/procset.h 0644 root bin
+f usr/include/sys/project.h 0644 root bin
+f usr/include/sys/prom_emul.h 0644 root bin
+f usr/include/sys/prom_isa.h 0644 root bin
+f usr/include/sys/prom_plat.h 0644 root bin
+f usr/include/sys/promif.h 0644 root bin
+f usr/include/sys/promimpl.h 0644 root bin
+f usr/include/sys/protosw.h 0644 root bin
+f usr/include/sys/prsystm.h 0644 root bin
+f usr/include/sys/pset.h 0644 root bin
+f usr/include/sys/pshot.h 0644 root bin
+f usr/include/sys/psw.h 0644 root bin
+f usr/include/sys/pte.h 0644 root bin
+f usr/include/sys/ptem.h 0644 root bin
+f usr/include/sys/ptms.h 0644 root bin
+f usr/include/sys/ptyvar.h 0644 root bin
+f usr/include/sys/queue.h 0644 root bin
+f usr/include/sys/raidioctl.h 0644 root bin
+f usr/include/sys/ramdisk.h 0644 root bin
+f usr/include/sys/random.h 0644 root bin
+f usr/include/sys/rctl.h 0644 root bin
+f usr/include/sys/rctl_impl.h 0644 root bin
+f usr/include/sys/rds.h 0644 root bin
+f usr/include/sys/reboot.h 0644 root bin
+f usr/include/sys/refstr.h 0644 root bin
+f usr/include/sys/refstr_impl.h 0644 root bin
+f usr/include/sys/reg.h 0644 root bin
+f usr/include/sys/regset.h 0644 root bin
+f usr/include/sys/resource.h 0644 root bin
+f usr/include/sys/rlioctl.h 0644 root bin
+d usr/include/sys/rsm 0755 root bin
+f usr/include/sys/rsm/rsm.h 0644 root bin
+f usr/include/sys/rsm/rsm_common.h 0644 root bin
+f usr/include/sys/rsm/rsmapi_common.h 0644 root bin
+f usr/include/sys/rsm/rsmka_path_int.h 0644 root bin
+f usr/include/sys/rsm/rsmndi.h 0644 root bin
+f usr/include/sys/rsm/rsmpi.h 0644 root bin
+f usr/include/sys/rsm/rsmpi_driver.h 0644 root bin
+f usr/include/sys/rt.h 0644 root bin
+f usr/include/sys/rtc.h 0644 root bin
+f usr/include/sys/rtpriocntl.h 0644 root bin
+f usr/include/sys/rwlock.h 0644 root bin
+f usr/include/sys/rwlock_impl.h 0644 root bin
+f usr/include/sys/rwstlock.h 0644 root bin
+f usr/include/sys/sad.h 0644 root bin
+d usr/include/sys/sata 0755 root bin
+f usr/include/sys/sata/sata_cfgadm.h 0644 root bin
+f usr/include/sys/sata/sata_defs.h 0644 root bin
+f usr/include/sys/sata/sata_hba.h 0644 root bin
+f usr/include/sys/schedctl.h 0644 root bin
+d usr/include/sys/scsi 0755 root bin
+d usr/include/sys/scsi/adapters 0755 root bin
+f usr/include/sys/scsi/adapters/iscsi_door.h 0644 root bin
+f usr/include/sys/scsi/adapters/iscsi_if.h 0644 root bin
+f usr/include/sys/scsi/adapters/mpapi_impl.h 0644 root bin
+f usr/include/sys/scsi/adapters/mpapi_scsi_vhci.h 0644 root bin
+f usr/include/sys/scsi/adapters/scsi_vhci.h 0644 root bin
+d usr/include/sys/scsi/conf 0755 root bin
+f usr/include/sys/scsi/conf/autoconf.h 0644 root bin
+f usr/include/sys/scsi/conf/device.h 0644 root bin
+d usr/include/sys/scsi/generic 0755 root bin
+f usr/include/sys/scsi/generic/commands.h 0644 root bin
+f usr/include/sys/scsi/generic/dad_mode.h 0644 root bin
+f usr/include/sys/scsi/generic/inquiry.h 0644 root bin
+f usr/include/sys/scsi/generic/message.h 0644 root bin
+f usr/include/sys/scsi/generic/mode.h 0644 root bin
+f usr/include/sys/scsi/generic/persist.h 0644 root bin
+f usr/include/sys/scsi/generic/sense.h 0644 root bin
+f usr/include/sys/scsi/generic/sff_frames.h 0644 root bin
+f usr/include/sys/scsi/generic/smp_frames.h 0644 root bin
+f usr/include/sys/scsi/generic/status.h 0644 root bin
+d usr/include/sys/scsi/impl 0755 root bin
+f usr/include/sys/scsi/impl/commands.h 0644 root bin
+f usr/include/sys/scsi/impl/inquiry.h 0644 root bin
+f usr/include/sys/scsi/impl/mode.h 0644 root bin
+f usr/include/sys/scsi/impl/scsi_reset_notify.h 0644 root bin
+f usr/include/sys/scsi/impl/scsi_sas.h 0644 root bin
+f usr/include/sys/scsi/impl/sense.h 0644 root bin
+f usr/include/sys/scsi/impl/services.h 0644 root bin
+f usr/include/sys/scsi/impl/smp_transport.h 0644 root bin
+f usr/include/sys/scsi/impl/spc3_types.h 0644 root bin
+f usr/include/sys/scsi/impl/status.h 0644 root bin
+f usr/include/sys/scsi/impl/transport.h 0644 root bin
+f usr/include/sys/scsi/impl/types.h 0644 root bin
+f usr/include/sys/scsi/impl/uscsi.h 0644 root bin
+f usr/include/sys/scsi/impl/usmp.h 0644 root bin
+f usr/include/sys/scsi/scsi.h 0644 root bin
+f usr/include/sys/scsi/scsi_address.h 0644 root bin
+f usr/include/sys/scsi/scsi_ctl.h 0644 root bin
+f usr/include/sys/scsi/scsi_fm.h 0644 root bin
+f usr/include/sys/scsi/scsi_names.h 0644 root bin
+f usr/include/sys/scsi/scsi_params.h 0644 root bin
+f usr/include/sys/scsi/scsi_pkt.h 0644 root bin
+f usr/include/sys/scsi/scsi_resource.h 0644 root bin
+f usr/include/sys/scsi/scsi_types.h 0644 root bin
+f usr/include/sys/scsi/scsi_watch.h 0644 root bin
+d usr/include/sys/scsi/targets 0755 root bin
+f usr/include/sys/scsi/targets/sddef.h 0644 root bin
+f usr/include/sys/scsi/targets/ses.h 0644 root bin
+f usr/include/sys/scsi/targets/sesio.h 0644 root bin
+f usr/include/sys/scsi/targets/sgendef.h 0644 root bin
+f usr/include/sys/scsi/targets/smp.h 0644 root bin
+f usr/include/sys/scsi/targets/stdef.h 0644 root bin
+d usr/include/sys/sdcard 0755 root bin
+f usr/include/sys/sdcard/sda.h 0644 root bin
+f usr/include/sys/sdcard/sda_impl.h 0644 root bin
+f usr/include/sys/sdcard/sda_ioctl.h 0644 root bin
+f usr/include/sys/sdt.h 0644 root bin
+f usr/include/sys/secflags.h 0644 root bin
+f usr/include/sys/segment.h 0644 root bin
+f usr/include/sys/segments.h 0644 root bin
+f usr/include/sys/select.h 0644 root bin
+f usr/include/sys/sem.h 0644 root bin
+f usr/include/sys/sem_impl.h 0644 root bin
+f usr/include/sys/sema_impl.h 0644 root bin
+f usr/include/sys/semaphore.h 0644 root bin
+f usr/include/sys/sendfile.h 0644 root bin
+f usr/include/sys/sensors.h 0644 root bin
+f usr/include/sys/ser_sync.h 0644 root bin
+f usr/include/sys/serializer.h 0644 root bin
+f usr/include/sys/session.h 0644 root bin
+f usr/include/sys/sha1.h 0644 root bin
+f usr/include/sys/sha1_consts.h 0644 root bin
+f usr/include/sys/sha2.h 0644 root bin
+f usr/include/sys/sha2_consts.h 0644 root bin
+f usr/include/sys/share.h 0644 root bin
+f usr/include/sys/shm.h 0644 root bin
+f usr/include/sys/shm_impl.h 0644 root bin
+f usr/include/sys/sid.h 0644 root bin
+f usr/include/sys/siginfo.h 0644 root bin
+f usr/include/sys/signal.h 0644 root bin
+f usr/include/sys/signalfd.h 0644 root bin
+f usr/include/sys/skein.h 0644 root bin
+f usr/include/sys/sleepq.h 0644 root bin
+f usr/include/sys/smbios.h 0644 root bin
+f usr/include/sys/smbios_impl.h 0644 root bin
+f usr/include/sys/smedia.h 0644 root bin
+f usr/include/sys/sobject.h 0644 root bin
+f usr/include/sys/socket.h 0644 root bin
+f usr/include/sys/socket_impl.h 0644 root bin
+f usr/include/sys/socket_proto.h 0644 root bin
+f usr/include/sys/socketvar.h 0644 root bin
+f usr/include/sys/sockfilter.h 0644 root bin
+f usr/include/sys/sockio.h 0644 root bin
+f usr/include/sys/soundcard.h 0644 root bin
+f usr/include/sys/spl.h 0644 root bin
+f usr/include/sys/squeue.h 0644 root bin
+f usr/include/sys/squeue_impl.h 0644 root bin
+f usr/include/sys/srn.h 0644 root bin
+f usr/include/sys/sservice.h 0644 root bin
+f usr/include/sys/stack.h 0644 root bin
+f usr/include/sys/stat.h 0644 root bin
+f usr/include/sys/stat_impl.h 0644 root bin
+f usr/include/sys/statfs.h 0644 root bin
+f usr/include/sys/statvfs.h 0644 root bin
+f usr/include/sys/stdbool.h 0644 root bin
+f usr/include/sys/stddef.h 0644 root bin
+f usr/include/sys/stdint.h 0644 root bin
+f usr/include/sys/stermio.h 0644 root bin
+f usr/include/sys/stmf.h 0644 root bin
+f usr/include/sys/stmf_defines.h 0644 root bin
+f usr/include/sys/stmf_ioctl.h 0644 root bin
+f usr/include/sys/stmf_sbd_ioctl.h 0644 root bin
+f usr/include/sys/stream.h 0644 root bin
+f usr/include/sys/strft.h 0644 root bin
+f usr/include/sys/strlog.h 0644 root bin
+f usr/include/sys/strmdep.h 0644 root bin
+f usr/include/sys/stropts.h 0644 root bin
+f usr/include/sys/strredir.h 0644 root bin
+f usr/include/sys/strstat.h 0644 root bin
+f usr/include/sys/strsubr.h 0644 root bin
+f usr/include/sys/strsun.h 0644 root bin
+f usr/include/sys/strtty.h 0644 root bin
+f usr/include/sys/sunddi.h 0644 root bin
+f usr/include/sys/sunldi.h 0644 root bin
+f usr/include/sys/sunldi_impl.h 0644 root bin
+f usr/include/sys/sunmdi.h 0644 root bin
+f usr/include/sys/sunndi.h 0644 root bin
+f usr/include/sys/sunpm.h 0644 root bin
+f usr/include/sys/suntpi.h 0644 root bin
+f usr/include/sys/suntty.h 0644 root bin
+f usr/include/sys/swap.h 0644 root bin
+f usr/include/sys/synch.h 0644 root bin
+f usr/include/sys/synch32.h 0644 root bin
+f usr/include/sys/syscall.h 0644 root bin
+f usr/include/sys/sysconf.h 0644 root bin
+f usr/include/sys/sysconfig.h 0644 root bin
+f usr/include/sys/sysconfig_impl.h 0644 root bin
+f usr/include/sys/sysdc.h 0644 root bin
+f usr/include/sys/sysdc_impl.h 0644 root bin
+d usr/include/sys/sysevent 0755 root bin
+f usr/include/sys/sysevent.h 0644 root bin
+f usr/include/sys/sysevent/datalink.h 0644 root bin
+f usr/include/sys/sysevent/dev.h 0644 root bin
+f usr/include/sys/sysevent/domain.h 0644 root bin
+f usr/include/sys/sysevent/dr.h 0644 root bin
+f usr/include/sys/sysevent/env.h 0644 root bin
+f usr/include/sys/sysevent/eventdefs.h 0644 root bin
+f usr/include/sys/sysevent/ipmp.h 0644 root bin
+f usr/include/sys/sysevent/pwrctl.h 0644 root bin
+f usr/include/sys/sysevent/vrrp.h 0644 root bin
+f usr/include/sys/sysevent_impl.h 0644 root bin
+f usr/include/sys/sysi86.h 0644 root bin
+f usr/include/sys/sysinfo.h 0644 root bin
+f usr/include/sys/syslog.h 0644 root bin
+f usr/include/sys/sysmacros.h 0644 root bin
+f usr/include/sys/sysmsg_impl.h 0644 root bin
+f usr/include/sys/systeminfo.h 0644 root bin
+f usr/include/sys/systm.h 0644 root bin
+f usr/include/sys/t_kuser.h 0644 root bin
+f usr/include/sys/t_lock.h 0644 root bin
+f usr/include/sys/task.h 0644 root bin
+f usr/include/sys/taskq.h 0644 root bin
+f usr/include/sys/taskq_impl.h 0644 root bin
+f usr/include/sys/telioctl.h 0644 root bin
+f usr/include/sys/termio.h 0644 root bin
+f usr/include/sys/termios.h 0644 root bin
+f usr/include/sys/termiox.h 0644 root bin
+f usr/include/sys/thread.h 0644 root bin
+f usr/include/sys/ticlts.h 0644 root bin
+f usr/include/sys/ticots.h 0644 root bin
+f usr/include/sys/ticotsord.h 0644 root bin
+f usr/include/sys/tihdr.h 0644 root bin
+f usr/include/sys/time.h 0644 root bin
+f usr/include/sys/time_impl.h 0644 root bin
+f usr/include/sys/time_std_impl.h 0644 root bin
+f usr/include/sys/timeb.h 0644 root bin
+f usr/include/sys/timer.h 0644 root bin
+f usr/include/sys/timerfd.h 0644 root bin
+f usr/include/sys/times.h 0644 root bin
+f usr/include/sys/timex.h 0644 root bin
+f usr/include/sys/timod.h 0644 root bin
+f usr/include/sys/tirdwr.h 0644 root bin
+f usr/include/sys/tiuser.h 0644 root bin
+f usr/include/sys/tl.h 0644 root bin
+f usr/include/sys/todio.h 0644 root bin
+f usr/include/sys/tpicommon.h 0644 root bin
+f usr/include/sys/trap.h 0644 root bin
+f usr/include/sys/traptrace.h 0644 root bin
+f usr/include/sys/ts.h 0644 root bin
+d usr/include/sys/tsol 0755 root bin
+f usr/include/sys/tsol/label.h 0644 root bin
+f usr/include/sys/tsol/label_macro.h 0644 root bin
+f usr/include/sys/tsol/priv.h 0644 root bin
+f usr/include/sys/tsol/tndb.h 0644 root bin
+f usr/include/sys/tsol/tsyscall.h 0644 root bin
+f usr/include/sys/tspriocntl.h 0644 root bin
+f usr/include/sys/tss.h 0644 root bin
+f usr/include/sys/ttcompat.h 0644 root bin
+f usr/include/sys/ttold.h 0644 root bin
+f usr/include/sys/tty.h 0644 root bin
+f usr/include/sys/ttychars.h 0644 root bin
+f usr/include/sys/ttydev.h 0644 root bin
+f usr/include/sys/tuneable.h 0644 root bin
+f usr/include/sys/turnstile.h 0644 root bin
+f usr/include/sys/types.h 0644 root bin
+f usr/include/sys/types32.h 0644 root bin
+f usr/include/sys/tzfile.h 0644 root bin
+f usr/include/sys/u8_textprep.h 0644 root bin
+f usr/include/sys/u8_textprep_data.h 0644 root bin
+f usr/include/sys/uadmin.h 0644 root bin
+f usr/include/sys/ucode.h 0644 root bin
+f usr/include/sys/ucontext.h 0644 root bin
+f usr/include/sys/ucred.h 0644 root bin
+f usr/include/sys/uio.h 0644 root bin
+f usr/include/sys/ulimit.h 0644 root bin
+f usr/include/sys/un.h 0644 root bin
+f usr/include/sys/unistd.h 0644 root bin
+d usr/include/sys/usb 0755 root bin
+d usr/include/sys/usb/clients 0755 root bin
+d usr/include/sys/usb/clients/audio 0755 root bin
+f usr/include/sys/usb/clients/audio/usb_audio.h 0644 root bin
+d usr/include/sys/usb/clients/ccid 0755 root bin
+f usr/include/sys/usb/clients/ccid/ccid.h 0644 root bin
+f usr/include/sys/usb/clients/ccid/uccid.h 0644 root bin
+d usr/include/sys/usb/clients/hid 0755 root bin
+f usr/include/sys/usb/clients/hid/hid.h 0644 root bin
+d usr/include/sys/usb/clients/mass_storage 0755 root bin
+f usr/include/sys/usb/clients/mass_storage/usb_bulkonly.h 0644 root bin
+f usr/include/sys/usb/clients/mass_storage/usb_cbi.h 0644 root bin
+d usr/include/sys/usb/clients/printer 0755 root bin
+f usr/include/sys/usb/clients/printer/usb_printer.h 0644 root bin
+d usr/include/sys/usb/clients/ugen 0755 root bin
+f usr/include/sys/usb/clients/ugen/usb_ugen.h 0644 root bin
+d usr/include/sys/usb/clients/usbcdc 0755 root bin
+f usr/include/sys/usb/clients/usbcdc/usb_cdc.h 0644 root bin
+d usr/include/sys/usb/clients/usbinput 0755 root bin
+d usr/include/sys/usb/clients/usbinput/usbwcm 0755 root bin
+f usr/include/sys/usb/clients/usbinput/usbwcm/usbwcm.h 0644 root bin
+d usr/include/sys/usb/clients/video 0755 root bin
+d usr/include/sys/usb/clients/video/usbvc 0755 root bin
+f usr/include/sys/usb/clients/video/usbvc/usbvc.h 0644 root bin
+d usr/include/sys/usb/hubd 0755 root bin
+f usr/include/sys/usb/hubd/hub.h 0644 root bin
+f usr/include/sys/usb/hubd/hubd_impl.h 0644 root bin
+f usr/include/sys/usb/usba.h 0644 root bin
+f usr/include/sys/usb/usbai.h 0644 root bin
+f usr/include/sys/usb/usbdevs.h 0644 root bin
+f usr/include/sys/user.h 0644 root bin
+f usr/include/sys/ustat.h 0644 root bin
+f usr/include/sys/utime.h 0644 root bin
+f usr/include/sys/utrap.h 0644 root bin
+f usr/include/sys/utsname.h 0644 root bin
+f usr/include/sys/utssys.h 0644 root bin
+f usr/include/sys/uuid.h 0644 root bin
+f usr/include/sys/va_impl.h 0644 root bin
+f usr/include/sys/va_list.h 0644 root bin
+f usr/include/sys/var.h 0644 root bin
+f usr/include/sys/varargs.h 0644 root bin
+f usr/include/sys/vfs.h 0644 root bin
+f usr/include/sys/vfs_opreg.h 0644 root bin
+f usr/include/sys/vfstab.h 0644 root bin
+f usr/include/sys/vgareg.h 0644 root bin
+f usr/include/sys/videodev2.h 0644 root bin
+f usr/include/sys/vio9p.h 0644 root bin
+f usr/include/sys/visual_io.h 0644 root bin
+f usr/include/sys/vlan.h 0644 root bin
+f usr/include/sys/vm.h 0644 root bin
+f usr/include/sys/vm_usage.h 0644 root bin
+f usr/include/sys/vmem.h 0644 root bin
+f usr/include/sys/vmem_impl.h 0644 root bin
+f usr/include/sys/vmem_impl_user.h 0644 root bin
+f usr/include/sys/vmparam.h 0644 root bin
+f usr/include/sys/vmsystm.h 0644 root bin
+f usr/include/sys/vnd.h 0644 root bin
+f usr/include/sys/vnd_errno.h 0644 root bin
+f usr/include/sys/vnic.h 0644 root bin
+f usr/include/sys/vnic_impl.h 0644 root bin
+f usr/include/sys/vnode.h 0644 root bin
+f usr/include/sys/vscan.h 0644 root bin
+f usr/include/sys/vt.h 0644 root bin
+f usr/include/sys/vtdaemon.h 0644 root bin
+f usr/include/sys/vtoc.h 0644 root bin
+f usr/include/sys/vtrace.h 0644 root bin
+f usr/include/sys/vuid_event.h 0644 root bin
+f usr/include/sys/vuid_queue.h 0644 root bin
+f usr/include/sys/vuid_state.h 0644 root bin
+f usr/include/sys/vuid_store.h 0644 root bin
+f usr/include/sys/vuid_wheel.h 0644 root bin
+f usr/include/sys/wait.h 0644 root bin
+f usr/include/sys/waitq.h 0644 root bin
+f usr/include/sys/watchpoint.h 0644 root bin
+f usr/include/sys/winlockio.h 0644 root bin
+f usr/include/sys/x86_archext.h 0644 root bin
+f usr/include/sys/xen_errno.h 0644 root bin
+f usr/include/sys/xti_inet.h 0644 root bin
+f usr/include/sys/xti_osi.h 0644 root bin
+f usr/include/sys/xti_xtiopt.h 0644 root bin
+f usr/include/sys/zcons.h 0644 root bin
+f usr/include/sys/zfd.h 0644 root bin
+f usr/include/sys/zmod.h 0644 root bin
+f usr/include/sys/zone.h 0644 root bin
+f usr/include/sysexits.h 0644 root bin
+f usr/include/syslog.h 0644 root bin
+f usr/include/tar.h 0644 root bin
+f usr/include/tcpd.h 0644 root bin
+f usr/include/term.h 0644 root bin
+f usr/include/termio.h 0644 root bin
+f usr/include/termios.h 0644 root bin
+f usr/include/tgmath.h 0644 root bin
+f usr/include/thread.h 0644 root bin
+f usr/include/thread_db.h 0644 root bin
+f usr/include/threads.h 0644 root bin
+f usr/include/time.h 0644 root bin
+f usr/include/tiuser.h 0644 root bin
+d usr/include/tsol 0755 root bin
+f usr/include/tsol/label.h 0644 root bin
+f usr/include/tzfile.h 0644 root bin
+f usr/include/uchar.h 0644 root bin
+f usr/include/ucontext.h 0644 root bin
+f usr/include/ucred.h 0644 root bin
+f usr/include/uid_stp.h 0644 root bin
+f usr/include/ulimit.h 0644 root bin
+f usr/include/umem.h 0644 root bin
+f usr/include/umem_impl.h 0644 root bin
+f usr/include/unctrl.h 0644 root bin
+f usr/include/unistd.h 0644 root bin
+f usr/include/upanic.h 0644 root bin
+f usr/include/user_attr.h 0644 root bin
+f usr/include/userdefs.h 0644 root bin
+f usr/include/ustat.h 0644 root bin
+f usr/include/utempter.h 0644 root bin
+f usr/include/utility.h 0644 root bin
+f usr/include/utime.h 0644 root bin
+f usr/include/utmp.h 0644 root bin
+f usr/include/utmpx.h 0644 root bin
+d usr/include/uuid 0755 root bin
+f usr/include/uuid/uuid.h 0644 root bin
+f usr/include/valtools.h 0644 root bin
+f usr/include/values.h 0644 root bin
+f usr/include/varargs.h 0644 root bin
+d usr/include/vm 0755 root bin
+f usr/include/vm/anon.h 0644 root bin
+f usr/include/vm/as.h 0644 root bin
+f usr/include/vm/faultcode.h 0644 root bin
+f usr/include/vm/hat.h 0644 root bin
+f usr/include/vm/kpm.h 0644 root bin
+f usr/include/vm/page.h 0644 root bin
+f usr/include/vm/pvn.h 0644 root bin
+f usr/include/vm/rm.h 0644 root bin
+f usr/include/vm/seg.h 0644 root bin
+f usr/include/vm/seg_dev.h 0644 root bin
+f usr/include/vm/seg_enum.h 0644 root bin
+f usr/include/vm/seg_kmem.h 0644 root bin
+f usr/include/vm/seg_kp.h 0644 root bin
+f usr/include/vm/seg_kpm.h 0644 root bin
+f usr/include/vm/seg_map.h 0644 root bin
+f usr/include/vm/seg_spt.h 0644 root bin
+f usr/include/vm/seg_vn.h 0644 root bin
+f usr/include/vm/vpage.h 0644 root bin
+f usr/include/vm/vpm.h 0644 root bin
+f usr/include/volmgt.h 0644 root bin
+f usr/include/wait.h 0644 root bin
+f usr/include/wchar.h 0644 root bin
+f usr/include/wchar_impl.h 0644 root bin
+f usr/include/wctype.h 0644 root bin
+f usr/include/widec.h 0644 root bin
+f usr/include/winscard.h 0644 root bin
+f usr/include/wintypes.h 0644 root bin
+f usr/include/wordexp.h 0644 root bin
+f usr/include/xlocale.h 0644 root bin
+f usr/include/xti.h 0644 root bin
+f usr/include/xti_inet.h 0644 root bin
+f usr/include/zdoor.h 0644 root bin
+f usr/include/zone.h 0644 root bin
+d usr/kernel 0755 root sys
+d usr/kernel/brand 0755 root sys
+d usr/kernel/brand/amd64 0755 root sys
+f usr/kernel/brand/amd64/lx_brand 0755 root sys
+d usr/kernel/drv 0755 root sys
+d usr/kernel/drv/amd64 0755 root sys
+f usr/kernel/drv/amd64/bpf 0755 root sys
+f usr/kernel/drv/amd64/dump 0755 root sys
+f usr/kernel/drv/amd64/eventfd 0755 root sys
+f usr/kernel/drv/amd64/fssnap 0755 root sys
+f usr/kernel/drv/amd64/inotify 0755 root sys
+f usr/kernel/drv/amd64/ipf 0755 root sys
+f usr/kernel/drv/amd64/ipmi 0755 root sys
+f usr/kernel/drv/amd64/kstat 0755 root sys
+f usr/kernel/drv/amd64/ksyms 0755 root sys
+f usr/kernel/drv/amd64/logindmux 0755 root sys
+f usr/kernel/drv/amd64/lx_ptm 0755 root sys
+f usr/kernel/drv/amd64/lx_systrace 0755 root sys
+f usr/kernel/drv/amd64/lxautofs 0755 root sys
+f usr/kernel/drv/amd64/nsmb 0755 root sys
+f usr/kernel/drv/amd64/pm 0755 root sys
+f usr/kernel/drv/amd64/pool 0755 root sys
+f usr/kernel/drv/amd64/ppt 0755 root sys
+f usr/kernel/drv/amd64/ptm 0755 root sys
+f usr/kernel/drv/amd64/pts 0755 root sys
+f usr/kernel/drv/amd64/signalfd 0755 root sys
+f usr/kernel/drv/amd64/smbsrv 0755 root sys
+f usr/kernel/drv/amd64/sppp 0755 root sys
+f usr/kernel/drv/amd64/sppptun 0755 root sys
+f usr/kernel/drv/amd64/timerfd 0755 root sys
+f usr/kernel/drv/amd64/viona 0755 root sys
+f usr/kernel/drv/amd64/vmm 0755 root sys
+f usr/kernel/drv/amd64/zcons 0755 root sys
+f usr/kernel/drv/amd64/zfd 0755 root sys
+f usr/kernel/drv/bpf.conf 0644 root sys
+f usr/kernel/drv/dump.conf 0644 root sys
+f usr/kernel/drv/eventfd.conf 0644 root sys
+f usr/kernel/drv/fssnap.conf 0644 root sys
+f usr/kernel/drv/inotify.conf 0644 root sys
+f usr/kernel/drv/ipf.conf 0644 root sys
+f usr/kernel/drv/ipmi.conf 0644 root sys
+f usr/kernel/drv/kstat.conf 0644 root sys
+f usr/kernel/drv/ksyms.conf 0644 root sys
+f usr/kernel/drv/logindmux.conf 0644 root sys
+f usr/kernel/drv/lx_ptm.conf 0644 root sys
+f usr/kernel/drv/lx_systrace.conf 0644 root sys
+f usr/kernel/drv/lxautofs.conf 0644 root sys
+f usr/kernel/drv/nsmb.conf 0644 root sys
+f usr/kernel/drv/pm.conf 0644 root sys
+f usr/kernel/drv/pool.conf 0644 root sys
+f usr/kernel/drv/ppt.conf 0644 root sys
+f usr/kernel/drv/ptm.conf 0644 root sys
+f usr/kernel/drv/pts.conf 0644 root sys
+f usr/kernel/drv/signalfd.conf 0644 root sys
+f usr/kernel/drv/smbsrv.conf 0644 root sys
+f usr/kernel/drv/sppp.conf 0644 root sys
+f usr/kernel/drv/sppptun.conf 0644 root sys
+f usr/kernel/drv/timerfd.conf 0644 root sys
+f usr/kernel/drv/viona.conf 0644 root sys
+f usr/kernel/drv/vmm.conf 0644 root sys
+d usr/kernel/fs 0755 root sys
+d usr/kernel/fs/amd64 0755 root sys
+f usr/kernel/fs/amd64/fdfs 0755 root sys
+f usr/kernel/fs/amd64/lx_cgroup 0755 root sys
+f usr/kernel/fs/amd64/lx_devfs 0755 root sys
+f usr/kernel/fs/amd64/lx_proc 0755 root sys
+f usr/kernel/fs/amd64/lx_sysfs 0755 root sys
+h usr/kernel/fs/amd64/lxautofs=usr/kernel/drv/amd64/lxautofs
+f usr/kernel/fs/amd64/pcfs 0755 root sys
+f usr/kernel/fs/amd64/smbfs 0755 root sys
+d usr/kernel/kmdb 0755 root sys
+d usr/kernel/kmdb/amd64 0755 root sys
+f usr/kernel/kmdb/amd64/smbfs 0555 root sys
+f usr/kernel/kmdb/amd64/smbsrv 0555 root sys
+d usr/kernel/misc 0755 root sys
+d usr/kernel/misc/amd64 0755 root sys
+f usr/kernel/misc/amd64/vmm_vtd 0755 root sys
+d usr/kernel/sched 0755 root sys
+d usr/kernel/sched/amd64 0755 root sys
+f usr/kernel/sched/amd64/FSS 0755 root sys
+f usr/kernel/sched/amd64/FX 0755 root sys
+f usr/kernel/sched/amd64/FX_DPTBL 0755 root sys
+f usr/kernel/sched/amd64/RT 0755 root sys
+f usr/kernel/sched/amd64/RT_DPTBL 0755 root sys
+d usr/kernel/socketmod 0755 root sys
+d usr/kernel/socketmod/amd64 0755 root sys
+f usr/kernel/socketmod/amd64/sockpfp 0755 root sys
+d usr/kernel/sys 0755 root sys
+d usr/kernel/sys/amd64 0755 root sys
+f usr/kernel/sys/amd64/acctctl 0755 root sys
+f usr/kernel/sys/amd64/exacctsys 0755 root sys
+f usr/kernel/sys/amd64/sysacct 0755 root sys
+d usr/kvm 0755 root bin
+f usr/kvm/README 0644 root sys
+d usr/lib 0755 root bin
+f usr/lib/0@0.so.1 0755 root bin
+s usr/lib/32=.
+s usr/lib/64=amd64
+d usr/lib/acct 0755 root bin
+f usr/lib/acct/acctcms 0555 root bin
+f usr/lib/acct/acctcon 0555 root bin
+f usr/lib/acct/acctcon1 0555 root bin
+f usr/lib/acct/acctcon2 0555 root bin
+f usr/lib/acct/acctdisk 0555 root bin
+f usr/lib/acct/acctdusg 0555 root bin
+f usr/lib/acct/acctmerg 0555 root bin
+f usr/lib/acct/accton 4755 root bin
+f usr/lib/acct/acctprc 0555 root bin
+f usr/lib/acct/acctprc1 0555 root bin
+f usr/lib/acct/acctprc2 0555 root bin
+f usr/lib/acct/acctwtmp 0555 root bin
+f usr/lib/acct/chargefee 0555 root bin
+f usr/lib/acct/ckpacct 0555 root bin
+f usr/lib/acct/closewtmp 0555 root bin
+f usr/lib/acct/dodisk 0555 root bin
+f usr/lib/acct/fwtmp 0555 root bin
+f usr/lib/acct/lastlogin 0555 root bin
+f usr/lib/acct/monacct 0555 root bin
+f usr/lib/acct/nulladm 0555 root bin
+f usr/lib/acct/prctmp 0555 root bin
+f usr/lib/acct/prdaily 0555 root bin
+f usr/lib/acct/prtacct 0555 root bin
+f usr/lib/acct/ptecms.awk 0555 root bin
+f usr/lib/acct/ptelus.awk 0555 root bin
+f usr/lib/acct/remove 0555 root bin
+f usr/lib/acct/runacct 0555 root bin
+f usr/lib/acct/shutacct 0555 root bin
+f usr/lib/acct/startup 0555 root bin
+f usr/lib/acct/turnacct 0555 root bin
+f usr/lib/acct/utmp2wtmp 0555 root bin
+f usr/lib/acct/wtmpfix 0555 root bin
+d usr/lib/adb 0755 root sys
+f usr/lib/adb/adbgen 0755 root sys
+f usr/lib/adb/adbgen1 0755 root sys
+f usr/lib/adb/adbgen3 0755 root sys
+f usr/lib/adb/adbgen4 0755 root sys
+f usr/lib/adb/adbsub.o 0644 root sys
+d usr/lib/adb/amd64 0755 root sys
+f usr/lib/adb/amd64/adbsub.o 0644 root sys
+d usr/lib/ahci 0755 root sys
+f usr/lib/ahci/ahciem 0555 root bin
+d usr/lib/amd64 0755 root bin
+f usr/lib/amd64/0@0.so.1 0755 root bin
+f usr/lib/amd64/crt1.o 0644 root bin
+f usr/lib/amd64/crti.o 0644 root bin
+f usr/lib/amd64/crtn.o 0644 root bin
+h usr/lib/amd64/gcrt1.o=usr/lib/amd64/crt1.o
+d usr/lib/amd64/gss 0755 root bin
+f usr/lib/amd64/gss/dh1024-0.so.1 0755 root bin
+s usr/lib/amd64/gss/dh1024-0.so=dh1024-0.so.1
+f usr/lib/amd64/gss/dh640-0.so.1 0755 root bin
+s usr/lib/amd64/gss/dh640-0.so=dh640-0.so.1
+s usr/lib/amd64/gss/libmech_krb5.so=mech_krb5.so.1
+f usr/lib/amd64/gss/mech_dh.so.1 0755 root bin
+s usr/lib/amd64/gss/mech_dh.so=mech_dh.so.1
+f usr/lib/amd64/gss/mech_krb5.so.1 0755 root bin
+s usr/lib/amd64/gss/mech_krb5.so=mech_krb5.so.1
+f usr/lib/amd64/gss/mech_spnego.so.1 0755 root bin
+s usr/lib/amd64/ld.so.1=../../../lib/amd64/ld.so.1
+f usr/lib/amd64/lddstub 0555 root bin
+f usr/lib/amd64/lib9p.so.1 0755 root bin
+f usr/lib/amd64/libHBAAPI.so.1 0755 root bin
+s usr/lib/amd64/libHBAAPI.so=libHBAAPI.so.1
+s usr/lib/amd64/libMPAPI.so.1=../../../lib/amd64/libMPAPI.so.1
+s usr/lib/amd64/libMPAPI.so=../../../lib/amd64/libMPAPI.so.1
+f usr/lib/amd64/libSMHBAAPI.so.1 0755 root bin
+s usr/lib/amd64/libSMHBAAPI.so=libSMHBAAPI.so.1
+s usr/lib/amd64/libadm.so.1=../../../lib/amd64/libadm.so.1
+s usr/lib/amd64/libadm.so=../../../lib/amd64/libadm.so.1
+f usr/lib/amd64/libads.so.1 0755 root bin
+s usr/lib/amd64/libads.so=libads.so.1
+f usr/lib/amd64/libadt_jni.so.1 0755 root bin
+s usr/lib/amd64/libadt_jni.so=libadt_jni.so.1
+f usr/lib/amd64/libadutils.so.1 0755 root bin
+s usr/lib/amd64/libadutils.so=libadutils.so.1
+s usr/lib/amd64/libaio.so.1=../../../lib/amd64/libaio.so.1
+s usr/lib/amd64/libaio.so=../../../lib/amd64/libaio.so.1
+f usr/lib/amd64/libast.so.1 0755 root bin
+s usr/lib/amd64/libast.so=libast.so.1
+s usr/lib/amd64/libavl.so.1=../../../lib/amd64/libavl.so.1
+s usr/lib/amd64/libavl.so=../../../lib/amd64/libavl.so.1
+f usr/lib/amd64/libbrand.so.1 0755 root bin
+s usr/lib/amd64/libbrand.so=libbrand.so.1
+f usr/lib/amd64/libbsdmalloc.so.1 0755 root bin
+s usr/lib/amd64/libbsdmalloc.so=libbsdmalloc.so.1
+s usr/lib/amd64/libbsm.so.1=../../../lib/amd64/libbsm.so.1
+s usr/lib/amd64/libbsm.so=../../../lib/amd64/libbsm.so.1
+f usr/lib/amd64/libbunyan.so.1 0755 root bin
+s usr/lib/amd64/libc.so.1=../../../lib/amd64/libc.so.1
+s usr/lib/amd64/libc.so=../../../lib/amd64/libc.so.1
+s usr/lib/amd64/libc_db.so.1=../../../lib/amd64/libc_db.so.1
+s usr/lib/amd64/libc_db.so=../../../lib/amd64/libc_db.so.1
+f usr/lib/amd64/libcfgadm.so.1 0755 root bin
+s usr/lib/amd64/libcfgadm.so=libcfgadm.so.1
+f usr/lib/amd64/libcmd.so.1 0755 root bin
+s usr/lib/amd64/libcmd.so=libcmd.so.1
+s usr/lib/amd64/libcmdutils.so.1=../../../lib/amd64/libcmdutils.so.1
+s usr/lib/amd64/libcmdutils.so=../../../lib/amd64/libcmdutils.so.1
+f usr/lib/amd64/libcommputil.so.1 0755 root bin
+s usr/lib/amd64/libcommputil.so=libcommputil.so.1
+s usr/lib/amd64/libcontract.so.1=../../../lib/amd64/libcontract.so.1
+s usr/lib/amd64/libcontract.so=../../../lib/amd64/libcontract.so.1
+f usr/lib/amd64/libcpc.so.1 0755 root bin
+s usr/lib/amd64/libcpc.so=libcpc.so.1
+f usr/lib/amd64/libcrle.so.1 0755 root bin
+f usr/lib/amd64/libcrypt.so.1 0755 root bin
+s usr/lib/amd64/libcrypt.so=libcrypt.so.1
+s usr/lib/amd64/libcrypt_d.so.1=libcrypt.so.1
+s usr/lib/amd64/libcrypt_d.so=libcrypt.so
+s usr/lib/amd64/libcrypt_i.so.1=libcrypt.so.1
+s usr/lib/amd64/libcrypt_i.so=libcrypt.so
+s usr/lib/amd64/libcryptoutil.so.1=../../../lib/amd64/libcryptoutil.so.1
+s usr/lib/amd64/libcryptoutil.so=../../../lib/amd64/libcryptoutil.so.1
+s usr/lib/amd64/libctf.so.1=../../../lib/amd64/libctf.so.1
+s usr/lib/amd64/libctf.so=../../../lib/amd64/libctf.so.1
+s usr/lib/amd64/libcurses.so.1=../../../lib/amd64/libcurses.so.1
+s usr/lib/amd64/libcurses.so=../../../lib/amd64/libcurses.so.1
+f usr/lib/amd64/libdat.so.1 0755 root bin
+s usr/lib/amd64/libdat.so=libdat.so.1
+f usr/lib/amd64/libdemangle-sys.so.1 0755 root bin
+s usr/lib/amd64/libdemangle-sys.so=libdemangle-sys.so.1
+s usr/lib/amd64/libdevice.so.1=../../../lib/amd64/libdevice.so.1
+s usr/lib/amd64/libdevice.so=../../../lib/amd64/libdevice.so.1
+s usr/lib/amd64/libdevid.so.1=../../../lib/amd64/libdevid.so.1
+s usr/lib/amd64/libdevid.so=../../../lib/amd64/libdevid.so.1
+s usr/lib/amd64/libdevinfo.so.1=../../../lib/amd64/libdevinfo.so.1
+s usr/lib/amd64/libdevinfo.so=../../../lib/amd64/libdevinfo.so.1
+f usr/lib/amd64/libdisasm.so.1 0755 root bin
+s usr/lib/amd64/libdisasm.so=libdisasm.so.1
+f usr/lib/amd64/libdiskmgt.so.1 0755 root bin
+s usr/lib/amd64/libdiskmgt.so=libdiskmgt.so.1
+s usr/lib/amd64/libdl.so.1=../../../lib/amd64/libdl.so.1
+s usr/lib/amd64/libdl.so=../../../lib/amd64/libdl.so.1
+s usr/lib/amd64/libdladm.so.1=../../../lib/amd64/libdladm.so.1
+s usr/lib/amd64/libdladm.so=../../../lib/amd64/libdladm.so.1
+f usr/lib/amd64/libdll.so.1 0755 root bin
+s usr/lib/amd64/libdll.so=libdll.so.1
+s usr/lib/amd64/libdlpi.so.1=../../../lib/amd64/libdlpi.so.1
+s usr/lib/amd64/libdlpi.so=../../../lib/amd64/libdlpi.so.1
+f usr/lib/amd64/libdns_sd.so.1 0755 root bin
+s usr/lib/amd64/libdns_sd.so=libdns_sd.so.1
+s usr/lib/amd64/libdoor.so.1=../../../lib/amd64/libdoor.so.1
+s usr/lib/amd64/libdoor.so=../../../lib/amd64/libdoor.so.1
+f usr/lib/amd64/libdtrace.so.1 0755 root bin
+s usr/lib/amd64/libdtrace.so=libdtrace.so.1
+f usr/lib/amd64/libdtrace_jni.so.1 0755 root bin
+s usr/lib/amd64/libdtrace_jni.so=libdtrace_jni.so.1
+s usr/lib/amd64/libdwarf.so.1=../../../lib/amd64/libdwarf.so.1
+s usr/lib/amd64/libefi.so.1=../../../lib/amd64/libefi.so.1
+s usr/lib/amd64/libefi.so=../../../lib/amd64/libefi.so.1
+s usr/lib/amd64/libelf.so.1=../../../lib/amd64/libelf.so.1
+s usr/lib/amd64/libelf.so=../../../lib/amd64/libelf.so.1
+f usr/lib/amd64/libexacct.so.1 0755 root bin
+s usr/lib/amd64/libexacct.so=libexacct.so.1
+s usr/lib/amd64/libfakekernel.so.1=../../../lib/amd64/libfakekernel.so.1
+f usr/lib/amd64/libfcoe.so.1 0755 root bin
+s usr/lib/amd64/libfcoe.so=libfcoe.so.1
+s usr/lib/amd64/libfdisk.so.1=../../../lib/amd64/libfdisk.so.1
+s usr/lib/amd64/libfdisk.so=../../../lib/amd64/libfdisk.so.1
+f usr/lib/amd64/libficl-sys.so.4.1.0 0755 root bin
+f usr/lib/amd64/libform.so.1 0755 root bin
+s usr/lib/amd64/libform.so=libform.so.1
+f usr/lib/amd64/libfru.so.1 0755 root bin
+s usr/lib/amd64/libfru.so=libfru.so.1
+f usr/lib/amd64/libfruraw.so.1 0755 root bin
+s usr/lib/amd64/libfruraw.so=libfruraw.so.1
+f usr/lib/amd64/libfrureg.so.1 0755 root bin
+s usr/lib/amd64/libfrureg.so=libfrureg.so.1
+f usr/lib/amd64/libfruutils.so.1 0755 root bin
+s usr/lib/amd64/libfruutils.so=libfruutils.so.1
+s usr/lib/amd64/libgen.so.1=../../../lib/amd64/libgen.so.1
+s usr/lib/amd64/libgen.so=../../../lib/amd64/libgen.so.1
+f usr/lib/amd64/libgss.so.1 0755 root bin
+s usr/lib/amd64/libgss.so=libgss.so.1
+f usr/lib/amd64/libhotplug.so.1 0755 root bin
+s usr/lib/amd64/libhotplug.so=libhotplug.so.1
+f usr/lib/amd64/libidmap.so.1 0755 root bin
+s usr/lib/amd64/libidmap.so=libidmap.so.1
+f usr/lib/amd64/libidspace.so.1 0755 root bin
+f usr/lib/amd64/libike.so.1 0755 root bin
+s usr/lib/amd64/libike.so=libike.so.1
+f usr/lib/amd64/libilb.so.1 0755 root bin
+s usr/lib/amd64/libilb.so=libilb.so.1
+f usr/lib/amd64/libima.so.1 0755 root bin
+s usr/lib/amd64/libima.so=libima.so.1
+s usr/lib/amd64/libinetutil.so.1=../../../lib/amd64/libinetutil.so.1
+s usr/lib/amd64/libinetutil.so=../../../lib/amd64/libinetutil.so.1
+s usr/lib/amd64/libintl.so.1=../../../lib/amd64/libintl.so.1
+s usr/lib/amd64/libintl.so=../../../lib/amd64/libintl.so.1
+s usr/lib/amd64/libipadm.so.1=../../../lib/amd64/libipadm.so.1
+s usr/lib/amd64/libipadm.so=../../../lib/amd64/libipadm.so.1
+f usr/lib/amd64/libipd.so.1 0755 root bin
+s usr/lib/amd64/libipd.so=libipd.so.1
+f usr/lib/amd64/libipmi.so.1 0755 root bin
+s usr/lib/amd64/libipmi.so=libipmi.so.1
+s usr/lib/amd64/libipmp.so.1=../../../lib/amd64/libipmp.so.1
+s usr/lib/amd64/libipmp.so=../../../lib/amd64/libipmp.so.1
+f usr/lib/amd64/libipp.so.1 0755 root bin
+s usr/lib/amd64/libipp.so=libipp.so.1
+f usr/lib/amd64/libipsecutil.so.1 0755 root bin
+s usr/lib/amd64/libipsecutil.so=libipsecutil.so.1
+f usr/lib/amd64/libiscsit.so.1 0755 root bin
+s usr/lib/amd64/libiscsit.so=libiscsit.so.1
+f usr/lib/amd64/libjedec.so.1 0755 root bin
+s usr/lib/amd64/libkmfberder.so.1=../../../lib/amd64/libkmfberder.so.1
+s usr/lib/amd64/libkmfberder.so=../../../lib/amd64/libkmfberder.so.1
+f usr/lib/amd64/libkrb5.so.1 0755 root bin
+s usr/lib/amd64/libkrb5.so=libkrb5.so.1
+s usr/lib/amd64/libkstat.so.1=../../../lib/amd64/libkstat.so.1
+s usr/lib/amd64/libkstat.so=../../../lib/amd64/libkstat.so.1
+f usr/lib/amd64/libkvm.so.1 0755 root bin
+s usr/lib/amd64/libkvm.so=libkvm.so.1
+f usr/lib/amd64/libl.so.1 0755 root bin
+s usr/lib/amd64/libl.so=libl.so.1
+f usr/lib/amd64/libldap.so.5 0755 root bin
+s usr/lib/amd64/libldap.so=libldap.so.5
+s usr/lib/amd64/liblddbg.so.4=../../../lib/amd64/liblddbg.so.4
+f usr/lib/amd64/libldstab.so.1 0755 root bin
+f usr/lib/amd64/liblgrp.so.1 0755 root bin
+s usr/lib/amd64/liblgrp.so=liblgrp.so.1
+f usr/lib/amd64/liblm.so.1 0755 root bin
+s usr/lib/amd64/liblm.so=liblm.so.1
+s usr/lib/amd64/libm.so.1=../../../lib/amd64/libm.so.1
+s usr/lib/amd64/libm.so.2=../../../lib/amd64/libm.so.2
+s usr/lib/amd64/libm.so=../../../lib/amd64/libm.so.2
+f usr/lib/amd64/libmail.so.1 0755 root bin
+s usr/lib/amd64/libmail.so=libmail.so.1
+f usr/lib/amd64/libmalloc.so.1 0755 root bin
+s usr/lib/amd64/libmalloc.so=libmalloc.so.1
+f usr/lib/amd64/libmapmalloc.so.1 0755 root bin
+s usr/lib/amd64/libmapmalloc.so=libmapmalloc.so.1
+s usr/lib/amd64/libmd.so.1=../../../lib/amd64/libmd.so.1
+s usr/lib/amd64/libmd.so=../../../lib/amd64/libmd.so.1
+s usr/lib/amd64/libmd5.so.1=../../../lib/amd64/libmd5.so.1
+s usr/lib/amd64/libmd5.so=../../../lib/amd64/libmd5.so.1
+s usr/lib/amd64/libmech_krb5.so=gss/mech_krb5.so
+f usr/lib/amd64/libmenu.so.1 0755 root bin
+s usr/lib/amd64/libmenu.so=libmenu.so.1
+s usr/lib/amd64/libmp.so.2=../../../lib/amd64/libmp.so.2
+s usr/lib/amd64/libmp.so=../../../lib/amd64/libmp.so.2
+s usr/lib/amd64/libmpscsi_vhci.so.1=../../../lib/amd64/libmpscsi_vhci.so.1
+s usr/lib/amd64/libmpscsi_vhci.so=../../../lib/amd64/libmpscsi_vhci.so.1
+f usr/lib/amd64/libmtmalloc.so.1 0755 root bin
+s usr/lib/amd64/libmtmalloc.so=libmtmalloc.so.1
+s usr/lib/amd64/libmvec.so.1=../../../lib/amd64/libmvec.so.1
+s usr/lib/amd64/libmvec.so=../../../lib/amd64/libmvec.so.1
+f usr/lib/amd64/libndmp.so.1 0755 root bin
+s usr/lib/amd64/libndmp.so=libndmp.so.1
+f usr/lib/amd64/libnls.so.1 0755 root bin
+s usr/lib/amd64/libnls.so=libnls.so.1
+s usr/lib/amd64/libnsl.so.1=../../../lib/amd64/libnsl.so.1
+s usr/lib/amd64/libnsl.so=../../../lib/amd64/libnsl.so.1
+f usr/lib/amd64/libnvfru.so.1 0755 root bin
+s usr/lib/amd64/libnvfru.so=libnvfru.so.1
+s usr/lib/amd64/libnvpair.so.1=../../../lib/amd64/libnvpair.so.1
+s usr/lib/amd64/libnvpair.so=../../../lib/amd64/libnvpair.so.1
+s usr/lib/amd64/libpam.so.1=../../../lib/amd64/libpam.so.1
+s usr/lib/amd64/libpam.so=../../../lib/amd64/libpam.so.1
+f usr/lib/amd64/libpanel.so.1 0755 root bin
+s usr/lib/amd64/libpanel.so=libpanel.so.1
+f usr/lib/amd64/libpcidb.so.1 0755 root bin
+f usr/lib/amd64/libpcsc.so.1 0755 root bin
+s usr/lib/amd64/libpcsc.so=libpcsc.so.1
+f usr/lib/amd64/libpctx.so.1 0755 root bin
+s usr/lib/amd64/libpctx.so=libpctx.so.1
+f usr/lib/amd64/libpicl.so.1 0755 root bin
+s usr/lib/amd64/libpicl.so=libpicl.so.1
+f usr/lib/amd64/libpkcs11.so.1 0755 root bin
+s usr/lib/amd64/libpkcs11.so=libpkcs11.so.1
+f usr/lib/amd64/libpool.so.1 0755 root bin
+s usr/lib/amd64/libpool.so=libpool.so.1
+s usr/lib/amd64/libposix4.so.1=../../../lib/amd64/librt.so.1
+s usr/lib/amd64/libposix4.so=../../../lib/amd64/librt.so.1
+f usr/lib/amd64/libppt.so.1 0755 root bin
+s usr/lib/amd64/libproc.so.1=../../../lib/amd64/libproc.so.1
+s usr/lib/amd64/libproc.so=../../../lib/amd64/libproc.so.1
+f usr/lib/amd64/libproject.so.1 0755 root bin
+s usr/lib/amd64/libproject.so=libproject.so.1
+s usr/lib/amd64/libpthread.so.1=../../../lib/amd64/libpthread.so.1
+s usr/lib/amd64/libpthread.so=../../../lib/amd64/libpthread.so.1
+f usr/lib/amd64/libraidcfg.so.1 0755 root bin
+s usr/lib/amd64/libraidcfg.so=libraidcfg.so.1
+s usr/lib/amd64/librcm.so.1=../../../lib/amd64/librcm.so.1
+s usr/lib/amd64/librcm.so=../../../lib/amd64/librcm.so.1
+f usr/lib/amd64/librefhash.so.1 0755 root bin
+f usr/lib/amd64/librename.so.1 0755 root bin
+f usr/lib/amd64/libreparse.so.1 0755 root bin
+s usr/lib/amd64/libreparse.so=libreparse.so.1
+s usr/lib/amd64/libresolv.so.2=../../../lib/amd64/libresolv.so.2
+s usr/lib/amd64/libresolv.so=../../../lib/amd64/libresolv.so.2
+s usr/lib/amd64/libresolv_joy.so.2=../../../lib/amd64/libresolv_joy.so.2
+s usr/lib/amd64/librestart.so.1=../../../lib/amd64/librestart.so.1
+s usr/lib/amd64/librestart.so=../../../lib/amd64/librestart.so.1
+s usr/lib/amd64/librpcsvc.so.1=../../../lib/amd64/librpcsvc.so.1
+s usr/lib/amd64/librpcsvc.so=../../../lib/amd64/librpcsvc.so.1
+f usr/lib/amd64/librsm.so.2 0755 root bin
+s usr/lib/amd64/librsm.so=librsm.so.2
+s usr/lib/amd64/librt.so.1=../../../lib/amd64/librt.so.1
+s usr/lib/amd64/librt.so=../../../lib/amd64/librt.so.1
+s usr/lib/amd64/librtld.so.1=../../../lib/amd64/librtld.so.1
+s usr/lib/amd64/librtld_db.so.1=../../../lib/amd64/librtld_db.so.1
+s usr/lib/amd64/librtld_db.so=../../../lib/amd64/librtld_db.so.1
+f usr/lib/amd64/libsasl.so.1 0755 root bin
+s usr/lib/amd64/libsasl.so=libsasl.so.1
+f usr/lib/amd64/libsaveargs.so.1 0755 root bin
+s usr/lib/amd64/libsaveargs.so=libsaveargs.so.1
+s usr/lib/amd64/libscf.so.1=../../../lib/amd64/libscf.so.1
+s usr/lib/amd64/libscf.so=../../../lib/amd64/libscf.so.1
+f usr/lib/amd64/libsched.so.1 0755 root bin
+s usr/lib/amd64/libsched.so=libsched.so.1
+f usr/lib/amd64/libsctp.so.1 0755 root bin
+s usr/lib/amd64/libsctp.so=libsctp.so.1
+s usr/lib/amd64/libsec.so.1=../../../lib/amd64/libsec.so.1
+s usr/lib/amd64/libsec.so=../../../lib/amd64/libsec.so.1
+s usr/lib/amd64/libsecdb.so.1=../../../lib/amd64/libsecdb.so.1
+s usr/lib/amd64/libsecdb.so=../../../lib/amd64/libsecdb.so.1
+s usr/lib/amd64/libsendfile.so.1=../../../lib/amd64/libsendfile.so.1
+s usr/lib/amd64/libsendfile.so=../../../lib/amd64/libsendfile.so.1
+f usr/lib/amd64/libsff.so.1 0755 root bin
+f usr/lib/amd64/libshare.so.1 0755 root bin
+s usr/lib/amd64/libshare.so=libshare.so.1
+f usr/lib/amd64/libshell.so.1 0755 root bin
+s usr/lib/amd64/libshell.so=libshell.so.1
+f usr/lib/amd64/libsip.so.1 0755 root bin
+s usr/lib/amd64/libsip.so=libsip.so.1
+f usr/lib/amd64/libsldap.so.1 0755 root bin
+s usr/lib/amd64/libsldap.so=libsldap.so.1
+f usr/lib/amd64/libslp.so.1 0755 root bin
+s usr/lib/amd64/libslp.so=libslp.so.1
+s usr/lib/amd64/libsmartsshd.so.1=../../../lib/amd64/libsmartsshd.so.1
+s usr/lib/amd64/libsmartsshd.so=../../../lib/amd64/libsmartsshd.so.1
+f usr/lib/amd64/libsmbfs.so.1 0755 root bin
+s usr/lib/amd64/libsmbfs.so=libsmbfs.so.1
+f usr/lib/amd64/libsmbios.so.1 0755 root bin
+s usr/lib/amd64/libsmbios.so=libsmbios.so.1
+f usr/lib/amd64/libsmedia.so.1 0755 root bin
+s usr/lib/amd64/libsmedia.so=libsmedia.so.1
+s usr/lib/amd64/libsocket.so.1=../../../lib/amd64/libsocket.so.1
+s usr/lib/amd64/libsocket.so=../../../lib/amd64/libsocket.so.1
+f usr/lib/amd64/libsoftcrypto.so.1 0755 root bin
+s usr/lib/amd64/libsoftcrypto.so=libsoftcrypto.so.1
+f usr/lib/amd64/libsrpt.so.1 0755 root bin
+s usr/lib/amd64/libsrpt.so=libsrpt.so.1
+f usr/lib/amd64/libssp_ns.a 0755 root bin
+f usr/lib/amd64/libstmf.so.1 0755 root bin
+s usr/lib/amd64/libstmf.so=libstmf.so.1
+f usr/lib/amd64/libstmfproxy.so.1 0755 root bin
+s usr/lib/amd64/libstmfproxy.so=libstmfproxy.so.1
+f usr/lib/amd64/libsum.so.1 0755 root bin
+s usr/lib/amd64/libsum.so=libsum.so.1
+f usr/lib/amd64/libsun_fc.so.1 0755 root bin
+s usr/lib/amd64/libsun_fc.so=libsun_fc.so.1
+f usr/lib/amd64/libsun_ima.so.1 0755 root bin
+s usr/lib/amd64/libsun_ima.so=libsun_ima.so.1
+f usr/lib/amd64/libsun_sas.so.1 0755 root bin
+s usr/lib/amd64/libsun_sas.so=libsun_sas.so.1
+s usr/lib/amd64/libsysevent.so.1=../../../lib/amd64/libsysevent.so.1
+s usr/lib/amd64/libsysevent.so=../../../lib/amd64/libsysevent.so.1
+f usr/lib/amd64/libtecla.so.1 0755 root bin
+s usr/lib/amd64/libtecla.so=libtecla.so.1
+s usr/lib/amd64/libtermcap.so.1=../../../lib/amd64/libtermcap.so.1
+s usr/lib/amd64/libtermcap.so=../../../lib/amd64/libtermcap.so.1
+s usr/lib/amd64/libtermlib.so.1=../../../lib/amd64/libcurses.so.1
+s usr/lib/amd64/libtermlib.so=../../../lib/amd64/libcurses.so.1
+s usr/lib/amd64/libthread.so.1=../../../lib/amd64/libthread.so.1
+s usr/lib/amd64/libthread.so=../../../lib/amd64/libthread.so.1
+s usr/lib/amd64/libthread_db.so.1=../../../lib/amd64/libc_db.so.1
+s usr/lib/amd64/libthread_db.so=../../../lib/amd64/libc_db.so.1
+s usr/lib/amd64/libtsnet.so.1=../../../lib/amd64/libtsnet.so.1
+s usr/lib/amd64/libtsnet.so=../../../lib/amd64/libtsnet.so.1
+s usr/lib/amd64/libtsol.so.2=../../../lib/amd64/libtsol.so.2
+s usr/lib/amd64/libtsol.so=../../../lib/amd64/libtsol.so.2
+s usr/lib/amd64/libumem.so.1=../../../lib/amd64/libumem.so.1
+s usr/lib/amd64/libumem.so=../../../lib/amd64/libumem.so.1
+f usr/lib/amd64/libutempter.so.1 0755 root bin
+s usr/lib/amd64/libutempter.so=libutempter.so.1
+s usr/lib/amd64/libuuid.so.1=../../../lib/amd64/libuuid.so.1
+s usr/lib/amd64/libuuid.so=../../../lib/amd64/libuuid.so.1
+s usr/lib/amd64/libuutil.so.1=../../../lib/amd64/libuutil.so.1
+s usr/lib/amd64/libuutil.so=../../../lib/amd64/libuutil.so.1
+f usr/lib/amd64/libvnd.so.1 0755 root bin
+s usr/lib/amd64/libvnd.so=libvnd.so.1
+f usr/lib/amd64/libvolmgt.so.1 0755 root bin
+s usr/lib/amd64/libvolmgt.so=libvolmgt.so.1
+f usr/lib/amd64/libvrrpadm.so.1 0755 root bin
+s usr/lib/amd64/libvrrpadm.so=libvrrpadm.so.1
+s usr/lib/amd64/libw.so.1=../../../lib/amd64/libw.so.1
+s usr/lib/amd64/libw.so=../../../lib/amd64/libw.so.1
+f usr/lib/amd64/libwrap.so.1.0 0755 root bin
+s usr/lib/amd64/libwrap.so.1=libwrap.so.1.0
+s usr/lib/amd64/libwrap.so=libwrap.so.1.0
+s usr/lib/amd64/libxnet.so.1=../../../lib/amd64/libxnet.so.1
+s usr/lib/amd64/libxnet.so=../../../lib/amd64/libxnet.so.1
+f usr/lib/amd64/liby.so.1 0755 root bin
+s usr/lib/amd64/liby.so=liby.so.1
+s usr/lib/amd64/libzdoor.so.1=../../../lib/amd64/libzdoor.so.1
+s usr/lib/amd64/libzdoor.so=../../../lib/amd64/libzdoor.so.1
+s usr/lib/amd64/libzfs.so.1=../../../lib/amd64/libzfs.so.1
+s usr/lib/amd64/libzfs.so=../../../lib/amd64/libzfs.so.1
+f usr/lib/amd64/libzfs_core.so.1 755 root bin
+s usr/lib/amd64/libzfs_core.so=libzfs_core.so.1
+f usr/lib/amd64/libzfs_jni.so.1 0755 root bin
+s usr/lib/amd64/libzfs_jni.so=libzfs_jni.so.1
+f usr/lib/amd64/libzonecfg.so.1 0755 root bin
+s usr/lib/amd64/libzonecfg.so=libzonecfg.so.1
+f usr/lib/amd64/libzpool.so.1 0755 root bin
+s usr/lib/amd64/libzpool.so=libzpool.so.1
+s usr/lib/amd64/libzutil.so.1=../../../lib/amd64/libzutil.so.1
+s usr/lib/amd64/libzutil.so=../../../lib/amd64/libzutil.so.1
+f usr/lib/amd64/lx_brand.so.1 0755 root sys
+f usr/lib/amd64/madv.so.1 0755 root bin
+f usr/lib/amd64/mpss.so.1 0755 root bin
+f usr/lib/amd64/nss_ad.so.1 0755 root bin
+s usr/lib/amd64/nss_compat.so.1=../../../lib/amd64/nss_compat.so.1
+s usr/lib/amd64/nss_dns.so.1=../../../lib/amd64/nss_dns.so.1
+s usr/lib/amd64/nss_files.so.1=../../../lib/amd64/nss_files.so.1
+f usr/lib/amd64/nss_ldap.so.1 0755 root bin
+f usr/lib/amd64/nss_mdns.so.1 0755 root bin
+s usr/lib/amd64/nss_nis.so.1=../../../lib/amd64/nss_nis.so.1
+s usr/lib/amd64/nss_user.so.1=../../../lib/amd64/nss_user.so.1
+f usr/lib/amd64/passwdutil.so.1 0755 root bin
+d usr/lib/amd64/pkgconfig 0755 root other
+f usr/lib/amd64/rpcsec.so.1 0755 root bin
+s usr/lib/amd64/rpcsec.so=rpcsec.so.1
+f usr/lib/amd64/straddr.so.2 0755 root bin
+s usr/lib/amd64/straddr.so=straddr.so.2
+f usr/lib/amd64/udapl_tavor.so.1 0755 root bin
+f usr/lib/amd64/values-Xa.o 0644 root bin
+f usr/lib/amd64/values-Xc.o 0644 root bin
+f usr/lib/amd64/values-Xs.o 0644 root bin
+f usr/lib/amd64/values-Xt.o 0644 root bin
+f usr/lib/amd64/values-xpg4.o 0644 root bin
+f usr/lib/amd64/values-xpg6.o 0644 root bin
+f usr/lib/amd64/watchmalloc.so.1 0755 root bin
+d usr/lib/audit 0755 root bin
+f usr/lib/audit/audit_record_attr 0444 root bin
+d usr/lib/autofs 0755 root sys
+f usr/lib/autofs/automountd 0555 root bin
+d usr/lib/brand 0755 root bin
+d usr/lib/brand/bhyve 0555 root sys
+f usr/lib/brand/bhyve/attach 0555 root sys
+f usr/lib/brand/bhyve/bhhwcompat 0555 root sys
+f usr/lib/brand/bhyve/boot 0555 root sys
+f usr/lib/brand/bhyve/config.xml 0444 root sys
+f usr/lib/brand/bhyve/detach 0555 root sys
+f usr/lib/brand/bhyve/platform.xml 0444 root sys
+f usr/lib/brand/bhyve/statechange 0555 root sys
+f usr/lib/brand/bhyve/uninstall 0555 root sys
+f usr/lib/brand/bhyve/zhyve 0555 root sys
+d usr/lib/brand/jcommon 0755 root bin
+f usr/lib/brand/jcommon/cattach 0444 root root
+f usr/lib/brand/jcommon/cdetach 0444 root root
+f usr/lib/brand/jcommon/cinstall 0444 root root
+f usr/lib/brand/jcommon/cuninstall 0444 root root
+f usr/lib/brand/jcommon/libhooks.ksh 0444 root root
+f usr/lib/brand/jcommon/poststate 0444 root root
+f usr/lib/brand/jcommon/prestate 0444 root root
+f usr/lib/brand/jcommon/query 0555 root root
+f usr/lib/brand/jcommon/statechange 0444 root root
+d usr/lib/brand/joyent 0755 root bin
+d usr/lib/brand/joyent-minimal 0755 root bin
+f usr/lib/brand/joyent-minimal/config.xml 0444 root root
+f usr/lib/brand/joyent-minimal/jattach 0555 root root
+f usr/lib/brand/joyent-minimal/jdetach 0555 root root
+f usr/lib/brand/joyent-minimal/jinstall 0555 root root
+f usr/lib/brand/joyent-minimal/juninstall 0555 root root
+f usr/lib/brand/joyent-minimal/manifests 0444 root root
+f usr/lib/brand/joyent-minimal/platform.xml 0444 root root
+f usr/lib/brand/joyent-minimal/poststate 0555 root root
+f usr/lib/brand/joyent-minimal/prestate 0555 root root
+s usr/lib/brand/joyent-minimal/query=../jcommon/query
+f usr/lib/brand/joyent-minimal/statechange 0555 root root
+f usr/lib/brand/joyent/config.xml 0444 root root
+f usr/lib/brand/joyent/jattach 0755 root root
+f usr/lib/brand/joyent/jdetach 0755 root root
+f usr/lib/brand/joyent/jinstall 0755 root root
+f usr/lib/brand/joyent/juninstall 0755 root root
+f usr/lib/brand/joyent/manifests 0444 root root
+f usr/lib/brand/joyent/platform.xml 0444 root root
+f usr/lib/brand/joyent/poststate 0755 root root
+f usr/lib/brand/joyent/prestate 0755 root root
+s usr/lib/brand/joyent/query=../jcommon/query
+f usr/lib/brand/joyent/statechange 0755 root root
+d usr/lib/brand/kvm 0755 root bin
+f usr/lib/brand/kvm/config.xml 0444 root root
+f usr/lib/brand/kvm/kattach 0755 root root
+f usr/lib/brand/kvm/kdetach 0755 root root
+f usr/lib/brand/kvm/kinstall 0755 root root
+f usr/lib/brand/kvm/kuninstall 0755 root root
+f usr/lib/brand/kvm/platform.xml 0444 root root
+f usr/lib/brand/kvm/poststate 0755 root root
+f usr/lib/brand/kvm/prestate 0755 root root
+f usr/lib/brand/kvm/statechange 0755 root root
+d usr/lib/brand/lx 0755 root bin
+s usr/lib/brand/lx/64=amd64
+d usr/lib/brand/lx/amd64 0755 root bin
+f usr/lib/brand/lx/amd64/lx_librtld_db.so.1 0755 root root
+f usr/lib/brand/lx/amd64/lx_vdso.so.1 0755 root root
+f usr/lib/brand/lx/config.xml 0444 root root
+f usr/lib/brand/lx/etc_default_nfs 0444 root root
+d usr/lib/brand/lx/ld 0755 root root
+d usr/lib/brand/lx/ld/64 0755 root root
+f usr/lib/brand/lx/ld/64/ld.config 0755 root root
+f usr/lib/brand/lx/ld/ld.config 0755 root root
+f usr/lib/brand/lx/ltp_skiplist 0444 root root
+f usr/lib/brand/lx/ltp_tests 0444 root root
+f usr/lib/brand/lx/lx_boot 0755 root root
+f usr/lib/brand/lx/lx_boot_zone_busybox 0755 root root
+f usr/lib/brand/lx/lx_boot_zone_debian 0755 root root
+f usr/lib/brand/lx/lx_boot_zone_docker 0755 root root
+f usr/lib/brand/lx/lx_boot_zone_redhat 0755 root root
+f usr/lib/brand/lx/lx_boot_zone_suse 0755 root root
+f usr/lib/brand/lx/lx_boot_zone_ubuntu 0755 root root
+f usr/lib/brand/lx/lx_boot_zone_void 0755 root root
+f usr/lib/brand/lx/lx_install 0755 root root
+f usr/lib/brand/lx/lx_librtld_db.so.1 0755 root root
+f usr/lib/brand/lx/lx_lockd 0755 root root
+f usr/lib/brand/lx/lx_support 0755 root root
+f usr/lib/brand/lx/lx_uninstall 0755 root root
+f usr/lib/brand/lx/lx_vdso.so.1 0755 root root
+f usr/lib/brand/lx/lxinit 0755 root root
+f usr/lib/brand/lx/platform.xml 0444 root root
+f usr/lib/brand/lx/poststate 0755 root root
+f usr/lib/brand/lx/prestate 0755 root root
+f usr/lib/brand/lx/pts_ignorelist 0444 root root
+f usr/lib/brand/lx/statechange 0755 root root
+d usr/lib/brand/shared 0755 root sys
+f usr/lib/brand/shared/common.ksh 0444 root bin
+f usr/lib/brand/shared/query 0755 root bin
+f usr/lib/brand/shared/uninstall.ksh 0444 root bin
+f usr/lib/bridged 0555 root bin
+d usr/lib/ccid 0755 root bin
+d usr/lib/cfgadm 0755 root bin
+d usr/lib/cfgadm/amd64 0755 root bin
+f usr/lib/cfgadm/amd64/ccid.so.1 0755 root bin
+s usr/lib/cfgadm/amd64/ccid.so=ccid.so.1
+f usr/lib/cfgadm/amd64/fp.so.1 0755 root bin
+s usr/lib/cfgadm/amd64/fp.so=fp.so.1
+f usr/lib/cfgadm/amd64/ib.so.1 0755 root bin
+s usr/lib/cfgadm/amd64/ib.so=ib.so.1
+f usr/lib/cfgadm/amd64/pci.so.1 0755 root bin
+s usr/lib/cfgadm/amd64/pci.so=pci.so.1
+f usr/lib/cfgadm/amd64/sata.so.1 0755 root bin
+s usr/lib/cfgadm/amd64/sata.so=sata.so.1
+f usr/lib/cfgadm/amd64/scsi.so.1 0755 root bin
+s usr/lib/cfgadm/amd64/scsi.so=scsi.so.1
+f usr/lib/cfgadm/amd64/shp.so.1 0755 root bin
+s usr/lib/cfgadm/amd64/shp.so=shp.so.1
+f usr/lib/cfgadm/amd64/usb.so.1 0755 root bin
+s usr/lib/cfgadm/amd64/usb.so=usb.so.1
+f usr/lib/cfgadm/ccid.so.1 0755 root bin
+s usr/lib/cfgadm/ccid.so=ccid.so.1
+f usr/lib/cfgadm/fp.so.1 0755 root bin
+s usr/lib/cfgadm/fp.so=fp.so.1
+f usr/lib/cfgadm/ib.so.1 0755 root bin
+s usr/lib/cfgadm/ib.so=ib.so.1
+f usr/lib/cfgadm/pci.so.1 0755 root bin
+s usr/lib/cfgadm/pci.so=pci.so.1
+f usr/lib/cfgadm/sata.so.1 0755 root bin
+s usr/lib/cfgadm/sata.so=sata.so.1
+f usr/lib/cfgadm/scsi.so.1 0755 root bin
+s usr/lib/cfgadm/scsi.so=scsi.so.1
+f usr/lib/cfgadm/shp.so.1 0755 root bin
+s usr/lib/cfgadm/shp.so=shp.so.1
+f usr/lib/cfgadm/usb.so.1 0755 root bin
+s usr/lib/cfgadm/usb.so=usb.so.1
+d usr/lib/class 0755 root bin
+d usr/lib/class/FSS 0755 root bin
+f usr/lib/class/FSS/FSSdispadmin 0555 root bin
+f usr/lib/class/FSS/FSSpriocntl 0555 root bin
+d usr/lib/class/FX 0755 root bin
+f usr/lib/class/FX/FXdispadmin 0555 root bin
+f usr/lib/class/FX/FXpriocntl 0555 root bin
+d usr/lib/class/IA 0755 root bin
+f usr/lib/class/IA/IAdispadmin 0555 root bin
+f usr/lib/class/IA/IApriocntl 0555 root bin
+d usr/lib/class/RT 0755 root bin
+f usr/lib/class/RT/RTdispadmin 0555 root bin
+f usr/lib/class/RT/RTpriocntl 0555 root bin
+d usr/lib/class/SDC 0755 root bin
+f usr/lib/class/SDC/SDCdispadmin 0555 root bin
+f usr/lib/class/SDC/SDCpriocntl 0555 root bin
+d usr/lib/class/TS 0755 root bin
+f usr/lib/class/TS/TSdispadmin 0555 root bin
+f usr/lib/class/TS/TSpriocntl 0555 root bin
+s usr/lib/cron=../../etc/cron.d
+f usr/lib/crt1.o 0644 root bin
+f usr/lib/crti.o 0644 root bin
+f usr/lib/crtn.o 0644 root bin
+d usr/lib/crypto 0755 root bin
+d usr/lib/devfsadm 0755 root sys
+s usr/lib/devfsadm/devfsadmd=../../sbin/devfsadm
+d usr/lib/devfsadm/linkmod 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_audio_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_cfg_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_dcam1394_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_disk_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_dtrace_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_fssnap_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_ieee1394_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_lofi_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_lx_link_i386.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_misc_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_misc_link_i386.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_port_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_ramdisk_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_sensor_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_sgen_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_smp_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_tape_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_usb_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_vscan_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_xen_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_zfs_link.so 0755 root sys
+f usr/lib/devfsadm/linkmod/SUNW_zut_link.so 0755 root sys
+f usr/lib/diff3prog 0555 root bin
+f usr/lib/diffh 0555 root bin
+d usr/lib/dl 0755 root sys
+f usr/lib/dl/dlled 0555 root bin
+f usr/lib/dl/dlrecv 0555 root bin
+f usr/lib/dl/dlsend 0555 root bin
+f usr/lib/dl/dltraninfo 0555 root bin
+d usr/lib/dtrace 0755 root bin
+d usr/lib/dtrace/64 0755 root bin
+f usr/lib/dtrace/64/drti.o 0644 root bin
+f usr/lib/dtrace/64/libdtrace_forceload.so 0644 root bin
+f usr/lib/dtrace/drti.o 0644 root bin
+f usr/lib/dtrace/errno.d 0644 root bin
+f usr/lib/dtrace/fc.d 0644 root bin
+f usr/lib/dtrace/io.d 0644 root bin
+f usr/lib/dtrace/ip.d 0644 root bin
+f usr/lib/dtrace/iscsit.d 0644 root bin
+f usr/lib/dtrace/libdtrace_forceload.so 0644 root bin
+f usr/lib/dtrace/mac.d 0644 root bin
+f usr/lib/dtrace/net.d 0644 root bin
+f usr/lib/dtrace/nfs.d 0644 root bin
+f usr/lib/dtrace/procfs.d 0644 root bin
+f usr/lib/dtrace/regs.d 0644 root bin
+f usr/lib/dtrace/sched.d 0644 root bin
+f usr/lib/dtrace/scsi.d 0644 root bin
+f usr/lib/dtrace/signal.d 0644 root bin
+f usr/lib/dtrace/smb.d 0644 root bin
+f usr/lib/dtrace/srp.d 0644 root bin
+f usr/lib/dtrace/sysevent.d 0644 root bin
+f usr/lib/dtrace/tcp.d 0644 root bin
+f usr/lib/dtrace/udp.d 0644 root bin
+f usr/lib/dtrace/unistd.d 0644 root bin
+f usr/lib/dtrace/vnd.d 0644 root bin
+d usr/lib/elfedit 0755 root bin
+s usr/lib/elfedit/32=.
+s usr/lib/elfedit/64=amd64
+d usr/lib/elfedit/amd64 0755 root bin
+f usr/lib/elfedit/amd64/cap.so 0755 root bin
+f usr/lib/elfedit/amd64/dyn.so 0755 root bin
+f usr/lib/elfedit/amd64/ehdr.so 0755 root bin
+f usr/lib/elfedit/amd64/phdr.so 0755 root bin
+f usr/lib/elfedit/amd64/shdr.so 0755 root bin
+f usr/lib/elfedit/amd64/str.so 0755 root bin
+f usr/lib/elfedit/amd64/sym.so 0755 root bin
+f usr/lib/elfedit/amd64/syminfo.so 0755 root bin
+f usr/lib/elfedit/cap.so 0755 root bin
+f usr/lib/elfedit/dyn.so 0755 root bin
+f usr/lib/elfedit/ehdr.so 0755 root bin
+f usr/lib/elfedit/phdr.so 0755 root bin
+f usr/lib/elfedit/shdr.so 0755 root bin
+f usr/lib/elfedit/str.so 0755 root bin
+f usr/lib/elfedit/sym.so 0755 root bin
+f usr/lib/elfedit/syminfo.so 0755 root bin
+s usr/lib/embedded_su=../bin/su
+f usr/lib/expreserve 0555 root bin
+f usr/lib/exrecover 0555 root bin
+f usr/lib/extendedFILE.so.1 0755 root bin
+d usr/lib/fm 0755 root bin
+d usr/lib/fm/amd64 0755 root bin
+f usr/lib/fm/amd64/libdiagcode.so.1 0755 root bin
+s usr/lib/fm/amd64/libdiagcode.so=libdiagcode.so.1
+f usr/lib/fm/amd64/libdiskstatus.so.1 0755 root bin
+s usr/lib/fm/amd64/libdiskstatus.so=libdiskstatus.so.1
+f usr/lib/fm/amd64/libfmd_adm.so.1 0755 root bin
+s usr/lib/fm/amd64/libfmd_adm.so=libfmd_adm.so.1
+f usr/lib/fm/amd64/libfmd_agent.so.1 0755 root bin
+s usr/lib/fm/amd64/libfmd_agent.so=libfmd_agent.so.1
+f usr/lib/fm/amd64/libfmd_log.so.1 0755 root bin
+s usr/lib/fm/amd64/libfmd_log.so=libfmd_log.so.1
+f usr/lib/fm/amd64/libfmd_msg.so.1 0755 root bin
+s usr/lib/fm/amd64/libfmd_msg.so=libfmd_msg.so.1
+s usr/lib/fm/amd64/libfmevent.so.1=../../../../lib/fm/amd64/libfmevent.so.1
+s usr/lib/fm/amd64/libfmevent.so=../../../../lib/fm/amd64/libfmevent.so.1
+f usr/lib/fm/amd64/libfmnotify.so.1 0755 root bin
+s usr/lib/fm/amd64/libfmnotify.so=libfmnotify.so.1
+f usr/lib/fm/amd64/libseslog.so.1 0755 root bin
+s usr/lib/fm/amd64/libseslog.so=libseslog.so.1
+f usr/lib/fm/amd64/libtopo.so.1 0755 root bin
+s usr/lib/fm/amd64/libtopo.so=libtopo.so.1
+d usr/lib/fm/dict 0755 root bin
+f usr/lib/fm/dict/AMD.dict 0444 root bin
+f usr/lib/fm/dict/DISK.dict 0444 root bin
+f usr/lib/fm/dict/FMD.dict 0444 root bin
+f usr/lib/fm/dict/FMNOTIFY.dict 0444 root bin
+f usr/lib/fm/dict/GMCA.dict 0444 root bin
+f usr/lib/fm/dict/INTEL.dict 0444 root bin
+f usr/lib/fm/dict/NIC.dict 0444 root bin
+f usr/lib/fm/dict/NXGE.dict 0444 root bin
+f usr/lib/fm/dict/PCI.dict 0444 root bin
+f usr/lib/fm/dict/PCIEX.dict 0444 root bin
+f usr/lib/fm/dict/SCA1000.dict 0444 root bin
+f usr/lib/fm/dict/SCA500.dict 0444 root bin
+f usr/lib/fm/dict/SENSOR.dict 0444 root bin
+f usr/lib/fm/dict/SMF.dict 0444 root bin
+f usr/lib/fm/dict/STORAGE.dict 0444 root bin
+f usr/lib/fm/dict/SUNOS.dict 0444 root bin
+f usr/lib/fm/dict/TEST.dict 0444 root bin
+f usr/lib/fm/dict/ZFS.dict 0444 root bin
+d usr/lib/fm/eft 0755 root bin
+f usr/lib/fm/eft/disk.eft 0444 root bin
+f usr/lib/fm/eft/neptune_xaui.eft 0444 root bin
+f usr/lib/fm/eft/neptune_xfp.eft 0444 root bin
+f usr/lib/fm/eft/nic.eft 0444 root bin
+f usr/lib/fm/eft/pci.eft 0444 root bin
+f usr/lib/fm/eft/pciex.eft 0444 root bin
+f usr/lib/fm/eft/pciexrc.eft 0444 root bin
+f usr/lib/fm/eft/sca1000.eft 0444 root bin
+f usr/lib/fm/eft/sca500.eft 0444 root bin
+f usr/lib/fm/eft/sensor.eft 0444 root bin
+f usr/lib/fm/eft/storage.eft 0444 root bin
+f usr/lib/fm/eftinfo 0555 root bin
+f usr/lib/fm/esc 0555 root bin
+d usr/lib/fm/fmd 0755 root bin
+f usr/lib/fm/fmd/fmd 0555 root bin
+f usr/lib/fm/fmd/fminject 0555 root bin
+f usr/lib/fm/fmd/fmsim 0555 root bin
+f usr/lib/fm/fmd/fmtopo 0555 root bin
+f usr/lib/fm/fmd/ipmitopo 0555 root bin
+f usr/lib/fm/fmd/mcdecode 0555 root bin
+d usr/lib/fm/fmd/plugins 0755 root bin
+f usr/lib/fm/fmd/plugins/cpumem-retire.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/cpumem-retire.so 0555 root bin
+f usr/lib/fm/fmd/plugins/disk-lights.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/disk-lights.so 0555 root bin
+f usr/lib/fm/fmd/plugins/disk-monitor.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/disk-monitor.so 0555 root bin
+f usr/lib/fm/fmd/plugins/disk-transport.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/disk-transport.so 0555 root bin
+f usr/lib/fm/fmd/plugins/eft.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/eft.so 0555 root bin
+f usr/lib/fm/fmd/plugins/ext-event-transport.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/ext-event-transport.so 0555 root bin
+f usr/lib/fm/fmd/plugins/fabric-xlate.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/fabric-xlate.so 0555 root bin
+f usr/lib/fm/fmd/plugins/fdd-msg.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/fdd-msg.so 0555 root bin
+f usr/lib/fm/fmd/plugins/io-retire.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/io-retire.so 0555 root bin
+f usr/lib/fm/fmd/plugins/ip-transport.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/ip-transport.so 0555 root bin
+f usr/lib/fm/fmd/plugins/sensor-transport.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/sensor-transport.so 0555 root bin
+f usr/lib/fm/fmd/plugins/ses-log-transport.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/ses-log-transport.so 0555 root bin
+f usr/lib/fm/fmd/plugins/software-diagnosis.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/software-diagnosis.so 0555 root bin
+f usr/lib/fm/fmd/plugins/software-response.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/software-response.so 0555 root bin
+f usr/lib/fm/fmd/plugins/sp-monitor.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/sp-monitor.so 0555 root bin
+f usr/lib/fm/fmd/plugins/syslog-msgs.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/syslog-msgs.so 0555 root bin
+f usr/lib/fm/fmd/plugins/zfs-diagnosis.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/zfs-diagnosis.so 0555 root bin
+f usr/lib/fm/fmd/plugins/zfs-retire.conf 0644 root bin
+f usr/lib/fm/fmd/plugins/zfs-retire.so 0555 root bin
+d usr/lib/fm/fmd/schemes 0755 root bin
+d usr/lib/fm/fmd/schemes/amd64 0755 root bin
+f usr/lib/fm/fmd/schemes/amd64/cpu.so 0555 root bin
+f usr/lib/fm/fmd/schemes/amd64/dev.so 0555 root bin
+f usr/lib/fm/fmd/schemes/amd64/fmd.so 0555 root bin
+f usr/lib/fm/fmd/schemes/amd64/hc.so 0555 root bin
+f usr/lib/fm/fmd/schemes/amd64/legacy-hc.so 0555 root bin
+f usr/lib/fm/fmd/schemes/amd64/mem.so 0555 root bin
+f usr/lib/fm/fmd/schemes/amd64/mod.so 0555 root bin
+f usr/lib/fm/fmd/schemes/amd64/pkg.so 0555 root bin
+f usr/lib/fm/fmd/schemes/amd64/svc.so 0555 root bin
+f usr/lib/fm/fmd/schemes/amd64/sw.so 0555 root bin
+f usr/lib/fm/fmd/schemes/amd64/zfs.so 0555 root bin
+f usr/lib/fm/fmd/schemes/cpu.so 0555 root bin
+f usr/lib/fm/fmd/schemes/dev.so 0555 root bin
+f usr/lib/fm/fmd/schemes/fmd.so 0555 root bin
+f usr/lib/fm/fmd/schemes/hc.so 0555 root bin
+f usr/lib/fm/fmd/schemes/legacy-hc.so 0555 root bin
+f usr/lib/fm/fmd/schemes/mem.so 0555 root bin
+f usr/lib/fm/fmd/schemes/mod.so 0555 root bin
+f usr/lib/fm/fmd/schemes/pkg.so 0555 root bin
+f usr/lib/fm/fmd/schemes/svc.so 0555 root bin
+f usr/lib/fm/fmd/schemes/sw.so 0555 root bin
+f usr/lib/fm/fmd/schemes/zfs.so 0555 root bin
+f usr/lib/fm/libdiagcode.so.1 0755 root bin
+s usr/lib/fm/libdiagcode.so=libdiagcode.so.1
+f usr/lib/fm/libdiskstatus.so.1 0755 root bin
+s usr/lib/fm/libdiskstatus.so=libdiskstatus.so.1
+f usr/lib/fm/libfmd_adm.so.1 0755 root bin
+s usr/lib/fm/libfmd_adm.so=libfmd_adm.so.1
+f usr/lib/fm/libfmd_agent.so.1 0755 root bin
+s usr/lib/fm/libfmd_agent.so=libfmd_agent.so.1
+f usr/lib/fm/libfmd_log.so.1 0755 root bin
+s usr/lib/fm/libfmd_log.so=libfmd_log.so.1
+f usr/lib/fm/libfmd_msg.so.1 0755 root bin
+s usr/lib/fm/libfmd_msg.so=libfmd_msg.so.1
+s usr/lib/fm/libfmevent.so.1=../../../lib/fm/libfmevent.so.1
+s usr/lib/fm/libfmevent.so=../../../lib/fm/libfmevent.so.1
+f usr/lib/fm/libfmnotify.so.1 0755 root bin
+s usr/lib/fm/libfmnotify.so=libfmnotify.so.1
+f usr/lib/fm/libseslog.so.1 0755 root bin
+s usr/lib/fm/libseslog.so=libseslog.so.1
+f usr/lib/fm/libtopo.so.1 0755 root bin
+s usr/lib/fm/libtopo.so=libtopo.so.1
+d usr/lib/fm/notify 0755 root bin
+f usr/lib/fm/notify/process_msg_template.sh 0555 root bin
+f usr/lib/fm/notify/smtp-notify 0555 root bin
+d usr/lib/fm/topo 0755 root bin
+d usr/lib/fm/topo/maps 0755 root bin
+f usr/lib/fm/topo/maps/xfp-hc-topology.xml 0444 root bin
+d usr/lib/fm/topo/plugins 0755 root bin
+f usr/lib/fm/topo/plugins/disk.so 0555 root bin
+f usr/lib/fm/topo/plugins/fac_prov_ahci.so 0555 root bin
+f usr/lib/fm/topo/plugins/fac_prov_ipmi.so 0555 root bin
+f usr/lib/fm/topo/plugins/fac_prov_mptsas.so 0555 root bin
+f usr/lib/fm/topo/plugins/ipmi.so 0555 root bin
+f usr/lib/fm/topo/plugins/nic.so 0555 root bin
+f usr/lib/fm/topo/plugins/ses.so 0555 root bin
+f usr/lib/fm/topo/plugins/smbios.so 0555 root bin
+f usr/lib/fm/topo/plugins/usb.so 0555 root bin
+f usr/lib/fm/topo/plugins/xfp.so 0555 root bin
+d usr/lib/fs 0755 root sys
+d usr/lib/fs/autofs 0755 root sys
+d usr/lib/fs/autofs/amd64 0755 root sys
+f usr/lib/fs/autofs/amd64/libshare_autofs.so.1 0755 root bin
+s usr/lib/fs/autofs/amd64/libshare_autofs.so=libshare_autofs.so.1
+f usr/lib/fs/autofs/automount 0555 root bin
+f usr/lib/fs/autofs/dfshares 0555 root bin
+f usr/lib/fs/autofs/libshare_autofs.so.1 0755 root bin
+s usr/lib/fs/autofs/libshare_autofs.so=libshare_autofs.so.1
+f usr/lib/fs/autofs/mount 0555 root bin
+f usr/lib/fs/autofs/share 0555 root bin
+f usr/lib/fs/autofs/unshare 0555 root bin
+d usr/lib/fs/bootfs 0755 root sys
+f usr/lib/fs/bootfs/mount 0555 root bin
+d usr/lib/fs/ctfs 0755 root sys
+f usr/lib/fs/ctfs/mount 0555 root bin
+d usr/lib/fs/dev 0755 root sys
+s usr/lib/fs/dev/mount=../../../../etc/fs/dev/mount
+d usr/lib/fs/fd 0755 root sys
+f usr/lib/fs/fd/mount 0555 root bin
+d usr/lib/fs/hsfs 0755 root sys
+f usr/lib/fs/hsfs/fstyp.so.1 0555 root bin
+h usr/lib/fs/hsfs/fstyp=usr/sbin/fstyp
+f usr/lib/fs/hsfs/labelit 0555 root bin
+s usr/lib/fs/hsfs/mount=../../../../etc/fs/hsfs/mount
+d usr/lib/fs/hyprlofs 0755 root sys
+f usr/lib/fs/hyprlofs/hlcfg 0555 root bin
+f usr/lib/fs/hyprlofs/mount 0555 root bin
+d usr/lib/fs/lofs 0755 root sys
+f usr/lib/fs/lofs/mount 0555 root bin
+d usr/lib/fs/lxproc 0755 root sys
+f usr/lib/fs/lxproc/mount 0555 root bin
+d usr/lib/fs/mntfs 0755 root sys
+f usr/lib/fs/mntfs/mount 0555 root bin
+d usr/lib/fs/nfs 0755 root sys
+d usr/lib/fs/nfs/amd64 0755 root sys
+f usr/lib/fs/nfs/amd64/libshare_nfs.so.1 0755 root sys
+s usr/lib/fs/nfs/amd64/libshare_nfs.so=libshare_nfs.so.1
+f usr/lib/fs/nfs/dfmounts 0555 root bin
+h usr/lib/fs/nfs/dfshares=usr/lib/fs/nfs/dfmounts
+f usr/lib/fs/nfs/libshare_nfs.so.1 0755 root sys
+s usr/lib/fs/nfs/libshare_nfs.so=libshare_nfs.so.1
+f usr/lib/fs/nfs/mount 0555 root bin
+f usr/lib/fs/nfs/nfsfind 0555 root sys
+f usr/lib/fs/nfs/showmount 0555 root bin
+f usr/lib/fs/nfs/umount 0555 root bin
+d usr/lib/fs/objfs 0755 root sys
+f usr/lib/fs/objfs/mount 0555 root bin
+d usr/lib/fs/pcfs 0755 root sys
+f usr/lib/fs/pcfs/fsck 0555 root bin
+f usr/lib/fs/pcfs/fstyp.so.1 0555 root bin
+h usr/lib/fs/pcfs/fstyp=usr/sbin/fstyp
+f usr/lib/fs/pcfs/mkfs 0555 root bin
+f usr/lib/fs/pcfs/mount 0555 root bin
+d usr/lib/fs/proc 0755 root sys
+f usr/lib/fs/proc/mount 0555 root bin
+d usr/lib/fs/sharefs 0755 root sys
+f usr/lib/fs/sharefs/mount 0555 root bin
+d usr/lib/fs/smb 0755 root sys
+d usr/lib/fs/smb/amd64 0755 root sys
+f usr/lib/fs/smb/amd64/libshare_smb.so.1 0755 root sys
+f usr/lib/fs/smb/libshare_smb.so.1 0755 root sys
+d usr/lib/fs/smbfs 0755 root sys
+d usr/lib/fs/smbfs/amd64 0755 root sys
+f usr/lib/fs/smbfs/amd64/libshare_smbfs.so.1 0755 root sys
+s usr/lib/fs/smbfs/amd64/libshare_smbfs.so=libshare_smbfs.so.1
+f usr/lib/fs/smbfs/dfshares 0555 root bin
+f usr/lib/fs/smbfs/libshare_smbfs.so.1 0755 root sys
+s usr/lib/fs/smbfs/libshare_smbfs.so=libshare_smbfs.so.1
+f usr/lib/fs/smbfs/mount 0555 root bin
+f usr/lib/fs/smbfs/share 0555 root bin
+f usr/lib/fs/smbfs/umount 0555 root bin
+f usr/lib/fs/smbfs/unshare 0555 root bin
+d usr/lib/fs/tmpfs 0755 root sys
+f usr/lib/fs/tmpfs/mount 0555 root bin
+d usr/lib/fs/ufs 0755 root sys
+f usr/lib/fs/ufs/clri 0555 root bin
+f usr/lib/fs/ufs/dcopy 0555 root bin
+f usr/lib/fs/ufs/df 0555 root bin
+f usr/lib/fs/ufs/edquota 0555 root bin
+f usr/lib/fs/ufs/ff 0555 root bin
+f usr/lib/fs/ufs/fsck 0555 root bin
+f usr/lib/fs/ufs/fsckall 0555 root bin
+f usr/lib/fs/ufs/fsdb 0555 root bin
+f usr/lib/fs/ufs/fsirand 0555 root bin
+f usr/lib/fs/ufs/fssnap 0555 root bin
+f usr/lib/fs/ufs/fstyp.so.1 0555 root bin
+h usr/lib/fs/ufs/fstyp=usr/sbin/fstyp
+f usr/lib/fs/ufs/labelit 0555 root bin
+f usr/lib/fs/ufs/lockfs 0555 root bin
+f usr/lib/fs/ufs/mkfs 0555 root bin
+s usr/lib/fs/ufs/mount=../../../../etc/fs/ufs/mount
+f usr/lib/fs/ufs/ncheck 0555 root bin
+f usr/lib/fs/ufs/newfs 0555 root bin
+f usr/lib/fs/ufs/quot 0555 root bin
+f usr/lib/fs/ufs/quota 4555 root bin
+f usr/lib/fs/ufs/quotacheck 0555 root bin
+f usr/lib/fs/ufs/quotaoff 0555 root bin
+f usr/lib/fs/ufs/quotaon 0555 root bin
+f usr/lib/fs/ufs/repquota 0555 root bin
+f usr/lib/fs/ufs/tunefs 0555 root bin
+f usr/lib/fs/ufs/ufsdump 4555 root bin
+f usr/lib/fs/ufs/ufsrestore 4555 root bin
+f usr/lib/fs/ufs/volcopy 0555 root bin
+d usr/lib/fs/zfs 0755 root sys
+f usr/lib/fs/zfs/bootinstall 0555 root bin
+f usr/lib/fs/zfs/fstyp.so.1 0555 root bin
+h usr/lib/fs/zfs/fstyp=usr/sbin/fstyp
+s usr/lib/fs/zfs/mount=../../../../sbin/zfs
+s usr/lib/fs/zfs/umount=../../../../sbin/zfs
+d usr/lib/fwflash 0755 root bin
+d usr/lib/fwflash/identify 0755 root bin
+f usr/lib/fwflash/identify/hermon.so 0755 root bin
+f usr/lib/fwflash/identify/sd.so 0755 root bin
+f usr/lib/fwflash/identify/ses.so 0755 root bin
+s usr/lib/fwflash/identify/sgen.so=ses.so
+f usr/lib/fwflash/identify/tavor.so 0755 root bin
+f usr/lib/fwflash/identify/ufm.so 0755 root bin
+d usr/lib/fwflash/verify 0755 root bin
+f usr/lib/fwflash/verify/hermon-MELLANOX.so 0755 root bin
+f usr/lib/fwflash/verify/sd-GENERIC.so 0755 root bin
+s usr/lib/fwflash/verify/ses-LSILOGIC.so=ses-SUN.so
+f usr/lib/fwflash/verify/ses-SUN.so 0755 root bin
+s usr/lib/fwflash/verify/sgen-LSILOGIC.so=ses-SUN.so
+s usr/lib/fwflash/verify/sgen-SUN.so=ses-SUN.so
+f usr/lib/fwflash/verify/tavor-MELLANOX.so 0755 root bin
+h usr/lib/gcrt1.o=usr/lib/crt1.o
+f usr/lib/getoptcvt 0555 root bin
+f usr/lib/gmsgfmt 0555 root bin
+d usr/lib/gss 0755 root bin
+f usr/lib/gss/dh1024-0.so.1 0755 root bin
+s usr/lib/gss/dh1024-0.so=dh1024-0.so.1
+f usr/lib/gss/dh640-0.so.1 0755 root bin
+s usr/lib/gss/dh640-0.so=dh640-0.so.1
+f usr/lib/gss/gsscred_clean 0744 root sys
+f usr/lib/gss/gssd 0555 root bin
+s usr/lib/gss/libmech_krb5.so=mech_krb5.so.1
+f usr/lib/gss/mech_dh.so.1 0755 root bin
+s usr/lib/gss/mech_dh.so=mech_dh.so.1
+f usr/lib/gss/mech_krb5.so.1 0755 root bin
+s usr/lib/gss/mech_krb5.so=mech_krb5.so.1
+f usr/lib/gss/mech_spnego.so.1 0755 root bin
+d usr/lib/help 0755 root bin
+d usr/lib/help/auths 0755 root bin
+d usr/lib/help/auths/locale 0755 root bin
+f usr/lib/help/auths/locale/AllSolAuthsHeader.html 0444 root bin
+f usr/lib/help/auths/locale/AuditHeader.html 0444 root bin
+f usr/lib/help/auths/locale/AuthJobsAdmin.html 0444 root bin
+f usr/lib/help/auths/locale/AuthJobsUser.html 0444 root bin
+f usr/lib/help/auths/locale/AuthProfmgrAssign.html 0444 root bin
+f usr/lib/help/auths/locale/AuthProfmgrDelegate.html 0444 root bin
+f usr/lib/help/auths/locale/AuthProfmgrExecattrWrite.html 0444 root bin
+f usr/lib/help/auths/locale/AuthProfmgrRead.html 0444 root bin
+f usr/lib/help/auths/locale/AuthProfmgrWrite.html 0444 root bin
+f usr/lib/help/auths/locale/AuthReadNDMP.html 0444 root bin
+f usr/lib/help/auths/locale/AuthReadSMB.html 0444 root bin
+f usr/lib/help/auths/locale/AuthRoleAssign.html 0444 root bin
+f usr/lib/help/auths/locale/AuthRoleDelegate.html 0444 root bin
+f usr/lib/help/auths/locale/AuthRoleWrite.html 0444 root bin
+f usr/lib/help/auths/locale/BindStates.html 0444 root bin
+d usr/lib/help/auths/locale/C 0755 root bin
+f usr/lib/help/auths/locale/C/AllSolAuthsHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuditHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthJobsAdmin.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthJobsUser.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthProfmgrAssign.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthProfmgrDelegate.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthProfmgrExecattrWrite.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthProfmgrRead.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthProfmgrWrite.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthReadNDMP.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthReadSMB.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthRoleAssign.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthRoleDelegate.html 0444 root bin
+f usr/lib/help/auths/locale/C/AuthRoleWrite.html 0444 root bin
+f usr/lib/help/auths/locale/C/BindStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/DevAllocHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/DevAllocate.html 0444 root bin
+f usr/lib/help/auths/locale/C/DevCDRW.html 0444 root bin
+f usr/lib/help/auths/locale/C/DevConfig.html 0444 root bin
+f usr/lib/help/auths/locale/C/DevGrant.html 0444 root bin
+f usr/lib/help/auths/locale/C/DevRevoke.html 0444 root bin
+f usr/lib/help/auths/locale/C/DhcpmgrHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/DhcpmgrWrite.html 0444 root bin
+f usr/lib/help/auths/locale/C/FileChown.html 0444 root bin
+f usr/lib/help/auths/locale/C/FileHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/FileOwner.html 0444 root bin
+f usr/lib/help/auths/locale/C/HotplugHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/HotplugModify.html 0444 root bin
+f usr/lib/help/auths/locale/C/IdmapRules.html 0444 root bin
+f usr/lib/help/auths/locale/C/JobHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/JobsGrant.html 0444 root bin
+f usr/lib/help/auths/locale/C/LabelFileDowngrade.html 0444 root bin
+f usr/lib/help/auths/locale/C/LabelFileUpgrade.html 0444 root bin
+f usr/lib/help/auths/locale/C/LabelHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/LabelPrint.html 0444 root bin
+f usr/lib/help/auths/locale/C/LabelRange.html 0444 root bin
+f usr/lib/help/auths/locale/C/LabelServer.html 0444 root bin
+f usr/lib/help/auths/locale/C/LinkSecurity.html 0444 root bin
+f usr/lib/help/auths/locale/C/LoginEnable.html 0444 root bin
+f usr/lib/help/auths/locale/C/LoginHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/LoginRemote.html 0444 root bin
+f usr/lib/help/auths/locale/C/MailHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/MailQueue.html 0444 root bin
+f usr/lib/help/auths/locale/C/NetworkAutoconfRead.html 0444 root bin
+f usr/lib/help/auths/locale/C/NetworkAutoconfSelect.html 0444 root bin
+f usr/lib/help/auths/locale/C/NetworkAutoconfWlan.html 0444 root bin
+f usr/lib/help/auths/locale/C/NetworkAutoconfWrite.html 0444 root bin
+f usr/lib/help/auths/locale/C/NetworkHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/NetworkILBconf.html 0444 root bin
+f usr/lib/help/auths/locale/C/NetworkILBenable.html 0444 root bin
+f usr/lib/help/auths/locale/C/NetworkInterfaceConfig.html 0444 root bin
+f usr/lib/help/auths/locale/C/NetworkVRRP.html 0444 root bin
+f usr/lib/help/auths/locale/C/PriAdmin.html 0444 root bin
+f usr/lib/help/auths/locale/C/PrintAdmin.html 0444 root bin
+f usr/lib/help/auths/locale/C/PrintCancel.html 0444 root bin
+f usr/lib/help/auths/locale/C/PrintHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/PrintList.html 0444 root bin
+f usr/lib/help/auths/locale/C/PrintNoBanner.html 0444 root bin
+f usr/lib/help/auths/locale/C/PrintPs.html 0444 root bin
+f usr/lib/help/auths/locale/C/PrintUnlabeled.html 0444 root bin
+f usr/lib/help/auths/locale/C/ProfmgrHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/RoleHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfAllocate.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfAutofsStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfCoreadmStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfCronStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfExAcctFlowStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfExAcctNetStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfExAcctProcessStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfExAcctTaskStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfILBStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfIPsecStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfIdmapStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfInetdStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfLocationStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfMDNSStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfManageAudit.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfManageHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfManageHotplug.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfManageZFSSnap.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfModifyAppl.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfModifyDepend.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfModifyFramework.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfModifyHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfModifyMethod.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfNADDStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfNDMPStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfNWAMStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfNscdStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfPowerStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfReparseStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfRoutingStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfSMBFSStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfSMBStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfSendmailStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfSshStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfSyslogStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfVRRPStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueAudit.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueCoreadm.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueExAcctFlow.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueExAcctNet.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueExAcctProcess.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueExAcctTask.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueFirewall.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueIPsec.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueIdmap.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueInetd.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueMDNS.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueNADD.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueNDMP.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueNWAM.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueProcSec.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueRouting.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueSMB.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueVscan.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfValueVt.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfVscanStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfVtStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SmfWpaStates.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysCpuPowerMgmt.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysDate.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysMaintenance.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysPowerMgmtBrightness.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysPowerMgmtHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysPowerMgmtSuspend.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysPowerMgmtSuspendtoDisk.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysPowerMgmtSuspendtoRAM.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysShutdown.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysSyseventRead.html 0444 root bin
+f usr/lib/help/auths/locale/C/SysSyseventWrite.html 0444 root bin
+f usr/lib/help/auths/locale/C/TNDaemon.html 0444 root bin
+f usr/lib/help/auths/locale/C/TNctl.html 0444 root bin
+f usr/lib/help/auths/locale/C/ValueTND.html 0444 root bin
+f usr/lib/help/auths/locale/C/WifiConfig.html 0444 root bin
+f usr/lib/help/auths/locale/C/WifiWep.html 0444 root bin
+f usr/lib/help/auths/locale/C/ZoneCloneFrom.html 0444 root bin
+f usr/lib/help/auths/locale/C/ZoneHeader.html 0444 root bin
+f usr/lib/help/auths/locale/C/ZoneLogin.html 0444 root bin
+f usr/lib/help/auths/locale/C/ZoneManage.html 0444 root bin
+f usr/lib/help/auths/locale/DevAllocHeader.html 0444 root bin
+f usr/lib/help/auths/locale/DevAllocate.html 0444 root bin
+f usr/lib/help/auths/locale/DevCDRW.html 0444 root bin
+f usr/lib/help/auths/locale/DevConfig.html 0444 root bin
+f usr/lib/help/auths/locale/DevGrant.html 0444 root bin
+f usr/lib/help/auths/locale/DevRevoke.html 0444 root bin
+f usr/lib/help/auths/locale/DhcpmgrHeader.html 0444 root bin
+f usr/lib/help/auths/locale/DhcpmgrWrite.html 0444 root bin
+f usr/lib/help/auths/locale/FileChown.html 0444 root bin
+f usr/lib/help/auths/locale/FileHeader.html 0444 root bin
+f usr/lib/help/auths/locale/FileOwner.html 0444 root bin
+f usr/lib/help/auths/locale/HotplugHeader.html 0444 root bin
+f usr/lib/help/auths/locale/HotplugModify.html 0444 root bin
+f usr/lib/help/auths/locale/IdmapRules.html 0444 root bin
+f usr/lib/help/auths/locale/JobHeader.html 0444 root bin
+f usr/lib/help/auths/locale/JobsGrant.html 0444 root bin
+f usr/lib/help/auths/locale/LabelFileDowngrade.html 0444 root bin
+f usr/lib/help/auths/locale/LabelFileUpgrade.html 0444 root bin
+f usr/lib/help/auths/locale/LabelHeader.html 0444 root bin
+f usr/lib/help/auths/locale/LabelPrint.html 0444 root bin
+f usr/lib/help/auths/locale/LabelRange.html 0444 root bin
+f usr/lib/help/auths/locale/LabelServer.html 0444 root bin
+f usr/lib/help/auths/locale/LinkSecurity.html 0444 root bin
+f usr/lib/help/auths/locale/LoginEnable.html 0444 root bin
+f usr/lib/help/auths/locale/LoginHeader.html 0444 root bin
+f usr/lib/help/auths/locale/LoginRemote.html 0444 root bin
+f usr/lib/help/auths/locale/MailHeader.html 0444 root bin
+f usr/lib/help/auths/locale/MailQueue.html 0444 root bin
+f usr/lib/help/auths/locale/NetworkAutoconfRead.html 0444 root bin
+f usr/lib/help/auths/locale/NetworkAutoconfSelect.html 0444 root bin
+f usr/lib/help/auths/locale/NetworkAutoconfWlan.html 0444 root bin
+f usr/lib/help/auths/locale/NetworkAutoconfWrite.html 0444 root bin
+f usr/lib/help/auths/locale/NetworkHeader.html 0444 root bin
+f usr/lib/help/auths/locale/NetworkILBconf.html 0444 root bin
+f usr/lib/help/auths/locale/NetworkILBenable.html 0444 root bin
+f usr/lib/help/auths/locale/NetworkInterfaceConfig.html 0444 root bin
+f usr/lib/help/auths/locale/NetworkVRRP.html 0444 root bin
+f usr/lib/help/auths/locale/PriAdmin.html 0444 root bin
+f usr/lib/help/auths/locale/PrintAdmin.html 0444 root bin
+f usr/lib/help/auths/locale/PrintCancel.html 0444 root bin
+f usr/lib/help/auths/locale/PrintHeader.html 0444 root bin
+f usr/lib/help/auths/locale/PrintList.html 0444 root bin
+f usr/lib/help/auths/locale/PrintNoBanner.html 0444 root bin
+f usr/lib/help/auths/locale/PrintPs.html 0444 root bin
+f usr/lib/help/auths/locale/PrintUnlabeled.html 0444 root bin
+f usr/lib/help/auths/locale/ProfmgrHeader.html 0444 root bin
+f usr/lib/help/auths/locale/RoleHeader.html 0444 root bin
+f usr/lib/help/auths/locale/SmfAllocate.html 0444 root bin
+f usr/lib/help/auths/locale/SmfAutofsStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfCoreadmStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfCronStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfExAcctFlowStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfExAcctNetStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfExAcctProcessStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfExAcctTaskStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfHeader.html 0444 root bin
+f usr/lib/help/auths/locale/SmfILBStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfIPsecStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfIdmapStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfInetdStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfLocationStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfMDNSStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfManageAudit.html 0444 root bin
+f usr/lib/help/auths/locale/SmfManageHeader.html 0444 root bin
+f usr/lib/help/auths/locale/SmfManageHotplug.html 0444 root bin
+f usr/lib/help/auths/locale/SmfManageZFSSnap.html 0444 root bin
+f usr/lib/help/auths/locale/SmfModifyAppl.html 0444 root bin
+f usr/lib/help/auths/locale/SmfModifyDepend.html 0444 root bin
+f usr/lib/help/auths/locale/SmfModifyFramework.html 0444 root bin
+f usr/lib/help/auths/locale/SmfModifyHeader.html 0444 root bin
+f usr/lib/help/auths/locale/SmfModifyMethod.html 0444 root bin
+f usr/lib/help/auths/locale/SmfNADDStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfNDMPStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfNWAMStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfNscdStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfPowerStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfReparseStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfRoutingStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfSMBFSStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfSMBStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfSendmailStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfSshStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfSyslogStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfVRRPStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueAudit.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueCoreadm.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueExAcctFlow.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueExAcctNet.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueExAcctProcess.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueExAcctTask.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueFirewall.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueHeader.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueIPsec.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueIdmap.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueInetd.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueMDNS.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueNADD.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueNDMP.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueNWAM.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueRouting.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueSMB.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueVscan.html 0444 root bin
+f usr/lib/help/auths/locale/SmfValueVt.html 0444 root bin
+f usr/lib/help/auths/locale/SmfVscanStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfVtStates.html 0444 root bin
+f usr/lib/help/auths/locale/SmfWpaStates.html 0444 root bin
+f usr/lib/help/auths/locale/SysCpuPowerMgmt.html 0444 root bin
+f usr/lib/help/auths/locale/SysDate.html 0444 root bin
+f usr/lib/help/auths/locale/SysHeader.html 0444 root bin
+f usr/lib/help/auths/locale/SysMaintenance.html 0444 root bin
+f usr/lib/help/auths/locale/SysPowerMgmtBrightness.html 0444 root bin
+f usr/lib/help/auths/locale/SysPowerMgmtHeader.html 0444 root bin
+f usr/lib/help/auths/locale/SysPowerMgmtSuspend.html 0444 root bin
+f usr/lib/help/auths/locale/SysPowerMgmtSuspendtoDisk.html 0444 root bin
+f usr/lib/help/auths/locale/SysPowerMgmtSuspendtoRAM.html 0444 root bin
+f usr/lib/help/auths/locale/SysShutdown.html 0444 root bin
+f usr/lib/help/auths/locale/SysSyseventRead.html 0444 root bin
+f usr/lib/help/auths/locale/SysSyseventWrite.html 0444 root bin
+f usr/lib/help/auths/locale/TNDaemon.html 0444 root bin
+f usr/lib/help/auths/locale/TNctl.html 0444 root bin
+f usr/lib/help/auths/locale/ValueTND.html 0444 root bin
+f usr/lib/help/auths/locale/WifiConfig.html 0444 root bin
+f usr/lib/help/auths/locale/WifiWep.html 0444 root bin
+f usr/lib/help/auths/locale/ZoneCloneFrom.html 0444 root bin
+f usr/lib/help/auths/locale/ZoneHeader.html 0444 root bin
+f usr/lib/help/auths/locale/ZoneLogin.html 0444 root bin
+f usr/lib/help/auths/locale/ZoneManage.html 0444 root bin
+d usr/lib/help/profiles 0755 root bin
+d usr/lib/help/profiles/locale 0755 root bin
+d usr/lib/help/profiles/locale/C 0755 root bin
+f usr/lib/help/profiles/locale/C/RtAcctadm.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtAll.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtAuditCfg.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtAuditCtrl.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtAuditReview.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtCPUPowerManagement.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtConsUser.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtContractObserver.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtCronMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtCryptoMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtDHCPMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtDatAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtDefault.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtDeviceMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtDeviceSecurity.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtExAcctFlow.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtExAcctNet.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtExAcctProcess.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtExAcctTask.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtFTPMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtFileSysMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtFileSysSecurity.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtHotplugMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtIPFilterMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtIdmapMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtIdmapNameRulesMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtInetdMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtInfoSec.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtKerberosClntMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtKerberosSrvrMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtLogMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtMailMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtMaintAndRepair.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtMediaBkup.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtMediaCtlg.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtMediaRestore.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNDMPMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNameServiceAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNameServiceSecure.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetAutoconfAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetAutoconfUser.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetILB.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetIPsec.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetLinkSecure.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetObservability.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetSecure.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetVRRP.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetWifiMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtNetWifiSecure.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtObAccessMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtObjectLabelMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtOperator.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtOutsideAccred.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtPriAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtPrntAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtProcManagement.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtReparseMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtReservedProfile.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtRightsDelegate.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtSMBFSMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtSMBMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtSoftwareInstall.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtSysAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtSysEvMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtSysPowerMgmt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtSysPowerMgmtBrightness.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtSysPowerMgmtSuspend.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtSysPowerMgmtSuspendtoDisk.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtSysPowerMgmtSuspendtoRAM.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtUserMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtUserSecurity.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtVscanMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtZFSFileSysMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtZFSStorageMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtZoneMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/C/RtZoneSecurity.html 0444 root bin
+f usr/lib/help/profiles/locale/RtAcctadm.html 0444 root bin
+f usr/lib/help/profiles/locale/RtAll.html 0444 root bin
+f usr/lib/help/profiles/locale/RtAuditCfg.html 0444 root bin
+f usr/lib/help/profiles/locale/RtAuditCtrl.html 0444 root bin
+f usr/lib/help/profiles/locale/RtAuditReview.html 0444 root bin
+f usr/lib/help/profiles/locale/RtCPUPowerManagement.html 0444 root bin
+f usr/lib/help/profiles/locale/RtConsUser.html 0444 root bin
+f usr/lib/help/profiles/locale/RtContractObserver.html 0444 root bin
+f usr/lib/help/profiles/locale/RtCronMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtCryptoMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtDHCPMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtDatAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/RtDefault.html 0444 root bin
+f usr/lib/help/profiles/locale/RtDeviceMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtDeviceSecurity.html 0444 root bin
+f usr/lib/help/profiles/locale/RtExAcctFlow.html 0444 root bin
+f usr/lib/help/profiles/locale/RtExAcctNet.html 0444 root bin
+f usr/lib/help/profiles/locale/RtExAcctProcess.html 0444 root bin
+f usr/lib/help/profiles/locale/RtExAcctTask.html 0444 root bin
+f usr/lib/help/profiles/locale/RtFTPMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtFileSysMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtFileSysSecurity.html 0444 root bin
+f usr/lib/help/profiles/locale/RtHotplugMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtIPFilterMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtIdmapMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtIdmapNameRulesMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtInetdMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtInfoSec.html 0444 root bin
+f usr/lib/help/profiles/locale/RtKerberosClntMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtKerberosSrvrMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtLogMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtMailMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtMaintAndRepair.html 0444 root bin
+f usr/lib/help/profiles/locale/RtMediaBkup.html 0444 root bin
+f usr/lib/help/profiles/locale/RtMediaCtlg.html 0444 root bin
+f usr/lib/help/profiles/locale/RtMediaRestore.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNDMPMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNameServiceAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNameServiceSecure.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetAutoconfAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetAutoconfUser.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetILB.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetIPsec.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetLinkSecure.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetObservability.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetSecure.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetVRRP.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetWifiMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtNetWifiSecure.html 0444 root bin
+f usr/lib/help/profiles/locale/RtObAccessMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtObjectLabelMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtOperator.html 0444 root bin
+f usr/lib/help/profiles/locale/RtOutsideAccred.html 0444 root bin
+f usr/lib/help/profiles/locale/RtPriAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/RtPrntAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/RtProcManagement.html 0444 root bin
+f usr/lib/help/profiles/locale/RtReparseMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtReservedProfile.html 0444 root bin
+f usr/lib/help/profiles/locale/RtRightsDelegate.html 0444 root bin
+f usr/lib/help/profiles/locale/RtSMBFSMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtSMBMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtSoftwareInstall.html 0444 root bin
+f usr/lib/help/profiles/locale/RtSysAdmin.html 0444 root bin
+f usr/lib/help/profiles/locale/RtSysEvMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtSysPowerMgmt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtSysPowerMgmtBrightness.html 0444 root bin
+f usr/lib/help/profiles/locale/RtSysPowerMgmtSuspend.html 0444 root bin
+f usr/lib/help/profiles/locale/RtSysPowerMgmtSuspendtoDisk.html 0444 root bin
+f usr/lib/help/profiles/locale/RtSysPowerMgmtSuspendtoRAM.html 0444 root bin
+f usr/lib/help/profiles/locale/RtUserMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtUserSecurity.html 0444 root bin
+f usr/lib/help/profiles/locale/RtVscanMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtZFSFileSysMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtZFSStorageMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtZoneMngmnt.html 0444 root bin
+f usr/lib/help/profiles/locale/RtZoneSecurity.html 0444 root bin
+f usr/lib/hotplugd 0555 root bin
+d usr/lib/iconv 0755 root bin
+f usr/lib/iconv/646%UTF-8.so 0755 root bin
+f usr/lib/iconv/UCS-2%UTF-8.so 0755 root bin
+f usr/lib/iconv/UCS-2LE%UTF-8.so 0755 root bin
+f usr/lib/iconv/UTF-8%646.so 0755 root bin
+f usr/lib/iconv/UTF-8%UCS-2.so 0755 root bin
+f usr/lib/iconv/UTF-8%UCS-2LE.so 0755 root bin
+d usr/lib/iconv/amd64 0755 root bin
+f usr/lib/iconv/amd64/646%UTF-8.so 0755 root bin
+f usr/lib/iconv/amd64/UCS-2%UTF-8.so 0755 root bin
+f usr/lib/iconv/amd64/UCS-2LE%UTF-8.so 0755 root bin
+f usr/lib/iconv/amd64/UTF-8%646.so 0755 root bin
+f usr/lib/iconv/amd64/UTF-8%UCS-2.so 0755 root bin
+f usr/lib/iconv/amd64/UTF-8%UCS-2LE.so 0755 root bin
+f usr/lib/idmapd 0555 root bin
+d usr/lib/inet 0755 root bin
+d usr/lib/inet/amd64 0755 root bin
+f usr/lib/inet/amd64/in.iked 0555 root bin
+f usr/lib/inet/certdb 0555 root bin
+f usr/lib/inet/certlocal 0555 root bin
+f usr/lib/inet/certrldb 0555 root bin
+d usr/lib/inet/i86 0755 root bin
+d usr/lib/inet/ilb 0755 root bin
+f usr/lib/inet/ilb/ilb_probe 0555 root bin
+f usr/lib/inet/ilbd 0555 root bin
+f usr/lib/inet/in.chargend 0555 root bin
+f usr/lib/inet/in.daytimed 0555 root bin
+f usr/lib/inet/in.discardd 0555 root bin
+f usr/lib/inet/in.echod 0555 root bin
+h usr/lib/inet/in.iked=usr/lib/isaexec
+s usr/lib/inet/in.mpathd=../../../lib/inet/in.mpathd
+f usr/lib/inet/in.ndpd 0555 root bin
+f usr/lib/inet/in.ripngd 0555 root bin
+f usr/lib/inet/in.timed 0555 root bin
+f usr/lib/inet/inetd 0555 root bin
+f usr/lib/inet/mdnsd 0555 root bin
+d usr/lib/inet/ppp 0755 root bin
+f usr/lib/inet/ppp/minconn.so 0544 root bin
+f usr/lib/inet/ppp/passprompt.so 0544 root bin
+f usr/lib/inet/ppp/pppoe.so 0544 root bin
+f usr/lib/inet/pppoec 0555 root bin
+f usr/lib/inet/pppoed 0555 root bin
+f usr/lib/inet/slpd 0555 root bin
+f usr/lib/inet/vrrpd 0555 root bin
+f usr/lib/inet/wpad 0555 root bin
+f usr/lib/intrd 0555 root bin
+d usr/lib/ipf 0755 root bin
+f usr/lib/ipf/IPFILTER.LICENCE 0644 root bin
+d usr/lib/ipf/amd64 0755 root sys
+f usr/lib/ipf/amd64/ipftest 0555 root bin
+d usr/lib/ipf/i86 0755 root sys
+h usr/lib/ipf/ipftest=usr/lib/isaexec
+d usr/lib/ipqosconf 0755 root bin
+f usr/lib/ipqosconf/dlcosmk.types 0444 root bin
+f usr/lib/ipqosconf/dscpmk.types 0444 root bin
+f usr/lib/ipqosconf/flowacct.types 0444 root bin
+f usr/lib/ipqosconf/ipgpc.types 0444 root bin
+f usr/lib/ipqosconf/tokenmt.types 0444 root bin
+f usr/lib/ipqosconf/tswtclmt.types 0444 root bin
+f usr/lib/isaexec 0555 root bin
+d usr/lib/krb5 0755 root bin
+f usr/lib/krb5/README.db2 0444 root bin
+d usr/lib/krb5/amd64 0755 root bin
+f usr/lib/krb5/amd64/libkadm5clnt.so.1 0755 root bin
+s usr/lib/krb5/amd64/libkadm5clnt.so=libkadm5clnt.so.1
+f usr/lib/krb5/db2.so.1 0755 root bin
+s usr/lib/krb5/db2.so=db2.so.1
+f usr/lib/krb5/kadmind 0500 root bin
+f usr/lib/krb5/kconf 0555 root bin
+f usr/lib/krb5/kdyndns 0555 root bin
+f usr/lib/krb5/kldap.so.1 0755 root bin
+s usr/lib/krb5/kldap.so=kldap.so.1
+f usr/lib/krb5/klookup 0555 root bin
+f usr/lib/krb5/kprop 0555 root bin
+f usr/lib/krb5/kprop_script 0555 root bin
+f usr/lib/krb5/kpropd 0555 root bin
+f usr/lib/krb5/krb5kdc 0500 root bin
+f usr/lib/krb5/ksetpw 0555 root bin
+f usr/lib/krb5/ksmb 0555 root bin
+f usr/lib/krb5/ktkt_warnd 0555 root bin
+f usr/lib/krb5/libdb2.so.1 0755 root bin
+s usr/lib/krb5/libdb2.so=libdb2.so.1
+f usr/lib/krb5/libdyn.so.1 0755 root bin
+s usr/lib/krb5/libdyn.so=libdyn.so.1
+f usr/lib/krb5/libkadm5clnt.so.1 0755 root bin
+s usr/lib/krb5/libkadm5clnt.so=libkadm5clnt.so.1
+f usr/lib/krb5/libkadm5srv.so.1 0755 root bin
+s usr/lib/krb5/libkadm5srv.so=libkadm5srv.so.1
+f usr/lib/krb5/libkdb.so.1 0755 root bin
+s usr/lib/krb5/libkdb.so=libkdb.so.1
+f usr/lib/krb5/libkdb_ldap.so.1 0755 root bin
+s usr/lib/krb5/libkdb_ldap.so=libkdb_ldap.so.1
+f usr/lib/krb5/libss.so.1 0755 root bin
+s usr/lib/krb5/libss.so=libss.so.1
+d usr/lib/krb5/plugins 0755 root bin
+d usr/lib/krb5/plugins/preauth 0755 root bin
+f usr/lib/krb5/plugins/preauth/pkinit.so.1 0755 root bin
+s usr/lib/krb5/plugins/preauth/pkinit.so=pkinit.so.1
+f usr/lib/labeld 0555 root bin
+d usr/lib/ld 0755 root bin
+s usr/lib/ld.so.1=../../lib/ld.so.1
+d usr/lib/ld/amd64 0755 root bin
+s usr/lib/ld/amd64/map.above4G=../map.above4G
+s usr/lib/ld/amd64/map.below4G=../map.below4G
+s usr/lib/ld/amd64/map.default=../map.default
+f usr/lib/ld/map.above4G 0444 root bin
+f usr/lib/ld/map.below4G 0444 root bin
+f usr/lib/ld/map.bssalign 0444 root bin
+f usr/lib/ld/map.default 0444 root bin
+f usr/lib/ld/map.execdata 0444 root bin
+f usr/lib/ld/map.filter 0444 root bin
+f usr/lib/ld/map.noexbss 0444 root bin
+f usr/lib/ld/map.noexdata 0444 root bin
+f usr/lib/ld/map.noexstk 0444 root bin
+f usr/lib/ld/map.pagealign 0444 root bin
+d usr/lib/ldap 0755 root bin
+f usr/lib/ldap/idsconfig 0555 root bin
+f usr/lib/ldap/ldap_cachemgr 0555 root bin
+f usr/lib/lddstub 0555 root bin
+f usr/lib/lib.b 0444 root bin
+f usr/lib/libHBAAPI.so.1 0755 root bin
+s usr/lib/libHBAAPI.so=libHBAAPI.so.1
+s usr/lib/libMPAPI.so.1=../../lib/libMPAPI.so.1
+s usr/lib/libMPAPI.so=../../lib/libMPAPI.so.1
+f usr/lib/libSMHBAAPI.so.1 0755 root bin
+s usr/lib/libSMHBAAPI.so=libSMHBAAPI.so.1
+s usr/lib/libadm.so.1=../../lib/libadm.so.1
+s usr/lib/libadm.so=../../lib/libadm.so.1
+f usr/lib/libads.so.1 0755 root bin
+s usr/lib/libads.so=libads.so.1
+f usr/lib/libadt_jni.so.1 0755 root bin
+s usr/lib/libadt_jni.so=libadt_jni.so.1
+f usr/lib/libadutils.so.1 0755 root bin
+s usr/lib/libadutils.so=libadutils.so.1
+s usr/lib/libaio.so.1=../../lib/libaio.so.1
+s usr/lib/libaio.so=../../lib/libaio.so.1
+f usr/lib/libast.so.1 0755 root bin
+s usr/lib/libast.so=libast.so.1
+s usr/lib/libavl.so.1=../../lib/libavl.so.1
+s usr/lib/libavl.so=../../lib/libavl.so.1
+f usr/lib/libbe.so.1 0755 root bin
+s usr/lib/libbe.so=libbe.so.1
+f usr/lib/libbrand.so.1 0755 root bin
+s usr/lib/libbrand.so=libbrand.so.1
+f usr/lib/libbsdmalloc.so.1 0755 root bin
+s usr/lib/libbsdmalloc.so=libbsdmalloc.so.1
+s usr/lib/libbsm.so.1=../../lib/libbsm.so.1
+s usr/lib/libbsm.so=../../lib/libbsm.so.1
+f usr/lib/libbunyan.so.1 0755 root bin
+d usr/lib/libc 0755 root bin
+s usr/lib/libc.so.1=../../lib/libc.so.1
+s usr/lib/libc.so=../../lib/libc.so.1
+f usr/lib/libc/libc_hwcap1.so.1 0755 root bin
+f usr/lib/libc/libc_hwcap2.so.1 0755 root bin
+f usr/lib/libc/libc_hwcap3.so.1 0755 root bin
+s usr/lib/libc_db.so.1=../../lib/libc_db.so.1
+s usr/lib/libc_db.so=../../lib/libc_db.so.1
+f usr/lib/libcfgadm.so.1 0755 root bin
+s usr/lib/libcfgadm.so=libcfgadm.so.1
+f usr/lib/libcmd.so.1 0755 root bin
+s usr/lib/libcmd.so=libcmd.so.1
+s usr/lib/libcmdutils.so.1=../../lib/libcmdutils.so.1
+s usr/lib/libcmdutils.so=../../lib/libcmdutils.so.1
+f usr/lib/libcommputil.so.1 0755 root bin
+s usr/lib/libcommputil.so=libcommputil.so.1
+s usr/lib/libcontract.so.1=../../lib/libcontract.so.1
+s usr/lib/libcontract.so=../../lib/libcontract.so.1
+f usr/lib/libcpc.so.1 0755 root bin
+s usr/lib/libcpc.so=libcpc.so.1
+f usr/lib/libcrle.so.1 0755 root bin
+f usr/lib/libcrypt.so.1 0755 root bin
+s usr/lib/libcrypt.so=libcrypt.so.1
+s usr/lib/libcrypt_d.so.1=libcrypt.so.1
+s usr/lib/libcrypt_d.so=libcrypt.so
+s usr/lib/libcrypt_i.so.1=libcrypt.so.1
+s usr/lib/libcrypt_i.so=libcrypt.so
+s usr/lib/libcryptoutil.so.1=../../lib/libcryptoutil.so.1
+s usr/lib/libcryptoutil.so=../../lib/libcryptoutil.so.1
+s usr/lib/libctf.so.1=../../lib/libctf.so.1
+s usr/lib/libctf.so=../../lib/libctf.so.1
+s usr/lib/libcurses.so.1=../../lib/libcurses.so.1
+s usr/lib/libcurses.so=../../lib/libcurses.so.1
+f usr/lib/libdat.so.1 0755 root bin
+s usr/lib/libdat.so=libdat.so.1
+f usr/lib/libdemangle-sys.so.1 0755 root bin
+s usr/lib/libdemangle-sys.so=libdemangle-sys.so.1
+s usr/lib/libdevice.so.1=../../lib/libdevice.so.1
+s usr/lib/libdevice.so=../../lib/libdevice.so.1
+s usr/lib/libdevid.so.1=../../lib/libdevid.so.1
+s usr/lib/libdevid.so=../../lib/libdevid.so.1
+s usr/lib/libdevinfo.so.1=../../lib/libdevinfo.so.1
+s usr/lib/libdevinfo.so=../../lib/libdevinfo.so.1
+s usr/lib/libdhcpagent.so.1=../../lib/libdhcpagent.so.1
+s usr/lib/libdhcputil.so.1=../../lib/libdhcputil.so.1
+f usr/lib/libdisasm.so.1 0755 root bin
+s usr/lib/libdisasm.so=libdisasm.so.1
+f usr/lib/libdiskmgt.so.1 0755 root bin
+s usr/lib/libdiskmgt.so=libdiskmgt.so.1
+s usr/lib/libdl.so.1=../../lib/libdl.so.1
+s usr/lib/libdl.so=../../lib/libdl.so.1
+s usr/lib/libdladm.so.1=../../lib/libdladm.so.1
+s usr/lib/libdladm.so=../../lib/libdladm.so.1
+f usr/lib/libdll.so.1 0755 root bin
+s usr/lib/libdll.so=libdll.so.1
+s usr/lib/libdlpi.so.1=../../lib/libdlpi.so.1
+s usr/lib/libdlpi.so=../../lib/libdlpi.so.1
+f usr/lib/libdns_sd.so.1 0755 root bin
+s usr/lib/libdns_sd.so=libdns_sd.so.1
+s usr/lib/libdoor.so.1=../../lib/libdoor.so.1
+s usr/lib/libdoor.so=../../lib/libdoor.so.1
+f usr/lib/libdtrace.so.1 0755 root bin
+s usr/lib/libdtrace.so=libdtrace.so.1
+f usr/lib/libdtrace_jni.so.1 0755 root bin
+s usr/lib/libdtrace_jni.so=libdtrace_jni.so.1
+s usr/lib/libdwarf.so.1=../../lib/libdwarf.so.1
+s usr/lib/libefi.so.1=../../lib/libefi.so.1
+s usr/lib/libefi.so=../../lib/libefi.so.1
+s usr/lib/libelf.so.1=../../lib/libelf.so.1
+s usr/lib/libelf.so=../../lib/libelf.so.1
+s usr/lib/libelfsign.so.1=../../lib/libelfsign.so.1
+s usr/lib/libelfsign.so=../../lib/libelfsign.so.1
+f usr/lib/libexacct.so.1 0755 root bin
+s usr/lib/libexacct.so=libexacct.so.1
+s usr/lib/libfakekernel.so.1=../../../lib/libfakekernel.so.1
+f usr/lib/libfcoe.so.1 0755 root bin
+s usr/lib/libfcoe.so=libfcoe.so.1
+s usr/lib/libfdisk.so.1=../../lib/libfdisk.so.1
+s usr/lib/libfdisk.so=../../lib/libfdisk.so.1
+f usr/lib/libficl-sys.so.4.1.0 0755 root bin
+f usr/lib/libform.so.1 0755 root bin
+s usr/lib/libform.so=libform.so.1
+f usr/lib/libfru.so.1 0755 root bin
+s usr/lib/libfru.so=libfru.so.1
+f usr/lib/libfruraw.so.1 0755 root bin
+s usr/lib/libfruraw.so=libfruraw.so.1
+f usr/lib/libfrureg.so.1 0755 root bin
+s usr/lib/libfrureg.so=libfrureg.so.1
+f usr/lib/libfruutils.so.1 0755 root bin
+s usr/lib/libfruutils.so=libfruutils.so.1
+f usr/lib/libfsmgt.so.1 0755 root bin
+s usr/lib/libfsmgt.so=libfsmgt.so.1
+f usr/lib/libfstyp.so.1 0755 root bin
+s usr/lib/libfstyp.so=libfstyp.so.1
+s usr/lib/libgen.so.1=../../lib/libgen.so.1
+s usr/lib/libgen.so=../../lib/libgen.so.1
+s usr/lib/libgrubmgmt.so.1=../../lib/libgrubmgmt.so.1
+s usr/lib/libgrubmgmt.so=../../lib/libgrubmgmt.so.1
+f usr/lib/libgss.so.1 0755 root bin
+s usr/lib/libgss.so=libgss.so.1
+f usr/lib/libhotplug.so.1 0755 root bin
+s usr/lib/libhotplug.so=libhotplug.so.1
+f usr/lib/libidmap.so.1 0755 root bin
+s usr/lib/libidmap.so=libidmap.so.1
+f usr/lib/libidspace.so.1 0755 root bin
+f usr/lib/libike.so.1 0755 root bin
+s usr/lib/libike.so=libike.so.1
+f usr/lib/libilb.so.1 0755 root bin
+s usr/lib/libilb.so=libilb.so.1
+f usr/lib/libima.so.1 0755 root bin
+s usr/lib/libima.so=libima.so.1
+f usr/lib/libinetsvc.so.1 0755 root bin
+s usr/lib/libinetsvc.so=libinetsvc.so.1
+s usr/lib/libinetutil.so.1=../../lib/libinetutil.so.1
+s usr/lib/libinetutil.so=../../lib/libinetutil.so.1
+f usr/lib/libinstzones.so.1 0755 root bin
+s usr/lib/libinstzones.so=libinstzones.so.1
+s usr/lib/libintl.so.1=../../lib/libintl.so.1
+s usr/lib/libintl.so=../../lib/libintl.so.1
+s usr/lib/libipadm.so.1=../../lib/libipadm.so.1
+s usr/lib/libipadm.so=../../lib/libipadm.so.1
+f usr/lib/libipd.so.1 0755 root bin
+s usr/lib/libipd.so=libipd.so.1
+f usr/lib/libipmi.so.1 0755 root bin
+s usr/lib/libipmi.so=libipmi.so.1
+s usr/lib/libipmp.so.1=../../lib/libipmp.so.1
+s usr/lib/libipmp.so=../../lib/libipmp.so.1
+f usr/lib/libipp.so.1 0755 root bin
+s usr/lib/libipp.so=libipp.so.1
+f usr/lib/libipsecutil.so.1 0755 root bin
+s usr/lib/libipsecutil.so=libipsecutil.so.1
+f usr/lib/libiscsit.so.1 0755 root bin
+s usr/lib/libiscsit.so=libiscsit.so.1
+f usr/lib/libjedec.so.1 0755 root bin
+s usr/lib/libkcfd.so.1=../../lib/libkcfd.so.1
+s usr/lib/libkcfd.so=../../lib/libkcfd.so.1
+s usr/lib/libkmf.so.1=../../lib/libkmf.so.1
+s usr/lib/libkmf.so=../../lib/libkmf.so.1
+s usr/lib/libkmfberder.so.1=../../lib/libkmfberder.so.1
+s usr/lib/libkmfberder.so=../../lib/libkmfberder.so.1
+f usr/lib/libkrb5.so.1 0755 root bin
+s usr/lib/libkrb5.so=libkrb5.so.1
+s usr/lib/libkstat.so.1=../../lib/libkstat.so.1
+s usr/lib/libkstat.so=../../lib/libkstat.so.1
+f usr/lib/libkvm.so.1 0755 root bin
+s usr/lib/libkvm.so=libkvm.so.1
+f usr/lib/libl.so.1 0755 root bin
+s usr/lib/libl.so=libl.so.1
+f usr/lib/libldap.so.5 0755 root bin
+s usr/lib/libldap.so=libldap.so.5
+s usr/lib/liblddbg.so.4=../../lib/liblddbg.so.4
+f usr/lib/libldstab.so.1 0755 root bin
+f usr/lib/liblgrp.so.1 0755 root bin
+s usr/lib/liblgrp.so=liblgrp.so.1
+f usr/lib/liblm.so.1 0755 root bin
+s usr/lib/liblm.so=liblm.so.1
+s usr/lib/libm.so.1=../../lib/libm.so.1
+s usr/lib/libm.so.2=../../lib/libm.so.2
+s usr/lib/libm.so=../../lib/libm.so.2
+f usr/lib/libmail.so.1 0755 root bin
+s usr/lib/libmail.so=libmail.so.1
+f usr/lib/libmalloc.so.1 0755 root bin
+s usr/lib/libmalloc.so=libmalloc.so.1
+f usr/lib/libmapmalloc.so.1 0755 root bin
+s usr/lib/libmapmalloc.so=libmapmalloc.so.1
+s usr/lib/libmd.so.1=../../lib/libmd.so.1
+s usr/lib/libmd.so=../../lib/libmd.so.1
+s usr/lib/libmd5.so.1=../../lib/libmd5.so.1
+s usr/lib/libmd5.so=../../lib/libmd5.so.1
+s usr/lib/libmech_krb5.so=gss/mech_krb5.so
+f usr/lib/libmenu.so.1 0755 root bin
+s usr/lib/libmenu.so=libmenu.so.1
+f usr/lib/libmilter.so.1 0755 root bin
+s usr/lib/libmilter.so=libmilter.so.1
+f usr/lib/libmlrpc.so.2 0755 root bin
+s usr/lib/libmp.so.1=../../lib/libmp.so.1
+s usr/lib/libmp.so.2=../../lib/libmp.so.2
+s usr/lib/libmp.so=../../lib/libmp.so.2
+s usr/lib/libmpscsi_vhci.so.1=../../lib/libmpscsi_vhci.so.1
+s usr/lib/libmpscsi_vhci.so=../../lib/libmpscsi_vhci.so.1
+f usr/lib/libmtmalloc.so.1 0755 root bin
+s usr/lib/libmtmalloc.so=libmtmalloc.so.1
+s usr/lib/libmvec.so.1=../../lib/libmvec.so.1
+s usr/lib/libmvec.so=../../lib/libmvec.so.1
+f usr/lib/libndmp.so.1 0755 root bin
+s usr/lib/libndmp.so=libndmp.so.1
+f usr/lib/libnisdb.so.2 0755 root bin
+s usr/lib/libnisdb.so=libnisdb.so.2
+f usr/lib/libnls.so.1 0755 root bin
+s usr/lib/libnls.so=libnls.so.1
+s usr/lib/libnsl.so.1=../../lib/libnsl.so.1
+s usr/lib/libnsl.so=../../lib/libnsl.so.1
+f usr/lib/libnvfru.so.1 0755 root bin
+s usr/lib/libnvfru.so=libnvfru.so.1
+s usr/lib/libnvpair.so.1=../../lib/libnvpair.so.1
+s usr/lib/libnvpair.so=../../lib/libnvpair.so.1
+s usr/lib/libnwam.so.1=../../lib/libnwam.so.1
+s usr/lib/libnwam.so=../../lib/libnwam.so.1
+s usr/lib/libpam.so.1=../../lib/libpam.so.1
+s usr/lib/libpam.so=../../lib/libpam.so.1
+f usr/lib/libpanel.so.1 0755 root bin
+s usr/lib/libpanel.so=libpanel.so.1
+f usr/lib/libpcidb.so.1 0755 root bin
+f usr/lib/libpcsc.so.1 0755 root bin
+s usr/lib/libpcsc.so=libpcsc.so.1
+f usr/lib/libpctx.so.1 0755 root bin
+s usr/lib/libpctx.so=libpctx.so.1
+f usr/lib/libpicl.so.1 0755 root bin
+s usr/lib/libpicl.so=libpicl.so.1
+f usr/lib/libpicld_pluginutil.so.1 0755 root bin
+s usr/lib/libpicld_pluginutil.so=libpicld_pluginutil.so.1
+f usr/lib/libpicltree.so.1 0755 root bin
+s usr/lib/libpicltree.so=libpicltree.so.1
+f usr/lib/libpkcs11.so.1 0755 root bin
+s usr/lib/libpkcs11.so=libpkcs11.so.1
+f usr/lib/libpkg.so.1 0755 root bin
+s usr/lib/libpkg.so=libpkg.so.1
+f usr/lib/libpool.so.1 0755 root bin
+s usr/lib/libpool.so=libpool.so.1
+s usr/lib/libposix4.so.1=../../lib/librt.so.1
+s usr/lib/libposix4.so=../../lib/librt.so.1
+f usr/lib/libpp.so.1 0755 root bin
+s usr/lib/libpp.so=libpp.so.1
+f usr/lib/libppt.so.1 0755 root bin
+s usr/lib/libproc.so.1=../../lib/libproc.so.1
+s usr/lib/libproc.so=../../lib/libproc.so.1
+f usr/lib/libproject.so.1 0755 root bin
+s usr/lib/libproject.so=libproject.so.1
+s usr/lib/libpthread.so.1=../../lib/libpthread.so.1
+s usr/lib/libpthread.so=../../lib/libpthread.so.1
+f usr/lib/libraidcfg.so.1 0755 root bin
+s usr/lib/libraidcfg.so=libraidcfg.so.1
+s usr/lib/librcm.so.1=../../lib/librcm.so.1
+s usr/lib/librcm.so=../../lib/librcm.so.1
+f usr/lib/librefhash.so.1 0755 root bin
+f usr/lib/librename.so.1 0755 root bin
+f usr/lib/libreparse.so.1 0755 root bin
+s usr/lib/libreparse.so=libreparse.so.1
+s usr/lib/libresolv.so.1=../../lib/libresolv.so.1
+s usr/lib/libresolv.so.2=../../lib/libresolv.so.2
+s usr/lib/libresolv.so=../../lib/libresolv.so.2
+s usr/lib/libresolv_joy.so.2=../../lib/libresolv_joy.so.2
+s usr/lib/librestart.so.1=../../lib/librestart.so.1
+s usr/lib/librestart.so=../../lib/librestart.so.1
+s usr/lib/librpcsvc.so.1=../../lib/librpcsvc.so.1
+s usr/lib/librpcsvc.so=../../lib/librpcsvc.so.1
+f usr/lib/librsm.so.2 0755 root bin
+s usr/lib/librsm.so=librsm.so.2
+f usr/lib/librstp.so.1 0755 root bin
+s usr/lib/librstp.so=librstp.so.1
+s usr/lib/librt.so.1=../../lib/librt.so.1
+s usr/lib/librt.so=../../lib/librt.so.1
+s usr/lib/librtld.so.1=../../lib/librtld.so.1
+s usr/lib/librtld_db.so.1=../../lib/librtld_db.so.1
+s usr/lib/librtld_db.so=../../lib/librtld_db.so.1
+f usr/lib/libsasl.so.1 0755 root bin
+s usr/lib/libsasl.so=libsasl.so.1
+s usr/lib/libscf.so.1=../../lib/libscf.so.1
+s usr/lib/libscf.so=../../lib/libscf.so.1
+f usr/lib/libsched.so.1 0755 root bin
+s usr/lib/libsched.so=libsched.so.1
+f usr/lib/libsctp.so.1 0755 root bin
+s usr/lib/libsctp.so=libsctp.so.1
+s usr/lib/libsec.so.1=../../lib/libsec.so.1
+s usr/lib/libsec.so=../../lib/libsec.so.1
+s usr/lib/libsecdb.so.1=../../lib/libsecdb.so.1
+s usr/lib/libsecdb.so=../../lib/libsecdb.so.1
+s usr/lib/libsendfile.so.1=../../lib/libsendfile.so.1
+s usr/lib/libsendfile.so=../../lib/libsendfile.so.1
+f usr/lib/libsff.so.1 0755 root bin
+f usr/lib/libshare.so.1 0755 root bin
+s usr/lib/libshare.so=libshare.so.1
+f usr/lib/libshell.so.1 0755 root bin
+s usr/lib/libshell.so=libshell.so.1
+f usr/lib/libsip.so.1 0755 root bin
+s usr/lib/libsip.so=libsip.so.1
+f usr/lib/libsldap.so.1 0755 root bin
+s usr/lib/libsldap.so=libsldap.so.1
+f usr/lib/libslp.so.1 0755 root bin
+s usr/lib/libslp.so=libslp.so.1
+s usr/lib/libsmartsshd.so.1=../../lib/libsmartsshd.so.1
+s usr/lib/libsmartsshd.so=../../lib/libsmartsshd.so.1
+f usr/lib/libsmbfs.so.1 0755 root bin
+s usr/lib/libsmbfs.so=libsmbfs.so.1
+f usr/lib/libsmbios.so.1 0755 root bin
+s usr/lib/libsmbios.so=libsmbios.so.1
+f usr/lib/libsmedia.so.1 0755 root bin
+s usr/lib/libsmedia.so=libsmedia.so.1
+s usr/lib/libsocket.so.1=../../lib/libsocket.so.1
+s usr/lib/libsocket.so=../../lib/libsocket.so.1
+f usr/lib/libsoftcrypto.so.1 0755 root bin
+s usr/lib/libsoftcrypto.so=libsoftcrypto.so.1
+f usr/lib/libsrpt.so.1 0755 root bin
+s usr/lib/libsrpt.so=libsrpt.so.1
+f usr/lib/libssp_ns.a 0755 root bin
+f usr/lib/libstmf.so.1 0755 root bin
+s usr/lib/libstmf.so=libstmf.so.1
+f usr/lib/libstmfproxy.so.1 0755 root bin
+s usr/lib/libstmfproxy.so=libstmfproxy.so.1
+f usr/lib/libsum.so.1 0755 root bin
+s usr/lib/libsum.so=libsum.so.1
+f usr/lib/libsun_fc.so.1 0755 root bin
+s usr/lib/libsun_fc.so=libsun_fc.so.1
+f usr/lib/libsun_ima.so.1 0755 root bin
+s usr/lib/libsun_ima.so=libsun_ima.so.1
+f usr/lib/libsun_sas.so.1 0755 root bin
+s usr/lib/libsun_sas.so=libsun_sas.so.1
+s usr/lib/libsysevent.so.1=../../lib/libsysevent.so.1
+s usr/lib/libsysevent.so=../../lib/libsysevent.so.1
+f usr/lib/libtecla.so.1 0755 root bin
+s usr/lib/libtecla.so=libtecla.so.1
+s usr/lib/libtermcap.so.1=../../lib/libtermcap.so.1
+s usr/lib/libtermcap.so=../../lib/libtermcap.so.1
+s usr/lib/libtermlib.so.1=../../lib/libcurses.so.1
+s usr/lib/libtermlib.so=../../lib/libcurses.so.1
+s usr/lib/libthread.so.1=../../lib/libthread.so.1
+s usr/lib/libthread.so=../../lib/libthread.so.1
+s usr/lib/libthread_db.so.1=../../lib/libc_db.so.1
+s usr/lib/libthread_db.so=../../lib/libc_db.so.1
+s usr/lib/libtsnet.so.1=../../lib/libtsnet.so.1
+s usr/lib/libtsnet.so=../../lib/libtsnet.so.1
+s usr/lib/libtsol.so.2=../../lib/libtsol.so.2
+s usr/lib/libtsol.so=../../lib/libtsol.so.2
+s usr/lib/libumem.so.1=../../lib/libumem.so.1
+s usr/lib/libumem.so=../../lib/libumem.so.1
+f usr/lib/libutempter.so.1 0755 root bin
+s usr/lib/libutempter.so=libutempter.so.1
+s usr/lib/libuuid.so.1=../../lib/libuuid.so.1
+s usr/lib/libuuid.so=../../lib/libuuid.so.1
+s usr/lib/libuutil.so.1=../../lib/libuutil.so.1
+s usr/lib/libuutil.so=../../lib/libuutil.so.1
+f usr/lib/libvnd.so.1 0755 root bin
+s usr/lib/libvnd.so=libvnd.so.1
+f usr/lib/libvolmgt.so.1 0755 root bin
+s usr/lib/libvolmgt.so=libvolmgt.so.1
+f usr/lib/libvrrpadm.so.1 0755 root bin
+s usr/lib/libvrrpadm.so=libvrrpadm.so.1
+s usr/lib/libw.so.1=../../lib/libw.so.1
+s usr/lib/libw.so=../../lib/libw.so.1
+f usr/lib/libwrap.so.1.0 0755 root bin
+s usr/lib/libwrap.so.1=libwrap.so.1.0
+s usr/lib/libwrap.so=libwrap.so.1.0
+s usr/lib/libxnet.so.1=../../lib/libxnet.so.1
+s usr/lib/libxnet.so=../../lib/libxnet.so.1
+f usr/lib/liby.so.1 0755 root bin
+s usr/lib/liby.so=liby.so.1
+s usr/lib/libzdoor.so.1=../../lib/libzdoor.so.1
+s usr/lib/libzdoor.so=../../lib/libzdoor.so.1
+s usr/lib/libzfs.so.1=../../lib/libzfs.so.1
+s usr/lib/libzfs.so=../../lib/libzfs.so.1
+f usr/lib/libzfs_core.so.1 755 root bin
+s usr/lib/libzfs_core.so=libzfs_core.so.1
+f usr/lib/libzfs_jni.so.1 0755 root bin
+s usr/lib/libzfs_jni.so=libzfs_jni.so.1
+f usr/lib/libzonecfg.so.1 0755 root bin
+s usr/lib/libzonecfg.so=libzonecfg.so.1
+f usr/lib/libzoneinfo.so.1 0755 root bin
+s usr/lib/libzoneinfo.so=libzoneinfo.so.1
+f usr/lib/libzpool.so.1 0755 root bin
+s usr/lib/libzpool.so=libzpool.so.1
+s usr/lib/libzutil.so.1=../../lib/libzutil.so.1
+s usr/lib/libzutil.so=../../lib/libzutil.so.1
+d usr/lib/link_audit 0755 root bin
+s usr/lib/link_audit/32=.
+s usr/lib/link_audit/64=amd64
+d usr/lib/link_audit/amd64 0755 root bin
+f usr/lib/link_audit/amd64/ldprof.so.1 0755 root bin
+f usr/lib/link_audit/amd64/truss.so.1 0755 root bin
+f usr/lib/link_audit/amd64/who.so.1 0755 root bin
+f usr/lib/link_audit/ldprof.so.1 0755 root bin
+f usr/lib/link_audit/truss.so.1 0755 root bin
+f usr/lib/link_audit/who.so.1 0755 root bin
+d usr/lib/locale 0755 root bin
+d usr/lib/locale/C 0755 root bin
+d usr/lib/locale/C.UTF-8 0755 root bin
+d usr/lib/locale/C.UTF-8/LC_COLLATE 0755 root bin
+d usr/lib/locale/C.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/C.UTF-8/LC_CTYPE/LCL_DATA 0444 root bin
+d usr/lib/locale/C.UTF-8/LC_MESSAGES 0755 root bin
+d usr/lib/locale/C.UTF-8/LC_MONETARY 0755 root bin
+d usr/lib/locale/C.UTF-8/LC_NUMERIC 0755 root bin
+d usr/lib/locale/C.UTF-8/LC_TIME 0755 root bin
+d usr/lib/locale/C/LC_COLLATE 0755 root bin
+d usr/lib/locale/C/LC_CTYPE 0755 root bin
+d usr/lib/locale/C/LC_MESSAGES 0755 root bin
+f usr/lib/locale/C/LC_MESSAGES/AMD.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/AMD.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/DISK.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/DISK.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/FMD.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/FMD.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/FMNOTIFY.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/FMNOTIFY.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/GMCA.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/GMCA.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/INTEL.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/INTEL.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/NIC.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/NIC.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/NXGE.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/NXGE.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/PCI.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/PCI.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/PCIEX.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/PCIEX.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SCA1000.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/SCA1000.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SCA500.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/SCA500.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SENSOR.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/SENSOR.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SMF.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/SMF.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/STORAGE.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/STORAGE.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNOS.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNOS.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNW_OST_ADMIN.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNW_OST_LINFO 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNW_OST_NETRPC.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNW_OST_OSCMD.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNW_OST_OSLIB.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNW_OST_SGS.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNW_OST_SYSOSPAM.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNW_OST_UCBCMD.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/SUNW_OST_ZONEINFO.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/TEST.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/TEST.po 0644 root bin
+f usr/lib/locale/C/LC_MESSAGES/ZFS.mo 0444 root bin
+f usr/lib/locale/C/LC_MESSAGES/ZFS.po 0644 root bin
+d usr/lib/locale/C/LC_MONETARY 0755 root bin
+d usr/lib/locale/C/LC_NUMERIC 0755 root bin
+d usr/lib/locale/C/LC_TIME 0755 root bin
+f usr/lib/locale/C/LC_TIME/SUNW_OST_OSCMD.po 0644 root bin
+s usr/lib/locale/POSIX=C
+d usr/lib/locale/af_ZA.UTF-8 0755 root bin
+d usr/lib/locale/af_ZA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/af_ZA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/af_ZA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/af_ZA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/af_ZA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/af_ZA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/af_ZA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/af_ZA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/af_ZA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/af_ZA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/af_ZA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/af_ZA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_AE.UTF-8 0755 root bin
+d usr/lib/locale/ar_AE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_AE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_AE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_AE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_AE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_AE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_AE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_AE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_AE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_AE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_AE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_AE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_BH.UTF-8 0755 root bin
+d usr/lib/locale/ar_BH.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_BH.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_BH.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_BH.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_BH.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_BH.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_BH.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_BH.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_BH.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_BH.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_BH.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_BH.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_DZ.UTF-8 0755 root bin
+d usr/lib/locale/ar_DZ.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_DZ.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_DZ.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_DZ.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_DZ.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_DZ.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_DZ.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_DZ.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_DZ.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_DZ.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_DZ.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_DZ.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_EG.UTF-8 0755 root bin
+d usr/lib/locale/ar_EG.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_EG.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_EG.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_EG.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_EG.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_EG.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_EG.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_EG.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_EG.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_EG.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_EG.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_EG.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_IQ.UTF-8 0755 root bin
+d usr/lib/locale/ar_IQ.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_IQ.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_IQ.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_IQ.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_IQ.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_IQ.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_IQ.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_IQ.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_IQ.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_IQ.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_IQ.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_IQ.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_JO.UTF-8 0755 root bin
+d usr/lib/locale/ar_JO.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_JO.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_JO.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_JO.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_JO.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_JO.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_JO.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_JO.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_JO.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_JO.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_JO.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_JO.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_KW.UTF-8 0755 root bin
+d usr/lib/locale/ar_KW.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_KW.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_KW.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_KW.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_KW.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_KW.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_KW.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_KW.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_KW.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_KW.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_KW.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_KW.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LB.UTF-8 0755 root bin
+d usr/lib/locale/ar_LB.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_LB.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LB.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_LB.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LB.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_LB.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LB.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_LB.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LB.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_LB.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LB.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_LB.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LY.UTF-8 0755 root bin
+d usr/lib/locale/ar_LY.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_LY.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LY.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_LY.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LY.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_LY.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LY.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_LY.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LY.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_LY.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_LY.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_LY.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_MA.UTF-8 0755 root bin
+d usr/lib/locale/ar_MA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_MA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_MA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_MA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_MA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_MA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_MA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_MA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_MA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_MA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_MA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_MA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_OM.UTF-8 0755 root bin
+d usr/lib/locale/ar_OM.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_OM.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_OM.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_OM.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_OM.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_OM.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_OM.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_OM.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_OM.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_OM.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_OM.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_OM.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_QA.UTF-8 0755 root bin
+d usr/lib/locale/ar_QA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_QA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_QA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_QA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_QA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_QA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_QA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_QA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_QA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_QA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_QA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_QA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_SA.UTF-8 0755 root bin
+d usr/lib/locale/ar_SA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_SA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_SA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_SA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_SA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_SA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_SA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_SA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_SA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_SA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_SA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_SA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_TN.UTF-8 0755 root bin
+d usr/lib/locale/ar_TN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_TN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_TN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_TN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_TN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_TN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_TN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_TN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_TN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_TN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_TN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_TN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_YE.UTF-8 0755 root bin
+d usr/lib/locale/ar_YE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ar_YE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_YE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ar_YE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_YE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ar_YE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_YE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ar_YE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_YE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ar_YE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ar_YE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ar_YE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/as_IN.UTF-8 0755 root bin
+d usr/lib/locale/as_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/as_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/as_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/as_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/as_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/as_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/as_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/as_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/as_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/as_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/as_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/as_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/az_AZ.UTF-8 0755 root bin
+d usr/lib/locale/az_AZ.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/az_AZ.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/az_AZ.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/az_AZ.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/az_AZ.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/az_AZ.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/az_AZ.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/az_AZ.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/az_AZ.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/az_AZ.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/az_AZ.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/az_AZ.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/be_BY.UTF-8 0755 root bin
+d usr/lib/locale/be_BY.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/be_BY.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/be_BY.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/be_BY.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/be_BY.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/be_BY.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/be_BY.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/be_BY.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/be_BY.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/be_BY.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/be_BY.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/be_BY.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.ISO8859-5 0755 root bin
+d usr/lib/locale/bg_BG.ISO8859-5/LC_COLLATE 0755 root bin
+f usr/lib/locale/bg_BG.ISO8859-5/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.ISO8859-5/LC_CTYPE 0755 root bin
+f usr/lib/locale/bg_BG.ISO8859-5/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.ISO8859-5/LC_MESSAGES 0755 root bin
+f usr/lib/locale/bg_BG.ISO8859-5/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.ISO8859-5/LC_MONETARY 0755 root bin
+f usr/lib/locale/bg_BG.ISO8859-5/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.ISO8859-5/LC_NUMERIC 0755 root bin
+f usr/lib/locale/bg_BG.ISO8859-5/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.ISO8859-5/LC_TIME 0755 root bin
+f usr/lib/locale/bg_BG.ISO8859-5/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.UTF-8 0755 root bin
+d usr/lib/locale/bg_BG.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/bg_BG.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/bg_BG.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/bg_BG.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/bg_BG.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/bg_BG.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/bg_BG.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/bg_BG.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_BD.UTF-8 0755 root bin
+d usr/lib/locale/bn_BD.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/bn_BD.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_BD.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/bn_BD.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_BD.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/bn_BD.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_BD.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/bn_BD.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_BD.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/bn_BD.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_BD.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/bn_BD.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_IN.UTF-8 0755 root bin
+d usr/lib/locale/bn_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/bn_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/bn_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/bn_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/bn_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/bn_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/bn_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/bn_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_CN.UTF-8 0755 root bin
+d usr/lib/locale/bo_CN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/bo_CN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_CN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/bo_CN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_CN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/bo_CN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_CN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/bo_CN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_CN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/bo_CN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_CN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/bo_CN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_IN.UTF-8 0755 root bin
+d usr/lib/locale/bo_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/bo_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/bo_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/bo_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/bo_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/bo_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/bo_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/bo_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/bs_BA.UTF-8 0755 root bin
+d usr/lib/locale/bs_BA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/bs_BA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/bs_BA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/bs_BA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/bs_BA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/bs_BA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/bs_BA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/bs_BA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/bs_BA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/bs_BA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/bs_BA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/bs_BA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.ISO8859-15 0755 root bin
+d usr/lib/locale/ca_ES.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/ca_ES.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/ca_ES.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ca_ES.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/ca_ES.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ca_ES.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/ca_ES.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.UTF-8 0755 root bin
+d usr/lib/locale/ca_ES.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ca_ES.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ca_ES.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ca_ES.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ca_ES.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ca_ES.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ca_ES.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ca_ES.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.ISO8859-2 0755 root bin
+d usr/lib/locale/cs_CZ.ISO8859-2/LC_COLLATE 0755 root bin
+f usr/lib/locale/cs_CZ.ISO8859-2/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.ISO8859-2/LC_CTYPE 0755 root bin
+f usr/lib/locale/cs_CZ.ISO8859-2/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.ISO8859-2/LC_MESSAGES 0755 root bin
+f usr/lib/locale/cs_CZ.ISO8859-2/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.ISO8859-2/LC_MONETARY 0755 root bin
+f usr/lib/locale/cs_CZ.ISO8859-2/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.ISO8859-2/LC_NUMERIC 0755 root bin
+f usr/lib/locale/cs_CZ.ISO8859-2/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.ISO8859-2/LC_TIME 0755 root bin
+f usr/lib/locale/cs_CZ.ISO8859-2/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.UTF-8 0755 root bin
+d usr/lib/locale/cs_CZ.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/cs_CZ.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/cs_CZ.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/cs_CZ.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/cs_CZ.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/cs_CZ.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/cs_CZ.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/cs_CZ.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-1 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-15 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/da_DK.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.UTF-8 0755 root bin
+d usr/lib/locale/da_DK.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/da_DK.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/da_DK.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/da_DK.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/da_DK.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/da_DK.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/da_DK.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/da_DK.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.ISO8859-15 0755 root bin
+d usr/lib/locale/de_AT.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/de_AT.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/de_AT.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/de_AT.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/de_AT.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/de_AT.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/de_AT.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.UTF-8 0755 root bin
+d usr/lib/locale/de_AT.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/de_AT.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/de_AT.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/de_AT.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/de_AT.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/de_AT.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/de_AT.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/de_AT.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/de_BE.UTF-8 0755 root bin
+d usr/lib/locale/de_BE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/de_BE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_BE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/de_BE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_BE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/de_BE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/de_BE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/de_BE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/de_BE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/de_BE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/de_BE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/de_BE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.ISO8859-1 0755 root bin
+d usr/lib/locale/de_CH.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/de_CH.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/de_CH.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/de_CH.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/de_CH.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/de_CH.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/de_CH.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.UTF-8 0755 root bin
+d usr/lib/locale/de_CH.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/de_CH.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/de_CH.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/de_CH.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/de_CH.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/de_CH.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/de_CH.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/de_CH.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-1 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-1/LC_COLLATE 0755 root bin
+h usr/lib/locale/de_DE.ISO8859-1/LC_COLLATE/LCL_DATA=usr/lib/locale/de_CH.ISO8859-1/LC_COLLATE/LCL_DATA
+d usr/lib/locale/de_DE.ISO8859-1/LC_CTYPE 0755 root bin
+h usr/lib/locale/de_DE.ISO8859-1/LC_CTYPE/LCL_DATA=usr/lib/locale/da_DK.ISO8859-1/LC_CTYPE/LCL_DATA
+d usr/lib/locale/de_DE.ISO8859-1/LC_MESSAGES 0755 root bin
+h usr/lib/locale/de_DE.ISO8859-1/LC_MESSAGES/LCL_DATA=usr/lib/locale/de_AT.ISO8859-15/LC_MESSAGES/LCL_DATA
+d usr/lib/locale/de_DE.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/de_DE.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/de_DE.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/de_DE.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-15 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/de_DE.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/de_DE.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/de_DE.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/de_DE.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/de_DE.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/de_DE.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.UTF-8 0755 root bin
+d usr/lib/locale/de_DE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/de_DE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/de_DE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/de_DE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/de_DE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/de_DE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/de_DE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/de_DE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LI.UTF-8 0755 root bin
+d usr/lib/locale/de_LI.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/de_LI.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LI.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/de_LI.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LI.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/de_LI.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LI.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/de_LI.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LI.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/de_LI.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LI.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/de_LI.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LU.UTF-8 0755 root bin
+d usr/lib/locale/de_LU.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/de_LU.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LU.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/de_LU.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LU.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/de_LU.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LU.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/de_LU.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LU.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/de_LU.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/de_LU.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/de_LU.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/el_CY.UTF-8 0755 root bin
+d usr/lib/locale/el_CY.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/el_CY.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/el_CY.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/el_CY.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/el_CY.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/el_CY.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/el_CY.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/el_CY.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/el_CY.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/el_CY.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/el_CY.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/el_CY.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.ISO8859-7 0755 root bin
+d usr/lib/locale/el_GR.ISO8859-7/LC_COLLATE 0755 root bin
+f usr/lib/locale/el_GR.ISO8859-7/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.ISO8859-7/LC_CTYPE 0755 root bin
+f usr/lib/locale/el_GR.ISO8859-7/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.ISO8859-7/LC_MESSAGES 0755 root bin
+f usr/lib/locale/el_GR.ISO8859-7/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.ISO8859-7/LC_MONETARY 0755 root bin
+f usr/lib/locale/el_GR.ISO8859-7/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.ISO8859-7/LC_NUMERIC 0755 root bin
+f usr/lib/locale/el_GR.ISO8859-7/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.ISO8859-7/LC_TIME 0755 root bin
+f usr/lib/locale/el_GR.ISO8859-7/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.UTF-8 0755 root bin
+d usr/lib/locale/el_GR.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/el_GR.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/el_GR.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/el_GR.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/el_GR.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/el_GR.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/el_GR.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/el_GR.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.ISO8859-1 0755 root bin
+d usr/lib/locale/en_AU.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_AU.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_AU.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_AU.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_AU.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_AU.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/en_AU.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.UTF-8 0755 root bin
+d usr/lib/locale/en_AU.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_AU.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_AU.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_AU.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_AU.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_AU.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_AU.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_AU.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BW.UTF-8 0755 root bin
+d usr/lib/locale/en_BW.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_BW.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BW.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_BW.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BW.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_BW.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BW.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_BW.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BW.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_BW.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BW.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_BW.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BZ.UTF-8 0755 root bin
+d usr/lib/locale/en_BZ.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_BZ.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BZ.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_BZ.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BZ.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_BZ.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BZ.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_BZ.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BZ.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_BZ.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_BZ.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_BZ.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.ISO8859-1 0755 root bin
+d usr/lib/locale/en_CA.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_CA.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_CA.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_CA.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_CA.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_CA.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/en_CA.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.UTF-8 0755 root bin
+d usr/lib/locale/en_CA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_CA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_CA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_CA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_CA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_CA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_CA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_CA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-1 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-15 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/en_GB.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.UTF-8 0755 root bin
+d usr/lib/locale/en_GB.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_GB.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_GB.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_GB.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_GB.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_GB.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_GB.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_GB.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_HK.UTF-8 0755 root bin
+d usr/lib/locale/en_HK.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_HK.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_HK.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_HK.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_HK.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_HK.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_HK.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_HK.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_HK.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_HK.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_HK.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_HK.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.ISO8859-15 0755 root bin
+d usr/lib/locale/en_IE.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_IE.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_IE.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_IE.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_IE.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_IE.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/en_IE.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.UTF-8 0755 root bin
+d usr/lib/locale/en_IE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_IE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_IE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_IE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_IE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_IE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_IE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IN.UTF-8 0755 root bin
+d usr/lib/locale/en_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_JM.UTF-8 0755 root bin
+d usr/lib/locale/en_JM.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_JM.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_JM.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_JM.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_JM.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_JM.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_JM.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_JM.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_JM.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_JM.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_JM.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_JM.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MH.UTF-8 0755 root bin
+d usr/lib/locale/en_MH.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_MH.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MH.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_MH.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MH.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_MH.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MH.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_MH.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MH.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_MH.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MH.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_MH.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MT.UTF-8 0755 root bin
+d usr/lib/locale/en_MT.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_MT.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MT.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_MT.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MT.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_MT.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MT.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_MT.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MT.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_MT.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_MT.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_MT.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NA.UTF-8 0755 root bin
+d usr/lib/locale/en_NA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_NA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_NA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_NA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_NA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_NA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_NA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.ISO8859-1 0755 root bin
+d usr/lib/locale/en_NZ.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_NZ.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_NZ.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_NZ.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_NZ.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_NZ.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/en_NZ.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.UTF-8 0755 root bin
+d usr/lib/locale/en_NZ.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_NZ.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_NZ.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_NZ.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_NZ.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_NZ.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_NZ.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_NZ.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PH.UTF-8 0755 root bin
+d usr/lib/locale/en_PH.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_PH.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PH.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_PH.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PH.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_PH.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PH.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_PH.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PH.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_PH.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PH.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_PH.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PK.UTF-8 0755 root bin
+d usr/lib/locale/en_PK.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_PK.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PK.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_PK.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PK.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_PK.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PK.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_PK.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PK.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_PK.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_PK.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_PK.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_SG.UTF-8 0755 root bin
+d usr/lib/locale/en_SG.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_SG.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_SG.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_SG.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_SG.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_SG.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_SG.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_SG.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_SG.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_SG.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_SG.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_SG.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_TT.UTF-8 0755 root bin
+d usr/lib/locale/en_TT.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_TT.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_TT.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_TT.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_TT.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_TT.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_TT.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_TT.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_TT.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_TT.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_TT.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_TT.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.ISO8859-1 0755 root bin
+d usr/lib/locale/en_US.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_US.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_US.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_US.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_US.ISO8859-1/LC_MONETARY/LCL_DATA 0444 root bin
+d usr/lib/locale/en_US.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_US.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/en_US.ISO8859-1/LC_TIME/LCL_DATA 0444 root bin
+d usr/lib/locale/en_US.ISO8859-15 0755 root bin
+d usr/lib/locale/en_US.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_US.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_US.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_US.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_US.ISO8859-15/LC_MONETARY/LCL_DATA 0444 root bin
+d usr/lib/locale/en_US.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_US.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/en_US.ISO8859-15/LC_TIME/LCL_DATA 0444 root bin
+d usr/lib/locale/en_US.UTF-8 0755 root bin
+d usr/lib/locale/en_US.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_US.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_US.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_US.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_US.UTF-8/LC_MONETARY/LCL_DATA 0444 root bin
+d usr/lib/locale/en_US.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_US.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_US.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_US.UTF-8/LC_TIME/LCL_DATA 0444 root bin
+d usr/lib/locale/en_ZA.UTF-8 0755 root bin
+d usr/lib/locale/en_ZA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_ZA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_ZA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_ZA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_ZA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_ZA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_ZA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZW.UTF-8 0755 root bin
+d usr/lib/locale/en_ZW.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/en_ZW.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZW.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/en_ZW.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZW.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/en_ZW.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZW.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/en_ZW.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZW.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/en_ZW.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/en_ZW.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/en_ZW.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.ISO8859-1 0755 root bin
+d usr/lib/locale/es_AR.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_AR.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_AR.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_AR.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_AR.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_AR.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_AR.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.UTF-8 0755 root bin
+d usr/lib/locale/es_AR.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_AR.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_AR.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_AR.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_AR.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_AR.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_AR.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_AR.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.ISO8859-1 0755 root bin
+d usr/lib/locale/es_BO.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_BO.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_BO.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_BO.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_BO.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_BO.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_BO.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.UTF-8 0755 root bin
+d usr/lib/locale/es_BO.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_BO.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_BO.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_BO.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_BO.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_BO.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_BO.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_BO.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.ISO8859-1 0755 root bin
+d usr/lib/locale/es_CL.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_CL.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_CL.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_CL.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_CL.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_CL.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_CL.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.UTF-8 0755 root bin
+d usr/lib/locale/es_CL.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_CL.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_CL.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_CL.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_CL.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_CL.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CL.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_CL.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.ISO8859-1 0755 root bin
+d usr/lib/locale/es_CO.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_CO.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_CO.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_CO.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_CO.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_CO.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_CO.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.UTF-8 0755 root bin
+d usr/lib/locale/es_CO.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_CO.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_CO.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_CO.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_CO.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_CO.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CO.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_CO.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CR.UTF-8 0755 root bin
+d usr/lib/locale/es_CR.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_CR.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CR.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_CR.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CR.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_CR.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CR.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_CR.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CR.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_CR.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_CR.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_CR.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_DO.UTF-8 0755 root bin
+d usr/lib/locale/es_DO.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_DO.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_DO.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_DO.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_DO.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_DO.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_DO.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_DO.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_DO.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_DO.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_DO.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_DO.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.ISO8859-1 0755 root bin
+d usr/lib/locale/es_EC.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_EC.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_EC.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_EC.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_EC.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_EC.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_EC.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.UTF-8 0755 root bin
+d usr/lib/locale/es_EC.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_EC.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_EC.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_EC.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_EC.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_EC.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_EC.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_EC.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-1 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_ES.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-1/LC_CTYPE 0755 root bin
+h usr/lib/locale/es_ES.ISO8859-1/LC_CTYPE/LCL_DATA=usr/lib/locale/da_DK.ISO8859-1/LC_CTYPE/LCL_DATA
+d usr/lib/locale/es_ES.ISO8859-1/LC_MESSAGES 0755 root bin
+h usr/lib/locale/es_ES.ISO8859-1/LC_MESSAGES/LCL_DATA=usr/lib/locale/es_AR.ISO8859-1/LC_MESSAGES/LCL_DATA
+d usr/lib/locale/es_ES.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_ES.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_ES.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_ES.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-15 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_ES.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_ES.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_ES.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_ES.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_ES.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/es_ES.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.UTF-8 0755 root bin
+d usr/lib/locale/es_ES.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_ES.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_ES.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_ES.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_ES.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_ES.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_ES.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_ES.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GQ.UTF-8 0755 root bin
+d usr/lib/locale/es_GQ.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_GQ.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GQ.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_GQ.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GQ.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_GQ.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GQ.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_GQ.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GQ.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_GQ.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GQ.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_GQ.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.ISO8859-1 0755 root bin
+d usr/lib/locale/es_GT.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_GT.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_GT.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_GT.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_GT.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_GT.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_GT.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.UTF-8 0755 root bin
+d usr/lib/locale/es_GT.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_GT.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_GT.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_GT.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_GT.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_GT.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_GT.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_GT.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_HN.UTF-8 0755 root bin
+d usr/lib/locale/es_HN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_HN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_HN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_HN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_HN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_HN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_HN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_HN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_HN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_HN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_HN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_HN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.ISO8859-1 0755 root bin
+d usr/lib/locale/es_MX.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_MX.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_MX.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_MX.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_MX.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_MX.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_MX.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.UTF-8 0755 root bin
+d usr/lib/locale/es_MX.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_MX.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_MX.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_MX.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_MX.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_MX.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_MX.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_MX.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.ISO8859-1 0755 root bin
+d usr/lib/locale/es_NI.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_NI.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_NI.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_NI.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_NI.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_NI.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_NI.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.UTF-8 0755 root bin
+d usr/lib/locale/es_NI.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_NI.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_NI.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_NI.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_NI.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_NI.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_NI.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_NI.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.ISO8859-1 0755 root bin
+d usr/lib/locale/es_PA.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_PA.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_PA.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_PA.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_PA.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_PA.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_PA.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.UTF-8 0755 root bin
+d usr/lib/locale/es_PA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_PA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_PA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_PA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_PA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_PA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_PA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.ISO8859-1 0755 root bin
+d usr/lib/locale/es_PE.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_PE.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_PE.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_PE.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_PE.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_PE.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_PE.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.UTF-8 0755 root bin
+d usr/lib/locale/es_PE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_PE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_PE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_PE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_PE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_PE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_PE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PR.UTF-8 0755 root bin
+d usr/lib/locale/es_PR.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_PR.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PR.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_PR.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PR.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_PR.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PR.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_PR.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PR.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_PR.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PR.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_PR.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PY.UTF-8 0755 root bin
+d usr/lib/locale/es_PY.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_PY.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PY.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_PY.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PY.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_PY.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PY.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_PY.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PY.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_PY.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_PY.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_PY.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.ISO8859-1 0755 root bin
+d usr/lib/locale/es_SV.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_SV.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_SV.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_SV.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_SV.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_SV.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_SV.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.UTF-8 0755 root bin
+d usr/lib/locale/es_SV.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_SV.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_SV.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_SV.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_SV.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_SV.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_SV.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_SV.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_US.UTF-8 0755 root bin
+d usr/lib/locale/es_US.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_US.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_US.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_US.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_US.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_US.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_US.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_US.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_US.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_US.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_US.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_US.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.ISO8859-1 0755 root bin
+d usr/lib/locale/es_UY.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_UY.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_UY.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_UY.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_UY.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_UY.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_UY.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.UTF-8 0755 root bin
+d usr/lib/locale/es_UY.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_UY.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_UY.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_UY.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_UY.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_UY.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_UY.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_UY.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.ISO8859-1 0755 root bin
+d usr/lib/locale/es_VE.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_VE.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_VE.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_VE.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_VE.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_VE.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/es_VE.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.UTF-8 0755 root bin
+d usr/lib/locale/es_VE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/es_VE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/es_VE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/es_VE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/es_VE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/es_VE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/es_VE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/es_VE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/et_EE.UTF-8 0755 root bin
+d usr/lib/locale/et_EE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/et_EE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/et_EE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/et_EE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/et_EE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/et_EE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/et_EE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/et_EE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/et_EE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/et_EE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/et_EE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/et_EE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.ISO8859-15 0755 root bin
+d usr/lib/locale/fi_FI.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/fi_FI.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/fi_FI.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fi_FI.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/fi_FI.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fi_FI.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/fi_FI.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.UTF-8 0755 root bin
+d usr/lib/locale/fi_FI.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fi_FI.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fi_FI.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fi_FI.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fi_FI.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fi_FI.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fi_FI.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fi_FI.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fil_PH.UTF-8 0755 root bin
+d usr/lib/locale/fil_PH.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fil_PH.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fil_PH.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fil_PH.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fil_PH.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fil_PH.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fil_PH.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fil_PH.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fil_PH.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fil_PH.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fil_PH.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fil_PH.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.ISO8859-15 0755 root bin
+d usr/lib/locale/fr_BE.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_BE.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_BE.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_BE.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_BE.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_BE.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/fr_BE.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.UTF-8 0755 root bin
+d usr/lib/locale/fr_BE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_BE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_BE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_BE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_BE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_BE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_BE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_BE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.ISO8859-1 0755 root bin
+d usr/lib/locale/fr_CA.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_CA.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_CA.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_CA.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_CA.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_CA.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/fr_CA.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.UTF-8 0755 root bin
+d usr/lib/locale/fr_CA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_CA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_CA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_CA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_CA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_CA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_CA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CF.UTF-8 0755 root bin
+d usr/lib/locale/fr_CF.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_CF.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CF.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_CF.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CF.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_CF.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CF.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_CF.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CF.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_CF.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CF.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_CF.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.ISO8859-1 0755 root bin
+d usr/lib/locale/fr_CH.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_CH.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_CH.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_CH.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_CH.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_CH.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/fr_CH.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.UTF-8 0755 root bin
+d usr/lib/locale/fr_CH.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_CH.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_CH.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_CH.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_CH.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_CH.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_CH.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_CH.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.ISO8859-1 0755 root bin
+d usr/lib/locale/fr_FR.ISO8859-1/LC_COLLATE 0755 root bin
+h usr/lib/locale/fr_FR.ISO8859-1/LC_COLLATE/LCL_DATA=usr/lib/locale/fr_CH.ISO8859-1/LC_COLLATE/LCL_DATA
+d usr/lib/locale/fr_FR.ISO8859-1/LC_CTYPE 0755 root bin
+h usr/lib/locale/fr_FR.ISO8859-1/LC_CTYPE/LCL_DATA=usr/lib/locale/da_DK.ISO8859-1/LC_CTYPE/LCL_DATA
+d usr/lib/locale/fr_FR.ISO8859-1/LC_MESSAGES 0755 root bin
+h usr/lib/locale/fr_FR.ISO8859-1/LC_MESSAGES/LCL_DATA=usr/lib/locale/fr_BE.ISO8859-15/LC_MESSAGES/LCL_DATA
+d usr/lib/locale/fr_FR.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_FR.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.ISO8859-1/LC_NUMERIC 0755 root bin
+h usr/lib/locale/fr_FR.ISO8859-1/LC_NUMERIC/LCL_DATA=usr/lib/locale/bg_BG.ISO8859-5/LC_NUMERIC/LCL_DATA
+d usr/lib/locale/fr_FR.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/fr_FR.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.ISO8859-15 0755 root bin
+d usr/lib/locale/fr_FR.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_FR.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_FR.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_FR.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_FR.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_FR.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/fr_FR.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.UTF-8 0755 root bin
+d usr/lib/locale/fr_FR.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_FR.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_FR.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_FR.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_FR.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_FR.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_FR.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_FR.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_GN.UTF-8 0755 root bin
+d usr/lib/locale/fr_GN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_GN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_GN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_GN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_GN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_GN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_GN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_GN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_GN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_GN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_GN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_GN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_LU.UTF-8 0755 root bin
+d usr/lib/locale/fr_LU.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_LU.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_LU.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_LU.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_LU.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_LU.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_LU.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_LU.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_LU.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_LU.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_LU.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_LU.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MC.UTF-8 0755 root bin
+d usr/lib/locale/fr_MC.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_MC.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MC.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_MC.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MC.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_MC.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MC.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_MC.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MC.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_MC.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MC.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_MC.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MG.UTF-8 0755 root bin
+d usr/lib/locale/fr_MG.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_MG.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MG.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_MG.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MG.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_MG.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MG.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_MG.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MG.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_MG.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_MG.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_MG.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_ML.UTF-8 0755 root bin
+d usr/lib/locale/fr_ML.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_ML.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_ML.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_ML.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_ML.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_ML.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_ML.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_ML.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_ML.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_ML.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_ML.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_ML.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_NE.UTF-8 0755 root bin
+d usr/lib/locale/fr_NE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_NE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_NE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_NE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_NE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_NE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_NE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_NE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_NE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_NE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_NE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_NE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_SN.UTF-8 0755 root bin
+d usr/lib/locale/fr_SN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/fr_SN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_SN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/fr_SN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_SN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/fr_SN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_SN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/fr_SN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_SN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/fr_SN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/fr_SN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/fr_SN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ga_IE.UTF-8 0755 root bin
+d usr/lib/locale/ga_IE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ga_IE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ga_IE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ga_IE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ga_IE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ga_IE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ga_IE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ga_IE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ga_IE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ga_IE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ga_IE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ga_IE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/gu_IN.UTF-8 0755 root bin
+d usr/lib/locale/gu_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/gu_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/gu_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/gu_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/gu_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/gu_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/gu_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/gu_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/gu_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/gu_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/gu_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/gu_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/he_IL.UTF-8 0755 root bin
+d usr/lib/locale/he_IL.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/he_IL.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/he_IL.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/he_IL.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/he_IL.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/he_IL.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/he_IL.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/he_IL.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/he_IL.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/he_IL.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/he_IL.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/he_IL.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/hi_IN.UTF-8 0755 root bin
+d usr/lib/locale/hi_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/hi_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/hi_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/hi_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/hi_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/hi_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/hi_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/hi_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/hi_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/hi_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/hi_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/hi_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.ISO8859-2 0755 root bin
+d usr/lib/locale/hr_HR.ISO8859-2/LC_COLLATE 0755 root bin
+f usr/lib/locale/hr_HR.ISO8859-2/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.ISO8859-2/LC_CTYPE 0755 root bin
+f usr/lib/locale/hr_HR.ISO8859-2/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.ISO8859-2/LC_MESSAGES 0755 root bin
+f usr/lib/locale/hr_HR.ISO8859-2/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.ISO8859-2/LC_MONETARY 0755 root bin
+f usr/lib/locale/hr_HR.ISO8859-2/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.ISO8859-2/LC_NUMERIC 0755 root bin
+f usr/lib/locale/hr_HR.ISO8859-2/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.ISO8859-2/LC_TIME 0755 root bin
+f usr/lib/locale/hr_HR.ISO8859-2/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.UTF-8 0755 root bin
+d usr/lib/locale/hr_HR.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/hr_HR.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/hr_HR.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/hr_HR.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/hr_HR.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/hr_HR.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/hr_HR.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/hr_HR.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.ISO8859-2 0755 root bin
+d usr/lib/locale/hu_HU.ISO8859-2/LC_COLLATE 0755 root bin
+f usr/lib/locale/hu_HU.ISO8859-2/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.ISO8859-2/LC_CTYPE 0755 root bin
+f usr/lib/locale/hu_HU.ISO8859-2/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.ISO8859-2/LC_MESSAGES 0755 root bin
+f usr/lib/locale/hu_HU.ISO8859-2/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.ISO8859-2/LC_MONETARY 0755 root bin
+f usr/lib/locale/hu_HU.ISO8859-2/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.ISO8859-2/LC_NUMERIC 0755 root bin
+f usr/lib/locale/hu_HU.ISO8859-2/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.ISO8859-2/LC_TIME 0755 root bin
+f usr/lib/locale/hu_HU.ISO8859-2/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.UTF-8 0755 root bin
+d usr/lib/locale/hu_HU.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/hu_HU.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/hu_HU.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/hu_HU.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/hu_HU.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/hu_HU.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/hu_HU.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/hu_HU.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/hy_AM.UTF-8 0755 root bin
+d usr/lib/locale/hy_AM.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/hy_AM.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/hy_AM.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/hy_AM.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/hy_AM.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/hy_AM.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/hy_AM.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/hy_AM.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/hy_AM.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/hy_AM.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/hy_AM.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/hy_AM.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/id_ID.UTF-8 0755 root bin
+d usr/lib/locale/id_ID.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/id_ID.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/id_ID.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/id_ID.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/id_ID.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/id_ID.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/id_ID.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/id_ID.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/id_ID.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/id_ID.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/id_ID.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/id_ID.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ii_CN.UTF-8 0755 root bin
+d usr/lib/locale/ii_CN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ii_CN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ii_CN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ii_CN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ii_CN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ii_CN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ii_CN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ii_CN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ii_CN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ii_CN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ii_CN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ii_CN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.ISO8859-1 0755 root bin
+d usr/lib/locale/is_IS.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/is_IS.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/is_IS.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/is_IS.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/is_IS.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/is_IS.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/is_IS.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.UTF-8 0755 root bin
+d usr/lib/locale/is_IS.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/is_IS.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/is_IS.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/is_IS.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/is_IS.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/is_IS.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/is_IS.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/is_IS.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.ISO8859-1 0755 root bin
+d usr/lib/locale/it_CH.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/it_CH.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/it_CH.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/it_CH.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/it_CH.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/it_CH.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/it_CH.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.UTF-8 0755 root bin
+d usr/lib/locale/it_CH.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/it_CH.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/it_CH.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/it_CH.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/it_CH.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/it_CH.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/it_CH.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/it_CH.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-1 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/it_IT.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-1/LC_CTYPE 0755 root bin
+h usr/lib/locale/it_IT.ISO8859-1/LC_CTYPE/LCL_DATA=usr/lib/locale/da_DK.ISO8859-1/LC_CTYPE/LCL_DATA
+d usr/lib/locale/it_IT.ISO8859-1/LC_MESSAGES 0755 root bin
+h usr/lib/locale/it_IT.ISO8859-1/LC_MESSAGES/LCL_DATA=usr/lib/locale/it_CH.ISO8859-1/LC_MESSAGES/LCL_DATA
+d usr/lib/locale/it_IT.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/it_IT.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/it_IT.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/it_IT.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-15 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/it_IT.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/it_IT.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/it_IT.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/it_IT.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/it_IT.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/it_IT.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.UTF-8 0755 root bin
+d usr/lib/locale/it_IT.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/it_IT.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/it_IT.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/it_IT.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/it_IT.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/it_IT.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/it_IT.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/it_IT.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ja_JP.UTF-8 0755 root bin
+d usr/lib/locale/ja_JP.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ja_JP.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ja_JP.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ja_JP.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ja_JP.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ja_JP.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ja_JP.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ja_JP.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ja_JP.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ja_JP.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ja_JP.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ja_JP.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ka_GE.UTF-8 0755 root bin
+d usr/lib/locale/ka_GE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ka_GE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ka_GE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ka_GE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ka_GE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ka_GE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ka_GE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ka_GE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ka_GE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ka_GE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ka_GE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ka_GE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/kk_KZ.UTF-8 0755 root bin
+d usr/lib/locale/kk_KZ.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/kk_KZ.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/kk_KZ.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/kk_KZ.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/kk_KZ.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/kk_KZ.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/kk_KZ.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/kk_KZ.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/kk_KZ.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/kk_KZ.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/kk_KZ.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/kk_KZ.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/km_KH.UTF-8 0755 root bin
+d usr/lib/locale/km_KH.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/km_KH.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/km_KH.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/km_KH.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/km_KH.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/km_KH.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/km_KH.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/km_KH.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/km_KH.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/km_KH.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/km_KH.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/km_KH.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/kn_IN.UTF-8 0755 root bin
+d usr/lib/locale/kn_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/kn_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/kn_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/kn_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/kn_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/kn_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/kn_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/kn_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/kn_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/kn_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/kn_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/kn_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ko_KR.UTF-8 0755 root bin
+d usr/lib/locale/ko_KR.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ko_KR.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ko_KR.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ko_KR.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ko_KR.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ko_KR.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ko_KR.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ko_KR.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ko_KR.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ko_KR.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ko_KR.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ko_KR.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/kok_IN.UTF-8 0755 root bin
+d usr/lib/locale/kok_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/kok_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/kok_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/kok_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/kok_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/kok_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/kok_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/kok_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/kok_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/kok_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/kok_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/kok_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.ISO8859-13 0755 root bin
+d usr/lib/locale/lt_LT.ISO8859-13/LC_COLLATE 0755 root bin
+f usr/lib/locale/lt_LT.ISO8859-13/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.ISO8859-13/LC_CTYPE 0755 root bin
+f usr/lib/locale/lt_LT.ISO8859-13/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.ISO8859-13/LC_MESSAGES 0755 root bin
+f usr/lib/locale/lt_LT.ISO8859-13/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.ISO8859-13/LC_MONETARY 0755 root bin
+f usr/lib/locale/lt_LT.ISO8859-13/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.ISO8859-13/LC_NUMERIC 0755 root bin
+f usr/lib/locale/lt_LT.ISO8859-13/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.ISO8859-13/LC_TIME 0755 root bin
+f usr/lib/locale/lt_LT.ISO8859-13/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.UTF-8 0755 root bin
+d usr/lib/locale/lt_LT.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/lt_LT.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/lt_LT.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/lt_LT.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/lt_LT.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/lt_LT.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/lt_LT.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/lt_LT.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.ISO8859-13 0755 root bin
+d usr/lib/locale/lv_LV.ISO8859-13/LC_COLLATE 0755 root bin
+f usr/lib/locale/lv_LV.ISO8859-13/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.ISO8859-13/LC_CTYPE 0755 root bin
+f usr/lib/locale/lv_LV.ISO8859-13/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.ISO8859-13/LC_MESSAGES 0755 root bin
+f usr/lib/locale/lv_LV.ISO8859-13/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.ISO8859-13/LC_MONETARY 0755 root bin
+f usr/lib/locale/lv_LV.ISO8859-13/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.ISO8859-13/LC_NUMERIC 0755 root bin
+f usr/lib/locale/lv_LV.ISO8859-13/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.ISO8859-13/LC_TIME 0755 root bin
+f usr/lib/locale/lv_LV.ISO8859-13/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.UTF-8 0755 root bin
+d usr/lib/locale/lv_LV.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/lv_LV.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/lv_LV.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/lv_LV.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/lv_LV.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/lv_LV.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/lv_LV.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/lv_LV.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.ISO8859-5 0755 root bin
+d usr/lib/locale/mk_MK.ISO8859-5/LC_COLLATE 0755 root bin
+f usr/lib/locale/mk_MK.ISO8859-5/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.ISO8859-5/LC_CTYPE 0755 root bin
+f usr/lib/locale/mk_MK.ISO8859-5/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.ISO8859-5/LC_MESSAGES 0755 root bin
+f usr/lib/locale/mk_MK.ISO8859-5/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.ISO8859-5/LC_MONETARY 0755 root bin
+f usr/lib/locale/mk_MK.ISO8859-5/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.ISO8859-5/LC_NUMERIC 0755 root bin
+f usr/lib/locale/mk_MK.ISO8859-5/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.ISO8859-5/LC_TIME 0755 root bin
+f usr/lib/locale/mk_MK.ISO8859-5/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.UTF-8 0755 root bin
+d usr/lib/locale/mk_MK.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/mk_MK.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/mk_MK.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/mk_MK.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/mk_MK.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/mk_MK.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/mk_MK.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/mk_MK.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ml_IN.UTF-8 0755 root bin
+d usr/lib/locale/ml_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ml_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ml_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ml_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ml_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ml_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ml_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ml_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ml_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ml_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ml_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ml_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_CN.UTF-8 0755 root bin
+d usr/lib/locale/mn_CN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/mn_CN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_CN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/mn_CN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_CN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/mn_CN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_CN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/mn_CN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_CN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/mn_CN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_CN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/mn_CN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_MN.UTF-8 0755 root bin
+d usr/lib/locale/mn_MN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/mn_MN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_MN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/mn_MN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_MN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/mn_MN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_MN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/mn_MN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_MN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/mn_MN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/mn_MN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/mn_MN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/mr_IN.UTF-8 0755 root bin
+d usr/lib/locale/mr_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/mr_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/mr_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/mr_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/mr_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/mr_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/mr_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/mr_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/mr_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/mr_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/mr_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/mr_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ms_MY.UTF-8 0755 root bin
+d usr/lib/locale/ms_MY.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ms_MY.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ms_MY.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ms_MY.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ms_MY.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ms_MY.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ms_MY.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ms_MY.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ms_MY.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ms_MY.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ms_MY.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ms_MY.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/mt_MT.UTF-8 0755 root bin
+d usr/lib/locale/mt_MT.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/mt_MT.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/mt_MT.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/mt_MT.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/mt_MT.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/mt_MT.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/mt_MT.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/mt_MT.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/mt_MT.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/mt_MT.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/mt_MT.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/mt_MT.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/nb_NO.UTF-8 0755 root bin
+d usr/lib/locale/nb_NO.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/nb_NO.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/nb_NO.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/nb_NO.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/nb_NO.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/nb_NO.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/nb_NO.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/nb_NO.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/nb_NO.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/nb_NO.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/nb_NO.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/nb_NO.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_IN.UTF-8 0755 root bin
+d usr/lib/locale/ne_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ne_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ne_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ne_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ne_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ne_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ne_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_NP.UTF-8 0755 root bin
+d usr/lib/locale/ne_NP.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ne_NP.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_NP.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ne_NP.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_NP.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ne_NP.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_NP.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ne_NP.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_NP.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ne_NP.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ne_NP.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ne_NP.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.ISO8859-15 0755 root bin
+d usr/lib/locale/nl_BE.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/nl_BE.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/nl_BE.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/nl_BE.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/nl_BE.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/nl_BE.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/nl_BE.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.UTF-8 0755 root bin
+d usr/lib/locale/nl_BE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/nl_BE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/nl_BE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/nl_BE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/nl_BE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/nl_BE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_BE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/nl_BE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.ISO8859-15 0755 root bin
+d usr/lib/locale/nl_NL.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/nl_NL.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/nl_NL.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/nl_NL.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/nl_NL.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/nl_NL.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/nl_NL.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.UTF-8 0755 root bin
+d usr/lib/locale/nl_NL.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/nl_NL.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/nl_NL.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/nl_NL.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/nl_NL.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/nl_NL.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/nl_NL.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/nl_NL.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/nn_NO.UTF-8 0755 root bin
+d usr/lib/locale/nn_NO.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/nn_NO.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/nn_NO.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/nn_NO.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/nn_NO.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/nn_NO.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/nn_NO.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/nn_NO.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/nn_NO.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/nn_NO.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/nn_NO.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/nn_NO.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/or_IN.UTF-8 0755 root bin
+d usr/lib/locale/or_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/or_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/or_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/or_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/or_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/or_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/or_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/or_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/or_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/or_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/or_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/or_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_IN.UTF-8 0755 root bin
+d usr/lib/locale/pa_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/pa_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/pa_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/pa_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/pa_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/pa_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/pa_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_PK.UTF-8 0755 root bin
+d usr/lib/locale/pa_PK.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/pa_PK.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_PK.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/pa_PK.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_PK.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/pa_PK.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_PK.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/pa_PK.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_PK.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/pa_PK.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/pa_PK.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/pa_PK.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.ISO8859-2 0755 root bin
+d usr/lib/locale/pl_PL.ISO8859-2/LC_COLLATE 0755 root bin
+f usr/lib/locale/pl_PL.ISO8859-2/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.ISO8859-2/LC_CTYPE 0755 root bin
+f usr/lib/locale/pl_PL.ISO8859-2/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.ISO8859-2/LC_MESSAGES 0755 root bin
+f usr/lib/locale/pl_PL.ISO8859-2/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.ISO8859-2/LC_MONETARY 0755 root bin
+f usr/lib/locale/pl_PL.ISO8859-2/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.ISO8859-2/LC_NUMERIC 0755 root bin
+f usr/lib/locale/pl_PL.ISO8859-2/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.ISO8859-2/LC_TIME 0755 root bin
+f usr/lib/locale/pl_PL.ISO8859-2/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.UTF-8 0755 root bin
+d usr/lib/locale/pl_PL.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/pl_PL.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/pl_PL.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/pl_PL.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/pl_PL.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/pl_PL.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/pl_PL.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/pl_PL.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_BR.UTF-8 0755 root bin
+d usr/lib/locale/pt_BR.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/pt_BR.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_BR.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/pt_BR.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_BR.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/pt_BR.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_BR.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/pt_BR.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_BR.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/pt_BR.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_BR.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/pt_BR.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_GW.UTF-8 0755 root bin
+d usr/lib/locale/pt_GW.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/pt_GW.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_GW.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/pt_GW.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_GW.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/pt_GW.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_GW.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/pt_GW.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_GW.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/pt_GW.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_GW.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/pt_GW.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_MZ.UTF-8 0755 root bin
+d usr/lib/locale/pt_MZ.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/pt_MZ.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_MZ.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/pt_MZ.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_MZ.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/pt_MZ.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_MZ.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/pt_MZ.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_MZ.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/pt_MZ.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_MZ.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/pt_MZ.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.ISO8859-15 0755 root bin
+d usr/lib/locale/pt_PT.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/pt_PT.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/pt_PT.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/pt_PT.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/pt_PT.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/pt_PT.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/pt_PT.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.UTF-8 0755 root bin
+d usr/lib/locale/pt_PT.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/pt_PT.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/pt_PT.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/pt_PT.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/pt_PT.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/pt_PT.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/pt_PT.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/pt_PT.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_MD.UTF-8 0755 root bin
+d usr/lib/locale/ro_MD.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ro_MD.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_MD.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ro_MD.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_MD.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ro_MD.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_MD.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ro_MD.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_MD.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ro_MD.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_MD.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ro_MD.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_RO.UTF-8 0755 root bin
+d usr/lib/locale/ro_RO.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ro_RO.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_RO.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ro_RO.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_RO.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ro_RO.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_RO.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ro_RO.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_RO.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ro_RO.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ro_RO.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ro_RO.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_MD.UTF-8 0755 root bin
+d usr/lib/locale/ru_MD.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ru_MD.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_MD.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ru_MD.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_MD.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ru_MD.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_MD.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ru_MD.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_MD.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ru_MD.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_MD.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ru_MD.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.ISO8859-5 0755 root bin
+d usr/lib/locale/ru_RU.ISO8859-5/LC_COLLATE 0755 root bin
+f usr/lib/locale/ru_RU.ISO8859-5/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.ISO8859-5/LC_CTYPE 0755 root bin
+f usr/lib/locale/ru_RU.ISO8859-5/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.ISO8859-5/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ru_RU.ISO8859-5/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.ISO8859-5/LC_MONETARY 0755 root bin
+f usr/lib/locale/ru_RU.ISO8859-5/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.ISO8859-5/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ru_RU.ISO8859-5/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.ISO8859-5/LC_TIME 0755 root bin
+f usr/lib/locale/ru_RU.ISO8859-5/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.KOI8-R 0755 root bin
+d usr/lib/locale/ru_RU.KOI8-R/LC_COLLATE 0755 root bin
+f usr/lib/locale/ru_RU.KOI8-R/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.KOI8-R/LC_CTYPE 0755 root bin
+f usr/lib/locale/ru_RU.KOI8-R/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.KOI8-R/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ru_RU.KOI8-R/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.KOI8-R/LC_MONETARY 0755 root bin
+f usr/lib/locale/ru_RU.KOI8-R/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.KOI8-R/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ru_RU.KOI8-R/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.KOI8-R/LC_TIME 0755 root bin
+f usr/lib/locale/ru_RU.KOI8-R/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.UTF-8 0755 root bin
+d usr/lib/locale/ru_RU.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ru_RU.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ru_RU.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ru_RU.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ru_RU.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ru_RU.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_RU.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ru_RU.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_UA.UTF-8 0755 root bin
+d usr/lib/locale/ru_UA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ru_UA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_UA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ru_UA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_UA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ru_UA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_UA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ru_UA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_UA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ru_UA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ru_UA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ru_UA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sa_IN.UTF-8 0755 root bin
+d usr/lib/locale/sa_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/sa_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sa_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/sa_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sa_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sa_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sa_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/sa_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sa_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sa_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sa_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/sa_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/si_LK.UTF-8 0755 root bin
+d usr/lib/locale/si_LK.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/si_LK.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/si_LK.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/si_LK.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/si_LK.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/si_LK.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/si_LK.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/si_LK.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/si_LK.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/si_LK.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/si_LK.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/si_LK.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sk_SK.UTF-8 0755 root bin
+d usr/lib/locale/sk_SK.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/sk_SK.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sk_SK.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/sk_SK.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sk_SK.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sk_SK.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sk_SK.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/sk_SK.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sk_SK.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sk_SK.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sk_SK.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/sk_SK.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sl_SI.UTF-8 0755 root bin
+d usr/lib/locale/sl_SI.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/sl_SI.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sl_SI.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/sl_SI.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sl_SI.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sl_SI.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sl_SI.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/sl_SI.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sl_SI.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sl_SI.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sl_SI.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/sl_SI.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.ISO8859-2 0755 root bin
+d usr/lib/locale/sq_AL.ISO8859-2/LC_COLLATE 0755 root bin
+f usr/lib/locale/sq_AL.ISO8859-2/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.ISO8859-2/LC_CTYPE 0755 root bin
+f usr/lib/locale/sq_AL.ISO8859-2/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.ISO8859-2/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sq_AL.ISO8859-2/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.ISO8859-2/LC_MONETARY 0755 root bin
+f usr/lib/locale/sq_AL.ISO8859-2/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.ISO8859-2/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sq_AL.ISO8859-2/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.ISO8859-2/LC_TIME 0755 root bin
+f usr/lib/locale/sq_AL.ISO8859-2/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.UTF-8 0755 root bin
+d usr/lib/locale/sq_AL.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/sq_AL.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/sq_AL.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sq_AL.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/sq_AL.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sq_AL.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sq_AL.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/sq_AL.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_BA.UTF-8 0755 root bin
+d usr/lib/locale/sr_BA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/sr_BA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_BA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/sr_BA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_BA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sr_BA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_BA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/sr_BA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_BA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sr_BA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_BA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/sr_BA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_ME.UTF-8 0755 root bin
+d usr/lib/locale/sr_ME.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/sr_ME.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_ME.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/sr_ME.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_ME.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sr_ME.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_ME.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/sr_ME.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_ME.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sr_ME.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_ME.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/sr_ME.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_RS.UTF-8 0755 root bin
+d usr/lib/locale/sr_RS.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/sr_RS.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_RS.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/sr_RS.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_RS.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sr_RS.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_RS.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/sr_RS.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_RS.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sr_RS.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sr_RS.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/sr_RS.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.ISO8859-15 0755 root bin
+d usr/lib/locale/sv_FI.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/sv_FI.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/sv_FI.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sv_FI.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/sv_FI.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sv_FI.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/sv_FI.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.UTF-8 0755 root bin
+d usr/lib/locale/sv_FI.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/sv_FI.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/sv_FI.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sv_FI.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/sv_FI.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sv_FI.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_FI.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/sv_FI.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-1 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-1/LC_COLLATE 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-1/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-1/LC_CTYPE 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-1/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-1/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-1/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-1/LC_MONETARY 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-1/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-1/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-1/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-1/LC_TIME 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-1/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-15 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-15/LC_COLLATE 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-15/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-15/LC_CTYPE 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-15/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-15/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-15/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-15/LC_MONETARY 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-15/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-15/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-15/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.ISO8859-15/LC_TIME 0755 root bin
+f usr/lib/locale/sv_SE.ISO8859-15/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.UTF-8 0755 root bin
+d usr/lib/locale/sv_SE.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/sv_SE.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/sv_SE.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/sv_SE.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/sv_SE.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/sv_SE.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/sv_SE.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/sv_SE.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_IN.UTF-8 0755 root bin
+d usr/lib/locale/ta_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ta_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ta_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ta_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ta_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ta_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ta_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_LK.UTF-8 0755 root bin
+d usr/lib/locale/ta_LK.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ta_LK.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_LK.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ta_LK.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_LK.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ta_LK.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_LK.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ta_LK.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_LK.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ta_LK.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ta_LK.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ta_LK.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/te_IN.UTF-8 0755 root bin
+d usr/lib/locale/te_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/te_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/te_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/te_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/te_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/te_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/te_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/te_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/te_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/te_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/te_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/te_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.ISO8859-11 0755 root bin
+d usr/lib/locale/th_TH.ISO8859-11/LC_COLLATE 0755 root bin
+f usr/lib/locale/th_TH.ISO8859-11/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.ISO8859-11/LC_CTYPE 0755 root bin
+f usr/lib/locale/th_TH.ISO8859-11/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.ISO8859-11/LC_MESSAGES 0755 root bin
+f usr/lib/locale/th_TH.ISO8859-11/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.ISO8859-11/LC_MONETARY 0755 root bin
+f usr/lib/locale/th_TH.ISO8859-11/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.ISO8859-11/LC_NUMERIC 0755 root bin
+f usr/lib/locale/th_TH.ISO8859-11/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.ISO8859-11/LC_TIME 0755 root bin
+f usr/lib/locale/th_TH.ISO8859-11/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.UTF-8 0755 root bin
+d usr/lib/locale/th_TH.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/th_TH.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/th_TH.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/th_TH.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/th_TH.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/th_TH.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/th_TH.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/th_TH.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.ISO8859-9 0755 root bin
+d usr/lib/locale/tr_TR.ISO8859-9/LC_COLLATE 0755 root bin
+f usr/lib/locale/tr_TR.ISO8859-9/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.ISO8859-9/LC_CTYPE 0755 root bin
+f usr/lib/locale/tr_TR.ISO8859-9/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.ISO8859-9/LC_MESSAGES 0755 root bin
+f usr/lib/locale/tr_TR.ISO8859-9/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.ISO8859-9/LC_MONETARY 0755 root bin
+f usr/lib/locale/tr_TR.ISO8859-9/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.ISO8859-9/LC_NUMERIC 0755 root bin
+f usr/lib/locale/tr_TR.ISO8859-9/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.ISO8859-9/LC_TIME 0755 root bin
+f usr/lib/locale/tr_TR.ISO8859-9/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.UTF-8 0755 root bin
+d usr/lib/locale/tr_TR.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/tr_TR.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/tr_TR.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/tr_TR.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/tr_TR.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/tr_TR.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/tr_TR.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/tr_TR.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ug_CN.UTF-8 0755 root bin
+d usr/lib/locale/ug_CN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ug_CN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ug_CN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ug_CN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ug_CN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ug_CN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ug_CN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ug_CN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ug_CN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ug_CN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ug_CN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ug_CN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/uk_UA.UTF-8 0755 root bin
+d usr/lib/locale/uk_UA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/uk_UA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/uk_UA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/uk_UA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/uk_UA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/uk_UA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/uk_UA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/uk_UA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/uk_UA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/uk_UA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/uk_UA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/uk_UA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_IN.UTF-8 0755 root bin
+d usr/lib/locale/ur_IN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ur_IN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_IN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ur_IN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_IN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ur_IN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_IN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ur_IN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_IN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ur_IN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_IN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ur_IN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_PK.UTF-8 0755 root bin
+d usr/lib/locale/ur_PK.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/ur_PK.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_PK.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/ur_PK.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_PK.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/ur_PK.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_PK.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/ur_PK.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_PK.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/ur_PK.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/ur_PK.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/ur_PK.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/vi_VN.UTF-8 0755 root bin
+d usr/lib/locale/vi_VN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/vi_VN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/vi_VN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/vi_VN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/vi_VN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/vi_VN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/vi_VN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/vi_VN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/vi_VN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/vi_VN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/vi_VN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/vi_VN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.GB18030 0755 root bin
+d usr/lib/locale/zh_CN.GB18030/LC_COLLATE 0755 root bin
+f usr/lib/locale/zh_CN.GB18030/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.GB18030/LC_CTYPE 0755 root bin
+f usr/lib/locale/zh_CN.GB18030/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.GB18030/LC_MESSAGES 0755 root bin
+f usr/lib/locale/zh_CN.GB18030/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.GB18030/LC_MONETARY 0755 root bin
+f usr/lib/locale/zh_CN.GB18030/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.GB18030/LC_NUMERIC 0755 root bin
+f usr/lib/locale/zh_CN.GB18030/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.GB18030/LC_TIME 0755 root bin
+f usr/lib/locale/zh_CN.GB18030/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.UTF-8 0755 root bin
+d usr/lib/locale/zh_CN.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/zh_CN.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/zh_CN.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/zh_CN.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/zh_CN.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/zh_CN.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_CN.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/zh_CN.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_HK.UTF-8 0755 root bin
+d usr/lib/locale/zh_HK.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/zh_HK.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_HK.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/zh_HK.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_HK.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/zh_HK.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_HK.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/zh_HK.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_HK.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/zh_HK.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_HK.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/zh_HK.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_MO.UTF-8 0755 root bin
+d usr/lib/locale/zh_MO.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/zh_MO.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_MO.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/zh_MO.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_MO.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/zh_MO.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_MO.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/zh_MO.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_MO.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/zh_MO.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_MO.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/zh_MO.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_SG.UTF-8 0755 root bin
+d usr/lib/locale/zh_SG.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/zh_SG.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_SG.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/zh_SG.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_SG.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/zh_SG.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_SG.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/zh_SG.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_SG.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/zh_SG.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_SG.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/zh_SG.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_TW.UTF-8 0755 root bin
+d usr/lib/locale/zh_TW.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/zh_TW.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_TW.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/zh_TW.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_TW.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/zh_TW.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_TW.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/zh_TW.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_TW.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/zh_TW.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/zh_TW.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/zh_TW.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/locale/zz_AA.UTF-8 0755 root bin
+d usr/lib/locale/zz_AA.UTF-8/LC_COLLATE 0755 root bin
+f usr/lib/locale/zz_AA.UTF-8/LC_COLLATE/LCL_DATA 0755 root bin
+d usr/lib/locale/zz_AA.UTF-8/LC_CTYPE 0755 root bin
+f usr/lib/locale/zz_AA.UTF-8/LC_CTYPE/LCL_DATA 0755 root bin
+d usr/lib/locale/zz_AA.UTF-8/LC_MESSAGES 0755 root bin
+f usr/lib/locale/zz_AA.UTF-8/LC_MESSAGES/LCL_DATA 0755 root bin
+f usr/lib/locale/zz_AA.UTF-8/LC_MESSAGES/SUNW_OST_OSLIB.mo 0755 root bin
+f usr/lib/locale/zz_AA.UTF-8/LC_MESSAGES/priv_names 0755 root bin
+d usr/lib/locale/zz_AA.UTF-8/LC_MONETARY 0755 root bin
+f usr/lib/locale/zz_AA.UTF-8/LC_MONETARY/LCL_DATA 0755 root bin
+d usr/lib/locale/zz_AA.UTF-8/LC_NUMERIC 0755 root bin
+f usr/lib/locale/zz_AA.UTF-8/LC_NUMERIC/LCL_DATA 0755 root bin
+d usr/lib/locale/zz_AA.UTF-8/LC_TIME 0755 root bin
+f usr/lib/locale/zz_AA.UTF-8/LC_TIME/LCL_DATA 0755 root bin
+d usr/lib/localedef 0755 root bin
+d usr/lib/localedef/extensions 0755 root bin
+f usr/lib/localedef/extensions/UTF-8.x 0444 root bin
+d usr/lib/localedef/src 0755 root bin
+f usr/lib/lslabels 0555 root bin
+d usr/lib/lwp 0755 root bin
+s usr/lib/lwp/32=.
+s usr/lib/lwp/64=amd64
+d usr/lib/lwp/amd64 0755 root bin
+s usr/lib/lwp/amd64/libthread.so.1=../../amd64/libthread.so.1
+s usr/lib/lwp/amd64/libthread_db.so.1=../../amd64/libthread_db.so.1
+s usr/lib/lwp/libthread.so.1=../libthread.so.1
+s usr/lib/lwp/libthread_db.so.1=../libthread_db.so.1
+f usr/lib/lx_brand.so.1 0755 root sys
+f usr/lib/madv.so.1 0755 root bin
+f usr/lib/mail.local 0555 root bin
+f usr/lib/mailwrapper 0555 root bin
+f usr/lib/makekey 0555 root bin
+h usr/lib/makewhatis=usr/bin/man
+d usr/lib/mdb 0755 root sys
+d usr/lib/mdb/kvm 0755 root sys
+d usr/lib/mdb/kvm/amd64 0755 root sys
+f usr/lib/mdb/kvm/amd64/arp.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/cpc.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/cpu.generic.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/cpu_ms.AuthenticAMD.15.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/crypto.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/dtrace.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/emlxs.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/fcip.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/fcp.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/fctl.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/genunix.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/hook.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/i40e.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/idm.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/ip.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/ipc.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/ipp.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/krtld.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/lofs.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/logindmux.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/mac.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/mdb_kb.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/mdb_ks.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/mm.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/mpt.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/mpt_sas.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/mr_sas.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/neti.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/nfs.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/nsmb.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/pmcs.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/ptm.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/qlc.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/random.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/s1394.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/sata.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/scsi_vhci.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/sctp.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/sd.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/smbfs.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/smbios.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/smbsrv.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/sockfs.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/specfs.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/sppp.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/srpt.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/stmf.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/stmf_sbd.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/ufs.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/uhci.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/usba.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/xhci.so 0555 root sys
+f usr/lib/mdb/kvm/amd64/zfs.so 0555 root sys
+d usr/lib/mdb/proc 0755 root sys
+d usr/lib/mdb/proc/amd64 0755 root sys
+f usr/lib/mdb/proc/amd64/ld.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libavl.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libc.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libnvpair.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libproc.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libpython3.9.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libsmbios.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libsysevent.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libtopo.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libumem.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libuutil.so 0555 root sys
+f usr/lib/mdb/proc/amd64/libzpool.so 0555 root sys
+f usr/lib/mdb/proc/amd64/list.so 0555 root sys
+f usr/lib/mdb/proc/amd64/mdb_ds.so 0555 root sys
+f usr/lib/mdb/proc/amd64/mdb_test.so 0555 root sys
+f usr/lib/mdb/proc/eft.so 0555 root sys
+f usr/lib/mdb/proc/fmd.so 0555 root sys
+f usr/lib/mdb/proc/ld.so 0555 root sys
+f usr/lib/mdb/proc/libavl.so 0555 root sys
+f usr/lib/mdb/proc/libc.so 0555 root sys
+f usr/lib/mdb/proc/libmlsvc.so 0555 root sys
+f usr/lib/mdb/proc/libnvpair.so 0555 root sys
+f usr/lib/mdb/proc/libproc.so 0555 root sys
+f usr/lib/mdb/proc/libsmbios.so 0555 root sys
+f usr/lib/mdb/proc/libsysevent.so 0555 root sys
+f usr/lib/mdb/proc/libtopo.so 0555 root sys
+f usr/lib/mdb/proc/libumem.so 0555 root sys
+f usr/lib/mdb/proc/libuutil.so 0555 root sys
+f usr/lib/mdb/proc/libzpool.so 0555 root sys
+f usr/lib/mdb/proc/list.so 0555 root sys
+f usr/lib/mdb/proc/mdb_ds.so 0555 root sys
+f usr/lib/mdb/proc/mdb_test.so 0555 root sys
+f usr/lib/mdb/proc/svc.configd.so 0555 root sys
+f usr/lib/mdb/proc/svc.startd.so 0555 root sys
+d usr/lib/mdb/raw 0755 root sys
+d usr/lib/mdb/raw/amd64 0755 root sys
+f usr/lib/mdb/raw/amd64/disk_label.so 0555 root sys
+f usr/lib/mdb/raw/amd64/dof.so 0555 root sys
+f usr/lib/mdb/raw/disk_label.so 0555 root sys
+f usr/lib/mdb/raw/dof.so 0555 root sys
+f usr/lib/more.help 0644 root bin
+f usr/lib/mpss.so.1 0755 root bin
+d usr/lib/ndmp 0755 root bin
+f usr/lib/ndmp/ndmpd 0555 root bin
+d usr/lib/netsvc 0755 root sys
+d usr/lib/netsvc/nis 0755 root sys
+d usr/lib/netsvc/rstat 0755 root sys
+f usr/lib/netsvc/rstat/rpc.rstatd 0555 root sys
+d usr/lib/netsvc/rusers 0755 root sys
+f usr/lib/netsvc/rusers/rpc.rusersd 0555 root sys
+d usr/lib/netsvc/rwall 0755 root sys
+f usr/lib/netsvc/rwall/rpc.rwalld 0555 root sys
+d usr/lib/netsvc/spray 0755 root sys
+f usr/lib/netsvc/spray/rpc.sprayd 0555 root sys
+d usr/lib/netsvc/yp 0755 root bin
+f usr/lib/netsvc/yp/inityp2l 0555 root sys
+f usr/lib/netsvc/yp/mkalias 0555 root sys
+f usr/lib/netsvc/yp/multi 0555 root sys
+f usr/lib/netsvc/yp/multi.awk 0555 root sys
+f usr/lib/netsvc/yp/rpc.yppasswdd 0555 root sys
+f usr/lib/netsvc/yp/rpc.ypupdated 0555 root sys
+f usr/lib/netsvc/yp/stdethers 0555 root sys
+f usr/lib/netsvc/yp/stdhosts 0555 root sys
+f usr/lib/netsvc/yp/udpublickey 0555 root sys
+f usr/lib/netsvc/yp/ypbind 0555 root bin
+f usr/lib/netsvc/yp/ypmap2src 0555 root sys
+f usr/lib/netsvc/yp/yppush 0555 root sys
+f usr/lib/netsvc/yp/ypserv 0555 root sys
+f usr/lib/netsvc/yp/ypstart 0555 root bin
+f usr/lib/netsvc/yp/ypstop 0555 root bin
+f usr/lib/netsvc/yp/ypxfr 0555 root bin
+f usr/lib/netsvc/yp/ypxfr_1perday 0555 root sys
+f usr/lib/netsvc/yp/ypxfr_1perhour 0555 root sys
+f usr/lib/netsvc/yp/ypxfr_2perday 0555 root sys
+f usr/lib/netsvc/yp/ypxfrd 0555 root sys
+f usr/lib/newsyslog 0555 root sys
+d usr/lib/nfs 0755 root sys
+d usr/lib/nfs/dtrace 0755 root sys
+f usr/lib/nfs/dtrace/nfs-time.d 0555 root bin
+f usr/lib/nfs/dtrace/nfs-trace.d 0555 root bin
+f usr/lib/nfs/libmapid.so.1 0755 root bin
+s usr/lib/nfs/libmapid.so=libmapid.so.1
+f usr/lib/nfs/lockd 0555 root bin
+f usr/lib/nfs/mountd 0555 root bin
+f usr/lib/nfs/nfs4cbd 0555 root bin
+f usr/lib/nfs/nfsd 0555 root bin
+f usr/lib/nfs/nfslogd 0555 root bin
+f usr/lib/nfs/nfsmapid 0555 root bin
+f usr/lib/nfs/rquotad 0555 root bin
+f usr/lib/nfs/statd 0555 root bin
+f usr/lib/nss_ad.so.1 0755 root bin
+s usr/lib/nss_compat.so.1=../../lib/nss_compat.so.1
+s usr/lib/nss_dns.so.1=../../lib/nss_dns.so.1
+s usr/lib/nss_files.so.1=../../lib/nss_files.so.1
+f usr/lib/nss_ldap.so.1 0755 root bin
+f usr/lib/nss_mdns.so.1 0755 root bin
+s usr/lib/nss_nis.so.1=../../lib/nss_nis.so.1
+s usr/lib/nss_user.so.1=../../lib/nss_user.so.1
+f usr/lib/passmgmt 0555 root sys
+f usr/lib/passwdutil.so.1 0755 root bin
+d usr/lib/pci 0755 root bin
+f usr/lib/pci/pcidb 0555 root bin
+f usr/lib/pci/pcidr 0555 root bin
+f usr/lib/pci/pcidr_plugin.so 0755 root bin
+f usr/lib/pci/pcieadm 0555 root bin
+f usr/lib/pci/pcieb 0555 root bin
+f usr/lib/pfexecd 0555 root bin
+d usr/lib/picl 0755 root sys
+f usr/lib/picl/picld 0555 root sys
+d usr/lib/picl/plugins 0755 root sys
+f usr/lib/picl/plugins/fru_container.conf 0644 root sys
+f usr/lib/picl/plugins/libpicldevtree.so.1 0755 root sys
+s usr/lib/picl/plugins/libpicldevtree.so=libpicldevtree.so.1
+f usr/lib/picl/plugins/libpiclevent.so.1 0755 root sys
+s usr/lib/picl/plugins/libpiclevent.so=libpiclevent.so.1
+f usr/lib/picl/plugins/libpiclfrutree.so.1 0755 root sys
+s usr/lib/picl/plugins/libpiclfrutree.so=libpiclfrutree.so.1
+f usr/lib/picl/plugins/libpiclmemcfg.so.1 0755 root sys
+s usr/lib/picl/plugins/libpiclmemcfg.so=libpiclmemcfg.so.1
+d usr/lib/pkgconfig 0755 root bin
+f usr/lib/platexec 0555 root bin
+d usr/lib/power 0755 root bin
+f usr/lib/power/powerd 0555 root bin
+d usr/lib/raidcfg 0755 root bin
+d usr/lib/raidcfg/amd64 0755 root bin
+f usr/lib/raidcfg/amd64/mpt.so.1 0755 root bin
+f usr/lib/raidcfg/mpt.so.1 0755 root bin
+d usr/lib/rcap 0755 root bin
+f usr/lib/rcap/rcapd 0555 root bin
+d usr/lib/rcm 0755 root bin
+d usr/lib/rcm/modules 0755 root bin
+f usr/lib/rcm/modules/SUNW_aggr_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_bridge_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_cluster_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_dump_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_filesys_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_ibpart_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_ip_anon_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_ip_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_mpxio_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_network_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_pool_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_swap_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_vlan_rcm.so 0555 root bin
+f usr/lib/rcm/modules/SUNW_vnic_rcm.so 0555 root bin
+f usr/lib/rcm/rcm_daemon 0555 root bin
+d usr/lib/rcm/scripts 0755 root bin
+f usr/lib/rcm/scripts/SUNW,ibsdpu.sh 0555 root bin
+f usr/lib/rcm/scripts/SUNW,rdsv3u.sh 0555 root bin
+d usr/lib/refer 0755 root bin
+f usr/lib/refer/hunt 0555 root bin
+f usr/lib/refer/inv 0555 root bin
+f usr/lib/refer/mkey 0555 root bin
+d usr/lib/refer/papers 0755 root bin
+f usr/lib/refer/papers/Rbstjissue 0644 root bin
+f usr/lib/refer/papers/Rv7man 0644 root bin
+f usr/lib/refer/papers/runinv 0755 root bin
+d usr/lib/reparse 0755 root bin
+d usr/lib/reparse/amd64 0755 root bin
+f usr/lib/reparse/amd64/libnfs_basic.so.1 0755 root bin
+s usr/lib/reparse/amd64/libnfs_basic.so=libnfs_basic.so.1
+f usr/lib/reparse/amd64/libreparse_smb.so.1 0755 root bin
+s usr/lib/reparse/amd64/libreparse_smb.so=libreparse_smb.so.1
+f usr/lib/reparse/libnfs_basic.so.1 0755 root bin
+s usr/lib/reparse/libnfs_basic.so=libnfs_basic.so.1
+f usr/lib/reparse/libreparse_smb.so.1 0755 root bin
+s usr/lib/reparse/libreparse_smb.so=libreparse_smb.so.1
+f usr/lib/reparse/reparsed 0555 root sys
+f usr/lib/rpcsec.so.1 0755 root bin
+s usr/lib/rpcsec.so=rpcsec.so.1
+s usr/lib/rsh=../bin/ksh93
+f usr/lib/rsrvrctl 0555 root sys
+d usr/lib/sa 0755 root bin
+f usr/lib/sa/sa1 0555 root bin
+f usr/lib/sa/sa2 0555 root bin
+f usr/lib/sa/sadc 0555 root bin
+d usr/lib/saf 0755 root bin
+f usr/lib/saf/listen 0755 root sys
+f usr/lib/saf/nlps_server 0755 root sys
+f usr/lib/saf/sac 0555 root sys
+f usr/lib/saf/ttymon 0555 root sys
+d usr/lib/sasl 0755 root bin
+d usr/lib/sasl/amd64 0755 root bin
+f usr/lib/sasl/amd64/crammd5.so.1 0755 root bin
+f usr/lib/sasl/amd64/digestmd5.so.1 0755 root bin
+f usr/lib/sasl/amd64/gssapi.so.1 0755 root bin
+f usr/lib/sasl/amd64/plain.so.1 0755 root bin
+f usr/lib/sasl/crammd5.so.1 0755 root bin
+f usr/lib/sasl/digestmd5.so.1 0755 root bin
+f usr/lib/sasl/gssapi.so.1 0755 root bin
+f usr/lib/sasl/plain.so.1 0755 root bin
+d usr/lib/scsi 0755 root bin
+d usr/lib/scsi/amd64 0755 root bin
+f usr/lib/scsi/amd64/libscsi.so.1 0755 root bin
+s usr/lib/scsi/amd64/libscsi.so=libscsi.so.1
+f usr/lib/scsi/amd64/libses.so.1 0755 root bin
+s usr/lib/scsi/amd64/libses.so=libses.so.1
+f usr/lib/scsi/amd64/libsmp.so.1 0755 root bin
+s usr/lib/scsi/amd64/libsmp.so=libsmp.so.1
+f usr/lib/scsi/libscsi.so.1 0755 root bin
+s usr/lib/scsi/libscsi.so=libscsi.so.1
+f usr/lib/scsi/libses.so.1 0755 root bin
+s usr/lib/scsi/libses.so=libses.so.1
+f usr/lib/scsi/libsmp.so.1 0755 root bin
+s usr/lib/scsi/libsmp.so=libsmp.so.1
+d usr/lib/scsi/plugins 0755 root bin
+d usr/lib/scsi/plugins/scsi 0755 root bin
+d usr/lib/scsi/plugins/scsi/engines 0755 root bin
+d usr/lib/scsi/plugins/scsi/engines/amd64 0755 root bin
+f usr/lib/scsi/plugins/scsi/engines/amd64/uscsi.so 0755 root bin
+f usr/lib/scsi/plugins/scsi/engines/uscsi.so 0755 root bin
+d usr/lib/scsi/plugins/ses 0755 root bin
+d usr/lib/scsi/plugins/ses/framework 0755 root bin
+d usr/lib/scsi/plugins/ses/framework/amd64 0755 root bin
+f usr/lib/scsi/plugins/ses/framework/amd64/libses.so 0755 root bin
+f usr/lib/scsi/plugins/ses/framework/amd64/ses2.so 0755 root bin
+f usr/lib/scsi/plugins/ses/framework/libses.so 0755 root bin
+f usr/lib/scsi/plugins/ses/framework/ses2.so 0755 root bin
+d usr/lib/scsi/plugins/ses/vendor 0755 root bin
+f usr/lib/scsi/plugins/ses/vendor/LSILOGIC-SASX28-A.0.so 0755 root bin
+s usr/lib/scsi/plugins/ses/vendor/LSILOGIC-SASX28-A.1.so=LSILOGIC-SASX28-A.0.so
+s usr/lib/scsi/plugins/ses/vendor/SUN-Storage-J4200.so=SUN-Storage-J4400.so
+f usr/lib/scsi/plugins/ses/vendor/SUN-Storage-J4400.so 0755 root bin
+f usr/lib/scsi/plugins/ses/vendor/SUN-Storage-J4500.so 0755 root bin
+f usr/lib/scsi/plugins/ses/vendor/SUN.so 0755 root bin
+d usr/lib/scsi/plugins/ses/vendor/amd64 0755 root bin
+f usr/lib/scsi/plugins/ses/vendor/amd64/LSILOGIC-SASX28-A.0.so 0755 root bin
+s usr/lib/scsi/plugins/ses/vendor/amd64/LSILOGIC-SASX28-A.1.so=LSILOGIC-SASX28-A.0.so
+s usr/lib/scsi/plugins/ses/vendor/amd64/SUN-Storage-J4200.so=SUN-Storage-J4400.so
+f usr/lib/scsi/plugins/ses/vendor/amd64/SUN-Storage-J4400.so 0755 root bin
+f usr/lib/scsi/plugins/ses/vendor/amd64/SUN-Storage-J4500.so 0755 root bin
+f usr/lib/scsi/plugins/ses/vendor/amd64/SUN.so 0755 root bin
+d usr/lib/scsi/plugins/smp 0755 root bin
+d usr/lib/scsi/plugins/smp/engine 0755 root bin
+d usr/lib/scsi/plugins/smp/engine/amd64 0755 root bin
+f usr/lib/scsi/plugins/smp/engine/amd64/usmp.so 0755 root bin
+f usr/lib/scsi/plugins/smp/engine/usmp.so 0755 root bin
+d usr/lib/scsi/plugins/smp/framework 0755 root bin
+d usr/lib/scsi/plugins/smp/framework/amd64 0755 root bin
+f usr/lib/scsi/plugins/smp/framework/amd64/sas2.so 0755 root bin
+f usr/lib/scsi/plugins/smp/framework/sas2.so 0755 root bin
+f usr/lib/scsi/sestopo 0555 root bin
+f usr/lib/scsi/smp 0555 root bin
+d usr/lib/secure 0755 root bin
+s usr/lib/secure/32=.
+s usr/lib/secure/64=amd64
+d usr/lib/secure/amd64 0755 root bin
+d usr/lib/security 0755 root bin
+s usr/lib/security/64=amd64
+d usr/lib/security/amd64 0755 root bin
+f usr/lib/security/amd64/crypt_bsdbf.so.1 0755 root bin
+s usr/lib/security/amd64/crypt_bsdbf.so=crypt_bsdbf.so.1
+f usr/lib/security/amd64/crypt_bsdmd5.so.1 0755 root bin
+s usr/lib/security/amd64/crypt_bsdmd5.so=crypt_bsdmd5.so.1
+f usr/lib/security/amd64/crypt_sha256.so.1 0755 root bin
+s usr/lib/security/amd64/crypt_sha256.so=crypt_sha256.so.1
+f usr/lib/security/amd64/crypt_sha512.so.1 0755 root bin
+s usr/lib/security/amd64/crypt_sha512.so=crypt_sha512.so.1
+f usr/lib/security/amd64/crypt_sunmd5.so.1 0755 root bin
+s usr/lib/security/amd64/crypt_sunmd5.so=crypt_sunmd5.so.1
+f usr/lib/security/amd64/pam_allow.so.1 0755 root bin
+s usr/lib/security/amd64/pam_allow.so=pam_allow.so.1
+f usr/lib/security/amd64/pam_authtok_check.so.1 0755 root bin
+s usr/lib/security/amd64/pam_authtok_check.so=pam_authtok_check.so.1
+f usr/lib/security/amd64/pam_authtok_get.so.1 0755 root bin
+s usr/lib/security/amd64/pam_authtok_get.so=pam_authtok_get.so.1
+f usr/lib/security/amd64/pam_authtok_store.so.1 0755 root bin
+s usr/lib/security/amd64/pam_authtok_store.so=pam_authtok_store.so.1
+f usr/lib/security/amd64/pam_deny.so.1 0755 root bin
+s usr/lib/security/amd64/pam_deny.so=pam_deny.so.1
+f usr/lib/security/amd64/pam_dhkeys.so.1 0755 root bin
+s usr/lib/security/amd64/pam_dhkeys.so=pam_dhkeys.so.1
+f usr/lib/security/amd64/pam_dial_auth.so.1 0755 root bin
+s usr/lib/security/amd64/pam_dial_auth.so=pam_dial_auth.so.1
+f usr/lib/security/amd64/pam_krb5.so.1 0755 root bin
+s usr/lib/security/amd64/pam_krb5.so=pam_krb5.so.1
+f usr/lib/security/amd64/pam_krb5_migrate.so.1 0755 root bin
+s usr/lib/security/amd64/pam_krb5_migrate.so=pam_krb5_migrate.so.1
+f usr/lib/security/amd64/pam_ldap.so.1 0755 root bin
+s usr/lib/security/amd64/pam_ldap.so=pam_ldap.so.1
+f usr/lib/security/amd64/pam_list.so.1 0755 root bin
+s usr/lib/security/amd64/pam_list.so=pam_list.so.1
+f usr/lib/security/amd64/pam_passwd_auth.so.1 0755 root bin
+s usr/lib/security/amd64/pam_passwd_auth.so=pam_passwd_auth.so.1
+f usr/lib/security/amd64/pam_rhosts_auth.so.1 0755 root bin
+s usr/lib/security/amd64/pam_rhosts_auth.so=pam_rhosts_auth.so.1
+f usr/lib/security/amd64/pam_roles.so.1 0755 root bin
+s usr/lib/security/amd64/pam_roles.so=pam_roles.so.1
+f usr/lib/security/amd64/pam_sample.so.1 0755 root bin
+s usr/lib/security/amd64/pam_sample.so=pam_sample.so.1
+f usr/lib/security/amd64/pam_smb_passwd.so.1 0755 root bin
+s usr/lib/security/amd64/pam_smb_passwd.so=pam_smb_passwd.so.1
+f usr/lib/security/amd64/pam_smbfs_login.so.1 0755 root bin
+s usr/lib/security/amd64/pam_smbfs_login.so=pam_smbfs_login.so.1
+f usr/lib/security/amd64/pam_timestamp.so.1 0755 root bin
+s usr/lib/security/amd64/pam_timestamp.so=pam_timestamp.so.1
+f usr/lib/security/amd64/pam_unix_account.so.1 0755 root bin
+s usr/lib/security/amd64/pam_unix_account.so=pam_unix_account.so.1
+f usr/lib/security/amd64/pam_unix_auth.so.1 0755 root bin
+s usr/lib/security/amd64/pam_unix_auth.so=pam_unix_auth.so.1
+f usr/lib/security/amd64/pam_unix_cred.so.1 0755 root bin
+s usr/lib/security/amd64/pam_unix_cred.so=pam_unix_cred.so.1
+f usr/lib/security/amd64/pam_unix_session.so.1 0755 root bin
+s usr/lib/security/amd64/pam_unix_session.so=pam_unix_session.so.1
+f usr/lib/security/amd64/pkcs11_kernel.so.1 0755 root bin
+s usr/lib/security/amd64/pkcs11_kernel.so=pkcs11_kernel.so.1
+f usr/lib/security/amd64/pkcs11_softtoken.so.1 0755 root bin
+s usr/lib/security/amd64/pkcs11_softtoken.so=pkcs11_softtoken.so.1
+f usr/lib/security/audit_binfile.so.1 0755 root bin
+s usr/lib/security/audit_binfile.so=audit_binfile.so.1
+f usr/lib/security/audit_remote.so.1 0755 root bin
+s usr/lib/security/audit_remote.so=audit_remote.so.1
+f usr/lib/security/audit_syslog.so.1 0755 root bin
+s usr/lib/security/audit_syslog.so=audit_syslog.so.1
+f usr/lib/security/crypt_bsdbf.so.1 0755 root bin
+s usr/lib/security/crypt_bsdbf.so=crypt_bsdbf.so.1
+f usr/lib/security/crypt_bsdmd5.so.1 0755 root bin
+s usr/lib/security/crypt_bsdmd5.so=crypt_bsdmd5.so.1
+f usr/lib/security/crypt_sha256.so.1 0755 root bin
+s usr/lib/security/crypt_sha256.so=crypt_sha256.so.1
+f usr/lib/security/crypt_sha512.so.1 0755 root bin
+s usr/lib/security/crypt_sha512.so=crypt_sha512.so.1
+f usr/lib/security/crypt_sunmd5.so.1 0755 root bin
+s usr/lib/security/crypt_sunmd5.so=crypt_sunmd5.so.1
+f usr/lib/security/pam_allow.so.1 0755 root bin
+s usr/lib/security/pam_allow.so=pam_allow.so.1
+f usr/lib/security/pam_authtok_check.so.1 0755 root bin
+s usr/lib/security/pam_authtok_check.so=pam_authtok_check.so.1
+f usr/lib/security/pam_authtok_get.so.1 0755 root bin
+s usr/lib/security/pam_authtok_get.so=pam_authtok_get.so.1
+f usr/lib/security/pam_authtok_store.so.1 0755 root bin
+s usr/lib/security/pam_authtok_store.so=pam_authtok_store.so.1
+f usr/lib/security/pam_deny.so.1 0755 root bin
+s usr/lib/security/pam_deny.so=pam_deny.so.1
+f usr/lib/security/pam_dhkeys.so.1 0755 root bin
+s usr/lib/security/pam_dhkeys.so=pam_dhkeys.so.1
+f usr/lib/security/pam_dial_auth.so.1 0755 root bin
+s usr/lib/security/pam_dial_auth.so=pam_dial_auth.so.1
+f usr/lib/security/pam_krb5.so.1 0755 root bin
+s usr/lib/security/pam_krb5.so=pam_krb5.so.1
+f usr/lib/security/pam_krb5_first 0444 root bin
+f usr/lib/security/pam_krb5_migrate.so.1 0755 root bin
+s usr/lib/security/pam_krb5_migrate.so=pam_krb5_migrate.so.1
+f usr/lib/security/pam_krb5_only 0444 root bin
+f usr/lib/security/pam_krb5_optional 0444 root bin
+f usr/lib/security/pam_ldap.so.1 0755 root bin
+s usr/lib/security/pam_ldap.so=pam_ldap.so.1
+f usr/lib/security/pam_list.so.1 0755 root bin
+s usr/lib/security/pam_list.so=pam_list.so.1
+f usr/lib/security/pam_passwd_auth.so.1 0755 root bin
+s usr/lib/security/pam_passwd_auth.so=pam_passwd_auth.so.1
+f usr/lib/security/pam_rhosts_auth.so.1 0755 root bin
+s usr/lib/security/pam_rhosts_auth.so=pam_rhosts_auth.so.1
+f usr/lib/security/pam_roles.so.1 0755 root bin
+s usr/lib/security/pam_roles.so=pam_roles.so.1
+f usr/lib/security/pam_sample.so.1 0755 root bin
+s usr/lib/security/pam_sample.so=pam_sample.so.1
+f usr/lib/security/pam_smb_passwd.so.1 0755 root bin
+s usr/lib/security/pam_smb_passwd.so=pam_smb_passwd.so.1
+f usr/lib/security/pam_smbfs_login.so.1 0755 root bin
+s usr/lib/security/pam_smbfs_login.so=pam_smbfs_login.so.1
+f usr/lib/security/pam_unix_account.so.1 0755 root bin
+s usr/lib/security/pam_unix_account.so=pam_unix_account.so.1
+f usr/lib/security/pam_unix_auth.so.1 0755 root bin
+s usr/lib/security/pam_unix_auth.so=pam_unix_auth.so.1
+f usr/lib/security/pam_unix_cred.so.1 0755 root bin
+s usr/lib/security/pam_unix_cred.so=pam_unix_cred.so.1
+f usr/lib/security/pam_unix_session.so.1 0755 root bin
+s usr/lib/security/pam_unix_session.so=pam_unix_session.so.1
+f usr/lib/security/pkcs11_kernel.so.1 0755 root bin
+s usr/lib/security/pkcs11_kernel.so=pkcs11_kernel.so.1
+f usr/lib/security/pkcs11_softtoken.so.1 0755 root bin
+s usr/lib/security/pkcs11_softtoken.so=pkcs11_softtoken.so.1
+s usr/lib/sendmail=mailwrapper
+f usr/lib/servinfo 0555 root bin
+f usr/lib/set_keyboard_layout 0555 root bin
+d usr/lib/smbfs 0755 root bin
+f usr/lib/smbfs/smbiod 0555 root bin
+f usr/lib/smbfs/smbiod-svc 0555 root bin
+d usr/lib/smbsrv 0755 root bin
+d usr/lib/smbsrv/amd64 0755 root bin
+f usr/lib/smbsrv/amd64/libsmb.so.1 0555 root bin
+d usr/lib/smbsrv/dtrace 0755 root bin
+f usr/lib/smbsrv/dtrace/nbl-conflict.d 0555 root bin
+f usr/lib/smbsrv/dtrace/smb-trace.d 0555 root bin
+f usr/lib/smbsrv/dtrace/smb2-trace.d 0555 root bin
+f usr/lib/smbsrv/dtrace/smbd-all.d 0555 root bin
+f usr/lib/smbsrv/dtrace/smbd-authsvc.d 0555 root bin
+f usr/lib/smbsrv/dtrace/smbd-doorsvc.d 0555 root bin
+f usr/lib/smbsrv/dtrace/smbd-pipesvc.d 0555 root bin
+f usr/lib/smbsrv/dtrace/smbnode.d 0555 root bin
+f usr/lib/smbsrv/dtrace/smbsrv.d 0555 root bin
+f usr/lib/smbsrv/dtrace/smbvfs.d 0555 root bin
+f usr/lib/smbsrv/libmlsvc.so.1 0755 root bin
+f usr/lib/smbsrv/libsmb.so.1 0755 root bin
+f usr/lib/smbsrv/libsmbns.so.1 0755 root bin
+f usr/lib/smbsrv/smbd 0755 root bin
+d usr/lib/smedia 0755 root bin
+d usr/lib/smedia/amd64 0755 root bin
+f usr/lib/smedia/amd64/sm_blkdev.so.1 0555 root bin
+f usr/lib/smedia/amd64/sm_fd.so.1 0555 root bin
+f usr/lib/smedia/amd64/sm_scsi.so.1 0555 root bin
+f usr/lib/smedia/rpc.smserverd 0555 root bin
+f usr/lib/smedia/sm_blkdev.so.1 0555 root bin
+f usr/lib/smedia/sm_fd.so.1 0555 root bin
+f usr/lib/smedia/sm_scsi.so.1 0555 root bin
+f usr/lib/smrsh 0555 root bin
+d usr/lib/smtp 0555 root bin
+d usr/lib/smtp/sendmail 0555 root bin
+f usr/lib/smtp/sendmail/mailq 4555 root bin
+s usr/lib/smtp/sendmail/newaliases=sendmail
+f usr/lib/smtp/sendmail/sendmail 2555 root smmsp
+d usr/lib/snmp 0755 root bin
+d usr/lib/spell 0755 root bin
+f usr/lib/spell/compress 0555 root bin
+f usr/lib/spell/hashcheck 0555 root bin
+f usr/lib/spell/hashmake 0555 root bin
+f usr/lib/spell/hlista 0644 root bin
+f usr/lib/spell/hlistb 0644 root bin
+f usr/lib/spell/hstop 0644 root bin
+f usr/lib/spell/spellin 0555 root bin
+f usr/lib/spell/spellprog 0555 root bin
+f usr/lib/straddr.so.2 0755 root bin
+s usr/lib/straddr.so=straddr.so.2
+s usr/lib/sunw,rcp=../bin/rcp
+d usr/lib/sysevent 0755 root bin
+d usr/lib/sysevent/modules 0755 root bin
+f usr/lib/sysevent/modules/datalink_mod.so 0755 root bin
+f usr/lib/sysevent/modules/devfsadmd_mod.so 0755 root bin
+f usr/lib/sysevent/modules/picl_slm.so 0755 root bin
+f usr/lib/sysevent/modules/sysevent_conf_mod.so 0755 root bin
+f usr/lib/sysevent/modules/sysevent_reg_mod.so 0755 root bin
+f usr/lib/sysevent/modules/zfs_mod.so 0755 root bin
+f usr/lib/sysevent/syseventconfd 0555 root bin
+f usr/lib/sysevent/syseventd 0555 root bin
+s usr/lib/tabset=../share/lib/tabset
+f usr/lib/th_script 0555 root bin
+s usr/lib/tmac=../share/lib/tmac
+f usr/lib/udapl_tavor.so.1 0755 root bin
+f usr/lib/utmp_update 4555 root bin
+f usr/lib/utmpd 0555 root bin
+f usr/lib/values-Xa.o 0644 root bin
+f usr/lib/values-Xc.o 0644 root bin
+f usr/lib/values-Xs.o 0644 root bin
+f usr/lib/values-Xt.o 0644 root bin
+f usr/lib/values-xpg4.o 0644 root bin
+f usr/lib/values-xpg6.o 0644 root bin
+d usr/lib/varpd 0755 root bin
+s usr/lib/varpd/64=amd64
+d usr/lib/varpd/amd64 0755 root bin
+f usr/lib/varpd/amd64/libvarpd_direct.so.1 0555 root bin
+s usr/lib/varpd/amd64/libvarpd_direct.so=libvarpd_direct.so.1
+f usr/lib/varpd/amd64/libvarpd_files.so.1 0555 root bin
+s usr/lib/varpd/amd64/libvarpd_files.so=libvarpd_files.so.1
+f usr/lib/varpd/amd64/libvarpd_svp.so.1 0555 root bin
+s usr/lib/varpd/amd64/libvarpd_svp.so=libvarpd_svp.so.1
+f usr/lib/varpd/libvarpd_direct.so.1 0555 root bin
+s usr/lib/varpd/libvarpd_direct.so=libvarpd_direct.so.1
+f usr/lib/varpd/libvarpd_files.so.1 0555 root bin
+s usr/lib/varpd/libvarpd_files.so=libvarpd_files.so.1
+f usr/lib/varpd/libvarpd_svp.so.1 0555 root bin
+s usr/lib/varpd/libvarpd_svp.so=libvarpd_svp.so.1
+f usr/lib/varpd/varpd 0555 root bin
+f usr/lib/vfontedpr 0555 root bin
+f usr/lib/vgrindefs 0444 root bin
+d usr/lib/vscan 0755 root bin
+f usr/lib/vscan/libvscan.so.1 0755 root bin
+s usr/lib/vscan/libvscan.so=libvscan.so.1
+f usr/lib/vscan/vscand 0555 root bin
+f usr/lib/vtdaemon 0555 root bin
+f usr/lib/vtinfo 0555 root bin
+f usr/lib/vtxlock 0555 root bin
+f usr/lib/watchmalloc.so.1 0755 root bin
+d usr/lib/xhci 0755 root bin
+f usr/lib/xhci/xhci_portsc 0555 root bin
+d usr/lib/zfs 0755 root bin
+f usr/lib/zfs/availdevs 0555 root bin
+d usr/lib/zones 0755 root bin
+d usr/lib/zones/amd64 0755 root bin
+f usr/lib/zones/amd64/zoneadmd 0555 root bin
+d usr/lib/zones/i86 0755 root bin
+h usr/lib/zones/zoneadmd=usr/lib/isaexec
+f usr/lib/zones/zoneshare 0555 root bin
+f usr/lib/zones/zoneunshare 0555 root bin
+d usr/libexec 07555 root bin
+d usr/local 0755 root root
+s usr/mail=../var/mail
+s usr/man=share/man
+d usr/net 0755 root sys
+d usr/net/nls 0755 root sys
+s usr/net/nls/listen=../../lib/saf/listen
+s usr/net/nls/nlps_server=../../lib/saf/nlps_server
+d usr/net/servers 0755 root sys
+s usr/news=../var/news
+d usr/perl5 0755 root bin
+d usr/perl5/5.12 0755 root bin
+d usr/perl5/5.12/lib 0755 root bin
+d usr/perl5/5.12/lib/Sun 0755 root bin
+d usr/perl5/5.12/lib/Sun/Solaris 0755 root bin
+d usr/perl5/5.12/lib/Sun/Solaris/BSM 0755 root bin
+f usr/perl5/5.12/lib/Sun/Solaris/BSM/_BSMparse.pm 0444 root bin
+f usr/perl5/5.12/lib/Sun/Solaris/Pg.pm 0444 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int 0755 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/Sun 0755 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/Sun/Solaris 0755 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/Sun/Solaris/Intrs.pm 0444 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/Sun/Solaris/Kstat.pm 0444 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/Sun/Solaris/Lgrp.pm 0444 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/Sun/Solaris/Project.pm 0444 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/Sun/Solaris/Task.pm 0444 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/Sun/Solaris/Utils.pm 0444 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/auto 0755 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun 0755 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris 0755 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Intrs 0755 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Intrs/Intrs.so 0555 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Kstat 0755 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Kstat/Kstat.so 0555 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp 0755 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Lgrp/Lgrp.so 0555 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project 0755 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Project/Project.so 0555 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task 0755 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Task/Task.so 0555 root bin
+d usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Utils 0755 root bin
+f usr/perl5/5.12/lib/i86pc-solaris-64int/auto/Sun/Solaris/Utils/Utils.so 0555 root bin
+d usr/platform 0755 root sys
+d usr/platform/i86pc 0755 root sys
+d usr/platform/i86pc/lib 0755 root sys
+d usr/platform/i86pc/lib/fm 0755 root sys
+d usr/platform/i86pc/lib/fm/eft 0755 root sys
+f usr/platform/i86pc/lib/fm/eft/amd64.eft 0444 root sys
+f usr/platform/i86pc/lib/fm/eft/gcpu.eft 0444 root sys
+f usr/platform/i86pc/lib/fm/eft/gcpu_amd.eft 0444 root sys
+f usr/platform/i86pc/lib/fm/eft/intel.eft 0444 root sys
+d usr/platform/i86pc/lib/fm/topo 0755 root sys
+d usr/platform/i86pc/lib/fm/topo/maps 0755 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-1101-disk-hc-topology.xml 0444 root sys
+s usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3101-hc-topology.xml=./SSG-2028R-ACR24L-hc-topology.xml
+s usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3101-usb.usbtopo=./SSG-2028R-ACR24L-usb.usbtopo
+s usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3102-hc-topology.xml=./SSG-2028R-ACR24L-hc-topology.xml
+s usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3102-usb.usbtopo=./SSG-2028R-ACR24L-usb.usbtopo
+f usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3301-hc-topology.xml 0444 root sys
+s usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3302-hc-topology.xml=./Joyent-Compute-Platform-3301-hc-topology.xml
+f usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-330x-chassis-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-330x-fan-hc-topology.xml 0444 root sys
+s usr/platform/i86pc/lib/fm/topo/maps/Joyent-M12G5-hc-topology.xml=./SSG-2029P-ACR24L-hc-topology.xml
+s usr/platform/i86pc/lib/fm/topo/maps/Joyent-M12G5-usb.usbtopo=./SSG-2029P-ACR24L-usb.usbtopo
+s usr/platform/i86pc/lib/fm/topo/maps/Joyent-S10G5-hc-topology.xml=./SSG-6049P-E1CR36L-hc-topology.xml
+s usr/platform/i86pc/lib/fm/topo/maps/Joyent-S10G5-usb.usbtopo=./SSG-6049P-E1CR36L-usb.usbtopo
+f usr/platform/i86pc/lib/fm/topo/maps/Joyent-Storage-Platform-7001-chassis-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/Joyent-Storage-Platform-7001-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/Joyent-Storage-Platform-7001-slot-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-chassis-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-disk-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-slot-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-usb.usbtopo 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-chassis-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-disk-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-slot-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-usb.usbtopo 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-6049P-E1CR36L-chassis-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-6049P-E1CR36L-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-6049P-E1CR36L-slot-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SSG-6049P-E1CR36L-usb.usbtopo 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SYS-2028U-E1CNRT+-chassis-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SYS-2028U-E1CNRT+-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/SYS-2028U-E1CNRT+-usb.usbtopo 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/chassis-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/chip-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/fan-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/i86pc-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/i86pc-legacy-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/psu-hc-topology.xml 0444 root sys
+d usr/platform/i86pc/lib/fm/topo/plugins 0755 root sys
+f usr/platform/i86pc/lib/fm/topo/plugins/chip.so 0555 root sys
+f usr/platform/i86pc/lib/fm/topo/plugins/chipset.so 0555 root sys
+f usr/platform/i86pc/lib/fm/topo/plugins/hostbridge.so 0555 root sys
+f usr/platform/i86pc/lib/fm/topo/plugins/pcibus.so 0555 root sys
+f usr/platform/i86pc/lib/fm/topo/plugins/x86pi.so 0555 root sys
+d usr/platform/i86pc/lib/mdb 0755 root sys
+d usr/platform/i86pc/lib/mdb/kvm 0755 root sys
+d usr/platform/i86pc/lib/mdb/kvm/amd64 0755 root sys
+f usr/platform/i86pc/lib/mdb/kvm/amd64/apix.so 0555 root sys
+f usr/platform/i86pc/lib/mdb/kvm/amd64/pcplusmp.so 0555 root sys
+f usr/platform/i86pc/lib/mdb/kvm/amd64/unix.so 0555 root sys
+f usr/platform/i86pc/lib/mdb/kvm/amd64/uppc.so 0555 root sys
+s usr/preserve=../var/preserve
+d usr/proc 0755 root bin
+d usr/proc/bin 0755 root bin
+s usr/proc/bin/pcred=../../bin/pcred
+s usr/proc/bin/pfiles=../../bin/pfiles
+s usr/proc/bin/pflags=../../bin/pflags
+s usr/proc/bin/pldd=../../bin/pldd
+s usr/proc/bin/pmap=../../bin/pmap
+s usr/proc/bin/prun=../../bin/prun
+s usr/proc/bin/psig=../../bin/psig
+s usr/proc/bin/pstack=../../bin/pstack
+s usr/proc/bin/pstop=../../bin/pstop
+s usr/proc/bin/ptime=../../bin/ptime
+s usr/proc/bin/ptree=../../bin/ptree
+s usr/proc/bin/pwait=../../bin/pwait
+s usr/proc/bin/pwdx=../../bin/pwdx
+s usr/pub=share/lib/pub
+d usr/sadm 0755 root bin
+d usr/sadm/bin 0755 root bin
+h usr/sadm/bin/dispgid=usr/bin/ckgid
+h usr/sadm/bin/dispuid=usr/bin/ckuid
+h usr/sadm/bin/errange=usr/bin/ckrange
+h usr/sadm/bin/errdate=usr/bin/ckdate
+h usr/sadm/bin/errgid=usr/bin/ckgid
+h usr/sadm/bin/errint=usr/bin/ckint
+h usr/sadm/bin/erritem=usr/bin/ckitem
+h usr/sadm/bin/errpath=usr/bin/ckpath
+h usr/sadm/bin/errstr=usr/bin/ckstr
+h usr/sadm/bin/errtime=usr/bin/cktime
+h usr/sadm/bin/erruid=usr/bin/ckuid
+h usr/sadm/bin/erryorn=usr/bin/ckyorn
+h usr/sadm/bin/helpdate=usr/bin/ckdate
+h usr/sadm/bin/helpgid=usr/bin/ckgid
+h usr/sadm/bin/helpint=usr/bin/ckint
+h usr/sadm/bin/helpitem=usr/bin/ckitem
+h usr/sadm/bin/helppath=usr/bin/ckpath
+h usr/sadm/bin/helprange=usr/bin/ckrange
+h usr/sadm/bin/helpstr=usr/bin/ckstr
+h usr/sadm/bin/helptime=usr/bin/cktime
+h usr/sadm/bin/helpuid=usr/bin/ckuid
+h usr/sadm/bin/helpyorn=usr/bin/ckyorn
+f usr/sadm/bin/puttext 0555 root bin
+h usr/sadm/bin/valdate=usr/bin/ckdate
+h usr/sadm/bin/valgid=usr/bin/ckgid
+h usr/sadm/bin/valint=usr/bin/ckint
+h usr/sadm/bin/valpath=usr/bin/ckpath
+h usr/sadm/bin/valrange=usr/bin/ckrange
+h usr/sadm/bin/valstr=usr/bin/ckstr
+h usr/sadm/bin/valtime=usr/bin/cktime
+h usr/sadm/bin/valuid=usr/bin/ckuid
+h usr/sadm/bin/valyorn=usr/bin/ckyorn
+d usr/sadm/install 0755 root bin
+d usr/sadm/install/scripts 0755 root bin
+f usr/sadm/install/scripts/i.ipsecalgs 0555 root sys
+f usr/sadm/install/scripts/i.kcfconf 0555 root sys
+f usr/sadm/install/scripts/i.kmfconf 0555 root sys
+f usr/sadm/install/scripts/i.manifest 0555 root sys
+f usr/sadm/install/scripts/i.pkcs11conf 0555 root sys
+f usr/sadm/install/scripts/i.rbac 0555 root sys
+f usr/sadm/install/scripts/r.ipsecalgs 0555 root sys
+f usr/sadm/install/scripts/r.kcfconf 0555 root sys
+f usr/sadm/install/scripts/r.kmfconf 0555 root sys
+f usr/sadm/install/scripts/r.manifest 0555 root sys
+f usr/sadm/install/scripts/r.pkcs11conf 0555 root sys
+f usr/sadm/install/scripts/r.rbac 0555 root sys
+d usr/sadm/sysadm 0755 root bin
+d usr/sadm/sysadm/add-ons 0755 root bin
+d usr/sadm/sysadm/bin 0755 root bin
+f usr/sadm/ugdates 0444 root bin
+d usr/sbin 0755 root bin
+f usr/sbin/6to4relay 0555 root bin
+f usr/sbin/acctadm 0555 root bin
+f usr/sbin/acpidump 0555 root bin
+f usr/sbin/acpixtract 0555 root bin
+f usr/sbin/add_drv 0555 root bin
+f usr/sbin/allocate 4555 root bin
+d usr/sbin/amd64 0755 root bin
+f usr/sbin/amd64/dtrace 0555 root bin
+f usr/sbin/amd64/intrstat 0555 root bin
+f usr/sbin/amd64/ipf 0555 root bin
+f usr/sbin/amd64/ipfs 0555 root bin
+f usr/sbin/amd64/ipfstat 0555 root bin
+f usr/sbin/amd64/ipmon 0555 root bin
+f usr/sbin/amd64/ipnat 0555 root bin
+f usr/sbin/amd64/ippool 0555 root bin
+f usr/sbin/amd64/lockstat 0555 root bin
+f usr/sbin/amd64/plockstat 0555 root bin
+f usr/sbin/arp 0555 root bin
+f usr/sbin/audit 0555 root bin
+f usr/sbin/auditconfig 0555 root bin
+f usr/sbin/auditd 0555 root bin
+f usr/sbin/auditrecord 0555 root bin
+f usr/sbin/auditreduce 0555 root bin
+f usr/sbin/auditstat 0555 root bin
+h usr/sbin/audlinks=usr/sbin/devfsadm
+s usr/sbin/automount=../lib/fs/autofs/automount
+s usr/sbin/autopush=../../sbin/autopush
+f usr/sbin/bhyve 0555 root sys
+f usr/sbin/bhyvectl 0555 root sys
+f usr/sbin/ccidadm 0555 root bin
+f usr/sbin/cfgadm 0555 root bin
+f usr/sbin/check-hostname 0555 root mail
+f usr/sbin/check-permissions 0555 root mail
+f usr/sbin/chk_encodings 0555 root sys
+f usr/sbin/chroot 0555 root bin
+f usr/sbin/clear_locks 0555 root bin
+f usr/sbin/clinfo 0555 root bin
+f usr/sbin/clri 0555 root bin
+f usr/sbin/consadm 0555 root sys
+f usr/sbin/consadmd 0555 root sys
+f usr/sbin/cpustat 0555 root bin
+f usr/sbin/cron 0555 root sys
+s usr/sbin/cryptoadm=../../sbin/cryptoadm
+f usr/sbin/cxgbetool 0555 root sys
+s usr/sbin/dcopy=clri
+h usr/sbin/deallocate=usr/sbin/allocate
+f usr/sbin/devfsadm 0755 root sys
+f usr/sbin/devinfo 0555 root bin
+h usr/sbin/devlinks=usr/sbin/devfsadm
+s usr/sbin/devnm=df
+f usr/sbin/df 0555 root bin
+f usr/sbin/dfmounts 0555 root bin
+h usr/sbin/dfshares=usr/sbin/dfmounts
+h usr/sbin/disks=usr/sbin/devfsadm
+f usr/sbin/dispadmin 0555 root bin
+s usr/sbin/dladm=../../sbin/dladm
+s usr/sbin/dlstat=../../sbin/dlstat
+f usr/sbin/dmesg 0555 root bin
+f usr/sbin/dminfo 0555 root bin
+h usr/sbin/drvconfig=usr/sbin/devfsadm
+h usr/sbin/dtrace=usr/lib/isaexec
+f usr/sbin/dumpadm 0555 root bin
+f usr/sbin/editmap 0555 root bin
+s usr/sbin/edquota=../lib/fs/ufs/edquota
+f usr/sbin/eeprom 2555 root sys
+f usr/sbin/etrn 0555 root bin
+f usr/sbin/exportfs 0555 root bin
+f usr/sbin/fcadm 0555 root bin
+h usr/sbin/fcinfo=usr/sbin/fcadm
+s usr/sbin/fdisk=../../sbin/fdisk
+f usr/sbin/ff 0555 root bin
+s usr/sbin/fiocompress=../../sbin/fiocompress
+s usr/sbin/flowadm=../../sbin/flowadm
+s usr/sbin/flowstat=../../sbin/flowstat
+f usr/sbin/fmadm 0555 root bin
+f usr/sbin/fmdump 0555 root bin
+f usr/sbin/fmstat 0555 root bin
+f usr/sbin/fmthard 0555 root sys
+f usr/sbin/format 0555 root bin
+f usr/sbin/fsck 0555 root bin
+s usr/sbin/fsdb=clri
+s usr/sbin/fsirand=../lib/fs/ufs/fsirand
+s usr/sbin/fssnap=clri
+f usr/sbin/fstyp 0555 root sys
+f usr/sbin/fuser 0555 root bin
+f usr/sbin/fwflash 0555 root bin
+f usr/sbin/getdevpolicy 0555 root sys
+f usr/sbin/getmajor 0755 root sys
+f usr/sbin/groupadd 0555 root sys
+f usr/sbin/groupdel 0555 root sys
+f usr/sbin/groupmod 0555 root sys
+f usr/sbin/grpck 0555 root bin
+f usr/sbin/grub 0555 root bin
+f usr/sbin/gsscred 0555 root sys
+f usr/sbin/halt 0755 root bin
+s usr/sbin/hostconfig=../../sbin/hostconfig
+f usr/sbin/hotplug 0555 root bin
+d usr/sbin/i86 0755 root bin
+f usr/sbin/iasl 0555 root bin
+f usr/sbin/idmap 0555 root bin
+f usr/sbin/if_mpadm 0555 root bin
+s usr/sbin/ifconfig=../../sbin/ifconfig
+f usr/sbin/ikeadm 0555 root bin
+f usr/sbin/ikecert 0555 root bin
+f usr/sbin/ilbadm 0555 root bin
+f usr/sbin/in.comsat 0555 root bin
+f usr/sbin/in.fingerd 0555 root bin
+f usr/sbin/in.rdisc 0555 root bin
+f usr/sbin/in.rexecd 0555 root bin
+f usr/sbin/in.rlogind 0555 root bin
+f usr/sbin/in.routed 0555 root bin
+f usr/sbin/in.rshd 0555 root bin
+f usr/sbin/in.rwhod 0555 root bin
+f usr/sbin/in.talkd 0555 root bin
+f usr/sbin/in.telnetd 0555 root bin
+f usr/sbin/in.tftpd 0555 root bin
+f usr/sbin/inetadm 0555 root bin
+f usr/sbin/inetconv 0555 root bin
+s usr/sbin/inetd=../lib/inet/inetd
+s usr/sbin/init=../../sbin/init
+f usr/sbin/install 0555 root bin
+f usr/sbin/installboot 0555 root sys
+s usr/sbin/installgrub=../../sbin/installgrub
+h usr/sbin/intrstat=usr/lib/isaexec
+f usr/sbin/ipaddrsel 0555 root bin
+s usr/sbin/ipadm=../../sbin/ipadm
+f usr/sbin/ipdadm 0555 root bin
+h usr/sbin/ipf=usr/lib/isaexec
+h usr/sbin/ipfs=usr/lib/isaexec
+h usr/sbin/ipfstat=usr/lib/isaexec
+h usr/sbin/ipmon=usr/lib/isaexec
+s usr/sbin/ipmpstat=../../sbin/ipmpstat
+h usr/sbin/ipnat=usr/lib/isaexec
+h usr/sbin/ippool=usr/lib/isaexec
+f usr/sbin/ipsecalgs 0555 root bin
+f usr/sbin/ipsecconf 0555 root bin
+f usr/sbin/ipseckey 0555 root bin
+f usr/sbin/iscsiadm 0555 root bin
+f usr/sbin/itadm 0555 root bin
+f usr/sbin/keyserv 0555 root sys
+f usr/sbin/killall 0555 root bin
+s usr/sbin/labelit=clri
+f usr/sbin/ldapaddent 0555 root bin
+f usr/sbin/ldapclient 0555 root bin
+f usr/sbin/link 0555 root bin
+h usr/sbin/list_devices=usr/sbin/allocate
+f usr/sbin/locator 0555 root bin
+s usr/sbin/lockfs=../lib/fs/ufs/lockfs
+h usr/sbin/lockstat=usr/lib/isaexec
+f usr/sbin/lofiadm 0555 root bin
+f usr/sbin/logadm 0555 root bin
+f usr/sbin/makedbm 0555 root bin
+f usr/sbin/makemap 0555 root bin
+f usr/sbin/mkdevalloc 0555 root bin
+h usr/sbin/mkdevmaps=usr/sbin/mkdevalloc
+f usr/sbin/mkfile 0555 root bin
+s usr/sbin/mkfs=clri
+f usr/sbin/mknod 0555 root bin
+f usr/sbin/modinfo 0555 root sys
+f usr/sbin/modload 0555 root sys
+f usr/sbin/modunload 0555 root sys
+s usr/sbin/mount=../../sbin/mount
+f usr/sbin/mountall 0555 root sys
+f usr/sbin/mpathadm 0555 root bin
+f usr/sbin/msgid 0555 root bin
+f usr/sbin/mvdir 0555 root bin
+s usr/sbin/ncheck=ff
+f usr/sbin/ndd 0555 root bin
+f usr/sbin/ndp 0555 root bin
+s usr/sbin/newaliases=../lib/mailwrapper
+s usr/sbin/newfs=../lib/fs/ufs/newfs
+f usr/sbin/newkey 0555 root sys
+f usr/sbin/nfsref 0555 root bin
+f usr/sbin/nlsadmin 0755 root adm
+f usr/sbin/nltest 0555 root bin
+f usr/sbin/nscd 0555 root bin
+f usr/sbin/nvmeadm 0555 root bin
+f usr/sbin/nwamadm 0555 root bin
+f usr/sbin/nwamcfg 0555 root bin
+f usr/sbin/pbind 0555 root sys
+f usr/sbin/pcitool 0555 root bin
+f usr/sbin/ping 4555 root bin
+h usr/sbin/plockstat=usr/lib/isaexec
+f usr/sbin/pmadm 0555 root sys
+f usr/sbin/pmconfig 4555 root bin
+h usr/sbin/ports=usr/sbin/devfsadm
+h usr/sbin/poweroff=usr/sbin/halt
+f usr/sbin/pptadm 0555 root bin
+f usr/sbin/praudit 0555 root bin
+f usr/sbin/projadd 0555 root sys
+f usr/sbin/projdel 0555 root sys
+f usr/sbin/projmod 0555 root sys
+f usr/sbin/prtconf 2555 root sys
+f usr/sbin/prtdiag 2755 root sys
+f usr/sbin/prtpicl 0755 root bin
+f usr/sbin/prtvtoc 0555 root sys
+f usr/sbin/psradm 0555 root sys
+f usr/sbin/psrinfo 0555 root sys
+f usr/sbin/psrset 0555 root sys
+f usr/sbin/pwck 0555 root bin
+f usr/sbin/pwconv 0555 root sys
+s usr/sbin/quot=../lib/fs/ufs/quot
+s usr/sbin/quota=../lib/fs/ufs/quota
+s usr/sbin/quotacheck=../lib/fs/ufs/quotacheck
+s usr/sbin/quotaoff=../lib/fs/ufs/quotaoff
+s usr/sbin/quotaon=../lib/fs/ufs/quotaon
+f usr/sbin/raidctl 0555 root bin
+f usr/sbin/ramdiskadm 0555 root bin
+f usr/sbin/rcapadm 0555 root bin
+f usr/sbin/rctladm 0555 root bin
+f usr/sbin/rdmsr 0555 root bin
+h usr/sbin/reboot=usr/sbin/halt
+f usr/sbin/rem_drv 0555 root sys
+s usr/sbin/repquota=../lib/fs/ufs/repquota
+f usr/sbin/rmt 0555 root bin
+f usr/sbin/roleadd 0555 root sys
+f usr/sbin/roledel 0555 root sys
+f usr/sbin/rolemod 0555 root sys
+s usr/sbin/route=../../sbin/route
+s usr/sbin/routeadm=../../sbin/routeadm
+f usr/sbin/rpcbind 0555 root bin
+f usr/sbin/rtc 0555 root bin
+f usr/sbin/rtquery 0555 root bin
+f usr/sbin/rwall 0555 root bin
+f usr/sbin/sacadm 4755 root sys
+f usr/sbin/safe_finger 0555 root bin
+f usr/sbin/sasinfo 0555 root bin
+f usr/sbin/sbdadm 0555 root bin
+s usr/sbin/sendmail=../lib/mailwrapper
+f usr/sbin/setmnt 0555 root bin
+h usr/sbin/share=usr/sbin/sharemgr
+f usr/sbin/shareall 0555 root bin
+f usr/sbin/sharectl 0555 root bin
+f usr/sbin/sharemgr 0555 root bin
+s usr/sbin/showmount=../lib/fs/nfs/showmount
+f usr/sbin/shutdown 0755 root sys
+f usr/sbin/smbadm 0555 root bin
+f usr/sbin/smbios 0555 root bin
+f usr/sbin/smbstat 0555 root bin
+f usr/sbin/snoop 0555 root bin
+f usr/sbin/sppptun 0555 root bin
+f usr/sbin/spray 0555 root bin
+f usr/sbin/stmfadm 0555 root bin
+f usr/sbin/stmsboot 0555 root bin
+f usr/sbin/strclean 0555 root sys
+f usr/sbin/strerr 0555 root sys
+f usr/sbin/sttydefs 0755 root sys
+s usr/sbin/svcadm=../../sbin/svcadm
+f usr/sbin/svccfg 0555 root bin
+f usr/sbin/swap 2555 root sys
+s usr/sbin/sync=../../sbin/sync
+f usr/sbin/syncinit 0555 root bin
+f usr/sbin/syncloop 0555 root bin
+f usr/sbin/syncstat 0555 root bin
+f usr/sbin/sysdef 2555 root sys
+f usr/sbin/syseventadm 0555 root sys
+f usr/sbin/syslogd 0555 root sys
+h usr/sbin/tapes=usr/sbin/devfsadm
+f usr/sbin/tar 0555 root bin
+f usr/sbin/tcpd 0555 root bin
+f usr/sbin/tcpdchk 0555 root bin
+f usr/sbin/tcpdmatch 0555 root bin
+f usr/sbin/traceroute 4555 root bin
+h usr/sbin/trapstat=usr/lib/platexec
+f usr/sbin/try-from 0555 root bin
+f usr/sbin/ttyadm 0755 root sys
+s usr/sbin/tunefs=../lib/fs/ufs/tunefs
+s usr/sbin/tzreload=../../sbin/tzreload
+s usr/sbin/uadmin=../../sbin/uadmin
+f usr/sbin/ucodeadm 0555 root bin
+s usr/sbin/ufsdump=../lib/fs/ufs/ufsdump
+s usr/sbin/ufsrestore=../lib/fs/ufs/ufsrestore
+s usr/sbin/umount=../../sbin/umount
+f usr/sbin/umountall 0555 root sys
+f usr/sbin/unlink 0555 root bin
+h usr/sbin/unshare=usr/sbin/sharemgr
+f usr/sbin/unshareall 0555 root bin
+f usr/sbin/update_drv 0555 root sys
+h usr/sbin/useradd=usr/sbin/roleadd
+h usr/sbin/userdel=usr/sbin/roledel
+h usr/sbin/usermod=usr/sbin/rolemod
+f usr/sbin/vndadm 0555 root bin
+f usr/sbin/volcopy 0555 root bin
+f usr/sbin/vrrpadm 0555 root bin
+f usr/sbin/wall 2555 root tty
+f usr/sbin/whodo 4555 root bin
+f usr/sbin/ypalias 0555 root bin
+f usr/sbin/ypinit 0555 root bin
+f usr/sbin/yppoll 0555 root bin
+f usr/sbin/ypset 0555 root bin
+f usr/sbin/zdb 0555 root bin
+f usr/sbin/zdump 0555 root bin
+s usr/sbin/zfs=../../sbin/zfs
+f usr/sbin/zic 0555 root bin
+f usr/sbin/zinject 0555 root bin
+f usr/sbin/zlogin 0555 root bin
+f usr/sbin/zoneadm 0555 root bin
+f usr/sbin/zonecfg 0555 root bin
+s usr/sbin/zpool=../../sbin/zpool
+f usr/sbin/zstreamdump 0555 root bin
+d usr/sfw 0755 root bin
+d usr/sfw/bin 0755 root bin
+d usr/sfw/bin/amd64 0755 root bin
+d usr/share 0755 root sys
+d usr/share/hwdata 0755 root sys
+f usr/share/hwdata/efi.fixes 0444 root bin
+f usr/share/hwdata/pci.ids 0444 root bin
+d usr/share/lib 0755 root sys
+d usr/share/lib/dict 0755 root bin
+f usr/share/lib/dict/words 0444 root bin
+s usr/share/lib/fonts=../../../boot/fonts
+d usr/share/lib/idn 0755 root sys
+d usr/share/lib/java 0755 root sys
+d usr/share/lib/keytables 0755 root bin
+d usr/share/lib/keytables/type_6 0755 root bin
+f usr/share/lib/keytables/type_6/albania 0444 root bin
+f usr/share/lib/keytables/type_6/arabia 0444 root bin
+f usr/share/lib/keytables/type_6/belarus 0444 root bin
+f usr/share/lib/keytables/type_6/belgium 0444 root bin
+f usr/share/lib/keytables/type_6/brazil 0444 root bin
+f usr/share/lib/keytables/type_6/bulgaria 0444 root bin
+f usr/share/lib/keytables/type_6/canadian_bilingual 0444 root bin
+f usr/share/lib/keytables/type_6/canadian_french 0444 root bin
+f usr/share/lib/keytables/type_6/croatia 0444 root bin
+f usr/share/lib/keytables/type_6/czech 0444 root bin
+f usr/share/lib/keytables/type_6/denmark 0444 root bin
+f usr/share/lib/keytables/type_6/dvorak 0444 root bin
+f usr/share/lib/keytables/type_6/estonia 0444 root bin
+f usr/share/lib/keytables/type_6/finnish 0444 root bin
+f usr/share/lib/keytables/type_6/france 0444 root bin
+f usr/share/lib/keytables/type_6/germany 0444 root bin
+f usr/share/lib/keytables/type_6/greece 0444 root bin
+f usr/share/lib/keytables/type_6/hungary 0444 root bin
+f usr/share/lib/keytables/type_6/iceland 0444 root bin
+f usr/share/lib/keytables/type_6/italy 0444 root bin
+f usr/share/lib/keytables/type_6/japan 0444 root bin
+f usr/share/lib/keytables/type_6/kbd_layouts 0444 root bin
+f usr/share/lib/keytables/type_6/korea 0444 root bin
+f usr/share/lib/keytables/type_6/latinamerica 0444 root bin
+f usr/share/lib/keytables/type_6/latvia 0444 root bin
+h usr/share/lib/keytables/type_6/layout_00=usr/share/lib/keytables/type_6/us
+h usr/share/lib/keytables/type_6/layout_01=usr/share/lib/keytables/type_6/arabia
+h usr/share/lib/keytables/type_6/layout_02=usr/share/lib/keytables/type_6/belgium
+h usr/share/lib/keytables/type_6/layout_03=usr/share/lib/keytables/type_6/canadian_bilingual
+h usr/share/lib/keytables/type_6/layout_04=usr/share/lib/keytables/type_6/canadian_french
+h usr/share/lib/keytables/type_6/layout_05=usr/share/lib/keytables/type_6/czech
+h usr/share/lib/keytables/type_6/layout_06=usr/share/lib/keytables/type_6/denmark
+h usr/share/lib/keytables/type_6/layout_07=usr/share/lib/keytables/type_6/finnish
+h usr/share/lib/keytables/type_6/layout_08=usr/share/lib/keytables/type_6/france
+h usr/share/lib/keytables/type_6/layout_09=usr/share/lib/keytables/type_6/germany
+h usr/share/lib/keytables/type_6/layout_0a=usr/share/lib/keytables/type_6/greece
+h usr/share/lib/keytables/type_6/layout_0c=usr/share/lib/keytables/type_6/hungary
+h usr/share/lib/keytables/type_6/layout_0e=usr/share/lib/keytables/type_6/italy
+h usr/share/lib/keytables/type_6/layout_0f=usr/share/lib/keytables/type_6/japan
+h usr/share/lib/keytables/type_6/layout_100=usr/share/lib/keytables/type_6/slovenia
+h usr/share/lib/keytables/type_6/layout_101=usr/share/lib/keytables/type_6/serbiaandmontenegro
+h usr/share/lib/keytables/type_6/layout_102=usr/share/lib/keytables/type_6/iceland
+h usr/share/lib/keytables/type_6/layout_103=usr/share/lib/keytables/type_6/croatia
+h usr/share/lib/keytables/type_6/layout_104=usr/share/lib/keytables/type_6/bulgaria
+h usr/share/lib/keytables/type_6/layout_105=usr/share/lib/keytables/type_6/belarus
+h usr/share/lib/keytables/type_6/layout_106=usr/share/lib/keytables/type_6/maltaus
+h usr/share/lib/keytables/type_6/layout_107=usr/share/lib/keytables/type_6/maltauk
+h usr/share/lib/keytables/type_6/layout_108=usr/share/lib/keytables/type_6/albania
+h usr/share/lib/keytables/type_6/layout_109=usr/share/lib/keytables/type_6/lithuania
+h usr/share/lib/keytables/type_6/layout_10=usr/share/lib/keytables/type_6/korea
+h usr/share/lib/keytables/type_6/layout_10a=usr/share/lib/keytables/type_6/latvia
+h usr/share/lib/keytables/type_6/layout_10b=usr/share/lib/keytables/type_6/macedonia
+h usr/share/lib/keytables/type_6/layout_10f=usr/share/lib/keytables/type_6/japan
+h usr/share/lib/keytables/type_6/layout_110=usr/share/lib/keytables/type_6/brazil
+h usr/share/lib/keytables/type_6/layout_111=usr/share/lib/keytables/type_6/dvorak
+h usr/share/lib/keytables/type_6/layout_112=usr/share/lib/keytables/type_6/estonia
+h usr/share/lib/keytables/type_6/layout_113=usr/share/lib/keytables/type_6/romania
+h usr/share/lib/keytables/type_6/layout_11=usr/share/lib/keytables/type_6/latinamerica
+h usr/share/lib/keytables/type_6/layout_12=usr/share/lib/keytables/type_6/netherlands
+h usr/share/lib/keytables/type_6/layout_13=usr/share/lib/keytables/type_6/norway
+h usr/share/lib/keytables/type_6/layout_15=usr/share/lib/keytables/type_6/poland
+h usr/share/lib/keytables/type_6/layout_16=usr/share/lib/keytables/type_6/portugal
+h usr/share/lib/keytables/type_6/layout_17=usr/share/lib/keytables/type_6/russia
+h usr/share/lib/keytables/type_6/layout_18=usr/share/lib/keytables/type_6/slovakia
+h usr/share/lib/keytables/type_6/layout_19=usr/share/lib/keytables/type_6/spain
+h usr/share/lib/keytables/type_6/layout_1a=usr/share/lib/keytables/type_6/sweden
+h usr/share/lib/keytables/type_6/layout_1b=usr/share/lib/keytables/type_6/swiss_french
+h usr/share/lib/keytables/type_6/layout_1c=usr/share/lib/keytables/type_6/swiss_german
+h usr/share/lib/keytables/type_6/layout_1e=usr/share/lib/keytables/type_6/traditional_chinese
+h usr/share/lib/keytables/type_6/layout_1f=usr/share/lib/keytables/type_6/turkeyq
+h usr/share/lib/keytables/type_6/layout_20=usr/share/lib/keytables/type_6/uk
+h usr/share/lib/keytables/type_6/layout_21=usr/share/lib/keytables/type_6/us
+h usr/share/lib/keytables/type_6/layout_23=usr/share/lib/keytables/type_6/turkeyf
+f usr/share/lib/keytables/type_6/lithuania 0444 root bin
+f usr/share/lib/keytables/type_6/macedonia 0444 root bin
+f usr/share/lib/keytables/type_6/maltauk 0444 root bin
+f usr/share/lib/keytables/type_6/maltaus 0444 root bin
+f usr/share/lib/keytables/type_6/netherlands 0444 root bin
+f usr/share/lib/keytables/type_6/norway 0444 root bin
+f usr/share/lib/keytables/type_6/poland 0444 root bin
+f usr/share/lib/keytables/type_6/portugal 0444 root bin
+f usr/share/lib/keytables/type_6/reset 0444 root bin
+f usr/share/lib/keytables/type_6/romania 0444 root bin
+f usr/share/lib/keytables/type_6/russia 0444 root bin
+f usr/share/lib/keytables/type_6/serbiaandmontenegro 0444 root bin
+f usr/share/lib/keytables/type_6/slovakia 0444 root bin
+f usr/share/lib/keytables/type_6/slovenia 0444 root bin
+f usr/share/lib/keytables/type_6/spain 0444 root bin
+f usr/share/lib/keytables/type_6/sweden 0444 root bin
+f usr/share/lib/keytables/type_6/swiss_french 0444 root bin
+f usr/share/lib/keytables/type_6/swiss_german 0444 root bin
+f usr/share/lib/keytables/type_6/traditional_chinese 0444 root bin
+f usr/share/lib/keytables/type_6/turkeyf 0444 root bin
+f usr/share/lib/keytables/type_6/turkeyq 0444 root bin
+f usr/share/lib/keytables/type_6/uk 0444 root bin
+f usr/share/lib/keytables/type_6/us 0444 root bin
+d usr/share/lib/mailx 0755 root bin
+d usr/share/lib/nterm 0755 root bin
+f usr/share/lib/nterm/tab.2631 0644 root bin
+f usr/share/lib/nterm/tab.2631-c 0644 root bin
+f usr/share/lib/nterm/tab.2631-e 0644 root bin
+f usr/share/lib/nterm/tab.300 0644 root bin
+f usr/share/lib/nterm/tab.300-12 0644 root bin
+f usr/share/lib/nterm/tab.300S 0644 root bin
+f usr/share/lib/nterm/tab.300S-12 0644 root bin
+f usr/share/lib/nterm/tab.300s 0644 root bin
+f usr/share/lib/nterm/tab.300s-12 0644 root bin
+f usr/share/lib/nterm/tab.37 0644 root bin
+f usr/share/lib/nterm/tab.382 0644 root bin
+f usr/share/lib/nterm/tab.4000A 0644 root bin
+f usr/share/lib/nterm/tab.4000a 0644 root bin
+f usr/share/lib/nterm/tab.450 0644 root bin
+f usr/share/lib/nterm/tab.450-12 0644 root bin
+f usr/share/lib/nterm/tab.832 0644 root bin
+f usr/share/lib/nterm/tab.8510 0644 root bin
+f usr/share/lib/nterm/tab.X 0644 root bin
+f usr/share/lib/nterm/tab.lp 0644 root bin
+f usr/share/lib/nterm/tab.tn300 0644 root bin
+d usr/share/lib/pub 0755 root bin
+f usr/share/lib/pub/ascii 0644 root bin
+f usr/share/lib/pub/eqnchar 0644 root bin
+f usr/share/lib/pub/greek 0644 root bin
+f usr/share/lib/pub/iso 0644 root bin
+d usr/share/lib/sgml 0755 root bin
+d usr/share/lib/tabset 0755 root bin
+f usr/share/lib/tabset/3101 0644 root bin
+f usr/share/lib/tabset/beehive 0644 root bin
+f usr/share/lib/tabset/hds 0644 root bin
+f usr/share/lib/tabset/hds3 0644 root bin
+f usr/share/lib/tabset/std 0644 root bin
+f usr/share/lib/tabset/stdcrt 0644 root bin
+f usr/share/lib/tabset/teleray 0644 root bin
+f usr/share/lib/tabset/vt100 0644 root bin
+f usr/share/lib/tabset/wyse-adds 0644 root bin
+f usr/share/lib/tabset/xerox1720 0644 root bin
+f usr/share/lib/termcap 0644 root bin
+d usr/share/lib/terminfo 0755 root bin
+d usr/share/lib/terminfo/3 0755 root bin
+f usr/share/lib/terminfo/3/386at 0644 root bin
+d usr/share/lib/terminfo/A 0755 root bin
+d usr/share/lib/terminfo/a 0755 root bin
+f usr/share/lib/terminfo/a/ansi 0644 root bin
+f usr/share/lib/terminfo/a/ansi+arrows 0644 root bin
+f usr/share/lib/terminfo/a/at386 0644 root bin
+d usr/share/lib/terminfo/r 0755 root bin
+f usr/share/lib/terminfo/r/rxvt-unicode 0644 root bin
+f usr/share/lib/terminfo/r/rxvt-unicode-256color 0644 root bin
+d usr/share/lib/terminfo/s 0755 root bin
+f usr/share/lib/terminfo/s/screen 0644 root bin
+f usr/share/lib/terminfo/s/screen-256color 0644 root bin
+f usr/share/lib/terminfo/s/screen-w 0644 root bin
+f usr/share/lib/terminfo/s/sun 0644 root bin
+f usr/share/lib/terminfo/s/sun-color 0644 root bin
+d usr/share/lib/terminfo/u 0755 root bin
+f usr/share/lib/terminfo/u/unknown 0644 root bin
+d usr/share/lib/terminfo/v 0755 root bin
+f usr/share/lib/terminfo/v/vt100 0644 root bin
+f usr/share/lib/terminfo/v/vt220 0644 root bin
+d usr/share/lib/terminfo/x 0755 root bin
+f usr/share/lib/terminfo/x/xterm 0644 root bin
+f usr/share/lib/terminfo/x/xterm-256color 0644 root bin
+f usr/share/lib/terminfo/x/xterm-color 0644 root bin
+f usr/share/lib/terminfo/x/xtermc 0644 root bin
+f usr/share/lib/terminfo/x/xtermm 0644 root bin
+f usr/share/lib/terminfo/x/xterms 0644 root bin
+d usr/share/lib/tmac 0755 root bin
+f usr/share/lib/tmac/acm.me 0644 root bin
+f usr/share/lib/tmac/an 0644 root bin
+f usr/share/lib/tmac/ansun 0644 root bin
+f usr/share/lib/tmac/ansun.tbl 0644 root bin
+f usr/share/lib/tmac/bib 0644 root bin
+f usr/share/lib/tmac/chars.me 0644 root bin
+f usr/share/lib/tmac/deltext.me 0644 root bin
+f usr/share/lib/tmac/e 0644 root bin
+f usr/share/lib/tmac/eqn.me 0644 root bin
+f usr/share/lib/tmac/float.me 0644 root bin
+f usr/share/lib/tmac/footnote.me 0644 root bin
+f usr/share/lib/tmac/index.me 0644 root bin
+f usr/share/lib/tmac/local.me 0644 root bin
+f usr/share/lib/tmac/m 0644 root bin
+f usr/share/lib/tmac/mmn 0644 root bin
+f usr/share/lib/tmac/mmt 0644 root bin
+f usr/share/lib/tmac/ms.acc 0644 root bin
+f usr/share/lib/tmac/ms.cov 0644 root bin
+f usr/share/lib/tmac/ms.eqn 0644 root bin
+f usr/share/lib/tmac/ms.ref 0644 root bin
+f usr/share/lib/tmac/ms.tbl 0644 root bin
+f usr/share/lib/tmac/ms.ths 0644 root bin
+f usr/share/lib/tmac/ms.toc 0644 root bin
+f usr/share/lib/tmac/null.me 0644 root bin
+f usr/share/lib/tmac/refer.me 0644 root bin
+f usr/share/lib/tmac/s 0644 root bin
+f usr/share/lib/tmac/sh.me 0644 root bin
+f usr/share/lib/tmac/tbl.me 0644 root bin
+f usr/share/lib/tmac/thesis.me 0644 root bin
+f usr/share/lib/tmac/tmac.bib 0644 root bin
+f usr/share/lib/tmac/tmac.vgrind 0644 root bin
+f usr/share/lib/tmac/tz.map 0644 root bin
+f usr/share/lib/tmac/v 0644 root bin
+f usr/share/lib/tmac/vgrind 0644 root bin
+f usr/share/lib/unittab 0444 root bin
+d usr/share/lib/xml 0755 root sys
+d usr/share/lib/xml/dtd 0755 root sys
+f usr/share/lib/xml/dtd/adt_record.dtd.1 0444 root bin
+f usr/share/lib/xml/dtd/brand.dtd.1 0644 root bin
+f usr/share/lib/xml/dtd/digraph-topology.dtd.1 0644 root bin
+f usr/share/lib/xml/dtd/kmfpolicy.dtd 0444 root bin
+f usr/share/lib/xml/dtd/rm_pool.dtd.1 0444 root bin
+f usr/share/lib/xml/dtd/service_bundle.dtd.1 0444 root sys
+f usr/share/lib/xml/dtd/topology.dtd.1 0444 root bin
+f usr/share/lib/xml/dtd/zone_platform.dtd.1 0644 root bin
+f usr/share/lib/xml/dtd/zonecfg.dtd.1 0444 root bin
+d usr/share/lib/xml/style 0755 root sys
+f usr/share/lib/xml/style/adt_record.xsl.1 0444 root bin
+d usr/share/lib/zoneinfo 0755 root bin
+d usr/share/lib/zoneinfo/Africa 0755 root bin
+f usr/share/lib/zoneinfo/Africa/Abidjan 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Accra=usr/share/lib/zoneinfo/Africa/Abidjan
+h usr/share/lib/zoneinfo/Africa/Addis_Ababa=usr/share/lib/zoneinfo/Africa/Nairobi
+f usr/share/lib/zoneinfo/Africa/Algiers 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Asmara=usr/share/lib/zoneinfo/Africa/Nairobi
+h usr/share/lib/zoneinfo/Africa/Asmera=usr/share/lib/zoneinfo/Africa/Nairobi
+h usr/share/lib/zoneinfo/Africa/Bamako=usr/share/lib/zoneinfo/Africa/Abidjan
+h usr/share/lib/zoneinfo/Africa/Bangui=usr/share/lib/zoneinfo/Africa/Lagos
+h usr/share/lib/zoneinfo/Africa/Banjul=usr/share/lib/zoneinfo/Africa/Abidjan
+f usr/share/lib/zoneinfo/Africa/Bissau 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Blantyre=usr/share/lib/zoneinfo/Africa/Maputo
+h usr/share/lib/zoneinfo/Africa/Brazzaville=usr/share/lib/zoneinfo/Africa/Lagos
+h usr/share/lib/zoneinfo/Africa/Bujumbura=usr/share/lib/zoneinfo/Africa/Maputo
+f usr/share/lib/zoneinfo/Africa/Cairo 0644 root bin
+f usr/share/lib/zoneinfo/Africa/Casablanca 0644 root bin
+f usr/share/lib/zoneinfo/Africa/Ceuta 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Conakry=usr/share/lib/zoneinfo/Africa/Abidjan
+h usr/share/lib/zoneinfo/Africa/Dakar=usr/share/lib/zoneinfo/Africa/Abidjan
+h usr/share/lib/zoneinfo/Africa/Dar_es_Salaam=usr/share/lib/zoneinfo/Africa/Nairobi
+h usr/share/lib/zoneinfo/Africa/Djibouti=usr/share/lib/zoneinfo/Africa/Nairobi
+h usr/share/lib/zoneinfo/Africa/Douala=usr/share/lib/zoneinfo/Africa/Lagos
+f usr/share/lib/zoneinfo/Africa/El_Aaiun 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Freetown=usr/share/lib/zoneinfo/Africa/Abidjan
+h usr/share/lib/zoneinfo/Africa/Gaborone=usr/share/lib/zoneinfo/Africa/Maputo
+h usr/share/lib/zoneinfo/Africa/Harare=usr/share/lib/zoneinfo/Africa/Maputo
+f usr/share/lib/zoneinfo/Africa/Johannesburg 0644 root bin
+f usr/share/lib/zoneinfo/Africa/Juba 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Kampala=usr/share/lib/zoneinfo/Africa/Nairobi
+f usr/share/lib/zoneinfo/Africa/Khartoum 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Kigali=usr/share/lib/zoneinfo/Africa/Maputo
+h usr/share/lib/zoneinfo/Africa/Kinshasa=usr/share/lib/zoneinfo/Africa/Lagos
+f usr/share/lib/zoneinfo/Africa/Lagos 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Libreville=usr/share/lib/zoneinfo/Africa/Lagos
+h usr/share/lib/zoneinfo/Africa/Lome=usr/share/lib/zoneinfo/Africa/Abidjan
+h usr/share/lib/zoneinfo/Africa/Luanda=usr/share/lib/zoneinfo/Africa/Lagos
+h usr/share/lib/zoneinfo/Africa/Lubumbashi=usr/share/lib/zoneinfo/Africa/Maputo
+h usr/share/lib/zoneinfo/Africa/Lusaka=usr/share/lib/zoneinfo/Africa/Maputo
+h usr/share/lib/zoneinfo/Africa/Malabo=usr/share/lib/zoneinfo/Africa/Lagos
+f usr/share/lib/zoneinfo/Africa/Maputo 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Maseru=usr/share/lib/zoneinfo/Africa/Johannesburg
+h usr/share/lib/zoneinfo/Africa/Mbabane=usr/share/lib/zoneinfo/Africa/Johannesburg
+h usr/share/lib/zoneinfo/Africa/Mogadishu=usr/share/lib/zoneinfo/Africa/Nairobi
+f usr/share/lib/zoneinfo/Africa/Monrovia 0644 root bin
+f usr/share/lib/zoneinfo/Africa/Nairobi 0644 root bin
+f usr/share/lib/zoneinfo/Africa/Ndjamena 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Niamey=usr/share/lib/zoneinfo/Africa/Lagos
+h usr/share/lib/zoneinfo/Africa/Nouakchott=usr/share/lib/zoneinfo/Africa/Abidjan
+h usr/share/lib/zoneinfo/Africa/Ouagadougou=usr/share/lib/zoneinfo/Africa/Abidjan
+h usr/share/lib/zoneinfo/Africa/Porto-Novo=usr/share/lib/zoneinfo/Africa/Lagos
+f usr/share/lib/zoneinfo/Africa/Sao_Tome 0644 root bin
+h usr/share/lib/zoneinfo/Africa/Timbuktu=usr/share/lib/zoneinfo/Africa/Abidjan
+f usr/share/lib/zoneinfo/Africa/Tripoli 0644 root bin
+f usr/share/lib/zoneinfo/Africa/Tunis 0644 root bin
+f usr/share/lib/zoneinfo/Africa/Windhoek 0644 root bin
+d usr/share/lib/zoneinfo/America 0755 root bin
+f usr/share/lib/zoneinfo/America/Adak 0644 root bin
+f usr/share/lib/zoneinfo/America/Anchorage 0644 root bin
+h usr/share/lib/zoneinfo/America/Anguilla=usr/share/lib/zoneinfo/America/Puerto_Rico
+h usr/share/lib/zoneinfo/America/Antigua=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/Araguaina 0644 root bin
+d usr/share/lib/zoneinfo/America/Argentina 0755 root bin
+f usr/share/lib/zoneinfo/America/Argentina/Buenos_Aires 0644 root bin
+f usr/share/lib/zoneinfo/America/Argentina/Catamarca 0644 root bin
+h usr/share/lib/zoneinfo/America/Argentina/ComodRivadavia=usr/share/lib/zoneinfo/America/Argentina/Catamarca
+f usr/share/lib/zoneinfo/America/Argentina/Cordoba 0644 root bin
+f usr/share/lib/zoneinfo/America/Argentina/Jujuy 0644 root bin
+f usr/share/lib/zoneinfo/America/Argentina/La_Rioja 0644 root bin
+f usr/share/lib/zoneinfo/America/Argentina/Mendoza 0644 root bin
+f usr/share/lib/zoneinfo/America/Argentina/Rio_Gallegos 0644 root bin
+f usr/share/lib/zoneinfo/America/Argentina/Salta 0644 root bin
+f usr/share/lib/zoneinfo/America/Argentina/San_Juan 0644 root bin
+f usr/share/lib/zoneinfo/America/Argentina/San_Luis 0644 root bin
+f usr/share/lib/zoneinfo/America/Argentina/Tucuman 0644 root bin
+f usr/share/lib/zoneinfo/America/Argentina/Ushuaia 0644 root bin
+h usr/share/lib/zoneinfo/America/Aruba=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/Asuncion 0644 root bin
+h usr/share/lib/zoneinfo/America/Atikokan=usr/share/lib/zoneinfo/America/Panama
+h usr/share/lib/zoneinfo/America/Atka=usr/share/lib/zoneinfo/America/Adak
+f usr/share/lib/zoneinfo/America/Bahia 0644 root bin
+f usr/share/lib/zoneinfo/America/Bahia_Banderas 0644 root bin
+f usr/share/lib/zoneinfo/America/Barbados 0644 root bin
+f usr/share/lib/zoneinfo/America/Belem 0644 root bin
+f usr/share/lib/zoneinfo/America/Belize 0644 root bin
+h usr/share/lib/zoneinfo/America/Blanc-Sablon=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/Boa_Vista 0644 root bin
+f usr/share/lib/zoneinfo/America/Bogota 0644 root bin
+f usr/share/lib/zoneinfo/America/Boise 0644 root bin
+h usr/share/lib/zoneinfo/America/Buenos_Aires=usr/share/lib/zoneinfo/America/Argentina/Buenos_Aires
+f usr/share/lib/zoneinfo/America/Cambridge_Bay 0644 root bin
+f usr/share/lib/zoneinfo/America/Campo_Grande 0644 root bin
+f usr/share/lib/zoneinfo/America/Cancun 0644 root bin
+f usr/share/lib/zoneinfo/America/Caracas 0644 root bin
+h usr/share/lib/zoneinfo/America/Catamarca=usr/share/lib/zoneinfo/America/Argentina/Catamarca
+f usr/share/lib/zoneinfo/America/Cayenne 0644 root bin
+h usr/share/lib/zoneinfo/America/Cayman=usr/share/lib/zoneinfo/America/Panama
+f usr/share/lib/zoneinfo/America/Chicago 0644 root bin
+f usr/share/lib/zoneinfo/America/Chihuahua 0644 root bin
+h usr/share/lib/zoneinfo/America/Coral_Harbour=usr/share/lib/zoneinfo/America/Panama
+h usr/share/lib/zoneinfo/America/Cordoba=usr/share/lib/zoneinfo/America/Argentina/Cordoba
+f usr/share/lib/zoneinfo/America/Costa_Rica 0644 root bin
+h usr/share/lib/zoneinfo/America/Creston=usr/share/lib/zoneinfo/America/Phoenix
+f usr/share/lib/zoneinfo/America/Cuiaba 0644 root bin
+h usr/share/lib/zoneinfo/America/Curacao=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/Danmarkshavn 0644 root bin
+f usr/share/lib/zoneinfo/America/Dawson 0644 root bin
+f usr/share/lib/zoneinfo/America/Dawson_Creek 0644 root bin
+h usr/share/lib/zoneinfo/America/Denver=usr/share/lib/zoneinfo/US/Mountain
+f usr/share/lib/zoneinfo/America/Detroit 0644 root bin
+h usr/share/lib/zoneinfo/America/Dominica=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/Edmonton 0644 root bin
+f usr/share/lib/zoneinfo/America/Eirunepe 0644 root bin
+f usr/share/lib/zoneinfo/America/El_Salvador 0644 root bin
+h usr/share/lib/zoneinfo/America/Ensenada=usr/share/lib/zoneinfo/America/Tijuana
+f usr/share/lib/zoneinfo/America/Fort_Nelson 0644 root bin
+h usr/share/lib/zoneinfo/America/Fort_Wayne=usr/share/lib/zoneinfo/America/Indiana/Indianapolis
+f usr/share/lib/zoneinfo/America/Fortaleza 0644 root bin
+f usr/share/lib/zoneinfo/America/Glace_Bay 0644 root bin
+h usr/share/lib/zoneinfo/America/Godthab=usr/share/lib/zoneinfo/America/Nuuk
+f usr/share/lib/zoneinfo/America/Goose_Bay 0644 root bin
+f usr/share/lib/zoneinfo/America/Grand_Turk 0644 root bin
+h usr/share/lib/zoneinfo/America/Grenada=usr/share/lib/zoneinfo/America/Puerto_Rico
+h usr/share/lib/zoneinfo/America/Guadeloupe=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/Guatemala 0644 root bin
+f usr/share/lib/zoneinfo/America/Guayaquil 0644 root bin
+f usr/share/lib/zoneinfo/America/Guyana 0644 root bin
+f usr/share/lib/zoneinfo/America/Halifax 0644 root bin
+f usr/share/lib/zoneinfo/America/Havana 0644 root bin
+f usr/share/lib/zoneinfo/America/Hermosillo 0644 root bin
+d usr/share/lib/zoneinfo/America/Indiana 0755 root bin
+f usr/share/lib/zoneinfo/America/Indiana/Indianapolis 0644 root bin
+f usr/share/lib/zoneinfo/America/Indiana/Knox 0644 root bin
+f usr/share/lib/zoneinfo/America/Indiana/Marengo 0644 root bin
+f usr/share/lib/zoneinfo/America/Indiana/Petersburg 0644 root bin
+f usr/share/lib/zoneinfo/America/Indiana/Tell_City 0644 root bin
+f usr/share/lib/zoneinfo/America/Indiana/Vevay 0644 root bin
+f usr/share/lib/zoneinfo/America/Indiana/Vincennes 0644 root bin
+f usr/share/lib/zoneinfo/America/Indiana/Winamac 0644 root bin
+h usr/share/lib/zoneinfo/America/Indianapolis=usr/share/lib/zoneinfo/America/Indiana/Indianapolis
+f usr/share/lib/zoneinfo/America/Inuvik 0644 root bin
+f usr/share/lib/zoneinfo/America/Iqaluit 0644 root bin
+f usr/share/lib/zoneinfo/America/Jamaica 0644 root bin
+h usr/share/lib/zoneinfo/America/Jujuy=usr/share/lib/zoneinfo/America/Argentina/Jujuy
+f usr/share/lib/zoneinfo/America/Juneau 0644 root bin
+d usr/share/lib/zoneinfo/America/Kentucky 0755 root bin
+f usr/share/lib/zoneinfo/America/Kentucky/Louisville 0644 root bin
+f usr/share/lib/zoneinfo/America/Kentucky/Monticello 0644 root bin
+h usr/share/lib/zoneinfo/America/Knox_IN=usr/share/lib/zoneinfo/America/Indiana/Knox
+h usr/share/lib/zoneinfo/America/Kralendijk=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/La_Paz 0644 root bin
+f usr/share/lib/zoneinfo/America/Lima 0644 root bin
+h usr/share/lib/zoneinfo/America/Los_Angeles=usr/share/lib/zoneinfo/US/Pacific
+h usr/share/lib/zoneinfo/America/Louisville=usr/share/lib/zoneinfo/America/Kentucky/Louisville
+h usr/share/lib/zoneinfo/America/Lower_Princes=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/Maceio 0644 root bin
+f usr/share/lib/zoneinfo/America/Managua 0644 root bin
+f usr/share/lib/zoneinfo/America/Manaus 0644 root bin
+h usr/share/lib/zoneinfo/America/Marigot=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/Martinique 0644 root bin
+f usr/share/lib/zoneinfo/America/Matamoros 0644 root bin
+f usr/share/lib/zoneinfo/America/Mazatlan 0644 root bin
+h usr/share/lib/zoneinfo/America/Mendoza=usr/share/lib/zoneinfo/America/Argentina/Mendoza
+f usr/share/lib/zoneinfo/America/Menominee 0644 root bin
+f usr/share/lib/zoneinfo/America/Merida 0644 root bin
+f usr/share/lib/zoneinfo/America/Metlakatla 0644 root bin
+f usr/share/lib/zoneinfo/America/Mexico_City 0644 root bin
+f usr/share/lib/zoneinfo/America/Miquelon 0644 root bin
+f usr/share/lib/zoneinfo/America/Moncton 0644 root bin
+f usr/share/lib/zoneinfo/America/Monterrey 0644 root bin
+f usr/share/lib/zoneinfo/America/Montevideo 0644 root bin
+h usr/share/lib/zoneinfo/America/Montreal=usr/share/lib/zoneinfo/America/Toronto
+h usr/share/lib/zoneinfo/America/Montserrat=usr/share/lib/zoneinfo/America/Puerto_Rico
+h usr/share/lib/zoneinfo/America/Nassau=usr/share/lib/zoneinfo/America/Toronto
+f usr/share/lib/zoneinfo/America/New_York 0644 root bin
+h usr/share/lib/zoneinfo/America/Nipigon=usr/share/lib/zoneinfo/America/Toronto
+f usr/share/lib/zoneinfo/America/Nome 0644 root bin
+f usr/share/lib/zoneinfo/America/Noronha 0644 root bin
+d usr/share/lib/zoneinfo/America/North_Dakota 0755 root bin
+f usr/share/lib/zoneinfo/America/North_Dakota/Beulah 0644 root bin
+f usr/share/lib/zoneinfo/America/North_Dakota/Center 0644 root bin
+f usr/share/lib/zoneinfo/America/North_Dakota/New_Salem 0644 root bin
+f usr/share/lib/zoneinfo/America/Nuuk 0644 root bin
+f usr/share/lib/zoneinfo/America/Ojinaga 0644 root bin
+f usr/share/lib/zoneinfo/America/Panama 0644 root bin
+f usr/share/lib/zoneinfo/America/Pangnirtung 0644 root bin
+f usr/share/lib/zoneinfo/America/Paramaribo 0644 root bin
+f usr/share/lib/zoneinfo/America/Phoenix 0644 root bin
+f usr/share/lib/zoneinfo/America/Port-au-Prince 0644 root bin
+h usr/share/lib/zoneinfo/America/Port_of_Spain=usr/share/lib/zoneinfo/America/Puerto_Rico
+h usr/share/lib/zoneinfo/America/Porto_Acre=usr/share/lib/zoneinfo/America/Rio_Branco
+f usr/share/lib/zoneinfo/America/Porto_Velho 0644 root bin
+f usr/share/lib/zoneinfo/America/Puerto_Rico 0644 root bin
+f usr/share/lib/zoneinfo/America/Punta_Arenas 0644 root bin
+h usr/share/lib/zoneinfo/America/Rainy_River=usr/share/lib/zoneinfo/America/Winnipeg
+f usr/share/lib/zoneinfo/America/Rankin_Inlet 0644 root bin
+f usr/share/lib/zoneinfo/America/Recife 0644 root bin
+f usr/share/lib/zoneinfo/America/Regina 0644 root bin
+f usr/share/lib/zoneinfo/America/Resolute 0644 root bin
+f usr/share/lib/zoneinfo/America/Rio_Branco 0644 root bin
+h usr/share/lib/zoneinfo/America/Rosario=usr/share/lib/zoneinfo/America/Argentina/Cordoba
+h usr/share/lib/zoneinfo/America/Santa_Isabel=usr/share/lib/zoneinfo/America/Tijuana
+f usr/share/lib/zoneinfo/America/Santarem 0644 root bin
+f usr/share/lib/zoneinfo/America/Santiago 0644 root bin
+f usr/share/lib/zoneinfo/America/Santo_Domingo 0644 root bin
+f usr/share/lib/zoneinfo/America/Sao_Paulo 0644 root bin
+f usr/share/lib/zoneinfo/America/Scoresbysund 0644 root bin
+h usr/share/lib/zoneinfo/America/Shiprock=usr/share/lib/zoneinfo/US/Mountain
+f usr/share/lib/zoneinfo/America/Sitka 0644 root bin
+h usr/share/lib/zoneinfo/America/St_Barthelemy=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/St_Johns 0644 root bin
+h usr/share/lib/zoneinfo/America/St_Kitts=usr/share/lib/zoneinfo/America/Puerto_Rico
+h usr/share/lib/zoneinfo/America/St_Lucia=usr/share/lib/zoneinfo/America/Puerto_Rico
+h usr/share/lib/zoneinfo/America/St_Thomas=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/St_Vincent 0644 root bin
+f usr/share/lib/zoneinfo/America/Swift_Current 0644 root bin
+f usr/share/lib/zoneinfo/America/Tegucigalpa 0644 root bin
+f usr/share/lib/zoneinfo/America/Thule 0644 root bin
+h usr/share/lib/zoneinfo/America/Thunder_Bay=usr/share/lib/zoneinfo/America/Toronto
+f usr/share/lib/zoneinfo/America/Tijuana 0644 root bin
+f usr/share/lib/zoneinfo/America/Toronto 0644 root bin
+h usr/share/lib/zoneinfo/America/Tortola=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/Vancouver 0644 root bin
+h usr/share/lib/zoneinfo/America/Virgin=usr/share/lib/zoneinfo/America/Puerto_Rico
+f usr/share/lib/zoneinfo/America/Whitehorse 0644 root bin
+f usr/share/lib/zoneinfo/America/Winnipeg 0644 root bin
+f usr/share/lib/zoneinfo/America/Yakutat 0644 root bin
+f usr/share/lib/zoneinfo/America/Yellowknife 0644 root bin
+d usr/share/lib/zoneinfo/Antarctica 0755 root bin
+f usr/share/lib/zoneinfo/Antarctica/Casey 0644 root bin
+f usr/share/lib/zoneinfo/Antarctica/Davis 0644 root bin
+h usr/share/lib/zoneinfo/Antarctica/DumontDUrville=usr/share/lib/zoneinfo/Pacific/Port_Moresby
+f usr/share/lib/zoneinfo/Antarctica/Macquarie 0644 root bin
+f usr/share/lib/zoneinfo/Antarctica/Mawson 0644 root bin
+h usr/share/lib/zoneinfo/Antarctica/McMurdo=usr/share/lib/zoneinfo/Pacific/Auckland
+f usr/share/lib/zoneinfo/Antarctica/Palmer 0644 root bin
+f usr/share/lib/zoneinfo/Antarctica/Rothera 0644 root bin
+f usr/share/lib/zoneinfo/Antarctica/South_Pole 0644 root bin
+h usr/share/lib/zoneinfo/Antarctica/Syowa=usr/share/lib/zoneinfo/Asia/Riyadh
+f usr/share/lib/zoneinfo/Antarctica/Troll 0644 root bin
+h usr/share/lib/zoneinfo/Antarctica/Vostok=usr/share/lib/zoneinfo/Asia/Urumqi
+d usr/share/lib/zoneinfo/Arctic 0755 root bin
+h usr/share/lib/zoneinfo/Arctic/Longyearbyen=usr/share/lib/zoneinfo/Europe/Berlin
+d usr/share/lib/zoneinfo/Asia 0755 root bin
+h usr/share/lib/zoneinfo/Asia/Aden=usr/share/lib/zoneinfo/Asia/Riyadh
+f usr/share/lib/zoneinfo/Asia/Almaty 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Amman 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Anadyr 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Aqtau 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Aqtobe 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Ashgabat 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Ashkhabad 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Atyrau 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Baghdad 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Bahrain=usr/share/lib/zoneinfo/Asia/Qatar
+f usr/share/lib/zoneinfo/Asia/Baku 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Bangkok 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Barnaul 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Beirut 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Bishkek 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Brunei=usr/share/lib/zoneinfo/Asia/Kuching
+h usr/share/lib/zoneinfo/Asia/Calcutta=usr/share/lib/zoneinfo/Asia/Kolkata
+f usr/share/lib/zoneinfo/Asia/Chita 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Choibalsan 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Chongqing=usr/share/lib/zoneinfo/Asia/Shanghai
+h usr/share/lib/zoneinfo/Asia/Chungking=usr/share/lib/zoneinfo/Asia/Shanghai
+f usr/share/lib/zoneinfo/Asia/Colombo 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Dacca 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Damascus 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Dhaka 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Dili 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Dubai 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Dushanbe 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Famagusta 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Gaza 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Harbin=usr/share/lib/zoneinfo/Asia/Shanghai
+f usr/share/lib/zoneinfo/Asia/Hebron 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Ho_Chi_Minh 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Hong_Kong 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Hovd 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Irkutsk 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Istanbul=usr/share/lib/zoneinfo/Turkey
+f usr/share/lib/zoneinfo/Asia/Jakarta 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Jayapura 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Jerusalem=usr/share/lib/zoneinfo/Israel
+f usr/share/lib/zoneinfo/Asia/Kabul 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Kamchatka 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Karachi 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Kashgar=usr/share/lib/zoneinfo/Asia/Urumqi
+f usr/share/lib/zoneinfo/Asia/Kathmandu 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Katmandu=usr/share/lib/zoneinfo/Asia/Kathmandu
+f usr/share/lib/zoneinfo/Asia/Khandyga 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Kolkata 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Krasnoyarsk 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Kuala_Lumpur=usr/share/lib/zoneinfo/Asia/Singapore
+f usr/share/lib/zoneinfo/Asia/Kuching 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Kuwait=usr/share/lib/zoneinfo/Asia/Riyadh
+h usr/share/lib/zoneinfo/Asia/Macao=usr/share/lib/zoneinfo/Asia/Macau
+f usr/share/lib/zoneinfo/Asia/Macau 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Magadan 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Makassar 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Manila 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Muscat=usr/share/lib/zoneinfo/Asia/Dubai
+f usr/share/lib/zoneinfo/Asia/Nicosia 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Novokuznetsk 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Novosibirsk 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Omsk 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Oral 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Phnom_Penh=usr/share/lib/zoneinfo/Asia/Bangkok
+f usr/share/lib/zoneinfo/Asia/Pontianak 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Pyongyang 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Qatar 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Qostanay 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Qyzylorda 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Rangoon=usr/share/lib/zoneinfo/Asia/Yangon
+f usr/share/lib/zoneinfo/Asia/Riyadh 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Saigon=usr/share/lib/zoneinfo/Asia/Ho_Chi_Minh
+f usr/share/lib/zoneinfo/Asia/Sakhalin 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Samarkand 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Seoul 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Shanghai 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Singapore 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Srednekolymsk 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Taipei 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Tashkent 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Tbilisi 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Tehran 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Tel_Aviv=usr/share/lib/zoneinfo/Israel
+h usr/share/lib/zoneinfo/Asia/Thimbu=usr/share/lib/zoneinfo/Asia/Thimphu
+f usr/share/lib/zoneinfo/Asia/Thimphu 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Tokyo 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Tomsk 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Ujung_Pandang=usr/share/lib/zoneinfo/Asia/Makassar
+f usr/share/lib/zoneinfo/Asia/Ulaanbaatar 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Ulan_Bator=usr/share/lib/zoneinfo/Asia/Ulaanbaatar
+f usr/share/lib/zoneinfo/Asia/Urumqi 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Ust-Nera 0644 root bin
+h usr/share/lib/zoneinfo/Asia/Vientiane=usr/share/lib/zoneinfo/Asia/Bangkok
+f usr/share/lib/zoneinfo/Asia/Vladivostok 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Yakutsk 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Yangon 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Yekaterinburg 0644 root bin
+f usr/share/lib/zoneinfo/Asia/Yerevan 0644 root bin
+d usr/share/lib/zoneinfo/Atlantic 0755 root bin
+f usr/share/lib/zoneinfo/Atlantic/Azores 0644 root bin
+f usr/share/lib/zoneinfo/Atlantic/Bermuda 0644 root bin
+f usr/share/lib/zoneinfo/Atlantic/Canary 0644 root bin
+f usr/share/lib/zoneinfo/Atlantic/Cape_Verde 0644 root bin
+h usr/share/lib/zoneinfo/Atlantic/Faeroe=usr/share/lib/zoneinfo/Atlantic/Faroe
+f usr/share/lib/zoneinfo/Atlantic/Faroe 0644 root bin
+h usr/share/lib/zoneinfo/Atlantic/Jan_Mayen=usr/share/lib/zoneinfo/Europe/Berlin
+f usr/share/lib/zoneinfo/Atlantic/Madeira 0644 root bin
+h usr/share/lib/zoneinfo/Atlantic/Reykjavik=usr/share/lib/zoneinfo/Africa/Abidjan
+f usr/share/lib/zoneinfo/Atlantic/South_Georgia 0644 root bin
+h usr/share/lib/zoneinfo/Atlantic/St_Helena=usr/share/lib/zoneinfo/Africa/Abidjan
+f usr/share/lib/zoneinfo/Atlantic/Stanley 0644 root bin
+d usr/share/lib/zoneinfo/Australia 0755 root bin
+f usr/share/lib/zoneinfo/Australia/ACT 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Adelaide 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Brisbane 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Broken_Hill 0644 root bin
+h usr/share/lib/zoneinfo/Australia/Canberra=usr/share/lib/zoneinfo/Australia/ACT
+f usr/share/lib/zoneinfo/Australia/Currie 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Darwin 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Eucla 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Hobart 0644 root bin
+f usr/share/lib/zoneinfo/Australia/LHI 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Lindeman 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Lord_Howe 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Melbourne 0644 root bin
+h usr/share/lib/zoneinfo/Australia/NSW=usr/share/lib/zoneinfo/Australia/ACT
+f usr/share/lib/zoneinfo/Australia/North 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Perth 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Queensland 0644 root bin
+f usr/share/lib/zoneinfo/Australia/South 0644 root bin
+h usr/share/lib/zoneinfo/Australia/Sydney=usr/share/lib/zoneinfo/Australia/ACT
+f usr/share/lib/zoneinfo/Australia/Tasmania 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Victoria 0644 root bin
+f usr/share/lib/zoneinfo/Australia/West 0644 root bin
+f usr/share/lib/zoneinfo/Australia/Yancowinna 0644 root bin
+d usr/share/lib/zoneinfo/Brazil 0755 root bin
+h usr/share/lib/zoneinfo/Brazil/Acre=usr/share/lib/zoneinfo/America/Rio_Branco
+f usr/share/lib/zoneinfo/Brazil/DeNoronha 0644 root bin
+f usr/share/lib/zoneinfo/Brazil/East 0644 root bin
+f usr/share/lib/zoneinfo/Brazil/West 0644 root bin
+f usr/share/lib/zoneinfo/CET 0644 root bin
+f usr/share/lib/zoneinfo/CST6CDT 0644 root bin
+d usr/share/lib/zoneinfo/Canada 0755 root bin
+f usr/share/lib/zoneinfo/Canada/Atlantic 0644 root bin
+f usr/share/lib/zoneinfo/Canada/Central 0644 root bin
+f usr/share/lib/zoneinfo/Canada/Eastern 0644 root bin
+f usr/share/lib/zoneinfo/Canada/Mountain 0644 root bin
+f usr/share/lib/zoneinfo/Canada/Newfoundland 0644 root bin
+f usr/share/lib/zoneinfo/Canada/Pacific 0644 root bin
+h usr/share/lib/zoneinfo/Canada/Saskatchewan=usr/share/lib/zoneinfo/America/Regina
+f usr/share/lib/zoneinfo/Canada/Yukon 0644 root bin
+d usr/share/lib/zoneinfo/Chile 0755 root bin
+f usr/share/lib/zoneinfo/Chile/Continental 0644 root bin
+f usr/share/lib/zoneinfo/Chile/EasterIsland 0644 root bin
+f usr/share/lib/zoneinfo/Cuba 0644 root bin
+f usr/share/lib/zoneinfo/EET 0644 root bin
+f usr/share/lib/zoneinfo/EST 0644 root bin
+f usr/share/lib/zoneinfo/EST5EDT 0644 root bin
+f usr/share/lib/zoneinfo/Egypt 0644 root bin
+f usr/share/lib/zoneinfo/Eire 0644 root bin
+d usr/share/lib/zoneinfo/Etc 0755 root bin
+h usr/share/lib/zoneinfo/Etc/GMT+0=usr/share/lib/zoneinfo/GMT
+f usr/share/lib/zoneinfo/Etc/GMT+1 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+10 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+11 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+12 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+2 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+3 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+4 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+5 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+6 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+7 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+8 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT+9 0644 root bin
+h usr/share/lib/zoneinfo/Etc/GMT-0=usr/share/lib/zoneinfo/GMT
+f usr/share/lib/zoneinfo/Etc/GMT-1 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-10 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-11 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-12 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-13 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-14 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-2 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-3 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-4 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-5 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-6 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-7 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-8 0644 root bin
+f usr/share/lib/zoneinfo/Etc/GMT-9 0644 root bin
+h usr/share/lib/zoneinfo/Etc/GMT0=usr/share/lib/zoneinfo/GMT
+h usr/share/lib/zoneinfo/Etc/GMT=usr/share/lib/zoneinfo/GMT
+h usr/share/lib/zoneinfo/Etc/Greenwich=usr/share/lib/zoneinfo/GMT
+h usr/share/lib/zoneinfo/Etc/UCT=usr/share/lib/zoneinfo/Etc/UTC
+f usr/share/lib/zoneinfo/Etc/UTC 0644 root bin
+h usr/share/lib/zoneinfo/Etc/Universal=usr/share/lib/zoneinfo/UTC
+h usr/share/lib/zoneinfo/Etc/Zulu=usr/share/lib/zoneinfo/UTC
+d usr/share/lib/zoneinfo/Europe 0755 root bin
+h usr/share/lib/zoneinfo/Europe/Amsterdam=usr/share/lib/zoneinfo/Europe/Brussels
+f usr/share/lib/zoneinfo/Europe/Andorra 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Astrakhan 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Athens 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Belfast=usr/share/lib/zoneinfo/GB
+f usr/share/lib/zoneinfo/Europe/Belgrade 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Berlin 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Bratislava 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Brussels 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Bucharest 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Budapest 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Busingen=usr/share/lib/zoneinfo/Europe/Zurich
+f usr/share/lib/zoneinfo/Europe/Chisinau 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Copenhagen=usr/share/lib/zoneinfo/Europe/Berlin
+f usr/share/lib/zoneinfo/Europe/Dublin 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Gibraltar 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Guernsey=usr/share/lib/zoneinfo/GB
+f usr/share/lib/zoneinfo/Europe/Helsinki 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Isle_of_Man=usr/share/lib/zoneinfo/GB
+h usr/share/lib/zoneinfo/Europe/Istanbul=usr/share/lib/zoneinfo/Turkey
+h usr/share/lib/zoneinfo/Europe/Jersey=usr/share/lib/zoneinfo/GB
+f usr/share/lib/zoneinfo/Europe/Kaliningrad 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Kiev=usr/share/lib/zoneinfo/Europe/Kyiv
+f usr/share/lib/zoneinfo/Europe/Kirov 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Kyiv 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Lisbon 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Ljubljana=usr/share/lib/zoneinfo/Europe/Belgrade
+h usr/share/lib/zoneinfo/Europe/London=usr/share/lib/zoneinfo/GB
+h usr/share/lib/zoneinfo/Europe/Luxembourg=usr/share/lib/zoneinfo/Europe/Brussels
+f usr/share/lib/zoneinfo/Europe/Madrid 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Malta 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Mariehamn 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Minsk 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Monaco=usr/share/lib/zoneinfo/Europe/Paris
+f usr/share/lib/zoneinfo/Europe/Moscow 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Nicosia 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Oslo=usr/share/lib/zoneinfo/Europe/Berlin
+f usr/share/lib/zoneinfo/Europe/Paris 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Podgorica=usr/share/lib/zoneinfo/Europe/Belgrade
+f usr/share/lib/zoneinfo/Europe/Prague 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Riga 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Rome 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Samara 0644 root bin
+h usr/share/lib/zoneinfo/Europe/San_Marino=usr/share/lib/zoneinfo/Europe/Rome
+h usr/share/lib/zoneinfo/Europe/Sarajevo=usr/share/lib/zoneinfo/Europe/Belgrade
+f usr/share/lib/zoneinfo/Europe/Saratov 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Simferopol 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Skopje=usr/share/lib/zoneinfo/Europe/Belgrade
+f usr/share/lib/zoneinfo/Europe/Sofia 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Stockholm=usr/share/lib/zoneinfo/Europe/Berlin
+f usr/share/lib/zoneinfo/Europe/Tallinn 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Tirane 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Tiraspol=usr/share/lib/zoneinfo/Europe/Chisinau
+f usr/share/lib/zoneinfo/Europe/Ulyanovsk 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Uzhgorod=usr/share/lib/zoneinfo/Europe/Kyiv
+h usr/share/lib/zoneinfo/Europe/Vaduz=usr/share/lib/zoneinfo/Europe/Zurich
+h usr/share/lib/zoneinfo/Europe/Vatican=usr/share/lib/zoneinfo/Europe/Rome
+f usr/share/lib/zoneinfo/Europe/Vienna 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Vilnius 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Volgograd 0644 root bin
+f usr/share/lib/zoneinfo/Europe/Warsaw 0644 root bin
+h usr/share/lib/zoneinfo/Europe/Zagreb=usr/share/lib/zoneinfo/Europe/Belgrade
+h usr/share/lib/zoneinfo/Europe/Zaporozhye=usr/share/lib/zoneinfo/Europe/Kyiv
+f usr/share/lib/zoneinfo/Europe/Zurich 0644 root bin
+f usr/share/lib/zoneinfo/Factory 0644 root bin
+f usr/share/lib/zoneinfo/GB 0644 root bin
+h usr/share/lib/zoneinfo/GB-Eire=usr/share/lib/zoneinfo/GB
+f usr/share/lib/zoneinfo/GMT 0644 root bin
+h usr/share/lib/zoneinfo/GMT+0=usr/share/lib/zoneinfo/GMT
+h usr/share/lib/zoneinfo/GMT-0=usr/share/lib/zoneinfo/GMT
+h usr/share/lib/zoneinfo/GMT0=usr/share/lib/zoneinfo/GMT
+h usr/share/lib/zoneinfo/Greenwich=usr/share/lib/zoneinfo/GMT
+f usr/share/lib/zoneinfo/HST 0644 root bin
+f usr/share/lib/zoneinfo/Hongkong 0644 root bin
+h usr/share/lib/zoneinfo/Iceland=usr/share/lib/zoneinfo/Africa/Abidjan
+d usr/share/lib/zoneinfo/Indian 0755 root bin
+h usr/share/lib/zoneinfo/Indian/Antananarivo=usr/share/lib/zoneinfo/Africa/Nairobi
+f usr/share/lib/zoneinfo/Indian/Chagos 0644 root bin
+h usr/share/lib/zoneinfo/Indian/Christmas=usr/share/lib/zoneinfo/Asia/Bangkok
+h usr/share/lib/zoneinfo/Indian/Cocos=usr/share/lib/zoneinfo/Asia/Yangon
+h usr/share/lib/zoneinfo/Indian/Comoro=usr/share/lib/zoneinfo/Africa/Nairobi
+h usr/share/lib/zoneinfo/Indian/Kerguelen=usr/share/lib/zoneinfo/Indian/Maldives
+h usr/share/lib/zoneinfo/Indian/Mahe=usr/share/lib/zoneinfo/Asia/Dubai
+f usr/share/lib/zoneinfo/Indian/Maldives 0644 root bin
+f usr/share/lib/zoneinfo/Indian/Mauritius 0644 root bin
+h usr/share/lib/zoneinfo/Indian/Mayotte=usr/share/lib/zoneinfo/Africa/Nairobi
+h usr/share/lib/zoneinfo/Indian/Reunion=usr/share/lib/zoneinfo/Asia/Dubai
+f usr/share/lib/zoneinfo/Iran 0644 root bin
+f usr/share/lib/zoneinfo/Israel 0644 root bin
+f usr/share/lib/zoneinfo/Jamaica 0644 root bin
+f usr/share/lib/zoneinfo/Japan 0644 root bin
+f usr/share/lib/zoneinfo/Kwajalein 0644 root bin
+f usr/share/lib/zoneinfo/Libya 0644 root bin
+f usr/share/lib/zoneinfo/MET 0644 root bin
+f usr/share/lib/zoneinfo/MST 0644 root bin
+f usr/share/lib/zoneinfo/MST7MDT 0644 root bin
+d usr/share/lib/zoneinfo/Mexico 0755 root bin
+h usr/share/lib/zoneinfo/Mexico/BajaNorte=usr/share/lib/zoneinfo/America/Tijuana
+f usr/share/lib/zoneinfo/Mexico/BajaSur 0644 root bin
+f usr/share/lib/zoneinfo/Mexico/General 0644 root bin
+f usr/share/lib/zoneinfo/NZ 0644 root bin
+f usr/share/lib/zoneinfo/NZ-CHAT 0644 root bin
+h usr/share/lib/zoneinfo/Navajo=usr/share/lib/zoneinfo/US/Mountain
+f usr/share/lib/zoneinfo/PRC 0644 root bin
+f usr/share/lib/zoneinfo/PST8PDT 0644 root bin
+d usr/share/lib/zoneinfo/Pacific 0755 root bin
+f usr/share/lib/zoneinfo/Pacific/Apia 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Auckland 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Bougainville 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Chatham 0644 root bin
+h usr/share/lib/zoneinfo/Pacific/Chuuk=usr/share/lib/zoneinfo/Pacific/Port_Moresby
+f usr/share/lib/zoneinfo/Pacific/Easter 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Efate 0644 root bin
+h usr/share/lib/zoneinfo/Pacific/Enderbury=usr/share/lib/zoneinfo/Pacific/Kanton
+f usr/share/lib/zoneinfo/Pacific/Fakaofo 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Fiji 0644 root bin
+h usr/share/lib/zoneinfo/Pacific/Funafuti=usr/share/lib/zoneinfo/Pacific/Tarawa
+f usr/share/lib/zoneinfo/Pacific/Galapagos 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Gambier 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Guadalcanal 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Guam 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Honolulu 0644 root bin
+h usr/share/lib/zoneinfo/Pacific/Johnston=usr/share/lib/zoneinfo/Pacific/Honolulu
+f usr/share/lib/zoneinfo/Pacific/Kanton 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Kiritimati 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Kosrae 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Kwajalein 0644 root bin
+h usr/share/lib/zoneinfo/Pacific/Majuro=usr/share/lib/zoneinfo/Pacific/Tarawa
+f usr/share/lib/zoneinfo/Pacific/Marquesas 0644 root bin
+h usr/share/lib/zoneinfo/Pacific/Midway=usr/share/lib/zoneinfo/US/Samoa
+f usr/share/lib/zoneinfo/Pacific/Nauru 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Niue 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Norfolk 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Noumea 0644 root bin
+h usr/share/lib/zoneinfo/Pacific/Pago_Pago=usr/share/lib/zoneinfo/US/Samoa
+f usr/share/lib/zoneinfo/Pacific/Palau 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Pitcairn 0644 root bin
+h usr/share/lib/zoneinfo/Pacific/Pohnpei=usr/share/lib/zoneinfo/Pacific/Guadalcanal
+h usr/share/lib/zoneinfo/Pacific/Ponape=usr/share/lib/zoneinfo/Pacific/Guadalcanal
+f usr/share/lib/zoneinfo/Pacific/Port_Moresby 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Rarotonga 0644 root bin
+h usr/share/lib/zoneinfo/Pacific/Saipan=usr/share/lib/zoneinfo/Pacific/Guam
+h usr/share/lib/zoneinfo/Pacific/Samoa=usr/share/lib/zoneinfo/US/Samoa
+f usr/share/lib/zoneinfo/Pacific/Tahiti 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Tarawa 0644 root bin
+f usr/share/lib/zoneinfo/Pacific/Tongatapu 0644 root bin
+h usr/share/lib/zoneinfo/Pacific/Truk=usr/share/lib/zoneinfo/Pacific/Port_Moresby
+h usr/share/lib/zoneinfo/Pacific/Wake=usr/share/lib/zoneinfo/Pacific/Tarawa
+h usr/share/lib/zoneinfo/Pacific/Wallis=usr/share/lib/zoneinfo/Pacific/Tarawa
+h usr/share/lib/zoneinfo/Pacific/Yap=usr/share/lib/zoneinfo/Pacific/Port_Moresby
+f usr/share/lib/zoneinfo/Poland 0644 root bin
+f usr/share/lib/zoneinfo/Portugal 0644 root bin
+h usr/share/lib/zoneinfo/ROC=usr/share/lib/zoneinfo/Asia/Taipei
+f usr/share/lib/zoneinfo/ROK 0644 root bin
+f usr/share/lib/zoneinfo/Singapore 0644 root bin
+f usr/share/lib/zoneinfo/Turkey 0644 root bin
+h usr/share/lib/zoneinfo/UCT=usr/share/lib/zoneinfo/Etc/UTC
+d usr/share/lib/zoneinfo/US 0755 root bin
+f usr/share/lib/zoneinfo/US/Alaska 0644 root bin
+f usr/share/lib/zoneinfo/US/Aleutian 0644 root bin
+f usr/share/lib/zoneinfo/US/Arizona 0644 root bin
+f usr/share/lib/zoneinfo/US/Central 0644 root bin
+h usr/share/lib/zoneinfo/US/East-Indiana=usr/share/lib/zoneinfo/America/Indiana/Indianapolis
+f usr/share/lib/zoneinfo/US/Eastern 0644 root bin
+f usr/share/lib/zoneinfo/US/Hawaii 0644 root bin
+h usr/share/lib/zoneinfo/US/Indiana-Starke=usr/share/lib/zoneinfo/America/Indiana/Knox
+f usr/share/lib/zoneinfo/US/Michigan 0644 root bin
+f usr/share/lib/zoneinfo/US/Mountain 0644 root bin
+f usr/share/lib/zoneinfo/US/Pacific 0644 root bin
+f usr/share/lib/zoneinfo/US/Samoa 0644 root bin
+f usr/share/lib/zoneinfo/UTC 0644 root bin
+h usr/share/lib/zoneinfo/Universal=usr/share/lib/zoneinfo/UTC
+f usr/share/lib/zoneinfo/W-SU 0644 root bin
+f usr/share/lib/zoneinfo/WET 0644 root bin
+h usr/share/lib/zoneinfo/Zulu=usr/share/lib/zoneinfo/UTC
+s usr/share/lib/zoneinfo/posixrules=US/Eastern
+d usr/share/lib/zoneinfo/src 0755 root bin
+f usr/share/lib/zoneinfo/src/README 0644 root bin
+f usr/share/lib/zoneinfo/src/africa 0644 root bin
+f usr/share/lib/zoneinfo/src/antarctica 0644 root bin
+f usr/share/lib/zoneinfo/src/asia 0644 root bin
+f usr/share/lib/zoneinfo/src/australasia 0644 root bin
+f usr/share/lib/zoneinfo/src/backward 0644 root bin
+f usr/share/lib/zoneinfo/src/etcetera 0644 root bin
+f usr/share/lib/zoneinfo/src/europe 0644 root bin
+f usr/share/lib/zoneinfo/src/factory 0644 root bin
+f usr/share/lib/zoneinfo/src/northamerica 0644 root bin
+f usr/share/lib/zoneinfo/src/southamerica 0644 root bin
+f usr/share/lib/zoneinfo/src/systemv 0644 root bin
+d usr/share/lib/zoneinfo/tab 0755 root bin
+f usr/share/lib/zoneinfo/tab/continent.tab 0644 root bin
+f usr/share/lib/zoneinfo/tab/country.tab 0644 root bin
+f usr/share/lib/zoneinfo/tab/zone_sun.tab 0644 root bin
+d usr/share/locale 0755 root other
+d usr/share/locale/C 0755 root bin
+d usr/share/locale/C/LC_MESSAGES 0755 root bin
+d usr/share/man 0755 root bin
+d usr/share/man/man1 0755 root bin
+f usr/share/man/man1/Intro.1 0444 root bin
+f usr/share/man/man1/alias.1 0444 root bin
+f usr/share/man/man1/allocate.1 0444 root bin
+f usr/share/man/man1/amt.1 0444 root bin
+f usr/share/man/man1/apropos.1 0444 root bin
+f usr/share/man/man1/ar.1 0444 root bin
+f usr/share/man/man1/arch.1 0444 root bin
+f usr/share/man/man1/asa.1 0444 root bin
+f usr/share/man/man1/at.1 0444 root bin
+f usr/share/man/man1/atq.1 0444 root bin
+f usr/share/man/man1/atrm.1 0444 root bin
+f usr/share/man/man1/auths.1 0444 root bin
+f usr/share/man/man1/awk.1 0444 root bin
+f usr/share/man/man1/banner.1 0444 root bin
+f usr/share/man/man1/basename.1 0444 root bin
+s usr/share/man/man1/batch.1=at.1
+f usr/share/man/man1/bc.1 0444 root bin
+f usr/share/man/man1/bdiff.1 0444 root bin
+s usr/share/man/man1/bg.1=jobs.1
+f usr/share/man/man1/cal.1 0444 root bin
+f usr/share/man/man1/cat.1 0444 root bin
+f usr/share/man/man1/cd.1 0444 root bin
+s usr/share/man/man1/checkeq.1=eqn.1
+f usr/share/man/man1/checknr.1 0444 root bin
+f usr/share/man/man1/chgrp.1 0444 root bin
+f usr/share/man/man1/chkey.1 0444 root bin
+f usr/share/man/man1/chmod.1 0444 root bin
+f usr/share/man/man1/chown.1 0444 root bin
+f usr/share/man/man1/ckdate.1 0444 root bin
+f usr/share/man/man1/ckgid.1 0444 root bin
+f usr/share/man/man1/ckint.1 0444 root bin
+f usr/share/man/man1/ckitem.1 0444 root bin
+f usr/share/man/man1/ckkeywd.1 0444 root bin
+f usr/share/man/man1/ckpath.1 0444 root bin
+f usr/share/man/man1/ckrange.1 0444 root bin
+f usr/share/man/man1/ckstr.1 0444 root bin
+f usr/share/man/man1/cksum.1 0444 root bin
+f usr/share/man/man1/cktime.1 0444 root bin
+f usr/share/man/man1/ckuid.1 0444 root bin
+f usr/share/man/man1/ckyorn.1 0444 root bin
+f usr/share/man/man1/clear.1 0444 root bin
+f usr/share/man/man1/cmp.1 0444 root bin
+f usr/share/man/man1/col.1 0444 root bin
+f usr/share/man/man1/column.1 0444 root bin
+f usr/share/man/man1/comm.1 0444 root bin
+f usr/share/man/man1/command.1 0444 root bin
+f usr/share/man/man1/compress.1 0444 root bin
+f usr/share/man/man1/cp.1 0444 root bin
+f usr/share/man/man1/cpio.1 0444 root bin
+f usr/share/man/man1/cputrack.1 0444 root bin
+f usr/share/man/man1/crle.1 0444 root bin
+f usr/share/man/man1/crontab.1 0444 root bin
+f usr/share/man/man1/crypt.1 0444 root bin
+f usr/share/man/man1/csh.1 0444 root bin
+f usr/share/man/man1/csplit.1 0444 root bin
+f usr/share/man/man1/ctfdiff.1 0444 root bin
+f usr/share/man/man1/ctfdump.1 0444 root bin
+f usr/share/man/man1/ctrun.1 0444 root bin
+f usr/share/man/man1/ctstat.1 0444 root bin
+f usr/share/man/man1/ctwatch.1 0444 root bin
+f usr/share/man/man1/cut.1 0444 root bin
+f usr/share/man/man1/date.1 0444 root bin
+f usr/share/man/man1/dc.1 0444 root bin
+f usr/share/man/man1/deallocate.1 0444 root bin
+s usr/share/man/man1/decrypt.1=encrypt.1
+f usr/share/man/man1/demangle.1 0444 root bin
+f usr/share/man/man1/deroff.1 0444 root bin
+f usr/share/man/man1/dhcpinfo.1 0444 root bin
+f usr/share/man/man1/diff.1 0444 root bin
+f usr/share/man/man1/diff3.1 0444 root bin
+f usr/share/man/man1/diffmk.1 0444 root bin
+f usr/share/man/man1/digest.1 0444 root bin
+f usr/share/man/man1/dircmp.1 0444 root bin
+s usr/share/man/man1/dirname.1=basename.1
+f usr/share/man/man1/dis.1 0444 root bin
+f usr/share/man/man1/dispgid.1 0444 root bin
+f usr/share/man/man1/dispuid.1 0444 root bin
+f usr/share/man/man1/dos2unix.1 0444 root bin
+f usr/share/man/man1/du.1 0444 root bin
+f usr/share/man/man1/dump.1 0444 root bin
+f usr/share/man/man1/dumpcs.1 0444 root bin
+s usr/share/man/man1/dumpkeys.1=loadkeys.1
+f usr/share/man/man1/echo.1 0444 root bin
+f usr/share/man/man1/ed.1 0444 root bin
+s usr/share/man/man1/edit.1=../../../has/man/man1has/edit.1has
+s usr/share/man/man1/egrep.1=grep.1
+f usr/share/man/man1/eject.1 0444 root bin
+f usr/share/man/man1/elfdump.1 0444 root bin
+f usr/share/man/man1/elfedit.1 0444 root bin
+f usr/share/man/man1/elfsign.1 0444 root bin
+f usr/share/man/man1/elfwrap.1 0444 root bin
+f usr/share/man/man1/encrypt.1 0444 root bin
+f usr/share/man/man1/enhance.1 0444 root bin
+f usr/share/man/man1/env.1 0444 root bin
+f usr/share/man/man1/eqn.1 0444 root bin
+s usr/share/man/man1/errange.1=ckrange.1
+s usr/share/man/man1/errdate.1=ckdate.1
+s usr/share/man/man1/errgid.1=ckgid.1
+s usr/share/man/man1/errint.1=ckint.1
+s usr/share/man/man1/erritem.1=ckitem.1
+s usr/share/man/man1/errpath.1=ckpath.1
+s usr/share/man/man1/errstr.1=ckstr.1
+s usr/share/man/man1/errtime.1=cktime.1
+s usr/share/man/man1/erruid.1=ckuid.1
+s usr/share/man/man1/erryorn.1=ckyorn.1
+f usr/share/man/man1/etdump.1 0444 root bin
+f usr/share/man/man1/expand.1 0444 root bin
+f usr/share/man/man1/expr.1 0444 root bin
+f usr/share/man/man1/exstr.1 0444 root bin
+f usr/share/man/man1/factor.1 0444 root bin
+s usr/share/man/man1/false.1=true.1
+s usr/share/man/man1/fc.1=history.1
+f usr/share/man/man1/fdformat.1 0444 root bin
+s usr/share/man/man1/fg.1=jobs.1
+s usr/share/man/man1/fgrep.1=grep.1
+f usr/share/man/man1/file.1 0444 root bin
+f usr/share/man/man1/filesync.1 0444 root bin
+f usr/share/man/man1/find.1 0444 root bin
+f usr/share/man/man1/fmt.1 0444 root bin
+f usr/share/man/man1/fmtmsg.1 0444 root bin
+f usr/share/man/man1/fold.1 0444 root bin
+f usr/share/man/man1/ftp.1 0444 root bin
+f usr/share/man/man1/gcore.1 0444 root bin
+f usr/share/man/man1/gencat.1 0444 root bin
+f usr/share/man/man1/genmsg.1 0444 root bin
+f usr/share/man/man1/getconf.1 0444 root bin
+f usr/share/man/man1/getfacl.1 0444 root bin
+f usr/share/man/man1/getopt.1 0444 root bin
+f usr/share/man/man1/getoptcvt.1 0444 root bin
+f usr/share/man/man1/getopts.1 0444 root bin
+f usr/share/man/man1/gettext.1 0444 root bin
+f usr/share/man/man1/gettxt.1 0444 root bin
+f usr/share/man/man1/grep.1 0444 root bin
+f usr/share/man/man1/groups.1 0444 root bin
+f usr/share/man/man1/hash.1 0444 root bin
+f usr/share/man/man1/head.1 0444 root bin
+s usr/share/man/man1/helpdate.1=ckdate.1
+s usr/share/man/man1/helpgid.1=ckgid.1
+s usr/share/man/man1/helpint.1=ckint.1
+s usr/share/man/man1/helpitem.1=ckitem.1
+s usr/share/man/man1/helppath.1=ckpath.1
+s usr/share/man/man1/helprange.1=ckrange.1
+s usr/share/man/man1/helpstr.1=ckstr.1
+s usr/share/man/man1/helptime.1=cktime.1
+s usr/share/man/man1/helpuid.1=ckuid.1
+s usr/share/man/man1/helpyorn.1=ckyorn.1
+f usr/share/man/man1/history.1 0444 root bin
+f usr/share/man/man1/hostid.1 0444 root bin
+f usr/share/man/man1/hostname.1 0444 root bin
+s usr/share/man/man1/i286.1=machid.1
+s usr/share/man/man1/i386.1=machid.1
+s usr/share/man/man1/i486.1=machid.1
+f usr/share/man/man1/iconv.1 0444 root bin
+s usr/share/man/man1/intro.1=Intro.1
+f usr/share/man/man1/ipcrm.1 0444 root bin
+f usr/share/man/man1/ipcs.1 0444 root bin
+f usr/share/man/man1/isainfo.1 0444 root bin
+f usr/share/man/man1/isalist.1 0444 root bin
+f usr/share/man/man1/jobs.1 0444 root bin
+f usr/share/man/man1/join.1 0444 root bin
+s usr/share/man/man1/jsh.1=ksh93.1
+f usr/share/man/man1/kbd.1 0444 root bin
+f usr/share/man/man1/kdestroy.1 0444 root bin
+f usr/share/man/man1/keylogin.1 0444 root bin
+f usr/share/man/man1/keylogout.1 0444 root bin
+f usr/share/man/man1/kill.1 0444 root bin
+f usr/share/man/man1/kinit.1 0444 root bin
+f usr/share/man/man1/klist.1 0444 root bin
+f usr/share/man/man1/kmdb.1 0444 root bin
+f usr/share/man/man1/kmfcfg.1 0444 root bin
+f usr/share/man/man1/kpasswd.1 0444 root bin
+f usr/share/man/man1/krb5-config.1 0444 root bin
+s usr/share/man/man1/ksh.1=ksh93.1
+f usr/share/man/man1/ksh93.1 0444 root bin
+f usr/share/man/man1/ktutil.1 0444 root bin
+f usr/share/man/man1/kvmstat.1 0444 root bin
+f usr/share/man/man1/last.1 0444 root bin
+f usr/share/man/man1/lastcomm.1 0444 root bin
+f usr/share/man/man1/ld.1 0444 root bin
+f usr/share/man/man1/ld.so.1.1 0444 root bin
+f usr/share/man/man1/ldap.1 0444 root bin
+s usr/share/man/man1/ldapadd.1=ldapmodify.1
+f usr/share/man/man1/ldapdelete.1 0444 root bin
+f usr/share/man/man1/ldaplist.1 0444 root bin
+f usr/share/man/man1/ldapmodify.1 0444 root bin
+f usr/share/man/man1/ldapmodrdn.1 0444 root bin
+f usr/share/man/man1/ldapsearch.1 0444 root bin
+f usr/share/man/man1/ldd.1 0444 root bin
+f usr/share/man/man1/lgrpinfo.1 0444 root bin
+f usr/share/man/man1/limit.1 0444 root bin
+f usr/share/man/man1/line.1 0444 root bin
+f usr/share/man/man1/list_devices.1 0444 root bin
+f usr/share/man/man1/listusers.1 0444 root bin
+f usr/share/man/man1/ln.1 0444 root bin
+f usr/share/man/man1/loadkeys.1 0444 root bin
+f usr/share/man/man1/locale.1 0444 root bin
+f usr/share/man/man1/localedef.1 0444 root bin
+f usr/share/man/man1/logger.1 0444 root bin
+f usr/share/man/man1/login.1 0444 root bin
+f usr/share/man/man1/logname.1 0444 root bin
+f usr/share/man/man1/look.1 0444 root bin
+f usr/share/man/man1/lorder.1 0444 root bin
+f usr/share/man/man1/ls.1 0444 root bin
+f usr/share/man/man1/m4.1 0444 root bin
+f usr/share/man/man1/mac.1 0444 root bin
+f usr/share/man/man1/mach.1 0444 root bin
+f usr/share/man/man1/machid.1 0444 root bin
+f usr/share/man/man1/mail.1 0444 root bin
+f usr/share/man/man1/mailcompat.1 0444 root bin
+s usr/share/man/man1/mailq.1=sendmail-mailq.1
+f usr/share/man/man1/mailstats.1 0444 root bin
+f usr/share/man/man1/mailx.1 0444 root bin
+f usr/share/man/man1/makekey.1 0444 root bin
+f usr/share/man/man1/man.1 0444 root bin
+f usr/share/man/man1/mandoc.1 0444 root bin
+f usr/share/man/man1/mconnect.1 0444 root bin
+f usr/share/man/man1/mcs.1 0444 root bin
+f usr/share/man/man1/mdb.1 0444 root bin
+f usr/share/man/man1/mesg.1 0444 root bin
+f usr/share/man/man1/mkdir.1 0444 root bin
+f usr/share/man/man1/mkmsgs.1 0444 root bin
+f usr/share/man/man1/mktemp.1 0444 root bin
+f usr/share/man/man1/moe.1 0444 root bin
+f usr/share/man/man1/more.1 0444 root bin
+f usr/share/man/man1/mpss.so.1.1 0444 root bin
+f usr/share/man/man1/msgfmt.1 0444 root bin
+f usr/share/man/man1/mt.1 0444 root bin
+f usr/share/man/man1/mv.1 0444 root bin
+s usr/share/man/man1/nawk.1=awk.1
+f usr/share/man/man1/nc.1 0444 root bin
+s usr/share/man/man1/neqn.1=eqn.1
+f usr/share/man/man1/newform.1 0444 root bin
+f usr/share/man/man1/newgrp.1 0444 root bin
+f usr/share/man/man1/news.1 0444 root bin
+f usr/share/man/man1/newtask.1 0444 root bin
+f usr/share/man/man1/nice.1 0444 root bin
+f usr/share/man/man1/nl.1 0444 root bin
+f usr/share/man/man1/nm.1 0444 root bin
+f usr/share/man/man1/nohup.1 0444 root bin
+f usr/share/man/man1/nroff.1 0444 root bin
+f usr/share/man/man1/od.1 0444 root bin
+f usr/share/man/man1/optisa.1 0444 root bin
+f usr/share/man/man1/pack.1 0444 root bin
+s usr/share/man/man1/page.1=more.1
+f usr/share/man/man1/pagesize.1 0444 root bin
+f usr/share/man/man1/pargs.1 0444 root bin
+f usr/share/man/man1/passwd.1 0444 root bin
+f usr/share/man/man1/paste.1 0444 root bin
+f usr/share/man/man1/pathchk.1 0444 root bin
+s usr/share/man/man1/pauxv.1=pargs.1
+f usr/share/man/man1/pax.1 0444 root bin
+s usr/share/man/man1/pcat.1=pack.1
+s usr/share/man/man1/pcred.1=proc.1
+s usr/share/man/man1/penv.1=pargs.1
+s usr/share/man/man1/pfcsh.1=pfexec.1
+f usr/share/man/man1/pfexec.1 0444 root bin
+s usr/share/man/man1/pfiles.1=proc.1
+s usr/share/man/man1/pfksh.1=pfexec.1
+s usr/share/man/man1/pflags.1=proc.1
+s usr/share/man/man1/pfsh.1=pfexec.1
+f usr/share/man/man1/pg.1 0444 root bin
+f usr/share/man/man1/pgrep.1 0444 root bin
+f usr/share/man/man1/pkgtrans.1 0444 root bin
+s usr/share/man/man1/pkill.1=pgrep.1
+f usr/share/man/man1/pktool.1 0444 root bin
+s usr/share/man/man1/pldd.1=proc.1
+f usr/share/man/man1/plgrp.1 0444 root bin
+f usr/share/man/man1/plimit.1 0444 root bin
+f usr/share/man/man1/pmadvise.1 0444 root bin
+f usr/share/man/man1/pmap.1 0444 root bin
+f usr/share/man/man1/ppgsz.1 0444 root bin
+f usr/share/man/man1/ppriv.1 0444 root bin
+f usr/share/man/man1/pr.1 0444 root bin
+f usr/share/man/man1/praliases.1 0444 root bin
+f usr/share/man/man1/prctl.1 0444 root bin
+f usr/share/man/man1/preap.1 0444 root bin
+f usr/share/man/man1/print.1 0444 root bin
+f usr/share/man/man1/printf.1 0444 root bin
+f usr/share/man/man1/priocntl.1 0444 root bin
+f usr/share/man/man1/proc.1 0444 root bin
+f usr/share/man/man1/profiles.1 0444 root bin
+f usr/share/man/man1/projects.1 0444 root bin
+s usr/share/man/man1/prun.1=proc.1
+f usr/share/man/man1/ps.1 0444 root bin
+f usr/share/man/man1/psecflags.1 0444 root bin
+s usr/share/man/man1/psig.1=proc.1
+s usr/share/man/man1/pstack.1=proc.1
+s usr/share/man/man1/pstop.1=proc.1
+s usr/share/man/man1/ptime.1=proc.1
+f usr/share/man/man1/ptree.1 0444 root bin
+f usr/share/man/man1/pvs.1 0444 root bin
+s usr/share/man/man1/pwait.1=proc.1
+f usr/share/man/man1/pwd.1 0444 root bin
+s usr/share/man/man1/pwdx.1=proc.1
+f usr/share/man/man1/rcapstat.1 0444 root bin
+f usr/share/man/man1/rcp.1 0444 root bin
+f usr/share/man/man1/rdist.1 0444 root bin
+f usr/share/man/man1/read.1 0444 root bin
+s usr/share/man/man1/red.1=ed.1
+f usr/share/man/man1/refer.1 0444 root bin
+f usr/share/man/man1/regcmp.1 0444 root bin
+s usr/share/man/man1/remote_shell.1=rsh.1
+s usr/share/man/man1/remsh.1=rsh.1
+f usr/share/man/man1/renice.1 0444 root bin
+f usr/share/man/man1/rev.1 0444 root bin
+s usr/share/man/man1/rksh.1=ksh93.1
+s usr/share/man/man1/rksh93.1=ksh93.1
+f usr/share/man/man1/rlogin.1 0444 root bin
+f usr/share/man/man1/rm.1 0444 root bin
+s usr/share/man/man1/rmail.1=mail.1
+s usr/share/man/man1/rmdir.1=rm.1
+f usr/share/man/man1/rmformat.1 044 root bin
+f usr/share/man/man1/roles.1 0444 root bin
+f usr/share/man/man1/rsh.1 0444 root bin
+f usr/share/man/man1/runat.1 0444 root bin
+f usr/share/man/man1/rup.1 0444 root bin
+f usr/share/man/man1/ruptime.1 0444 root bin
+f usr/share/man/man1/rusers.1 0444 root bin
+f usr/share/man/man1/script.1 0444 root bin
+f usr/share/man/man1/sdiff.1 0444 root bin
+f usr/share/man/man1/sed.1 0444 root bin
+f usr/share/man/man1/sendmail-mailq.1 0444 root bit
+f usr/share/man/man1/setfacl.1 0444 root bin
+f usr/share/man/man1/setpgrp.1 0444 root bin
+s usr/share/man/man1/settime.1=touch.1
+s usr/share/man/man1/sh.1=ksh93.1
+f usr/share/man/man1/shcomp.1 0444 root bin
+f usr/share/man/man1/size.1 044 root bin
+f usr/share/man/man1/sleep.1 0444 root bin
+f usr/share/man/man1/smbutil.1 0444 root bin
+f usr/share/man/man1/soelim.1 0444 root bin
+f usr/share/man/man1/sort.1 0444 root bin
+f usr/share/man/man1/sotruss.1 0444 root bin
+s usr/share/man/man1/sparc.1=machid.1
+f usr/share/man/man1/spell.1 0444 root bin
+f usr/share/man/man1/split.1 0444 root bin
+f usr/share/man/man1/srchtxt.1 0444 root bin
+f usr/share/man/man1/strchg.1 0444 root bin
+s usr/share/man/man1/strconf.1=strchg.1
+f usr/share/man/man1/strings.1 0444 root bin
+f usr/share/man/man1/strip.1 044 root bin
+f usr/share/man/man1/stty.1 0444 root bin
+f usr/share/man/man1/sum.1 0444 root bin
+s usr/share/man/man1/sun.1=machid.1
+f usr/share/man/man1/svcprop.1 0444 root bin
+f usr/share/man/man1/svcs.1 0444 root bin
+f usr/share/man/man1/tabs.1 0444 root bin
+f usr/share/man/man1/tail.1 0444 root bin
+f usr/share/man/man1/tar.1 0444 root bin
+f usr/share/man/man1/tbl.1 0444 root bin
+f usr/share/man/man1/tcopy.1 0444 root bin
+f usr/share/man/man1/tee.1 0444 root bin
+f usr/share/man/man1/telnet.1 0444 root bin
+f usr/share/man/man1/test.1 0444 root bin
+f usr/share/man/man1/time.1 0444 root bin
+f usr/share/man/man1/timex.1 044 root bin
+f usr/share/man/man1/tip.1 0444 root bin
+f usr/share/man/man1/touch.1 0444 root bin
+f usr/share/man/man1/tput.1 0444 root bin
+f usr/share/man/man1/tr.1 0444 root bin
+f usr/share/man/man1/troff.1 0444 root bin
+f usr/share/man/man1/true.1 0444 root bin
+f usr/share/man/man1/truss.1 0444 root bin
+f usr/share/man/man1/tsort.1 0444 root bin
+f usr/share/man/man1/tty.1 0444 root bin
+f usr/share/man/man1/type.1 0444 root bin
+f usr/share/man/man1/ul.1 0444 root bin
+s usr/share/man/man1/ulimit.1=limit.1
+f usr/share/man/man1/umask.1 0444 root bin
+s usr/share/man/man1/unalias.1=alias.1
+f usr/share/man/man1/uname.1 0444 root bin
+s usr/share/man/man1/uncompress.1=compress.1
+s usr/share/man/man1/unexpand.1=expand.1
+f usr/share/man/man1/uniq.1 0444 root bin
+f usr/share/man/man1/units.1 0444 root bin
+f usr/share/man/man1/unix2dos.1 0444 root bin
+s usr/share/man/man1/unpack.1=pack.1
+f usr/share/man/man1/uptime.1 0444 root bin
+f usr/share/man/man1/uuidgen.1 0444 root bin
+f usr/share/man/man1/vacation.1 0444 root bin
+s usr/share/man/man1/valdate.1=ckdate.1
+s usr/share/man/man1/valgid.1=ckgid.1
+s usr/share/man/man1/valint.1=ckint.1
+s usr/share/man/man1/valpath.1=ckpath.1
+s usr/share/man/man1/valrange.1=ckrange.1
+s usr/share/man/man1/valstr.1=ckstr.1
+s usr/share/man/man1/valtime.1=cktime.1
+s usr/share/man/man1/valuid.1=ckuid.1
+s usr/share/man/man1/valyorn.1=ckyorn.1
+s usr/share/man/man1/vedit.1=../../../has/man/man1has/vi.1has
+f usr/share/man/man1/vgrind.1 0444 root bin
+f usr/share/man/man1/vtfontcvt.1 0444 root bin
+f usr/share/man/man1/w.1 0444 root bin
+f usr/share/man/man1/wait.1 0444 root bin
+f usr/share/man/man1/wc.1 0444 root bin
+s usr/share/man/man1/whatis.1=apropos.1
+f usr/share/man/man1/which.1 0444 root bin
+f usr/share/man/man1/who.1 0444 root bin
+f usr/share/man/man1/whocalls.1 0444 root bin
+f usr/share/man/man1/whois.1 0444 root bin
+f usr/share/man/man1/write.1 0444 root bin
+f usr/share/man/man1/xargs.1 0444 root bin
+f usr/share/man/man1/xgettext.1 0444 root bin
+f usr/share/man/man1/xstr.1 0444 root bin
+f usr/share/man/man1/yes.1 0444 root bin
+f usr/share/man/man1/ypcat.1 0444 root bin
+f usr/share/man/man1/ypmatch.1 0444 root bin
+f usr/share/man/man1/yppasswd.1 0444 root bin
+f usr/share/man/man1/ypwhich.1 0444 root bin
+s usr/share/man/man1/zcat.1=compress.1
+f usr/share/man/man1/zlogin.1 0444 root bin
+f usr/share/man/man1/zonename.1 0444 root bin
+f usr/share/man/man1/zonestat.1 0444 root bin
+d usr/share/man/man1b 0755 root bin
+f usr/share/man/man1b/df.1b 0444 root bin
+f usr/share/man/man1b/ps.1b 0444 root bin
+f usr/share/man/man1b/shutdown.1b 0444 root bin
+f usr/share/man/man1b/touch.1b 0444 root bin
+d usr/share/man/man1c 0755 root bin
+s usr/share/man/man1c/uudecode.1c=uuencode.1c
+f usr/share/man/man1c/uuencode.1c 044 root bin
+
+
+d usr/share/man/man2 0755 root bin
+f usr/share/man/man2/Intro.2 0444 root bin
+s usr/share/man/man2/_Exit.2=exit.2
+s usr/share/man/man2/_exit.2=exit.2
+s usr/share/man/man2/_lwp_cond_broadcast.2=_lwp_cond_signal.2
+s usr/share/man/man2/_lwp_cond_reltimedwait.2=_lwp_cond_wait.2
+f usr/share/man/man2/_lwp_cond_signal.2 0444 root bin
+s usr/share/man/man2/_lwp_cond_timedwait.2=_lwp_cond_wait.2
+f usr/share/man/man2/_lwp_cond_wait.2 0444 root bin
+s usr/share/man/man2/_lwp_continue.2=_lwp_suspend.2
+f usr/share/man/man2/_lwp_info.2 0444 root bin
+f usr/share/man/man2/_lwp_kill.2 0444 root bin
+f usr/share/man/man2/_lwp_mutex_lock.2 0444 root bin
+s usr/share/man/man2/_lwp_mutex_trylock.2=_lwp_mutex_lock.2
+s usr/share/man/man2/_lwp_mutex_unlock.2=_lwp_mutex_lock.2
+f usr/share/man/man2/_lwp_self.2 0444 root bin
+s usr/share/man/man2/_lwp_sema_init.2=_lwp_sema_wait.2
+s usr/share/man/man2/_lwp_sema_post.2=_lwp_sema_wait.2
+s usr/share/man/man2/_lwp_sema_trywait.2=_lwp_sema_wait.2
+f usr/share/man/man2/_lwp_sema_wait.2 0444 root bin
+f usr/share/man/man2/_lwp_suspend.2 0444 root bin
+f usr/share/man/man2/access.2 0444 root bin
+f usr/share/man/man2/acct.2 0444 root bin
+f usr/share/man/man2/acl.2 0444 root bin
+f usr/share/man/man2/adjtime.2 0444 root bin
+f usr/share/man/man2/alarm.2 0444 root bin
+f usr/share/man/man2/audit.2 0444 root bin
+f usr/share/man/man2/auditon.2 0444 root bin
+f usr/share/man/man2/brk.2 0444 root bin
+f usr/share/man/man2/chdir.2 0444 root bin
+f usr/share/man/man2/chmod.2 0444 root bin
+f usr/share/man/man2/chown.2 0444 root bin
+f usr/share/man/man2/chroot.2 0444 root bin
+f usr/share/man/man2/close.2 0444 root bin
+f usr/share/man/man2/creat.2 0444 root bin
+f usr/share/man/man2/dup.2 0444 root bin
+f usr/share/man/man2/exec.2 0444 root bin
+s usr/share/man/man2/execl.2=exec.2
+s usr/share/man/man2/execle.2=exec.2
+s usr/share/man/man2/execlp.2=exec.2
+s usr/share/man/man2/execv.2=exec.2
+s usr/share/man/man2/execve.2=exec.2
+s usr/share/man/man2/execvp.2=exec.2
+f usr/share/man/man2/exit.2 0444 root bin
+s usr/share/man/man2/faccessat.2=access.2
+s usr/share/man/man2/facl.2=acl.2
+s usr/share/man/man2/fchdir.2=chdir.2
+s usr/share/man/man2/fchmod.2=chmod.2
+s usr/share/man/man2/fchmodat.2=chmod.2
+s usr/share/man/man2/fchown.2=chown.2
+s usr/share/man/man2/fchownat.2=chown.2
+s usr/share/man/man2/fchroot.2=chroot.2
+f usr/share/man/man2/fcntl.2 0444 root bin
+f usr/share/man/man2/fork.2 0444 root bin
+s usr/share/man/man2/fork1.2=fork.2
+s usr/share/man/man2/forkall.2=fork.2
+s usr/share/man/man2/forkallx.2=fork.2
+s usr/share/man/man2/forkx.2=fork.2
+f usr/share/man/man2/fpathconf.2 0444 root bin
+s usr/share/man/man2/fstat.2=stat.2
+s usr/share/man/man2/fstatat.2=stat.2
+s usr/share/man/man2/fstatvfs.2=statvfs.2
+s usr/share/man/man2/futimens.2=utimes.2
+s usr/share/man/man2/futimes.2=utimes.2
+s usr/share/man/man2/futimesat.2=utimes.2
+f usr/share/man/man2/getacct.2 0444 root bin
+f usr/share/man/man2/getaudit.2 0444 root bin
+s usr/share/man/man2/getaudit_addr.2=getaudit.2
+f usr/share/man/man2/getauid.2 0444 root bin
+f usr/share/man/man2/getcontext.2 0444 root bin
+f usr/share/man/man2/getdents.2 0444 root bin
+s usr/share/man/man2/getegid.2=getuid.2
+s usr/share/man/man2/geteuid.2=getuid.2
+s usr/share/man/man2/getgid.2=getuid.2
+f usr/share/man/man2/getgroups.2 0444 root bin
+f usr/share/man/man2/getisax.2 0444 root bin
+f usr/share/man/man2/getitimer.2 0444 root bin
+f usr/share/man/man2/getmsg.2 0444 root bin
+f usr/share/man/man2/getpflags.2 0444 root bin
+s usr/share/man/man2/getpgid.2=getpid.2
+s usr/share/man/man2/getpgrp.2=getpid.2
+f usr/share/man/man2/getpid.2 0444 root bin
+s usr/share/man/man2/getpmsg.2=getmsg.2
+s usr/share/man/man2/getppid.2=getpid.2
+f usr/share/man/man2/getppriv.2 0444 root bin
+s usr/share/man/man2/getprojid.2=settaskid.2
+f usr/share/man/man2/getrandom.2 0444 root bin
+s usr/share/man/man2/getrctl.2=setrctl.2
+f usr/share/man/man2/getrlimit.2 0444 root bin
+f usr/share/man/man2/getsid.2 0444 root bin
+s usr/share/man/man2/gettaskid.2=settaskid.2
+f usr/share/man/man2/getuid.2 0444 root bin
+f usr/share/man/man2/getustack.2 0444 root bin
+s usr/share/man/man2/intro.2=Intro.2
+f usr/share/man/man2/ioctl.2 0444 root bin
+f usr/share/man/man2/issetugid.2 0444 root bin
+f usr/share/man/man2/kill.2 0444 root bin
+s usr/share/man/man2/lchown.2=chown.2
+f usr/share/man/man2/link.2 0444 root bin
+s usr/share/man/man2/linkat.2=link.2
+f usr/share/man/man2/llseek.2 0444 root bin
+f usr/share/man/man2/lseek.2 0444 root bin
+s usr/share/man/man2/lstat.2=stat.2
+s usr/share/man/man2/lutimes.2=utimes.2
+f usr/share/man/man2/memcntl.2 0444 root bin
+f usr/share/man/man2/meminfo.2 0444 root bin
+f usr/share/man/man2/mincore.2 0444 root bin
+f usr/share/man/man2/mkdir.2 0444 root bin
+s usr/share/man/man2/mkdirat.2=mkdir.2
+f usr/share/man/man2/mknod.2 0444 root bin
+s usr/share/man/man2/mknodat.2=mknod.2
+f usr/share/man/man2/mmap.2 0444 root bin
+f usr/share/man/man2/mmapobj.2 0444 root bin
+f usr/share/man/man2/mount.2 0444 root bin
+f usr/share/man/man2/mprotect.2 0444 root bin
+f usr/share/man/man2/msgctl.2 0444 root bin
+f usr/share/man/man2/msgget.2 0444 root bin
+f usr/share/man/man2/msgids.2 0444 root bin
+f usr/share/man/man2/msgrcv.2 0444 root bin
+f usr/share/man/man2/msgsnap.2 0444 root bin
+f usr/share/man/man2/msgsnd.2 0444 root bin
+f usr/share/man/man2/munmap.2 0444 root bin
+f usr/share/man/man2/nice.2 0444 root bin
+f usr/share/man/man2/ntp_adjtime.2 0444 root bin
+f usr/share/man/man2/ntp_gettime.2 0444 root bin
+f usr/share/man/man2/open.2 0444 root bin
+s usr/share/man/man2/openat.2=open.2
+f usr/share/man/man2/p_online.2 0444 root bin
+s usr/share/man/man2/pathconf.2=fpathconf.2
+f usr/share/man/man2/pause.2 0444 root bin
+f usr/share/man/man2/pcsample.2 0444 root bin
+f usr/share/man/man2/pipe.2 0444 root bin
+s usr/share/man/man2/pipe2.2=pipe.2
+f usr/share/man/man2/poll.2 0444 root bin
+s usr/share/man/man2/ppoll.2=poll.2
+s usr/share/man/man2/pread.2=read.2
+s usr/share/man/man2/preadv.2=read.2
+f usr/share/man/man2/priocntl.2 0444 root bin
+f usr/share/man/man2/priocntlset.2 0444 root bin
+f usr/share/man/man2/processor_bind.2 0444 root bin
+f usr/share/man/man2/processor_info.2 0444 root bin
+f usr/share/man/man2/profil.2 0444 root bin
+s usr/share/man/man2/pset_assign.2=pset_create.2
+f usr/share/man/man2/pset_bind.2 0444 root bin
+f usr/share/man/man2/pset_create.2 0444 root bin
+s usr/share/man/man2/pset_destroy.2=pset_create.2
+s usr/share/man/man2/pset_getattr.2=pset_setattr.2
+f usr/share/man/man2/pset_info.2 0444 root bin
+f usr/share/man/man2/pset_list.2 0444 root bin
+f usr/share/man/man2/pset_setattr.2 0444 root bin
+s usr/share/man/man2/putacct.2=getacct.2
+f usr/share/man/man2/putmsg.2 0444 root bin
+s usr/share/man/man2/putpmsg.2=putmsg.2
+s usr/share/man/man2/pwrite.2=write.2
+s usr/share/man/man2/pwritev.2=write.2
+f usr/share/man/man2/read.2 0444 root bin
+f usr/share/man/man2/readlink.2 0444 root bin
+s usr/share/man/man2/readlinkat.2=readlink.2
+s usr/share/man/man2/readv.2=read.2
+f usr/share/man/man2/rename.2 0444 root bin
+s usr/share/man/man2/renameat.2=rename.2
+f usr/share/man/man2/resolvepath.2 0444 root bin
+f usr/share/man/man2/rmdir.2 0444 root bin
+s usr/share/man/man2/sbrk.2=brk.2
+f usr/share/man/man2/semctl.2 0444 root bin
+f usr/share/man/man2/semget.2 0444 root bin
+f usr/share/man/man2/semids.2 0444 root bin
+f usr/share/man/man2/semop.2 0444 root bin
+s usr/share/man/man2/semtimedop.2=semop.2
+s usr/share/man/man2/setaudit.2=getaudit.2
+s usr/share/man/man2/setaudit_addr.2=getaudit.2
+s usr/share/man/man2/setauid.2=getauid.2
+s usr/share/man/man2/setcontext.2=getcontext.2
+s usr/share/man/man2/setegid.2=setuid.2
+s usr/share/man/man2/seteuid.2=setuid.2
+s usr/share/man/man2/setgid.2=setuid.2
+s usr/share/man/man2/setgroups.2=getgroups.2
+s usr/share/man/man2/setitimer.2=getitimer.2
+s usr/share/man/man2/setpflags.2=getpflags.2
+f usr/share/man/man2/setpgid.2 0444 root bin
+f usr/share/man/man2/setpgrp.2 0444 root bin
+s usr/share/man/man2/setppriv.2=getppriv.2
+f usr/share/man/man2/setrctl.2 0444 root bin
+f usr/share/man/man2/setregid.2 0444 root bin
+f usr/share/man/man2/setreuid.2 0444 root bin
+s usr/share/man/man2/setrlimit.2=getrlimit.2
+f usr/share/man/man2/setsid.2 0444 root bin
+f usr/share/man/man2/settaskid.2 0444 root bin
+f usr/share/man/man2/setuid.2 0444 root bin
+s usr/share/man/man2/setustack.2=getustack.2
+s usr/share/man/man2/shmat.2=shmop.2
+f usr/share/man/man2/shmctl.2 0444 root bin
+s usr/share/man/man2/shmdt.2=shmop.2
+f usr/share/man/man2/shmget.2 0444 root bin
+f usr/share/man/man2/shmids.2 0444 root bin
+f usr/share/man/man2/shmop.2 0444 root bin
+f usr/share/man/man2/sigaction.2 0444 root bin
+f usr/share/man/man2/sigaltstack.2 0444 root bin
+f usr/share/man/man2/sigpending.2 0444 root bin
+f usr/share/man/man2/sigprocmask.2 0444 root bin
+f usr/share/man/man2/sigsend.2 0444 root bin
+s usr/share/man/man2/sigsendset.2=sigsend.2
+f usr/share/man/man2/sigsuspend.2 0444 root bin
+f usr/share/man/man2/sigwait.2 0444 root bin
+f usr/share/man/man2/stat.2 0444 root bin
+f usr/share/man/man2/statvfs.2 0444 root bin
+f usr/share/man/man2/stime.2 0444 root bin
+f usr/share/man/man2/swapctl.2 0444 root bin
+f usr/share/man/man2/symlink.2 0444 root bin
+s usr/share/man/man2/symlinkat.2=symlink.2
+f usr/share/man/man2/sync.2 0444 root bin
+f usr/share/man/man2/sysfs.2 0444 root bin
+f usr/share/man/man2/sysinfo.2 0444 root bin
+f usr/share/man/man2/time.2 0444 root bin
+f usr/share/man/man2/times.2 0444 root bin
+f usr/share/man/man2/uadmin.2 0444 root bin
+f usr/share/man/man2/ulimit.2 0444 root bin
+f usr/share/man/man2/umask.2 0444 root bin
+f usr/share/man/man2/umount.2 0444 root bin
+s usr/share/man/man2/umount2.2=umount.2
+f usr/share/man/man2/uname.2 0444 root bin
+f usr/share/man/man2/unlink.2 0444 root bin
+s usr/share/man/man2/unlinkat.2=unlink.2
+f usr/share/man/man2/upanic.2 0444 root bin
+f usr/share/man/man2/ustat.2 0444 root bin
+f usr/share/man/man2/utime.2 0444 root bin
+s usr/share/man/man2/utimensat.2=utimes.2
+f usr/share/man/man2/utimes.2 0444 root bin
+f usr/share/man/man2/uucopy.2 0444 root bin
+f usr/share/man/man2/vfork.2 0444 root bin
+s usr/share/man/man2/vforkx.2=vfork.2
+f usr/share/man/man2/vhangup.2 0444 root bin
+f usr/share/man/man2/waitid.2 0444 root bin
+s usr/share/man/man2/wracct.2=getacct.2
+f usr/share/man/man2/write.2 0444 root bin
+s usr/share/man/man2/writev.2=write.2
+f usr/share/man/man2/yield.2 0444 root bin
+d usr/share/man/man3 0755 root bin
+f usr/share/man/man3/Intro.3 0444 root bin
+f usr/share/man/man3/hosts_access.3 0444 root bin
+s usr/share/man/man3/hosts_ctl.3=hosts_access.3
+s usr/share/man/man3/intro.3=Intro.3
+s usr/share/man/man3/libwrap.3=hosts_access.3
+s usr/share/man/man3/request_init.3=hosts_access.3
+s usr/share/man/man3/request_set.3=hosts_access.3
+d usr/share/man/man3avl 0755 root bin
+s usr/share/man/man3avl/AVL_NEXT.3avl=avl_first.3avl
+s usr/share/man/man3avl/AVL_PREV.3avl=avl_first.3avl
+f usr/share/man/man3avl/avl_add.3avl 0444 root bin
+f usr/share/man/man3avl/avl_create.3avl 0444 root bin
+f usr/share/man/man3avl/avl_destroy.3avl 0444 root bin
+f usr/share/man/man3avl/avl_destroy_nodes.3avl 0444 root bin
+f usr/share/man/man3avl/avl_find.3avl 0444 root bin
+f usr/share/man/man3avl/avl_first.3avl 0444 root bin
+f usr/share/man/man3avl/avl_insert.3avl 0444 root bin
+s usr/share/man/man3avl/avl_insert_here.3avl=avl_insert.3avl
+f usr/share/man/man3avl/avl_is_empty.3avl 0444 root bin
+s usr/share/man/man3avl/avl_last.3avl=avl_first.3avl
+f usr/share/man/man3avl/avl_nearest.3avl 0444 root bin
+f usr/share/man/man3avl/avl_numnodes.3avl 0444 root bin
+s usr/share/man/man3avl/avl_remove.3avl=avl_add.3avl
+f usr/share/man/man3avl/avl_swap.3avl 0444 root bin
+d usr/share/man/man3bsm 0755 root bin
+s usr/share/man/man3bsm/au_close.3bsm=au_open.3bsm
+f usr/share/man/man3bsm/au_open.3bsm 0444 root bin
+f usr/share/man/man3bsm/au_preselect.3bsm 0444 root bin
+f usr/share/man/man3bsm/au_to.3bsm 0444 root bin
+s usr/share/man/man3bsm/au_to_arg.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_arg32.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_arg64.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_attr.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_cmd.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_data.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_groups.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_in_addr.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_ipc.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_iport.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_me.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_newgroups.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_opaque.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_path.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_process.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_process_ex.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_return.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_return32.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_return64.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_socket.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_subject.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_subject_ex.3bsm=au_to.3bsm
+s usr/share/man/man3bsm/au_to_text.3bsm=au_to.3bsm
+f usr/share/man/man3bsm/au_user_mask.3bsm 0444 root bin
+s usr/share/man/man3bsm/au_write.3bsm=au_open.3bsm
+s usr/share/man/man3bsm/endauclass.3bsm=getauclassent.3bsm
+s usr/share/man/man3bsm/endauevent.3bsm=getauevent.3bsm
+f usr/share/man/man3bsm/getauclassent.3bsm 0444 root bin
+s usr/share/man/man3bsm/getauclassent_r.3bsm=getauclassent.3bsm
+s usr/share/man/man3bsm/getauclassnam.3bsm=getauclassent.3bsm
+s usr/share/man/man3bsm/getauclassnam_r.3bsm=getauclassent.3bsm
+f usr/share/man/man3bsm/getauditflags.3bsm 0444 root bin
+s usr/share/man/man3bsm/getauditflagsbin.3bsm=getauditflags.3bsm
+s usr/share/man/man3bsm/getauditflagschar.3bsm=getauditflags.3bsm
+f usr/share/man/man3bsm/getauevent.3bsm 0444 root bin
+s usr/share/man/man3bsm/getauevent_r.3bsm=getauevent.3bsm
+s usr/share/man/man3bsm/getauevnam.3bsm=getauevent.3bsm
+s usr/share/man/man3bsm/getauevnam_r.3bsm=getauevent.3bsm
+s usr/share/man/man3bsm/getauevnonam.3bsm=getauevent.3bsm
+s usr/share/man/man3bsm/getauevnum.3bsm=getauevent.3bsm
+s usr/share/man/man3bsm/getauevnum_r.3bsm=getauevent.3bsm
+f usr/share/man/man3bsm/getfauditflags.3bsm 0444 root bin
+s usr/share/man/man3bsm/setauclass.3bsm=getauclassent.3bsm
+s usr/share/man/man3bsm/setauevent.3bsm=getauevent.3bsm
+d usr/share/man/man3c 0755 root bin
+s usr/share/man/man3c/FD_CLR.3c=select.3c
+s usr/share/man/man3c/FD_ISSET.3c=select.3c
+s usr/share/man/man3c/FD_SET.3c=select.3c
+s usr/share/man/man3c/FD_ZERO.3c=select.3c
+f usr/share/man/man3c/__fbufsize.3c 0444 root bin
+s usr/share/man/man3c/__flbf.3c=__fbufsize.3c
+s usr/share/man/man3c/__fpending.3c=__fbufsize.3c
+s usr/share/man/man3c/__fpurge.3c=__fbufsize.3c
+s usr/share/man/man3c/__freadable.3c=__fbufsize.3c
+s usr/share/man/man3c/__freading.3c=__fbufsize.3c
+s usr/share/man/man3c/__fsetlocking.3c=__fbufsize.3c
+s usr/share/man/man3c/__fwritable.3c=__fbufsize.3c
+s usr/share/man/man3c/__fwriting.3c=__fbufsize.3c
+s usr/share/man/man3c/_edata.3c=end.3c
+s usr/share/man/man3c/_end.3c=end.3c
+s usr/share/man/man3c/_etext.3c=end.3c
+s usr/share/man/man3c/_exithandle.3c=exit.3c
+s usr/share/man/man3c/_flushlbf.3c=__fbufsize.3c
+f usr/share/man/man3c/_longjmp.3c 0444 root bin
+s usr/share/man/man3c/_setjmp.3c=_longjmp.3c
+f usr/share/man/man3c/_stack_grow.3c 0444 root bin
+f usr/share/man/man3c/a64l.3c 0444 root bin
+f usr/share/man/man3c/abort.3c 0444 root bin
+s usr/share/man/man3c/abort_handler_s.3c=set_constraint_handler_s.3c
+f usr/share/man/man3c/abs.3c 0444 root bin
+s usr/share/man/man3c/addrtosymstr.3c=walkcontext.3c
+f usr/share/man/man3c/addsev.3c 0444 root bin
+f usr/share/man/man3c/addseverity.3c 0444 root bin
+f usr/share/man/man3c/aio_cancel.3c 0444 root bin
+f usr/share/man/man3c/aio_error.3c 0444 root bin
+f usr/share/man/man3c/aio_fsync.3c 0444 root bin
+f usr/share/man/man3c/aio_read.3c 0444 root bin
+f usr/share/man/man3c/aio_return.3c 0444 root bin
+f usr/share/man/man3c/aio_suspend.3c 0444 root bin
+f usr/share/man/man3c/aio_waitn.3c 0444 root bin
+f usr/share/man/man3c/aio_write.3c 0444 root bin
+f usr/share/man/man3c/aiocancel.3c 0444 root bin
+f usr/share/man/man3c/aioread.3c 0444 root bin
+f usr/share/man/man3c/aiowait.3c 0444 root bin
+s usr/share/man/man3c/aiowrite.3c=aioread.3c
+f usr/share/man/man3c/aligned_alloc.3c 0444 root bin
+s usr/share/man/man3c/alloca.3c=malloc.3c
+s usr/share/man/man3c/alphasort.3c=scandir.3c
+f usr/share/man/man3c/arc4random.3c 0444 root bin
+s usr/share/man/man3c/arc4random_buf.3c=arc4random.3c
+s usr/share/man/man3c/arc4random_uniform.3c=arc4random.3c
+s usr/share/man/man3c/ascftime.3c=strftime.3c
+s usr/share/man/man3c/asctime.3c=ctime.3c
+s usr/share/man/man3c/asctime_r.3c=ctime.3c
+s usr/share/man/man3c/asprintf.3c=printf.3c
+f usr/share/man/man3c/assert.3c 0444 root bin
+s usr/share/man/man3c/at_quick_exit.3c=quick_exit.3c
+f usr/share/man/man3c/atexit.3c 0444 root bin
+s usr/share/man/man3c/atof.3c=strtod.3c
+s usr/share/man/man3c/atoi.3c=strtol.3c
+s usr/share/man/man3c/atol.3c=strtol.3c
+s usr/share/man/man3c/atoll.3c=strtol.3c
+f usr/share/man/man3c/atomic_add.3c 0444 root bin
+s usr/share/man/man3c/atomic_add_16.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_16_nv.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_32.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_32_nv.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_64.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_64_nv.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_8.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_8_nv.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_char.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_char_nv.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_int.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_int_nv.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_long.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_long_nv.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_ptr.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_ptr_nv.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_short.3c=atomic_add.3c
+s usr/share/man/man3c/atomic_add_short_nv.3c=atomic_add.3c
+f usr/share/man/man3c/atomic_and.3c 0444 root bin
+s usr/share/man/man3c/atomic_and_16.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_16_nv.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_32.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_32_nv.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_64.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_64_nv.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_8.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_8_nv.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_uchar.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_uchar_nv.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_uint.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_uint_nv.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_ulong.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_ulong_nv.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_ushort.3c=atomic_and.3c
+s usr/share/man/man3c/atomic_and_ushort_nv.3c=atomic_and.3c
+f usr/share/man/man3c/atomic_bits.3c 0444 root bin
+f usr/share/man/man3c/atomic_cas.3c 0444 root bin
+s usr/share/man/man3c/atomic_cas_16.3c=atomic_cas.3c
+s usr/share/man/man3c/atomic_cas_32.3c=atomic_cas.3c
+s usr/share/man/man3c/atomic_cas_64.3c=atomic_cas.3c
+s usr/share/man/man3c/atomic_cas_8.3c=atomic_cas.3c
+s usr/share/man/man3c/atomic_cas_ptr.3c=atomic_cas.3c
+s usr/share/man/man3c/atomic_cas_uchar.3c=atomic_cas.3c
+s usr/share/man/man3c/atomic_cas_uint.3c=atomic_cas.3c
+s usr/share/man/man3c/atomic_cas_ulong.3c=atomic_cas.3c
+s usr/share/man/man3c/atomic_cas_ushort.3c=atomic_cas.3c
+s usr/share/man/man3c/atomic_clear_long_excl.3c=atomic_bits.3c
+f usr/share/man/man3c/atomic_dec.3c 0444 root bin
+s usr/share/man/man3c/atomic_dec_16.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_16_nv.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_32.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_32_nv.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_64.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_64_nv.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_8.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_8_nv.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_ptr.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_ptr_nv.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_uchar.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_uchar_nv.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_uint.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_uint_nv.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_ulong.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_ulong_nv.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_ushort.3c=atomic_dec.3c
+s usr/share/man/man3c/atomic_dec_ushort_nv.3c=atomic_dec.3c
+f usr/share/man/man3c/atomic_inc.3c 0444 root bin
+s usr/share/man/man3c/atomic_inc_16.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_16_nv.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_32.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_32_nv.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_64.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_64_nv.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_8.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_8_nv.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_ptr.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_ptr_nv.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_uchar.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_uchar_nv.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_uint.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_uint_nv.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_ulong.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_ulong_nv.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_ushort.3c=atomic_inc.3c
+s usr/share/man/man3c/atomic_inc_ushort_nv.3c=atomic_inc.3c
+f usr/share/man/man3c/atomic_ops.3c 0444 root bin
+f usr/share/man/man3c/atomic_or.3c 0444 root bin
+s usr/share/man/man3c/atomic_or_16.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_16_nv.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_32.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_32_nv.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_64.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_64_nv.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_8.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_8_nv.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_uchar.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_uchar_nv.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_uint.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_uint_nv.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_ulong.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_ulong_nv.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_ushort.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_or_ushort_nv.3c=atomic_or.3c
+s usr/share/man/man3c/atomic_set_long_excl.3c=atomic_bits.3c
+f usr/share/man/man3c/atomic_swap.3c 0444 root bin
+s usr/share/man/man3c/atomic_swap_16.3c=atomic_swap.3c
+s usr/share/man/man3c/atomic_swap_32.3c=atomic_swap.3c
+s usr/share/man/man3c/atomic_swap_64.3c=atomic_swap.3c
+s usr/share/man/man3c/atomic_swap_8.3c=atomic_swap.3c
+s usr/share/man/man3c/atomic_swap_ptr.3c=atomic_swap.3c
+s usr/share/man/man3c/atomic_swap_uchar.3c=atomic_swap.3c
+s usr/share/man/man3c/atomic_swap_uint.3c=atomic_swap.3c
+s usr/share/man/man3c/atomic_swap_ulong.3c=atomic_swap.3c
+s usr/share/man/man3c/atomic_swap_ushort.3c=atomic_swap.3c
+f usr/share/man/man3c/attropen.3c 0444 root bin
+s usr/share/man/man3c/backtrace.3c=walkcontext.3c
+s usr/share/man/man3c/backtrace_symbols.3c=walkcontext.3c
+s usr/share/man/man3c/backtrace_symbols_fd.3c=walkcontext.3c
+f usr/share/man/man3c/basename.3c 0444 root bin
+s usr/share/man/man3c/bcmp.3c=bstring.3c
+s usr/share/man/man3c/bcopy.3c=bstring.3c
+s usr/share/man/man3c/be16toh.3c=endian.3c
+s usr/share/man/man3c/be32toh.3c=endian.3c
+s usr/share/man/man3c/be64toh.3c=endian.3c
+s usr/share/man/man3c/betoh16.3c=endian.3c
+s usr/share/man/man3c/betoh32.3c=endian.3c
+s usr/share/man/man3c/betoh64.3c=endian.3c
+s usr/share/man/man3c/bind_textdomain_codeset.3c=gettext.3c
+s usr/share/man/man3c/bindtextdomain.3c=gettext.3c
+f usr/share/man/man3c/bsd_signal.3c 0444 root bin
+f usr/share/man/man3c/bsearch.3c 0444 root bin
+f usr/share/man/man3c/bstring.3c 0444 root bin
+f usr/share/man/man3c/btowc.3c 0444 root bin
+s usr/share/man/man3c/btowc_l.3c=btowc.3c
+f usr/share/man/man3c/byteorder.3c 0444 root bin
+s usr/share/man/man3c/bzero.3c=bstring.3c
+f usr/share/man/man3c/c16rtomb.3c 0444 root bin
+s usr/share/man/man3c/c32rtomb.3c=c16rtomb.3c
+f usr/share/man/man3c/call_once.3c 0444 root bin
+s usr/share/man/man3c/calloc.3c=malloc.3c
+s usr/share/man/man3c/canonicalize_file_name.3c=realpath.3c
+s usr/share/man/man3c/catclose.3c=catopen.3c
+f usr/share/man/man3c/catgets.3c 0444 root bin
+f usr/share/man/man3c/catopen.3c 0444 root bin
+f usr/share/man/man3c/cfgetispeed.3c 0444 root bin
+s usr/share/man/man3c/cfgetospeed.3c=cfgetispeed.3c
+f usr/share/man/man3c/cfsetispeed.3c 0444 root bin
+s usr/share/man/man3c/cfsetospeed.3c=cfsetispeed.3c
+s usr/share/man/man3c/cftime.3c=strftime.3c
+f usr/share/man/man3c/clearenv.3c 0444 root bin
+s usr/share/man/man3c/clearerr.3c=ferror.3c
+f usr/share/man/man3c/clock.3c 0444 root bin
+s usr/share/man/man3c/clock_getres.3c=clock_settime.3c
+s usr/share/man/man3c/clock_gettime.3c=clock_settime.3c
+f usr/share/man/man3c/clock_nanosleep.3c 0444 root bin
+f usr/share/man/man3c/clock_settime.3c 0444 root bin
+f usr/share/man/man3c/closedir.3c 0444 root bin
+f usr/share/man/man3c/closefrom.3c 0444 root bin
+s usr/share/man/man3c/closelog.3c=syslog.3c
+f usr/share/man/man3c/cnd.3c 0444 root bin
+s usr/share/man/man3c/cnd_broadcast.3c=cnd.3c
+s usr/share/man/man3c/cnd_destroy.3c=cnd.3c
+s usr/share/man/man3c/cnd_init.3c=cnd.3c
+s usr/share/man/man3c/cnd_signal.3c=cnd.3c
+s usr/share/man/man3c/cnd_timedwait.3c=cnd.3c
+s usr/share/man/man3c/cnd_wait.3c=cnd.3c
+s usr/share/man/man3c/cond_broadcast.3c=cond_init.3c
+s usr/share/man/man3c/cond_destroy.3c=cond_init.3c
+f usr/share/man/man3c/cond_init.3c 0444 root bin
+s usr/share/man/man3c/cond_reltimedwait.3c=cond_init.3c
+s usr/share/man/man3c/cond_signal.3c=cond_init.3c
+s usr/share/man/man3c/cond_timedwait.3c=cond_init.3c
+s usr/share/man/man3c/cond_wait.3c=cond_init.3c
+f usr/share/man/man3c/confstr.3c 0444 root bin
+f usr/share/man/man3c/crypt.3c 0444 root bin
+f usr/share/man/man3c/crypt_genhash_impl.3c 0444 root bin
+f usr/share/man/man3c/crypt_gensalt.3c 0444 root bin
+f usr/share/man/man3c/crypt_gensalt_impl.3c 0444 root bin
+f usr/share/man/man3c/cset.3c 0444 root bin
+s usr/share/man/man3c/csetcol.3c=cset.3c
+s usr/share/man/man3c/csetlen.3c=cset.3c
+s usr/share/man/man3c/csetno.3c=cset.3c
+f usr/share/man/man3c/ctermid.3c 0444 root bin
+s usr/share/man/man3c/ctermid_r.3c=ctermid.3c
+f usr/share/man/man3c/ctime.3c 0444 root bin
+s usr/share/man/man3c/ctime_r.3c=ctime.3c
+f usr/share/man/man3c/ctype.3c 0444 root bin
+f usr/share/man/man3c/cuserid.3c 0444 root bin
+f usr/share/man/man3c/daemon.3c 0444 root bin
+s usr/share/man/man3c/dbm_clearerr.3c=ndbm.3c
+s usr/share/man/man3c/dbm_close.3c=ndbm.3c
+s usr/share/man/man3c/dbm_delete.3c=ndbm.3c
+s usr/share/man/man3c/dbm_error.3c=ndbm.3c
+s usr/share/man/man3c/dbm_fetch.3c=ndbm.3c
+s usr/share/man/man3c/dbm_firstkey.3c=ndbm.3c
+s usr/share/man/man3c/dbm_nextkey.3c=ndbm.3c
+s usr/share/man/man3c/dbm_open.3c=ndbm.3c
+s usr/share/man/man3c/dbm_store.3c=ndbm.3c
+s usr/share/man/man3c/dcgettext.3c=gettext.3c
+s usr/share/man/man3c/dcngettext.3c=gettext.3c
+s usr/share/man/man3c/decimal_to_double.3c=decimal_to_floating.3c
+s usr/share/man/man3c/decimal_to_extended.3c=decimal_to_floating.3c
+f usr/share/man/man3c/decimal_to_floating.3c 0444 root bin
+s usr/share/man/man3c/decimal_to_quadruple.3c=decimal_to_floating.3c
+s usr/share/man/man3c/decimal_to_single.3c=decimal_to_floating.3c
+s usr/share/man/man3c/dgettext.3c=gettext.3c
+f usr/share/man/man3c/difftime.3c 0444 root bin
+f usr/share/man/man3c/directio.3c 0444 root bin
+f usr/share/man/man3c/dirfd.3c 0444 root bin
+f usr/share/man/man3c/dirname.3c 0444 root bin
+f usr/share/man/man3c/div.3c 0444 root bin
+f usr/share/man/man3c/dladdr.3c 0444 root bin
+s usr/share/man/man3c/dladdr1.3c=dladdr.3c
+f usr/share/man/man3c/dlclose.3c 0444 root bin
+f usr/share/man/man3c/dldump.3c 0444 root bin
+f usr/share/man/man3c/dlerror.3c 0444 root bin
+f usr/share/man/man3c/dlinfo.3c 0444 root bin
+s usr/share/man/man3c/dlmopen.3c=dlopen.3c
+f usr/share/man/man3c/dlopen.3c 0444 root bin
+f usr/share/man/man3c/dlsym.3c 0444 root bin
+s usr/share/man/man3c/dngettext.3c=gettext.3c
+f usr/share/man/man3c/door_bind.3c 0444 root bin
+f usr/share/man/man3c/door_call.3c 0444 root bin
+f usr/share/man/man3c/door_create.3c 0444 root bin
+f usr/share/man/man3c/door_cred.3c 0444 root bin
+f usr/share/man/man3c/door_getparam.3c 0444 root bin
+f usr/share/man/man3c/door_info.3c 0444 root bin
+f usr/share/man/man3c/door_return.3c 0444 root bin
+f usr/share/man/man3c/door_revoke.3c 0444 root bin
+f usr/share/man/man3c/door_server_create.3c 0444 root bin
+s usr/share/man/man3c/door_setparam.3c=door_getparam.3c
+f usr/share/man/man3c/door_ucred.3c 0444 root bin
+s usr/share/man/man3c/door_unbind.3c=door_bind.3c
+s usr/share/man/man3c/double_to_decimal.3c=floating_to_decimal.3c
+f usr/share/man/man3c/drand48.3c 0444 root bin
+f usr/share/man/man3c/dup2.3c 0444 root bin
+s usr/share/man/man3c/dup3.3c=dup2.3c
+s usr/share/man/man3c/duplocale.3c=newlocale.3c
+f usr/share/man/man3c/econvert.3c 0444 root bin
+f usr/share/man/man3c/ecvt.3c 0444 root bin
+s usr/share/man/man3c/edata.3c=end.3c
+f usr/share/man/man3c/enable_extended_FILE_stdio.3c 0444 root bin
+f usr/share/man/man3c/encrypt.3c 0444 root bin
+f usr/share/man/man3c/end.3c 0444 root bin
+s usr/share/man/man3c/endgrent.3c=getgrnam.3c
+f usr/share/man/man3c/endian.3c 0444 root bin
+s usr/share/man/man3c/endnetgrent.3c=getnetgrent.3c
+s usr/share/man/man3c/endpwent.3c=getpwnam.3c
+s usr/share/man/man3c/endspent.3c=getspnam.3c
+s usr/share/man/man3c/endusershell.3c=getusershell.3c
+s usr/share/man/man3c/endutent.3c=getutent.3c
+s usr/share/man/man3c/endutxent.3c=getutxent.3c
+f usr/share/man/man3c/epoll_create.3c 0444 root bin
+s usr/share/man/man3c/epoll_create1.3c=epoll_create.3c
+f usr/share/man/man3c/epoll_ctl.3c 0444 root bin
+s usr/share/man/man3c/epoll_pwait.3c=epoll_wait.3c
+f usr/share/man/man3c/epoll_wait.3c 0444 root bin
+s usr/share/man/man3c/erand48.3c=drand48.3c
+f usr/share/man/man3c/err.3c 0444 root bin
+s usr/share/man/man3c/errc.3c=err.3c
+s usr/share/man/man3c/errno.3c=perror.3c
+s usr/share/man/man3c/errx.3c=err.3c
+s usr/share/man/man3c/etext.3c=end.3c
+s usr/share/man/man3c/euccol.3c=euclen.3c
+f usr/share/man/man3c/euclen.3c 0444 root bin
+s usr/share/man/man3c/eucscol.3c=euclen.3c
+f usr/share/man/man3c/eventfd.3c 0444 root bin
+s usr/share/man/man3c/eventfd_read.3c=eventfd.3c
+s usr/share/man/man3c/eventfd_write.3c=eventfd.3c
+f usr/share/man/man3c/exit.3c 0444 root bin
+s usr/share/man/man3c/explicit_bzero.3c=bstring.3c
+s usr/share/man/man3c/extended_to_decimal.3c=floating_to_decimal.3c
+f usr/share/man/man3c/fattach.3c 0444 root bin
+f usr/share/man/man3c/fclose.3c 0444 root bin
+f usr/share/man/man3c/fcloseall.3c 0444 root bin
+s usr/share/man/man3c/fconvert.3c=econvert.3c
+s usr/share/man/man3c/fcvt.3c=ecvt.3c
+f usr/share/man/man3c/fdatasync.3c 0444 root bin
+f usr/share/man/man3c/fdetach.3c 0444 root bin
+f usr/share/man/man3c/fdopen.3c 0444 root bin
+s usr/share/man/man3c/fdopendir.3c=opendir.3c
+s usr/share/man/man3c/fdwalk.3c=closefrom.3c
+s usr/share/man/man3c/feof.3c=ferror.3c
+f usr/share/man/man3c/ferror.3c 0444 root bin
+f usr/share/man/man3c/fflush.3c 0444 root bin
+f usr/share/man/man3c/ffs.3c 0444 root bin
+s usr/share/man/man3c/ffsl.3c=ffs.3c
+s usr/share/man/man3c/ffsll.3c=ffs.3c
+f usr/share/man/man3c/fgetattr.3c 0444 root bin
+f usr/share/man/man3c/fgetc.3c 0444 root bin
+s usr/share/man/man3c/fgetgrent.3c=getgrnam.3c
+s usr/share/man/man3c/fgetgrent_r.3c=getgrnam.3c
+f usr/share/man/man3c/fgetpos.3c 0444 root bin
+s usr/share/man/man3c/fgetpwent.3c=getpwnam.3c
+s usr/share/man/man3c/fgetpwent_r.3c=getpwnam.3c
+s usr/share/man/man3c/fgets.3c=gets.3c
+s usr/share/man/man3c/fgetspent.3c=getspnam.3c
+s usr/share/man/man3c/fgetspent_r.3c=getspnam.3c
+f usr/share/man/man3c/fgetwc.3c 0444 root bin
+s usr/share/man/man3c/fgetwc_l.3c=fgetwc.3c
+s usr/share/man/man3c/fgetws.3c=getws.3c
+s usr/share/man/man3c/file_to_decimal.3c=string_to_decimal.3c
+s usr/share/man/man3c/fileno.3c=ferror.3c
+s usr/share/man/man3c/finite.3c=isnand.3c
+f usr/share/man/man3c/floating_to_decimal.3c 0444 root bin
+f usr/share/man/man3c/flock.3c 0444 root bin
+f usr/share/man/man3c/flockfile.3c 0444 root bin
+s usr/share/man/man3c/fls.3c=ffs.3c
+s usr/share/man/man3c/flsl.3c=ffs.3c
+s usr/share/man/man3c/flsll.3c=ffs.3c
+f usr/share/man/man3c/fmemopen.3c 0444 root bin
+f usr/share/man/man3c/fmtmsg.3c 0444 root bin
+f usr/share/man/man3c/fnmatch.3c 0444 root bin
+f usr/share/man/man3c/fopen.3c 0444 root bin
+s usr/share/man/man3c/fpclass.3c=isnand.3c
+s usr/share/man/man3c/fpgetmask.3c=fpgetround.3c
+f usr/share/man/man3c/fpgetround.3c 0444 root bin
+s usr/share/man/man3c/fpgetsticky.3c=fpgetround.3c
+s usr/share/man/man3c/fprintf.3c=printf.3c
+s usr/share/man/man3c/fpsetmask.3c=fpgetround.3c
+s usr/share/man/man3c/fpsetround.3c=fpgetround.3c
+s usr/share/man/man3c/fpsetsticky.3c=fpgetround.3c
+f usr/share/man/man3c/fputc.3c 0444 root bin
+s usr/share/man/man3c/fputs.3c=puts.3c
+f usr/share/man/man3c/fputwc.3c 0444 root bin
+f usr/share/man/man3c/fputws.3c 0444 root bin
+f usr/share/man/man3c/fread.3c 0444 root bin
+s usr/share/man/man3c/free.3c=malloc.3c
+s usr/share/man/man3c/freelocale.3c=newlocale.3c
+s usr/share/man/man3c/freezero.3c=malloc.3c
+f usr/share/man/man3c/freopen.3c 0444 root bin
+s usr/share/man/man3c/fscanf.3c=scanf.3c
+f usr/share/man/man3c/fseek.3c 0444 root bin
+s usr/share/man/man3c/fseeko.3c=fseek.3c
+s usr/share/man/man3c/fsetattr.3c=fgetattr.3c
+f usr/share/man/man3c/fsetpos.3c 0444 root bin
+f usr/share/man/man3c/fsync.3c 0444 root bin
+f usr/share/man/man3c/ftell.3c 0444 root bin
+s usr/share/man/man3c/ftello.3c=ftell.3c
+f usr/share/man/man3c/ftime.3c 0444 root bin
+f usr/share/man/man3c/ftok.3c 0444 root bin
+s usr/share/man/man3c/ftruncate.3c=truncate.3c
+s usr/share/man/man3c/ftrylockfile.3c=flockfile.3c
+f usr/share/man/man3c/fts.3c 0444 root bin
+s usr/share/man/man3c/fts_children.3c=fts.3c
+s usr/share/man/man3c/fts_close.3c=fts.3c
+s usr/share/man/man3c/fts_open.3c=fts.3c
+s usr/share/man/man3c/fts_read.3c=fts.3c
+s usr/share/man/man3c/fts_set.3c=fts.3c
+f usr/share/man/man3c/ftw.3c 0444 root bin
+s usr/share/man/man3c/func_to_decimal.3c=string_to_decimal.3c
+s usr/share/man/man3c/funlockfile.3c=flockfile.3c
+f usr/share/man/man3c/fwide.3c 0444 root bin
+f usr/share/man/man3c/fwprintf.3c 0444 root bin
+f usr/share/man/man3c/fwrite.3c 0444 root bin
+f usr/share/man/man3c/fwscanf.3c 0444 root bin
+s usr/share/man/man3c/gconvert.3c=econvert.3c
+s usr/share/man/man3c/gcvt.3c=ecvt.3c
+f usr/share/man/man3c/get_nprocs.3c 0444 root bin
+s usr/share/man/man3c/get_nprocs_conf.3c=get_nprocs.3c
+s usr/share/man/man3c/getattrat.3c=fgetattr.3c
+s usr/share/man/man3c/getc.3c=fgetc.3c
+s usr/share/man/man3c/getc_unlocked.3c=fgetc.3c
+s usr/share/man/man3c/getchar.3c=fgetc.3c
+s usr/share/man/man3c/getchar_unlocked.3c=fgetc.3c
+f usr/share/man/man3c/getcpuid.3c 0444 root bin
+f usr/share/man/man3c/getcwd.3c 0444 root bin
+f usr/share/man/man3c/getdate.3c 0444 root bin
+s usr/share/man/man3c/getdelim.3c=getline.3c
+f usr/share/man/man3c/getdtablesize.3c 0444 root bin
+f usr/share/man/man3c/getentropy.3c 0444 root bin
+f usr/share/man/man3c/getenv.3c 0444 root bin
+f usr/share/man/man3c/getexecname.3c 0444 root bin
+s usr/share/man/man3c/getextmntent.3c=getmntent.3c
+s usr/share/man/man3c/getgrent.3c=getgrnam.3c
+s usr/share/man/man3c/getgrent_r.3c=getgrnam.3c
+s usr/share/man/man3c/getgrgid.3c=getgrnam.3c
+s usr/share/man/man3c/getgrgid_r.3c=getgrnam.3c
+f usr/share/man/man3c/getgrnam.3c 0444 root bin
+s usr/share/man/man3c/getgrnam_r.3c=getgrnam.3c
+f usr/share/man/man3c/getgrouplist.3c 0444 root bin
+s usr/share/man/man3c/gethomelgroup.3c=getcpuid.3c
+f usr/share/man/man3c/gethostid.3c 0444 root bin
+f usr/share/man/man3c/gethostname.3c 0444 root bin
+f usr/share/man/man3c/gethrtime.3c 0444 root bin
+s usr/share/man/man3c/gethrvtime.3c=gethrtime.3c
+f usr/share/man/man3c/getline.3c 0444 root bin
+f usr/share/man/man3c/getloadavg.3c 0444 root bin
+f usr/share/man/man3c/getlogin.3c 0444 root bin
+s usr/share/man/man3c/getlogin_r.3c=getlogin.3c
+s usr/share/man/man3c/getmntany.3c=getmntent.3c
+f usr/share/man/man3c/getmntent.3c 0444 root bin
+f usr/share/man/man3c/getnetgrent.3c 0444 root bin
+s usr/share/man/man3c/getnetgrent_r.3c=getnetgrent.3c
+f usr/share/man/man3c/getopt.3c 0444 root bin
+f usr/share/man/man3c/getopt_long.3c 0444 root bin
+s usr/share/man/man3c/getopt_long_clip.3c=getopt_long.3c
+s usr/share/man/man3c/getopt_long_only.3c=getopt_long.3c
+f usr/share/man/man3c/getpagesize.3c 0444 root bin
+f usr/share/man/man3c/getpagesizes.3c 0444 root bin
+f usr/share/man/man3c/getpass.3c 0444 root bin
+s usr/share/man/man3c/getpassphrase.3c=getpass.3c
+f usr/share/man/man3c/getpeerucred.3c 0444 root bin
+f usr/share/man/man3c/getpriority.3c 0444 root bin
+f usr/share/man/man3c/getprogname.3c 0444 root bin
+f usr/share/man/man3c/getpw.3c 0444 root bin
+s usr/share/man/man3c/getpwent.3c=getpwnam.3c
+s usr/share/man/man3c/getpwent_r.3c=getpwnam.3c
+f usr/share/man/man3c/getpwnam.3c 0444 root bin
+s usr/share/man/man3c/getpwnam_r.3c=getpwnam.3c
+s usr/share/man/man3c/getpwuid.3c=getpwnam.3c
+s usr/share/man/man3c/getpwuid_r.3c=getpwnam.3c
+f usr/share/man/man3c/getrusage.3c 0444 root bin
+f usr/share/man/man3c/gets.3c 0444 root bin
+s usr/share/man/man3c/getspent.3c=getspnam.3c
+s usr/share/man/man3c/getspent_r.3c=getspnam.3c
+f usr/share/man/man3c/getspnam.3c 0444 root bin
+s usr/share/man/man3c/getspnam_r.3c=getspnam.3c
+f usr/share/man/man3c/getsubopt.3c 0444 root bin
+f usr/share/man/man3c/gettext.3c 0444 root bin
+f usr/share/man/man3c/gettimeofday.3c 0444 root bin
+f usr/share/man/man3c/gettxt.3c 0444 root bin
+f usr/share/man/man3c/getusershell.3c 0444 root bin
+f usr/share/man/man3c/getutent.3c 0444 root bin
+s usr/share/man/man3c/getutid.3c=getutent.3c
+s usr/share/man/man3c/getutline.3c=getutent.3c
+s usr/share/man/man3c/getutmp.3c=getutxent.3c
+s usr/share/man/man3c/getutmpx.3c=getutxent.3c
+f usr/share/man/man3c/getutxent.3c 0444 root bin
+s usr/share/man/man3c/getutxid.3c=getutxent.3c
+s usr/share/man/man3c/getutxline.3c=getutxent.3c
+s usr/share/man/man3c/getvfsany.3c=getvfsent.3c
+f usr/share/man/man3c/getvfsent.3c 0444 root bin
+s usr/share/man/man3c/getvfsfile.3c=getvfsent.3c
+s usr/share/man/man3c/getvfsspec.3c=getvfsent.3c
+s usr/share/man/man3c/getw.3c=fgetc.3c
+f usr/share/man/man3c/getwc.3c 0444 root bin
+s usr/share/man/man3c/getwc_l.3c=getwc.3c
+f usr/share/man/man3c/getwchar.3c 0444 root bin
+s usr/share/man/man3c/getwchar_l.3c=getwchar.3c
+f usr/share/man/man3c/getwd.3c 0444 root bin
+f usr/share/man/man3c/getwidth.3c 0444 root bin
+f usr/share/man/man3c/getws.3c 0444 root bin
+f usr/share/man/man3c/getzoneid.3c 0444 root bin
+s usr/share/man/man3c/getzoneidbyname.3c=getzoneid.3c
+s usr/share/man/man3c/getzonenamebyid.3c=getzoneid.3c
+f usr/share/man/man3c/glob.3c 0444 root bin
+s usr/share/man/man3c/globfree.3c=glob.3c
+s usr/share/man/man3c/gmtime.3c=ctime.3c
+s usr/share/man/man3c/gmtime_r.3c=ctime.3c
+f usr/share/man/man3c/grantpt.3c 0444 root bin
+s usr/share/man/man3c/gsignal.3c=ssignal.3c
+s usr/share/man/man3c/hasmntopt.3c=getmntent.3c
+s usr/share/man/man3c/hcreate.3c=hsearch.3c
+s usr/share/man/man3c/hdestroy.3c=hsearch.3c
+f usr/share/man/man3c/hsearch.3c 0444 root bin
+s usr/share/man/man3c/htobe16.3c=endian.3c
+s usr/share/man/man3c/htobe32.3c=endian.3c
+s usr/share/man/man3c/htobe64.3c=endian.3c
+s usr/share/man/man3c/htole16.3c=endian.3c
+s usr/share/man/man3c/htole32.3c=endian.3c
+s usr/share/man/man3c/htole64.3c=endian.3c
+s usr/share/man/man3c/htonl.3c=byteorder.3c
+s usr/share/man/man3c/htonll.3c=byteorder.3c
+s usr/share/man/man3c/htons.3c=byteorder.3c
+f usr/share/man/man3c/iconv.3c 0444 root bin
+f usr/share/man/man3c/iconv_close.3c 0444 root bin
+f usr/share/man/man3c/iconv_open.3c 0444 root bin
+s usr/share/man/man3c/ignore_handler_s.3c=set_constraint_handler_s.3c
+f usr/share/man/man3c/imaxabs.3c 0444 root bin
+f usr/share/man/man3c/imaxdiv.3c 0444 root bin
+f usr/share/man/man3c/index.3c 0444 root bin
+f usr/share/man/man3c/inet.3c 0444 root bin
+s usr/share/man/man3c/inet6.3c=inet.3c
+s usr/share/man/man3c/inet_addr.3c=inet.3c
+s usr/share/man/man3c/inet_aton.3c=inet.3c
+s usr/share/man/man3c/inet_lnaof.3c=inet.3c
+s usr/share/man/man3c/inet_makeaddr.3c=inet.3c
+s usr/share/man/man3c/inet_netof.3c=inet.3c
+s usr/share/man/man3c/inet_network.3c=inet.3c
+s usr/share/man/man3c/inet_ntoa.3c=inet.3c
+s usr/share/man/man3c/inet_ntop.3c=inet.3c
+s usr/share/man/man3c/inet_pton.3c=inet.3c
+f usr/share/man/man3c/initgroups.3c 0444 root bin
+s usr/share/man/man3c/initstate.3c=random.3c
+s usr/share/man/man3c/innetgr.3c=getnetgrent.3c
+f usr/share/man/man3c/inotify_add_watch.3c 0444 root bin
+f usr/share/man/man3c/inotify_init.3c 0444 root bin
+s usr/share/man/man3c/inotify_init1.3c=inotify_init.3c
+f usr/share/man/man3c/inotify_rm_watch.3c 0444 root bin
+f usr/share/man/man3c/insque.3c 0444 root bin
+f usr/share/man/man3c/is_system_labeled.3c 0444 root bin
+f usr/share/man/man3c/isaexec.3c 0444 root bin
+s usr/share/man/man3c/isalnum.3c=ctype.3c
+s usr/share/man/man3c/isalnum_l.3c=ctype.3c
+s usr/share/man/man3c/isalpha.3c=ctype.3c
+s usr/share/man/man3c/isalpha_l.3c=ctype.3c
+s usr/share/man/man3c/isascii.3c=ctype.3c
+f usr/share/man/man3c/isastream.3c 0444 root bin
+f usr/share/man/man3c/isatty.3c 0444 root bin
+s usr/share/man/man3c/isblank.3c=ctype.3c
+s usr/share/man/man3c/isblank_l.3c=ctype.3c
+s usr/share/man/man3c/iscntrl.3c=ctype.3c
+s usr/share/man/man3c/iscntrl_l.3c=ctype.3c
+s usr/share/man/man3c/isdigit.3c=ctype.3c
+s usr/share/man/man3c/isdigit_l.3c=ctype.3c
+s usr/share/man/man3c/isenglish.3c=iswalpha.3c
+s usr/share/man/man3c/isgraph.3c=ctype.3c
+s usr/share/man/man3c/isgraph_l.3c=ctype.3c
+s usr/share/man/man3c/isideogram.3c=iswalpha.3c
+s usr/share/man/man3c/islower.3c=ctype.3c
+s usr/share/man/man3c/islower_l.3c=ctype.3c
+f usr/share/man/man3c/isnand.3c 0444 root bin
+s usr/share/man/man3c/isnanf.3c=isnand.3c
+s usr/share/man/man3c/isnumber.3c=iswalpha.3c
+s usr/share/man/man3c/isphonogram.3c=iswalpha.3c
+s usr/share/man/man3c/isprint.3c=ctype.3c
+s usr/share/man/man3c/isprint_l.3c=ctype.3c
+s usr/share/man/man3c/ispunct.3c=ctype.3c
+s usr/share/man/man3c/ispunct_l.3c=ctype.3c
+s usr/share/man/man3c/isspace.3c=ctype.3c
+s usr/share/man/man3c/isspace_l.3c=ctype.3c
+s usr/share/man/man3c/isspecial.3c=iswalpha.3c
+s usr/share/man/man3c/isupper.3c=ctype.3c
+s usr/share/man/man3c/isupper_l.3c=ctype.3c
+s usr/share/man/man3c/iswalnum.3c=iswalpha.3c
+s usr/share/man/man3c/iswalnum_l.3c=iswalpha.3c
+f usr/share/man/man3c/iswalpha.3c 0444 root bin
+s usr/share/man/man3c/iswalpha_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswantrl_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswascii.3c=iswalpha.3c
+s usr/share/man/man3c/iswascii_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswblank.3c=iswalpha.3c
+s usr/share/man/man3c/iswblank_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswcntrl.3c=iswalpha.3c
+s usr/share/man/man3c/iswcntrl_l.3c=iswalpha.3c
+f usr/share/man/man3c/iswctype.3c 0444 root bin
+s usr/share/man/man3c/iswctype_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswdigit.3c=iswalpha.3c
+s usr/share/man/man3c/iswdigit_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswgraph.3c=iswalpha.3c
+s usr/share/man/man3c/iswgraph_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswhexnumber.3c=iswalpha.3c
+s usr/share/man/man3c/iswhexnumber_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswideogram.3c=iswalpha.3c
+s usr/share/man/man3c/iswideogram_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswlower.3c=iswalpha.3c
+s usr/share/man/man3c/iswlower_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswnumber.3c=iswalpha.3c
+s usr/share/man/man3c/iswnumber_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswphonogram.3c=iswalpha.3c
+s usr/share/man/man3c/iswphonogram_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswprint.3c=iswalpha.3c
+s usr/share/man/man3c/iswprint_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswpunct.3c=iswalpha.3c
+s usr/share/man/man3c/iswpunct_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswspace.3c=iswalpha.3c
+s usr/share/man/man3c/iswspace_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswspecial.3c=iswalpha.3c
+s usr/share/man/man3c/iswspecial_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswupper.3c=iswalpha.3c
+s usr/share/man/man3c/iswupper_l.3c=iswalpha.3c
+s usr/share/man/man3c/iswxdigit.3c=iswalpha.3c
+s usr/share/man/man3c/iswxdigit_l.3c=iswalpha.3c
+s usr/share/man/man3c/isxdigit.3c=ctype.3c
+s usr/share/man/man3c/isxdigit_l.3c=ctype.3c
+s usr/share/man/man3c/jrand48.3c=drand48.3c
+f usr/share/man/man3c/killpg.3c 0444 root bin
+s usr/share/man/man3c/l64a.3c=a64l.3c
+s usr/share/man/man3c/labs.3c=abs.3c
+f usr/share/man/man3c/lckpwdf.3c 0444 root bin
+s usr/share/man/man3c/lcong48.3c=drand48.3c
+s usr/share/man/man3c/ldiv.3c=div.3c
+s usr/share/man/man3c/le16toh.3c=endian.3c
+s usr/share/man/man3c/le32toh.3c=endian.3c
+s usr/share/man/man3c/le64toh.3c=endian.3c
+s usr/share/man/man3c/letoh16.3c=endian.3c
+s usr/share/man/man3c/letoh32.3c=endian.3c
+s usr/share/man/man3c/letoh64.3c=endian.3c
+s usr/share/man/man3c/lfind.3c=lsearch.3c
+f usr/share/man/man3c/lfmt.3c 0444 root bin
+f usr/share/man/man3c/lio_listio.3c 0444 root bin
+s usr/share/man/man3c/llabs.3c=abs.3c
+s usr/share/man/man3c/lldiv.3c=div.3c
+s usr/share/man/man3c/lltostr.3c=strtol.3c
+f usr/share/man/man3c/localeconv.3c 0444 root bin
+s usr/share/man/man3c/localtime.3c=ctime.3c
+s usr/share/man/man3c/localtime_r.3c=ctime.3c
+f usr/share/man/man3c/lockf.3c 0444 root bin
+s usr/share/man/man3c/longjmp.3c=setjmp.3c
+s usr/share/man/man3c/lrand48.3c=drand48.3c
+f usr/share/man/man3c/lsearch.3c 0444 root bin
+f usr/share/man/man3c/madvise.3c 0444 root bin
+s usr/share/man/man3c/major.3c=makedev.3c
+f usr/share/man/man3c/makecontext.3c 0444 root bin
+f usr/share/man/man3c/makedev.3c 0444 root bin
+f usr/share/man/man3c/malloc.3c 0444 root bin
+f usr/share/man/man3c/mblen.3c 0444 root bin
+s usr/share/man/man3c/mblen_l.3c=mblen.3c
+f usr/share/man/man3c/mbrlen.3c 0444 root bin
+s usr/share/man/man3c/mbrlen_l.3c=mbrlen.3c
+f usr/share/man/man3c/mbrtoc16.3c 0444 root bin
+s usr/share/man/man3c/mbrtoc32.3c=mbrtoc16.3c
+s usr/share/man/man3c/mbrtowc.3c=mbrtoc16.3c
+s usr/share/man/man3c/mbrtowc_l.3c=mbrtoc16.3c
+f usr/share/man/man3c/mbsinit.3c 0444 root bin
+s usr/share/man/man3c/mbsinit_l.3c=mbsinit.3c
+s usr/share/man/man3c/mbsnrtowcs.3c=mbsrtowcs.3c
+s usr/share/man/man3c/mbsnrtowcs_l.3c=mbsrtowcs.3c
+f usr/share/man/man3c/mbsrtowcs.3c 0444 root bin
+s usr/share/man/man3c/mbsrtowcs_l.3c=mbsrtowcs.3c
+f usr/share/man/man3c/mbstowcs.3c 0444 root bin
+s usr/share/man/man3c/mbstowcs_l.3c=mbstowcs.3c
+f usr/share/man/man3c/mbtowc.3c 0444 root bin
+s usr/share/man/man3c/mbtowc_l.3c=mbtowc.3c
+s usr/share/man/man3c/memalign.3c=malloc.3c
+s usr/share/man/man3c/membar_consumer.3c=membar_ops.3c
+s usr/share/man/man3c/membar_enter.3c=membar_ops.3c
+s usr/share/man/man3c/membar_exit.3c=membar_ops.3c
+f usr/share/man/man3c/membar_ops.3c 0444 root bin
+s usr/share/man/man3c/membar_producer.3c=membar_ops.3c
+s usr/share/man/man3c/memccpy.3c=memory.3c
+s usr/share/man/man3c/memchr.3c=memory.3c
+s usr/share/man/man3c/memcmp.3c=memory.3c
+s usr/share/man/man3c/memcpy.3c=memory.3c
+s usr/share/man/man3c/memmem.3c=memory.3c
+s usr/share/man/man3c/memmove.3c=memory.3c
+s usr/share/man/man3c/memrchr.3c=memory.3c
+f usr/share/man/man3c/memory.3c 0444 root bin
+s usr/share/man/man3c/memset.3c=memory.3c
+f usr/share/man/man3c/memset_s.3c 0444 root bin
+s usr/share/man/man3c/minor.3c=makedev.3c
+s usr/share/man/man3c/mkdtemp.3c=mkstemp.3c
+f usr/share/man/man3c/mkfifo.3c 0444 root bin
+s usr/share/man/man3c/mkfifoat.3c=mkfifo.3c
+s usr/share/man/man3c/mkostemp.3c=mkstemp.3c
+s usr/share/man/man3c/mkostemps.3c=mkstemp.3c
+f usr/share/man/man3c/mkstemp.3c 0444 root bin
+s usr/share/man/man3c/mkstemps.3c=mkstemp.3c
+f usr/share/man/man3c/mktemp.3c 0444 root bin
+f usr/share/man/man3c/mktime.3c 0444 root bin
+f usr/share/man/man3c/mlock.3c 0444 root bin
+f usr/share/man/man3c/mlockall.3c 0444 root bin
+f usr/share/man/man3c/monitor.3c 0444 root bin
+f usr/share/man/man3c/mq_close.3c 0444 root bin
+f usr/share/man/man3c/mq_getattr.3c 0444 root bin
+f usr/share/man/man3c/mq_notify.3c 0444 root bin
+f usr/share/man/man3c/mq_open.3c 0444 root bin
+f usr/share/man/man3c/mq_receive.3c 0444 root bin
+s usr/share/man/man3c/mq_reltimedreceive_np.3c=mq_receive.3c
+s usr/share/man/man3c/mq_reltimedsend_np.3c=mq_send.3c
+f usr/share/man/man3c/mq_send.3c 0444 root bin
+f usr/share/man/man3c/mq_setattr.3c 0444 root bin
+s usr/share/man/man3c/mq_timedreceive.3c=mq_receive.3c
+s usr/share/man/man3c/mq_timedsend.3c=mq_send.3c
+f usr/share/man/man3c/mq_unlink.3c 0444 root bin
+s usr/share/man/man3c/mrand48.3c=drand48.3c
+f usr/share/man/man3c/msync.3c 0444 root bin
+f usr/share/man/man3c/mtx.3c 0444 root bin
+s usr/share/man/man3c/mtx_destroy.3c=mtx.3c
+s usr/share/man/man3c/mtx_init.3c=mtx.3c
+s usr/share/man/man3c/mtx_lock.3c=mtx.3c
+s usr/share/man/man3c/mtx_timedlock.3c=mtx.3c
+s usr/share/man/man3c/mtx_trylock.3c=mtx.3c
+s usr/share/man/man3c/mtx_unlock.3c=mtx.3c
+s usr/share/man/man3c/munlock.3c=mlock.3c
+s usr/share/man/man3c/munlockall.3c=mlockall.3c
+s usr/share/man/man3c/mutex_consistent.3c=mutex_init.3c
+s usr/share/man/man3c/mutex_destroy.3c=mutex_init.3c
+f usr/share/man/man3c/mutex_init.3c 0444 root bin
+s usr/share/man/man3c/mutex_lock.3c=mutex_init.3c
+s usr/share/man/man3c/mutex_trylock.3c=mutex_init.3c
+s usr/share/man/man3c/mutex_unlock.3c=mutex_init.3c
+f usr/share/man/man3c/nanosleep.3c 0444 root bin
+f usr/share/man/man3c/ndbm.3c 0444 root bin
+f usr/share/man/man3c/newlocale.3c 0444 root bin
+s usr/share/man/man3c/nftw.3c=ftw.3c
+s usr/share/man/man3c/ngettext.3c=gettext.3c
+f usr/share/man/man3c/nl_langinfo.3c 0444 root bin
+s usr/share/man/man3c/nl_langinfo_l.3c=nl_langinfo.3c
+s usr/share/man/man3c/nrand48.3c=drand48.3c
+s usr/share/man/man3c/ntohl.3c=byteorder.3c
+s usr/share/man/man3c/ntohll.3c=byteorder.3c
+s usr/share/man/man3c/ntohs.3c=byteorder.3c
+f usr/share/man/man3c/offsetof.3c 0444 root bin
+f usr/share/man/man3c/opendir.3c 0444 root bin
+s usr/share/man/man3c/openlog.3c=syslog.3c
+f usr/share/man/man3c/open_memstream.3c 0444 root bin
+s usr/share/man/man3c/open_wmemstream.3c=open_memstream.3c
+s usr/share/man/man3c/pclose.3c=popen.3c
+f usr/share/man/man3c/perror.3c 0444 root bin
+f usr/share/man/man3c/pfmt.3c 0444 root bin
+f usr/share/man/man3c/plock.3c 0444 root bin
+f usr/share/man/man3c/popen.3c 0444 root bin
+f usr/share/man/man3c/port_alert.3c 0444 root bin
+f usr/share/man/man3c/port_associate.3c 0444 root bin
+f usr/share/man/man3c/port_create.3c 0444 root bin
+s usr/share/man/man3c/port_dissociate.3c=port_associate.3c
+f usr/share/man/man3c/port_get.3c 0444 root bin
+s usr/share/man/man3c/port_getn.3c=port_get.3c
+f usr/share/man/man3c/port_send.3c 0444 root bin
+s usr/share/man/man3c/port_sendn.3c=port_send.3c
+f usr/share/man/man3c/posix_fadvise.3c 0444 root bin
+f usr/share/man/man3c/posix_fallocate.3c 0444 root bin
+f usr/share/man/man3c/posix_madvise.3c 0444 root bin
+f usr/share/man/man3c/posix_memalign.3c 0444 root bin
+f usr/share/man/man3c/posix_openpt.3c 0444 root bin
+f usr/share/man/man3c/posix_spawn.3c 0444 root bin
+f usr/share/man/man3c/posix_spawn_file_actions_addclose.3c 0444 root bin
+f usr/share/man/man3c/posix_spawn_file_actions_addclosefrom_np.3c 0444 root bin
+f usr/share/man/man3c/posix_spawn_file_actions_adddup2.3c 0444 root bin
+s usr/share/man/man3c/posix_spawn_file_actions_addopen.3c=posix_spawn_file_actions_addclose.3c
+f usr/share/man/man3c/posix_spawn_file_actions_destroy.3c 0444 root bin
+s usr/share/man/man3c/posix_spawn_file_actions_init.3c=posix_spawn_file_actions_destroy.3c
+f usr/share/man/man3c/posix_spawn_pipe_np.3c 0444 root bin
+f usr/share/man/man3c/posix_spawnattr_destroy.3c 0444 root bin
+f usr/share/man/man3c/posix_spawnattr_getflags.3c 0444 root bin
+f usr/share/man/man3c/posix_spawnattr_getpgroup.3c 0444 root bin
+f usr/share/man/man3c/posix_spawnattr_getschedparam.3c 0444 root bin
+f usr/share/man/man3c/posix_spawnattr_getschedpolicy.3c 0444 root bin
+f usr/share/man/man3c/posix_spawnattr_getsigdefault.3c 0444 root bin
+f usr/share/man/man3c/posix_spawnattr_getsigignore_np.3c 0444 root bin
+f usr/share/man/man3c/posix_spawnattr_getsigmask.3c 0444 root bin
+s usr/share/man/man3c/posix_spawnattr_init.3c=posix_spawnattr_destroy.3c
+s usr/share/man/man3c/posix_spawnattr_setflags.3c=posix_spawnattr_getflags.3c
+s usr/share/man/man3c/posix_spawnattr_setpgroup.3c=posix_spawnattr_getpgroup.3c
+s usr/share/man/man3c/posix_spawnattr_setschedparam.3c=posix_spawnattr_getschedparam.3c
+s usr/share/man/man3c/posix_spawnattr_setschedpolicy.3c=posix_spawnattr_getschedpolicy.3c
+s usr/share/man/man3c/posix_spawnattr_setsigdefault.3c=posix_spawnattr_getsigdefault.3c
+s usr/share/man/man3c/posix_spawnattr_setsigignore_np.3c=posix_spawnattr_getsigignore_np.3c
+s usr/share/man/man3c/posix_spawnattr_setsigmask.3c=posix_spawnattr_getsigmask.3c
+s usr/share/man/man3c/posix_spawnp.3c=posix_spawn.3c
+f usr/share/man/man3c/printf.3c 0444 root bin
+s usr/share/man/man3c/printstack.3c=walkcontext.3c
+f usr/share/man/man3c/priv_addset.3c 0444 root bin
+s usr/share/man/man3c/priv_allocset.3c=priv_addset.3c
+s usr/share/man/man3c/priv_basicset.3c=priv_addset.3c
+s usr/share/man/man3c/priv_copyset.3c=priv_addset.3c
+s usr/share/man/man3c/priv_delset.3c=priv_addset.3c
+s usr/share/man/man3c/priv_emptyset.3c=priv_addset.3c
+s usr/share/man/man3c/priv_fillset.3c=priv_addset.3c
+s usr/share/man/man3c/priv_freeset.3c=priv_addset.3c
+s usr/share/man/man3c/priv_getbyname.3c=priv_str_to_set.3c
+s usr/share/man/man3c/priv_getbynum.3c=priv_str_to_set.3c
+s usr/share/man/man3c/priv_getsetbyname.3c=priv_str_to_set.3c
+s usr/share/man/man3c/priv_getsetbynum.3c=priv_str_to_set.3c
+s usr/share/man/man3c/priv_gettext.3c=priv_str_to_set.3c
+s usr/share/man/man3c/priv_ineffect.3c=priv_set.3c
+s usr/share/man/man3c/priv_intersect.3c=priv_addset.3c
+s usr/share/man/man3c/priv_inverse.3c=priv_addset.3c
+s usr/share/man/man3c/priv_isemptyset.3c=priv_addset.3c
+s usr/share/man/man3c/priv_isequalset.3c=priv_addset.3c
+s usr/share/man/man3c/priv_isfullset.3c=priv_addset.3c
+s usr/share/man/man3c/priv_ismember.3c=priv_addset.3c
+s usr/share/man/man3c/priv_issubset.3c=priv_addset.3c
+f usr/share/man/man3c/priv_set.3c 0444 root bin
+s usr/share/man/man3c/priv_set_to_str.3c=priv_str_to_set.3c
+f usr/share/man/man3c/priv_str_to_set.3c 0444 root bin
+s usr/share/man/man3c/priv_union.3c=priv_addset.3c
+s usr/share/man/man3c/pselect.3c=select.3c
+f usr/share/man/man3c/pset_getloadavg.3c 0444 root bin
+s usr/share/man/man3c/psiginfo.3c=psignal.3c
+f usr/share/man/man3c/psignal.3c 0444 root bin
+f usr/share/man/man3c/pthread_atfork.3c 0444 root bin
+s usr/share/man/man3c/pthread_attr_destroy.3c=pthread_attr_init.3c
+f usr/share/man/man3c/pthread_attr_get_np.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_getdetachstate.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_getguardsize.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_getinheritsched.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_getname_np.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_getschedparam.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_getschedpolicy.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_getscope.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_getstack.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_getstackaddr.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_getstacksize.3c 0444 root bin
+f usr/share/man/man3c/pthread_attr_init.3c 0444 root bin
+s usr/share/man/man3c/pthread_attr_setdetachstate.3c=pthread_attr_getdetachstate.3c
+s usr/share/man/man3c/pthread_attr_setguardsize.3c=pthread_attr_getguardsize.3c
+s usr/share/man/man3c/pthread_attr_setinheritsched.3c=pthread_attr_getinheritsched.3c
+s usr/share/man/man3c/pthread_attr_setname_np.3c=pthread_attr_getname_np.3c
+s usr/share/man/man3c/pthread_attr_setschedparam.3c=pthread_attr_getschedparam.3c
+s usr/share/man/man3c/pthread_attr_setschedpolicy.3c=pthread_attr_getschedpolicy.3c
+s usr/share/man/man3c/pthread_attr_setscope.3c=pthread_attr_getscope.3c
+s usr/share/man/man3c/pthread_attr_setstack.3c=pthread_attr_getstack.3c
+s usr/share/man/man3c/pthread_attr_setstackaddr.3c=pthread_attr_getstackaddr.3c
+s usr/share/man/man3c/pthread_attr_setstacksize.3c=pthread_attr_getstacksize.3c
+f usr/share/man/man3c/pthread_barrier_destroy.3c 0444 root bin
+s usr/share/man/man3c/pthread_barrier_init.3c=pthread_barrier_destroy.3c
+f usr/share/man/man3c/pthread_barrier_wait.3c 0444 root bin
+f usr/share/man/man3c/pthread_barrierattr_destroy.3c 0444 root bin
+f usr/share/man/man3c/pthread_barrierattr_getpshared.3c 0444 root bin
+s usr/share/man/man3c/pthread_barrierattr_init.3c=pthread_barrierattr_destroy.3c
+s usr/share/man/man3c/pthread_barrierattr_setpshared.3c=pthread_barrierattr_getpshared.3c
+f usr/share/man/man3c/pthread_cancel.3c 0444 root bin
+f usr/share/man/man3c/pthread_cleanup_pop.3c 0444 root bin
+f usr/share/man/man3c/pthread_cleanup_push.3c 0444 root bin
+s usr/share/man/man3c/pthread_cond_broadcast.3c=pthread_cond_signal.3c
+s usr/share/man/man3c/pthread_cond_destroy.3c=pthread_cond_init.3c
+f usr/share/man/man3c/pthread_cond_init.3c 0444 root bin
+s usr/share/man/man3c/pthread_cond_reltimedwait_np.3c=pthread_cond_wait.3c
+f usr/share/man/man3c/pthread_cond_signal.3c 0444 root bin
+s usr/share/man/man3c/pthread_cond_timedwait.3c=pthread_cond_wait.3c
+f usr/share/man/man3c/pthread_cond_wait.3c 0444 root bin
+s usr/share/man/man3c/pthread_condattr_destroy.3c=pthread_condattr_init.3c
+f usr/share/man/man3c/pthread_condattr_getclock.3c 0444 root bin
+f usr/share/man/man3c/pthread_condattr_getpshared.3c 0444 root bin
+f usr/share/man/man3c/pthread_condattr_init.3c 0444 root bin
+s usr/share/man/man3c/pthread_condattr_setclock.3c=pthread_condattr_getclock.3c
+s usr/share/man/man3c/pthread_condattr_setpshared.3c=pthread_condattr_getpshared.3c
+f usr/share/man/man3c/pthread_create.3c 0444 root bin
+f usr/share/man/man3c/pthread_detach.3c 0444 root bin
+f usr/share/man/man3c/pthread_equal.3c 0444 root bin
+f usr/share/man/man3c/pthread_exit.3c 0444 root bin
+f usr/share/man/man3c/pthread_getconcurrency.3c 0444 root bin
+f usr/share/man/man3c/pthread_getname_np.3c 0444 root bin
+f usr/share/man/man3c/pthread_getschedparam.3c 0444 root bin
+f usr/share/man/man3c/pthread_getspecific.3c 0444 root bin
+f usr/share/man/man3c/pthread_join.3c 0444 root bin
+f usr/share/man/man3c/pthread_key_create.3c 0444 root bin
+s usr/share/man/man3c/pthread_key_create_once_np.3c=pthread_key_create.3c
+f usr/share/man/man3c/pthread_key_delete.3c 0444 root bin
+f usr/share/man/man3c/pthread_kill.3c 0444 root bin
+f usr/share/man/man3c/pthread_mutex_consistent.3c 0444 root bin
+s usr/share/man/man3c/pthread_mutex_destroy.3c=pthread_mutex_init.3c
+f usr/share/man/man3c/pthread_mutex_getprioceiling.3c 0444 root bin
+f usr/share/man/man3c/pthread_mutex_init.3c 0444 root bin
+f usr/share/man/man3c/pthread_mutex_lock.3c 0444 root bin
+s usr/share/man/man3c/pthread_mutex_reltimedlock_np.3c=pthread_mutex_timedlock.3c
+s usr/share/man/man3c/pthread_mutex_setprioceiling.3c=pthread_mutex_getprioceiling.3c
+f usr/share/man/man3c/pthread_mutex_timedlock.3c 0444 root bin
+s usr/share/man/man3c/pthread_mutex_trylock.3c=pthread_mutex_lock.3c
+s usr/share/man/man3c/pthread_mutex_unlock.3c=pthread_mutex_lock.3c
+s usr/share/man/man3c/pthread_mutexattr_destroy.3c=pthread_mutexattr_init.3c
+f usr/share/man/man3c/pthread_mutexattr_getprioceiling.3c 0444 root bin
+f usr/share/man/man3c/pthread_mutexattr_getprotocol.3c 0444 root bin
+f usr/share/man/man3c/pthread_mutexattr_getpshared.3c 0444 root bin
+f usr/share/man/man3c/pthread_mutexattr_getrobust.3c 0444 root bin
+f usr/share/man/man3c/pthread_mutexattr_gettype.3c 0444 root bin
+f usr/share/man/man3c/pthread_mutexattr_init.3c 0444 root bin
+s usr/share/man/man3c/pthread_mutexattr_setprioceiling.3c=pthread_mutexattr_getprioceiling.3c
+s usr/share/man/man3c/pthread_mutexattr_setprotocol.3c=pthread_mutexattr_getprotocol.3c
+s usr/share/man/man3c/pthread_mutexattr_setpshared.3c=pthread_mutexattr_getpshared.3c
+s usr/share/man/man3c/pthread_mutexattr_setrobust.3c=pthread_mutexattr_getrobust.3c
+s usr/share/man/man3c/pthread_mutexattr_settype.3c=pthread_mutexattr_gettype.3c
+f usr/share/man/man3c/pthread_once.3c 0444 root bin
+s usr/share/man/man3c/pthread_rwlock_destroy.3c=pthread_rwlock_init.3c
+f usr/share/man/man3c/pthread_rwlock_init.3c 0444 root bin
+f usr/share/man/man3c/pthread_rwlock_rdlock.3c 0444 root bin
+s usr/share/man/man3c/pthread_rwlock_reltimedrdlock_np.3c=pthread_rwlock_timedrdlock.3c
+s usr/share/man/man3c/pthread_rwlock_reltimedwrlock_np.3c=pthread_rwlock_timedwrlock.3c
+f usr/share/man/man3c/pthread_rwlock_timedrdlock.3c 0444 root bin
+f usr/share/man/man3c/pthread_rwlock_timedwrlock.3c 0444 root bin
+s usr/share/man/man3c/pthread_rwlock_tryrdlock.3c=pthread_rwlock_rdlock.3c
+s usr/share/man/man3c/pthread_rwlock_trywrlock.3c=pthread_rwlock_wrlock.3c
+f usr/share/man/man3c/pthread_rwlock_unlock.3c 0444 root bin
+f usr/share/man/man3c/pthread_rwlock_wrlock.3c 0444 root bin
+s usr/share/man/man3c/pthread_rwlockattr_destroy.3c=pthread_rwlockattr_init.3c
+f usr/share/man/man3c/pthread_rwlockattr_getpshared.3c 0444 root bin
+f usr/share/man/man3c/pthread_rwlockattr_init.3c 0444 root bin
+s usr/share/man/man3c/pthread_rwlockattr_setpshared.3c=pthread_rwlockattr_getpshared.3c
+f usr/share/man/man3c/pthread_self.3c 0444 root bin
+f usr/share/man/man3c/pthread_setcancelstate.3c 0444 root bin
+f usr/share/man/man3c/pthread_setcanceltype.3c 0444 root bin
+s usr/share/man/man3c/pthread_setconcurrency.3c=pthread_getconcurrency.3c
+s usr/share/man/man3c/pthread_setname_np.3c=pthread_getname_np.3c
+s usr/share/man/man3c/pthread_setschedparam.3c=pthread_getschedparam.3c
+f usr/share/man/man3c/pthread_setschedprio.3c 0444 root bin
+s usr/share/man/man3c/pthread_setspecific.3c=pthread_getspecific.3c
+f usr/share/man/man3c/pthread_sigmask.3c 0444 root bin
+f usr/share/man/man3c/pthread_spin_destroy.3c 0444 root bin
+s usr/share/man/man3c/pthread_spin_init.3c=pthread_spin_destroy.3c
+f usr/share/man/man3c/pthread_spin_lock.3c 0444 root bin
+s usr/share/man/man3c/pthread_spin_trylock.3c=pthread_spin_lock.3c
+f usr/share/man/man3c/pthread_spin_unlock.3c 0444 root bin
+f usr/share/man/man3c/pthread_testcancel.3c 0444 root bin
+f usr/share/man/man3c/ptrace.3c 0444 root bin
+f usr/share/man/man3c/ptsname.3c 0444 root bin
+s usr/share/man/man3c/putc.3c=fputc.3c
+s usr/share/man/man3c/putc_unlocked.3c=fputc.3c
+s usr/share/man/man3c/putchar.3c=fputc.3c
+s usr/share/man/man3c/putchar_unlocked.3c=fputc.3c
+f usr/share/man/man3c/putenv.3c 0444 root bin
+s usr/share/man/man3c/putmntent.3c=getmntent.3c
+f usr/share/man/man3c/putpwent.3c 0444 root bin
+f usr/share/man/man3c/puts.3c 0444 root bin
+f usr/share/man/man3c/putspent.3c 0444 root bin
+s usr/share/man/man3c/pututline.3c=getutent.3c
+s usr/share/man/man3c/pututxline.3c=getutxent.3c
+s usr/share/man/man3c/putw.3c=fputc.3c
+s usr/share/man/man3c/putwc.3c=fputwc.3c
+s usr/share/man/man3c/putwchar.3c=fputwc.3c
+f usr/share/man/man3c/putws.3c 0444 root bin
+s usr/share/man/man3c/qeconvert.3c=econvert.3c
+s usr/share/man/man3c/qfconvert.3c=econvert.3c
+s usr/share/man/man3c/qgconvert.3c=econvert.3c
+f usr/share/man/man3c/qsort.3c 0444 root bin
+s usr/share/man/man3c/qsort_r.3c=qsort.3c
+s usr/share/man/man3c/quadruple_to_decimal.3c=floating_to_decimal.3c
+f usr/share/man/man3c/quick_exit.3c 0444 root bin
+f usr/share/man/man3c/raise.3c 0444 root bin
+f usr/share/man/man3c/rand.3c 0444 root bin
+s usr/share/man/man3c/rand_r.3c=rand.3c
+f usr/share/man/man3c/random.3c 0444 root bin
+f usr/share/man/man3c/rctl_walk.3c 0444 root bin
+s usr/share/man/man3c/rctlblk_get_enforced_value.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_get_firing_time.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_get_global_action.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_get_global_flags.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_get_local_action.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_get_local_flags.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_get_privilege.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_get_recipient_pid.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_get_value.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_set_local_action.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_set_local_flags.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_set_privilege.3c=rctlblk_set_value.3c
+s usr/share/man/man3c/rctlblk_set_recipient_pid.3c=rctlblk_set_value.3c
+f usr/share/man/man3c/rctlblk_set_value.3c 0444 root bin
+s usr/share/man/man3c/rctlblk_size.3c=rctlblk_set_value.3c
+f usr/share/man/man3c/re_comp.3c 0444 root bin
+s usr/share/man/man3c/re_exec.3c=re_comp.3c
+f usr/share/man/man3c/readdir.3c 0444 root bin
+s usr/share/man/man3c/readdir_r.3c=readdir.3c
+s usr/share/man/man3c/realloc.3c=malloc.3c
+s usr/share/man/man3c/reallocarray.3c=malloc.3c
+s usr/share/man/man3c/reallocf.3c=malloc.3c
+f usr/share/man/man3c/realpath.3c 0444 root bin
+f usr/share/man/man3c/reboot.3c 0444 root bin
+s usr/share/man/man3c/recallocarray.3c=malloc.3c
+f usr/share/man/man3c/regcmp.3c 0444 root bin
+f usr/share/man/man3c/regcomp.3c 0444 root bin
+s usr/share/man/man3c/regerror.3c=regcomp.3c
+s usr/share/man/man3c/regex.3c=regcmp.3c
+s usr/share/man/man3c/regexec.3c=regcomp.3c
+s usr/share/man/man3c/regfree.3c=regcomp.3c
+f usr/share/man/man3c/remove.3c 0444 root bin
+s usr/share/man/man3c/remque.3c=insque.3c
+s usr/share/man/man3c/resetmnttab.3c=getmntent.3c
+f usr/share/man/man3c/rewind.3c 0444 root bin
+f usr/share/man/man3c/rewinddir.3c 0444 root bin
+s usr/share/man/man3c/rindex.3c=index.3c
+s usr/share/man/man3c/rw_rdlock.3c=rwlock.3c
+s usr/share/man/man3c/rw_tryrdlock.3c=rwlock.3c
+s usr/share/man/man3c/rw_trywrlock.3c=rwlock.3c
+s usr/share/man/man3c/rw_unlock.3c=rwlock.3c
+s usr/share/man/man3c/rw_wrlock.3c=rwlock.3c
+f usr/share/man/man3c/rwlock.3c 0444 root bin
+s usr/share/man/man3c/rwlock_destroy.3c=rwlock.3c
+s usr/share/man/man3c/rwlock_init.3c=rwlock.3c
+f usr/share/man/man3c/scandir.3c 0444 root bin
+f usr/share/man/man3c/scanf.3c 0444 root bin
+f usr/share/man/man3c/sched_get_priority_max.3c 0444 root bin
+s usr/share/man/man3c/sched_get_priority_min.3c=sched_get_priority_max.3c
+f usr/share/man/man3c/sched_getparam.3c 0444 root bin
+f usr/share/man/man3c/sched_getscheduler.3c 0444 root bin
+f usr/share/man/man3c/sched_rr_get_interval.3c 0444 root bin
+f usr/share/man/man3c/sched_setparam.3c 0444 root bin
+f usr/share/man/man3c/sched_setscheduler.3c 0444 root bin
+f usr/share/man/man3c/sched_yield.3c 0444 root bin
+s usr/share/man/man3c/schedctl_exit.3c=schedctl_init.3c
+f usr/share/man/man3c/schedctl_init.3c 0444 root bin
+s usr/share/man/man3c/schedctl_lookup.3c=schedctl_init.3c
+s usr/share/man/man3c/schedctl_start.3c=schedctl_init.3c
+s usr/share/man/man3c/schedctl_stop.3c=schedctl_init.3c
+s usr/share/man/man3c/seconvert.3c=econvert.3c
+s usr/share/man/man3c/seed48.3c=drand48.3c
+f usr/share/man/man3c/seekdir.3c 0444 root bin
+f usr/share/man/man3c/select.3c 0444 root bin
+f usr/share/man/man3c/sem_close.3c 0444 root bin
+f usr/share/man/man3c/sem_destroy.3c 0444 root bin
+f usr/share/man/man3c/sem_getvalue.3c 0444 root bin
+f usr/share/man/man3c/sem_init.3c 0444 root bin
+f usr/share/man/man3c/sem_open.3c 0444 root bin
+f usr/share/man/man3c/sem_post.3c 0444 root bin
+s usr/share/man/man3c/sem_reltimedwait_np.3c=sem_timedwait.3c
+f usr/share/man/man3c/sem_timedwait.3c 0444 root bin
+s usr/share/man/man3c/sem_trywait.3c=sem_wait.3c
+f usr/share/man/man3c/sem_unlink.3c 0444 root bin
+f usr/share/man/man3c/sem_wait.3c 0444 root bin
+s usr/share/man/man3c/sema_destroy.3c=semaphore.3c
+s usr/share/man/man3c/sema_init.3c=semaphore.3c
+s usr/share/man/man3c/sema_post.3c=semaphore.3c
+s usr/share/man/man3c/sema_trywait.3c=semaphore.3c
+s usr/share/man/man3c/sema_wait.3c=semaphore.3c
+f usr/share/man/man3c/semaphore.3c 0444 root bin
+f usr/share/man/man3c/set_constraint_handler_s.3c 0444 root bin
+s usr/share/man/man3c/setattrat.3c=fgetattr.3c
+f usr/share/man/man3c/setbuf.3c 0444 root bin
+f usr/share/man/man3c/setbuffer.3c 0444 root bin
+f usr/share/man/man3c/setcat.3c 0444 root bin
+f usr/share/man/man3c/setenv.3c 0444 root bin
+s usr/share/man/man3c/setgrent.3c=getgrnam.3c
+s usr/share/man/man3c/sethostname.3c=gethostname.3c
+f usr/share/man/man3c/setjmp.3c 0444 root bin
+f usr/share/man/man3c/setkey.3c 0444 root bin
+f usr/share/man/man3c/setlabel.3c 0444 root bin
+s usr/share/man/man3c/setlinebuf.3c=setbuffer.3c
+f usr/share/man/man3c/setlocale.3c 0444 root bin
+s usr/share/man/man3c/setlogmask.3c=syslog.3c
+s usr/share/man/man3c/setnetgrent.3c=getnetgrent.3c
+s usr/share/man/man3c/setpriority.3c=getpriority.3c
+s usr/share/man/man3c/setprogname.3c=getprogname.3c
+s usr/share/man/man3c/setpwent.3c=getpwnam.3c
+s usr/share/man/man3c/setspent.3c=getspnam.3c
+s usr/share/man/man3c/setstate.3c=random.3c
+s usr/share/man/man3c/settimeofday.3c=gettimeofday.3c
+s usr/share/man/man3c/setusershell.3c=getusershell.3c
+s usr/share/man/man3c/setutent.3c=getutent.3c
+s usr/share/man/man3c/setutxent.3c=getutxent.3c
+s usr/share/man/man3c/setvbuf.3c=setbuf.3c
+s usr/share/man/man3c/sfconvert.3c=econvert.3c
+s usr/share/man/man3c/sgconvert.3c=econvert.3c
+f usr/share/man/man3c/shm_open.3c 0444 root bin
+f usr/share/man/man3c/shm_unlink.3c 0444 root bin
+s usr/share/man/man3c/sig2str.3c=str2sig.3c
+s usr/share/man/man3c/sigaddset.3c=sigsetops.3c
+s usr/share/man/man3c/sigdelset.3c=sigsetops.3c
+s usr/share/man/man3c/sigemptyset.3c=sigsetops.3c
+s usr/share/man/man3c/sigfillset.3c=sigsetops.3c
+f usr/share/man/man3c/sigfpe.3c 0444 root bin
+s usr/share/man/man3c/sighold.3c=signal.3c
+s usr/share/man/man3c/sigignore.3c=signal.3c
+f usr/share/man/man3c/siginterrupt.3c 0444 root bin
+s usr/share/man/man3c/sigismember.3c=sigsetops.3c
+s usr/share/man/man3c/siglongjmp.3c=setjmp.3c
+f usr/share/man/man3c/signal.3c 0444 root bin
+f usr/share/man/man3c/signalfd.3c 0444 root bin
+s usr/share/man/man3c/sigpause.3c=signal.3c
+f usr/share/man/man3c/sigqueue.3c 0444 root bin
+s usr/share/man/man3c/sigrelse.3c=signal.3c
+s usr/share/man/man3c/sigset.3c=signal.3c
+s usr/share/man/man3c/sigsetjmp.3c=setjmp.3c
+f usr/share/man/man3c/sigsetops.3c 0444 root bin
+f usr/share/man/man3c/sigstack.3c 0444 root bin
+s usr/share/man/man3c/sigtimedwait.3c=sigwaitinfo.3c
+f usr/share/man/man3c/sigwaitinfo.3c 0444 root bin
+s usr/share/man/man3c/single_to_decimal.3c=floating_to_decimal.3c
+f usr/share/man/man3c/sleep.3c 0444 root bin
+f usr/share/man/man3c/smt_pause.3c 0444 root bin
+s usr/share/man/man3c/snprintf.3c=printf.3c
+s usr/share/man/man3c/sprintf.3c=printf.3c
+s usr/share/man/man3c/srand.3c=rand.3c
+s usr/share/man/man3c/srand48.3c=drand48.3c
+s usr/share/man/man3c/srandom.3c=random.3c
+s usr/share/man/man3c/sscanf.3c=scanf.3c
+f usr/share/man/man3c/ssignal.3c 0444 root bin
+f usr/share/man/man3c/stack_getbounds.3c 0444 root bin
+f usr/share/man/man3c/stack_inbounds.3c 0444 root bin
+f usr/share/man/man3c/stack_setbounds.3c 0444 root bin
+f usr/share/man/man3c/stack_violation.3c 0444 root bin
+s usr/share/man/man3c/stderr.3c=stdio.3c
+s usr/share/man/man3c/stdin.3c=stdio.3c
+f usr/share/man/man3c/stdio.3c 0444 root bin
+s usr/share/man/man3c/stdout.3c=stdio.3c
+s usr/share/man/man3c/stpcpy.3c=string.3c
+s usr/share/man/man3c/stpncpy.3c=string.3c
+f usr/share/man/man3c/str2sig.3c 0444 root bin
+s usr/share/man/man3c/strcasecmp.3c=string.3c
+s usr/share/man/man3c/strcasecmp_l.3c=string.3c
+s usr/share/man/man3c/strcasestr.3c=string.3c
+s usr/share/man/man3c/strcasestr_l.3c=string.3c
+s usr/share/man/man3c/strcat.3c=string.3c
+s usr/share/man/man3c/strchr.3c=string.3c
+s usr/share/man/man3c/strchrnul.3c=string.3c
+s usr/share/man/man3c/strcmp.3c=string.3c
+f usr/share/man/man3c/strcoll.3c 0444 root bin
+s usr/share/man/man3c/strcoll_l.3c=strcoll.3c
+s usr/share/man/man3c/strcpy.3c=string.3c
+s usr/share/man/man3c/strcspn.3c=string.3c
+s usr/share/man/man3c/strdup.3c=string.3c
+s usr/share/man/man3c/strdupa.3c=string.3c
+f usr/share/man/man3c/strerror.3c 0444 root bin
+s usr/share/man/man3c/strerror_l.3c=strerror.3c
+s usr/share/man/man3c/strerror_r.3c=strerror.3c
+f usr/share/man/man3c/strfmon.3c 0444 root bin
+s usr/share/man/man3c/strfmon_l.3c=strfmon.3c
+f usr/share/man/man3c/strftime.3c 0444 root bin
+s usr/share/man/man3c/strftime_l.3c=strftime.3c
+f usr/share/man/man3c/string.3c 0444 root bin
+f usr/share/man/man3c/string_to_decimal.3c 0444 root bin
+s usr/share/man/man3c/strlcat.3c=string.3c
+s usr/share/man/man3c/strlcpy.3c=string.3c
+s usr/share/man/man3c/strlen.3c=string.3c
+s usr/share/man/man3c/strncasecmp.3c=string.3c
+s usr/share/man/man3c/strncasecmp_l.3c=string.3c
+s usr/share/man/man3c/strncat.3c=string.3c
+s usr/share/man/man3c/strncmp.3c=string.3c
+s usr/share/man/man3c/strncpy.3c=string.3c
+s usr/share/man/man3c/strndup.3c=string.3c
+s usr/share/man/man3c/strndupa.3c=string.3c
+s usr/share/man/man3c/strnlen.3c=string.3c
+s usr/share/man/man3c/strnstr.3c=string.3c
+s usr/share/man/man3c/strpbrk.3c=string.3c
+f usr/share/man/man3c/strptime.3c 0444 root bin
+s usr/share/man/man3c/strptime_l.3c=strptime.3c
+s usr/share/man/man3c/strrchr.3c=string.3c
+s usr/share/man/man3c/strsep.3c=string.3c
+f usr/share/man/man3c/strsignal.3c 0444 root bin
+s usr/share/man/man3c/strspn.3c=string.3c
+s usr/share/man/man3c/strstr.3c=string.3c
+f usr/share/man/man3c/strtod.3c 0444 root bin
+s usr/share/man/man3c/strtof.3c=strtod.3c
+f usr/share/man/man3c/strtoimax.3c 0444 root bin
+s usr/share/man/man3c/strtok.3c=string.3c
+s usr/share/man/man3c/strtok_r.3c=string.3c
+f usr/share/man/man3c/strtol.3c 0444 root bin
+s usr/share/man/man3c/strtold.3c=strtod.3c
+s usr/share/man/man3c/strtoll.3c=strtol.3c
+f usr/share/man/man3c/strtonum.3c 0444 root bin
+f usr/share/man/man3c/strtoul.3c 0444 root bin
+s usr/share/man/man3c/strtoull.3c=strtoul.3c
+s usr/share/man/man3c/strtoumax.3c=strtoimax.3c
+f usr/share/man/man3c/strtows.3c 0444 root bin
+f usr/share/man/man3c/strxfrm.3c 0444 root bin
+s usr/share/man/man3c/strxfrm_l.3c=strxfrm.3c
+f usr/share/man/man3c/swab.3c 0444 root bin
+s usr/share/man/man3c/swapcontext.3c=makecontext.3c
+s usr/share/man/man3c/swprintf.3c=fwprintf.3c
+s usr/share/man/man3c/swscanf.3c=fwscanf.3c
+f usr/share/man/man3c/sync_instruction_memory.3c 0444 root bin
+f usr/share/man/man3c/sysconf.3c 0444 root bin
+f usr/share/man/man3c/syslog.3c 0444 root bin
+f usr/share/man/man3c/system.3c 0444 root bin
+f usr/share/man/man3c/tcdrain.3c 0444 root bin
+f usr/share/man/man3c/tcflow.3c 0444 root bin
+f usr/share/man/man3c/tcflush.3c 0444 root bin
+f usr/share/man/man3c/tcgetattr.3c 0444 root bin
+f usr/share/man/man3c/tcgetpgrp.3c 0444 root bin
+f usr/share/man/man3c/tcgetsid.3c 0444 root bin
+f usr/share/man/man3c/tcsendbreak.3c 0444 root bin
+f usr/share/man/man3c/tcsetattr.3c 0444 root bin
+f usr/share/man/man3c/tcsetpgrp.3c 0444 root bin
+s usr/share/man/man3c/tdelete.3c=tsearch.3c
+f usr/share/man/man3c/tell.3c 0444 root bin
+f usr/share/man/man3c/telldir.3c 0444 root bin
+s usr/share/man/man3c/tempnam.3c=tmpnam.3c
+f usr/share/man/man3c/termios.3c 0444 root bin
+s usr/share/man/man3c/textdomain.3c=gettext.3c
+s usr/share/man/man3c/tfind.3c=tsearch.3c
+s usr/share/man/man3c/thr_continue.3c=thr_suspend.3c
+f usr/share/man/man3c/thr_create.3c 0444 root bin
+f usr/share/man/man3c/thr_exit.3c 0444 root bin
+f usr/share/man/man3c/thr_getconcurrency.3c 0444 root bin
+f usr/share/man/man3c/thr_getname.3c 0444 root bin
+f usr/share/man/man3c/thr_getprio.3c 0444 root bin
+s usr/share/man/man3c/thr_getspecific.3c=thr_keycreate.3c
+f usr/share/man/man3c/thr_join.3c 0444 root bin
+f usr/share/man/man3c/thr_keycreate.3c 0444 root bin
+s usr/share/man/man3c/thr_keycreate_once.3c=thr_keycreate.3c
+f usr/share/man/man3c/thr_kill.3c 0444 root bin
+f usr/share/man/man3c/thr_main.3c 0444 root bin
+f usr/share/man/man3c/thr_min_stack.3c 0444 root bin
+f usr/share/man/man3c/thr_self.3c 0444 root bin
+s usr/share/man/man3c/thr_setconcurrency.3c=thr_getconcurrency.3c
+s usr/share/man/man3c/thr_setname.3c=thr_getname.3c
+s usr/share/man/man3c/thr_setprio.3c=thr_getprio.3c
+s usr/share/man/man3c/thr_setspecific.3c=thr_keycreate.3c
+f usr/share/man/man3c/thr_sigsetmask.3c 0444 root bin
+f usr/share/man/man3c/thr_stksegment.3c 0444 root bin
+f usr/share/man/man3c/thr_suspend.3c 0444 root bin
+f usr/share/man/man3c/thr_yield.3c 0444 root bin
+f usr/share/man/man3c/thrd_create.3c 0444 root bin
+f usr/share/man/man3c/thrd_current.3c 0444 root bin
+f usr/share/man/man3c/thrd_detach.3c 0444 root bin
+f usr/share/man/man3c/thrd_equal.3c 0444 root bin
+f usr/share/man/man3c/thrd_exit.3c 0444 root bin
+f usr/share/man/man3c/thrd_join.3c 0444 root bin
+s usr/share/man/man3c/thrd_sleep.3c=nanosleep.3c
+f usr/share/man/man3c/thrd_yield.3c 0444 root bin
+s usr/share/man/man3c/timegm.3c=mktime.3c
+f usr/share/man/man3c/timer_create.3c 0444 root bin
+f usr/share/man/man3c/timer_delete.3c 0444 root bin
+s usr/share/man/man3c/timer_getoverrun.3c=timer_settime.3c
+s usr/share/man/man3c/timer_gettime.3c=timer_settime.3c
+f usr/share/man/man3c/timer_settime.3c 0444 root bin
+f usr/share/man/man3c/timeradd.3c 0444 root bin
+s usr/share/man/man3c/timerclear.3c=timeradd.3c
+s usr/share/man/man3c/timercmp.3c=timeradd.3c
+f usr/share/man/man3c/timerfd_create.3c 0444 root bin
+s usr/share/man/man3c/timerfd_gettime.3c=timerfd_create.3c
+s usr/share/man/man3c/timerfd_settime.3c=timerfd_create.3c
+s usr/share/man/man3c/timerisset.3c=timeradd.3c
+s usr/share/man/man3c/timersub.3c=timeradd.3c
+f usr/share/man/man3c/timespec_get.3c 0444 root bin
+f usr/share/man/man3c/tmpfile.3c 0444 root bin
+f usr/share/man/man3c/tmpnam.3c 0444 root bin
+s usr/share/man/man3c/tmpnam_r.3c=tmpnam.3c
+f usr/share/man/man3c/toascii.3c 0444 root bin
+f usr/share/man/man3c/tolower.3c 0444 root bin
+s usr/share/man/man3c/tolower_l.3c=tolower.3c
+f usr/share/man/man3c/toupper.3c 0444 root bin
+s usr/share/man/man3c/toupper_l.3c=toupper.3c
+s usr/share/man/man3c/towctrans.3c=wctrans.3c
+s usr/share/man/man3c/towctrans_l.3c=wctrans.3c
+f usr/share/man/man3c/towlower.3c 0444 root bin
+s usr/share/man/man3c/towlower_l.3c=towlower.3c
+f usr/share/man/man3c/towupper.3c 0444 root bin
+s usr/share/man/man3c/towupper_l.3c=towupper.3c
+f usr/share/man/man3c/truncate.3c 0444 root bin
+f usr/share/man/man3c/tsearch.3c 0444 root bin
+f usr/share/man/man3c/tss.3c 0444 root bin
+s usr/share/man/man3c/tss_create.3c=tss.3c
+s usr/share/man/man3c/tss_delete.3c=tss.3c
+s usr/share/man/man3c/tss_get.3c=tss.3c
+s usr/share/man/man3c/tss_set.3c=tss.3c
+f usr/share/man/man3c/ttyname.3c 0444 root bin
+s usr/share/man/man3c/ttyname_r.3c=ttyname.3c
+f usr/share/man/man3c/ttyslot.3c 0444 root bin
+s usr/share/man/man3c/twalk.3c=tsearch.3c
+s usr/share/man/man3c/tzset.3c=ctime.3c
+f usr/share/man/man3c/u8_strcmp.3c 0444 root bin
+f usr/share/man/man3c/u8_textprep_str.3c 0444 root bin
+f usr/share/man/man3c/u8_validate.3c 0444 root bin
+f usr/share/man/man3c/ualarm.3c 0444 root bin
+f usr/share/man/man3c/uconv_u16tou32.3c 0444 root bin
+s usr/share/man/man3c/uconv_u16tou8.3c=uconv_u16tou32.3c
+s usr/share/man/man3c/uconv_u32tou16.3c=uconv_u16tou32.3c
+s usr/share/man/man3c/uconv_u32tou8.3c=uconv_u16tou32.3c
+s usr/share/man/man3c/uconv_u8tou16.3c=uconv_u16tou32.3c
+s usr/share/man/man3c/uconv_u8tou32.3c=uconv_u16tou32.3c
+f usr/share/man/man3c/ucred.3c 0444 root bin
+s usr/share/man/man3c/ucred_free.3c=ucred.3c
+s usr/share/man/man3c/ucred_get.3c=ucred.3c
+s usr/share/man/man3c/ucred_getegid.3c=ucred.3c
+s usr/share/man/man3c/ucred_geteuid.3c=ucred.3c
+s usr/share/man/man3c/ucred_getgroups.3c=ucred.3c
+s usr/share/man/man3c/ucred_getlabel.3c=ucred.3c
+s usr/share/man/man3c/ucred_getpflags.3c=ucred.3c
+s usr/share/man/man3c/ucred_getpid.3c=ucred.3c
+s usr/share/man/man3c/ucred_getprivset.3c=ucred.3c
+s usr/share/man/man3c/ucred_getprojid.3c=ucred.3c
+s usr/share/man/man3c/ucred_getrgid.3c=ucred.3c
+s usr/share/man/man3c/ucred_getruid.3c=ucred.3c
+s usr/share/man/man3c/ucred_getsgid.3c=ucred.3c
+s usr/share/man/man3c/ucred_getsuid.3c=ucred.3c
+s usr/share/man/man3c/ucred_getzoneid.3c=ucred.3c
+s usr/share/man/man3c/ucred_size.3c=ucred.3c
+s usr/share/man/man3c/ulckpwdf.3c=lckpwdf.3c
+s usr/share/man/man3c/ulltostr.3c=strtol.3c
+f usr/share/man/man3c/ungetc.3c 0444 root bin
+f usr/share/man/man3c/ungetwc.3c 0444 root bin
+f usr/share/man/man3c/unlockpt.3c 0444 root bin
+s usr/share/man/man3c/unordered.3c=isnand.3c
+f usr/share/man/man3c/unsetenv.3c 0444 root bin
+s usr/share/man/man3c/updwtmp.3c=getutxent.3c
+s usr/share/man/man3c/updwtmpx.3c=getutxent.3c
+f usr/share/man/man3c/uselocale.3c 0444 root bin
+f usr/share/man/man3c/usleep.3c 0444 root bin
+s usr/share/man/man3c/utmpname.3c=getutent.3c
+s usr/share/man/man3c/utmpxname.3c=getutxent.3c
+s usr/share/man/man3c/valloc.3c=malloc.3c
+s usr/share/man/man3c/vasprintf.3c=vprintf.3c
+s usr/share/man/man3c/verr.3c=err.3c
+s usr/share/man/man3c/verrc.3c=err.3c
+s usr/share/man/man3c/verrx.3c=err.3c
+s usr/share/man/man3c/vfprintf.3c=vprintf.3c
+s usr/share/man/man3c/vfscanf.3c=scanf.3c
+f usr/share/man/man3c/vfwprintf.3c 0444 root bin
+s usr/share/man/man3c/vfwscanf.3c=fwscanf.3c
+f usr/share/man/man3c/vlfmt.3c 0444 root bin
+f usr/share/man/man3c/vpfmt.3c 0444 root bin
+f usr/share/man/man3c/vprintf.3c 0444 root bin
+s usr/share/man/man3c/vscanf.3c=scanf.3c
+s usr/share/man/man3c/vsnprintf.3c=vprintf.3c
+s usr/share/man/man3c/vsprintf.3c=vprintf.3c
+s usr/share/man/man3c/vsscanf.3c=scanf.3c
+s usr/share/man/man3c/vswprintf.3c=vfwprintf.3c
+s usr/share/man/man3c/vswscanf.3c=fwscanf.3c
+f usr/share/man/man3c/vsyslog.3c 0444 root bin
+s usr/share/man/man3c/vwarn.3c=err.3c
+s usr/share/man/man3c/vwarnc.3c=err.3c
+s usr/share/man/man3c/vwarnx.3c=err.3c
+s usr/share/man/man3c/vwprintf.3c=vfwprintf.3c
+s usr/share/man/man3c/vwscanf.3c=fwscanf.3c
+f usr/share/man/man3c/wait.3c 0444 root bin
+f usr/share/man/man3c/wait3.3c 0444 root bin
+s usr/share/man/man3c/wait4.3c=wait3.3c
+f usr/share/man/man3c/waitpid.3c 0444 root bin
+f usr/share/man/man3c/walkcontext.3c 0444 root bin
+s usr/share/man/man3c/warn.3c=err.3c
+s usr/share/man/man3c/warnc.3c=err.3c
+s usr/share/man/man3c/warnx.3c=err.3c
+s usr/share/man/man3c/watof.3c=wcstod.3c
+s usr/share/man/man3c/watoi.3c=wcstol.3c
+s usr/share/man/man3c/watol.3c=wcstol.3c
+s usr/share/man/man3c/watoll.3c=wcstol.3c
+f usr/share/man/man3c/wcpcpy.3c 0444 root bin
+s usr/share/man/man3c/wcpncpy.3c=wcpcpy.3c
+s usr/share/man/man3c/wcrtomb.3c=c16rtomb.3c
+s usr/share/man/man3c/wcrtomb_l.3c=c16rtomb.3c
+f usr/share/man/man3c/wcscasecmp.3c 0444 root bin
+s usr/share/man/man3c/wcscasecmp_l.3c=wcscasecmp.3c
+s usr/share/man/man3c/wcscat.3c=wcstring.3c
+s usr/share/man/man3c/wcschr.3c=wcstring.3c
+s usr/share/man/man3c/wcscmp.3c=wcstring.3c
+f usr/share/man/man3c/wcscoll.3c 0444 root bin
+s usr/share/man/man3c/wcscoll_l.3c=wcscoll.3c
+s usr/share/man/man3c/wcscpy.3c=wcstring.3c
+s usr/share/man/man3c/wcscspn.3c=wcstring.3c
+f usr/share/man/man3c/wcsdup.3c 0444 root bin
+s usr/share/man/man3c/wcsetno.3c=cset.3c
+f usr/share/man/man3c/wcsftime.3c 0444 root bin
+f usr/share/man/man3c/wcslen.3c 0444 root bin
+s usr/share/man/man3c/wcsncasecmp.3c=wcscasecmp.3c
+s usr/share/man/man3c/wcsncasecmp_l.3c=wcscasecmp.3c
+s usr/share/man/man3c/wcsncat.3c=wcstring.3c
+s usr/share/man/man3c/wcsncmp.3c=wcstring.3c
+s usr/share/man/man3c/wcsncpy.3c=wcstring.3c
+s usr/share/man/man3c/wcsnlen.3c=wcslen.3c
+s usr/share/man/man3c/wcsnrtombs.3c=wcsrtombs.3c
+s usr/share/man/man3c/wcsnrtombs_l.3c=wcsrtombs.3c
+s usr/share/man/man3c/wcspbrk.3c=wcstring.3c
+s usr/share/man/man3c/wcsrchr.3c=wcstring.3c
+f usr/share/man/man3c/wcsrtombs.3c 0444 root bin
+s usr/share/man/man3c/wcsrtombs_l.3c=wcsrtombs.3c
+s usr/share/man/man3c/wcsspn.3c=wcstring.3c
+f usr/share/man/man3c/wcsstr.3c 0444 root bin
+f usr/share/man/man3c/wcstod.3c 0444 root bin
+s usr/share/man/man3c/wcstof.3c=wcstod.3c
+f usr/share/man/man3c/wcstoimax.3c 0444 root bin
+s usr/share/man/man3c/wcstok.3c=wcstring.3c
+f usr/share/man/man3c/wcstol.3c 0444 root bin
+s usr/share/man/man3c/wcstold.3c=wcstod.3c
+s usr/share/man/man3c/wcstoll.3c=wcstol.3c
+f usr/share/man/man3c/wcstombs.3c 0444 root bin
+s usr/share/man/man3c/wcstombs_l.3c=wcstombs.3c
+f usr/share/man/man3c/wcstoul.3c 0444 root bin
+s usr/share/man/man3c/wcstoull.3c=wcstoul.3c
+s usr/share/man/man3c/wcstoumax.3c=wcstoimax.3c
+f usr/share/man/man3c/wcstring.3c 0444 root bin
+s usr/share/man/man3c/wcswcs.3c=wcstring.3c
+f usr/share/man/man3c/wcswidth.3c 0444 root bin
+s usr/share/man/man3c/wcswidth_l.3c=wcswidth.3c
+f usr/share/man/man3c/wcsxfrm.3c 0444 root bin
+f usr/share/man/man3c/wctob.3c 0444 root bin
+s usr/share/man/man3c/wctob_l.3c=wctob.3c
+f usr/share/man/man3c/wctomb.3c 0444 root bin
+s usr/share/man/man3c/wctomb_l.3c=wctomb.3c
+f usr/share/man/man3c/wctrans.3c 0444 root bin
+s usr/share/man/man3c/wctrans_l.3c=wctrans.3c
+f usr/share/man/man3c/wctype.3c 0444 root bin
+s usr/share/man/man3c/wctype_l.3c=wctype.3c
+f usr/share/man/man3c/wcwidth.3c 0444 root bin
+s usr/share/man/man3c/wcwidth_l.3c=wcwidth.3c
+s usr/share/man/man3c/windex.3c=wcstring.3c
+f usr/share/man/man3c/wmemchr.3c 0444 root bin
+f usr/share/man/man3c/wmemcmp.3c 0444 root bin
+f usr/share/man/man3c/wmemcpy.3c 0444 root bin
+f usr/share/man/man3c/wmemmove.3c 0444 root bin
+f usr/share/man/man3c/wmemset.3c 0444 root bin
+f usr/share/man/man3c/wordexp.3c 0444 root bin
+s usr/share/man/man3c/wordfree.3c=wordexp.3c
+s usr/share/man/man3c/wprintf.3c=fwprintf.3c
+s usr/share/man/man3c/wrindex.3c=wcstring.3c
+s usr/share/man/man3c/wscanf.3c=fwscanf.3c
+s usr/share/man/man3c/wscasecmp.3c=wstring.3c
+s usr/share/man/man3c/wscat.3c=wcstring.3c
+s usr/share/man/man3c/wschr.3c=wcstring.3c
+s usr/share/man/man3c/wscmp.3c=wcstring.3c
+s usr/share/man/man3c/wscol.3c=wstring.3c
+s usr/share/man/man3c/wscoll.3c=wcscoll.3c
+s usr/share/man/man3c/wscpy.3c=wcstring.3c
+s usr/share/man/man3c/wscspn.3c=wcstring.3c
+s usr/share/man/man3c/wsdup.3c=wstring.3c
+s usr/share/man/man3c/wslen.3c=wcstring.3c
+s usr/share/man/man3c/wsncasecmp.3c=wstring.3c
+s usr/share/man/man3c/wsncat.3c=wcstring.3c
+s usr/share/man/man3c/wsncmp.3c=wcstring.3c
+s usr/share/man/man3c/wsncpy.3c=wcstring.3c
+s usr/share/man/man3c/wspbrk.3c=wcstring.3c
+f usr/share/man/man3c/wsprintf.3c 0444 root bin
+s usr/share/man/man3c/wsrchr.3c=wcstring.3c
+f usr/share/man/man3c/wsscanf.3c 0444 root bin
+s usr/share/man/man3c/wsspn.3c=wcstring.3c
+s usr/share/man/man3c/wstod.3c=wcstod.3c
+s usr/share/man/man3c/wstok.3c=wcstring.3c
+s usr/share/man/man3c/wstol.3c=wcstol.3c
+s usr/share/man/man3c/wstostr.3c=strtows.3c
+f usr/share/man/man3c/wstring.3c 0444 root bin
+s usr/share/man/man3c/wsxfrm.3c=wcsxfrm.3c
+d usr/share/man/man3c_db 0755 root bin
+s usr/share/man/man3c_db/td_event_addset.3c_db=td_ta_event_addr.3c_db
+s usr/share/man/man3c_db/td_event_delset.3c_db=td_ta_event_addr.3c_db
+s usr/share/man/man3c_db/td_event_emptyset.3c_db=td_ta_event_addr.3c_db
+s usr/share/man/man3c_db/td_event_fillset.3c_db=td_ta_event_addr.3c_db
+s usr/share/man/man3c_db/td_eventisempty.3c_db=td_ta_event_addr.3c_db
+s usr/share/man/man3c_db/td_eventismember.3c_db=td_ta_event_addr.3c_db
+f usr/share/man/man3c_db/td_init.3c_db 0444 root bin
+f usr/share/man/man3c_db/td_log.3c_db 0444 root bin
+f usr/share/man/man3c_db/td_sync_get_info.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_sync_get_stats.3c_db=td_sync_get_info.3c_db
+s usr/share/man/man3c_db/td_sync_setstate.3c_db=td_sync_get_info.3c_db
+s usr/share/man/man3c_db/td_sync_waiters.3c_db=td_sync_get_info.3c_db
+s usr/share/man/man3c_db/td_ta_clear_event.3c_db=td_ta_event_addr.3c_db
+s usr/share/man/man3c_db/td_ta_delete.3c_db=td_ta_new.3c_db
+f usr/share/man/man3c_db/td_ta_enable_stats.3c_db 0444 root bin
+f usr/share/man/man3c_db/td_ta_event_addr.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_ta_event_getmsg.3c_db=td_ta_event_addr.3c_db
+f usr/share/man/man3c_db/td_ta_get_nthreads.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_ta_get_ph.3c_db=td_ta_new.3c_db
+s usr/share/man/man3c_db/td_ta_get_stats.3c_db=td_ta_enable_stats.3c_db
+f usr/share/man/man3c_db/td_ta_map_addr2sync.3c_db 0444 root bin
+f usr/share/man/man3c_db/td_ta_map_id2thr.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_ta_map_lwp2thr.3c_db=td_ta_map_id2thr.3c_db
+f usr/share/man/man3c_db/td_ta_new.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_ta_reset_stats.3c_db=td_ta_enable_stats.3c_db
+s usr/share/man/man3c_db/td_ta_set_event.3c_db=td_ta_event_addr.3c_db
+f usr/share/man/man3c_db/td_ta_setconcurrency.3c_db 0444 root bin
+f usr/share/man/man3c_db/td_ta_sync_iter.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_ta_sync_tracking_enable.3c_db=td_sync_get_info.3c_db
+s usr/share/man/man3c_db/td_ta_thr_iter.3c_db=td_ta_sync_iter.3c_db
+s usr/share/man/man3c_db/td_ta_tsd_iter.3c_db=td_ta_sync_iter.3c_db
+s usr/share/man/man3c_db/td_thr_clear_event.3c_db=td_ta_event_addr.3c_db
+s usr/share/man/man3c_db/td_thr_dbresume.3c_db=td_thr_dbsuspend.3c_db
+f usr/share/man/man3c_db/td_thr_dbsuspend.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_thr_event_enable.3c_db=td_ta_event_addr.3c_db
+s usr/share/man/man3c_db/td_thr_event_getmsg.3c_db=td_ta_event_addr.3c_db
+f usr/share/man/man3c_db/td_thr_get_info.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_thr_getfpregs.3c_db=td_thr_getgregs.3c_db
+f usr/share/man/man3c_db/td_thr_getgregs.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_thr_getxregs.3c_db=td_thr_getgregs.3c_db
+s usr/share/man/man3c_db/td_thr_getxregsize.3c_db=td_thr_getgregs.3c_db
+f usr/share/man/man3c_db/td_thr_lockowner.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_thr_set_event.3c_db=td_ta_event_addr.3c_db
+s usr/share/man/man3c_db/td_thr_setfpregs.3c_db=td_thr_getgregs.3c_db
+s usr/share/man/man3c_db/td_thr_setgregs.3c_db=td_thr_getgregs.3c_db
+f usr/share/man/man3c_db/td_thr_setprio.3c_db 0444 root bin
+f usr/share/man/man3c_db/td_thr_setsigpending.3c_db 0444 root bin
+s usr/share/man/man3c_db/td_thr_setxregs.3c_db=td_thr_getgregs.3c_db
+s usr/share/man/man3c_db/td_thr_sigsetmask.3c_db=td_thr_setsigpending.3c_db
+f usr/share/man/man3c_db/td_thr_sleepinfo.3c_db 0444 root bin
+f usr/share/man/man3c_db/td_thr_tsd.3c_db 0444 root bin
+f usr/share/man/man3c_db/td_thr_validate.3c_db 0444 root bin
+d usr/share/man/man3cfgadm 0755 root bin
+f usr/share/man/man3cfgadm/config_admin.3cfgadm 0444 root bin
+s usr/share/man/man3cfgadm/config_ap_id_cmp.3cfgadm=config_admin.3cfgadm
+s usr/share/man/man3cfgadm/config_change_state.3cfgadm=config_admin.3cfgadm
+s usr/share/man/man3cfgadm/config_list.3cfgadm=config_admin.3cfgadm
+s usr/share/man/man3cfgadm/config_list_ext.3cfgadm=config_admin.3cfgadm
+s usr/share/man/man3cfgadm/config_private_func.3cfgadm=config_admin.3cfgadm
+s usr/share/man/man3cfgadm/config_stat.3cfgadm=config_admin.3cfgadm
+s usr/share/man/man3cfgadm/config_strerror.3cfgadm=config_admin.3cfgadm
+s usr/share/man/man3cfgadm/config_test.3cfgadm=config_admin.3cfgadm
+s usr/share/man/man3cfgadm/config_unload_libs.3cfgadm=config_admin.3cfgadm
+d usr/share/man/man3commputil 0755 root bin
+s usr/share/man/man3commputil/sdp_add_attribute.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_bandwidth.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_connection.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_email.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_information.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_key.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_media.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_name.3commputil=sdp_add_origin.3commputil
+f usr/share/man/man3commputil/sdp_add_origin.3commputil 0444 root bin
+s usr/share/man/man3commputil/sdp_add_phone.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_repeat.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_time.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_uri.3commputil=sdp_add_origin.3commputil
+s usr/share/man/man3commputil/sdp_add_zone.3commputil=sdp_add_origin.3commputil
+f usr/share/man/man3commputil/sdp_clone_session.3commputil 0444 root bin
+f usr/share/man/man3commputil/sdp_delete_all_field.3commputil 0444 root bin
+s usr/share/man/man3commputil/sdp_delete_all_media_field.3commputil=sdp_delete_all_field.3commputil
+s usr/share/man/man3commputil/sdp_delete_attribute.3commputil=sdp_delete_media.3commputil
+f usr/share/man/man3commputil/sdp_delete_media.3commputil 0444 root bin
+f usr/share/man/man3commputil/sdp_find_attribute.3commputil 0444 root bin
+f usr/share/man/man3commputil/sdp_find_media.3commputil 0444 root bin
+f usr/share/man/man3commputil/sdp_find_media_rtpmap.3commputil 0444 root bin
+s usr/share/man/man3commputil/sdp_free_session.3commputil=sdp_new_session.3commputil
+f usr/share/man/man3commputil/sdp_new_session.3commputil 0444 root bin
+f usr/share/man/man3commputil/sdp_parse.3commputil 0444 root bin
+f usr/share/man/man3commputil/sdp_session_to_str.3commputil 0444 root bin
+d usr/share/man/man3contract 0755 root bin
+s usr/share/man/man3contract/ct_ctl_abandon.3contract=ct_ctl_adopt.3contract
+s usr/share/man/man3contract/ct_ctl_ack.3contract=ct_ctl_adopt.3contract
+f usr/share/man/man3contract/ct_ctl_adopt.3contract 0444 root bin
+s usr/share/man/man3contract/ct_ctl_nack.3contract=ct_ctl_adopt.3contract
+s usr/share/man/man3contract/ct_ctl_newct.3contract=ct_ctl_adopt.3contract
+s usr/share/man/man3contract/ct_ctl_qack.3contract=ct_ctl_adopt.3contract
+s usr/share/man/man3contract/ct_dev_status_get_aset.3contract=ct_dev_status_get_dev_state.3contract
+f usr/share/man/man3contract/ct_dev_status_get_dev_state.3contract 0444 root bin
+s usr/share/man/man3contract/ct_dev_status_get_minor.3contract=ct_dev_status_get_dev_state.3contract
+s usr/share/man/man3contract/ct_dev_status_get_noneg.3contract=ct_dev_status_get_dev_state.3contract
+s usr/share/man/man3contract/ct_dev_tmpl_clear_noneg.3contract=ct_dev_tmpl_set_aset.3contract
+s usr/share/man/man3contract/ct_dev_tmpl_get_aset.3contract=ct_dev_tmpl_set_aset.3contract
+s usr/share/man/man3contract/ct_dev_tmpl_get_minor.3contract=ct_dev_tmpl_set_aset.3contract
+s usr/share/man/man3contract/ct_dev_tmpl_get_noneg.3contract=ct_dev_tmpl_set_aset.3contract
+f usr/share/man/man3contract/ct_dev_tmpl_set_aset.3contract 0444 root bin
+s usr/share/man/man3contract/ct_dev_tmpl_set_minor.3contract=ct_dev_tmpl_set_aset.3contract
+s usr/share/man/man3contract/ct_dev_tmpl_set_noneg.3contract=ct_dev_tmpl_set_aset.3contract
+s usr/share/man/man3contract/ct_event_free.3contract=ct_event_read.3contract
+s usr/share/man/man3contract/ct_event_get_ctid.3contract=ct_event_read.3contract
+s usr/share/man/man3contract/ct_event_get_evid.3contract=ct_event_read.3contract
+s usr/share/man/man3contract/ct_event_get_flags.3contract=ct_event_read.3contract
+s usr/share/man/man3contract/ct_event_get_nevid.3contract=ct_event_read.3contract
+s usr/share/man/man3contract/ct_event_get_newct.3contract=ct_event_read.3contract
+s usr/share/man/man3contract/ct_event_get_type.3contract=ct_event_read.3contract
+f usr/share/man/man3contract/ct_event_read.3contract 0444 root bin
+s usr/share/man/man3contract/ct_event_read_critical.3contract=ct_event_read.3contract
+s usr/share/man/man3contract/ct_event_reliable.3contract=ct_event_read.3contract
+s usr/share/man/man3contract/ct_event_reset.3contract=ct_event_read.3contract
+s usr/share/man/man3contract/ct_pr_event_get_exitstatus.3contract=ct_pr_event_get_pid.3contract
+s usr/share/man/man3contract/ct_pr_event_get_gcorefile.3contract=ct_pr_event_get_pid.3contract
+s usr/share/man/man3contract/ct_pr_event_get_pcorefile.3contract=ct_pr_event_get_pid.3contract
+f usr/share/man/man3contract/ct_pr_event_get_pid.3contract 0444 root bin
+s usr/share/man/man3contract/ct_pr_event_get_ppid.3contract=ct_pr_event_get_pid.3contract
+s usr/share/man/man3contract/ct_pr_event_get_sender.3contract=ct_pr_event_get_pid.3contract
+s usr/share/man/man3contract/ct_pr_event_get_senderct.3contract=ct_pr_event_get_pid.3contract
+s usr/share/man/man3contract/ct_pr_event_get_signal.3contract=ct_pr_event_get_pid.3contract
+s usr/share/man/man3contract/ct_pr_event_get_zcorefile.3contract=ct_pr_event_get_pid.3contract
+s usr/share/man/man3contract/ct_pr_status_get_contracts.3contract=ct_pr_status_get_param.3contract
+s usr/share/man/man3contract/ct_pr_status_get_fatal.3contract=ct_pr_status_get_param.3contract
+s usr/share/man/man3contract/ct_pr_status_get_members.3contract=ct_pr_status_get_param.3contract
+f usr/share/man/man3contract/ct_pr_status_get_param.3contract 0444 root bin
+s usr/share/man/man3contract/ct_pr_status_get_svc_aux.3contract=ct_pr_status_get_param.3contract
+s usr/share/man/man3contract/ct_pr_status_get_svc_creator.3contract=ct_pr_status_get_param.3contract
+s usr/share/man/man3contract/ct_pr_status_get_svc_ctid.3contract=ct_pr_status_get_param.3contract
+s usr/share/man/man3contract/ct_pr_status_get_svc_fmri.3contract=ct_pr_status_get_param.3contract
+s usr/share/man/man3contract/ct_pr_tmpl_get_fatal.3contract=ct_pr_tmpl_set_transfer.3contract
+s usr/share/man/man3contract/ct_pr_tmpl_get_param.3contract=ct_pr_tmpl_set_transfer.3contract
+s usr/share/man/man3contract/ct_pr_tmpl_get_svc_aux.3contract=ct_pr_tmpl_set_transfer.3contract
+s usr/share/man/man3contract/ct_pr_tmpl_get_svc_fmri.3contract=ct_pr_tmpl_set_transfer.3contract
+s usr/share/man/man3contract/ct_pr_tmpl_get_transfer.3contract=ct_pr_tmpl_set_transfer.3contract
+s usr/share/man/man3contract/ct_pr_tmpl_set_fatal.3contract=ct_pr_tmpl_set_transfer.3contract
+s usr/share/man/man3contract/ct_pr_tmpl_set_param.3contract=ct_pr_tmpl_set_transfer.3contract
+s usr/share/man/man3contract/ct_pr_tmpl_set_svc_aux.3contract=ct_pr_tmpl_set_transfer.3contract
+s usr/share/man/man3contract/ct_pr_tmpl_set_svc_fmri.3contract=ct_pr_tmpl_set_transfer.3contract
+f usr/share/man/man3contract/ct_pr_tmpl_set_transfer.3contract 0444 root bin
+s usr/share/man/man3contract/ct_status_free.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_cookie.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_critical.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_holder.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_id.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_informative.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_nevents.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_nevid.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_ntime.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_qtime.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_state.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_type.3contract=ct_status_read.3contract
+s usr/share/man/man3contract/ct_status_get_zoneid.3contract=ct_status_read.3contract
+f usr/share/man/man3contract/ct_status_read.3contract 0444 root bin
+f usr/share/man/man3contract/ct_tmpl_activate.3contract 0444 root bin
+s usr/share/man/man3contract/ct_tmpl_clear.3contract=ct_tmpl_activate.3contract
+s usr/share/man/man3contract/ct_tmpl_create.3contract=ct_tmpl_activate.3contract
+s usr/share/man/man3contract/ct_tmpl_get_cookie.3contract=ct_tmpl_activate.3contract
+s usr/share/man/man3contract/ct_tmpl_get_critical.3contract=ct_tmpl_activate.3contract
+s usr/share/man/man3contract/ct_tmpl_get_informative.3contract=ct_tmpl_activate.3contract
+s usr/share/man/man3contract/ct_tmpl_set_cookie.3contract=ct_tmpl_activate.3contract
+s usr/share/man/man3contract/ct_tmpl_set_critical.3contract=ct_tmpl_activate.3contract
+s usr/share/man/man3contract/ct_tmpl_set_informative.3contract=ct_tmpl_activate.3contract
+d usr/share/man/man3cpc 0755 root bin
+f usr/share/man/man3cpc/amd_f17h_zen1_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/amd_f17h_zen2_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/amd_f19h_zen3_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/bdw_de_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/bdw_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/bdx_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/bnl_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/clx_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/cpc.3cpc 0444 root bin
+f usr/share/man/man3cpc/cpc_access.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_bind_cpu.3cpc=cpc_bind_curlwp.3cpc
+f usr/share/man/man3cpc/cpc_bind_curlwp.3cpc 0444 root bin
+f usr/share/man/man3cpc/cpc_bind_event.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_bind_pctx.3cpc=cpc_bind_curlwp.3cpc
+s usr/share/man/man3cpc/cpc_buf_add.3cpc=cpc_buf_create.3cpc
+s usr/share/man/man3cpc/cpc_buf_copy.3cpc=cpc_buf_create.3cpc
+f usr/share/man/man3cpc/cpc_buf_create.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_buf_destroy.3cpc=cpc_buf_create.3cpc
+s usr/share/man/man3cpc/cpc_buf_get.3cpc=cpc_buf_create.3cpc
+s usr/share/man/man3cpc/cpc_buf_hrtime.3cpc=cpc_buf_create.3cpc
+s usr/share/man/man3cpc/cpc_buf_set.3cpc=cpc_buf_create.3cpc
+s usr/share/man/man3cpc/cpc_buf_sub.3cpc=cpc_buf_create.3cpc
+s usr/share/man/man3cpc/cpc_buf_tick.3cpc=cpc_buf_create.3cpc
+s usr/share/man/man3cpc/cpc_buf_zero.3cpc=cpc_buf_create.3cpc
+s usr/share/man/man3cpc/cpc_caps.3cpc=cpc_npic.3cpc
+s usr/share/man/man3cpc/cpc_cciname.3cpc=cpc_npic.3cpc
+s usr/share/man/man3cpc/cpc_close.3cpc=cpc_open.3cpc
+s usr/share/man/man3cpc/cpc_count_sys_events.3cpc=cpc_count_usr_events.3cpc
+f usr/share/man/man3cpc/cpc_count_usr_events.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_cpuref.3cpc=cpc_npic.3cpc
+s usr/share/man/man3cpc/cpc_disable.3cpc=cpc_enable.3cpc
+f usr/share/man/man3cpc/cpc_enable.3cpc 0444 root bin
+f usr/share/man/man3cpc/cpc_event.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_event_accum.3cpc=cpc_event_diff.3cpc
+f usr/share/man/man3cpc/cpc_event_diff.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_eventtostr.3cpc=cpc_strtoevent.3cpc
+s usr/share/man/man3cpc/cpc_getcciname.3cpc=cpc_getcpuver.3cpc
+s usr/share/man/man3cpc/cpc_getcpuref.3cpc=cpc_getcpuver.3cpc
+f usr/share/man/man3cpc/cpc_getcpuver.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_getnpic.3cpc=cpc_getcpuver.3cpc
+s usr/share/man/man3cpc/cpc_getusage.3cpc=cpc_getcpuver.3cpc
+f usr/share/man/man3cpc/cpc_npic.3cpc 0444 root bin
+f usr/share/man/man3cpc/cpc_open.3cpc 0444 root bin
+f usr/share/man/man3cpc/cpc_pctx_bind_event.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_pctx_invalidate.3cpc=cpc_pctx_bind_event.3cpc
+s usr/share/man/man3cpc/cpc_pctx_rele.3cpc=cpc_pctx_bind_event.3cpc
+s usr/share/man/man3cpc/cpc_pctx_take_sample.3cpc=cpc_pctx_bind_event.3cpc
+s usr/share/man/man3cpc/cpc_rele.3cpc=cpc_bind_event.3cpc
+s usr/share/man/man3cpc/cpc_request_preset.3cpc=cpc_bind_curlwp.3cpc
+s usr/share/man/man3cpc/cpc_set_add_request.3cpc=cpc_set_create.3cpc
+f usr/share/man/man3cpc/cpc_set_create.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_set_destroy.3cpc=cpc_set_create.3cpc
+s usr/share/man/man3cpc/cpc_set_restart.3cpc=cpc_bind_curlwp.3cpc
+s usr/share/man/man3cpc/cpc_set_sample.3cpc=cpc_buf_create.3cpc
+f usr/share/man/man3cpc/cpc_seterrfn.3cpc 0444 root bin
+f usr/share/man/man3cpc/cpc_seterrhndlr.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_shared_bind_event.3cpc=cpc_shared_open.3cpc
+s usr/share/man/man3cpc/cpc_shared_close.3cpc=cpc_shared_open.3cpc
+f usr/share/man/man3cpc/cpc_shared_open.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_shared_rele.3cpc=cpc_shared_open.3cpc
+s usr/share/man/man3cpc/cpc_shared_take_sample.3cpc=cpc_shared_open.3cpc
+f usr/share/man/man3cpc/cpc_strtoevent.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_take_sample.3cpc=cpc_bind_event.3cpc
+s usr/share/man/man3cpc/cpc_unbind.3cpc=cpc_bind_curlwp.3cpc
+f usr/share/man/man3cpc/cpc_version.3cpc 0444 root bin
+s usr/share/man/man3cpc/cpc_walk_attrs.3cpc=cpc_npic.3cpc
+s usr/share/man/man3cpc/cpc_walk_events_all.3cpc=cpc_npic.3cpc
+s usr/share/man/man3cpc/cpc_walk_events_pic.3cpc=cpc_npic.3cpc
+s usr/share/man/man3cpc/cpc_walk_generic_events_all.3cpc=cpc_npic.3cpc
+s usr/share/man/man3cpc/cpc_walk_generic_events_pic.3cpc=cpc_npic.3cpc
+s usr/share/man/man3cpc/cpc_walk_names.3cpc=cpc_getcpuver.3cpc
+s usr/share/man/man3cpc/cpc_walk_requests.3cpc=cpc_set_create.3cpc
+f usr/share/man/man3cpc/generic_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/glm_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/glp_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/hsw_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/hsx_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/icl_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/ivb_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/ivt_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/jkt_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/nhm_ep_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/nhm_ex_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/pctx_capture.3cpc 0444 root bin
+s usr/share/man/man3cpc/pctx_create.3cpc=pctx_capture.3cpc
+s usr/share/man/man3cpc/pctx_release.3cpc=pctx_capture.3cpc
+s usr/share/man/man3cpc/pctx_run.3cpc=pctx_capture.3cpc
+f usr/share/man/man3cpc/pctx_set_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/skl_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/skx_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/slm_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/snb_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/snr_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/tgl_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/wsm_ep_dp_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/wsm_ep_sp_events.3cpc 0444 root bin
+f usr/share/man/man3cpc/wsm_ex_events.3cpc 0444 root bin
+d usr/share/man/man3curses 0755 root bin
+s usr/share/man/man3curses/addch.3curses=curs_addch.3curses
+s usr/share/man/man3curses/addchnstr.3curses=curs_addchstr.3curses
+s usr/share/man/man3curses/addchstr.3curses=curs_addchstr.3curses
+s usr/share/man/man3curses/addnstr.3curses=curs_addstr.3curses
+s usr/share/man/man3curses/addnwstr.3curses=curs_addwstr.3curses
+s usr/share/man/man3curses/addstr.3curses=curs_addstr.3curses
+s usr/share/man/man3curses/addwch.3curses=curs_addwch.3curses
+s usr/share/man/man3curses/addwchnstr.3curses=curs_addwchstr.3curses
+s usr/share/man/man3curses/addwchstr.3curses=curs_addwchstr.3curses
+s usr/share/man/man3curses/addwstr.3curses=curs_addwstr.3curses
+s usr/share/man/man3curses/adjcurspos.3curses=curs_alecompat.3curses
+s usr/share/man/man3curses/attroff.3curses=curs_attr.3curses
+s usr/share/man/man3curses/attron.3curses=curs_attr.3curses
+s usr/share/man/man3curses/attrset.3curses=curs_attr.3curses
+s usr/share/man/man3curses/baudrate.3curses=curs_termattrs.3curses
+s usr/share/man/man3curses/beep.3curses=curs_beep.3curses
+s usr/share/man/man3curses/bkgd.3curses=curs_bkgd.3curses
+s usr/share/man/man3curses/bkgdset.3curses=curs_bkgd.3curses
+s usr/share/man/man3curses/border.3curses=curs_border.3curses
+s usr/share/man/man3curses/bottom_panel.3curses=panel_top.3curses
+s usr/share/man/man3curses/box.3curses=curs_border.3curses
+s usr/share/man/man3curses/can_change_color.3curses=curs_color.3curses
+s usr/share/man/man3curses/cbreak.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/clear.3curses=curs_clear.3curses
+s usr/share/man/man3curses/clearok.3curses=curs_outopts.3curses
+s usr/share/man/man3curses/clrtobot.3curses=curs_clear.3curses
+s usr/share/man/man3curses/clrtoeol.3curses=curs_clear.3curses
+s usr/share/man/man3curses/color_content.3curses=curs_color.3curses
+s usr/share/man/man3curses/copywin.3curses=curs_overlay.3curses
+s usr/share/man/man3curses/current_field.3curses=form_page.3curses
+s usr/share/man/man3curses/current_item.3curses=menu_item_current.3curses
+f usr/share/man/man3curses/curs_addch.3curses 0444 root bin
+f usr/share/man/man3curses/curs_addchstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_addstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_addwch.3curses 0444 root bin
+f usr/share/man/man3curses/curs_addwchstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_addwstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_alecompat.3curses 0444 root bin
+f usr/share/man/man3curses/curs_attr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_beep.3curses 0444 root bin
+f usr/share/man/man3curses/curs_bkgd.3curses 0444 root bin
+f usr/share/man/man3curses/curs_border.3curses 0444 root bin
+f usr/share/man/man3curses/curs_clear.3curses 0444 root bin
+f usr/share/man/man3curses/curs_color.3curses 0444 root bin
+f usr/share/man/man3curses/curs_delch.3curses 0444 root bin
+f usr/share/man/man3curses/curs_deleteln.3curses 0444 root bin
+f usr/share/man/man3curses/curs_getch.3curses 0444 root bin
+f usr/share/man/man3curses/curs_getstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_getwch.3curses 0444 root bin
+f usr/share/man/man3curses/curs_getwstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_getyx.3curses 0444 root bin
+f usr/share/man/man3curses/curs_inch.3curses 0444 root bin
+f usr/share/man/man3curses/curs_inchstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_initscr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_inopts.3curses 0444 root bin
+f usr/share/man/man3curses/curs_insch.3curses 0444 root bin
+f usr/share/man/man3curses/curs_insstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_instr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_inswch.3curses 0444 root bin
+f usr/share/man/man3curses/curs_inswstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_inwch.3curses 0444 root bin
+f usr/share/man/man3curses/curs_inwchstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_inwstr.3curses 0444 root bin
+f usr/share/man/man3curses/curs_kernel.3curses 0444 root bin
+f usr/share/man/man3curses/curs_move.3curses 0444 root bin
+f usr/share/man/man3curses/curs_outopts.3curses 0444 root bin
+f usr/share/man/man3curses/curs_overlay.3curses 0444 root bin
+f usr/share/man/man3curses/curs_pad.3curses 0444 root bin
+f usr/share/man/man3curses/curs_printw.3curses 0444 root bin
+f usr/share/man/man3curses/curs_refresh.3curses 0444 root bin
+f usr/share/man/man3curses/curs_scanw.3curses 0444 root bin
+f usr/share/man/man3curses/curs_scr_dump.3curses 0444 root bin
+f usr/share/man/man3curses/curs_scroll.3curses 0444 root bin
+s usr/share/man/man3curses/curs_set.3curses=curs_kernel.3curses
+f usr/share/man/man3curses/curs_slk.3curses 0444 root bin
+f usr/share/man/man3curses/curs_termattrs.3curses 0444 root bin
+f usr/share/man/man3curses/curs_termcap.3curses 0444 root bin
+f usr/share/man/man3curses/curs_terminfo.3curses 0444 root bin
+f usr/share/man/man3curses/curs_touch.3curses 0444 root bin
+f usr/share/man/man3curses/curs_util.3curses 0444 root bin
+f usr/share/man/man3curses/curs_window.3curses 0444 root bin
+f usr/share/man/man3curses/curses.3curses 0444 root bin
+s usr/share/man/man3curses/data_ahead.3curses=form_data.3curses
+s usr/share/man/man3curses/data_behind.3curses=form_data.3curses
+s usr/share/man/man3curses/def_prog_mode.3curses=curs_kernel.3curses
+s usr/share/man/man3curses/def_shell_mode.3curses=curs_kernel.3curses
+s usr/share/man/man3curses/del_curterm.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/del_panel.3curses=panel_new.3curses
+s usr/share/man/man3curses/delay_output.3curses=curs_util.3curses
+s usr/share/man/man3curses/delch.3curses=curs_delch.3curses
+s usr/share/man/man3curses/deleteln.3curses=curs_deleteln.3curses
+s usr/share/man/man3curses/delscreen.3curses=curs_initscr.3curses
+s usr/share/man/man3curses/delwin.3curses=curs_window.3curses
+s usr/share/man/man3curses/derwin.3curses=curs_window.3curses
+s usr/share/man/man3curses/doupdate.3curses=curs_refresh.3curses
+s usr/share/man/man3curses/dup_field.3curses=form_field_new.3curses
+s usr/share/man/man3curses/dupwin.3curses=curs_window.3curses
+s usr/share/man/man3curses/dynamic_field_info.3curses=form_field_info.3curses
+s usr/share/man/man3curses/echo.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/echochar.3curses=curs_addch.3curses
+s usr/share/man/man3curses/echowchar.3curses=curs_addwch.3curses
+s usr/share/man/man3curses/endwin.3curses=curs_initscr.3curses
+s usr/share/man/man3curses/erase.3curses=curs_clear.3curses
+s usr/share/man/man3curses/erasechar.3curses=curs_termattrs.3curses
+s usr/share/man/man3curses/field_arg.3curses=form_field_validation.3curses
+s usr/share/man/man3curses/field_back.3curses=form_field_attributes.3curses
+s usr/share/man/man3curses/field_buffer.3curses=form_field_buffer.3curses
+s usr/share/man/man3curses/field_count.3curses=form_field.3curses
+s usr/share/man/man3curses/field_fore.3curses=form_field_attributes.3curses
+s usr/share/man/man3curses/field_index.3curses=form_page.3curses
+s usr/share/man/man3curses/field_info.3curses=form_field_info.3curses
+s usr/share/man/man3curses/field_init.3curses=form_hook.3curses
+s usr/share/man/man3curses/field_just.3curses=form_field_just.3curses
+s usr/share/man/man3curses/field_opts.3curses=form_field_opts.3curses
+s usr/share/man/man3curses/field_opts_off.3curses=form_field_opts.3curses
+s usr/share/man/man3curses/field_opts_on.3curses=form_field_opts.3curses
+s usr/share/man/man3curses/field_pad.3curses=form_field_attributes.3curses
+s usr/share/man/man3curses/field_status.3curses=form_field_buffer.3curses
+s usr/share/man/man3curses/field_term.3curses=form_hook.3curses
+s usr/share/man/man3curses/field_type.3curses=form_field_validation.3curses
+s usr/share/man/man3curses/field_userptr.3curses=form_field_userptr.3curses
+s usr/share/man/man3curses/filter.3curses=curs_util.3curses
+s usr/share/man/man3curses/flash.3curses=curs_beep.3curses
+s usr/share/man/man3curses/flushinp.3curses=curs_util.3curses
+f usr/share/man/man3curses/form_cursor.3curses 0444 root bin
+f usr/share/man/man3curses/form_data.3curses 0444 root bin
+f usr/share/man/man3curses/form_driver.3curses 0444 root bin
+f usr/share/man/man3curses/form_field.3curses 0444 root bin
+f usr/share/man/man3curses/form_field_attributes.3curses 0444 root bin
+f usr/share/man/man3curses/form_field_buffer.3curses 0444 root bin
+f usr/share/man/man3curses/form_field_info.3curses 0444 root bin
+f usr/share/man/man3curses/form_field_just.3curses 0444 root bin
+f usr/share/man/man3curses/form_field_new.3curses 0444 root bin
+f usr/share/man/man3curses/form_field_opts.3curses 0444 root bin
+f usr/share/man/man3curses/form_field_userptr.3curses 0444 root bin
+f usr/share/man/man3curses/form_field_validation.3curses 0444 root bin
+s usr/share/man/man3curses/form_fields.3curses=form_field.3curses
+f usr/share/man/man3curses/form_fieldtype.3curses 0444 root bin
+f usr/share/man/man3curses/form_hook.3curses 0444 root bin
+s usr/share/man/man3curses/form_init.3curses=form_hook.3curses
+f usr/share/man/man3curses/form_new.3curses 0444 root bin
+f usr/share/man/man3curses/form_new_page.3curses 0444 root bin
+f usr/share/man/man3curses/form_opts.3curses 0444 root bin
+s usr/share/man/man3curses/form_opts_off.3curses=form_opts.3curses
+s usr/share/man/man3curses/form_opts_on.3curses=form_opts.3curses
+f usr/share/man/man3curses/form_page.3curses 0444 root bin
+f usr/share/man/man3curses/form_post.3curses 0444 root bin
+s usr/share/man/man3curses/form_sub.3curses=form_win.3curses
+s usr/share/man/man3curses/form_term.3curses=form_hook.3curses
+f usr/share/man/man3curses/form_userptr.3curses 0444 root bin
+f usr/share/man/man3curses/form_win.3curses 0444 root bin
+f usr/share/man/man3curses/forms.3curses 0444 root bin
+s usr/share/man/man3curses/free_field.3curses=form_field_new.3curses
+s usr/share/man/man3curses/free_fieldtype.3curses=form_fieldtype.3curses
+s usr/share/man/man3curses/free_form.3curses=form_new.3curses
+s usr/share/man/man3curses/free_item.3curses=menu_item_new.3curses
+s usr/share/man/man3curses/free_menu.3curses=menu_new.3curses
+s usr/share/man/man3curses/getbegyx.3curses=curs_getyx.3curses
+s usr/share/man/man3curses/getch.3curses=curs_getch.3curses
+s usr/share/man/man3curses/getmaxyx.3curses=curs_getyx.3curses
+s usr/share/man/man3curses/getnwstr.3curses=curs_getwstr.3curses
+s usr/share/man/man3curses/getparyx.3curses=curs_getyx.3curses
+s usr/share/man/man3curses/getstr.3curses=curs_getstr.3curses
+s usr/share/man/man3curses/getsyx.3curses=curs_kernel.3curses
+s usr/share/man/man3curses/getwch.3curses=curs_getwch.3curses
+s usr/share/man/man3curses/getwin.3curses=curs_util.3curses
+s usr/share/man/man3curses/getwstr.3curses=curs_getwstr.3curses
+s usr/share/man/man3curses/getyx.3curses=curs_getyx.3curses
+s usr/share/man/man3curses/halfdelay.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/has_colors.3curses=curs_color.3curses
+s usr/share/man/man3curses/has_ic.3curses=curs_termattrs.3curses
+s usr/share/man/man3curses/has_il.3curses=curs_termattrs.3curses
+s usr/share/man/man3curses/hide_panel.3curses=panel_show.3curses
+s usr/share/man/man3curses/idcok.3curses=curs_outopts.3curses
+s usr/share/man/man3curses/idlok.3curses=curs_outopts.3curses
+s usr/share/man/man3curses/immedok.3curses=curs_outopts.3curses
+s usr/share/man/man3curses/inch.3curses=curs_inch.3curses
+s usr/share/man/man3curses/inchnstr.3curses=curs_inchstr.3curses
+s usr/share/man/man3curses/inchstr.3curses=curs_inchstr.3curses
+s usr/share/man/man3curses/init_color.3curses=curs_color.3curses
+s usr/share/man/man3curses/init_pair.3curses=curs_color.3curses
+s usr/share/man/man3curses/initscr.3curses=curs_initscr.3curses
+s usr/share/man/man3curses/innstr.3curses=curs_instr.3curses
+s usr/share/man/man3curses/innwstr.3curses=curs_inwstr.3curses
+s usr/share/man/man3curses/insch.3curses=curs_insch.3curses
+s usr/share/man/man3curses/insdelln.3curses=curs_deleteln.3curses
+s usr/share/man/man3curses/insertln.3curses=curs_deleteln.3curses
+s usr/share/man/man3curses/insnstr.3curses=curs_insstr.3curses
+s usr/share/man/man3curses/insnwstr.3curses=curs_inswstr.3curses
+s usr/share/man/man3curses/insstr.3curses=curs_insstr.3curses
+s usr/share/man/man3curses/instr.3curses=curs_instr.3curses
+s usr/share/man/man3curses/inswch.3curses=curs_inswch.3curses
+s usr/share/man/man3curses/inswstr.3curses=curs_inswstr.3curses
+s usr/share/man/man3curses/intrflush.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/inwch.3curses=curs_inwch.3curses
+s usr/share/man/man3curses/inwchnstr.3curses=curs_inwchstr.3curses
+s usr/share/man/man3curses/inwchstr.3curses=curs_inwchstr.3curses
+s usr/share/man/man3curses/inwstr.3curses=curs_inwstr.3curses
+s usr/share/man/man3curses/is_linetouched.3curses=curs_touch.3curses
+s usr/share/man/man3curses/is_wintouched.3curses=curs_touch.3curses
+s usr/share/man/man3curses/isendwin.3curses=curs_initscr.3curses
+s usr/share/man/man3curses/item_count.3curses=menu_items.3curses
+s usr/share/man/man3curses/item_description.3curses=menu_item_name.3curses
+s usr/share/man/man3curses/item_index.3curses=menu_item_current.3curses
+s usr/share/man/man3curses/item_init.3curses=menu_hook.3curses
+s usr/share/man/man3curses/item_name.3curses=menu_item_name.3curses
+s usr/share/man/man3curses/item_opts.3curses=menu_item_opts.3curses
+s usr/share/man/man3curses/item_opts_off.3curses=menu_item_opts.3curses
+s usr/share/man/man3curses/item_opts_on.3curses=menu_item_opts.3curses
+s usr/share/man/man3curses/item_term.3curses=menu_hook.3curses
+s usr/share/man/man3curses/item_userptr.3curses=menu_item_userptr.3curses
+s usr/share/man/man3curses/item_value.3curses=menu_item_value.3curses
+s usr/share/man/man3curses/item_visible.3curses=menu_item_visible.3curses
+s usr/share/man/man3curses/keyname.3curses=curs_util.3curses
+s usr/share/man/man3curses/keypad.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/killchar.3curses=curs_termattrs.3curses
+s usr/share/man/man3curses/leaveok.3curses=curs_outopts.3curses
+s usr/share/man/man3curses/link_field.3curses=form_field_new.3curses
+s usr/share/man/man3curses/link_fieldtype.3curses=form_fieldtype.3curses
+s usr/share/man/man3curses/longname.3curses=curs_termattrs.3curses
+f usr/share/man/man3curses/menu_attributes.3curses 0444 root bin
+s usr/share/man/man3curses/menu_back.3curses=menu_attributes.3curses
+f usr/share/man/man3curses/menu_cursor.3curses 0444 root bin
+f usr/share/man/man3curses/menu_driver.3curses 0444 root bin
+s usr/share/man/man3curses/menu_fore.3curses=menu_attributes.3curses
+f usr/share/man/man3curses/menu_format.3curses 0444 root bin
+s usr/share/man/man3curses/menu_grey.3curses=menu_attributes.3curses
+f usr/share/man/man3curses/menu_hook.3curses 0444 root bin
+s usr/share/man/man3curses/menu_init.3curses=menu_hook.3curses
+f usr/share/man/man3curses/menu_item_current.3curses 0444 root bin
+f usr/share/man/man3curses/menu_item_name.3curses 0444 root bin
+f usr/share/man/man3curses/menu_item_new.3curses 0444 root bin
+f usr/share/man/man3curses/menu_item_opts.3curses 0444 root bin
+f usr/share/man/man3curses/menu_item_userptr.3curses 0444 root bin
+f usr/share/man/man3curses/menu_item_value.3curses 0444 root bin
+f usr/share/man/man3curses/menu_item_visible.3curses 0444 root bin
+f usr/share/man/man3curses/menu_items.3curses 0444 root bin
+f usr/share/man/man3curses/menu_mark.3curses 0444 root bin
+f usr/share/man/man3curses/menu_new.3curses 0444 root bin
+f usr/share/man/man3curses/menu_opts.3curses 0444 root bin
+s usr/share/man/man3curses/menu_opts_off.3curses=menu_opts.3curses
+s usr/share/man/man3curses/menu_opts_on.3curses=menu_opts.3curses
+s usr/share/man/man3curses/menu_pad.3curses=menu_attributes.3curses
+f usr/share/man/man3curses/menu_pattern.3curses 0444 root bin
+f usr/share/man/man3curses/menu_post.3curses 0444 root bin
+s usr/share/man/man3curses/menu_sub.3curses=menu_win.3curses
+s usr/share/man/man3curses/menu_term.3curses=menu_hook.3curses
+f usr/share/man/man3curses/menu_userptr.3curses 0444 root bin
+f usr/share/man/man3curses/menu_win.3curses 0444 root bin
+f usr/share/man/man3curses/menus.3curses 0444 root bin
+s usr/share/man/man3curses/meta.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/move.3curses=curs_move.3curses
+s usr/share/man/man3curses/move_field.3curses=form_field.3curses
+s usr/share/man/man3curses/move_panel.3curses=panel_move.3curses
+s usr/share/man/man3curses/movenextch.3curses=curs_alecompat.3curses
+s usr/share/man/man3curses/moveprevch.3curses=curs_alecompat.3curses
+s usr/share/man/man3curses/mvaddch.3curses=curs_addch.3curses
+s usr/share/man/man3curses/mvaddchnstr.3curses=curs_addchstr.3curses
+s usr/share/man/man3curses/mvaddchstr.3curses=curs_addchstr.3curses
+s usr/share/man/man3curses/mvaddnstr.3curses=curs_addstr.3curses
+s usr/share/man/man3curses/mvaddnwstr.3curses=curs_addwstr.3curses
+s usr/share/man/man3curses/mvaddstr.3curses=curs_addstr.3curses
+s usr/share/man/man3curses/mvaddwch.3curses=curs_addwch.3curses
+s usr/share/man/man3curses/mvaddwchnstr.3curses=curs_addwchstr.3curses
+s usr/share/man/man3curses/mvaddwchstr.3curses=curs_addwchstr.3curses
+s usr/share/man/man3curses/mvaddwstr.3curses=curs_addwstr.3curses
+s usr/share/man/man3curses/mvcur.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/mvdelch.3curses=curs_delch.3curses
+s usr/share/man/man3curses/mvderwin.3curses=curs_window.3curses
+s usr/share/man/man3curses/mvgetch.3curses=curs_getch.3curses
+s usr/share/man/man3curses/mvgetnwstr.3curses=curs_getwstr.3curses
+s usr/share/man/man3curses/mvgetstr.3curses=curs_getstr.3curses
+s usr/share/man/man3curses/mvgetwch.3curses=curs_getwch.3curses
+s usr/share/man/man3curses/mvgetwstr.3curses=curs_getwstr.3curses
+s usr/share/man/man3curses/mvinch.3curses=curs_inch.3curses
+s usr/share/man/man3curses/mvinchnstr.3curses=curs_inchstr.3curses
+s usr/share/man/man3curses/mvinchstr.3curses=curs_inchstr.3curses
+s usr/share/man/man3curses/mvinnstr.3curses=curs_instr.3curses
+s usr/share/man/man3curses/mvinnwstr.3curses=curs_inwstr.3curses
+s usr/share/man/man3curses/mvinsch.3curses=curs_insch.3curses
+s usr/share/man/man3curses/mvinsnstr.3curses=curs_insstr.3curses
+s usr/share/man/man3curses/mvinsnwstr.3curses=curs_inswstr.3curses
+s usr/share/man/man3curses/mvinsstr.3curses=curs_insstr.3curses
+s usr/share/man/man3curses/mvinstr.3curses=curs_instr.3curses
+s usr/share/man/man3curses/mvinswch.3curses=curs_inswch.3curses
+s usr/share/man/man3curses/mvinswstr.3curses=curs_inswstr.3curses
+s usr/share/man/man3curses/mvinwch.3curses=curs_inwch.3curses
+s usr/share/man/man3curses/mvinwchnstr.3curses=curs_inwchstr.3curses
+s usr/share/man/man3curses/mvinwchstr.3curses=curs_inwchstr.3curses
+s usr/share/man/man3curses/mvinwstr.3curses=curs_inwstr.3curses
+s usr/share/man/man3curses/mvprintw.3curses=curs_printw.3curses
+s usr/share/man/man3curses/mvscanw.3curses=curs_scanw.3curses
+s usr/share/man/man3curses/mvwaddch.3curses=curs_addch.3curses
+s usr/share/man/man3curses/mvwaddchnstr.3curses=curs_addchstr.3curses
+s usr/share/man/man3curses/mvwaddchstr.3curses=curs_addchstr.3curses
+s usr/share/man/man3curses/mvwaddnstr.3curses=curs_addstr.3curses
+s usr/share/man/man3curses/mvwaddnwstr.3curses=curs_addwstr.3curses
+s usr/share/man/man3curses/mvwaddstr.3curses=curs_addstr.3curses
+s usr/share/man/man3curses/mvwaddwch.3curses=curs_addwch.3curses
+s usr/share/man/man3curses/mvwaddwchnstr.3curses=curs_addwchstr.3curses
+s usr/share/man/man3curses/mvwaddwchstr.3curses=curs_addwchstr.3curses
+s usr/share/man/man3curses/mvwaddwstr.3curses=curs_addwstr.3curses
+s usr/share/man/man3curses/mvwdelch.3curses=curs_delch.3curses
+s usr/share/man/man3curses/mvwgetch.3curses=curs_getch.3curses
+s usr/share/man/man3curses/mvwgetnwstr.3curses=curs_getwstr.3curses
+s usr/share/man/man3curses/mvwgetstr.3curses=curs_getstr.3curses
+s usr/share/man/man3curses/mvwgetwch.3curses=curs_getwch.3curses
+s usr/share/man/man3curses/mvwgetwstr.3curses=curs_getwstr.3curses
+s usr/share/man/man3curses/mvwin.3curses=curs_window.3curses
+s usr/share/man/man3curses/mvwinch.3curses=curs_inch.3curses
+s usr/share/man/man3curses/mvwinchnstr.3curses=curs_inchstr.3curses
+s usr/share/man/man3curses/mvwinchstr.3curses=curs_inchstr.3curses
+s usr/share/man/man3curses/mvwinnstr.3curses=curs_instr.3curses
+s usr/share/man/man3curses/mvwinnwstr.3curses=curs_inwstr.3curses
+s usr/share/man/man3curses/mvwinsch.3curses=curs_insch.3curses
+s usr/share/man/man3curses/mvwinsnstr.3curses=curs_insstr.3curses
+s usr/share/man/man3curses/mvwinsnwstr.3curses=curs_inswstr.3curses
+s usr/share/man/man3curses/mvwinsstr.3curses=curs_insstr.3curses
+s usr/share/man/man3curses/mvwinstr.3curses=curs_instr.3curses
+s usr/share/man/man3curses/mvwinswch.3curses=curs_inswch.3curses
+s usr/share/man/man3curses/mvwinswstr.3curses=curs_inswstr.3curses
+s usr/share/man/man3curses/mvwinwch.3curses=curs_inwch.3curses
+s usr/share/man/man3curses/mvwinwchnstr.3curses=curs_inwchstr.3curses
+s usr/share/man/man3curses/mvwinwchstr.3curses=curs_inwchstr.3curses
+s usr/share/man/man3curses/mvwinwstr.3curses=curs_inwstr.3curses
+s usr/share/man/man3curses/mvwprintw.3curses=curs_printw.3curses
+s usr/share/man/man3curses/mvwscanw.3curses=curs_scanw.3curses
+s usr/share/man/man3curses/napms.3curses=curs_kernel.3curses
+s usr/share/man/man3curses/new_field.3curses=form_field_new.3curses
+s usr/share/man/man3curses/new_fieldtype.3curses=form_fieldtype.3curses
+s usr/share/man/man3curses/new_form.3curses=form_new.3curses
+s usr/share/man/man3curses/new_item.3curses=menu_item_new.3curses
+s usr/share/man/man3curses/new_menu.3curses=menu_new.3curses
+s usr/share/man/man3curses/new_page.3curses=form_new_page.3curses
+s usr/share/man/man3curses/new_panel.3curses=panel_new.3curses
+s usr/share/man/man3curses/newpad.3curses=curs_pad.3curses
+s usr/share/man/man3curses/newterm.3curses=curs_initscr.3curses
+s usr/share/man/man3curses/newwin.3curses=curs_window.3curses
+s usr/share/man/man3curses/nl.3curses=curs_outopts.3curses
+s usr/share/man/man3curses/nocbreak.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/nodelay.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/noecho.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/nonl.3curses=curs_outopts.3curses
+s usr/share/man/man3curses/noqiflush.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/noraw.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/notimeout.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/overlay.3curses=curs_overlay.3curses
+s usr/share/man/man3curses/overwrite.3curses=curs_overlay.3curses
+s usr/share/man/man3curses/pair_content.3curses=curs_color.3curses
+f usr/share/man/man3curses/panel_above.3curses 0444 root bin
+s usr/share/man/man3curses/panel_below.3curses=panel_above.3curses
+s usr/share/man/man3curses/panel_hidden.3curses=panel_show.3curses
+f usr/share/man/man3curses/panel_move.3curses 0444 root bin
+f usr/share/man/man3curses/panel_new.3curses 0444 root bin
+f usr/share/man/man3curses/panel_show.3curses 0444 root bin
+f usr/share/man/man3curses/panel_top.3curses 0444 root bin
+f usr/share/man/man3curses/panel_update.3curses 0444 root bin
+f usr/share/man/man3curses/panel_userptr.3curses 0444 root bin
+f usr/share/man/man3curses/panel_window.3curses 0444 root bin
+f usr/share/man/man3curses/panels.3curses 0444 root bin
+s usr/share/man/man3curses/pechochar.3curses=curs_pad.3curses
+s usr/share/man/man3curses/pechowchar.3curses=curs_pad.3curses
+s usr/share/man/man3curses/pnoutrefresh.3curses=curs_pad.3curses
+s usr/share/man/man3curses/pos_form_cursor.3curses=form_cursor.3curses
+s usr/share/man/man3curses/pos_menu_cursor.3curses=menu_cursor.3curses
+s usr/share/man/man3curses/post_form.3curses=form_post.3curses
+s usr/share/man/man3curses/post_menu.3curses=menu_post.3curses
+s usr/share/man/man3curses/prefresh.3curses=curs_pad.3curses
+s usr/share/man/man3curses/printw.3curses=curs_printw.3curses
+s usr/share/man/man3curses/putp.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/putwin.3curses=curs_util.3curses
+s usr/share/man/man3curses/qiflush.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/raw.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/redrawwin.3curses=curs_refresh.3curses
+s usr/share/man/man3curses/refresh.3curses=curs_refresh.3curses
+s usr/share/man/man3curses/replace_panel.3curses=panel_window.3curses
+s usr/share/man/man3curses/reset_prog_mode.3curses=curs_kernel.3curses
+s usr/share/man/man3curses/reset_shell_mode.3curses=curs_kernel.3curses
+s usr/share/man/man3curses/resetty.3curses=curs_kernel.3curses
+s usr/share/man/man3curses/restartterm.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/ripoffline.3curses=curs_kernel.3curses
+s usr/share/man/man3curses/savetty.3curses=curs_kernel.3curses
+s usr/share/man/man3curses/scale_form.3curses=form_win.3curses
+s usr/share/man/man3curses/scale_menu.3curses=menu_win.3curses
+s usr/share/man/man3curses/scanw.3curses=curs_scanw.3curses
+s usr/share/man/man3curses/scr_dump.3curses=curs_scr_dump.3curses
+s usr/share/man/man3curses/scr_init.3curses=curs_scr_dump.3curses
+s usr/share/man/man3curses/scr_restore.3curses=curs_scr_dump.3curses
+s usr/share/man/man3curses/scr_set.3curses=curs_scr_dump.3curses
+s usr/share/man/man3curses/scrl.3curses=curs_scroll.3curses
+s usr/share/man/man3curses/scroll.3curses=curs_scroll.3curses
+s usr/share/man/man3curses/scrollok.3curses=curs_outopts.3curses
+s usr/share/man/man3curses/set_current_field.3curses=form_page.3curses
+s usr/share/man/man3curses/set_current_item.3curses=menu_item_current.3curses
+s usr/share/man/man3curses/set_curterm.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/set_field_back.3curses=form_field_attributes.3curses
+s usr/share/man/man3curses/set_field_buffer.3curses=form_field_buffer.3curses
+s usr/share/man/man3curses/set_field_fore.3curses=form_field_attributes.3curses
+s usr/share/man/man3curses/set_field_init.3curses=form_hook.3curses
+s usr/share/man/man3curses/set_field_just.3curses=form_field_just.3curses
+s usr/share/man/man3curses/set_field_opts.3curses=form_field_opts.3curses
+s usr/share/man/man3curses/set_field_pad.3curses=form_field_attributes.3curses
+s usr/share/man/man3curses/set_field_status.3curses=form_field_buffer.3curses
+s usr/share/man/man3curses/set_field_term.3curses=form_hook.3curses
+s usr/share/man/man3curses/set_field_type.3curses=form_field_validation.3curses
+s usr/share/man/man3curses/set_field_userptr.3curses=form_field_userptr.3curses
+s usr/share/man/man3curses/set_fieldtype_arg.3curses=form_fieldtype.3curses
+s usr/share/man/man3curses/set_fieldtype_choice.3curses=form_fieldtype.3curses
+s usr/share/man/man3curses/set_form_fields.3curses=form_field.3curses
+s usr/share/man/man3curses/set_form_init.3curses=form_hook.3curses
+s usr/share/man/man3curses/set_form_opts.3curses=form_opts.3curses
+s usr/share/man/man3curses/set_form_page.3curses=form_page.3curses
+s usr/share/man/man3curses/set_form_sub.3curses=form_win.3curses
+s usr/share/man/man3curses/set_form_term.3curses=form_hook.3curses
+s usr/share/man/man3curses/set_form_userptr.3curses=form_userptr.3curses
+s usr/share/man/man3curses/set_form_win.3curses=form_win.3curses
+s usr/share/man/man3curses/set_item_init.3curses=menu_hook.3curses
+s usr/share/man/man3curses/set_item_opts.3curses=menu_item_opts.3curses
+s usr/share/man/man3curses/set_item_term.3curses=menu_hook.3curses
+s usr/share/man/man3curses/set_item_userptr.3curses=menu_item_userptr.3curses
+s usr/share/man/man3curses/set_item_value.3curses=menu_item_value.3curses
+s usr/share/man/man3curses/set_max_field.3curses=form_field_buffer.3curses
+s usr/share/man/man3curses/set_menu_back.3curses=menu_attributes.3curses
+s usr/share/man/man3curses/set_menu_fore.3curses=menu_attributes.3curses
+s usr/share/man/man3curses/set_menu_format.3curses=menu_format.3curses
+s usr/share/man/man3curses/set_menu_grey.3curses=menu_attributes.3curses
+s usr/share/man/man3curses/set_menu_init.3curses=menu_hook.3curses
+s usr/share/man/man3curses/set_menu_items.3curses=menu_items.3curses
+s usr/share/man/man3curses/set_menu_mark.3curses=menu_mark.3curses
+s usr/share/man/man3curses/set_menu_opts.3curses=menu_opts.3curses
+s usr/share/man/man3curses/set_menu_pad.3curses=menu_attributes.3curses
+s usr/share/man/man3curses/set_menu_pattern.3curses=menu_pattern.3curses
+s usr/share/man/man3curses/set_menu_sub.3curses=menu_win.3curses
+s usr/share/man/man3curses/set_menu_term.3curses=menu_hook.3curses
+s usr/share/man/man3curses/set_menu_userptr.3curses=menu_userptr.3curses
+s usr/share/man/man3curses/set_menu_win.3curses=menu_win.3curses
+s usr/share/man/man3curses/set_new_page.3curses=form_new_page.3curses
+s usr/share/man/man3curses/set_panel_userptr.3curses=panel_userptr.3curses
+s usr/share/man/man3curses/set_term.3curses=curs_initscr.3curses
+s usr/share/man/man3curses/set_top_row.3curses=menu_item_current.3curses
+s usr/share/man/man3curses/setscrreg.3curses=curs_outopts.3curses
+s usr/share/man/man3curses/setsyx.3curses=curs_kernel.3curses
+s usr/share/man/man3curses/setterm.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/setupterm.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/show_panel.3curses=panel_show.3curses
+s usr/share/man/man3curses/slk_attroff.3curses=curs_slk.3curses
+s usr/share/man/man3curses/slk_attron.3curses=curs_slk.3curses
+s usr/share/man/man3curses/slk_attrset.3curses=curs_slk.3curses
+s usr/share/man/man3curses/slk_clear.3curses=curs_slk.3curses
+s usr/share/man/man3curses/slk_init.3curses=curs_slk.3curses
+s usr/share/man/man3curses/slk_label.3curses=curs_slk.3curses
+s usr/share/man/man3curses/slk_noutrefresh.3curses=curs_slk.3curses
+s usr/share/man/man3curses/slk_refresh.3curses=curs_slk.3curses
+s usr/share/man/man3curses/slk_restore.3curses=curs_slk.3curses
+s usr/share/man/man3curses/slk_set.3curses=curs_slk.3curses
+s usr/share/man/man3curses/slk_touch.3curses=curs_slk.3curses
+s usr/share/man/man3curses/standend.3curses=curs_attr.3curses
+s usr/share/man/man3curses/standout.3curses=curs_attr.3curses
+s usr/share/man/man3curses/start_color.3curses=curs_color.3curses
+s usr/share/man/man3curses/subpad.3curses=curs_pad.3curses
+s usr/share/man/man3curses/subwin.3curses=curs_window.3curses
+s usr/share/man/man3curses/syncok.3curses=curs_window.3curses
+s usr/share/man/man3curses/termattrs.3curses=curs_termattrs.3curses
+s usr/share/man/man3curses/termname.3curses=curs_termattrs.3curses
+s usr/share/man/man3curses/tgetent.3curses=curs_termcap.3curses
+s usr/share/man/man3curses/tgetflag.3curses=curs_termcap.3curses
+s usr/share/man/man3curses/tgetnum.3curses=curs_termcap.3curses
+s usr/share/man/man3curses/tgetstr.3curses=curs_termcap.3curses
+s usr/share/man/man3curses/tgoto.3curses=curs_termcap.3curses
+s usr/share/man/man3curses/tigetflag.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/tigetnum.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/tigetstr.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/timeout.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/top_panel.3curses=panel_top.3curses
+s usr/share/man/man3curses/top_row.3curses=menu_item_current.3curses
+s usr/share/man/man3curses/touchline.3curses=curs_touch.3curses
+s usr/share/man/man3curses/touchwin.3curses=curs_touch.3curses
+s usr/share/man/man3curses/tparm.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/tputs.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/typeahead.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/unctrl.3curses=curs_util.3curses
+s usr/share/man/man3curses/ungetch.3curses=curs_getch.3curses
+s usr/share/man/man3curses/ungetwch.3curses=curs_getwch.3curses
+s usr/share/man/man3curses/unpost_form.3curses=form_post.3curses
+s usr/share/man/man3curses/unpost_menu.3curses=menu_post.3curses
+s usr/share/man/man3curses/untouchwin.3curses=curs_touch.3curses
+s usr/share/man/man3curses/update_panels.3curses=panel_update.3curses
+s usr/share/man/man3curses/use_env.3curses=curs_util.3curses
+s usr/share/man/man3curses/vidattr.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/vidputs.3curses=curs_terminfo.3curses
+s usr/share/man/man3curses/vwprintw.3curses=curs_printw.3curses
+s usr/share/man/man3curses/vwscanw.3curses=curs_scanw.3curses
+s usr/share/man/man3curses/waddch.3curses=curs_addch.3curses
+s usr/share/man/man3curses/waddchnstr.3curses=curs_addchstr.3curses
+s usr/share/man/man3curses/waddchstr.3curses=curs_addchstr.3curses
+s usr/share/man/man3curses/waddnstr.3curses=curs_addstr.3curses
+s usr/share/man/man3curses/waddnwstr.3curses=curs_addwstr.3curses
+s usr/share/man/man3curses/waddstr.3curses=curs_addstr.3curses
+s usr/share/man/man3curses/waddwch.3curses=curs_addwch.3curses
+s usr/share/man/man3curses/waddwchnstr.3curses=curs_addwchstr.3curses
+s usr/share/man/man3curses/waddwchstr.3curses=curs_addwchstr.3curses
+s usr/share/man/man3curses/waddwstr.3curses=curs_addwstr.3curses
+s usr/share/man/man3curses/wadjcurspos.3curses=curs_alecompat.3curses
+s usr/share/man/man3curses/wattroff.3curses=curs_attr.3curses
+s usr/share/man/man3curses/wattron.3curses=curs_attr.3curses
+s usr/share/man/man3curses/wattrset.3curses=curs_attr.3curses
+s usr/share/man/man3curses/wbkgd.3curses=curs_bkgd.3curses
+s usr/share/man/man3curses/wbkgdset.3curses=curs_bkgd.3curses
+s usr/share/man/man3curses/wborder.3curses=curs_border.3curses
+s usr/share/man/man3curses/wclear.3curses=curs_clear.3curses
+s usr/share/man/man3curses/wclrtobot.3curses=curs_clear.3curses
+s usr/share/man/man3curses/wclrtoeol.3curses=curs_clear.3curses
+s usr/share/man/man3curses/wcursyncup.3curses=curs_window.3curses
+s usr/share/man/man3curses/wdelch.3curses=curs_delch.3curses
+s usr/share/man/man3curses/wdeleteln.3curses=curs_deleteln.3curses
+s usr/share/man/man3curses/wechochar.3curses=curs_addch.3curses
+s usr/share/man/man3curses/wechowchar.3curses=curs_addwch.3curses
+s usr/share/man/man3curses/werase.3curses=curs_clear.3curses
+s usr/share/man/man3curses/wgetch.3curses=curs_getch.3curses
+s usr/share/man/man3curses/wgetnstr.3curses=curs_getstr.3curses
+s usr/share/man/man3curses/wgetnwstr.3curses=curs_getwstr.3curses
+s usr/share/man/man3curses/wgetstr.3curses=curs_getstr.3curses
+s usr/share/man/man3curses/wgetwch.3curses=curs_getwch.3curses
+s usr/share/man/man3curses/wgetwstr.3curses=curs_getwstr.3curses
+s usr/share/man/man3curses/whline.3curses=curs_border.3curses
+s usr/share/man/man3curses/winch.3curses=curs_inch.3curses
+s usr/share/man/man3curses/winchnstr.3curses=curs_inchstr.3curses
+s usr/share/man/man3curses/winchstr.3curses=curs_inchstr.3curses
+s usr/share/man/man3curses/winnstr.3curses=curs_instr.3curses
+s usr/share/man/man3curses/winnwstr.3curses=curs_inwstr.3curses
+s usr/share/man/man3curses/winsch.3curses=curs_insch.3curses
+s usr/share/man/man3curses/winsdelln.3curses=curs_deleteln.3curses
+s usr/share/man/man3curses/winsertln.3curses=curs_deleteln.3curses
+s usr/share/man/man3curses/winsnstr.3curses=curs_insstr.3curses
+s usr/share/man/man3curses/winsnwstr.3curses=curs_inswstr.3curses
+s usr/share/man/man3curses/winsstr.3curses=curs_insstr.3curses
+s usr/share/man/man3curses/winstr.3curses=curs_instr.3curses
+s usr/share/man/man3curses/winswch.3curses=curs_inswch.3curses
+s usr/share/man/man3curses/winswstr.3curses=curs_inswstr.3curses
+s usr/share/man/man3curses/winwch.3curses=curs_inwch.3curses
+s usr/share/man/man3curses/winwchnstr.3curses=curs_inwchstr.3curses
+s usr/share/man/man3curses/winwchstr.3curses=curs_inwchstr.3curses
+s usr/share/man/man3curses/winwstr.3curses=curs_inwstr.3curses
+s usr/share/man/man3curses/wmove.3curses=curs_move.3curses
+s usr/share/man/man3curses/wmovenextch.3curses=curs_alecompat.3curses
+s usr/share/man/man3curses/wmoveprevch.3curses=curs_alecompat.3curses
+s usr/share/man/man3curses/wnoutrefresh.3curses=curs_refresh.3curses
+s usr/share/man/man3curses/wprintw.3curses=curs_printw.3curses
+s usr/share/man/man3curses/wredrawln.3curses=curs_refresh.3curses
+s usr/share/man/man3curses/wrefresh.3curses=curs_refresh.3curses
+s usr/share/man/man3curses/wscanw.3curses=curs_scanw.3curses
+s usr/share/man/man3curses/wscrl.3curses=curs_scroll.3curses
+s usr/share/man/man3curses/wsetscrreg.3curses=curs_outopts.3curses
+s usr/share/man/man3curses/wstandend.3curses=curs_attr.3curses
+s usr/share/man/man3curses/wstandout.3curses=curs_attr.3curses
+s usr/share/man/man3curses/wsyncdown.3curses=curs_window.3curses
+s usr/share/man/man3curses/wsyncup.3curses=curs_window.3curses
+s usr/share/man/man3curses/wtimeout.3curses=curs_inopts.3curses
+s usr/share/man/man3curses/wtouchln.3curses=curs_touch.3curses
+s usr/share/man/man3curses/wvline.3curses=curs_border.3curses
+d usr/share/man/man3dat 0755 root bin
+f usr/share/man/man3dat/dat_cno_create.3dat 0444 root bin
+f usr/share/man/man3dat/dat_cno_free.3dat 0444 root bin
+f usr/share/man/man3dat/dat_cno_modify_agent.3dat 0444 root bin
+f usr/share/man/man3dat/dat_cno_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_cno_wait.3dat 0444 root bin
+f usr/share/man/man3dat/dat_cr_accept.3dat 0444 root bin
+f usr/share/man/man3dat/dat_cr_handoff.3dat 0444 root bin
+f usr/share/man/man3dat/dat_cr_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_cr_reject.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_connect.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_create.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_create_with_srq.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_disconnect.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_dup_connect.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_free.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_get_status.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_modify.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_post_rdma_read.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_post_rdma_write.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_post_recv.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_post_send.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_recv_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_reset.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ep_set_watermark.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_clear_unwaitable.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_dequeue.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_disable.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_enable.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_free.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_modify_cno.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_post_se.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_resize.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_set_unwaitable.3dat 0444 root bin
+f usr/share/man/man3dat/dat_evd_wait.3dat 0444 root bin
+f usr/share/man/man3dat/dat_get_consumer_context.3dat 0444 root bin
+f usr/share/man/man3dat/dat_get_handle_type.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ia_close.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ia_open.3dat 0444 root bin
+f usr/share/man/man3dat/dat_ia_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_lmr_create.3dat 0444 root bin
+f usr/share/man/man3dat/dat_lmr_free.3dat 0444 root bin
+f usr/share/man/man3dat/dat_lmr_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_lmr_sync_rdma_read.3dat 0444 root bin
+f usr/share/man/man3dat/dat_lmr_sync_rdma_write.3dat 0444 root bin
+f usr/share/man/man3dat/dat_provider_fini.3dat 0444 root bin
+f usr/share/man/man3dat/dat_provider_init.3dat 0444 root bin
+f usr/share/man/man3dat/dat_psp_create.3dat 0444 root bin
+f usr/share/man/man3dat/dat_psp_create_any.3dat 0444 root bin
+f usr/share/man/man3dat/dat_psp_free.3dat 0444 root bin
+f usr/share/man/man3dat/dat_psp_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_pz_create.3dat 0444 root bin
+f usr/share/man/man3dat/dat_pz_free.3dat 0444 root bin
+f usr/share/man/man3dat/dat_pz_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_registry_add_provider.3dat 0444 root bin
+f usr/share/man/man3dat/dat_registry_list_providers.3dat 0444 root bin
+f usr/share/man/man3dat/dat_registry_remove_provider.3dat 0444 root bin
+f usr/share/man/man3dat/dat_rmr_bind.3dat 0444 root bin
+f usr/share/man/man3dat/dat_rmr_create.3dat 0444 root bin
+f usr/share/man/man3dat/dat_rmr_free.3dat 0444 root bin
+f usr/share/man/man3dat/dat_rmr_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_rsp_create.3dat 0444 root bin
+f usr/share/man/man3dat/dat_rsp_free.3dat 0444 root bin
+f usr/share/man/man3dat/dat_rsp_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_set_consumer_context.3dat 0444 root bin
+f usr/share/man/man3dat/dat_srq_create.3dat 0444 root bin
+f usr/share/man/man3dat/dat_srq_free.3dat 0444 root bin
+f usr/share/man/man3dat/dat_srq_post_recv.3dat 0444 root bin
+f usr/share/man/man3dat/dat_srq_query.3dat 0444 root bin
+f usr/share/man/man3dat/dat_srq_resize.3dat 0444 root bin
+f usr/share/man/man3dat/dat_srq_set_lw.3dat 0444 root bin
+f usr/share/man/man3dat/dat_strerror.3dat 0444 root bin
+d usr/share/man/man3devid 0755 root bin
+s usr/share/man/man3devid/devid_compare.3devid=devid_get.3devid
+s usr/share/man/man3devid/devid_deviceid_to_nmlist.3devid=devid_get.3devid
+s usr/share/man/man3devid/devid_free.3devid=devid_get.3devid
+s usr/share/man/man3devid/devid_free_nmlist.3devid=devid_get.3devid
+f usr/share/man/man3devid/devid_get.3devid 0444 root bin
+s usr/share/man/man3devid/devid_get_minor_name.3devid=devid_get.3devid
+s usr/share/man/man3devid/devid_sizeof.3devid=devid_get.3devid
+s usr/share/man/man3devid/devid_str_decode.3devid=devid_get.3devid
+s usr/share/man/man3devid/devid_str_encode.3devid=devid_get.3devid
+s usr/share/man/man3devid/devid_str_free.3devid=devid_get.3devid
+s usr/share/man/man3devid/devid_str_from_path.3devid=devid_get.3devid
+s usr/share/man/man3devid/devid_valid.3devid=devid_get.3devid
+d usr/share/man/man3devinfo 0755 root bin
+f usr/share/man/man3devinfo/di_binding_name.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_bus_addr.3devinfo=di_binding_name.3devinfo
+f usr/share/man/man3devinfo/di_child_node.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_compatible_names.3devinfo=di_binding_name.3devinfo
+s usr/share/man/man3devinfo/di_devfs_minor_path.3devinfo=di_devfs_path.3devinfo
+f usr/share/man/man3devinfo/di_devfs_path.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_devfs_path_free.3devinfo=di_devfs_path.3devinfo
+s usr/share/man/man3devinfo/di_devid.3devinfo=di_binding_name.3devinfo
+s usr/share/man/man3devinfo/di_devlink_content.3devinfo=di_devlink_path.3devinfo
+f usr/share/man/man3devinfo/di_devlink_dup.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_devlink_fini.3devinfo=di_devlink_init.3devinfo
+s usr/share/man/man3devinfo/di_devlink_free.3devinfo=di_devlink_dup.3devinfo
+f usr/share/man/man3devinfo/di_devlink_init.3devinfo 0444 root bin
+f usr/share/man/man3devinfo/di_devlink_path.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_devlink_type.3devinfo=di_devlink_path.3devinfo
+f usr/share/man/man3devinfo/di_devlink_walk.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_driver_major.3devinfo=di_binding_name.3devinfo
+s usr/share/man/man3devinfo/di_driver_name.3devinfo=di_binding_name.3devinfo
+s usr/share/man/man3devinfo/di_driver_ops.3devinfo=di_binding_name.3devinfo
+s usr/share/man/man3devinfo/di_drv_first_node.3devinfo=di_child_node.3devinfo
+s usr/share/man/man3devinfo/di_drv_next_node.3devinfo=di_child_node.3devinfo
+s usr/share/man/man3devinfo/di_fini.3devinfo=di_init.3devinfo
+f usr/share/man/man3devinfo/di_init.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_instance.3devinfo=di_binding_name.3devinfo
+s usr/share/man/man3devinfo/di_link_next_by_lnode.3devinfo=di_link_next_by_node.3devinfo
+f usr/share/man/man3devinfo/di_link_next_by_node.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_link_private_get.3devinfo=di_node_private_set.3devinfo
+s usr/share/man/man3devinfo/di_link_private_set.3devinfo=di_node_private_set.3devinfo
+f usr/share/man/man3devinfo/di_link_spectype.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_link_to_lnode.3devinfo=di_link_spectype.3devinfo
+s usr/share/man/man3devinfo/di_lnode_devinfo.3devinfo=di_lnode_name.3devinfo
+s usr/share/man/man3devinfo/di_lnode_devt.3devinfo=di_lnode_name.3devinfo
+f usr/share/man/man3devinfo/di_lnode_name.3devinfo 0444 root bin
+f usr/share/man/man3devinfo/di_lnode_next.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_lnode_private_get.3devinfo=di_node_private_set.3devinfo
+s usr/share/man/man3devinfo/di_lnode_private_set.3devinfo=di_node_private_set.3devinfo
+f usr/share/man/man3devinfo/di_minor_devt.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_minor_name.3devinfo=di_minor_devt.3devinfo
+f usr/share/man/man3devinfo/di_minor_next.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_minor_nodetype.3devinfo=di_minor_devt.3devinfo
+s usr/share/man/man3devinfo/di_minor_private_get.3devinfo=di_node_private_set.3devinfo
+s usr/share/man/man3devinfo/di_minor_private_set.3devinfo=di_node_private_set.3devinfo
+s usr/share/man/man3devinfo/di_minor_spectype.3devinfo=di_minor_devt.3devinfo
+s usr/share/man/man3devinfo/di_node_name.3devinfo=di_binding_name.3devinfo
+s usr/share/man/man3devinfo/di_node_private_get.3devinfo=di_node_private_set.3devinfo
+f usr/share/man/man3devinfo/di_node_private_set.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_nodeid.3devinfo=di_binding_name.3devinfo
+s usr/share/man/man3devinfo/di_parent_node.3devinfo=di_child_node.3devinfo
+f usr/share/man/man3devinfo/di_path_bus_addr.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_path_client_devfs_path.3devinfo=di_devfs_path.3devinfo
+f usr/share/man/man3devinfo/di_path_client_next_path.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_path_client_node.3devinfo=di_path_bus_addr.3devinfo
+s usr/share/man/man3devinfo/di_path_devfs_path.3devinfo=di_devfs_path.3devinfo
+s usr/share/man/man3devinfo/di_path_instance.3devinfo=di_path_bus_addr.3devinfo
+s usr/share/man/man3devinfo/di_path_node_name.3devinfo=di_path_bus_addr.3devinfo
+s usr/share/man/man3devinfo/di_path_phci_next_path.3devinfo=di_path_client_next_path.3devinfo
+s usr/share/man/man3devinfo/di_path_phci_node.3devinfo=di_path_bus_addr.3devinfo
+s usr/share/man/man3devinfo/di_path_private_get.3devinfo=di_node_private_set.3devinfo
+s usr/share/man/man3devinfo/di_path_private_set.3devinfo=di_node_private_set.3devinfo
+f usr/share/man/man3devinfo/di_path_prop_bytes.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_path_prop_int64s.3devinfo=di_path_prop_bytes.3devinfo
+s usr/share/man/man3devinfo/di_path_prop_ints.3devinfo=di_path_prop_bytes.3devinfo
+f usr/share/man/man3devinfo/di_path_prop_lookup_bytes.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_path_prop_lookup_int64s.3devinfo=di_path_prop_lookup_bytes.3devinfo
+s usr/share/man/man3devinfo/di_path_prop_lookup_ints.3devinfo=di_path_prop_lookup_bytes.3devinfo
+s usr/share/man/man3devinfo/di_path_prop_lookup_strings.3devinfo=di_path_prop_lookup_bytes.3devinfo
+s usr/share/man/man3devinfo/di_path_prop_name.3devinfo=di_path_prop_bytes.3devinfo
+f usr/share/man/man3devinfo/di_path_prop_next.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_path_prop_strings.3devinfo=di_path_prop_bytes.3devinfo
+s usr/share/man/man3devinfo/di_path_prop_type.3devinfo=di_path_prop_bytes.3devinfo
+s usr/share/man/man3devinfo/di_path_state.3devinfo=di_path_bus_addr.3devinfo
+s usr/share/man/man3devinfo/di_prom_fini.3devinfo=di_prom_init.3devinfo
+f usr/share/man/man3devinfo/di_prom_init.3devinfo 0444 root bin
+f usr/share/man/man3devinfo/di_prom_prop_data.3devinfo 0444 root bin
+f usr/share/man/man3devinfo/di_prom_prop_lookup_bytes.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_prom_prop_lookup_ints.3devinfo=di_prom_prop_lookup_bytes.3devinfo
+s usr/share/man/man3devinfo/di_prom_prop_lookup_strings.3devinfo=di_prom_prop_lookup_bytes.3devinfo
+s usr/share/man/man3devinfo/di_prom_prop_name.3devinfo=di_prom_prop_data.3devinfo
+s usr/share/man/man3devinfo/di_prom_prop_next.3devinfo=di_prom_prop_data.3devinfo
+f usr/share/man/man3devinfo/di_prop_bytes.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_prop_devt.3devinfo=di_prop_bytes.3devinfo
+s usr/share/man/man3devinfo/di_prop_int64.3devinfo=di_prop_bytes.3devinfo
+s usr/share/man/man3devinfo/di_prop_ints.3devinfo=di_prop_bytes.3devinfo
+f usr/share/man/man3devinfo/di_prop_lookup_bytes.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_prop_lookup_int64.3devinfo=di_prop_lookup_bytes.3devinfo
+s usr/share/man/man3devinfo/di_prop_lookup_ints.3devinfo=di_prop_lookup_bytes.3devinfo
+s usr/share/man/man3devinfo/di_prop_lookup_strings.3devinfo=di_prop_lookup_bytes.3devinfo
+s usr/share/man/man3devinfo/di_prop_name.3devinfo=di_prop_bytes.3devinfo
+f usr/share/man/man3devinfo/di_prop_next.3devinfo 0444 root bin
+s usr/share/man/man3devinfo/di_prop_strings.3devinfo=di_prop_bytes.3devinfo
+s usr/share/man/man3devinfo/di_prop_type.3devinfo=di_prop_bytes.3devinfo
+s usr/share/man/man3devinfo/di_sibling_node.3devinfo=di_child_node.3devinfo
+f usr/share/man/man3devinfo/di_walk_link.3devinfo 0444 root bin
+f usr/share/man/man3devinfo/di_walk_lnode.3devinfo 0444 root bin
+f usr/share/man/man3devinfo/di_walk_minor.3devinfo 0444 root bin
+f usr/share/man/man3devinfo/di_walk_node.3devinfo 0444 root bin
+d usr/share/man/man3dlpi 0755 root bin
+f usr/share/man/man3dlpi/dlpi_arptype.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_bind.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_close.3dlpi 0444 root bin
+s usr/share/man/man3dlpi/dlpi_disabmulti.3dlpi=dlpi_enabmulti.3dlpi
+f usr/share/man/man3dlpi/dlpi_disabnotify.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_enabmulti.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_enabnotify.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_fd.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_get_physaddr.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_iftype.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_info.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_linkname.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_mactype.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_open.3dlpi 0444 root bin
+s usr/share/man/man3dlpi/dlpi_open_zone.3dlpi=dlpi_open.3dlpi
+s usr/share/man/man3dlpi/dlpi_promiscoff.3dlpi=dlpi_promiscon.3dlpi
+f usr/share/man/man3dlpi/dlpi_promiscon.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_recv.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_send.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_set_physaddr.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_set_timeout.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_strerror.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_unbind.3dlpi 0444 root bin
+f usr/share/man/man3dlpi/dlpi_walk.3dlpi 0444 root bin
+d usr/share/man/man3dns_sd 0755 root bin
+s usr/share/man/man3dns_sd/DNSServiceAddRecord.3dns_sd=DNSServiceCreateConnection.3dns_sd
+f usr/share/man/man3dns_sd/DNSServiceBrowse.3dns_sd 0444 root bin
+f usr/share/man/man3dns_sd/DNSServiceConstructFullName.3dns_sd 0444 root bin
+f usr/share/man/man3dns_sd/DNSServiceCreateConnection.3dns_sd 0444 root bin
+f usr/share/man/man3dns_sd/DNSServiceEnumerateDomains.3dns_sd 0444 root bin
+f usr/share/man/man3dns_sd/DNSServiceProcessResult.3dns_sd 0444 root bin
+f usr/share/man/man3dns_sd/DNSServiceQueryRecord.3dns_sd 0444 root bin
+f usr/share/man/man3dns_sd/DNSServiceReconfirmRecord.3dns_sd 0444 root bin
+f usr/share/man/man3dns_sd/DNSServiceRefDeallocate.3dns_sd 0444 root bin
+f usr/share/man/man3dns_sd/DNSServiceRefSockFD.3dns_sd 0444 root bin
+f usr/share/man/man3dns_sd/DNSServiceRegister.3dns_sd 0444 root bin
+s usr/share/man/man3dns_sd/DNSServiceRegisterRecord.3dns_sd=DNSServiceCreateConnection.3dns_sd
+s usr/share/man/man3dns_sd/DNSServiceRemoveRecord.3dns_sd=DNSServiceCreateConnection.3dns_sd
+f usr/share/man/man3dns_sd/DNSServiceResolve.3dns_sd 0444 root bin
+s usr/share/man/man3dns_sd/DNSServiceUpdateRecord.3dns_sd=DNSServiceCreateConnection.3dns_sd
+s usr/share/man/man3dns_sd/TXTRecordContainsKey.3dns_sd=TXTRecordCreate.3dns_sd
+f usr/share/man/man3dns_sd/TXTRecordCreate.3dns_sd 0444 root bin
+s usr/share/man/man3dns_sd/TXTRecordDeallocate.3dns_sd=TXTRecordCreate.3dns_sd
+s usr/share/man/man3dns_sd/TXTRecordGetBytesPtr.3dns_sd=TXTRecordCreate.3dns_sd
+s usr/share/man/man3dns_sd/TXTRecordGetCount.3dns_sd=TXTRecordCreate.3dns_sd
+s usr/share/man/man3dns_sd/TXTRecordGetItemAtIndex.3dns_sd=TXTRecordCreate.3dns_sd
+s usr/share/man/man3dns_sd/TXTRecordGetLength.3dns_sd=TXTRecordCreate.3dns_sd
+s usr/share/man/man3dns_sd/TXTRecordGetValuePtr.3dns_sd=TXTRecordCreate.3dns_sd
+s usr/share/man/man3dns_sd/TXTRecordRemoveValue.3dns_sd=TXTRecordCreate.3dns_sd
+s usr/share/man/man3dns_sd/TXTRecordSetValue.3dns_sd=TXTRecordCreate.3dns_sd
+d usr/share/man/man3elf 0755 root bin
+f usr/share/man/man3elf/elf.3elf 0444 root bin
+f usr/share/man/man3elf/elf32_checksum.3elf 0444 root bin
+f usr/share/man/man3elf/elf32_fsize.3elf 0444 root bin
+f usr/share/man/man3elf/elf32_getehdr.3elf 0444 root bin
+f usr/share/man/man3elf/elf32_getphdr.3elf 0444 root bin
+f usr/share/man/man3elf/elf32_getshdr.3elf 0444 root bin
+s usr/share/man/man3elf/elf32_newehdr.3elf=elf32_getehdr.3elf
+s usr/share/man/man3elf/elf32_newphdr.3elf=elf32_getphdr.3elf
+f usr/share/man/man3elf/elf32_xlatetof.3elf 0444 root bin
+s usr/share/man/man3elf/elf32_xlatetom.3elf=elf32_xlatetof.3elf
+s usr/share/man/man3elf/elf64_checksum.3elf=elf32_checksum.3elf
+s usr/share/man/man3elf/elf64_fsize.3elf=elf32_fsize.3elf
+s usr/share/man/man3elf/elf64_getehdr.3elf=elf32_getehdr.3elf
+s usr/share/man/man3elf/elf64_getphdr.3elf=elf32_getphdr.3elf
+s usr/share/man/man3elf/elf64_getshdr.3elf=elf32_getshdr.3elf
+s usr/share/man/man3elf/elf64_newehdr.3elf=elf32_getehdr.3elf
+s usr/share/man/man3elf/elf64_newphdr.3elf=elf32_getphdr.3elf
+s usr/share/man/man3elf/elf64_xlatetof.3elf=elf32_xlatetof.3elf
+s usr/share/man/man3elf/elf64_xlatetom.3elf=elf32_xlatetof.3elf
+f usr/share/man/man3elf/elf_begin.3elf 0444 root bin
+f usr/share/man/man3elf/elf_cntl.3elf 0444 root bin
+s usr/share/man/man3elf/elf_end.3elf=elf_begin.3elf
+f usr/share/man/man3elf/elf_errmsg.3elf 0444 root bin
+s usr/share/man/man3elf/elf_errno.3elf=elf_errmsg.3elf
+f usr/share/man/man3elf/elf_fill.3elf 0444 root bin
+f usr/share/man/man3elf/elf_flagdata.3elf 0444 root bin
+s usr/share/man/man3elf/elf_flagehdr.3elf=elf_flagdata.3elf
+s usr/share/man/man3elf/elf_flagelf.3elf=elf_flagdata.3elf
+s usr/share/man/man3elf/elf_flagphdr.3elf=elf_flagdata.3elf
+s usr/share/man/man3elf/elf_flagscn.3elf=elf_flagdata.3elf
+s usr/share/man/man3elf/elf_flagshdr.3elf=elf_flagdata.3elf
+f usr/share/man/man3elf/elf_getarhdr.3elf 0444 root bin
+f usr/share/man/man3elf/elf_getarsym.3elf 0444 root bin
+f usr/share/man/man3elf/elf_getbase.3elf 0444 root bin
+f usr/share/man/man3elf/elf_getdata.3elf 0444 root bin
+f usr/share/man/man3elf/elf_getident.3elf 0444 root bin
+s usr/share/man/man3elf/elf_getphdrnum.3elf=elf_getident.3elf
+s usr/share/man/man3elf/elf_getphnum.3elf=elf_getident.3elf
+f usr/share/man/man3elf/elf_getscn.3elf 0444 root bin
+s usr/share/man/man3elf/elf_getshdrnum.3elf=elf_getident.3elf
+s usr/share/man/man3elf/elf_getshdrstrndx.3elf=elf_getident.3elf
+s usr/share/man/man3elf/elf_getshnum.3elf=elf_getident.3elf
+s usr/share/man/man3elf/elf_getshstrndx.3elf=elf_getident.3elf
+f usr/share/man/man3elf/elf_hash.3elf 0444 root bin
+f usr/share/man/man3elf/elf_kind.3elf 0444 root bin
+s usr/share/man/man3elf/elf_memory.3elf=elf_begin.3elf
+s usr/share/man/man3elf/elf_ndxscn.3elf=elf_getscn.3elf
+s usr/share/man/man3elf/elf_newdata.3elf=elf_getdata.3elf
+s usr/share/man/man3elf/elf_newscn.3elf=elf_getscn.3elf
+s usr/share/man/man3elf/elf_next.3elf=elf_begin.3elf
+s usr/share/man/man3elf/elf_nextscn.3elf=elf_getscn.3elf
+s usr/share/man/man3elf/elf_rand.3elf=elf_begin.3elf
+s usr/share/man/man3elf/elf_rawdata.3elf=elf_getdata.3elf
+f usr/share/man/man3elf/elf_rawfile.3elf 0444 root bin
+f usr/share/man/man3elf/elf_strptr.3elf 0444 root bin
+f usr/share/man/man3elf/elf_update.3elf 0444 root bin
+f usr/share/man/man3elf/elf_version.3elf 0444 root bin
+f usr/share/man/man3elf/gelf.3elf 0444 root bin
+s usr/share/man/man3elf/gelf_checksum.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_fsize.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getcap.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getclass.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getdyn.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getehdr.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getmove.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getphdr.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getrel.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getrela.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getshdr.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getsym.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getsyminfo.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_getsymshndx.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_newehdr.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_newphdr.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_cap.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_dyn.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_ehdr.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_getmove.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_move.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_phdr.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_rel.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_rela.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_shdr.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_sym.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_syminfo.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_update_symshndx.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_xlatetof.3elf=gelf.3elf
+s usr/share/man/man3elf/gelf_xlatetom.3elf=gelf.3elf
+f usr/share/man/man3elf/nlist.3elf 0444 root bin
+d usr/share/man/man3exacct 0755 root bin
+s usr/share/man/man3exacct/ea_alloc.3exacct=ea_set_item.3exacct
+s usr/share/man/man3exacct/ea_attach_to_group.3exacct=ea_set_item.3exacct
+s usr/share/man/man3exacct/ea_attach_to_object.3exacct=ea_set_item.3exacct
+s usr/share/man/man3exacct/ea_close.3exacct=ea_open.3exacct
+s usr/share/man/man3exacct/ea_copy_object.3exacct=ea_pack_object.3exacct
+s usr/share/man/man3exacct/ea_copy_object_tree.3exacct=ea_pack_object.3exacct
+f usr/share/man/man3exacct/ea_error.3exacct 0444 root bin
+s usr/share/man/man3exacct/ea_free.3exacct=ea_set_item.3exacct
+s usr/share/man/man3exacct/ea_free_item.3exacct=ea_set_item.3exacct
+s usr/share/man/man3exacct/ea_free_object.3exacct=ea_set_item.3exacct
+s usr/share/man/man3exacct/ea_get_creator.3exacct=ea_pack_object.3exacct
+s usr/share/man/man3exacct/ea_get_hostname.3exacct=ea_pack_object.3exacct
+s usr/share/man/man3exacct/ea_get_object.3exacct=ea_pack_object.3exacct
+s usr/share/man/man3exacct/ea_get_object_tree.3exacct=ea_pack_object.3exacct
+s usr/share/man/man3exacct/ea_match_object_catalog.3exacct=ea_set_item.3exacct
+s usr/share/man/man3exacct/ea_next_object.3exacct=ea_pack_object.3exacct
+f usr/share/man/man3exacct/ea_open.3exacct 0444 root bin
+f usr/share/man/man3exacct/ea_pack_object.3exacct 0444 root bin
+s usr/share/man/man3exacct/ea_previous_object.3exacct=ea_pack_object.3exacct
+s usr/share/man/man3exacct/ea_set_group.3exacct=ea_set_item.3exacct
+f usr/share/man/man3exacct/ea_set_item.3exacct 0444 root bin
+s usr/share/man/man3exacct/ea_strdup.3exacct=ea_set_item.3exacct
+s usr/share/man/man3exacct/ea_strfree.3exacct=ea_set_item.3exacct
+s usr/share/man/man3exacct/ea_unpack_object.3exacct=ea_pack_object.3exacct
+s usr/share/man/man3exacct/ea_write_object.3exacct=ea_pack_object.3exacct
+d usr/share/man/man3ext 0755 root bin
+s usr/share/man/man3ext/DES_FAILED.3ext=ecb_crypt.3ext
+s usr/share/man/man3ext/MD4Final.3ext=md4.3ext
+s usr/share/man/man3ext/MD4Init.3ext=md4.3ext
+s usr/share/man/man3ext/MD4Update.3ext=md4.3ext
+s usr/share/man/man3ext/MD5Final.3ext=md5.3ext
+s usr/share/man/man3ext/MD5Init.3ext=md5.3ext
+s usr/share/man/man3ext/MD5Update.3ext=md5.3ext
+f usr/share/man/man3ext/NOTE.3ext 0444 root bin
+s usr/share/man/man3ext/SHA1Final.3ext=sha1.3ext
+s usr/share/man/man3ext/SHA1Init.3ext=sha1.3ext
+s usr/share/man/man3ext/SHA1Update.3ext=sha1.3ext
+s usr/share/man/man3ext/SHA256Final.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA256Init.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA256Update.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA2Final.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA2Init.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA2Update.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA384Final.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA384Init.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA384Update.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA512Final.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA512Init.3ext=sha2.3ext
+s usr/share/man/man3ext/SHA512Update.3ext=sha2.3ext
+f usr/share/man/man3ext/SUNW_C_GetMechSession.3ext 0444 root bin
+s usr/share/man/man3ext/SUNW_C_KeyToObject.3ext=SUNW_C_GetMechSession.3ext
+s usr/share/man/man3ext/_NOTE.3ext=NOTE.3ext
+s usr/share/man/man3ext/cbc_crypt.3ext=ecb_crypt.3ext
+s usr/share/man/man3ext/cplus_demangle.3ext=demangle.3ext
+f usr/share/man/man3ext/crypt.3ext 0444 root bin
+s usr/share/man/man3ext/crypt_close.3ext=crypt.3ext
+f usr/share/man/man3ext/demangle.3ext 0444 root bin
+s usr/share/man/man3ext/des_crypt.3ext=crypt.3ext
+s usr/share/man/man3ext/des_encrypt.3ext=crypt.3ext
+s usr/share/man/man3ext/des_setkey.3ext=crypt.3ext
+s usr/share/man/man3ext/des_setparity.3ext=ecb_crypt.3ext
+f usr/share/man/man3ext/ecb_crypt.3ext 0444 root bin
+f usr/share/man/man3ext/efi_alloc_and_init.3ext 0444 root bin
+s usr/share/man/man3ext/efi_alloc_and_read.3ext=efi_alloc_and_init.3ext
+s usr/share/man/man3ext/efi_free.3ext=efi_alloc_and_init.3ext
+s usr/share/man/man3ext/efi_reserved_sectors.3ext=efi_alloc_and_init.3ext
+s usr/share/man/man3ext/efi_use_whole_disk.3ext=efi_alloc_and_init.3ext
+s usr/share/man/man3ext/efi_write.3ext=efi_alloc_and_init.3ext
+s usr/share/man/man3ext/encrypt.3ext=crypt.3ext
+s usr/share/man/man3ext/la_activity.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_amd64_pltenter.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_i86_pltenter.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_objfilter.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_objopen.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_objsearch.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_pltexit.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_pltexit64.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_preinit.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_sparcv8_pltenter.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_sparcv9_pltenter.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_symbind32.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_symbind64.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/la_version.3ext=rtld_audit.3ext
+s usr/share/man/man3ext/ld_atexit.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_atexit64.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_file.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_file64.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_input_done.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_input_section.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_input_section64.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_open.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_open64.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_section.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_section64.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_start.3ext=ld_support.3ext
+s usr/share/man/man3ext/ld_start64.3ext=ld_support.3ext
+f usr/share/man/man3ext/ld_support.3ext 0444 root bin
+s usr/share/man/man3ext/ld_version.3ext=ld_support.3ext
+f usr/share/man/man3ext/md4.3ext 0444 root bin
+f usr/share/man/man3ext/md5.3ext 0444 root bin
+s usr/share/man/man3ext/md5_calc.3ext=md5.3ext
+s usr/share/man/man3ext/rd_delete.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_errstr.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_event_addr.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_event_enable.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_event_getmsg.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_init.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_loadobj_iter.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_log.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_new.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_objpad_enable.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_plt_resolution.3ext=rtld_db.3ext
+s usr/share/man/man3ext/rd_reset.3ext=rtld_db.3ext
+f usr/share/man/man3ext/read_vtoc.3ext 0444 root bin
+f usr/share/man/man3ext/rtld_audit.3ext 0444 root bin
+f usr/share/man/man3ext/rtld_db.3ext 0444 root bin
+s usr/share/man/man3ext/run_crypt.3ext=crypt.3ext
+s usr/share/man/man3ext/run_setkey.3ext=crypt.3ext
+f usr/share/man/man3ext/sendfile.3ext 0444 root bin
+f usr/share/man/man3ext/sendfilev.3ext 0444 root bin
+s usr/share/man/man3ext/setkey.3ext=crypt.3ext
+f usr/share/man/man3ext/sha1.3ext 0444 root bin
+f usr/share/man/man3ext/sha2.3ext 0444 root bin
+f usr/share/man/man3ext/stdarg.3ext 0444 root bin
+f usr/share/man/man3ext/tsalarm_get.3ext 0444 root bin
+s usr/share/man/man3ext/tsalarm_set.3ext=tsalarm_get.3ext
+f usr/share/man/man3ext/varargs.3ext 0444 root bin
+s usr/share/man/man3ext/write_vtoc.3ext=read_vtoc.3ext
+d usr/share/man/man3fcoe 0755 root bin
+f usr/share/man/man3fcoe/FCOE_CreatePort.3fcoe 0444 root bin
+f usr/share/man/man3fcoe/FCOE_DeletePort.3fcoe 0444 root bin
+f usr/share/man/man3fcoe/FCOE_GetPortList.3fcoe 0444 root bin
+d usr/share/man/man3fstyp 0755 root bin
+s usr/share/man/man3fstyp/fstyp_fini.3fstyp=fstyp_init.3fstyp
+f usr/share/man/man3fstyp/fstyp_get_attr.3fstyp 0444 root bin
+f usr/share/man/man3fstyp/fstyp_ident.3fstyp 0444 root bin
+f usr/share/man/man3fstyp/fstyp_init.3fstyp 0444 root bin
+s usr/share/man/man3fstyp/fstyp_mod_dump.3fstyp=fstyp_mod_init.3fstyp
+s usr/share/man/man3fstyp/fstyp_mod_fini.3fstyp=fstyp_mod_init.3fstyp
+s usr/share/man/man3fstyp/fstyp_mod_get_attr.3fstyp=fstyp_mod_init.3fstyp
+s usr/share/man/man3fstyp/fstyp_mod_ident.3fstyp=fstyp_mod_init.3fstyp
+f usr/share/man/man3fstyp/fstyp_mod_init.3fstyp 0444 root bin
+f usr/share/man/man3fstyp/fstyp_strerror.3fstyp 0444 root bin
+d usr/share/man/man3gen 0755 root bin
+s usr/share/man/man3gen/advance.3gen=regexpr.3gen
+f usr/share/man/man3gen/bgets.3gen 0444 root bin
+f usr/share/man/man3gen/bufsplit.3gen 0444 root bin
+s usr/share/man/man3gen/compile.3gen=regexpr.3gen
+f usr/share/man/man3gen/copylist.3gen 0444 root bin
+f usr/share/man/man3gen/gmatch.3gen 0444 root bin
+f usr/share/man/man3gen/isencrypt.3gen 0444 root bin
+f usr/share/man/man3gen/mkdirp.3gen 0444 root bin
+s usr/share/man/man3gen/p2close.3gen=p2open.3gen
+f usr/share/man/man3gen/p2open.3gen 0444 root bin
+f usr/share/man/man3gen/pathfind.3gen 0444 root bin
+f usr/share/man/man3gen/regexpr.3gen 0444 root bin
+s usr/share/man/man3gen/rmdirp.3gen=mkdirp.3gen
+s usr/share/man/man3gen/step.3gen=regexpr.3gen
+s usr/share/man/man3gen/str.3gen=strfind.3gen
+s usr/share/man/man3gen/strcadd.3gen=strccpy.3gen
+f usr/share/man/man3gen/strccpy.3gen 0444 root bin
+s usr/share/man/man3gen/streadd.3gen=strccpy.3gen
+s usr/share/man/man3gen/strecpy.3gen=strccpy.3gen
+f usr/share/man/man3gen/strfind.3gen 0444 root bin
+s usr/share/man/man3gen/strrspn.3gen=strfind.3gen
+s usr/share/man/man3gen/strtrns.3gen=strfind.3gen
+d usr/share/man/man3gss 0755 root bin
+f usr/share/man/man3gss/gss_accept_sec_context.3gss 0444 root bin
+f usr/share/man/man3gss/gss_acquire_cred.3gss 0444 root bin
+f usr/share/man/man3gss/gss_add_cred.3gss 0444 root bin
+f usr/share/man/man3gss/gss_add_oid_set_member.3gss 0444 root bin
+f usr/share/man/man3gss/gss_canonicalize_name.3gss 0444 root bin
+f usr/share/man/man3gss/gss_compare_name.3gss 0444 root bin
+f usr/share/man/man3gss/gss_context_time.3gss 0444 root bin
+f usr/share/man/man3gss/gss_create_empty_oid_set.3gss 0444 root bin
+f usr/share/man/man3gss/gss_delete_sec_context.3gss 0444 root bin
+f usr/share/man/man3gss/gss_display_name.3gss 0444 root bin
+f usr/share/man/man3gss/gss_display_status.3gss 0444 root bin
+f usr/share/man/man3gss/gss_duplicate_name.3gss 0444 root bin
+f usr/share/man/man3gss/gss_export_name.3gss 0444 root bin
+f usr/share/man/man3gss/gss_export_sec_context.3gss 0444 root bin
+f usr/share/man/man3gss/gss_get_mic.3gss 0444 root bin
+f usr/share/man/man3gss/gss_import_name.3gss 0444 root bin
+f usr/share/man/man3gss/gss_import_sec_context.3gss 0444 root bin
+f usr/share/man/man3gss/gss_indicate_mechs.3gss 0444 root bin
+f usr/share/man/man3gss/gss_init_sec_context.3gss 0444 root bin
+f usr/share/man/man3gss/gss_inquire_context.3gss 0444 root bin
+f usr/share/man/man3gss/gss_inquire_cred.3gss 0444 root bin
+f usr/share/man/man3gss/gss_inquire_cred_by_mech.3gss 0444 root bin
+f usr/share/man/man3gss/gss_inquire_mechs_for_name.3gss 0444 root bin
+f usr/share/man/man3gss/gss_inquire_names_for_mech.3gss 0444 root bin
+f usr/share/man/man3gss/gss_oid_to_str.3gss 0444 root bin
+f usr/share/man/man3gss/gss_process_context_token.3gss 0444 root bin
+f usr/share/man/man3gss/gss_release_buffer.3gss 0444 root bin
+f usr/share/man/man3gss/gss_release_cred.3gss 0444 root bin
+f usr/share/man/man3gss/gss_release_name.3gss 0444 root bin
+f usr/share/man/man3gss/gss_release_oid.3gss 0444 root bin
+f usr/share/man/man3gss/gss_release_oid_set.3gss 0444 root bin
+f usr/share/man/man3gss/gss_store_cred.3gss 0444 root bin
+f usr/share/man/man3gss/gss_str_to_oid.3gss 0444 root bin
+f usr/share/man/man3gss/gss_test_oid_set_member.3gss 0444 root bin
+f usr/share/man/man3gss/gss_unwrap.3gss 0444 root bin
+f usr/share/man/man3gss/gss_verify_mic.3gss 0444 root bin
+f usr/share/man/man3gss/gss_wrap.3gss 0444 root bin
+f usr/share/man/man3gss/gss_wrap_size_limit.3gss 0444 root bin
+d usr/share/man/man3head 0755 root bin
+s usr/share/man/man3head/CMSG_DATA.3head=socket.h.3head
+s usr/share/man/man3head/CMSG_FIRSTHDR.3head=socket.h.3head
+s usr/share/man/man3head/CMSG_LEN.3head=socket.h.3head
+s usr/share/man/man3head/CMSG_NXTHDR.3head=socket.h.3head
+s usr/share/man/man3head/CMSG_SPACE.3head=socket.h.3head
+s usr/share/man/man3head/LIST_CLASS_ENTRY.3head=queue.h.3head
+s usr/share/man/man3head/LIST_CLASS_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/LIST_CONCAT.3head=queue.h.3head
+s usr/share/man/man3head/LIST_EMPTY.3head=queue.h.3head
+s usr/share/man/man3head/LIST_ENTRY.3head=queue.h.3head
+s usr/share/man/man3head/LIST_FIRST.3head=queue.h.3head
+s usr/share/man/man3head/LIST_FOREACH.3head=queue.h.3head
+s usr/share/man/man3head/LIST_FOREACH_FROM.3head=queue.h.3head
+s usr/share/man/man3head/LIST_FOREACH_FROM_SAFE.3head=queue.h.3head
+s usr/share/man/man3head/LIST_FOREACH_SAFE.3head=queue.h.3head
+s usr/share/man/man3head/LIST_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/LIST_HEAD_INITIALIZER.3head=queue.h.3head
+s usr/share/man/man3head/LIST_INIT.3head=queue.h.3head
+s usr/share/man/man3head/LIST_INSERT_AFTER.3head=queue.h.3head
+s usr/share/man/man3head/LIST_INSERT_BEFORE.3head=queue.h.3head
+s usr/share/man/man3head/LIST_INSERT_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/LIST_NEXT.3head=queue.h.3head
+s usr/share/man/man3head/LIST_PREV.3head=queue.h.3head
+s usr/share/man/man3head/LIST_REMOVE.3head=queue.h.3head
+s usr/share/man/man3head/LIST_SWAP.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_CLASS_ENTRY.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_CLASS_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_CONCAT.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_EMPTY.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_ENTRY.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_FIRST.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_FOREACH.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_FOREACH_FROM.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_FOREACH_FROM_SAFE.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_FOREACH_SAFE.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_HEAD_INITIALIZER.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_INIT.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_INSERT_AFTER.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_INSERT_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_NEXT.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_REMOVE.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_REMOVE_AFTER.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_REMOVE_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/SLIST_SWAP.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_CLASS_ENTRY.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_CLASS_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_CONCAT.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_EMPTY.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_ENTRY.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_FIRST.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_FOREACH.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_FOREACH_FROM.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_FOREACH_FROM_SAFE.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_FOREACH_SAFE.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_HEAD_INITIALIZER.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_INIT.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_INSERT_AFTER.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_INSERT_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_INSERT_TAIL.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_LAST.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_NEXT.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_REMOVE.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_REMOVE_AFTER.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_REMOVE_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/STAILQ_SWAP.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_CLASS_ENTRY.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_CLASS_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_CONCAT.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_EMPTY.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_ENTRY.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_FIRST.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_FOREACH.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_FOREACH_FROM.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_FOREACH_FROM_SAFE.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_FOREACH_REVERSE.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_FOREACH_REVERSE_FROM.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_FOREACH_REVERSE_FROM_SAFE.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_FOREACH_REVERSE_SAFE.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_FOREACH_SAFE.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_HEAD_INITIALIZER.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_INIT.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_INSERT_AFTER.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_INSERT_BEFORE.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_INSERT_HEAD.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_INSERT_TAIL.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_LAST.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_NEXT.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_PREV.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_REMOVE.3head=queue.h.3head
+s usr/share/man/man3head/TAILQ_SWAP.3head=queue.h.3head
+s usr/share/man/man3head/TIMESPEC_TO_TIMEVAL.3head=timespec.3head
+s usr/share/man/man3head/TIMEVAL_TO_TIMESPEC.3head=timespec.3head
+s usr/share/man/man3head/acct.3head=acct.h.3head
+f usr/share/man/man3head/acct.h.3head 0444 root bin
+s usr/share/man/man3head/aio.3head=aio.h.3head
+f usr/share/man/man3head/aio.h.3head 0444 root bin
+s usr/share/man/man3head/ar.3head=ar.h.3head
+f usr/share/man/man3head/ar.h.3head 0444 root bin
+s usr/share/man/man3head/archives.3head=archives.h.3head
+f usr/share/man/man3head/archives.h.3head 0444 root bin
+s usr/share/man/man3head/assert.3head=assert.h.3head
+f usr/share/man/man3head/assert.h.3head 0444 root bin
+s usr/share/man/man3head/complex.3head=complex.h.3head
+f usr/share/man/man3head/complex.h.3head 0444 root bin
+s usr/share/man/man3head/cpio.3head=cpio.h.3head
+f usr/share/man/man3head/cpio.h.3head 0444 root bin
+s usr/share/man/man3head/dirent.3head=dirent.h.3head
+f usr/share/man/man3head/dirent.h.3head 0444 root bin
+f usr/share/man/man3head/endian.h.3head 0444 root bin
+s usr/share/man/man3head/errno.3head=errno.h.3head
+f usr/share/man/man3head/errno.h.3head 0444 root bin
+s usr/share/man/man3head/fcntl.3head=fcntl.h.3head
+f usr/share/man/man3head/fcntl.h.3head 0444 root bin
+s usr/share/man/man3head/fenv.3head=fenv.h.3head
+f usr/share/man/man3head/fenv.h.3head 0444 root bin
+s usr/share/man/man3head/float.3head=float.h.3head
+f usr/share/man/man3head/float.h.3head 0444 root bin
+s usr/share/man/man3head/floatingpoint.3head=floatingpoint.h.3head
+f usr/share/man/man3head/floatingpoint.h.3head 0444 root bin
+s usr/share/man/man3head/fmtmsg.3head=fmtmsg.h.3head
+f usr/share/man/man3head/fmtmsg.h.3head 0444 root bin
+s usr/share/man/man3head/fnmatch.3head=fnmatch.h.3head
+f usr/share/man/man3head/fnmatch.h.3head 0444 root bin
+s usr/share/man/man3head/ftw.3head=ftw.h.3head
+f usr/share/man/man3head/ftw.h.3head 0444 root bin
+s usr/share/man/man3head/glob.3head=glob.h.3head
+f usr/share/man/man3head/glob.h.3head 0444 root bin
+s usr/share/man/man3head/grp.3head=grp.h.3head
+f usr/share/man/man3head/grp.h.3head 0444 root bin
+s usr/share/man/man3head/iconv.3head=iconv.h.3head
+f usr/share/man/man3head/iconv.h.3head 0444 root bin
+s usr/share/man/man3head/if.3head=if.h.3head
+f usr/share/man/man3head/if.h.3head 0444 root bin
+s usr/share/man/man3head/in.3head=in.h.3head
+f usr/share/man/man3head/in.h.3head 0444 root bin
+s usr/share/man/man3head/inet.3head=inet.h.3head
+f usr/share/man/man3head/inet.h.3head 0444 root bin
+s usr/share/man/man3head/inttypes.3head=inttypes.h.3head
+f usr/share/man/man3head/inttypes.h.3head 0444 root bin
+s usr/share/man/man3head/ipc.3head=ipc.h.3head
+f usr/share/man/man3head/ipc.h.3head 0444 root bin
+s usr/share/man/man3head/iso646.3head=iso646.h.3head
+f usr/share/man/man3head/iso646.h.3head 0444 root bin
+s usr/share/man/man3head/langinfo.3head=langinfo.h.3head
+f usr/share/man/man3head/langinfo.h.3head 0444 root bin
+s usr/share/man/man3head/libgen.3head=libgen.h.3head
+f usr/share/man/man3head/libgen.h.3head 0444 root bin
+s usr/share/man/man3head/libintl.3head=libintl.h.3head
+f usr/share/man/man3head/libintl.h.3head 0444 root bin
+s usr/share/man/man3head/limits.3head=limits.h.3head
+f usr/share/man/man3head/limits.h.3head 0444 root bin
+s usr/share/man/man3head/locale.3head=locale.h.3head
+f usr/share/man/man3head/locale.h.3head 0444 root bin
+s usr/share/man/man3head/math.3head=math.h.3head
+f usr/share/man/man3head/math.h.3head 0444 root bin
+s usr/share/man/man3head/mman.3head=mman.h.3head
+f usr/share/man/man3head/mman.h.3head 0444 root bin
+s usr/share/man/man3head/monetary.3head=monetary.h.3head
+f usr/share/man/man3head/monetary.h.3head 0444 root bin
+s usr/share/man/man3head/mqueue.3head=mqueue.h.3head
+f usr/share/man/man3head/mqueue.h.3head 0444 root bin
+s usr/share/man/man3head/msg.3head=msg.h.3head
+f usr/share/man/man3head/msg.h.3head 0444 root bin
+s usr/share/man/man3head/ndbm.3head=ndbm.h.3head
+f usr/share/man/man3head/ndbm.h.3head 0444 root bin
+s usr/share/man/man3head/netdb.3head=netdb.h.3head
+f usr/share/man/man3head/netdb.h.3head 0444 root bin
+s usr/share/man/man3head/nl_types.3head=nl_types.h.3head
+f usr/share/man/man3head/nl_types.h.3head 0444 root bin
+s usr/share/man/man3head/poll.3head=poll.h.3head
+f usr/share/man/man3head/poll.h.3head 0444 root bin
+s usr/share/man/man3head/pthread.3head=pthread.h.3head
+f usr/share/man/man3head/pthread.h.3head 0444 root bin
+s usr/share/man/man3head/pwd.3head=pwd.h.3head
+f usr/share/man/man3head/pwd.h.3head 0444 root bin
+f usr/share/man/man3head/queue.h.3head 0444 root bin
+s usr/share/man/man3head/regex.3head=regex.h.3head
+f usr/share/man/man3head/regex.h.3head 0444 root bin
+s usr/share/man/man3head/resource.3head=resource.h.3head
+f usr/share/man/man3head/resource.h.3head 0444 root bin
+s usr/share/man/man3head/sched.3head=sched.h.3head
+f usr/share/man/man3head/sched.h.3head 0444 root bin
+s usr/share/man/man3head/search.3head=search.h.3head
+f usr/share/man/man3head/search.h.3head 0444 root bin
+s usr/share/man/man3head/select.3head=select.h.3head
+f usr/share/man/man3head/select.h.3head 0444 root bin
+s usr/share/man/man3head/sem.3head=sem.h.3head
+f usr/share/man/man3head/sem.h.3head 0444 root bin
+s usr/share/man/man3head/semaphore.3head=semaphore.h.3head
+f usr/share/man/man3head/semaphore.h.3head 0444 root bin
+s usr/share/man/man3head/setjmp.3head=setjmp.h.3head
+f usr/share/man/man3head/setjmp.h.3head 0444 root bin
+s usr/share/man/man3head/shm.3head=shm.h.3head
+f usr/share/man/man3head/shm.h.3head 0444 root bin
+s usr/share/man/man3head/siginfo.3head=siginfo.h.3head
+f usr/share/man/man3head/siginfo.h.3head 0444 root bin
+s usr/share/man/man3head/signal.3head=signal.h.3head
+f usr/share/man/man3head/signal.h.3head 0444 root bin
+s usr/share/man/man3head/socket.3head=socket.h.3head
+f usr/share/man/man3head/socket.h.3head 0444 root bin
+s usr/share/man/man3head/spawn.3head=spawn.h.3head
+f usr/share/man/man3head/spawn.h.3head 0444 root bin
+s usr/share/man/man3head/stat.3head=stat.h.3head
+f usr/share/man/man3head/stat.h.3head 0444 root bin
+s usr/share/man/man3head/statvfs.3head=statvfs.h.3head
+f usr/share/man/man3head/statvfs.h.3head 0444 root bin
+s usr/share/man/man3head/stdbool.3head=stdbool.h.3head
+f usr/share/man/man3head/stdbool.h.3head 0444 root bin
+s usr/share/man/man3head/stddef.3head=stddef.h.3head
+f usr/share/man/man3head/stddef.h.3head 0444 root bin
+s usr/share/man/man3head/stdint.3head=stdint.h.3head
+f usr/share/man/man3head/stdint.h.3head 0444 root bin
+s usr/share/man/man3head/stdio.3head=stdio.h.3head
+f usr/share/man/man3head/stdio.h.3head 0444 root bin
+s usr/share/man/man3head/stdlib.3head=stdlib.h.3head
+f usr/share/man/man3head/stdlib.h.3head 0444 root bin
+s usr/share/man/man3head/string.3head=string.h.3head
+f usr/share/man/man3head/string.h.3head 0444 root bin
+s usr/share/man/man3head/strings.3head=strings.h.3head
+f usr/share/man/man3head/strings.h.3head 0444 root bin
+s usr/share/man/man3head/stropts.3head=stropts.h.3head
+f usr/share/man/man3head/stropts.h.3head 0444 root bin
+s usr/share/man/man3head/syslog.3head=syslog.h.3head
+f usr/share/man/man3head/syslog.h.3head 0444 root bin
+s usr/share/man/man3head/tar.3head=tar.h.3head
+f usr/share/man/man3head/tar.h.3head 0444 root bin
+s usr/share/man/man3head/tcp.3head=tcp.h.3head
+f usr/share/man/man3head/tcp.h.3head 0444 root bin
+s usr/share/man/man3head/termios.3head=termios.h.3head
+f usr/share/man/man3head/termios.h.3head 0444 root bin
+s usr/share/man/man3head/tgmath.3head=tgmath.h.3head
+f usr/share/man/man3head/tgmath.h.3head 0444 root bin
+s usr/share/man/man3head/time.3head=time.h.3head
+f usr/share/man/man3head/time.h.3head 0444 root bin
+s usr/share/man/man3head/timeb.3head=timeb.h.3head
+f usr/share/man/man3head/timeb.h.3head 0444 root bin
+s usr/share/man/man3head/times.3head=times.h.3head
+f usr/share/man/man3head/times.h.3head 0444 root bin
+f usr/share/man/man3head/timespec.3head 0444 root bin
+s usr/share/man/man3head/timeval.3head=timespec.3head
+s usr/share/man/man3head/types.3head=types.h.3head
+f usr/share/man/man3head/types.h.3head 0444 root bin
+s usr/share/man/man3head/types32.3head=types32.h.3head
+f usr/share/man/man3head/types32.h.3head 0444 root bin
+f usr/share/man/man3head/uchar.h.3head 0444 root bin
+s usr/share/man/man3head/ucontext.3head=ucontext.h.3head
+f usr/share/man/man3head/ucontext.h.3head 0444 root bin
+s usr/share/man/man3head/uio.3head=uio.h.3head
+f usr/share/man/man3head/uio.h.3head 0444 root bin
+s usr/share/man/man3head/ulimit.3head=ulimit.h.3head
+f usr/share/man/man3head/ulimit.h.3head 0444 root bin
+s usr/share/man/man3head/un.3head=un.h.3head
+f usr/share/man/man3head/un.h.3head 0444 root bin
+s usr/share/man/man3head/unistd.3head=unistd.h.3head
+f usr/share/man/man3head/unistd.h.3head 0444 root bin
+f usr/share/man/man3head/upanic.h.3head 0444 root bin
+s usr/share/man/man3head/utime.3head=utime.h.3head
+f usr/share/man/man3head/utime.h.3head 0444 root bin
+s usr/share/man/man3head/utmpx.3head=utmpx.h.3head
+f usr/share/man/man3head/utmpx.h.3head 0444 root bin
+s usr/share/man/man3head/utsname.3head=utsname.h.3head
+f usr/share/man/man3head/utsname.h.3head 0444 root bin
+s usr/share/man/man3head/values.3head=values.h.3head
+f usr/share/man/man3head/values.h.3head 0444 root bin
+s usr/share/man/man3head/wait.3head=wait.h.3head
+f usr/share/man/man3head/wait.h.3head 0444 root bin
+s usr/share/man/man3head/wchar.3head=wchar.h.3head
+f usr/share/man/man3head/wchar.h.3head 0444 root bin
+s usr/share/man/man3head/wctype.3head=wctype.h.3head
+f usr/share/man/man3head/wctype.h.3head 0444 root bin
+s usr/share/man/man3head/wordexp.3head=wordexp.h.3head
+f usr/share/man/man3head/wordexp.h.3head 0444 root bin
+s usr/share/man/man3head/xlocale.3head=xlocale.h.3head
+f usr/share/man/man3head/xlocale.h.3head 0444 root bin
+d usr/share/man/man3kstat 0755 root bin
+f usr/share/man/man3kstat/kstat.3kstat 0444 root bin
+f usr/share/man/man3kstat/kstat_chain_update.3kstat 0444 root bin
+s usr/share/man/man3kstat/kstat_close.3kstat=kstat_open.3kstat
+s usr/share/man/man3kstat/kstat_data_lookup.3kstat=kstat_lookup.3kstat
+f usr/share/man/man3kstat/kstat_lookup.3kstat 0444 root bin
+f usr/share/man/man3kstat/kstat_open.3kstat 0444 root bin
+f usr/share/man/man3kstat/kstat_read.3kstat 0444 root bin
+s usr/share/man/man3kstat/kstat_write.3kstat=kstat_read.3kstat
+d usr/share/man/man3kvm 0755 root bin
+s usr/share/man/man3kvm/kvm_close.3kvm=kvm_open.3kvm
+s usr/share/man/man3kvm/kvm_getcmd.3kvm=kvm_getu.3kvm
+s usr/share/man/man3kvm/kvm_getproc.3kvm=kvm_nextproc.3kvm
+f usr/share/man/man3kvm/kvm_getu.3kvm 0444 root bin
+f usr/share/man/man3kvm/kvm_kread.3kvm 0444 root bin
+s usr/share/man/man3kvm/kvm_kwrite.3kvm=kvm_kread.3kvm
+f usr/share/man/man3kvm/kvm_nextproc.3kvm 0444 root bin
+f usr/share/man/man3kvm/kvm_nlist.3kvm 0444 root bin
+f usr/share/man/man3kvm/kvm_open.3kvm 0444 root bin
+f usr/share/man/man3kvm/kvm_read.3kvm 0444 root bin
+s usr/share/man/man3kvm/kvm_setproc.3kvm=kvm_nextproc.3kvm
+s usr/share/man/man3kvm/kvm_uread.3kvm=kvm_kread.3kvm
+s usr/share/man/man3kvm/kvm_uwrite.3kvm=kvm_kread.3kvm
+s usr/share/man/man3kvm/kvm_write.3kvm=kvm_read.3kvm
+d usr/share/man/man3ldap 0755 root bin
+s usr/share/man/man3ldap/ber_alloc.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_alloc_t.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_bvdup.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_bvecfree.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_bvfree.3ldap=ber_decode.3ldap
+f usr/share/man/man3ldap/ber_decode.3ldap 0444 root bin
+f usr/share/man/man3ldap/ber_encode.3ldap 0444 root bin
+s usr/share/man/man3ldap/ber_first_element.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_flatten.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_free.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_get_bitstring.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_get_boolean.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_get_int.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_get_next.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_get_null.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_get_stringa.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_get_stringal.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_get_stringb.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_init.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_next_element.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_peek_tag.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_printf.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_put_bitstring.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_put_boolean.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_put_int.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_put_null.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_put_ostring.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_put_seq.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_put_set.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_put_string.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_scanf.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_skip_tag.3ldap=ber_decode.3ldap
+s usr/share/man/man3ldap/ber_start_seq.3ldap=ber_encode.3ldap
+s usr/share/man/man3ldap/ber_start_set.3ldap=ber_encode.3ldap
+f usr/share/man/man3ldap/cldap_close.3ldap 0444 root bin
+f usr/share/man/man3ldap/cldap_open.3ldap 0444 root bin
+f usr/share/man/man3ldap/cldap_search_s.3ldap 0444 root bin
+f usr/share/man/man3ldap/cldap_setretryinfo.3ldap 0444 root bin
+f usr/share/man/man3ldap/ldap.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_8859_to_t61.3ldap=ldap_charset.3ldap
+f usr/share/man/man3ldap/ldap_abandon.3ldap 0444 root bin
+f usr/share/man/man3ldap/ldap_add.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_add_ext.3ldap=ldap_add.3ldap
+s usr/share/man/man3ldap/ldap_add_ext_s.3ldap=ldap_add.3ldap
+s usr/share/man/man3ldap/ldap_add_s.3ldap=ldap_add.3ldap
+f usr/share/man/man3ldap/ldap_ber_free.3ldap 0444 root bin
+f usr/share/man/man3ldap/ldap_bind.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_bind_s.3ldap=ldap_bind.3ldap
+s usr/share/man/man3ldap/ldap_build_filter.3ldap=ldap_getfilter.3ldap
+f usr/share/man/man3ldap/ldap_charset.3ldap 0444 root bin
+f usr/share/man/man3ldap/ldap_compare.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_compare_ext.3ldap=ldap_compare.3ldap
+s usr/share/man/man3ldap/ldap_compare_ext_s.3ldap=ldap_compare.3ldap
+s usr/share/man/man3ldap/ldap_compare_s.3ldap=ldap_compare.3ldap
+f usr/share/man/man3ldap/ldap_control_free.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_controls_free.3ldap=ldap_control_free.3ldap
+s usr/share/man/man3ldap/ldap_count_entries.3ldap=ldap_first_entry.3ldap
+s usr/share/man/man3ldap/ldap_count_messages.3ldap=ldap_first_message.3ldap
+s usr/share/man/man3ldap/ldap_count_references.3ldap=ldap_first_entry.3ldap
+s usr/share/man/man3ldap/ldap_count_values.3ldap=ldap_get_values.3ldap
+s usr/share/man/man3ldap/ldap_count_values_len.3ldap=ldap_get_values.3ldap
+f usr/share/man/man3ldap/ldap_delete.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_delete_ext.3ldap=ldap_delete.3ldap
+s usr/share/man/man3ldap/ldap_delete_ext_s.3ldap=ldap_delete.3ldap
+s usr/share/man/man3ldap/ldap_delete_s.3ldap=ldap_delete.3ldap
+f usr/share/man/man3ldap/ldap_disptmpl.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_dn2ufn.3ldap=ldap_get_dn.3ldap
+s usr/share/man/man3ldap/ldap_dn_to_url.3ldap=ldap_url.3ldap
+s usr/share/man/man3ldap/ldap_dns_to_dn.3ldap=ldap_get_dn.3ldap
+s usr/share/man/man3ldap/ldap_dns_to_url.3ldap=ldap_url.3ldap
+s usr/share/man/man3ldap/ldap_enable_translation.3ldap=ldap_charset.3ldap
+s usr/share/man/man3ldap/ldap_entry2html.3ldap=ldap_entry2text.3ldap
+s usr/share/man/man3ldap/ldap_entry2html_search.3ldap=ldap_entry2text.3ldap
+f usr/share/man/man3ldap/ldap_entry2text.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_entry2text_search.3ldap=ldap_entry2text.3ldap
+s usr/share/man/man3ldap/ldap_err2string.3ldap=ldap_error.3ldap
+f usr/share/man/man3ldap/ldap_error.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_explode_dn.3ldap=ldap_get_dn.3ldap
+s usr/share/man/man3ldap/ldap_explode_dns.3ldap=ldap_get_dn.3ldap
+f usr/share/man/man3ldap/ldap_first_attribute.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_first_disptmpl.3ldap=ldap_disptmpl.3ldap
+f usr/share/man/man3ldap/ldap_first_entry.3ldap 0444 root bin
+f usr/share/man/man3ldap/ldap_first_message.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_first_reference.3ldap=ldap_first_entry.3ldap
+s usr/share/man/man3ldap/ldap_first_searchobj.3ldap=ldap_searchprefs.3ldap
+s usr/share/man/man3ldap/ldap_first_tmplcol.3ldap=ldap_disptmpl.3ldap
+s usr/share/man/man3ldap/ldap_first_tmplrow.3ldap=ldap_disptmpl.3ldap
+s usr/share/man/man3ldap/ldap_free_friendlymap.3ldap=ldap_friendly.3ldap
+s usr/share/man/man3ldap/ldap_free_searchprefs.3ldap=ldap_searchprefs.3ldap
+s usr/share/man/man3ldap/ldap_free_templates.3ldap=ldap_disptmpl.3ldap
+s usr/share/man/man3ldap/ldap_free_urldesc.3ldap=ldap_url.3ldap
+f usr/share/man/man3ldap/ldap_friendly.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_friendly_name.3ldap=ldap_friendly.3ldap
+f usr/share/man/man3ldap/ldap_get_dn.3ldap 0444 root bin
+f usr/share/man/man3ldap/ldap_get_entry_controls.3ldap 0444 root bin
+f usr/share/man/man3ldap/ldap_get_lang_values.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_get_lang_values_len.3ldap=ldap_get_lang_values.3ldap
+f usr/share/man/man3ldap/ldap_get_option.3ldap 0444 root bin
+f usr/share/man/man3ldap/ldap_get_values.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_get_values_len.3ldap=ldap_get_values.3ldap
+f usr/share/man/man3ldap/ldap_getfilter.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_getfilter_free.3ldap=ldap_getfilter.3ldap
+s usr/share/man/man3ldap/ldap_getfirstfilter.3ldap=ldap_getfilter.3ldap
+s usr/share/man/man3ldap/ldap_getnextfilter.3ldap=ldap_getfilter.3ldap
+s usr/share/man/man3ldap/ldap_init.3ldap=ldap_open.3ldap
+s usr/share/man/man3ldap/ldap_init_getfilter.3ldap=ldap_getfilter.3ldap
+s usr/share/man/man3ldap/ldap_init_getfilter_buf.3ldap=ldap_getfilter.3ldap
+s usr/share/man/man3ldap/ldap_init_searchprefs.3ldap=ldap_searchprefs.3ldap
+s usr/share/man/man3ldap/ldap_init_searchprefs_buf.3ldap=ldap_searchprefs.3ldap
+s usr/share/man/man3ldap/ldap_init_templates.3ldap=ldap_disptmpl.3ldap
+s usr/share/man/man3ldap/ldap_init_templates_buf.3ldap=ldap_disptmpl.3ldap
+s usr/share/man/man3ldap/ldap_is_dns_dn.3ldap=ldap_get_dn.3ldap
+s usr/share/man/man3ldap/ldap_is_ldap_url.3ldap=ldap_url.3ldap
+f usr/share/man/man3ldap/ldap_memcache.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_memcache_destroy.3ldap=ldap_memcache.3ldap
+s usr/share/man/man3ldap/ldap_memcache_flush.3ldap=ldap_memcache.3ldap
+s usr/share/man/man3ldap/ldap_memcache_get.3ldap=ldap_memcache.3ldap
+s usr/share/man/man3ldap/ldap_memcache_init.3ldap=ldap_memcache.3ldap
+s usr/share/man/man3ldap/ldap_memcache_set.3ldap=ldap_memcache.3ldap
+s usr/share/man/man3ldap/ldap_memcache_update.3ldap=ldap_memcache.3ldap
+f usr/share/man/man3ldap/ldap_memfree.3ldap 0444 root bin
+f usr/share/man/man3ldap/ldap_modify.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_modify_ext.3ldap=ldap_modify.3ldap
+s usr/share/man/man3ldap/ldap_modify_ext_s.3ldap=ldap_modify.3ldap
+s usr/share/man/man3ldap/ldap_modify_s.3ldap=ldap_modify.3ldap
+f usr/share/man/man3ldap/ldap_modrdn.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_modrdn2.3ldap=ldap_modrdn.3ldap
+s usr/share/man/man3ldap/ldap_modrdn2_s.3ldap=ldap_modrdn.3ldap
+s usr/share/man/man3ldap/ldap_modrdn_s.3ldap=ldap_modrdn.3ldap
+s usr/share/man/man3ldap/ldap_mods_free.3ldap=ldap_modify.3ldap
+s usr/share/man/man3ldap/ldap_msgfree.3ldap=ldap_result.3ldap
+s usr/share/man/man3ldap/ldap_msgtype.3ldap=ldap_first_message.3ldap
+s usr/share/man/man3ldap/ldap_name2template.3ldap=ldap_disptmpl.3ldap
+s usr/share/man/man3ldap/ldap_next_attribute.3ldap=ldap_first_attribute.3ldap
+s usr/share/man/man3ldap/ldap_next_disptmpl.3ldap=ldap_disptmpl.3ldap
+s usr/share/man/man3ldap/ldap_next_entry.3ldap=ldap_first_entry.3ldap
+s usr/share/man/man3ldap/ldap_next_message.3ldap=ldap_first_message.3ldap
+s usr/share/man/man3ldap/ldap_next_reference.3ldap=ldap_first_entry.3ldap
+s usr/share/man/man3ldap/ldap_next_searchobj.3ldap=ldap_searchprefs.3ldap
+s usr/share/man/man3ldap/ldap_next_tmplcol.3ldap=ldap_disptmpl.3ldap
+s usr/share/man/man3ldap/ldap_next_tmplrow.3ldap=ldap_disptmpl.3ldap
+s usr/share/man/man3ldap/ldap_oc2template.3ldap=ldap_disptmpl.3ldap
+f usr/share/man/man3ldap/ldap_open.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_parse_extended_result.3ldap=ldap_parse_result.3ldap
+f usr/share/man/man3ldap/ldap_parse_result.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_parse_sasl_bind_result.3ldap=ldap_parse_result.3ldap
+s usr/share/man/man3ldap/ldap_perror.3ldap=ldap_error.3ldap
+s usr/share/man/man3ldap/ldap_rename.3ldap=ldap_modrdn.3ldap
+s usr/share/man/man3ldap/ldap_rename_s.3ldap=ldap_modrdn.3ldap
+f usr/share/man/man3ldap/ldap_result.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_result2error.3ldap=ldap_error.3ldap
+s usr/share/man/man3ldap/ldap_sasl_bind.3ldap=ldap_bind.3ldap
+s usr/share/man/man3ldap/ldap_sasl_bind_s.3ldap=ldap_bind.3ldap
+s usr/share/man/man3ldap/ldap_sasl_interactive_bind_s.3ldap=ldap_bind.3ldap
+f usr/share/man/man3ldap/ldap_search.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_search_ext.3ldap=ldap_search.3ldap
+s usr/share/man/man3ldap/ldap_search_ext_s.3ldap=ldap_search.3ldap
+s usr/share/man/man3ldap/ldap_search_s.3ldap=ldap_search.3ldap
+s usr/share/man/man3ldap/ldap_search_st.3ldap=ldap_search.3ldap
+f usr/share/man/man3ldap/ldap_searchprefs.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_set_option.3ldap=ldap_get_option.3ldap
+s usr/share/man/man3ldap/ldap_set_rebind_proc.3ldap=ldap_bind.3ldap
+s usr/share/man/man3ldap/ldap_set_string_translators.3ldap=ldap_charset.3ldap
+s usr/share/man/man3ldap/ldap_setfilteraffixes.3ldap=ldap_getfilter.3ldap
+s usr/share/man/man3ldap/ldap_simple_bind.3ldap=ldap_bind.3ldap
+s usr/share/man/man3ldap/ldap_simple_bind_s.3ldap=ldap_bind.3ldap
+f usr/share/man/man3ldap/ldap_sort.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_sort_entries.3ldap=ldap_sort.3ldap
+s usr/share/man/man3ldap/ldap_sort_strcasecmp.3ldap=ldap_sort.3ldap
+s usr/share/man/man3ldap/ldap_sort_values.3ldap=ldap_sort.3ldap
+s usr/share/man/man3ldap/ldap_t61_to_8859.3ldap=ldap_charset.3ldap
+s usr/share/man/man3ldap/ldap_tmplattrs.3ldap=ldap_disptmpl.3ldap
+s usr/share/man/man3ldap/ldap_translate_from_t61.3ldap=ldap_charset.3ldap
+s usr/share/man/man3ldap/ldap_translate_to_t61.3ldap=ldap_charset.3ldap
+f usr/share/man/man3ldap/ldap_ufn.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_ufn_search_c.3ldap=ldap_ufn.3ldap
+s usr/share/man/man3ldap/ldap_ufn_search_ct.3ldap=ldap_ufn.3ldap
+s usr/share/man/man3ldap/ldap_ufn_search_s.3ldap=ldap_ufn.3ldap
+s usr/share/man/man3ldap/ldap_ufn_setfilter.3ldap=ldap_ufn.3ldap
+s usr/share/man/man3ldap/ldap_ufn_setprefix.3ldap=ldap_ufn.3ldap
+s usr/share/man/man3ldap/ldap_ufn_timeout.3ldap=ldap_ufn.3ldap
+s usr/share/man/man3ldap/ldap_unbind.3ldap=ldap_bind.3ldap
+s usr/share/man/man3ldap/ldap_unbind_ext.3ldap=ldap_bind.3ldap
+s usr/share/man/man3ldap/ldap_unbind_s.3ldap=ldap_bind.3ldap
+f usr/share/man/man3ldap/ldap_url.3ldap 0444 root bin
+s usr/share/man/man3ldap/ldap_url_parse.3ldap=ldap_url.3ldap
+s usr/share/man/man3ldap/ldap_url_parse_nodn.3ldap=ldap_url.3ldap
+s usr/share/man/man3ldap/ldap_url_search.3ldap=ldap_url.3ldap
+s usr/share/man/man3ldap/ldap_url_search_s.3ldap=ldap_url.3ldap
+s usr/share/man/man3ldap/ldap_url_search_st.3ldap=ldap_url.3ldap
+s usr/share/man/man3ldap/ldap_vals2html.3ldap=ldap_entry2text.3ldap
+s usr/share/man/man3ldap/ldap_vals2text.3ldap=ldap_entry2text.3ldap
+s usr/share/man/man3ldap/ldap_value_free.3ldap=ldap_get_values.3ldap
+s usr/share/man/man3ldap/ldap_value_free_len.3ldap=ldap_get_values.3ldap
+f usr/share/man/man3ldap/ldap_version.3ldap 0444 root bin
+d usr/share/man/man3lgrp 0755 root bin
+f usr/share/man/man3lgrp/lgrp_affinity_get.3lgrp 0444 root bin
+s usr/share/man/man3lgrp/lgrp_affinity_set.3lgrp=lgrp_affinity_get.3lgrp
+f usr/share/man/man3lgrp/lgrp_children.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_cookie_stale.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_cpus.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_fini.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_home.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_init.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_latency.3lgrp 0444 root bin
+s usr/share/man/man3lgrp/lgrp_latency_cookie.3lgrp=lgrp_latency.3lgrp
+f usr/share/man/man3lgrp/lgrp_mem_size.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_nlgrps.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_parents.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_resources.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_root.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_version.3lgrp 0444 root bin
+f usr/share/man/man3lgrp/lgrp_view.3lgrp 0444 root bin
+d usr/share/man/man3lib 0755 root bin
+s usr/share/man/man3lib/SMHBA_GetAdapterAttributes.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetAdapterPortAttributes.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetBindingCapability.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetBindingSupport.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetDiscoveredPortAttributes.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetFCPhyAttributes.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetLUNStatistics.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetNumberofPorts.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetPersistentBinding.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetPhyStatistics.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetPortAttributesByWWN.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetPortType.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetProtocolStatistics.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetSASPhyAttributes.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetTargetMapping.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetVendorLibraryAttributes.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetVersion.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_GetWrapperLibraryAttributes.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_RegisterForAdapterAddEvents.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_RegisterForAdapterEvents.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_RegisterForAdapterPhyStatEvents.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_RegisterForAdapterPortEvents.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_RegisterForAdapterPortStatEvents.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_RegisterForTargetEvents.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_RegisterLibrary.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_RemoveAllPersistentBindings.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_RemovePersistentBinding.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_ScsiInquiry.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_ScsiReadCapacity.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_ScsiReportLuns.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_SendECHO.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_SendSMPPassThru.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_SendTEST.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_SetBindingSupport.3lib=libSMHBAAPI.3lib
+s usr/share/man/man3lib/SMHBA_SetPersistentBinding.3lib=libSMHBAAPI.3lib
+f usr/share/man/man3lib/libMPAPI.3lib 0444 root bin
+f usr/share/man/man3lib/libSMHBAAPI.3lib 0444 root bin
+f usr/share/man/man3lib/libadm.3lib 0444 root bin
+f usr/share/man/man3lib/libaio.3lib 0444 root bin
+f usr/share/man/man3lib/libavl.3lib 0444 root bin
+f usr/share/man/man3lib/libbsdmalloc.3lib 0444 root bin
+f usr/share/man/man3lib/libbsm.3lib 0444 root bin
+f usr/share/man/man3lib/libc.3lib 0444 root bin
+f usr/share/man/man3lib/libc_db.3lib 0444 root bin
+f usr/share/man/man3lib/libcfgadm.3lib 0444 root bin
+f usr/share/man/man3lib/libcommputil.3lib 0444 root bin
+f usr/share/man/man3lib/libcontract.3lib 0444 root bin
+f usr/share/man/man3lib/libcpc.3lib 0444 root bin
+f usr/share/man/man3lib/libcrypt.3lib 0444 root bin
+f usr/share/man/man3lib/libcurses.3lib 0444 root bin
+f usr/share/man/man3lib/libdat.3lib 0444 root bin
+f usr/share/man/man3lib/libdevid.3lib 0444 root bin
+f usr/share/man/man3lib/libdevinfo.3lib 0444 root bin
+f usr/share/man/man3lib/libdl.3lib 0444 root bin
+f usr/share/man/man3lib/libdlpi.3lib 0444 root bin
+f usr/share/man/man3lib/libdns_sd.3lib 0444 root bin
+f usr/share/man/man3lib/libdoor.3lib 0444 root bin
+f usr/share/man/man3lib/libdtrace.3lib 0444 root bin
+f usr/share/man/man3lib/libefi.3lib 0444 root bin
+f usr/share/man/man3lib/libelf.3lib 0444 root bin
+f usr/share/man/man3lib/libexacct.3lib 0444 root bin
+f usr/share/man/man3lib/libfcoe.3lib 0444 root bin
+f usr/share/man/man3lib/libform.3lib 0444 root bin
+f usr/share/man/man3lib/libfstyp.3lib 0444 root bin
+f usr/share/man/man3lib/libgen.3lib 0444 root bin
+f usr/share/man/man3lib/libgss.3lib 0444 root bin
+f usr/share/man/man3lib/libintl.3lib 0444 root bin
+f usr/share/man/man3lib/libiscsit.3lib 0444 root bin
+f usr/share/man/man3lib/libkmf.3lib 0444 root bin
+f usr/share/man/man3lib/libkrb5.3lib 0444 root bin
+f usr/share/man/man3lib/libkstat.3lib 0444 root bin
+f usr/share/man/man3lib/libkvm.3lib 0444 root bin
+f usr/share/man/man3lib/libl.3lib 0444 root bin
+f usr/share/man/man3lib/liblgrp.3lib 0444 root bin
+f usr/share/man/man3lib/libm.3lib 0444 root bin
+f usr/share/man/man3lib/libmail.3lib 0444 root bin
+f usr/share/man/man3lib/libmalloc.3lib 0444 root bin
+f usr/share/man/man3lib/libmapmalloc.3lib 0444 root bin
+f usr/share/man/man3lib/libmd.3lib 0444 root bin
+f usr/share/man/man3lib/libmd5.3lib 0444 root bin
+f usr/share/man/man3lib/libmenu.3lib 0444 root bin
+f usr/share/man/man3lib/libmp.3lib 0444 root bin
+s usr/share/man/man3lib/libmpapi.3lib=libMPAPI.3lib
+f usr/share/man/man3lib/libmtmalloc.3lib 0444 root bin
+f usr/share/man/man3lib/libmvec.3lib 0444 root bin
+f usr/share/man/man3lib/libnls.3lib 0444 root bin
+f usr/share/man/man3lib/libnsl.3lib 0444 root bin
+f usr/share/man/man3lib/libnvpair.3lib 0444 root bin
+f usr/share/man/man3lib/libpam.3lib 0444 root bin
+f usr/share/man/man3lib/libpanel.3lib 0444 root bin
+f usr/share/man/man3lib/libpctx.3lib 0444 root bin
+f usr/share/man/man3lib/libpicl.3lib 0444 root bin
+f usr/share/man/man3lib/libpicltree.3lib 0444 root bin
+f usr/share/man/man3lib/libpkcs11.3lib 0444 root bin
+f usr/share/man/man3lib/libpool.3lib 0444 root bin
+s usr/share/man/man3lib/libposix4.3lib=librt.3lib
+f usr/share/man/man3lib/libproc.3lib 0444 root bin
+f usr/share/man/man3lib/libproject.3lib 0444 root bin
+f usr/share/man/man3lib/libpthread.3lib 0444 root bin
+f usr/share/man/man3lib/libresolv.3lib 0444 root bin
+f usr/share/man/man3lib/librpcsvc.3lib 0444 root bin
+f usr/share/man/man3lib/librt.3lib 0444 root bin
+f usr/share/man/man3lib/librtld_db.3lib 0444 root bin
+f usr/share/man/man3lib/libsasl.3lib 0444 root bin
+f usr/share/man/man3lib/libscf.3lib 0444 root bin
+f usr/share/man/man3lib/libsctp.3lib 0444 root bin
+f usr/share/man/man3lib/libsec.3lib 0444 root bin
+f usr/share/man/man3lib/libsecdb.3lib 0444 root bin
+f usr/share/man/man3lib/libsendfile.3lib 0444 root bin
+f usr/share/man/man3lib/libsip.3lib 0444 root bin
+f usr/share/man/man3lib/libslp.3lib 0444 root bin
+s usr/share/man/man3lib/libsmhbaapi.3lib=libSMHBAAPI.3lib
+f usr/share/man/man3lib/libsocket.3lib 0444 root bin
+f usr/share/man/man3lib/libstmf.3lib 0444 root bin
+f usr/share/man/man3lib/libsysevent.3lib 0444 root bin
+f usr/share/man/man3lib/libtecla.3lib 0444 root bin
+s usr/share/man/man3lib/libtermcap.3lib=libcurses.3lib
+s usr/share/man/man3lib/libtermlib.3lib=libcurses.3lib
+f usr/share/man/man3lib/libthread.3lib 0444 root bin
+f usr/share/man/man3lib/libtsalarm.3lib 0444 root bin
+f usr/share/man/man3lib/libumem.3lib 0444 root bin
+f usr/share/man/man3lib/libuuid.3lib 0444 root bin
+f usr/share/man/man3lib/libvnd.3lib 0444 root bin
+f usr/share/man/man3lib/libvolmgt.3lib 0444 root bin
+f usr/share/man/man3lib/libw.3lib 0444 root bin
+f usr/share/man/man3lib/libxnet.3lib 0444 root bin
+f usr/share/man/man3lib/liby.3lib 0444 root bin
+d usr/share/man/man3m 0755 root bin
+f usr/share/man/man3m/acos.3m 0444 root bin
+s usr/share/man/man3m/acosf.3m=acos.3m
+f usr/share/man/man3m/acosh.3m 0444 root bin
+s usr/share/man/man3m/acoshf.3m=acosh.3m
+s usr/share/man/man3m/acoshl.3m=acosh.3m
+s usr/share/man/man3m/acosl.3m=acos.3m
+f usr/share/man/man3m/asin.3m 0444 root bin
+s usr/share/man/man3m/asinf.3m=asin.3m
+f usr/share/man/man3m/asinh.3m 0444 root bin
+s usr/share/man/man3m/asinhf.3m=asinh.3m
+s usr/share/man/man3m/asinhl.3m=asinh.3m
+s usr/share/man/man3m/asinl.3m=asin.3m
+f usr/share/man/man3m/atan.3m 0444 root bin
+f usr/share/man/man3m/atan2.3m 0444 root bin
+s usr/share/man/man3m/atan2f.3m=atan2.3m
+s usr/share/man/man3m/atan2l.3m=atan2.3m
+s usr/share/man/man3m/atanf.3m=atan.3m
+f usr/share/man/man3m/atanh.3m 0444 root bin
+s usr/share/man/man3m/atanhf.3m=atanh.3m
+s usr/share/man/man3m/atanhl.3m=atanh.3m
+s usr/share/man/man3m/atanl.3m=atan.3m
+f usr/share/man/man3m/cabs.3m 0444 root bin
+s usr/share/man/man3m/cabsf.3m=cabs.3m
+s usr/share/man/man3m/cabsl.3m=cabs.3m
+f usr/share/man/man3m/cacos.3m 0444 root bin
+s usr/share/man/man3m/cacosf.3m=cacos.3m
+f usr/share/man/man3m/cacosh.3m 0444 root bin
+s usr/share/man/man3m/cacoshf.3m=cacosh.3m
+s usr/share/man/man3m/cacoshl.3m=cacosh.3m
+s usr/share/man/man3m/cacosl.3m=cacos.3m
+f usr/share/man/man3m/carg.3m 0444 root bin
+s usr/share/man/man3m/cargf.3m=carg.3m
+s usr/share/man/man3m/cargl.3m=carg.3m
+f usr/share/man/man3m/casin.3m 0444 root bin
+s usr/share/man/man3m/casinf.3m=casin.3m
+f usr/share/man/man3m/casinh.3m 0444 root bin
+s usr/share/man/man3m/casinhf.3m=casinh.3m
+s usr/share/man/man3m/casinhl.3m=casinh.3m
+s usr/share/man/man3m/casinl.3m=casin.3m
+f usr/share/man/man3m/catan.3m 0444 root bin
+s usr/share/man/man3m/catanf.3m=catan.3m
+f usr/share/man/man3m/catanh.3m 0444 root bin
+s usr/share/man/man3m/catanhf.3m=catanh.3m
+s usr/share/man/man3m/catanhl.3m=catanh.3m
+s usr/share/man/man3m/catanl.3m=catan.3m
+f usr/share/man/man3m/cbrt.3m 0444 root bin
+s usr/share/man/man3m/cbrtf.3m=cbrt.3m
+s usr/share/man/man3m/cbrtl.3m=cbrt.3m
+f usr/share/man/man3m/ccos.3m 0444 root bin
+s usr/share/man/man3m/ccosf.3m=ccos.3m
+f usr/share/man/man3m/ccosh.3m 0444 root bin
+s usr/share/man/man3m/ccoshf.3m=ccosh.3m
+s usr/share/man/man3m/ccoshl.3m=ccosh.3m
+s usr/share/man/man3m/ccosl.3m=ccos.3m
+f usr/share/man/man3m/ceil.3m 0444 root bin
+s usr/share/man/man3m/ceilf.3m=ceil.3m
+s usr/share/man/man3m/ceill.3m=ceil.3m
+f usr/share/man/man3m/cexp.3m 0444 root bin
+s usr/share/man/man3m/cexpf.3m=cexp.3m
+s usr/share/man/man3m/cexpl.3m=cexp.3m
+f usr/share/man/man3m/cimag.3m 0444 root bin
+s usr/share/man/man3m/cimagf.3m=cimag.3m
+s usr/share/man/man3m/cimagl.3m=cimag.3m
+f usr/share/man/man3m/clog.3m 0444 root bin
+s usr/share/man/man3m/clogf.3m=clog.3m
+s usr/share/man/man3m/clogl.3m=clog.3m
+f usr/share/man/man3m/conj.3m 0444 root bin
+s usr/share/man/man3m/conjf.3m=conj.3m
+s usr/share/man/man3m/conjl.3m=conj.3m
+f usr/share/man/man3m/copysign.3m 0444 root bin
+s usr/share/man/man3m/copysignf.3m=copysign.3m
+s usr/share/man/man3m/copysignl.3m=copysign.3m
+f usr/share/man/man3m/cos.3m 0444 root bin
+s usr/share/man/man3m/cosf.3m=cos.3m
+f usr/share/man/man3m/cosh.3m 0444 root bin
+s usr/share/man/man3m/coshf.3m=cosh.3m
+s usr/share/man/man3m/coshl.3m=cosh.3m
+s usr/share/man/man3m/cosl.3m=cos.3m
+f usr/share/man/man3m/cpow.3m 0444 root bin
+s usr/share/man/man3m/cpowf.3m=cpow.3m
+s usr/share/man/man3m/cpowl.3m=cpow.3m
+f usr/share/man/man3m/cproj.3m 0444 root bin
+s usr/share/man/man3m/cprojf.3m=cproj.3m
+s usr/share/man/man3m/cprojl.3m=cproj.3m
+f usr/share/man/man3m/creal.3m 0444 root bin
+s usr/share/man/man3m/crealf.3m=creal.3m
+s usr/share/man/man3m/creall.3m=creal.3m
+f usr/share/man/man3m/csin.3m 0444 root bin
+s usr/share/man/man3m/csinf.3m=csin.3m
+f usr/share/man/man3m/csinh.3m 0444 root bin
+s usr/share/man/man3m/csinhf.3m=csinh.3m
+s usr/share/man/man3m/csinhl.3m=csinh.3m
+s usr/share/man/man3m/csinl.3m=csin.3m
+f usr/share/man/man3m/csqrt.3m 0444 root bin
+s usr/share/man/man3m/csqrtf.3m=csqrt.3m
+s usr/share/man/man3m/csqrtl.3m=csqrt.3m
+f usr/share/man/man3m/ctan.3m 0444 root bin
+s usr/share/man/man3m/ctanf.3m=ctan.3m
+f usr/share/man/man3m/ctanh.3m 0444 root bin
+s usr/share/man/man3m/ctanhf.3m=ctanh.3m
+s usr/share/man/man3m/ctanhl.3m=ctanh.3m
+s usr/share/man/man3m/ctanl.3m=ctan.3m
+f usr/share/man/man3m/erf.3m 0444 root bin
+f usr/share/man/man3m/erfc.3m 0444 root bin
+s usr/share/man/man3m/erfcf.3m=erfc.3m
+s usr/share/man/man3m/erfcl.3m=erfc.3m
+s usr/share/man/man3m/erff.3m=erf.3m
+s usr/share/man/man3m/erfl.3m=erf.3m
+f usr/share/man/man3m/exp.3m 0444 root bin
+f usr/share/man/man3m/exp2.3m 0444 root bin
+s usr/share/man/man3m/exp2f.3m=exp2.3m
+s usr/share/man/man3m/exp2l.3m=exp2.3m
+s usr/share/man/man3m/expf.3m=exp.3m
+s usr/share/man/man3m/expl.3m=exp.3m
+f usr/share/man/man3m/expm1.3m 0444 root bin
+s usr/share/man/man3m/expm1f.3m=expm1.3m
+s usr/share/man/man3m/expm1l.3m=expm1.3m
+f usr/share/man/man3m/fabs.3m 0444 root bin
+s usr/share/man/man3m/fabsf.3m=fabs.3m
+s usr/share/man/man3m/fabsl.3m=fabs.3m
+f usr/share/man/man3m/fdim.3m 0444 root bin
+s usr/share/man/man3m/fdimf.3m=fdim.3m
+s usr/share/man/man3m/fdiml.3m=fdim.3m
+f usr/share/man/man3m/feclearexcept.3m 0444 root bin
+f usr/share/man/man3m/fegetenv.3m 0444 root bin
+f usr/share/man/man3m/fegetexceptflag.3m 0444 root bin
+s usr/share/man/man3m/fegetprec.3m=fesetprec.3m
+f usr/share/man/man3m/fegetround.3m 0444 root bin
+f usr/share/man/man3m/feholdexcept.3m 0444 root bin
+f usr/share/man/man3m/feraiseexcept.3m 0444 root bin
+s usr/share/man/man3m/fesetenv.3m=fegetenv.3m
+s usr/share/man/man3m/fesetexceptflag.3m=fegetexceptflag.3m
+f usr/share/man/man3m/fesetprec.3m 0444 root bin
+s usr/share/man/man3m/fesetround.3m=fegetround.3m
+f usr/share/man/man3m/fetestexcept.3m 0444 root bin
+f usr/share/man/man3m/feupdateenv.3m 0444 root bin
+s usr/share/man/man3m/fex_get_handling.3m=fex_set_handling.3m
+s usr/share/man/man3m/fex_get_log.3m=fex_set_log.3m
+s usr/share/man/man3m/fex_get_log_depth.3m=fex_set_log.3m
+s usr/share/man/man3m/fex_getexcepthandler.3m=fex_set_handling.3m
+s usr/share/man/man3m/fex_log_entry.3m=fex_set_log.3m
+f usr/share/man/man3m/fex_merge_flags.3m 0444 root bin
+f usr/share/man/man3m/fex_set_handling.3m 0444 root bin
+f usr/share/man/man3m/fex_set_log.3m 0444 root bin
+s usr/share/man/man3m/fex_set_log_depth.3m=fex_set_log.3m
+s usr/share/man/man3m/fex_setexcepthandler.3m=fex_set_handling.3m
+f usr/share/man/man3m/floor.3m 0444 root bin
+s usr/share/man/man3m/floorf.3m=floor.3m
+s usr/share/man/man3m/floorl.3m=floor.3m
+f usr/share/man/man3m/fma.3m 0444 root bin
+s usr/share/man/man3m/fmaf.3m=fma.3m
+s usr/share/man/man3m/fmal.3m=fma.3m
+f usr/share/man/man3m/fmax.3m 0444 root bin
+s usr/share/man/man3m/fmaxf.3m=fmax.3m
+s usr/share/man/man3m/fmaxl.3m=fmax.3m
+f usr/share/man/man3m/fmin.3m 0444 root bin
+s usr/share/man/man3m/fminf.3m=fmin.3m
+s usr/share/man/man3m/fminl.3m=fmin.3m
+f usr/share/man/man3m/fmod.3m 0444 root bin
+s usr/share/man/man3m/fmodf.3m=fmod.3m
+s usr/share/man/man3m/fmodl.3m=fmod.3m
+f usr/share/man/man3m/fpclassify.3m 0444 root bin
+f usr/share/man/man3m/frexp.3m 0444 root bin
+s usr/share/man/man3m/frexpf.3m=frexp.3m
+s usr/share/man/man3m/frexpl.3m=frexp.3m
+s usr/share/man/man3m/gamma.3m=lgamma.3m
+s usr/share/man/man3m/gamma_r.3m=lgamma.3m
+s usr/share/man/man3m/gammaf.3m=lgamma.3m
+s usr/share/man/man3m/gammaf_r.3m=lgamma.3m
+s usr/share/man/man3m/gammal.3m=lgamma.3m
+s usr/share/man/man3m/gammal_r.3m=lgamma.3m
+f usr/share/man/man3m/hypot.3m 0444 root bin
+s usr/share/man/man3m/hypotf.3m=hypot.3m
+s usr/share/man/man3m/hypotl.3m=hypot.3m
+f usr/share/man/man3m/ilogb.3m 0444 root bin
+s usr/share/man/man3m/ilogbf.3m=ilogb.3m
+s usr/share/man/man3m/ilogbl.3m=ilogb.3m
+f usr/share/man/man3m/isfinite.3m 0444 root bin
+f usr/share/man/man3m/isgreater.3m 0444 root bin
+f usr/share/man/man3m/isgreaterequal.3m 0444 root bin
+f usr/share/man/man3m/isinf.3m 0444 root bin
+f usr/share/man/man3m/isless.3m 0444 root bin
+f usr/share/man/man3m/islessequal.3m 0444 root bin
+f usr/share/man/man3m/islessgreater.3m 0444 root bin
+f usr/share/man/man3m/isnan.3m 0444 root bin
+f usr/share/man/man3m/isnormal.3m 0444 root bin
+f usr/share/man/man3m/isunordered.3m 0444 root bin
+f usr/share/man/man3m/j0.3m 0444 root bin
+s usr/share/man/man3m/j0f.3m=j0.3m
+s usr/share/man/man3m/j0l.3m=j0.3m
+s usr/share/man/man3m/j1.3m=j0.3m
+s usr/share/man/man3m/j1f.3m=j0.3m
+s usr/share/man/man3m/j1l.3m=j0.3m
+s usr/share/man/man3m/jn.3m=j0.3m
+s usr/share/man/man3m/jnf.3m=j0.3m
+s usr/share/man/man3m/jnl.3m=j0.3m
+f usr/share/man/man3m/ldexp.3m 0444 root bin
+s usr/share/man/man3m/ldexpf.3m=ldexp.3m
+s usr/share/man/man3m/ldexpl.3m=ldexp.3m
+f usr/share/man/man3m/lgamma.3m 0444 root bin
+s usr/share/man/man3m/lgamma_r.3m=lgamma.3m
+s usr/share/man/man3m/lgammaf.3m=lgamma.3m
+s usr/share/man/man3m/lgammaf_r.3m=lgamma.3m
+s usr/share/man/man3m/lgammal.3m=lgamma.3m
+s usr/share/man/man3m/lgammal_r.3m=lgamma.3m
+f usr/share/man/man3m/llrint.3m 0444 root bin
+s usr/share/man/man3m/llrintf.3m=llrint.3m
+s usr/share/man/man3m/llrintl.3m=llrint.3m
+f usr/share/man/man3m/llround.3m 0444 root bin
+s usr/share/man/man3m/llroundf.3m=llround.3m
+s usr/share/man/man3m/llroundl.3m=llround.3m
+f usr/share/man/man3m/log.3m 0444 root bin
+f usr/share/man/man3m/log10.3m 0444 root bin
+s usr/share/man/man3m/log10f.3m=log10.3m
+s usr/share/man/man3m/log10l.3m=log10.3m
+f usr/share/man/man3m/log1p.3m 0444 root bin
+s usr/share/man/man3m/log1pf.3m=log1p.3m
+s usr/share/man/man3m/log1pl.3m=log1p.3m
+f usr/share/man/man3m/log2.3m 0444 root bin
+s usr/share/man/man3m/log2f.3m=log2.3m
+s usr/share/man/man3m/log2l.3m=log2.3m
+f usr/share/man/man3m/logb.3m 0444 root bin
+s usr/share/man/man3m/logbf.3m=logb.3m
+s usr/share/man/man3m/logbl.3m=logb.3m
+s usr/share/man/man3m/logf.3m=log.3m
+s usr/share/man/man3m/logl.3m=log.3m
+f usr/share/man/man3m/lrint.3m 0444 root bin
+s usr/share/man/man3m/lrintf.3m=lrint.3m
+s usr/share/man/man3m/lrintl.3m=lrint.3m
+f usr/share/man/man3m/lround.3m 0444 root bin
+s usr/share/man/man3m/lroundf.3m=lround.3m
+s usr/share/man/man3m/lroundl.3m=lround.3m
+f usr/share/man/man3m/matherr.3m 0444 root bin
+f usr/share/man/man3m/modf.3m 0444 root bin
+s usr/share/man/man3m/modff.3m=modf.3m
+s usr/share/man/man3m/modfl.3m=modf.3m
+f usr/share/man/man3m/nan.3m 0444 root bin
+s usr/share/man/man3m/nanf.3m=nan.3m
+s usr/share/man/man3m/nanl.3m=nan.3m
+f usr/share/man/man3m/nearbyint.3m 0444 root bin
+s usr/share/man/man3m/nearbyintf.3m=nearbyint.3m
+s usr/share/man/man3m/nearbyintl.3m=nearbyint.3m
+f usr/share/man/man3m/nextafter.3m 0444 root bin
+s usr/share/man/man3m/nextafterf.3m=nextafter.3m
+s usr/share/man/man3m/nextafterl.3m=nextafter.3m
+s usr/share/man/man3m/nexttoward.3m=nextafter.3m
+s usr/share/man/man3m/nexttowardf.3m=nextafter.3m
+s usr/share/man/man3m/nexttowardl.3m=nextafter.3m
+f usr/share/man/man3m/pow.3m 0444 root bin
+s usr/share/man/man3m/powf.3m=pow.3m
+s usr/share/man/man3m/powl.3m=pow.3m
+f usr/share/man/man3m/remainder.3m 0444 root bin
+s usr/share/man/man3m/remainderf.3m=remainder.3m
+s usr/share/man/man3m/remainderl.3m=remainder.3m
+f usr/share/man/man3m/remquo.3m 0444 root bin
+s usr/share/man/man3m/remquof.3m=remquo.3m
+s usr/share/man/man3m/remquol.3m=remquo.3m
+f usr/share/man/man3m/rint.3m 0444 root bin
+s usr/share/man/man3m/rintf.3m=rint.3m
+s usr/share/man/man3m/rintl.3m=rint.3m
+f usr/share/man/man3m/round.3m 0444 root bin
+s usr/share/man/man3m/roundf.3m=round.3m
+s usr/share/man/man3m/roundl.3m=round.3m
+f usr/share/man/man3m/scalb.3m 0444 root bin
+s usr/share/man/man3m/scalbf.3m=scalb.3m
+s usr/share/man/man3m/scalbl.3m=scalb.3m
+f usr/share/man/man3m/scalbln.3m 0444 root bin
+s usr/share/man/man3m/scalblnf.3m=scalbln.3m
+s usr/share/man/man3m/scalblnl.3m=scalbln.3m
+s usr/share/man/man3m/scalbn.3m=scalbln.3m
+s usr/share/man/man3m/scalbnf.3m=scalbln.3m
+s usr/share/man/man3m/scalbnl.3m=scalbln.3m
+f usr/share/man/man3m/signbit.3m 0444 root bin
+f usr/share/man/man3m/significand.3m 0444 root bin
+s usr/share/man/man3m/significandf.3m=significand.3m
+s usr/share/man/man3m/significandl.3m=significand.3m
+f usr/share/man/man3m/sin.3m 0444 root bin
+f usr/share/man/man3m/sincos.3m 0444 root bin
+s usr/share/man/man3m/sincosf.3m=sincos.3m
+s usr/share/man/man3m/sincosl.3m=sincos.3m
+s usr/share/man/man3m/sinf.3m=sin.3m
+f usr/share/man/man3m/sinh.3m 0444 root bin
+s usr/share/man/man3m/sinhf.3m=sinh.3m
+s usr/share/man/man3m/sinhl.3m=sinh.3m
+s usr/share/man/man3m/sinl.3m=sin.3m
+f usr/share/man/man3m/sqrt.3m 0444 root bin
+s usr/share/man/man3m/sqrtf.3m=sqrt.3m
+s usr/share/man/man3m/sqrtl.3m=sqrt.3m
+f usr/share/man/man3m/tan.3m 0444 root bin
+s usr/share/man/man3m/tanf.3m=tan.3m
+f usr/share/man/man3m/tanh.3m 0444 root bin
+s usr/share/man/man3m/tanhf.3m=tanh.3m
+s usr/share/man/man3m/tanhl.3m=tanh.3m
+s usr/share/man/man3m/tanl.3m=tan.3m
+f usr/share/man/man3m/tgamma.3m 0444 root bin
+s usr/share/man/man3m/tgammaf.3m=tgamma.3m
+s usr/share/man/man3m/tgammal.3m=tgamma.3m
+f usr/share/man/man3m/trunc.3m 0444 root bin
+s usr/share/man/man3m/truncf.3m=trunc.3m
+s usr/share/man/man3m/truncl.3m=trunc.3m
+f usr/share/man/man3m/y0.3m 0444 root bin
+s usr/share/man/man3m/y0f.3m=y0.3m
+s usr/share/man/man3m/y0l.3m=y0.3m
+s usr/share/man/man3m/y1.3m=y0.3m
+s usr/share/man/man3m/y1f.3m=y0.3m
+s usr/share/man/man3m/y1l.3m=y0.3m
+s usr/share/man/man3m/yn.3m=y0.3m
+s usr/share/man/man3m/ynf.3m=y0.3m
+s usr/share/man/man3m/ynl.3m=y0.3m
+d usr/share/man/man3mail 0755 root bin
+f usr/share/man/man3mail/maillock.3mail 0444 root bin
+s usr/share/man/man3mail/mailunlock.3mail=maillock.3mail
+s usr/share/man/man3mail/touchlock.3mail=maillock.3mail
+d usr/share/man/man3malloc 0755 root bin
+f usr/share/man/man3malloc/bsdmalloc.3malloc 0444 root bin
+s usr/share/man/man3malloc/calloc.3malloc=malloc.3malloc
+s usr/share/man/man3malloc/free.3malloc=malloc.3malloc
+s usr/share/man/man3malloc/mallinfo.3malloc=malloc.3malloc
+f usr/share/man/man3malloc/malloc.3malloc 0444 root bin
+s usr/share/man/man3malloc/mallocctl.3malloc=mtmalloc.3malloc
+s usr/share/man/man3malloc/mallopt.3malloc=malloc.3malloc
+f usr/share/man/man3malloc/mapmalloc.3malloc 0444 root bin
+s usr/share/man/man3malloc/memalign.3malloc=malloc.3malloc
+f usr/share/man/man3malloc/mtmalloc.3malloc 0444 root bin
+s usr/share/man/man3malloc/realloc.3malloc=malloc.3malloc
+f usr/share/man/man3malloc/umem_alloc.3malloc 0444 root bin
+s usr/share/man/man3malloc/umem_cache_alloc.3malloc=umem_cache_create.3malloc
+f usr/share/man/man3malloc/umem_cache_create.3malloc 0444 root bin
+s usr/share/man/man3malloc/umem_cache_destroy.3malloc=umem_cache_create.3malloc
+s usr/share/man/man3malloc/umem_cache_free.3malloc=umem_cache_create.3malloc
+f usr/share/man/man3malloc/umem_debug.3malloc 0444 root bin
+s usr/share/man/man3malloc/umem_free.3malloc=umem_alloc.3malloc
+s usr/share/man/man3malloc/umem_nofail_callback.3malloc=umem_alloc.3malloc
+f usr/share/man/man3malloc/umem_setmtbf.3malloc 0444 root bin
+s usr/share/man/man3malloc/umem_zalloc.3malloc=umem_alloc.3malloc
+s usr/share/man/man3malloc/valloc.3malloc=malloc.3malloc
+f usr/share/man/man3malloc/watchmalloc.3malloc 0444 root bin
+d usr/share/man/man3mp 0755 root bin
+f usr/share/man/man3mp/mp.3mp 0444 root bin
+s usr/share/man/man3mp/mp_gcd.3mp=mp.3mp
+s usr/share/man/man3mp/mp_itom.3mp=mp.3mp
+s usr/share/man/man3mp/mp_madd.3mp=mp.3mp
+s usr/share/man/man3mp/mp_mcmp.3mp=mp.3mp
+s usr/share/man/man3mp/mp_mdiv.3mp=mp.3mp
+s usr/share/man/man3mp/mp_mfree.3mp=mp.3mp
+s usr/share/man/man3mp/mp_min.3mp=mp.3mp
+s usr/share/man/man3mp/mp_mout.3mp=mp.3mp
+s usr/share/man/man3mp/mp_msqrt.3mp=mp.3mp
+s usr/share/man/man3mp/mp_msub.3mp=mp.3mp
+s usr/share/man/man3mp/mp_mtox.3mp=mp.3mp
+s usr/share/man/man3mp/mp_mult.3mp=mp.3mp
+s usr/share/man/man3mp/mp_pow.3mp=mp.3mp
+s usr/share/man/man3mp/mp_rpow.3mp=mp.3mp
+s usr/share/man/man3mp/mp_sdiv.3mp=mp.3mp
+s usr/share/man/man3mp/mp_xtom.3mp=mp.3mp
+d usr/share/man/man3mpapi 0755 root bin
+f usr/share/man/man3mpapi/MP_AssignLogicalUnitToTPG.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_CancelOverridePath.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_CompareOIDs.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_DeregisterForObjectPropertyChanges.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_DeregisterForObjectVisibilityChanges.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_DeregisterPlugin.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_DisableAutoFailback.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_DisableAutoProbing.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_DisablePath.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_EnableAutoFailback.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_EnableAutoProbing.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_EnablePath.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_FreeOidList.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetAssociatedPathOidList.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetAssociatedPluginOid.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetAssociatedTPGOidList.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetDeviceProductOidList.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetDeviceProductProperties.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetInitiatorPortOidList.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetInitiatorPortProperties.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetLibraryProperties.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetMPLogicalUnitProperties.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetMPLuOidListFromTPG.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetMultipathLus.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetObjectType.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetPathLogicalUnitProperties.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetPluginOidList.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetPluginProperties.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetProprietaryLoadBalanceOidList.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetProprietaryLoadBalanceProperties.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetTargetPortGroupProperties.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetTargetPortOidList.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_GetTargetPortProperties.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_RegisterForObjectPropertyChanges.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_RegisterForObjectVisibilityChanges.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_RegisterPlugin.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_SetFailbackPollingRate.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_SetLogicalUnitLoadBalanceType.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_SetOverridePath.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_SetPathWeight.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_SetPluginLoadBalanceType.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_SetProbingPollingRate.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_SetProprietaryProperties.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/MP_SetTPGAccess.3mpapi 0444 root bin
+f usr/share/man/man3mpapi/Sun_MP_SendScsiCmd.3mpapi 0444 root bin
+d usr/share/man/man3mvec 0755 root bin
+f usr/share/man/man3mvec/vatan2_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vatan_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vcos_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vcospi_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vexp_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vhypot_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vlog_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vpow_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vrhypot_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vrsqrt_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vsin_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vsincos_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vsincospi_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vsinpi_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vsqrt_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vz_abs_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vz_exp_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vz_log_.3mvec 0444 root bin
+f usr/share/man/man3mvec/vz_pow_.3mvec 0444 root bin
+d usr/share/man/man3nsl 0755 root bin
+s usr/share/man/man3nsl/auth_destroy.3nsl=rpc_clnt_auth.3nsl
+s usr/share/man/man3nsl/authdes_create.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/authdes_getucred.3nsl=secure_rpc.3nsl
+s usr/share/man/man3nsl/authdes_seccreate.3nsl=secure_rpc.3nsl
+s usr/share/man/man3nsl/authnone_create.3nsl=rpc_clnt_auth.3nsl
+s usr/share/man/man3nsl/authsys_create.3nsl=rpc_clnt_auth.3nsl
+s usr/share/man/man3nsl/authsys_create_default.3nsl=rpc_clnt_auth.3nsl
+s usr/share/man/man3nsl/authunix_create.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/authunix_create_default.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/callrpc.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/clnt_broadcast.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/clnt_call.3nsl=rpc_clnt_calls.3nsl
+s usr/share/man/man3nsl/clnt_control.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_create.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_create_timed.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_create_vers.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_create_vers_timed.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_destroy.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_dg_create.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_door_create.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_freeres.3nsl=rpc_clnt_calls.3nsl
+s usr/share/man/man3nsl/clnt_geterr.3nsl=rpc_clnt_calls.3nsl
+s usr/share/man/man3nsl/clnt_pcreateerror.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_perrno.3nsl=rpc_clnt_calls.3nsl
+s usr/share/man/man3nsl/clnt_perror.3nsl=rpc_clnt_calls.3nsl
+s usr/share/man/man3nsl/clnt_raw_create.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_send.3nsl=rpc_clnt_calls.3nsl
+s usr/share/man/man3nsl/clnt_spcreateerror.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_sperrno.3nsl=rpc_clnt_calls.3nsl
+s usr/share/man/man3nsl/clnt_sperror.3nsl=rpc_clnt_calls.3nsl
+s usr/share/man/man3nsl/clnt_tli_create.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_tp_create.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_tp_create_timed.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clnt_vc_create.3nsl=rpc_clnt_create.3nsl
+s usr/share/man/man3nsl/clntraw_create.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/clnttcp_create.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/clntudp_bufcreate.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/clntudp_create.3nsl=rpc_soc.3nsl
+f usr/share/man/man3nsl/dial.3nsl 0444 root bin
+f usr/share/man/man3nsl/doconfig.3nsl 0444 root bin
+s usr/share/man/man3nsl/endhostent.3nsl=gethostbyname.3nsl
+s usr/share/man/man3nsl/endnetconfig.3nsl=getnetconfig.3nsl
+s usr/share/man/man3nsl/endnetpath.3nsl=getnetpath.3nsl
+s usr/share/man/man3nsl/endrpcent.3nsl=getrpcbyname.3nsl
+s usr/share/man/man3nsl/freeipsecalgent.3nsl=getipsecalgbyname.3nsl
+s usr/share/man/man3nsl/freenetconfigent.3nsl=getnetconfig.3nsl
+s usr/share/man/man3nsl/get_myaddress.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/gethostbyaddr.3nsl=gethostbyname.3nsl
+s usr/share/man/man3nsl/gethostbyaddr_r.3nsl=gethostbyname.3nsl
+f usr/share/man/man3nsl/gethostbyname.3nsl 0444 root bin
+s usr/share/man/man3nsl/gethostbyname_r.3nsl=gethostbyname.3nsl
+s usr/share/man/man3nsl/gethostent.3nsl=gethostbyname.3nsl
+s usr/share/man/man3nsl/gethostent_r.3nsl=gethostbyname.3nsl
+f usr/share/man/man3nsl/getipsecalgbyname.3nsl 0444 root bin
+s usr/share/man/man3nsl/getipsecalgbynum.3nsl=getipsecalgbyname.3nsl
+f usr/share/man/man3nsl/getipsecprotobyname.3nsl 0444 root bin
+s usr/share/man/man3nsl/getipsecprotobynum.3nsl=getipsecprotobyname.3nsl
+f usr/share/man/man3nsl/getnetconfig.3nsl 0444 root bin
+s usr/share/man/man3nsl/getnetconfigent.3nsl=getnetconfig.3nsl
+s usr/share/man/man3nsl/getnetname.3nsl=secure_rpc.3nsl
+f usr/share/man/man3nsl/getnetpath.3nsl 0444 root bin
+f usr/share/man/man3nsl/getpublickey.3nsl 0444 root bin
+f usr/share/man/man3nsl/getrpcbyname.3nsl 0444 root bin
+s usr/share/man/man3nsl/getrpcbyname_r.3nsl=getrpcbyname.3nsl
+s usr/share/man/man3nsl/getrpcbynumber.3nsl=getrpcbyname.3nsl
+s usr/share/man/man3nsl/getrpcbynumber_r.3nsl=getrpcbyname.3nsl
+s usr/share/man/man3nsl/getrpcent.3nsl=getrpcbyname.3nsl
+s usr/share/man/man3nsl/getrpcent_r.3nsl=getrpcbyname.3nsl
+s usr/share/man/man3nsl/getrpcport.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/getsecretkey.3nsl=getpublickey.3nsl
+s usr/share/man/man3nsl/host2netname.3nsl=secure_rpc.3nsl
+s usr/share/man/man3nsl/key_decryptsession.3nsl=secure_rpc.3nsl
+s usr/share/man/man3nsl/key_encryptsession.3nsl=secure_rpc.3nsl
+s usr/share/man/man3nsl/key_gendes.3nsl=secure_rpc.3nsl
+s usr/share/man/man3nsl/key_secretkey_is_set.3nsl=secure_rpc.3nsl
+s usr/share/man/man3nsl/key_setsecret.3nsl=secure_rpc.3nsl
+s usr/share/man/man3nsl/nc_perror.3nsl=getnetconfig.3nsl
+s usr/share/man/man3nsl/nc_sperror.3nsl=getnetconfig.3nsl
+f usr/share/man/man3nsl/netdir.3nsl 0444 root bin
+s usr/share/man/man3nsl/netdir_free.3nsl=netdir.3nsl
+s usr/share/man/man3nsl/netdir_getbyaddr.3nsl=netdir.3nsl
+s usr/share/man/man3nsl/netdir_getbyname.3nsl=netdir.3nsl
+s usr/share/man/man3nsl/netdir_mergeaddr.3nsl=netdir.3nsl
+s usr/share/man/man3nsl/netdir_options.3nsl=netdir.3nsl
+s usr/share/man/man3nsl/netdir_perror.3nsl=netdir.3nsl
+s usr/share/man/man3nsl/netdir_sperror.3nsl=netdir.3nsl
+s usr/share/man/man3nsl/netname2host.3nsl=secure_rpc.3nsl
+s usr/share/man/man3nsl/netname2user.3nsl=secure_rpc.3nsl
+f usr/share/man/man3nsl/nlsgetcall.3nsl 0444 root bin
+f usr/share/man/man3nsl/nlsprovider.3nsl 0444 root bin
+f usr/share/man/man3nsl/nlsrequest.3nsl 0444 root bin
+s usr/share/man/man3nsl/pmap_getmaps.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/pmap_getport.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/pmap_rmtcall.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/pmap_set.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/pmap_unset.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/publickey.3nsl=getpublickey.3nsl
+s usr/share/man/man3nsl/registerrpc.3nsl=rpc_soc.3nsl
+f usr/share/man/man3nsl/rpc.3nsl 0444 root bin
+s usr/share/man/man3nsl/rpc_broadcast.3nsl=rpc_clnt_calls.3nsl
+s usr/share/man/man3nsl/rpc_broadcast_exp.3nsl=rpc_clnt_calls.3nsl
+s usr/share/man/man3nsl/rpc_call.3nsl=rpc_clnt_calls.3nsl
+f usr/share/man/man3nsl/rpc_clnt_auth.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_clnt_calls.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_clnt_create.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_control.3nsl 0444 root bin
+s usr/share/man/man3nsl/rpc_createerr.3nsl=rpc_clnt_create.3nsl
+f usr/share/man/man3nsl/rpc_gss_get_error.3nsl 0444 root bin
+s usr/share/man/man3nsl/rpc_gss_get_mech_info.3nsl=rpc_gss_get_mechanisms.3nsl
+f usr/share/man/man3nsl/rpc_gss_get_mechanisms.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_gss_get_principal_name.3nsl 0444 root bin
+s usr/share/man/man3nsl/rpc_gss_get_versions.3nsl=rpc_gss_get_mechanisms.3nsl
+f usr/share/man/man3nsl/rpc_gss_getcred.3nsl 0444 root bin
+s usr/share/man/man3nsl/rpc_gss_is_installed.3nsl=rpc_gss_get_mechanisms.3nsl
+f usr/share/man/man3nsl/rpc_gss_max_data_length.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_gss_mech_to_oid.3nsl 0444 root bin
+s usr/share/man/man3nsl/rpc_gss_qop_to_num.3nsl=rpc_gss_mech_to_oid.3nsl
+f usr/share/man/man3nsl/rpc_gss_seccreate.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_gss_set_callback.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_gss_set_defaults.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_gss_set_svc_name.3nsl 0444 root bin
+s usr/share/man/man3nsl/rpc_gss_svc_max_data_length.3nsl=rpc_gss_max_data_length.3nsl
+s usr/share/man/man3nsl/rpc_reg.3nsl=rpc_svc_reg.3nsl
+f usr/share/man/man3nsl/rpc_soc.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_svc_calls.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_svc_create.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_svc_err.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_svc_input.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_svc_reg.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpc_xdr.3nsl 0444 root bin
+s usr/share/man/man3nsl/rpcb_getaddr.3nsl=rpcbind.3nsl
+s usr/share/man/man3nsl/rpcb_getmaps.3nsl=rpcbind.3nsl
+s usr/share/man/man3nsl/rpcb_gettime.3nsl=rpcbind.3nsl
+s usr/share/man/man3nsl/rpcb_rmtcall.3nsl=rpcbind.3nsl
+s usr/share/man/man3nsl/rpcb_set.3nsl=rpcbind.3nsl
+s usr/share/man/man3nsl/rpcb_unset.3nsl=rpcbind.3nsl
+f usr/share/man/man3nsl/rpcbind.3nsl 0444 root bin
+f usr/share/man/man3nsl/rpcsec_gss.3nsl 0444 root bin
+f usr/share/man/man3nsl/secure_rpc.3nsl 0444 root bin
+s usr/share/man/man3nsl/sethostent.3nsl=gethostbyname.3nsl
+s usr/share/man/man3nsl/setnetconfig.3nsl=getnetconfig.3nsl
+s usr/share/man/man3nsl/setnetpath.3nsl=getnetpath.3nsl
+s usr/share/man/man3nsl/setrpcent.3nsl=getrpcbyname.3nsl
+s usr/share/man/man3nsl/svc_add_input.3nsl=rpc_svc_input.3nsl
+s usr/share/man/man3nsl/svc_auth_reg.3nsl=rpc_svc_reg.3nsl
+s usr/share/man/man3nsl/svc_control.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svc_create.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svc_destroy.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svc_dg_create.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svc_dg_enablecache.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_done.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_door_create.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svc_exit.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_fd_create.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svc_fd_negotiate_ucred.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_fds.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/svc_fdset.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_freeargs.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_getargs.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_getcaller.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/svc_getcallerucred.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_getreq.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/svc_getreq_common.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_getreq_poll.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_getreqset.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_getrpccaller.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_max_pollfd.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_pollfd.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_raw_create.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svc_reg.3nsl=rpc_svc_reg.3nsl
+s usr/share/man/man3nsl/svc_register.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/svc_remove_input.3nsl=rpc_svc_input.3nsl
+s usr/share/man/man3nsl/svc_run.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_sendreply.3nsl=rpc_svc_calls.3nsl
+s usr/share/man/man3nsl/svc_tli_create.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svc_tp_create.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svc_tp_create_addr.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svc_unreg.3nsl=rpc_svc_reg.3nsl
+s usr/share/man/man3nsl/svc_unregister.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/svc_vc_create.3nsl=rpc_svc_create.3nsl
+s usr/share/man/man3nsl/svcerr_auth.3nsl=rpc_svc_err.3nsl
+s usr/share/man/man3nsl/svcerr_decode.3nsl=rpc_svc_err.3nsl
+s usr/share/man/man3nsl/svcerr_noproc.3nsl=rpc_svc_err.3nsl
+s usr/share/man/man3nsl/svcerr_noprog.3nsl=rpc_svc_err.3nsl
+s usr/share/man/man3nsl/svcerr_progvers.3nsl=rpc_svc_err.3nsl
+s usr/share/man/man3nsl/svcerr_systemerr.3nsl=rpc_svc_err.3nsl
+s usr/share/man/man3nsl/svcerr_weakauth.3nsl=rpc_svc_err.3nsl
+s usr/share/man/man3nsl/svcfd_create.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/svcraw_create.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/svctcp_create.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/svcudp_bufcreate.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/svcudp_create.3nsl=rpc_soc.3nsl
+f usr/share/man/man3nsl/t_accept.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_alloc.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_bind.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_close.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_connect.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_errno.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_error.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_free.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_getinfo.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_getprotaddr.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_getstate.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_listen.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_look.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_open.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_optmgmt.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_rcv.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_rcvconnect.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_rcvdis.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_rcvrel.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_rcvreldata.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_rcvudata.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_rcvuderr.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_rcvv.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_rcvvudata.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_snd.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_snddis.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_sndrel.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_sndreldata.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_sndudata.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_sndv.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_sndvudata.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_strerror.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_sync.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_sysconf.3nsl 0444 root bin
+f usr/share/man/man3nsl/t_unbind.3nsl 0444 root bin
+s usr/share/man/man3nsl/taddr2uaddr.3nsl=netdir.3nsl
+s usr/share/man/man3nsl/uaddr2taddr.3nsl=netdir.3nsl
+s usr/share/man/man3nsl/undial.3nsl=dial.3nsl
+s usr/share/man/man3nsl/user2netname.3nsl=secure_rpc.3nsl
+f usr/share/man/man3nsl/xdr.3nsl 0444 root bin
+s usr/share/man/man3nsl/xdr_accepted_reply.3nsl=rpc_xdr.3nsl
+f usr/share/man/man3nsl/xdr_admin.3nsl 0444 root bin
+s usr/share/man/man3nsl/xdr_array.3nsl=xdr_complex.3nsl
+s usr/share/man/man3nsl/xdr_authsys_parms.3nsl=rpc_xdr.3nsl
+s usr/share/man/man3nsl/xdr_authunix_parms.3nsl=rpc_soc.3nsl
+s usr/share/man/man3nsl/xdr_bool.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_bytes.3nsl=xdr_complex.3nsl
+s usr/share/man/man3nsl/xdr_callhdr.3nsl=rpc_xdr.3nsl
+s usr/share/man/man3nsl/xdr_callmsg.3nsl=rpc_xdr.3nsl
+s usr/share/man/man3nsl/xdr_char.3nsl=xdr_simple.3nsl
+f usr/share/man/man3nsl/xdr_complex.3nsl 0444 root bin
+s usr/share/man/man3nsl/xdr_control.3nsl=xdr_admin.3nsl
+f usr/share/man/man3nsl/xdr_create.3nsl 0444 root bin
+s usr/share/man/man3nsl/xdr_destroy.3nsl=xdr_create.3nsl
+s usr/share/man/man3nsl/xdr_double.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_enum.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_float.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_free.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_getpos.3nsl=xdr_admin.3nsl
+s usr/share/man/man3nsl/xdr_hyper.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_inline.3nsl=xdr_admin.3nsl
+s usr/share/man/man3nsl/xdr_int.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_long.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_longlong_t.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_opaque.3nsl=xdr_complex.3nsl
+s usr/share/man/man3nsl/xdr_opaque_auth.3nsl=rpc_xdr.3nsl
+s usr/share/man/man3nsl/xdr_pointer.3nsl=xdr_complex.3nsl
+s usr/share/man/man3nsl/xdr_quadruple.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_reference.3nsl=xdr_complex.3nsl
+s usr/share/man/man3nsl/xdr_rejected_reply.3nsl=rpc_xdr.3nsl
+s usr/share/man/man3nsl/xdr_replymsg.3nsl=rpc_xdr.3nsl
+s usr/share/man/man3nsl/xdr_setpos.3nsl=xdr_admin.3nsl
+s usr/share/man/man3nsl/xdr_short.3nsl=xdr_simple.3nsl
+f usr/share/man/man3nsl/xdr_simple.3nsl 0444 root bin
+s usr/share/man/man3nsl/xdr_sizeof.3nsl=xdr_admin.3nsl
+s usr/share/man/man3nsl/xdr_string.3nsl=xdr_complex.3nsl
+s usr/share/man/man3nsl/xdr_u_char.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_u_hyper.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_u_int.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_u_long.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_u_longlong_t.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_u_short.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_union.3nsl=xdr_complex.3nsl
+s usr/share/man/man3nsl/xdr_vector.3nsl=xdr_complex.3nsl
+s usr/share/man/man3nsl/xdr_void.3nsl=xdr_simple.3nsl
+s usr/share/man/man3nsl/xdr_wrapstring.3nsl=xdr_complex.3nsl
+s usr/share/man/man3nsl/xdrmem_create.3nsl=xdr_create.3nsl
+s usr/share/man/man3nsl/xdrrec_create.3nsl=xdr_create.3nsl
+s usr/share/man/man3nsl/xdrrec_endofrecord.3nsl=xdr_admin.3nsl
+s usr/share/man/man3nsl/xdrrec_eof.3nsl=xdr_admin.3nsl
+s usr/share/man/man3nsl/xdrrec_readbytes.3nsl=xdr_admin.3nsl
+s usr/share/man/man3nsl/xdrrec_skiprecord.3nsl=xdr_admin.3nsl
+s usr/share/man/man3nsl/xdrstdio_create.3nsl=xdr_create.3nsl
+s usr/share/man/man3nsl/xprt_register.3nsl=rpc_svc_reg.3nsl
+s usr/share/man/man3nsl/xprt_unregister.3nsl=rpc_svc_reg.3nsl
+s usr/share/man/man3nsl/yp_all.3nsl=ypclnt.3nsl
+s usr/share/man/man3nsl/yp_bind.3nsl=ypclnt.3nsl
+s usr/share/man/man3nsl/yp_first.3nsl=ypclnt.3nsl
+s usr/share/man/man3nsl/yp_get_default_domain.3nsl=ypclnt.3nsl
+s usr/share/man/man3nsl/yp_master.3nsl=ypclnt.3nsl
+s usr/share/man/man3nsl/yp_match.3nsl=ypclnt.3nsl
+s usr/share/man/man3nsl/yp_next.3nsl=ypclnt.3nsl
+s usr/share/man/man3nsl/yp_order.3nsl=ypclnt.3nsl
+s usr/share/man/man3nsl/yp_unbind.3nsl=ypclnt.3nsl
+f usr/share/man/man3nsl/yp_update.3nsl 0444 root bin
+f usr/share/man/man3nsl/ypclnt.3nsl 0444 root bin
+s usr/share/man/man3nsl/yperr_string.3nsl=ypclnt.3nsl
+s usr/share/man/man3nsl/ypprot_err.3nsl=ypclnt.3nsl
+d usr/share/man/man3nvpair 0755 root bin
+s usr/share/man/man3nvpair/nv_alloc_fini.3nvpair=nvlist_alloc.3nvpair
+s usr/share/man/man3nvpair/nv_alloc_init.3nvpair=nvlist_alloc.3nvpair
+s usr/share/man/man3nvpair/nv_alloc_reset.3nvpair=nvlist_alloc.3nvpair
+f usr/share/man/man3nvpair/nvlist_add_boolean.3nvpair 0444 root bin
+s usr/share/man/man3nvpair/nvlist_add_boolean_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_boolean_value.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_byte.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_byte_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_double.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_int16.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_int16_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_int32.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_int32_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_int64.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_int64_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_int8.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_int8_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_nvlist.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_nvlist_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_nvpair.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_string.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_string_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_uint16.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_uint16_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_uint32.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_uint32_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_uint64.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_uint64_array.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_uint8.3nvpair=nvlist_add_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_add_uint8_array.3nvpair=nvlist_add_boolean.3nvpair
+f usr/share/man/man3nvpair/nvlist_alloc.3nvpair 0444 root bin
+s usr/share/man/man3nvpair/nvlist_dup.3nvpair=nvlist_alloc.3nvpair
+s usr/share/man/man3nvpair/nvlist_empty.3nvpair=nvlist_lookup_nvpair.3nvpair
+s usr/share/man/man3nvpair/nvlist_exists.3nvpair=nvlist_lookup_nvpair.3nvpair
+s usr/share/man/man3nvpair/nvlist_free.3nvpair=nvlist_alloc.3nvpair
+f usr/share/man/man3nvpair/nvlist_lookup_boolean.3nvpair 0444 root bin
+s usr/share/man/man3nvpair/nvlist_lookup_boolean_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_boolean_value.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_byte.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_byte_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_double.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_int16.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_int16_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_int32.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_int32_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_int64.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_int64_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_int8.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_int8_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_nv_alloc.3nvpair=nvlist_alloc.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_nvlist.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_nvlist_array.3nvpair=nvlist_lookup_boolean.3nvpair
+f usr/share/man/man3nvpair/nvlist_lookup_nvpair.3nvpair 0444 root bin
+s usr/share/man/man3nvpair/nvlist_lookup_pairs.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_string.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_string_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_uint16.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_uint16_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_uint32.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_uint32_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_uint64.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_uint64_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_uint8.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_lookup_uint8_array.3nvpair=nvlist_lookup_boolean.3nvpair
+s usr/share/man/man3nvpair/nvlist_merge.3nvpair=nvlist_alloc.3nvpair
+f usr/share/man/man3nvpair/nvlist_next_nvpair.3nvpair 0444 root bin
+s usr/share/man/man3nvpair/nvlist_pack.3nvpair=nvlist_alloc.3nvpair
+f usr/share/man/man3nvpair/nvlist_remove.3nvpair 0444 root bin
+s usr/share/man/man3nvpair/nvlist_remove_all.3nvpair=nvlist_remove.3nvpair
+s usr/share/man/man3nvpair/nvlist_size.3nvpair=nvlist_alloc.3nvpair
+s usr/share/man/man3nvpair/nvlist_unpack.3nvpair=nvlist_alloc.3nvpair
+s usr/share/man/man3nvpair/nvlist_xalloc.3nvpair=nvlist_alloc.3nvpair
+s usr/share/man/man3nvpair/nvlist_xdup.3nvpair=nvlist_alloc.3nvpair
+s usr/share/man/man3nvpair/nvlist_xpack.3nvpair=nvlist_alloc.3nvpair
+s usr/share/man/man3nvpair/nvlist_xunpack.3nvpair=nvlist_alloc.3nvpair
+s usr/share/man/man3nvpair/nvpair_name.3nvpair=nvlist_next_nvpair.3nvpair
+s usr/share/man/man3nvpair/nvpair_type.3nvpair=nvlist_next_nvpair.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_boolean_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_boolean_value.3nvpair=nvpair_value_byte.3nvpair
+f usr/share/man/man3nvpair/nvpair_value_byte.3nvpair 0444 root bin
+s usr/share/man/man3nvpair/nvpair_value_byte_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_double.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_int16.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_int16_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_int32.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_int32_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_int64.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_int64_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_int8.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_int8_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_nvlist.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_nvlist_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_string.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_string_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_uint16.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_uint16_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_uint32.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_uint32_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_uint64.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_uint64_array.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_uint8.3nvpair=nvpair_value_byte.3nvpair
+s usr/share/man/man3nvpair/nvpair_value_uint8_array.3nvpair=nvpair_value_byte.3nvpair
+d usr/share/man/man3ofmt 0755 root bin
+f usr/share/man/man3ofmt/ofmt.3ofmt 0444 root bin
+s usr/share/man/man3ofmt/ofmt_close.3ofmt=ofmt.3ofmt
+s usr/share/man/man3ofmt/ofmt_open.3ofmt=ofmt.3ofmt
+s usr/share/man/man3ofmt/ofmt_print.3ofmt=ofmt.3ofmt
+s usr/share/man/man3ofmt/ofmt_print_header.3ofmt=ofmt.3ofmt
+s usr/share/man/man3ofmt/ofmt_set_fs.3ofmt=ofmt.3ofmt
+s usr/share/man/man3ofmt/ofmt_strerror.3ofmt=ofmt.3ofmt
+s usr/share/man/man3ofmt/ofmt_update_winsize.3ofmt=ofmt.3ofmt
+d usr/share/man/man3pam 0755 root bin
+f usr/share/man/man3pam/pam.3pam 0444 root bin
+f usr/share/man/man3pam/pam_acct_mgmt.3pam 0444 root bin
+f usr/share/man/man3pam/pam_authenticate.3pam 0444 root bin
+f usr/share/man/man3pam/pam_chauthtok.3pam 0444 root bin
+s usr/share/man/man3pam/pam_close_session.3pam=pam_open_session.3pam
+s usr/share/man/man3pam/pam_end.3pam=pam_start.3pam
+s usr/share/man/man3pam/pam_get_data.3pam=pam_set_data.3pam
+s usr/share/man/man3pam/pam_get_item.3pam=pam_set_item.3pam
+f usr/share/man/man3pam/pam_get_user.3pam 0444 root bin
+f usr/share/man/man3pam/pam_getenv.3pam 0444 root bin
+f usr/share/man/man3pam/pam_getenvlist.3pam 0444 root bin
+f usr/share/man/man3pam/pam_open_session.3pam 0444 root bin
+f usr/share/man/man3pam/pam_putenv.3pam 0444 root bin
+f usr/share/man/man3pam/pam_set_data.3pam 0444 root bin
+f usr/share/man/man3pam/pam_set_item.3pam 0444 root bin
+f usr/share/man/man3pam/pam_setcred.3pam 0444 root bin
+f usr/share/man/man3pam/pam_sm.3pam 0444 root bin
+f usr/share/man/man3pam/pam_sm_acct_mgmt.3pam 0444 root bin
+f usr/share/man/man3pam/pam_sm_authenticate.3pam 0444 root bin
+f usr/share/man/man3pam/pam_sm_chauthtok.3pam 0444 root bin
+s usr/share/man/man3pam/pam_sm_close_session.3pam=pam_sm_open_session.3pam
+f usr/share/man/man3pam/pam_sm_open_session.3pam 0444 root bin
+f usr/share/man/man3pam/pam_sm_setcred.3pam 0444 root bin
+f usr/share/man/man3pam/pam_start.3pam 0444 root bin
+f usr/share/man/man3pam/pam_strerror.3pam 0444 root bin
+d usr/share/man/man3papi 0755 root bin
+f usr/share/man/man3papi/papiAttributeListAddValue.3papi 0444 root bin
+f usr/share/man/man3papi/papiJobSubmit.3papi 0444 root bin
+f usr/share/man/man3papi/papiLibrarySupportedCall.3papi 0444 root bin
+f usr/share/man/man3papi/papiPrintersList.3papi 0444 root bin
+f usr/share/man/man3papi/papiServiceCreate.3papi 0444 root bin
+f usr/share/man/man3papi/papiStatusString.3papi 0444 root bin
+d usr/share/man/man3picl 0755 root bin
+f usr/share/man/man3picl/libpicl.3picl 0444 root bin
+f usr/share/man/man3picl/picl_find_node.3picl 0444 root bin
+f usr/share/man/man3picl/picl_get_first_prop.3picl 0444 root bin
+f usr/share/man/man3picl/picl_get_frutree_parent.3picl 0444 root bin
+s usr/share/man/man3picl/picl_get_next_by_col.3picl=picl_get_next_by_row.3picl
+f usr/share/man/man3picl/picl_get_next_by_row.3picl 0444 root bin
+s usr/share/man/man3picl/picl_get_next_prop.3picl=picl_get_first_prop.3picl
+f usr/share/man/man3picl/picl_get_node_by_path.3picl 0444 root bin
+f usr/share/man/man3picl/picl_get_prop_by_name.3picl 0444 root bin
+f usr/share/man/man3picl/picl_get_propinfo.3picl 0444 root bin
+f usr/share/man/man3picl/picl_get_propinfo_by_name.3picl 0444 root bin
+f usr/share/man/man3picl/picl_get_propval.3picl 0444 root bin
+s usr/share/man/man3picl/picl_get_propval_by_name.3picl=picl_get_propval.3picl
+f usr/share/man/man3picl/picl_get_root.3picl 0444 root bin
+f usr/share/man/man3picl/picl_initialize.3picl 0444 root bin
+f usr/share/man/man3picl/picl_set_propval.3picl 0444 root bin
+s usr/share/man/man3picl/picl_set_propval_by_name.3picl=picl_set_propval.3picl
+f usr/share/man/man3picl/picl_shutdown.3picl 0444 root bin
+f usr/share/man/man3picl/picl_strerror.3picl 0444 root bin
+f usr/share/man/man3picl/picl_wait.3picl 0444 root bin
+f usr/share/man/man3picl/picl_walk_tree_by_class.3picl 0444 root bin
+d usr/share/man/man3picltree 0755 root bin
+f usr/share/man/man3picltree/libpicltree.3picltree 0444 root bin
+f usr/share/man/man3picltree/picld_log.3picltree 0444 root bin
+f usr/share/man/man3picltree/picld_plugin_register.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_add_node.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_add_prop.3picltree 0444 root bin
+s usr/share/man/man3picltree/ptree_add_row_to_table.3picltree=ptree_create_table.3picltree
+f usr/share/man/man3picltree/ptree_create_and_add_node.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_create_and_add_prop.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_create_node.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_create_prop.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_create_table.3picltree 0444 root bin
+s usr/share/man/man3picltree/ptree_delete_node.3picltree=ptree_add_node.3picltree
+s usr/share/man/man3picltree/ptree_delete_prop.3picltree=ptree_add_prop.3picltree
+s usr/share/man/man3picltree/ptree_destroy_node.3picltree=ptree_create_node.3picltree
+s usr/share/man/man3picltree/ptree_destroy_prop.3picltree=ptree_create_prop.3picltree
+f usr/share/man/man3picltree/ptree_find_node.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_get_first_prop.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_get_frutree_parent.3picltree 0444 root bin
+s usr/share/man/man3picltree/ptree_get_next_by_col.3picltree=ptree_get_next_by_row.3picltree
+f usr/share/man/man3picltree/ptree_get_next_by_row.3picltree 0444 root bin
+s usr/share/man/man3picltree/ptree_get_next_prop.3picltree=ptree_get_first_prop.3picltree
+f usr/share/man/man3picltree/ptree_get_node_by_path.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_get_prop_by_name.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_get_propinfo.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_get_propinfo_by_name.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_get_propval.3picltree 0444 root bin
+s usr/share/man/man3picltree/ptree_get_propval_by_name.3picltree=ptree_get_propval.3picltree
+f usr/share/man/man3picltree/ptree_get_root.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_init_propinfo.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_post_event.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_register_handler.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_unregister_handler.3picltree 0444 root bin
+f usr/share/man/man3picltree/ptree_update_propval.3picltree 0444 root bin
+s usr/share/man/man3picltree/ptree_update_propval_by_name.3picltree=ptree_update_propval.3picltree
+f usr/share/man/man3picltree/ptree_walk_tree_by_class.3picltree 0444 root bin
+d usr/share/man/man3pool 0755 root bin
+f usr/share/man/man3pool/pool_associate.3pool 0444 root bin
+f usr/share/man/man3pool/pool_component_info.3pool 0444 root bin
+f usr/share/man/man3pool/pool_component_to_elem.3pool 0444 root bin
+f usr/share/man/man3pool/pool_conf_alloc.3pool 0444 root bin
+s usr/share/man/man3pool/pool_conf_close.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_commit.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_export.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_free.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_info.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_location.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_open.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_remove.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_rollback.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_status.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_to_elem.3pool=pool_component_to_elem.3pool
+s usr/share/man/man3pool/pool_conf_update.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_conf_validate.3pool=pool_conf_alloc.3pool
+s usr/share/man/man3pool/pool_create.3pool=pool_associate.3pool
+s usr/share/man/man3pool/pool_destroy.3pool=pool_associate.3pool
+s usr/share/man/man3pool/pool_dissociate.3pool=pool_associate.3pool
+f usr/share/man/man3pool/pool_dynamic_location.3pool 0444 root bin
+f usr/share/man/man3pool/pool_error.3pool 0444 root bin
+f usr/share/man/man3pool/pool_get_binding.3pool 0444 root bin
+s usr/share/man/man3pool/pool_get_owning_resource.3pool=pool_component_info.3pool
+f usr/share/man/man3pool/pool_get_pool.3pool 0444 root bin
+f usr/share/man/man3pool/pool_get_property.3pool 0444 root bin
+s usr/share/man/man3pool/pool_get_resource.3pool=pool_get_pool.3pool
+s usr/share/man/man3pool/pool_get_resource_binding.3pool=pool_get_binding.3pool
+s usr/share/man/man3pool/pool_get_status.3pool=pool_dynamic_location.3pool
+s usr/share/man/man3pool/pool_info.3pool=pool_associate.3pool
+s usr/share/man/man3pool/pool_put_property.3pool=pool_get_property.3pool
+s usr/share/man/man3pool/pool_query_components.3pool=pool_get_pool.3pool
+s usr/share/man/man3pool/pool_query_pool_resources.3pool=pool_associate.3pool
+s usr/share/man/man3pool/pool_query_pools.3pool=pool_get_pool.3pool
+s usr/share/man/man3pool/pool_query_resource_components.3pool=pool_resource_create.3pool
+s usr/share/man/man3pool/pool_query_resources.3pool=pool_get_pool.3pool
+f usr/share/man/man3pool/pool_resource_create.3pool 0444 root bin
+s usr/share/man/man3pool/pool_resource_destroy.3pool=pool_resource_create.3pool
+s usr/share/man/man3pool/pool_resource_info.3pool=pool_resource_create.3pool
+s usr/share/man/man3pool/pool_resource_to_elem.3pool=pool_component_to_elem.3pool
+s usr/share/man/man3pool/pool_resource_transfer.3pool=pool_resource_create.3pool
+s usr/share/man/man3pool/pool_resource_type_list.3pool=pool_dynamic_location.3pool
+s usr/share/man/man3pool/pool_resource_xtransfer.3pool=pool_resource_create.3pool
+s usr/share/man/man3pool/pool_rm_property.3pool=pool_get_property.3pool
+s usr/share/man/man3pool/pool_set_binding.3pool=pool_get_binding.3pool
+s usr/share/man/man3pool/pool_set_status.3pool=pool_dynamic_location.3pool
+s usr/share/man/man3pool/pool_static_location.3pool=pool_dynamic_location.3pool
+s usr/share/man/man3pool/pool_strerror.3pool=pool_error.3pool
+s usr/share/man/man3pool/pool_to_elem.3pool=pool_component_to_elem.3pool
+f usr/share/man/man3pool/pool_value_alloc.3pool 0444 root bin
+s usr/share/man/man3pool/pool_value_free.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_get_bool.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_get_double.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_get_int64.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_get_name.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_get_string.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_get_type.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_get_uint64.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_set_bool.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_set_double.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_set_int64.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_set_name.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_set_string.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_value_set_uint64.3pool=pool_value_alloc.3pool
+s usr/share/man/man3pool/pool_version.3pool=pool_dynamic_location.3pool
+f usr/share/man/man3pool/pool_walk_components.3pool 0444 root bin
+s usr/share/man/man3pool/pool_walk_pools.3pool=pool_walk_components.3pool
+s usr/share/man/man3pool/pool_walk_properties.3pool=pool_get_property.3pool
+s usr/share/man/man3pool/pool_walk_resources.3pool=pool_walk_components.3pool
+d usr/share/man/man3proc 0755 root bin
+s usr/share/man/man3proc/Lalt_stack.3proc=Plwp_stack.3proc
+s usr/share/man/man3proc/Lclearfault.3proc=Pclearfault.3proc
+s usr/share/man/man3proc/Lclearsig.3proc=Pclearsig.3proc
+f usr/share/man/man3proc/Lctlfd.3proc 0444 root bin
+s usr/share/man/man3proc/Ldstop.3proc=Pstopstatus.3proc
+f usr/share/man/man3proc/Lfree.3proc 0444 root bin
+s usr/share/man/man3proc/Lgetareg.3proc=Pgetareg.3proc
+f usr/share/man/man3proc/Lgrab.3proc 0444 root bin
+f usr/share/man/man3proc/Lgrab_error.3proc 0444 root bin
+s usr/share/man/man3proc/Lmain_stack.3proc=Plwp_stack.3proc
+f usr/share/man/man3proc/Lprochandle.3proc 0444 root bin
+f usr/share/man/man3proc/Lpsinfo.3proc 0444 root bin
+s usr/share/man/man3proc/Lputareg.3proc=Pgetareg.3proc
+s usr/share/man/man3proc/Lsetrun.3proc=Psetrun.3proc
+s usr/share/man/man3proc/Lstack.3proc=Plwp_stack.3proc
+f usr/share/man/man3proc/Lstate.3proc 0444 root bin
+f usr/share/man/man3proc/Lstatus.3proc 0444 root bin
+s usr/share/man/man3proc/Lstop.3proc=Pstopstatus.3proc
+s usr/share/man/man3proc/Lsync.3proc=Psync.3proc
+s usr/share/man/man3proc/Lwait.3proc=Pstopstatus.3proc
+s usr/share/man/man3proc/Lxecbkpt.3proc=Pxecbkpt.3proc
+s usr/share/man/man3proc/Lxecwapt.3proc=Pxecbkpt.3proc
+f usr/share/man/man3proc/Paddr_to_ctf.3proc 0444 root bin
+f usr/share/man/man3proc/Paddr_to_loadobj.3proc 0444 root bin
+f usr/share/man/man3proc/Paddr_to_map.3proc 0444 root bin
+s usr/share/man/man3proc/Paddr_to_text_map.3proc=Paddr_to_map.3proc
+f usr/share/man/man3proc/Pasfd.3proc 0444 root bin
+f usr/share/man/man3proc/Pclearfault.3proc 0444 root bin
+f usr/share/man/man3proc/Pclearsig.3proc 0444 root bin
+f usr/share/man/man3proc/Pcontent.3proc 0444 root bin
+f usr/share/man/man3proc/Pcreate.3proc 0444 root bin
+f usr/share/man/man3proc/Pcreate_agent.3proc 0444 root bin
+s usr/share/man/man3proc/Pcreate_callback.3proc=Pcreate.3proc
+f usr/share/man/man3proc/Pcreate_error.3proc 0444 root bin
+f usr/share/man/man3proc/Pcred.3proc 0444 root bin
+f usr/share/man/man3proc/Pctlfd.3proc 0444 root bin
+f usr/share/man/man3proc/Pdelbkpt.3proc 0444 root bin
+f usr/share/man/man3proc/Pdelwapt.3proc 0444 root bin
+f usr/share/man/man3proc/Pdestroy_agent.3proc 0444 root bin
+s usr/share/man/man3proc/Pdstop.3proc=Pstopstatus.3proc
+f usr/share/man/man3proc/Penv_iter.3proc 0444 root bin
+f usr/share/man/man3proc/Perror_printf.3proc 0444 root bin
+f usr/share/man/man3proc/Pexecname.3proc 0444 root bin
+f usr/share/man/man3proc/Pfault.3proc 0444 root bin
+f usr/share/man/man3proc/Pfdinfo_iter.3proc 0444 root bin
+s usr/share/man/man3proc/Pfgcore.3proc=Pgcore.3proc
+s usr/share/man/man3proc/Pfgrab_core.3proc=Pgrab_core.3proc
+s usr/share/man/man3proc/Pfree.3proc=Prelease.3proc
+f usr/share/man/man3proc/Pgcore.3proc 0444 root bin
+f usr/share/man/man3proc/Pgetareg.3proc 0444 root bin
+f usr/share/man/man3proc/Pgetauxval.3proc 0444 root bin
+f usr/share/man/man3proc/Pgetauxvec.3proc 0444 root bin
+f usr/share/man/man3proc/Pgetenv.3proc 0444 root bin
+f usr/share/man/man3proc/Pgrab.3proc 0444 root bin
+f usr/share/man/man3proc/Pgrab_core.3proc 0444 root bin
+f usr/share/man/man3proc/Pgrab_error.3proc 0444 root bin
+f usr/share/man/man3proc/Pgrab_file.3proc 0444 root bin
+f usr/share/man/man3proc/Pisprocdir.3proc 0444 root bin
+f usr/share/man/man3proc/Pissyscall.3proc 0444 root bin
+s usr/share/man/man3proc/Pissyscall_prev.3proc=Pissyscall.3proc
+f usr/share/man/man3proc/Pldt.3proc 0444 root bin
+f usr/share/man/man3proc/Plmid.3proc 0444 root bin
+s usr/share/man/man3proc/Plmid_to_ctf.3proc=Paddr_to_ctf.3proc
+s usr/share/man/man3proc/Plmid_to_loadobj.3proc=Paddr_to_loadobj.3proc
+s usr/share/man/man3proc/Plmid_to_map.3proc=Paddr_to_map.3proc
+f usr/share/man/man3proc/Plookup_by_addr.3proc 0444 root bin
+s usr/share/man/man3proc/Plookup_by_name.3proc=Plookup_by_addr.3proc
+s usr/share/man/man3proc/Plwp_alt_stack.3proc=Plwp_stack.3proc
+f usr/share/man/man3proc/Plwp_getasrs.3proc 0444 root bin
+s usr/share/man/man3proc/Plwp_getfpregs.3proc=Plwp_getregs.3proc
+f usr/share/man/man3proc/Plwp_getgwindows.3proc 0444 root bin
+f usr/share/man/man3proc/Plwp_getname.3proc 0444 root bin
+f usr/share/man/man3proc/Plwp_getpsinfo.3proc 0444 root bin
+f usr/share/man/man3proc/Plwp_getregs.3proc 0444 root bin
+f usr/share/man/man3proc/Plwp_getspymaster.3proc 0444 root bin
+f usr/share/man/man3proc/Plwp_getxregs.3proc 0444 root bin
+f usr/share/man/man3proc/Plwp_iter.3proc 0444 root bin
+s usr/share/man/man3proc/Plwp_iter_all.3proc=Plwp_iter.3proc
+s usr/share/man/man3proc/Plwp_main_stack.3proc=Plwp_stack.3proc
+s usr/share/man/man3proc/Plwp_setasrs.3proc=Plwp_getasrs.3proc
+s usr/share/man/man3proc/Plwp_setfpregs.3proc=Plwp_getregs.3proc
+s usr/share/man/man3proc/Plwp_setregs.3proc=Plwp_getregs.3proc
+s usr/share/man/man3proc/Plwp_setxregs.3proc=Plwp_getxregs.3proc
+f usr/share/man/man3proc/Plwp_stack.3proc 0444 root bin
+f usr/share/man/man3proc/Pmapping_iter.3proc 0444 root bin
+s usr/share/man/man3proc/Pmapping_iter_resolved.3proc=Pmapping_iter.3proc
+s usr/share/man/man3proc/Pname_to_ctf.3proc=Paddr_to_ctf.3proc
+s usr/share/man/man3proc/Pname_to_loadobj.3proc=Paddr_to_loadobj.3proc
+s usr/share/man/man3proc/Pname_to_map.3proc=Paddr_to_map.3proc
+s usr/share/man/man3proc/Pobject_iter.3proc=Pmapping_iter.3proc
+s usr/share/man/man3proc/Pobject_iter_resolved.3proc=Pmapping_iter.3proc
+f usr/share/man/man3proc/Pobjname.3proc 0444 root bin
+s usr/share/man/man3proc/Pobjname_resolved.3proc=Pobjname.3proc
+f usr/share/man/man3proc/Pplatform.3proc 0444 root bin
+f usr/share/man/man3proc/Ppltdest.3proc 0444 root bin
+f usr/share/man/man3proc/Ppriv.3proc 0444 root bin
+s usr/share/man/man3proc/Ppriv_free.3proc=Ppriv.3proc
+f usr/share/man/man3proc/Ppsinfo.3proc 0444 root bin
+s usr/share/man/man3proc/Pputareg.3proc=Pgetareg.3proc
+f usr/share/man/man3proc/Prd_agent.3proc 0444 root bin
+f usr/share/man/man3proc/Pread.3proc 0444 root bin
+s usr/share/man/man3proc/Pread_string.3proc=Pread.3proc
+f usr/share/man/man3proc/Prelease.3proc 0444 root bin
+f usr/share/man/man3proc/Preopen.3proc 0444 root bin
+f usr/share/man/man3proc/Preset_maps.3proc 0444 root bin
+f usr/share/man/man3proc/Psecflags.3proc 0444 root bin
+s usr/share/man/man3proc/Psecflags_free.3proc=Psecflags.3proc
+f usr/share/man/man3proc/Psetbkpt.3proc 0444 root bin
+f usr/share/man/man3proc/Psetcred.3proc 0444 root bin
+f usr/share/man/man3proc/Psetfault.3proc 0444 root bin
+f usr/share/man/man3proc/Psetflags.3proc 0444 root bin
+f usr/share/man/man3proc/Psetpriv.3proc 0444 root bin
+f usr/share/man/man3proc/Psetrun.3proc 0444 root bin
+f usr/share/man/man3proc/Psetsignal.3proc 0444 root bin
+f usr/share/man/man3proc/Psetsysentry.3proc 0444 root bin
+s usr/share/man/man3proc/Psetsysexit.3proc=Psetsysentry.3proc
+f usr/share/man/man3proc/Psetwapt.3proc 0444 root bin
+f usr/share/man/man3proc/Psetzoneid.3proc 0444 root bin
+f usr/share/man/man3proc/Psignal.3proc 0444 root bin
+f usr/share/man/man3proc/Pstack_iter.3proc 0444 root bin
+f usr/share/man/man3proc/Pstate.3proc 0444 root bin
+f usr/share/man/man3proc/Pstatus.3proc 0444 root bin
+s usr/share/man/man3proc/Pstop.3proc=Pstopstatus.3proc
+f usr/share/man/man3proc/Pstopstatus.3proc 0444 root bin
+f usr/share/man/man3proc/Psymbol_iter.3proc 0444 root bin
+s usr/share/man/man3proc/Psymbol_iter_by_addr.3proc=Psymbol_iter.3proc
+s usr/share/man/man3proc/Psymbol_iter_by_lmid.3proc=Psymbol_iter.3proc
+s usr/share/man/man3proc/Psymbol_iter_by_name.3proc=Psymbol_iter.3proc
+f usr/share/man/man3proc/Psync.3proc 0444 root bin
+f usr/share/man/man3proc/Psysentry.3proc 0444 root bin
+s usr/share/man/man3proc/Psysexit.3proc=Psysentry.3proc
+f usr/share/man/man3proc/Puname.3proc 0444 root bin
+s usr/share/man/man3proc/Punsetflags.3proc=Psetflags.3proc
+f usr/share/man/man3proc/Pupanic.3proc 0444 root bin
+s usr/share/man/man3proc/Pupanic_free.3proc=Pupanic.3proc
+f usr/share/man/man3proc/Pupdate_maps.3proc 0444 root bin
+f usr/share/man/man3proc/Pupdate_syms.3proc 0444 root bin
+s usr/share/man/man3proc/Pwait.3proc=Pstopstatus.3proc
+f usr/share/man/man3proc/Pwrite.3proc 0444 root bin
+s usr/share/man/man3proc/Pxcreate.3proc=Pcreate.3proc
+f usr/share/man/man3proc/Pxecbkpt.3proc 0444 root bin
+s usr/share/man/man3proc/Pxecwapt.3proc=Pxecbkpt.3proc
+s usr/share/man/man3proc/Pxlookup_by_addr.3proc=Plookup_by_addr.3proc
+s usr/share/man/man3proc/Pxlookup_by_addr_resolved.3proc=Plookup_by_addr.3proc
+s usr/share/man/man3proc/Pxlookup_by_name.3proc=Plookup_by_addr.3proc
+s usr/share/man/man3proc/Pxsymbol_iter.3proc=Psymbol_iter.3proc
+f usr/share/man/man3proc/Pzonename.3proc 0444 root bin
+s usr/share/man/man3proc/Pzonepath.3proc=Pzonename.3proc
+s usr/share/man/man3proc/Pzoneroot.3proc=Pzonename.3proc
+f usr/share/man/man3proc/pr_access.3proc 0444 root bin
+f usr/share/man/man3proc/pr_close.3proc 0444 root bin
+f usr/share/man/man3proc/pr_creat.3proc 0444 root bin
+f usr/share/man/man3proc/pr_door_info.3proc 0444 root bin
+f usr/share/man/man3proc/pr_exit.3proc 0444 root bin
+f usr/share/man/man3proc/pr_fcntl.3proc 0444 root bin
+s usr/share/man/man3proc/pr_fstat.3proc=pr_stat.3proc
+s usr/share/man/man3proc/pr_fstat64.3proc=pr_stat.3proc
+f usr/share/man/man3proc/pr_fstatvfs.3proc 0444 root bin
+f usr/share/man/man3proc/pr_getitimer.3proc 0444 root bin
+f usr/share/man/man3proc/pr_getpeername.3proc 0444 root bin
+f usr/share/man/man3proc/pr_getpeerucred.3proc 0444 root bin
+f usr/share/man/man3proc/pr_getprojid.3proc 0444 root bin
+f usr/share/man/man3proc/pr_getrctl.3proc 0444 root bin
+f usr/share/man/man3proc/pr_getrlimit.3proc 0444 root bin
+s usr/share/man/man3proc/pr_getrlimit64.3proc=pr_getrlimit.3proc
+f usr/share/man/man3proc/pr_getsockname.3proc 0444 root bin
+f usr/share/man/man3proc/pr_getsockopt.3proc 0444 root bin
+f usr/share/man/man3proc/pr_gettaskid.3proc 0444 root bin
+f usr/share/man/man3proc/pr_getzoneid.3proc 0444 root bin
+f usr/share/man/man3proc/pr_ioctl.3proc 0444 root bin
+f usr/share/man/man3proc/pr_link.3proc 0444 root bin
+f usr/share/man/man3proc/pr_llseek.3proc 0444 root bin
+f usr/share/man/man3proc/pr_lseek.3proc 0444 root bin
+s usr/share/man/man3proc/pr_lstat.3proc=pr_stat.3proc
+s usr/share/man/man3proc/pr_lstat64.3proc=pr_stat.3proc
+f usr/share/man/man3proc/pr_memcntl.3proc 0444 root bin
+f usr/share/man/man3proc/pr_meminfo.3proc 0444 root bin
+f usr/share/man/man3proc/pr_mmap.3proc 0444 root bin
+f usr/share/man/man3proc/pr_munmap.3proc 0444 root bin
+f usr/share/man/man3proc/pr_open.3proc 0444 root bin
+f usr/share/man/man3proc/pr_processor_bind.3proc 0444 root bin
+f usr/share/man/man3proc/pr_rename.3proc 0444 root bin
+f usr/share/man/man3proc/pr_setitimer.3proc 0444 root bin
+f usr/share/man/man3proc/pr_setrctl.3proc 0444 root bin
+f usr/share/man/man3proc/pr_setrlimit.3proc 0444 root bin
+s usr/share/man/man3proc/pr_setrlimit64.3proc=pr_setrlimit.3proc
+f usr/share/man/man3proc/pr_settaskid.3proc 0444 root bin
+f usr/share/man/man3proc/pr_sigaction.3proc 0444 root bin
+f usr/share/man/man3proc/pr_stat.3proc 0444 root bin
+s usr/share/man/man3proc/pr_stat64.3proc=pr_stat.3proc
+f usr/share/man/man3proc/pr_statvfs.3proc 0444 root bin
+f usr/share/man/man3proc/pr_unlink.3proc 0444 root bin
+f usr/share/man/man3proc/pr_waitid.3proc 0444 root bin
+f usr/share/man/man3proc/proc_arg_grab.3proc 0444 root bin
+f usr/share/man/man3proc/proc_arg_psinfo.3proc 0444 root bin
+s usr/share/man/man3proc/proc_arg_xgrab.3proc=proc_arg_grab.3proc
+s usr/share/man/man3proc/proc_arg_xpsinfo.3proc=proc_arg_psinfo.3proc
+f usr/share/man/man3proc/proc_content2str.3proc 0444 root bin
+s usr/share/man/man3proc/proc_dmodelname.3proc=proc_fltname.3proc
+f usr/share/man/man3proc/proc_fdinfo_free.3proc 0444 root bin
+f usr/share/man/man3proc/proc_fdinfo_misc.3proc 0444 root bin
+f usr/share/man/man3proc/proc_fdinfowalk.3proc 0444 root bin
+f usr/share/man/man3proc/proc_fdwalk.3proc 0444 root bin
+f usr/share/man/man3proc/proc_get_fdinfo.3proc 0444 root bin
+s usr/share/man/man3proc/proc_finistdio.3proc=proc_initstdio.3proc
+f usr/share/man/man3proc/proc_fltname.3proc 0444 root bin
+f usr/share/man/man3proc/proc_fltset2str.3proc 0444 root bin
+s usr/share/man/man3proc/proc_flushstdio.3proc=proc_initstdio.3proc
+s usr/share/man/man3proc/proc_free_priv.3proc=proc_get_priv.3proc
+f usr/share/man/man3proc/proc_get_auxv.3proc 0444 root bin
+f usr/share/man/man3proc/proc_get_cred.3proc 0444 root bin
+s usr/share/man/man3proc/proc_get_ldt.3proc=Pldt.3proc
+f usr/share/man/man3proc/proc_get_lwpsinfo.3proc 0444 root bin
+f usr/share/man/man3proc/proc_get_priv.3proc 0444 root bin
+f usr/share/man/man3proc/proc_get_psinfo.3proc 0444 root bin
+f usr/share/man/man3proc/proc_get_status.3proc 0444 root bin
+f usr/share/man/man3proc/proc_initstdio.3proc 0444 root bin
+f usr/share/man/man3proc/proc_lwp_in_set.3proc 0444 root bin
+s usr/share/man/man3proc/proc_lwp_range_valid.3proc=proc_lwp_in_set.3proc
+f usr/share/man/man3proc/proc_service.3proc 0444 root bin
+s usr/share/man/man3proc/proc_signame.3proc=proc_fltname.3proc
+s usr/share/man/man3proc/proc_sigset2str.3proc=proc_fltset2str.3proc
+s usr/share/man/man3proc/proc_str2content.3proc=proc_content2str.3proc
+f usr/share/man/man3proc/proc_str2flt.3proc 0444 root bin
+f usr/share/man/man3proc/proc_str2fltset.3proc 0444 root bin
+s usr/share/man/man3proc/proc_str2sig.3proc=proc_str2flt.3proc
+s usr/share/man/man3proc/proc_str2sigset.3proc=proc_str2fltset.3proc
+s usr/share/man/man3proc/proc_str2sys.3proc=proc_str2flt.3proc
+s usr/share/man/man3proc/proc_str2sysset.3proc=proc_str2fltset.3proc
+s usr/share/man/man3proc/proc_sysname.3proc=proc_fltname.3proc
+s usr/share/man/man3proc/proc_sysset2str.3proc=proc_fltset2str.3proc
+f usr/share/man/man3proc/proc_unctrl_psinfo.3proc 0444 root bin
+f usr/share/man/man3proc/proc_walk.3proc 0444 root bin
+s usr/share/man/man3proc/ps_kill.3proc=ps_pstop.3proc
+s usr/share/man/man3proc/ps_lcontinue.3proc=ps_pstop.3proc
+s usr/share/man/man3proc/ps_lgetfpregs.3proc=ps_lgetregs.3proc
+f usr/share/man/man3proc/ps_lgetregs.3proc 0444 root bin
+s usr/share/man/man3proc/ps_lgetxregs.3proc=ps_lgetregs.3proc
+s usr/share/man/man3proc/ps_lgetxregsize.3proc=ps_lgetregs.3proc
+s usr/share/man/man3proc/ps_lrolltoaddr.3proc=ps_pstop.3proc
+s usr/share/man/man3proc/ps_lsetfpregs.3proc=ps_lgetregs.3proc
+s usr/share/man/man3proc/ps_lsetregs.3proc=ps_lgetregs.3proc
+s usr/share/man/man3proc/ps_lsetxregs.3proc=ps_lgetregs.3proc
+s usr/share/man/man3proc/ps_lstop.3proc=ps_pstop.3proc
+s usr/share/man/man3proc/ps_pcontinue.3proc=ps_pstop.3proc
+s usr/share/man/man3proc/ps_pdread.3proc=ps_pread.3proc
+s usr/share/man/man3proc/ps_pdwrite.3proc=ps_pread.3proc
+f usr/share/man/man3proc/ps_pglobal_lookup.3proc 0444 root bin
+s usr/share/man/man3proc/ps_pglobal_sym.3proc=ps_pglobal_lookup.3proc
+f usr/share/man/man3proc/ps_pread.3proc 0444 root bin
+f usr/share/man/man3proc/ps_pstop.3proc 0444 root bin
+s usr/share/man/man3proc/ps_ptread.3proc=ps_pread.3proc
+s usr/share/man/man3proc/ps_ptwrite.3proc=ps_pread.3proc
+s usr/share/man/man3proc/ps_pwrite.3proc=ps_pread.3proc
+d usr/share/man/man3project 0755 root bin
+s usr/share/man/man3project/endprojent.3project=getprojent.3project
+s usr/share/man/man3project/fgetprojent.3project=getprojent.3project
+s usr/share/man/man3project/getdefaultproj.3project=getprojent.3project
+s usr/share/man/man3project/getprojbyid.3project=getprojent.3project
+s usr/share/man/man3project/getprojbyname.3project=getprojent.3project
+f usr/share/man/man3project/getprojent.3project 0444 root bin
+s usr/share/man/man3project/getprojidbyname.3project=getprojent.3project
+s usr/share/man/man3project/inproj.3project=getprojent.3project
+f usr/share/man/man3project/project_walk.3project 0444 root bin
+f usr/share/man/man3project/setproject.3project 0444 root bin
+s usr/share/man/man3project/setprojent.3project=getprojent.3project
+d usr/share/man/man3resolv 0755 root bin
+s usr/share/man/man3resolv/dn_comp.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/dn_expand.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/fp_resstat.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/herror.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/hstrerror.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_getservers.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_hostalias.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_init.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_mkquery.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_nclose.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_ndestroy.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_ninit.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_nmkquery.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_nquery.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_nquerydomain.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_nsearch.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_nsend.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_nsendsigned.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_query.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_search.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_send.3resolv=resolver.3resolv
+s usr/share/man/man3resolv/res_setservers.3resolv=resolver.3resolv
+f usr/share/man/man3resolv/resolver.3resolv 0444 root bin
+d usr/share/man/man3rpc 0755 root bin
+s usr/share/man/man3rpc/havedisk.3rpc=rstat.3rpc
+s usr/share/man/man3rpc/rnusers.3rpc=rusers.3rpc
+f usr/share/man/man3rpc/rstat.3rpc 0444 root bin
+f usr/share/man/man3rpc/rusers.3rpc 0444 root bin
+f usr/share/man/man3rpc/rwall.3rpc 0444 root bin
+d usr/share/man/man3rsm 0755 root bin
+f usr/share/man/man3rsm/rsm_create_localmemory_handle.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_get_controller.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_get_interconnect_topology.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_get_segmentid_range.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_intr_signal_post.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_intr_signal_wait_pollfd.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_export_create.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_export_publish.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_get_pollfd.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_import_connect.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_import_get.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_import_init_barrier.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_import_map.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_import_open_barrier.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_import_put.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_import_putv.3rsm 0444 root bin
+f usr/share/man/man3rsm/rsm_memseg_import_set_mode.3rsm 0444 root bin
+d usr/share/man/man3sasl 0755 root bin
+s usr/share/man/man3sasl/prop_clear.3sasl=sasl_auxprop.3sasl
+s usr/share/man/man3sasl/prop_dispose.3sasl=sasl_auxprop.3sasl
+s usr/share/man/man3sasl/prop_dup.3sasl=sasl_auxprop.3sasl
+s usr/share/man/man3sasl/prop_erase.3sasl=sasl_auxprop.3sasl
+s usr/share/man/man3sasl/prop_format.3sasl=sasl_auxprop.3sasl
+s usr/share/man/man3sasl/prop_get.3sasl=sasl_auxprop.3sasl
+s usr/share/man/man3sasl/prop_getnames.3sasl=sasl_auxprop.3sasl
+s usr/share/man/man3sasl/prop_new.3sasl=sasl_auxprop.3sasl
+s usr/share/man/man3sasl/prop_request.3sasl=sasl_auxprop.3sasl
+s usr/share/man/man3sasl/prop_set.3sasl=sasl_auxprop.3sasl
+s usr/share/man/man3sasl/prop_setvals.3sasl=sasl_auxprop.3sasl
+f usr/share/man/man3sasl/sasl_authorize_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_auxprop.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_auxprop_add_plugin.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_auxprop_getctx.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_auxprop_request.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_canon_user_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_canonuser_add_plugin.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_chalprompt_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_checkapop.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_checkpass.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_client_add_plugin.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_client_init.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_client_new.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_client_plug_init_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_client_start.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_client_step.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_decode.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_decode64.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_dispose.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_done.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_encode.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_encode64.3sasl 0444 root bin
+s usr/share/man/man3sasl/sasl_encodev.3sasl=sasl_encode.3sasl
+f usr/share/man/man3sasl/sasl_erasebuffer.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_errdetail.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_errors.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_errstring.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_getcallback_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_getopt_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_getpath_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_getprop.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_getrealm_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_getsecret_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_getsimple_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_global_listmech.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_idle.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_listmech.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_log_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_server_add_plugin.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_server_init.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_server_new.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_server_plug_init_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_server_start.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_server_step.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_server_userdb_checkpass_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_server_userdb_setpass_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_set_alloc.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_set_mutex.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_seterror.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_setpass.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_setprop.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_utf8verify.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_verifyfile_t.3sasl 0444 root bin
+f usr/share/man/man3sasl/sasl_version.3sasl 0444 root bin
+d usr/share/man/man3scf 0755 root bin
+s usr/share/man/man3scf/scf_count_ranges_destroy.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_entry_add_value.3scf=scf_entry_create.3scf
+f usr/share/man/man3scf/scf_entry_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_entry_destroy.3scf=scf_entry_create.3scf
+s usr/share/man/man3scf/scf_entry_destroy_children.3scf=scf_entry_create.3scf
+s usr/share/man/man3scf/scf_entry_handle.3scf=scf_entry_create.3scf
+s usr/share/man/man3scf/scf_entry_reset.3scf=scf_entry_create.3scf
+f usr/share/man/man3scf/scf_error.3scf 0444 root bin
+s usr/share/man/man3scf/scf_handle_bind.3scf=scf_handle_create.3scf
+f usr/share/man/man3scf/scf_handle_create.3scf 0444 root bin
+f usr/share/man/man3scf/scf_handle_decode_fmri.3scf 0444 root bin
+s usr/share/man/man3scf/scf_handle_decorate.3scf=scf_handle_create.3scf
+s usr/share/man/man3scf/scf_handle_destroy.3scf=scf_handle_create.3scf
+s usr/share/man/man3scf/scf_handle_get_scope.3scf=scf_scope_create.3scf
+s usr/share/man/man3scf/scf_handle_unbind.3scf=scf_handle_create.3scf
+s usr/share/man/man3scf/scf_instance_add_pg.3scf=scf_pg_create.3scf
+f usr/share/man/man3scf/scf_instance_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_instance_delete.3scf=scf_instance_create.3scf
+s usr/share/man/man3scf/scf_instance_destroy.3scf=scf_instance_create.3scf
+s usr/share/man/man3scf/scf_instance_get_name.3scf=scf_instance_create.3scf
+s usr/share/man/man3scf/scf_instance_get_parent.3scf=scf_instance_create.3scf
+s usr/share/man/man3scf/scf_instance_get_pg.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_instance_get_pg_composed.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_instance_get_snapshot.3scf=scf_snapshot_create.3scf
+s usr/share/man/man3scf/scf_instance_handle.3scf=scf_instance_create.3scf
+s usr/share/man/man3scf/scf_instance_to_fmri.3scf=scf_handle_decode_fmri.3scf
+s usr/share/man/man3scf/scf_int_ranges_destroy.3scf=scf_tmpl_prop_name.3scf
+f usr/share/man/man3scf/scf_iter_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_iter_destroy.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_handle.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_handle_scopes.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_instance_pgs.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_instance_pgs_composed.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_instance_pgs_typed.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_instance_pgs_typed_composed.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_instance_snapshots.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_next_instance.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_next_pg.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_next_property.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_next_scope.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_next_service.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_next_snapshot.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_next_value.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_pg_properties.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_property_values.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_reset.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_scope_services.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_service_instances.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_service_pgs.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_service_pgs_typed.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_snaplevel_pgs.3scf=scf_iter_create.3scf
+s usr/share/man/man3scf/scf_iter_snaplevel_pgs_typed.3scf=scf_iter_create.3scf
+f usr/share/man/man3scf/scf_limit.3scf 0444 root bin
+s usr/share/man/man3scf/scf_myname.3scf=scf_handle_create.3scf
+f usr/share/man/man3scf/scf_pg_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_pg_delete.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_pg_destroy.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_pg_get_flags.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_pg_get_name.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_pg_get_parent_instance.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_pg_get_parent_service.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_pg_get_parent_snaplevel.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_pg_get_property.3scf=scf_property_create.3scf
+s usr/share/man/man3scf/scf_pg_get_type.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_pg_get_underlying_pg.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_pg_handle.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_pg_to_fmri.3scf=scf_handle_decode_fmri.3scf
+s usr/share/man/man3scf/scf_pg_update.3scf=scf_pg_create.3scf
+f usr/share/man/man3scf/scf_property_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_property_destroy.3scf=scf_property_create.3scf
+s usr/share/man/man3scf/scf_property_get_name.3scf=scf_property_create.3scf
+s usr/share/man/man3scf/scf_property_get_value.3scf=scf_property_create.3scf
+s usr/share/man/man3scf/scf_property_handle.3scf=scf_property_create.3scf
+s usr/share/man/man3scf/scf_property_is_type.3scf=scf_property_create.3scf
+s usr/share/man/man3scf/scf_property_to_fmri.3scf=scf_handle_decode_fmri.3scf
+s usr/share/man/man3scf/scf_property_type.3scf=scf_property_create.3scf
+s usr/share/man/man3scf/scf_scope_add_service.3scf=scf_service_create.3scf
+f usr/share/man/man3scf/scf_scope_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_scope_destroy.3scf=scf_scope_create.3scf
+s usr/share/man/man3scf/scf_scope_get_name.3scf=scf_scope_create.3scf
+s usr/share/man/man3scf/scf_scope_get_service.3scf=scf_service_create.3scf
+s usr/share/man/man3scf/scf_scope_handle.3scf=scf_scope_create.3scf
+s usr/share/man/man3scf/scf_scope_to_fmri.3scf=scf_handle_decode_fmri.3scf
+s usr/share/man/man3scf/scf_service_add_instance.3scf=scf_instance_create.3scf
+s usr/share/man/man3scf/scf_service_add_pg.3scf=scf_pg_create.3scf
+f usr/share/man/man3scf/scf_service_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_service_delete.3scf=scf_service_create.3scf
+s usr/share/man/man3scf/scf_service_destroy.3scf=scf_service_create.3scf
+s usr/share/man/man3scf/scf_service_get_instance.3scf=scf_instance_create.3scf
+s usr/share/man/man3scf/scf_service_get_name.3scf=scf_service_create.3scf
+s usr/share/man/man3scf/scf_service_get_parent.3scf=scf_service_create.3scf
+s usr/share/man/man3scf/scf_service_get_pg.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_service_handle.3scf=scf_service_create.3scf
+s usr/share/man/man3scf/scf_service_to_fmri.3scf=scf_handle_decode_fmri.3scf
+s usr/share/man/man3scf/scf_simple_app_props_free.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_app_props_get.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_app_props_next.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_app_props_search.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_free.3scf=scf_simple_prop_get.3scf
+f usr/share/man/man3scf/scf_simple_prop_get.3scf 0444 root bin
+s usr/share/man/man3scf/scf_simple_prop_name.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_next_astring.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_next_boolean.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_next_count.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_next_integer.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_next_opaque.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_next_reset.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_next_time.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_next_ustring.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_numvalues.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_pgname.3scf=scf_simple_prop_get.3scf
+s usr/share/man/man3scf/scf_simple_prop_type.3scf=scf_simple_prop_get.3scf
+f usr/share/man/man3scf/scf_simple_walk_instances.3scf 0444 root bin
+f usr/share/man/man3scf/scf_snaplevel_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_snaplevel_destroy.3scf=scf_snaplevel_create.3scf
+s usr/share/man/man3scf/scf_snaplevel_get_instance_name.3scf=scf_snaplevel_create.3scf
+s usr/share/man/man3scf/scf_snaplevel_get_next_snaplevel.3scf=scf_snaplevel_create.3scf
+s usr/share/man/man3scf/scf_snaplevel_get_parent.3scf=scf_snaplevel_create.3scf
+s usr/share/man/man3scf/scf_snaplevel_get_pg.3scf=scf_pg_create.3scf
+s usr/share/man/man3scf/scf_snaplevel_get_scope_name.3scf=scf_snaplevel_create.3scf
+s usr/share/man/man3scf/scf_snaplevel_get_service_name.3scf=scf_snaplevel_create.3scf
+s usr/share/man/man3scf/scf_snaplevel_handle.3scf=scf_snaplevel_create.3scf
+f usr/share/man/man3scf/scf_snapshot_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_snapshot_destroy.3scf=scf_snapshot_create.3scf
+s usr/share/man/man3scf/scf_snapshot_get_base_snaplevel.3scf=scf_snaplevel_create.3scf
+s usr/share/man/man3scf/scf_snapshot_get_name.3scf=scf_snapshot_create.3scf
+s usr/share/man/man3scf/scf_snapshot_get_parent.3scf=scf_snapshot_create.3scf
+s usr/share/man/man3scf/scf_snapshot_handle.3scf=scf_snapshot_create.3scf
+s usr/share/man/man3scf/scf_snapshot_update.3scf=scf_snapshot_create.3scf
+s usr/share/man/man3scf/scf_strerror.3scf=scf_error.3scf
+s usr/share/man/man3scf/scf_string_to_type.3scf=scf_property_create.3scf
+s usr/share/man/man3scf/scf_tmpl_error_pg.3scf=scf_tmpl_validate_fmri.3scf
+s usr/share/man/man3scf/scf_tmpl_error_pg_tmpl.3scf=scf_tmpl_validate_fmri.3scf
+s usr/share/man/man3scf/scf_tmpl_error_prop.3scf=scf_tmpl_validate_fmri.3scf
+s usr/share/man/man3scf/scf_tmpl_error_prop_tmpl.3scf=scf_tmpl_validate_fmri.3scf
+s usr/share/man/man3scf/scf_tmpl_error_source_fmri.3scf=scf_tmpl_validate_fmri.3scf
+s usr/share/man/man3scf/scf_tmpl_error_type.3scf=scf_tmpl_validate_fmri.3scf
+s usr/share/man/man3scf/scf_tmpl_error_value.3scf=scf_tmpl_validate_fmri.3scf
+s usr/share/man/man3scf/scf_tmpl_errors_destroy.3scf=scf_tmpl_validate_fmri.3scf
+s usr/share/man/man3scf/scf_tmpl_get_by_pg.3scf=scf_tmpl_pg_create.3scf
+s usr/share/man/man3scf/scf_tmpl_get_by_pg_name.3scf=scf_tmpl_pg_create.3scf
+s usr/share/man/man3scf/scf_tmpl_get_by_prop.3scf=scf_tmpl_prop_create.3scf
+s usr/share/man/man3scf/scf_tmpl_iter_pgs.3scf=scf_tmpl_pg_create.3scf
+s usr/share/man/man3scf/scf_tmpl_iter_props.3scf=scf_tmpl_prop_create.3scf
+s usr/share/man/man3scf/scf_tmpl_next_error.3scf=scf_tmpl_validate_fmri.3scf
+s usr/share/man/man3scf/scf_tmpl_pg_common_name.3scf=scf_tmpl_pg_name.3scf
+f usr/share/man/man3scf/scf_tmpl_pg_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_tmpl_pg_description.3scf=scf_tmpl_pg_name.3scf
+s usr/share/man/man3scf/scf_tmpl_pg_destroy.3scf=scf_tmpl_pg_create.3scf
+f usr/share/man/man3scf/scf_tmpl_pg_name.3scf 0444 root bin
+s usr/share/man/man3scf/scf_tmpl_pg_required.3scf=scf_tmpl_pg_name.3scf
+s usr/share/man/man3scf/scf_tmpl_pg_reset.3scf=scf_tmpl_pg_create.3scf
+s usr/share/man/man3scf/scf_tmpl_pg_target.3scf=scf_tmpl_pg_name.3scf
+s usr/share/man/man3scf/scf_tmpl_pg_type.3scf=scf_tmpl_pg_name.3scf
+s usr/share/man/man3scf/scf_tmpl_prop_cardinality.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_prop_common_name.3scf=scf_tmpl_prop_name.3scf
+f usr/share/man/man3scf/scf_tmpl_prop_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_tmpl_prop_description.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_prop_destroy.3scf=scf_tmpl_prop_create.3scf
+s usr/share/man/man3scf/scf_tmpl_prop_internal_seps.3scf=scf_tmpl_prop_name.3scf
+f usr/share/man/man3scf/scf_tmpl_prop_name.3scf 0444 root bin
+s usr/share/man/man3scf/scf_tmpl_prop_required.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_prop_reset.3scf=scf_tmpl_prop_create.3scf
+s usr/share/man/man3scf/scf_tmpl_prop_type.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_prop_units.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_prop_visibility.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_reset_errors.3scf=scf_tmpl_validate_fmri.3scf
+s usr/share/man/man3scf/scf_tmpl_strerror.3scf=scf_tmpl_validate_fmri.3scf
+f usr/share/man/man3scf/scf_tmpl_validate_fmri.3scf 0444 root bin
+s usr/share/man/man3scf/scf_tmpl_value_common_name.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_value_count_range_choices.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_value_count_range_constraints.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_value_description.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_value_in_constraint.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_value_int_range_choices.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_value_int_range_constraints.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_value_name_choices.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_value_name_constraints.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_tmpl_visibility_to_string.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/scf_transaction_commit.3scf=scf_transaction_create.3scf
+f usr/share/man/man3scf/scf_transaction_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_transaction_destroy.3scf=scf_transaction_create.3scf
+s usr/share/man/man3scf/scf_transaction_destroy_children.3scf=scf_transaction_create.3scf
+s usr/share/man/man3scf/scf_transaction_handle.3scf=scf_transaction_create.3scf
+s usr/share/man/man3scf/scf_transaction_property_change.3scf=scf_transaction_create.3scf
+s usr/share/man/man3scf/scf_transaction_property_change_type.3scf=scf_transaction_create.3scf
+s usr/share/man/man3scf/scf_transaction_property_delete.3scf=scf_transaction_create.3scf
+s usr/share/man/man3scf/scf_transaction_property_new.3scf=scf_transaction_create.3scf
+s usr/share/man/man3scf/scf_transaction_reset.3scf=scf_transaction_create.3scf
+s usr/share/man/man3scf/scf_transaction_reset_all.3scf=scf_transaction_create.3scf
+s usr/share/man/man3scf/scf_transaction_start.3scf=scf_transaction_create.3scf
+s usr/share/man/man3scf/scf_type_base_type.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_type_to_string.3scf=scf_property_create.3scf
+s usr/share/man/man3scf/scf_value_base_type.3scf=scf_value_create.3scf
+f usr/share/man/man3scf/scf_value_create.3scf 0444 root bin
+s usr/share/man/man3scf/scf_value_destroy.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_get_as_string.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_get_as_string_typed.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_get_astring.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_get_boolean.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_get_count.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_get_integer.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_get_opaque.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_get_time.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_get_ustring.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_handle.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_is_type.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_reset.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_set_astring.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_set_boolean.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_set_count.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_set_from_string.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_set_integer.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_set_opaque.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_set_time.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_set_ustring.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_value_type.3scf=scf_value_create.3scf
+s usr/share/man/man3scf/scf_values_destroy.3scf=scf_tmpl_prop_name.3scf
+s usr/share/man/man3scf/smf_degrade_instance.3scf=smf_enable_instance.3scf
+s usr/share/man/man3scf/smf_disable_instance.3scf=smf_enable_instance.3scf
+s usr/share/man/man3scf/smf_disable_instance_with_comment.3scf=smf_enable_instance.3scf
+f usr/share/man/man3scf/smf_enable_instance.3scf 0444 root bin
+s usr/share/man/man3scf/smf_get_state.3scf=smf_enable_instance.3scf
+s usr/share/man/man3scf/smf_maintain_instance.3scf=smf_enable_instance.3scf
+s usr/share/man/man3scf/smf_refresh_instance.3scf=smf_enable_instance.3scf
+s usr/share/man/man3scf/smf_restart_instance.3scf=smf_enable_instance.3scf
+s usr/share/man/man3scf/smf_restore_instance.3scf=smf_enable_instance.3scf
+d usr/share/man/man3sec 0755 root bin
+f usr/share/man/man3sec/acl_check.3sec 0444 root bin
+f usr/share/man/man3sec/acl_free.3sec 0444 root bin
+s usr/share/man/man3sec/acl_fromtext.3sec=acl_totext.3sec
+f usr/share/man/man3sec/acl_get.3sec 0444 root bin
+s usr/share/man/man3sec/acl_set.3sec=acl_get.3sec
+f usr/share/man/man3sec/acl_strip.3sec 0444 root bin
+f usr/share/man/man3sec/acl_totext.3sec 0444 root bin
+f usr/share/man/man3sec/acl_trivial.3sec 0444 root bin
+f usr/share/man/man3sec/aclcheck.3sec 0444 root bin
+s usr/share/man/man3sec/aclfrommode.3sec=acltomode.3sec
+s usr/share/man/man3sec/aclfromtext.3sec=acltotext.3sec
+f usr/share/man/man3sec/aclsort.3sec 0444 root bin
+f usr/share/man/man3sec/acltomode.3sec 0444 root bin
+f usr/share/man/man3sec/acltotext.3sec 0444 root bin
+s usr/share/man/man3sec/facl_get.3sec=acl_get.3sec
+s usr/share/man/man3sec/facl_set.3sec=acl_get.3sec
+d usr/share/man/man3secdb 0755 root bin
+s usr/share/man/man3secdb/chkauthattr.3secdb=getauthattr.3secdb
+s usr/share/man/man3secdb/endauthattr.3secdb=getauthattr.3secdb
+s usr/share/man/man3secdb/endexecattr.3secdb=getexecattr.3secdb
+s usr/share/man/man3secdb/endprofattr.3secdb=getprofattr.3secdb
+s usr/share/man/man3secdb/enduserattr.3secdb=getuserattr.3secdb
+s usr/share/man/man3secdb/fgetuserattr.3secdb=getuserattr.3secdb
+s usr/share/man/man3secdb/free_authattr.3secdb=getauthattr.3secdb
+s usr/share/man/man3secdb/free_execattr.3secdb=getexecattr.3secdb
+s usr/share/man/man3secdb/free_profattr.3secdb=getprofattr.3secdb
+s usr/share/man/man3secdb/free_proflist.3secdb=getprofattr.3secdb
+s usr/share/man/man3secdb/free_userattr.3secdb=getuserattr.3secdb
+f usr/share/man/man3secdb/getauthattr.3secdb 0444 root bin
+s usr/share/man/man3secdb/getauthnam.3secdb=getauthattr.3secdb
+f usr/share/man/man3secdb/getexecattr.3secdb 0444 root bin
+s usr/share/man/man3secdb/getexecprof.3secdb=getexecattr.3secdb
+s usr/share/man/man3secdb/getexecuser.3secdb=getexecattr.3secdb
+f usr/share/man/man3secdb/getprofattr.3secdb 0444 root bin
+s usr/share/man/man3secdb/getproflist.3secdb=getprofattr.3secdb
+s usr/share/man/man3secdb/getprofnam.3secdb=getprofattr.3secdb
+f usr/share/man/man3secdb/getuserattr.3secdb 0444 root bin
+s usr/share/man/man3secdb/getusernam.3secdb=getuserattr.3secdb
+s usr/share/man/man3secdb/getuseruid.3secdb=getuserattr.3secdb
+f usr/share/man/man3secdb/kva_match.3secdb 0444 root bin
+s usr/share/man/man3secdb/match_execattr.3secdb=getexecattr.3secdb
+s usr/share/man/man3secdb/setauthattr.3secdb=getauthattr.3secdb
+s usr/share/man/man3secdb/setexecattr.3secdb=getexecattr.3secdb
+s usr/share/man/man3secdb/setprofattr.3secdb=getprofattr.3secdb
+s usr/share/man/man3secdb/setuserattr.3secdb=getuserattr.3secdb
+d usr/share/man/man3sip 0755 root bin
+s usr/share/man/man3sip/sip_add_accept.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_accept_enc.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_accept_lang.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_alert_info.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_allow.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_allow_events.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_authen_info.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_author.3sip=sip_add_from.3sip
+f usr/share/man/man3sip/sip_add_branchid_to_via.3sip 0444 root bin
+s usr/share/man/man3sip/sip_add_call_info.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_callid.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_contact.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_content.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_content_disp.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_content_enc.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_content_lang.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_content_type.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_cseq.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_date.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_error_info.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_event.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_expires.3sip=sip_add_from.3sip
+f usr/share/man/man3sip/sip_add_from.3sip 0444 root bin
+f usr/share/man/man3sip/sip_add_header.3sip 0444 root bin
+s usr/share/man/man3sip/sip_add_in_reply_to.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_maxforward.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_mime_version.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_min_expires.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_org.3sip=sip_add_from.3sip
+f usr/share/man/man3sip/sip_add_param.3sip 0444 root bin
+s usr/share/man/man3sip/sip_add_passertedid.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_ppreferredid.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_priority.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_privacy.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_proxy_authen.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_proxy_author.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_proxy_require.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_rack.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_record_route.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_reply_to.3sip=sip_add_from.3sip
+f usr/share/man/man3sip/sip_add_request_line.3sip 0444 root bin
+s usr/share/man/man3sip/sip_add_require.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_response_line.3sip=sip_add_request_line.3sip
+s usr/share/man/man3sip/sip_add_retry_after.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_route.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_rseq.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_server.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_subject.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_substate.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_supported.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_to.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_tstamp.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_unsupported.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_user_agent.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_via.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_warning.3sip=sip_add_from.3sip
+s usr/share/man/man3sip/sip_add_www_authen.3sip=sip_add_from.3sip
+f usr/share/man/man3sip/sip_branchid.3sip 0444 root bin
+s usr/share/man/man3sip/sip_clear_stale_data.3sip=sip_init_conn_object.3sip
+f usr/share/man/man3sip/sip_clone_msg.3sip 0444 root bin
+s usr/share/man/man3sip/sip_conn_destroyed.3sip=sip_init_conn_object.3sip
+s usr/share/man/man3sip/sip_copy_all_headers.3sip=sip_copy_start_line.3sip
+s usr/share/man/man3sip/sip_copy_header.3sip=sip_copy_start_line.3sip
+s usr/share/man/man3sip/sip_copy_header_by_name.3sip=sip_copy_start_line.3sip
+f usr/share/man/man3sip/sip_copy_start_line.3sip 0444 root bin
+f usr/share/man/man3sip/sip_create_OKack.3sip 0444 root bin
+f usr/share/man/man3sip/sip_create_dialog_req.3sip 0444 root bin
+s usr/share/man/man3sip/sip_create_dialog_req_nocontact.3sip=sip_create_dialog_req.3sip
+f usr/share/man/man3sip/sip_create_response.3sip 0444 root bin
+f usr/share/man/man3sip/sip_delete_dialog.3sip 0444 root bin
+s usr/share/man/man3sip/sip_delete_header.3sip=sip_delete_start_line.3sip
+s usr/share/man/man3sip/sip_delete_header_by_name.3sip=sip_delete_start_line.3sip
+f usr/share/man/man3sip/sip_delete_start_line.3sip 0444 root bin
+s usr/share/man/man3sip/sip_delete_value.3sip=sip_delete_start_line.3sip
+s usr/share/man/man3sip/sip_disable_counters.3sip=sip_enable_counters.3sip
+s usr/share/man/man3sip/sip_disable_dialog_logging.3sip=sip_enable_trans_logging.3sip
+s usr/share/man/man3sip/sip_disable_trans_logging.3sip=sip_enable_trans_logging.3sip
+f usr/share/man/man3sip/sip_enable_counters.3sip 0444 root bin
+s usr/share/man/man3sip/sip_enable_dialog_logging.3sip=sip_enable_trans_logging.3sip
+f usr/share/man/man3sip/sip_enable_trans_logging.3sip 0444 root bin
+s usr/share/man/man3sip/sip_free_msg.3sip=sip_hold_msg.3sip
+s usr/share/man/man3sip/sip_free_parsed_uri.3sip=sip_parse_uri.3sip
+s usr/share/man/man3sip/sip_get_accept_enc.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_accept_lang.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_accept_sub_type.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_accept_type.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_alert_info_uri.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_allow_events.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_allow_method.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_authen_info.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_author_param.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_author_scheme.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_branchid.3sip=sip_get_num_via.3sip
+s usr/share/man/man3sip/sip_get_call_info_uri.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_callid.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_callseq_method.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_callseq_num.3sip=sip_get_contact_display_name.3sip
+f usr/share/man/man3sip/sip_get_contact_display_name.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_contact_uri_str.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_content.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_content_disp.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_content_enc.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_content_lang.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_content_length.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_content_sub_type.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_content_type.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_counter_value.3sip=sip_enable_counters.3sip
+f usr/share/man/man3sip/sip_get_cseq.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_date_day.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_date_month.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_date_time.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_date_timezone.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_date_wkday.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_date_year.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_dialog_callid.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_local_contact_uri.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_local_cseq.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_local_tag.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_local_uri.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_method.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_msgcnt.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_remote_cseq.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_remote_tag.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_remote_target_uri.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_remote_uri.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_dialog_route_set.3sip=sip_get_dialog_state.3sip
+f usr/share/man/man3sip/sip_get_dialog_state.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_dialog_type.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_get_error_info_uri.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_event.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_expires.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_from_display_name.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_from_tag.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_from_uri_str.3sip=sip_get_contact_display_name.3sip
+f usr/share/man/man3sip/sip_get_header.3sip 0444 root bin
+f usr/share/man/man3sip/sip_get_header_value.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_in_reply_to.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_maxforward.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_mime_version.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_min_expires.3sip=sip_get_contact_display_name.3sip
+f usr/share/man/man3sip/sip_get_msg_len.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_next_value.3sip=sip_get_header_value.3sip
+f usr/share/man/man3sip/sip_get_num_via.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_org.3sip=sip_get_contact_display_name.3sip
+f usr/share/man/man3sip/sip_get_param_value.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_params.3sip=sip_get_param_value.3sip
+s usr/share/man/man3sip/sip_get_passertedid_display_name.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_passertedid_uri_str.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_ppreferredid_display_name.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_ppreferredid_uri_str.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_priority.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_priv_value.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_proxy_authen_param.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_proxy_authen_scheme.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_proxy_author_param.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_proxy_author_scheme.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_proxy_require.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_rack_cseq_num.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_rack_method.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_rack_resp_num.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_replyto_display_name.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_replyto_uri_str.3sip=sip_get_contact_display_name.3sip
+f usr/share/man/man3sip/sip_get_request_method.3sip 0444 root bin
+f usr/share/man/man3sip/sip_get_request_uri_str.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_require.3sip=sip_get_contact_display_name.3sip
+f usr/share/man/man3sip/sip_get_resp_desc.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_response_code.3sip=sip_get_request_method.3sip
+s usr/share/man/man3sip/sip_get_response_phrase.3sip=sip_get_request_method.3sip
+s usr/share/man/man3sip/sip_get_retry_after_cmts.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_retry_after_time.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_route_display_name.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_route_uri_str.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_rseq.3sip=sip_get_cseq.3sip
+s usr/share/man/man3sip/sip_get_rseq_resp_num.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_server.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_sip_version.3sip=sip_get_request_method.3sip
+s usr/share/man/man3sip/sip_get_subject.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_substate.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_supported.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_to_display_name.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_to_tag.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_to_uri_str.3sip=sip_get_contact_display_name.3sip
+f usr/share/man/man3sip/sip_get_trans.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_trans_branchid.3sip=sip_get_trans_method.3sip
+s usr/share/man/man3sip/sip_get_trans_conn_obj.3sip=sip_get_trans_method.3sip
+f usr/share/man/man3sip/sip_get_trans_method.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_trans_orig_msg.3sip=sip_get_trans_method.3sip
+s usr/share/man/man3sip/sip_get_trans_resp_msg.3sip=sip_get_trans_method.3sip
+s usr/share/man/man3sip/sip_get_trans_state.3sip=sip_get_trans_method.3sip
+s usr/share/man/man3sip/sip_get_tstamp_delay.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_tstamp_value.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_unsupported.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_uri_errflags.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_uri_headers.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_uri_host.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_uri_opaque.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_uri_params.3sip=sip_is_sip_uri.3sip
+f usr/share/man/man3sip/sip_get_uri_parsed.3sip 0444 root bin
+s usr/share/man/man3sip/sip_get_uri_password.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_uri_path.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_uri_port.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_uri_query.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_uri_regname.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_uri_scheme.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_uri_user.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_get_user_agent.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_via_sent_by_host.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_via_sent_by_port.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_via_sent_protocol_name.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_via_sent_protocol_version.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_via_sent_transport.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_warning_agent.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_warning_code.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_warning_text.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_www_authen_param.3sip=sip_get_contact_display_name.3sip
+s usr/share/man/man3sip/sip_get_www_authen_scheme.3sip=sip_get_contact_display_name.3sip
+f usr/share/man/man3sip/sip_guid.3sip 0444 root bin
+s usr/share/man/man3sip/sip_hdr_to_str.3sip=sip_msg_to_str.3sip
+f usr/share/man/man3sip/sip_hold_dialog.3sip 0444 root bin
+f usr/share/man/man3sip/sip_hold_msg.3sip 0444 root bin
+f usr/share/man/man3sip/sip_hold_trans.3sip 0444 root bin
+f usr/share/man/man3sip/sip_init_conn_object.3sip 0444 root bin
+s usr/share/man/man3sip/sip_is_dialog_secure.3sip=sip_get_dialog_state.3sip
+s usr/share/man/man3sip/sip_is_param_present.3sip=sip_get_param_value.3sip
+f usr/share/man/man3sip/sip_is_sip_uri.3sip 0444 root bin
+s usr/share/man/man3sip/sip_is_uri_teluser.3sip=sip_is_sip_uri.3sip
+s usr/share/man/man3sip/sip_message_is_response.3sip=sip_msg_is_request.3sip
+f usr/share/man/man3sip/sip_msg_is_request.3sip 0444 root bin
+f usr/share/man/man3sip/sip_msg_to_str.3sip 0444 root bin
+f usr/share/man/man3sip/sip_new_msg.3sip 0444 root bin
+f usr/share/man/man3sip/sip_parse_uri.3sip 0444 root bin
+f usr/share/man/man3sip/sip_process_new_packet.3sip 0444 root bin
+f usr/share/man/man3sip/sip_register_sent_by.3sip 0444 root bin
+s usr/share/man/man3sip/sip_release_dialog.3sip=sip_hold_dialog.3sip
+s usr/share/man/man3sip/sip_release_trans.3sip=sip_hold_trans.3sip
+s usr/share/man/man3sip/sip_reqline_to_str.3sip=sip_msg_to_str.3sip
+s usr/share/man/man3sip/sip_respline_to_str.3sip=sip_msg_to_str.3sip
+f usr/share/man/man3sip/sip_sendmsg.3sip 0444 root bin
+s usr/share/man/man3sip/sip_sent_by_to_str.3sip=sip_msg_to_str.3sip
+f usr/share/man/man3sip/sip_stack_init.3sip 0444 root bin
+s usr/share/man/man3sip/sip_unregister_all_sent_by.3sip=sip_register_sent_by.3sip
+s usr/share/man/man3sip/sip_unregister_sent_by.3sip=sip_register_sent_by.3sip
+s usr/share/man/man3sip/sip_uri_errflags_to_str.3sip=sip_is_sip_uri.3sip
+d usr/share/man/man3slp 0755 root bin
+f usr/share/man/man3slp/SLPClose.3slp 0444 root bin
+f usr/share/man/man3slp/SLPDelAttrs.3slp 0444 root bin
+f usr/share/man/man3slp/SLPDereg.3slp 0444 root bin
+f usr/share/man/man3slp/SLPEscape.3slp 0444 root bin
+f usr/share/man/man3slp/SLPFindAttrs.3slp 0444 root bin
+f usr/share/man/man3slp/SLPFindScopes.3slp 0444 root bin
+f usr/share/man/man3slp/SLPFindSrvTypes.3slp 0444 root bin
+f usr/share/man/man3slp/SLPFindSrvs.3slp 0444 root bin
+f usr/share/man/man3slp/SLPFree.3slp 0444 root bin
+f usr/share/man/man3slp/SLPGetProperty.3slp 0444 root bin
+f usr/share/man/man3slp/SLPGetRefreshInterval.3slp 0444 root bin
+f usr/share/man/man3slp/SLPOpen.3slp 0444 root bin
+f usr/share/man/man3slp/SLPParseSrvURL.3slp 0444 root bin
+f usr/share/man/man3slp/SLPReg.3slp 0444 root bin
+f usr/share/man/man3slp/SLPSetProperty.3slp 0444 root bin
+f usr/share/man/man3slp/SLPUnescape.3slp 0444 root bin
+f usr/share/man/man3slp/slp_api.3slp 0444 root bin
+f usr/share/man/man3slp/slp_strerror.3slp 0444 root bin
+d usr/share/man/man3socket 0755 root bin
+f usr/share/man/man3socket/accept.3socket 0444 root bin
+s usr/share/man/man3socket/accept4.3socket=accept.3socket
+f usr/share/man/man3socket/bind.3socket 0444 root bin
+f usr/share/man/man3socket/connect.3socket 0444 root bin
+s usr/share/man/man3socket/endnetent.3socket=getnetbyname.3socket
+s usr/share/man/man3socket/endprotoent.3socket=getprotobyname.3socket
+s usr/share/man/man3socket/endservent.3socket=getservbyname.3socket
+s usr/share/man/man3socket/ether_aton.3socket=ethers.3socket
+s usr/share/man/man3socket/ether_aton_r.3socket=ethers.3socket
+s usr/share/man/man3socket/ether_hostton.3socket=ethers.3socket
+s usr/share/man/man3socket/ether_line.3socket=ethers.3socket
+s usr/share/man/man3socket/ether_ntoa.3socket=ethers.3socket
+s usr/share/man/man3socket/ether_ntoa_r.3socket=ethers.3socket
+s usr/share/man/man3socket/ether_ntohost.3socket=ethers.3socket
+f usr/share/man/man3socket/ethers.3socket 0444 root bin
+s usr/share/man/man3socket/freeaddrinfo.3socket=getaddrinfo.3socket
+s usr/share/man/man3socket/freehostent.3socket=getipnodebyname.3socket
+s usr/share/man/man3socket/freeifaddrs.3socket=getifaddrs.3socket
+s usr/share/man/man3socket/gai_strerror.3socket=getaddrinfo.3socket
+f usr/share/man/man3socket/getaddrinfo.3socket 0444 root bin
+f usr/share/man/man3socket/getifaddrs.3socket 0444 root bin
+s usr/share/man/man3socket/getipnodebyaddr.3socket=getipnodebyname.3socket
+f usr/share/man/man3socket/getipnodebyname.3socket 0444 root bin
+s usr/share/man/man3socket/getipv4sourcefilter.3socket=getsourcefilter.3socket
+s usr/share/man/man3socket/getnameinfo.3socket=getaddrinfo.3socket
+s usr/share/man/man3socket/getnetbyaddr.3socket=getnetbyname.3socket
+s usr/share/man/man3socket/getnetbyaddr_r.3socket=getnetbyname.3socket
+f usr/share/man/man3socket/getnetbyname.3socket 0444 root bin
+s usr/share/man/man3socket/getnetbyname_r.3socket=getnetbyname.3socket
+s usr/share/man/man3socket/getnetent.3socket=getnetbyname.3socket
+s usr/share/man/man3socket/getnetent_r.3socket=getnetbyname.3socket
+f usr/share/man/man3socket/getpeername.3socket 0444 root bin
+f usr/share/man/man3socket/getprotobyname.3socket 0444 root bin
+s usr/share/man/man3socket/getprotobyname_r.3socket=getprotobyname.3socket
+s usr/share/man/man3socket/getprotobynumber.3socket=getprotobyname.3socket
+s usr/share/man/man3socket/getprotobynumber_r.3socket=getprotobyname.3socket
+s usr/share/man/man3socket/getprotoent.3socket=getprotobyname.3socket
+s usr/share/man/man3socket/getprotoent_r.3socket=getprotobyname.3socket
+f usr/share/man/man3socket/getservbyname.3socket 0444 root bin
+s usr/share/man/man3socket/getservbyname_r.3socket=getservbyname.3socket
+s usr/share/man/man3socket/getservbyport.3socket=getservbyname.3socket
+s usr/share/man/man3socket/getservbyport_r.3socket=getservbyname.3socket
+s usr/share/man/man3socket/getservent.3socket=getservbyname.3socket
+s usr/share/man/man3socket/getservent_r.3socket=getservbyname.3socket
+f usr/share/man/man3socket/getsockname.3socket 0444 root bin
+f usr/share/man/man3socket/getsockopt.3socket 0444 root bin
+f usr/share/man/man3socket/getsourcefilter.3socket 0444 root bin
+f usr/share/man/man3socket/icmp6_filter.3socket 0444 root bin
+s usr/share/man/man3socket/if_freenameindex.3socket=if_nametoindex.3socket
+s usr/share/man/man3socket/if_indextoname.3socket=if_nametoindex.3socket
+s usr/share/man/man3socket/if_nameindex.3socket=if_nametoindex.3socket
+f usr/share/man/man3socket/if_nametoindex.3socket 0444 root bin
+f usr/share/man/man3socket/inet6_opt.3socket 0444 root bin
+s usr/share/man/man3socket/inet6_opt_append.3socket=inet6_opt.3socket
+s usr/share/man/man3socket/inet6_opt_find.3socket=inet6_opt.3socket
+s usr/share/man/man3socket/inet6_opt_finish.3socket=inet6_opt.3socket
+s usr/share/man/man3socket/inet6_opt_get_val.3socket=inet6_opt.3socket
+s usr/share/man/man3socket/inet6_opt_init.3socket=inet6_opt.3socket
+s usr/share/man/man3socket/inet6_opt_next.3socket=inet6_opt.3socket
+s usr/share/man/man3socket/inet6_opt_set_val.3socket=inet6_opt.3socket
+f usr/share/man/man3socket/inet6_rth.3socket 0444 root bin
+s usr/share/man/man3socket/inet6_rth_add.3socket=inet6_rth.3socket
+s usr/share/man/man3socket/inet6_rth_getaddr.3socket=inet6_rth.3socket
+s usr/share/man/man3socket/inet6_rth_init.3socket=inet6_rth.3socket
+s usr/share/man/man3socket/inet6_rth_reverse.3socket=inet6_rth.3socket
+s usr/share/man/man3socket/inet6_rth_segments.3socket=inet6_rth.3socket
+s usr/share/man/man3socket/inet6_rth_space.3socket=inet6_rth.3socket
+f usr/share/man/man3socket/listen.3socket 0444 root bin
+f usr/share/man/man3socket/rcmd.3socket 0444 root bin
+s usr/share/man/man3socket/rcmd_af.3socket=rcmd.3socket
+f usr/share/man/man3socket/recv.3socket 0444 root bin
+s usr/share/man/man3socket/recvfrom.3socket=recv.3socket
+s usr/share/man/man3socket/recvmsg.3socket=recv.3socket
+f usr/share/man/man3socket/rexec.3socket 0444 root bin
+s usr/share/man/man3socket/rexec_af.3socket=rexec.3socket
+s usr/share/man/man3socket/rresvport.3socket=rcmd.3socket
+s usr/share/man/man3socket/rresvport_af.3socket=rcmd.3socket
+s usr/share/man/man3socket/ruserok.3socket=rcmd.3socket
+f usr/share/man/man3socket/sctp_bindx.3socket 0444 root bin
+s usr/share/man/man3socket/sctp_freeladdrs.3socket=sctp_getladdrs.3socket
+s usr/share/man/man3socket/sctp_freepaddrs.3socket=sctp_getpaddrs.3socket
+f usr/share/man/man3socket/sctp_getladdrs.3socket 0444 root bin
+f usr/share/man/man3socket/sctp_getpaddrs.3socket 0444 root bin
+f usr/share/man/man3socket/sctp_opt_info.3socket 0444 root bin
+f usr/share/man/man3socket/sctp_peeloff.3socket 0444 root bin
+f usr/share/man/man3socket/sctp_recvmsg.3socket 0444 root bin
+f usr/share/man/man3socket/sctp_send.3socket 0444 root bin
+f usr/share/man/man3socket/sctp_sendmsg.3socket 0444 root bin
+f usr/share/man/man3socket/send.3socket 0444 root bin
+s usr/share/man/man3socket/sendmsg.3socket=send.3socket
+s usr/share/man/man3socket/sendto.3socket=send.3socket
+s usr/share/man/man3socket/setipv4sourcefilter.3socket=getsourcefilter.3socket
+s usr/share/man/man3socket/setnetent.3socket=getnetbyname.3socket
+s usr/share/man/man3socket/setprotoent.3socket=getprotobyname.3socket
+s usr/share/man/man3socket/setservent.3socket=getservbyname.3socket
+s usr/share/man/man3socket/setsockopt.3socket=getsockopt.3socket
+s usr/share/man/man3socket/setsourcefilter.3socket=getsourcefilter.3socket
+f usr/share/man/man3socket/shutdown.3socket 0444 root bin
+f usr/share/man/man3socket/sockaddr.3socket 0444 root bin
+s usr/share/man/man3socket/sockaddr_dl.3socket=sockaddr.3socket
+s usr/share/man/man3socket/sockaddr_in.3socket=sockaddr.3socket
+s usr/share/man/man3socket/sockaddr_in6.3socket=sockaddr.3socket
+s usr/share/man/man3socket/sockaddr_ll.3socket=sockaddr.3socket
+s usr/share/man/man3socket/sockaddr_storage.3socket=sockaddr.3socket
+s usr/share/man/man3socket/sockaddr_un.3socket=sockaddr.3socket
+f usr/share/man/man3socket/socket.3socket 0444 root bin
+f usr/share/man/man3socket/socketpair.3socket 0444 root bin
+f usr/share/man/man3socket/spray.3socket 0444 root bin
+d usr/share/man/man3stmf 0755 root bin
+f usr/share/man/man3stmf/stmfAddToHostGroup.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfAddToTargetGroup.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfAddViewEntry.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfClearProviderData.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfCreateHostGroup.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfCreateLu.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfCreateLuResource.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfCreateTargetGroup.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfDeleteHostGroup.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfDeleteLu.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfDeleteTargetGroup.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfDevidFromIscsiName.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfDevidFromWwn.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfFreeLuResource.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfFreeMemory.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetHostGroupList.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetHostGroupMembers.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetLogicalUnitList.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetLogicalUnitProperties.3stmf 0444 root bin
+s usr/share/man/man3stmf/stmfGetLuProp.3stmf=stmfSetLuProp.3stmf
+f usr/share/man/man3stmf/stmfGetLuResource.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetPersistMethod.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetProviderData.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetProviderDataProt.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetState.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetTargetGroupList.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetTargetGroupMembers.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetTargetList.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetTargetProperties.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfGetViewEntryList.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfModifyLu.3stmf 0444 root bin
+s usr/share/man/man3stmf/stmfModifyLuByFname.3stmf=stmfModifyLu.3stmf
+f usr/share/man/man3stmf/stmfOfflineLogicalUnit.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfOfflineTarget.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfOnlineLogicalUnit.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfOnlineTarget.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfRemoveFromHostGroup.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfRemoveFromTargetGroup.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfRemoveViewEntry.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfSetLuProp.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfSetPersistMethod.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfSetProviderData.3stmf 0444 root bin
+f usr/share/man/man3stmf/stmfSetProviderDataProt.3stmf 0444 root bin
+d usr/share/man/man3sysevent 0755 root bin
+f usr/share/man/man3sysevent/sysevent_bind_handle.3sysevent 0444 root bin
+f usr/share/man/man3sysevent/sysevent_free.3sysevent 0444 root bin
+f usr/share/man/man3sysevent/sysevent_get_attr_list.3sysevent 0444 root bin
+f usr/share/man/man3sysevent/sysevent_get_class_name.3sysevent 0444 root bin
+s usr/share/man/man3sysevent/sysevent_get_pid.3sysevent=sysevent_get_vendor_name.3sysevent
+s usr/share/man/man3sysevent/sysevent_get_pub_name.3sysevent=sysevent_get_vendor_name.3sysevent
+s usr/share/man/man3sysevent/sysevent_get_seq.3sysevent=sysevent_get_class_name.3sysevent
+s usr/share/man/man3sysevent/sysevent_get_size.3sysevent=sysevent_get_class_name.3sysevent
+s usr/share/man/man3sysevent/sysevent_get_subclass_name.3sysevent=sysevent_get_class_name.3sysevent
+s usr/share/man/man3sysevent/sysevent_get_time.3sysevent=sysevent_get_class_name.3sysevent
+f usr/share/man/man3sysevent/sysevent_get_vendor_name.3sysevent 0444 root bin
+f usr/share/man/man3sysevent/sysevent_post_event.3sysevent 0444 root bin
+f usr/share/man/man3sysevent/sysevent_subscribe_event.3sysevent 0444 root bin
+s usr/share/man/man3sysevent/sysevent_unbind_handle.3sysevent=sysevent_bind_handle.3sysevent
+s usr/share/man/man3sysevent/sysevent_unsubscribe_event.3sysevent=sysevent_subscribe_event.3sysevent
+f usr/share/man/man3sysevent/sysevents.3sysevent 0444 root bin
+d usr/share/man/man3tecla 0755 root bin
+s usr/share/man/man3tecla/cfc_file_start.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/cfc_literal_escapes.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/cfc_set_check_fn.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/cpl_add_completion.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/cpl_check_exe.3tecla=cpl_complete_word.3tecla
+f usr/share/man/man3tecla/cpl_complete_word.3tecla 0444 root bin
+s usr/share/man/man3tecla/cpl_file_completions.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/cpl_last_error.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/cpl_list_completions.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/cpl_recall_matches.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/cpl_record_error.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/del_CplFileConf.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/del_ExpandFile.3tecla=ef_expand_file.3tecla
+s usr/share/man/man3tecla/del_GetLine.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/del_PathCache.3tecla=pca_lookup_file.3tecla
+s usr/share/man/man3tecla/del_PcaPathConf.3tecla=pca_lookup_file.3tecla
+s usr/share/man/man3tecla/del_WordCompletion.3tecla=cpl_complete_word.3tecla
+f usr/share/man/man3tecla/ef_expand_file.3tecla 0444 root bin
+s usr/share/man/man3tecla/ef_last_error.3tecla=ef_expand_file.3tecla
+s usr/share/man/man3tecla/ef_list_expansions.3tecla=ef_expand_file.3tecla
+s usr/share/man/man3tecla/gl_abandon_line.3tecla=gl_io_mode.3tecla
+s usr/share/man/man3tecla/gl_append_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_automatic_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_bind_keyseq.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_catch_blocked.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_change_terminal.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_clear_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_completion_action.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_configure_getline.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_customize_completion.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_display_text.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_echo_mode.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_erase_terminal.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_error_message.3tecla=gl_get_line.3tecla
+f usr/share/man/man3tecla/gl_get_line.3tecla 0444 root bin
+s usr/share/man/man3tecla/gl_group_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_handle_signal.3tecla=gl_io_mode.3tecla
+s usr/share/man/man3tecla/gl_ignore_signal.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_inactivity_timeout.3tecla=gl_get_line.3tecla
+f usr/share/man/man3tecla/gl_io_mode.3tecla 0444 root bin
+s usr/share/man/man3tecla/gl_last_signal.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_limit_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_list_signals.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_load_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_lookup_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_normal_io.3tecla=gl_io_mode.3tecla
+s usr/share/man/man3tecla/gl_pending_io.3tecla=gl_io_mode.3tecla
+s usr/share/man/man3tecla/gl_prompt_style.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_query_char.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_range_of_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_raw_io.3tecla=gl_io_mode.3tecla
+s usr/share/man/man3tecla/gl_read_char.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_register_action.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_replace_prompt.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_resize_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_return_status.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_save_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_set_term_size.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_show_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_size_of_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_state_of_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_terminal_size.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_toggle_history.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_trap_signal.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/gl_tty_signals.3tecla=gl_io_mode.3tecla
+s usr/share/man/man3tecla/gl_watch_fd.3tecla=gl_get_line.3tecla
+f usr/share/man/man3tecla/libtecla_version.3tecla 0444 root bin
+s usr/share/man/man3tecla/new_CplFileConf.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/new_ExpandFile.3tecla=ef_expand_file.3tecla
+s usr/share/man/man3tecla/new_GetLine.3tecla=gl_get_line.3tecla
+s usr/share/man/man3tecla/new_PathCache.3tecla=pca_lookup_file.3tecla
+s usr/share/man/man3tecla/new_PcaPathConf.3tecla=pca_lookup_file.3tecla
+s usr/share/man/man3tecla/new_WordCompletion.3tecla=cpl_complete_word.3tecla
+s usr/share/man/man3tecla/pca_last_error.3tecla=pca_lookup_file.3tecla
+f usr/share/man/man3tecla/pca_lookup_file.3tecla 0444 root bin
+s usr/share/man/man3tecla/pca_path_completions.3tecla=pca_lookup_file.3tecla
+s usr/share/man/man3tecla/pca_scan_path.3tecla=pca_lookup_file.3tecla
+s usr/share/man/man3tecla/pca_set_check_fn.3tecla=pca_lookup_file.3tecla
+s usr/share/man/man3tecla/ppc_file_start.3tecla=pca_lookup_file.3tecla
+s usr/share/man/man3tecla/ppc_literal_escapes.3tecla=pca_lookup_file.3tecla
+d usr/share/man/man3utempter 0755 root bin
+s usr/share/man/man3utempter/addToUtmp.3utempter=utempter_add_record.3utempter
+s usr/share/man/man3utempter/removeFromUtmp.3utempter=utempter_add_record.3utempter
+s usr/share/man/man3utempter/removeLineFromUtmp.3utempter=utempter_add_record.3utempter
+f usr/share/man/man3utempter/utempter_add_record.3utempter 0444 root bin
+s usr/share/man/man3utempter/utempter_remove_added_record.3utempter=utempter_add_record.3utempter
+s usr/share/man/man3utempter/utempter_remove_record.3utempter=utempter_add_record.3utempter
+d usr/share/man/man3uuid 0755 root bin
+f usr/share/man/man3uuid/uuid_clear.3uuid 0444 root bin
+s usr/share/man/man3uuid/uuid_compare.3uuid=uuid_clear.3uuid
+s usr/share/man/man3uuid/uuid_copy.3uuid=uuid_clear.3uuid
+s usr/share/man/man3uuid/uuid_generate.3uuid=uuid_clear.3uuid
+s usr/share/man/man3uuid/uuid_generate_random.3uuid=uuid_clear.3uuid
+s usr/share/man/man3uuid/uuid_generate_time.3uuid=uuid_clear.3uuid
+s usr/share/man/man3uuid/uuid_is_null.3uuid=uuid_clear.3uuid
+s usr/share/man/man3uuid/uuid_parse.3uuid=uuid_clear.3uuid
+s usr/share/man/man3uuid/uuid_time.3uuid=uuid_clear.3uuid
+s usr/share/man/man3uuid/uuid_unparse.3uuid=uuid_clear.3uuid
+s usr/share/man/man3uuid/uuid_unparse_lower.3uuid=uuid_clear.3uuid
+s usr/share/man/man3uuid/uuid_unparse_upper.3uuid=uuid_clear.3uuid
+d usr/share/man/man3vnd 0755 root bin
+s usr/share/man/man3vnd/frameio_t.3vnd=vnd_frameio_read.3vnd
+s usr/share/man/man3vnd/framevec_t.3vnd=vnd_frameio_read.3vnd
+s usr/share/man/man3vnd/vnd_close.3vnd=vnd_create.3vnd
+f usr/share/man/man3vnd/vnd_create.3vnd 0444 root bin
+f usr/share/man/man3vnd/vnd_errno.3vnd 0444 root bin
+f usr/share/man/man3vnd/vnd_frameio_read.3vnd 0444 root bin
+s usr/share/man/man3vnd/vnd_frameio_write.3vnd=vnd_frameio_read.3vnd
+s usr/share/man/man3vnd/vnd_open.3vnd=vnd_create.3vnd
+f usr/share/man/man3vnd/vnd_pollfd.3vnd 0444 root bin
+f usr/share/man/man3vnd/vnd_prop_get.3vnd 0444 root bin
+f usr/share/man/man3vnd/vnd_prop_iter.3vnd 0444 root bin
+s usr/share/man/man3vnd/vnd_prop_iter_f.3vnd=vnd_prop_iter.3vnd
+s usr/share/man/man3vnd/vnd_prop_set.3vnd=vnd_prop_get.3vnd
+f usr/share/man/man3vnd/vnd_prop_writeable.3vnd 0444 root bin
+s usr/share/man/man3vnd/vnd_strerror.3vnd=vnd_errno.3vnd
+s usr/share/man/man3vnd/vnd_strsyserror.3vnd=vnd_errno.3vnd
+s usr/share/man/man3vnd/vnd_syserrno.3vnd=vnd_errno.3vnd
+s usr/share/man/man3vnd/vnd_unlink.3vnd=vnd_create.3vnd
+f usr/share/man/man3vnd/vnd_walk.3vnd 0444 root bin
+s usr/share/man/man3vnd/vnd_walk_cb_f.3vnd=vnd_walk.3vnd
+d usr/share/man/man3volmgt 0755 root bin
+f usr/share/man/man3volmgt/media_findname.3volmgt 0444 root bin
+f usr/share/man/man3volmgt/media_getattr.3volmgt 0444 root bin
+f usr/share/man/man3volmgt/media_getid.3volmgt 0444 root bin
+s usr/share/man/man3volmgt/media_setattr.3volmgt=media_getattr.3volmgt
+f usr/share/man/man3volmgt/volmgt_acquire.3volmgt 0444 root bin
+f usr/share/man/man3volmgt/volmgt_check.3volmgt 0444 root bin
+f usr/share/man/man3volmgt/volmgt_feature_enabled.3volmgt 0444 root bin
+f usr/share/man/man3volmgt/volmgt_inuse.3volmgt 0444 root bin
+f usr/share/man/man3volmgt/volmgt_ownspath.3volmgt 0444 root bin
+f usr/share/man/man3volmgt/volmgt_release.3volmgt 0444 root bin
+f usr/share/man/man3volmgt/volmgt_root.3volmgt 0444 root bin
+f usr/share/man/man3volmgt/volmgt_running.3volmgt 0444 root bin
+s usr/share/man/man3volmgt/volmgt_symdev.3volmgt=volmgt_symname.3volmgt
+f usr/share/man/man3volmgt/volmgt_symname.3volmgt 0444 root bin
+d usr/share/man/man3x11 0755 root bin
+d usr/share/man/man3xau 0755 root bin
+d usr/share/man/man3xaw 0755 root bin
+d usr/share/man/man3xcurses 0755 root bin
+s usr/share/man/man3xcurses/COLORS.3xcurses=can_change_color.3xcurses
+s usr/share/man/man3xcurses/COLOR_PAIR.3xcurses=can_change_color.3xcurses
+s usr/share/man/man3xcurses/COLOR_PAIRS.3xcurses=can_change_color.3xcurses
+f usr/share/man/man3xcurses/COLS.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/LINES.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/PAIR_NUMBER.3xcurses=can_change_color.3xcurses
+f usr/share/man/man3xcurses/add_wch.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/add_wchnstr.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/add_wchstr.3xcurses=add_wchnstr.3xcurses
+f usr/share/man/man3xcurses/addch.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/addchnstr.3xcurses=addchstr.3xcurses
+f usr/share/man/man3xcurses/addchstr.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/addnstr.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/addnwstr.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/addstr.3xcurses=addnstr.3xcurses
+s usr/share/man/man3xcurses/addwstr.3xcurses=addnwstr.3xcurses
+f usr/share/man/man3xcurses/attr_get.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/attr_off.3xcurses=attr_get.3xcurses
+s usr/share/man/man3xcurses/attr_on.3xcurses=attr_get.3xcurses
+s usr/share/man/man3xcurses/attr_set.3xcurses=attr_get.3xcurses
+f usr/share/man/man3xcurses/attroff.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/attron.3xcurses=attroff.3xcurses
+s usr/share/man/man3xcurses/attrset.3xcurses=attroff.3xcurses
+f usr/share/man/man3xcurses/baudrate.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/beep.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/bkgd.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/bkgdset.3xcurses=bkgd.3xcurses
+f usr/share/man/man3xcurses/bkgrnd.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/bkgrndset.3xcurses=bkgrnd.3xcurses
+f usr/share/man/man3xcurses/border.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/border_set.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/box.3xcurses=border.3xcurses
+s usr/share/man/man3xcurses/box_set.3xcurses=border_set.3xcurses
+f usr/share/man/man3xcurses/can_change_color.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/cbreak.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/chgat.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/clear.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/clearok.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/clrtobot.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/clrtoeol.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/color_content.3xcurses=can_change_color.3xcurses
+s usr/share/man/man3xcurses/color_set.3xcurses=attr_get.3xcurses
+f usr/share/man/man3xcurses/copywin.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/cur_term.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/curs_set.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/curscr.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/curses.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/def_prog_mode.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/def_shell_mode.3xcurses=def_prog_mode.3xcurses
+f usr/share/man/man3xcurses/del_curterm.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/delay_output.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/delch.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/deleteln.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/delscreen.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/delwin.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/derwin.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/doupdate.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/dupwin.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/echo.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/echo_wchar.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/echochar.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/endwin.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/erase.3xcurses=clear.3xcurses
+f usr/share/man/man3xcurses/erasechar.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/erasewchar.3xcurses=erasechar.3xcurses
+f usr/share/man/man3xcurses/filter.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/flash.3xcurses=beep.3xcurses
+f usr/share/man/man3xcurses/flushinp.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/get_wch.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/get_wstr.3xcurses=getn_wstr.3xcurses
+f usr/share/man/man3xcurses/getbegyx.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/getbkgd.3xcurses=bkgd.3xcurses
+s usr/share/man/man3xcurses/getbkgrnd.3xcurses=bkgrnd.3xcurses
+f usr/share/man/man3xcurses/getcchar.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/getch.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/getmaxyx.3xcurses=getbegyx.3xcurses
+f usr/share/man/man3xcurses/getn_wstr.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/getnstr.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/getparyx.3xcurses=getbegyx.3xcurses
+s usr/share/man/man3xcurses/getstr.3xcurses=getnstr.3xcurses
+f usr/share/man/man3xcurses/getwin.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/getyx.3xcurses=getbegyx.3xcurses
+f usr/share/man/man3xcurses/halfdelay.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/has_colors.3xcurses=can_change_color.3xcurses
+f usr/share/man/man3xcurses/has_ic.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/has_il.3xcurses=has_ic.3xcurses
+f usr/share/man/man3xcurses/hline.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/hline_set.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/idcok.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/idlok.3xcurses=clearok.3xcurses
+f usr/share/man/man3xcurses/immedok.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/in_wch.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/in_wchnstr.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/in_wchstr.3xcurses=in_wchnstr.3xcurses
+f usr/share/man/man3xcurses/inch.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/inchnstr.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/inchstr.3xcurses=inchnstr.3xcurses
+s usr/share/man/man3xcurses/init_color.3xcurses=can_change_color.3xcurses
+s usr/share/man/man3xcurses/init_pair.3xcurses=can_change_color.3xcurses
+f usr/share/man/man3xcurses/initscr.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/innstr.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/innwstr.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/ins_nwstr.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/ins_wch.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/ins_wstr.3xcurses=ins_nwstr.3xcurses
+f usr/share/man/man3xcurses/insch.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/insdelln.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/insertln.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/insnstr.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/insstr.3xcurses=insnstr.3xcurses
+s usr/share/man/man3xcurses/instr.3xcurses=innstr.3xcurses
+f usr/share/man/man3xcurses/intrflush.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/inwstr.3xcurses=innwstr.3xcurses
+f usr/share/man/man3xcurses/is_linetouched.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/is_wintouched.3xcurses=is_linetouched.3xcurses
+s usr/share/man/man3xcurses/isendwin.3xcurses=endwin.3xcurses
+s usr/share/man/man3xcurses/key_name.3xcurses=keyname.3xcurses
+f usr/share/man/man3xcurses/keyname.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/keypad.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/killchar.3xcurses=erasechar.3xcurses
+s usr/share/man/man3xcurses/killwchar.3xcurses=erasechar.3xcurses
+s usr/share/man/man3xcurses/leaveok.3xcurses=clearok.3xcurses
+f usr/share/man/man3xcurses/libcurses.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/longname.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/meta.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/move.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/mvadd_wch.3xcurses=add_wch.3xcurses
+s usr/share/man/man3xcurses/mvadd_wchnstr.3xcurses=add_wchnstr.3xcurses
+s usr/share/man/man3xcurses/mvadd_wchstr.3xcurses=add_wchnstr.3xcurses
+s usr/share/man/man3xcurses/mvaddch.3xcurses=addch.3xcurses
+s usr/share/man/man3xcurses/mvaddchnstr.3xcurses=addchstr.3xcurses
+s usr/share/man/man3xcurses/mvaddchstr.3xcurses=addchstr.3xcurses
+s usr/share/man/man3xcurses/mvaddnstr.3xcurses=addnstr.3xcurses
+s usr/share/man/man3xcurses/mvaddnwstr.3xcurses=addnwstr.3xcurses
+s usr/share/man/man3xcurses/mvaddstr.3xcurses=addnstr.3xcurses
+s usr/share/man/man3xcurses/mvaddwstr.3xcurses=addnwstr.3xcurses
+s usr/share/man/man3xcurses/mvchgat.3xcurses=chgat.3xcurses
+f usr/share/man/man3xcurses/mvcur.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/mvdelch.3xcurses=delch.3xcurses
+f usr/share/man/man3xcurses/mvderwin.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/mvget_wch.3xcurses=get_wch.3xcurses
+s usr/share/man/man3xcurses/mvget_wstr.3xcurses=getn_wstr.3xcurses
+s usr/share/man/man3xcurses/mvgetch.3xcurses=getch.3xcurses
+s usr/share/man/man3xcurses/mvgetn_wstr.3xcurses=getn_wstr.3xcurses
+s usr/share/man/man3xcurses/mvgetnstr.3xcurses=getnstr.3xcurses
+s usr/share/man/man3xcurses/mvgetstr.3xcurses=getnstr.3xcurses
+s usr/share/man/man3xcurses/mvhline.3xcurses=hline.3xcurses
+s usr/share/man/man3xcurses/mvhline_set.3xcurses=hline_set.3xcurses
+s usr/share/man/man3xcurses/mvin_wch.3xcurses=in_wch.3xcurses
+s usr/share/man/man3xcurses/mvin_wchnstr.3xcurses=in_wchnstr.3xcurses
+s usr/share/man/man3xcurses/mvin_wchstr.3xcurses=in_wchnstr.3xcurses
+s usr/share/man/man3xcurses/mvinch.3xcurses=inch.3xcurses
+s usr/share/man/man3xcurses/mvinchnstr.3xcurses=inchnstr.3xcurses
+s usr/share/man/man3xcurses/mvinchstr.3xcurses=inchnstr.3xcurses
+s usr/share/man/man3xcurses/mvinnstr.3xcurses=innstr.3xcurses
+s usr/share/man/man3xcurses/mvinnwstr.3xcurses=innwstr.3xcurses
+s usr/share/man/man3xcurses/mvins_nwstr.3xcurses=ins_nwstr.3xcurses
+s usr/share/man/man3xcurses/mvins_wch.3xcurses=ins_wch.3xcurses
+s usr/share/man/man3xcurses/mvins_wstr.3xcurses=ins_nwstr.3xcurses
+s usr/share/man/man3xcurses/mvinsch.3xcurses=insch.3xcurses
+s usr/share/man/man3xcurses/mvinsnstr.3xcurses=insnstr.3xcurses
+s usr/share/man/man3xcurses/mvinsstr.3xcurses=insnstr.3xcurses
+s usr/share/man/man3xcurses/mvinstr.3xcurses=innstr.3xcurses
+s usr/share/man/man3xcurses/mvinwstr.3xcurses=innwstr.3xcurses
+f usr/share/man/man3xcurses/mvprintw.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/mvscanw.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/mvvline.3xcurses=hline.3xcurses
+s usr/share/man/man3xcurses/mvvline_set.3xcurses=hline_set.3xcurses
+s usr/share/man/man3xcurses/mvwadd_wch.3xcurses=add_wch.3xcurses
+s usr/share/man/man3xcurses/mvwadd_wchnstr.3xcurses=add_wchnstr.3xcurses
+s usr/share/man/man3xcurses/mvwadd_wchstr.3xcurses=add_wchnstr.3xcurses
+s usr/share/man/man3xcurses/mvwaddch.3xcurses=addch.3xcurses
+s usr/share/man/man3xcurses/mvwaddchnstr.3xcurses=addchstr.3xcurses
+s usr/share/man/man3xcurses/mvwaddchstr.3xcurses=addchstr.3xcurses
+s usr/share/man/man3xcurses/mvwaddnstr.3xcurses=addnstr.3xcurses
+s usr/share/man/man3xcurses/mvwaddnwstr.3xcurses=addnwstr.3xcurses
+s usr/share/man/man3xcurses/mvwaddstr.3xcurses=addnstr.3xcurses
+s usr/share/man/man3xcurses/mvwaddwstr.3xcurses=addnwstr.3xcurses
+s usr/share/man/man3xcurses/mvwchgat.3xcurses=chgat.3xcurses
+s usr/share/man/man3xcurses/mvwdelch.3xcurses=delch.3xcurses
+s usr/share/man/man3xcurses/mvwget_wch.3xcurses=get_wch.3xcurses
+s usr/share/man/man3xcurses/mvwget_wstr.3xcurses=getn_wstr.3xcurses
+s usr/share/man/man3xcurses/mvwgetch.3xcurses=getch.3xcurses
+s usr/share/man/man3xcurses/mvwgetn_wstr.3xcurses=getn_wstr.3xcurses
+s usr/share/man/man3xcurses/mvwgetnstr.3xcurses=getnstr.3xcurses
+s usr/share/man/man3xcurses/mvwgetstr.3xcurses=getnstr.3xcurses
+s usr/share/man/man3xcurses/mvwhline.3xcurses=hline.3xcurses
+s usr/share/man/man3xcurses/mvwhline_set.3xcurses=hline_set.3xcurses
+f usr/share/man/man3xcurses/mvwin.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/mvwin_wch.3xcurses=in_wch.3xcurses
+s usr/share/man/man3xcurses/mvwin_wchnstr.3xcurses=in_wchnstr.3xcurses
+s usr/share/man/man3xcurses/mvwin_wchstr.3xcurses=in_wchnstr.3xcurses
+s usr/share/man/man3xcurses/mvwinch.3xcurses=inch.3xcurses
+s usr/share/man/man3xcurses/mvwinchnstr.3xcurses=inchnstr.3xcurses
+s usr/share/man/man3xcurses/mvwinchstr.3xcurses=inchnstr.3xcurses
+s usr/share/man/man3xcurses/mvwinnstr.3xcurses=innstr.3xcurses
+s usr/share/man/man3xcurses/mvwinnwstr.3xcurses=innwstr.3xcurses
+s usr/share/man/man3xcurses/mvwins_nwstr.3xcurses=ins_nwstr.3xcurses
+s usr/share/man/man3xcurses/mvwins_wch.3xcurses=ins_wch.3xcurses
+s usr/share/man/man3xcurses/mvwins_wstr.3xcurses=ins_nwstr.3xcurses
+s usr/share/man/man3xcurses/mvwinsch.3xcurses=insch.3xcurses
+s usr/share/man/man3xcurses/mvwinsnstr.3xcurses=insnstr.3xcurses
+s usr/share/man/man3xcurses/mvwinsstr.3xcurses=insnstr.3xcurses
+s usr/share/man/man3xcurses/mvwinstr.3xcurses=innstr.3xcurses
+s usr/share/man/man3xcurses/mvwinwstr.3xcurses=innwstr.3xcurses
+s usr/share/man/man3xcurses/mvwprintw.3xcurses=mvprintw.3xcurses
+s usr/share/man/man3xcurses/mvwscanw.3xcurses=mvscanw.3xcurses
+s usr/share/man/man3xcurses/mvwvline.3xcurses=hline.3xcurses
+s usr/share/man/man3xcurses/mvwvline_set.3xcurses=hline_set.3xcurses
+f usr/share/man/man3xcurses/napms.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/newpad.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/newterm.3xcurses=initscr.3xcurses
+s usr/share/man/man3xcurses/newwin.3xcurses=derwin.3xcurses
+f usr/share/man/man3xcurses/nl.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/nocbreak.3xcurses=cbreak.3xcurses
+f usr/share/man/man3xcurses/nodelay.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/noecho.3xcurses=echo.3xcurses
+s usr/share/man/man3xcurses/nonl.3xcurses=nl.3xcurses
+f usr/share/man/man3xcurses/noqiflush.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/noraw.3xcurses=cbreak.3xcurses
+f usr/share/man/man3xcurses/notimeout.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/overlay.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/overwrite.3xcurses=overlay.3xcurses
+s usr/share/man/man3xcurses/pair_content.3xcurses=can_change_color.3xcurses
+s usr/share/man/man3xcurses/pecho_wchar.3xcurses=pechochar.3xcurses
+f usr/share/man/man3xcurses/pechochar.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/pnoutrefresh.3xcurses=newpad.3xcurses
+s usr/share/man/man3xcurses/prefresh.3xcurses=newpad.3xcurses
+s usr/share/man/man3xcurses/printw.3xcurses=mvprintw.3xcurses
+f usr/share/man/man3xcurses/putp.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/putwin.3xcurses=getwin.3xcurses
+s usr/share/man/man3xcurses/qiflush.3xcurses=noqiflush.3xcurses
+s usr/share/man/man3xcurses/raw.3xcurses=cbreak.3xcurses
+f usr/share/man/man3xcurses/redrawwin.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/refresh.3xcurses=doupdate.3xcurses
+s usr/share/man/man3xcurses/reset_prog_mode.3xcurses=def_prog_mode.3xcurses
+s usr/share/man/man3xcurses/reset_shell_mode.3xcurses=def_prog_mode.3xcurses
+f usr/share/man/man3xcurses/resetty.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/restartterm.3xcurses=del_curterm.3xcurses
+f usr/share/man/man3xcurses/ripoffline.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/savetty.3xcurses=resetty.3xcurses
+s usr/share/man/man3xcurses/scanw.3xcurses=mvscanw.3xcurses
+f usr/share/man/man3xcurses/scr_dump.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/scr_init.3xcurses=scr_dump.3xcurses
+s usr/share/man/man3xcurses/scr_restore.3xcurses=scr_dump.3xcurses
+s usr/share/man/man3xcurses/scr_set.3xcurses=scr_dump.3xcurses
+f usr/share/man/man3xcurses/scrl.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/scroll.3xcurses=scrl.3xcurses
+s usr/share/man/man3xcurses/scrollok.3xcurses=clearok.3xcurses
+s usr/share/man/man3xcurses/set_curterm.3xcurses=del_curterm.3xcurses
+f usr/share/man/man3xcurses/set_term.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/setcchar.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/setscrreg.3xcurses=clearok.3xcurses
+s usr/share/man/man3xcurses/setupterm.3xcurses=del_curterm.3xcurses
+s usr/share/man/man3xcurses/slk_attr_off.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_attr_on.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_attr_set.3xcurses=slk_attroff.3xcurses
+f usr/share/man/man3xcurses/slk_attroff.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/slk_attron.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_attrset.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_clear.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_color.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_init.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_label.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_noutrefresh.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_refresh.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_restore.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_set.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_touch.3xcurses=slk_attroff.3xcurses
+s usr/share/man/man3xcurses/slk_wset.3xcurses=slk_attroff.3xcurses
+f usr/share/man/man3xcurses/standend.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/standout.3xcurses=standend.3xcurses
+s usr/share/man/man3xcurses/start_color.3xcurses=can_change_color.3xcurses
+f usr/share/man/man3xcurses/stdscr.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/subpad.3xcurses=newpad.3xcurses
+s usr/share/man/man3xcurses/subwin.3xcurses=derwin.3xcurses
+f usr/share/man/man3xcurses/syncok.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/term_attrs.3xcurses=termattrs.3xcurses
+f usr/share/man/man3xcurses/termattrs.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/termname.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/tgetent.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/tgetflag.3xcurses=tgetent.3xcurses
+s usr/share/man/man3xcurses/tgetnum.3xcurses=tgetent.3xcurses
+s usr/share/man/man3xcurses/tgetstr.3xcurses=tgetent.3xcurses
+s usr/share/man/man3xcurses/tgoto.3xcurses=tgetent.3xcurses
+f usr/share/man/man3xcurses/tigetflag.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/tigetnum.3xcurses=tigetflag.3xcurses
+s usr/share/man/man3xcurses/tigetstr.3xcurses=tigetflag.3xcurses
+s usr/share/man/man3xcurses/timeout.3xcurses=notimeout.3xcurses
+s usr/share/man/man3xcurses/touchline.3xcurses=is_linetouched.3xcurses
+s usr/share/man/man3xcurses/touchwin.3xcurses=is_linetouched.3xcurses
+s usr/share/man/man3xcurses/tparm.3xcurses=tigetflag.3xcurses
+s usr/share/man/man3xcurses/tputs.3xcurses=putp.3xcurses
+f usr/share/man/man3xcurses/typeahead.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/unctrl.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/unget_wch.3xcurses=ungetch.3xcurses
+f usr/share/man/man3xcurses/ungetch.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/untouchwin.3xcurses=is_linetouched.3xcurses
+f usr/share/man/man3xcurses/use_env.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/vid_attr.3xcurses=vidattr.3xcurses
+s usr/share/man/man3xcurses/vid_puts.3xcurses=vidattr.3xcurses
+f usr/share/man/man3xcurses/vidattr.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/vidputs.3xcurses=vidattr.3xcurses
+s usr/share/man/man3xcurses/vline.3xcurses=hline.3xcurses
+s usr/share/man/man3xcurses/vline_set.3xcurses=hline_set.3xcurses
+f usr/share/man/man3xcurses/vw_printw.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/vw_scanw.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/vwprintw.3xcurses 0444 root bin
+f usr/share/man/man3xcurses/vwscanw.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/wadd_wch.3xcurses=add_wch.3xcurses
+s usr/share/man/man3xcurses/wadd_wchnstr.3xcurses=add_wchnstr.3xcurses
+s usr/share/man/man3xcurses/wadd_wchstr.3xcurses=add_wchnstr.3xcurses
+s usr/share/man/man3xcurses/waddch.3xcurses=addch.3xcurses
+s usr/share/man/man3xcurses/waddchnstr.3xcurses=addchstr.3xcurses
+s usr/share/man/man3xcurses/waddchstr.3xcurses=addchstr.3xcurses
+s usr/share/man/man3xcurses/waddnstr.3xcurses=addnstr.3xcurses
+s usr/share/man/man3xcurses/waddnwstr.3xcurses=addnwstr.3xcurses
+s usr/share/man/man3xcurses/waddstr.3xcurses=addnstr.3xcurses
+s usr/share/man/man3xcurses/waddwstr.3xcurses=addnwstr.3xcurses
+s usr/share/man/man3xcurses/wattr_get.3xcurses=attr_get.3xcurses
+s usr/share/man/man3xcurses/wattr_off.3xcurses=attr_get.3xcurses
+s usr/share/man/man3xcurses/wattr_on.3xcurses=attr_get.3xcurses
+s usr/share/man/man3xcurses/wattr_set.3xcurses=attr_get.3xcurses
+s usr/share/man/man3xcurses/wattroff.3xcurses=attroff.3xcurses
+s usr/share/man/man3xcurses/wattron.3xcurses=attroff.3xcurses
+s usr/share/man/man3xcurses/wattrset.3xcurses=attroff.3xcurses
+s usr/share/man/man3xcurses/wbkgd.3xcurses=bkgd.3xcurses
+s usr/share/man/man3xcurses/wbkgdset.3xcurses=bkgd.3xcurses
+s usr/share/man/man3xcurses/wbkgrnd.3xcurses=bkgrnd.3xcurses
+s usr/share/man/man3xcurses/wbkgrndset.3xcurses=bkgrnd.3xcurses
+s usr/share/man/man3xcurses/wborder.3xcurses=border.3xcurses
+s usr/share/man/man3xcurses/wborder_set.3xcurses=border_set.3xcurses
+s usr/share/man/man3xcurses/wchgat.3xcurses=chgat.3xcurses
+s usr/share/man/man3xcurses/wclear.3xcurses=clear.3xcurses
+s usr/share/man/man3xcurses/wclrtobot.3xcurses=clrtobot.3xcurses
+s usr/share/man/man3xcurses/wclrtoeol.3xcurses=clrtoeol.3xcurses
+s usr/share/man/man3xcurses/wcolor_set.3xcurses=attr_get.3xcurses
+s usr/share/man/man3xcurses/wcursyncup.3xcurses=syncok.3xcurses
+s usr/share/man/man3xcurses/wdelch.3xcurses=delch.3xcurses
+s usr/share/man/man3xcurses/wdeleteln.3xcurses=deleteln.3xcurses
+s usr/share/man/man3xcurses/wecho_wchar.3xcurses=echo_wchar.3xcurses
+s usr/share/man/man3xcurses/wechochar.3xcurses=echochar.3xcurses
+s usr/share/man/man3xcurses/werase.3xcurses=clear.3xcurses
+s usr/share/man/man3xcurses/wget_wch.3xcurses=get_wch.3xcurses
+s usr/share/man/man3xcurses/wget_wstr.3xcurses=getn_wstr.3xcurses
+s usr/share/man/man3xcurses/wgetbkgrnd.3xcurses=bkgrnd.3xcurses
+s usr/share/man/man3xcurses/wgetch.3xcurses=getch.3xcurses
+s usr/share/man/man3xcurses/wgetn_wstr.3xcurses=getn_wstr.3xcurses
+s usr/share/man/man3xcurses/wgetnstr.3xcurses=getnstr.3xcurses
+s usr/share/man/man3xcurses/wgetstr.3xcurses=getnstr.3xcurses
+s usr/share/man/man3xcurses/whline.3xcurses=hline.3xcurses
+s usr/share/man/man3xcurses/whline_set.3xcurses=hline_set.3xcurses
+s usr/share/man/man3xcurses/win_wch.3xcurses=in_wch.3xcurses
+s usr/share/man/man3xcurses/win_wchnstr.3xcurses=in_wchnstr.3xcurses
+s usr/share/man/man3xcurses/win_wchstr.3xcurses=in_wchnstr.3xcurses
+s usr/share/man/man3xcurses/winch.3xcurses=inch.3xcurses
+s usr/share/man/man3xcurses/winchnstr.3xcurses=inchnstr.3xcurses
+s usr/share/man/man3xcurses/winchstr.3xcurses=inchnstr.3xcurses
+s usr/share/man/man3xcurses/winnstr.3xcurses=innstr.3xcurses
+s usr/share/man/man3xcurses/winnwstr.3xcurses=innwstr.3xcurses
+s usr/share/man/man3xcurses/wins_nwstr.3xcurses=ins_nwstr.3xcurses
+s usr/share/man/man3xcurses/wins_wch.3xcurses=ins_wch.3xcurses
+s usr/share/man/man3xcurses/wins_wstr.3xcurses=ins_nwstr.3xcurses
+s usr/share/man/man3xcurses/winsch.3xcurses=insch.3xcurses
+s usr/share/man/man3xcurses/winsdelln.3xcurses=insdelln.3xcurses
+s usr/share/man/man3xcurses/winsertln.3xcurses=insertln.3xcurses
+s usr/share/man/man3xcurses/winsnstr.3xcurses=insnstr.3xcurses
+s usr/share/man/man3xcurses/winsstr.3xcurses=insnstr.3xcurses
+s usr/share/man/man3xcurses/winstr.3xcurses=innstr.3xcurses
+s usr/share/man/man3xcurses/winwstr.3xcurses=innwstr.3xcurses
+s usr/share/man/man3xcurses/wmove.3xcurses=move.3xcurses
+s usr/share/man/man3xcurses/wnoutrefresh.3xcurses=doupdate.3xcurses
+s usr/share/man/man3xcurses/wprintw.3xcurses=mvprintw.3xcurses
+s usr/share/man/man3xcurses/wredrawln.3xcurses=redrawwin.3xcurses
+s usr/share/man/man3xcurses/wrefresh.3xcurses=doupdate.3xcurses
+s usr/share/man/man3xcurses/wscanw.3xcurses=mvscanw.3xcurses
+s usr/share/man/man3xcurses/wscrl.3xcurses=scrl.3xcurses
+s usr/share/man/man3xcurses/wsetscrreg.3xcurses=clearok.3xcurses
+s usr/share/man/man3xcurses/wstandend.3xcurses=standend.3xcurses
+s usr/share/man/man3xcurses/wstandout.3xcurses=standend.3xcurses
+s usr/share/man/man3xcurses/wsyncdown.3xcurses=syncok.3xcurses
+s usr/share/man/man3xcurses/wsyncup.3xcurses=syncok.3xcurses
+s usr/share/man/man3xcurses/wtimeout.3xcurses=notimeout.3xcurses
+s usr/share/man/man3xcurses/wtouchln.3xcurses=is_linetouched.3xcurses
+f usr/share/man/man3xcurses/wunctrl.3xcurses 0444 root bin
+s usr/share/man/man3xcurses/wvline.3xcurses=hline.3xcurses
+s usr/share/man/man3xcurses/wvline_set.3xcurses=hline_set.3xcurses
+d usr/share/man/man3xext 0755 root bin
+d usr/share/man/man3xmu 0755 root bin
+d usr/share/man/man3xnet 0755 root bin
+f usr/share/man/man3xnet/accept.3xnet 0444 root bin
+f usr/share/man/man3xnet/bind.3xnet 0444 root bin
+f usr/share/man/man3xnet/connect.3xnet 0444 root bin
+f usr/share/man/man3xnet/endhostent.3xnet 0444 root bin
+f usr/share/man/man3xnet/endnetent.3xnet 0444 root bin
+f usr/share/man/man3xnet/endprotoent.3xnet 0444 root bin
+f usr/share/man/man3xnet/endservent.3xnet 0444 root bin
+f usr/share/man/man3xnet/freeaddrinfo.3xnet 0444 root bin
+f usr/share/man/man3xnet/gai_strerror.3xnet 0444 root bin
+s usr/share/man/man3xnet/getaddrinfo.3xnet=freeaddrinfo.3xnet
+s usr/share/man/man3xnet/gethostbyaddr.3xnet=endhostent.3xnet
+s usr/share/man/man3xnet/gethostbyname.3xnet=endhostent.3xnet
+s usr/share/man/man3xnet/gethostent.3xnet=endhostent.3xnet
+f usr/share/man/man3xnet/gethostname.3xnet 0444 root bin
+f usr/share/man/man3xnet/getnameinfo.3xnet 0444 root bin
+s usr/share/man/man3xnet/getnetbyaddr.3xnet=endnetent.3xnet
+s usr/share/man/man3xnet/getnetbyname.3xnet=endnetent.3xnet
+s usr/share/man/man3xnet/getnetent.3xnet=endnetent.3xnet
+f usr/share/man/man3xnet/getpeername.3xnet 0444 root bin
+s usr/share/man/man3xnet/getprotobyname.3xnet=endprotoent.3xnet
+s usr/share/man/man3xnet/getprotobynumber.3xnet=endprotoent.3xnet
+s usr/share/man/man3xnet/getprotoent.3xnet=endprotoent.3xnet
+s usr/share/man/man3xnet/getservbyname.3xnet=endservent.3xnet
+s usr/share/man/man3xnet/getservbyport.3xnet=endservent.3xnet
+s usr/share/man/man3xnet/getservent.3xnet=endservent.3xnet
+f usr/share/man/man3xnet/getsockname.3xnet 0444 root bin
+f usr/share/man/man3xnet/getsockopt.3xnet 0444 root bin
+f usr/share/man/man3xnet/htonl.3xnet 0444 root bin
+s usr/share/man/man3xnet/htonll.3xnet=htonl.3xnet
+s usr/share/man/man3xnet/htons.3xnet=htonl.3xnet
+s usr/share/man/man3xnet/if_freenameindex.3xnet=if_nametoindex.3xnet
+s usr/share/man/man3xnet/if_indextoname.3xnet=if_nametoindex.3xnet
+s usr/share/man/man3xnet/if_nameindex.3xnet=if_nametoindex.3xnet
+f usr/share/man/man3xnet/if_nametoindex.3xnet 0444 root bin
+f usr/share/man/man3xnet/inet_addr.3xnet 0444 root bin
+s usr/share/man/man3xnet/inet_lnaof.3xnet=inet_addr.3xnet
+s usr/share/man/man3xnet/inet_makeaddr.3xnet=inet_addr.3xnet
+s usr/share/man/man3xnet/inet_netof.3xnet=inet_addr.3xnet
+s usr/share/man/man3xnet/inet_network.3xnet=inet_addr.3xnet
+s usr/share/man/man3xnet/inet_ntoa.3xnet=inet_addr.3xnet
+f usr/share/man/man3xnet/inet_ntop.3xnet 0444 root bin
+s usr/share/man/man3xnet/inet_pton.3xnet=inet_ntop.3xnet
+f usr/share/man/man3xnet/listen.3xnet 0444 root bin
+s usr/share/man/man3xnet/ntohl.3xnet=htonl.3xnet
+s usr/share/man/man3xnet/ntohll.3xnet=htonl.3xnet
+s usr/share/man/man3xnet/ntohs.3xnet=htonl.3xnet
+f usr/share/man/man3xnet/recv.3xnet 0444 root bin
+f usr/share/man/man3xnet/recvfrom.3xnet 0444 root bin
+f usr/share/man/man3xnet/recvmsg.3xnet 0444 root bin
+f usr/share/man/man3xnet/send.3xnet 0444 root bin
+f usr/share/man/man3xnet/sendmsg.3xnet 0444 root bin
+f usr/share/man/man3xnet/sendto.3xnet 0444 root bin
+s usr/share/man/man3xnet/sethostent.3xnet=endhostent.3xnet
+s usr/share/man/man3xnet/setnetent.3xnet=endnetent.3xnet
+s usr/share/man/man3xnet/setprotoent.3xnet=endprotoent.3xnet
+s usr/share/man/man3xnet/setservent.3xnet=endservent.3xnet
+f usr/share/man/man3xnet/setsockopt.3xnet 0444 root bin
+f usr/share/man/man3xnet/shutdown.3xnet 0444 root bin
+f usr/share/man/man3xnet/sockatmark.3xnet 0444 root bin
+f usr/share/man/man3xnet/socket.3xnet 0444 root bin
+f usr/share/man/man3xnet/socketpair.3xnet 0444 root bin
+d usr/share/man/man4 0755 root bin
+f usr/share/man/man4/FSS.4 0444 root bin
+f usr/share/man/man4/Intro.4 0444 root bin
+f usr/share/man/man4/cpr.4 0444 root bin
+s usr/share/man/man4/intro.4=Intro.4
+f usr/share/man/man4/swap.4 0444 root bin
+d usr/share/man/man4d 0755 root bin
+s usr/share/man/man4d/1394.4d=ieee1394.4d
+f usr/share/man/man4d/aac.4d 0444 root bin
+f usr/share/man/man4d/afe.4d 0444 root bin
+f usr/share/man/man4d/ahci.4d 0444 root bin
+s usr/share/man/man4d/allkmem.4d=mem.4d
+f usr/share/man/man4d/amd8111s.4d 0444 root bin
+f usr/share/man/man4d/amdnbtemp.4d 0444 root bin
+f usr/share/man/man4d/amdzen.4d 0444 root bin
+s usr/share/man/man4d/amdzen_stub.4d=amdzen.4d
+f usr/share/man/man4d/amr.4d 0444 root bin
+f usr/share/man/man4d/arcmsr.4d 0444 root bin
+f usr/share/man/man4d/asy.4d 0444 root bin
+f usr/share/man/man4d/ata.4d 0444 root bin
+f usr/share/man/man4d/atge.4d 0444 root bin
+f usr/share/man/man4d/bcm_sata.4d 0444 root bin
+f usr/share/man/man4d/bfe.4d 0444 root bin
+f usr/share/man/man4d/bge.4d 0444 root bin
+f usr/share/man/man4d/blkdev.4d 0444 root bin
+f usr/share/man/man4d/bnxe.4d 0444 root bin
+f usr/share/man/man4d/ccid.4d 0444 root bin
+f usr/share/man/man4d/chxge.4d 0444 root bin
+f usr/share/man/man4d/cmdk.4d 0444 root bin
+f usr/share/man/man4d/coretemp.4d 0444 root bin
+f usr/share/man/man4d/cpqary3.4d 0444 root bin
+f usr/share/man/man4d/cpuid.4d 0444 root bin
+f usr/share/man/man4d/devinfo.4d 0444 root bin
+f usr/share/man/man4d/dmfe.4d 0444 root bin
+f usr/share/man/man4d/dnet.4d 0444 root bin
+f usr/share/man/man4d/dtrace.4d 0444 root bin
+f usr/share/man/man4d/e1000g.4d 0444 root bin
+f usr/share/man/man4d/ehci.4d 0444 root bin
+f usr/share/man/man4d/ena.4d 0444 root bin
+f usr/share/man/man4d/fasttrap.4d 0444 root bin
+f usr/share/man/man4d/fbt.4d 0444 root bin
+f usr/share/man/man4d/fcp.4d 0444 root bin
+f usr/share/man/man4d/fctl.4d 0444 root bin
+f usr/share/man/man4d/fd.4d 0444 root bin
+s usr/share/man/man4d/fdc.4d=fd.4d
+s usr/share/man/man4d/firewire.4d=ieee1394.4d
+f usr/share/man/man4d/fp.4d 0444 root bin
+f usr/share/man/man4d/full.4d 0444 root bin
+f usr/share/man/man4d/gld.4d 0444 root bin
+f usr/share/man/man4d/hci1394.4d 0444 root bin
+f usr/share/man/man4d/hid.4d 0444 root bin
+f usr/share/man/man4d/hubd.4d 0444 root bin
+f usr/share/man/man4d/hxge.4d 0444 root bin
+f usr/share/man/man4d/i40e.4d 0444 root bin
+f usr/share/man/man4d/ieee1394.4d 0444 root bin
+f usr/share/man/man4d/igb.4d 0444 root bin
+f usr/share/man/man4d/imc.4d 0444 root bin
+f usr/share/man/man4d/imcstub.4d 0444 root bin
+f usr/share/man/man4d/ipmi.4d 0444 root bin
+f usr/share/man/man4d/ipnet.4d 0444 root bin
+f usr/share/man/man4d/iprb.4d 0444 root bin
+f usr/share/man/man4d/iscsi.4d 0444 root bin
+f usr/share/man/man4d/iwn.4d 0444 root bin
+f usr/share/man/man4d/ixgbe.4d 0444 root bin
+f usr/share/man/man4d/kmdb.4d 0444 root bin
+s usr/share/man/man4d/kmem.4d=mem.4d
+f usr/share/man/man4d/ksensor.4d 0444 root bin
+f usr/share/man/man4d/kstat.4d 0444 root bin
+f usr/share/man/man4d/ksyms.4d 0444 root bin
+f usr/share/man/man4d/llc1.4d 0444 root bin
+s usr/share/man/man4d/lo0.4d=ipnet.4d
+f usr/share/man/man4d/lockstat.4d 0444 root bin
+f usr/share/man/man4d/lofi.4d 0444 root bin
+f usr/share/man/man4d/log.4d 0444 root bin
+f usr/share/man/man4d/mega_sas.4d 0444 root bin
+f usr/share/man/man4d/mem.4d 0444 root bin
+f usr/share/man/man4d/mlxcx.4d 0444 root bin
+f usr/share/man/man4d/mpt_sas.4d 0444 root bin
+f usr/share/man/man4d/mr_sas.4d 0444 root bin
+f usr/share/man/man4d/msglog.4d 0444 root bin
+f usr/share/man/man4d/mt.4d 0444 root bin
+f usr/share/man/man4d/mxfe.4d 0444 root bin
+f usr/share/man/man4d/myri10ge.4d 0444 root bin
+f usr/share/man/man4d/npe.4d 0444 root bin
+f usr/share/man/man4d/ntxn.4d 0444 root bin
+f usr/share/man/man4d/null.4d 0444 root bin
+f usr/share/man/man4d/nulldriver.4d 0444 root bin
+f usr/share/man/man4d/nv_sata.4d 0444 root bin
+f usr/share/man/man4d/nvme.4d 0444 root bin
+f usr/share/man/man4d/nxge.4d 0444 root bin
+f usr/share/man/man4d/ohci.4d 0444 root bin
+f usr/share/man/man4d/openprom.4d 0444 root bin
+f usr/share/man/man4d/pchtemp.4d 0444 root bin
+f usr/share/man/man4d/pcn.4d 0444 root bin
+f usr/share/man/man4d/physmem.4d 0444 root bin
+f usr/share/man/man4d/pm.4d 0444 root bin
+f usr/share/man/man4d/poll.4d 0444 root bin
+f usr/share/man/man4d/profile.4d 0444 root bin
+f usr/share/man/man4d/ptm.4d 0444 root bin
+s usr/share/man/man4d/pts.4d=ptm.4d
+f usr/share/man/man4d/pty.4d 0444 root bin
+f usr/share/man/man4d/qede.4d 0444 root bin
+f usr/share/man/man4d/qlc.4d 0444 root bin
+f usr/share/man/man4d/ramdisk.4d 0444 root bin
+f usr/share/man/man4d/random.4d 0444 root bin
+f usr/share/man/man4d/rge.4d 0444 root bin
+f usr/share/man/man4d/rtls.4d 0444 root bin
+f usr/share/man/man4d/sad.4d 0444 root bin
+f usr/share/man/man4d/sata.4d 0444 root bin
+f usr/share/man/man4d/scsa1394.4d 0444 root bin
+f usr/share/man/man4d/scsa2usb.4d 0444 root bin
+f usr/share/man/man4d/sd.4d 0444 root bin
+f usr/share/man/man4d/sda.4d 0444 root bin
+f usr/share/man/man4d/sdhost.4d 0444 root bin
+f usr/share/man/man4d/sdt.4d 0444 root bin
+f usr/share/man/man4d/ses.4d 0444 root bin
+f usr/share/man/man4d/sfe.4d 0444 root bin
+f usr/share/man/man4d/sfxge.4d 0444 root bin
+f usr/share/man/man4d/sgen.4d 0444 root bin
+f usr/share/man/man4d/si3124.4d 0444 root bin
+f usr/share/man/man4d/skd.4d 0444 root bin
+f usr/share/man/man4d/smbios.4d 0444 root bin
+f usr/share/man/man4d/smntemp.4d 0444 root bin
+f usr/share/man/man4d/st.4d 0444 root bin
+f usr/share/man/man4d/sysmsg.4d 0444 root bin
+f usr/share/man/man4d/systrace.4d 0444 root bin
+f usr/share/man/man4d/ticlts.4d 0444 root bin
+s usr/share/man/man4d/ticots.4d=ticlts.4d
+s usr/share/man/man4d/ticotsord.4d=ticlts.4d
+f usr/share/man/man4d/tty.4d 0444 root bin
+f usr/share/man/man4d/tzmon.4d 0444 root bin
+f usr/share/man/man4d/ufm.4d 0444 root bin
+f usr/share/man/man4d/ugen.4d 0444 root bin
+f usr/share/man/man4d/uhci.4d 0444 root bin
+s usr/share/man/man4d/urandom.4d=random.4d
+s usr/share/man/man4d/usb.4d=usba.4d
+f usr/share/man/man4d/usb_ac.4d 0444 root bin
+f usr/share/man/man4d/usb_as.4d 0444 root bin
+f usr/share/man/man4d/usb_ia.4d 0444 root bin
+f usr/share/man/man4d/usb_mid.4d 0444 root bin
+f usr/share/man/man4d/usba.4d 0444 root bin
+f usr/share/man/man4d/usbftdi.4d 0444 root bin
+f usr/share/man/man4d/usbsacm.4d 0444 root bin
+f usr/share/man/man4d/usbsksp.4d 0444 root bin
+f usr/share/man/man4d/usbsprl.4d 0444 root bin
+f usr/share/man/man4d/vio9p.4d 0444 root bin
+f usr/share/man/man4d/vioblk.4d 0444 root bin
+f usr/share/man/man4d/vioif.4d 0444 root bin
+f usr/share/man/man4d/vioscsi.4d 0444 root bin
+f usr/share/man/man4d/virtio.4d 0444 root bin
+f usr/share/man/man4d/virtualkm.4d 0444 root bin
+f usr/share/man/man4d/vnd.4d 0444 root bin
+f usr/share/man/man4d/vni.4d 0444 root bin
+f usr/share/man/man4d/vr.4d 0444 root bin
+f usr/share/man/man4d/wscons.4d 0444 root bin
+f usr/share/man/man4d/xge.4d 0444 root bin
+f usr/share/man/man4d/xhci.4d 0444 root bin
+f usr/share/man/man4d/yge.4d 0444 root bin
+f usr/share/man/man4d/zcons.4d 0444 root bin
+f usr/share/man/man4d/zero.4d 0444 root bin
+f usr/share/man/man4d/zfd.4d 0444 root bin
+d usr/share/man/man4fs 0755 root bin
+f usr/share/man/man4fs/bootfs.4fs 0444 root bin
+f usr/share/man/man4fs/ctfs.4fs 0444 root bin
+f usr/share/man/man4fs/dcfs.4fs 0444 root bin
+f usr/share/man/man4fs/dev.4fs 0444 root bin
+f usr/share/man/man4fs/devfs.4fs 0444 root bin
+f usr/share/man/man4fs/fd.4fs 0444 root bin
+f usr/share/man/man4fs/hsfs.4fs 0444 root bin
+f usr/share/man/man4fs/hyprlofs.4fs 0444 root bin
+f usr/share/man/man4fs/lofs.4fs 0444 root bin
+f usr/share/man/man4fs/lxproc.4fs 0444 root bin
+f usr/share/man/man4fs/objfs.4fs 0444 root bin
+f usr/share/man/man4fs/pcfs.4fs 0444 root bin
+f usr/share/man/man4fs/sharefs.4fs 0444 root bin
+f usr/share/man/man4fs/smbfs.4fs 0444 root bin
+s usr/share/man/man4fs/stderr.4fs=fd.4fs
+s usr/share/man/man4fs/stdin.4fs=fd.4fs
+s usr/share/man/man4fs/stdout.4fs=fd.4fs
+f usr/share/man/man4fs/tmpfs.4fs 0444 root bin
+f usr/share/man/man4fs/udfs.4fs 0444 root bin
+f usr/share/man/man4fs/ufs.4fs 0444 root bin
+d usr/share/man/man4i 0755 root bin
+f usr/share/man/man4i/cdio.4i 0444 root bin
+f usr/share/man/man4i/dkio.4i 0444 root bin
+f usr/share/man/man4i/fbio.4i 0444 root bin
+f usr/share/man/man4i/fdio.4i 0444 root bin
+f usr/share/man/man4i/hdio.4i 0444 root bin
+f usr/share/man/man4i/iec61883.4i 0444 root bin
+f usr/share/man/man4i/ipnat.4i 0444 root bin
+f usr/share/man/man4i/mhd.4i 0444 root bin
+f usr/share/man/man4i/mtio.4i 0444 root bin
+f usr/share/man/man4i/prnio.4i 0444 root bin
+f usr/share/man/man4i/quotactl.4i 0444 root bin
+f usr/share/man/man4i/sesio.4i 0444 root bin
+f usr/share/man/man4i/sockio.4i 0444 root bin
+f usr/share/man/man4i/streamio.4i 0444 root bin
+f usr/share/man/man4i/termio.4i 0444 root bin
+f usr/share/man/man4i/termiox.4i 0444 root bin
+s usr/share/man/man4i/uccid.4i=../man4d/ccid.4d
+f usr/share/man/man4i/uscsi.4i 0444 root bin
+f usr/share/man/man4i/visual_io.4i 0444 root bin
+f usr/share/man/man4i/vt.4i 0444 root bin
+d usr/share/man/man4ipp 0755 root bin
+f usr/share/man/man4ipp/dlcosmk.4ipp 0444 root bin
+f usr/share/man/man4ipp/dscpmk.4ipp 0444 root bin
+f usr/share/man/man4ipp/flowacct.4ipp 0444 root bin
+f usr/share/man/man4ipp/ipgpc.4ipp 0444 root bin
+f usr/share/man/man4ipp/ipqos.4ipp 0444 root bin
+f usr/share/man/man4ipp/tokenmt.4ipp 0444 root bin
+f usr/share/man/man4ipp/tswtclmt.4ipp 0444 root bin
+d usr/share/man/man4m 0755 root bin
+f usr/share/man/man4m/bufmod.4m 0444 root bin
+f usr/share/man/man4m/connld.4m 0444 root bin
+f usr/share/man/man4m/datafilt.4m 0444 root bin
+f usr/share/man/man4m/ldterm.4m 0444 root bin
+f usr/share/man/man4m/pckt.4m 0444 root bin
+f usr/share/man/man4m/pfmod.4m 0444 root bin
+f usr/share/man/man4m/pipemod.4m 0444 root bin
+f usr/share/man/man4m/ptem.4m 0444 root bin
+f usr/share/man/man4m/sppptun.4m 0444 root bin
+f usr/share/man/man4m/timod.4m 0444 root bin
+f usr/share/man/man4m/tirdwr.4m 0444 root bin
+f usr/share/man/man4m/ttcompat.4m 0444 root bin
+f usr/share/man/man4m/usb_ah.4m 0444 root bin
+f usr/share/man/man4m/usbkbm.4m 0444 root bin
+f usr/share/man/man4m/usbms.4m 0444 root bin
+s usr/share/man/man4m/vuid2ps2.4m=vuidmice.4m
+s usr/share/man/man4m/vuid3ps2.4m=vuidmice.4m
+s usr/share/man/man4m/vuidm3p.4m=vuidmice.4m
+s usr/share/man/man4m/vuidm4p.4m=vuidmice.4m
+s usr/share/man/man4m/vuidm5p.4m=vuidmice.4m
+f usr/share/man/man4m/vuidmice.4m 0444 root bin
+d usr/share/man/man4p 0755 root bin
+s usr/share/man/man4p/AH.4p=ipsecah.4p
+s usr/share/man/man4p/ARP.4p=arp.4p
+s usr/share/man/man4p/ESP.4p=ipsecesp.4p
+s usr/share/man/man4p/ICMP.4p=icmp.4p
+s usr/share/man/man4p/IP.4p=ip.4p
+s usr/share/man/man4p/NDP.4p=ndp.4p
+s usr/share/man/man4p/RARP.4p=rarp.4p
+s usr/share/man/man4p/SCTP.4p=sctp.4p
+s usr/share/man/man4p/TCP.4p=tcp.4p
+s usr/share/man/man4p/UDP.4p=udp.4p
+s usr/share/man/man4p/VXLAN.4p=vxlan.4p
+f usr/share/man/man4p/arp.4p 0444 root bin
+f usr/share/man/man4p/dlpi.4p 0444 root bin
+f usr/share/man/man4p/icmp.4p 0444 root bin
+f usr/share/man/man4p/icmp6.4p 0444 root bin
+s usr/share/man/man4p/if.4p=if_tcp.4p
+f usr/share/man/man4p/if_tcp.4p 0444 root bin
+f usr/share/man/man4p/inet.4p 0444 root bin
+f usr/share/man/man4p/inet6.4p 0444 root bin
+f usr/share/man/man4p/ip.4p 0444 root bin
+f usr/share/man/man4p/ip6.4p 0444 root bin
+f usr/share/man/man4p/ipsec.4p 0444 root bin
+f usr/share/man/man4p/ipsecah.4p 0444 root bin
+f usr/share/man/man4p/ipsecesp.4p 0444 root bin
+f usr/share/man/man4p/ndp.4p 0444 root bin
+f usr/share/man/man4p/pf_key.4p 0444 root bin
+f usr/share/man/man4p/rarp.4p 0444 root bin
+f usr/share/man/man4p/route.4p 0444 root bin
+f usr/share/man/man4p/routing.4p 0444 root bin
+f usr/share/man/man4p/sctp.4p 0444 root bin
+f usr/share/man/man4p/sip.4p 0444 root bin
+f usr/share/man/man4p/slp.4p 0444 root bin
+f usr/share/man/man4p/tcp.4p 0444 root bin
+f usr/share/man/man4p/udp.4p 0444 root bin
+f usr/share/man/man4p/vxlan.4p 0444 root bin
+d usr/share/man/man5 0755 root bin
+f usr/share/man/man5/Intro.5 0444 root bin
+f usr/share/man/man5/NISLDAPmapping.5 0444 root bin
+s usr/share/man/man5/TIMEZONE.5=init.5
+f usr/share/man/man5/a.out.5 0444 root bin
+s usr/share/man/man5/addresses.5=aliases.5
+f usr/share/man/man5/admin.5 0444 root bin
+f usr/share/man/man5/alias.5 0444 root bin
+f usr/share/man/man5/aliases.5 0444 root bin
+f usr/share/man/man5/au.5 0444 root bin
+f usr/share/man/man5/audit.log.5 0444 root bin
+f usr/share/man/man5/audit_class.5 0444 root bin
+f usr/share/man/man5/audit_event.5 0444 root bin
+f usr/share/man/man5/auth_attr.5 0444 root bin
+f usr/share/man/man5/autofs.5 0444 root bin
+f usr/share/man/man5/bart_manifest.5 0444 root bin
+f usr/share/man/man5/bart_rules.5 0444 root bin
+f usr/share/man/man5/bhyve_config.5 0444 root bin
+f usr/share/man/man5/bootparams.5 0444 root bin
+f usr/share/man/man5/cardbus.5 0444 root bin
+f usr/share/man/man5/compver.5 0444 root bin
+f usr/share/man/man5/contents.5 0444 root bin
+f usr/share/man/man5/contract.5 0444 root bin
+f usr/share/man/man5/copyright.5 0444 root bin
+f usr/share/man/man5/core.5 0444 root bin
+f usr/share/man/man5/crypt.conf.5 0444 root bin
+f usr/share/man/man5/crypto_certs.5 0444 root bin
+f usr/share/man/man5/ctf.5 0444 root bin
+f usr/share/man/man5/d_passwd.5 0444 root bin
+f usr/share/man/man5/dacf.conf.5 0444 root bin
+f usr/share/man/man5/dat.conf.5 0444 root bin
+f usr/share/man/man5/default_fs.5 0444 root bin
+f usr/share/man/man5/defaultdomain.5 0444 root bin
+f usr/share/man/man5/defaultrouter.5 0444 root bin
+f usr/share/man/man5/depend.5 0444 root bin
+f usr/share/man/man5/device_allocate.5 0444 root bin
+f usr/share/man/man5/device_contract.5 0444 root bin
+f usr/share/man/man5/device_maps.5 0444 root bin
+f usr/share/man/man5/devices.5 0444 root bin
+s usr/share/man/man5/devid_cache.5=devices.5
+s usr/share/man/man5/devname_cache.5=devices.5
+f usr/share/man/man5/dfstab.5 0444 root bin
+f usr/share/man/man5/dhcp_inittab.5 0444 root bin
+f usr/share/man/man5/dialups.5 0444 root bin
+s usr/share/man/man5/dir.5=dir_ufs.5
+f usr/share/man/man5/dir_ufs.5 0444 root bin
+f usr/share/man/man5/driver.conf.5 0444 root bin
+s usr/share/man/man5/dumpdates.5=ufsdump.5
+f usr/share/man/man5/ethers.5 0444 root bin
+f usr/share/man/man5/exec_attr.5 0444 root bin
+s usr/share/man/man5/fbtab.5=logindevperm.5
+f usr/share/man/man5/fdi.5 0444 root bin
+f usr/share/man/man5/format.dat.5 0444 root bin
+s usr/share/man/man5/forward.5=aliases.5
+s usr/share/man/man5/fs.5=default_fs.5
+f usr/share/man/man5/fspec.5 0444 root bin
+f usr/share/man/man5/fstypes.5 0444 root bin
+f usr/share/man/man5/ftp.5 0444 root bin
+f usr/share/man/man5/ftpusers.5 0444 root bin
+f usr/share/man/man5/fx_dptbl.5 0444 root bin
+f usr/share/man/man5/gateways.5 0444 root bin
+f usr/share/man/man5/group.5 0444 root bin
+f usr/share/man/man5/gsscred.conf.5 0444 root bin
+f usr/share/man/man5/hba.conf.5 0444 root bin
+f usr/share/man/man5/holidays.5 0444 root bin
+f usr/share/man/man5/hosts.5 0444 root bin
+s usr/share/man/man5/hosts.allow.5=hosts_access.5
+s usr/share/man/man5/hosts.deny.5=hosts_access.5
+f usr/share/man/man5/hosts.equiv.5 0444 root bin
+f usr/share/man/man5/hosts_access.5 0444 root bin
+f usr/share/man/man5/hosts_options.5 0444 root bin
+f usr/share/man/man5/ib.5 0444 root bin
+f usr/share/man/man5/ike.config.5 0444 root bin
+f usr/share/man/man5/ike.preshared.5 0444 root bin
+f usr/share/man/man5/inet_type.5 0444 root bin
+f usr/share/man/man5/inetd.conf.5 0444 root bin
+f usr/share/man/man5/init.5 0444 root bin
+f usr/share/man/man5/init.d.5 0444 root bin
+f usr/share/man/man5/inittab.5 0444 root bin
+s usr/share/man/man5/intro.5=Intro.5
+f usr/share/man/man5/ipaddrsel.conf.5 0444 root bin
+f usr/share/man/man5/ipf.5 0444 root bin
+s usr/share/man/man5/ipf.conf.5=ipf.5
+s usr/share/man/man5/ipf6.conf.5=ipf.5
+f usr/share/man/man5/ipmon.5 0444 root bin
+s usr/share/man/man5/ipmon.conf.5=ipmon.5
+f usr/share/man/man5/ipnat.5 0444 root bin
+s usr/share/man/man5/ipnat.conf.5=ipnat.5
+f usr/share/man/man5/ipnodes.5 0444 root bin
+f usr/share/man/man5/ippool.5 0444 root bin
+s usr/share/man/man5/ippool.conf.5=ippool.5
+s usr/share/man/man5/isa.5=sysbus.5
+f usr/share/man/man5/issue.5 0444 root bin
+f usr/share/man/man5/kadm5.acl.5 0444 root bin
+f usr/share/man/man5/kdc.conf.5 0444 root bin
+f usr/share/man/man5/keytables.5 0444 root bin
+f usr/share/man/man5/krb5.conf.5 0444 root bin
+f usr/share/man/man5/ldapfilter.conf.5 0444 root bin
+f usr/share/man/man5/ldapsearchprefs.conf.5 0444 root bin
+f usr/share/man/man5/ldaptemplates.conf.5 0444 root bin
+f usr/share/man/man5/loader.conf.5 0444 root bin
+f usr/share/man/man5/logadm.conf.5 0444 root bin
+f usr/share/man/man5/logindevperm.5 0444 root bin
+f usr/share/man/man5/loginlog.5 0444 root bin
+f usr/share/man/man5/magic.5 0444 root bin
+f usr/share/man/man5/mailer.conf.5 0444 root bin
+s usr/share/man/man5/mdi_ib_cache.5=devices.5
+s usr/share/man/man5/mdi_scsi_vhci_cache.5=devices.5
+f usr/share/man/man5/mech.5 0444 root bin
+f usr/share/man/man5/mnttab.5 0444 root bin
+f usr/share/man/man5/mpapi.conf.5 0444 root bin
+f usr/share/man/man5/ndmp.5 0444 root bin
+f usr/share/man/man5/ndpd.conf.5 0444 root bin
+f usr/share/man/man5/netconfig.5 0444 root bin
+f usr/share/man/man5/netgroup.5 0444 root bin
+f usr/share/man/man5/netid.5 0444 root bin
+f usr/share/man/man5/netmasks.5 0444 root bin
+f usr/share/man/man5/netrc.5 0444 root bin
+f usr/share/man/man5/networks.5 0444 root bin
+f usr/share/man/man5/nfs.5 0444 root bin
+f usr/share/man/man5/nfslog.conf.5 0444 root bin
+f usr/share/man/man5/nfssec.conf.5 0444 root bin
+f usr/share/man/man5/nodename.5 0444 root bin
+f usr/share/man/man5/nologin.5 0444 root bin
+f usr/share/man/man5/note.5 0444 root bin
+f usr/share/man/man5/notrouter.5 0444 root bin
+f usr/share/man/man5/nscd.conf.5 0444 root bin
+f usr/share/man/man5/nsmbrc.5 0444 root bin
+f usr/share/man/man5/nss.5 0444 root bin
+f usr/share/man/man5/nsswitch.conf.5 0444 root bin
+f usr/share/man/man5/overlay_files.5 0444 root bin
+f usr/share/man/man5/packingrules.5 0444 root bin
+f usr/share/man/man5/pam.conf.5 0444 root bin
+f usr/share/man/man5/passwd.5 0444 root bin
+f usr/share/man/man5/path_to_inst.5 0444 root bin
+f usr/share/man/man5/pci.5 0444 root bin
+s usr/share/man/man5/pci_unitaddr_persistent.5=devices.5
+s usr/share/man/man5/pcie.5=pci.5
+f usr/share/man/man5/phones.5 0444 root bin
+f usr/share/man/man5/pkginfo.5 0444 root bin
+f usr/share/man/man5/pkgmap.5 0444 root bin
+f usr/share/man/man5/policy.conf.5 0444 root bin
+f usr/share/man/man5/power.conf.5 0444 root bin
+f usr/share/man/man5/printers.5 0444 root bin
+f usr/share/man/man5/printers.conf.5 0444 root bin
+f usr/share/man/man5/priv_names.5 0444 root bin
+f usr/share/man/man5/proc.5 0444 root bin
+f usr/share/man/man5/process.5 0444 root bin
+f usr/share/man/man5/prof_attr.5 0444 root bin
+f usr/share/man/man5/profile.5 0444 root bin
+f usr/share/man/man5/project.5 0444 root bin
+f usr/share/man/man5/protocols.5 0444 root bin
+f usr/share/man/man5/prototype.5 0444 root bin
+f usr/share/man/man5/pseudo.5 0444 root bin
+f usr/share/man/man5/publickey.5 0444 root bin
+s usr/share/man/man5/qop.5=mech.5
+f usr/share/man/man5/queuedefs.5 0444 root bin
+f usr/share/man/man5/rcmscript.5 0444 root bin
+f usr/share/man/man5/remote.5 0444 root bin
+f usr/share/man/man5/resolv.conf.5 0444 root bin
+s usr/share/man/man5/rhosts.5=hosts.equiv.5
+f usr/share/man/man5/rmtab.5 0444 root bin
+f usr/share/man/man5/rpc.5 0444 root bin
+f usr/share/man/man5/rt_dptbl.5 0444 root bin
+f usr/share/man/man5/sasl_appname.conf.5 0444 root bin
+f usr/share/man/man5/scsi.5 0444 root bin
+f usr/share/man/man5/securenets.5 0444 root bin
+f usr/share/man/man5/sendmail.5 0444 root bin
+s usr/share/man/man5/sendmail.cf.5=sendmail.5
+f usr/share/man/man5/service_bundle.5 0444 root bin
+f usr/share/man/man5/service_provider.conf.5 0444 root bin
+f usr/share/man/man5/services.5 0444 root bin
+f usr/share/man/man5/shadow.5 0444 root bin
+f usr/share/man/man5/sharetab.5 0444 root bin
+f usr/share/man/man5/shells.5 0444 root bin
+f usr/share/man/man5/slp.conf.5 0444 root bin
+f usr/share/man/man5/slpd.reg.5 0444 root bin
+f usr/share/man/man5/smb.5 0444 root bin
+f usr/share/man/man5/smbautohome.5 0444 root bin
+f usr/share/man/man5/smhba.conf.5 0444 root bin
+s usr/share/man/man5/snapshot_cache.5=devices.5
+f usr/share/man/man5/sock2path.d.5 0444 root bin
+f usr/share/man/man5/space.5 0444 root bin
+s usr/share/man/man5/submit.cf.5=sendmail.5
+f usr/share/man/man5/sulog.5 0444 root bin
+f usr/share/man/man5/sysbus.5 0444 root bin
+f usr/share/man/man5/syslog.conf.5 0444 root bin
+f usr/share/man/man5/system.5 0444 root bin
+f usr/share/man/man5/term.5 0444 root bin
+f usr/share/man/man5/terminfo.5 0444 root bin
+f usr/share/man/man5/timezone.5 0444 root bin
+f usr/share/man/man5/ts_dptbl.5 0444 root bin
+f usr/share/man/man5/ttydefs.5 0444 root bin
+f usr/share/man/man5/ttysrch.5 0444 root bin
+f usr/share/man/man5/ufsdump.5 0444 root bin
+f usr/share/man/man5/updaters.5 0444 root bin
+f usr/share/man/man5/user_attr.5 0444 root bin
+f usr/share/man/man5/utmp.5 0444 root bin
+f usr/share/man/man5/utmpx.5 0444 root bin
+f usr/share/man/man5/vfstab.5 0444 root bin
+f usr/share/man/man5/warn.conf.5 0444 root bin
+s usr/share/man/man5/wtmp.5=utmp.5
+s usr/share/man/man5/wtmpx.5=utmpx.5
+f usr/share/man/man5/ypfiles.5 0444 root bin
+f usr/share/man/man5/yppasswdd.5 0444 root bin
+f usr/share/man/man5/ypserv.5 0444 root bin
+f usr/share/man/man5/zoneinfo.5 0444 root bin
+d usr/share/man/man5b 0755 root bin
+d usr/share/man/man7 0755 root bin
+s usr/share/man/man7/ANSI.7=standards.7
+s usr/share/man/man7/C++.7=standards.7
+s usr/share/man/man7/C.7=standards.7
+s usr/share/man/man7/CSI.7=attributes.7
+s usr/share/man/man7/ISO.7=standards.7
+f usr/share/man/man7/Intro.7 0444 root bin
+s usr/share/man/man7/MT-Level.7=attributes.7
+s usr/share/man/man7/POSIX.1.7=standards.7
+s usr/share/man/man7/POSIX.2.7=standards.7
+s usr/share/man/man7/POSIX.7=standards.7
+s usr/share/man/man7/RBAC.7=rbac.7
+s usr/share/man/man7/SUS.7=standards.7
+s usr/share/man/man7/SUSv2.7=standards.7
+s usr/share/man/man7/SUSv3.7=standards.7
+s usr/share/man/man7/SVID.7=standards.7
+s usr/share/man/man7/SVID3.7=standards.7
+s usr/share/man/man7/XNS.7=standards.7
+s usr/share/man/man7/XNS4.7=standards.7
+s usr/share/man/man7/XNS5.7=standards.7
+s usr/share/man/man7/XPG.7=standards.7
+s usr/share/man/man7/XPG3.7=standards.7
+s usr/share/man/man7/XPG4.7=standards.7
+s usr/share/man/man7/XPG4v2.7=standards.7
+f usr/share/man/man7/acl.7 0444 root bin
+f usr/share/man/man7/ad.7 0444 root bin
+s usr/share/man/man7/advance.7=regexp.7
+s usr/share/man/man7/architecture.7=attributes.7
+f usr/share/man/man7/ascii.7 0444 root bin
+f usr/share/man/man7/attributes.7 0444 root bin
+f usr/share/man/man7/audit_binfile.7 0444 root bin
+f usr/share/man/man7/audit_remote.7 0444 root bin
+f usr/share/man/man7/audit_syslog.7 0444 root bin
+s usr/share/man/man7/availability.7=attributes.7
+f usr/share/man/man7/beastie.4th.7 0444 root bin
+f usr/share/man/man7/brand.4th.7 0444 root bin
+f usr/share/man/man7/brands.7 0444 root bin
+f usr/share/man/man7/byteorder.7 0444 root bin
+f usr/share/man/man7/cancellation.7 0444 root bin
+f usr/share/man/man7/charmap.7 0444 root bin
+f usr/share/man/man7/check-password.4th.7 0444 root bin
+f usr/share/man/man7/color.4th.7 0444 root bin
+s usr/share/man/man7/compile.7=regexp.7
+f usr/share/man/man7/condition.7 0444 root bin
+f usr/share/man/man7/crypt_bsdbf.7 0444 root bin
+f usr/share/man/man7/crypt_bsdmd5.7 0444 root bin
+f usr/share/man/man7/crypt_sha256.7 0444 root bin
+f usr/share/man/man7/crypt_sha512.7 0444 root bin
+f usr/share/man/man7/crypt_sunmd5.7 0444 root bin
+f usr/share/man/man7/crypt_unix.7 0444 root bin
+f usr/share/man/man7/delay.4th.7 0444 root bin
+f usr/share/man/man7/device_clean.7 0444 root bin
+f usr/share/man/man7/dhcp.7 0444 root bin
+s usr/share/man/man7/endian.7=byteorder.7
+f usr/share/man/man7/environ.7 0444 root bin
+f usr/share/man/man7/epoll.7 0444 root bin
+f usr/share/man/man7/eqn.7 0444 root bin
+f usr/share/man/man7/eqnchar.7 0444 root bin
+f usr/share/man/man7/eventfd.7 0444 root bin
+f usr/share/man/man7/extendedFILE.7 0444 root bin
+f usr/share/man/man7/filesystem.7 0444 root bin
+f usr/share/man/man7/fnmatch.7 0444 root bin
+f usr/share/man/man7/formats.7 0444 root bin
+f usr/share/man/man7/fsattr.7 0444 root bin
+f usr/share/man/man7/gptzfsboot.7 0444 root bin
+f usr/share/man/man7/grub.7 0444 root bin
+f usr/share/man/man7/gss_auth_rules.7 0444 root bin
+f usr/share/man/man7/iconv.7 0444 root bin
+f usr/share/man/man7/iconv_unicode.7 0444 root bin
+f usr/share/man/man7/ieee802.11.7 0444 root bin
+f usr/share/man/man7/ieee802.3.7 0444 root bin
+f usr/share/man/man7/inotify.7 0444 root bin
+s usr/share/man/man7/intro.7=Intro.7
+f usr/share/man/man7/ipfilter.7 0444 root bin
+f usr/share/man/man7/isalist.7 0444 root bin
+f usr/share/man/man7/isoboot.7 0444 root bin
+f usr/share/man/man7/kerberos.7 0444 root bin
+f usr/share/man/man7/krb5_auth_rules.7 0444 root bin
+f usr/share/man/man7/krb5envvar.7 0444 root bin
+f usr/share/man/man7/largefile.7 0444 root bin
+f usr/share/man/man7/lf64.7 0444 root bin
+f usr/share/man/man7/lfcompile.7 0444 root bin
+f usr/share/man/man7/lfcompile64.7 0444 root bin
+f usr/share/man/man7/loader.4th.7 0444 root bin
+f usr/share/man/man7/loader.7 0444 root bin
+f usr/share/man/man7/locale.7 0444 root bin
+f usr/share/man/man7/lx.7 0444 root bin
+f usr/share/man/man7/man.7 0444 root bin
+f usr/share/man/man7/mandoc_char.7 0444 root bin
+f usr/share/man/man7/mandoc_roff.7 0444 root bin
+f usr/share/man/man7/mansun.7 0444 root bin
+f usr/share/man/man7/mdoc.7 0444 root bin
+f usr/share/man/man7/me.7 0444 root bin
+f usr/share/man/man7/mech_spnego.7 0444 root bin
+f usr/share/man/man7/menu.4th.7 0444 root bin
+f usr/share/man/man7/menusets.4th.7 0444 root bin
+f usr/share/man/man7/mm.7 0444 root bin
+f usr/share/man/man7/ms.7 0444 root bin
+f usr/share/man/man7/mutex.7 0444 root bin
+f usr/share/man/man7/nfssec.7 0444 root bin
+f usr/share/man/man7/overlay.7 0444 root bin
+f usr/share/man/man7/pam_allow.7 0444 root bin
+f usr/share/man/man7/pam_authtok_check.7 0444 root bin
+f usr/share/man/man7/pam_authtok_get.7 0444 root bin
+f usr/share/man/man7/pam_authtok_store.7 0444 root bin
+f usr/share/man/man7/pam_deny.7 0444 root bin
+f usr/share/man/man7/pam_dhkeys.7 0444 root bin
+f usr/share/man/man7/pam_dial_auth.7 0444 root bin
+f usr/share/man/man7/pam_krb5.7 0444 root bin
+f usr/share/man/man7/pam_krb5_migrate.7 0444 root bin
+f usr/share/man/man7/pam_ldap.7 0444 root bin
+f usr/share/man/man7/pam_list.7 0444 root bin
+f usr/share/man/man7/pam_passwd_auth.7 0444 root bin
+f usr/share/man/man7/pam_rhosts_auth.7 0444 root bin
+f usr/share/man/man7/pam_roles.7 0444 root bin
+f usr/share/man/man7/pam_sample.7 0444 root bin
+f usr/share/man/man7/pam_smb_passwd.7 0444 root bin
+f usr/share/man/man7/pam_smbfs_login.7 0444 root bin
+f usr/share/man/man7/pam_timestamp.7 0444 root bin
+f usr/share/man/man7/pam_unix_account.7 0444 root bin
+f usr/share/man/man7/pam_unix_auth.7 0444 root bin
+f usr/share/man/man7/pam_unix_cred.7 0444 root bin
+f usr/share/man/man7/pam_unix_session.7 0444 root bin
+f usr/share/man/man7/pkcs11_kernel.7 0444 root bin
+f usr/share/man/man7/pkcs11_softtoken.7 0444 root bin
+f usr/share/man/man7/privileges.7 0444 root bin
+f usr/share/man/man7/prof.7 0444 root bin
+s usr/share/man/man7/pthreads.7=threads.7
+f usr/share/man/man7/pxeboot.7 0444 root bin
+f usr/share/man/man7/rbac.7 0444 root bin
+f usr/share/man/man7/regex.7 0444 root bin
+f usr/share/man/man7/regexp.7 0444 root bin
+f usr/share/man/man7/resource_controls.7 0444 root bin
+f usr/share/man/man7/security-flags.7 0444 root bin
+f usr/share/man/man7/smf.7 0444 root bin
+f usr/share/man/man7/smf_bootstrap.7 0444 root bin
+f usr/share/man/man7/smf_method.7 0444 root bin
+f usr/share/man/man7/smf_restarter.7 0444 root bin
+f usr/share/man/man7/smf_security.7 0444 root bin
+f usr/share/man/man7/smf_template.7 0444 root bin
+s usr/share/man/man7/stability.7=attributes.7
+s usr/share/man/man7/standard.7=attributes.7
+f usr/share/man/man7/standards.7 0444 root bin
+s usr/share/man/man7/step.7=regexp.7
+f usr/share/man/man7/sticky.7 0444 root bin
+f usr/share/man/man7/tbl.7 0444 root bin
+f usr/share/man/man7/tecla.7 0444 root bin
+s usr/share/man/man7/teclarc.7=tecla.7
+f usr/share/man/man7/term.7 0444 root bin
+f usr/share/man/man7/threads.7 0444 root bin
+f usr/share/man/man7/timerfd.7 0444 root bin
+f usr/share/man/man7/version.4th.7 0444 root bin
+f usr/share/man/man7/vgrindefs.7 0444 root bin
+f usr/share/man/man7/zones.7 0444 root bin
+f usr/share/man/man7/zpool-features.7 0444 root bin
+d usr/share/man/man8 0755 root bin
+f usr/share/man/man8/6to4relay.8 0444 root bin
+f usr/share/man/man8/Intro.8 0444 root bin
+f usr/share/man/man8/acctadm.8 0444 root bin
+f usr/share/man/man8/acpidump.8 0444 root bin
+f usr/share/man/man8/acpixtract.8 0444 root bin
+f usr/share/man/man8/add_drv.8 0444 root bin
+f usr/share/man/man8/addbadsec.8 0444 root bin
+f usr/share/man/man8/arcstat.8 0444 root bin
+f usr/share/man/man8/arp.8 0444 root bin
+f usr/share/man/man8/audit.8 0444 root bin
+f usr/share/man/man8/audit_warn.8 0444 root bin
+f usr/share/man/man8/auditconfig.8 0444 root bin
+f usr/share/man/man8/auditd.8 0444 root bin
+f usr/share/man/man8/auditrecord.8 0444 root bin
+f usr/share/man/man8/auditreduce.8 0444 root bin
+f usr/share/man/man8/auditstat.8 0444 root bin
+s usr/share/man/man8/audlinks.8=devfsadm.8
+f usr/share/man/man8/automount.8 0444 root bin
+f usr/share/man/man8/automountd.8 0444 root bin
+f usr/share/man/man8/autopush.8 0444 root bin
+f usr/share/man/man8/bart.8 0444 root bin
+f usr/share/man/man8/bhyve.8 0444 root bin
+f usr/share/man/man8/bhyvectl.8 0444 root bin
+f usr/share/man/man8/bootadm.8 0444 root bin
+f usr/share/man/man8/busstat.8 0444 root bin
+f usr/share/man/man8/captoinfo.8 0444 root bin
+f usr/share/man/man8/catman.8 0444 root bin
+f usr/share/man/man8/ccidadm.8 0444 root bin
+f usr/share/man/man8/cfgadm.8 0444 root bin
+f usr/share/man/man8/cfgadm_fp.8 0444 root bin
+f usr/share/man/man8/cfgadm_pci.8 0444 root bin
+f usr/share/man/man8/cfgadm_sata.8 0444 root bin
+f usr/share/man/man8/cfgadm_scsi.8 0444 root bin
+f usr/share/man/man8/cfgadm_usb.8 0444 root bin
+f usr/share/man/man8/check-hostname.8 0444 root bin
+f usr/share/man/man8/check-permissions.8 0444 root bin
+f usr/share/man/man8/chroot.8 0444 root bin
+f usr/share/man/man8/clear_locks.8 0444 root bin
+f usr/share/man/man8/clinfo.8 0444 root bin
+f usr/share/man/man8/clri.8 0444 root bin
+f usr/share/man/man8/connstat.8 0444 root bin
+f usr/share/man/man8/consadm.8 0444 root bin
+f usr/share/man/man8/coreadm.8 0444 root bin
+f usr/share/man/man8/cpustat.8 0444 root bin
+f usr/share/man/man8/cron.8 0444 root bin
+f usr/share/man/man8/cryptoadm.8 0444 root bin
+s usr/share/man/man8/dcopy.8=clri.8
+f usr/share/man/man8/dd.8 0444 root bin
+f usr/share/man/man8/devattr.8 0444 root bin
+f usr/share/man/man8/devfree.8 0444 root bin
+f usr/share/man/man8/devfsadm.8 0444 root bin
+s usr/share/man/man8/devfsadmd.8=devfsadm.8
+f usr/share/man/man8/devinfo.8 0444 root bin
+f usr/share/man/man8/devlinks.8 0444 root bin
+f usr/share/man/man8/devnm.8 0444 root bin
+f usr/share/man/man8/devprop.8 0444 root bin
+f usr/share/man/man8/devreserv.8 0444 root bin
+f usr/share/man/man8/df.8 0444 root bin
+f usr/share/man/man8/df_ufs.8 0444 root bin
+f usr/share/man/man8/dfmounts.8 0444 root bin
+f usr/share/man/man8/dfmounts_nfs.8 0444 root bin
+f usr/share/man/man8/dfshares.8 0444 root bin
+f usr/share/man/man8/dfshares_nfs.8 0444 root bin
+f usr/share/man/man8/dhcpagent.8 0444 root bin
+f usr/share/man/man8/diskinfo.8 0444 root bin
+f usr/share/man/man8/disks.8 0444 root bin
+f usr/share/man/man8/diskscan.8 0444 root bin
+f usr/share/man/man8/dispadmin.8 0444 root bin
+f usr/share/man/man8/dladm.8 0444 root bin
+f usr/share/man/man8/dlmgmtd.8 0444 root bin
+f usr/share/man/man8/dmesg.8 0444 root bin
+f usr/share/man/man8/dminfo.8 0444 root bin
+f usr/share/man/man8/dns-sd.8 0444 root bin
+f usr/share/man/man8/domainname.8 0444 root bin
+f usr/share/man/man8/drvconfig.8 0444 root bin
+f usr/share/man/man8/dtrace.8 0444 root bin
+f usr/share/man/man8/dumpadm.8 0444 root bin
+f usr/share/man/man8/editmap.8 0444 root bin
+f usr/share/man/man8/edquota.8 0444 root bin
+f usr/share/man/man8/eeprom.8 0444 root bin
+f usr/share/man/man8/embedded_su.8 0444 root bin
+f usr/share/man/man8/etrn.8 0444 root bin
+s usr/share/man/man8/fcadm.8=fcinfo.8
+f usr/share/man/man8/fcinfo.8 0444 root bin
+f usr/share/man/man8/fdetach.8 0444 root bin
+f usr/share/man/man8/fdisk.8 0444 root bin
+f usr/share/man/man8/ff.8 0444 root bin
+f usr/share/man/man8/ff_ufs.8 0444 root bin
+f usr/share/man/man8/fiocompress.8 0444 root bin
+f usr/share/man/man8/flowadm.8 0444 root bin
+f usr/share/man/man8/fmadm.8 0444 root bin
+f usr/share/man/man8/fmd.8 0444 root bin
+f usr/share/man/man8/fmdump.8 0444 root bin
+f usr/share/man/man8/fmstat.8 0444 root bin
+f usr/share/man/man8/fmthard.8 0444 root bin
+f usr/share/man/man8/format.8 0444 root bin
+f usr/share/man/man8/fsck.8 0444 root bin
+f usr/share/man/man8/fsck_ufs.8 0444 root bin
+f usr/share/man/man8/fsdb.8 0444 root bin
+f usr/share/man/man8/fsdb_ufs.8 0444 root bin
+f usr/share/man/man8/fsirand.8 0444 root bin
+f usr/share/man/man8/fssnap.8 0444 root bin
+f usr/share/man/man8/fssnap_ufs.8 0444 root bin
+f usr/share/man/man8/fsstat.8 0444 root bin
+f usr/share/man/man8/fstyp.8 0444 root bin
+f usr/share/man/man8/fuser.8 0444 root bin
+f usr/share/man/man8/fwflash.8 0444 root bin
+f usr/share/man/man8/getdev.8 0444 root bin
+f usr/share/man/man8/getdevpolicy.8 0444 root bin
+f usr/share/man/man8/getdgrp.8 0444 root bin
+f usr/share/man/man8/getent.8 0444 root bin
+f usr/share/man/man8/getty.8 0444 root bin
+f usr/share/man/man8/getvol.8 0444 root bin
+f usr/share/man/man8/groupadd.8 0444 root bin
+f usr/share/man/man8/groupdel.8 0444 root bin
+f usr/share/man/man8/groupmod.8 0444 root bin
+s usr/share/man/man8/grpck.8=pwck.8
+f usr/share/man/man8/gsscred.8 0444 root bin
+f usr/share/man/man8/gssd.8 0444 root bin
+f usr/share/man/man8/halt.8 0444 root bin
+f usr/share/man/man8/hostconfig.8 0444 root bin
+f usr/share/man/man8/hotplug.8 0444 root bin
+f usr/share/man/man8/id.8 0444 root bin
+f usr/share/man/man8/idmap.8 0444 root bin
+f usr/share/man/man8/idmapd.8 0444 root bin
+f usr/share/man/man8/if_mpadm.8 0444 root bin
+f usr/share/man/man8/ifconfig.8 0444 root bin
+f usr/share/man/man8/ifparse.8 0444 root bin
+f usr/share/man/man8/ikeadm.8 0444 root bin
+f usr/share/man/man8/ikecert.8 0444 root bin
+f usr/share/man/man8/in.comsat.8 0444 root bin
+f usr/share/man/man8/in.fingerd.8 0444 root bin
+f usr/share/man/man8/in.iked.8 0444 root bin
+f usr/share/man/man8/in.mpathd.8 0444 root bin
+f usr/share/man/man8/in.ndpd.8 0444 root bin
+f usr/share/man/man8/in.rdisc.8 0444 root bin
+f usr/share/man/man8/in.rexecd.8 0444 root bin
+f usr/share/man/man8/in.ripngd.8 0444 root bin
+f usr/share/man/man8/in.rlogind.8 0444 root bin
+f usr/share/man/man8/in.routed.8 0444 root bin
+f usr/share/man/man8/in.rshd.8 0444 root bin
+f usr/share/man/man8/in.rwhod.8 0444 root bin
+f usr/share/man/man8/in.talkd.8 0444 root bin
+f usr/share/man/man8/in.telnetd.8 0444 root bin
+f usr/share/man/man8/in.tftpd.8 0444 root bin
+f usr/share/man/man8/inetadm.8 0444 root bin
+f usr/share/man/man8/inetconv.8 0444 root bin
+f usr/share/man/man8/inetd.8 0444 root bin
+f usr/share/man/man8/infocmp.8 0444 root bin
+f usr/share/man/man8/init.8 0444 root bin
+f usr/share/man/man8/install.8 0444 root bin
+f usr/share/man/man8/installboot.8 0444 root bin
+f usr/share/man/man8/installgrub.8 0444 root bin
+f usr/share/man/man8/intrd.8 0444 root bin
+s usr/share/man/man8/intro.8=Intro.8
+f usr/share/man/man8/intrstat.8 0444 root bin
+f usr/share/man/man8/iostat.8 0444 root bin
+f usr/share/man/man8/ipaddrsel.8 0444 root bin
+f usr/share/man/man8/ipadm.8 0444 root bin
+f usr/share/man/man8/ipdadm.8 0444 root bin
+f usr/share/man/man8/ipf.8 0444 root bin
+f usr/share/man/man8/ipfs.8 0444 root bin
+f usr/share/man/man8/ipfstat.8 0444 root bin
+f usr/share/man/man8/ipmon.8 0444 root bin
+f usr/share/man/man8/ipmpstat.8 0444 root bin
+f usr/share/man/man8/ipnat.8 0444 root bin
+f usr/share/man/man8/ippool.8 0444 root bin
+f usr/share/man/man8/ipqosconf.8 0444 root bin
+f usr/share/man/man8/ipsecalgs.8 0444 root bin
+f usr/share/man/man8/ipsecconf.8 0444 root bin
+f usr/share/man/man8/ipseckey.8 0444 root bin
+f usr/share/man/man8/iscsiadm.8 0444 root bin
+f usr/share/man/man8/itadm.8 0444 root bin
+f usr/share/man/man8/kernel.8 0444 root bin
+f usr/share/man/man8/keyserv.8 0444 root bin
+f usr/share/man/man8/killall.8 0444 root bin
+f usr/share/man/man8/kstat.8 0444 root bin
+f usr/share/man/man8/labelit.8 0444 root bin
+f usr/share/man/man8/labelit_hsfs.8 0444 root bin
+f usr/share/man/man8/labelit_ufs.8 0444 root bin
+f usr/share/man/man8/ldap_cachemgr.8 0444 root bin
+f usr/share/man/man8/ldapaddent.8 0444 root bin
+f usr/share/man/man8/ldapclient.8 0444 root bin
+f usr/share/man/man8/link.8 0444 root bin
+f usr/share/man/man8/listdgrp.8 0444 root bin
+f usr/share/man/man8/listen.8 0444 root bin
+f usr/share/man/man8/locator.8 0444 root bin
+f usr/share/man/man8/lockfs.8 0444 root bin
+f usr/share/man/man8/lockstat.8 0444 root bin
+f usr/share/man/man8/lofiadm.8 0444 root bin
+f usr/share/man/man8/logadm.8 0444 root bin
+f usr/share/man/man8/logins.8 0444 root bin
+f usr/share/man/man8/mail.local.8 0444 root bin
+f usr/share/man/man8/mailwrapper.8 0444 root bin
+f usr/share/man/man8/makedbm.8 0444 root bin
+f usr/share/man/man8/makemap.8 0444 root bin
+f usr/share/man/man8/mdnsd.8 0444 root bin
+f usr/share/man/man8/mkdevalloc.8 0444 root bin
+f usr/share/man/man8/mkdevmaps.8 0444 root bin
+f usr/share/man/man8/mkfifo.8 0444 root bin
+f usr/share/man/man8/mkfile.8 0444 root bin
+f usr/share/man/man8/mkfs.8 0444 root bin
+f usr/share/man/man8/mkfs_pcfs.8 0444 root bin
+f usr/share/man/man8/mkfs_udfs.8 0444 root bin
+f usr/share/man/man8/mkfs_ufs.8 0444 root bin
+f usr/share/man/man8/mknod.8 0444 root bin
+f usr/share/man/man8/mkpwdict.8 0444 root bin
+f usr/share/man/man8/modinfo.8 0444 root bin
+f usr/share/man/man8/modload.8 0444 root bin
+f usr/share/man/man8/modunload.8 0444 root bin
+f usr/share/man/man8/mount.8 0444 root bin
+f usr/share/man/man8/mount_hsfs.8 0444 root bin
+f usr/share/man/man8/mount_nfs.8 0444 root bin
+f usr/share/man/man8/mount_pcfs.8 0444 root bin
+f usr/share/man/man8/mount_smbfs.8 0444 root bin
+f usr/share/man/man8/mount_tmpfs.8 0444 root bin
+f usr/share/man/man8/mount_udfs.8 0444 root bin
+f usr/share/man/man8/mount_ufs.8 0444 root bin
+f usr/share/man/man8/mountall.8 0444 root bin
+f usr/share/man/man8/mountd.8 0444 root bin
+f usr/share/man/man8/mpathadm.8 0444 root bin
+f usr/share/man/man8/mpstat.8 0444 root bin
+f usr/share/man/man8/msgid.8 0444 root bin
+f usr/share/man/man8/mvdir.8 0444 root bin
+f usr/share/man/man8/ncheck.8 0444 root bin
+f usr/share/man/man8/ncheck_ufs.8 0444 root bin
+f usr/share/man/man8/ndd.8 0444 root bin
+f usr/share/man/man8/ndp.8 0444 root bin
+f usr/share/man/man8/netstat.8 0444 root bin
+f usr/share/man/man8/netstrategy.8 0444 root bin
+f usr/share/man/man8/newaliases.8 0444 root bin
+f usr/share/man/man8/newfs.8 0444 root bin
+f usr/share/man/man8/newkey.8 0444 root bin
+f usr/share/man/man8/nfs4cbd.8 0444 root bin
+f usr/share/man/man8/nfsd.8 0444 root bin
+f usr/share/man/man8/nfslogd.8 0444 root bin
+f usr/share/man/man8/nfsmapid.8 0444 root bin
+f usr/share/man/man8/nfsstat.8 0444 root bin
+f usr/share/man/man8/nlsadmin.8 0444 root bin
+f usr/share/man/man8/nscd.8 0444 root bin
+f usr/share/man/man8/nvmeadm.8 0444 root bin
+f usr/share/man/man8/nwamd.8 0444 root bin
+f usr/share/man/man8/passmgmt.8 0444 root bin
+f usr/share/man/man8/pbind.8 0444 root bin
+f usr/share/man/man8/pcitool.8 0444 root bin
+f usr/share/man/man8/picld.8 0444 root bin
+f usr/share/man/man8/ping.8 0444 root bin
+f usr/share/man/man8/plockstat.8 0444 root bin
+f usr/share/man/man8/pmadm.8 0444 root bin
+f usr/share/man/man8/pmconfig.8 0444 root bin
+f usr/share/man/man8/ports.8 0444 root bin
+s usr/share/man/man8/poweroff.8=halt.8
+f usr/share/man/man8/pptadm.8 0444 root bin
+f usr/share/man/man8/praudit.8 0444 root bin
+f usr/share/man/man8/projadd.8 0444 root bin
+f usr/share/man/man8/projdel.8 0444 root bin
+f usr/share/man/man8/projmod.8 0444 root bin
+f usr/share/man/man8/prstat.8 0444 root bin
+f usr/share/man/man8/prtconf.8 0444 root bin
+f usr/share/man/man8/prtdiag.8 0444 root bin
+f usr/share/man/man8/prtpicl.8 0444 root bin
+f usr/share/man/man8/prtvtoc.8 0444 root bin
+f usr/share/man/man8/psradm.8 0444 root bin
+f usr/share/man/man8/psrinfo.8 0444 root bin
+f usr/share/man/man8/psrset.8 0444 root bin
+f usr/share/man/man8/putdev.8 0444 root bin
+f usr/share/man/man8/putdgrp.8 0444 root bin
+f usr/share/man/man8/pwck.8 0444 root bin
+f usr/share/man/man8/pwconv.8 0444 root bin
+f usr/share/man/man8/quot.8 0444 root bin
+f usr/share/man/man8/quota.8 0444 root bin
+f usr/share/man/man8/quotacheck.8 0444 root bin
+s usr/share/man/man8/quotaoff.8=quotaon.8
+f usr/share/man/man8/quotaon.8 0444 root bin
+f usr/share/man/man8/raidctl.8 0444 root bin
+f usr/share/man/man8/ramdiskadm.8 0444 root bin
+f usr/share/man/man8/rcapadm.8 0444 root bin
+f usr/share/man/man8/rcapd.8 0444 root bin
+f usr/share/man/man8/rctladm.8 0444 root bin
+f usr/share/man/man8/rdate.8 0444 root bin
+s usr/share/man/man8/rdisc.8=in.rdisc.8
+f usr/share/man/man8/rdmsr.8 0444 root bin
+f usr/share/man/man8/reboot.8 0444 root bin
+f usr/share/man/man8/rem_drv.8 0444 root bin
+f usr/share/man/man8/repquota.8 0444 root bin
+s usr/share/man/man8/restricted_shell.8=rsh.8
+f usr/share/man/man8/rmt.8 0444 root bin
+f usr/share/man/man8/roleadd.8 0444 root bin
+f usr/share/man/man8/roledel.8 0444 root bin
+f usr/share/man/man8/rolemod.8 0444 root bin
+f usr/share/man/man8/route.8 0444 root bin
+f usr/share/man/man8/routeadm.8 0444 root bin
+s usr/share/man/man8/routed.8=in.routed.8
+f usr/share/man/man8/rpcbind.8 0444 root bin
+f usr/share/man/man8/rpcinfo.8 0444 root bin
+f usr/share/man/man8/rquotad.8 0444 root bin
+f usr/share/man/man8/rsh.8 0444 root bin
+f usr/share/man/man8/rtc.8 0444 root bin
+f usr/share/man/man8/rtquery.8 0444 root bin
+f usr/share/man/man8/rwall.8 0444 root bin
+f usr/share/man/man8/sac.8 0444 root bin
+f usr/share/man/man8/sacadm.8 0444 root bin
+f usr/share/man/man8/saf.8 0444 root bin
+f usr/share/man/man8/sasinfo.8 0444 root bin
+f usr/share/man/man8/savecore.8 0444 root bin
+f usr/share/man/man8/sbdadm.8 0444 root bin
+f usr/share/man/man8/sendmail.8 0444 root bin
+f usr/share/man/man8/share.8 0444 root bin
+f usr/share/man/man8/share_nfs.8 0444 root bin
+f usr/share/man/man8/shareall.8 0444 root bin
+f usr/share/man/man8/sharectl.8 0444 root bin
+f usr/share/man/man8/sharemgr.8 0444 root bin
+f usr/share/man/man8/showmount.8 0444 root bin
+f usr/share/man/man8/shutdown.8 0444 root bin
+f usr/share/man/man8/slpd.8 0444 root bin
+f usr/share/man/man8/smbadm.8 0444 root bin
+f usr/share/man/man8/smbd.8 0444 root bin
+f usr/share/man/man8/smbiod.8 0444 root bin
+f usr/share/man/man8/smbios.8 0444 root bin
+f usr/share/man/man8/smbstat.8 0444 root bin
+f usr/share/man/man8/smrsh.8 0444 root bin
+f usr/share/man/man8/snoop.8 0444 root bin
+f usr/share/man/man8/soconfig.8 0444 root bin
+f usr/share/man/man8/sppptun.8 0444 root bin
+f usr/share/man/man8/spray.8 0444 root bin
+f usr/share/man/man8/statd.8 0444 root bin
+f usr/share/man/man8/stmfadm.8 0444 root bin
+f usr/share/man/man8/stmsboot.8 0444 root bin
+f usr/share/man/man8/strclean.8 0444 root bin
+f usr/share/man/man8/strerr.8 0444 root bin
+f usr/share/man/man8/sttydefs.8 0444 root bin
+f usr/share/man/man8/su.8 0444 root bin
+f usr/share/man/man8/sulogin.8 0444 root bin
+f usr/share/man/man8/svc.configd.8 044 root bin
+f usr/share/man/man8/svc.ipfd.8 044 root bin
+f usr/share/man/man8/svc.startd.8 044 root bin
+f usr/share/man/man8/svcadm.8 0444 root bin
+f usr/share/man/man8/svccfg.8 0444 root bin
+f usr/share/man/man8/swap.8 0444 root bin
+f usr/share/man/man8/sync.8 0444 root bin
+f usr/share/man/man8/syncinit.8 0444 root bin
+f usr/share/man/man8/syncloop.8 0444 root bin
+f usr/share/man/man8/syncstat.8 0444 root bin
+f usr/share/man/man8/sysdef.8 0444 root bin
+f usr/share/man/man8/syseventadm.8 0444 root bin
+f usr/share/man/man8/syseventconfd.8 0444 root bin
+f usr/share/man/man8/syseventd.8 0444 root bin
+f usr/share/man/man8/syslogd.8 0444 root bin
+f usr/share/man/man8/tapes.8 0444 root bin
+f usr/share/man/man8/tcpd.8 0444 root bin
+f usr/share/man/man8/tcpdchk.8 0444 root bin
+f usr/share/man/man8/tcpdmatch.8 0444 root bin
+f usr/share/man/man8/tic.8 0444 root bin
+f usr/share/man/man8/traceroute.8 0444 root bin
+f usr/share/man/man8/trapstat.8 0444 root bin
+f usr/share/man/man8/ttyadm.8 0444 root bin
+f usr/share/man/man8/ttymon.8 0444 root bin
+f usr/share/man/man8/tunefs.8 0444 root bin
+f usr/share/man/man8/tzreload.8 0444 root bin
+f usr/share/man/man8/tzselect.8 0444 root bin
+f usr/share/man/man8/uadmin.8 0444 root bin
+f usr/share/man/man8/ucodeadm.8 0444 root bin
+f usr/share/man/man8/ufsdump.8 0444 root bin
+f usr/share/man/man8/ufsrestore.8 0444 root bin
+s usr/share/man/man8/umount.8=mount.8
+s usr/share/man/man8/umount_smbfs.8=mount_smbfs.8
+s usr/share/man/man8/umountall.8=mountall.8
+s usr/share/man/man8/unlink.8=link.8
+f usr/share/man/man8/unshare.8 0444 root bin
+f usr/share/man/man8/unshare_nfs.8 0444 root bin
+s usr/share/man/man8/unshareall.8=shareall.8
+f usr/share/man/man8/update_drv.8 0444 root bin
+f usr/share/man/man8/useradd.8 0444 root bin
+f usr/share/man/man8/userdel.8 0444 root bin
+f usr/share/man/man8/usermod.8 0444 root bin
+f usr/share/man/man8/utmpd.8 0444 root bin
+f usr/share/man/man8/vfsstat.8 0444 root bin
+f usr/share/man/man8/vmstat.8 0444 root bin
+f usr/share/man/man8/vndadm.8 0444 root bin
+f usr/share/man/man8/vndstat.8 0444 root bin
+f usr/share/man/man8/volcopy.8 0444 root bin
+f usr/share/man/man8/volcopy_ufs.8 0444 root bin
+f usr/share/man/man8/wall.8 0444 root bin
+f usr/share/man/man8/whodo.8 0444 root bin
+f usr/share/man/man8/wracct.8 0444 root bin
+f usr/share/man/man8/ypinit.8 0444 root bin
+f usr/share/man/man8/yppoll.8 0444 root bin
+f usr/share/man/man8/ypset.8 0444 root bin
+f usr/share/man/man8/zdb.8 0444 root bin
+f usr/share/man/man8/zdump.8 0444 root bin
+f usr/share/man/man8/zfs-program.8 0444 root bin
+f usr/share/man/man8/zfs.8 0444 root bin
+f usr/share/man/man8/zic.8 0444 root bin
+f usr/share/man/man8/zoneadm.8 0444 root bin
+f usr/share/man/man8/zoneadmd.8 0444 root bin
+f usr/share/man/man8/zonecfg.8 0444 root bin
+f usr/share/man/man8/zonestatd.8 0444 root bin
+f usr/share/man/man8/zpool.8 0444 root bin
+f usr/share/man/man8/zstreamdump.8 0444 root bin
+f usr/share/man/man8/ztest.8 0444 root bin
+d usr/share/man/man9 0755 root bin
+f usr/share/man/man9/Intro.9 0444 root bin
+s usr/share/man/man9/intro.9=Intro.9
+f usr/share/man/man9/iport.9 0444 root bin
+s usr/share/man/man9/iportmap.9=iport.9
+s usr/share/man/man9/phymap.9=iport.9
+s usr/share/man/man9/tgtmap.9=iport.9
+f usr/share/man/man9/vmem.9 0444 root bin
+d usr/share/man/man9e 0755 root bin
+s usr/share/man/man9e/GLDv3.9e=mac.9e
+f usr/share/man/man9e/Intro.9e 0444 root bin
+s usr/share/man/man9e/MAC.9e=mac.9e
+f usr/share/man/man9e/_fini.9e 0444 root bin
+s usr/share/man/man9e/_info.9e=_fini.9e
+s usr/share/man/man9e/_init.9e=_fini.9e
+f usr/share/man/man9e/aread.9e 0444 root bin
+f usr/share/man/man9e/attach.9e 0444 root bin
+f usr/share/man/man9e/awrite.9e 0444 root bin
+f usr/share/man/man9e/chpoll.9e 0444 root bin
+f usr/share/man/man9e/close.9e 0444 root bin
+f usr/share/man/man9e/csx_event_handler.9e 0444 root bin
+f usr/share/man/man9e/ddi_ufm.9e 0444 root bin
+s usr/share/man/man9e/ddi_ufm_op_fill_image.9e=ddi_ufm.9e
+s usr/share/man/man9e/ddi_ufm_op_fill_slot.9e=ddi_ufm.9e
+s usr/share/man/man9e/ddi_ufm_op_getcaps.9e=ddi_ufm.9e
+s usr/share/man/man9e/ddi_ufm_op_nimages.9e=ddi_ufm.9e
+s usr/share/man/man9e/ddi_ufm_op_nslots.9e=ddi_ufm.9e
+f usr/share/man/man9e/detach.9e 0444 root bin
+f usr/share/man/man9e/devmap.9e 0444 root bin
+f usr/share/man/man9e/devmap_access.9e 0444 root bin
+f usr/share/man/man9e/devmap_contextmgt.9e 0444 root bin
+f usr/share/man/man9e/devmap_dup.9e 0444 root bin
+f usr/share/man/man9e/devmap_map.9e 0444 root bin
+f usr/share/man/man9e/devmap_unmap.9e 0444 root bin
+f usr/share/man/man9e/dump.9e 0444 root bin
+f usr/share/man/man9e/getinfo.9e 0444 root bin
+f usr/share/man/man9e/gld.9e 0444 root bin
+s usr/share/man/man9e/gldm_get_stats.9e=gld.9e
+s usr/share/man/man9e/gldm_intr.9e=gld.9e
+s usr/share/man/man9e/gldm_ioctl.9e=gld.9e
+s usr/share/man/man9e/gldm_reset.9e=gld.9e
+s usr/share/man/man9e/gldm_send.9e=gld.9e
+s usr/share/man/man9e/gldm_set_mac_addr.9e=gld.9e
+s usr/share/man/man9e/gldm_set_multicast.9e=gld.9e
+s usr/share/man/man9e/gldm_set_promiscuous.9e=gld.9e
+s usr/share/man/man9e/gldm_start.9e=gld.9e
+s usr/share/man/man9e/gldm_stop.9e=gld.9e
+s usr/share/man/man9e/gldv3.9e=mac.9e
+f usr/share/man/man9e/identify.9e 0444 root bin
+s usr/share/man/man9e/intro.9e=Intro.9e
+f usr/share/man/man9e/ioctl.9e 0444 root bin
+f usr/share/man/man9e/ks_snapshot.9e 0444 root bin
+f usr/share/man/man9e/ks_update.9e 0444 root bin
+f usr/share/man/man9e/mac.9e 0444 root bin
+f usr/share/man/man9e/mac_capab_led.9e 0444 root bin
+f usr/share/man/man9e/mac_capab_rings.9e 0444 root bin
+f usr/share/man/man9e/mac_capab_transceiver.9e 0444 root bin
+f usr/share/man/man9e/mac_filter.9e 0444 root bin
+s usr/share/man/man9e/mc_close.9e=mc_open.9e
+f usr/share/man/man9e/mc_getcapab.9e 0444 root bin
+f usr/share/man/man9e/mc_getprop.9e 0444 root bin
+f usr/share/man/man9e/mc_getstat.9e 0444 root bin
+f usr/share/man/man9e/mc_ioctl.9e 0444 root bin
+f usr/share/man/man9e/mc_multicst.9e 0444 root bin
+f usr/share/man/man9e/mc_open.9e 0444 root bin
+f usr/share/man/man9e/mc_propinfo.9e 0444 root bin
+f usr/share/man/man9e/mc_setpromisc.9e 0444 root bin
+f usr/share/man/man9e/mc_setprop.9e 0444 root bin
+f usr/share/man/man9e/mc_start.9e 0444 root bin
+s usr/share/man/man9e/mc_stop.9e=mc_start.9e
+f usr/share/man/man9e/mc_tx.9e 0444 root bin
+f usr/share/man/man9e/mc_unicst.9e 0444 root bin
+s usr/share/man/man9e/mcl_set.9e=mac_capab_led.9e
+s usr/share/man/man9e/mct_info.9e=mac_capab_transceiver.9e
+s usr/share/man/man9e/mct_read.9e=mac_capab_transceiver.9e
+s usr/share/man/man9e/mgi_addmac.9e=mac_filter.9e
+s usr/share/man/man9e/mgi_addvlan.9e=mac_filter.9e
+s usr/share/man/man9e/mgi_remmac.9e=mac_filter.9e
+s usr/share/man/man9e/mgi_remvlan.9e=mac_filter.9e
+f usr/share/man/man9e/mgi_start.9e 0444 root bin
+s usr/share/man/man9e/mgi_stop.9e=mgi_start.9e
+s usr/share/man/man9e/mi_disable.9e=mi_enable.9e
+f usr/share/man/man9e/mi_enable.9e 0444 root bin
+f usr/share/man/man9e/mr_gget.9e 0444 root bin
+f usr/share/man/man9e/mr_rget.9e 0444 root bin
+f usr/share/man/man9e/mri_poll.9e 0444 root bin
+s usr/share/man/man9e/mri_start.9e=mgi_start.9e
+f usr/share/man/man9e/mri_stat.9e 0444 root bin
+s usr/share/man/man9e/mri_stop.9e=mgi_start.9e
+s usr/share/man/man9e/mri_tx.9e=mc_tx.9e
+f usr/share/man/man9e/mmap.9e 0444 root bin
+f usr/share/man/man9e/open.9e 0444 root bin
+f usr/share/man/man9e/power.9e 0444 root bin
+f usr/share/man/man9e/print.9e 0444 root bin
+f usr/share/man/man9e/probe.9e 0444 root bin
+f usr/share/man/man9e/prop_op.9e 0444 root bin
+f usr/share/man/man9e/put.9e 0444 root bin
+f usr/share/man/man9e/quiesce.9e 0444 root bin
+f usr/share/man/man9e/read.9e 0444 root bin
+f usr/share/man/man9e/segmap.9e 0444 root bin
+f usr/share/man/man9e/srv.9e 0444 root bin
+f usr/share/man/man9e/strategy.9e 0444 root bin
+f usr/share/man/man9e/tran_abort.9e 0444 root bin
+f usr/share/man/man9e/tran_bus_reset.9e 0444 root bin
+s usr/share/man/man9e/tran_destroy_pkt.9e=tran_init_pkt.9e
+f usr/share/man/man9e/tran_dmafree.9e 0444 root bin
+f usr/share/man/man9e/tran_getcap.9e 0444 root bin
+f usr/share/man/man9e/tran_init_pkt.9e 0444 root bin
+s usr/share/man/man9e/tran_pkt_constructor.9e=tran_setup_pkt.9e
+s usr/share/man/man9e/tran_pkt_destructor.9e=tran_setup_pkt.9e
+f usr/share/man/man9e/tran_quiesce.9e 0444 root bin
+f usr/share/man/man9e/tran_reset.9e 0444 root bin
+f usr/share/man/man9e/tran_reset_notify.9e 0444 root bin
+s usr/share/man/man9e/tran_setcap.9e=tran_getcap.9e
+f usr/share/man/man9e/tran_setup_pkt.9e 0444 root bin
+f usr/share/man/man9e/tran_start.9e 0444 root bin
+f usr/share/man/man9e/tran_sync_pkt.9e 0444 root bin
+s usr/share/man/man9e/tran_teardown_pkt.9e=tran_setup_pkt.9e
+f usr/share/man/man9e/tran_tgt_free.9e 0444 root bin
+f usr/share/man/man9e/tran_tgt_init.9e 0444 root bin
+f usr/share/man/man9e/tran_tgt_probe.9e 0444 root bin
+s usr/share/man/man9e/tran_unquiesce.9e=tran_quiesce.9e
+f usr/share/man/man9e/usba_hcdi.9e 0444 root bin
+s usr/share/man/man9e/usba_hcdi_cb_close.9e=usba_hcdi_cb_ops.9e
+s usr/share/man/man9e/usba_hcdi_cb_ioctl.9e=usba_hcdi_cb_ops.9e
+s usr/share/man/man9e/usba_hcdi_cb_open.9e=usba_hcdi_cb_ops.9e
+f usr/share/man/man9e/usba_hcdi_cb_ops.9e 0444 root bin
+f usr/share/man/man9e/usba_hcdi_device_address.9e 0444 root bin
+s usr/share/man/man9e/usba_hcdi_device_fini.9e=usba_hcdi_device_init.9e
+f usr/share/man/man9e/usba_hcdi_device_init.9e 0444 root bin
+f usr/share/man/man9e/usba_hcdi_hub_update.9e 0444 root bin
+f usr/share/man/man9e/usba_hcdi_pipe_bulk_xfer.9e 0444 root bin
+s usr/share/man/man9e/usba_hcdi_pipe_close.9e=usba_hcdi_pipe_open.9e
+f usr/share/man/man9e/usba_hcdi_pipe_ctrl_xfer.9e 0444 root bin
+f usr/share/man/man9e/usba_hcdi_pipe_intr_xfer.9e 0444 root bin
+f usr/share/man/man9e/usba_hcdi_pipe_isoc_xfer.9e 0444 root bin
+f usr/share/man/man9e/usba_hcdi_pipe_open.9e 0444 root bin
+f usr/share/man/man9e/usba_hcdi_pipe_reset.9e 0444 root bin
+f usr/share/man/man9e/usba_hcdi_pipe_stop_intr_polling.9e 0444 root bin
+s usr/share/man/man9e/usba_hcdi_pipe_stop_isoc_polling.9e=usba_hcdi_pipe_stop_intr_polling.9e
+f usr/share/man/man9e/write.9e 0444 root bin
+d usr/share/man/man9f 0755 root bin
+f usr/share/man/man9f/ASSERT.9f 0444 root bin
+s usr/share/man/man9f/AVL_NEXT.9f=avl.9f
+s usr/share/man/man9f/AVL_PREV.9f=avl.9f
+s usr/share/man/man9f/CRED.9f=credentials.9f
+f usr/share/man/man9f/Intro.9f 0444 root bin
+f usr/share/man/man9f/OTHERQ.9f 0444 root bin
+f usr/share/man/man9f/RD.9f 0444 root bin
+f usr/share/man/man9f/SAMESTR.9f 0444 root bin
+s usr/share/man/man9f/SIZEOF_PTR.9f=STRUCT_DECL.9f
+s usr/share/man/man9f/SIZEOF_STRUCT.9f=STRUCT_DECL.9f
+s usr/share/man/man9f/STRUCT_BUF.9f=STRUCT_DECL.9f
+f usr/share/man/man9f/STRUCT_DECL.9f 0444 root bin
+s usr/share/man/man9f/STRUCT_FADDR.9f=STRUCT_DECL.9f
+s usr/share/man/man9f/STRUCT_FGET.9f=STRUCT_DECL.9f
+s usr/share/man/man9f/STRUCT_FGETP.9f=STRUCT_DECL.9f
+s usr/share/man/man9f/STRUCT_FSET.9f=STRUCT_DECL.9f
+s usr/share/man/man9f/STRUCT_FSETP.9f=STRUCT_DECL.9f
+s usr/share/man/man9f/STRUCT_HANDLE.9f=STRUCT_DECL.9f
+s usr/share/man/man9f/STRUCT_INIT.9f=STRUCT_DECL.9f
+s usr/share/man/man9f/STRUCT_SET_HANDLE.9f=STRUCT_DECL.9f
+s usr/share/man/man9f/STRUCT_SIZE.9f=STRUCT_DECL.9f
+f usr/share/man/man9f/WR.9f 0444 root bin
+f usr/share/man/man9f/adjmsg.9f 0444 root bin
+f usr/share/man/man9f/allocb.9f 0444 root bin
+s usr/share/man/man9f/assert.9f=ASSERT.9f
+f usr/share/man/man9f/atomic_add.9f 0444 root bin
+s usr/share/man/man9f/atomic_add_16.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_16_nv.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_32.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_32_nv.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_64.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_64_nv.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_8.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_8_nv.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_char.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_char_nv.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_int.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_int_nv.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_long.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_long_nv.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_ptr.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_ptr_nv.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_short.9f=atomic_add.9f
+s usr/share/man/man9f/atomic_add_short_nv.9f=atomic_add.9f
+f usr/share/man/man9f/atomic_and.9f 0444 root bin
+s usr/share/man/man9f/atomic_and_16.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_16_nv.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_32.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_32_nv.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_64.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_64_nv.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_8.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_8_nv.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_uchar.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_uchar_nv.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_uint.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_uint_nv.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_ulong.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_ulong_nv.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_ushort.9f=atomic_and.9f
+s usr/share/man/man9f/atomic_and_ushort_nv.9f=atomic_and.9f
+f usr/share/man/man9f/atomic_bits.9f 0444 root bin
+f usr/share/man/man9f/atomic_cas.9f 0444 root bin
+s usr/share/man/man9f/atomic_cas_16.9f=atomic_cas.9f
+s usr/share/man/man9f/atomic_cas_32.9f=atomic_cas.9f
+s usr/share/man/man9f/atomic_cas_64.9f=atomic_cas.9f
+s usr/share/man/man9f/atomic_cas_8.9f=atomic_cas.9f
+s usr/share/man/man9f/atomic_cas_ptr.9f=atomic_cas.9f
+s usr/share/man/man9f/atomic_cas_uchar.9f=atomic_cas.9f
+s usr/share/man/man9f/atomic_cas_uint.9f=atomic_cas.9f
+s usr/share/man/man9f/atomic_cas_ulong.9f=atomic_cas.9f
+s usr/share/man/man9f/atomic_cas_ushort.9f=atomic_cas.9f
+s usr/share/man/man9f/atomic_clear_long_excl.9f=atomic_bits.9f
+f usr/share/man/man9f/atomic_dec.9f 0444 root bin
+s usr/share/man/man9f/atomic_dec_16.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_16_nv.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_32.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_32_nv.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_64.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_64_nv.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_8.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_8_nv.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_ptr.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_ptr_nv.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_uchar.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_uchar_nv.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_uint.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_uint_nv.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_ulong.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_ulong_nv.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_ushort.9f=atomic_dec.9f
+s usr/share/man/man9f/atomic_dec_ushort_nv.9f=atomic_dec.9f
+f usr/share/man/man9f/atomic_inc.9f 0444 root bin
+s usr/share/man/man9f/atomic_inc_16.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_16_nv.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_32.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_32_nv.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_64.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_64_nv.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_8.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_8_nv.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_ptr.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_ptr_nv.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_uchar.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_uchar_nv.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_uint.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_uint_nv.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_ulong.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_ulong_nv.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_ushort.9f=atomic_inc.9f
+s usr/share/man/man9f/atomic_inc_ushort_nv.9f=atomic_inc.9f
+f usr/share/man/man9f/atomic_ops.9f 0444 root bin
+f usr/share/man/man9f/atomic_or.9f 0444 root bin
+s usr/share/man/man9f/atomic_or_16.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_16_nv.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_32.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_32_nv.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_64.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_64_nv.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_8.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_8_nv.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_uchar.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_uchar_nv.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_uint.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_uint_nv.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_ulong.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_ulong_nv.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_ushort.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_or_ushort_nv.9f=atomic_or.9f
+s usr/share/man/man9f/atomic_set_long_excl.9f=atomic_bits.9f
+f usr/share/man/man9f/atomic_swap.9f 0444 root bin
+s usr/share/man/man9f/atomic_swap_16.9f=atomic_swap.9f
+s usr/share/man/man9f/atomic_swap_32.9f=atomic_swap.9f
+s usr/share/man/man9f/atomic_swap_64.9f=atomic_swap.9f
+s usr/share/man/man9f/atomic_swap_8.9f=atomic_swap.9f
+s usr/share/man/man9f/atomic_swap_ptr.9f=atomic_swap.9f
+s usr/share/man/man9f/atomic_swap_uchar.9f=atomic_swap.9f
+s usr/share/man/man9f/atomic_swap_uint.9f=atomic_swap.9f
+s usr/share/man/man9f/atomic_swap_ulong.9f=atomic_swap.9f
+s usr/share/man/man9f/atomic_swap_ushort.9f=atomic_swap.9f
+f usr/share/man/man9f/avl.9f 0444 root bin
+s usr/share/man/man9f/avl_add.9f=avl.9f
+s usr/share/man/man9f/avl_create.9f=avl.9f
+s usr/share/man/man9f/avl_destroy.9f=avl.9f
+s usr/share/man/man9f/avl_destroy_nodes.9f=avl.9f
+s usr/share/man/man9f/avl_find.9f=avl.9f
+s usr/share/man/man9f/avl_first.9f=avl.9f
+s usr/share/man/man9f/avl_insert.9f=avl.9f
+s usr/share/man/man9f/avl_insert_here.9f=avl.9f
+s usr/share/man/man9f/avl_is_empty.9f=avl.9f
+s usr/share/man/man9f/avl_last.9f=avl.9f
+s usr/share/man/man9f/avl_nearest.9f=avl.9f
+s usr/share/man/man9f/avl_numnodes.9f=avl.9f
+s usr/share/man/man9f/avl_remove.9f=avl.9f
+s usr/share/man/man9f/avl_swap.9f=avl.9f
+f usr/share/man/man9f/backq.9f 0444 root bin
+f usr/share/man/man9f/bcanput.9f 0444 root bin
+s usr/share/man/man9f/bcanputnext.9f=canputnext.9f
+f usr/share/man/man9f/bcmp.9f 0444 root bin
+f usr/share/man/man9f/bcopy.9f 0444 root bin
+f usr/share/man/man9f/bioclone.9f 0444 root bin
+f usr/share/man/man9f/biodone.9f 0444 root bin
+f usr/share/man/man9f/bioerror.9f 0444 root bin
+f usr/share/man/man9f/biofini.9f 0444 root bin
+f usr/share/man/man9f/bioinit.9f 0444 root bin
+f usr/share/man/man9f/biomodified.9f 0444 root bin
+f usr/share/man/man9f/bioreset.9f 0444 root bin
+f usr/share/man/man9f/biosize.9f 0444 root bin
+f usr/share/man/man9f/biowait.9f 0444 root bin
+f usr/share/man/man9f/bitdel64.9f 0444 root bin
+s usr/share/man/man9f/bitset16.9f=bitset64.9f
+s usr/share/man/man9f/bitset32.9f=bitset64.9f
+f usr/share/man/man9f/bitset64.9f 0444 root bin
+s usr/share/man/man9f/bitset8.9f=bitset64.9f
+s usr/share/man/man9f/bitx16.9f=bitx64.9f
+s usr/share/man/man9f/bitx32.9f=bitx64.9f
+f usr/share/man/man9f/bitx64.9f 0444 root bin
+s usr/share/man/man9f/bitx8.9f=bitx64.9f
+f usr/share/man/man9f/bp_copyin.9f 0444 root bin
+f usr/share/man/man9f/bp_copyout.9f 0444 root bin
+f usr/share/man/man9f/bp_mapin.9f 0444 root bin
+f usr/share/man/man9f/bp_mapout.9f 0444 root bin
+f usr/share/man/man9f/btop.9f 0444 root bin
+f usr/share/man/man9f/btopr.9f 0444 root bin
+f usr/share/man/man9f/bufcall.9f 0444 root bin
+f usr/share/man/man9f/bzero.9f 0444 root bin
+f usr/share/man/man9f/canput.9f 0444 root bin
+f usr/share/man/man9f/canputnext.9f 0444 root bin
+f usr/share/man/man9f/clrbuf.9f 0444 root bin
+f usr/share/man/man9f/cmn_err.9f 0444 root bin
+f usr/share/man/man9f/condvar.9f 0444 root bin
+f usr/share/man/man9f/copyb.9f 0444 root bin
+f usr/share/man/man9f/copyin.9f 0444 root bin
+f usr/share/man/man9f/copymsg.9f 0444 root bin
+f usr/share/man/man9f/copyout.9f 0444 root bin
+f usr/share/man/man9f/credentials.9f 0444 root bin
+s usr/share/man/man9f/crdup.9f=credentials.9f
+s usr/share/man/man9f/crfree.9f=credentials.9f
+s usr/share/man/man9f/crget.9f=credentials.9f
+s usr/share/man/man9f/crgetgid.9f=ddi_cred.9f
+s usr/share/man/man9f/crgetgroups.9f=ddi_cred.9f
+s usr/share/man/man9f/crgetngroups.9f=ddi_cred.9f
+s usr/share/man/man9f/crgetrgid.9f=ddi_cred.9f
+s usr/share/man/man9f/crgetruid.9f=ddi_cred.9f
+s usr/share/man/man9f/crgetsgid.9f=ddi_cred.9f
+s usr/share/man/man9f/crgetsuid.9f=ddi_cred.9f
+s usr/share/man/man9f/crgetuid.9f=ddi_cred.9f
+s usr/share/man/man9f/crgetzoneid.9f=ddi_cred.9f
+s usr/share/man/man9f/crhold.9f=credentials.9f
+f usr/share/man/man9f/csx_AccessConfigurationRegister.9f 0444 root bin
+f usr/share/man/man9f/csx_CS_DDI_Info.9f 0444 root bin
+f usr/share/man/man9f/csx_ConvertSize.9f 0444 root bin
+f usr/share/man/man9f/csx_ConvertSpeed.9f 0444 root bin
+f usr/share/man/man9f/csx_DeregisterClient.9f 0444 root bin
+f usr/share/man/man9f/csx_DupHandle.9f 0444 root bin
+f usr/share/man/man9f/csx_Error2Text.9f 0444 root bin
+f usr/share/man/man9f/csx_Event2Text.9f 0444 root bin
+f usr/share/man/man9f/csx_FreeHandle.9f 0444 root bin
+s usr/share/man/man9f/csx_Get16.9f=csx_Get8.9f
+s usr/share/man/man9f/csx_Get32.9f=csx_Get8.9f
+s usr/share/man/man9f/csx_Get64.9f=csx_Get8.9f
+f usr/share/man/man9f/csx_Get8.9f 0444 root bin
+s usr/share/man/man9f/csx_GetEventMask.9f=csx_SetEventMask.9f
+f usr/share/man/man9f/csx_GetFirstClient.9f 0444 root bin
+f usr/share/man/man9f/csx_GetFirstTuple.9f 0444 root bin
+f usr/share/man/man9f/csx_GetHandleOffset.9f 0444 root bin
+f usr/share/man/man9f/csx_GetMappedAddr.9f 0444 root bin
+s usr/share/man/man9f/csx_GetNextClient.9f=csx_GetFirstClient.9f
+s usr/share/man/man9f/csx_GetNextTuple.9f=csx_GetFirstTuple.9f
+f usr/share/man/man9f/csx_GetStatus.9f 0444 root bin
+f usr/share/man/man9f/csx_GetTupleData.9f 0444 root bin
+f usr/share/man/man9f/csx_MakeDeviceNode.9f 0444 root bin
+f usr/share/man/man9f/csx_MapLogSocket.9f 0444 root bin
+f usr/share/man/man9f/csx_MapMemPage.9f 0444 root bin
+f usr/share/man/man9f/csx_ModifyConfiguration.9f 0444 root bin
+f usr/share/man/man9f/csx_ModifyWindow.9f 0444 root bin
+f usr/share/man/man9f/csx_ParseTuple.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_BATTERY.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_BYTEORDER.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_CFTABLE_ENTRY.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_CONFIG.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_DATE.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_DEVICE.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_DEVICEGEO.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_DEVICEGEO_A.9f 0444 root bin
+s usr/share/man/man9f/csx_Parse_CISTPL_DEVICE_A.9f=csx_Parse_CISTPL_DEVICE.9f
+s usr/share/man/man9f/csx_Parse_CISTPL_DEVICE_OA.9f=csx_Parse_CISTPL_DEVICE.9f
+s usr/share/man/man9f/csx_Parse_CISTPL_DEVICE_OC.9f=csx_Parse_CISTPL_DEVICE.9f
+f usr/share/man/man9f/csx_Parse_CISTPL_FORMAT.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_FUNCE.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_FUNCID.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_GEOMETRY.9f 0444 root bin
+s usr/share/man/man9f/csx_Parse_CISTPL_JEDEC_A.9f=csx_Parse_CISTPL_JEDEC_C.9f
+f usr/share/man/man9f/csx_Parse_CISTPL_JEDEC_C.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_LINKTARGET.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_LONGLINK_A.9f 0444 root bin
+s usr/share/man/man9f/csx_Parse_CISTPL_LONGLINK_C.9f=csx_Parse_CISTPL_LONGLINK_A.9f
+f usr/share/man/man9f/csx_Parse_CISTPL_LONGLINK_MFC.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_MANFID.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_ORG.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_SPCL.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_SWIL.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_VERS_1.9f 0444 root bin
+f usr/share/man/man9f/csx_Parse_CISTPL_VERS_2.9f 0444 root bin
+s usr/share/man/man9f/csx_Put16.9f=csx_Put8.9f
+s usr/share/man/man9f/csx_Put32.9f=csx_Put8.9f
+s usr/share/man/man9f/csx_Put64.9f=csx_Put8.9f
+f usr/share/man/man9f/csx_Put8.9f 0444 root bin
+f usr/share/man/man9f/csx_RegisterClient.9f 0444 root bin
+f usr/share/man/man9f/csx_ReleaseConfiguration.9f 0444 root bin
+s usr/share/man/man9f/csx_ReleaseIO.9f=csx_RequestIO.9f
+s usr/share/man/man9f/csx_ReleaseIRQ.9f=csx_RequestIRQ.9f
+s usr/share/man/man9f/csx_ReleaseSocketMask.9f=csx_RequestSocketMask.9f
+s usr/share/man/man9f/csx_ReleaseWindow.9f=csx_RequestWindow.9f
+s usr/share/man/man9f/csx_RemoveDeviceNode.9f=csx_MakeDeviceNode.9f
+s usr/share/man/man9f/csx_RepGet16.9f=csx_RepGet8.9f
+s usr/share/man/man9f/csx_RepGet32.9f=csx_RepGet8.9f
+s usr/share/man/man9f/csx_RepGet64.9f=csx_RepGet8.9f
+f usr/share/man/man9f/csx_RepGet8.9f 0444 root bin
+s usr/share/man/man9f/csx_RepPut16.9f=csx_RepPut8.9f
+s usr/share/man/man9f/csx_RepPut32.9f=csx_RepPut8.9f
+s usr/share/man/man9f/csx_RepPut64.9f=csx_RepPut8.9f
+f usr/share/man/man9f/csx_RepPut8.9f 0444 root bin
+f usr/share/man/man9f/csx_RequestConfiguration.9f 0444 root bin
+f usr/share/man/man9f/csx_RequestIO.9f 0444 root bin
+f usr/share/man/man9f/csx_RequestIRQ.9f 0444 root bin
+f usr/share/man/man9f/csx_RequestSocketMask.9f 0444 root bin
+f usr/share/man/man9f/csx_RequestWindow.9f 0444 root bin
+f usr/share/man/man9f/csx_ResetFunction.9f 0444 root bin
+f usr/share/man/man9f/csx_SetEventMask.9f 0444 root bin
+f usr/share/man/man9f/csx_SetHandleOffset.9f 0444 root bin
+f usr/share/man/man9f/csx_ValidateCIS.9f 0444 root bin
+s usr/share/man/man9f/cv_broadcast.9f=condvar.9f
+s usr/share/man/man9f/cv_destroy.9f=condvar.9f
+s usr/share/man/man9f/cv_init.9f=condvar.9f
+s usr/share/man/man9f/cv_reltimedwait.9f=condvar.9f
+s usr/share/man/man9f/cv_reltimedwait_sig.9f=condvar.9f
+s usr/share/man/man9f/cv_signal.9f=condvar.9f
+s usr/share/man/man9f/cv_timedwait.9f=condvar.9f
+s usr/share/man/man9f/cv_timedwait_sig.9f=condvar.9f
+s usr/share/man/man9f/cv_wait.9f=condvar.9f
+s usr/share/man/man9f/cv_wait_sig.9f=condvar.9f
+f usr/share/man/man9f/datamsg.9f 0444 root bin
+f usr/share/man/man9f/ddi_add_event_handler.9f 0444 root bin
+f usr/share/man/man9f/ddi_add_intr.9f 0444 root bin
+f usr/share/man/man9f/ddi_add_softintr.9f 0444 root bin
+f usr/share/man/man9f/ddi_binding_name.9f 0444 root bin
+f usr/share/man/man9f/ddi_btop.9f 0444 root bin
+s usr/share/man/man9f/ddi_btopr.9f=ddi_btop.9f
+f usr/share/man/man9f/ddi_can_receive_sig.9f 0444 root bin
+f usr/share/man/man9f/ddi_cb_register.9f 0444 root bin
+s usr/share/man/man9f/ddi_cb_unregister.9f=ddi_cb_register.9f
+f usr/share/man/man9f/ddi_check_acc_handle.9f 0444 root bin
+s usr/share/man/man9f/ddi_check_dma_handle.9f=ddi_check_acc_handle.9f
+f usr/share/man/man9f/ddi_copyin.9f 0444 root bin
+f usr/share/man/man9f/ddi_copyout.9f 0444 root bin
+f usr/share/man/man9f/ddi_create_minor_node.9f 0444 root bin
+f usr/share/man/man9f/ddi_cred.9f 0444 root bin
+f usr/share/man/man9f/ddi_dev_is_needed.9f 0444 root bin
+f usr/share/man/man9f/ddi_dev_is_sid.9f 0444 root bin
+f usr/share/man/man9f/ddi_dev_nintrs.9f 0444 root bin
+f usr/share/man/man9f/ddi_dev_nregs.9f 0444 root bin
+f usr/share/man/man9f/ddi_dev_regsize.9f 0444 root bin
+f usr/share/man/man9f/ddi_dev_report_fault.9f 0444 root bin
+f usr/share/man/man9f/ddi_device_copy.9f 0444 root bin
+f usr/share/man/man9f/ddi_device_zero.9f 0444 root bin
+f usr/share/man/man9f/ddi_devid_compare.9f 0444 root bin
+s usr/share/man/man9f/ddi_devid_free.9f=ddi_devid_compare.9f
+s usr/share/man/man9f/ddi_devid_get.9f=ddi_devid_compare.9f
+s usr/share/man/man9f/ddi_devid_init.9f=ddi_devid_compare.9f
+s usr/share/man/man9f/ddi_devid_register.9f=ddi_devid_compare.9f
+s usr/share/man/man9f/ddi_devid_sizeof.9f=ddi_devid_compare.9f
+s usr/share/man/man9f/ddi_devid_str_decode.9f=ddi_devid_compare.9f
+s usr/share/man/man9f/ddi_devid_str_encode.9f=ddi_devid_compare.9f
+s usr/share/man/man9f/ddi_devid_str_free.9f=ddi_devid_compare.9f
+s usr/share/man/man9f/ddi_devid_unregister.9f=ddi_devid_compare.9f
+s usr/share/man/man9f/ddi_devid_valid.9f=ddi_devid_compare.9f
+s usr/share/man/man9f/ddi_devmap_segmap.9f=devmap_setup.9f
+f usr/share/man/man9f/ddi_dma_addr_bind_handle.9f 0444 root bin
+f usr/share/man/man9f/ddi_dma_alloc_handle.9f 0444 root bin
+f usr/share/man/man9f/ddi_dma_buf_bind_handle.9f 0444 root bin
+f usr/share/man/man9f/ddi_dma_burstsizes.9f 0444 root bin
+f usr/share/man/man9f/ddi_dma_cookie_iter.9f 0444 root bin
+s usr/share/man/man9f/ddi_dma_cookie_get.9f=ddi_dma_cookie_iter.9f
+s usr/share/man/man9f/ddi_dma_cookie_one.9f=ddi_dma_cookie_iter.9f
+f usr/share/man/man9f/ddi_dma_free_handle.9f 0444 root bin
+f usr/share/man/man9f/ddi_dma_getwin.9f 0444 root bin
+f usr/share/man/man9f/ddi_dma_mem_alloc.9f 0444 root bin
+f usr/share/man/man9f/ddi_dma_mem_free.9f 0444 root bin
+s usr/share/man/man9f/ddi_dma_ncookies.9f=ddi_dma_cookie_iter.9f
+s usr/share/man/man9f/ddi_dma_nextcookie.9f=ddi_dma_cookie_iter.9f
+f usr/share/man/man9f/ddi_dma_numwin.9f 0444 root bin
+f usr/share/man/man9f/ddi_dma_set_sbus64.9f 0444 root bin
+f usr/share/man/man9f/ddi_dma_sync.9f 0444 root bin
+f usr/share/man/man9f/ddi_dma_unbind_handle.9f 0444 root bin
+f usr/share/man/man9f/ddi_dmae.9f 0444 root bin
+s usr/share/man/man9f/ddi_dmae_1stparty.9f=ddi_dmae.9f
+s usr/share/man/man9f/ddi_dmae_alloc.9f=ddi_dmae.9f
+s usr/share/man/man9f/ddi_dmae_disable.9f=ddi_dmae.9f
+s usr/share/man/man9f/ddi_dmae_enable.9f=ddi_dmae.9f
+s usr/share/man/man9f/ddi_dmae_getattr.9f=ddi_dmae.9f
+s usr/share/man/man9f/ddi_dmae_getcnt.9f=ddi_dmae.9f
+s usr/share/man/man9f/ddi_dmae_getlim.9f=ddi_dmae.9f
+s usr/share/man/man9f/ddi_dmae_prog.9f=ddi_dmae.9f
+s usr/share/man/man9f/ddi_dmae_release.9f=ddi_dmae.9f
+s usr/share/man/man9f/ddi_dmae_stop.9f=ddi_dmae.9f
+f usr/share/man/man9f/ddi_driver_major.9f 0444 root bin
+f usr/share/man/man9f/ddi_driver_name.9f 0444 root bin
+f usr/share/man/man9f/ddi_enter_critical.9f 0444 root bin
+s usr/share/man/man9f/ddi_exit_critical.9f=ddi_enter_critical.9f
+f usr/share/man/man9f/ddi_ffs.9f 0444 root bin
+s usr/share/man/man9f/ddi_ffsll.9f=ddi_ffs.9f
+s usr/share/man/man9f/ddi_fls.9f=ddi_ffs.9f
+s usr/share/man/man9f/ddi_flsll.9f=ddi_ffs.9f
+f usr/share/man/man9f/ddi_fm_acc_err_clear.9f 0444 root bin
+f usr/share/man/man9f/ddi_fm_acc_err_get.9f 0444 root bin
+s usr/share/man/man9f/ddi_fm_capable.9f=ddi_fm_init.9f
+s usr/share/man/man9f/ddi_fm_dma_err_clear.9f=ddi_fm_acc_err_clear.9f
+s usr/share/man/man9f/ddi_fm_dma_err_get.9f=ddi_fm_acc_err_get.9f
+f usr/share/man/man9f/ddi_fm_ereport_post.9f 0444 root bin
+s usr/share/man/man9f/ddi_fm_fini.9f=ddi_fm_init.9f
+f usr/share/man/man9f/ddi_fm_handler_register.9f 0444 root bin
+s usr/share/man/man9f/ddi_fm_handler_unregister.9f=ddi_fm_handler_register.9f
+f usr/share/man/man9f/ddi_fm_init.9f 0444 root bin
+f usr/share/man/man9f/ddi_fm_service_impact.9f 0444 root bin
+s usr/share/man/man9f/ddi_get16.9f=ddi_get8.9f
+s usr/share/man/man9f/ddi_get32.9f=ddi_get8.9f
+s usr/share/man/man9f/ddi_get64.9f=ddi_get8.9f
+f usr/share/man/man9f/ddi_get8.9f 0444 root bin
+f usr/share/man/man9f/ddi_get_cred.9f 0444 root bin
+f usr/share/man/man9f/ddi_get_devstate.9f 0444 root bin
+f usr/share/man/man9f/ddi_get_driver_private.9f 0444 root bin
+f usr/share/man/man9f/ddi_get_eventcookie.9f 0444 root bin
+s usr/share/man/man9f/ddi_get_iblock_cookie.9f=ddi_add_intr.9f
+f usr/share/man/man9f/ddi_get_instance.9f 0444 root bin
+f usr/share/man/man9f/ddi_get_kt_did.9f 0444 root bin
+f usr/share/man/man9f/ddi_get_lbolt.9f 0444 root bin
+s usr/share/man/man9f/ddi_get_lbolt64.9f=ddi_get_lbolt.9f
+s usr/share/man/man9f/ddi_get_name.9f=ddi_binding_name.9f
+f usr/share/man/man9f/ddi_get_parent.9f 0444 root bin
+f usr/share/man/man9f/ddi_get_pid.9f 0444 root bin
+s usr/share/man/man9f/ddi_get_soft_iblock_cookie.9f=ddi_add_softintr.9f
+s usr/share/man/man9f/ddi_get_soft_state.9f=ddi_soft_state.9f
+f usr/share/man/man9f/ddi_get_time.9f 0444 root bin
+s usr/share/man/man9f/ddi_getb.9f=ddi_get8.9f
+s usr/share/man/man9f/ddi_getl.9f=ddi_get8.9f
+s usr/share/man/man9f/ddi_getll.9f=ddi_get8.9f
+s usr/share/man/man9f/ddi_getlongprop.9f=ddi_prop_op.9f
+s usr/share/man/man9f/ddi_getlongprop_buf.9f=ddi_prop_op.9f
+s usr/share/man/man9f/ddi_getprop.9f=ddi_prop_op.9f
+s usr/share/man/man9f/ddi_getproplen.9f=ddi_prop_op.9f
+s usr/share/man/man9f/ddi_getw.9f=ddi_get8.9f
+f usr/share/man/man9f/ddi_in_panic.9f 0444 root bin
+f usr/share/man/man9f/ddi_intr_add_handler.9f 0444 root bin
+f usr/share/man/man9f/ddi_intr_add_softint.9f 0444 root bin
+f usr/share/man/man9f/ddi_intr_alloc.9f 0444 root bin
+s usr/share/man/man9f/ddi_intr_block_disable.9f=ddi_intr_enable.9f
+s usr/share/man/man9f/ddi_intr_block_enable.9f=ddi_intr_enable.9f
+s usr/share/man/man9f/ddi_intr_clr_mask.9f=ddi_intr_set_mask.9f
+s usr/share/man/man9f/ddi_intr_disable.9f=ddi_intr_enable.9f
+f usr/share/man/man9f/ddi_intr_dup_handler.9f 0444 root bin
+f usr/share/man/man9f/ddi_intr_enable.9f 0444 root bin
+s usr/share/man/man9f/ddi_intr_free.9f=ddi_intr_alloc.9f
+f usr/share/man/man9f/ddi_intr_get_cap.9f 0444 root bin
+f usr/share/man/man9f/ddi_intr_get_hilevel_pri.9f 0444 root bin
+s usr/share/man/man9f/ddi_intr_get_navail.9f=ddi_intr_get_nintrs.9f
+f usr/share/man/man9f/ddi_intr_get_nintrs.9f 0444 root bin
+f usr/share/man/man9f/ddi_intr_get_pending.9f 0444 root bin
+f usr/share/man/man9f/ddi_intr_get_pri.9f 0444 root bin
+s usr/share/man/man9f/ddi_intr_get_softint_pri.9f=ddi_intr_add_softint.9f
+f usr/share/man/man9f/ddi_intr_get_supported_types.9f 0444 root bin
+f usr/share/man/man9f/ddi_intr_hilevel.9f 0444 root bin
+s usr/share/man/man9f/ddi_intr_remove_handler.9f=ddi_intr_add_handler.9f
+s usr/share/man/man9f/ddi_intr_remove_softint.9f=ddi_intr_add_softint.9f
+s usr/share/man/man9f/ddi_intr_set_cap.9f=ddi_intr_get_cap.9f
+f usr/share/man/man9f/ddi_intr_set_mask.9f 0444 root bin
+f usr/share/man/man9f/ddi_intr_set_nreq.9f 0444 root bin
+s usr/share/man/man9f/ddi_intr_set_pri.9f=ddi_intr_get_pri.9f
+s usr/share/man/man9f/ddi_intr_set_softint_pri.9f=ddi_intr_add_softint.9f
+s usr/share/man/man9f/ddi_intr_trigger_softint.9f=ddi_intr_add_softint.9f
+s usr/share/man/man9f/ddi_io_get16.9f=ddi_io_get8.9f
+s usr/share/man/man9f/ddi_io_get32.9f=ddi_io_get8.9f
+f usr/share/man/man9f/ddi_io_get8.9f 0444 root bin
+s usr/share/man/man9f/ddi_io_getb.9f=ddi_io_get8.9f
+s usr/share/man/man9f/ddi_io_getl.9f=ddi_io_get8.9f
+s usr/share/man/man9f/ddi_io_getw.9f=ddi_io_get8.9f
+s usr/share/man/man9f/ddi_io_put16.9f=ddi_io_put8.9f
+s usr/share/man/man9f/ddi_io_put32.9f=ddi_io_put8.9f
+f usr/share/man/man9f/ddi_io_put8.9f 0444 root bin
+s usr/share/man/man9f/ddi_io_putb.9f=ddi_io_put8.9f
+s usr/share/man/man9f/ddi_io_putl.9f=ddi_io_put8.9f
+s usr/share/man/man9f/ddi_io_putw.9f=ddi_io_put8.9f
+s usr/share/man/man9f/ddi_io_rep_get16.9f=ddi_io_rep_get8.9f
+s usr/share/man/man9f/ddi_io_rep_get32.9f=ddi_io_rep_get8.9f
+f usr/share/man/man9f/ddi_io_rep_get8.9f 0444 root bin
+s usr/share/man/man9f/ddi_io_rep_getb.9f=ddi_io_rep_get8.9f
+s usr/share/man/man9f/ddi_io_rep_getl.9f=ddi_io_rep_get8.9f
+s usr/share/man/man9f/ddi_io_rep_getw.9f=ddi_io_rep_get8.9f
+s usr/share/man/man9f/ddi_io_rep_put16.9f=ddi_io_rep_put8.9f
+s usr/share/man/man9f/ddi_io_rep_put32.9f=ddi_io_rep_put8.9f
+f usr/share/man/man9f/ddi_io_rep_put8.9f 0444 root bin
+s usr/share/man/man9f/ddi_io_rep_putb.9f=ddi_io_rep_put8.9f
+s usr/share/man/man9f/ddi_io_rep_putl.9f=ddi_io_rep_put8.9f
+s usr/share/man/man9f/ddi_io_rep_putw.9f=ddi_io_rep_put8.9f
+f usr/share/man/man9f/ddi_log_sysevent.9f 0444 root bin
+f usr/share/man/man9f/ddi_map_regs.9f 0444 root bin
+s usr/share/man/man9f/ddi_mem_get16.9f=ddi_mem_get8.9f
+s usr/share/man/man9f/ddi_mem_get32.9f=ddi_mem_get8.9f
+s usr/share/man/man9f/ddi_mem_get64.9f=ddi_mem_get8.9f
+f usr/share/man/man9f/ddi_mem_get8.9f 0444 root bin
+s usr/share/man/man9f/ddi_mem_getb.9f=ddi_mem_get8.9f
+s usr/share/man/man9f/ddi_mem_getl.9f=ddi_mem_get8.9f
+s usr/share/man/man9f/ddi_mem_getll.9f=ddi_mem_get8.9f
+s usr/share/man/man9f/ddi_mem_getw.9f=ddi_mem_get8.9f
+s usr/share/man/man9f/ddi_mem_put16.9f=ddi_mem_put8.9f
+s usr/share/man/man9f/ddi_mem_put32.9f=ddi_mem_put8.9f
+s usr/share/man/man9f/ddi_mem_put64.9f=ddi_mem_put8.9f
+f usr/share/man/man9f/ddi_mem_put8.9f 0444 root bin
+s usr/share/man/man9f/ddi_mem_putb.9f=ddi_mem_put8.9f
+s usr/share/man/man9f/ddi_mem_putl.9f=ddi_mem_put8.9f
+s usr/share/man/man9f/ddi_mem_putll.9f=ddi_mem_put8.9f
+s usr/share/man/man9f/ddi_mem_putw.9f=ddi_mem_put8.9f
+s usr/share/man/man9f/ddi_mem_rep_get16.9f=ddi_mem_rep_get8.9f
+s usr/share/man/man9f/ddi_mem_rep_get32.9f=ddi_mem_rep_get8.9f
+s usr/share/man/man9f/ddi_mem_rep_get64.9f=ddi_mem_rep_get8.9f
+f usr/share/man/man9f/ddi_mem_rep_get8.9f 0444 root bin
+s usr/share/man/man9f/ddi_mem_rep_getb.9f=ddi_mem_rep_get8.9f
+s usr/share/man/man9f/ddi_mem_rep_getl.9f=ddi_mem_rep_get8.9f
+s usr/share/man/man9f/ddi_mem_rep_getll.9f=ddi_mem_rep_get8.9f
+s usr/share/man/man9f/ddi_mem_rep_getw.9f=ddi_mem_rep_get8.9f
+s usr/share/man/man9f/ddi_mem_rep_put16.9f=ddi_mem_rep_put8.9f
+s usr/share/man/man9f/ddi_mem_rep_put32.9f=ddi_mem_rep_put8.9f
+s usr/share/man/man9f/ddi_mem_rep_put64.9f=ddi_mem_rep_put8.9f
+f usr/share/man/man9f/ddi_mem_rep_put8.9f 0444 root bin
+s usr/share/man/man9f/ddi_mem_rep_putb.9f=ddi_mem_rep_put8.9f
+s usr/share/man/man9f/ddi_mem_rep_putl.9f=ddi_mem_rep_put8.9f
+s usr/share/man/man9f/ddi_mem_rep_putll.9f=ddi_mem_rep_put8.9f
+s usr/share/man/man9f/ddi_mem_rep_putw.9f=ddi_mem_rep_put8.9f
+f usr/share/man/man9f/ddi_mmap_get_model.9f 0444 root bin
+s usr/share/man/man9f/ddi_modclose.9f=ddi_modopen.9f
+f usr/share/man/man9f/ddi_model_convert_from.9f 0444 root bin
+f usr/share/man/man9f/ddi_modopen.9f 0444 root bin
+s usr/share/man/man9f/ddi_modsym.9f=ddi_modopen.9f
+f usr/share/man/man9f/ddi_no_info.9f 0444 root bin
+f usr/share/man/man9f/ddi_node_name.9f 0444 root bin
+f usr/share/man/man9f/ddi_peek.9f 0444 root bin
+s usr/share/man/man9f/ddi_peek16.9f=ddi_peek.9f
+s usr/share/man/man9f/ddi_peek32.9f=ddi_peek.9f
+s usr/share/man/man9f/ddi_peek64.9f=ddi_peek.9f
+s usr/share/man/man9f/ddi_peek8.9f=ddi_peek.9f
+s usr/share/man/man9f/ddi_peekc.9f=ddi_peek.9f
+s usr/share/man/man9f/ddi_peekd.9f=ddi_peek.9f
+s usr/share/man/man9f/ddi_peekl.9f=ddi_peek.9f
+s usr/share/man/man9f/ddi_peeks.9f=ddi_peek.9f
+f usr/share/man/man9f/ddi_periodic_add.9f 0444 root bin
+f usr/share/man/man9f/ddi_periodic_delete.9f 0444 root bin
+f usr/share/man/man9f/ddi_poke.9f 0444 root bin
+s usr/share/man/man9f/ddi_poke16.9f=ddi_poke.9f
+s usr/share/man/man9f/ddi_poke32.9f=ddi_poke.9f
+s usr/share/man/man9f/ddi_poke64.9f=ddi_poke.9f
+s usr/share/man/man9f/ddi_poke8.9f=ddi_poke.9f
+s usr/share/man/man9f/ddi_pokec.9f=ddi_poke.9f
+s usr/share/man/man9f/ddi_poked.9f=ddi_poke.9f
+s usr/share/man/man9f/ddi_pokel.9f=ddi_poke.9f
+s usr/share/man/man9f/ddi_pokes.9f=ddi_poke.9f
+f usr/share/man/man9f/ddi_prop_create.9f 0444 root bin
+f usr/share/man/man9f/ddi_prop_exists.9f 0444 root bin
+s usr/share/man/man9f/ddi_prop_free.9f=ddi_prop_lookup.9f
+f usr/share/man/man9f/ddi_prop_get_int.9f 0444 root bin
+s usr/share/man/man9f/ddi_prop_get_int64.9f=ddi_prop_get_int.9f
+f usr/share/man/man9f/ddi_prop_lookup.9f 0444 root bin
+s usr/share/man/man9f/ddi_prop_lookup_byte_array.9f=ddi_prop_lookup.9f
+s usr/share/man/man9f/ddi_prop_lookup_int64_array.9f=ddi_prop_lookup.9f
+s usr/share/man/man9f/ddi_prop_lookup_int_array.9f=ddi_prop_lookup.9f
+s usr/share/man/man9f/ddi_prop_lookup_string.9f=ddi_prop_lookup.9f
+s usr/share/man/man9f/ddi_prop_lookup_string_array.9f=ddi_prop_lookup.9f
+s usr/share/man/man9f/ddi_prop_modify.9f=ddi_prop_create.9f
+f usr/share/man/man9f/ddi_prop_op.9f 0444 root bin
+s usr/share/man/man9f/ddi_prop_remove.9f=ddi_prop_create.9f
+s usr/share/man/man9f/ddi_prop_remove_all.9f=ddi_prop_create.9f
+s usr/share/man/man9f/ddi_prop_undefine.9f=ddi_prop_create.9f
+f usr/share/man/man9f/ddi_prop_update.9f 0444 root bin
+s usr/share/man/man9f/ddi_prop_update_byte_array.9f=ddi_prop_update.9f
+s usr/share/man/man9f/ddi_prop_update_int.9f=ddi_prop_update.9f
+s usr/share/man/man9f/ddi_prop_update_int64.9f=ddi_prop_update.9f
+s usr/share/man/man9f/ddi_prop_update_int64_array.9f=ddi_prop_update.9f
+s usr/share/man/man9f/ddi_prop_update_int_array.9f=ddi_prop_update.9f
+s usr/share/man/man9f/ddi_prop_update_string.9f=ddi_prop_update.9f
+s usr/share/man/man9f/ddi_prop_update_string_array.9f=ddi_prop_update.9f
+s usr/share/man/man9f/ddi_ptob.9f=ddi_btop.9f
+s usr/share/man/man9f/ddi_put16.9f=ddi_put8.9f
+s usr/share/man/man9f/ddi_put32.9f=ddi_put8.9f
+s usr/share/man/man9f/ddi_put64.9f=ddi_put8.9f
+f usr/share/man/man9f/ddi_put8.9f 0444 root bin
+s usr/share/man/man9f/ddi_putb.9f=ddi_put8.9f
+s usr/share/man/man9f/ddi_putl.9f=ddi_put8.9f
+s usr/share/man/man9f/ddi_putll.9f=ddi_put8.9f
+s usr/share/man/man9f/ddi_putw.9f=ddi_put8.9f
+f usr/share/man/man9f/ddi_regs_map_free.9f 0444 root bin
+f usr/share/man/man9f/ddi_regs_map_setup.9f 0444 root bin
+f usr/share/man/man9f/ddi_remove_event_handler.9f 0444 root bin
+s usr/share/man/man9f/ddi_remove_intr.9f=ddi_add_intr.9f
+f usr/share/man/man9f/ddi_remove_minor_node.9f 0444 root bin
+s usr/share/man/man9f/ddi_remove_softintr.9f=ddi_add_softintr.9f
+f usr/share/man/man9f/ddi_removing_power.9f 0444 root bin
+s usr/share/man/man9f/ddi_rep_get16.9f=ddi_rep_get8.9f
+s usr/share/man/man9f/ddi_rep_get32.9f=ddi_rep_get8.9f
+s usr/share/man/man9f/ddi_rep_get64.9f=ddi_rep_get8.9f
+f usr/share/man/man9f/ddi_rep_get8.9f 0444 root bin
+s usr/share/man/man9f/ddi_rep_getb.9f=ddi_rep_get8.9f
+s usr/share/man/man9f/ddi_rep_getl.9f=ddi_rep_get8.9f
+s usr/share/man/man9f/ddi_rep_getll.9f=ddi_rep_get8.9f
+s usr/share/man/man9f/ddi_rep_getw.9f=ddi_rep_get8.9f
+s usr/share/man/man9f/ddi_rep_put16.9f=ddi_rep_put8.9f
+s usr/share/man/man9f/ddi_rep_put32.9f=ddi_rep_put8.9f
+s usr/share/man/man9f/ddi_rep_put64.9f=ddi_rep_put8.9f
+f usr/share/man/man9f/ddi_rep_put8.9f 0444 root bin
+s usr/share/man/man9f/ddi_rep_putb.9f=ddi_rep_put8.9f
+s usr/share/man/man9f/ddi_rep_putl.9f=ddi_rep_put8.9f
+s usr/share/man/man9f/ddi_rep_putll.9f=ddi_rep_put8.9f
+s usr/share/man/man9f/ddi_rep_putw.9f=ddi_rep_put8.9f
+f usr/share/man/man9f/ddi_report_dev.9f 0444 root bin
+f usr/share/man/man9f/ddi_root_node.9f 0444 root bin
+f usr/share/man/man9f/ddi_segmap.9f 0444 root bin
+s usr/share/man/man9f/ddi_segmap_setup.9f=ddi_segmap.9f
+s usr/share/man/man9f/ddi_set_driver_private.9f=ddi_get_driver_private.9f
+f usr/share/man/man9f/ddi_slaveonly.9f 0444 root bin
+f usr/share/man/man9f/ddi_soft_state.9f 0444 root bin
+s usr/share/man/man9f/ddi_soft_state_fini.9f=ddi_soft_state.9f
+s usr/share/man/man9f/ddi_soft_state_free.9f=ddi_soft_state.9f
+s usr/share/man/man9f/ddi_soft_state_init.9f=ddi_soft_state.9f
+s usr/share/man/man9f/ddi_soft_state_zalloc.9f=ddi_soft_state.9f
+s usr/share/man/man9f/ddi_strdup.9f=string.9f
+f usr/share/man/man9f/ddi_strtol.9f 0444 root bin
+f usr/share/man/man9f/ddi_strtoll.9f 0444 root bin
+f usr/share/man/man9f/ddi_strtoul.9f 0444 root bin
+s usr/share/man/man9f/ddi_strtoull.9f=ddi_strtoll.9f
+s usr/share/man/man9f/ddi_taskq_create.9f=taskq.9f
+s usr/share/man/man9f/ddi_taskq_destroy.9f=taskq.9f
+s usr/share/man/man9f/ddi_taskq_dispatch.9f=taskq.9f
+s usr/share/man/man9f/ddi_taskq_resume.9f=taskq.9f
+s usr/share/man/man9f/ddi_taskq_suspend.9f=taskq.9f
+s usr/share/man/man9f/ddi_taskq_wait.9f=taskq.9f
+s usr/share/man/man9f/ddi_trigger_softintr.9f=ddi_add_softintr.9f
+f usr/share/man/man9f/ddi_ufm.9f 0444 root bin
+s usr/share/man/man9f/ddi_ufm_fini.9f=ddi_ufm.9f
+f usr/share/man/man9f/ddi_ufm_image.9f 0444 root bin
+s usr/share/man/man9f/ddi_ufm_image_set_desc.9f=ddi_ufm_image.9f
+s usr/share/man/man9f/ddi_ufm_image_set_misc.9f=ddi_ufm_image.9f
+s usr/share/man/man9f/ddi_ufm_image_set_nslots.9f=ddi_ufm_image.9f
+s usr/share/man/man9f/ddi_ufm_init.9f=ddi_ufm.9f
+f usr/share/man/man9f/ddi_ufm_slot.9f 0444 root bin
+s usr/share/man/man9f/ddi_ufm_slot_set_attrs.9f=ddi_ufm_slot.9f
+s usr/share/man/man9f/ddi_ufm_slot_set_imgsize.9f=ddi_ufm_slot.9f
+s usr/share/man/man9f/ddi_ufm_slot_set_misc.9f=ddi_ufm_slot.9f
+s usr/share/man/man9f/ddi_ufm_slot_set_version.9f=ddi_ufm_slot.9f
+s usr/share/man/man9f/ddi_ufm_update.9f=ddi_ufm.9f
+f usr/share/man/man9f/ddi_umem_alloc.9f 0444 root bin
+s usr/share/man/man9f/ddi_umem_free.9f=ddi_umem_alloc.9f
+f usr/share/man/man9f/ddi_umem_iosetup.9f 0444 root bin
+f usr/share/man/man9f/ddi_umem_lock.9f 0444 root bin
+s usr/share/man/man9f/ddi_umem_unlock.9f=ddi_umem_lock.9f
+s usr/share/man/man9f/ddi_unmap_regs.9f=ddi_map_regs.9f
+f usr/share/man/man9f/delay.9f 0444 root bin
+s usr/share/man/man9f/desballoc.9f=esballoc.9f
+s usr/share/man/man9f/desballoca.9f=esballoc.9f
+s usr/share/man/man9f/dev_err.9f=cmn_err.9f
+f usr/share/man/man9f/devfs_clean.9f 0444 root bin
+f usr/share/man/man9f/devmap_default_access.9f 0444 root bin
+f usr/share/man/man9f/devmap_devmem_setup.9f 0444 root bin
+f usr/share/man/man9f/devmap_do_ctxmgt.9f 0444 root bin
+s usr/share/man/man9f/devmap_load.9f=devmap_unload.9f
+f usr/share/man/man9f/devmap_set_ctx_timeout.9f 0444 root bin
+f usr/share/man/man9f/devmap_setup.9f 0444 root bin
+s usr/share/man/man9f/devmap_umem_setup.9f=devmap_devmem_setup.9f
+f usr/share/man/man9f/devmap_unload.9f 0444 root bin
+f usr/share/man/man9f/disksort.9f 0444 root bin
+f usr/share/man/man9f/dlbindack.9f 0444 root bin
+s usr/share/man/man9f/dlerrorack.9f=dlbindack.9f
+s usr/share/man/man9f/dlokack.9f=dlbindack.9f
+s usr/share/man/man9f/dlphysaddrack.9f=dlbindack.9f
+s usr/share/man/man9f/dluderrorind.9f=dlbindack.9f
+f usr/share/man/man9f/drv_getparm.9f 0444 root bin
+f usr/share/man/man9f/drv_hztousec.9f 0444 root bin
+f usr/share/man/man9f/drv_priv.9f 0444 root bin
+f usr/share/man/man9f/drv_usectohz.9f 0444 root bin
+f usr/share/man/man9f/drv_usecwait.9f 0444 root bin
+f usr/share/man/man9f/dupb.9f 0444 root bin
+f usr/share/man/man9f/dupmsg.9f 0444 root bin
+f usr/share/man/man9f/enableok.9f 0444 root bin
+f usr/share/man/man9f/esballoc.9f 0444 root bin
+f usr/share/man/man9f/esbbcall.9f 0444 root bin
+s usr/share/man/man9f/esballoca.9f=esballoc.9f
+f usr/share/man/man9f/firmload.9f 0444 root bin
+s usr/share/man/man9f/firmware_close.9f=firmload.9f
+s usr/share/man/man9f/firmware_free.9f=firmload.9f
+s usr/share/man/man9f/firmware_get_size.9f=firmload.9f
+s usr/share/man/man9f/firmware_malloc.9f=firmload.9f
+s usr/share/man/man9f/firmware_open.9f=firmload.9f
+s usr/share/man/man9f/firmware_read.9f=firmload.9f
+f usr/share/man/man9f/flushband.9f 0444 root bin
+f usr/share/man/man9f/flushq.9f 0444 root bin
+s usr/share/man/man9f/free_pktiopb.9f=get_pktiopb.9f
+f usr/share/man/man9f/freeb.9f 0444 root bin
+f usr/share/man/man9f/freemsg.9f 0444 root bin
+f usr/share/man/man9f/freerbuf.9f 0444 root bin
+f usr/share/man/man9f/freezestr.9f 0444 root bin
+f usr/share/man/man9f/get_pktiopb.9f 0444 root bin
+f usr/share/man/man9f/geterror.9f 0444 root bin
+f usr/share/man/man9f/gethrtime.9f 0444 root bin
+f usr/share/man/man9f/getmajor.9f 0444 root bin
+f usr/share/man/man9f/getminor.9f 0444 root bin
+f usr/share/man/man9f/getq.9f 0444 root bin
+f usr/share/man/man9f/getrbuf.9f 0444 root bin
+f usr/share/man/man9f/gld.9f 0444 root bin
+s usr/share/man/man9f/gld_intr.9f=gld.9f
+s usr/share/man/man9f/gld_mac_alloc.9f=gld.9f
+s usr/share/man/man9f/gld_mac_free.9f=gld.9f
+s usr/share/man/man9f/gld_recv.9f=gld.9f
+s usr/share/man/man9f/gld_register.9f=gld.9f
+s usr/share/man/man9f/gld_sched.9f=gld.9f
+s usr/share/man/man9f/gld_unregister.9f=gld.9f
+f usr/share/man/man9f/hook_alloc.9f 0444 root bin
+f usr/share/man/man9f/hook_free.9f 0444 root bin
+f usr/share/man/man9f/id32_alloc.9f 0444 root bin
+s usr/share/man/man9f/id32_free.9f=id32_alloc.9f
+s usr/share/man/man9f/id32_lookup.9f=id32_alloc.9f
+s usr/share/man/man9f/id_alloc.9f=id_space.9f
+s usr/share/man/man9f/id_alloc_nosleep.9f=id_space.9f
+s usr/share/man/man9f/id_alloc_specific_nosleep.9f=id_space.9f
+s usr/share/man/man9f/id_allocff.9f=id_space.9f
+s usr/share/man/man9f/id_allocff_nosleep.9f=id_space.9f
+s usr/share/man/man9f/id_free.9f=id_space.9f
+f usr/share/man/man9f/id_space.9f 0444 root bin
+s usr/share/man/man9f/id_space_create.9f=id_space.9f
+s usr/share/man/man9f/id_space_destroy.9f=id_space.9f
+s usr/share/man/man9f/id_space_extend.9f=id_space.9f
+f usr/share/man/man9f/inb.9f 0444 root bin
+s usr/share/man/man9f/inl.9f=inb.9f
+f usr/share/man/man9f/insq.9f 0444 root bin
+s usr/share/man/man9f/intro.9f=Intro.9f
+s usr/share/man/man9f/inw.9f=inb.9f
+s usr/share/man/man9f/kcred.9f=credentials.9f
+f usr/share/man/man9f/kiconv.9f 0444 root bin
+f usr/share/man/man9f/kiconv_close.9f 0444 root bin
+f usr/share/man/man9f/kiconv_open.9f 0444 root bin
+f usr/share/man/man9f/kiconvstr.9f 0444 root bin
+f usr/share/man/man9f/kmem_alloc.9f 0444 root bin
+s usr/share/man/man9f/kmem_cache_alloc.9f=kmem_cache_create.9f
+f usr/share/man/man9f/kmem_cache_create.9f 0444 root bin
+s usr/share/man/man9f/kmem_cache_destroy.9f=kmem_cache_create.9f
+s usr/share/man/man9f/kmem_cache_free.9f=kmem_cache_create.9f
+s usr/share/man/man9f/kmem_cache_set_move.9f=kmem_cache_create.9f
+s usr/share/man/man9f/kmem_free.9f=kmem_alloc.9f
+s usr/share/man/man9f/kmem_zalloc.9f=kmem_alloc.9f
+f usr/share/man/man9f/kstat_create.9f 0444 root bin
+f usr/share/man/man9f/kstat_delete.9f 0444 root bin
+f usr/share/man/man9f/kstat_install.9f 0444 root bin
+f usr/share/man/man9f/kstat_named_init.9f 0444 root bin
+s usr/share/man/man9f/kstat_named_setstr.9f=kstat_named_init.9f
+f usr/share/man/man9f/kstat_queue.9f 0444 root bin
+s usr/share/man/man9f/kstat_runq_back_to_waitq.9f=kstat_queue.9f
+s usr/share/man/man9f/kstat_runq_enter.9f=kstat_queue.9f
+s usr/share/man/man9f/kstat_runq_exit.9f=kstat_queue.9f
+s usr/share/man/man9f/kstat_waitq_enter.9f=kstat_queue.9f
+s usr/share/man/man9f/kstat_waitq_exit.9f=kstat_queue.9f
+s usr/share/man/man9f/kstat_waitq_to_runq.9f=kstat_queue.9f
+f usr/share/man/man9f/ldi_add_event_handler.9f 0444 root bin
+f usr/share/man/man9f/ldi_aread.9f 0444 root bin
+s usr/share/man/man9f/ldi_awrite.9f=ldi_aread.9f
+s usr/share/man/man9f/ldi_close.9f=ldi_open_by_dev.9f
+f usr/share/man/man9f/ldi_devmap.9f 0444 root bin
+f usr/share/man/man9f/ldi_dump.9f 0444 root bin
+f usr/share/man/man9f/ldi_ev_finalize.9f 0444 root bin
+f usr/share/man/man9f/ldi_ev_get_cookie.9f 0444 root bin
+f usr/share/man/man9f/ldi_ev_get_type.9f 0444 root bin
+f usr/share/man/man9f/ldi_ev_notify.9f 0444 root bin
+f usr/share/man/man9f/ldi_ev_register_callbacks.9f 0444 root bin
+f usr/share/man/man9f/ldi_ev_remove_callbacks.9f 0444 root bin
+f usr/share/man/man9f/ldi_get_dev.9f 0444 root bin
+s usr/share/man/man9f/ldi_get_devid.9f=ldi_get_dev.9f
+f usr/share/man/man9f/ldi_get_eventcookie.9f 0444 root bin
+s usr/share/man/man9f/ldi_get_minor_name.9f=ldi_get_dev.9f
+s usr/share/man/man9f/ldi_get_otyp.9f=ldi_get_dev.9f
+f usr/share/man/man9f/ldi_get_size.9f 0444 root bin
+s usr/share/man/man9f/ldi_getmsg.9f=ldi_putmsg.9f
+f usr/share/man/man9f/ldi_ident_from_dev.9f 0444 root bin
+s usr/share/man/man9f/ldi_ident_from_dip.9f=ldi_ident_from_dev.9f
+s usr/share/man/man9f/ldi_ident_from_stream.9f=ldi_ident_from_dev.9f
+s usr/share/man/man9f/ldi_ident_release.9f=ldi_ident_from_dev.9f
+f usr/share/man/man9f/ldi_ioctl.9f 0444 root bin
+f usr/share/man/man9f/ldi_open_by_dev.9f 0444 root bin
+s usr/share/man/man9f/ldi_open_by_devid.9f=ldi_open_by_dev.9f
+s usr/share/man/man9f/ldi_open_by_name.9f=ldi_open_by_dev.9f
+f usr/share/man/man9f/ldi_poll.9f 0444 root bin
+f usr/share/man/man9f/ldi_prop_exists.9f 0444 root bin
+f usr/share/man/man9f/ldi_prop_get_int.9f 0444 root bin
+s usr/share/man/man9f/ldi_prop_get_int64.9f=ldi_prop_get_int.9f
+s usr/share/man/man9f/ldi_prop_lookup_byte_array.9f=ldi_prop_lookup_int_array.9f
+s usr/share/man/man9f/ldi_prop_lookup_int64_array.9f=ldi_prop_lookup_int_array.9f
+f usr/share/man/man9f/ldi_prop_lookup_int_array.9f 0444 root bin
+s usr/share/man/man9f/ldi_prop_lookup_string.9f=ldi_prop_lookup_int_array.9f
+s usr/share/man/man9f/ldi_prop_lookup_string_array.9f=ldi_prop_lookup_int_array.9f
+f usr/share/man/man9f/ldi_putmsg.9f 0444 root bin
+f usr/share/man/man9f/ldi_read.9f 0444 root bin
+f usr/share/man/man9f/ldi_remove_event_handler.9f 0444 root bin
+f usr/share/man/man9f/ldi_strategy.9f 0444 root bin
+s usr/share/man/man9f/ldi_write.9f=ldi_read.9f
+f usr/share/man/man9f/linkb.9f 0444 root bin
+f usr/share/man/man9f/list_create.9f 0444 root bin
+s usr/share/man/man9f/list_destroy.9f=list_create.9f
+s usr/share/man/man9f/list_head.9f=list_create.9f
+s usr/share/man/man9f/list_insert_after.9f=list_create.9f
+s usr/share/man/man9f/list_insert_before.9f=list_create.9f
+s usr/share/man/man9f/list_insert_head.9f=list_create.9f
+s usr/share/man/man9f/list_insert_tail.9f=list_create.9f
+s usr/share/man/man9f/list_is_empty.9f=list_create.9f
+s usr/share/man/man9f/list_link_active.9f=list_create.9f
+s usr/share/man/man9f/list_link_init.9f=list_create.9f
+s usr/share/man/man9f/list_link_replace.9f=list_create.9f
+s usr/share/man/man9f/list_move_tail.9f=list_create.9f
+s usr/share/man/man9f/list_next.9f=list_create.9f
+s usr/share/man/man9f/list_prev.9f=list_create.9f
+s usr/share/man/man9f/list_remove.9f=list_create.9f
+s usr/share/man/man9f/list_remove_head.9f=list_create.9f
+s usr/share/man/man9f/list_remove_tail.9f=list_create.9f
+s usr/share/man/man9f/list_tail.9f=list_create.9f
+f usr/share/man/man9f/mac_alloc.9f 0444 root bin
+s usr/share/man/man9f/mac_fini_ops.9f=mac_init_ops.9f
+s usr/share/man/man9f/mac_free.9f=mac_alloc.9f
+f usr/share/man/man9f/mac_hcksum_get.9f 0444 root bin
+s usr/share/man/man9f/mac_hcksum_set.9f=mac_hcksum_get.9f
+f usr/share/man/man9f/mac_init_ops.9f 0444 root bin
+f usr/share/man/man9f/mac_link_update.9f 0444 root bin
+f usr/share/man/man9f/mac_lso_get.9f 0444 root bin
+f usr/share/man/man9f/mac_maxsdu_update.9f 0444 root bin
+f usr/share/man/man9f/mac_prop_info.9f 0444 root bin
+s usr/share/man/man9f/mac_prop_info_set_default_fec.9f=mac_prop_info.9f
+s usr/share/man/man9f/mac_prop_info_set_default_link_flowctrl.9f=mac_prop_info.9f
+s usr/share/man/man9f/mac_prop_info_set_default_str.9f=mac_prop_info.9f
+s usr/share/man/man9f/mac_prop_info_set_default_uint32.9f=mac_prop_info.9f
+s usr/share/man/man9f/mac_prop_info_set_default_uint64.9f=mac_prop_info.9f
+s usr/share/man/man9f/mac_prop_info_set_default_uint8.9f=mac_prop_info.9f
+s usr/share/man/man9f/mac_prop_info_set_perm.9f=mac_prop_info.9f
+s usr/share/man/man9f/mac_prop_info_set_range_uint32.9f=mac_prop_info.9f
+f usr/share/man/man9f/mac_register.9f 0444 root bin
+s usr/share/man/man9f/mac_ring_rx.9f=mac_rx.9f
+f usr/share/man/man9f/mac_rx.9f 0444 root bin
+f usr/share/man/man9f/mac_transceiver_info.9f 0444 root bin
+s usr/share/man/man9f/mac_transceiver_info_set_present.9f=mac_transceiver_info.9f
+s usr/share/man/man9f/mac_transceiver_info_set_usable.9f=mac_transceiver_info.9f
+s usr/share/man/man9f/mac_tx_ring_update.9f=mac_tx_update.9f
+f usr/share/man/man9f/mac_tx_update.9f 0444 root bin
+s usr/share/man/man9f/mac_unregister.9f=mac_register.9f
+f usr/share/man/man9f/makecom.9f 0444 root bin
+s usr/share/man/man9f/makecom_g0.9f=makecom.9f
+s usr/share/man/man9f/makecom_g0_s.9f=makecom.9f
+s usr/share/man/man9f/makecom_g1.9f=makecom.9f
+s usr/share/man/man9f/makecom_g5.9f=makecom.9f
+f usr/share/man/man9f/makedevice.9f 0444 root bin
+f usr/share/man/man9f/max.9f 0444 root bin
+f usr/share/man/man9f/mcopyin.9f 0444 root bin
+f usr/share/man/man9f/mcopymsg.9f 0444 root bin
+f usr/share/man/man9f/mcopyout.9f 0444 root bin
+s usr/share/man/man9f/membar_consumer.9f=membar_ops.9f
+s usr/share/man/man9f/membar_enter.9f=membar_ops.9f
+s usr/share/man/man9f/membar_exit.9f=membar_ops.9f
+f usr/share/man/man9f/membar_ops.9f 0444 root bin
+s usr/share/man/man9f/membar_producer.9f=membar_ops.9f
+f usr/share/man/man9f/memchr.9f 0444 root bin
+s usr/share/man/man9f/memcmp.9f=memchr.9f
+s usr/share/man/man9f/memcpy.9f=memchr.9f
+s usr/share/man/man9f/memmove.9f=memchr.9f
+s usr/share/man/man9f/memset.9f=memchr.9f
+f usr/share/man/man9f/merror.9f 0444 root bin
+f usr/share/man/man9f/mexchange.9f 0444 root bin
+f usr/share/man/man9f/min.9f 0444 root bin
+s usr/share/man/man9f/minphys.9f=physio.9f
+f usr/share/man/man9f/mioc2ack.9f 0444 root bin
+f usr/share/man/man9f/miocack.9f 0444 root bin
+f usr/share/man/man9f/miocnak.9f 0444 root bin
+f usr/share/man/man9f/miocpullup.9f 0444 root bin
+f usr/share/man/man9f/mkiocb.9f 0444 root bin
+s usr/share/man/man9f/mod_info.9f=mod_install.9f
+f usr/share/man/man9f/mod_install.9f 0444 root bin
+s usr/share/man/man9f/mod_modname.9f=mod_install.9f
+s usr/share/man/man9f/mod_remove.9f=mod_install.9f
+f usr/share/man/man9f/msgdsize.9f 0444 root bin
+f usr/share/man/man9f/msgpullup.9f 0444 root bin
+f usr/share/man/man9f/msgsize.9f 0444 root bin
+f usr/share/man/man9f/mt-streams.9f 0444 root bin
+f usr/share/man/man9f/mutex.9f 0444 root bin
+s usr/share/man/man9f/mutex_destroy.9f=mutex.9f
+s usr/share/man/man9f/mutex_enter.9f=mutex.9f
+s usr/share/man/man9f/mutex_exit.9f=mutex.9f
+s usr/share/man/man9f/mutex_init.9f=mutex.9f
+s usr/share/man/man9f/mutex_owned.9f=mutex.9f
+s usr/share/man/man9f/mutex_tryenter.9f=mutex.9f
+f usr/share/man/man9f/net_event_notify_register.9f 0444 root bin
+s usr/share/man/man9f/net_event_notify_unregister.9f=net_event_notify_register.9f
+f usr/share/man/man9f/net_getifname.9f 0444 root bin
+f usr/share/man/man9f/net_getlifaddr.9f 0444 root bin
+f usr/share/man/man9f/net_getmtu.9f 0444 root bin
+f usr/share/man/man9f/net_getnetid.9f 0444 root bin
+f usr/share/man/man9f/net_getpmtuenabled.9f 0444 root bin
+f usr/share/man/man9f/net_hook_register.9f 0444 root bin
+f usr/share/man/man9f/net_hook_unregister.9f 0444 root bin
+f usr/share/man/man9f/net_inject.9f 0444 root bin
+f usr/share/man/man9f/net_inject_alloc.9f 0444 root bin
+f usr/share/man/man9f/net_inject_free.9f 0444 root bin
+f usr/share/man/man9f/net_instance_alloc.9f 0444 root bin
+f usr/share/man/man9f/net_instance_free.9f 0444 root bin
+f usr/share/man/man9f/net_instance_notify_register.9f 0444 root bin
+s usr/share/man/man9f/net_instance_notify_unregister.9f=net_instance_notify_register.9f
+s usr/share/man/man9f/net_instance_protocol_unregister.9f=net_protocol_notify_register.9f
+f usr/share/man/man9f/net_instance_register.9f 0444 root bin
+f usr/share/man/man9f/net_instance_unregister.9f 0444 root bin
+f usr/share/man/man9f/net_ispartialchecksum.9f 0444 root bin
+f usr/share/man/man9f/net_isvalidchecksum.9f 0444 root bin
+f usr/share/man/man9f/net_kstat_create.9f 0444 root bin
+f usr/share/man/man9f/net_kstat_delete.9f 0444 root bin
+f usr/share/man/man9f/net_lifgetnext.9f 0444 root bin
+f usr/share/man/man9f/net_netidtozonid.9f 0444 root bin
+f usr/share/man/man9f/net_phygetnext.9f 0444 root bin
+f usr/share/man/man9f/net_phylookup.9f 0444 root bin
+f usr/share/man/man9f/net_protocol_lookup.9f 0444 root bin
+f usr/share/man/man9f/net_protocol_notify_register.9f 0444 root bin
+f usr/share/man/man9f/net_protocol_release.9f 0444 root bin
+f usr/share/man/man9f/net_protocol_walk.9f 0444 root bin
+f usr/share/man/man9f/net_routeto.9f 0444 root bin
+f usr/share/man/man9f/net_zoneidtonetid.9f 0444 root bin
+f usr/share/man/man9f/netinfo.9f 0444 root bin
+f usr/share/man/man9f/nochpoll.9f 0444 root bin
+f usr/share/man/man9f/nodev.9f 0444 root bin
+f usr/share/man/man9f/noenable.9f 0444 root bin
+f usr/share/man/man9f/nulldev.9f 0444 root bin
+s usr/share/man/man9f/numtos.9f=stoi.9f
+s usr/share/man/man9f/nv_alloc_fini.9f=nvlist_alloc.9f
+s usr/share/man/man9f/nv_alloc_init.9f=nvlist_alloc.9f
+f usr/share/man/man9f/nvlist_add_boolean.9f 0444 root bin
+s usr/share/man/man9f/nvlist_add_boolean_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_boolean_value.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_byte.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_byte_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_int16.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_int16_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_int32.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_int32_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_int64.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_int64_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_int8.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_int8_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_nvlist.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_nvlist_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_nvpair.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_string.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_string_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_uint16.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_uint16_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_uint32.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_uint32_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_uint64.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_uint64_array.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_uint8.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_add_uint8_array.9f=nvlist_add_boolean.9f
+f usr/share/man/man9f/nvlist_alloc.9f 0444 root bin
+s usr/share/man/man9f/nvlist_dup.9f=nvlist_alloc.9f
+s usr/share/man/man9f/nvlist_exists.9f=nvlist_lookup_nvpair.9f
+s usr/share/man/man9f/nvlist_free.9f=nvlist_alloc.9f
+f usr/share/man/man9f/nvlist_lookup_boolean.9f 0444 root bin
+s usr/share/man/man9f/nvlist_lookup_boolean_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_boolean_value.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_byte.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_byte_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_int16.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_int16_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_int32.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_int32_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_int64.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_int64_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_int8.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_int8_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_nvlist.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_nvlist_array.9f=nvlist_lookup_boolean.9f
+f usr/share/man/man9f/nvlist_lookup_nvpair.9f 0444 root bin
+s usr/share/man/man9f/nvlist_lookup_pairs.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_string.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_string_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_uint16.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_uint16_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_uint32.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_uint32_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_uint64.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_uint64_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_uint8.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_lookup_uint8_array.9f=nvlist_lookup_boolean.9f
+s usr/share/man/man9f/nvlist_merge.9f=nvlist_alloc.9f
+f usr/share/man/man9f/nvlist_next_nvpair.9f 0444 root bin
+s usr/share/man/man9f/nvlist_pack.9f=nvlist_alloc.9f
+f usr/share/man/man9f/nvlist_remove.9f 0444 root bin
+s usr/share/man/man9f/nvlist_remove_all.9f=nvlist_remove.9f
+s usr/share/man/man9f/nvlist_size.9f=nvlist_alloc.9f
+s usr/share/man/man9f/nvlist_t.9f=nvlist_add_boolean.9f
+s usr/share/man/man9f/nvlist_unpack.9f=nvlist_alloc.9f
+s usr/share/man/man9f/nvlist_xalloc.9f=nvlist_alloc.9f
+s usr/share/man/man9f/nvlist_xdup.9f=nvlist_alloc.9f
+s usr/share/man/man9f/nvlist_xpack.9f=nvlist_alloc.9f
+s usr/share/man/man9f/nvlist_xunpack.9f=nvlist_alloc.9f
+s usr/share/man/man9f/nvpair_name.9f=nvlist_next_nvpair.9f
+s usr/share/man/man9f/nvpair_type.9f=nvlist_next_nvpair.9f
+s usr/share/man/man9f/nvpair_value_boolean_array.9f=nvpair_value_byte.9f
+f usr/share/man/man9f/nvpair_value_byte.9f 0444 root bin
+s usr/share/man/man9f/nvpair_value_byte_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_int16.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_int16_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_int32.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_int32_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_int64.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_int64_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_int8.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_int8_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_nvlist.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_nvlist_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_string.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_string_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_uint16.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_uint16_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_uint32.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_uint32_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_uint64.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_uint64_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_uint8.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/nvpair_value_uint8_array.9f=nvpair_value_byte.9f
+s usr/share/man/man9f/otherq.9f=OTHERQ.9f
+f usr/share/man/man9f/outb.9f 0444 root bin
+s usr/share/man/man9f/outl.9f=outb.9f
+s usr/share/man/man9f/outw.9f=outb.9f
+s usr/share/man/man9f/pci_config_get16.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_get32.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_get64.9f=pci_config_get8.9f
+f usr/share/man/man9f/pci_config_get8.9f 0444 root bin
+s usr/share/man/man9f/pci_config_getb.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_getl.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_getll.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_getw.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_put16.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_put32.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_put64.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_put8.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_putb.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_putl.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_putll.9f=pci_config_get8.9f
+s usr/share/man/man9f/pci_config_putw.9f=pci_config_get8.9f
+f usr/share/man/man9f/pci_config_setup.9f 0444 root bin
+s usr/share/man/man9f/pci_config_teardown.9f=pci_config_setup.9f
+s usr/share/man/man9f/pci_ereport_post.9f=pci_ereport_setup.9f
+f usr/share/man/man9f/pci_ereport_setup.9f 0444 root bin
+s usr/share/man/man9f/pci_ereport_teardown.9f=pci_ereport_setup.9f
+f usr/share/man/man9f/pci_report_pmcap.9f 0444 root bin
+s usr/share/man/man9f/pci_restore_config_regs.9f=pci_save_config_regs.9f
+f usr/share/man/man9f/pci_save_config_regs.9f 0444 root bin
+f usr/share/man/man9f/physio.9f 0444 root bin
+f usr/share/man/man9f/pm_busy_component.9f 0444 root bin
+s usr/share/man/man9f/pm_idle_component.9f=pm_busy_component.9f
+s usr/share/man/man9f/pm_lower_power.9f=pm_raise_power.9f
+f usr/share/man/man9f/pm_power_has_changed.9f 0444 root bin
+f usr/share/man/man9f/pm_raise_power.9f 0444 root bin
+f usr/share/man/man9f/pm_trans_check.9f 0444 root bin
+f usr/share/man/man9f/pollhead_clean.9f 0444 root bin
+f usr/share/man/man9f/pollwakeup.9f 0444 root bin
+f usr/share/man/man9f/priv_getbyname.9f 0444 root bin
+f usr/share/man/man9f/priv_policy.9f 0444 root bin
+s usr/share/man/man9f/priv_policy_choice.9f=priv_policy.9f
+s usr/share/man/man9f/priv_policy_only.9f=priv_policy.9f
+s usr/share/man/man9f/proc_ref.9f=proc_signal.9f
+f usr/share/man/man9f/proc_signal.9f 0444 root bin
+s usr/share/man/man9f/proc_unref.9f=proc_signal.9f
+f usr/share/man/man9f/ptob.9f 0444 root bin
+f usr/share/man/man9f/pullupmsg.9f 0444 root bin
+f usr/share/man/man9f/put.9f 0444 root bin
+f usr/share/man/man9f/putbq.9f 0444 root bin
+f usr/share/man/man9f/putctl.9f 0444 root bin
+f usr/share/man/man9f/putctl1.9f 0444 root bin
+f usr/share/man/man9f/putnext.9f 0444 root bin
+f usr/share/man/man9f/putnextctl.9f 0444 root bin
+f usr/share/man/man9f/putnextctl1.9f 0444 root bin
+f usr/share/man/man9f/putq.9f 0444 root bin
+f usr/share/man/man9f/qassociate.9f 0444 root bin
+f usr/share/man/man9f/qbufcall.9f 0444 root bin
+f usr/share/man/man9f/qenable.9f 0444 root bin
+s usr/share/man/man9f/qprocsoff.9f=qprocson.9f
+f usr/share/man/man9f/qprocson.9f 0444 root bin
+f usr/share/man/man9f/qreply.9f 0444 root bin
+f usr/share/man/man9f/qsize.9f 0444 root bin
+f usr/share/man/man9f/qtimeout.9f 0444 root bin
+f usr/share/man/man9f/qunbufcall.9f 0444 root bin
+f usr/share/man/man9f/quntimeout.9f 0444 root bin
+f usr/share/man/man9f/qwait.9f 0444 root bin
+s usr/share/man/man9f/qwait_sig.9f=qwait.9f
+f usr/share/man/man9f/qwriter.9f 0444 root bin
+s usr/share/man/man9f/rd.9f=RD.9f
+s usr/share/man/man9f/repinsb.9f=inb.9f
+s usr/share/man/man9f/repinsd.9f=inb.9f
+s usr/share/man/man9f/repinsw.9f=inb.9f
+s usr/share/man/man9f/repoutsb.9f=outb.9f
+s usr/share/man/man9f/repoutsd.9f=outb.9f
+s usr/share/man/man9f/repoutsw.9f=outb.9f
+f usr/share/man/man9f/rmalloc.9f 0444 root bin
+f usr/share/man/man9f/rmalloc_wait.9f 0444 root bin
+f usr/share/man/man9f/rmallocmap.9f 0444 root bin
+s usr/share/man/man9f/rmallocmap_wait.9f=rmallocmap.9f
+f usr/share/man/man9f/rmfree.9f 0444 root bin
+s usr/share/man/man9f/rmfreemap.9f=rmallocmap.9f
+f usr/share/man/man9f/rmvb.9f 0444 root bin
+f usr/share/man/man9f/rmvq.9f 0444 root bin
+s usr/share/man/man9f/rw_destroy.9f=rwlock.9f
+s usr/share/man/man9f/rw_downgrade.9f=rwlock.9f
+s usr/share/man/man9f/rw_enter.9f=rwlock.9f
+s usr/share/man/man9f/rw_exit.9f=rwlock.9f
+s usr/share/man/man9f/rw_init.9f=rwlock.9f
+s usr/share/man/man9f/rw_read_locked.9f=rwlock.9f
+s usr/share/man/man9f/rw_tryenter.9f=rwlock.9f
+s usr/share/man/man9f/rw_tryupgrade.9f=rwlock.9f
+f usr/share/man/man9f/rwlock.9f 0444 root bin
+s usr/share/man/man9f/samestr.9f=SAMESTR.9f
+f usr/share/man/man9f/sas_phymap_create.9f 0444 root bin
+s usr/share/man/man9f/sas_phymap_destroy.9f=sas_phymap_create.9f
+f usr/share/man/man9f/sas_phymap_lookup_ua.9f 0444 root bin
+s usr/share/man/man9f/sas_phymap_lookup_uapriv.9f=sas_phymap_lookup_ua.9f
+s usr/share/man/man9f/sas_phymap_phy2ua.9f=sas_phymap_lookup_ua.9f
+s usr/share/man/man9f/sas_phymap_phy_add.9f=sas_phymap_create.9f
+s usr/share/man/man9f/sas_phymap_phy_rem.9f=sas_phymap_create.9f
+s usr/share/man/man9f/sas_phymap_phys_free.9f=sas_phymap_lookup_ua.9f
+s usr/share/man/man9f/sas_phymap_phys_next.9f=sas_phymap_lookup_ua.9f
+s usr/share/man/man9f/sas_phymap_ua2phys.9f=sas_phymap_lookup_ua.9f
+s usr/share/man/man9f/sas_phymap_ua_free.9f=sas_phymap_lookup_ua.9f
+s usr/share/man/man9f/sas_phymap_uahasphys.9f=sas_phymap_lookup_ua.9f
+f usr/share/man/man9f/scsi_abort.9f 0444 root bin
+f usr/share/man/man9f/scsi_address_device.9f 0444 root bin
+f usr/share/man/man9f/scsi_alloc_consistent_buf.9f 0444 root bin
+f usr/share/man/man9f/scsi_cname.9f 0444 root bin
+f usr/share/man/man9f/scsi_destroy_pkt.9f 0444 root bin
+s usr/share/man/man9f/scsi_device_hba_private_get.9f=scsi_address_device.9f
+s usr/share/man/man9f/scsi_device_hba_private_set.9f=scsi_address_device.9f
+s usr/share/man/man9f/scsi_device_unit_address.9f=scsi_address_device.9f
+s usr/share/man/man9f/scsi_dmafree.9f=scsi_dmaget.9f
+f usr/share/man/man9f/scsi_dmaget.9f 0444 root bin
+s usr/share/man/man9f/scsi_dname.9f=scsi_cname.9f
+f usr/share/man/man9f/scsi_errmsg.9f 0444 root bin
+f usr/share/man/man9f/scsi_ext_sense_fields.9f 0444 root bin
+f usr/share/man/man9f/scsi_find_sense_descr.9f 0444 root bin
+f usr/share/man/man9f/scsi_free_consistent_buf.9f 0444 root bin
+s usr/share/man/man9f/scsi_free_wwnstr.9f=scsi_wwnstr_to_wwn.9f
+f usr/share/man/man9f/scsi_get_device_type_scsi_options.9f 0444 root bin
+f usr/share/man/man9f/scsi_get_device_type_string.9f 0444 root bin
+s usr/share/man/man9f/scsi_hba_attach.9f=scsi_hba_attach_setup.9f
+f usr/share/man/man9f/scsi_hba_attach_setup.9f 0444 root bin
+s usr/share/man/man9f/scsi_hba_detach.9f=scsi_hba_attach_setup.9f
+s usr/share/man/man9f/scsi_hba_fini.9f=scsi_hba_init.9f
+f usr/share/man/man9f/scsi_hba_init.9f 0444 root bin
+f usr/share/man/man9f/scsi_hba_iport_exist.9f 0444 root bin
+s usr/share/man/man9f/scsi_hba_iport_find.9f=scsi_hba_iport_exist.9f
+f usr/share/man/man9f/scsi_hba_iport_register.9f 0444 root bin
+f usr/share/man/man9f/scsi_hba_iport_unit_address.9f 0444 root bin
+f usr/share/man/man9f/scsi_hba_iportmap_create.9f 0444 root bin
+s usr/share/man/man9f/scsi_hba_iportmap_destroy.9f=scsi_hba_iportmap_create.9f
+s usr/share/man/man9f/scsi_hba_iportmap_iport_add.9f=scsi_hba_iportmap_create.9f
+s usr/share/man/man9f/scsi_hba_iportmap_iport_remove.9f=scsi_hba_iportmap_create.9f
+f usr/share/man/man9f/scsi_hba_lookup_capstr.9f 0444 root bin
+f usr/share/man/man9f/scsi_hba_pkt_alloc.9f 0444 root bin
+f usr/share/man/man9f/scsi_hba_pkt_comp.9f 0444 root bin
+s usr/share/man/man9f/scsi_hba_pkt_free.9f=scsi_hba_pkt_alloc.9f
+f usr/share/man/man9f/scsi_hba_probe.9f 0444 root bin
+f usr/share/man/man9f/scsi_hba_tgtmap_create.9f 0444 root bin
+s usr/share/man/man9f/scsi_hba_tgtmap_destroy.9f=scsi_hba_tgtmap_create.9f
+s usr/share/man/man9f/scsi_hba_tgtmap_scan_luns.9f=scsi_hba_tgtmap_create.9f
+s usr/share/man/man9f/scsi_hba_tgtmap_set_add.9f=scsi_hba_tgtmap_create.9f
+s usr/share/man/man9f/scsi_hba_tgtmap_set_begin.9f=scsi_hba_tgtmap_create.9f
+s usr/share/man/man9f/scsi_hba_tgtmap_set_end.9f=scsi_hba_tgtmap_create.9f
+s usr/share/man/man9f/scsi_hba_tgtmap_set_flush.9f=scsi_hba_tgtmap_create.9f
+s usr/share/man/man9f/scsi_hba_tgtmap_tgt_add.9f=scsi_hba_tgtmap_create.9f
+s usr/share/man/man9f/scsi_hba_tgtmap_tgt_remove.9f=scsi_hba_tgtmap_create.9f
+f usr/share/man/man9f/scsi_hba_tran_alloc.9f 0444 root bin
+s usr/share/man/man9f/scsi_hba_tran_free.9f=scsi_hba_tran_alloc.9f
+f usr/share/man/man9f/scsi_ifgetcap.9f 0444 root bin
+s usr/share/man/man9f/scsi_ifsetcap.9f=scsi_ifgetcap.9f
+f usr/share/man/man9f/scsi_init_pkt.9f 0444 root bin
+f usr/share/man/man9f/scsi_log.9f 0444 root bin
+s usr/share/man/man9f/scsi_mname.9f=scsi_cname.9f
+f usr/share/man/man9f/scsi_pktalloc.9f 0444 root bin
+s usr/share/man/man9f/scsi_pktfree.9f=scsi_pktalloc.9f
+f usr/share/man/man9f/scsi_poll.9f 0444 root bin
+f usr/share/man/man9f/scsi_probe.9f 0444 root bin
+s usr/share/man/man9f/scsi_resalloc.9f=scsi_pktalloc.9f
+f usr/share/man/man9f/scsi_reset.9f 0444 root bin
+f usr/share/man/man9f/scsi_reset_notify.9f 0444 root bin
+s usr/share/man/man9f/scsi_resfree.9f=scsi_pktalloc.9f
+s usr/share/man/man9f/scsi_rname.9f=scsi_cname.9f
+s usr/share/man/man9f/scsi_sense_asc.9f=scsi_sense_key.9f
+s usr/share/man/man9f/scsi_sense_ascq.9f=scsi_sense_key.9f
+s usr/share/man/man9f/scsi_sense_cmdspecific_uint64.9f=scsi_ext_sense_fields.9f
+s usr/share/man/man9f/scsi_sense_info_uint64.9f=scsi_ext_sense_fields.9f
+f usr/share/man/man9f/scsi_sense_key.9f 0444 root bin
+f usr/share/man/man9f/scsi_setup_cdb.9f 0444 root bin
+f usr/share/man/man9f/scsi_slave.9f 0444 root bin
+s usr/share/man/man9f/scsi_sname.9f=scsi_cname.9f
+f usr/share/man/man9f/scsi_sync_pkt.9f 0444 root bin
+f usr/share/man/man9f/scsi_transport.9f 0444 root bin
+f usr/share/man/man9f/scsi_unprobe.9f 0444 root bin
+s usr/share/man/man9f/scsi_unslave.9f=scsi_unprobe.9f
+f usr/share/man/man9f/scsi_validate_sense.9f 0444 root bin
+f usr/share/man/man9f/scsi_vu_errmsg.9f 0444 root bin
+s usr/share/man/man9f/scsi_wwn_to_wwnstr.9f=scsi_wwnstr_to_wwn.9f
+f usr/share/man/man9f/scsi_wwnstr_to_wwn.9f 0444 root bin
+s usr/share/man/man9f/sema_destroy.9f=semaphore.9f
+s usr/share/man/man9f/sema_init.9f=semaphore.9f
+s usr/share/man/man9f/sema_p.9f=semaphore.9f
+s usr/share/man/man9f/sema_p_sig.9f=semaphore.9f
+s usr/share/man/man9f/sema_tryp.9f=semaphore.9f
+s usr/share/man/man9f/sema_v.9f=semaphore.9f
+f usr/share/man/man9f/semaphore.9f 0444 root bin
+s usr/share/man/man9f/snprintf.9f=sprintf.9f
+f usr/share/man/man9f/sprintf.9f 0444 root bin
+f usr/share/man/man9f/stoi.9f 0444 root bin
+s usr/share/man/man9f/strcasecmp.9f=string.9f
+s usr/share/man/man9f/strcat.9f=string.9f
+s usr/share/man/man9f/strchr.9f=string.9f
+s usr/share/man/man9f/strcmp.9f=string.9f
+s usr/share/man/man9f/strcpy.9f=string.9f
+s usr/share/man/man9f/strdup.9f=string.9f
+s usr/share/man/man9f/strfree.9f=string.9f
+f usr/share/man/man9f/string.9f 0444 root bin
+s usr/share/man/man9f/strlcat.9f=string.9f
+s usr/share/man/man9f/strlcpy.9f=string.9f
+s usr/share/man/man9f/strlen.9f=string.9f
+f usr/share/man/man9f/strlog.9f 0444 root bin
+s usr/share/man/man9f/strncasecmp.9f=string.9f
+s usr/share/man/man9f/strncat.9f=string.9f
+s usr/share/man/man9f/strncmp.9f=string.9f
+s usr/share/man/man9f/strncpy.9f=string.9f
+s usr/share/man/man9f/strnlen.9f=string.9f
+f usr/share/man/man9f/strqget.9f 0444 root bin
+f usr/share/man/man9f/strqset.9f 0444 root bin
+s usr/share/man/man9f/strrchr.9f=string.9f
+s usr/share/man/man9f/strspn.9f=string.9f
+f usr/share/man/man9f/swab.9f 0444 root bin
+f usr/share/man/man9f/taskq.9f 0444 root bin
+s usr/share/man/man9f/taskq_suspended.9f=taskq.9f
+f usr/share/man/man9f/testb.9f 0444 root bin
+f usr/share/man/man9f/timeout.9f 0444 root bin
+f usr/share/man/man9f/u8_strcmp.9f 0444 root bin
+f usr/share/man/man9f/u8_textprep_str.9f 0444 root bin
+f usr/share/man/man9f/u8_validate.9f 0444 root bin
+f usr/share/man/man9f/uconv_u16tou32.9f 0444 root bin
+s usr/share/man/man9f/uconv_u16tou8.9f=uconv_u16tou32.9f
+s usr/share/man/man9f/uconv_u32tou16.9f=uconv_u16tou32.9f
+s usr/share/man/man9f/uconv_u32tou8.9f=uconv_u16tou32.9f
+s usr/share/man/man9f/uconv_u8tou16.9f=uconv_u16tou32.9f
+s usr/share/man/man9f/uconv_u8tou32.9f=uconv_u16tou32.9f
+f usr/share/man/man9f/uiomove.9f 0444 root bin
+f usr/share/man/man9f/unbufcall.9f 0444 root bin
+s usr/share/man/man9f/unfreezestr.9f=freezestr.9f
+f usr/share/man/man9f/unlinkb.9f 0444 root bin
+f usr/share/man/man9f/untimeout.9f 0444 root bin
+f usr/share/man/man9f/ureadc.9f 0444 root bin
+s usr/share/man/man9f/usb_alloc_bulk_req.9f=usb_alloc_request.9f
+s usr/share/man/man9f/usb_alloc_ctrl_req.9f=usb_alloc_request.9f
+s usr/share/man/man9f/usb_alloc_intr_req.9f=usb_alloc_request.9f
+s usr/share/man/man9f/usb_alloc_isoc_req.9f=usb_alloc_request.9f
+f usr/share/man/man9f/usb_alloc_request.9f 0444 root bin
+f usr/share/man/man9f/usb_client_attach.9f 0444 root bin
+s usr/share/man/man9f/usb_client_detach.9f=usb_client_attach.9f
+f usr/share/man/man9f/usb_clr_feature.9f 0444 root bin
+f usr/share/man/man9f/usb_create_pm_components.9f 0444 root bin
+f usr/share/man/man9f/usb_ep_xdescr_fill.9f 0444 root bin
+s usr/share/man/man9f/usb_free_bulk_req.9f=usb_alloc_request.9f
+s usr/share/man/man9f/usb_free_ctrl_req.9f=usb_alloc_request.9f
+s usr/share/man/man9f/usb_free_descr_tree.9f=usb_get_dev_data.9f
+s usr/share/man/man9f/usb_free_dev_data.9f=usb_get_dev_data.9f
+s usr/share/man/man9f/usb_free_intr_req.9f=usb_alloc_request.9f
+s usr/share/man/man9f/usb_free_isoc_req.9f=usb_alloc_request.9f
+f usr/share/man/man9f/usb_get_addr.9f 0444 root bin
+f usr/share/man/man9f/usb_get_alt_if.9f 0444 root bin
+f usr/share/man/man9f/usb_get_cfg.9f 0444 root bin
+f usr/share/man/man9f/usb_get_current_frame_number.9f 0444 root bin
+f usr/share/man/man9f/usb_get_dev_data.9f 0444 root bin
+s usr/share/man/man9f/usb_get_if_number.9f=usb_get_alt_if.9f
+f usr/share/man/man9f/usb_get_max_pkts_per_isoc_request.9f 0444 root bin
+f usr/share/man/man9f/usb_get_status.9f 0444 root bin
+f usr/share/man/man9f/usb_get_string_descr.9f 0444 root bin
+f usr/share/man/man9f/usb_handle_remote_wakeup.9f 0444 root bin
+f usr/share/man/man9f/usb_lookup_ep_data.9f 0444 root bin
+s usr/share/man/man9f/usb_owns_device.9f=usb_get_alt_if.9f
+f usr/share/man/man9f/usb_parse_data.9f 0444 root bin
+f usr/share/man/man9f/usb_pipe_bulk_xfer.9f 0444 root bin
+f usr/share/man/man9f/usb_pipe_close.9f 0444 root bin
+f usr/share/man/man9f/usb_pipe_ctrl_xfer.9f 0444 root bin
+s usr/share/man/man9f/usb_pipe_ctrl_xfer_wait.9f=usb_pipe_ctrl_xfer.9f
+f usr/share/man/man9f/usb_pipe_drain_reqs.9f 0444 root bin
+f usr/share/man/man9f/usb_pipe_get_max_bulk_transfer_size.9f 0444 root bin
+s usr/share/man/man9f/usb_pipe_get_private.9f=usb_pipe_set_private.9f
+f usr/share/man/man9f/usb_pipe_get_state.9f 0444 root bin
+f usr/share/man/man9f/usb_pipe_intr_xfer.9f 0444 root bin
+f usr/share/man/man9f/usb_pipe_isoc_xfer.9f 0444 root bin
+s usr/share/man/man9f/usb_pipe_open.9f=usb_pipe_xopen.9f
+f usr/share/man/man9f/usb_pipe_reset.9f 0444 root bin
+f usr/share/man/man9f/usb_pipe_set_private.9f 0444 root bin
+s usr/share/man/man9f/usb_pipe_stop_intr_polling.9f=usb_pipe_intr_xfer.9f
+s usr/share/man/man9f/usb_pipe_stop_isoc_polling.9f=usb_pipe_isoc_xfer.9f
+f usr/share/man/man9f/usb_pipe_xopen.9f 0444 root bin
+s usr/share/man/man9f/usb_print_descr_tree.9f=usb_get_dev_data.9f
+f usr/share/man/man9f/usb_register_hotplug_cbs.9f 0444 root bin
+f usr/share/man/man9f/usb_reset_device.9f 0444 root bin
+s usr/share/man/man9f/usb_set_alt_if.9f=usb_get_alt_if.9f
+s usr/share/man/man9f/usb_set_cfg.9f=usb_get_cfg.9f
+s usr/share/man/man9f/usb_unregister_hotplug_cbs.9f=usb_register_hotplug_cbs.9f
+f usr/share/man/man9f/usba_alloc_hcdi_ops.9f 0444 root bin
+s usr/share/man/man9f/usba_free_hcdi_ops.9f=usba_alloc_hcdi_ops.9f
+f usr/share/man/man9f/usba_hcdi_cb.9f 0444 root bin
+f usr/share/man/man9f/usba_hcdi_dup_intr_req.9f 0444 root bin
+f usr/share/man/man9f/usba_hcdi_dup_isoc_req.9f 0444 root bin
+f usr/share/man/man9f/usba_hcdi_get_device_private.9f 0444 root bin
+f usr/share/man/man9f/usba_hcdi_register.9f 0444 root bin
+s usr/share/man/man9f/usba_hcdi_unregister.9f=usba_hcdi_register.9f
+f usr/share/man/man9f/usba_hubdi_bind_root_hub.9f 0444 root bin
+f usr/share/man/man9f/usba_hubdi_cb_ops.9f 0444 root bin
+s usr/share/man/man9f/usba_hubdi_close.9f=usba_hubdi_cb_ops.9f
+f usr/share/man/man9f/usba_hubdi_dev_ops.9f 0444 root bin
+s usr/share/man/man9f/usba_hubdi_ioctl.9f=usba_hubdi_cb_ops.9f
+s usr/share/man/man9f/usba_hubdi_open.9f=usba_hubdi_cb_ops.9f
+s usr/share/man/man9f/usba_hubdi_root_hub_power.9f=usba_hubdi_dev_ops.9f
+s usr/share/man/man9f/usba_hubdi_unbind_root_hub.9f=usba_hubdi_bind_root_hub.9f
+f usr/share/man/man9f/uwritec.9f 0444 root bin
+f usr/share/man/man9f/va_arg.9f 0444 root bin
+s usr/share/man/man9f/va_copy.9f=va_arg.9f
+s usr/share/man/man9f/va_end.9f=va_arg.9f
+s usr/share/man/man9f/va_start.9f=va_arg.9f
+s usr/share/man/man9f/vcmn_err.9f=cmn_err.9f
+f usr/share/man/man9f/vmem_add.9f 0444 root bin
+f usr/share/man/man9f/vmem_alloc.9f 0444 root bin
+f usr/share/man/man9f/vmem_contains.9f 0444 root bin
+f usr/share/man/man9f/vmem_create.9f 0444 root bin
+s usr/share/man/man9f/vmem_destroy.9f=vmem_create.9f
+s usr/share/man/man9f/vmem_free.9f=vmem_alloc.9f
+s usr/share/man/man9f/vmem_size.9f=vmem_walk.9f
+f usr/share/man/man9f/vmem_walk.9f 0444 root bin
+s usr/share/man/man9f/vmem_xalloc.9f=vmem_alloc.9f
+s usr/share/man/man9f/vmem_xcreate.9f=vmem_create.9f
+s usr/share/man/man9f/vmem_xfree.9f=vmem_alloc.9f
+s usr/share/man/man9f/vsnprintf.9f=sprintf.9f
+s usr/share/man/man9f/vsprintf.9f=sprintf.9f
+s usr/share/man/man9f/vzcmn_err.9f=cmn_err.9f
+s usr/share/man/man9f/wr.9f=WR.9f
+s usr/share/man/man9f/zcmn_err.9f=cmn_err.9f
+s usr/share/man/man9f/zone_kcred.9f=credentials.9f
+d usr/share/man/man9p 0755 root bin
+s usr/share/man/man9p/Nblock.9p=size.9p
+s usr/share/man/man9p/blksize.9p=size.9p
+f usr/share/man/man9p/ddi-forceattach.9p 0444 root bin
+s usr/share/man/man9p/ddi-no-autodetach.9p=ddi-forceattach.9p
+s usr/share/man/man9p/device-blksize.9p=size.9p
+s usr/share/man/man9p/device-nblocks.9p=size.9p
+f usr/share/man/man9p/inquiry-device-type.9p 0444 root bin
+s usr/share/man/man9p/inquiry-product-id.9p=inquiry-device-type.9p
+s usr/share/man/man9p/inquiry-revision-id.9p=inquiry-device-type.9p
+s usr/share/man/man9p/inquiry-serial-no.9p=inquiry-device-type.9p
+s usr/share/man/man9p/inquiry-vendor-id.9p=inquiry-device-type.9p
+f usr/share/man/man9p/no-involuntary-power-cycles.9p 0444 root bin
+f usr/share/man/man9p/pm-components.9p 0444 root bin
+f usr/share/man/man9p/pm.9p 0444 root bin
+f usr/share/man/man9p/removable-media.9p 0444 root bin
+f usr/share/man/man9p/size.9p 0444 root bin
+d usr/share/man/man9s 0755 root bin
+f usr/share/man/man9s/Intro.9s 0444 root bin
+f usr/share/man/man9s/aio_req.9s 0444 root bin
+f usr/share/man/man9s/buf.9s 0444 root bin
+f usr/share/man/man9s/cb_ops.9s 0444 root bin
+f usr/share/man/man9s/copyreq.9s 0444 root bin
+f usr/share/man/man9s/copyresp.9s 0444 root bin
+f usr/share/man/man9s/datab.9s 0444 root bin
+s usr/share/man/man9s/dblk.9s=datab.9s
+f usr/share/man/man9s/ddi_device_acc_attr.9s 0444 root bin
+f usr/share/man/man9s/ddi_dma_attr.9s 0444 root bin
+f usr/share/man/man9s/ddi_dma_cookie.9s 0444 root bin
+f usr/share/man/man9s/ddi_dmae_req.9s 0444 root bin
+f usr/share/man/man9s/ddi_fm_error.9s 0444 root bin
+f usr/share/man/man9s/ddi_idevice_cookie.9s 0444 root bin
+f usr/share/man/man9s/dev_ops.9s 0444 root bin
+f usr/share/man/man9s/devmap_callback_ctl.9s 0444 root bin
+f usr/share/man/man9s/fmodsw.9s 0444 root bin
+f usr/share/man/man9s/free_rtn.9s 0444 root bin
+f usr/share/man/man9s/gld_mac_info.9s 0444 root bin
+f usr/share/man/man9s/gld_stats.9s 0444 root bin
+f usr/share/man/man9s/hook_nic_event.9s 0444 root bin
+f usr/share/man/man9s/hook_pkt_event.9s 0444 root bin
+f usr/share/man/man9s/hook_t.9s 0444 root bin
+s usr/share/man/man9s/intro.9s=Intro.9s
+f usr/share/man/man9s/iocblk.9s 0444 root bin
+f usr/share/man/man9s/iovec.9s 0444 root bin
+f usr/share/man/man9s/kstat.9s 0444 root bin
+f usr/share/man/man9s/kstat_intr.9s 0444 root bin
+f usr/share/man/man9s/kstat_io.9s 0444 root bin
+f usr/share/man/man9s/kstat_named.9s 0444 root bin
+f usr/share/man/man9s/linkblk.9s 0444 root bin
+f usr/share/man/man9s/mac_callbacks.9s 0444 root bin
+s usr/share/man/man9s/mac_callbacks_t.9s=mac_callbacks.9s
+f usr/share/man/man9s/mac_group_info.9s 0444 root bin
+s usr/share/man/man9s/mac_group_info_t.9s=mac_group_info.9s
+f usr/share/man/man9s/mac_intr.9s 0444 root bin
+s usr/share/man/man9s/mac_intr_t.9s=mac_intr.9s
+f usr/share/man/man9s/mac_register.9s 0444 root bin
+s usr/share/man/man9s/mac_register_t.9s=mac_register.9s
+f usr/share/man/man9s/mac_ring_info.9s 0444 root bin
+s usr/share/man/man9s/mac_ring_info_t.9s=mac_ring_info.9s
+s usr/share/man/man9s/mblk.9s=msgb.9s
+f usr/share/man/man9s/modldrv.9s 0444 root bin
+f usr/share/man/man9s/modlinkage.9s 0444 root bin
+f usr/share/man/man9s/modlmisc.9s 0444 root bin
+f usr/share/man/man9s/modlstrmod.9s 0444 root bin
+f usr/share/man/man9s/module_info.9s 0444 root bin
+f usr/share/man/man9s/msgb.9s 0444 root bin
+f usr/share/man/man9s/net_inject_t.9s 0444 root bin
+f usr/share/man/man9s/net_instance_t.9s 0444 root bin
+f usr/share/man/man9s/qband.9s 0444 root bin
+f usr/share/man/man9s/qinit.9s 0444 root bin
+f usr/share/man/man9s/queclass.9s 0444 root bin
+f usr/share/man/man9s/queue.9s 0444 root bin
+f usr/share/man/man9s/scsi_address.9s 0444 root bin
+f usr/share/man/man9s/scsi_arq_status.9s 0444 root bin
+f usr/share/man/man9s/scsi_asc_key_strings.9s 0444 root bin
+f usr/share/man/man9s/scsi_device.9s 0444 root bin
+f usr/share/man/man9s/scsi_extended_sense.9s 0444 root bin
+f usr/share/man/man9s/scsi_hba_tran.9s 0444 root bin
+f usr/share/man/man9s/scsi_inquiry.9s 0444 root bin
+f usr/share/man/man9s/scsi_pkt.9s 0444 root bin
+f usr/share/man/man9s/scsi_status.9s 0444 root bin
+f usr/share/man/man9s/streamtab.9s 0444 root bin
+f usr/share/man/man9s/stroptions.9s 0444 root bin
+f usr/share/man/man9s/tuple.9s 0444 root bin
+f usr/share/man/man9s/uio.9s 0444 root bin
+f usr/share/man/man9s/usb_bulk_req.9s 0444 root bin
+s usr/share/man/man9s/usb_bulk_req_t.9s=usb_bulk_req.9s
+s usr/share/man/man9s/usb_bulk_request.9s=usb_bulk_req.9s
+f usr/share/man/man9s/usb_callback_flags.9s 0444 root bin
+f usr/share/man/man9s/usb_cfg_descr.9s 0444 root bin
+f usr/share/man/man9s/usb_client_dev_data.9s 0444 root bin
+f usr/share/man/man9s/usb_completion_reason.9s 0444 root bin
+f usr/share/man/man9s/usb_ctrl_req.9s 0444 root bin
+s usr/share/man/man9s/usb_ctrl_req_t.9s=usb_ctrl_req.9s
+s usr/share/man/man9s/usb_ctrl_request.9s=usb_ctrl_req.9s
+f usr/share/man/man9s/usb_dev_descr.9s 0444 root bin
+f usr/share/man/man9s/usb_dev_qlf_descr.9s 0444 root bin
+f usr/share/man/man9s/usb_ep_descr.9s 0444 root bin
+f usr/share/man/man9s/usb_ep_ss_comp_descr.9s 0444 root bin
+s usr/share/man/man9s/usb_ep_ss_comp_descr_t.9s=usb_ep_ss_comp_descr.9s
+f usr/share/man/man9s/usb_ep_xdescr.9s 0444 root bin
+s usr/share/man/man9s/usb_ep_xdescr_t.9s=usb_ep_xdescr.9s
+f usr/share/man/man9s/usb_if_descr.9s 0444 root bin
+f usr/share/man/man9s/usb_intr_req.9s 0444 root bin
+s usr/share/man/man9s/usb_intr_req_t.9s=usb_intr_req.9s
+s usr/share/man/man9s/usb_intr_request.9s=usb_intr_req.9s
+f usr/share/man/man9s/usb_isoc_req.9s 0444 root bin
+s usr/share/man/man9s/usb_isoc_req_t.9s=usb_isoc_req.9s
+s usr/share/man/man9s/usb_isoc_request.9s=usb_isoc_req.9s
+f usr/share/man/man9s/usb_other_speed_cfg_descr.9s 0444 root bin
+f usr/share/man/man9s/usb_request_attributes.9s 0444 root bin
+f usr/share/man/man9s/usb_string_descr.9s 0444 root bin
+f usr/share/man/man9s/usba_device.9s 0444 root bin
+s usr/share/man/man9s/usba_device_t.9s=usba_device.9s
+f usr/share/man/man9s/usba_hcdi_ops.9s 0444 root bin
+s usr/share/man/man9s/usba_hcdi_ops_t.9s=usba_hcdi_ops.9s
+f usr/share/man/man9s/usba_hcdi_register_args.9s 0444 root bin
+s usr/share/man/man9s/usba_hcdi_register_args_t.9s=usba_hcdi_register_args.9s
+f usr/share/man/man9s/usba_pipe_handle_data.9s 0444 root bin
+s usr/share/man/man9s/usba_pipe_handle_data_t.9s=usba_pipe_handle_data.9s
+d usr/share/src 0755 root sys
+s usr/share/terminfo=../share/lib/terminfo
+s usr/spool=../var/spool
+s usr/src=share/src
+s usr/tmp=../var/tmp
+d usr/ucb 0755 root bin
+d usr/ucb/amd64 0755 root bin
+f usr/ucb/df 0755 root bin
+d usr/ucb/i86 0755 root bin
+f usr/ucb/ls 0755 root bin
+s usr/ucb/more=../bin/more
+s usr/ucb/page=../bin/more
+h usr/ucb/ps=usr/bin/ps
+f usr/ucb/shutdown 0755 root bin
+f usr/ucb/touch 0755 root bin
+s usr/ucb/uptime=../bin/uptime
+d usr/ucbinclude 0755 root bin
+f usr/ucbinclude/curses.h 0755 root bin
+f usr/ucbinclude/dbm.h 0755 root bin
+s usr/ucbinclude/fcntl.h=sys/fcntl.h
+f usr/ucbinclude/setjmp.h 0755 root bin
+f usr/ucbinclude/sgtty.h 0755 root bin
+f usr/ucbinclude/signal.h 0755 root bin
+f usr/ucbinclude/stdio.h 0755 root bin
+f usr/ucbinclude/strings.h 0755 root bin
+d usr/ucbinclude/sys 0755 root bin
+f usr/ucbinclude/sys/dir.h 0755 root bin
+f usr/ucbinclude/sys/fcntl.h 0755 root bin
+f usr/ucbinclude/sys/file.h 0755 root bin
+f usr/ucbinclude/sys/ioctl.h 0755 root bin
+f usr/ucbinclude/sys/param.h 0755 root bin
+f usr/ucbinclude/sys/resource.h 0755 root bin
+f usr/ucbinclude/sys/rusage.h 0755 root bin
+f usr/ucbinclude/sys/signal.h 0755 root bin
+f usr/ucbinclude/sys/sysmacros.h 0755 root bin
+f usr/ucbinclude/sys/ttychars.h 0755 root bin
+f usr/ucbinclude/sys/types.h 0755 root bin
+f usr/ucbinclude/sys/vfs.h 0755 root bin
+f usr/ucbinclude/sys/wait.h 0755 root bin
+f usr/ucbinclude/unistd.h 0755 root bin
+d usr/ucblib 0755 root bin
+s usr/ucblib/32=.
+s usr/ucblib/64=amd64
+d usr/ucblib/amd64 0755 root bin
+f usr/ucblib/amd64/libucb.so.1 0755 root bin
+s usr/ucblib/amd64/libucb.so=libucb.so.1
+f usr/ucblib/libucb.so.1 0755 root bin
+s usr/ucblib/libucb.so=libucb.so.1
+d usr/xpg4 0755 root bin
+d usr/xpg4/bin 0755 root bin
+s usr/xpg4/bin/alias=../../bin/alias
+f usr/xpg4/bin/ar 0555 root bin
+f usr/xpg4/bin/at 4755 root bin
+f usr/xpg4/bin/awk 0555 root bin
+f usr/xpg4/bin/basename 0555 root bin
+f usr/xpg4/bin/batch 0555 root bin
+s usr/xpg4/bin/bg=../../bin/alias
+s usr/xpg4/bin/cd=../../bin/alias
+s usr/xpg4/bin/chgrp=../../bin/chgrp
+s usr/xpg4/bin/chown=../../bin/chown
+s usr/xpg4/bin/command=../../bin/alias
+f usr/xpg4/bin/cp 0555 root bin
+f usr/xpg4/bin/crontab 4555 root bin
+f usr/xpg4/bin/ctags 0555 root bin
+f usr/xpg4/bin/date 0555 root bin
+f usr/xpg4/bin/df 0555 root bin
+f usr/xpg4/bin/du 0555 root bin
+f usr/xpg4/bin/ed 0555 root bin
+f usr/xpg4/bin/edit 0555 root bin
+s usr/xpg4/bin/egrep=../../bin/grep
+f usr/xpg4/bin/env 0555 root bin
+h usr/xpg4/bin/ex=usr/xpg4/bin/edit
+f usr/xpg4/bin/expr 0555 root bin
+s usr/xpg4/bin/fc=../../bin/alias
+s usr/xpg4/bin/fg=../../bin/alias
+s usr/xpg4/bin/fgrep=../../bin/grep
+f usr/xpg4/bin/file 0555 root bin
+f usr/xpg4/bin/find 0555 root bin
+f usr/xpg4/bin/getconf 0555 root bin
+s usr/xpg4/bin/getopts=../../bin/alias
+s usr/xpg4/bin/grep=../../bin/grep
+s usr/xpg4/bin/hash=../../bin/alias
+f usr/xpg4/bin/id 0555 root bin
+s usr/xpg4/bin/ipcs=../../bin/ipcs
+s usr/xpg4/bin/jobs=../../bin/alias
+s usr/xpg4/bin/kill=../../bin/alias
+f usr/xpg4/bin/link 0555 root bin
+h usr/xpg4/bin/ln=usr/xpg4/bin/cp
+f usr/xpg4/bin/ls 0555 root bin
+f usr/xpg4/bin/m4 0555 root bin
+f usr/xpg4/bin/more 0555 root bin
+h usr/xpg4/bin/mv=usr/xpg4/bin/cp
+f usr/xpg4/bin/nice 0555 root bin
+f usr/xpg4/bin/nl 0555 root bin
+f usr/xpg4/bin/nm 0555 root bin
+f usr/xpg4/bin/nohup 0555 root bin
+s usr/xpg4/bin/od=../../bin/od
+s usr/xpg4/bin/pfsh=../../bin/pfexec
+f usr/xpg4/bin/pr 0555 root bin
+s usr/xpg4/bin/read=../../bin/alias
+f usr/xpg4/bin/rm 0555 root bin
+f usr/xpg4/bin/sed 0555 root bin
+s usr/xpg4/bin/sh=../../bin/ksh93
+f usr/xpg4/bin/sort 0555 root bin
+f usr/xpg4/bin/stty 0555 root bin
+s usr/xpg4/bin/tail=../../bin/tail
+s usr/xpg4/bin/test=../../bin/alias
+s usr/xpg4/bin/tr=../../bin/tr
+s usr/xpg4/bin/type=../../bin/alias
+s usr/xpg4/bin/ulimit=../../bin/alias
+s usr/xpg4/bin/umask=../../bin/alias
+s usr/xpg4/bin/unalias=../../bin/alias
+h usr/xpg4/bin/vedit=usr/xpg4/bin/edit
+h usr/xpg4/bin/vi=usr/xpg4/bin/edit
+h usr/xpg4/bin/view=usr/xpg4/bin/edit
+s usr/xpg4/bin/wait=../../bin/alias
+f usr/xpg4/bin/who 0555 root bin
+d usr/xpg4/include 0755 root bin
+f usr/xpg4/include/curses.h 0644 root bin
+f usr/xpg4/include/term.h 0644 root bin
+f usr/xpg4/include/unctrl.h 0644 root bin
+d usr/xpg4/lib 0755 root bin
+s usr/xpg4/lib/64=amd64
+d usr/xpg4/lib/amd64 0755 root bin
+f usr/xpg4/lib/amd64/libcurses.so.1 0755 root bin
+f usr/xpg4/lib/amd64/libcurses.so.2 0755 root bin
+s usr/xpg4/lib/amd64/libcurses.so=libcurses.so.2
+f usr/xpg4/lib/libcurses.so.1 0755 root bin
+f usr/xpg4/lib/libcurses.so.2 0755 root bin
+s usr/xpg4/lib/libcurses.so=libcurses.so.2
+d var 0755 root sys
+d var/adm 0775 root sys
+d var/adm/acct 0775 adm adm
+d var/adm/acct/fiscal 0775 adm adm
+d var/adm/acct/nite 0775 adm adm
+d var/adm/acct/sum 0775 adm adm
+d var/adm/exacct 0755 adm adm
+d var/adm/log 0755 adm adm
+d var/adm/pool 0755 root sys
+d var/adm/sa 0775 adm sys
+d var/adm/sm.bin 0755 root sys
+d var/adm/streams 0755 root sys
+d var/audit 0755 root sys
+d var/cache 0755 root bin
+d var/cores 0755 root sys
+d var/cron 0755 root sys
+d var/db 0755 root sys
+d var/db/ipf 0755 root sys
+d var/fm 0755 root sys
+d var/fm/fmd 0755 root sys
+d var/fm/fmd/ckpt 0755 root sys
+d var/fm/fmd/rsrc 0755 root sys
+d var/fm/fmd/xprt 0755 root sys
+d var/games 0755 root bin
+d var/idmap 0755 daemon daemon
+d var/inet 0755 root sys
+d var/krb5 0755 root sys
+d var/krb5/rcache 1777 root sys
+d var/krb5/rcache/root 0700 root sys
+d var/ldap 0755 root sys
+d var/lib 0755 root other
+d var/lib/sgml 0755 root sys
+s var/lib/sgml/CATALOG.jade_dsl=CATALOG.openjade
+d var/log 0755 root sys
+d var/log/pool 0755 root sys
+d var/logadm 0755 root bin
+d var/mail 1777 root mail
+d var/mail/:saved 0775 root mail
+d var/news 0755 root bin
+d var/ntp 0755 root sys
+d var/ntp/ntpstats 0755 root sys
+d var/opt 0755 root sys
+d var/pkg 0755 root root
+d var/pkg/download 0755 root root
+d var/pkg/file 0755 root root
+d var/pkg/history 0755 root root
+d var/pkg/index 0755 root root
+d var/pkg/pkg 0755 root root
+d var/pkg/publisher 0755 root root
+d var/pkg/state 0755 root root
+d var/pkg/state/installed 0755 root root
+d var/pkg/state/known 0755 root root
+d var/pkg/tmp 0755 root root
+d var/preserve 1777 root bin
+d var/run 0755 root sys
+d var/sadm 0755 root sys
+d var/sadm/install 0755 root bin
+d var/sadm/install/admin 0755 root bin
+d var/sadm/install/logs 0555 root bin
+d var/sadm/install_data 0755 root sys
+d var/sadm/pkg 0555 root sys
+d var/sadm/security 0555 root sys
+d var/sadm/softinfo 0755 root sys
+s var/sadm/softinfo/INST_RELEASE=../system/admin/INST_RELEASE
+d var/sadm/system 0755 root sys
+d var/sadm/system/admin 0755 root sys
+d var/sadm/system/admin/services 0755 root sys
+d var/sadm/system/data 0755 root sys
+d var/sadm/system/logs 0755 root sys
+d var/saf 0755 root bin
+d var/saf/zsmon 0755 root sys
+d var/smb 0755 root sys
+d var/spool 0755 root bin
+d var/spool/clientmqueue 0770 smmsp smmsp
+d var/spool/cron 0755 root sys
+d var/spool/cron/atjobs 0755 root sys
+d var/spool/cron/crontabs 0755 root sys
+d var/spool/locks 0755 uucp uucp
+d var/spool/mqueue 0750 root bin
+d var/spool/pkg 1777 root bin
+d var/svc 0755 root sys
+d var/svc/log 0755 root sys
+d var/svc/manifest 0755 root sys
+d var/svc/manifest/application 0755 root sys
+d var/svc/manifest/application/management 0755 root sys
+d var/svc/manifest/application/security 0755 root sys
+d var/svc/manifest/device 0755 root sys
+d var/svc/manifest/milestone 0755 root sys
+d var/svc/manifest/network 0755 root sys
+d var/svc/manifest/network/dns 0755 root sys
+d var/svc/manifest/network/ipsec 0755 root sys
+d var/svc/manifest/network/ldap 0755 root sys
+d var/svc/manifest/network/nfs 0755 root sys
+d var/svc/manifest/network/nis 0755 root sys
+d var/svc/manifest/network/routing 0755 root sys
+d var/svc/manifest/network/rpc 0755 root sys
+d var/svc/manifest/network/security 0755 root sys
+d var/svc/manifest/network/shares 0755 root sys
+d var/svc/manifest/network/ssl 0755 root sys
+d var/svc/manifest/platform 0755 root sys
+d var/svc/manifest/site 0755 root sys
+d var/svc/manifest/system 0755 root sys
+d var/svc/manifest/system/device 0755 root sys
+d var/svc/manifest/system/filesystem 0755 root sys
+d var/svc/manifest/system/security 0755 root sys
+d var/svc/manifest/system/svc 0755 root sys
+d var/svc/profile 0755 root sys
+d var/tmp 1777 root sys
+d var/yp 0755 root bin
+d var/yp/binding 0755 root bin
diff --git a/usr/src/Makefile b/usr/src/Makefile
index 4342f8d6dd..7f60c24fe8 100644
--- a/usr/src/Makefile
+++ b/usr/src/Makefile
@@ -25,7 +25,7 @@
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
# Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright 2016 Toomas Soome <tsoome@me.com>
-# Copyright 2018 Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
#
@@ -50,7 +50,7 @@ $(SPARC_BLD)psm: stand
SUBDIRS= $(COMMON_SUBDIRS) $($(MACH)_SUBDIRS)
-HDRSUBDIRS= uts head lib cmd
+HDRSUBDIRS= uts head lib cmd ucbhead
# UCB headers are bug-for-bug compatible and not checkable against the header
# standards.
@@ -60,7 +60,7 @@ CHKHDRSUBDIRS= head uts lib
#
# Headers that can be built in parallel
#
-PARALLEL_HEADERS = sysheaders userheaders libheaders cmdheaders
+PARALLEL_HEADERS = sysheaders userheaders libheaders ucbheaders cmdheaders
#
# Directories that can be built in parallel
@@ -105,7 +105,7 @@ all: mapfiles closedbins sgs .WAIT $(SUBDIRS) pkg
# packaging to be pulled from $(SRC) and $(CLOSED) and staged in
# $(ROOT)/licenses.
#
-install: install1 install2 _msg stage-licenses
+install: install1 install2 _msg
@cd msg; pwd; $(MAKE) _msg
@rm -rf "$(ROOT)/catalog"
@@ -133,7 +133,10 @@ clobber_local:
# If the tarballs are included inside ON_CLOSED_BINS, use those to extract and
# preserve the permissions (in case a distro ships them).
-
+#
+# intel_nhmex hard codes the use of older smbios structures. Therefore,
+# we disable its use of smbios. See OS-4435 for more information.
+#
closedbins: bldtools $(ROOTDIRS) FRC
@CLOSED_ROOT="$$ON_CLOSED_BINS/root_$(MACH)$${RELEASE_BUILD+-nd}"; \
if [ -f "$$ON_CLOSED_BINS/on-closed-bins$${RELEASE_BUILD+-nd}.$(MACH).tar.bz2" ]; then \
@@ -154,9 +157,14 @@ closedbins: bldtools $(ROOTDIRS) FRC
$(TAR) cfX - $(CODEMGR_WS)/exception_lists/closed-bins .) | \
(cd $(ROOT); $(TAR) xBpf -); \
$(CHMOD) 0400 $(ROOT)/etc/security/tsol/label_encodings; \
+ $(ECHO) "Stripping CTF from closed-bins in $$ROOT"; \
( cd $(ROOT); $(CTFSTRIP) $$(cd $$CLOSED_ROOT; $(FIND) \
./kernel ./usr/kernel ./platform/*/kernel -type f -a -perm -u+x | \
- $(EGREP) -vf $(CODEMGR_WS)/exception_lists/closed-bins) )
+ $(EGREP) -vf $(CODEMGR_WS)/exception_lists/closed-bins) ); \
+ $(ECHO) "Fixing intel_nhmex.conf in $$ROOT"; \
+ (cd $(ROOT); \
+ echo "no-smbios=1;" >> kernel/drv/intel_nhmex.conf)
+
#
# Declare what parts can be built in parallel
@@ -166,13 +174,13 @@ closedbins: bldtools $(ROOTDIRS) FRC
.PARALLEL: $(PARALLEL_HEADERS) DUMMY
.PARALLEL: $(PARALLEL_DIRS) DUMMY
-$(SUBDIRS) head pkg: FRC
+$(SUBDIRS) head ucbhead pkg: FRC
@cd $@; pwd; $(MAKE) $(TARGET)
# librpcsvc has a dependency on headers installed by
# userheaders, hence the .WAIT before libheaders.
sgs: rootdirs .WAIT sysheaders userheaders .WAIT \
- libheaders cmdheaders
+ libheaders ucbheaders cmdheaders
#
# Top-level setup target to setup the development environment that includes
@@ -215,6 +223,9 @@ libheaders: bldtools
sysheaders: FRC
@cd uts; pwd; $(MAKE) install_h
+ucbheaders: FRC
+ @cd ucbhead; pwd; $(MAKE) install_h
+
cmdheaders: FRC
@cd cmd/devfsadm; pwd; $(MAKE) install_h
@cd cmd/fm; pwd; $(MAKE) install_h
diff --git a/usr/src/Makefile.master b/usr/src/Makefile.master
index 983c57f0bc..071e5dcab1 100644
--- a/usr/src/Makefile.master
+++ b/usr/src/Makefile.master
@@ -203,8 +203,8 @@ GENOFFSETS= $(ONBLD_TOOLS)/bin/genoffsets
XREF= $(ONBLD_TOOLS)/bin/xref
FIND= /usr/bin/find
PERL= /usr/bin/perl
-PERL_VERSION= 5.10.0
-PERL_PKGVERS= -510
+PERL_VERSION= 5.12
+PERL_PKGVERS= -512
PERL_MACH= i86pc
$(SPARC_BLD)PERL_MACH= sun4
PERL_VARIANT=
@@ -303,7 +303,8 @@ PYSHEBANG= $(PYTHON3)
# rebuilds if the baked-in mtime != the mtime of the source file
# (rather than only if it's less than), thus when installing python
# files we must make certain to not adjust the mtime of the source
-# (.py) file.
+# (.py) file. As a part of this we also go through and change the #!
+# line in the python script to that of the actual python we are using.
#
INS.pyfile= $(RM) $@; $(SED) \
-e "1s:^\#!@PYTHON@:\#!$(PYSHEBANG):" \
@@ -560,9 +561,17 @@ sparcv9_COPTFLAG= -xO3
i386_COPTFLAG= -O
amd64_COPTFLAG= -xO3
+# This would normally be added by cw(1) but cannot be while we want to support
+# Both GCC 3.x and GCC 4.x
+$(__GNUC4)$(MACH)_COPTFLAG += -_gcc=-fno-inline-small-functions \
+ -_gcc=-fno-inline-functions-called-once
+$(__GNUC4)$(MACH64)_COPTFLAG += -_gcc=-fno-inline-small-functions \
+ -_gcc=-fno-inline-functions-called-once
+
COPTFLAG= $($(MACH)_COPTFLAG)
COPTFLAG64= $($(MACH64)_COPTFLAG)
+
# When -g is used, the compiler globalizes static objects
# (gives them a unique prefix). Disable that.
CNOGLOBAL= -W0,-noglobal
@@ -716,8 +725,7 @@ CPPFLAGS.first= # Please keep empty. Only lower makefiles should set this.
CPPFLAGS.master=$(DTEXTDOM) $(DTS_ERRNO) \
$(ENVCPPFLAGS1) $(ENVCPPFLAGS2) $(ENVCPPFLAGS3) $(ENVCPPFLAGS4) \
$(ADJUNCT_PROTO:%=-I%/usr/include)
-CPPFLAGS.native=$(ENVCPPFLAGS1) $(ENVCPPFLAGS2) $(ENVCPPFLAGS3) \
- $(ENVCPPFLAGS4) -I$(NATIVE_ADJUNCT)/include
+CPPFLAGS.native=-I$(NATIVE_ADJUNCT)/include
CPPFLAGS= $(CPPFLAGS.first) $(CPPFLAGS.master)
AS_CPPFLAGS= $(CPPFLAGS.first) $(CPPFLAGS.master)
JAVAFLAGS= -source 7 -target 7 -Xlint:deprecation,-options
diff --git a/usr/src/Makefile.master.64 b/usr/src/Makefile.master.64
index f203e4cce4..37d6d288a3 100644
--- a/usr/src/Makefile.master.64
+++ b/usr/src/Makefile.master.64
@@ -21,6 +21,7 @@
#
# Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
+# Copyright (c) 2014 Joyent, Inc. All rights reserved.
# Copyright 2016 RackTop Systems.
# Copyright 2021 Joyent, Inc.
#
diff --git a/usr/src/Makefile.psm.targ b/usr/src/Makefile.psm.targ
index 15b6cb6d17..9c19babf3f 100644
--- a/usr/src/Makefile.psm.targ
+++ b/usr/src/Makefile.psm.targ
@@ -49,6 +49,9 @@ $(ROOT_PSM_MOD_DIR): $(ROOT_PSM_DIR)
$(ROOT_PSM_MOD_DIRS_32): $(ROOT_PSM_MOD_DIR)
-$(INS.dir)
+$(ROOT_PSM_PCBE_DIR): $(ROOT_PSM_MOD_DIR)
+ -$(INS.dir)
+
$(ROOT_PSM_DIR)/%: % $(ROOT_PSM_DIR)
$(INS.file)
diff --git a/usr/src/Makefile.testarchive b/usr/src/Makefile.testarchive
new file mode 100644
index 0000000000..066ee12281
--- /dev/null
+++ b/usr/src/Makefile.testarchive
@@ -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 2021 Joyent, Inc.
+#
+
+#
+# Used by smartos-live.git, this Makefile simply defines a macro
+# that contains the set of illumos-joyent tests that get built
+# into the test archive deliverable.
+#
+TEST_IPS_MANIFEST_FILES = \
+ developer-build-make.p5m \
+ system-bhyve-tests.p5m \
+ system-dtrace-tests.p5m \
+ system-file-system-zfs-tests.p5m \
+ system-io-tests.p5m \
+ system-test-cryptotest.p5m \
+ system-test-elftest.p5m \
+ system-test-libctest.p5m \
+ system-test-nettest.p5m \
+ system-test-ostest.p5m \
+ system-test-smbclient.p5m \
+ system-test-testrunner.p5m \
+ system-test-utiltest.p5m \
+ system-test-vndtest.p5m \
+ system-test-smartostest.p5m \
+ system-test-zfstest.p5m
+
diff --git a/usr/src/Targetdirs b/usr/src/Targetdirs
index a8ee77b2c4..7e30546e68 100644
--- a/usr/src/Targetdirs
+++ b/usr/src/Targetdirs
@@ -46,15 +46,20 @@ $(BUILD64) TARGETDIRS += $(DIRS64)
TARGETDIRS += $(FILELINKS) $(DIRLINKS)
-i386_DIRS= \
- /boot/acpi \
- /boot/acpi/tables \
- /boot/grub \
- /boot/grub/bin \
- /platform/i86pc \
- /lib/libmvec \
- /usr/lib/xen \
- /usr/lib/xen/bin \
+i386_DIRS= \
+ /boot/acpi \
+ /boot/acpi/tables \
+ /boot/grub \
+ /boot/grub/bin \
+ /lib/libmvec \
+ /platform/i86pc \
+ /usr/lib/brand/bhyve \
+ /usr/lib/brand/kvm \
+ /usr/lib/brand/lx \
+ /usr/lib/brand/lx/amd64 \
+ /usr/lib/brand/lx/distros \
+ /usr/lib/xen \
+ /usr/lib/xen/bin \
/usr/share/bhyve
sparc_DIRS= \
@@ -102,9 +107,12 @@ DIRS= \
/devices \
/devices/pseudo \
/etc \
+ /etc/bash \
+ /etc/bash/bash_completion.d \
/etc/brand \
/etc/brand/solaris10 \
/etc/cron.d \
+ /etc/cron.d/crontabs \
/etc/crypto \
/etc/crypto/certs \
/etc/crypto/crls \
@@ -134,6 +142,7 @@ DIRS= \
/etc/hal/fdi/preprobe/20thirdparty \
/etc/hal/fdi/preprobe/30user \
/etc/ipadm \
+ /etc/ipf \
/etc/iscsi \
/etc/rpcsec \
/etc/security \
@@ -263,6 +272,9 @@ DIRS= \
/usr/lib/abi \
/usr/lib/brand \
/usr/lib/brand/ipkg \
+ /usr/lib/brand/jcommon \
+ /usr/lib/brand/joyent \
+ /usr/lib/brand/joyent-minimal \
/usr/lib/brand/labeled \
/usr/lib/brand/shared \
/usr/lib/brand/sn1 \
@@ -289,6 +301,7 @@ DIRS= \
/usr/lib/inet/ilb \
/usr/lib/krb5 \
/usr/lib/link_audit \
+ /usr/lib/localedef/extensions \
/usr/lib/lwp \
/usr/lib/mdb \
/usr/lib/mdb/kvm \
@@ -513,6 +526,9 @@ SYM.DIRS= \
/usr/ucblib/32 \
/var/ld/32
+i386_SYM.DIRS64= \
+ /usr/lib/brand/lx/64
+
sparc_SYM.DIRS64=
SYM.DIRS64= \
@@ -626,6 +642,7 @@ $(BUILD64) $(ROOT)/lib/crypto/64:= LINKDEST=$(MACH64)
$(BUILD64) $(ROOT)/lib/secure/64:= LINKDEST=$(MACH64)
$(BUILD64) $(ROOT)/usr/lib/64:= LINKDEST=$(MACH64)
$(BUILD64) $(ROOT)/usr/lib/elfedit/64:= LINKDEST=$(MACH64)
+$(BUILD64) $(ROOT)/usr/lib/brand/lx/64:= LINKDEST=$(MACH64)
$(BUILD64) $(ROOT)/usr/lib/brand/sn1/64:= LINKDEST=$(MACH64)
$(BUILD64) $(ROOT)/usr/lib/brand/solaris10/64:= LINKDEST=$(MACH64)
$(BUILD64) $(ROOT)/usr/lib/lwp/64:= LINKDEST=$(MACH64)
diff --git a/usr/src/boot/common/console.c b/usr/src/boot/common/console.c
index 4b0b5c5eb3..3f65566d6c 100644
--- a/usr/src/boot/common/console.c
+++ b/usr/src/boot/common/console.c
@@ -23,7 +23,9 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
-
+/*
+ * Copyright (c) 2019, Joyent, Inc.
+ */
#include <sys/cdefs.h>
#include <stand.h>
@@ -321,15 +323,6 @@ cons_change(const char *string, char **list)
*list = cons_add_list(*list, curpos);
continue;
}
-
- if (active != 0) {
- /*
- * If no consoles have initialised we wouldn't
- * see this.
- */
- printf("console %s failed to initialize\n",
- consoles[cons]->c_name);
- }
}
}
diff --git a/usr/src/boot/efi/loader/Makefile.com b/usr/src/boot/efi/loader/Makefile.com
index 576f60eaf0..963ea246e6 100644
--- a/usr/src/boot/efi/loader/Makefile.com
+++ b/usr/src/boot/efi/loader/Makefile.com
@@ -12,6 +12,8 @@
#
# Copyright 2016 Toomas Soome <tsoome@me.com>
#
+# Copyright 2022 Joyent, Inc.
+#
include $(SRC)/boot/Makefile.version
include $(SRC)/boot/Makefile.inc
@@ -77,6 +79,14 @@ CPPFLAGS += -I$(SRC)/uts/intel/sys/acpi
CPPFLAGS += -I$(PNGLITE)
CPPFLAGS += -DNO_PCI -DEFI
+#
+# Using SNP from loader causes issues when chain-loading iPXE, as described in
+# TRITON-1191. While the exact problem is not known, we have no use for SNP, so
+# we'll just disable it.
+#
+CPPFLAGS += -DLOADER_DISABLE_SNP
+
+
DPLIBSA= ../../../libsa/$(MACHINE)/libsa_pics.a
LIBSA= -L../../../libsa/$(MACHINE) -lsa_pics
diff --git a/usr/src/boot/efi/loader/conf.c b/usr/src/boot/efi/loader/conf.c
index d29cbca573..e493d7cb03 100644
--- a/usr/src/boot/efi/loader/conf.c
+++ b/usr/src/boot/efi/loader/conf.c
@@ -24,6 +24,10 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+/*
+ * Copyright (c) 2019, Joyent, Inc.
+ */
+
#include <sys/cdefs.h>
#include <stand.h>
@@ -38,7 +42,9 @@ struct devsw *devsw[] = {
&efipart_fddev,
&efipart_cddev,
&efipart_hddev,
+#ifndef LOADER_DISABLE_SNP
&efinet_dev,
+#endif
&vdisk_dev,
&zfs_dev,
NULL
@@ -60,7 +66,9 @@ struct fs_ops *file_system[] = {
};
struct netif_driver *netif_drivers[] = {
+#ifndef LOADER_DISABLE_SNP
&efinetif,
+#endif
NULL
};
diff --git a/usr/src/boot/forth/Makefile b/usr/src/boot/forth/Makefile
index 5de1fc3878..5d43a4e9ad 100644
--- a/usr/src/boot/forth/Makefile
+++ b/usr/src/boot/forth/Makefile
@@ -11,6 +11,8 @@
#
# Copyright 2022 Toomas Soome <tsoome@me.com>
+# Copyright 2022 Joyent, Inc.
+# Copyright 2022 MNX Cloud, Inc.
#
include $(SRC)/Makefile.master
@@ -27,6 +29,7 @@ FORTH = beastie.4th
FORTH += beadm.4th
FORTH += brand.4th
FORTH += brand-illumos.4th
+FORTH += brand-smartos.4th
FORTH += check-password.4th
FORTH += color.4th
FORTH += delay.4th
@@ -40,6 +43,7 @@ FORTH += logo-fbsdbw.4th
FORTH += logo-illumos.4th
FORTH += logo-orb.4th
FORTH += logo-orbbw.4th
+FORTH += logo-smartos.4th
FORTH += menu.4th
FORTH += menu.rc
FORTH += menu-commands.4th
@@ -51,6 +55,8 @@ FORTH += support.4th
FORTH += version.4th
FILES += illumos-logo.png
FILES += illumos-brand.png
+FILES += triton-logo.png
+FILES += triton.png
FILES += loader.rc
all clean clobber:
@@ -64,6 +70,11 @@ $(ROOT_BOOT)/%: % $(ROOT_BOOT)
$(ROOT_BOOT_DEFAULTS)/%: % $(ROOT_BOOT_DEFAULTS)
$(INS.file)
+$(ROOT_BOOT_FORTH)/menu.rc: triton.menu.rc $(ROOT_BOOT_FORTH)
+ $(RM) $@; \
+ $(INS) -s -m $(FILEMODE) -f $(@D) triton.menu.rc; \
+ $(MV) $(@D)/triton.menu.rc $@
+
$(ROOT_BOOT_FORTH)/%: % $(ROOT_BOOT_FORTH)
$(INS.file)
diff --git a/usr/src/boot/forth/brand-smartos.4th b/usr/src/boot/forth/brand-smartos.4th
new file mode 100644
index 0000000000..acdbcf13af
--- /dev/null
+++ b/usr/src/boot/forth/brand-smartos.4th
@@ -0,0 +1,38 @@
+\
+\ 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 2019 Joyent, Inc.
+\ Copyright 2022 MNX Cloud, Inc.
+\
+
+2 brandX ! 1 brandY ! \ Initialize brand placement defaults
+
+: brand+ ( x y c-addr/u -- x y' )
+ 2swap 2dup at-xy 2swap \ position the cursor
+ type \ print to the screen
+ 1+ \ increase y for next time we're called
+;
+
+: brand ( x y -- ) \ "triton" [wide] logo in B/W (7 rows x 42 columns)
+
+ 0 1 1 0 0 s" /boot/triton-logo.png" fb-putimage if 2drop exit then
+
+ s" " brand+
+ s" ##### #### # ##### ### # # " brand+
+ s" # # # # # # # ## # " brand+
+ s" # #### # # # # # # # " brand+
+ s" # # # # # # # # ## " brand+
+ s" # # # # # # # # ## " brand+
+ s" # # # # # ### # # TM " brand+
+
+ 2drop
+;
diff --git a/usr/src/boot/forth/loader.4th b/usr/src/boot/forth/loader.4th
index d010b72a33..9bd01644e0 100644
--- a/usr/src/boot/forth/loader.4th
+++ b/usr/src/boot/forth/loader.4th
@@ -606,6 +606,24 @@ only forth definitions also support-functions
then
;
+create pathname 1024 chars allot
+
+: set-platform ( c-addr u -- )
+ 2dup
+ pathname place
+ s" /platform/i86pc/kernel/amd64/unix" pathname append
+ pathname count s" bootfile" setenv
+ pathname count erase
+ 2dup
+ pathname place
+ s" /platform/i86pc/amd64/boot_archive" pathname append
+ pathname count s" boot_archive" set-module-path
+ pathname count erase
+ pathname place
+ s" /platform/i86pc/amd64/boot_archive.hash" pathname append
+ pathname count s" boot_archive.hash" set-module-path
+;
+
\ Words to be used inside configuration files
: retry false ; \ For use in load error commands
diff --git a/usr/src/boot/forth/loader.rc b/usr/src/boot/forth/loader.rc
index 32f6bf8043..989a014da4 100644
--- a/usr/src/boot/forth/loader.rc
+++ b/usr/src/boot/forth/loader.rc
@@ -2,6 +2,16 @@
\
\ Includes additional commands
include /boot/forth/loader.4th
+
+\ For SmartOS, override the default color scheme back to the more traditional
+\ black background and white foreground.
+set tem.fg_color=white
+set tem.bg_color=black
+
+\ As another SmartOS customization, work around OS-8160 by limiting the size of
+\ the framebuffer.
+framebuffer set 1024x768
+
try-include /boot/loader.rc.local
\ Reads and processes loader.conf variables
diff --git a/usr/src/boot/forth/logo-smartos.4th b/usr/src/boot/forth/logo-smartos.4th
new file mode 100644
index 0000000000..f897c45165
--- /dev/null
+++ b/usr/src/boot/forth/logo-smartos.4th
@@ -0,0 +1,41 @@
+\
+\ 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 2019 Joyent, Inc.
+\
+
+52 logoX ! 11 logoY ! \ Initialize logo placement defaults
+
+: logo+ ( x y c-addr/u -- x y' )
+ 2swap 2dup at-xy 2swap \ position the cursor
+ [char] @ escc! \ replace @ with Esc
+ type \ print to the screen
+ 1+ \ increase y for next time we're called
+;
+
+: logo ( x y -- ) \ color Illumos logo
+
+ 0 0 0 0 0 s" /boot/triton.png" fb-putimage if 2drop exit then
+
+ s" @[31m--@[0;31m+--@[1;31m*@[0;33m--@[1;33m*" logo+
+ s" @[31m|@[1m\@[0m @[31m|\ |@[33m\ @[1m|\" logo+
+ s" @[31m| @[1m\@[0;31m|@[37m @[31m\| @[33m\@[1m| \" logo+
+ s" @[31m+--@[1;31m*@[31m--+@[0;33m--@[1;33m*@[33m--@[33m*" logo+
+ s" |@[31m\ |\ |\ @[33m|@[1m\ |" logo+
+ s" | @[31m\| \| \@[33m| @[1m\|" logo+
+ s" @[1m*@[0m--+@[31m--+@[33m--+@[1m--+" logo+
+ s" @[1m \ |@[0;34m\ |\ |@[1m\ |" logo+
+ s" @[1m \| @[0;34m\| \| @[1m\|" logo+
+ s" @[1m *--+@[0;34m--@[1;34m*@[34m--@[34m*" logo+
+
+ 2drop
+;
diff --git a/usr/src/boot/forth/menu-commands.4th b/usr/src/boot/forth/menu-commands.4th
index 5fe6c8c62f..4938608fb5 100644
--- a/usr/src/boot/forth/menu-commands.4th
+++ b/usr/src/boot/forth/menu-commands.4th
@@ -41,6 +41,43 @@ variable kmdb_state
also menu-namespace also menu-command-helpers
+\ PATH_MAX + 6
+create chaincmd 1030 chars allot
+
+\
+\ Rollback to previous platform image.
+\ Used by Joyent Triton
+\
+: rollback_boot ( N -- NOTREACHED )
+ dup
+ s" prev-platform" getenv s" bootfile" setenv
+ s" prev-archive" getenv s" boot_archive" set-module-path
+ s" prev-hash" getenv s" boot_archive.hash" set-module-path
+ 0 boot ( state -- )
+;
+
+\
+\ Boot from ipxe kernel
+\ Used by Joyent Triton when booted in BIOS/CSM mode
+\
+: ipxe_boot ( N -- NOTREACHED )
+ dup
+ s" ipxe-bootfile" getenv s" bootfile" setenv
+ s" ipxe-archive" getenv s" boot_archive" set-module-path
+ s" boot_archive.hash" disable-module
+ 0 boot ( state -- )
+;
+
+\
+\ Chainload the ipxe EFI binary
+\ Used by Joyent Triton when booted in UEFI mode
+\
+: ipxe_chainload ( N -- NOTREACHED )
+ s" chain " chaincmd place
+ s" ipxe-efi" getenv chaincmd append
+ chaincmd count evaluate
+;
+
\
\ Boot
\
@@ -102,6 +139,25 @@ also menu-namespace also menu-command-helpers
;
\
+\ Platform-image selection for standalone SmartOS is mostly in pi.rc.
+\ We will also steal a boot environment routine in pi_draw_screen (way below).
+
+: init_pi ( -- )
+ s" bootpi" getenv? 0= if
+ s" default" s" bootpi" setenv
+ then
+
+ \ Reset the "options" text to show current bootpi selected.
+ s" set menu_optionstext=${pitext}${bootpi}" evaluate
+ s" set pimenu_optionstext=${pitext}${bootpi}" evaluate
+;
+
+\ Shorter than inlining this in pi.rc.
+: pi_unload ( -- )
+ s" unload" evaluate
+;
+
+\
\ Single User Mode
\
@@ -246,6 +302,53 @@ also menu-namespace also menu-command-helpers
;
\
+\ Disaster Recovery boot
+\
+
+: rescue_enabled? ( -- flag )
+ s" noimport" getenv -1 <> dup if
+ swap drop ( c-addr flag -- flag )
+ then
+;
+
+: rescue_enable ( -- )
+ s" set noimport=true" evaluate
+ s" smartos" getenv? if
+ s" set standalone=true" evaluate
+ s" set smartos=false" evaluate
+ then
+;
+
+: rescue_disable ( -- )
+ s" noimport" unsetenv
+ s" standalone" unsetenv
+ s" smartos" getenv? if
+ s" set smartos=true" evaluate
+ then
+;
+
+: init_rescue ( N -- N )
+ rescue_enabled? if
+ toggle_menuitem ( n -- n )
+ then
+;
+
+: toggle_rescue ( N -- N TRUE )
+ toggle_menuitem
+ menu-redraw
+
+ \ Now we're going to make the change effective
+
+ dup toggle_stateN @ 0= if
+ rescue_disable
+ else
+ rescue_enable
+ then
+
+ TRUE \ loop menu again
+;
+
+\
\ Escape to Prompt
\
@@ -505,6 +608,17 @@ also menu-namespace also menu-command-helpers
menu-init \ Initialize menu and draw bounding box (in menu.4th)
;
+\ PI reuse of be_draw_screen, plus some other things to be used by pi.rc.
+
+: pi_draw_screen ( -- TRUE )
+ \ So we can make SURE we have the current boot PI on display.
+ init_pi
+
+ be_draw_screen
+ menu-redraw
+ TRUE
+;
+
\
\ Select a boot environment
\
diff --git a/usr/src/boot/forth/menu.4th b/usr/src/boot/forth/menu.4th
index 1fd20811c6..fee6ec3806 100644
--- a/usr/src/boot/forth/menu.4th
+++ b/usr/src/boot/forth/menu.4th
@@ -3,6 +3,7 @@
\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
\ Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
\ All rights reserved.
+\ Copyright 2019 Joyent, Inc.
\
\ Redistribution and use in source and binary forms, with or without
\ modification, are permitted provided that the following conditions
@@ -332,7 +333,7 @@ also menu-infrastructure definitions
\ Print the frame caption at (x,y)
s" loader_menu_title" getenv dup -1 = if
- drop s" Welcome to illumos"
+ drop s" Welcome to SmartOS"
then
TRUE ( use default alignment )
s" loader_menu_title_align" getenv dup -1 <> if
@@ -549,14 +550,39 @@ also menu-infrastructure definitions
then
;
-\ Takes an integer on the stack and updates the timeout display.
+: do_ipxe ( -- bool)
+ \ getenv? leaves -1 on stack if the env var exists. Thus if both
+ \ headnode and ipxe exist then the sum of what will be left on the
+ \ stack should be -2.
+ s" headnode" getenv? s" ipxe" getenv? + -2 = if
+ s" ipxe" getenv s" true" compare 0= if
+ true
+ else
+ false
+ then
+ else
+ false
+ then
+;
+
+\ Takes a single integer on the stack and updates the timeout display. The
+\ integer must be between 0 and 9 (we will only update a single digit in the
+\ source message).
\
: menu-timeout-update ( N -- )
\ Enforce minimum
dup 0 < if drop 0 then
- menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
+ s" headnode" getenv? if
+ do_ipxe if
+ s" Autoboot in N seconds from PXE. [Space] to pause" ( n -- n c-addr/u )
+ else
+ s" Autoboot in N seconds from the USB Key. [Space] to pause" ( n -- n c-addr/u )
+ then
+ else
+ s" Autoboot in N seconds. [Space] to pause" ( n -- n c-addr/u )
+ then
dup 0> if
s" Autoboot in " type
@@ -1012,8 +1038,16 @@ also menu-namespace
dup over 13 = swap 10 = or if
drop ( no longer needed )
cursor-normal cursor-set
- s" boot" evaluate
- exit ( pedantic; never reached )
+ do_ipxe if
+ s" efi-version" getenv? if
+ s" ipxe_chainload" evaluate
+ else
+ s" ipxe_boot" evaluate
+ then
+ else
+ s" boot" evaluate
+ exit ( pedantic; never reached )
+ then
then
dup menureboot @ = if 0 reboot then
diff --git a/usr/src/boot/forth/support.4th b/usr/src/boot/forth/support.4th
index 2abf48f70b..961d3b528a 100644
--- a/usr/src/boot/forth/support.4th
+++ b/usr/src/boot/forth/support.4th
@@ -245,6 +245,21 @@ create last_module_option sizeof module.next allot 0 last_module_option !
then
;
+\ Place string into an allocated buffer
+\
+\ e.g
+\ create mystring 32 chars allot
+\ s" Burning down " mystring place
+\
+: place over over >r >r char+ swap chars move r> r> c! ;
+
+\ Append string
+\
+\ e.g.
+\ s" the house!" mystring append
+\
+: append over over >r >r count chars + swap chars move r> r> dup >r c@ + r> c! ;
+
\ execute xt for each device listed in console variable.
\ this allows us to have device specific output for logos, menu frames etc
: console-iterate { xt | caddr clen taddr tlen -- }
diff --git a/usr/src/boot/forth/triton-logo.png b/usr/src/boot/forth/triton-logo.png
new file mode 100644
index 0000000000..79e917b1e8
--- /dev/null
+++ b/usr/src/boot/forth/triton-logo.png
Binary files differ
diff --git a/usr/src/boot/forth/triton.menu.rc b/usr/src/boot/forth/triton.menu.rc
new file mode 100644
index 0000000000..3dddb7ffe7
--- /dev/null
+++ b/usr/src/boot/forth/triton.menu.rc
@@ -0,0 +1,109 @@
+\ Menu.rc
+\
+\ Load required Forth modules
+include /boot/forth/version.4th
+include /boot/forth/brand.4th
+include /boot/forth/menu.4th
+include /boot/forth/menu-commands.4th
+include /boot/forth/shortcuts.4th
+
+\ Screen prep
+clear \ clear the screen (see `screen.4th')
+print_version \ print version string (bottom-right; see `version.4th')
+draw-beastie \ draw freebsd mascot (on right; see `beastie.4th')
+draw-brand \ draw the FreeBSD title (top-left; see `brand.4th')
+menu-init \ initialize the menu area (see `menu.4th')
+
+\ Initialize main menu constructs (see `menu.4th')
+\ NOTE: To use `non-ansi' variants, add `loader_color=0' to loader.conf(5)
+\ NOTE: ANSI variants can use `^' in place of literal `Esc' (ASCII 27)
+
+\
+\ MAIN MENU
+\
+
+set menuset_name1="main"
+
+set mainmenu_init[1]="init_boot"
+
+set mainmenu_caption[1]="Boot SmartOS [Enter]"
+set mainansi_caption[1]="^[1mB^[moot SmartOS ^[1m[Enter]^[m"
+set mainmenu_command[1]="boot"
+\ keycode set by init_boot
+
+set mainmenu_caption[2]="[Esc]ape to loader prompt"
+set mainmenu_command[2]="goto_prompt"
+set mainmenu_keycode[2]=27
+set mainansi_caption[2]="^[1mEsc^[mape to loader prompt"
+
+\ Enable built-in "Reboot" trailing menuitem
+\ NOTE: appears before menu_options if configured
+\
+set mainmenu_reboot
+
+\ Enable "Options:" separator. When set to a numerical value (1-8), a visual
+\ separator is inserted before that menuitem number.
+\
+set mainmenu_options=4
+
+set mainmenu_caption[4]="Configure Boot [O]ptions..."
+set mainmenu_command[4]="2 goto_menu"
+set mainmenu_keycode[4]=111
+set mainansi_caption[4]="Configure Boot ^[1mO^[mptions..."
+
+\
+\ If available, load the "Platform Image Selection" option.
+\
+try-include /os/pi.rc
+
+\
+\ BOOT OPTIONS MENU
+\
+
+set menuset_name2="options"
+
+set optionsmenu_caption[1]="Back to Main Menu [Backspace]"
+set optionsmenu_command[1]="1 goto_menu"
+set optionsmenu_keycode[1]=8
+set optionsansi_caption[1]="Back to Main Menu ^[1m[Backspace]^[m"
+
+set optionsmenu_options=2
+set optionsmenu_optionstext="Boot Options:"
+
+set optionsmenu_osconsole=2
+set optionsmenu_command[2]="cycle_osconsole"
+set optionsmenu_keycode[2]=99
+
+set optionsmenu_init[3]="init_verbose"
+set optionsmenu_caption[3]="[V]erbose............. Off"
+set optionstoggled_text[3]="[V]erbose............. On"
+set optionsmenu_command[3]="toggle_verbose"
+set optionsmenu_keycode[3]=118
+set optionsansi_caption[3]="^[1mV^[merbose............... ^[34;1mOff^[m"
+set optionstoggled_ansi[3]="^[1mV^[merbose............... ^[32;7mOn^[m"
+
+set optionsmenu_kmdb=4
+set optionsmenu_command[4]="cycle_kmdb"
+set optionsmenu_keycode[4]=107
+
+set optionsmenu_init[5]="init_rescue"
+set optionsmenu_caption[5]="[R]escue Mode......... Off"
+set optionstoggled_text[5]="[R]escue Mode......... On"
+set optionsmenu_command[5]="toggle_rescue"
+set optionsmenu_keycode[5]=114
+set optionsansi_caption[5]="^[1mR^[mescue Mode........... ^[34;1mOff^[m"
+set optionstoggled_ansi[5]="^[1mR^[mescue Mode........... ^[32;7mOn^[m"
+
+\ Enable automatic booting (add ``autoboot_delay=N'' to loader.conf(5) to
+\ customize the timeout; default is 10-seconds)
+\
+set menu_timeout_command="boot"
+
+\ Include optional elements defined in a local file
+\
+try-include /boot/menu.rc.local
+
+\ Display the main menu (see `menu.4th')
+set menuset_initial=1
+menuset-loadinitial
+menu-display
diff --git a/usr/src/boot/forth/triton.png b/usr/src/boot/forth/triton.png
new file mode 100644
index 0000000000..9b7e9a94b6
--- /dev/null
+++ b/usr/src/boot/forth/triton.png
Binary files differ
diff --git a/usr/src/boot/i386/libi386/i386_copy.c b/usr/src/boot/i386/libi386/i386_copy.c
index bcfd475c37..e34eddd5b0 100644
--- a/usr/src/boot/i386/libi386/i386_copy.c
+++ b/usr/src/boot/i386/libi386/i386_copy.c
@@ -24,6 +24,10 @@
* SUCH DAMAGE.
*/
+/*
+ * Copyright (c) 2019, Joyent, Inc.
+ */
+
#include <sys/cdefs.h>
/*
@@ -96,6 +100,8 @@ smap_find(struct bios_smap *smap, int smaplen, vm_offset_t addr, size_t size)
return (0);
}
+#define SAFE_LOAD_BASE 0xc800000
+
/*
* Find usable address for loading. The address for the kernel is fixed, as
* it is determined by kernel linker map (dboot PT_LOAD address).
@@ -119,8 +125,13 @@ i386_loadaddr(uint_t type, void *data, vm_offset_t addr)
if (type == LOAD_KERN)
return (addr);
+ /*
+ * Nothing is yet loaded. We shouldn't get to a module with a load
+ * address of zero still, and the kernel loads at its own multiboot
+ * address, so we don't need to make any adjustments here.
+ */
if (addr == 0)
- return (addr); /* nothing to do */
+ return (0);
if (type == LOAD_ELF)
return (0); /* not supported */
@@ -149,12 +160,19 @@ i386_loadaddr(uint_t type, void *data, vm_offset_t addr)
/* Start from the end of the kernel. */
mfp = fp;
do {
+
+
if (mfp == NULL) {
off = roundup2(addr + 1, MULTIBOOT_MOD_ALIGN);
} else {
off = roundup2(mfp->f_addr + mfp->f_size + 1,
MULTIBOOT_MOD_ALIGN);
}
+
+ /* RICHMOND-16 work around. */
+ if (off < SAFE_LOAD_BASE)
+ off = SAFE_LOAD_BASE;
+
/* Avoid possible framebuffer memory */
if (plat_stdout_is_framebuffer()) {
vm_offset_t fb_addr;
diff --git a/usr/src/boot/i386/pmbr/pmbr.s b/usr/src/boot/i386/pmbr/pmbr.s
index 46088cc78c..e5f059e88b 100644
--- a/usr/src/boot/i386/pmbr/pmbr.s
+++ b/usr/src/boot/i386/pmbr/pmbr.s
@@ -31,10 +31,12 @@
#
# Partly from: src/sys/boot/i386/mbr/mbr.s 1.7
+# Copyright (c) 2019, Joyent, Inc.
+
# A 512 byte PMBR boot manager to read a boot program and run it.
# The embedded MBR is set up for PMBR and default bootblock sector
# is hardcoded to 256 and size 1. The actual values are supposed to be
-# updated by installboot.
+# updated by installboot (and smartos-live's format_image).
.set LOAD,0x7c00 # Load address
.set EXEC,0x600 # Execution address
diff --git a/usr/src/cmd/Adm/group b/usr/src/cmd/Adm/group
index 651c919016..30503bcdfa 100644
--- a/usr/src/cmd/Adm/group
+++ b/usr/src/cmd/Adm/group
@@ -12,6 +12,7 @@ staff::10:
daemon::12:root
sysadmin::14:
games::20:
+smmsp::25:
gdm::50:
upnp::52:
xvm::60:
diff --git a/usr/src/cmd/Adm/sun/Makefile b/usr/src/cmd/Adm/sun/Makefile
index 9c280f54ec..636dc875e6 100644
--- a/usr/src/cmd/Adm/sun/Makefile
+++ b/usr/src/cmd/Adm/sun/Makefile
@@ -21,9 +21,11 @@
#
# Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2010 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2019 Joyent, Inc.
#
-ETCFILES= ioctl.syscon passwd shadow motd
+ETCGENFILES=motd issue
+ETCFILES= ioctl.syscon passwd shadow $(ETCGENFILES)
FTPDFILES= ftpusers
KVMFILES= README
SMBFILES= smbpasswd
@@ -45,35 +47,28 @@ $(ROOTVARSMB)/smbpasswd := FILEMODE = 0400
.KEEP_STATE:
-$(ROOTETCFTPUSERSLINK): $(ROOTETCFTPDFILES)
- $(RM) $@; $(SYMLINK) ftpd/ftpusers $@
-
all: $(ETCFILES) $(KVMFILES) $(SMBFILES) $(FTPDFILES)
install: all $(ROOTETCFILES) $(ROOTETCFTPDFILES) $(ROOTUSRKVMFILES) $(ROOTVARSMBFILES) $(ROOTETCFTPUSERSLINK)
clean:
- $(RM) $(ROOTETCFTPUSERSLINK)
+ $(RM) $(ROOTETCFTPUSERSLINK) $(ETCGENFILES) $(SMBFILES)
-lint:
+clobber: clean
-clobber:
+$(ROOTETCFTPUSERSLINK): $(ROOTETCFTPDFILES)
+ $(RM) $@; $(SYMLINK) ftpd/ftpusers $@
-motd: FRC
+motd: $(ROOT)/buildstamp
@-$(ECHO) "rebuilding motd"
- @$(RELEASE_BUILD)-$(ECHO) "The illumos Project\t$(VERSION)\t$(RELEASE_DATE)" > motd
- @$(NOT_RELEASE_BUILD)-$(ECHO) "The illumos Project\t$(VERSION)\t`date +'%h. %d, %Y'`" > motd
+ @$(ECHO) "SmartOS (build: $$(cat $(ROOT)/buildstamp))" >$@
+
+issue: issue.in $(ROOT)/buildstamp
+ sed "s+build: 00000000T000000Z+$$(cat $(ROOT)/buildstamp)+" issue.in >$@
@$(NOT_RELEASE_BUILD)-$(ECHO) $(DEV_CM) | sed -e "s/@(#)//" >> motd
@-$(CAT) release_info >> motd
smbpasswd:
$(TOUCH) smbpasswd
-clean:
-
-lint:
-
-clobber:
- $(RM) motd smbpasswd
-
FRC:
diff --git a/usr/src/cmd/Adm/sun/ftpusers b/usr/src/cmd/Adm/sun/ftpusers
index b821c641e7..30f67d7a3c 100644
--- a/usr/src/cmd/Adm/sun/ftpusers
+++ b/usr/src/cmd/Adm/sun/ftpusers
@@ -12,6 +12,7 @@ nuucp
dladm
netadm
netcfg
+smmsp
listen
gdm
xvm
diff --git a/usr/src/cmd/Adm/sun/issue.in b/usr/src/cmd/Adm/sun/issue.in
new file mode 100644
index 0000000000..06404ce335
--- /dev/null
+++ b/usr/src/cmd/Adm/sun/issue.in
@@ -0,0 +1,14 @@
+
+
+ *--+--*--*
+ |\ |\ |\ |\
+ | \| \| \| \ ##### #### # ##### ### # # TM
+ +--*--+--*--* # # # # # # # ## #
+ |\ |\ |\ |\ | # #### # # # # # # #
+ | \| \| \| \| # # # # # # # # ##
+ *--+--+--+--+ # # # # # ### # #
+ \ |\ |\ |\ |
+ \| \| \| \| SmartOS
+ *--+--*--* build: 00000000T000000Z
+
+
diff --git a/usr/src/cmd/Adm/sun/passwd b/usr/src/cmd/Adm/sun/passwd
index a8025c2c18..e9089255fa 100644
--- a/usr/src/cmd/Adm/sun/passwd
+++ b/usr/src/cmd/Adm/sun/passwd
@@ -9,6 +9,7 @@ nuucp:x:9:9:uucp Admin:/var/spool/uucppublic:/usr/lib/uucp/uucico
dladm:x:15:65:Datalink Admin:/:
netadm:x:16:65:Network Admin:/:
netcfg:x:17:65:Network Configuration Admin:/:
+smmsp:x:25:25:SendMail Message Submission Program:/:
listen:x:37:4:Network Admin:/usr/net/nls:
gdm:x:50:50:GDM Reserved UID:/var/lib/gdm:
zfssnap:x:51:12:ZFS Automatic Snapshots Reserved UID:/:/usr/bin/pfsh
diff --git a/usr/src/cmd/Adm/sun/shadow b/usr/src/cmd/Adm/sun/shadow
index e3b5898878..73ab98be85 100644
--- a/usr/src/cmd/Adm/sun/shadow
+++ b/usr/src/cmd/Adm/sun/shadow
@@ -9,6 +9,7 @@ nuucp:NP:6445::::::
dladm:*LK*:::::::
netadm:*LK*:::::::
netcfg:*LK*:::::::
+smmsp:NP:6445::::::
listen:*LK*:::::::
gdm:*LK*:::::::
zfssnap:NP:::::::
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 309f70d598..806b8043e3 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -35,8 +35,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.
#
@@ -57,7 +57,6 @@ FIRST_SUBDIRS= \
COMMON_SUBDIRS= \
allocate \
availdevs \
- lp \
perl \
Adm \
abi \
@@ -99,6 +98,7 @@ COMMON_SUBDIRS= \
cmd-crypto \
cmd-inet \
col \
+ column \
compress \
connstat \
consadm \
@@ -198,7 +198,6 @@ COMMON_SUBDIRS= \
growfs \
grpck \
gss \
- hal \
halt \
head \
hostid \
@@ -233,7 +232,6 @@ COMMON_SUBDIRS= \
kvmstat \
last \
lastcomm \
- latencytop \
ldap \
ldapcachemgr \
lgrpinfo \
@@ -256,6 +254,7 @@ COMMON_SUBDIRS= \
ls \
luxadm \
mach \
+ machid \
mail \
mailwrapper \
mailx \
@@ -290,6 +289,7 @@ COMMON_SUBDIRS= \
news \
newtask \
nice \
+ nicstat \
nl \
nlsadmin \
nohup \
@@ -317,7 +317,6 @@ COMMON_SUBDIRS= \
picl \
plimit \
policykit \
- pools \
power \
powertop \
ppgsz \
@@ -325,7 +324,6 @@ COMMON_SUBDIRS= \
plockstat \
pr \
prctl \
- print \
printf \
priocntl \
profiles \
@@ -392,7 +390,6 @@ COMMON_SUBDIRS= \
srchtxt \
srptadm \
srptsvc \
- ssh \
stat \
stmfadm \
stmfproxy \
@@ -446,6 +443,8 @@ COMMON_SUBDIRS= \
varpd \
vgrind \
vi \
+ vndadm \
+ vndstat \
volcheck \
volrmmount \
vrrpadm \
@@ -517,11 +516,14 @@ sparc_SUBDIRS= \
vntsd
#
-# Commands that are messaged. Note that 'lp' comes first
-# (see previous comment about 'lp'.)
+# Commands that are messaged. Note that 'lp' comes first (see previous comment
+# about 'lp'.)
+#
+# We purposefully leave out auditrecord's messages from illumos-joyent: it
+# attempts to use Perl modules constructed as part of the build alongside the
+# native Perl.
#
MSGSUBDIRS= \
- lp \
abi \
acctadm \
allocate \
@@ -531,7 +533,6 @@ MSGSUBDIRS= \
audit \
auditconfig \
auditd \
- auditrecord \
auditset \
auths \
autopush \
@@ -658,11 +659,9 @@ MSGSUBDIRS= \
pg \
pgrep \
picl \
- pools \
power \
pr \
praudit \
- print \
profiles \
projadd \
projects \
diff --git a/usr/src/cmd/Makefile.check b/usr/src/cmd/Makefile.check
index 9c8f0b2b1b..ba624dfc73 100644
--- a/usr/src/cmd/Makefile.check
+++ b/usr/src/cmd/Makefile.check
@@ -118,16 +118,12 @@ MANIFEST_SUBDIRS= \
krb5/krb5kdc \
krb5/kwarn \
krb5/slave \
- lp/cmd/lpsched \
picl/picld \
pools/poold \
- print/bsd-sysv-commands \
- print/ppdmgr \
rcap/rcapd \
rpcsvc/rpc.bootparamd \
sendmail/lib \
smbsrv/smbd \
- ssh/etc \
svc/milestone \
tsol/labeld \
tsol/tnctl \
diff --git a/usr/src/cmd/adbgen/common/adbsub.c b/usr/src/cmd/adbgen/common/adbsub.c
index d9b7a37273..773a215ae8 100644
--- a/usr/src/cmd/adbgen/common/adbsub.c
+++ b/usr/src/cmd/adbgen/common/adbsub.c
@@ -19,7 +19,6 @@
*
* CDDL HEADER END
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Copyright (c) 1983-1998 by Sun Microsystems, Inc.
@@ -27,6 +26,10 @@
*/
/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
* Subroutines to be called by adbgen2.c, the C program generated
* by adbgen1.c.
*/
@@ -155,9 +158,8 @@ do_fmt(char *acp)
case 'S':
case 'i':
if (warnings) {
- fprintf(stderr,
- "Unknown format size \"%s\", assuming zero\n",
- acp);
+ fprintf(stderr, "Unknown format size \"%s\", "
+ "assuming zero\n", acp);
warns++;
}
width = 0;
@@ -167,7 +169,7 @@ do_fmt(char *acp)
exit(1);
}
for (i = 0; i < rcount; i++) {
- putchar(*cp);
+ (void) putchar(*cp);
}
cp++;
sum += width * rcount;
@@ -186,8 +188,8 @@ format(char *name, size_t size, char *fmt)
fs = do_fmt(fmt);
if (fs != size && warnings) {
fprintf(stderr,
- "warning: \"%s\" size is %ld, \"%s\" width is %d\n",
- name, size, fmt, fs);
+ "warning: \"%s\" size is %ld, \"%s\" width is %d\n",
+ name, size, fmt, fs);
warns++;
}
last_off += fs;
diff --git a/usr/src/cmd/ast/libast/amd64/FEATURE/aso b/usr/src/cmd/ast/libast/amd64/FEATURE/aso
index 8baa447c72..af56c08611 100644
--- a/usr/src/cmd/ast/libast/amd64/FEATURE/aso
+++ b/usr/src/cmd/ast/libast/amd64/FEATURE/aso
@@ -2,6 +2,33 @@
#ifndef _def_aso_ast
#define _def_aso_ast 1
#define _sys_types 1 /* #include <sys/types.h> ok */
+
+/*
+ * This is horrible, but since SmartOS still compiles with gcc4 in its
+ * Jenkins pipeline (and for building modern smartos on older systems),
+ * we need to use sparc-style workaround for the illumos gcc4 compiler.
+ */
+#if defined (__GNUC__) && (__GNUC__ == 4)
+/* <atomic.h> atomic_cas_64 */
+#include <atomic.h>
+#define _aso_cas8(p,o,n) atomic_cas_8(p,o,n)
+#define _aso_inc8(p) (atomic_add_8_nv(p,1)-1)
+#define _aso_dec8(p) (atomic_add_8_nv(p,-1)+1)
+#define _aso_cas16(p,o,n) atomic_cas_16(p,o,n)
+#define _aso_inc16(p) (atomic_add_16_nv(p,1)-1)
+#define _aso_dec16(p) (atomic_add_16_nv(p,-1)+1)
+#define _aso_cas32(p,o,n) atomic_cas_32(p,o,n)
+#define _aso_inc32(p) (atomic_add_32_nv(p,1)-1)
+#define _aso_dec32(p) (atomic_add_32_nv(p,-1)+1)
+#define _aso_cas64(p,o,n) atomic_cas_64(p,o,n)
+#define _aso_inc64(p) (atomic_add_64_nv(p,1)-1)
+#define _aso_dec64(p) (atomic_add_64_nv(p,-1)+1)
+#if _ast_sizeof_pointer == 8
+#define _aso_casptr(p,o,n) ((void*)atomic_cas_64((uint64_t*)p,(uint64_t)o,(uint64_t)n))
+#else
+#define _aso_casptr(p,o,n) ((void*)atomic_cas_32((uint32_t*)p,(uint32_t)o,(uint32_t)n))
+#endif
+#else /* illumos-gcc4 */
/* gcc 4.1+ 64 bit memory atomic operations model */
#define _aso_cas8(p,o,n) __sync_val_compare_and_swap(p,o,n)
#define _aso_inc8(p) __sync_fetch_and_add(p,1)
@@ -20,5 +47,6 @@
#else
#define _aso_casptr(p,o,n) ((void*)__sync_val_compare_and_swap(p,(uint32_t)o,(uint32_t)n))
#endif
+#endif /* illumos-gcc4 */
#endif
diff --git a/usr/src/cmd/auditd/auditd.xml b/usr/src/cmd/auditd/auditd.xml
index 2c3208ad87..99dba6091c 100644
--- a/usr/src/cmd/auditd/auditd.xml
+++ b/usr/src/cmd/auditd/auditd.xml
@@ -142,7 +142,7 @@
-->
<property_group name='preselection' type='application'>
<propval name='flags' type='astring'
- value='lo' />
+ value='lo,ex' />
<propval name='naflags' type='astring'
value='lo' />
<propval name='read_authorization' type='astring'
@@ -188,7 +188,7 @@
<propval name='arge' type='boolean'
value='false' />
<propval name='argv' type='boolean'
- value='false' />
+ value='true' />
<propval name='cnt' type='boolean'
value='true' />
<propval name='group' type='boolean'
@@ -196,7 +196,7 @@
<propval name='path' type='boolean'
value='false' />
<propval name='perzone' type='boolean'
- value='false' />
+ value='true' />
<propval name='public' type='boolean'
value='false' />
<propval name='seq' type='boolean'
diff --git a/usr/src/cmd/auditrecord/Makefile b/usr/src/cmd/auditrecord/Makefile
index 4f4670dfc9..fdf5213b3f 100644
--- a/usr/src/cmd/auditrecord/Makefile
+++ b/usr/src/cmd/auditrecord/Makefile
@@ -22,13 +22,15 @@
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright (c) 2018, Joyent, Inc.
+#
include $(SRC)/cmd/Makefile.cmd
ATTR = audit_record_attr
ROOTAUDITDIR = $(ROOT)/usr/lib/audit
-SECURITYFILES = $(ATTR:%=$(ROOTAUDITDIR)/%)
+SECURITYFILES = $(ATTR:%=$(ROOTAUDITDIR)/%)
$(SECURITYFILES) := FILEMODE = $(LIBFILEMODE)
PROG = auditrecord
@@ -41,7 +43,7 @@ ADTXMLFILE = $(LIBBSMDIR)/common/adt.xml
.KEEP_STATE:
-all: $(PROG) $(ATTR)
+all: $(PROG) $(ATTR)
install: all $(ROOTUSRSBINPROG) install_data
@@ -59,7 +61,7 @@ clean:
$(RM) $(ATTR) $(STRIPTEXT) $(AGETTEXT)
$(ATTR): $(STRIPTEXT) $(ATTRPROC) $(ADTXMLFILE) $(ATTR).txt
- ./$(STRIPTEXT) < $(ATTR).txt > $(ATTR)
+ $(PERL) ./$(STRIPTEXT) < $(ATTR).txt > $(ATTR)
$(PERL) -I $(LIBBSMDIR) ./$(ATTRPROC) $(ADTXMLFILE) >> $(ATTR)
$(ROOTUSRSBINPROG): $(PROG)
diff --git a/usr/src/cmd/bhyve/Makefile b/usr/src/cmd/bhyve/Makefile
index 509c389f11..6de5407015 100644
--- a/usr/src/cmd/bhyve/Makefile
+++ b/usr/src/cmd/bhyve/Makefile
@@ -105,12 +105,16 @@ SRCS = acpi.c \
OBJS = $(SRCS:.c=.o)
+ZHYVE_DIR = $(ROOT)/usr/lib/brand/bhyve
+ZHYVE_PROG = zhyve
+ZHYVE = $(ZHYVE_DIR)/$(ZHYVE_PROG)
+
MEVENT_TEST_PROG = mevent_test
MEVENT_TEST_SRCS = mevent.c mevent_test.c
MEVENT_TEST_OBJS = $(MEVENT_TEST_SRCS:.c=.o)
CLEANFILES = $(OBJS) $(MEVENT_TEST_OBJS)
-CLOBBERFILES = $(PROG) $(MEVENT_TEST_PROG)
+CLOBBERFILES = $(PROG) $(MEVENT_TEST_PROG) $(ZHYVE_PROG)
CFLAGS += $(CCVERBOSE)
CFLAGS += -_gcc=-Wimplicit-function-declaration -_gcc=-Wno-parentheses
@@ -124,6 +128,7 @@ CPPFLAGS = -I$(COMPAT)/bhyve -I$(CONTRIB)/bhyve \
-I$(SRC)/uts/intel/io/vmm \
-I$(SRC)/uts/common \
-I$(SRC)/uts/intel \
+ -I$(SRC)/lib/libdladm/common \
-DWITHOUT_CAPSICUM
pci_nvme.o := CERRWARN += -_gcc=-Wno-pointer-sign
@@ -149,7 +154,6 @@ CSTD= $(CSTD_GNU99)
$(PROG) := LDLIBS += \
-l9p \
-lcmdutils \
- -lcrypto \
-ldladm \
-ldlpi \
-lidspace \
@@ -157,17 +161,19 @@ $(PROG) := LDLIBS += \
-lnsl \
-lnvpair \
-lsocket \
+ -lsunw_crypto \
-lumem \
-luuid \
-lvmmapi \
-lz
-NATIVE_LIBS += libz.so libcrypto.so
+NATIVE_LIBS += libz.so libsunw_crypto.so
+$(ZHYVE_PROG) := LDLIBS += -lnvpair
$(MEVENT_TEST_PROG) := LDLIBS += -lsocket
$(PROG) := LDFLAGS += $(ZASLR)
.KEEP_STATE:
-all: $(PROG) $(MEVENT_TEST_PROG) $(SUBDIRS)
+all: $(PROG) $(MEVENT_TEST_PROG) $(ZHYVE_PROG) $(SUBDIRS)
$(PROG): $(OBJS)
$(LINK.c) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
@@ -176,7 +182,7 @@ $(PROG): $(OBJS)
$(MEVENT_TEST_PROG): $(MEVENT_TEST_OBJS)
$(LINK.c) -o $@ $(MEVENT_TEST_OBJS) $(LDFLAGS) $(LDLIBS)
-install: all $(ROOTUSRSBINPROG) $(SUBDIRS)
+install: all $(ZHYVE) $(ROOTUSRSBINPROG) $(SUBDIRS)
clean: $(SUBDIRS)
$(RM) $(CLEANFILES)
@@ -188,3 +194,12 @@ $(SUBDIRS): FRC
@cd $@; pwd; $(MAKE) $(TARGET)
FRC:
+
+include ../Makefile.targ
+
+$(ZHYVE_DIR)/%: %
+ $(INS.file)
+
+%.o: $(SRC)/uts/intel/io/vmm/%.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/cmd/bhyve/acpi.c b/usr/src/cmd/bhyve/acpi.c
index fd0a6f732e..695354b2ac 100644
--- a/usr/src/cmd/bhyve/acpi.c
+++ b/usr/src/cmd/bhyve/acpi.c
@@ -272,15 +272,7 @@ basl_fwrite_madt(FILE *fp)
EFPRINTF(fp, "[0001]\t\tLocal Apic ID : %02x\n", i);
EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000001\n");
EFPRINTF(fp, "\t\t\tProcessor Enabled : 1\n");
-#ifdef __FreeBSD__
EFPRINTF(fp, "\t\t\tRuntime Online Capable : 0\n");
-#else
- /*
- * Until iasl is updated to support the "Runtime Online
- * Capable" entry, it must be omitted. This should be
- * re-checked when illumos receives an acpica update.
- */
-#endif /* __FreeBSD__ */
EFPRINTF(fp, "\n");
}
diff --git a/usr/src/cmd/bhyve/bhyverun.c b/usr/src/cmd/bhyve/bhyverun.c
index 90260a4ac9..dfe5e84766 100644
--- a/usr/src/cmd/bhyve/bhyverun.c
+++ b/usr/src/cmd/bhyve/bhyverun.c
@@ -86,6 +86,10 @@ __FBSDID("$FreeBSD$");
#endif
#include <vmmapi.h>
+#ifndef __FreeBSD__
+#include <sys/stat.h>
+#endif
+
#include "bhyverun.h"
#include "acpi.h"
#include "atkbdc.h"
@@ -1365,6 +1369,28 @@ do_open(const char *vmname)
return (ctx);
}
+#ifndef __FreeBSD__
+
+#define FILE_PROVISIONING "/var/svc/provisioning"
+#define FILE_PROVISION_SUCCESS "/var/svc/provision_success"
+
+static void
+mark_provisioned(void)
+{
+ struct stat stbuf;
+
+ if (lstat(FILE_PROVISIONING, &stbuf) != 0)
+ return;
+
+ if (rename(FILE_PROVISIONING, FILE_PROVISION_SUCCESS) != 0) {
+ (void) fprintf(stderr, "Cannot rename %s to %s: %s\n",
+ FILE_PROVISIONING, FILE_PROVISION_SUCCESS,
+ strerror(errno));
+ }
+}
+
+#endif
+
static bool
parse_config_option(const char *option)
{
@@ -1770,9 +1796,7 @@ main(int argc, char *argv[])
errx(EX_OSERR, "cap_enter() failed");
#endif
-#ifndef __FreeBSD__
- illumos_priv_lock();
-#endif
+/* XXX SmartOS: Upstream drops privs here, but we can't yet. See below... */
#ifdef __FreeBSD__
/*
@@ -1790,7 +1814,17 @@ main(int argc, char *argv[])
for (uint_t i = 1; i < guest_ncpus; i++) {
spinup_halted_ap(ctx, i);
}
+ mark_provisioned();
+ /*
+ * XXX SmartOS: The mark_provisioned() call above required file-access
+ * privileges that are dropped by the generic call. We must widen the
+ * full-privilege window a bit. A better solution might be to have
+ * a way to keep file-access a bit longer, and only have THAT privilege
+ * to drop here.
+ */
+ illumos_priv_lock();
#endif
+
/*
* Head off to the main event dispatch loop
*/
diff --git a/usr/src/cmd/bhyve/rfb.c b/usr/src/cmd/bhyve/rfb.c
index 139d75183f..576b66c0e8 100644
--- a/usr/src/cmd/bhyve/rfb.c
+++ b/usr/src/cmd/bhyve/rfb.c
@@ -54,6 +54,7 @@
#include <signal.h>
#include <stdatomic.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/usr/src/cmd/bhyve/test/tests/mevent/lists_delete.c b/usr/src/cmd/bhyve/test/tests/mevent/lists_delete.c
index c5ed91a790..d09ac133a3 100644
--- a/usr/src/cmd/bhyve/test/tests/mevent/lists_delete.c
+++ b/usr/src/cmd/bhyve/test/tests/mevent/lists_delete.c
@@ -163,7 +163,7 @@ main(int argc, const char *argv[])
*/
flush_and_wait(flush_pipe[1]);
count2 = get_count();
- if (count1 - 1 != count2) {
+ if (count1 - 1 != count2 ) {
FAIL(("mevent_delete() did not decrease count by 1: "
"was %d, now %d", count1, count2));
}
diff --git a/usr/src/cmd/bhyve/test/tests/mevent/testlib.c b/usr/src/cmd/bhyve/test/tests/mevent/testlib.c
index 67261b9a31..af756d1509 100644
--- a/usr/src/cmd/bhyve/test/tests/mevent/testlib.c
+++ b/usr/src/cmd/bhyve/test/tests/mevent/testlib.c
@@ -25,8 +25,7 @@ const char *testlib_prog;
boolean_t testlib_verbose;
static void
-timed_out(int signo)
-{
+timed_out(int signo) {
ASSERT_INT_EQ(("timeout signal"), signo, SIGALRM);
FAIL(("Timed out"));
diff --git a/usr/src/cmd/bhyve/test/tests/mevent/testlib.h b/usr/src/cmd/bhyve/test/tests/mevent/testlib.h
index 1639f29f87..aeaf3a39db 100644
--- a/usr/src/cmd/bhyve/test/tests/mevent/testlib.h
+++ b/usr/src/cmd/bhyve/test/tests/mevent/testlib.h
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _TESTLIB_H_
diff --git a/usr/src/cmd/bhyve/zhyve.c b/usr/src/cmd/bhyve/zhyve.c
new file mode 100644
index 0000000000..d3e764b14d
--- /dev/null
+++ b/usr/src/cmd/bhyve/zhyve.c
@@ -0,0 +1,167 @@
+/*
+ * 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) 2018, Joyent, Inc.
+ */
+
+/*
+ * This small 'zhyve' stub is init for the zone: we therefore need to pick up
+ * our command-line arguments placed in ZHYVE_CMD_FILE by the boot stub, do a
+ * little administration, and exec the real bhyve binary.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libnvpair.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/corectl.h>
+
+#define ZHYVE_CMD_FILE "/var/run/bhyve/zhyve.cmd"
+
+/*
+ * Do a read of the specified size or return an error. Returns 0 on success
+ * and -1 on error. Sets errno to EINVAL if EOF is encountered. For other
+ * errors, see read(2).
+ */
+static int
+full_read(int fd, char *buf, size_t len)
+{
+ ssize_t nread = 0;
+ size_t totread = 0;
+
+ while (totread < len) {
+ nread = read(fd, buf + totread, len - totread);
+ if (nread == 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if (nread < 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ continue;
+ }
+ return (-1);
+ }
+ totread += nread;
+ }
+ assert(totread == len);
+
+ return (0);
+}
+
+/*
+ * Reads the command line options from the packed nvlist in the file referenced
+ * by path. On success, 0 is returned and the members of *argv reference memory
+ * allocated from an nvlist. On failure, -1 is returned.
+ */
+
+static int
+parse_options_file(const char *path, uint_t *argcp, char ***argvp)
+{
+ int fd = -1;
+ struct stat stbuf;
+ char *buf = NULL;
+ nvlist_t *nvl = NULL;
+ int ret;
+
+ if ((fd = open(path, O_RDONLY)) < 0 ||
+ fstat(fd, &stbuf) != 0 ||
+ (buf = malloc(stbuf.st_size)) == NULL ||
+ full_read(fd, buf, stbuf.st_size) != 0 ||
+ nvlist_unpack(buf, stbuf.st_size, &nvl, 0) != 0 ||
+ nvlist_lookup_string_array(nvl, "bhyve_args", argvp, argcp) != 0) {
+ nvlist_free(nvl);
+ ret = -1;
+ } else {
+ ret = 0;
+ }
+
+ free(buf);
+ (void) close(fd);
+
+ (void) printf("Configuration from %s:\n", path);
+ nvlist_print(stdout, nvl);
+
+ return (ret);
+}
+
+/*
+ * Setup to suppress core dumps within the zone.
+ */
+static void
+config_core_dumps()
+{
+ (void) core_set_options(0x0);
+}
+
+int
+main(int argc, char **argv)
+{
+ char **tmpargs;
+ uint_t zargc;
+ char **zargv;
+ int fd;
+
+ config_core_dumps();
+
+ fd = open("/dev/null", O_WRONLY);
+ assert(fd >= 0);
+ if (fd != STDIN_FILENO) {
+ (void) dup2(fd, STDIN_FILENO);
+ (void) close(fd);
+ }
+
+ fd = open("/dev/zfd/1", O_WRONLY);
+ assert(fd >= 0);
+ if (fd != STDOUT_FILENO) {
+ (void) dup2(fd, STDOUT_FILENO);
+ (void) close(fd);
+ }
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ fd = open("/dev/zfd/2", O_WRONLY);
+ assert(fd >= 0);
+ if (fd != STDERR_FILENO) {
+ (void) dup2(fd, STDERR_FILENO);
+ (void) close(fd);
+ }
+ setvbuf(stderr, NULL, _IONBF, 0);
+
+ if (parse_options_file(ZHYVE_CMD_FILE, &zargc, &zargv) != 0) {
+ (void) fprintf(stderr, "%s: failed to parse %s: %s\n",
+ argv[0], ZHYVE_CMD_FILE, strerror(errno));
+ return (EXIT_FAILURE);
+ }
+
+ /*
+ * Annoyingly, we need a NULL at the end.
+ */
+
+ if ((tmpargs = malloc(sizeof (*zargv) * (zargc + 1))) == NULL) {
+ perror("malloc failed");
+ return (EXIT_FAILURE);
+ }
+
+ memcpy(tmpargs, zargv, sizeof (*zargv) * zargc);
+ tmpargs[zargc] = NULL;
+
+ (void) execv("/usr/sbin/bhyve", tmpargs);
+
+ perror("execv failed");
+ return (EXIT_FAILURE);
+}
diff --git a/usr/src/cmd/cmd-crypto/etc/pkcs11.conf b/usr/src/cmd/cmd-crypto/etc/pkcs11.conf
index dafa22357b..236c52aebb 100644
--- a/usr/src/cmd/cmd-crypto/etc/pkcs11.conf
+++ b/usr/src/cmd/cmd-crypto/etc/pkcs11.conf
@@ -20,6 +20,7 @@
#
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2011 Joyent, Inc. All rights reserved.
#
# /etc/crypto/pkcs11.conf
#
@@ -39,5 +40,4 @@
metaslot:metaslot_status=enabled;metaslot_auto_key_migrate=enabled;metaslot_token=Sun Software PKCS#11 softtoken;metaslot_slot=Sun Crypto Softtoken
/usr/lib/security/$ISA/pkcs11_kernel.so
/usr/lib/security/$ISA/pkcs11_softtoken.so
-/usr/lib/security/$ISA/pkcs11_tpm.so
# End SUNWcsr
diff --git a/usr/src/cmd/cmd-inet/etc/services b/usr/src/cmd/cmd-inet/etc/services
index 4562baff66..5673b61626 100644
--- a/usr/src/cmd/cmd-inet/etc/services
+++ b/usr/src/cmd/cmd-inet/etc/services
@@ -1,7 +1,6 @@
-#
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-# Copyright 2015 Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
#
# CDDL HEADER START
#
@@ -23,9 +22,18 @@
# CDDL HEADER END
#
# Network services, Internet style
-# Look at http://www.iana.org/assignments/port-numbers for more
#
-tcpmux 1/tcp
+# Note that it is presently the policy of IANA to assign a single well-known
+# port number for both TCP and UDP; hence, officially ports have two entries
+# even if the protocol doesn't support UDP operations.
+#
+# Updated from http://www.iana.org/assignments/port-numbers and other
+# sources like http://www.freebsd.org/cgi/cvsweb.cgi/src/etc/services .
+# New ports will be added on request if they have been officially assigned
+# by IANA and used in the real-world or are needed by a debian package.
+# If you need a huge list of used numbers please install the nmap package.
+
+tcpmux 1/tcp # TCP port service multiplexer
echo 7/tcp
echo 7/udp
discard 9/tcp sink null
@@ -34,208 +42,615 @@ systat 11/tcp users
daytime 13/tcp
daytime 13/udp
netstat 15/tcp
-qotd 17/tcp # Quote of the Day
+qotd 17/tcp quote
+msp 18/tcp # message send protocol
+msp 18/udp
chargen 19/tcp ttytst source
chargen 19/udp ttytst source
ftp-data 20/tcp
ftp 21/tcp
ftp 21/sctp
-ssh 22/tcp # Secure Shell
+fsp 21/udp fspd
+ssh 22/tcp # SSH Remote Login Protocol
ssh 22/sctp
+ssh 22/udp
telnet 23/tcp
smtp 25/tcp mail
time 37/tcp timserver
time 37/udp timserver
rlp 39/tcp # Resource Location Protocol
-rlp 39/udp # Resource Location Protocol
-name 42/udp nameserver
-whois 43/tcp nicname # usually to sri-nic
-tacacs 49/tcp
+rlp 39/udp resource # resource location
+nameserver 42/tcp name # IEN 116
+nameserver 42/udp name
+whois 43/tcp nicname
+tacacs 49/tcp # Login Host Protocol (TACACS)
tacacs 49/udp
+re-mail-ck 50/tcp # Remote Mail Checking Protocol
+re-mail-ck 50/udp
+domain 53/tcp # name-domain server
domain 53/udp
-domain 53/tcp
-tacacs-ds 65/tcp
+mtp 57/tcp # deprecated
+tacacs-ds 65/tcp # TACACS-Database Service
tacacs-ds 65/udp
-bootps 67/udp # BOOTP/DHCP server
-bootpc 68/udp # BOOTP/DHCP client
-http 80/tcp www www-http
-http 80/udp www www-http
-http 80/sctp
-kerberos 88/udp kdc # Kerberos V5 KDC
-kerberos 88/tcp kdc # Kerberos V5 KDC
-hostnames 101/tcp hostname # usually to sri-nic
-pop2 109/tcp pop-2 # Post Office Protocol - V2
-pop3 110/tcp # Post Office Protocol - Version 3
-sunrpc 111/udp rpcbind
-sunrpc 111/tcp rpcbind
+bootps 67/tcp # BOOTP server
+bootps 67/udp
+bootpc 68/tcp # BOOTP client
+bootpc 68/udp
+tftp 69/udp
+gopher 70/tcp # Internet Gopher
+gopher 70/udp
+rje 77/tcp netrjs
+finger 79/tcp
+www 80/tcp http # WorldWideWeb HTTP
+www 80/sctp http
+www 80/udp # HyperText Transfer Protocol
+link 87/tcp ttylink
+kerberos 88/tcp kerberos5 krb5 kerberos-sec # Kerberos v5
+kerberos 88/udp kerberos5 krb5 kerberos-sec # Kerberos v5
+supdup 95/tcp
+hostnames 101/tcp hostname # usually from sri-nic
+iso-tsap 102/tcp tsap # part of ISODE
+x400 103/tcp # ISO Mail
+acr-nema 104/tcp dicom # Digital Imag. & Comm. 300
+acr-nema 104/udp dicom # Digital Imag. & Comm. 300
+csnet-ns 105/tcp cso-ns # also used by CSO name server
+csnet-ns 105/udp cso-ns
+rtelnet 107/tcp # Remote Telnet
+rtelnet 107/udp
+pop2 109/tcp postoffice pop-2 # POP version 2
+pop2 109/udp pop-2
+pop3 110/tcp pop-3 # POP version 3
+pop3 110/udp pop-3
+sunrpc 111/tcp portmapper # RPC 4.0 portmapper
+sunrpc 111/udp portmapper
+auth 113/tcp authentication tap ident
sftp 115/tcp
-imap 143/tcp imap2 # Internet Mail Access Protocol v2
+uucp-path 117/tcp
+nntp 119/tcp readnews untp # USENET News Transfer Protocol
+ntp 123/tcp
+ntp 123/udp # Network Time Protocol
+pwdgen 129/tcp # PWDGEN service
+pwdgen 129/udp # PWDGEN service
+loc-srv 135/tcp epmap # Location Service
+loc-srv 135/udp epmap
+netbios-ns 137/tcp # NETBIOS Name Service
+netbios-ns 137/udp
+netbios-dgm 138/tcp # NETBIOS Datagram Service
+netbios-dgm 138/udp
+netbios-ssn 139/tcp # NETBIOS session service
+netbios-ssn 139/udp
+imap2 143/tcp imap # Interim Mail Access P 2 and 4
+imap2 143/udp imap
+snmp 161/tcp # Simple Net Mgmt Protocol
+snmp 161/udp # Simple Net Mgmt Protocol
+snmp-trap 162/tcp snmptrap # Traps for SNMP
+snmp-trap 162/udp snmptrap # Traps for SNMP
+cmip-man 163/tcp # ISO mgmt over IP (CMOT)
+cmip-man 163/udp
+cmip-agent 164/tcp
+cmip-agent 164/udp
+mailq 174/tcp # Mailer transport queue for Zmailer
+mailq 174/udp # Mailer transport queue for Zmailer
+xdmcp 177/tcp # X Display Mgr. Control Proto
+xdmcp 177/udp
+nextstep 178/tcp NeXTStep NextStep # NeXTStep window
+nextstep 178/udp NeXTStep NextStep # server
bgp 179/tcp # Border Gateway Protocol
-bgp 179/udp
bgp 179/sctp
-irc 194/tcp
+bgp 179/udp
+prospero 191/tcp # Cliff Neuman's Prospero
+prospero 191/udp
+irc 194/tcp # Internet Relay Chat
irc 194/udp
-smux 199/tcp
+smux 199/tcp # SNMP Unix Multiplexer
smux 199/udp
-imap3 220/tcp
-imap3 220/udp
-clearcase 371/tcp
-clearcase 371/udp
-ldap 389/tcp # Lightweight Directory Access Protocol
-ldap 389/udp # Lightweight Directory Access Protocol
-https 443/tcp
+at-rtmp 201/tcp # AppleTalk routing
+at-rtmp 201/udp
+at-nbp 202/tcp # AppleTalk name binding
+at-nbp 202/udp
+at-echo 204/tcp # AppleTalk echo
+at-echo 204/udp
+at-zis 206/tcp # AppleTalk zone information
+at-zis 206/udp
+qmtp 209/tcp # Quick Mail Transfer Protocol
+qmtp 209/udp # Quick Mail Transfer Protocol
+z3950 210/tcp wais # NISO Z39.50 database
+z3950 210/udp wais
+ipx 213/tcp # IPX
+ipx 213/udp
+imap3 220/tcp # Interactive Mail Access
+imap3 220/udp # Protocol v3
+pawserv 345/tcp # Perf Analysis Workbench
+pawserv 345/udp
+zserv 346/tcp # Zebra server
+zserv 346/udp
+fatserv 347/tcp # Fatmen Server
+fatserv 347/udp
+rpc2portmap 369/tcp
+rpc2portmap 369/udp # Coda portmapper
+codaauth2 370/tcp
+codaauth2 370/udp # Coda authentication server
+clearcase 371/tcp Clearcase
+clearcase 371/udp Clearcase
+ulistserv 372/tcp # UNIX Listserv
+ulistserv 372/udp
+ldap 389/tcp # Lightweight Directory Access Protocol
+ldap 389/udp
+imsp 406/tcp # Interactive Mail Support Protocol
+imsp 406/udp
+slp 427/tcp slp # Service Location Protocol, V2
+slp 427/udp slp # Service Location Protocol, V2
+mobile-ip 434/udp mobile-ip # Mobile-IP
+cvc_hostd 442/tcp # Network Console
+https 443/tcp # http protocol over TLS/SSL
https 443/udp
https 443/sctp
+snpp 444/tcp # Simple Network Paging Protocol
+snpp 444/udp
+microsoft-ds 445/tcp # Microsoft Naked CIFS
+microsoft-ds 445/udp
kpasswd 464/tcp
kpasswd 464/udp
+saft 487/tcp # Simple Asynchronous File Transfer
+saft 487/udp
+isakmp 500/tcp # IPsec - Internet Security Association
+isakmp 500/udp # and Key Management Protocol
dhcpv6-client 546/udp dhcpv6c # DHCPv6 Client (RFC 3315)
dhcpv6-client 546/tcp
dhcpv6-server 547/udp dhcpv6s # DHCPv6 Server (RFC 3315)
dhcpv6-server 547/tcp
-rtsp 554/tcp
-rtsp 554/udp
-nntps 563/tcp snntp
-nntps 563/udp snntp
-submission 587/tcp # Mail Message Submission
-submission 587/udp # see RFC 2476
-ipp 631/tcp
+rtsp 554/tcp # Real Time Stream Control Protocol
+rtsp 554/udp # Real Time Stream Control Protocol
+nqs 607/tcp # Network Queuing system
+nqs 607/udp
+npmp-local 610/tcp dqs313_qmaster # npmp-local / DQS
+npmp-local 610/udp dqs313_qmaster
+npmp-gui 611/tcp dqs313_execd # npmp-gui / DQS
+npmp-gui 611/udp dqs313_execd
+hmmp-ind 612/tcp dqs313_intercell # HMMP Indication / DQS
+hmmp-ind 612/udp dqs313_intercell
+qmqp 628/tcp
+qmqp 628/udp
+ipp 631/tcp # Internet Printing Protocol
ipp 631/udp
-ldaps 636/tcp # LDAP protocol over TLS/SSL (was sldap)
-ldaps 636/udp # LDAP protocol over TLS/SSL (was sldap)
-silc 706/tcp
-silc 706/udp
-iscsi 860/tcp
-iscsi 860/udp
-rsync 873/tcp
-rsync 873/udp
-ftps-data 989/tcp
-ftps-data 989/udp
-ftps 990/tcp
-ftps 990/udp
-imaps 993/tcp
-imaps 993/udp
-pop3s 995/tcp
-pop3s 995/udp
-socks 1080/tcp
-socks 1080/udp
-openvpn 1194/tcp
-openvpn 1194/udp
-icap 1344/tcp # Internet Content Adaptation Protocol
-wins 1512/tcp
-wins 1512/udp
-radius 1812/tcp
-radius 1812/udp
-radius-acct 1813/tcp
-radius-acct 1813/udp
-cvspserver 2401/tcp
-icpv2 3130/tcp
-icpv2 3130/udp
-iscsi-target 3260/tcp
-iscsi-target 3260/udp
-mysql 3306/tcp
-mysql 3306/udp
-nut 3493/tcp # Network UPS Tools
-svn 3690/tcp
-svn 3690/udp
-epmd 4369/tcp # Erlang Port Mapper Daemon
-epmd 4369/udp
-sip 5060/tcp
-sip 5060/udp
-sip-tls 5061/tcp
-sip-tls 5061/udp
-xmpp-client 5222/tcp
-xmpp-server 5269/tcp
-postgresql 5432/tcp postgres
-postgresql 5432/udp postgres
-http-alt 8080/tcp webcache # HTTP Alternate, webcache
-http-alt 8080/udp
-memcache 11211/tcp
-memcache 11211/udp
-#
-# Host specific functions
-#
-tftp 69/udp
-rje 77/tcp
-finger 79/tcp
-link 87/tcp ttylink
-supdup 95/tcp
-iso-tsap 102/tcp
-x400 103/tcp # ISO Mail
-x400-snd 104/tcp
-csnet-ns 105/tcp
-uucp-path 117/tcp
-nntp 119/tcp usenet # Network News Transfer
-ntp 123/tcp # Network Time Protocol
-ntp 123/udp # Network Time Protocol
-netbios-ns 137/tcp # NETBIOS Name Service
-netbios-ns 137/udp # NETBIOS Name Service
-netbios-dgm 138/tcp # NETBIOS Datagram Service
-netbios-dgm 138/udp # NETBIOS Datagram Service
-netbios-ssn 139/tcp # NETBIOS Session Service
-netbios-ssn 139/udp # NETBIOS Session Service
-NeWS 144/tcp news # Window System
-snmpd 161/udp snmp # Net-SNMP snmp daemon
-slp 427/tcp slp # Service Location Protocol, V2
-slp 427/udp slp # Service Location Protocol, V2
-mobile-ip 434/udp mobile-ip # Mobile-IP
-cvc_hostd 442/tcp # Network Console
-microsoft-ds 445/tcp # Microsoft Directory Services
-microsoft-ds 445/udp # Microsoft Directory Services
-ike 500/udp ike # Internet Key Exchange
uuidgen 697/tcp # UUID Generator
uuidgen 697/udp # UUID Generator
#
# UNIX specific services
#
-# these are NOT officially assigned
-#
rdc 121/tcp # SNDR server daemon
exec 512/tcp
-login 513/tcp
-shell 514/tcp cmd # no passwords used
-printer 515/tcp spooler # line printer spooler
-courier 530/tcp rpc # experimental
-uucp 540/tcp uucpd # uucp daemon
biff 512/udp comsat
+login 513/tcp
who 513/udp whod
+shell 514/tcp cmd # no passwords used
syslog 514/udp
+printer 515/tcp spooler # line printer spooler
talk 517/udp
-route 520/udp router routed
+ntalk 518/udp
+route 520/udp router routed # RIP
ripng 521/udp
-klogin 543/tcp # Kerberos authenticated rlogin
-kshell 544/tcp cmd # Kerberos authenticated remote shell
+timed 525/udp timeserver
+tempo 526/tcp newdate
+courier 530/tcp rpc
+conference 531/tcp chat
+netnews 532/tcp readnews
+netwall 533/udp # for emergency broadcasts
+gdomap 538/tcp # GNUstep distributed objects
+gdomap 538/udp
+uucp 540/tcp uucpd # uucp daemon
+klogin 543/tcp # Kerberized `rlogin' (v5)
+kshell 544/tcp krcmd # Kerberized `rsh' (v5)
+afpovertcp 548/tcp # AFP over TCP
+afpovertcp 548/udp
new-rwho 550/udp new-who # experimental
+remotefs 556/tcp rfs_server rfs # Brunhoff remote filesystem
rmonitor 560/udp rmonitord # experimental
monitor 561/udp # experimental
+nntps 563/tcp snntp # NNTP over SSL
+nntps 563/udp snntp
+submission 587/tcp # Submission [RFC4409]
+submission 587/udp
pcserver 600/tcp # ECD Integrated PC board srvr
+ldaps 636/tcp # LDAP over SSL
+ldaps 636/udp
+tinc 655/tcp # tinc control port
+tinc 655/udp
sun-dr 665/tcp # Remote Dynamic Reconfiguration
-kerberos-adm 749/tcp # Kerberos V5 Administration
+silc 706/tcp
+silc 706/udp
+kerberos-adm 749/tcp # Kerberos `kadmin' (v5)
kerberos-adm 749/udp # Kerberos V5 Administration
kerberos-iv 750/udp # Kerberos V4 key server
krb5_prop 754/tcp # Kerberos V5 KDC propogation
-swat 901/tcp # Samba Web Adm.Tool
+#
+webster 765/tcp # Network dictionary
+webster 765/udp
+iscsi 860/tcp
+iscsi 860/udp
+rsync 873/tcp
+rsync 873/udp
+ftps-data 989/tcp # FTP over SSL (data)
+ftps 990/tcp
+telnets 992/tcp # Telnet over SSL
+telnets 992/udp
+imaps 993/tcp # IMAP over SSL
+imaps 993/udp
+ircs 994/tcp # IRC over SSL
+ircs 994/udp
+pop3s 995/tcp # POP-3 over SSL
+pop3s 995/udp
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
+#
+# From ``Assigned Numbers'':
+#
+#> The Registered Ports are not controlled by the IANA and on most systems
+#> can be used by ordinary user processes or programs executed by ordinary
+#> users.
+#
+#> Ports are used in the TCP [45,106] to name the ends of logical
+#> connections which carry long term conversations. For the purpose of
+#> providing services to unknown callers, a service contact port is
+#> defined. This list specifies the port used by the server process as its
+#> contact port. While the IANA can not control uses of these ports it
+#> does register or list uses of these ports as a convienence to the
+#> community.
+#
+socks 1080/tcp # socks proxy server
+socks 1080/udp
+proofd 1093/tcp
+proofd 1093/udp
+rootd 1094/tcp
+rootd 1094/udp
+openvpn 1194/tcp
+openvpn 1194/udp
+rmiregistry 1099/tcp # Java RMI Registry
+rmiregistry 1099/udp
+kazaa 1214/tcp
+kazaa 1214/udp
+nessus 1241/tcp # Nessus vulnerability
+nessus 1241/udp # assessment scanner
+icap 1344/tcp # Internet Content Adaptation Protocol
+lotusnote 1352/tcp lotusnotes # Lotus Note
+lotusnote 1352/udp lotusnotes
+ms-sql-s 1433/tcp # Microsoft SQL Server
+ms-sql-s 1433/udp
+ms-sql-m 1434/tcp # Microsoft SQL Monitor
+ms-sql-m 1434/udp
+wins 1512/tcp
+wins 1512/udp
ingreslock 1524/tcp
+ingreslock 1524/udp
+prospero-np 1525/tcp # Prospero non-privileged
+prospero-np 1525/udp
+datametrics 1645/tcp old-radius
+datametrics 1645/udp old-radius
+sa-msg-port 1646/tcp old-radacct
+sa-msg-port 1646/udp old-radacct
+kermit 1649/tcp
+kermit 1649/udp
+l2f 1701/tcp l2tp
+l2f 1701/udp l2tp
www-ldap-gw 1760/tcp # HTTP to LDAP gateway
www-ldap-gw 1760/udp # HTTP to LDAP gateway
-listen 2766/tcp # System V listener port
-nfsd 2049/udp nfs # NFS server daemon (clts)
-nfsd 2049/tcp nfs # NFS server daemon (cots)
+radius 1812/tcp
+radius 1812/udp
+radius-acct 1813/tcp radacct # Radius Accounting
+radius-acct 1813/udp radacct
+msnp 1863/tcp # MSN Messenger
+msnp 1863/udp
+unix-status 1957/tcp # remstats unix-status server
+log-server 1958/tcp # remstats log server
+remoteping 1959/tcp # remstats remoteping server
+cisco-sccp 2000/tcp sieve # Cisco SCCP
+cisco-sccp 2000/udp
+search 2010/tcp ndtp
+pipe_server 2010/tcp
+nfs 2049/tcp # Network File System
+nfs 2049/udp # Network File System
nfsd 2049/sctp nfs
-eklogin 2105/tcp # Kerberos encrypted rlogin
+gnunet 2086/tcp
+gnunet 2086/udp
+rtcm-sc104 2101/tcp # RTCM SC-104 IANA 1/29/99
+rtcm-sc104 2101/udp
+gsigatekeeper 2119/tcp
+gsigatekeeper 2119/udp
+gris 2135/tcp # Grid Resource Information Server
+gris 2135/udp # Grid Resource Information Server
+cvspserver 2401/tcp # CVS client/server operations
+cvspserver 2401/udp
+venus 2430/tcp # codacon port
+venus 2430/udp # Venus callback/wbc interface
+venus-se 2431/tcp # tcp side effects
+venus-se 2431/udp # udp sftp side effect
+codasrv 2432/tcp # not used
+codasrv 2432/udp # server port
+codasrv-se 2433/tcp # tcp side effects
+codasrv-se 2433/udp # udp sftp side effect
+mon 2583/tcp # MON traps
+mon 2583/udp
+dict 2628/tcp # Dictionary server
+dict 2628/udp
+listen 2766/tcp # System V listener port
+gsiftp 2811/tcp
+gsiftp 2811/udp
+gpsd 2947/tcp
+gpsd 2947/udp
+gds_db 3050/tcp # InterBase server
+gds_db 3050/udp
+icpv2 3130/tcp icp # Internet Cache Protocol
+icpv2 3130/udp icp
+iscsi-target 3260/tcp
+iscsi-target 3260/udp
+mysql 3306/tcp
+mysql 3306/udp
+nut 3493/tcp # Network UPS Tools
+nut 3493/udp
+distcc 3632/tcp # distributed compiler
+distcc 3632/udp
+daap 3689/tcp # Digital Audio Access Protocol
+daap 3689/udp
+svn 3690/tcp subversion # Subversion protocol
+svn 3690/udp subversion
+suucp 4031/tcp # UUCP over SSL
+suucp 4031/udp # UUCP over SSL
lockd 4045/udp # NFS lock daemon/manager
lockd 4045/tcp
+sysrqd 4094/tcp # sysrq daemon
+sysrqd 4094/udp # sysrq daemon
+remctl 4373/tcp # Remote Authenticated Command Service
+remctl 4373/udp # Remote Authenticated Command Service
+epmd 4369/tcp # Erlang Port Mapper Daemon
+epmd 4369/udp
ipsec-nat-t 4500/udp # IPsec NAT-Traversal
+iax 4569/tcp # Inter-Asterisk eXchange
+iax 4569/udp
vxlan 4789/udp # Virtual eXtensible Local Area Network (VXLAN)
+radmin-port 4899/tcp # RAdmin Port
+radmin-port 4899/udp
+rfe 5002/udp # Radio Free Ethernet
+rfe 5002/tcp
+mmcc 5050/tcp # multimedia conference control tool (Yahoo IM)
+mmcc 5050/udp
+sip 5060/tcp # Session Initiation Protocol
+sip 5060/udp
+sip-tls 5061/tcp
+sip-tls 5061/udp
+aol 5190/tcp # AIM
+aol 5190/udp
+xmpp-client 5222/tcp jabber-client # Jabber Client Connection
+xmpp-client 5222/udp jabber-client
+xmpp-server 5269/tcp jabber-server # Jabber Server Connection
+xmpp-server 5269/udp jabber-server
+cfengine 5308/tcp
+cfengine 5308/udp
+mdns 5353/tcp # Multicast DNS
mdns 5353/udp # Multicast DNS
-mdns 5353/tcp
+postgresql 5432/tcp postgres # PostgreSQL Database
+postgresql 5432/udp postgres
+freeciv 5556/tcp rptp # Freeciv gameplay
+freeciv 5556/udp
+amqp 5672/tcp
+amqp 5672/udp
+amqp 5672/sctp
+ggz 5688/tcp # GGZ Gaming Zone
+ggz 5688/udp # GGZ Gaming Zone
vnc-server 5900/tcp # VNC Server
+x11 6000/tcp x11-0 # X Window System
+x11 6000/udp x11-0
+x11-1 6001/tcp
+x11-1 6001/udp
+x11-2 6002/tcp
+x11-2 6002/udp
+x11-3 6003/tcp
+x11-3 6003/udp
+x11-4 6004/tcp
+x11-4 6004/udp
+x11-5 6005/tcp
+x11-5 6005/udp
+x11-6 6006/tcp
+x11-6 6006/udp
+x11-7 6007/tcp
+x11-7 6007/udp
dtspc 6112/tcp # CDE subprocess control
+gnutella-svc 6346/tcp # gnutella
+gnutella-svc 6346/udp
+gnutella-rtr 6347/tcp # gnutella
+gnutella-rtr 6347/udp
+sge_qmaster 6444/tcp # Grid Engine Qmaster Service
+sge_qmaster 6444/udp # Grid Engine Qmaster Service
+sge_execd 6445/tcp # Grid Engine Execution Service
+sge_execd 6445/udp # Grid Engine Execution Service
servicetag 6481/udp
servicetag 6481/tcp
-fs 7100/tcp # Font server
+afs3-fileserver 7000/tcp bbs # file server itself
+afs3-fileserver 7000/udp bbs
+afs3-callback 7001/tcp # callbacks to cache managers
+afs3-callback 7001/udp
+afs3-prserver 7002/tcp # users & groups database
+afs3-prserver 7002/udp
+afs3-vlserver 7003/tcp # volume location database
+afs3-vlserver 7003/udp
+afs3-kaserver 7004/tcp # AFS/Kerberos authentication
+afs3-kaserver 7004/udp
+afs3-volser 7005/tcp # volume managment server
+afs3-volser 7005/udp
+afs3-errors 7006/tcp # error interpretation service
+afs3-errors 7006/udp
+afs3-bos 7007/tcp # basic overseer process
+afs3-bos 7007/udp
+afs3-update 7008/tcp # server-to-server updater
+afs3-update 7008/udp
+afs3-rmtsys 7009/tcp # remote cache manager service
+afs3-rmtsys 7009/udp
+font-service 7100/tcp xfs # X Font Service
+font-service 7100/udp xfs
+http-alt 8080/tcp webcache # WWW caching service
+http-alt 8080/udp # WWW caching service
+bacula-dir 9101/tcp # Bacula Director
+bacula-dir 9101/udp
+bacula-fd 9102/tcp # Bacula File Daemon
+bacula-fd 9102/udp
+bacula-sd 9103/tcp # Bacula Storage Daemon
+bacula-sd 9103/udp
+xmms2 9667/tcp # Cross-platform Music Multiplexing System
+xmms2 9667/udp # Cross-platform Music Multiplexing System
+amanda 10080/tcp # amanda backup services
+amanda 10080/udp
+memcache 11211/tcp
+memcache 11211/udp
+hkp 11371/tcp # OpenPGP HTTP Keyserver
+hkp 11371/udp # OpenPGP HTTP Keyserver
+bprd 13720/tcp # VERITAS NetBackup
+bprd 13720/udp
+bpdbm 13721/tcp # VERITAS NetBackup
+bpdbm 13721/udp
+bpjava-msvc 13722/tcp # BP Java MSVC Protocol
+bpjava-msvc 13722/udp
+vnetd 13724/tcp # Veritas Network Utility
+vnetd 13724/udp
+bpcd 13782/tcp # VERITAS NetBackup
+bpcd 13782/udp
+vopied 13783/tcp # VERITAS NetBackup
+vopied 13783/udp
solaris-audit 16162/tcp # Secure remote audit logging
-wnn6 22273/tcp # Wnn6 jserver
-wnn6 22273/udp # Wnn6 jserver
+wnn6 22273/tcp # wnn6
+wnn6 22273/udp
+
+#
+# Datagram Delivery Protocol services
+#
+rtmp 1/ddp # Routing Table Maintenance Protocol
+nbp 2/ddp # Name Binding Protocol
+echo 4/ddp # AppleTalk Echo Protocol
+zip 6/ddp # Zone Information Protocol
+
+#=========================================================================
+# The remaining port numbers are not as allocated by IANA.
+#=========================================================================
+
+# Kerberos (Project Athena/MIT) services
+# Note that these are for Kerberos v4, and are unofficial. Sites running
+# v4 should uncomment these and comment out the v5 entries above.
+#
+kerberos4 750/udp kerberos-iv kdc # Kerberos (server)
+kerberos4 750/tcp kerberos-iv kdc
+kerberos_master 751/udp # Kerberos authentication
+kerberos_master 751/tcp
+passwd_server 752/udp # Kerberos passwd server
+krb_prop 754/tcp krb5_prop hprop # Kerberos slave propagation
+krbupdate 760/tcp kreg # Kerberos registration
+swat 901/tcp # swat
+kpop 1109/tcp # Pop with Kerberos
+knetd 2053/tcp # Kerberos de-multiplexor
+zephyr-srv 2102/udp # Zephyr server
+zephyr-clt 2103/udp # Zephyr serv-hm connection
+zephyr-hm 2104/udp # Zephyr hostmanager
+eklogin 2105/tcp # Kerberos encrypted rlogin
+# Hmmm. Are we using Kv4 or Kv5 now? Worrying.
+# The following is probably Kerberos v5 --- ajt@debian.org (11/02/2000)
+kx 2111/tcp # X over Kerberos
+iprop 2121/tcp # incremental propagation
+#
+# Unofficial but necessary (for NetBSD) services
+#
+supfilesrv 871/tcp # SUP server
+supfiledbg 1127/tcp # SUP debugging
+
+#
+# Services added for the Debian GNU/Linux distribution
+#
+linuxconf 98/tcp # LinuxConf
+poppassd 106/tcp # Eudora
+poppassd 106/udp
+ssmtp 465/tcp smtps # SMTP over SSL
+moira_db 775/tcp # Moira database
+moira_update 777/tcp # Moira update protocol
+moira_ureg 779/udp # Moira user registration
+spamd 783/tcp # spamassassin daemon
+omirr 808/tcp omirrd # online mirror
+omirr 808/udp omirrd
+customs 1001/tcp # pmake customs server
+customs 1001/udp
+skkserv 1178/tcp # skk jisho server port
+predict 1210/udp # predict -- satellite tracking
+rmtcfg 1236/tcp # Gracilis Packeten remote config server
+wipld 1300/tcp # Wipl network monitor
+xtel 1313/tcp # french minitel
+xtelw 1314/tcp # french minitel
+support 1529/tcp # GNATS
+cfinger 2003/tcp # GNU Finger
+frox 2121/tcp # frox: caching ftp proxy
+ninstall 2150/tcp # ninstall service
+ninstall 2150/udp
+zebrasrv 2600/tcp # zebra service
+zebra 2601/tcp # zebra vty
+ripd 2602/tcp # ripd vty (zebra)
+ripngd 2603/tcp # ripngd vty (zebra)
+ospfd 2604/tcp # ospfd vty (zebra)
+bgpd 2605/tcp # bgpd vty (zebra)
+ospf6d 2606/tcp # ospf6d vty (zebra)
+ospfapi 2607/tcp # OSPF-API
+isisd 2608/tcp # ISISd vty (zebra)
+afbackup 2988/tcp # Afbackup system
+afbackup 2988/udp
+afmbackup 2989/tcp # Afmbackup system
+afmbackup 2989/udp
+xtell 4224/tcp # xtell server
+fax 4557/tcp # FAX transmission service (old)
+hylafax 4559/tcp # HylaFAX client-server protocol (new)
+distmp3 4600/tcp # distmp3host daemon
+munin 4949/tcp lrrd # Munin
+enbd-cstatd 5051/tcp # ENBD client statd
+enbd-sstatd 5052/tcp # ENBD server statd
+pcrd 5151/tcp # PCR-1000 Daemon
+noclog 5354/tcp # noclogd with TCP (nocol)
+noclog 5354/udp # noclogd with UDP (nocol)
+hostmon 5355/tcp # hostmon uses TCP (nocol)
+hostmon 5355/udp # hostmon uses UDP (nocol)
+rplay 5555/udp # RPlay audio service
+nsca 5667/tcp # Nagios Agent - NSCA
+mrtd 5674/tcp # MRT Routing Daemon
+bgpsim 5675/tcp # MRT Routing Simulator
+canna 5680/tcp # cannaserver
+sane-port 6566/tcp sane saned # SANE network scanner daemon
+ircd 6667/tcp # Internet Relay Chat
+zope-ftp 8021/tcp # zope management by ftp
+tproxy 8081/tcp # Transparent Proxy
+omniorb 8088/tcp # OmniORB
+omniorb 8088/udp
+clc-build-daemon 8990/tcp # Common lisp build daemon
+xinetd 9098/tcp
+mandelspawn 9359/udp mandelbrot # network mandelbrot
+git 9418/tcp # Git Version Control System
+zope 9673/tcp # zope server
+webmin 10000/tcp
+kamanda 10081/tcp # amanda backup services (Kerberos)
+kamanda 10081/udp
+amandaidx 10082/tcp # amanda backup services
+amidxtape 10083/tcp # amanda backup services
+smsqp 11201/tcp # Alamin SMS gateway
+smsqp 11201/udp
+xpilot 15345/tcp # XPilot Contact Port
+xpilot 15345/udp
+sgi-cmsd 17001/udp # Cluster membership services daemon
+sgi-crsd 17002/udp
+sgi-gcd 17003/udp # SGI Group membership daemon
+sgi-cad 17004/tcp # Cluster Admin daemon
+isdnlog 20011/tcp # isdn logging system
+isdnlog 20011/udp
+vboxd 20012/tcp # voice box system
+vboxd 20012/udp
+binkp 24554/tcp # binkp fidonet protocol
+asp 27374/tcp # Address Search Protocol
+asp 27374/udp
+csync2 30865/tcp # cluster synchronization tool
+dircproxy 57000/tcp # Detachable IRC Proxy
+tfido 60177/tcp # fidonet EMSI over telnet
+fido 60179/tcp # fidonet EMSI over TCP
+
+# Local services
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/Makefile b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile
index 1c32ce2ff1..1ec235b9a5 100644
--- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile
+++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile
@@ -21,6 +21,7 @@
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2015 Joyent, Inc.
# Copyright 2021 Tintri by DDN, Inc. All rights reserved.
#
@@ -28,7 +29,8 @@
include ../../../../lib/Makefile.lib
PROG= ipmgmtd
-OBJS= ipmgmt_main.o ipmgmt_door.o ipmgmt_persist.o ipmgmt_util.o
+OBJS= ipmgmt_main.o ipmgmt_door.o ipmgmt_persist.o ipmgmt_util.o \
+ ipmgmt_path.o
SRCS= $(OBJS:.o=.c)
SVCMETHOD= net-ipmgmt
MANIFEST= network-ipmgmt.xml
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 e3f18fea71..45080518b4 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.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
* Copyright 2021, Tintri by DDN. All rights reserved.
* Copyright 2022, Oxide Computer Company.
@@ -120,7 +121,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;
@@ -132,24 +135,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/ipmgmt_impl.h b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h
index fe7e95a87e..bf70839f36 100644
--- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h
+++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
* Copyright 2021 Tintri by DDN, Inc. All rights reserved.
*/
@@ -144,8 +145,6 @@ extern ipmgmt_aobjmap_list_t aobjmap;
#define ADDROBJ_LOOKUPADD 0x00000004
#define ADDROBJ_SETLIFNUM 0x00000008
-/* Permanent data store for ipadm */
-#define IPADM_DB_FILE "/etc/ipadm/ipadm.conf"
#define IPADM_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
/*
@@ -155,20 +154,12 @@ extern ipmgmt_aobjmap_list_t aobjmap;
*/
#define IPADM_DB_VERSION 1
-/*
- * A temporary file created in SMF volatile filesystem. This file captures the
- * in-memory copy of list `aobjmap' on disk. This is done to recover from
- * daemon reboot (using svcadm) or crashes.
- */
-#define IPADM_TMPFS_DIR "/etc/svc/volatile/ipadm"
-#define ADDROBJ_MAPPING_DB_FILE IPADM_TMPFS_DIR"/aobjmap.conf"
-
-/*
- * A temporary copy of the ipadm configuration file might need
- * to be created if write requests are encountered during boottime
- * and the root filesystem is mounted read-only.
- */
-#define IPADM_VOL_DB_FILE IPADM_TMPFS_DIR"/ipadm.conf"
+typedef enum ipadm_path {
+ IPADM_PATH_TMPFS_DIR = 1,
+ IPADM_PATH_ADDROBJ_MAP_DB,
+ IPADM_PATH_DB,
+ IPADM_PATH_VOL_DB
+} ipadm_path_t;
/* SCF resources required to interact with svc.configd */
typedef struct scf_resources {
@@ -200,6 +191,8 @@ extern void ipmgmt_release_scf_resources(scf_resources_t *);
extern boolean_t ipmgmt_needs_upgrade(scf_resources_t *);
extern void ipmgmt_update_dbver(scf_resources_t *);
+extern void ipmgmt_path(ipadm_path_t, char *, size_t);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c
index c31c91a547..41aeb2d98c 100644
--- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c
+++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
* Copyright 2021 Tintri by DDN, Inc. All rights reserved.
*/
@@ -106,6 +107,7 @@ ipmgmt_db_init()
int fd, err, scferr;
scf_resources_t res;
boolean_t upgrade = B_TRUE;
+ char aobjpath[MAXPATHLEN];
/*
* Check to see if we need to upgrade the data-store. We need to
@@ -135,11 +137,11 @@ ipmgmt_db_init()
ipmgmt_release_scf_resources(&res);
/* creates the address object data store, if it doesn't exist */
- if ((fd = open(ADDROBJ_MAPPING_DB_FILE, O_CREAT|O_RDONLY,
- IPADM_FILE_MODE)) == -1) {
+ ipmgmt_path(IPADM_PATH_ADDROBJ_MAP_DB, aobjpath, sizeof (aobjpath));
+ if ((fd = open(aobjpath, O_CREAT|O_RDONLY, IPADM_FILE_MODE)) == -1) {
err = errno;
- ipmgmt_log(LOG_ERR, "could not open %s: %s",
- ADDROBJ_MAPPING_DB_FILE, strerror(err));
+ ipmgmt_log(LOG_ERR, "could not open %s: %s", aobjpath,
+ strerror(err));
return (err);
}
(void) close(fd);
@@ -153,8 +155,8 @@ ipmgmt_db_init()
* representation of the mapping. That is, build `aobjmap' structure
* from address object data store.
*/
- if ((err = ipadm_rw_db(ipmgmt_aobjmap_init, NULL,
- ADDROBJ_MAPPING_DB_FILE, 0, IPADM_DB_READ)) != 0) {
+ if ((err = ipadm_rw_db(ipmgmt_aobjmap_init, NULL, aobjpath, 0,
+ IPADM_DB_READ)) != 0) {
/* if there was nothing to initialize, it's fine */
if (err != ENOENT)
return (err);
@@ -166,17 +168,42 @@ ipmgmt_db_init()
return (err);
}
+static const char *
+ipmgmt_door_path()
+{
+ static char door[MAXPATHLEN];
+ static boolean_t init_done = B_FALSE;
+
+ if (!init_done) {
+ const char *zroot = zone_get_nroot();
+
+ /*
+ * If this is a branded zone, make sure we use the "/native"
+ * prefix for the door path:
+ */
+ (void) snprintf(door, sizeof (door), "%s%s", zroot != NULL ?
+ zroot : "", IPMGMT_DOOR);
+
+ init_done = B_TRUE;
+ }
+
+ return (door);
+}
+
static int
ipmgmt_door_init()
{
int fd;
int err;
+ const char *door = ipmgmt_door_path();
- /* create the door file for ipmgmtd */
- if ((fd = open(IPMGMT_DOOR, O_CREAT|O_RDONLY, IPADM_FILE_MODE)) == -1) {
+ /*
+ * Create the door file for ipmgmtd.
+ */
+ if ((fd = open(door, O_CREAT | O_RDONLY, IPADM_FILE_MODE)) == -1) {
err = errno;
- ipmgmt_log(LOG_ERR, "could not open %s: %s",
- IPMGMT_DOOR, strerror(err));
+ ipmgmt_log(LOG_ERR, "could not open %s: %s", door,
+ strerror(err));
return (err);
}
(void) close(fd);
@@ -187,15 +214,16 @@ ipmgmt_door_init()
ipmgmt_log(LOG_ERR, "failed to create door: %s", strerror(err));
return (err);
}
+
/*
* fdetach first in case a previous daemon instance exited
* ungracefully.
*/
- (void) fdetach(IPMGMT_DOOR);
- if (fattach(ipmgmt_door_fd, IPMGMT_DOOR) != 0) {
+ (void) fdetach(door);
+ if (fattach(ipmgmt_door_fd, door) != 0) {
err = errno;
- ipmgmt_log(LOG_ERR, "failed to attach door to %s: %s",
- IPMGMT_DOOR, strerror(err));
+ ipmgmt_log(LOG_ERR, "failed to attach door to %s: %s", door,
+ strerror(err));
goto fail;
}
return (0);
@@ -208,13 +236,15 @@ fail:
static void
ipmgmt_door_fini()
{
+ const char *door = ipmgmt_door_path();
+
if (ipmgmt_door_fd == -1)
return;
- (void) fdetach(IPMGMT_DOOR);
+ (void) fdetach(door);
if (door_revoke(ipmgmt_door_fd) == -1) {
ipmgmt_log(LOG_ERR, "failed to revoke access to door %s: %s",
- IPMGMT_DOOR, strerror(errno));
+ door, strerror(errno));
}
}
@@ -351,10 +381,14 @@ ipmgmt_init_privileges()
{
struct stat statbuf;
int err;
+ char tmpfsdir[MAXPATHLEN];
- /* create the IPADM_TMPFS_DIR directory */
- if (stat(IPADM_TMPFS_DIR, &statbuf) < 0) {
- if (mkdir(IPADM_TMPFS_DIR, (mode_t)0755) < 0) {
+ /*
+ * Create the volatile storage directory:
+ */
+ ipmgmt_path(IPADM_PATH_TMPFS_DIR, tmpfsdir, sizeof (tmpfsdir));
+ if (stat(tmpfsdir, &statbuf) < 0) {
+ if (mkdir(tmpfsdir, (mode_t)0755) < 0) {
err = errno;
goto fail;
}
@@ -365,8 +399,8 @@ ipmgmt_init_privileges()
}
}
- if ((chmod(IPADM_TMPFS_DIR, 0755) < 0) ||
- (chown(IPADM_TMPFS_DIR, UID_NETADM, GID_NETADM) < 0)) {
+ if ((chmod(tmpfsdir, 0755) < 0) ||
+ (chown(tmpfsdir, UID_NETADM, GID_NETADM) < 0)) {
err = errno;
goto fail;
}
diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_path.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_path.c
new file mode 100644
index 0000000000..0219ac1522
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_path.c
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+/*
+ * Lookup functions for various file paths used by ipmgmtd. This mechanism
+ * primarily exists to account for a native root prefix when run within a
+ * branded zone (e.g. "/native").
+ */
+
+#include <stdio.h>
+#include <zone.h>
+#include "ipmgmt_impl.h"
+
+#define IPADM_PERM_DIR "/etc/ipadm"
+#define IPADM_TMPFS_DIR "/etc/svc/volatile/ipadm"
+
+typedef struct ipadm_path_ent {
+ ipadm_path_t ipe_id;
+ const char *ipe_path;
+} ipadm_path_ent_t;
+
+static ipadm_path_ent_t ipadm_paths[] = {
+ /*
+ * A temporary directory created in the SMF volatile filesystem.
+ */
+ { IPADM_PATH_TMPFS_DIR, IPADM_TMPFS_DIR },
+
+ /*
+ * This file captures the in-memory copy of list `aobjmap' on disk.
+ * This allows the system to recover in the event that the daemon
+ * crashes or is restarted.
+ */
+ { IPADM_PATH_ADDROBJ_MAP_DB, IPADM_TMPFS_DIR "/aobjmap.conf" },
+
+ /*
+ * The permanent data store for ipadm.
+ */
+ { IPADM_PATH_DB, IPADM_PERM_DIR "/ipadm.conf" },
+
+ /*
+ * A temporary copy of the ipadm configuration created, if needed, to
+ * service write requests early in boot. This file is merged with the
+ * permanent data store once it is available for writes.
+ */
+ { IPADM_PATH_VOL_DB, IPADM_TMPFS_DIR "/ipadm.conf" },
+
+ { 0, NULL }
+};
+
+/*
+ * Load one of the paths used by ipadm into the provided string buffer.
+ * Prepends the native system prefix (e.g. "/native") if one is in effect,
+ * such as when running within a branded zone.
+ */
+void
+ipmgmt_path(ipadm_path_t ip, char *buf, size_t bufsz)
+{
+ int i;
+
+ for (i = 0; ipadm_paths[i].ipe_path != NULL; i++) {
+ if (ipadm_paths[i].ipe_id == ip) {
+ const char *zroot = zone_get_nroot();
+
+ (void) snprintf(buf, bufsz, "%s%s", zroot != NULL ?
+ zroot : "", ipadm_paths[i].ipe_path);
+
+ return;
+ }
+ }
+
+ abort();
+}
diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c
index e29ecbaeb2..0c44b51f5a 100644
--- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c
+++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c
@@ -542,13 +542,18 @@ static void *
ipmgmt_db_restore_thread(void *arg)
{
int err;
+ char confpath[MAXPATHLEN];
+ char tmpconfpath[MAXPATHLEN];
+
+ ipmgmt_path(IPADM_PATH_DB, confpath, sizeof (confpath));
+ ipmgmt_path(IPADM_PATH_VOL_DB, tmpconfpath, sizeof (tmpconfpath));
for (;;) {
(void) sleep(5);
(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
if (!ipmgmt_rdonly_root)
break;
- err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE);
+ err = ipmgmt_cpfile(tmpconfpath, confpath, B_FALSE);
if (err == 0) {
ipmgmt_rdonly_root = B_FALSE;
break;
@@ -580,6 +585,11 @@ ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
mode_t mode;
pthread_t tid;
pthread_attr_t attr;
+ char confpath[MAXPATHLEN];
+ char tmpconfpath[MAXPATHLEN];
+
+ ipmgmt_path(IPADM_PATH_DB, confpath, sizeof (confpath));
+ ipmgmt_path(IPADM_PATH_VOL_DB, tmpconfpath, sizeof (tmpconfpath));
writeop = (db_op != IPADM_DB_READ);
if (writeop) {
@@ -592,11 +602,10 @@ ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
/*
* Did a previous write attempt fail? If so, don't even try to
- * read/write to IPADM_DB_FILE.
+ * read/write to the permanent configuration file.
*/
if (!ipmgmt_rdonly_root) {
- err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE,
- mode, db_op);
+ err = ipadm_rw_db(db_walk_func, db_warg, confpath, mode, db_op);
if (err != EROFS)
goto done;
}
@@ -604,11 +613,11 @@ ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
/*
* If we haven't already copied the file to the volatile
* file system, do so. This should only happen on a failed
- * writeop(i.e., we have acquired the write lock above).
+ * writeop (i.e., we have acquired the write lock above).
*/
- if (access(IPADM_VOL_DB_FILE, F_OK) != 0) {
+ if (access(tmpconfpath, F_OK) != 0) {
assert(writeop);
- err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE);
+ err = ipmgmt_cpfile(confpath, tmpconfpath, B_TRUE);
if (err != 0)
goto done;
(void) pthread_attr_init(&attr);
@@ -619,7 +628,7 @@ ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
NULL);
(void) pthread_attr_destroy(&attr);
if (err != 0) {
- (void) unlink(IPADM_VOL_DB_FILE);
+ (void) unlink(tmpconfpath);
goto done;
}
ipmgmt_rdonly_root = B_TRUE;
@@ -628,7 +637,7 @@ ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
/*
* Read/write from the volatile copy.
*/
- err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE,
+ err = ipadm_rw_db(db_walk_func, db_warg, tmpconfpath,
mode, db_op);
done:
(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
@@ -1506,6 +1515,9 @@ ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
int err;
ipadm_dbwrite_cbarg_t cb;
nvlist_t *nvl = NULL;
+ char aobjpath[MAXPATHLEN];
+
+ ipmgmt_path(IPADM_PATH_ADDROBJ_MAP_DB, aobjpath, sizeof (aobjpath));
if (op == IPADM_DB_WRITE) {
if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
@@ -1516,14 +1528,14 @@ ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
else
cb.dbw_flags = 0;
- err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb,
- ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE);
+ err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb, aobjpath,
+ IPADM_FILE_MODE, IPADM_DB_WRITE);
nvlist_free(nvl);
} else {
assert(op == IPADM_DB_DELETE);
- err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep,
- ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE);
+ err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep, aobjpath,
+ IPADM_FILE_MODE, IPADM_DB_DELETE);
}
return (err);
}
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c
index 133254be4a..e6a88304a7 100644
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c
@@ -32,6 +32,7 @@
#include <stdio.h>
#include <sys/stat.h>
#include <libnvpair.h>
+#include <zone.h>
#include "common.h"
#include "defaults.h"
@@ -67,6 +68,32 @@ static struct dhcp_default defaults[] = {
{ "ADOPT_DOMAINNAME", "0", 0, 0 },
};
+
+/*
+ * df_find_defaults(): builds the path to the default configuration file
+ *
+ * input: void
+ * output: void
+ */
+
+static const char *
+df_find_defaults(void)
+{
+ static char agent_defaults_path[MAXPATHLEN] = { 0 };
+ const char *zroot = NULL;
+
+ if (agent_defaults_path[0] != '\0') {
+ return agent_defaults_path;
+ }
+
+ zroot = zone_get_nroot();
+
+ (void) snprintf(agent_defaults_path, MAXPATHLEN, "%s%s",
+ zroot != NULL ? zroot : "", DHCP_AGENT_DEFAULTS);
+
+ return agent_defaults_path;
+}
+
/*
* df_build_cache(): builds the defaults nvlist cache
*
@@ -77,6 +104,7 @@ static struct dhcp_default defaults[] = {
static nvlist_t *
df_build_cache(void)
{
+ const char *agent_defaults_path = df_find_defaults();
char entry[1024];
int i;
char *param, *pastv6, *value, *end;
@@ -84,7 +112,7 @@ df_build_cache(void)
nvlist_t *nvlist;
struct dhcp_default *defp;
- if ((fp = fopen(DHCP_AGENT_DEFAULTS, "r")) == NULL)
+ if ((fp = fopen(agent_defaults_path, "r")) == NULL)
return (NULL);
if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
@@ -164,6 +192,7 @@ df_build_cache(void)
const char *
df_get_string(const char *if_name, boolean_t isv6, uint_t param)
{
+ const char *agent_defaults_path = df_find_defaults();
char *value;
char paramstr[256];
char name[256];
@@ -175,10 +204,11 @@ df_get_string(const char *if_name, boolean_t isv6, uint_t param)
if (param >= (sizeof (defaults) / sizeof (*defaults)))
return (NULL);
- if (stat(DHCP_AGENT_DEFAULTS, &statbuf) != 0) {
+
+ if (stat(agent_defaults_path, &statbuf) != 0) {
if (!df_unavail_msg) {
dhcpmsg(MSG_WARNING, "cannot access %s; using "
- "built-in defaults", DHCP_AGENT_DEFAULTS);
+ "built-in defaults", agent_defaults_path);
df_unavail_msg = B_TRUE;
}
return (defaults[param].df_default);
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c
index 6b5a08a51a..7517f2c094 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.
* Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
*
* REQUESTING state of the client state machine.
@@ -39,6 +40,7 @@
#include <dhcp_hostconf.h>
#include <dhcpagent_util.h>
#include <dhcpmsg.h>
+#include <strings.h>
#include "states.h"
#include "util.h"
@@ -645,8 +647,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/wpad/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile
index 7d72bad58f..0efa270e8b 100644
--- a/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile
+++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile
@@ -34,8 +34,7 @@ include ../../../Makefile.cmd
ROOTMANIFESTDIR = $(ROOTSVCNETWORK)
LDLIBS += -ldladm -ldlpi
-NATIVE_LIBS += libcrypto.so
-all install := LDLIBS += -lcrypto
+all install := LDLIBS += -lsunw_crypto
SMOFF += all_func_returns
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/Makefile
index 864920184a..3f794a331a 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/Makefile
+++ b/usr/src/cmd/cmd-inet/usr.sbin/Makefile
@@ -157,6 +157,7 @@ route := CPPFLAGS += -DNDEBUG
ndd := LDLIBS += -ldladm -lipadm
$(RELEASE_BUILD)ndd := CERRWARN += -_gcc=-Wno-unused
in.comsat := LDFLAGS += $(MAPFILE.NGB:%=-Wl,-M%)
+route := LDLIBS += -lzonecfg -lcontract
.KEEP_STATE:
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/arp.c b/usr/src/cmd/cmd-inet/usr.sbin/arp.c
index 720b996f57..784e87ca6f 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/arp.c
+++ b/usr/src/cmd/cmd-inet/usr.sbin/arp.c
@@ -58,6 +58,7 @@
#include <arpa/inet.h>
#include <net/if_types.h>
#include <net/if_dl.h>
+#include <zone.h>
static int file(char *);
static int set(int, char *[]);
@@ -119,7 +120,11 @@ main(int argc, char *argv[])
* is to let netstat, which prints it as part of
* the MIB statistics, do it.
*/
- (void) execl("/usr/bin/netstat", "netstat",
+ char netstat_path[MAXPATHLEN];
+ const char *zroot = zone_get_nroot();
+ (void) snprintf(netstat_path, sizeof (netstat_path), "%s%s", zroot != NULL ?
+ zroot : "", "/usr/bin/netstat");
+ (void) execl(netstat_path, "netstat",
(n_flag ? "-np" : "-p"),
"-f", "inet", (char *)0);
(void) fprintf(stderr, "failed to exec netstat: %s\n",
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ndp.c b/usr/src/cmd/cmd-inet/usr.sbin/ndp.c
index d2c26bf0b2..c77e1587d9 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/ndp.c
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ndp.c
@@ -40,6 +40,7 @@
#include <inet/ip.h>
#include <net/if_dl.h>
#include <net/route.h>
+#include <zone.h>
typedef struct sockaddr_in6 sin6_t;
@@ -95,7 +96,6 @@ static int ndp_set_nce(char *, char *, char *[], int);
static int ndp_set_file(char *);
static char *ndp_iface = NULL;
-static char *netstat_path = "/usr/bin/netstat";
static pid_t ndp_pid;
static boolean_t ndp_noresolve = B_FALSE; /* Don't lookup addresses */
static boolean_t ndp_run = B_TRUE;
@@ -103,6 +103,7 @@ static boolean_t ndp_run = B_TRUE;
#define MAX_ATTEMPTS 5
#define MAX_OPTS 5
#define WORDSEPS " \t\r\n"
+#define NETSTAT_PATH "/usr/bin/netstat"
/*
* Macros borrowed from route(8) for working with PF_ROUTE messages
@@ -767,6 +768,12 @@ ndp_get(int fd, struct lifreq *lifrp, void *unused)
static void
ndp_get_all(void)
{
+ char netstat_path[MAXPATHLEN];
+ const char *zroot = zone_get_nroot();
+
+ (void) snprintf(netstat_path, sizeof (netstat_path), "%s%s", zroot != NULL ?
+ zroot : "", NETSTAT_PATH);
+
(void) execl(netstat_path, "netstat",
(ndp_noresolve ? "-np" : "-p"),
"-f", "inet6", (char *)0);
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/route.c b/usr/src/cmd/cmd-inet/usr.sbin/route.c
index d8f11bd4a6..17a63d6f95 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/route.c
+++ b/usr/src/cmd/cmd-inet/usr.sbin/route.c
@@ -6,6 +6,7 @@
/* All Rights Reserved */
/* Copyright (c) 1990 Mentat Inc. */
+/* Copyright 2018, Joyent, Inc. */
/*
*
@@ -79,6 +80,13 @@
#include <assert.h>
#include <strings.h>
+#include <libcontract.h>
+#include <sys/ctfs.h>
+#include <sys/contract/process.h>
+#include <sys/wait.h>
+#include <libzonecfg.h>
+#include <zone.h>
+
#include <libtsnet.h>
#include <tsol/label.h>
@@ -292,6 +300,7 @@ static void syntax_error(char *err, ...);
static void usage(char *cp);
static void write_to_rtfile(FILE *fp, int argc, char **argv);
static void pmsg_secattr(const char *, size_t, const char *);
+static void do_zone(char *);
static pid_t pid;
static int s;
@@ -308,6 +317,7 @@ static char perm_file_sfx[] = "/etc/inet/static_routes";
static char *perm_file;
static char temp_file_sfx[] = "/etc/inet/static_routes.tmp";
static char *temp_file;
+static char *zonename;
static struct in6_addr in6_host_mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
/*
@@ -354,7 +364,7 @@ usage(char *cp)
cp);
}
(void) fprintf(stderr, gettext("usage: route [ -fnpqv ] "
- "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
+ "[-z <zone> ] [ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n"));
exit(1);
/* NOTREACHED */
}
@@ -418,7 +428,7 @@ main(int argc, char **argv)
if (argc < 2)
usage(NULL);
- while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) {
+ while ((ch = getopt(argc, argv, "R:nqdtvfpz:")) != EOF) {
switch (ch) {
case 'n':
nflag = B_TRUE;
@@ -444,6 +454,9 @@ main(int argc, char **argv)
case 'R':
root_dir = optarg;
break;
+ case 'z':
+ zonename = optarg;
+ break;
case '?':
default:
usage(NULL);
@@ -453,6 +466,8 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
+ do_zone(zonename);
+
pid = getpid();
if (tflag)
s = open("/dev/null", O_WRONLY);
@@ -3252,3 +3267,74 @@ pmsg_secattr(const char *sptr, size_t msglen, const char *labelstr)
sizeof (buf)));
}
}
+
+static void
+do_zone(char *name)
+{
+ zoneid_t zoneid;
+ zone_state_t st;
+ int fd, status, rc = 0;
+ pid_t pid;
+
+ if (name == NULL)
+ return;
+
+ if (getzoneid() != GLOBAL_ZONEID) {
+ (void) fprintf(stderr,
+ "route: -z can only be specified from the global zone\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (strcmp(name, GLOBAL_ZONENAME) == 0)
+ return;
+
+ if (zone_get_state(name, &st) != Z_OK)
+ quit("unable to get zone state", errno);
+
+ if (st != ZONE_STATE_RUNNING) {
+ (void) fprintf(stderr, "route: zone must be running\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((zoneid = getzoneidbyname(name)) == -1)
+ quit("cannot determine zone id", errno);
+
+ if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) == -1)
+ quit("cannot open ctfs template", errno);
+
+ /*
+ * zone_enter() does not allow contracts to straddle zones, so we must
+ * create a new, though largely unused contract. Once we fork, the
+ * child is the only member of the new contract, so it can perform a
+ * zone_enter().
+ */
+ rc |= ct_tmpl_set_critical(fd, 0);
+ rc |= ct_tmpl_set_informative(fd, 0);
+ rc |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
+ rc |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
+ if (rc || ct_tmpl_activate(fd)) {
+ (void) close(fd);
+ quit("could not create contract", errno);
+ }
+
+ switch (pid = fork1()) {
+ case 0:
+ (void) ct_tmpl_clear(fd);
+ (void) close(fd);
+ if (zone_enter(zoneid) == -1)
+ quit("could not enter zone", errno);
+ return;
+
+ case -1:
+ quit("fork1 failed", errno);
+
+ default:
+ (void) ct_tmpl_clear(fd);
+ (void) close(fd);
+ if (waitpid(pid, &status, 0) < 0)
+ quit("waitpid failed", errno);
+
+ exit(WEXITSTATUS(status));
+ }
+
+}
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 657fc77f9d..6fad8a3513 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c
+++ b/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.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.
*/
#include <stdio.h>
@@ -44,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
@@ -2333,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.
*/
@@ -2342,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 e026093057..4839757233 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile
@@ -40,17 +40,18 @@ 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_vxlan.o \
- snoop_zip.o
+ snoop_svp.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
-LDLIBS += -ldhcputil -ldlpi -lsocket -lnsl -ltsol
+LDLIBS += -ldhcputil -ldlpi -lsocket -lnsl -ltsol -luuid
LDFLAGS += $(MAPFILE.NGB:%=-Wl,-M%)
CERRWARN += -_gcc=-Wno-switch
CERRWARN += -_gcc=-Wno-implicit-function-declaration
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 860bb55f79..c9b1eb848e 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c
@@ -124,6 +124,7 @@ main(int argc, char **argv)
char *output_area;
int nbytes;
char *datalink = NULL;
+ char *zonename = NULL;
dlpi_handle_t dh;
names[0] = '\0';
@@ -230,7 +231,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:fc:x:U?rqz"))
+ while ((c = getopt(argc, argv, "at:CPDSi:o:Nn:s:d:I:vVp:fc:x:U?rqz:Z"))
!= EOF) {
switch (c) {
case 'a':
@@ -337,8 +338,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 */
@@ -360,7 +364,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);
@@ -801,6 +805,8 @@ usage(void)
(void) fprintf(stderr,
"\t[ -r ] # Do not resolve address to name\n");
(void) fprintf(stderr,
+ "\t[ -z zone ] # Open links from named zone\n");
+ (void) fprintf(stderr,
"\n\t[ filter expression ]\n");
(void) fprintf(stderr, "\nExample:\n");
(void) fprintf(stderr, "\tsnoop -o saved host fred\n\n");
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 52a496db73..19a8c25a87 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h
@@ -216,7 +216,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);
@@ -295,6 +295,7 @@ 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 int interpret_svp(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 b0cc78b5f2..63eb4973a0 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
@@ -30,6 +30,7 @@
#include <strings.h>
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/signal.h>
@@ -115,7 +116,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;
@@ -123,6 +124,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.
@@ -146,7 +150,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));
}
@@ -636,6 +641,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_rport.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c
index 6e67d03950..77e9d97766 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,7 +21,7 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2015, Joyent, Inc.
+ * Copyright 2018, Joyent, Inc.
*/
#include <stdio.h>
@@ -130,6 +130,7 @@ static struct porttable pt_tcp[] = {
{ 540, "UUCP" },
{ 600, "PCSERVER" },
{ IPPORT_SOCKS, "SOCKS" },
+ { 1296, "SVP" },
{ 1524, "INGRESLOCK" },
{ 2904, "M2UA" },
{ 2905, "M3UA" },
@@ -430,6 +431,12 @@ interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst,
case IPPORT_VXLAN:
(void) interpret_vxlan(flags, data, dlen);
return (1);
+ case 1296:
+ if (proto == IPPROTO_TCP) {
+ (void) interpret_svp(flags, data, dlen);
+ return (1);
+ }
+ break;
}
}
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_svp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_svp.c
new file mode 100644
index 0000000000..a0768c2234
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_svp.c
@@ -0,0 +1,557 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+/*
+ * Decode SVP (SmartDC VxLAN Protocol) packets
+ */
+
+#include <inttypes.h>
+#include <sys/crc32.h>
+#include <uuid/uuid.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <libvarpd_svp_prot.h>
+#include "snoop.h"
+
+/*
+ * String size large enough for an IPv6 address + / + a 3 digit (or less)
+ * prefix length
+ */
+#define ADDRSTR_LEN (INET6_ADDRSTRLEN + 4)
+
+/*
+ * Large enough for all currently known status strings as well as a
+ * 16-bit hex value.
+ */
+#define STATUSSTR_LEN 32
+
+/*
+ * Large enough for all currently known op strings, as well as a
+ * 16-bit hex value.
+ */
+#define OPSTR_LEN 32
+
+/*
+ * Large enough for VL3 types and bulk types, as well as a 32-bit
+ * hex value.
+ */
+#define TYPESTR_LEN 32
+
+static uint32_t svp_crc32_tab[] = { CRC32_TABLE };
+
+#define STR(_x, _buf, _len) \
+ case _x: \
+ (void) strlcpy(_buf, #_x, _len); \
+ break
+
+static void
+svp_op_str(uint16_t op, char *buf, size_t buflen)
+{
+ switch (op) {
+ STR(SVP_R_UNKNOWN, buf, buflen);
+ STR(SVP_R_PING, buf, buflen);
+ STR(SVP_R_PONG, buf, buflen);
+ STR(SVP_R_VL2_REQ, buf, buflen);
+ STR(SVP_R_VL2_ACK, buf, buflen);
+ STR(SVP_R_VL3_REQ, buf, buflen);
+ STR(SVP_R_VL3_ACK, buf, buflen);
+ STR(SVP_R_BULK_REQ, buf, buflen);
+ STR(SVP_R_BULK_ACK, buf, buflen);
+ STR(SVP_R_LOG_REQ, buf, buflen);
+ STR(SVP_R_LOG_ACK, buf, buflen);
+ STR(SVP_R_LOG_RM, buf, buflen);
+ STR(SVP_R_LOG_RM_ACK, buf, buflen);
+ STR(SVP_R_SHOOTDOWN, buf, buflen);
+ default:
+ (void) snprintf(buf, buflen, "0x%hx", op);
+ }
+}
+
+static void
+svp_status_str(uint16_t status, char *buf, size_t buflen)
+{
+ switch (status) {
+ STR(SVP_S_OK, buf, buflen);
+ STR(SVP_S_FATAL, buf, buflen);
+ STR(SVP_S_NOTFOUND, buf, buflen);
+ STR(SVP_S_BADL3TYPE, buf, buflen);
+ STR(SVP_S_BADBULK, buf, buflen);
+ default:
+ (void) snprintf(buf, buflen, "0x%hx", status);
+ }
+}
+
+static void
+svp_vl3_type_str(uint32_t type, char *buf, size_t buflen)
+{
+ switch (type) {
+ STR(SVP_VL3_IP, buf, buflen);
+ STR(SVP_VL3_IPV6, buf, buflen);
+ default:
+ (void) snprintf(buf, buflen, "0x%x", type);
+ }
+}
+
+static void
+svp_bulk_type_str(uint32_t type, char *buf, size_t buflen)
+{
+ switch (type) {
+ STR(SVP_BULK_VL2, buf, buflen);
+ STR(SVP_BULK_VL3, buf, buflen);
+ default:
+ (void) snprintf(buf, buflen, "0x%x", type);
+ }
+}
+
+static void
+svp_log_type_str(uint32_t type, char *buf, size_t buflen)
+{
+ switch (type) {
+ STR(SVP_LOG_VL2, buf, buflen);
+ STR(SVP_LOG_VL3, buf, buflen);
+ default:
+ (void) snprintf(buf, buflen, "0x%x", type);
+ }
+}
+#undef STR
+
+static void
+svp_addr_str(void *addrp, uint8_t *prefixp, char *buf, size_t buflen)
+{
+ struct in_addr v4;
+ int af = AF_INET6;
+
+ if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addrp)) {
+ af = AF_INET;
+ IN6_V4MAPPED_TO_INADDR((struct in6_addr *)addrp, &v4);
+ addrp = &v4;
+ }
+
+ if (inet_ntop(af, addrp, buf, buflen) == NULL) {
+ uint8_t *p = addrp;
+ size_t i;
+
+ (void) strlcpy(buf, "0x", buflen);
+ for (i = 0; i < 16; i++) {
+ (void) snprintf(buf + 2 + i * 2,
+ sizeof (buf) - 2 - i * 2, "%02hhx", p[i]);
+ }
+ }
+
+ if (prefixp != NULL && *prefixp != 128) {
+ char buf2[5]; /* / + 3 digits + NUL */
+
+ if (af == AF_INET)
+ *prefixp -= 96;
+
+ (void) snprintf(buf2, sizeof (buf2), "/%hhu", *prefixp);
+ (void) strlcat(buf, buf2, buflen);
+ }
+}
+
+static boolean_t
+svp_check_crc(char *data, int len)
+{
+ svp_req_t *req = (svp_req_t *)data;
+ uint32_t save_crc = req->svp_crc32;
+ uint32_t crc = -1U;
+
+ req->svp_crc32 = 0;
+ CRC32(crc, (uint8_t *)data, len, -1U, svp_crc32_tab);
+ crc = ~crc;
+ req->svp_crc32 = save_crc;
+
+ return (ntohl(save_crc) == crc ? B_TRUE : B_FALSE);
+}
+
+static void
+do_svp_vl2_req(void *data, int len)
+{
+ svp_vl2_req_t *vl2 = data;
+
+ show_printf("MAC = %s", ether_ntoa((struct ether_addr *)vl2->sl2r_mac));
+ show_printf("Virtual network id = %u", ntohl(vl2->sl2r_vnetid));
+}
+
+static void
+do_svp_vl2_ack(void *data, int len)
+{
+ svp_vl2_ack_t *vl2a = data;
+ char status[STATUSSTR_LEN];
+ char addr[ADDRSTR_LEN];
+
+ svp_status_str(ntohs(vl2a->sl2a_status), status, sizeof (status));
+ svp_addr_str(vl2a->sl2a_addr, NULL, addr, sizeof (addr));
+
+ show_printf("Status = %s", status);
+ show_printf("UL3 Address = %s", addr);
+ show_printf("UL3 Port = %hu", ntohs(vl2a->sl2a_port));
+}
+
+static void
+do_svp_vl3_req(void *data, int len)
+{
+ svp_vl3_req_t *req = data;
+ char type[TYPESTR_LEN];
+ char addr[ADDRSTR_LEN];
+
+ svp_vl3_type_str(ntohl(req->sl3r_type), type, sizeof (type));
+ svp_addr_str(req->sl3r_ip, NULL, addr, sizeof (addr));
+
+ show_printf("Virtual network id = %u", ntohl(req->sl3r_vnetid));
+ show_printf("Type = %s", type);
+ show_printf("VL3 Address = %s", addr);
+}
+
+static void
+do_svp_vl3_ack(void *data, int len)
+{
+ svp_vl3_ack_t *vl3a = data;
+ char status[STATUSSTR_LEN];
+ char addr[ADDRSTR_LEN];
+
+ svp_status_str(ntohl(vl3a->sl3a_status), status, sizeof (status));
+ svp_addr_str(vl3a->sl3a_uip, NULL, addr, sizeof (addr));
+
+ show_printf("Status = %s", status);
+ show_printf("MAC = %s",
+ ether_ntoa((struct ether_addr *)vl3a->sl3a_mac));
+ show_printf("UL3 Address = %s", addr);
+ show_printf("UL3 Port = %hu", ntohs(vl3a->sl3a_uport));
+}
+
+static void
+do_svp_bulk_req(void *data, int len)
+{
+ svp_bulk_req_t *req = data;
+ char type[TYPESTR_LEN];
+
+ if (len < sizeof (svp_bulk_req_t)) {
+ show_printf("SVP_R_BULK_REQ runt");
+ return;
+ }
+
+ svp_bulk_type_str(ntohl(req->svbr_type), type, sizeof (type));
+ show_printf("Type = %s", type);
+}
+
+static void
+do_svp_bulk_ack(void *data, int len)
+{
+ svp_bulk_ack_t *ack = data;
+ char status[STATUSSTR_LEN];
+ char type[TYPESTR_LEN];
+
+ svp_status_str(ntohl(ack->svba_status), status, sizeof (status));
+ svp_bulk_type_str(ntohl(ack->svba_type), type, sizeof (type));
+
+ show_printf("Status = %s", status);
+ show_printf("Type = %s", type);
+
+ /*
+ * Currently the data format is undefined (see libvarp_svp_prot.h),
+ * so there is nothing else we can display.
+ */
+}
+
+static void
+do_svp_log_req(void *data, int len)
+{
+ svp_log_req_t *svlr = data;
+ char addr[ADDRSTR_LEN];
+
+ svp_addr_str(svlr->svlr_ip, NULL, addr, sizeof (addr));
+
+ show_printf("Count = %u", ntohl(svlr->svlr_count));
+ show_printf("Address = %s", addr);
+}
+
+static void
+do_svp_log_ack(void *data, int len)
+{
+ svp_log_ack_t *ack = data;
+ union {
+ svp_log_vl2_t *vl2;
+ svp_log_vl3_t *vl3;
+ uint32_t *vtype;
+ void *vd;
+ } u;
+ size_t total = 0, rlen = 0;
+ uint8_t prefixlen;
+ boolean_t is_host;
+ char status[STATUSSTR_LEN];
+ char typestr[TYPESTR_LEN];
+ char uuid[UUID_PRINTABLE_STRING_LENGTH];
+ char addr[ADDRSTR_LEN];
+
+ u.vd = (ack + 1);
+
+ svp_status_str(ntohl(ack->svla_status), status, sizeof (status));
+
+ show_printf("Status = %s", status);
+ len -= sizeof (*ack);
+
+ while (len > 0) {
+ uint32_t type;
+
+ if (len < sizeof (uint32_t)) {
+ show_printf(" Trailing runt");
+ break;
+ }
+
+ type = ntohl(*u.vtype);
+ svp_log_type_str(type, typestr, sizeof (typestr));
+
+ switch (type) {
+ case SVP_LOG_VL2:
+ rlen = sizeof (svp_log_vl2_t);
+ break;
+ case SVP_LOG_VL3:
+ rlen = sizeof (svp_log_vl3_t);
+ break;
+ default:
+ /*
+ * If we don't know the type of log record we have,
+ * we cannot determine the size of the record, so we
+ * cannot continue past this.
+ */
+ show_printf("Log %-4zu: Log type = %s", ++total,
+ typestr);
+ return;
+ }
+
+ if (len < rlen) {
+ show_printf("Log %-4zu %s runt", ++total, typestr);
+ return;
+ }
+
+ /* These are the same in SVP_LOG_VL2 and SVP_LOG_VL3 records */
+ show_printf("Log %-4zu Log type = %s", ++total, typestr);
+
+ uuid_parse(uuid, u.vl2->svl2_id);
+ show_printf("%8s UUID = %s", "", uuid);
+
+ switch (type) {
+ case SVP_LOG_VL2:
+ show_printf("%8s MAC = %s", "",
+ ether_ntoa((struct ether_addr *)u.vl2->svl2_mac));
+ show_printf("%8s Vnet = %u", "",
+ ntohl(u.vl2->svl2_vnetid));
+ u.vl2++;
+ break;
+ case SVP_LOG_VL3:
+ svp_addr_str(u.vl3->svl3_ip, NULL, addr, sizeof (addr));
+
+ show_printf("%8s VLAN = %hu", "",
+ ntohs(u.vl3->svl3_vlan));
+ show_printf("%8s Address = %s", "", addr);
+ show_printf("%8s Vnet = %u", "",
+ ntohl(u.vl3->svl3_vnetid));
+ u.vl3++;
+ break;
+ }
+
+ len -= rlen;
+ show_space();
+ }
+ show_printf("Total log records = %zu", total);
+}
+
+static void
+do_svp_lrm_req(void *data, int len)
+{
+ /*
+ * Sized large enough to hold the expected size message
+ * (formatted below) if there's a length mismatch.
+ */
+ char mismatch_str[64] = { 0 };
+ svp_lrm_req_t *req = data;
+ size_t expected_sz = sizeof (*req);
+ size_t i, n;
+
+ n = ntohl(req->svrr_count);
+
+ /* IDs are 16-byte UUIDs */
+ expected_sz += n * UUID_LEN;
+ if (len != expected_sz) {
+ (void) snprintf(mismatch_str, sizeof (mismatch_str),
+ " (expected %zu bytes, actual size is %d bytes)",
+ expected_sz, len);
+ }
+ show_printf("ID Count = %u%s", n, mismatch_str);
+ if (len != expected_sz)
+ return;
+
+ for (i = 0; i < n; i++) {
+ char uuid[UUID_PRINTABLE_STRING_LENGTH];
+
+ uuid_parse(uuid, &req->svrr_ids[UUID_LEN * i]);
+ show_printf("%-4s %s", (i == 0) ? "IDs:" : "", uuid);
+ }
+}
+
+static void
+do_svp_lrm_ack(void *data, int len)
+{
+ svp_lrm_ack_t *ack = data;
+ char status[STATUSSTR_LEN];
+
+ svp_status_str(ntohl(ack->svra_status), status, sizeof (status));
+ show_printf("Status = %s", status);
+}
+
+static void
+do_svp_shootdown(void *data, int len)
+{
+ svp_shootdown_t *sd = data;
+
+ show_printf("Vnet = %u", ntohl(sd->svsd_vnetid));
+ show_printf("MAC Address = %s",
+ ether_ntoa((struct ether_addr *)sd->svsd_mac));
+}
+
+static struct svp_len_tbl {
+ uint16_t slt_op;
+ size_t slt_len;
+} svp_len_tbl[] = {
+ { SVP_R_UNKNOWN, 0 },
+ { SVP_R_PING, 0 },
+ { SVP_R_PONG, 0 },
+ { SVP_R_VL2_REQ, sizeof (svp_vl2_req_t) },
+ { SVP_R_VL2_ACK, sizeof (svp_vl2_ack_t) },
+ { SVP_R_VL3_REQ, sizeof (svp_vl3_req_t) },
+ { SVP_R_VL3_ACK, sizeof (svp_vl3_ack_t) },
+ { SVP_R_BULK_REQ, sizeof (svp_bulk_req_t) },
+ { SVP_R_BULK_ACK, sizeof (svp_bulk_ack_t) },
+ { SVP_R_LOG_REQ, sizeof (svp_log_req_t) },
+ { SVP_R_LOG_ACK, 0 },
+ { SVP_R_LOG_RM, sizeof (svp_lrm_req_t) },
+ { SVP_R_LOG_RM_ACK, sizeof (svp_lrm_ack_t) },
+ { SVP_R_SHOOTDOWN, sizeof (svp_shootdown_t) },
+};
+
+static boolean_t
+svp_check_runt(uint16_t op, int len)
+{
+ if (op > SVP_R_SHOOTDOWN)
+ return (B_FALSE);
+
+ if (len < svp_len_tbl[op].slt_len) {
+ char opstr[OPSTR_LEN];
+
+ svp_op_str(op, opstr, sizeof (opstr));
+ show_printf("%s Runt", opstr);
+ show_space();
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+int
+interpret_svp(int flags, char *data, int fraglen)
+{
+ svp_req_t *req = (svp_req_t *)data;
+ char opstr[OPSTR_LEN];
+ uint16_t op;
+ boolean_t crc_ok;
+
+ if (fraglen < sizeof (svp_req_t)) {
+ if (flags & F_SUM)
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "SVP RUNT");
+ if (flags & F_DTAIL)
+ show_header("SVP RUNT: ", "Short packet", fraglen);
+
+ return (fraglen);
+ }
+
+ op = ntohs(req->svp_op);
+ svp_op_str(op, opstr, sizeof (opstr));
+
+ crc_ok = svp_check_crc(data, fraglen);
+
+ if (flags & F_SUM) {
+ (void) snprintf(get_sum_line(), MAXLINE,
+ "SVP V=%hu OP=%s ID=%u%s", ntohs(req->svp_ver), opstr,
+ ntohl(req->svp_id), crc_ok ? "" : " (BAD CRC)");
+ }
+
+ if (flags & F_DTAIL) {
+ show_header("SVP: ", "SVP Header", sizeof (svp_req_t));
+ show_space();
+ show_printf("Version = %hu", ntohs(req->svp_ver));
+ show_printf("Op = %s", opstr);
+ show_printf("Packet length = %u bytes%s", ntohl(req->svp_size),
+ (ntohl(req->svp_size) == fraglen - sizeof (*req)) ?
+ "" : " (mismatch)");
+ show_printf("Id = %u", ntohl(req->svp_id));
+ show_printf("CRC = %x%s", ntohl(req->svp_crc32),
+ crc_ok ? "" : " (bad)");
+ show_space();
+
+ req++;
+ fraglen -= sizeof (*req);
+
+ /*
+ * Since we cannot know the length of an unknown op,
+ * svp_check_runt() returns B_TRUE for both truncated packets
+ * and unknown packets -- we have nothing meaningful besides
+ * the header we could print anyway.
+ */
+ if (svp_check_runt(op, fraglen))
+ return (fraglen);
+
+ switch (op) {
+ case SVP_R_VL2_REQ:
+ do_svp_vl2_req(req, fraglen);
+ break;
+ case SVP_R_VL2_ACK:
+ do_svp_vl2_ack(req, fraglen);
+ break;
+ case SVP_R_VL3_REQ:
+ do_svp_vl3_req(req, fraglen);
+ break;
+ case SVP_R_VL3_ACK:
+ do_svp_vl3_ack(req, fraglen);
+ break;
+ case SVP_R_BULK_REQ:
+ do_svp_bulk_req(req, fraglen);
+ break;
+ case SVP_R_BULK_ACK:
+ do_svp_bulk_ack(req, fraglen);
+ break;
+ case SVP_R_LOG_REQ:
+ do_svp_log_req(req, fraglen);
+ break;
+ case SVP_R_LOG_ACK:
+ do_svp_log_ack(req, fraglen);
+ break;
+ case SVP_R_LOG_RM:
+ do_svp_lrm_req(req, fraglen);
+ break;
+ case SVP_R_LOG_RM_ACK:
+ do_svp_lrm_ack(req, fraglen);
+ break;
+ case SVP_R_SHOOTDOWN:
+ do_svp_shootdown(req, fraglen);
+ break;
+ }
+
+ show_space();
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/column/Makefile b/usr/src/cmd/column/Makefile
new file mode 100644
index 0000000000..b9b395b384
--- /dev/null
+++ b/usr/src/cmd/column/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 2018 Joyent, Inc.
+#
+
+PROG=column
+OBJS=column.o
+
+include ../Makefile.cmd
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(OBJS)
+
+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..5c76cb8751
--- /dev/null
+++ b/usr/src/cmd/column/column.c
@@ -0,0 +1,329 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+#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/connstat/connstat_tcp.c b/usr/src/cmd/connstat/connstat_tcp.c
index 4cd20c9b09..bcf2c2ef5b 100644
--- a/usr/src/cmd/connstat/connstat_tcp.c
+++ b/usr/src/cmd/connstat/connstat_tcp.c
@@ -14,6 +14,7 @@
*/
/*
* Copyright (c) 2015, 2016 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#include <stdio.h>
@@ -77,6 +78,7 @@ typedef struct tcp_fields_buf_s {
uint64_t t_rtt_sum;
int t_state;
uint64_t t_rtt;
+ uint64_t t_rttvar;
} tcp_fields_buf_t;
static boolean_t print_tcp_state(ofmt_arg_t *, char *, uint_t);
@@ -126,6 +128,8 @@ static ofmt_field_t tcp_fields[] = {
offsetof(tcp_fields_buf_t, t_rtt_sum), print_uint64 },
{ "RTTC", 11,
offsetof(tcp_fields_buf_t, t_rtt_cnt), print_uint32 },
+ { "RTTVAR", 8,
+ offsetof(tcp_fields_buf_t, t_rttvar), print_uint64 },
{ "STATE", 12,
offsetof(tcp_fields_buf_t, t_state), print_tcp_state },
{ NULL, 0, 0, NULL}
@@ -189,6 +193,7 @@ tcp_ci2buf(struct tcpConnEntryInfo_s *ci)
fields_buf.t_rtt = (ci->ce_out_data_segs == 0 ? 0 : ci->ce_rtt_sa);
fields_buf.t_rtt_sum = ci->ce_rtt_sum;
fields_buf.t_rtt_cnt = ci->ce_rtt_cnt;
+ fields_buf.t_rttvar = ci->ce_rtt_sd;
fields_buf.t_state = ci->ce_state;
}
diff --git a/usr/src/cmd/coreadm/coreadm.c b/usr/src/cmd/coreadm/coreadm.c
index ca7edc179e..23916a7c06 100644
--- a/usr/src/cmd/coreadm/coreadm.c
+++ b/usr/src/cmd/coreadm/coreadm.c
@@ -244,6 +244,12 @@ main(int argc, char **argv)
command);
usage();
}
+ if (glob_pattern != NULL && glob_pattern[0] != '/') {
+ (void) fprintf(stderr, gettext(
+ "%s: The -g option must specify an absolute path\n"),
+ command);
+ usage();
+ }
if ((proc_pattern != NULL || proc_content != CC_CONTENT_INVALID) &&
npids == 0) {
(void) sprintf(curpid, "%u", (uint_t)getppid());
diff --git a/usr/src/cmd/coreadm/coreadm.xml b/usr/src/cmd/coreadm/coreadm.xml
index d367ddbbb8..fbe621f6af 100644
--- a/usr/src/cmd/coreadm/coreadm.xml
+++ b/usr/src/cmd/coreadm/coreadm.xml
@@ -92,17 +92,17 @@
value='solaris.smf.value.coreadm' />
<propval name='global_pattern'
- type='astring' value='' />
+ type='astring' value='/%Z/cores/core.%f.%p' />
<propval name='global_content'
type='astring' value='default' />
<propval name='init_pattern'
- type='astring' value='core' />
+ type='astring' value='/%Z/cores/core.%f.%p' />
<propval name='init_content'
type='astring' value='default' />
<propval name='global_enabled'
- type='boolean' value='false' />
- <propval name='process_enabled'
type='boolean' value='true' />
+ <propval name='process_enabled'
+ type='boolean' value='false' />
<propval name='global_setid_enabled'
type='boolean' value='false' />
<propval name='process_setid_enabled'
diff --git a/usr/src/cmd/cron/Makefile b/usr/src/cmd/cron/Makefile
index 9a555562d8..405b7d5f61 100644
--- a/usr/src/cmd/cron/Makefile
+++ b/usr/src/cmd/cron/Makefile
@@ -28,6 +28,7 @@
DEFAULTFILES = cron.dfl
include ../Makefile.cmd
+include ../Makefile.ctf
MANIFEST = cron.xml
@@ -47,6 +48,7 @@ ROOTVAR = $(ROOT)/var
ROOTSPCRON = $(ROOTVAR)/spool/cron
ROOTCROND = $(ROOTETC)/cron.d
+ROOTCRONDCRONTABS = $(ROOTCROND)/crontabs
ROOTCRONTABS = $(ROOTSPCRON)/crontabs
ROOTATJOBS = $(ROOTSPCRON)/atjobs
ROOTLIBCRON = $(ROOTLIB)/cron
@@ -66,9 +68,6 @@ POFILES1= at.po crontab.po funcs.po batch.po
POFILES= $(POFILES1) atrm.po
$(POFILES1) := XGETFLAGS= -a -x $(PROG1).xcl
-ROOTDIRS = $(ROOTSPCRON) $(ROOTCROND) \
- $(ROOTCRONTABS) $(ROOTATJOBS)
-
ROOTPROG = $(PROG1:%=$(ROOTUSRSBIN)/%) $(PROG2:%=$(ROOTBIN)/%) \
$(SCRIPT:%=$(ROOTBIN)/%) \
$(XPG6PROG:%=$(ROOTXPG6BIN)/%) \
@@ -102,21 +101,21 @@ 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)
-parsetest := POBJS = $(PARSETESTOBJS)
+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)
+parsetest := OBJS = $(PARSETESTOBJS)
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)
@@ -159,34 +158,37 @@ all : $(PROG) $(XPG4) $(XPG6) $(SCRIPT) $(XPG4SCRIPT) $(FILES) \
$(PARSETEST)
install : all $(ROOTPROG) $(ROOTETCDEFAULTFILES) $(ROOTSYMLINK) \
- $(ROOTMANIFEST) $(ROOTMETHOD)
+ $(ROOTMANIFEST) $(ROOTMETHOD) $(ROOTCRONDCRONTABS)/root
-$(PROG) $(PARSETEST): $$(POBJS)
- $(LINK.c) $(POBJS) -o $@ $(LDLIBS)
+$(PROG) $(PARSETEST): $$(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/crt/common/values-xpg4.c
$(COMPILE.c) -o $@ ../../lib/crt/common/values-xpg4.c
+ $(POST_PROCESS_O)
%.o: $(SRC)/common/util/%.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
@@ -210,20 +212,21 @@ att2.c : att2.l att2.ed att1.c
ed - lex.yy.c < att2.ed
$(MV) lex.yy.c att2.c
-# Don't re-install directories installed by Targetdirs
-#$(ROOTDIRS):
-# $(INS.dir)
-
$(ROOTSYMLINK) :
$(RM) $@; $(SYMLINK) $(SYMLNKDEST) $@
+$(ROOTCRONDCRONTABS)/root: crontab.root
+ $(RM) $@; \
+ $(INS) -s -m $(FILEMODE) -f $(@D) crontab.root; \
+ $(MV) $(@D)/crontab.root $@
+
check: $(CHKMANIFEST)
$(POFILE): $(POFILES)
$(RM) $@; cat $(POFILES) > $@
clean :
- $(RM) $(OBJS) att1.h att1.c att2.c
+ $(RM) $(COBJS) att1.h att1.c att2.c
strip :
$(STRIP) $(PROG) $(XPG4) $(XPG6)
diff --git a/usr/src/cmd/cron/cron.c b/usr/src/cmd/cron/cron.c
index 1be2e71968..26abd30d18 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
* Copyright (c) 2016 by Delphix. All rights reserved.
* Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
@@ -313,7 +313,8 @@ static struct usr *find_usr(char *);
static int ex(struct event *e);
static void read_dirs(int);
static void mail(char *, char *, 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 *);
@@ -418,7 +419,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);
@@ -757,6 +758,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;
@@ -765,7 +778,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);
@@ -851,23 +864,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);
}
}
@@ -948,7 +956,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;
@@ -971,7 +979,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
@@ -980,7 +988,7 @@ mod_ctab(char *name, time_t reftime)
#endif
rm_ctevents(u);
el_remove(u->ctid, 0);
- readcron(u, reftime);
+ readcron(CRONDIR, u, reftime);
}
}
@@ -1114,8 +1122,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
@@ -1123,12 +1217,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 */
@@ -1305,7 +1383,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);
@@ -2411,6 +2488,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 b2e241f202..743fde1d24 100644
--- a/usr/src/cmd/cron/cron.h
+++ b/usr/src/cmd/cron/cron.h
@@ -76,6 +76,10 @@ struct message {
char logname[LLEN];
};
+/* anything below here can be changed */
+
+#define SYSCRONDIR "/etc/cron.d/crontabs"
+
/*
* Errors from the crontab field parser.
*/
diff --git a/usr/src/cmd/cron/crontab.c b/usr/src/cmd/cron/crontab.c
index 4ff985240e..fad9131ad0 100644
--- a/usr/src/cmd/cron/crontab.c
+++ b/usr/src/cmd/cron/crontab.c
@@ -74,8 +74,8 @@
#define BADUSAGE \
"usage:\n" \
"\tcrontab [-u username] [file]\n" \
- "\tcrontab [-u username] { -e | -l | -r }\n" \
- "\tcrontab { -e | -l | -r } [username]"
+ "\tcrontab [-u username] { -e | -g | -l | -r }\n" \
+ "\tcrontab { -e | -g | -l | -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."
#define NOTROOT \
@@ -124,6 +124,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;
@@ -156,11 +157,14 @@ main(int argc, char **argv)
exit(1);
}
- while ((c = getopt(argc, argv, "elru:")) != EOF) {
+ while ((c = getopt(argc, argv, "eglru:")) != EOF) {
switch (c) {
case 'e':
eflag++;
break;
+ case 'g':
+ gflag++;
+ break;
case 'l':
lflag++;
break;
@@ -182,6 +186,9 @@ main(int argc, char **argv)
if (eflag + lflag + rflag > 1)
errflg++;
+ if (gflag && !lflag)
+ errflg++;
+
if ((eflag || lflag || rflag) && argc > 0) {
if (user != NULL)
errflg++;
@@ -255,12 +262,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/cron/crontab.root b/usr/src/cmd/cron/crontab.root
new file mode 100644
index 0000000000..4153ad2d6a
--- /dev/null
+++ b/usr/src/cmd/cron/crontab.root
@@ -0,0 +1,18 @@
+## Rotate and trim the audit logs nightly.
+0 0 * * * /smartdc/bin/sdc-lastcomm -R 30
+## Run logadm hourly so that the minimum rotate interval is 1h
+0 * * * * /usr/sbin/logadm
+## Rotate vm.log files for any KVM VMs
+0 * * * * SDC_LOG_ROLL_BACKWARD=1 /usr/vm/sbin/rotate-kvm-logs.sh >> /var/log/rotate-kvm-logs.log 2>&1
+## Headnode should phone home nightly.
+0 1 * * * [ -x /opt/smartdc/bin/sdc-phonehome ] && /opt/smartdc/bin/sdc-phonehome
+## Delete saved core dumps over 7 days old
+15 0 * * * find /zones/*/cores -type f -mtime +7 -exec rm -f "{}" \;
+## Delete archived zone data over 7 days old
+30 0 * * * find /zones/archive/ -mount -maxdepth 1 -mindepth 1 -type d -mtime +7 -exec rm -rf "{}" \;
+## Delete logs to be uploaded over 7 days old if they aren't being consumed
+45 0 * * * [ -d /var/log/sdc/upload/ ] && find /var/log/sdc/upload/ -mount -maxdepth 1 -mindepth 1 -type f -mtime +7 -exec rm -f "{}" \;
+
+# NOTE: all entries above are in root's system-defined crontab; see the
+# crontab(1) man page.
+
diff --git a/usr/src/cmd/cron/svc-cron b/usr/src/cmd/cron/svc-cron
index 4def6071b8..55033608ee 100644
--- a/usr/src/cmd/cron/svc-cron
+++ b/usr/src/cmd/cron/svc-cron
@@ -22,8 +22,7 @@
#
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright 2012 Joyent, Inc. All rights reserved.
#
# Start method script for the cron service.
#
@@ -37,6 +36,45 @@ if [ -p /etc/cron.d/FIFO ]; then
fi
fi
+if smf_is_globalzone && [ -n "$(bootparams | grep '^headnode=true')" ]; then
+ #
+ # Randomize root's crontab so all HN's are not in sync.
+ #
+ utc_offset=`nawk -F= '{if ($1 == "utc_offset") print $2}' \
+ /usbkey/config.inc/generic`
+ [ -z "$utc_offset" ] && utc_offset=0
+
+ n=$(date +%S)
+ hr=$(($n % 5))
+ hr=$(($hr + $utc_offset))
+
+ n=$(date +%M)
+ mn=$(($n % 15))
+
+ nawk -v hr=$hr -v mn=$mn '{
+ if (substr($1, 1, 1) == "#") {
+ print $0
+ next
+ }
+ if (substr($2, 1, 1) == "*") {
+ print $0
+ next
+ }
+ if (length($0) == 0) {
+ print $0
+ next
+ }
+
+ printf("%d %d %s %s %s %s", mn, hr, $3, $4, $5, $6)
+ for (i = 7; i <= NF; i++)
+ printf(" %s", $i)
+ printf("\n")
+ mn += 10
+ }' /etc/cron.d/crontabs/root >/etc/cron.d/crontabs/root.$$
+ cp /etc/cron.d/crontabs/root.$$ /etc/cron.d/crontabs/root
+ rm -f /etc/cron.d/crontabs/root.$$
+fi
+
if [ -x /usr/sbin/cron ]; then
/usr/bin/rm -f /etc/cron.d/FIFO
/usr/sbin/cron &
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..b99a8361a0
--- /dev/null
+++ b/usr/src/cmd/devfsadm/i386/lx_link_i386.c
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <devfsadm.h>
+#include <strings.h>
+#include <stdio.h>
+#include <sys/lx_ptm.h>
+#include <sys/lx_autofs.h>
+
+static int lx_ptm(di_minor_t minor, di_node_t node);
+static int lx_autofs(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_AUTOFS_NAME,
+ TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_autofs },
+ { "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_autofs(di_minor_t minor, di_node_t node)
+{
+ char *mname = di_minor_name(minor);
+
+ if (strcmp(LX_AUTOFS_MINORNAME, mname) == 0)
+ (void) devfsadm_mklink("brand/lx/autofs", 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 e5b21a5957..e90ce22330 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) 2015, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2022 Garrett D'Amore <garrett@damore.org>
* Copyright 2022 Oxide Computer Company
*/
@@ -34,6 +34,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);
@@ -55,6 +56,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);
@@ -117,12 +119,15 @@ static devfsadm_create_t misc_cbt[] = {
"(^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
},
{ "pseudo", "ddi_pseudo",
"(^ipf$)|(^ipnat$)|(^ipstate$)|(^ipauth$)|"
- "(^ipsync$)|(^ipscan$)|(^iplookup$)",
+ "(^ipsync$)|(^ipscan$)|(^iplookup$)|(^ipfev$)",
TYPE_EXACT | DRV_RE, ILEVEL_0, minor_name,
},
{ "pseudo", "ddi_pseudo", "dld",
@@ -183,6 +188,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,
},
@@ -237,6 +245,9 @@ static devfsadm_remove_t misc_remove_cbt[] = {
ZCONS_SUBSIDIARY_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
},
@@ -707,6 +718,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/dispadmin/Makefile b/usr/src/cmd/dispadmin/Makefile
index 131b8567f3..93e4ab653d 100644
--- a/usr/src/cmd/dispadmin/Makefile
+++ b/usr/src/cmd/dispadmin/Makefile
@@ -22,12 +22,14 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# cmd/dispadmin/Makefile
+# Copyright 2019 Joyent, Inc.
#
PROG= dispadmin
MANIFEST= scheduler.xml
SVCMETHOD= svc-scheduler
+ETCFILES= dispadmin.conf
+
SDC= SDC$(PROG)
RT= RT$(PROG)
TS= TS$(PROG)
@@ -36,6 +38,8 @@ FSS= FSS$(PROG)
FX= FX$(PROG)
PROGS= $(PROG) $(RT) $(TS) $(IA) $(FSS) $(FX) $(SDC)
+ROOTETCFILES= $(ETCFILES:%=$(ROOTETC)/%)
+
include ../Makefile.cmd
CFLAGS += $(CCVERBOSE)
@@ -59,6 +63,8 @@ ROOTSDC= $(SDC:%=$(ROOTDIR)/SDC/%)
ROOTTS= $(TS:%=$(ROOTDIR)/TS/%)
ROOTMANIFESTDIR= $(ROOTSVCSYSTEM)
+$(ROOTETCFILES) := FILEMODE = 644
+
# this would be simpler if we renamed rtdispadmin.c and tsdispadmin.c
OBJECTS= $(PROG).o rt$(PROG).o ts$(PROG).o ia$(PROG).o \
fss$(PROG).o fx$(PROG).o sdc$(PROG).o subr.o
@@ -83,20 +89,15 @@ $(ROOTDIR)/TS/% : %
$(INS.file)
.KEEP_STATE:
-
-all: $(PROGS)
+
+all: $(PROGS)
$(PROGS): $$(OBJ) subr.o
$(LINK.c) -o $@ $(OBJ) subr.o $(LDLIBS)
$(POST_PROCESS)
-llib-lsubr.ln: subr.c
- $(LINT.c) -y -o subr subr.c
-
-lint := LDLIBS += -L. -lsubr
-
install: all $(ROOTPROG) $(ROOTRT) $(ROOTTS) $(ROOTIA) $(ROOTFSS) $(ROOTFX) \
- $(ROOTSDC) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
+ $(ROOTSDC) $(ROOTMANIFEST) $(ROOTSVCMETHOD) $(ROOTETCFILES)
# Don't re-install directories already installed by Targetdirs
#$(ROOTDIRS):
@@ -105,15 +106,6 @@ install: all $(ROOTPROG) $(ROOTRT) $(ROOTTS) $(ROOTIA) $(ROOTFSS) $(ROOTFX) \
check: $(CHKMANIFEST)
clean:
- $(RM) $(OBJECTS) $(PROGS) llib-lsubr.ln
-
-lint: llib-lsubr.ln
- $(LINT.c) dispadmin.c $(LDLIBS)
- $(LINT.c) fssdispadmin.c $(LDLIBS)
- $(LINT.c) fxdispadmin.c $(LDLIBS)
- $(LINT.c) iadispadmin.c $(LDLIBS)
- $(LINT.c) rtdispadmin.c $(LDLIBS)
- $(LINT.c) sdcdispadmin.c $(LDLIBS)
- $(LINT.c) tsdispadmin.c $(LDLIBS)
-
+ $(RM) $(OBJECTS) $(PROGS)
+
include ../Makefile.targ
diff --git a/usr/src/cmd/dispadmin/dispadmin.conf b/usr/src/cmd/dispadmin/dispadmin.conf
new file mode 100644
index 0000000000..7970647c62
--- /dev/null
+++ b/usr/src/cmd/dispadmin/dispadmin.conf
@@ -0,0 +1 @@
+DEFAULT_SCHEDULER=FSS
diff --git a/usr/src/cmd/dladm/Makefile b/usr/src/cmd/dladm/Makefile
index bba8a8cede..0b4a2413c2 100644
--- a/usr/src/cmd/dladm/Makefile
+++ b/usr/src/cmd/dladm/Makefile
@@ -20,9 +20,8 @@
#
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-# Copyright 2015 Joyent, Inc.
+# Copyright 2018 Joyent, Inc.
#
-# Copyright (c) 2018, Joyent, Inc.
PROG= dladm
ROOTFS_PROG= $(PROG)
diff --git a/usr/src/cmd/dladm/dladm.c b/usr/src/cmd/dladm/dladm.c
index f44a0083d4..dac9006a22 100644
--- a/usr/src/cmd/dladm/dladm.c
+++ b/usr/src/cmd/dladm/dladm.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2017 Joyent, Inc.
* Copyright 2016 Nexenta Systems, Inc.
* Copyright (c) 2015 Joyent, Inc. All rights reserved.
* Copyright 2020 Peter Tribble.
@@ -162,6 +163,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 {
@@ -285,7 +287,7 @@ typedef struct cmd {
static cmd_t cmds[] = {
{ "help", do_help, NULL },
{ "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" },
@@ -320,12 +322,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,
@@ -367,10 +370,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"
@@ -991,6 +994,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;
@@ -1043,21 +1047,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", 9,
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}}
;
@@ -2628,13 +2635,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;
@@ -2650,7 +2661,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");
}
@@ -3542,11 +3553,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, ":pPsi:o:",
+ while ((option = getopt_long(argc, argv, ":pPsi:o:z:",
show_lopts, NULL)) != -1) {
switch (option) {
case 'p':
@@ -3579,6 +3591,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;
@@ -3598,8 +3613,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);
}
@@ -4868,6 +4883,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();
@@ -4960,9 +4981,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':
@@ -4971,6 +4993,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);
}
@@ -4983,8 +5008,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]);
@@ -5116,6 +5141,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)
@@ -5145,6 +5173,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 */
@@ -5220,6 +5260,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);
@@ -5258,10 +5305,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':
@@ -5300,6 +5348,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);
}
@@ -5310,8 +5361,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]);
@@ -5322,8 +5373,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);
@@ -5335,6 +5386,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)
@@ -6823,6 +6875,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;
@@ -6833,7 +6886,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':
@@ -6852,6 +6905,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;
@@ -6859,8 +6915,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) {
@@ -6871,6 +6927,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)
@@ -6915,6 +6972,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;
@@ -6997,11 +7065,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':
@@ -7016,6 +7085,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);
@@ -7038,8 +7110,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]);
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_db.c b/usr/src/cmd/dlmgmtd/dlmgmt_db.c
index 99307dbc03..6ccd9d97b8 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 2016 Joyent, Inc.
*/
#include <assert.h>
@@ -43,6 +44,7 @@
#include <libcontract.h>
#include <libcontract_priv.h>
#include <sys/contract/process.h>
+#include <zone.h>
#include "dlmgmt_impl.h"
typedef enum dlmgmt_db_op {
@@ -552,6 +554,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 +720,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 +776,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 +834,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 +1255,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 +1422,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) {
/*
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_door.c b/usr/src/cmd/dlmgmtd/dlmgmt_door.c
index 11e4329669..60fa361caa 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 2019 Joyent, Inc.
*/
/*
@@ -58,6 +59,7 @@
#include <libsysevent.h>
#include <libdlmgmt.h>
#include <librcm.h>
+#include <unistd.h>
#include "dlmgmt_impl.h"
typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t,
@@ -423,8 +425,11 @@ dlmgmt_getname(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
retvalp->lr_flags = linkp->ll_flags;
retvalp->lr_class = linkp->ll_class;
retvalp->lr_media = linkp->ll_media;
+ retvalp->lr_flags |= (linkp->ll_trans == B_TRUE) ?
+ DLMGMT_TRANSIENT : 0;
}
+
dlmgmt_table_unlock();
retvalp->lr_err = err;
}
@@ -439,6 +444,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 +657,7 @@ dlmgmt_remapid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
goto done;
+
if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) {
err = EEXIST;
goto done;
@@ -1232,8 +1242,7 @@ dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
* Before we remove the link from its current zone, make sure that
* there isn't a link with the same name in the destination zone.
*/
- if (zoneid != GLOBAL_ZONEID &&
- link_by_name(linkp->ll_link, newzoneid) != NULL) {
+ if (link_by_name(linkp->ll_link, newzoneid) != NULL) {
err = EEXIST;
goto done;
}
@@ -1245,9 +1254,10 @@ 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);
+
linkp->ll_onloan = B_FALSE;
}
+
if (newzoneid != GLOBAL_ZONEID) {
if (zone_add_datalink(newzoneid, linkid) != 0) {
err = errno;
@@ -1256,7 +1266,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 +1318,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] == '\0')
+ (void) snprintf(my_pid, sizeof (my_pid), "%d\n", getpid());
if ((err = dlmgmt_checkprivs(0, cred)) == 0) {
if (zoneid != GLOBAL_ZONEID) {
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_impl.h b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h
index cdfd0d8a4d..c65a0438d6 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 2017 Joyent, Inc.
*/
/*
@@ -60,13 +61,24 @@ typedef struct dlmgmt_link_s {
datalink_class_t ll_class;
uint32_t ll_media;
datalink_id_t ll_linkid;
+
+ /*
+ * The zone that owns the link. If this is set to the id of
+ * an NGZ and ll_onloan is set then the link was created and
+ * is owned by the GZ but is currently being loaned out to an
+ * NGZ. E.g., when the GZ admin creates a VNIC for exclusive
+ * use by an NGZ. If ll_onloan is set then ll_zoneid cannot be 0.
+ *
+ * If ll_zoneid is set to the id of an NGZ but ll_onloan is
+ * not set then the link was created and is owned by the NGZ.
+ */
zoneid_t ll_zoneid;
boolean_t ll_onloan;
avl_node_t ll_name_node;
avl_node_t ll_id_node;
- avl_node_t ll_loan_node;
uint32_t ll_flags;
uint32_t ll_gen; /* generation number */
+ boolean_t ll_trans; /* transient link */
} dlmgmt_link_t;
/*
@@ -91,7 +103,6 @@ extern dladm_handle_t dld_handle;
extern datalink_id_t dlmgmt_nextlinkid;
extern avl_tree_t dlmgmt_name_avl;
extern avl_tree_t dlmgmt_id_avl;
-extern avl_tree_t dlmgmt_loan_avl;
extern avl_tree_t dlmgmt_dlconf_avl;
boolean_t linkattr_equal(dlmgmt_linkattr_t **, const char *, void *,
@@ -138,7 +149,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 658078e624..43f3f0eeec 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 2016 Joyent, Inc.
*/
/*
@@ -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
diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_util.c b/usr/src/cmd/dlmgmtd/dlmgmt_util.c
index afcfbed37b..c8ba0009a0 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 2017 Joyent, Inc.
*/
/*
@@ -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);
}
@@ -359,9 +354,9 @@ link_destroy(dlmgmt_link_t *linkp)
}
/*
- * Set the DLMGMT_ACTIVE flag on the link to note that it is active. When a
- * link becomes active and it belongs to a non-global zone, it is also added
- * to that zone.
+ * Set the DLMGMT_ACTIVE flag on the link to note that it is active.
+ * When a link is active and is owned by an NGZ then it is added to
+ * that zone's datalink list.
*/
int
link_activate(dlmgmt_link_t *linkp)
@@ -369,27 +364,75 @@ link_activate(dlmgmt_link_t *linkp)
int err = 0;
zoneid_t zoneid = ALL_ZONES;
+ /*
+ * If zone_check_datalink() returns 0 it means we found the
+ * link in one of the NGZ's datalink lists. Otherwise the link
+ * is under the GZ.
+ */
if (zone_check_datalink(&zoneid, linkp->ll_linkid) == 0) {
/*
- * This link was already added to a non-global zone. This can
- * happen if dlmgmtd is restarted.
+ * This is a bit subtle. If the following expression
+ * is true then the link was found in one of the NGZ's
+ * datalink lists but the link structure has it under
+ * the GZ. This means that the link is supposed to be
+ * loaned out to an NGZ but the dlmgmtd state is out
+ * of sync -- possibly due to the process restarting.
+ * In this case we need to sync the dlmgmtd state by
+ * marking it as on-loan to the NGZ it's currently
+ * under.
*/
if (zoneid != linkp->ll_zoneid) {
+ assert(linkp->ll_zoneid == 0);
+ assert(linkp->ll_onloan == B_FALSE);
+
+ /*
+ * If dlmgmtd already has a link with this
+ * name under the NGZ then we have a problem.
+ */
if (link_by_name(linkp->ll_link, zoneid) != NULL) {
err = EEXIST;
goto done;
}
+ /*
+ * Remove the current linkp entry from the
+ * list because it's under the wrong zoneid.
+ * We don't have to update the dlmgmt_id_avl
+ * because it compares entries by ll_linkid
+ * only.
+ */
if (avl_find(&dlmgmt_name_avl, linkp, NULL) != NULL)
avl_remove(&dlmgmt_name_avl, linkp);
+ /*
+ * Update the link to reflect the fact that
+ * it's on-loan to an NGZ and re-add it to the
+ * list.
+ */
linkp->ll_zoneid = zoneid;
avl_add(&dlmgmt_name_avl, linkp);
- avl_add(&dlmgmt_loan_avl, linkp);
linkp->ll_onloan = B_TRUE;
+
+ /*
+ * When a VNIC is not persistent and loaned to
+ * a zone it is considered transient. This is
+ * the same logic found in do_create_vnic()
+ * and is needed here in the event of a
+ * dlmgmtd restart.
+ */
+ if (linkp->ll_class == DATALINK_CLASS_VNIC &&
+ !(linkp->ll_flags & DLMGMT_PERSIST))
+ linkp->ll_trans = B_TRUE;
}
} else if (linkp->ll_zoneid != GLOBAL_ZONEID) {
+ /*
+ * In this case the link was not found under any NGZs
+ * but according to its ll_zoneid member it is owned
+ * by an NGZ. Add the datalink to the appropriate zone
+ * datalink list.
+ */
err = zone_add_datalink(linkp->ll_zoneid, linkp->ll_linkid);
+ assert(linkp->ll_onloan == B_FALSE);
}
done:
if (err == 0)
@@ -430,10 +473,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);
}
@@ -449,6 +488,10 @@ dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media,
return (EINVAL);
if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID)
return (ENOSPC);
+ if (flags & ~(DLMGMT_ACTIVE | DLMGMT_PERSIST | DLMGMT_TRANSIENT) ||
+ ((flags & DLMGMT_PERSIST) && (flags & DLMGMT_TRANSIENT)) ||
+ flags == 0)
+ return (EINVAL);
if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) {
err = ENOMEM;
@@ -462,6 +505,15 @@ dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media,
linkp->ll_zoneid = zoneid;
linkp->ll_gen = 0;
+ /*
+ * While DLMGMT_TRANSIENT starts off as a flag it is converted
+ * into a link field since it is really a substate of
+ * DLMGMT_ACTIVE -- it should not survive as a flag beyond
+ * this point.
+ */
+ linkp->ll_trans = (flags & DLMGMT_TRANSIENT) ? B_TRUE : B_FALSE;
+ flags &= ~DLMGMT_TRANSIENT;
+
if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL ||
avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) {
err = EEXIST;
@@ -490,6 +542,12 @@ done:
int
dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags)
{
+ /*
+ * After dlmgmt_create_common() the link flags should only
+ * ever include ACTIVE or PERSIST.
+ */
+ assert((linkp->ll_flags & ~(DLMGMT_ACTIVE | DLMGMT_PERSIST)) == 0);
+
if ((linkp->ll_flags & flags) == 0) {
/*
* The link does not exist in the specified space.
@@ -511,8 +569,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/dlstat/dlstat.c b/usr/src/cmd/dlstat/dlstat.c
index 915c2196d1..d11e623206 100644
--- a/usr/src/cmd/dlstat/dlstat.c
+++ b/usr/src/cmd/dlstat/dlstat.c
@@ -70,7 +70,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 {
@@ -147,6 +147,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[] = {
@@ -160,6 +161,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}};
/*
@@ -963,8 +966,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;
@@ -976,6 +979,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);
@@ -994,8 +998,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;
@@ -1028,8 +1032,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;
@@ -1062,8 +1066,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;
@@ -1096,8 +1100,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;
@@ -1130,8 +1134,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;
@@ -1178,8 +1182,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;
@@ -1223,8 +1227,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;
@@ -1289,8 +1293,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;
@@ -1344,8 +1348,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;
@@ -1398,8 +1402,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;
@@ -1476,7 +1480,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;
@@ -1486,7 +1491,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);
@@ -1501,12 +1507,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]) {
/*
@@ -1514,7 +1528,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);
}
}
@@ -1634,7 +1649,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/demo/Makefile b/usr/src/cmd/dtrace/demo/Makefile
index a75a418a96..cfb35083ee 100644
--- a/usr/src/cmd/dtrace/demo/Makefile
+++ b/usr/src/cmd/dtrace/demo/Makefile
@@ -21,6 +21,8 @@
#
# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
#
+# Copyright (c) 2018, Joyent, Inc.
+#
include ../../Makefile.cmd
@@ -157,11 +159,11 @@ $(ROOTDEMODIR):
$(ROOTDEMODIR)/%: %
$(INS.file)
-$(HTMLFILES): $(DFILES) $(MKDEMO)
- ./$(MKDEMO) $@
+$(HTMLFILES): $(DFILES) $(MKDEMO)
+ $(PERL) ./$(MKDEMO) $@
-$(DFILES): $(MKDEMO)
- ./$(MKDEMO) $@
+$(DFILES): $(MKDEMO)
+ $(PERL) ./$(MKDEMO) $@
$(ROOTDEMOFILES): $(ROOTDEMODIR)
diff --git a/usr/src/cmd/dtrace/test/README b/usr/src/cmd/dtrace/test/README
index 51ab650fd7..094f70e7da 100644
--- a/usr/src/cmd/dtrace/test/README
+++ b/usr/src/cmd/dtrace/test/README
@@ -20,13 +20,10 @@ CDDL HEADER END
Copyright 2006 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
-
-ident "%Z%%M% %I% %E% SMI"
+Copyright 2017 Joyent, Inc.
DTrace Testing Suite
The SUNWdtrt package delivers a set of test programs and D source
-files into the directory /opt/SUNWdtrt. For more information see
-the following web site:
-
- http://www.opensolaris.org/os/community/dtrace/dtest
+files into the directory /opt/SUNWdtrt. To run the tests:
+ /opt/SUNWdtrt/bin/dtest
diff --git a/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile b/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile
index 4d88a584de..a71f59e549 100644
--- a/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile
+++ b/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile
@@ -73,7 +73,7 @@ $(PROG): $(SRCS)
$(POST_PROCESS) ; $(STRIP_STABS)
JFLAGS= -g -cp $(CLASSPATH) -d $(CLASSDIR)
-JFLAGS += -source 7 -target 7 -Xlint:all,-options
+JFLAGS += -source 7 -target 7 -Xlint:all,-options,-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 348e7be086..645a7eb543 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/Makefile.com b/usr/src/cmd/dtrace/test/tst/Makefile.com
index 0d8fb3316c..b7100f4af5 100644
--- a/usr/src/cmd/dtrace/test/tst/Makefile.com
+++ b/usr/src/cmd/dtrace/test/tst/Makefile.com
@@ -113,6 +113,6 @@ scripts: FRC
@cd ../cmd/scripts; pwd; $(MAKE) install
dstyle: FRC
- @if [ -n "$(DSRCS)" ]; then $(DSTYLE) $(DSRCS); fi
+ @if [ -n "$(DSRCS)" ]; then $(PERL) $(DSTYLE) $(DSRCS); fi
FRC:
diff --git a/usr/src/cmd/dtrace/test/tst/common/Makefile b/usr/src/cmd/dtrace/test/tst/common/Makefile
index 6237963c0e..14ba3acd19 100644
--- a/usr/src/cmd/dtrace/test/tst/common/Makefile
+++ b/usr/src/cmd/dtrace/test/tst/common/Makefile
@@ -157,6 +157,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 982e906cf0..f8f6131c44 100644
--- a/usr/src/cmd/dtrace/test/tst/common/java_api/Makefile
+++ b/usr/src/cmd/dtrace/test/tst/common/java_api/Makefile
@@ -58,7 +58,7 @@ all: default
install: all $(PROTO_TEST_JAR)
JFLAGS= -g -cp $(CLASSPATH) -d $(CLASSDIR)
-JFLAGS += -source 7 -target 7 -Xlint:all,-deprecation,-options,-rawtypes
+JFLAGS += -source 7 -target 7 -Xlint:all,-deprecation,-options,-rawtypes,-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/dumpadm/Makefile b/usr/src/cmd/dumpadm/Makefile
index e1303c5d0e..c6b80188fa 100644
--- a/usr/src/cmd/dumpadm/Makefile
+++ b/usr/src/cmd/dumpadm/Makefile
@@ -26,12 +26,12 @@
PROG = dumpadm
MANIFEST = dumpadm.xml
SVCMETHOD= svc-dumpadm
+ETCFILES= dumpadm.conf
OBJS = main.o dconf.o minfree.o utils.o swap.o
SRCS = $(OBJS:.o=.c)
-
-lint := LINTFLAGS = -mx
+ROOTETCFILES= $(ETCFILES:%=$(ROOTETC)/%)
include ../Makefile.cmd
@@ -52,13 +52,11 @@ $(PROG): $(OBJS)
$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
$(POST_PROCESS)
-install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
+install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) $(ROOTETCFILES)
check: $(CHKMANIFEST)
clean:
$(RM) $(OBJS)
-lint: lint_SRCS
-
include ../Makefile.targ
diff --git a/usr/src/cmd/dumpadm/dconf.c b/usr/src/cmd/dumpadm/dconf.c
index f39bdd3df9..b5c86098cf 100644
--- a/usr/src/cmd/dumpadm/dconf.c
+++ b/usr/src/cmd/dumpadm/dconf.c
@@ -28,6 +28,7 @@
#include <sys/stat.h>
#include <sys/swap.h>
#include <sys/dumpadm.h>
+#include <sys/dumphdr.h>
#include <sys/utsname.h>
#include <unistd.h>
@@ -539,6 +540,42 @@ dconf_get_dumpsize(dumpconf_t *dcp)
return (0);
}
+int
+dconf_set_crypt(dumpconf_t *dcp, const char *keyfile)
+{
+ int fd;
+ uint8_t key[DUMP_CRYPT_KEYLEN];
+
+ if ((fd = open(keyfile, O_RDONLY)) == -1) {
+ warn(gettext("failed to open %s"), keyfile);
+ return (-1);
+ }
+
+ if (read(fd, key, sizeof (key)) != sizeof (key)) {
+ warn(gettext("failed to read %d byte key from %s"),
+ DUMP_CRYPT_KEYLEN, keyfile);
+ (void) close(fd);
+ return (-1);
+ }
+
+ (void) close(fd);
+
+ if (ioctl(dcp->dc_dump_fd, DIOCSCRYPTKEY, key) == -1) {
+ warn(gettext("failed to set encryption key"));
+ return (-1);
+ }
+
+ /*
+ * Reload our config flags as they may have changed.
+ */
+ if ((dcp->dc_cflags = ioctl(dcp->dc_dump_fd, DIOCGETCONF, 0)) == -1) {
+ warn(gettext("failed to get kernel dump settings"));
+ return (-1);
+ }
+
+ return (0);
+}
+
void
dconf_print(dumpconf_t *dcp, FILE *fp)
{
@@ -580,6 +617,8 @@ dconf_print(dumpconf_t *dcp, FILE *fp)
(void) fprintf(fp, gettext(" Save compressed: %s\n"),
(dcp->dc_csave == DC_UNCOMPRESSED) ? gettext("off") :
gettext("on"));
+ (void) fprintf(fp, gettext(" Dump encrypted: %s\n"),
+ (dcp->dc_cflags & DUMP_ENCRYPT) ? gettext("yes") : gettext("no"));
}
int
diff --git a/usr/src/cmd/dumpadm/dconf.h b/usr/src/cmd/dumpadm/dconf.h
index 74920f0def..e2f609cee7 100644
--- a/usr/src/cmd/dumpadm/dconf.h
+++ b/usr/src/cmd/dumpadm/dconf.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _DCONF_H
@@ -73,6 +74,7 @@ extern int dconf_update(dumpconf_t *, int);
extern void dconf_print(dumpconf_t *, FILE *);
extern int dconf_write_uuid(dumpconf_t *);
extern int dconf_get_dumpsize(dumpconf_t *);
+extern int dconf_set_crypt(dumpconf_t *, const char *);
extern int dconf_str2device(dumpconf_t *, char *);
extern int dconf_str2savdir(dumpconf_t *, char *);
diff --git a/usr/src/cmd/dumpadm/dumpadm.conf b/usr/src/cmd/dumpadm/dumpadm.conf
new file mode 100644
index 0000000000..804e1da11a
--- /dev/null
+++ b/usr/src/cmd/dumpadm/dumpadm.conf
@@ -0,0 +1,11 @@
+#
+# dumpadm.conf
+#
+# Configuration parameters for system crash dump.
+# Do NOT edit this file by hand -- use dumpadm(1m) instead.
+#
+DUMPADM_DEVICE=/dev/zvol/dsk/zones/dump
+DUMPADM_SAVDIR=/var/crash/volatile
+DUMPADM_CONTENT=kernel
+DUMPADM_ENABLE=no
+DUMPADM_CSAVE=on
diff --git a/usr/src/cmd/dumpadm/main.c b/usr/src/cmd/dumpadm/main.c
index 07a7dd5207..dccafbba33 100644
--- a/usr/src/cmd/dumpadm/main.c
+++ b/usr/src/cmd/dumpadm/main.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#include <sys/stat.h>
@@ -36,10 +37,10 @@
static const char USAGE[] = "\
Usage: %s [-enuy] [-c kernel | curproc | all ]\n\
- [-d dump-device | swap | none ] [-m min {k|m|%%} ] [-s savecore-dir]\n\
- [-r root-dir] [-z on|off]\n";
+ [-d dump-device | swap | none ] [-k key-file] [-m min {k|m|%%} ]\n\
+ [-s savecore-dir] [-r root-dir] [-z on|off]\n";
-static const char OPTS[] = "einuyc:d:m:s:r:z:";
+static const char OPTS[] = "einuyc:d:m:s:r:z:k:";
static const char PATH_DEVICE[] = "/dev/dump";
static const char PATH_CONFIG[] = "/etc/dumpadm.conf";
@@ -57,6 +58,7 @@ main(int argc, char *argv[])
int dcmode = DC_CURRENT; /* kernel settings override unless -u */
int modified = 0; /* have we modified the dump config? */
char *minfstr = NULL; /* string value of -m argument */
+ char *keyfile = NULL; /* key file for -k argument */
dumpconf_t dc; /* current configuration */
int chrooted = 0;
int douuid = 0;
@@ -136,6 +138,9 @@ main(int argc, char *argv[])
}
douuid++;
break;
+ case 'k':
+ keyfile = optarg;
+ break;
case 'm':
minfstr = optarg;
@@ -191,6 +196,9 @@ main(int argc, char *argv[])
return (E_ERROR);
}
+ if (keyfile != NULL && dconf_set_crypt(&dc, keyfile) == -1)
+ return (E_ERROR);
+
if (dcmode == DC_OVERRIDE) {
/*
* In override mode, we try to force an update. If this
diff --git a/usr/src/cmd/dumpadm/svc-dumpadm b/usr/src/cmd/dumpadm/svc-dumpadm
index 4ec5a5d794..bf574df0cb 100644
--- a/usr/src/cmd/dumpadm/svc-dumpadm
+++ b/usr/src/cmd/dumpadm/svc-dumpadm
@@ -21,6 +21,7 @@
#
#
# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2020 Joyent, Inc.
#
. /lib/svc/share/smf_include.sh
@@ -100,7 +101,7 @@ fi
# how to modify the dump settings.
#
if [ -x /usr/sbin/dumpadm ]; then
- /usr/sbin/dumpadm -u || $SMF_EXIT_ERR_CONFIG
+ /usr/sbin/dumpadm -u || exit $SMF_EXIT_ERR_CONFIG
else
echo "WARNING: /usr/sbin/dumpadm is missing or not executable" >& 2
exit $SMF_EXIT_ERR_CONFIG
@@ -113,6 +114,11 @@ else
exit $SMF_EXIT_ERR_CONFIG
fi
+if [[ -f $DUMPADM_SAVDIR/keyfile ]]; then
+ /usr/sbin/dumpadm -k $DUMPADM_SAVDIR/keyfile || \
+ exit $SMT_EXIT_ERR_CONFIG
+fi
+
#
# If the savecore executable is absent then we're done
#
@@ -147,11 +153,15 @@ if [ "x$DUMPADM_ENABLE" != xno ]; then
mksavedir && /usr/bin/savecore $DUMPADM_SAVDIR &
fi
else
+ keyarg=""
+ [[ -f "$DUMPADM_SAVDIR/keyfile" ]] && \
+ keyarg="-k $DUMPADM_SAVDIR/keyfile"
+
#
# The dump device couldn't have been dedicated before we
# ran dumpadm, so we must execute savecore again.
#
- mksavedir && /usr/bin/savecore $DUMPADM_SAVDIR &
+ mksavedir && /usr/bin/savecore $keyarg $DUMPADM_SAVDIR &
fi
else
#
diff --git a/usr/src/cmd/flowadm/flowadm.c b/usr/src/cmd/flowadm/flowadm.c
index 058c1e03d8..a1f1c7387e 100644
--- a/usr/src/cmd/flowadm/flowadm.c
+++ b/usr/src/cmd/flowadm/flowadm.c
@@ -236,9 +236,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"
@@ -336,11 +336,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':
@@ -354,9 +355,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':
@@ -371,6 +369,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);
}
@@ -379,6 +380,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;
@@ -417,11 +422,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':
@@ -435,12 +441,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;
@@ -461,6 +466,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;
@@ -600,11 +611,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':
@@ -625,17 +637,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 23e973c8a2..a0e2b85f32 100644
--- a/usr/src/cmd/flowstat/flowstat.c
+++ b/usr/src/cmd/flowstat/flowstat.c
@@ -201,9 +201,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 [-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> "
@@ -566,6 +566,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";
@@ -592,10 +593,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, ":rtApi:o:u:l:h",
+ while ((option = getopt_long(argc, argv, ":rtApi:o:u:l:hz:",
NULL, NULL)) != -1) {
switch (option) {
case 'r':
@@ -647,9 +649,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 ||
@@ -660,6 +659,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 +685,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/fm/fmdump/common/nvlrender.c b/usr/src/cmd/fm/fmdump/common/nvlrender.c
index 2c2f5ca662..99f027a77d 100644
--- a/usr/src/cmd/fm/fmdump/common/nvlrender.c
+++ b/usr/src/cmd/fm/fmdump/common/nvlrender.c
@@ -83,7 +83,7 @@ fmdump_render_nvlist(nvlist_prtctl_t pctl, void *private, nvlist_t *nvl,
int
fmdump_print_json(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp)
{
- if (nvlist_print_json(fp, rp->rec_nvl) != 0 || fprintf(fp, "\n") < 0 ||
+ if (nvlist_print_json(fp, rp->rec_nvl) < 0 || fprintf(fp, "\n") < 0 ||
fflush(fp) != 0)
return (-1);
diff --git a/usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c b/usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c
index fa46bc51b3..20f5820cf1 100644
--- a/usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c
+++ b/usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
/*
* Copyright 2019 Joyent, Inc.
diff --git a/usr/src/cmd/fs.d/Makefile b/usr/src/cmd/fs.d/Makefile
index 8562ed1ed0..91507ef2ed 100644
--- a/usr/src/cmd/fs.d/Makefile
+++ b/usr/src/cmd/fs.d/Makefile
@@ -40,8 +40,8 @@ DEFAULTFILES= fs.dfl
include ../Makefile.cmd
-SUBDIR1= bootfs lofs zfs
-SUBDIR2= dev fd pcfs nfs hsfs proc ctfs udfs ufs tmpfs \
+SUBDIR1= bootfs hyprlofs lofs zfs
+SUBDIR2= dev fd pcfs nfs hsfs lxproc proc ctfs udfs ufs tmpfs \
autofs mntfs objfs sharefs smbclnt reparsed
SUBDIRS= $(SUBDIR1) $(SUBDIR2)
I18NDIRS= $(SUBDIR2)
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/mount.c b/usr/src/cmd/fs.d/mount.c
index 8c8d7034fe..19be4b28c1 100644
--- a/usr/src/cmd/fs.d/mount.c
+++ b/usr/src/cmd/fs.d/mount.c
@@ -18,6 +18,12 @@
*
* CDDL HEADER END
*/
+
+/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
+ */
+
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
@@ -52,6 +58,7 @@
#include <stropts.h>
#include <sys/conf.h>
#include <locale.h>
+#include <zone.h>
#include <priv.h>
#include "fslib.h"
@@ -799,6 +806,7 @@ mnterror(int flag)
void
doexec(char *fstype, char *newargv[])
{
+ const char *zroot = zone_get_nroot();
char full_path[PATH_MAX];
char alter_path[PATH_MAX];
char *vfs_path = VFS_PATH;
@@ -806,7 +814,8 @@ doexec(char *fstype, char *newargv[])
int i;
/* build the full pathname of the fstype dependent command. */
- sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname);
+ (void) snprintf(full_path, sizeof (full_path), "%s/%s/%s/%s",
+ (zroot != NULL ? zroot : ""), vfs_path, fstype, myname);
sprintf(alter_path, "%s/%s/%s", alt_path, fstype, myname);
newargv[1] = myname;
diff --git a/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c b/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c
index 6b387ca674..1890010fc3 100644
--- a/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c
+++ b/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c
@@ -45,6 +45,7 @@
#include <stdlib.h>
#include <syslog.h>
#include <synch.h>
+#include <zone.h>
#include <rpc/rpc.h>
#include <nfs/nfs_sec.h>
#include <rpc/rpcsec_gss.h>
@@ -700,12 +701,17 @@ get_seconfig(int whichway, char *name, int num,
{
char line[BUFSIZ]; /* holds each line of NFSSEC_CONF */
FILE *fp; /* file stream for NFSSEC_CONF */
+ char nfssec_conf[MAXPATHLEN];
+ const char *zroot = zone_get_nroot();
if ((whichway == GETBYNAME) && (name == NULL))
return (SC_NOTFOUND);
+ (void) snprintf(nfssec_conf, sizeof (nfssec_conf), "%s%s", zroot != NULL ?
+ zroot : "", NFSSEC_CONF);
+
(void) mutex_lock(&matching_lock);
- if ((fp = fopen(NFSSEC_CONF, "r")) == NULL) {
+ if ((fp = fopen(nfssec_conf, "r")) == NULL) {
(void) mutex_unlock(&matching_lock);
return (SC_OPENFAIL);
}
diff --git a/usr/src/cmd/fs.d/nfs/lib/smfcfg.c b/usr/src/cmd/fs.d/nfs/lib/smfcfg.c
index 40922c895b..da196077e3 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 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#include <stdio.h>
@@ -348,8 +349,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/lockd/lockd.c b/usr/src/cmd/fs.d/nfs/lockd/lockd.c
index 3541ee13d0..6ea338b01a 100644
--- a/usr/src/cmd/fs.d/nfs/lockd/lockd.c
+++ b/usr/src/cmd/fs.d/nfs/lockd/lockd.c
@@ -23,6 +23,7 @@
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -88,7 +89,7 @@
struct lm_svc_args lmargs = {
.version = LM_SVC_CUR_VERS,
/* fd, n_fmly, n_proto, n_rdev (below) */
- .debug = 0,
+ .n_v4_only = 0,
.timout = 5 * 60,
.grace = 90,
.retransmittimeout = 5
@@ -137,6 +138,8 @@ int (*Mysvc)(int, struct netbuf, struct netconfig *) = nlmsvc;
/* used by cots_listen_event() */
int max_conns_allowed = -1; /* used by cots_listen_event() */
+int debug = 0;
+
int
main(int ac, char *av[])
{
@@ -238,7 +241,7 @@ main(int ac, char *av[])
break;
case 'd': /* debug */
- lmargs.debug = atoi(optarg);
+ debug = atoi(optarg);
break;
case 'g': /* grace_period */
@@ -288,12 +291,12 @@ main(int ac, char *av[])
if (optind != ac)
usage();
- if (lmargs.debug) {
+ if (debug != 0) {
printf("%s: debug= %d, conn_idle_timout= %d,"
" grace_period= %d, listen_backlog= %d,"
" max_connections= %d, max_servers= %d,"
" retrans_timeout= %d\n",
- MyName, lmargs.debug, lmargs.timout,
+ MyName, debug, lmargs.timout,
lmargs.grace, listen_backlog,
max_conns_allowed, max_servers,
lmargs.retransmittimeout);
@@ -309,7 +312,7 @@ main(int ac, char *av[])
}
/* Daemonize, if not debug. */
- if (lmargs.debug == 0)
+ if (debug == 0)
pipe_fd = daemonize_init();
openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
@@ -405,7 +408,7 @@ main(int ac, char *av[])
/*
* lockd is up and running as far as we are concerned.
*/
- if (lmargs.debug == 0)
+ if (debug == 0)
daemonize_fini(pipe_fd);
/*
diff --git a/usr/src/cmd/fs.d/nfs/mount/Makefile b/usr/src/cmd/fs.d/nfs/mount/Makefile
index a53b876974..cb6c77044b 100644
--- a/usr/src/cmd/fs.d/nfs/mount/Makefile
+++ b/usr/src/cmd/fs.d/nfs/mount/Makefile
@@ -25,13 +25,6 @@
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
@@ -69,7 +62,7 @@ CLOBBERFILES += $(LIBPROG)
.KEEP_STATE:
-all: $(ROOTFS_PROG)
+all: $(LIBPROG)
$(LIBPROG): webnfs.h $(OBJS)
$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
@@ -118,7 +111,9 @@ $(POFILE): $(SRCS) webnfs.h
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 efb1f998f3..e1206e186a 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/svc/nfs-server b/usr/src/cmd/fs.d/nfs/svc/nfs-server
index bf8e73be98..d982829758 100644
--- a/usr/src/cmd/fs.d/nfs/svc/nfs-server
+++ b/usr/src/cmd/fs.d/nfs/svc/nfs-server
@@ -53,13 +53,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 c4f04609fc..9b4825b98b 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.
@@ -1249,6 +1250,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";
@@ -1313,7 +1325,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,
@@ -1511,7 +1523,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(__x86)
@@ -1603,7 +1616,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/init.c b/usr/src/cmd/init/init.c
index 1afc73b976..e195573349 100644
--- a/usr/src/cmd/init/init.c
+++ b/usr/src/cmd/init/init.c
@@ -25,6 +25,7 @@
* Copyright (c) 2013 Gary Mills
*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2020 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -148,6 +149,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.
@@ -552,6 +555,8 @@ static time_t init_boot_time; /* Substitute for kernel boot time. */
#define NSTARTD_FAILURE_TIMES 3 /* trigger after 3 failures */
#define STARTD_FAILURE_RATE_NS 5000000000LL /* 1 failure/5 seconds */
+#define STARTD_THROTTLE_RETRY 60 /* space failure retry after 60 secs */
+#define ROOT_MIN_FREE 524288 /* 512KB min. space needed in root */
static hrtime_t startd_failure_time[NSTARTD_FAILURE_TIMES];
static uint_t startd_failure_index;
@@ -697,9 +702,7 @@ main(int argc, char *argv[])
console(B_FALSE,
"\n\n%s Release %s Version %s %s-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 2010-2020 Joyent, Inc.\r\n");
#else
bootbanner_print(init_bootbanner_print, 0);
#endif
@@ -3471,6 +3474,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
@@ -3558,6 +3583,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) {
@@ -4267,9 +4298,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
@@ -4400,6 +4429,28 @@ startd_run(const char *cline, int tmpl, ctid_t old_ctid)
if (pid == 0) {
/* child */
+ struct statvfs64 sbuf;
+
+ /*
+ * svc.configd needs some space (a few hundred KB) in / for its
+ * database. One common cause for startd failure is when
+ * configd dies because / is full. We don't want to go into the
+ * fast restart loop (startd_failure_rate_critical) and enter
+ * maintenance so we check for this case and slow down the
+ * failure rate so as to keep retrying in the hope space will
+ * free up.
+ */
+ if (statvfs64("/", &sbuf) != -1 &&
+ (sbuf.f_bsize * sbuf.f_bfree) < ROOT_MIN_FREE) {
+ syslog(LOG_ERR, "Insufficent space (%ld) in / to "
+ "start svc.startd.\n",
+ (long)(sbuf.f_bsize * sbuf.f_bfree));
+ console(B_TRUE, "Insufficent space (%ld) in / to "
+ "start svc.startd.\n",
+ (long)(sbuf.f_bsize * sbuf.f_bfree));
+ sleep(STARTD_THROTTLE_RETRY);
+ exit(1);
+ }
/* See the comment in efork() */
for (i = SIGHUP; i <= SIGRTMAX; ++i) {
diff --git a/usr/src/cmd/init/init.dfl b/usr/src/cmd/init/init.dfl
index 371b48becc..150f720b1c 100644
--- a/usr/src/cmd/init/init.dfl
+++ b/usr/src/cmd/init/init.dfl
@@ -29,5 +29,6 @@
# TZ, LANG, CMASK, or any of the LC_* environment variables. value may
# be enclosed in double quotes (") or single quotes (').
#
-TZ=PST8PDT
+TZ=UTC
CMASK=022
+LANG=en_US.UTF-8
diff --git a/usr/src/cmd/initpkg/mountall.sh b/usr/src/cmd/initpkg/mountall.sh
index fa59a20a41..2302c7592c 100644
--- a/usr/src/cmd/initpkg/mountall.sh
+++ b/usr/src/cmd/initpkg/mountall.sh
@@ -29,6 +29,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
@@ -149,6 +151,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 e58e6b9918..038289b630 100644
--- a/usr/src/cmd/initpkg/shutdown.sh
+++ b/usr/src/cmd/initpkg/shutdown.sh
@@ -43,7 +43,7 @@ usage() {
}
notify() {
- /usr/sbin/wall -a <<-!
+ /usr/sbin/wall -Za <<-!
$*
!
# We used to do rwall here if showmounts had any output, but
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/etc/Makefile b/usr/src/cmd/ipf/etc/Makefile
index 3e06187ae9..a5eb399f56 100644
--- a/usr/src/cmd/ipf/etc/Makefile
+++ b/usr/src/cmd/ipf/etc/Makefile
@@ -22,42 +22,31 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#cmd/ipf/etc/Makefile
+# Copyright 2019 Joyent, Inc.
#
-IPFCONF= ipf.conf
-IPFPROG= $(IPFCONF)
+IPFFILES = ipf.conf smartos_version
include ../../Makefile.cmd
-
ETCIPF= $(ROOTETC)/ipf
-DIRS= $(ETCIPF)
-
-ETCIPFPROG= $(IPFPROG:%=$(ETCIPF)/%)
+ROOTETCIPFFILES = $(IPFFILES:%=$(ETCIPF)/%)
-$(ETCIPFPROG):= FILEMODE= 0644
+$(ETCIPF)/ipf.conf := FILEMODE= 0644
+$(ETCIPF)/smartos_version := FILEMODE= 0444
.KEEP_STATE:
-all: $(IPFPROG) $(DIRS) $(ETCIPFPROG)
-
-install: all $(DIRS)
+all:
-$(PFILAP):
- $(SH) $@.sh
+install: all $(ROOTETCIPFFILES)
-$(ETCIPF)/% : % $(ETCIPF)
+$(ETCIPF)/% : %
$(INS.file)
-$(DIRS):
- $(INS.dir)
-
clean:
clobber:
-lint:
-
.PARALLEL:
diff --git a/usr/src/cmd/ipf/etc/smartos_version b/usr/src/cmd/ipf/etc/smartos_version
new file mode 100644
index 0000000000..0cfbf08886
--- /dev/null
+++ b/usr/src/cmd/ipf/etc/smartos_version
@@ -0,0 +1 @@
+2
diff --git a/usr/src/cmd/ipf/lib/common/printfr.c b/usr/src/cmd/ipf/lib/common/printfr.c
index 063eb87c07..b096c46e0e 100644
--- a/usr/src/cmd/ipf/lib/common/printfr.c
+++ b/usr/src/cmd/ipf/lib/common/printfr.c
@@ -7,9 +7,10 @@
*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+#include <uuid/uuid.h>
#include "ipf.h"
@@ -386,7 +387,8 @@ ioctlfunc_t iocfunc;
printf(" head %s", fp->fr_grhead);
if (*fp->fr_group != '\0')
printf(" group %s", fp->fr_group);
- if (fp->fr_logtag != FR_NOLOGTAG || *fp->fr_nattag.ipt_tag) {
+ if (fp->fr_logtag != FR_NOLOGTAG || *fp->fr_nattag.ipt_tag ||
+ (fp->fr_flags & FR_CFWLOG) || !uuid_is_null(fp->fr_uuid)) {
char *s = "";
printf(" set-tag(");
@@ -397,6 +399,18 @@ ioctlfunc_t iocfunc;
if (*fp->fr_nattag.ipt_tag) {
printf("%snat=%-.*s", s, IPFTAG_LEN,
fp->fr_nattag.ipt_tag);
+ s = ", ";
+ }
+ if (fp->fr_flags & FR_CFWLOG) {
+ printf("cfwlog");
+ s = ", ";
+ }
+
+ if (!uuid_is_null(fp->fr_uuid)) {
+ char uuid[UUID_PRINTABLE_STRING_LENGTH];
+
+ uuid_unparse(fp->fr_uuid, uuid);
+ printf("%suuid=%s", s, uuid);
}
printf(")");
}
diff --git a/usr/src/cmd/ipf/svc/ipfilter b/usr/src/cmd/ipf/svc/ipfilter
index 48e3e2e915..bb25316b44 100644
--- a/usr/src/cmd/ipf/svc/ipfilter
+++ b/usr/src/cmd/ipf/svc/ipfilter
@@ -23,9 +23,12 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright (c) 2013, Joyent, Inc. All rights reserved.
# Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
#
+set -o xtrace
+
. /lib/svc/share/smf_include.sh
. /lib/svc/share/ipf_include.sh
@@ -152,8 +155,25 @@ upgrade_config()
svcadm refresh $SMF_FMRI >/dev/null 2>&1
}
+symlink_persistent_file()
+{
+ persist_file=/var/fw/$1
+ etc_file=$ETC_IPF_DIR/$1
+
+ [ ! -e $persist_file ] && return 0
+ [ -L $etc_file ] && return 0
+
+ [ -e $etc_file ] && mv $etc_file{,.orig}
+
+ ln -s $persist_file $etc_file
+}
+
+
configure_firewall()
{
+ symlink_persistent_file ipnat.conf
+ symlink_persistent_file ipf.conf
+ symlink_persistent_file ipf6.conf
create_global_rules || exit $SMF_EXIT_ERR_CONFIG
create_global_ovr_rules || exit $SMF_EXIT_ERR_CONFIG
create_services_rules || exit $SMF_EXIT_ERR_CONFIG
diff --git a/usr/src/cmd/ipf/svc/ipfilter.xml b/usr/src/cmd/ipf/svc/ipfilter.xml
index 44bc52a41b..1dade14f5b 100644
--- a/usr/src/cmd/ipf/svc/ipfilter.xml
+++ b/usr/src/cmd/ipf/svc/ipfilter.xml
@@ -4,6 +4,7 @@
Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
+
CDDL HEADER START
The contents of this file are subject to the terms of the
@@ -97,13 +98,13 @@
type='method'
name='refresh'
exec='/lib/svc/method/ipfilter reload'
- timeout_seconds='120' >
+ timeout_seconds='1200' >
</exec_method>
- <instance name='default' enabled='false'>
+ <instance name='default' enabled='true'>
<property_group name='firewall_config_default'
type='com.sun,fw_configuration'>
- <propval name='policy' type='astring' value='none' />
+ <propval name='policy' type='astring' value='custom' />
<propval name='block_policy' type='astring'
value='none' />
<propval name='custom_policy_file' type='astring'
diff --git a/usr/src/cmd/ipf/tools/Makefile.tools b/usr/src/cmd/ipf/tools/Makefile.tools
index 15b1634758..2bea0cd0b0 100644
--- a/usr/src/cmd/ipf/tools/Makefile.tools
+++ b/usr/src/cmd/ipf/tools/Makefile.tools
@@ -23,8 +23,7 @@
# Use is subject to license terms.
#
# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
-#
-# Copyright (c) 2012, Joyent Inc. All rights reserved.
+# Copyright 2020 Joyent, Inc.
#
PROG= ipf ipfs ipmon ipnat ippool ipfstat
@@ -36,7 +35,7 @@ IPFSTAT_OBJS= ipfstat.o ipfzone.o
IPMON_OBJS= ipmon.o ipfzone.o ipmon_y.o ipmon_l.o
IPNAT_OBJS= ipnat.o ipfzone.o ipnat_y.o ipnat_l.o
IPPOOL_OBJS= ippool.o ipfzone.o ippool_y.o ippool_l.o
-IPFTEST_OBJS= ipftest.o ipfzone.o \
+IPFTEST_OBJS= cfw.o ipftest.o ipfzone.o \
ip_fil.o ip_state.o ip_compat.o \
ip_frag.o ip_nat.o ip_nat6.o fil.o \
ip_htable.o ip_lookup.o \
@@ -65,12 +64,12 @@ CPPFLAGS += -I. -DIPFILTER_LOOKUP -DIPFILTER_LOG
ipfstat.o := CPPFLAGS += -DSTATETOP
ipfstat := LDLIBS += -lcurses
-ipf := LDLIBS += -lsocket -lnsl
-ipftest := LDLIBS += -lsocket -lnsl -lmd
-ipfstat := LDLIBS += -lsocket -lnsl -lkvm -lelf
-ipmon := LDLIBS += -lsocket -lnsl
-ipnat := LDLIBS += -lsocket -lnsl -lkvm -lelf
-ippool := LDLIBS += -lsocket -lnsl -lkvm -lelf
+ipf := LDLIBS += -lsocket -lnsl -luuid
+ipftest := LDLIBS += -lsocket -lnsl -lmd -luuid
+ipfstat := LDLIBS += -lsocket -lnsl -lkvm -lelf -luuid
+ipmon := LDLIBS += -lsocket -lnsl -luuid
+ipnat := LDLIBS += -lsocket -lnsl -lkvm -lelf -luuid
+ippool := LDLIBS += -lsocket -lnsl -lkvm -lelf -luuid
CLEANFILES += $(OBJS)
CLOBBERFILES += $(IPFPROG)
diff --git a/usr/src/cmd/ipf/tools/ipf_y.y b/usr/src/cmd/ipf/tools/ipf_y.y
index 7689d676c7..c8909b4e92 100644
--- a/usr/src/cmd/ipf/tools/ipf_y.y
+++ b/usr/src/cmd/ipf/tools/ipf_y.y
@@ -6,6 +6,7 @@
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
#include "ipf.h"
@@ -16,6 +17,7 @@
# define _NET_BPF_H_
# include <pcap.h>
#endif
+#include <uuid/uuid.h>
#include "netinet/ip_pool.h"
#include "netinet/ip_htable.h"
#include "netinet/ipl.h"
@@ -98,6 +100,7 @@ static int set_ipv6_addr = 0;
union i6addr m;
} ipp;
union i6addr ip6;
+ uuid_t uuid;
};
%type <port> portnum
@@ -117,6 +120,7 @@ static int set_ipv6_addr = 0;
%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
%token YY_RANGE_OUT YY_RANGE_IN
%token <ip6> YY_IPV6
+%token <uuid> YY_UUID
%token IPFY_PASS IPFY_BLOCK IPFY_COUNT IPFY_CALL
%token IPFY_RETICMP IPFY_RETRST IPFY_RETICMPASDST
@@ -127,6 +131,7 @@ static int set_ipv6_addr = 0;
%token IPFY_HEAD IPFY_GROUP
%token IPFY_AUTH IPFY_PREAUTH
%token IPFY_LOG IPFY_BODY IPFY_FIRST IPFY_LEVEL IPFY_ORBLOCK
+%token IPFY_UUID IPFY_CFWLOG
%token IPFY_LOGTAG IPFY_MATCHTAG IPFY_SETTAG IPFY_SKIP
%token IPFY_FROM IPFY_ALL IPFY_ANY IPFY_BPFV4 IPFY_BPFV6 IPFY_POOL IPFY_HASH
%token IPFY_PPS
@@ -518,6 +523,8 @@ taginlist:
taginspec:
logtag
|nattag
+ |uuidtag
+ |cfwtag
;
nattag: IPFY_NAT '=' YY_STR { DOALL(strncpy(fr->fr_nattag.ipt_tag,\
@@ -530,6 +537,12 @@ nattag: IPFY_NAT '=' YY_STR { DOALL(strncpy(fr->fr_nattag.ipt_tag,\
logtag: IPFY_LOG '=' YY_NUMBER { DOALL(fr->fr_logtag = $3;) }
;
+cfwtag: IPFY_CFWLOG { DOALL(fr->fr_flags |= FR_CFWLOG;) }
+ ;
+
+uuidtag: IPFY_UUID '=' YY_UUID { DOALL(uuid_copy(fr->fr_uuid, $3);) }
+ ;
+
settagout:
| IPFY_SETTAG '(' tagoutlist ')'
;
@@ -542,6 +555,8 @@ tagoutlist:
tagoutspec:
logtag
| nattag
+ | uuidtag
+ | cfwtag
;
matchtagin:
@@ -1566,6 +1581,7 @@ static struct wordtab ipfwords[96] = {
{ "bpf-v6", IPFY_BPFV6 },
#endif
{ "call", IPFY_CALL },
+ { "cfwlog", IPFY_CFWLOG },
{ "code", IPFY_ICMPCODE },
{ "count", IPFY_COUNT },
{ "dup-to", IPFY_DUPTO },
@@ -1641,6 +1657,7 @@ static struct wordtab ipfwords[96] = {
{ "to", IPFY_TO },
{ "ttl", IPFY_TTL },
{ "udp", IPFY_UDP },
+ { "uuid", IPFY_UUID },
{ "v6hdrs", IPF6_V6HDRS },
{ "with", IPFY_WITH },
{ NULL, 0 }
diff --git a/usr/src/cmd/ipf/tools/ipfstat.c b/usr/src/cmd/ipf/tools/ipfstat.c
index 1a3a5f4be8..52c4421404 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/ipf/tools/ipmon_y.y b/usr/src/cmd/ipf/tools/ipmon_y.y
index e6dda81c35..b4fce4015d 100644
--- a/usr/src/cmd/ipf/tools/ipmon_y.y
+++ b/usr/src/cmd/ipf/tools/ipmon_y.y
@@ -1,11 +1,14 @@
/*
* Copyright (C) 1993-2005 by Darren Reed.
* See the IPFILTER.LICENCE file for details on licencing.
+ *
+ * Copyright 2019 Joyent, Inc.
*/
%{
#include "ipf.h"
#include <syslog.h>
+#include <uuid/uuid.h>
#undef OPT_NAT
#undef OPT_VERBOSE
#include "ipmon_l.h"
@@ -42,11 +45,13 @@ static ipmon_action_t *alist = NULL;
struct in_addr addr;
struct opt *opt;
union i6addr ip6;
+ uuid_t uuid;
}
%token <num> YY_NUMBER YY_HEX
%token <str> YY_STR
%token <ip6> YY_IPV6
+%token <uuid> YY_UUID
%token YY_COMMENT
%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
%token YY_RANGE_OUT YY_RANGE_IN
diff --git a/usr/src/cmd/ipf/tools/ipnat_y.y b/usr/src/cmd/ipf/tools/ipnat_y.y
index d929bf413a..2c913afea2 100644
--- a/usr/src/cmd/ipf/tools/ipnat_y.y
+++ b/usr/src/cmd/ipf/tools/ipnat_y.y
@@ -6,6 +6,7 @@
*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
@@ -39,6 +40,7 @@
#include <sys/time.h>
#include <syslog.h>
#include <net/if.h>
+#include <uuid/uuid.h>
#if __FreeBSD_version >= 300000
# include <net/if_var.h>
#endif
@@ -89,6 +91,7 @@ static void setnatproto __P((int));
int v;
} ipp;
union i6addr ip6;
+ uuid_t uuid;
};
%token <num> YY_NUMBER YY_HEX
@@ -97,6 +100,7 @@ static void setnatproto __P((int));
%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
%token YY_RANGE_OUT YY_RANGE_IN
%token <ip6> YY_IPV6
+%token <uuid> YY_UUID
%token IPNY_MAPBLOCK IPNY_RDR IPNY_PORT IPNY_PORTS IPNY_AUTO IPNY_RANGE
%token IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY
diff --git a/usr/src/cmd/ipf/tools/ippool_y.y b/usr/src/cmd/ipf/tools/ippool_y.y
index cca5052bd4..5aadd22206 100644
--- a/usr/src/cmd/ipf/tools/ippool_y.y
+++ b/usr/src/cmd/ipf/tools/ippool_y.y
@@ -6,6 +6,7 @@
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
@@ -34,6 +35,7 @@
#include <netdb.h>
#include <ctype.h>
#include <unistd.h>
+#include <uuid/uuid.h>
#include "ipf.h"
#include "netinet/ip_lookup.h"
@@ -66,6 +68,7 @@ static int set_ipv6_addr = 0;
iphtent_t *ipe;
ip_pool_node_t *ipp;
union i6addr ip6;
+ uuid_t uuid;
}
%token <num> YY_NUMBER YY_HEX
@@ -74,6 +77,7 @@ static int set_ipv6_addr = 0;
%token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
%token YY_RANGE_OUT YY_RANGE_IN
%token <ip6> YY_IPV6
+%token <uuid> YY_UUID
%token IPT_IPF IPT_NAT IPT_COUNT IPT_AUTH IPT_IN IPT_OUT
%token IPT_TABLE IPT_GROUPMAP IPT_HASH
diff --git a/usr/src/cmd/ipf/tools/lexer.c b/usr/src/cmd/ipf/tools/lexer.c
index 3db3a0888b..b4ee8b3f77 100644
--- a/usr/src/cmd/ipf/tools/lexer.c
+++ b/usr/src/cmd/ipf/tools/lexer.c
@@ -5,6 +5,7 @@
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
#include <ctype.h>
@@ -14,6 +15,7 @@
#endif
#include <sys/ioctl.h>
#include <syslog.h>
+#include <uuid/uuid.h>
#ifdef TEST_LEXER
# define NO_YACC
union {
@@ -21,6 +23,7 @@ union {
char *str;
struct in_addr ipa;
i6addr_t ip6;
+ uuid_t uuid;
} yylval;
#endif
#include "lexer.h"
@@ -455,6 +458,40 @@ nextchar:
}
#endif
+ /*
+ * UUID: e.g., "2426e38c-9f63-c0b8-cfd5-9aaeaf992d42" or its uppercase
+ * variant.
+ */
+ if (isbuilding == 0 && (ishex(c) || c == '-')) {
+ char uuidbuf[UUID_PRINTABLE_STRING_LENGTH], *s, oc;
+ int start;
+
+ start = yypos;
+ s = uuidbuf;
+ oc = c;
+
+ /*
+ * Don't worry about exact position of hexdigits and hyphens
+ * because uuid_parse() will provide the sanity check.
+ */
+ do {
+ *s++ = c;
+ c = yygetc(1);
+ } while ((ishex(c) || c == '-') &&
+ (s - uuidbuf < sizeof (uuidbuf)));
+ yyunputc(c);
+ *s = '\0';
+
+ if (uuid_parse(uuidbuf, yylval.uuid) == 0) {
+ rval = YY_UUID;
+ yyexpectaddr = 0;
+ goto done;
+ }
+ yypos = start;
+ c = oc;
+ }
+
+
if (c == ':') {
if (isbuilding == 1) {
yyunputc(c);
diff --git a/usr/src/cmd/ipf/tools/lexer.h b/usr/src/cmd/ipf/tools/lexer.h
index a296cb0bc3..448b3e6ffd 100644
--- a/usr/src/cmd/ipf/tools/lexer.h
+++ b/usr/src/cmd/ipf/tools/lexer.h
@@ -1,4 +1,6 @@
-
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
typedef struct wordtab {
char *w_word;
int w_value;
@@ -16,6 +18,7 @@ typedef struct wordtab {
#define YY_IPV6 1008
#define YY_STR 1009
#define YY_IPADDR 1010
+#define YY_UUID 1011
#endif
#define YYBUFSIZ 8192
diff --git a/usr/src/cmd/iscsid/iscsi-initiator b/usr/src/cmd/iscsid/iscsi-initiator
index 48b97f0f35..39d983ffff 100644
--- a/usr/src/cmd/iscsid/iscsi-initiator
+++ b/usr/src/cmd/iscsid/iscsi-initiator
@@ -22,6 +22,7 @@
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2020 Joyent, Inc.
#
#
@@ -170,6 +171,25 @@ mount_iscsi() {
}
umount_iscsi () {
+ # In case any non-system zpools reside on iscsi drives, export
+ # all of them now so that the system won't hang later in the shutdown
+ # sequence when those zpools need to be unmounted.
+ SYS_POOL=`svcprop -p config/zpool svc:/system/smartdc/init:default \
+ 2>/dev/null`
+ SYS_POOL=${SYS_POOL:-zones}
+
+ zpools=$(zpool list -Ho name)
+ for pool in $zpools; do
+ if [ "$pool" == $SYS_POOL ]; then
+ continue
+ fi
+ zpool export $pool
+ got=$?
+ if [ $got -ne 0 ]; then
+ echo "Exporting zpool $pool failed ($got)"
+ fi
+ done
+
#
# Generate iscsi mountp list from /etc/vfstab
exec < /etc/vfstab
diff --git a/usr/src/cmd/iscsid/iscsi-initiator.xml b/usr/src/cmd/iscsid/iscsi-initiator.xml
index bd62da0977..d132815117 100644
--- a/usr/src/cmd/iscsid/iscsi-initiator.xml
+++ b/usr/src/cmd/iscsid/iscsi-initiator.xml
@@ -23,6 +23,7 @@
CDDL HEADER END
Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright 2020 Joyent, Inc.
Service manifests for the iSCSI Initiator
-->
@@ -98,6 +99,20 @@ potential to specialize all the properties/methods.
<service_fmri value='svc:/network/loopback' />
</dependency>
+ <!--
+ We declare a dependency on the optional iscsi-target svc so that it
+ will not shutdown before the initiator svc. In this way we can use
+ targets on the same machine and not see a failure or hang when we're
+ exporting any zpools that might be using local targets.
+ -->
+ <dependency
+ name='iscsi-target'
+ grouping='optional_all'
+ restart_on='error'
+ type='service'>
+ <service_fmri value='svc:/network/iscsi/target' />
+ </dependency>
+
<dependent
name='iscsi-initiator_multi-user'
grouping='optional_all'
@@ -136,7 +151,7 @@ potential to specialize all the properties/methods.
<method_credential
user='root'
group='root'
- privileges='basic,sys_devices,sys_mount'
+ privileges='basic,sys_config,sys_devices,sys_mount'
/>
</method_context>
</exec_method>
diff --git a/usr/src/cmd/localedef/Makefile b/usr/src/cmd/localedef/Makefile
index 358f63de01..8a8e5dc2e0 100644
--- a/usr/src/cmd/localedef/Makefile
+++ b/usr/src/cmd/localedef/Makefile
@@ -13,7 +13,7 @@
# Copyright 2017 Nexenta Systems, Inc.
# Copyright 2011 EveryCity Ltd. All rights reserved.
# Copyright 2013 DEY Storage Systems, Inc.
-# Copyright 2016 Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
# Copyright 2017 RackTop Systems.
#
@@ -23,6 +23,10 @@ include $(SRC)/cmd/localedef/Makefile.common
LDLIBS += -lavl -lgen
+I18NEXTFILE = $(ROOTI18NEXT)/UTF-8.x
+
+$(I18NEXTFILE) := FILEMODE = 0444
+
.KEEP_STATE:
all: $(PROG)
@@ -43,9 +47,7 @@ $(POFILE): $(PIFILES)
$(SED) -e '/domain/d' messages.po > $@
$(RM) $(PIFILES) messages.po
-install: all $(ROOTPROG)
-
-lint: lint_SRCS
+install: all $(ROOTPROG) $(I18NEXTFILE)
clean:
$(RM) $(CLEANFILES)
diff --git a/usr/src/cmd/localedef/UTF-8.x b/usr/src/cmd/localedef/UTF-8.x
new file mode 100644
index 0000000000..b7ab359bd4
--- /dev/null
+++ b/usr/src/cmd/localedef/UTF-8.x
@@ -0,0 +1,114 @@
+#
+# 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 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 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 1996-1997, 2000-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Method file for Solaris Unicode locales.
+#
+#
+#ident "@(#)UTF-8.x 1.4 03/08/19 SMI"
+#
+
+METHODS
+
+#
+# Encoding definitions to use UTF-8 (MB) and UTF-32 (WC):
+file_code utf8
+process_code ucs4
+
+#
+# We use the following methods from the libc:
+iswctype@native "__iswctype_std" "libc" "/usr/lib/" "libc.so.1"
+towctrans@native "__towctrans_std"
+towlower@native "__towlower_std"
+towupper@native "__towupper_std"
+trwctype "__trwctype_std"
+wctrans "__wctrans_std"
+wctype "__wctype_std"
+
+mbsinit "__mbsinit_gen"
+mbrlen "__mbrlen_gen"
+
+strcoll "__strcoll_std"
+strxfrm "__strxfrm_std"
+wcscoll "__wcscoll_bc"
+wcscoll@native "__wcscoll_std"
+wcsxfrm "__wcsxfrm_bc"
+wcsxfrm@native "__wcsxfrm_std"
+
+fnmatch "__fnmatch_std"
+regcomp "__regcomp_std"
+regexec "__regexec_std"
+regerror "__regerror_std"
+regfree "__regfree_std"
+
+strfmon "__strfmon_std"
+
+strftime "__strftime_std"
+strptime "__strptime_std"
+wcsftime "__wcsftime_std"
+
+getdate "__getdate_std"
+
+#
+# The methods designated at below are all Unicode locale-specific methods
+# coming from the methods_unicode.so.3 shared object:
+eucpctowc "__u32_to_dense_u32_utf8" "localelib" "/usr/lib/locale/common" "methods_unicode.so.3"
+wctoeucpc "__dense_u32_to_u32_utf8"
+
+iswctype "__iswctype_bc_utf8"
+towctrans "__towctrans_bc_utf8"
+towlower "__towlower_bc_utf8"
+towupper "__towupper_bc_utf8"
+
+mbftowc "__mbftowc_dense_utf8"
+mbftowc@native "__mbftowc_dense_native_utf8"
+fgetwc "__fgetwc_dense_utf8"
+fgetwc@native "__fgetwc_dense_native_utf8"
+mblen "__mblen_dense_utf8"
+mbstowcs "__mbstowcs_dense_utf8"
+mbstowcs@native "__mbstowcs_dense_native_utf8"
+mbtowc "__mbtowc_dense_utf8"
+mbtowc@native "__mbtowc_dense_native_utf8"
+wcstombs "__wcstombs_dense_utf8"
+wcstombs@native "__wcstombs_dense_native_utf8"
+wcswidth "__wcswidth_dense_utf8"
+wcswidth@native "__wcswidth_dense_utf8"
+wctomb "__wctomb_dense_utf8"
+wctomb@native "__wctomb_dense_native_utf8"
+wcwidth "__wcwidth_dense_utf8"
+wcwidth@native "__wcwidth_dense_utf8"
+
+btowc "__btowc_dense_utf8"
+btowc@native "__btowc_dense_utf8"
+wctob "__wctob_dense_utf8"
+wctob@native "__wctob_dense_utf8"
+mbrtowc "__mbrtowc_dense_utf8"
+mbrtowc@native "__mbrtowc_dense_native_utf8"
+wcrtomb "__wcrtomb_dense_utf8"
+wcrtomb@native "__wcrtomb_dense_native_utf8"
+mbsrtowcs "__mbsrtowcs_dense_utf8"
+mbsrtowcs@native "__mbsrtowcs_dense_native_utf8"
+wcsrtombs "__wcsrtombs_dense_utf8"
+wcsrtombs@native "__wcsrtombs_dense_native_utf8"
+
+END METHODS
diff --git a/usr/src/cmd/lofiadm/main.c b/usr/src/cmd/lofiadm/main.c
index 758632b8ec..f1c360ec6f 100644
--- a/usr/src/cmd/lofiadm/main.c
+++ b/usr/src/cmd/lofiadm/main.c
@@ -412,7 +412,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;
@@ -425,7 +426,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(li);
+ if (!no_devlink_flag)
+ wait_until_dev_complete(li);
return (minor);
}
@@ -436,7 +438,7 @@ 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,
- boolean_t label)
+ boolean_t label, boolean_t no_devlink_flag)
{
struct lofi_ioctl li;
@@ -475,7 +477,7 @@ add_mapping(int lfd, const char *devicename, const char *filename,
char path[MAXPATHLEN];
/* pick one via the driver */
- minor = lofi_map_file(lfd, &li, filename);
+ minor = lofi_map_file(lfd, &li, filename, no_devlink_flag);
if (minor > 0) {
make_blkdevname(&li, path, sizeof (path));
@@ -500,7 +502,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);
+ if (!no_devlink_flag)
+ wait_until_dev_complete(&li);
}
/*
@@ -1393,7 +1396,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);
@@ -1935,6 +1938,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 */
@@ -1950,7 +1954,7 @@ main(int argc, char *argv[])
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
- while ((c = getopt(argc, argv, "a:c:Cd:efk:lrs:T:U")) != EOF) {
+ while ((c = getopt(argc, argv, "a:c:Cd:efk:lrs:T:UX")) != EOF) {
switch (c) {
case 'a':
addflag = B_TRUE;
@@ -2031,6 +2035,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;
@@ -2163,7 +2174,7 @@ main(int argc, char *argv[])
*/
if (addflag)
add_mapping(lfd, devicename, filename, cipher, rkey, rksz,
- rdflag, labelflag);
+ rdflag, labelflag, no_devlink_flag);
else if (compressflag)
lofi_compress(&lfd, filename, compress_index, segsize);
else if (uncompressflag)
diff --git a/usr/src/cmd/logadm/Makefile b/usr/src/cmd/logadm/Makefile
index 1b88186dcc..82cd4e5398 100644
--- a/usr/src/cmd/logadm/Makefile
+++ b/usr/src/cmd/logadm/Makefile
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2019 Joyent, Inc.
#
PROG= logadm
@@ -35,6 +36,7 @@ include ../Makefile.cmd
CPPFLAGS += -D_FILE_OFFSET_BITS=64
CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-clobbered
+CERRWARN += -_gcc=-Wno-unused-label
XGETFLAGS += -a -x logadm.xcl
$(ROOTETC)/$(CONFIGFILE):= FILEMODE= 644
diff --git a/usr/src/cmd/logadm/conf.c b/usr/src/cmd/logadm/conf.c
index a4275ce378..c910dabef7 100644
--- a/usr/src/cmd/logadm/conf.c
+++ b/usr/src/cmd/logadm/conf.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2018 Sebastian Wiedenroth
*/
@@ -279,8 +279,6 @@ conf_scan(const char *fname, char *buf, int buflen, int timescan)
if (SETJMP) {
err(EF_FILE, "cannot process invalid entry %s",
entry);
- ret = 0;
- LOCAL_ERR_BREAK;
}
if (timescan) {
@@ -529,7 +527,6 @@ conf_close(struct opts *opts)
(void) unlink(tuname);
err(EF_JMP, "unsafe to update configuration file "
"or timestamps");
- return;
}
/* rename updated files into place */
diff --git a/usr/src/cmd/logadm/glob.c b/usr/src/cmd/logadm/glob.c
index 338a1c6b9f..0978c876e9 100644
--- a/usr/src/cmd/logadm/glob.c
+++ b/usr/src/cmd/logadm/glob.c
@@ -22,6 +22,8 @@
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
+ * Copyright 2019 Joyent, Inc.
+ *
* logadm/glob.c -- globbing routines
*
* these routines support two kinds of globs. first, the
@@ -62,8 +64,6 @@
* braces, and don't support the more powerful reglobs required by logadm.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <stdio.h>
#include <libintl.h>
#include <stdlib.h>
@@ -100,8 +100,6 @@ glob_debrace(struct fn *fnp)
while (sp != NULL && (left = strchr(sp, '{')) != NULL)
if ((right = strchr(left, '}')) == NULL) {
err(EF_FILE|EF_JMP, "Missing }");
- fn_list_free(ret);
- return (NULL);
} else {
/* stuff before "left" is finished */
fn_list_appendrange(ret, sp, left);
diff --git a/usr/src/cmd/logadm/logadm.conf b/usr/src/cmd/logadm/logadm.conf
index 706bcff7b5..6c0df38306 100644
--- a/usr/src/cmd/logadm/logadm.conf
+++ b/usr/src/cmd/logadm/logadm.conf
@@ -19,6 +19,7 @@
# CDDL HEADER END
#
# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, Joyent, Inc.
#
# logadm.conf
#
@@ -35,12 +36,18 @@
# logadm typically runs early every morning via an entry in
# root's crontab (see crontab(1)).
#
-/var/log/syslog -C 8 -a 'kill -HUP `cat /var/run/syslog.pid`'
-/var/adm/messages -C 4 -a 'kill -HUP `cat /var/run/syslog.pid`'
+/var/log/syslog -C 8 -a 'kill -HUP `cat /var/run/*syslog*.pid`'
+/var/adm/messages -C 4 -a 'kill -HUP `cat /var/run/*syslog*.pid`'
/var/cron/log -c -s 512k -t /var/cron/olog
/var/lp/logs/lpsched -C 2 -N -t '$file.$N'
/var/fm/fmd/errlog -N -s 2m -M '/usr/sbin/fmadm -q rotate errlog && mv /var/fm/fmd/errlog.0- $nfile'
/var/fm/fmd/fltlog -N -A 6m -s 10m -M '/usr/sbin/fmadm -q rotate fltlog && mv /var/fm/fmd/fltlog.0- $nfile'
+vm_logs /var/log/vm/vmadm.log -b '/usr/vm/sbin/rotate-logs.sh -m /var/log/vm/logs/ /var/log/vm/vmadm.log' -t '/var/log/vm/vmadm_$nodename_%FT%H:%M:%S.log' -C 168 -S 1g -p 1h
+fw_logs /var/log/fw/fwadm.log -b '/usr/vm/sbin/rotate-logs.sh -i /var/log/fw/logs/ /var/log/fw/fwadm.log' -t '/var/log/fw/fwadm_$nodename_%FT%H:%M:%S.log' -C 168 -S 1g -p 1h
+vmadmd_logs /var/svc/log/*vmadmd*.log -C 168 -S 1g -c -p 1h -t '/var/log/vm/vmadmd_$nodename_%FT%H:%M:%S.log'
+vminfod_logs /var/svc/log/*vminfod*.log -C 168 -S 1g -c -p 1h -t '/var/log/vm/vminfod_$nodename_%FT%H:%M:%S.log'
+/var/log/*.log -C 2 -s 5m -c
+/var/log/*.debug -C 2 -s 5m -c
smf_logs /var/svc/log/*.log -C 8 -s 1m -c
#
# The entry below is used by turnacct(8)
diff --git a/usr/src/cmd/logadm/main.c b/usr/src/cmd/logadm/main.c
index 4423fd7ea3..3c959841f5 100644
--- a/usr/src/cmd/logadm/main.c
+++ b/usr/src/cmd/logadm/main.c
@@ -605,17 +605,14 @@ rotatelog(struct fn *fnp, struct opts *opts)
if (opts_count(opts, "N"))
return (1);
err(EF_WARN|EF_SYS, "%s", fname);
- return (B_FALSE);
}
if ((stbuf.st_mode & S_IFMT) == S_IFLNK) {
err(EF_WARN, "%s is a symlink", fname);
- return (B_FALSE);
}
if ((stbuf.st_mode & S_IFMT) != S_IFREG) {
err(EF_WARN, "%s is not a regular file", fname);
- return (B_FALSE);
}
/* even if size condition is not met, this entry is "done" */
@@ -1076,7 +1073,6 @@ docmd(struct opts *opts, const char *msg, const char *cmd,
(arg2) ? arg2 : "",
(arg3) ? " " : "",
(arg3) ? arg3 : "");
- first = B_FALSE;
}
err_fromfd(pfd.fd);
}
@@ -1086,7 +1082,6 @@ docmd(struct opts *opts, const char *msg, const char *cmd,
}
if (waitpid(pid, &wstat, 0) < 0) {
err(EF_SYS, "waitpid");
- return;
}
if (!first) {
@@ -1151,20 +1146,15 @@ docopytruncate(struct opts *opts, const char *file, const char *file_copy)
/* open log file to be rotated and remember its chmod mask */
if ((fi = open(file, O_RDWR)) < 0) {
err(EF_SYS, "cannot open file %s", file);
- return;
}
if (fstat(fi, &s) < 0) {
err(EF_SYS, "cannot access: %s", file);
- (void) close(fi);
- return;
}
/* create new file for copy destination with correct attributes */
if ((fo = open(file_copy, O_CREAT|O_TRUNC|O_WRONLY, s.st_mode)) < 0) {
err(EF_SYS, "cannot create file: %s", file_copy);
- (void) close(fi);
- return;
}
(void) fchown(fo, s.st_uid, s.st_gid);
@@ -1190,10 +1180,6 @@ docopytruncate(struct opts *opts, const char *file, const char *file_copy)
do {
if (fstat(fi, &s) < 0) {
err(EF_SYS, "cannot stat: %s", file);
- (void) close(fi);
- (void) close(fo);
- (void) remove(file_copy);
- return;
}
if ((rem = s.st_size - written) < thresh) {
@@ -1233,10 +1219,6 @@ docopytruncate(struct opts *opts, const char *file, const char *file_copy)
}
err(EF_SYS, "cannot write into file %s", file_copy);
- (void) close(fi);
- (void) close(fo);
- (void) remove(file_copy);
- return;
}
} while (len >= 0);
@@ -1251,12 +1233,6 @@ docopytruncate(struct opts *opts, const char *file, const char *file_copy)
while ((len = read(fi, buf, sizeof (buf))) > 0)
if (write(fo, buf, len) != len) {
err(EF_SYS, "cannot write into file %s", file_copy);
- (void) lockf(fi, F_ULOCK, 0);
- (void) fchmod(fi, s.st_mode);
- (void) close(fi);
- (void) close(fo);
- (void) remove(file_copy);
- return;
}
(void) ftruncate(fi, 0);
diff --git a/usr/src/cmd/login/login.dfl b/usr/src/cmd/login/login.dfl
index e2913bcf55..f747f95201 100644
--- a/usr/src/cmd/login/login.dfl
+++ b/usr/src/cmd/login/login.dfl
@@ -36,7 +36,7 @@
# any of the currently enabled /dev/vt/# virtual terminal devices.
# Comment this line out to allow remote login by root.
#
-CONSOLE=/dev/console
+#CONSOLE=/dev/console
# PASSREQ determines if login requires a password.
#
@@ -47,19 +47,16 @@ PASSREQ=YES
ALTSHELL=YES
# PATH sets the initial shell PATH variable
-# sample with GNU tools in front of the path
-# PATH=/usr/gnu/bin:/usr/bin:/usr/sbin:/sbin
-# sample with XPG4 tools in front of the path
-# PATH=/usr/xpg4/bin:/usr/bin:/usr/sbin:/sbin
-PATH=/usr/bin:/usr/sbin:/sbin:/usr/gnu/bin
+#
+#PATH=/usr/bin:
# SUPATH sets the initial shell PATH variable for root
#
-SUPATH=/usr/sbin:/sbin:/usr/bin
+#SUPATH=/usr/sbin:/usr/bin
# TIMEOUT sets the number of seconds (between 0 and 900) to wait before
# abandoning a login session.
-#
+#
#TIMEOUT=300
# UMASK sets the initial shell file creation mode mask. See umask(1).
@@ -80,9 +77,9 @@ SYSLOG=YES
#SLEEPTIME=4
# DISABLETIME If present, and greater than zero, the number of seconds
-# login will wait after RETRIES failed attempts or the PAM framework returns
+# login will wait after RETRIES failed attempts or the PAM framework returns
# PAM_ABORT. Default is 20. Minimum is 0. No maximum is imposed.
-#
+#
#DISABLETIME=20
# RETRIES determines the number of failed logins that will be
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.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 99013197f9..10c38700ef 100644
--- a/usr/src/cmd/mdb/Makefile.common
+++ b/usr/src/cmd/mdb/Makefile.common
@@ -108,6 +108,7 @@ COMMON_MODULES_KVM = \
stmf_sbd \
ufs \
usba \
+ xhci \
zfs
include $(SRC)/Makefile.master
diff --git a/usr/src/cmd/mdb/Makefile.module b/usr/src/cmd/mdb/Makefile.module
index 8ff1e215d6..41a905cb9a 100644
--- a/usr/src/cmd/mdb/Makefile.module
+++ b/usr/src/cmd/mdb/Makefile.module
@@ -92,7 +92,7 @@ ROOTTGTS_raw = $(ROOTMOD)/$(MODULE)
ROOTTGTS = $(ROOTTGTS_$(MDBTGT))
#
-# Python specific flags. To try and make life easier for folks how are
+# Python specific flags. To try and make life easier for folks who are
# building with an LFS python, we attempt to use -isystem when it's
# available.
#
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
index 9b42344351..00a23aacb9 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
@@ -2173,6 +2173,46 @@ cmd_dis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
}
+static void
+dis_help(void)
+{
+ static const char dis_desc[] =
+"Disassembles instructions starting at the final argument or the current\n"
+"value of dot. If the address is the start of a function, the entire\n"
+"function is disassembled, or else a window of instructions before and after\n"
+"the disassembled address are displayed.\n"
+"\n";
+
+ static const char dis_opts[] =
+" -a Print instruction addresses as numeric values instead of \n"
+" symbolic values.\n"
+" -b Print instruction addresses as both numeric and symbolic "
+"values.\n"
+" -f Read instructions from the target's object file instead of the \n"
+" target's virtual address space.\n"
+" -n instr Display 'instr' instructions before and after the given "
+"address.\n"
+" -w Force window behavior, even at the start of a function.\n"
+"\n";
+
+ static const char dis_examples[] =
+" ::dis\n"
+" clock::dis\n"
+" ::dis gethrtime\n"
+" set_freemem+0x16::dis -n 4\n"
+"\n";
+
+ mdb_printf("%s", dis_desc);
+ (void) mdb_dec_indent(2);
+ mdb_printf("%<b>OPTIONS%</b>\n");
+ (void) mdb_inc_indent(2);
+ mdb_printf("%s", dis_opts);
+ (void) mdb_dec_indent(2);
+ mdb_printf("%<b>EXAMPLES%</b>\n");
+ (void) mdb_inc_indent(2);
+ (void) mdb_printf("%s", dis_examples);
+}
+
/*ARGSUSED*/
static int
walk_step(uintptr_t addr, const void *data, void *private)
@@ -3113,7 +3153,8 @@ const mdb_dcmd_t mdb_dcmd_builtins[] = {
{ "dcmds", "[[-n] pattern]",
"list available debugger commands", cmd_dcmds, cmd_dcmds_help },
{ "delete", "?[id|all]", "delete traced software events", cmd_delete },
- { "dis", "?[-abfw] [-n cnt] [addr]", "disassemble near addr", cmd_dis },
+ { "dis", "?[-abfw] [-n cnt] [addr]", "disassemble near addr", cmd_dis,
+ dis_help },
{ "disasms", NULL, "list available disassemblers", cmd_disasms },
{ "dismode", "[mode]", "get/set disassembly mode", cmd_dismode },
{ "dmods", "[-l] [mod]", "list loaded debugger modules", cmd_dmods },
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
index e5c11927bc..0368f88ad1 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
@@ -838,6 +838,7 @@ static int
member_info_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data)
{
mbr_info_t *mbrp = data;
+ int kind, ret;
if (strcmp(name, mbrp->mbr_member) == 0) {
if (mbrp->mbr_offp != NULL)
@@ -848,7 +849,46 @@ member_info_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data)
return (1);
}
- return (0);
+ /*
+ * C11 as well as earlier GNU extensions allow an embedded struct
+ * or union to be unnamed as long as there are no unambiguous member
+ * names. If we encounter a SOU member with a 0-length name,
+ * recurse into it and see if any of them match.
+ */
+ if (strlen(name) != 0)
+ return (0);
+
+ kind = mdb_ctf_type_kind(id);
+ if (kind == CTF_ERR)
+ return (-1);
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (0);
+
+ /*
+ * Search the unnamed SOU for mbrp->mbr_member, possibly recursing if
+ * it also contains unnamed members. If the desired member is found.
+ * *mbrp->mbr_offp will contain the offset of the member relative to
+ * this unnamed SOU (if the offset was requested) -- i.e.
+ * we effectively have *mbrp->mbr_offp == offsetof("", member). We want
+ * unnamed SOUs to act as members of the enclosing SOUs, so we need to
+ * return the offset as relative to the outer SOU. Since 'off' is
+ * the offset of the unnamed SOU relative to the enclosing SOU (i.e.
+ * off == offsetof(outer, "")), we add the two together to produce the
+ * desired offset. This can recurse as necessary -- the compiler
+ * prevents any ambiguities from occurring (or else it wouldn't be
+ * able to compile the code), and the result will be relative to
+ * the start of the SOU given in the mdb_ctf_member_info() call.
+ */
+ ret = mdb_ctf_member_iter(id, member_info_cb, mbrp);
+ if (ret == -1)
+ return (-1);
+ if (ret == 0)
+ return (0);
+
+ if (mbrp->mbr_offp != NULL)
+ *(mbrp->mbr_offp) += off;
+
+ return (1);
}
int
@@ -856,10 +896,21 @@ mdb_ctf_member_info(mdb_ctf_id_t id, const char *member, ulong_t *offp,
mdb_ctf_id_t *typep)
{
mbr_info_t mbr;
+ /*
+ * We want the resulting offset (if requested -- offp != NULL) to
+ * be relative to the start of _this_ SOU. If we have to search any
+ * embedded unnamed SOUs, instead of merely assigning the resulting
+ * offset value to mbr_offp, we will have to add the offsets of
+ * any nested SOUs along the way (see comments in member_info_cb()).
+ * Therefore, initialize off to 0 here so we do not need to worry about
+ * recursion depth in member_info_cb (otherwise we would need to set
+ * mbr_offp when depth = 1, and add when depth > 1).
+ */
+ ulong_t off = 0;
int rc;
mbr.mbr_member = member;
- mbr.mbr_offp = offp;
+ mbr.mbr_offp = &off;
mbr.mbr_typep = typep;
rc = mdb_ctf_member_iter(id, member_info_cb, &mbr);
@@ -872,6 +923,9 @@ mdb_ctf_member_info(mdb_ctf_id_t id, const char *member, ulong_t *offp,
if (rc == 0)
return (set_errno(EMDB_CTFNOMEMB));
+ if (offp != NULL)
+ *offp = off;
+
return (0);
}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_main.c b/usr/src/cmd/mdb/common/mdb/mdb_main.c
index 8747464328..e1c2ce5fa4 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_main.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_main.c
@@ -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>
@@ -818,9 +819,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 d85bba5e2a..0d6ea3821c 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
@@ -80,11 +80,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 */
@@ -113,10 +108,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 */
@@ -124,7 +127,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 +351,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 +376,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 52b1b80cd9..9bd5028b08 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_print.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_print.c
@@ -1732,6 +1732,52 @@ elt_print(const char *name, mdb_ctf_id_t id, mdb_ctf_id_t base,
mdb_printf("%s%s", pap->pa_prefix,
(depth == 0) ? "" : pap->pa_suffix);
mdb_printf("%s", name);
+
+ /*
+ * When no name is present (i.e. an unnamed struct or union),
+ * display '(anon)' instead only when no prefix is present.
+ * When printing out a struct or union (sou), prefixes are
+ * only present (i.e. !NULL or non-empty) when printing
+ * individual members of that sou, e.g.
+ * `::print struct foo f_member`. When printing an entire sou,
+ * the prefix will be NULL or empty. We end up with:
+ *
+ * > ::print struct foo
+ * {
+ * ...
+ * f_member = 0xabcd
+ * (anon) = {
+ * anon_member = 0x1234
+ * ....
+ * }
+ * ...
+ * }
+ *
+ * and
+ *
+ * > ::print struct foo anon_member
+ * anon_member = 0x1234
+ *
+ * instead of:
+ *
+ * > ::print struct foo
+ * {
+ * ...
+ * f_member = 0xabcd
+ * = {
+ * anon_member = 0x1234
+ * }
+ * ...
+ * }
+ *
+ * and
+ *
+ * > ::print struct foo anon_member
+ * anon_member(anon) = 0x1234
+ */
+ if (depth > 0 && strlen(name) == 0 &&
+ (pap->pa_prefix == NULL || strlen(pap->pa_prefix) == 0))
+ mdb_printf("(anon)");
}
if ((pap->pa_flags & PA_SHOWTYPE) && kind == CTF_K_INTEGER) {
@@ -1750,8 +1796,9 @@ elt_print(const char *name, mdb_ctf_id_t id, mdb_ctf_id_t base,
}
if (depth != 0 ||
- ((pap->pa_flags & PA_SHOWNAME) && pap->pa_prefix != NULL))
+ ((pap->pa_flags & PA_SHOWNAME) && pap->pa_prefix != NULL)) {
mdb_printf("%s ", pap->pa_flags & PA_SHOWVAL ? " =" : "");
+ }
if (depth == 0 && pap->pa_prefix != NULL)
name = pap->pa_prefix;
@@ -2156,10 +2203,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);
@@ -2169,11 +2216,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) {
@@ -2230,7 +2277,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
@@ -2260,6 +2307,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
@@ -2469,8 +2517,10 @@ cmd_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
kind = mdb_ctf_type_kind(rid);
if (last_deref && IS_SOU(kind)) {
char *end;
+ size_t len = strlen(member);
(void) mdb_snprintf(buf, sizeof (buf),
- "%s", member);
+ "%s", (len == 0) ?
+ "<anon>" : member);
end = strrchr(buf, '[');
*end = '\0';
pa.pa_suffix = "->";
@@ -2589,8 +2639,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)
{
size_t size;
mdb_ctf_id_t base;
@@ -2608,7 +2657,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);
}
@@ -2866,7 +2915,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));
@@ -3000,7 +3048,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);
}
/*
@@ -3009,12 +3057,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);
@@ -3198,22 +3240,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"
@@ -3226,13 +3268,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 8addc11a46..cd6995f887 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)
{
@@ -501,31 +496,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/modules/genunix/Makefile.files b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
index 315a3df138..4e1900c44b 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
+++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
@@ -72,6 +72,7 @@ GENUNIX_SRCS = \
pci.c \
pg.c \
rctl.c \
+ refhash.c \
refstr.c \
sobj.c \
streams.c \
diff --git a/usr/src/cmd/mdb/common/modules/genunix/ctxop.c b/usr/src/cmd/mdb/common/modules/genunix/ctxop.c
index d78432b743..c8a59ce9ac 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/ctxop.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/ctxop.c
@@ -27,8 +27,9 @@
* Copyright 2018 Joyent, Inc.
*/
-#include <mdb/mdb_modapi.h>
+#include <sys/mdb_modapi.h>
#include <mdb/mdb_ctf.h>
+#include <sys/thread.h>
#include "ctxop.h"
struct ctxop_walk_state {
diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
index 4a48514d81..30449f0631 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
@@ -99,6 +99,7 @@
#include "pci.h"
#include "pg.h"
#include "rctl.h"
+#include "refhash.h"
#include "sobj.h"
#include "streams.h"
#include "sysevent.h"
@@ -116,10 +117,6 @@
*/
#define NINTR 16
-#define KILOS 10
-#define MEGS 20
-#define GIGS 30
-
#ifndef STACK_BIAS
#define STACK_BIAS 0
#endif
@@ -2136,24 +2133,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 }
};
@@ -2205,15 +2202,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;
@@ -2254,9 +2245,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");
@@ -2265,9 +2254,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)
@@ -2276,20 +2264,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;
@@ -2307,16 +2293,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);
@@ -2331,44 +2311,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");
@@ -2385,7 +2356,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);
}
@@ -4481,6 +4452,8 @@ static const mdb_dcmd_t dcmds[] = {
/* 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 },
@@ -4802,6 +4775,10 @@ static const mdb_walker_t walkers[] = {
{ "rctl_val", "given a rctl_t, walk all rctl_val entries associated",
rctl_val_walk_init, rctl_val_walk_step },
+ /* from refhash.c */
+ { REFHASH_WALK_NAME, REFHASH_WALK_DESC,
+ refhash_walk_init, refhash_walk_step, NULL },
+
/* from sobj.c */
{ "blocked", "walk threads blocked on a given sobj",
blocked_walk_init, blocked_walk_step, NULL },
diff --git a/usr/src/cmd/mdb/common/modules/genunix/refhash.c b/usr/src/cmd/mdb/common/modules/genunix/refhash.c
new file mode 100644
index 0000000000..4b4c92dc14
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/genunix/refhash.c
@@ -0,0 +1,61 @@
+/*
+ * 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 2018, Joyent, Inc.
+ */
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_ctf.h>
+
+#include <inttypes.h>
+#include <sys/refhash.h>
+
+typedef struct refhash_walk_data {
+ size_t rwd_offset;
+} refhash_walk_data_t;
+
+int
+refhash_walk_init(mdb_walk_state_t *wsp)
+{
+ refhash_t refh = { 0 };
+ refhash_walk_data_t *rwd;
+ int offset;
+
+ /* mdb_ctf_offsetof_by_name() will print any errors */
+ if ((offset = mdb_ctf_offsetof_by_name("refhash_t", "rh_objs")) == -1)
+ return (WALK_ERR);
+
+ if (mdb_vread(&refh, sizeof (refhash_t), wsp->walk_addr) == -1) {
+ mdb_warn("failed to read refhash_t at %#lx", wsp->walk_addr);
+ return (WALK_ERR);
+ }
+
+ rwd = wsp->walk_data = mdb_zalloc(sizeof (*rwd), UM_SLEEP | UM_GC);
+ rwd->rwd_offset = refh.rh_link_off;
+
+ wsp->walk_addr += offset;
+ if (mdb_layered_walk("list", wsp) == -1) {
+ mdb_warn("can't walk refhash_t");
+ return (WALK_ERR);
+ }
+
+ return (WALK_NEXT);
+}
+
+int
+refhash_walk_step(mdb_walk_state_t *wsp)
+{
+ refhash_walk_data_t *rwd = wsp->walk_data;
+ uintptr_t addr = wsp->walk_addr - rwd->rwd_offset;
+
+ return (wsp->walk_callback(addr, wsp->walk_layer, wsp->walk_cbdata));
+}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/refhash.h b/usr/src/cmd/mdb/common/modules/genunix/refhash.h
new file mode 100644
index 0000000000..1e91ced8d5
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/genunix/refhash.h
@@ -0,0 +1,35 @@
+/*
+ * 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 2018, Joyent, Inc.
+ */
+
+#ifndef _REFHASH_H
+#define _REFHASH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define REFHASH_WALK_NAME "refhash"
+#define REFHASH_WALK_DESC "walk a refhash"
+
+struct mdb_walk_state;
+
+extern int refhash_walk_init(struct mdb_walk_state *);
+extern int refhash_walk_step(struct mdb_walk_state *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _REFHASH_H */
diff --git a/usr/src/cmd/mdb/common/modules/genunix/zone.c b/usr/src/cmd/mdb/common/modules/genunix/zone.c
index 49cd3e7b6e..57545d94a3 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/zone.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/zone.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2018, Joyent, Inc. All rights reserved.
*/
#include <mdb/mdb_param.h>
@@ -34,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
/*
@@ -52,7 +52,8 @@ char *zone_status_names[] = {
"empty", /* ZONE_IS_EMPTY */
"down", /* ZONE_IS_DOWN */
"dying", /* ZONE_IS_DYING */
- "dead" /* ZONE_IS_DEAD */
+ "dead", /* ZONE_IS_DEAD */
+ "free" /* ZONE_IS_FREE */
};
static int
@@ -80,6 +81,31 @@ zid2zone(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
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)
{
@@ -122,10 +148,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");
}
@@ -164,7 +190,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 {
/*
@@ -172,7 +198,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;
@@ -410,7 +436,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) {
@@ -419,7 +445,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 0881f9bbae..94a383e41c 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/zone.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/zone.h
@@ -34,6 +34,7 @@ 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/ipc/ipc.c b/usr/src/cmd/mdb/common/modules/ipc/ipc.c
index bc87716015..04db8f7fcf 100644
--- a/usr/src/cmd/mdb/common/modules/ipc/ipc.c
+++ b/usr/src/cmd/mdb/common/modules/ipc/ipc.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#include <mdb/mdb_modapi.h>
@@ -228,7 +229,9 @@ shm_print(kshmid_t *shmid, uintptr_t addr)
printtime_nice("ctime: ", shmid->shm_ctime);
mdb_printf("sptinfo: %-?p sptseg: %-?p\n",
shmid->shm_sptinfo, shmid->shm_sptseg);
- mdb_printf("sptprot: <%lb>\n", shmid->shm_sptprot, prot_flag_bits);
+ mdb_printf("opts: rmpend: %d prot: <%b>\n",
+ ((shmid->shm_opts & SHM_RM_PENDING) != 0),
+ (shmid->shm_opts & SHM_PROT_MASK), prot_flag_bits);
}
diff --git a/usr/src/cmd/mdb/common/modules/libc/libc.c b/usr/src/cmd/mdb/common/modules/libc/libc.c
index 69f4beda74..8ee6af407e 100644
--- a/usr/src/cmd/mdb/common/modules/libc/libc.c
+++ b/usr/src/cmd/mdb/common/modules/libc/libc.c
@@ -138,6 +138,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);
}
@@ -849,14 +851,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",
@@ -879,12 +886,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/mdb_ks/mdb_ks.c b/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c
index 85f3839c96..68b2e9d362 100644
--- a/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c
+++ b/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c
@@ -60,6 +60,12 @@
#define MDB_PATH_NELEM 256 /* Maximum path components */
+/*
+ * Due to mdb_param.h shenanigans, there's no simple way to include string.h
+ * here...
+ */
+extern char *strtok(char *restrict, const char *restrict);
+
typedef struct mdb_path {
size_t mdp_nelem; /* Number of components */
uint_t mdp_complete; /* Path completely resolved? */
@@ -1811,18 +1817,73 @@ mdb_get_lbolt(void)
return ((ts/nsec) - lbi.lbi_debug_time);
}
+#define startswith(a, b) (strncmp((a), (b), strlen(b)) == 0)
+
+/*
+ * Dig out the branch and revision of the illumos-joyent repo, if we were
+ * provided with it. This is a rather fragile JSON parser, in that it requires
+ * JSON formatted exactly as per the boot_archive.gitstatus file that
+ * "buildversion" is built from.
+ */
void
mdb_print_buildversion(void)
{
+ boolean_t in_joyent = B_FALSE;
GElf_Sym sym;
- if (mdb_lookup_by_name("buildversion", &sym) != 0)
- return;
+ if (mdb_lookup_by_name("buildversion", &sym) != 0) {
+ /* Older kernels used this name. */
+ if (mdb_lookup_by_name("gitstatus_start", &sym) != 0)
+ return;
+ }
char *str = mdb_zalloc(4096, UM_SLEEP | UM_GC);
if (mdb_readstr(str, 4096, sym.st_value) < 1)
return;
- mdb_printf("build version: %s\n", str);
+ /*
+ * Each line is of the form
+ *
+ * "repo": "smartos-live",
+ */
+ for (char *line = strtok(str, "\n"); line != NULL;
+ line = strtok(NULL, "\n")) {
+ /* skip whitespace and first " */
+ line += strspn(line, " \t\"");
+
+ if (startswith(line, "repo")) {
+ line += sizeof ("repo") - 1;
+ line += strspn(line, " \t\":");
+
+ if (startswith(line, "illumos-joyent"))
+ in_joyent = B_TRUE;
+ else if (in_joyent)
+ return;
+ continue;
+ }
+
+ if (!in_joyent)
+ continue;
+
+ if (startswith(line, "branch")) {
+ char *trail = strrchr(line, '"');
+ if (trail != NULL)
+ *trail = '\0';
+ line += sizeof ("branch") - 1;
+ line += strspn(line, " \t\":");
+ mdb_printf("git branch: %s\n", line);
+ continue;
+ }
+
+ if (startswith(line, "rev")) {
+ char *trail = strrchr(line, '"');
+ if (trail != NULL)
+ *trail = '\0';
+ line += sizeof ("rev") - 1;
+ line += strspn(line, " \t\":");
+ mdb_printf("git rev: %s\n", line);
+ continue;
+ }
+ }
}
diff --git a/usr/src/cmd/mdb/common/modules/random/random.c b/usr/src/cmd/mdb/common/modules/random/random.c
index a1b41f0eb8..2da56dae32 100644
--- a/usr/src/cmd/mdb/common/modules/random/random.c
+++ b/usr/src/cmd/mdb/common/modules/random/random.c
@@ -24,7 +24,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
#include <sys/mdb_modapi.h>
#include <mdb/mdb_ctf.h>
@@ -39,10 +41,9 @@ rnd_get_stats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
rnd_stats_t rnd_stats, rnd_stats_cpu;
uint32_t random_max_ncpus;
- size_t rndmag_t_size;
+ size_t rndmag_pad_t_size;
ulong_t rndmag_t_offset;
uintptr_t rndmag;
- mdb_ctf_id_t rndmag_id;
int i;
if ((flags & DCMD_ADDRSPEC) || argc != 0)
@@ -53,23 +54,20 @@ rnd_get_stats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_ERR);
}
- if ((mdb_ctf_lookup_by_name("rndmag_t", &rndmag_id) != 0) ||
- (mdb_ctf_offsetof(rndmag_id, "rm_stats", &rndmag_t_offset) != 0) ||
+ if (((rndmag_t_offset = mdb_ctf_offsetof_by_name("rndmag_t", "rm_stats")) == -1) ||
(mdb_readvar(&random_max_ncpus, "random_max_ncpus") == -1) ||
(mdb_readvar(&rndmag, "rndmag") == -1) ||
- ((rndmag_t_size = mdb_ctf_type_size(rndmag_id)) == 0)) {
+ ((rndmag_pad_t_size = mdb_ctf_sizeof_by_name("rndmag_pad_t")) == -1)) {
/* Can't find per-cpu stats. Don't add them in. */
random_max_ncpus = 0;
}
- rndmag_t_offset /= 8;
-
/*
* Read and aggregate per-cpu stats if we have them.
*/
for (i = 0; i < random_max_ncpus; i++) {
mdb_vread(&rnd_stats_cpu, sizeof (rnd_stats_cpu),
- rndmag + rndmag_t_offset + i * rndmag_t_size);
+ rndmag + rndmag_t_offset + i * rndmag_pad_t_size);
rnd_stats.rs_rndOut += rnd_stats_cpu.rs_rndOut;
rnd_stats.rs_rndcOut += rnd_stats_cpu.rs_rndcOut;
diff --git a/usr/src/cmd/mdb/common/modules/xhci/xhci.c b/usr/src/cmd/mdb/common/modules/xhci/xhci.c
new file mode 100644
index 0000000000..1a1882e738
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/xhci/xhci.c
@@ -0,0 +1,893 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <sys/mdb_modapi.h>
+#include <sys/usb/hcd/xhci/xhci.h>
+
+#define XHCI_MDB_TRB_INDENT 4
+
+static const char *xhci_mdb_epctx_eptypes[] = {
+ "Not Valid",
+ "ISOCH OUT",
+ "BULK OUT",
+ "INTR OUT",
+ "CONTROL",
+ "ISOCH IN",
+ "BULK IN",
+ "INTR IN"
+};
+
+static const char *xhci_mdb_epctx_states[] = {
+ "Disabled",
+ "Running",
+ "Halted",
+ "Stopped",
+ "Error",
+ "<Unknown>",
+ "<Unknown>",
+ "<Unknown>"
+};
+
+static const mdb_bitmask_t xhci_mdb_trb_flags[] = {
+ { "C", XHCI_TRB_CYCLE, XHCI_TRB_CYCLE },
+ { "ENT", XHCI_TRB_ENT, XHCI_TRB_ENT },
+ { "ISP", XHCI_TRB_ISP, XHCI_TRB_ISP },
+ { "NS", XHCI_TRB_NOSNOOP, XHCI_TRB_NOSNOOP },
+ { "CH", XHCI_TRB_CHAIN, XHCI_TRB_CHAIN },
+ { "IOC", XHCI_TRB_IOC, XHCI_TRB_IOC },
+ { "IDT", XHCI_TRB_IDT, XHCI_TRB_IDT },
+ { "BEI", XHCI_TRB_BEI, XHCI_TRB_BEI },
+ { NULL, 0, 0 }
+};
+
+typedef struct xhci_mdb_walk_endpoint {
+ xhci_device_t xmwe_device;
+ uint_t xmwe_ep;
+} xhci_mdb_walk_endpoint_t;
+
+static const char *
+xhci_mdb_trb_code_to_str(int code)
+{
+ switch (code) {
+ case XHCI_CODE_INVALID:
+ return ("Invalid");
+ case XHCI_CODE_SUCCESS:
+ return ("Success");
+ case XHCI_CODE_DATA_BUF:
+ return ("Data Overrun or Underrun");
+ case XHCI_CODE_BABBLE:
+ return ("Babble");
+ case XHCI_CODE_TXERR:
+ return ("Transaction Error");
+ case XHCI_CODE_TRB:
+ return ("Invalid TRB");
+ case XHCI_CODE_STALL:
+ return ("Stall");
+ case XHCI_CODE_RESOURCE:
+ return ("No Resources Available");
+ case XHCI_CODE_BANDWIDTH:
+ return ("No Bandwidth Available");
+ case XHCI_CODE_NO_SLOTS:
+ return ("No Slots Available");
+ case XHCI_CODE_STREAM_TYPE:
+ return ("Stream Context Type Detected");
+ case XHCI_CODE_SLOT_NOT_ON:
+ return ("Slot disabled");
+ case XHCI_CODE_ENDP_NOT_ON:
+ return ("Endpoint disabled");
+ case XHCI_CODE_SHORT_XFER:
+ return ("Short Transfer");
+ case XHCI_CODE_RING_UNDERRUN:
+ return ("Isoch. Ring Underrun");
+ case XHCI_CODE_RING_OVERRUN:
+ return ("Isoch. Ring Overrun");
+ case XHCI_CODE_VF_RING_FULL:
+ return ("VF Ring Full");
+ case XHCI_CODE_PARAMETER:
+ return ("Invalid Context Paramenter");
+ case XHCI_CODE_BW_OVERRUN:
+ return ("Bandwidth Overrun");
+ case XHCI_CODE_CONTEXT_STATE:
+ return ("Illegal Context Transition");
+ case XHCI_CODE_NO_PING_RESP:
+ return ("Failed to Complete Periodic Transfer");
+ case XHCI_CODE_EV_RING_FULL:
+ return ("Event Ring Full");
+ case XHCI_CODE_INCOMPAT_DEV:
+ return ("Incompatible Device");
+ case XHCI_CODE_MISSED_SRV:
+ return ("Missed Isoch. Service Window");
+ case XHCI_CODE_CMD_RING_STOP:
+ return ("Command Ring Stop");
+ case XHCI_CODE_CMD_ABORTED:
+ return ("Command Aborted");
+ case XHCI_CODE_XFER_STOPPED:
+ return ("Transfer Stopped");
+ case XHCI_CODE_XFER_STOPINV:
+ return ("Invalid Transfer Length");
+ case XHCI_CODE_XFER_STOPSHORT:
+ return ("Stopped before End of Transfer Descriptor");
+ case XHCI_CODE_MELAT:
+ return ("Max Exit Latency too large");
+ case XHCI_CODE_RESERVED:
+ return ("Reserved");
+ case XHCI_CODE_ISOC_OVERRUN:
+ return ("Isochronus Overrun");
+ case XHCI_CODE_EVENT_LOST:
+ return ("Event Lost");
+ case XHCI_CODE_UNDEFINED:
+ return ("Undefined Fatal Error");
+ case XHCI_CODE_INVALID_SID:
+ return ("Invalid Stream ID");
+ case XHCI_CODE_SEC_BW:
+ return ("Secondary Bandwith Allocation Failure");
+ case XHCI_CODE_SPLITERR:
+ return ("USB2 SPlit Transaction Error");
+ default:
+ break;
+ }
+
+ if (code >= 192 && code <= 223)
+ return ("Vendor Defined Error");
+ if (code >= 224 && code <= 255)
+ return ("Vendor Defined Info");
+
+ return ("Reserved");
+}
+
+static const char *
+xhci_mdb_trb_type_to_str(int code)
+{
+ /*
+ * The macros for the types are all already shifted over based on their
+ * place in the TRB, so shift there again ourselves.
+ */
+ switch (code << 10) {
+ case XHCI_TRB_TYPE_NORMAL:
+ return ("Normal");
+ case XHCI_TRB_TYPE_SETUP:
+ return ("Setup");
+ case XHCI_TRB_TYPE_DATA:
+ return ("Data");
+ case XHCI_TRB_TYPE_STATUS:
+ return ("Status");
+ case XHCI_TRB_TYPE_LINK:
+ return ("Link");
+ case XHCI_TRB_TYPE_EVENT:
+ return ("Event");
+ case XHCI_TRB_TYPE_NOOP:
+ return ("No-Op");
+ case XHCI_CMD_ENABLE_SLOT:
+ return ("Enable Slot");
+ case XHCI_CMD_DISABLE_SLOT:
+ return ("Disable Slot");
+ case XHCI_CMD_ADDRESS_DEVICE:
+ return ("Address Device");
+ case XHCI_CMD_CONFIG_EP:
+ return ("Configure Endpoint");
+ case XHCI_CMD_EVAL_CTX:
+ return ("Evaluate Context");
+ case XHCI_CMD_RESET_EP:
+ return ("Reset Endpoint");
+ case XHCI_CMD_STOP_EP:
+ return ("Stop Endpoint");
+ case XHCI_CMD_SET_TR_DEQ:
+ return ("Set Transfer Ring Dequeue Pointer");
+ case XHCI_CMD_RESET_DEV:
+ return ("Reset Device");
+ case XHCI_CMD_FEVENT:
+ return ("Force Event");
+ case XHCI_CMD_NEG_BW:
+ return ("Negotiate Bandwidth");
+ case XHCI_CMD_SET_LT:
+ return ("Set Latency Tolerance");
+ case XHCI_CMD_GET_BW:
+ return ("Get Bandwidth");
+ case XHCI_CMD_FHEADER:
+ return ("Force Header");
+ case XHCI_CMD_NOOP:
+ return ("No-Op Command");
+ case XHCI_EVT_XFER:
+ return ("Transfer Event");
+ case XHCI_EVT_CMD_COMPLETE:
+ return ("Command Completion Event");
+ case XHCI_EVT_PORT_CHANGE:
+ return ("Port Status Change Event");
+ case XHCI_EVT_BW_REQUEST:
+ return ("Bandwidth Request Event");
+ case XHCI_EVT_DOORBELL:
+ return ("Doorbell Event");
+ case XHCI_EVT_HOST_CTRL:
+ return ("Host Controller Event");
+ case XHCI_EVT_DEVICE_NOTIFY:
+ return ("Device Notification Event");
+ case XHCI_EVT_MFINDEX_WRAP:
+ return ("MFINDEX Wrap Event");
+ default:
+ break;
+ }
+
+ if (code >= 43 && code <= 63)
+ return ("Vendor Defiend");
+ return ("Reserved");
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_epctx(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ uint32_t info, info2, txinfo;
+ xhci_endpoint_context_t epctx;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ mdb_warn("::xhci_epctx requires an address\n");
+ return (DCMD_USAGE);
+ }
+
+ if (mdb_vread(&epctx, sizeof (epctx), addr) != sizeof (epctx)) {
+ mdb_warn("failed to read xhci_endpoint_context_t at %p", addr);
+ return (DCMD_ERR);
+ }
+
+ info = LE_32(epctx.xec_info);
+ info2 = LE_32(epctx.xec_info2);
+ txinfo = LE_32(epctx.xec_txinfo);
+
+ mdb_printf("Endpoint State: %s (%d)\n",
+ xhci_mdb_epctx_states[XHCI_EPCTX_STATE(info)],
+ XHCI_EPCTX_STATE(info));
+
+ mdb_printf("Mult: %d\n", XHCI_EPCTX_GET_MULT(info));
+ mdb_printf("Max Streams: %d\n", XHCI_EPCTX_GET_MAXP_STREAMS(info));
+ mdb_printf("LSA: %d\n", XHCI_EPCTX_GET_LSA(info));
+ mdb_printf("Interval: %d\n", XHCI_EPCTX_GET_IVAL(info));
+ mdb_printf("Max ESIT Hi: %d\n", XHCI_EPCTX_GET_MAX_ESIT_HI(info));
+
+ mdb_printf("CErr: %d\n", XHCI_EPCTX_GET_CERR(info2));
+ mdb_printf("EP Type: %s (%d)\n",
+ xhci_mdb_epctx_eptypes[XHCI_EPCTX_GET_EPTYPE(info2)],
+ XHCI_EPCTX_GET_EPTYPE(info2));
+ mdb_printf("Host Initiate Disable: %d\n", XHCI_EPCTX_GET_HID(info2));
+ mdb_printf("Max Burst: %d\n", XHCI_EPCTX_GET_MAXB(info2));
+ mdb_printf("Max Packet Size: %d\n", XHCI_EPCTX_GET_MPS(info2));
+
+ mdb_printf("Ring DCS: %d\n", LE_64(epctx.xec_dequeue) & 0x1);
+ mdb_printf("Ring PA: 0x%lx\n", LE_64(epctx.xec_dequeue) & ~0xf);
+
+ mdb_printf("Average TRB Length: %d\n", XHCI_EPCTX_AVG_TRB_LEN(txinfo));
+ mdb_printf("Max ESIT: %d\n", XHCI_EPCTX_GET_MAX_ESIT_PAYLOAD(txinfo));
+
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_slotctx(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ uint32_t info, info2, tt, state;
+ xhci_slot_context_t sctx;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ mdb_warn("::xhci_slotctx requires an address\n");
+ return (DCMD_USAGE);
+ }
+
+ if (mdb_vread(&sctx, sizeof (sctx), addr) != sizeof (sctx)) {
+ mdb_warn("failed to read xhci_slot_context_t at %p", addr);
+ return (DCMD_ERR);
+ }
+
+ info = LE_32(sctx.xsc_info);
+ info2 = LE_32(sctx.xsc_info2);
+ tt = LE_32(sctx.xsc_tt);
+ state = LE_32(sctx.xsc_state);
+
+ mdb_printf("Route: 0x%x\n", XHCI_SCTX_GET_ROUTE(info));
+
+ mdb_printf("Slot Speed: ");
+ switch (XHCI_SCTX_GET_SPEED(info)) {
+ case XHCI_SPEED_FULL:
+ mdb_printf("Full");
+ break;
+ case XHCI_SPEED_LOW:
+ mdb_printf("Low");
+ break;
+ case XHCI_SPEED_HIGH:
+ mdb_printf("High");
+ break;
+ case XHCI_SPEED_SUPER:
+ mdb_printf("Super");
+ break;
+ default:
+ mdb_printf("Unknown");
+ break;
+ }
+ mdb_printf(" (%d)\n", XHCI_SCTX_GET_SPEED(info));
+
+
+ mdb_printf("MTT: %d\n", XHCI_SCTX_GET_MTT(info));
+ mdb_printf("HUB: %d\n", XHCI_SCTX_GET_HUB(info));
+ mdb_printf("DCI: %d\n", XHCI_SCTX_GET_DCI(info));
+
+ mdb_printf("Max Exit Latency: %d\n", XHCI_SCTX_GET_MAX_EL(info2));
+ mdb_printf("Root Hub Port: %d\n", XHCI_SCTX_GET_RHPORT(info2));
+ mdb_printf("Hub Number of Ports: %d\n", XHCI_SCTX_GET_NPORTS(info2));
+
+ mdb_printf("TT Hub Slot id: %d\n", XHCI_SCTX_GET_TT_HUB_SID(tt));
+ mdb_printf("TT Port Number: %d\n", XHCI_SCTX_GET_TT_PORT_NUM(tt));
+ mdb_printf("TT Think Time: %d\n", XHCI_SCTX_GET_TT_THINK_TIME(tt));
+ mdb_printf("IRQ Target: %d\n", XHCI_SCTX_GET_IRQ_TARGET(tt));
+
+ mdb_printf("Device Address: 0x%x\n", XHCI_SCTX_GET_DEV_ADDR(state));
+ mdb_printf("Slot State: ");
+ switch (XHCI_SCTX_GET_SLOT_STATE(state)) {
+ case XHCI_SLOT_DIS_ENAB:
+ mdb_printf("Disabled/Enabled");
+ break;
+ case XHCI_SLOT_DEFAULT:
+ mdb_printf("Default");
+ break;
+ case XHCI_SLOT_ADDRESSED:
+ mdb_printf("Addressed");
+ break;
+ case XHCI_SLOT_CONFIGURED:
+ mdb_printf("Configured");
+ break;
+ default:
+ mdb_printf("Unknown");
+ break;
+ }
+ mdb_printf(" (%d)\n", XHCI_SCTX_GET_SLOT_STATE(state));
+
+ return (DCMD_OK);
+}
+
+static int
+xhci_mdb_print_transfer_event(uint64_t pa, uint32_t status, uint32_t flags)
+{
+ mdb_printf("TRB Address: 0x%lx\n", pa);
+ mdb_printf("Transfer Length (Remain): %d\n", XHCI_TRB_REMAIN(status));
+ mdb_printf("Completion Code: %s (%d)\n",
+ xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)),
+ XHCI_TRB_GET_CODE(status));
+
+ mdb_printf("Cycle: %d\n", XHCI_TRB_GET_CYCLE(flags));
+ mdb_printf("Event Data: %d\n", XHCI_TRB_GET_ED(flags));
+ mdb_printf("Endpoint ID: %d\n", XHCI_TRB_GET_EP(flags));
+ mdb_printf("Slot ID: %d\n", XHCI_TRB_GET_SLOT(flags));
+ mdb_dec_indent(XHCI_MDB_TRB_INDENT);
+
+ return (DCMD_OK);
+}
+
+static int
+xhci_mdb_print_command_event(uint64_t pa, uint32_t status, uint32_t flags)
+{
+ mdb_printf("TRB Address: 0x%lx\n", pa);
+ mdb_printf("Command Param: 0x%x\n", XHCI_TRB_REMAIN(status));
+ mdb_printf("Completion Code: %s (%d)\n",
+ xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)),
+ XHCI_TRB_GET_CODE(status));
+
+ mdb_printf("Cycle: %d\n", XHCI_TRB_GET_CYCLE(flags));
+ /* Skip VF ID as we don't support VFs */
+ mdb_printf("Slot ID: %d\n", XHCI_TRB_GET_SLOT(flags));
+ mdb_dec_indent(XHCI_MDB_TRB_INDENT);
+
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_psc(uint64_t pa, uint32_t status, uint32_t flags)
+{
+ mdb_printf("Port: %d\n", XHCI_TRB_PORTID(pa));
+ mdb_printf("Completion Code: %s (%d)\n",
+ xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)),
+ XHCI_TRB_GET_CODE(status));
+ mdb_dec_indent(XHCI_MDB_TRB_INDENT);
+ return (DCMD_OK);
+}
+
+static int
+xhci_mdb_print_normal_trb(uint64_t pa, uint32_t status, uint32_t flags)
+{
+ mdb_printf("TRB Address: 0x%lx\n", pa);
+ mdb_printf("TRB Length: %d bytes\n", XHCI_TRB_LEN(status));
+ mdb_printf("TRB TD Size: %d packets\n", XHCI_TRB_GET_TDREM(status));
+ mdb_printf("TRB Interrupt: %d\n", XHCI_TRB_GET_INTR(status));
+ mdb_printf("TRB Flags: %b (0x%x)\n", flags, xhci_mdb_trb_flags,
+ XHCI_TRB_GET_FLAGS(flags));
+ mdb_dec_indent(XHCI_MDB_TRB_INDENT);
+
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_trb(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ xhci_trb_t trb;
+ uint64_t pa;
+ uint32_t status, trbflags, type;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ mdb_warn("::xhci_trb expects an address\n");
+ return (DCMD_USAGE);
+ }
+
+ if (mdb_vread(&trb, sizeof (trb), addr) != sizeof (trb)) {
+ mdb_warn("failed to read xhci_trb_t at 0x%x", addr);
+ return (DCMD_ERR);
+ }
+
+ pa = LE_64(trb.trb_addr);
+ status = LE_32(trb.trb_status);
+ trbflags = LE_32(trb.trb_flags);
+
+ type = XHCI_TRB_GET_TYPE(trbflags);
+
+ if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST))
+ mdb_printf("\n");
+
+ mdb_set_dot(addr + sizeof (xhci_trb_t));
+ mdb_printf("%s TRB (%d)\n", xhci_mdb_trb_type_to_str(type), type);
+ mdb_inc_indent(XHCI_MDB_TRB_INDENT);
+
+ switch (XHCI_RING_TYPE_SHIFT(type)) {
+ case XHCI_EVT_XFER:
+ return (xhci_mdb_print_transfer_event(pa, status, trbflags));
+ case XHCI_EVT_CMD_COMPLETE:
+ return (xhci_mdb_print_command_event(pa, status, trbflags));
+ case XHCI_EVT_PORT_CHANGE:
+ return (xhci_mdb_print_psc(pa, status, trbflags));
+ case XHCI_TRB_TYPE_NORMAL:
+ return (xhci_mdb_print_normal_trb(pa, status, trbflags));
+ }
+
+ /*
+ * Just print generic information if we don't have a specific printer
+ * for that TRB type.
+ */
+ mdb_printf("TRB Address: 0x%lx\n", pa);
+ mdb_printf("TRB Status: 0x%x\n", status);
+ mdb_printf("TRB Flags: 0x%x\n", trbflags);
+ mdb_dec_indent(XHCI_MDB_TRB_INDENT);
+
+ return (DCMD_OK);
+}
+
+static int
+xhci_mdb_walk_xhci_init(mdb_walk_state_t *wsp)
+{
+ GElf_Sym sym;
+ uintptr_t addr;
+
+ if (wsp->walk_addr != 0) {
+ mdb_warn("::walk xhci only supports global walks\n");
+ return (WALK_ERR);
+ }
+
+ if (mdb_lookup_by_obj("xhci", "xhci_soft_state", &sym) != 0) {
+ mdb_warn("failed to find xhci_soft_state symbol");
+ return (WALK_ERR);
+ }
+
+ if (mdb_vread(&addr, sizeof (addr), sym.st_value) != sizeof (addr)) {
+ mdb_warn("failed to read xhci_soft_state at %p", addr);
+ return (WALK_ERR);
+ }
+
+ wsp->walk_addr = addr;
+ if (mdb_layered_walk("softstate", wsp) != 0) {
+ mdb_warn("failed to walk softstate");
+ return (WALK_ERR);
+ }
+
+ return (WALK_NEXT);
+}
+
+static int
+xhci_mdb_walk_xhci_step(mdb_walk_state_t *wsp)
+{
+ xhci_t xhci;
+
+ if (mdb_vread(&xhci, sizeof (xhci), wsp->walk_addr) != sizeof (xhci)) {
+ mdb_warn("failed to read xhci_t at %p", wsp->walk_addr);
+ return (WALK_ERR);
+ }
+
+ return (wsp->walk_callback(wsp->walk_addr, &xhci, wsp->walk_cbdata));
+}
+
+static int
+xhci_mdb_walk_xhci_device_init(mdb_walk_state_t *wsp)
+{
+ uintptr_t addr;
+
+ if (wsp->walk_addr == 0) {
+ mdb_warn("::walk xhci_device requires an xhci_t\n");
+ return (WALK_ERR);
+ }
+
+ addr = wsp->walk_addr;
+ addr += offsetof(xhci_t, xhci_usba);
+ addr += offsetof(xhci_usba_t, xa_devices);
+ wsp->walk_addr = (uintptr_t)addr;
+ if (mdb_layered_walk("list", wsp) != 0) {
+ mdb_warn("failed to walk list");
+ return (WALK_ERR);
+ }
+
+ return (WALK_NEXT);
+}
+
+static int
+xhci_mdb_walk_xhci_device_step(mdb_walk_state_t *wsp)
+{
+ xhci_device_t xd;
+
+ if (mdb_vread(&xd, sizeof (xd), wsp->walk_addr) != sizeof (xd)) {
+ mdb_warn("failed to read xhci_device_t at %p", wsp->walk_addr);
+ return (WALK_ERR);
+ }
+
+ return (wsp->walk_callback(wsp->walk_addr, &xd, wsp->walk_cbdata));
+}
+
+static int
+xhci_mdb_walk_xhci_endpoint_init(mdb_walk_state_t *wsp)
+{
+ xhci_mdb_walk_endpoint_t *xm;
+ xhci_device_t *xd;
+
+ if (wsp->walk_addr == 0) {
+ mdb_warn("::walk xhci_endpoint requires an xhci_device_t\n");
+ return (WALK_ERR);
+ }
+
+ xm = mdb_alloc(sizeof (xhci_mdb_walk_endpoint_t), UM_SLEEP | UM_GC);
+ xm->xmwe_ep = 0;
+ xd = &xm->xmwe_device;
+ if (mdb_vread(xd, sizeof (*xd), wsp->walk_addr) != sizeof (*xd)) {
+ mdb_warn("failed to read xhci_endpoint_t at %p",
+ wsp->walk_addr);
+ return (WALK_ERR);
+ }
+ wsp->walk_data = xm;
+
+ return (WALK_NEXT);
+}
+
+static int
+xhci_mdb_walk_xhci_endpoint_step(mdb_walk_state_t *wsp)
+{
+ int ret;
+ uintptr_t addr;
+ xhci_mdb_walk_endpoint_t *xm = wsp->walk_data;
+
+ if (xm->xmwe_ep >= XHCI_NUM_ENDPOINTS)
+ return (WALK_DONE);
+
+ addr = (uintptr_t)xm->xmwe_device.xd_endpoints[xm->xmwe_ep];
+ if (addr != (uintptr_t)NULL) {
+ xhci_endpoint_t xe;
+
+ if (mdb_vread(&xe, sizeof (xe), addr) != sizeof (xe)) {
+ mdb_warn("failed to read xhci_endpoint_t at %p",
+ xm->xmwe_device.xd_endpoints[xm->xmwe_ep]);
+ return (WALK_ERR);
+ }
+
+ ret = wsp->walk_callback(addr, &xe, wsp->walk_cbdata);
+ } else {
+ ret = WALK_NEXT;
+ }
+ xm->xmwe_ep++;
+
+ return (ret);
+}
+
+typedef struct xhci_mdb_find {
+ int xmf_slot;
+ int xmf_ep;
+ uintptr_t xmf_addr;
+} xhci_mdb_find_t;
+
+static int
+xhci_mdb_find_endpoint_cb(uintptr_t addr, const void *data, void *arg)
+{
+ const xhci_endpoint_t *xep = data;
+ xhci_mdb_find_t *xmf = arg;
+
+ /*
+ * The endpoints that are presented here are off by one from the actual
+ * endpoint ID in the xhci_endpoint_t, as we're really displaying the
+ * index into the device input context.
+ */
+ if (xep->xep_num + 1 == xmf->xmf_ep) {
+ xmf->xmf_addr = addr;
+ return (WALK_DONE);
+ }
+
+ return (WALK_NEXT);
+}
+
+static int
+xhci_mdb_find_device_cb(uintptr_t addr, const void *data, void *arg)
+{
+ const xhci_device_t *xd = data;
+ xhci_mdb_find_t *xmf = arg;
+
+ if (xd->xd_slot == xmf->xmf_slot) {
+ if (xmf->xmf_ep == -1) {
+ xmf->xmf_addr = addr;
+ return (WALK_DONE);
+ }
+
+ if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_find_endpoint_cb,
+ xmf, addr) == -1) {
+ mdb_warn("failed to walk xhci_endpoint at %p", addr);
+ return (WALK_ERR);
+ }
+
+ return (WALK_DONE);
+ }
+
+ return (WALK_NEXT);
+}
+
+static int
+xhci_mdb_find(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ uintptr_t ep, slot;
+ boolean_t ep_set, slot_set;
+ xhci_mdb_find_t xmf;
+
+ if ((flags & DCMD_ADDRSPEC) == 0)
+ return (DCMD_USAGE);
+
+ ep_set = slot_set = B_FALSE;
+ if (mdb_getopts(argc, argv, 'e', MDB_OPT_UINTPTR_SET, &ep_set, &ep,
+ 's', MDB_OPT_UINTPTR_SET, &slot_set, &slot, NULL) != argc)
+ return (DCMD_USAGE);
+
+ if (!slot_set) {
+ mdb_warn("-s is required\n");
+ return (DCMD_USAGE);
+ }
+
+ xmf.xmf_slot = (int)slot;
+ if (ep_set)
+ xmf.xmf_ep = (int)ep;
+ else
+ xmf.xmf_ep = -1;
+ xmf.xmf_addr = 0;
+
+ if (mdb_pwalk("xhci`xhci_device", xhci_mdb_find_device_cb,
+ &xmf, addr) == -1) {
+ mdb_warn("failed to walk xhci_device at %p", addr);
+ return (DCMD_ERR);
+ }
+
+ if (xmf.xmf_addr == 0) {
+ if (ep_set) {
+ mdb_warn("failed to find xhci_endpoint_t for slot %d "
+ "and endpoint %d\n", slot, ep);
+ } else {
+ mdb_warn("failed to find xhci_device_t for slot %d\n",
+ slot);
+ }
+ return (DCMD_ERR);
+ }
+
+ mdb_printf("%p\n", xmf.xmf_addr);
+ return (DCMD_OK);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_endpoint_count(uintptr_t addr, const void *ep, void *arg)
+{
+ int *countp = arg;
+
+ *countp += 1;
+ return (WALK_NEXT);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_endpoint_summary(uintptr_t addr, const void *ep, void *arg)
+{
+ const xhci_device_t *xd = arg;
+ const xhci_endpoint_t *xep = ep;
+ const char *type;
+ const char *state;
+ xhci_endpoint_context_t epctx;
+ int eptype;
+
+ if (mdb_vread(&epctx, sizeof (epctx),
+ (uintptr_t)xd->xd_endout[xep->xep_num]) != sizeof (epctx)) {
+ mdb_warn("failed to read endpoint context at %p",
+ xd->xd_endout[xep->xep_num]);
+ return (WALK_ERR);
+ }
+
+ eptype = XHCI_EPCTX_GET_EPTYPE(LE_32(epctx.xec_info2));
+ type = xhci_mdb_epctx_eptypes[eptype];
+ state = xhci_mdb_epctx_states[XHCI_EPCTX_STATE(LE_32(epctx.xec_info))];
+
+ mdb_printf("%-4d %-10s %-10s 0x%-04x 0x%-04x\n", xep->xep_num, type,
+ state, xep->xep_ring.xr_head, xep->xep_ring.xr_tail);
+
+ return (WALK_NEXT);
+}
+
+/* ARGSUSED */
+static int
+xhci_mdb_print_device(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ int count;
+ xhci_device_t xd;
+ usba_device_t ud;
+ char product[256], mfg[256];
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ return (mdb_eval("::walk xhci`xhci | ::walk xhci`xhci_device | "
+ "::xhci_device"));
+ }
+
+ if (mdb_vread(&xd, sizeof (xd), addr) != sizeof (xd)) {
+ mdb_warn("failed to read xhci_device_t at 0x%x", addr);
+ return (DCMD_ERR);
+ }
+
+ if (mdb_vread(&ud, sizeof (ud), (uintptr_t)xd.xd_usbdev) !=
+ sizeof (ud)) {
+ mdb_warn("failed to read usba_device_t at %p\n", xd.xd_usbdev);
+ return (DCMD_ERR);
+ }
+
+ if (ud.usb_mfg_str == NULL || mdb_readstr(mfg, sizeof (mfg),
+ (uintptr_t)ud.usb_mfg_str) <= 0) {
+ (void) strlcpy(mfg, "Unknown Manufacturer", sizeof (mfg));
+ }
+
+ if (ud.usb_product_str == NULL || mdb_readstr(product, sizeof (product),
+ (uintptr_t)ud.usb_product_str) <= 0) {
+ (void) strlcpy(product, "Unknown Product", sizeof (product));
+ }
+
+ mdb_printf("%<b>%s - %s%</b>\n", mfg, product);
+
+ count = 0;
+ if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_endpoint_count, &count,
+ addr) == -1) {
+ mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr);
+ return (DCMD_ERR);
+ }
+
+ mdb_printf("Port %02d | Slot %02d | # Endpoints %02d\n", xd.xd_port,
+ xd.xd_slot, count);
+ mdb_printf("%<u>%-4s %-10s %-10s %-6s %-6s%</u>\n", "EP", "Type",
+ "State", "Head", "Tail");
+
+ if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_print_endpoint_summary,
+ &xd, addr) == -1) {
+ mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr);
+ return (DCMD_ERR);
+ }
+
+
+ mdb_printf("\n");
+
+ return (DCMD_OK);
+}
+
+static int
+xhci_mdb_find_trb(uintptr_t addr, uint_t flags, int argc,
+ const mdb_arg_t *argv)
+{
+ xhci_ring_t xr;
+ uint64_t base, max, target;
+
+ if (!(flags & DCMD_ADDRSPEC)) {
+ mdb_warn("missing required xhci_ring_t\n");
+ return (DCMD_USAGE);
+ }
+
+ if (argc == 0) {
+ mdb_warn("missing required PA of ring\n");
+ return (DCMD_USAGE);
+ }
+
+ if (argc > 1) {
+ mdb_warn("too many arguments\n");
+ return (DCMD_USAGE);
+ }
+
+ if (mdb_vread(&xr, sizeof (xr), addr) != sizeof (xr)) {
+ mdb_warn("failed to read xhci_ring_t at %p", addr);
+ return (DCMD_USAGE);
+ }
+
+ if (argv[0].a_type == MDB_TYPE_IMMEDIATE) {
+ target = argv[0].a_un.a_val;
+ } else if (argv[0].a_type == MDB_TYPE_STRING) {
+ target = mdb_strtoull(argv[0].a_un.a_str);
+ } else {
+ mdb_warn("argument is an unknown supported type: %d\n",
+ argv[0].a_type);
+ return (DCMD_USAGE);
+ }
+ target = roundup(target, sizeof (xhci_trb_t));
+
+ base = xr.xr_dma.xdb_cookies[0].dmac_laddress;
+ max = base + xr.xr_ntrb * sizeof (xhci_trb_t);
+
+ if (target < base || target > max) {
+ mdb_warn("target address %p is outside the range of PAs for "
+ "TRBs in the ring [%p, %p)", target, base, max);
+ return (DCMD_ERR);
+ }
+ target -= base;
+ mdb_printf("0x%" PRIx64 "\n", target + (uintptr_t)xr.xr_trb);
+
+ return (DCMD_OK);
+}
+
+static const mdb_dcmd_t xhci_dcmds[] = {
+ { "xhci_epctx", ":", "print endpoint context",
+ xhci_mdb_print_epctx, NULL },
+ { "xhci_slotctx", ":", "print slot context",
+ xhci_mdb_print_slotctx, NULL },
+ { "xhci_trb", ":", "print TRB",
+ xhci_mdb_print_trb, NULL },
+ { "xhci_find", ": -s slot [-e endpiont]",
+ "find given xhci slot or endpoint",
+ xhci_mdb_find, NULL },
+ { "xhci_device", ":", "device summary",
+ xhci_mdb_print_device, NULL },
+ { "xhci_find_trb", ": pa", "find trb with PA in ring",
+ xhci_mdb_find_trb, NULL },
+ { NULL }
+};
+
+static const mdb_walker_t xhci_walkers[] = {
+ { "xhci", "walk list of xhci_t structures",
+ xhci_mdb_walk_xhci_init, xhci_mdb_walk_xhci_step, NULL },
+ { "xhci_device", "walk list of xhci_device_t structures",
+ xhci_mdb_walk_xhci_device_init, xhci_mdb_walk_xhci_device_step,
+ NULL },
+ { "xhci_endpoint", "walk list of xhci_endpoint_t structures",
+ xhci_mdb_walk_xhci_endpoint_init, xhci_mdb_walk_xhci_endpoint_step,
+ NULL },
+ { NULL }
+};
+
+static const mdb_modinfo_t xhci_modinfo = {
+ MDB_API_VERSION, xhci_dcmds, xhci_walkers
+};
+
+const mdb_modinfo_t *
+_mdb_init(void)
+{
+ return (&xhci_modinfo);
+}
diff --git a/usr/src/cmd/mdb/intel/amd64/libpython/Makefile b/usr/src/cmd/mdb/intel/amd64/libpython/Makefile
index 7a43f8d9f7..7937144a67 100644
--- a/usr/src/cmd/mdb/intel/amd64/libpython/Makefile
+++ b/usr/src/cmd/mdb/intel/amd64/libpython/Makefile
@@ -42,7 +42,6 @@ include ../../../Makefile.module
%.ln := CPPFLAGS += $(PYLNFLAGS)
LINTFLAGS += -erroff=E_MACRO_REDEFINED
-
dmod/$(MODULE) := LDLIBS += -lproc
%.o: $(MODSRCS_DIR)/%.c
diff --git a/usr/src/cmd/mdb/intel/amd64/libumem/Makefile b/usr/src/cmd/mdb/intel/amd64/libumem/Makefile
index c13578207d..5a9b46141d 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/xhci/Makefile b/usr/src/cmd/mdb/intel/amd64/xhci/Makefile
new file mode 100644
index 0000000000..0de7a701a7
--- /dev/null
+++ b/usr/src/cmd/mdb/intel/amd64/xhci/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 2016 Joyent, Inc.
+#
+
+
+MODULE = xhci.so
+MDBTGT = kvm
+
+MODSRCS = xhci.c
+
+include ../../../../Makefile.cmd
+include ../../../../Makefile.cmd.64
+include ../../Makefile.amd64
+include ../../../Makefile.module
+
+CPPFLAGS += -I$(SRC)/uts/common
diff --git a/usr/src/cmd/mdb/intel/ia32/libumem/Makefile b/usr/src/cmd/mdb/intel/ia32/libumem/Makefile
index 51f0985cf2..ca4d07a66a 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/xhci/Makefile b/usr/src/cmd/mdb/intel/ia32/xhci/Makefile
new file mode 100644
index 0000000000..6fb72a3b2d
--- /dev/null
+++ b/usr/src/cmd/mdb/intel/ia32/xhci/Makefile
@@ -0,0 +1,25 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+MODULE = xhci.so
+MDBTGT = kvm
+
+MODSRCS = xhci.c
+
+include ../../../../Makefile.cmd
+include ../../Makefile.ia32
+include ../../../Makefile.module
+
+CPPFLAGS += -I$(SRC)/uts/common
diff --git a/usr/src/cmd/netfiles/Makefile b/usr/src/cmd/netfiles/Makefile
index 0ae84c2276..aa6886311d 100644
--- a/usr/src/cmd/netfiles/Makefile
+++ b/usr/src/cmd/netfiles/Makefile
@@ -21,6 +21,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2019 Joyent, Inc.
+#
TXTS= nsswitch.conf netconfig hosts services
DEFAULTFILES= nss.dfl
@@ -29,7 +31,7 @@ include ../Makefile.cmd
FILES= hosts services
ETCFILES= netconfig nsswitch.conf nsswitch.files nsswitch.nis \
- nsswitch.dns nsswitch.ldap nsswitch.ad
+ nsswitch.dns nsswitch.ldap nsswitch.ad resolv.conf
ROOTNET= $(ROOTETC)/net
TICLTS= $(ROOTNET)/ticlts
diff --git a/usr/src/cmd/netfiles/nsswitch.conf b/usr/src/cmd/netfiles/nsswitch.conf
index a88791b430..573b3107af 100644
--- a/usr/src/cmd/netfiles/nsswitch.conf
+++ b/usr/src/cmd/netfiles/nsswitch.conf
@@ -29,8 +29,8 @@
passwd: files
group: files
-hosts: files
-ipnodes: files
+hosts: files dns mdns
+ipnodes: files dns mdns
networks: files
protocols: files
rpc: files
diff --git a/usr/src/cmd/netfiles/resolv.conf b/usr/src/cmd/netfiles/resolv.conf
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/usr/src/cmd/netfiles/resolv.conf
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/nsadmin/Makefile b/usr/src/cmd/nsadmin/Makefile
index 533351bd86..45bdcc6701 100644
--- a/usr/src/cmd/nsadmin/Makefile
+++ b/usr/src/cmd/nsadmin/Makefile
@@ -25,64 +25,85 @@
# Copyright 2019 Joyent, Inc.
#
-PROG= profile .login ksh.kshrc system
-PROGSKEL= .profile .kshrc
-PROGROOT= .profile .bashrc
+ETCFILES = profile .login ksh.kshrc system zshrc
+ETCSKELFILES = .profile .kshrc .bashrc
+ETCBASHFILES = bash_completion
+ETCBASHCOMPLETIONDFILES = dladm zones zone_alias
+ROOTFILES = .profile .bashrc .bash_profile
include ../Makefile.cmd
-ROOTROOT= $(ROOT)/root
+ROOTETCFILES = $(ETCFILES:%=$(ROOTETC)/%)
ROOTETCSKEL= $(ROOTETC)/skel
-ROOTETCSKELPROG= $(PROGSKEL:%=$(ROOTETCSKEL)/%)
+ROOTETCSKELFILES = $(ETCSKELFILES:%=$(ROOTETCSKEL)/%)
ROOTETCVERSIONS= $(ROOTETC)/versions
-ROOTETCVERSIONSPROG= $(ROOTETCVERSIONS)/build
-ROOTROOTPROG= $(PROGROOT:%=$(ROOTROOT)/%)
+ROOTETCVERSIONSFILES = $(ROOTETCVERSIONS)/build
+ETCBASHDIR = $(ROOTETC)/bash
+ROOTETCBASHFILES = $(ETCBASHFILES:%=$(ETCBASHDIR)/%)
+ETCBASHCOMPLETIONDDIR = $(ETCBASHDIR)/bash_completion.d
+ROOTETCBASHCOMPLETIONDFILES = \
+ $(ETCBASHCOMPLETIONDFILES:%=$(ETCBASHCOMPLETIONDDIR)/%)
+ROOTROOT= $(ROOT)/root
+ROOTROOTFILES = $(ROOTFILES:%=$(ROOTROOT)/%)
+
+FILES = $(ROOTETCFILES) $(ROOTETCSKELFILES) \
+ $(ROOTETCBASHFILES) $(ROOTETCBASHCOMPLETIONDFILES) \
+ $(ROOTROOTFILES) $(ROOTETCVERSIONSFILES)
+
FILEMODE= 0644
-CLOBBERFILES = profile .bashrc .profile .login .kshrc
+.KEEP_STATE:
+
+all:
+
+# Use $^ instead of $< - it's broken without %
+INS.file = $(RM) $@; $(INS) -s -m $(FILEMODE) -f $(@D) $^
+INS.rename = $(INS.file); $(MV) $(@D)/$(^F) $@
+
+$(ROOTROOT) \
+$(ROOTETCVERSIONS) \
+$(ETCBASHDIR) \
+$(ROOTETCBASHCOMPLETIONDDIR) \
+$(ROOTETCSKEL):
+ $(INS.dir)
-.login: login.csh
- $(RM) .login
- $(CP) login.csh .login
+$(ROOTETC)/profile: etc-profile.sh
+ $(INS.rename)
-profile: etc-profile.sh
- $(RM) profile
- $(CP) etc-profile.sh $@
+$(ROOTETC)/.login: login.csh
+ $(INS.rename)
-.profile: dot-profile.sh
- $(RM) .profile
- $(CP) dot-profile.sh $@
+# skel file and root's default
+$(ROOTROOT)/.kshrc $(ROOTETCSKEL)/.kshrc: dot-kshrc.sh
+ $(INS.rename)
-.kshrc: dot-kshrc.sh
- $(RM) .kshrc
- $(CP) dot-kshrc.sh $@
+# skel file and root's default
+$(ROOTROOT)/.profile $(ROOTETCSKEL)/.profile: dot-profile.sh
+ $(INS.rename)
-.bashrc: bashrc.sh
- $(RM) .bashrc
- $(CP) bashrc.sh $@
+$(ROOTETCSKEL)/.bashrc: etc-skel-bashrc.sh
+ $(INS.rename)
-.KEEP_STATE:
+$(ROOTROOT)/.bash_profile: dot-bash_profile.sh
+ $(INS.rename)
-all: $(PROG) $(PROGSKEL)
+$(ROOTROOT)/.bashrc: dot-bashrc.sh
+ $(INS.rename)
-$(ROOTROOT)/% \
-$(ROOTETCSKEL)/%: %
+$(ROOTETCBASHDIR)/%: %
$(INS.file)
-$(ROOTROOT) \
-$(ROOTETCVERSIONS) \
-$(ROOTETCSKEL):
- $(INS.dir)
+$(ROOTETCBASHCOMPLETIONDDIR)/%: %
+ $(INS.file)
-$(ROOTETCVERSIONSPROG): $(ROOTETCVERSIONS) FRC
+$(ROOTETCVERSIONSFILES): $(ROOTETCVERSIONS) FRC
if [[ -n "$$BUILDVERSION_EXEC" ]]; then \
- $$BUILDVERSION_EXEC >$(ROOTETCVERSIONSPROG) ; \
+ $$BUILDVERSION_EXEC >$(ROOTETCVERSIONSFILES) ; \
else \
- touch $(ROOTETCVERSIONSPROG) ; \
+ touch $(ROOTETCVERSIONSFILES) ; \
fi
-install: all $(ROOTETCSKEL) $(ROOTETCPROG) \
- $(ROOTETCSKELPROG) $(ROOTROOTPROG) $(ROOTETCVERSIONSPROG)
+install: all $(FILES)
clean:
diff --git a/usr/src/cmd/nsadmin/bash/bash_completion b/usr/src/cmd/nsadmin/bash/bash_completion
new file mode 100755
index 0000000000..71f3e9e63f
--- /dev/null
+++ b/usr/src/cmd/nsadmin/bash/bash_completion
@@ -0,0 +1,9417 @@
+#
+# This file contains an example set of shell completions that can be used with
+# bash(1). These completions allow a user to complete filenames, commands
+# name, command line options, and command line arguments using the [tab] key.
+# The completions defined here are specific to the GNU command set, as a result
+# they will provide the choice of GNU command line options in response to the
+# [tab] key. For the completed options to match the command implementation,
+# you may have to have /usr/gnu/bin at the head of your PATH.
+#
+# These completions are not included in the default bash(1) environment. To
+# include them in the default environment, it is recommended that this file be
+# copied to /etc/bash/bash_completion and be sourced from either a system wide
+# bashrc in /etc/bash/bashrc or individual bashrcs in ~/.bashrc via
+# [ -f /etc/bash/bash_completion ] && . /etc/bash/bash_completion
+#
+
+# bash_completion - programmable completion functions for bash 3.x
+# (backwards compatible with bash 2.05b)
+#
+# $Id: bash_completion,v 1.872 2006/03/01 16:20:18 ianmacd Exp $
+#
+# Copyright (C) Ian Macdonald <ian@caliban.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# The latest version of this software can be obtained here:
+#
+# http://www.caliban.org/bash/index.shtml#completion
+#
+# RELEASE: 20060301
+
+if [[ $- == *v* ]]; then
+ BASH_COMPLETION_ORIGINAL_V_VALUE="-v"
+else
+ BASH_COMPLETION_ORIGINAL_V_VALUE="+v"
+fi
+
+if [[ -n $BASH_COMPLETION_DEBUG ]]; then
+ set -v
+else
+ set +v
+fi
+
+# Alter the following to reflect the location of this file.
+#
+[ -n "$BASH_COMPLETION" ] || BASH_COMPLETION=/etc/bash/bash_completion
+[ -n "$BASH_COMPLETION_DIR" ] || BASH_COMPLETION_DIR=/etc/bash/bash_completion.d
+readonly BASH_COMPLETION BASH_COMPLETION_DIR
+
+# Set a couple of useful vars
+#
+UNAME=$( uname -s )
+# strip OS type and version under Cygwin (e.g. CYGWIN_NT-5.1 => Cygwin)
+UNAME=${UNAME/CYGWIN_*/Cygwin}
+RELEASE=$( uname -r )
+
+# features supported by bash 2.05 and higher
+if [ ${BASH_VERSINFO[0]} -eq 2 ] && [[ ${BASH_VERSINFO[1]} > 04 ]] ||
+ [ ${BASH_VERSINFO[0]} -gt 2 ]; then
+ declare -r bash205=$BASH_VERSION 2>/dev/null || :
+ default="-o default"
+ dirnames="-o dirnames"
+ filenames="-o filenames"
+fi
+# features supported by bash 2.05b and higher
+if [ ${BASH_VERSINFO[0]} -eq 2 ] && [[ ${BASH_VERSINFO[1]} = "05b" ]] ||
+ [ ${BASH_VERSINFO[0]} -gt 2 ]; then
+ declare -r bash205b=$BASH_VERSION 2>/dev/null || :
+ nospace="-o nospace"
+fi
+# features supported by bash 3.0 and higher
+if [ ${BASH_VERSINFO[0]} -gt 2 ]; then
+ declare -r bash3=$BASH_VERSION 2>/dev/null || :
+ bashdefault="-o bashdefault"
+ plusdirs="-o plusdirs"
+fi
+
+# Turn on extended globbing and programmable completion
+shopt -s extglob progcomp
+
+# A lot of the following one-liners were taken directly from the
+# completion examples provided with the bash 2.04 source distribution
+
+# Make directory commands see only directories
+complete -d pushd
+
+# The following section lists completions that are redefined later
+# Do NOT break these over multiple lines.
+#
+# START exclude -- do NOT remove this line
+complete -f -X '!*.?(t)bz?(2)' bunzip2 bzcat bzcmp bzdiff bzegrep bzfgrep bzgrep
+complete -f -X '!*.@(zip|ZIP|jar|JAR|exe|EXE|pk3|war|wsz|ear|zargo|xpi|sxw|ott)' unzip zipinfo
+complete -f -X '*.Z' compress znew
+complete -f -X '!*.@(Z|gz|tgz|Gz|dz)' gunzip zcmp zdiff zcat zegrep zfgrep zgrep zless zmore
+complete -f -X '!*.Z' uncompress
+complete -f -X '!*.@(gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX)' ee display
+complete -f -X '!*.@(gif|jp?(e)g|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|GIF|JPG|JP?(E)G|TIF?(F)|PNG|P[BGP]M|BMP|X[BP]M|RLE|RGB|PCX|FITS|PM)' xv qiv
+complete -f -X '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ|.bz2|.BZ2|.Z))' gv ggv kghostview
+complete -f -X '!*.@(dvi|DVI)?(.@(gz|Z|bz2))' xdvi
+complete -f -X '!*.@(dvi|DVI)?(.@(gz|bz2))' kdvi
+complete -f -X '!*.@(dvi|DVI)' dvips dviselect dvitype dvipdf advi dvipdfm dvipdfmx
+complete -f -X '!*.@(pdf|PDF)' acroread gpdf xpdf
+complete -f -X '!*.@(?(e)ps|?(E)PS|pdf|PDF)' kpdf
+complete -f -X '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ)|cb(r|z)|CB(R|Z)|djv?(u)|DJV?(U)||dvi|DVI|gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX)' evince
+complete -f -X '!*.@(?(e)ps|?(E)PS)' ps2pdf
+complete -f -X '!*.texi*' makeinfo texi2html
+complete -f -X '!*.@(?(la)tex|?(LA)TEX|texi|TEXI|dtx|DTX|ins|INS)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi
+complete -f -X '!*.@(mp3|MP3)' mpg123 mpg321 madplay
+complete -f -X '!*.@(mp?(e)g|MP?(E)G|wma|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|viv|rm|ram|yuv|mov|MOV|qt|QT|wmv|mp3|MP3|m4v|M4V|ogg|OGG|ogm|OGM|mp4|MP4|wav|WAV|asx|ASX|mng|MNG|srt)' xine aaxine fbxine kaffeine
+complete -f -X '!*.@(avi|asf|wmv)' aviplay
+complete -f -X '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay
+complete -f -X '!*.@(mpg|mpeg|avi|mov|qt)' xanim
+complete -f -X '!*.@(ogg|OGG|m3u|flac|spx)' ogg123
+complete -f -X '!*.@(mp3|MP3|ogg|OGG|pls|m3u)' gqmpeg freeamp
+complete -f -X '!*.fig' xfig
+complete -f -X '!*.@(mid?(i)|MID?(I))' playmidi
+complete -f -X '!*.@(mid?(i)|MID?(I)|rmi|RMI|rcp|RCP|[gr]36|[GR]36|g18|G18|mod|MOD|xm|XM|it|IT|x3m|X3M)' timidity
+complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' vi vim gvim rvim view rview rgvim rgview gview
+complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' emacs
+complete -f -X '!*.@(exe|EXE|com|COM|scr|SCR|exe.so)' wine
+complete -f -X '!*.@(zip|ZIP|z|Z|gz|GZ|tgz|TGZ)' bzme
+complete -f -X '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL]))' netscape mozilla lynx opera galeon curl dillo elinks amaya
+complete -f -X '!*.@(sxw|stw|sxg|sgl|doc|dot|rtf|txt|htm|html|odt|ott|odm)' oowriter
+complete -f -X '!*.@(sxi|sti|pps|ppt|pot|odp|otp)' ooimpress
+complete -f -X '!*.@(sxc|stc|xls|xlw|xlt|csv|ods|ots)' oocalc
+complete -f -X '!*.@(sxd|std|sda|sdd|odg|otg)' oodraw
+complete -f -X '!*.@(sxm|smf|mml|odf)' oomath
+complete -f -X '!*.odb' oobase
+complete -f -X '!*.rpm' rpm2cpio
+# FINISH exclude -- do not remove this line
+
+# start of section containing compspecs that can be handled within bash
+
+# user commands see only users
+complete -u su usermod userdel passwd chage write chfn groups slay w sux
+
+# group commands see only groups
+[ -n "$bash205" ] && complete -g groupmod groupdel newgrp 2>/dev/null
+
+# bg completes with stopped jobs
+complete -A stopped -P '%' bg
+
+# other job commands
+complete -j -P '%' fg jobs disown
+
+# readonly and unset complete with shell variables
+complete -v readonly unset
+
+# set completes with set options
+complete -A setopt set
+
+# shopt completes with shopt options
+complete -A shopt shopt
+
+# helptopics
+complete -A helptopic help
+
+# unalias completes with aliases
+complete -a unalias
+
+# bind completes with readline bindings (make this more intelligent)
+complete -A binding bind
+
+# type and which complete on commands
+complete -c command type which
+
+# builtin completes on builtins
+complete -b builtin
+
+# start of section containing completion functions called by other functions
+
+# This function checks whether we have a given program on the system.
+# No need for bulky functions in memory if we don't.
+#
+have()
+{
+ unset -v have
+ PATH=/usr/gnu/bin:$PATH:/sbin:/usr/sbin type $1 &>/dev/null &&
+ have="yes"
+}
+
+# use GNU sed if we have it, since its extensions are still used in our code
+#
+[ $UNAME != Linux ] && have gsed && alias sed=gsed
+
+# This function checks whether a given readline variable
+# is `on'.
+#
+_rl_enabled()
+{
+ [[ "$( bind -v )" = *$1+([[:space:]])on* ]]
+}
+
+# This function shell-quotes the argument
+quote()
+{
+ echo \'${1//\'/\'\\\'\'}\' #'# Help vim syntax highlighting
+}
+
+# This function quotes the argument in a way so that readline dequoting
+# results in the original argument
+quote_readline()
+{
+ local t="${1//\\/\\\\}"
+ echo \'${t//\'/\'\\\'\'}\' #'# Help vim syntax highlighting
+}
+
+# This function shell-dequotes the argument
+dequote()
+{
+ eval echo "$1"
+}
+
+
+# Get the word to complete
+# This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it handles cases
+# where the user is completing in the middle of a word.
+# (For example, if the line is "ls foobar",
+# and the cursor is here --------> ^
+# it will complete just "foo", not "foobar", which is what the user wants.)
+_get_cword()
+{
+ if [[ "${#COMP_WORDS[COMP_CWORD]}" -eq 0 ]] || [[ "$COMP_POINT" == "${#COMP_LINE}" ]]; then
+ echo "${COMP_WORDS[COMP_CWORD]}"
+ else
+ local i
+ local cur="$COMP_LINE"
+ local index="$COMP_POINT"
+ for (( i = 0; i <= COMP_CWORD; ++i )); do
+ while [[ "${#cur}" -ge ${#COMP_WORDS[i]} ]] && [[ "${cur:0:${#COMP_WORDS[i]}}" != "${COMP_WORDS[i]}" ]]; do
+ cur="${cur:1}"
+ index="$(( index - 1 ))"
+ done
+ if [[ "$i" -lt "$COMP_CWORD" ]]; then
+ local old_size="${#cur}"
+ cur="${cur#${COMP_WORDS[i]}}"
+ local new_size="${#cur}"
+ index="$(( index - old_size + new_size ))"
+ fi
+ done
+
+ if [[ "${COMP_WORDS[COMP_CWORD]:0:${#cur}}" != "$cur" ]]; then
+ # We messed up! At least return the whole word so things keep working
+ echo "${COMP_WORDS[COMP_CWORD]}"
+ else
+ echo "${cur:0:$index}"
+ fi
+ fi
+}
+
+
+# This function performs file and directory completion. It's better than
+# simply using 'compgen -f', because it honours spaces in filenames.
+# If passed -d, it completes only on directories. If passed anything else,
+# it's assumed to be a file glob to complete on.
+#
+_filedir()
+{
+ local IFS=$'\t\n' xspec
+
+ _expand || return 0
+
+ local toks=( ) tmp
+ while read -r tmp; do
+ [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp
+ done < <( compgen -d -- "$(quote_readline "$cur")" )
+
+ if [[ "$1" != -d ]]; then
+ xspec=${1:+"!*.$1"}
+ while read -r tmp; do
+ [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp
+ done < <( compgen -f -X "$xspec" -- "$(quote_readline "$cur")" )
+ fi
+
+ COMPREPLY=( "${COMPREPLY[@]}" "${toks[@]}" )
+}
+
+# This function completes on signal names
+#
+_signals()
+{
+ local i
+
+ # standard signal completion is rather braindead, so we need
+ # to hack around to get what we want here, which is to
+ # complete on a dash, followed by the signal name minus
+ # the SIG prefix
+ COMPREPLY=( $( compgen -A signal SIG${cur#-} ))
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ COMPREPLY[i]=-${COMPREPLY[i]#SIG}
+ done
+}
+
+# This function completes on configured network interfaces
+#
+_configured_interfaces()
+{
+ if [ -f /etc/debian_version ]; then
+ # Debian system
+ COMPREPLY=( $( sed -ne 's|^iface \([^ ]\+\).*$|\1|p' \
+ /etc/network/interfaces ) )
+ elif [ -f /etc/SuSE-release ]; then
+ # SuSE system
+ COMPREPLY=( $( command ls \
+ /etc/sysconfig/network/ifcfg-* | \
+ sed -ne 's|.*ifcfg-\('$cur'.*\)|\1|p' ) )
+ elif [ -f /etc/pld-release ]; then
+ # PLD Linux
+ COMPREPLY=( $( command ls -B \
+ /etc/sysconfig/interfaces | \
+ sed -ne 's|.*ifcfg-\('$cur'.*\)|\1|p' ) )
+ else
+ # Assume Red Hat
+ COMPREPLY=( $( command ls \
+ /etc/sysconfig/network-scripts/ifcfg-* | \
+ sed -ne 's|.*ifcfg-\('$cur'.*\)|\1|p' ) )
+ fi
+}
+
+# This function completes on all available network interfaces
+# -a: restrict to active interfaces only
+# -w: restrict to wireless interfaces only
+#
+_available_interfaces()
+{
+ local cmd
+
+ if [ "${1:-}" = -w ]; then
+ cmd="iwconfig"
+ elif [ "${1:-}" = -a ]; then
+ cmd="ifconfig"
+ else
+ cmd="ifconfig -a"
+ fi
+
+ COMPREPLY=( $( eval $cmd 2>/dev/null | \
+ sed -ne 's|^\('$cur'[^[:space:][:punct:]]\{1,\}\).*$|\1|p') )
+}
+
+# This function expands tildes in pathnames
+#
+_expand()
+{
+ # FIXME: Why was this here?
+ # [ "$cur" != "${cur%\\}" ] && cur="$cur\\"
+
+ # expand ~username type directory specifications
+ if [[ "$cur" == \~*/* ]]; then
+ eval cur=$cur
+ elif [[ "$cur" == \~* ]]; then
+ cur=${cur#\~}
+ COMPREPLY=( $( compgen -P '~' -u $cur ) )
+ return ${#COMPREPLY[@]}
+ fi
+}
+
+# This function completes on process IDs.
+# AIX and Solaris ps prefers X/Open syntax.
+[ $UNAME = SunOS -o $UNAME = AIX ] &&
+_pids()
+{
+ COMPREPLY=( $( compgen -W '$( command ps -efo pid | sed 1d )' -- $cur ))
+} ||
+_pids()
+{
+ COMPREPLY=( $( compgen -W '$( command ps axo pid | sed 1d )' -- $cur ) )
+}
+
+# This function completes on process group IDs.
+# AIX and SunOS prefer X/Open, all else should be BSD.
+[ $UNAME = SunOS -o $UNAME = AIX ] &&
+_pgids()
+{
+ COMPREPLY=( $( compgen -W '$( command ps -efo pgid | sed 1d )' -- $cur ))
+} ||
+_pgids()
+{
+ COMPREPLY=( $( compgen -W '$( command ps axo pgid | sed 1d )' -- $cur ))
+}
+
+# This function completes on user IDs
+#
+_uids()
+{
+ if type getent &>/dev/null; then
+ COMPREPLY=( $( getent passwd | \
+ awk -F: '{if ($3 ~ /^'$cur'/) print $3}' ) )
+ elif type perl &>/dev/null; then
+ COMPREPLY=( $( compgen -W '$( perl -e '"'"'while (($uid) = (getpwent)[2]) { print $uid . "\n" }'"'"' )' -- $cur ) )
+ else
+ # make do with /etc/passwd
+ COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($3 ~ /^'$cur'/) print $3}'\
+ /etc/passwd ) )
+ fi
+}
+
+# This function completes on group IDs
+#
+_gids()
+{
+ if type getent &>/dev/null; then
+ COMPREPLY=( $( getent group | \
+ awk -F: '{if ($3 ~ /^'$cur'/) print $3}' ) )
+ elif type perl &>/dev/null; then
+ COMPREPLY=( $( compgen -W '$( perl -e '"'"'while (($gid) = (getgrent)[2]) { print $gid . "\n" }'"'"' )' -- $cur ) )
+ else
+ # make do with /etc/group
+ COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($3 ~ /^'$cur'/) print $3}'\
+ /etc/group ) )
+ fi
+}
+
+# This function completes on services
+#
+_services()
+{
+ local sysvdir famdir
+ [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d || sysvdir=/etc/init.d
+ famdir=/etc/xinetd.d
+ COMPREPLY=( $( builtin echo $sysvdir/!(*.rpmsave|*.rpmorig|*~|functions)) )
+
+ if [ -d $famdir ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" $( builtin echo $famdir/!(*.rpmsave|*.rpmorig|*~)) )
+ fi
+
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]#@($sysvdir|$famdir)/}' -- $cur ) )
+}
+
+# This function complete on modules
+#
+_modules()
+{
+ local modpath
+ modpath=/lib/modules/$1
+ COMPREPLY=( $( command ls -R $modpath | \
+ sed -ne 's/^\('$cur'.*\)\.k\?o\(\|.gz\)$/\1/p') )
+}
+
+# this function complete on user:group format
+#
+_usergroup()
+{
+ local IFS=$'\n'
+ cur=${cur//\\\\ / }
+ if [[ $cur = *@(\\:|.)* ]] && [ -n "$bash205" ]; then
+ user=${cur%%*([^:.])}
+ COMPREPLY=( $(compgen -P ${user/\\\\} -g -- ${cur##*[.:]}) )
+ elif [[ $cur = *:* ]] && [ -n "$bash205" ]; then
+ COMPREPLY=( $( compgen -g -- ${cur##*[.:]} ) )
+ else
+ COMPREPLY=( $( compgen -S : -u -- $cur ) )
+ fi
+}
+
+# this function count the number of mandatory args
+#
+_count_args()
+{
+ args=1
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" != -* ]]; then
+ args=$(($args+1))
+ fi
+ done
+}
+
+# start of section containing completion functions for bash built-ins
+
+# bash alias completion
+#
+_alias()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[$COMP_CWORD]}
+
+ case "$COMP_LINE" in
+ *[^=])
+ COMPREPLY=( $( compgen -A alias -S '=' -- $cur ) )
+ ;;
+ *=)
+ COMPREPLY=( "$( alias ${cur%=} 2>/dev/null | \
+ sed -e 's|^alias '$cur'\(.*\)$|\1|' )" )
+ ;;
+ esac
+}
+complete -F _alias $nospace alias
+
+# bash export completion
+#
+_export()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[$COMP_CWORD]}
+
+ case "$COMP_LINE" in
+ *=\$*)
+ COMPREPLY=( $( compgen -v -P '$' -- ${cur#*=\$} ) )
+ ;;
+ *[^=])
+ COMPREPLY=( $( compgen -v -S '=' -- $cur ) )
+ ;;
+ *=)
+ COMPREPLY=( "$( eval echo -n \"$`echo ${cur%=}`\" |
+ ( echo -n \'
+ sed -e 's/'\''/'\''\\\'\'''\''/g'
+ echo -n \' ) )" )
+ ;;
+ esac
+}
+complete -F _export $default $nospace export
+
+# bash shell function completion
+#
+_function()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ $1 == @(declare|typeset) ]]; then
+ if [ "$prev" = -f ]; then
+ COMPREPLY=( $( compgen -A function -- $cur ) )
+ elif [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a -f -F -i -r -x -p' -- \
+ $cur ) )
+ fi
+ elif [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -A function -- $cur ) )
+ else
+ COMPREPLY=( "() $( type -- ${COMP_WORDS[1]} | sed -e 1,2d )" )
+ fi
+}
+complete -F _function function declare typeset
+
+# bash complete completion
+#
+_complete()
+{
+ local cur prev options
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -o)
+ options="default dirnames filenames"
+ [ -n "$bash205b" ] && options="$options nospace"
+ [ -n "$bash3" ] && options="$options bashdefault plusdirs"
+ COMPREPLY=( $( compgen -W "$options" -- $cur ) )
+ return 0
+ ;;
+
+ -A)
+ COMPREPLY=( $( compgen -W 'alias arrayvar binding \
+ builtin command directory disabled enabled \
+ export file function group helptopic hostname \
+ job keyword running service setopt shopt \
+ signal stopped user variable' -- $cur ) )
+ return 0
+ ;;
+
+ -C)
+ COMPREPLY=( $( compgen -A command -- $cur ) )
+ return 0
+ ;;
+ -F)
+ COMPREPLY=( $( compgen -A function -- $cur ) )
+ return 0
+ ;;
+ -@(p|r))
+ COMPREPLY=( $( complete -p | sed -e 's|.* ||' | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ options="-a -b -c -d -e -f -g -j -k -s -v -u -A -G -W -P -S -X -F -C"
+ [ -n "$bash205" ] && options="$options -o"
+ COMPREPLY=( $( compgen -W "$options" -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -A command -- $cur ) )
+ fi
+}
+complete -F _complete complete
+
+# start of section containing completion functions for external programs
+
+# a little help for FreeBSD ports users
+[ $UNAME = FreeBSD ] && complete -W 'index search fetch fetch-list \
+ extract patch configure build install reinstall \
+ deinstall clean clean-depends kernel buildworld' make
+
+# This completes on a list of all available service scripts for the
+# 'service' command and/or the SysV init.d directory, followed by
+# that script's available commands
+#
+{ have service || [ -d /etc/init.d/ ]; } &&
+_service()
+{
+ local cur sysvdir
+
+ COMPREPLY=()
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ cur=`_get_cword`
+
+ # don't complete for things like killall, ssh and mysql if it's
+ # the standalone command, rather than the init script
+ [[ ${COMP_WORDS[0]} != @(*init.d/!(functions|~)|service) ]] && return 0
+
+ # don't complete past 2nd token
+ [ $COMP_CWORD -gt 2 ] && return 0
+
+ [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d \
+ || sysvdir=/etc/init.d
+
+ if [[ $COMP_CWORD -eq 1 ]] && [[ $prev == "service" ]]; then
+ _services
+ else
+ COMPREPLY=( $( compgen -W '`sed -ne "y/|/ /; \
+ s/^.*Usage.*{\(.*\)}.*$/\1/p" \
+ $sysvdir/${prev##*/} 2>/dev/null`' -- $cur ) )
+ fi
+
+ return 0
+} &&
+complete -F _service service
+[ -d /etc/init.d/ ] && complete -F _service $default \
+ $(for i in /etc/init.d/*; do echo ${i##*/}; done)
+
+# chown(1) completion
+#
+_chown()
+{
+ local cur
+ cur=`_get_cword`
+
+ # options completion
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c -h -f -R -v --changes \
+ --dereference --no-dereference --from= --silent --quiet \
+ --reference= --recursive --verbose --help --version' -- $cur ) )
+ else
+ _count_args
+
+ case $args in
+ 1)
+ _usergroup
+ ;;
+ *)
+ _filedir
+ ;;
+ esac
+ fi
+}
+complete -F _chown $filenames chown
+
+# chgrp(1) completion
+#
+_chgrp()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ cur=${cur//\\\\/}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # options completion
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c -h -f -R -v --changes \
+ --dereference --no-dereference --silent --quiet \
+ --reference= --recursive --verbose --help --version' -- $cur ) )
+ return 0
+ fi
+
+ # first parameter on line or first since an option?
+ if [ $COMP_CWORD -eq 1 ] && [[ "$cur" != -* ]] || \
+ [[ "$prev" == -* ]] && [ -n "$bash205" ]; then
+ local IFS=$'\n'
+ COMPREPLY=( $( compgen -g $cur 2>/dev/null ) )
+ else
+ _filedir || return 0
+ fi
+
+ return 0
+}
+complete -F _chgrp $filenames chgrp
+
+# umount(8) completion. This relies on the mount point being the third
+# space-delimited field in the output of mount(8)
+#
+_umount()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ OLDIFS="$IFS"
+ IFS="\n"
+ COMPREPLY=( $( compgen -W '$( mount | cut -d" " -f 3 )' -- $cur ) )
+ IFS="$OLDIFS"
+
+ return 0
+}
+complete -F _umount $dirnames umount
+
+# mount(8) completion. This will pull a list of possible mounts out of
+# /etc/{,v}fstab, unless the word being completed contains a ':', which
+# would indicate the specification of an NFS server. In that case, we
+# query the server for a list of all available exports and complete on
+# that instead.
+#
+_mount()
+{ local cur i sm host
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ [[ "$cur" == \\ ]] && cur="/"
+
+ for i in {,/usr}/{,s}bin/showmount; do [ -x $i ] && sm=$i && break; done
+
+ if [ -n "$sm" ] && [[ "$cur" == *:* ]]; then
+ COMPREPLY=( $( $sm -e ${cur%%:*} | sed 1d | \
+ grep ^${cur#*:} | awk '{print $1}' ) )
+ elif [[ "$cur" == //* ]]; then
+ host=${cur#//}
+ host=${host%%/*}
+ if [ -n "$host" ]; then
+ COMPREPLY=( $( compgen -W "$( echo $( smbclient -d 0 -NL $host 2>/dev/null|
+ sed -ne '/^['"$'\t '"']*Sharename/,/^$/p' |
+ sed -ne '3,$s|^[^A-Za-z]*\([^'"$'\t '"']*\).*$|//'$host'/\1|p' ) )" -- "$cur" ) )
+ fi
+ elif [ -r /etc/vfstab ]; then
+ # Solaris
+ COMPREPLY=( $( awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' \
+ /etc/vfstab | grep "^$cur" ) )
+ elif [ ! -e /etc/fstab ]; then
+ # probably Cygwin
+ COMPREPLY=( $( mount | awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' \
+ | grep "^$cur" ) )
+ else
+ # probably Linux
+ COMPREPLY=( $( awk '! /^[ \t]*#/ {if ($2 ~ /\//) print $2}' \
+ /etc/fstab | grep "^$cur" ) )
+ fi
+
+ return 0
+}
+complete -F _mount $default $filenames mount
+
+# Linux rmmod(8) completion. This completes on a list of all currently
+# installed kernel modules.
+#
+have rmmod && {
+_rmmod()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( /sbin/lsmod | \
+ awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}' 2>/dev/null ))
+ return 0
+}
+complete -F _rmmod rmmod
+
+# Linux insmod(8), modprobe(8) and modinfo(8) completion. This completes on a
+# list of all available modules for the version of the kernel currently
+# running.
+#
+_insmod()
+{
+ local cur prev modpath
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # behave like lsmod for modprobe -r
+ if [ $1 = "modprobe" ] &&
+ [ "${COMP_WORDS[1]}" = "-r" ]; then
+ COMPREPLY=( $( /sbin/lsmod | \
+ awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}' ) )
+ return 0
+ fi
+
+ # do filename completion if we're giving a path to a module
+ if [[ "$cur" == */* ]]; then
+ _filedir '@(?(k)o?(.gz))'
+ return 0
+ fi
+
+ if [ $COMP_CWORD -gt 1 ] &&
+ [[ "${COMP_WORDS[COMP_CWORD-1]}" != -* ]]; then
+ # do module parameter completion
+ COMPREPLY=( $( /sbin/modinfo -p ${COMP_WORDS[1]} 2>/dev/null | \
+ awk '{if ($1 ~ /^parm:/ && $2 ~ /^'$cur'/) { print $2 } \
+ else if ($1 !~ /:/ && $1 ~ /^'$cur'/) { print $1 }}' ) )
+ else
+ _modules $(uname -r)
+ fi
+
+ return 0
+}
+complete -F _insmod $filenames insmod modprobe modinfo
+}
+
+# man(1) completion
+#
+[ $UNAME = GNU -o $UNAME = Linux -o $UNAME = Darwin \
+ -o $UNAME = FreeBSD -o $UNAME = SunOS -o $UNAME = Cygwin \
+ -o $UNAME = OpenBSD ] &&
+_man()
+{
+ local cur prev sect manpath UNAME
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ _expand || return 0
+
+ # default completion if parameter contains /
+ if [[ "$cur" == */* ]]; then
+ _filedir
+ return 0
+ fi
+
+ UNAME=$( uname -s )
+ # strip OS type and version under Cygwin
+ UNAME=${UNAME/CYGWIN_*/Cygwin}
+ if [ $UNAME = GNU -o $UNAME = Linux -o $UNAME = FreeBSD \
+ -o $UNAME = Cygwin ]; then
+ manpath=$( manpath 2>/dev/null || command man --path )
+ else
+ manpath=$MANPATH
+ fi
+
+ if [ -z "$manpath" ]; then
+ COMPREPLY=( $( compgen -c -- $cur ) )
+ return 0
+ fi
+
+ # determine manual section to search
+ [[ "$prev" == [0-9ln] ]] && sect=$prev || sect='*'
+
+ manpath=$manpath:
+ if [ -n "$cur" ]; then
+ manpath="${manpath//://*man$sect/$cur* } ${manpath//://*cat$sect/$cur* }"
+ else
+ manpath="${manpath//://*man$sect/ } ${manpath//://*cat$sect/ }"
+ fi
+
+ # redirect stderr for when path doesn't exist
+ COMPREPLY=( $( eval command ls "$manpath" 2>/dev/null ) )
+ # weed out directory path names and paths to man pages
+ COMPREPLY=( ${COMPREPLY[@]##*/?(:)} )
+ # strip suffix from man pages
+ COMPREPLY=( ${COMPREPLY[@]%.@(gz|bz2)} )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]%.*}' -- "${cur//\\\\/}" ) )
+
+ [[ "$prev" != [0-9ln] ]] && _filedir '[0-9ln]'
+
+ return 0
+}
+[ $UNAME = GNU -o $UNAME = Linux -o $UNAME = Darwin \
+ -o $UNAME = FreeBSD -o $UNAME = SunOS -o $UNAME = Cygwin \
+ -o $UNAME = OpenBSD ] && \
+complete -F _man $filenames man apropos whatis
+
+# renice(8) completion
+#
+_renice()
+{
+ local command cur curopt i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ command=$1
+
+ i=0
+ # walk back through command line and find last option
+ while [ $i -le $COMP_CWORD -a ${#COMPREPLY[@]} -eq 0 ]; do
+ curopt=${COMP_WORDS[COMP_CWORD-$i]}
+ case "$curopt" in
+ -u)
+ COMPREPLY=( $( compgen -u -- $cur ) )
+ ;;
+ -g)
+ _pgids
+ ;;
+ -p|$command)
+ _pids
+ ;;
+ esac
+ i=$(( ++i ))
+ done
+}
+complete -F _renice renice
+
+# kill(1) completion
+#
+_kill()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then
+ # return list of available signals
+ _signals
+ else
+ # return list of available PIDs
+ _pids
+ fi
+}
+complete -F _kill kill
+
+# Linux and FreeBSD killall(1) completion.
+#
+[ $UNAME = Linux -o $UNAME = FreeBSD ] &&
+_killall()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then
+ _signals
+ else
+ COMPREPLY=( $( compgen -W '$( command ps axo command | \
+ sed -ne "1d; s/^\[\?\([^-][^] ]*\).*$/\1/p" | \
+ sed -e "s/.*\///" )' -- $cur ) )
+ fi
+
+ return 0
+}
+[ $UNAME = Linux -o $UNAME = FreeBSD ] && complete -F _killall killall pkill
+
+# Linux and FreeBSD pgrep(1) completion.
+#
+[ $UNAME = Linux -o $UNAME = FreeBSD ] &&
+_pgrep()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( compgen -W '$( command ps axo command | \
+ sed -ne "1d; s/^\[\?\([^-][^] ]*\).*$/\1/p" | \
+ sed -e "s/.*\///" )' -- $cur ) )
+
+ return 0
+}
+[ $UNAME = Linux -o $UNAME = FreeBSD ] && complete -F _pgrep pgrep
+# Linux pidof(8) completion.
+[ $UNAME = Linux ] && complete -F _pgrep pidof
+
+# GNU find(1) completion. This makes heavy use of ksh style extended
+# globs and contains Linux specific code for completing the parameter
+# to the -fstype option.
+#
+_find()
+{
+ local cur prev i exprfound onlyonce
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(max|min)depth)
+ COMPREPLY=( $( compgen -W '0 1 2 3 4 5 6 7 8 9' -- $cur ) )
+ return 0
+ ;;
+ -?(a|c)newer|-fls|-fprint?(0|f)|-?(i)?(l)name|-?(i)wholename)
+ _filedir
+ return 0
+ ;;
+ -fstype)
+ # this is highly non-portable
+ [ -e /proc/filesystems ] &&
+ COMPREPLY=( $( cut -d$'\t' -f 2 /proc/filesystems | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+ -gid)
+ _gids
+ return 0
+ ;;
+ -group)
+ if [ -n "$bash205" ]; then
+ COMPREPLY=( $( compgen -g -- $cur 2>/dev/null) )
+ fi
+ return 0
+ ;;
+ -?(x)type)
+ COMPREPLY=( $( compgen -W 'b c d p f l s' -- $cur ) )
+ return 0
+ ;;
+ -uid)
+ _uids
+ return 0
+ ;;
+ -user)
+ COMPREPLY=( $( compgen -u -- $cur ) )
+ return 0
+ ;;
+ -exec|-ok)
+ COMP_WORDS=(COMP_WORDS[0] $cur)
+ COMP_CWORD=1
+ _command
+ return 0
+ ;;
+ -[acm]min|-[acm]time|-?(i)?(l)name|-inum|-?(i)path|-?(i)regex| \
+ -links|-perm|-size|-used|-printf)
+ # do nothing, just wait for a parameter to be given
+ return 0
+ ;;
+ esac
+
+ _expand || return 0
+
+ # set exprfound to 1 if there is already an expression present
+ for i in ${COMP_WORDS[@]}; do
+ [[ "$i" = [-\(\),\!]* ]] && exprfound=1 && break
+ done
+
+ # handle case where first parameter is not a dash option
+ if [ "$exprfound" != 1 ] && [[ "$cur" != [-\(\),\!]* ]]; then
+ _filedir -d
+ return 0
+ fi
+
+ # complete using basic options
+ COMPREPLY=( $( compgen -W '-daystart -depth -follow -help -maxdepth \
+ -mindepth -mount -noleaf -version -xdev -amin -anewer \
+ -atime -cmin -cnewer -ctime -empty -false -fstype \
+ -gid -group -ilname -iname -inum -ipath -iregex \
+ -wholename \
+ -links -lname -mmin -mtime -name -newer -nouser \
+ -nogroup -perm -regex -size -true -type -uid -used \
+ -user -xtype -exec -fls -fprint -fprint0 -fprintf -ok \
+ -print -print0 -printf -prune -ls' -- $cur ) )
+
+ # this removes any options from the list of completions that have
+ # already been specified somewhere on the command line, as long as
+ # these options can only be used once (in a word, "options", in
+ # opposition to "tests" and "actions", as in the find(1) manpage).
+ onlyonce=' -daystart -depth -follow -help -maxdepth -mindepth -mount \
+ -noleaf -version -xdev '
+ COMPREPLY=( $( echo "${COMP_WORDS[@]}" | \
+ (while read -d ' ' i; do
+ [ "$i" == "" ] ||
+ [ "${onlyonce/ ${i%% *} / }" == "$onlyonce" ] &&
+ continue
+ # flatten array with spaces on either side,
+ # otherwise we cannot grep on word boundaries of
+ # first and last word
+ COMPREPLY=" ${COMPREPLY[@]} "
+ # remove word from list of completions
+ COMPREPLY=( ${COMPREPLY/ ${i%% *} / } )
+ done
+ echo "${COMPREPLY[@]}")
+ ) )
+
+ _filedir
+
+ return 0
+}
+complete -F _find $filenames find
+
+# Linux iwconfig(8) completion
+#
+[ $UNAME = Linux ] && have iwconfig &&
+_iwconfig()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ mode)
+ COMPREPLY=( $( compgen -W 'managed ad-hoc master \
+ repeater secondary monitor' -- $cur ) )
+ return 0
+ ;;
+ essid)
+ COMPREPLY=( $( compgen -W 'on off any' -- $cur ) )
+ if [ -n "${COMP_IWLIST_SCAN:-}" ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" \
+ $( iwlist ${COMP_WORDS[1]} scan | \
+ awk -F '"' '/ESSID/ {print $2}' | \
+ grep "^$cur" ))
+ fi
+ return 0
+ ;;
+ nwid)
+ COMPREPLY=( $( compgen -W 'on off' -- $cur ) )
+ return 0
+ ;;
+ channel)
+ COMPREPLY=( $( iwlist ${COMP_WORDS[1]} channel | \
+ awk '/^[[:space:]]*Channel/ {print $2}' | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+
+ freq)
+ COMPREPLY=( $( iwlist ${COMP_WORDS[1]} channel | \
+ awk '/^[[:space:]]*Channel/ {print $4"G"}' | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+ ap)
+ COMPREPLY=( $( compgen -W 'on off any' -- $cur ) )
+ if [ -n "${COMP_IWLIST_SCAN:-}" ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" \
+ $( iwlist ${COMP_WORDS[1]} scan | \
+ awk -F ': ' '/Address/ {print $2}' | \
+ grep "^$cur" ) )
+ fi
+ return 0
+ ;;
+ rate)
+ COMPREPLY=( $( compgen -W 'auto fixed' -- $cur ) )
+ COMPREPLY=( "${COMPREPLY[@]}" \
+ $( iwlist ${COMP_WORDS[1]} rate | \
+ awk '/^[[:space:]]*[0-9]/ {print $1"M"}' | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+ rts)
+ COMPREPLY=( $( compgen -W 'auto fixed off' -- $cur ) )
+ return 0
+ ;;
+ frag)
+ COMPREPLY=( $( compgen -W 'auto fixed off' -- $cur ) )
+ return 0
+ ;;
+ key)
+ COMPREPLY=( $( compgen -W 'off on open restricted' -- $cur ) )
+ return 0
+ ;;
+ enc)
+ COMPREPLY=( $( compgen -W 'off on open restricted' -- $cur ) )
+ return 0
+ ;;
+ power)
+ COMPREPLY=( $( compgen -W 'period timeout off on' -- $cur ) )
+ return 0
+ ;;
+ txpower)
+ COMPREPLY=( $( compgen -W 'off on auto' -- $cur ) )
+ return 0
+ ;;
+ retry)
+ COMPREPLY=( $( compgen -W 'limit lifetime' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --version' -- $cur ) )
+ else
+ _available_interfaces -w
+ fi
+ else
+ COMPREPLY=( $( compgen -W 'essid nwid mode freq channel sens mode \
+ ap nick rate rts frag enc key power txpower commit' -- $cur ) )
+ fi
+
+} &&
+complete -F _iwconfig iwconfig
+
+# Linux iwlist(8) completion
+#
+[ $UNAME = Linux ] && have iwlist &&
+_iwlist()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --version' -- $cur ) )
+ else
+ _available_interfaces -w
+ fi
+ else
+ COMPREPLY=( $( compgen -W 'scan scanning freq frequency \
+ channel rate bit bitrate key enc encryption power \
+ txpower retry ap accesspoint peers event' -- $cur ) )
+ fi
+} &&
+complete -F _iwlist iwlist
+
+# Linux iwspy(8) completion
+#
+[ $UNAME = Linux ] && have iwspy &&
+_iwspy()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --version' -- $cur ) )
+ else
+ _available_interfaces -w
+ fi
+ else
+ COMPREPLY=( $( compgen -W 'setthr getthr off' -- $cur ) )
+ fi
+} &&
+complete -F _iwspy iwspy
+
+# Linux iwpriv(8) completion
+#
+[ $UNAME = Linux ] && have iwpriv &&
+_iwpriv()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ roam)
+ COMPREPLY=( $( compgen -W 'on off' -- $cur ) )
+ return 0
+ ;;
+ port)
+ COMPREPLY=( $( compgen -W 'ad-hoc managed' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --version' -- $cur ) )
+ else
+ _available_interfaces -w
+ fi
+ else
+ COMPREPLY=( $( compgen -W '--all roam port' -- $cur ) )
+ fi
+} &&
+complete -F _iwpriv iwpriv
+
+# RedHat & Debian GNU/Linux if{up,down} completion
+#
+[ $UNAME = Linux ] && { have ifup || have ifdown; } &&
+_ifupdown()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ _configured_interfaces
+ COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- "$cur") )
+ fi
+
+ return 0
+} &&
+complete -F _ifupdown ifup ifdown
+[ $UNAME = Linux ] && have ifstatus && complete -F _ifupdown ifstatus
+
+# Linux ipsec(8) completion (for FreeS/WAN)
+#
+[ $UNAME = Linux ] && have ipsec &&
+_ipsec()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'auto barf eroute klipsdebug look \
+ manual pluto ranbits rsasigkey \
+ setup showdefaults showhostkey spi \
+ spigrp tncfg whack' -- $cur ) )
+ return 0
+ fi
+
+ case ${COMP_WORDS[1]} in
+ auto)
+ COMPREPLY=( $( compgen -W '--asynchronous --up --add --delete \
+ --replace --down --route --unroute \
+ --ready --status --rereadsecrets' \
+ -- $cur ) )
+ ;;
+ manual)
+ COMPREPLY=( $( compgen -W '--up --down --route --unroute \
+ --union' -- $cur ) )
+ ;;
+ ranbits)
+ COMPREPLY=( $( compgen -W '--quick --continuous --bytes' \
+ -- $cur ) )
+ ;;
+ setup)
+ COMPREPLY=( $( compgen -W '--start --stop --restart' -- $cur ) )
+ ;;
+
+ *)
+ ;;
+ esac
+
+ return 0
+} &&
+complete -F _ipsec ipsec
+
+# Postfix completion.
+#
+have postfix && {
+# postfix(1)
+#
+_postfix()
+{
+ local cur prev
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ $cur == '-' ]]; then
+ COMPREPLY=(-c -D -v)
+ return 0
+ fi
+ if [[ $prev == '-c' ]]; then
+ _filedir -d
+ return 0
+ fi
+ if [[ $prev == '-D' ]]; then
+ COMPREPLY=( $( compgen -W 'start' -- "`get_cword`" ) )
+ return 0
+ fi
+ COMPREPLY=( $( compgen -W 'start stop reload abort flush check' -- \
+ "`get_cword`" ) )
+}
+complete -F _postfix postfix
+
+# postalias(1) and postmap(1)
+#
+_postmap()
+{
+ local cur prev len idx
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ $cur == '-' ]]; then
+ COMPREPLY=(-N -f -i -n -o -p -r -v -w -c -d -q)
+ return 0
+ fi
+ if [[ $prev == '-c' ]]; then
+ _filedir -d
+ return 0
+ fi
+ if [[ $prev == -[dq] ]]; then
+ return 0
+ fi
+
+ if [[ "$cur" == *:* ]]; then
+ COMPREPLY=( $( compgen -f -- ${cur#*:} ) )
+ else
+ len=${#cur}
+ idx=0
+ for pval in $( /usr/sbin/postconf -m ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]="$pval:"
+ idx=$(($idx+1))
+ fi
+ done
+ if [[ $idx -eq 0 ]]; then
+ COMPREPLY=( $( compgen -f -- "$cur" ) )
+ fi
+ fi
+ return 0
+}
+complete -F _postmap postmap postalias
+
+# postcat(1)
+#
+_postcat()
+{
+ local cur prev pval len idx qfile
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ $cur == '-' ]]; then
+ COMPREPLY=(-c -q -v)
+ return 0
+ fi
+ if [[ $prev == '-c' ]]; then
+ _filedir -d
+ return 0
+ fi
+
+ qfile=0
+ for idx in "${COMP_WORDS[@]}"; do
+ [[ "$idx" = -q ]] && qfile=1 && break
+ done
+ if [[ $qfile == 1 ]]; then
+ len=${#cur}
+ idx=0
+ for pval in $( mailq | \
+ sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; s/[* !].*$//' ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]=$pval
+ idx=$(($idx+1))
+ fi
+ done
+ return 0
+ else
+ _filedir
+ return 0
+ fi
+}
+complete -F _postcat postcat
+
+# postconf(1)
+#
+_postconf()
+{
+ local cur prev pval len idx eqext
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ if [[ $cur == '-' ]]; then
+ COMPREPLY=(-c -d -e -h -m -l -n -v)
+ return 0
+ fi
+ if [[ $prev == '-c' ]]; then
+ _filedir -d
+ return 0
+ fi
+ if [[ $prev == '-e' ]]; then
+ cur=${cur#[\"\']}
+ eqext='='
+ fi
+ len=${#cur}
+ idx=0
+ for pval in $( /usr/sbin/postconf | cut -d ' ' -f 1 ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]="$pval$eqext"
+ idx=$(($idx+1))
+ fi
+ done
+ return 0
+}
+complete -F _postconf postconf
+
+# postsuper(1)
+#
+_postsuper()
+{
+ local cur prev pval len idx
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ $cur == '-' ]]; then
+ COMPREPLY=(-c -d -h -H -p -r -s -v)
+ return 0
+ fi
+ case $prev in
+ -[dr])
+ len=${#cur}
+ idx=0
+ for pval in $( echo ALL; mailq | \
+ sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; s/[* !].*$//' ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]=$pval
+ idx=$(($idx+1))
+ fi
+ done
+ return 0
+ ;;
+ -h)
+ len=${#cur}
+ idx=0
+ for pval in $( echo ALL; mailq | \
+ sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; s/[* ].*$//; /!$/d' ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]=$pval
+ idx=$(($idx+1))
+ fi
+ done
+ return 0
+ ;;
+ -H)
+ len=${#cur}
+ idx=0
+ for pval in $( echo ALL; mailq | \
+ sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; /^[0-9A-Z]*[* ]/d; s/!.*$//' ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]=$pval
+ idx=$(($idx+1))
+ fi
+ done
+ return 0
+ ;;
+ esac
+ COMPREPLY=( $( compgen -W 'hold incoming active deferred' -- $cur ) )
+ return 0
+}
+complete -F _postsuper postsuper
+}
+
+# cvs(1) completion
+#
+have cvs && {
+set_prefix()
+{
+ [ -z ${prefix:-} ] || prefix=${cur%/*}/
+ [ -r ${prefix:-}CVS/Entries ] || prefix=""
+}
+
+get_entries()
+{
+ local IFS=$'\n'
+ [ -r ${prefix:-}CVS/Entries ] && \
+ entries=$(cut -d/ -f2 -s ${prefix:-}CVS/Entries)
+}
+
+get_modules()
+{
+ if [ -n "$prefix" ]; then
+ COMPREPLY=( $( command ls -d ${cvsroot}/${prefix}/!(CVSROOT) ) )
+ else
+ COMPREPLY=( $( command ls -d ${cvsroot}/!(CVSROOT) ) )
+ fi
+}
+
+_cvs()
+{
+ local cur count mode i cvsroot cvsroots pwd
+ local -a flags miss files entries changed newremoved
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ count=0
+ for i in "${COMP_WORDS[@]}"; do
+ [ $count -eq $COMP_CWORD ] && break
+ # Last parameter was the CVSROOT, now go back to mode selection
+ if [ "${COMP_WORDS[((count))]}" == "$cvsroot" -a "$mode" == "cvsroot" ]; then
+ mode=""
+ fi
+ if [ -z "$mode" ]; then
+ case $i in
+ -d)
+ mode=cvsroot
+ cvsroot=${COMP_WORDS[((count+1))]}
+ ;;
+ @(ad?(d)|new))
+ mode=add
+ ;;
+ @(adm?(in)|rcs))
+ mode=admin
+ ;;
+ ann?(notate))
+ mode=annotate
+ ;;
+ @(checkout|co|get))
+ mode=checkout
+ ;;
+ @(com?(mit)|ci))
+ mode=commit
+ ;;
+ di?(f?(f)))
+ mode=diff
+ ;;
+ ex?(p?(ort)))
+ mode=export
+ ;;
+ ?(un)edit)
+ mode=$i
+ ;;
+ hi?(s?(tory)))
+ mode=history
+ ;;
+ im?(p?(ort)))
+ mode=import
+ ;;
+ re?(l?(ease)))
+ mode=release
+ ;;
+ ?(r)log)
+ mode=log
+ ;;
+ @(rdiff|patch))
+ mode=rdiff
+ ;;
+ @(remove|rm|delete))
+ mode=remove
+ ;;
+ @(rtag|rfreeze))
+ mode=rtag
+ ;;
+ st?(at?(us)))
+ mode=status
+ ;;
+ @(tag|freeze))
+ mode=tag
+ ;;
+ up?(d?(ate)))
+ mode=update
+ ;;
+ *)
+ ;;
+ esac
+ elif [[ "$i" = -* ]]; then
+ flags=( "${flags[@]}" $i )
+ fi
+ count=$((++count))
+ done
+
+ case "$mode" in
+ add)
+ if [[ "$cur" != -* ]]; then
+ set_prefix
+ if [ $COMP_CWORD -gt 1 -a -r ${prefix:-}CVS/Entries ]; then
+ get_entries
+ [ -z "$cur" ] && \
+ files=$( command ls -Ad !(CVS) ) || \
+ files=$( command ls -d ${cur}* 2>/dev/null )
+ for i in "${entries[@]}"; do
+ files=( ${files[@]/#$i//} )
+ done
+ COMPREPLY=( $( compgen -W '${files[@]}' -- \
+ $cur ) )
+ fi
+ else
+ COMPREPLY=( $( compgen -W '-k -m' -- $cur ) )
+ fi
+ ;;
+ admin)
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $( compgen -W '-i -a -A -e -b -c -k -l -u \
+ -L -U -m -M -n -N -o -q -I \
+ -s -t -t- -T -V -x -z' -- \
+ $cur ) )
+ fi
+ ;;
+ annotate)
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $( compgen -W '-D -F -f -l -R -r' -- $cur ) )
+ else
+ get_entries
+ COMPREPLY=( $( compgen -W '${entries[@]}' -- $cur ) )
+ fi
+ ;;
+ checkout)
+ if [[ "$cur" != -* ]]; then
+ [ -z "$cvsroot" ] && cvsroot=$CVSROOT
+ COMPREPLY=( $( cvs -d "$cvsroot" co -c 2> /dev/null | \
+ awk '{print $1}' ) )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W '-A -N -P -R -c -f -l -n -p \
+ -s -r -D -d -k -j' -- $cur ) )
+ fi
+ ;;
+ commit)
+ set_prefix
+
+ if [[ "$cur" != -* ]] && [ -r ${prefix:-}CVS/Entries ]; then
+ # if $COMP_CVS_REMOTE is not null, 'cvs commit' will
+ # complete on remotely checked-out files (requires
+ # passwordless access to the remote repository
+ if [ -n "${COMP_CVS_REMOTE:-}" ]; then
+ # this is the least computationally intensive
+ # way found so far, but other changes
+ # (something other than changed/removed/new)
+ # may be missing
+ changed=( $( cvs -q diff --brief 2>&1 | \
+ sed -ne 's/^Files [^ ]* and \([^ ]*\) differ$/\1/p' ) )
+ newremoved=( $( cvs -q diff --brief 2>&1 | \
+ sed -ne 's/^cvs diff: \([^ ]*\) .*, no comparison available$/\1/p' ) )
+ COMPREPLY=( $( compgen -W '${changed[@]:-} \
+ ${newremoved[@]:-}' -- $cur ) )
+ else
+ _filedir
+ fi
+ else
+ COMPREPLY=( $( compgen -W '-n -R -l -f -F -m -r' -- \
+ $cur ) )
+ fi
+ ;;
+ cvsroot)
+ if [ -r ~/.cvspass ]; then
+ # Ugly escaping because of bash treating ':' specially
+ cvsroots=$( sed 's/^[^ ]* //; s/:/\\:/g' ~/.cvspass )
+ COMPREPLY=( $( compgen -W '$cvsroots' -- $cur ) )
+ fi
+ ;;
+ export)
+ if [[ "$cur" != -* ]]; then
+ [ -z "$cvsroot" ] && cvsroot=$CVSROOT
+ COMPREPLY=( $( cvs -d "$cvsroot" co -c | awk '{print $1}' ) )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W '-N -f -l -R -n \
+ -r -D -d -k' -- $cur ) )
+ fi
+ ;;
+ diff)
+ if [[ "$cur" == -* ]]; then
+ _longopt diff
+ else
+ get_entries
+ COMPREPLY=( $( compgen -W '${entries[@]:-}' -- $cur ) )
+ fi
+ ;;
+ remove)
+ if [[ "$cur" != -* ]]; then
+ set_prefix
+ if [ $COMP_CWORD -gt 1 -a -r ${prefix:-}CVS/Entries ]; then
+ get_entries
+ # find out what files are missing
+ for i in "${entries[@]}"; do
+ [ ! -r "$i" ] && miss=( "${miss[@]}" $i )
+ done
+ COMPREPLY=( $(compgen -W '${miss[@]:-}' -- $cur) )
+ fi
+ else
+ COMPREPLY=( $( compgen -W '-f -l -R' -- $cur ) )
+ fi
+ ;;
+ import)
+ if [[ "$cur" != -* ]]; then
+ # starts with same algorithm as checkout
+ [ -z "$cvsroot" ] && cvsroot=$CVSROOT
+ prefix=${cur%/*}
+ if [ -r ${cvsroot}/${prefix} ]; then
+ get_modules
+ COMPREPLY=( ${COMPREPLY[@]#$cvsroot} )
+ COMPREPLY=( ${COMPREPLY[@]#\/} )
+ fi
+ pwd=$( pwd )
+ pwd=${pwd##*/}
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]} $pwd' -- \
+ $cur ) )
+ else
+ COMPREPLY=( $( compgen -W '-d -k -I -b -m -W' -- $cur ))
+ fi
+ ;;
+ update)
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $( compgen -W '-A -P -C -d -f -l -R -p \
+ -k -r -D -j -I -W' -- \
+ $cur ) )
+ fi
+ ;;
+ "")
+ COMPREPLY=( $( compgen -W 'add admin annotate checkout ci co \
+ commit diff delete edit export \
+ freeze get history import log new \
+ patch rcs rdiff release remove \
+ rfreeze rlog rm rtag stat status \
+ tag unedit up update -H -Q -q -b \
+ -d -e -f -l -n -t -r -v -w -x -z \
+ --help --version' -- $cur ) )
+ ;;
+ *)
+ ;;
+ esac
+
+ return 0
+}
+complete -F _cvs $default cvs
+}
+
+have rpm && {
+# helper functions for rpm completion
+#
+_rpm_installed_packages()
+{
+ local ver nodig nosig
+
+ if [ -r /var/log/rpmpkgs -a \
+ /var/log/rpmpkgs -nt /var/lib/rpm/Packages ]; then
+ # using RHL 7.2 or later - this is quicker than querying the DB
+ COMPREPLY=( $( sed -ne \
+ 's|^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9a-z.@]\+.*\.rpm$|\1|p' \
+ /var/log/rpmpkgs ) )
+ else
+ nodig=""
+ nosig=""
+ ver=$(rpm --version)
+ ver=${ver##* }
+
+ if [[ "$ver" > "4.0.4" ]]; then
+ nodig="--nodigest"
+ fi
+ if [[ "$ver" > "4.0.99" ]]; then
+ nosig="--nosignature"
+ fi
+
+ COMPREPLY=( $( rpm -qa $nodig $nosig | sed -ne \
+ 's|^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9a-z.@]\+$|\1|p' ) )
+ fi
+}
+
+_rpm_groups()
+{
+ local IFS=$'\t'
+ # remove trailing backslash, or grep will complain
+ cur=${cur%"\\"}
+ COMPREPLY=( $( rpm -qa $nodig $nosig --queryformat '%{group}\n' | \
+ grep "^$cur" ) )
+ # backslash escape spaces and translate newlines to tabs
+ COMPREPLY=( $( echo "${COMPREPLY[@]}" | sed 's/ /\\ /g' | tr '\n' '\t' ) )
+}
+
+# rpm(8) completion
+#
+_rpm()
+{
+ local cur prev ver nodig nosig
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ nodig=""
+ nosig=""
+ ver=$(rpm --version); ver=${ver##* }
+
+ if [[ "$ver" > "4.0.4" ]]; then
+ nodig="--nodigest"
+ fi
+ if [[ "$ver" > "4.0.99" ]]; then
+ nosig="--nosignature"
+ fi
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ # first parameter on line
+ case "$cur" in
+ -b*)
+ COMPREPLY=( $( compgen -W '-ba -bb -bc -bi -bl -bp -bs'\
+ -- $cur ) )
+ ;;
+ -t*)
+ COMPREPLY=( $( compgen -W '-ta -tb -tc -ti -tl -tp -ts'\
+ -- $cur ) )
+ ;;
+ --*)
+ COMPREPLY=( $( compgen -W '--help --version --initdb \
+ --checksig --recompile --rebuild --resign --addsign \
+ --rebuilddb --showrc --setperms --setugids --tarbuild \
+ --eval --install --upgrade --query --freshen --erase \
+ --verify --querytags --rmsource --rmspec --clean \
+ --import' -- $cur ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W '-b -e -F -i -q -t -U -V' \
+ -- $cur ) )
+ ;;
+ esac
+
+ return 0
+ fi
+
+ case "$prev" in
+ --@(@(db|exclude)path|prefix|relocate|root))
+ _filedir -d
+ return 0
+ ;;
+ --eval)
+ # get a list of macros
+ COMPREPLY=( $( sed -ne 's|^\(%'${cur#\%}'[^ '$'\t'']*\).*$|\1|p' \
+ /usr/lib/rpm/macros ) )
+ return 0
+ ;;
+ --pipe)
+ COMPREPLY=( $( compgen -c -- $cur ) )
+ return 0
+ ;;
+ --rcfile)
+ _filedir
+ return 0
+ ;;
+ --specfile)
+ # complete on .spec files
+ _filedir spec
+ return 0
+ ;;
+ --whatprovides)
+ if [[ "$cur" == */* ]]; then
+ _filedir
+ else
+ # complete on capabilities
+ COMPREPLY=( $( rpm -qa $nodig $nosig --queryformat \
+ '%{providename}\n' | grep "^$cur" ) )
+ fi
+ return 0
+ ;;
+ --whatrequires)
+ # complete on capabilities
+ COMPREPLY=( $( rpm -qa $nodig $nosig --queryformat \
+ '%{requirename}\n' | grep "^$cur" ) )
+ return 0
+ ;;
+ esac
+
+ case "${COMP_WORDS[1]}" in
+ -@([iFU]*|-install|-freshen|-upgrade))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--percent --force --test \
+ --replacepkgs --replacefiles --root --excludedocs \
+ --includedocs --noscripts --rcfile --ignorearch \
+ --dbpath --prefix --ignoreos --nodeps --allfiles \
+ --ftpproxy --ftpport --justdb --httpproxy --httpport \
+ --noorder --relocate --badreloc --notriggers \
+ --excludepath --ignoresize --oldpackage --define \
+ --eval --pipe --queryformat --repackage --nosuggests \
+ --nodigest --nosignature' -- $cur ) )
+ else
+ _filedir 'rpm'
+ fi
+ ;;
+ -@(e|-erase))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--allmatches --noscripts \
+ --notriggers --nodeps --test --repackage' -- $cur ) )
+ else
+ _rpm_installed_packages
+ fi
+ ;;
+ -@(q*|-query))
+ # check whether we're doing file completion
+ if [ "${COMP_LINE#* -*([^ -])f}" != "$COMP_LINE" ]; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--scripts --root \
+ --rcfile --requires --ftpport --ftpproxy \
+ --httpproxy --httpport --provides --triggers \
+ --dump --changelog --dbpath \
+ --last --filesbypkg \
+ --info --list --state \
+ --docfiles --configfiles --queryformat \
+ --conflicts --obsoletes \
+ --nodigest --nosignature \
+ --triggerscripts' -- $cur ) )
+ else
+ _filedir
+ fi
+ elif [ "${COMP_LINE#* -*([^ -])g}" != "$COMP_LINE" ]; then
+ _rpm_groups
+ elif [ "${COMP_LINE#* -*([^ -])p}" != "$COMP_LINE" ]; then
+ # uninstalled package completion
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--scripts --root \
+ --rcfile --whatprovides --whatrequires \
+ --requires --triggeredby --ftpport --ftpproxy \
+ --httpproxy --httpport --provides --triggers \
+ --dump --changelog --dbpath --filesbypkg \
+ --define --eval --pipe --showrc --info --list \
+ --state --docfiles --configfiles --queryformat\
+ --conflicts --obsoletes --nodigest \
+ --nosignature' -- $cur ) )
+ else
+ _filedir 'rpm'
+ fi
+ else
+ # installed package completion
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--scripts --root \
+ --rcfile --whatprovides --whatrequires \
+ --requires --triggeredby --ftpport --ftpproxy \
+ --httpproxy --httpport --provides --triggers \
+ --dump --changelog --dbpath --specfile \
+ --querybynumber --last --filesbypkg --define \
+ --eval --pipe --showrc --info --list --state \
+ --docfiles --configfiles --queryformat \
+ --conflicts --obsoletes --pkgid --hdrid \
+ --fileid --tid --nodigest --nosignature \
+ --triggerscripts' -- $cur ) )
+ elif [ "${COMP_LINE#* -*([^ -])a}" == "$COMP_LINE" ]; then
+ _rpm_installed_packages
+ fi
+ fi
+ ;;
+ -@(K*|-checksig))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--nopgp --nogpg --nomd5 \
+ --nodigest --nosignature' -- $cur ) )
+ else
+ _filedir 'rpm'
+ fi
+ ;;
+ -@([Vy]*|-verify))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--root --rcfile --dbpath \
+ --nodeps --nogroup --nolinkto --nomode --nomtime \
+ --nordev --nouser --nofiles --noscripts --nomd5 \
+ --querytags --specfile --whatrequires --whatprovides \
+ --nodigest --nosignature' -- $cur ) )
+ # check whether we're doing file completion
+ elif [ "${COMP_LINE#* -*([^ -])f}" != "$COMP_LINE" ]; then
+ _filedir
+ elif [ "${COMP_LINE#* -*([^ -])g}" != "$COMP_LINE" ]; then
+ _rpm_groups
+ elif [ "${COMP_LINE#* -*([^ -])p}" != "$COMP_LINE" ]; then
+ _filedir 'rpm'
+ else
+ _rpm_installed_packages
+ fi
+ ;;
+ -[bt]*)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--short-circuit --timecheck \
+ --clean --rmsource --rmspec --test --sign --buildroot \
+ --target -- buildarch --buildos --nobuild --nodeps \
+ --nodirtokens' -- $cur ) )
+ elif [[ ${COMP_WORDS[1]} == -b* ]]; then
+ _filedir 'spec'
+ else
+ _filedir '@(tgz|tar.@(gz|bz2))'
+ fi
+ ;;
+ --re@(build|compile))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--nodeps --rmsource \
+ --rmspec --sign --nodirtokens --target' -- $cur ) )
+ else
+ _filedir '?(no)src.rpm'
+ fi
+ ;;
+ --tarbuild)
+ _filedir '@(tgz|tar.@(gz|bz2))'
+ ;;
+ --@(re|add)sign)
+ _filedir 'rpm'
+ ;;
+ --set@(perms|gids))
+ _rpm_installed_packages
+ ;;
+ --@(clean|rms@(ource|pec)))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--clean --rmsource \
+ --rmspec' -- $cur ) )
+ else
+ _filedir 'spec'
+ fi
+ ;;
+ --@(import|dbpath|root))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--import --dbpath --root' \
+ -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ esac
+
+ return 0
+}
+complete -F _rpm $filenames rpm rpmbuild
+}
+
+# Debian apt-get(8) completion.
+#
+have apt-get &&
+_apt_get()
+{
+ local cur prev special i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == @(install|remove|purge|source|build-dep) ]]; then
+ special=${COMP_WORDS[i]}
+ fi
+ done
+
+ if [ -n "$special" ]; then
+ case $special in
+ remove|purge)
+ if [ -f /etc/debian_version ]; then
+ # Debian system
+ COMPREPLY=( $( _comp_dpkg_installed_packages \
+ $cur ) )
+ else
+ # assume RPM based
+ _rpm_installed_packages
+ fi
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) )
+ return 0
+ ;;
+
+ esac
+ fi
+
+ case "$prev" in
+ -@(c|-config-file))
+ _filedir
+ return 0
+ ;;
+
+ -@(t|-target-release|-default-release))
+ COMPREPLY=( $( apt-cache policy | \
+ grep "release.o=Debian,a=$cur" | \
+ sed -e "s/.*a=\(\w*\).*/\1/" | uniq 2> /dev/null) )
+ return 0
+ ;;
+
+ esac
+
+ if [[ "$cur" == -* ]]; then
+
+ COMPREPLY=( $( compgen -W '-d -f -h -v -m -q -s -y \
+ -u -t -b -c -o --download-only --fix-broken \
+ --help --version --ignore-missing \
+ --fix-missing --no-download --quiet --simulate \
+ --just-print --dry-run --recon --no-act --yes \
+ --assume-yes --show-upgraded --only-source \
+ --compile --build --ignore-hold \
+ --target-release --no-upgrade --force-yes \
+ --print-uris --purge --reinstall \
+ --list-cleanup --default-release \
+ --trivial-only --no-remove --diff-only \
+ --tar-only --config-file --option --auto-remove' -- $cur ) )
+ else
+
+ COMPREPLY=( $( compgen -W 'update upgrade dselect-upgrade \
+ dist-upgrade install remove purge source \
+ build-dep check clean autoclean autoremove' \
+ -- $cur ) )
+
+ fi
+
+
+ return 0
+} &&
+complete -F _apt_get $filenames apt-get
+
+# Debian apt-cache(8) completion.
+#
+have apt-cache &&
+_apt_cache()
+{
+ local cur prev special i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+
+ if [ "$cur" != show ]; then
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == @(add|depends|dotty|policy|rdepends|madison|show?(pkg|src|)) ]]; then
+ special=${COMP_WORDS[i]}
+ fi
+ done
+ fi
+
+
+ if [ -n "$special" ]; then
+ case $special in
+ add)
+ _filedir
+ return 0
+ ;;
+
+ *)
+ COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) )
+ return 0
+ ;;
+
+ esac
+ fi
+
+
+ case "$prev" in
+ -@(c|p|s|-config-file|-@(pkg|src)-cache))
+ _filedir
+ return 0
+ ;;
+ search)
+ if [[ "$cur" != -* ]]; then
+ return 0
+ fi
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+
+ COMPREPLY=( $( compgen -W '-h -v -p -s -q -i -f -a -g -c \
+ -o --help --version --pkg-cache --src-cache \
+ --quiet --important --full --all-versions \
+ --no-all-versions --generate --no-generate \
+ --names-only --all-names --recurse \
+ --config-file --option' -- $cur ) )
+ else
+
+ COMPREPLY=( $( compgen -W 'add gencaches show showpkg showsrc \
+ stats dump dumpavail unmet search search \
+ depends rdepends pkgnames dotty xvcg \
+ policy madison' -- $cur ) )
+
+ fi
+
+
+ return 0
+} &&
+complete -F _apt_cache $filenames apt-cache
+
+
+# Debian aptitude(1) completion
+#
+have aptitude && {
+have grep-status && {
+_comp_dpkg_hold_packages()
+{
+ grep-status -P -e "^$1" -a -FStatus 'hold' -n -s Package
+}
+} || {
+_comp_dpkg_hold_packages()
+{
+ grep -B 2 'hold' /var/lib/dpkg/status | grep "Package: $1" \
+ | cut -d\ -f2
+}
+}
+
+_aptitude()
+{
+ local cur dashoptions prev special i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+
+ dashoptions='-S -u -i -h --help --version -s --simulate -d \
+ --download-only -P --prompt -y --assume-yes -F \
+ --display-format -O --sort -w --width -f -r -g \
+ --with-recommends --with-suggests -R -G \
+ --without-recommends --without-suggests -t \
+ --target-release -V --show-versions -D --show-deps\
+ -Z -v --verbose --purge-unused'
+
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == @(install|reinstall|hold|unhold|markauto|unmarkauto|dist-upgrade|full-upgrade|download|show|forbid-version|purge|remove|changelog|why|why-not|keep|keep-all) ]]; then
+ special=${COMP_WORDS[i]}
+ fi
+ #exclude some mutually exclusive options
+ [[ ${COMP_WORDS[i]} == '-u' ]] && dashoptions=${dashoptions/-i}
+ [[ ${COMP_WORDS[i]} == '-i' ]] && dashoptions=${dashoptions/-u}
+ done
+
+ if [[ -n "$special" ]]; then
+ case $special in
+ @(install|hold|markauto|unmarkauto|dist-upgrade|full-upgrade|download|show|changelog|why|why-not))
+ COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) )
+ return 0
+ ;;
+ @(purge|remove|reinstall|forbid-version))
+ COMPREPLY=( $( _comp_dpkg_installed_packages $cur ) )
+ return 0
+ ;;
+ unhold)
+ COMPREPLY=( $( _comp_dpkg_hold_packages $cur ) )
+ return 0
+ ;;
+
+ esac
+ fi
+
+ case $prev in
+ # don't complete anything if these options are found
+ @(autoclean|clean|forget-new|search|upgrade|safe-upgrade|update|keep-all))
+ return 0
+ ;;
+
+ -S)
+ _filedir
+ return 0
+ ;;
+
+ -@(t|-target-release|-default-release))
+ COMPREPLY=( $( apt-cache policy | \
+ grep "release.o=Debian,a=$cur" | \
+ sed -e "s/.*a=\(\w*\).*/\1/" | uniq 2> /dev/null ) )
+ return 0
+ ;;
+
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W "$dashoptions" -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W 'update upgrade safe-upgrade forget-new clean \
+ autoclean install reinstall remove \
+ hold unhold purge markauto unmarkauto why why-not \
+ dist-upgrade full-upgrade download search show \
+ forbid-version changelog keep-all' -- $cur ) )
+ fi
+
+
+ return 0
+}
+complete -F _aptitude $default aptitude
+}
+
+# Debian apt-build(1) completion.
+#
+have apt-build &&
+_apt_build()
+{
+ local cur prev special i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == @(install|remove|source|info|clean) ]]; then
+ special=${COMP_WORDS[i]}
+ fi
+ done
+
+ if [ -n "$special" ]; then
+ case $special in
+ @(install|source|info))
+ COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) )
+ return 0
+ ;;
+ remove)
+ COMPREPLY=( $( _comp_dpkg_installed_packages \
+ $cur ) )
+ return 0
+ ;;
+ *)
+ return 0
+ ;;
+ esac
+ fi
+
+ case "$prev" in
+
+ --@(patch|build-dir|repository-dir))
+ _filedir
+ return 0
+ ;;
+
+ -@(h|-help))
+ return 0
+ ;;
+
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --show-upgraded -u --build-dir \
+ --repository-dir --build-only \
+ --build-command --reinstall --rebuild \
+ --remove-builddep --no-wrapper --purge \
+ --patch --patch-strip -p --yes -y \
+ --version -v --no-source' -- $cur ) )
+
+ else
+ COMPREPLY=( $( compgen -W 'update upgrade install remove \
+ source dist-upgrade world clean info \
+ clean-build update-repository ' -- $cur ) )
+ fi
+
+
+ return 0
+} &&
+complete -F _apt_build $filenames apt-build
+
+# chsh(1) completion
+#
+_chsh()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ "$prev" = "-s" ]; then
+ if [ -f /etc/debian_version ]; then
+ COMPREPLY=( $( </etc/shells ) )
+ else
+ COMPREPLY=( $( chsh -l | grep "^$cur" ) )
+ fi
+ else
+ COMPREPLY=( $( compgen -u -- $cur ) )
+ fi
+
+ return 0
+}
+complete -F _chsh chsh
+
+# chkconfig(8) completion
+#
+have chkconfig &&
+_chkconfig()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ @([1-6]|--@(list|add|del)))
+ _services
+ return 0
+ ;;
+ --level)
+ COMPREPLY=( $( compgen -W '1 2 3 4 5 6' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--list --add --del --level' -- $cur ) )
+ else
+ if [ $COMP_CWORD -eq 2 -o $COMP_CWORD -eq 4 ]; then
+ COMPREPLY=( $( compgen -W 'on off reset' -- $cur ) )
+ else
+ _services
+ fi
+ fi
+} &&
+complete -F _chkconfig chkconfig
+
+# This function provides simple user@host completion
+#
+_user_at_host() {
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ $cur == *@* ]]; then
+ _known_hosts
+ else
+ COMPREPLY=( $( compgen -u -- "$cur" ) )
+ fi
+
+ return 0
+}
+shopt -u hostcomplete && complete -F _user_at_host $nospace talk ytalk finger
+
+# This function performs host completion based on ssh's known_hosts files,
+# defaulting to standard host completion if they don't exist.
+#
+_known_hosts()
+{
+ local cur curd ocur user suffix aliases global_kh user_kh hosts i host
+ local -a kh khd config
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ ocur=$cur
+
+ [ "$1" = -a ] || [ "$2" = -a ] && aliases='yes'
+ [ "$1" = -c ] || [ "$2" = -c ] && suffix=':'
+ [[ $cur == *@* ]] && user=${cur%@*}@ && cur=${cur#*@}
+ kh=()
+
+ # ssh config files
+ [ -r /etc/ssh/ssh_config ] &&
+ config=( "${config[@]}" "/etc/ssh/ssh_config" )
+ [ -r "${HOME}/.ssh/config" ] &&
+ config=( "${config[@]}" "${HOME}/.ssh/config" )
+ [ -r "${HOME}/.ssh2/config" ] &&
+ config=( "${config[@]}" "${HOME}/.ssh2/config" )
+
+ if [ ${#config[@]} -gt 0 ]; then
+ # expand path (if present) to global known hosts file
+ global_kh=$( eval echo $( sed -ne 's/^[ \t]*[Gg][Ll][Oo][Bb][Aa][Ll][Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee]['"$'\t '"']*\(.*\)$/\1/p' "${config[@]}" ) )
+ # expand path (if present) to user known hosts file
+ user_kh=$( eval echo $( sed -ne 's/^[ \t]*[Uu][Ss][Ee][Rr][Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee]['"$'\t '"']*\(.*\)$/\1/p' "${config[@]}" ) )
+ fi
+
+ # Global known_hosts files
+ [ -r "$global_kh" ] &&
+ kh=( "${kh[@]}" "$global_kh" )
+ [ -r /etc/ssh/ssh_known_hosts ] &&
+ kh=( "${kh[@]}" /etc/ssh/ssh_known_hosts )
+ [ -r /etc/ssh/ssh_known_hosts2 ] &&
+ kh=( "${kh[@]}" /etc/ssh/ssh_known_hosts2 )
+ [ -r /etc/known_hosts ] &&
+ kh=( "${kh[@]}" /etc/known_hosts )
+ [ -r /etc/known_hosts2 ] &&
+ kh=( "${kh[@]}" /etc/known_hosts2 )
+ [ -d /etc/ssh2/knownhosts ] &&
+ khd=( "${khd[@]}" /etc/ssh2/knownhosts/*pub )
+
+ # User known_hosts files
+ [ -r "$user_kh" ] &&
+ kh=( "${kh[@]}" "$user_kh" )
+ [ -r ~/.ssh/known_hosts ] &&
+ kh=( "${kh[@]}" ~/.ssh/known_hosts )
+ [ -r ~/.ssh/known_hosts2 ] &&
+ kh=( "${kh[@]}" ~/.ssh/known_hosts2 )
+ [ -d ~/.ssh2/hostkeys ] &&
+ khd=( "${khd[@]}" ~/.ssh2/hostkeys/*pub )
+
+ # If we have known_hosts files to use
+ if [ ${#kh[@]} -gt 0 -o ${#khd[@]} -gt 0 ]; then
+ # Escape slashes and dots in paths for awk
+ cur=${cur//\//\\\/}
+ cur=${cur//\./\\\.}
+ curd=$cur
+
+ if [[ "$cur" == [0-9]*.* ]]; then
+ # Digits followed by a dot - just search for that
+ cur="^$cur.*"
+ elif [[ "$cur" == [0-9]* ]]; then
+ # Digits followed by no dot - search for digits followed
+ # by a dot
+ cur="^$cur.*\."
+ elif [ -z "$cur" ]; then
+ # A blank - search for a dot or an alpha character
+ cur="[a-z.]"
+ else
+ cur="^$cur"
+ fi
+
+ if [ ${#kh[@]} -gt 0 ]; then
+
+ # FS needs to look for a comma separated list
+ COMPREPLY=( $( awk 'BEGIN {FS=","}
+ /^[^|]/ {for (i=1; i<=2; ++i) { \
+ gsub(" .*$", "", $i); \
+ if ($i ~ /'$cur'/) {print $i} \
+ }}' "${kh[@]}" 2>/dev/null ) )
+ fi
+ if [ ${#khd[@]} -gt 0 ]; then
+ # Needs to look for files called
+ # .../.ssh2/key_22_<hostname>.pub
+ # dont fork any processes, because in a cluster environment,
+ # there can be hundreds of hostkeys
+ for i in "${khd[@]}" ; do
+ if [[ "$i" == *key_22_$curd*.pub ]] && [ -r "$i" ] ; then
+ host=${i/#*key_22_/}
+ host=${host/%.pub/}
+ COMPREPLY=( "${COMPREPLY[@]}" $host )
+ fi
+ done
+ fi
+
+ # append any available aliases from config files
+ if [ ${#config[@]} -gt 0 ] && [ -n "$aliases" ]; then
+ local host_aliases=$( sed -ne 's/^[Hh][Oo][Ss][Tt]\([Nn][Aa][Mm][Ee]\)\?['"$'\t '"']\+\([^*?]*\)$/\2/p' "${config[@]}" )
+ hosts=$( compgen -W "$host_aliases" -- $ocur )
+ COMPREPLY=( "${COMPREPLY[@]}" $hosts )
+ fi
+
+ # Now add results of normal hostname completion
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -A hostname -- $ocur ) )
+
+ # apply suffix
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ COMPREPLY[i]=$user${COMPREPLY[i]}$suffix
+ done
+ else
+ # Just do normal hostname completion
+ COMPREPLY=( $( compgen -A hostname -S "$suffix" -- $cur ) )
+ fi
+
+ return 0
+}
+complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 \
+ ping ping6 fping fping6 telnet host nslookup rsh rlogin ftp dig ssh-installkeys mtr
+
+# ssh(1) completion
+#
+have ssh && {
+_ssh()
+{
+ local cur prev
+ local -a config
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -*c)
+ COMPREPLY=( $( compgen -W 'blowfish 3des 3des-cbc blowfish-cbc \
+ arcfour cast128-cbc' -- $cur ) )
+ ;;
+ -*i)
+ _filedir
+ ;;
+ -*l)
+ COMPREPLY=( $( compgen -u -- $cur ) )
+ ;;
+ *)
+ _known_hosts -a
+
+ [ $COMP_CWORD -eq 1 ] || \
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -c -- $cur ) )
+ esac
+
+ return 0
+}
+shopt -u hostcomplete && complete -F _ssh ssh slogin sftp xhost autossh
+
+# scp(1) completion
+#
+_scp()
+{
+ local cur userhost path
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _expand || return 0
+
+ if [[ "$cur" == *:* ]]; then
+ local IFS=$'\t\n'
+ # remove backslash escape from :
+ cur=${cur/\\:/:}
+ userhost=${cur%%?(\\):*}
+ path=${cur#*:}
+ # unescape spaces
+ path=${path//\\\\\\\\ / }
+ if [ -z "$path" ]; then
+ # default to home dir of specified user on remote host
+ path=$(ssh -o 'Batchmode yes' $userhost pwd 2>/dev/null)
+ fi
+ # escape spaces; remove executables, aliases, pipes and sockets;
+ # add space at end of file names
+ COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \
+ command ls -aF1d "$path*" 2>/dev/null | \
+ sed -e "s/[][(){}<>\",:;^&!$&=?\`|\\ ']/\\\\\\\\\\\\&/g" \
+ -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' ) )
+ return 0
+ fi
+
+ [[ "$cur" == */* ]] || _known_hosts -c -a
+ local IFS=$'\t\n'
+ COMPREPLY=( "${COMPREPLY[@]}" $( command ls -aF1d $cur* \
+ 2>/dev/null | sed \
+ -e "s/[][(){}<>\",:;^&!$&=?\`|\\ ']/\\\\&/g" \
+ -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' ) )
+ return 0
+}
+complete -F _scp $nospace scp
+}
+
+# rsync(1) completion
+#
+have rsync &&
+_rsync()
+{
+ local cur prev shell i userhost path
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ _expand || return 0
+
+ case "$prev" in
+ --@(config|password-file|include-from|exclude-from))
+ _filedir
+ return 0
+ ;;
+ -@(T|-temp-dir|-compare-dest))
+ _filedir -d
+ return 0
+ ;;
+ -@(e|-rsh))
+ COMPREPLY=( $( compgen -W 'rsh ssh' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W '-v -q -c -a -r -R -b -u -l -L -H \
+ -p -o -g -D -t -S -n -W -x -B -e -C -I -T -P \
+ -z -h -4 -6 --verbose --quiet --checksum \
+ --archive --recursive --relative --backup \
+ --backup-dir --suffix= --update --links \
+ --copy-links --copy-unsafe-links --safe-links \
+ --hard-links --perms --owner --group --devices\
+ --times --sparse --dry-run --whole-file \
+ --no-whole-file --one-file-system \
+ --block-size= --rsh= --rsync-path= \
+ --cvs-exclude --existing --ignore-existing \
+ --delete --delete-excluded --delete-after \
+ --ignore-errors --max-delete= --partial \
+ --force --numeric-ids --timeout= \
+ --ignore-times --size-only --modify-window= \
+ --temp-dir= --compare-dest= --compress \
+ --exclude= --exclude-from= --include= \
+ --include-from= --version --daemon --no-detach\
+ --address= --config= --port= --blocking-io \
+ --no-blocking-io --stats --progress \
+ --log-format= --password-file= --bwlimit= \
+ --write-batch= --read-batch= --help' -- $cur ))
+ ;;
+ *:*)
+ # find which remote shell is used
+ shell=rsh
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == -@(e|-rsh) ]]; then
+ shell=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+ if [[ "$shell" == ssh ]]; then
+ # remove backslash escape from :
+ cur=${cur/\\:/:}
+ userhost=${cur%%?(\\):*}
+ path=${cur#*:}
+ # unescape spaces
+ path=${path//\\\\\\\\ / }
+ if [ -z "$path" ]; then
+ # default to home dir of specified
+ # user on remote host
+ path=$(ssh -o 'Batchmode yes' \
+ $userhost pwd 2>/dev/null)
+ fi
+ # escape spaces; remove executables, aliases, pipes
+ # and sockets; add space at end of file names
+ COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \
+ command ls -aF1d "$path*" 2>/dev/null | \
+ sed -e 's/ /\\\\\\\ /g' -e 's/[*@|=]$//g' \
+ -e 's/[^\/]$/& /g' ) )
+ fi
+ ;;
+ *)
+ _known_hosts -c -a
+ _filedir
+ ;;
+ esac
+
+ return 0
+} &&
+complete -F _rsync $nospace $filenames rsync
+
+# Linux route(8) completion
+#
+[ $UNAME = Linux ] &&
+_route()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ "$prev" = dev ]; then
+ COMPREPLY=( $( ifconfig -a | sed -ne 's|^\('$cur'[^ ]*\).*$|\1|p' ))
+ return 0
+ fi
+
+ COMPREPLY=( $( compgen -W 'add del -host -net netmask metric mss \
+ window irtt reject mod dyn reinstate dev \
+ default gw' -- $cur ) )
+
+ COMPREPLY=( $( echo " ${COMP_WORDS[@]}" | \
+ (while read -d ' ' i; do
+ [ "$i" == "" ] && continue
+ # flatten array with spaces on either side,
+ # otherwise we cannot grep on word
+ # boundaries of first and last word
+ COMPREPLY=" ${COMPREPLY[@]} "
+ # remove word from list of completions
+ COMPREPLY=( ${COMPREPLY/ $i / } )
+ done
+ echo "${COMPREPLY[@]}")
+ ) )
+ return 0
+}
+[ $UNAME = Linux ] && complete -F _route route
+
+# GNU make(1) completion
+#
+have make || have gmake || have gnumake || have pmake &&
+_make()
+{
+ local file makef makef_dir="." makef_inc cur prev i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # --name value style option
+ case $prev in
+ -@(f|o|W))
+ _filedir
+ return 0
+ ;;
+ -@(I|C))
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ --@(file|makefile))
+ _filedir
+ return 0
+ ;;
+ --@(directory|include-dir))
+ _filedir -d
+ return 0
+ ;;
+ esac
+ fi
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-b -m -B -C -d -e -f -h -i -I\
+ -j -l -k -n -o -p -q -r -R - s -S -t -v -w -W \
+ --always-make --directory= --debug \
+ --environment-overrides --file= --makefile= --help \
+ --ignore-errors --include-dir= --jobs --load-average \
+ --max-load --keep-going --just-print --dry-run \
+ --recon --old-file= --assume-old= --print-data-base \
+ --question --no-builtin-rules --no-builtin-variables \
+ --silent --quiet --no-keep-goind --stop --touch \
+ --version --print-directory --no-print-directory \
+ --what-if= --new-file= --assume-new= \
+ --warn-undefined-variables' -- $cur ) )
+ else
+ # before we check for makefiles, see if a path was specified
+ # with -C
+ for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do
+ if [[ ${COMP_WORDS[i]} == -C ]]; then
+ # eval for tilde expansion
+ eval makef_dir=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+
+ # make reads `GNUmakefile', then `makefile', then `Makefile'
+ if [ -f ${makef_dir}/GNUmakefile ]; then
+ makef=${makef_dir}/GNUmakefile
+ elif [ -f ${makef_dir}/makefile ]; then
+ makef=${makef_dir}/makefile
+ elif [ -f ${makef_dir}/Makefile ]; then
+ makef=${makef_dir}/Makefile
+ else
+ makef=${makef_dir}/*.mk # local convention
+ fi
+
+ # before we scan for targets, see if a Makefile name was
+ # specified with -f
+ for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do
+ if [[ ${COMP_WORDS[i]} == -f ]]; then
+ # eval for tilde expansion
+ eval makef=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+
+ [ ! -f $makef ] && return 0
+
+ # deal with included Makefiles
+ makef_inc=$( grep -E '^-?include' $makef | sed -e "s,^.* ,"$makef_dir"/," )
+
+ for file in $makef_inc; do
+ [ -f $file ] && makef="$makef $file"
+ done
+
+ COMPREPLY=( $( awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ \
+ {split($1,A,/ /);for(i in A)print A[i]}' \
+ $makef 2>/dev/null | command grep "^$cur" ))
+ fi
+} &&
+complete -f -F _make $filenames make gmake gnumake pmake
+
+# GNU tar(1) completion
+#
+_tar()
+{
+ local cur ext regex tar untar
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'c t x u r d A' -- $cur ) )
+ return 0
+ fi
+
+ case "${COMP_WORDS[1]}" in
+ ?(-)[cr]*f)
+ _filedir
+ return 0
+ ;;
+ +([^IZzjy])f)
+ ext='t@(ar?(.@(Z|gz|bz?(2)))|gz|bz?(2))'
+ regex='t\(ar\(\.\(Z\|gz\|bz2\?\)\)\?\|gz\|bz2\?\)'
+ ;;
+ *[Zz]*f)
+ ext='t?(ar.)@(gz|Z)'
+ regex='t\(ar\.\)\?\(gz\|Z\)'
+ ;;
+ *[Ijy]*f)
+ ext='t?(ar.)bz?(2)'
+ regex='t\(ar\.\)\?bz2\?'
+ ;;
+ *)
+ _filedir
+ return 0
+ ;;
+
+ esac
+
+ if [[ "$COMP_LINE" == *$ext' ' ]]; then
+ # complete on files in tar file
+ #
+ # get name of tar file from command line
+ tar=$( echo "$COMP_LINE" | \
+ sed -e 's/^.* \([^ ]*'$regex'\) .*$/\1/' )
+ # devise how to untar and list it
+ untar=t${COMP_WORDS[1]//[^Izjyf]/}
+
+ COMPREPLY=( $( compgen -W "$( echo $( tar $untar $tar \
+ 2>/dev/null ) )" -- "$cur" ) )
+ return 0
+ fi
+
+ # file completion on relevant files
+ _filedir "$ext"
+
+ return 0
+}
+[ -n "${COMP_TAR_INTERNAL_PATHS:-}" ] && complete -F _tar $dirnames tar ||
+ complete -F _tar $filenames tar
+
+# jar(1) completion
+#
+have jar &&
+_jar()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD = 1 ]; then
+ COMPREPLY=( $( compgen -W 'c t x u' -- $cur ) )
+ return 0
+ fi
+
+ case "${COMP_WORDS[1]}" in
+ *c*f)
+ _filedir
+ ;;
+ *f)
+ _filedir '?(e|j|w)ar'
+ ;;
+ *)
+ _filedir
+ ;;
+ esac
+} &&
+complete -F _jar $filenames jar
+
+# Linux iptables(8) completion
+#
+have iptables &&
+_iptables()
+{
+ local cur prev table chain
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ chain='s/^Chain \([^ ]\+\).*$/\1/p'
+
+ if [[ $COMP_LINE == *-t\ *filter* ]]; then
+ table="-t filter"
+ elif [[ $COMP_LINE == *-t\ *nat* ]]; then
+ table="-t nat"
+ elif [[ $COMP_LINE == *-t\ *mangle* ]]; then
+ table="-t mangle"
+ fi
+
+ case "$prev" in
+ -*[AIDRPFXLZ])
+ COMPREPLY=( $( compgen -W '`iptables $table -nL | \
+ sed -ne "s/^Chain \([^ ]\+\).*$/\1/p"`' -- $cur ) )
+ ;;
+ -*t)
+ COMPREPLY=( $( compgen -W 'nat filter mangle' -- $cur ) )
+ ;;
+ -j)
+ if [ "$table" = "-t filter" -o "$table" = "" ]; then
+ COMPREPLY=( $( compgen -W 'ACCEPT DROP LOG ULOG REJECT \
+ `iptables $table -nL | sed -ne "$chain" \
+ -e "s/INPUT|OUTPUT|FORWARD|PREROUTING|POSTROUTING//"`' -- \
+ $cur ) )
+ elif [ "$table" = "-t nat" ]; then
+ COMPREPLY=( $( compgen -W 'ACCEPT DROP LOG ULOG REJECT \
+ MIRROR SNAT DNAT MASQUERADE `iptables $table -nL | \
+ sed -ne "$chain" -e "s/OUTPUT|PREROUTING|POSTROUTING//"`' \
+ -- $cur ) )
+ elif [ "$table" = "-t mangle" ]; then
+ COMPREPLY=( $( compgen -W 'ACCEPT DROP LOG ULOG REJECT \
+ MARK TOS `iptables $table -nL | sed -ne "$chain" \
+ -e "s/INPUT|OUTPUT|FORWARD|PREROUTING|POSTROUTING//"`' -- \
+ $cur ) )
+ fi
+ ;;
+ *)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-i -o -s -d -p -f -m --append \
+ --delete --insert --replace --list --flush --zero --new \
+ --delete-chain --policy --rename-chain --proto --source \
+ --destination --in-interface --jump --match --numeric \
+ --out-interface --table --verbose --line-numbers --exact \
+ --fragment --modprobe= --set-counters --version' -- "$cur") )
+ fi
+ ;;
+ esac
+
+} &&
+complete -F _iptables iptables
+
+# tcpdump(8) completion
+#
+have tcpdump &&
+_tcpdump()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(r|w|F))
+ _filedir
+ return 0
+ ;;
+ -i)
+ _available_interfaces -a
+ return 0
+ ;;
+ esac
+
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a -d -e -f -l -n -N -O -p \
+ -q -R -S -t -u -v -x -C -F -i -m -r -s -T -w \
+ -E' -- $cur ) )
+ fi
+
+} &&
+complete -F _tcpdump tcpdump
+
+# autorpm(8) completion
+#
+have autorpm &&
+_autorpm()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( compgen -W '--notty --debug --help --version \
+ auto add fullinfo info help install list \
+ remove set' -- $cur ) )
+
+} &&
+complete -F _autorpm autorpm
+
+# This meta-cd function observes the CDPATH variable, so that cd additionally
+# completes on directories under those specified in CDPATH.
+#
+_cd()
+{
+ local IFS=$'\t\n' cur=`_get_cword` i j k
+
+ # try to allow variable completion
+ if [[ "$cur" == ?(\\)\$* ]]; then
+ COMPREPLY=( $( compgen -v -P '$' -- "${cur#?(\\)$}" ) )
+ return 0
+ fi
+
+ # Use standard dir completion if no CDPATH or parameter starts with /,
+ # ./ or ../
+ if [ -z "${CDPATH:-}" ] || [[ "$cur" == ?(.)?(.)/* ]]; then
+ _filedir -d
+ return 0
+ fi
+
+ local -r mark_dirs=$(_rl_enabled mark-directories && echo y)
+ local -r mark_symdirs=$(_rl_enabled mark-symlinked-directories && echo y)
+
+ # we have a CDPATH, so loop on its contents
+ for i in ${CDPATH//:/$'\t'}; do
+ # create an array of matched subdirs
+ k="${#COMPREPLY[@]}"
+ for j in $( compgen -d $i/$cur ); do
+ if [[ ( $mark_symdirs && -h $j || $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then
+ j="${j}/"
+ fi
+ COMPREPLY[k++]=${j#$i/}
+ done
+ done
+
+ _filedir -d
+
+ if [[ ${#COMPREPLY[@]} -eq 1 ]]; then
+ i=${COMPREPLY[0]}
+ if [ "$i" == "$cur" ] && [[ $i != "*/" ]]; then
+ COMPREPLY[0]="${i}/"
+ fi
+ fi
+
+ return 0
+}
+if shopt -q cdable_vars; then
+ complete -v -F _cd $nospace $filenames cd
+else
+ complete -F _cd $nospace $filenames cd
+fi
+
+_remove_comp_word()
+{
+ if [[ COMP_CWORD -eq 0 ]]; then
+ return
+ elif [[ ${#COMP_WORDS[@]} -ge 2 ]]; then
+ local old_cw0="${COMP_WORDS[0]}"
+ local new_cw0="${COMP_WORDS[1]}"
+ local old_length="${#COMP_LINE}"
+ COMP_LINE=${COMP_LINE#${old_cw0}}
+ local head=${COMP_LINE:0:${#new_cw0}}
+ local i=1
+ while [[ $head != $new_cw0 ]]; do
+ COMP_LINE=${COMP_LINE:1}
+ head=${COMP_LINE:0:${#new_cw0}}
+ if (( ++i > 10 )); then
+ break
+ fi
+ done
+ local new_length="${#COMP_LINE}"
+ COMP_POINT=$(( COMP_POINT + new_length - old_length))
+
+ COMP_CWORD=$(( COMP_CWORD - 1 ))
+ for (( i=0; i < ${#COMP_WORDS[@]} - 1; ++i )); do
+ COMP_WORDS[i]="${COMP_WORDS[i+1]}"
+ done
+ unset COMP_WORDS[${#COMP_WORDS[@]}-1]
+ else
+ return
+ fi
+}
+
+# A meta-command completion function for commands like sudo(8), which need to
+# first complete on a command, then complete according to that command's own
+# completion definition - currently not quite foolproof (e.g. mount and umount
+# don't work properly), but still quite useful.
+#
+_command()
+{
+ local cur func cline cspec noglob cmd done i \
+ _COMMAND_FUNC _COMMAND_FUNC_ARGS
+
+ _remove_comp_word
+ COMPREPLY=()
+ cur=`_get_cword`
+ # If the the first arguments following our meta-command-invoker are
+ # switches, get rid of them. Most definitely not foolproof.
+ done=
+ while [ -z $done ] ; do
+ cmd=${COMP_WORDS[0]}
+ if [[ "$cmd" == -* ]] && [ $COMP_CWORD -ge 1 ]; then
+ _remove_comp_word
+ elif [[ "$cmd" == -* ]] && [[ $COMP_CWORD -eq 0 ]]; then
+ return
+ else
+ done=1
+ fi
+ done
+
+ if [ $COMP_CWORD -eq 0 ]; then
+ COMPREPLY=( $( compgen -c -- $cur ) )
+ elif complete -p $cmd &>/dev/null; then
+ cspec=$( complete -p $cmd )
+ if [ "${cspec#* -F }" != "$cspec" ]; then
+ # COMP_CWORD and COMP_WORDS() are not read-only,
+ # so we can set them before handing off to regular
+ # completion routine
+
+ # get function name
+ func=${cspec#*-F }
+ func=${func%% *}
+
+ if [[ ${#COMP_WORDS[@]} -ge 2 ]]; then
+ $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}" "${COMP_WORDS[${#COMP_WORDS[@]}-2]}"
+ else
+ $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}"
+ fi
+
+ # remove any \: generated by a command that doesn't
+ # default to filenames or dirnames (e.g. sudo chown)
+ # FIXME: I'm pretty sure this does not work!
+ if [ "${cspec#*-o }" != "$cspec" ]; then
+ cspec=${cspec#*-o }
+ cspec=${cspec%% *}
+ if [[ "$cspec" != @(dir|file)names ]]; then
+ COMPREPLY=("${COMPREPLY[@]//\\\\:/:}")
+ fi
+ fi
+ elif [ -n "$cspec" ]; then
+ cspec=${cspec#complete};
+ cspec=${cspec%%$cmd};
+ COMPREPLY=( $( eval compgen "$cspec" -- "$cur" ) );
+ fi
+ fi
+
+ [ ${#COMPREPLY[@]} -eq 0 ] && _filedir
+}
+complete -F _command $filenames nohup exec nice eval strace time ltrace then \
+ else do vsound command xargs
+
+_root_command()
+{
+ PATH=/usr/gnu/bin:$PATH:/sbin:/usr/sbin _command $1 $2 $3
+}
+complete -F _root_command $filenames sudo fakeroot really
+
+# ant(1) completion
+#
+have ant && {
+_ant()
+{
+ local cur prev buildfile i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -buildfile|-f)
+ _filedir 'xml'
+ return 0
+ ;;
+ -logfile)
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ COMPREPLY=( $( compgen -W '-help -projecthelp -version -quiet \
+ -verbose -debug -emacs -logfile -logger \
+ -listener -buildfile -f -D -find' -- $cur ) )
+ else
+ # available targets completion
+ # find which buildfile to use
+ buildfile=build.xml
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == -buildfile ]]; then
+ buildfile=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+ [ ! -f $buildfile ] && return 0
+
+ # parse buildfile for targets
+ COMPREPLY=( $( awk -F'"' '/<target name="/ {print $2}' \
+ $buildfile | grep "^$cur" )
+ $( awk -F"'" "/<target name='/ "'{print $2}' \
+ $buildfile | grep "^$cur" )
+ $( awk -F'"' '/<target [^n]/ {if ($1 ~ /name=/) { print $2 } else if ($3 ~ /name=/) {print $4} else if ($5 ~ /name=/) {print $6}}' \
+ $buildfile | grep "^$cur" ) )
+ fi
+}
+have complete-ant-cmd.pl && \
+ complete -C complete-ant-cmd.pl -F _ant $filenames ant || \
+ complete -F _ant $filenames ant
+}
+
+have nslookup &&
+_nslookup()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]#-}
+
+ COMPREPLY=( $( compgen -P '-' -W 'all class= debug d2 domain= \
+ srchlist= defname search port= querytype= \
+ type= recurse retry root timeout vc \
+ ignoretc' -- $cur ) )
+} &&
+complete -F _nslookup nslookup
+
+# mysqladmin(1) completion
+#
+have mysqladmin &&
+_mysqladmin()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -u)
+ COMPREPLY=( $( compgen -u -- $cur ) )
+ return 0
+ ;;
+ *)
+ ;;
+ esac
+
+ COMPREPLY=( $( compgen -W '-# -f -? -C -h -p -P -i -r -E -s -S -t -u \
+ -v -V -w' -- $cur ) )
+
+ COMPREPLY=( "${COMPREPLY[@]}" \
+ $( compgen -W 'create drop extended-status flush-hosts \
+ flush-logs flush-status flush-tables \
+ flush-threads flush-privileges kill \
+ password ping processlist reload refresh \
+ shutdown status variables version' \
+ -- $cur ) )
+} &&
+complete -F _mysqladmin mysqladmin
+
+# gzip(1) completion
+#
+have gzip &&
+_gzip()
+{
+ local cur prev xspec IFS=$'\t\n'
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c -d -f \
+ -h -l -L -n -N -q -r -S -t -v -V \
+ -1 -2 -3 -4 -5 -6 -7 -8 -9 \
+ --stdout --decompress --force --help --list \
+ --license --no-name --name --quiet --recursive \
+ --suffix --test --verbose --version --fast \
+ --best' -- $cur ) )
+ return 0
+ fi
+
+ xspec="*.?(t)gz"
+ if [[ "$prev" == --* ]]; then
+ [[ "$prev" == --decompress || \
+ "$prev" == --list || \
+ "$prev" == --test ]] && xspec="!"$xspec
+ [[ "$prev" == --force ]] && xspec=
+ elif [[ "$prev" == -* ]]; then
+ [[ "$prev" == -*[dlt]* ]] && xspec="!"$xspec
+ [[ "$prev" == -*f* ]] && xspec=
+ elif [ "$prev" = '>' ]; then
+ xspec=
+ elif [ "$prev" = '<' ]; then
+ xspec=
+ fi
+
+ _expand || return 0
+
+ COMPREPLY=( $( compgen -f -X "$xspec" -- $cur ) \
+ $( compgen -d -- $cur ) )
+} &&
+complete -F _gzip $filenames gzip
+
+# bzip2(1) completion
+#
+have bzip2 &&
+_bzip2()
+{
+ local cur prev xspec IFS=$'\t\n'
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c -d -f -h -k -L -q -s \
+ -t -v -V -z -1 -2 -3 -4 -5 -6 -7 -8 -9 \
+ --help --decompress --compress --keep --force \
+ --test --stdout --quiet --verbose --license \
+ --version --small --fast --best' -- $cur ) )
+ return 0
+ fi
+
+ xspec="*.bz2"
+ if [[ "$prev" == --* ]]; then
+ [[ "$prev" == --decompress || \
+ "$prev" == --list || \
+ "$prev" == --test ]] && xspec="!"$xspec
+ [[ "$prev" == --compress ]] && xspec=
+ elif [[ "$prev" == -* ]]; then
+ [[ "$prev" == -*[dt]* ]] && xspec="!"$xspec
+ [[ "$prev" == -*z* ]] && xspec=
+ fi
+
+ _expand || return 0
+
+ COMPREPLY=( $( compgen -f -X "$xspec" -- $cur ) \
+ $( compgen -d -- $cur ) )
+} &&
+complete -F _bzip2 $filenames bzip2
+
+# openssl(1) completion
+#
+have openssl && {
+_openssl_sections()
+{
+ local config
+
+ config=/etc/ssl/openssl.cnf
+ [ ! -f $config ] && config=/usr/share/ssl/openssl.cnf
+ for (( i=2; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == -config ]]; then
+ config=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+ [ ! -f $config ] && return 0
+
+ COMPREPLY=( $( awk '/\[.*\]/ {print $2} ' $config | grep "^$cur" ) )
+}
+
+_openssl()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'asn1parse ca ciphers crl crl2pkcs7 \
+ dgst dh dhparam dsa dsaparam enc errstr gendh gendsa \
+ genrsa nseq passwd pkcs12 pkcs7 pkcs8 rand req rsa \
+ rsautl s_client s_server s_time sess_id smime speed \
+ spkac verify version x509 md2 md4 md5 mdc2 rmd160 sha \
+ sha1 base64 bf bf-cbc bf-cfb bf-ecb bf-ofb cast \
+ cast-cbc cast5-cbc cast5-cfb cast5-ecb cast5-ofb des \
+ des-cbc des-cfb des-ecb des-ede des-ede-cbc \
+ des-ede-cfb des-ede-ofb des-ede3 des-ede3-cbc \
+ des-ede3-cfb des-ede3-ofb des-ofb des3 desx rc2 \
+ rc2-40-cbc rc2-64-cbc rc2-cbc rc2-cfb rc2-ecb rc2-ofb \
+ rc4 rc4-40' -- $cur ) )
+ else
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ case ${COMP_WORDS[1]} in
+ asn1parse)
+ case $prev in
+ -inform)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out|oid))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -in -out -noout -offset \
+ -length -i -oid -strparse' -- $cur ) )
+ fi
+ ;;
+ ca)
+ case $prev in
+ -@(config|revoke|cert|in|out|spkac|ss_cert))
+ _filedir
+ return 0
+ ;;
+ -outdir)
+ _filedir -d
+ return 0
+ ;;
+ -@(name|crlexts|extensions))
+ _openssl_sections
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-verbose -config -name \
+ -gencrl -revoke -crldays -crlhours -crlexts \
+ -startdate -enddate -days -md -policy -keyfile \
+ -key -passin -cert -in -out -notext -outdir \
+ -infiles -spkac -ss_cert -preserveDN -batch \
+ -msie_hack -extensions' -- $cur ) )
+ fi
+ ;;
+ ciphers)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-v -ssl2 -ssl3 -tls1' -- $cur ) )
+ fi
+ ;;
+ crl)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out|CAfile))
+ _filedir
+ return 0
+ ;;
+ -CAPath)
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -text -in -out -noout \
+ -hash -issuer -lastupdate -nextupdate -CAfile -CApath' -- $cur ) )
+ fi
+ ;;
+ crl2pkcs7)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -out -print_certs' -- $cur ) )
+ fi
+ ;;
+ dgst)
+ case $prev in
+ -@(out|sign|verify|prvrify|signature))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-md5 -md4 -md2 -sha1 -sha -mdc2 -ripemd160 -dss1 \
+ -c -d -hex -binary -out -sign -verify -prverify -signature' -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ dsa)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -passin -out -passout -des -des3 -idea -text -noout \
+ -modulus -pubin -pubout' -- $cur ) )
+ fi
+ ;;
+ dsaparam)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -out -noout \
+ -text -C -rand -genkey' -- $cur ) )
+ fi
+ ;;
+ enc)
+ case $prev in
+ -@(in|out|kfile))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-ciphername -in -out -pass \
+ -e -d -a -A -k -kfile -S -K -iv -p -P -bufsize -debug' -- $cur ) )
+ fi
+ ;;
+ dhparam)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -out -dsaparam -noout \
+ -text -C -2 -5 -rand' -- $cur ) )
+ fi
+ ;;
+ gendsa)
+ case $prev in
+ -@(out|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-out -des -des3 -idea -rand' -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ genrsa)
+ case $prev in
+ -@(out|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-out -passout -des -des3 -idea -f4 -3 -rand' -- $cur ) )
+ fi
+ ;;
+ pkcs7)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -out -print_certs -text -noout' -- $cur ) )
+ fi
+ ;;
+ rand)
+ case $prev in
+ -@(out|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-out -rand -base64' -- $cur ) )
+ fi
+ ;;
+ req)
+ case "$prev" in
+ -@(in|out|key)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+
+ -@(in|out|rand|key|keyout|config))
+ _filedir
+ return 0
+ ;;
+ -extensions)
+ _openssl_sections
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in \
+ -passin -out -passout -text -noout -verify \
+ -modulus -new -rand -newkey -newkey -nodes \
+ -key -keyform -keyout -md5 -sha1 -md2 -mdc2 \
+ -config -x509 -days -asn1-kludge -newhdr \
+ -extensions -reqexts section' -- $cur ) )
+ fi
+ ;;
+ rsa)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER NET PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -passin -out -passout \
+ -sgckey -des -des3 -idea -text -noout -modulus -check -pubin \
+ -pubout -engine' -- $cur ) )
+ fi
+ ;;
+ rsautl)
+ case $prev in
+ -@(in|out|inkey))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-in -out -inkey -pubin -certin -sign -verify \
+ -encrypt -decrypt -pkcs -ssl -raw -hexdump -asn1parse' -- $cur ) )
+ fi
+ ;;
+ s_client)
+ case $prev in
+ -connect)
+ _known_hosts
+ return 0
+ ;;
+ -@(cert|key|CAfile|rand))
+ _filedir
+ return 0
+ ;;
+ -CApath)
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-connect -verify -cert -key -CApath -CAfile \
+ -reconnect -pause -showcerts -debug -msg -nbio_test -state -nbio \
+ -crlf -ign_eof -quiet -ssl2 -ssl3 -tls1 -no_ssl2 -no_ssl3 -no_tls1 \
+ -bugs -cipher -starttls -engine -rand' -- $cur ) )
+ fi
+ ;;
+ s_server)
+ case $prev in
+ -@(cert|key|dcert|dkey|dhparam|CAfile|rand))
+ _filedir
+ return 0
+ ;;
+ -CApath)
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-accept -context -verify -Verify -cert -key \
+ -dcert -dkey -dhparam -nbio -nbio_test -crlf -debug -msg -state -CApath \
+ -CAfile -nocert -cipher -quiet -no_tmp_rsa -ssl2 -ssl3 -tls1 -no_ssl2 \
+ -no_ssl3 -no_tls1 -no_dhe -bugs -hack -www -WWW -HTTP -engine -id_prefix \
+ -rand' -- $cur ) )
+ fi
+ ;;
+ s_time)
+ case $prev in
+ -connect)
+ _known_hosts
+ return 0
+ ;;
+ -@(cert|key|CAfile))
+ _filedir
+ return 0
+ ;;
+ -CApath)
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-connect -www -cert -key -CApath -CAfile -reuse \
+ -new -verify -nbio -time -ssl2 -ssl3 -bugs -cipher' -- $cur ) )
+ fi
+ ;;
+
+ sess_id)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out))
+ _filedir
+ return 0
+ ;;
+ esac
+
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -out -text -noout \
+ -context ID' -- $cur ) )
+ fi
+ ;;
+ smime)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'SMIME DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out|certfile|signer|recip|inkey|content|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-encrypt -decrypt -sign -verify -pk7out -des -des3 \
+ -rc2-40 -rc2-64 -rc2-128 -aes128 -aes192 -aes256 -in -certfile -signer \
+ -recip -inform -passin -inkey -out -outform -content -to -from -subject \
+ -text -rand' -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ speed)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-engine' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W 'md2 mdc2 md5 hmac sha1 rmd160 idea-cbc \
+ rc2-cbc rc5-cbc bf-cbc des-cbc des-ede3 rc4 rsa512 rsa1024 rsa2048 \
+ rsa4096 dsa512 dsa1024 dsa2048 idea rc2 des rsa blowfish' -- $cur ) )
+ fi
+ ;;
+ verify)
+ case $prev in
+ -@(CAfile|untrusted))
+ _filedir
+ return 0
+ ;;
+ -CApath)
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-CApath -CAfile -purpose -untrusted -help -issuer_checks \
+ -verbose -certificates' -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ x509)
+ case "$prev" in
+ -@(in|out|CA|CAkey|CAserial|extfile))
+ _filedir
+ return 0
+ ;;
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM NET' -- $cur ) )
+ return 0
+ ;;
+ -@(key|CA|CAkey)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -extensions)
+ _openssl_sections
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform \
+ -keyform -CAform -CAkeyform -in -out \
+ -serial -hash -subject -issuer -nameopt \
+ -email -startdate -enddate -purpose \
+ -dates -modulus -fingerprint -alias \
+ -noout -trustout -clrtrust -clrreject \
+ -addtrust -addreject -setalias -days \
+ -set_serial -signkey -x509toreq -req \
+ -CA -CAkey -CAcreateserial -CAserial \
+ -text -C -md2 -md5 -sha1 -mdc2 -clrext \
+ -extfile -extensions -engine' -- $cur ) )
+ fi
+ ;;
+ @(md5|md4|md2|sha1|sha|mdc2|ripemd160))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c -d' -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ esac
+ fi
+
+ return 0
+}
+complete -F _openssl $default openssl
+}
+
+# screen(1) completion
+#
+have screen &&
+_screen()
+{
+ local cur prev preprev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ [ "$COMP_CWORD" -ge 2 ] && preprev=${COMP_WORDS[COMP_CWORD-2]}
+
+ if [ "$preprev" = "-d" -o "$preprev" = "-D" -a "$prev" = "-r" -o \
+ "$prev" = "-R" ]; then
+ # list all
+ COMPREPLY=( $( command screen -ls | \
+ sed -ne 's|^['$'\t'']\+\('$cur'[0-9]\+\.[^'$'\t'']\+\).*$|\1|p' ) )
+ else
+ case "$prev" in
+ -[rR])
+ # list detached
+ COMPREPLY=( $( command screen -ls | \
+ sed -ne 's|^['$'\t'']\+\('$cur'[0-9]\+\.[^'$'\t'']\+\).*Detached.*$|\1|p' ) )
+ ;;
+ -[dDx])
+ # list attached
+ COMPREPLY=( $( command screen -ls | \
+ sed -ne 's|^['$'\t'']\+\('$cur'[0-9]\+\.[^'$'\t'']\+\).*Attached.*$|\1|p' ) )
+ ;;
+ -s)
+ # shells
+ COMPREPLY=( $( grep ^${cur:-[^#]} /etc/shells ) )
+ ;;
+ *)
+ ;;
+ esac
+ fi
+
+ return 0
+} &&
+complete -F _screen $default screen
+
+# lftp(1) bookmark completion
+#
+have lftp &&
+_lftp()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ] && [ -f ~/.lftp/bookmarks ]; then
+ COMPREPLY=( $( compgen -W '$( sed -ne "s/^\(.*\)'$'\t''.*$/\1/p" \
+ ~/.lftp/bookmarks )' -- $cur ) )
+ fi
+
+ return 0
+} &&
+complete -F _lftp $default lftp
+
+# ncftp(1) bookmark completion
+#
+have ncftp &&
+_ncftp()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ] && [ -f ~/.ncftp/bookmarks ]; then
+ COMPREPLY=( $( compgen -W '$( sed -ne "s/^\([^,]\{1,\}\),.*$/\1/p" \
+ ~/.ncftp/bookmarks )' -- $cur ) )
+ fi
+
+ return 0
+} &&
+complete -F _ncftp $default ncftp
+
+# gdb(1) completion
+#
+have gdb &&
+_gdb()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -c -- $cur ) )
+ elif [ $COMP_CWORD -eq 2 ]; then
+ prev=${prev##*/}
+ COMPREPLY=( $( compgen -fW "$( command ps axo comm,pid | \
+ awk '{if ($1 ~ /^'"$prev"'/) print $2}' ) )" \
+ -- "$cur" ) )
+ fi
+} &&
+complete -F _gdb $filenames gdb
+
+# Postgresql completion
+#
+have psql && {
+_pg_databases()
+{
+ return
+ COMPREPLY=( $( psql -l 2>/dev/null | \
+ sed -e '1,/^-/d' -e '/^(/,$d' | \
+ awk '{print $1}' | grep "^$cur" ) )
+}
+
+_pg_users()
+{
+ #COMPREPLY=( $( psql -qtc 'select usename from pg_user' template1 2>/dev/null | \
+ # grep "^ $cur" ) )
+ #[ ${#COMPREPLY[@]} -eq 0 ] &&
+ COMPREPLY=( $( compgen -u -- $cur ) )
+}
+
+# createdb(1) completion
+#
+_createdb()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(h|-host=))
+ _known_hosts
+ return 0
+ ;;
+ -@(U|-username=))
+ _pg_users
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-D -T -E -h -p -U -W -e -q \
+ --location= --template= --encoding= --host= --port= \
+ --username= --password --echo --quiet --help' -- $cur ))
+ else
+ _pg_databases
+ fi
+}
+complete -F _createdb $default createdb
+
+# dropdb(1) completion
+#
+_dropdb()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(h|-host=))
+ _known_hosts
+ return 0
+ ;;
+ -@(U|-username=))
+ _pg_users
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-h -p -U -W -e -q \
+ --host= --port= --username= --password \
+ --interactive --echo --quiet --help' -- $cur ) )
+ else
+ _pg_databases
+ fi
+}
+complete -F _dropdb $default dropdb
+
+# psql(1) completion
+#
+_psql()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -h|--host)
+ _known_hosts
+ return 0
+ ;;
+ -U|--username)
+ _pg_users
+ return 0
+ ;;
+ -d|--dbname)
+ _pg_databases
+ return 0
+ ;;
+ -@(o|f)|--output|--file)
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # return list of available options
+ COMPREPLY=( $( compgen -W '-a --echo-all -A --no-align \
+ -c --command -d --dbname -e --echo-queries \
+ -E --echo-hidden -f --file -F --filed-separator \
+ -h --host -H --html -l --list -n -o --output \
+ -p --port -P --pset -q -R --record-separator \
+ -s --single-step -S --single-line -t --tuples-only \
+ -T --table-attr -U --username -v --variable \
+ -V --version -W --password -x --expanded -X --nopsqlrc \
+ -? --help ' -- $cur ) )
+ else
+ # return list of available databases
+ _pg_databases
+ fi
+}
+complete -F _psql $default psql
+}
+
+_longopt()
+{
+ local cur opt
+
+ cur=`_get_cword`
+
+ if [[ "$cur" == --*=* ]]; then
+ opt=${cur%%=*}
+ # cut backslash that gets inserted before '=' sign
+ opt=${opt%\\*}
+ cur=${cur#*=}
+ _filedir
+ COMPREPLY=( $( compgen -P "$opt=" -W '${COMPREPLY[@]}' -- $cur))
+ return 0
+ fi
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( $1 --help 2>&1 | sed -e '/--/!d' \
+ -e 's/.*\(--[-A-Za-z0-9]\+=\?\).*/\1/' | \
+ command grep "^$cur" | sort -u ) )
+ elif [[ "$1" == @(mk|rm)dir ]]; then
+ _filedir -d
+ else
+ _filedir
+ fi
+}
+# makeinfo and texi2dvi are defined elsewhere.
+for i in a2ps autoconf automake bc gprof ld nm objcopy objdump readelf strip \
+ bison cpio diff patch enscript cp df dir du ln ls mkfifo mknod mv rm \
+ touch vdir awk gperf grep grub indent less m4 sed shar date \
+ tee who texindex cat csplit cut expand fmt fold head \
+ md5sum nl od paste pr ptx sha1sum sort split tac tail tr unexpand \
+ uniq wc ldd bash id irb mkdir rmdir; do
+ have $i && complete -F _longopt $filenames $i
+done
+
+# These commands use filenames, so '-o filenames' is not needed.
+for i in env netstat seq uname units wget; do
+ have $i && complete -F _longopt $default $i
+done
+unset i
+
+# gcc(1) completion
+#
+# The only unusual feature is that we don't parse "gcc --help -v" output
+# directly, because that would include the options of all the other backend
+# tools (linker, assembler, preprocessor, etc) without any indication that
+# you cannot feed such options to the gcc driver directly. (For example, the
+# linker takes a -z option, but you must type -Wl,-z for gcc.) Instead, we
+# ask the driver ("g++") for the name of the compiler ("cc1"), and parse the
+# --help output of the compiler.
+#
+have gcc &&
+_gcc()
+{
+ local cur cc backend
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _expand || return 0
+
+ case "$1" in
+ gcj)
+ backend=jc1
+ ;;
+ gpc)
+ backend=gpc1
+ ;;
+ *77)
+ backend=f771
+ ;;
+ *)
+ backend=cc1 # (near-)universal backend
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ cc=$( $1 -print-prog-name=$backend )
+ # sink stderr:
+ # for C/C++/ObjectiveC it's useless
+ # for FORTRAN/Java it's an error
+ COMPREPLY=( $( $cc --help 2>/dev/null | tr '\t' ' ' | \
+ sed -e '/^ *-/!d' -e 's/ *-\([^ ]*\).*/-\1/' | \
+ command grep "^$cur" | sort -u ) )
+ else
+ _filedir
+ fi
+} &&
+complete $filenames -F _gcc gcc g++ c++ g77 gcj gpc
+[ $UNAME = GNU -o $UNAME = Linux -o $UNAME = Cygwin ] && \
+[ -n "${have:-}" ] && complete $filenames -F _gcc cc
+
+# Linux cardctl(8) completion
+#
+have cardctl &&
+_cardctl()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'status config ident suspend \
+ resume reset eject insert scheme' \
+ -- $cur ) )
+ fi
+} &&
+complete -F _cardctl cardctl
+
+# This function is required by _dpkg() and _dpkg-reconfigure()
+#
+have dpkg && {
+_comp_dpkg_installed_packages()
+{
+ grep -A 1 "Package: $1" /var/lib/dpkg/status | \
+ grep -B 1 -E "ok installed|half-installed|unpacked| \
+ half-configured|config-files" | \
+ grep "Package: $1" | cut -d\ -f2
+}
+
+# Debian dpkg(8) completion
+#
+_dpkg()
+{
+ local cur prev i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ i=$COMP_CWORD
+
+ _expand || return 0
+
+ # find the last option flag
+ if [[ $cur != -* ]]; then
+ while [[ $prev != -* && $i != 1 ]]; do
+ i=$((i-1))
+ prev=${COMP_WORDS[i-1]}
+ done
+ fi
+
+ case "$prev" in
+ -@(c|i|A|I|f|e|x|X|-@(install|unpack|record-avail|contents|info|fsys-tarfile|field|control|extract)))
+ _filedir '?(u)deb'
+ return 0
+ ;;
+ -@(b|-build))
+ _filedir -d
+ return 0
+ ;;
+ -@(s|p|l|-@(status|print-avail|list)))
+ COMPREPLY=( $( apt-cache pkgnames $cur 2>/dev/null ) )
+ return 0
+ ;;
+ -@(S|-search))
+ _filedir
+ return 0
+ ;;
+ -@(r|L|P|-@(remove|purge|listfiles)))
+ COMPREPLY=( $( _comp_dpkg_installed_packages $cur ) )
+ return 0
+ ;;
+ *)
+
+ COMPREPLY=( $( compgen -W '-i --install --unpack -A --record-avail \
+ --configure -r --remove -P --purge --get-selections \
+ --set-selections --update-avail --merge-avail \
+ --clear-avail --command-fd --forget-old-unavail -s \
+ --status -p --print-avail -L --listfiles -l --list \
+ -S --search -C --audit --print-architecture \
+ --print-gnu-build-architecture \
+ --print-installation-architecture \
+ --compare-versions --help --version --force-help \
+ --force-all --force-auto-select --force-downgrade \
+ --force-configure-any --force-hold --force-bad-path \
+ --force-not-root --force-overwrite \
+ --force-overwrite-diverted --force-bad-verify \
+ --force-depends-version --force-depends \
+ --force-confnew --force-confold --force-confdef \
+ --force-confmiss --force-conflicts --force-architecture\
+ --force-overwrite-dir --force-remove-reinstreq \
+ --force-remove-essential -Dh \
+ --debug=help --licence --admindir= --root= --instdir= \
+ -O --selected-only -E --skip-same-version \
+ -G --refuse-downgrade -B --auto-deconfigure \
+ --no-debsig --no-act -D --debug= --status-fd \
+ -b --build -I --info -f --field -c --contents \
+ -x --extract -X --vextract --fsys-tarfile -e --control \
+ --ignore-depends= --abort-after' -- $cur ) )
+ ;;
+ esac
+
+
+}
+complete -F _dpkg $filenames dpkg dpkg-deb
+}
+
+# Debian GNU dpkg-reconfigure(8) completion
+#
+have dpkg-reconfigure &&
+_dpkg_reconfigure()
+{
+ local cur prev opt
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+
+ case "$prev" in
+ -@(f|-frontend))
+ opt=( $( echo /usr/share/perl5/Debconf/FrontEnd/* ) )
+ opt=( ${opt[@]##*/} )
+ opt=( ${opt[@]%.pm} )
+ COMPREPLY=( $( compgen -W '${opt[@]}' -- $cur ) )
+ return 0
+ ;;
+ -@(p|-priority))
+ COMPREPLY=( $( compgen -W 'low medium high critical' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-f --frontend -p --priority -a --all \
+ -u --unseen-only -h --help -s --showold \
+ --force --terse' -- $cur ) )
+ else
+ COMPREPLY=( $( _comp_dpkg_installed_packages $cur ) )
+ fi
+} &&
+complete -F _dpkg_reconfigure $default dpkg-reconfigure
+
+# Debian dpkg-source completion
+#
+have dpkg-source &&
+_dpkg_source()
+{
+ local cur prev options work i action packopts unpackopts
+
+ packopts="-c -l -F -V -T -D -U -W -E -sa -i -I -sk -sp -su -sr -ss -sn -sA -sK -sP -sU -sR"
+ unpackopts="-sp -sn -su"
+ options=`echo "-x -b $packopts $unpackopts" | xargs echo | sort -u | xargs echo`
+
+ COMPREPLY=()
+ if [ "$1" != "dpkg-source" ]; then
+ exit 1
+ fi
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ action="options"
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[$i]} == "-x" ]]; then
+ action=unpack
+ elif [[ ${COMP_WORDS[$i]} == "-b" ]]; then
+ action=pack
+ elif [[ ${COMP_WORDS[$i]} == "-h" ]]; then
+ action=help
+ fi
+ done
+ # if currently seeing a complete option, return just itself.
+ for i in $options; do
+ if [ "$cur" = "$i" ]; then
+ COMPREPLY=( "$cur" )
+ return 0
+ fi
+ done
+ case "$action" in
+ "unpack")
+ if [ "$cur" = "-" -o "$cur" = "-s" ]; then
+ COMPREPLY=( $unpackots )
+ return 0
+ fi
+ case "$prev" in
+ "-x")
+ COMPREPLY=( $( compgen -d -- "$cur" ) \
+ $( compgen -f -X '!*.dsc' -- "$cur" ) )
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $unpackopts $(compgen -d -f -- "$cur" ) )
+ return 0
+ ;;
+ esac
+ return 0
+ ;;
+ "pack")
+ if [ "$cur" = "-" ]; then
+ COMPREPLY=( $packopts )
+ return 0
+ fi
+ if [ "$cur" = "-s" ]; then
+ COMPREPLY=( "-sa" "-sk" "-sp" "-su" "-sr" "-ss" "-sn" \
+ "-sA" "-sK" "-sP" "-sU" "-sR" )
+ return 0
+ fi
+ case "$prev" in
+ "-b")
+ COMPREPLY=( $( compgen -d -- "$cur" ) )
+ return 0
+ ;;
+ "-c"|"-l"|"-T"|"-i"|"-I")
+ # -c: get controlfile
+ # -l: get per-version info from this file
+ # -T: read variables here, not debian/substvars
+ # -i: <regexp> filter out files to ignore diffs of.
+ # -I: filter out files when building tarballs.
+ # return directory names and file names
+ COMPREPLY=( $( compgen -d -f ) )
+ return 0
+ ;;
+ "-F")
+ # -F: force change log format
+ COMPREPLY=( $( ( cd /usr/lib/dpkg/parsechangelog; compgen -f "$cur" ) ) )
+ return 0
+ ;;
+ "-V"|"-D")
+ # -V: set a substitution variable
+ # we don't know anything about possible variables or values
+ # so we don't try to suggest any completion.
+ COMPREPLY=()
+ return 0
+ ;;
+ "-D")
+ # -D: override or add a .dsc field and value
+ # if $cur doesn't contain a = yet, suggest variable names
+ if echo -- "$cur" | grep -q "="; then
+ # $cur contains a "="
+ COMPREPLY=()
+ return 0
+ else
+ COMPREPLY=( Format Source Version Binary Maintainer Uploader Architecture Standards-Version Build-Depends Files )
+ return 0
+ fi
+ ;;
+ "-U")
+ # -U: remove a field
+ # Suggest possible fieldnames
+ COMPREPLY=( Format Source Version Binary Maintainer Uploader Architecture Standards-Version Build-Depends Files )
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $packopts )
+ return 0
+ ;;
+ esac
+ return 0
+ ;;
+ *)
+ # if seeing a partial option, return possible completions.
+ if [ "$cur" = "-s" ]; then
+ COMPREPLY=( "-sa" "-sk" "-sp" "-su" "-sr" "-ss" "-sn" \
+ "-sA" "-sK" "-sP" "-sU" "-sR" )
+ return 0
+ fi
+ # else return all possible options.
+ COMPREPLY=( $options )
+ return 0
+ ;;
+ esac
+} &&
+complete -F _dpkg_source dpkg-source
+
+# Debian Linux dselect(8) completion.
+#
+have dselect &&
+_dselect()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --admindir)
+ _filedir -d
+ return 0
+ ;;
+
+ -@(D|debug))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--admindir --help --version --licence \
+ --license --expert --debug' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W 'access update select install config \
+ remove quit' -- $cur ) )
+ fi
+
+
+ return 0
+} &&
+complete -F _dselect $filenames dselect
+
+# Java completion
+#
+
+# available path elements completion
+have java && {
+_java_path()
+{
+ cur=${cur##*:}
+ _filedir '@(jar|zip)'
+}
+
+# exact classpath determination
+_java_find_classpath()
+{
+ local i
+
+ # search first in current options
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == -@(cp|classpath) ]]; then
+ classpath=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+
+ # default to environment
+ [ -z "$classpath" ] && classpath=$CLASSPATH
+
+ # default to current directory
+ [ -z "$classpath" ] && classpath=.
+}
+
+# exact sourcepath determination
+_java_find_sourcepath()
+{
+ local i
+
+ # search first in current options
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == -sourcepath ]]; then
+ sourcepath=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+
+ # default to classpath
+ [ -z "$sourcepath" ] && _java_find_classpath
+ sourcepath=$classpath
+}
+
+# available classes completion
+_java_classes()
+{
+ local classpath i
+
+ # find which classpath to use
+ _java_find_classpath
+
+ # convert package syntax to path syntax
+ cur=${cur//.//}
+ # parse each classpath element for classes
+ for i in ${classpath//:/ }; do
+ if [ -r $i ] && [[ "$i" == *.@(jar|zip) ]]; then
+ if type zipinfo &> /dev/null; then
+ COMPREPLY=( "${COMPREPLY[@]}" $( zipinfo -1 \
+ "$i" | grep "^$cur" | grep '\.class$' | \
+ grep -v "\\$" ) )
+ else
+ COMPREPLY=( "${COMPREPLY[@]}" $( jar tf "$i" \
+ "$cur" | grep "\.class$" | grep -v "\\$" ) )
+ fi
+
+ elif [ -d $i ]; then
+ i=${i%/}
+ COMPREPLY=( "${COMPREPLY[@]}" $( find "$i" -type f \
+ -path "$i/$cur*.class" 2>/dev/null | \
+ grep -v "\\$" | sed -e "s|^$i/||" ) )
+ fi
+ done
+
+ # remove class extension
+ COMPREPLY=( ${COMPREPLY[@]%.class} )
+ # convert path syntax to package syntax
+ COMPREPLY=( ${COMPREPLY[@]//\//.} )
+}
+
+# available packages completion
+_java_packages()
+{
+ local sourcepath i
+
+ # find wich sourcepath to use
+ _java_find_sourcepath
+
+ # convert package syntax to path syntax
+ cur=${cur//.//}
+ # parse each sourcepath element for packages
+ for i in ${sourcepath//:/ }; do
+ if [ -d $i ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" $( command ls -F -d \
+ $i/$cur* 2>/dev/null | sed -e 's|^'$i'/||' ) )
+ fi
+ done
+ # keep only packages
+ COMPREPLY=( $( echo "${COMPREPLY[@]}" | tr " " "\n" | grep "/$" ) )
+ # remove packages extension
+ COMPREPLY=( ${COMPREPLY[@]%/} )
+ # convert path syntax to package syntax
+ cur=${COMPREPLY[@]//\//.}
+}
+
+# java completion
+#
+_java()
+{
+ local cur prev i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ for ((i=1; i < $COMP_CWORD; i++)); do
+ case ${COMP_WORDS[$i]} in
+ -cp|-classpath)
+ ((i++)) # skip the classpath string.
+ ;;
+ -*)
+ # this is an option, not a class/jarfile name.
+ ;;
+ *)
+ # once we've seen a class, just do filename completion
+ _filedir
+ return 0
+ ;;
+ esac
+ done
+
+ case $prev in
+ -@(cp|classpath))
+ _java_path
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ COMPREPLY=( $( compgen -W '-client -hotspot -server -classic \
+ -cp -classpath -D -verbose -verbose:class \
+ -verbose:gc -version:jni -version \
+ -showversion -? -help -X -jar \
+ -ea -enableassertions -da -disableassertions \
+ -esa -enablesystemassertions \
+ -dsa -disablesystemassertions ' -- $cur ) )
+ else
+ if [[ "$prev" == -jar ]]; then
+ # jar file completion
+ _filedir jar
+ else
+ # classes completion
+ _java_classes
+ fi
+ fi
+}
+complete -F _java $filenames java
+}
+
+# javadoc completion
+#
+have javadoc &&
+_javadoc()
+{
+ COMPREPLY=()
+ local cur prev
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -@(overview|helpfile|stylesheetfile))
+ _filedir
+ return 0
+ ;;
+ -d)
+ _filedir -d
+ return 0
+ ;;
+ -@(classpath|bootclasspath|docletpath|sourcepath|extdirs))
+ _java_path
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ COMPREPLY=( $( compgen -W '-overview -public -protected \
+ -package -private -help -doclet -docletpath \
+ -sourcepath -classpath -exclude -subpackages \
+ -breakiterator -bootclasspath -source -extdirs \
+ -verbose -locale -encoding -J -d -use -version \
+ -author -docfilessubdirs -splitindex \
+ -windowtitle -doctitle -header -footer -bottom \
+ -link -linkoffline -excludedocfilessubdir \
+ -group -nocomment -nodeprecated -noqualifier \
+ -nosince -nodeprecatedlist -notree -noindex \
+ -nohelp -nonavbar -quiet -serialwarn -tag \
+ -taglet -tagletpath -charset -helpfile \
+ -linksource -stylesheetfile -docencoding' -- \
+ $cur ) )
+ else
+ # source files completion
+ _filedir java
+ # packages completion
+ _java_packages
+ fi
+} &&
+complete -F _javadoc $filenames javadoc
+
+# javac completion
+#
+have javac &&
+_javac()
+{
+ COMPREPLY=()
+ local cur prev
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -d)
+ _filedir -d
+ return 0
+ ;;
+ -@(classpath|bootclasspath|sourcepath|extdirs))
+ _java_path
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ COMPREPLY=( $( compgen -W '-g -g:none -g:lines -g:vars\
+ -g:source -O -nowarn -verbose -deprecation -classpath\
+ -sourcepath -bootclasspath -extdirs -d -encoding -source\
+ -target -help' -- $cur ) )
+ else
+ # source files completion
+ _filedir java
+ fi
+} &&
+complete -F _javac $filenames javac
+
+# PINE address-book completion
+#
+have pine &&
+_pineaddr()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( compgen -W '$( awk "{print \$1}" ~/.addressbook 2>/dev/null)' \
+ -- $cur ) )
+} &&
+complete -F _pineaddr $default pine
+
+# mutt completion
+#
+# Mutt doesn't have an "addressbook" like Pine, but it has aliases and
+# a "query" function to retrieve addresses, so that's what we use here.
+have mutt || have muttng && {
+_muttaddr()
+{
+ _muttaliases
+ _muttquery
+ return 0
+}
+
+_muttconffiles()
+{
+ local file sofar
+ local -a newconffiles
+
+ sofar=" $1 "
+ shift
+ while [[ "$1" ]]; do
+ newconffiles=( $(sed -rn 's|^source[[:space:]]+([^[:space:]]+).*$|\1|p' $(eval echo $1) ) )
+ for file in "${newconffiles[@]}"; do
+ [[ ! "$file" ]] || [[ "${sofar/ ${file} / }" != "$sofar" ]] &&
+ continue
+ sofar="$sofar $file"
+ sofar=" $(eval _muttconffiles \"$sofar\" $file) "
+ done
+ shift
+ done
+ echo $sofar
+}
+
+_muttaliases()
+{
+ local cur muttrc
+ local -a conffiles aliases
+ cur=`_get_cword`
+
+ [ -f ~/.${muttcmd}/${muttcmd}rc ] && muttrc="~/.${muttcmd}/${muttcmd}rc"
+ [ -f ~/.${muttcmd}rc ] && muttrc="~/.${muttcmd}rc"
+ [ -z "$muttrc" ] && return 0
+
+ conffiles=( $(eval _muttconffiles $muttrc $muttrc) )
+ aliases=( $( sed -rn 's|^alias[[:space:]]+([^[:space:]]+).*$|\1|p' \
+ $(eval echo "${conffiles[@]}") ) )
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "${aliases[*]}" -- $cur ) )
+
+ return 0
+}
+
+_muttquery()
+{
+ local cur querycmd
+ local -a queryresults
+ cur=`_get_cword`
+
+ querycmd="$( $muttcmd -Q query_command | sed -r 's|^query_command=\"(.*)\"$|\1|; s|%s|'$cur'|' )"
+ if [ -z "$cur" -o -z "$querycmd" ]; then
+ queryresults=()
+ else
+ queryresults=( $( $querycmd | \
+ sed -nr '2,$s|^([^[:space:]]+).*|\1|p' ) )
+ fi
+
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "${queryresults[*]}" \
+ -- $cur ) )
+
+ return 0
+}
+
+_muttfiledir()
+{
+ local cur folder spoolfile
+ cur=`_get_cword`
+
+ # This is currently not working so well. Perhaps this function should
+ # just call _filedir() for the moment.
+ if [[ $cur == [=+]* ]]; then
+ folder="$( $muttcmd -Q folder | sed -r 's|^folder=\"(.*)\"$|\1|' )"
+ : folder:=~/Mail
+
+ # Match any file in $folder beginning with $cur
+ # (minus the leading '=' sign).
+ COMPREPLY=( $( compgen -f -- "$folder/${cur:1}" ) )
+ COMPREPLY=( ${COMPREPLY[@]#$folder/} )
+ return 0
+ elif [ "$cur" == !* ]; then
+ spoolfile="$( $muttcmd -Q spoolfile | sed -r 's|^spoolfile=\"(.*)\"$|\1|' )"
+ [ ! -z "$spoolfile" ] && eval cur="${cur/^!/$spoolfile}";
+ fi
+ _filedir
+
+ return 0
+}
+
+_mutt()
+{
+ local cur prev
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMPREPLY=()
+
+ [ ${COMP_WORDS[0]} == muttng ] && muttcmd="muttng" || muttcmd="mutt"
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W '-A -a -b -c -e -f -F -H -i -m -n \
+ -p -Q -R -s -v -x -y -z -Z -h' \
+ -- $cur ) )
+ return 0
+ ;;
+ *)
+ case "$prev" in
+ -@(a|f|F|H|i))
+ _muttfiledir
+ return 0
+ ;;
+ -A)
+ _muttaliases
+ return 0
+ ;;
+ -@(e|m|Q|s|h|p|R|v|y|z|Z))
+ return 0
+ ;;
+ *)
+ _muttaddr
+ return 0
+ ;;
+ esac
+ ;;
+ esac
+
+}
+complete -F _mutt $default $filenames mutt muttng
+}
+
+_configure_func()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ # if $COMP_CONFIGURE_HINTS is not null, then completions of the form
+ # --option=SETTING will include 'SETTING' as a contextual hint
+ [[ "$cur" != -* ]] && return 0
+
+ if [ -n "$COMP_CONFIGURE_HINTS" ]; then
+ COMPREPLY=( $( $1 --help | awk '/^ --[A-Za-z]/ { print $1; if ($2 ~ /--[A-Za-z]/) print $2 }' | sed -e 's/[[,].*//g' | grep ^$cur ) )
+
+ else
+ COMPREPLY=( $( $1 --help | awk '/^ --[A-Za-z]/ { print $1; if ($2 ~ /--[A-Za-z]/) print $2 }' | sed -e 's/[[,=].*//g' | grep ^$cur ) )
+ fi
+}
+complete -F _configure_func $default configure
+
+# Debian reportbug(1) completion
+#
+have reportbug &&
+_reportbug()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -f|--filename|-i|--include|--mta|-o|--output)
+ _filedir
+ return 0
+ ;;
+ -B|--bts)
+ COMPREPLY=( $( compgen -W "debian guug kde mandrake help" -- \
+ $cur ))
+ return 0
+ ;;
+ -e|--editor|--mua)
+ COMP_WORDS=(COMP_WORDS[0] $cur)
+ COMP_CWORD=1
+ _command
+ return 0
+ ;;
+ --mode)
+ COMPREPLY=( $( compgen -W "novice standard expert" -- $cur ) )
+ return 0
+ ;;
+ -S|--severity)
+ COMPREPLY=( $( compgen -W "grave serious important normal \
+ minor wishlist" -- $cur ) )
+ return 0
+ ;;
+ -u|--ui|--interface)
+ COMPREPLY=( $( compgen -W "newt text gnome" -- $cur ) )
+ return 0
+ ;;
+ -t|--type)
+ COMPREPLY=( $( compgen -W "gnats debbugs" -- $cur ) )
+ return 0
+ ;;
+ -T|--tags)
+ COMPREPLY=( $( compgen -W "none patch security upstream sid \
+ woody potato sarge fixed" -- $cur ))
+ return 0
+ ;;
+ *)
+ ;;
+ esac
+
+ COMPREPLY=($( compgen -W '-h --help -v --version -a --af -b \
+ --no-query-bts --query-bts -B --bts -c --configure \
+ --no-config-files --check-available -d --debug \
+ --no-check-available -e --editor --email -f \
+ --filename -g --gnupg -H --header -i --include -j \
+ --justification -l --ldap --no-ldap -L --list-cc -m \
+ --maintonly --mode --mua --mta --mutt -n --mh --nmh \
+ -o --output -p --print -P --pgp --proxy --http_proxy\
+ -q --quiet -Q --query-only --realname --report-quiet \
+ --reply-to --replyto -s --subject -S --severity \
+ --smtphost -t --type -T --tags --template -V -x \
+ --no-cc --package-version -z --no-compress \
+ --ui --interface -u \
+ wnpp boot-floppies kernel-image' -- $cur ) \
+ $( apt-cache pkgnames -- $cur 2> /dev/null ) )
+ _filedir
+ return 0
+} &&
+complete -F _reportbug $filenames reportbug
+
+# Debian querybts(1) completion
+#
+have querybts &&
+_querybts()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -B|--bts)
+ COMPREPLY=( $( compgen -W "debian guug kde mandrake help" -- \
+ $cur ))
+ return 0
+ ;;
+ -u|--ui|--interface)
+ COMPREPLY=($( compgen -W "newt text gnome" -- $cur ))
+ return 0
+ ;;
+ *)
+ ;;
+ esac
+
+ COMPREPLY=($( compgen -W '-h --help -v --version -A --archive \
+ -B --bts -l --ldap --no-ldap --proxy= --http_proxy= \
+ -s --source -w --web -u --ui --interface \
+ wnpp boot-floppies' -- $cur ) \
+ $( apt-cache pkgnames -- $cur 2> /dev/null ) )
+} &&
+complete -F _querybts $filenames querybts
+
+# update-alternatives completion
+#
+have update-alternatives && {
+installed_alternatives()
+{
+ local admindir
+ # find the admin dir
+ for i in alternatives dpkg/alternatives rpm/alternatives; do
+ [ -d /var/lib/$i ] && admindir=/var/lib/$i && break
+ done
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == --admindir ]]; then
+ admindir=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+ COMPREPLY=( $( command ls $admindir | grep "^$cur" ) )
+}
+
+_update_alternatives()
+{
+ local cur prev mode args i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --@(altdir|admindir))
+ _filedir -d
+ return 0
+ ;;
+ --@(help|version))
+ return 0
+ ;;
+ esac
+
+ # find wich mode to use and how many real args used so far
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == --@(install|remove|auto|display|config|remove-all) ]]; then
+ mode=${COMP_WORDS[i]}
+ args=$(($COMP_CWORD - i))
+ break
+ fi
+ done
+
+ case $mode in
+ --install)
+ case $args in
+ 1)
+ _filedir
+ ;;
+ 2)
+ installed_alternatives
+ ;;
+ 3)
+ _filedir
+ ;;
+ esac
+ ;;
+ --remove)
+ case $args in
+ 1)
+ installed_alternatives
+ ;;
+ 2)
+ _filedir
+ ;;
+ esac
+ ;;
+ --auto)
+ installed_alternatives
+ ;;
+ --remove-all)
+ installed_alternatives
+ ;;
+ --display)
+ installed_alternatives
+ ;;
+ --config)
+ installed_alternatives
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W '--verbose --quiet --help --version \
+ --altdir --admindir' -- $cur ) \
+ $( compgen -W '--install --remove --auto --display \
+ --config' -- $cur ) )
+ esac
+}
+complete -F _update_alternatives update-alternatives
+}
+
+# Python completion
+#
+have python &&
+_python()
+{
+ local prev cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]##*/}
+
+ case "$prev" in
+ -Q)
+ COMPREPLY=( $( compgen -W "old new warn warnall" -- $cur ) )
+ return 0
+ ;;
+ -W)
+ COMPREPLY=( $( compgen -W "ignore default all module once error" -- $cur ) )
+ return 0
+ ;;
+ -c)
+ _filedir '@(py|pyc|pyo)'
+ return 0
+ ;;
+ !(python|-?))
+ [[ ${COMP_WORDS[COMP_CWORD-2]} != -@(Q|W) ]] && _filedir
+ ;;
+ esac
+
+
+ # if '-c' is already given, complete all kind of files.
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == -c ]]; then
+ _filedir
+ fi
+ done
+
+
+ if [[ "$cur" != -* ]]; then
+ _filedir '@(py|pyc|pyo)'
+ else
+ COMPREPLY=( $( compgen -W "- -d -E -h -i -O -Q -S -t -u \
+ -U -v -V -W -x -c" -- $cur ) )
+ fi
+
+
+
+ return 0
+} &&
+complete -F _python $filenames python
+
+# Perl completion
+#
+have perl &&
+{
+_perlmodules()
+{
+ COMPREPLY=( $( compgen -P "$prefix" -W "$( perl -e 'sub mods { my ($base,$dir)=@_; return if $base !~ /^\Q$ENV{cur}/; chdir($dir) or return; for (glob(q[*.pm])) {s/\.pm$//; print qq[$base$_\n]}; mods(/^(?:[.\d]+|$Config{archname}-$Config{osname}|auto)$/ ? undef : qq[${base}${_}\\\\:\\\\:],qq[$dir/$_]) for grep {-d} glob(q[*]); } mods(undef,$_) for @INC;' )" -- $cur ) )
+}
+
+_perl()
+{
+ local cur prev prefix temp
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ prefix=""
+
+ # completing an option (may or may not be separated by a space)
+ if [[ "$cur" == -?* ]]; then
+ temp=$cur
+ prev=${temp:0:2}
+ cur=${temp:2}
+ prefix=$prev
+ fi
+
+ # only handle module completion for now
+ case "$prev" in
+ -I|-x)
+ COMPREPLY=( $( compgen -d -P "$prev" -- "$cur" ) )
+ return 0
+ ;;
+ -m|-M)
+ _perlmodules
+ return 0
+ ;;
+ esac
+
+ # handle case where first parameter is not a dash option
+ if [[ "${COMP_WORDS[COMP_CWORD]}" != -* ]]; then
+ _filedir
+ return 0
+ fi
+
+ # complete using basic options
+ COMPREPLY=( $( compgen -W '-C -s -T -u -U -W -X -h -v -V -c -w -d -D -p \
+ -n -a -F -l -0 -I -m -M -P -S -x -i -e ' -- $cur ) )
+ return 0
+}
+complete -F _perl $filenames perl
+
+_perldoc()
+{
+ local cur prev prefix temp
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ prefix=""
+
+ # completing an option (may or may not be separated by a space)
+ if [[ "$cur" == -?* ]]; then
+ temp=$cur
+ prev=${temp:0:2}
+ cur=${temp:2}
+ prefix=$prev
+ fi
+
+ # complete builtin perl functions
+ case $prev in
+ -f)
+ COMPREPLY=( $( compgen -W 'chomp chop chr crypt hex index lc \
+ lcfirst length oct ord pack q qq reverse rindex sprintf \
+ substr tr uc ucfirst y m pos quotemeta s split study qr abs \
+ atan2 cos exp hex int log oct rand sin sqrt srand pop push \
+ shift splice unshift grep join map qw reverse sort unpack \
+ delete each exists keys values binmode close closedir \
+ dbmclose dbmopen die eof fileno flock format getc print \
+ printf read readdir rewinddir seek seekdir select syscall \
+ sysread sysseek syswrite tell telldir truncate warn write \
+ pack read syscall sysread syswrite unpack vec -X chdir chmod \
+ chown chroot fcntl glob ioctl link lstat mkdir open opendir \
+ readlink rename rmdir stat symlink umask unlink utime caller \
+ continue do dump eval exit goto last next redo return \
+ sub wantarray caller import local my our package use defined \
+ formline reset scalar undef \
+ alarm exec fork getpgrp getppid getpriority kill pipe qx \
+ setpgrp setpriority sleep system times wait waitpid \
+ import no package require use bless dbmclose dbmopen package \
+ ref tie tied untie use accept bind connect getpeername \
+ getsockname getsockopt listen recv send setsockopt shutdown \
+ socket socketpair msgctl msgget msgrcv msgsnd semctl semget \
+ semop shmctl shmget shmread shmwrite endgrent endhostent \
+ endnetent endpwent getgrent getgrgid getgrnam getlogin \
+ getpwent getpwnam getpwuid setgrent setpwent endprotoent \
+ endservent gethostbyaddr gethostbyname gethostent \
+ getnetbyaddr getnetbyname getnetent getprotobyname \
+ getprotobynumber getprotoent getservbyname getservbyport \
+ getservent sethostent setnetent setprotoent setservent \
+ gmtime localtime time times' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ case $cur in
+ -*)
+ COMPREPLY=( $( compgen -W '-h -v -t -u -m -l -F -X -f -q' -- $cur ))
+ return 0
+ ;;
+ */*)
+ return 0
+ ;;
+ *)
+ _perlmodules
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W '$( PAGER=cat man perl 2>/dev/null | sed -ne "/perl.*Perl overview/,/perlwin32/s/^[^a-z0-9]*\([a-z0-9]*\).*$/\1/p")' -- $cur ) )
+
+ return 0
+ ;;
+ esac
+}
+complete -F _perldoc $default perldoc
+}
+
+# rcs(1) completion
+#
+have rcs &&
+_rcs()
+{
+ local cur prev file dir i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ file=${cur##*/}
+ dir=${cur%/*}
+
+ # deal with relative directory
+ [ "$file" = "$dir" ] && dir=.
+
+ COMPREPLY=( $( compgen -f "$dir/RCS/$file" ) )
+
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ file=${COMPREPLY[$i]##*/}
+ dir=${COMPREPLY[$i]%RCS/*}
+ COMPREPLY[$i]=$dir$file
+ done
+
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -G "$dir/$file*,v" ) )
+
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ COMPREPLY[$i]=${COMPREPLY[$i]%,v}
+ done
+
+ # default to files if nothing returned and we're checking in.
+ # otherwise, default to directories
+ [ ${#COMPREPLY[@]} -eq 0 -a $1 = ci ] && _filedir || _filedir -d
+} &&
+complete -F _rcs $filenames ci co rlog rcs rcsdiff
+
+# lilo(8) completion
+#
+have lilo && {
+_lilo_labels()
+{
+ COMPREPLY=( $( awk -F'=' '/label/ {print $2}' \
+ /etc/lilo.conf | sed -e 's/"//g' | grep "^$cur" ) )
+}
+
+_lilo()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -@(C|i|m|s|S))
+ _filedir
+ return 0
+ ;;
+ -r)
+ _filedir -d
+ return 0
+ ;;
+ -@(I|D|R))
+ # label completion
+ _lilo_labels
+ return 0
+ ;;
+ -@(A|b|M|u|U))
+ # device completion
+ cur=${cur:=/dev/}
+ _filedir
+ return 0
+ ;;
+ -T)
+ # topic completion
+ COMPREPLY=( $( compgen -W 'help ChRul EBDA geom geom= \
+ table= video' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ COMPREPLY=( $( compgen -W '-A -b -c -C -d -f -g -i -I -l -L -m \
+ -M -p -P -q -r -R -s -S -t -T -u -U -v -V -w -x -z' -- \
+ $cur ) )
+ fi
+}
+complete -F _lilo lilo
+}
+
+# links completion
+#
+have links &&
+_links()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ case "$cur" in
+ --*)
+ COMPREPLY=( $( compgen -W '--help' -- $cur ) )
+ ;;
+ -*)
+ COMPREPLY=( $( compgen -W '-async-dns -max-connections \
+ -max-connections-to-host -retries \
+ -receive-timeout -unrestartable-receive-timeout\
+ -format-cache-size -memory-cache-size \
+ -http-proxy -ftp-proxy -download-dir \
+ -assume-codepage -anonymous -dump -no-connect \
+ -source -version -help' -- $cur ) )
+ ;;
+ *)
+ if [ -r ~/.links/links.his ]; then
+ COMPREPLY=( $( compgen -W '$( < ~/.links/links.his )' \
+ -- $cur ) )
+ fi
+ _filedir '@(htm|html)'
+ return 0
+ ;;
+ esac
+
+ return 0
+} &&
+complete -F _links $filenames links
+
+[ $UNAME = FreeBSD ] && {
+# FreeBSD package management tool completion
+#
+_pkg_delete()
+{
+ local cur pkgdir prev
+
+ pkgdir=${PKG_DBDIR:-/var/db/pkg}/
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ [ "$prev" = "-o" -o "$prev" = "-p" -o "$prev" = "-W" ] && return 0
+
+ COMPREPLY=( $( compgen -d $pkgdir$cur ) )
+ COMPREPLY=( ${COMPREPLY[@]#$pkgdir} )
+
+ return 0
+}
+complete -F _pkg_delete $dirnames pkg_delete pkg_info
+have pkg_deinstall && complete -F _pkg_delete $dirnames pkg_deinstall
+
+# FreeBSD kernel module commands
+#
+_kldload()
+{
+ local cur moddir
+
+ moddir=/modules/
+ [ -d $moddir ] || moddir=/boot/kernel/
+ cur=`_get_cword`
+
+ COMPREPLY=( $( compgen -f $moddir$cur ) )
+ COMPREPLY=( ${COMPREPLY[@]#$moddir} )
+ COMPREPLY=( ${COMPREPLY[@]%.ko} )
+
+ return 0
+}
+complete -F _kldload $filenames kldload
+
+_kldunload()
+{
+ local cur
+ cur=`_get_cword`
+ COMPREPLY=( $(kldstat | sed -ne "s/^.*[ \t]\+\($cur[a-z_]\+\).ko$/\1/p") )
+}
+complete -F _kldunload $filenames kldunload
+}
+
+# FreeBSD portupgrade completion
+#
+have portupgrade &&
+_portupgrade()
+{
+ local cur pkgdir prev
+
+ pkgdir=${PKG_DBDIR:-/var/db/pkg}/
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ [ "$prev" = "-l" -o "$prev" = "-L" -o "$prev" = "-o" ] && return 0
+
+ COMPREPLY=( $( compgen -d $pkgdir$cur ) )
+ COMPREPLY=( ${COMPREPLY[@]#$pkgdir} )
+ COMPREPLY=( ${COMPREPLY[@]%-*} )
+
+ return 0
+} &&
+complete -F _portupgrade $dirnames portupgrade
+
+# FreeBSD portinstall completion
+#
+have portinstall &&
+_portinstall()
+{
+ local cur portsdir prev indexfile
+ local -a COMPREPLY2
+
+ portsdir=${PORTSDIR:-/usr/ports}/
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ # First try INDEX-5
+ indexfile=$portsdir/INDEX-5
+ # Then INDEX if INDEX-5 does not exist or system is not FreeBSD 5.x
+ [ "${OSTYPE%.*}" = "freebsd5" -a -f $indexfile ] ||
+ indexfile=$portsdir/INDEX
+
+ [ "$prev" = "-l" -o "$prev" = "-L" -o "$prev" = "-o" ] && return 0
+
+ COMPREPLY=( $( egrep "^$cur" < $indexfile | cut -d'|' -f1 ) )
+ COMPREPLY2=( $( egrep "^[^\|]+\|$portsdir$cur" < $indexfile | \
+ cut -d'|' -f2 ) )
+ COMPREPLY2=( ${COMPREPLY2[@]#$portsdir} )
+ COMPREPLY=( "${COMPREPLY[@]}" "${COMPREPLY2[@]}" )
+
+ return 0
+} &&
+complete -F _portinstall $dirnames portinstall
+
+# Slackware Linux removepkg completion
+#
+have removepkg && [ -f /etc/slackware-version ] &&
+_removepkg()
+{
+ local packages cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( (cd /var/log/packages; compgen -f -- "$cur") ) )
+} &&
+complete -F _removepkg $filenames removepkg &&
+ complete $dirnames -f -X '!*.tgz' installpkg upgradepkg explodepkg
+
+# look(1) completion
+#
+have look &&
+_look()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD = 1 ]; then
+ COMPREPLY=( $( compgen -W '$(look $cur)' ) )
+ fi
+} &&
+complete -F _look $default look
+
+# ypcat(1) and ypmatch(1) completion
+#
+have ypmatch &&
+_ypmatch()
+{
+ local cur map
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ [ $1 = ypcat ] && [ $COMP_CWORD -gt 1 ] && return 0
+ [ $1 = ypmatch ] && [ $COMP_CWORD -gt 2 ] && return 0
+
+ if [ $1 = ypmatch ] && [ $COMP_CWORD -eq 1 ] && \
+ [ ${#COMP_WORDS[@]} -eq 3 ]; then
+ map=${COMP_WORDS[2]}
+ COMPREPLY=( $( compgen -W '$( ypcat $map | \
+ cut -d':' -f 1 )' -- $cur) )
+ else
+ [ $1 = ypmatch ] && [ $COMP_CWORD -ne 2 ] && return 0
+ COMPREPLY=( $( compgen -W \
+ '$( echo $(ypcat -x | cut -d"\"" -f 2))' -- $cur))
+ fi
+
+ return 0
+} &&
+complete -F _ypmatch ypmatch ypcat
+
+# mplayer(1) completion
+#
+have mplayer && {
+_mplayer_options_list()
+{
+ cur=${cur%\\}
+ COMPREPLY=( $( $1 $2 help 2> /dev/null | \
+ sed -e '1,/^Available/d' | awk '{print $1}' | \
+ sed -e 's/:$//' -e 's/^'${2#-}'$//' -e 's/<.*//' | \
+ grep "^$cur" ) )
+}
+
+_mplayer()
+{
+ local cmd cur prev skinsdir IFS=$' \t\n' i j k=0
+
+ COMPREPLY=()
+ cmd=${COMP_WORDS[0]}
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(ac|afm|vc|vfm|ao|vo|vop|fstype))
+ _mplayer_options_list mplayer $prev
+ return 0
+ ;;
+ -@(oac|ovc|of))
+ _mplayer_options_list mencoder $prev
+ return 0
+ ;;
+ -audiofile)
+ _filedir '@(mp3|MP3|mpg|MPG|ogg|OGG|wav|WAV|mid|MID|flac|FLAC|mka|MKA)'
+ return 0
+ ;;
+ -font)
+ _filedir '@(desc|ttf)'
+ return 0
+ ;;
+ -sub)
+ _filedir '@(srt|SRT|sub|SUB|txt|TXT|utf|UTF|rar|RAR|mpsub|smi|js|ssa|SSA|aas|AAS)'
+ return 0
+ ;;
+ -vobsub)
+ _filedir '@(idx|IDX|ifo|IFO|sub|SUB)'
+ IFS=$'\t\n'
+ COMPREPLY=( $( for i in "${COMPREPLY[@]}"; do
+ if [ -f $i -a -r $i ]; then
+ echo ${i%.*}
+ else
+ echo $i
+ fi
+ done ) )
+ IFS=$' \t\n'
+ return 0
+ ;;
+ -ifo)
+ _filedir '@(ifo|IFO)'
+ return 0
+ ;;
+ -cuefile)
+ _filedir '@(bin|BIN|cue|CUE)'
+ return 0
+ ;;
+ -skin)
+ # if you don't have installed mplayer in /usr you
+ # may want to set the MPLAYER_SKINS_DIR global variable
+ if [ -n "$MPLAYER_SKINS_DIR" ]; then
+ skinsdir=$MPLAYER_SKINS_DIR
+ else
+ skinsdir=/usr/share/mplayer/Skin
+ fi
+
+ IFS=$'\t\n'
+ for i in ~/.mplayer/Skin $skinsdir; do
+ if [ -d $i -a -r $i ]; then
+ for j in $( compgen -d $i/$cur ); do
+ COMPREPLY[$k]=${j#$i/}
+ k=$((++k))
+ done
+ fi
+ done
+ IFS=$' \t\n'
+ return 0
+ ;;
+ -@(mixer|@(cdrom|dvd)-device|dvdauth|fb|zrdev))
+ cur=${cur:=/dev/}
+ _filedir
+ return 0
+ ;;
+ -@(edl?(out)|lircconf|menu-cfg|playlist|csslib|dumpfile)| \
+ -@(subfile|vobsub|aofile|fbmodeconfig|include|o|dvdkey)| \
+ -passlogfile)
+ _filedir
+ return 0
+ ;;
+ -@(auto@(q|sync)|loop|menu-root|speed|sstep|aid|alang)| \
+ -@(?(@(audio|sub)-)demuxer|bandwidth|cache|chapter)| \
+ -@(dvd?(angle)|fps|frames|mc|passwd|user|sb|srate|ss|vcd)| \
+ -@(vi?(d|vo)|ffactor|sid|slang|spu@(align|aa|gauss))| \
+ -@(vobsubid|delay|bpp|brightness|contrast|dfbopts|display)| \
+ -@(fbmode|geometry|guiwid|hue|icelayer|screen[wh]|wid)| \
+ -@(monitor@(aspect|-@(dotclock|[hv]freq))|panscan|saturation)| \
+ -@(xineramascreen|zr@(crop|norm|quality|[xy]doff|[vh]dec))| \
+ -@(aspect|pp|x|y|xy|z|stereo|audio-@(density|delay|preload))| \
+ -@(endpos|osdlevel|ffourcc|sws|channels|skiplimit|format)| \
+ -@(ofps|aa@(driver|@(osd|sub)color)|vobsubout?(i@(ndex|d)))| \
+ -sub@(-bg-@(alpha|color)|cp|delay|fps|pos|align|width)| \
+ -sub@(font-@(blur|outline|autoscale|encoding|@(osd|text)-scale)))
+ return 0
+ ;;
+ -lavdopts)
+ COMPREPLY=( $( compgen -W 'ec er= bug= idct= gray' \
+ -- $cur ) )
+ return 0
+ ;;
+ -lavcopts)
+ COMPREPLY=( $( compgen -W 'vcodec= vqmin= vqscale= \
+ vqmax= mbqmin= mbqmax= vqdiff= \
+ vmax_b_frames= vme= vhq v4mv \
+ keyint= vb_strategy= vpass= \
+ aspect= vbitrate= vratetol= \
+ vrc_maxrate= vrc_minrate= \
+ vrc_buf_size= vb_qfactor= vi_qfactor= \
+ vb_qoffset= vi_qoffset= vqblur= \
+ vqcomp= vrc_eq= vrc_override= \
+ vrc_init_cplx= vqsquish= vlelim= \
+ vcelim= vstrict= vdpart vpsize= gray \
+ vfdct= idct= lumi_mask= dark_mask= \
+ tcplx_mask= scplx_mask= naq ildct \
+ format= pred qpel precmp= cmp= \
+ subcmp= predia= dia= trell last_pred= \
+ preme= subq= psnr mpeg_quant aic umv' \
+ -- $cur ) )
+ return 0
+ ;;
+ -ssf)
+ COMPREPLY=( $( compgen -W 'lgb= cgb= ls= cs= chs= \
+ cvs=' -- $cur ) )
+ return 0
+ ;;
+ -jpeg)
+ COMPREPLY=( $( compgen -W 'noprogressive progressive \
+ nobaseline baseline optimize= \
+ smooth= quality= outdir=' -- $cur ) )
+ return 0
+ ;;
+ -xvidopts)
+ COMPREPLY=( $( compgen -W 'dr2 nodr2' -- $cur ) )
+ return 0
+ ;;
+ -xvidencopts)
+ COMPREPLY=( $( compgen -W 'pass= bitrate= \
+ fixed_quant= me_quality= 4mv \
+ rc_reaction_delay_factor= \
+ rc_averaging_period= rc_buffer= \
+ quant_range= min_key_interval= \
+ max_key_interval= mpeg_quant \
+ mod_quant lumi_mask hintedme \
+ hintfile debug keyframe_boost= \
+ kfthreshold= kfreduction=' -- $cur ) )
+ return 0
+ ;;
+ -divx4opts)
+ COMPREPLY=( $( compgen -W 'br= key= deinterlace q= \
+ min_quant= max_quant= rc_period= \
+ rc_reaction_period= crispness= \
+ rc_reaction_ratio= pass= vbrpass= \
+ help' -- $cur ) )
+ return 0
+ ;;
+ -info)
+ COMPREPLY=( $( compgen -W 'name= artist= genre= \
+ subject= copyright= srcform= \
+ comment= help' -- $cur ) )
+ return 0
+ ;;
+ -lameopts)
+ COMPREPLY=( $( compgen -W 'vbr= abr cbr br= q= aq= \
+ ratio= vol= mode= padding= fast \
+ preset= help' -- $cur ) )
+ return 0
+ ;;
+ -rawaudio)
+ COMPREPLY=( $( compgen -W 'on channels= rate= \
+ samplesize= format=' -- $cur ) )
+ return 0
+ ;;
+ -rawvideo)
+ COMPREPLY=( $( compgen -W 'on fps= sqcif qcif cif \
+ 4cif pal ntsc w= h= y420 yv12 yuy2 \
+ y8 format= size=' -- $cur ) )
+ return 0
+ ;;
+ -aop)
+ COMPREPLY=( $( compgen -W 'list= delay= format= fout= \
+ volume= mul= softclip' -- $cur ) )
+ return 0
+ ;;
+ -dxr2)
+ COMPREPLY=( $( compgen -W 'ar-mode= iec958-encoded \
+ iec958-decoded mute ucode= 75ire bw \
+ color interlaced macrovision= norm= \
+ square-pixel ccir601-pixel cr-left= \
+ cr-right= cr-top= cr-bot= ck-rmin= \
+ ck-gmin= ck-bmin= ck-rmax= ck-gmax= \
+ ck-bmax= ck-r= ck-g= ck-b= \
+ ignore-cache= ol-osd= olh-cor= \
+ olw-cor= olx-cor= oly-cor= overlay \
+ overlay-ratio= update-cache' -- $cur ))
+ return 0
+ ;;
+ -tv)
+ COMPREPLY=( $( compgen -W 'on noaudio driver= device= \
+ input= freq= outfmt= width= height= \
+ buffersize= norm= channel= chanlist= \
+ audiorate= forceaudio alsa amode= \
+ forcechan= adevice= audioid= volume= \
+ bass= treble= balance= fps= \
+ channels= immediatemode=' -- $cur ) )
+ return 0
+ ;;
+ -mf)
+ COMPREPLY=( $( compgen -W 'on w= h= fps= type=' \
+ -- $cur ) )
+ return 0
+ ;;
+ -cdda)
+ COMPREPLY=( $( compgen -W 'speed= paranoia= \
+ generic-dev= sector-size= overlap= \
+ toc-bias toc-offset= skip noskip' \
+ -- $cur ) )
+ return 0
+ ;;
+ -input)
+ COMPREPLY=( $( compgen -W 'conf= ar-delay ar-rate \
+ keylist cmdlist js-dev file' -- $cur ) )
+ return 0
+ ;;
+ -af)
+ COMPREPLY=( $( compgen -W 'resample resample= \
+ channels channels= format format= \
+ volume volume= delay delay= pan \
+ pan= sub sub= surround surround=' \
+ -- $cur ) )
+ return 0
+ ;;
+ -af-adv)
+ COMPREPLY=( $( compgen -W 'force= list=' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W '-aid -alang -audio-demuxer \
+ -audiofile -cdrom-device -cache -cdda \
+ -channels -chapter -csslib -demuxer \
+ -dvd -dvd-device -dvdangle -dvdauth \
+ -dvdkey -dvdnav -forceidx -fps -frames \
+ -hr-mp3-seek -idx -mc -mf -ni -nobps \
+ -passwd -rawaudio -rtsp-stream-over-tcp\
+ -skipopening -sb -srate -ss -tv -user \
+ -vcd -vid -vivo -ifo -ffactor -font \
+ -noautosub -nooverlapsub -sid -slang \
+ -sub -subcc -subcp -sub-demuxer \
+ -subdelay -subfont-autoscale \
+ -subfont-blur -subfont-encoding \
+ -subfont-osd-scale -subfont-outline \
+ -subfont-text-scale -subfps -subfile \
+ -subpos -unicode -utf8 -vobsub \
+ -vobsubid -ac -afm -aspect -flip \
+ -lavdopts -noaspect -nosound -pp -ssf \
+ -stereo -sws -vc -vfm -vop -xvidopts\
+ -xy -zoom -bandwidth -cuefile \
+ -noextbased -rawvideo -overlapsub \
+ -sub-bg-alpha -sub-bg-color -subalign \
+ -subwidth -sub-no-text-pp -spualign \
+ -spuaa -spugauss -pphelp -verbose -v \
+ -noni -noidx -nohr-mp3-seek -extbased \
+ -bps -oldpp -nozoom -noflip -nounicode \
+ -noutf8' -- $cur ) )
+ # add mplayer specific options
+ [[ "$cmd" == @(?(g)mplayer) ]] && COMPREPLY=( "${COMPREPLY[@]}" \
+ $(compgen -W '-autoq -autosync -benchmark \
+ -framedrop -h -help -hardframedrop \
+ -identify -input -lircconf -loop \
+ -nojoystick -nolirc -nortc -playlist \
+ -quiet -really-quiet -rnd -sdp -skin \
+ -slave -softsleep -speed -sstep \
+ -use-stdin -dumpaudio -dumpfile \
+ -dumpstream -dumpvideo -dumpmicrodvdsub\
+ -dumpmpsub -dumpsrtsub -dumpjacosub \
+ -dumpsami -dumpsub -osdlevel -af \
+ -af-adv -ao -aofile -aop -delay -mixer \
+ -nowaveheader -bpp -brightness \
+ -contrast -display -double -dr -dxr2 \
+ -fb -fbmode -fbmodeconfig -forcexv -fs \
+ -geometry -hue -icelayer -jpeg \
+ -monitor-dotclock -monitor-hfreq \
+ -monitor-vfreq -monitoraspect \
+ -nograbpointer -noslices -panscan \
+ -rootwin -saturation -screenw -screenh \
+ -stop-xscreensaver -vm -vo -vsync -wid \
+ -xineramascreen -z -zrbw -zrcrop \
+ -zrdev -zrfd -zrhelp -zrnorm -zrquality \
+ -zrvdec -zrhdec -zrxdoff -zrydoff -y \
+ -edl -edlout -enqueue -fixed-vo \
+ -menu -menu-root -menu-cfg -shuffle \
+ -format -aahelp -dfbopts -fstype \
+ -guiwid -nokeepaspect -x --help \
+ -aaosdcolor -aasubcolor -aadriver \
+ -aaextended -aaeight' -- $cur) )
+ # add mencoder specific options
+ [[ "$cmd" = mencoder ]] && COMPREPLY=( "${COMPREPLY[@]}" \
+ $(compgen -W '-audio-density -audio-delay \
+ -audio-preload -divx4opts -endpos \
+ -ffourcc -include -info -lameopts \
+ -lavcopts -noskip -o -oac -ofps -ovc \
+ -passlogfile -skiplimit -vobsubout \
+ -vobsuboutindex -vobsuboutid \
+ -xvidencopts -of --verbose' -- $cur) )
+ ;;
+ *)
+ _filedir '@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|ASF|vob|VOB|bin|BIN|dat|DAT|vcd|VCD|ps|PS|pes|PES|fli|FLI|flv|FLV|viv|VIV|rm?(j)|RM?(J)|ra?(m)|RA?(M)|yuv|YUV|mov|MOV|qt|QT|mp[34]|MP[34]|m4v|M4V|og[gm]|OG[GM]|wav|WAV|dump|DUMP|mk[av]|MK[AV]|m4a|M4A|aac|AAC|m2v|M2V|dv|DV|rmvb|RMVB|mid|MID|ts|TS|3gp|mpc|MPC|flac|FLAC|flv|FLV|divx|DIVX)'
+ ;;
+ esac
+
+ return 0
+}
+complete $filenames -F _mplayer mplayer mencoder gmplayer kplayer
+}
+
+# KDE dcop completion
+#
+have dcop &&
+_dcop()
+{
+ local cur compstr
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ if [ -z $cur ]; then
+ compstr=${COMP_WORDS[*]}
+ else
+ compstr=$( command echo ${COMP_WORDS[*]} | sed "s/ $cur$//" )
+ fi
+ COMPREPLY=( $( compgen -W '$( command $compstr | sed s/\(.*\)// )' -- $cur ) )
+} &&
+complete -F _dcop dcop
+
+# wvdial(1) completion
+#
+have wvdial &&
+_wvdial()
+{
+ local cur prev config i IFS=$'\t\n'
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ --config)
+ _filedir
+ return 0
+ ;;
+ esac
+
+ case $cur in
+ -*)
+ COMPREPLY=( $( compgen -W '--config --chat \
+ --remotename --help --version --no-syslog' \
+ -- $cur ) )
+ ;;
+ *)
+ # start with global and personal config files
+ config="/etc/wvdial.conf"$'\t'"$HOME/.wvdialrc"
+ # replace with command line config file if present
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == "--config" ]]; then
+ config=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+ # parse config files for sections and
+ # remove default section
+ COMPREPLY=( $( sed -ne \
+ "s|^\[Dialer \($cur.*\)\]$|\1|p" \
+ $config 2>/dev/null |grep -v '^Defaults$'))
+ # escape spaces
+ COMPREPLY=${COMPREPLY// /\\ }
+ ;;
+ esac
+
+} &&
+complete -F _wvdial wvdial
+
+# gpg(1) completion
+#
+have gpg &&
+_gpg()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(s|-sign|-clearsign|-decrypt-files|-load-extension))
+ _filedir
+ return 0
+ ;;
+ --@(export|@(?(l|nr|nrl)sign|edit)-key))
+ # return list of public keys
+ COMPREPLY=( $( compgen -W "$( gpg --list-keys 2>/dev/null | sed -ne 's@^pub.*/\([^ ]*\).*\(<\([^>]*\)>\).*$@\1 \3@p')" -- "$cur" ))
+ return 0
+ ;;
+ -@(r|-recipient))
+ COMPREPLY=( $( compgen -W "$( gpg --list-keys 2>/dev/null | sed -ne 's@^pub.*<\([^>]*\)>.*$@\1@p')" -- "$cur" ))
+ if [ -e ~/.gnupg/gpg.conf ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "$( sed -ne 's@^[ \t]*group[ \t][ \t]*\([^=]*\).*$@\1@p' ~/.gnupg/gpg.conf )" -- "$cur") )
+ fi
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-s -b -e -f -c -d -a -r -u -Z -o -v\
+ -q -n -N $(gpg --dump-options)' -- $cur ) )
+ fi
+
+} &&
+complete -F _gpg $default gpg
+
+# iconv(1) completion
+#
+have iconv &&
+_iconv()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(f|t|-@(from|to)-code))
+ COMPREPLY=( $( compgen -W \
+ '$( iconv --list | sed -e "s@//@@;" )' -- "$cur" ) )
+ return 0
+ ;;
+ esac
+
+
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $( compgen -W '--from-code -f --to-code -t --list
+ --output -o --verbose' -- "$cur" ) )
+ return 0
+ fi
+} &&
+complete -F _iconv $default iconv
+
+# dict(1) completion
+#
+{ have dict || have rdict; } && {
+_dictdata()
+{
+ dict $host $port $1 2>/dev/null | sed -ne \
+ 's/^['$'\t '']['$'\t '']*\([^'$'\t '']*\).*$/\1/p'
+}
+
+_dict()
+{
+ local cur prev host port db dictfile
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ dictfile=/usr/share/dict/words
+
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ case "${COMP_WORDS[i]}" in
+ -@(h|--host))
+ host=${COMP_WORDS[i+1]}
+ [ -n "$host" ] && host="-h $host"
+ i=$((++i))
+ ;;
+ -@(p|-port))
+ port=${COMP_WORDS[i+1]}
+ [ -n "$port" ] && port="-p $port"
+ i=$((++i))
+ ;;
+ -@(d|-database))
+ db=${COMP_WORDS[i+1]}
+ [ -n "$db" ] && host="-d $db"
+ i=$((++i))
+ ;;
+ *)
+ ;;
+ esac
+ done
+
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $( compgen -W '-h --host -p --port -d --database \
+ -m --match -s --strategy -c --config -C \
+ --nocorrect -D --dbs -S --strats -H \
+ --serverhelp -i --info -I --serverinfo \
+ -a --noauth -u --user -k --key -V --version \
+ -L --license --help -v --verbose -r --raw \
+ -P --pager --debug --html --pipesize --client' \
+ -- "$cur" ) )
+ return 0
+ fi
+
+ case "$prev" in
+ -@(d|-database|i|info))
+ COMPREPLY=( $( compgen -W '$( _dictdata -D )' -- "$cur" ) )
+ return 0
+ ;;
+ -@(s|-strategy))
+ COMPREPLY=( $( compgen -W '$( _dictdata -S )' -- "$cur" ) )
+ return 0
+ ;;
+ *)
+ ;;
+ esac
+
+ [ -r $dictfile ] && \
+ COMPREPLY=( $( compgen -W '$( cat $dictfile )' -- "$cur" ) )
+}
+complete -F _dict $default dict rdict
+}
+
+# cdrecord(1) completion
+#
+have cdrecord &&
+_cdrecord()
+{
+ local cur prev i generic_options track_options track_mode
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # foo=bar style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ @(text|cue)file)
+ _filedir
+ return 0
+ ;;
+ blank)
+ COMPREPLY=( $( compgen -W 'help all fast \
+ track unreserve trtail unclose session' \
+ -- $cur ) )
+ return 0
+ ;;
+ driveropts)
+ COMPREPLY=( $( compgen -W 'burnfree noburnfree\
+ varirec= audiomaster forcespeed noforcespeed\
+ speedread nospeedread singlesession \
+ nosinglesession hidecdr nohidecdr tattooinfo\
+ tattoofile=' -- $cur ) )
+ return 0
+ ;;
+ esac
+ fi
+
+ generic_options=(-version -v -V -d -silent -s -force -immed -dummy \
+ -dao -raw -raw96r -raw96p -raw16 -multi -msinfo -toc \
+ -atip -fix -nofix -waiti -load -lock -eject -format \
+ -setdropts -checkdrive -prcap -inq -scanbus -reset \
+ -abort -overburn -ignsize -useinfo -packet -noclose \
+ -text debug= kdebug= kd= minbuf= speed= blank= fs= \
+ dev= gracetime= timeout= driver= driveropts= \
+ defpregap= pktsize= mcn= textfile= cuefile=)
+ track_options=(-audio -swab -data -mode2 -xa -xa1 -xa2 -xamix -cdi \
+ -isosize -pad padsize= -nopad -shorttrack -noshorttrack\
+ pregap= -preemp -nopreemp -copy -nocopy -scms tcsize= \
+ isrc= index=)
+ # look if previous was either a file or a track option
+ track_mode=0
+ if [ $COMP_CWORD -gt 1 ]; then
+ if [ -f "$prev" ]; then
+ track_mode=1
+ else
+ for (( i=0; i < ${#track_options[@]}; i++ )); do
+ if [[ "${track_options[i]}" == "$prev" ]]; then
+ track_mode=1
+ break
+ fi
+ done
+ fi
+ fi
+
+ # files are always eligible completion
+ _filedir
+ # track options are always available
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W '${track_options[@]}' -- $cur ) )
+ # general options are no more available after file or track option
+ if [ $track_mode -eq 0 ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" \
+ $( compgen -W '${generic_options[@]}' -- $cur ) )
+ fi
+
+} &&
+complete -F _cdrecord $filenames cdrecord
+
+# mkisofs(8) completion
+#
+have mkisofs &&
+_mkisofs()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(o|abstract|biblio|check-session|copyright|log-file|root-info|prep-boot|*-list))
+ _filedir
+ return 0
+ ;;
+ -*-charset)
+ COMPREPLY=( $( mkisofs -input-charset help 2>&1 | \
+ tail +3 | grep "^$cur") )
+ return 0
+ ;;
+ -uid)
+ _uids
+ return 0
+ ;;
+ -gid)
+ _gids
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-abstract -A -allow-lowercase \
+ -allow-multidot -biblio -cache-inodes \
+ -no-cache-inodes -b -eltorito-alt-boot -B -G \
+ -hard-disk-boot -no-emul-boot -no-boot \
+ -boot-load-seg -boot-load-size \
+ -boot-info-table -C -c -check-oldname \
+ -check-session -copyright -d -D -dir-mode \
+ -dvd-video -f -file-mode -gid -gui \
+ -graft-points -hide -hide-list -hidden \
+ -hidden-list -hide-joliet -hide-joliet-list \
+ -hide-joliet-trans-tbl -hide-rr-moved \
+ -input-charset -output-charset -iso-level -J \
+ -joliet-long -jcharset -l -L -log-file -m \
+ -exclude-list -max-iso9660-filenames -M -N \
+ -new-dir-mode -nobak -no-bak -force-rr -no-rr \
+ -no-split-symlink-components \
+ -no-split-symlink-fields -o -pad -no-pad \
+ -path-list -P -p -print-size -quiet -R -r \
+ -relaxed-filenames -sort -split-output \
+ -stream-media-size -stream-file-name -sysid -T\
+ -table-name -ucs-level -udf -uid \
+ -use-fileversion -U -no-iso-translate -V \
+ -volset -volset-size -volset-seqno -v -x -z \
+ -hfs -apple -map -magic -hfs-creator \
+ -hfs-type -probe -no-desktop -mac-name \
+ -boot-hfs-file -part -auto -cluster-size \
+ -hide-hfs -hide-hfs-list -hfs-volid \
+ -icon-position -root-info -prep-boot \
+ -input-hfs-charset -output-hfs-charset \
+ -hfs-unlock -hfs-bless -hfs-parms --cap \
+ --netatalk --double --ethershare --ushare \
+ --exchange --sgi --xinet --macbin --single \
+ --dave --sfm --osx-double --osx-hfs' -- $cur ))
+ else
+ _filedir
+ fi
+
+} &&
+complete -F _mkisofs $filenames mkisofs
+
+# mc(1) completion
+#
+have mc &&
+_mc()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # -name value style option
+ case "$prev" in
+ -@(e|v|l|P))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ --@(edit|view|ftplog|printwd))
+ _filedir
+ return 0
+ ;;
+ esac
+ fi
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a --stickchars -b --nocolor -c \
+ --color -C --colors= -d --nomouse -e --edit= -f \
+ --datadir -k --resetsoft -l --ftplog= -P --printwd= \
+ -s --slow -t --termcap -u --nosubshell -U --subshell \
+ -v --view= -V --version -x --xterm -h --help' -- $cur ) )
+ else
+ _filedir -d
+ fi
+} &&
+complete -F _mc $filenames mc
+
+# yum(8) completion
+#
+have yum && {
+_yum()
+{
+ local cur prev special
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == @(install|update|upgrade|remove|erase|deplist) ]]; then
+ special=${COMP_WORDS[i]}
+ fi
+ done
+
+ if [ -n "$special" ]; then
+ case $special in
+ install|deplist)
+ COMPREPLY=( $( compgen -W '$( yum -C list | cut -d" " -f1 )' -- $cur ) )
+ return 0
+ ;;
+ *)
+ _rpm_installed_packages
+ return 0
+ ;;
+ esac
+ fi
+
+ case $cur in
+ --*)
+ COMPREPLY=( $( compgen -W '--installroot --version --help --enablerepo --disablerepo --exclude --obsoletes --noplugins' -- $cur ) )
+ return 0
+ ;;
+ -*)
+ COMPREPLY=( $( compgen -W '-c -e -d -y -t -R -C -h' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ case $prev in
+ list)
+ COMPREPLY=( $( compgen -W 'all available updates installed extras obsoletes recent' -- $cur ) )
+ ;;
+ clean)
+ COMPREPLY=( $( compgen -W 'packages headers metadata cache dbcache all' -- $cur ) )
+ ;;
+ localinstall)
+ _filedir rpm
+ ;;
+ -c)
+ _filedir
+ ;;
+ --installroot)
+ _filedir -d
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W 'install update check-update upgrade remove list \
+ search info provides clean groupinstall groupupdate \
+ grouplist deplist erase groupinfo groupremove \
+ localinstall localupdate makecache resolvedep \
+ shell whatprovides' -- $cur ) )
+ ;;
+ esac
+}
+complete -F _yum $filenames yum
+
+# yum-arch(8) completion
+#
+_yum_arch()
+{
+ local cur
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W '-d -v -vv -n -c -z -s -l -q' -- $cur ) )
+ ;;
+ *)
+ _filedir -d
+ ;;
+ esac
+
+ return 0
+
+}
+complete -F _yum_arch $filenames yum-arch
+}
+
+# ImageMagick completion
+#
+have convert && {
+_ImageMagick()
+{
+ local prev
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -channel)
+ COMPREPLY=( $( compgen -W 'Red Green Blue Opacity \
+ Matte Cyan Magenta Yellow Black' -- $cur ) )
+ return 0
+ ;;
+ -colormap)
+ COMPREPLY=( $( compgen -W 'shared private' -- $cur ) )
+ return 0
+ ;;
+ -colorspace)
+ COMPREPLY=( $( compgen -W 'GRAY OHTA RGB Transparent \
+ XYZ YCbCr YIQ YPbPr YUV CMYK' -- $cur ) )
+ return 0
+ ;;
+ -compose)
+ COMPREPLY=( $( compgen -W 'Over In Out Atop Xor Plus \
+ Minus Add Subtract Difference Multiply Bumpmap\
+ Copy CopyRed CopyGreen CopyBlue CopyOpacity' \
+ -- $cur ) )
+ return 0
+ ;;
+ -compress)
+ COMPREPLY=( $( compgen -W 'None BZip Fax Group4 JPEG \
+ Lossless LZW RLE Zip' -- $cur ) )
+ return 0
+ ;;
+ -dispose)
+ COMPREPLY=( $( compgen -W 'Undefined None Background \
+ Previous' -- $cur ) )
+ return 0
+ ;;
+ -encoding)
+ COMPREPLY=( $( compgen -W 'AdobeCustom AdobeExpert \
+ AdobeStandard AppleRoman BIG5 GB2312 Latin2 \
+ None SJIScode Symbol Unicode Wansung' -- $cur))
+ return 0
+ ;;
+ -endian)
+ COMPREPLY=( $( compgen -W 'MSB LSB' -- $cur ) )
+ return 0
+ ;;
+ -filter)
+ COMPREPLY=( $( compgen -W 'Point Box Triangle Hermite \
+ Hanning Hamming Blackman Gaussian Quadratic \
+ Cubic Catrom Mitchell Lanczos Bessel Sinc' \
+ -- $cur ) )
+ return 0
+ ;;
+ -format)
+ COMPREPLY=( $( convert -list format | \
+ awk '/ [r-][w-][+-] / {print $1}' | \
+ tr -d '*' | tr [:upper:] [:lower:] | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+ -gravity)
+ COMPREPLY=( $( compgen -W 'Northwest North NorthEast \
+ West Center East SouthWest South SouthEast' \
+ -- $cur ) )
+ return 0
+ ;;
+ -intent)
+ COMPREPLY=( $( compgen -W 'Absolute Perceptual \
+ Relative Saturation' -- $cur ) )
+ return 0
+ ;;
+ -interlace)
+ COMPREPLY=( $( compgen -W 'None Line Plane Partition' \
+ -- $cur ) )
+ return 0
+ ;;
+ -limit)
+ COMPREPLY=( $( compgen -W 'Disk File Map Memory' \
+ -- $cur ) )
+ return 0
+ ;;
+ -list)
+ COMPREPLY=( $( compgen -W 'Delegate Format Magic \
+ Module Resource Type' -- $cur ) )
+ return 0
+ ;;
+ -map)
+ COMPREPLY=( $( compgen -W 'best default gray red \
+ green blue' -- $cur ) )
+ _filedir
+ return 0
+ ;;
+ -noise)
+ COMPREPLY=( $( compgen -W 'Uniform Gaussian \
+ Multiplicative \
+ Impulse Laplacian Poisson' -- $cur ) )
+ return 0
+ ;;
+ -preview)
+ COMPREPLY=( $( compgen -W 'Rotate Shear Roll Hue \
+ Saturation Brightness Gamma Spiff \
+ Dull Grayscale Quantize Despeckle \
+ ReduceNoise AddNoise Sharpen Blur \
+ Treshold EdgeDetect Spread Shade \
+ Raise Segment Solarize Swirl Implode \
+ Wave OilPaint CharcoalDrawing JPEG' \
+ -- $cur ) )
+ return 0
+ ;;
+ -@(mask|profile|texture|tile|write))
+ _filedir
+ return 0
+ ;;
+ -type)
+ COMPREPLY=( $( compgen -W 'Bilevel Grayscale Palette \
+ PaletteMatte TrueColor TrueColorMatte \
+ ColorSeparation ColorSeparationlMatte \
+ Optimize' -- $cur ) )
+ return 0
+ ;;
+ -units)
+ COMPREPLY=( $( compgen -W 'Undefined PixelsPerInch \
+ PixelsPerCentimeter' -- $cur ) )
+ return 0
+ ;;
+ -virtual-pixel)
+ COMPREPLY=( $( compgen -W 'Constant Edge mirror tile' \
+ -- $cur ) )
+ return 0
+ ;;
+ -visual)
+ COMPREPLY=( $( compgen -W 'StaticGray GrayScale \
+ StaticColor PseudoColor TrueColor \
+ DirectColor defaut visualid' -- $cur ))
+ return 0
+ ;;
+ esac
+}
+
+_convert()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-adjoin -affine -antialias -append \
+ -authenticate -average -background -black-threshold \
+ -blue-primary -blur -border -bordercolor -channel \
+ -charcoal -chop -clip -coalesce -colorize -colors \
+ -colorspace -comment -compress -contrast -convolve \
+ -crop -cycle -debug -deconstruct -delay -density \
+ -depth -despeckle -display -dispose -dither -draw \
+ -edge -emboss -encoding -endian -enhance -equalize \
+ -extract -fill -filter -flatten -flip -flop -font \
+ -frame -fuzz -gamma -gaussian -geometry \
+ -green-primary -gravity -help -implode -intent \
+ -interlace -label -lat -level -limit -list -log -loop \
+ -map -mask -matte -median -modulate -monochrome \
+ -morph -mosaic -negate -noop -noise -normalize \
+ -opaque -ordered-dither -page -paint -ping -pointsize \
+ -preview -profile -quality -raise -random-threshold \
+ -region -raise -red-primary -render -resize -resample \
+ -roll -rotate -sample -sampling-factor -scale -scene \
+ -seed -segment -shade -sharpen -shave -shear -size \
+ -solarize -spread -stroke -strokewidth -swirl \
+ -texture -threshold -thumbnail -tile -transform \
+ -transparent -treedepth -trim -type -undercolor \
+ -units -unsharp -verbose -version -view \
+ -virtual-pixel -wave -white-point -white-threshold \
+ -write' -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+adjoin +append +compress \
+ +contrast +debug +dither +endian +gamma +label +map \
+ +mask +matte +negate +noise +page +raise +render \
+ +write' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _convert $filenames convert
+
+_mogrify()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-affine -antialias -authenticate \
+ -background -black-threshold -blue-primary -blur \
+ -border -bordercolor -channel -charcoal -chop \
+ -colorize -colors -colorspace -comment -compress \
+ -contrast -convolve -crop -cycle -debug -delay \
+ -density -depth -despeckle -display -dispose -dither \
+ -draw -edge -emboss -encoding -endian -enhance \
+ -equalize -extract -fill -filter -flip -flop -font \
+ -format -frame -fuzz -gamma -gaussian -geometry \
+ -green-primary -implode -interlace -help -label -lat \
+ -level -limit -list -log -loop -map -mask -matte \
+ -median -modulate -monochrome -negate -noop \
+ -normalize -opaque -page -paint -fill -ordered-dither \
+ -pointsize -profile -quality -raise -random-threshold \
+ -red-primary -region -resample -resize -roll -rotate \
+ -sample -sampling-factor -scale -scene -seed -segment \
+ -shade -sharpen -shear -size -solarize -spread \
+ -stroke -strokewidth -swirl -texture -threshold \
+ -thumbnail -tile -transform -transparent -treedepth \
+ -trim -type -undercolor -units -unsharp -verbose \
+ -version -view -virtual-pixel -wave -white-point \
+ -white-threshold' -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+compress +contrast +debug +dither \
+ +endian +gamma +label +map +mask +matte +negate +page \
+ +raise' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _mogrify $filenames mogrify
+
+_display()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-authenticate -backdrop -border \
+ -colormap -colors -colorspace -comment -compress \
+ -contrast -crop -debug -delay -density -depth \
+ -despeckle -display -dispose -dither -edge -endian \
+ -enhance -extract -filter -flip -flop -frame -gamma \
+ -geometry -help -immutable -interlace -label -limit \
+ -log -map -matte -monochrome -negate -noop -page \
+ -quality -raise -remote -roll -rotate -sample \
+ -sampling-factor -scene -segment -sharpen -size \
+ -texture -treedepth -trim -update -verbose -version \
+ -virtual-pixel -window -window_group -write' -- $cur))
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+compress +contrast +debug +dither \
+ +endian +gamma +label +map +matte +negate +page \
+ +raise +write' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _display $filenames display
+
+_animate()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-authenticate -backdrop -colormap \
+ -colors -colorspace -crop -debug -delay -density \
+ -depth -display -dither -extract -gamma -geometry \
+ -help -interlace -limit -log -matte -map -monochrome \
+ -noop -pause -remote -rotate -sampling-factor -scene \
+ -size -treedepth -trim -verbose -version -visual \
+ -virtual-pixel -window' -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+debug +dither +gamma +map +matte' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _animate $filenames animate
+
+_identify()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-authenticate -debug -density \
+ -depth -extract -format -help -interlace -limit -list \
+ -log -size -sampling-factor -verbose -version \
+ -virtual-pixel' -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+debug ' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _identify $filenames identify
+
+_montage()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-adjoin -affine -authenticate \
+ -blue-primary -blur -colors -colorspace -comment \
+ -compose -compress -crop -debug -density -depth \
+ -display -dispose -dither -draw -encoding -endian \
+ -extract -fill -filter -flip -flop -frame -gamma \
+ -geometry -gravity -green-primary -interlace -help \
+ -label -limit -log -matte -mode -monochrome -noop \
+ -page -pointsize -quality -red-primary -resize \
+ -rotate -sampling-factor -scene -shadow -size \
+ -stroke -texture -thumbnail -tile -transform \
+ -transparent -treedepth -trim -type -verbose \
+ -version -virtual-pixel -white-point' -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+adjoin +compress +debug +dither \
+ +endian +gamma +label +matte +page' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _montage $filenames montage
+
+_composite()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-affine -authenticate \
+ -blue-primary -colors -colorspace -comment -compose \
+ -compress -debug -density -depth -displace -display \
+ -dispose -dissolve -dither -encoding -endian -extract \
+ -filter -font -geometry -gravity -green-primary -help \
+ -interlace -label -limit -log -matte -monochrome \
+ -negate -page -profile -quality -red-primary -rotate \
+ -resize -sampling-factor -scene -sharpen -size \
+ -stegano -stereo -thumbnail -tile -transform \
+ -treedepth -type -units -unsharp -verbose -version \
+ -virtual-pixel -watermark -white-point -write' \
+ -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+compress +debug +dither +endian +label \
+ +matte +negate +page +write' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _composite $filenames composite
+}
+
+# dd(1) completion
+#
+have dd &&
+_dd()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ case "$cur" in
+ if=*|of=*)
+ cur=${cur#*=}
+ _filedir
+ return 0
+ ;;
+ conv=*)
+ cur=${cur#*=}
+ COMPREPLY=( $( compgen -W 'ascii ebcdic ibm block unblock \
+ lcase notrunc ucase swab noerror sync' \
+ -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ _expand || return 0
+
+ COMPREPLY=( $( compgen -W '--help --version' -- $cur ) \
+ $( compgen -W 'bs cbs conv count ibs if obs of seek skip'\
+ -S '=' -- $cur ) )
+} &&
+complete -F _dd $nospace $filenames dd
+
+# CUPS cancel(1) completion
+#
+have cancel &&
+_cancel()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( lpstat | cut -d' ' -f1 | grep "^$cur" ) )
+} &&
+complete -F _cancel $filenames cancel
+
+# aspell(1) completion
+#
+have aspell && {
+_aspell_dictionary()
+{
+ local datadir
+ datadir=/usr/lib/aspell
+ COMPREPLY=( $( command ls $datadir/*.@(multi|alias) ) )
+ COMPREPLY=( ${COMPREPLY[@]%.@(multi|alias)} )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]#$datadir/}' -- $cur ) )
+}
+
+_aspell()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # --name value style option
+ case "$prev" in
+ @(-c|-p|check))
+ _filedir
+ return 0
+ ;;
+ @(dump|create|merge))
+ COMPREPLY=( $( compgen -W 'master personal repl' -- $cur ) )
+ return 0
+ ;;
+ -d)
+ _aspell_dictionary
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ --@(conf|personal|repl|per-conf))
+ _filedir
+ return 0
+ ;;
+ --@(conf-dir|data-dir|dict-dir|home-dir|local-data-dir|prefix))
+ _filedir -d
+ return 0
+ ;;
+ --master)
+ _aspell_dictionary
+ return 0
+ ;;
+ --mode)
+ COMPREPLY=( $( compgen -W 'none url email sgml tex' -- $cur ) )
+ return 0
+ ;;
+ --sug-mode)
+ COMPREPLY=( $( compgen -W 'ultra fast normal bad-speller' -- $cur ) )
+ return 0
+ ;;
+ --keymapping)
+ COMPREPLY=( $( compgen -W 'aspell ispell' -- $cur ) )
+ return 0
+ ;;
+ esac
+ fi
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--conf= --conf-dir= --data-dir= --dict-dir= \
+ --encoding= --add-filter= --rem-filter= --mode= -e \
+ -H -t --add-extra-dicts= --rem-extra-dicts= \
+ --home-dir= -W --ignore= --ignore-accents \
+ --dont-ignore-accents --ignore-case --dont-ignore-case \
+ --ignore-repl --dont-ignore-repl --jargon= --keyboard= \
+ --lang= --language-tag= --local-data-dir= -d --master= \
+ --module= --add-module-search-order= \
+ --rem-module-search-order= --per-conf= -p --personal= \
+ --prefix= --repl= -C -B --run-together --dont-run-together \
+ --run-together-limit= --run-together-min= --save-repl \
+ --dont-save-repl --set-prefix --dont-set-prefix --size= \
+ --spelling= --strip-accents --dont-strip-accents \
+ --sug-mode= --add-word-list-path= --rem-word-list-path= \
+ -b -x --backup -b|-x --dont-backup --reverse --dont-reverse \
+ --time --dont-time --keymapping= --add-email-quote= \
+ --rem-email-quote= --email-margin= --add-tex-command= \
+ --rem-tex-command= --tex-check-comments \
+ --dont-tex-check-comments --add-tex-extension= \
+ --rem-tex-extension= --add-sgml-check= --rem-sgml-check= \
+ --add-sgml-extension= --rem-sgml-extension=' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W '-? help -c check -a pipe -l list \
+ config config soundslike filter -v version dump \
+ create merge' -- $cur ) )
+ fi
+
+}
+complete -F _aspell $filenames aspell
+}
+
+# xmms(1) completion
+#
+have xmms &&
+_xmms()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-h --help -r --rew -p --play \
+ -u --pause -s --stop -t --play-pause -f --fwd -e \
+ --enqueue -m --show-main-window -i --sm-client-id \
+ -v --version' -- $cur ) )
+ else
+ _filedir '@(mp[23]|MP[23]|ogg|OGG|wav|WAV|pls|m3u|xm|mod|s[3t]m|it|mtm|ult|flac)'
+
+ fi
+
+} &&
+complete -F _xmms $filenames xmms
+
+# info(1) completion
+#
+have info &&
+_info()
+{
+ local cur infopath UNAME
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _expand || return 0
+
+ # default completion if parameter contains /
+ if [[ "$cur" == */* ]]; then
+ _filedir
+ return 0
+ fi
+
+ infopath='/usr/share/info'
+
+ if [ "${INFOPATH: -1:1}" == ':' ]; then
+ infopath=${INFOPATH}${infopath}
+ elif [ ${INFOPATH:+set} ]; then
+ infopath=$INFOPATH
+ fi
+
+ infopath=$infopath:
+ if [ -n "$cur" ]; then
+ infopath="${infopath//://$cur* }"
+ else
+ infopath="${infopath//:// }"
+ fi
+
+ # redirect stderr for when path doesn't exist
+ COMPREPLY=( $( eval command ls "$infopath" 2>/dev/null ) )
+ # weed out directory path names and paths to info pages
+ COMPREPLY=( ${COMPREPLY[@]##*/?(:)} )
+ # weed out info dir file
+ for (( i=0 ; i < ${#COMPREPLY[@]} ; ++i )); do
+ if [ "${COMPREPLY[$i]}" == 'dir' ]; then
+ unset COMPREPLY[$i];
+ fi;
+ done
+ # strip suffix from info pages
+ COMPREPLY=( ${COMPREPLY[@]%.@(gz|bz2)} )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]%.*}' -- "${cur//\\\\/}" ) )
+
+ return 0
+} &&
+complete -F _info $filenames info
+
+# dhclient(1) completion
+#
+have dhclient && _dhclient()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(cf|lf|pf|sf))
+ _filedir
+ return 0
+ ;;
+ -s)
+ _known_hosts
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-p -d -q -1 -r -lf -pf \
+ -cf -sf -s -g -n -nw -w' -- $cur ) )
+ else
+ _available_interfaces
+ fi
+} &&
+complete -F _dhclient dhclient
+
+# lvm(8) completion
+#
+have lvm && {
+_volumegroups()
+{
+ COMPREPLY=( $(compgen -W "$( vgscan 2>/dev/null | \
+ sed -n -e 's|.*Found.*"\(.*\)".*$|\1|p' )" -- $cur ) )
+}
+
+_physicalvolumes()
+{
+ COMPREPLY=( $(compgen -W "$( pvscan 2>/dev/null | \
+ sed -n -e 's|^.*PV \(.*\) VG.*$|\1|p' )" -- $cur ) )
+}
+
+_logicalvolumes()
+{
+ COMPREPLY=( $(compgen -W "$( lvscan 2>/dev/null | \
+ sed -n -e "s|^.*'\(.*\)'.*$|\1|p" )" -- $cur ) )
+}
+
+_units()
+{
+ COMPREPLY=( $( compgen -W 'h s b k m g t H K M G T' -- $cur ) )
+}
+
+_sizes()
+{
+ COMPREPLY=( $( compgen -W 'k K m M g G t T' -- $cur ) )
+}
+
+_args()
+{
+ args=0
+ if [[ "${COMP_WORDS[0]}" == lvm ]]; then
+ offset=2
+ else
+ offset=1
+ fi
+ for (( i=$offset; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" != -* ]]; then
+ args=$(($args + 1))
+ fi
+ done
+}
+
+_lvmdiskscan()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h -? --help -l \
+ --lvmpartition -v --verbose --version' -- $cur ) )
+ fi
+}
+complete -F _lvmdiskscan lvmdiskscan
+
+_pvscan()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -e \
+ --exported -n --novolumegroup -h -? \
+ --help --ignorelockingfailure -P \
+ --partial -s --short -u --uuid -v \
+ --verbose --version' -- $cur ) )
+ fi
+}
+complete -F _pvscan pvscan
+
+_pvs()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(o|O|-options|-sort))
+ COMPREPLY=( $( compgen -W 'pv_fmt pv_uuid \
+ pv_size pv_free pv_used pv_name \
+ pv_attr pv_pe_count \
+ pv_pe_alloc_count' -- $cur ) )
+ return 0
+ ;;
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--aligned -a --all -d --debug \
+ -h -? --help --ignorelockingfailure --noheadings \
+ --nosuffix -o --options -O --sort \
+ --separator --unbuffered --units \
+ -v --verbose --version' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvs pvs
+
+_pvdisplay()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c --colon -C --columns --units \
+ -v --verbose -d --debug -h --help --version' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvdisplay pvdisplay
+
+_pvchange()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|x|-autobackup|--allocatable))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a --all -A --autobackup \
+ -d --debug -h --help -t --test -u --uuid -x \
+ --allocatable -v --verbose --addtag --deltag \
+ --version' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvchange pvchange
+
+_pvcreate()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --restorefile)
+ _filedir
+ return 0
+ ;;
+ -@(M|-metadatatype))
+ COMPREPLY=( $( compgen -W '1 2' -- $cur ) )
+ return 0
+ ;;
+ --metadatacopies)
+ COMPREPLY=( $( compgen -W '0 1 2' -- $cur ) )
+ return 0
+ ;;
+ --@(metadatasize|setphysicalvolumesize))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--restorefile -d --debug -f \
+ --force -h -? --help --labelsector -M --metadatatype \
+ --metadatacopies --metadatasize \
+ --setphysicalvolumesize -t --test -u --uuid uuid -v \
+ --verbose -y --yes --version' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvcreate pvcreate
+
+_pvmove()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(n|-name))
+ _logicalvolumes
+ return 0
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--abort -A --autobackup \
+ -b --background -d --debug -f --force -h -? \
+ --help -i --interval -t --test -v --verbose \
+ --version -n --name' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvmove pvmove
+
+_pvremove()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -f --force -h -? \
+ --help -y --yes -t --test -v --verbose \
+ --version' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvremove pvremove
+
+_vgscan()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h --help \
+ --ignorelockingfailure --mknodes -P \
+ --partial -v --verbose --version' -- $cur ) )
+ fi
+}
+complete -F _vgscan vgscan
+
+_vgs()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(o|O|-options|-sort))
+ COMPREPLY=( $( compgen -W 'vg_fmt vg_uuid vg_name \
+ vg_attr vg_size vg_free vg_sysid \
+ vg_extent_size vg_extent_count vg_free_count \
+ max_lv max_pv pv_count lv_count snap_count \
+ vg_seqno' -- $cur ) )
+ return 0
+ ;;
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--aligned -d --debug \
+ -h --help --ignorelockingfailure --noheadings \
+ --nosuffix -o --options -O --sort -P --partial \
+ --separator --unbuffered --units \
+ -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgs vgs
+
+_vgdisplay()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c --colon -C --columns --units \
+ -P --partial -A --activevolumegroups -v --verbose \
+ -d --debug -h --help --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgdisplay vgdisplay
+
+_vgchange()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(a|A|x|-available|-autobackup|-resizeable))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup --alloc -P \
+ --partial -d --debug -h --help --ignorelockingfailure \
+ -t --test -u --uuid -v --verbose --version -a \
+ --available -x --resizeable -l --logicalvolume \
+ --addtag --deltag' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgchange vgchange
+
+_vgcreate()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(M|-metadatatype))
+ COMPREPLY=( $( compgen -W '1 2' -- $cur ) )
+ return 0
+ ;;
+ -@(s|-physicalextentsize))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup --addtag \
+ --alloc -d --debug -h --help -l --maxlogicalvolumes \
+ -M --metadatatype -p --maxphysicalvolumes -s \
+ --physicalextentsize -t --test -v --verbose \
+ --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _volumegroups
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _vgcreate vgcreate
+
+_vgremove()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h --help -t --test \
+ -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgremove vgremove
+
+_vgrename()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -h \
+ -? --help -t --test -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgrename vgrename
+
+_vgreduce()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a --all -A --autobackup -d \
+ --debug -h --help --removemissing -t --test -v \
+ --verbose --version' -- $cur ) )
+
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _volumegroups
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _vgreduce vgreduce
+
+_vgextend()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(L|-size))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -h \
+ -? --help -t --test -v --verbose --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _volumegroups
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _vgextend vgextend
+
+_vgport()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a --all -d --debug -h \
+ -? --help -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgport vgimport vgexport
+
+_vgck()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h \
+ -? --help -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgck vgck
+
+_vgconvert()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(M|-metadatatype))
+ COMPREPLY=( $( compgen -W '1 2' -- $cur ) )
+ return 0
+ ;;
+ --metadatacopies)
+ COMPREPLY=( $( compgen -W '0 1 2' -- $cur ) )
+ return 0
+ ;;
+ --metadatasize)
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h --help --labelsector \
+ -M --metadatatype --metadatacopies --metadatasize \
+ -t --test -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgconvert vgconvert
+
+_vgcfgbackup()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(f|-file))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -f --file -h --help \
+ --ignorelockingfailure -P --partial -v --verbose \
+ --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgcfgbackup vgcfgbackup
+
+_vgcfgrestore()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(f|-file))
+ _filedir
+ return 0
+ ;;
+ -@(M|-metadatatype))
+ COMPREPLY=( $( compgen -W '1 2' -- $cur ) )
+ return 0
+ ;;
+ -@(n|-name))
+ _volumegroups
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -f --file -l --list \
+ -h --help -M --Metadatatype -n --name -t --test \
+ -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgcfgrestore vgcfgrestore
+
+_vgmerge()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug \
+ -h --help -l --list -t --test -v --verbose \
+ --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgmerge vgmerge
+
+_vgsplit()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(M|-metadatatype))
+ COMPREPLY=( $( compgen -W '1 2' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug \
+ -h --help -l --list -M --metadatatype -t --test \
+ -v --verbose --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 -o $args -eq 1 ]; then
+ _volumegroups
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _vgsplit vgsplit
+
+_vgmknodes()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h --help -v --verbose \
+ --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgmknodes vgmknodes
+
+_lvscan()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-b --blockdevice -d --debug \
+ -h -? --help --ignorelockingfailure -P \
+ --partial -v --verbose --version' -- $cur ) )
+ fi
+}
+complete -F _lvscan lvscan
+
+_lvs()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(o|O|-options|-sort))
+ COMPREPLY=( $( compgen -W 'lv_uuid lv_name \
+ lv_attr lv_minor lv_size seg_count \
+ origin snap_percent segtype stripes \
+ stripesize chunksize seg_start \
+ seg_size' -- $cur ) )
+ return 0
+ ;;
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--aligned -d --debug \
+ -h --help --ignorelockingfailure --noheadings \
+ --nosuffix -o --options -O --sort -P --partial \
+ --segments --separator --unbuffered --units \
+ -v --verbose --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvs lvs
+
+_lvdisplay()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c --colon -C --columns --units \
+ -P --partial -m --maps -v --verbose -d --debug -h \
+ --help --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvdisplay lvdisplay
+
+_lvchange()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(a|A|C|M|-available|-autobackup|-continguous|-persistent))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(p|-permission))
+ COMPREPLY=( $( compgen -W 'r rw' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -a --available \
+ --addtag --alloc -C --contiguous -d --debug --deltag \
+ -f --force -h --help --ignorelockingfailure -M \
+ --persistent --major major --minor minor -P --partial \
+ -p --permission -r --readahead --refresh -t --test \
+ -v --verbose --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvchange lvchange
+
+_lvcreate()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|C|M|Z|-autobackup|-continguous|-persistent|-zero))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(L|-size))
+ _sizes
+ return 0
+ ;;
+ -@(p|-permission))
+ COMPREPLY=( $( compgen -W 'r rw' -- $cur ) )
+ return 0
+ ;;
+ -@(n|-name))
+ _logicalvolumes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup --addtag --alloc \
+ -C --contiguous -d --debug -h -? --help -i --stripes \
+ -I --stripesize -l --extents -L --size -M --persistent \
+ --major --minor -n --name -p --permission -r \
+ --readahead -t --test --type -v --verbose -Z --zero \
+ --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _volumegroups
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _lvcreate lvcreate
+
+_lvremove()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -f \
+ --force -h -? --help -t --test -v --verbose \
+ --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvremove lvremove
+
+_lvrename()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -h \
+ -? --help -t --test -v --verbose --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvrename lvrename
+
+_lvreduce()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(L|-size))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d \
+ --debug -f --force -h --help -l --extents \
+ -L --size -n --nofsck -r --resizefs -t --test \
+ -v --verbose --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvreduce lvreduce
+
+_lvresize()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(L|-size))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup --alloc -d \
+ --debug -h --help -i --stripes -I --stripesize \
+ -l --extents -L --size -n --nofsck -r --resizefs \
+ -t --test --type -v --verbose --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _logicalvolumes
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _lvresize lvresize
+
+_lvextend()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(L|-size))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup --alloc -d \
+ --debug -h --help -i --stripes -I --stripesize \
+ -l --extents -L --size -n --nofsck -r --resizefs \
+ -t --test --type -v --verbose --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _logicalvolumes
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _lvextend lvextend
+
+_lvm()
+{
+ local prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'dumpconfig help lvchange \
+ lvcreate lvdisplay lvextend lvmchange \
+ lvmdiskscan lvmsadc lvmsar lvreduce \
+ lvremove lvrename lvresize lvs lvscan \
+ pvchange pvcreate pvdata pvdisplay pvmove \
+ pvremove pvresize pvs pvscan vgcfgbackup \
+ vgcfgrestore vgchange vgck vgconvert \
+ vgcreate vgdisplay vgexport vgextend \
+ vgimport vgmerge vgmknodes vgreduce \
+ vgremove vgrename vgs vgscan vgsplit \
+ version' -- $cur ) )
+ else
+ case ${COMP_WORDS[1]} in
+ pvchange)
+ _pvchange
+ ;;
+ pvcreate)
+ _pvcreate
+ ;;
+ pvdisplay)
+ _pvdisplay
+ ;;
+ pvmove)
+ _pvmove
+ ;;
+ pvremove)
+ _pvremove
+ ;;
+ pvresize)
+ _pvresize
+ ;;
+ pvs)
+ _pvs
+ ;;
+ pvscan)
+ _pvscan
+ ;;
+ vgcfgbackup)
+ _vgcfgbackup
+ ;;
+ vgcfgrestore)
+ _vgcfgrestore
+ ;;
+ vgchange)
+ _vgchange
+ ;;
+ vgck)
+ _vgck
+ ;;
+ vgconvert)
+ _vgconvert
+ ;;
+ vgcreate)
+ _vgcreate
+ ;;
+ vgdisplay)
+ _vgdisplay
+ ;;
+ vgexport)
+ _vgexport
+ ;;
+ vgextend)
+ _vgextend
+ ;;
+ vgimport)
+ _vgimport
+ ;;
+ vgmerge)
+ _vgmerge
+ ;;
+ vgmknodes)
+ _vgmknodes
+ ;;
+ vgreduce)
+ _vgreduce
+ ;;
+ vgremove)
+ _vgremove
+ ;;
+ vgrename)
+ _vgrename
+ ;;
+ vgs)
+ _vgs
+ ;;
+ vgscan)
+ _vgscan
+ ;;
+ vgsplit)
+ _vgsplit
+ ;;
+ lvchange)
+ _lvchange
+ ;;
+ lvcreate)
+ _lvcreate
+ ;;
+ lvdisplay)
+ _lvdisplay
+ ;;
+ lvextend)
+ _lvextend
+ ;;
+ lvreduce)
+ _lvreduce
+ ;;
+ lvremove)
+ _lvremove
+ ;;
+ lvrename)
+ _lvrename
+ ;;
+ lvresize)
+ _lvresize
+ ;;
+ lvs)
+ _lvs
+ ;;
+ lvscan)
+ _lvscan
+ ;;
+ esac
+ fi
+}
+complete -F _lvm lvm
+}
+
+# mkinitrd(8) completion
+#
+have mkinitrd &&
+_mkinitrd()
+{
+ local cur args
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # --name value style option
+ case "$prev" in
+ --preload)
+ _modules
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ --@(with|builtin))
+ _modules
+ return 0
+ ;;
+ --@(fstab|dsdt))
+ _filedir
+ return 0
+ ;;
+ --tmpdir)
+ _filedir -d
+ return 0
+ ;;
+ esac
+ fi
+
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--version -v -f --preload \
+ --with= --omit-scsi-modules --omit-raid-modules \
+ --images-version --fstab= --nocompress --builtin= \
+ --nopivot --noudev --allow-missing --tmpdir= \
+ --initrdfs= --dsdt= --lvm-version= --froce-usb' \
+ -- $cur ) )
+ else
+ _count_args
+
+ case $args in
+ 1)
+ _filedir
+ ;;
+ 2)
+ COMPREPLY=( $( command ls /lib/modules | grep "^$cur" ) )
+ ;;
+ esac
+ fi
+
+} &&
+complete -F _mkinitrd mkinitrd
+
+# pkgconfig(1) completion
+#
+have pkg-config &&
+_pkg_config()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ # return list of available options
+ COMPREPLY=( $( compgen -W '-version --modversion \
+ --atleast-pkgconfig-version= --libs --libs-only-l \
+ --libs-only-other --libs-only-L --cflags \
+ --cflags-only-I --cflags-only-other --variable= \
+ --define-variable= --exists --uninstalled \
+ --atleast-version= --exact-version= --max-version= \
+ --list-all --debug --print-errors --silence-errors \
+ --errors-to-stdout -? --help --usage' -- $cur))
+ else
+ COMPREPLY=( $( pkg-config --list-all 2>/dev/null | \
+ awk '{print $1}' | grep "^$cur" ) )
+ fi
+} &&
+complete -F _pkg_config pkg-config
+
+
+# cpio(1) completion
+#
+have cpio && {
+_cpio_format()
+{
+ COMPREPLY=( $( compgen -W 'bin odc newc crc tar ustar hpbin hpodc' -- $cur ) )
+}
+
+_cpio()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # --name value style option
+ case $prev in
+ -H)
+ _cpio_format
+ return 0
+ ;;
+ -@(E|F|I))
+ _filedir
+ return 0
+ ;;
+ -R)
+ _usergroup
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case $prev in
+ --format)
+ _cpio_format
+ return 0
+ ;;
+ --@(file|pattern-file))
+ _filedir
+ return 0
+ ;;
+ --owner)
+ _usergroup
+ return 0
+ ;;
+ --rsh-command)
+ COMPREPLY=( $( compgen -c -- $cur ) )
+ return 0
+ ;;
+ esac
+ fi
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W '-o --create -i --extract -p --pass-through' -- $cur) )
+ else
+ case ${COMP_WORDS[1]} in
+ -@(o|-create))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-0 -a -c -v -A -B\
+ -L -V -C -H -M -O -F --file= --format=\
+ --message= --null --reset-access-time\
+ --verbose --dot --append --block-size=\
+ --dereference --io-size= --quiet\
+ --force-local --rsh-command= --help\
+ --version' -- $cur ) )
+ fi
+ ;;
+ -@(i|-extract))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-b -c -d -f -m -n -r\
+ -t -s -u -v -B -S -V -C -E -H -M -R -I\
+ -F --file= --make-directories\
+ --nonmatching\
+ --preserve-modification-time\
+ --numeric-uid-gid --rename -t --list\
+ --swap-bytes --swap --dot\
+ --unconditional --verbose --block-size=\
+ --swap-halfwords --io-size=\
+ --pattern-file= --format= --owner=\
+ --no-preserve-owner --message=\
+ --force-local --no-absolute-filenames\
+ --sparse --only-verify-crc --quiet\
+ --rsh-command= --help\
+ --version' -- $cur ) )
+ fi
+ ;;
+ -@(p|-pass-through))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-0 -a -d -l -m -u -v\
+ -L -V -R --null --reset-access-time\
+ --make-directories --link --quiet\
+ --preserve-modification-time\
+ --unconditional --verbose --dot\
+ --dereference --owner=\
+ --no-preserve-owner --sparse --help\
+ --version' -- $cur ) )
+ else
+ _filedir -d
+ fi
+ ;;
+ esac
+ fi
+}
+complete -F _cpio cpio
+}
+
+# id(1) completion
+#
+have id &&
+_id()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a -g --group -G --groups -n --name\
+ -r --real -u --user --help --version' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -u $cur ) )
+ fi
+} &&
+complete -F _id id
+
+# getent(1) completion
+#
+have getent &&
+_getent()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ passwd)
+ COMPREPLY=( $( compgen -u $cur ) )
+ return 0
+ ;;
+ group)
+ COMPREPLY=( $( compgen -g $cur ) )
+ return 0
+ ;;
+ services)
+ COMPREPLY=( $( compgen -s $cur ) )
+ return 0
+ ;;
+ hosts)
+ COMPREPLY=( $( compgen -A hostname $cur ) )
+ return 0
+ ;;
+ protocols)
+ COMPREPLY=( $( getent protocols | awk '{print $1}' | grep "^$cur" ) )
+ return 0
+ ;;
+ networks)
+ COMPREPLY=( $( getent networks | awk '{print $1}' | grep "^$cur" ) )
+ return 0
+ ;;
+ esac
+
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'passwd group hosts services protocols networks' -- $cur ) )
+ fi
+} &&
+complete -F _getent getent
+
+# ntpdate(1) completion
+#
+have ntpdate &&
+_ntpdate()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -k)
+ _filedir
+ return 0
+ ;;
+ -U)
+ COMPREPLY=( $( compgen -u $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-4 -6 -b -B -d -Q -q -s -u -v -a\
+ -e -k -p -o -r -t' -- $cur ) )
+ else
+ _known_hosts
+ fi
+} &&
+complete -F _ntpdate ntpdate
+
+# smartctl(8) completion
+#
+have smartctl && {
+_smartctl_quietmode()
+{
+ COMPREPLY=( $( compgen -W 'errorsonly silent' -- $cur ) )
+}
+_smartctl_device()
+{
+ COMPREPLY=( $( compgen -W 'ata scsi 3ware' -- $cur ) )
+}
+_smartctl_tolerance()
+{
+ COMPREPLY=( $( compgen -W 'warn exit ignore' -- $cur ) )
+}
+_smartctl_badsum()
+{
+ COMPREPLY=( $( compgen -W 'normal conservative permissive verypermissive' -- $cur ) )
+}
+_smartctl_report()
+{
+ COMPREPLY=( $( compgen -W 'ioctl ataioctl scsiioctl' -- $cur ) )
+}
+_smartctl_feature()
+{
+ COMPREPLY=( $( compgen -W 'on off' -- $cur ) )
+}
+_smartctl_log()
+{
+ COMPREPLY=( $( compgen -W 'error selftest selective directory' -- $cur ) )
+}
+_smartctl_vendorattribute()
+{
+ COMPREPLY=( $( compgen -W 'help 9,minutes 9,seconds 9,halfminutes \
+ 9,temp 192,emergencyretractcyclect 193,loadunload \
+ 194,10xCelsius 194,unknown 198,offlinescanuncsectorct \
+ 200,writeerrorcount 201,detectedtacount 220,temp' -- $cur ) )
+}
+_smartctl_firmwarebug()
+{
+ COMPREPLY=( $( compgen -W 'none samsung samsung2' -- $cur ) )
+}
+_smartctl_presets()
+{
+ COMPREPLY=( $( compgen -W 'use ignore show showall' -- $cur ) )
+}
+_smartctl_test()
+{
+ COMPREPLY=( $( compgen -W 'offline short long conveyance select afterselect,on afterselect,off pending' -- $cur ) )
+}
+
+_smartctl()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # --name value style option
+ case "$prev" in
+ -q)
+ _smartctl_quietmode
+ ;;
+ -d)
+ _smartctl_device
+ return 0
+ ;;
+ -t)
+ _smartctl_tolerance
+ return 0
+ ;;
+ -b)
+ _smartctl_badsum
+ return 0
+ ;;
+ -r)
+ _smartctl_report
+ return 0
+ ;;
+ -s)
+ _smartctl_feature
+ return 0
+ ;;
+ -o)
+ _smartctl_feature
+ return 0
+ ;;
+ -S)
+ _smartctl_feature
+ return 0
+ ;;
+ -l)
+ _smartctl_log
+ return 0
+ ;;
+ -v)
+ _smartctl_vendorattribute
+ return 0
+ ;;
+ -F)
+ _smartctl_firmwarebug
+ return 0
+ ;;
+ -P)
+ _smartctl_presets
+ return 0
+ ;;
+ -t)
+ _smartctl_test
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ --quietmode)
+ _smartctl_quietmode
+ return 0
+ ;;
+ --device)
+ _smartctl_device
+ return 0
+ ;;
+ --tolerance)
+ _smartctl_tolerance
+ return 0
+ ;;
+ --badsum)
+ _smartctl_badsum
+ return 0
+ ;;
+ --report)
+ _smartctl_report
+ return 0
+ ;;
+ --smart)
+ _smartctl_feature
+ return 0
+ ;;
+ --offlineauto)
+ _smartctl_feature
+ return 0
+ ;;
+ --saveauto)
+ _smartctl_feature
+ return 0
+ ;;
+ --log)
+ _smartctl_log
+ return 0
+ ;;
+ --vendorattribute)
+ _smartctl_vendorattribute
+ return 0
+ ;;
+ --firmwarebug)
+ _smartctl_firmwarebug
+ return 0
+ ;;
+ --presets)
+ _smartctl_presets
+ return 0
+ ;;
+ --test)
+ _smartctl_test
+ return 0
+ ;;
+ esac
+ fi
+
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-h --help --usage -V --version \
+ --copyright --license-i --info -a --all -q \
+ --quietmode= -d --device= -T --tolerance= -b --badsum= \
+ -r --report= -s --smart= -o --offlineauto= -S \
+ --saveauto= -H --health -c --capabilities -A \
+ --attributes -l --log= -v --vendorattribute= -F \
+ --firmwarebug= -P --presets= -t --test= -C \
+ --captive -X --abort' -- $cur ) )
+ else
+ cur=${cur:=/dev/}
+ _filedir
+ fi
+}
+complete -F _smartctl smartctl
+}
+
+# vncviewer(1) completion
+#
+have vncviewer &&
+_vncviewer()
+{
+ local cur prev
+ local -a config
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -via)
+ _known_hosts -a
+ ;;
+ *)
+ # ssh into the the server, find and ping the broadcast address, then
+ # sort and show the results.
+ COMPREPLY=( $( ssh -o 'Batchmode yes' $prev \
+ "ping -bnc 4 255.255.255.255" 2>/dev/null | \
+ awk -F ' ' '{print $4}' | \
+ sort -n | uniq | egrep '[0-9]+\.[0-9]+\.' 2>/dev/null ) )
+ esac
+
+ return 0
+} &&
+complete -F _vncviewer vncviewer
+
+# sysctl(8) completion
+#
+have sysctl &&
+_sysctl()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( compgen -W "$(sysctl -N -a 2>/dev/null)" -- $cur ) )
+
+ return 0
+} &&
+complete -F _sysctl sysctl
+
+# update-rc.d(8) completion
+#
+# Copyright (C) 2004 Servilio Afre Puentes <servilio@gmail.com>
+#
+have update-rc.d &&
+_update_rc_d()
+{
+ local cur prev sysvdir services options valid_options
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d \
+ || sysvdir=/etc/init.d
+
+ services=( $(echo $sysvdir/!(README*|*.sh|*.dpkg*|*.rpm*)) )
+ services=( ${services[@]#$sysvdir/} )
+ options=( -f -n )
+
+ if [[ $COMP_CWORD -eq 1 || "$prev" == -* ]]; then
+ valid_options=( $( \
+ echo "${COMP_WORDS[@]} ${options[@]}" \
+ | tr " " "\n" \
+ | sed -ne "/$( echo "${options[@]}" | sed "s/ /\\|/g" )/p" \
+ | sort | uniq -u \
+ ) )
+ COMPREPLY=( $( compgen -W '${options[@]} ${services[@]}' \
+ -X '$( echo ${COMP_WORDS[@]} | tr " " "|" )' -- $cur ) )
+ elif [[ "$prev" == ?($( echo ${services[@]} | tr " " "|" )) ]]; then
+ COMPREPLY=( $( compgen -W 'remove defaults start stop' -- $cur ) )
+ elif [[ "$prev" == defaults && "$cur" == [0-9] ]]; then
+ COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 )
+ elif [[ "$prev" == defaults && "$cur" == [sk]?([0-9]) ]]; then
+ COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 )
+ elif [[ "$prev" == defaults && -z "$cur" ]]; then
+ COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 s k )
+ elif [[ "$prev" == ?(start|stop) ]]; then
+ if [[ "$cur" == [0-9] || -z "$cur" ]]; then
+ COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 )
+ elif [[ "$cur" == [0-9][0-9] ]]; then
+ COMPREPLY=( $cur )
+ else
+ COMPREPLY=()
+ fi
+ elif [[ "$prev" == ?([0-9][0-9]|[0-6S]) ]]; then
+ if [[ -z "$cur" ]]; then
+ if [[ $prev == [0-9][0-9] ]]; then
+ COMPREPLY=( 0 1 2 3 4 5 6 S )
+ else
+ COMPREPLY=( 0 1 2 3 4 5 6 S . )
+ fi
+ elif [[ "$cur" == [0-6S.] ]]; then
+ COMPREPLY=( $cur )
+ else
+ COMPREPLY=()
+ fi
+ elif [[ "$prev" == "." ]]; then
+ COMPREPLY=( $(compgen -W "start stop" -- $cur) )
+ else
+ COMPREPLY=()
+ fi
+
+ return 0
+} &&
+complete -F _update_rc_d update-rc.d
+
+# invoke-rc.d(8) completion
+#
+# Copyright (C) 2004 Servilio Afre Puentes <servilio@gmail.com>
+#
+have invoke-rc.d &&
+_invoke_rc_d()
+{
+ local cur prev sysvdir services options valid_options
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d \
+ || sysvdir=/etc/init.d
+
+ services=( $(echo $sysvdir/!(README*|*.sh|*.dpkg*|*.rpm*)) )
+ services=( ${services[@]#$sysvdir/} )
+ options=( --help --quiet --force --try-anyway --disclose-deny --query --no-fallback )
+
+ if [[ ($COMP_CWORD -eq 1) || ("$prev" == --* ) ]]; then
+ valid_options=( $( \
+ echo ${COMP_WORDS[@]} ${options[@]} \
+ | tr " " "\n" \
+ | sed -ne "/$( echo ${options[@]} | sed "s/ /\\\\|/g" )/p" \
+ | sort | uniq -u \
+ ) )
+ COMPREPLY=( $( compgen -W '${valid_options[@]} ${services[@]}' -- \
+ $cur ) )
+ elif [ -x $sysvdir/$prev ]; then
+ COMPREPLY=( $( compgen -W '`sed -ne "y/|/ /; \
+ s/^.*Usage:[ ]*[^ ]*[ ]*{*\([^}\"]*\).*$/\1/p" \
+ $sysvdir/$prev`' -- \
+ $cur ) )
+ else
+ COMPREPLY=()
+ fi
+
+ return 0
+} &&
+complete -F _invoke_rc_d invoke-rc.d
+
+# minicom(1) completion
+#
+have minicom &&
+_minicom()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -@(a|c))
+ COMPREPLY=( $( compgen -W 'on off' -- $cur ) )
+ return 0
+ ;;
+ -@(S|C))
+ _filedir
+ return 0
+ ;;
+ -P)
+ COMPREPLY=( $( command ls /dev/tty* ) )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]} ${COMPREPLY[@]#/dev/}' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-s -o -m -M -z -l -L -w -a -t \
+ -c -S -d -p -C -T -8' -- $cur ) )
+ else
+ COMPREPLY=( $( command ls /etc/minicom/minirc.* 2>/dev/null | sed -e 's|/etc/minicom/minirc.||' | grep "^$cur" ) )
+ fi
+} &&
+complete -F _minicom minicom
+
+# svn completion
+#
+have svn &&
+{
+_svn()
+{
+ local cur prev commands options command
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ commands='add blame praise annotate ann cat checkout co cleanup commit \
+ ci copy cp delete del remove rm diff di export help ? h import \
+ info list ls lock log merge mkdir move mv rename ren \
+ propdel pdel pd propedit pedit pe propget pget pg \
+ proplist plist pl propset pset ps resolved revert \
+ status stat st switch sw unlock update up'
+
+ if [[ $COMP_CWORD -eq 1 ]] ; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--version' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ fi
+ else
+
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ case $prev in
+ --config-dir)
+ _filedir -d
+ return 0;
+ ;;
+ -@(F|-file|-targets))
+ _filedir
+ return 0;
+ ;;
+ --encoding)
+ COMPREPLY=( $( compgen -W \
+ '$( iconv --list | sed -e "s@//@@;" )' \
+ -- "$cur" ) )
+ return 0;
+ ;;
+ --@(editor|diff|diff3)-cmd)
+ COMP_WORDS=(COMP_WORDS[0] $cur)
+ COMP_CWORD=1
+ _command
+ return 0;
+ ;;
+ esac
+
+ command=${COMP_WORDS[1]}
+
+ if [[ "$cur" == -* ]]; then
+ # possible options for the command
+ case $command in
+ add)
+ options='--auto-props --no-auto-props \
+ --force --targets --no-ignore \
+ --non-recursive -N -q --quiet'
+ ;;
+ @(blame|annotate|ann|praise))
+ options='-r --revisions --username \
+ --password --no-auth-cache \
+ --non-interactive -v \
+ --verbose --incremental --xml'
+ ;;
+ cat)
+ options='-r --revision --username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(checkout|co))
+ options='-r --revision -q --quiet -N \
+ --non-recursive --username \
+ --password --no-auth-cache \
+ --non-interactive \
+ --ignore-externals'
+ ;;
+ cleanup)
+ options='--diff3-cmd'
+ ;;
+ @(commit|ci))
+ options='-m --message -F --file \
+ --encoding --force-log -q \
+ --quiet --non-recursive -N \
+ --targets --editor-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive --no-unlock'
+ ;;
+ @(copy|cp))
+ options='-m --message -F --file \
+ --encoding --force-log -r \
+ --revision -q --quiet \
+ --editor-cmd -username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(delete|del|remove|rm))
+ options='--force -m --message -F \
+ --file --encoding --force-log \
+ -q --quiet --targets \
+ --editor-cmd -username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(diff|di))
+ options='-r --revision -x --extensions \
+ --diff-cmd --no-diff-deleted \
+ -N --non-recursive --username \
+ --password --no-auth-cache \
+ --non-interactive --force \
+ --old --new --notice-ancestry'
+ ;;
+ export)
+ options='-r --revision -q --quiet \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive -N \
+ --non-recursive --force \
+ --native-eol --ignore-externals'
+ ;;
+ import)
+ options='--auto-props --no-auto-props \
+ -m --message -F --file \
+ --encoding --force-log -q \
+ --quiet --non-recursive \
+ --no-ignore --editor-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive'
+ ;;
+ info)
+ options='--username --password \
+ --no-auth-cache \
+ --non-interactive -r \
+ --revision --xml --targets \
+ -R --recursive --incremental'
+ ;;
+ @(list|ls))
+ options='-r --revision -v --verbose -R \
+ --recursive --username \
+ --password --no-auth-cache \
+ --non-interactive \
+ --incremental --xml'
+ ;;
+ lock)
+ options='-m --message -F --file \
+ --encoding --force-log \
+ --targets --force --username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ log)
+ options='-r --revision -v --verbose \
+ --targets --username \
+ --password --no-auth-cache \
+ --non-interactive \
+ --stop-on-copy --incremental \
+ --xml -q --quiet --limit'
+ ;;
+ merge)
+ options='-r --revision -N \
+ --non-recursive -q --quiet \
+ --force --dry-run --diff3-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive \
+ --ignore-ancestry'
+ ;;
+ mkdir)
+ options='-m --message -F --file \
+ --encoding --force-log -q \
+ --quiet --editor-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(move|mv|rename|ren))
+ options='-m --message -F --file \
+ --encoding --force-log -r \
+ --revision -q --quiet \
+ --force --editor-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(propdel|pdel|pd))
+ options='-q --quiet -R --recursive -r \
+ --revision --revprop \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(propedit|pedit|pe))
+ options='-r --revision --revprop \
+ --encoding --editor-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive --force'
+ ;;
+ @(propget|pget|pg))
+ options='-R --recursive -r --revision \
+ --revprop --strict --username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(proplist|plist|pl))
+ options='-v --verbose -R --recursive \
+ -r --revision --revprop -q \
+ --quiet --username --password \
+ --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(propset|pset|ps))
+ options='-F --file -q --quiet \
+ --targets -R --recursive \
+ --revprop --encoding \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive -r \
+ --revision --force'
+ ;;
+ resolved)
+ options='--targets -R --recursive -q \
+ --quiet'
+ ;;
+ revert)
+ options='--targets -R --recursive -q \
+ --quiet'
+ ;;
+ @(status|stat|st))
+ options='-u --show-updates -v \
+ --verbose -N --non-recursive \
+ -q --quiet --username \
+ --password --no-auth-cache \
+ --non-interactive --no-ignore \
+ --ignore-externals \
+ --incremental --xml'
+ ;;
+ @(switch|sw))
+ options='--relocate -r --revision -N \
+ --non-recursive -q --quiet \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive --diff3-cmd'
+ ;;
+ unlock)
+ options='--targets --force --username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(update|up))
+ options='-r --revision -N \
+ --non-recursive -q --quiet \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive \
+ --diff3-cmd --ignore-externals'
+ ;;
+ esac
+ options="$options --help -h --config-dir"
+
+ COMPREPLY=( $( compgen -W "$options" -- $cur ) )
+ else
+ if [[ "$command" == @(help|h|\?) ]]; then
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ else
+ _filedir
+ fi
+ fi
+ fi
+
+ return 0
+}
+complete -F _svn $default svn
+
+_svnadmin()
+{
+ local cur prev commands options mode
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ commands='create deltify dump help ? hotcopy list-dblogs \
+ list-unused-dblogs load lslocks lstxns recover rmlocks \
+ rmtxns setlog verify'
+
+ if [[ $COMP_CWORD -eq 1 ]] ; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--version' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ fi
+ else
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ case $prev in
+ --config-dir)
+ _filedir -d
+ return 0;
+ ;;
+ --fs-type)
+ COMPREPLY=( $( compgen -W 'fsfs bdb' -- $cur ) )
+ return 0;
+ ;;
+ esac
+
+ command=${COMP_WORDS[1]}
+
+ if [[ "$cur" == -* ]]; then
+ # possible options for the command
+ case $command in
+ create)
+ options='--bdb-txn-nosync \
+ --bdb-log-keep --config-dir \
+ --fs-type'
+ ;;
+ deltify)
+ options='-r --revision -q --quiet'
+ ;;
+ dump)
+ options='-r --revision --incremental \
+ -q --quiet --deltas'
+ ;;
+ hotcopy)
+ options='--clean-logs'
+ ;;
+ load)
+ options='--ignore-uuid --force-uuid \
+ --parent-dir -q --quiet \
+ --use-pre-commit-hook \
+ --use-post-commit-hook'
+ ;;
+ rmtxns)
+ options='-q --quiet'
+ ;;
+ setlog)
+ options='-r --revision --bypass-hooks'
+ ;;
+ esac
+
+ options="$options --help -h"
+ COMPREPLY=( $( compgen -W "$options" -- $cur ) )
+ else
+ if [[ "$command" == @(help|h|\?) ]]; then
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ else
+ _filedir
+ fi
+ fi
+ fi
+
+ return 0
+}
+complete -F _svnadmin $default svnadmin
+
+_svnlook()
+{
+ local cur prev commands options mode
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ commands='author cat changed date diff dirs-changed help ? h history \
+ info lock log propget pget pg proplist plist pl tree uuid \
+ youngest'
+
+ if [[ $COMP_CWORD -eq 1 ]] ; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--version' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ fi
+ else
+ command=${COMP_WORDS[1]}
+
+ if [[ "$cur" == -* ]]; then
+ # possible options for the command
+ case $command in
+ @(author|cat|date|dirs-changed|info|log))
+ options='-r --revision -t \
+ --transaction'
+ ;;
+ changed)
+ options='-r --revision -t \
+ --transaction --copy-info'
+ ;;
+ diff)
+ options='-r --revision -t \
+ --transaction \
+ --no-diff-deleted \
+ --no-diff-added \
+ --diff-copy-from'
+ ;;
+ history)
+ options='-r --revision --show-ids'
+ ;;
+ prop@(get|list))
+ options='-r --revision -t \
+ --transaction --revprop'
+ ;;
+ tree)
+ options='-r --revision -t \
+ --transaction --show-ids \
+ --full-paths'
+ ;;
+ esac
+
+ options="$options --help -h"
+ COMPREPLY=( $( compgen -W "$options" -- $cur ) )
+ else
+ if [[ "$command" == @(help|h|\?) ]]; then
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ else
+ _filedir
+ fi
+ fi
+ fi
+
+ return 0
+}
+complete -F _svnlook $default svnlook
+}
+
+_filedir_xspec()
+{
+ local IFS cur xspec
+
+ IFS=$'\t\n'
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _expand || return 0
+
+ # get first exclusion compspec that matches this command
+ xspec=$( sed -ne $'/^complete .*[ \t]'${1##*/}$'\([ \t]\|$\)/{p;q;}' \
+ $BASH_COMPLETION )
+ # prune to leave nothing but the -X spec
+ xspec=${xspec#*-X }
+ xspec=${xspec%% *}
+
+ local toks=( ) tmp
+
+ while read -r tmp; do
+ [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp
+ done < <( compgen -d -- "$(quote_readline "$cur")" )
+
+ while read -r tmp; do
+ [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp
+ done < <( eval compgen -f -X $xspec -- "\$(quote_readline "\$cur")" )
+
+ COMPREPLY=( "${toks[@]}" )
+}
+list=( $( sed -ne '/^# START exclude/,/^# FINISH exclude/p' \
+ $BASH_COMPLETION | \
+ # read exclusion compspecs
+ (
+ while read line
+ do
+ # ignore compspecs that are commented out
+ if [ "${line#\#}" != "$line" ]; then continue; fi
+ line=${line%# START exclude*}
+ line=${line%# FINISH exclude*}
+ line=${line##*\'}
+ list=( "${list[@]}" $line )
+ done
+ echo "${list[@]}"
+ )
+ ) )
+# remove previous compspecs
+if [ ${#list[@]} -gt 0 ]; then
+ eval complete -r ${list[@]}
+ # install new compspecs
+ eval complete -F _filedir_xspec $filenames "${list[@]}"
+fi
+unset list
+
+# source completion directory definitions
+if [ -d $BASH_COMPLETION_DIR -a -r $BASH_COMPLETION_DIR -a \
+ -x $BASH_COMPLETION_DIR ]; then
+ for i in $BASH_COMPLETION_DIR/*; do
+ [[ ${i##*/} != @(*~|*.bak|*.swp|\#*\#|*.dpkg*|.rpm*) ]] &&
+ [ \( -f $i -o -h $i \) -a -r $i ] && . $i
+ done
+fi
+unset i
+
+# source user completion file
+[ $BASH_COMPLETION != ~/.bash_completion -a -r ~/.bash_completion ] \
+ && . ~/.bash_completion
+unset -f have
+unset UNAME RELEASE default dirnames filenames have nospace bashdefault \
+ plusdirs
+
+set $BASH_COMPLETION_ORIGINAL_V_VALUE
+unset BASH_COMPLETION_ORIGINAL_V_VALUE
+
+### Local Variables:
+### mode: shell-script
+### End:
diff --git a/usr/src/cmd/nsadmin/bash/bash_completion.d/dladm b/usr/src/cmd/nsadmin/bash/bash_completion.d/dladm
new file mode 100644
index 0000000000..179227b2a2
--- /dev/null
+++ b/usr/src/cmd/nsadmin/bash/bash_completion.d/dladm
@@ -0,0 +1,34 @@
+_dladm()
+{
+ local cur prev opts base
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+ if [[ ${prev} == 'dladm' ]]; then
+ local cmds="rename-link show-link create-aggr delete-aggr add-aggr \
+ remove-aggr modify-aggr show-aggr scan-wifi connect-wifi \
+ disconnect-wifi show-wifi set-linkprop reset-linkprop \
+ show-linkprop show-ether create-secobj delete-secobj show-secobj \
+ create-vlan delete-vlan show-vlan create-iptun delete-iptun \
+ modify-iptun show-iptun delete-phys show-phys create-vnic \
+ delete-vnic show-vnic create-part delete-part show-part show-ib \
+ create-etherstub delete-etherstub show-etherstub create-bridge \
+ modify-bridge delete-bridge add-bridge remove-bridge show-bridge \
+ show-bridge show-usage"
+
+ COMPREPLY=( $(compgen -W "$cmds" -- ${cur}) )
+ # The -z takes a zone option, ignore everything else
+ elif [[ ${prev} =~ -.*z$ ]]; then
+ local zones=$(zoneadm list -c | grep -v '^global$')
+ COMPREPLY=( $(compgen -W "${zones}" -- ${cur}) )
+ elif [[ ${prev} =~ 'delete-vnic' ]]; then
+ local vnics=$(dladm show-vnic -p -o link)
+ COMPREPLY=( $(compgen -W "${vnics}" -- ${cur}) )
+ elif [[ ${prev} =~ 'delete-etherstub' ]]; then
+ local stubs=$(dladm show-etherstub -p)
+ COMPREPLY=( $(compgen -W "${stubs}" -- ${cur}) )
+ fi
+}
+
+complete -F _dladm dladm
diff --git a/usr/src/cmd/nsadmin/bash/bash_completion.d/machines b/usr/src/cmd/nsadmin/bash/bash_completion.d/machines
new file mode 100644
index 0000000000..05de1b75a4
--- /dev/null
+++ b/usr/src/cmd/nsadmin/bash/bash_completion.d/machines
@@ -0,0 +1,19 @@
+_machine()
+{
+ local cur prev opts base
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+ if [[ ${prev} == 'machine-json' ]]; then
+ machines=$(zoneadm list -cp | grep -v ':global:' | cut -d':' -f2,5 | tr ':' '\n' | sort | uniq)
+ COMPREPLY=( $(compgen -W "${machines}" -- ${cur}) )
+ else
+ # Just expand files by default
+ COMPREPLY=( $(compgen -f -- ${cur}) )
+ fi
+
+ return 0
+}
+
+complete -F _machine machine-json
diff --git a/usr/src/cmd/nsadmin/bash/bash_completion.d/vms b/usr/src/cmd/nsadmin/bash/bash_completion.d/vms
new file mode 100644
index 0000000000..71bcac5994
--- /dev/null
+++ b/usr/src/cmd/nsadmin/bash/bash_completion.d/vms
@@ -0,0 +1,62 @@
+_vmadm()
+{
+ local cur prev opts base
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+ up_patterns=$(printf " running *\$\n halting *\$\n")
+
+ if [[ ${prev} == 'vmadm' ]]; then
+ COMPREPLY=( $(compgen -W "boot destroy dump kill halt list reboot reset info nmi" -- ${cur}) )
+ else
+ case ${prev} in
+ boot)
+ local not_running=$(vmadm list -v | grep -v "^UUID" | \
+ /usr/xpg4/bin/grep -v -e "${up_patterns}" | cut -d' ' -f1)
+ COMPREPLY=( $(compgen -W "${not_running}" -- ${cur}) )
+ ;;
+ info|kill)
+ local running=$(vmadm list -v | grep -v "^UUID" | \
+ /usr/xpg4/bin/grep -e "${up_patterns}" | cut -d ' ' -f1)
+ COMPREPLY=( $(compgen -W "${running}" -- ${cur}) )
+ ;;
+ halt|reboot|reset|nmi|screenshot)
+ local running=$(vmadm list -v | grep -v "^UUID" | \
+ grep " running *$" | cut -d ' ' -f1)
+ COMPREPLY=( $(compgen -W "${running}" -- ${cur}) )
+ ;;
+ dump|destroy)
+ local all=$(vmadm list)
+ COMPREPLY=( $(compgen -W "${all}" -- ${cur}) )
+ ;;
+ *)
+ # Just expand files by default
+ COMPREPLY=( $(compgen -f -- ${cur}) )
+ ;;
+ esac
+ fi
+
+ return 0
+}
+
+_vmcfg()
+{
+ local cur prev opts base
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+ if [[ ${prev} == 'vmcfg' ]]; then
+ vms=$(vmadm list)
+ COMPREPLY=( $(compgen -W "${vms}" -- ${cur}) )
+ else
+ # Just expand files by default
+ COMPREPLY=( $(compgen -f -- ${cur}) )
+ fi
+
+ return 0
+}
+
+complete -F _vmadm vmadm
+complete -F _vmcfg vmcfg
diff --git a/usr/src/cmd/nsadmin/bash/bash_completion.d/zone_alias b/usr/src/cmd/nsadmin/bash/bash_completion.d/zone_alias
new file mode 100644
index 0000000000..5ae8e1d7c7
--- /dev/null
+++ b/usr/src/cmd/nsadmin/bash/bash_completion.d/zone_alias
@@ -0,0 +1,42 @@
+_zone_alias()
+{
+ local RES alias completed_alias results_arr uuid
+ # Attempt alias -> uuid mapping
+ alias="${COMP_WORDS[COMP_CWORD]}"
+ RES=$(vmadm list -H -o alias,uuid | awk -v alias="$alias" '
+ BEGIN { count = 0; uuid = ""; }
+ $1 == alias { count = 1; aliases[1] = $1; uuid = $2; exit;}
+ $1 ~ "^"alias { count += 1; aliases[count] = $1; uuid = $2; }
+ END { if (count == 1) {
+ print "smartos_alias_completed " aliases[1] " " uuid;
+ } else {
+ for (i=1; i <= count; i++) {
+ print aliases[i]
+ }
+ }
+ }
+ ')
+ results_arr=( $RES )
+ # we use 'smartos_alias_completed' as a flag to determine that
+ # a single alias was matched.
+ if [[ "${results_arr[0]}" == "smartos_alias_completed" ]]; then
+ completed_alias="${results_arr[1]}"
+ uuid="${results_arr[2]}"
+ if [[ -n "$SMARTOS_MULTILINE_ALIAS_COMPLETION" ]]; then
+ tput sc
+ tput bold
+ echo "[completed alias: ${completed_alias}]"
+ tput rc
+ tput sgr0
+ COMPREPLY=$uuid
+ elif [[ "$completed_alias" == "$alias" ]]; then
+ COMPREPLY=$uuid
+ else
+ COMPREPLY=$completed_alias
+ compopt -o nospace
+ fi
+ else
+ COMPREPLY=( $(compgen -W "$RES" -- ${cur}) )
+ fi
+ return 0
+}
diff --git a/usr/src/cmd/nsadmin/bash/bash_completion.d/zones b/usr/src/cmd/nsadmin/bash/bash_completion.d/zones
new file mode 100644
index 0000000000..e4bc5b4155
--- /dev/null
+++ b/usr/src/cmd/nsadmin/bash/bash_completion.d/zones
@@ -0,0 +1,50 @@
+_zlogin()
+{
+ local cur prev opts base
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+ # The -e and -l take non-zone arguments, otherwise complete zone
+ if [[ ${prev} != '-e' ]] && [[ ${prev} != '-l' ]]; then
+ local running=$(zoneadm list | grep -v '^global$')
+ COMPREPLY=( $(compgen -W "${running}" -- ${cur}) )
+ fi
+ if [[ -n "$COMPREPLY" ]]; then
+ return 0
+ fi
+
+ _zone_alias
+ return 0
+}
+
+_dash_z_zone()
+{
+ local cur prev opts base
+ COMPREPLY=()
+ cur="${COMP_WORDS[COMP_CWORD]}"
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+ # The -z takes a zone option, ignore everything else
+ if [[ ${prev} =~ -.*z$ ]]; then
+ local zones=$(zoneadm list -c | grep -v '^global$')
+ COMPREPLY=( $(compgen -W "${zones}" -- ${cur}) )
+ if [[ -n "$COMPREPLY" ]]; then
+ return 0
+ fi
+ _zone_alias
+ fi
+ return 0
+}
+
+complete -F _zlogin zlogin
+complete -F _dash_z_zone zoneadm
+complete -F _dash_z_zone zonecfg
+complete -F _dash_z_zone svcs
+complete -F _dash_z_zone svcadm
+complete -F _dash_z_zone svcprop
+complete -F _dash_z_zone pgrep
+complete -F _dash_z_zone pkill
+complete -F _dash_z_zone ps
+complete -F _dash_z_zone ptree
+complete -F _dash_z_zone wall
diff --git a/usr/src/cmd/nsadmin/bashrc.sh b/usr/src/cmd/nsadmin/bashrc.sh
deleted file mode 100644
index 938b0da67e..0000000000
--- a/usr/src/cmd/nsadmin/bashrc.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#
-# Define default prompt to <username>@<hostname>:<path><"($|#) ">
-# and print '#' for user "root" and '$' for normal users.
-#
-typeset +x PS1="\u@\h:\w\\$ "
diff --git a/usr/src/cmd/nsadmin/dot-bash_profile.sh b/usr/src/cmd/nsadmin/dot-bash_profile.sh
new file mode 100644
index 0000000000..dc732f6814
--- /dev/null
+++ b/usr/src/cmd/nsadmin/dot-bash_profile.sh
@@ -0,0 +1,12 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+#
+# Copyright (c) 2014, Joyent, Inc.
+#
+
+[ -f /root/.profile ] && source /root/.profile
+[ -f /root/.bashrc ] && source /root/.bashrc
diff --git a/usr/src/cmd/nsadmin/dot-bashrc.sh b/usr/src/cmd/nsadmin/dot-bashrc.sh
new file mode 100644
index 0000000000..0f06d70ece
--- /dev/null
+++ b/usr/src/cmd/nsadmin/dot-bashrc.sh
@@ -0,0 +1,57 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+#
+# Copyright (c) 2014, Joyent, Inc.
+#
+
+if [ "$PS1" ]; then
+ mt_tty=$(/usr/bin/tty 2>/dev/null)
+ if [[ $mt_tty =~ ^/dev/term/[abcd] ]]; then
+ # If we're on the serial console, we generally won't know how
+ # big our terminal is. Attempt to ask it using control sequences
+ # and resize our pty accordingly.
+ mt_output=$(/usr/lib/measure_terminal 2>/dev/null)
+ if [[ $? -eq 0 ]]; then
+ eval "$mt_output"
+ else
+ # We could not read the size, but we should set a 'sane'
+ # default as the dimensions of the previous user's terminal
+ # persist on the tty device.
+ export LINES=25
+ export COLUMNS=80
+ fi
+ /usr/bin/stty rows ${LINES} columns ${COLUMNS} 2>/dev/null
+ fi
+ unset mt_output mt_tty
+ shopt -s checkwinsize
+ if [[ -f /.dcinfo ]]; then
+ . /.dcinfo
+ DC_NAME="${SDC_DATACENTER_NAME}"
+ fi
+ if [[ -n "${DC_NAME}" ]]; then
+ PS1="[\u@\h (${DC_NAME}) \w]\\$ "
+ else
+ PS1="[\u@\h \w]\\$ "
+ fi
+ alias ll='ls -lF'
+ alias ls='ls --color=auto'
+ [ -n "${SSH_CLIENT}" ] && export PROMPT_COMMAND='echo -ne "\033]0;${HOSTNAME} \007" && history -a'
+fi
+
+# Load bash completion
+[ -f /etc/bash/bash_completion ] && . /etc/bash/bash_completion
+
+svclog() {
+ if [[ -z "$PAGER" ]]; then
+ PAGER=less
+ fi
+ $PAGER `svcs -L $1`
+}
+
+svclogf() {
+ /usr/bin/tail -f `svcs -L $1`
+}
diff --git a/usr/src/cmd/nsadmin/dot-profile.sh b/usr/src/cmd/nsadmin/dot-profile.sh
index a45e47b58d..fa6fb19714 100644
--- a/usr/src/cmd/nsadmin/dot-profile.sh
+++ b/usr/src/cmd/nsadmin/dot-profile.sh
@@ -1,14 +1,19 @@
#
-# Uncommenting PATH below will place /usr/gnu/bin at front,
-# adds /usr/sbin and /sbin to the end.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
-# export PATH=/usr/gnu/bin:/usr/bin:/usr/sbin:/sbin
+
#
-# Define default prompt to <username>@<hostname>:<path><"($|#) ">
-# and print '#' for user "root" and '$' for normal users.
+# Copyright 2019 Joyent, Inc.
#
-# override default prompt for bash
-# case "$0" in
-# -bash)
-# export PS1="\u@\h:\w\\$ "
-# esac
+
+PATH=/usr/bin:/usr/sbin:/smartdc/bin:/opt/smartdc/bin:/opt/local/bin:/opt/local/sbin:/opt/tools/bin:/opt/tools/sbin:/opt/smartdc/agents/bin
+MANPATH=/usr/share/man:/smartdc/man:/opt/smartdc/man:/opt/local/man:/opt/tools/man
+PAGER=less
+# If pkgsrc-tools is set up and the mozilla-rootcerts package is installed
+# configure the platform curl to use it.
+if [[ -f /opt/tools/share/mozilla-rootcerts/cacert.pem ]]; then
+ CURL_CA_BUNDLE=/opt/tools/share/mozilla-rootcerts/cacert.pem
+fi
+export PATH MANPATH PAGER CURL_CA_BUNDLE
diff --git a/usr/src/cmd/nsadmin/etc-profile.sh b/usr/src/cmd/nsadmin/etc-profile.sh
index c1dfae8eb8..b59a80791b 100644
--- a/usr/src/cmd/nsadmin/etc-profile.sh
+++ b/usr/src/cmd/nsadmin/etc-profile.sh
@@ -21,6 +21,7 @@
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+#
# The profile that all logins get before using their own .profile.
@@ -41,19 +42,10 @@ fi
# -rsh is given its environment in its .profile.
case "$0" in
--bash)
- # set prompt for bash
- PS1="\u@\h:\w\\$ "
- export PS1
- ;;
-esac
-
-case "$0" in
-sh | -ksh | -ksh93 | -jsh | -bash | -zsh)
if [ ! -f .hushlogin ]
then
- /usr/sbin/quota
# Allow the user to break the Message-Of-The-Day only.
trap "trap '' 2" 2
/bin/cat -s /etc/motd
@@ -61,12 +53,12 @@ case "$0" in
/bin/mail -E
case $? in
- 0)
+ 0)
echo "You have new mail."
- ;;
- 2)
+ ;;
+ 2)
echo "You have mail."
- ;;
+ ;;
esac
fi
esac
diff --git a/usr/src/cmd/nsadmin/etc-skel-bashrc.sh b/usr/src/cmd/nsadmin/etc-skel-bashrc.sh
new file mode 100644
index 0000000000..128f6b79d7
--- /dev/null
+++ b/usr/src/cmd/nsadmin/etc-skel-bashrc.sh
@@ -0,0 +1,7 @@
+#
+# Define default prompt to <username>@<hostname>:<path><"($|#) ">
+# and print '#' for user "root" and '$' for normal users.
+#
+PS1='${LOGNAME}@$(/usr/bin/hostname):$(
+ [[ "${LOGNAME}" == "root" ]] && printf "%s" "${PWD/${HOME}/~}# " ||
+ printf "%s" "${PWD/${HOME}/~}\$ ")'
diff --git a/usr/src/cmd/nsadmin/system b/usr/src/cmd/nsadmin/system
index 542788923e..f9663c7db2 100644
--- a/usr/src/cmd/nsadmin/system
+++ b/usr/src/cmd/nsadmin/system
@@ -23,6 +23,7 @@
* SYSTEM SPECIFICATION FILE
*
+*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* It is not recommended to edit this file directly but rather
@@ -34,6 +35,10 @@
* recommendations on naming fragment files.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+*
+* As SmartOS ships with /etc/ off a ramdisk, we don't support using the
+* /etc/system.d mechanism.
+*
* moddir:
*
@@ -108,3 +113,53 @@
*
* set test_module:debug = 0x13
+set ibft_noprobe=1
+
+set noexec_user_stack=1
+set noexec_user_stack_log=1
+set rlim_fd_cur=65536
+
+* Ensure that c-states are disabled
+set idle_cpu_no_deep_c=1
+
+* 10 GbE Tuning
+set ip:ip_squeue_fanout=1
+
+*
+* Machines should take a crash dump and reboot when receiving an NMI
+*
+set pcplusmp:apic_panic_on_nmi=1
+set apix:apic_panic_on_nmi=1
+
+*
+* Don't use multi-threaded fast crash dump or a high compression level
+*
+set dump_plat_mincpu=0
+set dump_bzip2_level=1
+
+*
+* Want additional crash dump metrics
+*
+set dump_metrics_on=1
+
+*
+* The traditional (and essentially entirely brain dead) cfgadm(1M)-centric
+* model of hotpluggin' appears to be basically unnecessary. This tunable
+* enables the system to create device nodes for newly inserted devices
+* automatically. See: usr/src/uts/common/io/sata/impl/sata.c:97
+*
+set sata:sata_auto_online=1
+
+*
+* We want to limit the time spent in any one I/O to 10 seconds for targets
+* that are not optical. This is still a very long time; our queue depth is
+* typically 10 or less, and disks will usually fail a command after 2-3s.
+* So we'd have to have multiple reads of bad sectors queued up to have any
+* chance of timing out. In practice, timeouts occur because of problems with
+* disk controllers or firmware, not media errors, and in those cases it will
+* not help at all to wait longer.
+*
+set sd:sd_io_time=10
+
+* Use hires tick to improve some scheduling latency issues
+set hires_tick=1
diff --git a/usr/src/cmd/nsadmin/zshrc b/usr/src/cmd/nsadmin/zshrc
new file mode 100644
index 0000000000..1e9c83e57f
--- /dev/null
+++ b/usr/src/cmd/nsadmin/zshrc
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "@(#)zshrc 1.1 10/02/08 SMI"
+#
+
+# Turn on the "new" completion system. See zshcompsys(1).
+autoload -Uz compinit
+compinit -i
diff --git a/usr/src/cmd/nscd/Makefile b/usr/src/cmd/nscd/Makefile
index 9103db70ae..9bd4d3e18f 100644
--- a/usr/src/cmd/nscd/Makefile
+++ b/usr/src/cmd/nscd/Makefile
@@ -30,6 +30,7 @@ MANIFEST= name-service-cache.xml
SVCMETHOD= svc-nscd
include ../Makefile.cmd
+include ../Makefile.ctf
ROOTMANIFESTDIR= $(ROOTSVCSYSTEM)
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/Makefile b/usr/src/cmd/pgrep/Makefile
index 67993f0ff2..09fb900da7 100644
--- a/usr/src/cmd/pgrep/Makefile
+++ b/usr/src/cmd/pgrep/Makefile
@@ -22,6 +22,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2019 Joyent, Inc.
+#
PROG = pgrep
PKILLFILE = pkill
@@ -31,13 +33,14 @@ ROOTLINKS = $(ROOTBIN)/$(PKILLFILE)
OBJS = pgrep.o idtab.o psexp.o
SRCS = $(OBJS:.o=.c)
POFILES = $(OBJS:.o=.po)
-lint := LINTFLAGS = -ux
include ../Makefile.cmd
CLOBBERFILES += $(PKILLFILE)
+
CFLAGS += $(CCVERBOSE)
-CERRWARN += -_gcc=-Wno-parentheses
+CSTD = $(CSTD_GNU99)
+
LDLIBS += -luutil -lproject -lcontract
POFILE = ppgrep.po
@@ -69,6 +72,4 @@ $(ROOTLINKS): $(ROOTPROG)
clean:
$(RM) $(OBJS)
-lint: lint_SRCS
-
include ../Makefile.targ
diff --git a/usr/src/cmd/pgrep/idtab.c b/usr/src/cmd/pgrep/idtab.c
index 52d6e88cdb..56db6eab91 100644
--- a/usr/src/cmd/pgrep/idtab.c
+++ b/usr/src/cmd/pgrep/idtab.c
@@ -24,7 +24,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
#include <libintl.h>
#include <string.h>
@@ -61,7 +63,8 @@ idtab_append(idtab_t *idt, idkey_t id)
if (idt->id_nelems >= idt->id_size) {
size = idt->id_size ? idt->id_size * IDTAB_GROW : IDTAB_DEFSIZE;
- if (data = realloc(idt->id_data, sizeof (idkey_t) * size)) {
+ if ((data = realloc(idt->id_data,
+ sizeof (idkey_t) * size)) != NULL) {
idt->id_data = data;
idt->id_size = size;
} else {
diff --git a/usr/src/cmd/pgrep/pgrep.c b/usr/src/cmd/pgrep/pgrep.c
index 4531f11267..0fda6733fb 100644
--- a/usr/src/cmd/pgrep/pgrep.c
+++ b/usr/src/cmd/pgrep/pgrep.c
@@ -24,6 +24,10 @@
*/
/* Copyright (c) 2012 by Delphix. All rights reserved */
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
@@ -59,11 +63,11 @@
#define TEXT_DOMAIN "SYS_TEST"
#endif
-#define OPT_SETB 0x0001 /* Set the bits specified by o_bits */
-#define OPT_CLRB 0x0002 /* Clear the bits specified by o_bits */
-#define OPT_FUNC 0x0004 /* Call the function specified by o_func */
-#define OPT_STR 0x0008 /* Set the string specified by o_ptr */
-#define OPT_CRIT 0x0010 /* Option is part of selection criteria */
+#define OPT_SETB 0x0001 /* Set the bits specified by o_bits */
+#define OPT_CLRB 0x0002 /* Clear the bits specified by o_bits */
+#define OPT_FUNC 0x0004 /* Call the function specified by o_func */
+#define OPT_STR 0x0008 /* Set the string specified by o_ptr */
+#define OPT_CRIT 0x0010 /* Option is part of selection criteria */
#define F_LONG_FMT 0x0001 /* Match against long format cmd */
#define F_NEWEST 0x0002 /* Match only newest pid */
@@ -138,7 +142,7 @@ static optdesc_t g_optdtab[] = {
{ 0, 0, 0, 0 }, /* 'k' */
{ OPT_SETB, F_LONG_OUT, 0, &g_flags }, /* 'l' */
{ 0, 0, 0, 0 }, /* 'm' */
- { OPT_SETB, F_NEWEST, 0, &g_flags }, /* -n */
+ { OPT_SETB, F_NEWEST, 0, &g_flags }, /* -n */
{ OPT_SETB, F_OLDEST, 0, &g_flags }, /* -o */
{ 0, 0, 0, 0 }, /* 'p' */
{ 0, 0, 0, 0 }, /* 'q' */
@@ -173,7 +177,7 @@ static pid_t g_pid; /* Current pid */
static int g_signal = SIGTERM; /* Signal to send */
static void
-print_proc(psinfo_t *psinfo)
+print_proc(psinfo_t *psinfo, char *argv __unused, size_t len __unused)
{
if (g_flags & F_OUTPUT)
(void) printf("%s%d", g_delim, (int)psinfo->pr_pid);
@@ -183,7 +187,7 @@ print_proc(psinfo_t *psinfo)
}
}
-static char *
+static void
mbstrip(char *buf, size_t nbytes)
{
wchar_t wc;
@@ -191,6 +195,7 @@ mbstrip(char *buf, size_t nbytes)
int n;
buf[nbytes - 1] = '\0';
+
p = buf;
while (*p != '\0') {
@@ -212,30 +217,23 @@ mbstrip(char *buf, size_t nbytes)
p += n;
}
}
-
- return (buf);
}
static void
-print_proc_long(psinfo_t *psinfo)
+print_proc_long(psinfo_t *psinfo, char *argv, size_t len)
{
- char *name;
-
- if (g_flags & F_LONG_FMT)
- name = mbstrip(psinfo->pr_psargs, PRARGSZ);
- else
- name = psinfo->pr_fname;
+ mbstrip(argv, len);
if (g_flags & F_OUTPUT)
- (void) printf("%s%5d %s", g_delim, (int)psinfo->pr_pid, name);
+ (void) printf("%s%5d %s", g_delim, (int)psinfo->pr_pid, argv);
else {
- (void) printf("%5d %s", (int)psinfo->pr_pid, name);
+ (void) printf("%5d %s", (int)psinfo->pr_pid, argv);
g_flags |= F_OUTPUT;
}
}
static void
-kill_proc(psinfo_t *psinfo)
+kill_proc(psinfo_t *psinfo, char *argv __unused, size_t len __unused)
{
if (psinfo->pr_pid > 0 && kill(psinfo->pr_pid, g_signal) == -1)
uu_warn(gettext("Failed to signal pid %d"),
@@ -268,6 +266,46 @@ open_proc_dir(const char *dirpath)
return (dirp);
}
+static void
+get_argv(int flags, psinfo_t *ps, char *buf, size_t bufsize)
+{
+ char *path = NULL;
+ ssize_t size = 0;
+ int fd;
+
+ if (!(flags & F_LONG_FMT)) {
+ (void) strlcpy(buf, ps->pr_fname, bufsize);
+ return;
+ }
+
+ if (getenv("SHORT_PSARGS") != NULL) {
+ (void) strlcpy(buf, ps->pr_psargs, bufsize);
+ return;
+ }
+
+ if (asprintf(&path, "%s/%d/cmdline", g_procdir,
+ (int)ps->pr_pid) != -1 && (fd = open(path, O_RDONLY)) != -1) {
+ size = read(fd, buf, bufsize);
+ (void) close(fd);
+ }
+
+ free(path);
+
+ if (size <= 0) {
+ (void) strlcpy(buf, ps->pr_psargs, bufsize);
+ } else {
+ buf[bufsize - 1] = '\0';
+ for (char *cp = buf; cp - buf < size; cp++) {
+ if (*cp == '\0' && (cp - buf) + 1 < size)
+ *cp = ' ';
+ }
+ }
+
+ for (ssize_t i = strlen(buf) - 1; i >= 0 && isspace(buf[i]); i--) {
+ buf[i] = '\0';
+ }
+}
+
#define NEWER(ps1, ps2) \
((ps1.pr_start.tv_sec > ps2.pr_start.tv_sec) || \
(ps1.pr_start.tv_sec == ps2.pr_start.tv_sec && \
@@ -275,9 +313,10 @@ open_proc_dir(const char *dirpath)
static int
scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp,
- void (*funcp)(psinfo_t *))
+ void (*funcp)(psinfo_t *, char *, size_t))
{
char procpath[MAXPATHLEN];
+ char argv[PRMAXARGVLEN] = "";
psinfo_t ps, ops;
dirent_t *dent;
int procfd;
@@ -285,9 +324,6 @@ scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp,
int reverse = (g_flags & F_REVERSE) ? 1 : 0;
int ovalid = 0, nmatches = 0, flags = 0;
- if (g_flags & F_LONG_FMT)
- flags |= PSEXP_PSARGS;
-
if (g_flags & F_EXACT_MATCH)
flags |= PSEXP_EXACT;
@@ -302,12 +338,17 @@ scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp,
if ((procfd = open(procpath, O_RDONLY)) == -1)
continue;
- if ((read(procfd, &ps, sizeof (ps)) == sizeof (psinfo_t)) &&
- (ps.pr_nlwp != 0) && (ps.pr_pid != g_pid) &&
- (psexp_match(psexp, &ps, flags) ^ reverse)) {
+ if (read(procfd, &ps, sizeof (ps)) != sizeof (psinfo_t)) {
+ (void) close(procfd);
+ continue;
+ }
+
+ get_argv(g_flags, &ps, argv, sizeof (argv));
+
+ if ((ps.pr_nlwp != 0) && (ps.pr_pid != g_pid) &&
+ (psexp_match(psexp, &ps, argv, flags) ^ reverse)) {
if (g_flags & F_NEWEST) {
- /* LINTED - opsinfo use ok */
if (!ovalid || NEWER(ps, ops)) {
(void) memcpy(&ops, &ps,
sizeof (psinfo_t));
@@ -320,7 +361,7 @@ scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp,
ovalid = 1;
}
} else {
- (*funcp)(&ps);
+ (*funcp)(&ps, argv, sizeof (argv));
nmatches++;
}
}
@@ -329,7 +370,8 @@ scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp,
}
if ((g_flags & (F_NEWEST | F_OLDEST)) && ovalid) {
- (*funcp)(&ops);
+ (*funcp)(&ops, argv, sizeof (argv));
+
nmatches++;
}
@@ -592,11 +634,13 @@ print_usage(FILE *stream)
int
main(int argc, char *argv[])
{
- void (*funcp)(psinfo_t *);
+ void (*funcp)(psinfo_t *, char *, size_t);
const char *optstr;
optdesc_t *optd;
int nmatches, c;
+ const char *zroot;
+ char buf[PATH_MAX];
DIR *dirp;
@@ -626,6 +670,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/pgrep/psexp.c b/usr/src/cmd/pgrep/psexp.c
index be14393bd1..abb8699523 100644
--- a/usr/src/cmd/pgrep/psexp.c
+++ b/usr/src/cmd/pgrep/psexp.c
@@ -24,7 +24,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
#include <string.h>
#include <stdlib.h>
@@ -110,10 +112,9 @@ psexp_compile(psexp_t *psexp)
psexp->__f1.id_data && !idtab_search(&psexp->__f1, psinfo->__f2)
int
-psexp_match(psexp_t *psexp, psinfo_t *psinfo, int flags)
+psexp_match(psexp_t *psexp, psinfo_t *psinfo, const char *argv, int flags)
{
regmatch_t pmatch;
- const char *s;
if (NOMATCH(ps_euids, pr_euid))
return (0);
@@ -139,14 +140,11 @@ psexp_match(psexp_t *psexp, psinfo_t *psinfo, int flags)
return (0);
if (psexp->ps_pat != NULL) {
- s = (flags & PSEXP_PSARGS) ?
- psinfo->pr_psargs : psinfo->pr_fname;
-
- if (regexec(&psexp->ps_reg, s, 1, &pmatch, 0) != 0)
+ if (regexec(&psexp->ps_reg, argv, 1, &pmatch, 0) != 0)
return (0);
if ((flags & PSEXP_EXACT) &&
- (pmatch.rm_so != 0 || s[pmatch.rm_eo] != '\0'))
+ (pmatch.rm_so != 0 || argv[pmatch.rm_eo] != '\0'))
return (0);
}
diff --git a/usr/src/cmd/pgrep/psexp.h b/usr/src/cmd/pgrep/psexp.h
index 741050cbe4..7de6742311 100644
--- a/usr/src/cmd/pgrep/psexp.h
+++ b/usr/src/cmd/pgrep/psexp.h
@@ -24,11 +24,13 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
#ifndef _PSEXP_H
#define _PSEXP_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <procfs.h>
#include <regex.h>
@@ -39,8 +41,7 @@ extern "C" {
#include "idtab.h"
-#define PSEXP_PSARGS 0x1 /* Match against psargs rather than fname */
-#define PSEXP_EXACT 0x2 /* Match must be exact (entire string) */
+#define PSEXP_EXACT 0x1 /* Match must be exact (entire string) */
typedef struct psexp {
idtab_t ps_euids; /* Table of effective uids to match */
@@ -54,14 +55,14 @@ typedef struct psexp {
idtab_t ps_taskids; /* Table of task ids to match */
idtab_t ps_zoneids; /* Table of zone ids to match */
idtab_t ps_ctids; /* Table of contract ids to match */
- const char *ps_pat; /* Uncompiled fname/psargs regexp pattern */
- regex_t ps_reg; /* Compiled fname/psargs regexp */
+ const char *ps_pat; /* Uncompiled fname/argv regexp pattern */
+ regex_t ps_reg; /* Compiled fname/argv regexp */
} psexp_t;
extern void psexp_create(psexp_t *);
extern void psexp_destroy(psexp_t *);
extern int psexp_compile(psexp_t *);
-extern int psexp_match(psexp_t *, psinfo_t *, int);
+extern int psexp_match(psexp_t *, psinfo_t *, const char *, int);
#ifdef __cplusplus
}
diff --git a/usr/src/cmd/prstat/prstat.c b/usr/src/cmd/prstat/prstat.c
index 0ff4f51bcd..f40219d75a 100644
--- a/usr/src/cmd/prstat/prstat.c
+++ b/usr/src/cmd/prstat/prstat.c
@@ -182,6 +182,33 @@ 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).
@@ -849,9 +876,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);
@@ -1415,6 +1442,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);
@@ -1425,7 +1453,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;
@@ -1505,6 +1533,9 @@ main(int argc, char **argv)
while (p = strtok(NULL, ", "))
add_uid(&ruid_tbl, p);
break;
+ case 'V':
+ /* obsolete argument - accepted for compatability */
+ break;
case 'p':
fill_table(&pid_tbl, optarg, 'p');
break;
@@ -1614,7 +1645,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 e205a98bd2..7d3913f731 100644
--- a/usr/src/cmd/prstat/prstat.h
+++ b/usr/src/cmd/prstat/prstat.h
@@ -73,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 */
+ /* 0x10000 available for re-use */
#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/prtconf/prtconf.c b/usr/src/cmd/prtconf/prtconf.c
index 4ca2dc6364..cf9eb99814 100644
--- a/usr/src/cmd/prtconf/prtconf.c
+++ b/usr/src/cmd/prtconf/prtconf.c
@@ -270,10 +270,11 @@ main(int argc, char *argv[])
sizeof (hw_provider));
/*
* If 0 bytes are returned (the system returns '1', for the \0),
- * we're probably on x86, default to "Unknown Hardware Vendor".
+ * 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, "Unknown Hardware Vendor",
+ (void) strncpy(hw_provider, "Joyent",
sizeof (hw_provider));
}
(void) printf("System Configuration: %s %s\n", hw_provider,
diff --git a/usr/src/cmd/ps/ps.c b/usr/src/cmd/ps/ps.c
index 1a3e91689a..1387a9440a 100644
--- a/usr/src/cmd/ps/ps.c
+++ b/usr/src/cmd/ps/ps.c
@@ -27,7 +27,7 @@
*/
/*
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -64,6 +64,8 @@
#include <sys/pset.h>
#include <project.h>
#include <zone.h>
+#include <assert.h>
+#include <stdbool.h>
#define min(a, b) ((a) > (b) ? (b) : (a))
#define max(a, b) ((a) < (b) ? (b) : (a))
@@ -288,7 +290,7 @@ static int nzoneid = 0;
static int kbytes_per_page;
static int pidwidth;
-static char *procdir = "/proc"; /* standard /proc directory */
+static char procdir[MAXPATHLEN]; /* standard /proc directory */
static struct ughead euid_tbl; /* table to store selected euid's */
static struct ughead ruid_tbl; /* table to store selected real uid's */
@@ -337,10 +339,22 @@ static int pidcmp(const void *p1, const void *p2);
extern int ucbmain(int, char **);
static int stdmain(int, char **);
+/* also used by ucbps.c */
+void get_psargs(bool, bool, psinfo_t *, char *, size_t);
+void print_psargs(char *, int);
+
int
main(int argc, char **argv)
{
const char *me;
+ const char *zroot = zone_get_nroot();
+
+ /*
+ * If this is a branded zone, the native procfs may mounted in a
+ * non-standard location. Apply such a path prefix if it exists.
+ */
+ (void) snprintf(procdir, sizeof (procdir), "%s/proc", zroot != NULL ?
+ zroot : "");
/*
* The original two ps'es are linked in a single binary;
@@ -1362,14 +1376,12 @@ prfind(int found, psinfo_t *psinfo, char **tpp)
static void
prcom(psinfo_t *psinfo, char *ttyp)
{
- char *cp;
- long tm;
- int bytesleft;
- int wcnt, length;
- wchar_t wchar;
+ long tm;
+ int wcnt;
struct passwd *pwd;
- int zombie_lwp;
- char zonename[ZONENAME_MAX];
+ int zombie_lwp;
+ char zonename[ZONENAME_MAX];
+ char psargs[PRMAXARGVLEN] = "";
/*
* If process is zombie, call zombie print routine and return.
@@ -1558,44 +1570,22 @@ prcom(psinfo_t *psinfo, char *ttyp)
if (psinfo->pr_time.tv_nsec > 500000000)
tm++;
}
- (void) printf(" %4ld:%.2ld", tm / 60, tm % 60); /* [L]TIME */
+ (void) printf(" %4ld:%.2ld ", tm / 60, tm % 60); /* [L]TIME */
if (zombie_lwp) {
- (void) printf(" <defunct>\n");
+ (void) printf("<defunct>\n");
return;
}
if (!fflg) { /* CMD */
wcnt = namencnt(psinfo->pr_fname, 16, 8);
- (void) printf(" %.*s\n", wcnt, psinfo->pr_fname);
+ (void) printf("%.*s\n", wcnt, psinfo->pr_fname);
return;
}
-
- /*
- * PRARGSZ == length of cmd arg string.
- */
- psinfo->pr_psargs[PRARGSZ-1] = '\0';
- bytesleft = PRARGSZ;
- for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) {
- length = mbtowc(&wchar, cp, MB_LEN_MAX);
- if (length == 0)
- break;
- if (length < 0 || !iswprint(wchar)) {
- if (length < 0)
- length = 1;
- if (bytesleft <= length) {
- *cp = '\0';
- break;
- }
- /* omit the unprintable character */
- (void) memmove(cp, cp+length, bytesleft-length);
- length = 0;
- }
- bytesleft -= length;
- }
- wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, lflg ? 35 : PRARGSZ);
- (void) printf(" %.*s\n", wcnt, psinfo->pr_psargs);
+ get_psargs(false, fflg, psinfo, psargs, sizeof (psargs));
+ print_psargs(psargs, 0);
+ printf("\n");
}
/*
@@ -1650,20 +1640,98 @@ print_time(time_t tim, int width)
(void) printf("%*s", width, buf);
}
+void
+get_psargs(bool comm, bool full, psinfo_t *psinfo, char *buf, size_t bufsize)
+{
+ char *path = NULL;
+ ssize_t size = 0;
+ char *cp;
+ int fd;
+
+ assert(psinfo->pr_psargs[PRARGSZ - 1] == '\0');
+
+ if (full && getenv("SHORT_PSARGS") == NULL &&
+ asprintf(&path, "%s/%d/cmdline", procdir,
+ (int)psinfo->pr_pid) != -1 && (fd = open(path, O_RDONLY)) != -1) {
+ size = read(fd, buf, bufsize);
+ (void) close(fd);
+ }
+
+ free(path);
+
+ if (size <= 0) {
+ (void) strlcpy(buf, psinfo->pr_psargs, bufsize);
+ } else {
+ ssize_t i;
+
+ buf[bufsize - 1] = '\0';
+
+ for (cp = buf; cp - buf < size; cp++) {
+ if (*cp == '\0' && (cp - buf) + 1 < size)
+ *cp = ' ';
+ }
+
+ for (i = strlen(buf) - 1; i >= 0 && isspace(buf[i]); i--) {
+ buf[i] = '\0';
+ }
+ }
+
+ if (comm && (cp = strpbrk(buf, " \t\r\v\f\n")) != NULL)
+ *cp = '\0';
+}
+
+void
+print_psargs(char *psargs, int width)
+{
+ int bytesleft;
+ int length;
+ char *cp;
+ int wcnt;
+
+ bytesleft = strlen(psargs);
+
+ for (cp = psargs; *cp != '\0'; cp += length) {
+ wchar_t wchar;
+
+ length = mbtowc(&wchar, cp, MB_LEN_MAX);
+
+ if (length == 0)
+ break;
+
+ if (length < 0 || !iswprint(wchar)) {
+ if (length < 0)
+ length = 1;
+ if (bytesleft <= length) {
+ *cp = '\0';
+ break;
+ }
+ /* omit the unprintable character */
+ (void) memmove(cp, cp + length, bytesleft - length);
+ bytesleft -= length;
+ length = 0;
+ }
+ bytesleft -= length;
+ }
+
+ wcnt = namencnt(psargs, PRMAXARGVLEN, width);
+
+ if (width != 0) {
+ (void) printf("%.*s", width, psargs);
+ } else {
+ (void) printf("%-.*s", wcnt, psargs);
+ }
+}
+
static void
print_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
{
+ char psargs[PRMAXARGVLEN] = "";
int width = f->width;
struct passwd *pwd;
struct group *grp;
time_t cputime;
- int bytesleft;
int wcnt;
- wchar_t wchar;
- char *cp;
- int length;
ulong_t mask;
- char c = '\0', *csave = NULL;
int zombie_lwp;
zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z');
@@ -1916,12 +1984,11 @@ print_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
(void) printf("%s", "<defunct>");
break;
}
- csave = strpbrk(psinfo->pr_psargs, " \t\r\v\f\n");
- if (csave) {
- c = *csave;
- *csave = '\0';
- }
- /* FALLTHROUGH */
+
+ get_psargs(true, false, psinfo, psargs, sizeof (psargs));
+ print_psargs(psargs, f->next != NULL ? width : 0);
+ break;
+
case F_ARGS:
/*
* PRARGSZ == length of cmd arg string.
@@ -1930,38 +1997,11 @@ print_field(psinfo_t *psinfo, struct field *f, const char *ttyp)
(void) printf("%-*s", width, "<defunct>");
break;
}
- psinfo->pr_psargs[PRARGSZ-1] = '\0';
- bytesleft = PRARGSZ;
- for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) {
- length = mbtowc(&wchar, cp, MB_LEN_MAX);
- if (length == 0)
- break;
- if (length < 0 || !iswprint(wchar)) {
- if (length < 0)
- length = 1;
- if (bytesleft <= length) {
- *cp = '\0';
- break;
- }
- /* omit the unprintable character */
- (void) memmove(cp, cp+length, bytesleft-length);
- length = 0;
- }
- bytesleft -= length;
- }
- wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, width);
- /*
- * Print full width unless this is the last format.
- */
- if (f->next != NULL)
- (void) printf("%-*.*s", width, wcnt,
- psinfo->pr_psargs);
- else
- (void) printf("%-.*s", wcnt,
- psinfo->pr_psargs);
- if (f->fname == F_COMM && csave)
- *csave = c;
+
+ get_psargs(false, fflg, psinfo, psargs, sizeof (psargs));
+ print_psargs(psargs, f->next != NULL ? width : 0);
break;
+
case F_TASKID:
(void) printf("%*d", width, (int)psinfo->pr_taskid);
break;
@@ -2422,7 +2462,8 @@ namencnt(char *cmd, int csisize, int scrsize)
return (8); /* default to use for illegal chars */
if ((nscrsz = wcwidth(wchar)) <= 0)
return (8);
- if (csiwcnt + ncsisz > csisize || scrwcnt + nscrsz > scrsize)
+ if (csiwcnt + ncsisz > csisize ||
+ (scrsize != 0 && scrwcnt + nscrsz > scrsize))
break;
csiwcnt += ncsisz;
scrwcnt += nscrsz;
diff --git a/usr/src/cmd/ps/ucbps.c b/usr/src/cmd/ps/ucbps.c
index 6abeb1baea..05997029fd 100644
--- a/usr/src/cmd/ps/ucbps.c
+++ b/usr/src/cmd/ps/ucbps.c
@@ -21,7 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -65,8 +65,10 @@
#include <wctype.h>
#include <stdarg.h>
#include <sys/proc.h>
+#include <sys/procfs.h>
#include <priv_utils.h>
#include <zone.h>
+#include <stdbool.h>
#define NTTYS 2 /* max ttys that can be specified with the -t option */
/* only one tty can be specified with SunOS ps */
@@ -101,6 +103,7 @@ static int nflg; /* Numerical output */
static int pflg; /* Specific process id passed as argument */
static int Uflg; /* Update private database, ups_data */
static int errflg;
+static int wflag;
static char *gettty();
static char argbuf[ARGSIZ];
@@ -133,8 +136,7 @@ static void getarg(void);
static void prtime(timestruc_t st);
static void przom(psinfo_t *psinfo);
static int num(char *);
-static int preadargs(int, psinfo_t *, char *);
-static int preadenvs(int, psinfo_t *, char *);
+static int preadenvs(int, psinfo_t *, char *, size_t);
static int prcom(int, psinfo_t *, char *);
static int namencnt(char *, int, int);
static int pscompare(const void *, const void *);
@@ -142,12 +144,18 @@ static char *err_string(int);
extern int scrwidth(wchar_t); /* header file? */
+/* from ps.c */
+void get_psargs(bool, bool, psinfo_t *, char *, size_t);
+void print_psargs(char *, int);
+
int
ucbmain(int argc, char **argv)
{
psinfo_t info; /* process information structure from /proc */
- char *psargs = NULL; /* pointer to buffer for -w and -ww options */
- char *svpsargs = NULL;
+ /*
+ * This can also store env vars, so we bump up the size.
+ */
+ char psargs[PRMAXARGVLEN * 2] = "";
struct psent *psent;
int entsize;
int nent;
@@ -235,11 +243,12 @@ ucbmain(int argc, char **argv)
case 'U': /* update private database ups_data */
Uflg++;
break;
- case 'w': /* increase display width */
+ case 'w':
if (twidth < 132)
twidth = 132;
- else /* second w option */
+ if (wflag)
twidth = NCARGS;
+ wflag++;
break;
case 'v': /* display virtual memory format */
vflg++;
@@ -371,15 +380,9 @@ ucbmain(int argc, char **argv)
(void) sprintf(hdr, "%*s TT S TIME COMMAND",
pidwidth + 1, "PID");
- twidth = twidth - strlen(hdr) + 6;
+ twidth = twidth - strlen(hdr) + 7;
(void) printf("%s\n", hdr);
- if (twidth > PRARGSZ && (psargs = malloc(twidth)) == NULL) {
- (void) fprintf(stderr, "ps: no memory\n");
- exit(1);
- }
- svpsargs = psargs;
-
/*
* Determine which processes to print info about by searching
* the /proc directory and looking at each process.
@@ -412,7 +415,7 @@ retry:
if ((psfd = open(psname, O_RDONLY)) == -1)
continue;
asfd = -1;
- if (psargs != NULL || eflg) {
+ if (eflg) {
/* now we need the proc_owner privilege */
(void) __priv_bracket(PRIV_ON);
@@ -472,26 +475,20 @@ retry:
if (!found && !tflg && !aflg && info.pr_euid != my_uid)
goto closeit;
- /*
- * Read the args for the -w and -ww cases
- */
- if (asfd > 0) {
- if ((psargs != NULL &&
- preadargs(asfd, &info, psargs) == -1) ||
- (eflg && preadenvs(asfd, &info, psargs) == -1)) {
- int saverr = errno;
+ get_psargs(false, wflag, &info, psargs, sizeof (psargs));
- (void) close(asfd);
- if (saverr == EAGAIN)
- goto retry;
- if (saverr != ENOENT)
- (void) fprintf(stderr,
- "ps: read() on %s: %s\n",
- asname, err_string(saverr));
- continue;
+ if (eflg && asfd > 0 &&
+ preadenvs(asfd, &info, psargs, sizeof (psargs)) == -1) {
+ int saverr = errno;
+
+ (void) close(asfd);
+ if (saverr == EAGAIN)
+ goto retry;
+ if (saverr != ENOENT) {
+ (void) fprintf(stderr, "ps: read() on %s: %s\n",
+ asname, err_string(saverr));
}
- } else {
- psargs = info.pr_psargs;
+ continue;
}
if (nent >= entsize) {
@@ -509,22 +506,17 @@ retry:
exit(1);
}
*psent[nent].psinfo = info;
- if (psargs == NULL)
- psent[nent].psargs = NULL;
- else {
- if ((psent[nent].psargs = malloc(strlen(psargs)+1))
- == NULL) {
- (void) fprintf(stderr, "ps: no memory\n");
- exit(1);
- }
- (void) strcpy(psent[nent].psargs, psargs);
+
+ if ((psent[nent].psargs = strndup(psargs, twidth)) == NULL) {
+ (void) fprintf(stderr, "ps: no memory\n");
+ exit(1);
}
+
psent[nent].found = found;
nent++;
closeit:
if (asfd > 0)
(void) close(asfd);
- psargs = svpsargs;
}
/* revert to non-privileged user */
@@ -555,100 +547,17 @@ usage(void) /* print usage message and quit */
}
/*
- * Read the process arguments from the process.
- * This allows >PRARGSZ characters of arguments to be displayed but,
- * unlike pr_psargs[], the process may have changed them.
- */
-#define NARG 100
-static int
-preadargs(int pfd, psinfo_t *psinfo, char *psargs)
-{
- off_t argvoff = (off_t)psinfo->pr_argv;
- size_t len;
- char *psa = psargs;
- int bsize = twidth;
- int narg = NARG;
- off_t argv[NARG];
- off_t argoff;
- off_t nextargoff;
- int i;
-#ifdef _LP64
- caddr32_t argv32[NARG];
- int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
-#endif
-
- if (psinfo->pr_nlwp == 0 ||
- strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0)
- goto out;
-
- (void) memset(psa, 0, bsize--);
- nextargoff = 0;
- errno = EIO;
- while (bsize > 0) {
- if (narg == NARG) {
- (void) memset(argv, 0, sizeof (argv));
-#ifdef _LP64
- if (is32) {
- if ((i = pread(pfd, argv32, sizeof (argv32),
- argvoff)) <= 0) {
- if (i == 0 || errno == EIO)
- break;
- return (-1);
- }
- for (i = 0; i < NARG; i++)
- argv[i] = argv32[i];
- } else
-#endif
- if ((i = pread(pfd, argv, sizeof (argv),
- argvoff)) <= 0) {
- if (i == 0 || errno == EIO)
- break;
- return (-1);
- }
- narg = 0;
- }
- if ((argoff = argv[narg++]) == 0)
- break;
- if (argoff != nextargoff &&
- (i = pread(pfd, psa, bsize, argoff)) <= 0) {
- if (i == 0 || errno == EIO)
- break;
- return (-1);
- }
- len = strlen(psa);
- psa += len;
- *psa++ = ' ';
- bsize -= len + 1;
- nextargoff = argoff + len + 1;
-#ifdef _LP64
- argvoff += is32? sizeof (caddr32_t) : sizeof (caddr_t);
-#else
- argvoff += sizeof (caddr_t);
-#endif
- }
- while (psa > psargs && isspace(*(psa-1)))
- psa--;
-
-out:
- *psa = '\0';
- if (strlen(psinfo->pr_psargs) > strlen(psargs))
- (void) strcpy(psargs, psinfo->pr_psargs);
-
- return (0);
-}
-
-/*
* Read environment variables from the process.
* Append them to psargs if there is room.
*/
+#define NARG 100
static int
-preadenvs(int pfd, psinfo_t *psinfo, char *psargs)
+preadenvs(int pfd, psinfo_t *psinfo, char *psargs, size_t bufsize)
{
off_t envpoff = (off_t)psinfo->pr_envp;
int len;
char *psa;
- char *psainit;
- int bsize;
+ int remaining;
int nenv = NARG;
off_t envp[NARG];
off_t envoff;
@@ -659,18 +568,18 @@ preadenvs(int pfd, psinfo_t *psinfo, char *psargs)
int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64);
#endif
- psainit = psa = (psargs != NULL)? psargs : psinfo->pr_psargs;
+ psa = psargs;
len = strlen(psa);
psa += len;
- bsize = twidth - len - 1;
+ remaining = bufsize - len - 1;
- if (bsize <= 0 || psinfo->pr_nlwp == 0 ||
+ if (remaining <= 0 || psinfo->pr_nlwp == 0 ||
strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0)
return (0);
nextenvoff = 0;
errno = EIO;
- while (bsize > 0) {
+ while (remaining > 0) {
if (nenv == NARG) {
(void) memset(envp, 0, sizeof (envp));
#ifdef _LP64
@@ -696,7 +605,7 @@ preadenvs(int pfd, psinfo_t *psinfo, char *psargs)
if ((envoff = envp[nenv++]) == 0)
break;
if (envoff != nextenvoff &&
- (i = pread(pfd, psa+1, bsize, envoff)) <= 0) {
+ (i = pread(pfd, psa+1, remaining, envoff)) <= 0) {
if (i == 0 || errno == EIO)
break;
return (-1);
@@ -704,7 +613,7 @@ preadenvs(int pfd, psinfo_t *psinfo, char *psargs)
*psa++ = ' ';
len = strlen(psa);
psa += len;
- bsize -= len + 1;
+ remaining -= len + 1;
nextenvoff = envoff + len + 1;
#ifdef _LP64
envpoff += is32? sizeof (caddr32_t) : sizeof (caddr_t);
@@ -712,7 +621,7 @@ preadenvs(int pfd, psinfo_t *psinfo, char *psargs)
envpoff += sizeof (caddr_t);
#endif
}
- while (psa > psainit && isspace(*(psa-1)))
+ while (psa > psargs && isspace(*(psa-1)))
psa--;
*psa = '\0';
@@ -844,12 +753,9 @@ prtpct(ushort_t pct)
static int
prcom(int found, psinfo_t *psinfo, char *psargs)
{
- char *cp;
char *tp;
- char *psa;
long tm;
- int i, wcnt, length;
- wchar_t wchar;
+ int wcnt;
struct tty *ttyp;
/*
@@ -869,7 +775,6 @@ prcom(int found, psinfo_t *psinfo, char *psargs)
* info. If 't' is set, check if term is in list of desired terminals
* and print it if it is.
*/
- i = 0;
tp = gettty(psinfo);
if (*tp == '?' && !found && !xflg)
@@ -1001,37 +906,9 @@ prcom(int found, psinfo_t *psinfo, char *psargs)
(void) printf(" %.*s", wcnt, psinfo->pr_fname);
return (1);
}
- /*
- * PRARGSZ == length of cmd arg string.
- */
- if (psargs == NULL) {
- psa = &psinfo->pr_psargs[0];
- i = PRARGSZ;
- tp = &psinfo->pr_psargs[PRARGSZ];
- } else {
- psa = psargs;
- i = strlen(psargs);
- tp = psa + i;
- }
- for (cp = psa; cp < tp; /* empty */) {
- if (*cp == 0)
- break;
- length = mbtowc(&wchar, cp, MB_LEN_MAX);
- if (length < 0 || !iswprint(wchar)) {
- (void) printf(" [ %.16s ]", psinfo->pr_fname);
- return (1);
- }
- cp += length;
- }
- wcnt = namencnt(psa, i, maxlen);
-#if 0
- /* dumps core on really long strings */
- (void) printf(" %.*s", wcnt, psa);
-#else
- (void) putchar(' ');
- (void) fwrite(psa, 1, wcnt, stdout);
-#endif
+ printf(" ");
+ print_psargs(psargs, wflag < 2 ? maxlen : 0);
return (1);
}
diff --git a/usr/src/cmd/ptools/Makefile.bld b/usr/src/cmd/ptools/Makefile.bld
index 8db9c449fe..efbfee9d04 100644
--- a/usr/src/cmd/ptools/Makefile.bld
+++ b/usr/src/cmd/ptools/Makefile.bld
@@ -22,6 +22,7 @@
#
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2015 Joyent, Inc.
#
# Copyright 2019 Joyent, Inc.
@@ -79,6 +80,12 @@ CERRWARN_pwait += -_smatch=off
CERRWARN += $(CERRWARN_$(PROG))
+#
+# Common code definitions
+#
+COBJS = ptools_common.o
+CINC = -I../../common
+
# pargs depends on ../../common/elfcap components
# pmadvise depends on pmap components
@@ -89,14 +96,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)
+
LN_pargs = penv pauxv
CPPFLAGS += $(CPPFLAGS_$(PROG))
@@ -124,6 +149,10 @@ pmap_common.o: $(PMAP)/pmap_common.c
$(COMPILE.c) $<
$(POST_PROCESS_O)
+%.o: ../../common/%.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
all: $(PROG) $(LN_$(PROG))
ROOTBINLN=$(LN_$(PROG):%=$(ROOTBIN)/%)
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 a4a2654e30..83335db976 100644
--- a/usr/src/cmd/ptools/pargs/pargs.c
+++ b/usr/src/cmd/ptools/pargs/pargs.c
@@ -835,6 +835,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 },
@@ -855,9 +856,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_AUX4, "AT_SUN_BRAND_AUX4", at_null },
{ AT_SUN_COMMPAGE, "AT_SUN_COMMPAGE", at_null },
{ AT_SUN_FPTYPE, "AT_SUN_FPTYPE", at_null },
{ AT_SUN_FPSIZE, "AT_SUN_FPSIZE", at_null }
diff --git a/usr/src/cmd/ptools/pfiles/pfiles.c b/usr/src/cmd/ptools/pfiles/pfiles.c
index dd5ce4af11..1128286794 100644
--- a/usr/src/cmd/ptools/pfiles/pfiles.c
+++ b/usr/src/cmd/ptools/pfiles/pfiles.c
@@ -25,7 +25,7 @@
*/
/*
* Copyright (c) 2017 Joyent, Inc. All Rights reserved.
- * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
*/
#include <stdio.h>
@@ -44,6 +44,7 @@
#include <sys/mkdev.h>
#include <sys/stropts.h>
#include <sys/timod.h>
+#include <sys/file.h>
#include <sys/un.h>
#include <libproc.h>
#include <netinet/in.h>
@@ -364,12 +365,13 @@ dofcntl(struct ps_prochandle *Pr, const prfdinfo_t *info, int mandatory,
#define ALL_O_FLAGS O_ACCMODE | O_NDELAY | O_NONBLOCK | O_APPEND | \
O_SYNC | O_DSYNC | O_RSYNC | O_XATTR | \
- O_CREAT | O_TRUNC | O_EXCL | O_NOCTTY | O_LARGEFILE
+ O_CREAT | O_TRUNC | O_EXCL | O_NOCTTY | O_LARGEFILE | \
+ __FLXPATH
static void
show_fileflags(int flags)
{
- char buffer[136];
+ char buffer[147];
char *str = buffer;
switch (flags & O_ACCMODE) {
@@ -417,6 +419,8 @@ show_fileflags(int flags)
(void) strcat(str, "|O_LARGEFILE");
if (flags & O_XATTR)
(void) strcat(str, "|O_XATTR");
+ if (flags & __FLXPATH)
+ (void) strcat(str, "|__FLXPATH");
if (flags & ~(ALL_O_FLAGS))
(void) sprintf(str + strlen(str), "|0x%x",
flags & ~(ALL_O_FLAGS));
@@ -511,6 +515,7 @@ show_sockaddr(const char *str, const struct sockaddr *sa, socklen_t len)
case AF_KEY: p = "AF_KEY"; break;
case AF_POLICY: p = "AF_POLICY"; 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);
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/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 2790463b9a..6733c7b8d4 100644
--- a/usr/src/cmd/ptools/psig/psig.c
+++ b/usr/src/cmd/ptools/psig/psig.c
@@ -21,6 +21,7 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
#include <stdio.h>
@@ -35,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"
@@ -170,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;
@@ -197,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;
@@ -211,7 +214,7 @@ look(char *arg)
action = malloc(maxsig * sizeof (struct sigaction));
if (action == NULL) {
(void) fprintf(stderr,
- "%s: cannot malloc() space for %d sigaction structures\n",
+ "%s: cannot malloc() space for %d sigaction structures\n",
command, maxsig);
goto look_error;
}
diff --git a/usr/src/cmd/ptools/ptime/ptime.c b/usr/src/cmd/ptools/ptime/ptime.c
index 2da2f8d281..b1f53593c2 100644
--- a/usr/src/cmd/ptools/ptime/ptime.c
+++ b/usr/src/cmd/ptools/ptime/ptime.c
@@ -41,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);
@@ -189,7 +191,7 @@ main(int argc, char **argv)
static int
look(pid_t pid)
{
- char pathname[100];
+ char pathname[PATH_MAX];
int rval = 0;
int fd;
psinfo_t psinfo;
@@ -203,7 +205,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"));
diff --git a/usr/src/cmd/ptools/pwait/pwait.c b/usr/src/cmd/ptools/pwait/pwait.c
index 4337d92f2a..efb79c38f1 100644
--- a/usr/src/cmd/ptools/pwait/pwait.c
+++ b/usr/src/cmd/ptools/pwait/pwait.c
@@ -38,6 +38,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;
@@ -48,6 +50,7 @@ static char *command;
int
main(int argc, char **argv)
{
+ char buf[PATH_MAX];
unsigned long remain = 0;
struct pollfd *pollfd;
struct pollfd *pfd;
@@ -79,6 +82,8 @@ main(int argc, char **argv)
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();
@@ -115,7 +120,7 @@ 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",
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/Makefile b/usr/src/cmd/rcap/rcapd/Makefile
index 77f4d6812e..355b5eef0a 100644
--- a/usr/src/cmd/rcap/rcapd/Makefile
+++ b/usr/src/cmd/rcap/rcapd/Makefile
@@ -22,6 +22,8 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2020 Joyent, Inc.
+#
include ../../Makefile.cmd
include ../../Makefile.cmd.64
@@ -62,7 +64,7 @@ LINTSRCS = rcapd_main.c \
$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
CPPFLAGS += -DDEBUG_MSG
CPPFLAGS += -I$(COMMON_DIR)
-LDLIBS += -lkstat -lproc -lproject -lzonecfg -lumem -lscf
+LDLIBS += -lkstat -lproc -lproject -lumem -lscf
LDLIBS += $(EXTRA_LDLIBS)
LINTFLAGS64 += -u
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 3f09a7f4ed..458e8f3bef 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.
*/
#include <sys/mman.h>
@@ -549,7 +550,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/Makefile b/usr/src/cmd/rcap/rcapstat/Makefile
index 45c4debcdc..20d5d735fc 100644
--- a/usr/src/cmd/rcap/rcapstat/Makefile
+++ b/usr/src/cmd/rcap/rcapstat/Makefile
@@ -22,6 +22,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2020 Joyent, Inc.
+#
.KEEP_STATE:
.SUFFIXES:
@@ -39,7 +41,7 @@ STAT_COMMON_SRCS = $(STAT_COMMON_OBJS:%.o=$(STATCOMMONDIR)/%.c)
$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
CPPFLAGS += -I$(COMMON_DIR) -I$(STATCOMMONDIR)
-LDLIBS += -lumem -lzonecfg -lscf
+LDLIBS += -lumem -lscf
LDFLAGS += $(MAPFILE.NGB:%=-Wl,-M%)
PROG = rcapstat
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/savecore/Makefile.com b/usr/src/cmd/savecore/Makefile.com
index f0c3b42b9b..0ee5137460 100644
--- a/usr/src/cmd/savecore/Makefile.com
+++ b/usr/src/cmd/savecore/Makefile.com
@@ -21,7 +21,7 @@
#
# Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
#
-# Copyright (c) 2018, Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
PROG= savecore
SRCS= ../savecore.c ../../../uts/common/os/compress.c
@@ -54,12 +54,14 @@ BZIP2OBJS = bz2blocksort.o \
bz2crctable.o \
bz2huffman.o
+CHACHAOBJ = chacha.o
+
.KEEP_STATE:
all: $(PROG)
-$(PROG): $(OBJS) $(BZIP2OBJS)
- $(LINK.c) -o $(PROG) $(OBJS) $(BZIP2OBJS) $(LDLIBS)
+$(PROG): $(OBJS) $(BZIP2OBJS) $(CHACHAOBJ)
+ $(LINK.c) -o $(PROG) $(OBJS) $(BZIP2OBJS) $(CHACHAOBJ) $(LDLIBS)
$(POST_PROCESS)
clean:
@@ -78,3 +80,8 @@ include ../../Makefile.targ
bz2%.o: ../../../common/bzip2/%.c
$(COMPILE.c) -o $@ -I$(SRC)/common -I$(SRC)/common/bzip2 $<
$(POST_PROCESS_O)
+
+%.o: ../../../common/crypto/chacha/%.c
+ $(COMPILE.c) -o $@ -I$(SRC)/common -I$(SRC)/common/crypto/chacha $<
+ $(POST_PROCESS_O)
+
diff --git a/usr/src/cmd/savecore/savecore.c b/usr/src/cmd/savecore/savecore.c
index b5ac0397aa..16529d8d9f 100644
--- a/usr/src/cmd/savecore/savecore.c
+++ b/usr/src/cmd/savecore/savecore.c
@@ -42,6 +42,8 @@
#include <atomic.h>
#include <libnvpair.h>
#include <libintl.h>
+#include <assert.h>
+#include <strings.h>
#include <sys/mem.h>
#include <sys/statvfs.h>
#include <sys/dumphdr.h>
@@ -55,6 +57,7 @@
#include <sys/fm/util.h>
#include <fm/libfmevent.h>
#include <sys/int_fmtio.h>
+#include <crypto/chacha/chacha.h>
/* fread/fwrite buffer size */
@@ -74,6 +77,8 @@ static long pagesize; /* dump pagesize */
static int dumpfd = -1; /* dumpfile descriptor */
static boolean_t have_dumpfile = B_TRUE; /* dumpfile existence */
static dumphdr_t corehdr, dumphdr; /* initial and terminal dumphdrs */
+static dump_crypt_t dcrypt; /* dump encryption header */
+static size_t dumphdr_size; /* size of dump header */
static boolean_t dump_incomplete; /* dumphdr indicates incomplete */
static boolean_t fm_panic; /* dump is the result of fm_panic */
static offset_t endoff; /* offset of end-of-dump header */
@@ -165,7 +170,8 @@ static void
usage(void)
{
(void) fprintf(stderr,
- "usage: %s [-L | -r] [-vd] [-f dumpfile] [dirname]\n", progname);
+ "usage: %s [-L | -r] [-vd] [-k keyfile] [-f dumpfile] [dirname]\n",
+ progname);
exit(1);
}
@@ -329,6 +335,19 @@ Pwrite(int fd, void *buf, size_t size, off64_t off)
strerror(errno));
}
+static void
+Read(int fd, void *buf, size_t size)
+{
+ ssize_t sz = read(fd, buf, size);
+
+ if (sz < 0)
+ logprint(SC_SL_ERR | SC_EXIT_ERR,
+ "read: %s", strerror(errno));
+ else if (sz != size)
+ logprint(SC_SL_ERR | SC_EXIT_ERR,
+ "read: size %ld != %ld", sz, size);
+}
+
static void *
Zalloc(size_t size)
{
@@ -362,9 +381,9 @@ read_dumphdr(void)
dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644);
endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET;
Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff);
- Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + sizeof (dumphdr));
pagesize = dumphdr.dump_pagesize;
+ dumphdr_size = sizeof (dumphdr);
if (dumphdr.dump_magic != DUMP_MAGIC)
logprint(SC_SL_NONE | SC_EXIT_PEND, "bad magic number %x",
@@ -384,6 +403,20 @@ read_dumphdr(void)
"dump is from %u-bit kernel - cannot save on %u-bit kernel",
dumphdr.dump_wordsize, DUMP_WORDSIZE);
+ if (dumphdr.dump_flags & DF_ENCRYPTED) {
+ /*
+ * If our dump is encrypted, our encryption header follows
+ * our dump header. Read it, and then increment our
+ * dumphdr_size to assure that reads of data following the
+ * encryption header account for it.
+ */
+ Pread(dumpfd, &dcrypt, sizeof (dcrypt),
+ endoff + sizeof (dumphdr));
+ dumphdr_size += sizeof (dcrypt);
+ }
+
+ Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + dumphdr_size);
+
if (datahdr.dump_datahdr_magic == DUMP_DATAHDR_MAGIC) {
if (datahdr.dump_datahdr_version != DUMP_DATAHDR_VERSION)
logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_PEND,
@@ -500,19 +533,76 @@ build_dump_map(int corefd, const pfn_t *pfn_table)
free(inbuf);
}
+static void
+Decrypt(offset_t dumpoff, len_t nb, char *buf, size_t sz, uint8_t *key)
+{
+ size_t nelems = nb / sizeof (uint64_t), i;
+ uint64_t *clear = (uint64_t *)buf;
+ uint64_t *stream = (uint64_t *)(buf + sz);
+ uint64_t *crypt = (uint64_t *)(buf + (2 * sz));
+ uint64_t ctr = dumpoff >> DUMP_CRYPT_BLOCKSHIFT;
+ chacha_ctx_t ctx;
+
+ /*
+ * If our size is not 8-byte aligned, prepare our ciphertext to the
+ * next 8-byte boundary.
+ */
+ if (nb & (sizeof (uint64_t) - 1)) {
+ assert(nb < sz);
+ nelems++;
+ }
+
+ chacha_keysetup(&ctx, key, DUMP_CRYPT_KEYLEN * 8, 0);
+ chacha_ivsetup(&ctx, dcrypt.dump_crypt_nonce, (uint8_t *)&ctr);
+
+ for (i = 0; i < nelems; i++) {
+ stream[i] = dumpoff;
+ dumpoff += sizeof (uint64_t);
+ }
+
+ chacha_encrypt_bytes(&ctx, (uint8_t *)stream, (uint8_t *)crypt, nb);
+
+ for (i = 0; i < nelems; i++)
+ clear[i] ^= crypt[i];
+}
+
+static void
+Verify(uint8_t *key)
+{
+ chacha_ctx_t ctx;
+ uint8_t hmac[DUMP_CRYPT_HMACLEN];
+
+ chacha_keysetup(&ctx, key, DUMP_CRYPT_KEYLEN * 8, 0);
+ chacha_ivsetup(&ctx, dcrypt.dump_crypt_nonce, NULL);
+
+ chacha_encrypt_bytes(&ctx, (uint8_t *)&dumphdr.dump_utsname,
+ (uint8_t *)hmac, DUMP_CRYPT_HMACLEN);
+
+ if (bcmp(hmac, &dcrypt.dump_crypt_hmac, DUMP_CRYPT_HMACLEN) == 0)
+ return;
+
+ logprint(SC_SL_NONE | SC_EXIT_ERR,
+ "provided key does not match encryption key");
+}
+
/*
* Copy whole sections of the dump device to the file.
*/
static void
Copy(offset_t dumpoff, len_t nb, offset_t *offp, int fd, char *buf,
- size_t sz)
+ size_t sz, uint8_t *key)
{
size_t nr;
offset_t off = *offp;
while (nb > 0) {
nr = sz < nb ? sz : (size_t)nb;
+
Pread(dumpfd, buf, nr, dumpoff);
+
+ if (dumphdr.dump_flags & DF_ENCRYPTED)
+ Decrypt(dumpoff, nr, buf, sz, key);
+
Pwrite(fd, buf, nr, off);
off += nr;
dumpoff += nr;
@@ -567,21 +657,56 @@ CopyPages(offset_t *offp, int fd, char *buf, size_t sz)
* Update corehdr with new offsets.
*/
static void
-copy_crashfile(const char *corefile)
+copy_crashfile(const char *corefile, const char *keyfile)
{
int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ uint8_t keybuf[DUMP_CRYPT_KEYLEN];
size_t bufsz = FBUFSIZE;
- char *inbuf = Zalloc(bufsz);
+ char *inbuf;
offset_t coreoff;
size_t nb;
+ uint8_t *key = NULL;
+
+ if (dumphdr.dump_flags & DF_ENCRYPTED) {
+ int keyfd;
+
+ if (dcrypt.dump_crypt_algo != DUMP_CRYPT_ALGO_CHACHA20) {
+ logprint(SC_SL_NONE | SC_EXIT_ERR, "unrecognized dump "
+ "encryption algorithm %u", dcrypt.dump_crypt_algo);
+ }
+
+ if (keyfile == NULL) {
+ logprint(SC_SL_NONE | SC_EXIT_ERR, "dump is encrypted; "
+ "key must be provided");
+ }
- logprint(SC_SL_ERR | SC_IF_VERBOSE,
- "Copying %s to %s/%s\n", dumpfile, savedir, corefile);
+ keyfd = Open(keyfile, O_RDONLY, 0600);
+ Read(keyfd, keybuf, sizeof (keybuf));
+ (void) close(keyfd);
+
+ /*
+ * For the encrypted case, we triple our buffer size to
+ * allow for the stream buffer and the ciphertext buffer.
+ */
+ inbuf = Zalloc(bufsz * 3);
+ key = keybuf;
+ Verify(key);
+
+ logprint(SC_SL_ERR | SC_IF_VERBOSE, "Decrypting and copying "
+ "%s to %s/%s\n", dumpfile, savedir, corefile);
+ } else {
+ inbuf = Zalloc(bufsz);
+
+ logprint(SC_SL_ERR | SC_IF_VERBOSE,
+ "Copying %s to %s/%s\n", dumpfile, savedir, corefile);
+ }
/*
- * This dump file is still compressed
+ * This dump file is still compressed -- but it will no longer be
+ * encrypted.
*/
corehdr.dump_flags |= DF_COMPRESSED | DF_VALID;
+ corehdr.dump_flags &= ~DF_ENCRYPTED;
/*
* Leave room for corehdr, it is updated and written last
@@ -595,7 +720,7 @@ copy_crashfile(const char *corefile)
coreoff = roundup(coreoff, pagesize);
corehdr.dump_ksyms = coreoff;
Copy(dumphdr.dump_ksyms, dumphdr.dump_ksyms_csize, &coreoff, corefd,
- inbuf, bufsz);
+ inbuf, bufsz, key);
/*
* Save the pfn table.
@@ -603,7 +728,7 @@ copy_crashfile(const char *corefile)
coreoff = roundup(coreoff, pagesize);
corehdr.dump_pfn = coreoff;
Copy(dumphdr.dump_pfn, dumphdr.dump_npages * sizeof (pfn_t), &coreoff,
- corefd, inbuf, bufsz);
+ corefd, inbuf, bufsz, key);
/*
* Save the dump map.
@@ -611,7 +736,7 @@ copy_crashfile(const char *corefile)
coreoff = roundup(coreoff, pagesize);
corehdr.dump_map = coreoff;
Copy(dumphdr.dump_map, dumphdr.dump_nvtop * sizeof (mem_vtop_t),
- &coreoff, corefd, inbuf, bufsz);
+ &coreoff, corefd, inbuf, bufsz, key);
/*
* Save the data pages.
@@ -620,7 +745,7 @@ copy_crashfile(const char *corefile)
corehdr.dump_data = coreoff;
if (datahdr.dump_data_csize != 0)
Copy(dumphdr.dump_data, datahdr.dump_data_csize, &coreoff,
- corefd, inbuf, bufsz);
+ corefd, inbuf, bufsz, key);
else
CopyPages(&coreoff, corefd, inbuf, bufsz);
@@ -1657,6 +1782,8 @@ main(int argc, char *argv[])
struct rlimit rl;
long filebounds = -1;
char namelist[30], corefile[30], boundstr[30];
+ char *keyfile = NULL;
+
dumpfile = NULL;
startts = gethrtime();
@@ -1672,7 +1799,7 @@ main(int argc, char *argv[])
if (savedir != NULL)
savedir = strdup(savedir);
- while ((c = getopt(argc, argv, "Lvcdmf:r")) != EOF) {
+ while ((c = getopt(argc, argv, "Lvcdmf:rk:")) != EOF) {
switch (c) {
case 'L':
livedump++;
@@ -1696,6 +1823,9 @@ main(int argc, char *argv[])
dumpfile = optarg;
filebounds = getbounds(dumpfile);
break;
+ case 'k':
+ keyfile = optarg;
+ break;
case '?':
usage();
}
@@ -1756,6 +1886,18 @@ main(int argc, char *argv[])
read_dumphdr();
+ if (dumphdr.dump_flags & DF_ENCRYPTED) {
+ if (filemode) {
+ logprint(SC_SL_NONE | SC_EXIT_ERR, "saved dump file is "
+ "erroneously encrypted");
+ }
+
+ if (!csave) {
+ logprint(SC_SL_NONE | SC_EXIT_ERR, "dump is encrypted; "
+ "cannot be saved uncompressed");
+ }
+ }
+
/*
* We want this message to go to the log file, but not the console.
* There's no good way to do that with the existing syslog facility.
@@ -1860,7 +2002,7 @@ main(int argc, char *argv[])
"Saving compressed system crash dump in %s/%s",
savedir, corefile);
- copy_crashfile(corefile);
+ copy_crashfile(corefile, keyfile);
/*
* Raise a fault management event that indicates the system
@@ -1876,7 +2018,7 @@ main(int argc, char *argv[])
char *metrics = Zalloc(metrics_size + 1);
Pread(dumpfd, metrics, metrics_size, endoff +
- sizeof (dumphdr) + sizeof (datahdr));
+ dumphdr_size + sizeof (datahdr));
if (sec < 1)
sec = 1;
diff --git a/usr/src/cmd/sed/main.c b/usr/src/cmd/sed/main.c
index c928131958..b148f7293e 100644
--- a/usr/src/cmd/sed/main.c
+++ b/usr/src/cmd/sed/main.c
@@ -2,7 +2,7 @@
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
* 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.
@@ -43,9 +43,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>
@@ -54,6 +52,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <libintl.h>
#include "defs.h"
#include "extern.h"
@@ -108,11 +107,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);
@@ -134,18 +128,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':
@@ -162,10 +152,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':
@@ -206,8 +193,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 19698de5ad..df7645e3d0 100644
--- a/usr/src/cmd/sendmail/src/Makefile
+++ b/usr/src/cmd/sendmail/src/Makefile
@@ -46,8 +46,7 @@ LDFLAGS += $(MAPFILES:%=-Wl,-M%)
LDLIBS += ../libsmutil/libsmutil.a ../libsm/libsm.a -lresolv -lsocket \
-lnsl ../db/libdb.a -lldap -lsldap -lwrap -lumem \
- -lssl -lcrypto -lsasl
-NATIVE_LIBS += libssl.so libcrypto.so
+ -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 c9e191d3f1..61a62a61ba 100644
--- a/usr/src/cmd/sgs/elfdump/Makefile.targ
+++ b/usr/src/cmd/sgs/elfdump/Makefile.targ
@@ -74,7 +74,7 @@ clean:
check_struct_layout gen_layout_obj.o \
struct_layout_$(ARCH).tmp
-install: all $(ROOTPROG) $(ROOTCCSBINLINK)
+install: all $(VAR_SGSBINPROG) $(VAR_SGSCCSLINK)
include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/cmd/sgs/elfdump/amd64/Makefile b/usr/src/cmd/sgs/elfdump/amd64/Makefile
index eac6a377dd..0294565bb4 100644
--- a/usr/src/cmd/sgs/elfdump/amd64/Makefile
+++ b/usr/src/cmd/sgs/elfdump/amd64/Makefile
@@ -32,6 +32,8 @@ include ../Makefile.com
ARCH = amd64
+install: all $(ROOTPROG64)
+
include ../Makefile.targ
include $(SRC)/Makefile.master.64
diff --git a/usr/src/cmd/sgs/include/conv.h b/usr/src/cmd/sgs/include/conv.h
index cfd690098c..f9e649d6cb 100644
--- a/usr/src/cmd/sgs/include/conv.h
+++ b/usr/src/cmd/sgs/include/conv.h
@@ -291,7 +291,7 @@ typedef union {
} Conv_bnd_obj_buf_t;
/* conv_phdr_flags() */
-#define CONV_PHDR_FLAGS_BUFSIZE 88
+#define CONV_PHDR_FLAGS_BUFSIZE 244
typedef union {
Conv_inv_buf_t inv_buf;
char buf[CONV_PHDR_FLAGS_BUFSIZE];
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 ad75016a6d..9e4b0e5e00 100644
--- a/usr/src/cmd/sgs/lex/common/once.h
+++ b/usr/src/cmd/sgs/lex/common/once.h
@@ -26,6 +26,8 @@
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
+/* Copyright (c) 2013, joyent, Inc. All rights reserved. */
+
#ifndef _ONCE_H
#define _ONCE_H
@@ -71,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 d23a1086d3..d7403991ed 100644
--- a/usr/src/cmd/sgs/libconv/common/corenote.c
+++ b/usr/src/cmd/sgs/libconv/common/corenote.c
@@ -26,6 +26,7 @@
/*
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
* Copyright (c) 2018 Joyent, Inc.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
* Copyright 2022 Oxide Computer Company
*/
@@ -79,7 +80,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,
@@ -91,10 +92,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,
@@ -113,7 +115,7 @@ conv_cnote_auxv_type(Word type, Conv_fmt_flags_t fmt_flags,
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,
- 0, 0,
+ MSG_AUXV_AT_SUN_BRAND_NROOT, MSG_AUXV_AT_SUN_BRAND_AUX4,
MSG_AUXV_AT_SUN_COMMPAGE, MSG_AUXV_AT_SUN_FPTYPE,
MSG_AUXV_AT_SUN_FPSIZE, MSG_AUXV_AT_SUN_HWCAP3
};
@@ -121,7 +123,7 @@ conv_cnote_auxv_type(Word type, Conv_fmt_flags_t fmt_flags,
CONV_DS_MSG_INIT(2014, types_2014_2029) };
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_0_25), CONV_DS_ADDR(ds_types_2000_2011),
CONV_DS_ADDR(ds_types_2014_2029), NULL };
return (conv_map_ds(ELFOSABI_NONE, EM_NONE, type, ds, fmt_flags,
@@ -2490,6 +2492,7 @@ conv_cnote_fileflags(uint32_t fileflags, Conv_fmt_flags_t fmt_flags,
{ 0x2000, MSG_PR_O_LARGEFILE },
{ 0x20000, MSG_PR_O_NOFOLLOW },
{ 0x40000, MSG_PR_O_NOLINKS },
+ { 0x80000000, MSG_PR___FLXPATH },
{ 0, 0 },
};
diff --git a/usr/src/cmd/sgs/libconv/common/corenote.msg b/usr/src/cmd/sgs/libconv/common/corenote.msg
index 1d944cc18c..981c0800ce 100644
--- a/usr/src/cmd/sgs/libconv/common/corenote.msg
+++ b/usr/src/cmd/sgs/libconv/common/corenote.msg
@@ -25,6 +25,7 @@
#
# Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
# Copyright (c) 2018 Joyent, Inc.
+# Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
# Copyright 2022 Oxide Computer Company
#
@@ -82,6 +83,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"
@@ -104,6 +108,8 @@
@ 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_AUXV_AT_SUN_BRAND_AUX4 "SUN_BRAND_AUX4"
@ MSG_AUXV_AT_SUN_COMMPAGE "SUN_COMMPAGE"
@ MSG_AUXV_AT_SUN_FPTYPE "SUN_FPTYPE"
@ MSG_AUXV_AT_SUN_FPSIZE "SUN_FPSIZE"
@@ -1113,6 +1119,7 @@
@ MSG_PR_O_XATTR "O_XATTR"
@ MSG_PR_O_NOFOLLOW "O_NOFOLLOW"
@ MSG_PR_O_NOLINKS "O_NOLINKS"
+@ MSG_PR___FLXPATH "__FLXPATH"
@ MSG_S_IFIFO "S_IFIFO"
@ MSG_S_IFCHR "S_IFCHR"
diff --git a/usr/src/cmd/sgs/libconv/common/phdr.c b/usr/src/cmd/sgs/libconv/common/phdr.c
index d2b29a201f..382a1bf9f1 100644
--- a/usr/src/cmd/sgs/libconv/common/phdr.c
+++ b/usr/src/cmd/sgs/libconv/common/phdr.c
@@ -25,6 +25,10 @@
*/
/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
+/*
* String conversion routines for program header attributes.
*/
#include <stdio.h>
@@ -95,6 +99,7 @@ error "PT_NUM has grown. Update phdrs[]"
{ PT_GNU_EH_FRAME, LIN, MSG_PT_GNU_EH_FRAME },
{ PT_GNU_STACK, LIN, MSG_PT_GNU_STACK },
{ PT_GNU_RELRO, LIN, MSG_PT_GNU_RELRO },
+ { PT_PAX_FLAGS, LIN, MSG_PT_PAX_FLAGS },
{ 0 }
};
@@ -109,6 +114,7 @@ error "PT_NUM has grown. Update phdrs[]"
{ PT_GNU_EH_FRAME, LIN, MSG_PT_GNU_EH_FRAME_CF },
{ PT_GNU_STACK, LIN, MSG_PT_GNU_STACK_CF },
{ PT_GNU_RELRO, LIN, MSG_PT_GNU_RELRO_CF },
+ { PT_PAX_FLAGS, LIN, MSG_PT_PAX_FLAGS_CF },
{ 0 }
};
@@ -123,6 +129,7 @@ error "PT_NUM has grown. Update phdrs[]"
{ PT_GNU_EH_FRAME, LIN, MSG_PT_GNU_EH_FRAME_CFNP },
{ PT_GNU_STACK, LIN, MSG_PT_GNU_STACK_CFNP },
{ PT_GNU_RELRO, LIN, MSG_PT_GNU_RELRO_CFNP },
+ { PT_PAX_FLAGS, LIN, MSG_PT_PAX_FLAGS_CFNP },
{ 0 }
};
@@ -137,6 +144,7 @@ error "PT_NUM has grown. Update phdrs[]"
{ PT_GNU_EH_FRAME, LIN, MSG_PT_GNU_EH_FRAME_NF },
{ PT_GNU_STACK, LIN, MSG_PT_GNU_STACK_NF },
{ PT_GNU_RELRO, LIN, MSG_PT_GNU_RELRO_NF },
+ { PT_PAX_FLAGS, LIN, MSG_PT_PAX_FLAGS_NF },
{ 0 }
};
@@ -214,6 +222,18 @@ conv_phdr_flags_strings(Conv_fmt_flags_t fmt_flags)
MSG_PF_X_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
MSG_PF_W_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
MSG_PF_R_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_PAGEEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_NOPAGEEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_SEGMEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_NOSEGMEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_MPROTECT_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_NOMPROTECT_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_RANDEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_NORANDEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_EMUTRAMP_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_NOEMUTRAMP_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_RANDMMAP_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
+ MSG_PF_NORANDMMAP_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
MSG_PF_SUNW_FAILURE_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
MSG_PF_SUNW_KILLED_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
MSG_PF_SUNW_SIGINFO_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \
@@ -237,11 +257,24 @@ conv_phdr_flags_strings(Conv_fmt_flags_t fmt_flags)
#define ALL ELFOSABI_NONE, EM_NONE
#define SOL ELFOSABI_SOLARIS, EM_NONE
+#define LIN ELFOSABI_LINUX, EM_NONE
static const Val_desc2 vda_cf[] = {
{ PF_X, ALL, MSG_PF_X_CF },
{ PF_W, ALL, MSG_PF_W_CF },
{ PF_R, ALL, MSG_PF_R_CF },
+ { PF_PAGEEXEC, LIN, MSG_PF_PAGEEXEC_CF },
+ { PF_NOPAGEEXEC, LIN, MSG_PF_NOPAGEEXEC_CF },
+ { PF_SEGMEXEC, LIN, MSG_PF_SEGMEXEC_CF },
+ { PF_NOSEGMEXEC, LIN, MSG_PF_NOSEGMEXEC_CF },
+ { PF_MPROTECT, LIN, MSG_PF_MPROTECT_CF },
+ { PF_NOMPROTECT, LIN, MSG_PF_NOMPROTECT_CF },
+ { PF_RANDEXEC, LIN, MSG_PF_RANDEXEC_CF },
+ { PF_NORANDEXEC, LIN, MSG_PF_NORANDEXEC_CF },
+ { PF_EMUTRAMP, LIN, MSG_PF_EMUTRAMP_CF },
+ { PF_NOEMUTRAMP, LIN, MSG_PF_NOEMUTRAMP_CF },
+ { PF_RANDMMAP, LIN, MSG_PF_RANDMMAP_CF },
+ { PF_NORANDMMAP, LIN, MSG_PF_NORANDMMAP_CF },
{ PF_SUNW_FAILURE, SOL, MSG_PF_SUNW_FAILURE_CF },
{ PF_SUNW_KILLED, SOL, MSG_PF_SUNW_KILLED_CF },
{ PF_SUNW_SIGINFO, SOL, MSG_PF_SUNW_SIGINFO_CF },
@@ -251,6 +284,18 @@ conv_phdr_flags_strings(Conv_fmt_flags_t fmt_flags)
{ PF_X, ALL, MSG_PF_X_NF },
{ PF_W, ALL, MSG_PF_W_NF },
{ PF_R, ALL, MSG_PF_R_NF },
+ { PF_PAGEEXEC, LIN, MSG_PF_PAGEEXEC_NF },
+ { PF_NOPAGEEXEC, LIN, MSG_PF_NOPAGEEXEC_NF },
+ { PF_SEGMEXEC, LIN, MSG_PF_SEGMEXEC_NF },
+ { PF_NOSEGMEXEC, LIN, MSG_PF_NOSEGMEXEC_NF },
+ { PF_MPROTECT, LIN, MSG_PF_MPROTECT_NF },
+ { PF_NOMPROTECT, LIN, MSG_PF_NOMPROTECT_NF },
+ { PF_RANDEXEC, LIN, MSG_PF_RANDEXEC_NF },
+ { PF_NORANDEXEC, LIN, MSG_PF_NORANDEXEC_NF },
+ { PF_EMUTRAMP, LIN, MSG_PF_EMUTRAMP_NF },
+ { PF_NOEMUTRAMP, LIN, MSG_PF_NOEMUTRAMP_NF },
+ { PF_RANDMMAP, LIN, MSG_PF_RANDMMAP_NF },
+ { PF_NORANDMMAP, LIN, MSG_PF_NORANDMMAP_NF },
{ PF_SUNW_FAILURE, SOL, MSG_PF_SUNW_FAILURE_NF },
{ PF_SUNW_KILLED, SOL, MSG_PF_SUNW_KILLED_NF },
{ PF_SUNW_SIGINFO, SOL, MSG_PF_SUNW_SIGINFO_NF },
@@ -262,6 +307,7 @@ conv_phdr_flags_strings(Conv_fmt_flags_t fmt_flags)
#undef ALL
#undef SOL
+#undef LIN
}
const char *
diff --git a/usr/src/cmd/sgs/libconv/common/phdr.msg b/usr/src/cmd/sgs/libconv/common/phdr.msg
index 789832a16a..e84182277b 100644
--- a/usr/src/cmd/sgs/libconv/common/phdr.msg
+++ b/usr/src/cmd/sgs/libconv/common/phdr.msg
@@ -24,6 +24,8 @@
# Use is subject to license terms.
#
+# Copyright (c) 2015, Joyent, Inc. All rights reserved.
+
@ MSG_PT_NULL "[ PT_NULL ]" # 0
@ MSG_PT_NULL_CF "PT_NULL"
@ MSG_PT_NULL_CFNP "NULL"
@@ -79,6 +81,10 @@
@ MSG_PT_GNU_RELRO_CF "PT_GNU_RELRO"
@ MSG_PT_GNU_RELRO_CFNP "GNU_RELRO"
@ MSG_PT_GNU_RELRO_NF "gnu_relro"
+@ MSG_PT_PAX_FLAGS "[ PT_PAX_FLAGS ]" # 0x65041580
+@ MSG_PT_PAX_FLAGS_CF "PT_PAX_FLAGS"
+@ MSG_PT_PAX_FLAGS_CFNP "PAX_FLAGS"
+@ MSG_PT_PAX_FLAGS_NF "pax_flags"
@ MSG_PT_SUNWBSS "[ PT_SUNWBSS ]" # 0x6ffffffa
@ MSG_PT_SUNWBSS_CF "PT_SUNWBSS"
@@ -103,6 +109,30 @@
@ MSG_PF_W_NF "w"
@ MSG_PF_R_CF "PF_R" # 0x4
@ MSG_PF_R_NF "r"
+@ MSG_PF_PAGEEXEC_CF "PF_PAGEEXEC" # 0x00000010
+@ MSG_PF_PAGEEXEC_NF "pageexec"
+@ MSG_PF_NOPAGEEXEC_CF "PF_NOPAGEEXEC" # 0x00000020
+@ MSG_PF_NOPAGEEXEC_NF "nopageexec"
+@ MSG_PF_SEGMEXEC_CF "PF_SEGMEXEC" # 0x00000040
+@ MSG_PF_SEGMEXEC_NF "segmexec"
+@ MSG_PF_NOSEGMEXEC_CF "PF_NOSEGMEXEC" # 0x00000080
+@ MSG_PF_NOSEGMEXEC_NF "nosegmexec"
+@ MSG_PF_MPROTECT_CF "PF_MPROTECT" # 0x00000100
+@ MSG_PF_MPROTECT_NF "mprotect"
+@ MSG_PF_NOMPROTECT_CF "PF_NOMPROTECT" # 0x00000200
+@ MSG_PF_NOMPROTECT_NF "nomprotect"
+@ MSG_PF_RANDEXEC_CF "PF_RANDEXEC" # 0x00000400
+@ MSG_PF_RANDEXEC_NF "randexec"
+@ MSG_PF_NORANDEXEC_CF "PF_NORANDEXEC" # 0x00000800
+@ MSG_PF_NORANDEXEC_NF "norandexec"
+@ MSG_PF_EMUTRAMP_CF "PF_EMUTRAMP" # 0x00001000
+@ MSG_PF_EMUTRAMP_NF "emutramp"
+@ MSG_PF_NOEMUTRAMP_CF "PF_NOEMUTRAMP" # 0x00002000
+@ MSG_PF_NOEMUTRAMP_NF "noemutramp"
+@ MSG_PF_RANDMMAP_CF "PF_RANDMMAP" # 0x00004000
+@ MSG_PF_RANDMMAP_NF "randmmap"
+@ MSG_PF_NORANDMMAP_CF "PF_NORANDMMAP" # 0x00008000
+@ MSG_PF_NORANDMMAP_NF "norandmmap"
@ MSG_PF_SUNW_FAILURE_CF "PF_SUNW_FAILURE" # 0x00100000
@ MSG_PF_SUNW_FAILURE_NF "sunw_failure"
@ MSG_PF_SUNW_KILLED_CF "PF_SUNW_KILLED" # 0x00200000
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 737fb2ee0f..c78d39e714 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 190483fa10..0ff1e3f804 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
@@ -584,6 +584,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 06b78023bd..4edbfb6d38 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 2015, Joyent, Inc.
*/
/*
@@ -833,6 +834,57 @@ 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;
+ size_t flen;
+ Pdesc *pdp;
+ Aliste idx;
+
+ fpath = PATHNAME(nlmp);
+
+ /*
+ * If we have a NULL path name, that indicates that rtld is processing
+ * an in-memory shared object. For example, trying to run ldd or doing
+ * an LD_PRELOAD on an object file. In those cases, we'll always allow
+ * it.
+ */
+ if (fpath == NULL)
+ return (0);
+
+ flen = strlen(fpath);
+
+ 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.
@@ -2158,6 +2210,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 3bf68a17da..88cdfd409c 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.
* Copyright 2022 Oxide Computer Company
*/
@@ -133,6 +134,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 f511806030..9d0876ba1b 100644
--- a/usr/src/cmd/sgs/rtld/common/rtld.msg
+++ b/usr/src/cmd/sgs/rtld/common/rtld.msg
@@ -100,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
@@ -401,6 +406,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_PTHS "TRACE_SEARCH_PATHS"
diff --git a/usr/src/cmd/sgs/rtld/common/setup.c b/usr/src/cmd/sgs/rtld/common/setup.c
index 1cbe41aa6b..96835607c1 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.
* Copyright 2022 Oxide Computer Company
*/
@@ -759,6 +759,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 654b626aa0..83a43896f3 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
@@ -1603,8 +1605,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;
}
}
@@ -1815,6 +1816,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/common/dextern.h b/usr/src/cmd/sgs/yacc/common/dextern.h
index dfabe65748..54b441cac4 100644
--- a/usr/src/cmd/sgs/yacc/common/dextern.h
+++ b/usr/src/cmd/sgs/yacc/common/dextern.h
@@ -26,6 +26,10 @@
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
#ifndef _DEXTERN_H
#define _DEXTERN_H
@@ -40,6 +44,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <wctype.h>
+#include <limits.h>
#ifdef __cplusplus
extern "C" {
@@ -299,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 */
@@ -322,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 09106fbc0d..d0ed9c85aa 100644
--- a/usr/src/cmd/sgs/yacc/common/y1.c
+++ b/usr/src/cmd/sgs/yacc/common/y1.c
@@ -26,12 +26,17 @@
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
#include "dextern.h"
#include <sys/param.h>
#include <sys/errno.h>
#include <unistd.h>
#include <locale.h>
#include <stdarg.h> /* For error() */
+#include <libgen.h>
static void mktbls(void);
static void others(void);
@@ -235,6 +240,25 @@ mktbls(void)
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(void)
@@ -243,7 +267,17 @@ others(void)
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 954cee6fa8..480d637dab 100644
--- a/usr/src/cmd/sgs/yacc/common/y2.c
+++ b/usr/src/cmd/sgs/yacc/common/y2.c
@@ -26,6 +26,10 @@
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
+
#include "dextern.h"
#include "sgs.h"
#include <stdio.h>
@@ -58,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/smbios/Makefile b/usr/src/cmd/smbios/Makefile
index 9b2f9fb528..391fe3e53f 100644
--- a/usr/src/cmd/smbios/Makefile
+++ b/usr/src/cmd/smbios/Makefile
@@ -22,6 +22,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2018 Joyent, Inc.
+#
PROG = smbios
OBJS = smbios.o
@@ -35,6 +37,8 @@ LDLIBS += -lsmbios -ljedec
FILEMODE = 0555
STRIPFLAG =
+LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN
+
.KEEP_STATE:
all: $(PROG)
diff --git a/usr/src/cmd/smbios/smbios.c b/usr/src/cmd/smbios/smbios.c
index b93e647e25..1d3a38892d 100644
--- a/usr/src/cmd/smbios/smbios.c
+++ b/usr/src/cmd/smbios/smbios.c
@@ -21,7 +21,7 @@
/*
* Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved.
- * Copyright (c) 2017, Joyent, Inc.
+ * Copyright (c) 2018, Joyent, Inc.
* Copyright 2022 Oxide Computer Company
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -431,10 +431,30 @@ print_system(smbios_hdl_t *shp, FILE *fp)
return;
}
+ /*
+ * SMBIOS definition section 3.3.2.1 is clear that the first three
+ * fields are little-endian, but this utility traditionally got this
+ * wrong, and followed RFC 4122. We keep this old behavior, but also
+ * provide a corrected UUID.
+ */
oprintf(fp, " UUID: ");
- for (i = 0; i < s.smbs_uuidlen; i++) {
+ oprintf(fp, "%02x%02x%02x%02x-%02x%02x-%02x%02x-",
+ s.smbs_uuid[0], s.smbs_uuid[1], s.smbs_uuid[2], s.smbs_uuid[3],
+ s.smbs_uuid[4], s.smbs_uuid[5], s.smbs_uuid[6], s.smbs_uuid[7]);
+ for (i = 8; i < s.smbs_uuidlen; i++) {
+ oprintf(fp, "%02x", s.smbs_uuid[i]);
+ if (i == 9)
+ oprintf(fp, "-");
+ }
+ oprintf(fp, "\n");
+
+ oprintf(fp, " UUID (Endian-corrected): ");
+ oprintf(fp, "%08x-%04hx-%04hx-", *((uint_t *)&s.smbs_uuid[0]),
+ *((ushort_t *)&s.smbs_uuid[4]),
+ *((ushort_t *)&s.smbs_uuid[6]));
+ for (i = 8; i < s.smbs_uuidlen; i++) {
oprintf(fp, "%02x", s.smbs_uuid[i]);
- if (i == 3 || i == 5 || i == 7 || i == 9)
+ if (i == 9)
oprintf(fp, "-");
}
oprintf(fp, "\n");
diff --git a/usr/src/cmd/ssh/THIRDPARTYLICENSE.descrip b/usr/src/cmd/ssh/THIRDPARTYLICENSE.descrip
deleted file mode 100644
index 7e936fffc7..0000000000
--- a/usr/src/cmd/ssh/THIRDPARTYLICENSE.descrip
+++ /dev/null
@@ -1 +0,0 @@
-OPENSSH SOFTWARE
diff --git a/usr/src/cmd/ssh/doc/LICENCE b/usr/src/cmd/ssh/doc/LICENCE
deleted file mode 100644
index 04d6fe18e3..0000000000
--- a/usr/src/cmd/ssh/doc/LICENCE
+++ /dev/null
@@ -1,194 +0,0 @@
-This file is part of the ssh software.
-
-The licences which components of this software falls under are as
-follows. First, we will summarize and say that that all components
-are under a BSD licence, or a licence more free than that.
-
-OpenSSH contains no GPL code.
-
-1)
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- * All rights reserved
- *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose. Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
-
- [Tatu continues]
- * However, I am not implying to give any licenses to any patents or
- * copyrights held by third parties, and the software includes parts that
- * are not under my direct control. As far as I know, all included
- * source code is used in accordance with the relevant license agreements
- * and can be used freely for any purpose (the GNU license being the most
- * restrictive); see below for details.
-
- [However, none of that term is relevant at this point in time. All of
- these restrictively licenced software components which he talks about
- have been removed from OpenSSH, ie.
-
- - RSA is no longer included, found in the OpenSSL library
- - IDEA is no longer included, it's use is depricated
- - DES is now external, in the OpenSSL library
- - GMP is no longer used, and instead we call BN code from OpenSSL
- - Zlib is now external, in a library
- - The make-ssh-known-hosts script is no longer included
- - TSS has been removed
- - MD5 is now external, in the OpenSSL library
- - RC4 support has been replaced with ARC4 support from OpenSSL
- - Blowfish is now external, in the OpenSSL library
-
- [The licence continues]
-
- Note that any information and cryptographic algorithms used in this
- software are publicly available on the Internet and at any major
- bookstore, scientific library, and patent office worldwide. More
- information can be found e.g. at "http://www.cs.hut.fi/crypto".
-
- The legal status of this program is some combination of all these
- permissions and restrictions. Use only at your own responsibility.
- You will be responsible for any legal consequences yourself; I am not
- making any claims whether possessing or using this is legal or not in
- your country, and I am not taking any responsibility on your behalf.
-
-
- NO WARRANTY
-
- BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
- FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
- OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
- PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
- OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
- TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
- PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
- REPAIR OR CORRECTION.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
- WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
- REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
- INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
- OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
- TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
- YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
- PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGES.
-
-2)
- The 32-bit CRC implementation in crc32.c is due to Gary S. Brown.
- Comments in the file indicate it may be used for any purpose without
- restrictions:
-
- * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
- * code or tables extracted from it, as desired without restriction.
-
-3)
- The 32-bit CRC compensation attack detector in deattack.c was
- contributed by CORE SDI S.A. under a BSD-style license. See
- http://www.core-sdi.com/english/ssh/ for details.
-
- * Cryptographic attack detector for ssh - source code
- *
- * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina.
- *
- * All rights reserved. Redistribution and use in source and binary
- * forms, with or without modification, are permitted provided that
- * this copyright notice is retained.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
- * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS
- * SOFTWARE.
- *
- * Ariel Futoransky <futo@core-sdi.com>
- * <http://www.core-sdi.com>
-
-3a)
- Various parts are from the University of California.
-
- * Copyright (c) 1983, 1987, 1989-1995
- * 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.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 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.
-
- * Copyright (c) 1989, 1991, 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.
- *
- * 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.
-
-4)
- Remaining components of the software are provided under a standard
- 2-term BSD licence with the following names as copyright holders:
-
- Markus Friedl
- Theo de Raadt
- Niels Provos
- Dug Song
- Aaron Campbell
-
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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/ssh/etc/ssh.xml b/usr/src/cmd/ssh/etc/ssh.xml
deleted file mode 100644
index c941a18550..0000000000
--- a/usr/src/cmd/ssh/etc/ssh.xml
+++ /dev/null
@@ -1,177 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
-<!--
- 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 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
-
- NOTE: This service manifest is not editable; its contents will
- be overwritten by package or patch operations, including
- operating system upgrade. Make customizations in a different
- file.
--->
-
-<service_bundle type='manifest' name='SUNWsshdr:ssh'>
-
-<service
- name='network/ssh'
- type='service'
- version='1'>
-
- <create_default_instance enabled='false' />
-
- <single_instance />
-
- <dependency name='fs-local'
- grouping='require_all'
- restart_on='none'
- type='service'>
- <service_fmri
- value='svc:/system/filesystem/local' />
- </dependency>
-
- <dependency name='fs-autofs'
- grouping='optional_all'
- restart_on='none'
- type='service'>
- <service_fmri value='svc:/system/filesystem/autofs' />
- </dependency>
-
- <dependency name='net-loopback'
- grouping='require_all'
- restart_on='none'
- type='service'>
- <service_fmri value='svc:/network/loopback' />
- </dependency>
-
- <dependency name='net-physical'
- grouping='require_all'
- restart_on='none'
- type='service'>
- <service_fmri value='svc:/network/physical' />
- </dependency>
-
- <dependency name='cryptosvc'
- grouping='require_all'
- restart_on='none'
- type='service'>
- <service_fmri value='svc:/system/cryptosvc' />
- </dependency>
-
- <dependency name='utmp'
- grouping='require_all'
- restart_on='none'
- type='service'>
- <service_fmri value='svc:/system/utmp' />
- </dependency>
-
- <dependency name='network_ipfilter'
- grouping='optional_all'
- restart_on='error'
- type='service'>
- <service_fmri value='svc:/network/ipfilter:default' />
- </dependency>
-
- <dependency name='config_data'
- grouping='require_all'
- restart_on='restart'
- type='path'>
- <service_fmri
- value='file://localhost/etc/ssh/sshd_config' />
- </dependency>
-
- <dependent
- name='ssh_multi-user-server'
- grouping='optional_all'
- restart_on='none'>
- <service_fmri
- value='svc:/milestone/multi-user-server' />
- </dependent>
-
- <exec_method
- type='method'
- name='start'
- exec='/lib/svc/method/sshd start'
- timeout_seconds='60'/>
-
- <exec_method
- type='method'
- name='stop'
- exec=':kill'
- timeout_seconds='60' />
-
- <exec_method
- type='method'
- name='refresh'
- exec='/lib/svc/method/sshd restart'
- timeout_seconds='60' />
-
- <property_group name='startd'
- type='framework'>
- <!-- sub-process core dumps shouldn't restart session -->
- <propval name='ignore_error'
- type='astring' value='core,signal' />
- </property_group>
-
- <property_group name='general' type='framework'>
- <!-- to start stop sshd -->
- <propval name='action_authorization' type='astring'
- value='solaris.smf.manage.ssh' />
- </property_group>
-
- <property_group name='firewall_context' type='com.sun,fw_definition'>
- <propval name='name' type='astring' value='ssh' />
- <propval name='ipf_method' type='astring'
- value='/lib/svc/method/sshd ipfilter' />
- </property_group>
-
- <property_group name='firewall_config' type='com.sun,fw_configuration'>
- <propval name='policy' type='astring' value='use_global' />
- <propval name='block_policy' type='astring'
- value='use_global' />
- <propval name='apply_to' type='astring' value='' />
- <propval name='apply_to_6' type='astring' value='' />
- <propval name='exceptions' type='astring' value='' />
- <propval name='exceptions_6' type='astring' value='' />
- <propval name='target' type='astring' value='' />
- <propval name='target_6' type='astring' value='' />
- <propval name='value_authorization' type='astring'
- value='solaris.smf.value.firewall.config' />
- </property_group>
-
- <stability value='Unstable' />
-
- <template>
- <common_name>
- <loctext xml:lang='C'>
- SSH server
- </loctext>
- </common_name>
- <documentation>
- <manpage title='sshd' section='8' manpath='/usr/share/man' />
- </documentation>
- </template>
-
-</service>
-
-</service_bundle>
diff --git a/usr/src/cmd/ssh/etc/ssh_config b/usr/src/cmd/ssh/etc/ssh_config
deleted file mode 100644
index cdb9d97d45..0000000000
--- a/usr/src/cmd/ssh/etc/ssh_config
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (c) 2001 by Sun Microsystems, Inc.
-# All rights reserved.
-#
-# ident "%Z%%M% %I% %E% SMI"
-#
-# This file provides defaults for ssh(1).
-# The values can be changed in per-user configuration files $HOME/.ssh/config
-# or on the command line of ssh(1).
-
-# Configuration data is parsed as follows:
-# 1. command line options
-# 2. user-specific file
-# 3. system-wide file /etc/ssh/ssh_config
-#
-# Any configuration value is only changed the first time it is set.
-# host-specific definitions should be at the beginning of the
-# configuration file, and defaults at the end.
-
-# Example (matches compiled in defaults):
-#
-# Host *
-# ForwardAgent no
-# ForwardX11 no
-# PubkeyAuthentication yes
-# PasswordAuthentication yes
-# FallBackToRsh no
-# UseRsh no
-# BatchMode no
-# CheckHostIP yes
-# StrictHostKeyChecking ask
-# EscapeChar ~
diff --git a/usr/src/cmd/ssh/etc/sshd b/usr/src/cmd/ssh/etc/sshd
deleted file mode 100644
index 0cc1d90267..0000000000
--- a/usr/src/cmd/ssh/etc/sshd
+++ /dev/null
@@ -1,127 +0,0 @@
-#!/sbin/sh
-#
-# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
-#
-# Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
-#
-
-. /lib/svc/share/ipf_include.sh
-. /lib/svc/share/smf_include.sh
-
-SSHDIR=/etc/ssh
-KEYGEN="/usr/bin/ssh-keygen -q"
-PIDFILE=/var/run/sshd.pid
-
-# Checks to see if RSA, and DSA host keys are available
-# if any of these keys are not present, the respective keys are created.
-create_key()
-{
- keypath=$1
- keytype=$2
-
- if [ ! -f $keypath ]; then
- #
- # HostKey keywords in sshd_config may be preceded or
- # followed by a mix of any number of space or tabs,
- # and optionally have an = between keyword and
- # argument. We use two grep invocations such that we
- # can match HostKey case insensitively but still have
- # the case of the path name be significant, keeping
- # the pattern somewhat more readable.
- #
- # The character classes below contain one literal
- # space and one literal tab.
- #
- grep -i "^[ ]*HostKey[ ]*=\{0,1\}[ ]*$keypath" \
- $SSHDIR/sshd_config | grep "$keypath" > /dev/null 2>&1
-
- if [ $? -eq 0 ]; then
- echo Creating new $keytype public/private host key pair
- $KEYGEN -f $keypath -t $keytype -N ''
- if [ $? -ne 0 ]; then
- echo "Could not create $keytype key: $keypath"
- exit $SMF_EXIT_ERR_CONFIG
- fi
- fi
- fi
-}
-
-create_ipf_rules()
-{
- FMRI=$1
- ipf_file=`fmri_to_file ${FMRI} $IPF_SUFFIX`
- ipf6_file=`fmri_to_file ${FMRI} $IPF6_SUFFIX`
- policy=`get_policy ${FMRI}`
-
- #
- # Get port from /etc/ssh/sshd_config
- #
- tports=`grep "^Port" /etc/ssh/sshd_config 2>/dev/null | \
- awk '{print $2}'`
-
- echo "# $FMRI" >$ipf_file
- echo "# $FMRI" >$ipf6_file
- for port in $tports; do
- generate_rules $FMRI $policy "tcp" $port $ipf_file
- generate_rules $FMRI $policy "tcp" $port $ipf6_file _6
- done
-}
-
-# This script is being used for two purposes: as part of an SMF
-# start/stop/refresh method, and as a sysidconfig(8)/sys-unconfig(8)
-# application.
-#
-# Both, the SMF methods and sysidconfig/sys-unconfig use different
-# arguments..
-
-case $1 in
- # sysidconfig/sys-unconfig arguments (-c and -u)
-'-c')
- /usr/bin/ssh-keygen -A
- if [ $? -ne 0 ]; then
- create_key $SSHDIR/ssh_host_rsa_key rsa
- create_key $SSHDIR/ssh_host_dsa_key dsa
- fi
- ;;
-
-'-u')
- # sys-unconfig(8) knows how to remove ssh host keys, so there's
- # nothing to do here.
- :
- ;;
-
- # SMF arguments (start and restart [really "refresh"])
-
-'ipfilter')
- create_ipf_rules $2
- ;;
-
-'start')
- #
- # If host keys don't exist when the service is started, create
- # them; sysidconfig is not run in every situation (such as on
- # the install media).
- #
- /usr/bin/ssh-keygen -A
- if [ $? -ne 0 ]; then
- create_key $SSHDIR/ssh_host_rsa_key rsa
- create_key $SSHDIR/ssh_host_dsa_key dsa
- fi
-
- /usr/lib/ssh/sshd
- ;;
-
-'restart')
- if [ -f "$PIDFILE" ]; then
- /usr/bin/kill -HUP `/usr/bin/cat $PIDFILE`
- fi
- ;;
-
-*)
- echo "Usage: $0 { start | restart }"
- exit 1
- ;;
-esac
-
-exit $?
diff --git a/usr/src/cmd/ssh/etc/sshd_config b/usr/src/cmd/ssh/etc/sshd_config
deleted file mode 100644
index 60d3941ebb..0000000000
--- a/usr/src/cmd/ssh/etc/sshd_config
+++ /dev/null
@@ -1,145 +0,0 @@
-#
-# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
-#
-# Configuration file for sshd(8) (see also sshd_config(5))
-#
-
-# Protocol versions supported
-#
-# The sshd shipped in this release of Solaris has support for major versions
-# 1 and 2. It is recommended due to security weaknesses in the v1 protocol
-# that sites run only v2 if possible. Support for v1 is provided to help sites
-# with existing ssh v1 clients/servers to transition.
-# Support for v1 may not be available in a future release of Solaris.
-#
-# To enable support for v1 an RSA1 key must be created with ssh-keygen(1).
-# RSA and DSA keys for protocol v2 are created by /etc/init.d/sshd if they
-# do not already exist, RSA1 keys for protocol v1 are not automatically created.
-
-# Uncomment ONLY ONE of the following Protocol statements.
-
-# Only v2 (recommended)
-Protocol 2
-
-# Both v1 and v2 (not recommended)
-#Protocol 2,1
-
-# Only v1 (not recommended)
-#Protocol 1
-
-# Listen port (the IANA registered port number for ssh is 22)
-Port 22
-
-# The default listen address is all interfaces, this may need to be changed
-# if you wish to restrict the interfaces sshd listens on for a multi homed host.
-# Multiple ListenAddress entries are allowed.
-
-# IPv4 only
-#ListenAddress 0.0.0.0
-# IPv4 & IPv6
-ListenAddress ::
-
-# If port forwarding is enabled (default), specify if the server can bind to
-# INADDR_ANY.
-# This allows the local port forwarding to work when connections are received
-# from any remote host.
-GatewayPorts no
-
-# X11 tunneling options
-X11Forwarding yes
-X11DisplayOffset 10
-X11UseLocalhost yes
-
-# The maximum number of concurrent unauthenticated connections to sshd.
-# start:rate:full see sshd(1) for more information.
-# The default is 10 unauthenticated clients.
-#MaxStartups 10:30:60
-
-# Banner to be printed before authentication starts.
-#Banner /etc/issue
-
-# Should sshd print the /etc/motd file and check for mail.
-# On Solaris it is assumed that the login shell will do these (eg /etc/profile).
-PrintMotd no
-
-# KeepAlive specifies whether keep alive messages are sent to the client.
-# See sshd(1) for detailed description of what this means.
-# Note that the client may also be sending keep alive messages to the server.
-KeepAlive yes
-
-# Syslog facility and level
-SyslogFacility auth
-LogLevel info
-
-#
-# Authentication configuration
-#
-
-# Host private key files
-# Must be on a local disk and readable only by the root user (root:sys 600).
-HostKey /etc/ssh/ssh_host_rsa_key
-HostKey /etc/ssh/ssh_host_dsa_key
-
-# Length of the server key
-# Default 768, Minimum 512
-ServerKeyBits 768
-
-# sshd regenerates the key every KeyRegenerationInterval seconds.
-# The key is never stored anywhere except the memory of sshd.
-# The default is 1 hour (3600 seconds).
-KeyRegenerationInterval 3600
-
-# Ensure secure permissions on users .ssh directory.
-StrictModes yes
-
-# Length of time in seconds before a client that hasn't completed
-# authentication is disconnected.
-# Default is 600 seconds. 0 means no time limit.
-LoginGraceTime 600
-
-# Maximum number of retries for authentication
-# Default is 6. Default (if unset) for MaxAuthTriesLog is MaxAuthTries / 2
-MaxAuthTries 6
-MaxAuthTriesLog 3
-
-# Are logins to accounts with empty passwords allowed.
-# If PermitEmptyPasswords is no, pass PAM_DISALLOW_NULL_AUTHTOK
-# to pam_authenticate(3PAM).
-PermitEmptyPasswords no
-
-# To disable tunneled clear text passwords, change PasswordAuthentication to no.
-PasswordAuthentication yes
-
-# Are root logins permitted using sshd.
-# Note that sshd uses pam_authenticate(3PAM) so the root (or any other) user
-# maybe denied access by a PAM module regardless of this setting.
-# Valid options are yes, without-password, no.
-PermitRootLogin no
-
-# sftp subsystem
-Subsystem sftp internal-sftp
-
-
-# SSH protocol v1 specific options
-#
-# The following options only apply to the v1 protocol and provide
-# some form of backwards compatibility with the very weak security
-# of /usr/bin/rsh. Their use is not recommended and the functionality
-# will be removed when support for v1 protocol is removed.
-
-# Should sshd use .rhosts and .shosts for password less authentication.
-IgnoreRhosts yes
-RhostsAuthentication no
-
-# Rhosts RSA Authentication
-# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts.
-# If the user on the client side is not root then this won't work on
-# Solaris since /usr/bin/ssh is not installed setuid.
-RhostsRSAAuthentication no
-
-# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication.
-#IgnoreUserKnownHosts yes
-
-# Is pure RSA authentication allowed.
-# Default is yes
-RSAAuthentication yes
diff --git a/usr/src/cmd/stat/Makefile b/usr/src/cmd/stat/Makefile
index b127c9e697..bcb031a98e 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 8185c35125..3dcc4119d8 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 d4f12a9e1c..d4f12a9e1c 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/common/notify_params.c b/usr/src/cmd/svc/common/notify_params.c
index 16104d899e..73ea6ccc5a 100644
--- a/usr/src/cmd/svc/common/notify_params.c
+++ b/usr/src/cmd/svc/common/notify_params.c
@@ -287,7 +287,7 @@ listnotify_print(nvlist_t *nvl, const char *event)
{
char *fmri;
nvlist_t **params;
- size_t n;
+ uint_t n;
int32_t tset;
int i;
diff --git a/usr/src/cmd/svc/configd/backend.c b/usr/src/cmd/svc/configd/backend.c
index 466d2d54cc..40726dd1d0 100644
--- a/usr/src/cmd/svc/configd/backend.c
+++ b/usr/src/cmd/svc/configd/backend.c
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc.
*/
/*
@@ -2194,7 +2195,24 @@ backend_tx_begin(backend_type_t t, backend_tx_t **txp)
UPDATE_TOTALS((*txp)->bt_be, bt_exec, ts, vts);
if (r == SQLITE_FULL)
(*txp)->bt_full = 1;
- r = backend_error((*txp)->bt_be, r, errmsg);
+ /*
+ * We explicitly handle an ENOSPC error here for the beginning of the
+ * transaction, instead of in backend_error, which calls backend_panic
+ * for this case, resulting in the death of svc.configd. That may be
+ * appropriate in other cases, but in this case we would rather fail so
+ * that configd remains up and the caller gets an approprate error. The
+ * failure mode is that there is not enough swap space to open the
+ * non-persistent database, so there won't be enough space to restart
+ * configd, leaving SMF in a state requiring manual intervention.
+ */
+ if (r == SQLITE_CANTOPEN && errno == ENOSPC &&
+ (*txp)->bt_type == BACKEND_TYPE_NONPERSIST) {
+ configd_info("Warning: no space to open %s\n",
+ bes[BACKEND_TYPE_NONPERSIST]->be_path);
+ r = REP_PROTOCOL_FAIL_NO_RESOURCES;
+ } else {
+ r = backend_error((*txp)->bt_be, r, errmsg);
+ }
if (r != REP_PROTOCOL_SUCCESS) {
assert(r != REP_PROTOCOL_DONE);
diff --git a/usr/src/cmd/svc/configd/rc_node.c b/usr/src/cmd/svc/configd/rc_node.c
index a5b968c53c..33cb2be7a2 100644
--- a/usr/src/cmd/svc/configd/rc_node.c
+++ b/usr/src/cmd/svc/configd/rc_node.c
@@ -24,6 +24,9 @@
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
* Copyright (c) 2016 by Delphix. 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/Makefile b/usr/src/cmd/svc/milestone/Makefile
index 901727dc9f..576576ba2e 100644
--- a/usr/src/cmd/svc/milestone/Makefile
+++ b/usr/src/cmd/svc/milestone/Makefile
@@ -21,6 +21,8 @@
#
# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
#
+# Copyright 2019 Joyent, Inc.
+#
include ../../Makefile.cmd
@@ -30,6 +32,7 @@ BUILTXML= \
console-login.xml
FSSVCS= \
+ joyent-fs.xml \
local-fs.xml \
minimal-fs.xml \
root-fs.xml \
@@ -38,6 +41,7 @@ FSSVCS= \
FSMANIFESTS= $(FSSVCS:%=$(ROOTSVCSYSTEMFILESYSTEM)/%)
NETSVCS= \
+ network-early-admin.xml \
network-initial.xml \
network-install.xml \
network-iptun.xml \
@@ -75,8 +79,12 @@ SYSTEMSVCS= \
early-manifest-import.xml \
identity.xml \
manifest-import.xml \
+ mdata.xml \
process-security.xml \
rmtmpfiles.xml \
+ smartdc-config.xml \
+ smartdc-init.xml \
+ smartdc-ur.xml \
vtdaemon.xml
SYSTEMMANIFESTS = $(SYSTEMSVCS:%=$(ROOTSVCSYSTEM)/%)
@@ -103,6 +111,7 @@ SVCMETHOD=\
console-login \
devices-audio \
devices-local \
+ fs-joyent \
fs-local \
fs-minimal \
fs-root \
@@ -110,6 +119,9 @@ SVCMETHOD=\
identity-domain \
identity-node \
manifest-import \
+ mdata-execute \
+ mdata-fetch \
+ net-early-admin \
net-loc \
net-loopback \
net-init \
@@ -122,6 +134,11 @@ SVCMETHOD=\
net-routing-setup \
net-svc \
rmtmpfiles \
+ smartdc-config \
+ smartdc-init \
+ smartdc-ur \
+ sysidtool-net \
+ sysidtool-system \
vtdaemon
$(ROOTSVCMETHOD) := FILEMODE = 0555
@@ -161,4 +178,4 @@ $(ROOTSVCSYSTEM)/svc/%: %
$(ROOT)/lib/svc/share/%: %.share
$(INS.rename)
-clean lint _msg:
+clean _msg:
diff --git a/usr/src/cmd/svc/milestone/console-login b/usr/src/cmd/svc/milestone/console-login
index 81010231b5..8cc6495b2d 100644
--- a/usr/src/cmd/svc/milestone/console-login
+++ b/usr/src/cmd/svc/milestone/console-login
@@ -73,6 +73,20 @@ if [ "$val" = "/dev/vt/1" ]; then
exit $SMF_EXIT_ERR_CONFIG
fi
+# In SmartOS we use ttyb for metadata so we set to a non-existent device here
+# to disable the service and exit.
+if [[ ${val} == "/dev/term/b" && \
+ $(sysinfo | json "Product") == "SmartDC HVM" ]]; then
+
+ val=/dev/do_not_use_ttyb_in_a_vm
+fi
+
+if [[ ! -e $val ]]; then
+ # This device doesn't exist, can't run a tty on it.
+ /usr/sbin/svcadm disable $SMF_FMRI
+ exit $SMF_EXIT_OK
+fi
+
args="$args -d $val"
args="$args `getproparg -l ttymon/label`"
diff --git a/usr/src/cmd/svc/milestone/fs-joyent b/usr/src/cmd/svc/milestone/fs-joyent
new file mode 100755
index 0000000000..0a9dc065a3
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/fs-joyent
@@ -0,0 +1,312 @@
+#!/bin/bash
+#
+# 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 2020 Joyent, Inc.
+#
+
+set -o xtrace
+
+fatal()
+{
+ echo "Error: $1"
+ exit $SMF_EXIT_ERR_FATAL
+}
+
+. /lib/svc/share/smf_include.sh
+. /lib/svc/share/fs_include.sh
+. /lib/sdc/usb-key.sh
+
+# first of all, if we aren't the global zone this doesn't make any sense to run
+
+smf_is_globalzone || exit $SMF_EXIT_OK
+
+# We need the links to /dev/dsk. Rather than trying to play games with manually
+# invoking syseventd ask devfsadm to do some work.
+/usr/sbin/devfsadm -c disk
+
+function destroy_zpools
+{
+ for pool in $(zpool list -p -o name | grep -v NAME) ; do
+ zpool destroy -f ${pool}
+ done
+}
+
+function mount_zfs
+{
+ local dataset=$1
+ local mountpoint=$2
+ local output=
+
+ #
+ # Try to mount the ZFS dataset. If the mountpoint is busy, wait five
+ # seconds and try again. Fail if the mount attempt returns EBUSY three
+ # consecutive times.
+ #
+ for i in {1..3}; do
+ output=$(mount -F zfs ${dataset} ${mountpoint} 2>&1)
+ if [[ $? -eq 0 ]]; then
+ break
+ fi
+
+ if [ "${output}" == "mount failed: Device busy" ]; then
+ sleep 5
+ else
+ echo ${output} 1>&2
+ return
+ fi
+ done
+
+ # The mount attempt must have failed
+ echo ${output} 1>&2
+}
+
+function unlock_pool
+{
+ local pool=$1
+
+ # If the key is already loaded, don't bother trying again
+ local keystatus="$(zfs get -Hpo value keystatus $pool)"
+ if [[ "$keystatus" == "available" ]]; then
+ return
+ fi
+
+ kbmadm unlock $pool && return
+
+ echo "Failed to unlock $pool; recovery may be required" | \
+ tee -a /dev/console >&2
+
+ exit $SMF_EXIT_ERR_FATAL
+}
+
+/bin/bootparams | grep "^noimport=true" >/dev/null
+if [ $? -ne 0 ]; then
+ # If the zpool doesn't exist, then there's nothing to mount.
+
+ # Assume the system zpool is zones, but if a different system pool
+ # identifies itself (by virtue of the .system_pool file being present in the
+ # pool's root dataset), then use that system pool instead.
+ SYS_ZPOOL=zones
+
+ # Import specified zpools, or all zpools available
+ pools=$(/bin/bootparams | egrep "^zpools?=" | cut -d= -f2 | tr , ' ')
+ if [ -z ${pools} ]; then
+ pools=$(zpool import | grep "pool:" | awk '{print $2}')
+ fi
+
+ for pool in $pools; do
+ zpool import -f $pool || continue
+
+ is_encr="$(zfs get -Hpo value encryption $pool)"
+
+ [[ "$is_encr" != "off" ]] && unlock_pool $pool
+
+ # Due to early, failed attempts to support the filesystem_limits
+ # feature we now need to ensure the dependent feature is enabled.
+ zpool set feature@extensible_dataset=enabled $pool
+ if [[ -f /$pool/.system_pool ]]; then
+ SYS_ZPOOL=$pool
+ [[ "$is_encr" != "off" ]] && kbmadm set-syspool $pool
+ fi
+ done
+
+ svccfg -s svc:/system/smartdc/init setprop \
+ config/zpool=${SYS_ZPOOL}
+ svccfg -s svc:/system/smartdc/init:default refresh
+
+ # If the destroy_zpools boot parameter is set, destroy all zpools
+ /bin/bootparams | grep "^destroy_zpools=true" >/dev/null
+ if [ $? -eq 0 ]; then
+ destroy_zpools
+ fi
+
+ # A machine is reset to its original unsetup state (i.e. a 'factory reset')
+ # when the smartdc:factoryreset ZFS user property is set on the var dataset.
+ reset=$(zfs get -H -o value smartdc:factoryreset ${SYS_ZPOOL}/var)
+ if [ "${reset}" == "yes" ]; then
+ destroy_zpools
+ fi
+
+ # Capture the zpool's status output in the method's log file for
+ # troubleshooting.
+ #
+ # Note: It is critical that we do not run 'status -v'. If there are errors
+ # in the zpool error log and the zpool is large (e.g. > 200TB), then the
+ # lookup for the error file names can take a very long time (several hours).
+ # This would block the system boot until it completed.
+ zpool status ${SYS_ZPOOL}
+ if [ $? -eq 0 ]; then
+
+ # Stash the SUNWdefault.xml file so we can update the
+ # persistent version after mounting zones/config.
+ cp /etc/zones/SUNWdefault.xml /tmp/
+
+ # Mount and configure all system datasets
+ mount_zfs ${SYS_ZPOOL}/var /var
+ mount_zfs ${SYS_ZPOOL}/config /etc/zones
+ mount_zfs ${SYS_ZPOOL}/opt /opt
+
+ # Update the the persistent SUNWdefault.xml file to match the
+ # contents on ramdisk now that zones/config is mounted.
+ cp /tmp/SUNWdefault.xml /etc/zones/
+ rm -f /tmp/SUNWdefault.xml
+
+ #
+ # We include a manifest of all files shipped in the platform image,
+ # along with an MD5 hash of their contents. This was originally
+ # shipped as "/var/log/manifest", but once a machine is set up, "/var"
+ # now comes from the pool. The upshot of this is that every SmartOS
+ # machine has the manifest from the platform at setup time stored in
+ # "/var/log/manifest". Now that the manifest has moved to an
+ # accessible location, we should remove this file and replace it with a
+ # symbolic link.
+ #
+ if [[ -f '/var/log/manifest' && ! -L '/var/log/manifest' &&
+ ! -e '/var/log/manifest.original' ]]; then
+ mv '/var/log/manifest' '/var/log/manifest.original'
+ ln -s '../../usr/share/smartos/manifest' '/var/log/manifest'
+ fi
+
+ if [[ -z $(/bin/bootparams | grep '^smartos=true') ]]; then
+ mkdir -p /opt/smartdc/agents/smf
+ mount -O -F lofs /var/svc/manifest/site /opt/smartdc/agents/smf
+ fi
+
+ if [[ -n $(/bin/bootparams | grep '^headnode=true') || \
+ -n $(/bin/bootparams | grep '^smartos=true') ]]; then
+ mkdir /usbkey
+ mount_zfs ${SYS_ZPOOL}/usbkey /usbkey
+ fi
+
+ if [[ -n $(/bin/bootparams | grep '^smartos=true') ]]; then
+ mount -F lofs /usbkey/shadow /etc/shadow
+ mount -F lofs /usbkey/ssh /etc/ssh
+ fi
+
+ swap -a /dev/zvol/dsk/${SYS_ZPOOL}/swap || \
+ fatal "failed to configure swap device"
+
+ #
+ # Configure the dump device on top of a ZFS volume. In addition to the
+ # usual dumpadm(1m) call, there are two prerequisites for using this
+ # volume as a dump device: (1) that zvol must be using the noparity
+ # checksum algorithem, and (2) the MULTI_VDEV_CRASH_DUMP ZFS feature
+ # must be enabled. Prerequisite (1) is necessary since the exact
+ # on-disk value for ZIO_CHECKSUM_NOPARITY has changed, so to avoid a
+ # flag day on all systems, this service just sets that property again
+ # every time.
+ #
+ zfs set checksum=noparity ${SYS_ZPOOL}/dump || \
+ fatal "failed to set checksum=noparity on dump zvol"
+ zpool set feature@multi_vdev_crash_dump=enabled ${SYS_ZPOOL} || \
+ fatal "failed to enable multi_vdev_crash_dump ZFS feature"
+ dumpadm -y -d /dev/zvol/dsk/${SYS_ZPOOL}/dump || \
+ fatal "failed to configure dump device"
+
+ zfs list -H -o name ${SYS_ZPOOL}/cores/global >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ # Booting for the first time on a CN whose cores dataset is setup
+ # in the 6.x style. Convert to the new style.
+ zfs destroy -r ${SYS_ZPOOL}/cores
+ zfs create -o compression=gzip -o mountpoint=none ${SYS_ZPOOL}/cores
+ zfs create -o quota=10g -o mountpoint=/${SYS_ZPOOL}/global/cores \
+ ${SYS_ZPOOL}/cores/global
+ fi
+
+ ln -s /${SYS_ZPOOL}/global/cores /cores
+
+ [[ -f /${SYS_ZPOOL}/currbooted ]] && \
+ mv /${SYS_ZPOOL}/currbooted /${SYS_ZPOOL}/lastbooted
+ uname -v >/${SYS_ZPOOL}/currbooted
+ fi
+fi
+
+
+# The rest only applies to the headnode
+/bin/bootparams | grep "^headnode=true" >/dev/null || exit $SMF_EXIT_OK
+
+# If we rebooted during an upgrade, we're in deep trouble.
+if [ -d /var/upgrade_in_progress ]; then
+ echo "ERROR: An upgrade was in progress when the system rebooted." \
+ >/dev/console
+ echo " The system is in an indeterminate state, unable to continue." \
+ >/dev/console
+ exit $SMF_EXIT_ERR_FATAL
+fi
+
+COPYINPOINT=`svcprop -p "joyentfs/usb_copy_path" ${SMF_FMRI}`
+DEBUG=`svcprop -p "joyentfs/debug" ${SMF_FMRI}`
+
+if [[ -d /mnt ]]; then
+ chown root:root /mnt
+ chmod 700 /mnt
+else
+ mkdir -m 700 /mnt
+fi
+
+function make_usb_copy_if_possible
+{
+ [[ -n "${SYS_ZPOOL}" ]] || fatal "don't know system zpool name"
+
+ zpool list -Ho name | grep "^${SYS_ZPOOL}\$"
+ if [[ $? != 0 ]]; then
+ echo "skipping USB copy setup: no ${SYS_ZPOOL} zpool" >/dev/console
+ # Still return OK, because this is the expected case for first headnode
+ # boot.
+ return 0
+ fi
+
+ USBDATASET=${SYS_ZPOOL}/usbkey
+ if ! zfs list -Ho name | grep "^${USBDATASET}\$" >/dev/null; then
+ echo "skipping USB copy setup: no zones/usbkey dataset" >/dev/console
+ # Still return OK, because as of HEAD-2343 a CN being converted to a HN
+ # will not yet have this dataset on its first boot as an HN.
+ return 0
+ fi
+
+ echo "" > /dev/console
+ echo "Moving files from USB boot device onto disk storage." > /dev/console
+ echo "This may take several minutes. Please note the time..." > /dev/console
+ echo "" > /dev/console
+ echo "" > /dev/console
+
+ mkdir ${COPYINPOINT}
+ mount_zfs ${USBDATASET} ${COPYINPOINT}
+
+ (cd ${USBMOUNTPOINT}; rsync -av --log-file=/dev/console --exclude private --exclude os * ${COPYINPOINT})
+ if [[ -d ${USBMOUNTPOINT}/os ]]; then
+ (cd ${USBMOUNTPOINT}/os ; \
+ for dir in $(ls -d *); do
+ # source comes from pcfs which we've got lowering the case
+ # of everything, but we normally use capital T and Z for
+ # buildstamp, so fix it here.
+ source_dir=${dir}
+ target_dir=$(echo ${dir} | tr "[:lower:]" "[:upper:]")
+ mkdir -p ${COPYINPOINT}/os
+ echo "Copying: ${source_dir}/ ${COPYINPOINT}/os/${target_dir}" > /dev/console
+ rsync -a ${source_dir}/ ${COPYINPOINT}/os/${target_dir}
+ done
+ )
+ fi
+
+ echo "" > /dev/console
+ echo "Done copying files from USB device" > /dev/console
+ return 0
+}
+
+USBMOUNTPOINT=$(mount_usb_key "")
+if [[ $? -ne 0 ]]; then
+ fatal "couldn't mount USB key"
+fi
+
+make_usb_copy_if_possible
+exit $?
diff --git a/usr/src/cmd/svc/milestone/fs-root b/usr/src/cmd/svc/milestone/fs-root
index 9652eaaf94..f9de44c831 100644
--- a/usr/src/cmd/svc/milestone/fs-root
+++ b/usr/src/cmd/svc/milestone/fs-root
@@ -22,6 +22,7 @@
#
# Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2016 Joyent, Inc.
#
# Make sure that the libraries essential to this stage of booting can be found.
@@ -64,92 +65,27 @@ if smf_is_nonglobalzone; then
exit $SMF_EXIT_OK
fi
-#
-# Root is already mounted (by the kernel), but still needs to be
-# checked, possibly remounted and entered into mnttab. First
-# mount /usr if it is a separate file system. If the file system
-# type is something other than zfs, mount it read-only. This must
-# be done first to allow utilities such as fsck and setmnt to
-# reside on /usr minimizing the space required by the root file
-# system.
-#
-readvfstab "/usr" < $vfstab
-if [ -n "$mountp" ]; then
- if [ "$fstype" = zfs ]; then
- mountfs - /usr $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL
- else
- #
- # Must use -o largefiles here to ensure the
- # read-only mount does not fail as a result of
- # having a large file present on /usr. This gives
- # fsck a chance to fix up the largefiles flag
- # before we remount /usr read-write.
- #
- if [ "x$mntopts" = x- ]; then
- mntopts='ro,largefiles'
- else
- checkopt largefiles $mntopts
- if [ "x$option" != xlargefiles ]; then
- mntopts="largefiles,$mntopts"
- fi
-
- checkopt ro $mntopts
- if [ "x$option" != xro ]; then
- mntopts="ro,$mntopts"
- fi
-
- #
- # Requesting logging on a read-only mount
- # causes errors to be displayed, so remove
- # "logging" from the list of options for now.
- # The read-write mount performed later will
- # specify the logging option if appropriate.
- #
-
- checkopt logging $mntopts
- if [ "x$option" = xlogging ]; then
- mntopts="$otherops"
- fi
- fi
-
- mountfs -O /usr $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL
- fi
-fi
+/sbin/mount -F ufs -o remount,rw,nologging /devices/ramdisk:a /
+/usr/sbin/lofiadm -X -a /usr.lgz
#
-# if we are booted from zfs, the /usr mount probably won't be a
-# legacy mount. Use the standard zfs mount command instead.
-
-readmnttab "/" < /etc/mnttab
-if [ "$fstype" = zfs ]; then
- mountp=`/sbin/zfs get -H -o value mountpoint $special/usr 2>/dev/null`
- #
- # if mountp = /usr, there is a non-legacy mount of /usr
- # in the boot environment being booted.
- #
- if [ "x$mountp" = "x/usr" ] ; then
- /sbin/zfs mount $special/usr
- if [ $? != 0 ] ; then
- msg='zfs-mount failed'
- echo $msg
- echo "$SMF_FMRI:" $msg >/dev/msglog
- exit $SMF_EXIT_ERR_FATAL
- fi
+# Prior to mounting /usr, devfsadm is not yet available. As such, we must
+# locate the lofi block device node in /devices rather than in /dev. This
+# path has changed over time so we try both the old (pre-partition support)
+# and new paths.
+#
+lofi_devices_path='/devices/pseudo/lofi@1:disk'
+if [ ! -b "$lofi_devices_path" ]; then
+ lofi_devices_path='/devices/pseudo/lofi@0:1'
+ if [ ! -b "$lofi_devices_path" ]; then
+ echo 'could not locate lofi block device in /devices' >&2
+ exit $SMF_EXIT_ERR_FATAL
fi
fi
-#
-# Also mount /boot now so that things like keymap.sh can access
-# boot properties through eeprom. Readonly isn't required because
-# /boot (and other pcfs filesystems) aren't fsck'ed at boot yet.
-# Also, we don't account for caching /boot as it must be on a local
-# disk. So what's in vfstab is fine as it stands; just look to see
-# if it's there and avoid the mount if not.
-#
-readvfstab "/boot" < $vfstab
-
-if [ -n "$mountp" ]; then
- mountfs - /boot $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL
+if ! /sbin/mount -F ufs -o ro "$lofi_devices_path" /usr; then
+ echo "could not mount /usr from $lofi_devices_path" >&2
+ exit $SMF_EXIT_ERR_FATAL
fi
#
diff --git a/usr/src/cmd/svc/milestone/fs-usr b/usr/src/cmd/svc/milestone/fs-usr
index 715cb1bca3..e4b6a263e1 100644
--- a/usr/src/cmd/svc/milestone/fs-usr
+++ b/usr/src/cmd/svc/milestone/fs-usr
@@ -19,171 +19,23 @@
#
# CDDL HEADER END
#
-
#
# Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T.
# All rights reserved.
# Copyright 2016 Nexenta Systems, Inc.
+# Copyright 2012, Joyent, Inc. All rights reserved.
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
#
-
. /lib/svc/share/smf_include.sh
-. /lib/svc/share/fs_include.sh
-
-UPDATEFILE=/etc/svc/volatile/boot_archive_needs_update
-
-#
-# Once root is read/write we can enable the dedicated dumpdevice if it exists
-# locally. This is an optimization as svc-dumpadm will attempt do this later.
-#
-dump_setup()
-{
- [ -r /etc/dumpadm.conf ] && . /etc/dumpadm.conf
-
- readswapdev $DUMPADM_DEVICE < $vfstab
-
- #
- # Make sure that the dump save area has been configured before
- # proceeding. If the variable has not been defined or does not exist
- # then bail out early. This will prevent us from configuring a
- # dump save area before a hostname has been configured (i.e after
- # sys-unconfig has been invoked).
- #
- [ -z "$DUMPADM_SAVDIR" ] && return
-
- #
- # If we have a dedicated dump device, then go ahead and configure it.
- #
- if [ "x$special" != "x$DUMPADM_DEVICE" ]; then
- if [ -x /usr/sbin/dumpadm -a -b $DUMPADM_DEVICE ]; then
- /usr/sbin/dumpadm -u || exit $SMF_EXIT_ERR_CONFIG
- fi
- fi
-}
-
-#
-# Write a unique id into this kernel image; this will be included
-# in the dump header and panicbuf of any crashdump of this image.
-#
-if [ -x /usr/sbin/dumpadm ]; then
- /usr/sbin/dumpadm -i
-fi
-
-rootiszfs=0
-# get the fstype of root
-readmnttab / </etc/mnttab
-if [ "$fstype" = zfs ] ; then
- rootiszfs=1
- dump_setup
-fi
-
-#
-# Add physical swap.
-#
-/sbin/swapadd -1
-
-#
-# Check and remount the / (root) file system.
-# For NFS mounts, force the llock option on.
-#
-if smf_is_globalzone && [ $rootiszfs = 0 ]; then
- readvfstab / < $vfstab
- checkfs $fsckdev $fstype $mountp || exit $SMF_EXIT_ERR_FATAL
- checkopt "llock" $mntopts
- mntopts='remount'
-
- [ -n "$otherops" ] && mntopts="${mntopts},${otherops}"
- [ "$fstype" = nfs ] && mntopts="${mntopts},llock"
-
- mountfs -m $mountp $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL
-fi
-
-#
-# Check and remount the /usr file system (formerly mounted read-only).
-# Unless root is zfs, in which case we've already mounted /usr read-write
-#
-if [ "$rootiszfs" = 0 ] ; then
- readvfstab /usr < $vfstab
- if [ "$mountp" ]; then
- checkopt ro $mntopts
- if [ "x$option" != xro ]; then
- checkfs $fsckdev $fstype $mountp ||
- exit $SMF_EXIT_ERR_FATAL
- if [ "x$mntopts" != x- ]; then
- mntopts="remount,$mntopts"
- else
- mntopts="remount"
- fi
-
- mountfs - /usr $fstype $mntopts - ||
- exit $SMF_EXIT_ERR_FATAL
- fi
- fi
-fi
-
-#
-# Check and mount the /usr/platform file system. This should only be
-# present when a SunOS 5.5 (Solaris 2.5) or greater client is being
-# administered by a SunOS 5.4 or less host.
-#
-readvfstab /usr/platform < $vfstab
-if [ "$mountp" ]; then
- checkfs $fsckdev $fstype $mountp || exit $SMF_EXIT_ERR_FATAL
- mountfs - $mountp $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL
-fi
-
-#
-# Mount the fd file systems if mount point exists.
-#
-readvfstab /dev/fd < $vfstab
-if [ "$mountp" -a -d /dev/fd ]; then
- mountfs - /dev/fd - - - || exit $SMF_EXIT_ERR_FATAL
-fi
-
-if [ -f "${UPDATEFILE}" ]; then
- /usr/sbin/bootadm update-archive
- if [ $? != 0 ]; then
- cecho ""
- cecho "WARNING: Automatic update of the boot archive failed."
- cecho "Update the archives using 'bootadm update-archive'"
- cecho "command and then reboot the system from the same device"
- cecho "that was previously booted."
- cecho ""
- exit $SMF_EXIT_ERR_FATAL
- fi
- rm -f $UPDATEFILE
- cecho ""
- cecho "WARNING: Reboot required."
- cecho "The system has updated the cache of files (boot archive) that"
- cecho "is used during the early boot sequence. To avoid booting and"
- cecho "running the system with the previously out-of-sync version of"
- cecho "these files, the system will be restarted."
- cecho ""
+mount /dev/fd
- bootcmd=`/usr/sbin/eeprom bootcmd | /usr/bin/sed -e 's#bootcmd=##g'`
- if [ `uname -p` = "i386" ]; then
- /usr/sbin/reboot -f dryrun
- if [ $? = 0 ]; then
- /usr/sbin/reboot -f -- "$bootcmd"
- exit $SMF_EXIT_OK
- fi
- boot_prop=`/usr/sbin/svccfg -s svc:/system/boot-config:default \
- listprop config/auto-reboot-safe | \
- /usr/bin/nawk '{ print $3}'`
- if [ "$boot_prop" = "true" ]; then
- /usr/sbin/reboot -p
- exit $SMF_EXIT_OK
- fi
- cecho ""
- cecho "It has not been possible to restart automatically."
- cecho "Reboot the system from the same device that was"
- cecho "previously booted."
- cecho ""
- exit $SMF_EXIT_ERR_FATAL
- fi
- /usr/sbin/reboot -- "$bootcmd"
+if smf_is_globalzone; then
+ # svc.startd makes a backup of the repo on boot. Since this is a
+ # live-image, the backup takes up an unnecessary 4MB in memory, so remove
+ # it now.
+ rm -f /etc/svc/repository-*
fi
exit $SMF_EXIT_OK
diff --git a/usr/src/cmd/svc/milestone/identity-node b/usr/src/cmd/svc/milestone/identity-node
index c9b20ba669..6b740f3dd1 100644
--- a/usr/src/cmd/svc/milestone/identity-node
+++ b/usr/src/cmd/svc/milestone/identity-node
@@ -27,18 +27,30 @@
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright 2019 Joyent, Inc.
+#
. /lib/svc/share/smf_include.sh
. /lib/svc/share/net_include.sh
+set -o xtrace
+
# Make sure that the libraries essential to this stage of booting can be found.
LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH
#
-# If DHCP was used on a primary interface then set the hostname
-# that was returned. If no hostname was returned, set the name
-# to be "unknown". The hostname must be set to something, because
+# For the GZ, use one of the following values for hostname, in order:
+# * DHCP hostname (if set on a primary interface)
+# * hostname value from config file
+# * hostname bootparam
+# * if not a headnode:
+# * admin MAC address
+# * any other MAC address
+#
+# If none of the above could be found, default to "headnode" for headnodes, and
+# "unknown" for non-headnodes.
+#
+# The hostname must be set to something, because
# tooltalk will hang unless the name can be locally resolved.
# Sendmail also requires the name to be resolvable locally.
# Later, in inetsvc, we create a name "unknown" and create a entry
@@ -51,44 +63,124 @@ LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH
# kernel if /etc/nodename does not exist, as is expected on an initial boot.
#
+set_gz_hostname() {
+ hostname=${CONFIG_hostname}
+ if [ -z "$hostname" ] || [ "$hostname" == "unknown" ]; then
+ hostname=$SYSINFO_Bootparam_hostname
+ fi
+
+ if [ -n "$hostname" ] && [ "$hostname" != "unknown" ]; then
+ return
+ fi
+
+ # $headnode is set by load_sdc_config()
+ if [ "$headnode" == "true" ]; then
+ hostname="headnode"
+ return
+ fi
+
+ if [[ -n ${SYSINFO_NIC_admin} ]]; then
+ eval "admin_mac=\${SYSINFO_Network_Interface_${SYSINFO_NIC_admin}_MAC_Address}"
+ if [[ -n ${admin_mac} ]]; then
+ hostname=$(echo "${admin_mac}" | tr ':' '-')
+ return
+ fi
+ fi
+
+ fallback_mac=$(set | grep "^SYSINFO_Network_Interface_.*_MAC_Address" | head -n1 | cut -d'=' -f2)
+ if [[ -n ${fallback_mac} ]]; then
+ hostname=$(echo "${fallback_mac}" | tr ':' '-')
+ return
+ fi
+
+ hostname="unknown"
+}
+
+
smf_netstrategy
case "$_INIT_NET_STRATEGY" in
- "dhcp") hostname=`/sbin/dhcpinfo Hostname` ;;
- "rarp") hostname=`/sbin/hostconfig -h -p bootparams`
- trap 'intr=1' 2 3
- while [ -z "$hostname" -a ! -f /etc/.UNCONFIGURED -a \
- -z "$intr" ]; do
- echo "re-trying host configuration..."
- # Restrict this to IPv4 interfaces.
- /sbin/ifconfig -adD4 auto-revarp up
- hostname=`/sbin/hostconfig -h -p bootparams`
- done
- trap 2 3 ;;
- "none") hostname="`shcat /etc/nodename 2>/dev/null`"
- if [ -z "$hostname" ]; then
- if smf_is_globalzone; then
- hostname=`/sbin/hostconfig -h -p bootparams`
- else
- hostname=`/sbin/uname -n`
- fi
- fi ;;
+ "dhcp") hostname=`/sbin/dhcpinfo Hostname` ;;
+ "rarp") hostname=`/sbin/hostconfig -h -p bootparams`
+ trap 'intr=1' 2 3
+ while [ -z "$hostname" -a ! -f /etc/.UNCONFIGURED -a \
+ -z "$intr" ]; do
+ echo "re-trying host configuration..."
+ # Restrict this to IPv4 interfaces.
+ /sbin/ifconfig -adD4 auto-revarp up
+ hostname=`/sbin/hostconfig -h -p bootparams`
+ done
+ trap 2 3 ;;
+ # /etc/nodename defaults to "unknown" on SmartOS
+ "none") hostname="`shcat /etc/nodename 2>/dev/null`"
+ if [ -z "$hostname" ]; then
+ if smf_is_globalzone; then
+ hostname=`/sbin/hostconfig -h -p bootparams`
+ else
+ hostname=`/sbin/uname -n`
+ fi
+ fi ;;
esac
+# Load sysinfo variables with SYSINFO_ prefix and config variables with
+# CONFIG_ prefix.
+# Note: since we're still starting up, "soft" values like network IP and such could
+# not be set yet.
+
+if smf_is_globalzone; then
+ . /lib/sdc/config.sh
+
+ load_sdc_sysinfo
+
+ if boot_file_config_enabled; then
+ load_boot_file_config
+ else
+ load_sdc_config
+ fi
+fi
+
#
# If the netstrategy was unsuccessful and we haven't got a locally configured
# name, default to "unknown"
#
-if [ -z "$hostname" ]; then
- hostname="`shcat /etc/nodename 2>/dev/null`"
- if [ -z "$hostname" ]; then
- hostname="unknown"
- fi
+if [ -z "$hostname" ] || [ "$hostname" == "unknown" ]; then
+ hostname="`shcat /etc/nodename 2>/dev/null`"
+ if [ -z "$hostname" ] || [ "$hostname" == "unknown" ]; then
+ if smf_is_globalzone; then
+ set_gz_hostname
+ else
+ hostname="unknown"
+ fi
+ fi
+fi
+
+if smf_is_globalzone; then
+ echo "$hostname" > /etc/nodename
fi
/sbin/uname -S $hostname
-echo "Hostname: `/sbin/uname -n`" > /dev/msglog
+# Reloading sysinfo here serves two purposes:
+# - getting the IP info (which should exist now)
+# - updating the host info (which we just set)
+eval $(/usr/bin/sysinfo -f -p | sed -e "s/^/SYSINFO_/")
+
+# Try to add the /etc/hosts entry if we can find an IP
+if [[ -n ${SYSINFO_NIC_admin} ]]; then
+ eval "ipaddr=\${SYSINFO_Network_Interface_${SYSINFO_NIC_admin}_IPv4_Address}"
+fi
+if [[ -z ${ipaddr} ]]; then
+ ipaddr=$(set | grep "^SYSINFO_Network_Interface_.*_IPv4_Address" | head -n1 | cut -d'=' -f2)
+fi
+if [[ -n ${ipaddr} ]]; then
+ fullname=""
+
+ if [ -n "$CONFIG_dns_domain" ]; then
+ fullname=" ${hostname}.${CONFIG_dns_domain}"
+ fi
+
+ printf "${ipaddr}\t${hostname}${fullname}\n" >> /etc/hosts
+fi
# Reset the library path now that we are past the critical stage
unset LD_LIBRARY_PATH
diff --git a/usr/src/cmd/svc/milestone/joyent-fs.xml b/usr/src/cmd/svc/milestone/joyent-fs.xml
new file mode 100644
index 0000000000..f21eae27d8
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/joyent-fs.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+-->
+
+<service_bundle type='manifest' name='SUNWcsr:filesystem-joyent'>
+
+<service
+ name='system/filesystem/smartdc'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <single_instance/>
+
+ <dependency
+ name='usr'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/usr' />
+ </dependency>
+
+ <dependency
+ name='kbmd'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/kbmd:default' />
+ </dependency>
+
+ <!--
+ Start method timeout is infinite to handle potentially unbounded
+ fsck times.
+ -->
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/fs-joyent'
+ timeout_seconds='0' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':true'
+ timeout_seconds='0' />
+
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
+ <property_group name='joyentfs' type='application'>
+ <stability value='Evolving'/>
+ <propval name='debug' type='boolean' value='false'/>
+ <propval name='usb_copy_path' type='astring' value='/usbkey'/>
+ <propval name='usb_mountpoint' type='astring' value='usbkey'/>
+ </property_group>
+
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ Joyent file system mounts
+ </loctext>
+ </common_name>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/svc/milestone/make-console-login-xml b/usr/src/cmd/svc/milestone/make-console-login-xml
index 336ba8fe45..bcb13c1c7c 100644
--- a/usr/src/cmd/svc/milestone/make-console-login-xml
+++ b/usr/src/cmd/svc/milestone/make-console-login-xml
@@ -24,6 +24,8 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2019 Joyent, Inc.
+#
cat >console-login.xml <<EOF
<?xml version="1.0"?>
@@ -31,6 +33,8 @@ cat >console-login.xml <<EOF
Copyright 2008 Sun Microsystems, Inc. All rights reserved.
Use is subject to license terms.
+ Copyright 2015 Joyent, Inc.
+
NOTE: This service manifest is not editable; its contents will
be overwritten by package or patch operations, including
operating system upgrade. Make customizations in a different
@@ -125,14 +129,14 @@ cat >console-login.xml <<EOF
<property_group name='ttymon' type='application'>
<propval name='value_authorization' type='astring'
value='solaris.smf.value.vt' />
- <propval name='device' type='astring' value='/dev/console' />
+ <propval name='device' type='astring' value='/dev/wscons' />
<propval name='label' type='astring' value='console' />
<propval name='timeout' type='count' value='0' />
<propval name='nohangup' type='boolean' value='true' />
<propval name='modules' type='astring'
value='ldterm,ttcompat' />
<propval name='prompt' type='astring'
- value='\`uname -n\` console login:' />
+ value='\`uname -n\` wscons login:' />
<propval name='terminal_type' type='astring'
value='' />
</property_group>
@@ -140,8 +144,39 @@ cat >console-login.xml <<EOF
<instance name='default' enabled='true'>
</instance>
+EOF
+
+for tty in a b c d; do
+ cat >>console-login.xml <<EOF
+<instance name='tty$tty' enabled='true'>
+
+ <dependency
+ name='system-console'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/console-login:default' />
+ </dependency>
+
+ <!-- these are passed to ttymon in the method script -->
+ <property_group name='ttymon' type='application'>
+ <propval name='value_authorization' type='astring'
+ value='solaris.smf.value.vt' />
+ <propval name='device' type='astring' value='/dev/term/$tty' />
+ <propval name='label' type='astring' value='115200' />
+ <propval name='timeout' type='count' value='0' />
+ <propval name='nohangup' type='boolean' value='true' />
+ <propval name='modules' type='astring'
+ value='ldterm,ttcompat' />
+ <propval name='prompt' type='astring'
+ value='\`uname -n\` tty$tty login:' />
+ <propval name='terminal_type' type='astring'
+ value='xterm' />
+ </property_group>
+</instance>
EOF
+done
# Note that this script file is normally parsed during build by sh(1).
# When the parser encounters an EOF token (like the one above), it
diff --git a/usr/src/cmd/svc/milestone/manifest-import b/usr/src/cmd/svc/milestone/manifest-import
index 30f23c26b4..b67361183f 100644
--- a/usr/src/cmd/svc/milestone/manifest-import
+++ b/usr/src/cmd/svc/milestone/manifest-import
@@ -74,13 +74,17 @@ function svccfg_apply {
}
#
-# If the smf repository has file entries that are missing
+# If the smf/manifest table has file entries that are missing
# then there is work to be done by the cleanup process.
#
function cleanup_needwork {
- smfmfiles=`svcprop -p manifestfiles '*' 2>/dev/null |
- nawk -v early="$early" '$2 == "astring" &&
- (early != "true" || $3 ~ "^/lib/") { print $3 }'`
+ if [ "$early" == true ]; then
+ smfmfiles=`/usr/bin/svcprop smf/manifest | \
+ awk '(/^lib_/ && /\/manifestfile /) {print $3}'`
+ else
+ smfmfiles=`/usr/bin/svcprop smf/manifest | \
+ awk '/\/manifestfile / {print $3}'`
+ fi
nw=`/lib/svc/bin/mfstscan $smfmfiles 2>&1 1>/dev/null`
[ "$nw" ] && return 1
@@ -201,8 +205,13 @@ function import_manifests {
rm -f $logf
- nonsite_dirs=`/usr/bin/find $basedir/* -name site \
- -prune -o -type d -print -prune`
+ if [ "${basedir}" == "/opt/custom/smf" ]; then
+ # Special case where we will just import from the root, not subdirs
+ nonsite_dirs=${basedir}
+ else
+ nonsite_dirs=`/usr/bin/find $basedir/* -name site \
+ -prune -o -type d -print -prune`
+ fi
if [ -n "$_MFST_DEBUG" ]; then
nonsite_manifests=`/lib/svc/bin/mfstscan $nonsite_dirs`
@@ -454,6 +463,10 @@ else
import_manifests "/lib/svc/manifest" true
import_manifests "/var/svc/manifest" true
+ if [ -d "/opt/custom/smf" ]; then
+ import_manifests "/opt/custom/smf" true
+ fi
+
#
# Apply profiles
#
@@ -472,9 +485,9 @@ fi
# 6. Final actions.
#
-if $activity; then
- /usr/sbin/svcadm _smf_backup "manifest_import" || true
-fi
+#if $activity; then
+# /usr/sbin/svcadm _smf_backup "manifest_import" || true
+#fi
#
# If the filesystem is NOT read only then move the repo back to perm
diff --git a/usr/src/cmd/svc/milestone/mdata-execute b/usr/src/cmd/svc/milestone/mdata-execute
new file mode 100755
index 0000000000..fca08ffbc7
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/mdata-execute
@@ -0,0 +1,53 @@
+#!/usr/bin/bash
+#
+# 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 http://smartos.org/CDDL
+#
+# 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.
+#
+# 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.
+#
+
+set -o xtrace
+
+. /lib/svc/share/smf_include.sh
+smf_is_globalzone && exit ${SMF_EXIT_OK}
+
+# If we got as far as running the user-script the 'provision' was a success
+# from here out a failure will leave the zone running.
+if [ -f /var/svc/provisioning ]; then
+ mv /var/svc/provision{ing,_success}
+fi
+
+if [[ -x /var/svc/mdata-operator-script ]]; then
+ /var/svc/mdata-operator-script
+ operator_script_exit=$?
+ if [[ ${operator_script_exit} -gt 0 ]]; then
+ echo "WARNING: operator-script failed: exited ${operator_script_exit}" \
+ >&2
+ fi
+fi
+
+user_script_exit=${SMF_EXIT_OK}
+if [ -x /var/svc/mdata-user-script ]; then
+ /var/svc/mdata-user-script
+ [ $? -gt 0 ] && user_script_exit=${SMF_EXIT_ERR_FATAL}
+fi
+
+exit ${user_script_exit}
diff --git a/usr/src/cmd/svc/milestone/mdata-fetch b/usr/src/cmd/svc/milestone/mdata-fetch
new file mode 100755
index 0000000000..10574ca7e0
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/mdata-fetch
@@ -0,0 +1,477 @@
+#!/usr/bin/bash
+#
+# 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 http://smartos.org/CDDL
+#
+# 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.
+#
+# 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 2022 Joyent, Inc.
+#
+
+export PS4='[\D{%FT%TZ}] ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
+export PATH=/usr/bin:/usr/sbin:$PATH
+
+set -o xtrace
+
+. /lib/svc/share/smf_include.sh
+smf_is_globalzone && exit ${SMF_EXIT_OK}
+
+if [ ! -x /usr/sbin/mdata-get ]; then
+ echo "Metadata mdata-get tool not found."
+ exit ${SMF_EXIT_ERR_FATAL}
+fi
+
+function fatal() {
+ if [[ -n $1 ]]; then
+ echo "FATAL: $*" >&2
+ fi
+ exit ${SMF_EXIT_ERR_FATAL}
+}
+
+# Test if an address looks like an IPv4 address.
+function isIPv4() {
+ [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
+}
+
+# Test if an address looks like an IPv4 address + CIDR.
+function isIPv4AndCIDR() {
+ [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$ ]]
+}
+
+# Test if an address looks like an IPv6 address.
+function isIPv6() {
+ [[ "${1,,}" =~ ^[0-9a-f]*:[0-9a-f]*:[:0-9a-f]*$ ]]
+}
+
+# Test if an address looks like an IPv6 address + CIDR.
+function isIPv6AndCIDR() {
+ [[ "${1,,}" =~ ^[0-9a-f]*:[0-9a-f]*:[:0-9a-f]*/[0-9]{1,3}$ ]]
+}
+
+# For old zones that were created prior to OS-2253 and bumping the mdata:fetch
+# start timeout, we need to fix this otherwise we could timeout waiting for the
+# socket.
+cur_timeout=$(svcprop -p start/timeout_seconds svc:/smartdc/mdata:fetch)
+if [[ -z ${cur_timeout} || ${cur_timeout} -lt 1800 ]]; then
+ # The current manifest has an old timeout value, fix in case we timeout
+ # here. XXX we can still hit OS-2296 here where smf will forget that we
+ # set this.
+ svccfg -s svc:/smartdc/mdata:fetch 'setprop start/timeout_seconds = 1800'
+ svcadm refresh svc:/smartdc/mdata:fetch
+fi
+
+# This waits until /.zonecontrol/metadata.sock exists then exits 0
+/usr/vm/sbin/filewait /.zonecontrol/metadata.sock
+
+if [[ ! -e /.zonecontrol/metadata.sock ]]; then
+ # this is a bug since filewait should not have returned until file existed.
+ fatal "missing /.zonecontrol/metadata.sock, Unable to start mdata:fetch"
+fi
+
+# Update sysinfo to ensure values that come from metadata are populated.
+/usr/bin/sysinfo -fu
+
+echo "Retrieving metadata user-data"
+/usr/sbin/mdata-get user-data >/var/db/mdata-user-data.new
+case $? in
+ 0)
+ echo "Metadata user-data successfuly retrieved."
+ mv /var/db/mdata-user-data{.new,}
+ ;;
+ 1)
+ echo "Metadata user-data not defined."
+ rm -f /var/db/mdata-user-data{,.new}
+ ;;
+ *)
+ echo "Metadata couldn't be retrieved."
+ exit ${SMF_EXIT_ERR_FATAL}
+ ;;
+esac
+
+echo "Retrieving metadata user-script..."
+/usr/sbin/mdata-get user-script >/var/svc/mdata-user-script.new
+case $? in
+ 0)
+ echo "Metadata user-script successfuly retrieved."
+ mv /var/svc/mdata-user-script{.new,}
+ chmod +x /var/svc/mdata-user-script
+ ;;
+ 1)
+ echo "Metadata user-script not defined."
+ rm -f /var/svc/mdata-user-script{,.new}
+ ;;
+ *)
+ echo "Metadata couldn't be retrieved."
+ exit ${SMF_EXIT_ERR_FATAL}
+ ;;
+esac
+
+echo "Retrieving metadata operator-script..."
+/usr/sbin/mdata-get sdc:operator-script >/var/svc/mdata-operator-script.new
+case $? in
+ 0)
+ echo "Metadata operator-script successfuly retrieved."
+ mv /var/svc/mdata-operator-script{.new,}
+ chmod +x /var/svc/mdata-operator-script
+ ;;
+ 1)
+ echo "Metadata operator-script not defined."
+ rm -f /var/svc/mdata-operator-script{,.new}
+ ;;
+ *)
+ echo "Metadata couldn't be retrieved."
+ exit ${SMF_EXIT_ERR_FATAL}
+ ;;
+esac
+
+echo "Retrieving tmpfs value..."
+tmpfs=$(/usr/sbin/mdata-get sdc:tmpfs)
+if [[ $? == 0 && -n ${tmpfs} && -f /etc/vfstab ]]; then
+ check="swap - /tmp tmpfs";
+
+ if [[ ${tmpfs} == "0" ]]; then
+ # When tmpfs is set 0, we remove any entry from /etc/vfstab but cannot
+ # adjust the "live" value as current /tmp will be in-use. On reboot the
+ # new value will take effect.
+
+ grep -v "^${check}" /etc/vfstab > /etc/vfstab.new \
+ && mv /etc/vfstab.new /etc/vfstab
+ else
+ new="swap - /tmp tmpfs - yes size=${tmpfs}m";
+ if ! /usr/bin/grep "^${new}" /etc/vfstab; then
+ if ! /usr/bin/grep "^${check}" /etc/vfstab; then
+ # no tmpfs line. add it.
+ echo "${new}" >> /etc/vfstab
+ else
+ # existing tmpfs line, but wrong value. fix it.
+ /usr/bin/sed -i "" -e "s|^swap.*/tmp.*tmpfs.*$|${new}|" /etc/vfstab
+ echo $?
+ fi
+
+ if mount | grep "^/tmp"; then
+ # Also fix current size, since /etc/vfstab didn't have our correct line
+ # but only if we have /tmp mounted at all. If not, we'll have to wait
+ # until the next reboot since /tmp will be in-use.
+ /usr/sbin/mount -F tmpfs -o remount,size=${tmpfs}m /tmp
+ fi
+
+ fi
+ fi
+fi
+
+#
+# If we have NFS volumes, we'll add them to vfstab, and enable the nfs/client
+# service so that will mount the volumes for us.
+#
+echo "Retrieving volume metadata..."
+
+volumes_added=0
+while IFS="|" read -r nfsvolume mountpoint name mode type; do
+
+ cat >&2 <<EOF
+*** VOLUME ***
+NFSVOLUME: ${nfsvolume}
+MOUNTPOINT: ${mountpoint}
+MODE: ${mode}
+NAME: ${name}
+TYPE: ${type}
+EOF
+
+ if [[ -n ${type} && ${type} != "tritonnfs" ]]; then
+ fatal "unsupported volume type: ${type}"
+ fi
+
+ # if we don't have volume and mountpoint, we can't add vfstab lines
+ if [[ -z ${nfsvolume} || -z ${mountpoint} ]]; then
+ fatal "invalid volume specification (need both volume & mountpoint)"
+ fi
+
+ if ! grep "^${nfsvolume}[ ]" /etc/vfstab; then
+ if [[ -z ${mode} ]]; then
+ mode="rw"
+ fi
+
+ if [[ ${mode} != "rw" && ${mode} != "ro" ]]; then
+ fatal "invalid volume mode: '${mode}'"
+ fi
+
+ line=$(printf "%s - %s nfs - yes %s\n" "${nfsvolume}" "${mountpoint}" "${mode}")
+ volumes_added=$((${volumes_added}+1))
+
+ mkdir -p ${mountpoint}
+ echo "${line}" >> /etc/vfstab
+ fi
+done < <(/usr/sbin/mdata-get sdc:volumes \
+ | /usr/bin/json \
+ -d '|' \
+ -a nfsvolume mountpoint name mode type)
+
+if [[ ${volumes_added} -gt 0 ]]; then
+ for svc in \
+ svc:/network/nfs/nlockmgr:default \
+ svc:/network/nfs/status:default \
+ svc:/network/rpc/bind:default \
+ svc:/network/nfs/client:default \
+ ; do
+
+ svcadm enable ${svc} || fatal "Unable to enable ${svc}"
+ done
+fi
+
+# We use the special sdc:nics value here though this is not an interface for
+# use elsewhere. If this is changed please also update agent.js in the metadata
+# agent.
+#
+# We run this every startup in case nics have changed since last boot. As
+# network/physical has an optional_all dependency on this service, we'll have
+# had our chance to write these files out before networking comes up. This
+# might eventually be replaced by network/physical grabbing data directly.
+echo "Retrieving nics data..."
+while IFS="|" read -r iface ip ips netmask primary gateway gateways; do
+
+ # if we don't have interface name, we don't know what files to write out.
+ if [[ -z ${iface} ]]; then
+ continue;
+ fi
+
+ # A VM created on an older platform may not have the "ips" or "gateways"
+ # properties, so we will need to grab the older "ip" and "gateway" versions
+ # instead.
+ [[ -z $ips ]] && ips=$ip
+ [[ -z $gateways ]] && gateways=$gateway
+
+ # We remove the hostname.netX file first, so we can append to a clean file
+ rm -f /etc/hostname.${iface}
+
+ # remove any existing DHCP or addrconf configuration, since we'll create one
+ # if it belongs later
+ rm -f /etc/dhcp.${iface}
+ rm -f /etc/addrconf.${iface}
+
+ OLDIFS=$IFS
+ IFS=$','
+ for ip in $ips; do
+ # so it shows up in the logs
+ echo "iface[${iface}] ip[${ip}] netmask[${netmask}]" \
+ "primary[${primary}] gateway[${gateway}]"
+
+ [[ -z ${ip} ]] && continue;
+
+ if [[ ${ip} == "dhcp" ]]; then
+ touch /etc/dhcp.${iface}
+ if hostname=`mdata-get sdc:hostname`; then
+ echo "inet ${hostname}" >> /etc/hostname.${iface}
+ fi
+ elif [[ ${ip} == "addrconf" ]]; then
+ touch /etc/addrconf.${iface}
+ elif isIPv4 ${ip} && [[ -n ${netmask} ]]; then
+ # We're using an older configuration where the routing prefix is
+ # specified using a mask instead of CIDR notation. We'll need to
+ # invoke ifconfig differently.
+ echo "${ip} netmask ${netmask} up" >> /etc/hostname.${iface}
+ elif isIPv4AndCIDR ${ip} \
+ || isIPv6AndCIDR ${ip} \
+ || isIPv6 ${ip}; then
+ # Either a routing prefix was specified, or, in the case of IPv6, we
+ # won't specify one and fall back on NDP to find it when we configure
+ # our interfaces.
+ echo "${ip} up" >> /etc/hostname.${iface}
+ fi
+ done
+
+ if [[ ${primary} == "true" ]]; then
+ rm -f /etc/defaultrouter
+ for gateway in $gateways; do
+ if [[ -n ${gateway} ]] && isIPv4 ${gateway}; then
+ echo "${gateway}" >> /etc/defaultrouter
+ fi
+ done
+ fi
+ IFS=$OLDIFS
+
+ # XXX we leave old hostname.netX files around and just replace when we have
+ # one next.
+done < <(/usr/sbin/mdata-get sdc:nics \
+ | /usr/bin/json \
+ -d '|' \
+ -e 'this.ips = this.ips ? this.ips.join(",") : ""' \
+ -e 'this.gateways = this.gateways ? this.gateways.join(",") : ""' \
+ -a interface ip ips netmask primary gateway gateways)
+
+# rebuild resolv.conf
+resolvers=$(mdata-get sdc:resolvers)
+resolvers_result=$?
+
+# Determine if resolv.conf is managed for us
+maintain_resolvers=$(mdata-get sdc:maintain_resolvers)
+maintain_result=$?
+if [[ ${maintain_result} != 0 ]]; then
+ echo "Error getting maintain_resolvers, code: ${maintain_result}"
+ maintain_resolvers="false"
+fi
+
+# If this is our first boot, write an initial set of resolvers
+if [[ -f /var/svc/provisioning ]]; then
+ echo "First boot: writing resolvers"
+ maintain_resolvers="true"
+fi
+
+if [[ ${resolvers_result} == 0 && ${maintain_resolvers} == "true" ]]; then
+
+ # if dns_domain is missing or broken, we still write out, just w/o search
+ search="search $(mdata-get sdc:dns_domain)"
+ if [[ $? != 0 ]]; then
+ search=
+ fi
+
+ if [[ ${resolvers} == "[]" ]]; then
+ nameservers=
+ else
+ nameservers=$(echo ${resolvers} | json -a | sed -e "s/^/nameserver /")
+ fi
+
+ rm -f /etc/.resolv.conf.tmp
+ if [[ -n ${search} ]]; then
+ echo "${search}" > /etc/.resolv.conf.tmp
+ fi
+ if [[ -n ${nameservers} ]]; then
+ echo "${nameservers}" >> /etc/.resolv.conf.tmp
+ fi
+
+ cp /etc/.resolv.conf.tmp /etc/resolv.conf \
+ && cat /etc/.resolv.conf.tmp >&2 \
+ && rm -f /etc/.resolv.conf.tmp
+else
+ if [[ ${resolvers_result} == 0 ]]; then
+ echo "Error getting resolvers, code: ${resolvers_result}"
+ fi
+
+ if [[ ${maintain_resolvers} == "true" ]]; then
+ echo "Not setting resolvers, maintain_resolvers=${maintain_resolvers}"
+ fi
+fi
+
+
+# Fetch routes
+
+# It is possible to specify the same route in several different ways using
+# route(1m). We therefore use route(1m) itself to manage adding, deleting
+# and determining duplicates.
+zone_routes_file=/etc/inet/static_routes
+vmadm_routes_file=/etc/inet/static_routes.vmadm
+tmpdir=$(mktemp -d /tmp/mdata.XXXXXX)
+
+if [ -z $tmpdir ]; then
+ echo "Error creating temporary directory."
+ exit ${SMF_EXIT_ERR_FATAL}
+fi
+
+# directory structure for the new copy of static_routes.vmadm (the one that
+# will replace the current static_routes.vmadm once all of the adds and
+# deletes have been applied):
+new_root=${tmpdir}/new-routes
+new_inet=${new_root}/etc/inet
+# directory structure for the previous copy of static_routes.vmadm (used to
+# determine routes that have been removed since the last time mdata-fetch
+# was run):
+old_root=${tmpdir}/old-routes
+old_inet=${old_root}/etc/inet
+# directory structure for the zone's persistent routes - those not created
+# by vmadm (used to determine if vmadm routes are duplicates):
+zone_root=${tmpdir}/zone-routes
+zone_inet=${zone_root}/etc/inet
+
+mkdir -p $new_inet
+mkdir -p ${old_root}/etc/inet
+mkdir -p ${zone_root}/etc/inet
+
+if [[ -f ${vmadm_routes_file} ]]; then
+ cp $vmadm_routes_file ${old_inet}/static_routes
+fi
+
+function route_in_per_zone_file()
+{
+ cp $zone_routes_file $zone_inet
+ output=$(route -pR $zone_root add $* 2>&1)
+ [[ $output =~ "entry exists" ]]
+}
+
+# If re-running this script after initial boot, network/physical and
+# network/routing-setup are already enabled, so apply routing adds and
+# deletes manually
+if [[ $(/usr/bin/svcs -H -o state network/routing-setup) == "online" ]]; then
+ routing_up="true"
+fi
+
+while IFS="|" read -r gateway dst linklocal; do
+ echo "route: gateway[${gateway}] dst[${dst}] linklocal[${linklocal}]"
+ route_type=""
+ if [[ ${linklocal} == "true" ]]; then
+ route_type="-interface "
+ fi
+ route_str="${route_type}${dst} ${gateway}"
+
+ if route_in_per_zone_file $route_str; then
+ echo "not adding duplicate route: ${route_str}"
+ # the zone has also defined this route; do nothing
+ else
+ echo "adding route to file: ${route_str}"
+ route -pR $new_root add $route_str
+ if [[ -n "${routing_up}" ]]; then
+ route add $route_str
+ fi
+ fi
+
+ route -pR $old_root delete $route_str
+done < <(/usr/sbin/mdata-get sdc:routes \
+ | /usr/bin/json -d '|' -a gateway dst linklocal)
+
+
+# Anything left in the old static_routes file is a delete. Don't delete the
+# route from the routing tables if there's a duplicate route in the zone's
+# static_routes file
+if [[ -f ${old_inet}/static_routes ]]; then
+ egrep -v "^(#|$)" ${old_inet}/static_routes | while read -r route_str; do
+ if [[ "${route_str}" == "" ]]; then
+ continue
+ fi
+
+ if route_in_per_zone_file "$route_str"; then
+ echo "not deleting duplicate route: ${route_str}"
+ else
+ if [[ -n "${routing_up}" ]]; then
+ route delete $route_str
+ fi
+ fi
+ done
+fi
+
+if [[ -f ${new_inet}/static_routes ]]; then
+ cp ${new_inet}/static_routes ${vmadm_routes_file}
+else
+ rm ${vmadm_routes_file}
+fi
+rm -rf $tmpdir
+
+
+# Unconditionally enable mdata:execute, so that the last provisioning step
+# is always taken (regardless of whether user-script exists or not)
+svcadm enable smartdc/mdata:execute
+
+exit ${SMF_EXIT_OK}
diff --git a/usr/src/cmd/svc/milestone/mdata.xml b/usr/src/cmd/svc/milestone/mdata.xml
new file mode 100644
index 0000000000..152a7cb485
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/mdata.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<service_bundle type="manifest" name="mdata">
+ <service name="smartdc/mdata" type="service" version="1">
+ <dependency name="filesystem" grouping="require_all" restart_on="error" type="service">
+ <service_fmri value="svc:/system/filesystem/minimal" />
+ </dependency>
+ <property_group name="startd" type="framework">
+ <propval name="duration" type="astring" value="transient" />
+ <propval name="ignore_error" type="astring" value="core,signal" />
+ </property_group>
+ <instance name="fetch" enabled="true">
+ <dependency name="boot-file" grouping="exclude_all" restart_on="refresh" type="path">
+ <service_fmri value="file://localhost/tmp/.FIRST_REBOOT_NOT_YET_COMPLETE"/>
+ </dependency>
+ <dependency name="rmtmpfiles" grouping="optional_all" restart_on="error" type="service">
+ <service_fmri value="svc:/system/rmtmpfiles" />
+ </dependency>
+ <exec_method type="method" name="start" exec="/lib/svc/method/mdata-fetch" timeout_seconds="1800" />
+ <exec_method type="method" name="stop" exec=":true" timeout_seconds="60" />
+ </instance>
+ <instance name="execute" enabled="false">
+ <dependency name="network" grouping="require_all" restart_on="error" type="service">
+ <service_fmri value="svc:/milestone/multi-user:default" />
+ </dependency>
+ <dependency name="mdata" grouping="require_all" restart_on="error" type="service">
+ <service_fmri value="svc:/smartdc/mdata:fetch" />
+ </dependency>
+ <exec_method type="method" name="start" exec="/lib/svc/method/mdata-execute" timeout_seconds="300" />
+ <exec_method type="method" name="stop" exec=":true" timeout_seconds="60" />
+ </instance>
+ <stability value="Evolving" />
+ <template>
+ <common_name>
+ <loctext xml:lang="C">Joyent SDC metadata handler</loctext>
+ </common_name>
+ </template>
+ </service>
+</service_bundle>
diff --git a/usr/src/cmd/svc/milestone/minimal-fs.xml b/usr/src/cmd/svc/milestone/minimal-fs.xml
index b7af22bfcd..844760baa1 100644
--- a/usr/src/cmd/svc/milestone/minimal-fs.xml
+++ b/usr/src/cmd/svc/milestone/minimal-fs.xml
@@ -44,11 +44,11 @@
<single_instance/>
<dependency
- name='usr'
+ name='joyent'
grouping='require_all'
restart_on='none'
type='service'>
- <service_fmri value='svc:/system/filesystem/usr' />
+ <service_fmri value='svc:/system/filesystem/smartdc' />
</dependency>
<dependency
diff --git a/usr/src/cmd/svc/milestone/net-early-admin b/usr/src/cmd/svc/milestone/net-early-admin
new file mode 100644
index 0000000000..a01881c904
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/net-early-admin
@@ -0,0 +1,260 @@
+#!/bin/ksh93
+#
+#
+# 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 2020 Joyent, Inc.
+#
+
+# Traditionally, when a Triton compute node boots, the network is not
+# configured until after the local filesystems are mounted (in fact
+# network/physical:default depends upon /system/filesystem/smartdc). Most
+# obviously, in the case of a head node or a standalone SmartOS install, the
+# network configuration is stored on the local zpool, so the network cannot
+# be configured until this happens.
+#
+# For Triton compute nodes with encrypted zpools, we must enable the admin
+# network before the local zpool filesytems are online -- we have to be able
+# to communicate to the head node services to obtain the pin to unlock the
+# local zpool. For PXE booted Triton compute nodes, we therefore configure
+# the admin network sooner via the network/early-admin:default service. When
+# network/physical:default runs, it will skip the configuration of the admin
+# network and configuring the remaining interfaces.
+#
+# If we are not a Triton compute node, we exit successfully almost immediately
+# without configuring the admin network. When the network/physical:default
+# service runs, it will configure all the network interfaces as it traditionally
+# has
+
+PATH=/bin:/usr/bin:/sbin:/usr/sbin
+
+. /lib/svc/share/smf_include.sh
+. /lib/sdc/config.sh
+. /lib/sdc/network.sh
+
+PS4='+ [$LINENO] '
+
+set -o xtrace
+
+if ! smf_is_globalzone; then
+ echo "Non-global zone; no action required; exiting"
+ exit $SMT_EXIT_OK
+fi
+
+# XXX Until OS-8367 is sorted out, immediately enable the varpd service.
+# SMF will properly cope with things if it fails (and blocks both
+# network/physical and zones).
+svcadm enable varpd
+
+if ! boot_file_config_enabled; then
+ echo "Boot-time networking files not present; exiting"
+ exit $SMF_EXIT_OK
+fi
+
+function fatal
+{
+ # XXX: For SMF methods, does it matter/better to redirect to stderr?
+ echo "Error: $*" >&2
+ exit $SMF_EXIT_ERR_FATAL
+}
+
+if ! boot_file_config_valid; then
+ echo "ERROR: boot-time network config file incorrect" >&2
+ exit $SMF_EXIT_ERR_CONFIG
+fi
+
+unset aggrs
+unset tags
+typeset -A tagv
+typeset -A aggr_links aggr_mode
+typeset -A ip ip6 netmask gateway gateway6 mtu mac
+/usr/lib/sdc/net-boot-config | while IFS="=" read var value; do
+ if [[ "$var" =~ _nic$ ]]; then
+ name=${var%_nic}
+ tags+=("${name}")
+ tagv[$name]="$value"
+ elif [[ "$var" =~ _aggr$ ]]; then
+ name=${var%_aggr}
+ aggrs+=(${name})
+ aggr_links[$name]="${value//,/ }"
+ elif [[ "$var" =~ _lacp_mode$ ]]; then
+ name=${var%_lacp_mode}
+ aggr_mode[$name]="$value"
+ elif [[ "$var" =~ _mtu$ ]]; then
+ (( value < 1500 || value > 65535 )) &&
+ fatal "ERROR: $var MTU value \'$value\' is not in" \
+ "range [1500, 65535]"
+
+ name=${var%_mtu}
+ mtu[$name]="$value"
+ elif [[ "$var" =~ _ip$ ]]; then
+ name=${var%_ip}
+ ip[$name]="$value"
+ elif [[ "$var" =~ _ip6$ ]]; then
+ name=${var%_ip6}
+ ip6[$name]="$value"
+ elif [[ "$var" =~ _netmask$ ]]; then
+ name=${var%_netmask}
+ netmask[$name]="$value"
+ elif [[ "$var" =~ _gateway$ ]]; then
+ name=${var%_gateway}
+ gateway[$name]="$value"
+ elif [[ "$var" =~ _gateway6$ ]]; then
+ name=${var%_gateway6}
+ gateway6[$name]="$value"
+ elif [[ "$var" =~ _mac$ ]]; then
+ name=${var%_mac}
+ mac[$name]="$value"
+ elif [[ "$var" == "dns_resolvers" ]]; then
+ dns_resolvers=(${value//,/ })
+ fi
+
+ eval "CONFIG_$var"="$value"
+done
+
+# This must happen befor any other dladm commands (which includes log_if_state)
+# or else dladm commands can fail
+dladm init-phys
+
+log_if_state before
+
+typeset -A mac_to_link
+out=$(dladm show-phys -mpo link,address)
+(( $? == 0 )) || fatal "dladm show-phys failed"
+while IFS=: read link addr; do
+ macaddr=$(normalize_mac $addr)
+ mac_to_link["$macaddr"]="$link"
+done <<< "$out"
+
+ADMIN_NIC_TAG=${CONFIG_admin_tag:-"admin"}
+[[ -n "${tagv[$ADMIN_NIC_TAG]}" ]] || \
+ fatal "ERROR: admin nic tag '$ADMIN_NIC_TAG' not present"
+
+# Set $nic to the link that has the admin nic tag. For unfortunate
+# historic reasons, for aggrs, the MAC address and link name of an aggr
+# are the same value by net-boot-config (and the source of many errors).
+# If ${aggr_links[$nic]} is non-empty, it means $nic isn't a MAC address
+# but an aggr name (and we're done). Otherwise we must map the MAC address
+# from $tagv back to the link.
+nic="${tagv[$ADMIN_NIC_TAG]}"
+if [[ -z "${aggr_links[$nic]}" ]]; then
+ if !valid_mac "$nic"; then
+ fatal "ERROR: admin mac address $nic not found on system"
+ fi
+
+ _nic=${mac_to_link[$nic]}
+ if [[ -z "$_nic" ]]; then
+ fatal "ERROR: Invalid value of ${ADMIN_NIC_TAG}_nic ($nic)"
+ fi
+ nic=$_nic
+fi
+
+# If there are other nic tags configured on the same link as
+# the admin tag, find the largest MTU to use to set the
+# datalink MTU
+dlmtu="${mtu[$ADMIN_NIC_TAG]}"
+
+for tag in ${tags[@]}; do
+ tagmac="${tagv[$tag]}"
+
+ # If the 'mac' for the nic tag is actually an aggr, the
+ # tag will appear in $aggr_links (and we want to use that as
+ # the link name). If it is not an aggr, we need to map the
+ # MAC address to the link name so we can check if $tag
+ # resides on the same link as the admin interface
+ if [[ -n "${aggr_links[$tagmac]}" ]]; then
+ link="$tagmac"
+ else
+ link="${mac_to_link[$tagmac]}"
+ fi
+
+ if [[ "$link" == "$nic" && $dlmtu -lt ${mtu[$tag]} ]]; then
+ dlmtu=${mtu[$tag]}
+ fi
+done
+
+
+if [[ -n "${aggr_links[$nic]}" ]]; then
+ mode=${aggr_mode[$nic]:-"off"}
+
+ for l in ${aggr_links[$nic]}; do
+ [[ -n "${mac_to_link[$l]}" ]] || fatal "MAC '$l' not present"
+ link="${mac_to_link[$l]}"
+ if [[ -n "$dlmtu" ]]; then
+ dladm set-linkprop -p mtu=$dlmtu $link || \
+ fatal "ERROR: Failed to set mtu on $link to $dlmtu"
+ fi
+ links+="${link} "
+ done
+ links="${links% }"
+
+ echo "Creating aggr: $nic (mode=$mode, links=${links})"
+ dladm create-aggr -l ${links// / -l } -L $mode $nic
+fi
+
+if [[ -n "$dlmtu" ]]; then
+ dladm set-linkprop -p mtu=$dlmtu $nic || \
+ fatal "ERROR: Failed to set mtu on aggr $nic to $dlmtu"
+fi
+
+driver=${nic%%+([0-9])}
+get_link_state $nic
+if [[ "$link_state" == "down" ]]; then
+ echo "admin nic '${nic}' is down: unplumbing"
+ /sbin/ifconfig $nic down unplumb
+ wait_for_nic_state $nic "unknown"
+fi
+
+# There's some sort of race condition in the bnx driver: if the plumb
+# command comes too soon after the unplumb, the interface can come up
+# in a state where it never fires interrupts for link state changes.
+if [[ "$driver" == "bnx" ]]; then
+ sleep 5
+fi
+
+/sbin/ifconfig $nic plumb mtu ${mtu[$ADMIN_NIC_TAG]}
+wait_for_nic_state $nic "up"
+
+if [[ -n "${ip[$ADMIN_NIC_TAG]}" ]]; then
+ /sbin/ifconfig $nic inet ${ip[$ADMIN_NIC_TAG]} \
+ netmask ${netmask[$ADMIN_NIC_TAG]:-"+"} up
+ [[ -n "${gateway[$ADMIN_NIC_TAG]}" ]] && \
+ /usr/sbin/route add default ${gateway[$ADMIN_NIC_TAG]}
+fi
+
+if [[ -n "${ip6[$ADMIN_NIC_TAG]}" ]]; then
+ /sbin/ifconfig $nic inet6 plumb mtu ${mtu[$ADMIN_NIC_TAG]}
+ [[ "${ip6[$ADMIN_NIC_TAG]}" != "addrconf" ]] && \
+ /sbin/ifconfig $nic inet6 addif ${ip6[$ADMIN_NIC_TAG]} preferred up
+ [[ -n "${gateway6[$ADMIN_NIC_TAG]}" ]] && \
+ /usr/sbin/route add -inet6 default ${gateway6[$ADMIN_NIC_TAG]}
+fi
+
+# Add just the routes reachable through the admin network -- usually these are
+# only present with rack aware networking (RAN)
+/usr/lib/sdc/net-boot-config --routes | while read dst gw; do
+ if ! ip_in_net $gw ${ip[$ADMIN_NIC_TAG]} ${netmask[$ADMIN_NIC_TAG]}; then
+ continue
+ fi
+ route add "$dst" "$gw"
+done
+
+if [[ -n "${CONFIG_dns_domain}" && -n ${dns_resolvers[0]} ]]; then
+ echo "search ${CONFIG_dns_domain}" > /etc/resolv.conf
+ for serv in ${dns_resolvers[@]}; do
+ echo "nameserver $serv" >> /etc/resolv.conf
+ done
+fi
+
+touch /etc/svc/volatile/.early_admin_setup
+
+log_if_state after
diff --git a/usr/src/cmd/svc/milestone/net-physical b/usr/src/cmd/svc/milestone/net-physical
index 15929cb28a..5c459a3622 100644
--- a/usr/src/cmd/svc/milestone/net-physical
+++ b/usr/src/cmd/svc/milestone/net-physical
@@ -1,4 +1,4 @@
-#!/sbin/sh
+#!/usr/bin/ksh93
#
# CDDL HEADER START
#
@@ -25,12 +25,22 @@
# All rights reserved.
# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2012 Milan Jurik. All rights reserved.
+# Copyright 2021 Joyent, Inc.
# Copyright 2021 Tintri by DDN, Inc. All rights reserved.
#
-# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+
+#
+# NOTE: Upstream illumos has IPMP configuration and upgrading of ipadm.conf in
+# here. SmartOS does not. Recent improvements to IPMP specifically for
+# upgrading ipadm.conf do not appear here, therefore.
+#
. /lib/svc/share/smf_include.sh
-. /lib/svc/share/net_include.sh
+. /lib/sdc/config.sh
+. /lib/sdc/network.sh
+
+set -o errexit
+set -o xtrace
#
# In a shared-IP zone we need this service to be up, but all of the work
@@ -38,557 +48,832 @@
# failing if we try to do it), so just bail out.
# In the global zone and exclusive-IP zones we proceed.
#
-smf_configure_ip || exit $SMF_EXIT_OK
+smf_configure_ip || exit ${SMF_EXIT_OK}
# Make sure that the libraries essential to this stage of booting can be found.
LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH
+# Time (in seconds) to wait for admin NIC to get DHCP address before continuing.
+ADMIN_DHCP_TIMEOUT=300
+ActiveAggrLinks=
+typeset -A ActiveAggrLinks
+
smf_netstrategy
-if smf_is_globalzone; then
- net_reconfigure || exit $SMF_EXIT_ERR_CONFIG
-
- # Update PVID on interfaces configured with VLAN 1
- update_pvid
-
- #
- # Upgrade handling. The upgrade file consists of a series of dladm(8)
- # commands. Note that after we are done, we cannot rename the upgrade
- # script file as the file system is still read-only at this point.
- # Defer this to the manifest-import service.
- #
- upgrade_script=/var/svc/profile/upgrade_datalink
- if [ -f "${upgrade_script}" ]; then
- . "${upgrade_script}"
- fi
-
- #
- # Upgrade handling for ibd:
- # After we are done with the upgrade handling, we can not set the
- # ibd/ibd_upgraded property to "true" as the file system is
- # read-only at this point. It will be done later by ibd-post-upgrade
- # service.
- #
- if [ -x /sbin/ibd_upgrade ]; then
- ibd_upgraded=`/bin/svcprop -c -p ibd/ibd_upgraded \
- $SMF_FMRI 2> /dev/null`
- if [ "$ibd_upgraded" != "true" ]; then
- /sbin/ibd_upgrade -v
- fi
- fi
-
- #
- # Bring up simnets, link aggregations and initialize security objects.
- # Note that link property initialization is deferred until after
- # IP interfaces are plumbed to ensure that the links will not
- # be unloaded (and the property settings lost). We should bring
- # up simnets prior to VLANs/Aggrs to enable creation of VLANs/Aggrs
- # over simnets.
- #
- /sbin/dladm up-simnet
- /sbin/dladm up-aggr
- /sbin/dladm up-vlan
- /sbin/dladm up-part
- /sbin/dladm init-secobj
- #
- # Bring up VNICs
- #
- /sbin/dladm up-vnic
- #
- # Create flows via flowadm.
- #
- /sbin/flowadm init-flow
- #
- # Bring up overlays
- # Note that there may be VNICs configured over these overlays but
- # these cannot be brought up until the network interface on which
- # varpd will listen is enabled, and that doesn't happen until
- # near the end of this script. Therefore VNIC initialisation is
- # repeated below if overlays are present.
- #
- /sbin/dladm up-overlay
-fi
+function add_active_aggr_links
+{
+ set -o xtrace
+ typeset alink
-#
-# If the system was net booted by DHCP, hand DHCP management off to the
-# DHCP agent (ifconfig communicates to the DHCP agent through the
-# loopback interface).
-#
-if [ -n "$_INIT_NET_IF" -a "$_INIT_NET_STRATEGY" = "dhcp" ]; then
- /sbin/dhcpagent -a
-fi
+ for alink in ${2//,/ }; do
+ ActiveAggrLinks[$alink]=$1
+ done
+}
-#
-# The network initialization is done early to support diskless and
-# dataless configurations. For IPv4 interfaces that were configured by
-# the kernel (e.g. those on diskless machines) and not configured by
-# DHCP, reset the netmask using the local "/etc/netmasks" file if one
-# exists, and then reset the broadcast address based on the netmask.
-#
-/sbin/ifconfig -auD4 netmask + broadcast +
+# Waits for up to 10 seconds for the link state to change to the given value
+function wait_for_admin_nic_state
+{
+ wait_for_nic_state "${SYSINFO_NIC_admin}" "$1"
+}
-is_iptun ()
+# Plumbs the admin interface, and attempts to work around poorly-behaved
+# drivers that can't handle plumb commands too quickly after one another
+function plumb_admin
{
- intf=$1
- # Is this a persistent IP tunnel link?
- /sbin/dladm show-iptun -P $intf > /dev/null 2>&1
- if [ $? -eq 0 ]; then
- return 0
- fi
- # Is this an implicit IP tunnel (i.e., ip.tun0)
- ORIGIFS="$IFS"
- IFS="$IFS."
- set -- $intf
- IFS="$ORIGIFS"
- if [ $# -eq 2 -a \( "$1" = "ip" -o "$1" = "ip6" \) ]; then
- #
- # It looks like one, but another type of link might be
- # using a name that looks like an implicit IP tunnel.
- # If dladm show-link -P finds it, then it's not an IP
- # tunnel.
- #
- /sbin/dladm show-link -Pp $intf > /dev/null 2>&1
- if [ $? -eq 0 ]; then
- return 1
- else
- return 0
- fi
- fi
- return 1
+ set -o xtrace
+ driver=${SYSINFO_NIC_admin%%[0-9]*}
+ get_link_state ${SYSINFO_NIC_admin}
+ if [[ "$link_state" == "down" ]]; then
+ echo "admin nic '${SYSINFO_NIC_admin}' is down: unplumbing"
+ /sbin/ifconfig ${SYSINFO_NIC_admin} down unplumb
+ wait_for_admin_nic_state "unknown"
+ fi
+
+ # There's some sort of race condition in the bnx driver: if the plumb
+ # command comes too soon after the unplumb, the interface can come up
+ # in a state where it never fires interrupts for link state changes.
+ if [[ "$driver" == "bnx" ]]; then
+ sleep 5
+ fi
+ /sbin/ifconfig ${SYSINFO_NIC_admin} plumb mtu ${CONFIG_admin_mtu:-1500}
+ wait_for_admin_nic_state "up"
}
-bringup_ipif()
+# Creates, plumbs and brings up a vnic with the specified inet parameters
+function vnic_up
{
- for showif_output in `\
- /sbin/ipadm show-if -p -o ifname,state,current`; do
- intf=`echo $showif_output | /usr/bin/cut -f1 -d:`
- state=`echo $showif_output | /usr/bin/cut -f2 -d:`
- current=`echo $showif_output | /usr/bin/cut -f3 -d:`
- if [[ "$state" != "disabled" && $current != *Z* ]]; then
- #
- # skip if not a persistent interface, or if it should
- # get IP configuration from the global zone ('Z' flag
- # is set)
- #
- continue;
- elif is_iptun $intf; then
- # skip IP tunnel interfaces plumbed by net-iptun
- continue;
- elif [ -f /etc/hostname.$intf ] || \
- [ -f /etc/hostname6.$intf ]; then
- if [[ $current != *Z* ]]; then
- echo "found /etc/hostname.$intf "\
- "or /etc/hostname6.$intf, "\
- "ignoring ipadm configuration" > /dev/msglog
- continue;
- else
- echo "Ignoring /etc/hostname*.$intf" \
- > /dev/msglog
- {
- /sbin/ifconfig $intf unplumb
- /sbin/ifconfig $intf inet6 unplumb
- } > /dev/null 2>&1
- fi
- fi
-
- # Enable the interface managed by ipadm
- /sbin/ipadm enable-if -t $intf
- done
+ set -o xtrace
+
+ typeset link="$1"
+ typeset iface="$2"
+ typeset ip="$3"
+ typeset netmask="$4"
+ typeset vlan_id="$5"
+ typeset mac_addr="$6"
+ typeset dhcp_primary="$7"
+ typeset mtu="$8"
+ typeset details=
+ typeset vlan_opt=
+ typeset mac_addr_opt=
+ typeset prop_opt=
+
+ details="link='${link}', iface='${iface}', ip='${ip}'"
+ details="${details}, netmask='${netmask}, vlan_id='${vlan_id}'"
+
+ if [[ -z ${link} ]] || [[ -z ${iface} ]] || \
+ [[ -z ${ip} ]] || ([[ ${ip} != "dhcp" ]] && [[ -z ${netmask} ]]); then
+ echo "WARNING: not bringing up nic (insufficient configuration): " \
+ "$details"
+ return
+ fi
+
+ eval "vnic_already_up=\${vnic_${iface}_up}"
+ if [[ -n "${vnic_already_up}" ]]; then
+ echo "vnic already up: $details"
+ return
+ fi
+
+ if [[ -n ${mac_addr} ]] && [[ -n ${ActiveAggrLinks[${mac_addr}]} ]]; then
+ echo "WARNING: trying to assign MAC address \"${mac_addr}\" to vnic," \
+ " but it already belongs to link aggr " \
+ "\"${ActiveAggrLinks[${mac_addr}]}\""
+ return
+ fi
+
+ echo "Bringing up nic: $details"
+
+ if [[ -n ${vlan_id} ]] && [[ ${vlan_id} != 0 ]]; then
+ vlan_opt="-v ${vlan_id}"
+ fi
+
+ if [[ -n ${mac_addr} ]]; then
+ mac_addr_opt="-m ${mac_addr}"
+ fi
+
+ if [[ -n ${mtu} ]]; then
+ valid_mtu ${iface} ${mtu}
+ prop_opt="-p mtu=${mtu}"
+ fi
+
+ /usr/sbin/dladm create-vnic -t -l ${link} ${prop_opt} ${vlan_opt} \
+ ${mac_addr_opt} ${iface}
+ if [[ $? -ne 0 ]]; then
+ echo "Failed to create VNIC ${iface}"
+ exit $SMF_EXIT_ERR_FATAL
+ fi
+
+ /sbin/ifconfig ${iface} plumb
+ if [[ $? -ne 0 ]]; then
+ echo "Failed to plumb ${iface}"
+ exit $SMF_EXIT_ERR_FATAL
+ fi
+
+ if [[ ${ip} == "dhcp" ]]; then
+ # We ignore errors here because the most common one is that DHCP
+ # is already running.
+
+ if [[ -n ${dhcp_primary} ]]; then
+ /sbin/ifconfig ${iface} dhcp primary || /bin/true
+ else
+ /sbin/ifconfig ${iface} dhcp || /bin/true
+ fi
+ else
+ /sbin/ifconfig ${iface} inet ${ip} netmask ${netmask} up
+ fi
+ eval "vnic_${iface}_up=true"
}
-#
-# All the IPv4 and IPv6 interfaces are plumbed before doing any
-# interface configuration. This prevents errors from plumb failures
-# getting mixed in with the configured interface lists that the script
-# outputs.
-#
+# Creates, plumbs and brings up a vnic with the specified inet6 parameters
+function vnic_up6
+{
+ set -o xtrace
+
+ typeset link="$1"
+ typeset iface="$2"
+ typeset ip="$3"
+ typeset vlan_id="$4"
+ typeset mac_addr="$5"
+ typeset mtu="$6"
+ typeset details=
+ typeset vlan_opt=
+ typeset mac_addr_opt=
+ typeset prop_opt=
+
+ details="link='${link}', iface='${iface}', ip6='${ip}'"
+ details="${details}, vlan_id='${vlan_id}'"
+
+ if [[ -z ${link} ]] || [[ -z ${iface} ]] || [[ -z ${ip} ]]; then
+ echo "WARNING: not bringing up nic (insufficient configuration): " \
+ "$details"
+ return
+ fi
+
+ if [[ -n ${mac_addr} ]] && [[ -n ${ActiveAggrLinks[${mac_addr}]} ]]; then
+ echo "WARNING: trying to assign MAC address \"${mac_addr}\" to vnic," \
+ " but it already belongs to link aggr " \
+ "\"${ActiveAggrLinks[${mac_addr}]}\""
+ return
+ fi
+
+ # only bring up nic if not already up
+ eval "vnic_already_up=\${vnic_${iface}_up}"
+ if [[ -z "${vnic_already_up}" ]]; then
+ echo "Bringing up nic: $details"
+
+ if [[ -n ${vlan_id} ]] && [[ ${vlan_id} != 0 ]]; then
+ vlan_opt="-v ${vlan_id}"
+ fi
+
+ if [[ -n ${mac_addr} ]]; then
+ mac_addr_opt="-m ${mac_addr}"
+ fi
+
+ if [[ -n ${mtu} ]]; then
+ valid_mtu ${iface} ${mtu}
+ prop_opt="-p mtu=${mtu}"
+ fi
+
+ /usr/sbin/dladm create-vnic -t -l ${link} ${prop_opt} ${vlan_opt} \
+ ${mac_addr_opt} ${iface}
+ if [[ $? -ne 0 ]]; then
+ echo "Failed to create VNIC ${iface}"
+ exit $SMF_EXIT_ERR_FATAL
+ fi
+ fi
+
+ /sbin/ifconfig ${iface} inet6 plumb
+ if [[ $? -ne 0 ]]; then
+ echo "Failed to plumb ${iface}"
+ exit $SMF_EXIT_ERR_FATAL
+ fi
+
+ if [[ -n ${ip} ]]; then
+ /sbin/ifconfig ${iface} inet6 up
+ fi
+ if [[ ${ip} != "addrconf" ]]; then
+ /sbin/ifconfig ${iface} inet6 \
+ addif ${ip} preferred up
+ fi
+ eval "vnic_${iface}_up=true"
+}
-#
-# First deal with /etc/hostname
-#
-# Get the list of IPv4 interfaces to configure by breaking
-# /etc/hostname.* into separate args by using "." as a shell separator
-# character.
-#
-interface_names="`echo /etc/hostname.*[0-9] 2>/dev/null`"
-if [ "$interface_names" != "/etc/hostname.*[0-9]" ]; then
- ORIGIFS="$IFS"
- IFS="$IFS."
- set -- $interface_names
- IFS="$ORIGIFS"
- while [ $# -ge 2 ]; do
- shift
- intf_name=$1
- while [ $# -gt 1 -a "$2" != "/etc/hostname" ]; do
- intf_name="$intf_name.$2"
- shift
- done
- shift
-
- # skip IP tunnel interfaces plumbed by net-iptun.
- if is_iptun $intf_name; then
- continue
- fi
-
- read one rest < /etc/hostname.$intf_name
- if [ "$one" = ipmp ]; then
- ipmp_list="$ipmp_list $intf_name"
- else
- inet_list="$inet_list $intf_name"
- fi
- done
-fi
+# If there are aggregations in sysinfo, set them up.
+function create_aggrs
+{
+ set -o xtrace
+ typeset links macs mode mtu
+ if [[ -z "${SYSINFO_Aggregations}" ]]; then
+ return 0
+ fi
+
+ aggrs=(${SYSINFO_Aggregations//,/ })
+ for aggr in "${aggrs[@]}"; do
+ eval "links=\${SYSINFO_Aggregation_${aggr}_Interfaces}"
+ eval "macs=\${SYSINFO_Aggregation_${aggr}_MACs}"
+ eval "mode=\${SYSINFO_Aggregation_${aggr}_LACP_mode}"
+ eval "mtu=\${CONFIG_${aggr}_mtu}"
+ [[ -z "$mode" ]] && mode="off"
+
+ echo "Creating aggr: ${aggr} (mode=${mode}, links=${links})"
+ dladm create-aggr -l ${links//,/ -l } -L ${mode} ${aggr}
+ if [[ $? -eq 0 ]]; then
+ add_active_aggr_links ${aggr} ${macs}
+ fi
+
+ if [[ -n "$mtu" ]]; then
+ dladm set-linkprop -p mtu=${mtu} ${aggr}
+ if [[ $? -ne 0 ]]; then
+ echo "Failed to set mtu on aggr ${aggr} to ${mtu}"
+ exit $SMF_EXIT_ERR_FATAL
+ fi
+ fi
+ done
+
+ # Creating the aggregations may affect the nic tags in sysinfo, so update:
+ /usr/bin/sysinfo -u
+ load_sdc_sysinfo
+}
#
-# Get the list of IPv6 interfaces to configure by breaking
-# /etc/hostname6.* into separate args by using "." as a shell separator
-# character.
+# Try various config parameters to set the default route
#
-interface_names="`echo /etc/hostname6.*[0-9] 2>/dev/null`"
-if [ "$interface_names" != "/etc/hostname6.*[0-9]" ]; then
- ORIGIFS="$IFS"
- IFS="$IFS."
- set -- $interface_names
- IFS="$ORIGIFS"
- while [ $# -ge 2 ]; do
- shift
- intf_name=$1
- while [ $# -gt 1 -a "$2" != "/etc/hostname6" ]; do
- intf_name="$intf_name.$2"
- shift
- done
- shift
-
- # skip IP tunnel interfaces plumbed by net-iptun.
- if is_iptun $intf_name; then
- continue
- fi
-
- read one rest < /etc/hostname6.$intf_name
- if [ "$one" = ipmp ]; then
- ipmp6_list="$ipmp6_list $intf_name"
- else
- inet6_list="$inet6_list $intf_name"
- fi
- done
-fi
+function set_default_route
+{
+ set -o xtrace
+ typeset default_gw
-#
-# Create all of the IPv4 IPMP interfaces.
-#
-if [ -n "$ipmp_list" ]; then
- set -- $ipmp_list
- while [ $# -gt 0 ]; do
- if /sbin/ifconfig $1 ipmp; then
- ipmp_created="$ipmp_created $1"
- else
- ipmp_failed="$ipmp_failed $1"
- fi
- shift
- done
- [ -n "$ipmp_failed" ] && warn_failed_ifs "create IPv4 IPMP" \
- "$ipmp_failed"
-fi
+ if [[ -n "${CONFIG_headnode_default_gateway}" ]]; then
+ default_gw="${CONFIG_headnode_default_gateway}"
-#
-# Step through the IPv4 interface list and try to plumb every interface.
-# Generate list of plumbed and failed IPv4 interfaces.
-#
-if [ -n "$inet_list" ]; then
- set -- $inet_list
- while [ $# -gt 0 ]; do
- /sbin/ifconfig $1 plumb
- if /sbin/ifconfig $1 inet >/dev/null 2>&1; then
- inet_plumbed="$inet_plumbed $1"
- else
- inet_failed="$inet_failed $1"
- fi
- shift
- done
- [ -n "$inet_failed" ] && warn_failed_ifs "plumb IPv4" "$inet_failed"
-fi
+ elif [[ -n ${CONFIG_admin_gateway} ]]; then
+ default_gw="${CONFIG_admin_gateway}"
-# Run autoconf to connect to a WLAN if the interface is a wireless one
-if [ -x /sbin/wificonfig -a -n "$inet_plumbed" ]; then
- set -- $inet_plumbed
- while [ $# -gt 0 ]; do
- if [ -r /dev/wifi/$1 ]; then
- /sbin/wificonfig -i $1 startconf >/dev/null
- fi
- shift
- done
-fi
+ elif [[ -n ${BOOT_admin_gateway} ]]; then
+ default_gw=${BOOT_admin_gateway}
-#
-# Step through the IPv6 interface list and plumb every interface.
-# Generate list of plumbed and failed IPv6 interfaces. Each plumbed
-# interface will be brought up later, after processing any contents of
-# the /etc/hostname6.* file.
-#
-if [ -n "$inet6_list" ]; then
- set -- $inet6_list
- while [ $# -gt 0 ]; do
- /sbin/ifconfig $1 inet6 plumb
- if /sbin/ifconfig $1 inet6 >/dev/null 2>&1; then
- inet6_plumbed="$inet6_plumbed $1"
- else
- inet6_failed="$inet6_failed $1"
- fi
- shift
- done
- [ -n "$inet6_failed" ] && warn_failed_ifs "plumb IPv6" "$inet6_failed"
-fi
+ elif [[ -n ${CONFIG_external_gateway} ]]; then
+ default_gw=${CONFIG_external_gateway}
+ fi
-#
-# Create all of the IPv6 IPMP interfaces.
-#
-if [ -n "$ipmp6_list" ]; then
- set -- $ipmp6_list
- while [ $# -gt 0 ]; do
- if /sbin/ifconfig $1 inet6 ipmp; then
- ipmp6_created="$ipmp6_created $1"
- else
- ipmp6_failed="$ipmp6_failed $1"
- fi
- shift
- done
- [ -n "$ipmp6_failed" ] && warn_failed_ifs "create IPv6 IPMP" \
- "$ipmp6_failed"
-fi
+ if [[ -n ${default_gw} ]]; then
+ echo "${default_gw}" > /etc/defaultrouter
+ fi
-#
-# Upgrade ipadm.conf.
-#
-if /usr/bin/grep -q _family /etc/ipadm/ipadm.conf; then
- oldifs=$(/usr/bin/sed -En \
- 's/^_ifname=([a-z0-9_]+);_family=[0-9]+;$/\1/p' \
- /etc/ipadm/ipadm.conf | /usr/bin/sort -u)
- /usr/bin/sed -i '/_family/d' /etc/ipadm/ipadm.conf
- for oldif in $oldifs; do
- /usr/bin/printf \
- "_ifname=%s;_ifclass=0;_families=2,26;\n" \
- $oldif >> /etc/ipadm/ipadm.conf
- done
-fi
+ if [[ -n ${CONFIG_admin_gateway6} ]]; then
+ default_gw6="${CONFIG_admin_gateway6}"
-#
-# Finally configure interfaces set up with ipadm. Any /etc/hostname*.intf
-# files take precedence over ipadm defined configurations except when
-# we are in a non-global zone and Layer-3 protection of IP addresses is
-# enforced on the interface by the global zone.
-#
-bringup_ipif
+ elif [[ -n ${BOOT_admin_gateway6} ]]; then
+ default_gw6=${BOOT_admin_gateway6}
-#
-# Process the /etc/hostname[6].* files for IPMP interfaces. Processing these
-# before non-IPMP interfaces avoids accidental implicit IPMP group creation.
-#
-[ -n "$ipmp_created" ] && if_configure inet "IPMP" $ipmp_created
-[ -n "$ipmp6_created" ] && if_configure inet6 "IPMP" $ipmp6_created
+ elif [[ -n ${CONFIG_external_gateway6} ]]; then
+ default_gw6=${CONFIG_external_gateway6}
+ fi
+
+ if [[ -n ${default_gw6} ]]; then
+ # add static route
+ /usr/sbin/route add -inet6 default ${default_gw6}
+ fi
+}
#
-# Process the /etc/hostname[6].* files for non-IPMP interfaces.
+# Go through and set up the MTU for all of the various nic tags
#
-[ -n "$inet_plumbed" ] && if_configure inet "" $inet_plumbed
-[ -n "$inet6_plumbed" ] && if_configure inet6 "" $inet6_plumbed
+function setup_mtu
+{
+ set -o xtrace
+ typeset tag oldifs val mac link curmtu
+ typeset -A mtus
+ typeset -A tagmap
+
+ set -o xtrace
+ oldifs=$IFS
+ IFS=,
+ for tag in ${SYSINFO_Nic_Tags}; do
+ eval "val=\${CONFIG_${tag}_mtu}"
+ eval "mac=\${CONFIG_${tag}_nic}"
+ [[ -z "$val" ]] && continue
+
+ valid_mtu ${tag} $val
+
+ #
+ # Note, it doesn't matter what tag we use for a given mac
+ # address, because we'll always get the same link name later on.
+ #
+ if [[ -z "${tagmap[$mac]}" ]]; then
+ tagmap[$mac]=$tag
+ fi
+
+ if [[ -z "${mtus[$mac]}" ]]; then
+ mtus[$mac]=$val
+ elif [[ "${mtus[$mac]}" -lt $val ]]; then
+ mtus[$mac]=$val
+ fi
+ done
+ IFS=$oldifs
+
+ for mac in ${!mtus[@]}; do
+ tag=${tagmap[$mac]}
+ eval "link=\${SYSINFO_NIC_${tag}}"
+ if [[ -z "${link}" ]]; then
+ echo "/usbkey/config error: Missing link name for ${tag}"
+ exit $SMF_EXIT_ERR_FATAL
+ fi
+
+ #
+ # Check the current MTU of the device. To help out devices which
+ # don't support the setting of the MTU (here's looking at you
+ # bnx), if the MTU is identical to its default, don't do
+ # anything and save the poor folks stuck with bnx some grief.
+ #
+ curmtu=$(/usr/sbin/dladm show-linkprop -c -o value -p mtu ${link})
+ [[ $? -eq 0 ]] && [[ "$curmtu" -eq "${mtus[$mac]}" ]] && continue
+
+ if ! /usr/sbin/dladm set-linkprop -p mtu=${mtus[$mac]} ${link}; then
+ echo "Failed to set mtu to ${mtus[$mac]} for link ${link}"
+ exit $SMF_EXIT_ERR_FATAL
+ fi
+ done
+}
#
-# For the IPv4 and IPv6 interfaces that failed to plumb, find (or create)
-# IPMP meta-interfaces to host their data addresses.
+# Set up resolvers, if we can
#
-[ -n "$inet_failed" ] && move_addresses inet
-[ -n "$inet6_failed" ] && move_addresses inet6
-
-# Run DHCP if requested. Skip boot-configured interface.
-interface_names="`echo /etc/dhcp.*[0-9] 2>/dev/null`"
-if [ "$interface_names" != '/etc/dhcp.*[0-9]' ]; then
- #
- # First find the primary interface. Default to the first
- # interface if not specified. First primary interface found
- # "wins". Use care not to "reconfigure" a net-booted interface
- # configured using DHCP. Run through the list of interfaces
- # again, this time trying DHCP.
- #
- i4d_fail=
- firstif=
- primary=
- ORIGIFS="$IFS"
- IFS="${IFS}."
- set -- $interface_names
-
- while [ $# -ge 2 ]; do
- shift
- [ -z "$firstif" ] && firstif=$1
-
- for i in `shcat /etc/dhcp\.$1`; do
- if [ "$i" = primary ]; then
- primary=$1
- break
- fi
- done
-
- [ -n "$primary" ] && break
- shift
- done
-
- [ -z "$primary" ] && primary="$firstif"
- cmdline=`shcat /etc/dhcp\.${primary}`
-
- if [ "$_INIT_NET_IF" != "$primary" ]; then
- echo "starting DHCP on primary interface $primary"
- /sbin/ifconfig $primary auto-dhcp primary $cmdline
- # Exit code 4 means ifconfig timed out waiting for dhcpagent
- [ $? != 0 ] && [ $? != 4 ] && i4d_fail="$i4d_fail $primary"
- fi
-
- set -- $interface_names
-
- while [ $# -ge 2 ]; do
- shift
- cmdline=`shcat /etc/dhcp\.$1`
- if [ "$1" != "$primary" -a \
- "$1" != "$_INIT_NET_IF" ]; then
- echo "starting DHCP on interface $1"
- /sbin/ifconfig $1 dhcp start wait 0 $cmdline
- # Exit code can't be timeout when wait is 0
- [ $? != 0 ] && i4d_fail="$i4d_fail $1"
- fi
- shift
- done
- IFS="$ORIGIFS"
- unset ORIGIFS
- [ -n "$i4d_fail" ] && warn_failed_ifs "configure IPv4 DHCP" "$i4d_fail"
-fi
+function configure_resolv_conf
+{
+ if [[ ${RESOLV_CONF_DONE} == true ]]; then
+ return
+ elif [[ -n ${CONFIG_dns_domain} ]] && [[ -n ${CONFIG_dns_resolvers} ]]; then
+ echo "search ${CONFIG_dns_domain}" > /etc/resolv.conf
+ if [[ -n ${CONFIG_binder_admin_ips} ]]; then
+ for serv in $(echo "${CONFIG_binder_admin_ips}" | sed -e "s/,/ /g"); do
+ echo "nameserver ${serv}" >> /etc/resolv.conf
+ done
+ fi
+ for serv in $(echo "${CONFIG_dns_resolvers}" | sed -e "s/,/ /g"); do
+ echo "nameserver ${serv}" >> /etc/resolv.conf
+ done
+ RESOLV_CONF_DONE=true
+ fi
+}
-# There is a chicken-and-egg problem with bringing up overlay VNICs at boot
-# time. When the first VNIC is added to an overlay, it creates a kernel socket
-# to listen for incoming encapsulated frames. Therefore, VNICs cannot be added
-# until after IP interfaces have been brought up. Overlay VNICs may themselves
-# have IP interfaces over them and so it is necessary to attempt to bring up
-# any remaining IP interfaces once the overlay VNICs are in place.
-if smf_is_globalzone && dladm show-link -p -o class | egrep -s 'overlay'; then
- echo "Bringing up any remaining VNICs on overlays"
- /sbin/dladm up-vnic
- echo "Bringing up any remaining IP interfaces on overlay VNICs"
- bringup_ipif
-fi
+# Helper function for plumbing an interface for an address family once
+typeset -A plumbedifs
+function plumbif
+{
+ iface=$1
+ inet=$2
+ addrtype=$3
+ if [[ -z ${plumbedifs[${iface},${inet}]} ]]; then
+ plumbedifs[${iface},${inet}]="true"
+ /sbin/ifconfig ${iface} ${inet} plumb
+ if [[ ! ${addrtype} =~ "^vrrp" ]]; then
+ /sbin/ifconfig ${iface} ${inet} up
+ fi
+ fi
+}
-# In order to avoid bringing up the interfaces that have
-# intentionally been left down, perform RARP only if the system
-# has no configured hostname in /etc/nodename
-hostname="`shcat /etc/nodename 2>/dev/null`"
-if [ "$_INIT_NET_STRATEGY" = "rarp" -o -z "$hostname" ]; then
- /sbin/ifconfig -adD4 auto-revarp netmask + broadcast + up
+if smf_is_globalzone; then
+ EARLY_ADMIN=
+ [[ -f /etc/svc/volatile/.early_admin_setup ]] && EARLY_ADMIN=1
+ [[ -n "$EARLY_ADMIN" ]] || /usr/sbin/dladm init-phys
+
+ # The next command is for logging purposes only
+ log_if_state before
+
+ # Load sysinfo variables with SYSINFO_ prefix: we primarily care about
+ # the NIC_variables, which contain the actual interface name for a nic
+ # tag (it has mapped the foo_nic=<MAC address> variables to interface
+ # names for us).
+ load_sdc_sysinfo
+
+ if boot_file_config_enabled; then
+ # We have a boot-time networking file present - use its values rather
+ # than ones from the config file or bootparams
+ if ! boot_file_config_valid; then
+ echo "ERROR: boot-time network config file incorrect"
+ exit ${SMF_EXIT_ERR_CONFIG}
+ fi
+
+ load_boot_file_config
+
+ # NOTE: some of the routes boot_file_config_init tries to add may
+ # fail if they are admin network routes added by the
+ # network/early-admin service. This is expected and not a problem.
+ boot_file_config_init
+ else
+ # Load config variables with CONFIG_ prefix,
+ # and sets the headnode variable
+ load_sdc_config
+ # Load boot params with BOOT_ prefix
+ load_sdc_bootparams
+ fi
+
+ # Set up etherstubs
+ for stub in $(echo "${CONFIG_etherstub}" | sed -e "s/,/ /g"); do
+ /usr/sbin/dladm create-etherstub -t $stub || echo "ERROR: could not create etherstub ${stub}."
+ done
+
+ # Create aggregations
+ create_aggrs
+
+ # Make any mtu adjustments that may be necessary
+ setup_mtu
+
+ # Setup admin NIC
+ ADMIN_NIC_TAG=${CONFIG_admin_tag:-"admin"}
+
+ # If there is no NIC with the admin tag, and the config has
+ # admin_nic_autoselect=true, designate the first NIC reported
+ # by dladm for admin use. This is useful in environments where
+ # the NICs are known to change beneath us.
+ if [[ "${BOOT_smartos}" == "true" ]] && \
+ [[ "${CONFIG_admin_nic_autoselect}" == "true" ]] && \
+ ! nictagadm exists $ADMIN_NIC_TAG ; then
+ autoselected_admin_nic=$(dladm show-phys -m -p -o address | head -n1)
+ if [[ -z ${autoselected_admin_nic} ]] ; then
+ echo "ERROR: no NICs found, unable to autoselect admin NIC."
+ exit ${SMF_EXIT_ERR_CONFIG}
+ fi
+
+ nictagadm add $ADMIN_NIC_TAG "${autoselected_admin_nic}"
+ if [[ $? -ne 0 ]] ; then
+ echo "ERROR: unable to add admin tag to NIC ${autoselected_admin_nic}"
+ exit ${SMF_EXIT_ERR_FATAL}
+ fi
+
+ SYSINFO_NIC_admin=$(dladm show-phys -m -p -o link | head -n1)
+ if [[ -n ${SYSINFO_NIC_admin} ]] ; then
+ echo "Autoselected ${SYSINFO_NIC_admin} for use as admin NIC."
+ fi
+
+ nictagadm list
+ elif [[ -v CONFIG_admin_tag ]]; then
+ #
+ # This handles the case when the 'admin_tag' property is set to
+ # override the default admin nic tag.
+ #
+ eval SYSINFO_NIC_admin='$'SYSINFO_NIC_${CONFIG_admin_tag}
+
+ eval CONFIG_admin_ip='$'CONFIG_${CONFIG_admin_tag}_ip
+ eval CONFIG_admin_ip6='$'CONFIG_${CONFIG_admin_tag}_ip6
+ eval CONFIG_admin_netmask='$'CONFIG_${CONFIG_admin_tag}_netmask
+ eval CONFIG_admin_mtu='$'CONFIG_${CONFIG_admin_tag}_mtu
+ eval CONFIG_admin_gateway='$'CONFIG_${CONFIG_admin_tag}_gateway
+ eval CONFIG_admin_gateway6='$'CONFIG_${CONFIG_admin_tag}_gateway6
+ fi
+
+ if [[ -z "${SYSINFO_NIC_admin}" ]]; then
+ echo "ERROR: admin NIC not found, unable to bring up admin network."
+ exit ${SMF_EXIT_ERR_CONFIG}
+ fi
+
+ [[ -n "$EARLY_ADMIN" ]] || plumb_admin
+
+ # If we performed early configuration of the admin network and
+ # the admin interface is on a link aggregation, the aggregation
+ # has already been configured. Add it to the list of active aggrs
+ # so the checks in vnic_up[6] are aware of it.
+ if [[ -n "$EARLY_ADMIN" && "${SYSINFO_NIC_admin}" =~ aggr[0-9]+$ ]]; then
+ eval "macs=\${SYSINFO_Aggregation_${SYSINFO_NIC_admin}_MACs}"
+
+ add_active_aggr_links ${SYSINFO_NIC_admin} $macs
+ fi
+
+ # Prefer the config file for admin nic values, but use
+ # bootparams if present
+ admin_ip=${CONFIG_admin_ip}
+ if [[ -z "$admin_ip" ]]; then
+ admin_ip=${BOOT_admin_ip}
+ fi
+
+ admin_netmask=${CONFIG_admin_netmask}
+ if [[ -z "$admin_netmask" ]]; then
+ admin_netmask=${BOOT_admin_netmask}
+ fi
+
+ admin_ip6=${CONFIG_admin_ip6}
+ if [[ -z "$admin_ip6" ]]; then
+ admin_ip6=${BOOT_admin_ip6}
+ fi
+
+ if [[ $admin_ip == 'none' ]]; then
+ echo 'INFO: not configuring IP on admin interface (admin_ip=none)'
+ elif [[ -n "$EARLY_ADMIN" ]]; then
+ echo 'INFO: admin interface already configured (early setup)'
+ ADMIN_NIC_UP=true
+ elif [[ -n $admin_ip ]] && [[ -n $admin_netmask ]]; then
+ /sbin/ifconfig ${SYSINFO_NIC_admin} inet ${admin_ip} \
+ netmask ${admin_netmask} up
+ ADMIN_NIC_UP=true
+
+ # also setup resolv.conf if we can
+ configure_resolv_conf
+ else
+ if [[ ${headnode} == "true" ]]; then
+ echo "ERROR: headnode but no admin_{ip,netmask} in config, not bringing up admin network."
+ # Set a flag, but try to plumb the other interfaces anyway
+ ADMIN_NIC_MISCONFIGURED=true
+ else
+ # We ignore errors here because the most common one is that DHCP is
+ # already running.
+ /sbin/ifconfig ${SYSINFO_NIC_admin} dhcp || /bin/true
+
+ # Wait for DHCP
+ timeout=${ADMIN_DHCP_TIMEOUT}
+ dhcp_admin_ip=$(/sbin/ifconfig ${SYSINFO_NIC_admin} | grep inet | awk '{ print $2 }')
+ while [[ (-z ${dhcp_admin_ip} || ${dhcp_admin_ip} == "0.0.0.0") && ${timeout} -gt 0 ]]; do
+ dhcp_admin_ip=$(/sbin/ifconfig ${SYSINFO_NIC_admin} | grep inet | awk '{ print $2 }')
+ timeout=$((${timeout} - 1))
+ sleep 1
+ done
+
+ ADMIN_NIC_UP=true
+ fi
+ fi
+
+ if [[ -n ${admin_ip6} && -z "$EARLY_ADMIN" ]]; then
+ # Plumb interface for inet6
+ ifconfig ${SYSINFO_NIC_admin} inet6 \
+ plumb mtu ${CONFIG_admin_mtu:-1500} up
+
+ # Autodiscovery IPv6 using SLAAC
+ # NOTE: in.ndpd will be started later, due to plumbing the interface.
+ # this means autodiscovery also happens when configuring a
+ # static address.
+ #
+ # in.ndpd also sets a default route and this can't be disabled.
+
+ # Configure static IPv6
+ if [[ ${admin_ip6} != "addrconf" ]]; then
+ /sbin/ifconfig ${SYSINFO_NIC_admin} inet6 \
+ addif ${admin_ip6} preferred up
+ fi
+
+ ADMIN_NIC_UP=true
+
+ # don't setup resolv.conf, IPv6 addresses already work
+ fi
+
+ # If on Parallels or VirtualBox, create a bridge which
+ # allows traffic to flow correctly to the host-only network
+ if [[ "${ADMIN_NIC_UP}" == "true" ]] \
+ && [[ ${SYSINFO_Product} == "Parallels Virtual Platform" \
+ || ${SYSINFO_Product} == "VirtualBox" ]] \
+ && [[ -z $(/usr/sbin/dladm show-bridge -p vmwarebr) ]]; then
+ /usr/sbin/dladm create-bridge -l ${SYSINFO_NIC_admin} vmwarebr
+ fi
+
+ # Setup the external NIC. The installer may have already set up external0,
+ # so, if it exists, we're not going to try and set up the vnic again.
+ if [[ -n ${SYSINFO_NIC_external} ]] \
+ && ! dladm show-vnic external0 > /dev/null; then
+
+ if [[ -n "${CONFIG_external_ip}" ]]; then
+ vnic_up "${SYSINFO_NIC_external}" "external0" \
+ "${CONFIG_external_ip}" "${CONFIG_external_netmask}" \
+ "${CONFIG_external_vlan_id}" "${CONFIG_external_mac}" \
+ "primary" "${CONFIG_external_mtu}"
+ fi
+
+ if [[ -n "${CONFIG_external_ip6}" ]]; then
+ vnic_up6 "${SYSINFO_NIC_external}" "external0" \
+ "${CONFIG_external_ip6}" \
+ "${CONFIG_external_vlan_id}" "${CONFIG_external_mac}" \
+ "${CONFIG_external_mtu}"
+ fi
+ fi
+
+ set_default_route
+
+ # Setup extra nics, if specified in the config file
+ nic_tags="${SYSINFO_Nic_Tags}"
+ if [[ -n "${nic_tags}" ]]; then
+ tags=(${nic_tags//,/ })
+
+ if boot_file_config_enabled; then
+ bootparam_ip_keys=""
+ bootparam_ip6_keys=""
+ config_ip_keys=${CONFIG_bootfile_ip_keys//,/ }
+ config_ip6_keys=${CONFIG_bootfile_ip6_keys//,/ }
+ else
+ bootparam_ip_keys=$(sdc_bootparams_keys | grep -- "-ip$" || true)
+ bootparam_ip6_keys=$(sdc_bootparams_keys | grep -- "-ip6$" || true)
+ config_ip_keys=$(sdc_config_keys | grep "_ip$" || true)
+ config_ip6_keys=$(sdc_config_keys | grep "_ip6$" || true)
+ fi
+
+ for tag in "${tags[@]}"; do
+
+ eval "link=\${SYSINFO_NIC_${tag}}"
+ if [[ -z "${link}" ]]; then
+ echo "WARNING: No link found with tag '${tag}'"
+ continue
+ fi
+
+ for key in ${config_ip_keys}; do
+ if [[ ${key} == ${tag}[0-9]_ip ]] || [[ ${key} == ${tag}[0-9][0-9]_ip ]]; then
+ iface=${key//_ip/}
+ #echo " iface=$iface"
+ eval "ip=\${CONFIG_${iface}_ip}"
+ eval "netmask=\${CONFIG_${iface}_netmask}"
+ eval "vlan=\${CONFIG_${iface}_vlan_id}"
+ eval "macaddr=\${CONFIG_${iface}_mac}"
+ eval "mtu=\${CONFIG_${iface}_mtu}"
+
+ echo vnic_up "${link}" "${iface}" "${ip}" "${netmask}" "${vlan}" "${macaddr}" "${mtu}"
+ vnic_up "${link}" "${iface}" "${ip}" "${netmask}" \
+ "${vlan}" "${macaddr}" "" "${mtu}"
+ fi
+ done
+
+ for key in ${bootparam_ip_keys}; do
+ if [[ ${key} == ${tag}[0-9]-ip ]] || [[ ${key} == ${tag}[0-9][0-9]-ip ]]; then
+ iface=${key//-ip/}
+ eval "ip=\${BOOT_${iface}_ip}"
+ eval "netmask=\${BOOT_${iface}_netmask}"
+ eval "vlan=\${BOOT_${iface}_vlan_id}"
+ eval "macaddr=\${BOOT_${iface}_mac}"
+ eval "mtu=\${CONFIG_${iface}_mtu}"
+ echo vnic_up "${link}" "${iface}" "${ip}" "${netmask}" "${vlan}" "${macaddr}" "${mtu}"
+ vnic_up "${link}" "${iface}" "${ip}" "${netmask}" \
+ "${vlan}" "${macaddr}" "" "${mtu}"
+ fi
+ done
+
+ for key in ${config_ip6_keys}; do
+ if [[ ${key} == ${tag}[0-9]_ip6 ]] || [[ ${key} == ${tag}[0-9][0-9]_ip6 ]]; then
+ iface=${key//_ip6/}
+ #echo " iface=$iface"
+ eval "ip=\${CONFIG_${iface}_ip6}"
+ eval "vlan=\${CONFIG_${iface}_vlan_id}"
+ eval "macaddr=\${CONFIG_${iface}_mac}"
+ eval "mtu=\${CONFIG_${iface}_mtu}"
+
+ echo vnic_up6 "${link}" "${iface}" "${ip}" "${vlan}" "${macaddr}" "${mtu}"
+ vnic_up6 "${link}" "${iface}" "${ip}" \
+ "${vlan}" "${macaddr}" "${mtu}"
+ fi
+ done
+
+ for key in ${bootparam_ip6_keys}; do
+ if [[ ${key} == ${tag}[0-9]-ip6 ]] || [[ ${key} == ${tag}[0-9][0-9]-ip6 ]]; then
+ iface=${key//-ip6/}
+ eval "ip=\${BOOT_${iface}_ip6}"
+ eval "vlan=\${BOOT_${iface}_vlan_id}"
+ eval "macaddr=\${BOOT_${iface}_mac}"
+ eval "mtu=\${CONFIG_${iface}_mtu}"
+ echo vnic_up6 "${link}" "${iface}" "${ip}" "${vlan}" "${macaddr}" "${mtu}"
+ vnic_up6 "${link}" "${iface}" "${ip}" \
+ "${vlan}" "${macaddr}" "${mtu}"
+ fi
+ done
+
+ done
+
+ # All vnics are done. If the config has admin_ip=none, then we won't
+ # have resolvers set up yet, so try again here.
+ configure_resolv_conf
+ fi
+else
+ # Non-global zones
+
+ # Bring up statically assigned interfaces, and find the primary DHCP
+ # interface, if it exists
+ while IFS=: read -r iface addrtype; do
+ # Keep track of whether or not we've configured our first IPv4
+ # address on this interface
+ first_ipv4_configured=""
+ iface_configured=""
+
+ if [[ -f /etc/dhcp.${iface} ]]; then
+ plumbif ${iface} inet ${addrtype}
+ if [[ -z "${primary}" ]]; then
+ /sbin/ifconfig ${iface} auto-dhcp primary
+ primary=${iface}
+ else
+ /sbin/ifconfig ${iface} auto-dhcp
+ fi
+ first_ipv4_configured="true"
+ iface_configured="true"
+ fi
+
+ if [[ -f /etc/hostname.${iface} ]]; then
+ while read ifparams; do
+ # For IPv4, we need to set the address on the first logical
+ # interface. For IPv6, the address on the first interface is
+ # the link-local address, and can't be changed.
+ #
+ # Lines starting with inet indicate the hostname for that
+ # interface, and are used by dhcpagent. Skip over them.
+ if [[ -f /etc/dhcp.${iface} && "${ifparams}" == inet* ]]; then
+ continue
+ elif [[ "${ifparams}" == {3}({1,3}(\d).){1,3}(\d)* ]]; then
+ plumbif ${iface} inet ${addrtype}
+ if [[ -z "${first_ipv4_configured}" ]]; then
+ first_ipv4_configured="true"
+ ifcommand="inet"
+ else
+ ifcommand="inet addif"
+ fi
+ else
+ plumbif ${iface} inet6 ${addrtype}
+ ifcommand="inet6 addif"
+ fi
+
+ # vrrp interfaces can't be brought up with ifconfig: vrrpadm
+ # handles that instead
+ if [[ "${addrtype}" =~ "^vrrp" ]]; then
+ /sbin/ifconfig ${iface} ${ifcommand} \
+ `printf "%s" "${ifparams}" | sed -e 's/ up//'`
+ else
+ /sbin/ifconfig ${iface} ${ifcommand} ${ifparams} up
+ fi
+
+ iface_configured="true"
+ done < /etc/hostname.${iface}
+ fi
+
+ if [[ -f /etc/addrconf.${iface} ]]; then
+ # Enable the NDP daemon, so that once this script finishes, we'll
+ # be able to pick up router advertisments and finish configuring
+ # the network interface. We then just need to plumb the interface,
+ # and let in.ndpd take care of configuring addresses.
+ svcadm enable svc:/network/routing/ndp:default
+ plumbif ${iface} inet6 ${addrtype}
+ iface_configured="true"
+ fi
+
+ # If we didn't configure the device at all, mark it for DHCP if we
+ # don't do DHCP on anyone else.
+ if [[ -z ${iface_configured} && -z "${first_iface}" ]]; then
+ first_iface=${iface}
+ fi
+ done < <(/usr/sbin/dladm show-vnic -p -o link,macaddrtype 2>/dev/null)
+
+ if [[ -z "${primary}" && -n "${first_iface}" ]]; then
+ first_iface_type=$(dladm show-vnic ${first_iface} -p -o macaddrtype)
+ plumbif ${first_iface} inet ${first_iface_type}
+ /sbin/ifconfig ${first_iface} auto-dhcp
+ primary=${first_iface}
+ fi
+
+ while IFS=: read -r iface addrtype; do
+ if [[ "${iface}" == "${primary}"
+ || -f /etc/hostname.${iface}
+ || -f /etc/dhcp.${iface}
+ || -f /etc/addrconf.${iface}
+ || "${addrtype}" =~ "^vrrp" ]]; then
+ continue
+ fi
+
+ plumbif ${iface} inet ${addrtype}
+ /sbin/ifconfig ${iface} auto-dhcp start wait 0
+ done < <(/usr/sbin/dladm show-vnic -p -o link,macaddrtype 2>/dev/null)
fi
-#
-# If the /etc/defaultrouter file exists, process it now so that the next
-# stage of booting will have access to NFS.
-#
-if [ -f /etc/defaultrouter ]; then
- while read router rubbish; do
- case "$router" in
- '#'* | '') ;; # Ignore comments, empty lines
- *) /sbin/route -n add default -gateway $router ;;
- esac
- done </etc/defaultrouter
+log_if_state after
+
+# Since we hopefully made networking changes here, update the sysinfo cache
+if smf_is_globalzone; then
+ /usr/bin/sysinfo -u
fi
-#
-# If we get here and were not asked to plumb any IPv4 interfaces, look
-# for boot properties that direct us.
-#
-# - The "network-interface" property is required and indicates the
-# interface name.
-# - The "xpv-hcp" property, if present, is used by the hypervisor
-# tools to indicate how the specified interface should be configured.
-# Permitted values are "dhcp" and "off", where "off" indicates static
-# IP configuration.
-#
-# In the case where "xpv-hcp" is set to "dhcp", no further properties
-# are required or examined.
-#
-# In the case where "xpv-hcp" is not present or set to "off", the
-# "host-ip" and "subnet-mask" properties are used to configure
-# the specified interface. The "router-ip" property, if present,
-# is used to add a default route.
-#
-nic="`/sbin/devprop network-interface`"
-if smf_is_globalzone && [ -z "$inet_list" ] && [ -n "$nic" ]; then
- hcp="`/sbin/devprop xpv-hcp`"
- case "$hcp" in
- "dhcp")
- /sbin/ifconfig $nic plumb 2>/dev/null
- [ -n "`/sbin/ifconfig $nic 2>/dev/null`" ] && (
- # The interface is successfully plumbed, so
- # modify "inet_list" to force the exit code
- # checks to work.
- inet_list=$nic;
- # Given that this is the only IPv4 interface,
- # we assert that it is primary.
- echo "starting DHCP on primary interface $primary";
- /sbin/ifconfig $nic auto-dhcp primary;
- # Exit code 4 means ifconfig timed out waiting
- # for dhcpagent
- [ $? != 0 ] && [ $? != 4 ] && \
- i4d_fail="$i4d_fail $nic";
- )
- ;;
-
- "off"|"")
- /sbin/devprop host-ip subnet-mask router-ip | (
- read ip;
- read mask;
- read router;
- [ -n "$ip" ] && [ -n "$mask" ] && \
- /sbin/ifconfig $nic plumb 2>/dev/null
- [ -n "`/sbin/ifconfig $nic 2>/dev/null`" ] && (
- # The interface is successfully
- # plumbed, so modify "inet_list" to
- # force the exit code checks to work.
- inet_list=$nic;
- /sbin/ifconfig $nic inet $ip \
- netmask $mask broadcast + up 2>/dev/null;
- [ -n "$router" ] && route add \
- default $router 2>/dev/null;
- )
- )
- ;;
- esac
+# Enable symmetric routing: when there are multiple nics configured, always
+# take into account the interface a packet is being sent over when
+# selecting a route. This prevents packets being sent with another nic's
+# source IP.
+/usr/sbin/ndd -set /dev/ip ip_strict_src_multihoming 1
+
+# If the admin nic was missing config options, exit with a config error
+if [[ -n "${ADMIN_NIC_MISCONFIGURED}" ]]; then
+ exit ${SMF_EXIT_ERR_CONFIG}
fi
-#
-# We tell smf this service is online if any of the following is true:
-# - no interfaces were configured for plumbing and no DHCP failures
-# - any non-loopback IPv4 interfaces are up and have a non-zero address
-# - there are any DHCP interfaces started
-# - any non-loopback IPv6 interfaces are up
-#
-# If we weren't asked to configure any interfaces, exit
-if [ -z "$inet_list" ] && [ -z "$inet6_list" ]; then
- # Config error if DHCP was attempted without plumbed interfaces
- [ -n "$i4d_fail" ] && exit $SMF_EXIT_ERR_CONFIG
- exit $SMF_EXIT_OK
+if [[ $admin_ip == 'none' ]]; then
+ #
+ # We're done, even if there are not any usable IP addresses.
+ #
+ exit $SMF_EXIT_OK
fi
# Any non-loopback IPv4 interfaces with usable addresses up?
-if [ -n "`/sbin/ifconfig -a4u`" ]; then
- /sbin/ifconfig -a4u | while read intf addr rest; do
- [ $intf = inet ] && [ $addr != 127.0.0.1 ] &&
- [ $addr != 0.0.0.0 ] && exit $SMF_EXIT_OK
- done && exit $SMF_EXIT_OK
+if [[ -n "`/sbin/ifconfig -a4u`" ]]; then
+ /sbin/ifconfig -a4u | while read intf addr rest; do
+ [[ ${intf} == "inet" ]] && [[ ${addr} != "127.0.0.1" ]] &&
+ [[ ${addr} != "0.0.0.0" ]] && exit ${SMF_EXIT_OK}
+ done && exit ${SMF_EXIT_OK}
fi
# Any DHCP interfaces started?
-[ -n "`/sbin/ifconfig -a4 dhcp status 2>/dev/null`" ] && exit $SMF_EXIT_OK
+[[ -n "`/sbin/ifconfig -a4 dhcp status 2>/dev/null`" ]] && exit ${SMF_EXIT_OK}
# Any non-loopback IPv6 interfaces up?
-if [ -n "`/sbin/ifconfig -au6`" ]; then
- /sbin/ifconfig -au6 | while read intf addr rest; do
- [ $intf = inet6 ] && [ $addr != ::1/128 ] && exit $SMF_EXIT_OK
- done && exit $SMF_EXIT_OK
+if [[ -n "`/sbin/ifconfig -au6`" ]]; then
+ /sbin/ifconfig -au6 | while read intf addr rest; do
+ [[ ${intf} = "inet6" ]] && [[ ${addr} != "::1/128" ]] && exit ${SMF_EXIT_OK}
+ done && exit ${SMF_EXIT_OK}
fi
# This service was supposed to configure something yet didn't. Exit
# with config error.
-exit $SMF_EXIT_ERR_CONFIG
+exit ${SMF_EXIT_ERR_CONFIG}
diff --git a/usr/src/cmd/svc/milestone/net-routing-setup b/usr/src/cmd/svc/milestone/net-routing-setup
index 5b65f90d91..3ac6a3f7aa 100644
--- a/usr/src/cmd/svc/milestone/net-routing-setup
+++ b/usr/src/cmd/svc/milestone/net-routing-setup
@@ -21,12 +21,16 @@
#
#
# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+# Copyright (c) 2012 Joyent, Inc. All rights reserved.
# Copyright (c) 2021 H. William Welliver
# 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
@@ -82,6 +86,17 @@ fi
smf_netstrategy
#
+# Read /etc/inet/static_routes.vmadm and add each link-local route.
+#
+if [ -f /etc/inet/static_routes.vmadm ]; then
+ echo "Adding vmadm persistent link-local routes:"
+ /usr/bin/egrep -v "^(#|$)" /etc/inet/static_routes.vmadm |
+ /usr/bin/grep -- "-interface " | while read line; do
+ /usr/sbin/route add $line
+ done
+fi
+
+#
# See if static routes were created by install. If so, they were created
# under /etc/svc/volatile. Copy them into their proper place.
#
@@ -185,7 +200,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.
@@ -223,5 +239,22 @@ if [ -f /etc/inet/static_routes ]; then
done
fi
+#
+# Read /etc/inet/static_routes.vmadm and add each non-link-local route.
+#
+if [ -f /etc/inet/static_routes.vmadm ]; then
+ echo "Adding vmadm persistent routes:"
+ /usr/bin/egrep -v "^(#|$)" /etc/inet/static_routes.vmadm |
+ /usr/bin/grep -v -- "-interface " | 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-early-admin.xml b/usr/src/cmd/svc/milestone/network-early-admin.xml
new file mode 100644
index 0000000000..6566f31bff
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/network-early-admin.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2019, Joyent, Inc.
+
+ 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.
+
+-->
+
+<service_bundle type='manifest' name='network-early-admin'>
+
+<service
+ name='network/early-admin'
+ type='service'
+ version='1'>
+
+ <!-- ifconfig needs loopback for IPC with dhcpagent -->
+ <dependency
+ name='network'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/loopback' />
+ <service_fmri value='svc:/network/datalink-management' />
+ <service_fmri value='svc:/network/ip-interface-management' />
+ </dependency>
+
+ <instance name='default' enabled='true'>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/net-early-admin'
+ timeout_seconds='600' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':true'
+ timeout_seconds='3' />
+
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ Triton admin network on compute nodes
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='ifconfig' section='1M'
+ manpath='/usr/share/man' />
+ <manpage title='dladm' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+
+ </instance>
+
+ <stability value='Unstable' />
+
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/svc/milestone/network-location.xml b/usr/src/cmd/svc/milestone/network-location.xml
index a53dfce90a..f669d38d4c 100644
--- a/usr/src/cmd/svc/milestone/network-location.xml
+++ b/usr/src/cmd/svc/milestone/network-location.xml
@@ -81,14 +81,6 @@
</dependency>
<dependency
- name='location_netcfg'
- grouping='require_all'
- restart_on='none'
- type='service'>
- <service_fmri value='svc:/network/netcfg:default' />
- </dependency>
-
- <dependency
name='filesystem'
grouping='require_all'
restart_on='none'
diff --git a/usr/src/cmd/svc/milestone/network-physical.xml b/usr/src/cmd/svc/milestone/network-physical.xml
index 288eced560..d0eb7db1b9 100644
--- a/usr/src/cmd/svc/milestone/network-physical.xml
+++ b/usr/src/cmd/svc/milestone/network-physical.xml
@@ -47,7 +47,13 @@
<service_fmri value='svc:/network/loopback' />
</dependency>
- <instance name='default' enabled='true'>
+ <dependency
+ name='joyent'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/smartdc' />
+ </dependency>
<!--
physical:default and physical:nwam are mutually exclusive.
@@ -55,13 +61,25 @@
does not work.
-->
<dependency
- name='physical_nwam'
- grouping='exclude_all'
+ name='mdata-fetch'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/smartdc/mdata:fetch' />
+ </dependency>
+
+ <!-- Prevent this and network/early-admin from trying to configure the
+ admin interface at the same time -->
+ <dependency
+ name='early-admin'
+ grouping='optional_all'
restart_on='none'
type='service'>
- <service_fmri value='svc:/network/physical:nwam' />
+ <service_fmri value='svc:/network/early-admin:default' />
</dependency>
+ <instance name='default' enabled='true'>
+
<exec_method
type='method'
name='start'
@@ -92,82 +110,6 @@
</instance>
- <instance name='nwam' enabled='false'>
-
- <exec_method
- type='method'
- name='start'
- exec='/lib/svc/method/net-nwam start'
- timeout_seconds='120' >
- <method_context>
- <method_credential user='root' group='root'
- supp_groups='netadm' privileges='zone' />
- </method_context>
- </exec_method>
-
- <exec_method
- type='method'
- name='stop'
- exec='/lib/svc/method/net-nwam stop'
- timeout_seconds='60' >
- <method_context>
- <method_credential user='root' group='root'
- supp_groups='netadm' privileges='zone' />
- </method_context>
- </exec_method>
-
- <exec_method
- type='method'
- name='refresh'
- exec='/lib/svc/method/net-nwam refresh'
- timeout_seconds='60' >
- <method_context>
- <method_credential user='root' group='root'
- supp_groups='netadm' privileges='zone' />
- </method_context>
- </exec_method>
-
- <property_group name='general' type='framework'>
- <!-- to start/stop NWAM services -->
- <propval name='action_authorization' type='astring'
- value='solaris.smf.manage.nwam' />
- <propval name='value_authorization' type='astring'
- value='solaris.smf.manage.nwam' />
- </property_group>
-
- <property_group name='nwamd' type='application'>
- <stability value='Unstable' />
- <propval name='debug' type='boolean' value='false' />
- <propval name='autoconf' type='boolean' value='false' />
- <propval name='ncu_wait_time' type='count' value='60' />
- <propval name='condition_check_interval' type='count'
- value='120' />
- <propval name='scan_interval' type='count' value='120' />
- <propval name='scan_level' type='astring' value='weak' />
- <propval name='strict_bssid' type='boolean' value='false' />
- <propval name='active_ncp' type='astring' value='Automatic' />
- <propval name='value_authorization' type='astring'
- value='solaris.smf.value.nwam' />
- </property_group>
-
- <template>
- <common_name>
- <loctext xml:lang='C'>
- physical network interface autoconfiguration
- </loctext>
- </common_name>
- <documentation>
- <manpage title='nwamd' section='8'
- manpath='/usr/share/man' />
- <doc_link
- name='Network Auto-Magic OpenSolaris Project Page'
- uri='http://hub.opensolaris.org/bin/view/Project+nwam/'
- />
- </documentation>
- </template>
-
- </instance>
-
<stability value='Unstable' />
</service>
diff --git a/usr/src/cmd/svc/milestone/network-routing-setup.xml b/usr/src/cmd/svc/milestone/network-routing-setup.xml
index bc7560fa98..c84a88577e 100644
--- a/usr/src/cmd/svc/milestone/network-routing-setup.xml
+++ b/usr/src/cmd/svc/milestone/network-routing-setup.xml
@@ -38,11 +38,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 ffa150af57..88b846a2ce 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/milestone/smartdc-config b/usr/src/cmd/svc/milestone/smartdc-config
new file mode 100755
index 0000000000..510c93add6
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/smartdc-config
@@ -0,0 +1,211 @@
+#!/bin/bash
+#
+# 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 2019 Joyent, Inc.
+#
+
+#
+# Despite its "smartdc/config" name, this service is used both for SmartOS and
+# Triton. It has two jobs:
+#
+# During an initial setup, this runs through the initial (possibly interactive)
+# configuration.
+#
+# During normal operation, it does some miscellaneous configuration based on
+# /usbkey/config (which, under Triton, will have already been updated from the
+# USB key by svc:/system/filesystem/smartdc:default).
+#
+
+set -o errexit
+set -o xtrace
+
+. /lib/svc/share/smf_include.sh
+. /lib/sdc/config.sh
+
+export PATH="/usr/sbin:/sbin:/usr/bin"
+
+set_root_password() {
+ enc_password=$1
+
+ sed -e "s|^root:[^\:]*:|root:${enc_password}:|" /etc/shadow > /etc/shadow.new \
+ && chmod 400 /etc/shadow.new \
+ && mv /etc/shadow.new /etc/shadow
+}
+
+case "$1" in
+'start')
+
+ # If we're a headnode, see if we have to do interactive configuration.
+ if /bin/bootparams | grep "^headnode=true" > /dev/null 2>&1; then
+ USB_PATH=/mnt/`svcprop -p "joyentfs/usb_mountpoint" svc:/system/filesystem/smartdc:default`
+
+ # Check for config and run interactive if it doesn't exist.
+ if [[ ! -f ${USB_PATH}/config ]]; then
+ if /bin/bootparams | grep "^noimport=true" >/dev/null 2>&1; then
+ # Skipping interactive config, bypass rest of script.
+ exit $SMF_EXIT_OK
+ fi
+
+ /smartdc/lib/sdc-on-tty -d /dev/console \
+ ${USB_PATH}/scripts/prompt-config.sh "${USB_PATH}"
+
+ # If user quit from interactive configuration then we're done.
+ [[ ! -f ${USB_PATH}/config ]] && exit $SMF_EXIT_OK
+ fi
+ elif /bin/bootparams | grep "^smartos=true" > /dev/null 2>&1; then
+ USB_PATH=/`svcprop -p "joyentfs/usb_copy_path" svc:/system/filesystem/smartdc:default`
+
+ # Check for config and run interactive if it doesn't exist.
+ if [[ ! -f ${USB_PATH}/config ]]; then
+ if /bin/bootparams | grep "^noimport=true" >/dev/null 2>&1; then
+ # Skipping interactive config, bypass rest of script.
+ exit $SMF_EXIT_OK
+ fi
+
+ /smartdc/lib/sdc-on-tty -d /dev/console \
+ /smartdc/lib/smartos_prompt_config.sh "${USB_PATH}"
+
+ # If user quit from interactive configuration then we're done.
+ [[ ! -f ${USB_PATH}/config ]] && exit $SMF_EXIT_OK
+ fi
+ fi
+
+ # This puts config vars in CONFIG_
+ load_sdc_config
+ load_sdc_sysinfo
+
+ # Write the info about this datacenter to /.dcinfo so we can use in the GZ
+ echo "SDC_DATACENTER_NAME='${CONFIG_datacenter_name}'" > /.dcinfo
+
+ if [[ -n "${SYSINFO_Bootparam_smartos}" && -f /usbkey/shadow ]]; then
+ echo "setting root password from /usbkey/shadow"
+ # Boot parameter takes precidence over config
+ elif [[ -n "${SYSINFO_Bootparam_root_shadow}" ]]; then
+ set_root_password "${SYSINFO_Bootparam_root_shadow}"
+ echo "Set root password boot parameters."
+ elif [[ -n "${CONFIG_root_shadow}" ]]; then
+ set_root_password "${CONFIG_root_shadow}"
+ echo "Set root password from config."
+ else
+ echo "No root shadow entry in the config, cannot set."
+ fi
+
+ # Set authorized_keys for root
+ if [[ -n "${CONFIG_root_authorized_keys_file}" ]] \
+ && [[ -n "${CONFIG_config_inc_dir}" ]] \
+ && [[ -d "${CONFIG_config_inc_dir}" ]] \
+ && [[ -f "${CONFIG_config_inc_dir}/${CONFIG_root_authorized_keys_file}" ]]; then
+
+ mkdir -p /root/.ssh
+ cp "${CONFIG_config_inc_dir}/${CONFIG_root_authorized_keys_file}" /root/.ssh/authorized_keys
+ chmod 0600 /root/.ssh/authorized_keys
+ chmod 0700 /root/.ssh
+ fi
+
+ if [[ -n "${CONFIG_ntp_conf_file}" ]] \
+ && [[ -n "${CONFIG_config_inc_dir}" ]] \
+ && [[ -d "${CONFIG_config_inc_dir}" ]] \
+ && [[ -f "${CONFIG_config_inc_dir}/${CONFIG_ntp_conf_file}" ]]; then
+ #
+ # We were given a valid NTP configuration file, so use it without
+ # modification:
+ #
+ cp "${CONFIG_config_inc_dir}/${CONFIG_ntp_conf_file}" \
+ /etc/inet/ntp.conf
+ echo "Copied NTP configuration."
+ else
+ #
+ # If we have an admin network defined, allow time service from this
+ # network:
+ #
+ ntp_aflag=
+ if [[ -n ${CONFIG_admin_network} && -n ${CONFIG_admin_netmask} ]]; then
+ if [[ "${CONFIG_admin_network}" != "..." ]]; then
+ ntp_aflag="-a ${CONFIG_admin_network}/${CONFIG_admin_netmask}"
+ fi
+ fi
+
+ #
+ # If we were given a list of servers, use it:
+ #
+ ntp_hosts="${CONFIG_ntp_hosts}"
+ if [[ -z "${ntp_hosts}" ]]; then
+ #
+ # Otherwise, use the default SmartOS vendor pool from the NTP
+ # Pool Project:
+ #
+ ntp_hosts='0.smartos.pool.ntp.org'
+ fi
+
+ #
+ # Generate NTP configuration:
+ #
+ if ! /smartdc/lib/ntp_config -f /etc/inet/ntp.conf \
+ -s "${ntp_hosts}" ${ntp_aflag}; then
+ echo "FATAL: could not configure NTP" >&2
+ exit ${SMF_EXIT_ERR_CONFIG}
+ fi
+ echo "Generated NTP configuration."
+ fi
+
+ # set the keymap. For dvorak for instance
+ if [[ -n ${CONFIG_default_keymap} ]]; then
+ /usr/bin/loadkeys ${CONFIG_default_keymap}
+ fi
+
+ #
+ # In SmartOS, disabling SMT via the boot option is a pain, so we support a
+ # config option in /usbkey/config, the official mechanism for permanent
+ # configuration. We'd like to do this earlier but we have to wait for
+ # /usbkey to be mounted first. This does imply we've potentially handed out
+ # "too many" interrupts for the set of CPUs remaining online.
+ #
+ if [[ -n $(/bin/bootparams | grep '^smartos=true') ]]; then
+ if [[ "$CONFIG_smt_enabled" = "false" ]]; then
+ psradm -aS || exit $SMF_EXIT_ERR_FATAL
+ fi
+ fi
+
+ # Enable virtual terminals to support interactive installation
+ vtdaemon="svc:/system/vtdaemon"
+ svccfg -s ${vtdaemon} setprop options/hotkeys=true
+ svcadm refresh ${vtdaemon}
+ svcadm enable ${vtdaemon}
+ svcadm enable svc:/system/console-login:vt2
+ svcadm enable svc:/system/console-login:vt3
+ svcadm enable svc:/system/console-login:vt4
+ svcadm enable svc:/system/console-login:vt5
+ svcadm enable svc:/system/console-login:vt6
+
+ # force update of sysinfo (and dump to stdout so we have in the log)
+ sysinfo -f
+
+ ;;
+
+'stop')
+ ;;
+
+*)
+ echo "Usage: $0 { start | stop }"
+ exit $SMF_EXIT_ERR_FATAL
+ ;;
+esac
+exit $SMF_EXIT_OK
diff --git a/usr/src/cmd/svc/milestone/smartdc-config.xml b/usr/src/cmd/svc/milestone/smartdc-config.xml
new file mode 100644
index 0000000000..bf84d1991b
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/smartdc-config.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2011 Joyent, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='smartdc:config'>
+
+<!--
+ This service applies inital configuration for the GZ in smartdc.
+-->
+<service
+ name='system/smartdc/config'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <single_instance />
+
+ <!--
+ dependency/dependent info
+ We need to wait for fs-joyent to be done so we can access the USB key
+ We need to run before sysconfig so interactive configuration can talk
+ on the console
+ (sysconfig)
+ We don't want anything else running which spews on console
+ (system/identy)
+ We don't want manifest import scribbling on the console while running
+ (manifest-import)
+ network/physical can't run without a config file
+ (network/physical)
+ -->
+ <dependency
+ name='filesystem-joyent'
+ type='service'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/filesystem/smartdc' />
+ </dependency>
+
+ <!--
+ We don't want ssh starting if we haven't updated the passwords/keys
+ -->
+ <dependent
+ name='ssh_multi-user-server'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/network/ssh' />
+ </dependent>
+
+ <!-- Make ntp wait so we can setup the config. -->
+ <dependent
+ name='ntp'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/network/ntp' />
+ </dependent>
+
+ <dependent
+ name='headnode_config'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/milestone/sysconfig' />
+ </dependent>
+
+ <dependent
+ name='identity'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/identity:node' />
+ </dependent>
+
+ <dependent
+ name='headnode_no_output'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/manifest-import' />
+ </dependent>
+
+ <dependent
+ name='network-physical'
+ grouping='optional_all'
+ restart_on='none'>
+ <service_fmri value='svc:/network/physical:default' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/smartdc-config %m'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec='/lib/svc/method/smartdc-config %m'
+ timeout_seconds='60'>
+ </exec_method>
+
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ SmartDC live-image config management service
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='smartdc' section='5' manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/svc/milestone/smartdc-init b/usr/src/cmd/svc/milestone/smartdc-init
new file mode 100755
index 0000000000..1b2a550095
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/smartdc-init
@@ -0,0 +1,246 @@
+#!/bin/bash
+#
+# 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) 2018, Joyent, Inc.
+#
+
+export PS4='[\D{%FT%TZ}] ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
+set -o xtrace
+
+. /lib/svc/share/smf_include.sh
+. /lib/sdc/config.sh
+
+# Make sure working directory is / to prevent unmounting problems.
+cd /
+PATH=/usr/sbin:/usr/bin; export PATH
+
+wait_and_clear()
+{
+ while [ true ]; do
+ # It seems like jobs -p can miscount if we don't run jobs first
+ jobs >/dev/null
+ local cnt=`jobs -p | wc -l`
+ [ $cnt -eq 0 ] && break
+ for s in `svcs -x | nawk '{
+ if ($1 ~ /^svc:/) nm=$1
+ if ($1 == "State:" && $2 == "maintenance") print nm
+ }'`
+ do
+ svcadm clear $s
+ done
+ sleep 1
+ done
+}
+
+# Sets the default firewall rules for a node (unless they're already set)
+set_default_fw_rules() {
+ local fw_default_v
+ if [[ -f /var/fw/.default_rules_setup ]]; then
+ read fw_default_v < /var/fw/.default_rules_setup
+ else
+ fw_default_v=0
+ fi
+
+ # Handle empty files from before we started versioning default rules
+ if [[ -z $fw_default_v ]]; then
+ fw_default_v=1
+ fi
+
+ if [[ $fw_default_v -lt 1 ]]; then
+ /usr/sbin/fwadm add -f - <<RULES
+{
+ "rules": [
+ {
+ "description": "allow all ICMPv4 types",
+ "rule": "FROM any TO all vms ALLOW icmp type all",
+ "enabled": true,
+ "global": true
+ }
+ ]
+}
+RULES
+ [[ $? -ne 0 ]] && return 1
+ echo 1 > /var/fw/.default_rules_setup
+ fi
+
+ if [[ $fw_default_v -lt 2 ]]; then
+ /usr/sbin/fwadm add -f - <<RULES
+{
+ "rules": [
+ {
+ "description": "allow all ICMPv6 types",
+ "rule": "FROM any TO all vms ALLOW icmp6 type all",
+ "enabled": true,
+ "global": true
+ }
+ ]
+}
+RULES
+ [[ $? -ne 0 ]] && return 1
+ echo 2 > /var/fw/.default_rules_setup
+ fi
+}
+
+configure_fwadm()
+{
+ if [[ ! -d /var/log/fw/logs ]]; then
+ mkdir -p /var/log/fw/logs
+ mv /var/log/fw/*-*.log /var/log/fw/logs
+ fi
+
+ # See also OS-2635
+ if [[ -f /var/log/fw/fw.log.0 ]]; then
+ for file in /var/log/fw/fw.log.[0-9]*; do
+ mv ${file} "/var/log/fw/fwadm_$(uname -n)_$(stat -c "%y" ${file} | cut -d'.' -f1 | tr ' ' 'T').log"
+ done
+ fi
+ if [[ -f /var/log/fw/fw.log ]]; then
+ mv /var/log/fw/fw.log /var/log/fw/fwadm.log
+ fi
+
+ if [[ ! -e /var/log/fw/fwadm.log ]]; then
+ touch /var/log/fw/fwadm.log
+ fi
+}
+
+configure_vmadm()
+{
+ # ensure /var/log/vm exists for VM.log logs
+ mkdir -p /var/log/vm/logs
+
+ # See also OS-2635
+ if [[ -f /var/log/vm/vm.log.0 ]]; then
+ for file in /var/log/vm/vm.log.[0-9]*; do
+ mv ${file} "/var/log/vm/vmadm_$(uname -n)_$(stat -c "%y" ${file} | cut -d'.' -f1 | tr ' ' 'T').log"
+ done
+ fi
+ if [[ -f /var/log/vm/vm.log ]]; then
+ mv /var/log/vm/vm.log /var/log/vm/vmadm.log
+ fi
+
+ # need to create this file so rotation works
+ if [[ ! -e /var/log/vm/vmadm.log ]]; then
+ touch /var/log/vm/vmadm.log
+ fi
+}
+
+update_root_password()
+{
+
+ enc_password=`nawk -F= '{
+ if ($1 == "root_shadow")
+ print substr($2, 2, length($2) - 2)
+ }' /opt/smartdc/config/node.config`
+
+ [[ -z "$enc_password" ]] && return 0
+
+ sed -e "s|^root:[^\:]*:|root:${enc_password}:|" /etc/shadow \
+ >/etc/shadow.new \
+ && chmod 400 /etc/shadow.new \
+ && mv /etc/shadow.new /etc/shadow
+}
+
+# Loads config file for the node. These are the config values from the headnode
+# plus authorized keys and anything else we want.
+# This function is only invoked on a compute node.
+install_config()
+{
+ # On standalone machines we don't do this update
+ [[ -n $(/usr/bin/bootparams | grep "^standalone=true") ]] && return 0
+
+ load_sdc_config
+
+ curl -k -o /tmp/node.config --silent \
+ "http://${CONFIG_assets_admin_ip}/extra/joysetup/node.config"
+
+ [[ ! -f /tmp/node.config ]] && return 0
+ grep datacenter_name /tmp/node.config >/dev/null 2>&1
+ if [ $? != 0 ]; then
+ # There is no valid config file served by the assets zone
+ rm -f /tmp/node.config
+ return 0
+ fi
+
+ # Install the file if the local copy is different
+ diff /tmp/node.config /opt/smartdc/config/node.config >/dev/null 2>&1
+ if [ $? != 0 ]; then
+ printf "Updating config file\n" >/dev/console
+ mkdir -p /opt/smartdc/config
+ mv /tmp/node.config /opt/smartdc/config
+ update_root_password
+ else
+ rm -f /tmp/node.config
+ fi
+}
+
+case "$1" in
+'start')
+
+ # Always setup socket filter no matter what happens next.
+ /sbin/soconfig -F datafilt datafilt prog '2:2:0,2:2:6,26:2:0,26:2:6'
+
+ USBMOUNT=
+
+ # If we're not importing the pools, we shouldn't try to setup as a headnode
+ # (since there'll be no zpool)
+ if /bin/bootparams | grep "^noimport=true" > /dev/null 2>&1; then
+ exit $SMF_EXIT_OK
+ fi
+
+ # If we're a headnode, we'll not have AMQP args on the cmdline, and we want
+ # to run an initial_script first anyway.
+ if /bin/bootparams | grep "^headnode=true" > /dev/null 2>&1; then
+ USBMOUNT=/mnt/`svcprop -p joyentfs/usb_mountpoint svc:/system/filesystem/smartdc:default`
+
+ # No config file (e.g. user quit during interactive configuration), so
+ # treat as if "noimport=true".
+ [[ ! -f $USBMOUNT/config ]] && exit $SMF_EXIT_OK
+
+ initial_script=${USBMOUNT}/$(grep "^initial_script=" $USBMOUNT/config.inc/generic 2>/dev/null | cut -d'=' -f2-)
+ if [ -n ${initial_script} ] && [ -e ${initial_script} ]; then
+ # Execute the script
+ ${initial_script}
+ result=$?
+ if [ ${result} -eq 2 ]; then
+ # we're rebooting, no need to start ur
+ echo "REBOOTING!" >> /dev/console
+ enable_ur="false"
+ elif [ ${result} -ne 0 ]; then
+ echo "WARNING: initial_script failed with exit code [${result}]."
+ exit $SMF_EXIT_ERR_FATAL
+ fi
+ fi
+ elif /bin/bootparams | grep "^smartos=true" > /dev/null 2>&1; then
+ set_default_fw_rules
+ else
+ install_config
+ fi
+
+ configure_fwadm
+ configure_vmadm
+
+ if /bin/bootparams | grep "^headnode=true" > /dev/null 2>&1; then
+ /usr/sbin/umount $USBMOUNT
+ fi
+
+ ;;
+
+'stop')
+ ;;
+
+*)
+ echo "Usage: $0 { start | stop }"
+ exit $SMF_EXIT_ERR_FATAL
+ ;;
+esac
+exit $SMF_EXIT_OK
diff --git a/usr/src/cmd/svc/milestone/smartdc-init.xml b/usr/src/cmd/svc/milestone/smartdc-init.xml
new file mode 100644
index 0000000000..266aac53df
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/smartdc-init.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2012 Joyent, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+-->
+
+<service_bundle type='manifest' name='Joyent:joyent'>
+
+<!--
+ This service imports the zone's zpool on bootup, before zones try to
+ start. It also sets up configuration data on that zpool, if necessary.
+-->
+<service
+ name='system/smartdc/init'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <single_instance />
+
+ <!--
+ dependency info
+ This depends on the system/zones svc which seems weird since that
+ svc runs late in boot because it depends on milestone/multi-user-server.
+ Our svc does the initial setup of the infrastructure zones on the
+ headnode so we need the system/zones svc to run first so that we can
+ install and boot the infrastructure zones. The reason this seems
+ weird is that this svc also sets up the zpool when we first boot,
+ before we reboot to install the infrastructure zones, so it might seem
+ like it should run early in boot, but it can't because of the zone
+ issue described above.
+
+ The dependency on network/physical is needed since all of the earlier
+ svcs which have a dependency on network/physical are using optional_all.
+ That makes sense for those svcs since they can come up without a
+ network, but for SmartOS, by this point we require the network to be
+ there.
+ -->
+ <dependency
+ name='zones'
+ type='service'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/zones' />
+ </dependency>
+
+ <dependency
+ name='net-physical'
+ type='service'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/network/physical' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/smartdc-init %m'
+ timeout_seconds='0'>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec='/lib/svc/method/smartdc-init %m'
+ timeout_seconds='60'>
+ </exec_method>
+
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
+
+ <!--
+ The zpool holding the zones (this must be imported).
+ -->
+ <property_group name='config' type='application'>
+ <propval name='zpool' type='astring' value='zones' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ Joyent live-image management service
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='zones' section='5' manpath='/usr/share/man' />
+ <manpage
+ title='zpool'
+ section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/svc/milestone/smartdc-ur b/usr/src/cmd/svc/milestone/smartdc-ur
new file mode 100755
index 0000000000..2c32482368
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/smartdc-ur
@@ -0,0 +1,79 @@
+#!/bin/bash
+#
+# 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 2010-2011 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+
+set -o xtrace
+
+. /lib/svc/share/smf_include.sh
+. /lib/sdc/config.sh
+
+PATH=/usr/sbin:/usr/bin; export PATH
+
+if [[ -n "$(/bin/bootparams | grep "^standalone=true")" ||
+ -n "$(/bin/bootparams | grep "^smartos=true")" ]]; then
+ # Standalone and SmartOS systems do not need ur
+ svcadm disable "${SMF_FMRI}"
+ exit $SMF_EXIT_OK
+fi
+
+case "$1" in
+'start')
+ #
+ # Grab AMQP parameters from the kernel command line or headnode config
+ #
+
+ load_sdc_config
+ load_sdc_sysinfo
+
+ if [[ -n ${SYSINFO_Bootparam_rabbitmq} ]]; then
+ rabbit=${SYSINFO_Bootparam_rabbitmq}
+ fi
+ if [[ -z ${rabbit} ]] && [[ -n ${CONFIG_rabbitmq} ]]; then
+ rabbit=${CONFIG_rabbitmq}
+ fi
+
+ if [[ -z $rabbit ]]; then
+ echo "unable to find AMQP parameters!"
+ exit $SMF_EXIT_ERR_FATAL
+ fi
+
+ export AMQP_LOGIN=$(echo $rabbit | cut -d: -f1)
+ export AMQP_PASSWORD=$(echo $rabbit | cut -d: -f2)
+ export AMQP_HOST=$(echo $rabbit | cut -d: -f3)
+ export AMQP_PORT=$(echo $rabbit | cut -d: -f4)
+ export NODE_PATH=/usr/node/node_modules
+
+ /usr/bin/ctrun -l child -o noorphan /smartdc/ur-agent/ur-agent 2>&1 &
+
+ ;;
+
+'stop')
+ ;;
+
+*)
+ echo "Usage: $0 { start | stop }"
+ exit $SMF_EXIT_ERR_FATAL
+ ;;
+esac
+exit $SMF_EXIT_OK
diff --git a/usr/src/cmd/svc/milestone/smartdc-ur.xml b/usr/src/cmd/svc/milestone/smartdc-ur.xml
new file mode 100644
index 0000000000..7ee78dcb02
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/smartdc-ur.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+
+<!--
+ Copyright (c) 2014, Joyent, Inc.
+-->
+
+<service_bundle type="manifest" name="ur">
+ <service name="smartdc/agent/ur" type="service" version="0.0.1">
+
+ <create_default_instance enabled="true"/>
+ <single_instance/>
+
+ <dependency name="smartdc-init" grouping="require_all" restart_on="error" type="service">
+ <service_fmri value="svc:/milestone/single-user"/>
+ </dependency>
+
+ <exec_method
+ type="method"
+ name="start"
+ exec="/lib/svc/method/smartdc-ur %m"
+ timeout_seconds="60">
+ <method_context>
+ <method_credential user="root" group="staff"/>
+ </method_context>
+ </exec_method>
+
+ <exec_method type="method" name="restart" exec=":kill" timeout_seconds="60">
+ <method_context>
+ <method_credential user="root" group="staff"/>
+ </method_context>
+ </exec_method>
+
+ <exec_method type="method" name="stop" exec=":kill" timeout_seconds="60">
+ <method_context>
+ <method_credential user="root" group="staff"/>
+ </method_context>
+ </exec_method>
+
+ <property_group name="startd" type="framework">
+ <propval name="ignore_error" type="astring" value="core,signal"/>
+ </property_group>
+
+ <property_group name="application" type="application">
+ </property_group>
+
+ <stability value="Evolving"/>
+
+ <template>
+ <common_name>
+ <loctext xml:lang="C">Ur Agent (node)</loctext>
+ </common_name>
+ </template>
+
+ </service>
+
+</service_bundle>
diff --git a/usr/src/cmd/svc/milestone/sysidtool-net b/usr/src/cmd/svc/milestone/sysidtool-net
new file mode 100755
index 0000000000..538d17bba1
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/sysidtool-net
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+# This exists solely to support the service on older SmartOS zone images.
+
+exit 0
diff --git a/usr/src/cmd/svc/milestone/sysidtool-system b/usr/src/cmd/svc/milestone/sysidtool-system
new file mode 100755
index 0000000000..538d17bba1
--- /dev/null
+++ b/usr/src/cmd/svc/milestone/sysidtool-system
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+# This exists solely to support the service on older SmartOS zone images.
+
+exit 0
diff --git a/usr/src/cmd/svc/profile/Makefile b/usr/src/cmd/svc/profile/Makefile
index 2b00542413..8c44470471 100644
--- a/usr/src/cmd/svc/profile/Makefile
+++ b/usr/src/cmd/svc/profile/Makefile
@@ -23,6 +23,7 @@
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright 2019 Peter Tribble.
+# Copyright 2019 Joyent, Inc.
#
include ../../Makefile.cmd
@@ -32,6 +33,7 @@ FILEMODE = 0444
ROOTPROFILE = $(ROOT)/etc/svc/profile
PROFILESRCS = \
+ generic.xml \
generic_open.xml \
generic_limited_net.xml \
inetd_generic.xml \
@@ -60,7 +62,7 @@ TEST = /usr/bin/test
LISTSVCS = listsvcs.pl
install: all $(PROFILES)
- $(RM) $(ROOTPROFILE)/platform.xml
+ $(CP) platform_none.xml $(ROOTPROFILE)/platform.xml
# SUNW,Sun-Fire-V890
$(RM) $(ROOTPROFILE)/platform_SUNW,Sun-Fire-V890.xml
$(LN) $(ROOTPROFILE)/platform_SUNW,Sun-Fire-880.xml \
@@ -87,7 +89,7 @@ $(CHECK_OPEN) $(CHECK_LMTD): \
@$(COMM) -23 $@.enabled $@.all | $(TEE) $@.notcovered
@$(TEST) ! -s $@.notcovered && $(TOUCH) $@
-lint _msg:
+_msg:
clobber clean:
$(RM) $(CHECK_OPEN)* $(CHECK_LMTD)*
diff --git a/usr/src/cmd/svc/profile/generic.xml b/usr/src/cmd/svc/profile/generic.xml
new file mode 100644
index 0000000000..61232545dd
--- /dev/null
+++ b/usr/src/cmd/svc/profile/generic.xml
@@ -0,0 +1,397 @@
+<?xml version='1.0'?>
+<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
+<!--
+ 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 2010 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ Copyright 2019 Joyent, Inc.
+
+ The purpose of the limited_net profile is to provide a set of
+ active services that allow one to connect to the machine via ssh
+ (requires sshd). The services which are deactivated here are those
+ that are at odds with this goal. Those which are activated are
+ explicit requirements for the goal's satisfaction.
+
+ NOTE: Service profiles delivered by this package are not editable,
+ and their contents will be overwritten by package or patch
+ operations, including operating system upgrade. Make customizations
+ in a distinct file. The paths, /etc/svc/profile/site.xml and
+ /var/svc/profile/site.xml, are distinguished locations for site-specific
+ service profile, treated otherwise equivalently to this file.
+-->
+<service_bundle type='profile' name='generic_limited_net'
+ xmlns:xi='http://www.w3.org/2003/XInclude' >
+
+ <!--
+ svc.startd(1M) services
+ -->
+ <service name='system/cryptosvc' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/coreadm' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/metainit' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='system/cron' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/dbus' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/extended-accounting' version='1' type='service'>
+ <instance name='flow' enabled='false'/>
+ <instance name='process' enabled='false'/>
+ <instance name='task' enabled='false'/>
+ <instance name='net' enabled='false'/>
+ </service>
+ <service name='system/identity' version='1' type='service'>
+ <instance name='domain' enabled='true'/>
+ </service>
+ <service name='system/intrd' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/keymap' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/picl' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/sac' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='system/scheduler' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/system-log' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/utmp' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/zones' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/rcap' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/hotplug' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='network/rpc/bind' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/name-service-cache' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='network/netmask' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/nfs/status' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/nfs/nlockmgr' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/nfs/client' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/nfs/server' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/nfs/rquota' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/nfs/cbd' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/nfs/mapid' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/smb/client' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <service name='network/ssh' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='network/smtp' version='1' type='service'>
+ <instance name='sendmail' enabled='false'/>
+ </service>
+ <service name='network/sendmail-client' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/inetd' version='1' type='restarter'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='system/filesystem/autofs' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='system/filesystem/rmvolmgr' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='system/power' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+
+ <service name='network/dns/multicast' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+ <service name='network/dhcp-server' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='network/ntp' version='1' type='service'>
+ <instance name='default' enabled='true' />
+ </service>
+ <service name='network/rarp' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='network/slp' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='network/security/kadmin' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='network/security/krb5_prop' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='network/security/krb5kdc' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+
+ <service name='application/management/net-snmp' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='application/management/seaport' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='application/management/snmpdx' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='application/management/wbem' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='application/print/ipp-listener' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='application/print/ppd-cache-update' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='application/print/rfc1179' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='application/cups/in-lpd' version='1' type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='application/stosreg' version='1' type='service'>
+ <instance name='default' enabled='true' />
+ </service>
+
+ <!--
+ default inetd(1M) services
+ -->
+ <service name='network/finger' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/ftp' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/login' version='1' type='service'>
+ <instance name='rlogin' enabled='false'/>
+ <instance name='klogin' enabled='false'/>
+ <instance name='eklogin' enabled='false'/>
+ </service>
+ <service name='network/shell' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ <instance name='kshell' enabled='false'/>
+ </service>
+ <service name='network/telnet' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <!--
+ non-default inetd(1M) services
+ -->
+ <service name='network/uucp' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/chargen' version='1' type='service'>
+ <instance name='stream' enabled='false'/>
+ <instance name='dgram' enabled='false'/>
+ </service>
+ <service name='network/daytime' version='1' type='service'>
+ <instance name='stream' enabled='false'/>
+ <instance name='dgram' enabled='false'/>
+ </service>
+ <service name='network/discard' version='1' type='service'>
+ <instance name='stream' enabled='false'/>
+ <instance name='dgram' enabled='false'/>
+ </service>
+ <service name='network/echo' version='1' type='service'>
+ <instance name='stream' enabled='false'/>
+ <instance name='dgram' enabled='false'/>
+ </service>
+ <service name='network/time' version='1' type='service'>
+ <instance name='stream' enabled='false'/>
+ <instance name='dgram' enabled='false'/>
+ </service>
+ <service name='network/comsat' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/rexec' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/talk' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/stdiscover' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/stlisten' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <!--
+ default inetd(1M) RPC services enabled
+ -->
+ <service name='network/rpc/gss' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/rpc/mdcomm' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/rpc/smserver' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/security/ktkt_warn' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <!--
+ default inetd(1M) RPC services disabled
+ -->
+ <service name='network/rpc/rstat' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/rpc/rusers' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/rpc/meta' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/rpc/metamed' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/rpc/metamh' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <!--
+ non-default inetd(1M) RPC services disabled
+ -->
+ <service name='network/rpc/rex' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/rpc/spray' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+ <service name='network/rpc/wall' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <!--
+ Disable Avahi mDNS bridge service
+ -->
+ <service name='system/avahi-bridge-dsd' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <!--
+ Enable CDE/ToolTalk/GDM services.
+ -->
+ <service name='network/rpc/cde-ttdbserver' version='1' type='service'>
+ <instance name='tcp' enabled='false' />
+ </service>
+ <service name='application/graphical-login/gdm' version='1'
+ type='service'>
+ <instance name='default' enabled='false' />
+ </service>
+ <service name='network/rpc/cde-calendar-manager' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <!--
+ Disable X11 services.
+ -->
+ <service name='application/x11/xfs' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <!--
+ pkg.depotd service
+ -->
+ <service name='system/pkgserv' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <!--
+ Enable VNC config service for xVM
+ -->
+ <service name='system/xvm/vnc-config' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <service name='system/xvm/ipagent' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <service name='application/print/service-selector' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <service name='system/dumpadm' version='1' type='service'>
+ <instance name='default' enabled='true'/>
+ </service>
+
+ <service name='system/metasync' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <service name='application/font/fc-cache' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <service name='system/consolekit' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <service name='application/opengl/ogl-select' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+ <service name='system/hal' version='1' type='service'>
+ <instance name='default' enabled='false'/>
+ </service>
+
+
+</service_bundle>
diff --git a/usr/src/cmd/svc/shell/mfsthistory b/usr/src/cmd/svc/shell/mfsthistory
index 7a719d1c49..2a1bac3b74 100644
--- a/usr/src/cmd/svc/shell/mfsthistory
+++ b/usr/src/cmd/svc/shell/mfsthistory
@@ -311,7 +311,6 @@ svc:/network/routing/legacy-routing:ipv4 var/svc/manifest/network/routing/legacy
svc:/network/routing/legacy-routing var/svc/manifest/network/routing/legacy-routing.xml
svc:/network/shares/group:default var/svc/manifest/network/shares/group.xml
svc:/network/shares/group var/svc/manifest/network/shares/group.xml
-svc:/network/ssl/proxy var/svc/manifest/network/ssl/kssl-proxy.xml
svc:/system/auditd:default var/svc/manifest/system/auditd.xml
svc:/system/auditd var/svc/manifest/system/auditd.xml
svc:/system/boot-archive-update:default var/svc/manifest/system/boot-archive-update.xml
diff --git a/usr/src/cmd/svc/shell/smf_include.sh b/usr/src/cmd/svc/shell/smf_include.sh
index e7285c42e7..7aef5c73a9 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.
# Copyright 2015 Nexenta Systems, Inc. All rights reserved.
# Copyright 2012 Joyent, Inc. All rights reserved.
# Copyright 2021 Oxide Computer Company
diff --git a/usr/src/cmd/svc/startd/graph.c b/usr/src/cmd/svc/startd/graph.c
index f8d5f2b0ce..685c1c262c 100644
--- a/usr/src/cmd/svc/startd/graph.c
+++ b/usr/src/cmd/svc/startd/graph.c
@@ -145,6 +145,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>
@@ -4846,6 +4848,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) {
@@ -6785,6 +6801,9 @@ 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;
+
+ (void) pthread_setname_np(pthread_self(), "repository_event");
(void) pthread_setname_np(pthread_self(), "repository_event");
@@ -6809,6 +6828,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 1c5549179c..2629b74210 100644
--- a/usr/src/cmd/svc/startd/method.c
+++ b/usr/src/cmd/svc/startd/method.c
@@ -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);
}
/*
diff --git a/usr/src/cmd/svc/startd/startd.h b/usr/src/cmd/svc/startd/startd.h
index a952144c93..cf6e7e5cc5 100644
--- a/usr/src/cmd/svc/startd/startd.h
+++ b/usr/src/cmd/svc/startd/startd.h
@@ -398,7 +398,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 */
@@ -420,6 +420,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 07d22a2226..fae2142dac 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/Makefile b/usr/src/cmd/svc/svccfg/Makefile
index d5721fd12e..d000d419cd 100644
--- a/usr/src/cmd/svc/svccfg/Makefile
+++ b/usr/src/cmd/svc/svccfg/Makefile
@@ -93,7 +93,8 @@ $(NOT_NATIVE)SVCCFG_EXTRA_LIBS += -ltecla
#
# If no adjunct, then we'll already find /usr/lib via LDLIBS below.
#
-NATIVE_LIBS += libxml2.so libl.so libumem.so libmd.so libnvpair.so libc.so
+NATIVE_LIBS += libxml2.so libl.so libumem.so libmd.so libnvpair.so libc.so \
+ libscf.so libuutil.so
LIBSCF = $(SRC)/lib/libscf
LIBTECLA = $(SRC)/lib/libtecla # just for the header
@@ -101,9 +102,10 @@ LIBUUTIL = $(SRC)/lib/libuutil
LDLIBS += $(SVCCFG_EXTRA_LIBS)
-$(NATIVE_BUILD)CC = $(NATIVECC)
-$(NATIVE_BUILD)LD = $(NATIVELD)
-$(NATIVE_BUILD)CFLAGS = $(NATIVE_CFLAGS)
+$(NATIVE_BUILD)CC = $(NATIVECC64)
+$(NATIVE_BUILD)LD = $(NATIVELD64)
+# Add the -g debug flag in spite of being native so ctfconvert won't whine.
+$(NATIVE_BUILD)CFLAGS = $(NATIVE_CFLAGS64) -g
$(NATIVE_BUILD)CPPFLAGS = \
-DNATIVE_BUILD \
$(MYCPPFLAGS) \
diff --git a/usr/src/cmd/svc/svccfg/svccfg_libscf.c b/usr/src/cmd/svc/svccfg/svccfg_libscf.c
index a1a3a44ad2..491040eca7 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_libscf.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_libscf.c
@@ -12604,7 +12604,7 @@ lscf_service_delete(scf_service_t *svc, int force)
static int
delete_callback(void *data, scf_walkinfo_t *wip)
{
- int force = (int)data;
+ int force = (int)(intptr_t)data;
if (wip->inst != NULL)
(void) lscf_instance_delete(wip->inst, force);
@@ -12684,7 +12684,7 @@ lscf_delete(const char *fmri, int force)
* Match FMRI to entity.
*/
if ((ret = scf_walk_fmri(g_hndl, 1, (char **)&fmri, SCF_WALK_SERVICE,
- delete_callback, (void *)force, NULL, semerr)) != 0) {
+ delete_callback, (void *)(intptr_t)force, NULL, semerr)) != 0) {
semerr(gettext("Failed to walk instances: %s\n"),
scf_strerror(ret));
}
diff --git a/usr/src/cmd/svc/svcs/Makefile b/usr/src/cmd/svc/svcs/Makefile
index aac8f82e0a..10cbbe2127 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 -lproc
+LDLIBS += -lcontract -lscf -luutil -lumem -lnvpair -lzonecfg -lsasl -lproc
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 331a5375fd..8a8dfa043b 100644
--- a/usr/src/cmd/svc/svcs/explain.c
+++ b/usr/src/cmd/svc/svcs/explain.c
@@ -196,6 +196,7 @@ static char *emsg_invalid_dep;
extern scf_handle_t *h;
extern char *g_zonename;
+extern char *g_zonealias;
/* ARGSUSED */
static int
@@ -2017,6 +2018,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 87735a69c7..9beb62047c 100644
--- a/usr/src/cmd/svc/svcs/svcs.c
+++ b/usr/src/cmd/svc/svcs/svcs.c
@@ -60,6 +60,7 @@
#include <sys/ctfs.h>
#include <sys/stat.h>
+#include <sasl/saslutil.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
@@ -138,6 +139,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.
@@ -244,7 +248,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);
@@ -3684,6 +3706,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 "
@@ -3706,18 +3746,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. "
@@ -3776,7 +3844,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/syslogd/syslog.conf b/usr/src/cmd/syslogd/syslog.conf
index 1ed7a40d39..a894b3ed90 100644
--- a/usr/src/cmd/syslogd/syslog.conf
+++ b/usr/src/cmd/syslogd/syslog.conf
@@ -54,3 +54,7 @@ user.err /var/adm/messages
user.alert `root, operator'
user.emerg *
)
+
+auth.info /var/log/auth.log
+mail.info /var/log/postfix.log
+#local0.info /var/log/courier.log
diff --git a/usr/src/cmd/syslogd/system-log b/usr/src/cmd/syslogd/system-log
index a8264d4b78..d8b3eaf9d7 100644
--- a/usr/src/cmd/syslogd/system-log
+++ b/usr/src/cmd/syslogd/system-log
@@ -21,9 +21,12 @@
#
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2016, Joyent, Inc. All rights reserved.
#
OLD_CONF=/etc/default/syslogd
+DFLT_SUM="7c87d765805cc45a0dad9fb5c86cc4f2"
+CONF_SUM="$DFLT_SUM"
. /lib/svc/share/smf_include.sh
@@ -51,14 +54,72 @@ convert()
rm -f ${OLD_CONF}.new
}
-if [ ! -f /etc/syslog.conf ]; then
- echo "/etc/syslog.conf is missing. Exiting."
- exit $SMF_EXIT_ERR_CONFIG
+#
+# If the rsyslog.conf file doesn't exist and the syslog.conf file has not
+# been customized, generate the default rsyslog.conf.
+#
+generate_rsyslog_conf()
+{
+ if [ ! -f /etc/syslog.conf ]; then
+ return
+ fi
+
+ CONF_SUM=`nawk '{
+ if (substr($1, 1, 1) != "#" && NF != 0) print $0
+ }' /etc/syslog.conf | sum -x md5`
+
+ if [ "$CONF_SUM" != "$DFLT_SUM" ]; then
+ return
+ fi
+
+ cat <<-DONE >/etc/rsyslog.conf
+ #
+ # Sample rsyslog configuration file
+ #
+
+ $ModLoad immark
+ $ModLoad imsolaris
+ $ModLoad imtcp
+ $ModLoad imudp
+
+ *.err;kern.notice;auth.notice /dev/sysmsg
+ *.err;kern.debug;daemon.notice;mail.crit /var/adm/messages
+
+ *.alert;kern.err;daemon.err operator
+ *.alert root
+
+ *.emerg *
+
+ mail.debug /var/log/syslog
+
+ auth.info /var/log/auth.log
+ mail.info /var/log/postfix.log
+ DONE
+}
+
+if [ ! -f /etc/rsyslog.conf -a -x /usr/sbin/rsyslogd ]; then
+ generate_rsyslog_conf
fi
-if [ ! -x /usr/sbin/syslogd ]; then
- echo "Executable /usr/sbin/syslogd not found. Exiting"
- exit $SMF_EXIT_ERR_CONFIG
+if [ -f /etc/rsyslog.conf ]; then
+ if [ ! -x /usr/sbin/rsyslogd ]; then
+ echo "Executable /usr/sbin/rsyslogd not found or not executable. Exiting."
+ exit $SMF_EXIT_ERR_CONFIG
+ fi
+else
+ #
+ # Fall back to old syslogd
+ #
+ echo "/etc/rsyslog.conf is missing. Using syslogd instead."
+ if [ ! -f /etc/syslog.conf ]; then
+ echo "/etc/syslog.conf is missing. Exiting."
+ exit $SMF_EXIT_ERR_CONFIG
+ fi
+
+ if [ ! -x /usr/sbin/syslogd ]; then
+ echo "Executable /usr/sbin/syslogd not found or not executable. Exiting."
+ exit $SMF_EXIT_ERR_CONFIG
+ fi
fi
if smf_is_globalzone; then
@@ -79,6 +140,27 @@ if [ ! -f /var/adm/messages ]; then
/usr/bin/cp /dev/null /var/adm/messages
/usr/bin/chmod 0644 /var/adm/messages
fi
+
+if [ -f /etc/rsyslog.conf ]; then
+ #
+ # If we have a config file for the old syslogd, only run rsyslogd
+ # if there are no customizations in the old syslog.conf file.
+ #
+ if [ -f /etc/syslog.conf ]; then
+ CONF_SUM=`nawk '{
+ if (substr($1, 1, 1) != "#" && NF != 0) print $0
+ }' /etc/syslog.conf | sum -x md5`
+ fi
+
+ if [ "$CONF_SUM" == "$DFLT_SUM" ]; then
+ /usr/sbin/rsyslogd -c5 -n &
+ exit $SMF_EXIT_OK
+ fi
+fi
+
+#
+# Run the old syslogd
+#
remote=`awk -F= '
/^LOG_FROM_REMOTE=[yY][Ee][Ss]/ {print "true"}
/^LOG_FROM_REMOTE=[Nn][Oo]/ {print "false"}' < ${OLD_CONF}`
@@ -95,3 +177,10 @@ case ${remote} in
esac
/usr/sbin/syslogd >/dev/msglog 2>&1 &
+
+if [ -f /etc/rsyslog.conf ]; then
+ logger -p daemon.err \
+ "Not using rsyslogd because there is a custom /etc/syslog.conf file"
+fi
+
+exit $SMF_EXIT_OK
diff --git a/usr/src/cmd/tail/Makefile b/usr/src/cmd/tail/Makefile
index 49b4a042b5..c93a9d32b9 100644
--- a/usr/src/cmd/tail/Makefile
+++ b/usr/src/cmd/tail/Makefile
@@ -22,6 +22,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
@@ -44,6 +45,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/truss/codes.c b/usr/src/cmd/truss/codes.c
index 394ddff17c..a8a4ab4dfa 100644
--- a/usr/src/cmd/truss/codes.c
+++ b/usr/src/cmd/truss/codes.c
@@ -25,6 +25,7 @@
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright 2020 Joyent, Inc.
* Copyright (c) 2014, OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
* Copyright 2022 Garrett D'Amore <garrett@damore.org>
*/
@@ -1960,7 +1961,7 @@ pathconfname(int code)
#define ALL_O_FLAGS \
(O_NDELAY|O_APPEND|O_SYNC|O_DSYNC|O_NONBLOCK|O_CREAT|O_TRUNC\
|O_EXCL|O_NOCTTY|O_LARGEFILE|O_RSYNC|O_XATTR|O_NOFOLLOW|O_NOLINKS\
- |O_CLOEXEC|O_DIRECTORY|O_DIRECT|FXATTRDIROPEN)
+ |O_CLOEXEC|O_DIRECTORY|O_DIRECT|FXATTRDIROPEN|__FLXPATH)
const char *
openarg(private_t *pri, int arg)
@@ -2026,6 +2027,8 @@ openarg(private_t *pri, int arg)
(void) strlcat(str, "|O_DIRECT", sizeof (pri->code_buf));
if (arg & FXATTRDIROPEN)
(void) strlcat(str, "|FXATTRDIROPEN", sizeof (pri->code_buf));
+ if (arg & __FLXPATH)
+ (void) strlcat(str, "|__FLXPATH", sizeof (pri->code_buf));
return ((const char *)str);
}
diff --git a/usr/src/cmd/truss/print.c b/usr/src/cmd/truss/print.c
index 177239d031..0e2c9f5e5d 100644
--- a/usr/src/cmd/truss/print.c
+++ b/usr/src/cmd/truss/print.c
@@ -878,7 +878,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);
@@ -887,6 +889,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;
@@ -2103,6 +2108,7 @@ udp_optname(private_t *pri, long val)
case UDP_RCVHDR: return ("UDP_RCVHDR");
case UDP_NAT_T_ENDPOINT: return ("UDP_NAT_T_ENDPOINT");
case UDP_SRCPORT_HASH: return ("UDP_SRCPORT_HASH");
+ case UDP_SND_TO_CONNECTED: return ("UDP_SND_TO_CONNECTED");
default: (void) snprintf(pri->code_buf,
sizeof (pri->code_buf), "0x%lx",
@@ -2677,8 +2683,21 @@ prt_zga(private_t *pri, int raw, long val)
case ZONE_ATTR_INITNAME: s = "ZONE_ATTR_INITNAME"; break;
case ZONE_ATTR_BOOTARGS: s = "ZONE_ATTR_BOOTARGS"; break;
case ZONE_ATTR_BRAND: s = "ZONE_ATTR_BRAND"; break;
+ case ZONE_ATTR_SCHED_CLASS: s = "ZONE_ATTR_SCHED_CLASS"; break;
case ZONE_ATTR_FLAGS: s = "ZONE_ATTR_FLAGS"; break;
- case ZONE_ATTR_PHYS_MCAP: s = "ZONE_ATTR_PHYS_MCAP"; break;
+ case ZONE_ATTR_HOSTID: s = "ZONE_ATTR_HOSTID"; break;
+ case ZONE_ATTR_FS_ALLOWED: s = "ZONE_ATTR_FS_ALLOWED"; break;
+ case ZONE_ATTR_NETWORK: s = "ZONE_ATTR_NETWORK"; break;
+ case ZONE_ATTR_INITNORESTART: s = "ZONE_ATTR_INITNORESTART";
+ break;
+ case ZONE_ATTR_SECFLAGS: s = "ZONE_ATTR_SECFLAGS"; break;
+ case ZONE_ATTR_INITRESTART0: s = "ZONE_ATTR_INITRESTART0";
+ break;
+ case ZONE_ATTR_INITREBOOT: s = "ZONE_ATTR_INITREBOOT"; break;
+ case ZONE_ATTR_DID: s = "ZONE_ATTR_DID"; break;
+ case ZONE_ATTR_APP_SVC_CT: s = "ZONE_ATTR_APP_SVC_CT"; break;
+ case ZONE_ATTR_SCHED_FIXEDHI: s = "ZONE_ATTR_SCHED_FIXEDHI";
+ break;
}
}
diff --git a/usr/src/cmd/truss/systable.c b/usr/src/cmd/truss/systable.c
index 199c3131a1..ecc8c66b3a 100644
--- a/usr/src/cmd/truss/systable.c
+++ b/usr/src/cmd/truss/systable.c
@@ -30,6 +30,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>
@@ -1717,9 +1720,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
index 4d9e29cd26..05569be767 100644
--- a/usr/src/cmd/varpd/Makefile
+++ b/usr/src/cmd/varpd/Makefile
@@ -51,6 +51,9 @@ clean:
check: $(CHKMANIFEST)
+clobber: clean
+ $(RM) $(PROG)
+
install: $(PROG) $(ROOTLIBVARPDPROG) $(ROOTMANIFEST)
$(ROOTLIBVARPD):
diff --git a/usr/src/cmd/varpd/varpd.c b/usr/src/cmd/varpd/varpd.c
index 3eb969c371..84283cce5c 100644
--- a/usr/src/cmd/varpd/varpd.c
+++ b/usr/src/cmd/varpd/varpd.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright (c) 2021 Joyent, Inc.
+ * Copyright 2021 Joyent, Inc.
*/
/*
diff --git a/usr/src/cmd/vi/port/Makefile b/usr/src/cmd/vi/port/Makefile
index aee7a77ae1..99b292d833 100644
--- a/usr/src/cmd/vi/port/Makefile
+++ b/usr/src/cmd/vi/port/Makefile
@@ -78,6 +78,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:%=-Wl,-M%)
diff --git a/usr/src/cmd/vi/port/ex_cmdsub.c b/usr/src/cmd/vi/port/ex_cmdsub.c
index 9004ac7271..7e2cd4740d 100644
--- a/usr/src/cmd/vi/port/ex_cmdsub.c
+++ b/usr/src/cmd/vi/port/ex_cmdsub.c
@@ -1740,7 +1740,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..2b9ca6c3c1
--- /dev/null
+++ b/usr/src/cmd/vndadm/Makefile
@@ -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) 2018 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:
+
+all: $(PROG)
+
+$(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..224306ffe9
--- /dev/null
+++ b/usr/src/cmd/vndadm/test/scripts/vndtest.ksh
@@ -0,0 +1,298 @@
+#!/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 2019 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
+
+ tests=$(ls -1 $vt_root/*/*/@(ecreate|create|tst|err).*.@(ksh|exe))
+ 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 b00317f900..7e81e2143b 100644
--- a/usr/src/cmd/zfs/zfs_main.c
+++ b/usr/src/cmd/zfs/zfs_main.c
@@ -249,9 +249,9 @@ get_usage(zfs_help_t idx)
return (gettext("\tclone [-p] [-o property=value] ... "
"<snapshot> <filesystem|volume>\n"));
case HELP_CREATE:
- return (gettext("\tcreate [-p] [-o property=value] ... "
+ return (gettext("\tcreate [-Pnpv] [-o property=value] ... "
"<filesystem>\n"
- "\tcreate [-ps] [-b blocksize] [-o property=value] ... "
+ "\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "
"-V <size> <volume>\n"));
case HELP_DESTROY:
return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
@@ -259,7 +259,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[,...]> "
@@ -705,7 +705,7 @@ should_auto_mount(zfs_handle_t *zhp)
}
/*
- * 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
@@ -713,12 +713,18 @@ should_auto_mount(zfs_handle_t *zhp)
* 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;
@@ -727,8 +733,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)) {
nvlist_free(props);
@@ -797,11 +806,16 @@ zfs_do_clone(int argc, char **argv)
* step.
*/
if (should_auto_mount(clone)) {
- if ((ret = zfs_mount(clone, NULL, 0)) != 0) {
- (void) fprintf(stderr, gettext("clone "
- "successfully created, "
- "but not mounted\n"));
- } else if ((ret = zfs_share(clone)) != 0) {
+ while ((ret = zfs_mount(clone, NULL, 0)) != 0) {
+ if (!keeptrying || errno != EBUSY) {
+ (void) fprintf(stderr,
+ gettext("clone "
+ "successfully created, "
+ "but not mounted\n"));
+ break;
+ }
+ }
+ if (ret == 0 && (ret = zfs_share(clone)) != 0) {
(void) fprintf(stderr, gettext("clone "
"successfully created, "
"but not shared\n"));
@@ -825,8 +839,8 @@ usage:
}
/*
- * zfs create [-p] [-o prop=value] ... fs
- * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size
+ * zfs create [-Pnpv] [-o prop=value] ... fs
+ * zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size
*
* Create a new dataset. This command can be used to create filesystems
* and volumes. Snapshot creation is handled by 'zfs snapshot'.
@@ -838,17 +852,30 @@ usage:
* SPA_VERSION_REFRESERVATION, we set a refreservation instead.
*
* The '-p' flag creates all the non-existing ancestors of the target first.
+ *
+ * The '-n' flag is no-op (dry run) mode. This will perform a user-space sanity
+ * check of arguments and properties, but does not check for permissions,
+ * available space, etc.
+ *
+ * The '-v' flag is for verbose output.
+ *
+ * The '-P' flag is used for parseable output. It implies '-v'.
*/
static int
zfs_do_create(int argc, char **argv)
{
zfs_type_t type = ZFS_TYPE_FILESYSTEM;
zfs_handle_t *zhp = NULL;
+ zpool_handle_t *zpool_handle = NULL;
+ nvlist_t *real_props = NULL;
uint64_t volsize = 0;
int c;
boolean_t noreserve = B_FALSE;
boolean_t bflag = B_FALSE;
boolean_t parents = B_FALSE;
+ boolean_t dryrun = B_FALSE;
+ boolean_t verbose = B_FALSE;
+ boolean_t parseable = B_FALSE;
int ret = 1;
nvlist_t *props;
uint64_t intval;
@@ -857,7 +884,7 @@ zfs_do_create(int argc, char **argv)
nomem();
/* check options */
- while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) {
+ while ((c = getopt(argc, argv, ":PV:b:nso:pv")) != -1) {
switch (c) {
case 'V':
type = ZFS_TYPE_VOLUME;
@@ -873,6 +900,10 @@ zfs_do_create(int argc, char **argv)
nomem();
volsize = intval;
break;
+ case 'P':
+ verbose = B_TRUE;
+ parseable = B_TRUE;
+ break;
case 'p':
parents = B_TRUE;
break;
@@ -890,6 +921,9 @@ zfs_do_create(int argc, char **argv)
intval) != 0)
nomem();
break;
+ case 'n':
+ dryrun = B_TRUE;
+ break;
case 'o':
if (!parseprop(props, optarg))
goto error;
@@ -897,6 +931,9 @@ zfs_do_create(int argc, char **argv)
case 's':
noreserve = B_TRUE;
break;
+ case 'v':
+ verbose = B_TRUE;
+ break;
case ':':
(void) fprintf(stderr, gettext("missing size "
"argument\n"));
@@ -928,14 +965,9 @@ zfs_do_create(int argc, char **argv)
goto badusage;
}
- if (type == ZFS_TYPE_VOLUME && !noreserve) {
- zpool_handle_t *zpool_handle;
- nvlist_t *real_props = NULL;
- uint64_t spa_version;
+ if (dryrun || (type == ZFS_TYPE_VOLUME && !noreserve)) {
+ char msg[ZFS_MAX_DATASET_NAME_LEN * 2];
char *p;
- zfs_prop_t resv_prop;
- char *strval;
- char msg[1024];
if ((p = strchr(argv[0], '/')) != NULL)
*p = '\0';
@@ -944,25 +976,31 @@ zfs_do_create(int argc, char **argv)
*p = '/';
if (zpool_handle == NULL)
goto error;
- spa_version = zpool_get_prop_int(zpool_handle,
- ZPOOL_PROP_VERSION, NULL);
- if (spa_version >= SPA_VERSION_REFRESERVATION)
- resv_prop = ZFS_PROP_REFRESERVATION;
- else
- resv_prop = ZFS_PROP_RESERVATION;
(void) snprintf(msg, sizeof (msg),
+ dryrun ? gettext("cannot verify '%s'") :
gettext("cannot create '%s'"), argv[0]);
if (props && (real_props = zfs_valid_proplist(g_zfs, type,
props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) {
zpool_close(zpool_handle);
goto error;
}
+ }
+
+ if (type == ZFS_TYPE_VOLUME && !noreserve) {
+ uint64_t spa_version;
+ zfs_prop_t resv_prop;
+ char *strval;
+
+ spa_version = zpool_get_prop_int(zpool_handle,
+ ZPOOL_PROP_VERSION, NULL);
+ if (spa_version >= SPA_VERSION_REFRESERVATION)
+ resv_prop = ZFS_PROP_REFRESERVATION;
+ else
+ resv_prop = ZFS_PROP_RESERVATION;
volsize = zvol_volsize_to_reservation(zpool_handle, volsize,
real_props);
- nvlist_free(real_props);
- zpool_close(zpool_handle);
if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
&strval) != 0) {
@@ -973,6 +1011,10 @@ zfs_do_create(int argc, char **argv)
}
}
}
+ if (zpool_handle != NULL) {
+ zpool_close(zpool_handle);
+ nvlist_free(real_props);
+ }
if (parents && zfs_name_valid(argv[0], type)) {
/*
@@ -984,8 +1026,50 @@ zfs_do_create(int argc, char **argv)
ret = 0;
goto error;
}
- if (zfs_create_ancestors(g_zfs, argv[0]) != 0)
- goto error;
+ if (verbose) {
+ (void) printf(parseable ? "create_ancestors\t%s\n" :
+ dryrun ? "would create ancestors of %s\n" :
+ "create ancestors of %s\n", argv[0]);
+ }
+ if (!dryrun) {
+ if (zfs_create_ancestors(g_zfs, argv[0]) != 0) {
+ goto error;
+ }
+ }
+ }
+
+ if (verbose) {
+ nvpair_t *nvp = NULL;
+ (void) printf(parseable ? "create\t%s\n" :
+ dryrun ? "would create %s\n" : "create %s\n", argv[0]);
+ while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) {
+ uint64_t uval;
+ char *sval;
+
+ switch (nvpair_type(nvp)) {
+ case DATA_TYPE_UINT64:
+ VERIFY0(nvpair_value_uint64(nvp, &uval));
+ (void) printf(parseable ?
+ "property\t%s\t%llu\n" : "\t%s=%llu\n",
+ nvpair_name(nvp), (u_longlong_t)uval);
+ break;
+ case DATA_TYPE_STRING:
+ VERIFY0(nvpair_value_string(nvp, &sval));
+ (void) printf(parseable ?
+ "property\t%s\t%s\n" : "\t%s=%s\n",
+ nvpair_name(nvp), sval);
+ break;
+ default:
+ (void) fprintf(stderr, "property '%s' "
+ "has illegal type %d\n",
+ nvpair_name(nvp), nvpair_type(nvp));
+ abort();
+ }
+ }
+ }
+ if (dryrun) {
+ ret = 0;
+ goto error;
}
/* pass to libzfs */
@@ -1028,12 +1112,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,
@@ -1043,6 +1128,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;
@@ -1126,13 +1212,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 {
@@ -1147,13 +1238,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
@@ -1162,23 +1250,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) {
+ (void) 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
@@ -1334,7 +1465,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;
@@ -1353,6 +1484,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;
@@ -1723,8 +1857,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;
@@ -3245,6 +3382,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;
@@ -3363,6 +3501,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 0ad4013e84..5438549648 100644
--- a/usr/src/cmd/zlogin/zlogin.c
+++ b/usr/src/cmd/zlogin/zlogin.c
@@ -23,12 +23,12 @@
* Copyright 2013 DEY Storage Systems, Inc.
* Copyright (c) 2014 Gary Mills
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
*/
/*
- * 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
@@ -44,12 +44,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>
@@ -94,7 +104,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;
@@ -103,12 +114,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;
@@ -125,10 +137,14 @@ 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 MAX_RETRY 30
#define CLUSTER_BRAND_NAME "cluster"
@@ -155,7 +171,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);
}
@@ -250,57 +266,60 @@ 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, boolean_t verbose)
{
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"));
+ if (verbose)
+ zperror(gettext("could not create socket"));
return (-1);
}
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;
+ if (verbose)
+ zperror(gettext("Could not connect to zone"));
+ close(sockfd);
+ return (-1);
}
- masterfd = sockfd;
+ return (sockfd);
+}
- msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s %d\n",
- getpid(), setlocale(LC_MESSAGES, NULL), disconnect);
+
+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 %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')
@@ -310,26 +329,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
@@ -518,8 +559,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;
@@ -863,28 +928,34 @@ 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 & POLLHUP)
+ break;
+
+ 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 & POLLHUP)
+ break;
+
+ 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;
}
}
@@ -1054,7 +1125,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.
*/
/*
@@ -1100,62 +1171,148 @@ 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;
+ int argc = 0, i;
+ size_t subshell_len = 1;
+ char *subshell = NULL, *supath = NULL;
+ char **new_argv = NULL;
- if (argv != NULL) {
- size_t subshell_len = 1;
- char *subshell;
+ if (argv == NULL) {
+ if (failsafe) {
+ if ((new_argv = malloc(sizeof (char *) * 2)) == NULL)
+ return (NULL);
+ new_argv[0] = FAILSAFESHELL;
+ new_argv[1] = NULL;
+ } else {
+ new_argv = zone_login_cmd(bh, login);
+ }
+ return (new_argv);
+ }
- while (argv[argc] != NULL)
- argc++;
+ /*
+ * Attempt to locate a 'su' binary if not using the failsafe shell.
+ */
+ if (!failsafe) {
+ struct stat sb;
+ char zonepath[MAXPATHLEN];
+ char supath_check[MAXPATHLEN];
+
+ if (zone_get_zonepath(zonename, zonepath,
+ sizeof (zonepath)) != Z_OK) {
+ zerror(gettext("unable to determine zone "
+ "path"));
+ return (NULL);
+ }
- for (i = 0; i < argc; i++) {
- subshell_len += strlen(argv[i]) + 1;
+ (void) snprintf(supath_check, sizeof (supath), "%s/root/%s",
+ zonepath, SUPATH1);
+ if (stat(supath_check, &sb) == 0) {
+ supath = SUPATH1;
+ } else {
+ (void) snprintf(supath_check, sizeof (supath_check),
+ "%s/root/%s", zonepath, SUPATH2);
+ if (stat(supath_check, &sb) == 0) {
+ supath = SUPATH2;
+ }
}
- if ((subshell = calloc(1, subshell_len)) == NULL)
+ }
+
+ /*
+ * With no failsafe shell or supath to wrap the incoming command, the
+ * arguments are passed straight through.
+ */
+ if (!failsafe && supath == NULL) {
+ /*
+ * Such an outcome is not acceptable, however, if the caller
+ * expressed a desire to switch users.
+ */
+ if (strcmp(login, "root") != 0) {
+ zerror(gettext("unable to find 'su' command"));
return (NULL);
+ }
+ return (argv);
+ }
- for (i = 0; i < argc; i++) {
- (void) strcat(subshell, argv[i]);
+ /*
+ * Inventory arguments and allocate a buffer to escape them for the
+ * subshell.
+ */
+ while (argv[argc] != NULL) {
+ /*
+ * Allocate enough space for the delimiter and 2
+ * quotes which might be needed.
+ */
+ subshell_len += strlen(argv[argc]) + 3;
+ argc++;
+ }
+ if ((subshell = calloc(1, subshell_len)) == NULL) {
+ return (NULL);
+ }
+
+ /*
+ * The handling of quotes in the following block may seem unusual, but
+ * it is done this way for backward compatibility.
+ * When running a command, zlogin is documented as:
+ * zlogin zonename command args
+ * However, some code has come to depend on the following usage:
+ * zlogin zonename 'command args'
+ * This relied on the fact that the single argument would be re-parsed
+ * within the zone and excuted as a command with an argument. To remain
+ * compatible with this (incorrect) usage, if there is only a single
+ * argument, it is not quoted, even if it has embedded spaces.
+ *
+ * Here are two examples which both need to work:
+ * 1) zlogin foo 'echo hello'
+ * This has a single argv member with a space in it but will not be
+ * quoted on the command passed into the zone.
+ * 2) zlogin foo bash -c 'echo hello'
+ * This has 3 argv members. The 3rd arg has a space and must be
+ * quoted on the command passed into the zone.
+ */
+ for (i = 0; i < argc; i++) {
+ if (i > 0)
(void) strcat(subshell, " ");
+
+ if (argc > 1 && (strchr(argv[i], ' ') != NULL ||
+ strchr(argv[i], '\t') != NULL)) {
+ (void) strcat(subshell, "'");
+ (void) strcat(subshell, argv[i]);
+ (void) strcat(subshell, "'");
+ } else {
+ (void) strcat(subshell, argv[i]);
}
+ }
- if (failsafe) {
- n = 4;
- if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
- return (NULL);
+ if (failsafe) {
+ int a = 0, n = 4;
- new_argv[a++] = FAILSAFESHELL;
- } else {
- n = 5;
- if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
- return (NULL);
+ if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
+ return (NULL);
- new_argv[a++] = SUPATH;
- if (strcmp(login, "root") != 0) {
- new_argv[a++] = "-";
- n++;
- }
- new_argv[a++] = (char *)login;
- }
+ new_argv[a++] = FAILSAFESHELL;
new_argv[a++] = "-c";
new_argv[a++] = subshell;
new_argv[a++] = NULL;
assert(a == n);
} else {
- if (failsafe) {
- n = 2;
- if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
- return (NULL);
- new_argv[a++] = FAILSAFESHELL;
- new_argv[a++] = NULL;
- assert(n == a);
+ int a = 0, n = 6;
+
+ assert(supath != NULL);
+ if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
+ return (NULL);
+
+ new_argv[a++] = supath;
+ if (strcmp(login, "root") != 0) {
+ new_argv[a++] = "-";
} else {
- new_argv = zone_login_cmd(bh, login);
+ n--;
}
+ new_argv[a++] = (char *)login;
+ new_argv[a++] = "-c";
+ new_argv[a++] = subshell;
+ new_argv[a++] = NULL;
+ assert(a == n);
}
return (new_argv);
@@ -1186,6 +1343,7 @@ prep_env()
int e = 0, size = 1;
char **new_env, *estr;
char *term = getenv("TERM");
+ char *path;
size++; /* for $PATH */
if (term != NULL)
@@ -1202,7 +1360,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;
@@ -1724,24 +1887,61 @@ get_username()
return (nptr->pw_name);
}
+static boolean_t
+zlog_mode_logging(char *zonename, boolean_t *found)
+{
+ boolean_t lm = B_FALSE;
+ zone_dochandle_t handle;
+ struct zone_attrtab attr;
+
+ *found = B_FALSE;
+ 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) {
+ int len = strlen(attr.zone_attr_value);
+
+ *found = B_TRUE;
+ if (strncmp("log", attr.zone_attr_value, 3) == 0 ||
+ strncmp("nolog", attr.zone_attr_value, 5) == 0 ||
+ (len >= 3 && attr.zone_attr_value[len - 2] == '-'))
+ 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];
@@ -1755,7 +1955,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;
@@ -1763,6 +1963,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."));
@@ -1782,15 +1992,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;
@@ -1801,6 +2018,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"));
@@ -1827,17 +2050,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
@@ -1860,7 +2093,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();
}
@@ -1946,10 +2180,31 @@ 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;
+ int retry;
+ boolean_t set_raw = B_TRUE;
+
+ if (imode) {
+ boolean_t has_zfd_config;
+
+ if (zlog_mode_logging(zonename, &has_zfd_config))
+ set_raw = B_FALSE;
+
+ /*
+ * Asked for standalone interactive mode but the
+ * zlog-mode attribute is not configured on the zone.
+ */
+ if (!has_zfd_config) {
+ zerror(gettext("'%s' is not configured on "
+ "the zone"), "zlog-mode");
+ return (1);
+ }
+ }
+
/*
* Ensure that zoneadmd for this zone is running.
*/
@@ -1958,16 +2213,56 @@ main(int argc, char **argv)
/*
* Make contact with zoneadmd.
+ *
+ * Handshake with the control socket first. We handle retries
+ * here since the relevant thread in zoneadmd might not have
+ * finished setting up yet.
*/
- if (get_console_master(zonename) == -1)
+ for (retry = 0; retry < MAX_RETRY; retry++) {
+ masterfd = connect_zone_sock(zonename,
+ (imode ? "server_ctl" : "console_sock"), B_FALSE);
+ if (masterfd != -1)
+ break;
+ sleep(1);
+ }
+
+ if (retry == MAX_RETRY) {
+ zerror(gettext("unable to connect for %d seconds"),
+ MAX_RETRY);
return (1);
+ }
- if (!quiet)
- (void) printf(
- gettext("[Connected to zone '%s' console]\n"),
- zonename);
+ if (handshake_zone_sock(masterfd, connect_flags) != 0) {
+ (void) close(masterfd);
+ return (1);
+ }
+
+ if (imode) {
+ ctlfd = masterfd;
+
+ /* Now open the io-related sockets */
+ masterfd = connect_zone_sock(zonename, "server_out",
+ B_TRUE);
+ gz_stderr_fd = connect_zone_sock(zonename,
+ "server_err", B_TRUE);
+ if (masterfd == -1 || gz_stderr_fd == -1) {
+ (void) close(ctlfd);
+ (void) close(masterfd);
+ (void) close(gz_stderr_fd);
+ return (1);
+ }
+ }
+
+ 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);
@@ -1976,15 +2271,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);
}
@@ -2052,11 +2357,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(5) entry for login.
@@ -2202,6 +2519,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);
@@ -2243,8 +2562,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);
@@ -2253,13 +2582,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);
@@ -2284,8 +2617,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 5dea2ace60..4581c14af9 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
@@ -44,13 +48,14 @@ CERRWARN += $(CNOWARN_UNINIT)
.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:%=%)
@@ -60,7 +65,7 @@ $(POFILE): $(POFILES)
$(CAT) $(POFILES) > $@
clean:
- $(RM) $(OBJS) $(POFILES)
+ $(RM) $(OBJS) $(POFILES) $(SCRIPTS)
lint: lint_SRCS
diff --git a/usr/src/cmd/zoneadm/svc-resource-mgmt b/usr/src/cmd/zoneadm/svc-resource-mgmt
index 762de4c0d8..57fd21c3d9 100644
--- a/usr/src/cmd/zoneadm/svc-resource-mgmt
+++ b/usr/src/cmd/zoneadm/svc-resource-mgmt
@@ -22,25 +22,19 @@
#
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-
-# ident "%Z%%M% %I% %E% SMI"
-#
+# Copyright 2012 Joyent, Inc. All rights reserved.
. /lib/svc/share/smf_include.sh
-[ ! -f /etc/zones/global.xml ] && exit $SMF_EXIT_OK # No global zone
- # resource mgmt.
- # configuration
-
-[ ! -x /usr/sbin/zoneadm ] && exit $SMF_EXIT_OK # SUNWzoneu not installed
-
# Make sure working directory is / to prevent unmounting problems.
cd /
PATH=/usr/sbin:/usr/bin; export PATH
+smf_is_globalzone || exit $SMF_EXIT_OK
+
case "$1" in
'start')
- zoneadm -z global apply
+ prctl -r -n zone.cpu-shares -v 65535 -t priv -i zone global
if [ $? -ne 0 ]; then
exit $SMF_EXIT_ERR_FATAL
fi
diff --git a/usr/src/cmd/zoneadm/svc-zones b/usr/src/cmd/zoneadm/svc-zones
index cd06d0789a..09a9dc4d10 100644
--- a/usr/src/cmd/zoneadm/svc-zones
+++ b/usr/src/cmd/zoneadm/svc-zones
@@ -22,9 +22,128 @@
#
# Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+# Copyright (c) 2012, Joyent Inc. All rights reserved.
+
+set -o xtrace
. /lib/svc/share/smf_include.sh
+ZPOOL=`svcprop -p config/zpool svc:/system/smartdc/init:default 2>/dev/null`
+ZPOOL=${ZPOOL:-zones}
+
+MAN_SRCDIR=/lib/svc/manifest
+MAN_DESTDIR=/$ZPOOL/manifests
+
+MAN_DIRS="\
+ application \
+ milestone \
+ network \
+ network/dns \
+ network/ipsec \
+ network/ldap \
+ network/loadbalancer \
+ network/nfs \
+ network/nis \
+ network/routing \
+ network/rpc \
+ network/security \
+ network/shares \
+ network/smb \
+ network/ssl \
+ site \
+ system \
+ system/device \
+ system/filesystem \
+ system/fm \
+ system/install \
+ system/security \
+ system/svc"
+
+cp_brand_manifests()
+{
+ brand=$1
+
+ # Create the dir hierarchy under the dest dir for SMF manifests.
+ mkdir -m755 $MAN_DESTDIR/$brand
+
+ for dir in $MAN_DIRS
+ do
+ mkdir -m755 $MAN_DESTDIR/$brand/$dir
+ done
+
+ #
+ # Process the SMF SVC configuration list to setup the brand-specific
+ # SMF svcs.
+ #
+ nawk -v base=$MAN_SRCDIR -v dest=$MAN_DESTDIR/$brand '{
+ # Ignore comments and empty lines.
+ if (substr($1, 1, 1) == "#" || length($1) == 0)
+ next
+
+ # entry format is: name status
+ proc_file($1, $2);
+ }
+
+ #
+ # Copy the manifest from the global zone to the brand-specific
+ # manifest area. At the same time, update any manifests whose
+ # status needs to be changed, based on what our configuration
+ # file indicates.
+ #
+ function proc_file(fname, status)
+ {
+ f = base "/" fname
+ of = dest "/" fname
+
+ while (getline <f > 0) {
+ # Fix up the console svc to work with zones.
+ if (fname == "system/console-login.xml")
+ sub("wscons", "console")
+
+ if (($1 == "<instance" &&
+ $2 == "name=\047default\047") ||
+ $1 == "<create_default_instance") {
+ if (status == "enabled")
+ n=sub("\047false\047", "\047true\047")
+ else
+ n=sub("\047true\047", "\047false\047")
+
+ if (n > 0)
+ printf("update svc state: %s %s\n",
+ fname, $0)
+ }
+
+ print $0 >of
+ }
+ close(f)
+ close(of)
+ }' /usr/lib/brand/$brand/manifests
+}
+
+#
+# If we're running off of a live-image, setup a zone-specific collection of
+# manifest files which are mounted in a zone's /lib/svc/manifest directory.
+#
+# Regenerate the manifest data each time this service starts, so that it's
+# always in sync with the running platform (and any fixes included there).
+#
+setup_manifests()
+{
+ echo "Initializing manifest dir."
+
+ rm -rf $MAN_DESTDIR
+ mkdir -m755 -p $MAN_DESTDIR
+
+ for i in /usr/lib/brand/*
+ do
+ brand=`basename $i`
+ [[ ! -f /usr/lib/brand/$brand/manifests ]] && continue
+ # joyent-minimal uses /zones/manifests/joyent too
+ [[ "$brand" == "joyent-minimal" ]] && continue
+ cp_brand_manifests $brand
+ done
+}
+
#
# Return a list of running, non-global zones for which a shutdown via
# "/sbin/init 0" may work (typically only Solaris zones.)
@@ -51,6 +170,16 @@ PATH=/usr/sbin:/usr/bin; export PATH
case "$1" in
'start')
+ #
+ # Generate the manifest, even if no zones, since zones could be
+ # provisioned later.
+ #
+ zfs list -H -o name $ZPOOL >/dev/null 2>&1
+ [ $? -eq 0 ] && setup_manifests
+
+ # Create directory for zone sockets
+ mkdir -m755 -p /var/zonecontrol
+
egrep -vs '^#|^global:' /etc/zones/index || exit 0 # no local zones
#
@@ -68,6 +197,14 @@ case "$1" in
[ -z "$ZONES" ] && echo "Booting zones:\c"
ZONES=yes
echo " $zone\c"
+
+ #
+ # Make sure a site dir exists, it wasn't initially
+ # being created.
+ #
+ zonepath=`zonecfg -z $zone info zonepath | cut -d: -f2`
+ [ ! -d $zonepath/site ] && mkdir -m755 $zonepath/site
+
#
# zoneadmd puts itself into its own contract so
# this service will lose sight of it. We don't
@@ -110,7 +247,7 @@ case "$1" in
for zone in $zonelist; do
echo " $zone\c"
- zoneadm -z $zone shutdown &
+ zlogin -S $zone /sbin/init 0 < /dev/null >&0 2>&0 &
SHUTDOWN=1
done
diff --git a/usr/src/cmd/zoneadm/zfs.c b/usr/src/cmd/zoneadm/zfs.c
index b261504336..f1ce9fab0a 100644
--- a/usr/src/cmd/zoneadm/zfs.c
+++ b/usr/src/cmd/zoneadm/zfs.c
@@ -21,8 +21,8 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright (c) 2016 Martin Matuska. All rights reserved.
* Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
*/
@@ -975,6 +975,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;
@@ -1010,9 +1011,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 6ee7bf244c..05a5006e42 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.
* Copyright (c) 2015 by Delphix. All rights reserved.
*/
@@ -101,13 +102,11 @@ 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"
-static zone_entry_t *zents;
-static size_t nzents;
-
#define LOOPBACK_IF "lo0"
#define SOCKET_AF(af) (((af) == AF_UNSPEC) ? AF_INET : (af))
@@ -406,19 +405,6 @@ zerror(const char *fmt, ...)
va_end(alist);
}
-static void *
-safe_calloc(size_t nelem, size_t elsize)
-{
- void *r = calloc(nelem, elsize);
-
- if (r == NULL) {
- zerror(gettext("failed to allocate %lu bytes: %s"),
- (ulong_t)nelem * elsize, strerror(errno));
- exit(Z_ERR);
- }
- return (r);
-}
-
static void
zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable)
{
@@ -443,6 +429,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);
@@ -458,8 +445,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) {
@@ -485,6 +476,9 @@ lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent)
(void) strlcpy(zent->zbrand, "???", sizeof (zent->zbrand));
zent->zstate_str = "???";
+ if (strcmp(zone_name, GLOBAL_ZONENAME) == 0)
+ zid = zent->zdid = GLOBAL_ZONEID;
+
zent->zid = zid;
if (zonecfg_get_uuid(zone_name, uuid) == Z_OK &&
@@ -529,8 +523,8 @@ lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent)
zent->zstate_str = zone_state_str(zent->zstate_num);
/*
- * A zone's brand is only available in the .xml file describing it,
- * which is only visible to the global zone. This causes
+ * A zone's brand might only be available in the .xml file describing
+ * it, which is only visible to the global zone. This causes
* zone_get_brand() to fail when called from within a non-global
* zone. Fortunately we only do this on labeled systems, where we
* know all zones are native.
@@ -554,6 +548,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
@@ -574,193 +584,76 @@ 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);
}
-/*
- * fetch_zents() calls zone_list(2) to find out how many zones are running
- * (which is stored in the global nzents), then calls zone_list(2) again
- * to fetch the list of running zones (stored in the global zents). This
- * function may be called multiple times, so if zents is already set, we
- * return immediately to save work.
- *
- * Note that the data about running zones can change while this function
- * is running, so its possible that the list of zones will have empty slots
- * at the end.
- */
-
-static int
-fetch_zents(void)
-{
- zoneid_t *zids = NULL;
- uint_t nzents_saved;
- int i, retv;
- FILE *fp;
- boolean_t inaltroot;
- zone_entry_t *zentp;
- const char *altroot;
-
- if (nzents > 0)
- return (Z_OK);
-
- if (zone_list(NULL, &nzents) != 0) {
- zperror(gettext("failed to get zoneid list"), B_FALSE);
- return (Z_ERR);
- }
-
-again:
- if (nzents == 0)
- return (Z_OK);
-
- zids = safe_calloc(nzents, sizeof (zoneid_t));
- nzents_saved = nzents;
-
- if (zone_list(zids, &nzents) != 0) {
- zperror(gettext("failed to get zone list"), B_FALSE);
- free(zids);
- return (Z_ERR);
- }
- if (nzents != nzents_saved) {
- /* list changed, try again */
- free(zids);
- goto again;
- }
-
- zents = safe_calloc(nzents, sizeof (zone_entry_t));
-
- inaltroot = zonecfg_in_alt_root();
- if (inaltroot) {
- fp = zonecfg_open_scratch("", B_FALSE);
- altroot = zonecfg_get_root();
- } else {
- fp = NULL;
- }
- zentp = zents;
- retv = Z_OK;
- for (i = 0; i < nzents; i++) {
- char name[ZONENAME_MAX];
- char altname[ZONENAME_MAX];
- char rev_altroot[MAXPATHLEN];
-
- if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) {
- /*
- * There is a race condition where the zone may have
- * shutdown since we retrieved the number of running
- * zones above. This is not an error, there will be
- * an empty slot at the end of the list.
- */
- continue;
- }
- if (zonecfg_is_scratch(name)) {
- /* Ignore scratch zones by default */
- if (!inaltroot)
- continue;
- if (fp == NULL ||
- zonecfg_reverse_scratch(fp, name, altname,
- sizeof (altname), rev_altroot,
- sizeof (rev_altroot)) == -1) {
- zerror(gettext("could not resolve scratch "
- "zone %s"), name);
- retv = Z_ERR;
- continue;
- }
- /* Ignore zones in other alternate roots */
- if (strcmp(rev_altroot, altroot) != 0)
- continue;
- (void) strcpy(name, altname);
- } else {
- /* Ignore non-scratch when in an alternate root */
- if (inaltroot && strcmp(name, GLOBAL_ZONENAME) != 0)
- continue;
- }
- if (lookup_zone_info(name, zids[i], zentp) != Z_OK) {
- /*
- * There is a race condition where the zone may have
- * shutdown since we retrieved the number of running
- * zones above. This is not an error, there will be
- * an empty slot at the end of the list.
- */
- continue;
- }
- zentp++;
- }
- nzents = zentp - zents;
- if (fp != NULL)
- zonecfg_close_scratch(fp);
-
- free(zids);
- return (retv);
-}
-
static int
zone_print_list(zone_state_t min_state, boolean_t verbose, boolean_t parsable,
boolean_t exclude_global)
{
- int i;
zone_entry_t zent;
FILE *cookie;
- char *name;
+ struct zoneent *ze;
/*
- * First get the list of running zones from the kernel and print them.
- * If that is all we need, then return.
- */
- if ((i = fetch_zents()) != Z_OK) {
- /*
- * No need for error messages; fetch_zents() has already taken
- * care of this.
- */
- return (i);
- }
- for (i = 0; i < nzents; i++) {
- if (exclude_global && zents[i].zid == GLOBAL_ZONEID)
- continue;
- zone_print(&zents[i], verbose, parsable);
- }
- if (min_state >= ZONE_STATE_RUNNING)
- return (Z_OK);
- /*
- * Next, get the full list of zones from the configuration, skipping
- * any we have already printed.
+ * Get the full list of zones from the configuration.
*/
cookie = setzoneent();
- while ((name = getzoneent(cookie)) != NULL) {
- for (i = 0; i < nzents; i++) {
- if (strcmp(zents[i].zname, name) == 0)
- break;
- }
- if (i < nzents) {
- free(name);
- continue;
- }
- if (lookup_zone_info(name, ZONE_ID_UNDEFINED, &zent) != Z_OK) {
- free(name);
+ while ((ze = getzoneent_private(cookie)) != NULL) {
+ char *name = ze->zone_name;
+ zoneid_t zid;
+
+ zid = getzoneidbyname(name);
+ if (exclude_global && zid == GLOBAL_ZONEID)
continue;
+
+ if (ze->zone_brand[0] == '\0') {
+ /* old, incomplete index entry */
+ if (lookup_zone_info(name, zid, &zent) != Z_OK) {
+ free(ze);
+ continue;
+ }
+ } else {
+ /* new, full index entry */
+ (void) strlcpy(zent.zname, name, sizeof (zent.zname));
+ (void) strlcpy(zent.zroot, ze->zone_path,
+ sizeof (zent.zroot));
+ uuid_unparse(ze->zone_uuid, zent.zuuid);
+ (void) strlcpy(zent.zbrand, ze->zone_brand,
+ sizeof (zent.zbrand));
+ zent.ziptype = ze->zone_iptype;
+ zent.zdid = ze->zone_did;
+ zent.zid = zid;
+
+ if (zid != -1) {
+ int err;
+
+ err = zone_get_state(name,
+ (zone_state_t *)&ze->zone_state);
+ if (err != Z_OK) {
+ errno = err;
+ zperror2(name, gettext("could not get "
+ "state"));
+ free(ze);
+ continue;
+ }
+ }
+
+ zent.zstate_num = ze->zone_state;
+ zent.zstate_str = zone_state_str(zent.zstate_num);
}
- free(name);
+
if (zent.zstate_num >= min_state)
zone_print(&zent, verbose, parsable);
+
+ free(ze);
}
endzoneent(cookie);
return (Z_OK);
@@ -770,18 +663,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 ((zid = getzoneidbyname(name)) == -1)
+ return (NULL);
- if (fetch_zents() != Z_OK)
+ if ((zent = malloc(sizeof (zone_entry_t))) == NULL)
return (NULL);
- for (i = 0; i < nzents; i++) {
- if (strcmp(str, zents[i].zname) == 0)
- return (&zents[i]);
+ if (lookup_zone_info(name, zid, zent) != Z_OK) {
+ free(zent);
+ return (NULL);
}
- return (NULL);
+ return (zent);
}
/*
@@ -1017,8 +914,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
@@ -1205,6 +1106,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()) {
@@ -1213,11 +1115,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);
@@ -1234,6 +1139,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);
@@ -1246,6 +1152,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()) {
@@ -1272,7 +1179,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);
@@ -1284,6 +1191,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);
@@ -1309,6 +1219,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);
@@ -1623,10 +1534,10 @@ auth_check(char *user, char *zone, int cmd_num)
* not already running (or ready).
*/
static int
-sanity_check(char *zone, int cmd_num, boolean_t running,
+sanity_check(char *zone, int cmd_num, boolean_t need_running,
boolean_t unsafe_when_running, boolean_t force)
{
- zone_entry_t *zent;
+ boolean_t is_running = B_FALSE;
priv_set_t *privset;
zone_state_t state, min_state;
char kernzone[ZONENAME_MAX];
@@ -1697,51 +1608,54 @@ sanity_check(char *zone, int cmd_num, boolean_t running,
}
if (!zonecfg_in_alt_root()) {
- zent = lookup_running_zone(zone);
- } else if ((fp = zonecfg_open_scratch("", B_FALSE)) == NULL) {
- zent = NULL;
- } else {
- if (zonecfg_find_scratch(fp, zone, zonecfg_get_root(),
- kernzone, sizeof (kernzone)) == 0)
- zent = lookup_running_zone(kernzone);
- else
- zent = NULL;
+ /* Avoid the xml read overhead of lookup_running_zone */
+ if (getzoneidbyname(zone) != -1)
+ is_running = B_TRUE;
+
+ } else if ((fp = zonecfg_open_scratch("", B_FALSE)) != NULL) {
+ if (zonecfg_find_scratch(fp, zone, zonecfg_get_root(), kernzone,
+ sizeof (kernzone)) == 0 && getzoneidbyname(kernzone) != -1)
+ is_running = B_TRUE;
+
zonecfg_close_scratch(fp);
}
/*
* Look up from the kernel for 'running' zones.
*/
- if (running && !force) {
- if (zent == NULL) {
+ if (need_running && !force) {
+ if (!is_running) {
zerror(gettext("not running"));
return (Z_ERR);
}
} else {
int err;
- if (unsafe_when_running && zent != NULL) {
+ err = zone_get_state(zone, &state);
+
+ if (unsafe_when_running && is_running) {
/* check whether the zone is ready or running */
- if ((err = zone_get_state(zent->zname,
- &zent->zstate_num)) != Z_OK) {
+ char *zstate_str;
+
+ if (err != Z_OK) {
errno = err;
- zperror2(zent->zname,
- gettext("could not get state"));
+ zperror2(zone, gettext("could not get state"));
/* can't tell, so hedge */
- zent->zstate_str = "ready/running";
+ zstate_str = "ready/running";
} else {
- zent->zstate_str =
- zone_state_str(zent->zstate_num);
+ zstate_str = zone_state_str(state);
}
zerror(gettext("%s operation is invalid for %s zones."),
- cmd_to_str(cmd_num), zent->zstate_str);
+ cmd_to_str(cmd_num), zstate_str);
return (Z_ERR);
}
- if ((err = zone_get_state(zone, &state)) != Z_OK) {
+
+ if (err != Z_OK) {
errno = err;
zperror2(zone, gettext("could not get state"));
return (Z_ERR);
}
+
switch (cmd_num) {
case CMD_UNINSTALL:
if (state == ZONE_STATE_CONFIGURED) {
@@ -1829,6 +1743,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()) {
@@ -1837,11 +1752,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);
@@ -1867,6 +1785,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);
}
@@ -1944,6 +1863,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()) {
@@ -1952,11 +1872,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);
@@ -1991,6 +1914,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);
}
@@ -2218,6 +2142,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,
@@ -2621,7 +2549,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)
@@ -2704,11 +2631,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;
@@ -2792,6 +2714,74 @@ 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;
+ struct zoneent *ze;
+ FILE *cookie;
+ 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 ((ze = getzoneent_private(cookie)) != NULL) {
+ char *name;
+ zoneid_t did;
+
+ name = ze->zone_name;
+ if (strcmp(name, GLOBAL_ZONENAME) == 0 ||
+ strcmp(name, target_zone) == 0) {
+ free(ze);
+ continue;
+ }
+
+ if (ze->zone_brand[0] == '\0') {
+ /* old, incomplete index entry */
+ zone_entry_t zent;
+
+ if (lookup_zone_info(name, ZONE_ID_UNDEFINED,
+ &zent) != Z_OK) {
+ free(ze);
+ continue;
+ }
+ did = zent.zdid;
+ } else {
+ /* new, full index entry */
+ did = ze->zone_did;
+ }
+ free(ze);
+
+ if (did == 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[])
{
@@ -2851,6 +2841,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,
@@ -2936,6 +2938,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) {
@@ -3011,6 +3014,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;
@@ -3063,7 +3072,8 @@ install_func(int argc, char *argv[])
goto done;
}
- create_zfs_zonepath(zonepath);
+ if (do_dataset)
+ create_zfs_zonepath(zonepath);
}
status = do_subproc(cmdbuf);
@@ -3874,10 +3884,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
@@ -5027,6 +5037,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);
@@ -5142,6 +5153,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");
@@ -5163,6 +5175,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);
@@ -5384,7 +5397,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();
@@ -5475,19 +5488,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 5d81a78ca6..91b9f0eda4 100644
--- a/usr/src/cmd/zoneadm/zones.xml
+++ b/usr/src/cmd/zoneadm/zones.xml
@@ -54,11 +54,32 @@
<service_fmri value='svc:/milestone/multi-user-server' />
</dependency>
+ <!--
+ Until overlay device creation is moved out of the zone
+ state-change script, zones must be dependent on varpd's
+ successful launch.
+ -->
+ <dependency
+ name='varpd'
+ type='service'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/network/varpd' />
+ </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 e75453fe56..cfff59f12a 100644
--- a/usr/src/cmd/zoneadmd/Makefile
+++ b/usr/src/cmd/zoneadmd/Makefile
@@ -18,12 +18,10 @@
#
# 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
@@ -31,40 +29,46 @@ PROG= zoneadmd
include ../Makefile.cmd
include ../Makefile.ctf
-ROOTCMDDIR= $(ROOTLIB)/zones
+$(64ONLY)SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
-OBJS= zoneadmd.o zcons.o vplat.o
-SRCS = $(OBJS:.o=.c)
-POFILE=zoneadmd_all.po
-POFILES= $(OBJS:%.o=%.po)
+all := TARGET = all
+install := TARGET = install
+clean := TARGET = clean
+clobber := TARGET = clobber
+lint := TARGET = lint
CFLAGS += $(CCVERBOSE)
-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)
+install: $(SUBDIRS)
+ -$(RM) $(ROOTUSRLIBZONES)/$(PROG)
+ -$(LN) $(ISAEXEC) $(ROOTUSRLIBZONES)/$(PROG)
-$(POFILE): $(POFILES)
- $(RM) $@
- $(CAT) $(POFILES) > $@
+$(POFILE):
clean:
$(RM) $(OBJS)
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..aaf21c7f5b
--- /dev/null
+++ b/usr/src/cmd/zoneadmd/Makefile.com
@@ -0,0 +1,72 @@
+#
+# 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 2020 Joyent, Inc.
+#
+
+PROG= zoneadmd
+
+include ../../Makefile.cmd
+include ../../Makefile.ctf
+
+ROOTCMDDIR= $(ROOTLIB)/zones
+
+OBJS= zoneadmd.o zcons.o zfd.o vplat.o log.o
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \
+ -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol \
+ -linetutil -lscf -lppt -lcustr
+
+CSTD= $(CSTD_GNU99)
+
+.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/log.c b/usr/src/cmd/zoneadmd/log.c
new file mode 100644
index 0000000000..a4ecc3e1e8
--- /dev/null
+++ b/usr/src/cmd/zoneadmd/log.c
@@ -0,0 +1,1027 @@
+/*
+ * 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 2020 Joyent, Inc.
+ */
+
+/*
+ * zoneadmd logging
+ *
+ * zoneadmd logs to log files under <zonepath>/logs. Each log entry is a json
+ * structure of the form:
+ *
+ * {
+ * "log": "some message\n",
+ * "stream": "stderr",
+ * "time": "2018-03-28T13:25:02.670423000Z"
+ * }
+ *
+ * Unlike the example above, the entries in the log file are not pretty-printed.
+ * Messages are processed so that they have the proper json escapes for
+ * problematic characters. Excessively long messages may be truncated.
+ *
+ * To use these interfaces:
+ *
+ * int logid;
+ *
+ * logstream_init(zlogp);
+ *
+ * logid = logstream_open("stdio.log", "stdout", flags);
+ * ...
+ * logstream_write(logid, buf, len);
+ * ...
+ * logstream_close(logid);
+ *
+ * logstream_init() needs to be called only once.
+ *
+ * logstream_open() opens a log file (if not already open) and associates the
+ * specified stream with it.
+ *
+ * The following flag is supported:
+ *
+ * LS_LINE_BUFFERED Buffer writes until a newline is encountered or the
+ * buffer fills. This should only be used with streams
+ * that are written to by a single thread. The timestamp
+ * on log messages are the time that the log entry was
+ * written to the log file. This means the timestamp is
+ * the time when the console user hits enter, not the time
+ * that the prompt was printed.
+ *
+ * Line buffering is particularly useful for bhyve console logging because
+ * bhyve's UART emulation causes read() calls in zcons.c to return far fewer
+ * than 10 characters at a time. Without line buffering, a small number of
+ * logged characters are accompanied by about 64 characters of timestamp and
+ * other overhead. Line buffering saves quite a lot of space and makes the log
+ * much easier to read.
+ *
+ *
+ * Log rotation
+ *
+ * Two attributes, zlog-max-size and zlog-keep-rotated are used for automatic
+ * log rotation. zlog-max-size is the approximate maximum size of a log before
+ * it is automatically rotated. Rotated logs are renamed as
+ * <log>.<iso-8601-stamp>. If zlog-keep-rotated is specified and is an integer
+ * greater than zero, only that number of rotated logs will be retained.
+ *
+ * If zlog-max-size is not specified, log rotation will not happen
+ * automatically. An external log rotation program may rename the log file(s),
+ * then send SIGHUP to zoneadmd.
+ *
+ * Log rotation can be forced with SIGUSR1. In this case, the log will be
+ * rotated as though it hit the maximum size and will be subject to retention
+ * rules described above.
+ *
+ *
+ * Locking strategy
+ *
+ * Callers need not worry about locking. In the interest of simplicity, a
+ * single global lock is used to protect the state of the log files and the
+ * associated streams. Locking is necessary because reboots and log rotations
+ * can cause various state changes. Without locking, races could cause log
+ * entries to be directed to the wrong file descriptors.
+ *
+ * The simplistic global lock complicates error reporting within logging
+ * routines. zerror() must not be called while holding logging_lock. Rather,
+ * logstream_err() should be used to log via syslog.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <libcustr.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <strings.h>
+#include <synch.h>
+#include <syslog.h>
+#include <time.h>
+#include <thread.h>
+#include <unistd.h>
+
+#include <sys/debug.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/varargs.h>
+
+#include "zoneadmd.h"
+
+/*
+ * Currently we only expect stdout, stderr, zoneadmd, and console. Increase
+ * MAX_ZLOG_STREAMS if more streams are added. If the count increases
+ * significantly, logfile_t and logstream_t elements should be dynamically
+ * allocated and the algorithms associated with opening and closing them should
+ * become more efficient.
+ */
+#define MAX_LOG_STREAMS 4
+
+#define ZLOG_MAXSZ "zlog-max-size" /* zonecfg attr */
+#define ZLOG_MAXSZ_MIN (1024 * 1024) /* min size for autorotate */
+#define ZLOG_KEEP "zlog-keep-rotated" /* zonecfg attr */
+#define ZLOG_KEEP_MAX 1000 /* number of log files */
+
+/*
+ * While we could get the numeric value of BUNYAN_LOG_INFO from bunyan.h,
+ * the log version is internal to the library, so we just define the values
+ * we need here.
+ */
+#define BUNYAN_VERSION 0
+#define BUNYAN_LOG_LEVEL 30 /* info */
+
+typedef struct logfile {
+ char lf_path[MAXPATHLEN]; /* log file name (absolute path) */
+ char lf_name[MAXNAMELEN]; /* tail of log file name */
+ char lf_buf[BUFSIZ]; /* Buffer for event messages */
+ custr_t *lf_cus; /* custr_t wrapper for lf_buf */
+ int lf_fd; /* file descriptor */
+ size_t lf_size; /* Current size */
+ boolean_t lf_write_err; /* Avoid spamming console via logsys */
+ boolean_t lf_closing; /* Avoid rotation recursion */
+} logfile_t;
+
+/* Large enough to hold BUFSIZ bytes with some escaping */
+#define LS_BUFSZ (BUFSIZ * 2)
+
+/* Large enough to hold LS_BUF contents + bunyan mandatory properties */
+#define LS_OBUFSZ (LS_BUFSZ + MAXNAMELEN + 128)
+
+typedef struct logstream {
+ char ls_stream[MAXNAMELEN]; /* stdout, stderr, etc. */
+ char ls_buf[LS_BUFSZ]; /* Not-yet written data, json */
+ char ls_obuf[LS_OBUFSZ]; /* Buffer to form output json */
+ custr_t *ls_cusbuf; /* custr_t wrapper to ls_buf */
+ custr_t *ls_cusobuf; /* custr_t wrapper to ls_ofbuf */
+ logstream_flags_t ls_flags;
+ logfile_t *ls_logfile; /* N streams per log file */
+} logstream_t;
+
+typedef struct jsonpair {
+ const char *jp_key;
+ const char *jp_val;
+} jsonpair_t;
+
+boolean_t logging_poisoned = B_FALSE;
+
+/*
+ * MAX_LOG_STREAMS is a small number so we allocate in the simplest way.
+ */
+static logstream_t streams[MAX_LOG_STREAMS];
+static logfile_t logfiles[MAX_LOG_STREAMS];
+
+static char host[MAXHOSTNAMELEN];
+static char pidstr[10];
+
+static boolean_t logging_initialized = B_FALSE;
+static uint64_t logging_rot_size; /* See ZLOG_MAXSZ */
+static uint64_t logging_rot_keep; /* See ZLOG_KEEP */
+static int logging_pending_sig = 0; /* Signal recvd while logging */
+static mutex_t logging_lock = ERRORCHECKMUTEX; /* The global logging lock */
+
+static void logstream_flush_all(logfile_t *);
+static void logstream_sighandler(int);
+static void rotate_log(logfile_t *);
+static size_t make_json(jsonpair_t *, size_t, custr_t *);
+static void logfile_write(logfile_t *, custr_t *);
+
+/*
+ * If errors are encountered while logging_lock is held, we can't use zerror().
+ */
+static void
+logstream_err(boolean_t use_strerror, const char *fmt, ...)
+{
+ va_list alist;
+ char buf[MAXPATHLEN * 2];
+ char *bp;
+ int saved_errno = errno;
+
+ (void) snprintf(buf, sizeof (buf), "[zone %s] ", zone_name);
+
+ bp = &buf[strlen(buf)];
+
+ va_start(alist, fmt);
+ (void) vsnprintf(bp, sizeof (buf) - (bp - buf), fmt, alist);
+ va_end(alist);
+
+ if (use_strerror) {
+ bp = &buf[strlen(buf)];
+ (void) snprintf(bp, sizeof (buf) - (bp - buf), ": %s",
+ strerror(saved_errno));
+ }
+ syslog(LOG_ERR, "%s", buf);
+
+ errno = saved_errno;
+}
+
+static void
+logstream_lock(void)
+{
+ VERIFY(logging_initialized);
+ VERIFY(!logging_poisoned);
+
+ mutex_enter(&logging_lock);
+}
+
+static void
+logstream_unlock(void)
+{
+ int sig = logging_pending_sig;
+
+ logging_pending_sig = 0;
+ mutex_exit(&logging_lock);
+
+ /*
+ * If a signal arrived while this thread was holding the lock, call the
+ * handler.
+ */
+ if (sig != 0) {
+ logstream_sighandler(sig);
+ }
+}
+
+static void
+logfile_write_event(logfile_t *lfp, const char *stream, const char *event)
+{
+ size_t len;
+ jsonpair_t pairs[] = {
+ { "stream", stream },
+ { "msg", event }
+ };
+
+ len = make_json(pairs, ARRAY_SIZE(pairs), lfp->lf_cus);
+ if (len >= sizeof (lfp->lf_buf)) {
+ logstream_err(B_FALSE, "%s: buffer too small. Need %zu bytes, "
+ "have %zu bytes", __func__, len + 1, sizeof (lfp->lf_buf));
+ return;
+ }
+
+ logfile_write(lfp, lfp->lf_cus);
+}
+
+static void
+close_log(logfile_t *lfp, const char *why, boolean_t ign_err)
+{
+ int err;
+
+ VERIFY(MUTEX_HELD(&logging_lock));
+
+ /*
+ * Something may have gone wrong during log rotation, leading to a
+ * zombie log.
+ */
+ if (lfp->lf_fd == -1) {
+ return;
+ }
+
+ lfp->lf_closing = B_TRUE;
+
+ logstream_flush_all(lfp);
+
+ logfile_write_event(lfp, "logfile", why);
+
+ err = close(lfp->lf_fd);
+ if (!ign_err)
+ VERIFY0(err);
+
+ lfp->lf_size = 0;
+ lfp->lf_fd = -1;
+}
+
+static void
+open_log(logfile_t *lfp, const char *why)
+{
+ struct stat64 sb;
+
+ VERIFY(MUTEX_HELD(&logging_lock));
+ VERIFY3S(lfp->lf_fd, ==, -1);
+
+ lfp->lf_fd = open(lfp->lf_path,
+ O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0600);
+ if (lfp->lf_fd == -1) {
+ logstream_err(B_TRUE, "Cannot open log file %s",
+ lfp->lf_path);
+ lfp->lf_write_err = B_TRUE;
+ return;
+ }
+
+ VERIFY0(fstat64(lfp->lf_fd, &sb));
+ lfp->lf_size = sb.st_size;
+ lfp->lf_write_err = B_FALSE;
+ lfp->lf_closing = B_FALSE;
+
+ logfile_write_event(lfp, "logfile", why);
+}
+
+static void
+logstream_sighandler(int sig)
+{
+ int i;
+
+ /*
+ * Protect against recursive mutex enters when a signal comes during
+ * logging. This will cause this function to be called again just after
+ * this thread drops the lock.
+ */
+ if (MUTEX_HELD(&logging_lock)) {
+ logging_pending_sig = sig;
+ return;
+ }
+
+ logstream_lock();
+ if (logging_poisoned) {
+ logstream_unlock();
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(logfiles); i++) {
+ /* Inactive logfile slot */
+ if (logfiles[i].lf_name[0] == '\0') {
+ continue;
+ }
+
+ switch (sig) {
+ case SIGHUP:
+ close_log(&logfiles[i], "close-rotate", B_FALSE);
+ open_log(&logfiles[i], "open-rotate");
+ break;
+ case SIGUSR1:
+ rotate_log(&logfiles[i]);
+ break;
+ default:
+ logstream_err(B_FALSE, "unhandled signal %d", sig);
+ }
+ }
+
+ logstream_unlock();
+}
+
+static void
+get_attr_uint64(zlog_t *zlogp, zone_dochandle_t handle, const char *name,
+ uint64_t max, uint64_t *valp)
+{
+ struct zone_attrtab tab = { 0 };
+ char *p;
+ uint64_t val;
+
+ ASSERT(!MUTEX_HELD(&logging_lock));
+
+ (void) strlcpy(tab.zone_attr_name, name, sizeof (tab.zone_attr_name));
+ if (zonecfg_lookup_attr(handle, &tab) != Z_OK) {
+ return;
+ }
+
+ errno = 0;
+ val = strtol(tab.zone_attr_value, &p, 10);
+ if (errno != 0 && *p == '\0') {
+ zerror(zlogp, errno != 0, "Bad value '%s' for 'attr name=%s'",
+ tab.zone_attr_value, tab.zone_attr_name);
+ return;
+ }
+ if (val > max) {
+ zerror(zlogp, B_FALSE, "Value of attr '%s' is too large. "
+ "Reducing to %llu", name, max);
+ val = max;
+ }
+
+ *valp = val;
+}
+
+static void
+logstream_atfork_prepare(void)
+{
+ logstream_lock();
+}
+
+static void
+logstream_atfork_parent(void)
+{
+ logstream_unlock();
+}
+
+/*
+ * logstream_*() should never be called in a child process, so we make sure this
+ * code is never called there.
+ *
+ * zerror() in a child process is still safe: it knows to check for poisoning,
+ * and in such a case will redirect its output to stderr on the presumption it
+ * is a pipe to the parent.
+ */
+static void
+logstream_atfork_child(void)
+{
+ logging_poisoned = B_TRUE;
+ logging_pending_sig = 0;
+ (void) snprintf(pidstr, sizeof (pidstr), "%d", getpid());
+ logstream_unlock();
+}
+
+void
+logstream_init(zlog_t *zlogp)
+{
+ zone_dochandle_t handle;
+ int i;
+
+ VERIFY(!logging_initialized);
+
+ VERIFY0(gethostname(host, sizeof (host)));
+ (void) snprintf(pidstr, sizeof (pidstr), "%d", getpid());
+
+ for (i = 0; i < ARRAY_SIZE(logfiles); i++) {
+ logfile_t *lfp = &logfiles[i];
+
+ lfp->lf_fd = -1;
+ if (custr_alloc_buf(&lfp->lf_cus, lfp->lf_buf,
+ sizeof (lfp->lf_buf)) != 0) {
+ (void) fprintf(stderr, "failed to allocate custr_t for "
+ "log file\n");
+ abort();
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(streams); i++) {
+ logstream_t *lsp = &streams[i];
+
+ if (custr_alloc_buf(&lsp->ls_cusbuf, lsp->ls_buf,
+ sizeof (lsp->ls_buf)) != 0 ||
+ custr_alloc_buf(&lsp->ls_cusobuf, lsp->ls_obuf,
+ sizeof (lsp->ls_obuf)) != 0) {
+ (void) fprintf(stderr, "failed to allocate custr_t for "
+ "log stream\n");
+ abort();
+ }
+ }
+
+ VERIFY0(pthread_atfork(logstream_atfork_prepare,
+ logstream_atfork_parent, logstream_atfork_child));
+
+ logging_initialized = B_TRUE;
+
+ /* Now it is safe to use zlogp */
+
+ if ((handle = zonecfg_init_handle()) == NULL ||
+ zonecfg_get_handle(zone_name, handle) != Z_OK) {
+ zerror(zlogp, B_FALSE, "failed to open zone configuration "
+ "while initializing logging");
+ } else {
+ get_attr_uint64(zlogp, handle, ZLOG_MAXSZ, UINT64_MAX,
+ &logging_rot_size);
+ if (logging_rot_size != 0 &&
+ logging_rot_size < ZLOG_MAXSZ_MIN) {
+ zerror(zlogp, B_FALSE, "%s value %llu is too small. "
+ "Setting to %d", ZLOG_MAXSZ, logging_rot_size,
+ ZLOG_MAXSZ_MIN);
+ logging_rot_size = ZLOG_MAXSZ_MIN;
+ }
+ get_attr_uint64(zlogp, handle, ZLOG_KEEP, ZLOG_KEEP_MAX,
+ &logging_rot_keep);
+ }
+
+ zonecfg_fini_handle(handle);
+
+ /*
+ * This thread should receive SIGHUP so that it can close the log
+ * file and reopen it during log rotation. SIGUSR1 can be used to force
+ * a log rotation.
+ */
+ (void) sigset(SIGHUP, logstream_sighandler);
+ (void) sigset(SIGUSR1, logstream_sighandler);
+}
+
+/*
+ * Rotate a single log file. The global lock must be held while this is called.
+ */
+static void
+rotate_log(logfile_t *lfp)
+{
+ time_t t;
+ struct tm gtm;
+ char path[MAXPATHLEN];
+ int64_t i;
+ size_t len;
+ glob_t glb = { 0 };
+ int err;
+
+ VERIFY(MUTEX_HELD(&logging_lock));
+
+ if (lfp->lf_closing) {
+ return;
+ }
+
+ if ((t = time(NULL)) == (time_t)-1 || gmtime_r(&t, &gtm) == NULL) {
+ logstream_err(B_TRUE, "failed to format time");
+ return;
+ }
+
+ (void) snprintf(path, sizeof (path), "%s.%04d%02d%02dT%02d%02d%02dZ",
+ lfp->lf_path, gtm.tm_year + 1900, gtm.tm_mon + 1, gtm.tm_mday,
+ gtm.tm_hour, gtm.tm_min, gtm.tm_sec);
+
+ if (rename(lfp->lf_path, path) != 0) {
+ logstream_err(B_TRUE, "failed to rotate log file "
+ "'%s' to '%s'", lfp->lf_path, path);
+ }
+
+ close_log(lfp, "close-rotate", B_FALSE);
+ open_log(lfp, "open-rotate");
+
+ if (logging_rot_keep == 0) {
+ return;
+ }
+
+ /*
+ * Remove old logs.
+ */
+ len = snprintf(path, sizeof (path),
+ /* <lf_path>.YYYYmmdd */
+ "%s.[12][0-9][0-9][0-9][01][0-9][0-3][0-9]"
+ /* THHMMSSZ */
+ "T[012][0-9][0-5][0-9][0-6][0-9]Z", lfp->lf_path);
+ if (len >= sizeof (path)) {
+ logstream_err(B_FALSE, "log rotation glob too long");
+ return;
+ }
+
+ if ((err = glob(path, GLOB_LIMIT, NULL, &glb)) != 0) {
+ if (err != GLOB_NOMATCH) {
+ logstream_err(B_TRUE, "glob terminated with error %d",
+ err);
+ }
+ globfree(&glb);
+ return;
+ }
+
+ if (glb.gl_pathc <= logging_rot_keep) {
+ globfree(&glb);
+ return;
+ }
+
+ for (i = glb.gl_pathc - logging_rot_keep - 1; i >= 0; i--) {
+ if (unlink(glb.gl_pathv[i]) != 0) {
+ logstream_err(B_TRUE, "log rotation could not remove "
+ "%s", glb.gl_pathv[i]);
+ }
+ }
+ globfree(&glb);
+}
+
+/*
+ * Modify the input string with json escapes. Since the destination can thus
+ * be larger than the source, multiple calls may be required to fully convert
+ * sbuf to json.
+ *
+ * sbuf, slen Source buffer and the number of bytes in it to process
+ * dest Destination custr_t containing escaped JSON.
+ * scntp On return, *scntp stores number of scnt bytes consumed
+ * flushp If non-NULL, line-buffered mode is enabled. Processing
+ * will stop at the first newline or when dest is full and
+ * *flushp will be set to B_TRUE.
+ *
+ * This function makes no attempt to handle wide characters properly because
+ * the messages that come in may be using any character encoding. Since
+ * characters other than 7-bit ASCII are not directly readable in the log
+ * anyway, it is better to log the raw data and leave it to specialized log
+ * readers to interpret non-ASCII data.
+ */
+static void
+escape_json(const char *sbuf, size_t slen, custr_t *dest, size_t *scntp,
+ boolean_t *flushp)
+{
+ char c;
+ const char *save_sbuf = sbuf;
+ const char *sbuf_end = sbuf + slen - 1;
+ char append_buf[7]; /* "\\u0000\0" */
+ const char *append;
+ int len;
+
+ if (slen == 0) {
+ *scntp = 0;
+ return;
+ }
+
+ if (flushp != NULL) {
+ *flushp = B_FALSE;
+ }
+
+ while (sbuf <= sbuf_end) {
+ c = sbuf[0];
+
+ switch (c) {
+ case '\\':
+ append = "\\\\";
+ break;
+
+ case '"':
+ append = "\\\"";
+ break;
+
+ case '\b':
+ append = "\\b";
+ break;
+
+ case '\f':
+ append = "\\f";
+ break;
+
+ case '\n':
+ append = "\\n";
+ if (flushp != NULL) {
+ *flushp = B_TRUE;
+ }
+ break;
+
+ case '\r':
+ append = "\\r";
+ break;
+
+ case '\t':
+ append = "\\t";
+ break;
+
+ default:
+ if (c >= 0x20 && c < 0x7f) {
+ append_buf[0] = c;
+ append_buf[1] = '\0';
+ } else {
+ len = snprintf(append_buf, sizeof (append_buf),
+ "\\u%04x", (int)(0xff & c));
+ VERIFY3S(len, <, sizeof (append_buf));
+ }
+ append = append_buf;
+ break;
+ }
+
+ if (custr_append(dest, append) != 0) {
+ VERIFY3S(errno, ==, EOVERFLOW);
+ if (flushp != NULL) {
+ *flushp = B_TRUE;
+ }
+ break;
+ }
+
+ sbuf++;
+
+ if (flushp != NULL && *flushp) {
+ break;
+ }
+ }
+
+ *scntp = sbuf - save_sbuf;
+
+ VERIFY3U(*scntp, <=, slen);
+}
+
+/*
+ * Like write(2), but to a logfile_t and with retries on short writes.
+ */
+static void
+logfile_write(logfile_t *lfp, custr_t *cus)
+{
+ const char *buf = custr_cstr(cus);
+ size_t buflen = custr_len(cus);
+ ssize_t wlen;
+ size_t wanted = buflen;
+
+ while (buflen > 0) {
+ wlen = write(lfp->lf_fd, buf, buflen);
+ if (wlen == -1) {
+ if (lfp->lf_write_err) {
+ lfp->lf_write_err = B_TRUE;
+ logstream_err(B_TRUE, "log file fd %d '%s': "
+ "failed to write %llu of %llu bytes",
+ lfp->lf_fd, lfp->lf_path, buflen, wanted);
+ }
+ return;
+ }
+ buf += wlen;
+ buflen -= wlen;
+ lfp->lf_size += wlen;
+
+ lfp->lf_write_err = B_FALSE;
+ }
+
+ if (logging_rot_size != 0 && lfp->lf_size > logging_rot_size) {
+ rotate_log(lfp);
+ }
+}
+
+static void
+add_bunyan_preamble(custr_t *cus)
+{
+ struct tm gtm;
+ struct timeval tv;
+ /* Large enough for YYYY-MM-DDTHH:MM:SS.000000000Z + NUL */
+ char timestr[32] = { 0 };
+ size_t len;
+
+ if (gettimeofday(&tv, NULL) != 0 ||
+ gmtime_r(&tv.tv_sec, &gtm) == NULL) {
+ logstream_err(B_TRUE, "failed to get time of day");
+ abort();
+ }
+
+ len = strftime(timestr, sizeof (timestr) - 1, "%FT%T", &gtm);
+ VERIFY3U(len, >, 0);
+ VERIFY3U(len, <, sizeof (timestr) - 1);
+
+ VERIFY0(custr_append_printf(cus, "\"time\": \"%s.%09ldZ\", ",
+ timestr, tv.tv_usec * 1000));
+ VERIFY0(custr_append_printf(cus, "\"v\": %d, ", BUNYAN_VERSION));
+ VERIFY0(custr_append_printf(cus, "\"hostname\": \"%s\", ", host));
+ VERIFY0(custr_append(cus, "\"name\": \"zoneadmd\","));
+ VERIFY0(custr_append_printf(cus, "\"pid\": %s, ", pidstr));
+ VERIFY0(custr_append_printf(cus, "\"level\": %d", BUNYAN_LOG_LEVEL));
+}
+
+/*
+ * Convert the json pairs into a json object. The properties required for
+ * bunyan-formatted json objects are added to every object.
+ * Returns the number of bytes that would have been written to
+ * buf if bufsz had buf been sufficiently large (excluding the terminating null
+ * byte). Like snprintf().
+ */
+static size_t
+make_json(jsonpair_t *pairs, size_t npairs, custr_t *cus)
+{
+ int i;
+ const char *key, *val;
+ const char *start = ", ";
+
+ VERIFY3S(npairs, >, 0);
+
+ custr_reset(cus);
+
+ VERIFY0(custr_append(cus, "{ "));
+
+ add_bunyan_preamble(cus);
+
+ for (i = 0; i < npairs; i++) {
+ size_t len;
+
+ key = pairs[i].jp_key;
+ val = pairs[i].jp_val;
+
+ /* The total number of bytes we're adding to cus */
+ len = 3 + strlen(key) + 3 + strlen(val) + 1;
+ if (custr_append_printf(cus, "%s\"%s\":\"%s\"",
+ start, key, val) != 0) {
+ VERIFY3S(errno, ==, EOVERFLOW);
+ return (custr_len(cus) + len);
+ }
+ }
+
+ if (custr_append(cus, " }\n") != 0) {
+ return (custr_len(cus) + 3);
+ }
+
+ return (custr_len(cus));
+}
+
+static void
+logstream_write_json(logstream_t *lsp)
+{
+ size_t len;
+ jsonpair_t pairs[] = {
+ { "msg", lsp->ls_buf },
+ { "stream", lsp->ls_stream },
+ };
+
+ if (custr_len(lsp->ls_cusbuf) == 0) {
+ return;
+ }
+
+ len = make_json(pairs, ARRAY_SIZE(pairs), lsp->ls_cusobuf);
+
+ custr_reset(lsp->ls_cusbuf);
+ if (len >= sizeof (lsp->ls_obuf)) {
+ logstream_err(B_FALSE, "%s: buffer too small. Need %llu bytes, "
+ "have %llu bytes", __func__, len + 1,
+ sizeof (lsp->ls_obuf));
+ return;
+ }
+
+ logfile_write(lsp->ls_logfile, lsp->ls_cusobuf);
+}
+
+/*
+ * 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 compatibility.
+ *
+ * We keep track of the size of the log file and rotate it when we exceed
+ * the log size limit (if one is set).
+ */
+void
+logstream_write(int ls, char *buf, int len)
+{
+ logstream_t *lsp;
+ size_t scnt;
+ boolean_t newline;
+ boolean_t buffered;
+
+ if (ls == -1 || len == 0) {
+ return;
+ }
+ VERIFY3S(ls, >=, 0);
+ VERIFY3S(ls, <, ARRAY_SIZE(streams));
+
+ logstream_lock();
+
+ lsp = &streams[ls];
+ if (lsp->ls_stream[0] == '\0' || lsp->ls_logfile == NULL) {
+ logstream_unlock();
+ return;
+ }
+
+ buffered = !!(lsp->ls_flags & LS_LINE_BUFFERED);
+
+ do {
+ escape_json(buf, len, lsp->ls_cusbuf, &scnt,
+ buffered ? &newline : NULL);
+
+ buf += scnt;
+ len -= scnt;
+
+ if (!buffered || newline) {
+ logstream_write_json(lsp);
+ }
+ } while (len > 0 && (!buffered || newline));
+
+ logstream_unlock();
+}
+
+static void
+logstream_flush(int ls)
+{
+ logstream_t *lsp;
+
+ VERIFY(MUTEX_HELD(&logging_lock));
+
+ lsp = &streams[ls];
+ if (lsp->ls_stream[0] == '\0' || lsp->ls_logfile == NULL) {
+ return;
+ }
+ logstream_write_json(lsp);
+}
+
+static void
+logstream_flush_all(logfile_t *lfp)
+{
+ int i;
+
+ VERIFY(MUTEX_HELD(&logging_lock));
+
+ for (i = 0; i < ARRAY_SIZE(streams); i++) {
+ if (streams[i].ls_logfile == lfp) {
+ logstream_flush(i);
+ }
+ }
+}
+
+int
+logstream_open(const char *logname, const char *stream, logstream_flags_t flags)
+{
+ int ls = -1;
+ int i;
+ logstream_t *lsp;
+ logfile_t *lfp = NULL;
+
+ VERIFY3U(strlen(logname), <, sizeof (lfp->lf_name));
+ VERIFY3U(strlen(stream), <, sizeof (lsp->ls_stream));
+
+ logstream_lock();
+
+ /*
+ * Find an empty logstream_t and verify that the stream is not already
+ * open.
+ */
+ for (i = 0; i < ARRAY_SIZE(streams); i++) {
+ if (ls == -1 && streams[i].ls_stream[0] == '\0') {
+ VERIFY3P(streams[i].ls_logfile, ==, NULL);
+ ls = i;
+ continue;
+ }
+ if (strcmp(stream, streams[i].ls_stream) == 0) {
+ logstream_unlock();
+ logstream_err(B_FALSE, "log stream %s already open",
+ stream);
+ return (-1);
+ }
+ }
+ VERIFY3S(ls, !=, -1);
+
+ /* Find an existing or available logfile_t */
+ for (i = 0; i < ARRAY_SIZE(logfiles); i++) {
+ if (lfp == NULL && logfiles[i].lf_name[0] == '\0') {
+ lfp = &logfiles[i];
+ }
+ if (strcmp(logname, logfiles[i].lf_name) == 0) {
+ lfp = &logfiles[i];
+ break;
+ }
+ }
+ if (lfp->lf_name[0] == '\0') {
+ (void) strlcpy(lfp->lf_name, logname, sizeof (lfp->lf_name));
+ (void) snprintf(lfp->lf_path, sizeof (lfp->lf_path), "%s/logs",
+ zonepath);
+ (void) mkdir(lfp->lf_path, 0700);
+
+ (void) snprintf(lfp->lf_path, sizeof (lfp->lf_path),
+ "%s/logs/%s", zonepath, logname);
+
+ open_log(lfp, "open");
+ if (lfp->lf_fd == -1) {
+ logstream_unlock();
+ return (-1);
+ }
+ }
+
+ lsp = &streams[ls];
+ (void) strlcpy(lsp->ls_stream, stream, sizeof (lsp->ls_stream));
+
+ lsp->ls_flags = flags;
+ lsp->ls_logfile = lfp;
+
+ logstream_unlock();
+
+ return (ls);
+}
+
+static void
+logstream_reset(logstream_t *lsp)
+{
+ custr_t *buf = lsp->ls_cusbuf;
+ custr_t *obuf = lsp->ls_cusobuf;
+
+ (void) memset(lsp, 0, sizeof (*lsp));
+ lsp->ls_cusbuf = buf;
+ lsp->ls_cusobuf = obuf;
+
+ custr_reset(buf);
+ custr_reset(obuf);
+}
+
+static void
+logfile_reset(logfile_t *lfp)
+{
+ custr_t *buf = lfp->lf_cus;
+
+ (void) memset(lfp, 0, sizeof (*lfp));
+ lfp->lf_cus = buf;
+ lfp->lf_fd = -1;
+
+ custr_reset(buf);
+}
+
+void
+logstream_close(int ls, boolean_t abrupt)
+{
+ logstream_t *lsp;
+ logfile_t *lfp;
+ int i;
+
+ if (ls == -1) {
+ return;
+ }
+ VERIFY3S(ls, >=, 0);
+ VERIFY3S(ls, <, ARRAY_SIZE(streams));
+
+ logstream_lock();
+ logstream_flush(ls);
+
+ lsp = &streams[ls];
+ lfp = lsp->ls_logfile;
+
+ VERIFY(lsp->ls_stream[0] != '\0');
+ VERIFY3P(lfp, !=, NULL);
+
+ logstream_reset(lsp);
+
+ for (i = 0; i < ARRAY_SIZE(streams); i++) {
+ if (streams[i].ls_logfile == lfp) {
+ logstream_unlock();
+ return;
+ }
+ }
+
+ /* No more streams using this log file so return to initial state */
+
+ close_log(lfp, "close", abrupt);
+
+ logfile_reset(lfp);
+
+ logstream_unlock();
+}
diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c
index cbba769f94..0ace033738 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 2016, Joyent Inc.
+ * Copyright 2018, Joyent Inc.
* Copyright (c) 2015, 2016 by Delphix. All rights reserved.
* Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
* Copyright 2020 RackTop Systems Inc.
@@ -80,10 +80,12 @@
#include <sys/conf.h>
#include <sys/systeminfo.h>
#include <sys/secflags.h>
+#include <sys/vnic.h>
#include <libdlpi.h>
#include <libdllink.h>
#include <libdlvlan.h>
+#include <libdlvnic.h>
#include <inet/tcp.h>
#include <arpa/inet.h>
@@ -139,6 +141,9 @@
#define DFSTYPES "/etc/dfs/fstypes"
#define MAXTNZLEN 2048
+/* Number of times to retry unmounting if it fails */
+#define UMOUNT_RETRIES 30
+
#define ALT_MOUNT(mount_cmd) ((mount_cmd) != Z_MNT_BOOT)
/* a reasonable estimate for the number of lwps per process */
@@ -164,11 +169,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
@@ -209,7 +228,7 @@ autofs_cleanup(zoneid_t zoneid)
* Handle ENOSYS in the case that the autofs kernel module is not
* installed.
*/
- r = _autofssys(AUTOFS_UNMOUNTALL, (void *)zoneid);
+ r = _autofssys(AUTOFS_UNMOUNTALL, (void *)((uintptr_t)zoneid));
if (r != 0 && errno == ENOSYS) {
return (0);
}
@@ -604,6 +623,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
@@ -636,6 +673,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;
@@ -723,18 +761,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);
+ }
}
}
/*
@@ -1072,23 +1131,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);
}
@@ -1101,14 +1147,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");
@@ -1146,6 +1191,8 @@ mount_one_dev(zlog_t *zlogp, char *devpath, zone_mnt_t mount_cmd)
zerror(zlogp, B_FALSE, "bad ip-type");
goto cleanup;
}
+ if (curr_iptype == NULL)
+ abort();
if (brand_platform_iter_devices(bh, zone_name,
mount_one_dev_device_cb, prof, curr_iptype) != 0) {
@@ -1160,28 +1207,25 @@ 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)) != 0) {
- zerror(zlogp, B_FALSE, "can't get handle for zone "
- "%s: %s", zone_name, zonecfg_strerror(err));
- goto cleanup;
- }
- if ((err = zonecfg_setdevent(handle)) != 0) {
+ 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) {
- if (di_prof_add_dev(prof, ztab.zone_dev_match)) {
+ while (zonecfg_getdevent(snap_hndl, &ztab) == Z_OK) {
+ char path[MAXPATHLEN];
+
+ if ((err = resolve_device_match(zlogp, &ztab,
+ path, sizeof (path))) != Z_OK)
+ goto cleanup;
+
+ if (di_prof_add_dev(prof, path)) {
zerror(zlogp, B_TRUE, "failed to add "
- "user-specified device");
+ "user-specified device '%s'", path);
goto cleanup;
}
}
- (void) zonecfg_enddevent(handle);
+ (void) zonecfg_enddevent(snap_hndl);
/* Send profile to kernel */
if (di_prof_commit(prof)) {
@@ -1194,8 +1238,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);
@@ -1686,12 +1728,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;
@@ -1705,22 +1745,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;
}
@@ -1738,7 +1768,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);
}
@@ -1753,7 +1782,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);
@@ -1764,13 +1792,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
@@ -1810,23 +1835,40 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd)
qsort(fs_ptr, num_fs, sizeof (*fs_ptr), fs_compare);
for (i = 0; i < num_fs; i++) {
- if (ALT_MOUNT(mount_cmd) &&
- strcmp(fs_ptr[i].zone_fs_dir, "/dev") == 0) {
- size_t slen = strlen(rootpath) - 2;
+ if (ALT_MOUNT(mount_cmd)) {
+ if (strcmp(fs_ptr[i].zone_fs_dir, "/dev") == 0) {
+ size_t slen = strlen(rootpath) - 2;
- /*
- * By default we'll try to mount /dev as /a/dev
- * but /dev is special and always goes at the top
- * so strip the trailing '/a' from the rootpath.
- */
- assert(strcmp(&rootpath[slen], "/a") == 0);
- rootpath[slen] = '\0';
- if (mount_one(zlogp, &fs_ptr[i], rootpath, mount_cmd)
- != 0)
- goto bad;
- rootpath[slen] = '/';
- continue;
+ /*
+ * By default we'll try to mount /dev
+ * as /a/dev but /dev is special and
+ * always goes at the top so strip the
+ * trailing '/a' from the rootpath.
+ */
+ assert(strcmp(&rootpath[slen], "/a") == 0);
+ rootpath[slen] = '\0';
+ if (mount_one(zlogp, &fs_ptr[i], rootpath,
+ mount_cmd) != 0)
+ goto bad;
+ rootpath[slen] = '/';
+ continue;
+ } else if (strcmp(brand_name, default_brand) != 0) {
+ /*
+ * If mounting non-native brand, skip
+ * mounting global mounts and
+ * filesystem entries since they are
+ * only needed for native pkg upgrade
+ * tools.
+ *
+ * The only exception right now is
+ * /dev (handled above), which is
+ * needed in the luroot in order to
+ * zlogin -S into the zone.
+ */
+ continue;
+ }
}
+
if (mount_one(zlogp, &fs_ptr[i], rootpath, mount_cmd) != 0)
goto bad;
}
@@ -1849,8 +1891,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);
}
@@ -2210,9 +2250,6 @@ configure_one_interface(zlog_t *zlogp, zoneid_t zone_id,
* the console by zoneadm(8) 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);
}
@@ -2425,7 +2462,6 @@ bad:
static int
configure_shared_network_interfaces(zlog_t *zlogp)
{
- zone_dochandle_t handle;
struct zone_nwiftab nwiftab, loopback_iftab;
zoneid_t zoneid;
@@ -2434,29 +2470,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
@@ -2910,7 +2936,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];
@@ -2919,30 +2944,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);
@@ -2950,8 +2963,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);
@@ -2975,17 +2987,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));
@@ -2995,16 +3007,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) {
@@ -3140,48 +3150,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)
{
@@ -3263,26 +3248,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);
}
@@ -3295,21 +3268,18 @@ get_privset(zlog_t *zlogp, priv_set_t *privs, zone_mnt_t mount_cmd)
break;
default:
zerror(zlogp, B_FALSE, "bad ip-type");
- zonecfg_fini_handle(handle);
return (-1);
}
- 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;
@@ -3332,10 +3302,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)
{
@@ -3345,7 +3327,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;
@@ -3355,16 +3336,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");
@@ -3379,17 +3350,17 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep)
* from max-processes. If only the max-lwps property is set, we add a
* max-processes property with the same limit as max-lwps.
*/
- rproc = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPROCS, &maxprocs);
- rlwp = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLWPS, &maxlwps);
+ rproc = zonecfg_get_aliased_rctl(snap_hndl, ALIAS_MAXPROCS, &maxprocs);
+ rlwp = zonecfg_get_aliased_rctl(snap_hndl, ALIAS_MAXLWPS, &maxlwps);
if (rproc == Z_OK && rlwp == 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;
}
} else if (rlwp == Z_OK && rproc == Z_NO_ENTRY) {
/* no scaling for max-proc value */
- if (zonecfg_set_aliased_rctl(handle, ALIAS_MAXPROCS,
+ if (zonecfg_set_aliased_rctl(snap_hndl, ALIAS_MAXPROCS,
maxlwps) != Z_OK) {
zerror(zlogp, B_FALSE,
"unable to set max-processes alias");
@@ -3397,7 +3368,7 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep)
}
}
- if (zonecfg_setrctlent(handle) != Z_OK) {
+ if (zonecfg_setrctlent(snap_hndl) != Z_OK) {
zerror(zlogp, B_FALSE, "%s failed", "zonecfg_setrctlent");
goto out;
}
@@ -3406,10 +3377,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)) {
@@ -3476,6 +3448,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",
@@ -3488,7 +3480,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;
@@ -3512,8 +3504,6 @@ out:
nvlist_free(nvl);
if (nvlv != NULL)
free(nvlv);
- if (handle != NULL)
- zonecfg_fini_handle(handle);
return (error);
}
@@ -3529,7 +3519,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);
@@ -3538,7 +3528,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;
@@ -3549,30 +3538,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);
@@ -3589,12 +3568,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);
@@ -3602,7 +3581,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);
@@ -3614,8 +3593,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);
@@ -3625,40 +3602,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);
}
@@ -3673,7 +3636,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);
@@ -3681,9 +3643,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);
@@ -3737,17 +3698,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);
@@ -4418,62 +4373,52 @@ 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)
+ if (strcmp(sched, "FX") == 0) {
+ /*
+ * When FX is specified then by default all processes
+ * will start at the lowest priority level (0) and
+ * stay there. We support an optional attr which
+ * indicates that all the processes should be "high
+ * priority". We set this on the zone so that starting
+ * init will set the priority high.
+ */
+ struct zone_attrtab a;
+
+ bzero(&a, sizeof (a));
+ (void) strlcpy(a.zone_attr_name, "fixed-hi-prio",
+ sizeof (a.zone_attr_name));
+
+ if (zonecfg_lookup_attr(snap_hndl, &a) == Z_OK &&
+ strcmp(a.zone_attr_value, "true") == 0) {
+ boolean_t hi = B_TRUE;
+
+ if (zone_setattr(zoneid,
+ ZONE_ATTR_SCHED_FIXEDHI, (void *)hi,
+ sizeof (hi)) == -1)
+ zerror(zlogp, B_TRUE, "WARNING: unable "
+ "to set high priority");
+ }
+ }
+
+ } 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
@@ -4484,7 +4429,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");
@@ -4517,7 +4462,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 "
@@ -4526,14 +4471,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"
@@ -4543,7 +4487,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 "
@@ -4557,10 +4501,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);
}
@@ -4787,36 +4730,31 @@ 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;
- if ((res = setup_zone_secflags(handle, zlogp, zoneid)) != Z_OK)
+ if ((res = setup_zone_secflags(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;
@@ -4832,7 +4770,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) {
@@ -4851,6 +4789,8 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd)
} else {
flags = 0;
}
+ if (flags == -1)
+ abort();
if ((privs = priv_allocset()) == NULL) {
zerror(zlogp, B_TRUE, "%s failed", "priv_allocset");
@@ -4954,7 +4894,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,
@@ -5000,7 +4940,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) {
@@ -5058,6 +4998,8 @@ error:
}
if (rctlbuf != NULL)
free(rctlbuf);
+ if (zfsbuf != NULL)
+ free(zfsbuf);
priv_freeset(privs);
if (fp != NULL)
zonecfg_close_scratch(fp);
@@ -5146,7 +5088,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();
@@ -5157,15 +5099,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);
}
@@ -5200,6 +5138,8 @@ vplat_bringup(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zoneid)
return (-1);
}
break;
+ default:
+ abort();
}
}
@@ -5274,14 +5214,88 @@ unmounted:
}
}
+/*
+ * Delete all transient VNICs belonging to this zone. A transient VNIC
+ * is one that is created and destroyed along with the lifetime of the
+ * zone. Non-transient VNICs, ones that are assigned from the GZ to a
+ * NGZ, are reassigned to the GZ in zone_shutdown() via the
+ * zone-specific data (zsd) callbacks.
+ */
+static int
+delete_transient_vnics(zlog_t *zlogp, zoneid_t zoneid)
+{
+ dladm_status_t status;
+ int num_links = 0;
+ datalink_id_t *links, link;
+ uint32_t link_flags;
+ datalink_class_t link_class;
+ char link_name[MAXLINKNAMELEN];
+
+ if (zone_list_datalink(zoneid, &num_links, NULL) != 0) {
+ zerror(zlogp, B_TRUE, "unable to determine "
+ "number of network interfaces");
+ return (-1);
+ }
+
+ if (num_links == 0)
+ return (0);
+
+ links = malloc(num_links * sizeof (datalink_id_t));
+
+ if (links == NULL) {
+ zerror(zlogp, B_TRUE, "failed to delete "
+ "network interfaces because of alloc fail");
+ return (-1);
+ }
+
+ if (zone_list_datalink(zoneid, &num_links, links) != 0) {
+ zerror(zlogp, B_TRUE, "failed to delete "
+ "network interfaces because of failure "
+ "to list them");
+ return (-1);
+ }
+
+ for (int i = 0; i < num_links; i++) {
+ char dlerr[DLADM_STRSIZE];
+ link = links[i];
+
+ status = dladm_datalink_id2info(dld_handle, link, &link_flags,
+ &link_class, NULL, link_name, sizeof (link_name));
+
+ if (status != DLADM_STATUS_OK) {
+ zerror(zlogp, B_FALSE, "failed to "
+ "delete network interface (%u)"
+ "due to failure to get link info: %s",
+ link,
+ dladm_status2str(status, dlerr));
+ return (-1);
+ }
+
+ if (link_flags & DLADM_OPT_TRANSIENT) {
+ assert(link_class & DATALINK_CLASS_VNIC);
+ status = dladm_vnic_delete(dld_handle, link,
+ DLADM_OPT_ACTIVE);
+
+ if (status != DLADM_STATUS_OK) {
+ zerror(zlogp, B_TRUE, "failed to delete link "
+ "with id %d: %s", link,
+ dladm_status2str(status, dlerr));
+ return (-1);
+ }
+ }
+ }
+
+ return (0);
+}
+
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;
@@ -5314,16 +5328,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
@@ -5337,12 +5347,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");
@@ -5353,7 +5357,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 "
@@ -5363,7 +5367,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;
}
@@ -5395,17 +5399,18 @@ 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");
+ if (delete_transient_vnics(zlogp, zoneid) != 0) {
+ zerror(zlogp, B_FALSE, "unable to delete "
+ "transient vnics in zone");
goto error;
}
+
status = dladm_zone_halt(dld_handle, zoneid);
if (status != DLADM_STATUS_OK) {
zerror(zlogp, B_FALSE, "unable to notify "
"dlmgmtd of zone halt: %s",
dladm_status2str(status, errmsg));
+ goto error;
}
break;
}
@@ -5437,14 +5442,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 130b97d984..09a9f9ba8e 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 2019 Joyent, Inc.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
@@ -40,10 +40,10 @@
*
* Global Zone | Non-Global Zone
* .--------------. |
- * .-----------. | zoneadmd -z | | .--------. .---------.
- * | zlogin -C | | myzone | | | ttymon | | syslogd |
- * `-----------' `--------------' | `--------' `---------'
- * | | | | | | |
+ * .-----------. | zoneadmd -z |--. | .--------. .---------.
+ * | zlogin -C | | myzone | | | | ttymon | | syslogd |
+ * `-----------' `--------------' V | `--------' `---------'
+ * | | | | console.log | | |
* User | | | | | V V
* - - - - - - - - -|- - - -|- - - -|-|- - - - - - -|- - /dev/zconsole - - -
* Kernel V V | | |
@@ -59,7 +59,7 @@
* V +-----------+
* +---manager--+-subsidiary+
* | |
- * | zcons driver |
+ * | Zcons driver |
* | zonename="myzone" |
* +------------------------+
*
@@ -81,6 +81,8 @@
* functions as a two-way proxy for console I/O, relaying user input
* to the manager side of the console, and relaying output from the
* zone to the user.
+ *
+ * - Logging output to <zonepath>/logs/console.log.
*/
#include <sys/types.h>
@@ -118,9 +120,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 +133,10 @@ char bad_boot_arg[BOOTARGS_MAX];
*/
static int eventstream[2];
-
+/* flag used to cope with race creating manager zcons devlink */
+static boolean_t manager_zcons_failed = B_FALSE;
+/* flag to track if we've seen a state change when there is no manager zcons */
+static boolean_t state_changed = B_FALSE;
int
eventstream_init()
@@ -323,7 +329,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 manager and subsidiary
* console devices) are placed in /dev in the global zone.
*/
@@ -410,45 +416,66 @@ devlinks:
* ioctl, which will cause the manager to retain a reference to the
* subsidiary. This prevents ttymon from blowing through the
* subsidiary'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_MANAGER_NAME);
- if ((managerfd = open(conspath, O_RDWR | O_NOCTTY)) == -1) {
+ for (i = 0; i < ZCONS_RETRY; i++) {
+ managerfd = open(conspath, O_RDWR | O_NOCTTY);
+ if (managerfd >= 0 || errno != ENOENT)
+ break;
+ (void) sleep(1);
+ }
+ if (managerfd == -1) {
zerror(zlogp, B_TRUE, "ERROR: could not open manager side of "
"zone console for %s to acquire subsidiary handle",
zone_name);
- goto error;
+ manager_zcons_failed = B_TRUE;
}
+
(void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
zone_name, ZCONS_SUBSIDIARY_NAME);
- if ((subfd = open(conspath, O_RDWR | O_NOCTTY)) == -1) {
+ for (i = 0; i < ZCONS_RETRY; i++) {
+ subfd = open(conspath, O_RDWR | O_NOCTTY);
+ if (subfd >= 0 || errno != ENOENT)
+ break;
+ (void) sleep(1);
+ }
+ if (subfd == -1)
zerror(zlogp, B_TRUE, "ERROR: could not open subsidiary side "
"of zone console for %s to acquire subsidiary handle",
zone_name);
- (void) close(managerfd);
- 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(managerfd, ZC_HOLDSUBSID, (caddr_t)(intptr_t)subfd)
- == 0) {
- rv = 0;
- break;
- } else if (errno != ENXIO) {
- break;
+ if (managerfd != -1 && subfd != -1) {
+ for (i = 0; i < ZCONS_RETRY; i++) {
+ if (ioctl(managerfd, ZC_HOLDSUBSID,
+ (caddr_t)(intptr_t)subfd) == 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 "
+ "subsidiary handle of zone console for %s",
+ zone_name);
}
- if (rv != 0)
- zerror(zlogp, B_TRUE, "ERROR: error while acquiring "
- "subsidiary handle of zone console for %s", zone_name);
- (void) close(subfd);
- (void) close(managerfd);
+ if (subfd != -1)
+ (void) close(subfd);
+ if (managerfd != -1)
+ (void) close(managerfd);
error:
if (ddef_hdl)
@@ -521,6 +548,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) {
@@ -554,18 +582,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++;
@@ -671,14 +703,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;
}
@@ -717,7 +741,7 @@ test_client(int clifd)
* messages) can be output in the user's locale.
*/
static void
-do_console_io(zlog_t *zlogp, int consfd, int servfd)
+do_console_io(zlog_t *zlogp, int consfd, int servfd, int conslog)
{
struct pollfd pollfds[4];
char ibuf[BUFSIZ];
@@ -763,14 +787,21 @@ do_console_io(zlog_t *zlogp, int consfd, int servfd)
(POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
errno = 0;
cc = read(consfd, ibuf, BUFSIZ);
- if (cc <= 0 && (errno != EINTR) &&
- (errno != EAGAIN))
- break;
- /*
- * Lose I/O if no one is listening
- */
- if (clifd != -1 && cc > 0)
- (void) write(clifd, ibuf, cc);
+ if (cc <= 0) {
+ if (errno != EINTR &&
+ errno != EAGAIN) {
+ break;
+ }
+ } else {
+ logstream_write(conslog, ibuf, cc);
+
+ /*
+ * Lose I/O if no one is listening
+ */
+ if (clifd != -1) {
+ (void) write(clifd, ibuf, cc);
+ }
+ }
} else {
pollerr = pollfds[0].revents;
zerror(zlogp, B_FALSE,
@@ -882,7 +913,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) {
@@ -894,6 +924,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.
@@ -911,6 +952,10 @@ serve_console(zlog_t *zlogp)
int managerfd;
zone_state_t zstate;
char conspath[MAXPATHLEN];
+ static boolean_t cons_warned = B_FALSE;
+ int conslog;
+
+ conslog = logstream_open("console.log", "console", LS_LINE_BUFFERED);
(void) snprintf(conspath, sizeof (conspath),
"/dev/zcons/%s/%s", zone_name, ZCONS_MANAGER_NAME);
@@ -918,6 +963,46 @@ serve_console(zlog_t *zlogp)
for (;;) {
managerfd = open(conspath, O_RDWR|O_NONBLOCK|O_NOCTTY);
if (managerfd == -1) {
+ if (manager_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 manager");
(void) mutex_lock(&lock);
goto death;
@@ -937,7 +1022,7 @@ serve_console(zlog_t *zlogp)
goto death;
}
- do_console_io(zlogp, managerfd, serverfd);
+ do_console_io(zlogp, managerfd, serverfd, conslog);
/*
* We would prefer not to do this, but hostile zone processes
@@ -978,4 +1063,6 @@ death:
destroy_console_sock(serverfd);
(void) destroy_console_devs(zlogp);
+
+ logstream_close(conslog, B_FALSE);
}
diff --git a/usr/src/cmd/zoneadmd/zfd.c b/usr/src/cmd/zoneadmd/zfd.c
new file mode 100644
index 0000000000..6647ef0c5f
--- /dev/null
+++ b/usr/src/cmd/zoneadmd/zfd.c
@@ -0,0 +1,1238 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+/*
+ * Zone file descriptor support is used as a mechanism for a process inside the
+ * zone to log messages to the GZ zoneadmd and also 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.
+ *
+ * The mode, which is controlled by the zone attribute "zlog-mode" is somewhat
+ * of a misnomer since its purpose has evolved. The attribute currently
+ * can have six values which are used to control:
+ * - how the zfd devices are used inside the zone
+ * - if the output on the device(s) is also teed into another stream within
+ * the zone
+ * - if we do logging in the GZ
+ * See the comment on get_mode_logmax() in this file, and the comment in
+ * uts/common/io/zfd.c for more details.
+ *
+ * Internally the zfd_mode_t struct holds the number of stdio devs (1 or 3),
+ * the number of additional devs corresponding to the zone attr value and the
+ * GZ logging flag.
+ *
+ * Note that although the mode indicates the number of devices needed, we always
+ * create all possible zfd devices for simplicity.
+ */
+
+#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 int shutting_down = 0;
+static thread_t logger_tid;
+static char log_name[MAXNAMELEN] = "stdio.log";
+
+/*
+ * 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 ZLOG_MODE "zlog-mode"
+#define ZLOG_NAME "zlog-name"
+#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
+
+#define NUM_ZFD_DEVS 5
+
+typedef struct zfd_mode {
+ uint_t zmode_n_stddevs;
+ uint_t zmode_n_addl_devs;
+ boolean_t zmode_gzlogging;
+} zfd_mode_t;
+static zfd_mode_t mode;
+
+/*
+ * cb_data is only used by destroy_cb.
+ */
+struct cb_data {
+ zlog_t *zlogp;
+ int killed;
+};
+
+/*
+ * 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);
+ }
+
+ 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 all of 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 name of the zone, the dev name
+ * seen inside the zone, a flag which lets pseudo know that it is OK to
+ * automatically allocate an instance # for this device, and the last
+ * one 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, zfd_mode_t *mode)
+{
+ devctl_hdl_t bus_hdl = NULL;
+ di_devlink_handle_t dl = NULL;
+ int rv = -1;
+ int i;
+
+ /*
+ * 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 < NUM_ZFD_DEVS; i++) {
+ if (init_zfd_dev(zlogp, bus_hdl, i) != 0)
+ goto error;
+ }
+
+ 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->zmode_n_stddevs == 1) {
+ /* We want the primary stream to look like a tty. */
+ make_tty(zlogp, 0);
+ }
+
+error:
+ if (bus_hdl)
+ devctl_release(bus_hdl);
+ return (rv);
+}
+
+static int
+init_server_sock(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(&logplat, 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(&logplat, B_TRUE,
+ "server setup: could not bind to socket");
+ goto err;
+ }
+
+ if (listen(resfd, 4) == -1) {
+ zerror(&logplat, 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] = '\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);
+}
+
+/*
+ * 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, int logout, int logerr)
+{
+ 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(&logplat, 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(&logplat, 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(&logplat, 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);
+ /* zfd is a stream, so ignore 0 length read */
+ if (cc < 0 && (errno != EINTR) &&
+ (errno != EAGAIN))
+ break;
+ if (cc > 0) {
+ logstream_write(logout, ibuf, cc);
+
+ /*
+ * 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(&logplat, 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);
+ /* zfd is a stream, so ignore 0 length read */
+ if (cc < 0 && (errno != EINTR) &&
+ (errno != EAGAIN))
+ break;
+ if (cc > 0) {
+ logstream_write(logerr, ibuf, cc);
+
+ /*
+ * 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(&logplat, 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(&logplat, 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(&logplat, 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(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(&logplat, B_TRUE,
+ "failed to set options on zfd");
+ return (-1);
+ }
+ return (fd);
+ }
+
+ if (retried++ > 60)
+ break;
+
+ (void) sleep(1);
+ }
+
+ zerror(&logplat, B_TRUE, "failed to open zfd");
+ return (-1);
+}
+
+/*
+ * 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)
+{
+ zfd_mode_t *mode = (zfd_mode_t *)modearg;
+ int gzctlfd = -1;
+ int gzoutfd = -1;
+ int stdinfd = -1;
+ int stdoutfd = -1;
+ int gzerrfd = -1;
+ int stderrfd = -1;
+ int flags;
+ int len;
+ char ibuf[BUFSIZ + 1];
+ int logout = -1;
+ int logerr = -1;
+
+ if (!shutting_down && mode->zmode_gzlogging) {
+ logout = logstream_open(log_name, "stdout", 0);
+ logerr = logstream_open(log_name, "stderr", 0);
+ }
+
+ if (!shutting_down) {
+ if (pipe(eventstream) != 0) {
+ zerror(&logplat, B_TRUE, "failed to open logger "
+ "control pipe");
+ return (NULL);
+ }
+ }
+
+ while (!shutting_down) {
+ if (init_server_sock(&gzctlfd, "ctl") == -1) {
+ zerror(&logplat, B_FALSE,
+ "server setup: control socket init failed");
+ goto death;
+ }
+ if (init_server_sock(&gzoutfd, "out") == -1) {
+ zerror(&logplat, B_FALSE,
+ "server setup: stdout socket init failed");
+ goto death;
+ }
+ if (init_server_sock(&gzerrfd, "err") == -1) {
+ zerror(&logplat, B_FALSE,
+ "server setup: stderr socket init failed");
+ goto death;
+ }
+
+ if (mode->zmode_n_stddevs == 1) {
+ if ((stdinfd = open_fd(0, O_RDWR)) == -1) {
+ goto death;
+ }
+ stdoutfd = stdinfd;
+ } else {
+ if ((stdinfd = open_fd(0, O_WRONLY)) == -1 ||
+ (stdoutfd = open_fd(1, O_RDONLY)) == -1 ||
+ (stderrfd = open_fd(2, O_RDONLY)) == -1) {
+ goto death;
+ }
+ }
+
+ do_zfd_io(gzctlfd, gzoutfd, gzerrfd, stdinfd, stdoutfd,
+ stderrfd, logout, logerr);
+death:
+ destroy_server_sock(gzctlfd, "ctl");
+ destroy_server_sock(gzoutfd, "out");
+ destroy_server_sock(gzerrfd, "err");
+
+ /* when shutting down, leave open until drained */
+ if (!shutting_down) {
+ (void) close(stdinfd);
+ if (mode->zmode_n_stddevs == 3) {
+ (void) close(stdoutfd);
+ (void) close(stderrfd);
+ }
+ }
+ }
+
+ /*
+ * Attempt to drain remaining log output from the zone prior to closing
+ * the file descriptors. This helps ensure that complete logs are
+ * captured during shutdown.
+ */
+ flags = fcntl(stdoutfd, F_GETFL, 0);
+ if (fcntl(stdoutfd, F_SETFL, flags | O_NONBLOCK) != -1) {
+ while ((len = read(stdoutfd, ibuf, BUFSIZ)) > 0) {
+ logstream_write(logout, ibuf, len);
+ }
+ }
+ (void) close(stdoutfd);
+
+ if (mode->zmode_n_stddevs > 1) {
+ (void) close(stdinfd);
+ flags = fcntl(stderrfd, F_GETFL, 0);
+ if (fcntl(stderrfd, F_SETFL, flags | O_NONBLOCK) != -1) {
+ while ((len = read(stderrfd, ibuf, BUFSIZ)) > 0) {
+ logstream_write(logerr, ibuf, len);
+ }
+ }
+ (void) close(stderrfd);
+ }
+
+
+ (void) close(eventstream[0]);
+ eventstream[0] = -1;
+ (void) close(eventstream[1]);
+ eventstream[1] = -1;
+ logstream_close(logout, B_FALSE);
+ logstream_close(logerr, B_FALSE);
+ return (NULL);
+}
+
+/*
+ * The meaning of the original legacy values for the zlog-mode evolved over
+ * time, to the point where the old names no longer made sense. The current
+ * values are simply positional letters used to indicate various capabilities.
+ * The following table shows the meaning of the mode values, along with the
+ * legacy name which we continue to support for compatability. Any future
+ * capability can add a letter to the left and '-' is implied for existing
+ * strings.
+ *
+ * zlog-mode gz log - tty - ngz log
+ * --------- ------ --- -------
+ * gt- (int) y y n
+ * g-- (log) y n n
+ * gtn (nlint) y y y
+ * g-n (nolog) y n y
+ * -t- n y n
+ * --- n n n
+ *
+ * This function also obtains any custom name for stdio.log while it is reading
+ * the zone configuration.
+ */
+static void
+get_mode_logmax(zfd_mode_t *mode)
+{
+ zone_dochandle_t handle;
+ struct zone_attrtab attr;
+
+ bzero(mode, sizeof (zfd_mode_t));
+
+ if ((handle = zonecfg_init_handle()) == NULL)
+ return;
+
+ 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 (strcmp("g--", attr.zone_attr_value) == 0 ||
+ strncmp("log", attr.zone_attr_value, 3) == 0) {
+ mode->zmode_gzlogging = B_TRUE;
+ mode->zmode_n_stddevs = 3;
+ mode->zmode_n_addl_devs = 0;
+ } else if (strcmp("g-n", attr.zone_attr_value) == 0 ||
+ strncmp("nolog", attr.zone_attr_value, 5) == 0) {
+ mode->zmode_gzlogging = B_TRUE;
+ mode->zmode_n_stddevs = 3;
+ mode->zmode_n_addl_devs = 2;
+ } else if (strcmp("gt-", attr.zone_attr_value) == 0 ||
+ strncmp("int", attr.zone_attr_value, 3) == 0) {
+ mode->zmode_gzlogging = B_TRUE;
+ mode->zmode_n_stddevs = 1;
+ mode->zmode_n_addl_devs = 0;
+ } else if (strcmp("gtn", attr.zone_attr_value) == 0 ||
+ strncmp("nlint", attr.zone_attr_value, 5) == 0) {
+ mode->zmode_gzlogging = B_TRUE;
+ mode->zmode_n_stddevs = 1;
+ mode->zmode_n_addl_devs = 1;
+ } else if (strcmp("-t-", attr.zone_attr_value) == 0) {
+ mode->zmode_gzlogging = B_FALSE;
+ mode->zmode_n_stddevs = 1;
+ mode->zmode_n_addl_devs = 0;
+ } else if (strcmp("---", attr.zone_attr_value) == 0) {
+ mode->zmode_gzlogging = B_FALSE;
+ mode->zmode_n_stddevs = 3;
+ mode->zmode_n_addl_devs = 0;
+ }
+ continue;
+ }
+
+ if (strcmp(ZLOG_NAME, attr.zone_attr_name) == 0) {
+ (void) strlcpy(log_name, attr.zone_attr_value,
+ sizeof (log_name));
+ continue;
+ }
+ }
+ (void) zonecfg_endattrent(handle);
+
+done:
+ zonecfg_fini_handle(handle);
+}
+
+void
+create_log_thread(zlog_t *zlogp)
+{
+ int res;
+
+ shutting_down = 0;
+
+ get_mode_logmax(&mode);
+ if (mode.zmode_n_stddevs == 0)
+ return;
+
+ if (init_zfd_devs(zlogp, &mode) == -1) {
+ zerror(zlogp, B_FALSE,
+ "zfd setup: device initialization failed");
+ return;
+ }
+
+ res = thr_create(NULL, 0, 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(zlog_t *zlogp)
+{
+ 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 b1c2d2bbf5..342b1bf958 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 2021 Joyent, Inc.
* Copyright (c) 2016 by Delphix. All rights reserved.
*/
@@ -69,6 +70,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>
@@ -102,6 +104,8 @@
#include <libdladm.h>
#include <sys/dls_mgmt.h>
#include <libscf.h>
+#include <uuid/uuid.h>
+#include <libppt.h>
#include <libzonecfg.h>
#include <zonestat_impl.h>
@@ -109,6 +113,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];
@@ -117,13 +123,15 @@ 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;
+zlog_t logsys; /* log to syslog */
+zlog_t logplat; /* log to platform.log */
mutex_t lock = DEFAULTMUTEX; /* to serialize stuff */
mutex_t msglock = DEFAULTMUTEX; /* for calling setlocale() */
@@ -136,12 +144,17 @@ static int zone_door = -1;
boolean_t in_death_throes = B_FALSE; /* daemon is dying */
boolean_t bringup_failure_recovery = B_FALSE; /* ignore certain failures */
+static int platloghdl = -1; /* Handle for <zonepath>/logs/platform.log */
+
#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
#endif
#define DEFAULT_LOCALE "C"
+#define RSRC_NET "net"
+#define RSRC_DEV "device"
+
static const char *
z_cmd_name(zone_cmd_t zcmd)
{
@@ -215,17 +228,14 @@ zerror(zlog_t *zlogp, boolean_t use_strerror, const char *fmt, ...)
{
va_list alist;
char buf[MAXPATHLEN * 2]; /* enough space for err msg with a path */
- char *bp;
+ char *bp, *bp_nozone;
int saved_errno = errno;
- if (zlogp == NULL)
- return;
if (zlogp == &logsys)
- (void) snprintf(buf, sizeof (buf), "[zone '%s'] ",
- zone_name);
+ (void) snprintf(buf, sizeof (buf), "[zone '%s'] ", zone_name);
else
buf[0] = '\0';
- bp = &(buf[strlen(buf)]);
+ bp = bp_nozone = &(buf[strlen(buf)]);
/*
* In theory, the locale pointer should be set to either "C" or a
@@ -242,15 +252,38 @@ zerror(zlog_t *zlogp, boolean_t use_strerror, const char *fmt, ...)
if (use_strerror)
(void) snprintf(bp, sizeof (buf) - (bp - buf), ": %s",
strerror(saved_errno));
+
+ (void) strlcat(buf, "\n", sizeof (buf));
+
+ /*
+ * If we don't have the platform log, we are in a child process, and
+ * should log to stderr (which is a pipe) instead of the file.
+ */
+ if (logging_poisoned) {
+ (void) fprintf(stderr, "%s", buf);
+
+ if (zlogp != &logsys && zlogp->logfile == stderr)
+ return;
+ } else {
+ logstream_write(platloghdl, bp_nozone, strlen(bp_nozone));
+
+ if (zlogp == &logplat)
+ return;
+ }
+
if (zlogp == &logsys) {
+ bp = strrchr(buf, '\n');
+ if (bp != NULL && bp[1] == '\0') {
+ *bp = '\0';
+ }
(void) syslog(LOG_ERR, "%s", buf);
} else if (zlogp->logfile != NULL) {
- (void) fprintf(zlogp->logfile, "%s\n", buf);
+ (void) fprintf(zlogp->logfile, "%s", buf);
} else {
size_t buflen;
size_t copylen;
- buflen = snprintf(zlogp->log, zlogp->loglen, "%s\n", buf);
+ buflen = snprintf(zlogp->log, zlogp->loglen, "%s", buf);
copylen = MIN(buflen, zlogp->loglen);
zlogp->log += copylen;
zlogp->loglen -= copylen;
@@ -258,34 +291,58 @@ zerror(zlog_t *zlogp, boolean_t use_strerror, const char *fmt, ...)
}
/*
+ * Append src to dest, modifying dest in the process. Prefix src with
+ * a space character if dest is a non-empty string. Assumes dest is already
+ * properly \0-terminated OR overruns destsize.
+ */
+static void
+strnappend(char *dest, size_t destsize, const char *src)
+{
+ size_t startpoint = strnlen(dest, destsize);
+
+ if (startpoint >= destsize - 1) {
+ /* We've run out of room. Record something?! */
+ return;
+ }
+
+ if (startpoint > 0) {
+ /* Add the space per the function's intro comment. */
+ dest[startpoint] = ' ';
+ startpoint++;
+ }
+
+ /* Arguably we should check here too... */
+ (void) strlcpy(dest + startpoint, src, destsize - startpoint);
+}
+
+/*
* Emit a warning for any boot arguments which are unrecognized. Since
* Solaris boot arguments are getopt(3c) compatible (see kernel(8)), 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-native 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 scratchopt[3];
char c;
bzero(outargs, BOOTARGS_MAX);
- bzero(badarg, BOOTARGS_MAX);
/*
* If the user didn't specify transient boot arguments, check
@@ -293,25 +350,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) {
@@ -348,14 +390,22 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs,
}
/*
- * We preserve compatibility with the Solaris system boot behavior,
+ * We preserve compatibility with the illumos system boot behavior,
* which allows:
*
* # reboot kernel/unix -s -m verbose
*
- * In this example, kernel/unix tells the booter what file to
- * boot. We don't want reboot in a zone to be gratuitously different,
- * so we silently ignore the boot file, if necessary.
+ * In this example, kernel/unix tells the booter what file to boot. The
+ * original intent of this was that we didn't want reboot in a zone to
+ * be gratuitously different, so we would silently ignore the boot
+ * file, if necessary. However, this usage is archaic and has never
+ * been common, since it is impossible to boot a zone onto a different
+ * kernel. Ignoring the first argument breaks for non-native brands
+ * which pass boot arguments in a different style. e.g.
+ * systemd.log_level=debug
+ * Thus, for backward compatibility we only ignore the first argument
+ * if it appears to be in the illumos form and attempting to specify a
+ * kernel.
*/
if (argv[0] == NULL)
goto done;
@@ -363,7 +413,7 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs,
assert(argv[0][0] != ' ');
assert(argv[0][0] != '\t');
- if (argv[0][0] != '-' && argv[0][0] != '\0') {
+ if (strncmp(argv[0], "kernel/", 7) == 0) {
argv = &argv[1];
argc--;
}
@@ -386,41 +436,35 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs,
case 'm':
case 's':
/* These pass through unmolested */
- (void) snprintf(outargs, BOOTARGS_MAX,
- "%s -%c %s ", outargs, c, optarg ? optarg : "");
+ (void) snprintf(scratchopt, sizeof (scratchopt),
+ "-%c", c);
+ strnappend(outargs, BOOTARGS_MAX, scratchopt);
+ if (optarg != NULL)
+ strnappend(outargs, BOOTARGS_MAX, optarg);
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);
+ strnappend(outargs, BOOTARGS_MAX, 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) {
+ strnappend(outargs, BOOTARGS_MAX, argv[optind]);
+ optind++;
}
done:
@@ -459,7 +503,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;
@@ -472,7 +516,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);
@@ -482,7 +526,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;
@@ -495,7 +539,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);
@@ -532,37 +576,51 @@ notify_zonestatd(zoneid_t zoneid)
* Bring a zone up to the pre-boot "ready" stage. The mount_cmd argument is
* 'true' if this is being invoked as part of the processing for the "mount"
* subcommand.
+ *
+ * If a scratch zone mount (ALT_MOUNT) is being performed then do not
+ * call the state change hooks.
*/
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 (!ALT_MOUNT(mount_cmd) &&
+ 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 (!ALT_MOUNT(mount_cmd) &&
+ brand_poststatechg(zlogp, zstate, Z_READY, debug) != 0)
goto bad;
return (0);
@@ -572,7 +630,16 @@ 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);
+ if (!ALT_MOUNT(mount_cmd))
+ (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);
}
@@ -624,15 +691,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));
@@ -687,6 +747,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);
/*
@@ -714,9 +776,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 */
@@ -740,18 +819,275 @@ mount_early_fs(void *data, const char *spec, const char *dir,
}
/*
+ * Replace characters other than [A-Za-z0-9_] with '_' so that the string is a
+ * valid environment variable name.
+ */
+static void
+sanitize_env_var_name(char *var)
+{
+ for (char *p = var; *p != '\0'; p++) {
+ if (!isalnum(*p)) {
+ *p = '_';
+ }
+ }
+}
+
+/*
+ * 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)
+{
+ /* Enough for maximal name, rsrc + attr, & slop for ZONECFG & _'s */
+ char nm[2 * MAXNAMELEN + 32];
+
+ 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);
+
+ sanitize_env_var_name(nm);
+
+ (void) setenv(nm, val, 1);
+}
+
+/*
+ * Resolve a device:match value to a path. This is only different for PPT
+ * devices, where we expect the match property to be a /devices/... path, and
+ * configured for PPT already.
+ */
+int
+resolve_device_match(zlog_t *zlogp, struct zone_devtab *dtab,
+ char *path, size_t len)
+{
+ struct zone_res_attrtab *rap;
+
+ for (rap = dtab->zone_dev_attrp; rap != NULL;
+ rap = rap->zone_res_attr_next) {
+ if (strcmp(rap->zone_res_attr_name, "model") == 0 &&
+ strcmp(rap->zone_res_attr_value, "passthru") == 0)
+ break;
+ }
+
+ if (rap == NULL) {
+ if (strlcpy(path, dtab->zone_dev_match, len) >= len)
+ return (Z_INVAL);
+ return (Z_OK);
+ }
+
+ if (strncmp(dtab->zone_dev_match, "/devices",
+ strlen("/devices")) != 0) {
+ zerror(zlogp, B_FALSE, "invalid passthru match value '%s'",
+ dtab->zone_dev_match);
+ return (Z_INVAL);
+ }
+
+ if (ppt_devpath_to_dev(dtab->zone_dev_match, path, len) != 0) {
+ zerror(zlogp, B_TRUE, "failed to resolve passthru device %s",
+ dtab->zone_dev_match);
+ return (Z_INVAL);
+ }
+
+ return (Z_OK);
+}
+
+/*
+ * Export various zonecfg properties into environment for the boot and state
+ * change hooks.
+ *
+ * If debug is true, _ZONEADMD_brand_debug is set to 1, else it is set to an
+ * empty string. Brand hooks consider any non-empty string as an indication
+ * that debug output is requested.
+ *
+ * We could export more of the config in the future, as necessary. A better
+ * solution would be to make it so brand-specific behavior is handled by
+ * brand-specific callbacks written in C. Then the normal libzonecfg interfaces
+ * can be used for accessing any parts of the configuration that are needed.
+ *
+ * All of the environment variables set by this function are specific to
+ * SmartOS.
+ */
+static int
+setup_subproc_env(zlog_t *zlogp, 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];
+ char didstr[16];
+ char uuidstr[UUID_PRINTABLE_STRING_LENGTH];
+ uuid_t uuid;
+
+ /* snap_hndl is null when called through the set_brand_env code path */
+ if (snap_hndl == NULL)
+ return (Z_OK);
+
+ if ((res = zonecfg_get_uuid(zone_name, uuid)) != Z_OK)
+ return (res);
+
+ uuid_unparse(uuid, uuidstr);
+ (void) setenv("_ZONECFG_uuid", uuidstr, 1);
+
+ (void) snprintf(didstr, sizeof (didstr), "%d", zone_did);
+ (void) setenv("_ZONECFG_did", didstr, 1);
+
+ /*
+ * "net" resources are exported because zoneadmd does not handle
+ * automatic configuration of vnics and so that the bhyve boot hook
+ * can generate the argument list for the brand's init program. At such
+ * a time as vnic creation is handled in zoneadmd and brand callbacks
+ * can be executed as part of the zoneadmd process this should be
+ * removed.
+ */
+ 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);
+
+ /*
+ * "device" resources are exported because the bhyve boot brand callback
+ * needs them to generate the argument list for the brand's init
+ * program. At such a time as brand callbacks can be executed as part
+ * of the zoneadmd process, this should be removed.
+ *
+ * The bhyve brand only supports disk-like and ppt devices and does not
+ * support regular expressions.
+ */
+ if ((res = zonecfg_setdevent(snap_hndl)) != Z_OK)
+ goto done;
+
+ dev_resources[0] = '\0';
+ while (zonecfg_getdevent(snap_hndl, &dtab) == Z_OK) {
+ char *match = dtab.zone_dev_match;
+ struct zone_res_attrtab *rap;
+ char path[MAXPATHLEN];
+
+ res = resolve_device_match(zlogp, &dtab, path, sizeof (path));
+ if (res != Z_OK)
+ goto done;
+
+ /*
+ * Even if not modified, the match path will be mangled in the
+ * environment variable name, so we always store the value here.
+ */
+ set_zonecfg_env(RSRC_DEV, match, "path", path);
+
+ 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);
+ }
+
+ /*
+ * _ZONECFG_device_resources will contain a space separated list
+ * of devices that have _ZONECFG_device_<device>* environment
+ * variables. So that each element of the list matches up with
+ * <device>, each list item needs to be sanitized in the same
+ * way that environment variable names are sanitized.
+ */
+ sanitize_env_var_name(match);
+ (void) strlcat(dev_resources, match, sizeof (dev_resources));
+ (void) strlcat(dev_resources, " ", sizeof (dev_resources));
+ }
+ (void) zonecfg_enddevent(snap_hndl);
+
+ (void) setenv("_ZONECFG_device_resources", dev_resources, 1);
+
+ /*
+ * "attr" resources are exported because the bhyve brand's boot hook
+ * needs access to the "ram", "cpu", "bootrom", etc. to form the
+ * argument list for the brand's init program. Once the bhyve brand is
+ * configured via proper resources and properties, this should be
+ * removed.
+ */
+ 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;
FILE *file;
int status;
int rd_cnt;
+ int fds[2];
+ pid_t child;
if (retstr != NULL) {
if ((*retstr = malloc(1024)) == NULL) {
@@ -764,31 +1100,104 @@ do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr)
inbuf = buf;
}
- file = popen(cmdbuf, "r");
- if (file == NULL) {
- zerror(zlogp, B_TRUE, "could not launch: %s", cmdbuf);
+ if (pipe(fds) != 0) {
+ zerror(zlogp, B_TRUE, "failed to create pipe for subprocess");
return (-1);
}
+ if ((child = fork()) == 0) {
+ int in;
+
+ /*
+ * SIGINT is currently ignored. It probably shouldn't be so
+ * hard to kill errant children, so we revert to SIG_DFL.
+ * SIGHUP and SIGUSR1 are used to perform log rotation. We
+ * leave those as-is because we don't want a 'pkill -HUP
+ * zoneadmd' to kill this child process before exec(). On
+ * exec(), SIGHUP and SIGUSR1 will become SIG_DFL.
+ */
+ (void) sigset(SIGINT, SIG_DFL);
+
+ /*
+ * Set up a pipe for the child to log to.
+ */
+ if (dup2(fds[1], STDERR_FILENO) == -1) {
+ (void) snprintf(buf, sizeof (buf),
+ "subprocess failed to dup2(STDERR_FILENO): %s\n",
+ strerror(errno));
+ (void) write(fds[1], buf, strlen(buf));
+ _exit(127);
+ }
+ if (dup2(fds[1], STDOUT_FILENO) == -1) {
+ perror("subprocess failed to dup2(STDOUT_FILENO)");
+ _exit(127);
+ }
+ /*
+ * Some naughty children may try to read from stdin. Be sure
+ * that the first file that a child opens doesn't get stdin's
+ * file descriptor.
+ */
+ if ((in = open("/dev/null", O_RDONLY)) == -1 ||
+ dup2(in, STDIN_FILENO) == -1) {
+ zerror(zlogp, B_TRUE,
+ "subprocess failed to set up STDIN_FILENO");
+ _exit(127);
+ }
+ closefrom(STDERR_FILENO + 1);
+
+ if (setup_subproc_env(zlogp, debug) != Z_OK) {
+ zerror(zlogp, B_FALSE, "failed to setup environment");
+ _exit(127);
+ }
+
+ (void) execl("/bin/sh", "sh", "-c", cmdbuf, NULL);
+
+ zerror(zlogp, B_TRUE, "subprocess execl failed");
+ _exit(127);
+ } else if (child == -1) {
+ zerror(zlogp, B_TRUE, "failed to create subprocess for '%s'",
+ cmdbuf);
+ (void) close(fds[0]);
+ (void) close(fds[1]);
+ return (-1);
+ }
+
+ (void) close(fds[1]);
+
+ file = fdopen(fds[0], "r");
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;
rd_cnt += 1024 - 1;
if ((p = realloc(*retstr, rd_cnt + 1024)) == NULL) {
zerror(zlogp, B_FALSE, "out of memory");
- (void) pclose(file);
- return (-1);
+ break;
}
*retstr = p;
inbuf = *retstr + rd_cnt;
}
}
- status = pclose(file);
+
+ while (fclose(file) != 0) {
+ assert(errno == EINTR);
+ }
+ while (waitpid(child, &status, 0) == -1) {
+ if (errno != EINTR) {
+ zerror(zlogp, B_TRUE,
+ "failed to get exit status of '%s'", cmdbuf);
+ return (-1);
+ }
+ }
if (WIFSIGNALED(status)) {
zerror(zlogp, B_FALSE, "%s unexpectedly terminated due to "
@@ -803,24 +1212,91 @@ 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
+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));
+}
+
+/*
+ * Get the app-svc-dependent flag for this zone's init process. This is a
+ * zone-specific attr which controls the type of contract we create for the
+ * zone's init. When true, the contract will include CT_PR_EV_EXIT in the fatal
+ * set, so that when any service which is in the same contract exits, the init
+ * application will be terminated.
+ */
+static boolean_t
+is_app_svc_dep(void)
+{
+ struct zone_attrtab a;
+
+ bzero(&a, sizeof (a));
+ (void) strlcpy(a.zone_attr_name, "app-svc-dependent",
+ sizeof (a.zone_attr_name));
+
+ if (zonecfg_lookup_attr(snap_hndl, &a) == Z_OK &&
+ strcmp(a.zone_attr_value, "true") == 0) {
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
static int
-zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate)
+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 app_svc_dep;
boolean_t restart_init, restart_init0, restart_initreboot;
- 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) {
@@ -853,13 +1329,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");
@@ -868,7 +1339,7 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate)
}
/* Get the path for this zone's init(8) (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(8) location");
brand_close(bh);
@@ -876,35 +1347,44 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate)
}
/* See if this zone's brand should restart init if it dies. */
- restart_init = brand_restartinit(bh);
+ restart_init = restartinit(bh);
restart_init0 = brand_restartinit0(bh);
restart_initreboot = brand_restartinitreboot(bh);
+ /*
+ * See if we need to setup contract dependencies between the zone's
+ * primary application and any of its services.
+ */
+ app_svc_dep = is_app_svc_dep();
+
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) {
+ /*
+ * Try to anticipate possible problems: If possible, make sure init is
+ * executable.
+ */
+ 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) {
+ if (lstat(initpath, &st) == -1) {
zerror(zlogp, B_TRUE, "could not stat %s", initpath);
goto bad;
}
- if ((st.st_mode & S_IXUSR) == 0) {
+ /* LINTED: E_NOP_IF_STMT */
+ if ((st.st_mode & S_IFMT) == S_IFLNK) {
+ /* symlink, we'll have to wait and resolve when we boot */
+ } else if ((st.st_mode & S_IXUSR) == 0) {
zerror(zlogp, B_FALSE, "%s is not executable", initpath);
goto bad;
}
@@ -922,7 +1402,6 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate)
" %s", dladm_status2str(status, errmsg));
goto bad;
}
- links_loaded = B_TRUE;
}
/*
@@ -931,7 +1410,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;
}
@@ -963,19 +1442,31 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate)
goto bad;
}
+ if (app_svc_dep && zone_setattr(zoneid, ZONE_ATTR_APP_SVC_CT,
+ (void *)B_TRUE, sizeof (boolean_t)) == -1) {
+ zerror(zlogp, B_TRUE, "could not set zone app-die");
+ goto bad;
+ }
+
/*
* Inform zonestatd of a new zone so that it can install a door for
* the zone to contact it.
*/
notify_zonestatd(zone_id);
+ /* Startup a thread to perform zfd logging/tty svc for the zone. */
+ create_log_thread(zlogp);
+
if (zone_boot(zoneid) == -1) {
zerror(zlogp, B_TRUE, "unable to boot zone");
+ destroy_log_thread(zlogp);
goto bad;
}
- if (brand_poststatechg(zlogp, zstate, Z_BOOT) != 0)
+ if (brand_poststatechg(zlogp, zstate, Z_BOOT, debug) != 0) {
+ destroy_log_thread(zlogp);
goto bad;
+ }
return (0);
@@ -984,32 +1475,45 @@ 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 performing a scratch zone unmount then do not call the
+ * state change hooks.
+ */
+ if (unmount_cmd == B_FALSE &&
+ brand_prestatechg(zlogp, zstate, Z_HALT, debug) != 0)
return (-1);
- if (vplat_teardown(zlogp, unmount_cmd, rebooting) != 0) {
+ if (vplat_teardown(zlogp, unmount_cmd, rebooting, debug) != 0) {
if (!bringup_failure_recovery)
zerror(zlogp, B_FALSE, "unable to destroy zone");
+ destroy_log_thread(zlogp);
return (-1);
}
+ /* Shut down is done, stop the log thread */
+ destroy_log_thread(zlogp);
+
+ if (unmount_cmd == B_FALSE &&
+ 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);
}
@@ -1021,7 +1525,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;
@@ -1042,18 +1545,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);
}
@@ -1191,6 +1688,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 */
@@ -1203,9 +1730,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 = B_FALSE;
+ boolean_t kernelcall = B_TRUE;
int rval = -1;
uint64_t uniqid;
@@ -1226,6 +1755,8 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp,
* it is time for us to shut down zoneadmd.
*/
if (zargp == DOOR_UNREF_DATA) {
+ logstream_close(platloghdl, B_TRUE);
+
/*
* See comment at end of main() for info on the last rites.
*/
@@ -1255,6 +1786,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");
@@ -1335,7 +1868,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp,
rval = -1;
goto out;
}
- zlogp = &logsys; /* Log errors to syslog */
+ zlogp = &logplat; /* Log errors to platform.log */
}
/*
@@ -1361,23 +1894,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;
@@ -1429,7 +1964,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;
@@ -1495,12 +2030,14 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp,
(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';
@@ -1508,9 +2045,10 @@ 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:
@@ -1534,7 +2072,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);
@@ -1556,10 +2094,12 @@ 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);
@@ -1576,32 +2116,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';
@@ -1782,6 +2330,29 @@ top:
"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
+ * for the zone. zlogp won't be valid for much longer
+ * so use logplat.
+ */
+ if (getzoneidbyname(zone_name) != -1) {
+ create_log_thread(&logplat);
+ }
+
+ /* 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);
@@ -1795,21 +2366,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);
@@ -1820,7 +2432,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);
@@ -1831,7 +2443,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);
@@ -1959,6 +2571,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");
@@ -2030,6 +2647,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);
@@ -2156,6 +2778,15 @@ main(int argc, char *argv[])
openlog("zoneadmd", LOG_PID, LOG_DAEMON);
/*
+ * Allow logging to <zonepath>/logs/<file>.
+ */
+ logstream_init(zlogp);
+ platloghdl = logstream_open("platform.log", "zoneadmd", 0);
+
+ /* logplat looks the same as logsys, but logs to platform.log */
+ logplat = logsys;
+
+ /*
* The eventstream is used to publish state changes in the zone
* from the door threads to the console I/O poller.
*/
@@ -2174,7 +2805,6 @@ main(int argc, char *argv[])
if (make_daemon_exclusive(zlogp) == -1)
goto child_out;
-
/*
* Create/join a new session; we need to be careful of what we do with
* the console from now on so we don't end up being the session leader
@@ -2184,9 +2814,13 @@ main(int argc, char *argv[])
/*
* This thread shouldn't be receiving any signals; in particular,
- * SIGCHLD should be received by the thread doing the fork().
+ * SIGCHLD should be received by the thread doing the fork(). The
+ * exceptions are SIGHUP and SIGUSR1 for log rotation, set up by
+ * logstream_init().
*/
(void) sigfillset(&blockset);
+ (void) sigdelset(&blockset, SIGHUP);
+ (void) sigdelset(&blockset, SIGUSR1);
(void) thr_sigsetmask(SIG_BLOCK, &blockset, NULL);
/*
diff --git a/usr/src/cmd/zoneadmd/zoneadmd.h b/usr/src/cmd/zoneadmd/zoneadmd.h
index d784a303b3..06353cbe61 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 2019 Joyent, Inc.
*/
#ifndef _ZONEADMD_H
@@ -32,6 +33,9 @@ extern "C" {
#endif
#include <libdladm.h>
+#include <libzonecfg.h>
+#include <thread.h>
+#include <synch.h>
/*
* Multi-threaded programs should avoid MT-unsafe library calls (i.e., any-
@@ -69,6 +73,7 @@ extern "C" {
#define DEFAULT_DIR_USER -1 /* user ID for chown: -1 means don't change */
#define DEFAULT_DIR_GROUP -1 /* grp ID for chown: -1 means don't change */
+#define ALT_MOUNT(mount_cmd) ((mount_cmd) != Z_MNT_BOOT)
typedef struct zlog {
FILE *logfile; /* file to log to */
@@ -83,24 +88,27 @@ typedef struct zlog {
char *locale; /* locale to use for gettext() */
} zlog_t;
-extern zlog_t logsys;
+extern zlog_t logsys; /* syslog */
+extern zlog_t logplat; /* platform.log */
extern mutex_t lock;
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 +120,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 +142,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 +161,23 @@ 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();
+
+/*
+ * Logging routines
+ */
+typedef enum {
+ LS_LINE_BUFFERED = 0x1 /* Write when \n found or full buffer */
+} logstream_flags_t;
+
+extern boolean_t logging_poisoned;
+
+extern void create_log_thread(zlog_t *);
+extern void destroy_log_thread(zlog_t *);
+extern void logstream_init(zlog_t *);
+extern int logstream_open(const char *, const char *, logstream_flags_t);
+extern void logstream_write(int, char *, int);
+extern void logstream_close(int, boolean_t);
/*
* Contract handling.
@@ -163,7 +187,13 @@ 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);
+
+/*
+ * Resource handling.
+ */
+extern int resolve_device_match(zlog_t *, struct zone_devtab *,
+ char *, size_t);
#ifdef __cplusplus
}
diff --git a/usr/src/cmd/zonecfg/Makefile b/usr/src/cmd/zonecfg/Makefile
index 5cab6b079f..b4e3d585eb 100644
--- a/usr/src/cmd/zonecfg/Makefile
+++ b/usr/src/cmd/zonecfg/Makefile
@@ -29,6 +29,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
@@ -38,7 +39,8 @@ MAPOPTS = $(MAPFILES:%=-Wl,-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
+CFLAGS += -DYYLMAX=2048
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 a97444dd88..f7bd684606 100644
--- a/usr/src/cmd/zonecfg/zonecfg.c
+++ b/usr/src/cmd/zonecfg/zonecfg.c
@@ -81,6 +81,7 @@
#include <libinetutil.h>
#include <pwd.h>
#include <inet/ip.h>
+#include <uuid/uuid.h>
#include <libzonecfg.h>
#include "zonecfg.h"
@@ -128,7 +129,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>=" \
@@ -189,6 +190,8 @@ char *res_types[] = {
"admin",
"fs-allowed",
ALIAS_MAXPROCS,
+ ALIAS_ZFSPRI,
+ "uuid",
"security-flags",
NULL
};
@@ -237,6 +240,12 @@ char *prop_types[] = {
"fs-allowed",
ALIAS_MAXPROCS,
"allowed-address",
+ ALIAS_ZFSPRI,
+ "mac-addr",
+ "vlan-id",
+ "global-nic",
+ "property",
+ "uuid",
"default",
"lower",
"upper",
@@ -306,6 +315,7 @@ static const char *clear_cmds[] = {
"clear " ALIAS_MAXSEMIDS,
"clear " ALIAS_SHARES,
"clear " ALIAS_MAXPROCS,
+ "clear " ALIAS_ZFSPRI,
NULL
};
@@ -358,6 +368,8 @@ static const char *set_cmds[] = {
"set hostid=",
"set fs-allowed=",
"set " ALIAS_MAXPROCS "=",
+ "set " ALIAS_ZFSPRI "=",
+ "set uuid=",
NULL
};
@@ -391,6 +403,7 @@ static const char *info_cmds[] = {
"info admin",
"info fs-allowed",
"info max-processes",
+ "info uuid",
NULL
};
@@ -416,10 +429,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 allowed-address=",
- "set physical=",
"set defrouter=",
+ "set global-nic=",
+ "set mac-addr=",
+ "set physical=",
+ "set vlan-id=",
NULL
};
@@ -429,6 +452,7 @@ static const char *device_res_scope_cmds[] = {
"exit",
"help",
"info",
+ "add property ",
"set match=",
NULL
};
@@ -546,12 +570,14 @@ 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;
/* set in modifying functions, checked in read_input() */
static boolean_t need_to_commit = B_FALSE;
+static boolean_t is_create = B_FALSE;
boolean_t saw_error;
/* set in yacc parser, checked in read_input() */
@@ -600,7 +626,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 struct zone_secflagstab old_secflagstab, in_progress_secflagstab;
@@ -1106,11 +1131,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(8) for "
"details of the <interface> string.\n"));
(void) fprintf(fp, gettext("%s %s is valid "
@@ -1118,10 +1152,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, "
@@ -1137,6 +1173,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;
@@ -1283,10 +1322,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) {
@@ -1375,15 +1416,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),
@@ -1441,6 +1489,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 "
@@ -1708,6 +1759,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);
@@ -1715,7 +1767,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 '?':
@@ -1741,6 +1793,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;
@@ -1794,9 +1857,14 @@ create_func(cmd_t *cmd)
}
need_to_commit = B_TRUE;
+ is_create = B_TRUE;
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);
}
/*
@@ -1845,8 +1913,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;
struct zone_secflagstab secflagstab;
int err, arg;
@@ -1860,6 +1928,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;
@@ -1970,6 +2039,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;
@@ -2017,7 +2094,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);
@@ -2030,21 +2117,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;
@@ -2208,7 +2291,6 @@ add_resource(cmd_t *cmd)
{
int type;
struct zone_psettab tmp_psettab;
- struct zone_mcaptab tmp_mcaptab;
struct zone_secflagstab tmp_secflagstab;
uint64_t tmp;
uint64_t tmp_mcap;
@@ -2301,9 +2383,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 "
@@ -2316,7 +2399,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));
@@ -2427,6 +2509,79 @@ 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;
+ }
+ if (strlcpy(np->zone_res_attr_name, cp->cp_value,
+ sizeof (np->zone_res_attr_name)) >=
+ sizeof (np->zone_res_attr_name)) {
+ zerr(gettext("Input for %s is too long"),
+ pt_to_str(PT_NAME));
+ goto bad;
+ }
+ seen_name = B_TRUE;
+ break;
+ case PT_VALUE:
+ if (seen_value) {
+ zerr(gettext("%s already specified"),
+ pt_to_str(PT_VALUE));
+ goto bad;
+ }
+ if (strlcpy(np->zone_res_attr_value, cp->cp_value,
+ sizeof (np->zone_res_attr_value)) >=
+ sizeof (np->zone_res_attr_value)) {
+ zerr(gettext("Input for %s is too long"),
+ pt_to_str(PT_VALUE));
+ goto bad;
+ }
+
+ 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)
{
@@ -2494,6 +2649,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,
@@ -2538,7 +2731,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));
}
@@ -2547,7 +2740,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));
}
@@ -2598,8 +2791,9 @@ add_func(cmd_t *cmd)
resource_scope = cmd->cmd_res_type;
end_op = CMD_ADD;
add_resource(cmd);
- } else
+ } else {
add_property(cmd);
+ }
}
/*
@@ -2764,6 +2958,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 != '\0' || 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)
@@ -2797,6 +3017,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,
@@ -3095,6 +3330,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);
@@ -3123,7 +3360,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;
@@ -3132,13 +3369,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;
}
@@ -3157,7 +3397,7 @@ remove_fs(cmd_t *cmd)
}
static void
-remove_net(cmd_t *cmd)
+remove_net(cmd_t *cmd, boolean_t force)
{
int err;
@@ -3166,13 +3406,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;
}
@@ -3190,7 +3435,7 @@ remove_net(cmd_t *cmd)
}
static void
-remove_device(cmd_t *cmd)
+remove_device(cmd_t *cmd, boolean_t force)
{
int err;
@@ -3199,13 +3444,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;
}
@@ -3223,7 +3473,7 @@ remove_device(cmd_t *cmd)
}
static void
-remove_attr(cmd_t *cmd)
+remove_attr(cmd_t *cmd, boolean_t force)
{
int err;
@@ -3232,13 +3482,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;
}
@@ -3256,7 +3511,7 @@ remove_attr(cmd_t *cmd)
}
static void
-remove_dataset(cmd_t *cmd)
+remove_dataset(cmd_t *cmd, boolean_t force)
{
int err;
@@ -3265,13 +3520,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;
}
@@ -3289,7 +3549,7 @@ remove_dataset(cmd_t *cmd)
}
static void
-remove_rctl(cmd_t *cmd)
+remove_rctl(cmd_t *cmd, boolean_t force)
{
int err;
@@ -3298,13 +3558,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;
}
@@ -3323,72 +3588,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;
}
@@ -3396,8 +3679,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;
}
@@ -3408,7 +3694,7 @@ remove_mcap()
}
static void
-remove_admin(cmd_t *cmd)
+remove_admin(cmd_t *cmd, boolean_t force)
{
int err;
@@ -3417,34 +3703,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
@@ -3474,6 +3759,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);
@@ -3488,6 +3774,7 @@ remove_resource(cmd_t *cmd)
arg_err = B_TRUE;
break;
case 'F':
+ force = B_TRUE;
break;
default:
short_usage(CMD_REMOVE);
@@ -3503,34 +3790,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;
case RT_SECFLAGS:
remove_secflags();
@@ -3550,7 +3837,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];
@@ -3592,7 +3899,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;
@@ -3604,12 +3911,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,
@@ -3658,22 +4015,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);
@@ -3736,8 +4081,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);
@@ -3747,6 +4091,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;
case RT_SECFLAGS:
switch (prop_type) {
case PT_LOWER:
@@ -3788,6 +4156,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;
@@ -3850,6 +4220,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);
@@ -3895,7 +4268,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;
@@ -3990,7 +4363,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)
@@ -3999,12 +4373,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))
@@ -4281,9 +4649,8 @@ 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;
+ double cap;
char *unitp;
struct zone_psettab tmp_psettab;
boolean_t arg_err = B_FALSE;
@@ -4356,6 +4723,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."));
@@ -4385,10 +4756,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."),
@@ -4561,6 +4934,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) {
@@ -4574,6 +4950,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);
@@ -4648,6 +5033,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) {
@@ -4658,6 +5058,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);
@@ -4673,6 +5087,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);
@@ -4816,35 +5244,50 @@ set_func(cmd_t *cmd)
* the add_resource() function.
*/
- if ((cap = strtof(prop_id, &unitp)) <= 0 || *unitp != '\0' ||
- (int)(cap * 100) < 1) {
+ if ((cap = strtod(prop_id, &unitp)) <= 0 || *unitp != '\0' ||
+ (cap * 100.0) < 1) {
zerr(gettext("%s property is out of range."),
pt_to_str(PT_NCPUS));
saw_error = B_TRUE;
return;
}
+ cap *= 100.0;
+ /* To avoid rounding issues add .5 to force correct value. */
if ((err = zonecfg_set_aliased_rctl(handle, ALIAS_CPUCAP,
- (int)(cap * 100))) != Z_OK)
+ (uint_t)(cap + 0.5))) != Z_OK) {
zone_perror(zone, err, B_TRUE);
- else
+ } else {
need_to_commit = B_TRUE;
+ }
return;
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:
@@ -5139,6 +5582,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];
@@ -5220,12 +5680,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
@@ -5268,8 +5741,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
@@ -5528,15 +6011,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) {
@@ -5558,16 +6044,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
@@ -5637,9 +6123,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);
@@ -5687,7 +6175,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:
@@ -5730,6 +6220,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);
@@ -5738,6 +6229,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);
@@ -5802,6 +6294,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;
@@ -5832,6 +6327,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;
@@ -6410,10 +6908,33 @@ 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));
+
+ if (is_create) {
+ zonecfg_notify_create(handle);
+ is_create = B_FALSE;
+ }
+ }
+
+ /*
+ * 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);
@@ -6584,6 +7105,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);
@@ -6887,8 +7409,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,
@@ -6904,11 +7426,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."),
@@ -6920,23 +7437,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. */
@@ -7499,8 +7999,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);
@@ -7527,7 +8029,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 == '?')
@@ -7554,6 +8056,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;
@@ -7564,6 +8081,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);
@@ -7571,7 +8089,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 108d0ce507..e4ae4d4d61 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,7 +91,9 @@ 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_SECFLAGS 29
+#define RT_ZFSPRI 29 /* really a rctl alias property, but for info */
+#define RT_UUID 30 /* really a property, but for info */
+#define RT_SECFLAGS 31
#define RT_MIN RT_UNKNOWN
#define RT_MAX RT_SECFLAGS
@@ -138,9 +141,15 @@ extern "C" {
#define PT_FS_ALLOWED 39
#define PT_MAXPROCS 40
#define PT_ALLOWED_ADDRESS 41
-#define PT_DEFAULT 42
-#define PT_LOWER 43
-#define PT_UPPER 44
+#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_DEFAULT 48
+#define PT_LOWER 49
+#define PT_UPPER 50
#define PT_MIN PT_UNKNOWN
#define PT_MAX PT_UPPER
diff --git a/usr/src/cmd/zonecfg/zonecfg_grammar.y b/usr/src/cmd/zonecfg/zonecfg_grammar.y
index fe660bb607..107f8252a8 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.
*/
/*
@@ -135,7 +136,8 @@ complex_piece_func(int cp_type, const char *str, complex_property_ptr_t cp_next)
%token NAME MATCH PRIV LIMIT ACTION VALUE EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET
%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 SECFLAGS USER AUTHS MAXPROCS
+%token MAXSEMIDS LOCKED SWAP SCHED CLEAR DEFROUTER ADMIN USER AUTHS MAXPROCS
+%token ZFSPRI MAC VLANID GNIC NPROP UUID SECFLAGS
%token DEFAULT UPPER LOWER
%type <strval> TOKEN EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET
@@ -146,7 +148,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 DEFAULT UPPER LOWER
+ ALLOWED_ADDRESS MAC VLANID GNIC NPROP UUID DEFAULT UPPER LOWER
%type <cmd> command
%type <cmd> add_command ADD
%type <cmd> cancel_command CANCEL
@@ -651,6 +653,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)
@@ -735,6 +755,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)
@@ -746,6 +779,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)
@@ -759,6 +806,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)
@@ -774,6 +837,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
{
@@ -978,6 +1059,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; }
@@ -1001,6 +1086,8 @@ property_name: SPECIAL { $$ = PT_SPECIAL; }
| USER { $$ = PT_USER; }
| AUTHS { $$ = PT_AUTHS; }
| FS_ALLOWED { $$ = PT_FS_ALLOWED; }
+ | UUID { $$ = PT_UUID; }
+ | ZFSPRI { $$ = PT_ZFSPRI; }
| DEFAULT { $$ = PT_DEFAULT; }
| UPPER { $$ = PT_UPPER; }
| LOWER { $$ = PT_LOWER; }
diff --git a/usr/src/cmd/zonecfg/zonecfg_lex.l b/usr/src/cmd/zonecfg/zonecfg_lex.l
index 8714c55499..05b41df48b 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
-%p 5000
+%a 8000
+%p 5500
%e 2000
%n 1000
+%o 14000
%{
/*
@@ -238,6 +240,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; }
@@ -310,6 +324,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>default { return DEFAULT; }
<CSTATE>default { return DEFAULT; }
@@ -368,6 +388,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 026d188fda..10b73fd26e 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 e8cc7c5209..612f03540a 100644
--- a/usr/src/cmd/zpool/zpool_main.c
+++ b/usr/src/cmd/zpool/zpool_main.c
@@ -24,6 +24,7 @@
* Copyright (c) 2011, 2018 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.
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
* Copyright 2016 Nexenta Systems, Inc.
* Copyright (c) 2017 Datto Inc.
@@ -2792,6 +2793,8 @@ zpool_do_checkpoint(int argc, char **argv)
*
* -F Attempt rewind if necessary.
*
+ * -m Allow import with a missing log device.
+ *
* -n See if rewind would work, but don't actually rewind.
*
* -N Import the pool but don't mount datasets.
diff --git a/usr/src/common/brand/lx/lx_auxv.c b/usr/src/common/brand/lx/lx_auxv.c
new file mode 100644
index 0000000000..2ed5fd0517
--- /dev/null
+++ b/usr/src/common/brand/lx/lx_auxv.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 2016 Joyent, Inc.
+ */
+
+#include <sys/auxv.h>
+#include <sys/lx_brand.h>
+
+/*
+ * Linux does not make the distinction between 'int' and 'long' when it comes
+ * to the format of the aux vector. In order to properly clear the struct
+ * padding present in the native auxv_t in 64-bit, we employ the Linux format.
+ */
+struct lx_auxv {
+ long la_type;
+ long la_val;
+};
+
+int
+lx_auxv_stol(const auxv_t *ap, auxv_t *oap, const lx_elf_data_t *edp)
+{
+ struct lx_auxv *loap = (struct lx_auxv *)oap;
+
+ switch (ap->a_type) {
+ case AT_BASE:
+ loap->la_val = edp->ed_base;
+ break;
+ case AT_ENTRY:
+ loap->la_val = edp->ed_entry;
+ break;
+ case AT_PHDR:
+ loap->la_val = edp->ed_phdr;
+ break;
+ case AT_PHENT:
+ loap->la_val = edp->ed_phent;
+ break;
+ case AT_PHNUM:
+ loap->la_val = edp->ed_phnum;
+ break;
+ case AT_SUN_BRAND_LX_SYSINFO_EHDR:
+ loap->la_type = AT_SYSINFO_EHDR;
+ loap->la_val = ap->a_un.a_val;
+ return (0);
+ case AT_SUN_BRAND_LX_CLKTCK:
+ loap->la_type = AT_CLKTCK;
+ loap->la_val = ap->a_un.a_val;
+ return (0);
+ case AT_SUN_AUXFLAGS:
+ if ((ap->a_un.a_val & AF_SUN_SETUGID) != 0) {
+ loap->la_type = AT_SECURE;
+ loap->la_val = 1;
+ return (0);
+ } else {
+ return (1);
+ }
+ case AT_SUN_GID:
+ loap->la_type = AT_LX_EGID;
+ loap->la_val = ap->a_un.a_val;
+ return (0);
+ case AT_SUN_RGID:
+ loap->la_type = AT_LX_GID;
+ loap->la_val = ap->a_un.a_val;
+ return (0);
+ case AT_SUN_UID:
+ loap->la_type = AT_LX_EUID;
+ loap->la_val = ap->a_un.a_val;
+ return (0);
+ case AT_SUN_RUID:
+ loap->la_type = AT_LX_UID;
+ loap->la_val = ap->a_un.a_val;
+ return (0);
+ case AT_EXECFD:
+ case AT_PAGESZ:
+ case AT_FLAGS:
+ case AT_RANDOM:
+ case AT_NULL:
+ /* No translate needed */
+ loap->la_val = ap->a_un.a_val;
+ break;
+ default:
+ /* All other unrecognized entries are ignored */
+ return (1);
+ }
+ loap->la_type = ap->a_type;
+ return (0);
+}
diff --git a/usr/src/common/brand/lx/lx_auxv.h b/usr/src/common/brand/lx/lx_auxv.h
new file mode 100644
index 0000000000..190d939f35
--- /dev/null
+++ b/usr/src/common/brand/lx/lx_auxv.h
@@ -0,0 +1,32 @@
+/*
+ * 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 _LX_AUXV_H
+#define _LX_AUXV_H
+
+#include <sys/auxv.h>
+#include <sys/lx_brand.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int lx_auxv_stol(const auxv_t *, auxv_t *, const lx_elf_data_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_AUXV_H */
diff --git a/usr/src/common/brand/lx/lx_errno.c b/usr/src/common/brand/lx/lx_errno.c
new file mode 100644
index 0000000000..269ed470dc
--- /dev/null
+++ b/usr/src/common/brand/lx/lx_errno.c
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+
+/*
+ * This file contains a mapping table and lookup function for converting
+ * illumos native error numbers into error numbers appropriate for Linux
+ * emulation.
+ *
+ * The translation table is generated by the "gen_errno", built from and
+ * documented in "usr/src/common/brand/lx/tools".
+ */
+
+#include <sys/debug.h>
+
+const int
+lx_stol_errno[] = {
+ 0, /* 0: No Error */
+ 1, /* 1: EPERM --> 1: EPERM */
+ 2, /* 2: ENOENT --> 2: ENOENT */
+ 3, /* 3: ESRCH --> 3: ESRCH */
+ 4, /* 4: EINTR --> 4: EINTR */
+ 5, /* 5: EIO --> 5: EIO */
+ 6, /* 6: ENXIO --> 6: ENXIO */
+ 7, /* 7: E2BIG --> 7: E2BIG */
+ 8, /* 8: ENOEXEC --> 8: ENOEXEC */
+ 9, /* 9: EBADF --> 9: EBADF */
+ 10, /* 10: ECHILD --> 10: ECHILD */
+ 11, /* 11: EAGAIN --> 11: EAGAIN */
+ 12, /* 12: ENOMEM --> 12: ENOMEM */
+ 13, /* 13: EACCES --> 13: EACCES */
+ 14, /* 14: EFAULT --> 14: EFAULT */
+ 15, /* 15: ENOTBLK --> 15: ENOTBLK */
+ 16, /* 16: EBUSY --> 16: EBUSY */
+ 17, /* 17: EEXIST --> 17: EEXIST */
+ 18, /* 18: EXDEV --> 18: EXDEV */
+ 19, /* 19: ENODEV --> 19: ENODEV */
+ 20, /* 20: ENOTDIR --> 20: ENOTDIR */
+ 21, /* 21: EISDIR --> 21: EISDIR */
+ 22, /* 22: EINVAL --> 22: EINVAL */
+ 23, /* 23: ENFILE --> 23: ENFILE */
+ 24, /* 24: EMFILE --> 24: EMFILE */
+ 25, /* 25: ENOTTY --> 25: ENOTTY */
+ 26, /* 26: ETXTBSY --> 26: ETXTBSY */
+ 27, /* 27: EFBIG --> 27: EFBIG */
+ 28, /* 28: ENOSPC --> 28: ENOSPC */
+ 29, /* 29: ESPIPE --> 29: ESPIPE */
+ 30, /* 30: EROFS --> 30: EROFS */
+ 31, /* 31: EMLINK --> 31: EMLINK */
+ 32, /* 32: EPIPE --> 32: EPIPE */
+ 33, /* 33: EDOM --> 33: EDOM */
+ 34, /* 34: ERANGE --> 34: ERANGE */
+ 42, /* 35: ENOMSG --> 42: ENOMSG */
+ 43, /* 36: EIDRM --> 43: EIDRM */
+ 44, /* 37: ECHRNG --> 44: ECHRNG */
+ 45, /* 38: EL2NSYNC --> 45: EL2NSYNC */
+ 46, /* 39: EL3HLT --> 46: EL3HLT */
+ 47, /* 40: EL3RST --> 47: EL3RST */
+ 48, /* 41: ELNRNG --> 48: ELNRNG */
+ 49, /* 42: EUNATCH --> 49: EUNATCH */
+ 50, /* 43: ENOCSI --> 50: ENOCSI */
+ 51, /* 44: EL2HLT --> 51: EL2HLT */
+ 35, /* 45: EDEADLK --> 35: EDEADLK */
+ 37, /* 46: ENOLCK --> 37: ENOLCK */
+ 125, /* 47: ECANCELED --> 125: ECANCELED */
+ 38, /* 48: ENOTSUP --> 38: ENOSYS */
+ 122, /* 49: EDQUOT --> 122: EDQUOT */
+ 52, /* 50: EBADE --> 52: EBADE */
+ 53, /* 51: EBADR --> 53: EBADR */
+ 54, /* 52: EXFULL --> 54: EXFULL */
+ 55, /* 53: ENOANO --> 55: ENOANO */
+ 56, /* 54: EBADRQC --> 56: EBADRQC */
+ 57, /* 55: EBADSLT --> 57: EBADSLT */
+ 35, /* 56: EDEADLOCK --> 35: EDEADLK */
+ 59, /* 57: EBFONT --> 59: EBFONT */
+ 130, /* 58: EOWNERDEAD --> 130: EOWNERDEAD */
+ 131, /* 59: ENOTRECOVERABLE --> 131: ENOTRECOVERABLE */
+ 60, /* 60: ENOSTR --> 60: ENOSTR */
+ 61, /* 61: ENODATA --> 61: ENODATA */
+ 62, /* 62: ETIME --> 62: ETIME */
+ 63, /* 63: ENOSR --> 63: ENOSR */
+ 64, /* 64: ENONET --> 64: ENONET */
+ 65, /* 65: ENOPKG --> 65: ENOPKG */
+ 66, /* 66: EREMOTE --> 66: EREMOTE */
+ 67, /* 67: ENOLINK --> 67: ENOLINK */
+ 68, /* 68: EADV --> 68: EADV */
+ 69, /* 69: ESRMNT --> 69: ESRMNT */
+ 70, /* 70: ECOMM --> 70: ECOMM */
+ 71, /* 71: EPROTO --> 71: EPROTO */
+ -2, /* 72: ELOCKUNMAPPED --> -2: No Analogue */
+ -2, /* 73: ENOTACTIVE --> -2: No Analogue */
+ 72, /* 74: EMULTIHOP --> 72: EMULTIHOP */
+ -1, /* 75: Unused Number */
+ -1, /* 76: Unused Number */
+ 74, /* 77: EBADMSG --> 74: EBADMSG */
+ 36, /* 78: ENAMETOOLONG --> 36: ENAMETOOLONG */
+ 75, /* 79: EOVERFLOW --> 75: EOVERFLOW */
+ 76, /* 80: ENOTUNIQ --> 76: ENOTUNIQ */
+ 77, /* 81: EBADFD --> 77: EBADFD */
+ 78, /* 82: EREMCHG --> 78: EREMCHG */
+ 79, /* 83: ELIBACC --> 79: ELIBACC */
+ 80, /* 84: ELIBBAD --> 80: ELIBBAD */
+ 81, /* 85: ELIBSCN --> 81: ELIBSCN */
+ 82, /* 86: ELIBMAX --> 82: ELIBMAX */
+ 83, /* 87: ELIBEXEC --> 83: ELIBEXEC */
+ 84, /* 88: EILSEQ --> 84: EILSEQ */
+ 38, /* 89: ENOSYS --> 38: ENOSYS */
+ 40, /* 90: ELOOP --> 40: ELOOP */
+ 85, /* 91: ERESTART --> 85: ERESTART */
+ 86, /* 92: ESTRPIPE --> 86: ESTRPIPE */
+ 39, /* 93: ENOTEMPTY --> 39: ENOTEMPTY */
+ 87, /* 94: EUSERS --> 87: EUSERS */
+ 88, /* 95: ENOTSOCK --> 88: ENOTSOCK */
+ 89, /* 96: EDESTADDRREQ --> 89: EDESTADDRREQ */
+ 90, /* 97: EMSGSIZE --> 90: EMSGSIZE */
+ 91, /* 98: EPROTOTYPE --> 91: EPROTOTYPE */
+ 92, /* 99: ENOPROTOOPT --> 92: ENOPROTOOPT */
+ -1, /* 100: Unused Number */
+ -1, /* 101: Unused Number */
+ -1, /* 102: Unused Number */
+ -1, /* 103: Unused Number */
+ -1, /* 104: Unused Number */
+ -1, /* 105: Unused Number */
+ -1, /* 106: Unused Number */
+ -1, /* 107: Unused Number */
+ -1, /* 108: Unused Number */
+ -1, /* 109: Unused Number */
+ -1, /* 110: Unused Number */
+ -1, /* 111: Unused Number */
+ -1, /* 112: Unused Number */
+ -1, /* 113: Unused Number */
+ -1, /* 114: Unused Number */
+ -1, /* 115: Unused Number */
+ -1, /* 116: Unused Number */
+ -1, /* 117: Unused Number */
+ -1, /* 118: Unused Number */
+ -1, /* 119: Unused Number */
+ 93, /* 120: EPROTONOSUPPORT --> 93: EPROTONOSUPPORT */
+ 94, /* 121: ESOCKTNOSUPPORT --> 94: ESOCKTNOSUPPORT */
+ 95, /* 122: EOPNOTSUPP --> 95: EOPNOTSUPP */
+ 96, /* 123: EPFNOSUPPORT --> 96: EPFNOSUPPORT */
+ 97, /* 124: EAFNOSUPPORT --> 97: EAFNOSUPPORT */
+ 98, /* 125: EADDRINUSE --> 98: EADDRINUSE */
+ 99, /* 126: EADDRNOTAVAIL --> 99: EADDRNOTAVAIL */
+ 100, /* 127: ENETDOWN --> 100: ENETDOWN */
+ 101, /* 128: ENETUNREACH --> 101: ENETUNREACH */
+ 102, /* 129: ENETRESET --> 102: ENETRESET */
+ 103, /* 130: ECONNABORTED --> 103: ECONNABORTED */
+ 104, /* 131: ECONNRESET --> 104: ECONNRESET */
+ 105, /* 132: ENOBUFS --> 105: ENOBUFS */
+ 106, /* 133: EISCONN --> 106: EISCONN */
+ 107, /* 134: ENOTCONN --> 107: ENOTCONN */
+ -1, /* 135: Unused Number */
+ -1, /* 136: Unused Number */
+ -1, /* 137: Unused Number */
+ -1, /* 138: Unused Number */
+ -1, /* 139: Unused Number */
+ -1, /* 140: Unused Number */
+ -1, /* 141: Unused Number */
+ -1, /* 142: Unused Number */
+ 108, /* 143: ESHUTDOWN --> 108: ESHUTDOWN */
+ 109, /* 144: ETOOMANYREFS --> 109: ETOOMANYREFS */
+ 110, /* 145: ETIMEDOUT --> 110: ETIMEDOUT */
+ 111, /* 146: ECONNREFUSED --> 111: ECONNREFUSED */
+ 112, /* 147: EHOSTDOWN --> 112: EHOSTDOWN */
+ 113, /* 148: EHOSTUNREACH --> 113: EHOSTUNREACH */
+ 114, /* 149: EALREADY --> 114: EALREADY */
+ 115, /* 150: EINPROGRESS --> 115: EINPROGRESS */
+ 116 /* 151: ESTALE --> 116: ESTALE */
+};
+
+/*
+ * Convert an illumos native error number to a Linux error number and return
+ * it. If no valid conversion is possible, the function fails back to the
+ * value of "defval". In userland, passing a default error number of "-1"
+ * will abort the program if the error number could not be converted.
+ */
+int
+lx_errno(int native_errno, int defval)
+{
+#ifdef _KERNEL
+ VERIFY3S(defval, >=, 0);
+#endif
+
+ if (native_errno < 0 || native_errno >= (sizeof (lx_stol_errno) /
+ sizeof (lx_stol_errno[0]))) {
+#ifndef _KERNEL
+ VERIFY3S(defval, >=, 0);
+#endif
+
+ return (defval);
+ }
+
+ return (lx_stol_errno[native_errno]);
+}
diff --git a/usr/src/common/brand/lx/lx_errno.h b/usr/src/common/brand/lx/lx_errno.h
new file mode 100644
index 0000000000..10b6b3066c
--- /dev/null
+++ b/usr/src/common/brand/lx/lx_errno.h
@@ -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 2015 Joyent, Inc.
+ */
+
+#ifndef _LX_ERRNO_H
+#define _LX_ERRNO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int lx_errno(int, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_ERRNO_H */
diff --git a/usr/src/common/brand/lx/lx_signum.c b/usr/src/common/brand/lx/lx_signum.c
new file mode 100644
index 0000000000..9c861c282a
--- /dev/null
+++ b/usr/src/common/brand/lx/lx_signum.c
@@ -0,0 +1,339 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/signal.h>
+#include <sys/lx_siginfo.h>
+#include <lx_signum.h>
+#include <sys/debug.h>
+
+/*
+ * Delivering signals to a Linux process is complicated by differences in
+ * signal numbering, stack structure and contents, and the action taken when a
+ * signal handler exits. In addition, many signal-related structures, such as
+ * sigset_ts, vary between Solaris and Linux.
+ *
+ * The simplest transformation that must be done when sending signals is to
+ * translate between Linux and Solaris signal numbers.
+ *
+ * These are the major signal number differences between Linux and Solaris:
+ *
+ * ====================================
+ * | Number | Linux | Solaris |
+ * | ====== | ========= | ========== |
+ * | 7 | SIGBUS | SIGEMT |
+ * | 10 | SIGUSR1 | SIGBUS |
+ * | 12 | SIGUSR2 | SIGSYS |
+ * | 16 | SIGSTKFLT | SIGUSR1 |
+ * | 17 | SIGCHLD | SIGUSR2 |
+ * | 18 | SIGCONT | SIGCHLD |
+ * | 19 | SIGSTOP | SIGPWR |
+ * | 20 | SIGTSTP | SIGWINCH |
+ * | 21 | SIGTTIN | SIGURG |
+ * | 22 | SIGTTOU | SIGPOLL |
+ * | 23 | SIGURG | SIGSTOP |
+ * | 24 | SIGXCPU | SIGTSTP |
+ * | 25 | SIGXFSZ | SIGCONT |
+ * | 26 | SIGVTALARM | SIGTTIN |
+ * | 27 | SIGPROF | SIGTTOU |
+ * | 28 | SIGWINCH | SIGVTALARM |
+ * | 29 | SIGPOLL | SIGPROF |
+ * | 30 | SIGPWR | SIGXCPU |
+ * | 31 | SIGSYS | SIGXFSZ |
+ * ====================================
+ *
+ * Not every Linux signal maps to a Solaris signal, nor does every Solaris
+ * signal map to a Linux counterpart. However, when signals do map, the
+ * mapping is unique.
+ *
+ * One mapping issue is that Linux supports 33 real time signals, with SIGRTMIN
+ * typically starting at or near 32 (SIGRTMIN) and proceeding to 64 (SIGRTMAX)
+ * (SIGRTMIN is "at or near" 32 because glibc usually "steals" one ore more of
+ * these signals for its own internal use, adjusting SIGRTMIN and SIGRTMAX as
+ * needed.) Conversely, Solaris actively uses signals 32-40 for other purposes
+ * and supports exactly 32 real time signals, in the range 41 (SIGRTMIN)
+ * to 72 (SIGRTMAX).
+ *
+ * At present, attempting to translate a Linux signal equal to 63
+ * will generate an error (we allow SIGRTMAX because a program
+ * should be able to send SIGRTMAX without getting an EINVAL, though obviously
+ * anything that loops through the signals from SIGRTMIN to SIGRTMAX will
+ * fail.)
+ *
+ * Similarly, attempting to translate a native Solaris signal in the range
+ * 32-40 will also generate an error as we don't want to support the receipt of
+ * those signals from the Solaris global zone.
+ */
+
+/*
+ * Linux to Solaris signal map
+ *
+ * Usage: solaris_signal = ltos_signum[lx_signal];
+ */
+const int
+ltos_signo[LX_NSIG + 1] = {
+ 0,
+ SIGHUP,
+ SIGINT,
+ SIGQUIT,
+ SIGILL,
+ SIGTRAP,
+ SIGABRT,
+ SIGBUS,
+ SIGFPE,
+ SIGKILL,
+ SIGUSR1,
+ SIGSEGV,
+ SIGUSR2,
+ SIGPIPE,
+ SIGALRM,
+ SIGTERM,
+ SIGEMT, /* 16: Linux SIGSTKFLT; use Solaris SIGEMT */
+ SIGCHLD,
+ SIGCONT,
+ SIGSTOP,
+ SIGTSTP,
+ SIGTTIN,
+ SIGTTOU,
+ SIGURG,
+ SIGXCPU,
+ SIGXFSZ,
+ SIGVTALRM,
+ SIGPROF,
+ SIGWINCH,
+ SIGPOLL,
+ SIGPWR,
+ SIGSYS,
+ _SIGRTMIN, /* 32: Linux SIGRTMIN */
+ _SIGRTMIN + 1,
+ _SIGRTMIN + 2,
+ _SIGRTMIN + 3,
+ _SIGRTMIN + 4,
+ _SIGRTMIN + 5,
+ _SIGRTMIN + 6,
+ _SIGRTMIN + 7,
+ _SIGRTMIN + 8,
+ _SIGRTMIN + 9,
+ _SIGRTMIN + 10,
+ _SIGRTMIN + 11,
+ _SIGRTMIN + 12,
+ _SIGRTMIN + 13,
+ _SIGRTMIN + 14,
+ _SIGRTMIN + 15,
+ _SIGRTMIN + 16,
+ _SIGRTMIN + 17,
+ _SIGRTMIN + 18,
+ _SIGRTMIN + 19,
+ _SIGRTMIN + 20,
+ _SIGRTMIN + 21,
+ _SIGRTMIN + 22,
+ _SIGRTMIN + 23,
+ _SIGRTMIN + 24,
+ _SIGRTMIN + 25,
+ _SIGRTMIN + 26,
+ _SIGRTMIN + 27,
+ _SIGRTMIN + 28,
+ _SIGRTMIN + 29,
+ _SIGRTMIN + 30,
+ _SIGRTMIN + 31,
+ _SIGRTMAX, /* 64: Linux SIGRTMAX */
+};
+
+/*
+ * Solaris to Linux signal map
+ *
+ * Usage: lx_signal = stol_signo[solaris_signal];
+ */
+const int
+stol_signo[NSIG] = {
+ 0,
+ LX_SIGHUP,
+ LX_SIGINT,
+ LX_SIGQUIT,
+ LX_SIGILL,
+ LX_SIGTRAP,
+ LX_SIGABRT,
+ LX_SIGSTKFLT, /* 7: Solaris SIGEMT; use for LX_SIGSTKFLT */
+ LX_SIGFPE,
+ LX_SIGKILL,
+ LX_SIGBUS,
+ LX_SIGSEGV,
+ LX_SIGSYS,
+ LX_SIGPIPE,
+ LX_SIGALRM,
+ LX_SIGTERM,
+ LX_SIGUSR1,
+ LX_SIGUSR2,
+ LX_SIGCHLD,
+ LX_SIGPWR,
+ LX_SIGWINCH,
+ LX_SIGURG,
+ LX_SIGPOLL,
+ LX_SIGSTOP,
+ LX_SIGTSTP,
+ LX_SIGCONT,
+ LX_SIGTTIN,
+ LX_SIGTTOU,
+ LX_SIGVTALRM,
+ LX_SIGPROF,
+ LX_SIGXCPU,
+ LX_SIGXFSZ,
+ -1, /* 32: Solaris SIGWAITING */
+ -1, /* 33: Solaris SIGLWP */
+ -1, /* 34: Solaris SIGFREEZE */
+ -1, /* 35: Solaris SIGTHAW */
+ -1, /* 36: Solaris SIGCANCEL */
+ -1, /* 37: Solaris SIGLOST */
+ -1, /* 38: Solaris SIGXRES */
+ -1, /* 39: Solaris SIGJVM1 */
+ -1, /* 40: Solaris SIGJVM2 */
+ -1, /* 41: Solaris SIGINFO */
+ LX_SIGRTMIN, /* 42: Solaris _SIGRTMIN */
+ LX_SIGRTMIN + 1,
+ LX_SIGRTMIN + 2,
+ LX_SIGRTMIN + 3,
+ LX_SIGRTMIN + 4,
+ LX_SIGRTMIN + 5,
+ LX_SIGRTMIN + 6,
+ LX_SIGRTMIN + 7,
+ LX_SIGRTMIN + 8,
+ LX_SIGRTMIN + 9,
+ LX_SIGRTMIN + 10,
+ LX_SIGRTMIN + 11,
+ LX_SIGRTMIN + 12,
+ LX_SIGRTMIN + 13,
+ LX_SIGRTMIN + 14,
+ LX_SIGRTMIN + 15,
+ LX_SIGRTMIN + 16,
+ LX_SIGRTMIN + 17,
+ LX_SIGRTMIN + 18,
+ LX_SIGRTMIN + 19,
+ LX_SIGRTMIN + 20,
+ LX_SIGRTMIN + 21,
+ LX_SIGRTMIN + 22,
+ LX_SIGRTMIN + 23,
+ LX_SIGRTMIN + 24,
+ LX_SIGRTMIN + 25,
+ LX_SIGRTMIN + 26,
+ LX_SIGRTMIN + 27,
+ LX_SIGRTMIN + 28,
+ LX_SIGRTMIN + 29,
+ LX_SIGRTMIN + 30,
+ LX_SIGRTMIN + 31,
+ LX_SIGRTMAX, /* 74: Solaris _SIGRTMAX */
+};
+
+/*
+ * Convert an illumos native signal number to a Linux signal number and return
+ * it. If no valid conversion is possible, the function fails back to the
+ * value of "defsig". In userland, passing a default signal number of "-1"
+ * will abort the program if the signal number could not be converted.
+ */
+int
+lx_stol_signo(int signo, int defsig)
+{
+ int rval;
+
+#ifdef _KERNEL
+ VERIFY3S(defsig, >=, 0);
+#endif
+
+ if (signo < 0 || signo >= NSIG || (rval = stol_signo[signo]) < 1) {
+#ifndef _KERNEL
+ VERIFY3S(defsig, >=, 0);
+#endif
+ return (defsig);
+ }
+
+ return (rval);
+}
+
+
+/*
+ * Convert a Linux signal number to an illumos signal number and return it.
+ * Error behavior is identical to lx_stol_signo.
+ */
+int
+lx_ltos_signo(int signo, int defsig)
+{
+#ifdef _KERNEL
+ VERIFY3S(defsig, >=, 0);
+#endif
+
+ if (signo < 1 || signo >= NSIG) {
+#ifndef _KERNEL
+ VERIFY3S(defsig, >=, 0);
+#endif
+ return (defsig);
+ }
+
+ return (ltos_signo[signo]);
+}
+
+/*
+ * Convert the "status" field of a SIGCLD siginfo_t. We need to extract the
+ * illumos signal number and convert it to a Linux signal number while leaving
+ * the ptrace(2) event bits intact. In userland, passing a default signal
+ * number of "-1" will abort the program if the signal number could not be
+ * converted, as for lx_stol_signo().
+ */
+int
+lx_stol_status(int s, int defsig)
+{
+ /*
+ * We mask out the top bit here in case PTRACE_O_TRACESYSGOOD
+ * is in use and 0x80 has been ORed with the signal number.
+ */
+ int stat = lx_stol_signo(s & 0x7f, defsig);
+
+ /*
+ * We must mix in the ptrace(2) event which may be stored in
+ * the second byte of the status code. We also re-include the
+ * PTRACE_O_TRACESYSGOOD bit.
+ */
+ return ((s & 0xff80) | stat);
+}
+
+int
+lx_stol_sigcode(int code)
+{
+ switch (code) {
+ case SI_USER:
+ return (LX_SI_USER);
+ case SI_LWP:
+ return (LX_SI_TKILL);
+ case SI_QUEUE:
+ return (LX_SI_QUEUE);
+ case SI_TIMER:
+ return (LX_SI_TIMER);
+ case SI_ASYNCIO:
+ return (LX_SI_ASYNCIO);
+ case SI_MESGQ:
+ return (LX_SI_MESGQ);
+ default:
+ return (code);
+ }
+}
diff --git a/usr/src/common/brand/lx/lx_signum.h b/usr/src/common/brand/lx/lx_signum.h
new file mode 100644
index 0000000000..b6c5f32731
--- /dev/null
+++ b/usr/src/common/brand/lx/lx_signum.h
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _LX_SIGNUM_H
+#define _LX_SIGNUM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LX_SIGHUP 1
+#define LX_SIGINT 2
+#define LX_SIGQUIT 3
+#define LX_SIGILL 4
+#define LX_SIGTRAP 5
+#define LX_SIGABRT 6
+#define LX_SIGIOT 6
+#define LX_SIGBUS 7
+#define LX_SIGFPE 8
+#define LX_SIGKILL 9
+#define LX_SIGUSR1 10
+#define LX_SIGSEGV 11
+#define LX_SIGUSR2 12
+#define LX_SIGPIPE 13
+#define LX_SIGALRM 14
+#define LX_SIGTERM 15
+#define LX_SIGSTKFLT 16
+#define LX_SIGCHLD 17
+#define LX_SIGCONT 18
+#define LX_SIGSTOP 19
+#define LX_SIGTSTP 20
+#define LX_SIGTTIN 21
+#define LX_SIGTTOU 22
+#define LX_SIGURG 23
+#define LX_SIGXCPU 24
+#define LX_SIGXFSZ 25
+#define LX_SIGVTALRM 26
+#define LX_SIGPROF 27
+#define LX_SIGWINCH 28
+#define LX_SIGIO 29
+#define LX_SIGPOLL LX_SIGIO
+#define LX_SIGPWR 30
+#define LX_SIGSYS 31
+#define LX_SIGUNUSED 31
+
+#define LX_NSIG 64 /* Linux _NSIG */
+
+#define LX_SIGRTMIN 32
+#define LX_SIGRTMAX LX_NSIG
+
+extern const int ltos_signo[];
+extern const int stol_signo[];
+
+extern int lx_stol_signo(int, int);
+extern int lx_ltos_signo(int, int);
+extern int lx_stol_status(int, int);
+extern int lx_stol_sigcode(int);
+
+/*
+ * NOTE: Linux uses different definitions for 'sigset_t's and 'sigaction_t's
+ * depending on whether the definition is for user space or the kernel.
+ *
+ * The definitions below MUST correspond to the Linux kernel versions,
+ * as glibc will do the necessary translation from the Linux user
+ * versions.
+ */
+#if defined(_LP64)
+#define LX_NSIG_WORDS 1
+#define LX_WSHIFT 6
+#elif defined(_ILP32)
+#define LX_NSIG_WORDS 2
+#define LX_WSHIFT 5
+#else
+#error "LX only supports LP64 and ILP32"
+#endif
+
+typedef struct {
+ ulong_t __bits[LX_NSIG_WORDS];
+} lx_sigset_t;
+
+#define LX_NBITS (sizeof (ulong_t) * NBBY)
+#define lx_sigmask(n) (1UL << (((n) - 1) % LX_NBITS))
+#define lx_sigword(n) (((ulong_t)((n) - 1)) >> LX_WSHIFT)
+#define lx_sigismember(s, n) (lx_sigmask(n) & (s)->__bits[lx_sigword(n)])
+#define lx_sigaddset(s, n) ((s)->__bits[lx_sigword(n)] |= lx_sigmask(n))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_SIGNUM_H */
diff --git a/usr/src/common/brand/lx/lx_syscall.h b/usr/src/common/brand/lx/lx_syscall.h
new file mode 100644
index 0000000000..01e8b79512
--- /dev/null
+++ b/usr/src/common/brand/lx/lx_syscall.h
@@ -0,0 +1,123 @@
+/*
+ * 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 _LX_SYSCALL_H
+#define _LX_SYSCALL_H
+
+#include <sys/lx_brand.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The br_scall_args field of lx_lwp_data is going to be populated with
+ * pointers to structs. The types of these structs should be defined in this
+ * header file. These are Linux specific arguments to system calls that don't
+ * exist in illumos. Each section should be labelled with which system call it
+ * belongs to.
+ */
+
+/* arguments for waitpid(2) */
+/* see comments in usr/src/lib/brand/lx/lx_brand/common/wait.c */
+#define LX_WNOTHREAD 0x20000000 /* Do not wait on siblings' children */
+#define LX_WALL 0x40000000 /* Wait on all children */
+#define LX_WCLONE 0x80000000 /* Wait only on clone children */
+
+/* For arch_prctl(2) */
+#define LX_ARCH_SET_GS 0x1001
+#define LX_ARCH_SET_FS 0x1002
+#define LX_ARCH_GET_FS 0x1003
+#define LX_ARCH_GET_GS 0x1004
+
+/*
+ * For ptrace(2):
+ */
+#define LX_PTRACE_TRACEME 0
+#define LX_PTRACE_PEEKTEXT 1
+#define LX_PTRACE_PEEKDATA 2
+#define LX_PTRACE_PEEKUSER 3
+#define LX_PTRACE_POKETEXT 4
+#define LX_PTRACE_POKEDATA 5
+#define LX_PTRACE_POKEUSER 6
+#define LX_PTRACE_CONT 7
+#define LX_PTRACE_KILL 8
+#define LX_PTRACE_SINGLESTEP 9
+#define LX_PTRACE_GETREGS 12
+#define LX_PTRACE_SETREGS 13
+#define LX_PTRACE_GETFPREGS 14
+#define LX_PTRACE_SETFPREGS 15
+#define LX_PTRACE_ATTACH 16
+#define LX_PTRACE_DETACH 17
+#define LX_PTRACE_GETFPXREGS 18
+#define LX_PTRACE_SETFPXREGS 19
+#define LX_PTRACE_SYSCALL 24
+#define LX_PTRACE_SETOPTIONS 0x4200
+#define LX_PTRACE_GETEVENTMSG 0x4201
+#define LX_PTRACE_GETSIGINFO 0x4202
+
+/*
+ * For clone(2):
+ */
+#define LX_CSIGNAL 0x000000ff
+#define LX_CLONE_VM 0x00000100
+#define LX_CLONE_FS 0x00000200
+#define LX_CLONE_FILES 0x00000400
+#define LX_CLONE_SIGHAND 0x00000800
+#define LX_CLONE_PID 0x00001000
+#define LX_CLONE_PTRACE 0x00002000
+#define LX_CLONE_VFORK 0x00004000
+#define LX_CLONE_PARENT 0x00008000
+#define LX_CLONE_THREAD 0x00010000
+#define LX_CLONE_NEWNS 0x00020000
+#define LX_CLONE_SYSVSEM 0x00040000
+#define LX_CLONE_SETTLS 0x00080000
+#define LX_CLONE_PARENT_SETTID 0x00100000
+#define LX_CLONE_CHILD_CLEARTID 0x00200000
+#define LX_CLONE_DETACH 0x00400000
+#define LX_CLONE_UNTRACED 0x00800000
+#define LX_CLONE_CHILD_SETTID 0x01000000
+#define LX_CLONE_NEWCGROUP 0x02000000
+#define LX_CLONE_NEWUTS 0x04000000
+#define LX_CLONE_NEWIPC 0x08000000
+#define LX_CLONE_NEWUSER 0x10000000
+#define LX_CLONE_NEWPID 0x20000000
+#define LX_CLONE_NEWNET 0x40000000
+#define LX_CLONE_IO 0x80000000
+
+#define SHARED_AS \
+ (LX_CLONE_VM | LX_CLONE_FS | LX_CLONE_FILES | LX_CLONE_SIGHAND | \
+ LX_CLONE_THREAD)
+
+/*
+ * Valid clone flags when not a full process or full thread (SHARED_AS), This
+ * can be expanded as additional clone-group support is added.
+ */
+#define LX_CLONE_GRP_SUBSET (LX_CLONE_FS)
+
+#define LX_IS_CLONE_GRP(X) ((X & SHARED_AS) != 0 && \
+ (X & SHARED_AS) != SHARED_AS && \
+ ((X & SHARED_AS) & ~LX_CLONE_GRP_SUBSET) == 0)
+
+#define LX_CLONE_NS_UNSUP (LX_CLONE_NEWNS | LX_CLONE_NEWCGROUP | \
+ LX_CLONE_NEWUTS | LX_CLONE_NEWIPC | \
+ LX_CLONE_NEWUSER | LX_CLONE_NEWPID | \
+ LX_CLONE_NEWNET | LX_CLONE_IO)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_SYSCALL_H */
diff --git a/usr/src/common/brand/lx/tools/Makefile b/usr/src/common/brand/lx/tools/Makefile
new file mode 100644
index 0000000000..2b5bb92251
--- /dev/null
+++ b/usr/src/common/brand/lx/tools/Makefile
@@ -0,0 +1,47 @@
+#
+# 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 2018 Joyent, Inc.
+#
+
+PROG = gen_errno
+
+include ../../../../cmd/Makefile.cmd
+
+OBJS = gen_errno.o
+
+CLOBBERFILES += $(PROG)
+
+NATIVECC_CFLAGS += $(CFLAGS) $(CCVERBOSE)
+# As evidenced by the use of the NATIVE_ variables, gen_errno is intended
+# to be able to run on the build host. We continue to link it against
+# libcmdutils.so instead of libcustr.so in order to allow it to run on
+# older build hosts (relying on the libcmdutil filter entries if run on
+# newer hosts with libcustr.so).
+NATIVECC_LDLIBS += -lcmdutils -lnvpair
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all
+
+lint: lint_PROG
+
+clean:
+ $(RM) $(OBJS)
+
+$(PROG): $(OBJS)
+ $(NATIVECC) $(NATIVECC_CFLAGS) $(NATIVECC_LDLIBS) $(OBJS) -o $@
+ $(POST_PROCESS)
+
+include ../../../../cmd/Makefile.targ
diff --git a/usr/src/common/brand/lx/tools/README.md b/usr/src/common/brand/lx/tools/README.md
new file mode 100644
index 0000000000..5e4976f200
--- /dev/null
+++ b/usr/src/common/brand/lx/tools/README.md
@@ -0,0 +1,39 @@
+# Updating Error Number Translations
+
+To create an updated error number translation table, you can use the
+`gen_errno` tool. This tool requires, as input:
+
+* the illumos native `errno.h` file
+* a set of foreign operating system `errno.h` files
+
+The output is a set of translation table entries suitable for inclusion in a
+cstyled C array. The index of the array is the native error number and the
+value at each index is the translated error number for use with the foreign
+operating system.
+
+## Example
+
+To generate a translation table for the LX Brand, you will require two files
+from the current Linux source:
+
+* `include/uapi/asm-generic/errno-base.h` (low-valued, or base, error numbers)
+* `include/uapi/asm-generic/errno.h` (extended error numbers)
+
+Assuming the files are in the current directory, you should run the tool as
+follows:
+
+ $ dmake
+ ...
+ $ ./gen_errno -F errno-base.h -F errno.h \
+ -N $SRC/uts/common/sys/errno.h
+ 0, /* 0: No Error */
+ 1, /* 1: EPERM --> 1: EPERM */
+ 2, /* 2: ENOENT --> 2: ENOENT */
+ 3, /* 3: ESRCH --> 3: ESRCH */
+ 4, /* 4: EINTR --> 4: EINTR */
+ 5, /* 5: EIO --> 5: EIO */
+ 6, /* 6: ENXIO --> 6: ENXIO */
+ 7, /* 7: E2BIG --> 7: E2BIG */
+ ...
+
+The output may be used in the `$SRC/common/brand/lx/lx_errno.c` file.
diff --git a/usr/src/common/brand/lx/tools/gen_errno.c b/usr/src/common/brand/lx/tools/gen_errno.c
new file mode 100644
index 0000000000..6089fed3bd
--- /dev/null
+++ b/usr/src/common/brand/lx/tools/gen_errno.c
@@ -0,0 +1,444 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+/*
+ * Take the error number definitions from a foreign system and generate a
+ * translation table that converts illumos native error numbers to foreign
+ * system error numbers.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+#include <sys/sysmacros.h>
+#include <libcustr.h>
+#include <libnvpair.h>
+
+nvlist_t *native_errors;
+nvlist_t *foreign_errors;
+
+struct override {
+ const char *ovr_from;
+ const char *ovr_to;
+} overrides[] = {
+ { "ENOTSUP", "ENOSYS" },
+ { 0 }
+};
+
+static const char *
+lookup_override(const char *from)
+{
+ int i;
+
+ for (i = 0; overrides[i].ovr_from != NULL; i++) {
+ if (strcmp(overrides[i].ovr_from, from) == 0) {
+ return (overrides[i].ovr_to);
+ }
+ }
+
+ return (NULL);
+}
+
+static int
+parse_int(const char *number, int *rval)
+{
+ long n;
+ char *endpos;
+
+ errno = 0;
+ if ((n = strtol(number, &endpos, 10)) == 0 && errno != 0) {
+ return (-1);
+ }
+
+ if (endpos != NULL && *endpos != '\0') {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (n > INT_MAX || n < INT_MIN) {
+ errno = EOVERFLOW;
+ return (-1);
+ }
+
+ *rval = (int)n;
+ return (0);
+}
+
+static int
+errnum_add(nvlist_t *nvl, const char *name, const char *number)
+{
+ int val;
+
+ if (nvlist_exists(nvl, name)) {
+ (void) fprintf(stderr, "ERROR: duplicate definition: %s -> "
+ "%s\n", name, number);
+ errno = EEXIST;
+ return (-1);
+ }
+
+ /*
+ * Try and parse the error number:
+ */
+ if (parse_int(number, &val) == 0) {
+ /*
+ * The name refers to a number.
+ */
+ if (nvlist_add_int32(nvl, name, val) != 0) {
+ (void) fprintf(stderr, "ERROR: nvlist_add_int32: %s\n",
+ strerror(errno));
+ return (-1);
+ }
+ } else {
+ /*
+ * The name refers to another definition.
+ */
+ if (nvlist_add_string(nvl, name, number) != 0) {
+ (void) fprintf(stderr, "ERROR: nvlist_add_string: %s\n",
+ strerror(errno));
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+errnum_max(nvlist_t *nvl)
+{
+ int max = 0;
+ nvpair_t *nvp = NULL;
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ if (nvpair_type(nvp) != DATA_TYPE_INT32) {
+ continue;
+ }
+
+ max = MAX(fnvpair_value_int32(nvp), max);
+ }
+
+ return (max);
+}
+
+static int
+errname_by_num(nvlist_t *nvl, int num, const char **name)
+{
+ nvpair_t *nvp = NULL;
+
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+ if (nvpair_type(nvp) != DATA_TYPE_INT32) {
+ continue;
+ }
+
+ if (fnvpair_value_int32(nvp) == num) {
+ *name = nvpair_name(nvp);
+ return (0);
+ }
+ }
+
+ errno = ENOENT;
+ return (-1);
+}
+
+static int
+errno_by_name(nvlist_t *nvl, const char *name, int *rval, const char **rname)
+{
+ nvpair_t *nvp = NULL;
+
+ if (nvlist_lookup_nvpair(nvl, name, &nvp) != 0) {
+ errno = ENOENT;
+ return (-1);
+ }
+
+ if (nvpair_type(nvp) == DATA_TYPE_STRING) {
+ return (errno_by_name(nvl, fnvpair_value_string(nvp), rval,
+ rname));
+ } else {
+ *rval = fnvpair_value_int32(nvp);
+ if (rname != NULL) {
+ *rname = name;
+ }
+ return (0);
+ }
+}
+
+static int
+process_line(const char *line, nvlist_t *nvl)
+{
+ custr_t *nam = NULL, *num = NULL;
+ const char *c = line;
+
+ if (custr_alloc(&nam) != 0 || custr_alloc(&num) != 0) {
+ int en = errno;
+
+ custr_free(nam);
+ custr_free(num);
+
+ errno = en;
+ return (-1);
+ }
+
+ /*
+ * Valid lines begin with "#define":
+ */
+ if (*c++ != '#' || *c++ != 'd' || *c++ != 'e' || *c++ != 'f' ||
+ *c++ != 'i' || *c++ != 'n' || *c++ != 'e') {
+ return (0);
+ }
+
+ /*
+ * Eat whitespace:
+ */
+ for (;;) {
+ if (*c == '\0') {
+ return (0);
+ }
+
+ if (*c != ' ' && *c != '\t') {
+ break;
+ }
+
+ c++;
+ }
+
+ /*
+ * Read error number token:
+ */
+ for (;;) {
+ if (*c == '\0') {
+ return (0);
+ }
+
+ if (*c == ' ' || *c == '\t') {
+ break;
+ }
+
+ if (custr_appendc(nam, *c) != 0) {
+ return (-1);
+ }
+
+ c++;
+ }
+
+ /*
+ * Eat whitespace:
+ */
+ for (;;) {
+ if (*c == '\0') {
+ return (0);
+ }
+
+ if (*c != ' ' && *c != '\t') {
+ break;
+ }
+
+ c++;
+ }
+
+ /*
+ * Read error number token:
+ */
+ for (;;) {
+ if (*c == '\0') {
+ break;
+ }
+
+ if (*c == ' ' || *c == '\t') {
+ break;
+ }
+
+ if (custr_appendc(num, *c) != 0) {
+ return (-1);
+ }
+
+ c++;
+ }
+
+ return (errnum_add(nvl, custr_cstr(nam), custr_cstr(num)));
+}
+
+static int
+read_file_into_list(const char *path, nvlist_t *nvl)
+{
+ int rval = 0, en = 0;
+ FILE *f;
+ custr_t *cu = NULL;
+
+ if (custr_alloc(&cu) != 0) {
+ return (-1);
+ }
+
+ if ((f = fopen(path, "r")) == NULL) {
+ custr_free(cu);
+ return (-1);
+ }
+
+ for (;;) {
+ int c;
+
+ errno = 0;
+ switch (c = fgetc(f)) {
+ case '\n':
+ case EOF:
+ if (errno != 0) {
+ en = errno;
+ rval = -1;
+ goto out;
+ }
+ if (process_line(custr_cstr(cu), nvl) != 0) {
+ en = errno;
+ rval = -1;
+ goto out;
+ }
+ custr_reset(cu);
+ if (c == EOF) {
+ goto out;
+ }
+ break;
+
+ case '\r':
+ case '\0':
+ /*
+ * Ignore these characters.
+ */
+ break;
+
+ default:
+ if (custr_appendc(cu, c) != 0) {
+ en = errno;
+ rval = -1;
+ goto out;
+ }
+ break;
+ }
+ }
+
+out:
+ (void) fclose(f);
+ custr_free(cu);
+ errno = en;
+ return (rval);
+}
+
+int
+main(int argc, char **argv)
+{
+ int max;
+ int fval;
+ int c;
+
+ if (nvlist_alloc(&native_errors, NV_UNIQUE_NAME, 0) != 0 ||
+ nvlist_alloc(&foreign_errors, NV_UNIQUE_NAME, 0) != 0) {
+ err(1, "could not allocate memory");
+ }
+
+ while ((c = getopt(argc, argv, ":N:F:")) != -1) {
+ switch (c) {
+ case 'N':
+ if (read_file_into_list(optarg, native_errors) != 0) {
+ err(1, "could not read file: %s", optarg);
+ }
+ break;
+
+ case 'F':
+ if (read_file_into_list(optarg, foreign_errors) != 0) {
+ err(1, "could not read file: %s", optarg);
+ }
+ break;
+
+ case ':':
+ errx(1, "option -%c requires an operand", c);
+ break;
+
+ case '?':
+ errx(1, "option -%c unrecognised", c);
+ break;
+ }
+ }
+
+ /*
+ * Print an array entry for each error number:
+ */
+ max = errnum_max(native_errors);
+ for (fval = 0; fval <= max; fval++) {
+ const char *fname;
+ const char *tname = NULL;
+ int32_t tval;
+ const char *msg = NULL;
+ const char *comma = (fval != max) ? "," : "";
+
+ if (errname_by_num(native_errors, fval, &fname) == -1) {
+ fname = NULL;
+ }
+
+ if (fval == 0) {
+ /*
+ * The error number "0" is special: it means no worries.
+ */
+ msg = "No Error";
+ tval = 0;
+ } else if (fname == NULL) {
+ /*
+ * There is no defined name for this error number; it
+ * is unused.
+ */
+ msg = "Unused Number";
+ tval = -1;
+ } else {
+ /*
+ * Check if we want to override the name of this error
+ * in the foreign error number lookup:
+ */
+ const char *oname = lookup_override(fname);
+
+ /*
+ * Do the lookup:
+ */
+ if (errno_by_name(foreign_errors, oname != NULL ?
+ oname : fname, &tval, &tname) != 0) {
+ /*
+ * There was no foreign error number by that
+ * name.
+ */
+ tname = "No Analogue";
+ tval = -2;
+ }
+ }
+
+ if (msg == NULL) {
+ size_t flen = strlen(fname);
+ size_t tlen = strlen(tname);
+ const char *t = flen > 7 ? "\t" : "\t\t";
+ const char *tt = tlen < 7 ? "\t\t\t" : tlen < 15 ?
+ "\t\t" : "\t";
+
+ (void) fprintf(stdout, "\t%d%s\t/* %3d: %s%s--> %3d: "
+ "%s%s*/\n", tval, comma, fval, fname, t, tval,
+ tname, tt);
+ } else {
+ const char *t = "\t\t\t\t\t";
+
+ (void) fprintf(stdout, "\t%d%s\t/* %3d: %s%s*/\n", tval,
+ comma, fval, msg, t);
+ }
+ }
+
+ (void) nvlist_free(native_errors);
+ (void) nvlist_free(foreign_errors);
+
+ return (0);
+}
diff --git a/usr/src/common/crypto/chacha/chacha.c b/usr/src/common/crypto/chacha/chacha.c
index cef4aac466..3665ae28d4 100644
--- a/usr/src/common/crypto/chacha/chacha.c
+++ b/usr/src/common/crypto/chacha/chacha.c
@@ -1,13 +1,25 @@
/*
+ * This implementation of ChaCha20 comes from the initial Dan Bernstein
+ * implementation, including a 256-bit key, a 64-bit nonce and a 64-bit
+ * counter. This is in contrast to ChaCha20 as defined in RFC 7539, which
+ * defines a 256-bit key, a 96-bit nonce and a 32-bit counter. In particular,
+ * kernel crash dump encryption relies on the fact that our larger counter
+ * allows for the encryption of very large messages (many gigabytes in
+ * length); any change to this implementation that reduces the size of the
+ * counter should be mindful of this use case.
+ */
+
+/*
chacha-merged.c version 20080118
D. J. Bernstein
Public domain.
*/
-/* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */
+/* $OpenBSD: chacha.c,v 1.1 2013/11/21 00:45:44 djm Exp $ */
-#include <chacha.h>
-#include <stddef.h>
+#include "chacha.h"
+#include <sys/stddef.h>
+#include <sys/null.h>
typedef unsigned char u8;
typedef unsigned int u32;
@@ -76,10 +88,10 @@ chacha_keysetup(chacha_ctx_t *x, const u8 *k, u32 kbits, u32 ivbits __unused)
}
void
-chacha_ivsetup(chacha_ctx_t *x, const u8 *iv)
+chacha_ivsetup(chacha_ctx_t *x,const u8 *iv, const u8 *counter)
{
- x->chacha_input[12] = 0;
- x->chacha_input[13] = 0;
+ x->chacha_input[12] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 0);
+ x->chacha_input[13] = counter == NULL ? 0 : U8TO32_LITTLE(counter + 4);
x->chacha_input[14] = U8TO32_LITTLE(iv + 0);
x->chacha_input[15] = U8TO32_LITTLE(iv + 4);
}
diff --git a/usr/src/common/crypto/chacha/chacha.h b/usr/src/common/crypto/chacha/chacha.h
index ac9993a8a4..edadca4934 100644
--- a/usr/src/common/crypto/chacha/chacha.h
+++ b/usr/src/common/crypto/chacha/chacha.h
@@ -10,7 +10,7 @@
*/
/*
- * Copyright (c) 2015, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _CHACHA_H
@@ -27,7 +27,7 @@
* over the data and xoring it with the generated cipher.
*/
-#include <inttypes.h>
+#include <sys/inttypes.h>
#ifdef __cplusplus
extern "C" {
@@ -39,7 +39,7 @@ typedef struct chacha_ctx {
extern void chacha_keysetup(chacha_ctx_t *, const uint8_t *, uint32_t,
uint32_t);
-extern void chacha_ivsetup(chacha_ctx_t *, const uint8_t *);
+extern void chacha_ivsetup(chacha_ctx_t *, const uint8_t *, const uint8_t *);
extern void chacha_encrypt_bytes(chacha_ctx_t *, const uint8_t *, uint8_t *,
uint32_t);
diff --git a/usr/src/common/net/dhcp/octet.c b/usr/src/common/net/dhcp/octet.c
index d8367bbf0b..370604c4e3 100644
--- a/usr/src/common/net/dhcp/octet.c
+++ b/usr/src/common/net/dhcp/octet.c
@@ -77,6 +77,9 @@ octet_to_hexascii(const void *nump, uint_t nlen, char *bufp, uint_t *blen)
* Converts an ASCII string into an octet string.
*
* Returns 0 for success, errno otherwise.
+ *
+ * If the string contains invalid hexadecimal characters, or an odd number of
+ * characters then this function returns EINVAL.
*/
int
hexascii_to_octet(const char *asp, uint_t alen, void *bufp, uint_t *blen)
diff --git a/usr/src/common/pnglite/pnglite.c b/usr/src/common/pnglite/pnglite.c
index 7a30bdc609..5d8b41f9e9 100644
--- a/usr/src/common/pnglite/pnglite.c
+++ b/usr/src/common/pnglite/pnglite.c
@@ -9,6 +9,7 @@
#else
#include <stdio.h>
#include <stdlib.h>
+#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
diff --git a/usr/src/common/refhash/refhash.c b/usr/src/common/refhash/refhash.c
new file mode 100644
index 0000000000..02ba869ef9
--- /dev/null
+++ b/usr/src/common/refhash/refhash.c
@@ -0,0 +1,231 @@
+/*
+ * 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) 2018, Joyent, Inc.
+ */
+
+#include <sys/refhash.h>
+#include <sys/types.h>
+#include <sys/list.h>
+#include <sys/debug.h>
+
+#ifdef _KERNEL
+#include <sys/sysmacros.h>
+#include <sys/ddi.h>
+#include <sys/kmem.h>
+#define REFHASH_ALLOC kmem_alloc
+#define REFHASH_ZALLOC kmem_zalloc
+#define REFHASH_FREE kmem_free
+#else
+#include <stddef.h>
+#include <umem.h>
+#define REFHASH_ALLOC umem_alloc
+#define REFHASH_ZALLOC umem_zalloc
+#define REFHASH_FREE umem_free
+#endif
+
+#define RHL_F_DEAD 0x01
+
+#ifdef lint
+extern refhash_link_t *obj_to_link(refhash_t *, void *);
+extern void *link_to_obj(refhash_t *, refhash_link_t *);
+extern void *obj_to_tag(refhash_t *, void *);
+#else
+#define obj_to_link(_h, _o) \
+ ((refhash_link_t *)(((char *)(_o)) + (_h)->rh_link_off))
+#define link_to_obj(_h, _l) \
+ ((void *)(((char *)(_l)) - (_h)->rh_link_off))
+#define obj_to_tag(_h, _o) \
+ ((void *)(((char *)(_o)) + (_h)->rh_tag_off))
+#endif
+
+refhash_t *
+refhash_create(uint_t bucket_count, refhash_hash_f hash,
+ refhash_cmp_f cmp, refhash_dtor_f dtor, size_t obj_size, size_t link_off,
+ size_t tag_off, int km_flags)
+{
+ refhash_t *hp;
+ uint_t i;
+
+ hp = REFHASH_ALLOC(sizeof (refhash_t), km_flags);
+ if (hp == NULL)
+ return (NULL);
+ hp->rh_buckets = REFHASH_ZALLOC(bucket_count * sizeof (list_t),
+ km_flags);
+ if (hp->rh_buckets == NULL) {
+ REFHASH_FREE(hp, sizeof (refhash_t));
+ return (NULL);
+ }
+ hp->rh_bucket_count = bucket_count;
+
+ for (i = 0; i < bucket_count; i++) {
+ list_create(&hp->rh_buckets[i], sizeof (refhash_link_t),
+ offsetof(refhash_link_t, rhl_chain_link));
+ }
+ list_create(&hp->rh_objs, sizeof (refhash_link_t),
+ offsetof(refhash_link_t, rhl_global_link));
+
+ hp->rh_obj_size = obj_size;
+ hp->rh_link_off = link_off;
+ hp->rh_tag_off = tag_off;
+ hp->rh_hash = hash;
+ hp->rh_cmp = cmp;
+ hp->rh_dtor = dtor;
+
+ return (hp);
+}
+
+void
+refhash_destroy(refhash_t *hp)
+{
+ ASSERT(list_is_empty(&hp->rh_objs));
+
+ REFHASH_FREE(hp->rh_buckets, hp->rh_bucket_count * sizeof (list_t));
+ REFHASH_FREE(hp, sizeof (refhash_t));
+}
+
+void
+refhash_insert(refhash_t *hp, void *op)
+{
+ uint_t bucket;
+ refhash_link_t *lp = obj_to_link(hp, op);
+
+ bucket = hp->rh_hash(obj_to_tag(hp, op)) % hp->rh_bucket_count;
+ list_link_init(&lp->rhl_chain_link);
+ list_link_init(&lp->rhl_global_link);
+ lp->rhl_flags = 0;
+ lp->rhl_refcnt = 0;
+ list_insert_tail(&hp->rh_buckets[bucket], lp);
+ list_insert_tail(&hp->rh_objs, lp);
+}
+
+static void
+refhash_delete(refhash_t *hp, void *op)
+{
+ refhash_link_t *lp = obj_to_link(hp, op);
+ uint_t bucket;
+
+ bucket = hp->rh_hash(obj_to_tag(hp, op)) % hp->rh_bucket_count;
+ list_remove(&hp->rh_buckets[bucket], lp);
+ list_remove(&hp->rh_objs, lp);
+ hp->rh_dtor(op);
+}
+
+void
+refhash_remove(refhash_t *hp, void *op)
+{
+ refhash_link_t *lp = obj_to_link(hp, op);
+
+ if (lp->rhl_refcnt > 0) {
+ lp->rhl_flags |= RHL_F_DEAD;
+ } else {
+ refhash_delete(hp, op);
+ }
+}
+
+void *
+refhash_lookup(refhash_t *hp, const void *tp)
+{
+ uint_t bucket;
+ refhash_link_t *lp;
+ void *op;
+
+ bucket = hp->rh_hash(tp) % hp->rh_bucket_count;
+ for (lp = list_head(&hp->rh_buckets[bucket]); lp != NULL;
+ lp = list_next(&hp->rh_buckets[bucket], lp)) {
+ op = link_to_obj(hp, lp);
+ if (hp->rh_cmp(obj_to_tag(hp, op), tp) == 0 &&
+ !(lp->rhl_flags & RHL_F_DEAD)) {
+ return (op);
+ }
+ }
+
+ return (NULL);
+}
+
+void *
+refhash_linear_search(refhash_t *hp, refhash_eval_f eval, void *arg)
+{
+ void *op;
+ refhash_link_t *lp;
+
+ for (lp = list_head(&hp->rh_objs); lp != NULL;
+ lp = list_next(&hp->rh_objs, lp)) {
+ op = link_to_obj(hp, lp);
+ if (eval(op, arg) == 0)
+ return (op);
+ }
+
+ return (NULL);
+}
+
+void
+refhash_hold(refhash_t *hp, void *op)
+{
+ refhash_link_t *lp = obj_to_link(hp, op);
+
+ ++lp->rhl_refcnt;
+}
+
+void
+refhash_rele(refhash_t *hp, void *op)
+{
+ refhash_link_t *lp = obj_to_link(hp, op);
+
+ ASSERT(lp->rhl_refcnt > 0);
+
+ if (--lp->rhl_refcnt == 0 && (lp->rhl_flags & RHL_F_DEAD))
+ refhash_remove(hp, op);
+}
+
+void *
+refhash_first(refhash_t *hp)
+{
+ refhash_link_t *lp;
+
+ lp = list_head(&hp->rh_objs);
+ if (lp == NULL)
+ return (NULL);
+
+ ++lp->rhl_refcnt;
+
+ return (link_to_obj(hp, lp));
+}
+
+void *
+refhash_next(refhash_t *hp, void *op)
+{
+ refhash_link_t *lp;
+
+ lp = obj_to_link(hp, op);
+ while ((lp = list_next(&hp->rh_objs, lp)) != NULL) {
+ if (!(lp->rhl_flags & RHL_F_DEAD))
+ break;
+ }
+
+ refhash_rele(hp, op);
+ if (lp == NULL)
+ return (NULL);
+
+ ++lp->rhl_refcnt;
+
+ return (link_to_obj(hp, lp));
+}
+
+boolean_t
+refhash_obj_valid(refhash_t *hp, const void *op)
+{
+ /* LINTED - E_ARG_INCOMPATIBLE_WITH_ARG_L */
+ const refhash_link_t *lp = obj_to_link(hp, op);
+
+ return ((lp->rhl_flags & RHL_F_DEAD) != 0);
+}
diff --git a/usr/src/common/zfs/zfs_prop.c b/usr/src/common/zfs/zfs_prop.c
index dd79442179..434e5bc90d 100644
--- a/usr/src/common/zfs/zfs_prop.c
+++ b/usr/src/common/zfs/zfs_prop.c
@@ -547,6 +547,23 @@ zfs_prop_delegatable(zfs_prop_t prop)
return (pd->pd_attr != PROP_READONLY);
}
+boolean_t
+zfs_prop_cacheable(zfs_prop_t prop)
+{
+ /*
+ * It'd be nice if each prop had a flags field which could have flag
+ * like PROP_CACHEABLE, but since zprop_attr_t is an enum and this
+ * setting is orthogonal to the concepts of PROP_READONLY, etc., we have
+ * this function.
+ */
+ return (prop == ZFS_PROP_VERSION ||
+ prop == ZFS_PROP_NORMALIZE ||
+ prop == ZFS_PROP_UTF8ONLY ||
+ prop == ZFS_PROP_CASE ||
+ prop == ZFS_PROP_VOLSIZE ||
+ prop == ZFS_PROP_VOLBLOCKSIZE);
+}
+
/*
* Given a zfs dataset property name, returns the corresponding property ID.
*/
diff --git a/usr/src/common/zfs/zfs_prop.h b/usr/src/common/zfs/zfs_prop.h
index 45423cc72f..3f34ad64a6 100644
--- a/usr/src/common/zfs/zfs_prop.h
+++ b/usr/src/common/zfs/zfs_prop.h
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#ifndef _ZFS_PROP_H
@@ -89,6 +90,7 @@ typedef struct {
void zfs_prop_init(void);
zprop_type_t zfs_prop_get_type(zfs_prop_t);
boolean_t zfs_prop_delegatable(zfs_prop_t prop);
+boolean_t zfs_prop_cacheable(zfs_prop_t prop);
zprop_desc_t *zfs_prop_get_table(void);
/*
diff --git a/usr/src/grub/Makefile b/usr/src/grub/Makefile
index 28c2eca2ff..c31e9a802c 100644
--- a/usr/src/grub/Makefile
+++ b/usr/src/grub/Makefile
@@ -29,6 +29,7 @@ INST_TARGETS += $(ROOT_BOOT_GRUB)/$(GRUB_MENU)
INST_TARGETS += $(ROOT_BOOT_GRUB)/$(INSTALL_MENU)
INST_TARGETS += $(ROOT_BOOT_GRUB)/$(GRUB_DEFAULT)
INST_TARGETS += $(ROOT_BOOT_GRUB)/$(CAPABILITY)
+INST_TARGETS += $(ROOT_USR_SBIN)/grub
$(ROOT_BOOT_GRUB)/$(GRUB_DEFAULT) := FILEMODE = 444
$(ROOT_BOOT_GRUB)/$(CAPABILITY) := FILEMODE = 444
@@ -45,9 +46,14 @@ $(GRUB): FRC
$(ROOT_BOOT_GRUB)/%: $(ROOT_BOOT_GRUB) %
$(INS.file)
+$(ROOT_USR_SBIN)/%: $(GRUB)/grub/grub $(ROOT_USR_SBIN)
+ $(INS.file)
+
$(ROOT_BOOT_GRUB):
$(INS.dir)
+$(ROOT_USR_SBIN):
+ $(INS.dir)
clean clobber: $(SUBDIRS)
diff --git a/usr/src/grub/Makefile.grub b/usr/src/grub/Makefile.grub
index 18354324ae..99942fa2ed 100644
--- a/usr/src/grub/Makefile.grub
+++ b/usr/src/grub/Makefile.grub
@@ -10,3 +10,4 @@ PLATFORM = i86pc
ROOT_BOOT_GRUB = $(ROOT)/boot/grub
ROOT_PLAT_GRUB = $(ROOT)/platform/$(PLATFORM)/boot/grub
ROOT_SRC = $(ROOT)/usr/share/src/grub
+ROOT_USR_SBIN = $(ROOT)/usr/sbin
diff --git a/usr/src/grub/grub-0.97/stage2/boot.c b/usr/src/grub/grub-0.97/stage2/boot.c
index cfc2336a4c..027de7709b 100644
--- a/usr/src/grub/grub-0.97/stage2/boot.c
+++ b/usr/src/grub/grub-0.97/stage2/boot.c
@@ -25,6 +25,8 @@
#include "imgact_aout.h"
#include "i386-elf.h"
+#define SAFE_LOAD_BASE 0xc800000
+
static int cur_addr;
entry_func entry_addr;
static struct mod_list mll[99];
@@ -773,6 +775,17 @@ load_module (char *module, char *arg)
{
int len;
+ /*
+ * XXX Workaround for RICHMOND-16: on some systems, the region
+ * [c700000, c800000) is corrupted by an unknown external (off-CPU) actor(s)
+ * during boot. To be on the safe side, we will simply ensure that every
+ * module is loaded above this region. Note that this means this particular
+ * boot loader supports only systems with at least 200 MB of DRAM plus the
+ * amount of space used by any modules.
+ */
+ if (cur_addr < SAFE_LOAD_BASE)
+ cur_addr = SAFE_LOAD_BASE;
+
/* if we are supposed to load on 4K boundaries */
cur_addr = (cur_addr + 0xFFF) & 0xFFFFF000;
diff --git a/usr/src/grub/grub-0.97/stage2/cmdline.c b/usr/src/grub/grub-0.97/stage2/cmdline.c
index 46c5fda027..6d5591e1de 100644
--- a/usr/src/grub/grub-0.97/stage2/cmdline.c
+++ b/usr/src/grub/grub-0.97/stage2/cmdline.c
@@ -212,8 +212,27 @@ run_script (char *script, char *heap)
intervention. */
if (fallback_entryno < 0)
{
- grub_printf ("\nPress any key to continue...");
- (void) getkey ();
+ int time1, time2 = -1;
+
+ grub_printf (
+ "\nRebooting in 2 minutes (press any key to continue)...");
+ grub_timeout = 120;
+
+ /* using RT clock now, need to initialize value */
+ while ((time1 = getrtsecs()) == 0xFF);
+
+ while (grub_timeout >= 0) {
+ if ((time1 = getrtsecs()) != time2 && time1 != 0xFF) {
+ time2 = time1;
+ grub_timeout--;
+ }
+
+ if (checkkey() >= 0)
+ break;
+ }
+
+ grub_printf ("\nresetting...");
+ grub_reboot();
}
return 1;
diff --git a/usr/src/head/Makefile b/usr/src/head/Makefile
index 87bf0ce786..ac2e8a89f7 100644
--- a/usr/src/head/Makefile
+++ b/usr/src/head/Makefile
@@ -25,13 +25,15 @@
# Copyright 2013 Garrett D'Amore <garrett@damore.org>
# Copyright 2015 Igor Kozhukhov <ikozhukhov@gmail.com>
# Copyright 2017 Nexenta Systems, Inc.
+# Copyright 2017 Joyent, Inc.
#
# include global definitions
include ../Makefile.master
sparc_HDRS=
-i386_HDRS= stack_unwind.h
+i386_HDRS= \
+ stack_unwind.h
KRB5HDRS= \
mit_copyright.h \
@@ -156,6 +158,7 @@ HDRS= $($(MACH)_HDRS) \
regex.h \
regexp.h \
resolv.h \
+ resolv_joy.h \
rje.h \
rtld_db.h \
sac.h \
@@ -221,6 +224,7 @@ HDRS= $($(MACH)_HDRS) \
xlocale.h \
xti.h \
xti_inet.h \
+ zdoor.h \
zone.h
ISOHDRS = \
diff --git a/usr/src/head/libzonecfg.h b/usr/src/head/libzonecfg.h
index 2ca7060101..ffea74505e 100644
--- a/usr/src/head/libzonecfg.h
+++ b/usr/src/head/libzonecfg.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#ifndef _LIBZONECFG_H
@@ -42,6 +43,7 @@ extern "C" {
#include <netinet/in.h>
#include <sys/socket.h>
#include <net/if.h>
+#include <sys/mac.h>
#include <stdio.h>
#include <rctl.h>
#include <zone.h>
@@ -118,6 +120,8 @@ extern "C" {
#define ZONE_STATE_MAXSTRLEN 14
+#define ZONE_PROP_MAXSTRLEN 1024
+
#define LIBZONECFG_PATH "libzonecfg.so.1"
#define ZONE_CONFIG_ROOT "/etc/zones"
@@ -127,6 +131,8 @@ extern "C" {
#define MAXAUTHS 4096
#define ZONE_MGMT_PROF "Zone Management"
+#define ZONE_INT32SZ 11 /* string to hold 32bit int. */
+
/* Owner, group, and mode (defined by packaging) for the config directory */
#define ZONE_CONFIG_UID 0 /* root */
#define ZONE_CONFIG_GID 3 /* sys */
@@ -150,9 +156,11 @@ extern "C" {
#define ALIAS_MAXSEMIDS "max-sem-ids"
#define ALIAS_MAXLOCKEDMEM "locked"
#define ALIAS_MAXSWAP "swap"
+#define ALIAS_MAXPHYSMEM "physical"
#define ALIAS_SHARES "cpu-shares"
#define ALIAS_CPUCAP "cpu-cap"
#define ALIAS_MAXPROCS "max-processes"
+#define ALIAS_ZFSPRI "zfs-io-priority"
/* Default name for zone detached manifest */
#define ZONE_DETACHED "SUNWdetached.xml"
@@ -162,6 +170,11 @@ extern "C" {
*/
#define ZONE_DRY_RUN 0x01
+typedef enum zone_iptype {
+ ZS_SHARED,
+ ZS_EXCLUSIVE
+} zone_iptype_t;
+
/*
* The integer field expresses the current values on a get.
* On a put, it represents the new values if >= 0 or "don't change" if < 0.
@@ -172,6 +185,9 @@ struct zoneent {
char zone_path[MAXPATHLEN]; /* path to zone storage */
uuid_t zone_uuid; /* unique ID for zone */
char zone_newname[ZONENAME_MAX]; /* for doing renames */
+ char zone_brand[MAXNAMELEN]; /* zone's brand */
+ zone_iptype_t zone_iptype; /* zone's IP type */
+ zoneid_t zone_did; /* persistent debug ID */
};
typedef struct zone_dochandle *zone_dochandle_t; /* opaque handle */
@@ -191,15 +207,30 @@ struct zone_fstab {
char zone_fs_raw[MAXPATHLEN]; /* device to fsck */
};
+/*
+ * Generic resource attribute list.
+ * Key/value resource that can be attached to net or device.
+ */
+struct zone_res_attrtab {
+ char zone_res_attr_name[MAXNAMELEN];
+ char zone_res_attr_value[ZONE_PROP_MAXSTRLEN];
+ struct zone_res_attrtab *zone_res_attr_next;
+};
+
struct zone_nwiftab {
char zone_nwif_address[INET6_ADDRSTRLEN]; /* shared-ip only */
char zone_nwif_allowed_address[INET6_ADDRSTRLEN]; /* excl-ip only */
char zone_nwif_physical[LIFNAMSIZ];
+ char zone_nwif_mac[MAXMACADDRLEN]; /* excl-ip only */
+ char zone_nwif_vlan_id[ZONE_INT32SZ]; /* excl-ip only */
+ char zone_nwif_gnic[LIFNAMSIZ]; /* excl-ip only */
char zone_nwif_defrouter[INET6_ADDRSTRLEN];
+ struct zone_res_attrtab *zone_nwif_attrp;
};
struct zone_devtab {
char zone_dev_match[MAXPATHLEN];
+ struct zone_res_attrtab *zone_dev_attrp;
};
struct zone_rctlvaltab {
@@ -230,10 +261,6 @@ struct zone_psettab {
char zone_importance[MAXNAMELEN];
};
-struct zone_mcaptab {
- char zone_physmem_cap[MAXNAMELEN];
-};
-
struct zone_pkgtab {
char zone_pkg_name[MAXNAMELEN];
char zone_pkg_version[ZONE_PKG_VERSMAX];
@@ -271,11 +298,6 @@ typedef struct {
char *zpe_vers;
} zone_pkg_entry_t;
-typedef enum zone_iptype {
- ZS_SHARED,
- ZS_EXCLUSIVE
-} zone_iptype_t;
-
/*
* Basic configuration management routines.
*/
@@ -303,6 +325,7 @@ extern boolean_t zonecfg_valid_importance(char *);
extern int zonecfg_str_to_bytes(char *, uint64_t *);
extern boolean_t zonecfg_valid_memlimit(char *, uint64_t *);
extern boolean_t zonecfg_valid_alias_limit(char *, char *, uint64_t *);
+extern void zonecfg_notify_create(zone_dochandle_t);
/*
* Zone name, path to zone directory, autoboot setting, pool, boot
@@ -324,6 +347,8 @@ extern int zonecfg_set_bootargs(zone_dochandle_t, char *);
extern int zonecfg_get_sched_class(zone_dochandle_t, char *, size_t);
extern int zonecfg_set_sched(zone_dochandle_t, char *);
extern int zonecfg_get_dflt_sched_class(zone_dochandle_t, char *, int);
+extern zoneid_t zonecfg_get_did(zone_dochandle_t);
+extern void zonecfg_set_did(zone_dochandle_t);
/*
* Set/retrieve the brand for the zone
@@ -348,6 +373,15 @@ extern int zonecfg_find_mounts(char *, int(*)(const struct mnttab *,
void *), void *);
/*
+ * Resource key/value attributes (properties).
+ */
+extern int zonecfg_add_res_attr(struct zone_res_attrtab **,
+ struct zone_res_attrtab *);
+extern void zonecfg_free_res_attr_list(struct zone_res_attrtab *);
+extern int zonecfg_remove_res_attr(struct zone_res_attrtab **,
+ struct zone_res_attrtab *);
+
+/*
* Network interface configuration.
*/
extern int zonecfg_add_nwif(zone_dochandle_t, struct zone_nwiftab *);
@@ -428,13 +462,6 @@ extern int zonecfg_delete_pset(zone_dochandle_t);
extern int zonecfg_modify_pset(zone_dochandle_t, struct zone_psettab *);
extern int zonecfg_lookup_pset(zone_dochandle_t, struct zone_psettab *);
-/*
- * mem-cap configuration.
- */
-extern int zonecfg_delete_mcap(zone_dochandle_t);
-extern int zonecfg_modify_mcap(zone_dochandle_t, struct zone_mcaptab *);
-extern int zonecfg_lookup_mcap(zone_dochandle_t, struct zone_mcaptab *);
-
/* security-flags configuration */
extern int zonecfg_add_secflags(zone_dochandle_t,
struct zone_secflagstab *);
@@ -502,7 +529,6 @@ extern int zonecfg_setdsent(zone_dochandle_t);
extern int zonecfg_getdsent(zone_dochandle_t, struct zone_dstab *);
extern int zonecfg_enddsent(zone_dochandle_t);
extern int zonecfg_getpsetent(zone_dochandle_t, struct zone_psettab *);
-extern int zonecfg_getmcapent(zone_dochandle_t, struct zone_mcaptab *);
extern int zonecfg_getpkgdata(zone_dochandle_t, uu_avl_pool_t *,
uu_avl_t *);
extern int zonecfg_setdevperment(zone_dochandle_t);
@@ -528,6 +554,7 @@ extern int zonecfg_set_limitpriv(zone_dochandle_t, char *);
* Higher-level routines.
*/
extern int zone_get_brand(char *, char *, size_t);
+extern zoneid_t zone_get_did(char *);
extern int zone_get_rootpath(char *, char *, size_t);
extern int zone_get_devroot(char *, char *, size_t);
extern int zone_get_zonepath(char *, char *, size_t);
@@ -536,7 +563,9 @@ extern int zone_set_state(char *, zone_state_t);
extern char *zone_state_str(zone_state_t);
extern int zonecfg_get_name_by_uuid(const uuid_t, char *, size_t);
extern int zonecfg_get_uuid(const char *, uuid_t);
+extern int zonecfg_set_uuid(const char *, const char *, const char *);
extern int zonecfg_default_brand(char *, size_t);
+extern int zonecfg_fix_obsolete(zone_dochandle_t);
/*
* Iterator for configured zones.
diff --git a/usr/src/head/regexp.h b/usr/src/head/regexp.h
index 5c6714caf4..8dfa8ae528 100644
--- a/usr/src/head/regexp.h
+++ b/usr/src/head/regexp.h
@@ -396,12 +396,12 @@ advance(const char *lp, const char *ep)
/*FALLTHRU*/
case CBRA:
- braslist[*ep++] = (char *)lp;
+ braslist[(int)*ep++] = (char *)lp;
continue;
/*FALLTHRU*/
case CKET:
- braelist[*ep++] = (char *)lp;
+ braelist[(int)*ep++] = (char *)lp;
continue;
/*FALLTHRU*/
@@ -479,8 +479,8 @@ advance(const char *lp, const char *ep)
/*FALLTHRU*/
case CBACK:
- bbeg = braslist[*ep];
- ct = braelist[*ep++] - bbeg;
+ bbeg = braslist[(int)*ep];
+ ct = braelist[(int)*ep++] - bbeg;
if (ecmp(bbeg, lp, ct)) {
lp += ct;
@@ -490,8 +490,8 @@ advance(const char *lp, const char *ep)
/*FALLTHRU*/
case CBACK | STAR:
- bbeg = braslist[*ep];
- ct = braelist[*ep++] - bbeg;
+ bbeg = braslist[(int)*ep];
+ ct = braelist[(int)*ep++] - bbeg;
curlp = lp;
while (ecmp(bbeg, lp, ct))
lp += ct;
diff --git a/usr/src/head/resolv_joy.h b/usr/src/head/resolv_joy.h
new file mode 100644
index 0000000000..262cf6121d
--- /dev/null
+++ b/usr/src/head/resolv_joy.h
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved
+ *
+ * Portions of this source code were derived from Berkeley
+ * 4.3 BSD under license from the regents of the University of
+ * California.
+ */
+
+/*
+ * BIND 4.9.4:
+ */
+
+/*
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * --Copyright--
+ *
+ * End BIND 4.9.4
+ */
+
+/*
+ * Copyright (c) 1983, 1987, 1989
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * @(#)resolv.h 8.1 (Berkeley) 6/2/93
+ * $Id: resolv.h,v 8.52 2003/04/29 02:27:03 marka Exp $
+ */
+
+#ifndef _RESOLV_JOY_H
+#define _RESOLV_JOY_H
+
+#ifdef _RESOLV_H_
+#error "resolv.h and resolv_joy.h should not be used together"
+#endif
+
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <arpa/nameser.h>
+#include <sys/socket.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Revision information. This is the release date in YYYYMMDD format.
+ * It can change every day so the right thing to do with it is use it
+ * in preprocessor commands such as "#if (__RES > 19931104)". Do not
+ * compare for equality; rather, use it to determine whether your resolver
+ * is new enough to contain a certain feature.
+ */
+
+#define __RES 20090302
+
+#pragma redefine_extname dn_expand joy_dn_expand
+#pragma redefine_extname res_nsearch joy_res_nsearch
+#pragma redefine_extname res_ninit joy_res_ninit
+#pragma redefine_extname res_ndestroy joy_res_ndestroy
+#pragma redefine_extname res_gethostbyaddr joy_res_gethostbyaddr
+#pragma redefine_extname res_gethostbyname joy_res_gethostbyname
+#pragma redefine_extname res_gethostbyname2 joy_res_gethostbyname2
+#pragma redefine_extname res_endhostent joy_res_endhostent
+#pragma redefine_extname res_sethostent joy_res_sethostent
+#pragma redefine_extname __res_override_retry __joy_res_override_retry
+#pragma redefine_extname __res_unset_no_hosts_fallback \
+ __joy_res_unset_no_hosts_fallback
+#pragma redefine_extname __res_set_no_hosts_fallback \
+ __joy_res_set_no_hosts_fallback
+#pragma redefine_extname __h_errno __joy_h_errno
+#pragma redefine_extname __ns_get16 __joy_ns_get16
+#pragma redefine_extname __ns_get32 __joy_ns_get32
+
+
+#define RES_SET_H_ERRNO(r, x) __h_errno_set(r, x)
+struct __res_state; /* forward */
+
+void __h_errno_set(struct __res_state *res, int err);
+
+/*
+ * Resolver configuration file.
+ * Normally not present, but may contain the address of the
+ * initial name server(s) to query and the domain search list.
+ */
+
+#ifndef _PATH_RESCONF
+#define _PATH_RESCONF "/etc/resolv.conf"
+#endif
+
+#ifdef __STDC__
+#ifndef __P
+#define __P(x) x
+#endif
+#else
+#ifndef __P
+#define __P(x) ()
+#endif
+#endif /* __STDC__ */
+
+typedef enum { res_goahead, res_nextns, res_modified, res_done, res_error }
+ res_sendhookact;
+
+typedef res_sendhookact (*res_send_qhook)__P((struct sockaddr * const *ns,
+ const uchar_t **query,
+ int *querylen,
+ uchar_t *ans,
+ int anssiz,
+ int *resplen));
+
+typedef res_sendhookact (*res_send_rhook)__P((const struct sockaddr *ns,
+ const uchar_t *query,
+ int querylen,
+ uchar_t *ans,
+ int anssiz,
+ int *resplen));
+
+struct res_sym {
+ int number; /* Identifying number, like T_MX */
+ const char *name; /* Its symbolic name, like "MX" */
+ const char *humanname; /* Its fun name, like "mail exchanger" */
+};
+
+/*
+ * Global defines and variables for resolver stub.
+ */
+/* ADDRSORT and MAXADDR retained for compatibility; not used */
+#define ADDRSORT 1 /* enable the address-sorting option */
+#define MAXADDR 10 /* max # addresses to sort by */
+
+#define MAXNS 32 /* max # name servers we'll track */
+#define MAXDFLSRCH 3 /* # default domain levels to try */
+#define MAXDNSRCH 6 /* max # domains in search path */
+#define LOCALDOMAINPARTS 2 /* min levels in name that is "local" */
+
+#define RES_TIMEOUT 5 /* min. seconds between retries */
+#define MAXRESOLVSORT 10 /* number of net to sort on */
+#define RES_MAXNDOTS 15 /* should reflect bit field size */
+#define RES_MAXRETRANS 30 /* only for resolv.conf/RES_OPTIONS */
+#define RES_MAXRETRY 5 /* only for resolv.conf/RES_OPTIONS */
+#define RES_DFLRETRY 2 /* Default #/tries. */
+#define RES_MAXTIME 65535 /* Infinity, in milliseconds. */
+
+struct __res_state_ext;
+
+struct __res_state {
+ int retrans; /* retransmission time interval */
+ int retry; /* number of times to retransmit */
+#ifdef __sun
+ uint_t options; /* option flags - see below. */
+#else
+ ulong_t options; /* option flags - see below. */
+#endif
+ int nscount; /* number of name servers */
+ struct sockaddr_in
+ nsaddr_list[MAXNS]; /* address of name server */
+#define nsaddr nsaddr_list[0] /* for backward compatibility */
+ ushort_t id; /* current packet id */
+ char *dnsrch[MAXDNSRCH+1]; /* components of domain to search */
+ char defdname[256]; /* default domain (deprecated) */
+#ifdef __sun
+ uint_t pfcode; /* RES_PRF_ flags - see below. */
+#else
+ ulong_t pfcode; /* RES_PRF_ flags - see below. */
+#endif
+ unsigned ndots:4; /* threshold for initial abs. query */
+ unsigned nsort:4; /* number of elements in sort_list[] */
+ char unused[3];
+ struct {
+ struct in_addr addr;
+ unsigned int mask;
+ } sort_list[MAXRESOLVSORT];
+ res_send_qhook qhook; /* query hook */
+ res_send_rhook rhook; /* response hook */
+ int res_h_errno; /* last one set for this context */
+ int _vcsock; /* PRIVATE: for res_send VC i/o */
+ uint_t _flags; /* PRIVATE: see below */
+ uint_t _pad; /* make _u 64 bit aligned */
+ union {
+ /* On an 32-bit arch this means 512b total. */
+ char pad[72 - 4*sizeof (int) - 2*sizeof (void *)];
+ struct {
+ uint16_t nscount;
+ uint16_t nstimes[MAXNS]; /* ms. */
+ int nssocks[MAXNS];
+ struct __res_state_ext *ext; /* extention for IPv6 */
+ uchar_t _rnd[16]; /* PRIVATE: random state */
+ } _ext;
+ } _u;
+};
+
+typedef struct __res_state *res_state;
+
+union res_sockaddr_union {
+ struct sockaddr_in sin;
+#ifdef IN6ADDR_ANY_INIT
+ struct sockaddr_in6 sin6;
+#endif
+#ifdef ISC_ALIGN64
+ int64_t __align64; /* 64bit alignment */
+#else
+ int32_t __align32; /* 32bit alignment */
+#endif
+ char __space[128]; /* max size */
+};
+
+/*
+ * Resolver flags (used to be discrete per-module statics ints).
+ */
+#define RES_F_VC 0x00000001 /* socket is TCP */
+#define RES_F_CONN 0x00000002 /* socket is connected */
+#define RES_F_EDNS0ERR 0x00000004 /* EDNS0 caused errors */
+#define RES_F__UNUSED 0x00000008 /* (unused) */
+#define RES_F_LASTMASK 0x000000F0 /* ordinal server of last res_nsend */
+#define RES_F_LASTSHIFT 4 /* bit position of LASTMASK "flag" */
+#define RES_GETLAST(res) (((res)._flags & RES_F_LASTMASK) >> RES_F_LASTSHIFT)
+
+/* res_findzonecut2() options */
+#define RES_EXHAUSTIVE 0x00000001 /* always do all queries */
+#define RES_IPV4ONLY 0x00000002 /* IPv4 only */
+#define RES_IPV6ONLY 0x00000004 /* IPv6 only */
+
+/*
+ * Resolver options (keep these in synch with res_debug.c, please)
+ */
+#define RES_INIT 0x00000001 /* address initialized */
+#define RES_DEBUG 0x00000002 /* print debug messages */
+#define RES_AAONLY 0x00000004 /* authoritative answers only (!IMPL) */
+#define RES_USEVC 0x00000008 /* use virtual circuit */
+#define RES_PRIMARY 0x00000010 /* query primary server only (!IMPL) */
+#define RES_IGNTC 0x00000020 /* ignore trucation errors */
+#define RES_RECURSE 0x00000040 /* recursion desired */
+#define RES_DEFNAMES 0x00000080 /* use default domain name */
+#define RES_STAYOPEN 0x00000100 /* Keep TCP socket open */
+#define RES_DNSRCH 0x00000200 /* search up local domain tree */
+#define RES_INSECURE1 0x00000400 /* type 1 security disabled */
+#define RES_INSECURE2 0x00000800 /* type 2 security disabled */
+#define RES_NOALIASES 0x00001000 /* shuts off HOSTALIASES feature */
+#define RES_USE_INET6 0x00002000 /* use/map IPv6 in gethostbyname() */
+#define RES_ROTATE 0x00004000 /* rotate ns list after each query */
+#define RES_NOCHECKNAME 0x00008000 /* do not check names for sanity. */
+#define RES_KEEPTSIG 0x00010000 /* do not strip TSIG records */
+#define RES_BLAST 0x00020000 /* blast all recursive servers */
+#define RES_NO_NIBBLE 0x00040000 /* disable IPv6 nibble mode reverse */
+#define RES_NO_BITSTRING 0x00080000 /* disable IPv6 bitstring mode revrse */
+#define RES_NOTLDQUERY 0x00100000 /* don't unqualified name as a tld */
+#define RES_USE_DNSSEC 0x00200000 /* use DNSSEC using OK bit in OPT */
+/* KAME extensions: use higher bit to avoid conflict with ISC use */
+#define RES_USE_DNAME 0x10000000 /* use DNAME */
+#define RES_USE_EDNS0 0x40000000 /* use EDNS0 if configured */
+#define RES_NO_NIBBLE2 0x80000000 /* disable alternate nibble lookup */
+
+#define RES_DEFAULT (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH)
+
+/*
+ * Resolver "pfcode" values. Used by dig.
+ */
+#define RES_PRF_STATS 0x00000001
+#define RES_PRF_UPDATE 0x00000002
+#define RES_PRF_CLASS 0x00000004
+#define RES_PRF_CMD 0x00000008
+#define RES_PRF_QUES 0x00000010
+#define RES_PRF_ANS 0x00000020
+#define RES_PRF_AUTH 0x00000040
+#define RES_PRF_ADD 0x00000080
+#define RES_PRF_HEAD1 0x00000100
+#define RES_PRF_HEAD2 0x00000200
+#define RES_PRF_TTLID 0x00000400
+#define RES_PRF_HEADX 0x00000800
+#define RES_PRF_QUERY 0x00001000
+#define RES_PRF_REPLY 0x00002000
+#define RES_PRF_INIT 0x00004000
+#define RES_PRF_TRUNC 0x00008000
+/* 0x00010000 */
+
+/* Things involving an internal (static) resolver context. */
+#ifdef _REENTRANT
+extern struct __res_state *__res_state(void);
+#define _res (*__res_state())
+#else
+#ifndef __BIND_NOSTATIC
+extern struct __res_state _res;
+#endif
+#endif
+
+#ifndef __BIND_NOSTATIC
+void fp_nquery __P((const uchar_t *, int, FILE *));
+void fp_query __P((const uchar_t *, FILE *));
+const char *hostalias __P((const char *));
+void p_query __P((const uchar_t *));
+void res_close __P((void));
+int res_init __P((void));
+int res_isourserver __P((const struct sockaddr_in *));
+int res_mkquery __P((int, const char *, int, int, const uchar_t *,
+ int, const uchar_t *, uchar_t *, int));
+int res_query __P((const char *, int, int, uchar_t *, int));
+int res_querydomain __P((const char *, const char *, int, int,
+ uchar_t *, int));
+int res_search __P((const char *, int, int, uchar_t *, int));
+int res_send __P((const uchar_t *, int, uchar_t *, int));
+int res_sendsigned __P((const uchar_t *, int, ns_tsig_key *,
+ uchar_t *, int));
+#endif /* __BIND_NOSTATIC */
+
+extern const struct res_sym __p_key_syms[];
+extern const struct res_sym __p_cert_syms[];
+extern const struct res_sym __p_class_syms[];
+extern const struct res_sym __p_type_syms[];
+extern const struct res_sym __p_rcode_syms[];
+
+int res_hnok __P((const char *));
+int res_ownok __P((const char *));
+int res_mailok __P((const char *));
+int res_dnok __P((const char *));
+int sym_ston __P((const struct res_sym *, const char *, int *));
+const char *sym_ntos __P((const struct res_sym *, int, int *));
+const char *sym_ntop __P((const struct res_sym *, int, int *));
+int b64_ntop __P((uchar_t const *, size_t, char *, size_t));
+int b64_pton __P((char const *, uchar_t *, size_t));
+int loc_aton __P((const char *ascii, uchar_t *binary));
+const char *loc_ntoa __P((const uchar_t *binary, char *ascii));
+int dn_skipname __P((const uchar_t *, const uchar_t *));
+void putlong __P((unsigned int, uchar_t *));
+void putshort __P((unsigned short, uchar_t *));
+const char *p_class __P((int));
+const char *p_time __P((unsigned int));
+const char *p_type __P((int));
+const char *p_rcode __P((int));
+const char *p_sockun __P((union res_sockaddr_union, char *, size_t));
+const uchar_t *p_cdnname __P((const uchar_t *, const uchar_t *, int,
+ FILE *));
+const uchar_t *p_cdname __P((const uchar_t *, const uchar_t *, FILE *));
+const uchar_t *p_fqnname __P((const uchar_t *cp, const uchar_t *msg,
+ int, char *, int));
+const uchar_t *p_fqname __P((const uchar_t *, const uchar_t *, FILE *));
+const char *p_option __P((uint_t option));
+char *p_secstodate __P((uint_t));
+int dn_count_labels __P((const char *));
+int dn_comp __P((const char *, uchar_t *, int,
+ uchar_t **, uchar_t **));
+int dn_expand __P((const uchar_t *, const uchar_t *,
+ const uchar_t *, char *, int));
+void res_rndinit __P((res_state));
+uint_t res_randomid __P((void));
+uint_t res_nrandomid __P((res_state));
+int res_nameinquery __P((const char *, int, int,
+ const uchar_t *, const uchar_t *));
+int res_queriesmatch __P((const uchar_t *, const uchar_t *,
+ const uchar_t *, const uchar_t *));
+const char *p_section __P((int section, int opcode));
+
+
+/* Things involving a resolver context. */
+int res_ninit __P((res_state));
+int res_nisourserver __P((const res_state,
+ const struct sockaddr_in *));
+void fp_resstat __P((const res_state, FILE *));
+void res_pquery __P((const res_state, const uchar_t *, int, FILE *));
+const char *res_hostalias __P((const res_state, const char *,
+ char *, size_t));
+int res_nquery __P((res_state,
+ const char *, int, int, uchar_t *, int));
+int res_nsearch __P((res_state, const char *, int,
+ int, uchar_t *, int));
+int res_nquerydomain __P((res_state,
+ const char *, const char *, int, int,
+ uchar_t *, int));
+int res_nmkquery __P((res_state,
+ int, const char *, int, int, const uchar_t *,
+ int, const uchar_t *, uchar_t *, int));
+int res_nsend __P((res_state, const uchar_t *, int, uchar_t *,
+ int));
+int res_nsendsigned __P((res_state, const uchar_t *, int,
+ ns_tsig_key *, uchar_t *, int));
+int res_findzonecut __P((res_state, const char *, ns_class, int,
+ char *, size_t, struct in_addr *, int));
+int res_findzonecut2 __P((res_state, const char *, ns_class, int,
+ char *, size_t, union res_sockaddr_union *,
+ int));
+void res_nclose __P((res_state));
+int res_nopt __P((res_state, int, uchar_t *, int, int));
+int res_nopt_rdata __P((res_state, int, uchar_t *, int, uchar_t *,
+ ushort_t, ushort_t, uchar_t *));
+void res_send_setqhook __P((res_send_qhook hook));
+void res_send_setrhook __P((res_send_rhook hook));
+int __res_vinit __P((res_state, int));
+void res_destroyservicelist __P((void));
+const char *res_servicename __P((uint16_t port, const char *proto));
+const char *res_protocolname __P((int num));
+void res_destroyprotolist __P((void));
+void res_buildprotolist __P((void));
+const char *res_get_nibblesuffix __P((res_state));
+const char *res_get_nibblesuffix2 __P((res_state));
+void res_ndestroy __P((res_state));
+uint16_t res_nametoclass __P((const char *buf, int *success));
+uint16_t res_nametotype __P((const char *buf, int *success));
+void res_setservers __P((res_state,
+ const union res_sockaddr_union *, int));
+int res_getservers __P((res_state,
+ union res_sockaddr_union *, int));
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_RESOLV_JOY_H */
diff --git a/usr/src/head/zdoor.h b/usr/src/head/zdoor.h
new file mode 100644
index 0000000000..f2d204042d
--- /dev/null
+++ b/usr/src/head/zdoor.h
@@ -0,0 +1,74 @@
+/*
+ * 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 2011 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ZDOOR_H
+#define _ZDOOR_H
+
+#include <sys/types.h>
+#include <zone.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct zdoor_handle *zdoor_handle_t;
+
+typedef struct zdoor_cookie {
+ char *zdc_zonename;
+ char *zdc_service;
+ void *zdc_biscuit;
+} zdoor_cookie_t;
+
+typedef struct zdoor_result {
+ char *zdr_data;
+ size_t zdr_size;
+} zdoor_result_t;
+
+typedef zdoor_result_t *(*zdoor_callback) (zdoor_cookie_t *cookie,
+ char *argp, size_t arpg_sz);
+
+#define ZDOOR_OK 0
+#define ZDOOR_ERROR -1
+#define ZDOOR_NOT_GLOBAL_ZONE -2
+#define ZDOOR_ZONE_NOT_RUNNING -3
+#define ZDOOR_ZONE_FORBIDDEN -4
+#define ZDOOR_ARGS_ERROR -5
+#define ZDOOR_OUT_OF_MEMORY -6
+
+extern zdoor_handle_t zdoor_handle_init();
+
+extern int zdoor_open(zdoor_handle_t handle, const char *zonename,
+ const char *service, void *biscuit, zdoor_callback callback);
+
+extern void * zdoor_close(zdoor_handle_t handle, const char *zonename,
+ const char *service);
+
+extern void zdoor_handle_destroy(zdoor_handle_t handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZDOOR_H */
diff --git a/usr/src/head/zone.h b/usr/src/head/zone.h
index 34528a27f5..f9ea8d4f82 100644
--- a/usr/src/head/zone.h
+++ b/usr/src/head/zone.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent Inc.
*/
#ifndef _ZONE_H
@@ -46,17 +47,20 @@ extern ssize_t getzonenamebyid(zoneid_t, char *, size_t);
* NOTE
*
* The remaining contents of this file are private to the implementation
- * of Solaris and are subject to change at any time without notice,
+ * of Illumos and are subject to change at any time without notice,
* Applications using these interfaces may fail to run on future releases.
*/
+#define ZLOGIN_DISCONNECT 0x1 /* disconnect on zone halt */
+#define ZLOGIN_ZFD_EOF 0x2 /* EOF on ZFD */
+
extern int zonept(int, zoneid_t);
extern int zone_get_id(const char *, zoneid_t *);
/* System call API */
extern zoneid_t zone_create(const char *, const char *,
const struct priv_set *, const char *, size_t, const char *, size_t, int *,
- int, int, const bslabel_t *, int);
+ int, int, const bslabel_t *, int, zoneid_t);
extern int zone_boot(zoneid_t);
extern int zone_destroy(zoneid_t);
extern ssize_t zone_getattr(zoneid_t, int, void *, size_t);
@@ -69,6 +73,7 @@ extern int zone_add_datalink(zoneid_t, datalink_id_t);
extern int zone_remove_datalink(zoneid_t, datalink_id_t);
extern int zone_check_datalink(zoneid_t *, datalink_id_t);
extern int zone_list_datalink(zoneid_t, int *, datalink_id_t *);
+extern const char *zone_get_nroot(void);
#ifdef __cplusplus
}
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 33480db3fa..411e315b5f 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -97,6 +97,7 @@ SUBDIRS += \
libbrand \
libbsdmalloc \
libbsm \
+ libbunyan \
libc_db \
libcfgadm \
libcmdutils \
@@ -189,10 +190,12 @@ SUBDIRS += \
libpthread \
libraidcfg \
librcm \
+ librefhash \
librename \
libreparse \
libresolv \
libresolv2 \
+ libresolv2_joy \
librestart \
librpcsvc \
librsm \
@@ -210,6 +213,7 @@ SUBDIRS += \
libsip \
libsldap \
libslp \
+ libsmartsshd \
libsmbfs \
libsmbios \
libsmedia \
@@ -228,6 +232,7 @@ SUBDIRS += \
libutempter \
libuuid \
libuutil \
+ libvnd \
libvolmgt \
libvrrpadm \
libvscan \
@@ -236,6 +241,7 @@ SUBDIRS += \
libxcurses \
libxcurses2 \
libxnet \
+ libzdoor \
libzfs \
libzfsbootenv \
libzfs_core \
@@ -255,9 +261,6 @@ SUBDIRS += \
pkcs11 \
policykit \
print \
- pylibbe \
- pysolaris \
- pyzfs \
raidcfg_plugins \
rpcsec_gss \
sasl_plugins \
@@ -340,6 +343,7 @@ MSGSUBDIRS= \
libshare \
libsldap \
libslp \
+ libsmartsshd \
libsmbfs \
libsmedia \
libtsol \
@@ -348,10 +352,10 @@ MSGSUBDIRS= \
libvscan \
libzfs \
libzonecfg \
+ libzdoor \
madv \
mpss \
pam_modules \
- pyzfs \
rpcsec_gss \
$($(MACH)_MSGSUBDIRS)
@@ -370,6 +374,7 @@ HDRSUBDIRS= \
libads \
libbrand \
libbsm \
+ libbunyan \
libc \
libcmdutils \
libcommputil \
@@ -467,6 +472,7 @@ HDRSUBDIRS= \
libumem \
libutempter \
libuutil \
+ libvnd \
libvolmgt \
libvrrpadm \
libvscan \
@@ -570,7 +576,7 @@ dbusdeps: libsecdb libtsol libinetutil libscf libuutil libgen libsmbios \
../cmd/sendmail/libmilter: libsocket libnsl
abi: libctf libmapmalloc libproc
auditd_plugins: libbsm libsecdb libgss libmtmalloc
-brand: libzonecfg libmapmalloc
+brand: libzonecfg libmapmalloc libipadm libcustr libproc librpcsvc
cfgadm_plugins: libdevice libdevinfo libhotplug librcm hbaapi libkstat libscf
fm: libexacct libipmi libzfs scsi libdevinfo libdevid libcfgadm \
libcontract libsysevent ../cmd/sgs/libelf libdladm libsff \
@@ -589,6 +595,7 @@ libadutils: libldap5 libresolv2
libbe: libzfs libinstzones libuuid libgen libdevinfo libefi libficl \
libzfsbootenv
libbsm: libinetutil libscf libsecdb libtsol
+libbunyan: libumem
libcfgadm: libdevinfo
libcontract: libnvpair
libcmdutils: libavl libnvpair
@@ -607,7 +614,7 @@ libdladm: libdevinfo libinetutil libscf librcm libexacct libkstat \
libpool varpd
libdlpi: libinetutil libdladm
libds: libsysevent
-libdtrace: libproc libgen libctf libmapmalloc
+libdtrace: libproc libgen libctf libmapmalloc libzonecfg ../cmd/sgs/libelf
libdtrace_jni: libuutil libdtrace
libdwarf: ../cmd/sgs/libelf
libefi: libuuid libsmbios
@@ -653,6 +660,7 @@ libprtdiag: libkstat libdevinfo
libprtdiag_psr: libprtdiag
libraidcfg: libdevinfo
librcm: libnvpair
+librefhash: libumem
librestart: libuutil libscf libpool libproject libsecdb libsysevent
libsasl: libgss pkcs11
libsaveargs: libdisasm
@@ -663,6 +671,7 @@ libsff: libnvpair
libshare: libscf libzfs libuuid libfsmgt libsecdb libumem libsmbfs
libsip: libmd5
libsldap: libldap5 libscf
+libsmartsshd: libc libcontract
libsmbfs: libkrb5 libsec libidmap pkcs11
libsmbios: libdevinfo
libsrpt: libstmf
@@ -681,6 +690,7 @@ libvmm: libvmmapi
libvolmgt: libadm
libvrrpadm: libdladm libscf
libvscan: libscf libsecdb
+libzdoor: libc libzonecfg libcontract
libzfs: libdevid libgen libuutil libavl libefi libidmap \
libumem libtsol libzfs_core libcryptoutil pkcs11 libmd libzutil
libzfsbootenv: libzfs libnvpair
@@ -694,7 +704,8 @@ libzutil: libadm libavl libdevid libefi
madv: libgen
mpapi: libpthread libdevinfo libsysevent
mpss: libgen
-nsswitch: libadutils libidmap libdns_sd libscf libldap5 libsldap
+nsswitch: libadutils libidmap libdns_sd libscf libldap5 libsldap \
+ libresolv2_joy
pam_modules: libproject passwdutil smbsrv libtsnet libpam libbsm libsecdb
passwdutil: libsldap
pkcs11: libcryptoutil libgen libuuid
@@ -716,7 +727,7 @@ sun_fc: libdevinfo libsysevent
sun_sas: libdevinfo libsysevent libkstat libdevid
udapl: libdevinfo libdladm
varpd: libavl libidspace libumem libnsl libnvpair libmd5 librename \
- libcustr
+ libbunyan libcustr
#
# The reason this rule checks for the existence of the
diff --git a/usr/src/lib/Makefile.lib b/usr/src/lib/Makefile.lib
index 151f633a3e..4e1886838f 100644
--- a/usr/src/lib/Makefile.lib
+++ b/usr/src/lib/Makefile.lib
@@ -124,6 +124,25 @@ SONAME= $(DYNLIB)
# either within the library or as dependencies, all text should be pure, and
# combining relocations into one relocation table reduces startup costs.
# All options are tunable to allow overload/omission from lower makefiles.
+
+#
+# DTrace related rules
+#
+# These allow for multiple USDT providers to be specified by a library.
+# If a library needs to break up the set of objects that are passed to
+# the dtrace -G invocation, then they can place the following in heir
+# Makefile.com:
+#
+# pics/<provider>.o := USDT_G_PICS = <files>
+#
+# <provider> should be replaced with the basename of one of the USDT
+# providers specified in USDT_PROVIDERS in their Makefile.com.
+#
+USDT_HEADERS= $(USDT_PROVIDERS:%.d=%_impl.h)
+USDT_PICS= $(USDT_PROVIDERS:%.d=pics/%.o)
+USDT_G_PICS= $(PICS)
+
+
HSONAME= -Wl,-h$(SONAME)
DYNFLAGS= $(HSONAME) $(ZTEXT) $(ZDEFS) $(BDIRECT) \
$(MAPFILES:%=-Wl,-M%) $(MAPFILE.PGA:%=-Wl,-M%) $(MAPFILE.NED:%=-Wl,-M%) \
@@ -146,9 +165,9 @@ SRCS= $(OBJECTS:%.o=$(SRCDIR)/%.c)
# overridden locally when extra processing is needed
BUILD.AR= $(AR) $(ARFLAGS) $@ $(AROBJS)
BUILD.SO= $(CC) $(CFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) \
- $(PICS) $(EXTPICS) $(LDLIBS)
+ $(PICS) $(EXTPICS) $(USDT_PICS) $(LDLIBS)
BUILD64.SO= $(CC64) $(CFLAGS64) -o $@ $(GSHARED) $(DYNFLAGS) \
- $(PICS) $(EXTPICS) $(LDLIBS)
+ $(PICS) $(EXTPICS) $(USDT_PICS) $(LDLIBS)
BUILDCCC.SO= $(CCC) $(CCFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) \
$(PICS) $(EXTPICS) $(LDLIBS) $(CCNEEDED)
BUILDCCC64.SO= $(CCC64) $(CCFLAGS64) -o $@ $(GSHARED) $(DYNFLAGS) \
diff --git a/usr/src/lib/Makefile.targ b/usr/src/lib/Makefile.targ
index a06499a5cb..851f29b395 100644
--- a/usr/src/lib/Makefile.targ
+++ b/usr/src/lib/Makefile.targ
@@ -86,7 +86,7 @@ $(LIBRARY): objs .WAIT $$(OBJS)
$(DYNLIB): $$(MAPFILES)
-$(DYNLIB): pics .WAIT $$(PICS) $$(ALTPICS) $$(EXTPICS)
+$(DYNLIB): pics $(USDT_HEADERS) .WAIT $$(PICS) $$(ALTPICS) $$(EXTPICS) .WAIT $(USDT_PICS)
$(BUILD.SO)
$(POST_PROCESS_SO)
diff --git a/usr/src/lib/Makefile.usdt b/usr/src/lib/Makefile.usdt
new file mode 100644
index 0000000000..17140161ad
--- /dev/null
+++ b/usr/src/lib/Makefile.usdt
@@ -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.
+#
+
+#
+# This makefile contains the necessary targets for USDT providers; it should
+# be included after Makefile.targ. (It is in a separate file rather than in
+# Makefile.targ because the dependency on $(USDT_G_PICS) is incompatible with
+# libraries that dynamically define $(OBJECTS).)
+#
+pics/%.o: $(SRCDIR)/%.d $(USDT_G_PICS)
+ $(COMPILE.d) -o $@ -s $< $(USDT_G_PICS)
+ $(POST_PROCESS_O)
+
+%_impl.h: $(SRCDIR)/%.d
+ $(DTRACE) -h -o $@ -s $<
+
diff --git a/usr/src/lib/brand/Makefile b/usr/src/lib/brand/Makefile
index 763cdb2fa6..a095ec4a63 100644
--- a/usr/src/lib/brand/Makefile
+++ b/usr/src/lib/brand/Makefile
@@ -20,17 +20,18 @@
#
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2019 Joyent, Inc.
#
-# lib/brand/Makefile
-#
-# include global definitions
include ../../Makefile.master
-#
-# Build everything in parallel; use .WAIT for dependencies
.PARALLEL:
-SUBDIRS= shared .WAIT sn1 solaris10 ipkg labeled $($(MACH)_SUBDIRS)
+i386_SUBDIRS= bhyve kvm lx
+i386_MSGSUBDIRS= lx
+
+SUBDIRS= shared .WAIT sn1 solaris10 ipkg labeled \
+ jcommon joyent joyent-minimal $($(MACH)_SUBDIRS)
+
MSGSUBDIRS= solaris10 shared $($(MACH)_MSGSUBDIRS)
all := TARGET= all
diff --git a/usr/src/lib/brand/bhyve/Makefile b/usr/src/lib/brand/bhyve/Makefile
new file mode 100644
index 0000000000..b95c3cefbd
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/Makefile
@@ -0,0 +1,32 @@
+#
+# 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) 2018, Joyent, Inc.
+#
+
+default: all
+
+# Build everything in parallel; use .WAIT for dependencies
+.PARALLEL:
+
+SUBDIRS = zone
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+
+all install clean clobber lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/brand/bhyve/Makefile.bhyve b/usr/src/lib/brand/bhyve/Makefile.bhyve
new file mode 100644
index 0000000000..c2ac3fac2c
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/Makefile.bhyve
@@ -0,0 +1,17 @@
+#
+# 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) 2018, Joyent, Inc.
+#
+
+BRAND= bhyve
+
+include $(SRC)/lib/brand/Makefile.brand
+
diff --git a/usr/src/lib/brand/bhyve/zone/Makefile b/usr/src/lib/brand/bhyve/zone/Makefile
new file mode 100644
index 0000000000..4b2819769b
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/zone/Makefile
@@ -0,0 +1,45 @@
+#
+# 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 2020 Joyent, Inc.
+#
+
+include $(SRC)/cmd/Makefile.cmd
+include ../Makefile.bhyve
+include $(SRC)/cmd/Makefile.cmd.64
+
+PROGS = attach detach statechange uninstall
+
+PROG1 = boot
+PROG2 = bhhwcompat
+PROGS += $(PROG1) $(PROG2)
+CLEANFILES += $(PROG1) $(PROG2)
+
+$(PROG1) := LDLIBS += -lnvpair -lcustr
+$(PROG2) := CPPFLAGS = -I$(COMPAT)/bhyve -I$(CONTRIB)/bhyve \
+ $(CPPFLAGS.master) -I$(SRC)/uts/intel \
+ -I$(COMPAT)/bhyve/amd64 -I$(CONTRIB)/bhyve/amd64
+
+TEMPLATES = SYSbhyve.xml
+XMLDOCS = config.xml platform.xml
+CLOBBERFILES = $(ROOTXMLDOCS) $(ROOTTEMPLATES) $(ROOTPROGS)
+
+# This shouldn't be necessary, but for some reason the same thing in
+# ../../Makefile.brand is not being picked up.
+$(ROOTPROGS) := FILEMODE = 755
+
+all: $(PROGS)
+
+install: $(PROGS) $(ROOTPROGS) $(ROOTXMLDOCS) $(ROOTTEMPLATES)
+
+clean:
+ $(RM) $(CLEANFILES)
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/lib/brand/bhyve/zone/SYSbhyve.xml b/usr/src/lib/brand/bhyve/zone/SYSbhyve.xml
new file mode 100644
index 0000000000..4fd8cb5406
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/zone/SYSbhyve.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ DO NOT EDIT THIS FILE. Use zonecfg(1M) instead.
+-->
+<!DOCTYPE zone PUBLIC "-//Sun Microsystems Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1">
+<zone name="SYSbhyve" zonepath="" autoboot="false" brand="bhyve" >
+ <attr name="bootrom" type="string" value="/usr/share/bhyve/uefi-csm-rom.bin"/>
+ <attr name="com1" type="string" value="/dev/zconsole"/>
+</zone>
diff --git a/usr/src/lib/brand/bhyve/zone/attach b/usr/src/lib/brand/bhyve/zone/attach
new file mode 100755
index 0000000000..626e608579
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/zone/attach
@@ -0,0 +1,26 @@
+#!/bin/ksh -p
+#
+#
+# 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) 2018, Joyent, Inc.
+#
+
+. /usr/lib/brand/jcommon/libhooks.ksh
+
+function jcommon_attach_hook
+{
+ jattach_kvm_final_setup
+}
+
+ps_brand="bhyve"
+. /usr/lib/brand/jcommon/cattach
diff --git a/usr/src/lib/brand/bhyve/zone/bhhwcompat.c b/usr/src/lib/brand/bhyve/zone/bhhwcompat.c
new file mode 100644
index 0000000000..a09719849e
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/zone/bhhwcompat.c
@@ -0,0 +1,98 @@
+/*
+ * 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 2018, Joyent, Inc.
+ * Copyright 2022 MNX Cloud, Inc.
+ */
+
+/*
+ * With no option, exit 0 if the current hardware is bhyve-compatible, non-zero
+ * otherwise. A '-v' option can be used to print the incompatibility reason
+ * provided by the kernel.
+ *
+ * The -c option can be used to print the number of virtual CPUs supported by
+ * bhyve build.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/vmm.h>
+
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#include <sys/vmm.h>
+#include <sys/vmm_dev.h>
+
+static void
+usage()
+{
+ fprintf(stderr, "bhhwcompat [-cv]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fd, c, version;
+ char emsg[128];
+ boolean_t max_cpu = B_FALSE;
+ boolean_t verbose = B_FALSE;
+
+ while ((c = getopt(argc, argv, "cv")) != -1) {
+ switch (c) {
+ case 'c':
+ max_cpu = B_TRUE;
+ break;
+ case 'v':
+ verbose = B_TRUE;
+ break;
+ default:
+ usage();
+ }
+ }
+
+/*
+ * Uggh, recent changes to sys/vmm.h exclude this from userland. Keep in synch.
+ * See illumos#14691 for the correct answer to this problem.
+ */
+#define VM_MAXCPU 32
+ if (max_cpu) {
+ (void) printf("%d\n", VM_MAXCPU);
+ }
+
+ if ((fd = open(VMM_CTL_DEV, O_RDONLY | O_EXCL)) < 0) {
+ if (verbose)
+ fprintf(stderr, "missing %s\n", VMM_CTL_DEV);
+ exit(1);
+ }
+
+ emsg[0] = '\0';
+ version = ioctl(fd, VMM_INTERFACE_VERSION, 0);
+ if (version != VMM_CURRENT_INTERFACE_VERSION) {
+ if (verbose) {
+ fprintf(stderr, "mismatched interface version, "
+ "expecting %d, got %d\n",
+ VMM_CURRENT_INTERFACE_VERSION, version);
+ }
+ exit(1);
+ }
+ if (ioctl(fd, VMM_VM_SUPPORTED, emsg) < 0) {
+ if (verbose)
+ fprintf(stderr, "%s\n", emsg);
+ exit(1);
+ }
+
+ (void) close(fd);
+ return (0);
+}
diff --git a/usr/src/lib/brand/bhyve/zone/boot.c b/usr/src/lib/brand/bhyve/zone/boot.c
new file mode 100644
index 0000000000..3d26e350d4
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/zone/boot.c
@@ -0,0 +1,926 @@
+/*
+ * 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 2020 Joyent, Inc.
+ */
+
+/*
+ * This program runs as a child of zoneadmd, which sets a variety of
+ * _ZONECFG_<resource>_<instance> properties so that child processes don't have
+ * to parse xml.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libnvpair.h>
+#include <libcustr.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/debug.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <zone.h>
+
+/* These two paths must be relative to the zone root. */
+#define BHYVE_DIR "var/run/bhyve"
+#define BHYVE_ARGS_FILE BHYVE_DIR "/" "zhyve.cmd"
+
+#define ZH_MAXARGS 100
+
+#define DEFAULT_BOOTROM "/usr/share/bhyve/uefi-rom.bin"
+#define DEFAULT_BOOTROM_CSM "/usr/share/bhyve/uefi-csm-rom.bin"
+
+typedef enum {
+ PCI_SLOT_HOSTBRIDGE = 0,
+ PCI_SLOT_CD = 3, /* Windows ahci allows slots 3 - 6 */
+ PCI_SLOT_BOOT_DISK,
+ PCI_SLOT_OTHER_DISKS,
+ PCI_SLOT_NICS,
+ PCI_SLOT_FBUF = 30,
+ PCI_SLOT_LPC = 31, /* Windows requires lpc in slot 31 */
+} pci_slot_t;
+
+static boolean_t debug;
+static const char *zonename;
+static const char *zonepath;
+
+#define dprintf(x) if (debug) (void)printf x
+
+static char *
+get_zcfg_var(const char *rsrc, const char *inst, const char *prop)
+{
+ char envvar[MAXNAMELEN];
+ char *ret;
+
+ if (prop == NULL) {
+ if (snprintf(envvar, sizeof (envvar), "_ZONECFG_%s_%s",
+ rsrc, inst) >= sizeof (envvar)) {
+ return (NULL);
+ }
+ } else {
+ if (snprintf(envvar, sizeof (envvar), "_ZONECFG_%s_%s_%s",
+ rsrc, inst, prop) >= sizeof (envvar)) {
+ return (NULL);
+ }
+ }
+
+ ret = getenv(envvar);
+
+ dprintf(("%s: '%s=%s'\n", __func__, envvar, ret ? ret : "<null>"));
+
+ return (ret);
+}
+
+static boolean_t
+is_env_true(const char *rsrc, const char *inst, const char *prop)
+{
+ char *val = get_zcfg_var(rsrc, inst, prop);
+
+ return (val != NULL && strcmp(val, "true") == 0);
+}
+
+static boolean_t
+is_env_string(const char *rsrc, const char *inst, const char *prop,
+ const char *val)
+{
+ char *pval = get_zcfg_var(rsrc, inst, prop);
+
+ return (pval != NULL && strcmp(pval, val) == 0);
+}
+
+static int
+add_arg(int *argc, char **argv, const char *val)
+{
+ if (*argc >= ZH_MAXARGS) {
+ (void) printf("Error: too many arguments\n");
+ return (1);
+ }
+ argv[*argc] = strdup(val);
+ assert(argv[*argc] != NULL);
+ dprintf(("%s: argv[%d]='%s'\n", __func__, *argc, argv[*argc]));
+ (*argc)++;
+ return (0);
+}
+
+static int
+add_smbios(int *argc, char **argv)
+{
+ char smbios[MAXPATHLEN];
+ struct utsname utsname;
+ const char *version;
+ const char *uuid;
+
+ if ((uuid = getenv("_ZONECFG_uuid")) != NULL) {
+ if (add_arg(argc, argv, "-U") != 0 ||
+ add_arg(argc, argv, uuid) != 0)
+ return (1);
+ }
+
+ /*
+ * Look for something like joyent_20180329T120303Z. A little mucky, but
+ * it's exactly what sysinfo does.
+ */
+ (void) uname(&utsname);
+ if (strncmp(utsname.version, "joyent_", strlen("joyent_")) == 0)
+ version = utsname.version + strlen("joyent_");
+ else
+ version = "?";
+
+ /*
+ * This is based upon the SMBIOS values we expose to KVM guests.
+ */
+ (void) snprintf(smbios, sizeof (smbios),
+ "1,manufacturer=Joyent,product=SmartDC HVM,version=7.%s,"
+ "serial=%s,sku=001,family=Virtual Machine",
+ version, zonename);
+
+ if (add_arg(argc, argv, "-B") != 0 ||
+ add_arg(argc, argv, smbios) != 0)
+ return (1);
+
+ return (0);
+}
+
+static int
+add_cpu(int *argc, char **argv)
+{
+ char *val;
+
+ if ((val = get_zcfg_var("attr", "vcpus", NULL)) != NULL) {
+ if (add_arg(argc, argv, "-c") != 0 ||
+ add_arg(argc, argv, val) != 0) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static int
+add_ram(int *argc, char **argv)
+{
+ char *val;
+
+ if ((val = get_zcfg_var("attr", "ram", NULL)) != NULL) {
+ if (add_arg(argc, argv, "-m") != 0 ||
+ add_arg(argc, argv, val) != 0) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static int
+parse_pcislot(const char *pcislot, uint_t *busp, uint_t *devp, uint_t *funcp)
+{
+ char junk;
+
+ switch (sscanf(pcislot, "%u:%u:%u%c", busp, devp, funcp, &junk)) {
+ case 3:
+ break;
+ case 2:
+ case 1:
+ *funcp = *devp;
+ *devp = *busp;
+ *busp = 0;
+ break;
+ default:
+ (void) printf("Error: device %d has illegal PCI slot: %s\n",
+ *devp, pcislot);
+ return (-1);
+ }
+
+ if (*busp > 255 || *devp > 31 || *funcp > 7) {
+ (void) printf("Error: device %d has illegal PCI slot: %s\n",
+ *devp, pcislot);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * In the initial implementation, slot assignment was dynamic on every boot.
+ * Now, each device resource can have a pci_slot property that will override
+ * dynamic assignment. The original behavior is preserved, but no effort is
+ * made to detect or avoid conflicts between legacy behavior and new behavior.
+ * When used with vmadm, this is not an issue, as it will update the zone
+ * config at boot time to contain static assignments.
+ */
+static int
+add_disk(char *disk, char *path, char *slotconf, size_t slotconf_len)
+{
+ static char *boot = NULL;
+ static int next_cd = 0;
+ static int next_other = 0;
+ custr_t *sconfstr = NULL;
+ const char *model = "virtio-blk";
+ uint_t pcibus = 0, pcidev = 0, pcifn = 0;
+ const char *slotstr;
+ const char *guest_block_size = NULL;
+ boolean_t isboot;
+ boolean_t nodelete = B_FALSE;
+
+ if (custr_alloc_buf(&sconfstr, slotconf, slotconf_len) == -1) {
+ return (-1);
+ }
+
+ isboot = is_env_true("device", disk, "boot");
+ if (isboot) {
+ /* Allow at most one "primary" disk */
+ if (boot != NULL) {
+ (void) printf("Error: multiple boot disks: %s %s\n",
+ boot, path);
+ goto fail;
+ }
+ boot = path;
+ }
+
+ if ((slotstr = get_zcfg_var("device", disk, "pci_slot")) != NULL) {
+ if (parse_pcislot(slotstr, &pcibus, &pcidev, &pcifn) != 0) {
+ goto fail;
+ }
+ } else {
+ if (isboot) {
+ pcidev = PCI_SLOT_BOOT_DISK;
+ pcifn = 0;
+ } else if (is_env_string("device", disk, "media", "cdrom")) {
+ pcidev = PCI_SLOT_CD;
+ pcifn = next_cd;
+ next_cd++;
+ } else {
+ pcidev = PCI_SLOT_OTHER_DISKS;
+ pcifn = next_other;
+ next_other++;
+ }
+ }
+
+ if (is_env_string("device", disk, "model", "virtio")) {
+ model = "virtio-blk";
+ /*
+ * bhyve's blockif code refers to the UNMAP/DISCARD/TRIM
+ * feature as 'delete' and so 'nodelete' is used by
+ * bhyve to disable the feature. We use 'trim' for
+ * interfaces we expose to the operator as that seems to
+ * be the most familiar name for the operation (and less
+ * likely to cause confusion).
+ */
+ nodelete = is_env_true("device", disk, "notrim");
+ guest_block_size = get_zcfg_var("device", disk,
+ "guest_block_size");
+
+ /* Treat a 0 size to mean the whatever the volume advertises */
+ if (guest_block_size != NULL &&
+ strcmp(guest_block_size, "0") == 0) {
+ guest_block_size = NULL;
+ }
+ } else if (is_env_string("device", disk, "model", "nvme")) {
+ model = "nvme";
+ } else if (is_env_string("device", disk, "model", "ahci")) {
+ if (is_env_string("device", disk, "media", "cdrom")) {
+ model = "ahci-cd";
+ } else {
+ model = "ahci-hd";
+ }
+ } else {
+ (void) printf("Error: unknown disk model '%s'\n", model);
+ goto fail;
+ }
+
+ if (custr_append_printf(sconfstr, "%u:%u:%u,%s,%s",
+ pcibus, pcidev, pcifn, model, path) == -1) {
+ (void) printf("Error: disk path '%s' too long\n", path);
+ goto fail;
+ }
+
+ if (nodelete && custr_append(sconfstr, ",nodelete") == -1) {
+ (void) printf("Error: too many disk options\n");
+ goto fail;
+ }
+
+ if (guest_block_size != NULL && custr_append_printf(sconfstr,
+ ",sectorsize=%s", guest_block_size) == -1) {
+ (void) printf("Error: too many disk options\n");
+ goto fail;
+ }
+
+ custr_free(sconfstr);
+ return (0);
+
+fail:
+ custr_free(sconfstr);
+ return (-1);
+}
+
+static int
+add_ppt(int *argc, char **argv, char *ppt, char *path, char *slotconf,
+ size_t slotconf_len)
+{
+ static boolean_t wired = B_FALSE;
+ static boolean_t acpi = B_FALSE;
+ uint_t bus = 0, dev = 0, func = 0;
+ char *pcislot;
+
+ pcislot = get_zcfg_var("device", ppt, "pci_slot");
+
+ if (pcislot == NULL) {
+ (void) printf("Error: device %s has no PCI slot\n", ppt);
+ return (-1);
+ }
+
+ if (parse_pcislot(pcislot, &bus, &dev, &func) != 0) {
+ return (-1);
+ }
+
+ if (bus > 0) {
+ if (!acpi && add_arg(argc, argv, "-A") != 0)
+ return (-1);
+ acpi = B_TRUE;
+ }
+
+ if (!wired && add_arg(argc, argv, "-S") != 0)
+ return (-1);
+
+ wired = B_TRUE;
+
+ if (snprintf(slotconf, slotconf_len, "%d:%d:%d,passthru,%s",
+ bus, dev, func, path) >= slotconf_len) {
+ (void) printf("Error: device path '%s' too long\n", path);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+add_devices(int *argc, char **argv)
+{
+ char *devices;
+ char *dev;
+ char *lasts;
+ char slotconf[MAXNAMELEN];
+
+ if ((devices = get_zcfg_var("device", "resources", NULL)) == NULL) {
+ return (0);
+ }
+
+ for (dev = strtok_r(devices, " ", &lasts); dev != NULL;
+ dev = strtok_r(NULL, " ", &lasts)) {
+ int ret;
+ char *path;
+ char *model;
+
+ /* zoneadmd is not careful about a trailing delimiter. */
+ if (dev[0] == '\0') {
+ continue;
+ }
+
+ if ((path = get_zcfg_var("device", dev, "path")) == NULL) {
+ (void) printf("Error: device %s has no path\n", dev);
+ return (-1);
+ }
+
+ if ((model = get_zcfg_var("device", dev, "model")) == NULL) {
+ (void) printf("Error: device %s has no model\n", dev);
+ return (-1);
+ }
+
+ if (strcmp(model, "passthru") == 0) {
+ ret = add_ppt(argc, argv, dev, path, slotconf,
+ sizeof (slotconf));
+ } else {
+ ret = add_disk(dev, path, slotconf, sizeof (slotconf));
+ }
+
+ if (ret != 0)
+ return (-1);
+
+ if (add_arg(argc, argv, "-s") != 0 ||
+ add_arg(argc, argv, slotconf) != 0) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+add_nets(int *argc, char **argv)
+{
+ char *nets;
+ char *net;
+ char *lasts;
+ int nextpcifn = 1; /* 0 reserved for primary */
+ char slotconf[MAXNAMELEN];
+ char *primary = NULL;
+
+ if ((nets = get_zcfg_var("net", "resources", NULL)) == NULL ||
+ strcmp(nets, "") == 0) {
+ return (0);
+ }
+
+ for (net = strtok_r(nets, " ", &lasts); net != NULL;
+ net = strtok_r(NULL, " ", &lasts)) {
+ int pcifn;
+
+ /* zoneadmd is not careful about a trailing delimiter. */
+ if (net[0] == '\0') {
+ continue;
+ }
+
+ /* Allow at most one "primary" net */
+ if (is_env_true("net", net, "primary")) {
+ if (primary != NULL) {
+ (void) printf("Error: "
+ "multiple primary nets: %s %s\n",
+ primary, net);
+ return (-1);
+ }
+ primary = net;
+ pcifn = 0;
+ } else {
+ pcifn = nextpcifn;
+ nextpcifn++;
+ }
+
+ if (snprintf(slotconf, sizeof (slotconf),
+ "%d:%d,virtio-net-viona,%s", PCI_SLOT_NICS, pcifn, net) >=
+ sizeof (slotconf)) {
+ (void) printf("Error: net '%s' too long\n", net);
+ return (-1);
+ }
+
+ if (add_arg(argc, argv, "-s") != 0 ||
+ add_arg(argc, argv, slotconf) != 0) {
+ return (-1);
+ }
+ }
+
+ /* Make sure there is a "primary" net */
+ if (primary == NULL) {
+ (void) printf("Error: no primary net has been specified\n");
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+add_lpc(int *argc, char **argv)
+{
+ char *lpcdevs[] = { "bootrom", "com1", "com2", NULL };
+ const int bootrom_idx = 0;
+ int i;
+ char *val;
+ char conf[MAXPATHLEN];
+ boolean_t found_bootrom = B_FALSE;
+
+ assert(strcmp(lpcdevs[bootrom_idx], "bootrom") == 0);
+
+ (void) snprintf(conf, sizeof (conf), "%d,lpc", PCI_SLOT_LPC);
+ if (add_arg(argc, argv, "-s") != 0 ||
+ add_arg(argc, argv, conf) != 0) {
+ return (-1);
+ }
+
+ for (i = 0; lpcdevs[i] != NULL; i++) {
+ if ((val = get_zcfg_var("attr", lpcdevs[i], NULL)) == NULL) {
+ continue;
+ }
+ if (i == bootrom_idx) {
+ found_bootrom = B_TRUE;
+ if (strcmp(val, "bios") == 0) {
+ val = DEFAULT_BOOTROM_CSM;
+ } else if (strcmp(val, "uefi") == 0) {
+ val = DEFAULT_BOOTROM;
+ }
+ }
+ if (snprintf(conf, sizeof (conf), "%s,%s", lpcdevs[i], val) >=
+ sizeof (conf)) {
+ (void) printf("Error: value of attr '%s' too long\n",
+ lpcdevs[i]);
+ return (-1);
+ }
+ if (add_arg(argc, argv, "-l") != 0 ||
+ add_arg(argc, argv, conf) != 0) {
+ return (-1);
+ }
+ }
+
+ if (!found_bootrom) {
+ if (add_arg(argc, argv, "-l") != 0 ||
+ add_arg(argc, argv, "bootrom," DEFAULT_BOOTROM_CSM) != 0) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+add_hostbridge(int *argc, char **argv)
+{
+ char conf[MAXPATHLEN];
+ char *model = NULL;
+ boolean_t raw_config = B_FALSE;
+
+ if ((model = get_zcfg_var("attr", "hostbridge", NULL)) != NULL) {
+ /* Easy bypass for doing testing */
+ if (strcmp("none", model) == 0) {
+ return (0);
+ }
+
+ if (strchr(model, '=') != NULL) {
+ /*
+ * If the attribute contains '=', assume the creator
+ * wants total control over the config. Do not prepend
+ * the value with 'model='.
+ */
+ raw_config = B_TRUE;
+ }
+ }
+
+ /* Default to Natoma if nothing else is specified */
+ if (model == NULL) {
+ model = "i440fx";
+ }
+
+ (void) snprintf(conf, sizeof (conf), "%d,hostbridge,%s%s",
+ PCI_SLOT_HOSTBRIDGE, raw_config ? "" : "model=", model);
+ if (add_arg(argc, argv, "-s") != 0 ||
+ add_arg(argc, argv, conf) != 0) {
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+add_bhyve_extra_opts(int *argc, char **argv)
+{
+ char *val;
+ char *tok;
+ char *lasts;
+
+ if ((val = get_zcfg_var("attr", "bhyve_extra_opts", NULL)) == NULL) {
+ return (0);
+ }
+
+ val = strdup(val);
+ if (val == NULL) {
+ (void) printf("Error: strdup failed\n");
+ return (-1);
+ }
+
+ for (tok = strtok_r(val, " \t", &lasts); tok != NULL;
+ tok = strtok_r(NULL, " \t", &lasts)) {
+ if (tok[0] == '\0') {
+ continue;
+ }
+ if (add_arg(argc, argv, tok) != 0) {
+ return (-1);
+ }
+ }
+
+ free(val);
+ return (0);
+}
+
+#define INVALID_CHAR (char)(255)
+
+static char
+decode_char(char encoded)
+{
+ if (encoded >= 'A' && encoded <= 'Z')
+ return (encoded - 'A');
+ if (encoded >= 'a' && encoded <= 'z')
+ return (encoded - 'a' + 26);
+ if (encoded >= '0' && encoded <= '9')
+ return (encoded - '0' + 52);
+ if (encoded == '+')
+ return (62);
+ if (encoded == '/')
+ return (63);
+ if (encoded == '=')
+ return (0);
+ return (INVALID_CHAR);
+}
+
+static int
+add_base64(custr_t *cus, const char *b64)
+{
+ size_t b64len = strlen(b64);
+
+ if (b64len == 0 || b64len % 4 != 0)
+ return (-1);
+
+ while (b64len > 0) {
+ uint_t padding = 0;
+ char c0 = decode_char(b64[0]);
+ char c1 = decode_char(b64[1]);
+ char c2 = decode_char(b64[2]);
+ char c3 = decode_char(b64[3]);
+
+ if (c0 == INVALID_CHAR || c1 == INVALID_CHAR ||
+ c2 == INVALID_CHAR || c3 == INVALID_CHAR) {
+ (void) printf("Error: base64 value contains invalid "
+ "character(s)\n");
+ return (-1);
+ }
+
+ /*
+ * For each block of 4 input characters, an '=' should
+ * only appear as the last two characters.
+ */
+ if (b64[0] == '=' || b64[1] == '=') {
+ (void) printf("Error: base64 value contains invalid "
+ "padding\n");
+ return (-1);
+ }
+
+ if (b64len == 4) {
+ /*
+ * We can end with '==' or '=', but never '='
+ * followed by something else.
+ */
+ if (b64[2] == '=') {
+ if (b64[3] != '=') {
+ (void) printf("Error: base64 value "
+ "contains invalid padding\n");
+ return (-1);
+ }
+ padding = 2;
+ } else if (b64[3] == '=') {
+ padding = 1;
+ }
+ }
+
+ VERIFY0(custr_appendc(cus, c0 << 2 | c1 >> 4));
+ if (padding < 2)
+ VERIFY0(custr_appendc(cus, c1 << 4 | c2 >> 2));
+ if (padding < 1)
+ VERIFY0(custr_appendc(cus, c2 << 6 | c3));
+
+ b64len -= 4;
+ b64 += 4;
+ }
+
+ return (0);
+}
+
+/*
+ * Adds the frame buffer and an xhci tablet to help with the pointer.
+ */
+static int
+add_fbuf(int *argc, char **argv)
+{
+ char conf[MAXPATHLEN];
+ custr_t *cconf = NULL;
+ char *password = NULL;
+
+ /*
+ * Do not add a frame buffer or tablet if VNC is disabled.
+ */
+ if (is_env_string("attr", "vnc_port", NULL, "-1")) {
+ return (0);
+ }
+
+ if (custr_alloc_buf(&cconf, conf, sizeof (conf)) != 0) {
+ return (-1);
+ }
+
+ VERIFY0(custr_append_printf(cconf, "%d:0,fbuf,vga=off,unix=/tmp/vm.vnc",
+ PCI_SLOT_FBUF));
+
+ password = get_zcfg_var("attr", "vnc_password", NULL);
+ if (password != NULL) {
+ VERIFY0(custr_append(cconf, ",password="));
+
+ if (add_base64(cconf, password) != 0) {
+ goto fail;
+ }
+ }
+
+ if (add_arg(argc, argv, "-s") != 0 ||
+ add_arg(argc, argv, conf) != 0) {
+ goto fail;
+ }
+
+ custr_reset(cconf);
+ VERIFY0(custr_append_printf(cconf, "%d:1,xhci,tablet", PCI_SLOT_FBUF));
+
+ if (add_arg(argc, argv, "-s") != 0 ||
+ add_arg(argc, argv, conf) != 0) {
+ goto fail;
+ }
+
+ /*
+ * Since cconf was allocated using custr_alloc_buf() where 'conf'
+ * is the underlying fixed buffer for cconf, we can free cconf
+ * which in this instance will just free cconf, but _not_ the
+ * underlying fixed buffer (conf) which is left unchanged by
+ * custr_free().
+ */
+
+ custr_free(cconf);
+ return (0);
+
+fail:
+ custr_free(cconf);
+ return (-1);
+}
+
+/* Must be called last */
+static int
+add_vmname(int *argc, char **argv)
+{
+ char buf[229]; /* VM_MAX_NAMELEN */
+ char *val = getenv("_ZONECFG_did");
+
+ if (val == NULL || val[0] == '\0') {
+ val = "SYSbhyve-unknown";
+ } else {
+ (void) snprintf(buf, sizeof (buf), "SYSbhyve-%s", val);
+ val = buf;
+ }
+ if (add_arg(argc, argv, val) != 0) {
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Write the entire buffer or return an error. This function could be more
+ * paranoid and call fdsync() at the end. That's not really need for this use
+ * case because it is being written to tmpfs.
+ */
+static int
+full_write(int fd, char *buf, size_t buflen)
+{
+ ssize_t nwritten;
+ size_t totwritten = 0;
+
+ while (totwritten < buflen) {
+ nwritten = write(fd, buf + totwritten, buflen - totwritten);
+ if (nwritten < 0) {
+ if (errno == EAGAIN || errno == EINTR) {
+ continue;
+ }
+ return (-1);
+ }
+ assert(nwritten > 0);
+ totwritten += nwritten;
+ }
+ assert(totwritten == buflen);
+
+ return (0);
+}
+
+static void
+init_debug(void)
+{
+ char *val = getenv("_ZONEADMD_brand_debug");
+
+ debug = (val != NULL && val[0] != '\0');
+}
+
+static int
+setup_reboot(void)
+{
+ zoneid_t zoneid;
+
+ if ((zoneid = getzoneidbyname(zonename)) < 0) {
+ (void) printf("Error: bhyve zoneid (%s) does not exist\n",
+ zonename);
+ return (-1);
+ }
+
+ if (zoneid == GLOBAL_ZONEID) {
+ (void) printf("Error: bhyve global zoneid (%s)\n", zonename);
+ return (-1);
+ }
+
+ if (zone_setattr(zoneid, ZONE_ATTR_INITRESTART0, NULL, 0) < 0) {
+ (void) printf("Error: bhyve zoneid %ld setattr failed: %s\n",
+ zoneid, strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+main(int argc, char **argv)
+{
+ int fd, err;
+ char *zhargv[ZH_MAXARGS] = {
+ "bhyve", /* Squats on argv[0] */
+ "-H", /* vmexit on halt isns */
+ NULL };
+ int zhargc = 2;
+ nvlist_t *nvl;
+ char *nvbuf = NULL;
+ size_t nvbuflen = 0;
+ char zoneroot[MAXPATHLEN];
+ int zrfd;
+
+ init_debug();
+
+ if (argc != 3) {
+ (void) printf("Error: bhyve boot program called with "
+ "%d args, expecting 2\n", argc - 1);
+ return (1);
+ }
+ zonename = argv[1];
+ zonepath = argv[2];
+
+ if (setup_reboot() < 0)
+ return (1);
+
+ if (add_smbios(&zhargc, (char **)&zhargv) != 0 ||
+ add_lpc(&zhargc, (char **)&zhargv) != 0 ||
+ add_hostbridge(&zhargc, (char **)&zhargv) != 0 ||
+ add_cpu(&zhargc, (char **)&zhargv) != 0 ||
+ add_ram(&zhargc, (char **)&zhargv) != 0 ||
+ add_devices(&zhargc, (char **)&zhargv) != 0 ||
+ add_nets(&zhargc, (char **)&zhargv) != 0 ||
+ add_bhyve_extra_opts(&zhargc, (char **)&zhargv) != 0 ||
+ add_fbuf(&zhargc, (char **)&zhargv) != 0 ||
+ add_vmname(&zhargc, (char **)&zhargv) != 0) {
+ return (1);
+ }
+
+ /*
+ * This and other dynamically allocated resources are intentionally
+ * leaked. It's a short-lived program and it will all get mopped up on
+ * exit.
+ */
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
+ nvlist_add_string_array(nvl, "bhyve_args", zhargv, zhargc) != 0) {
+ (void) printf("Error: failed to create nvlist: %s\n",
+ strerror(errno));
+ return (1);
+ }
+
+ if (debug) {
+ dprintf(("packing nvlist:\n"));
+ nvlist_print(stdout, nvl);
+ }
+
+ err = nvlist_pack(nvl, &nvbuf, &nvbuflen, NV_ENCODE_XDR, 0);
+ if (err != 0) {
+ (void) printf("Error: failed to pack nvlist: %s\n",
+ strerror(err));
+ return (1);
+ }
+
+ if (snprintf(zoneroot, sizeof (zoneroot), "%s/root", zonepath) >=
+ sizeof (zoneroot)) {
+ (void) printf("Error: zonepath '%s' too long\n", zonepath);
+ return (1);
+ }
+
+ if ((zrfd = open(zoneroot, O_RDONLY|O_SEARCH)) < 0) {
+ (void) printf("Error: cannot open zone root '%s': %s\n",
+ zoneroot, strerror(errno));
+ return (1);
+ }
+
+ /*
+ * This mkdirat() and the subsequent openat() are only safe because the
+ * zone root is always under the global zone's exclusive control (always
+ * read-only in all zones) and the writable directory is a tmpfs file
+ * system that was just mounted and no zone code has run yet.
+ */
+ if (mkdirat(zrfd, BHYVE_DIR, 0700) != 0 && errno != EEXIST) {
+ (void) printf("Error: failed to create directory %s "
+ "in zone: %s\n", BHYVE_DIR, strerror(errno));
+ return (1);
+ }
+
+ fd = openat(zrfd, BHYVE_ARGS_FILE, O_WRONLY|O_CREAT|O_EXCL, 0600);
+ if (fd < 0) {
+ (void) printf("Error: failed to create file %s in zone: %s\n",
+ BHYVE_ARGS_FILE, strerror(errno));
+ return (1);
+ }
+ if (full_write(fd, nvbuf, nvbuflen) != 0) {
+ (void) printf("Error: failed to write %s: %s\n",
+ BHYVE_ARGS_FILE, strerror(errno));
+ (void) unlink(BHYVE_ARGS_FILE);
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/brand/bhyve/zone/config.xml b/usr/src/lib/brand/bhyve/zone/config.xml
new file mode 100644
index 0000000000..82b95f4e57
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/zone/config.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+
+<!--
+ 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) 2018, Joyent, Inc.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE brand PUBLIC "-//Joyent Inc//DTD Brands//EN"
+ "file:///usr/share/lib/xml/dtd/brand.dtd.1">
+
+<brand name="bhyve">
+ <modname></modname>
+
+ <initname>/usr/lib/brand/bhyve/zhyve</initname>
+ <login_cmd />
+ <forcedlogin_cmd />
+ <user_cmd />
+
+ <!-- XXX-mg
+ Until we have better separation of concerns, bhyve brand will use the
+ kvm installer, which is intertwined with vmadm.
+ -->
+ <install>/usr/lib/brand/kvm/kinstall -z %z -R %R</install>
+ <installopts>R:t:U:q:z:</installopts>
+ <boot>/usr/lib/brand/bhyve/boot %z %R</boot>
+ <halt />
+ <verify_cfg />
+ <verify_adm />
+ <postclone />
+ <postinstall />
+ <attach>/usr/lib/brand/bhyve/attach -z %z -R %R</attach>
+ <detach>/usr/lib/brand/bhyve/detach -z %z -R %R</detach>
+ <clone />
+ <uninstall>/usr/lib/brand/bhyve/uninstall -z %z -R %R</uninstall>
+ <prestatechange>/usr/lib/brand/bhyve/statechange pre %z %R</prestatechange>
+ <poststatechange>/usr/lib/brand/bhyve/statechange post %z %R</poststatechange>
+
+ <privilege set="default" name="net_rawaccess" ip-type="exclusive" />
+ <privilege set="default" name="proc_clock_highres" />
+ <privilege set="default" name="proc_lock_memory" />
+ <privilege set="default" name="sys_admin" />
+ <privilege set="default" name="sys_mount" />
+
+ <privilege set="prohibited" name="dtrace_kernel" />
+ <privilege set="prohibited" name="proc_zone" />
+ <privilege set="prohibited" name="sys_config" />
+ <privilege set="prohibited" name="sys_devices" />
+ <privilege set="prohibited" name="sys_ip_config" ip-type="shared" />
+ <privilege set="prohibited" name="sys_linkdir" />
+ <privilege set="prohibited" name="sys_net_config" />
+ <privilege set="prohibited" name="sys_res_config" />
+ <privilege set="prohibited" name="sys_suser_compat" />
+ <privilege set="prohibited" name="xvm_control" />
+ <privilege set="prohibited" name="virt_manage" />
+ <privilege set="prohibited" name="sys_ppp_config" ip-type="shared" />
+
+ <privilege set="required" name="proc_exec" />
+ <privilege set="required" name="sys_mount" />
+</brand>
diff --git a/usr/src/lib/brand/bhyve/zone/detach b/usr/src/lib/brand/bhyve/zone/detach
new file mode 100755
index 0000000000..8a41815258
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/zone/detach
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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) 2018, Joyent, Inc.
+#
+
+ps_brand="bhyve"
+. /usr/lib/brand/jcommon/cdetach
diff --git a/usr/src/lib/brand/bhyve/zone/platform.xml b/usr/src/lib/brand/bhyve/zone/platform.xml
new file mode 100644
index 0000000000..4f4446b345
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/zone/platform.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+
+<!--
+ 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) 2018, Joyent, Inc.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE platform PUBLIC "-//Joyent Inc//Zones Platform//EN"
+ "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1">
+
+<platform name="bhyve" allow-exclusive-ip="true">
+
+ <!-- Global filesystems to mount when booting the zone -->
+ <global_mount special="/dev" directory="/dev" type="dev"
+ opt="attrdir=%R/root/dev"/>
+ <global_mount special="/lib" directory="/lib"
+ opt="ro,nodevices,nosub" type="lofs" />
+ <global_mount special="/usr" directory="/usr"
+ opt="ro,nodevices,nosub" type="lofs" />
+
+ <!-- Local filesystems to mount when booting the zone -->
+ <mount special="/proc" directory="/proc" type="proc" />
+ <mount special="swap" directory="/var/run" type="tmpfs"
+ opt="size=50m"/>
+
+ <!-- Needed by dlmgmtd -->
+ <mount special="swap" directory="/etc/svc/volatile" type="tmpfs"
+ opt="size=10m" />
+
+ <!-- Devices to create under /dev -->
+ <device match="dld" ip-type="exclusive" />
+ <device match="fd" />
+ <device match="null" />
+ <device match="random" />
+ <device match="rdsk" />
+ <device match="viona" />
+
+ <!--
+ Careful: this is not just about catching both /dev/vmmctl and the
+ /dev/vmm directory. In fact it's required to prevent a race where
+ sdev processes "/dev/vmm/", sees it doesn't exist in the global
+ /dev (as the module isn't loaded), then never updates its own sdev
+ directory. Adding the glob here ensures it's checked as needed.
+ -->
+ <device match="vmm*" />
+
+ <!-- Renamed devices to create under /dev -->
+ <device match="zcons/%z/zoneconsole" name="zconsole" />
+ <device match="zfd/%z/slave/1" name="zfd/1" />
+ <device match="zfd/%z/slave/2" name="zfd/2" />
+
+ <!-- Symlinks to create under /dev -->
+ <symlink source="console" target="zconsole" />
+ <symlink source="stderr" target="./fd/2" />
+ <symlink source="stdin" target="./fd/0" />
+ <symlink source="stdout" target="./fd/1" />
+
+</platform>
diff --git a/usr/src/lib/brand/bhyve/zone/statechange b/usr/src/lib/brand/bhyve/zone/statechange
new file mode 100644
index 0000000000..76cfbffc17
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/zone/statechange
@@ -0,0 +1,22 @@
+#! /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) 2018, Joyent, Inc.
+#
+
+ps_brand=bhyve
+
+typeset -A FORCED_ATTRS
+FORCED_ATTRS["zlog-mode"]=g--
+FORCED_ATTRS["zlog-name"]=platform.log
+
+. /usr/lib/brand/jcommon/statechange
diff --git a/usr/src/lib/brand/bhyve/zone/uninstall b/usr/src/lib/brand/bhyve/zone/uninstall
new file mode 100755
index 0000000000..136044ad9e
--- /dev/null
+++ b/usr/src/lib/brand/bhyve/zone/uninstall
@@ -0,0 +1,23 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+function jcommon_uninstall_hook
+{
+ :
+}
+
+. /usr/lib/brand/jcommon/cuninstall
diff --git a/usr/src/lib/brand/ipkg/zone/Makefile b/usr/src/lib/brand/ipkg/zone/Makefile
index bbd1ef7305..b88e80ebe9 100644
--- a/usr/src/lib/brand/ipkg/zone/Makefile
+++ b/usr/src/lib/brand/ipkg/zone/Makefile
@@ -23,8 +23,10 @@
# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2019 Joyent, Inc.
+#
-TEMPLATES= SUNWdefault.xml SUNWblank.xml
+TEMPLATES= SUNWblank.xml
XMLDOCS= config.xml platform.xml
CLOBBERFILES= $(ROOTXMLDOCS) $(ROOTTEMPLATES)
diff --git a/usr/src/lib/brand/jcommon/Makefile b/usr/src/lib/brand/jcommon/Makefile
new file mode 100644
index 0000000000..59bc0de644
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/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 2020 Joyent, Inc.
+#
+
+PROGS = cattach cdetach cinstall cuninstall libhooks.ksh \
+ poststate prestate query statechange
+BRAND = jcommon
+FILEMODE = 0444
+
+include ../Makefile.brand
+
+clean:
+
+clobber:
+ $(RM) $(ROOTPROGS)
+
+all:
+
+install: $(ROOTPROGS)
diff --git a/usr/src/lib/brand/jcommon/README.smf b/usr/src/lib/brand/jcommon/README.smf
new file mode 100644
index 0000000000..64cd3ade4f
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/README.smf
@@ -0,0 +1,107 @@
+#
+# 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.
+#
+
+There are a few rather brand specific issues related to how SMF works inside of
+them. This README describes how the different pieces interact and where they are
+defined. Note that this README only applies to the Joyent-style sparse zones. It
+does not apply to the KVM brand or the traditional ipkg and S10 branded zones.
+
+#
+# Per-brand manifests file
+#
+
+Each brand has a file called `manifests`. This file lists the set of manifests
+that the brand cares about being available to the zone. It is formatted as:
+
+<manifest_name> <enabled | disabled>
+
+Examples are:
+
+network/smb/client.xml disabled
+network/ssh.xml enabled
+
+The use of enabled or disabled determines the default disposition of the service
+when it is imported.
+
+This list is used in various places throughout the rest of the system for
+determining what shows up in the SMF repositories by default and what shows up
+in /lib/svc/manifest.
+
+#
+# Files in /lib/svc/manifest
+#
+
+/lib/svc/manifest is part of the sparse filesystem that gets placed into every
+zone. Unlike /usr, /lib/svc/manifest is brand specific. The zones service
+(svc:/system/zones:default) is responsible for creating the per-brand
+/lib/svc/manifest directories and they live in /zones/manifests/<brand-name>.
+This brand specific directory is lofs-mounted read-only into each zone.
+
+The presence of the enabled and disabled option in the brand's manifests file
+determine whether or not the service is enabled by default when imported. The
+xml file is changed to match the setting.
+
+#
+# Initial SMF repositories
+#
+
+SMF in the minimal brand works differently than it does in the normal Joyent
+Brand when it comes to specifying the initial services that are inside of the
+dataset and what files are in the SMF repository.
+
+SMF has the notion of a `seed repository`. This repository is the initial one
+that is used or copied for new zones. This repository contains various services
+already imported, whether or not they are enabled or disabled, and the various
+service properties.
+
+The traditional `joyent` brand gets this from the dataset itself. In other words,
+the database is already populated with the proper SMF state.
+
+In the `joyent-minimal` brand we handle this differently. We want to be able to
+reuse the datasets that exist but not be stuck with their rather large seed
+repositories that contain many things which are harmful in the minimal context,
+particularly manifest import (both the early and normal kind). To handle this
+the joyent-minimal brand defines a seed repository of its own that gets
+installed at zone creation time and replaces any existing repository.
+
+This seed repository is generated using the `svc.configd-native` and
+`svccfg-native` binaries. Every manifest listed in the brand's manifests file is
+included.
+
+Manifests available to the `joyent` brand not imported into the database are
+available for manual import in /lib/svc/manifest. With the minimal brand, only
+the bare minimum number of manifests should be imported.
+
+#
+# Using non-imported manifests
+#
+
+To use one of the manifests that exists but hasn't been imported is pretty easy.
+At some point in time after the initial creation of the zone (during the first
+boot setup script for example), you can import the service. For example, if you
+were going to import the cron service you would run:
+
+svccfg import /lib/svc/manifest/system/cron.xml
+
+Next, you need to potentially enable the service depending on the default
+disposition of the service. You enable the service by running:
+
+svcadm enable -s <service>
+
+Adding the `-s` flag causes the enabling to be synchronous. If you do not
+include the flag then it will poke svc.startd to enable the service and return.
+If the service is already enabled by default, then this is safe to run and it
+won't change anything. It is safer to just always enable or disable the service
+after importing it based on your needs.
diff --git a/usr/src/lib/brand/jcommon/cattach b/usr/src/lib/brand/jcommon/cattach
new file mode 100644
index 0000000000..4f2e65ad83
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/cattach
@@ -0,0 +1,63 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+unset LD_LIBRARY_PATH
+PATH=/usr/bin:/usr/sbin
+export PATH
+
+. /usr/lib/brand/shared/common.ksh
+
+ZONENAME=""
+ZONEPATH=""
+# Default to 10GB diskset quota
+ZQUOTA=10
+
+while getopts "R:t:U:q:z:" opt
+do
+ case "$opt" in
+ R) ZONEPATH="$OPTARG";;
+ q) ZQUOTA="$OPTARG";;
+ z) ZONENAME="$OPTARG";;
+ *) printf "$m_usage\n"
+ exit $ZONE_SUBPROC_USAGE;;
+ esac
+done
+shift OPTIND-1
+
+if [[ -z $ZONEPATH || -z $ZONENAME ]]; then
+ print -u2 "Brand error: No zone path or name"
+ exit $ZONE_SUBPROC_USAGE
+fi
+
+# The dataset quota must be a number.
+case $ZQUOTA in *[!0-9]*)
+ print -u2 "Brand error: The quota $ZQUOTA is not a number"
+ exit $ZONE_SUBPROC_USAGE;;
+esac
+
+ZROOT=$ZONEPATH/root
+
+# Get the dataset of the parent directory of the zonepath.
+dname=${ZONEPATH%/*}
+bname=${ZONEPATH##*/}
+PDS_NAME=`mount | nawk -v p=$dname '{if ($1 == p) print $3}'`
+[ -z "$PDS_NAME" ] && \
+ print -u2 "Brand error: missing parent ZFS dataset for $dname"
+
+jcommon_attach_hook
+
+exit $ZONE_SUBPROC_OK
diff --git a/usr/src/lib/brand/jcommon/cdetach b/usr/src/lib/brand/jcommon/cdetach
new file mode 100644
index 0000000000..4b67b31fc5
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/cdetach
@@ -0,0 +1,50 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+unset LD_LIBRARY_PATH
+PATH=/usr/bin:/usr/sbin
+export PATH
+
+. /usr/lib/brand/shared/common.ksh
+
+ZONENAME=""
+ZONEPATH=""
+
+while getopts "R:t:U:z:" opt
+do
+ case "$opt" in
+ R) ZONEPATH="$OPTARG";;
+ z) ZONENAME="$OPTARG";;
+ *) printf "$m_usage\n"
+ exit $ZONE_SUBPROC_USAGE;;
+ esac
+done
+shift OPTIND-1
+
+if [[ -z $ZONEPATH || -z $ZONENAME ]]; then
+ print -u2 "Brand error: No zone path or name"
+ exit $ZONE_SUBPROC_USAGE
+fi
+
+#
+# We just need a brand hook so that we can bypass the mount checking
+# that zoneadm does. This is needed because we have the cores dataset
+# mounted under {zonepath}/root.
+#
+cp /etc/zones/${ZONENAME}.xml ${ZONEPATH}/SUNWdetached.xml
+
+exit $ZONE_SUBPROC_OK
diff --git a/usr/src/lib/brand/jcommon/cinstall b/usr/src/lib/brand/jcommon/cinstall
new file mode 100644
index 0000000000..98b9322b5f
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/cinstall
@@ -0,0 +1,183 @@
+#!/bin/ksh -p
+#
+# 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) 2016, Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+unset LD_LIBRARY_PATH
+PATH=/usr/bin:/usr/sbin
+export PATH
+
+. /usr/lib/brand/shared/common.ksh
+
+REPROVISONING=""
+ZONENAME=""
+ZONEPATH=""
+# Default to 10GB diskset quota
+ZQUOTA=10
+ZVOL_NAME=`/usr/bin/uuid -v 4`
+
+#
+# The following are the list of features that a corresponding brand may
+# enable. If they wish to do so, then they must set the following flags
+# to values such that [[ -z $flag ]] is true. The following are the
+# currently supported flags:
+#
+# o jst_reprovision - Brand supports reprovision
+# o jst_tmplopt - Template image optional
+#
+
+function fixup_images
+{
+ # New imgadm renames the dataset's snapshot at import to @final for us
+ # and when it exists, we use that. However, when it does not exist we
+ # still use the old method of creating a zones/<uuid>@<uuid> snapshot
+ # so we can support old datasets.
+ exists=$(zfs list -Ho name ${PDS_NAME}/${TMPLZONE}@final 2>&1)
+ if [[ $? == 0 && ${exists} == "${PDS_NAME}/${TMPLZONE}@final" ]]; then
+ zfs clone -o devices=off -F ${QUOTA_ARG} ${PDS_NAME}/${TMPLZONE}@final \
+ ${PDS_NAME}/${bname} || fatal "failed to clone zone dataset"
+ elif [[ ${exists} =~ "dataset does not exist" ]]; then
+ zfs snapshot ${PDS_NAME}/${TMPLZONE}@${bname}
+ zfs clone -o devices=off -F ${QUOTA_ARG} ${PDS_NAME}/${TMPLZONE}@${bname} \
+ ${PDS_NAME}/${bname} || fatal "failed to clone zone dataset"
+ else
+ fatal "Unable to determine snapshot for ${PDS_NAME}/${TMPLZONE}"
+ fi
+}
+
+while getopts "rR:t:U:q:z:" opt
+do
+ case "$opt" in
+ r)
+ set -x
+ if [[ -z "$jst_reprovision" ]]; then
+ print -u2 "unsupported reprovision requested"
+ exit $ZONE_SUBPROC_USAGE
+ fi
+ REPROVISIONING="true"
+ ;;
+ R) ZONEPATH="$OPTARG";;
+ t) TMPLZONE="$OPTARG";;
+ # UUID is only used in the postinstall script
+ U) UUID="$OPTARG";;
+ q) ZQUOTA="$OPTARG";;
+ z) ZONENAME="$OPTARG";;
+ *) printf "$m_usage\n"
+ exit $ZONE_SUBPROC_USAGE;;
+ esac
+done
+shift OPTIND-1
+
+#
+# IMPORTANT: all actions below need to consider reprovision. If the action
+# modifies files in the zoneroot itself, it should be run on reprovision
+# any other changes to the zone or dataset should not be done on reprovision.
+#
+
+if [[ -z ${REPROVISIONING} \
+ && -n $(zonecfg -z "${ZONENAME}" info attr name=transition \
+ | grep "value: receiving:") ]]; then
+
+ # Here we're doing an install for a received zone, the dataset should have
+ # already been created.
+ exit $ZONE_SUBPROC_OK
+fi
+
+if [[ -z $ZONEPATH || -z $ZONENAME ]]; then
+ print -u2 "Brand error: No zone path or name"
+ exit $ZONE_SUBPROC_USAGE
+fi
+
+if [[ -z ${REPROVISIONING} ]]; then
+ # The install may requires a template zone.
+ if [[ -z $TMPLZONE && -z "$jst_tmplopt" ]]; then
+ print -u2 "Brand error: a zone template is required"
+ exit $ZONE_SUBPROC_USAGE
+ fi
+
+ # The dataset quota must be a number.
+ case $ZQUOTA in *[!0-9]*)
+ print -u2 "Brand error: The quota $ZQUOTA is not a number"
+ exit $ZONE_SUBPROC_USAGE;;
+ esac
+fi
+
+ZROOT=$ZONEPATH/root
+
+if [[ -z ${REPROVISIONING} ]]; then
+ # Get the dataset of the parent directory of the zonepath.
+ dname=${ZONEPATH%/*}
+ bname=${ZONEPATH##*/}
+ PDS_NAME=`mount | nawk -v p=$dname '{if ($1 == p) print $3}'`
+ [ -z "$PDS_NAME" ] && \
+ print -u2 "Brand error: missing parent ZFS dataset for $dname"
+
+ # We expect that zoneadm was invoked with '-x nodataset', so it won't have
+ # created the dataset.
+
+ QUOTA_ARG=
+ if [[ ${ZQUOTA} != "0" ]]; then
+ QUOTA_ARG="-o quota=${ZQUOTA}g"
+ fi
+
+ #
+ # Some zone brands (KVM) optionally have a top level dataset. If
+ # they don't have one, we need to set the quota on the data
+ # disk. Otherwise, we set it on the normal top level dataset.
+ #
+ if [[ -z ${TMPLZONE} ]]; then
+ zfs set quota=${ZQUOTA}g ${PDS_NAME}/${bname}
+ else
+ fixup_images
+ fi
+
+fi
+
+# The rest should be run when REPROVISIONING is set as well.
+
+# Make sure zoneinit is setup to use -o xtrace, this handles old datasets where
+# is not yet enabled by default.
+if [[ -f ${ZROOT}/root/zoneinit && \
+ -z $(grep "^set -o xtrace" ${ZROOT}/root/zoneinit) ]]; then
+ sed -i "" -e "s/^#set -o xtrace/set -o xtrace/" ${ZROOT}/root/zoneinit
+fi
+
+if [ ! -d ${ZONEPATH}/config ]; then
+ mkdir -p ${ZONEPATH}/config
+ chmod 755 ${ZONEPATH}/config
+fi
+
+if [ ! -d ${ZROOT}/tmp ]; then
+ mkdir -p ${ZROOT}/tmp
+ chmod 1777 ${ZROOT}/tmp
+fi
+
+# make /var/svc for the 'provisioning file'
+if [ ! -d ${ZROOT}/var/svc ]; then
+ mkdir -p ${ZROOT}/var/svc
+ chmod 0755 ${ZROOT}/var/svc
+fi
+
+jcommon_attach_hook
+
+exit $ZONE_SUBPROC_OK
diff --git a/usr/src/lib/brand/jcommon/cuninstall b/usr/src/lib/brand/jcommon/cuninstall
new file mode 100644
index 0000000000..9be35bc7a9
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/cuninstall
@@ -0,0 +1,74 @@
+#!/bin/ksh -p
+#
+#
+# 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) 2016 Joyent, Inc. All rights reserved.
+#
+
+unset LD_LIBRARY_PATH
+PATH=/usr/bin:/usr/sbin
+export PATH
+
+. /usr/lib/brand/shared/common.ksh
+
+ZONENAME=""
+ZONEPATH=""
+
+while getopts "FR:z:" opt
+do
+ case "$opt" in
+ F) ;;
+ R) ZONEPATH="$OPTARG";;
+ z) ZONENAME="$OPTARG";;
+ *) printf "$m_usage\n"
+ exit $ZONE_SUBPROC_USAGE;;
+ esac
+done
+shift OPTIND-1
+
+if [[ -z $ZONEPATH || -z $ZONENAME ]]; then
+ print -u2 "Brand error: No zone path or name"
+ exit $ZONE_SUBPROC_USAGE
+fi
+
+# Get the dataset of the parent directory of the zonepath.
+dname=${ZONEPATH%/*}
+bname=${ZONEPATH##*/}
+PDS_NAME=`mount | nawk -v p=$dname '{if ($1 == p) print $3}'`
+if [[ -z "$PDS_NAME" ]]; then
+ print -u2 "Brand error: missing parent ZFS dataset for $dname"
+ exit $ZONE_SUBPROC_USAGE
+fi
+
+# check if the origin is not an @final dataset, if not, we'll destroy it
+ORIGIN=`zfs get -H -ovalue origin $PDS_NAME/$bname | grep -v "@final$"`
+
+zfs destroy -rF $PDS_NAME/cores/$bname
+zfs destroy -rF $PDS_NAME/$bname
+if [[ $? != 0 ]]; then
+ echo "processes in zone: "
+ fuser ${ZONEPATH}
+ # Since we are destroying the zone, we don't want to leave a zoneroot behind
+ # just because something couldn't be umounted. So we'll also force the
+ # umount with the 'f' option here.
+ zfs destroy -rfF $PDS_NAME/$bname
+fi
+
+[[ -n ${ORIGIN} && ${ORIGIN} != "-" ]] && zfs destroy -F $ORIGIN
+
+rm -rf $ZONEPATH
+rm -rf /var/zonecontrol/${ZONENAME}
+
+jcommon_uninstall_hook
+
+exit $ZONE_SUBPROC_OK
diff --git a/usr/src/lib/brand/jcommon/libhooks.ksh b/usr/src/lib/brand/jcommon/libhooks.ksh
new file mode 100644
index 0000000000..913d81899b
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/libhooks.ksh
@@ -0,0 +1,86 @@
+#!/bin/ksh -p
+#
+#
+# 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 file contains various hooks that are used by more than a single
+# brand. This file should be included by the brand-specific files.
+#
+
+jattach_kvm_final_setup()
+{
+ ZRAM=$(zonecfg -z ${ZONENAME} info attr name=ram | \
+ grep "value: " | cut -d ':' -f2 | tr -d ' ')
+
+ if [[ -z ${ZRAM} ]]; then
+ echo "Unable to find RAM value for KVM VM"
+ exit $ZONE_SUBPROC_FATAL
+ fi
+
+ # 100G unless the VM has 80G or more DRAM, in which case: DRAM + 20G.
+ CORE_QUOTA=102400
+ if [[ ${ZRAM} -gt 81920 ]]; then
+ CORE_QUOTA=$((${ZRAM} + 20480))
+ fi
+
+ # The cores quota exists to control run-away zones. As such we make it
+ # such that it will protect the system from a single run-away, but
+ # still allow us to get most cores.
+ rm -rf $ZONEPATH/cores
+ zfs create -o quota=${CORE_QUOTA}m -o mountpoint=/${PDS_NAME}/$bname/cores \
+ ${PDS_NAME}/cores/$bname
+}
+
+jattach_zone_final_setup()
+{
+ if [[ -z ${REPROVISIONING} ]]; then
+ # The cores quota exists to control run-away zones. As such we make it
+ # such that it will protect the system from a single run-away, but
+ # still allow us to get most cores. 100G seems good enough based on
+ # samples from JPC.
+ rm -rf $ZONEPATH/cores
+ CORE_QUOTA=102400
+ zfs create -o quota=${CORE_QUOTA}m -o mountpoint=/${PDS_NAME}/$bname/cores \
+ ${PDS_NAME}/cores/$bname
+
+ chmod 700 $ZONEPATH
+ fi
+
+ egrep -s "netcfg:" $ZROOT/etc/passwd
+ if (( $? != 0 )); then
+ echo "netcfg:x:17:65:Network Configuration Admin:/:" \
+ >> $ZROOT/etc/passwd
+ echo "netcfg:*LK*:::::::" >> $ZROOT/etc/shadow
+ fi
+ egrep -s "netadm:" $ZROOT/etc/group
+ (( $? != 0 )) && echo "netadm::65:" >> $ZROOT/etc/group
+
+ # /etc/svc/profile needs to be a directory with some contents which we
+ # can get from the template. The early manifest import svc
+ # (lib/svc/method/manifest-import) copies some symlinks from the
+ # template's var/svc/profile dir and we need to make sure those are
+ # pointing at the right files and not left dangling.
+ ZPROFILE=$ZROOT/etc/svc/profile
+ if [ ! -d $ZPROFILE ]; then
+ mkdir $ZPROFILE
+ cp -p $ZROOT/var/svc/profile/generic_limited_net.xml $ZPROFILE
+ cp -p $ZROOT/var/svc/profile/inetd_generic.xml $ZPROFILE
+ cp -p $ZROOT/var/svc/profile/ns_dns.xml $ZPROFILE
+ cp -p $ZROOT/var/svc/profile/platform_none.xml $ZPROFILE
+ fi
+
+ touch $ZROOT/var/log/courier.log
+}
diff --git a/usr/src/lib/brand/jcommon/poststate b/usr/src/lib/brand/jcommon/poststate
new file mode 100644
index 0000000000..4e4b1207ed
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/poststate
@@ -0,0 +1,34 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+unset LD_LIBRARY_PATH
+PATH=/usr/bin:/usr/sbin
+export PATH
+
+if [[ -n $_ZONEADMD_brand_debug ]]; then
+ logfile=/var/log/zone_bh.$1
+ date >>$logfile
+ echo "zone $1 post-state-change $3 $4" >>$logfile
+ ksh -x /usr/lib/brand/$ps_brand/statechange "post" $@ 2>>$logfile
+ res=$?
+ echo "zone $1 post-state-change result $res" >>$logfile
+else
+ /usr/lib/brand/$ps_brand/statechange "post" $@
+ res=$?
+fi
+
+exit $res
diff --git a/usr/src/lib/brand/jcommon/prestate b/usr/src/lib/brand/jcommon/prestate
new file mode 100644
index 0000000000..a61af89752
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/prestate
@@ -0,0 +1,34 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+unset LD_LIBRARY_PATH
+PATH=/usr/bin:/usr/sbin
+export PATH
+
+if [[ -n $_ZONEADMD_brand_debug ]]; then
+ logfile=/var/log/zone_bh.$1
+ date >>$logfile
+ echo "zone $1 pre-state-change $3 $4" >>$logfile
+ ksh -x /usr/lib/brand/$ps_brand/statechange "pre" $@ 2>>$logfile
+ res=$?
+ echo "zone $1 pre-state-change result $res" >>$logfile
+else
+ /usr/lib/brand/$ps_brand/statechange "pre" $@
+ res=$?
+fi
+
+exit $res
diff --git a/usr/src/lib/brand/jcommon/query b/usr/src/lib/brand/jcommon/query
new file mode 100755
index 0000000000..70ea39d8e2
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/query
@@ -0,0 +1,52 @@
+#!/bin/ksh -p
+#
+# 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, Oracle and/or its affiliates. All rights reserved.
+#
+
+PATH=/usr/bin:/usr/sbin
+export PATH
+
+. /usr/lib/brand/shared/common.ksh
+
+zonename=$1
+zonepath=$2
+cmd=$3
+
+if [ $3 == "env" ]; then
+ #
+ # zoneadmd reads one (arbitrary length) line of input from the query
+ # hook. If there is more than one environment variable to pass back,
+ # delimit each one with tabs. zoneadmd will split the line at the tabs
+ # and set each key/value pair in its environment.
+ #
+ # Currently, only _ZONEADMD_ZPOOL is used to set the %P substitution
+ # for the brand configuration.
+ #
+ entry=$(svccfg -s smartdc/init listprop '*/zpool')
+ if [ -n "$entry" ]; then
+ val=${entry##* * }
+ [ -n "$val" ] && echo "_ZONEADMD_ZPOOL=/${val}\c"
+ fi
+fi
+
+exit $ZONE_SUBPROC_OK
diff --git a/usr/src/lib/brand/jcommon/statechange b/usr/src/lib/brand/jcommon/statechange
new file mode 100644
index 0000000000..58eae5ab5a
--- /dev/null
+++ b/usr/src/lib/brand/jcommon/statechange
@@ -0,0 +1,906 @@
+#!/bin/ksh -p
+#
+#
+# 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) 2018 Joyent, Inc. All rights reserved.
+#
+
+unset LD_LIBRARY_PATH
+PATH=/usr/bin:/usr/sbin
+export PATH
+
+. /lib/sdc/config.sh
+
+# subcommand:
+# pre
+# post
+
+# state
+# ZONE_STATE_CONFIGURED 0 (script will never see this)
+# ZONE_STATE_INCOMPLETE 1 (script will never see this)
+# ZONE_STATE_INSTALLED 2
+# ZONE_STATE_READY 3
+# ZONE_STATE_RUNNING 4
+# ZONE_STATE_SHUTTING_DOWN 5
+# ZONE_STATE_DOWN 6
+# ZONE_STATE_MOUNTED 7
+
+# cmd
+#
+# ready 0
+# boot 1
+# forceboot 2
+# reboot 3
+# halt 4
+# uninstalling 5
+# mount 6
+# forcemount 7
+# unmount 8
+
+subcommand=$1
+ZONENAME=$2
+ZONEPATH=$3
+state=$4
+cmd=$5
+
+VNDADM=/usr/sbin/vndadm
+SNAPSHOT_DIR=root/checkpoints
+OVERLAY_RULES=/var/run/smartdc/networking/overlay_rules.json
+DEFAULT_MTU=1500
+
+#
+# The following are the list of features that a corresponding brand may
+# enable. If they wish to do so, then they must set the following flags
+# to values such that [[ -z $flag ]] is true. The following are the
+# currently supported flags:
+#
+# o jst_vrrp - Enables vrrp
+# o jst_ufpromisc - Supports unfiltered promiscuous mode
+# o jst_createvnd - Create vnd devices
+# o jst_simplefs - Only setup lastbooted in the FS
+# o jst_showsnap - Show snapshots in the FS
+#
+# In addition, the brand must also specify the following parameters:
+#
+# o jst_mdatapath - The path the metadata socket is expected in the zone
+#
+
+get_boolean_nic_property()
+{
+ bool_val=$(eval echo \$_ZONECFG_net_${1}_${2})
+ if [[ "${bool_val}" == "1" ]] || [[ "${bool_val}" == "true" ]]; then
+ echo "true"
+ else
+ echo "false"
+ fi
+}
+
+nm2prefix()
+{
+ prefix=0
+ numparts=0
+ OLDIFS=$IFS
+ IFS=.
+ for digit in $1 ; do
+ (( numparts+=1 ))
+ case $digit in
+ 255)
+ (( prefix+=8 ))
+ ;;
+ 254)
+ (( prefix+=7 ))
+ ;;
+ 252)
+ (( prefix+=6 ))
+ ;;
+ 248)
+ (( prefix+=5 ))
+ ;;
+ 240)
+ (( prefix+=4 ))
+ ;;
+ 224)
+ (( prefix+=3 ))
+ ;;
+ 192)
+ (( prefix+=2 ))
+ ;;
+ 128)
+ (( prefix+=1 ))
+ ;;
+ 0);;
+ *)
+ echo "Invalid digit in netmask: $digit" 1>&2;
+ IFS=$OLDIFS
+ return 1;;
+ esac
+ done
+ if [[ $numparts -ne 4 ]]; then
+ echo "Too many parts in the IP address" 1>&2;
+ IFS=$OLDIFS
+ return 1
+ fi
+ IFS=$OLDIFS
+ echo "$prefix"
+}
+
+#
+# Set up the vnic(s) for the zone.
+#
+setup_net()
+{
+ typeset tmp overlay tag id rule
+ for nic in $_ZONECFG_net_resources
+ do
+ # Get simplified versions of the network config. variables.
+ address=$(eval echo \$_ZONECFG_net_${nic}_address)
+ # If address set, must be a shared stack zone
+ [[ -n $address ]] && exit 0
+
+ global_nic=$(eval echo \$_ZONECFG_net_${nic}_global_nic)
+ # If no global-nic, must be a dedicated physical NIC instead
+ # of a vnic
+ [[ -z $global_nic ]] && continue
+
+ dhcp_server=$(get_boolean_nic_property ${nic} dhcp_server)
+ mac_addr=$(eval echo \$_ZONECFG_net_${nic}_mac_addr)
+ vlan_id=$(eval echo \$_ZONECFG_net_${nic}_vlan_id)
+ blocked_outgoing_ports=$(eval \
+ echo \$_ZONECFG_net_${nic}_blocked_outgoing_ports)
+ zone_ips=$(eval echo \$_ZONECFG_net_${nic}_ips)
+ zone_ip=$(eval echo \$_ZONECFG_net_${nic}_ip)
+ zone_netmask=$(eval echo \$_ZONECFG_net_${nic}_netmask)
+ allow_dhcp_spoof=$(get_boolean_nic_property ${nic} allow_dhcp_spoofing)
+ allow_ip_spoof=$(get_boolean_nic_property ${nic} allow_ip_spoofing)
+ allow_mac_spoof=$(get_boolean_nic_property ${nic} allow_mac_spoofing)
+ allow_restricted_traffic=$(get_boolean_nic_property ${nic} \
+ allow_restricted_traffic)
+ allow_unfiltered_promisc=$(get_boolean_nic_property ${nic} \
+ allow_unfiltered_promisc)
+ allowed_ips=$(eval echo \$_ZONECFG_net_${nic}_allowed_ips)
+ allowed_dhcp_cids=$(eval echo \$_ZONECFG_net_${nic}_allowed_dhcp_cids)
+ vrid=$(eval echo \$_ZONECFG_net_${nic}_vrrp_vrid)
+ vrrp_primary_ip=$(eval \
+ echo \$_ZONECFG_net_${nic}_vrrp_primary_ip)
+ mtu=$(eval echo \$_ZONECFG_net_${nic}_mtu)
+ isoverlay=
+
+ # If we don't have our zone_ips, it may be because the configuration
+ # was made on an older platform. In that case, use the "ip" and
+ # "netmask" properties for this NIC, and save it as "ips".
+ if [[ -z $zone_ips && -n $zone_ip ]]; then
+ [[ -n $zone_netmask ]] &&
+ zone_ip=$zone_ip/`nm2prefix $zone_netmask`
+
+ zone_ips=$zone_ip
+ fi
+
+
+
+ orig_global=$global_nic
+
+ #
+ # The nic tag for a device (the zonecfg global_nic) can come in
+ # one of a few forms. It may:
+ #
+ # 1) Be a traditional tag which refers to a physical device or
+ # aggregation to create a VNIC over. The source of this
+ # mapping is sysinfo.
+ #
+ # 2) It can be the name of an etherstub. The source of these is
+ # from dladm show-etherstub
+ #
+ # 3) It can take the form of an overlay device rule. An overlay
+ # device rule is an invalid DLPI device and invalid nic tag.
+ # It has the form of <name>/<number>. For example,
+ # sdc_sdn/23. That refers to the overlay rule sdc_sdn. If we
+ # have an overlay rule, we may need to dynamically create the
+ # overlay device if it doesn't exist.
+ #
+ # To handle these cases, we first check if it's an overlay
+ # device, and then if not, check the other cases.
+ #
+
+ tmp=$(echo $orig_global | sed -E 's_[a-zA-Z_0-9]+/[0-9]+__')
+ if [[ -n "$tmp" ]]; then
+
+ #
+ # We only need sysinfo if we get here, and we only need to load it
+ # once. Loading is about the same cost as looking up a single
+ # value.
+ #
+ if [[ -z $SYSINFO_LOADED ]]; then
+ load_sdc_sysinfo
+ SYSINFO_LOADED="LOADED"
+ fi
+ global_nic=$(eval echo \$SYSINFO_NIC_${orig_global})
+
+ # If the global nic is specified as a device or etherstub name
+ # rather than a tag.
+ if [[ -z $global_nic ]]; then
+ echo "$(dladm show-phys -p -o LINK) $(dladm show-etherstub -p -o LINK)" \
+ | egrep "(^| )${orig_global}( |$)" > /dev/null
+ (( $? == 0 )) && global_nic=${orig_global}
+ fi
+ else
+ isoverlay="true"
+ tag=${orig_global%/*}
+ num=${orig_global#*/}
+ global_nic="$tag$num"
+ rule=$(json -f $OVERLAY_RULES $tag)
+ if [[ $? -ne 0 || -z "$rule" ]]; then
+ logger -p daemon.err "zone $ZONENAME had tag " \
+ "$tag which indicates an overlay rule, " \
+ "no corresponding overlay rule found."
+ exit 1
+ fi
+ fi
+
+ # For backwards compatibility with the other parts of the
+ # system, check if this zone already has this vnic setup.
+ # If so, move on to the next vnic.
+ dladm show-vnic -p -o LINK -z $ZONENAME $nic >/dev/null 2>&1
+ (( $? == 0 )) && continue
+
+ if [[ -z $global_nic ]]; then
+ echo "undefined VNIC $nic " \
+ "(global NIC $orig_global)"
+ logger -p daemon.err "zone $ZONENAME " \
+ "undefined VNIC $nic (global NIC $orig_global)"
+ exit 1
+ fi
+
+ #
+ # If we have an overlay device, do we need to create it, or does
+ # it already exist?
+ #
+ if [[ -n "$isoverlay" ]]; then
+ if ! dladm show-overlay $global_nic 2>/dev/null; then
+ dladm create-overlay $rule -v $num $global_nic
+ if [[ $? -ne 0 ]]; then
+ # If creation fails, ALSO check
+ # for existence again, in case
+ # someone beat us to it.
+ if ! dladm show-overlay $global_nic \
+ 2> /dev/null; then
+ logger -p daemon.err "zone $ZONENAME " \
+ "failed to create overlay device " \
+ "$global_nic with command " \
+ "'dladm create-overlay $rule -v " \
+ "$num $global_nic"
+ exit 1
+ fi
+ fi
+ fi
+ fi
+
+
+ #
+ # Create the vnic.
+ #
+
+ opt_str="-p "
+
+ #
+ # Traditionally we created VNICs without ever specifying
+ # the MTU. In the world before we supported any kind of
+ # jumbo frames, this is fine, because it would always
+ # match the physical which was 1500 by default for
+ # almost all of our devices. However, when we added
+ # support for mtu in nictagadm and changing it in boot
+ # up, we didn't properly assert the default MTU. This
+ # has led to VMs potentially getting the wrong MTU and
+ # ending up using jumbo frames when the network is
+ # expecting 1500 byte frames. Marx Brothers-esque comedy
+ # and despair ensues.
+ #
+ # Thus we always assert that if no MTU is specified by
+ # the VM, then we go back to the traditional 'default'
+ # value which is 1500.
+ #
+ if [[ -z "$mtu" ]]; then
+ mtu=$DEFAULT_MTU
+ fi
+
+ opt_str="$opt_str mtu=$mtu,"
+
+ #
+ # Always append the zone as the last property. This is
+ # to work around the fact that once we associate it with
+ # a zone, the zone will have a hold on the device and
+ # we'll not be able to delete it if a create fails due
+ # to a bad property (say an invalid MTU). Note if we
+ # have other properties, it is their responsibility to
+ # put a trailing comma on it.
+ #
+ opt_str="${opt_str}zone=$ZONENAME"
+
+ if [[ -n "$jst_vrrp" && -n $vrid ]]; then
+ # MAC addresses for VRRP vnics are determined by the VRID
+ mac_addr="vrrp"
+ opt_str="$opt_str -V $vrid -A inet"
+ fi
+
+ [[ -n $mac_addr ]] && opt_str="$opt_str -m $mac_addr"
+
+ [[ -n $vlan_id && $vlan_id != 0 ]] && \
+ opt_str="$opt_str -v $vlan_id"
+
+
+ #
+ # Creating a VNIC in a zone is a multi-step process internally.
+ # This means there is a short window where the VNIC exists in
+ # the global zone and that could lead to a race condition if
+ # two zones boot at the same time with the same VNIC name. Use
+ # a temp. name to create the VNIC then rename it to have the
+ # correct name.
+ #
+ tname=tmp$$0
+ dout=`dladm create-vnic -t -l $global_nic $opt_str $tname 2>&1`
+ if (( $? != 0 )); then
+ printf "error creating VNIC %s (global NIC %s)\n" \
+ "$nic" "$orig_global"
+ printf "msg: %s\n" "$dout"
+ printf "Failed cmd: dladm create-vnic %s" \
+ "-t -l $global_nic $opt_str $tname"
+ logger -p daemon.err "zone $ZONENAME error creating " \
+ "VNIC $nic (global NIC $orig_global $global_nic)"
+ logger -p daemon.err "msg: $dout"
+ logger -p daemon.err "Failed cmd: dladm create-vnic " \
+ "-t -l $global_nic $opt_str $tname"
+
+ # Show more info if dup MAC addr.
+ echo $dout | egrep -s "MAC address is already in use"
+ if (( $? == 0 )); then
+ entry=`dladm show-vnic -olink,macaddress,zone \
+ | nawk -v addr=$mac_addr '{
+ if ($2 == addr)
+ print $0
+ }'`
+ if [[ -n $entry ]]; then
+ print -f "LINK\tMACADDRESS\tZONE\n"
+ print -f "%s\n" "$entry"
+ fi
+ fi
+ exit 1
+ fi
+ dladm rename-link -z $ZONENAME $tname $nic
+ if (( $? != 0 )); then
+ echo "error renaming VNIC $tname $nic"
+ logger -p daemon.err "zone $ZONENAME error renaming " \
+ "VNIC $tname $nic"
+ exit 1
+ fi
+
+ if [[ -z $mac_addr ]]; then
+ # There was no assigned mac address
+
+ # Get newly assigned mac address.
+ mac_addr=$(dladm show-vnic -z $ZONENAME -p -o \
+ MACADDRESS ${nic})
+
+ # Save newly assigned mac address
+ [[ -n $mac_addr ]] && zonecfg -z $ZONENAME \
+ "select net physical=$nic; " \
+ "set mac-addr=$mac_addr; end; exit"
+ fi
+
+ # Set up antispoof options
+
+ if [[ $dhcp_server == "true" ]] || [[ $allow_dhcp_spoof == "true" ]]; then
+ enable_dhcp="true"
+ # This needs to be off for dhcp server zones
+ allow_ip_spoof="true"
+ else
+ enable_dhcp="false"
+ fi
+
+ comma=""
+ spoof_opts=""
+ if [[ $allow_mac_spoof != "true" ]]; then
+ spoof_opts="${spoof_opts}${comma}mac-nospoof"
+ comma=","
+ fi
+ if [[ $allow_ip_spoof != "true" ]]; then
+ spoof_opts="${spoof_opts}${comma}ip-nospoof"
+ comma=","
+ fi
+ if [[ $allow_restricted_traffic != "true" ]]; then
+ spoof_opts="${spoof_opts}${comma}restricted"
+ comma=","
+ fi
+ if [[ ${enable_dhcp} == "false" ]]; then
+ spoof_opts="${spoof_opts}${comma}dhcp-nospoof"
+ comma=","
+ fi
+
+ if [[ -n ${spoof_opts} ]]; then
+ dladm set-linkprop -t -z $ZONENAME -p \
+ "protection=${spoof_opts}" ${nic}
+ if (( $? != 0 )); then
+ echo "error setting VNIC protection $nic $spoof_opts"
+ logger -p daemon.err "zone $ZONENAME error setting " \
+ "VNIC protection $nic $spoof_opts"
+ exit 1
+ fi
+ fi
+
+ # If we aren't using IP spoofing, we'll need to set the allowed-ips
+ # property on the NIC so that the zone will be able to ifconfig the
+ # proper addresses.
+ if [[ $allow_ip_spoof != "true" ]]; then
+ unset allowed_ip_map
+ typeset -A allowed_ip_map
+
+ dynamic_methods=""
+ separator=""
+ OLDIFS=$IFS
+ IFS=,
+
+ for zone_ip in $zone_ips; do
+ # For each static IP available, add it to the list.
+ if [[ $zone_ip == "dhcp" ]]; then
+ dynamic_methods+="${separator}dhcpv4"
+ separator=","
+ elif [[ $zone_ip == "addrconf" ]]; then
+ dynamic_methods+="${separator}addrconf"
+ separator=","
+ else
+ clean_ip=`printf "%s" "${zone_ip}" | sed 's|^\([^/]*\)/.*|\1|'`
+ allowed_ip_map[${clean_ip}]=true
+ fi
+ done
+
+ # If any additional IPs have been specified (for example, older
+ # VMs set up for IPv6 before vmadm gained support), add them to
+ # the list.
+ for allowed_ip in $allowed_ips; do
+ allowed_ip_map[${allowed_ip}]=true
+ done
+ IFS=$OLDIFS
+
+ # If we're using VRRP and have the IP, add it to the list.
+ if [[ -n "$jst_vrrp" && -n $vrrp_primary_ip ]]; then
+ allowed_ip_map[${vrrp_primary_ip}]=true
+ fi
+
+ allowed_ip_list=""
+
+ separator=""
+ for allowed_ip in ${!allowed_ip_map[@]}; do
+ allowed_ip_list+="${separator}${allowed_ip}"
+ separator=","
+ done
+
+ # Set the allowed-ips property on the NIC
+ if [[ -n ${allowed_ip_list} ]] &&
+ ! dladm set-linkprop -t -z $ZONENAME \
+ -p "allowed-ips=${allowed_ip_list}" ${nic}; then
+ log_and_exit \
+ "error setting VNIC allowed-ips $nic $allowed_ip_list"
+ fi
+
+ # Set the dynamic-methods property on the NIC
+ if [[ -n ${dynamic_methods} ]] &&
+ ! dladm set-linkprop -t -z $ZONENAME \
+ -p "dynamic-methods=${dynamic_methods}" ${nic}; then
+ log_and_exit \
+ "error setting VNIC dynamic-methods $nic $dynamic_methods"
+ fi
+ fi
+
+ if [[ "$enable_dhcp" != "true" ]] && [[ -n "$allowed_dhcp_cids" ]] &&
+ ! dladm set-linkprop -p "allowed-dhcp-cids=${allowed_dhcp_cids}" \
+ -t -z $ZONENAME $nic; then
+ log_and_exit \
+ "error setting VNIC allowed-dhcp-cids $nic $allowed_dhcp_cids"
+ fi
+
+ if [[ "$enable_dhcp" != "true" ]] && [[ -z "$allowed_dhcp_cids" ]] &&
+ [[ "$zone_ips" == *dhcp* || "$zone_ips" == *addrconf* ]] &&
+ ! dladm set-linkprop -p "allow-all-dhcp-cids=true" \
+ -t -z $ZONENAME $nic; then
+ log_and_exit "error setting VNIC allow-all-dhcp-cids $nic"
+ fi
+
+ if [[ -n "$jst_ufpromisc" && ${allow_unfiltered_promisc} == "true" ]]; then
+ dladm set-linkprop -t -z $ZONENAME -p "promisc-filtered=off" ${nic}
+ fi
+
+ if [[ -n $blocked_outgoing_ports ]]; then
+ OLDIFS=$IFS
+ IFS=,
+ for port in $blocked_outgoing_ports; do
+ # br='block remote'. Flow names should be < 31
+ # chars in length so that they get unique
+ # kstats.
+ # Use the VNIC mac addr. to generate a unique
+ # name.
+ mac_addr=`dladm show-vnic -z $ZONENAME -p \
+ -o MACADDRESS $nic | tr ':' '_'`
+ flowadm add-flow -t -l $nic -z $ZONENAME \
+ -a transport=tcp,remote_port=$port \
+ -p maxbw=0 f${mac_addr}_br_${port}
+ if (( $? != 0 )); then
+ echo "error adding flow " \
+ "$nic f${mac_addr}_br_${port}"
+ logger -p daemon.err "zone $ZONENAME " \
+ "error adding flow " \
+ "$nic f${mac_addr}_br_${port}"
+ exit 1
+ fi
+ done
+ IFS=$OLDIFS
+ fi
+
+ if [[ -n "$jst_createvnd" ]]; then
+ #
+ # At this point we should go ahead and set up
+ # the vnd interface for this datalink.
+ #
+ $VNDADM create -z $ZONENAME $nic
+ if [[ $? -ne 0 ]]; then
+ echo "failed to create vnd device"
+ exit 1
+ fi
+ fi
+ done
+}
+
+#
+# Log a message, then exit
+#
+log_and_exit()
+{
+ echo "$1"
+ logger -p daemon.err "zone $ZONENAME $1"
+ exit 1
+}
+
+#
+# Set up the firewall for the zone.
+#
+setup_fw()
+{
+ ipf_conf=$ZONEPATH/config/ipf.conf
+ ipf6_conf=$ZONEPATH/config/ipf6.conf
+ if [ -e $ipf_conf ]; then
+ echo "starting firewall ($ipf_conf)"
+ /usr/sbin/ipf -GE $ZONENAME
+ if (( $? != 0 )); then
+ log_and_exit "error enabling ipfilter"
+ fi
+
+ /usr/sbin/ipf -GFa $ZONENAME
+ if (( $? != 0 )); then
+ log_and_exit "error flushing ipfilter (IPv4)"
+ fi
+
+ /usr/sbin/ipf -6GFa $ZONENAME
+ if (( $? != 0 )); then
+ log_and_exit "error flushing ipfilter (IPv6)"
+ fi
+
+ /usr/sbin/ipf -Gf $ipf_conf $ZONENAME
+ if (( $? != 0 )); then
+ log_and_exit "error loading ipfilter config for IPv4"
+ fi
+
+ if [[ -e $ipf6_conf ]] &&
+ ! /usr/sbin/ipf -6Gf $ipf6_conf $ZONENAME; then
+ log_and_exit "error loading ipfilter config for IPv6"
+ fi
+
+ /usr/sbin/ipf -Gy $ZONENAME
+ if (( $? != 0 )); then
+ log_and_exit "error syncing ipfilter interfaces"
+ fi
+ fi
+}
+
+#
+# We're readying the zone. Make sure the per-zone writable
+# directories exist so that we can lofs mount them. We do this here,
+# instead of in the install script, since this list has evolved and
+# there are already zones out there in the installed state.
+#
+setup_fs()
+{
+ # create directory for metadata socket
+ mkdir -m755 -p /var/zonecontrol/${ZONENAME}
+
+ uname -v > $ZONEPATH/lastbooted
+ [[ -n "$jst_simplefs" ]] && return
+
+ [ ! -d $ZONEPATH/site ] && mkdir -m755 $ZONEPATH/site
+ [ ! -d $ZONEPATH/local ] && mkdir -m755 $ZONEPATH/local
+ [ ! -d $ZONEPATH/$SNAPSHOT_DIR ] && mkdir -m755 $ZONEPATH/$SNAPSHOT_DIR
+ if [ ! -d $ZONEPATH/ccs ]; then
+ mkdir -m755 $ZONEPATH/ccs
+ (cd /usr/ccs; tar cbf 512 - *) | \
+ (cd $ZONEPATH/ccs; tar xbf 512 -)
+ fi
+
+}
+
+setup_snapshots()
+{
+ #
+ # Because the top-level directory of each ZFS snapshot contains some
+ # internal information, mount the /root directory of each snapshot
+ # separately.
+ #
+ for snap in $(ls -1 $ZONEPATH/.zfs/snapshot); do
+ snapdir=$ZONEPATH/$SNAPSHOT_DIR/$(echo ${snap} | sed -e "s/^vmsnap-//")
+ mkdir -p ${snapdir}
+ mount -F lofs -o ro,setuid,nodevices \
+ $ZONEPATH/.zfs/snapshot/${snap}/root ${snapdir}
+ done
+}
+
+#
+# If the zone has a CPU cap, calculate the CPU baseline and set it so we can
+# track when we're bursting. There are many ways that the baseline can be
+# calculated based on the other settings in the zones (e.g. a simple way would
+# be as a precentage of the cap).
+#
+# For SmartMachines, our CPU baseline is calculated off of the system's
+# provisionable memory and the memory cap of the zone. We assume that 83% of
+# the system's memory is usable by zones (the rest is for the OS) and we assume
+# that the zone memory cap is set so that we're proportional to how many zones
+# we can provision on the system (i.e. we don't overprovision memory). Using
+# these assumptions, we calculate the proportion of CPU for the zone based on
+# its proportion of memory. Thus, the zone's CPU baseline is calculated using:
+# ((zone capped memsize in MB) * 100) / (MB/core).
+# Uncapped zones have no baseline (i.e. infrastructure zones).
+#
+# Remember that the cpu-cap rctl and the baseline are expressed in units of
+# a percent of a CPU, so 100 is 1 full CPU.
+#
+setup_cpu_baseline()
+{
+ # A brand can override the setup of bursting.
+ [ -n "$NO_BURSTING" ] && return
+
+ # If there is already a baseline, don't set one heuristically
+ curr_base=`prctl -P -n zone.cpu-baseline -i zone $ZONENAME | nawk '{
+ if ($2 == "privileged") print $3
+ }'`
+ [ -n "$curr_base" ] && return
+
+ # Get current cap and convert from zonecfg format into rctl format
+ cap=`zonecfg -z $ZONENAME info capped-cpu | nawk '{
+ if ($1 == "[ncpus:") print (substr($2, 1, length($2) - 1) * 100)
+ }'`
+ [ -z "$cap" ] && return
+
+ # Get zone's memory cap in MB times 100
+ zmem=`zonecfg -z $ZONENAME info capped-memory | nawk '{
+ if ($1 == "[physical:") {
+ val = substr($2, 1, length($2) - 2)
+ units = substr($2, length($2) - 1, 1)
+
+ # convert GB to MB
+ if (units == "G")
+ val *= 1024
+ print (val * 100)
+ }
+ }'`
+ [ -z "$zmem" ] && return
+
+ # Get system's total memory in MB
+ smem=`prtconf -m`
+ # provisionable memory is 83% of total memory (bash can't do floats)
+ prov_mem=$((($smem * 83) / 100))
+ nprocs=`psrinfo -v | \
+ nawk '/virtual processor/ {cnt++} END {print cnt}'`
+
+ mb_per_core=$(($prov_mem / $nprocs))
+
+ baseline=$(($zmem / $mb_per_core))
+ [[ $baseline == 0 ]] && baseline=1
+ [[ $baseline -gt $cap ]] && baseline=$cap
+
+ prctl -n zone.cpu-baseline -v $baseline -t priv -i zone $ZONENAME
+}
+
+cleanup_snapshots()
+{
+ #
+ # Each ZFS snapshot is mounted separately, so find all mounted
+ # snapshots for this zone, and unmount them.
+ #
+ snaps=$(ls -1 $ZONEPATH/$SNAPSHOT_DIR)
+
+ for snap in ${snaps}; do
+ snapdir=$ZONEPATH/$SNAPSHOT_DIR/$(echo ${snap} | sed -e "s/^vmsnap-//")
+ umount ${snapdir}
+ rmdir ${snapdir}
+ done
+}
+
+#
+# We're halting the zone, perform network cleanup.
+#
+cleanup_net()
+{
+ # Cleanup any flows that were setup.
+ for nic in $_ZONECFG_net_resources
+ do
+ flowadm remove-flow -t -z $ZONENAME -l $nic
+ if (( $? != 0 )); then
+ echo "error removing flows for $nic"
+ logger -p daemon.err "zone $ZONENAME " \
+ "error removing flows for $nic"
+ fi
+ done
+}
+
+id_gz_sockholder()
+{
+ echo "searching for GZ process holding socket $1"
+ logger -p daemon.err "zone $ZONENAME " \
+ "searching for GZ process holding socket $1"
+
+ pid=`(cd /proc;
+ for i in *;
+ do
+ pfiles $i 2>/dev/null | egrep -s "AF_UNIX $1";
+ [ $? == 0 ] && echo "$i";
+ done)`
+
+ [ -z "$pid" ] && return
+
+ echo "Error: GZ process $pid holding socket $1 blocking shutdown"
+ logger -p daemon.err "Error: zone $ZONENAME:" \
+ "GZ process $pid holding socket $1 blocking shutdown"
+}
+
+# zonadmd unable to unmount the given path, try to cleanup so unmount can
+# succeed.
+cleanup_mount()
+{
+ echo "attempting to cleanup mount $1"
+ logger -p daemon.err "zone $ZONENAME attempting to cleanup mount $1"
+
+ fnd_procs=0
+ for i in `fuser -c $1 2>/dev/null`
+ do
+ fnd_procs=1
+
+ pty=`ps -otty -p $i | \
+ nawk '{if ($1 != "TT" && $1 != "?") print $0}'`
+
+ if [ -n "$pty" ]; then
+ echo "shell process $i blocking zone" \
+ "$ZONENAME shutdown, killing the process" | wall
+ echo "killing GZ user shell $i under $1"
+ logger -p daemon.err "zone $ZONENAME:" \
+ "killing GZ user shell $i under $1"
+ kill -9 $i
+ else
+ echo "Error: GZ process $i under $1 blocking shutdown"
+ logger -p daemon.err "Error: zone $ZONENAME:" \
+ "GZ process $i under $1 blocking shutdown"
+
+ local args="pargs: `pargs $i`"
+ echo "$args"
+ logger -p daemon.err "$args"
+
+ local tree="ptree: `ptree $i`"
+ echo "$tree"
+ logger -p daemon.err "$tree"
+ fi
+ done
+
+ if [ $fnd_procs -eq 1 ]; then
+ # Exit out to give the zoneadmd umount a chance to suceed now.
+ # Zoneadmd will give us another shot if it still can't umount.
+ sleep 1
+ exit 0
+ fi
+
+ # Processes which are injected into a zone and then open a file as a
+ # socket end-point will show in pfiles with the path relative to the
+ # zone's root. For example, a zone with its root at /zones/foo/root and
+ # an open socket as /zones/foo/root/var/run/x will show up in a pfiles
+ # search as /var/run/x. This is a problem since we have no way to
+ # narrow down which process is the culprit.
+ #
+ # Because the socket doesn't have enough information for us to tie to
+ # the specific GZ process, we hardcode to id things we know will open
+ # sockets into the zone:
+ # $jst_mdatapath/metadata.sock
+ # /var/run/.smartdc-amon.sock
+
+ ZVR=$ZONEPATH/root/var/run
+ [ -S $ZVR/smartdc/metadata.sock ] &&
+ id_gz_sockholder $jst_mdatapath/metadata.sock
+
+ [ -S $ZVR/.smartdc-amon.sock ] &&
+ id_gz_sockholder /var/run/.smartdc-amon.sock
+}
+
+function fix_forced_attrs {
+ typeset attr
+
+ for attr in ${!FORCED_ATTRS[@]}; do
+ typeset nval=${FORCED_ATTRS["$attr"]}
+ typeset -n envvar=_ZONECFG_attr_${attr//-/_}
+ typeset cval=$envvar
+
+ if [[ $cval == $nval ]]; then
+ # In most cases, $nval and $cval will be the same and
+ # nothing needs to be done. This includes the case where
+ # $nval and $cval are "".
+ continue
+ elif [[ -z $nval ]]; then
+ logger -p daemon.error "[zone $ZONENAME]" \
+ "Illegal value for attr '$attr': '$cval'." \
+ "Removing attr '$attr'"
+ zonecfg -z "$ZONENAME" "remove -F attr name=$attr"
+
+ unset ${!envvar}
+ else
+ logger -p daemon.error "[zone $ZONENAME]" \
+ "Illegal value for attr '$attr': '$cval'." \
+ "Setting to '$nval'"
+ zonecfg -z "$ZONENAME" "remove -F attr name=$attr;" \
+ "add attr; set type=string;" \
+ "set name=$attr; set value=\"$nval\"; end;"
+
+ export ${!envvar}="$nval"
+ fi
+ done
+}
+
+#
+# Main
+#
+
+case $subcommand in
+pre)
+ case $cmd in
+ 0) # pre-ready
+ fix_forced_attrs
+ setup_fs
+ ;;
+ 4) # pre-halt
+ [[ -n "$jst_showsnap" ]] && cleanup_snapshots
+ cleanup_net
+ ;;
+ esac
+ ;;
+post)
+ case $cmd in
+ 0) # post-ready
+ [[ -n "$jst_showsnap" ]] && setup_snapshots
+ setup_net
+ setup_fw
+ ;;
+ 1) # post-boot
+ # We can't set a rctl until we have a process in the zone to
+ # grab
+ setup_cpu_baseline
+ ;;
+ 8) # post-unmount
+ # Zone halt is hung unmounting, try to recover
+ if [[ $state == 6 ]]; then
+ cleanup_mount "$6"
+ fi
+ ;;
+ esac
+ ;;
+esac
+
+exit 0
diff --git a/usr/src/lib/brand/joyent-minimal/Makefile b/usr/src/lib/brand/joyent-minimal/Makefile
new file mode 100644
index 0000000000..0823a1f94e
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/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 2019 Joyent, Inc.
+#
+
+default: all
+
+# Build everything in parallel; use .WAIT for dependencies
+.PARALLEL:
+
+SUBDIRS = zone
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+
+all install clean clobber: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/brand/joyent-minimal/zone/Makefile b/usr/src/lib/brand/joyent-minimal/zone/Makefile
new file mode 100644
index 0000000000..a0fbc14d5c
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/Makefile
@@ -0,0 +1,37 @@
+#
+# 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 2019 Joyent, Inc.
+#
+
+BRAND = joyent-minimal
+
+include $(SRC)/lib/brand/Makefile.brand
+
+PROGS = jattach jdetach jinstall juninstall poststate prestate statechange
+XMLDOCS = config.xml platform.xml
+TXTS = manifests
+
+CLOBBERFILES = $(ROOTXMLDOCS) $(ROOTTXTS) $(ROOTPROGS)
+
+# These shouldn't be necessary, but for some reason the same thing in
+# ../../Makefile.brand is not being picked up.
+$(ROOTPROGS) := FILEMODE = 755
+$(ROOTTXTS) := FILEMODE = 444
+$(ROOTXMLDOCS) := FILEMODE = 444
+
+all: $(PROGS)
+
+install: $(PROGS) $(ROOTPROGS) $(ROOTXMLDOCS) $(ROOTTXTS)
+
+clean:
+ $(RM) $(CLEANFILES)
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/lib/brand/joyent-minimal/zone/config.xml b/usr/src/lib/brand/joyent-minimal/zone/config.xml
new file mode 100644
index 0000000000..64e92464fe
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/config.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright 2015 Joyent, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE brand PUBLIC "-//Joyent Inc//DTD Brands//EN"
+ "file:///usr/share/lib/xml/dtd/brand.dtd.1">
+
+<brand name="joyent-minimal">
+ <modname></modname>
+
+ <initname>/sbin/init</initname>
+ <login_cmd>/usr/bin/login -z %Z %u</login_cmd>
+ <forcedlogin_cmd>/usr/bin/login -z %Z -f %u</forcedlogin_cmd>
+ <user_cmd>/usr/bin/getent passwd %u</user_cmd>
+
+ <install>/usr/lib/brand/joyent-minimal/jinstall -z %z -R %R</install>
+ <installopts>R:t:U:q:z:</installopts>
+ <boot></boot>
+ <halt></halt>
+ <verify_cfg></verify_cfg>
+ <verify_adm></verify_adm>
+ <postclone></postclone>
+ <postinstall></postinstall>
+ <attach>/usr/lib/brand/joyent-minimal/jattach -z %z -R %R</attach>
+ <detach>/usr/lib/brand/joyent-minimal/jdetach -z %z -R %R</detach>
+ <clone></clone>
+ <uninstall>/usr/lib/brand/joyent-minimal/juninstall -z %z -R %R</uninstall>
+ <prestatechange>/usr/lib/brand/joyent-minimal/prestate %z %R</prestatechange>
+ <poststatechange>/usr/lib/brand/joyent-minimal/poststate %z %R</poststatechange>
+ <query>/usr/lib/brand/joyent-minimal/query %z %R</query>
+
+ <privilege set="default" name="contract_event" />
+ <privilege set="default" name="contract_identity" />
+ <privilege set="default" name="contract_observer" />
+ <privilege set="default" name="dtrace_proc" />
+ <privilege set="default" name="dtrace_user" />
+ <privilege set="default" name="file_chown" />
+ <privilege set="default" name="file_chown_self" />
+ <privilege set="default" name="file_dac_execute" />
+ <privilege set="default" name="file_dac_read" />
+ <privilege set="default" name="file_dac_search" />
+ <privilege set="default" name="file_dac_write" />
+ <privilege set="default" name="file_owner" />
+ <privilege set="default" name="file_setid" />
+ <privilege set="default" name="ipc_dac_read" />
+ <privilege set="default" name="ipc_dac_write" />
+ <privilege set="default" name="ipc_owner" />
+ <privilege set="default" name="net_bindmlp" />
+ <privilege set="default" name="net_icmpaccess" />
+ <privilege set="default" name="net_mac_aware" />
+ <privilege set="default" name="net_observability" />
+ <privilege set="default" name="net_privaddr" />
+ <privilege set="default" name="net_rawaccess" ip-type="exclusive" />
+ <privilege set="default" name="proc_chroot" />
+ <privilege set="default" name="sys_audit" />
+ <privilege set="default" name="proc_audit" />
+ <privilege set="default" name="proc_lock_memory" />
+ <privilege set="default" name="proc_owner" />
+ <privilege set="default" name="proc_prioup" />
+ <privilege set="default" name="proc_secflags" />
+ <privilege set="default" name="proc_setid" />
+ <privilege set="default" name="proc_taskid" />
+ <privilege set="default" name="sys_acct" />
+ <privilege set="default" name="sys_admin" />
+ <privilege set="default" name="sys_fs_import" />
+ <privilege set="default" name="sys_ip_config" ip-type="exclusive" />
+ <privilege set="default" name="sys_iptun_config" ip-type="exclusive" />
+ <privilege set="default" name="sys_mount" />
+ <privilege set="default" name="sys_nfs" />
+ <privilege set="default" name="sys_smb" />
+ <privilege set="default" name="sys_resource" />
+ <privilege set="default" name="sys_ppp_config" ip-type="exclusive" />
+
+ <privilege set="prohibited" name="dtrace_kernel" />
+ <privilege set="prohibited" name="proc_zone" />
+ <privilege set="prohibited" name="sys_config" />
+ <privilege set="prohibited" name="sys_devices" />
+ <privilege set="prohibited" name="sys_ip_config" ip-type="shared" />
+ <privilege set="prohibited" name="sys_linkdir" />
+ <privilege set="prohibited" name="sys_net_config" />
+ <privilege set="prohibited" name="sys_res_config" />
+ <privilege set="prohibited" name="sys_suser_compat" />
+ <privilege set="prohibited" name="xvm_control" />
+ <privilege set="prohibited" name="virt_manage" />
+ <privilege set="prohibited" name="sys_ppp_config" ip-type="shared" />
+
+ <privilege set="required" name="proc_exec" />
+ <privilege set="required" name="proc_fork" />
+ <privilege set="required" name="sys_ip_config" ip-type="exclusive" />
+ <privilege set="required" name="sys_mount" />
+</brand>
diff --git a/usr/src/lib/brand/joyent-minimal/zone/jattach b/usr/src/lib/brand/joyent-minimal/zone/jattach
new file mode 100755
index 0000000000..b99c0211fe
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/jattach
@@ -0,0 +1,26 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+. /usr/lib/brand/jcommon/libhooks.ksh
+
+function jcommon_attach_hook
+{
+ jattach_zone_final_setup
+}
+
+ps_brand="joyent-minimal"
+. /usr/lib/brand/jcommon/cattach
diff --git a/usr/src/lib/brand/joyent-minimal/zone/jdetach b/usr/src/lib/brand/joyent-minimal/zone/jdetach
new file mode 100755
index 0000000000..5671f06bf9
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/jdetach
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="joyent-minimal"
+. /usr/lib/brand/jcommon/cdetach
diff --git a/usr/src/lib/brand/joyent-minimal/zone/jinstall b/usr/src/lib/brand/joyent-minimal/zone/jinstall
new file mode 100755
index 0000000000..167566bdca
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/jinstall
@@ -0,0 +1,32 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+# Does this brand support reprovisioning?
+jst_reprovision="yes"
+
+# Is a template image optional?
+# jst_tmplopt="yes"
+
+. /usr/lib/brand/jcommon/libhooks.ksh
+
+function jcommon_attach_hook
+{
+ jattach_zone_final_setup
+ cp /usr/lib/brand/joyent-minimal/repository.db ${ZROOT}/etc/svc
+}
+
+. /usr/lib/brand/jcommon/cinstall
diff --git a/usr/src/lib/brand/joyent-minimal/zone/juninstall b/usr/src/lib/brand/joyent-minimal/zone/juninstall
new file mode 100755
index 0000000000..136044ad9e
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/juninstall
@@ -0,0 +1,23 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+function jcommon_uninstall_hook
+{
+ :
+}
+
+. /usr/lib/brand/jcommon/cuninstall
diff --git a/usr/src/lib/brand/joyent-minimal/zone/manifests b/usr/src/lib/brand/joyent-minimal/zone/manifests
new file mode 100644
index 0000000000..8426667584
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/manifests
@@ -0,0 +1,126 @@
+#
+# This file lists the SMF manifests which are available in the global
+# zone and which should be setup for use in non-global zones. Not
+# all SMF services are usable within a zone, so only those that are usable
+# should be listed here.
+#
+# The format of the file is:
+# manifest_name enable/disable
+# The manifest_name is found relative to /lib/svc/manifest on the system.
+# The second field is used to indicate if the svc should be enabled or
+# disabled by default within zones, independently of its current status in
+# the global zone.
+#
+# The following is a list of SMF svcs which could be run in a zone, but
+# which we're not currently installing on our live image. If we add any
+# of these to the image and we want it to also be available in zones,
+# then we need to move the entry to the end and uncomment it out.
+#
+# application/cups.xml
+# application/font/fc-cache.xml
+# application/graphical-login/gdm.xml
+# application/management/net-snmp.xml
+# application/management/seaport.xml
+# application/management/snmpdx.xml
+# application/opengl/ogl-select.xml
+# application/print/service-selector.xml
+# application/security/tcsd.xml
+# application/x11/x11-server.xml
+# application/x11/xfs.xml
+# application/x11/xvnc-inetd.xml
+# network/ldap/client.xml
+# network/rpc/keyserv.xml
+# network/nfs/rquota.xml
+# network/smb/server.xml
+# network/dns/server.xml
+# network/ftp.xml
+# network/finger.xml
+# network/comsat.xml
+# network/rpc/rstat.xml
+# network/rpc/rusers.xml
+# network/rpc/spray.xml
+# network/rpc/wall.xml
+# network/talk.xml
+# network/ntp.xml
+# network/sendmail-client.xml
+# network/smtp-sendmail.xml
+# network/telnet.xml
+# network/wpa.xml
+# network/rpc/gss.xml
+# network/security/kadmin.xml
+# network/security/krb5kdc.xml
+# network/security/ktkt_warn.xml
+# network/rpc/smserver.xml
+# network/nfs/cbd.xml
+# network/nfs/mapid.xml
+# network/nfs/status.xml
+# network/ipmievd.xml
+# network/nis/client.xml
+# network/http-apache22.xml
+# system/consolekit.xml
+# system/device/devices-audio.xml
+# system/fm/notify-params.xml
+# system/install/system-config.xml
+# system/pkgserv.xml
+#
+
+#
+# The following is a list of SMF svcs which are installed on our live image and
+# which could be run in a zone, but which we choose not to run in our zones.
+# If we ever do want to run any of these, move the entry to the end and
+# uncomment it out.
+#
+# network/nfs/client.xml
+# network/nfs/nlockmgr.xml
+# system/boot-archive-update.xml
+# system/boot-archive.xml
+# system/boot-config.xml
+# system/cron.xml
+# system/fmd.xml
+# system/sac.xml
+#
+
+#
+# The following is a list of SMF svc manifests under /lib/svc/manifest. These
+# are available on our live image and are runnable in a zone. Since the zones
+# are sharing the base file system with the global zone, we set up so that
+# only these svcs are configured in zones, independently of the global zone
+# /lib/svc/manifest hierarchy.
+#
+
+system/svc/restarter.xml enabled
+network/dlmgmt.xml enabled
+network/routing/ndp.xml disabled
+system/filesystem/root-fs.xml enabled
+#
+# This was running, but not in our set of manifests. Maybe it came in
+# from /var.
+#
+system/boot-archive.xml enabled
+network/network-ipmgmt.xml enabled
+system/filesystem/usr-fs.xml enabled
+network/network-loopback.xml enabled
+system/device/devices-local.xml enabled
+milestone/single-user.xml enabled
+network/network-physical.xml enabled
+system/filesystem/minimal-fs.xml enabled
+milestone/network.xml enabled
+system/filesystem/joyent-fs.xml enabled
+system/filesystem/local-fs.xml enabled
+system/name-service-cache.xml enabled
+system/process-security.xml enabled
+milestone/name-services.xml enabled
+system/identity.xml enabled
+network/network-initial.xml enabled
+network/network-service.xml enabled
+network/dns/client.xml enabled
+network/network-netmask.xml enabled
+network/network-routing-setup.xml enabled
+milestone/sysconfig.xml enabled
+system/system-log.xml enabled
+milestone/multi-user-server.xml enabled
+milestone/multi-user.xml enabled
+milestone/name-services.xml enabled
+system/early-manifest-import.xml disabled
+system/manifest-import.xml disabled
+system/mdata.xml enabled
diff --git a/usr/src/lib/brand/joyent-minimal/zone/platform.xml b/usr/src/lib/brand/joyent-minimal/zone/platform.xml
new file mode 100644
index 0000000000..f227b409b3
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/platform.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0"?>
+
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright 2017 Joyent, Inc.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE platform PUBLIC "-//Joyent Inc//Zones Platform//EN"
+ "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1">
+
+<platform name="joyent-minimal" allow-exclusive-ip="true">
+
+ <!-- Global filesystems to mount when booting the zone -->
+ <global_mount special="/dev" directory="/dev" type="dev"
+ opt="attrdir=%R/root/dev"/>
+
+ <global_mount special="/var/zonecontrol/%z" directory="/.zonecontrol"
+ opt="ro,nodevices,nosetuid,noexec" type="lofs" />
+
+ <global_mount special="/lib" directory="/lib"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="%P/manifests/joyent" directory="/lib/svc/manifest"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="%R/site" directory="/lib/svc/manifest/site"
+ opt="nodevices" type="lofs" />
+ <global_mount special="/sbin" directory="/sbin"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="/usr" directory="/usr"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="%R/local" directory="/usr/local"
+ opt="nodevices" type="lofs" />
+ <global_mount special="%R/ccs" directory="/usr/ccs"
+ opt="nodevices" type="lofs" />
+ <mount special="sharefs" directory="/etc/dfs/sharetab" type="sharefs" />
+
+ <!-- Local filesystems to mount when booting the zone -->
+ <mount special="/proc" directory="/proc" type="proc" />
+ <mount special="ctfs" directory="/system/contract" type="ctfs" />
+ <mount special="mnttab" directory="/etc/mnttab" type="mntfs" />
+ <mount special="objfs" directory="/system/object" type="objfs" />
+ <mount special="lxproc" directory="/system/lxproc" type="lxproc" />
+ <mount special="swap" directory="/etc/svc/volatile" type="tmpfs" />
+
+ <!-- Devices to create under /dev -->
+ <device match="arp" />
+ <device match="bpf" />
+ <device match="conslog" />
+ <device match="cpu/self/cpuid" />
+ <device match="crypto" />
+ <device match="cryptoadm" />
+ <device match="dsk" />
+ <device match="dtrace/*" />
+ <device match="dtrace/provider/*" />
+ <device match="eventfd" />
+ <device match="fd" />
+ <device match="full" />
+ <device match="inotify" />
+ <device match="ipnet" />
+ <device match="kstat" />
+ <device match="lo0" />
+ <device match="lofictl" />
+ <device match="lofi" />
+ <device match="log" />
+ <device match="logindmux" />
+ <device match="nsmb" />
+ <device match="net/*" />
+ <device match="null" />
+ <device match="openprom" arch="sparc" />
+ <device match="poll" />
+ <device match="pool" />
+ <device match="ptmx" />
+ <device match="pts/*" />
+ <device match="random" />
+ <device match="rdsk" />
+ <device match="rlofi" />
+ <device match="rmt" />
+ <device match="sad/user" />
+ <device match="smbsrv" />
+ <device match="signalfd" />
+ <device match="svvslo0" />
+ <device match="svvslo1" />
+ <device match="svvslo2" />
+ <device match="svvslo3" />
+ <device match="swap" />
+ <device match="sysevent" />
+ <device match="tap" />
+ <device match="tcp" />
+ <device match="tcp6" />
+ <device match="term" />
+ <device match="ticlts" />
+ <device match="ticots" />
+ <device match="ticotsord" />
+ <device match="timerfd" />
+ <device match="tty" />
+ <device match="tun" />
+ <device match="udp" />
+ <device match="udp6" />
+ <device match="urandom" />
+ <device match="vnd/*" />
+ <device match="zero" />
+ <device match="zfd/%z/slave/0" name="zfd/0" />
+ <device match="zfd/%z/slave/1" name="zfd/1" />
+ <device match="zfd/%z/slave/2" name="zfd/2" />
+ <device match="zfs" />
+ <device match="zvol/dsk/%P/%z/*" />
+ <device match="zvol/rdsk/%P/%z/*" />
+
+ <!-- Devices to create in exclusive IP zone only -->
+ <device match="dld" ip-type="exclusive" />
+ <device match="icmp" ip-type="exclusive" />
+ <device match="icmp6" ip-type="exclusive" />
+ <device match="ip" ip-type="exclusive" />
+ <device match="ip6" ip-type="exclusive" />
+ <device match="ipauth" ip-type="exclusive" />
+ <device match="ipd" ip-type="exclusive" />
+ <device match="ipf" ip-type="exclusive" />
+ <device match="ipl" ip-type="exclusive" />
+ <device match="iplookup" ip-type="exclusive" />
+ <device match="ipmpstub" ip-type="exclusive" />
+ <device match="ipnat" ip-type="exclusive" />
+ <device match="ipscan" ip-type="exclusive" />
+ <device match="ipsecah" ip-type="exclusive" />
+ <device match="ipsecesp" ip-type="exclusive" />
+ <device match="ipstate" ip-type="exclusive" />
+ <device match="ipsync" ip-type="exclusive" />
+ <device match="keysock" ip-type="exclusive" />
+ <device match="rawip" ip-type="exclusive" />
+ <device match="rawip6" ip-type="exclusive" />
+ <device match="rts" ip-type="exclusive" />
+ <device match="sad/admin" ip-type="exclusive" />
+ <device match="sctp" ip-type="exclusive" />
+ <device match="sctp6" ip-type="exclusive" />
+ <device match="spdsock" ip-type="exclusive" />
+ <device match="sppp" ip-type="exclusive" />
+ <device match="sppptun" ip-type="exclusive" />
+ <device match="vni" ip-type="exclusive" />
+
+ <!-- Renamed devices to create under /dev -->
+ <device match="zcons/%z/zoneconsole" name="zconsole" />
+
+ <!-- Symlinks to create under /dev -->
+ <symlink source="console" target="zconsole" />
+ <symlink source="dtremote" target="/dev/null" />
+ <symlink source="msglog" target="zconsole" />
+ <symlink source="stderr" target="./fd/2" />
+ <symlink source="stdin" target="./fd/0" />
+ <symlink source="stdout" target="./fd/1" />
+ <symlink source="syscon" target="zconsole" />
+ <symlink source="sysmsg" target="zconsole" />
+ <symlink source="systty" target="zconsole" />
+
+</platform>
diff --git a/usr/src/lib/brand/joyent-minimal/zone/poststate b/usr/src/lib/brand/joyent-minimal/zone/poststate
new file mode 100755
index 0000000000..be489ef55b
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/poststate
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="joyent-minimal"
+. /usr/lib/brand/jcommon/poststate
diff --git a/usr/src/lib/brand/joyent-minimal/zone/prestate b/usr/src/lib/brand/joyent-minimal/zone/prestate
new file mode 100755
index 0000000000..e6aeb1cba5
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/prestate
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="joyent-minimal"
+. /usr/lib/brand/jcommon/prestate
diff --git a/usr/src/lib/brand/joyent-minimal/zone/statechange b/usr/src/lib/brand/joyent-minimal/zone/statechange
new file mode 100755
index 0000000000..5e2eeff0f2
--- /dev/null
+++ b/usr/src/lib/brand/joyent-minimal/zone/statechange
@@ -0,0 +1,42 @@
+#!/bin/ksh -p
+#
+#
+# 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) 2018 Joyent, Inc. All rights reserved.
+#
+
+# Do we support vrrp?
+jst_vrrp="yes"
+
+# Do we support unfiltered promiscuous mode
+# jst_ufpromisc="yes"
+
+# Do we use vnd devices
+# jst_createvnd="yes"
+
+# Do we not need to have various SMF and ccs stuff created for us?
+# jst_simplefs="yes"
+
+# Should we show snapshots?
+jst_showsnap="yes"
+
+# Where does the mdata socket live?
+jst_mdatapath="/.zonecontrol/"
+
+# Ensure docker zfd(7D) log is not put in the wrong place
+if [[ "$_ZONECFG_attr_docker" == true ]]; then
+ typeset -A FORCED_ATTRS
+ FORCED_ATTRS["zlog-name"]=
+fi
+
+. /usr/lib/brand/jcommon/statechange
diff --git a/usr/src/lib/brand/joyent/Makefile b/usr/src/lib/brand/joyent/Makefile
new file mode 100644
index 0000000000..0823a1f94e
--- /dev/null
+++ b/usr/src/lib/brand/joyent/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 2019 Joyent, Inc.
+#
+
+default: all
+
+# Build everything in parallel; use .WAIT for dependencies
+.PARALLEL:
+
+SUBDIRS = zone
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+
+all install clean clobber: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/brand/joyent/zone/Joyent.xml b/usr/src/lib/brand/joyent/zone/Joyent.xml
new file mode 100644
index 0000000000..cbb810301e
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/Joyent.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+
+<!--
+ Copyright 2010 Joyent, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+
+ DO NOT EDIT THIS FILE. Use zonecfg(1M) instead.
+-->
+
+<!DOCTYPE zone PUBLIC "-//Joyent Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1">
+
+<zone name="default" zonepath="" autoboot="false" brand="joyent">
+</zone>
diff --git a/usr/src/lib/brand/joyent/zone/Makefile b/usr/src/lib/brand/joyent/zone/Makefile
new file mode 100644
index 0000000000..3ffc11c8a0
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/Makefile
@@ -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 2019 Joyent, Inc.
+#
+
+BRAND = joyent
+
+include $(SRC)/lib/brand/Makefile.brand
+
+PROGS = jattach jdetach jinstall juninstall poststate prestate statechange
+XMLDOCS = config.xml platform.xml
+TEMPLATES = Joyent.xml SUNWdefault.xml
+TXTS = manifests
+
+CLOBBERFILES = $(ROOTXMLDOCS) $(ROOTTXTS) $(ROOTPROGS) $(ROOTTEMPLATES)
+
+# These shouldn't be necessary, but for some reason the same thing in
+# ../../Makefile.brand is not being picked up.
+$(ROOTPROGS) := FILEMODE = 755
+$(ROOTTXTS) := FILEMODE = 444
+$(ROOTXMLDOCS) := FILEMODE = 444
+$(ROOTTEMPLATES) := FILEMODE = 444
+
+all: $(PROGS)
+
+install: $(PROGS) $(ROOTPROGS) $(ROOTXMLDOCS) $(ROOTTXTS) $(ROOTTEMPLATES)
+
+clean:
+ $(RM) $(CLEANFILES)
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/lib/brand/ipkg/zone/SUNWdefault.xml b/usr/src/lib/brand/joyent/zone/SUNWdefault.xml
index bc4bfc7b79..2ab9089dfe 100644
--- a/usr/src/lib/brand/ipkg/zone/SUNWdefault.xml
+++ b/usr/src/lib/brand/joyent/zone/SUNWdefault.xml
@@ -28,5 +28,5 @@
<!DOCTYPE zone PUBLIC "-//Sun Microsystems Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1">
-<zone name="default" zonepath="" autoboot="false" brand="ipkg">
+<zone name="default" zonepath="" autoboot="false" brand="joyent">
</zone>
diff --git a/usr/src/lib/brand/joyent/zone/config.xml b/usr/src/lib/brand/joyent/zone/config.xml
new file mode 100644
index 0000000000..c6fdb574dd
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/config.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0"?>
+
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright 2015 Joyent, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE brand PUBLIC "-//Joyent Inc//DTD Brands//EN"
+ "file:///usr/share/lib/xml/dtd/brand.dtd.1">
+
+<brand name="joyent">
+ <modname></modname>
+
+ <initname>/sbin/init</initname>
+ <login_cmd>/usr/bin/login -z %Z %u</login_cmd>
+ <forcedlogin_cmd>/usr/bin/login -z %Z -f %u</forcedlogin_cmd>
+ <user_cmd>/usr/bin/getent passwd %u</user_cmd>
+
+ <install>/usr/lib/brand/joyent/jinstall -z %z -R %R</install>
+ <installopts>R:t:U:q:z:</installopts>
+ <boot></boot>
+ <halt></halt>
+ <verify_cfg></verify_cfg>
+ <verify_adm></verify_adm>
+ <postclone></postclone>
+ <postinstall></postinstall>
+ <attach>/usr/lib/brand/joyent/jattach -z %z -R %R</attach>
+ <detach>/usr/lib/brand/joyent/jdetach -z %z -R %R</detach>
+ <clone></clone>
+ <uninstall>/usr/lib/brand/joyent/juninstall -z %z -R %R</uninstall>
+ <prestatechange>/usr/lib/brand/joyent/prestate %z %R</prestatechange>
+ <poststatechange>/usr/lib/brand/joyent/poststate %z %R</poststatechange>
+ <query>/usr/lib/brand/joyent/query %z %R</query>
+
+ <privilege set="default" name="contract_event" />
+ <privilege set="default" name="contract_identity" />
+ <privilege set="default" name="contract_observer" />
+ <privilege set="default" name="dtrace_proc" />
+ <privilege set="default" name="dtrace_user" />
+ <privilege set="default" name="file_chown" />
+ <privilege set="default" name="file_chown_self" />
+ <privilege set="default" name="file_dac_execute" />
+ <privilege set="default" name="file_dac_read" />
+ <privilege set="default" name="file_dac_search" />
+ <privilege set="default" name="file_dac_write" />
+ <privilege set="default" name="file_owner" />
+ <privilege set="default" name="file_setid" />
+ <privilege set="default" name="ipc_dac_read" />
+ <privilege set="default" name="ipc_dac_write" />
+ <privilege set="default" name="ipc_owner" />
+ <privilege set="default" name="net_bindmlp" />
+ <privilege set="default" name="net_icmpaccess" />
+ <privilege set="default" name="net_mac_aware" />
+ <privilege set="default" name="net_observability" />
+ <privilege set="default" name="net_privaddr" />
+ <privilege set="default" name="net_rawaccess" ip-type="exclusive" />
+ <privilege set="default" name="proc_chroot" />
+ <privilege set="default" name="sys_audit" />
+ <privilege set="default" name="proc_audit" />
+ <privilege set="default" name="proc_lock_memory" />
+ <privilege set="default" name="proc_owner" />
+ <privilege set="default" name="proc_prioup" />
+ <privilege set="default" name="proc_secflags" />
+ <privilege set="default" name="proc_setid" />
+ <privilege set="default" name="proc_taskid" />
+ <privilege set="default" name="sys_acct" />
+ <privilege set="default" name="sys_admin" />
+ <privilege set="default" name="sys_fs_import" />
+ <privilege set="default" name="sys_ip_config" ip-type="exclusive" />
+ <privilege set="default" name="sys_iptun_config" ip-type="exclusive" />
+ <privilege set="default" name="sys_mount" />
+ <privilege set="default" name="sys_nfs" />
+ <privilege set="default" name="sys_smb" />
+ <privilege set="default" name="sys_resource" />
+ <privilege set="default" name="sys_ppp_config" ip-type="exclusive" />
+
+ <privilege set="prohibited" name="dtrace_kernel" />
+ <privilege set="prohibited" name="proc_zone" />
+ <privilege set="prohibited" name="sys_config" />
+ <privilege set="prohibited" name="sys_devices" />
+ <privilege set="prohibited" name="sys_ip_config" ip-type="shared" />
+ <privilege set="prohibited" name="sys_linkdir" />
+ <privilege set="prohibited" name="sys_net_config" />
+ <privilege set="prohibited" name="sys_res_config" />
+ <privilege set="prohibited" name="sys_suser_compat" />
+ <privilege set="prohibited" name="xvm_control" />
+ <privilege set="prohibited" name="virt_manage" />
+ <privilege set="prohibited" name="sys_ppp_config" ip-type="shared" />
+
+ <privilege set="required" name="proc_exec" />
+ <privilege set="required" name="proc_fork" />
+ <privilege set="required" name="sys_ip_config" ip-type="exclusive" />
+ <privilege set="required" name="sys_mount" />
+</brand>
diff --git a/usr/src/lib/brand/joyent/zone/jattach b/usr/src/lib/brand/joyent/zone/jattach
new file mode 100755
index 0000000000..11844b9ac4
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/jattach
@@ -0,0 +1,26 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+. /usr/lib/brand/jcommon/libhooks.ksh
+
+function jcommon_attach_hook
+{
+ jattach_zone_final_setup
+}
+
+ps_brand="joyent"
+. /usr/lib/brand/jcommon/cattach
diff --git a/usr/src/lib/brand/joyent/zone/jdetach b/usr/src/lib/brand/joyent/zone/jdetach
new file mode 100755
index 0000000000..89681919f1
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/jdetach
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="joyent"
+. /usr/lib/brand/jcommon/cdetach
diff --git a/usr/src/lib/brand/joyent/zone/jinstall b/usr/src/lib/brand/joyent/zone/jinstall
new file mode 100755
index 0000000000..8e6ec2a7b2
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/jinstall
@@ -0,0 +1,31 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+# Does this brand support reprovisioning?
+jst_reprovision="yes"
+
+# Is a template image optional?
+# jst_tmplopt="yes"
+
+. /usr/lib/brand/jcommon/libhooks.ksh
+
+function jcommon_attach_hook
+{
+ jattach_zone_final_setup
+}
+
+. /usr/lib/brand/jcommon/cinstall
diff --git a/usr/src/lib/brand/joyent/zone/juninstall b/usr/src/lib/brand/joyent/zone/juninstall
new file mode 100755
index 0000000000..136044ad9e
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/juninstall
@@ -0,0 +1,23 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+function jcommon_uninstall_hook
+{
+ :
+}
+
+. /usr/lib/brand/jcommon/cuninstall
diff --git a/usr/src/lib/brand/joyent/zone/manifests b/usr/src/lib/brand/joyent/zone/manifests
new file mode 100644
index 0000000000..e465b8c7cc
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/manifests
@@ -0,0 +1,175 @@
+#
+# This file lists the SMF manifests which are available in the global
+# zone and which should be setup for use in non-global zones. Not
+# all SMF services are usable within a zone, so only those that are usable
+# should be listed here.
+#
+# The format of the file is:
+# manifest_name enable/disable
+# The manifest_name is found relative to /lib/svc/manifest on the system.
+# The second field is used to indicate if the svc should be enabled or
+# disabled by default within zones, independently of its current status in
+# the global zone.
+#
+# The following is a list of SMF svcs which could be run in a zone, but
+# which we're not currently installing on our live image. If we add any
+# of these to the image and we want it to also be available in zones,
+# then we need to move the entry to the end and uncomment it out.
+#
+# application/cups.xml
+# application/font/fc-cache.xml
+# application/graphical-login/gdm.xml
+# application/management/net-snmp.xml
+# application/management/seaport.xml
+# application/management/snmpdx.xml
+# application/opengl/ogl-select.xml
+# application/print/service-selector.xml
+# application/security/tcsd.xml
+# application/x11/x11-server.xml
+# application/x11/xfs.xml
+# application/x11/xvnc-inetd.xml
+# network/rpc/keyserv.xml
+# network/dns/server.xml
+# network/ftp.xml
+# network/finger.xml
+# network/comsat.xml
+# network/rpc/rstat.xml
+# network/rpc/rusers.xml
+# network/rpc/spray.xml
+# network/rpc/wall.xml
+# network/talk.xml
+# network/ntp.xml
+# network/sendmail-client.xml
+# network/smtp-sendmail.xml
+# network/telnet.xml
+# network/wpa.xml
+# network/rpc/gss.xml
+# network/security/kadmin.xml
+# network/security/krb5kdc.xml
+# network/security/ktkt_warn.xml
+# network/rpc/smserver.xml
+# network/nfs/cbd.xml
+# network/ipmievd.xml
+# network/http-apache22.xml
+# system/consolekit.xml
+# system/device/devices-audio.xml
+# system/fm/notify-params.xml
+# system/install/system-config.xml
+# system/pkgserv.xml
+#
+
+#
+# The following is a list of SMF svcs which are installed on our live image and
+# which could be run in a zone, but which we choose not to run in our zones.
+# If we ever do want to run any of these, move the entry to the end and
+# uncomment it out.
+#
+# system/boot-archive-update.xml
+# system/boot-config.xml
+#
+
+#
+# The following is a list of SMF svc manifests under /lib/svc/manifest. These
+# are available on our live image and are runnable in a zone. Since the zones
+# are sharing the base file system with the global zone, we set up so that
+# only these svcs are configured in zones, independently of the global zone
+# /lib/svc/manifest hierarchy.
+#
+milestone/multi-user-server.xml enabled
+milestone/multi-user.xml enabled
+milestone/name-services.xml enabled
+milestone/network.xml enabled
+milestone/single-user.xml enabled
+milestone/sysconfig.xml enabled
+network/bridge.xml enabled
+network/dlmgmt.xml enabled
+network/dns/client.xml enabled
+network/dns/install.xml disabled
+network/dns/multicast.xml disabled
+network/forwarding.xml disabled
+network/inetd-upgrade.xml enabled
+network/inetd.xml disabled
+network/ipfilter.xml disabled
+network/ipsec/ike.xml disabled
+network/ipsec/ipsecalgs.xml enabled
+network/ipsec/manual-key.xml disabled
+network/ipsec/policy.xml enabled
+network/ldap/client.xml disabled
+network/loadbalancer/ilbd.xml disabled
+network/login.xml enabled
+network/network-initial.xml enabled
+network/network-install.xml disabled
+network/network-ipmgmt.xml enabled
+network/network-ipqos.xml disabled
+network/network-iptun.xml enabled
+network/network-location.xml disabled
+network/network-loopback.xml enabled
+network/network-netcfg.xml enabled
+network/network-netmask.xml enabled
+network/network-physical.xml enabled
+network/network-routing-setup.xml enabled
+network/network-service.xml enabled
+network/nfs/client.xml disabled
+network/nfs/mapid.xml disabled
+network/nfs/nlockmgr.xml disabled
+network/nfs/rquota.xml disabled
+network/nfs/status.xml disabled
+network/nfs/server.xml disabled
+network/nis/client.xml disabled
+network/rexec.xml disabled
+network/routing/legacy-routing.xml enabled
+network/routing/ndp.xml disabled
+network/routing/rdisc.xml disabled
+network/routing/ripng.xml disabled
+network/routing/route.xml disabled
+network/rpc/bind.xml enabled
+network/shares/group.xml enabled
+network/shares/reparsed.xml disabled
+network/shell.xml disabled
+network/slp.xml disabled
+network/smb/client.xml disabled
+network/smb/server.xml disabled
+network/ssh.xml enabled
+network/vrrp.xml disabled
+system/auditd.xml disabled
+system/auditset.xml disabled
+system/boot-archive.xml enabled
+system/consadm.xml disabled
+system/console-login.xml disabled
+system/coreadm.xml enabled
+system/cron.xml enabled
+system/cryptosvc.xml enabled
+system/device/allocate.xml disabled
+system/device/devices-local.xml enabled
+system/device/mpxio-upgrade.xml disabled
+system/early-manifest-import.xml enabled
+system/extended-accounting.xml enabled
+system/filesystem/autofs.xml disabled
+system/filesystem/joyent-fs.xml enabled
+system/filesystem/local-fs.xml enabled
+system/filesystem/minimal-fs.xml enabled
+system/filesystem/root-fs.xml enabled
+system/filesystem/usr-fs.xml enabled
+system/fmd.xml disabled
+system/hostid.xml enabled
+system/hotplug.xml disabled
+system/identity.xml enabled
+system/idmap.xml disabled
+system/keymap.xml enabled
+system/logadm-upgrade.xml enabled
+system/manifest-import.xml enabled
+system/mdata.xml disabled
+system/name-service-cache.xml enabled
+system/pfexecd.xml enabled
+system/process-security.xml enabled
+system/rbac.xml enabled
+system/rcap.xml disabled
+system/rmtmpfiles.xml enabled
+system/sac.xml disabled
+system/sar.xml disabled
+system/svc/global.xml disabled
+system/svc/restarter.xml enabled
+system/sysidtool.xml enabled
+system/system-log.xml enabled
+system/utmp.xml enabled
+system/vtdaemon.xml disabled
diff --git a/usr/src/lib/brand/joyent/zone/platform.xml b/usr/src/lib/brand/joyent/zone/platform.xml
new file mode 100644
index 0000000000..f07f2378e7
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/platform.xml
@@ -0,0 +1,169 @@
+<?xml version="1.0"?>
+
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright 2017 Joyent, Inc.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE platform PUBLIC "-//Joyent Inc//Zones Platform//EN"
+ "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1">
+
+<platform name="joyent" allow-exclusive-ip="true">
+
+ <!-- Global filesystems to mount when booting the zone -->
+ <global_mount special="/dev" directory="/dev" type="dev"
+ opt="attrdir=%R/root/dev"/>
+
+ <global_mount special="/var/zonecontrol/%z" directory="/.zonecontrol"
+ opt="ro,nodevices,nosetuid,noexec" type="lofs" />
+ <global_mount special="/lib" directory="/lib"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="%P/manifests/joyent"
+ directory="/lib/svc/manifest"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="%R/site" directory="/lib/svc/manifest/site"
+ opt="nodevices" type="lofs" />
+ <global_mount special="/sbin" directory="/sbin"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="/usr" directory="/usr"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="%R/local" directory="/usr/local"
+ opt="nodevices" type="lofs" />
+ <global_mount special="%R/ccs" directory="/usr/ccs"
+ opt="nodevices" type="lofs" />
+
+ <!-- Local filesystems to mount when booting the zone -->
+ <mount special="/proc" directory="/proc" type="proc" />
+ <mount special="ctfs" directory="/system/contract" type="ctfs" />
+ <mount special="mnttab" directory="/etc/mnttab" type="mntfs" />
+ <mount special="objfs" directory="/system/object" type="objfs" />
+ <mount special="lxproc" directory="/system/lxproc" type="lxproc" />
+ <mount special="swap" directory="/etc/svc/volatile" type="tmpfs" />
+ <mount special="sharefs" directory="/etc/dfs/sharetab" type="sharefs" />
+
+ <!-- Devices to create under /dev -->
+ <device match="arp" />
+ <device match="bpf" />
+ <device match="conslog" />
+ <device match="cpu/self/cpuid" />
+ <device match="crypto" />
+ <device match="cryptoadm" />
+ <device match="dsk" />
+ <device match="dtrace/*" />
+ <device match="dtrace/provider/*" />
+ <device match="eventfd" />
+ <device match="fd" />
+ <device match="full" />
+ <device match="inotify" />
+ <device match="ipnet" />
+ <device match="kstat" />
+ <device match="lo0" />
+ <device match="lofictl" />
+ <device match="lofi" />
+ <device match="log" />
+ <device match="logindmux" />
+ <device match="nsmb" />
+ <device match="net/*" />
+ <device match="null" />
+ <device match="openprom" arch="sparc" />
+ <device match="poll" />
+ <device match="pool" />
+ <device match="ptmx" />
+ <device match="pts/*" />
+ <device match="random" />
+ <device match="rdsk" />
+ <device match="rlofi" />
+ <device match="rmt" />
+ <device match="sad/user" />
+ <device match="smbsrv" />
+ <device match="signalfd" />
+ <device match="svvslo0" />
+ <device match="svvslo1" />
+ <device match="svvslo2" />
+ <device match="svvslo3" />
+ <device match="swap" />
+ <device match="sysevent" />
+ <device match="tap" />
+ <device match="tcp" />
+ <device match="tcp6" />
+ <device match="term" />
+ <device match="ticlts" />
+ <device match="ticots" />
+ <device match="ticotsord" />
+ <device match="timerfd" />
+ <device match="tty" />
+ <device match="tun" />
+ <device match="udp" />
+ <device match="udp6" />
+ <device match="urandom" />
+ <device match="vnd/*" />
+ <device match="zero" />
+ <device match="zfs" />
+ <device match="zvol/dsk/%P/%z/*" />
+ <device match="zvol/rdsk/%P/%z/*" />
+
+ <!-- Devices to create in exclusive IP zone only -->
+ <device match="dld" ip-type="exclusive" />
+ <device match="icmp" ip-type="exclusive" />
+ <device match="icmp6" ip-type="exclusive" />
+ <device match="ip" ip-type="exclusive" />
+ <device match="ip6" ip-type="exclusive" />
+ <device match="ipauth" ip-type="exclusive" />
+ <device match="ipd" ip-type="exclusive" />
+ <device match="ipf" ip-type="exclusive" />
+ <device match="ipl" ip-type="exclusive" />
+ <device match="iplookup" ip-type="exclusive" />
+ <device match="ipmpstub" ip-type="exclusive" />
+ <device match="ipnat" ip-type="exclusive" />
+ <device match="ipscan" ip-type="exclusive" />
+ <device match="ipsecah" ip-type="exclusive" />
+ <device match="ipsecesp" ip-type="exclusive" />
+ <device match="ipstate" ip-type="exclusive" />
+ <device match="ipsync" ip-type="exclusive" />
+ <device match="keysock" ip-type="exclusive" />
+ <device match="rawip" ip-type="exclusive" />
+ <device match="rawip6" ip-type="exclusive" />
+ <device match="rts" ip-type="exclusive" />
+ <device match="sad/admin" ip-type="exclusive" />
+ <device match="sctp" ip-type="exclusive" />
+ <device match="sctp6" ip-type="exclusive" />
+ <device match="spdsock" ip-type="exclusive" />
+ <device match="sppp" ip-type="exclusive" />
+ <device match="sppptun" ip-type="exclusive" />
+ <device match="vni" ip-type="exclusive" />
+
+ <!-- Renamed devices to create under /dev -->
+ <device match="zcons/%z/zoneconsole" name="zconsole" />
+
+ <!-- Symlinks to create under /dev -->
+ <symlink source="console" target="zconsole" />
+ <symlink source="dtremote" target="/dev/null" />
+ <symlink source="msglog" target="zconsole" />
+ <symlink source="stderr" target="./fd/2" />
+ <symlink source="stdin" target="./fd/0" />
+ <symlink source="stdout" target="./fd/1" />
+ <symlink source="syscon" target="zconsole" />
+ <symlink source="sysmsg" target="zconsole" />
+ <symlink source="systty" target="zconsole" />
+
+</platform>
diff --git a/usr/src/lib/brand/joyent/zone/poststate b/usr/src/lib/brand/joyent/zone/poststate
new file mode 100755
index 0000000000..6cd0d0a18f
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/poststate
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="joyent"
+. /usr/lib/brand/jcommon/poststate
diff --git a/usr/src/lib/brand/joyent/zone/prestate b/usr/src/lib/brand/joyent/zone/prestate
new file mode 100755
index 0000000000..10b73285c6
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/prestate
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="joyent"
+. /usr/lib/brand/jcommon/prestate
diff --git a/usr/src/lib/brand/joyent/zone/statechange b/usr/src/lib/brand/joyent/zone/statechange
new file mode 100755
index 0000000000..5e2eeff0f2
--- /dev/null
+++ b/usr/src/lib/brand/joyent/zone/statechange
@@ -0,0 +1,42 @@
+#!/bin/ksh -p
+#
+#
+# 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) 2018 Joyent, Inc. All rights reserved.
+#
+
+# Do we support vrrp?
+jst_vrrp="yes"
+
+# Do we support unfiltered promiscuous mode
+# jst_ufpromisc="yes"
+
+# Do we use vnd devices
+# jst_createvnd="yes"
+
+# Do we not need to have various SMF and ccs stuff created for us?
+# jst_simplefs="yes"
+
+# Should we show snapshots?
+jst_showsnap="yes"
+
+# Where does the mdata socket live?
+jst_mdatapath="/.zonecontrol/"
+
+# Ensure docker zfd(7D) log is not put in the wrong place
+if [[ "$_ZONECFG_attr_docker" == true ]]; then
+ typeset -A FORCED_ATTRS
+ FORCED_ATTRS["zlog-name"]=
+fi
+
+. /usr/lib/brand/jcommon/statechange
diff --git a/usr/src/lib/brand/kvm/Makefile b/usr/src/lib/brand/kvm/Makefile
new file mode 100644
index 0000000000..0823a1f94e
--- /dev/null
+++ b/usr/src/lib/brand/kvm/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 2019 Joyent, Inc.
+#
+
+default: all
+
+# Build everything in parallel; use .WAIT for dependencies
+.PARALLEL:
+
+SUBDIRS = zone
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+
+all install clean clobber: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/brand/kvm/zone/Makefile b/usr/src/lib/brand/kvm/zone/Makefile
new file mode 100644
index 0000000000..0fc0a1a375
--- /dev/null
+++ b/usr/src/lib/brand/kvm/zone/Makefile
@@ -0,0 +1,35 @@
+#
+# 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 2019 Joyent, Inc.
+#
+
+BRAND = kvm
+
+include $(SRC)/lib/brand/Makefile.brand
+
+PROGS = kattach kdetach kinstall kuninstall prestate poststate statechange
+XMLDOCS = config.xml platform.xml
+
+CLOBBERFILES = $(ROOTXMLDOCS) $(ROOTPROGS)
+
+# These shouldn't be necessary, but for some reason the same thing in
+# ../../Makefile.brand is not being picked up.
+$(ROOTPROGS) := FILEMODE = 755
+$(ROOTXMLDOCS) := FILEMODE = 444
+
+all: $(PROGS)
+
+install: $(PROGS) $(ROOTPROGS) $(ROOTXMLDOCS)
+
+clean:
+ $(RM) $(CLEANFILES)
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/lib/brand/kvm/zone/config.xml b/usr/src/lib/brand/kvm/zone/config.xml
new file mode 100644
index 0000000000..461805ae1f
--- /dev/null
+++ b/usr/src/lib/brand/kvm/zone/config.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright 2010,2011 Joyent, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE brand PUBLIC "-//Joyent Inc//DTD Brands//EN"
+ "file:///usr/share/lib/xml/dtd/brand.dtd.1">
+
+<brand name="kvm">
+ <modname></modname>
+
+ <initname>/startvm</initname>
+ <restartinit>false</restartinit>
+ <login_cmd>/usr/bin/login -z %Z %u</login_cmd>
+ <forcedlogin_cmd>/usr/bin/login -z %Z -f %u</forcedlogin_cmd>
+ <user_cmd>/usr/bin/getent passwd %u</user_cmd>
+
+ <install>/usr/lib/brand/kvm/kinstall -z %z -R %R</install>
+ <installopts>R:t:U:q:z:</installopts>
+ <boot></boot>
+ <halt></halt>
+ <verify_cfg></verify_cfg>
+ <verify_adm></verify_adm>
+ <postclone></postclone>
+ <postinstall></postinstall>
+ <attach>/usr/lib/brand/kvm/kattach -z %z -R %R</attach>
+ <detach>/usr/lib/brand/kvm/kdetach -z %z -R %R</detach>
+ <clone></clone>
+ <uninstall>/usr/lib/brand/kvm/kuninstall -z %z -R %R</uninstall>
+ <prestatechange>/usr/lib/brand/kvm/prestate %z %R</prestatechange>
+ <poststatechange>/usr/lib/brand/kvm/poststate %z %R</poststatechange>
+
+ <privilege set="default" name="net_rawaccess" ip-type="exclusive" />
+ <privilege set="default" name="proc_clock_highres" />
+ <privilege set="default" name="proc_lock_memory" />
+ <privilege set="default" name="sys_admin" />
+ <privilege set="default" name="sys_mount" />
+
+ <privilege set="prohibited" name="dtrace_kernel" />
+ <privilege set="prohibited" name="proc_zone" />
+ <privilege set="prohibited" name="sys_config" />
+ <privilege set="prohibited" name="sys_devices" />
+ <privilege set="prohibited" name="sys_ip_config" ip-type="shared" />
+ <privilege set="prohibited" name="sys_linkdir" />
+ <privilege set="prohibited" name="sys_net_config" />
+ <privilege set="prohibited" name="sys_res_config" />
+ <privilege set="prohibited" name="sys_suser_compat" />
+ <privilege set="prohibited" name="xvm_control" />
+ <privilege set="prohibited" name="virt_manage" />
+ <privilege set="prohibited" name="sys_ppp_config" ip-type="shared" />
+
+ <privilege set="required" name="proc_exec" />
+ <privilege set="required" name="sys_mount" />
+</brand>
diff --git a/usr/src/lib/brand/kvm/zone/kattach b/usr/src/lib/brand/kvm/zone/kattach
new file mode 100755
index 0000000000..17d1242c83
--- /dev/null
+++ b/usr/src/lib/brand/kvm/zone/kattach
@@ -0,0 +1,26 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+. /usr/lib/brand/jcommon/libhooks.ksh
+
+function jcommon_attach_hook
+{
+ jattach_kvm_final_setup
+}
+
+ps_brand="kvm"
+. /usr/lib/brand/jcommon/cattach
diff --git a/usr/src/lib/brand/kvm/zone/kdetach b/usr/src/lib/brand/kvm/zone/kdetach
new file mode 100755
index 0000000000..f3785b45e2
--- /dev/null
+++ b/usr/src/lib/brand/kvm/zone/kdetach
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="kvm"
+. /usr/lib/brand/jcommon/cdetach
diff --git a/usr/src/lib/brand/kvm/zone/kinstall b/usr/src/lib/brand/kvm/zone/kinstall
new file mode 100755
index 0000000000..b070f3a176
--- /dev/null
+++ b/usr/src/lib/brand/kvm/zone/kinstall
@@ -0,0 +1,31 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+# Does this brand support reprovisioning?
+# jst_reprovision="yes"
+
+# Is a template image optional?
+jst_tmplopt="yes"
+
+. /usr/lib/brand/jcommon/libhooks.ksh
+
+function jcommon_attach_hook
+{
+ jattach_kvm_final_setup
+}
+
+. /usr/lib/brand/jcommon/cinstall
diff --git a/usr/src/lib/brand/kvm/zone/kuninstall b/usr/src/lib/brand/kvm/zone/kuninstall
new file mode 100755
index 0000000000..136044ad9e
--- /dev/null
+++ b/usr/src/lib/brand/kvm/zone/kuninstall
@@ -0,0 +1,23 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+function jcommon_uninstall_hook
+{
+ :
+}
+
+. /usr/lib/brand/jcommon/cuninstall
diff --git a/usr/src/lib/brand/kvm/zone/platform.xml b/usr/src/lib/brand/kvm/zone/platform.xml
new file mode 100644
index 0000000000..825344b1e8
--- /dev/null
+++ b/usr/src/lib/brand/kvm/zone/platform.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0"?>
+
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright 2010, 2011 Joyent, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE platform PUBLIC "-//Joyent Inc//Zones Platform//EN"
+ "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1">
+
+<platform name="kvm" allow-exclusive-ip="true">
+
+ <!-- Global filesystems to mount when booting the zone -->
+ <global_mount special="/dev" directory="/dev" type="dev"
+ opt="attrdir=%R/root/dev"/>
+
+ <global_mount special="/lib" directory="/lib"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="/sbin" directory="/sbin"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="/usr" directory="/usr"
+ opt="ro,nodevices" type="lofs" />
+ <global_mount special="/smartdc" directory="/smartdc"
+ opt="ro,nodevices" type="lofs" />
+
+ <!-- Local filesystems to mount when booting the zone -->
+ <mount special="swap" directory="/etc/svc/volatile" type="tmpfs" />
+
+ <!-- Devices to create under /dev -->
+ <device match="arp" />
+ <device match="bpf" />
+ <device match="conslog" />
+ <device match="cpu/self/cpuid" />
+ <device match="crypto" />
+ <device match="cryptoadm" />
+ <device match="dsk" />
+ <device match="dtrace/*" />
+ <device match="dtrace/provider/*" />
+ <device match="fd" />
+ <device match="ipnet" />
+ <device match="kstat" />
+ <device match="kvm" />
+ <device match="lo0" />
+ <device match="lofictl" />
+ <device match="lofi" />
+ <device match="log" />
+ <device match="logindmux" />
+ <device match="nsmb" />
+ <device match="net/*" />
+ <device match="null" />
+ <device match="openprom" arch="sparc" />
+ <device match="poll" />
+ <device match="pool" />
+ <device match="ptmx" />
+ <device match="pts/*" />
+ <device match="random" />
+ <device match="rdsk" />
+ <device match="rlofi" />
+ <device match="rmt" />
+ <device match="sad/user" />
+ <device match="svvslo0" />
+ <device match="svvslo1" />
+ <device match="svvslo2" />
+ <device match="svvslo3" />
+ <device match="swap" />
+ <device match="sysevent" />
+ <device match="tcp" />
+ <device match="tcp6" />
+ <device match="term" />
+ <device match="ticlts" />
+ <device match="ticots" />
+ <device match="ticotsord" />
+ <device match="tty" />
+ <device match="udp" />
+ <device match="udp6" />
+ <device match="urandom" />
+ <device match="vnd/*" />
+ <device match="zero" />
+ <device match="zfs" />
+
+ <!-- Devices to create in exclusive IP zone only -->
+ <device match="dld" ip-type="exclusive" />
+ <device match="icmp" ip-type="exclusive" />
+ <device match="icmp6" ip-type="exclusive" />
+ <device match="ip" ip-type="exclusive" />
+ <device match="ip6" ip-type="exclusive" />
+ <device match="ipauth" ip-type="exclusive" />
+ <device match="ipf" ip-type="exclusive" />
+ <device match="ipl" ip-type="exclusive" />
+ <device match="iplookup" ip-type="exclusive" />
+ <device match="ipmpstub" ip-type="exclusive" />
+ <device match="ipnat" ip-type="exclusive" />
+ <device match="ipscan" ip-type="exclusive" />
+ <device match="ipsecah" ip-type="exclusive" />
+ <device match="ipsecesp" ip-type="exclusive" />
+ <device match="ipstate" ip-type="exclusive" />
+ <device match="ipsync" ip-type="exclusive" />
+ <device match="keysock" ip-type="exclusive" />
+ <device match="rawip" ip-type="exclusive" />
+ <device match="rawip6" ip-type="exclusive" />
+ <device match="rts" ip-type="exclusive" />
+ <device match="sad/admin" ip-type="exclusive" />
+ <device match="sctp" ip-type="exclusive" />
+ <device match="sctp6" ip-type="exclusive" />
+ <device match="spdsock" ip-type="exclusive" />
+ <device match="sppp" ip-type="exclusive" />
+ <device match="sppptun" ip-type="exclusive" />
+ <device match="vni" ip-type="exclusive" />
+
+ <!-- Renamed devices to create under /dev -->
+ <device match="zcons/%z/zoneconsole" name="zconsole" />
+
+ <!-- Symlinks to create under /dev -->
+ <symlink source="console" target="zconsole" />
+ <symlink source="dtremote" target="/dev/null" />
+ <symlink source="msglog" target="zconsole" />
+ <symlink source="stderr" target="./fd/2" />
+ <symlink source="stdin" target="./fd/0" />
+ <symlink source="stdout" target="./fd/1" />
+ <symlink source="syscon" target="zconsole" />
+ <symlink source="sysmsg" target="zconsole" />
+ <symlink source="systty" target="zconsole" />
+
+</platform>
diff --git a/usr/src/lib/brand/kvm/zone/poststate b/usr/src/lib/brand/kvm/zone/poststate
new file mode 100755
index 0000000000..17060a3b68
--- /dev/null
+++ b/usr/src/lib/brand/kvm/zone/poststate
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="kvm"
+. /usr/lib/brand/jcommon/poststate
diff --git a/usr/src/lib/brand/kvm/zone/prestate b/usr/src/lib/brand/kvm/zone/prestate
new file mode 100755
index 0000000000..a05fe69368
--- /dev/null
+++ b/usr/src/lib/brand/kvm/zone/prestate
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="kvm"
+. /usr/lib/brand/jcommon/prestate
diff --git a/usr/src/lib/brand/kvm/zone/statechange b/usr/src/lib/brand/kvm/zone/statechange
new file mode 100755
index 0000000000..4e3fab34ca
--- /dev/null
+++ b/usr/src/lib/brand/kvm/zone/statechange
@@ -0,0 +1,36 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+# Do we support vrrp?
+# jst_vrrp="yes"
+
+# Do we support unfiltered promiscuous mode
+jst_ufpromisc="yes"
+
+# Do we use vnd devices
+jst_createvnd="yes"
+
+# Do we not need to have various SMF and ccs stuff created for us?
+jst_simplefs="yes"
+
+# Should we show snapshots?
+# jst_showsnap="yes"
+
+# Where does the mdata socket live?
+jst_mdatapath="/var/run/smartdc"
+
+. /usr/lib/brand/jcommon/statechange
diff --git a/usr/src/lib/brand/lx/Makefile b/usr/src/lib/brand/lx/Makefile
new file mode 100644
index 0000000000..bdf0c9f441
--- /dev/null
+++ b/usr/src/lib/brand/lx/Makefile
@@ -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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# Copyright 2017 Joyent, Inc.
+#
+
+default: all
+
+include Makefile.lx
+
+# Build everything in parallel; use .WAIT for dependencies
+.PARALLEL:
+
+SUBDIRS= librtld_db lx_support lx_init lx_lockd lx_brand netfiles \
+ zone lx_vdso testing .WAIT
+MSGSUBDIRS= lx_brand lx_support zone
+
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint: $(SUBDIRS)
+
+_msg: $(MSGSUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/brand/lx/Makefile.lx b/usr/src/lib/brand/lx/Makefile.lx
new file mode 100644
index 0000000000..4db4679cef
--- /dev/null
+++ b/usr/src/lib/brand/lx/Makefile.lx
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# lib/brand/lx/Makefile.lx
+#
+# include global definitions
+
+BRAND= lx
+
+include $(SRC)/lib/brand/Makefile.brand
+
diff --git a/usr/src/cmd/ssh/Makefile b/usr/src/lib/brand/lx/librtld_db/Makefile
index c68aa94238..2fc0a818f6 100644
--- a/usr/src/cmd/ssh/Makefile
+++ b/usr/src/lib/brand/lx/librtld_db/Makefile
@@ -20,39 +20,35 @@
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# ident "%Z%%M% %I% %E% SMI"
+#
-include ../Makefile.cmd
+default: all
-SUBDIRS= \
- etc
+include $(SRC)/lib/Makefile.lib
-CLOBBERFILES += $(MSGFILE) THIRDPARTYLICENSE
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
-.KEEP_STATE:
+LINT_SUBDIRS= $(MACH)
+$(BUILD64)LINT_SUBDIRS += $(MACH64)
all := TARGET= all
clean := TARGET= clean
clobber := TARGET= clobber
install := TARGET= install
+lint := TARGET= lint
-all clean install: $(SUBDIRS)
-
-lint:
-
-clobber: $(SUBDIRS) clobber_local
-clobber_local:
- $(RM) $(CLOBBERFILES)
+.KEEP_STATE:
-all install: THIRDPARTYLICENSE
+all install clean clobber: $(SUBDIRS)
-$(SUBDIRS): FRC
- cd $@; pwd; $(MAKE) $(TARGET)
+lint: $(LINT_SUBDIRS)
-# skip the summary; just include the actual license clauses.
-THIRDPARTYLICENSE: doc/LICENCE
- $(SED) -n '/1)/,$$p' doc/LICENCE > $@
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
FRC:
diff --git a/usr/src/lib/brand/lx/librtld_db/Makefile.com b/usr/src/lib/brand/lx/librtld_db/Makefile.com
new file mode 100644
index 0000000000..0237de1cc3
--- /dev/null
+++ b/usr/src/lib/brand/lx/librtld_db/Makefile.com
@@ -0,0 +1,84 @@
+#
+# 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 2020 Joyent, Inc.
+
+LIBRARY = lx_librtld_db.a
+VERS = .1
+COBJS = lx_librtld_db.o
+OBJECTS = $(COBJS) $(COBJS64)
+
+include $(SRC)/lib/Makefile.lib
+include ../../Makefile.lx
+
+CSRCS = $(COBJS:%o=../common/%c)
+SRCS = $(CSRCS)
+
+SRCDIR = ../common
+UTSBASE = $(SRC)/uts
+
+#
+# ATTENTION:
+# Librtl_db brand plugin libraries should NOT directly invoke any
+# libproc.so interfaces or be linked against libproc. If a librtl_db
+# brand plugin library uses libproc.so interfaces then it may break
+# any other librtld_db consumers (like mdb) that tries to attach
+# to a branded process. The only safe interfaces that the a librtld_db
+# brand plugin library can use to access a target process are the
+# proc_service(3PROC) apis.
+#
+MAPFILES = $(BRAND_SHARED)/librtld_db/common/mapfile-vers
+DYNFLAGS += $(VERSREF)
+LIBS = $(DYNLIB)
+LDLIBS += -lc -lrtld_db
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -D_REENTRANT -I../ -I$(UTSBASE)/common/brand/lx \
+ -I$(SRC)/cmd/sgs/librtld_db/common \
+ -I$(SRC)/cmd/sgs/include \
+ -I$(SRC)/cmd/sgs/include/$(MACH)
+
+# not linted
+SMATCH=off
+
+ROOTLIBDIR = $(ROOT)/usr/lib/brand/lx
+ROOTLIBDIR64 = $(ROOT)/usr/lib/brand/lx/$(MACH64)
+
+#
+# The top level Makefiles define TEXT_DOMAIN. But librtld_db.so.1 isn't
+# internationalized and this library won't be either. The only messages that
+# this library can generate are messages used for debugging the operation of the
+# library itself.
+#
+DTEXTDOM =
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+pics/%64.o: ../common/%.c
+ $(COMPILE.c) -D_ELF64 $(PICFLAGS) -o $@ $<
+ $(POST_PROCESS_O)
+
+include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/brand/lx/librtld_db/amd64/Makefile b/usr/src/lib/brand/lx/librtld_db/amd64/Makefile
new file mode 100644
index 0000000000..bdef5f5adb
--- /dev/null
+++ b/usr/src/lib/brand/lx/librtld_db/amd64/Makefile
@@ -0,0 +1,36 @@
+#
+# 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 2020 Joyent, Inc.
+#
+
+COBJS64 = lx_librtld_db64.o
+
+include ../Makefile.com
+include $(SRC)/lib/Makefile.lib.64
+
+CLOBBERFILES = $(ROOTLIBDIR64)/$(DYNLIB)
+
+install: all $(ROOTLIBS64)
diff --git a/usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c b/usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c
new file mode 100644
index 0000000000..9f729ab1a6
--- /dev/null
+++ b/usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c
@@ -0,0 +1,853 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/link.h>
+#include <libproc.h>
+#include <proc_service.h>
+#include <rtld_db.h>
+#include <synch.h>
+
+#include <sys/lx_brand.h>
+
+/*
+ * Overview of this library derived from the original "BrandZ" PSARC design
+ * document.
+ *
+ * Since Linux binaries are standard ELF objects, Illumos debug tools (i.e. mdb
+ * or ptools) are able to process them in essentially the same way that Illumos
+ * binaries are processed. The main objective is to retrieve symbols and
+ * thereby aid debugging and observability. Unfortunately, most Linux
+ * distributions strip(1) their binaries as a misguided "optimization" so the
+ * majority of the useful debugging information is lost.
+ *
+ * The debug tools use interfaces provided by librtld_db to debug live
+ * processes and core files. librtld_db discovers ELF objects which have been
+ * mapped into the target's address space and reports these back to the tool.
+ * The librtld_db library understands enough of the internals of the Illumos
+ * runtime linker to iterate over the linker's private link maps and process
+ * the objects it finds. librtld_db allows our tools to debug the Illumos
+ * portions of a branded process (e.g. the brand library, libc, etc.) but they
+ * can't understand any Linux objects that are mapped into the address space
+ * because the Illumos linker only has Illumos objects on its link maps.
+ *
+ * In order to give the tools visibility into Linux binaries, a brand helper
+ * framework is implemented in librtld_db. When librtld_db is asked to examine
+ * a branded target process or core file, it uses the AT_SUN_BRANDNAME aux
+ * vector to get our brand name (lx). It then dlopen-s this lx_librtld_db.so
+ * helper library.
+ *
+ * Once loaded, this helper library is responsible for finding any lx-specific
+ * information it needs, such as the Linux equivalent LDDATA aux entry and
+ * preparing to return details about the objects loaded into the address space
+ * by the Linux linker.
+ *
+ * When a debug tool asks to know what objects are loaded in the target,
+ * librtld_db walks the Illumos link maps and iterates over each object it
+ * finds there, handing information about each to the tool. It then calls down
+ * into this helper library, which does the same for the brand-specific objects
+ * used by the target.
+ *
+ * This debug-helper code contains a bunch of helpful ps_plog calls. To enable
+ * this output with the ptools (e.g. pmap) set LIBPROC_DEBUG=1 in your
+ * environment. To enable it with mdb set MDB_DEBUG=psvc in your environment.
+ */
+
+/*
+ * ATTENTION:
+ * Librtl_db brand plugin libraries should NOT directly invoke any
+ * libproc.so interfaces or be linked against libproc. If a librtl_db
+ * brand plugin library uses libproc.so interfaces then it may break
+ * any other librtld_db consumers (like mdb) that tries to attach
+ * to a branded process. The only safe interfaces that the a librtld_db
+ * brand plugin library can use to access a target process are the
+ * proc_service(3PROC) apis.
+ */
+
+/*
+ * M_DATA comes from some streams header file but is also redifined in
+ * _rtld_db.h, so nuke the old streams definition here.
+ */
+#ifdef M_DATA
+#undef M_DATA
+#endif /* M_DATA */
+
+/*
+ * For 32-bit versions of this library, this file gets compiled once.
+ * For 64-bit versions of this library, this file gets compiled twice,
+ * once with _ELF64 defined and once without. The expectation is that
+ * the 64-bit version of the library can properly deal with both 32-bit
+ * and 64-bit elf files, hence in the 64-bit library there are two copies
+ * of all the interfaces in this file, one set named *32 and one named *64.
+ *
+ * This also means that we need to be careful when declaring local pointers
+ * that point to objects in another processes address space, since these
+ * pointers may not match the current processes pointer width. Basically,
+ * we should avoid using data types that change size between 32 and 64 bit
+ * modes like: long, void *, uintptr_t, caddr_t, psaddr_t, size_t, etc.
+ * Instead we should declare all pointers as uint32_t. Then when we
+ * are compiled to deal with 64-bit targets we'll re-define uint32_t
+ * to be a uint64_t.
+ */
+#ifdef _LP64
+#ifdef _ELF64
+#define lx_ldb_get_dyns32 lx_ldb_get_dyns64
+#define lx_ldb_init32 lx_ldb_init64
+#define lx_ldb_fini32 lx_ldb_fini64
+#define lx_ldb_loadobj_iter32 lx_ldb_loadobj_iter64
+#define lx_ldb_getauxval32 lx_ldb_getauxval64
+#define lx_elf_props32 lx_elf_props64
+#define _rd_get_dyns32 _rd_get_dyns64
+#define _rd_get_ehdr32 _rd_get_ehdr64
+#define uint32_t uint64_t
+#define Elf32_Dyn Elf64_Dyn
+#define Elf32_Ehdr Elf64_Ehdr
+#define Elf32_Phdr Elf64_Phdr
+#define Elf32_Sym Elf64_Sym
+#endif /* _ELF64 */
+#endif /* _LP64 */
+
+/* Included from usr/src/cmd/sgs/librtld_db/common */
+#include <_rtld_db.h>
+
+typedef struct lx_rd {
+ rd_agent_t *lr_rap;
+ struct ps_prochandle *lr_php; /* proc handle pointer */
+ uint32_t lr_rdebug; /* address of lx r_debug */
+ uint32_t lr_exec; /* base address of executable */
+} lx_rd_t;
+
+typedef struct lx_link_map {
+ uint32_t lxm_addr; /* Base address shared object is loaded at. */
+ uint32_t lxm_name; /* Absolute file name object was found in. */
+ uint32_t lxm_ld; /* Dynamic section of the shared object. */
+ uint32_t lxm_next; /* Chain of loaded objects. */
+} lx_link_map_t;
+
+typedef struct lx_r_debug {
+ int r_version; /* Version number for this protocol. */
+ uint32_t r_map; /* Head of the chain of loaded objects. */
+
+ /*
+ * This is the address of a function internal to the run-time linker,
+ * that will always be called when the linker begins to map in a
+ * library or unmap it, and again when the mapping change is complete.
+ * The debugger can set a breakpoint at this address if it wants to
+ * notice shared object mapping changes.
+ */
+ uint32_t r_brk;
+ r_state_e r_state; /* defined the same way between lx/solaris */
+ uint32_t r_ldbase; /* Base address the linker is loaded at. */
+} lx_r_debug_t;
+
+static uint32_t
+lx_ldb_getauxval32(struct ps_prochandle *php, int type)
+{
+ const auxv_t *auxvp = NULL;
+
+ if (ps_pauxv(php, &auxvp) != PS_OK)
+ return ((uint32_t)-1);
+
+ while (auxvp->a_type != AT_NULL) {
+ if (auxvp->a_type == type)
+ return ((uint32_t)(uintptr_t)auxvp->a_un.a_ptr);
+ auxvp++;
+ }
+ return ((uint32_t)-1);
+}
+
+/*
+ * A key difference between the linux linker and ours' is that the linux
+ * linker adds the base address of segments to certain values in the
+ * segments' ELF header. As an example, look at the address of the
+ * DT_HASH hash table in a Solaris section - it is a relative address
+ * which locates the start of the hash table, relative to the beginning
+ * of the ELF file. However, when the linux linker loads a section, it
+ * modifies the in-memory ELF image by changing address of the hash
+ * table to be an absolute address. This is only done for libraries - not for
+ * executables.
+ *
+ * Solaris tools expect the relative address to remain relative, so
+ * here we will modify the in-memory ELF image so that it once again
+ * contains relative addresses.
+ *
+ * To accomplish this, we walk through all sections in the target.
+ * Linux sections are identified by pointing to the linux linker or libc in the
+ * DT_NEEDED section. For all matching sections, we subtract the segment
+ * base address to get back to relative addresses.
+ */
+static rd_err_e
+lx_ldb_get_dyns32(rd_helper_data_t rhd,
+ psaddr_t addr, void **dynpp, size_t *dynpp_sz)
+{
+ lx_rd_t *lx_rd = (lx_rd_t *)rhd;
+ rd_agent_t *rap = lx_rd->lr_rap;
+ Elf32_Ehdr ehdr;
+ Elf32_Dyn *dynp = NULL;
+ size_t dynp_sz;
+ uint_t ndyns;
+ int i;
+
+ ps_plog("lx_ldb_get_dyns: invoked for object at 0x%p", addr);
+
+ /* Read in a copy of the ehdr */
+ if (_rd_get_ehdr32(rap, addr, &ehdr, NULL) != RD_OK) {
+ ps_plog("lx_ldb_get_dyns: _rd_get_ehdr() failed");
+ return (RD_ERR);
+ }
+
+ /* read out the PT_DYNAMIC elements for this object */
+ if (_rd_get_dyns32(rap, addr, &dynp, &dynp_sz) != RD_OK) {
+ ps_plog("lx_ldb_get_dyns: _rd_get_dyns() failed");
+ return (RD_ERR);
+ }
+
+ /*
+ * From here on out if we encounter an error we'll just return
+ * success and pass back the unmolested dynamic elements that
+ * we've already obtained.
+ */
+ if (dynpp != NULL)
+ *dynpp = dynp;
+ if (dynpp_sz != NULL)
+ *dynpp_sz = dynp_sz;
+ ndyns = dynp_sz / sizeof (Elf32_Dyn);
+
+ /* If this isn't a dynamic object, there's nothing left todo */
+ if (ehdr.e_type != ET_DYN) {
+ ps_plog("lx_ldb_get_dyns: done: not a shared object");
+ return (RD_OK);
+ }
+
+ /*
+ * Before we blindly start changing dynamic section addresses
+ * we need to figure out if the current object that we're looking
+ * at is a linux object or a solaris object. To do this first
+ * we need to find the string tab dynamic section element.
+ */
+ for (i = 0; i < ndyns; i++) {
+ if (dynp[i].d_tag == DT_STRTAB)
+ break;
+ }
+ if (i == ndyns) {
+ ps_plog("lx_ldb_get_dyns: "
+ "failed to find string tab in the dynamic section");
+ return (RD_OK);
+ }
+
+ /*
+ * Check if the strtab value looks like an offset or an address.
+ * It's an offset if the value is less then the base address that
+ * the object is loaded at, or if the value is less than the offset
+ * of the section headers in the same elf object. This check isn't
+ * perfect, but in practice it's good enough.
+ */
+ if ((dynp[i].d_un.d_ptr < addr) ||
+ (dynp[i].d_un.d_ptr < ehdr.e_shoff)) {
+ ps_plog("lx_ldb_get_dyns: "
+ "doesn't appear to be an lx object");
+ return (RD_OK);
+ }
+
+ /*
+ * This seems to be a a linux object, so we'll patch up the dynamic
+ * section addresses
+ */
+ ps_plog("lx_ldb_get_dyns: "
+ "patching up lx object dynamic section addresses");
+ for (i = 0; i < ndyns; i++) {
+ switch (dynp[i].d_tag) {
+ case DT_PLTGOT:
+ case DT_HASH:
+ case DT_STRTAB:
+ case DT_SYMTAB:
+ case DT_RELA:
+ case DT_REL:
+ case DT_DEBUG:
+ case DT_JMPREL:
+ case DT_VERSYM:
+ if (dynp[i].d_un.d_val > addr) {
+ dynp[i].d_un.d_ptr -= addr;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return (RD_OK);
+}
+
+static void
+lx_ldb_fini32(rd_helper_data_t rhd)
+{
+ lx_rd_t *lx_rd = (lx_rd_t *)rhd;
+ ps_plog("lx_ldb_fini: cleaning up lx helper");
+ free(lx_rd);
+}
+
+/*
+ * The linux linker has an r_debug structure somewhere in its data section that
+ * contains the address of the head of the link map list. To find this, we will
+ * use the DT_DEBUG token in the executable's dynamic section. The linux linker
+ * wrote the address of its r_debug structure to the DT_DEBUG dynamic entry. We
+ * get the address of the executable's program headers from the
+ * AT_SUN_BRAND_LX_PHDR aux vector entry. From there, we calculate the
+ * address of the Elf header, and from there we can easily get to the DT_DEBUG
+ * entry.
+ */
+static rd_helper_data_t
+lx_ldb_init32(rd_agent_t *rap, struct ps_prochandle *php)
+{
+ lx_rd_t *lx_rd;
+ uint32_t addr, phdr_addr, dyn_addr;
+ uint32_t symtab, strtab, offs;
+ uint32_t vaddr, memsz;
+ caddr_t mem;
+ Elf32_Dyn *dyn;
+ Elf32_Phdr phdr, *ph, *dph, *phdrs;
+ Elf32_Ehdr ehdr;
+ Elf32_Sym *sym;
+ int i, dyn_count;
+
+ lx_rd = calloc(sizeof (lx_rd_t), 1);
+ if (lx_rd == NULL) {
+ ps_plog("lx_ldb_init: cannot allocate memory");
+ return (NULL);
+ }
+ lx_rd->lr_rap = rap;
+ lx_rd->lr_php = php;
+
+ phdr_addr = lx_ldb_getauxval32(php, AT_SUN_BRAND_LX_PHDR);
+ if (phdr_addr == (uint32_t)-1) {
+ ps_plog("lx_ldb_init: no LX_PHDR found in aux vector");
+ return (NULL);
+ }
+ ps_plog("lx_ldb_init: found LX_PHDR auxv phdr at: 0x%p",
+ phdr_addr);
+
+ if (ps_pread(php, phdr_addr, &phdr, sizeof (phdr)) != PS_OK) {
+ ps_plog("lx_ldb_init: couldn't read phdr at 0x%p",
+ phdr_addr);
+ free(lx_rd);
+ return (NULL);
+ }
+
+ /* The ELF header should be before the program header in memory */
+ lx_rd->lr_exec = addr = phdr_addr - phdr.p_offset;
+ if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
+ ps_plog("lx_ldb_init: couldn't read ehdr at 0x%p",
+ lx_rd->lr_exec);
+ free(lx_rd);
+ return (NULL);
+ }
+ ps_plog("lx_ldb_init: read ehdr at: 0x%p", addr);
+ ps_plog("lx_ldb_init: ehdr t %d ent 0x%p poff 0x%p psize %d pnum %d",
+ ehdr.e_type, ehdr.e_entry, ehdr.e_phoff, ehdr.e_phentsize,
+ ehdr.e_phnum);
+
+ if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL) {
+ ps_plog("lx_ldb_init: couldn't alloc phdrs memory");
+ free(lx_rd);
+ return (NULL);
+ }
+
+ if (ps_pread(php, phdr_addr, phdrs, ehdr.e_phnum * ehdr.e_phentsize) !=
+ PS_OK) {
+ ps_plog("lx_ldb_init: couldn't read phdrs at 0x%p",
+ phdr_addr);
+ free(lx_rd);
+ free(phdrs);
+ return (NULL);
+ }
+
+ /* program headers */
+ ps_plog("lx_ldb_init: read %d phdrs at: 0x%p",
+ ehdr.e_phnum, phdr_addr);
+
+ for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++,
+ /*LINTED */
+ ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
+ ps_plog("lx_ldb_init: ph[%d] 0x%p type %d", i,
+ (phdr_addr + ((char *)ph - (char *)phdrs)), ph->p_type);
+ if (ph->p_type == PT_DYNAMIC)
+ break;
+ }
+ if (i == ehdr.e_phnum) {
+ ps_plog("lx_ldb_init: no PT_DYNAMIC in executable");
+ free(lx_rd);
+ free(phdrs);
+ return (NULL);
+ }
+ ps_plog("lx_ldb_init: found PT_DYNAMIC phdr[%d] at: 0x%p",
+ i, (phdr_addr + ((char *)ph - (char *)phdrs)));
+ ps_plog("lx_ldb_init: ph t 0x%x f 0x%x o 0x%p v 0x%p s %d",
+ ph->p_type, ph->p_flags, ph->p_offset, ph->p_vaddr, ph->p_filesz);
+
+ if ((dyn = malloc(ph->p_filesz)) == NULL) {
+ ps_plog("lx_ldb_init: couldn't alloc for PT_DYNAMIC");
+ free(lx_rd);
+ free(phdrs);
+ return (NULL);
+ }
+
+ /*
+ * Unclear why the dyn_addr works sometimes with one value and
+ * sometimes for the other, so we handle both cases.
+ */
+
+ dyn_count = ph->p_filesz / sizeof (Elf32_Dyn);
+ ps_plog("lx_ldb_init: dyn_count %d %d", dyn_count, sizeof (Elf32_Dyn));
+ dyn_addr = addr + ph->p_vaddr;
+ ps_plog("lx_ldb_init: dyn_addr 0x%p 0x%x = 0x%p",
+ addr, ph->p_offset, dyn_addr);
+ if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) {
+ ps_plog("lx_ldb_init: couldn't read dynamic at 0x%p, "
+ "trying dyn_addr 0x%p",
+ dyn_addr, ph->p_vaddr);
+
+ dyn_addr = ph->p_vaddr;
+ if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) {
+ ps_plog("lx_ldb_init: couldn't read dynamic at 0x%p",
+ dyn_addr);
+
+ free(lx_rd);
+ free(phdrs);
+ free(dyn);
+ return (NULL);
+ }
+ }
+ ps_plog("lx_ldb_init: read %d dynamic headers at: 0x%p",
+ dyn_count, dyn_addr);
+
+ for (i = 0; i < dyn_count; i++) {
+ if (dyn[i].d_tag == DT_DEBUG) {
+ lx_rd->lr_rdebug = dyn[i].d_un.d_ptr;
+ break;
+ }
+ }
+ free(phdrs);
+ free(dyn);
+
+ if (lx_rd->lr_rdebug != 0) {
+ ps_plog("lx_ldb_init: found DT_DEBUG: 0x%p", lx_rd->lr_rdebug);
+ return ((rd_helper_data_t)lx_rd);
+ }
+
+ ps_plog("lx_ldb_init: no DT_DEBUG found in exe; looking for r_debug");
+
+ /*
+ * If we didn't find DT_DEBUG, we're going to employ the same fallback
+ * as gdb: pawing through the dynamic linker's symbol table looking
+ * for the r_debug symbol.
+ */
+ addr = lx_ldb_getauxval32(php, AT_SUN_BRAND_LX_INTERP);
+
+ if (addr == (uint32_t)-1) {
+ ps_plog("lx_ldb_init: no interpreter; failing");
+ free(lx_rd);
+ return (NULL);
+ }
+
+ ps_plog("lx_ldb_init: reading interp ehdr at 0x%p", addr);
+
+ if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
+ ps_plog("lx_ldb_init: couldn't read interp ehdr at 0x%p", addr);
+ free(lx_rd);
+ return (NULL);
+ }
+
+ if (ehdr.e_type != ET_DYN) {
+ ps_plog("lx_ldb_init: interp ehdr not of type ET_DYN");
+ free(lx_rd);
+ return (NULL);
+ }
+
+ phdr_addr = addr + ehdr.e_phoff;
+
+ if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL) {
+ ps_plog("lx_ldb_init: couldn't alloc interp phdrs memory");
+ free(lx_rd);
+ return (NULL);
+ }
+
+ if (ps_pread(php, phdr_addr, phdrs,
+ ehdr.e_phnum * ehdr.e_phentsize) != PS_OK) {
+ ps_plog("lx_ldb_init: couldn't read interp phdrs at 0x%p",
+ phdr_addr);
+ free(lx_rd);
+ free(phdrs);
+ return (NULL);
+ }
+
+ ps_plog("lx_ldb_init: read %d interp phdrs at: 0x%p",
+ ehdr.e_phnum, phdr_addr);
+
+ vaddr = (uint32_t)-1;
+ memsz = 0;
+
+ for (i = 0, ph = phdrs, dph = NULL; i < ehdr.e_phnum; i++,
+ /*LINTED */
+ ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
+ /*
+ * Keep track of our lowest PT_LOAD address, as this segment
+ * contains the DT_SYMTAB and DT_STRTAB.
+ */
+ if (ph->p_type == PT_LOAD && ph->p_vaddr < vaddr) {
+ vaddr = ph->p_vaddr;
+ memsz = ph->p_memsz;
+ }
+
+ if (ph->p_type == PT_DYNAMIC)
+ dph = ph;
+ }
+
+ if (vaddr == (uint32_t)-1 || memsz == 0) {
+ ps_plog("lx_ldb_init: no PT_LOAD section in interp");
+ free(lx_rd);
+ free(phdrs);
+ return (NULL);
+ }
+
+ ps_plog("lx_ldb_init: found interp PT_LOAD to be %d bytes at 0x%p",
+ memsz, vaddr);
+
+ if ((ph = dph) == NULL) {
+ ps_plog("lx_ldb_init: no PT_DYNAMIC in interp");
+ free(lx_rd);
+ free(phdrs);
+ return (NULL);
+ }
+
+ ps_plog("lx_ldb_init: found interp PT_DYNAMIC phdr[%d] at: 0x%p",
+ i, (phdr_addr + ((char *)ph - (char *)phdrs)));
+
+ if ((dyn = malloc(ph->p_filesz)) == NULL) {
+ ps_plog("lx_ldb_init: couldn't alloc for interp PT_DYNAMIC");
+ free(lx_rd);
+ free(phdrs);
+ return (NULL);
+ }
+
+ dyn_addr = addr + ph->p_offset;
+ dyn_count = ph->p_filesz / sizeof (Elf32_Dyn);
+
+ if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) {
+ ps_plog("lx_ldb_init: couldn't read interp dynamic at 0x%p",
+ dyn_addr);
+ free(lx_rd);
+ free(phdrs);
+ free(dyn);
+ return (NULL);
+ }
+
+ free(phdrs);
+
+ ps_plog("lx_ldb_init: read %d interp dynamic headers at: 0x%p",
+ dyn_count, dyn_addr);
+
+ /*
+ * As noted in lx_ldb_get_dyns32(), in Linux, the PT_DYNAMIC table
+ * is adjusted to represent absolute addresses instead of offsets.
+ * This is not true for the interpreter, however -- where the values
+ * will be represented as offsets from the lowest PT_LOAD p_vaddr.
+ */
+ symtab = strtab = (uint32_t)-1;
+
+ for (i = 0; i < dyn_count; i++) {
+ if (dyn[i].d_tag == DT_STRTAB)
+ strtab = dyn[i].d_un.d_ptr - vaddr;
+
+ if (dyn[i].d_tag == DT_SYMTAB)
+ symtab = dyn[i].d_un.d_ptr - vaddr;
+ }
+
+ free(dyn);
+
+ if (strtab == (uint32_t)-1 || strtab > memsz) {
+ ps_plog("lx_ldb_init: didn't find valid interp strtab");
+ free(lx_rd);
+ return (NULL);
+ }
+
+ if (symtab == (uint32_t)-1 || symtab > memsz) {
+ ps_plog("lx_ldb_init: didn't find valid interp symtab");
+ free(lx_rd);
+ return (NULL);
+ }
+
+ ps_plog("lx_ldb_init: strtab is 0x%p, symtab is 0x%p",
+ addr + strtab, addr + symtab);
+
+ if ((mem = malloc(memsz)) == NULL) {
+ ps_plog("lx_ldb_init: couldn't allocate interp "
+ "buffer of 0x%p bytes", memsz);
+ free(lx_rd);
+ return (NULL);
+ }
+
+ if (ps_pread(php, addr, mem, memsz) != PS_OK) {
+ ps_plog("lx_ldb_init: couldn't read interp at 0x%p", addr);
+ free(lx_rd);
+ free(mem);
+ return (NULL);
+ }
+
+ /*
+ * We make an assumption that is made elsewhere in the Linux linker:
+ * that the DT_SYMTAB immediately precedes the DT_STRTAB.
+ */
+ for (offs = symtab; offs < strtab; offs += sizeof (Elf32_Sym)) {
+ uint32_t p;
+
+ sym = (Elf32_Sym *)&mem[offs];
+
+ if (sym->st_name > memsz) {
+ ps_plog("lx_ldb_init: invalid st_name at sym 0x%p",
+ addr + offs);
+ continue;
+ }
+
+ p = sym->st_name;
+
+ if (strtab + p > memsz) {
+ ps_plog("lx_ldb_init: invalid symbol address 0x%p, "
+ "memsz 0x%p", strtab + p, memsz);
+ continue;
+ }
+
+ if (mem[strtab + p] == '\0')
+ continue;
+
+ /* Sometimes we're pointing into the middle of a symbol? */
+ while ((strtab + p) > 0 && (strtab + p) < memsz &&
+ mem[strtab + p] != '\0')
+ p--;
+ p++;
+
+ ps_plog("lx_ldb_init: interp symbol (0x%p) %s",
+ strtab + p, &mem[strtab + p]);
+
+ if (strcmp(&mem[strtab + p], "_r_debug") == 0)
+ break;
+ }
+
+ if (offs >= strtab) {
+ ps_plog("lx_ldb_init: no _r_debug found in interpreter");
+ free(mem);
+ free(lx_rd);
+ return (NULL);
+ }
+
+ lx_rd->lr_rdebug = (sym->st_value - vaddr) + addr;
+ ps_plog("lx_ldb_init: found _r_debug at 0x%p", lx_rd->lr_rdebug);
+ free(mem);
+
+ return ((rd_helper_data_t)lx_rd);
+}
+
+/*
+ * Given the address of an ELF object in the target, return its size and
+ * the proper link map ID.
+ */
+static size_t
+lx_elf_props32(struct ps_prochandle *php, uint32_t addr, psaddr_t *data_addr)
+{
+ Elf32_Ehdr ehdr;
+ Elf32_Phdr *phdrs, *ph;
+ int i;
+ uint32_t min = (uint32_t)-1;
+ uint32_t max = 0;
+ size_t sz = 0;
+
+ if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
+ ps_plog("lx_elf_props: Couldn't read ELF header at 0x%p",
+ addr);
+ return (0);
+ }
+
+ if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL)
+ return (0);
+
+ if (ps_pread(php, addr + ehdr.e_phoff, phdrs, ehdr.e_phnum *
+ ehdr.e_phentsize) != PS_OK) {
+ ps_plog("lx_elf_props: Couldn't read program headers at 0x%p",
+ addr + ehdr.e_phoff);
+ return (0);
+ }
+
+ for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++,
+ /*LINTED */
+ ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
+
+ if (ph->p_type != PT_LOAD)
+ continue;
+
+ if ((ph->p_flags & (PF_W | PF_R)) == (PF_W | PF_R)) {
+ *data_addr = ph->p_vaddr;
+ if (ehdr.e_type == ET_DYN)
+ *data_addr += addr;
+ if (*data_addr & (ph->p_align - 1))
+ *data_addr = *data_addr & (~(ph->p_align -1));
+ }
+
+ if (ph->p_vaddr < min)
+ min = ph->p_vaddr;
+
+ if (ph->p_vaddr > max) {
+ max = ph->p_vaddr;
+ sz = ph->p_memsz + max - min;
+ if (sz & (ph->p_align - 1))
+ sz = (sz & (~(ph->p_align - 1))) + ph->p_align;
+ }
+ }
+
+ free(phdrs);
+ return (sz);
+}
+
+static int
+lx_ldb_loadobj_iter32(rd_helper_data_t rhd, rl_iter_f *cb, void *client_data)
+{
+ lx_rd_t *lx_rd = (lx_rd_t *)rhd;
+ struct ps_prochandle *php = lx_rd->lr_php;
+ lx_r_debug_t r_debug;
+ lx_link_map_t map;
+ uint32_t p = (uint32_t)(uintptr_t)NULL;
+ int rc;
+ rd_loadobj_t exec;
+
+ if ((rc = ps_pread(php, (psaddr_t)lx_rd->lr_rdebug, &r_debug,
+ sizeof (r_debug))) != PS_OK) {
+ ps_plog("lx_ldb_loadobj_iter: "
+ "Couldn't read linux r_debug at 0x%p", lx_rd->lr_rdebug);
+ return (rc);
+ }
+
+ p = r_debug.r_map;
+
+ /*
+ * The first item on the link map list is for the executable, but it
+ * doesn't give us any useful information about it. We need to
+ * synthesize a rd_loadobj_t for the client.
+ *
+ * Linux doesn't give us the executable name, so we'll get it from
+ * the AT_EXECNAME entry instead.
+ */
+ if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) != PS_OK) {
+ ps_plog("lx_ldb_loadobj_iter: "
+ "Couldn't read linux link map at 0x%p", p);
+ return (rc);
+ }
+
+ bzero(&exec, sizeof (exec));
+ exec.rl_base = lx_rd->lr_exec;
+ exec.rl_dynamic = map.lxm_ld;
+ exec.rl_nameaddr = lx_ldb_getauxval32(php, AT_SUN_EXECNAME);
+ exec.rl_lmident = LM_ID_BASE;
+
+ exec.rl_bend = exec.rl_base +
+ lx_elf_props32(php, lx_rd->lr_exec, &exec.rl_data_base);
+
+ if ((*cb)(&exec, client_data) == 0) {
+ ps_plog("lx_ldb_loadobj_iter: "
+ "client callb failed for executable");
+ return (PS_ERR);
+ }
+ ps_plog("lx_ldb_loadobj_iter: exec base 0x%p dyn 0x%p",
+ exec.rl_base, exec.rl_dynamic);
+
+ for (p = map.lxm_next; p != (uint32_t)(uintptr_t)NULL;
+ p = map.lxm_next) {
+ rd_loadobj_t obj;
+
+ if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) !=
+ PS_OK) {
+ ps_plog("lx_ldb_loadobj_iter: "
+ "Couldn't read lk map at %p", p);
+ return (rc);
+ }
+
+ /*
+ * The linux link map has less information than the Solaris one.
+ * We need to go fetch the missing information from the ELF
+ * headers.
+ */
+
+ obj.rl_nameaddr = (psaddr_t)map.lxm_name;
+ obj.rl_base = map.lxm_addr;
+ obj.rl_refnameaddr = (psaddr_t)map.lxm_name;
+ obj.rl_plt_base = (psaddr_t)NULL;
+ obj.rl_plt_size = 0;
+ obj.rl_lmident = LM_ID_BASE;
+
+ ps_plog("lx_ldb_loadobj_iter: map base 0x%p 0x%p",
+ obj.rl_base, obj.rl_nameaddr);
+
+ /*
+ * Ugh - we have to walk the ELF stuff, find the PT_LOAD
+ * sections, and calculate the end of the file's mappings
+ * ourselves.
+ */
+
+ obj.rl_bend = map.lxm_addr +
+ lx_elf_props32(php, map.lxm_addr, &obj.rl_data_base);
+ obj.rl_padstart = obj.rl_base;
+ obj.rl_padend = obj.rl_bend;
+ obj.rl_dynamic = map.lxm_ld;
+ obj.rl_tlsmodid = 0;
+
+ ps_plog("lx_ldb_loadobj_iter: 0x%p to 0x%p",
+ obj.rl_base, obj.rl_bend);
+
+ if ((*cb)(&obj, client_data) == 0) {
+ ps_plog("lx_ldb_loadobj_iter: "
+ "Client callback failed on %s", map.lxm_name);
+ return (rc);
+ }
+ }
+ return (RD_OK);
+}
+
+/*
+ * Librtld_db plugin linkage struct.
+ *
+ * When we get loaded by librtld_db, it will look for the symbol below
+ * to find our plugin entry points.
+ */
+rd_helper_ops_t RTLD_DB_BRAND_OPS = {
+ LM_ID_BRAND,
+ lx_ldb_init32,
+ lx_ldb_fini32,
+ lx_ldb_loadobj_iter32,
+ lx_ldb_get_dyns32
+};
diff --git a/usr/src/lib/brand/lx/librtld_db/i386/Makefile b/usr/src/lib/brand/lx/librtld_db/i386/Makefile
new file mode 100644
index 0000000000..b5f780c072
--- /dev/null
+++ b/usr/src/lib/brand/lx/librtld_db/i386/Makefile
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+CLOBBERFILES = $(ROOTLIBDIR)/$(DYNLIB)
+
+install: all $(ROOTLIBS)
diff --git a/usr/src/lib/brand/lx/lx_brand/Makefile b/usr/src/lib/brand/lx/lx_brand/Makefile
new file mode 100644
index 0000000000..fc217b672c
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/Makefile
@@ -0,0 +1,49 @@
+#
+# 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.
+# Copyright 2014 Joyent, Inc. All rights reserved.
+#
+
+include ../../../Makefile.lib
+
+default: all
+
+SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint _msg: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/brand/lx/lx_brand/Makefile.com b/usr/src/lib/brand/lx/lx_brand/Makefile.com
new file mode 100644
index 0000000000..dd457ab2ae
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/Makefile.com
@@ -0,0 +1,103 @@
+#
+# 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.
+# Copyright 2020 Joyent, Inc.
+#
+
+LX_CMN = $(SRC)/common/brand/lx
+
+LIBRARY = lx_brand.a
+VERS = .1
+COBJS = capabilities.o \
+ clock.o \
+ clone.o \
+ debug.o \
+ dir.o \
+ file.o \
+ fork.o \
+ lx_brand.o \
+ misc.o \
+ module.o \
+ mount.o \
+ mount_nfs.o \
+ ptrace.o \
+ sendfile.o \
+ signal.o \
+ stack.o \
+ statfs.o \
+ sysctl.o \
+ sysv_ipc.o \
+ time.o \
+ truncate.o
+
+CMNOBJS = lx_auxv.o \
+ lx_errno.o \
+ lx_signum.o
+ASOBJS = lx_handler.o lx_crt.o
+OBJECTS = $(CMNOBJS) $(COBJS) $(ASOBJS)
+
+USDT_PROVIDERS = lx_provider.d
+
+include ../../Makefile.lx
+include ../../../../Makefile.lib
+
+CSRCS = $(COBJS:%o=../common/%c) $(CMNOBJS:%o=$(LX_CMN)/%c)
+ASSRCS = $(ASOBJS:%o=$(ISASRCDIR)/%s)
+SRCS = $(CSRCS) $(ASSRCS)
+
+SRCDIR = ../common
+UTSBASE = ../../../../../uts
+
+LIBS = $(DYNLIB)
+LDLIBS += -lmapmalloc -lsocket -lproc -lrtld_db -lrpcsvc -lnsl -lc
+DYNFLAGS += $(DYNFLAGS_$(CLASS))
+DYNFLAGS += $(BLOCAL) $(ZNOVERSION) -Wl,-e_start
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -D_REENTRANT -I. -I../ -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+ASFLAGS = -P $(ASFLAGS_$(CURTYPE)) -D_ASM -I../ \
+ -I$(UTSBASE)/common/brand/lx
+
+ZGUIDANCE = -Wl,-zguidance=nounused
+
+# not linted
+SMATCH=off
+
+C99MODE= -xc99=%all
+C99LMODE= -Xc99=%all
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../../../Makefile.targ
+include ../../../../Makefile.usdt
+
+pics/%.o: $(ISASRCDIR)/%.s
+ $(COMPILE.s) -o $@ $<
+ $(POST_PROCESS_S_O)
+
+pics/%.o: $(LX_CMN)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/lib/brand/lx/lx_brand/amd64/Makefile b/usr/src/lib/brand/lx/lx_brand/amd64/Makefile
new file mode 100644
index 0000000000..a1db90cd38
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/amd64/Makefile
@@ -0,0 +1,42 @@
+#
+# 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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# Copyright 2015 Joyent, Inc.
+#
+#
+# lib/brand/lx/amd64/Makefile
+
+ISASRCDIR=.
+
+ASFLAGS += -P -D_ASM
+
+include ../Makefile.com
+include $(SRC)/lib/Makefile.lib.64
+
+DYNFLAGS += -Wl,-I/native/lib/64/ld.so.1
+CPPFLAGS += -D_SYSCALL32
+
+POFILE= lx_brand.po
+MSGFILES= $(CSRCS)
+
+ASSYMDEP_OBJS = lx_handler.o
+
+install: all $(ROOTLIBS64)
+
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+include $(SRC)/Makefile.msg.targ
diff --git a/usr/src/lib/brand/lx/lx_brand/amd64/lx_crt.s b/usr/src/lib/brand/lx/lx_brand/amd64/lx_crt.s
new file mode 100644
index 0000000000..13a88a3d50
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/amd64/lx_crt.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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
+ */
+
+#include <sys/asm_linkage.h>
+
+#if defined(lint)
+
+void
+_start(void)
+{
+}
+
+#else /* lint */
+
+ /*
+ * C language startup routine for the lx brand shared library.
+ *
+ * That routine expects to be called with the following arguments:
+ * brand_init(int argc, char *argv[], char *envp[])
+ *
+ * There are no arguments explicitly passed to this entry point,
+ * routine, but we do know how our initial stack has been setup by
+ * the kernel. The stack format is documented in:
+ * usr/src/cmd/sgs/rtld/amd64/boot.s
+ *
+ * So this routine will troll through the stack to setup the argument
+ * values for the common brand library startup routine and then invoke
+ * it. This routine is modeled after the default crt1.s`_start()
+ * routines.
+ */
+ ENTRY_NP(_start)
+ pushq $0 / Build a stack frame. retpc = NULL
+ pushq $0 / fp = NULL
+ movq %rsp, %rbp / first stack frame
+
+ /*
+ * Calculate the location of the envp array by adding the size of
+ * the argv array to the start of the argv array.
+ */
+ movq 16(%rbp), %rdi / argc in %rdi (1st param)
+ leaq 24(%rbp), %rsi / &argv[0] in %rsi (2nd param)
+ leaq 32(%rbp,%rdi,8), %rdx / envp in %rdx (3rd param)
+ call lx_init
+
+ /* lx_init will never return. */
+ /*NOTREACHED*/
+ SET_SIZE(_start)
+#endif /* lint */
diff --git a/usr/src/lib/brand/lx/lx_brand/amd64/lx_handler.s b/usr/src/lib/brand/lx/lx_brand/amd64/lx_handler.s
new file mode 100644
index 0000000000..a01d66554d
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/amd64/lx_handler.s
@@ -0,0 +1,53 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/asm_linkage.h>
+#include <sys/regset.h>
+#include <sys/segments.h>
+#include <sys/syscall.h>
+#include <sys/lx_brand.h>
+
+#if defined(_ASM)
+#include <sys/lx_signal.h>
+#include <sys/lx_syscall.h>
+#endif /* _ASM */
+
+/* 64-bit signal syscall numbers */
+#define LX_SYS_rt_sigreturn 15
+
+#if defined(lint)
+
+#include <sys/types.h>
+#include <sys/regset.h>
+#include <sys/signal.h>
+
+void
+lx_rt_sigreturn_tramp(void)
+{}
+
+#else /* lint */
+
+ /*
+ * Trampoline code is called by the return at the end of a Linux
+ * signal handler to return control to the interrupted application
+ * via the lx_rt_sigreturn() syscall.
+ */
+ ENTRY_NP(lx_rt_sigreturn_tramp)
+ movq $LX_SYS_rt_sigreturn, %rax
+ syscall
+ SET_SIZE(lx_rt_sigreturn_tramp)
+
+#endif /* lint */
diff --git a/usr/src/lib/brand/lx/lx_brand/common/capabilities.c b/usr/src/lib/brand/lx/lx_brand/common/capabilities.c
new file mode 100644
index 0000000000..e034560452
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/capabilities.c
@@ -0,0 +1,516 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+/*
+ * LX Brand emulation of capget/capset syscalls
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <sys/lx_types.h>
+#include <sys/lx_syscall.h>
+#include <sys/syscall.h>
+#include <alloca.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/lx_misc.h>
+#include <priv.h>
+
+typedef struct {
+ uint32_t version;
+ int pid;
+} lx_cap_user_header_t;
+
+typedef struct {
+ uint32_t effective;
+ uint32_t permitted;
+ uint32_t inheritable;
+} lx_cap_user_data_t;
+
+typedef struct {
+ priv_set_t *p_effective;
+ priv_set_t *p_permitted;
+ priv_set_t *p_inheritable;
+} lx_cap_privs_t;
+
+#define LX_CAP_UPDATE_PERMITTED 0x1
+#define LX_CAP_UPDATE_EFFECTIVE 0x2
+#define LX_CAP_UPDATE_INHERITABLE 0x4
+
+
+#define LX_CAP_MAXLEN 2
+
+typedef struct {
+ uint32_t effective[LX_CAP_MAXLEN];
+ uint32_t permitted[LX_CAP_MAXLEN];
+ uint32_t inheritable[LX_CAP_MAXLEN];
+} lx_cap_data_t;
+
+#define LX_CAP_VERSION_1 0x19980330
+#define LX_CAP_VERSION_2 0x20071026 /* deprecated by Linux */
+#define LX_CAP_VERSION_3 0x20080522
+
+#define LX_CAP_SETPCAP 8
+
+/*
+ * Even though we lack mappings for capabilities higher than LX_CAP_MAX_VALID,
+ * it's valuable to test all the way out to the end of the second field. This
+ * ensures that new capabilities we lack support for are not silently accepted.
+ */
+#define LX_CAP_MAX_CHECK 63
+
+#define LX_CAP_CAPISSET(id, cap) \
+ (((id < 32) && (((0x1 << id) & cap[0]) != 0)) || \
+ ((id >= 32) && (((0x1 << (id - 32) & cap[1]) != 0))))
+
+#define LX_CAP_CAPSET(id, cap) \
+ if (id < 32) { cap[0] |= (0x1 << id); } \
+ else { cap[1] |= (0x1 << (id - 32)); }
+
+static const char *lx_cap_map_chown[] = {
+ PRIV_FILE_CHOWN,
+ PRIV_FILE_CHOWN_SELF,
+ NULL
+};
+static const char *lx_cap_map_dac_override[] = {
+ PRIV_FILE_DAC_READ,
+ PRIV_FILE_DAC_WRITE,
+ PRIV_FILE_DAC_EXECUTE,
+ NULL
+};
+static const char *lx_cap_map_dac_read_search[] = {
+ PRIV_FILE_DAC_SEARCH,
+ PRIV_FILE_DAC_READ,
+ NULL
+};
+static const char *lx_cap_map_fowner[] = { PRIV_FILE_OWNER, NULL };
+static const char *lx_cap_map_fsetid[] = { PRIV_FILE_SETID, NULL };
+static const char *lx_cap_map_kill[] = { PRIV_PROC_OWNER, NULL };
+/*
+ * One way that Linux capabilities(7) differs from Illumos privileges(5) is
+ * that it distinguishes between setuid and setgroups rights. This will be a
+ * problem if an lx-branded process requests to drop only CAP_SETUID but not
+ * CAP_SETGID.
+ *
+ * In that case, CAP_SETUID will be maintained.
+ */
+static const char *lx_cap_map_setgid[] = { PRIV_PROC_SETID, NULL };
+static const char *lx_cap_map_setuid[] = { PRIV_PROC_SETID, NULL };
+static const char *lx_cap_map_linux_immutable[] = { PRIV_FILE_FLAG_SET, NULL };
+static const char *lx_cap_map_bind_service[] = { PRIV_NET_PRIVADDR, NULL };
+static const char *lx_cap_map_net_admin[] = {
+ PRIV_SYS_IP_CONFIG,
+ NULL
+ /*
+ * It would probably make sense to include PRIV_SYS_DL_CONFIG, but that
+ * privilege is not extended to non-global zones by default. A more
+ * sophisticated capabilities translation layer could make it optional.
+ */
+};
+static const char *lx_cap_map_net_raw[] = {
+ PRIV_NET_RAWACCESS,
+ PRIV_NET_ICMPACCESS,
+ NULL
+};
+static const char *lx_cap_map_ipc_lock[] = { PRIV_PROC_LOCK_MEMORY, NULL };
+static const char *lx_cap_map_ipc_owner[] = {
+ PRIV_IPC_DAC_READ,
+ PRIV_IPC_DAC_WRITE,
+ PRIV_IPC_OWNER,
+ NULL
+};
+static const char *lx_cap_map_sys_chroot[] = { PRIV_PROC_CHROOT, NULL };
+static const char *lx_cap_map_sys_admin[] = {
+ PRIV_SYS_MOUNT,
+ PRIV_SYS_ADMIN,
+ NULL
+};
+static const char *lx_cap_map_sys_nice[] = { PRIV_PROC_PRIOUP, NULL };
+static const char *lx_cap_map_sys_resource[] = { PRIV_SYS_RESOURCE, NULL };
+static const char *lx_cap_map_audit_write[] = { PRIV_PROC_AUDIT, NULL };
+static const char *lx_cap_map_audit_control[] = { PRIV_SYS_AUDIT, NULL };
+
+/*
+ * Mapping of Linux capabilities -> Illumos privileges
+ * The ID definitions can be found in the Linux sources here:
+ * include/uapi/linux/capability.h
+ *
+ * Order is critical.
+ */
+static const char ** lx_cap_mapping[LX_CAP_MAX_VALID + 1] = {
+ lx_cap_map_chown, /* CAP_CHOWN */
+ lx_cap_map_dac_override, /* CAP_DAC_OVERRIDE */
+ lx_cap_map_dac_read_search, /* CAP_DAC_READ_SEARCH */
+ lx_cap_map_fowner, /* CAP_FOWNER */
+ lx_cap_map_fsetid, /* CAP_FSETID */
+ lx_cap_map_kill, /* CAP_KILL */
+ lx_cap_map_setgid, /* CAP_SETGID */
+ lx_cap_map_setuid, /* CAP_SETUID */
+ NULL, /* CAP_SETPCAP */
+ lx_cap_map_linux_immutable, /* CAP_LINUX_IMMUTABLE */
+ lx_cap_map_bind_service, /* CAP_BIND_SERVICE */
+ NULL, /* CAP_BROADCAST */
+ lx_cap_map_net_admin, /* CAP_NET_ADMIN */
+ lx_cap_map_net_raw, /* CAP_NET_RAW */
+ lx_cap_map_ipc_lock, /* CAP_IPC_LOCK */
+ lx_cap_map_ipc_owner, /* CAP_IPC_OWNER */
+ NULL, /* CAP_MODULE */
+ NULL, /* CAP_RAWIO */
+ lx_cap_map_sys_chroot, /* CAP_SYS_CHROOT */
+ NULL, /* CAP_PTRACE */
+ NULL, /* CAP_PACCT */
+ lx_cap_map_sys_admin, /* CAP_SYS_ADMIN */
+ NULL, /* CAP_BOOT */
+ lx_cap_map_sys_nice, /* CAP_SYS_NICE */
+ lx_cap_map_sys_resource, /* CAP_SYS_RESOURCE */
+ NULL, /* CAP_SYS_TIME */
+ NULL, /* CAP_SYS_TTY_CONFIG */
+ NULL, /* CAP_MKNOD */
+ NULL, /* CAP_LEASE */
+ lx_cap_map_audit_write, /* CAP_AUDIT_WRITE */
+ lx_cap_map_audit_control, /* CAP_AUDIT_CONTROL */
+ NULL, /* CAP_SETFCAP */
+ NULL, /* CAP_MAC_OVERRIDE */
+ NULL, /* CAP_MAC_ADMIN */
+ NULL, /* CAP_SYSLOG */
+ NULL, /* CAP_WAKE_ALARM */
+ NULL /* CAP_BLOCK_SUSPEND */
+};
+
+/* track priv_set_t size, set on entry to lx_capset/lx_capget */
+static unsigned int lx_cap_priv_size = 0;
+
+/* safely allocate priv_set_t triplet on the stack */
+#define LX_CAP_ALLOC_PRIVS(ptr) \
+ { \
+ ptr = SAFE_ALLOCA(sizeof (lx_cap_privs_t) + \
+ (3 * lx_cap_priv_size)); \
+ if (ptr != NULL) { \
+ ptr->p_effective = (priv_set_t *) \
+ ((caddr_t)ptr + sizeof (lx_cap_privs_t)); \
+ ptr->p_permitted = (priv_set_t *) \
+ ((caddr_t)ptr + sizeof (lx_cap_privs_t) + \
+ lx_cap_priv_size);\
+ ptr->p_inheritable = (priv_set_t *) \
+ ((caddr_t)ptr + sizeof (lx_cap_privs_t) + \
+ 2 * lx_cap_priv_size); \
+ } \
+ }
+
+static long
+lx_cap_update_priv(priv_set_t *priv, const uint32_t cap[])
+{
+ int i, j;
+ boolean_t cap_set;
+ boolean_t priv_set;
+ boolean_t updated = B_FALSE;
+ for (i = 0; i <= LX_CAP_MAX_CHECK; i++) {
+ cap_set = LX_CAP_CAPISSET(i, cap);
+ if (i > LX_CAP_MAX_VALID || lx_cap_mapping[i] == NULL) {
+ /* don't allow setting unsupported caps */
+ if (cap_set) {
+ /*
+ * CAP_SETPCAP is a special capability, with
+ * varying behavior, that can be used to
+ * control if the process can change other
+ * process's capabilities, or to control moving
+ * capabilities between sets. For now we ignore
+ * this if its passed in.
+ */
+ if (i == LX_CAP_SETPCAP) {
+ continue;
+ }
+ lx_unsupported("set unsupported capability %d",
+ i);
+ return (-1);
+ } else {
+ continue;
+ }
+ }
+ for (j = 0; lx_cap_mapping[i][j] != NULL; j++) {
+ priv_set = priv_ismember(priv, lx_cap_mapping[i][j]);
+ if (priv_set && !cap_set) {
+ VERIFY0(priv_delset(priv,
+ lx_cap_mapping[i][j]));
+ updated = B_TRUE;
+ } else if (!priv_set && cap_set) {
+ VERIFY0(priv_addset(priv,
+ lx_cap_mapping[i][j]));
+ updated = B_TRUE;
+ }
+ }
+ }
+ if (updated)
+ return (1);
+ else
+ return (0);
+}
+
+static long
+lx_cap_to_priv(lx_cap_data_t *cap, lx_cap_privs_t *priv)
+{
+ long changes = 0;
+ long result;
+
+ result = lx_cap_update_priv(priv->p_permitted, cap->permitted);
+ if (result < 0)
+ return (-1);
+ else if (result > 0)
+ changes |= LX_CAP_UPDATE_PERMITTED;
+
+ result = lx_cap_update_priv(priv->p_effective, cap->effective);
+ if (result < 0)
+ return (-1);
+ else if (result > 0)
+ changes |= LX_CAP_UPDATE_EFFECTIVE;
+
+ result = lx_cap_update_priv(priv->p_inheritable, cap->inheritable);
+ if (result < 0)
+ return (-1);
+ else if (result > 0)
+ changes |= LX_CAP_UPDATE_INHERITABLE;
+
+ return (changes);
+}
+
+static void
+lx_cap_from_priv(const priv_set_t *priv, uint32_t cap[])
+{
+ int i, j;
+ boolean_t valid;
+ (void) memset(cap, '\0', sizeof (uint32_t) * LX_CAP_MAXLEN);
+ for (i = 0; i <= LX_CAP_MAX_VALID; i++) {
+ if (lx_cap_mapping[i] == NULL) {
+ continue;
+ }
+ valid = B_TRUE;
+ for (j = 0; lx_cap_mapping[i][j] != NULL; j++) {
+ if (!priv_ismember(priv,
+ lx_cap_mapping[i][j])) {
+ valid = B_FALSE;
+ }
+ }
+ if (valid) {
+ LX_CAP_CAPSET(i, cap);
+ }
+ }
+}
+
+static long
+lx_cap_read_cap(const lx_cap_user_header_t *uhp, const lx_cap_user_data_t *udp,
+ lx_cap_data_t *cd)
+{
+ lx_cap_user_header_t uh;
+ lx_cap_user_data_t ud_buf;
+ int cap_count;
+ int i;
+
+ if (uucopy(uhp, &uh, sizeof (uh)) != 0)
+ return (-errno);
+
+ switch (uh.version) {
+ case LX_CAP_VERSION_1:
+ cap_count = 1;
+ break;
+ case LX_CAP_VERSION_2:
+ case LX_CAP_VERSION_3:
+ cap_count = 2;
+ break;
+ default:
+ return (-EINVAL);
+ }
+
+ /* Only allow capset on calling process */
+ if (uh.pid != 0 && uh.pid != getpid())
+ return (-EPERM);
+
+ /* zero the struct in case cap_count < 2 */
+ (void) memset(cd, '\0', sizeof (lx_cap_data_t));
+
+ for (i = 0; i < cap_count; i++) {
+ if (uucopy(udp + i, &ud_buf, sizeof (ud_buf)) != 0)
+ return (-errno);
+ cd->permitted[i] = ud_buf.permitted;
+ cd->effective[i] = ud_buf.effective;
+ cd->inheritable[i] = ud_buf.inheritable;
+ }
+ return (0);
+}
+
+long
+lx_capget(uintptr_t p1, uintptr_t p2)
+{
+ const priv_impl_info_t *impl;
+ lx_cap_user_header_t *uhp = (lx_cap_user_header_t *)p1;
+ lx_cap_user_data_t *udp = (lx_cap_user_data_t *)p2;
+ lx_cap_user_header_t uh;
+ lx_cap_privs_t *privs;
+ lx_cap_data_t cd_result;
+ lx_cap_user_data_t cd_buf;
+ int cap_count;
+ int i;
+
+ if (lx_cap_priv_size == 0) {
+ impl = getprivimplinfo();
+ lx_cap_priv_size = sizeof (priv_chunk_t) * impl->priv_setsize;
+ }
+
+ if (uucopy(uhp, &uh, sizeof (uh)) != 0)
+ return (-errno);
+
+ switch (uh.version) {
+ case LX_CAP_VERSION_1:
+ cap_count = 1;
+ break;
+ case LX_CAP_VERSION_2:
+ case LX_CAP_VERSION_3:
+ cap_count = 2;
+ break;
+ default:
+ /*
+ * As per the man page: call will fail with EINVAL and set the
+ * version field of the header to the kernel preferred version
+ * when an unsupported version value is provided.
+ */
+ uh.version = LX_CAP_VERSION_3;
+ if (uucopy(&uh, uhp, sizeof (uh)) != 0)
+ return (-errno);
+ return (-EINVAL);
+ }
+
+ /*
+ * Only allow capget on the calling process.
+ * If a pid is specified, lie about being able to locate it.
+ */
+ if (uh.pid > 0 && uh.pid != getpid())
+ return (-ESRCH);
+ if (uh.pid < 0)
+ return (-EINVAL);
+
+ LX_CAP_ALLOC_PRIVS(privs)
+
+ if (privs == NULL)
+ return (-ENOMEM);
+
+ if (getppriv(PRIV_PERMITTED, privs->p_permitted) != 0)
+ return (-errno);
+ if (getppriv(PRIV_EFFECTIVE, privs->p_effective) != 0)
+ return (-errno);
+ if (getppriv(PRIV_INHERITABLE, privs->p_inheritable) != 0)
+ return (-errno);
+
+ lx_cap_from_priv(privs->p_permitted, cd_result.permitted);
+ lx_cap_from_priv(privs->p_effective, cd_result.effective);
+ lx_cap_from_priv(privs->p_inheritable, cd_result.inheritable);
+
+ /* convert to output format */
+ for (i = 0; i < cap_count; i++) {
+ cd_buf.effective = cd_result.effective[i];
+ cd_buf.permitted = cd_result.permitted[i];
+ cd_buf.inheritable = cd_result.inheritable[i];
+ if (uucopy(&cd_buf, udp + i, sizeof (cd_buf)) != 0)
+ return (-errno);
+ }
+
+ return (0);
+}
+
+long
+lx_capset(uintptr_t p1, uintptr_t p2)
+{
+ const priv_impl_info_t *impl;
+ lx_cap_data_t cd;
+ lx_cap_privs_t *privs;
+ long result;
+
+ if (lx_cap_priv_size == 0) {
+ impl = getprivimplinfo();
+ lx_cap_priv_size = sizeof (priv_chunk_t) * impl->priv_setsize;
+ }
+
+ /* verify header and read in desired capabilities */
+ result = lx_cap_read_cap((lx_cap_user_header_t *)p1,
+ (lx_cap_user_data_t *)p2, &cd);
+ if (result != 0)
+ return (result);
+
+ LX_CAP_ALLOC_PRIVS(privs)
+
+ if (privs == NULL)
+ return (-ENOMEM);
+
+ /* fetch current privs to compare against */
+ if (getppriv(PRIV_PERMITTED, privs->p_permitted) != 0)
+ return (-errno);
+ if (getppriv(PRIV_EFFECTIVE, privs->p_effective) != 0)
+ return (-errno);
+ if (getppriv(PRIV_INHERITABLE, privs->p_inheritable) != 0)
+ return (-errno);
+
+
+ result = lx_cap_to_priv(&cd, privs);
+ if (result < 0)
+ return (-EPERM);
+
+ /* report success if no changes needed */
+ if (result == 0)
+ return (0);
+
+ /* Ensure the effective/inheritable caps aren't > permitted */
+ if (!priv_issubset(privs->p_effective, privs->p_permitted) ||
+ !priv_issubset(privs->p_inheritable, privs->p_permitted))
+ return (-EPERM);
+
+ /*
+ * Here is where things become racy. Linux updates all three
+ * capability sets simultaneously in the capset syscall. In order to
+ * emulate capabilities via privileges, three setppriv operations are
+ * required in sequence. If one or two should fail, there is not a
+ * mechanism to convey the incomplete operation to the caller.
+ *
+ * We do two things to make this less risky:
+ * 1. Verify that both the desired effective and inheritable
+ * sets are subsets of the desired permitted set.
+ * 2. Perform the setppriv of the permitted set first.
+ *
+ * Should the setppriv(permitted) fail, we can safely bail out with an
+ * error. If it succeeds, the setppriv of effective and inheritable
+ * are likely to succeed given that they've been verified legal.
+ *
+ * If the partial error does happen, we'll be forced to report failure
+ * even though the privileges were altered.
+ */
+
+ if ((result & LX_CAP_UPDATE_PERMITTED) != 0) {
+ /* failure here is totally safe */
+ if (setppriv(PRIV_SET, PRIV_PERMITTED, privs->p_permitted) != 0)
+ return (-errno);
+ }
+ if ((result & LX_CAP_UPDATE_EFFECTIVE) != 0) {
+ /* failure here is a bummer */
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, privs->p_effective) != 0)
+ return (-errno);
+ }
+ if ((result & LX_CAP_UPDATE_EFFECTIVE) != 0) {
+ /* failure here is a major bummer */
+ if (setppriv(PRIV_SET, PRIV_INHERITABLE,
+ privs->p_inheritable) != 0)
+ return (-errno);
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/clock.c b/usr/src/lib/brand/lx/lx_brand/common/clock.c
new file mode 100644
index 0000000000..e627df68dc
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/clock.c
@@ -0,0 +1,192 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/resource.h>
+#include <sys/timerfd.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+#include <lx_signum.h>
+
+/*
+ * Translating from the Linux clock types to the illumos types is a bit of a
+ * mess.
+ *
+ * Linux uses different values for it clock identifiers, so we have to do basic
+ * translations between the two. Thankfully, both Linux and illumos implement
+ * the same POSIX SUSv3 clock types, so the semantics should be identical.
+ *
+ * However, CLOCK_REALTIME and CLOCK_HIGHRES (CLOCK_MONOTONIC) are the only two
+ * clock backends currently implemented on illumos. Functions in the kernel
+ * that use the CLOCK_BACKEND macro will return an error for any clock type
+ * that does not exist in the clock_backend array. These functions are
+ * clock_settime, clock_gettime, clock_getres and timer_create.
+ *
+ * For reference, the kernel's clock_backend array looks like this:
+ *
+ * clock_backend[CLOCK_MAX] (6 entries)
+ * 0 __CLOCK_REALTIME0 valid ptr. (obs. same as CLOCK_REALTIME)
+ * 1 CLOCK_VIRTUAL NULL
+ * 2 CLOCK_THREAD_CPUTIME_ID NULL
+ * 3 CLOCK_REALTIME valid ptr.
+ * 4 CLOCK_MONOTONIC (CLOCK_HIGHRES) valid ptr.
+ * 5 CLOCK_PROCESS_CPUTIME_ID NULL
+ */
+
+#define CLOCK_RT_SLOT 0
+
+#define LX_CLOCK_REALTIME 0
+#define LX_CLOCK_MONOTONIC 1
+
+/*
+ * Limits for a minimum interval are enforced when creating timers from the
+ * CLOCK_HIGHRES source. Values below this minimum will be clamped if the
+ * process lacks the proc_clock_highres privilege.
+ */
+static int ltos_clock[] = {
+ CLOCK_REALTIME, /* LX_CLOCK_REALTIME */
+ CLOCK_HIGHRES, /* LX_CLOCK_MONOTONIC */
+ CLOCK_PROCESS_CPUTIME_ID, /* LX_CLOCK_PROCESS_CPUTIME_ID */
+ CLOCK_THREAD_CPUTIME_ID, /* LX_CLOCK_THREAD_CPUTIME_ID */
+ CLOCK_HIGHRES, /* LX_CLOCK_MONOTONIC_RAW */
+ CLOCK_REALTIME, /* LX_CLOCK_REALTIME_COARSE */
+ CLOCK_HIGHRES /* LX_CLOCK_MONOTONIC_COARSE */
+};
+
+#define LX_CLOCK_MAX (sizeof (ltos_clock) / sizeof (ltos_clock[0]))
+
+
+long
+lx_clock_nanosleep(int clock, int flags, struct timespec *rqtp,
+ struct timespec *rmtp)
+{
+ int ret = 0;
+ int err;
+ struct timespec rqt, rmt;
+
+ if (clock < 0 || clock >= LX_CLOCK_MAX)
+ return (-EINVAL);
+
+ if (uucopy(rqtp, &rqt, sizeof (struct timespec)) < 0)
+ return (-EFAULT);
+
+ /* the TIMER_RELTIME and TIMER_ABSTIME flags are the same on Linux */
+ if ((err = clock_nanosleep(ltos_clock[clock], flags, &rqt, &rmt))
+ != 0) {
+ if (err != EINTR)
+ return (-err);
+ ret = -EINTR;
+ /*
+ * We fall through in case we have to pass back the remaining
+ * time.
+ */
+ }
+
+ /*
+ * Only copy values to rmtp if the timer is TIMER_RELTIME and rmtp is
+ * non-NULL.
+ */
+ if (((flags & TIMER_RELTIME) == TIMER_RELTIME) && (rmtp != NULL) &&
+ (uucopy(&rmt, rmtp, sizeof (struct timespec)) < 0))
+ return (-EFAULT);
+
+ return (ret);
+}
+
+/*ARGSUSED*/
+long
+lx_adjtimex(void *tp)
+{
+ return (-EPERM);
+}
+
+long
+lx_timer_settime(timer_t tid, int flags, struct itimerspec *new_val,
+ struct itimerspec *old_val)
+{
+ return ((timer_settime(tid, flags, new_val, old_val) < 0) ? -errno : 0);
+}
+
+long
+lx_timer_gettime(timer_t tid, struct itimerspec *val)
+{
+ return ((timer_gettime(tid, val) < 0) ? -errno : 0);
+}
+
+long
+lx_timer_getoverrun(timer_t tid)
+{
+ int val;
+
+ val = timer_getoverrun(tid);
+ return ((val < 0) ? -errno : val);
+}
+
+long
+lx_timer_delete(timer_t tid)
+{
+ return ((timer_delete(tid) < 0) ? -errno : 0);
+}
+
+long
+lx_timerfd_create(int clockid, int flags)
+{
+ int r;
+
+ /* These are the only two valid values. LTP tests for this. */
+ if (clockid != LX_CLOCK_REALTIME && clockid != LX_CLOCK_MONOTONIC)
+ return (-EINVAL);
+
+ r = timerfd_create(ltos_clock[clockid], flags);
+ /*
+ * As with the eventfd case, we return a slightly less jarring
+ * error condition if we cannot open /dev/timerfd.
+ */
+ if (r == -1 && errno == ENOENT)
+ return (-ENOTSUP);
+
+ return (r == -1 ? -errno : r);
+}
+
+long
+lx_timerfd_settime(int fd, int flags, const struct itimerspec *value,
+ struct itimerspec *ovalue)
+{
+ int r = timerfd_settime(fd, flags, value, ovalue);
+
+ return (r == -1 ? -errno : r);
+}
+
+long
+lx_timerfd_gettime(int fd, struct itimerspec *value)
+{
+ int r = timerfd_gettime(fd, value);
+
+ return (r == -1 ? -errno : r);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/clone.c b/usr/src/lib/brand/lx/lx_brand/common/clone.c
new file mode 100644
index 0000000000..c6048281b5
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/clone.c
@@ -0,0 +1,724 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <ucontext.h>
+#include <thread.h>
+#include <strings.h>
+#include <libintl.h>
+#include <sys/regset.h>
+#include <sys/syscall.h>
+#include <sys/inttypes.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/segments.h>
+#include <signal.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_types.h>
+#include <sys/lx_signal.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_thread.h>
+#include <sys/fork.h>
+#include <sys/mman.h>
+#include <sys/debug.h>
+#include <lx_syscall.h>
+
+#define CLONE_VFORK (LX_CLONE_VM | LX_CLONE_VFORK)
+#define CLONE_TD (LX_CLONE_THREAD|LX_CLONE_DETACH)
+
+#define IS_FORK(f) (((f) & SHARED_AS) == 0)
+#define IS_VFORK(f) (((f) & CLONE_VFORK) == CLONE_VFORK)
+
+/*
+ * This is dicey. This seems to be an internal glibc structure, and not
+ * part of any external interface. Thus, it is subject to change without
+ * notice. FWIW, clone(2) itself seems to be an internal (or at least
+ * unstable) interface, since strace(1) shows it differently than the man
+ * page.
+ */
+struct lx_desc
+{
+ uint32_t entry_number;
+ uint32_t base_addr;
+ uint32_t limit;
+ uint32_t seg_32bit:1;
+ uint32_t contents:2;
+ uint32_t read_exec_only:1;
+ uint32_t limit_in_pages:1;
+ uint32_t seg_not_present:1;
+ uint32_t useable:1;
+ uint32_t empty:25;
+};
+
+struct clone_state {
+ void *c_retaddr; /* instr after clone()'s int80 */
+ int c_flags; /* flags to clone(2) */
+ int c_sig; /* signal to send on thread exit */
+ void *c_stk; /* %esp of new thread */
+ void *c_ptidp;
+ struct lx_desc *c_ldtinfo; /* thread-specific segment */
+ void *c_ctidp;
+ ucontext_t c_uc; /* original register state/sigmask */
+ volatile int *c_clone_res; /* pid/error returned to cloner */
+ int c_ptrace_event; /* ptrace(2) event for child stop */
+ void *c_ntv_stk; /* native stack for this thread */
+ size_t c_ntv_stk_sz; /* native stack size */
+ lx_tsd_t *c_lx_tsd; /* tsd area for thread */
+};
+
+long
+lx_exit(uintptr_t p1)
+{
+ int status = (int)p1;
+ lx_tsd_t *lx_tsd = lx_get_tsd();
+
+ /*
+ * If we are a vfork(2)ed child, we need to exit as quickly and
+ * cleanly as possible to avoid corrupting our parent.
+ */
+ if (lx_tsd->lxtsd_is_vforked != 0) {
+ _exit(status);
+ }
+
+ lx_tsd->lxtsd_exit = LX_ET_EXIT;
+ lx_tsd->lxtsd_exit_status = status;
+
+ lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEEXIT, B_FALSE,
+ (ulong_t)status, NULL);
+
+ /*
+ * This thread is exiting. Restore the state of the thread to
+ * what it was before we started running linux code.
+ */
+ (void) setcontext(&lx_tsd->lxtsd_exit_context);
+
+ /*
+ * If we returned from the setcontext(2), something is very wrong.
+ */
+ lx_err_fatal("exit: unable to set exit context: %s", strerror(errno));
+
+ /*NOTREACHED*/
+ return (0);
+}
+
+long
+lx_group_exit(uintptr_t p1)
+{
+ int status = (int)p1;
+ lx_tsd_t *lx_tsd = lx_get_tsd();
+
+ /*
+ * If we are a vfork(2)ed child, we need to exit as quickly and
+ * cleanly as possible to avoid corrupting our parent.
+ */
+ if (lx_tsd->lxtsd_is_vforked != 0) {
+ _exit(status);
+ }
+
+ lx_tsd->lxtsd_exit = LX_ET_EXIT_GROUP;
+ lx_tsd->lxtsd_exit_status = status;
+
+ /*
+ * This thread is exiting. Restore the state of the thread to
+ * what it was before we started running linux code.
+ */
+ (void) setcontext(&lx_tsd->lxtsd_exit_context);
+
+ /*
+ * If we returned from the setcontext(2), something is very wrong.
+ */
+ lx_err_fatal("group_exit: unable to set exit context: %s",
+ strerror(errno));
+
+ /*NOTREACHED*/
+ return (0);
+}
+
+static void *
+clone_start(void *arg)
+{
+ int rval;
+ struct clone_state *cs = (struct clone_state *)arg;
+ lx_tsd_t *lxtsd;
+
+ /*
+ * Let the kernel finish setting up all the needed state for this
+ * new thread.
+ *
+ * We already created the thread using the thr_create(3C) library
+ * call, so most of the work required to emulate lx_clone(2) has
+ * been done by the time we get to this point.
+ */
+ lx_debug("\tre-vectoring to lx kernel module to complete lx_clone()");
+ lx_debug("\tB_HELPER_CLONE(0x%x, 0x%p, 0x%p, 0x%p)",
+ cs->c_flags, cs->c_ptidp, cs->c_ldtinfo, cs->c_ctidp);
+
+ rval = syscall(SYS_brand, B_HELPER_CLONE, cs->c_flags, cs->c_ptidp,
+ cs->c_ldtinfo, cs->c_ctidp);
+
+ /*
+ * At this point the parent is waiting for cs->c_clone_res to go
+ * non-zero to indicate the thread has been cloned. The value set
+ * in cs->c_clone_res will be used for the return value from
+ * clone().
+ */
+ if (rval < 0) {
+ *(cs->c_clone_res) = -errno;
+ lx_debug("\tkernel clone failed, errno %d\n", errno);
+ free(cs->c_lx_tsd);
+ free(cs);
+ return (NULL);
+ }
+
+ /*
+ * Initialize the thread specific data for this thread.
+ */
+ lxtsd = cs->c_lx_tsd;
+ lx_init_tsd(lxtsd);
+ lxtsd->lxtsd_clone_state = cs;
+
+ /*
+ * Install the emulation stack for this thread. Register the
+ * thread-specific data structure with the stack list so that it may be
+ * freed at thread exit or fork(2).
+ */
+ lx_install_stack(cs->c_ntv_stk, cs->c_ntv_stk_sz, lxtsd);
+
+ /*
+ * Let the parent know that the clone has (effectively) been
+ * completed.
+ */
+ *(cs->c_clone_res) = rval;
+
+ /*
+ * We want to load the general registers from this context, restore the
+ * original signal mask, and switch to the BRAND stack. The original
+ * signal mask was saved to the context by lx_clone().
+ */
+ cs->c_uc.uc_flags = UC_CPU | UC_SIGMASK;
+ cs->c_uc.uc_brand_data[0] = (void *)LX_UC_STACK_BRAND;
+
+ /*
+ * New threads will not link into the existing context chain.
+ */
+ cs->c_uc.uc_link = NULL;
+
+ /*
+ * Set stack pointer and entry point for new thread:
+ */
+ LX_REG(&cs->c_uc, REG_SP) = (uintptr_t)cs->c_stk;
+ LX_REG(&cs->c_uc, REG_PC) = (uintptr_t)cs->c_retaddr;
+
+ /*
+ * Return 0 to the child:
+ */
+ LX_REG(&cs->c_uc, REG_R0) = (uintptr_t)0;
+
+ /*
+ * Fire the ptrace(2) event stop in the new thread:
+ */
+ lx_ptrace_stop_if_option(cs->c_ptrace_event, B_TRUE, 0, &cs->c_uc);
+
+ /*
+ * Jump to the Linux process. This call cannot return.
+ */
+ lx_jump_to_linux(&cs->c_uc);
+ /* NOTREACHED */
+}
+
+/*
+ * The way Linux handles stopping for FORK vs. CLONE does not map exactly to
+ * which syscall was used. Instead, it has to do with which signal is set in
+ * the low byte of the clone flag. The only time the CLONE event is emitted is
+ * if the clone signal (the low byte of the flags argument) is set to something
+ * other than SIGCHLD (see the Linux src in kernel/fork.c do_fork() for the
+ * actual code).
+ */
+static int
+ptrace_clone_event(int flags)
+{
+ if (flags & LX_CLONE_VFORK)
+ return (LX_PTRACE_O_TRACEVFORK);
+
+ if ((flags & LX_CSIGNAL) != LX_SIGCHLD)
+ return (LX_PTRACE_O_TRACECLONE);
+
+ return (LX_PTRACE_O_TRACEFORK);
+}
+
+/*
+ * See glibc sysdeps/unix/sysv/linux/x86_64/clone.S code for x64 argument order
+ * and the Linux kernel/fork.c code for the various ways arguments can be passed
+ * to the clone syscall (CONFIG_CLONE_BACKWARDS, et al).
+ */
+long
+lx_clone(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5)
+{
+ struct clone_state *cs;
+ int flags = (int)p1;
+ void *cldstk = (void *)p2;
+ void *ptidp = (void *)p3;
+#if defined(_LP64)
+ void *ctidp = (void *)p4;
+ struct lx_desc *ldtinfo = (void *)p5;
+#else /* is 32bit */
+ struct lx_desc *ldtinfo = (void *)p4;
+ void *ctidp = (void *)p5;
+#endif
+ thread_t tid;
+ volatile int clone_res;
+ int sig;
+ int rval;
+ int pid;
+ ucontext_t *ucp;
+ sigset_t sigmask, osigmask;
+ int fork_flags = 0;
+ int ptrace_event;
+ lx_tsd_t *lx_tsd = lx_get_tsd();
+
+ if (flags & LX_CLONE_SETTLS) {
+ lx_debug("lx_clone(flags=0x%x stk=0x%p ptidp=0x%p ldt=0x%p "
+ "ctidp=0x%p", flags, cldstk, ptidp, ldtinfo, ctidp);
+ } else {
+ lx_debug("lx_clone(flags=0x%x stk=0x%p ptidp=0x%p)",
+ flags, cldstk, ptidp);
+ }
+
+ /*
+ * Only supported for pid 0 on Linux after version 2.3.21, and
+ * apparently not at all since 2.5.16.
+ */
+ if (flags & LX_CLONE_PID)
+ return (-EINVAL);
+
+ /*
+ * CLONE_THREAD requires CLONE_SIGHAND.
+ *
+ * CLONE_THREAD and CLONE_DETACHED must both be either set or cleared
+ * in kernel 2.4 and prior.
+ * In kernel 2.6 (and later) CLONE_DETACHED was dropped completely, so
+ * we no longer have this requirement.
+ */
+
+ if (flags & CLONE_TD) {
+ if (!(flags & LX_CLONE_SIGHAND))
+ return (-EINVAL);
+ if (strncmp(lx_release, "2.4", 3) == 0 &&
+ (flags & CLONE_TD) != CLONE_TD)
+ return (-EINVAL);
+ }
+
+ if ((flags & LX_CLONE_NS_UNSUP) != 0) {
+ lx_unsupported("clone(2) no namespace support "
+ "(flags:0x%08X)\n", flags);
+ /*
+ * When the "kernel" does not support namespaces, applications
+ * (e.g. chromium) expect EINVAL, not ENOTSUP.
+ */
+ return (-EINVAL);
+ }
+
+ ucp = lx_syscall_regs();
+
+ /* test if pointer passed by user are writable */
+ if (flags & LX_CLONE_PARENT_SETTID) {
+ if (uucopy(ptidp, &pid, sizeof (int)) != 0)
+ return (-EFAULT);
+ if (uucopy(&pid, ptidp, sizeof (int)) != 0)
+ return (-EFAULT);
+ }
+ if (flags & LX_CLONE_CHILD_SETTID) {
+ if (uucopy(ctidp, &pid, sizeof (int)) != 0)
+ return (-EFAULT);
+ if (uucopy(&pid, ctidp, sizeof (int)) != 0)
+ return (-EFAULT);
+ }
+
+ ptrace_event = ptrace_clone_event(flags);
+
+ /*
+ * Inform the in-kernel ptrace(2) subsystem that we are about to
+ * emulate a fork(2), vfork(2) or clone(2) system call.
+ */
+ lx_ptrace_clone_begin(ptrace_event, !!(flags & LX_CLONE_PTRACE), flags);
+
+ /*
+ * Handle a fork(2) operation here. If this is not a fork, a new
+ * thread will be created after this block. We can also create a new
+ * clone-group here (when two or more processes share data represented
+ * by a subset of the SHARED_AS flags, but not a true thread).
+ */
+ if (IS_FORK(flags) || IS_VFORK(flags) || LX_IS_CLONE_GRP(flags)) {
+ if (flags & LX_CLONE_PARENT) {
+ lx_unsupported("clone(2) only supports CLONE_PARENT "
+ "for threads.\n");
+ return (-ENOTSUP);
+ }
+
+ if ((flags & LX_CSIGNAL) == 0)
+ fork_flags |= FORK_NOSIGCHLD;
+
+ /*
+ * Suspend signal delivery, run the stack management prefork
+ * handler and perform the actual fork(2) operation.
+ *
+ * During vfork, Linux will not deliver any signals to any
+ * thread in the parent. Some applications (e.g. Go) depend on
+ * this. For example, we must prevent the following sequence:
+ * 1) Parent with many threads, one thread calls vfork
+ * 2) vforked child resets all signal handlers
+ * 3) a different child of the parent exits and SIGCHLD is sent
+ * to parent before the vforked child execs/exits
+ * The parent cannot receive the SIGCHLD until afer we repair
+ * the parent's signal handlers in lx_sighandlers_restore, once
+ * the parent resumes after the vfork.
+ */
+ if (flags & LX_CLONE_VFORK) {
+ lx_block_all_signals();
+ } else {
+ _sigoff();
+ }
+ lx_stack_prefork();
+ if (flags & LX_CLONE_VFORK) {
+ lx_sighandlers_t saved;
+
+ /*
+ * Because we keep our signal disposition at user-land
+ * (and in memory), we must prevent it from being
+ * clobbered should our vforked child change the
+ * disposition (e.g., via sigaction()) before releasing
+ * the address space. We preserve our disposition by
+ * taking a snapshot of it before the vfork and
+ * restoring it afterwards -- which we can get away
+ * with because we know that we aren't executing
+ * concurrently with our child.
+ */
+ lx_sighandlers_save(&saved);
+ lx_tsd->lxtsd_is_vforked++;
+ rval = vforkx(fork_flags);
+ if (rval != 0) {
+ lx_tsd->lxtsd_is_vforked--;
+ lx_sighandlers_restore(&saved);
+ }
+ } else {
+ rval = forkx(fork_flags);
+ }
+
+ /*
+ * The parent process returns through the regular system call
+ * path here.
+ */
+ if (rval != 0) {
+ /*
+ * Run the stack management postfork handler in the
+ * parent. In the CLONE_VFORK case, where it only
+ * needs to be performed once due to the shared address
+ * space, it is critical that this step is performed in
+ * the parent and not the child. The latter can result
+ * in un-woken threads blocked on lx_stack_list_lock.
+ */
+ lx_stack_postfork();
+
+ /*
+ * Since we've already forked, we can't do much if
+ * uucopy fails, so we just ignore failure. Failure is
+ * unlikely since we've tested the memory before we did
+ * the fork.
+ */
+ if (rval > 0 && (flags & LX_CLONE_PARENT_SETTID)) {
+ (void) uucopy(&rval, ptidp, sizeof (int));
+ }
+
+ /*
+ * Re-enable signal delivery in the parent process.
+ */
+ if (flags & LX_CLONE_VFORK) {
+ lx_unblock_all_signals();
+ } else {
+ _sigon();
+ }
+
+ if (rval > 0) {
+ lx_ptrace_stop_if_option(ptrace_event, B_FALSE,
+ (ulong_t)rval, NULL);
+ }
+
+ return ((rval < 0) ? -errno : rval);
+ }
+
+ /*
+ * The rest of this block runs only within the new child
+ * process.
+ */
+
+ if (!IS_VFORK(flags)) {
+ /*
+ * For non-vfork children run the stack management
+ * postfork handler.
+ */
+ lx_stack_postfork();
+
+ /*
+ * We must free the stacks and thread-specific data
+ * objects for every thread except the one duplicated
+ * from the parent by forkx().
+ */
+ lx_free_other_stacks();
+ }
+
+ if (rval == 0 && (flags & LX_CLONE_CHILD_SETTID)) {
+ /*
+ * lx_getpid should not fail, and if it does, there's
+ * not much we can do about it since we've already
+ * forked, so on failure, we just don't copy the
+ * memory.
+ */
+ pid = syscall(SYS_brand, B_GETPID);
+ if (pid >= 0)
+ (void) uucopy(&pid, ctidp, sizeof (int));
+ }
+
+ /*
+ * Set up additional data in the lx_proc_data structure as
+ * necessary.
+ */
+ if ((rval = syscall(SYS_brand, B_HELPER_CLONE, flags, ptidp,
+ ldtinfo, ctidp)) < 0) {
+ return (rval);
+ }
+
+ if (IS_VFORK(flags)) {
+ ucontext_t vforkuc;
+
+ /*
+ * The vfork(2) interface is somewhat less than ideal.
+ * The unfortunate notion of borrowing the address
+ * space of the parent process requires us to jump
+ * through several hoops to prevent corrupting parent
+ * emulation state.
+ *
+ * When returning in the child, we make a copy of the
+ * system call return context and discard three pages
+ * of the native stack. Returning normally would
+ * clobber the native stack frame in which the brand
+ * library in the parent process is presently waiting.
+ *
+ * The calling program is expected to correctly use
+ * this dusty, underspecified relic. Neglecting to
+ * immediately call execve(2) or exit(2) is not
+ * cricket; this stack space will be permanently lost,
+ * not to mention myriad other undefined behaviour.
+ */
+ bcopy(ucp, &vforkuc, sizeof (vforkuc));
+ vforkuc.uc_brand_data[1] =
+ (caddr_t)vforkuc.uc_brand_data[1] -
+ LX_NATIVE_STACK_VFORK_GAP;
+ vforkuc.uc_link = NULL;
+
+ lx_debug("\tvfork native stack sp %p",
+ vforkuc.uc_brand_data[1]);
+
+ /*
+ * If provided, the child needs its new stack set up.
+ */
+ if (cldstk != 0) {
+ lx_debug("\tvfork cldstk %p", cldstk);
+ LX_REG(&vforkuc, REG_SP) = (uintptr_t)cldstk;
+ }
+
+ /*
+ * Re-enable signal delivery in the child process.
+ */
+ lx_unblock_all_signals();
+
+ /*
+ * Stop for ptrace if required.
+ */
+ lx_ptrace_stop_if_option(ptrace_event, B_TRUE, 0, NULL);
+
+ /*
+ * Return to the child via the specially constructed
+ * vfork(2) context.
+ */
+ LX_EMULATE_RETURN(&vforkuc, LX_SYS_clone, 0, 0);
+ (void) syscall(SYS_brand, B_EMULATION_DONE, &vforkuc,
+ LX_SYS_clone, 0, 0);
+
+ assert(0);
+ }
+
+ /*
+ * If provided, the child needs its new stack set up.
+ */
+ if (cldstk != 0) {
+ lx_debug("\tcldstk %p", cldstk);
+ LX_REG(ucp, REG_SP) = (uintptr_t)cldstk;
+ }
+
+ /*
+ * Re-enable signal delivery in the child process.
+ */
+ if (flags & LX_CLONE_VFORK) {
+ lx_unblock_all_signals();
+ } else {
+ _sigon();
+ }
+
+ /*
+ * Stop for ptrace if required.
+ */
+ lx_ptrace_stop_if_option(ptrace_event, B_TRUE, 0, NULL);
+
+ /*
+ * The child process returns via the regular emulated system
+ * call path:
+ */
+ return (0);
+ }
+
+ /*
+ * A supported clone-group was handled above, so now it must be a
+ * true native thread, which means exactly these flags are supported
+ */
+ if (((flags & SHARED_AS) != SHARED_AS)) {
+ lx_unsupported("clone(2) a thread requires that all or none of "
+ "CLONE_VM/FS/FILES/THREAD/SIGHAND be set. (flags:0x%08X)\n",
+ flags);
+ return (-ENOTSUP);
+ }
+
+ if (cldstk == NULL) {
+ lx_unsupported("clone(2) requires the caller to allocate the "
+ "child's stack.\n");
+ return (-ENOTSUP);
+ }
+
+ /*
+ * If we want a signal-on-exit, ensure that the signal is valid.
+ */
+ if ((sig = ltos_signo[flags & LX_CSIGNAL]) == -1) {
+ lx_unsupported("clone(2) passed unsupported signal: %d", sig);
+ return (-ENOTSUP);
+ }
+
+ /*
+ * Initialise the state structure we pass as an argument to the new
+ * thread:
+ */
+ if ((cs = malloc(sizeof (*cs))) == NULL) {
+ lx_debug("could not allocate clone_state: %d", errno);
+ return (-ENOMEM);
+ }
+ cs->c_flags = flags;
+ cs->c_sig = sig;
+ cs->c_stk = cldstk;
+ cs->c_ptidp = ptidp;
+ cs->c_ldtinfo = ldtinfo;
+ cs->c_ctidp = ctidp;
+ cs->c_clone_res = &clone_res;
+ cs->c_ptrace_event = ptrace_event;
+ /*
+ * We want the new thread to return directly to the call site for
+ * the system call.
+ */
+ cs->c_retaddr = (void *)LX_REG(ucp, REG_PC);
+ /*
+ * Copy the saved context for the clone(2) system call so that the
+ * new thread may use it to initialise registers.
+ */
+ bcopy(ucp, &cs->c_uc, sizeof (cs->c_uc));
+ if ((cs->c_lx_tsd = malloc(sizeof (*cs->c_lx_tsd))) == NULL) {
+ free(cs);
+ return (-ENOMEM);
+ }
+
+ clone_res = 0;
+
+ /*
+ * Block all signals because the thread we create won't be able to
+ * properly handle them until it's fully set up.
+ */
+ VERIFY0(sigfillset(&sigmask));
+ if (sigprocmask(SIG_BLOCK, &sigmask, &osigmask) < 0) {
+ lx_debug("lx_clone sigprocmask() failed: %d", errno);
+ free(cs->c_lx_tsd);
+ free(cs);
+ return (-errno);
+ }
+ cs->c_uc.uc_sigmask = osigmask;
+
+ /*
+ * Allocate the native stack for this new thread now, so that we
+ * can return failure gracefully as ENOMEM.
+ */
+ if (lx_alloc_stack(&cs->c_ntv_stk, &cs->c_ntv_stk_sz) != 0) {
+ free(cs->c_lx_tsd);
+ free(cs);
+ return (-ENOMEM);
+ }
+
+ rval = thr_create(NULL, 0, clone_start, cs, THR_DETACHED, &tid);
+
+ /*
+ * If the thread did not start, free the resources we allocated:
+ */
+ if (rval != 0) {
+ (void) munmap(cs->c_ntv_stk, cs->c_ntv_stk_sz);
+ free(cs->c_lx_tsd);
+ free(cs);
+ }
+
+ /*
+ * Release any pending signals
+ */
+ (void) sigprocmask(SIG_SETMASK, &osigmask, NULL);
+
+ /*
+ * Wait for the child to be created and have its tid assigned.
+ */
+ if (rval == 0) {
+ while (clone_res == 0)
+ ;
+
+ rval = clone_res;
+ lx_ptrace_stop_if_option(ptrace_event, B_FALSE, (ulong_t)rval,
+ NULL);
+
+ return (rval);
+ } else {
+ /*
+ * Return the error from thr_create(3C).
+ */
+ return (-rval);
+ }
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/debug.c b/usr/src/lib/brand/lx/lx_brand/common/debug.c
new file mode 100644
index 0000000000..a8f994c43d
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/debug.c
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <thread.h>
+#include <unistd.h>
+
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+
+/* internal debugging state */
+static const char *lx_debug_path = NULL; /* debug output file path */
+static char lx_debug_path_buf[MAXPATHLEN];
+
+int lx_dtrace_lazyload = 1; /* patchable; see below */
+
+void
+lx_debug_init(boolean_t do_dtrace, boolean_t dbg_enable, const char *dbg_file)
+{
+ /*
+ * Our DTrace USDT provider is loaded in our .init section, which is
+ * not run by our e_entry ELF entry point (_start, which calls into
+ * lx_init()). We exploit this to only actually load our USDT provider
+ * if LX_DTRACE is set, assuring that we don't compromise fork()
+ * performance in the (common) case that DTrace of lx_brand.so.1 itself
+ * isn't enabled or desired. (As with all USDT providers, it can always
+ * be loaded by explicitly specifying the full provider name). Note
+ * that we also allow this behavior to be set via a manual override,
+ * lx_dtrace_lazyload -- allowing for USDT probes to be automatically
+ * provided in situations where setting an environment variable is
+ * tedious or otherwise impossible.
+ */
+ if (do_dtrace || !lx_dtrace_lazyload) {
+ extern void _init(void);
+ _init();
+ }
+
+ if (!dbg_enable)
+ return;
+
+ /*
+ * It's OK to use this value without any locking, as all callers can
+ * use the return value to decide whether extra work should be done
+ * before calling lx_debug().
+ *
+ * If debugging is disabled after a routine calls this function it
+ * doesn't really matter as lx_debug() will see debugging is disabled
+ * and will not output anything.
+ */
+ lx_debug_enabled = 1;
+
+ /* check if there's a debug log file specified */
+ lx_debug_path = dbg_file;
+ if (lx_debug_path == NULL) {
+ /* send all debugging output to /dev/tty */
+ lx_debug_path = "/dev/tty";
+ }
+
+ (void) strlcpy(lx_debug_path_buf, lx_debug_path,
+ sizeof (lx_debug_path_buf));
+ lx_debug_path = lx_debug_path_buf;
+
+ lx_debug("lx_debug: debugging output ENABLED to path: \"%s\"",
+ lx_debug_path);
+}
+
+void
+lx_debug(const char *msg, ...)
+{
+ va_list ap;
+ char *buf;
+ int rv, fd, n;
+ int errno_backup;
+ int size = LX_MSG_MAXLEN + 1;
+
+ if (lx_debug_enabled == 0 && !LX_DEBUG_ENABLED())
+ return;
+
+ /*
+ * If debugging is not enabled, we do not wish to have a large stack
+ * footprint. The buffer allocation is thus done conditionally,
+ * rather than as regular automatic storage.
+ */
+ if ((buf = SAFE_ALLOCA(size)) == NULL)
+ return;
+
+ errno_backup = errno;
+
+ /* prefix the message with pid/tid */
+ if ((n = snprintf(buf, size, "%u/%u: ", getpid(), thr_self())) == -1) {
+ errno = errno_backup;
+ return;
+ }
+
+ /* format the message */
+ va_start(ap, msg);
+ rv = vsnprintf(&buf[n], size - n, msg, ap);
+ va_end(ap);
+ if (rv == -1) {
+ errno = errno_backup;
+ return;
+ }
+
+ /* add a carrige return if there isn't one already */
+ if ((buf[strlen(buf) - 1] != '\n') &&
+ (strlcat(buf, "\n", size) >= size)) {
+ errno = errno_backup;
+ return;
+ }
+
+ LX_DEBUG(buf);
+
+ if (!lx_debug_enabled)
+ return;
+
+ /*
+ * Open the debugging output file. note that we don't protect
+ * ourselves against exec or fork1 here. if an mt process were
+ * to exec/fork1 while we're doing this they'd end up with an
+ * extra open desciptor in their fd space. a'well. shouldn't
+ * really matter.
+ */
+ if ((fd = open(lx_debug_path,
+ O_WRONLY|O_APPEND|O_CREAT|O_NDELAY|O_NOCTTY, 0666)) == -1) {
+ return;
+ }
+ (void) fchmod(fd, 0666);
+
+ /* we retry in case of EINTR */
+ do {
+ rv = write(fd, buf, strlen(buf));
+ } while ((rv == -1) && (errno == EINTR));
+ (void) fsync(fd);
+
+ (void) close(fd);
+ errno = errno_backup;
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/dir.c b/usr/src/lib/brand/lx/lx_brand/common/dir.c
new file mode 100644
index 0000000000..ed10dfb822
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/dir.c
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <string.h>
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/dirent.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_syscall.h>
+
+#define LX_NAMEMAX 256
+
+struct lx_old_dirent {
+ long d_ino; /* not l_ino_t */
+ long d_off;
+ ushort_t d_reclen;
+ char d_name[LX_NAMEMAX];
+};
+
+/*
+ * Read in one dirent structure from fd into dirp.
+ * p3 (count) is ignored.
+ */
+/*ARGSUSED*/
+long
+lx_readdir(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ int fd = (int)p1;
+ struct lx_old_dirent *dirp = (struct lx_old_dirent *)p2;
+ uint_t count = sizeof (struct lx_old_dirent);
+ int rc = 0;
+ struct lx_old_dirent _ld;
+ struct dirent *sd = (struct dirent *)&_ld;
+
+ /*
+ * The return value from getdents is not applicable, as
+ * it might have squeezed more than one dirent in the buffer
+ * we provided.
+ *
+ * getdents() will deal with the case of dirp == NULL
+ */
+ if ((rc = getdents(fd, sd, count)) < 0)
+ return (-errno);
+
+ /*
+ * Set rc 1 (pass), or 0 (end of directory).
+ */
+ rc = (sd->d_reclen == 0) ? 0 : 1;
+
+ if (uucopy(sd, dirp, count) != 0)
+ return (-errno);
+
+ return (rc);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/file.c b/usr/src/lib/brand/lx/lx_brand/common/file.c
new file mode 100644
index 0000000000..a1f55e2899
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/file.c
@@ -0,0 +1,347 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/fstyp.h>
+#include <sys/fsid.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <fcntl.h>
+#include <string.h>
+#include <utime.h>
+#include <atomic.h>
+#include <sys/syscall.h>
+
+#include <sys/lx_syscall.h>
+#include <sys/lx_types.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_fcntl.h>
+
+#define LX_UTIME_NOW ((1l << 30) - 1l)
+#define LX_UTIME_OMIT ((1l << 30) - 2l)
+
+static int
+install_checkpath(uintptr_t p1)
+{
+ int saved_errno = errno;
+ char path[MAXPATHLEN];
+
+ /*
+ * The "dev" RPM package wants to modify /dev/pts, but /dev/pts is a
+ * lofs mounted copy of /native/dev/pts, so that won't work.
+ *
+ * Instead, if we're trying to modify /dev/pts from install mode, just
+ * act as if it succeded.
+ */
+ if (uucopystr((void *)p1, path, MAXPATHLEN) == -1)
+ return (-errno);
+
+ if (strcmp(path, "/dev/pts") == 0)
+ return (0);
+
+ errno = saved_errno;
+ return (-errno);
+}
+
+/*
+ * Miscellaneous file-related system calls.
+ */
+
+/*
+ * fsync() and fdatasync() - On Illumos, these calls translate into a common
+ * fdsync() syscall with a different parameter. fsync is handled in the
+ * fsync wrapper.
+ */
+long
+lx_fsync(uintptr_t fd)
+{
+ int fildes = (int)fd;
+ struct stat64 statbuf;
+
+ if ((fstat64(fildes, &statbuf) == 0) &&
+ (S_ISCHR(statbuf.st_mode) || S_ISFIFO(statbuf.st_mode)))
+ return (-EINVAL);
+
+ return (fsync((int)fd) ? -errno : 0);
+}
+
+long
+lx_fdatasync(uintptr_t fd)
+{
+ int fildes = (int)fd;
+ struct stat64 statbuf;
+
+ if ((fstat64(fildes, &statbuf) == 0) &&
+ (S_ISCHR(statbuf.st_mode) || S_ISFIFO(statbuf.st_mode)))
+ return (-EINVAL);
+
+ return (fdatasync((int)fd) ? -errno : 0);
+}
+
+long
+lx_utime(uintptr_t p1, uintptr_t p2)
+{
+ int ret;
+
+ ret = utime((const char *)p1, (const struct utimbuf *)p2);
+
+ if (ret < 0) {
+ /*
+ * If utime() failed and we're in install mode, return success
+ * if the the reason we failed was because the source file
+ * didn't actually exist or if we're trying to modify /dev/pts.
+ */
+ if ((lx_install != 0) &&
+ ((errno == ENOENT) || (install_checkpath(p1) == 0)))
+ return (0);
+
+ return (-errno);
+ }
+
+ return (0);
+}
+
+long
+lx_rmdir(uintptr_t p1)
+{
+ int r;
+ char *nm = (char *)p1;
+
+ r = rmdir(nm);
+ if (r < 0) {
+ int terr = errno;
+
+ /*
+ * On both Illumos and Linux rmdir returns EINVAL if the last
+ * component of the path is '.', but on Illumos we also return
+ * this errno if we're trying to remove the CWD. Unfortunately,
+ * at least the LTP test suite assumes that it can rmdir the
+ * CWD, so we need handle this. We try to get out of the
+ * directory we're trying to remove.
+ */
+ if (terr == EINVAL) {
+ int l;
+
+ l = strlen(nm);
+ if (l >= 2 && !(nm[l - 2] == '/' && nm[l - 1] == '.')) {
+ if (chdir("..") == 0 && rmdir(nm) == 0) {
+ return (0);
+ }
+ }
+ }
+
+ return ((terr == EEXIST) ? -ENOTEMPTY : -terr);
+ }
+ return (0);
+}
+
+/*
+ * Exactly the same as Illumos' sysfs(2), except Linux numbers their fs indices
+ * starting at 0, and Illumos starts at 1.
+ */
+long
+lx_sysfs(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ int option = (int)p1;
+ int res;
+
+ /*
+ * Linux actually doesn't have #defines for these; their sysfs(2)
+ * man page literally defines the "option" field as being 1, 2 or 3,
+ * corresponding to Solaris' GETFSIND, GETFSTYP and GETNFSTYP,
+ * respectively.
+ */
+ switch (option) {
+ case 1:
+ if ((res = sysfs(GETFSIND, (const char *)p2)) < 0)
+ return (-errno);
+
+ return (res - 1);
+
+ case 2:
+ if ((res = sysfs(GETFSTYP, (int)p2 + 1,
+ (char *)p3)) < 0)
+ return (-errno);
+
+ return (0);
+
+ case 3:
+ if ((res = sysfs(GETNFSTYP)) < 0)
+ return (-errno);
+
+ return (res);
+
+ default:
+ break;
+ }
+
+ return (-EINVAL);
+}
+
+long
+lx_futimesat(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ int atfd = (int)p1;
+ char *path = (char *)p2;
+ struct timeval *times = (struct timeval *)p3;
+
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ return (futimesat(atfd, path, times) ? -errno : 0);
+}
+
+/*
+ * From the utimensat man page:
+ * On Linux, futimens() is a library function implemented on top of the
+ * utimensat() system call. To support this, the Linux utimensat() system
+ * call implements a nonstandard feature: if pathname is NULL, then the
+ * call modifies the timestamps of the file referred to by the file
+ * descriptor dirfd (which may refer to any type of file). Using this
+ * feature, the call futimens(fd, times) is implemented as:
+ *
+ * utimensat(fd, NULL, times, 0);
+ *
+ * Some of the returns fail here. Linux allows the time to be modified if:
+ *
+ * the caller must have write access to the file
+ * or
+ * the caller's effective user ID must match the owner of the file
+ * or
+ * the caller must have appropriate privileges
+ *
+ * We behave differently. We fail with EPERM if:
+ *
+ * the calling process's euid has write access to the file but does not match
+ * the owner of the file and the calling process does not have the
+ * appropriate privileges
+ *
+ * This causes some of the LTP utimensat tests to fail because they expect an
+ * unprivileged process can update the time on a file it can write but does not
+ * own. There are also other LTP failures when the test uses attributes
+ * (e.g. chattr a+) and expects a failure, but we succeed.
+ */
+long
+lx_utimensat(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+ int fd = (int)p1;
+ const char *path = (const char *)p2;
+ const timespec_t *times = (const timespec_t *)p3;
+ timespec_t ts[2];
+ int flag = (int)p4;
+
+ if (times != NULL) {
+ if (uucopy((void *)p3, ts, sizeof (ts)) == -1)
+ return (-errno);
+
+ if (ts[0].tv_nsec == LX_UTIME_NOW)
+ ts[0].tv_nsec = UTIME_NOW;
+ if (ts[1].tv_nsec == LX_UTIME_NOW)
+ ts[1].tv_nsec = UTIME_NOW;
+
+ if (ts[0].tv_nsec == LX_UTIME_OMIT)
+ ts[0].tv_nsec = UTIME_OMIT;
+ if (ts[1].tv_nsec == LX_UTIME_OMIT)
+ ts[1].tv_nsec = UTIME_OMIT;
+
+ times = (const timespec_t *)ts;
+ }
+
+ if (flag == LX_AT_SYMLINK_NOFOLLOW)
+ flag = AT_SYMLINK_NOFOLLOW;
+
+ if (fd == LX_AT_FDCWD)
+ fd = AT_FDCWD;
+
+ if (path == NULL) {
+ return (futimens(fd, times) ? -errno : 0);
+ } else {
+ return (utimensat(fd, path, times, flag) ? -errno : 0);
+ }
+}
+
+/*
+ * Constructs an absolute path string in buf from the path of fd and the
+ * relative path string pointed to by "p1". This is required for emulating
+ * *at() system calls.
+ * Example:
+ * If the path of fd is "/foo/bar" and path is "etc" the string returned is
+ * "/foo/bar/etc", if the fd is a file fd then it fails with ENOTDIR.
+ * If path is absolute then no modifcations are made to it when copied.
+ */
+static int
+getpathat(int fd, uintptr_t p1, char *outbuf, size_t outbuf_size)
+{
+ char pathbuf[MAXPATHLEN];
+ char fdpathbuf[MAXPATHLEN];
+ char *fdpath;
+ struct stat64 statbuf;
+
+ if (uucopystr((void *)p1, pathbuf, MAXPATHLEN) == -1)
+ return (-errno);
+
+ /* If the path is absolute then we can early out */
+ if ((pathbuf[0] == '/') || (fd == LX_AT_FDCWD)) {
+ (void) strlcpy(outbuf, pathbuf, outbuf_size);
+ return (0);
+ }
+
+ fdpath = lx_fd_to_path(fd, fdpathbuf, sizeof (fdpathbuf));
+ if (fdpath == NULL)
+ return (-EBADF);
+
+ if ((fstat64(fd, &statbuf) < 0))
+ return (-EBADF);
+
+ if (!S_ISDIR(statbuf.st_mode))
+ return (-ENOTDIR);
+
+ if (snprintf(outbuf, outbuf_size, "%s/%s", fdpath, pathbuf) >
+ (outbuf_size-1))
+ return (-ENAMETOOLONG);
+
+ return (0);
+}
+
+long
+lx_mknodat(uintptr_t ext1, uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ int atfd = (int)ext1;
+ char pathbuf[MAXPATHLEN];
+ int ret;
+
+ ret = getpathat(atfd, p1, pathbuf, sizeof (pathbuf));
+ if (ret < 0)
+ return (ret);
+
+ return (lx_mknod((uintptr_t)pathbuf, p2, p3));
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/fork.c b/usr/src/lib/brand/lx/lx_brand/common/fork.c
new file mode 100644
index 0000000000..beb76fcf59
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/fork.c
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ * Copyright 2017 Joyent, Inc.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/fork.h>
+#include <sys/syscall.h>
+#include <sys/debug.h>
+#include <strings.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+/*
+ * fork() and vfork()
+ *
+ * These cannot be pass thru system calls because we need libc to do its own
+ * initialization or else bad things will happen (i.e. ending up with a bad
+ * schedctl page). On Linux, there is no such thing as forkall(), so we use
+ * fork1() here.
+ */
+
+long
+lx_fork(void)
+{
+ int ret;
+
+ /*
+ * Inform the in-kernel ptrace(2) subsystem that we are about to
+ * emulate fork(2).
+ */
+ lx_ptrace_clone_begin(LX_PTRACE_O_TRACEFORK, B_FALSE, 0);
+
+ /*
+ * Suspend signal delivery, run the stack management prefork handler
+ * and perform the fork operation.
+ */
+ _sigoff();
+ lx_stack_prefork();
+ ret = fork1();
+ lx_stack_postfork();
+
+ switch (ret) {
+ case -1:
+ _sigon();
+ return (-errno);
+
+ case 0:
+ /*
+ * Returning in the new child. We must free the stacks and
+ * thread-specific data objects for the threads we did not
+ * duplicate; i.e. every other thread.
+ */
+ lx_free_other_stacks();
+
+ lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEFORK, B_TRUE, 0,
+ NULL);
+
+ /*
+ * Re-enable signal delivery in the child and return to the
+ * new process.
+ */
+ _sigon();
+ return (0);
+
+ default:
+ lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEFORK, B_FALSE,
+ (ulong_t)ret, NULL);
+
+ /*
+ * Re-enable signal delivery in the parent and return from
+ * the emulated system call.
+ */
+ _sigon();
+ return (ret);
+ }
+}
+
+long
+lx_vfork(void)
+{
+ int ret;
+ lx_sighandlers_t saved;
+ ucontext_t vforkuc;
+ ucontext_t *ucp;
+ lx_tsd_t *lx_tsd = lx_get_tsd();
+
+ ucp = lx_syscall_regs();
+
+ /*
+ * Inform the in-kernel ptrace(2) subsystem that we are about to
+ * emulate vfork(2).
+ */
+ lx_ptrace_clone_begin(LX_PTRACE_O_TRACEVFORK, B_FALSE, 0);
+
+ /*
+ * Suspend signal delivery, run the stack management prefork handler
+ * and perform the vfork operation. We use the same approach as in
+ * lx_clone for signal handling and child return across vfork. See
+ * the comments in lx_clone for more detail.
+ */
+
+ lx_block_all_signals();
+ lx_stack_prefork();
+ lx_sighandlers_save(&saved);
+ lx_tsd->lxtsd_is_vforked++;
+ ret = vfork();
+ if (ret != 0) {
+ /* parent/error */
+ lx_tsd->lxtsd_is_vforked--;
+ lx_sighandlers_restore(&saved);
+ }
+
+ switch (ret) {
+ case -1:
+ lx_stack_postfork();
+ lx_unblock_all_signals();
+ return (-errno);
+
+ case 0:
+ /*
+ * child
+ * Unlike the regular fork case where the child also calls
+ * lx_stack_postfork(), we only do that in the parent once it
+ * resumes execution. This is required there to wake any other
+ * threads in that process that are blocked on the lock taken
+ * in lx_stack_prefork().
+ */
+ bcopy(ucp, &vforkuc, sizeof (vforkuc));
+ vforkuc.uc_brand_data[1] = (caddr_t)vforkuc.uc_brand_data[1] -
+ LX_NATIVE_STACK_VFORK_GAP;
+ vforkuc.uc_link = NULL;
+
+ lx_debug("\tvfork native stack sp %p",
+ vforkuc.uc_brand_data[1]);
+
+ lx_unblock_all_signals();
+
+ /* Stop for ptrace if required. */
+ lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEVFORK, B_TRUE, 0,
+ NULL);
+
+ /*
+ * Return to the child via the specially constructed vfork(2)
+ * context.
+ */
+ LX_EMULATE_RETURN(&vforkuc, LX_SYS_vfork, 0, 0);
+ (void) syscall(SYS_brand, B_EMULATION_DONE, &vforkuc,
+ LX_SYS_vfork, 0, 0);
+
+ VERIFY(0);
+ return (0);
+
+ default:
+ /* parent - child should have exited or exec-ed by now */
+ lx_stack_postfork();
+ lx_unblock_all_signals();
+ lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEVFORK, B_FALSE,
+ (ulong_t)ret, NULL);
+ return (ret);
+ }
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
new file mode 100644
index 0000000000..7373a0d02f
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
@@ -0,0 +1,1709 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/utsname.h>
+#include <sys/inttypes.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/fstyp.h>
+#include <sys/fsid.h>
+#include <sys/systm.h>
+#include <sys/auxv.h>
+#include <sys/frame.h>
+#include <zone.h>
+#include <sys/brand.h>
+#include <sys/epoll.h>
+#include <sys/stack.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <synch.h>
+#include <libelf.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <utime.h>
+#include <dirent.h>
+#include <ucontext.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/lx_statfs.h>
+#include <sys/lx_signal.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_thread.h>
+#include <lx_auxv.h>
+#include <sys/lx_userhz.h>
+
+/*
+ * There is a block comment in "uts/common/brand/lx/os/lx_brand.c" that
+ * describes the functioning of the LX brand in some detail.
+ *
+ * *** Setting errno
+ *
+ * This emulation library is loaded onto a seperate link map from the
+ * application whose address space we're running in. The Linux libc errno is
+ * independent of our native libc errno. To pass back an error the emulation
+ * function should return -errno back to the Linux caller.
+ */
+
+char lx_release[LX_KERN_RELEASE_MAX];
+char lx_cmd_name[MAXNAMLEN];
+boolean_t lx_no_abort_handler = B_FALSE;
+
+/*
+ * Map a linux locale ending string to the solaris equivalent.
+ */
+struct lx_locale_ending {
+ const char *linux_end; /* linux ending string */
+ const char *solaris_end; /* to transform with this string */
+ int le_size; /* linux ending string length */
+ int se_size; /* solaris ending string length */
+};
+
+#define l2s_locale(lname, sname) \
+ {(lname), (sname), sizeof ((lname)) - 1, sizeof ((sname)) - 1}
+
+#define MAXLOCALENAMELEN 30
+#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
+#endif
+
+/*
+ * Most syscalls return an int but some return something else, typically a
+ * ssize_t. This can be either an int or a long, depending on if we're compiled
+ * for 32-bit or 64-bit. To correctly propagate the -errno return code in the
+ * 64-bit case, we declare all emulation wrappers will return a long. Thus,
+ * when we save the return value into the %eax or %rax register and return to
+ * Linux, we will have the right size value in both the 32 and 64 bit cases.
+ */
+
+typedef long (*lx_syscall_handler_t)();
+
+static lx_syscall_handler_t lx_handlers[LX_NSYSCALLS + 1];
+
+static uintptr_t stack_size;
+
+#if defined(_LP64)
+long lx_fsb;
+long lx_fs;
+#endif
+int lx_install = 0; /* install mode enabled if non-zero */
+int lx_verbose = 0; /* verbose mode enabled if non-zero */
+int lx_debug_enabled = 0; /* debugging output enabled if non-zero */
+
+pid_t zoneinit_pid; /* zone init PID */
+
+thread_key_t lx_tsd_key;
+
+uint_t lx_hz_scale; /* USER_HZ scaling factor */
+
+int
+uucopy_unsafe(const void *src, void *dst, size_t n)
+{
+ bcopy(src, dst, n);
+ return (0);
+}
+
+int
+uucopystr_unsafe(const void *src, void *dst, size_t n)
+{
+ (void) strncpy((char *)src, dst, n);
+ return (0);
+}
+
+static void
+i_lx_msg(int fd, char *msg, va_list ap)
+{
+ int i;
+ char buf[LX_MSG_MAXLEN];
+
+ /* LINTED [possible expansion issues] */
+ i = vsnprintf(buf, sizeof (buf), msg, ap);
+ buf[LX_MSG_MAXLEN - 1] = '\0';
+ if (i == -1)
+ return;
+
+ /* if debugging is enabled, send this message to debug output */
+ if (LX_DEBUG_ISENABLED)
+ lx_debug(buf);
+
+ if (fd == 2) {
+ /*
+ * We let the user choose whether or not to see these
+ * messages on the console.
+ */
+ if (lx_verbose == 0)
+ return;
+ }
+
+ /* we retry in case of EINTR */
+ do {
+ i = write(fd, buf, strlen(buf));
+ } while ((i == -1) && (errno == EINTR));
+}
+
+/*PRINTFLIKE1*/
+void
+lx_err(char *msg, ...)
+{
+ va_list ap;
+
+ assert(msg != NULL);
+
+ va_start(ap, msg);
+ i_lx_msg(STDERR_FILENO, msg, ap);
+ va_end(ap);
+}
+
+/*
+ * This is just a non-zero exit value which also isn't one that would allow
+ * us to easily detect if a branded process exited because of a recursive
+ * fatal error.
+ */
+#define LX_ERR_FATAL 42
+
+/*
+ * Our own custom version of abort(), this routine will be used in place
+ * of the one located in libc. The primary difference is that this version
+ * will first reset the signal handler for SIGABRT to SIG_DFL, ensuring the
+ * SIGABRT sent causes us to dump core and is not caught by a user program.
+ */
+void
+abort(void)
+{
+ static int aborting = 0;
+
+ struct sigaction sa;
+ sigset_t sigmask;
+
+ /* watch out for recursive calls to this function */
+ if (aborting != 0)
+ exit(LX_ERR_FATAL);
+
+ aborting = 1;
+
+ /*
+ * Block all signals here to avoid taking any signals while exiting
+ * in an effort to avoid any strange user interaction with our death.
+ */
+ (void) sigfillset(&sigmask);
+ (void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
+
+ /*
+ * Our own version of abort(3C) that we know will never call
+ * a user-installed SIGABRT handler first. We WANT to die.
+ *
+ * Do this by resetting the handler to SIG_DFL, and releasing any
+ * held SIGABRTs.
+ *
+ * If no SIGABRTs are pending, send ourselves one.
+ *
+ * The while loop is a bit of overkill, but abort(3C) does it to
+ * assure it never returns so we will as well.
+ */
+ (void) sigemptyset(&sa.sa_mask);
+ sa.sa_sigaction = SIG_DFL;
+ sa.sa_flags = 0;
+
+ for (;;) {
+ (void) sigaction(SIGABRT, &sa, NULL);
+ (void) sigrelse(SIGABRT);
+ (void) thr_kill(thr_self(), SIGABRT);
+ }
+
+ /*NOTREACHED*/
+}
+
+/*PRINTFLIKE1*/
+void
+lx_msg(char *msg, ...)
+{
+ va_list ap;
+
+ assert(msg != NULL);
+ va_start(ap, msg);
+ i_lx_msg(STDOUT_FILENO, msg, ap);
+ va_end(ap);
+}
+
+/*PRINTFLIKE1*/
+void
+lx_err_fatal(char *msg, ...)
+{
+ va_list ap;
+
+ assert(msg != NULL);
+
+ va_start(ap, msg);
+ i_lx_msg(STDERR_FILENO, msg, ap);
+ va_end(ap);
+ abort();
+}
+
+/*
+ * See if it is safe to alloca() sz bytes. Return 1 for yes, 0 for no.
+ * We can't be certain we won't blow the stack since we don't know where it
+ * starts, but since the stack is only two pages we know any allocation bigger
+ * than that will blow the stack. Fortunately most allocations are small (e.g.
+ * 128 bytes).
+ */
+int
+lx_check_alloca(size_t sz)
+{
+ uintptr_t sp = (uintptr_t)&sz;
+ uintptr_t end = sp - sz;
+
+ return ((end < sp) && (sz < stack_size));
+}
+
+/*PRINTFLIKE1*/
+void
+lx_unsupported(char *msg, ...)
+{
+ va_list ap;
+ char dmsg[256];
+ int lastc;
+
+ assert(msg != NULL);
+
+ /* make a brand call so we can easily dtrace unsupported actions */
+ va_start(ap, msg);
+ (void) vsnprintf(dmsg, sizeof (dmsg), msg, ap);
+ dmsg[255] = '\0';
+ lastc = strlen(dmsg) - 1;
+ if (dmsg[lastc] == '\n')
+ dmsg[lastc] = '\0';
+ (void) syscall(SYS_brand, B_UNSUPPORTED, dmsg);
+ va_end(ap);
+
+ /* send the msg to the error stream */
+ va_start(ap, msg);
+ i_lx_msg(STDERR_FILENO, msg, ap);
+ va_end(ap);
+}
+
+int lx_init(int argc, char *argv[], char *envp[]);
+
+lx_tsd_t *
+lx_get_tsd(void)
+{
+ int ret;
+ lx_tsd_t *lx_tsd;
+
+ if ((ret = thr_getspecific(lx_tsd_key, (void **)&lx_tsd)) != 0) {
+ lx_err_fatal("lx_get_tsd: unable to read "
+ "thread-specific data: %s", strerror(ret));
+ }
+
+ assert(lx_tsd != 0);
+
+ return (lx_tsd);
+}
+
+/*
+ * This function is called from the kernel like a signal handler. Each
+ * function call is a request to provide emulation for a system call that, on
+ * illumos, is implemented in userland. The system call number selection and
+ * argument parsing have already been done by the kernel.
+ */
+void
+lx_emulate(ucontext_t *ucp, int syscall_num, uintptr_t *args)
+{
+ long emu_ret;
+ int emu_errno = 0;
+
+ LX_EMULATE_ENTER(ucp, syscall_num, args);
+ lx_debug("lx_emulate(%p, %d, [%p, %p, %p, %p, %p, %p])\n",
+ ucp, syscall_num, args[0], args[1], args[2], args[3], args[4],
+ args[5]);
+
+ /*
+ * The kernel should have saved us a context that will not restore the
+ * previous signal mask. Some emulated system calls alter the signal
+ * mask; restoring it after the emulation would cancel that out.
+ */
+ assert(!(ucp->uc_flags & UC_SIGMASK));
+
+ /*
+ * The kernel ensures that the syscall_num is sane; Use it as is.
+ */
+ assert(syscall_num >= 0);
+ assert(syscall_num < (sizeof (lx_handlers) / sizeof (lx_handlers[0])));
+ if (lx_handlers[syscall_num] == NULL) {
+ lx_err_fatal("lx_emulate: kernel sent us a call we cannot "
+ "emulate (%d)", syscall_num);
+ }
+
+ /*
+ * Call our handler function:
+ */
+ emu_ret = lx_handlers[syscall_num](args[0], args[1], args[2], args[3],
+ args[4], args[5]);
+
+ /*
+ * If the return value is between -1 and -4095 then it's an errno.
+ * The kernel will translate it to the Linux equivalent for us.
+ */
+ if (emu_ret < 0 && emu_ret > -4096) {
+ emu_errno = (int)-emu_ret;
+ }
+
+ /*
+ * Return to the context we were passed
+ */
+ LX_EMULATE_RETURN(ucp, syscall_num, emu_ret, emu_errno);
+ lx_debug("\tlx_emulate(%d) done (ret %ld / 0x%p ; errno %d)",
+ syscall_num, emu_ret, emu_ret, emu_errno);
+ (void) syscall(SYS_brand, B_EMULATION_DONE, ucp, syscall_num, emu_ret,
+ emu_errno);
+
+ assert(!"cannot be returned here");
+}
+
+static void
+lx_close_fh(FILE *file)
+{
+ int fd, fd_new;
+
+ if (file == NULL)
+ return;
+
+ if ((fd = fileno(file)) < 0)
+ return;
+
+ fd_new = dup(fd);
+ if (fd_new == -1)
+ return;
+
+ (void) fclose(file);
+ (void) dup2(fd_new, fd);
+ (void) close(fd_new);
+}
+
+
+extern int set_l10n_alternate_root(char *path);
+
+/*
+ * Initialize the thread specific data for this thread.
+ */
+void
+lx_init_tsd(lx_tsd_t *lxtsd)
+{
+ int err;
+
+ bzero(lxtsd, sizeof (*lxtsd));
+ lxtsd->lxtsd_exit = LX_ET_NONE;
+
+ /*
+ * The Linux alternate signal stack is initially disabled:
+ */
+ lxtsd->lxtsd_sigaltstack.ss_flags = LX_SS_DISABLE;
+
+ /*
+ * Create a per-thread exit context from the current register and
+ * native/brand stack state. Replace the saved program counter value
+ * with the address of lx_exit_common(); we wish to revector there when
+ * the thread or process is exiting.
+ */
+ if (getcontext(&lxtsd->lxtsd_exit_context) != 0) {
+ lx_err_fatal("Unable to initialize thread-specific exit "
+ "context: %s", strerror(errno));
+ }
+ LX_REG(&lxtsd->lxtsd_exit_context, REG_PC) = (uintptr_t)lx_exit_common;
+
+ /*
+ * Align the stack pointer and clear the frame pointer.
+ */
+ LX_REG(&lxtsd->lxtsd_exit_context, REG_FP) = 0;
+ LX_REG(&lxtsd->lxtsd_exit_context, REG_SP) &= ~(STACK_ALIGN - 1UL);
+#if defined(_LP64)
+#if (STACK_ENTRY_ALIGN != 8) && (STACK_ALIGN != 16)
+#error "lx_init_tsd: unexpected STACK_[ENTRY_]ALIGN values"
+#endif
+ /*
+ * The AMD64 ABI requires that, on entry to a function, the stack
+ * pointer must be 8-byte aligned, but _not_ 16-byte aligned. When
+ * the frame pointer is pushed, the alignment will then be correct.
+ */
+ LX_REG(&lxtsd->lxtsd_exit_context, REG_SP) -= STACK_ENTRY_ALIGN;
+#endif
+
+ /*
+ * Block all signals in the exit context to avoid taking any signals
+ * (to the degree possible) while exiting.
+ */
+ (void) sigfillset(&lxtsd->lxtsd_exit_context.uc_sigmask);
+
+ if ((err = thr_setspecific(lx_tsd_key, lxtsd)) != 0) {
+ lx_err_fatal("Unable to initialize thread-specific data: %s",
+ strerror(err));
+ }
+}
+
+void
+lx_jump_to_linux(ucontext_t *ucp)
+{
+ extern void setcontext_sigmask(ucontext_t *);
+
+ /*
+ * Call into this private libc interface to allow us to use only the
+ * signal mask handling part of a regular setcontext() operation.
+ */
+ setcontext_sigmask(ucp);
+
+ if (syscall(SYS_brand, B_JUMP_TO_LINUX, ucp) != 0) {
+ lx_err_fatal("B_JUMP_TO_LINUX failed: %s", strerror(errno));
+ }
+
+ /*
+ * This system call should not return.
+ */
+ abort();
+}
+
+static void
+lx_start(uintptr_t sp, uintptr_t entry)
+{
+ ucontext_t jump_uc;
+
+ if (getcontext(&jump_uc) != 0) {
+ lx_err_fatal("Unable to getcontext for program start: %s",
+ strerror(errno));
+ }
+
+ /*
+ * We want to load the general registers from this
+ * context, and switch to the BRAND stack.
+ */
+ jump_uc.uc_flags = UC_CPU;
+ jump_uc.uc_brand_data[0] = (void *)LX_UC_STACK_BRAND;
+
+ LX_REG(&jump_uc, REG_FP) = (uintptr_t)NULL;
+ LX_REG(&jump_uc, REG_SP) = sp;
+ LX_REG(&jump_uc, REG_PC) = entry;
+
+ /*
+ * The AMD64 ABI states that at process entry, %rdx contains "a
+ * function pointer that the application should register with
+ * atexit()". This behavior has been observed in statically linked
+ * i386 programs as well. As a precaution, all of the registers are
+ * zeroed prior to initial execution.
+ */
+#if defined(_LP64)
+ LX_REG(&jump_uc, REG_RAX) = 0;
+ LX_REG(&jump_uc, REG_RCX) = 0;
+ LX_REG(&jump_uc, REG_RDX) = 0;
+ LX_REG(&jump_uc, REG_RBX) = 0;
+ LX_REG(&jump_uc, REG_RBP) = 0;
+ LX_REG(&jump_uc, REG_RSI) = 0;
+ LX_REG(&jump_uc, REG_RDI) = 0;
+ LX_REG(&jump_uc, REG_R8) = 0;
+ LX_REG(&jump_uc, REG_R9) = 0;
+ LX_REG(&jump_uc, REG_R10) = 0;
+ LX_REG(&jump_uc, REG_R11) = 0;
+ LX_REG(&jump_uc, REG_R12) = 0;
+ LX_REG(&jump_uc, REG_R13) = 0;
+ LX_REG(&jump_uc, REG_R14) = 0;
+ LX_REG(&jump_uc, REG_R15) = 0;
+#else
+ LX_REG(&jump_uc, EAX) = 0;
+ LX_REG(&jump_uc, ECX) = 0;
+ LX_REG(&jump_uc, EDX) = 0;
+ LX_REG(&jump_uc, EBX) = 0;
+ LX_REG(&jump_uc, EBP) = 0;
+ LX_REG(&jump_uc, ESI) = 0;
+ LX_REG(&jump_uc, EDI) = 0;
+#endif /* defined(_LP64) */
+
+ lx_debug("starting Linux program sp %p ldentry %p", sp, entry);
+ lx_jump_to_linux(&jump_uc);
+}
+
+enum lx_env_setting {
+ LXES_INSTALL = 0,
+ LXES_VERBOSE,
+ LXES_DTRACE,
+ LXES_DEBUG,
+ LXES_DEBUG_FILE,
+ LXES_NO_ABORT_HANDLER,
+ LXES_RELEASE,
+ LXES_VERSION,
+ LXES_STRICT,
+ LXES_LIMIT
+};
+
+static void
+lx_parse_env(char *envp[], char *settings[])
+{
+ int i, j;
+ char *env;
+
+ typedef struct lx_env_entry {
+ char *lee_name;
+ int lee_len;
+ int lee_index;
+ } lx_env_entry_t;
+#define LX_ENV_ENTRY(name, idx) { name, (sizeof (name)) - 1, idx }
+ static const lx_env_entry_t lx_env_entries[] = {
+ LX_ENV_ENTRY("LX_INSTALL", LXES_INSTALL),
+ LX_ENV_ENTRY("LX_VERBOSE", LXES_VERBOSE),
+ LX_ENV_ENTRY("LX_DTRACE", LXES_DTRACE),
+ LX_ENV_ENTRY("LX_DEBUG", LXES_DEBUG),
+ LX_ENV_ENTRY("LX_DEBUG_FILE", LXES_DEBUG_FILE),
+ LX_ENV_ENTRY("LX_NO_ABORT_HANDLER", LXES_NO_ABORT_HANDLER),
+ LX_ENV_ENTRY("LX_RELEASE", LXES_RELEASE),
+ LX_ENV_ENTRY("LX_VERSION", LXES_VERSION),
+ LX_ENV_ENTRY("LX_STRICT", LXES_STRICT)
+ };
+#define LX_ENV_ENTRY_COUNT \
+ (sizeof (lx_env_entries) / sizeof (lx_env_entries[0]))
+
+ for (i = 0; (env = envp[i]) != NULL; i++) {
+ if (env[0] != 'L' || env[1] != 'X' || env[2] != '_')
+ continue;
+ for (j = 0; j < LX_ENV_ENTRY_COUNT; j++) {
+ const lx_env_entry_t *lee = &lx_env_entries[j];
+
+ if (strncmp(env, lee->lee_name, lee->lee_len) != 0 ||
+ env[lee->lee_len] != '=')
+ continue;
+ settings[lee->lee_index] = &env[lee->lee_len + 1];
+ break;
+ }
+ }
+}
+
+/*ARGSUSED*/
+int
+lx_init(int argc, char *argv[], char *envp[])
+{
+ auxv_t *ap, *oap;
+ long *p;
+ int err;
+ lx_elf_data_t edp;
+ lx_brand_registration_t reg;
+ lx_tsd_t *lxtsd;
+ char *lx_settings[LXES_LIMIT];
+
+ bzero(&reg, sizeof (reg));
+ stack_size = 2 * sysconf(_SC_PAGESIZE);
+
+ lx_hz_scale = sysconf(_SC_CLK_TCK) / LX_USERHZ;
+
+ /*
+ * We need to shutdown all libc stdio. libc stdio normally goes to
+ * file descriptors, but since we're actually part of a linux
+ * process we don't own these file descriptors and we can't make
+ * any assumptions about their state.
+ */
+ lx_close_fh(stdin);
+ lx_close_fh(stdout);
+ lx_close_fh(stderr);
+
+ /*
+ * Parse LX-related settings out of the environment array.
+ * This is done manually instead of utilizing libc's getenv() to avoid
+ * triggering any env-cleaning routines which are present.
+ */
+ bzero(lx_settings, sizeof (lx_settings));
+ lx_parse_env(envp, lx_settings);
+
+ /*
+ * Setting LX_NO_ABORT_HANDLER in the environment will prevent the
+ * emulated Linux program from modifying the signal handling
+ * disposition for SIGSEGV or SIGABRT. It is useful for debugging
+ * programs which fall over themselves to prevent useful core files
+ * being generated.
+ */
+ lx_no_abort_handler = (lx_settings[LXES_NO_ABORT_HANDLER] != NULL);
+
+ lx_debug_init(lx_settings[LXES_DTRACE] != NULL,
+ lx_settings[LXES_DEBUG] != NULL,
+ lx_settings[LXES_DEBUG_FILE]);
+
+ if (lx_settings[LXES_RELEASE] == NULL) {
+ if (zone_getattr(getzoneid(), LX_ATTR_KERN_RELEASE,
+ lx_release, sizeof (lx_release)) <= 0)
+ (void) strlcpy(lx_release, "2.4.21",
+ LX_KERN_RELEASE_MAX);
+ } else {
+ (void) strlcpy(lx_release, lx_settings[LXES_RELEASE],
+ LX_KERN_RELEASE_MAX);
+ }
+
+ if (lx_settings[LXES_RELEASE] != NULL ||
+ lx_settings[LXES_VERSION] != NULL) {
+ if (syscall(SYS_brand, B_OVERRIDE_KERN_VER,
+ lx_settings[LXES_RELEASE],
+ lx_settings[LXES_VERSION]) != 0) {
+ lx_debug("failed to override kernel release/version");
+ }
+ }
+ lx_debug("lx_release: %s\n", lx_release);
+
+
+ /*
+ * Should we kill an application that attempts an unimplemented
+ * system call?
+ */
+ if (lx_settings[LXES_STRICT] != NULL) {
+ reg.lxbr_flags |= LX_PROC_STRICT_MODE;
+ lx_debug("STRICT mode enabled.\n");
+ }
+
+ /*
+ * Are we in install mode?
+ */
+ if (lx_settings[LXES_INSTALL] != NULL) {
+ reg.lxbr_flags |= LX_PROC_INSTALL_MODE;
+ lx_install = 1;
+ lx_debug("INSTALL mode enabled.\n");
+ }
+
+ (void) strlcpy(lx_cmd_name, basename(argv[0]), sizeof (lx_cmd_name));
+ lx_debug("executing linux process: %s", argv[0]);
+ lx_debug("branding myself and setting handler to 0x%p",
+ (void *)lx_emulate);
+
+ reg.lxbr_version = LX_VERSION;
+ reg.lxbr_handler = (void *)&lx_emulate;
+
+ /*
+ * Register the address of the user-space handler with the lx brand
+ * module. As a side-effect this leaves the thread in native syscall
+ * mode so that it's ok to continue to make syscalls during setup. We
+ * need to switch to Linux mode at the end of initialization.
+ */
+ if (syscall(SYS_brand, B_REGISTER, &reg))
+ lx_err_fatal("failed to brand the process");
+
+ /* Look up the PID that serves as init for this zone */
+ if ((err = lx_lpid_to_spid(1, &zoneinit_pid)) < 0)
+ lx_err_fatal("Unable to find PID for zone init process: %s",
+ strerror(err));
+
+ /*
+ * Upload data about the lx executable from the kernel.
+ */
+ if (syscall(SYS_brand, B_ELFDATA, (void *)&edp))
+ lx_err_fatal("failed to get required ELF data from the kernel");
+
+ if (lx_statfs_init() != 0)
+ lx_err_fatal("failed to setup the statfs translator");
+
+ /*
+ * Find the aux vector on the stack.
+ */
+ p = (long *)envp;
+ while (*p != (uintptr_t)NULL)
+ p++;
+
+ /*
+ * Now 'p' points at the NULL word immediately following the environ
+ * pointers. The list of auxv entries _should_ immediately follow.
+ * If anything (such as the native linker or libc) has removed entries
+ * from the environment array, extra NULLs will be present.
+ *
+ * The brand library takes care to avoid such behavior (via the
+ * lx_parse_env routine above) but a belt-and-suspenders approach is
+ * taken for safety.
+ *
+ * The address following the NULL spacer is recorded as the target for
+ * auxv translation and any addition NULLs following it are skipped
+ * until the first auxv entry is located.
+ */
+ p++;
+ oap = (auxv_t *)p;
+ while (*p == (uintptr_t)NULL)
+ p++;
+ ap = (auxv_t *)p;
+
+ /*
+ * Translate auxv entries to Linux equivalents.
+ */
+ for (; ap->a_type != AT_NULL; ap++) {
+ if (lx_auxv_stol(ap, oap, &edp) == 0) {
+ /*
+ * Copy only auxv entries which Linux programs will
+ * understand. Other entries will be skipped.
+ */
+ oap++;
+ }
+ }
+
+ /* NULL out skipped entries */
+ if (oap < ap) {
+ bzero(oap, (uintptr_t)ap - (uintptr_t)oap);
+ }
+
+ /* Setup signal handler information. */
+ if (lx_siginit()) {
+ lx_err_fatal("failed to initialize lx signals for the "
+ "branded process");
+ }
+
+ /* Setup thread-specific data area for managing linux threads. */
+ if ((err = thr_keycreate(&lx_tsd_key, NULL)) != 0) {
+ lx_err_fatal("thr_keycreate(lx_tsd_key) failed: %s",
+ strerror(err));
+ }
+
+ lx_debug("thr_keycreate created lx_tsd_key (%d)", lx_tsd_key);
+
+ /*
+ * Initialize the thread specific data for this thread.
+ */
+ if ((lxtsd = malloc(sizeof (*lxtsd))) == NULL) {
+ lx_err_fatal("failed to allocate tsd for main thread: %s",
+ strerror(errno));
+ }
+ lx_debug("lx tsd allocated @ %p", lxtsd);
+ lx_init_tsd(lxtsd);
+
+ /*
+ * Allocate the brand emulation stack for the main process thread.
+ * Register the thread-specific data structure with the stack list so
+ * that it may be freed at thread exit or fork(2).
+ */
+ lx_install_stack(NULL, 0, lxtsd);
+
+ /*
+ * The brand linker expects the stack pointer to point to
+ * "argc", which is just before &argv[0].
+ */
+ lx_start((uintptr_t)argv - sizeof (void *), edp.ed_ldentry);
+
+ /*NOTREACHED*/
+ abort();
+ return (0);
+}
+
+/*
+ * We "return" to this function via a context hand-crafted by
+ * "lx_init_tsd()"; see that function for more detail.
+ *
+ * NOTE: Our call frame is on the main thread stack, not the alternate native
+ * stack -- it is safe to release the latter here. The frame does not have a
+ * valid return address, so this function MUST NOT return.
+ */
+void
+lx_exit_common(void)
+{
+ lx_tsd_t *lxtsd = lx_get_tsd();
+ int ev = (0xff & lxtsd->lxtsd_exit_status);
+
+ switch (lxtsd->lxtsd_exit) {
+ case LX_ET_EXIT:
+ lx_debug("lx_exit_common(LX_ET_EXIT, %d, %d)\n", thr_self(),
+ ev);
+
+ if (thr_self() == 1) {
+ /*
+ * Modern versions of glibc will call the exit_group
+ * syscall when exit(3) is called, but if the primary
+ * thread explicitly invokes the exit syscall we now
+ * need to exit with the proper value.
+ */
+ exit(ev);
+ } else {
+ /*
+ * If the thread is exiting, but not the entire process,
+ * we must free the stack we allocated for usermode
+ * emulation. This is safe to do here because the
+ * setcontext() put us back on the BRAND stack for this
+ * process. This function also frees the
+ * thread-specific data object for this thread.
+ */
+ lx_free_stack();
+
+ /*
+ * The native thread return value is never seen so we
+ * pass NULL.
+ */
+ thr_exit(NULL);
+ }
+ break;
+
+ case LX_ET_EXIT_GROUP:
+ lx_debug("lx_exit_common(LX_ET_EXIT_GROUP, %d)\n", ev);
+ exit(ev);
+ break;
+
+ default:
+ abort();
+ }
+
+ abort();
+}
+
+const ucontext_t *
+lx_find_brand_uc(void)
+{
+ ucontext_t *ucp = NULL;
+
+ /*
+ * Ask for the current emulation (or signal handling) ucontext_t...
+ */
+ assert(syscall(SYS_brand, B_GET_CURRENT_CONTEXT, &ucp) == 0);
+
+ for (;;) {
+ uintptr_t flags;
+
+ lx_debug("lx_find_brand_uc: inspect ucp %p...\n", ucp);
+ assert(ucp != NULL);
+
+ flags = (uintptr_t)ucp->uc_brand_data[0];
+
+ if (flags & LX_UC_STACK_BRAND) {
+ lx_debug("lx_find_brand_uc: ucp %p\n", ucp);
+
+ return (ucp);
+ }
+
+ lx_debug("lx_find_brand_uc: skip non-BRAND ucp %p\n", ucp);
+
+ /*
+ * Walk up the context chain to find the most recently stored
+ * brand register state.
+ */
+ ucp = ucp->uc_link;
+ }
+}
+
+uintptr_t
+lx_find_brand_sp(void)
+{
+ const ucontext_t *ucp = lx_find_brand_uc();
+ uintptr_t sp = LX_REG(ucp, REG_SP);
+
+ lx_debug("lx_find_brand_sp: ucp %p sp %p\n", ucp, sp);
+
+ return (sp);
+}
+
+ucontext_t *
+lx_syscall_regs(void)
+{
+ ucontext_t *ucp = NULL;
+ uintptr_t flags;
+
+ /*
+ * Ask for the current emulation (or signal handling) ucontext_t...
+ */
+ assert(syscall(SYS_brand, B_GET_CURRENT_CONTEXT, &ucp) == 0);
+ assert(ucp != NULL);
+
+ /*
+ * Use of the lx_syscall_regs() function implies that the topmost (i.e.
+ * current) context is for a system call emulation request from the
+ * kernel, rather than a signal handling frame.
+ */
+ flags = (uintptr_t)ucp->uc_brand_data[0];
+ assert(flags & LX_UC_FRAME_IS_SYSCALL);
+
+ lx_debug("lx_syscall_regs: ucp %p\n", ucp);
+
+ return (ucp);
+}
+
+int
+lx_lpid_to_spair(pid_t lpid, pid_t *spid, lwpid_t *slwp)
+{
+ pid_t pid;
+ lwpid_t tid;
+
+ if (lpid == 0) {
+ pid = getpid();
+ tid = thr_self();
+ } else {
+ if (syscall(SYS_brand, B_LPID_TO_SPAIR, lpid, &pid, &tid) < 0)
+ return (-errno);
+
+ /*
+ * If the returned pid is -1, that indicates we tried to
+ * look up the PID for init, but that process no longer
+ * exists.
+ */
+ if (pid == -1)
+ return (-ESRCH);
+ }
+
+ if (uucopy(&pid, spid, sizeof (pid_t)) != 0)
+ return (-errno);
+
+ if (uucopy(&tid, slwp, sizeof (lwpid_t)) != 0)
+ return (-errno);
+
+ return (0);
+}
+
+int
+lx_lpid_to_spid(pid_t lpid, pid_t *spid)
+{
+ lwpid_t slwp;
+
+ return (lx_lpid_to_spair(lpid, spid, &slwp));
+}
+
+char *
+lx_fd_to_path(int fd, char *buf, int buf_size)
+{
+ char path_proc[MAXPATHLEN];
+ pid_t pid;
+ int n;
+
+ assert((buf != NULL) && (buf_size >= 0));
+
+ if (fd < 0)
+ return (NULL);
+
+ if ((pid = getpid()) == -1)
+ return (NULL);
+
+ (void) snprintf(path_proc, MAXPATHLEN,
+ "/native/proc/%d/path/%d", pid, fd);
+
+ if ((n = readlink(path_proc, buf, buf_size - 1)) == -1)
+ return (NULL);
+ buf[n] = '\0';
+
+ return (buf);
+}
+
+#if defined(_LP64)
+/* The following is the 64-bit syscall table */
+
+static lx_syscall_handler_t lx_handlers[] = {
+ NULL, /* 0: read */
+ NULL, /* 1: write */
+ NULL, /* 2: open */
+ NULL, /* 3: close */
+ NULL, /* 4: stat */
+ NULL, /* 5: fstat */
+ NULL, /* 6: lstat */
+ NULL, /* 7: poll */
+ NULL, /* 8: lseek */
+ NULL, /* 9: mmap */
+ NULL, /* 10: mprotect */
+ NULL, /* 11: munmap */
+ NULL, /* 12: brk */
+ lx_rt_sigaction, /* 13: rt_sigaction */
+ lx_rt_sigprocmask, /* 14: rt_sigprocmask */
+ lx_rt_sigreturn, /* 15: rt_sigreturn */
+ NULL, /* 16: ioctl */
+ NULL, /* 17: pread64 */
+ NULL, /* 18: pwrite64 */
+ NULL, /* 19: readv */
+ NULL, /* 20: writev */
+ NULL, /* 21: access */
+ NULL, /* 22: pipe */
+ NULL, /* 23: select */
+ NULL, /* 24: sched_yield */
+ NULL, /* 25: mremap */
+ NULL, /* 26: msync */
+ NULL, /* 27: mincore */
+ NULL, /* 28: madvise */
+ lx_shmget, /* 29: shmget */
+ lx_shmat, /* 30: shmat */
+ lx_shmctl, /* 31: shmctl */
+ NULL, /* 32: dup */
+ NULL, /* 33: dup2 */
+ NULL, /* 34: pause */
+ NULL, /* 35: nanosleep */
+ NULL, /* 36: getitimer */
+ NULL, /* 37: alarm */
+ lx_setitimer, /* 38: setitimer */
+ NULL, /* 39: getpid */
+ lx_sendfile64, /* 40: sendfile */
+ NULL, /* 41: socket */
+ NULL, /* 42: connect */
+ NULL, /* 43: accept */
+ NULL, /* 44: sendto */
+ NULL, /* 45: recvfrom */
+ NULL, /* 46: sendmsg */
+ NULL, /* 47: recvmsg */
+ NULL, /* 48: shutdown */
+ NULL, /* 49: bind */
+ NULL, /* 50: listen */
+ NULL, /* 51: getsockname */
+ NULL, /* 52: getpeername */
+ NULL, /* 53: socketpair */
+ NULL, /* 54: setsockopt */
+ NULL, /* 55: getsockopt */
+ lx_clone, /* 56: clone */
+ lx_fork, /* 57: fork */
+ lx_vfork, /* 58: vfork */
+ lx_execve, /* 59: execve */
+ lx_exit, /* 60: exit */
+ NULL, /* 61: wait4 */
+ NULL, /* 62: kill */
+ NULL, /* 63: uname */
+ lx_semget, /* 64: semget */
+ lx_semop, /* 65: semop */
+ lx_semctl, /* 66: semctl */
+ lx_shmdt, /* 67: shmdt */
+ lx_msgget, /* 68: msgget */
+ lx_msgsnd, /* 69: msgsnd */
+ lx_msgrcv, /* 70: msgrcv */
+ lx_msgctl, /* 71: msgctl */
+ NULL, /* 72: fcntl */
+ NULL, /* 73: flock */
+ lx_fsync, /* 74: fsync */
+ lx_fdatasync, /* 75: fdatasync */
+ lx_truncate, /* 76: truncate */
+ lx_ftruncate, /* 77: ftruncate */
+ NULL, /* 78: getdents */
+ NULL, /* 79: getcwd */
+ NULL, /* 80: chdir */
+ NULL, /* 81: fchdir */
+ NULL, /* 82: rename */
+ NULL, /* 83: mkdir */
+ lx_rmdir, /* 84: rmdir */
+ NULL, /* 85: creat */
+ NULL, /* 86: link */
+ NULL, /* 87: unlink */
+ NULL, /* 88: symlink */
+ NULL, /* 89: readlink */
+ NULL, /* 90: chmod */
+ NULL, /* 91: fchmod */
+ NULL, /* 92: chown */
+ NULL, /* 93: fchown */
+ NULL, /* 94: lchown */
+ NULL, /* 95: umask */
+ NULL, /* 96: gettimeofday */
+ NULL, /* 97: getrlimit */
+ NULL, /* 98: getrusage */
+ NULL, /* 99: sysinfo */
+ NULL, /* 100: times */
+ NULL, /* 101: ptrace */
+ NULL, /* 102: getuid */
+ NULL, /* 103: syslog */
+ NULL, /* 104: getgid */
+ NULL, /* 105: setuid */
+ NULL, /* 106: setgid */
+ NULL, /* 107: geteuid */
+ NULL, /* 108: getegid */
+ NULL, /* 109: setpgid */
+ NULL, /* 110: getppid */
+ NULL, /* 111: getpgrp */
+ NULL, /* 112: setsid */
+ NULL, /* 113: setreuid */
+ NULL, /* 114: setregid */
+ lx_getgroups, /* 115: getgroups */
+ lx_setgroups, /* 116: setgroups */
+ NULL, /* 117: setresuid */
+ NULL, /* 118: getresuid */
+ NULL, /* 119: setresgid */
+ NULL, /* 120: getresgid */
+ NULL, /* 121: getpgid */
+ NULL, /* 122: setfsuid */
+ NULL, /* 123: setfsgid */
+ NULL, /* 124: getsid */
+ lx_capget, /* 125: capget */
+ lx_capset, /* 126: capset */
+ lx_rt_sigpending, /* 127: rt_sigpending */
+ lx_rt_sigtimedwait, /* 128: rt_sigtimedwait */
+ lx_rt_sigqueueinfo, /* 129: rt_sigqueueinfo */
+ lx_rt_sigsuspend, /* 130: rt_sigsuspend */
+ lx_sigaltstack, /* 131: sigaltstack */
+ lx_utime, /* 132: utime */
+ lx_mknod, /* 133: mknod */
+ NULL, /* 134: uselib */
+ NULL, /* 135: personality */
+ NULL, /* 136: ustat */
+ lx_statfs, /* 137: statfs */
+ lx_fstatfs, /* 138: fstatfs */
+ lx_sysfs, /* 139: sysfs */
+ NULL, /* 140: getpriority */
+ NULL, /* 141: setpriority */
+ NULL, /* 142: sched_setparam */
+ NULL, /* 143: sched_getparam */
+ NULL, /* 144: sched_setscheduler */
+ NULL, /* 145: sched_getscheduler */
+ NULL, /* 146: sched_get_priority_max */
+ NULL, /* 147: sched_get_priority_min */
+ NULL, /* 148: sched_rr_get_interval */
+ NULL, /* 149: mlock */
+ NULL, /* 150: munlock */
+ NULL, /* 151: mlockall */
+ NULL, /* 152: munlockall */
+ NULL, /* 153: vhangup */
+ NULL, /* 154: modify_ldt */
+ NULL, /* 155: pivot_root */
+ lx_sysctl, /* 156: sysctl */
+ NULL, /* 157: prctl */
+ NULL, /* 158: arch_prctl */
+ lx_adjtimex, /* 159: adjtimex */
+ NULL, /* 160: setrlimit */
+ NULL, /* 161: chroot */
+ NULL, /* 162: sync */
+ NULL, /* 163: acct */
+ lx_settimeofday, /* 164: settimeofday */
+ lx_mount, /* 165: mount */
+ NULL, /* 166: umount2 */
+ NULL, /* 167: swapon */
+ NULL, /* 168: swapoff */
+ NULL, /* 169: reboot */
+ NULL, /* 170: sethostname */
+ NULL, /* 171: setdomainname */
+ NULL, /* 172: iopl */
+ NULL, /* 173: ioperm */
+ NULL, /* 174: create_module */
+ NULL, /* 175: init_module */
+ NULL, /* 176: delete_module */
+ NULL, /* 177: get_kernel_syms */
+ lx_query_module, /* 178: query_module */
+ NULL, /* 179: quotactl */
+ NULL, /* 180: nfsservctl */
+ NULL, /* 181: getpmsg */
+ NULL, /* 182: putpmsg */
+ NULL, /* 183: afs_syscall */
+ NULL, /* 184: tux */
+ NULL, /* 185: security */
+ NULL, /* 186: gettid */
+ NULL, /* 187: readahead */
+ NULL, /* 188: setxattr */
+ NULL, /* 189: lsetxattr */
+ NULL, /* 190: fsetxattr */
+ NULL, /* 191: getxattr */
+ NULL, /* 192: lgetxattr */
+ NULL, /* 193: fgetxattr */
+ NULL, /* 194: listxattr */
+ NULL, /* 195: llistxattr */
+ NULL, /* 196: flistxattr */
+ NULL, /* 197: removexattr */
+ NULL, /* 198: lremovexattr */
+ NULL, /* 199: fremovexattr */
+ NULL, /* 200: tkill */
+ NULL, /* 201: time */
+ NULL, /* 202: futex */
+ NULL, /* 203: sched_setaffinity */
+ NULL, /* 204: sched_getaffinity */
+ NULL, /* 205: set_thread_area */
+ NULL, /* 206: io_setup */
+ NULL, /* 207: io_destroy */
+ NULL, /* 208: io_getevents */
+ NULL, /* 209: io_submit */
+ NULL, /* 210: io_cancel */
+ NULL, /* 211: get_thread_area */
+ NULL, /* 212: lookup_dcookie */
+ NULL, /* 213: epoll_create */
+ NULL, /* 214: epoll_ctl_old */
+ NULL, /* 215: epoll_wait_old */
+ NULL, /* 216: remap_file_pages */
+ NULL, /* 217: getdents64 */
+ NULL, /* 218: set_tid_address */
+ NULL, /* 219: restart_syscall */
+ lx_semtimedop, /* 220: semtimedop */
+ NULL, /* 221: fadvise64 */
+ NULL, /* 222: timer_create */
+ lx_timer_settime, /* 223: timer_settime */
+ lx_timer_gettime, /* 224: timer_gettime */
+ lx_timer_getoverrun, /* 225: timer_getoverrun */
+ lx_timer_delete, /* 226: timer_delete */
+ NULL, /* 227: clock_settime */
+ NULL, /* 228: clock_gettime */
+ NULL, /* 229: clock_getres */
+ lx_clock_nanosleep, /* 230: clock_nanosleep */
+ lx_group_exit, /* 231: exit_group */
+ NULL, /* 232: epoll_wait */
+ NULL, /* 233: epoll_ctl */
+ NULL, /* 234: tgkill */
+ lx_utimes, /* 235: utimes */
+ NULL, /* 236: vserver */
+ NULL, /* 237: mbind */
+ NULL, /* 238: set_mempolicy */
+ NULL, /* 239: get_mempolicy */
+ NULL, /* 240: mq_open */
+ NULL, /* 241: mq_unlink */
+ NULL, /* 242: mq_timedsend */
+ NULL, /* 243: mq_timedreceive */
+ NULL, /* 244: mq_notify */
+ NULL, /* 245: mq_getsetattr */
+ NULL, /* 246: kexec_load */
+ NULL, /* 247: waitid */
+ NULL, /* 248: add_key */
+ NULL, /* 249: request_key */
+ NULL, /* 250: keyctl */
+ NULL, /* 251: ioprio_set */
+ NULL, /* 252: ioprio_get */
+ lx_inotify_init, /* 253: inotify_init */
+ lx_inotify_add_watch, /* 254: inotify_add_watch */
+ lx_inotify_rm_watch, /* 255: inotify_rm_watch */
+ NULL, /* 256: migrate_pages */
+ NULL, /* 257: openat */
+ NULL, /* 258: mkdirat */
+ lx_mknodat, /* 259: mknodat */
+ NULL, /* 260: fchownat */
+ lx_futimesat, /* 261: futimesat */
+ NULL, /* 262: fstatat64 */
+ NULL, /* 263: unlinkat */
+ NULL, /* 264: renameat */
+ NULL, /* 265: linkat */
+ NULL, /* 266: symlinkat */
+ NULL, /* 267: readlinkat */
+ NULL, /* 268: fchmodat */
+ NULL, /* 269: faccessat */
+ NULL, /* 270: pselect6 */
+ NULL, /* 271: ppoll */
+ NULL, /* 272: unshare */
+ NULL, /* 273: set_robust_list */
+ NULL, /* 274: get_robust_list */
+ NULL, /* 275: splice */
+ NULL, /* 276: tee */
+ NULL, /* 277: sync_file_range */
+ NULL, /* 278: vmsplice */
+ NULL, /* 279: move_pages */
+ lx_utimensat, /* 280: utimensat */
+ NULL, /* 281: epoll_pwait */
+ lx_signalfd, /* 282: signalfd */
+ lx_timerfd_create, /* 283: timerfd_create */
+ NULL, /* 284: eventfd */
+ NULL, /* 285: fallocate */
+ lx_timerfd_settime, /* 286: timerfd_settime */
+ lx_timerfd_gettime, /* 287: timerfd_gettime */
+ NULL, /* 288: accept4 */
+ lx_signalfd4, /* 289: signalfd4 */
+ NULL, /* 290: eventfd2 */
+ NULL, /* 291: epoll_create1 */
+ NULL, /* 292: dup3 */
+ NULL, /* 293: pipe2 */
+ lx_inotify_init1, /* 294: inotify_init1 */
+ NULL, /* 295: preadv */
+ NULL, /* 296: pwritev */
+ lx_rt_tgsigqueueinfo, /* 297: rt_tgsigqueueinfo */
+ NULL, /* 298: perf_event_open */
+ NULL, /* 299: recvmmsg */
+ NULL, /* 300: fanotify_init */
+ NULL, /* 301: fanotify_mark */
+ NULL, /* 302: prlimit64 */
+ NULL, /* 303: name_to_handle_at */
+ NULL, /* 304: open_by_handle_at */
+ NULL, /* 305: clock_adjtime */
+ NULL, /* 306: syncfs */
+ NULL, /* 307: sendmmsg */
+ NULL, /* 309: setns */
+ NULL, /* 309: getcpu */
+ NULL, /* 310: process_vm_readv */
+ NULL, /* 311: process_vm_writev */
+ NULL, /* 312: kcmp */
+ NULL, /* 313: finit_module */
+ NULL, /* 314: sched_setattr */
+ NULL, /* 315: sched_getattr */
+ NULL, /* 316: renameat2 */
+ NULL, /* 317: seccomp */
+ NULL, /* 318: getrandom */
+ NULL, /* 319: memfd_create */
+ NULL, /* 320: kexec_file_load */
+ NULL, /* 321: bpf */
+ NULL, /* 322: execveat */
+
+ /* XXX TBD gap then x32 syscalls from 512 - 544 */
+};
+
+#else
+/* The following is the 32-bit syscall table */
+
+static lx_syscall_handler_t lx_handlers[] = {
+ NULL, /* 0: nosys */
+ lx_exit, /* 1: exit */
+ lx_fork, /* 2: fork */
+ NULL, /* 3: read */
+ NULL, /* 4: write */
+ NULL, /* 5: open */
+ NULL, /* 6: close */
+ NULL, /* 7: waitpid */
+ NULL, /* 8: creat */
+ NULL, /* 9: link */
+ NULL, /* 10: unlink */
+ lx_execve, /* 11: execve */
+ NULL, /* 12: chdir */
+ NULL, /* 13: time */
+ lx_mknod, /* 14: mknod */
+ NULL, /* 15: chmod */
+ NULL, /* 16: lchown16 */
+ NULL, /* 17: break */
+ NULL, /* 18: stat */
+ NULL, /* 19: lseek */
+ NULL, /* 20: getpid */
+ lx_mount, /* 21: mount */
+ NULL, /* 22: umount */
+ NULL, /* 23: setuid16 */
+ NULL, /* 24: getuid16 */
+ NULL, /* 25: stime */
+ NULL, /* 26: ptrace */
+ NULL, /* 27: alarm */
+ NULL, /* 28: fstat */
+ NULL, /* 29: pause */
+ lx_utime, /* 30: utime */
+ NULL, /* 31: stty */
+ NULL, /* 32: gtty */
+ NULL, /* 33: access */
+ NULL, /* 34: nice */
+ NULL, /* 35: ftime */
+ NULL, /* 36: sync */
+ NULL, /* 37: kill */
+ NULL, /* 38: rename */
+ NULL, /* 39: mkdir */
+ lx_rmdir, /* 40: rmdir */
+ NULL, /* 41: dup */
+ NULL, /* 42: pipe */
+ NULL, /* 43: times */
+ NULL, /* 44: prof */
+ NULL, /* 45: brk */
+ NULL, /* 46: setgid16 */
+ NULL, /* 47: getgid16 */
+ lx_signal, /* 48: signal */
+ NULL, /* 49: geteuid16 */
+ NULL, /* 50: getegid16 */
+ NULL, /* 51: acct */
+ NULL, /* 52: umount2 */
+ NULL, /* 53: lock */
+ NULL, /* 54: ioctl */
+ NULL, /* 55: fcntl */
+ NULL, /* 56: mpx */
+ NULL, /* 57: setpgid */
+ NULL, /* 58: ulimit */
+ NULL, /* 59: olduname */
+ NULL, /* 60: umask */
+ NULL, /* 61: chroot */
+ NULL, /* 62: ustat */
+ NULL, /* 63: dup2 */
+ NULL, /* 64: getppid */
+ NULL, /* 65: getpgrp */
+ NULL, /* 66: setsid */
+ lx_sigaction, /* 67: sigaction */
+ NULL, /* 68: sgetmask */
+ NULL, /* 69: ssetmask */
+ NULL, /* 70: setreuid16 */
+ NULL, /* 71: setregid16 */
+ lx_sigsuspend, /* 72: sigsuspend */
+ lx_sigpending, /* 73: sigpending */
+ NULL, /* 74: sethostname */
+ NULL, /* 75: setrlimit */
+ NULL, /* 76: getrlimit */
+ NULL, /* 77: getrusage */
+ NULL, /* 78: gettimeofday */
+ lx_settimeofday, /* 79: settimeofday */
+ lx_getgroups16, /* 80: getgroups16 */
+ lx_setgroups16, /* 81: setgroups16 */
+ NULL, /* 82: select */
+ NULL, /* 83: symlink */
+ NULL, /* 84: oldlstat */
+ NULL, /* 85: readlink */
+ NULL, /* 86: uselib */
+ NULL, /* 87: swapon */
+ NULL, /* 88: reboot */
+ lx_readdir, /* 89: readdir */
+ NULL, /* 90: mmap */
+ NULL, /* 91: munmap */
+ lx_truncate, /* 92: truncate */
+ lx_ftruncate, /* 93: ftruncate */
+ NULL, /* 94: fchmod */
+ NULL, /* 95: fchown16 */
+ NULL, /* 96: getpriority */
+ NULL, /* 97: setpriority */
+ NULL, /* 98: profil */
+ lx_statfs, /* 99: statfs */
+ lx_fstatfs, /* 100: fstatfs */
+ NULL, /* 101: ioperm */
+ NULL, /* 102: socketcall */
+ NULL, /* 103: syslog */
+ lx_setitimer, /* 104: setitimer */
+ NULL, /* 105: getitimer */
+ NULL, /* 106: stat */
+ NULL, /* 107: lstat */
+ NULL, /* 108: fstat */
+ NULL, /* 109: uname */
+ NULL, /* 110: oldiopl */
+ NULL, /* 111: vhangup */
+ NULL, /* 112: idle */
+ NULL, /* 113: vm86old */
+ NULL, /* 114: wait4 */
+ NULL, /* 115: swapoff */
+ NULL, /* 116: sysinfo */
+ lx_ipc, /* 117: ipc */
+ lx_fsync, /* 118: fsync */
+ lx_sigreturn, /* 119: sigreturn */
+ lx_clone, /* 120: clone */
+ NULL, /* 121: setdomainname */
+ NULL, /* 122: uname */
+ NULL, /* 123: modify_ldt */
+ lx_adjtimex, /* 124: adjtimex */
+ NULL, /* 125: mprotect */
+ lx_sigprocmask, /* 126: sigprocmask */
+ NULL, /* 127: create_module */
+ NULL, /* 128: init_module */
+ NULL, /* 129: delete_module */
+ NULL, /* 130: get_kernel_syms */
+ NULL, /* 131: quotactl */
+ NULL, /* 132: getpgid */
+ NULL, /* 133: fchdir */
+ NULL, /* 134: bdflush */
+ lx_sysfs, /* 135: sysfs */
+ NULL, /* 136: personality */
+ NULL, /* 137: afs_syscall */
+ NULL, /* 138: setfsuid16 */
+ NULL, /* 139: setfsgid16 */
+ NULL, /* 140: llseek */
+ NULL, /* 141: getdents */
+ NULL, /* 142: select */
+ NULL, /* 143: flock */
+ NULL, /* 144: msync */
+ NULL, /* 145: readv */
+ NULL, /* 146: writev */
+ NULL, /* 147: getsid */
+ lx_fdatasync, /* 148: fdatasync */
+ lx_sysctl, /* 149: sysctl */
+ NULL, /* 150: mlock */
+ NULL, /* 151: munlock */
+ NULL, /* 152: mlockall */
+ NULL, /* 153: munlockall */
+ NULL, /* 154: sched_setparam */
+ NULL, /* 155: sched_getparam */
+ NULL, /* 156: sched_setscheduler */
+ NULL, /* 157: sched_getscheduler */
+ NULL, /* 158: sched_yield */
+ NULL, /* 159: sched_get_priority_max */
+ NULL, /* 160: sched_get_priority_min */
+ NULL, /* 161: sched_rr_get_interval */
+ NULL, /* 162: nanosleep */
+ NULL, /* 163: mremap */
+ NULL, /* 164: setresuid16 */
+ NULL, /* 165: getresuid16 */
+ NULL, /* 166: vm86 */
+ lx_query_module, /* 167: query_module */
+ NULL, /* 168: poll */
+ NULL, /* 169: nfsservctl */
+ NULL, /* 170: setresgid16 */
+ NULL, /* 171: getresgid16 */
+ NULL, /* 172: prctl */
+ lx_rt_sigreturn, /* 173: rt_sigreturn */
+ lx_rt_sigaction, /* 174: rt_sigaction */
+ lx_rt_sigprocmask, /* 175: rt_sigprocmask */
+ lx_rt_sigpending, /* 176: rt_sigpending */
+ lx_rt_sigtimedwait, /* 177: rt_sigtimedwait */
+ lx_rt_sigqueueinfo, /* 178: rt_sigqueueinfo */
+ lx_rt_sigsuspend, /* 179: rt_sigsuspend */
+ NULL, /* 180: pread64 */
+ NULL, /* 181: pwrite64 */
+ NULL, /* 182: chown16 */
+ NULL, /* 183: getcwd */
+ lx_capget, /* 184: capget */
+ lx_capset, /* 185: capset */
+ lx_sigaltstack, /* 186: sigaltstack */
+ lx_sendfile, /* 187: sendfile */
+ NULL, /* 188: getpmsg */
+ NULL, /* 189: putpmsg */
+ lx_vfork, /* 190: vfork */
+ NULL, /* 191: getrlimit */
+ NULL, /* 192: mmap2 */
+ lx_truncate64, /* 193: truncate64 */
+ lx_ftruncate64, /* 194: ftruncate64 */
+ NULL, /* 195: stat64 */
+ NULL, /* 196: lstat64 */
+ NULL, /* 197: fstat64 */
+ NULL, /* 198: lchown */
+ NULL, /* 199: getuid */
+ NULL, /* 200: getgid */
+ NULL, /* 201: geteuid */
+ NULL, /* 202: getegid */
+ NULL, /* 203: setreuid */
+ NULL, /* 204: setregid */
+ lx_getgroups, /* 205: getgroups */
+ lx_setgroups, /* 206: setgroups */
+ NULL, /* 207: fchown */
+ NULL, /* 208: setresuid */
+ NULL, /* 209: getresuid */
+ NULL, /* 210: setresgid */
+ NULL, /* 211: getresgid */
+ NULL, /* 212: chown */
+ NULL, /* 213: setuid */
+ NULL, /* 214: setgid */
+ NULL, /* 215: setfsuid */
+ NULL, /* 216: setfsgid */
+ NULL, /* 217: pivot_root */
+ NULL, /* 218: mincore */
+ NULL, /* 219: madvise */
+ NULL, /* 220: getdents64 */
+ NULL, /* 221: fcntl64 */
+ NULL, /* 222: tux */
+ NULL, /* 223: security */
+ NULL, /* 224: gettid */
+ NULL, /* 225: readahead */
+ NULL, /* 226: setxattr */
+ NULL, /* 227: lsetxattr */
+ NULL, /* 228: fsetxattr */
+ NULL, /* 229: getxattr */
+ NULL, /* 230: lgetxattr */
+ NULL, /* 231: fgetxattr */
+ NULL, /* 232: listxattr */
+ NULL, /* 233: llistxattr */
+ NULL, /* 234: flistxattr */
+ NULL, /* 235: removexattr */
+ NULL, /* 236: lremovexattr */
+ NULL, /* 237: fremovexattr */
+ NULL, /* 238: tkill */
+ lx_sendfile64, /* 239: sendfile64 */
+ NULL, /* 240: futex */
+ NULL, /* 241: sched_setaffinity */
+ NULL, /* 242: sched_getaffinity */
+ NULL, /* 243: set_thread_area */
+ NULL, /* 244: get_thread_area */
+ NULL, /* 245: io_setup */
+ NULL, /* 246: io_destroy */
+ NULL, /* 247: io_getevents */
+ NULL, /* 248: io_submit */
+ NULL, /* 249: io_cancel */
+ NULL, /* 250: fadvise64 */
+ NULL, /* 251: nosys */
+ lx_group_exit, /* 252: group_exit */
+ NULL, /* 253: lookup_dcookie */
+ NULL, /* 254: epoll_create */
+ NULL, /* 255: epoll_ctl */
+ NULL, /* 256: epoll_wait */
+ NULL, /* 257: remap_file_pages */
+ NULL, /* 258: set_tid_address */
+ NULL, /* 259: timer_create */
+ lx_timer_settime, /* 260: timer_settime */
+ lx_timer_gettime, /* 261: timer_gettime */
+ lx_timer_getoverrun, /* 262: timer_getoverrun */
+ lx_timer_delete, /* 263: timer_delete */
+ NULL, /* 264: clock_settime */
+ NULL, /* 265: clock_gettime */
+ NULL, /* 266: clock_getres */
+ lx_clock_nanosleep, /* 267: clock_nanosleep */
+ lx_statfs64, /* 268: statfs64 */
+ lx_fstatfs64, /* 269: fstatfs64 */
+ NULL, /* 270: tgkill */
+ lx_utimes, /* 271: utimes */
+ NULL, /* 272: fadvise64_64 */
+ NULL, /* 273: vserver */
+ NULL, /* 274: mbind */
+ NULL, /* 275: get_mempolicy */
+ NULL, /* 276: set_mempolicy */
+ NULL, /* 277: mq_open */
+ NULL, /* 278: mq_unlink */
+ NULL, /* 279: mq_timedsend */
+ NULL, /* 280: mq_timedreceive */
+ NULL, /* 281: mq_notify */
+ NULL, /* 282: mq_getsetattr */
+ NULL, /* 283: kexec_load */
+ NULL, /* 284: waitid */
+ NULL, /* 285: sys_setaltroot */
+ NULL, /* 286: add_key */
+ NULL, /* 287: request_key */
+ NULL, /* 288: keyctl */
+ NULL, /* 289: ioprio_set */
+ NULL, /* 290: ioprio_get */
+ lx_inotify_init, /* 291: inotify_init */
+ lx_inotify_add_watch, /* 292: inotify_add_watch */
+ lx_inotify_rm_watch, /* 293: inotify_rm_watch */
+ NULL, /* 294: migrate_pages */
+ NULL, /* 295: openat */
+ NULL, /* 296: mkdirat */
+ lx_mknodat, /* 297: mknodat */
+ NULL, /* 298: fchownat */
+ lx_futimesat, /* 299: futimesat */
+ NULL, /* 300: fstatat64 */
+ NULL, /* 301: unlinkat */
+ NULL, /* 302: renameat */
+ NULL, /* 303: linkat */
+ NULL, /* 304: symlinkat */
+ NULL, /* 305: readlinkat */
+ NULL, /* 306: fchmodat */
+ NULL, /* 307: faccessat */
+ NULL, /* 308: pselect6 */
+ NULL, /* 309: ppoll */
+ NULL, /* 310: unshare */
+ NULL, /* 311: set_robust_list */
+ NULL, /* 312: get_robust_list */
+ NULL, /* 313: splice */
+ NULL, /* 314: sync_file_range */
+ NULL, /* 315: tee */
+ NULL, /* 316: vmsplice */
+ NULL, /* 317: move_pages */
+ NULL, /* 318: getcpu */
+ NULL, /* 319: epoll_pwait */
+ lx_utimensat, /* 320: utimensat */
+ lx_signalfd, /* 321: signalfd */
+ lx_timerfd_create, /* 322: timerfd_create */
+ NULL, /* 323: eventfd */
+ NULL, /* 324: fallocate */
+ lx_timerfd_settime, /* 325: timerfd_settime */
+ lx_timerfd_gettime, /* 326: timerfd_gettime */
+ lx_signalfd4, /* 327: signalfd4 */
+ NULL, /* 328: eventfd2 */
+ NULL, /* 329: epoll_create1 */
+ NULL, /* 330: dup3 */
+ NULL, /* 331: pipe2 */
+ lx_inotify_init1, /* 332: inotify_init1 */
+ NULL, /* 333: preadv */
+ NULL, /* 334: pwritev */
+ lx_rt_tgsigqueueinfo, /* 335: rt_tgsigqueueinfo */
+ NULL, /* 336: perf_event_open */
+ NULL, /* 337: recvmmsg */
+ NULL, /* 338: fanotify_init */
+ NULL, /* 339: fanotify_mark */
+ NULL, /* 340: prlimit64 */
+ NULL, /* 341: name_to_handle_at */
+ NULL, /* 342: open_by_handle_at */
+ NULL, /* 343: clock_adjtime */
+ NULL, /* 344: syncfs */
+ NULL, /* 345: sendmmsg */
+ NULL, /* 346: setns */
+ NULL, /* 347: process_vm_readv */
+ NULL, /* 348: process_vm_writev */
+ NULL, /* 349: kcmp */
+ NULL, /* 350: finit_module */
+ NULL, /* 351: sched_setattr */
+ NULL, /* 352: sched_getattr */
+ NULL, /* 353: renameat2 */
+ NULL, /* 354: seccomp */
+ NULL, /* 355: getrandom */
+ NULL, /* 356: memfd_create */
+ NULL, /* 357: bpf */
+ NULL, /* 358: execveat */
+};
+#endif
diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_provider.d b/usr/src/lib/brand/lx/lx_brand/common/lx_provider.d
new file mode 100644
index 0000000000..14326e8f56
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/lx_provider.d
@@ -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 2015 Joyent, Inc.
+ */
+
+provider lx {
+ probe debug(char *buf);
+ probe sigdeliver(int sig, void *lx_sigaction, void *lx_sigstack);
+ probe sigreturn(void *lx_ucontext, void *ucontext, uintptr_t sp);
+
+ probe signal__delivery__frame__create(void *lx_sigdeliver_frame);
+ probe signal__delivery__frame__found(void *lx_sigdeliver_frame);
+ probe signal__delivery__frame__corrupt(void *lx_sigdeliver_frame);
+
+ probe signal__post__handler(uintptr_t old_sp, uintptr_t new_sp);
+
+ probe signal__altstack__enable(uintptr_t alt_sp);
+ probe signal__altstack__disable();
+
+ probe emulate__enter(void *ucp, int syscall_num, uintptr_t *args);
+ probe emulate__return(void *ucp, int syscall_num, uintptr_t ret,
+ uintptr_t errn);
+};
+
+#pragma D attributes Evolving/Evolving/ISA provider lx provider
+#pragma D attributes Private/Private/Unknown provider lx module
+#pragma D attributes Private/Private/Unknown provider lx function
+#pragma D attributes Private/Private/ISA provider lx name
+#pragma D attributes Private/Private/ISA provider lx args
diff --git a/usr/src/lib/brand/lx/lx_brand/common/mapfile-vers b/usr/src/lib/brand/lx/lx_brand/common/mapfile-vers
new file mode 100644
index 0000000000..9d2712a12f
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/mapfile-vers
@@ -0,0 +1,50 @@
+#
+# 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) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+# Copyright 2020 Joyent, Inc.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+#
+# Scope everything local -- our .init section is our only public interface.
+#
+SYMBOL_SCOPE {
+ local:
+ *;
+};
diff --git a/usr/src/lib/brand/lx/lx_brand/common/misc.c b/usr/src/lib/brand/lx/lx_brand/common/misc.c
new file mode 100644
index 0000000000..a223d0ac35
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/misc.c
@@ -0,0 +1,359 @@
+/*
+ * 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 2017 Joyent, Inc.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <priv.h>
+#include <strings.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysmacros.h>
+#include <sys/systeminfo.h>
+#include <sys/types.h>
+#include <sys/shm.h>
+#include <sys/socket.h>
+#include <sys/inotify.h>
+#include <sys/eventfd.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_fcntl.h>
+
+/*
+ * {get,set}groups16() - Handle the conversion between 16-bit Linux gids and
+ * 32-bit illumos gids.
+ */
+long
+lx_getgroups16(uintptr_t p1, uintptr_t p2)
+{
+ int count = (int)p1;
+ lx_gid16_t *grouplist = (lx_gid16_t *)p2;
+ gid_t *grouplist32;
+ int ret;
+ int i;
+
+ if (count < 0)
+ return (-EINVAL);
+
+ grouplist32 = malloc(count * sizeof (gid_t));
+ if (grouplist32 == NULL && count > 0) {
+ free(grouplist32);
+ return (-ENOMEM);
+ }
+ if ((ret = getgroups(count, grouplist32)) < 0) {
+ free(grouplist32);
+ return (-errno);
+ }
+
+ /* we must not modify the list if the incoming count was 0 */
+ if (count > 0) {
+ for (i = 0; i < ret; i++)
+ grouplist[i] = LX_GID32_TO_GID16(grouplist32[i]);
+ }
+
+ free(grouplist32);
+ return (ret);
+}
+
+long
+lx_setgroups16(uintptr_t p1, uintptr_t p2)
+{
+ long rv;
+ int count = (int)p1;
+ lx_gid16_t *grouplist = NULL;
+ gid_t *grouplist32 = NULL;
+ int i;
+
+ if ((grouplist = malloc(count * sizeof (lx_gid16_t))) == NULL) {
+ return (-ENOMEM);
+ }
+ if (uucopy((void *)p2, grouplist, count * sizeof (lx_gid16_t)) != 0) {
+ free(grouplist);
+ return (-EFAULT);
+ }
+
+ grouplist32 = malloc(count * sizeof (gid_t));
+ if (grouplist32 == NULL) {
+ free(grouplist);
+ return (-ENOMEM);
+ }
+ for (i = 0; i < count; i++)
+ grouplist32[i] = LX_GID16_TO_GID32(grouplist[i]);
+
+ /* order matters here to get the correct errno back */
+ if (count > NGROUPS_MAX_DEFAULT) {
+ free(grouplist);
+ free(grouplist32);
+ return (-EINVAL);
+ }
+
+ rv = setgroups(count, grouplist32);
+
+ free(grouplist);
+ free(grouplist32);
+
+ return (rv != 0 ? -errno : 0);
+}
+
+/*
+ * mknod() - Since we don't have the SYS_CONFIG privilege within a zone, the
+ * only mode we have to support is S_IFIFO. We also have to distinguish between
+ * an invalid type and insufficient privileges.
+ */
+#define LX_S_IFMT 0170000
+#define LX_S_IFDIR 0040000
+#define LX_S_IFCHR 0020000
+#define LX_S_IFBLK 0060000
+#define LX_S_IFREG 0100000
+#define LX_S_IFIFO 0010000
+#define LX_S_IFLNK 0120000
+#define LX_S_IFSOCK 0140000
+
+/*ARGSUSED*/
+long
+lx_mknod(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ char *path = (char *)p1;
+ lx_dev_t lx_dev = (lx_dev_t)p3;
+ struct sockaddr_un sockaddr;
+ struct stat statbuf;
+ mode_t mode, type;
+ dev_t dev;
+ int fd;
+
+ type = ((mode_t)p2 & LX_S_IFMT);
+ mode = ((mode_t)p2 & 07777);
+
+ switch (type) {
+ case 0:
+ case LX_S_IFREG:
+ /* create a regular file */
+ if (stat(path, &statbuf) == 0)
+ return (-EEXIST);
+
+ if (errno != ENOENT)
+ return (-errno);
+
+ if ((fd = creat(path, mode)) < 0)
+ return (-errno);
+
+ (void) close(fd);
+ return (0);
+
+ case LX_S_IFSOCK:
+ /*
+ * Create a UNIX domain socket.
+ *
+ * Most programmers aren't even aware you can do this.
+ *
+ * Note you can also do this via illumos' mknod(2), but
+ * Linux allows anyone who can create a UNIX domain
+ * socket via bind(2) to create one via mknod(2);
+ * illumos requires the caller to be privileged.
+ */
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ return (-errno);
+
+ if (stat(path, &statbuf) == 0)
+ return (-EEXIST);
+
+ if (errno != ENOENT)
+ return (-errno);
+
+ if (uucopy(path, &sockaddr.sun_path,
+ sizeof (sockaddr.sun_path)) < 0)
+ return (-errno);
+
+ /* assure NULL termination of sockaddr.sun_path */
+ sockaddr.sun_path[sizeof (sockaddr.sun_path) - 1] = '\0';
+ sockaddr.sun_family = AF_UNIX;
+
+ if (bind(fd, (struct sockaddr *)&sockaddr,
+ strlen(sockaddr.sun_path) +
+ sizeof (sockaddr.sun_family)) < 0)
+ return (-errno);
+
+ (void) close(fd);
+ return (0);
+
+ case LX_S_IFIFO:
+ dev = 0;
+ break;
+
+ case LX_S_IFCHR:
+ case LX_S_IFBLK:
+ /*
+ * The "dev" RPM package wants to create all possible Linux
+ * device nodes, so just report its mknod()s as having
+ * succeeded if we're in install mode.
+ */
+ if (lx_install != 0) {
+ lx_debug("lx_mknod: install mode spoofed creation of "
+ "Linux device [%lld, %lld]\n",
+ LX_GETMAJOR(lx_dev), LX_GETMINOR(lx_dev));
+
+ return (0);
+ }
+
+ dev = makedevice(LX_GETMAJOR(lx_dev), LX_GETMINOR(lx_dev));
+ break;
+
+ default:
+ return (-EINVAL);
+ }
+
+ return (mknod(path, mode | type, dev) ? -errno : 0);
+}
+
+long
+lx_execve(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ char *filename = (char *)p1;
+ char **argv = (char **)p2;
+ char **envp = (char **)p3;
+ char *nullist[] = { NULL };
+
+ if (argv == NULL)
+ argv = nullist;
+
+ /*
+ * Emulate PR_SET_KEEPCAPS which is reset on execve. If this is not done
+ * the emulated capabilities could be reduced more than expected.
+ */
+ (void) setpflags(PRIV_AWARE_RESET, 1);
+
+ /* This is a normal exec call. */
+ (void) execve(filename, argv, envp);
+
+ return (-errno);
+}
+
+long
+lx_setgroups(uintptr_t p1, uintptr_t p2)
+{
+ int ng = (int)p1;
+ gid_t *glist = NULL;
+ int i, r;
+
+ lx_debug("\tlx_setgroups(%d, 0x%p", ng, p2);
+
+ if (ng > 0) {
+ if ((glist = (gid_t *)malloc(ng * sizeof (gid_t))) == NULL)
+ return (-ENOMEM);
+
+ if (uucopy((void *)p2, glist, ng * sizeof (gid_t)) != 0) {
+ free(glist);
+ return (-errno);
+ }
+
+ /*
+ * Linux doesn't check the validity of the group IDs, but
+ * illumos does. Change any invalid group IDs to a known, valid
+ * value (yuck).
+ */
+ for (i = 0; i < ng; i++) {
+ if (glist[i] > MAXUID)
+ glist[i] = MAXUID;
+ }
+ }
+
+ /* order matters here to get the correct errno back */
+ if (ng > NGROUPS_MAX_DEFAULT) {
+ free(glist);
+ return (-EINVAL);
+ }
+
+ r = syscall(SYS_brand, B_HELPER_SETGROUPS, ng, glist);
+
+ free(glist);
+ return ((r == -1) ? -errno : r);
+}
+
+long
+lx_getgroups(int gidsetsize, gid_t *grouplist)
+{
+ int r;
+
+ r = getgroups(gidsetsize, grouplist);
+ return ((r == -1) ? -errno : r);
+}
+
+long
+lx_inotify_add_watch(int fd, const char *pathname, uint32_t mask)
+{
+ int r;
+
+ r = inotify_add_watch(fd, pathname, mask);
+ return ((r == -1) ? -errno : r);
+}
+
+long
+lx_inotify_init(void)
+{
+ int r;
+
+ r = inotify_init();
+ return ((r == -1) ? -errno : r);
+}
+
+long
+lx_inotify_init1(int flags)
+{
+ int r;
+
+ r = inotify_init1(flags);
+ return ((r == -1) ? -errno : r);
+}
+
+long
+lx_inotify_rm_watch(int fd, int wd)
+{
+ int r;
+
+ r = inotify_rm_watch(fd, wd);
+ return ((r == -1) ? -errno : r);
+}
+
+long
+lx_shmdt(char *shmaddr)
+{
+ int r;
+
+ r = shmdt(shmaddr);
+ return ((r == -1) ? -errno : r);
+}
+
+long
+lx_utimes(const char *path, const struct timeval times[2])
+{
+ int r;
+
+ r = utimes(path, times);
+ return ((r == -1) ? -errno : r);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/module.c b/usr/src/lib/brand/lx/lx_brand/common/module.c
new file mode 100644
index 0000000000..78a593712f
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/module.c
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * We don't support Linux modules, but we have to emulate enough of the system
+ * calls to show that we don't have any modules installed.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+/*
+ * For query_module(), we provide an empty list of modules, and return ENOENT
+ * on any request for a specific module.
+ */
+#define LX_QM_MODULES 1
+#define LX_QM_DEPS 2
+#define LX_QM_REFS 3
+#define LX_QM_SYMBOLS 4
+#define LX_QM_INFO 5
+
+/*ARGSUSED*/
+long
+lx_query_module(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
+ uintptr_t p5)
+{
+ /*
+ * parameter p1 is the 'name' argument.
+ */
+ int which = (int)p2;
+ char *buf = (char *)p3;
+ size_t bufsize = (size_t)p4;
+ size_t *ret = (size_t *)p5;
+
+ switch (which) {
+ case 0:
+ /*
+ * Special case: always return 0
+ */
+ return (0);
+
+ case LX_QM_MODULES:
+ /*
+ * Generate an empty list of modules.
+ */
+ if (bufsize && buf)
+ buf[0] = '\0';
+ if (ret)
+ *ret = 0;
+ return (0);
+
+ case LX_QM_DEPS:
+ case LX_QM_REFS:
+ case LX_QM_SYMBOLS:
+ case LX_QM_INFO:
+ /*
+ * Any requests for specific module information return ENOENT.
+ */
+ return (-ENOENT);
+
+ default:
+ return (-EINVAL);
+ }
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/mount.c b/usr/src/lib/brand/lx/lx_brand/common/mount.c
new file mode 100644
index 0000000000..6ecd5cfc4f
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/mount.c
@@ -0,0 +1,540 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <strings.h>
+#include <nfs/mount.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_mount.h>
+
+/*
+ * support definitions
+ */
+union fh_buffer {
+ struct nfs_fid fh2;
+ struct nfs_fh3 fh3;
+ char fh_data[NFS3_FHSIZE + 2];
+};
+
+static int
+i_add_option(char *option, char *buf, size_t buf_size)
+{
+ char *fmt_str = NULL;
+
+ assert((option != NULL) && (strlen(option) > 0));
+ assert((buf != NULL) && (buf_size > 0));
+
+ if (buf[0] == '\0') {
+ fmt_str = "%s";
+ } else {
+ fmt_str = ",%s";
+ }
+
+ buf_size -= strlen(buf);
+ buf += strlen(buf);
+
+ /*LINTED*/
+ if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1))
+ return (-EOVERFLOW);
+ return (0);
+}
+
+static int
+i_add_option_int(char *option, int val, char *buf, size_t buf_size)
+{
+ char *fmt_str = NULL;
+
+ assert((option != NULL) && (strlen(option) > 0));
+ assert((buf != NULL) && (buf_size > 0));
+
+ if (buf[0] == '\0') {
+ fmt_str = "%s=%d";
+ } else {
+ fmt_str = ",%s=%d";
+ }
+
+ buf_size -= strlen(buf);
+ buf += strlen(buf);
+
+ /*LINTED*/
+ if (snprintf(buf, buf_size, fmt_str, option, val) > (buf_size - 1))
+ return (-EOVERFLOW);
+ return (0);
+}
+
+static int
+i_make_nfs_args(lx_nfs_mount_data_t *lx_nmd, struct nfs_args *nfs_args,
+ struct netbuf *nfs_args_addr, struct knetconfig *nfs_args_knconf,
+ union fh_buffer *nfs_args_fh, struct sec_data *nfs_args_secdata,
+ char *fstype, char *options, int options_size)
+{
+ struct stat statbuf;
+ int i, rv, use_tcp;
+
+ /* Sanity check the incomming Linux request. */
+ if ((lx_nmd->nmd_rsize < 0) || (lx_nmd->nmd_wsize < 0) ||
+ (lx_nmd->nmd_timeo < 0) || (lx_nmd->nmd_retrans < 0) ||
+ (lx_nmd->nmd_acregmin < 0) || (lx_nmd->nmd_acregmax < 0) ||
+ (lx_nmd->nmd_acdirmax < 0)) {
+ return (-EINVAL);
+ }
+
+ /*
+ * Additional sanity checks of incomming request.
+ *
+ * Some of the sanity checks below should probably return
+ * EINVAL (or some other error code) instead or ENOTSUP,
+ * but without experiminting on Linux to see how it
+ * deals with certain strange values there is no way
+ * to really know what we should return, hence we return
+ * ENOTSUP to tell us that eventually if we see some
+ * application hitting the problem we can go to a real
+ * Linux system, figure out how it deals with the situation
+ * and update our code to handle it in the same fashion.
+ */
+ if (lx_nmd->nmd_version != 4) {
+ lx_unsupported("unsupported nfs mount request, "
+ "unrecognized NFS mount structure: %d\n",
+ lx_nmd->nmd_version);
+ return (-ENOTSUP);
+ }
+ if ((lx_nmd->nmd_flags & ~LX_NFS_MOUNT_SUPPORTED) != 0) {
+ lx_unsupported("unsupported nfs mount request, "
+ "flags: 0x%x\n", lx_nmd->nmd_flags);
+ return (-ENOTSUP);
+ }
+ if (lx_nmd->nmd_addr.sin_family != AF_INET) {
+ lx_unsupported("unsupported nfs mount request, "
+ "transport address family: 0x%x\n",
+ lx_nmd->nmd_addr.sin_family);
+ return (-ENOTSUP);
+ }
+ for (i = 0; i < LX_NMD_MAXHOSTNAMELEN; i++) {
+ if (lx_nmd->nmd_hostname[i] == '\0')
+ break;
+ }
+ if (i == 0) {
+ lx_unsupported("unsupported nfs mount request, "
+ "no hostname specified\n");
+ return (-ENOTSUP);
+ }
+ if (i == LX_NMD_MAXHOSTNAMELEN) {
+ lx_unsupported("unsupported nfs mount request, "
+ "hostname not terminated\n");
+ return (-ENOTSUP);
+ }
+ if (lx_nmd->nmd_namlen < i) {
+ lx_unsupported("unsupported nfs mount request, "
+ "invalid namlen value: 0x%x\n", lx_nmd->nmd_namlen);
+ return (-ENOTSUP);
+ }
+ if (lx_nmd->nmd_bsize != 0) {
+ lx_unsupported("unsupported nfs mount request, "
+ "bsize value: 0x%x\n", lx_nmd->nmd_bsize);
+ return (-ENOTSUP);
+ }
+
+ /* Initialize and clear the output structure pointers passed in. */
+ bzero(nfs_args, sizeof (*nfs_args));
+ bzero(nfs_args_addr, sizeof (*nfs_args_addr));
+ bzero(nfs_args_knconf, sizeof (*nfs_args_knconf));
+ bzero(nfs_args_fh, sizeof (*nfs_args_fh));
+ bzero(nfs_args_secdata, sizeof (*nfs_args_secdata));
+ nfs_args->addr = nfs_args_addr;
+ nfs_args->knconf = nfs_args_knconf;
+ nfs_args->fh = (caddr_t)nfs_args_fh;
+ nfs_args->nfs_ext_u.nfs_extB.secdata = nfs_args_secdata;
+
+ /* Check if we're using tcp. */
+ use_tcp = (lx_nmd->nmd_flags & LX_NFS_MOUNT_TCP) ? 1 : 0;
+
+ /*
+ * These seem to be the default flags used by Solaris for v2 and v3
+ * nfs mounts.
+ *
+ * Don't bother with NFSMNT_TRYRDMA since we always specify a
+ * transport (either udp or tcp).
+ */
+ nfs_args->flags = NFSMNT_NEWARGS | NFSMNT_KNCONF | NFSMNT_INT |
+ NFSMNT_HOSTNAME;
+
+ /* Translate some Linux mount flags into Solaris mount flags. */
+ if (lx_nmd->nmd_flags & LX_NFS_MOUNT_SOFT)
+ nfs_args->flags |= NFSMNT_SOFT;
+ if (lx_nmd->nmd_flags & LX_NFS_MOUNT_INTR)
+ nfs_args->flags |= NFSMNT_INT;
+ if (lx_nmd->nmd_flags & LX_NFS_MOUNT_POSIX)
+ nfs_args->flags |= NFSMNT_POSIX;
+ if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NOCTO)
+ nfs_args->flags |= NFSMNT_NOCTO;
+ if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NOAC)
+ nfs_args->flags |= NFSMNT_NOAC;
+ if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NONLM)
+ nfs_args->flags |= NFSMNT_LLOCK;
+
+ if ((lx_nmd->nmd_flags & LX_NFS_MOUNT_VER3) != 0) {
+ (void) strcpy(fstype, "nfs3");
+ if ((rv = i_add_option_int("vers", 3,
+ options, options_size)) != 0)
+ return (rv);
+
+ if (lx_nmd->nmd_root.lx_fh3_length >
+ sizeof (nfs_args_fh->fh3.fh3_u.data)) {
+ lx_unsupported("unsupported nfs mount request, "
+ "nfs file handle length: 0x%x\n",
+ lx_nmd->nmd_root.lx_fh3_length);
+ return (-ENOTSUP);
+ }
+
+ /* Set the v3 file handle info. */
+ nfs_args_fh->fh3.fh3_length = lx_nmd->nmd_root.lx_fh3_length;
+ bcopy(&lx_nmd->nmd_root.lx_fh3_data,
+ nfs_args_fh->fh3.fh3_u.data,
+ lx_nmd->nmd_root.lx_fh3_length);
+ } else {
+ /*
+ * Assume nfs v2. Note that this could also be a v1
+ * mount request but there doesn't seem to be any difference
+ * in the parameters passed to the Linux mount system
+ * call for v1 or v2 mounts so there is no way of really
+ * knowing.
+ */
+ (void) strcpy(fstype, "nfs");
+ if ((rv = i_add_option_int("vers", 2,
+ options, options_size)) != 0)
+ return (rv);
+
+ /* Solaris seems to add this flag when using v2. */
+ nfs_args->flags |= NFSMNT_SECDEFAULT;
+
+ /* Set the v2 file handle info. */
+ bcopy(&lx_nmd->nmd_old_root,
+ nfs_args_fh, sizeof (nfs_args_fh->fh2));
+ }
+
+ /*
+ * We can't use getnetconfig() here because there is no netconfig
+ * database in linux.
+ */
+ nfs_args_knconf->knc_protofmly = "inet";
+ if (use_tcp) {
+ /*
+ * TCP uses NC_TPI_COTS_ORD semantics.
+ * See /etc/netconfig.
+ */
+ nfs_args_knconf->knc_semantics = NC_TPI_COTS_ORD;
+ nfs_args_knconf->knc_proto = "tcp";
+ if ((rv = i_add_option("proto=tcp",
+ options, options_size)) != 0)
+ return (rv);
+ if (stat("/dev/tcp", &statbuf) != 0)
+ return (-errno);
+ nfs_args_knconf->knc_rdev = statbuf.st_rdev;
+ } else {
+ /*
+ * Assume UDP. UDP uses NC_TPI_CLTS semantics.
+ * See /etc/netconfig.
+ */
+ nfs_args_knconf->knc_semantics = NC_TPI_CLTS;
+ nfs_args_knconf->knc_proto = "udp";
+ if ((rv = i_add_option("proto=udp",
+ options, options_size)) != 0)
+ return (rv);
+ if (stat("/dev/udp", &statbuf) != 0)
+ return (-errno);
+ nfs_args_knconf->knc_rdev = statbuf.st_rdev;
+ }
+
+ /* Set the server address. */
+ nfs_args_addr->maxlen = nfs_args_addr->len =
+ sizeof (struct sockaddr_in);
+ nfs_args_addr->buf = (char *)&lx_nmd->nmd_addr;
+
+ /* Set the server hostname string. */
+ nfs_args->hostname = lx_nmd->nmd_hostname;
+
+ /* Translate Linux nfs mount parameters into Solaris mount options. */
+ if (lx_nmd->nmd_rsize != LX_NMD_DEFAULT_RSIZE) {
+ if ((rv = i_add_option_int("rsize", lx_nmd->nmd_rsize,
+ options, options_size)) != 0)
+ return (rv);
+ nfs_args->rsize = lx_nmd->nmd_rsize;
+ nfs_args->flags |= NFSMNT_RSIZE;
+ }
+ if (lx_nmd->nmd_wsize != LX_NMD_DEFAULT_WSIZE) {
+ if ((rv = i_add_option_int("wsize", lx_nmd->nmd_wsize,
+ options, options_size)) != 0)
+ return (rv);
+ nfs_args->wsize = lx_nmd->nmd_wsize;
+ nfs_args->flags |= NFSMNT_WSIZE;
+ }
+ if ((rv = i_add_option_int("timeo", lx_nmd->nmd_timeo,
+ options, options_size)) != 0)
+ return (rv);
+ nfs_args->timeo = lx_nmd->nmd_timeo;
+ nfs_args->flags |= NFSMNT_TIMEO;
+ if ((rv = i_add_option_int("retrans", lx_nmd->nmd_retrans,
+ options, options_size)) != 0)
+ return (rv);
+ nfs_args->retrans = lx_nmd->nmd_retrans;
+ nfs_args->flags |= NFSMNT_RETRANS;
+ if ((rv = i_add_option_int("acregmin", lx_nmd->nmd_acregmin,
+ options, options_size)) != 0)
+ return (rv);
+ nfs_args->acregmin = lx_nmd->nmd_acregmin;
+ nfs_args->flags |= NFSMNT_ACREGMIN;
+ if ((rv = i_add_option_int("acregmax", lx_nmd->nmd_acregmax,
+ options, options_size)) != 0)
+ return (rv);
+ nfs_args->acregmax = lx_nmd->nmd_acregmax;
+ nfs_args->flags |= NFSMNT_ACREGMAX;
+ if ((rv = i_add_option_int("acdirmin", lx_nmd->nmd_acdirmin,
+ options, options_size)) != 0)
+ return (rv);
+ nfs_args->acdirmin = lx_nmd->nmd_acdirmin;
+ nfs_args->flags |= NFSMNT_ACDIRMIN;
+ if ((rv = i_add_option_int("acdirmax", lx_nmd->nmd_acdirmax,
+ options, options_size)) != 0)
+ return (rv);
+ nfs_args->acdirmax = lx_nmd->nmd_acdirmax;
+ nfs_args->flags |= NFSMNT_ACDIRMAX;
+
+ /* We only support nfs with a security type of AUTH_SYS. */
+ nfs_args->nfs_args_ext = NFS_ARGS_EXTB;
+ nfs_args_secdata->secmod = AUTH_SYS;
+ nfs_args_secdata->rpcflavor = AUTH_SYS;
+ nfs_args_secdata->flags = 0;
+ nfs_args_secdata->uid = 0;
+ nfs_args_secdata->data = NULL;
+ nfs_args->nfs_ext_u.nfs_extB.next = NULL;
+
+ /*
+ * The Linux nfs mount command seems to pass an open socket fd
+ * to the kernel during the mount system call. We don't need
+ * this fd on Solaris so just close it.
+ */
+ (void) close(lx_nmd->nmd_fd);
+
+ return (0);
+}
+
+/*
+ * The user-level mount(2) code is only used to support NFS mounts. All other
+ * fstypes are handled in-kernel.
+ */
+long
+lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
+ uintptr_t p5)
+{
+ /* Linux input arguments. */
+ const char *sourcep = (const char *)p1;
+ const char *targetp = (const char *)p2;
+ const char *fstypep = (const char *)p3;
+ unsigned int flags = (unsigned int)p4;
+ const void *datap = (const void *)p5;
+
+ char source[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1];
+ char target[MAXPATHLEN];
+ char fstype[8], options[MAX_MNTOPT_STR];
+ int sflags, rv;
+ long res;
+
+ lx_nfs_mount_data_t lx_nmd;
+ struct nfs_args nfs_args;
+ struct netbuf nfs_args_addr;
+ struct knetconfig nfs_args_knconf;
+ union fh_buffer nfs_args_fh;
+ struct sec_data nfs_args_secdata;
+ char *sdataptr = NULL;
+ int sdatalen = 0;
+ int vers;
+
+ /* Initialize illumos mount arguments. */
+ sflags = MS_OPTIONSTR;
+ options[0] = '\0';
+ sdatalen = 0;
+
+ /* Copy in parameters that are always present. */
+ rv = uucopystr((void *)sourcep, &source, sizeof (source));
+ if ((rv == -1) || (rv == sizeof (source)))
+ return (-EFAULT);
+
+ rv = uucopystr((void *)targetp, &target, sizeof (target));
+ if ((rv == -1) || (rv == sizeof (target)))
+ return (-EFAULT);
+
+ rv = uucopystr((void *)fstypep, &fstype, sizeof (fstype));
+ if ((rv == -1) || (rv == sizeof (fstype)))
+ return (-EFAULT);
+
+ lx_debug("\tlinux mount source: %s", source);
+ lx_debug("\tlinux mount target: %s", target);
+ lx_debug("\tlinux mount fstype: %s", fstype);
+
+ /* The in-kernel mount code should only call us for an NFS mount. */
+ assert(strcmp(fstype, "nfs") == 0 || strcmp(fstype, "nfs4") == 0);
+
+ /*
+ * While SunOS is picky about mount(2) target paths being absolute,
+ * Linux is not so strict. In order to facilitate this looser
+ * requirement, the cwd is prepended to non-absolute target paths.
+ */
+ if (target[0] != '/') {
+ char *cpath, *buf = NULL;
+ int len;
+
+ if ((cpath = getcwd(NULL, MAXPATHLEN)) == NULL) {
+ return (-ENOMEM);
+ }
+ len = asprintf(&buf, "%s/%s", cpath, target);
+ free(cpath);
+ if (len < 0) {
+ return (-ENOMEM);
+ } else if (len >= MAXPATHLEN) {
+ free(buf);
+ return (-ENAMETOOLONG);
+ }
+ (void) strlcpy(target, buf, sizeof (target));
+ free(buf);
+ }
+
+ /* Make sure we support the requested mount flags. */
+ if ((flags & ~LX_MS_SUPPORTED) != 0) {
+ lx_unsupported("unsupported mount flags: 0x%x", flags);
+ return (-ENOTSUP);
+ }
+
+ /*
+ * Copy in Linux mount options. Note that for older Linux kernels
+ * (pre 2.6.23) the mount options pointer (which normally points to a
+ * string) points to a structure which is populated by the user-level
+ * code after it has done the preliminary RPCs (similar to how our NFS
+ * mount cmd works). For newer kernels the options pointer is just a
+ * string of options. We're unlikely to actually emulate a kernel that
+ * uses the old style but support is kept and handled in
+ * i_make_nfs_args(). The new style handling is implemented in
+ * lx_nfs_mount(). The user-level mount caller is in charge of
+ * determining the format in which it passes the data parameter.
+ */
+ if (datap == NULL)
+ return (-EINVAL);
+ if (uucopy((void *)datap, &vers, sizeof (int)) < 0)
+ return (-errno);
+
+ /*
+ * As described above, the data parameter might be a versioned lx_nmd
+ * structure or (most likely on a modern distribution) it is a string.
+ */
+ if (vers < 1 || vers > 6) {
+ /*
+ * Handle the modern style with options as a string, make the
+ * preliminary RPC calls and do the native mount all within
+ * lx_nfs_mount().
+ */
+ if (uucopystr((void *)datap, options, sizeof (options)) < 0)
+ return (-errno);
+ return (lx_nfs_mount(source, target, fstype, flags, options));
+ }
+
+ /*
+ * This is an old style NFS mount call and we only support v4.
+ */
+ if (vers != 4) {
+ lx_unsupported("unsupported nfs mount request version: %d\n",
+ vers);
+ return (-ENOTSUP);
+ }
+
+ if (uucopy((void *)datap, &lx_nmd, sizeof (lx_nmd)) < 0)
+ return (-errno);
+
+ /*
+ * For illumos NFS mounts, the kernel expects a special structure, but
+ * a pointer to this structure is passed in via an extra parameter
+ * (sdataptr below.)
+ */
+ if ((rv = i_make_nfs_args(&lx_nmd, &nfs_args, &nfs_args_addr,
+ &nfs_args_knconf, &nfs_args_fh, &nfs_args_secdata, fstype, options,
+ sizeof (options))) != 0)
+ return (rv);
+
+ /*
+ * For the following old-style NFS mount we need to tell the mount
+ * system call to expect extra parameters.
+ */
+ sflags |= MS_DATA;
+ sdataptr = (char *)&nfs_args;
+ sdatalen = sizeof (nfs_args);
+
+ /* Linux seems to always allow overlay mounts */
+ sflags |= MS_OVERLAY;
+
+ /* Convert some Linux flags to illumos flags. */
+ if (flags & LX_MS_RDONLY)
+ sflags |= MS_RDONLY;
+ if (flags & LX_MS_NOSUID)
+ sflags |= MS_NOSUID;
+ if (flags & LX_MS_REMOUNT)
+ sflags |= MS_REMOUNT;
+
+ /*
+ * Convert some Linux flags to illumos option strings.
+ */
+ if (flags & LX_MS_STRICTATIME) {
+ /*
+ * The "strictatime" mount option ensures that none of the
+ * weaker atime-related mode options are in effect.
+ */
+ flags &= ~(LX_MS_RELATIME | LX_MS_NOATIME);
+ }
+ if ((flags & LX_MS_NODEV) &&
+ ((rv = i_add_option("nodev", options, sizeof (options))) != 0))
+ return (rv);
+ if ((flags & LX_MS_NOEXEC) &&
+ ((rv = i_add_option("noexec", options, sizeof (options))) != 0))
+ return (rv);
+ if ((flags & LX_MS_NOATIME) &&
+ ((rv = i_add_option("noatime", options, sizeof (options))) != 0))
+ return (rv);
+
+ lx_debug("\tsolaris mount fstype: %s", fstype);
+ lx_debug("\tsolaris mount options: \"%s\"", options);
+
+ res = mount(source, target, sflags, fstype, sdataptr, sdatalen,
+ options, sizeof (options));
+
+ return ((res == 0) ? 0 : -errno);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/mount_nfs.c b/usr/src/lib/brand/lx/lx_brand/common/mount_nfs.c
new file mode 100644
index 0000000000..ca90a80e5f
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/mount_nfs.c
@@ -0,0 +1,1506 @@
+/*
+ * 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.
+ */
+
+/*
+ * NFS mount syscall support
+ *
+ * The illumos NFS user-level mount code encapsulates a significant amount of
+ * functionality into librpc and libnsl. This includes a variety of functions
+ * to perform lookups in the various /etc configuration files. For v2/v3 in
+ * particular, the library code must make a call to the server's 'mountd' to
+ * obtain a file handle to pass into the mount(2) syscall. There can be a
+ * variety of calls to the server's 'rpcbind' made by the libraries during the
+ * preliminaries before calling mount(2). All of the logic for falling back
+ * when determining which version to use (when none is explicitly provided), as
+ * well as retries when the server is not responding, is encapsulated in the
+ * libraries.
+ *
+ * For Linux, much of this functionality is also included in the user-level
+ * mount code, and thus not of concern to us at the Linux syscall level. The
+ * major difference is that the RPC to the 'mountd' to get the file handle for
+ * v2/v3 is made within the kernel as part of the mount(2) syscall. However,
+ * the Linux user-level code will perform all of the logical name lookups (e.g.
+ * hostname to IP address), will make the 'rpcbind' call to determine the
+ * server's 'mountd' protocol and port, and will handle the retry logic.
+ *
+ * Thus, when we reach our code here, we don't need to do any name lookups in
+ * any of the /etc files and we never need to call 'rpcbind'. We only need to
+ * make the RPC to get the file handle for v2/v3 mounts. We're still dependent
+ * on librpc/libnsl to make this RPC, but our overall complexity is much less
+ * than what is seen with the native mount library usage. In addition, we also
+ * have to convert the Linux mount arguments into our native format. Because
+ * we're really just making the RPC to get a file handle and reformatting the
+ * mount arguments, this code should be amenable to living in-kernel at some
+ * point.
+ *
+ * Finally, in most of the functions below, when the code refers to the
+ * hostname we're really working with the IP addr that the Linux user-level
+ * mount command passed in to us.
+ */
+
+/*
+ * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+/*
+ * Copyright 2017 Joyent, Inc.
+ */
+
+#define NFSCLIENT
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/mount.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <nfs/nfs.h>
+#include <nfs/mount.h>
+#include <rpcsvc/mount.h>
+#include <sys/pathconf.h>
+#include <netdir.h>
+#include <netconfig.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <nfs/nfs_sec.h>
+#include <rpcsvc/daemon_utils.h>
+#include <rpcsvc/nfs4_prot.h>
+#include <limits.h>
+#include <nfs/nfssys.h>
+#include <strings.h>
+#include <assert.h>
+#include <sys/lx_mount.h>
+#include <sys/lx_misc.h>
+#include <sys/syscall.h>
+
+#ifndef NFS_VERSMAX
+#define NFS_VERSMAX 4
+#endif
+#ifndef NFS_VERSMIN
+#define NFS_VERSMIN 2
+#endif
+
+#define RET_OK 0
+#define RET_RETRY 32
+#define RET_ERR 33
+#define RET_PROTOUNSUPP 34
+#define RET_MNTERR 1000
+#define ERR_PROTO_NONE 0
+#define ERR_PROTO_INVALID 901
+#define ERR_PROTO_UNSUPP 902
+#define ERR_NETPATH 903
+#define ERR_NOHOST 904
+#define ERR_RPCERROR 905
+
+typedef struct err_ret {
+ int error_type;
+ int error_value;
+} err_ret_t;
+
+#define SET_ERR_RET(errst, etype, eval) \
+ { \
+ (errst)->error_type = etype; \
+ (errst)->error_value = eval; \
+ }
+
+/*
+ * Built-in netconfig table.
+ */
+#define N_NETCONF_ENTS 4
+static struct netconfig nca[N_NETCONF_ENTS] = {
+ {"udp6", NC_TPI_CLTS, 1, "inet6", "udp", "/dev/udp6", 0, NULL},
+ {"tcp6", NC_TPI_COTS_ORD, 1, "inet6", "tcp", "/dev/tcp6", 0, NULL},
+ {"udp", NC_TPI_CLTS, 1, "inet", "udp", "/dev/udp", 0, NULL},
+ {"tcp", NC_TPI_COTS_ORD, 1, "inet", "tcp", "/dev/tcp", 0, NULL}
+};
+
+/*
+ * Mapping table of Linux NFS mount options to the corresponding Illumos
+ * option. The nmo_argtyp field tells us how to handle the argument.
+ */
+typedef enum map_mount_opt_type {
+ MOUNT_OPT_INVALID = 0,
+ MOUNT_OPT_PASTHRU = 1,
+ MOUNT_OPT_IGNORE = 2,
+ MOUNT_OPT_TOKEN = 3,
+ MOUNT_OPT_HAS_ARG = 4
+} map_mount_opt_type_t;
+
+typedef struct nfs_map_opt {
+ char *nmo_lx_opt;
+ char *nmo_il_opt;
+ map_mount_opt_type_t nmo_argtyp;
+} nfs_map_opt_t;
+
+static nfs_map_opt_t nmo_tab[] = {
+ {"ac", NULL, MOUNT_OPT_IGNORE},
+ {"acdirmax", NULL, MOUNT_OPT_PASTHRU},
+ {"acdirmin", NULL, MOUNT_OPT_PASTHRU},
+ {"acl", NULL, MOUNT_OPT_INVALID},
+ {"acregmax", NULL, MOUNT_OPT_PASTHRU},
+ {"acregmin", NULL, MOUNT_OPT_PASTHRU},
+ {"actimeo", NULL, MOUNT_OPT_PASTHRU},
+ {"bg", NULL, MOUNT_OPT_IGNORE},
+ {"cto", NULL, MOUNT_OPT_IGNORE},
+ {"fg", NULL, MOUNT_OPT_IGNORE},
+ {"fsc", NULL, MOUNT_OPT_IGNORE},
+ {"hard", NULL, MOUNT_OPT_PASTHRU},
+ {"intr", NULL, MOUNT_OPT_PASTHRU},
+ {"lock", NULL, MOUNT_OPT_IGNORE},
+ {"lookupcache", NULL, MOUNT_OPT_INVALID},
+ {"local_lock=%s", NULL, MOUNT_OPT_INVALID },
+ {"migration", NULL, MOUNT_OPT_INVALID},
+ {"minorversion", NULL, MOUNT_OPT_INVALID},
+ {"mountaddr", NULL, MOUNT_OPT_INVALID},
+ {"mounthost", NULL, MOUNT_OPT_INVALID},
+ {"mountport", NULL, MOUNT_OPT_PASTHRU},
+ {"mountproto", NULL, MOUNT_OPT_PASTHRU},
+ {"mountvers", NULL, MOUNT_OPT_PASTHRU},
+ {"namlen", NULL, MOUNT_OPT_INVALID},
+ {"nfsvers", NULL, MOUNT_OPT_INVALID},
+ {"noac", NULL, MOUNT_OPT_PASTHRU},
+ {"noacl", NULL, MOUNT_OPT_INVALID},
+ {"nocto", NULL, MOUNT_OPT_PASTHRU},
+ {"nofsc", NULL, MOUNT_OPT_IGNORE},
+ {"nointr", NULL, MOUNT_OPT_PASTHRU},
+ {"nolock", "llock", MOUNT_OPT_TOKEN},
+ {"nomigration", NULL, MOUNT_OPT_INVALID},
+ {"noposix", NULL, MOUNT_OPT_IGNORE},
+ {"nordirplus", NULL, MOUNT_OPT_IGNORE},
+ {"noresvport", NULL, MOUNT_OPT_INVALID},
+ {"nosharecache", NULL, MOUNT_OPT_IGNORE},
+ {"port", NULL, MOUNT_OPT_PASTHRU},
+ {"posix", NULL, MOUNT_OPT_PASTHRU},
+ {"proto", NULL, MOUNT_OPT_PASTHRU},
+ {"rdirplus", NULL, MOUNT_OPT_IGNORE},
+ {"rdma", "proto=rdma", MOUNT_OPT_TOKEN},
+ {"resvport", NULL, MOUNT_OPT_IGNORE},
+ {"retrans", NULL, MOUNT_OPT_PASTHRU},
+ {"retry", NULL, MOUNT_OPT_IGNORE},
+ {"rsize", NULL, MOUNT_OPT_PASTHRU},
+ {"sec", NULL, MOUNT_OPT_PASTHRU},
+ {"sharecache", NULL, MOUNT_OPT_IGNORE},
+ {"sloppy", NULL, MOUNT_OPT_IGNORE},
+ {"soft", NULL, MOUNT_OPT_PASTHRU},
+ {"tcp", "proto=tcp", MOUNT_OPT_TOKEN},
+ {"timeo", NULL, MOUNT_OPT_PASTHRU},
+ {"udp", "proto=udp", MOUNT_OPT_TOKEN},
+ {"vers", NULL, MOUNT_OPT_PASTHRU},
+ {"wsize", NULL, MOUNT_OPT_PASTHRU},
+ {NULL, NULL, MOUNT_OPT_INVALID}
+};
+
+/*
+ * This struct is used to keep track of misc. variables which are set deep
+ * in one function then referenced someplace else. We pass this around to
+ * avoid the use of global variables as is done the the NFS mount command.
+ *
+ * The nfsvers variables control the NFS version number to be used.
+ *
+ * nmd_nfsvers defaults to 0 which means to use the highest number that
+ * both the client and the server support. It can also be set to
+ * a particular value, either 2, 3, or 4 to indicate the version
+ * number of choice. If the server (or the client) do not support
+ * the version indicated, then the mount attempt will be failed.
+ */
+typedef struct nfs_mnt_data {
+ int nmd_posix;
+ ushort_t nmd_nfs_port;
+ char *nmd_nfs_proto;
+ ushort_t nmd_mnt_port;
+ char *nmd_mnt_proto;
+ char *nmd_fstype;
+ seconfig_t nmd_nfs_sec;
+ int nmd_sec_opt; /* any security option ? */
+ int nmd_nolock_opt; /* 'nolock' specified */
+ rpcvers_t nmd_mnt_vers;
+ rpcvers_t nmd_nfsvers;
+} nfs_mnt_data_t;
+
+/* number of transports to try */
+#define MNT_PREF_LISTLEN 2
+#define FIRST_TRY 1
+#define SECOND_TRY 2
+
+#define BIGRETRY 10000
+
+/* maximum length of RPC header for NFS messages */
+#define NFS_RPC_HDR 432
+
+extern int __clnt_bindresvport(CLIENT *);
+
+static int set_args(int *, struct nfs_args *, char *, char *, nfs_mnt_data_t *);
+static int get_fh(struct nfs_args *, char *, char *, nfs_mnt_data_t *);
+static int make_secure(struct nfs_args *, nfs_mnt_data_t *);
+static int getaddr_nfs(struct nfs_args *, char *, struct netconfig **,
+ nfs_mnt_data_t *);
+
+static void
+log_err(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[128];
+ int fd;
+
+ va_start(ap, fmt);
+ (void) vsnprintf(buf, sizeof (buf), fmt, ap);
+ va_end(ap);
+
+ if ((fd = open("/dev/conslog", O_WRONLY)) != -1) {
+ (void) write(fd, buf, strlen(buf));
+ (void) close(fd);
+ }
+}
+
+static int
+i_add_option(char *option, char *buf, size_t buf_size)
+{
+ int len;
+ char *fmt_str = NULL;
+
+ if (buf[0] == '\0') {
+ fmt_str = "%s";
+ } else {
+ fmt_str = ",%s";
+ }
+
+ len = strlen(buf);
+ buf_size -= len;
+ buf += len;
+
+ /*LINTED*/
+ if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1))
+ return (-EOVERFLOW);
+ return (0);
+}
+
+/*
+ * These options were initially derived from uts/common/fs/nfs/nfs_dlinet.c
+ * but have been extended to add additional Linux options.
+ */
+static char *optlist[] = {
+#define OPT_RO 0
+ MNTOPT_RO,
+#define OPT_RW 1
+ MNTOPT_RW,
+#define OPT_QUOTA 2
+ MNTOPT_QUOTA,
+#define OPT_NOQUOTA 3
+ MNTOPT_NOQUOTA,
+#define OPT_SOFT 4
+ MNTOPT_SOFT,
+#define OPT_HARD 5
+ MNTOPT_HARD,
+#define OPT_SUID 6
+ MNTOPT_SUID,
+#define OPT_NOSUID 7
+ MNTOPT_NOSUID,
+#define OPT_GRPID 8
+ MNTOPT_GRPID,
+#define OPT_REMOUNT 9
+ MNTOPT_REMOUNT,
+#define OPT_NOSUB 10
+ MNTOPT_NOSUB,
+#define OPT_INTR 11
+ MNTOPT_INTR,
+#define OPT_NOINTR 12
+ MNTOPT_NOINTR,
+#define OPT_PORT 13
+ MNTOPT_PORT,
+#define OPT_SECURE 14
+ MNTOPT_SECURE,
+#define OPT_RSIZE 15
+ MNTOPT_RSIZE,
+#define OPT_WSIZE 16
+ MNTOPT_WSIZE,
+#define OPT_TIMEO 17
+ MNTOPT_TIMEO,
+#define OPT_RETRANS 18
+ MNTOPT_RETRANS,
+#define OPT_ACTIMEO 19
+ MNTOPT_ACTIMEO,
+#define OPT_ACREGMIN 20
+ MNTOPT_ACREGMIN,
+#define OPT_ACREGMAX 21
+ MNTOPT_ACREGMAX,
+#define OPT_ACDIRMIN 22
+ MNTOPT_ACDIRMIN,
+#define OPT_ACDIRMAX 23
+ MNTOPT_ACDIRMAX,
+#define OPT_BG 24
+ MNTOPT_BG,
+#define OPT_FG 25
+ MNTOPT_FG,
+#define OPT_RETRY 26
+ MNTOPT_RETRY,
+#define OPT_NOAC 27
+ MNTOPT_NOAC,
+#define OPT_NOCTO 28
+ MNTOPT_NOCTO,
+#define OPT_LLOCK 29
+ MNTOPT_LLOCK,
+#define OPT_POSIX 30
+ MNTOPT_POSIX,
+#define OPT_VERS 31
+ MNTOPT_VERS,
+#define OPT_PROTO 32
+ MNTOPT_PROTO,
+#define OPT_SEMISOFT 33
+ MNTOPT_SEMISOFT,
+#define OPT_NOPRINT 34
+ MNTOPT_NOPRINT,
+#define OPT_SEC 35
+ MNTOPT_SEC,
+#define OPT_LARGEFILES 36
+ MNTOPT_LARGEFILES,
+#define OPT_NOLARGEFILES 37
+ MNTOPT_NOLARGEFILES,
+#define OPT_PUBLIC 38
+ MNTOPT_PUBLIC,
+#define OPT_DIRECTIO 39
+ MNTOPT_FORCEDIRECTIO,
+#define OPT_NODIRECTIO 40
+ MNTOPT_NOFORCEDIRECTIO,
+#define OPT_XATTR 41
+ MNTOPT_XATTR,
+#define OPT_NOXATTR 42
+ MNTOPT_NOXATTR,
+#define OPT_DEVICES 43
+ MNTOPT_DEVICES,
+#define OPT_NODEVICES 44
+ MNTOPT_NODEVICES,
+#define OPT_SETUID 45
+ MNTOPT_SETUID,
+#define OPT_NOSETUID 46
+ MNTOPT_NOSETUID,
+#define OPT_EXEC 47
+ MNTOPT_EXEC,
+#define OPT_NOEXEC 48
+ MNTOPT_NOEXEC,
+#define OPT_MNT_VERS 49
+ "mountvers",
+#define OPT_MNT_PORT 50
+ "mountport",
+#define OPT_MNT_PROTO 51
+ "mountproto",
+
+ NULL
+};
+
+static int
+convert_int(int *val, char *str)
+{
+ long lval;
+
+ if (str == NULL || !isdigit(*str))
+ return (-1);
+
+ lval = strtol(str, &str, 10);
+ if (*str != '\0' || lval > INT_MAX)
+ return (-2);
+
+ *val = (int)lval;
+ return (0);
+}
+
+static int
+set_args(int *mntflags, struct nfs_args *args, char *fshost, char *mntopts,
+ nfs_mnt_data_t *nmdp)
+{
+ char *saveopt, *optstr, *opts, *newopts, *val;
+ int num;
+ int largefiles = 0;
+ int invalid = 0;
+ int attrpref = 0;
+ int optlen, oldlen;
+
+ args->flags = NFSMNT_INT; /* default is "intr" */
+ args->flags |= NFSMNT_HOSTNAME;
+ args->flags |= NFSMNT_NEWARGS; /* using extented nfs_args structure */
+ args->hostname = fshost;
+
+ oldlen = strlen(mntopts);
+ optstr = opts = strdup(mntopts);
+ if (opts == NULL)
+ return (-ENOMEM);
+ /* sizeof (MNTOPT_XXX) includes one extra byte we may need for "," */
+ optlen = oldlen + sizeof (MNTOPT_XATTR) + 1;
+ if (optlen > MAX_MNTOPT_STR)
+ return (-EINVAL);
+
+ newopts = malloc(optlen);
+ if (opts == NULL || newopts == NULL) {
+ if (opts)
+ free(opts);
+ if (newopts)
+ free(newopts);
+ return (-EINVAL);
+ }
+ newopts[0] = '\0';
+
+ while (*opts) {
+ invalid = 0;
+ saveopt = opts;
+ switch (getsubopt(&opts, optlist, &val)) {
+ case OPT_RO:
+ *mntflags |= MS_RDONLY;
+ break;
+ case OPT_RW:
+ *mntflags &= ~(MS_RDONLY);
+ break;
+ case OPT_QUOTA:
+ case OPT_NOQUOTA:
+ break;
+ case OPT_SOFT:
+ args->flags |= NFSMNT_SOFT;
+ args->flags &= ~(NFSMNT_SEMISOFT);
+ break;
+ case OPT_SEMISOFT:
+ args->flags |= NFSMNT_SOFT;
+ args->flags |= NFSMNT_SEMISOFT;
+ break;
+ case OPT_HARD:
+ args->flags &= ~(NFSMNT_SOFT);
+ args->flags &= ~(NFSMNT_SEMISOFT);
+ break;
+ case OPT_SUID:
+ *mntflags &= ~(MS_NOSUID);
+ break;
+ case OPT_NOSUID:
+ *mntflags |= MS_NOSUID;
+ break;
+ case OPT_GRPID:
+ args->flags |= NFSMNT_GRPID;
+ break;
+ case OPT_REMOUNT:
+ *mntflags |= MS_REMOUNT;
+ break;
+ case OPT_INTR:
+ args->flags |= NFSMNT_INT;
+ break;
+ case OPT_NOINTR:
+ args->flags &= ~(NFSMNT_INT);
+ break;
+ case OPT_NOAC:
+ args->flags |= NFSMNT_NOAC;
+ break;
+ case OPT_PORT:
+ if (convert_int(&num, val) != 0)
+ goto badopt;
+ nmdp->nmd_nfs_port = num;
+ break;
+
+ case OPT_NOCTO:
+ args->flags |= NFSMNT_NOCTO;
+ break;
+
+ case OPT_RSIZE:
+ if (convert_int(&args->rsize, val) != 0)
+ goto badopt;
+ args->flags |= NFSMNT_RSIZE;
+ break;
+ case OPT_WSIZE:
+ if (convert_int(&args->wsize, val) != 0)
+ goto badopt;
+ args->flags |= NFSMNT_WSIZE;
+ break;
+ case OPT_TIMEO:
+ if (convert_int(&args->timeo, val) != 0)
+ goto badopt;
+ args->flags |= NFSMNT_TIMEO;
+ break;
+ case OPT_RETRANS:
+ if (convert_int(&args->retrans, val) != 0)
+ goto badopt;
+ args->flags |= NFSMNT_RETRANS;
+ break;
+ case OPT_ACTIMEO:
+ if (convert_int(&args->acregmax, val) != 0)
+ goto badopt;
+ args->acdirmin = args->acregmin = args->acdirmax
+ = args->acregmax;
+ args->flags |= NFSMNT_ACDIRMAX;
+ args->flags |= NFSMNT_ACREGMAX;
+ args->flags |= NFSMNT_ACDIRMIN;
+ args->flags |= NFSMNT_ACREGMIN;
+ break;
+ case OPT_ACREGMIN:
+ if (convert_int(&args->acregmin, val) != 0)
+ goto badopt;
+ args->flags |= NFSMNT_ACREGMIN;
+ break;
+ case OPT_ACREGMAX:
+ if (convert_int(&args->acregmax, val) != 0)
+ goto badopt;
+ args->flags |= NFSMNT_ACREGMAX;
+ break;
+ case OPT_ACDIRMIN:
+ if (convert_int(&args->acdirmin, val) != 0)
+ goto badopt;
+ args->flags |= NFSMNT_ACDIRMIN;
+ break;
+ case OPT_ACDIRMAX:
+ if (convert_int(&args->acdirmax, val) != 0)
+ goto badopt;
+ args->flags |= NFSMNT_ACDIRMAX;
+ break;
+ case OPT_BG:
+ /* Ignored as does Linux kernel */
+ break;
+ case OPT_FG:
+ /* Ignored as does Linux kernel */
+ break;
+ case OPT_RETRY:
+ /* Ignored as does Linux kernel */
+ break;
+ case OPT_LLOCK:
+ args->flags |= NFSMNT_LLOCK;
+ break;
+ case OPT_POSIX:
+ nmdp->nmd_posix = 1;
+ break;
+ case OPT_VERS:
+ if (convert_int(&num, val) != 0)
+ goto badopt;
+ nmdp->nmd_nfsvers = (rpcvers_t)num;
+ break;
+ case OPT_PROTO:
+ if (val == NULL)
+ goto badopt;
+
+ nmdp->nmd_nfs_proto = (char *)malloc(strlen(val)+1);
+ if (!nmdp->nmd_nfs_proto)
+ return (-EINVAL);
+
+ (void) strncpy(nmdp->nmd_nfs_proto, val, strlen(val)+1);
+ break;
+
+ case OPT_NOPRINT:
+ args->flags |= NFSMNT_NOPRINT;
+ break;
+
+ case OPT_LARGEFILES:
+ largefiles = 1;
+ break;
+
+ case OPT_NOLARGEFILES:
+ free(optstr);
+ return (-EINVAL);
+
+ case OPT_SEC:
+ if (val == NULL)
+ return (-EINVAL);
+ /*
+ * We initialize the nfs_sec struct as if we had the
+ * basic /etc/nfssec.conf file.
+ */
+ if (strcmp(val, "none") == 0) {
+ (void) strlcpy(nmdp->nmd_nfs_sec.sc_name,
+ "none", MAX_NAME_LEN);
+ nmdp->nmd_nfs_sec.sc_nfsnum =
+ nmdp->nmd_nfs_sec.sc_rpcnum = 0;
+ } else if (strcmp(val, "sys") == 0) {
+ (void) strlcpy(nmdp->nmd_nfs_sec.sc_name,
+ "sys", MAX_NAME_LEN);
+ nmdp->nmd_nfs_sec.sc_nfsnum =
+ nmdp->nmd_nfs_sec.sc_rpcnum = 1;
+ } else {
+ return (-EINVAL);
+ }
+ nmdp->nmd_sec_opt++;
+ break;
+
+ case OPT_DIRECTIO:
+ args->flags |= NFSMNT_DIRECTIO;
+ break;
+
+ case OPT_NODIRECTIO:
+ args->flags &= ~(NFSMNT_DIRECTIO);
+ break;
+
+ case OPT_XATTR:
+ case OPT_NOXATTR:
+ /*
+ * VFS options; just need to get them into the
+ * new mount option string and note we've seen them
+ */
+ attrpref = 1;
+ break;
+
+ case OPT_MNT_VERS:
+ if (convert_int(&num, val) != 0)
+ goto badopt;
+ nmdp->nmd_mnt_vers = (rpcvers_t)num;
+ invalid = 1; /* Invalid as a native option */
+ break;
+
+ case OPT_MNT_PORT:
+ if (convert_int(&num, val) != 0)
+ goto badopt;
+ nmdp->nmd_mnt_port = num;
+ invalid = 1; /* Invalid as a native option */
+ break;
+
+ case OPT_MNT_PROTO:
+ if (val == NULL)
+ goto badopt;
+ nmdp->nmd_mnt_proto = strdup(val);
+ if (nmdp->nmd_mnt_proto == NULL)
+ return (-ENOMEM);
+ invalid = 1; /* Invalid as a native option */
+ break;
+
+ default:
+ invalid = 1;
+ break;
+ }
+ if (!invalid) {
+ if (newopts[0] != '\0') {
+ (void) strlcat(newopts, ",", optlen);
+ }
+ (void) strlcat(newopts, saveopt, optlen);
+ }
+ }
+ /* Default is to turn extended attrs on */
+ if (!attrpref) {
+ if (newopts[0]) {
+ (void) strlcat(newopts, ",", optlen);
+ }
+ (void) strlcat(newopts, MNTOPT_XATTR, optlen);
+ }
+ (void) strlcpy(mntopts, newopts, oldlen);
+ free(newopts);
+ free(optstr);
+
+ /* ensure that only one secure mode is requested */
+ if (nmdp->nmd_sec_opt > 1)
+ return (-EINVAL);
+
+ /* ensure that the user isn't trying to get large files over V2 */
+ if (nmdp->nmd_nfsvers == NFS_VERSION && largefiles)
+ return (-EINVAL);
+
+ if (nmdp->nmd_nfsvers == NFS_V4) {
+ /*
+ * NFSv4 specifies the TCP protocol and port 2049 - default to
+ * these. The user-level mount code is not expected to pass
+ * these in, but if it did, validate the proto value.
+ */
+ if (nmdp->nmd_nfs_proto == NULL) {
+ nmdp->nmd_nfs_proto = strdup(NC_TCP);
+ if (nmdp->nmd_nfs_proto == NULL)
+ return (-ENOMEM);
+
+ } else if (strcmp(nmdp->nmd_nfs_proto, NC_TCP) != 0) {
+ return (-EINVAL);
+ }
+
+ } else {
+ /*
+ * The user-level mount code normally passes in the proto, but
+ * if it didn't for some reason, use a sensible default.
+ * Otherwise we normally just validate the proto value and we
+ * only support TCP or UDP.
+ */
+ if (nmdp->nmd_nfs_proto == NULL) {
+ nmdp->nmd_nfs_proto = strdup(NC_TCP);
+ if (nmdp->nmd_nfs_proto == NULL)
+ return (-ENOMEM);
+
+ } else if (strcmp(nmdp->nmd_nfs_proto, NC_TCP) != 0 &&
+ strcmp(nmdp->nmd_nfs_proto, NC_UDP) != 0) {
+ return (-EINVAL);
+ }
+ }
+
+ /*
+ * The user-level mount code only passes the port when it is
+ * non-standard.
+ */
+ if (nmdp->nmd_nfs_port == 0) {
+ nmdp->nmd_nfs_port = NFS_PORT;
+ }
+
+ return (0);
+
+badopt:
+ free(optstr);
+ return (-EINVAL);
+}
+
+static int
+make_secure(struct nfs_args *args, nfs_mnt_data_t *nmdp)
+{
+ sec_data_t *secdata;
+
+ /*
+ * Check to see if any secure mode is requested. If not, use default
+ * security mode. Note: we currently only support sec=none and sec=sys.
+ */
+ if (nmdp->nmd_sec_opt == 0) {
+ /* AUTH_UNIX is the default. */
+ (void) strlcpy(nmdp->nmd_nfs_sec.sc_name, "sys", MAX_NAME_LEN);
+ nmdp->nmd_nfs_sec.sc_nfsnum = nmdp->nmd_nfs_sec.sc_rpcnum = 1;
+ args->flags |= NFSMNT_SECDEFAULT;
+ }
+
+ secdata = malloc(sizeof (sec_data_t));
+ if (secdata == NULL)
+ return (-ENOMEM);
+
+ (void) memset(secdata, 0, sizeof (sec_data_t));
+
+ secdata->secmod = nmdp->nmd_nfs_sec.sc_nfsnum;
+ secdata->rpcflavor = nmdp->nmd_nfs_sec.sc_rpcnum;
+ secdata->uid = nmdp->nmd_nfs_sec.sc_uid;
+ secdata->flags = 0;
+ secdata->data = NULL;
+
+ args->nfs_args_ext = NFS_ARGS_EXTB;
+ args->nfs_ext_u.nfs_extB.secdata = secdata;
+
+ return (0);
+}
+
+/*
+ * Use our built-in netconfig table to lookup and construct a netconfig struct
+ * for the given netid.
+ */
+static struct netconfig *
+get_netconf(char *id)
+{
+ int i;
+ struct netconfig *nconf, *np;
+
+ if ((nconf = calloc(1, sizeof (struct netconfig))) == NULL)
+ return (NULL);
+
+ for (i = 0; i < N_NETCONF_ENTS; i++) {
+ np = &nca[i];
+ if (strcmp(np->nc_netid, id) != 0)
+ continue;
+
+ nconf->nc_semantics = np->nc_semantics;
+ if ((nconf->nc_netid = strdup(np->nc_netid)) == NULL)
+ goto out;
+ if ((nconf->nc_protofmly = strdup(np->nc_protofmly)) == NULL)
+ goto out;
+ if ((nconf->nc_proto = strdup(np->nc_proto)) == NULL)
+ goto out;
+ if ((nconf->nc_device = strdup(np->nc_device)) == NULL)
+ goto out;
+
+ return (nconf);
+ }
+
+out:
+ freenetconfigent(nconf);
+ return (NULL);
+}
+
+/*
+ * If the user provided a logical name for the NFS server, then the user-level
+ * mount command will have already resolved that name and passed it in using
+ * the 'addr' option (see convert_nfs_arg_str where we've already handled this).
+ * We construct a netbuf from that provided IP and a given port option.
+ *
+ * Note: this code may need to be revisited when we add IPv6 support.
+ */
+static struct netbuf *
+get_netbuf(struct netconfig *nconf, char *ip, ushort_t port)
+{
+ char buf[64];
+
+ assert(port != 0);
+ (void) snprintf(buf, sizeof (buf), "%s.%d.%d", ip,
+ port >> 8 & 0xff, port & 0xff);
+ return (uaddr2taddr(nconf, buf));
+}
+
+/*
+ * Construct a CLIENT handle to talk to the mountd without having to contact
+ * the rpcbind daemon on the server. This works for both the TCP and UDP cases,
+ * but it is primarily intended to the handle the TCP case. As an aside, note
+ * that TCP is never used by the native NFS mount client, even when the
+ * 'proto=tcp' argument is given to the mount command. The native mount code
+ * always uses UDP to get a file handle from the mountd.
+ */
+static CLIENT *
+get_mountd_client(char *fshost, nfs_mnt_data_t *nmdp, int *fdp)
+{
+ struct netconfig *nconf;
+ struct netbuf *srvaddr;
+ CLIENT *cl = NULL;
+ rpcvers_t vers;
+ int fd;
+ struct t_bind *tbind = NULL;
+ struct t_info tinfo;
+
+ vers = nmdp->nmd_mnt_vers;
+ *fdp = -1;
+
+ if ((nconf = get_netconf(nmdp->nmd_mnt_proto)) == NULL)
+ return (NULL);
+
+ if ((srvaddr = get_netbuf(nconf, fshost, nmdp->nmd_mnt_port)) == NULL) {
+ freenetconfigent(nconf);
+ return (NULL);
+ }
+
+ tinfo.tsdu = 0;
+ if ((fd = t_open(nconf->nc_device, O_RDWR, &tinfo)) == -1)
+ goto done;
+
+ /* LINTED pointer alignment */
+ if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL) {
+ (void) t_close(fd);
+ goto done;
+ }
+
+ /* assign our srvaddr to tbind addr */
+ (void) memcpy(tbind->addr.buf, srvaddr->buf, srvaddr->len);
+ tbind->addr.len = srvaddr->len;
+
+ /*
+ * For compatibility, the mountd call to get the file handle must come
+ * from a privileged port.
+ */
+ (void) netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL);
+
+ cl = clnt_tli_create(fd, nconf, &tbind->addr, MOUNTPROG, vers, 0, 0);
+ if (cl == NULL) {
+ (void) t_close(fd);
+ /*
+ * Unfortunately, a failure in the:
+ * clnt_tli_create -> set_up_connection -> t_connect
+ * call path doesn't return any useful information about why we
+ * had an error. We basically see the following rpc_createerr
+ * status for a variety of conditions (e.g. invalid port, no
+ * service at IP, timeout, etc.):
+ * rpc_createerr.cf_stat == RPC_TLIERROR
+ * rpc_createerr.cf_error.re_terrno == 9 (TLOOK)
+ * rpc_createerr.cf_error.re_errno == 0
+ */
+ } else {
+ *fdp = fd;
+ }
+
+done:
+ if (tbind != NULL)
+ (void) t_free((char *)tbind, T_BIND);
+ free(srvaddr->buf);
+ free(srvaddr);
+ freenetconfigent(nconf);
+
+ return (cl);
+}
+
+static int
+get_fh_cleanup(CLIENT *cl, int fd, int err)
+{
+ if (cl != NULL)
+ clnt_destroy(cl);
+ if (fd != -1)
+ (void) t_close(fd);
+ assert(err <= 0);
+ return (err);
+}
+
+/*
+ * Get fhandle of remote path from server's mountd. This is only applicable to
+ * v2 or v3.
+ */
+static int
+get_fh(struct nfs_args *args, char *fshost, char *fspath, nfs_mnt_data_t *nmdp)
+{
+ struct fhstatus fhs;
+ struct mountres3 mountres3;
+ struct pathcnf p;
+ nfs_fh3 *fh3p;
+ struct timeval timeout = { 25, 0};
+ CLIENT *cl = NULL;
+ int fd = -1;
+ enum clnt_stat rpc_stat;
+ int count, i, *auths;
+
+ bzero(&fhs, sizeof (fhs));
+ bzero(&mountres3, sizeof (mountres3));
+ bzero(&p, sizeof (p));
+
+ /*
+ * The user-level mount code should have contacted the server's rpcbind
+ * daemon and passed us the mount protocol and port.
+ */
+ if (nmdp->nmd_mnt_port == 0 || nmdp->nmd_mnt_proto == NULL ||
+ nmdp->nmd_mnt_vers == 0) {
+ return (-EAGAIN);
+ }
+
+ cl = get_mountd_client(fshost, nmdp, &fd);
+ if (cl == NULL) {
+ /*
+ * As noted in get_mountd_client, we don't get a good indication
+ * as to why the connection failed. Linux returns ETIMEDOUT
+ * under many of the same conditions, and our native code notes
+ * that this is a common reason, so we do that here too.
+ */
+ return (-ETIMEDOUT);
+ }
+
+ if ((cl->cl_auth = authsys_create_default()) == NULL) {
+ return (get_fh_cleanup(cl, fd, -EAGAIN));
+ }
+
+ if (nmdp->nmd_mnt_vers == 2) {
+ rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
+ (caddr_t)&fspath, xdr_fhstatus, (caddr_t)&fhs, timeout);
+ if (rpc_stat != RPC_SUCCESS) {
+ log_err("%s:%s: server not responding %s\n",
+ fshost, fspath, clnt_sperror(cl, ""));
+ return (get_fh_cleanup(cl, fd, -EAGAIN));
+ }
+
+ if ((errno = fhs.fhs_status) != MNT_OK) {
+ return (get_fh_cleanup(cl, fd, -fhs.fhs_status));
+ }
+ args->fh = malloc(sizeof (fhs.fhstatus_u.fhs_fhandle));
+ if (args->fh == NULL)
+ return (get_fh_cleanup(cl, fd, -EAGAIN));
+
+ (void) memcpy((caddr_t)args->fh,
+ (caddr_t)&fhs.fhstatus_u.fhs_fhandle,
+ sizeof (fhs.fhstatus_u.fhs_fhandle));
+ if (!errno && nmdp->nmd_posix) {
+ rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
+ xdr_dirpath, (caddr_t)&fspath, xdr_ppathcnf,
+ (caddr_t)&p, timeout);
+ if (rpc_stat != RPC_SUCCESS) {
+ log_err("%s:%s: server not responding %s\n",
+ fshost, fspath, clnt_sperror(cl, ""));
+ free(args->fh);
+ return (get_fh_cleanup(cl, fd, -EAGAIN));
+ }
+ if (_PC_ISSET(_PC_ERROR, p.pc_mask)) {
+ free(args->fh);
+ return (get_fh_cleanup(cl, fd, -EAGAIN));
+ }
+ args->flags |= NFSMNT_POSIX;
+ args->pathconf = malloc(sizeof (p));
+ if (args->pathconf == NULL) {
+ free(args->fh);
+ return (get_fh_cleanup(cl, fd, -EAGAIN));
+ }
+ (void) memcpy((caddr_t)args->pathconf, (caddr_t)&p,
+ sizeof (p));
+ }
+
+ } else { /* nmdp->nmd_mnt_vers == 3 */
+
+ rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
+ (caddr_t)&fspath, xdr_mountres3, (caddr_t)&mountres3,
+ timeout);
+ if (rpc_stat != RPC_SUCCESS) {
+ log_err("%s:%s: server not responding %s\n",
+ fshost, fspath, clnt_sperror(cl, ""));
+ return (get_fh_cleanup(cl, fd, -EAGAIN));
+ }
+
+ /*
+ * Assume here that most of the MNT3ERR_*
+ * codes map into E* errors. See the nfsstat enum for values.
+ */
+ if ((errno = mountres3.fhs_status) != MNT_OK) {
+ return (get_fh_cleanup(cl, fd, -mountres3.fhs_status));
+ }
+
+ fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
+ if (fh3p == NULL)
+ return (get_fh_cleanup(cl, fd, -EAGAIN));
+
+ fh3p->fh3_length =
+ mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len;
+ (void) memcpy(fh3p->fh3_u.data,
+ mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val,
+ fh3p->fh3_length);
+ args->fh = (caddr_t)fh3p;
+ nmdp->nmd_fstype = MNTTYPE_NFS3;
+
+ /*
+ * If "sec=flavor" is a mount option, check if the server
+ * supports the "flavor". If the server does not support the
+ * flavor, return error. It is unlikely that the server will
+ * not support "sys", although "none" may not be allowed.
+ */
+ auths =
+ mountres3.mountres3_u.mountinfo.auth_flavors
+ .auth_flavors_val;
+ count =
+ mountres3.mountres3_u.mountinfo.auth_flavors
+ .auth_flavors_len;
+
+ if (count <= 0) {
+ return (get_fh_cleanup(cl, fd, -EAGAIN));
+ }
+
+ if (nmdp->nmd_sec_opt) {
+ for (i = 0; i < count; i++) {
+ if (auths[i] == nmdp->nmd_nfs_sec.sc_nfsnum)
+ break;
+ }
+ if (i == count)
+ return (get_fh_cleanup(cl, fd, -EACCES));
+ } else {
+ /* AUTH_SYS is our default. */
+ (void) strlcpy(nmdp->nmd_nfs_sec.sc_name, "sys",
+ MAX_NAME_LEN);
+ nmdp->nmd_nfs_sec.sc_nfsnum =
+ nmdp->nmd_nfs_sec.sc_rpcnum = 1;
+ }
+ }
+
+ return (get_fh_cleanup(cl, fd, 0));
+}
+
+/*
+ * Fill in the address for the server's NFS service and fill in a knetconfig
+ * structure for the transport that the service is available on.
+ */
+static int
+getaddr_nfs(struct nfs_args *args, char *fshost, struct netconfig **nconfp,
+ nfs_mnt_data_t *nmdp)
+{
+ struct stat sb;
+ struct netconfig *nconf = NULL;
+ struct knetconfig *knconfp;
+ struct t_info tinfo;
+ err_ret_t addr_error;
+
+ SET_ERR_RET(&addr_error, ERR_PROTO_NONE, 0);
+
+ /*
+ * Given the values passed in from the user-land mount command (or our
+ * built-in defaults), we should have the necessary NFS host address
+ * info. We have already validated that we have a supported
+ * nmd_nfs_proto.
+ */
+ assert(nmdp->nmd_nfs_port != 0);
+ assert(nmdp->nmd_nfs_proto != NULL);
+ nconf = get_netconf(nmdp->nmd_nfs_proto);
+ assert(nconf != NULL);
+ args->addr = get_netbuf(nconf, fshost, nmdp->nmd_nfs_port);
+ if (args->addr == NULL)
+ return (-ENOMEM);
+ *nconfp = nconf;
+ tinfo.tsdu = 0;
+
+ /* This shouldn't fail unless the zone is misconfigured */
+ if (stat(nconf->nc_device, &sb) < 0)
+ return (-ENOSR);
+
+ knconfp = (struct knetconfig *)malloc(sizeof (*knconfp));
+ if (!knconfp)
+ return (-ENOMEM);
+
+ knconfp->knc_semantics = nconf->nc_semantics;
+ knconfp->knc_protofmly = nconf->nc_protofmly;
+ knconfp->knc_proto = nconf->nc_proto;
+ knconfp->knc_rdev = sb.st_rdev;
+ args->flags |= NFSMNT_KNCONF;
+ args->knconf = knconfp;
+
+ /* make sure we don't overload the transport */
+ if (tinfo.tsdu > 0 && tinfo.tsdu < NFS_MAXDATA + NFS_RPC_HDR) {
+ args->flags |= (NFSMNT_RSIZE | NFSMNT_WSIZE);
+ if (args->rsize == 0 || args->rsize > tinfo.tsdu - NFS_RPC_HDR)
+ args->rsize = tinfo.tsdu - NFS_RPC_HDR;
+ if (args->wsize == 0 || args->wsize > tinfo.tsdu - NFS_RPC_HDR)
+ args->wsize = tinfo.tsdu - NFS_RPC_HDR;
+ }
+
+ return (0);
+}
+
+static int
+append_opt(char *optstr, int len, char *k, char *v)
+{
+ int i;
+
+ for (i = 0; nmo_tab[i].nmo_lx_opt != NULL; i++) {
+ if (strcmp(k, nmo_tab[i].nmo_lx_opt) == 0) {
+ switch (nmo_tab[i].nmo_argtyp) {
+ case MOUNT_OPT_INVALID:
+ lx_unsupported("invalid NFS mount option: %s",
+ k);
+ return (-EINVAL);
+
+ case MOUNT_OPT_PASTHRU:
+ if (*optstr != '\0')
+ (void) strlcat(optstr, ",", len);
+ if (v == NULL) {
+ (void) strlcat(optstr, k, len);
+ } else {
+ (void) strlcat(optstr, k, len);
+ (void) strlcat(optstr, "=", len);
+ (void) strlcat(optstr, v, len);
+ }
+ break;
+
+ case MOUNT_OPT_IGNORE:
+ break;
+
+ case MOUNT_OPT_TOKEN:
+ if (*optstr != '\0')
+ (void) strlcat(optstr, ",", len);
+ (void) strlcat(optstr,
+ nmo_tab[i].nmo_il_opt, len);
+ break;
+
+ case MOUNT_OPT_HAS_ARG:
+ if (*optstr != '\0')
+ (void) strlcat(optstr, ",", len);
+ (void) strlcat(optstr,
+ nmo_tab[i].nmo_il_opt, len);
+ (void) strlcat(optstr, "=", len);
+ (void) strlcat(optstr, v, len);
+ break;
+ }
+ break;
+ }
+ }
+
+ return (0);
+}
+
+static int
+get_nfs_kv(char *vs, char **kp, char **vp)
+{
+ char *p;
+
+ p = strchr(vs, '=');
+ if (p == NULL) {
+ *kp = vs;
+ return (1);
+ }
+
+ *vp = p + 1;
+ *p = '\0';
+ *kp = vs;
+ return (0);
+}
+
+/*
+ * Convert the Linux-specific opt string into an Illumos opt string. We also
+ * fix up the special string (host:/path) to use the address that the
+ * user-level mount code has looked up. This overwrites both the srcp special
+ * string and the mntopts string.
+ *
+ * example input string, given 'nolock' as the only user-level option:
+ * nolock,vers=4,addr=127.0.0.1,clientaddr=0.0.0.0
+ *
+ * opt string (all one line) from a Centos 6 distro given 'nolock,vers=3' as
+ * the explicit options:
+ * nolock,addr=127.0.0.1,vers=3,proto=tcp,mountvers=3,mountproto=tcp,
+ * mountport=1892
+ *
+ * This is an example emitted by the Ubuntu 14.04 automounter for an explicit
+ * v3 mount:
+ * timeo=60,soft,intr,sloppy,addr=10.88.88.200,vers=3,proto=tcp,
+ * mountvers=3,mountproto=tcp,mountport=63484
+ */
+static int
+convert_nfs_arg_str(char *srcp, char *mntopts, nfs_mnt_data_t *nmdp)
+{
+ char *key, *val, *p;
+ char tmpbuf[MAX_MNTOPT_STR];
+ char *tbp = tmpbuf;
+ boolean_t no_sec = B_TRUE;
+ boolean_t no_addr = B_TRUE;
+
+ (void) strlcpy(tmpbuf, mntopts, sizeof (tmpbuf));
+ *mntopts = '\0';
+
+ while ((p = strsep(&tbp, ",")) != NULL) {
+ int tok;
+
+ tok = get_nfs_kv(p, &key, &val);
+
+ if (tok == 0) {
+ if (strcmp(key, "addr") == 0) {
+ /*
+ * The Linux user-level code looked up the
+ * address of the NFS server. We need to
+ * substitute that into the special string.
+ */
+ char *pp;
+ char spec[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN
+ + 1];
+
+ (void) strlcpy(spec, srcp, sizeof (spec));
+ pp = strchr(spec, ':');
+ if (pp == NULL)
+ return (-EINVAL);
+
+ pp++;
+ (void) snprintf(srcp,
+ MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1,
+ "%s:%s", val, pp);
+
+ no_addr = B_FALSE;
+ } else if (strcmp(key, "clientaddr") == 0) {
+ /*
+ * Ignore, this is an artifact of the
+ * user-level lx mount code.
+ */
+ /* EMPTY */
+ } else if (strcmp(key, "vers") == 0) {
+ /*
+ * This may be implicitly or explicitly passed.
+ * Check for the versions we want to support.
+ */
+ int r;
+ int v = atoi(val);
+
+ if (v != 3 && v != 4)
+ return (-EINVAL);
+
+ r = append_opt(mntopts, MAX_MNTOPT_STR,
+ key, val);
+ if (r != 0)
+ return (r);
+
+ } else if (strcmp(key, "sec") == 0) {
+ /*
+ * Linux supports: none, sys, krb5, krb5i, and
+ * krb5p. Of these, only none and sys overlap
+ * with our current support. Anything else is
+ * an error.
+ */
+ int r;
+
+ if (strcmp(val, "none") != 0 &&
+ strcmp(val, "sys") != 0)
+ return (-EINVAL);
+ r = append_opt(mntopts, MAX_MNTOPT_STR, key,
+ val);
+ if (r != 0)
+ return (r);
+ no_sec = B_FALSE;
+ } else if (strcmp(key, "nolock") == 0) {
+ int r;
+ nmdp->nmd_nolock_opt = 1;
+ r = append_opt(mntopts, MAX_MNTOPT_STR, key,
+ val);
+ if (r != 0)
+ return (r);
+ } else {
+ int r;
+
+ r = append_opt(mntopts, MAX_MNTOPT_STR,
+ key, val);
+ if (r != 0)
+ return (r);
+ }
+ } else {
+ int r;
+
+ r = append_opt(mntopts, MAX_MNTOPT_STR, key, NULL);
+ if (r != 0)
+ return (r);
+ }
+ }
+
+ if (no_addr) {
+ /*
+ * The Linux kernel requires an 'addr' option and will return
+ * EINVAL if one has not been provided. In particular, this
+ * behavior can be seen when the package which delivers NFS CLI
+ * support (e.g. nfs-common on Ubuntu, nfs-utils on Centos,
+ * etc.) is not installed. The generic mount command will not
+ * implicitly pass in the 'addr' option, the kernel will return
+ * EINVAL, and the mount will fail.
+ */
+ return (-EINVAL);
+ }
+
+ if (no_sec) {
+ /*
+ * XXX Temporarily work around missing DES auth by defaulting
+ * to sec=sys.
+ */
+ int r;
+
+ r = append_opt(mntopts, MAX_MNTOPT_STR, "sec", "sys");
+ if (r != 0)
+ return (r);
+ }
+
+ return (0);
+}
+
+int
+lx_nfs_mount(char *srcp, char *mntp, char *fst, int lx_flags, char *opts)
+{
+ int r;
+ int il_flags = 0;
+ nfs_mnt_data_t nmd, *nmdp = &nmd;
+ struct nfs_args *argp = NULL;
+ struct netconfig *nconf = NULL;
+ char *colonp;
+ char *path;
+ char *host;
+ char spec_buf[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1];
+
+ bzero(&nmd, sizeof (nmd));
+ nmd.nmd_fstype = MNTTYPE_NFS;
+
+ /*
+ * This will modify the special string so that the hostname passed
+ * in will be replaced with the host address that the user-land code
+ * looked up. This also converts the opts string so that we'll be
+ * dealing with illumos options after this.
+ */
+ if ((r = convert_nfs_arg_str(srcp, opts, nmdp)) < 0) {
+ return (r);
+ }
+
+ if (strcmp(fst, "nfs4") == 0)
+ nmdp->nmd_nfsvers = NFS_V4;
+
+ /* Linux seems to always allow overlay mounts */
+ il_flags |= MS_OVERLAY;
+
+ /* Convert some Linux flags to Illumos flags. */
+ if (lx_flags & LX_MS_RDONLY)
+ il_flags |= MS_RDONLY;
+ if (lx_flags & LX_MS_NOSUID)
+ il_flags |= MS_NOSUID;
+ if (lx_flags & LX_MS_REMOUNT)
+ il_flags |= MS_REMOUNT;
+
+ /*
+ * Convert some Linux flags to Illumos option strings.
+ */
+ if (lx_flags & LX_MS_STRICTATIME) {
+ /*
+ * The "strictatime" mount option ensures that none of the
+ * weaker atime-related mode options are in effect.
+ */
+ lx_flags &= ~(LX_MS_RELATIME | LX_MS_NOATIME);
+ }
+ if ((lx_flags & LX_MS_NODEV) &&
+ ((r = i_add_option("nodev", opts, MAX_MNTOPT_STR)) != 0))
+ return (r);
+ if ((lx_flags & LX_MS_NOEXEC) &&
+ ((r = i_add_option("noexec", opts, MAX_MNTOPT_STR)) != 0))
+ return (r);
+ if ((lx_flags & LX_MS_NOATIME) &&
+ ((r = i_add_option("noatime", opts, MAX_MNTOPT_STR)) != 0))
+ return (r);
+
+ (void) strlcpy(spec_buf, srcp, sizeof (spec_buf));
+ colonp = strchr(spec_buf, ':');
+ if (colonp == NULL)
+ return (-EINVAL);
+
+ *colonp = '\0';
+ host = spec_buf;
+ path = colonp + 1;
+
+ argp = (struct nfs_args *)malloc(sizeof (*argp));
+ if (argp == NULL)
+ return (-ENOMEM);
+
+ (void) memset(argp, 0, sizeof (*argp));
+ (void) memset(&nmdp->nmd_nfs_sec, 0, sizeof (seconfig_t));
+ nmdp->nmd_sec_opt = 0;
+
+ /* returns a negative errno */
+ if ((r = set_args(&il_flags, argp, host, opts, nmdp)) != 0)
+ goto out;
+
+ if (nmdp->nmd_nfsvers == NFS_V4) {
+ /*
+ * In the case of version 4 there is no MOUNT program, thus no
+ * need for an RPC to get a file handle.
+ */
+ nmdp->nmd_fstype = MNTTYPE_NFS4;
+ argp->fh = strdup(path);
+ if (argp->fh == NULL) {
+ r = -ENOMEM;
+ goto out;
+ }
+ } else {
+ if ((r = get_fh(argp, host, path, nmdp)) < 0)
+ goto out;
+ }
+
+ if ((r = getaddr_nfs(argp, host, &nconf, nmdp)) < 0)
+ goto out;
+
+ if ((r = make_secure(argp, nmdp)) < 0)
+ goto out;
+
+ il_flags |= MS_DATA | MS_OPTIONSTR;
+
+ r = mount(srcp, mntp, il_flags, nmdp->nmd_fstype, argp, sizeof (*argp),
+ opts, MAX_MNTOPT_STR);
+ if (r != 0) {
+ r = -errno;
+ } else if (nmdp->nmd_nolock_opt == 0) {
+ (void) syscall(SYS_brand, B_START_NFS_LOCKD);
+ }
+
+out:
+ if (nconf != NULL)
+ freenetconfigent(nconf);
+ if (argp->fh)
+ free(argp->fh);
+ if (argp->pathconf)
+ free(argp->pathconf);
+ if (argp->knconf)
+ free(argp->knconf);
+ if (argp->addr) {
+ free(argp->addr->buf);
+ free(argp->addr);
+ }
+ if (argp->nfs_ext_u.nfs_extB.secdata)
+ free(argp->nfs_ext_u.nfs_extB.secdata);
+ if (argp->syncaddr) {
+ free(argp->syncaddr->buf);
+ free(argp->syncaddr);
+ }
+ if (argp->netname)
+ free(argp->netname);
+ free(argp);
+ if (nmdp->nmd_nfs_proto != NULL)
+ free(nmdp->nmd_nfs_proto);
+
+ return (r);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/ptrace.c b/usr/src/lib/brand/lx/lx_brand/common/ptrace.c
new file mode 100644
index 0000000000..2c6f5041a1
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/ptrace.c
@@ -0,0 +1,105 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_signal.h>
+#include <sys/lx_thread.h>
+#include <sys/lwp.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <procfs.h>
+#include <sys/frame.h>
+#include <strings.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <sys/auxv.h>
+#include <thread.h>
+#include <pthread.h>
+#include <synch.h>
+#include <elf.h>
+#include <ieeefp.h>
+#include <assert.h>
+#include <libintl.h>
+#include <lx_syscall.h>
+
+/*
+ * Much of the Linux ptrace(2) emulation is performed in the kernel, and there
+ * is a block comment in "lx_ptrace.c" that describes the facility in some
+ * detail.
+ */
+
+
+void
+lx_ptrace_stop_if_option(int option, boolean_t child, ulong_t msg,
+ ucontext_t *ucp)
+{
+ /*
+ * We call into the kernel to see if we need to stop for specific
+ * ptrace(2) events.
+ */
+ lx_debug("lx_ptrace_stop_if_option(%d, %s, %lu, %p)", option,
+ child ? "TRUE [child]" : "FALSE [parent]", msg, ucp);
+ if (ucp == NULL) {
+ ucp = (ucontext_t *)lx_find_brand_uc();
+ lx_debug("\tucp = %p", ucp);
+ }
+ if (syscall(SYS_brand, B_PTRACE_STOP_FOR_OPT, option, child, msg,
+ ucp) != 0) {
+ if (errno != ESRCH) {
+ /*
+ * This should _only_ fail if we are not traced, or do
+ * not have this option set.
+ */
+ lx_err_fatal("B_PTRACE_STOP_FOR_OPT failed: %s",
+ strerror(errno));
+ }
+ }
+}
+
+/*
+ * Signal to the in-kernel ptrace(2) subsystem that the next native fork() or
+ * thr_create() is part of an emulated fork(2) or clone(2). If PTRACE_CLONE
+ * was passed to clone(2), inherit_flag should be B_TRUE.
+ */
+void
+lx_ptrace_clone_begin(int option, boolean_t inherit_flag, int flags)
+{
+ lx_debug("lx_ptrace_clone_begin(%d, %sPTRACE_CLONE)", option,
+ inherit_flag ? "" : "!");
+ if (syscall(SYS_brand, B_PTRACE_CLONE_BEGIN, option,
+ inherit_flag, flags) != 0) {
+ lx_err_fatal("B_PTRACE_CLONE_BEGIN failed: %s",
+ strerror(errno));
+ }
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/sendfile.c b/usr/src/lib/brand/lx/lx_brand/common/sendfile.c
new file mode 100644
index 0000000000..7b87958ac0
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/sendfile.c
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * lx_sendfile() and lx_sendfile64() are primarily branded versions of the
+ * library calls available in the Solaris libsendfile (see sendfile(3EXT)).
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/sendfile.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+#include <sys/stat.h>
+
+#if defined(_ILP32)
+long
+lx_sendfile(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+ sysret_t rval;
+ off_t off = 0;
+ off_t *offp = (off_t *)p3;
+ int error;
+ struct sendfilevec sfv;
+ size_t xferred = 0;
+ size_t sz = (size_t)p4;
+
+ if (offp == NULL) {
+ /* If no offp, we must use the current offset */
+ if ((off = lseek((int)p2, 0, SEEK_CUR)) == -1)
+ return (-errno);
+ } else {
+ if (sz > 0 && uucopy(offp, &off, sizeof (off)) != 0)
+ return (-errno);
+ if (off < 0)
+ return (-EINVAL);
+ }
+
+ sfv.sfv_fd = p2;
+ sfv.sfv_flag = 0;
+ sfv.sfv_off = off;
+ sfv.sfv_len = sz;
+ error = __systemcall(&rval, SYS_sendfilev, SENDFILEV, p1, &sfv,
+ 1, &xferred);
+
+ /* Suppress errors if we were able to write any data at all. */
+ if (xferred > 0) {
+ error = 0;
+ }
+ /*
+ * If we got EINVAL due to our offset being past EOF, also suppress
+ * errors (Linux just returns 0 here).
+ */
+ if (error == EINVAL) {
+ struct stat stat;
+ if (fstat((int)p2, &stat) == 0) {
+ if (off >= stat.st_size) {
+ error = 0;
+ }
+ }
+ }
+
+ if (error == 0) {
+ off += xferred;
+ if (offp == NULL) {
+ /* If no offp, we must adjust current offset */
+ if (lseek((int)p2, off, SEEK_SET) == -1)
+ return (-errno);
+ } else {
+ if (uucopy(&off, offp, sizeof (off)) != 0) {
+ return (-EFAULT);
+ }
+ }
+ }
+
+ return (error ? -error : xferred);
+}
+#endif
+
+long
+lx_sendfile64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+ sysret_t rval;
+ off64_t off = 0;
+ off64_t *offp = (off64_t *)p3;
+ size_t sz = (size_t)p4;
+ int error;
+ struct sendfilevec64 sfv;
+ size_t xferred;
+
+ if (offp == NULL) {
+ /* If no offp, we must use the current offset */
+ if ((off = lseek((int)p2, 0, SEEK_CUR)) == -1)
+ return (-errno);
+ } else {
+ if (sz > 0 && uucopy(offp, &off, sizeof (off)) != 0)
+ return (-errno);
+ if (off < 0)
+ return (-EINVAL);
+ }
+
+ sfv.sfv_fd = p2;
+ sfv.sfv_flag = 0;
+ sfv.sfv_off = off;
+ sfv.sfv_len = sz;
+ xferred = 0;
+ error = __systemcall(&rval, SYS_sendfilev, SENDFILEV64, p1, &sfv,
+ 1, &xferred);
+
+ /* Suppress errors if we were able to write any data at all. */
+ if (xferred > 0) {
+ error = 0;
+ }
+ /*
+ * If we got EINVAL due to our offset being past EOF, also suppress
+ * errors (Linux just returns 0 here).
+ */
+ if (error == EINVAL) {
+ struct stat stat;
+ if (fstat((int)p2, &stat) == 0) {
+ if (off >= stat.st_size) {
+ error = 0;
+ }
+ }
+ }
+
+ if (error == 0) {
+ off += xferred;
+ if (offp == NULL) {
+ /* If no offp, we must adjust current offset */
+ if (lseek((int)p2, off, SEEK_SET) == -1)
+ return (-errno);
+ } else {
+ if (uucopy(&off, offp, sizeof (off)) != 0) {
+ return (-EFAULT);
+ }
+ }
+ }
+
+ return (error ? -error : xferred);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/signal.c b/usr/src/lib/brand/lx/lx_brand/common/signal.c
new file mode 100644
index 0000000000..b83a60380d
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/signal.c
@@ -0,0 +1,2431 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/segments.h>
+#include <sys/lx_types.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_poll.h>
+#include <sys/lx_signal.h>
+#include <sys/lx_sigstack.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_thread.h>
+#include <sys/lx_userhz.h>
+#include <sys/syscall.h>
+#include <lx_provider_impl.h>
+#include <sys/stack.h>
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <rctl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <thread.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <libintl.h>
+#include <ieeefp.h>
+#include <sys/signalfd.h>
+
+#if defined(_ILP32)
+extern int pselect_large_fdset(int nfds, fd_set *in0, fd_set *out0, fd_set *ex0,
+ const timespec_t *tsp, const sigset_t *sp);
+#endif
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/*
+ * Delivering signals to a Linux process is complicated by differences in
+ * signal numbering, stack structure and contents, and the action taken when a
+ * signal handler exits. In addition, many signal-related structures, such as
+ * sigset_ts, vary between Illumos and Linux.
+ *
+ * To support user-level signal handlers, the brand uses a double layer of
+ * indirection to process and deliver signals to branded threads.
+ *
+ * When a Linux process sends a signal using the kill(2) system call, we must
+ * translate the signal into the Illumos equivalent before handing control off
+ * to the standard signalling mechanism. When a signal is delivered to a Linux
+ * process, we translate the signal number from Illumos to back to Linux.
+ * Translating signals both at generation and delivery time ensures both that
+ * Illumos signals are sent properly to Linux applications and that signals'
+ * default behavior works as expected.
+ *
+ * In a normal Illumos process, signal delivery is interposed on for any thread
+ * registering a signal handler by libc. Libc needs to do various bits of magic
+ * to provide thread-safe critical regions, so it registers its own handler,
+ * named sigacthandler(), using the sigaction(2) system call. When a signal is
+ * received, sigacthandler() is called, and after some processing, libc turns
+ * around and calls the user's signal handler via a routine named
+ * call_user_handler().
+ *
+ * Adding a Linux branded thread to the mix complicates things somewhat.
+ *
+ * First, when a thread receives a signal, it may either be running in an
+ * emulated Linux context or a native illumos context. In either case, the
+ * in-kernel brand module is responsible for preserving the register state
+ * from the interrupted context, regardless of whether emulated or native
+ * software was running at the time. The kernel is also responsible for
+ * ensuring that the illumos native sigacthandler() is called with register
+ * values appropriate for native code. Of particular note is the %gs segment
+ * selector for 32-bit code, and the %fsbase segment base register for 64-bit
+ * code; these are used by libc to locate per-thread data structures.
+ *
+ * Second, the signal number translation referenced above must take place.
+ * Finally, when we hand control to the Linux signal handler we must do so
+ * on the brand stack, and with registers configured appropriately for the
+ * Linux application.
+ *
+ * This need to translate signal numbers (and manipulate the signal handling
+ * context) means that with standard Illumos libc, following a signal from
+ * generation to delivery looks something like:
+ *
+ * kernel ->
+ * sigacthandler() ->
+ * call_user_handler() ->
+ * user signal handler
+ *
+ * but for the brand's Linux threads, this would look like:
+ *
+ * kernel ->
+ * sigacthandler() ->
+ * call_user_handler() ->
+ * lx_call_user_handler() ->
+ * lx_sigdeliver() ->
+ * syscall(B_JUMP_TO_LINUX, ...) ->
+ * Linux user signal handler
+ *
+ * The new addtions are:
+ *
+ * lx_call_user_handler
+ * ====================
+ * This routine is responsible for translating Illumos signal numbers to
+ * their Linux equivalents, building a Linux signal stack based on the
+ * information Illumos has provided, and passing the stack to the
+ * registered Linux signal handler. It is, in effect, the Linux thread
+ * equivalent to libc's call_user_handler().
+ *
+ * lx_sigdeliver
+ * =============
+ *
+ * Note that none of this interposition is necessary unless a Linux thread
+ * registers a user signal handler, as the default action for all signals is the
+ * same between Illumos and Linux save for one signal, SIGPWR. For this reason,
+ * the brand ALWAYS installs its own internal signal handler for SIGPWR that
+ * translates the action to the Linux default, to terminate the process.
+ * (Illumos' default action is to ignore SIGPWR.)
+ *
+ * A notable behavior of lx_sigdeliver is that it must replace the stack
+ * pointer in the context that will be handed to the Linux signal handler.
+ * There is at least one application (mono) which inspects the SP in the
+ * context it receives and which fails when the SP is not within the thread's
+ * stack range. There is not much else within the context that a signal
+ * handler could depend on, so we only ensure that the SP is from the Linux
+ * stack and not the alternate stack. lx_sigdeliver will restore the correct
+ * SP when setcontext returns into this function as part of returning from
+ * the signal handler.
+ *
+ * It is also important to note that when signals are not translated, the brand
+ * relies upon code interposing upon the wait(2) system call to translate
+ * signals to their proper values for any Linux threads retrieving the status
+ * of others. So while the Illumos signal number for a particular signal is set
+ * in a process' data structures (and would be returned as the result of say,
+ * WTERMSIG()), the brand's interposiiton upon wait(2) is responsible for
+ * translating the value WTERMSIG() would return from a Illumos signal number
+ * to the appropriate Linux value.
+ *
+ * lx_call_user_handler() calls lx_sigdeliver() with a helper function
+ * (typically lx_build_signal_frame) which builds a stack frame for the 32-bit
+ * Linux signal handler, or populates a local (on the stack) structure for the
+ * 64-bit Linux signal handler. The stack at that time looks like this:
+ *
+ * =========================================================
+ * | | lx_sigdeliver_frame_t -- includes LX_SIGRT_MAGIC and |
+ * | | a return context for the eventual sigreturn(2) call |
+ * | =========================================================
+ * | | Linux signal frame (32-bit) or local data |
+ * V | (64-bit) built by stack_builder() |
+ * =========================================================
+ *
+ * The process of returning to an interrupted thread of execution from a user
+ * signal handler is entirely different between Illumos and Linux. While
+ * Illumos generally expects to set the context to the interrupted one on a
+ * normal return from a signal handler, in the normal case Linux instead calls
+ * code that calls a specific Linux system call, rt_sigreturn(2) (or it also
+ * can call sigreturn(2) in 32-bit code). Thus when a Linux signal handler
+ * completes execution, instead of returning through what would in libc be a
+ * call to setcontext(2), the rt_sigreturn(2) Linux system call is responsible
+ * for accomplishing much the same thing. It's for this reason that the stack
+ * frame we build has the lx_(rt_)sigreturn_tramp code on the top of the
+ * stack. The code looks like this:
+ *
+ * 32-bit 64-bit
+ * -------------------------------- -----------------------------
+ * mov LX_SYS_rt_sigreturn, %eax movq LX_SYS_rt_sigreturn, %rax
+ * int $0x80 syscall
+ *
+ * We also use these same functions (lx_rt_sigreturn_tramp or
+ * lx_sigreturn_tramp) to actually return from the signal handler.
+ *
+ * (Note that this trampoline code actually lives in a proper executable segment
+ * and not on the stack, but gdb checks for the exact code sequence of the
+ * trampoline code on the stack to determine whether it is in a signal stack
+ * frame or not. Really.)
+ *
+ * When the 32-bit Linux user signal handler is eventually called, the brand
+ * stack frame looks like this (in the case of a "modern" signal stack; see
+ * the lx_sigstack structure definition):
+ *
+ * =========================================================
+ * | | lx_sigdeliver_frame_t |
+ * | =========================================================
+ * | | Trampoline code (marker for gdb, not really executed) |
+ * | =========================================================
+ * | | Linux struct _fpstate |
+ * | =========================================================
+ * V | Linux ucontext_t | <--+
+ * ========================================================= |
+ * | Linux siginfo_t | <--|-----+
+ * ========================================================= | |
+ * | Pointer to Linux ucontext_t (or NULL) (sigaction arg2)| ---+ |
+ * ========================================================= |
+ * | Pointer to Linux siginfo_t (or NULL) (sigaction arg1)| ---------+
+ * =========================================================
+ * | Linux signal number (sigaction arg0)|
+ * =========================================================
+ * | Pointer to signal return code (trampoline code) |
+ * =========================================================
+ *
+ * The 64-bit stack-local data looks like this:
+ *
+ * =========================================================
+ * | | lx_sigdeliver_frame_t |
+ * | =========================================================
+ * | | Trampoline code (marker for gdb, not really executed) |
+ * | =========================================================
+ * | | Linux struct _fpstate |
+ * | =========================================================
+ * V | Linux ucontext_t | %rdx arg2
+ * =========================================================
+ * | Linux siginfo_t | %rsi arg1
+ * =========================================================
+ * | Pointer to signal return code (trampoline code) |
+ * =========================================================
+ *
+ * As usual in 64-bit code, %rdi is arg0 which is the signal number.
+ *
+ * The *sigreturn(2) family of emulated system call handlers locates the
+ * "lx_sigdeliver_frame_t" struct on the Linux stack as part of processing
+ * the system call. This object contains a guard value (LX_SIGRT_MAGIC) to
+ * detect stack smashing or an incorrect stack pointer. It also contains a
+ * "return" context, which we use to get back to the "lx_sigdeliver()" frame
+ * on the native stack that originally dispatched to the Linux signal
+ * handler. The lx_sigdeliver() function is then able to return to the
+ * native libc signal handler in the usual way. This results in a further
+ * setcontext() back to whatever was running when we took the signal.
+ *
+ * There are some edge cases where the "return" context cannot be located
+ * by inspection of the Linux stack; e.g. if the guard value has been
+ * corrupted, or the emulated program has relocated parts of the signal
+ * delivery stack frame. If this case is detected, a fallback mechanism is
+ * used to attempt to find the return context. A chain of "lx_sigbackup_t"
+ * objects is maintained in signal interposer call frames, with the current
+ * head stored in the thread-specific "lx_tsd_t". This mechanism is
+ * similar in principle to the "lwp_oldcontext" member of the "klwp_t" used
+ * by the native signal handling infrastructure. This backup chain is used
+ * by the sigreturn(2) family of emulated system calls in the event that
+ * the Linux stack did not correctly reference a return context.
+ */
+
+typedef struct lx_sigdeliver_frame {
+ uintptr_t lxsdf_magic;
+ ucontext_t *lxsdf_retucp;
+ ucontext_t *lxsdf_sigucp;
+ lx_sigbackup_t *lxsdf_sigbackup;
+} lx_sigdeliver_frame_t;
+
+struct lx_oldsigstack {
+ void (*retaddr)(); /* address of real lx_sigreturn code */
+ int sig; /* signal number */
+ lx_sigcontext_t sigc; /* saved user context */
+ lx_fpstate_t fpstate; /* saved FP state */
+ int sig_extra; /* signal mask for signals [32 .. NSIG - 1] */
+ char trampoline[8]; /* code for trampoline to lx_sigreturn() */
+};
+
+/*
+ * The lx_sighandlers structure needs to be a global due to the semantics of
+ * clone().
+ *
+ * If CLONE_SIGHAND is set, the calling process and child share signal
+ * handlers, and if either calls sigaction(2) it should change the behavior
+ * in the other thread. Each thread does, however, have its own signal mask
+ * and set of pending signals.
+ *
+ * If CLONE_SIGHAND is not set, the child process should inherit a copy of
+ * the signal handlers at the time of the clone() but later calls to
+ * sigaction(2) should only affect the individual thread calling it.
+ *
+ * This maps perfectly to a thr_create(3C) thread semantic in the first
+ * case and a fork(2)-type semantic in the second case. By making
+ * lx_sighandlers global, we automatically get the correct behavior.
+ */
+static lx_sighandlers_t lx_sighandlers;
+
+static void lx_sigdeliver(int, siginfo_t *, ucontext_t *, size_t, void (*)(),
+ void (*)(), struct lx_sigaction *);
+
+/*
+ * stol_stack() and ltos_stack() convert between Illumos and Linux stack_t
+ * structures.
+ *
+ * These routines are needed because although the two structures have the same
+ * contents, their contents are declared in a different order, so the content
+ * of the structures cannot be copied with a simple bcopy().
+ */
+static void
+stol_stack(stack_t *fr, lx_stack_t *to)
+{
+ to->ss_sp = fr->ss_sp;
+ to->ss_flags = fr->ss_flags;
+ to->ss_size = fr->ss_size;
+}
+
+static void
+ltos_stack(lx_stack_t *fr, stack_t *to)
+{
+ to->ss_sp = fr->ss_sp;
+ to->ss_flags = fr->ss_flags;
+ to->ss_size = fr->ss_size;
+}
+
+static int
+ltos_sigset(lx_sigset_t *lx_sigsetp, sigset_t *s_sigsetp)
+{
+ lx_sigset_t l;
+ int lx_sig, sig;
+
+ if (uucopy(lx_sigsetp, &l, sizeof (lx_sigset_t)) != 0)
+ return (-errno);
+
+ (void) sigemptyset(s_sigsetp);
+
+ for (lx_sig = 1; lx_sig <= LX_NSIG; lx_sig++) {
+ if (lx_sigismember(&l, lx_sig) &&
+ ((sig = ltos_signo[lx_sig]) > 0))
+ (void) sigaddset(s_sigsetp, sig);
+ }
+
+ return (0);
+}
+
+static int
+stol_sigset(sigset_t *s_sigsetp, lx_sigset_t *lx_sigsetp)
+{
+ lx_sigset_t l;
+ int sig, lx_sig;
+
+ bzero(&l, sizeof (lx_sigset_t));
+
+ for (sig = 1; sig < NSIG; sig++) {
+ if (sigismember(s_sigsetp, sig) &&
+ ((lx_sig = stol_signo[sig]) > 0))
+ lx_sigaddset(&l, lx_sig);
+ }
+
+ return ((uucopy(&l, lx_sigsetp, sizeof (lx_sigset_t)) != 0)
+ ? -errno : 0);
+}
+
+#if defined(_ILP32)
+static int
+ltos_osigset(lx_osigset_t *lx_osigsetp, sigset_t *s_sigsetp)
+{
+ lx_osigset_t lo;
+ int lx_sig, sig;
+
+ if (uucopy(lx_osigsetp, &lo, sizeof (lx_osigset_t)) != 0)
+ return (-errno);
+
+ (void) sigemptyset(s_sigsetp);
+
+ for (lx_sig = 1; lx_sig <= OSIGSET_NBITS; lx_sig++)
+ if ((lo & OSIGSET_BITSET(lx_sig)) &&
+ ((sig = ltos_signo[lx_sig]) > 0))
+ (void) sigaddset(s_sigsetp, sig);
+
+ return (0);
+}
+
+static int
+stol_osigset(sigset_t *s_sigsetp, lx_osigset_t *lx_osigsetp)
+{
+ lx_osigset_t lo = 0;
+ int lx_sig, sig;
+
+ /*
+ * Note that an lx_osigset_t can only represent the signals from
+ * [1 .. OSIGSET_NBITS], so even though a signal may be present in the
+ * Illumos sigset_t, it may not be representable as a bit in the
+ * lx_osigset_t.
+ */
+ for (sig = 1; sig < NSIG; sig++)
+ if (sigismember(s_sigsetp, sig) &&
+ ((lx_sig = stol_signo[sig]) > 0) &&
+ (lx_sig <= OSIGSET_NBITS))
+ lo |= OSIGSET_BITSET(lx_sig);
+
+ return ((uucopy(&lo, lx_osigsetp, sizeof (lx_osigset_t)) != 0)
+ ? -errno : 0);
+}
+#endif
+
+static int
+ltos_sigcode(int si_code)
+{
+ switch (si_code) {
+ case LX_SI_USER:
+ return (SI_USER);
+ case LX_SI_TKILL:
+ return (SI_LWP);
+ case LX_SI_QUEUE:
+ return (SI_QUEUE);
+ case LX_SI_TIMER:
+ return (SI_TIMER);
+ case LX_SI_ASYNCIO:
+ return (SI_ASYNCIO);
+ case LX_SI_MESGQ:
+ return (SI_MESGQ);
+ default:
+ return (LX_SI_CODE_NOT_EXIST);
+ }
+}
+
+int
+stol_siginfo(siginfo_t *siginfop, lx_siginfo_t *lx_siginfop)
+{
+ int ret = 0;
+ lx_siginfo_t lx_siginfo;
+
+ bzero(&lx_siginfo, sizeof (*lx_siginfop));
+
+ if ((lx_siginfo.lsi_signo = stol_signo[siginfop->si_signo]) <= 0) {
+ /*
+ * Depending on the caller we may still need to get a usable
+ * converted siginfo struct.
+ */
+ lx_siginfo.lsi_signo = LX_SIGKILL;
+ errno = EINVAL;
+ ret = -1;
+ }
+
+ lx_siginfo.lsi_code = lx_stol_sigcode(siginfop->si_code);
+ lx_siginfo.lsi_errno = siginfop->si_errno;
+
+ switch (lx_siginfo.lsi_signo) {
+ /*
+ * Semantics ARE defined for SIGKILL, but since
+ * we can't catch it, we can't translate it. :-(
+ */
+ case LX_SIGPOLL:
+ lx_siginfo.lsi_band = siginfop->si_band;
+ lx_siginfo.lsi_fd = siginfop->si_fd;
+ break;
+
+ case LX_SIGCHLD:
+ lx_siginfo.lsi_pid = siginfop->si_pid;
+ if (siginfop->si_code <= 0 || siginfop->si_code ==
+ CLD_EXITED) {
+ lx_siginfo.lsi_status = siginfop->si_status;
+ } else {
+ lx_siginfo.lsi_status = lx_stol_status(
+ siginfop->si_status, -1);
+ }
+ lx_siginfo.lsi_utime =
+ HZ_TO_LX_USERHZ(siginfop->si_utime);
+ lx_siginfo.lsi_stime =
+ HZ_TO_LX_USERHZ(siginfop->si_stime);
+ break;
+
+ case LX_SIGILL:
+ case LX_SIGBUS:
+ case LX_SIGFPE:
+ case LX_SIGSEGV:
+ lx_siginfo.lsi_addr = siginfop->si_addr;
+ break;
+
+ default:
+ lx_siginfo.lsi_pid = siginfop->si_pid;
+ lx_siginfo.lsi_uid =
+ LX_UID32_TO_UID16(siginfop->si_uid);
+ lx_siginfo.lsi_value = siginfop->si_value;
+ break;
+ }
+
+ if (uucopy(&lx_siginfo, lx_siginfop, sizeof (lx_siginfo_t)) != 0)
+ return (-errno);
+ return ((ret != 0) ? -errno : 0);
+}
+
+static void
+stol_fpstate(fpregset_t *fpr, lx_fpstate_t *lfpr)
+{
+ size_t copy_len;
+
+#if defined(_LP64)
+ /*
+ * The 64-bit Illumos struct fpregset_t and lx_fpstate_t are identical
+ * so just bcopy() those entries (see usr/src/uts/intel/sys/regset.h
+ * for __amd64's struct fpu).
+ */
+ copy_len = sizeof (fpr->fp_reg_set.fpchip_state);
+ bcopy(fpr, lfpr, copy_len);
+
+#else /* is _ILP32 */
+ struct _fpstate *fpsp = (struct _fpstate *)fpr;
+
+ /*
+ * The Illumos struct _fpstate and lx_fpstate_t are identical from the
+ * beginning of the structure to the lx_fpstate_t "magic" field, so
+ * just bcopy() those entries.
+ */
+ copy_len = (size_t)&(((lx_fpstate_t *)0)->magic);
+ bcopy(fpsp, lfpr, copy_len);
+
+ /*
+ * These fields are all only significant for the first 16 bits.
+ */
+ lfpr->cw &= 0xffff; /* x87 control word */
+ lfpr->tag &= 0xffff; /* x87 tag word */
+ lfpr->cssel &= 0xffff; /* cs selector */
+ lfpr->datasel &= 0xffff; /* ds selector */
+
+ /*
+ * Linux wants the x87 status word field to contain the value of the
+ * x87 saved exception status word.
+ */
+ lfpr->sw = lfpr->status & 0xffff; /* x87 status word */
+
+ lfpr->mxcsr = fpsp->mxcsr;
+
+ if (fpsp->mxcsr != 0) {
+ /*
+ * Linux uses the "magic" field to denote whether the XMM
+ * registers contain legal data or not. Since we can't get to
+ * %cr4 from userland to check the status of the OSFXSR bit,
+ * check the mxcsr field to see if it's 0, which it should
+ * never be on a system with the OXFXSR bit enabled.
+ */
+ lfpr->magic = LX_X86_FXSR_MAGIC;
+ bcopy(fpsp->xmm, lfpr->_xmm, sizeof (lfpr->_xmm));
+ } else {
+ lfpr->magic = LX_X86_FXSR_NONE;
+ }
+#endif
+}
+
+static void
+ltos_fpstate(lx_fpstate_t *lfpr, fpregset_t *fpr)
+{
+ size_t copy_len;
+
+#if defined(_LP64)
+ /*
+ * The 64-bit Illumos struct fpregset_t and lx_fpstate_t are identical
+ * so just bcopy() those entries (see usr/src/uts/intel/sys/regset.h
+ * for __amd64's struct fpu).
+ */
+ copy_len = sizeof (fpr->fp_reg_set.fpchip_state);
+ bcopy(lfpr, fpr, copy_len);
+
+#else /* is _ILP32 */
+ struct _fpstate *fpsp = (struct _fpstate *)fpr;
+
+ /*
+ * The lx_fpstate_t and Illumos struct _fpstate are identical from the
+ * beginning of the structure to the struct _fpstate "mxcsr" field, so
+ * just bcopy() those entries.
+ *
+ * Note that we do NOT have to propogate changes the user may have made
+ * to the "status" word back to the "sw" word, unlike the way we have
+ * to deal with processing the ESP and UESP register values on return
+ * from a signal handler.
+ */
+ copy_len = (size_t)&(((struct _fpstate *)0)->mxcsr);
+ bcopy(lfpr, fpsp, copy_len);
+
+ /*
+ * These fields are all only significant for the first 16 bits.
+ */
+ fpsp->cw &= 0xffff; /* x87 control word */
+ fpsp->sw &= 0xffff; /* x87 status word */
+ fpsp->tag &= 0xffff; /* x87 tag word */
+ fpsp->cssel &= 0xffff; /* cs selector */
+ fpsp->datasel &= 0xffff; /* ds selector */
+ fpsp->status &= 0xffff; /* saved status */
+
+ fpsp->mxcsr = lfpr->mxcsr;
+
+ if (lfpr->magic == LX_X86_FXSR_MAGIC)
+ bcopy(lfpr->_xmm, fpsp->xmm, sizeof (fpsp->xmm));
+#endif
+}
+
+/*
+ * We do not use the system sigaltstack() infrastructure as that would conflict
+ * with our handling of both system call emulation and native signals on the
+ * native stack. Instead, we track the Linux stack structure in our
+ * thread-specific data. This function is modeled on the behaviour of the
+ * native sigaltstack system call handler.
+ */
+long
+lx_sigaltstack(uintptr_t ssp, uintptr_t oss)
+{
+ lx_tsd_t *lxtsd = lx_get_tsd();
+ lx_stack_t ss;
+
+ if (ssp != (uintptr_t)NULL) {
+ if (lxtsd->lxtsd_sigaltstack.ss_flags & LX_SS_ONSTACK) {
+ /*
+ * If we are currently using the installed alternate
+ * stack for signal handling, the user may not modify
+ * the stack for this thread.
+ */
+ return (-EPERM);
+ }
+
+ if (uucopy((void *)ssp, &ss, sizeof (ss)) != 0) {
+ return (-EFAULT);
+ }
+
+ if (ss.ss_flags & ~LX_SS_DISABLE) {
+ /*
+ * The user may not specify a value for flags other
+ * than 0 or SS_DISABLE.
+ */
+ return (-EINVAL);
+ }
+
+ if (!(ss.ss_flags & LX_SS_DISABLE) && ss.ss_size <
+ LX_MINSIGSTKSZ) {
+ return (-ENOMEM);
+ }
+
+ if ((ss.ss_flags & LX_SS_DISABLE) != 0) {
+ ss.ss_sp = NULL;
+ ss.ss_size = 0;
+ }
+ }
+
+ if (oss != (uintptr_t)NULL) {
+ /*
+ * User provided old and new stack_t pointers may point to
+ * the same location. Copy out before we modify.
+ */
+ if (uucopy(&lxtsd->lxtsd_sigaltstack, (void *)oss,
+ sizeof (lxtsd->lxtsd_sigaltstack)) != 0) {
+ return (-EFAULT);
+ }
+ }
+
+ if (ssp != (uintptr_t)NULL) {
+ lxtsd->lxtsd_sigaltstack = ss;
+ }
+
+ return (0);
+}
+
+#if defined(_ILP32)
+/*
+ * The following routines are needed because sigset_ts and siginfo_ts are
+ * different in format between Linux and Illumos.
+ *
+ * Note that there are two different lx_sigset structures, lx_sigset_ts and
+ * lx_osigset_ts:
+ *
+ * + An lx_sigset_t is the equivalent of a Illumos sigset_t and supports
+ * more than 32 signals.
+ *
+ * + An lx_osigset_t is simply a uint32_t, so it by definition only supports
+ * 32 signals.
+ *
+ * When there are two versions of a routine, one prefixed with lx_rt_ and
+ * one prefixed with lx_ alone, in GENERAL the lx_rt_ routines deal with
+ * lx_sigset_ts while the lx_ routines deal with lx_osigset_ts. Unfortunately,
+ * this is not always the case (e.g. lx_sigreturn() vs. lx_rt_sigreturn())
+ */
+long
+lx_sigpending(uintptr_t sigpend)
+{
+ sigset_t sigpendset;
+
+ if (sigpending(&sigpendset) != 0)
+ return (-errno);
+
+ return (stol_osigset(&sigpendset, (lx_osigset_t *)sigpend));
+}
+#endif
+
+long
+lx_rt_sigpending(uintptr_t sigpend, uintptr_t setsize)
+{
+ sigset_t sigpendset;
+
+ if ((size_t)setsize != sizeof (lx_sigset_t))
+ return (-EINVAL);
+
+ if (sigpending(&sigpendset) != 0)
+ return (-errno);
+
+ return (stol_sigset(&sigpendset, (lx_sigset_t *)sigpend));
+}
+
+/*
+ * Create a common routine to encapsulate all of the sigprocmask code,
+ * as the only difference between lx_sigprocmask() and lx_rt_sigprocmask()
+ * is the usage of lx_osigset_ts vs. lx_sigset_ts, as toggled in the code by
+ * the setting of the "sigset_type" flag.
+ */
+static int
+lx_sigprocmask_common(uintptr_t how, uintptr_t l_setp, uintptr_t l_osetp,
+ uintptr_t sigset_type)
+{
+ int err = 0;
+ sigset_t set, oset;
+ sigset_t *s_setp = NULL;
+ sigset_t *s_osetp;
+
+ if (l_setp) {
+ switch (how) {
+ case LX_SIG_BLOCK:
+ how = SIG_BLOCK;
+ break;
+
+ case LX_SIG_UNBLOCK:
+ how = SIG_UNBLOCK;
+ break;
+
+ case LX_SIG_SETMASK:
+ how = SIG_SETMASK;
+ break;
+
+ default:
+ return (-EINVAL);
+ }
+
+ s_setp = &set;
+
+ /* Only 32-bit code passes other than USE_SIGSET */
+ if (sigset_type == USE_SIGSET)
+ err = ltos_sigset((lx_sigset_t *)l_setp, s_setp);
+#if defined(_ILP32)
+ else
+ err = ltos_osigset((lx_osigset_t *)l_setp, s_setp);
+#endif
+
+ if (err != 0)
+ return (err);
+
+ }
+
+ s_osetp = (l_osetp ? &oset : NULL);
+
+ /*
+ * In a multithreaded environment, a call to sigprocmask(2) should
+ * only affect the current thread's signal mask so we don't need to
+ * explicitly call thr_sigsetmask(3C) here.
+ */
+ if (sigprocmask(how, s_setp, s_osetp) != 0)
+ return (-errno);
+
+ if (l_osetp) {
+ if (sigset_type == USE_SIGSET)
+ err = stol_sigset(s_osetp, (lx_sigset_t *)l_osetp);
+#if defined(_ILP32)
+ else
+ err = stol_osigset(s_osetp, (lx_osigset_t *)l_osetp);
+#endif
+
+ if (err != 0) {
+ /*
+ * Encountered a fault while writing to the old signal
+ * mask buffer, so unwind the signal mask change made
+ * above.
+ */
+ (void) sigprocmask(how, s_osetp, (sigset_t *)NULL);
+ return (err);
+ }
+ }
+
+ return (0);
+}
+
+#if defined(_ILP32)
+long
+lx_sigprocmask(uintptr_t how, uintptr_t setp, uintptr_t osetp)
+{
+ return (lx_sigprocmask_common(how, setp, osetp, USE_OSIGSET));
+}
+#endif
+
+long
+lx_rt_sigprocmask(uintptr_t how, uintptr_t setp, uintptr_t osetp,
+ uintptr_t setsize)
+{
+ if ((size_t)setsize != sizeof (lx_sigset_t))
+ return (-EINVAL);
+
+ return (lx_sigprocmask_common(how, setp, osetp, USE_SIGSET));
+}
+
+#if defined(_ILP32)
+long
+lx_sigsuspend(uintptr_t set)
+{
+ sigset_t s_set;
+
+ if (ltos_osigset((lx_osigset_t *)set, &s_set) != 0)
+ return (-errno);
+
+ return ((sigsuspend(&s_set) == -1) ? -errno : 0);
+}
+#endif
+
+long
+lx_rt_sigsuspend(uintptr_t set, uintptr_t setsize)
+{
+ sigset_t s_set;
+
+ if ((size_t)setsize != sizeof (lx_sigset_t))
+ return (-EINVAL);
+
+ if (ltos_sigset((lx_sigset_t *)set, &s_set) != 0)
+ return (-errno);
+
+ return ((sigsuspend(&s_set) == -1) ? -errno : 0);
+}
+
+long
+lx_rt_sigwaitinfo(uintptr_t set, uintptr_t sinfo, uintptr_t setsize)
+{
+ sigset_t s_set;
+ siginfo_t s_sinfo, *s_sinfop;
+ int rc;
+
+ lx_sigset_t *setp = (lx_sigset_t *)set;
+ lx_siginfo_t *sinfop = (lx_siginfo_t *)sinfo;
+
+ if ((size_t)setsize != sizeof (lx_sigset_t))
+ return (-EINVAL);
+
+ if (ltos_sigset(setp, &s_set) != 0)
+ return (-errno);
+
+ s_sinfop = (sinfop == NULL) ? NULL : &s_sinfo;
+
+ if ((rc = sigwaitinfo(&s_set, s_sinfop)) == -1)
+ return (-errno);
+
+ if (s_sinfop == NULL)
+ return (stol_signo[rc]);
+
+ return ((stol_siginfo(s_sinfop, sinfop) != 0)
+ ? -errno : stol_signo[rc]);
+}
+
+long
+lx_rt_sigtimedwait(uintptr_t set, uintptr_t sinfo, uintptr_t toutp,
+ uintptr_t setsize)
+{
+ sigset_t s_set;
+ siginfo_t s_sinfo, *s_sinfop;
+ int rc;
+
+ lx_sigset_t *setp = (lx_sigset_t *)set;
+ lx_siginfo_t *sinfop = (lx_siginfo_t *)sinfo;
+
+ if ((size_t)setsize != sizeof (lx_sigset_t))
+ return (-EINVAL);
+
+ if (ltos_sigset(setp, &s_set) != 0)
+ return (-errno);
+
+ s_sinfop = (sinfop == NULL) ? NULL : &s_sinfo;
+
+ /*
+ * "If timeout is the NULL pointer, the behavior is unspecified."
+ * Match what LTP expects.
+ */
+ if ((rc = sigtimedwait(&s_set, s_sinfop,
+ (struct timespec *)toutp)) == -1)
+ return (toutp == (uintptr_t)NULL ? -EINTR : -errno);
+
+ if (s_sinfop == NULL)
+ return (stol_signo[rc]);
+
+ return ((stol_siginfo(s_sinfop, sinfop) != 0)
+ ? -errno : stol_signo[rc]);
+}
+
+static void
+lx_sigreturn_find_native_context(const char *caller, ucontext_t **sigucp,
+ ucontext_t **retucp, uintptr_t sp)
+{
+ lx_tsd_t *lxtsd = lx_get_tsd();
+ lx_sigdeliver_frame_t *lxsdfp = (lx_sigdeliver_frame_t *)sp;
+ lx_sigdeliver_frame_t lxsdf;
+ boolean_t copy_ok;
+
+ lx_debug("%s: reading lx_sigdeliver_frame_t @ %p\n", caller, lxsdfp);
+ if (uucopy(lxsdfp, &lxsdf, sizeof (lxsdf)) != 0) {
+ lx_debug("%s: failed to read lx_sigdeliver_frame_t @ %p\n",
+ lxsdfp);
+
+ copy_ok = B_FALSE;
+ } else {
+ lx_debug("%s: lxsdf: magic %p retucp %p sigucp %p\n", caller,
+ lxsdf.lxsdf_magic, lxsdf.lxsdf_retucp, lxsdf.lxsdf_sigucp);
+
+ copy_ok = B_TRUE;
+ }
+
+ /*
+ * lx_sigdeliver() pushes a lx_sigdeliver_frame_t onto the stack
+ * before it creates the struct lx_oldsigstack.
+ */
+ if (copy_ok && lxsdf.lxsdf_magic == LX_SIGRT_MAGIC) {
+ LX_SIGNAL_DELIVERY_FRAME_FOUND(lxsdfp);
+
+ /*
+ * The guard value is intact; use the context pointers stored
+ * in the signal delivery frame:
+ */
+ *sigucp = lxsdf.lxsdf_sigucp;
+ *retucp = lxsdf.lxsdf_retucp;
+
+ /*
+ * Ensure that the backup signal delivery chain is in sync with
+ * the frame we are returning via:
+ */
+ lxtsd->lxtsd_sigbackup = lxsdf.lxsdf_sigbackup;
+ } else {
+ /*
+ * The guard value was not intact. Either the program smashed
+ * the stack unintentionally, or worse: intentionally moved
+ * some parts of the signal delivery frame we constructed to
+ * another location before calling rt_sigreturn(2).
+ */
+ LX_SIGNAL_DELIVERY_FRAME_CORRUPT(lxsdfp);
+
+ if (lxtsd->lxtsd_sigbackup == NULL) {
+ /*
+ * There was no backup context to use, so we must
+ * kill the process.
+ */
+ if (copy_ok) {
+ lx_err_fatal("%s: sp 0x%p, expected 0x%x, "
+ "found 0x%lx!", caller, (void *)sp,
+ LX_SIGRT_MAGIC,
+ (unsigned long)lxsdf.lxsdf_magic);
+ } else {
+ lx_err_fatal("%s: sp 0x%p, could not read "
+ "magic", caller, (void *)sp);
+ }
+ }
+
+ /*
+ * Attempt to recover by using the backup signal delivery
+ * chain:
+ */
+ lx_debug("%s: SIGRT_MAGIC not found @ sp %p; using backup "
+ "@ %p\n", caller, (void *)sp, lxtsd->lxtsd_sigbackup);
+ *sigucp = lxtsd->lxtsd_sigbackup->lxsb_sigucp;
+ *retucp = lxtsd->lxtsd_sigbackup->lxsb_retucp;
+ }
+}
+
+#if defined(_ILP32)
+/*
+ * Intercept the Linux sigreturn() syscall to turn it into the return through
+ * the libc call stack that Illumos expects.
+ *
+ * When control returns to libc's call_user_handler() routine, a setcontext(2)
+ * will be done that returns thread execution to the point originally
+ * interrupted by receipt of the signal.
+ *
+ * This is only used by 32-bit code.
+ */
+long
+lx_sigreturn(void)
+{
+ struct lx_oldsigstack *lx_ossp;
+ lx_sigset_t lx_sigset;
+ ucontext_t *ucp;
+ ucontext_t *sigucp;
+ ucontext_t *retucp;
+ uintptr_t sp;
+
+ ucp = lx_syscall_regs();
+
+ /*
+ * NOTE: The sp saved in the context is eight bytes off of where we
+ * need it to be (either due to trampoline or the copying of
+ * sp = uesp, not clear which).
+ */
+ sp = LX_REG(ucp, REG_SP) - 8;
+
+ /*
+ * At this point, the stack pointer should point to the struct
+ * lx_oldsigstack that lx_build_old_signal_frame() constructed and
+ * placed on the stack. We need to reference it a bit later, so
+ * save a pointer to it before incrementing our copy of the sp.
+ */
+ lx_ossp = (struct lx_oldsigstack *)sp;
+ sp += SA(sizeof (struct lx_oldsigstack));
+
+ lx_sigreturn_find_native_context(__func__, &sigucp, &retucp, sp);
+
+ /*
+ * We need to copy machine registers the Linux signal handler may have
+ * modified back to the Illumos ucontext_t.
+ *
+ * General registers copy across as-is, except Linux expects that
+ * changes made to uc_mcontext.gregs[ESP] will be reflected when the
+ * interrupted thread resumes execution after the signal handler. To
+ * emulate this behavior, we must modify uc_mcontext.gregs[UESP] to
+ * match uc_mcontext.gregs[ESP] as Illumos will restore the UESP
+ * value to ESP.
+ */
+ lx_ossp->sigc.sc_esp_at_signal = lx_ossp->sigc.sc_esp;
+ bcopy(&lx_ossp->sigc, &sigucp->uc_mcontext, sizeof (gregset_t));
+
+ LX_SIGRETURN(NULL, sigucp, sp);
+
+ /* copy back FP regs if present */
+ if (lx_ossp->sigc.sc_fpstate != NULL)
+ ltos_fpstate(&lx_ossp->fpstate, &sigucp->uc_mcontext.fpregs);
+
+ /* convert Linux signal mask back to its Illumos equivalent */
+ bzero(&lx_sigset, sizeof (lx_sigset_t));
+ lx_sigset.__bits[0] = lx_ossp->sigc.sc_mask;
+ lx_sigset.__bits[1] = lx_ossp->sig_extra;
+ (void) ltos_sigset(&lx_sigset, &sigucp->uc_sigmask);
+
+ /*
+ * For signal mask handling to be done properly, this call needs to
+ * return to the libc routine that originally called the signal handler
+ * rather than directly set the context back to the place the signal
+ * interrupted execution as the original Linux code would do.
+ */
+ lx_debug("lx_sigreturn: calling setcontext; retucp %p flags %lx "
+ "link %p\n", retucp, retucp->uc_flags, retucp->uc_link);
+ (void) setcontext(retucp);
+ assert(0);
+
+ /*NOTREACHED*/
+ return (0);
+}
+#endif
+
+/*
+ * This signal return syscall is used by both 32-bit and 64-bit code.
+ */
+long
+lx_rt_sigreturn(void)
+{
+ struct lx_sigstack *lx_ssp;
+ lx_ucontext_t *lx_ucp;
+ ucontext_t *ucp;
+ ucontext_t *sigucp;
+ ucontext_t *retucp;
+ uintptr_t sp;
+
+ /*
+ * Since we don't take the normal return path from this syscall, we
+ * inform the kernel that we're returning, for the sake of ptrace.
+ */
+ (void) syscall(SYS_brand, B_PTRACE_SIG_RETURN);
+
+ /* Get the registers at the emulated Linux rt_sigreturn syscall */
+ ucp = lx_syscall_regs();
+
+#if defined(_ILP32)
+ lx_debug("lx_rt_sigreturn: ESP %p UESP %p\n", LX_REG(ucp, ESP),
+ LX_REG(ucp, UESP));
+ /*
+ * For 32-bit
+ *
+ * NOTE: Because of the silly compatibility measures done in the
+ * signal trampoline code to make sure the stack holds the
+ * _exact same_ instruction sequence Linux does, we have to
+ * manually "pop" some extra instructions off the stack here
+ * before passing the stack address to the syscall because the
+ * trampoline code isn't allowed to do it due to the gdb
+ * compatability issues.
+ *
+ * No, I'm not kidding.
+ *
+ * The sp saved in the context is eight bytes off of where we
+ * need it to be (either due to trampoline or the copying of
+ * sp = uesp, not clear which but looks like the uesp case), so
+ * the need to pop the extra four byte instruction means we need
+ * to subtract a net four bytes from the sp before "popping" the
+ * struct lx_sigstack off the stack.
+ *
+ * This will yield the value the stack pointer had before
+ * lx_sigdeliver() created the stack frame for the Linux signal
+ * handler.
+ */
+ sp = (uintptr_t)LX_REG(ucp, REG_SP) - 4;
+#else
+ /*
+ * We need to make an adjustment for 64-bit code as well. Since 64-bit
+ * does not use the trampoline, it's probably for the same reason as
+ * alluded to above.
+ */
+ sp = (uintptr_t)LX_REG(ucp, REG_SP) - 8;
+#endif
+
+ /*
+ * At this point, the stack pointer should point to the struct
+ * lx_sigstack that lx_build_signal_frame() constructed and
+ * placed on the stack. We need to reference it a bit later, so
+ * save a pointer to it before incrementing our copy of the sp.
+ */
+ lx_ssp = (struct lx_sigstack *)sp;
+ sp += SA(sizeof (struct lx_sigstack));
+
+#if defined(_LP64)
+ /*
+ * The 64-bit lx_sigdeliver() inserts 8 bytes of padding between
+ * the lx_sigstack_t and the delivery frame to maintain ABI stack
+ * alignment.
+ */
+ sp += 8;
+#endif
+
+ lx_sigreturn_find_native_context(__func__, &sigucp, &retucp, sp);
+
+ /*
+ * We need to copy machine registers the Linux signal handler may have
+ * modified back to the Illumos version.
+ */
+#if defined(_LP64)
+ lx_ucp = &lx_ssp->uc;
+
+ /*
+ * General register layout is completely different.
+ */
+ LX_REG(sigucp, REG_R15) = lx_ucp->uc_sigcontext.sc_r15;
+ LX_REG(sigucp, REG_R14) = lx_ucp->uc_sigcontext.sc_r14;
+ LX_REG(sigucp, REG_R13) = lx_ucp->uc_sigcontext.sc_r13;
+ LX_REG(sigucp, REG_R12) = lx_ucp->uc_sigcontext.sc_r12;
+ LX_REG(sigucp, REG_R11) = lx_ucp->uc_sigcontext.sc_r11;
+ LX_REG(sigucp, REG_R10) = lx_ucp->uc_sigcontext.sc_r10;
+ LX_REG(sigucp, REG_R9) = lx_ucp->uc_sigcontext.sc_r9;
+ LX_REG(sigucp, REG_R8) = lx_ucp->uc_sigcontext.sc_r8;
+ LX_REG(sigucp, REG_RDI) = lx_ucp->uc_sigcontext.sc_rdi;
+ LX_REG(sigucp, REG_RSI) = lx_ucp->uc_sigcontext.sc_rsi;
+ LX_REG(sigucp, REG_RBP) = lx_ucp->uc_sigcontext.sc_rbp;
+ LX_REG(sigucp, REG_RBX) = lx_ucp->uc_sigcontext.sc_rbx;
+ LX_REG(sigucp, REG_RDX) = lx_ucp->uc_sigcontext.sc_rdx;
+ LX_REG(sigucp, REG_RCX) = lx_ucp->uc_sigcontext.sc_rcx;
+ LX_REG(sigucp, REG_RAX) = lx_ucp->uc_sigcontext.sc_rax;
+ LX_REG(sigucp, REG_TRAPNO) = lx_ucp->uc_sigcontext.sc_trapno;
+ LX_REG(sigucp, REG_ERR) = lx_ucp->uc_sigcontext.sc_err;
+ LX_REG(sigucp, REG_RIP) = lx_ucp->uc_sigcontext.sc_rip;
+ LX_REG(sigucp, REG_CS) = lx_ucp->uc_sigcontext.sc_cs;
+ LX_REG(sigucp, REG_RFL) = lx_ucp->uc_sigcontext.sc_eflags;
+ LX_REG(sigucp, REG_RSP) = lx_ucp->uc_sigcontext.sc_rsp;
+ LX_REG(sigucp, REG_SS) = lx_ucp->uc_sigcontext.sc_pad0;
+ LX_REG(sigucp, REG_FS) = lx_ucp->uc_sigcontext.sc_fs;
+ LX_REG(sigucp, REG_GS) = lx_ucp->uc_sigcontext.sc_gs;
+
+#else /* is _ILP32 */
+ lx_ucp = &lx_ssp->uc;
+
+ /*
+ * Illumos and Linux both follow the SysV i386 ABI layout for the
+ * mcontext.
+ *
+ * General registers copy across as-is, except Linux expects that
+ * changes made to uc_mcontext.gregs[ESP] will be reflected when the
+ * interrupted thread resumes execution after the signal handler. To
+ * emulate this behavior, we must modify uc_mcontext.gregs[UESP] to
+ * match uc_mcontext.gregs[ESP] as Illumos will restore the UESP value
+ * to ESP.
+ */
+ lx_ucp->uc_sigcontext.sc_esp_at_signal = lx_ucp->uc_sigcontext.sc_esp;
+
+ bcopy(&lx_ucp->uc_sigcontext, &sigucp->uc_mcontext.gregs,
+ sizeof (gregset_t));
+#endif
+
+ LX_SIGRETURN(lx_ucp, sigucp, sp);
+
+ if (lx_ucp->uc_sigcontext.sc_fpstate != NULL) {
+ ltos_fpstate(lx_ucp->uc_sigcontext.sc_fpstate,
+ &sigucp->uc_mcontext.fpregs);
+ }
+
+ /*
+ * Convert the Linux signal mask and stack back to their
+ * Illumos equivalents.
+ */
+ (void) ltos_sigset(&lx_ucp->uc_sigmask, &sigucp->uc_sigmask);
+ ltos_stack(&lx_ucp->uc_stack, &sigucp->uc_stack);
+
+ /*
+ * For signal mask handling to be done properly, this call needs to
+ * return to the libc routine that originally called the signal handler
+ * rather than directly set the context back to the place the signal
+ * interrupted execution as the original Linux code would do.
+ */
+ lx_debug("lx_rt_sigreturn: calling setcontext; retucp %p\n", retucp);
+ (void) setcontext(retucp);
+ assert(0);
+
+ /*NOTREACHED*/
+ return (0);
+}
+
+
+#if defined(_ILP32)
+/*
+ * Build signal frame for processing for "old" (legacy) Linux signals
+ * This stack-builder function is only used by 32-bit code.
+ */
+/* ARGSUSED4 */
+static void
+lx_build_old_signal_frame(int lx_sig, siginfo_t *sip, void *p, void *sp,
+ uintptr_t *hargs)
+{
+ extern void lx_sigreturn_tramp();
+
+ lx_sigset_t lx_sigset;
+ ucontext_t *ucp = (ucontext_t *)p;
+ struct lx_sigaction *lxsap;
+ struct lx_oldsigstack *lx_ossp = sp;
+
+ lx_debug("building old signal frame for lx sig %d at 0x%p", lx_sig, sp);
+
+ lx_ossp->sig = lx_sig;
+ lxsap = &lx_sighandlers.lx_sa[lx_sig];
+ lx_debug("lxsap @ 0x%p", lxsap);
+
+ if (lxsap && (lxsap->lxsa_flags & LX_SA_RESTORER) &&
+ lxsap->lxsa_restorer) {
+ lx_ossp->retaddr = lxsap->lxsa_restorer;
+ lx_debug("lxsa_restorer exists @ 0x%p", lx_ossp->retaddr);
+ } else {
+ lx_ossp->retaddr = lx_sigreturn_tramp;
+ lx_debug("lx_ossp->retaddr set to 0x%p", lx_sigreturn_tramp);
+ }
+
+ lx_debug("osf retaddr = 0x%p", lx_ossp->retaddr);
+
+ /* convert Illumos signal mask and stack to their Linux equivalents */
+ (void) stol_sigset(&ucp->uc_sigmask, &lx_sigset);
+ lx_ossp->sigc.sc_mask = lx_sigset.__bits[0];
+ lx_ossp->sig_extra = lx_sigset.__bits[1];
+
+ /*
+ * General registers copy across as-is, except Linux expects that
+ * uc_mcontext.gregs[ESP] == uc_mcontext.gregs[UESP] on receipt of a
+ * signal.
+ */
+ bcopy(&ucp->uc_mcontext, &lx_ossp->sigc, sizeof (gregset_t));
+ lx_ossp->sigc.sc_esp = lx_ossp->sigc.sc_esp_at_signal;
+
+ /*
+ * cr2 contains the faulting address, and Linux only sets cr2 for a
+ * a segmentation fault.
+ */
+ lx_ossp->sigc.sc_cr2 = (((lx_sig == LX_SIGSEGV) && (sip)) ?
+ (uintptr_t)sip->si_addr : 0);
+
+ /* convert FP regs if present */
+ if (ucp->uc_flags & UC_FPU) {
+ stol_fpstate(&ucp->uc_mcontext.fpregs, &lx_ossp->fpstate);
+ lx_ossp->sigc.sc_fpstate = &lx_ossp->fpstate;
+ } else {
+ lx_ossp->sigc.sc_fpstate = NULL;
+ }
+
+ /*
+ * Believe it or not, gdb wants to SEE the trampoline code on the
+ * bottom of the stack to determine whether the stack frame belongs to
+ * a signal handler, even though this code is no longer actually
+ * called.
+ *
+ * You can't make this stuff up.
+ */
+ bcopy((void *)lx_sigreturn_tramp, lx_ossp->trampoline,
+ sizeof (lx_ossp->trampoline));
+}
+#endif
+
+/*
+ * Build stack frame (32-bit) or stack local data (64-bit) for processing for
+ * modern Linux signals. This is the only stack-builder function for 64-bit
+ * code (32-bit code also calls this when using "modern" signals).
+ */
+/* ARGSUSED4 */
+static void
+lx_build_signal_frame(int lx_sig, siginfo_t *sip, void *p, void *sp,
+ uintptr_t *hargs)
+{
+ extern void lx_rt_sigreturn_tramp();
+
+ lx_ucontext_t *lx_ucp;
+ ucontext_t *ucp = (ucontext_t *)p;
+ struct lx_sigstack *lx_ssp = sp;
+ struct lx_sigaction *lxsap;
+
+ lx_debug("building signal frame for lx sig %d at 0x%p", lx_sig, sp);
+
+ lx_ucp = &lx_ssp->uc;
+#if defined(_ILP32)
+ /*
+ * Arguments are passed to the 32-bit signal handler on the stack.
+ */
+ lx_ssp->ucp = lx_ucp;
+ lx_ssp->sip = sip != NULL ? &lx_ssp->si : NULL;
+ lx_ssp->sig = lx_sig;
+#else
+ /*
+ * Arguments to the 64-bit signal handler are passed in registers:
+ * hdlr(int sig, siginfo_t *sip, void *ucp);
+ */
+ hargs[0] = lx_sig;
+ hargs[1] = sip != NULL ? (uintptr_t)&lx_ssp->si : (uintptr_t)NULL;
+ hargs[2] = (uintptr_t)lx_ucp;
+#endif
+
+ lxsap = &lx_sighandlers.lx_sa[lx_sig];
+ lx_debug("lxsap @ 0x%p", lxsap);
+
+ if (lxsap && (lxsap->lxsa_flags & LX_SA_RESTORER) &&
+ lxsap->lxsa_restorer) {
+ /*
+ * lxsa_restorer is explicitly set by sigaction in 32-bit code
+ * but it can also be implicitly set for both 32 and 64 bit
+ * code via lx_sigaction_common when we bcopy the user-supplied
+ * lx_sigaction element into the proper slot in the sighandler
+ * array.
+ */
+ lx_ssp->retaddr = lxsap->lxsa_restorer;
+ lx_debug("lxsa_restorer exists @ 0x%p", lx_ssp->retaddr);
+ } else {
+ lx_ssp->retaddr = lx_rt_sigreturn_tramp;
+ lx_debug("lx_ssp->retaddr set to 0x%p", lx_rt_sigreturn_tramp);
+ }
+
+ /* Linux has these fields but always clears them to 0 */
+ lx_ucp->uc_flags = 0;
+ lx_ucp->uc_link = NULL;
+
+ /* convert Illumos signal mask and stack to their Linux equivalents */
+ (void) stol_sigset(&ucp->uc_sigmask, &lx_ucp->uc_sigmask);
+ stol_stack(&ucp->uc_stack, &lx_ucp->uc_stack);
+
+#if defined(_LP64)
+ /*
+ * General register layout is completely different.
+ */
+ lx_ucp->uc_sigcontext.sc_r8 = LX_REG(ucp, REG_R8);
+ lx_ucp->uc_sigcontext.sc_r9 = LX_REG(ucp, REG_R9);
+ lx_ucp->uc_sigcontext.sc_r10 = LX_REG(ucp, REG_R10);
+ lx_ucp->uc_sigcontext.sc_r11 = LX_REG(ucp, REG_R11);
+ lx_ucp->uc_sigcontext.sc_r12 = LX_REG(ucp, REG_R12);
+ lx_ucp->uc_sigcontext.sc_r13 = LX_REG(ucp, REG_R13);
+ lx_ucp->uc_sigcontext.sc_r14 = LX_REG(ucp, REG_R14);
+ lx_ucp->uc_sigcontext.sc_r15 = LX_REG(ucp, REG_R15);
+ lx_ucp->uc_sigcontext.sc_rdi = LX_REG(ucp, REG_RDI);
+ lx_ucp->uc_sigcontext.sc_rsi = LX_REG(ucp, REG_RSI);
+ lx_ucp->uc_sigcontext.sc_rbp = LX_REG(ucp, REG_RBP);
+ lx_ucp->uc_sigcontext.sc_rbx = LX_REG(ucp, REG_RBX);
+ lx_ucp->uc_sigcontext.sc_rdx = LX_REG(ucp, REG_RDX);
+ lx_ucp->uc_sigcontext.sc_rax = LX_REG(ucp, REG_RAX);
+ lx_ucp->uc_sigcontext.sc_rcx = LX_REG(ucp, REG_RCX);
+ lx_ucp->uc_sigcontext.sc_rsp = LX_REG(ucp, REG_RSP);
+ lx_ucp->uc_sigcontext.sc_rip = LX_REG(ucp, REG_RIP);
+ lx_ucp->uc_sigcontext.sc_eflags = LX_REG(ucp, REG_RFL);
+ lx_ucp->uc_sigcontext.sc_cs = LX_REG(ucp, REG_CS);
+ lx_ucp->uc_sigcontext.sc_gs = LX_REG(ucp, REG_GS);
+ lx_ucp->uc_sigcontext.sc_fs = LX_REG(ucp, REG_FS);
+ lx_ucp->uc_sigcontext.sc_pad0 = LX_REG(ucp, REG_SS);
+ lx_ucp->uc_sigcontext.sc_err = LX_REG(ucp, REG_ERR);
+ lx_ucp->uc_sigcontext.sc_trapno = LX_REG(ucp, REG_TRAPNO);
+
+#else /* is _ILP32 */
+ /*
+ * General registers copy across as-is, except Linux expects that
+ * uc_mcontext.gregs[ESP] == uc_mcontext.gregs[UESP] on receipt of a
+ * signal.
+ */
+ bcopy(&ucp->uc_mcontext, &lx_ucp->uc_sigcontext, sizeof (gregset_t));
+ lx_ucp->uc_sigcontext.sc_esp = lx_ucp->uc_sigcontext.sc_esp_at_signal;
+#endif
+
+ /*
+ * cr2 contains the faulting address, which Linux only sets for a
+ * a segmentation fault.
+ */
+ lx_ucp->uc_sigcontext.sc_cr2 = ((lx_sig == LX_SIGSEGV) && (sip)) ?
+ (uintptr_t)sip->si_addr : 0;
+
+ /*
+ * This should only return an error if the signum is invalid but that
+ * also gets converted into a LX_SIGKILL by this function.
+ */
+ if (sip != NULL)
+ (void) stol_siginfo(sip, &lx_ssp->si);
+ else
+ bzero(&lx_ssp->si, sizeof (lx_siginfo_t));
+
+ /* convert FP regs if present */
+ if (ucp->uc_flags & UC_FPU) {
+ /*
+ * Copy FP regs to the appropriate place in the the lx_sigstack
+ * structure.
+ */
+ stol_fpstate(&ucp->uc_mcontext.fpregs, &lx_ssp->fpstate);
+ lx_ucp->uc_sigcontext.sc_fpstate = &lx_ssp->fpstate;
+ } else {
+ lx_ucp->uc_sigcontext.sc_fpstate = NULL;
+ }
+
+#if defined(_ILP32)
+ /*
+ * Believe it or not, gdb wants to SEE the sigreturn code on the
+ * top of the stack to determine whether the stack frame belongs to
+ * a signal handler, even though this code is not actually called.
+ *
+ * You can't make this stuff up.
+ */
+ bcopy((void *)lx_rt_sigreturn_tramp, lx_ssp->trampoline,
+ sizeof (lx_ssp->trampoline));
+#endif
+}
+
+/*
+ * This is the interposition handler for Linux signals.
+ */
+static void
+lx_call_user_handler(int sig, siginfo_t *sip, void *p)
+{
+ void (*user_handler)();
+ void (*stk_builder)();
+ volatile struct lx_sigaction *lxsap;
+ ucontext_t *ucp = (ucontext_t *)p;
+ size_t stksize;
+ int lx_sig;
+
+ /*
+ * If Illumos signal has no Linux equivalent, effectively ignore it.
+ */
+ if ((lx_sig = stol_signo[sig]) == -1) {
+ lx_unsupported("caught Illumos signal %d, no Linux equivalent",
+ sig);
+ return;
+ }
+
+ lx_debug("interpose caught Illumos signal %d, translating to Linux "
+ "signal %d", sig, lx_sig);
+
+ lxsap = &lx_sighandlers.lx_sa[lx_sig];
+ lx_debug("lxsap @ 0x%p", lxsap);
+
+ if ((sig == SIGPWR) && (lxsap->lxsa_handler == SIG_DFL)) {
+ /*
+ * Linux SIG_DFL for SIGPWR is to terminate. The lx wait
+ * emulation will translate SIGPWR to LX_SIGPWR.
+ */
+ (void) syscall(SYS_brand, B_EXIT_AS_SIG, SIGPWR);
+ /* This should never return */
+ assert(0);
+ }
+
+ while (lxsap->lxsa_handler == SIG_DFL ||
+ lxsap->lxsa_handler == SIG_IGN) {
+ /*
+ * This normally shouldn't be possible, but there is a window
+ * in which a vfork()'d process can have its signal disposition
+ * corrupted by its child. While this window is narrowed by
+ * blocking all signals in the brand, that leaves a (smaller)
+ * window whereby a signal in flight is delivered before the
+ * brand has blocked them. To detect this case, we will spin
+ * if our signal disposition is impossible and all signals are
+ * blocked due to vfork() activity: we know that the vfork()'d
+ * child will eventually restore the signal disposition before
+ * it unblocks signals, allowing us to proceed.
+ */
+ if (lx_all_signals_blocked())
+ continue;
+
+ if (lxsap->lxsa_handler != SIG_DFL &&
+ lxsap->lxsa_handler != SIG_IGN)
+ break;
+
+ lx_err_fatal("lxsa_handler set to %s? How?!?!?",
+ (lxsap->lxsa_handler == SIG_DFL) ? "SIG_DFL" : "SIG_IGN");
+ }
+
+#if defined(_LP64)
+ stksize = sizeof (struct lx_sigstack);
+ stk_builder = lx_build_signal_frame;
+#else
+ if (lxsap->lxsa_flags & LX_SA_SIGINFO) {
+ stksize = sizeof (struct lx_sigstack);
+ stk_builder = lx_build_signal_frame;
+ } else {
+ stksize = sizeof (struct lx_oldsigstack);
+ stk_builder = lx_build_old_signal_frame;
+ }
+#endif
+
+ user_handler = lxsap->lxsa_handler;
+
+ lx_debug("delivering %d (lx %d) to handler at 0x%p", sig, lx_sig,
+ lxsap->lxsa_handler);
+
+ if (lxsap->lxsa_flags & LX_SA_RESETHAND)
+ lxsap->lxsa_handler = SIG_DFL;
+
+ lx_sigdeliver(lx_sig, sip, ucp, stksize, stk_builder, user_handler,
+ (struct lx_sigaction *)lxsap);
+
+ /*
+ * We need to handle restarting system calls if requested by the
+ * program for this signal type:
+ */
+ if (lxsap->lxsa_flags & LX_SA_RESTART) {
+ uintptr_t flags = (uintptr_t)ucp->uc_brand_data[0];
+ long ret = (long)LX_REG(ucp, REG_R0);
+ boolean_t interrupted = (ret == -lx_errno(EINTR, -1));
+
+ /*
+ * If the system call returned EINTR, and the system
+ * call handler set "br_syscall_restart" when returning,
+ * we modify the context to try the system call again
+ * when we return from this signal handler.
+ */
+ if ((flags & LX_UC_RESTART_SYSCALL) && interrupted) {
+ int syscall_num = (int)(uintptr_t)ucp->uc_brand_data[2];
+
+ lx_debug("restarting interrupted system call %d",
+ syscall_num);
+
+ /*
+ * Both the "int 0x80" and the "syscall" instruction
+ * are two bytes long. Wind the program counter back
+ * to the start of this instruction.
+ *
+ * The system call we interrupted is preserved in the
+ * brand-specific data in the ucontext_t when the
+ * LX_UC_RESTART_SYSCALL flag is set. This is
+ * analogous to the "orig_[er]ax" field in the Linux
+ * "user_regs_struct".
+ */
+ LX_REG(ucp, REG_PC) -= 2;
+ LX_REG(ucp, REG_R0) = syscall_num;
+ }
+ }
+}
+
+/*
+ * The "lx_sigdeliver()" function is responsible for constructing the emulated
+ * signal delivery frame on the brand stack for this LWP. A context is saved
+ * on the stack which will be used by the "sigreturn(2)" family of emulated
+ * system calls to get us back here after the Linux signal handler returns.
+ * This function is modelled on the in-kernel "sendsig()" signal delivery
+ * mechanism.
+ */
+void
+lx_sigdeliver(int lx_sig, siginfo_t *sip, ucontext_t *ucp, size_t stacksz,
+ void (*stack_builder)(), void (*user_handler)(),
+ struct lx_sigaction *lxsap)
+{
+ lx_sigbackup_t sigbackup;
+ ucontext_t uc;
+ lx_tsd_t *lxtsd = lx_get_tsd();
+ int totsz = 0;
+ uintptr_t flags;
+ uintptr_t hargs[3];
+ uintptr_t orig_sp = 0;
+
+ /*
+ * These variables must be "volatile", as they are modified after the
+ * getcontext() stores the register state:
+ */
+ volatile boolean_t signal_delivered = B_FALSE;
+ volatile boolean_t sp_modified = B_FALSE;
+ volatile uintptr_t lxfp = 0;
+ volatile uintptr_t old_tsd_sp = 0;
+ volatile int newstack = 0;
+
+ /*
+ * This function involves modifying the Linux process stack for this
+ * thread. To do so without corruption requires us to exclude other
+ * signal handlers (or emulated system calls called from within those
+ * handlers) from running while we reserve space on that stack. We
+ * defer the execution of further instances of lx_call_user_handler()
+ * until we have completed this operation.
+ */
+ _sigoff();
+
+ /*
+ * Clear register arguments vector.
+ */
+ bzero(hargs, sizeof (hargs));
+
+ /* Save our SP so we can restore it after coming back in. */
+ orig_sp = LX_REG(ucp, REG_SP);
+
+ /*
+ * We save a context here so that we can be returned later to complete
+ * handling the signal.
+ */
+ lx_debug("lx_sigdeliver: STORING RETURN CONTEXT @ %p\n", &uc);
+ assert(getcontext(&uc) == 0);
+ lx_debug("lx_sigdeliver: RETURN CONTEXT %p LINK %p FLAGS %lx\n",
+ &uc, uc.uc_link, uc.uc_flags);
+ if (signal_delivered) {
+ /*
+ * If the "signal_delivered" flag is set, we are returned here
+ * via setcontext() as called by the emulated Linux signal
+ * return system call.
+ */
+ lx_debug("lx_sigdeliver: WE ARE BACK, VIA UC @ %p!\n", &uc);
+
+ if (sp_modified) {
+ /*
+ * Restore the original stack pointer, which we saved
+ * on our alt. stack, back into the context.
+ */
+ LX_REG(ucp, REG_SP) = orig_sp;
+ }
+
+ goto after_signal_handler;
+ }
+ signal_delivered = B_TRUE;
+
+ /*
+ * Preserve the current tsd value of the Linux process stack pointer,
+ * even if it is zero. We will restore it when we are returned here
+ * via setcontext() after the Linux process has completed execution of
+ * its signal handler.
+ */
+ old_tsd_sp = lxtsd->lxtsd_lx_sp;
+
+ /*
+ * Figure out whether we will be handling this signal on an alternate
+ * stack specified by the user.
+ */
+ newstack = (lxsap->lxsa_flags & LX_SA_ONSTACK) &&
+ !(lxtsd->lxtsd_sigaltstack.ss_flags & (LX_SS_ONSTACK |
+ LX_SS_DISABLE));
+
+ /*
+ * Find the first unused region of the Linux process stack, where
+ * we will assemble our signal delivery frame.
+ */
+ flags = (uintptr_t)ucp->uc_brand_data[0];
+ if (newstack) {
+ /*
+ * We are moving to the user-provided alternate signal
+ * stack.
+ */
+ lxfp = SA((uintptr_t)lxtsd->lxtsd_sigaltstack.ss_sp) +
+ SA(lxtsd->lxtsd_sigaltstack.ss_size) - STACK_ALIGN;
+ lx_debug("lx_sigdeliver: moving to ALTSTACK sp %p\n", lxfp);
+ LX_SIGNAL_ALTSTACK_ENABLE(lxfp);
+ } else if (flags & LX_UC_STACK_BRAND) {
+ /*
+ * We interrupted the Linux process to take this signal. The
+ * stack pointer is the one saved in this context.
+ */
+ lxfp = LX_REG(ucp, REG_SP);
+ } else {
+ /*
+ * We interrupted a native (emulation) routine, so we must get
+ * the current stack pointer from either the tsd (if one is
+ * stored there) or via the context chain.
+ *
+ */
+ lxfp = lx_find_brand_sp();
+ if (lxtsd->lxtsd_lx_sp != 0) {
+ /*
+ * We must also make room for the possibility of nested
+ * signal delivery -- we may be pre-empting the
+ * in-progress handling of another signal.
+ *
+ * Note that if we were already on the alternate stack,
+ * any emulated Linux system calls would be betwixt
+ * that original signal frame and this new one on the
+ * one contiguous stack, so this logic holds either
+ * way:
+ */
+ lxfp = MIN(lxtsd->lxtsd_lx_sp, lxfp);
+ }
+
+ /* Replace the context SP with the one from the Linux context */
+ LX_REG(ucp, REG_SP) = lxfp;
+ sp_modified = B_TRUE;
+ }
+
+ /*
+ * Account for a reserved stack region (for amd64, this is 128 bytes),
+ * and align the stack:
+ */
+ lxfp -= STACK_RESERVE;
+ lxfp &= ~(STACK_ALIGN - 1);
+
+ /*
+ * Allocate space on the Linux process stack for our delivery frame,
+ * including:
+ *
+ * ----------------------------------------------------- old %sp
+ * - lx_sigdeliver_frame_t
+ * - (ucontext_t pointers and stack magic)
+ * -----------------------------------------------------
+ * - (amd64-only 8-byte alignment gap)
+ * -----------------------------------------------------
+ * - frame of size "stacksz" from the stack builder
+ * ----------------------------------------------------- new %sp
+ */
+#if defined(_LP64)
+ /*
+ * The AMD64 ABI requires us to align the stack such that when the
+ * called function pushes the base pointer, the stack is 16 byte
+ * aligned. The stack must, therefore, be 8- but _not_ 16-byte
+ * aligned.
+ */
+#if (STACK_ALIGN != 16) || (STACK_ENTRY_ALIGN != 8)
+#error "lx_sigdeliver() did not find expected stack alignment"
+#endif
+ totsz = SA(sizeof (lx_sigdeliver_frame_t)) + SA(stacksz) + 8;
+ assert((totsz & (STACK_ENTRY_ALIGN - 1)) == 0);
+ assert((totsz & (STACK_ALIGN - 1)) == 8);
+#else
+ totsz = SA(sizeof (lx_sigdeliver_frame_t)) + SA(stacksz);
+ assert((totsz & (STACK_ALIGN - 1)) == 0);
+#endif
+
+ /*
+ * Copy our return frame into place:
+ */
+ lxfp -= SA(sizeof (lx_sigdeliver_frame_t));
+ lx_debug("lx_sigdeliver: lx_sigdeliver_frame_t @ %p\n", lxfp);
+ {
+ lx_sigdeliver_frame_t frm;
+
+ frm.lxsdf_magic = LX_SIGRT_MAGIC;
+ frm.lxsdf_retucp = &uc;
+ frm.lxsdf_sigucp = ucp;
+ frm.lxsdf_sigbackup = &sigbackup;
+
+ lx_debug("lx_sigdeliver: retucp %p sigucp %p\n",
+ frm.lxsdf_retucp, frm.lxsdf_sigucp);
+
+ if (uucopy(&frm, (void *)lxfp, sizeof (frm)) != 0) {
+ /*
+ * We could not modify the stack of the emulated Linux
+ * program. Act like the kernel and terminate the
+ * program with a segmentation violation.
+ */
+ (void) syscall(SYS_brand, B_EXIT_AS_SIG, SIGSEGV);
+ }
+
+ LX_SIGNAL_DELIVERY_FRAME_CREATE((void *)lxfp);
+
+ /*
+ * Populate a backup copy of signal linkage to use in case
+ * the Linux program completely destroys (or relocates) the
+ * delivery frame.
+ *
+ * This is necessary for programs that have flown so far off
+ * the architectural rails that they believe it is
+ * acceptable to make assumptions about the precise size and
+ * layout of the signal handling frame assembled by the
+ * kernel.
+ */
+ sigbackup.lxsb_retucp = frm.lxsdf_retucp;
+ sigbackup.lxsb_sigucp = frm.lxsdf_sigucp;
+ sigbackup.lxsb_sigdeliver_frame = lxfp;
+ sigbackup.lxsb_previous = lxtsd->lxtsd_sigbackup;
+ lxtsd->lxtsd_sigbackup = &sigbackup;
+
+ lx_debug("lx_sigdeliver: installed sigbackup %p; prev %p\n",
+ &sigbackup, sigbackup.lxsb_previous);
+ }
+
+ /*
+ * Build the Linux signal handling frame:
+ */
+#if defined(_LP64)
+ lxfp -= SA(stacksz) + 8;
+#else
+ lxfp -= SA(stacksz);
+#endif
+ lx_debug("lx_sigdeliver: Linux sig frame @ %p\n", lxfp);
+ stack_builder(lx_sig, sip, ucp, lxfp, hargs);
+
+ /*
+ * Record our reservation so that any nested signal handlers
+ * can see it.
+ */
+ lx_debug("lx_sigdeliver: Linux tsd sp %p -> %p\n", lxtsd->lxtsd_lx_sp,
+ lxfp);
+ lxtsd->lxtsd_lx_sp = lxfp;
+
+ if (newstack) {
+ lxtsd->lxtsd_sigaltstack.ss_flags |= LX_SS_ONSTACK;
+ }
+
+ LX_SIGDELIVER(lx_sig, lxsap, (void *)lxfp);
+
+ /*
+ * Re-enable signal delivery. If a signal was queued while we were
+ * in the critical section, it will be delivered immediately.
+ */
+ _sigon();
+
+ /*
+ * Pass control to the Linux signal handler:
+ */
+ lx_debug("lx_sigdeliver: JUMPING TO LINUX (sig %d sp %p eip %p)\n",
+ lx_sig, lxfp, user_handler);
+ {
+ ucontext_t jump_uc;
+
+ bcopy(lx_find_brand_uc(), &jump_uc, sizeof (jump_uc));
+
+ /*
+ * We want to load the general registers from this context, and
+ * switch to the BRAND stack. We do _not_ want to restore the
+ * uc_link value from this synthetic context, as that would
+ * break the signal handling context chain.
+ */
+ jump_uc.uc_flags = UC_CPU;
+ jump_uc.uc_brand_data[0] = (void *)(LX_UC_STACK_BRAND |
+ LX_UC_IGNORE_LINK);
+
+ LX_REG(&jump_uc, REG_FP) = 0;
+ LX_REG(&jump_uc, REG_SP) = lxfp;
+ LX_REG(&jump_uc, REG_PC) = (uintptr_t)user_handler;
+
+#if defined(_LP64)
+ /*
+ * Pass signal handler arguments by registers on AMD64.
+ */
+ LX_REG(&jump_uc, REG_RDI) = hargs[0];
+ LX_REG(&jump_uc, REG_RSI) = hargs[1];
+ LX_REG(&jump_uc, REG_RDX) = hargs[2];
+#endif
+
+ lx_jump_to_linux(&jump_uc);
+ }
+
+ assert(0);
+ abort();
+
+after_signal_handler:
+ /*
+ * Ensure all nested signal handlers have completed correctly
+ * and then remove our stack reservation.
+ */
+ _sigoff();
+ LX_SIGNAL_POST_HANDLER(lxfp, old_tsd_sp);
+ assert(lxtsd->lxtsd_lx_sp == lxfp);
+ lx_debug("lx_sigdeliver: after; Linux tsd sp %p -> %p\n", lxfp,
+ old_tsd_sp);
+ lxtsd->lxtsd_lx_sp = old_tsd_sp;
+ if (newstack) {
+ LX_SIGNAL_ALTSTACK_DISABLE();
+ lx_debug("lx_sigdeliver: disabling ALTSTACK sp %p\n", lxfp);
+ lxtsd->lxtsd_sigaltstack.ss_flags &= ~LX_SS_ONSTACK;
+ }
+ /*
+ * Restore backup signal tracking chain pointer to previous value:
+ */
+ if (lxtsd->lxtsd_sigbackup != NULL) {
+ lx_sigbackup_t *bprev = lxtsd->lxtsd_sigbackup->lxsb_previous;
+
+ lx_debug("lx_sigdeliver: restoring sigbackup %p to %p\n",
+ lxtsd->lxtsd_sigbackup, bprev);
+
+ lxtsd->lxtsd_sigbackup = bprev;
+ }
+ _sigon();
+
+ /*
+ * Here we return to libc so that it may clean up and restore the
+ * context originally interrupted by this signal.
+ */
+}
+
+/*
+ * Common routine to modify sigaction characteristics of a thread.
+ *
+ * We shouldn't need any special locking code here as we actually use our copy
+ * of libc's sigaction() to do all the real work, so its thread locking should
+ * take care of any issues for us.
+ */
+static int
+lx_sigaction_common(int lx_sig, struct lx_sigaction *lxsp,
+ struct lx_sigaction *olxsp)
+{
+ struct lx_sigaction *lxsap;
+ struct sigaction sa;
+
+ if (lx_sig <= 0 || lx_sig > LX_NSIG)
+ return (-EINVAL);
+
+ lxsap = &lx_sighandlers.lx_sa[lx_sig];
+ lx_debug("&lx_sighandlers.lx_sa[%d] = 0x%p", lx_sig, lxsap);
+
+ if ((olxsp != NULL) &&
+ ((uucopy(lxsap, olxsp, sizeof (struct lx_sigaction))) != 0))
+ return (-errno);
+
+ if (lxsp != NULL) {
+ int err, sig;
+ struct lx_sigaction lxsa;
+ sigset_t new_set, oset;
+
+ if (uucopy(lxsp, &lxsa, sizeof (struct lx_sigaction)) != 0)
+ return (-errno);
+
+ if ((sig = ltos_signo[lx_sig]) != -1) {
+ if (lx_no_abort_handler) {
+ /*
+ * If LX_NO_ABORT_HANDLER has been set, we will
+ * not allow the emulated program to do
+ * anything hamfisted with SIGSEGV or SIGABRT
+ * signals.
+ */
+ if (sig == SIGSEGV || sig == SIGABRT) {
+ return (0);
+ }
+ }
+
+ /*
+ * Block this signal while messing with its dispostion
+ */
+ (void) sigemptyset(&new_set);
+ (void) sigaddset(&new_set, sig);
+
+ if (sigprocmask(SIG_BLOCK, &new_set, &oset) < 0) {
+ err = errno;
+ lx_debug("unable to block signal %d: %d",
+ sig, err);
+ return (-err);
+ }
+
+ /*
+ * We don't really need the old signal disposition at
+ * this point, but this weeds out signals that would
+ * cause sigaction() to return an error before we change
+ * anything other than the current signal mask.
+ */
+ if (sigaction(sig, NULL, &sa) < 0) {
+ err = errno;
+ lx_debug("sigaction() to get old "
+ "disposition for signal %d failed: %d",
+ sig, err);
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+ return (-err);
+ }
+
+ if ((lxsa.lxsa_handler != SIG_DFL) &&
+ (lxsa.lxsa_handler != SIG_IGN)) {
+ sa.sa_handler = lx_call_user_handler;
+
+ /*
+ * The interposition signal handler needs the
+ * information provided via the SA_SIGINFO flag.
+ */
+ sa.sa_flags = SA_SIGINFO;
+
+ /*
+ * When translating from Linux to illumos
+ * sigaction(2) flags, we explicitly do not
+ * pass SA_ONSTACK to the kernel. The
+ * alternate stack for Linux signal handling is
+ * handled entirely by the emulation code.
+ */
+ if (lxsa.lxsa_flags & LX_SA_NOCLDSTOP)
+ sa.sa_flags |= SA_NOCLDSTOP;
+ if (lxsa.lxsa_flags & LX_SA_NOCLDWAIT)
+ sa.sa_flags |= SA_NOCLDWAIT;
+ if (lxsa.lxsa_flags & LX_SA_RESTART)
+ sa.sa_flags |= SA_RESTART;
+ if (lxsa.lxsa_flags & LX_SA_NODEFER)
+ sa.sa_flags |= SA_NODEFER;
+
+ /*
+ * RESETHAND cannot be used be passed through
+ * for SIGPWR due to different default actions
+ * between Linux and Illumos.
+ */
+ if ((sig != SIGPWR) &&
+ (lxsa.lxsa_flags & LX_SA_RESETHAND))
+ sa.sa_flags |= SA_RESETHAND;
+
+ if (ltos_sigset(&lxsa.lxsa_mask,
+ &sa.sa_mask) != 0) {
+ err = errno;
+ (void) sigprocmask(SIG_SETMASK, &oset,
+ NULL);
+ return (-err);
+ }
+
+ lx_debug("interposing handler @ 0x%p for "
+ "signal %d (lx %d), flags 0x%x",
+ lxsa.lxsa_handler, sig, lx_sig,
+ lxsa.lxsa_flags);
+
+ if (sigaction(sig, &sa, NULL) < 0) {
+ err = errno;
+ lx_debug("sigaction() to set new "
+ "disposition for signal %d failed: "
+ "%d", sig, err);
+ (void) sigprocmask(SIG_SETMASK, &oset,
+ NULL);
+ return (-err);
+ }
+ } else if ((sig != SIGPWR) ||
+ ((sig == SIGPWR) &&
+ (lxsa.lxsa_handler == SIG_IGN))) {
+ /*
+ * There's no need to interpose for SIG_DFL or
+ * SIG_IGN so just call our copy of libc's
+ * sigaction(), but don't allow SIG_DFL for
+ * SIGPWR due to differing default actions
+ * between Linux and Illumos.
+ *
+ * Get the previous disposition first so things
+ * like sa_mask and sa_flags are preserved over
+ * a transition to SIG_DFL or SIG_IGN, which is
+ * what Linux expects.
+ */
+
+ sa.sa_handler = lxsa.lxsa_handler;
+
+ if (sigaction(sig, &sa, NULL) < 0) {
+ err = errno;
+ lx_debug("sigaction(%d, %s) failed: %d",
+ sig, ((sa.sa_handler == SIG_DFL) ?
+ "SIG_DFL" : "SIG_IGN"), err);
+ (void) sigprocmask(SIG_SETMASK, &oset,
+ NULL);
+ return (-err);
+ }
+ }
+ } else {
+ lx_debug("Linux signal with no kill support "
+ "specified: %d", lx_sig);
+ }
+
+ /*
+ * Save the new disposition for the signal in the global
+ * lx_sighandlers structure.
+ */
+ bcopy(&lxsa, lxsap, sizeof (struct lx_sigaction));
+
+ /*
+ * Reset the signal mask to what we came in with if
+ * we were modifying a kill-supported signal.
+ */
+ if (sig != -1)
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+ }
+
+ return (0);
+}
+
+#if defined(_ILP32)
+/*
+ * sigaction is only used in 32-bit code.
+ */
+long
+lx_sigaction(uintptr_t lx_sig, uintptr_t actp, uintptr_t oactp)
+{
+ int val;
+ struct lx_sigaction sa, osa;
+ struct lx_sigaction *sap, *osap;
+ struct lx_osigaction *osp;
+
+ sap = (actp ? &sa : NULL);
+ osap = (oactp ? &osa : NULL);
+
+ /*
+ * If we have a source pointer, convert source lxsa_mask from
+ * lx_osigset_t to lx_sigset_t format.
+ */
+ if (sap) {
+ osp = (struct lx_osigaction *)actp;
+ sap->lxsa_handler = osp->lxsa_handler;
+
+ bzero(&sap->lxsa_mask, sizeof (lx_sigset_t));
+
+ for (val = 1; val <= OSIGSET_NBITS; val++)
+ if (osp->lxsa_mask & OSIGSET_BITSET(val))
+ (void) lx_sigaddset(&sap->lxsa_mask, val);
+
+ sap->lxsa_flags = osp->lxsa_flags;
+ sap->lxsa_restorer = osp->lxsa_restorer;
+ }
+
+ if ((val = lx_sigaction_common(lx_sig, sap, osap)))
+ return (val);
+
+ /*
+ * If we have a save pointer, convert the old lxsa_mask from
+ * lx_sigset_t to lx_osigset_t format.
+ */
+ if (osap) {
+ osp = (struct lx_osigaction *)oactp;
+
+ osp->lxsa_handler = osap->lxsa_handler;
+
+ bzero(&osp->lxsa_mask, sizeof (osp->lxsa_mask));
+ for (val = 1; val <= OSIGSET_NBITS; val++)
+ if (lx_sigismember(&osap->lxsa_mask, val))
+ osp->lxsa_mask |= OSIGSET_BITSET(val);
+
+ osp->lxsa_flags = osap->lxsa_flags;
+ osp->lxsa_restorer = osap->lxsa_restorer;
+ }
+
+ return (0);
+}
+#endif
+
+long
+lx_rt_sigaction(uintptr_t lx_sig, uintptr_t actp, uintptr_t oactp,
+ uintptr_t setsize)
+{
+ /*
+ * The "new" rt_sigaction call checks the setsize
+ * parameter.
+ */
+ if ((size_t)setsize != sizeof (lx_sigset_t))
+ return (-EINVAL);
+
+ return (lx_sigaction_common(lx_sig, (struct lx_sigaction *)actp,
+ (struct lx_sigaction *)oactp));
+}
+
+#if defined(_ILP32)
+/*
+ * Convert signal syscall to a call to the lx_sigaction() syscall
+ * Only used in 32-bit code.
+ */
+long
+lx_signal(uintptr_t lx_sig, uintptr_t handler)
+{
+ struct sigaction act;
+ struct sigaction oact;
+ int rc;
+
+ /*
+ * Use sigaction to mimic SYSV signal() behavior; glibc will
+ * actually call sigaction(2) itself, so we're really reaching
+ * back for signal(2) semantics here.
+ */
+ bzero(&act, sizeof (act));
+ act.sa_handler = (void (*)())handler;
+ act.sa_flags = SA_RESETHAND | SA_NODEFER;
+
+ rc = lx_sigaction(lx_sig, (uintptr_t)&act, (uintptr_t)&oact);
+ return ((rc == 0) ? ((ssize_t)oact.sa_handler) : rc);
+}
+#endif
+
+void
+lx_sighandlers_save(lx_sighandlers_t *saved)
+{
+ bcopy(&lx_sighandlers, saved, sizeof (lx_sighandlers_t));
+}
+
+void
+lx_sighandlers_restore(lx_sighandlers_t *saved)
+{
+ bcopy(saved, &lx_sighandlers, sizeof (lx_sighandlers_t));
+}
+
+int
+lx_siginit(void)
+{
+ extern void set_setcontext_enforcement(int);
+ extern void set_escaped_context_cleanup(int);
+
+ struct sigaction sa;
+ sigset_t new_set, oset;
+ int lx_sig, sig;
+
+ /*
+ * Block all signals possible while setting up the signal imposition
+ * mechanism.
+ */
+ (void) sigfillset(&new_set);
+
+ if (sigprocmask(SIG_BLOCK, &new_set, &oset) < 0)
+ lx_err_fatal("unable to block signals while setting up "
+ "imposition mechanism: %s", strerror(errno));
+
+ /*
+ * Ignore any signals that have no Linux analog so that those
+ * signals cannot be sent to Linux processes from the global zone
+ */
+ for (sig = 1; sig < NSIG; sig++)
+ if (stol_signo[sig] < 0)
+ (void) sigignore(sig);
+
+ /*
+ * Mark any signals that are ignored as ignored in our interposition
+ * handler array
+ */
+ for (lx_sig = 1; lx_sig <= LX_NSIG; lx_sig++) {
+ if (((sig = ltos_signo[lx_sig]) != -1) &&
+ (sigaction(sig, NULL, &sa) < 0))
+ lx_err_fatal("unable to determine previous disposition "
+ "for signal %d: %s", sig, strerror(errno));
+
+ if (sa.sa_handler == SIG_IGN) {
+ lx_debug("marking signal %d (lx %d) as SIG_IGN",
+ sig, lx_sig);
+ lx_sighandlers.lx_sa[lx_sig].lxsa_handler = SIG_IGN;
+ }
+ }
+
+ /*
+ * Have our interposition handler handle SIGPWR to start with,
+ * as it has a default action of terminating the process in Linux
+ * but its default is to be ignored in Illumos.
+ */
+ (void) sigemptyset(&sa.sa_mask);
+ sa.sa_sigaction = lx_call_user_handler;
+ sa.sa_flags = SA_SIGINFO;
+
+ if (sigaction(SIGPWR, &sa, NULL) < 0)
+ lx_err_fatal("sigaction(SIGPWR) failed: %s", strerror(errno));
+
+ /*
+ * Illumos' libc forces certain register values in the ucontext_t
+ * used to restore a post-signal user context to be those Illumos
+ * expects; however that is not what we want to happen if the signal
+ * was taken while branded code was executing, so we must disable
+ * that behavior.
+ */
+ set_setcontext_enforcement(0);
+
+ /*
+ * The illumos libc attempts to clean up dangling uc_link pointers in
+ * signal handling contexts when libc believes us to have escaped a
+ * signal handler incorrectly in the past. We want to disable this
+ * behaviour, so that the system call emulation context saved by the
+ * kernel brand module for lx_emulate() may be part of the context
+ * chain without itself being used for signal handling.
+ */
+ set_escaped_context_cleanup(0);
+
+ /*
+ * Reset the signal mask to what we came in with.
+ */
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+
+ lx_debug("interposition handler setup for SIGPWR");
+ return (0);
+}
+
+/*
+ * The first argument is the pid (Linux tgid) to send the signal to, second
+ * argument is the signal to send (an lx signal), and third is the siginfo_t
+ * with extra information. We translate the code and signal only from the
+ * siginfo_t, and leave everything else the same as it gets passed through the
+ * signalling system. This is enough to get sigqueue working. See Linux man
+ * page rt_sigqueueinfo(2).
+ */
+long
+lx_rt_sigqueueinfo(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ pid_t tgid = (pid_t)p1;
+ int lx_sig = (int)p2;
+ int sig;
+ lx_siginfo_t lx_siginfo;
+ siginfo_t siginfo;
+ int s_code;
+ pid_t s_pid;
+
+ if (uucopy((void *)p3, &lx_siginfo, sizeof (lx_siginfo_t)) != 0)
+ return (-EFAULT);
+ s_code = ltos_sigcode(lx_siginfo.lsi_code);
+ if (s_code == LX_SI_CODE_NOT_EXIST)
+ return (-EINVAL);
+ if (lx_sig < 0 || lx_sig > LX_NSIG || (sig = ltos_signo[lx_sig]) < 0) {
+ return (-EINVAL);
+ }
+ /*
+ * This case (when trying to kill pid 0) just has a different errno
+ * returned in illumos than in Linux.
+ */
+ if (tgid == 0)
+ return (-ESRCH);
+ if (lx_lpid_to_spid(tgid, &s_pid) != 0)
+ return (-ESRCH);
+ if (SI_CANQUEUE(s_code)) {
+ return ((syscall(SYS_sigqueue, s_pid, sig,
+ lx_siginfo.lsi_value, s_code, 0) == -1) ?
+ (-errno): 0);
+ } else {
+ /*
+ * This case is unlikely, as the main entry point is through
+ * sigqueue, which always has a queuable si_code.
+ */
+ siginfo.si_signo = sig;
+ siginfo.si_code = s_code;
+ siginfo.si_pid = lx_siginfo.lsi_pid;
+ siginfo.si_value = lx_siginfo.lsi_value;
+ siginfo.si_uid = lx_siginfo.lsi_uid;
+ return ((syscall(SYS_brand, B_HELPER_SIGQUEUE,
+ tgid, sig, &siginfo)) ? (-errno) : 0);
+ }
+}
+
+/*
+ * Adds an additional argument for which thread within a thread group to send
+ * the signal to (added as the second argument).
+ */
+long
+lx_rt_tgsigqueueinfo(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+ pid_t tgid = (pid_t)p1;
+ pid_t tid = (pid_t)p2;
+ int lx_sig = (int)p3;
+ int sig;
+ lx_siginfo_t lx_siginfo;
+ siginfo_t siginfo;
+ int si_code;
+
+ if (uucopy((void *)p4, &lx_siginfo, sizeof (lx_siginfo_t)) != 0)
+ return (-EFAULT);
+ if (lx_sig < 0 || lx_sig > LX_NSIG || (sig = ltos_signo[lx_sig]) < 0) {
+ return (-EINVAL);
+ }
+ si_code = ltos_sigcode(lx_siginfo.lsi_code);
+ if (si_code == LX_SI_CODE_NOT_EXIST)
+ return (-EINVAL);
+ /*
+ * Check for invalid tgid and tids. That appears to be only negatives
+ * and 0 values. Everything else that doesn't exist is instead ESRCH.
+ */
+ if (tgid <= 0 || tid <= 0)
+ return (-EINVAL);
+ siginfo.si_signo = sig;
+ siginfo.si_code = si_code;
+ siginfo.si_pid = lx_siginfo.lsi_pid;
+ siginfo.si_value = lx_siginfo.lsi_value;
+ siginfo.si_uid = lx_siginfo.lsi_uid;
+
+ return ((syscall(SYS_brand, B_HELPER_TGSIGQUEUE, tgid, tid, sig,
+ &siginfo)) ? (-errno) : 0);
+}
+
+long
+lx_signalfd(int fd, uintptr_t mask, size_t msize)
+{
+ return (lx_signalfd4(fd, mask, msize, 0));
+}
+
+long
+lx_signalfd4(int fd, uintptr_t mask, size_t msize, int flags)
+{
+ sigset_t s_set;
+ int r;
+
+ if (msize != sizeof (int64_t))
+ return (-EINVAL);
+
+ if (ltos_sigset((lx_sigset_t *)mask, &s_set) != 0)
+ return (-errno);
+
+ r = signalfd(fd, &s_set, flags);
+
+ /*
+ * signalfd(3C) may fail with ENOENT if /dev/signalfd is not available.
+ * It is less jarring to Linux programs to tell them that internal
+ * allocation failed than to report an error number they are not
+ * expecting.
+ */
+ if (r == -1 && errno == ENOENT)
+ return (-ENODEV);
+
+ return (r == -1 ? -errno : r);
+}
+
+/*
+ * Since this brackets vfork, we also use it as a synchronisation point to
+ * prevent multiple vfork() calls occuring in parallel. This is necessary
+ * because vfork() on illumos is not MT-safe whereas it is on Linux (with
+ * caveats).
+ *
+ * Some real-world applications (java in particular) can run multiple vfork()
+ * calls in parallel across different threads and they need to be serialised
+ * in the lx brand.
+ */
+void
+lx_block_all_signals()
+{
+ while (syscall(SYS_brand, B_BLOCK_ALL_SIGS) != 0 && errno == EAGAIN)
+ yield();
+}
+
+void
+lx_unblock_all_signals()
+{
+ (void) syscall(SYS_brand, B_UNBLOCK_ALL_SIGS);
+}
+
+int
+lx_all_signals_blocked()
+{
+ return (syscall(SYS_brand, B_ALL_SIGS_BLOCKED));
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/stack.c b/usr/src/lib/brand/lx/lx_brand/common/stack.c
new file mode 100644
index 0000000000..8f2c1cacc7
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/stack.c
@@ -0,0 +1,337 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+/*
+ * Manage the native/emulation stack for LX-branded LWPs.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+
+#include <thread.h>
+#include <sys/mman.h>
+#include <sys/brand.h>
+#include <sys/syscall.h>
+#include <sys/debug.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_thread.h>
+
+
+typedef struct lx_stack_list_ent {
+ thread_t sle_tid;
+ void *sle_stack;
+ size_t sle_stack_size;
+ lx_tsd_t *sle_tsd;
+} lx_stack_list_ent_t;
+
+static mutex_t lx_stack_list_lock = ERRORCHECKMUTEX;
+lx_stack_list_ent_t *lx_stack_list = NULL;
+unsigned int lx_stack_list_elems = 0;
+
+/*
+ * Usermode emulation alternate stack size, expressed as a page count:
+ */
+int lx_native_stack_page_count = LX_NATIVE_STACK_PAGE_COUNT;
+
+/*
+ * We use these private functions from libc to suspend signal delivery in
+ * critical sections:
+ */
+extern void _sigon(void);
+extern void _sigoff(void);
+
+void
+lx_stack_prefork(void)
+{
+ lx_tsd_t *lx_tsd = lx_get_tsd();
+
+ /*
+ * The "lx_stack_list_lock" mutex is used to protect access to the list
+ * of per-thread native stacks. Management of native stacks is
+ * generally performed while servicing an emulated fork(2), vfork(2) or
+ * clone(2) system call.
+ *
+ * Multiple threads may be attempting to create new threads or
+ * processes concurrently, but in the case of fork(2) only the
+ * currently executing thread is duplicated in the child process. We
+ * require that the stack list lock be taken before the native fork1()
+ * or forkx(), and released in both the parent and the child once the
+ * operation is complete. For vfork() the lock must only be released in
+ * the parent (once it resumes execution) since the child is borrowing
+ * the parent's thread. The _sigoff/_sigon dance will also only take
+ * place in the parent.
+ *
+ * Holding this mutex prevents the forked child from containing a
+ * copy-on-write copy of a locked mutex without the thread that would
+ * later unlock it. We also suspend signal delivery while entering
+ * this critical section to ensure async signal safety.
+ *
+ * Unfortunately some Linux applications (e.g. busybox) will call vfork
+ * and then call fork (without the expected intervening exec). We
+ * avoid the mutex deadlock by skipping the call since we know this
+ * thread has borrowed the parent's address space and the parent cannot
+ * execute until we exit/exec.
+ */
+ _sigoff();
+ if (lx_tsd->lxtsd_is_vforked == 0)
+ VERIFY0(mutex_lock(&lx_stack_list_lock));
+}
+
+void
+lx_stack_postfork(void)
+{
+ lx_tsd_t *lx_tsd = lx_get_tsd();
+
+ if (lx_tsd->lxtsd_is_vforked == 0)
+ VERIFY0(mutex_unlock(&lx_stack_list_lock));
+ _sigon();
+}
+
+/*
+ * Free the alternate stack for this thread.
+ */
+void
+lx_free_stack(void)
+{
+ thread_t me = thr_self();
+ int i;
+
+ _sigoff();
+ VERIFY0(mutex_lock(&lx_stack_list_lock));
+
+ /*
+ * Find this thread's stack in the list of stacks.
+ */
+ for (i = 0; i < lx_stack_list_elems; i++) {
+ if (lx_stack_list[i].sle_tid != me) {
+ continue;
+ }
+
+ (void) munmap(lx_stack_list[i].sle_stack,
+ lx_stack_list[i].sle_stack_size);
+
+ /*
+ * Free the thread-specific data structure for this thread.
+ */
+ if (lx_stack_list[i].sle_tsd != NULL) {
+ free(lx_stack_list[i].sle_tsd->lxtsd_clone_state);
+ free(lx_stack_list[i].sle_tsd);
+ }
+
+ /*
+ * Free up this stack list entry:
+ */
+ bzero(&lx_stack_list[i], sizeof (lx_stack_list[i]));
+
+ VERIFY0(mutex_unlock(&lx_stack_list_lock));
+ _sigon();
+ return;
+ }
+
+ /*
+ * Did not find the stack in the list.
+ */
+ assert(0);
+}
+
+/*
+ * After fork1(), we must unmap the stack of every thread other than the
+ * one copied into the child process.
+ */
+void
+lx_free_other_stacks(void)
+{
+ int i, this_stack = -1;
+ thread_t me = thr_self();
+ lx_tsd_t *lx_tsd = lx_get_tsd();
+
+ _sigoff();
+
+ /*
+ * We don't need to check or take the lx_stack_list_lock here because
+ * we are the only thread in this process, but if we got here via an
+ * evil vfork->fork path then we must drop the lock for the new child
+ * and reset our "is_vforked" counter.
+ */
+ if (lx_tsd->lxtsd_is_vforked != 0) {
+ VERIFY0(mutex_unlock(&lx_stack_list_lock));
+ lx_tsd->lxtsd_is_vforked = 0;
+ }
+
+ for (i = 0; i < lx_stack_list_elems; i++) {
+ if (lx_stack_list[i].sle_tid == me) {
+ /*
+ * Do not unmap the stack for this LWP.
+ */
+ this_stack = i;
+ continue;
+ } else if (lx_stack_list[i].sle_tid == 0) {
+ /*
+ * Skip any holes in the list.
+ */
+ continue;
+ }
+
+ /*
+ * Free the thread-specific data structure for this thread.
+ */
+ if (lx_stack_list[i].sle_tsd != NULL) {
+ free(lx_stack_list[i].sle_tsd->lxtsd_clone_state);
+ free(lx_stack_list[i].sle_tsd);
+ }
+
+ /*
+ * Unmap the stack of every other LWP.
+ */
+ (void) munmap(lx_stack_list[i].sle_stack,
+ lx_stack_list[i].sle_stack_size);
+ }
+ /*
+ * Did not find the stack for this LWP in the list.
+ */
+ assert(this_stack != -1);
+
+ /*
+ * Ensure the stack data for this LWP is in the first slot and shrink
+ * the list.
+ */
+ if (this_stack != 0) {
+ lx_stack_list[0] = lx_stack_list[this_stack];
+ }
+ lx_stack_list_elems = 1;
+ lx_stack_list = realloc(lx_stack_list, lx_stack_list_elems *
+ sizeof (lx_stack_list[0]));
+ if (lx_stack_list == NULL) {
+ lx_err_fatal("failed to shrink stack list: %s",
+ strerror(errno));
+ }
+
+ _sigon();
+}
+
+/*
+ * Allocate an alternate stack for the execution of native emulation routines.
+ * This routine is based, in part, on find_stack() from libc.
+ */
+int
+lx_alloc_stack(void **nstack, size_t *nstack_size)
+{
+ static int pagesize = 0;
+ static int stackprot = 0;
+ int stacksize = 0;
+ void *stack;
+
+ /*
+ * Fetch configuration once:
+ */
+ if (pagesize == 0) {
+ pagesize = _sysconf(_SC_PAGESIZE);
+ assert(pagesize > 0);
+ }
+ if (stackprot == 0) {
+ long lprot = _sysconf(_SC_STACK_PROT);
+
+ stackprot = lprot > 0 ? lprot : (PROT_READ | PROT_WRITE);
+ }
+
+ stacksize = lx_native_stack_page_count * pagesize;
+
+ if ((stack = mmap(NULL, stacksize, stackprot, MAP_PRIVATE |
+ MAP_NORESERVE | MAP_ANON, -1, (off_t)0)) == MAP_FAILED) {
+ int en = errno;
+ lx_debug("lx_alloc_stack: failed to allocate stack: %d", errno);
+ errno = en;
+ return (-1);
+ }
+
+#if DEBUG
+ /*
+ * Write a recognisable pattern into the allocated stack pages.
+ */
+ for (pos = 0; pos < ((stacksize - 1) / 4); pos++) {
+ ((uint32_t *)stack)[pos] = 0x0facade0;
+ }
+#endif
+
+ *nstack = stack;
+ *nstack_size = stacksize;
+
+ return (0);
+}
+
+/*
+ * Configure the in-kernel brand-specific LWP data with the native stack
+ * pointer for this thread. If a stack is not passed, allocate one first.
+ */
+void
+lx_install_stack(void *stack, size_t stacksize, lx_tsd_t *tsd)
+{
+ thread_t me = thr_self();
+ int i;
+ uintptr_t stack_top;
+
+ if (stack == NULL) {
+ /*
+ * If we were not passed a stack, then allocate one:
+ */
+ if (lx_alloc_stack(&stack, &stacksize) == -1) {
+ lx_err_fatal("failed to allocate stack for thread "
+ "%d: %s", me, strerror(errno));
+ }
+ }
+
+ /*
+ * Install the stack in the global list of thread stacks.
+ */
+ _sigoff();
+ VERIFY0(mutex_lock(&lx_stack_list_lock));
+
+ for (i = 0; i < lx_stack_list_elems; i++) {
+ assert(lx_stack_list[i].sle_tid != me);
+ if (lx_stack_list[i].sle_tid == 0)
+ break;
+ }
+ if (i >= lx_stack_list_elems) {
+ lx_stack_list_elems++;
+ lx_stack_list = realloc(lx_stack_list, lx_stack_list_elems *
+ sizeof (lx_stack_list[0]));
+ if (lx_stack_list == NULL) {
+ lx_err_fatal("failed to extend stack list: %s",
+ strerror(errno));
+ }
+ }
+ lx_stack_list[i].sle_tid = me;
+ lx_stack_list[i].sle_stack = stack;
+ lx_stack_list[i].sle_stack_size = stacksize;
+ lx_stack_list[i].sle_tsd = tsd;
+
+ VERIFY0(mutex_unlock(&lx_stack_list_lock));
+ _sigon();
+
+ /*
+ * Inform the kernel of the location of the brand emulation
+ * stack for this LWP:
+ */
+ stack_top = (uintptr_t)stack + stacksize;
+ lx_debug("stack %p stack_top %p\n", stack, stack_top);
+ if (syscall(SYS_brand, B_SET_NATIVE_STACK, stack_top) != 0) {
+ lx_err_fatal("unable to set native stack: %s", strerror(errno));
+ }
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/statfs.c b/usr/src/lib/brand/lx/lx_brand/common/statfs.c
new file mode 100644
index 0000000000..629e549831
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/statfs.c
@@ -0,0 +1,348 @@
+/*
+ * 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.
+ * Copyright 2017 Joyent, Inc. All rights reserved.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_statfs.h>
+#include <sys/lx_syscall.h>
+
+/*
+ * these defines must exist before we include regexp.h, see regexp(5)
+ */
+#define RE_SIZE 1024
+#define INIT char *sp = instring;
+#define GETC() (*sp++)
+#define PEEKC() (*sp)
+#define UNGETC(c) (--sp)
+#define RETURN(c) return (NULL);
+#define ERROR(c) return ((char *)c);
+
+/*
+ * for regular expressions we're using regexp(5).
+ *
+ * we'd really prefer to use some other nicer regular expressions
+ * interfaces (like regcmp(3c), regcomp(3c), or re_comp(3c)) but we
+ * can't because all these other interfaces rely on the ability
+ * to allocate memory via libc malloc()/calloc() calls, which
+ * we can't really do here.
+ *
+ * we could optionally use regexpr(3gen) but we don't since the
+ * interfaces there are incredibly similar to the regexp(5)
+ * interfaces we're already using and we'd have the added
+ * requirement of linking against libgen.
+ *
+ * another option that was considered is fnmatch(3c) but the
+ * limited pattern expansion capability of this interface would
+ * force us to include more patterns to check against.
+ */
+#include <regexp.h>
+
+static struct lx_ftype_path {
+ char *lfp_path;
+ char lfp_re[RE_SIZE];
+ int lfp_magic;
+ char *lfp_magic_str;
+} ftype_path_list[] = {
+ { "^/dev/pts$", "",
+ LX_DEVPTS_SUPER_MAGIC, "LX_DEVPTS_SUPER_MAGIC" },
+ { "^/dev/pts/$", "",
+ LX_DEVPTS_SUPER_MAGIC, "LX_DEVPTS_SUPER_MAGIC" },
+ { "^/dev/pts/[0-9][0-9]*$", "",
+ LX_DEVPTS_SUPER_MAGIC, "LX_DEVPTS_SUPER_MAGIC" },
+ { NULL, "",
+ 0, NULL }
+};
+
+/*
+ * For lack of linux equivalents, we present lofs and zfs as being ext2. Yes,
+ * this is a total lie, but Linux can't handle the truth of ZFS -- and ext2's
+ * small surface area seems to make it the most tenable lie to tell.
+ */
+static struct lx_ftype_name {
+ const char *lfn_name;
+ int lfn_magic;
+ char *lfn_magic_str;
+} ftype_name_list[] = {
+ { "hsfs", LX_ISOFS_SUPER_MAGIC, "LX_ISOFS_SUPER_MAGIC" },
+ { "nfs", LX_NFS_SUPER_MAGIC, "LX_NFS_SUPER_MAGIC" },
+ { "pcfs", LX_MSDOS_SUPER_MAGIC, "LX_MSDOS_SUPER_MAGIC" },
+ { "lx_proc", LX_PROC_SUPER_MAGIC, "LX_PROC_SUPER_MAGIC" },
+ { "tmpfs", LX_TMPFS_SUPER_MAGIC, "LX_TMPFS_SUPER_MAGIC" },
+ { "ufs", LX_UFS_MAGIC, "LX_UFS_MAGIC" },
+ { "lofs", LX_EXT2_SUPER_MAGIC, "LX_EXT2_SUPER_MAGIC" },
+ { "zfs", LX_EXT2_SUPER_MAGIC, "LX_EXT2_SUPER_MAGIC" },
+ { "lxautofs", LX_AUTOFS_SUPER_MAGIC, "LX_AUTOFS_SUPER_MAGIC" },
+ { "lx_cgroup", LX_CGROUP_SUPER_MAGIC, "LX_CGROUP_SUPER_MAGIC" },
+ { "lx_sysfs", LX_SYSFS_SUPER_MAGIC, "LX_SYSFS_SUPER_MAGIC" },
+ { NULL, 0, NULL }
+};
+
+int
+lx_statfs_init()
+{
+ int i;
+ char *rv;
+
+ for (i = 0; ftype_path_list[i].lfp_path != NULL; i++) {
+ rv = compile(
+ ftype_path_list[i].lfp_path,
+ ftype_path_list[i].lfp_re,
+ ftype_path_list[i].lfp_re + RE_SIZE, '\0');
+ if (rv == NULL)
+ continue;
+
+ lx_debug("lx_statfs_init compile(\"%s\") failed",
+ ftype_path_list[i].lfp_path);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+stol_type(const char *path, const char *name)
+{
+ int i;
+ lx_debug("\tstol_type(\"%s\", \"%s\")\n", path == NULL ? "NULL" : path,
+ name == NULL ? "NULL" : name);
+
+ if (path != NULL) {
+ char userpath[MAXPATHLEN];
+
+ if (uucopystr(path, userpath, MAXPATHLEN) == -1)
+ return (-errno);
+
+ for (i = 0; ftype_path_list[i].lfp_path != NULL; i++) {
+ if (step(userpath, ftype_path_list[i].lfp_re) == 0)
+ continue;
+
+ /* got a match on the fs path */
+ lx_debug("\ttranslated f_type to 0x%x - %s",
+ ftype_path_list[i].lfp_magic,
+ ftype_path_list[i].lfp_magic_str);
+ return (ftype_path_list[i].lfp_magic);
+ }
+ }
+
+ assert(name != NULL);
+ for (i = 0; ftype_name_list[i].lfn_name != NULL; i++) {
+ if (strcmp(name, ftype_name_list[i].lfn_name) == 0) {
+
+ /* got a match on the fs name */
+ lx_debug("\ttranslated f_type to 0x%x - %s",
+ ftype_name_list[i].lfn_magic,
+ ftype_name_list[i].lfn_magic_str);
+ return (ftype_name_list[i].lfn_magic);
+ }
+ }
+
+ /* we don't know what the fs type is so just set it to 0 */
+ return (0);
+}
+
+/*
+ * The Linux statfs() is similar to the Solaris statvfs() call, the main
+ * difference being the use of a numeric 'f_type' identifier instead of the
+ * 'f_basetype' string.
+ */
+static int
+stol_statfs(const char *path, struct lx_statfs *l, struct statvfs *s)
+{
+ int type;
+
+ if ((type = stol_type(path, s->f_basetype)) < 0)
+ return (type);
+
+ l->f_type = type;
+ l->f_bsize = s->f_frsize; /* other fields depend on frsize */
+ l->f_blocks = s->f_blocks;
+ l->f_bfree = s->f_bfree;
+ l->f_bavail = s->f_bavail;
+ l->f_files = s->f_files;
+ l->f_ffree = s->f_ffree;
+ l->f_fsid = s->f_fsid;
+ l->f_namelen = s->f_namemax;
+ l->f_frsize = s->f_frsize;
+ bzero(&(l->f_spare), sizeof (l->f_spare));
+
+ return (0);
+}
+
+static int
+stol_statfs64(const char *path, struct lx_statfs64 *l, struct statvfs64 *s)
+{
+ int type;
+
+ if ((type = stol_type(path, s->f_basetype)) < 0)
+ return (type);
+
+ l->f_type = type;
+ l->f_bsize = s->f_frsize; /* other fields depend on frsize */
+ l->f_blocks = s->f_blocks;
+ l->f_bfree = s->f_bfree;
+ l->f_bavail = s->f_bavail;
+ l->f_files = s->f_files;
+ l->f_ffree = s->f_ffree;
+ l->f_fsid = s->f_fsid;
+ l->f_namelen = s->f_namemax;
+ l->f_frsize = s->f_frsize;
+ bzero(&(l->f_spare), sizeof (l->f_spare));
+
+ return (0);
+}
+
+long
+lx_statfs(uintptr_t p1, uintptr_t p2)
+{
+ const char *path = (const char *)p1;
+ struct lx_statfs lxfs, *fs = (struct lx_statfs *)p2;
+ struct statvfs vfs;
+ int err;
+
+ lx_debug("\tstatvfs(%s, 0x%p)", path, fs);
+ if (statvfs(path, &vfs) != 0)
+ return (-errno);
+
+ if ((err = stol_statfs(path, &lxfs, &vfs)) != 0)
+ return (err);
+
+ if (uucopy(&lxfs, fs, sizeof (struct lx_statfs)) != 0)
+ return (-errno);
+
+ return (0);
+}
+
+long
+lx_fstatfs(uintptr_t p1, uintptr_t p2)
+{
+ struct lx_statfs lxfs, *fs = (struct lx_statfs *)p2;
+ struct stat64 sb;
+ struct statvfs vfs;
+ char *path, path_buf[MAXPATHLEN];
+ int fd = (int)p1;
+ int err;
+
+ lx_debug("\tfstatvfs(%d, 0x%p)", fd, fs);
+
+ /*
+ * fstatfs emulation for a pipe.
+ */
+ if (fstat64(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) {
+ lxfs.f_type = LX_PIPEFS_MAGIC;
+ lxfs.f_bsize = 4096;
+ lxfs.f_blocks = 0;
+ lxfs.f_bfree = 0;
+ lxfs.f_bavail = 0;
+ lxfs.f_files = 0;
+ lxfs.f_ffree = 0;
+ lxfs.f_fsid = 0;
+ lxfs.f_namelen = 255;
+ lxfs.f_frsize = 4096;
+ } else {
+ if (fstatvfs(fd, &vfs) != 0)
+ return (-errno);
+
+ path = lx_fd_to_path(fd, path_buf, sizeof (path_buf));
+
+ if ((err = stol_statfs(path, &lxfs, &vfs)) != 0)
+ return (err);
+ }
+
+ if (uucopy(&lxfs, fs, sizeof (struct lx_statfs)) != 0)
+ return (-errno);
+
+ return (0);
+}
+
+/* ARGSUSED */
+long
+lx_statfs64(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ const char *path = (const char *)p1;
+ struct lx_statfs64 lxfs, *fs = (struct lx_statfs64 *)p3;
+ struct statvfs64 vfs;
+ int err;
+
+ lx_debug("\tstatvfs64(%s, %d, 0x%p)", path, p2, fs);
+ if (statvfs64(path, &vfs) != 0)
+ return (-errno);
+
+ if ((err = stol_statfs64(path, &lxfs, &vfs)) != 0)
+ return (err);
+
+ if (uucopy(&lxfs, fs, sizeof (struct lx_statfs64)) != 0)
+ return (-errno);
+
+ return (0);
+}
+
+/* ARGSUSED */
+long
+lx_fstatfs64(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ struct lx_statfs64 lxfs, *fs = (struct lx_statfs64 *)p3;
+ struct stat64 sb;
+ struct statvfs64 vfs;
+ char *path, path_buf[MAXPATHLEN];
+ int fd = (int)p1;
+ int err;
+
+ lx_debug("\tfstatvfs64(%d, %d, 0x%p)", fd, p2, fs);
+ if (fstat64(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) {
+ lxfs.f_type = LX_PIPEFS_MAGIC;
+ lxfs.f_bsize = 4096;
+ lxfs.f_blocks = 0;
+ lxfs.f_bfree = 0;
+ lxfs.f_bavail = 0;
+ lxfs.f_files = 0;
+ lxfs.f_ffree = 0;
+ lxfs.f_fsid = 0;
+ lxfs.f_namelen = 255;
+ lxfs.f_frsize = 4096;
+ } else {
+ if (fstatvfs64(fd, &vfs) != 0)
+ return (-errno);
+
+ path = lx_fd_to_path(fd, path_buf, sizeof (path_buf));
+
+ if ((err = stol_statfs64(path, &lxfs, &vfs)) != 0)
+ return (err);
+ }
+
+ if (uucopy(&lxfs, fs, sizeof (struct lx_statfs64)) != 0)
+ return (-errno);
+
+ return (0);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/sysctl.c b/usr/src/lib/brand/lx/lx_brand/common/sysctl.c
new file mode 100644
index 0000000000..262a9c3830
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/sysctl.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 (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
+ */
+
+#include <alloca.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+
+/*
+ * sysctl() implementation. The full set of possible values is incredibly
+ * large; we only implement the bare minimum here, namely basic kernel
+ * information.
+ *
+ * For the moment, we also print out debugging messages if the application
+ * attempts to write or access any other values, so we can tell if we are not
+ * supporting something we should be.
+ */
+
+struct lx_sysctl_args {
+ int *name;
+ int nlen;
+ void *oldval;
+ size_t *oldlenp;
+ void *newval;
+ size_t newlen;
+};
+
+#define LX_CTL_KERN 1
+
+#define LX_KERN_OSTYPE 1
+#define LX_KERN_OSRELEASE 2
+#define LX_KERN_OSREV 3
+#define LX_KERN_VERSION 4
+
+long
+lx_sysctl(uintptr_t raw)
+{
+ struct lx_sysctl_args args;
+ int name[2];
+ size_t oldlen;
+ char *namebuf;
+
+ if (uucopy((void *)raw, &args, sizeof (args)) < 0)
+ return (-EFAULT);
+
+ /*
+ * We only allow [ CTL_KERN, KERN_* ] pairs, so reject anything that
+ * doesn't have exactly two values starting with LX_CTL_KERN.
+ */
+ if (args.nlen != 2)
+ return (-ENOTDIR);
+
+ if (uucopy(args.name, name, sizeof (name)) < 0)
+ return (-EFAULT);
+
+ if (name[0] != LX_CTL_KERN) {
+ lx_debug("sysctl: read of [%d, %d] unsupported",
+ name[0], name[1]);
+ return (-ENOTDIR);
+ }
+
+ /* We don't support writing new sysctl values. */
+ if ((args.newval != NULL) || (args.newlen != 0)) {
+ lx_debug("sysctl: write of [%d, %d] unsupported",
+ name[0], name[1]);
+ return (-EPERM);
+ }
+
+ /*
+ * It may seem silly, but passing in a NULL oldval pointer and not
+ * writing any new values is a perfectly legal thing to do and should
+ * succeed.
+ */
+ if (args.oldval == NULL)
+ return (0);
+
+ /*
+ * Likewise, Linux specifies that setting a non-NULL oldval but a
+ * zero *oldlenp should result in an errno of EFAULT.
+ */
+ if ((uucopy(args.oldlenp, &oldlen, sizeof (oldlen)) < 0) ||
+ (oldlen == 0))
+ return (-EFAULT);
+
+ namebuf = SAFE_ALLOCA(oldlen);
+ if (namebuf == NULL)
+ return (-ENOMEM);
+
+ switch (name[1]) {
+ case LX_KERN_OSTYPE:
+ (void) strlcpy(namebuf, LX_UNAME_SYSNAME, oldlen);
+ break;
+ case LX_KERN_OSRELEASE:
+ (void) strlcpy(namebuf, lx_release, oldlen);
+ break;
+ case LX_KERN_VERSION:
+ (void) strlcpy(namebuf, LX_UNAME_VERSION, oldlen);
+ break;
+ default:
+ lx_debug("sysctl: read of [CTL_KERN, %d] unsupported", name[1]);
+ return (-ENOTDIR);
+ }
+
+ oldlen = strlen(namebuf);
+
+ if ((uucopy(namebuf, args.oldval, oldlen) < 0) ||
+ (uucopy(&oldlen, args.oldlenp, sizeof (oldlen)) < 0))
+ return (-EFAULT);
+
+ return (0);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c b/usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c
new file mode 100644
index 0000000000..aa4f4ffb40
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c
@@ -0,0 +1,987 @@
+/*
+ * 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.
+ * Copyright 2017 Joyent, Inc. All rights reserved.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <strings.h>
+#include <rctl.h>
+#include <alloca.h>
+#include <values.h>
+#include <sys/syscall.h>
+#include <sys/msg.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_types.h>
+#include <sys/lx_sysv_ipc.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+#define SLOT_SEM 0
+#define SLOT_SHM 1
+#define SLOT_MSG 2
+
+/* Use private SHM_RMID interface for IPC_RMID */
+#define SHM_RMID 5
+
+static int
+get_rctlval(rctlblk_t *rblk, char *name, ulong_t limit, uint64_t *val)
+{
+ rctl_qty_t r;
+
+ if (getrctl(name, NULL, rblk, RCTL_FIRST) == -1)
+ return (-errno);
+
+ r = rctlblk_get_value(rblk);
+ if (r > limit)
+ return (-EOVERFLOW);
+
+ *val = r;
+ return (0);
+}
+
+/*
+ * Given a slot number and a maximum number of ids to extract from the
+ * kernel, return the msgid in the provided slot.
+ */
+static int
+slot_to_id(int type, int slot)
+{
+ uint_t nids, max;
+ int *idbuf = NULL;
+ int r = 0;
+
+ nids = 0;
+ for (;;) {
+ switch (type) {
+ case SLOT_SEM:
+ r = semids(idbuf, nids, &max);
+ break;
+ case SLOT_SHM:
+ r = shmids(idbuf, nids, &max);
+ break;
+ case SLOT_MSG:
+ r = msgids(idbuf, nids, &max);
+ break;
+ }
+
+ if (r < 0)
+ return (-errno);
+
+ if (max == 0)
+ return (-EINVAL);
+
+ if (max <= nids)
+ return (idbuf[slot]);
+
+ nids = max;
+ if ((idbuf = (int *)SAFE_ALLOCA(sizeof (int) * nids)) == NULL)
+ return (-ENOMEM);
+ }
+}
+
+/*
+ * Semaphore operations.
+ */
+long
+lx_semget(key_t key, int nsems, int semflg)
+{
+ int sol_flag;
+ int r;
+
+ lx_debug("\nsemget(%d, %d, %d)\n", key, nsems, semflg);
+ sol_flag = semflg & S_IAMB;
+ if (semflg & LX_IPC_CREAT)
+ sol_flag |= IPC_CREAT;
+ if (semflg & LX_IPC_EXCL)
+ sol_flag |= IPC_EXCL;
+
+ r = semget(key, nsems, sol_flag);
+ return ((r < 0) ? -errno : r);
+}
+
+long
+lx_semop(int semid, void *p1, size_t nsops)
+{
+ int r;
+ struct sembuf *sops = (struct sembuf *)p1;
+
+ lx_debug("\nsemop(%d, 0x%p, %u)\n", semid, sops, nsops);
+ if (nsops == 0)
+ return (-EINVAL);
+
+ r = semop(semid, sops, nsops);
+ return ((r < 0) ? -errno : r);
+}
+
+long
+lx_semtimedop(int semid, void *p1, size_t nsops, struct timespec *timeout)
+{
+ int r;
+ struct sembuf *sops = (struct sembuf *)p1;
+
+ lx_debug("\nsemtimedop(%d, 0x%p, %u, 0x%p)\n", semid, sops, nsops,
+ timeout);
+ if (nsops == 0)
+ return (-EINVAL);
+
+ r = semtimedop(semid, sops, nsops, timeout);
+ return ((r < 0) ? -errno : r);
+}
+
+static int
+lx_semctl_ipcset(int semid, void *buf)
+{
+ struct lx_semid_ds semds;
+ struct semid_ds sol_semds;
+ int r;
+
+ if (uucopy(buf, &semds, sizeof (semds)))
+ return (-errno);
+
+ bzero(&sol_semds, sizeof (sol_semds));
+ sol_semds.sem_perm.uid = semds.sem_perm.uid;
+ sol_semds.sem_perm.gid = semds.sem_perm.gid;
+ sol_semds.sem_perm.mode = semds.sem_perm.mode;
+
+ r = semctl(semid, 0, IPC_SET, &sol_semds);
+ return ((r < 0) ? -errno : r);
+}
+
+static int
+lx_semctl_ipcstat(int semid, void *buf)
+{
+ struct lx_semid_ds semds;
+ struct semid_ds sol_semds;
+
+ if (semctl(semid, 0, IPC_STAT, &sol_semds) != 0)
+ return (-errno);
+
+ bzero(&semds, sizeof (semds));
+ semds.sem_perm.key = sol_semds.sem_perm.key;
+ semds.sem_perm.seq = sol_semds.sem_perm.seq;
+ semds.sem_perm.uid = sol_semds.sem_perm.uid;
+ semds.sem_perm.gid = sol_semds.sem_perm.gid;
+ semds.sem_perm.cuid = sol_semds.sem_perm.cuid;
+ semds.sem_perm.cgid = sol_semds.sem_perm.cgid;
+
+ /* Linux only uses the bottom 9 bits */
+ semds.sem_perm.mode = sol_semds.sem_perm.mode & S_IAMB;
+ semds.sem_otime = sol_semds.sem_otime;
+ semds.sem_ctime = sol_semds.sem_ctime;
+ semds.sem_nsems = sol_semds.sem_nsems;
+
+ if (uucopy(&semds, buf, sizeof (semds)))
+ return (-errno);
+
+ return (0);
+}
+
+static int
+lx_semctl_ipcinfo(void *buf)
+{
+ struct lx_seminfo i;
+ rctlblk_t *rblk;
+ int rblksz;
+ uint_t nids;
+ int idbuf;
+ int err;
+ uint64_t val;
+
+ rblksz = rctlblk_size();
+ if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL)
+ return (-ENOMEM);
+
+ bzero(&i, sizeof (i));
+ err = get_rctlval(rblk, "project.max-sem-ids", (ulong_t)MAXINT, &val);
+ if (err < 0)
+ return (err);
+ i.semmni = (int)val;
+ err = get_rctlval(rblk, "process.max-sem-nsems", (ulong_t)MAXINT, &val);
+ if (err < 0)
+ return (err);
+ i.semmsl = (int)val;
+ err = get_rctlval(rblk, "process.max-sem-ops", (ulong_t)MAXINT, &val);
+ if (err < 0)
+ return (err);
+ i.semopm = (int)val;
+
+ /*
+ * We don't have corresponding rctls for these fields. The values
+ * are taken from the formulas used to derive the defaults listed
+ * in the Linux header file. We're lying, but trying to be
+ * coherent about it.
+ */
+ i.semmap = i.semmni;
+ i.semmns = i.semmni * i.semmsl;
+ i.semmnu = INT_MAX;
+ i.semume = INT_MAX;
+ i.semvmx = LX_SEMVMX;
+ if (semids(&idbuf, 0, &nids) < 0)
+ return (-errno);
+ i.semusz = nids;
+ i.semaem = INT_MAX;
+
+ if (uucopy(&i, buf, sizeof (i)) != 0)
+ return (-errno);
+
+ return (nids);
+}
+
+static int
+lx_semctl_semstat(int slot, void *buf)
+{
+ int r, semid;
+
+ semid = slot_to_id(SLOT_SEM, slot);
+ if (semid < 0)
+ return (semid);
+
+ r = lx_semctl_ipcstat(semid, buf);
+ return (r < 0 ? r : semid);
+}
+
+/*
+ * For the SETALL operation, we have to examine each of the semaphore
+ * values to be sure it is legal.
+ */
+static int
+lx_semctl_setall(int semid, ushort_t *arg)
+{
+ struct semid_ds semds;
+ ushort_t *vals;
+ int i, sz, r;
+
+ /*
+ * Find out how many semaphores are involved, reserve enough
+ * memory for an internal copy of the array, and then copy it in
+ * from the process.
+ */
+ if (semctl(semid, 0, IPC_STAT, &semds) != 0)
+ return (-errno);
+ sz = semds.sem_nsems * sizeof (ushort_t);
+ if ((vals = SAFE_ALLOCA(sz)) == NULL)
+ return (-ENOMEM);
+ if (uucopy(arg, vals, sz))
+ return (-errno);
+
+ /* Validate each of the values. */
+ for (i = 0; i < semds.sem_nsems; i++)
+ if (vals[i] > LX_SEMVMX)
+ return (-ERANGE);
+
+ r = semctl(semid, 0, SETALL, arg);
+
+ return ((r < 0) ? -errno : r);
+}
+
+long
+lx_semctl(int semid, int semnum, int cmd, void *ptr)
+{
+#if defined(_ILP32)
+ union lx_semun arg;
+#endif
+ int rval;
+ int opt = cmd & ~LX_IPC_64;
+ int use_errno = 0;
+ uint_t val;
+
+ lx_debug("\nsemctl(%d, %d, %d, 0x%p)\n", semid, semnum, cmd, ptr);
+
+#if defined(_ILP32)
+ /*
+ * The final arg to semctl() is a pointer to a union. For some
+ * commands we can hand that pointer directly to the kernel. For
+ * these commands, we need to extract an argument from the union
+ * before calling into the kernel.
+ */
+ if (opt == LX_SETVAL || opt == LX_SETALL || opt == LX_GETALL ||
+ opt == LX_IPC_SET || opt == LX_IPC_STAT || opt == LX_SEM_STAT ||
+ opt == LX_IPC_INFO || opt == LX_SEM_INFO)
+ if (uucopy(ptr, &arg, sizeof (arg)))
+ return (-errno);
+#endif
+
+ switch (opt) {
+ case LX_GETVAL:
+ use_errno = 1;
+ rval = semctl(semid, semnum, GETVAL, NULL);
+ break;
+ case LX_SETVAL:
+#if defined(_ILP32)
+ val = arg.val;
+#else
+ val = (uint_t)(uintptr_t)ptr;
+#endif
+ if (val > LX_SEMVMX) {
+ return (-ERANGE);
+ }
+ use_errno = 1;
+ rval = semctl(semid, semnum, SETVAL, val);
+ break;
+ case LX_GETPID:
+ use_errno = 1;
+ rval = semctl(semid, semnum, GETPID, NULL);
+ break;
+ case LX_GETNCNT:
+ use_errno = 1;
+ rval = semctl(semid, semnum, GETNCNT, NULL);
+ break;
+ case LX_GETZCNT:
+ use_errno = 1;
+ rval = semctl(semid, semnum, GETZCNT, NULL);
+ break;
+ case LX_GETALL:
+ use_errno = 1;
+#if defined(_ILP32)
+ rval = semctl(semid, semnum, GETALL, arg.sems);
+#else
+ rval = semctl(semid, semnum, GETALL, ptr);
+#endif
+ break;
+ case LX_SETALL:
+#if defined(_ILP32)
+ rval = lx_semctl_setall(semid, arg.sems);
+#else
+ rval = lx_semctl_setall(semid, ptr);
+#endif
+ break;
+ case LX_IPC_RMID:
+ use_errno = 1;
+ rval = semctl(semid, semnum, IPC_RMID, NULL);
+ break;
+ case LX_SEM_STAT:
+#if defined(_ILP32)
+ rval = lx_semctl_semstat(semid, arg.semds);
+#else
+ rval = lx_semctl_semstat(semid, ptr);
+#endif
+ break;
+ case LX_IPC_STAT:
+#if defined(_ILP32)
+ rval = lx_semctl_ipcstat(semid, arg.semds);
+#else
+ rval = lx_semctl_ipcstat(semid, ptr);
+#endif
+ break;
+
+ case LX_IPC_SET:
+#if defined(_ILP32)
+ rval = lx_semctl_ipcset(semid, arg.semds);
+#else
+ rval = lx_semctl_ipcset(semid, ptr);
+#endif
+ break;
+
+ case LX_IPC_INFO:
+ case LX_SEM_INFO:
+#if defined(_ILP32)
+ rval = lx_semctl_ipcinfo(arg.semds);
+#else
+ rval = lx_semctl_ipcinfo(ptr);
+#endif
+ break;
+
+ default:
+ return (-EINVAL);
+ }
+
+ if (use_errno == 1 && rval < 0)
+ return (-errno);
+ return (rval);
+}
+
+/*
+ * msg operations.
+ */
+long
+lx_msgget(key_t key, int flag)
+{
+ int sol_flag;
+ int r;
+
+ lx_debug("\tlx_msgget(%d, %d)\n", key, flag);
+
+ sol_flag = flag & S_IAMB;
+ if (flag & LX_IPC_CREAT)
+ sol_flag |= IPC_CREAT;
+ if (flag & LX_IPC_EXCL)
+ sol_flag |= IPC_EXCL;
+
+ r = msgget(key, sol_flag);
+ return (r < 0 ? -errno : r);
+}
+
+long
+lx_msgsnd(int id, void *p1, size_t sz, int flag)
+{
+ int sol_flag = 0;
+ int r;
+ struct msgbuf *buf = (struct msgbuf *)p1;
+
+ lx_debug("\tlx_msgsnd(%d, 0x%p, %d, %d)\n", id, buf, sz, flag);
+
+ if (flag & LX_IPC_NOWAIT)
+ sol_flag |= IPC_NOWAIT;
+
+ if (((ssize_t)sz < 0) || (sz > LX_MSGMAX))
+ return (-EINVAL);
+
+ r = msgsnd(id, buf, sz, sol_flag);
+ return (r < 0 ? -errno : r);
+}
+
+long
+lx_msgrcv(int id, void *msgp, size_t sz, long msgtype, int flag)
+{
+ int sol_flag = 0;
+ ssize_t r;
+
+ lx_debug("\tlx_msgrcv(%d, 0x%p, %d, %d, %ld, %d)\n",
+ id, msgp, sz, msgtype, flag);
+
+ /*
+ * Check for a negative sz parameter.
+ *
+ * Unlike msgsnd(2), the Linux man page does not specify that
+ * msgrcv(2) should return EINVAL if (sz > MSGMAX), only if (sz < 0).
+ */
+ if ((ssize_t)sz < 0)
+ return (-EINVAL);
+
+ if (flag & LX_MSG_NOERROR)
+ sol_flag |= MSG_NOERROR;
+ if (flag & LX_IPC_NOWAIT)
+ sol_flag |= IPC_NOWAIT;
+
+ r = msgrcv(id, msgp, sz, msgtype, sol_flag);
+ return (r < 0 ? -errno : r);
+}
+
+static int
+lx_msgctl_ipcstat(int msgid, void *buf)
+{
+ struct lx_msqid_ds msgids;
+ struct msqid_ds sol_msgids;
+ int r;
+
+ r = msgctl(msgid, IPC_STAT, &sol_msgids);
+ if (r < 0)
+ return (-errno);
+
+ bzero(&msgids, sizeof (msgids));
+ msgids.msg_perm.key = sol_msgids.msg_perm.key;
+ msgids.msg_perm.seq = sol_msgids.msg_perm.seq;
+ msgids.msg_perm.uid = sol_msgids.msg_perm.uid;
+ msgids.msg_perm.gid = sol_msgids.msg_perm.gid;
+ msgids.msg_perm.cuid = sol_msgids.msg_perm.cuid;
+ msgids.msg_perm.cgid = sol_msgids.msg_perm.cgid;
+
+ /* Linux only uses the bottom 9 bits */
+ msgids.msg_perm.mode = sol_msgids.msg_perm.mode & S_IAMB;
+
+ msgids.msg_stime = sol_msgids.msg_stime;
+ msgids.msg_rtime = sol_msgids.msg_rtime;
+ msgids.msg_ctime = sol_msgids.msg_ctime;
+ msgids.msg_qbytes = sol_msgids.msg_qbytes;
+ msgids.msg_cbytes = sol_msgids.msg_cbytes;
+ msgids.msg_qnum = sol_msgids.msg_qnum;
+ msgids.msg_lspid = sol_msgids.msg_lspid;
+ msgids.msg_lrpid = sol_msgids.msg_lrpid;
+
+ if (uucopy(&msgids, buf, sizeof (msgids)))
+ return (-errno);
+
+ return (0);
+}
+
+static int
+lx_msgctl_ipcinfo(int cmd, void *buf)
+{
+ struct lx_msginfo m;
+ rctlblk_t *rblk;
+ int idbuf, rblksz, msgseg, maxmsgs;
+ uint_t nids;
+ int rval;
+ int err;
+ uint64_t val;
+
+ rblksz = rctlblk_size();
+ if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL)
+ return (-ENOMEM);
+
+ bzero(&m, sizeof (m));
+ err = get_rctlval(rblk, "project.max-msg-ids", (ulong_t)MAXINT, &val);
+ if (err < 0)
+ return (err);
+ m.msgmni = (int)val;
+ err = get_rctlval(rblk, "process.max-msg-qbytes", (ulong_t)MAXINT,
+ &val);
+ if (err < 0)
+ return (err);
+ m.msgmnb = (int)val;
+
+ if (cmd == LX_IPC_INFO) {
+ err = get_rctlval(rblk, "process.max-msg-messages",
+ (ulong_t)MAXINT, &val);
+ if (err < 0)
+ return (err);
+ maxmsgs = (int)val;
+ m.msgtql = maxmsgs * m.msgmni;
+ m.msgmap = m.msgmnb;
+ m.msgpool = m.msgmax * m.msgmnb;
+ rval = 0;
+ } else {
+ if (msgids(&idbuf, 0, &nids) < 0)
+ return (-errno);
+ m.msgpool = nids;
+
+ /*
+ * For these fields, we can't even come up with a good fake
+ * approximation. These are listed as 'obsolete' or
+ * 'unused' in the header files, so hopefully nobody is
+ * relying on them anyway.
+ */
+ m.msgtql = INT_MAX;
+ m.msgmap = INT_MAX;
+ rval = nids;
+ }
+
+ /*
+ * We don't have corresponding rctls for these fields. The values
+ * are taken from the formulas used to derive the defaults listed
+ * in the Linux header file. We're lying, but trying to be
+ * coherent about it.
+ */
+ m.msgmax = LX_MSGMAX;
+ m.msgssz = 16;
+ msgseg = (m.msgpool * 1024) / m.msgssz;
+ m.msgseg = (msgseg > 0xffff) ? 0xffff : msgseg;
+
+ if (uucopy(&m, buf, sizeof (m)))
+ return (-errno);
+ return (rval);
+}
+
+static int
+lx_msgctl_ipcset(int msgid, void *buf)
+{
+ struct lx_msqid_ds msgids;
+ struct msqid_ds sol_msgids;
+ int r;
+
+ if (uucopy(buf, &msgids, sizeof (msgids)))
+ return (-errno);
+
+ bzero(&sol_msgids, sizeof (sol_msgids));
+ sol_msgids.msg_perm.uid = LX_UID16_TO_UID32(msgids.msg_perm.uid);
+ sol_msgids.msg_perm.gid = LX_UID16_TO_UID32(msgids.msg_perm.gid);
+
+ /* Linux only uses the bottom 9 bits */
+ sol_msgids.msg_perm.mode = msgids.msg_perm.mode & S_IAMB;
+ sol_msgids.msg_qbytes = msgids.msg_qbytes;
+
+ r = msgctl(msgid, IPC_SET, &sol_msgids);
+ return (r < 0 ? -errno : r);
+}
+
+static int
+lx_msgctl_msgstat(int slot, void *buf)
+{
+ int r, msgid;
+
+ lx_debug("msgstat(%d, 0x%p)\n", slot, buf);
+
+ msgid = slot_to_id(SLOT_MSG, slot);
+
+ if (msgid < 0)
+ return (msgid);
+
+ r = lx_msgctl_ipcstat(msgid, buf);
+ return (r < 0 ? r : msgid);
+}
+
+/*
+ * Split off the various msgctl's here
+ */
+long
+lx_msgctl(int msgid, int cmd, void *buf)
+{
+ int r;
+
+ lx_debug("\tlx_msgctl(%d, %d, 0x%p)\n", msgid, cmd, buf);
+ switch (cmd & ~LX_IPC_64) {
+ case LX_IPC_RMID:
+ r = msgctl(msgid, IPC_RMID, NULL);
+ if (r < 0)
+ r = -errno;
+ break;
+ case LX_IPC_SET:
+ r = lx_msgctl_ipcset(msgid, buf);
+ break;
+ case LX_IPC_STAT:
+ r = lx_msgctl_ipcstat(msgid, buf);
+ break;
+ case LX_MSG_STAT:
+ r = lx_msgctl_msgstat(msgid, buf);
+ break;
+
+ case LX_IPC_INFO:
+ case LX_MSG_INFO:
+ r = lx_msgctl_ipcinfo(cmd, buf);
+ break;
+
+ default:
+ r = -EINVAL;
+ break;
+ }
+
+ return (r);
+}
+
+/*
+ * shm-related operations.
+ */
+long
+lx_shmget(key_t key, size_t size, int flag)
+{
+ int sol_flag;
+ int r;
+
+ lx_debug("\tlx_shmget(%d, %d, %d)\n", key, size, flag);
+
+ sol_flag = flag & S_IAMB;
+ if (flag & LX_IPC_CREAT)
+ sol_flag |= IPC_CREAT;
+ if (flag & LX_IPC_EXCL)
+ sol_flag |= IPC_EXCL;
+
+ r = shmget(key, size, sol_flag);
+ return (r < 0 ? -errno : r);
+}
+
+long
+lx_shmat(int shmid, void *addr, int flags)
+{
+ int sol_flags;
+ void *ptr;
+
+ lx_debug("\tlx_shmat(%d, 0x%p, %d)\n", shmid, addr, flags);
+
+ /*
+ * Linux has a fix for CVE-2017-5669 which LTP is testing for. The
+ * kernel will disallow mapping into the first 64k of the address space.
+ * LTP passes 1 as the address which will then round down to 0.
+ * In the future, once more work has been done to tighten up the lx
+ * brand handling for the minimum mappable address (e.g. with secflags),
+ * then we can remove this check.
+ */
+ if ((flags & LX_SHM_RND) && addr != NULL && addr < (void *)0x10000) {
+ return (-EINVAL);
+ }
+
+ sol_flags = 0;
+ if (flags & LX_SHM_RDONLY)
+ sol_flags |= SHM_RDONLY;
+ if (flags & LX_SHM_RND)
+ sol_flags |= SHM_RND;
+ if ((flags & LX_SHM_REMAP) && (addr == NULL))
+ return (-EINVAL);
+
+ ptr = shmat(shmid, addr, sol_flags);
+ if (ptr == (void *)-1)
+ return (-errno);
+
+ return ((ssize_t)ptr);
+}
+
+static int
+lx_shmctl_ipcinfo(void *buf)
+{
+ struct lx_shminfo s;
+ rctlblk_t *rblk;
+ int rblksz;
+ int err;
+ uint64_t val;
+
+ rblksz = rctlblk_size();
+ if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL)
+ return (-ENOMEM);
+
+ bzero(&s, sizeof (s));
+ err = get_rctlval(rblk, "project.max-shm-ids", ULONG_MAX, &val);
+ if (err < 0)
+ return (err);
+ s.shmmni = val;
+ err = get_rctlval(rblk, "project.max-shm-memory", ULONG_MAX, &val);
+ if (err < 0)
+ return (err);
+ s.shmmax = val;
+
+ /*
+ * We don't have corresponding rctls for these fields. The values
+ * are taken from the formulas used to derive the defaults listed
+ * in the Linux header file. We're lying, but trying to be
+ * coherent about it.
+ */
+ s.shmmin = 1;
+ s.shmseg = ULONG_MAX;
+ s.shmall = s.shmmax / getpagesize();
+
+ if (uucopy(&s, buf, sizeof (s)))
+ return (-errno);
+
+ return (0);
+}
+
+static int
+lx_shmctl_ipcstat(int shmid, void *buf)
+{
+ struct lx_shmid_ds shmds;
+ struct shmid_ds sol_shmds;
+
+ if (shmctl(shmid, IPC_STAT, &sol_shmds) != 0)
+ return (-errno);
+
+ bzero(&shmds, sizeof (shmds));
+ shmds.shm_perm.key = sol_shmds.shm_perm.key;
+ shmds.shm_perm.seq = sol_shmds.shm_perm.seq;
+ shmds.shm_perm.uid = sol_shmds.shm_perm.uid;
+ shmds.shm_perm.gid = sol_shmds.shm_perm.gid;
+ shmds.shm_perm.cuid = sol_shmds.shm_perm.cuid;
+ shmds.shm_perm.cgid = sol_shmds.shm_perm.cgid;
+ shmds.shm_perm.mode = sol_shmds.shm_perm.mode & S_IAMB;
+ if (sol_shmds.shm_lkcnt > 0)
+ shmds.shm_perm.mode |= LX_SHM_LOCKED;
+ shmds.shm_segsz = sol_shmds.shm_segsz;
+ shmds.shm_atime = sol_shmds.shm_atime;
+ shmds.shm_dtime = sol_shmds.shm_dtime;
+ shmds.shm_ctime = sol_shmds.shm_ctime;
+ shmds.shm_cpid = sol_shmds.shm_cpid;
+ shmds.shm_lpid = sol_shmds.shm_lpid;
+ shmds.shm_nattch = (ushort_t)sol_shmds.shm_nattch;
+
+ if (uucopy(&shmds, buf, sizeof (shmds)))
+ return (-errno);
+
+ return (0);
+}
+
+static int
+lx_shmctl_ipcset(int shmid, void *buf)
+{
+ struct lx_shmid_ds shmds;
+ struct shmid_ds sol_shmds;
+ int r;
+
+ if (uucopy(buf, &shmds, sizeof (shmds)))
+ return (-errno);
+
+ bzero(&sol_shmds, sizeof (sol_shmds));
+ sol_shmds.shm_perm.uid = shmds.shm_perm.uid;
+ sol_shmds.shm_perm.gid = shmds.shm_perm.gid;
+ sol_shmds.shm_perm.mode = shmds.shm_perm.mode & S_IAMB;
+
+ r = shmctl(shmid, IPC_SET, &sol_shmds);
+ return (r < 0 ? -errno : r);
+}
+
+/*
+ * Build and return a shm_info structure. We only return the bare
+ * essentials required by ipcs. The rest of the info is not readily
+ * available.
+ */
+static int
+lx_shmctl_shminfo(void *buf)
+{
+ struct lx_shm_info shminfo;
+ uint_t nids;
+ int idbuf;
+
+ bzero(&shminfo, sizeof (shminfo));
+
+ if (shmids(&idbuf, 0, &nids) < 0)
+ return (-errno);
+
+ shminfo.used_ids = nids;
+ if (uucopy(&shminfo, buf, sizeof (shminfo)) != 0)
+ return (-errno);
+
+ return (nids);
+}
+
+static int
+lx_shmctl_shmstat(int slot, void *buf)
+{
+ int r, shmid;
+
+ lx_debug("shmctl_shmstat(%d, 0x%p)\n", slot, buf);
+ shmid = slot_to_id(SLOT_SHM, slot);
+ if (shmid < 0)
+ return (shmid);
+
+ r = lx_shmctl_ipcstat(shmid, buf);
+ return (r < 0 ? r : shmid);
+}
+
+long
+lx_shmctl(int shmid, int cmd, void *buf)
+{
+ int r;
+ int use_errno = 0;
+
+ lx_debug("\tlx_shmctl(%d, %d, 0x%p)\n", shmid, cmd, buf);
+ switch (cmd & ~LX_IPC_64) {
+ case LX_IPC_RMID:
+ use_errno = 1;
+ r = shmctl(shmid, SHM_RMID, NULL); /* lx-private cmd */
+ break;
+
+ case LX_IPC_SET:
+ r = lx_shmctl_ipcset(shmid, buf);
+ break;
+
+ case LX_IPC_STAT:
+ r = lx_shmctl_ipcstat(shmid, buf);
+ break;
+
+ case LX_IPC_INFO:
+ r = lx_shmctl_ipcinfo(buf);
+ break;
+
+ case LX_SHM_LOCK:
+ use_errno = 1;
+ r = shmctl(shmid, SHM_LOCK, NULL);
+ break;
+
+ case LX_SHM_UNLOCK:
+ use_errno = 1;
+ r = shmctl(shmid, SHM_UNLOCK, NULL);
+ break;
+
+ case LX_SHM_INFO:
+ r = lx_shmctl_shminfo(buf);
+ break;
+
+ case LX_SHM_STAT:
+ r = lx_shmctl_shmstat(shmid, buf);
+ break;
+ default:
+ r = -EINVAL;
+ break;
+ }
+
+ if (use_errno == 1 && r < 0)
+ return (-errno);
+
+ return (r);
+}
+
+/*
+ * Under 32-bit Linux, glibc funnels all of the sysv IPC operations into this
+ * single ipc(2) system call. We need to blow that up and filter the
+ * remnants into the proper Solaris system calls.
+ */
+long
+lx_ipc(uintptr_t cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
+ uintptr_t arg4)
+{
+ int r;
+ void *bufptr = (void *)arg4;
+
+ lx_debug("lx_ipc(%d, %d, %d, %d, 0x%p, %d)\n",
+ cmd, arg1, arg2, arg3, bufptr, arg4);
+
+ switch (cmd) {
+ case LX_MSGGET:
+ r = lx_msgget((key_t)arg1, (int)arg2);
+ break;
+ case LX_MSGSND:
+ r = lx_msgsnd((int)arg1, bufptr, (size_t)arg2, (int)arg3);
+ break;
+ case LX_MSGRCV:
+ {
+ struct {
+ void *msgp;
+ long msgtype;
+ } args;
+
+ /*
+ * Rather than passing 5 args into ipc(2) directly,
+ * glibc passes 4 args and uses the buf argument to
+ * point to a structure containing two args: a pointer
+ * to the message and the message type.
+ */
+ if (uucopy(bufptr, &args, sizeof (args)))
+ return (-errno);
+ r = lx_msgrcv((int)arg1, args.msgp, (size_t)arg2,
+ args.msgtype, (int)arg3);
+ }
+ break;
+ case LX_MSGCTL:
+ r = lx_msgctl((int)arg1, (int)arg2, bufptr);
+ break;
+ case LX_SEMCTL:
+ r = lx_semctl((int)arg1, (size_t)arg2, (int)arg3, bufptr);
+ break;
+ case LX_SEMOP:
+ /*
+ * 'struct sembuf' is the same on Linux and Solaris, so we
+ * pass bufptr straight through.
+ */
+ r = lx_semop((int)arg1, bufptr, (size_t)arg2);
+ break;
+ case LX_SEMGET:
+ r = lx_semget((int)arg1, (size_t)arg2, (int)arg3);
+ break;
+ case LX_SHMAT:
+ r = lx_shmat((int)arg1, bufptr, (size_t)arg2);
+ if (r >= 0 || r <= -4096) {
+ if (uucopy(&r, (void *)arg3, sizeof (r)) != 0)
+ r = -errno;
+ }
+ break;
+ case LX_SHMDT:
+ r = shmdt(bufptr);
+ if (r < 0)
+ r = -errno;
+ break;
+ case LX_SHMGET:
+ r = lx_shmget((int)arg1, (size_t)arg2, (int)arg3);
+ break;
+ case LX_SHMCTL:
+ r = lx_shmctl((int)arg1, (int)arg2, bufptr);
+ break;
+
+ default:
+ r = -EINVAL;
+ }
+
+ return (r);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/time.c b/usr/src/lib/brand/lx/lx_brand/common/time.c
new file mode 100644
index 0000000000..9b82ec98b5
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/time.c
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/times.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_misc.h>
+
+/*
+ * setitimer() - the Linux implementation can handle tv_usec values greater
+ * than 1,000,000 where Illumos would return EINVAL.
+ *
+ * There's still an issue here where Linux can handle a
+ * tv_sec value greater than 100,000,000 but Illumos cannot,
+ * but that would also mean setting an interval timer to fire
+ * over _three years_ in the future so it's unlikely anything
+ * other than Linux test suites will trip over it.
+ */
+long
+lx_setitimer(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ struct itimerval itv;
+ struct itimerval *itp = (struct itimerval *)p2;
+
+ if (itp != NULL) {
+ if (uucopy(itp, &itv, sizeof (itv)) != 0)
+ return (-errno);
+
+ /*
+ * Adjust any tv_usec fields >= 1,000,000 by adding any whole
+ * seconds so indicated to tv_sec and leaving tv_usec as the
+ * remainder.
+ */
+ if (itv.it_interval.tv_usec >= MICROSEC) {
+ itv.it_interval.tv_sec +=
+ itv.it_interval.tv_usec / MICROSEC;
+
+ itv.it_interval.tv_usec %= MICROSEC;
+ }
+ if (itv.it_value.tv_usec >= MICROSEC) {
+ itv.it_value.tv_sec +=
+ itv.it_value.tv_usec / MICROSEC;
+
+ itv.it_value.tv_usec %= MICROSEC;
+ }
+
+ itp = &itv;
+ }
+
+ return ((setitimer((int)p1, itp, (struct itimerval *)p3) != 0) ?
+ -errno : 0);
+}
+
+/*
+ * NOTE: The Linux man pages state this structure is obsolete and is
+ * unsupported, so it is declared here for sizing purposes only.
+ */
+struct lx_timezone {
+ int tz_minuteswest; /* minutes W of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+
+long
+lx_settimeofday(uintptr_t p1, uintptr_t p2)
+{
+ struct timeval tv;
+ struct lx_timezone tz;
+
+ if ((p1 != (uintptr_t)NULL) &&
+ (uucopy((struct timeval *)p1, &tv, sizeof (tv)) < 0))
+ return (-errno);
+
+ /*
+ * The Linux man page states use of the second parameter is obsolete,
+ * but settimeofday(2) should still return EFAULT if it is set
+ * to a bad non-NULL pointer (sigh...)
+ */
+ if ((p2 != (uintptr_t)NULL) &&
+ (uucopy((struct lx_timezone *)p2, &tz, sizeof (tz)) < 0))
+ return (-errno);
+
+ if ((p1 != (uintptr_t)NULL) && (settimeofday(&tv, NULL) < 0))
+ return (-errno);
+
+ return (0);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/truncate.c b/usr/src/lib/brand/lx/lx_brand/common/truncate.c
new file mode 100644
index 0000000000..ba3e452408
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/common/truncate.c
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <sys/lx_types.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+/*
+ * ZFS does not enforce the process.max-file-size rctl on a file which is
+ * grown in size via truncate/ftruncate, since that is simply metadata which
+ * does not consume any additional space. However, LTP truncate03 depends on
+ * this behavior so we enforce it here.
+ */
+static boolean_t
+p_fsize_excd(const char *path, off_t length)
+{
+ struct stat64 sb;
+ struct rlimit rl;
+
+ if (stat64(path, &sb) == 0 && sb.st_size < length) {
+ /* We are growing the file, check the rlimit */
+ if (getrlimit(RLIMIT_FSIZE, &rl) == 0 && length > rl.rlim_cur)
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+static boolean_t
+f_fsize_excd(int fd, off_t length)
+{
+ struct stat64 sb;
+ struct rlimit rl;
+
+ if (fstat64(fd, &sb) == 0 && sb.st_size < length) {
+ /* We are growing the file, check the rlimit */
+ if (getrlimit(RLIMIT_FSIZE, &rl) == 0 && length > rl.rlim_cur)
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * On Illumos, truncate() and ftruncate() are implemented in libc, so these are
+ * layered on those interfaces.
+ */
+
+long
+lx_truncate(uintptr_t path, uintptr_t length)
+{
+#if defined(_ILP32)
+ if ((off_t)length >= 0xffffffffUL)
+ return (-EFBIG);
+#endif
+
+ if (length > 0 && p_fsize_excd((const char *)path, (off_t)length))
+ return (-EFBIG);
+
+ return (truncate((const char *)path, (off_t)length) == 0 ? 0 : -errno);
+}
+
+long
+lx_ftruncate(uintptr_t fd, uintptr_t length)
+{
+ int r;
+
+#if defined(_ILP32)
+ if ((off_t)length >= 0xffffffffUL)
+ return (-EFBIG);
+#endif
+
+ if (length > 0 && f_fsize_excd((int)fd, (off_t)length))
+ return (-EFBIG);
+
+ r = ftruncate((int)fd, (off_t)length);
+ /*
+ * On Linux, truncating a file opened read-only returns EINVAL whereas
+ * Illumos returns EBADF.
+ */
+ if (r != 0) {
+ if (errno == EBADF) {
+ int mode;
+
+ if ((mode = fcntl(fd, F_GETFL, 0)) != -1 &&
+ (mode & O_ACCMODE) == O_RDONLY)
+ r = -EINVAL;
+ else
+ r = -EBADF; /* keep existing errno */
+ } else {
+ r = -errno;
+ }
+ }
+ return (r);
+}
+
+long
+lx_truncate64(uintptr_t path, uintptr_t length_lo, uintptr_t length_hi)
+{
+ uint64_t len = LX_32TO64(length_lo, length_hi);
+
+ if (len >= 0x7fffffffffffffffULL)
+ return (-EFBIG);
+
+ if (len > 0 && p_fsize_excd((const char *)path, (off_t)len))
+ return (-EFBIG);
+
+ return (truncate64((const char *)path, len) == 0 ? 0 : -errno);
+}
+
+long
+lx_ftruncate64(uintptr_t fd, uintptr_t length_lo, uintptr_t length_hi)
+{
+ int r;
+ uint64_t len = LX_32TO64(length_lo, length_hi);
+
+ if (len >= 0x7fffffffffffffffULL)
+ return (-EFBIG);
+
+ if (len > 0 && f_fsize_excd((int)fd, (off_t)len))
+ return (-EFBIG);
+
+ r = ftruncate64((int)fd, len);
+ /*
+ * On Linux, truncating a file opened read-only returns EINVAL whereas
+ * Illumos returns EBADF.
+ */
+ if (r != 0) {
+ if (errno == EBADF) {
+ int mode;
+
+ if ((mode = fcntl(fd, F_GETFL, 0)) != -1 &&
+ (mode & O_ACCMODE) == O_RDONLY)
+ r = -EINVAL;
+ else
+ r = -EBADF; /* keep existing errno */
+ } else {
+ r = -errno;
+ }
+ }
+
+ return (r);
+}
diff --git a/usr/src/lib/brand/lx/lx_brand/i386/Makefile b/usr/src/lib/brand/lx/lx_brand/i386/Makefile
new file mode 100644
index 0000000000..8723f64292
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/i386/Makefile
@@ -0,0 +1,48 @@
+#
+# 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.
+# Copyright 2015 Joyent, Inc.
+#
+# lib/brand/lx/i386/Makefile
+
+ISASRCDIR=.
+
+ASFLAGS += -P -D_ASM
+
+include ../Makefile.com
+
+DYNFLAGS += -Wl,-I/native/lib/ld.so.1
+
+POFILE= lx_brand.po
+MSGFILES= $(CSRCS)
+
+ASSYMDEP_OBJS = lx_handler.o
+
+install: all $(ROOTLIBS)
+
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+include $(SRC)/Makefile.msg.targ
diff --git a/usr/src/lib/brand/lx/lx_brand/i386/lx_crt.s b/usr/src/lib/brand/lx/lx_brand/i386/lx_crt.s
new file mode 100644
index 0000000000..c457c1c209
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/i386/lx_crt.s
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/asm_linkage.h>
+
+#if defined(lint)
+
+void
+_start(void)
+{
+}
+
+#else /* lint */
+
+ /*
+ * C language startup routine for the lx brand shared library.
+ */
+ ENTRY_NP(_start)
+ pushl $0 / Build a stack frame. retpc = NULL
+ pushl $0 / fp = NULL
+ movl %esp, %ebp / first stack frame
+
+ /*
+ * Calculate the location of the envp array by adding the size of
+ * the argv array to the start of the argv array.
+ */
+ movl 8(%ebp), %eax / argc in %eax
+ leal 16(%ebp,%eax,4), %edx / envp in %edx
+ andl $-16, %esp
+ pushl %edx / push envp
+ leal 12(%ebp),%edx / compute &argv[0]
+ pushl %edx / push argv
+ pushl %eax / push argc
+ call lx_init
+ /*
+ * lx_init will never return.
+ */
+ SET_SIZE(_start)
+
+#endif /* lint */
diff --git a/usr/src/lib/brand/lx/lx_brand/i386/lx_handler.s b/usr/src/lib/brand/lx/lx_brand/i386/lx_handler.s
new file mode 100644
index 0000000000..7afb9c4b7f
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/i386/lx_handler.s
@@ -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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/asm_linkage.h>
+#include <sys/regset.h>
+#include <sys/segments.h>
+#include <sys/syscall.h>
+#include <sys/lx_brand.h>
+
+#if defined(_ASM)
+#include <sys/lx_signal.h>
+#include <sys/lx_syscall.h>
+#endif /* _ASM */
+
+/* 32-bit syscall numbers */
+#define LX_SYS_sigreturn 119
+#define LX_SYS_rt_sigreturn 173
+
+#if defined(lint)
+
+#include <sys/types.h>
+#include <sys/regset.h>
+#include <sys/signal.h>
+
+void
+lx_sigreturn_tramp(void)
+{}
+
+void
+lx_rt_sigreturn_tramp(void)
+{}
+
+#else /* lint */
+
+ ENTRY_NP(lx_swap_gs)
+ push %eax /* save the current eax value */
+ movl 0xc(%esp),%eax /* 2nd param is a pointer */
+ movw %gs,(%eax) /* use the pointer to save current gs */
+ movl 0x8(%esp),%eax /* first parameter is the new gs value */
+ movw %ax, %gs /* switch to the new gs value */
+ pop %eax /* restore eax */
+ ret
+ SET_SIZE(lx_swap_gs)
+
+ /*
+ * Trampoline code is called by the return at the end of a Linux
+ * signal handler to return control to the interrupted application
+ * via the lx_sigreturn() or lx_rt_sigreturn() syscalls.
+ *
+ * (lx_sigreturn() is called for legacy signal handling, and
+ * lx_rt_sigreturn() is called for "new"-style signals.)
+ *
+ * These two routines must consist of the EXACT code sequences below
+ * as gdb looks at the sequence of instructions a routine will return
+ * to determine whether it is in a signal handler or not.
+ * See the Linux code setup_signal_stack_sc() in arch/x86/um/signal.c.
+ */
+ ENTRY_NP(lx_sigreturn_tramp)
+ popl %eax
+ movl $LX_SYS_sigreturn, %eax
+ int $0x80
+ SET_SIZE(lx_sigreturn_tramp)
+
+ ENTRY_NP(lx_rt_sigreturn_tramp)
+ movl $LX_SYS_rt_sigreturn, %eax
+ int $0x80
+ SET_SIZE(lx_rt_sigreturn_tramp)
+#endif /* lint */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h
new file mode 100644
index 0000000000..18e452876a
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _LX_DEBUG_H
+#define _LX_DEBUG_H
+
+#include <lx_provider_impl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* initialize the debugging subsystem */
+extern void lx_debug_init(boolean_t, boolean_t, const char *);
+
+/* printf() style debug message functionality */
+extern void lx_debug(const char *, ...);
+
+extern int lx_debug_enabled;
+
+#define LX_DEBUG_ISENABLED \
+ (lx_debug_enabled || LX_DEBUG_ENABLED())
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_DEBUG_H */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h
new file mode 100644
index 0000000000..455ac174df
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h
@@ -0,0 +1,179 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_H
+#define _SYS_LX_H
+
+#include <stdio.h>
+#include <alloca.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/lwp.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_thread.h>
+
+#include <lx_errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char lx_release[LX_KERN_RELEASE_MAX];
+extern char lx_cmd_name[MAXNAMLEN];
+extern pid_t zoneinit_pid;
+extern int lx_is_vforked;
+extern boolean_t lx_no_abort_handler;
+
+/*
+ * Values Linux expects for init
+ */
+#define LX_INIT_PID 1
+
+/*
+ * the maximum length of messages to be output with lx_msg(), lx_err(),
+ * lx_debug(), or lx_unsupported().
+ */
+#define LX_MSG_MAXLEN (128 + MAXPATHLEN)
+
+/*
+ * Linux scheduler priority ranges.
+ */
+#define LX_SCHED_PRIORITY_MIN_OTHER 0
+#define LX_SCHED_PRIORITY_MAX_OTHER 0
+#define LX_SCHED_PRIORITY_MIN_RRFIFO 1
+#define LX_SCHED_PRIORITY_MAX_RRFIFO 99
+
+/*
+ * Based on code from brand_misc.h, but use of that is incompatible with the
+ * lx brand.
+ *
+ * These macros invoke a brandsys subcommand, B_TRUSS_POINT, which makes it
+ * easy to debug with DTrace.
+ */
+#define B_TRUSS_POINT 6
+
+#define B_TRACE_POINT_5(a0, a1, a2, a3, a4) \
+ (void) syscall(SYS_brand, B_TRUSS_POINT, (a0), (a1), (a2), (a3), (a4))
+
+#define B_TRACE_POINT_4(a0, a1, a2, a3) \
+ B_TRACE_POINT_5((a0), (a1), (a2), (a3), 0)
+
+#define B_TRACE_POINT_3(a0, a1, a2) \
+ B_TRACE_POINT_5((a0), (a1), (a2), 0, 0)
+
+#define B_TRACE_POINT_2(a0, a1) \
+ B_TRACE_POINT_5((a0), (a1), 0, 0, 0)
+
+#define B_TRACE_POINT_1(a0) \
+ B_TRACE_POINT_5((a0), 0, 0, 0, 0)
+
+#define B_TRACE_POINT_0() \
+ B_TRACE_POINT_5(0, 0, 0, 0, 0)
+
+/*
+ * Macros to access register state within a ucontext_t:
+ */
+#define LX_REG(ucp, r) ((ucp)->uc_mcontext.gregs[(r)])
+
+/*
+ * normally we never want to write to stderr or stdout because it's unsafe
+ * to make assumptions about the underlying file descriptors. to protect
+ * against writes to these file descriptors we go ahead and close them
+ * our brand process initalization code. but there are still occasions
+ * where we are willing to make assumptions about our file descriptors
+ * and write to them. at thes times we should use one lx_msg() or
+ * lx_msg_error()
+ */
+extern void lx_msg(char *, ...);
+extern void lx_err(char *, ...);
+extern void lx_err_fatal(char *, ...);
+extern void lx_unsupported(char *, ...);
+
+struct ucontext;
+
+extern ucontext_t *lx_syscall_regs(void);
+extern uintptr_t lx_find_brand_sp(void);
+extern const ucontext_t *lx_find_brand_uc(void);
+
+extern void lx_jump_to_linux(ucontext_t *) __NORETURN;
+
+extern char *lx_fd_to_path(int fd, char *buf, int buf_size);
+extern int lx_lpid_to_spair(pid_t, pid_t *, lwpid_t *);
+extern int lx_lpid_to_spid(pid_t, pid_t *);
+
+extern void lx_ptrace_init();
+extern int lx_ptrace_wait(siginfo_t *);
+extern void lx_ptrace_fork(void);
+extern void lx_ptrace_stop_if_option(int, boolean_t, ulong_t msg, ucontext_t *);
+extern void lx_ptrace_clone_begin(int, boolean_t, int);
+
+extern int lx_check_alloca(size_t);
+#define SAFE_ALLOCA(sz) (lx_check_alloca(sz) ? alloca(sz) : NULL)
+
+extern void lx_init_tsd(lx_tsd_t *);
+extern int lx_alloc_stack(void **, size_t *);
+extern void lx_install_stack(void *, size_t, lx_tsd_t *);
+extern void lx_free_stack(void);
+extern void lx_free_other_stacks(void);
+extern void lx_stack_prefork(void);
+extern void lx_stack_postfork(void);
+
+extern void lx_block_all_signals();
+extern void lx_unblock_all_signals();
+extern int lx_all_signals_blocked();
+
+/*
+ * NO_UUCOPY disables calls to the uucopy* system calls to help with
+ * debugging brand library accesses to linux application memory.
+ */
+#ifdef NO_UUCOPY
+
+int uucopy_unsafe(const void *src, void *dst, size_t n);
+int uucopystr_unsafe(const void *src, void *dst, size_t n);
+
+#define uucopy(src, dst, n) uucopy_unsafe((src), (dst), (n))
+#define uucopystr(src, dst, n) uucopystr_unsafe((src), (dst), (n))
+
+#endif /* NO_UUCOPY */
+
+/*
+ * We use these Private libc interfaces to defer signals during critical
+ * sections.
+ */
+extern void _sigon(void);
+extern void _sigoff(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_H */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_mount.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_mount.h
new file mode 100644
index 0000000000..72e0342be6
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_mount.h
@@ -0,0 +1,163 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _LX_MOUNT_H
+#define _LX_MOUNT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rpc/rpc.h>
+#include <nfs/nfs.h>
+
+extern int lx_nfs_mount(char *, char *, char *, int, char *);
+
+/*
+ * mount() is significantly different between Linux and Solaris. The main
+ * difference is between the set of flags. Some flags on Linux can be
+ * translated to a Solaris equivalent, some are converted to a
+ * filesystem-specific option, while others have no equivalent whatsoever.
+ */
+#define LX_MS_MGC_VAL 0xC0ED0000
+#define LX_MS_RDONLY 0x00000001
+#define LX_MS_NOSUID 0x00000002
+#define LX_MS_NODEV 0x00000004
+#define LX_MS_NOEXEC 0x00000008
+#define LX_MS_SYNCHRONOUS 0x00000010
+#define LX_MS_REMOUNT 0x00000020
+#define LX_MS_MANDLOCK 0x00000040
+#define LX_MS_NOATIME 0x00000400
+#define LX_MS_NODIRATIME 0x00000800
+#define LX_MS_BIND 0x00001000
+#define LX_MS_MOVE 0x00002000
+#define LX_MS_REC 0x00004000
+#define LX_MS_SILENT 0x00008000
+#define LX_MS_POSIXACL 0x00010000
+#define LX_MS_UNBINDABLE 0x00020000
+#define LX_MS_PRIVATE 0x00040000
+#define LX_MS_SLAVE 0x00080000
+#define LX_MS_SHARED 0x00100000
+#define LX_MS_RELATIME 0x00200000
+#define LX_MS_KERNMOUNT 0x00400000
+#define LX_MS_I_VERSION 0x00800000
+#define LX_MS_STRICTATIME 0x01000000
+#define LX_MS_LAZYTIME 0x02000000
+
+/* internal flags - ignored if passed in */
+#define LX_MS_NOSEC 0x10000000
+#define LX_MS_BORN 0x20000000
+#define LX_MS_ACTIVE 0x40000000
+#define LX_MS_NOUSER 0x80000000
+
+#define LX_MS_SUPPORTED (LX_MS_MGC_VAL | \
+ LX_MS_RDONLY | LX_MS_NOSUID | \
+ LX_MS_NODEV | LX_MS_NOEXEC | \
+ LX_MS_REMOUNT | LX_MS_NOATIME | \
+ LX_MS_NODIRATIME | LX_MS_BIND | LX_MS_SILENT | \
+ LX_MS_STRICTATIME | LX_MS_NOSEC | \
+ LX_MS_BORN | LX_MS_ACTIVE | LX_MS_NOUSER)
+
+/*
+ * support for nfs mounts
+ */
+#define LX_NMD_MAXHOSTNAMELEN 256
+
+#define LX_NFS_MOUNT_SOFT 0x00000001
+#define LX_NFS_MOUNT_INTR 0x00000002
+#define LX_NFS_MOUNT_SECURE 0x00000004
+#define LX_NFS_MOUNT_POSIX 0x00000008
+#define LX_NFS_MOUNT_NOCTO 0x00000010
+#define LX_NFS_MOUNT_NOAC 0x00000020
+#define LX_NFS_MOUNT_TCP 0x00000040
+#define LX_NFS_MOUNT_VER3 0x00000080
+#define LX_NFS_MOUNT_KERBEROS 0x00000100
+#define LX_NFS_MOUNT_NONLM 0x00000200
+#define LX_NFS_MOUNT_BROKEN_SUID 0x00000400
+#define LX_NFS_MOUNT_SUPPORTED (LX_NFS_MOUNT_SOFT | \
+ LX_NFS_MOUNT_INTR | \
+ LX_NFS_MOUNT_POSIX | \
+ LX_NFS_MOUNT_NOCTO | \
+ LX_NFS_MOUNT_NOAC | \
+ LX_NFS_MOUNT_TCP | \
+ LX_NFS_MOUNT_VER3 | \
+ LX_NFS_MOUNT_NONLM)
+
+#define LX_NMD_DEFAULT_RSIZE 0
+#define LX_NMD_DEFAULT_WSIZE 0
+
+/*
+ * the nfs v3 file handle structure definitions are _almost_ the same
+ * on linux and solaris. the key difference are:
+ *
+ * 1) on linux fh3_length is an unsigned short where as on solaris it's
+ * an int.
+ *
+ * 2) on linux the file handle data doesn't 32 bit members, so the structure
+ * is not 32 bit aligned. (where as on solaris it is.)
+ *
+ * so rather than defining a structure that would allow us to intrepret
+ * all the contents of the nfs v3 file handle here, we decide to treate
+ * the file handle as an array of chars. this works just fine since it
+ * avoids the alignment issues and the actual file handle handle contects
+ * are defined by the nfs specification so they are common across solaris
+ * and linux. we do the same thing for nfs v2 file handles.
+ */
+struct lx_nfs_fh2 {
+ unsigned char lx_fh_data[NFS_FHSIZE];
+};
+
+struct lx_nfs_fh3 {
+ unsigned short lx_fh3_length;
+ unsigned char lx_fh3_data[NFS3_FHSIZE];
+};
+
+typedef struct lx_nfs_mount_data {
+ int nmd_version;
+ int nmd_fd;
+ struct lx_nfs_fh2 nmd_old_root;
+ int nmd_flags;
+ int nmd_rsize;
+ int nmd_wsize;
+ int nmd_timeo;
+ int nmd_retrans;
+ int nmd_acregmin;
+ int nmd_acregmax;
+ int nmd_acdirmin;
+ int nmd_acdirmax;
+ struct sockaddr_in nmd_addr;
+ char nmd_hostname[LX_NMD_MAXHOSTNAMELEN];
+ int nmd_namlen;
+ uint_t nmd_bsize;
+ struct lx_nfs_fh3 nmd_root;
+} lx_nfs_mount_data_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_MOUNT_H */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_poll.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_poll.h
new file mode 100644
index 0000000000..99abdbbf46
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_poll.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYS_LX_POLL_H
+#define _SYS_LX_POLL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * These events are identical between Linux and Solaris
+ */
+#define LX_POLLIN 0x001
+#define LX_POLLPRI 0x002
+#define LX_POLLOUT 0x004
+#define LX_POLLERR 0x008
+#define LX_POLLHUP 0x010
+#define LX_POLLNVAL 0x020
+#define LX_POLLRDNORM 0x040
+#define LX_POLLRDBAND 0x080
+
+#define LX_POLL_COMMON_EVENTS (LX_POLLIN | LX_POLLPRI | LX_POLLOUT | \
+ LX_POLLERR | LX_POLLHUP | LX_POLLNVAL | LX_POLLRDNORM | LX_POLLRDBAND)
+
+/*
+ * These events differ between Linux and Solaris
+ */
+#define LX_POLLWRNORM 0x0100
+#define LX_POLLWRBAND 0x0200
+#define LX_POLLRDHUP 0x2000
+
+
+#define LX_POLL_SUPPORTED_EVENTS \
+ (LX_POLL_COMMON_EVENTS | LX_POLLWRNORM | LX_POLLWRBAND | LX_POLLRDHUP)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_POLL_H */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h
new file mode 100644
index 0000000000..9d77524410
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h
@@ -0,0 +1,289 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_SIGNAL_H
+#define _SYS_LX_SIGNAL_H
+
+#if !defined(_ASM)
+#include <sys/lx_types.h>
+#include <sys/ucontext.h>
+#include <sys/lx_siginfo.h>
+#include <lx_signum.h>
+
+#endif /* !defined(_ASM) */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Linux sigaction flags
+ */
+#define LX_SA_NOCLDSTOP 0x00000001
+#define LX_SA_NOCLDWAIT 0x00000002
+#define LX_SA_SIGINFO 0x00000004
+#define LX_SA_RESTORER 0x04000000
+#define LX_SA_ONSTACK 0x08000000
+#define LX_SA_RESTART 0x10000000
+#define LX_SA_NODEFER 0x40000000
+#define LX_SA_RESETHAND 0x80000000
+#define LX_SA_NOMASK LX_SA_NODEFER
+#define LX_SA_ONESHOT LX_SA_RESETHAND
+
+#define LX_SIG_BLOCK 0
+#define LX_SIG_UNBLOCK 1
+#define LX_SIG_SETMASK 2
+
+#define LX_MINSIGSTKSZ 2048
+#define LX_SS_ONSTACK 1
+#define LX_SS_DISABLE 2
+
+#define LX_SIGRT_MAGIC 0xdeadf00d
+
+#if !defined(_ASM)
+
+typedef struct lx_sigaction {
+ void (*lxsa_handler)();
+ int lxsa_flags;
+ void (*lxsa_restorer)(void);
+ lx_sigset_t lxsa_mask;
+} lx_sigaction_t;
+
+#if defined(_ILP32)
+typedef uint32_t lx_osigset_t;
+
+#define OSIGSET_NBITS (sizeof (lx_osigset_t) * NBBY)
+#define OSIGSET_BITSET(sig) (1U << (((sig) - 1) % OSIGSET_NBITS))
+
+typedef struct lx_osigaction {
+ void (*lxsa_handler)();
+ lx_osigset_t lxsa_mask;
+ int lxsa_flags;
+ void (*lxsa_restorer)(void);
+} lx_osigaction_t;
+#endif
+
+/*
+ * Flag settings to determine whether common routines should operate on
+ * lx_sigset_ts or lx_osigset_ts.
+ */
+#define USE_OSIGSET 0
+#define USE_SIGSET 1
+
+typedef struct lx_sighandlers {
+ struct lx_sigaction lx_sa[LX_NSIG + 1];
+} lx_sighandlers_t;
+
+typedef struct lx_sigaltstack {
+ void *ss_sp;
+ int ss_flags;
+ size_t ss_size;
+} lx_stack_t;
+
+/*
+ * _fpreg, _fpxreg, _xmmreg and _fpstate are defined in Linux src in:
+ * arch/x86/include/uapi/asm/sigcontext.h
+ */
+#define LX_X86_FXSR_MAGIC 0x0000
+#define LX_X86_FXSR_NONE 0xffff
+
+#if defined(_LP64)
+
+typedef struct lx_fpstate {
+ ushort_t cwd;
+ ushort_t swd;
+ ushort_t twd; /* Note this is not the same as the 32bit/x87/FSAVE twd */
+ ushort_t fop;
+ uint64_t rip;
+ uint64_t rdp;
+ uint32_t mxcsr;
+ uint32_t mxcsr_mask;
+ uint32_t st_space[32]; /* 8 * 16 bytes for each FP-reg */
+ uint32_t xmm_space[64]; /* 16 * 16 bytes for each XMM-reg */
+ uint32_t reserved2[12];
+ uint32_t reserved3[12];
+} lx_fpstate_t;
+
+/*
+ * The Linux layout is defined in the Linux src tree in:
+ * arch/x86/include/asm/sigcontext.h
+ * and the user-level def (which is what we want) at:
+ * arch/x86/include/uapi/asm/sigcontext.h
+ *
+ * The Illumos offsets of the registers in the context are defined in:
+ * usr/src/uts/intel/sys/regset.h
+ * this is an mcontext_t from uc_mcontext.
+ *
+ * For the 64-bit case the register layout is completely different in the
+ * context.
+ */
+typedef struct lx_sigcontext {
+ ulong_t sc_r8;
+ ulong_t sc_r9;
+ ulong_t sc_r10;
+ ulong_t sc_r11;
+ ulong_t sc_r12;
+ ulong_t sc_r13;
+ ulong_t sc_r14;
+ ulong_t sc_r15;
+ ulong_t sc_rdi;
+ ulong_t sc_rsi;
+ ulong_t sc_rbp;
+ ulong_t sc_rbx;
+ ulong_t sc_rdx;
+ ulong_t sc_rax;
+ ulong_t sc_rcx;
+ ulong_t sc_rsp;
+ ulong_t sc_rip;
+ ulong_t sc_eflags;
+ ushort_t sc_cs;
+ ushort_t sc_gs;
+ ushort_t sc_fs;
+ ushort_t sc_pad0;
+ ulong_t sc_err;
+ ulong_t sc_trapno;
+
+ ulong_t sc_mask;
+ ulong_t sc_cr2;
+ lx_fpstate_t *sc_fpstate;
+
+ ulong_t reserved[8];
+} lx_sigcontext_t;
+
+#else /* is _ILP32 */
+
+struct lx_fpreg {
+ ushort_t significand[4];
+ ushort_t exponent;
+};
+
+struct lx_fpxreg {
+ ushort_t significand[4];
+ ushort_t exponent;
+ ushort_t padding[3];
+};
+
+struct lx_xmmreg {
+ uint32_t element[4];
+};
+
+typedef struct lx_fpstate {
+ /* Regular FPU environment */
+ ulong_t cw;
+ ulong_t sw;
+ ulong_t tag;
+ ulong_t ipoff;
+ ulong_t cssel;
+ ulong_t dataoff;
+ ulong_t datasel;
+ struct lx_fpreg _st[8];
+ ushort_t status;
+ ushort_t magic; /* 0xffff = regular FPU data */
+
+ /* FXSR FPU environment */
+ ulong_t _fxsr_env[6]; /* env is ignored */
+ ulong_t mxcsr;
+ ulong_t reserved;
+ struct lx_fpxreg _fxsr_st[8]; /* reg data is ignored */
+ struct lx_xmmreg _xmm[8];
+ ulong_t padding[56];
+} lx_fpstate_t;
+
+/*
+ * The Linux layout is defined in the Linux src tree in:
+ * arch/x86/include/asm/sigcontext.h
+ * and the user-level def (which is what we want) at:
+ * arch/x86/include/uapi/asm/sigcontext.h
+ *
+ * The Illumos offsets of the registers in the context are defined by the
+ * i386 ABI (see usr/src/uts/intel/sys/regset.h).
+ *
+ * Both Illumos and Linux match up here.
+ */
+typedef struct lx_sigcontext {
+ ulong_t sc_gs;
+ ulong_t sc_fs;
+ ulong_t sc_es;
+ ulong_t sc_ds;
+ ulong_t sc_edi;
+ ulong_t sc_esi;
+ ulong_t sc_ebp;
+ ulong_t sc_esp;
+ ulong_t sc_ebx;
+ ulong_t sc_edx;
+ ulong_t sc_ecx;
+ ulong_t sc_eax;
+ ulong_t sc_trapno;
+ ulong_t sc_err;
+ ulong_t sc_eip;
+ ulong_t sc_cs;
+ ulong_t sc_eflags;
+ ulong_t sc_esp_at_signal;
+ ulong_t sc_ss;
+
+ lx_fpstate_t *sc_fpstate;
+ ulong_t sc_mask;
+ ulong_t sc_cr2;
+} lx_sigcontext_t;
+#endif
+
+typedef struct lx_ucontext {
+ ulong_t uc_flags; /* Linux always sets this to 0 */
+ struct lx_ucontext *uc_link; /* Linux always sets this to NULL */
+ lx_stack_t uc_stack;
+ lx_sigcontext_t uc_sigcontext;
+ lx_sigset_t uc_sigmask;
+} lx_ucontext_t;
+
+typedef struct lx_sigbackup lx_sigbackup_t;
+struct lx_sigbackup {
+ ucontext_t *lxsb_retucp;
+ ucontext_t *lxsb_sigucp;
+ uintptr_t lxsb_sigdeliver_frame;
+ lx_sigbackup_t *lxsb_previous;
+};
+
+extern const int ltos_signo[];
+extern const int stol_signo[];
+
+extern void setsigacthandler(void (*)(int, siginfo_t *, void *),
+ void (**)(int, siginfo_t *, void *),
+ int (*)(const ucontext_t *));
+
+extern int lx_siginit(void);
+extern void lx_sighandlers_save(lx_sighandlers_t *);
+extern void lx_sighandlers_restore(lx_sighandlers_t *);
+
+extern int stol_siginfo(siginfo_t *siginfop, lx_siginfo_t *lx_siginfop);
+extern int stol_status(int);
+
+#endif /* !defined(_ASM) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_SIGNAL_H */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_sigstack.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_sigstack.h
new file mode 100644
index 0000000000..fbbf462389
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_sigstack.h
@@ -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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_SIGSTACK_H
+#define _SYS_LX_SIGSTACK_H
+
+#if !defined(_ASM)
+#include <sys/lx_types.h>
+#include <sys/ucontext.h>
+#include <sys/lx_signal.h>
+#include <lx_signum.h>
+
+#endif /* !defined(_ASM) */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Two flavors of Linux signal stacks:
+ *
+ * lx_sigstack - used for "modern" signal handlers, in practice those
+ * that have the sigaction(2) flag SA_SIGINFO set
+ *
+ * lx_oldsigstack - used for legacy signal handlers, those that do not have
+ * the sigaction(2) flag SA_SIGINFO set or that were setup via
+ * the signal(2) call.
+ *
+ * NOTE: Since these structures will be placed on the stack and stack math will
+ * be done with their sizes, for the 32-bit code they must be word
+ * aligned in size (4 bytes) so the stack remains word aligned per the
+ * i386 ABI, or, for 64-bit code they must be 16 byte aligned as per the
+ * AMD64 ABI.
+ *
+ * The precise layout of these stack frames is also potentially
+ * depended on by particularly esoteric (or broken) software, and
+ * should be preserved. The Linux structures (rt_sigframe, et al)
+ * are defined in "arch/x86/include/asm/sigframe.h".
+ */
+#if defined(_LP64)
+typedef struct lx_sigstack {
+ void (*retaddr)(); /* address of real lx_rt_sigreturn code */
+ lx_ucontext_t uc; /* saved user context */
+ lx_siginfo_t si; /* saved signal information */
+ lx_fpstate_t fpstate; /* saved FP state */
+ char pad[2]; /* stack alignment */
+} lx_sigstack_t;
+#else
+struct lx_sigstack {
+ void (*retaddr)(); /* address of real lx_rt_sigreturn code */
+ int sig; /* signal number */
+ lx_siginfo_t *sip; /* points to "si" if valid, NULL if not */
+ lx_ucontext_t *ucp; /* points to "uc" */
+ lx_siginfo_t si; /* saved signal information */
+ lx_ucontext_t uc; /* saved user context */
+ lx_fpstate_t fpstate; /* saved FP state */
+ char trampoline[8]; /* code for trampoline to lx_rt_sigreturn() */
+};
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_SIGSTACK_H */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_socket.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_socket.h
new file mode 100644
index 0000000000..2bbc31a234
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_socket.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_LX_SOCKET_H
+#define _SYS_LX_SOCKET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/lx_types.h>
+
+/*
+ * Linux address family definitions
+ * Some of these are not supported
+ */
+#define LX_AF_UNSPEC 0 /* Unspecified */
+#define LX_AF_UNIX 1 /* local file/pipe name */
+#define LX_AF_INET 2 /* IP protocol family */
+#define LX_AF_AX25 3 /* Amateur Radio AX.25 */
+#define LX_AF_IPX 4 /* Novell Internet Protocol */
+#define LX_AF_APPLETALK 5 /* Appletalk */
+#define LX_AF_NETROM 6 /* Amateur radio */
+#define LX_AF_BRIDGE 7 /* Multiprotocol bridge */
+#define LX_AF_ATMPVC 8 /* ATM PVCs */
+#define LX_AF_X25 9 /* X.25 */
+#define LX_AF_INET6 10 /* IPV 6 */
+#define LX_AF_ROSE 11 /* Amateur Radio X.25 */
+#define LX_AF_DECnet 12 /* DECnet */
+#define LX_AF_NETBEUI 13 /* 802.2LLC */
+#define LX_AF_SECURITY 14 /* Security callback */
+#define LX_AF_KEY 15 /* key management */
+#define LX_AF_ROUTE 16 /* Alias to emulate 4.4BSD */
+#define LX_AF_PACKET 17 /* Packet family */
+#define LX_AF_ASH 18 /* Ash ? */
+#define LX_AF_ECONET 19 /* Acorn Econet */
+#define LX_AF_ATMSVC 20 /* ATM SVCs */
+#define LX_AF_SNA 22 /* Linux SNA */
+#define LX_AF_IRDA 23 /* IRDA sockets */
+#define LX_AF_PPPOX 24 /* PPPoX sockets */
+#define LX_AF_WANPIPE 25 /* Wanpipe API sockets */
+#define LX_AF_BLUETOOTH 31 /* Bluetooth sockets */
+#define LX_AF_MAX 33 /* MAX socket type */
+
+#define AF_NOTSUPPORTED -1
+#define AF_INVAL -2
+
+/*
+ * Linux ARP protocol hardware identifiers
+ */
+#define LX_ARPHRD_ETHER 1 /* Ethernet */
+#define LX_ARPHRD_LOOPBACK 772 /* Loopback */
+#define LX_ARPHRD_VOID 0xffff /* Unknown */
+
+/*
+ * Linux socket type definitions
+ */
+#define LX_SOCK_STREAM 1 /* Connection-based byte streams */
+#define LX_SOCK_DGRAM 2 /* Connectionless, datagram */
+#define LX_SOCK_RAW 3 /* Raw protocol interface */
+#define LX_SOCK_RDM 4 /* Reliably-delivered message */
+#define LX_SOCK_SEQPACKET 5 /* Sequenced packet stream */
+#define LX_SOCK_PACKET 10 /* Linux specific */
+#define LX_SOCK_MAX 11
+
+/*
+ * The Linux socket type can be or-ed with other flags (e.g. SOCK_CLOEXEC).
+ */
+#define LX_SOCK_TYPE_MASK 0xf
+
+/*
+ * Linux flags for socket, socketpair and accept4. These are or-ed into the
+ * socket type value. In the Linux net.h header these come from fcntl.h (note
+ * that they are in octal in the Linux header).
+ */
+#define LX_SOCK_CLOEXEC 0x80000
+#define LX_SOCK_NONBLOCK 0x800
+
+#define SOCK_NOTSUPPORTED -1
+#define SOCK_INVAL -2
+
+/*
+ * PF_PACKET protocol definitions.
+ */
+#define LX_ETH_P_802_3 0x0001
+#define LX_ETH_P_ALL 0x0003
+#define LX_ETH_P_802_2 0x0004
+#define LX_ETH_P_IP 0x0800
+#define LX_ETH_P_ARP 0x0806
+#define LX_ETH_P_IPV6 0x86DD
+
+/*
+ * Linux socketcall indices.
+ * These constitute all 17 socket related system calls
+ *
+ * These system calls are called via a single system call socketcall().
+ * The first arg being the endex of the system call type
+ */
+#define LX_SOCKET 1
+#define LX_BIND 2
+#define LX_CONNECT 3
+#define LX_LISTEN 4
+#define LX_ACCEPT 5
+#define LX_GETSOCKNAME 6
+#define LX_GETPEERNAME 7
+#define LX_SOCKETPAIR 8
+#define LX_SEND 9
+#define LX_RECV 10
+#define LX_SENDTO 11
+#define LX_RECVFROM 12
+#define LX_SHUTDOWN 13
+#define LX_SETSOCKOPT 14
+#define LX_GETSOCKOPT 15
+#define LX_SENDMSG 16
+#define LX_RECVMSG 17
+#define LX_ACCEPT4 18
+#define LX_RECVMMSG 19
+#define LX_SENDMMSG 20
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_SOCKET_H */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_statfs.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_statfs.h
new file mode 100644
index 0000000000..02f1a9b32b
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_statfs.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ * Copyright 2017 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _LX_STATFS_H
+#define _LX_STATFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int lx_statfs_init(void);
+
+struct lx_statfs {
+ size_t f_type;
+ size_t f_bsize;
+ ulong_t f_blocks;
+ ulong_t f_bfree;
+ ulong_t f_bavail;
+ ulong_t f_files;
+ ulong_t f_ffree;
+ u_longlong_t f_fsid;
+ size_t f_namelen;
+ size_t f_frsize;
+ size_t f_spare[5];
+};
+
+struct lx_statfs64 {
+ int f_type;
+ int f_bsize;
+ u_longlong_t f_blocks;
+ u_longlong_t f_bfree;
+ u_longlong_t f_bavail;
+ u_longlong_t f_files;
+ u_longlong_t f_ffree;
+ u_longlong_t f_fsid;
+ int f_namelen;
+ int f_frsize;
+ int f_spare[5];
+};
+
+/*
+ * These magic values are taken mostly from statfs(2) or magic.h
+ */
+#define LX_AUTOFS_SUPER_MAGIC 0x0187
+#define LX_CGROUP_SUPER_MAGIC 0x27e0eb
+#define LX_DEVFS_SUPER_MAGIC 0x1373
+#define LX_DEVPTS_SUPER_MAGIC 0x1cd1
+#define LX_EXT2_SUPER_MAGIC 0xEF53
+#define LX_ISOFS_SUPER_MAGIC 0x9660
+#define LX_MSDOS_SUPER_MAGIC 0x4d44
+#define LX_NFS_SUPER_MAGIC 0x6969
+#define LX_PROC_SUPER_MAGIC 0x9fa0
+#define LX_SYSFS_SUPER_MAGIC 0x62656572
+#define LX_TMPFS_SUPER_MAGIC 0x01021994
+#define LX_UFS_MAGIC 0x00011954
+#define LX_PIPEFS_MAGIC 0x50495045
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_STATFS_H */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
new file mode 100644
index 0000000000..6b06140db2
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
@@ -0,0 +1,194 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_SYSCALL_H
+#define _SYS_LX_SYSCALL_H
+
+#include <sys/lx_brand.h>
+
+#if !defined(_ASM)
+
+#include <sys/types.h>
+#include <sys/procset.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int lx_install;
+
+extern long lx_mknodat(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_futimesat(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_utimensat(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_fstatat64(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_stat(uintptr_t, uintptr_t);
+extern long lx_fstat(uintptr_t, uintptr_t);
+extern long lx_lstat(uintptr_t, uintptr_t);
+extern long lx_stat64(uintptr_t, uintptr_t);
+extern long lx_fstat64(uintptr_t, uintptr_t);
+extern long lx_lstat64(uintptr_t, uintptr_t);
+extern long lx_fcntl(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_fcntl64(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_readdir(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_execve(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_ioctl(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_settimeofday(uintptr_t, uintptr_t);
+extern long lx_mknod(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_capget(uintptr_t, uintptr_t);
+extern long lx_capset(uintptr_t, uintptr_t);
+
+extern long lx_clock_nanosleep(int, int flags, struct timespec *,
+ struct timespec *);
+extern long lx_adjtimex(void *);
+extern long lx_timer_settime(timer_t, int, struct itimerspec *,
+ struct itimerspec *);
+extern long lx_timer_gettime(timer_t, struct itimerspec *);
+extern long lx_timer_getoverrun(timer_t);
+extern long lx_timer_delete(timer_t);
+
+extern long lx_truncate(uintptr_t, uintptr_t);
+extern long lx_ftruncate(uintptr_t, uintptr_t);
+extern long lx_truncate64(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_ftruncate64(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_sysctl(uintptr_t);
+extern long lx_fsync(uintptr_t);
+extern long lx_fdatasync(uintptr_t);
+extern long lx_rmdir(uintptr_t);
+extern long lx_utime(uintptr_t, uintptr_t);
+extern long lx_sysfs(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_uname(uintptr_t);
+extern long lx_getgroups16(uintptr_t, uintptr_t);
+extern long lx_setgroups(uintptr_t, uintptr_t);
+extern long lx_setgroups16(uintptr_t, uintptr_t);
+
+extern long lx_query_module(uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+ uintptr_t);
+
+extern long lx_setitimer(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_clone(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_exit(uintptr_t);
+extern long lx_group_exit(uintptr_t);
+
+extern long lx_mount(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_statfs(uintptr_t, uintptr_t);
+extern long lx_fstatfs(uintptr_t, uintptr_t);
+extern long lx_statfs64(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_fstatfs64(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_sigreturn(void);
+extern long lx_rt_sigreturn(void);
+extern long lx_signal(uintptr_t, uintptr_t);
+extern long lx_sigaction(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_rt_sigaction(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_sigaltstack(uintptr_t, uintptr_t);
+extern long lx_sigpending(uintptr_t);
+extern long lx_rt_sigpending(uintptr_t, uintptr_t);
+extern long lx_sigprocmask(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_rt_sigprocmask(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_sigsuspend(uintptr_t);
+extern long lx_rt_sigsuspend(uintptr_t, uintptr_t);
+extern long lx_rt_sigwaitinfo(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_rt_sigtimedwait(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_rt_sigqueueinfo(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_rt_tgsigqueueinfo(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_futex(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+ uintptr_t);
+
+extern long lx_tkill(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+ uintptr_t);
+extern long lx_tgkill(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+ uintptr_t);
+
+extern long lx_sendfile(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_sendfile64(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_fork(void);
+extern long lx_vfork(void);
+extern long lx_exec(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_ptrace(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_xattr2(uintptr_t, uintptr_t);
+extern long lx_xattr3(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_xattr4(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_keyctl(void);
+
+extern long lx_ipc(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_msgget(key_t, int);
+extern long lx_msgsnd(int, void *, size_t, int);
+extern long lx_msgrcv(int, void *, size_t, long, int);
+extern long lx_msgctl(int, int, void *);
+extern long lx_semget(key_t, int, int);
+extern long lx_semop(int, void *, size_t);
+extern long lx_semtimedop(int, void *, size_t, struct timespec *);
+extern long lx_semctl(int, int, int, void *);
+extern long lx_shmget(key_t, size_t, int);
+extern long lx_shmat(int, void *, int);
+extern long lx_shmctl(int, int, void *);
+
+extern long lx_getgroups(int, gid_t *);
+extern long lx_inotify_add_watch(int, const char *, uint32_t);
+extern long lx_inotify_init(void);
+extern long lx_inotify_init1(int);
+extern long lx_inotify_rm_watch(int, int);
+extern long lx_shmdt(char *);
+extern long lx_signalfd(int, uintptr_t, size_t);
+extern long lx_signalfd4(int, uintptr_t, size_t, int);
+extern long lx_timerfd_create(int, int);
+extern long lx_timerfd_settime(int, int,
+ const struct itimerspec *, struct itimerspec *);
+extern long lx_timerfd_gettime(int, struct itimerspec *);
+extern long lx_utimes(const char *, const struct timeval *);
+
+#endif /* !defined(_ASM) */
+
+
+#if defined(_LP64)
+#define LX_SYS_clone 56
+#define LX_SYS_vfork 58
+#else
+#define LX_SYS_clone 120
+#define LX_SYS_vfork 190
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_SYSCALL_H */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_sysv_ipc.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_sysv_ipc.h
new file mode 100644
index 0000000000..f9f49598b7
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_sysv_ipc.h
@@ -0,0 +1,222 @@
+/*
+ * 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.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _LX_SYSV_IPC_H
+#define _LX_SYSV_IPC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * msg-related definitions.
+ */
+#define LX_IPC_CREAT 00001000
+#define LX_IPC_EXCL 00002000
+#define LX_IPC_NOWAIT 00004000
+
+#define LX_IPC_RMID 0
+#define LX_IPC_SET 1
+#define LX_IPC_STAT 2
+#define LX_IPC_INFO 3
+
+#define LX_IPC_64 0x0100
+
+#define LX_SEMOP 1
+#define LX_SEMGET 2
+#define LX_SEMCTL 3
+#define LX_MSGSND 11
+#define LX_MSGRCV 12
+#define LX_MSGGET 13
+#define LX_MSGCTL 14
+#define LX_SHMAT 21
+#define LX_SHMDT 22
+#define LX_SHMGET 23
+#define LX_SHMCTL 24
+
+#define LX_MSG_STAT 11
+#define LX_MSG_INFO 12
+
+#define LX_MSG_NOERROR 010000
+
+/*
+ * Linux hard codes the maximum msgbuf length to be 8192 bytes. Really.
+ */
+#define LX_MSGMAX 8192
+
+struct lx_ipc_perm {
+ key_t key;
+ uid_t uid;
+ uid_t gid;
+ uid_t cuid;
+ uid_t cgid;
+ ushort_t mode;
+ ushort_t _pad1;
+ ushort_t seq;
+ ushort_t _pad2;
+ ulong_t _unused1;
+ ulong_t _unused2;
+};
+
+struct lx_msqid_ds {
+ struct lx_ipc_perm msg_perm;
+ time_t msg_stime;
+#if defined(_ILP32)
+ ulong_t _unused1;
+#endif
+ time_t msg_rtime;
+#if defined(_ILP32)
+ ulong_t _unused2;
+#endif
+ time_t msg_ctime;
+#if defined(_ILP32)
+ ulong_t _unused3;
+#endif
+ ulong_t msg_cbytes;
+ ulong_t msg_qnum;
+ ulong_t msg_qbytes;
+ pid_t msg_lspid;
+ pid_t msg_lrpid;
+ ulong_t _unused4;
+ ulong_t _unused5;
+};
+
+struct lx_msginfo {
+ int msgpool;
+ int msgmap;
+ int msgmax;
+ int msgmnb;
+ int msgmni;
+ int msgssz;
+ int msgtql;
+ ushort_t msgseg;
+};
+
+/*
+ * semaphore-related definitions.
+ */
+#define LX_GETPID 11
+#define LX_GETVAL 12
+#define LX_GETALL 13
+#define LX_GETNCNT 14
+#define LX_GETZCNT 15
+#define LX_SETVAL 16
+#define LX_SETALL 17
+#define LX_SEM_STAT 18
+#define LX_SEM_INFO 19
+#define LX_SEM_UNDO 0x1000
+#define LX_SEMVMX 32767
+
+struct lx_semid_ds {
+ struct lx_ipc_perm sem_perm;
+ time_t sem_otime;
+ ulong_t _unused1;
+ time_t sem_ctime;
+ ulong_t _unused2;
+ ulong_t sem_nsems;
+ ulong_t _unused3;
+ ulong_t _unused4;
+};
+
+struct lx_seminfo {
+ int semmap;
+ int semmni;
+ int semmns;
+ int semmnu;
+ int semmsl;
+ int semopm;
+ int semume;
+ int semusz;
+ int semvmx;
+ int semaem;
+};
+
+union lx_semun {
+ int val;
+ struct lx_semid_ds *semds;
+ ushort_t *sems;
+ struct lx_seminfo *info;
+ uintptr_t dummy;
+};
+
+/*
+ * shm-related definitions
+ */
+#define LX_SHM_LOCKED 02000
+#define LX_SHM_RDONLY 010000
+#define LX_SHM_RND 020000
+#define LX_SHM_REMAP 040000
+
+#define LX_SHM_LOCK 11
+#define LX_SHM_UNLOCK 12
+#define LX_SHM_STAT 13
+#define LX_SHM_INFO 14
+
+struct lx_shmid_ds {
+ struct lx_ipc_perm shm_perm;
+ size_t shm_segsz;
+ time_t shm_atime;
+#if defined(_ILP32)
+ ulong_t _unused1;
+#endif
+ time_t shm_dtime;
+#if defined(_ILP32)
+ ulong_t _unused2;
+#endif
+ time_t shm_ctime;
+#if defined(_ILP32)
+ ulong_t _unused3;
+#endif
+ pid_t shm_cpid;
+ pid_t shm_lpid;
+ ushort_t shm_nattch;
+ ulong_t _unused4;
+ ulong_t _unused5;
+};
+
+struct lx_shm_info {
+ int used_ids;
+ ulong_t shm_tot;
+ ulong_t shm_rss;
+ ulong_t shm_swp;
+ ulong_t swap_attempts;
+ ulong_t swap_successes;
+};
+
+struct lx_shminfo {
+ ulong_t shmmax;
+ ulong_t shmmin;
+ ulong_t shmmni;
+ ulong_t shmseg;
+ ulong_t shmall;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_SYSV_IPC_H */
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h
new file mode 100644
index 0000000000..0a17705dd1
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ * Copyright 2016 Joyent, Inc
+ */
+
+#ifndef _SYS_LX_THREAD_H
+#define _SYS_LX_THREAD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/lx_signal.h>
+#include <thread.h>
+
+typedef enum lx_exit_type {
+ LX_ET_NONE = 0,
+ LX_ET_EXIT,
+ LX_ET_EXIT_GROUP
+} lx_exit_type_t;
+
+typedef struct lx_tsd {
+ /* per-thread flag set on parent vfork, cleared on thread resume */
+ int lxtsd_is_vforked;
+ lx_exit_type_t lxtsd_exit;
+ int lxtsd_exit_status;
+ ucontext_t lxtsd_exit_context;
+
+ /*
+ * If this value is non-zero, we use it in lx_sigdeliver() to represent
+ * the in-use extent of the Linux (i.e. BRAND) stack for this thread.
+ * Access to this value must be protected by _sigoff()/_sigon().
+ */
+ uintptr_t lxtsd_lx_sp;
+
+ /*
+ * Alternate stack for Linux sigaltstack emulation:
+ */
+ lx_stack_t lxtsd_sigaltstack;
+
+ void *lxtsd_clone_state;
+
+ lx_sigbackup_t *lxtsd_sigbackup;
+} lx_tsd_t;
+
+extern thread_key_t lx_tsd_key;
+
+extern void lx_swap_gs(long, long *);
+
+extern void lx_exit_common(void) __NORETURN;
+
+extern lx_tsd_t *lx_get_tsd(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_THREAD_H */
diff --git a/usr/src/lib/brand/lx/lx_init/Makefile b/usr/src/lib/brand/lx/lx_init/Makefile
new file mode 100644
index 0000000000..7ce36ba51d
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_init/Makefile
@@ -0,0 +1,74 @@
+#
+# 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 2019 Joyent, Inc.
+#
+
+PROG = lxinit
+
+PROG_OBJS = lxinit.o pipe_stream.o run_command.o
+LIST_OBJS = list.o
+
+OBJS = $(PROG_OBJS) \
+ $(LIST_OBJS)
+SRCS = $(PROG_OBJS:%.o=%.c) \
+ $(LIST_OBJS:%.o=$(SRC)/common/list/%.c)
+
+all: $(PROG)
+
+include ../Makefile.lx
+include $(SRC)/cmd/Makefile.cmd
+
+# override the install directory
+ROOTBIN = $(ROOTBRANDDIR)
+CLOBBERFILES = $(OBJS) $(ROOTPROG)
+
+UTSBASE = $(SRC)/uts
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -D_REENTRANT -I$(UTSBASE)/common/brand/lx
+LDLIBS += -lzonecfg -lipadm -lsocket -linetutil -lnsl -lcustr -ldhcpagent
+
+# not linted
+SMATCH=off
+
+.KEEP_STATE:
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(PROG) $(OBJS)
+
+lint: lint_PROG lint_SRCS
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: %.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+%.o: $(SRC)/common/list/%.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/lib/brand/lx/lx_init/lxinit.c b/usr/src/lib/brand/lx/lx_init/lxinit.c
new file mode 100644
index 0000000000..5f98d4598e
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_init/lxinit.c
@@ -0,0 +1,1025 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+/*
+ * lxinit performs zone-specific initialization prior to handing control to the
+ * guest Linux init. This primarily consists of:
+ *
+ * - Starting ipmgmtd
+ * - Configuring network interfaces
+ * - Adding a default route
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <net/if.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stropts.h>
+#include <sys/ioccom.h>
+#include <sys/stat.h>
+#include <sys/systeminfo.h>
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/varargs.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <locale.h>
+#include <libcustr.h>
+
+#include <netinet/dhcp.h>
+#include <dhcpagent_util.h>
+#include <dhcpagent_ipc.h>
+
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <libipadm.h>
+#include <libzonecfg.h>
+#include <libinetutil.h>
+#include <sys/lx_brand.h>
+
+#include "run_command.h"
+
+static void lxi_err(char *msg, ...) __NORETURN;
+static void lxi_err(char *msg, ...);
+
+#define IPMGMTD_PATH "/lib/inet/ipmgmtd"
+#define IN_NDPD_PATH "/usr/lib/inet/in.ndpd"
+#define HOOK_POSTNET_PATH "/usr/lib/brand/lx/lx_hook_postnet"
+
+#define PREFIX_LOG_WARN "lx_init warn: "
+#define PREFIX_LOG_ERR "lx_init err: "
+
+#define RTMBUFSZ (sizeof (struct rt_msghdr) + \
+ (3 * sizeof (struct sockaddr_in)))
+
+ipadm_handle_t iph;
+
+static void
+lxi_err(char *msg, ...)
+{
+ char buf[1024];
+ int len;
+ va_list ap;
+
+ va_start(ap, msg);
+ /*LINTED*/
+ len = vsnprintf(buf, sizeof (buf), msg, ap);
+ va_end(ap);
+
+ (void) write(1, PREFIX_LOG_ERR, strlen(PREFIX_LOG_ERR));
+ (void) write(1, buf, len);
+ (void) write(1, "\n", 1);
+
+ /*
+ * Since a non-zero exit will cause the zone to reboot, a pause here
+ * will prevent a mis-configured zone from spinning in a reboot loop.
+ */
+ pause();
+ exit(1);
+ /*NOTREACHED*/
+}
+
+static void
+lxi_warn(char *msg, ...)
+{
+ char buf[1024];
+ int len;
+ va_list ap;
+
+ va_start(ap, msg);
+ /*LINTED*/
+ len = vsnprintf(buf, sizeof (buf), msg, ap);
+ va_end(ap);
+
+ (void) write(1, PREFIX_LOG_WARN, strlen(PREFIX_LOG_WARN));
+ (void) write(1, buf, len);
+ (void) write(1, "\n", 1);
+}
+
+static void
+lxi_log_open()
+{
+ int fd = open("/dev/console", O_WRONLY);
+
+ if (fd < 0) {
+ /* hard to log at this point... */
+ exit(1);
+ } else if (fd != 1) {
+ /*
+ * Use stdout as the log fd. Init should start with no files
+ * open, so we should be required to perform this relocation
+ * every time.
+ */
+ if (dup2(fd, 1) != 1) {
+ exit(1);
+ }
+ }
+}
+
+static void
+lxi_log_close()
+{
+ (void) close(0);
+ (void) close(1);
+}
+
+static zone_dochandle_t
+lxi_config_open()
+{
+ zoneid_t zoneid;
+ char zonename[ZONENAME_MAX];
+ zone_dochandle_t handle;
+ zone_iptype_t iptype;
+ int res;
+
+ zoneid = getzoneid();
+ if (getzonenamebyid(zoneid, zonename, sizeof (zonename)) < 0) {
+ lxi_err("could not determine zone name");
+ }
+
+ if ((handle = zonecfg_init_handle()) == NULL)
+ lxi_err("internal libzonecfg.so.1 error", 0);
+
+ if ((res = zonecfg_get_handle(zonename, handle)) != Z_OK) {
+ zonecfg_fini_handle(handle);
+ lxi_err("could not locate zone config %d", res);
+ }
+
+ /*
+ * Only exclusive stack is supported.
+ */
+ if (zonecfg_get_iptype(handle, &iptype) != Z_OK ||
+ iptype != ZS_EXCLUSIVE) {
+ zonecfg_fini_handle(handle);
+ lxi_err("lx zones do not support shared IP stacks");
+ }
+
+ return (handle);
+
+}
+
+static int
+zone_find_attr(struct zone_res_attrtab *attrs, const char *name,
+ const char **result)
+{
+ while (attrs != NULL) {
+ if (strncmp(attrs->zone_res_attr_name, name,
+ MAXNAMELEN) == 0) {
+ *result = attrs->zone_res_attr_value;
+ return (0);
+ }
+ attrs = attrs->zone_res_attr_next;
+ }
+ return (-1);
+}
+
+void
+lxi_svc_start(char *name, char *path, char *fmri)
+{
+ pid_t pid;
+ int status;
+ char *const argv[] = {
+ name,
+ NULL
+ };
+ char *const envp[] = {
+ fmri,
+ NULL
+ };
+
+ pid = fork();
+ if (pid == -1) {
+ lxi_err("fork() failed: %s", strerror(errno));
+ }
+
+ if (pid == 0) {
+ /* child */
+ const char *zroot = zone_get_nroot();
+ char cmd[MAXPATHLEN];
+
+ /*
+ * Construct the full path to the binary, including the native
+ * system root (e.g. "/native") if in use for this zone:
+ */
+ (void) snprintf(cmd, sizeof (cmd), "%s%s", zroot != NULL ?
+ zroot : "", path);
+
+ execve(cmd, argv, envp);
+
+ lxi_err("execve(%s) failed: %s", cmd, strerror(errno));
+ /* NOTREACHED */
+ }
+
+ /* parent */
+ while (wait(&status) != pid) {
+ /* EMPTY */;
+ }
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0) {
+ lxi_err("%s[%d] exited: %d", name,
+ (int)pid, WEXITSTATUS(status));
+ }
+ } else if (WIFSIGNALED(status)) {
+ lxi_err("%s[%d] died on signal: %d", name,
+ (int)pid, WTERMSIG(status));
+ } else {
+ lxi_err("%s[%d] failed in unknown way", name,
+ (int)pid);
+ }
+}
+
+void
+lxi_net_ipmgmtd_start()
+{
+ lxi_svc_start("ipmgmtd", IPMGMTD_PATH,
+ "SMF_FMRI=svc:/network/ip-interface-management:default");
+}
+
+void
+lxi_net_ndpd_start()
+{
+ lxi_svc_start("in.ndpd", IN_NDPD_PATH,
+ "SMF_FMRI=svc:/network/routing/ndp:default");
+}
+
+
+static void
+lxi_net_ipadm_open()
+{
+ ipadm_status_t status;
+
+ if ((status = ipadm_open(&iph, IPH_LEGACY)) != IPADM_SUCCESS) {
+ lxi_err("Error opening ipadm handle: %s",
+ ipadm_status2str(status));
+ }
+}
+
+static void
+lxi_net_ipadm_close()
+{
+ ipadm_close(iph);
+}
+
+void
+lxi_net_plumb(const char *iface)
+{
+ ipadm_status_t status;
+ char ifbuf[LIFNAMSIZ];
+
+ /* ipadm_create_if stomps on ifbuf, so create a copy: */
+ (void) strncpy(ifbuf, iface, sizeof (ifbuf));
+
+ if ((status = ipadm_create_if(iph, ifbuf, AF_INET, IPADM_OPT_ACTIVE))
+ != IPADM_SUCCESS) {
+ lxi_err("ipadm_create_if error %d: %s/v4: %s",
+ status, iface, ipadm_status2str(status));
+ }
+
+ if ((status = ipadm_create_if(iph, ifbuf, AF_INET6, IPADM_OPT_ACTIVE))
+ != IPADM_SUCCESS) {
+ lxi_err("ipadm_create_if error %d: %s/v6: %s",
+ status, iface, ipadm_status2str(status));
+ }
+}
+
+static int
+lxi_getif(int af, char *iface, int len, boolean_t first_ipv4_configured)
+{
+ struct lifreq lifr;
+ int s = socket(af, SOCK_DGRAM, 0);
+ if (s < 0) {
+ lxi_warn("socket error %d: bringing up %s: %s",
+ errno, iface, strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * We need a new logical interface for every IP address we add, except
+ * for the very first IPv4 address.
+ */
+ if (af == AF_INET6 || first_ipv4_configured) {
+ (void) strncpy(lifr.lifr_name, iface, sizeof (lifr.lifr_name));
+ (void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
+ if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0) {
+ if (close(s) != 0) {
+ lxi_warn("failed to close socket: %s\n",
+ strerror(errno));
+ }
+ return (-1);
+ }
+ (void) strncpy(iface, lifr.lifr_name, len);
+ }
+
+ if (close(s) != 0) {
+ lxi_warn("failed to close socket: %s\n",
+ strerror(errno));
+ }
+ return (0);
+}
+
+static int
+lxi_iface_ip(const char *origiface, const char *addr,
+ boolean_t *first_ipv4_configured)
+{
+ static int addrnum = 0;
+ ipadm_status_t status;
+ ipadm_addrobj_t ipaddr = NULL;
+ char iface[LIFNAMSIZ];
+ char aobjname[IPADM_AOBJSIZ];
+ int af, err = 0;
+
+ (void) strncpy(iface, origiface, sizeof (iface));
+
+ af = strstr(addr, ":") == NULL ? AF_INET : AF_INET6;
+ if (lxi_getif(af, iface, sizeof (iface), *first_ipv4_configured) != 0) {
+ lxi_warn("failed to create new logical interface "
+ "on %s: %s", origiface, strerror(errno));
+ return (-1);
+ }
+
+ (void) snprintf(aobjname, IPADM_AOBJSIZ, "%s/addr%d", iface,
+ addrnum++);
+
+ if ((status = ipadm_create_addrobj(IPADM_ADDR_STATIC, aobjname,
+ &ipaddr)) != IPADM_SUCCESS) {
+ lxi_warn("ipadm_create_addrobj error %d: addr %s, "
+ "interface %s: %s\n", status, addr, iface,
+ ipadm_status2str(status));
+ return (-2);
+ }
+
+ if ((status = ipadm_set_addr(ipaddr, addr, AF_UNSPEC))
+ != IPADM_SUCCESS) {
+ lxi_warn("ipadm_set_addr error %d: addr %s"
+ ", interface %s: %s\n", status, addr,
+ iface, ipadm_status2str(status));
+ err = -3;
+ goto done;
+ }
+
+ if ((status = ipadm_create_addr(iph, ipaddr,
+ IPADM_OPT_ACTIVE | IPADM_OPT_UP)) != IPADM_SUCCESS) {
+ lxi_warn("ipadm_create_addr error for %s: %s\n", iface,
+ ipadm_status2str(status));
+ err = -4;
+ goto done;
+ }
+
+ if (af == AF_INET) {
+ *first_ipv4_configured = B_TRUE;
+ }
+
+done:
+ ipadm_destroy_addrobj(ipaddr);
+ return (err);
+}
+
+static int
+lxi_iface_dhcp(const char *origiface, boolean_t *first_ipv4_configured)
+{
+ dhcp_ipc_request_t *dhcpreq = NULL;
+ dhcp_ipc_reply_t *dhcpreply = NULL;
+ int err = 0, timeout = 5;
+ char iface[LIFNAMSIZ];
+
+ (void) strncpy(iface, origiface, sizeof (iface));
+
+ if (lxi_getif(AF_INET, iface, sizeof (iface), *first_ipv4_configured)
+ != 0) {
+ lxi_warn("failed to create new logical interface "
+ "on %s: %s", origiface, strerror(errno));
+ return (-1);
+ }
+
+ if (dhcp_start_agent(timeout) != 0) {
+ lxi_err("Failed to start dhcpagent\n");
+ /* NOTREACHED */
+ }
+
+ dhcpreq = dhcp_ipc_alloc_request(DHCP_START, iface,
+ NULL, 0, DHCP_TYPE_NONE);
+ if (dhcpreq == NULL) {
+ lxi_warn("Unable to allocate memory "
+ "to start DHCP on %s\n", iface);
+ return (-1);
+ }
+
+ err = dhcp_ipc_make_request(dhcpreq, &dhcpreply, timeout);
+ if (err != 0) {
+ free(dhcpreq);
+ lxi_warn("Failed to start DHCP on %s: %s\n", iface,
+ dhcp_ipc_strerror(err));
+ return (-1);
+ }
+ err = dhcpreply->return_code;
+ if (err != 0) {
+ lxi_warn("Failed to start DHCP on %s: %s\n", iface,
+ dhcp_ipc_strerror(err));
+ goto done;
+ }
+
+ *first_ipv4_configured = B_TRUE;
+
+done:
+ free(dhcpreq);
+ free(dhcpreply);
+ return (err);
+}
+
+/*
+ * Initialize an IPv6 link-local address on a given interface
+ */
+static int
+lxi_iface_ipv6_link_local(const char *iface)
+{
+ struct lifreq lifr;
+ int s;
+
+ s = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (s == -1) {
+ lxi_warn("socket error %d: bringing up %s: %s",
+ errno, iface, strerror(errno));
+ }
+
+ (void) strncpy(lifr.lifr_name, iface, sizeof (lifr.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+ lxi_warn("SIOCGLIFFLAGS error %d: bringing up %s: %s",
+ errno, iface, strerror(errno));
+ return (-1);
+ }
+
+ lifr.lifr_flags |= IFF_UP;
+ if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+ lxi_warn("SIOCSLIFFLAGS error %d: bringing up %s: %s",
+ errno, iface, strerror(errno));
+ return (-1);
+ }
+
+ (void) close(s);
+ return (0);
+}
+
+static int lxi_route_send_msg(struct rt_msghdr *rtm)
+{
+ int sockfd, len;
+
+ if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+ lxi_warn("socket(PF_ROUTE): %s\n", strerror(errno));
+ return (-1);
+ }
+
+ if ((len = write(sockfd, (const void *)rtm, rtm->rtm_msglen)) < 0) {
+ lxi_warn("could not write rtmsg: %s", strerror(errno));
+ close(sockfd);
+ return (-1);
+ } else if (len < rtm->rtm_msglen) {
+ lxi_warn("write() rtmsg incomplete");
+ close(sockfd);
+ return (-1);
+ }
+
+ close(sockfd);
+ return (0);
+}
+
+static int
+lxi_iface_gateway(const char *iface, const char *dst, int dstpfx,
+ const char *gwaddr, boolean_t llroute)
+{
+ int idx;
+ char rtbuf[RTMBUFSZ];
+ struct rt_msghdr *rtm = (struct rt_msghdr *)rtbuf;
+ struct sockaddr_in *dst_sin = (struct sockaddr_in *)
+ (rtbuf + sizeof (struct rt_msghdr));
+ struct sockaddr_in *gw_sin = (struct sockaddr_in *)(dst_sin + 1);
+ struct sockaddr_in *netmask_sin = (struct sockaddr_in *)(gw_sin + 1);
+
+ (void) bzero(rtm, RTMBUFSZ);
+ rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+
+ if (!llroute)
+ rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY;
+
+ rtm->rtm_msglen = sizeof (rtbuf);
+ rtm->rtm_pid = getpid();
+ rtm->rtm_type = RTM_ADD;
+ rtm->rtm_version = RTM_VERSION;
+
+
+ /*
+ * The destination and netmask components have already been zeroed,
+ * which represents the default gateway. If we were passed a more
+ * specific destination network, use that instead.
+ */
+
+ if (dstpfx == -1) {
+ /*
+ * no prefix was specified; assume a prefix length of 32,
+ * which seems in line with the behavior of vmadm.
+ */
+ dstpfx = 32;
+ }
+
+ dst_sin->sin_family = AF_INET;
+ netmask_sin->sin_family = AF_INET;
+ if (dst != NULL) {
+ struct sockaddr *mask = (struct sockaddr *)netmask_sin;
+
+ if ((inet_pton(AF_INET, dst, &(dst_sin->sin_addr))) != 1 ||
+ plen2mask(dstpfx, AF_INET, mask) != 0) {
+ lxi_warn("bad destination network %s/%d: %s", dst,
+ dstpfx, strerror(errno));
+ return (-1);
+ }
+ }
+
+ if ((inet_pton(AF_INET, gwaddr, &(gw_sin->sin_addr))) != 1) {
+ lxi_warn("bad gateway %s: %s", gwaddr, strerror(errno));
+ return (-1);
+ }
+
+ if (iface != NULL) {
+ if ((idx = if_nametoindex(iface)) == 0) {
+ lxi_warn("unable to get interface index for %s: %s\n",
+ iface, strerror(errno));
+ return (-1);
+ }
+ rtm->rtm_index = idx;
+ }
+
+ return (lxi_route_send_msg(rtm));
+}
+
+static void
+lxi_net_loopback()
+{
+ const char *iface = "lo0";
+ boolean_t first_ipv4_configured = B_FALSE;
+
+ lxi_net_plumb(iface);
+ (void) lxi_iface_ip(iface, "127.0.0.1/8", &first_ipv4_configured);
+ (void) lxi_iface_ipv6_link_local(iface);
+}
+
+
+/*
+ * This function is used when the "ips" property doesn't exist in a zone's
+ * configuration. It may be an older configuration, so we should search for
+ * "ip" and "netmask" and convert them into the new format.
+ */
+static int
+lxi_get_old_ip(struct zone_res_attrtab *attrs, const char **ipaddrs,
+ char *cidraddr, int len)
+{
+
+ const char *netmask;
+ int prefixlen;
+ struct sockaddr_in mask_sin;
+
+ lxi_warn("Could not find \"ips\" property for zone. Looking "
+ "for older \"ip\" and \"netmask\" properties, instead.");
+
+ if (zone_find_attr(attrs, "ip", ipaddrs) != 0) {
+ return (-1);
+ }
+
+ if (strcmp(*ipaddrs, "dhcp") == 0) {
+ return (0);
+ }
+
+ if (zone_find_attr(attrs, "netmask", &netmask) != 0) {
+ lxi_err("could not find netmask for interface");
+ /* NOTREACHED */
+ }
+
+ /* Convert the netmask to a number */
+ mask_sin.sin_family = AF_INET;
+ if (inet_pton(AF_INET, netmask, &mask_sin.sin_addr) != 1) {
+ lxi_err("invalid netmask address: %s\n",
+ strerror(errno));
+ /* NOTREACHED */
+ }
+ prefixlen = mask2plen((struct sockaddr *)&mask_sin);
+
+ /*
+ * Write out the IP address in the new format and use
+ * that instead
+ */
+ (void) snprintf(cidraddr, len, "%s/%d", *ipaddrs, prefixlen);
+
+ *ipaddrs = cidraddr;
+ return (0);
+}
+
+static void
+lxi_net_setup(zone_dochandle_t handle)
+{
+ struct zone_nwiftab lookup;
+ boolean_t do_addrconf = B_FALSE;
+
+ if (zonecfg_setnwifent(handle) != Z_OK)
+ return;
+ while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
+ const char *iface = lookup.zone_nwif_physical;
+ struct zone_res_attrtab *attrs = lookup.zone_nwif_attrp;
+ const char *ipaddrs;
+ char ipaddrs_copy[MAXNAMELEN], cidraddr[BUFSIZ],
+ *ipaddr, *tmp, *lasts;
+ boolean_t first_ipv4_configured = B_FALSE;
+ boolean_t *ficp = &first_ipv4_configured;
+
+ lxi_net_plumb(iface);
+ if (zone_find_attr(attrs, "ips", &ipaddrs) != 0 &&
+ lxi_get_old_ip(attrs, &ipaddrs, cidraddr, BUFSIZ) != 0) {
+ lxi_warn("Could not find a valid network configuration "
+ "for the %s interface", iface);
+ continue;
+ }
+
+ if (lxi_iface_ipv6_link_local(iface) != 0) {
+ lxi_warn("unable to bring up link-local address on "
+ "interface %s", iface);
+ }
+
+ /*
+ * If we're going to be doing DHCP, we have to do it first since
+ * dhcpagent doesn't like to operate on non-zero logical
+ * interfaces.
+ */
+ if (strstr(ipaddrs, "dhcp") != NULL &&
+ lxi_iface_dhcp(iface, ficp) != 0) {
+ lxi_warn("Failed to start DHCP on %s\n", iface);
+ }
+
+ /*
+ * Copy the ipaddrs string, since strtok_r will write NUL
+ * characters into it.
+ */
+ (void) strlcpy(ipaddrs_copy, ipaddrs, MAXNAMELEN);
+ tmp = ipaddrs_copy;
+
+ /*
+ * Iterate over each IP and then set it up on the interface.
+ */
+ while ((ipaddr = strtok_r(tmp, ",", &lasts)) != NULL) {
+ tmp = NULL;
+ if (strcmp(ipaddr, "addrconf") == 0) {
+ do_addrconf = B_TRUE;
+ } else if (strcmp(ipaddr, "dhcp") == 0) {
+ continue;
+ } else if (lxi_iface_ip(iface, ipaddr, ficp) < 0) {
+ lxi_warn("Unable to add new IP address (%s) "
+ "to interface %s", ipaddr, iface);
+ }
+ }
+ }
+
+ if (do_addrconf) {
+ lxi_net_ndpd_start();
+ }
+
+ (void) zonecfg_endnwifent(handle);
+}
+
+static void
+lxi_net_setup_gateways(zone_dochandle_t handle)
+{
+ struct zone_nwiftab lookup;
+
+ if (zonecfg_setnwifent(handle) != Z_OK)
+ return;
+
+ while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
+ const char *iface = lookup.zone_nwif_physical;
+ struct zone_res_attrtab *attrs = lookup.zone_nwif_attrp;
+ const char *primary, *gateway;
+
+ if (zone_find_attr(attrs, "primary", &primary) == 0 &&
+ strcmp(primary, "true") == 0 &&
+ zone_find_attr(attrs, "gateway", &gateway) == 0) {
+ lxi_iface_gateway(iface, NULL, 0, gateway, B_FALSE);
+ }
+ }
+
+ (void) zonecfg_endnwifent(handle);
+}
+
+/*
+ * Parse a static route line string of the form:
+ *
+ * "10.77.77.2|10.1.1.0/24|false"
+ *
+ * i.e. gateway address, destination network, and whether this is
+ * a "link local" route or a next hop route.
+ */
+static void
+lxi_parse_route_line(const char *line, custr_t *cu, char *gw, char *dst,
+ int *pfx, size_t gwlen, size_t dstlen)
+{
+ int i;
+ boolean_t havegw = B_FALSE, havedst = B_FALSE, nopfx = B_FALSE;
+
+ for (i = 0; line[i] != '\0'; i++) {
+ if (!havegw) {
+ if (line[i] == '|') {
+ if (strlcpy(gw, custr_cstr(cu), gwlen)
+ >= gwlen) {
+ lxi_err("strlcpy failure");
+ } else {
+ havegw = B_TRUE;
+ }
+ custr_reset(cu);
+ } else {
+ if (custr_appendc(cu, line[i]) != 0) {
+ lxi_err("custr_appendc failure");
+ }
+ }
+ continue;
+ }
+
+ if (!havedst) {
+ if (line[i] == '/' || line[i] == '|') {
+ if ((strlcpy(dst, custr_cstr(cu), dstlen))
+ >= dstlen) {
+ lxi_err("strlcpy failure");
+ } else {
+ havedst = B_TRUE;
+ if (line[i] == '|') {
+ nopfx = B_TRUE;
+ }
+ }
+ custr_reset(cu);
+ } else {
+ if (custr_appendc(cu, line[i]) != 0) {
+ lxi_err("custr_appendc failure");
+ }
+ }
+ continue;
+ }
+
+ if (*pfx == -1 && !nopfx) {
+ if (line[i] == '|') {
+ *pfx = atoi(custr_cstr(cu));
+ custr_reset(cu);
+ } else {
+ if (custr_appendc(cu, line[i]) != 0) {
+ lxi_err("custr_appendc failure");
+ }
+ }
+ continue;
+ }
+
+ if (custr_appendc(cu, line[i]) != 0) {
+ lxi_err("custr_appendc failure");
+ }
+ }
+}
+
+static void
+lxi_net_process_route_line(const char *line, boolean_t llonly)
+{
+ custr_t *cu = NULL;
+ int pfx = -1;
+ char gw[INET6_ADDRSTRLEN];
+ char dst[INET6_ADDRSTRLEN];
+
+ if (custr_alloc(&cu) != 0) {
+ lxi_err("custr_alloc failure");
+ }
+
+ lxi_parse_route_line(line, cu, gw, dst, &pfx, sizeof (gw),
+ sizeof (dst));
+
+ if (llonly && strcmp(custr_cstr(cu), "true") == 0) {
+ if (lxi_iface_gateway(NULL, dst, pfx, gw, B_TRUE) != 0) {
+ lxi_err("failed to add link-local route: %s/%d -> %s",
+ dst, pfx, gw);
+ }
+ } else if (!llonly && strcmp(custr_cstr(cu), "false") == 0) {
+ if (lxi_iface_gateway(NULL, dst, pfx, gw, B_FALSE) != 0) {
+ lxi_err("failed to add next-hop route: %s/%d -> %s",
+ dst, pfx, gw);
+ }
+ } else if (strcmp(custr_cstr(cu), "true") != 0 &&
+ strcmp(custr_cstr(cu), "false") != 0) {
+ /*
+ * try to be helpful when we run into something we don't expect.
+ */
+ lxi_warn("skipping unknown static route defined in line %s, "
+ " parsed link-local flag=%s", line, custr_cstr(cu));
+ }
+
+ custr_free(cu);
+}
+
+static void
+lxi_net_static_route(const char *line)
+{
+ lxi_net_process_route_line(line, B_FALSE);
+}
+
+static void
+lxi_net_linklocal_route(const char *line)
+{
+ lxi_net_process_route_line(line, B_TRUE);
+}
+
+static void
+lxi_run_routeinfo(void * callback)
+{
+ const char *cmd = "/native/usr/lib/brand/lx/routeinfo";
+ char *const argv[] = { "routeinfo", NULL };
+ char *const envp[] = { NULL };
+ int code;
+ struct stat st;
+ char errbuf[512];
+
+ if (stat(cmd, &st) != 0 || !S_ISREG(st.st_mode)) {
+ /*
+ * This binary is (potentially) shipped from another
+ * consolidation. If it does not exist, then the platform does
+ * not currently support link-local or static routes for
+ * LX-branded zones.
+ */
+ return;
+ }
+
+ /*
+ * Run the command, firing the callback for each line that it
+ * outputs. When this function returns, static route processing
+ * is complete.
+ */
+ if (run_command(cmd, argv, envp, errbuf, sizeof (errbuf),
+ callback, &code) != 0 || code != 0) {
+ lxi_err("failed to run \"%s\": %s", cmd, errbuf);
+ }
+}
+
+static void
+lxi_net_linklocal_routes(void)
+{
+ lxi_run_routeinfo(lxi_net_linklocal_route);
+}
+
+
+static void
+lxi_net_static_routes(void)
+{
+ lxi_run_routeinfo(lxi_net_static_route);
+}
+
+static void
+lxi_config_close(zone_dochandle_t handle)
+{
+ zonecfg_fini_handle(handle);
+}
+
+static void
+lxi_hook_postnet()
+{
+ char cmd[MAXPATHLEN];
+ const char *zroot = zone_get_nroot();
+ pid_t pid;
+ int status;
+
+ (void) snprintf(cmd, sizeof (cmd), "%s%s", zroot, HOOK_POSTNET_PATH);
+ if (access(cmd, X_OK) != 0) {
+ /* If no suitable script is present, soldier on. */
+ return;
+ }
+
+ if ((pid = fork()) < 0) {
+ lxi_err("fork() failed: %s", strerror(errno));
+ }
+ if (pid == 0) {
+ char *const argv[] = { cmd, NULL };
+ char *const envp[] = { NULL };
+
+ /* wire up stderr first, in case the hook wishes to use it */
+ if (dup2(1, 2) < 0) {
+ lxi_err("dup2() failed: %s", cmd, strerror(errno));
+ }
+
+ /* child executes the hook */
+ execve(cmd, argv, envp);
+
+ /*
+ * Since this is running as root, access(2) is less strict than
+ * necessary to ensure a successful exec. If the permissions
+ * on the hook are busted, ignore the failure and move on.
+ */
+ if (errno == EACCES) {
+ exit(0);
+ }
+
+ lxi_err("execve(%s) failed: %s", cmd, strerror(errno));
+ /* NOTREACHED */
+ }
+
+ /* Parent waits for the hook to complete */
+ while (wait(&status) != pid) {
+ /* EMPTY */;
+ }
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status) != 0) {
+ lxi_err("%s[%d] exited: %d", cmd, (int)pid,
+ WEXITSTATUS(status));
+ }
+ } else if (WIFSIGNALED(status)) {
+ lxi_err("%s[%d] died on signal: %d", cmd, (int)pid,
+ WTERMSIG(status));
+ } else {
+ lxi_err("%s[%d] failed in unknown way", cmd, (int)pid);
+ }
+}
+
+static void
+lxi_init_exec(char **argv)
+{
+ const char *cmd = "/sbin/init";
+ char *const envp[] = { "container=zone", NULL };
+ int e;
+
+ argv[0] = "init";
+
+ /*
+ * systemd uses the 'container' env var to determine it is running
+ * inside a container. It only supports a few well-known types and
+ * treats anything else as 'other' but this is enough to make it
+ * behave better inside a zone. See 'detect_container' in systemd.
+ */
+ execve(cmd, argv, envp);
+ e = errno;
+
+ /*
+ * Because stdout was closed prior to exec, it must be opened again in
+ * the face of failure to log the error.
+ */
+ lxi_log_open();
+ lxi_err("execve(%s) failed: %s", cmd, strerror(e));
+}
+
+/*ARGSUSED*/
+int
+main(int argc, char *argv[])
+{
+ zone_dochandle_t handle;
+
+ lxi_log_open();
+
+ lxi_net_ipmgmtd_start();
+ lxi_net_ipadm_open();
+
+ handle = lxi_config_open();
+ lxi_net_loopback();
+ lxi_net_setup(handle);
+
+ lxi_net_linklocal_routes();
+ lxi_net_setup_gateways(handle);
+
+ lxi_config_close(handle);
+
+ lxi_net_static_routes();
+
+ lxi_net_ipadm_close();
+
+ lxi_hook_postnet();
+
+ lxi_log_close();
+
+ lxi_init_exec(argv);
+
+ /* NOTREACHED */
+ return (0);
+}
diff --git a/usr/src/lib/brand/lx/lx_init/pipe_stream.c b/usr/src/lib/brand/lx/lx_init/pipe_stream.c
new file mode 100644
index 0000000000..8f06a07906
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_init/pipe_stream.c
@@ -0,0 +1,326 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <port.h>
+#include <poll.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/debug.h>
+#include <sys/list.h>
+
+#include "pipe_stream.h"
+
+struct pipe_stream {
+ pipe_stream_loop_t *pis_loop;
+
+ boolean_t pis_finished;
+ boolean_t pis_associated;
+
+ void *pis_arg0;
+ void *pis_arg1;
+
+ int pis_fd_write;
+ int pis_fd_read;
+ list_node_t pis_linkage;
+};
+
+struct pipe_stream_loop {
+ int psl_port;
+
+ uint8_t *psl_buf;
+ size_t psl_buf_cap;
+ size_t psl_buf_occ;
+
+ list_t psl_pipes;
+
+ pipe_stream_data_cb *psl_cb_data;
+ pipe_stream_eof_cb *psl_cb_eof;
+ pipe_stream_error_cb *psl_cb_error;
+};
+
+
+int
+pipe_stream_loop_fini(pipe_stream_loop_t *psl)
+{
+ if (psl == NULL) {
+ return (0);
+ }
+
+ VERIFY0(close(psl->psl_port));
+
+ while (!list_is_empty(&psl->psl_pipes)) {
+ pipe_stream_fini(list_head(&psl->psl_pipes));
+ }
+
+ list_destroy(&psl->psl_pipes);
+ free(psl);
+
+ return (0);
+}
+
+int
+pipe_stream_loop_init(pipe_stream_loop_t **pslp, size_t bufsize,
+ pipe_stream_data_cb *data_cb, pipe_stream_eof_cb *eof_cb,
+ pipe_stream_error_cb *error_cb)
+{
+ pipe_stream_loop_t *psl;
+
+ if ((psl = calloc(1, sizeof (*psl))) == NULL) {
+ return (-1);
+ }
+
+ psl->psl_buf_cap = bufsize;
+ psl->psl_buf_occ = 0;
+ if ((psl->psl_buf = calloc(1, bufsize)) == NULL) {
+ free(psl);
+ return (-1);
+ }
+
+ if ((psl->psl_port = port_create()) == -1) {
+ free(psl->psl_buf);
+ free(psl);
+ return (-1);
+ }
+
+ psl->psl_cb_data = data_cb;
+ psl->psl_cb_eof = eof_cb;
+ psl->psl_cb_error = error_cb;
+
+ list_create(&psl->psl_pipes, sizeof (pipe_stream_t),
+ offsetof(pipe_stream_t, pis_linkage));
+
+ *pslp = psl;
+ return (0);
+}
+
+boolean_t
+pipe_stream_loop_should_run(pipe_stream_loop_t *psl)
+{
+ pipe_stream_t *pis;
+
+ for (pis = list_head(&psl->psl_pipes); pis != NULL;
+ pis = list_next(&psl->psl_pipes, pis)) {
+ if (!pis->pis_finished) {
+ return (B_TRUE);
+ }
+ }
+
+ return (B_FALSE);
+}
+
+int
+pipe_stream_loop_run(pipe_stream_loop_t *psl)
+{
+ pipe_stream_t *pis;
+ port_event_t pev;
+ ssize_t sz;
+
+ for (pis = list_head(&psl->psl_pipes); pis != NULL;
+ pis = list_next(&psl->psl_pipes, pis)) {
+ if (pis->pis_finished || pis->pis_associated) {
+ /*
+ * Skip streams that are already finished, as well as
+ * those that have already been associated with the
+ * port.
+ */
+ continue;
+ }
+
+ if (port_associate(psl->psl_port, PORT_SOURCE_FD,
+ pis->pis_fd_read, POLLIN, pis) != 0) {
+ return (-1);
+ }
+ }
+
+again:
+ if (port_get(psl->psl_port, &pev, NULL) != 0) {
+ switch (errno) {
+ case ETIME:
+ /*
+ * Timeout expired; return to caller.
+ */
+ return (0);
+
+ case EINTR:
+ /*
+ * Interrupted by signal. Try again.
+ */
+ goto again;
+
+ default:
+ return (-1);
+ }
+ }
+
+ VERIFY(pev.portev_source == PORT_SOURCE_FD);
+ pis = (pipe_stream_t *)pev.portev_user;
+ VERIFY((int)pev.portev_object == pis->pis_fd_read);
+ pis->pis_associated = B_FALSE;
+
+read_again:
+ if ((sz = read(pis->pis_fd_read, psl->psl_buf,
+ psl->psl_buf_cap)) == -1) {
+ if (errno == EINTR) {
+ goto read_again;
+ }
+
+ if (psl->psl_cb_error != NULL) {
+ psl->psl_cb_error(errno, pis->pis_arg0, pis->pis_arg1);
+ }
+
+ VERIFY0(close(pis->pis_fd_read));
+ pis->pis_fd_read = -1;
+ pis->pis_finished = B_TRUE;
+ }
+ psl->psl_buf_occ = sz;
+
+ if (sz == 0) {
+ /*
+ * Stream EOF.
+ */
+ pis->pis_finished = B_TRUE;
+ VERIFY0(close(pis->pis_fd_read));
+ pis->pis_fd_read = -1;
+ if (psl->psl_cb_eof != NULL) {
+ psl->psl_cb_eof(pis->pis_arg0, pis->pis_arg1);
+ }
+ return (0);
+ }
+
+ if (psl->psl_cb_data != NULL) {
+ int cbr = psl->psl_cb_data(psl->psl_buf, psl->psl_buf_occ,
+ pis->pis_arg0, pis->pis_arg1);
+
+ if (cbr != 0) {
+ /*
+ * Callback failure: close file descriptor.
+ */
+ pis->pis_finished = B_TRUE;
+ VERIFY0(close(pis->pis_fd_read));
+ pis->pis_fd_read = -1;
+ if (psl->psl_cb_eof != NULL) {
+ psl->psl_cb_eof(pis->pis_arg0, pis->pis_arg1);
+ }
+ }
+
+ return (0);
+ }
+
+ return (0);
+}
+
+int
+pipe_stream_init(pipe_stream_loop_t *psl, pipe_stream_t **pisp, void *arg0,
+ void *arg1)
+{
+ int e = 0;
+ pipe_stream_t *pis;
+ int fds[2] = { -1, -1 };
+
+ if ((pis = calloc(1, sizeof (*pis))) == NULL) {
+ return (-1);
+ }
+
+ if (pipe(fds) != 0) {
+ e = errno;
+ goto fail;
+ }
+
+ pis->pis_fd_read = fds[0];
+ pis->pis_fd_write = fds[1];
+
+ pis->pis_arg0 = arg0;
+ pis->pis_arg1 = arg1;
+
+ pis->pis_finished = B_FALSE;
+ pis->pis_associated = B_FALSE;
+
+ pis->pis_loop = psl;
+ list_insert_tail(&psl->psl_pipes, pis);
+
+ *pisp = pis;
+ return (0);
+
+fail:
+ if (fds[0] != -1) {
+ VERIFY0(close(fds[0]));
+ }
+ if (fds[1] != -1) {
+ VERIFY0(close(fds[1]));
+ }
+ free(pis);
+ errno = e;
+ return (-1);
+}
+
+int
+pipe_stream_fini(pipe_stream_t *pis)
+{
+ if (pis == NULL) {
+ return (0);
+ }
+
+ if (pis->pis_fd_read != -1) {
+ VERIFY0(close(pis->pis_fd_read));
+ }
+ if (pis->pis_fd_write != -1) {
+ VERIFY0(close(pis->pis_fd_write));
+ }
+
+ list_remove(&pis->pis_loop->psl_pipes, pis);
+
+ free(pis);
+ return (0);
+}
+
+/*
+ * Called in the parent, after forking, to close the "write" end of the pipe.
+ */
+void
+pipe_stream_parent_afterfork(pipe_stream_t *pis)
+{
+ if (pis->pis_fd_write != -1) {
+ (void) close(pis->pis_fd_write);
+ pis->pis_fd_write = -1;
+ }
+}
+
+/*
+ * Called in the child, after forking, to close the "read" end of the
+ * pipe, and to dup the file descriptor into the right place.
+ */
+int
+pipe_stream_child_afterfork(pipe_stream_t *pis, int dup_to)
+{
+ int e = 0;
+
+ if (dup_to != -1) {
+ if (dup2(pis->pis_fd_write, dup_to) == -1) {
+ e = errno;
+ }
+ VERIFY0(close(pis->pis_fd_write));
+ pis->pis_fd_write = dup_to;
+ }
+
+ (void) close(pis->pis_fd_read);
+ pis->pis_fd_read = -1;
+
+ errno = e;
+ return (e == 0 ? 0 : -1);
+}
diff --git a/usr/src/lib/brand/lx/lx_init/pipe_stream.h b/usr/src/lib/brand/lx/lx_init/pipe_stream.h
new file mode 100644
index 0000000000..140ef18f8c
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_init/pipe_stream.h
@@ -0,0 +1,48 @@
+/*
+ * 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 _PIPE_STREAM_H
+#define _PIPE_STREAM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int pipe_stream_data_cb(const uint8_t *, size_t, void *, void *);
+typedef void pipe_stream_eof_cb(void *, void *);
+typedef void pipe_stream_error_cb(int, void *, void *);
+
+typedef struct pipe_stream pipe_stream_t;
+typedef struct pipe_stream_loop pipe_stream_loop_t;
+
+extern int pipe_stream_loop_fini(pipe_stream_loop_t *);
+extern int pipe_stream_loop_init(pipe_stream_loop_t **, size_t,
+ pipe_stream_data_cb *, pipe_stream_eof_cb *, pipe_stream_error_cb *);
+
+extern int pipe_stream_init(pipe_stream_loop_t *, pipe_stream_t **, void *,
+ void *);
+extern int pipe_stream_fini(pipe_stream_t *);
+
+extern void pipe_stream_parent_afterfork(pipe_stream_t *);
+extern int pipe_stream_child_afterfork(pipe_stream_t *, int);
+
+extern boolean_t pipe_stream_loop_should_run(pipe_stream_loop_t *);
+extern int pipe_stream_loop_run(pipe_stream_loop_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PIPE_STREAM_H */
diff --git a/usr/src/lib/brand/lx/lx_init/run_command.c b/usr/src/lib/brand/lx/lx_init/run_command.c
new file mode 100644
index 0000000000..9130838625
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_init/run_command.c
@@ -0,0 +1,282 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <strings.h>
+#include <unistd.h>
+#include <wait.h>
+#include <sys/types.h>
+#include <sys/debug.h>
+#include <libcustr.h>
+
+#include "run_command.h"
+#include "pipe_stream.h"
+
+typedef struct cmd {
+ int cmd_pid;
+ int cmd_wstatus;
+ pipe_stream_t *cmd_pipe[2];
+ custr_t *cmd_err;
+ custr_t *cmd_out;
+ run_command_line_cb *cmd_func;
+ boolean_t cmd_cancel;
+} cmd_t;
+
+static int cb_data(const uint8_t *, size_t, void *, void *);
+static void cb_eof(void *, void *);
+static void cb_error(int, void *, void *);
+
+void
+post_error(cmd_t *cmd, const char *estr)
+{
+ if (cmd->cmd_cancel) {
+ return;
+ }
+ cmd->cmd_cancel = B_TRUE;
+
+ custr_reset(cmd->cmd_err);
+ (void) custr_append(cmd->cmd_err, estr);
+}
+
+int
+run_command(const char *path, char *const argv[], char *const envp[],
+ char *errbuf, size_t errlen, run_command_line_cb *func, int *status)
+{
+ pipe_stream_loop_t *psl = NULL;
+ int e = 0;
+ cmd_t cmd;
+ pid_t wpid;
+
+ bzero(&cmd, sizeof (cmd));
+
+ cmd.cmd_func = func;
+
+ /*
+ * Allocate string buffers for stdout line buffering and error
+ * messages:
+ */
+ if (custr_alloc_buf(&cmd.cmd_err, errbuf, errlen) != 0 ||
+ custr_alloc(&cmd.cmd_out) != 0) {
+ e = errno;
+ goto out;
+ }
+
+ /*
+ * Initialise pipe stream event loop:
+ */
+ if (pipe_stream_loop_init(&psl, 256, cb_data, cb_eof, cb_error) != 0) {
+ e = errno;
+ post_error(&cmd, "could not init pipe stream loop");
+ goto out;
+ }
+
+ /*
+ * Create pipe streams for stdout and stderr communication with
+ * child process:
+ */
+ if (pipe_stream_init(psl, &cmd.cmd_pipe[0], &cmd,
+ (void *)STDOUT_FILENO) != 0 ||
+ pipe_stream_init(psl, &cmd.cmd_pipe[1], &cmd,
+ (void *)STDERR_FILENO) != 0) {
+ e = errno;
+ post_error(&cmd, "could not init pipe streams");
+ goto out;
+ }
+
+ /*
+ * Fork a child process:
+ */
+ if ((cmd.cmd_pid = fork()) == -1) {
+ e = errno;
+ post_error(&cmd, "could not fork");
+ goto out;
+ }
+
+ if (cmd.cmd_pid == 0) {
+ /*
+ * This is the child process. Clean up file descriptors, and
+ * connect stdio to the pipes we allocated:
+ */
+ VERIFY0(close(STDIN_FILENO));
+ VERIFY0(pipe_stream_child_afterfork(cmd.cmd_pipe[0],
+ STDOUT_FILENO));
+ VERIFY0(pipe_stream_child_afterfork(cmd.cmd_pipe[1],
+ STDERR_FILENO));
+ closefrom(3);
+
+ execve(path, argv, envp);
+ err(127, "exec(%s) failed", path);
+ }
+
+ /*
+ * Back in the parent. Close the remote end of the stdio pipes:
+ */
+ pipe_stream_parent_afterfork(cmd.cmd_pipe[0]);
+ pipe_stream_parent_afterfork(cmd.cmd_pipe[1]);
+
+ /*
+ * Run the pipe event loop until all streams are completely
+ * consumed:
+ */
+ while (pipe_stream_loop_should_run(psl)) {
+ if (pipe_stream_loop_run(psl) != 0) {
+ e = errno;
+ post_error(&cmd, "pipe stream loop run failure");
+ goto out;
+ }
+ }
+
+ /*
+ * Collect exit status of child process:
+ */
+ while ((wpid = waitpid(cmd.cmd_pid, &cmd.cmd_wstatus, 0)) !=
+ cmd.cmd_pid) {
+ if (wpid == -1 && errno != EINTR) {
+ e = errno;
+ post_error(&cmd, "waitpid failure");
+ goto out;
+ }
+ }
+
+ /*
+ * If the child died on a signal, fail the whole operation:
+ */
+ if (WIFSIGNALED(cmd.cmd_wstatus)) {
+ e = ENXIO;
+ post_error(&cmd, "child process died on signal");
+ (void) custr_append_printf(cmd.cmd_err, " (pid %d signal %d)",
+ cmd.cmd_pid, WTERMSIG(cmd.cmd_wstatus));
+ goto out;
+ }
+
+ /*
+ * If the child did not appear to exit, fail the whole operation:
+ */
+ if (!WIFEXITED(cmd.cmd_wstatus)) {
+ e = ENXIO;
+ post_error(&cmd, "child process did not exit");
+ (void) custr_append_printf(cmd.cmd_err, " (pid %d status %x)",
+ cmd.cmd_pid, cmd.cmd_wstatus);
+ goto out;
+ }
+
+ /*
+ * Report exit status to the caller:
+ */
+ *status = WEXITSTATUS(cmd.cmd_wstatus);
+ e = 0;
+
+out:
+ VERIFY0(pipe_stream_loop_fini(psl));
+ /*
+ * Note that freeing the static error custr_t does not touch the
+ * underlying storage; we use this property to return the error
+ * message (if one exists) to the caller.
+ */
+ custr_free(cmd.cmd_err);
+ custr_free(cmd.cmd_out);
+ errno = e;
+ return (e == 0 ? 0 : -1);
+}
+
+static int
+cb_data(const uint8_t *buf, size_t sz, void *arg0, void *arg1)
+{
+ cmd_t *cmd = arg0;
+ int fd = (int)arg1;
+ unsigned int i;
+
+ if (cmd->cmd_cancel) {
+ return (-1);
+ }
+
+ switch (fd) {
+ case STDOUT_FILENO:
+ for (i = 0; i < sz; i++) {
+ if (buf[i] == '\0' || buf[i] == '\r') {
+ continue;
+ }
+
+ if (buf[i] == '\n') {
+ cmd->cmd_func(custr_cstr(cmd->cmd_out));
+ custr_reset(cmd->cmd_out);
+ continue;
+ }
+
+ if (custr_appendc(cmd->cmd_out, buf[i]) != 0) {
+ /*
+ * Failed to allocate memory; returning
+ * -1 here will abort the stream.
+ */
+ post_error(cmd, "custr_appendc failure");
+ return (-1);
+ }
+ }
+ break;
+
+ case STDERR_FILENO:
+ /*
+ * Collect as much stderr output as will fit in our static
+ * buffer.
+ */
+ for (i = 0; i < sz; i++) {
+ if (buf[i] == '\0') {
+ continue;
+ }
+
+ (void) custr_appendc(cmd->cmd_err, buf[i]);
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ return (0);
+}
+
+static void
+cb_eof(void *arg0, void *arg1)
+{
+ cmd_t *cmd = arg0;
+ int fd = (int)arg1;
+
+ if (cmd->cmd_cancel) {
+ return;
+ }
+
+ if (fd == STDOUT_FILENO && custr_len(cmd->cmd_out) > 0) {
+ cmd->cmd_func(custr_cstr(cmd->cmd_out));
+ custr_reset(cmd->cmd_out);
+ }
+}
+
+static void
+cb_error(int e, void *arg0, void *arg1)
+{
+ cmd_t *cmd = arg0;
+ int fd = (int)arg1;
+
+ if (cmd->cmd_cancel) {
+ return;
+ }
+
+ post_error(cmd, "stream read failure");
+ (void) custr_append_printf(cmd->cmd_err, " (pid %d fd %d): %s",
+ cmd->cmd_pid, fd, strerror(e));
+}
diff --git a/usr/src/lib/brand/lx/lx_init/run_command.h b/usr/src/lib/brand/lx/lx_init/run_command.h
new file mode 100644
index 0000000000..3484b265b6
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_init/run_command.h
@@ -0,0 +1,32 @@
+/*
+ * 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 _RUN_COMMAND_H
+#define _RUN_COMMAND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void run_command_line_cb(const char *);
+
+extern int run_command(const char *, char *const [], char *const [], char *,
+ size_t, run_command_line_cb *, int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RUN_COMMAND_H */
diff --git a/usr/src/lib/brand/lx/lx_lockd/Makefile b/usr/src/lib/brand/lx/lx_lockd/Makefile
new file mode 100644
index 0000000000..c1ff1b2d62
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_lockd/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 2019 Joyent, Inc.
+#
+
+PROG = lx_lockd
+
+PROG_OBJS = lockd.o nfs_tbind.o
+UTIL_OBJS = thrpool.o
+
+OBJS = $(PROG_OBJS) $(UTIL_OBJS)
+SRCS = $(PROG_OBJS:%.o=%.c)
+
+all: $(PROG)
+
+include ../Makefile.lx
+include $(SRC)/cmd/Makefile.cmd
+
+# override the install directory
+ROOTBIN = $(ROOTBRANDDIR)
+CLOBBERFILES = $(OBJS) $(ROOTPROG)
+
+CPPFLAGS += -I$(SRC)/cmd/fs.d/nfs/lib
+CSTD = $(CSTD_GNU99)
+LDLIBS += -lnsl -lsocket
+
+CERRWARN += -_gcc=-Wno-parentheses
+CERRWARN += -_gcc=-Wno-uninitialized
+CERRWARN += -_gcc=-Wno-unused-function
+
+# not linted
+SMATCH=off
+
+.KEEP_STATE:
+
+install: all $(ROOTPROG)
+
+clean:
+ $(RM) $(PROG) $(OBJS)
+
+lint:
+ $(LINT.c) -erroff=E_SEC_PRINTF_VAR_FMT lockd.c $(LDLIBS)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: %.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+%.o: $(SRC)/cmd/fs.d/nfs/lib/%.c
+ $(COMPILE.c) $<
+ $(POST_PROCESS_O)
+
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/lib/brand/lx/lx_lockd/lockd.c b/usr/src/lib/brand/lx/lx_lockd/lockd.c
new file mode 100644
index 0000000000..6a80b5355c
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_lockd/lockd.c
@@ -0,0 +1,568 @@
+/*
+ * 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) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Joyent, Inc.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+/*
+ * lx-brand NFS lockd (NLM) server.
+ *
+ * This code is derived from the native lockd. The original history starts
+ * from:
+ * copied from usr/src/cmd/fs.d/nfs/nfsd/nfsd.c and then s:NFS:NLM: applied
+ *
+ * On Linux 'lockd' is implemented entirely inside the kernel, whereas our
+ * native lockd support is a combination of user-level and kernel code.
+ * Unfortunately, the native lockd is unable to run in lx for several reasons:
+ * - tightly bound to SMF
+ * - interacts with various native libnsl configuration files
+ * - expects to register with a native rpcbind using /dev/ticlts
+ * Thus, this code is derived from the native lockd, but modified to address
+ * these issues and run inside an lx-branded zone. Because the Linux lockd
+ * lives entirely in the kernel, our lockd must be started automatically if
+ * it is needed. This is done by the NFS mount code when it determines that
+ * a lockd is not already running. The kernel code ensures that there is only a
+ * single instance of the lockd running, in the case that there is a race with
+ * two NFS mounts occuring in parallel.
+ *
+ * lockd is required for both NFSv3 and v4 locking. Although v4 locking is
+ * part of the v4 protocol, the kernel support to allow NFS locking is enabled
+ * by the lockd when it starts. For v3, there must be a lockd registered with
+ * rpcbind or the server side will fail the lock. This is because the server
+ * side expects to make callbacks to the client. We must successfully register
+ * with the Linux rpcbind, otherwise the NFS syscall to enable the kernel side
+ * of locking will fail. For the v3 case, the user-level Linux mount helper cmd
+ * already checks for the presence of rpcbind. It fails if rpcbind is not
+ * running and the mount does not include the "nolock" option. For v4 the use
+ * of rpcbind appears unnecessary, since locking is built-in to the protocol,
+ * but it still required by our kernel NFS locking code.
+ *
+ * As with the native lockd, the kernel locking code makes upcalls to our lockd
+ * over /dev/ticotsord, so that device must be present inside an lx zone.
+ *
+ * Because this process tries to mimic the Linux kernel lockd, there is no
+ * stdin/out/err and we block all signals, unless we're running in debug mode.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <tiuser.h>
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <thread.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <nfs/nfs.h>
+#include <nfs/nfssys.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <netconfig.h>
+#include <netdir.h>
+#include <string.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <sys/tihdr.h>
+#include <poll.h>
+#include <priv_utils.h>
+#include <sys/tiuser.h>
+#include <netinet/tcp.h>
+#include <deflt.h>
+#include <rpcsvc/nlm_prot.h>
+#include <ctype.h>
+#include <strings.h>
+#include <sys/varargs.h>
+#include <rpcsvc/nlm_prot.h>
+#include <rpc/pmap_prot.h>
+#include "nfs_tbind.h"
+#include "thrpool.h"
+
+/* Option defaults. See nfssys.h */
+struct lm_svc_args lmargs = {
+ .version = LM_SVC_CUR_VERS,
+ /* fd, n_fmly, n_proto, n_rdev (below) */
+ .n_v4_only = 0,
+ .timout = 5 * 60,
+ .grace = 90, /* How long to wait for clients to re-establish locks. */
+ .retransmittimeout = 5
+};
+int max_servers = 256;
+
+#define RET_OK 0 /* return code for no error */
+#define RET_ERR 33 /* return code for error(s) */
+
+#define SYSLOG_BLEN 256
+
+int nlmsvc(int fd, struct netbuf addrmask, struct netconfig *nconf);
+
+static int nlmsvcpool(int max_servers);
+static void usage(void);
+
+static struct timeval tottimeout = { 60, 0 };
+static struct timeval rpcbtime = { 15, 0 };
+static const char nullstring[] = "\000";
+
+boolean_t have_rpcbind = B_FALSE;
+
+extern int _nfssys(int, void *);
+extern void nlm_do_one(char *, int (*)(int, struct netbuf, struct netconfig *));
+extern int nlm_bind_to_provider(char *, struct netbuf **, struct netconfig **);
+
+/*
+ * We want to bind to these TLI providers, and in this order,
+ * because the kernel NLM needs the loopback first for its
+ * initialization. (It uses it to talk to statd.)
+ */
+static NETSELDECL(defaultproviders)[] = {
+ "/dev/ticotsord",
+ "/dev/tcp",
+ "/dev/udp",
+ "/dev/tcp6",
+ "/dev/udp6",
+ NULL
+};
+
+/*
+ * The following are all globals used by routines in nfs_tbind.c.
+ */
+size_t end_listen_fds; /* used by conn_close_oldest() */
+size_t num_fds = 0; /* used by multiple routines */
+int listen_backlog = 32; /* used by bind_to_{provider,proto}() */
+ /* used by cots_listen_event() */
+int max_conns_allowed = -1; /* used by cots_listen_event() */
+int (*Mysvc)(int, struct netbuf, struct netconfig *) = nlmsvc;
+
+boolean_t debug = B_FALSE;
+
+#define LX_PMAP_VERS 4
+
+/*
+ * Set a mapping between program, version and address.
+ * Calls the lx-zone's rpcbind service to do the mapping.
+ */
+boolean_t
+lx_rpcb_set(const rpcvers_t version, const struct netconfig *nconf,
+ const struct netbuf *address)
+{
+ CLIENT *client;
+ bool_t rslt = FALSE;
+ RPCB parms;
+ char uidbuf[32];
+
+ client = clnt_create_timed("localhost", PMAPPROG, LX_PMAP_VERS,
+ "datagram_v", &rpcbtime);
+ if (client == NULL)
+ return (B_FALSE);
+
+ parms.r_addr = taddr2uaddr((struct netconfig *)nconf,
+ (struct netbuf *)address); /* convert to universal */
+ if (!parms.r_addr) {
+ rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
+ return (B_FALSE);
+ }
+ parms.r_prog = NLM_PROG;
+ parms.r_vers = version;
+ parms.r_netid = nconf->nc_netid;
+ /*
+ * Though uid is not being used directly, we still send it for
+ * completeness. For non-unix platforms, perhaps some other
+ * string or an empty string can be sent.
+ */
+ (void) sprintf(uidbuf, "%d", (int)geteuid());
+ parms.r_owner = uidbuf;
+
+ CLNT_CALL(client, RPCBPROC_SET, (xdrproc_t)xdr_rpcb, (char *)&parms,
+ (xdrproc_t)xdr_bool, (char *)&rslt, tottimeout);
+
+ CLNT_DESTROY(client);
+ free(parms.r_addr);
+ return (B_TRUE);
+}
+
+/*
+ * Remove the mapping between program, version and netbuf address.
+ * Calls the rpcbind service to do the un-mapping.
+ */
+void
+lx_rpcb_unset(const rpcvers_t version, char *nc_netid)
+{
+ CLIENT *client;
+ bool_t rslt;
+ RPCB parms;
+ char uidbuf[32];
+
+ if (!have_rpcbind)
+ return;
+
+ client = clnt_create_timed("localhost", PMAPPROG, LX_PMAP_VERS,
+ "datagram_v", &rpcbtime);
+ if (client == NULL)
+ return;
+
+ parms.r_prog = NLM_PROG;
+ parms.r_vers = version;
+ parms.r_netid = nc_netid;
+ parms.r_addr = (char *)&nullstring[0];
+ (void) sprintf(uidbuf, "%d", (int)geteuid());
+ parms.r_owner = uidbuf;
+
+ CLNT_CALL(client, RPCBPROC_UNSET, (xdrproc_t)xdr_rpcb, (char *)&parms,
+ (xdrproc_t)xdr_bool, (char *)&rslt, tottimeout);
+
+ CLNT_DESTROY(client);
+}
+
+static void
+lx_nlm_unreg(char *provider)
+{
+ struct netconfig *retnconf;
+ int vers;
+
+ if (nlm_bind_to_provider(provider, NULL, &retnconf) == -1)
+ return;
+
+ /* Unregister all versions of the program. */
+ for (vers = NLM_VERS; vers <= NLM4_VERS; vers++) {
+ lx_rpcb_unset(vers, retnconf->nc_netid);
+ }
+}
+
+void
+lx_syslog(char *fmt, ...)
+{
+ int fd, l;
+ struct sockaddr_un snd_addr;
+ char buf[SYSLOG_BLEN], fb[SYSLOG_BLEN], *bp, *fp, *ep;
+ va_list ap;
+
+ /* First we replace %m in fmt string with error string into fb. */
+ ep = fb + sizeof (fb);
+ fb[SYSLOG_BLEN - 1] = '\0';
+ for (bp = fb, fp = fmt; bp < ep && (*bp = *fp) != '\0'; bp++, fp++) {
+ if (*fp == '%' && *(fp + 1) == 'm') {
+ (void) strlcpy(bp, strerror(errno), ep - bp);
+ bp += strlen(bp);
+ fp += 2;
+ }
+ }
+
+ va_start(ap, fmt);
+ (void) snprintf(buf, sizeof (buf), " rpc.lockd[%d]: ", getpid());
+ l = strlen(buf);
+ bp = &buf[l];
+ (void) vsnprintf(bp, sizeof (buf) - l, fb, ap);
+ va_end(ap);
+
+ if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
+ return;
+
+ bzero(&snd_addr, sizeof (snd_addr));
+ strcpy(snd_addr.sun_path, "/dev/log");
+ snd_addr.sun_family = AF_LOCAL;
+ l = strlen(snd_addr.sun_path) + sizeof (snd_addr.sun_family);
+
+ if (connect(fd, (struct sockaddr *)&snd_addr, l) == 0) {
+ l = strlen(buf);
+ (void) send(fd, buf, l, 0);
+ }
+
+ close(fd);
+}
+
+/* When debugging, ensure cleanup */
+static void
+sigint_handler(void)
+{
+ NETSELPDECL(providerp);
+
+ lx_syslog("Stopping");
+
+ /* unregister from rpcbind */
+ for (providerp = defaultproviders; *providerp != NULL; providerp++) {
+ char *provider = *providerp;
+ lx_nlm_unreg(provider);
+ }
+ (void) _nfssys(KILL_LOCKMGR, NULL);
+
+ exit(0);
+}
+
+int
+main(int ac, char *av[])
+{
+ NETSELPDECL(providerp);
+ int i, c, val;
+
+ if (geteuid() != 0) {
+ exit(1);
+ }
+
+ /* Initializations that require more privileges than we need to run. */
+ if (__init_daemon_priv(PU_RESETGROUPS | PU_CLEARLIMITSET, 1, 1,
+ PRIV_SYS_NFS, NULL) == -1) {
+ exit(1);
+ }
+
+ (void) enable_extended_FILE_stdio(-1, -1);
+
+ while ((c = getopt(ac, av, "c:dg:l:t:")) != EOF)
+ switch (c) {
+ case 'c': /* max_connections */
+ if ((val = atoi(optarg)) <= 0)
+ goto badval;
+ max_conns_allowed = val;
+ break;
+
+ case 'd': /* debug */
+ debug = B_TRUE;
+ break;
+
+ case 'g': /* grace_period */
+ if ((val = atoi(optarg)) <= 0)
+ goto badval;
+ lmargs.grace = val;
+ break;
+
+ case 'l': /* listen_backlog */
+ if ((val = atoi(optarg)) <= 0)
+ goto badval;
+ listen_backlog = val;
+ break;
+
+ case 't': /* retrans_timeout */
+ if ((val = atoi(optarg)) <= 0)
+ goto badval;
+ lmargs.retransmittimeout = val;
+ break;
+
+ badval:
+ if (debug) {
+ fprintf(stderr, "Invalid -%c option value", c);
+ }
+ /* FALLTHROUGH */
+ default:
+ if (debug) {
+ usage();
+ }
+ exit(1);
+ }
+
+ /* If there is one more argument, it is the number of servers. */
+ if (optind < ac) {
+ val = atoi(av[optind]);
+ if (val <= 0) {
+ if (debug) {
+ fprintf(stderr, "Invalid max_servers argument");
+ usage();
+ }
+ exit(1);
+ }
+ max_servers = val;
+ optind++;
+ }
+ /* If there are two or more arguments, then this is a usage error. */
+ if (optind != ac) {
+ if (debug) {
+ usage();
+ }
+ exit(1);
+ }
+
+ if (debug) {
+ printf("lx_lockd: debug=%d, conn_idle_timout=%d, "
+ "grace_period=%d, listen_backlog=%d, "
+ "max_connections=%d, max_servers=%d, "
+ "retrans_timeout=%d\n",
+ debug, lmargs.timout, lmargs.grace, listen_backlog,
+ max_conns_allowed, max_servers, lmargs.retransmittimeout);
+ }
+
+ /* Set current dir to server root */
+ if (chdir("/") < 0) {
+ lx_syslog("chdir: %m");
+ exit(1);
+ }
+
+ if (!debug) {
+ /* Block all signals if not debugging. */
+ sigset_t set;
+
+ (void) sigfillset(&set);
+ (void) sigprocmask(SIG_BLOCK, &set, NULL);
+ (void) setsid();
+ } else {
+ struct sigaction act;
+
+ act.sa_handler = sigint_handler;
+ act.sa_flags = 0;
+ (void) sigaction(SIGINT, &act, NULL);
+ }
+
+ lx_syslog("Starting");
+
+ /* Unregister any previous versions. */
+ for (i = NLM_VERS; i < NLM4_VERS; i++) {
+ svc_unreg(NLM_PROG, i);
+ }
+
+ /* Set up kernel RPC thread pool for the NLM server. */
+ if (nlmsvcpool(max_servers)) {
+ lx_syslog("Can't set up kernel NLM service: %m. Exiting");
+ exit(1);
+ }
+
+ /* Set up blocked thread to do LWP creation on behalf of the kernel. */
+ if (svcwait(NLM_SVCPOOL_ID)) {
+ lx_syslog("Can't set up NLM pool creator: %m. Exiting");
+ exit(1);
+ }
+
+ for (providerp = defaultproviders; *providerp != NULL; providerp++) {
+ char *provider = *providerp;
+ nlm_do_one(provider, nlmsvc);
+ }
+
+ if (num_fds == 0) {
+ lx_syslog("Could not start NLM service for any protocol. "
+ "Exiting");
+ exit(1);
+ }
+
+ end_listen_fds = num_fds;
+
+ /*
+ * lockd is up and running as far as we are concerned.
+ *
+ * Get rid of unneeded privileges.
+ */
+ __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
+ PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL);
+
+ /* Poll for non-data control events on the transport descriptors. */
+ poll_for_action();
+
+ /* If we get here, something failed in poll_for_action(). */
+ return (1);
+}
+
+static int
+nlmsvcpool(int maxservers)
+{
+ struct svcpool_args npa;
+
+ npa.id = NLM_SVCPOOL_ID;
+ npa.maxthreads = maxservers;
+ npa.redline = 0;
+ npa.qsize = 0;
+ npa.timeout = 0;
+ npa.stksize = 0;
+ npa.max_same_xprt = 0;
+ return (_nfssys(SVCPOOL_CREATE, &npa));
+}
+
+static int
+ncfmly_to_lmfmly(const char *ncfmly)
+{
+ if (0 == strcmp(ncfmly, NC_INET))
+ return (LM_INET);
+ if (0 == strcmp(ncfmly, NC_INET6))
+ return (LM_INET6);
+ if (0 == strcmp(ncfmly, NC_LOOPBACK))
+ return (LM_LOOPBACK);
+ return (-1);
+}
+
+static int
+nctype_to_lmprot(uint_t semantics)
+{
+ switch (semantics) {
+ case NC_TPI_CLTS:
+ return (LM_UDP);
+ case NC_TPI_COTS_ORD:
+ return (LM_TCP);
+ }
+ return (-1);
+}
+
+static dev_t
+ncdev_to_rdev(const char *ncdev)
+{
+ struct stat st;
+
+ if (stat(ncdev, &st) < 0)
+ return (NODEV);
+ return (st.st_rdev);
+}
+
+/*
+ * Establish NLM service thread.
+ */
+int
+nlmsvc(int fd, struct netbuf addrmask, struct netconfig *nconf)
+{
+ struct lm_svc_args lma;
+
+ lma = lmargs; /* init by struct copy */
+
+ /*
+ * The kernel code needs to reconstruct a complete
+ * knetconfig from n_fmly, n_proto. We use these
+ * two fields to convey the family and semantics.
+ */
+ lma.fd = fd;
+ lma.n_fmly = ncfmly_to_lmfmly(nconf->nc_protofmly);
+ lma.n_proto = nctype_to_lmprot(nconf->nc_semantics);
+ lma.n_rdev = ncdev_to_rdev(nconf->nc_device);
+
+ if (!have_rpcbind) {
+ /*
+ * Inform the kernel NLM code to run without rpcbind and
+ * rpc.statd.
+ */
+ lma.n_v4_only = -1;
+ }
+
+ return (_nfssys(LM_SVC, &lma));
+}
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, "usage: lx_lockd [options] [max_servers]\n");
+ (void) fprintf(stderr, "\t-c max_connections\n");
+ (void) fprintf(stderr, "\t-d enable debugging\n");
+ (void) fprintf(stderr, "\t-g grace_period\n");
+ (void) fprintf(stderr, "\t-l listen_backlog\n");
+ (void) fprintf(stderr, "\t-t retransmit_timeout\n");
+}
diff --git a/usr/src/lib/brand/lx/lx_lockd/nfs_tbind.c b/usr/src/lib/brand/lx/lx_lockd/nfs_tbind.c
new file mode 100644
index 0000000000..1f8f50e23c
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_lockd/nfs_tbind.c
@@ -0,0 +1,1832 @@
+/*
+ * 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) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Gary Mills
+ * Copyright 2017 Joyent, Inc.
+ */
+
+/*
+ * Derived from usr/src/cmd/fs.d/nfs/lib/nfs_tbind.c, but modified to work
+ * within an lx-branded zone and to only support the lx lockd. Changes from the
+ * original have been minimized, thus this file is not cstyle or lint clean.
+ */
+
+#include <tiuser.h>
+#include <fcntl.h>
+#include <netconfig.h>
+#include <stropts.h>
+#include <errno.h>
+#include <rpc/rpc.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <signal.h>
+#include <netdir.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/tcp.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include "nfs_tbind.h"
+#include <nfs/nfs.h>
+#include <nfs/nfs_acl.h>
+#include <nfs/nfssys.h>
+#include <nfs/nfs4.h>
+#include <zone.h>
+#include <sys/socket.h>
+#include <tsol/label.h>
+#include <sys/varargs.h>
+#include <rpcsvc/nlm_prot.h>
+
+/*
+ * Determine valid semantics for most applications.
+ */
+#define OK_TPI_TYPE(_nconf) \
+ (_nconf->nc_semantics == NC_TPI_CLTS || \
+ _nconf->nc_semantics == NC_TPI_COTS || \
+ _nconf->nc_semantics == NC_TPI_COTS_ORD)
+
+#define BE32_TO_U32(a) \
+ ((((ulong_t)((uchar_t *)a)[0] & 0xFF) << (ulong_t)24) | \
+ (((ulong_t)((uchar_t *)a)[1] & 0xFF) << (ulong_t)16) | \
+ (((ulong_t)((uchar_t *)a)[2] & 0xFF) << (ulong_t)8) | \
+ ((ulong_t)((uchar_t *)a)[3] & 0xFF))
+
+/*
+ * Number of elements to add to the poll array on each allocation.
+ */
+#define POLL_ARRAY_INC_SIZE 64
+
+/*
+ * Number of file descriptors by which the process soft limit may be
+ * increased on each call to nofile_increase(0).
+ */
+#define NOFILE_INC_SIZE 64
+
+/*
+ * Default TCP send and receive buffer size of NFS server.
+ */
+#define NFSD_TCP_BUFSZ (1024*1024)
+
+struct conn_ind {
+ struct conn_ind *conn_next;
+ struct conn_ind *conn_prev;
+ struct t_call *conn_call;
+};
+
+struct conn_entry {
+ bool_t closing;
+ struct netconfig nc;
+};
+
+/*
+ * this file contains transport routines common to nfsd and lockd
+ */
+static int nofile_increase(int);
+static int reuseaddr(int);
+static int recvucred(int);
+static int anonmlp(int);
+static void add_to_poll_list(int, struct netconfig *);
+static char *serv_name_to_port_name(char *);
+static int bind_to_proto(char *, char *, struct netbuf **,
+ struct netconfig **);
+int nlm_bind_to_provider(char *, struct netbuf **, struct netconfig **);
+static void conn_close_oldest(void);
+static boolean_t conn_get(int, struct netconfig *, struct conn_ind **);
+static void cots_listen_event(int, int);
+static int discon_get(int, struct netconfig *, struct conn_ind **);
+static int do_poll_clts_action(int, int);
+static int do_poll_cots_action(int, int);
+static void remove_from_poll_list(int);
+static int set_addrmask(int, struct netconfig *, struct netbuf *);
+static int is_listen_fd_index(int);
+
+static struct pollfd *poll_array;
+static struct conn_entry *conn_polled;
+static int num_conns; /* Current number of connections */
+int (*Mysvc4)(int, struct netbuf *, struct netconfig *, int,
+ struct netbuf *);
+static int setopt(int fd, int level, int name, int value);
+static int get_opt(int fd, int level, int name);
+static void nfslib_set_sockbuf(int fd);
+
+extern boolean_t have_rpcbind;
+extern void lx_syslog(char *, ...);
+extern boolean_t lx_rpcb_set(const rpcvers_t, const struct netconfig *,
+ const struct netbuf *);
+extern void lx_rpcb_unset(const rpcvers_t, char *);
+
+/* We can't include syslog.h since we override it, so define these here. */
+#define LOG_ERR 3
+#define LOG_WARNING 4
+#define LOG_DEBUG 7
+
+/*
+ * Built-in netconfig table.
+ */
+#define N_NETCONF_ENTS 5
+static struct netconfig nca[N_NETCONF_ENTS] = {
+ {"udp6", NC_TPI_CLTS, 1, "inet6", "udp", "/dev/udp6", 0, NULL},
+ {"tcp6", NC_TPI_COTS_ORD, 1, "inet6", "tcp", "/dev/tcp6", 0, NULL},
+ {"udp", NC_TPI_CLTS, 1, "inet", "udp", "/dev/udp", 0, NULL},
+ {"tcp", NC_TPI_COTS_ORD, 1, "inet", "tcp", "/dev/tcp", 0, NULL},
+ {"ticotsord", NC_TPI_COTS_ORD, 1, "loopback", "-", "/dev/ticotsord", 0,
+ NULL}
+};
+
+/* To minimize file diffs we provide our own syslog wrapper. */
+static void
+syslog(int level, char *fmt, ...)
+{
+ va_list ap;
+
+ if (level == LOG_DEBUG)
+ return;
+ va_start(ap, fmt);
+ lx_syslog(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Called to create and prepare a transport descriptor for in-kernel
+ * RPC service.
+ * Returns -1 on failure and a valid descriptor on success.
+ */
+int
+nfslib_transport_open(struct netconfig *nconf)
+{
+ int fd;
+ struct strioctl strioc;
+
+ if ((nconf == (struct netconfig *)NULL) ||
+ (nconf->nc_device == (char *)NULL)) {
+ syslog(LOG_ERR, "no netconfig device");
+ return (-1);
+ }
+
+ /*
+ * Open the transport device.
+ */
+ fd = t_open(nconf->nc_device, O_RDWR, (struct t_info *)NULL);
+ if (fd == -1) {
+ if (t_errno == TSYSERR && errno == EMFILE &&
+ (nofile_increase(0) == 0)) {
+ /* Try again with a higher NOFILE limit. */
+ fd = t_open(nconf->nc_device, O_RDWR,
+ (struct t_info *)NULL);
+ }
+ if (fd == -1) {
+ syslog(LOG_ERR, "t_open %s failed: t_errno %d, %m",
+ nconf->nc_device, t_errno);
+ return (-1);
+ }
+ }
+
+ /*
+ * Pop timod because the RPC module must be as close as possible
+ * to the transport.
+ */
+ if (ioctl(fd, I_POP, 0) < 0) {
+ syslog(LOG_ERR, "I_POP of timod failed: %m");
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ /*
+ * Common code for CLTS and COTS transports
+ */
+ if (ioctl(fd, I_PUSH, "rpcmod") < 0) {
+ syslog(LOG_ERR, "I_PUSH of rpcmod failed: %m");
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ strioc.ic_cmd = RPC_SERVER;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+ strioc.ic_timout = -1;
+
+ /* Tell rpcmod to act like a server stream. */
+ if (ioctl(fd, I_STR, &strioc) < 0) {
+ syslog(LOG_ERR, "rpcmod set-up ioctl failed: %m");
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ /*
+ * Re-push timod so that we will still be doing TLI
+ * operations on the descriptor.
+ */
+ if (ioctl(fd, I_PUSH, "timod") < 0) {
+ syslog(LOG_ERR, "I_PUSH of timod failed: %m");
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ /*
+ * Enable options of returning the ip's for udp.
+ */
+ if (strcmp(nconf->nc_netid, "udp6") == 0)
+ __rpc_tli_set_options(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, 1);
+ else if (strcmp(nconf->nc_netid, "udp") == 0)
+ __rpc_tli_set_options(fd, IPPROTO_IP, IP_RECVDSTADDR, 1);
+
+ return (fd);
+}
+
+static int
+nofile_increase(int limit)
+{
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
+ syslog(LOG_ERR, "getrlimit of NOFILE failed: %m");
+ return (-1);
+ }
+
+ if (limit > 0)
+ rl.rlim_cur = limit;
+ else
+ rl.rlim_cur += NOFILE_INC_SIZE;
+
+ if (rl.rlim_cur > rl.rlim_max &&
+ rl.rlim_max != RLIM_INFINITY)
+ rl.rlim_max = rl.rlim_cur;
+
+ if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
+ syslog(LOG_ERR, "setrlimit of NOFILE to %d failed: %m",
+ rl.rlim_cur);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+nfslib_set_sockbuf(int fd)
+{
+ int curval, val;
+
+ val = NFSD_TCP_BUFSZ;
+
+ curval = get_opt(fd, SOL_SOCKET, SO_SNDBUF);
+ syslog(LOG_DEBUG, "Current SO_SNDBUF value is %d", curval);
+ if ((curval != -1) && (curval < val)) {
+ syslog(LOG_DEBUG, "Set SO_SNDBUF option to %d", val);
+ if (setopt(fd, SOL_SOCKET, SO_SNDBUF, val) < 0) {
+ syslog(LOG_ERR,
+ "couldn't set SO_SNDBUF to %d - t_errno = %d",
+ val, t_errno);
+ syslog(LOG_ERR,
+ "Check and increase system-wide tcp_max_buf");
+ }
+ }
+
+ curval = get_opt(fd, SOL_SOCKET, SO_RCVBUF);
+ syslog(LOG_DEBUG, "Current SO_RCVBUF value is %d", curval);
+ if ((curval != -1) && (curval < val)) {
+ syslog(LOG_DEBUG, "Set SO_RCVBUF option to %d", val);
+ if (setopt(fd, SOL_SOCKET, SO_RCVBUF, val) < 0) {
+ syslog(LOG_ERR,
+ "couldn't set SO_RCVBUF to %d - t_errno = %d",
+ val, t_errno);
+ syslog(LOG_ERR,
+ "Check and increase system-wide tcp_max_buf");
+ }
+ }
+}
+
+static int
+nlm_bindit(struct netconfig *nconf, struct netbuf **addr, int backlog)
+{
+ int fd;
+ struct t_bind *ntb;
+ struct t_bind tb;
+ struct nd_addrlist *addrlist;
+ struct t_optmgmt req, resp;
+ struct opthdr *opt;
+ char reqbuf[128];
+ struct nd_hostserv hs;
+
+ if ((fd = nfslib_transport_open(nconf)) == -1) {
+ syslog(LOG_ERR, "cannot establish transport service over %s",
+ nconf->nc_device);
+ return (-1);
+ }
+
+ addrlist = (struct nd_addrlist *)NULL;
+
+ /*
+ * This is the well-defined 'lockd' port from /etc/services. We can
+ * pass down the NLM port number to nlm_bindit and that resolves
+ * properly in the native *getby* calling paths.
+ */
+ hs.h_serv = "4045";
+ hs.h_host = HOST_SELF;
+
+ /*
+ * The following block is based on _netdir_getbyname in
+ * usr/src/lib/nametoaddr/straddr/common/straddr.c. This is what would
+ * be used by __classic_netdir_getbyname for the loopback.
+ */
+ if (strcmp(nconf->nc_netid, "ticotsord") == 0) {
+ struct nd_addrlist *ap;
+ struct netbuf *netbufp;
+ char *fulladdr = "localhost.4045";
+
+ if ((ap = malloc(sizeof (struct nd_addrlist))) == NULL) {
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ ap->n_cnt = 1;
+ if ((ap->n_addrs = malloc(sizeof (struct netbuf))) == NULL) {
+ free(ap);
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ netbufp = ap->n_addrs;
+
+ /* Don't include the terminating NULL character in the length */
+ netbufp->len = netbufp->maxlen = (int)strlen(fulladdr);
+ if ((netbufp->buf = strdup(fulladdr)) == NULL) {
+ free(netbufp);
+ free(ap);
+ (void) t_close(fd);
+ return (-1);
+ }
+ addrlist = ap;
+
+ } else if (netdir_getbyname(nconf, &hs, &addrlist) != 0) {
+ syslog(LOG_ERR,
+ "Cannot get address for transport %s",
+ nconf->nc_netid);
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ if (strcmp(nconf->nc_proto, "tcp") == 0) {
+ /*
+ * If we're running over TCP, then set the
+ * SO_REUSEADDR option so that we can bind
+ * to our preferred address even if previously
+ * left connections exist in FIN_WAIT states.
+ * This is somewhat bogus, but otherwise you have
+ * to wait 2 minutes to restart after killing it.
+ */
+ if (reuseaddr(fd) == -1) {
+ syslog(LOG_WARNING,
+ "couldn't set SO_REUSEADDR option on transport");
+ }
+ } else if (strcmp(nconf->nc_proto, "udp") == 0) {
+ /*
+ * In order to run MLP on UDP, we need to handle creds.
+ */
+ if (recvucred(fd) == -1) {
+ syslog(LOG_WARNING,
+ "couldn't set SO_RECVUCRED option on transport");
+ }
+ }
+
+ if (nconf->nc_semantics == NC_TPI_CLTS)
+ tb.qlen = 0;
+ else
+ tb.qlen = backlog;
+
+ /* LINTED pointer alignment */
+ ntb = (struct t_bind *)t_alloc(fd, T_BIND, T_ALL);
+ if (ntb == (struct t_bind *)NULL) {
+ syslog(LOG_ERR, "t_alloc failed: t_errno %d, %m", t_errno);
+ (void) t_close(fd);
+ netdir_free((void *)addrlist, ND_ADDRLIST);
+ return (-1);
+ }
+
+ /*
+ * XXX - what about the space tb->addr.buf points to? This should
+ * be either a memcpy() to/from the buf fields, or t_alloc(fd,T_BIND,)
+ * should't be called with T_ALL.
+ */
+ if (addrlist)
+ tb.addr = *(addrlist->n_addrs); /* structure copy */
+
+ if (t_bind(fd, &tb, ntb) == -1) {
+ syslog(LOG_ERR, "t_bind failed: t_errno %d, %m", t_errno);
+ (void) t_free((char *)ntb, T_BIND);
+ netdir_free((void *)addrlist, ND_ADDRLIST);
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ /* make sure we bound to the right address */
+ if (tb.addr.len != ntb->addr.len ||
+ memcmp(tb.addr.buf, ntb->addr.buf, tb.addr.len) != 0) {
+ syslog(LOG_ERR, "t_bind to wrong address");
+ (void) t_free((char *)ntb, T_BIND);
+ netdir_free((void *)addrlist, ND_ADDRLIST);
+ (void) t_close(fd);
+ return (-1);
+ }
+
+ *addr = &ntb->addr;
+ netdir_free((void *)addrlist, ND_ADDRLIST);
+
+ if (strcmp(nconf->nc_proto, "tcp") == 0) {
+ /*
+ * Disable the Nagle algorithm on TCP connections.
+ * Connections accepted from this listener will
+ * inherit the listener options.
+ */
+
+ /* LINTED pointer alignment */
+ opt = (struct opthdr *)reqbuf;
+ opt->level = IPPROTO_TCP;
+ opt->name = TCP_NODELAY;
+ opt->len = sizeof (int);
+
+ /* LINTED pointer alignment */
+ *(int *)((char *)opt + sizeof (*opt)) = 1;
+
+ req.flags = T_NEGOTIATE;
+ req.opt.len = sizeof (*opt) + opt->len;
+ req.opt.buf = (char *)opt;
+ resp.flags = 0;
+ resp.opt.buf = reqbuf;
+ resp.opt.maxlen = sizeof (reqbuf);
+
+ if (t_optmgmt(fd, &req, &resp) < 0 ||
+ resp.flags != T_SUCCESS) {
+ syslog(LOG_ERR,
+ "couldn't set NODELAY option for proto %s: t_errno = %d, %m",
+ nconf->nc_proto, t_errno);
+ }
+
+ nfslib_set_sockbuf(fd);
+ }
+
+ return (fd);
+}
+
+static int
+get_opt(int fd, int level, int name)
+{
+ struct t_optmgmt req, res;
+ struct {
+ struct opthdr opt;
+ int value;
+ } reqbuf;
+
+ reqbuf.opt.level = level;
+ reqbuf.opt.name = name;
+ reqbuf.opt.len = sizeof (int);
+ reqbuf.value = 0;
+
+ req.flags = T_CURRENT;
+ req.opt.len = sizeof (reqbuf);
+ req.opt.buf = (char *)&reqbuf;
+
+ res.flags = 0;
+ res.opt.buf = (char *)&reqbuf;
+ res.opt.maxlen = sizeof (reqbuf);
+
+ if (t_optmgmt(fd, &req, &res) < 0 || res.flags != T_SUCCESS) {
+ t_error("t_optmgmt");
+ return (-1);
+ }
+ return (reqbuf.value);
+}
+
+static int
+setopt(int fd, int level, int name, int value)
+{
+ struct t_optmgmt req, resp;
+ struct {
+ struct opthdr opt;
+ int value;
+ } reqbuf;
+
+ reqbuf.opt.level = level;
+ reqbuf.opt.name = name;
+ reqbuf.opt.len = sizeof (int);
+
+ reqbuf.value = value;
+
+ req.flags = T_NEGOTIATE;
+ req.opt.len = sizeof (reqbuf);
+ req.opt.buf = (char *)&reqbuf;
+
+ resp.flags = 0;
+ resp.opt.buf = (char *)&reqbuf;
+ resp.opt.maxlen = sizeof (reqbuf);
+
+ if (t_optmgmt(fd, &req, &resp) < 0 || resp.flags != T_SUCCESS) {
+ t_error("t_optmgmt");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+reuseaddr(int fd)
+{
+ return (setopt(fd, SOL_SOCKET, SO_REUSEADDR, 1));
+}
+
+static int
+recvucred(int fd)
+{
+ return (setopt(fd, SOL_SOCKET, SO_RECVUCRED, 1));
+}
+
+static int
+anonmlp(int fd)
+{
+ return (setopt(fd, SOL_SOCKET, SO_ANON_MLP, 1));
+}
+
+void
+nfslib_log_tli_error(char *tli_name, int fd, struct netconfig *nconf)
+{
+ int error;
+
+ /*
+ * Save the error code across syslog(), just in case syslog()
+ * gets its own error and, therefore, overwrites errno.
+ */
+ error = errno;
+ if (t_errno == TSYSERR) {
+ syslog(LOG_ERR, "%s(file descriptor %d/transport %s) %m",
+ tli_name, fd, nconf->nc_proto);
+ } else {
+ syslog(LOG_ERR,
+ "%s(file descriptor %d/transport %s) TLI error %d",
+ tli_name, fd, nconf->nc_proto, t_errno);
+ }
+ errno = error;
+}
+
+/*
+ * Called to set up service over a particular transport.
+ */
+void
+nlm_do_one(char *provider, int (*svc)(int, struct netbuf, struct netconfig *))
+{
+ register int sock;
+ struct netbuf *retaddr;
+ struct netconfig *retnconf;
+ struct netbuf addrmask;
+ int vers;
+ int err;
+ static boolean_t elogged = B_FALSE;
+
+ sock = nlm_bind_to_provider(provider, &retaddr, &retnconf);
+ if (sock == -1) {
+ (void) syslog(LOG_ERR,
+ "Cannot establish NLM service over %s: transport setup problem.",
+ provider);
+ return;
+ }
+
+ if (set_addrmask(sock, retnconf, &addrmask) < 0) {
+ (void) syslog(LOG_ERR,
+ "Cannot set address mask for %s", retnconf->nc_netid);
+ return;
+ }
+
+ /* Register all versions of the NLM with rpcbind. */
+ if (strcmp(provider, "/dev/ticotsord") != 0) {
+ for (vers = NLM_VERS; vers <= NLM4_VERS; vers++) {
+ lx_rpcb_unset(vers, retnconf->nc_netid);
+ have_rpcbind = lx_rpcb_set(vers, retnconf, retaddr);
+ if (!have_rpcbind && !elogged) {
+ /*
+ * No rpcbind running. The kernel NFS locking
+ * code interacts with rpcbind & rpc.statd for
+ * NFSv3 locking. We warn about this but
+ * proceed since NFSv4 locking is still usable.
+ */
+ lx_syslog("Warning: rpcbind is not running, "
+ "but is required for NFSv3 locking");
+ elogged = B_TRUE;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Register services with CLTS semantics right now.
+ * Note: services with COTS/COTS_ORD semantics will be
+ * registered later from cots_listen_event function.
+ */
+ if (retnconf->nc_semantics == NC_TPI_CLTS) {
+ /* Don't drop core if supporting module(s) aren't loaded. */
+ (void) signal(SIGSYS, SIG_IGN);
+
+ /*
+ * svc() doesn't block, it returns success or failure.
+ */
+
+ if (svc == NULL && Mysvc4 != NULL)
+ err = (*Mysvc4)(sock, &addrmask, retnconf,
+ NFS4_SETPORT|NFS4_KRPC_START, retaddr);
+ else
+ err = (*svc)(sock, addrmask, retnconf);
+
+ if (err < 0) {
+ (void) syslog(LOG_ERR,
+ "Cannot establish NLM service over <file desc."
+ " %d, protocol %s> : %m. Exiting",
+ sock, retnconf->nc_proto);
+ exit(1);
+ }
+ }
+ free(addrmask.buf);
+
+ /*
+ * We successfully set up the server over this transport.
+ * Add this descriptor to the one being polled on.
+ */
+ add_to_poll_list(sock, retnconf);
+}
+
+/*
+ * Set up the NFS service over all the available transports.
+ * Returns -1 for failure, 0 for success.
+ */
+int
+do_all(struct protob *protobp,
+ int (*svc)(int, struct netbuf, struct netconfig *))
+{
+ struct netconfig *nconf;
+ NCONF_HANDLE *nc;
+ int l;
+
+ if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) {
+ syslog(LOG_ERR, "setnetconfig failed: %m");
+ return (-1);
+ }
+ l = strlen(NC_UDP);
+ while (nconf = getnetconfig(nc)) {
+ if ((nconf->nc_flag & NC_VISIBLE) &&
+ strcmp(nconf->nc_protofmly, NC_LOOPBACK) != 0 &&
+ OK_TPI_TYPE(nconf) &&
+ (protobp->program != NFS4_CALLBACK ||
+ strncasecmp(nconf->nc_proto, NC_UDP, l) != 0))
+ continue; /* do_all should never be called */
+ }
+ (void) endnetconfig(nc);
+ return (0);
+}
+
+/*
+ * poll on the open transport descriptors for events and errors.
+ */
+void
+poll_for_action(void)
+{
+ int nfds;
+ int i;
+
+ /*
+ * Keep polling until all transports have been closed. When this
+ * happens, we return.
+ */
+ while ((int)num_fds > 0) {
+ nfds = poll(poll_array, num_fds, INFTIM);
+ switch (nfds) {
+ case 0:
+ continue;
+
+ case -1:
+ /*
+ * Some errors from poll could be
+ * due to temporary conditions, and we try to
+ * be robust in the face of them. Other
+ * errors (should never happen in theory)
+ * are fatal (eg. EINVAL, EFAULT).
+ */
+ switch (errno) {
+ case EINTR:
+ continue;
+
+ case EAGAIN:
+ case ENOMEM:
+ (void) sleep(10);
+ continue;
+
+ default:
+ (void) syslog(LOG_ERR,
+ "poll failed: %m. Exiting");
+ exit(1);
+ }
+ default:
+ break;
+ }
+
+ /*
+ * Go through the poll list looking for events.
+ */
+ for (i = 0; i < num_fds && nfds > 0; i++) {
+ if (poll_array[i].revents) {
+ nfds--;
+ /*
+ * We have a message, so try to read it.
+ * Record the error return in errno,
+ * so that syslog(LOG_ERR, "...%m")
+ * dumps the corresponding error string.
+ */
+ if (conn_polled[i].nc.nc_semantics ==
+ NC_TPI_CLTS) {
+ errno = do_poll_clts_action(
+ poll_array[i].fd, i);
+ } else {
+ errno = do_poll_cots_action(
+ poll_array[i].fd, i);
+ }
+
+ if (errno == 0)
+ continue;
+ /*
+ * Most returned error codes mean that there is
+ * fatal condition which we can only deal with
+ * by closing the transport.
+ */
+ if (errno != EAGAIN && errno != ENOMEM) {
+ (void) syslog(LOG_ERR,
+ "Error (%m) reading descriptor %d/transport %s. Closing it.",
+ poll_array[i].fd,
+ conn_polled[i].nc.nc_proto);
+ (void) t_close(poll_array[i].fd);
+ remove_from_poll_list(poll_array[i].fd);
+
+ } else if (errno == ENOMEM)
+ (void) sleep(5);
+ }
+ }
+ }
+
+ (void) syslog(LOG_ERR,
+ "All transports have been closed with errors. Exiting.");
+}
+
+/*
+ * Allocate poll/transport array entries for this descriptor.
+ */
+static void
+add_to_poll_list(int fd, struct netconfig *nconf)
+{
+ static int poll_array_size = 0;
+
+ /*
+ * If the arrays are full, allocate new ones.
+ */
+ if (num_fds == poll_array_size) {
+ struct pollfd *tpa;
+ struct conn_entry *tnp;
+
+ if (poll_array_size != 0) {
+ tpa = poll_array;
+ tnp = conn_polled;
+ } else
+ tpa = (struct pollfd *)0;
+
+ poll_array_size += POLL_ARRAY_INC_SIZE;
+ /*
+ * Allocate new arrays.
+ */
+ poll_array = (struct pollfd *)
+ malloc(poll_array_size * sizeof (struct pollfd) + 256);
+ conn_polled = (struct conn_entry *)
+ malloc(poll_array_size * sizeof (struct conn_entry) + 256);
+ if (poll_array == (struct pollfd *)NULL ||
+ conn_polled == (struct conn_entry *)NULL) {
+ syslog(LOG_ERR, "malloc failed for poll array");
+ exit(1);
+ }
+
+ /*
+ * Copy the data of the old ones into new arrays, and
+ * free the old ones.
+ */
+ if (tpa) {
+ (void) memcpy((void *)poll_array, (void *)tpa,
+ num_fds * sizeof (struct pollfd));
+ (void) memcpy((void *)conn_polled, (void *)tnp,
+ num_fds * sizeof (struct conn_entry));
+ free((void *)tpa);
+ free((void *)tnp);
+ }
+ }
+
+ /*
+ * Set the descriptor and event list. All possible events are
+ * polled for.
+ */
+ poll_array[num_fds].fd = fd;
+ poll_array[num_fds].events = POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI;
+
+ /*
+ * Copy the transport data over too.
+ */
+ conn_polled[num_fds].nc = *nconf;
+ conn_polled[num_fds].closing = 0;
+
+ /*
+ * Set the descriptor to non-blocking. Avoids a race
+ * between data arriving on the stream and then having it
+ * flushed before we can read it.
+ */
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ (void) syslog(LOG_ERR,
+ "fcntl(file desc. %d/transport %s, F_SETFL, O_NONBLOCK): %m. Exiting",
+ num_fds, nconf->nc_proto);
+ exit(1);
+ }
+
+ /*
+ * Count this descriptor.
+ */
+ ++num_fds;
+}
+
+static void
+remove_from_poll_list(int fd)
+{
+ int i;
+ int num_to_copy;
+
+ for (i = 0; i < num_fds; i++) {
+ if (poll_array[i].fd == fd) {
+ --num_fds;
+ num_to_copy = num_fds - i;
+ (void) memcpy((void *)&poll_array[i],
+ (void *)&poll_array[i+1],
+ num_to_copy * sizeof (struct pollfd));
+ (void) memset((void *)&poll_array[num_fds], 0,
+ sizeof (struct pollfd));
+ (void) memcpy((void *)&conn_polled[i],
+ (void *)&conn_polled[i+1],
+ num_to_copy * sizeof (struct conn_entry));
+ (void) memset((void *)&conn_polled[num_fds], 0,
+ sizeof (struct conn_entry));
+ return;
+ }
+ }
+ syslog(LOG_ERR, "attempt to remove nonexistent fd from poll list");
+
+}
+
+/*
+ * Called to read and interpret the event on a connectionless descriptor.
+ * Returns 0 if successful, or a UNIX error code if failure.
+ */
+static int
+do_poll_clts_action(int fd, int conn_index)
+{
+ int error;
+ int ret;
+ int flags;
+ struct netconfig *nconf = &conn_polled[conn_index].nc;
+ static struct t_unitdata *unitdata = NULL;
+ static struct t_uderr *uderr = NULL;
+ static int oldfd = -1;
+ struct nd_hostservlist *host = NULL;
+ struct strbuf ctl[1], data[1];
+ /*
+ * We just need to have some space to consume the
+ * message in the event we can't use the TLI interface to do the
+ * job.
+ *
+ * We flush the message using getmsg(). For the control part
+ * we allocate enough for any TPI header plus 32 bytes for address
+ * and options. For the data part, there is nothing magic about
+ * the size of the array, but 256 bytes is probably better than
+ * 1 byte, and we don't expect any data portion anyway.
+ *
+ * If the array sizes are too small, we handle this because getmsg()
+ * (called to consume the message) will return MOREDATA|MORECTL.
+ * Thus we just call getmsg() until it's read the message.
+ */
+ char ctlbuf[sizeof (union T_primitives) + 32];
+ char databuf[256];
+
+ /*
+ * If this is the same descriptor as the last time
+ * do_poll_clts_action was called, we can save some
+ * de-allocation and allocation.
+ */
+ if (oldfd != fd) {
+ oldfd = fd;
+
+ if (unitdata) {
+ (void) t_free((char *)unitdata, T_UNITDATA);
+ unitdata = NULL;
+ }
+ if (uderr) {
+ (void) t_free((char *)uderr, T_UDERROR);
+ uderr = NULL;
+ }
+ }
+
+ /*
+ * Allocate a unitdata structure for receiving the event.
+ */
+ if (unitdata == NULL) {
+ /* LINTED pointer alignment */
+ unitdata = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, T_ALL);
+ if (unitdata == NULL) {
+ if (t_errno == TSYSERR) {
+ /*
+ * Save the error code across
+ * syslog(), just in case
+ * syslog() gets its own error
+ * and therefore overwrites errno.
+ */
+ error = errno;
+ (void) syslog(LOG_ERR,
+ "t_alloc(file descriptor %d/transport %s, T_UNITDATA) failed: %m",
+ fd, nconf->nc_proto);
+ return (error);
+ }
+ (void) syslog(LOG_ERR,
+"t_alloc(file descriptor %d/transport %s, T_UNITDATA) failed TLI error %d",
+ fd, nconf->nc_proto, t_errno);
+ goto flush_it;
+ }
+ }
+
+try_again:
+ flags = 0;
+
+ /*
+ * The idea is we wait for T_UNITDATA_IND's. Of course,
+ * we don't get any, because rpcmod filters them out.
+ * However, we need to call t_rcvudata() to let TLI
+ * tell us we have a T_UDERROR_IND.
+ *
+ * algorithm is:
+ * t_rcvudata(), expecting TLOOK.
+ * t_look(), expecting T_UDERR.
+ * t_rcvuderr(), expecting success (0).
+ * expand destination address into ASCII,
+ * and dump it.
+ */
+
+ ret = t_rcvudata(fd, unitdata, &flags);
+ if (ret == 0 || t_errno == TBUFOVFLW) {
+ (void) syslog(LOG_WARNING,
+"t_rcvudata(file descriptor %d/transport %s) got unexpected data, %d bytes",
+ fd, nconf->nc_proto, unitdata->udata.len);
+
+ /*
+ * Even though we don't expect any data, in case we do,
+ * keep reading until there is no more.
+ */
+ if (flags & T_MORE)
+ goto try_again;
+
+ return (0);
+ }
+
+ switch (t_errno) {
+ case TNODATA:
+ return (0);
+ case TSYSERR:
+ /*
+ * System errors are returned to caller.
+ * Save the error code across
+ * syslog(), just in case
+ * syslog() gets its own error
+ * and therefore overwrites errno.
+ */
+ error = errno;
+ (void) syslog(LOG_ERR,
+ "t_rcvudata(file descriptor %d/transport %s) %m",
+ fd, nconf->nc_proto);
+ return (error);
+ case TLOOK:
+ break;
+ default:
+ (void) syslog(LOG_ERR,
+ "t_rcvudata(file descriptor %d/transport %s) TLI error %d",
+ fd, nconf->nc_proto, t_errno);
+ goto flush_it;
+ }
+
+ ret = t_look(fd);
+ switch (ret) {
+ case 0:
+ return (0);
+ case -1:
+ /*
+ * System errors are returned to caller.
+ */
+ if (t_errno == TSYSERR) {
+ /*
+ * Save the error code across
+ * syslog(), just in case
+ * syslog() gets its own error
+ * and therefore overwrites errno.
+ */
+ error = errno;
+ (void) syslog(LOG_ERR,
+ "t_look(file descriptor %d/transport %s) %m",
+ fd, nconf->nc_proto);
+ return (error);
+ }
+ (void) syslog(LOG_ERR,
+ "t_look(file descriptor %d/transport %s) TLI error %d",
+ fd, nconf->nc_proto, t_errno);
+ goto flush_it;
+ case T_UDERR:
+ break;
+ default:
+ (void) syslog(LOG_WARNING,
+ "t_look(file descriptor %d/transport %s) returned %d not T_UDERR (%d)",
+ fd, nconf->nc_proto, ret, T_UDERR);
+ }
+
+ if (uderr == NULL) {
+ /* LINTED pointer alignment */
+ uderr = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ALL);
+ if (uderr == NULL) {
+ if (t_errno == TSYSERR) {
+ /*
+ * Save the error code across
+ * syslog(), just in case
+ * syslog() gets its own error
+ * and therefore overwrites errno.
+ */
+ error = errno;
+ (void) syslog(LOG_ERR,
+ "t_alloc(file descriptor %d/transport %s, T_UDERROR) failed: %m",
+ fd, nconf->nc_proto);
+ return (error);
+ }
+ (void) syslog(LOG_ERR,
+"t_alloc(file descriptor %d/transport %s, T_UDERROR) failed TLI error: %d",
+ fd, nconf->nc_proto, t_errno);
+ goto flush_it;
+ }
+ }
+
+ ret = t_rcvuderr(fd, uderr);
+ if (ret == 0) {
+
+ /*
+ * Save the datagram error in errno, so that the
+ * %m argument to syslog picks up the error string.
+ */
+ errno = uderr->error;
+
+ /*
+ * Log the datagram error, then log the host that
+ * probably triggerred. Cannot log both in the
+ * same transaction because of packet size limitations
+ * in /dev/log.
+ */
+ (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING,
+"NFS response over <file descriptor %d/transport %s> generated error: %m",
+ fd, nconf->nc_proto);
+
+ /*
+ * Try to map the client's address back to a
+ * name.
+ */
+ ret = netdir_getbyaddr(nconf, &host, &uderr->addr);
+ if (ret != -1 && host && host->h_cnt > 0 &&
+ host->h_hostservs) {
+ (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING,
+"Bad NFS response was sent to client with host name: %s; service port: %s",
+ host->h_hostservs->h_host,
+ host->h_hostservs->h_serv);
+ } else {
+ int i, j;
+ char *buf;
+ char *hex = "0123456789abcdef";
+
+ /*
+ * Mapping failed, print the whole thing
+ * in ASCII hex.
+ */
+ buf = (char *)malloc(uderr->addr.len * 2 + 1);
+ for (i = 0, j = 0; i < uderr->addr.len; i++, j += 2) {
+ buf[j] = hex[((uderr->addr.buf[i]) >> 4) & 0xf];
+ buf[j+1] = hex[uderr->addr.buf[i] & 0xf];
+ }
+ buf[j] = '\0';
+ (void) syslog((errno == ECONNREFUSED) ? LOG_DEBUG : LOG_WARNING,
+ "Bad NFS response was sent to client with transport address: 0x%s",
+ buf);
+ free((void *)buf);
+ }
+
+ if (ret == 0 && host != NULL)
+ netdir_free((void *)host, ND_HOSTSERVLIST);
+ return (0);
+ }
+
+ switch (t_errno) {
+ case TNOUDERR:
+ goto flush_it;
+ case TSYSERR:
+ /*
+ * System errors are returned to caller.
+ * Save the error code across
+ * syslog(), just in case
+ * syslog() gets its own error
+ * and therefore overwrites errno.
+ */
+ error = errno;
+ (void) syslog(LOG_ERR,
+ "t_rcvuderr(file descriptor %d/transport %s) %m",
+ fd, nconf->nc_proto);
+ return (error);
+ default:
+ (void) syslog(LOG_ERR,
+ "t_rcvuderr(file descriptor %d/transport %s) TLI error %d",
+ fd, nconf->nc_proto, t_errno);
+ goto flush_it;
+ }
+
+flush_it:
+ /*
+ * If we get here, then we could not cope with whatever message
+ * we attempted to read, so flush it. If we did read a message,
+ * and one isn't present, that is all right, because fd is in
+ * nonblocking mode.
+ */
+ (void) syslog(LOG_ERR,
+ "Flushing one input message from <file descriptor %d/transport %s>",
+ fd, nconf->nc_proto);
+
+ /*
+ * Read and discard the message. Do this this until there is
+ * no more control/data in the message or until we get an error.
+ */
+ do {
+ ctl->maxlen = sizeof (ctlbuf);
+ ctl->buf = ctlbuf;
+ data->maxlen = sizeof (databuf);
+ data->buf = databuf;
+ flags = 0;
+ ret = getmsg(fd, ctl, data, &flags);
+ if (ret == -1)
+ return (errno);
+ } while (ret != 0);
+
+ return (0);
+}
+
+static void
+conn_close_oldest(void)
+{
+ int fd;
+ int i1;
+
+ /*
+ * Find the oldest connection that is not already in the
+ * process of shutting down.
+ */
+ for (i1 = end_listen_fds; /* no conditional expression */; i1++) {
+ if (i1 >= num_fds)
+ return;
+ if (conn_polled[i1].closing == 0)
+ break;
+ }
+#ifdef DEBUG
+ printf("too many connections (%d), releasing oldest (%d)\n",
+ num_conns, poll_array[i1].fd);
+#else
+ syslog(LOG_WARNING, "too many connections (%d), releasing oldest (%d)",
+ num_conns, poll_array[i1].fd);
+#endif
+ fd = poll_array[i1].fd;
+ if (conn_polled[i1].nc.nc_semantics == NC_TPI_COTS) {
+ /*
+ * For politeness, send a T_DISCON_REQ to the transport
+ * provider. We close the stream anyway.
+ */
+ (void) t_snddis(fd, (struct t_call *)0);
+ num_conns--;
+ remove_from_poll_list(fd);
+ (void) t_close(fd);
+ } else {
+ /*
+ * For orderly release, we do not close the stream
+ * until the T_ORDREL_IND arrives to complete
+ * the handshake.
+ */
+ if (t_sndrel(fd) == 0)
+ conn_polled[i1].closing = 1;
+ }
+}
+
+static boolean_t
+conn_get(int fd, struct netconfig *nconf, struct conn_ind **connp)
+{
+ struct conn_ind *conn;
+ struct conn_ind *next_conn;
+
+ conn = (struct conn_ind *)malloc(sizeof (*conn));
+ if (conn == NULL) {
+ syslog(LOG_ERR, "malloc for listen indication failed");
+ return (FALSE);
+ }
+
+ /* LINTED pointer alignment */
+ conn->conn_call = (struct t_call *)t_alloc(fd, T_CALL, T_ALL);
+ if (conn->conn_call == NULL) {
+ free((char *)conn);
+ nfslib_log_tli_error("t_alloc", fd, nconf);
+ return (FALSE);
+ }
+
+ if (t_listen(fd, conn->conn_call) == -1) {
+ nfslib_log_tli_error("t_listen", fd, nconf);
+ (void) t_free((char *)conn->conn_call, T_CALL);
+ free((char *)conn);
+ return (FALSE);
+ }
+
+ if (conn->conn_call->udata.len > 0) {
+ syslog(LOG_WARNING,
+ "rejecting inbound connection(%s) with %d bytes of connect data",
+ nconf->nc_proto, conn->conn_call->udata.len);
+
+ conn->conn_call->udata.len = 0;
+ (void) t_snddis(fd, conn->conn_call);
+ (void) t_free((char *)conn->conn_call, T_CALL);
+ free((char *)conn);
+ return (FALSE);
+ }
+
+ if ((next_conn = *connp) != NULL) {
+ next_conn->conn_prev->conn_next = conn;
+ conn->conn_next = next_conn;
+ conn->conn_prev = next_conn->conn_prev;
+ next_conn->conn_prev = conn;
+ } else {
+ conn->conn_next = conn;
+ conn->conn_prev = conn;
+ *connp = conn;
+ }
+ return (TRUE);
+}
+
+static int
+discon_get(int fd, struct netconfig *nconf, struct conn_ind **connp)
+{
+ struct conn_ind *conn;
+ struct t_discon discon;
+
+ discon.udata.buf = (char *)0;
+ discon.udata.maxlen = 0;
+ if (t_rcvdis(fd, &discon) == -1) {
+ nfslib_log_tli_error("t_rcvdis", fd, nconf);
+ return (-1);
+ }
+
+ conn = *connp;
+ if (conn == NULL)
+ return (0);
+
+ do {
+ if (conn->conn_call->sequence == discon.sequence) {
+ if (conn->conn_next == conn)
+ *connp = (struct conn_ind *)0;
+ else {
+ if (conn == *connp) {
+ *connp = conn->conn_next;
+ }
+ conn->conn_next->conn_prev = conn->conn_prev;
+ conn->conn_prev->conn_next = conn->conn_next;
+ }
+ free((char *)conn);
+ break;
+ }
+ conn = conn->conn_next;
+ } while (conn != *connp);
+
+ return (0);
+}
+
+static void
+cots_listen_event(int fd, int conn_index)
+{
+ struct t_call *call;
+ struct conn_ind *conn;
+ struct conn_ind *conn_head;
+ int event;
+ struct netconfig *nconf = &conn_polled[conn_index].nc;
+ int new_fd;
+ struct netbuf addrmask;
+ int ret = 0;
+ char *clnt;
+ char *clnt_uaddr = NULL;
+ struct nd_hostservlist *clnt_serv = NULL;
+
+ conn_head = NULL;
+ (void) conn_get(fd, nconf, &conn_head);
+
+ while ((conn = conn_head) != NULL) {
+ conn_head = conn->conn_next;
+ if (conn_head == conn)
+ conn_head = NULL;
+ else {
+ conn_head->conn_prev = conn->conn_prev;
+ conn->conn_prev->conn_next = conn_head;
+ }
+ call = conn->conn_call;
+ free(conn);
+
+ /*
+ * If we have already accepted the maximum number of
+ * connections allowed on the command line, then drop
+ * the oldest connection (for any protocol) before
+ * accepting the new connection. Unless explicitly
+ * set on the command line, max_conns_allowed is -1.
+ */
+ if (max_conns_allowed != -1 && num_conns >= max_conns_allowed)
+ conn_close_oldest();
+
+ /*
+ * Create a new transport endpoint for the same proto as
+ * the listener.
+ */
+ new_fd = nfslib_transport_open(nconf);
+ if (new_fd == -1) {
+ call->udata.len = 0;
+ (void) t_snddis(fd, call);
+ (void) t_free((char *)call, T_CALL);
+ syslog(LOG_ERR, "Cannot establish transport over %s",
+ nconf->nc_device);
+ continue;
+ }
+
+ /* Bind to a generic address/port for the accepting stream. */
+ if (t_bind(new_fd, NULL, NULL) == -1) {
+ nfslib_log_tli_error("t_bind", new_fd, nconf);
+ call->udata.len = 0;
+ (void) t_snddis(fd, call);
+ (void) t_free((char *)call, T_CALL);
+ (void) t_close(new_fd);
+ continue;
+ }
+
+ while (t_accept(fd, new_fd, call) == -1) {
+ if (t_errno != TLOOK) {
+#ifdef DEBUG
+ nfslib_log_tli_error("t_accept", fd, nconf);
+#endif
+ call->udata.len = 0;
+ (void) t_snddis(fd, call);
+ (void) t_free((char *)call, T_CALL);
+ (void) t_close(new_fd);
+ goto do_next_conn;
+ }
+ while (event = t_look(fd)) {
+ switch (event) {
+ case T_LISTEN:
+#ifdef DEBUG
+ printf(
+"cots_listen_event(%s): T_LISTEN during accept processing\n", nconf->nc_proto);
+#endif
+ (void) conn_get(fd, nconf, &conn_head);
+ continue;
+ case T_DISCONNECT:
+#ifdef DEBUG
+ printf(
+ "cots_listen_event(%s): T_DISCONNECT during accept processing\n",
+ nconf->nc_proto);
+#endif
+ (void) discon_get(fd, nconf,
+ &conn_head);
+ continue;
+ default:
+ syslog(LOG_ERR,
+ "unexpected event 0x%x during accept processing (%s)",
+ event, nconf->nc_proto);
+ call->udata.len = 0;
+ (void) t_snddis(fd, call);
+ (void) t_free((char *)call, T_CALL);
+ (void) t_close(new_fd);
+ goto do_next_conn;
+ }
+ }
+ }
+
+ if (set_addrmask(new_fd, nconf, &addrmask) < 0) {
+ (void) syslog(LOG_ERR,
+ "Cannot set address mask for %s",
+ nconf->nc_netid);
+ (void) t_snddis(new_fd, NULL);
+ (void) t_free((char *)call, T_CALL);
+ (void) t_close(new_fd);
+ continue;
+ }
+
+ /* Tell kRPC about the new stream. */
+ if (Mysvc4 != NULL)
+ ret = (*Mysvc4)(new_fd, &addrmask, nconf,
+ NFS4_KRPC_START, &call->addr);
+ else
+ ret = (*Mysvc)(new_fd, addrmask, nconf);
+
+ if (ret < 0) {
+ if (errno != ENOTCONN) {
+ syslog(LOG_ERR,
+ "unable to register new connection: %m");
+ } else {
+ /*
+ * This is the only error that could be
+ * caused by the client, so who was it?
+ */
+ if (netdir_getbyaddr(nconf, &clnt_serv,
+ &(call->addr)) == ND_OK &&
+ clnt_serv->h_cnt > 0)
+ clnt = clnt_serv->h_hostservs->h_host;
+ else
+ clnt = clnt_uaddr = taddr2uaddr(nconf,
+ &(call->addr));
+ /*
+ * If we don't know who the client was,
+ * remain silent.
+ */
+ if (clnt)
+ syslog(LOG_ERR,
+"unable to register new connection: client %s has dropped connection", clnt);
+ if (clnt_serv) {
+ netdir_free(clnt_serv, ND_HOSTSERVLIST);
+ clnt_serv = NULL;
+ }
+ if (clnt_uaddr) {
+ free(clnt_uaddr);
+ clnt_uaddr = NULL;
+ }
+ }
+ free(addrmask.buf);
+ (void) t_snddis(new_fd, NULL);
+ (void) t_free((char *)call, T_CALL);
+ (void) t_close(new_fd);
+ goto do_next_conn;
+ }
+
+ free(addrmask.buf);
+ (void) t_free((char *)call, T_CALL);
+
+ /*
+ * Poll on the new descriptor so that we get disconnect
+ * and orderly release indications.
+ */
+ num_conns++;
+ add_to_poll_list(new_fd, nconf);
+
+ /* Reset nconf in case it has been moved. */
+ nconf = &conn_polled[conn_index].nc;
+do_next_conn:;
+ }
+}
+
+static int
+do_poll_cots_action(int fd, int conn_index)
+{
+ char buf[256];
+ int event;
+ int i1;
+ int flags;
+ struct conn_entry *connent = &conn_polled[conn_index];
+ struct netconfig *nconf = &(connent->nc);
+ const char *errorstr;
+
+ while (event = t_look(fd)) {
+ switch (event) {
+ case T_LISTEN:
+#ifdef DEBUG
+printf("do_poll_cots_action(%s,%d): T_LISTEN event\n", nconf->nc_proto, fd);
+#endif
+ cots_listen_event(fd, conn_index);
+ break;
+
+ case T_DATA:
+#ifdef DEBUG
+printf("do_poll_cots_action(%d,%s): T_DATA event\n", fd, nconf->nc_proto);
+#endif
+ /*
+ * Receive a private notification from CONS rpcmod.
+ */
+ i1 = t_rcv(fd, buf, sizeof (buf), &flags);
+ if (i1 == -1) {
+ syslog(LOG_ERR, "t_rcv failed");
+ break;
+ }
+ if (i1 < sizeof (int))
+ break;
+ i1 = BE32_TO_U32(buf);
+ if (i1 == 1 || i1 == 2) {
+ /*
+ * This connection has been idle for too long,
+ * so release it as politely as we can. If we
+ * have already initiated an orderly release
+ * and we get notified that the stream is
+ * still idle, pull the plug. This prevents
+ * hung connections from continuing to consume
+ * resources.
+ */
+#ifdef DEBUG
+printf("do_poll_cots_action(%s,%d): ", nconf->nc_proto, fd);
+printf("initiating orderly release of idle connection\n");
+#endif
+ if (nconf->nc_semantics == NC_TPI_COTS ||
+ connent->closing != 0) {
+ (void) t_snddis(fd, (struct t_call *)0);
+ goto fdclose;
+ }
+ /*
+ * For NC_TPI_COTS_ORD, the stream is closed
+ * and removed from the poll list when the
+ * T_ORDREL is received from the provider. We
+ * don't wait for it here because it may take
+ * a while for the transport to shut down.
+ */
+ if (t_sndrel(fd) == -1) {
+ syslog(LOG_ERR,
+ "unable to send orderly release %m");
+ }
+ connent->closing = 1;
+ } else
+ syslog(LOG_ERR,
+ "unexpected event from CONS rpcmod %d", i1);
+ break;
+
+ case T_ORDREL:
+#ifdef DEBUG
+printf("do_poll_cots_action(%s,%d): T_ORDREL event\n", nconf->nc_proto, fd);
+#endif
+ /* Perform an orderly release. */
+ if (t_rcvrel(fd) == 0) {
+ /* T_ORDREL on listen fd's should be ignored */
+ if (!is_listen_fd_index(conn_index)) {
+ (void) t_sndrel(fd);
+ goto fdclose;
+ }
+ break;
+
+ } else if (t_errno == TLOOK) {
+ break;
+ } else {
+ nfslib_log_tli_error("t_rcvrel", fd, nconf);
+
+ /*
+ * check to make sure we do not close
+ * listen fd
+ */
+ if (is_listen_fd_index(conn_index))
+ break;
+ else
+ goto fdclose;
+ }
+
+ case T_DISCONNECT:
+#ifdef DEBUG
+printf("do_poll_cots_action(%s,%d): T_DISCONNECT event\n", nconf->nc_proto, fd);
+#endif
+ if (t_rcvdis(fd, (struct t_discon *)NULL) == -1)
+ nfslib_log_tli_error("t_rcvdis", fd, nconf);
+
+ /*
+ * T_DISCONNECT on listen fd's should be ignored.
+ */
+ if (is_listen_fd_index(conn_index))
+ break;
+ else
+ goto fdclose;
+
+ default:
+ if (t_errno == TSYSERR) {
+ if ((errorstr = strerror(errno)) == NULL) {
+ (void) sprintf(buf,
+ "Unknown error num %d", errno);
+ errorstr = (const char *) buf;
+ }
+ } else if (event == -1)
+ errorstr = t_strerror(t_errno);
+ else
+ errorstr = "";
+ syslog(LOG_ERR,
+ "unexpected TLI event (0x%x) on "
+ "connection-oriented transport(%s,%d):%s",
+ event, nconf->nc_proto, fd, errorstr);
+fdclose:
+ num_conns--;
+ remove_from_poll_list(fd);
+ (void) t_close(fd);
+ return (0);
+ }
+ }
+
+ return (0);
+}
+
+static char *
+serv_name_to_port_name(char *name)
+{
+ /*
+ * Map service names (used primarily in logging) to
+ * RPC port names (used by netdir_*() routines).
+ */
+ if (strcmp(name, "NFS") == 0) {
+ return ("nfs");
+ } else if (strcmp(name, "NLM") == 0) {
+ return ("lockd");
+ } else if (strcmp(name, "NFS4_CALLBACK") == 0) {
+ return ("nfs4_callback");
+ }
+
+ return ("unrecognized");
+}
+
+int
+nlm_bind_to_provider(char *provider, struct netbuf **addr,
+ struct netconfig **retnconf)
+{
+ int i, res;
+ struct netconfig *nconf = NULL, *np;
+
+ for (i = 0; i < N_NETCONF_ENTS; i++) {
+ np = &nca[i];
+
+ if (strcmp(np->nc_device, provider) != 0)
+ continue;
+
+ /* Construct our own netconfig */
+ if ((nconf = calloc(1, sizeof (struct netconfig))) == NULL)
+ goto out;
+
+ nconf->nc_semantics = np->nc_semantics;
+ if ((nconf->nc_netid = strdup(np->nc_netid)) == NULL)
+ goto out;
+ if ((nconf->nc_protofmly = strdup(np->nc_protofmly)) == NULL)
+ goto out;
+ if ((nconf->nc_proto = strdup(np->nc_proto)) == NULL)
+ goto out;
+ if ((nconf->nc_device = strdup(np->nc_device)) == NULL)
+ goto out;
+
+ *retnconf = nconf;
+
+ /*
+ * Passing addr == NULL implies we skip the bind since we only
+ * need the netconfig for unregistering from rpcbind.
+ */
+ if (addr == NULL)
+ return (0);
+ if ((res = nlm_bindit(nconf, addr, listen_backlog)) == -1)
+ freenetconfigent(nconf);
+ return (res);
+ }
+
+out:
+ if (nconf != NULL)
+ freenetconfigent(nconf);
+ syslog(LOG_ERR, "couldn't find netconfig entry for provider %s",
+ provider);
+ return (-1);
+}
+
+static int
+bind_to_proto(NETSELDECL(proto), char *serv, struct netbuf **addr,
+ struct netconfig **retnconf)
+{
+ struct netconfig *nconf;
+ NCONF_HANDLE *nc = NULL;
+ struct nd_hostserv hs;
+
+ hs.h_host = HOST_SELF;
+ hs.h_serv = serv_name_to_port_name(serv);
+
+ if ((nc = setnetconfig()) == (NCONF_HANDLE *)NULL) {
+ syslog(LOG_ERR, "setnetconfig failed: %m");
+ return (-1);
+ }
+ while (nconf = getnetconfig(nc)) {
+ if (OK_TPI_TYPE(nconf) && NETSELEQ(nconf->nc_proto, proto)) {
+ *retnconf = nconf;
+ return (nfslib_bindit(nconf, addr, &hs,
+ listen_backlog));
+ }
+ }
+ (void) endnetconfig(nc);
+
+ syslog(LOG_ERR, "couldn't find netconfig entry for protocol %s",
+ proto);
+ return (-1);
+}
+
+#include <netinet/in.h>
+
+/*
+ * Create an address mask appropriate for the transport.
+ * The mask is used to obtain the host-specific part of
+ * a network address when comparing addresses.
+ * For an internet address the host-specific part is just
+ * the 32 bit IP address and this part of the mask is set
+ * to all-ones. The port number part of the mask is zeroes.
+ */
+static int
+set_addrmask(int fd, struct netconfig *nconf, struct netbuf *mask)
+{
+ struct t_info info;
+
+ /*
+ * Find the size of the address we need to mask.
+ */
+ if (t_getinfo(fd, &info) < 0) {
+ t_error("t_getinfo");
+ return (-1);
+ }
+ mask->len = mask->maxlen = info.addr;
+ if (info.addr <= 0) {
+ /*
+ * loopback devices have infinite addr size
+ * (it is identified by -1 in addr field of t_info structure),
+ * so don't build the netmask for them. It's a special case
+ * that should be handled properly.
+ */
+ if ((info.addr == -1) &&
+ (0 == strcmp(nconf->nc_protofmly, NC_LOOPBACK))) {
+ memset(mask, 0, sizeof (*mask));
+ return (0);
+ }
+
+ syslog(LOG_ERR, "set_addrmask: address size: %ld", info.addr);
+ return (-1);
+ }
+
+ mask->buf = (char *)malloc(mask->len);
+ if (mask->buf == NULL) {
+ syslog(LOG_ERR, "set_addrmask: no memory");
+ return (-1);
+ }
+ (void) memset(mask->buf, 0, mask->len); /* reset all mask bits */
+
+ if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
+ /*
+ * Set the mask so that the port is ignored.
+ */
+ /* LINTED pointer alignment */
+ ((struct sockaddr_in *)mask->buf)->sin_addr.s_addr =
+ (ulong_t)~0;
+ /* LINTED pointer alignment */
+ ((struct sockaddr_in *)mask->buf)->sin_family =
+ (ushort_t)~0;
+ } else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
+ /* LINTED pointer alignment */
+ (void) memset(&((struct sockaddr_in6 *)mask->buf)->sin6_addr,
+ (uchar_t)~0, sizeof (struct in6_addr));
+ /* LINTED pointer alignment */
+ ((struct sockaddr_in6 *)mask->buf)->sin6_family =
+ (ushort_t)~0;
+ } else {
+
+ /*
+ * Set all mask bits.
+ */
+ (void) memset(mask->buf, 0xFF, mask->len);
+ }
+ return (0);
+}
+
+/*
+ * For listen fd's index is always less than end_listen_fds.
+ * end_listen_fds is defined externally in the daemon that uses this library.
+ * It's value is equal to the number of open file descriptors after the
+ * last listen end point was opened but before any connection was accepted.
+ */
+static int
+is_listen_fd_index(int index)
+{
+ return (index < end_listen_fds);
+}
diff --git a/usr/src/cmd/ssh/etc/Makefile b/usr/src/lib/brand/lx/lx_support/Makefile
index 66a60e0705..094f63d16a 100644
--- a/usr/src/cmd/ssh/etc/Makefile
+++ b/usr/src/lib/brand/lx/lx_support/Makefile
@@ -18,41 +18,40 @@
#
# CDDL HEADER END
#
+#
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright (c) 2018, Joyent, Inc.
-
-MANIFEST = ssh.xml
-SVCMETHOD = sshd
-
-include ../../Makefile.cmd
+# Copyright 2019 Joyent, Inc.
-ETCSSHDIR= $(ROOTETC)/ssh
-DIRS= $(ETCSSHDIR)
+PROG = lx_support
+PROGS = $(PROG)
+OBJS = lx_support
-FILES= sshd_config ssh_config
+all: $(PROG)
-ETCSSHFILES= $(FILES:%=$(ETCSSHDIR)/%)
+include ../Makefile.lx
+include $(SRC)/cmd/Makefile.cmd
-$(ETCSSHFILES) := FILEMODE= 644
+# override the install directory
+ROOTBIN = $(ROOTBRANDDIR)
+CLOBBERFILES = $(OBJS) $(ROOTPROGS)
-ROOTMANIFESTDIR = $(ROOTSVCNETWORK)
+UTSBASE = $(SRC)/uts
-$(ETCSSHDIR)/% : %
- $(INS.file)
-
-$(DIRS):
- $(INS.dir)
-
-$(POFILE):
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -D_REENTRANT -I$(UTSBASE)/common/brand/lx
+LDLIBS += -lzonecfg
SMOFF += signed
-all lint clean clobber _msg:
+.KEEP_STATE:
+
+install: all $(ROOTPROGS)
-install: all $(DIRS) $(ETCSSHFILES) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
+clean:
+ $(RM) $(PROG) $(OBJS)
-check: $(CHKMANIFEST)
+lint: lint_PROG
-include ../../Makefile.targ
+include $(SRC)/cmd/Makefile.targ
diff --git a/usr/src/lib/brand/lx/lx_support/lx_support.c b/usr/src/lib/brand/lx/lx_support/lx_support.c
new file mode 100644
index 0000000000..d35c68ed8d
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_support/lx_support.c
@@ -0,0 +1,392 @@
+/*
+ * 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 2017 Joyent, Inc.
+ */
+
+/*
+ * lx_support is a small cli utility used to perform some brand-specific
+ * tasks when booting, halting, or verifying a zone. This utility is not
+ * intended to be called by users - it is intended to be invoked by the
+ * zones utilities.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stropts.h>
+#include <sys/ioccom.h>
+#include <sys/stat.h>
+#include <sys/systeminfo.h>
+#include <sys/types.h>
+#include <sys/varargs.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include <libzonecfg.h>
+#include <sys/lx_brand.h>
+
+static void lxs_err(char *msg, ...) __NORETURN;
+static void usage(void) __NORETURN;
+
+#define CP_CMD "/usr/bin/cp"
+#define MOUNT_CMD "/sbin/mount"
+
+static char *bname = NULL;
+static char *zonename = NULL;
+static char *zoneroot = NULL;
+
+#if !defined(TEXT_DOMAIN) /* should be defined by cc -D */
+#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
+#endif
+
+static void
+lxs_err(char *msg, ...)
+{
+ char buf[1024];
+ va_list ap;
+
+ va_start(ap, msg);
+ /*LINTED*/
+ (void) vsnprintf(buf, sizeof (buf), msg, ap);
+ va_end(ap);
+
+ (void) printf("%s error: %s\n", bname, buf);
+
+ exit(1);
+ /*NOTREACHED*/
+}
+
+/*
+ * Cleanup from earlier versions of the code which created a /dev/initctl FIFO.
+ */
+static void
+lxs_remove_initctl()
+{
+ char special[MAXPATHLEN];
+
+ if (snprintf(special, sizeof (special), "%s/dev/initctl", zoneroot) >=
+ sizeof (special))
+ lxs_err("%s: %s", gettext("Failed to cleanup /dev/initctl"),
+ gettext("zoneroot is too long"));
+
+ (void) unlink(special);
+}
+
+/*
+ * fsck gets really confused when run inside a zone. Removing this file
+ * prevents it from running
+ */
+static void
+lxs_remove_autofsck()
+{
+ char path[MAXPATHLEN];
+ int err;
+
+ if (snprintf(path, MAXPATHLEN, "%s/root/.autofsck", zoneroot) >=
+ MAXPATHLEN)
+ lxs_err("%s: %s", gettext("Failed to remove /.autofsck"),
+ gettext("zoneroot is too long"));
+
+ if (unlink(path) < 0) {
+ err = errno;
+ if (err != ENOENT)
+ lxs_err("%s: %s",
+ gettext("Failed to remove /.autofsck"),
+ strerror(err));
+ }
+}
+
+/*
+ * Extract any lx-supported attributes from the zone configuration file.
+ */
+static void
+lxs_getattrs(zone_dochandle_t zdh, char **krelease)
+{
+ struct zone_attrtab attrtab;
+ int err;
+
+ /* initialize the attribute iterator */
+ if (zonecfg_setattrent(zdh) != Z_OK) {
+ zonecfg_fini_handle(zdh);
+ lxs_err(gettext("error accessing zone configuration"));
+ }
+
+ *krelease = (char *)malloc(LX_KERN_RELEASE_MAX);
+ if (*krelease == NULL)
+ lxs_err(gettext("out of memory"));
+
+ bzero(*krelease, LX_KERN_RELEASE_MAX);
+ while ((err = zonecfg_getattrent(zdh, &attrtab)) == Z_OK) {
+ if ((strcmp(attrtab.zone_attr_name, "kernel-version") == 0) &&
+ (zonecfg_get_attr_string(&attrtab, *krelease,
+ LX_KERN_RELEASE_MAX) != Z_OK))
+ lxs_err(gettext("invalid type for zone attribute: %s"),
+ attrtab.zone_attr_name);
+ }
+
+ if (strlen(*krelease) == 0) {
+ free(*krelease);
+ *krelease = NULL;
+ }
+
+ /* some kind of error while looking up attributes */
+ if (err != Z_NO_ENTRY)
+ lxs_err(gettext("error accessing zone configuration"));
+}
+
+/*
+ * Attempt to lookup the "tty" gid from within the zone's /etc/group file.
+ * The gid is used to emulate existing Linux udev behavior for setting the gid
+ * on a pty. If we cannot lookup the gid for some reason, we still allow
+ * the zone to boot.
+ *
+ * Because we're reading the lx zone's /etc/group file, we have to parse it
+ * ourselves, but this is simple since we only need the gid.
+ */
+static void
+lxs_set_ttygid(zoneid_t zoneid)
+{
+ char buf[MAXPATHLEN];
+ FILE *fp;
+ gid_t tty_gid = 0;
+
+ if (snprintf(buf, sizeof (buf), "%s/root/etc/group", zoneroot) >=
+ sizeof (buf))
+ return;
+
+ if ((fp = fopen(buf, "r")) == NULL)
+ return;
+
+ /*
+ * Look for the "tty" line and get the gid. Note that this loop will
+ * properly handle a long line that won't fit into buf on a single
+ * fgets. The subsequent fgets will consume more of the line but we
+ * continue on through that. In practice this is unlikely to occur.
+ */
+ while (fgets(buf, sizeof (buf), fp) != NULL) {
+ char *p, *p_id;
+ long val;
+
+ /* group name */
+ if ((p = strchr(buf, ':')) == NULL)
+ continue;
+ *p = '\0';
+ if (strcmp(buf, "tty") != 0)
+ continue;
+
+ /* found tty entry, skip the "x" field */
+ if ((p = strchr(p + 1, ':')) == NULL)
+ goto done;
+ /* gid field */
+ p_id = p + 1;
+ if ((p = strchr(p_id, ':')) == NULL)
+ goto done;
+ *p = '\0';
+
+ errno = 0;
+ val = strtol(p_id, &p, 10);
+ /* Perform simple validation on the gid */
+ if (errno != 0 || *p != '\0' || val > MAXUID || val < 0)
+ goto done;
+ tty_gid = (gid_t)val;
+ break;
+ }
+
+ if (tty_gid != 0)
+ (void) zone_setattr(zoneid, LX_ATTR_TTY_GID, &tty_gid,
+ sizeof (tty_gid));
+
+done:
+ (void) fclose(fp);
+}
+
+static int
+lxs_boot()
+{
+ zoneid_t zoneid;
+ zone_dochandle_t zdh;
+ char *krelease;
+
+ lxs_remove_initctl();
+ lxs_remove_autofsck();
+
+ if ((zdh = zonecfg_init_handle()) == NULL)
+ lxs_err(gettext("unable to initialize zone handle"));
+
+ if (zonecfg_get_handle((char *)zonename, zdh) != Z_OK) {
+ zonecfg_fini_handle(zdh);
+ lxs_err(gettext("unable to load zone configuration"));
+ }
+
+ /* Extract any relevant attributes from the config file. */
+ lxs_getattrs(zdh, &krelease);
+ zonecfg_fini_handle(zdh);
+
+ /*
+ * Let the kernel know whether or not this zone's init process
+ * should be automatically restarted on its death.
+ */
+ if ((zoneid = getzoneidbyname(zonename)) < 0)
+ lxs_err(gettext("unable to get zoneid"));
+
+ if (krelease != NULL) {
+ /* Backward compatability with incomplete version attr */
+ if (strcmp(krelease, "2.4") == 0) {
+ krelease = "2.4.21";
+ } else if (strcmp(krelease, "2.6") == 0) {
+ krelease = "2.6.18";
+ }
+
+ if (zone_setattr(zoneid, LX_ATTR_KERN_RELEASE, krelease,
+ strlen(krelease)) < 0)
+ lxs_err(gettext("unable to set kernel version"));
+ }
+
+ lxs_set_ttygid(zoneid);
+
+ return (0);
+}
+
+static int
+lxs_halt()
+{
+ return (0);
+}
+
+static int
+lxs_verify(char *xmlfile)
+{
+ zone_dochandle_t handle;
+ char *krelease;
+ char hostidp[HW_HOSTID_LEN];
+ zone_iptype_t iptype;
+
+ if ((handle = zonecfg_init_handle()) == NULL)
+ lxs_err(gettext("internal libzonecfg.so.1 error"), 0);
+
+ if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) {
+ zonecfg_fini_handle(handle);
+ lxs_err(gettext("zonecfg provided an invalid XML file"));
+ }
+
+ /*
+ * Check to see whether the zone has hostid emulation enabled.
+ */
+ if (zonecfg_get_hostid(handle, hostidp, sizeof (hostidp)) == Z_OK) {
+ zonecfg_fini_handle(handle);
+ lxs_err(gettext("lx zones do not support hostid emulation"));
+ }
+
+ /*
+ * Only exclusive stack is supported.
+ */
+ if (zonecfg_get_iptype(handle, &iptype) != Z_OK ||
+ iptype != ZS_EXCLUSIVE) {
+ zonecfg_fini_handle(handle);
+ lxs_err(gettext("lx zones do not support shared IP stacks"));
+ }
+
+ /* Extract any relevant attributes from the config file. */
+ lxs_getattrs(handle, &krelease);
+ zonecfg_fini_handle(handle);
+
+ if (krelease) {
+ char *pdot, *ep;
+ long major_ver;
+
+ pdot = strchr(krelease, '.');
+ if (pdot != NULL)
+ *pdot = '\0';
+ errno = 0;
+ major_ver = strtol(krelease, &ep, 10);
+ if (major_ver < 2 || errno != 0 || *ep != '\0')
+ lxs_err(gettext("invalid value for zone attribute: %s"),
+ "kernel-version");
+ if (pdot != NULL)
+ *pdot = '.';
+
+ }
+ return (0);
+}
+
+static void
+usage()
+{
+
+ (void) fprintf(stderr,
+ gettext("usage:\t%s boot <zoneroot> <zonename>\n"), bname);
+ (void) fprintf(stderr,
+ gettext(" \t%s halt <zoneroot> <zonename>\n"), bname);
+ (void) fprintf(stderr,
+ gettext(" \t%s verify <xml file>\n\n"), bname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ bname = basename(argv[0]);
+
+ if (argc < 3)
+ usage();
+
+ if (strcmp(argv[1], "boot") == 0) {
+ if (argc != 4)
+ lxs_err(gettext("usage: %s %s <zoneroot> <zonename>"),
+ bname, argv[1]);
+ zoneroot = argv[2];
+ zonename = argv[3];
+ return (lxs_boot());
+ }
+
+ if (strcmp(argv[1], "halt") == 0) {
+ if (argc != 4)
+ lxs_err(gettext("usage: %s %s <zoneroot> <zonename>"),
+ bname, argv[1]);
+ zoneroot = argv[2];
+ zonename = argv[3];
+ return (lxs_halt());
+ }
+
+ if (strcmp(argv[1], "verify") == 0) {
+ if (argc != 3)
+ lxs_err(gettext("usage: %s verify <xml file>"),
+ bname);
+ return (lxs_verify(argv[2]));
+ }
+
+ usage();
+ /*NOTREACHED*/
+}
diff --git a/usr/src/lib/brand/lx/lx_vdso/Makefile b/usr/src/lib/brand/lx/lx_vdso/Makefile
new file mode 100644
index 0000000000..56dd0a7a3c
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/Makefile
@@ -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 2016 Joyent, Inc.
+#
+
+include ../../../Makefile.lib
+
+SUBDIRS = tools $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+all install clean clobber: $(SUBDIRS)
+
+lint: $(LINT_SUBDIRS)
+
+$(MACH): tools
+$(MACH64): tools
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/brand/lx/lx_vdso/Makefile.com b/usr/src/lib/brand/lx/lx_vdso/Makefile.com
new file mode 100644
index 0000000000..ebc4d71042
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/Makefile.com
@@ -0,0 +1,85 @@
+#
+# 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) 2019, Joyent, Inc.
+#
+
+LIBRARY = lx_vdso.a
+VERS = .1
+
+include $(SRC)/lib/commpage/Makefile.shared.com
+
+COBJS = vdso_main.o vdso_subr.o
+OBJECTS = $(COBJS) $(COMMPAGE_OBJS)
+
+include ../../../../Makefile.lib
+include ../../Makefile.lx
+
+#
+# Since our name doesn't start with "lib", Makefile.lib incorrectly
+# calculates LIBNAME. Therefore, we set it here.
+#
+LIBNAME = lx_vdso
+
+MAPFILES = ../common/mapfile-vers
+MAPOPTS = $(MAPFILES:%=-Wl,-M%)
+
+ASOBJS = vdso_subr.o
+COBJS = vdso_main.o
+OBJECTS = $(ASOBJS) $(COBJS) $(COMMPAGE_OBJS)
+
+SRCDIR = ../common
+
+ASSRCS = $(ASOBJS:%.o=$(ISASRCDIR)/%.s)
+CSRCS = $(COBJS:%.o=$(SRCDIR)/%.c)
+SRCS = $(ASSRCS) $(CSRCS)
+
+LIBS = $(DYNLIB)
+DYNFLAGS += $(DYNFLAGS_$(CLASS))
+DYNFLAGS += $(MAPOPTS)
+LDLIBS +=
+ASFLAGS = -P $(ASFLAGS_$(CURTYPE)) -D_ASM
+
+
+LIBS = $(DYNLIB)
+
+CLEANFILES += $(DYNLIB)
+ROOTLIBDIR = $(ROOT)/usr/lib/brand/lx
+ROOTLIBDIR64 = $(ROOT)/usr/lib/brand/lx/$(MACH64)
+
+VDSO_TOOL = ../tools/vdso_tool
+
+.KEEP_STATE:
+
+#
+# While $(VDSO_TOOL) performs most of the transformations required to
+# construct a correct VDSO object, we still make use of $(ELFEDIT). To
+# remove the $(ELFEDIT) requirement would mean shouldering the burden of
+# becoming a link-editor; this dark lore is best left to the linker aliens.
+#
+all: $(LIBS)
+ $(ELFEDIT) -e "dyn:value -add VERSYM $$($(ELFEDIT) \
+ -e 'shdr:dump .SUNW_versym' $(DYNLIB) | \
+ $(AWK) '{ if ($$1 == "sh_addr:") { print $$2 } }')" $(DYNLIB)
+ $(VDSO_TOOL) -f $(DYNLIB)
+
+lint: $(LINTLIB) lintcheck
+
+include ../../../../Makefile.targ
+include $(SRC)/lib/commpage/Makefile.shared.targ
+
+pics/%.o: $(ISASRCDIR)/%.s
+ $(COMPILE.s) -o $@ $<
+ $(POST_PROCESS_S_O)
+
+pics/vdso_main.o := CPPFLAGS += $(COMMPAGE_CPPFLAGS) -I$(SRCDIR)
+pics/vdso_subr.o := ASFLAGS += -I$(SRC)/uts/common/brand/lx -I$(SRCDIR)
diff --git a/usr/src/lib/brand/lx/lx_vdso/amd64/Makefile b/usr/src/lib/brand/lx/lx_vdso/amd64/Makefile
new file mode 100644
index 0000000000..1a12492a97
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/amd64/Makefile
@@ -0,0 +1,42 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+ISASRCDIR=.
+TARGET_ARCH=$(MACH64)
+
+include ../Makefile.com
+include $(SRC)/lib/Makefile.lib.64
+
+ASFLAGS += -D__$(MACH64)
+
+SONAME = linux-vdso.so.1
+
+# Disable save-args since some vDSO consumers are sensitive to stack usage.
+SAVEARGS =
+
+#
+# You might ask, why aren't we overriding BUILD.SO in Makefile.com.
+# That's a sad story. The answer is that Makefile.lib.64 includes
+# Makefile.master.64 which redefines BUILD.SO, leaving us in an
+# unfortunate jumble. Therefore we have to redefine it in the
+# lower-level Makefile.
+#
+BUILD.SO = $(LD) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) $(LDLIBS)
+
+ASSYMDEP_OBJS = lx_vdso.o
+
+CLOBBERFILES = $(ROOTLIBDIR64)/$(DYNLIB) $(ROOTLIBDIR64)/$(LINTLIB)
+
+install: all $(ROOTLIBS64)
diff --git a/usr/src/lib/brand/lx/lx_vdso/amd64/vdso_subr.s b/usr/src/lib/brand/lx/lx_vdso/amd64/vdso_subr.s
new file mode 100644
index 0000000000..592884aa32
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/amd64/vdso_subr.s
@@ -0,0 +1,131 @@
+/*
+ *
+ * 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 2016 Joyent, Inc.
+ */
+
+
+#include <sys/asm_linkage.h>
+#include <sys/lx_syscalls.h>
+#include <vdso_defs.h>
+
+#if defined(lint)
+
+comm_page_t *
+__vdso_find_commpage()
+{}
+
+long
+__vdso_sys_clock_gettime(uint_t clock_id, timespec_t *tp)
+{}
+
+int
+__vdso_sys_gettimeofday(timespec_t *tp, struct lx_timezone *tz)
+{}
+
+time_t
+__vdso_sys_time(timespec_t *tp)
+{}
+
+#else /* lint */
+
+ ENTRY_NP(__vdso_find_commpage)
+ leaq 0x0(%rip), %rax
+ andq $LX_VDSO_ADDR_MASK, %rax
+ addq $LX_VDSO_SIZE, %rax
+ ret
+ SET_SIZE(__vdso_find_commpage)
+
+ ENTRY_NP(__vdso_sys_clock_gettime)
+ movl $LX_SYS_clock_gettime, %eax
+ syscall
+ ret
+ SET_SIZE(__vdso_sys_clock_gettime)
+
+ ENTRY_NP(__vdso_sys_gettimeofday)
+ movl $LX_SYS_gettimeofday, %eax
+ syscall
+ ret
+ SET_SIZE(__vdso_sys_gettimeofday)
+
+ ENTRY_NP(__vdso_sys_time)
+ movl $LX_SYS_time, %eax
+ syscall
+ ret
+ SET_SIZE(__vdso_sys_time)
+
+/*
+ * long
+ * __vdso_clock_gettime(uint_t, timespec_t *)
+ */
+ ENTRY_NP(__vdso_clock_gettime)
+ subq $0x18, %rsp
+ movl %edi, (%rsp)
+ movq %rsi, 0x8(%rsp)
+
+ call __vdso_find_commpage
+ movq %rax, 0x10(%rsp)
+
+ movq %rax, %rdi
+ call __cp_can_gettime
+ cmpl $0, %eax
+ je 5f
+
+ /*
+ * Restore the original args/stack (with commpage pointer in rdx)
+ * This enables the coming tail-call to the desired function, be it
+ * __cp_clock_gettime_* or __vdso_sys_clock_gettime.
+ */
+ movl (%rsp), %edi
+ movq 0x8(%rsp), %rsi
+ movq 0x10(%rsp), %rdx
+ addq $0x18, %rsp
+
+ cmpl $LX_CLOCK_REALTIME, %edi
+ jne 2f
+1:
+ movq %rdx, %rdi
+ jmp __cp_clock_gettime_realtime
+
+2:
+ cmpl $LX_CLOCK_MONOTONIC, %edi
+ jne 4f
+3:
+ movq %rdx, %rdi
+ jmp __cp_clock_gettime_monotonic
+
+4:
+ cmpl $LX_CLOCK_REALTIME_COARSE, %edi
+ je 1b
+ cmpl $LX_CLOCK_MONOTONIC_RAW, %edi
+ je 3b
+ cmpl $LX_CLOCK_MONOTONIC_COARSE, %edi
+ je 3b
+ jmp 6f
+
+5:
+ /*
+ * When falling through from a failed cp_can_gettime, the stack
+ * allocation must be released before a tail-call is made to the
+ * fallback syscall function.
+ */
+ addq $0x18, %rsp
+
+6:
+ /* Let the real syscall handle all other cases */
+ jmp __vdso_sys_clock_gettime
+ SET_SIZE(__vdso_clock_gettime)
+
+
+#endif /* lint */
diff --git a/usr/src/lib/brand/lx/lx_vdso/common/mapfile-vers b/usr/src/lib/brand/lx/lx_vdso/common/mapfile-vers
new file mode 100644
index 0000000000..11690ce7d6
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/common/mapfile-vers
@@ -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 2014 Joyent, Inc. All rights reserved.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION LINUX_2.6 {
+ global:
+ __vdso_gettimeofday;
+ __vdso_clock_gettime;
+ __vdso_getcpu;
+ __vdso_time;
+ local:
+ *;
+};
+
+#
+# The vDSO module in GNU/Linux must only have a single PT_LOAD section.
+# Further, we should not have any data sections at all. Therefore, we go
+# through and explicitly disable several of the writeable sections that
+# might commonly show up.
+#
+LOAD_SEGMENT data {
+ disable;
+};
+
+LOAD_SEGMENT ldata {
+ disable;
+};
+
+LOAD_SEGMENT bss {
+ disable;
+};
+
diff --git a/usr/src/lib/brand/lx/lx_vdso/common/vdso_defs.h b/usr/src/lib/brand/lx/lx_vdso/common/vdso_defs.h
new file mode 100644
index 0000000000..dfac918a53
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/common/vdso_defs.h
@@ -0,0 +1,42 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _VDSO_DEFS_H_
+#define _VDSO_DEFS_H_
+
+#define LX_CLOCK_REALTIME 0 /* CLOCK_REALTIME */
+#define LX_CLOCK_MONOTONIC 1 /* CLOCK_HIGHRES */
+#define LX_CLOCK_PROCESS_CPUTIME_ID 2 /* Emulated */
+#define LX_CLOCK_THREAD_CPUTIME_ID 3 /* Emulated */
+#define LX_CLOCK_MONOTONIC_RAW 4 /* CLOCK_HIGHRES */
+#define LX_CLOCK_REALTIME_COARSE 5 /* CLOCK_REALTIME */
+#define LX_CLOCK_MONOTONIC_COARSE 6 /* CLOCK_HIGHRES */
+
+#if !defined(_ASM)
+
+struct lx_timezone {
+ int tz_minuteswest; /* minutes W of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+
+/* Functions provided by the mach-specific vdso_subr.s */
+extern comm_page_t *__vdso_find_commpage();
+extern int __vdso_sys_gettimeofday(timespec_t *, struct lx_timezone *);
+extern time_t __vdso_sys_time(time_t *);
+extern long __vdso_sys_clock_gettime(uint_t, timespec_t *);
+
+#endif /* !defined(_ASM) */
+
+#endif /* _VDSO_DEFS_H_ */
diff --git a/usr/src/lib/brand/lx/lx_vdso/common/vdso_main.c b/usr/src/lib/brand/lx/lx_vdso/common/vdso_main.c
new file mode 100644
index 0000000000..40528a152b
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/common/vdso_main.c
@@ -0,0 +1,139 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <cp_defs.h>
+#include <vdso_defs.h>
+
+
+#if defined(__i386)
+
+long
+__vdso_clock_gettime(uint_t clock_id, timespec_t *tp)
+{
+ comm_page_t *cp = __vdso_find_commpage();
+
+ if (__cp_can_gettime(cp) == 0) {
+ return (__vdso_sys_clock_gettime(clock_id, tp));
+ }
+
+ switch (clock_id) {
+ case LX_CLOCK_REALTIME:
+ case LX_CLOCK_REALTIME_COARSE:
+ return (__cp_clock_gettime_realtime(cp, tp));
+
+ case LX_CLOCK_MONOTONIC:
+ case LX_CLOCK_MONOTONIC_RAW:
+ case LX_CLOCK_MONOTONIC_COARSE:
+ return (__cp_clock_gettime_monotonic(cp, tp));
+
+ case LX_CLOCK_PROCESS_CPUTIME_ID:
+ case LX_CLOCK_THREAD_CPUTIME_ID:
+ default:
+ return (__vdso_sys_clock_gettime(clock_id, tp));
+ }
+}
+
+/*
+ * On i386, the implementation of __cp_clock_gettime_monotonic expects that an
+ * hrt2ts function is provided. It is provided below since the vDSO is
+ * operating on its own, without native libc.
+ */
+void
+hrt2ts(hrtime_t hrt, timespec_t *tsp)
+{
+ uint32_t sec, nsec, tmp;
+
+ tmp = (uint32_t)(hrt >> 30);
+ sec = tmp - (tmp >> 2);
+ sec = tmp - (sec >> 5);
+ sec = tmp + (sec >> 1);
+ sec = tmp - (sec >> 6) + 7;
+ sec = tmp - (sec >> 3);
+ sec = tmp + (sec >> 1);
+ sec = tmp + (sec >> 3);
+ sec = tmp + (sec >> 4);
+ tmp = (sec << 7) - sec - sec - sec;
+ tmp = (tmp << 7) - tmp - tmp - tmp;
+ tmp = (tmp << 7) - tmp - tmp - tmp;
+ nsec = (uint32_t)hrt - (tmp << 9);
+ while (nsec >= NANOSEC) {
+ nsec -= NANOSEC;
+ sec++;
+ }
+ tsp->tv_sec = (time_t)sec;
+ tsp->tv_nsec = nsec;
+}
+
+#else
+
+/*
+ * On amd64, the __vdso_clock_gettime function is implemented in asm to stay
+ * within the allowed stack budget.
+ */
+
+#endif /* defined(__i386) */
+
+
+int
+__vdso_gettimeofday(timespec_t *tp, struct lx_timezone *tz)
+{
+ if (tz != NULL) {
+ tz->tz_minuteswest = 0;
+ tz->tz_dsttime = 0;
+ }
+
+ if (tp != NULL) {
+ comm_page_t *cp = __vdso_find_commpage();
+
+ if (__cp_can_gettime(cp) == 0) {
+ return (__vdso_sys_gettimeofday(tp, tz));
+ }
+
+ (void) __cp_clock_gettime_realtime(cp, tp);
+ tp->tv_nsec /= 1000;
+ }
+ return (0);
+}
+
+time_t
+__vdso_time(time_t *tp)
+{
+ comm_page_t *cp = __vdso_find_commpage();
+ timespec_t ts;
+
+ if (__cp_can_gettime(cp) == 0) {
+ return (__vdso_sys_time(tp));
+ }
+
+ (void) __cp_clock_gettime_realtime(cp, &ts);
+ if (tp != NULL) {
+ *tp = ts.tv_sec;
+ }
+ return (ts.tv_sec);
+}
+
+int
+__vdso_getcpu(uint_t *cpu, uint_t *node, void *tcache)
+{
+ comm_page_t *cp = __vdso_find_commpage();
+
+ if (cpu != NULL) {
+ *cpu = __cp_getcpu(cp);
+ }
+ if (node != NULL) {
+ *node = 0;
+ }
+ return (0);
+}
diff --git a/usr/src/lib/brand/lx/lx_vdso/i386/Makefile b/usr/src/lib/brand/lx/lx_vdso/i386/Makefile
new file mode 100644
index 0000000000..7f9a6a13e6
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/i386/Makefile
@@ -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 2016 Joyent, Inc.
+#
+
+ISASRCDIR=.
+TARGET_ARCH=$(MACH)
+
+include ../Makefile.com
+
+ASFLAGS += -D__$(MACH)
+
+SONAME = linux-gate.so.1
+
+#
+# You might ask, why aren't we overriding BUILD.SO in Makefile.com.
+# See the amd64 Makefile for more answers
+#
+BUILD.SO = $(LD) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) $(LDLIBS)
+
+ASSYMDEP_OBJS = lx_vdso.o
+
+CLOBBERFILES = $(ROOTLIBDIR)/$(DYNLIB) $(ROOTLIBDIR)/$(LINTLIB)
+
+# Set the object entry point for __vsyscall-ers
+entryfix: $(DYNLIB)
+ $(ELFEDIT) -e "ehdr:e_entry \
+ $$($(ELFEDIT) -re 'sym:st_value -osimple __vsyscall' $(DYNLIB))" \
+ $(DYNLIB)
+
+all: entryfix
+
+install: all $(ROOTLIBS)
diff --git a/usr/src/lib/brand/lx/lx_vdso/i386/vdso_subr.s b/usr/src/lib/brand/lx/lx_vdso/i386/vdso_subr.s
new file mode 100644
index 0000000000..ed7be8bb23
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/i386/vdso_subr.s
@@ -0,0 +1,92 @@
+/*
+ *
+ * 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 2016 Joyent, Inc.
+ */
+
+
+#include <sys/asm_linkage.h>
+#include <sys/lx_syscalls.h>
+
+
+#if defined(lint)
+
+comm_page_t *
+__vdso_find_commpage()
+{}
+
+long
+__vdso_sys_clock_gettime(uint_t clock_id, timespec_t *tp)
+{}
+
+int
+__vdso_sys_gettimeofday(timespec_t *tp, struct lx_timezone *tz)
+{}
+
+time_t
+__vdso_sys_time(timespec_t *tp)
+{}
+
+#else /* lint */
+
+ ENTRY_NP(__vdso_find_commpage)
+ call 1f
+1: popl %eax
+ andl $LX_VDSO_ADDR_MASK, %eax
+ addl $LX_VDSO_SIZE, %eax
+ ret
+ SET_SIZE(__vdso_find_commpage)
+
+ ENTRY_NP(__vdso_sys_clock_gettime)
+ movl $LX_SYS_clock_gettime, %eax
+ movl 0x4(%esp), %ebx
+ movl 0x8(%esp), %ecx
+ int $0x80
+ ret
+ SET_SIZE(__vdso_sys_clock_gettime)
+
+ ENTRY_NP(__vdso_sys_gettimeofday)
+ movl $LX_SYS_gettimeofday, %eax
+ movl 0x4(%esp), %ebx
+ movl 0x8(%esp), %ecx
+ int $0x80
+ ret
+ SET_SIZE(__vdso_sys_gettimeofday)
+
+ ENTRY_NP(__vdso_sys_time)
+ movl $LX_SYS_time, %eax
+ movl 0x4(%esp), %ebx
+ int $0x80
+ ret
+ SET_SIZE(__vdso_sys_time)
+
+ ENTRY_NP(__vsyscall)
+ /*
+ * On 32-bit Linux, the VDSO entry point (specified by e_entry)
+ * provides a potentially accelerated means to vector into the kernel.
+ * Normally this means using 'sysenter' with a Linux-custom calling
+ * convention so programs expecting int80 behavior are not required to
+ * change how arguments are passed.
+ *
+ * The SunOS sysenter entry point does _not_ tolerate such a departure
+ * from convention, so if this function is updated to use sysenter, it
+ * must properly marshal arguments onto the stack from the int80 style.
+ * Such an enhancement can only occur once sysenter receives the same
+ * branding hooks as syscall and int80.
+ */
+ int $0x80
+ ret
+ SET_SIZE(__vsyscall)
+
+#endif /* lint */
diff --git a/usr/src/lib/brand/lx/lx_vdso/tools/Makefile b/usr/src/lib/brand/lx/lx_vdso/tools/Makefile
new file mode 100644
index 0000000000..272234b66e
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/tools/Makefile
@@ -0,0 +1,45 @@
+#
+# 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 = vdso_tool
+
+include ../../../../../cmd/Makefile.cmd
+
+OBJS = vdso_tool.o
+
+# Native tool, doesn't need stack protection.
+STACKPROTECT = none
+
+CLOBBERFILES += $(PROG)
+
+NATIVECC_CFLAGS += $(CFLAGS) $(CCVERBOSE)
+NATIVECC_LDLIBS += -lelf
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all
+
+lint: lint_PROG
+
+clean:
+ $(RM) $(OBJS)
+
+$(PROG): $(OBJS)
+ $(NATIVECC) $(NATIVECC_CFLAGS) $(NATIVECC_LDLIBS) $(OBJS) -o $@
+ $(POST_PROCESS)
+
+include ../../../../../cmd/Makefile.targ
diff --git a/usr/src/lib/brand/lx/lx_vdso/tools/vdso_tool.c b/usr/src/lib/brand/lx/lx_vdso/tools/vdso_tool.c
new file mode 100644
index 0000000000..a097cd2414
--- /dev/null
+++ b/usr/src/lib/brand/lx/lx_vdso/tools/vdso_tool.c
@@ -0,0 +1,456 @@
+/*
+ * 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 2019 Joyent, Inc.
+ * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+/*
+ * vdso_tool: a build-time tool for adjusting properties of the "lx_vdso.so.1"
+ * object we build for VDSO emulation in the LX brand.
+ *
+ * This tool:
+ * - sets a few attributes in the ELF header;
+ * - ensures that there is only a single PT_LOAD program header section;
+ * - extends the size of that PT_LOAD program header to induce the loading of
+ * all sections into memory.
+ * - ensures that there is only a single PT_DYNAMIC program header section;
+ * - ensures that that PT_DYNAMIC section's p_flags do not have the writeable
+ * bit set; this is used by glibc >= 2.35 as the basis for whether to
+ * do relocation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <libelf.h>
+#include <gelf.h>
+
+#define PROG "vdso_tool"
+
+typedef enum vdso_flags {
+ VDSO_UNLINK = 0x0001,
+ VDSO_UPDATE = 0x0002
+} vdso_flags_t;
+
+typedef struct vdso {
+ int v_fd;
+ char *v_path;
+ Elf *v_elf;
+ vdso_flags_t v_flags;
+ int v_ptload_phdr;
+ int v_ptdynamic_phdr;
+ Elf64_Off v_max_offset;
+} vdso_t;
+
+static int
+open_vdso(vdso_t **vp, char *path)
+{
+ vdso_t *v;
+
+ if ((v = calloc(1, sizeof (vdso_t))) == NULL ||
+ (v->v_path = strdup(path)) == NULL) {
+ err(1, "could not allocate memory");
+ }
+ v->v_ptload_phdr = v->v_ptdynamic_phdr = -1;
+ v->v_fd = -1;
+ *vp = v;
+
+ /*
+ * Open shared object file.
+ */
+ if ((v->v_fd = open(v->v_path, O_RDWR)) == -1) {
+ (void) fprintf(stderr, "could not open: %s: %s\n", v->v_path,
+ strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * Attach libelf.
+ */
+ if ((v->v_elf = elf_begin(v->v_fd, ELF_C_RDWR, NULL)) == NULL) {
+ (void) fprintf(stderr, "could not attach libelf: %s\n",
+ elf_errmsg(-1));
+ return (-1);
+ }
+
+ if (elf_kind(v->v_elf) != ELF_K_ELF) {
+ (void) fprintf(stderr, "wrong elf type\n");
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+close_vdso(vdso_t *v)
+{
+ int rval = 0;
+
+ if (v == NULL) {
+ return (0);
+ }
+
+ if (v->v_elf != NULL) {
+ /*
+ * If we want to write to the file, do so now.
+ */
+ if (v->v_flags & VDSO_UPDATE) {
+ if (elf_update(v->v_elf, ELF_C_WRITE) == -1) {
+ (void) fprintf(stderr, "ERROR: elf_update "
+ "failed: %s\n", elf_errmsg(-1));
+ v->v_flags |= VDSO_UNLINK;
+ rval = -1;
+ }
+ }
+
+ /*
+ * Close the libelf handle for this file.
+ */
+ if (elf_end(v->v_elf) == -1) {
+ (void) fprintf(stderr, "ERROR: elf_end failed: %s\n",
+ elf_errmsg(-1));
+ v->v_flags |= VDSO_UNLINK;
+ rval = -1;
+ }
+ }
+
+ if (v->v_fd > 0) {
+ (void) close(v->v_fd);
+ }
+
+ if (v->v_flags & VDSO_UNLINK) {
+ (void) fprintf(stderr, "unlinking file: %s\n", v->v_path);
+ if (unlink(v->v_path) != 0) {
+ (void) fprintf(stderr, "unlink failed: %s\n",
+ strerror(errno));
+ rval = -1;
+ }
+ }
+
+ free(v->v_path);
+ free(v);
+
+ return (rval);
+}
+
+static int
+adjust_elf_ehdr(vdso_t *v)
+{
+ GElf_Ehdr ehdr;
+ boolean_t dirty = B_FALSE;
+
+ if (gelf_getehdr(v->v_elf, &ehdr) == NULL) {
+ (void) fprintf(stderr, "could not get ehdr: %s\n",
+ elf_errmsg(-1));
+ goto errout;
+ }
+
+ if (ehdr.e_ident[EI_OSABI] != ELFOSABI_NONE) {
+ (void) fprintf(stdout, "set EI_OSABI = ELFOSABI_NONE\n");
+ ehdr.e_ident[EI_OSABI] = ELFOSABI_NONE;
+ dirty = B_TRUE;
+ }
+
+ if (ehdr.e_ident[EI_ABIVERSION] != 0) {
+ (void) fprintf(stdout, "set EI_ABIVERSION = 0\n");
+ ehdr.e_ident[EI_ABIVERSION] = 0;
+ dirty = B_TRUE;
+ }
+
+ if (dirty && gelf_update_ehdr(v->v_elf, &ehdr) == 0) {
+ (void) fprintf(stderr, "could not update ehdr: %s\n",
+ elf_errmsg(-1));
+ goto errout;
+ }
+
+ v->v_flags |= VDSO_UPDATE;
+ return (0);
+
+errout:
+ v->v_flags |= VDSO_UNLINK;
+ return (-1);
+}
+
+static int
+find_phdrs(vdso_t *v)
+{
+ size_t nphdr, nloadable = 0, ndynamic = 0;
+ int i;
+
+ if (elf_getphdrnum(v->v_elf, &nphdr) != 0) {
+ (void) fprintf(stderr, "could not get phdr count: %s\n",
+ elf_errmsg(-1));
+ goto errout;
+ }
+ (void) fprintf(stdout, "phdr count: %d\n", nphdr);
+
+ for (i = 0; i < nphdr; i++) {
+ GElf_Phdr phdr;
+
+ if (gelf_getphdr(v->v_elf, i, &phdr) == NULL) {
+ (void) fprintf(stderr, "could not get phdr[%d] count: "
+ "%s\n", i, elf_errmsg(-1));
+ goto errout;
+ }
+
+ if (phdr.p_type == PT_LOAD) {
+ if (nloadable++ != 0) {
+ (void) fprintf(stderr,
+ "multiple PT_LOAD phdrs\n");
+ goto errout;
+ }
+
+ (void) fprintf(stdout, "PT_LOAD header is phdr[%d]\n",
+ i);
+ v->v_ptload_phdr = i;
+
+ if (phdr.p_filesz != phdr.p_memsz) {
+ (void) fprintf(stderr, "mismatched filesz "
+ "(%llx) and memsz (%llx)\n", phdr.p_filesz,
+ phdr.p_memsz);
+ goto errout;
+ }
+
+ if (phdr.p_filesz == 0) {
+ (void) fprintf(stderr,
+ "PT_LOAD filesz was zero\n");
+ goto errout;
+ }
+ }
+
+ if (phdr.p_type == PT_DYNAMIC) {
+ if (ndynamic++ != 0) {
+ (void) fprintf(stderr,
+ "multiple PT_DYNAMIC phdrs\n");
+ goto errout;
+ }
+
+ (void) fprintf(stdout,
+ "PT_DYNAMIC header is phdr[%d]\n", i);
+ v->v_ptdynamic_phdr = i;
+
+ if (phdr.p_filesz == 0) {
+ (void) fprintf(stderr,
+ "PT_DYNAMIC filesz was zero\n");
+ goto errout;
+ }
+ }
+ }
+
+ return (0);
+
+errout:
+ v->v_flags |= VDSO_UNLINK;
+ return (-1);
+}
+
+static int
+find_maximum_offset(vdso_t *v)
+{
+ size_t nshdr;
+ int i;
+
+ if (elf_getshdrnum(v->v_elf, &nshdr) != 0) {
+ (void) fprintf(stderr, "could not get shdr count: %s\n",
+ elf_errmsg(-1));
+ v->v_flags |= VDSO_UNLINK;
+ return (-1);
+ }
+ (void) fprintf(stdout, "shdr count: %d\n", nshdr);
+
+ for (i = 0; i < nshdr; i++) {
+ Elf_Scn *scn = elf_getscn(v->v_elf, i);
+ GElf_Shdr shdr;
+ Elf64_Off end;
+
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ (void) fprintf(stderr, "could not get shdr[%d] "
+ "count: %s\n", i, elf_errmsg(-1));
+ goto errout;
+ }
+
+ end = shdr.sh_offset + shdr.sh_size;
+
+ if (end > v->v_max_offset) {
+ v->v_max_offset = end;
+ }
+ }
+
+ (void) fprintf(stdout, "maximum offset: %llx\n", v->v_max_offset);
+
+ return (0);
+
+errout:
+ v->v_flags |= VDSO_UNLINK;
+ return (-1);
+}
+
+static int
+update_pt_load_size(vdso_t *v)
+{
+ GElf_Phdr phdr;
+
+ if (gelf_getphdr(v->v_elf, v->v_ptload_phdr, &phdr) == NULL) {
+ (void) fprintf(stderr, "could not get phdr[%d] count: %s\n",
+ v->v_ptload_phdr, elf_errmsg(-1));
+ goto errout;
+ }
+
+ (void) fprintf(stdout, "PT_LOAD size is currently %llx\n",
+ phdr.p_filesz);
+ if (phdr.p_filesz < v->v_max_offset) {
+ (void) fprintf(stdout, "extending PT_LOAD size from %llx "
+ "to %llx\n", phdr.p_filesz, v->v_max_offset);
+
+ phdr.p_memsz = phdr.p_filesz = v->v_max_offset;
+
+ if (gelf_update_phdr(v->v_elf, v->v_ptload_phdr, &phdr) == 0) {
+ (void) fprintf(stderr, "could not update PT_LOAD "
+ "phdr: %s", elf_errmsg(-1));
+ goto errout;
+ }
+
+ v->v_flags |= VDSO_UPDATE;
+ }
+
+ return (0);
+
+errout:
+ v->v_flags |= VDSO_UNLINK;
+ return (-1);
+}
+
+static int
+update_pt_dynamic_flags(vdso_t *v)
+{
+ GElf_Phdr phdr;
+
+ if (gelf_getphdr(v->v_elf, v->v_ptdynamic_phdr, &phdr) == NULL) {
+ (void) fprintf(stderr, "could not get phdr[%d] count: %s\n",
+ v->v_ptdynamic_phdr, elf_errmsg(-1));
+ goto errout;
+ }
+
+ (void) fprintf(stdout, "PT_DYNAMIC flags are currently %x\n",
+ phdr.p_flags);
+
+ if (phdr.p_flags & PF_W) {
+ phdr.p_flags &= ~PF_W;
+
+ (void) fprintf(stdout, "PT_DYNAMIC flags are now %x\n",
+ phdr.p_flags);
+
+ if (gelf_update_phdr(v->v_elf, v->v_ptdynamic_phdr,
+ &phdr) == 0) {
+ (void) fprintf(stderr,
+ "could not update PT_DYNAMIC phdr: %s",
+ elf_errmsg(-1));
+ goto errout;
+ }
+
+ v->v_flags |= VDSO_UPDATE;
+ }
+
+ return (0);
+
+errout:
+ v->v_flags |= VDSO_UNLINK;
+ return (-1);
+}
+
+int
+main(int argc, char **argv)
+{
+ vdso_t *v;
+ char *filen = NULL;
+ int errflg = 0;
+ int c;
+ int status = 0;
+ boolean_t do_update = B_TRUE;
+
+ while ((c = getopt(argc, argv, ":nf:")) != -1) {
+ switch (c) {
+ case 'n':
+ do_update = B_FALSE;
+ break;
+ case 'f':
+ filen = optarg;
+ break;
+ case ':':
+ (void) fprintf(stderr, "option -%c requires an "
+ "operand\n", optopt);
+ errflg++;
+ break;
+ case '?':
+ (void) fprintf(stderr, "unrecognised option: -%c\n",
+ optopt);
+ errflg++;
+ break;
+ }
+ }
+
+ if (errflg != 0 || optind != argc || filen == NULL) {
+ (void) fprintf(stderr, "usage: %s -f <vdso.so>\n", PROG);
+ return (1);
+ }
+
+ (void) fprintf(stdout, "vdso file: %s\n", filen);
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ (void) fprintf(stderr, "libelf mismatch: %s\n", elf_errmsg(-1));
+ return (2);
+ }
+
+ status = 3;
+ if (open_vdso(&v, filen) == -1) {
+ goto out;
+ }
+
+ status++;
+ if (adjust_elf_ehdr(v) == -1) {
+ goto out;
+ }
+
+ status++;
+ if (find_phdrs(v) == -1) {
+ goto out;
+ }
+
+ status++;
+ if (find_maximum_offset(v) == -1) {
+ goto out;
+ }
+
+ status++;
+ if (do_update && update_pt_load_size(v) == -1) {
+ goto out;
+ }
+
+ status++;
+ if (do_update && update_pt_dynamic_flags(v) == -1) {
+ goto out;
+ }
+
+out:
+ status++;
+ if (close_vdso(v) == 0) {
+ status = 0;
+ }
+
+ return (status);
+}
diff --git a/usr/src/lib/brand/lx/netfiles/Makefile b/usr/src/lib/brand/lx/netfiles/Makefile
new file mode 100644
index 0000000000..1d15d69850
--- /dev/null
+++ b/usr/src/lib/brand/lx/netfiles/Makefile
@@ -0,0 +1,47 @@
+#
+# 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.
+# Copyright 2015 Joyent, Inc.
+#
+
+TXTS = etc_default_nfs
+NFS_DFL = ../../../../cmd/fs.d/nfs/etc/nfs.dfl
+
+all: $(TXTS)
+
+include ../Makefile.lx
+
+lint:
+
+install: $(ROOTTXTS)
+
+clean:
+ -$(RM) etc_default_nfs
+
+clobber: clean
+ -$(RM) $(ROOTXMLDOCS) $(ROOTTXTS)
+
+etc_default_nfs: $(NFS_DFL)
+ $(RM) $@
+ $(CP) $(NFS_DFL) $@
diff --git a/usr/src/lib/brand/lx/testing/Makefile b/usr/src/lib/brand/lx/testing/Makefile
new file mode 100644
index 0000000000..9149929b60
--- /dev/null
+++ b/usr/src/lib/brand/lx/testing/Makefile
@@ -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 2016 Joyent, Inc.
+#
+
+TXTS = ltp_skiplist ltp_tests pts_ignorelist
+
+all:
+
+include $(SRC)/cmd/Makefile.cmd
+include ../Makefile.lx
+
+all := TARGET= all
+install := TARGET= install
+clobber := TARGET= clobber
+
+install: $(ROOTTXTS)
+
+_msg:
+
+lint:
+
+clean:
+
+clobber:
+ -$(RM) $(ROOTTXTS)
diff --git a/usr/src/lib/brand/lx/testing/Readme_ltp b/usr/src/lib/brand/lx/testing/Readme_ltp
new file mode 100644
index 0000000000..5165546c1a
--- /dev/null
+++ b/usr/src/lib/brand/lx/testing/Readme_ltp
@@ -0,0 +1,295 @@
+The Linux Test Project (LTP) provides the basis for testing the lx API
+implementation. The project webpage is at http://linux-test-project.github.io/
+and the source is available on GitHub at
+https://github.com/linux-test-project/ltp.git.
+
+LTP should be built and run from within an lx zone.
+
+To build
+--------
+As root first make sure you have the tools installed:
+ apt-get install build-essential autoconf automake git quota
+
+For 64-bit:
+ apt-get install libc6-dev-i386
+
+Additional prerequisites are required activate some tests:
+ apt-get install attr-dev libaio-dev
+
+As a normal user:
+ git clone https://github.com/linux-test-project/ltp.git
+ cd ltp
+ git checkout ed01f6a05c77f65cb5d1089474c9a0e2129c581a
+ make autotools
+ ./configure
+ make all
+
+As root:
+ make install
+
+The tests can be built in both a zone that has been installed with a 32-bit
+version of Linux and another zone that has been installed with a 64-bit version
+of the same release. When testing the 64-bit zone, a copy of the 32-bit build
+can be run in the 64-bit zone to ensure that 32-bit applications work
+correctly on a 64-bit install.
+
+Running the tests
+-----------------
+The LTP source tree provides detailed documentation on using the test suite, so
+this readme only give a short summary.
+
+Because many of the tests are targetted at kernel functionality which does not
+apply to Illumos, or test capabilities which are not available from within a
+zone, or test system call functionality which has not yet been completed, a
+skip list is used during the test run to bypass tests which are known to fail.
+
+The skip list lives in this directory and is delivered on the system. It is
+available from within the lx zone as /native/usr/lib/brand/lx/ltp_skiplist. As
+new functionality is completed, the skip list should be updated to remove tests
+which now work.
+
+As root:
+ cd /opt/ltp
+ /opt/ltp/runltp -f `cat /native/usr/lib/brand/lx/ltp_tests` \
+ -S /native/usr/lib/brand/lx/ltp_skiplist -p >/tmp/test.log 2>&1
+
+When the test run has finished, the results will be logged in a date/time
+stamped file under /opt/ltp/results. The summary at the end of the log file
+should show "Total Failures: 0". If not, something is wrong.
+
+Running tests for development
+-----------------------------
+The source for the tests can be found under the testcases directory. The
+largest and most useful set for lx live under testcases/kernel/syscalls.
+
+For development purposes, an individual test (or tests) can be run by listing
+them in a command file, one per line. For example, with a command file named
+~/tcmds, to run the read01 test you setup the file so it looks like this:
+ read01 read01
+
+You can run that specific test as follows:
+ /opt/ltp/runltp -f ~/tcmds -p -l ~/read.log
+
+Test status
+-----------
+This section provides a short summary of the rationale for why tests are being
+skipped.
+
+LTP groups tests into command files (i.e. syscalls, nptl, etc. provided with
+the -f option in the runltp command shown above). A complete list of the groups
+can be seen in LTP source tree under the runtest directory. Some of these
+groups are obviously not applicable when running in an lx zone. The remaining
+groups still need work before they can be run. The groups shown in the runltp
+command above are expected to work when the skip list is used. The 'syscalls'
+command file runs the majority of the actual system call tests which we are
+interested in.
+
+The following table indicates why specific subtests are being skipped. Also
+note that the following tests pass in a 64-bit lx zone, but fail in a zone
+installed with a 32-bit Linux build: mmap15, open12, openat02 and sendfile09.
+
+ Legend:
+ x = never in a zone
+ * = fails on kvm and bare metal too
+ # = emulation not implemented yet
+ - = could enable with a test zone config change
+
+- access06 wants a block device
+x acct01 enables bsd process accounting
+# add_key01
+# add_key02
+x adjtimex01
+x adjtimex02
+x bdflush01
+x cacheflush01
+x chmod03 need PRIV_SYS_CONFIG to set sticky bit on reg file
+- chmod06 needs dev
+x chmod07 need PRIV_SYS_CONFIG to set sticky bit on reg file
+- chown04 needs dev
+- chown04_16 needs dev
+# clone02
+# clone08
+- creat06 wants to mount a ro fs
+x creat07 we don't behave this way for ETXTBSY
+x creat08 sets euid to 'nobody', loses PRIV_FILE_SETID to set sgid
+x execve04 we don't behave this way for ETXTBSY
+# fallocate01
+# fallocate02
+# fallocate03
+x fchmod02 need PRIV_SYS_CONFIG to set sticky bit on reg file
+x fchmod03 need PRIV_SYS_CONFIG to set sticky bit on reg file
+- fchmod06 needs dev
+# fchown04 mounts
+# fchown04_16
+# fcntl06 not supported on linux
+# fcntl06_64
+# fcntl23 leases not implemented
+# fcntl23_64 "
+# fcntl24 "
+# fcntl24_64 "
+# fcntl25 "
+# fcntl25_64 "
+# fcntl26 "
+# fcntl26_64 "
+# fcntl30
+# fcntl30_64
+# fcntl31 setown/getown not impl
+# fcntl31_64
+# fcntl32 F_SETLEASE not impl
+# fcntl32_64
+# fcntl33 F_SETLEASE not impl
+# fcntl33_64
+# fork05 asm into %fs reg
+- fork09 needs a swap cap of ~9GB
+# fork13 decided not to support this
+# fork14 "
+# ftruncate04 need a mnt with mandatory locking
+# ftruncate04_64
+# getdents02 wrong errno on test 4 - perf. impact too high
+# getdents02_64
+# get_mempolicy01
+x getrusage03 we don't fill in the ru_maxrss field
+- getxattr01 need attr/xattr.h at build time
+- getxattr02
+- getxattr03
+# ioctl03 needs /dev/net/tun
+# io_cancel01 libaio stuff not done
+# io_destroy01
+# io_getevents01
+# io_setup01
+# io_submit01
+- inotify03 needs dev
+# fanotify01 don't have fanotify
+# fanotify02
+# fanotify03
+# fanotify04
+# fanotify05
+# keyctl01 no kernel keyring support
+- lchown03 needs to mount ro fs
+- lchown03_16
+- linkat02 needs dev
+- link08 needs dev
+x mem01 crashme test which expects OOM killer to save the day
+- mkdir03 needs dev
+- mkdirat02 needs dev
+x mknod01 makes block and chr devs
+- mknod07 needs dev
+- mknodat02 needs dev
+# mmap13 expects "invalid access" to SIGBUS
+- mount01 needs dev
+- mount02 needs dev
+x mount03 mounts ext2
+- mount04 needs dev
+x mount05 mounts ext2
+x mount06 mounts ext2
+# mq_notify01
+# mq_notify02
+# mq_open01
+# mq_timedreceive01
+# mq_timedsend01
+# mq_unlink01
+x mremap01
+x mremap02
+x mremap03
+x mremap04
+x mremap05
+# msgctl12 uses MSG_STAT
+# msgrcv07 MSG_EXCEPT subtest - not avail.
+x open01 need PRIV_SYS_CONFIG to set sticky bit on reg file
+# open02 expects NOATIME to cause err for unpriv user
+# open10 setgid on sgid subdir behavior
+x open11 makes device
+# ppoll01
+# process_vm_readv01
+# process_vm_readv02
+# process_vm_readv03
+# process_vm_writev01
+# process_vm_writev02
+# prot_hsymlinks /proc/sys/fs/protected_hardlinks
+x ptrace04 not supp on our arch
+# ptrace05 OS-3307
+# read02 checks errno for O_DIRECT
+# readahead01
+# readahead02
+# readdir21 dir perf. issue
+- rename11 needs dev
+- renameat01 needs dev
+- rmdir02 needs dev
+x sched_getparam01 assumes Linux SCHED_OTHER return value
+x sched_getparam02 assumes Linux SCHED_OTHER return value
+# sched_rr_get_interval01
+# sched_rr_get_interval02
+# sched_rr_get_interval03
+x sched_setparam02 tries to set Linux policies
+x sched_setparam03 tries to set Linux policies
+# sched_getscheduler01
+# sched_getscheduler02
+x sched_setscheduler01
+x sched_setaffinity01
+x sched_getaffinity01
+# semctl01 all pass but SEM_STAT - linux specific
+# semop02 last test fails errno - expensive
+# sendfile02 OS-3296
+# sendfile02_64 "
+# sendfile04 "
+# sendfile04_64 "
+# sendfile06 "
+# sendfile06_64 "
+# sendfile07 "
+# sendfile07_64 "
+sendmsg01 OS-3295 - tests actually pass
+x setfsuid04 no real equiv. and only for NFS server
+x setfsuid04_16
+# sgetmask01 obsolete
+# setgroups04_16 expects sig11 for certain err
+# setns01
+# setns02
+# setpgid02 all pass but one, expects pid 0 to be there
+* setregid02 fails on bare metal, expects to lookup group "nobody" which
+* setregid02_16 doesn't exist. it is "nogroup" on ubuntu at least
+# setrlimit01 all pass but one, expects to set proc limit
+x settimeofday01
+# setxattr01
+# setxattr02
+# setxattr03
+# shmget05 OS-3326
+# splice01
+# splice02
+# splice03
+# tee01
+# tee02
+# ssetmask01 obsolete
+x stime01
+x switch01
+# sync_file_range01
+# sysconf01 most pass but see OS-3305
+# sysctl01 the build compiled this out,
+# sysctl03 this syscall is basically obsolete
+# sysctl04 obsolete
+# sysctl05
+# syslog01
+# syslog02
+# syslog03
+# syslog04
+# syslog05
+# syslog06
+# syslog07
+# syslog08
+# syslog09
+# syslog10
+# syslog11
+# syslog12
+# unshare01
+# unshare02
+- umount01 needs dev
+- umount02 needs dev
+- umount03 needs dev
+x ustat01 obsolete call to stat FS
+x ustat02 obsolete call to stat FS
+- utime06 needs dev
+- utimes01 needs dev
+# utimensat01 many subtests pass but see OS-3328 for the rest
+# vmsplice01
+# vmsplice02
+# perf_event_open01
+# perf_event_open02
diff --git a/usr/src/lib/brand/lx/testing/ltp_skiplist b/usr/src/lib/brand/lx/testing/ltp_skiplist
new file mode 100644
index 0000000000..09ff2cacc1
--- /dev/null
+++ b/usr/src/lib/brand/lx/testing/ltp_skiplist
@@ -0,0 +1,272 @@
+#
+# 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 2017 Joyent, Inc.
+
+# Broken tests
+poll02 # OS-3997
+
+# tests functionality not allowed in a zone
+accept4_01
+acct01
+adjtimex01
+adjtimex02
+bdflush01
+cacheflush01
+chmod03
+chmod07
+creat07
+creat08
+execve04
+fchmod02
+fchmod03
+getrusage03
+ioperm01
+ioperm02
+iopl01
+iopl02
+isofs
+mbind01
+mem01
+migrate_pages01
+migrate_pages02
+mknod01
+mmap12
+mount03
+mount05
+mount06
+modify_ldt01
+modify_ldt02
+move_pages01
+move_pages02
+move_pages03
+move_pages04
+move_pages05
+move_pages06
+move_pages07
+move_pages08
+move_pages09
+move_pages10
+move_pages11
+mremap04 # we don't yet support remapping shm
+open01
+open11
+open12 # needs a device
+ptrace04
+quotactl01
+quotactl02
+remap_file_pages01
+remap_file_pages02
+setfsuid04
+setfsuid04_16
+settimeofday01
+stime01
+swapoff02
+swapon02
+swapon03
+switch01
+ustat01
+ustat02
+
+# needs a config with a dev or mounting
+# As of February 2017, an increasing number of tests require device support
+# (often via a zeroed file mounted via loopback) to function properly.
+access04
+chmod06
+chown04
+chown04_16
+creat06
+fchmod06
+fchown04
+fchown04_16
+inotify03
+lchown03
+lchown03_16
+linkat02
+link08
+madvise06
+mkdir03
+mkdirat02
+mknod07
+mknodat02
+mmap16
+mount01
+mount02
+mount04
+rename11
+renameat01
+rmdir02
+umount01
+umount02
+umount2_01
+umount2_02
+umount2_03
+umount03
+utime06
+utimes01
+
+# These tests are broken on 32-bit (including on "real" Linux)
+preadv01
+preadv02
+pwritev01
+pwritev02
+
+# tests functionality not implemented yet
+access06
+add_key01
+add_key02
+clone02
+clone08
+clone09
+crash02
+dirtyc0w
+fallocate04 # needs SEEK_HOLE
+fallocate05 # needs to mount a filesystem
+fanotify01
+fanotify02
+fanotify03
+fanotify04
+fanotify05
+fanotify06
+fcntl06
+fcntl23
+fcntl23_64
+fcntl24
+fcntl24_64
+fcntl25
+fcntl25_64
+fcntl26
+fcntl26_64
+fcntl31
+fcntl31_64
+fcntl32
+fcntl32_64
+fcntl33
+fcntl33_64
+fork05
+fork09
+fork13
+fork14
+fsync01
+ftruncate04
+ftruncate04_64
+futex_wake04 # OS-4471
+getdents02
+getdents02_64
+get_mempolicy01
+getxattr01
+getxattr02
+getxattr03
+getxattr04
+ioctl03
+ioctl04
+ioctl05
+ioctl06
+ioctl07 # OS-6397
+keyctl01
+keyctl02
+keyctl03
+keyctl04
+keyctl05
+keyctl06
+keyctl07
+kcmp01
+kcmp02
+kcmp03
+lgetxattr01
+lgetxattr02
+llistxattr01
+llistxattr02
+llistxattr03
+madvise08 # no MADV_DONTDUMP support (yet?)
+mmap13
+mq_notify01
+mq_notify02
+mq_open01
+mq_timedreceive01
+mq_timedsend01
+mq_unlink01
+msgctl12
+msgrcv07
+msync04
+open02
+open10
+perf_event_open01
+perf_event_open02
+ppoll01
+process_vm_readv01
+process_vm_readv02
+process_vm_readv03
+process_vm_writev01
+process_vm_writev02
+prot_hsymlinks
+pty01 # OS-5437
+read02
+readahead01
+readahead02
+readdir2
+readdir21
+removexattr01
+removexattr02
+request_key01
+request_key02
+semctl01
+semop02
+sendfile02 # OS-3296
+sendfile02_64
+sendfile04
+sendfile04_64
+sendfile06
+sendfile06_64
+sendfile07
+sendfile07_64
+sendmsg01
+sendto02 # OS-5764
+setgroups04_16
+setns01
+setns02
+setpgid02
+setregid02
+setregid02_16
+setrlimit01
+setsockopt02
+setxattr01
+setxattr02
+setxattr03
+sgetmask01
+shmget05 # OS-3326
+splice02 # bad test, fails on Linux too
+ssetmask01
+sync_file_range01
+sysconf01 # OS-3305
+sysctl01
+sysctl03
+sysctl04
+sysctl05
+syslog01
+syslog02
+syslog03
+syslog04
+syslog05
+syslog06
+syslog07
+syslog08
+syslog09
+syslog10
+syslog11
+syslog12
+tee01
+tee02
+unshare01
+utimensat01 # OS-3328
+unshare02
+vmsplice01
+vmsplice02
diff --git a/usr/src/lib/brand/lx/testing/ltp_tests b/usr/src/lib/brand/lx/testing/ltp_tests
new file mode 100644
index 0000000000..86e9725638
--- /dev/null
+++ b/usr/src/lib/brand/lx/testing/ltp_tests
@@ -0,0 +1 @@
+syscalls,nptl,ipc,pipes,pty,crashme,math
diff --git a/usr/src/lib/brand/lx/testing/pts_ignorelist b/usr/src/lib/brand/lx/testing/pts_ignorelist
new file mode 100644
index 0000000000..2c4f307b55
--- /dev/null
+++ b/usr/src/lib/brand/lx/testing/pts_ignorelist
@@ -0,0 +1,366 @@
+#
+# 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 2016 Joyent, Inc.
+
+# The Open POSIX Test Suite (PTS) tests the POSIX conformance of an
+# operating system. A test may have one of six different results:
+#
+# o PASS: the test passed
+# o FAIL: the test failed
+# o UNRESOLVED: the test was unable to determine pass/fail
+# o UNSUPPORTED: this feature is not supported by the OS
+# o UNTESTED: this test is not yet implemented
+#
+# You can read more about these result codes in:
+#
+# ltp/testcases/open_posix_testsuite/Documentation/HOWTO_ResultCodes
+#
+# This file (pts_ignorelist) contains a list of tests which are known
+# not to pass on lx-brand zones, along with the expected test result.
+# These tests are grouped based on the reason for their failure,
+# described by the comment above the grouping. Ideally, any test
+# resulting in FAILED indicates a bug in lx-brand, but PTS is not
+# ideal. Many of the tests will return FAILED when they should return
+# UNRESOLVED instead. E.g., some of the MQ tests will return FAILED if
+# mq_open() fails while some will return UNRESOLVED. Unless otherwise
+# noted in the group comment, assume FAILED is a true bug (as opposed
+# to a lack of support by lx-brand).
+#
+# To run the tests and check your results against this list:
+#
+# cd ltp/testcases/open_posix_testsuite
+# make
+# make test 2>&1 | tee pts.log
+# diff <(egrep -v '^#|^$' /native/usr/lib/brand/lx/pts_ignorelist | sort) \
+# <(grep execution pts.log | tr -d : | awk '{ print $1, $3 }' | sort)
+#
+# Any delta reported by the diff is either a regression in lx-brand or
+# a change in the test code.
+
+#
+# POSIX message queues are not implemented in lx-brand. In illumos,
+# MQs are implemented completely in libc. In Linux, MQs are
+# implemented as system calls. The lx-brand code returns ENOSYS for
+# all the MQ system calls. The MQ tests are not consistent in how they
+# classify a failed mq_open() and thus the tests return a mixture of
+# UNRESOLVED and FAILED.
+#
+conformance/interfaces/fork/fork_19-1 UNRESOLVED
+conformance/interfaces/mq_close/mq_close_1-1 UNRESOLVED
+conformance/interfaces/mq_close/mq_close_2-1 UNRESOLVED
+conformance/interfaces/mq_close/mq_close_3-1 UNRESOLVED
+conformance/interfaces/mq_close/mq_close_4-1 UNRESOLVED
+conformance/interfaces/mq_getattr/mq_getattr_2-1 UNRESOLVED
+conformance/interfaces/mq_getattr/mq_getattr_2-2 UNRESOLVED
+conformance/interfaces/mq_getattr/mq_getattr_3-1 UNRESOLVED
+conformance/interfaces/mq_getattr/mq_getattr_4-1 UNRESOLVED
+conformance/interfaces/mq_notify/mq_notify_1-1 UNRESOLVED
+conformance/interfaces/mq_notify/mq_notify_2-1 UNRESOLVED
+conformance/interfaces/mq_notify/mq_notify_3-1 UNRESOLVED
+conformance/interfaces/mq_notify/mq_notify_4-1 UNRESOLVED
+conformance/interfaces/mq_notify/mq_notify_5-1 UNRESOLVED
+conformance/interfaces/mq_notify/mq_notify_8-1 FAILED
+conformance/interfaces/mq_notify/mq_notify_9-1 UNRESOLVED
+conformance/interfaces/mq_open/mq_open_1-1 FAILED
+conformance/interfaces/mq_open/mq_open_11-1 FAILED
+conformance/interfaces/mq_open/mq_open_12-1 FAILED
+conformance/interfaces/mq_open/mq_open_13-1 FAILED
+conformance/interfaces/mq_open/mq_open_15-1 UNRESOLVED
+conformance/interfaces/mq_open/mq_open_16-1 FAILED
+conformance/interfaces/mq_open/mq_open_18-1 FAILED
+conformance/interfaces/mq_open/mq_open_19-1 UNRESOLVED
+conformance/interfaces/mq_open/mq_open_2-1 UNRESOLVED
+conformance/interfaces/mq_open/mq_open_20-1 FAILED
+conformance/interfaces/mq_open/mq_open_23-1 UNRESOLVED
+conformance/interfaces/mq_open/mq_open_25-2 FAILED
+conformance/interfaces/mq_open/mq_open_27-1 FAILED
+conformance/interfaces/mq_open/mq_open_27-2 FAILED
+conformance/interfaces/mq_open/mq_open_29-1 FAILED
+conformance/interfaces/mq_open/mq_open_7-1 UNRESOLVED
+conformance/interfaces/mq_open/mq_open_7-2 UNRESOLVED
+conformance/interfaces/mq_open/mq_open_7-3 FAILED
+conformance/interfaces/mq_open/mq_open_8-1 UNRESOLVED
+conformance/interfaces/mq_open/mq_open_8-2 UNRESOLVED
+conformance/interfaces/mq_open/mq_open_9-1 UNRESOLVED
+conformance/interfaces/mq_open/mq_open_9-2 UNRESOLVED
+conformance/interfaces/mq_receive/mq_receive_1-1 FAILED
+conformance/interfaces/mq_receive/mq_receive_10-1 FAILED
+conformance/interfaces/mq_receive/mq_receive_11-1 FAILED
+conformance/interfaces/mq_receive/mq_receive_11-2 FAILED
+conformance/interfaces/mq_receive/mq_receive_12-1 FAILED
+conformance/interfaces/mq_receive/mq_receive_13-1 UNRESOLVED
+conformance/interfaces/mq_receive/mq_receive_2-1 UNRESOLVED
+conformance/interfaces/mq_receive/mq_receive_5-1 FAILED
+conformance/interfaces/mq_receive/mq_receive_7-1 UNRESOLVED
+conformance/interfaces/mq_receive/mq_receive_8-1 FAILED
+conformance/interfaces/mq_send/mq_send_1-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_10-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_11-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_11-2 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_12-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_13-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_14-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_2-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_3-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_3-2 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_4-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_4-2 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_4-3 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_5-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_5-2 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_7-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_8-1 UNRESOLVED
+conformance/interfaces/mq_send/mq_send_9-1 UNRESOLVED
+conformance/interfaces/mq_setattr/mq_setattr_1-1 UNRESOLVED
+conformance/interfaces/mq_setattr/mq_setattr_1-2 UNRESOLVED
+conformance/interfaces/mq_setattr/mq_setattr_2-1 UNRESOLVED
+conformance/interfaces/mq_setattr/mq_setattr_5-1 UNRESOLVED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_1-1 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_10-1 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_10-2 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_11-1 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_13-1 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_14-1 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_15-1 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_17-1 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_17-2 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_17-3 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_18-1 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_18-2 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_2-1 UNRESOLVED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_5-1 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_5-2 FAILED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_5-3 UNRESOLVED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_7-1 UNRESOLVED
+conformance/interfaces/mq_timedreceive/mq_timedreceive_8-1 FAILED
+conformance/interfaces/mq_timedsend/mq_timedsend_1-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_10-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_11-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_11-2 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_12-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_13-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_14-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_15-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_16-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_18-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_19-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_2-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_20-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_3-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_3-2 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_4-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_4-2 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_4-3 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_5-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_5-2 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_5-3 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_7-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_8-1 UNRESOLVED
+conformance/interfaces/mq_timedsend/mq_timedsend_9-1 UNRESOLVED
+conformance/interfaces/mq_unlink/mq_unlink_1-1 UNRESOLVED
+conformance/interfaces/mq_unlink/mq_unlink_2-1 UNRESOLVED
+conformance/interfaces/mq_unlink/mq_unlink_2-2 UNRESOLVED
+conformance/interfaces/mq_unlink/mq_unlink_7-1 FAILED
+functional/mqueues/mqueues_send_rev_1 UNRESOLVED
+functional/mqueues/mqueues_send_rev_2 UNRESOLVED
+
+#
+# Other features not implemented on Linux.
+#
+conformance/interfaces/pthread_rwlock_unlock/pthread_rwlock_unlock_4-1 UNSUPPORTED
+conformance/interfaces/pthread_rwlock_unlock/pthread_rwlock_unlock_4-2 UNSUPPORTED
+
+#
+# Part of this test is verifying specific thread scheduling and I'm
+# not sure how much I trust it. This test fails on native too.
+#
+conformance/interfaces/sched_setparam/sched_setparam_9-1 FAILED
+
+#
+# Linux doesn't support PTHREAD_SCOPE_PROCESS, see
+# pthread_attr_setscope(3).
+#
+conformance/interfaces/sched_setscheduler/sched_setscheduler_22-1 UNSUPPORTED
+conformance/interfaces/sched_setscheduler/sched_setscheduler_22-2 UNSUPPORTED
+
+#
+# These tests test specific scheduling behavior. They pass on native
+# when actually running in the RT class, but we run lx zones in the
+# FSS class and can't guarantee specific scheduling order.
+#
+conformance/interfaces/pthread_create/pthread_create_1-6 FAILED
+conformance/interfaces/sem_post/sem_post_8-1 FAILED
+
+#
+# It looks like we are missing some futex support that
+# pthread_mutex_timedlock() requires:
+#
+# futex(0x602820, FUTEX_LOCK_PI_PRIVATE, 1) = -1 ENOSYS
+#
+functional/threads/pi_test/pi_test_pitest-1 FAILED
+functional/threads/pi_test/pi_test_pitest-2 FAILED
+functional/threads/pi_test/pi_test_pitest-3 FAILED
+functional/threads/pi_test/pi_test_pitest-4 FAILED
+functional/threads/pi_test/pi_test_pitest-6 FAILED
+
+#
+# Another case where we need FUTEX_LOCK_PI, but slightly different
+# from the ones above. Glibc's implementation of pthread_mutex_lock()
+# doesn't check for ENOSYS from FUTEX_LOCK_PI and instead chugs along
+# assuming it has the mutex.
+#
+functional/threads/pi_test/pi_test_pitest-5 UNRESOLVED
+
+#
+# glibc doesn't support PTHREAD_SCOPE_PROCESS.
+#
+conformance/interfaces/sched_setparam/sched_setparam_20-1 UNSUPPORTED
+conformance/interfaces/sched_setparam/sched_setparam_21-1 UNSUPPORTED
+conformance/interfaces/sched_setparam/sched_setparam_21-2 UNSUPPORTED
+conformance/interfaces/sched_setscheduler/sched_setscheduler_15-1 UNSUPPORTED
+conformance/interfaces/sched_setscheduler/sched_setscheduler_15-2 UNSUPPORTED
+
+#
+# _POSIX_SPORADIC_SERVER is not available on Linux.
+#
+conformance/interfaces/sched_get_priority_max/sched_get_priority_max_1-3 UNSUPPORTED
+conformance/interfaces/sched_get_priority_min/sched_get_priority_min_1-3 UNSUPPORTED
+conformance/interfaces/sched_setparam/sched_setparam_23-2 UNSUPPORTED
+conformance/interfaces/sched_setparam/sched_setparam_23-3 UNSUPPORTED
+conformance/interfaces/sched_setparam/sched_setparam_23-4 UNSUPPORTED
+conformance/interfaces/sched_setparam/sched_setparam_23-5 UNSUPPORTED
+conformance/interfaces/sched_setparam/sched_setparam_25-2 UNSUPPORTED
+conformance/interfaces/sched_setparam/sched_setparam_25-3 UNSUPPORTED
+conformance/interfaces/sched_setparam/sched_setparam_25-4 UNSUPPORTED
+conformance/interfaces/sched_setscheduler/sched_setscheduler_17-2 UNSUPPORTED
+conformance/interfaces/sched_setscheduler/sched_setscheduler_17-3 UNSUPPORTED
+conformance/interfaces/sched_setscheduler/sched_setscheduler_17-4 UNSUPPORTED
+conformance/interfaces/sched_setscheduler/sched_setscheduler_19-2 UNSUPPORTED
+conformance/interfaces/sched_setscheduler/sched_setscheduler_19-3 UNSUPPORTED
+conformance/interfaces/sched_setscheduler/sched_setscheduler_19-4 UNSUPPORTED
+
+#
+# No semaphore limit (_SC_SEM_NSEMS_MAX).
+#
+conformance/interfaces/sem_init/sem_init_7-1 UNTESTED
+
+#
+# CLOCK_PROCESS_CPUTIME_ID/CLOCK_THREAD_CPUTIME_ID not supported.
+#
+conformance/interfaces/fork/fork_22-1 UNRESOLVED
+conformance/interfaces/timer_create/timer_create_10-1 UNRESOLVED
+
+#
+# Multiple instances of the same signal are coalesced on sigwait().
+#
+conformance/interfaces/sigwait/sigwait_2-1 FAILED
+
+#
+# Need sys_time privs.
+#
+# clock_settime_20-1 returns FAILED because it expects EINVAL but gets EPERM.
+#
+conformance/interfaces/clock_settime/clock_settime_1-1 UNRESOLVED
+conformance/interfaces/clock_settime/clock_settime_4-1 UNRESOLVED
+conformance/interfaces/clock_settime/clock_settime_4-2 UNRESOLVED
+conformance/interfaces/clock_settime/clock_settime_5-1 UNRESOLVED
+conformance/interfaces/clock_settime/clock_settime_5-2 UNRESOLVED
+conformance/interfaces/clock_settime/clock_settime_7-1 UNRESOLVED
+conformance/interfaces/clock_settime/clock_settime_7-2 UNRESOLVED
+conformance/interfaces/clock_settime/clock_settime_8-1 UNRESOLVED
+conformance/interfaces/clock_settime/clock_settime_20-1 FAILED
+
+#
+# https://github.com/joyent/illumos-joyent/issues/66
+#
+conformance/interfaces/clock_getcpuclockid/clock_getcpuclockid_1-1 FAILED
+conformance/interfaces/clock_getcpuclockid/clock_getcpuclockid_1-2 UNRESOLVED
+conformance/interfaces/clock_getcpuclockid/clock_getcpuclockid_2-1 FAILED
+conformance/interfaces/clock_getcpuclockid/clock_getcpuclockid_5-1 UNRESOLVED
+
+#
+# Various AIO functions don't EINVAL on bogus struct aiocb.
+#
+# Faulty on lx brand, Linux on KVM, and illumos.
+#
+conformance/interfaces/aio_error/aio_error_3-1 UNTESTED
+conformance/interfaces/aio_return/aio_return_4-1 UNTESTED
+
+#
+# _SC_AIO_MAX == -1
+#
+conformance/interfaces/aio_read/aio_read_9-1 UNSUPPORTED
+conformance/interfaces/aio_write/aio_write_7-1 UNSUPPORTED
+
+#
+# PTS is a wasteland, these tests hard-coded to fail.
+#
+conformance/interfaces/aio_suspend/aio_suspend_5-1 UNSUPPORTED
+
+#
+# Tests that fail on lx-brand and Linux but pass illumos.
+#
+conformance/interfaces/aio_return/aio_return_2-1 UNTESTED
+conformance/interfaces/aio_return/aio_return_3-2 UNTESTED
+conformance/interfaces/pthread_rwlock_rdlock/pthread_rwlock_rdlock_2-1 FAILED
+conformance/interfaces/pthread_rwlock_rdlock/pthread_rwlock_rdlock_2-2 FAILED
+
+#
+# Tests that fail everywhere: lx-brand, Linux, and illumos.
+#
+conformance/interfaces/pthread_rwlock_unlock/pthread_rwlock_unlock_3-1 FAILED
+conformance/interfaces/sched_getparam/sched_getparam_6-1 UNTESTED
+conformance/interfaces/sched_getscheduler/sched_getscheduler_7-1 UNTESTED
+
+#
+# mmap() is supposed to return ENODEV for pipe but returns ENOSYS.
+# This is an illumos bug.
+#
+conformance/interfaces/mmap/mmap_23-1 FAILED
+
+#
+# I'm not sure.
+#
+conformance/interfaces/mmap/mmap_18-1 FAILED
+
+#
+# According to the spec "Memory access within the mapping but beyond
+# the current end of the underlying objects __may__ result in SIGBUS
+# signals being sent to the process". So I don't think it breaks POSIX
+# but it surely doesn't behave like Linux.
+#
+conformance/interfaces/mmap/mmap_11-2 FAILED
+
+#
+# The test uses /proc/mounts to check for noatime but the lx-brand
+# does not indicate noatime even if the underlying zfs fs has atime
+# disabled.
+#
+conformance/interfaces/mmap/mmap_13-1 FAILED
+
+#
+# Doesn't run on 64bit.
+#
+conformance/interfaces/mmap/mmap_31-1 UNSUPPORTED
+
+#
+# Bytes written past the end of the mmap object (but still in page)
+# are not written to disk but they do appear to persist in the page
+# when mmap'd by another pid. Perhaps this test is really testing an
+# implementation detail of Linux's VM system.
+#
+conformance/interfaces/mmap/mmap_11-4 FAILED
+
+#
+# lx-brand doesn't implement clock_getcpudclockid(pid,...).
+#
+conformance/interfaces/pthread_condattr_setclock/pthread_condattr_setclock_1-3 FAILED
diff --git a/usr/src/lib/brand/lx/zone/Makefile b/usr/src/lib/brand/lx/zone/Makefile
new file mode 100644
index 0000000000..f5ce1579c7
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/Makefile
@@ -0,0 +1,72 @@
+#
+# 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 2020 Joyent, Inc.
+# Copyright 2017 ASS-Einrichtungssysteme GmbH, Inc. All rights reserved.
+#
+
+PROGS = lx_boot lx_install lx_uninstall prestate poststate statechange
+PROGS += lx_boot_zone_redhat lx_boot_zone_ubuntu lx_boot_zone_debian
+PROGS += lx_boot_zone_busybox lx_boot_zone_suse lx_boot_zone_docker
+PROGS += lx_boot_zone_void
+XMLDOCS = config.xml platform.xml
+TEMPLATES = SUNWlx.xml SUNWlx26.xml
+
+all: $(PROGS)
+
+include $(SRC)/cmd/Makefile.cmd
+include ../Makefile.lx
+
+all := TARGET= all
+install := TARGET= install
+clobber := TARGET= clobber
+
+POFILES= $(PROGS:%=%.po)
+POFILE= lx_zone.po
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(BUILDPO.pofiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+install: $(PROGS) $(ROOTXMLDOCS) $(ROOTTEMPLATES) $(ROOTPROGS)
+ mkdir -p $(ROOT)/usr/lib/brand/lx/ld/64
+ crle -c $(ROOT)/usr/lib/brand/lx/ld/ld.config \
+ -l /native/lib:/native/usr/lib \
+ -s /native/lib/secure:/native/usr/lib/secure
+ crle -64 -c $(ROOT)/usr/lib/brand/lx/ld/64/ld.config \
+ -l /native/lib/64:/native/usr/lib/64 \
+ -s /native/lib/secure/64:/native/usr/lib/secure/64
+
+lint:
+
+clean:
+ -$(RM) $(PROGS)
+
+clobber: clean
+ -$(RM) $(ROOTXMLDOCS) $(ROOTPROGS) $(ROOTTEMPLATES)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
diff --git a/usr/src/lib/brand/lx/zone/SUNWlx.xml b/usr/src/lib/brand/lx/zone/SUNWlx.xml
new file mode 100644
index 0000000000..04c38873de
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/SUNWlx.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+
+<!--
+ Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ DO NOT EDIT THIS FILE. Use zonecfg(1M) instead.
+-->
+
+<!DOCTYPE zone PUBLIC "-//Sun Microsystems Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1">
+
+<zone name="default" zonepath="" autoboot="false" brand="lx">
+</zone>
diff --git a/usr/src/lib/brand/lx/zone/SUNWlx26.xml b/usr/src/lib/brand/lx/zone/SUNWlx26.xml
new file mode 100644
index 0000000000..9bd8af4d92
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/SUNWlx26.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+
+<!--
+ Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ 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
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ DO NOT EDIT THIS FILE. Use zonecfg(1M) instead.
+-->
+
+<!DOCTYPE zone PUBLIC "-//Sun Microsystems Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1">
+
+<zone name="default" zonepath="" autoboot="false" brand="lx">
+ <attr name="kernel-version" type="string" value="2.6"/>
+</zone>
diff --git a/usr/src/lib/brand/lx/zone/config.xml b/usr/src/lib/brand/lx/zone/config.xml
new file mode 100644
index 0000000000..81322a0f2f
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/config.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright 2020 Joyent, Inc.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE brand PUBLIC "-//Sun Microsystems Inc//DTD Brands//EN"
+ "file:///usr/share/lib/xml/dtd/brand.dtd.1">
+
+<brand name="lx">
+ <modname>lx_brand</modname>
+
+ <initname>/native/usr/lib/brand/lx/lxinit</initname>
+ <login_cmd>/bin/login -h zone:%Z %u</login_cmd>
+ <forcedlogin_cmd>/bin/login -h zone:%Z -f %u</forcedlogin_cmd>
+ <user_cmd>/usr/bin/getent passwd %u</user_cmd>
+
+ <install>/usr/lib/brand/lx/lx_install -z %z -R %R</install>
+ <installopts>R:t:U:q:z:</installopts>
+ <boot>/usr/lib/brand/lx/lx_boot %z %R</boot>
+ <halt>/usr/lib/brand/lx/lx_support halt %R %z</halt>
+ <verify_cfg>/usr/lib/brand/lx/lx_support verify</verify_cfg>
+ <verify_adm></verify_adm>
+ <postclone></postclone>
+ <postinstall></postinstall>
+ <uninstall>/usr/lib/brand/lx/lx_uninstall -z %z -R %R</uninstall>
+ <prestatechange>/usr/lib/brand/lx/prestate %z %R</prestatechange>
+ <poststatechange>/usr/lib/brand/lx/poststate %z %R</poststatechange>
+
+ <privilege set="default" name="contract_event" />
+ <privilege set="default" name="contract_identity" />
+ <privilege set="default" name="contract_observer" />
+ <privilege set="default" name="dtrace_proc" />
+ <privilege set="default" name="dtrace_user" />
+ <privilege set="default" name="file_chown" />
+ <privilege set="default" name="file_chown_self" />
+ <privilege set="default" name="file_dac_execute" />
+ <privilege set="default" name="file_dac_read" />
+ <privilege set="default" name="file_dac_search" />
+ <privilege set="default" name="file_dac_write" />
+ <privilege set="default" name="file_owner" />
+ <privilege set="default" name="file_setid" />
+ <privilege set="default" name="ipc_dac_read" />
+ <privilege set="default" name="ipc_dac_write" />
+ <privilege set="default" name="ipc_owner" />
+ <privilege set="default" name="net_bindmlp" />
+ <privilege set="default" name="net_icmpaccess" />
+ <privilege set="default" name="net_mac_aware" />
+ <privilege set="default" name="net_privaddr" />
+ <privilege set="default" name="net_rawaccess" ip-type="exclusive" />
+ <privilege set="default" name="proc_chroot" />
+ <privilege set="default" name="sys_audit" />
+ <privilege set="default" name="proc_audit" />
+ <privilege set="default" name="proc_lock_memory" />
+ <privilege set="default" name="proc_owner" />
+ <privilege set="default" name="proc_secflags" />
+ <privilege set="default" name="proc_setid" />
+ <privilege set="default" name="proc_prioup" />
+ <privilege set="default" name="proc_taskid" />
+ <privilege set="default" name="sys_acct" />
+ <privilege set="default" name="sys_admin" />
+ <privilege set="default" name="sys_fs_import" />
+ <privilege set="default" name="sys_ip_config" ip-type="exclusive" />
+ <privilege set="default" name="sys_iptun_config" ip-type="exclusive" />
+ <privilege set="default" name="sys_mount" />
+ <privilege set="default" name="sys_nfs" />
+ <privilege set="default" name="sys_smb" />
+ <privilege set="default" name="sys_resource" />
+ <privilege set="default" name="sys_ppp_config" ip-type="exclusive" />
+
+ <privilege set="prohibited" name="dtrace_kernel" />
+ <privilege set="prohibited" name="proc_zone" />
+ <privilege set="prohibited" name="sys_config" />
+ <privilege set="prohibited" name="sys_devices" />
+ <privilege set="prohibited" name="sys_ip_config" ip-type="shared" />
+ <privilege set="prohibited" name="sys_linkdir" />
+ <privilege set="prohibited" name="sys_net_config" />
+ <privilege set="prohibited" name="sys_ppp_config" ip-type="shared" />
+ <privilege set="prohibited" name="sys_res_config" />
+ <privilege set="prohibited" name="sys_suser_compat" />
+ <privilege set="prohibited" name="xvm_control" />
+ <privilege set="prohibited" name="virt_manage" />
+
+ <privilege set="required" name="proc_exec" />
+ <privilege set="required" name="proc_fork" />
+ <privilege set="required" name="sys_ip_config" ip-type="exclusive" />
+ <privilege set="required" name="sys_mount" />
+</brand>
diff --git a/usr/src/lib/brand/lx/zone/lx_boot.ksh b/usr/src/lib/brand/lx/zone/lx_boot.ksh
new file mode 100644
index 0000000000..e2eb523a3d
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_boot.ksh
@@ -0,0 +1,106 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2017 ASS-Einrichtungssysteme GmbH, Inc.
+# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2020 Joyent, Inc.
+#
+# lx boot script.
+#
+# The arguments to this script are the zone name and the zonepath.
+#
+
+. /usr/lib/brand/shared/common.ksh
+
+ZONENAME=$1
+ZONEPATH=$2
+ZONEROOT=$ZONEPATH/root
+
+w_missing=$(gettext "Warning: \"%s\" is not installed in the global zone")
+
+arch=`uname -p`
+if [ "$arch" = "i386" ]; then
+ ARCH32=i86
+ ARCH64=amd64
+else
+ echo "Unsupported architecture: $arch"
+ exit 2
+fi
+
+#
+# Run the lx_support boot hook.
+#
+/usr/lib/brand/lx/lx_support boot $ZONEPATH $ZONENAME
+if (( $? != 0 )) ; then
+ exit 1
+fi
+
+BRANDDIR=/native/usr/lib/brand/lx;
+EXIT_CODE=1
+
+#
+# Before we boot we validate and fix, if necessary, the required files within
+# the zone. These modifications can be lost if a patch or upgrade is applied
+# within the zone, so we validate and fix the zone every time it boots.
+#
+
+#
+# Determine the distro.
+#
+distro=""
+if [[ $(zonecfg -z $ZONENAME info attr name=docker) =~ "value: true" ]]; then
+ distro="docker"
+elif [[ -f $ZONEROOT/etc/redhat-release ]]; then
+ distro="redhat"
+elif [[ -f $ZONEROOT/etc/lsb-release ]]; then
+ if egrep -s Ubuntu $ZONEROOT/etc/lsb-release; then
+ distro="ubuntu"
+ elif [[ -f $ZONEROOT/etc/debian_version ]]; then
+ distro="debian"
+ fi
+elif [[ -f $ZONEROOT/etc/debian_version ]]; then
+ distro="debian"
+elif [[ -f $ZONEROOT/etc/alpine-release ]]; then
+ distro="busybox"
+elif [[ -f $ZONEROOT/etc/SuSE-release ]]; then
+ distro="suse"
+elif [[ -f $ZONEROOT/etc/void-release ]]; then
+ distro="void"
+fi
+
+[[ -z $distro ]] && fatal "Unsupported distribution!"
+
+#
+# Retrieve an attribute from the zone configuration
+#
+zone_attr() {
+ zonecfg -z "$ZONENAME" info attr "name=$1" \
+ | nawk '$1 == "value:" {print $2}'
+}
+
+#
+# Perform distro-specific customization.
+#
+. $(dirname $0)/lx_boot_zone_${distro}
+
+exit 0
diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_busybox.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_busybox.ksh
new file mode 100644
index 0000000000..702ac782a1
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_busybox.ksh
@@ -0,0 +1,182 @@
+#!/bin/ksh -p
+#
+# 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.
+#
+
+#
+# Customisation for busybox-init-based distributions. Assumes to have been
+# sourced from lx_boot.
+#
+
+tmpfile=/tmp/lx-busybox.$$
+
+# Check that the directories we're writing to aren't symlinks outside the zone
+safe_dir /etc
+safe_dir /etc/init.d
+
+# Generate network setup script
+#
+cat > $tmpfile <<EOF
+#!/sbin/runscript
+depend() {
+ need localmount
+ after bootmisc hwdrivers modules
+ provide net
+ keyword nojail noprefix novserver
+}
+start() {
+EOF
+# Only alter resolv.conf if we're getting info from zonecfg(1M).
+zonecfg -z $ZONENAME info attr name=resolvers | grep -q resolvers
+if [[ $? == 0 ]]; then
+ cat > $tmpfile <<EOF
+ if [ ! -e /etc/resolv.conf ]; then
+ echo "# AUTOMATIC ZONE CONFIG" > /etc/resolv.conf
+EOF
+ zonecfg -z $ZONENAME info attr name=resolvers |
+ awk '
+ {
+ if ($1 == "value:") {
+ nres = split($2, resolvers, ",")
+ }
+ }
+ END {
+ for (i = 1; i <= nres; i++) {
+ printf(" echo \"nameserver %s\" >> %s\n", resolvers[i],
+ "/etc/resolv.conf")
+ }
+ }
+ ' >> $tmpfile
+ zonecfg -z $ZONENAME info attr name=dns-domain |
+ awk '
+ {
+ if ($1 == "value:") {
+ dom = $2
+ }
+ }
+ END {
+ printf(" echo \"search %s\" >> %s\n", dom, "/etc/resolv.conf")
+ }
+ ' >> $tmpfile
+ cat >> $tmpfile <<EOF
+ fi
+ return 0
+ }
+EOF
+else
+ cat >> $tmpfile <<EOF
+ return 0
+ }
+EOF
+fi
+
+cat >> $tmpfile <<EOF
+ stop() {
+ return 0
+ }
+EOF
+fnm=$ZONEROOT/etc/init.d/networking
+if [[ -f $fnm || -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+ chmod 755 $fnm
+fi
+
+
+#
+# The default /etc/inittab might spawn mingetty on each of the virtual consoles
+# as well as xdm on the X console. Since we don't have virtual consoles nor
+# an X console, spawn a single mingetty on /dev/console instead.
+#
+# Don't bother changing the file if it looks like we already did.
+#
+fnm=$ZONEROOT/etc/inittab
+if ! egrep -s "Modified by lx brand" $fnm; then
+ sed 's/^tty[1-6]:/# Disabled by lx brand: &/' \
+ $fnm > $tmpfile
+ echo "console::respawn:/sbin/getty 38400 console" >> $tmpfile
+ echo "# Modified by lx brand" >> $tmpfile
+
+ if [[ ! -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+ chmod 644 $fnm
+ fi
+fi
+
+#
+# The following scripts attempt to start services or otherwise configure the
+# system in ways incompatible with zones, so replace them with stubs.
+#
+
+fnm=$ZONEROOT/etc/init.d/fsck
+[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm
+#!/sbin/runscript
+
+depend() {
+ use dev clock modules
+}
+
+start() {
+ return 0
+}
+DONE
+
+fnm=$ZONEROOT/etc/init.d/hwclock
+[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm
+#!/sbin/runscript
+
+depend() {
+ provide clock
+}
+
+start() {
+ return 0
+}
+DONE
+
+fnm=$ZONEROOT/etc/init.d/klogd
+[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm
+#!/sbin/runscript
+
+depend() {
+ need clock hostname localmount
+ before net
+}
+
+start() {
+ return 0
+}
+DONE
+
+fnm=$ZONEROOT/etc/init.d/sysfs
+[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm
+#!/sbin/runscript
+
+depend() {
+}
+
+start() {
+ return 0
+}
+DONE
+
+#
+# Setup for the /dev/shm mount.
+#
+fnm=$ZONEROOT/etc/fstab
+entry=$(awk '{if ($2 == "/dev/shm") print $2}' $fnm)
+if [[ -z "$entry" && ! -h $fnm ]]; then
+ echo "swapfs /dev/shm tmpfs defaults 0 0" >> $fnm
+fi
+
+# Hand control back to lx_boot
diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_debian.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_debian.ksh
new file mode 100644
index 0000000000..86b5bffbe5
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_debian.ksh
@@ -0,0 +1,175 @@
+#!/bin/ksh -p
+#
+# 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.
+#
+
+#
+# Customisation for Debian-based distributions. Assumes to have been
+# sourced from lx_boot.
+#
+
+tmpfile=/tmp/lx-debian.$$
+
+
+# Check that the directories we're writing to aren't symlinks outside the zone
+safe_dir /etc
+safe_dir /etc/init.d
+safe_dir /etc/network
+safe_dir /etc/rc0.d
+safe_dir /etc/rc1.d
+safe_dir /etc/rc2.d
+safe_dir /etc/rc3.d
+safe_dir /etc/rc4.d
+safe_dir /etc/rc5.d
+safe_dir /etc/rc6.d
+safe_dir /etc/rcS.d
+safe_opt_dir /etc/selinux
+
+# Populate resolv.conf setup files IF we have resolvers information.
+zonecfg -z $ZONENAME info attr name=resolvers | grep -q resolvers
+if [[ $? == 0 ]]; then
+ zonecfg -z $ZONENAME info attr name=resolvers | awk '
+ BEGIN {
+ print("# AUTOMATIC ZONE CONFIG")
+ }
+ $1 == "value:" {
+ nres = split($2, resolvers, ",");
+ for (i = 1; i <= nres; i++) {
+ print("nameserver", resolvers[i]);
+ }
+ }
+ ' > $tmpfile
+ zonecfg -z $ZONENAME info attr name=dns-domain | awk '
+ $1 == "value:" {
+ dom = $2
+ }
+ END {
+ print("search", dom);
+ }
+ ' >> $tmpfile
+ fnm=$ZONEROOT/etc/resolv.conf
+ if [[ -f $fnm || -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+ fi
+fi
+
+# Override network configuration
+zonecfg -z $ZONENAME info net | awk '
+BEGIN {
+ print("# AUTOMATIC ZONE CONFIG")
+ print("iface lo inet manual");
+}
+$1 == "physical:" {
+ print("iface", $2, "inet manual");
+}
+' > $tmpfile
+fnm=$ZONEROOT/etc/network/interfaces
+if [[ -f $fnm || -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+fi
+
+#
+# The default /etc/inittab might spawn mingetty on each of the virtual consoles
+# as well as xdm on the X console. Since we don't have virtual consoles nor
+# an X console, spawn a single mingetty on /dev/console instead.
+#
+# Don't bother changing the file if it looks like we already did.
+#
+fnm=$ZONEROOT/etc/inittab
+if ! egrep -s "Modified by lx brand" $fnm; then
+ sed 's/^[1-6]:/# Disabled by lx brand: &/' \
+ $fnm > $tmpfile
+ echo "1:2345:respawn:/sbin/getty 38400 console" >> $tmpfile
+ echo "# Modified by lx brand" >> $tmpfile
+
+ if [[ ! -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+ chmod 644 $fnm
+ fi
+fi
+
+# The Debian init uses a combination of traditional rc-style service
+# definitions and upstart-style definitions.
+
+#
+# The following rc-style scripts attempt to start services or otherwise
+# configure the system in ways incompatible with zones, so don't execute them
+# at boot time.
+#
+unsupported_rc_services="
+ checkfs.sh
+ checkroot.sh
+ hwclock.sh
+ kmod
+ mtab.sh
+ procps
+ udev
+ udev-mtab
+"
+
+for file in $unsupported_rc_services; do
+ rm -f $ZONEROOT/etc/init.d/$file
+
+ rc_files="$(echo $ZONEROOT/etc/rc[0-6S].d/[SK]+([0-9])$file)"
+
+ if [[ "$rc_files" != \
+ "$ZONEROOT/etc/rc[0-6S].d/[SK]+([0-9])$file" ]]; then
+ for file in $rc_files; do
+ rm -f "$file"
+ done
+ fi
+done
+
+disable_svc()
+{
+ fnm=$ZONEROOT/etc/init/$1.override
+ [[ -h $fnm || -f $fnm ]] && return
+ echo "manual" > $fnm
+}
+
+
+#
+# Now customize upstart
+#
+
+RMSVCS="
+ network-interface-security
+ udev
+ udevmonitor
+ udevtrigger
+ udev-fallback-graphics
+ udev-finish
+"
+for f in $RMSVCS
+do
+ disable_svc $f
+done
+
+#
+# We need to setup for the /dev/shm mount. Unlike some other distros, Debian
+# can handle it as either /dev/shm or /run/shm. For simplicity we create an
+# fstab entry to force it into the /dev/shm style.
+#
+fnm=$ZONEROOT/etc/fstab
+entry=$(awk '{if ($2 == "/dev/shm") print $2}' $fnm)
+if [[ -z "$entry" && ! -h $fnm ]]; then
+ echo "swapfs /dev/shm tmpfs defaults 0 0" >> $fnm
+fi
+
+#
+# upstart modifications are complete
+#
+rm -f $tmpfile
+
+# Hand control back to lx_boot
diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_docker.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_docker.ksh
new file mode 100755
index 0000000000..9d332a723d
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_docker.ksh
@@ -0,0 +1,17 @@
+#!/bin/ksh -p
+#
+# 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 2014 Joyent, Inc. All rights reserved.
+#
+
+# Hand control back to lx_boot
diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh
new file mode 100644
index 0000000000..19b54b23b3
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh
@@ -0,0 +1,357 @@
+#!/bin/ksh -p
+#
+# 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 2021 Joyent, Inc.
+# Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
+#
+
+#
+# Since CentOS, Red Hat Enterprise Linux, and Fedora all use approximately
+# the same source, this file should be good for all three.
+#
+# Currently, this file assumed a pre-systemd existence, so this should be
+# CentOS 6.x or earlier. Testing has been done on CentOS 6.6.
+#
+
+tmpfile=/tmp/lx-redhat.$$
+
+
+# Before doing anything else, make sure some Centos-specific dirs are safe.
+# /etc/init.d is normally a symlink so we can't easily tell if it's safe so
+# check rc.d/init.d instead.
+
+safe_dir /etc/sysconfig
+safe_dir /etc/rc.d
+safe_dir /etc/rc.d/init.d
+safe_dir /etc/rc.d/rc0.d
+safe_dir /etc/rc.d/rc1.d
+safe_dir /etc/rc.d/rc2.d
+safe_dir /etc/rc.d/rc3.d
+safe_dir /etc/rc.d/rc4.d
+safe_dir /etc/rc.d/rc5.d
+safe_dir /etc/rc.d/rc6.d
+safe_opt_dir /etc/selinux
+
+# Generate the /etc/rc.d/init.d/network rc script
+cat > $tmpfile <<EOF
+#!/bin/bash
+# network Bring up/down networking
+#
+### BEGIN INIT INFO
+# Provides: \$network
+# Short-Description: Bring up/down networking
+# Description: Bring up/down networking
+### END INIT INFO
+
+case "\$1" in
+ start)
+ [ "\$EUID" != "0" ] && exit 4
+
+EOF
+
+# Populate resolv.conf setup files IFF we have resolvers information.
+resolvers=$(zone_attr resolvers)
+if [ -n "$resolvers" ]; then
+
+ echo "# AUTOMATIC ZONE CONFIG" > $tmpfile
+ _IFS=$IFS; IFS=,; for r in $resolvers; do
+ echo "nameserver $r"
+ done >> $tmpfile
+ IFS=$_IFS
+ domain=$(zone_attr dns-domain)
+ [ -n "$domain" ] && echo "search $domain" >> $tmpfile
+
+ if [ -f $ZONEROOT/etc/systemd/resolved.conf ]; then
+ cf=$ZONEROOT/etc/systemd/resolved.conf
+ sed -i -E '/^(DNS|Domains) *=/d' $cf
+ echo "DNS=$resolvers" >> $cf
+ [[ -n "$domain" ]] && echo "Domains=$domain" >> $cf
+ mv -f $tmpfile $ZONEROOT/etc/resolv.conf
+ else
+ fnm=$ZONEROOT/etc/resolv.conf
+ if [[ -f $fnm || -h $fnm || ! -e $fnm ]]; then
+ mv -f $tmpfile $fnm
+ fi
+ fi
+fi
+
+cat >> $tmpfile <<EOF
+ touch /var/lock/subsys/network
+ rc=0
+ ;;
+ stop)
+ [ "\$EUID" != "0" ] && exit 4
+
+ rm -f /var/lock/subsys/network
+ rc=0
+ ;;
+ status)
+ echo "Configured devices:"
+ echo "lo \$(cd /dev/net; ls)"
+ echo "Currently active devices:"
+ echo \$(/sbin/ip -o link show up | awk -F ": " '{ print \$2 }')
+ rc=0
+ ;;
+ restart|reload|force-reload)
+ cd "\$CWD"
+ \$0 stop
+ \$0 start
+ rc=\$?
+ ;;
+ *)
+ echo "Usage: \$0 {start|stop|status|restart|reload|force-reload}"
+ exit 2
+esac
+
+exit \$rc
+EOF
+fnm=$ZONEROOT/etc/rc.d/init.d/network
+if [[ -f $fnm || -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+ chmod 755 $fnm
+fi
+
+# This is specific to a systemd-based image
+sysdir=$ZONEROOT/etc/systemd/system
+if [[ -d $ZONEROOT/etc && ! -h $ZONEROOT/etc && -d $ZONEROOT/etc/systemd &&
+ ! -h $ZONEROOT/etc/systemd && -d $sysdir && ! -h $sysdir ]]; then
+ # don't use NetworkManager
+ rm -f $sysdir/dbus-org.freedesktop.nm-dispatcher.service
+ rm -f $sysdir/multi-user.target.wants/NetworkManager.service
+ rm -f $sysdir/dbus-org.freedesktop.NetworkManager.service
+ # our network setup needs to run
+ fnm=$sysdir/multi-user.target.wants/network.service
+ if [[ ! -f $fnm ]]; then
+ ln -s /etc/rc.d/init.d/network \
+ $sysdir/multi-user.target.wants/network.service
+ fi
+fi
+
+#
+# The default /etc/inittab only sets the runlevel. Make sure it's runlevel 3
+# and not runlevel 5 (X11).
+# Don't bother changing the file if it looks like we already did.
+#
+fnm=$ZONEROOT/etc/inittab
+if ! egrep -s "Modified by lx brand" $fnm; then
+ sed 's/^id:5:initdefault:/id:3:initdefault: &/' \
+ $fnm > $tmpfile
+ echo "# Modified by lx brand" >> $tmpfile
+
+ if [[ ! -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+ chmod 644 $fnm
+ fi
+fi
+
+#
+# Ensure svcs depending on $network will start.
+#
+fnm=$ZONEROOT/etc/sysconfig/network
+if ! egrep -s "NETWORKING=yes" $fnm; then
+ cfghnm=$(zonecfg -z $ZONENAME info attr name=hostname | \
+ awk '{if ($1 == "value:") print $2}')
+ if [[ -z "$cfghnm" ]]; then
+ cfghnm=$ZONENAME
+ fi
+ if [[ ! -h $fnm ]]; then
+ cat > $fnm <<- EOF
+ NETWORKING=yes
+ HOSTNAME=$cfghnm
+EOF
+ fi
+fi
+
+#
+# SELinux must be disabled otherwise we won't get past init.
+#
+fnm=$ZONEROOT/etc/selinux/config
+if egrep -s "^SELINUX=enforcing|^SELINUX=permissive" $fnm; then
+ tmpfile=/tmp/selinux_config.$$
+
+ sed 's/^SELINUX=.*$/SELINUX=disabled/' $fnm > $tmpfile
+ if [[ ! -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+ chmod 644 $fnm
+ fi
+fi
+
+#
+# /etc/rc.d/init.d/keytable tries to load a physical keyboard map, which won't
+# work in a zone. If we remove etc/sysconfig/keyboard, it won't try this at all.
+#
+fnm=$ZONEROOT/etc/sysconfig/keyboard
+if [[ ! -h $fnm ]]; then
+ rm -f $ZONEROOT/etc/sysconfig/keyboard
+fi
+
+# The Centos init uses a combination of traditional rc-style service
+# definitions and upstart-style definitions.
+
+#
+# The following rc-style scripts attempt to start services or otherwise
+# configure the system in ways incompatible with zones, so don't execute them
+# at boot time.
+#
+unsupported_rc_services="
+ acpid
+ auditd
+ gpm
+ hpoj
+ ip6tables
+ iptables
+ irda
+ irqbalance
+ iscsi
+ isdn
+ kdump
+ kudzu
+ mdmpd
+ mdmonitor
+ microcode_ctl
+ netdump
+ ntpd
+ ntpdate
+ pcmcia
+ psacct
+ quota_nld
+ random
+ rawdevices
+ smartd
+"
+
+for file in $unsupported_rc_services; do
+ rm -f $ZONEROOT/etc/rc.d/init.d/$file
+
+ rc_files="$(echo $ZONEROOT/etc/rc.d/rc[0-6].d/[SK]+([0-9])$file)"
+
+ if [[ "$rc_files" != \
+ "$ZONEROOT/etc/rc.d/rc[0-6].d/[SK]+([0-9])$file" ]]; then
+ for file in $rc_files; do
+ rm -f "$file"
+ done
+ fi
+done
+
+disable_svc()
+{
+ # XXX - TBD does this work like on Ubuntu?
+ #
+ fnm=$ZONEROOT/etc/init/$1.override
+ [[ -h $fnm || -f $fnm ]] && return
+ echo "manual" > $fnm
+
+ # fnm=$ZONEROOT/etc/init/$1.conf
+ # rm -f $fnm
+}
+
+RMSVCS="control-alt-delete
+ ttyS0"
+
+#
+# Now customize upstart services
+#
+
+for f in $RMSVCS
+do
+ disable_svc $f
+done
+
+if [[ ! -f $ZONEROOT/etc/init/tty.override ]]; then
+ cat > $ZONEROOT/etc/init/tty.override <<- EOF
+ # tty - getty
+ #
+ # This service maintains a getty on the console.
+
+ stop on runlevel [S016]
+
+ respawn
+ instance console
+ exec /sbin/mingetty console
+EOF
+fi
+
+if [[ ! -f $ZONEROOT/etc/init/start-ttys.override ]]; then
+ cat > $ZONEROOT/etc/init/start-ttys.override <<- EOF
+ # This service starts the configured number of gettys.
+ #
+
+ start on stopped rc RUNLEVEL=[2345]
+
+ task
+ script
+ initctl start tty
+ end script
+EOF
+fi
+
+#
+# There is a lot of stuff in the standard halt and reboot scripts that we
+# have no business running in a zone. Fortunately, the stuff we want to
+# skip is all in one contiguous chunk.
+#
+# Don't bother to modify the file if it looks like we already did.
+#
+fnm=$ZONEROOT/etc/rc.d/init.d/halt
+if ! egrep -s "Disabled by lx brand" $fnm; then
+ awk 'BEGIN {skip = ""}
+ /^# Save mixer/ {skip = "# Disabled by lx brand: "}
+ /halt.local/ {skip = ""}
+ /./ {print skip $0}' $fnm > /tmp/halt.$$
+
+ if [[ $? -eq 0 && ! -h $fnm ]]; then
+ mv -f /tmp/halt.$$ $fnm
+ chmod 755 $fnm
+ fi
+fi
+
+#
+# Fix up /etc/rc.d/rc.sysinit:
+#
+# 1) /sbin/hwclock requires the iopl() system call, which BrandZ won't support.
+# Since the hardware clock cannot be set from within a zone, we comment out
+# the line.
+#
+# 2) Disable dmesg commands, since we don't implement klogctl
+#
+# 3) Disable initlog and the mount of /dev/pts
+#
+# 4) Don't touch /dev/tty* in order to start virtual terminals, as that won't
+# work from within a zone.
+#
+# 5) Don't try to check the root filesystem (/) as there is no associated
+# physical device, and any attempt to run fsck will fail.
+#
+fnm=$ZONEROOT/etc/rc.d/rc.sysinit
+tmpfile=/tmp/lx_rc.sysinit.$$
+
+sed 's@^/sbin/hwclock@# lx: &@
+ s@^/bin/dmesg -n@# lx: &@
+ s@^dmesg -s@# lx: &@
+ s@^initlog -c \"fsck@# lx: &@
+ s@^mount -n -o remount /dev/shm @mount -t tmpfs tmpfs /dev/shm @
+ s@^mount .* /dev/pts@# lx: &@
+ /^#remount \/dev\/shm/d' \
+ $fnm > $tmpfile
+
+if [[ ! -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+ chmod 755 $fnm
+fi
+
+#
+# sysinit modifications are complete
+#
+rm -f $tmpfile
+
+# Hand control back to lx_boot
diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_suse.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_suse.ksh
new file mode 100644
index 0000000000..b8e3b69b21
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_suse.ksh
@@ -0,0 +1,213 @@
+#!/bin/ksh -p
+#
+# 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 2017 Joyent, Inc.
+# Copyright 2017 ASS-Einrichtungssysteme GmbH, Inc.
+#
+
+#
+# Customisation for SuSE-based distributions. Assumes to have been
+# sourced from lx_boot.
+#
+
+tmpfile=/tmp/lx-suse.$$
+
+
+# Check that the directories we're writing to aren't symlinks outside the zone
+safe_dir /etc
+safe_dir /etc/init.d
+safe_dir /etc/rc.d/rc0.d
+safe_dir /etc/rc.d/rc1.d
+safe_dir /etc/rc.d/rc2.d
+safe_dir /etc/rc.d/rc3.d
+safe_dir /etc/rc.d/rc4.d
+safe_dir /etc/rc.d/rc5.d
+safe_dir /etc/rc.d/rc6.d
+safe_dir /etc/rc.d/rcS.d
+safe_dir /etc/sysconfig
+safe_dir /etc/sysconfig/network
+safe_opt_dir /etc/systemd
+safe_opt_dir /etc/systemd/system
+safe_opt_dir /etc/systemd/system/multi-user.target.wants
+safe_opt_dir /etc/systemd/system/network-online.target.wants
+safe_dir /etc/YaST2
+safe_opt_dir /etc/selinux
+
+# Populate resolv.conf setup files
+zonecfg -z $ZONENAME info attr name=resolvers | awk '
+BEGIN {
+ print("# AUTOMATIC ZONE CONFIG")
+}
+$1 == "value:" {
+ nres = split($2, resolvers, ",");
+ for (i = 1; i <= nres; i++) {
+ print("nameserver", resolvers[i]);
+ }
+}
+' > $tmpfile
+zonecfg -z $ZONENAME info attr name=dns-domain | awk '
+$1 == "value:" {
+ dom = $2
+}
+END {
+ print("search", dom);
+}
+' >> $tmpfile
+fnm=$ZONEROOT/etc/resolv.conf
+if [[ -f $fnm || -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+fi
+
+# network configuration
+netdir="$ZONEROOT/etc/sysconfig/network"
+
+# first cleanup potentially obsolete configuration
+rm -f $netdir/ifcfg-*
+
+# Override network configuration for Loopback (lo) configuration
+cat <<LOEOF > $netdir/ifcfg-lo
+# AUTOMATIC ZONE CONFIG
+IPADDR=127.0.0.1/8
+NETMASK=255.0.0.0
+NETWORK=127.0.0.0
+STARTMODE=nfsroot
+BOOTPROTO=static
+USERCONTROL=no
+FIREWALL=no
+LOEOF
+
+zonecfg -z $ZONENAME info net | awk -v npath=$netdir '
+$1 == "physical:" {
+ fname = npath "/ifcfg-" $2
+ print("# Automatic zone config for interface:", $2) > fname
+ print("STARTMODE=auto") >> fname
+ print("BOOTPROTO=dhcp4") >> fname
+}
+$1 == "property:" && $2 == "(name=primary,value=\"true\")" {
+ print("DHCLIENT_SET_DEFAULT_ROUTE=yes") >> fname
+}'
+
+# This is specific to a systemd-based image
+sysdir="$ZONEROOT/etc/systemd/system"
+if [[ -d $sysdir ]]; then
+ # don't use NetworkManager wickedd service units
+ rm -f $sysdir/dbus-org.opensuse.Network.AUTO4.service
+ rm -f $sysdir/dbus-org.opensuse.Network.DHCP4.service
+ rm -f $sysdir/dbus-org.opensuse.Network.DHCP6.service
+ rm -f $sysdir/dbus-org.opensuse.Network.Nanny.service
+ rm -f $sysdir/network-online.target.wants/wicked.service
+ rm -f $sysdir/multi-user.target.wants/wicked.service
+ # our network setup needs to run
+ fnm=$sysdir/multi-user.target.wants/network.service
+ if [[ ! -f $fnm ]]; then
+ ln -s /usr/lib/systemd/system/wicked.service \
+ $sysdir/network.service
+ fi
+ # disable purge-kernels.service
+ rm -f $sysdir/multi-user.target.wants/purge-kernels.service
+fi
+
+#
+# The default /etc/inittab might spawn mingetty on each of the virtual consoles
+# as well as xdm on the X console. Since we don't have virtual consoles nor
+# an X console, spawn a single mingetty on /dev/console instead.
+#
+# Don't bother changing the file if it looks like we already did.
+#
+fnm=$ZONEROOT/etc/inittab
+if ! egrep -s "Modified by lx brand" $fnm; then
+ sed 's/^[1-6]:/# Disabled by lx brand: &/' \
+ $fnm > $tmpfile
+ echo "1:2345:respawn:/sbin/getty 38400 console" >> $tmpfile
+ echo "# Modified by lx brand" >> $tmpfile
+
+ if [[ ! -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+ chmod 644 $fnm
+ fi
+fi
+
+# The SuSE init uses a combination of traditional rc-style service
+# definitions and systemd-style definitions.
+
+#
+# The following rc-style scripts attempt to start services or otherwise
+# configure the system in ways incompatible with zones, so don't execute them
+# at boot time.
+#
+unsupported_rc_services="
+ checkfs.sh
+ checkroot.sh
+ hwclock.sh
+ kmod
+ mtab.sh
+ procps
+ udev
+ udev-mtab
+"
+
+for file in $unsupported_rc_services; do
+ rm -f $ZONEROOT/etc/init.d/$file
+
+ rc_files="$(echo $ZONEROOT/etc/rc[0-6S].d/[SK]+([0-9])$file)"
+
+ if [[ "$rc_files" != \
+ "$ZONEROOT/etc/rc[0-6S].d/[SK]+([0-9])$file" ]]; then
+ for file in $rc_files; do
+ rm -f "$file"
+ done
+ fi
+done
+
+disable_svc()
+{
+ fnm=$ZONEROOT/etc/init/$1.override
+ [[ -h $fnm || -f $fnm ]] && return
+ echo "manual" > $fnm
+}
+
+
+#
+# Now customize systemd
+#
+
+RMSVCS="
+ network-interface-security
+ udev
+ udevmonitor
+ udevtrigger
+ udev-fallback-graphics
+ udev-finish
+"
+for f in $RMSVCS
+do
+ disable_svc $f
+done
+
+#
+# We need to setup for the /dev/shm mount. Unlike some other distros, SuSE
+# can handle it as either /dev/shm or /run/shm. For simplicity we create an
+# fstab entry to force it into the /dev/shm style.
+#
+fnm=$ZONEROOT/etc/fstab
+entry=$(awk '{if ($2 == "/dev/shm") print $2}' $fnm)
+if [[ -z "$entry" && ! -h $fnm ]]; then
+ echo "swapfs /dev/shm tmpfs defaults 0 0" >> $fnm
+fi
+
+#
+# systemd modifications are complete
+#
+rm -f $tmpfile
+
+# Hand control back to lx_boot
diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu.ksh
new file mode 100644
index 0000000000..d1423a38c1
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu.ksh
@@ -0,0 +1,142 @@
+#!/bin/ksh -p
+#
+# 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 2016 Joyent, Inc.
+# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+#
+
+#
+# Customisation for Ubuntu-based distributions. Assumes to have been
+# sourced from lx_boot.
+#
+tmpfile=/tmp/lx-ubuntu.$$
+
+# Check that the directories we're writing to aren't symlinks outside the zone
+safe_dir /etc
+safe_dir /etc/init
+safe_dir /etc/resolvconf
+safe_dir /etc/resolvconf/resolv.conf.d
+safe_dir /etc/network
+safe_dir /etc/network/interfaces.d
+safe_dir /etc/network/interfaces.d/smartos
+safe_dir /etc/systemd
+
+# Populate resolv.conf setup files IFF we have resolvers information.
+resolvers=`zone_attr resolvers`
+if [ -n "$resolvers" ]; then
+
+ echo "# AUTOMATIC ZONE CONFIG" > $tmpfile
+ _IFS=$IFS; IFS=,; for r in $resolvers; do
+ echo "nameserver $r"
+ done >> $tmpfile
+ IFS=$_IFS
+ domain=`zone_attr dns-domain`
+ [ -n "$domain" ] && echo "search $domain" >> $tmpfile
+
+ if [ -f $ZONEROOT/etc/systemd/resolved.conf ]; then
+ cf=$ZONEROOT/etc/systemd/resolved.conf
+ sed -i -E '/^(DNS|Domains) *=/d' $cf
+ echo "DNS=$resolvers" >> $cf
+ [[ -n "$domain" ]] && echo "Domains=$domain" >> $cf
+ mv -f $tmpfile $ZONEROOT/etc/resolv.conf
+ else
+ fnm=$ZONEROOT/etc/resolvconf/resolv.conf.d/tail
+ if [[ -f $fnm || -h $fnm || ! -e $fnm ]]; then
+ mv -f $tmpfile $fnm
+ fi
+ fi
+fi
+
+# Override network configuration
+zonecfg -z $ZONENAME info net | awk '
+BEGIN {
+ print("# AUTOMATIC ZONE CONFIG")
+}
+$1 == "physical:" {
+ print("iface", $2, "inet manual");
+}
+' > $tmpfile
+fnm=$ZONEROOT/etc/network/interfaces.d/smartos
+if [[ -f $fnm || -h $fnm ]]; then
+ mv -f $tmpfile $fnm
+fi
+
+src_fnm=$ZONEROOT/etc/init/console.conf
+tgt_fnm=$ZONEROOT/etc/init/console.override
+if [[ -f $src_fnm && ! -f $tgt_fnm && ! -h $tgt_fnm ]] then
+ sed -e 's/lxc/smartos/' $src_fnm > /tmp/console.conf.$$
+ mv /tmp/console.conf.$$ $tgt_fnm
+fi
+
+fnm=$ZONEROOT/etc/init/container-detect.override
+if [[ ! -f $fnm && ! -h $fnm ]] then
+ cat <<'DONE' > $fnm
+description "Track if upstart is running in a container"
+
+start on mounted MOUNTPOINT=/run
+
+env container
+env LIBVIRT_LXC_UUID
+
+emits container
+
+pre-start script
+ container=smartos
+ echo "$container" > /run/container_type || true
+ initctl emit --no-wait container CONTAINER=$container
+ exit 0
+end script
+DONE
+fi
+
+# XXX need to add real mounting into this svc definition
+
+fnm=$ZONEROOT/etc/init/mountall.override
+if [[ ! -h $fnm ]] then
+ cat <<DONE > $fnm
+description "Mount filesystems on boot"
+
+start on startup
+
+task
+
+emits virtual-filesystems
+emits local-filesystems
+emits remote-filesystems
+emits all-swaps
+emits filesystem
+emits mounted
+
+script
+ echo "/dev/zfsds0 / zfs rw 0 0" > /etc/mtab
+ echo "proc /proc proc rw,noexec,nosuid,nodev 0 0" >> /etc/mtab
+
+ /sbin/initctl emit --no-wait virtual-filesystems
+ /bin/mount -t tmpfs tmpfs /dev/shm || true
+ /bin/mount -t tmpfs tmpfs /run || true
+ /bin/mkdir -p /run/lock || true
+ /bin/ln -s /dev/shm /run/shm || true
+ /sbin/initctl emit --no-wait mounted MOUNTPOINT=/run TYPE=tmpfs
+ /sbin/initctl emit --no-wait local-filesystems
+ /sbin/initctl emit --no-wait all-swaps
+ /sbin/initctl emit --no-wait filesystem
+end script
+DONE
+fi
+
+#
+# upstart modifications are complete
+#
+rm -f $tmpfile
+
+# Hand control back to lx_boot
diff --git a/usr/src/lib/brand/lx/zone/lx_boot_zone_void.ksh b/usr/src/lib/brand/lx/zone/lx_boot_zone_void.ksh
new file mode 100755
index 0000000000..df7218ca21
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_boot_zone_void.ksh
@@ -0,0 +1,17 @@
+#!/bin/ksh -p
+#
+# 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 2020 Joyent, Inc.
+#
+
+# Hand control back to lx_boot
diff --git a/usr/src/lib/brand/lx/zone/lx_install.ksh b/usr/src/lib/brand/lx/zone/lx_install.ksh
new file mode 100644
index 0000000000..d979bb373e
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_install.ksh
@@ -0,0 +1,62 @@
+#!/bin/ksh -p
+#
+# 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.
+#
+
+# Does this brand support reprovisioning?
+jst_reprovision="yes"
+
+# Is a template image optional?
+# jst_tmplopt="yes"
+
+. /usr/lib/brand/jcommon/libhooks.ksh
+
+function jcommon_attach_hook
+{
+ # lx-brand specific stuff is done here as final step of install
+
+ #
+ # Make sure the localhost has a hostname alias in the zone's
+ # /etc/hosts file
+ #
+ zroot=$ZONEPATH/root
+ hname=$ZONENAME
+ hostnamef=$zroot/etc/hostname
+ if [[ ! -h $hostnamef ]]; then
+ echo $hname >$hostnamef
+ fi
+
+ hostfile=$zroot/etc/hosts
+ if [[ -f $hostfile && ! -h $hostfile ]]; then
+ # use awk to search and append to loopback in one command
+ awk -v hname="$hname" '{
+ if ($1 ~ /^127\./ && index($0, hname) == 0) {
+ printf("%s %s\n", $0, hname);
+ } else {
+ print $0
+ }
+ }' $hostfile >/tmp/tmp_${ZONENAME}_$$
+ mv /tmp/tmp_${ZONENAME}_$$ $hostfile
+ chmod 644 $hostfile
+ fi
+
+ rm -rf $ZONEPATH/cores
+ CORE_QUOTA=102400
+ zfs create -o quota=${CORE_QUOTA}m \
+ -o mountpoint=/${PDS_NAME}/$bname/cores ${PDS_NAME}/cores/$bname
+
+ chmod 700 $ZONEPATH
+}
+
+. /usr/lib/brand/jcommon/cinstall
diff --git a/usr/src/lib/brand/lx/zone/lx_uninstall.ksh b/usr/src/lib/brand/lx/zone/lx_uninstall.ksh
new file mode 100755
index 0000000000..136044ad9e
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/lx_uninstall.ksh
@@ -0,0 +1,23 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+function jcommon_uninstall_hook
+{
+ :
+}
+
+. /usr/lib/brand/jcommon/cuninstall
diff --git a/usr/src/lib/brand/lx/zone/platform.xml b/usr/src/lib/brand/lx/zone/platform.xml
new file mode 100644
index 0000000000..060343e38f
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/platform.xml
@@ -0,0 +1,163 @@
+<?xml version="1.0"?>
+
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+ Copyright 2017 Joyent, Inc.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE platform PUBLIC "-//Sun Microsystems Inc//Zones Platform//EN"
+ "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1">
+
+<platform name="lx" allow-exclusive-ip="true">
+ <!-- Global filesystems to mount when booting the zone -->
+ <global_mount special="/dev" directory="/native/dev" type="dev"
+ opt="attrdir=%R/dev" />
+
+ <!--
+ Local filesystems to mount when booting the zone. The /native/sbin
+ entry is needed so that we can replace ifconfig with its native
+ counterpart (this is required for exclusive-stack zones to work).
+ We also need dladm from /native/sbin.
+ -->
+ <global_mount special="/lib" directory="/native/lib"
+ opt="ro" type="lofs" />
+ <global_mount special="/usr" directory="/native/usr"
+ opt="ro" type="lofs" />
+ <global_mount special="/usr/lib/brand/lx/etc_default_nfs"
+ directory="/native/etc/default/nfs" type="lofs" opt="ro" />
+ <global_mount special="/etc/default/dhcpagent"
+ directory="/native/etc/default/dhcpagent" type="lofs" opt="ro" />
+ <global_mount special="/etc/netconfig"
+ directory="/native/etc/netconfig" type="lofs" opt="ro" />
+ <global_mount special="/etc/nfssec.conf"
+ directory="/native/etc/nfssec.conf" type="lofs" opt="ro" />
+ <global_mount special="/sbin"
+ directory="/native/sbin" opt="ro,nodevices" type="lofs" />
+ <global_mount special="/usr/lib/brand/lx/ld" directory="/var/ld"
+ opt="ro" type="lofs" />
+ <global_mount special="/etc/zones/%z.xml"
+ directory="/native/etc/zones/%z.xml" opt="ro" type="lofs" />
+ <global_mount special="/var/zonecontrol/%z" directory="/native/.zonecontrol"
+ opt="ro,nodevices,nosetuid,noexec" type="lofs" />
+
+ <!-- Local filesystems to mount when booting the zone -->
+ <mount special="/native/dev" directory="/dev" type="lx_devfs" />
+ <mount special="proc" directory="/native/proc" type="proc" />
+ <mount special="swap" directory="/native/etc/svc/volatile"
+ type="tmpfs" />
+ <mount special="swap" directory="/native/tmp" type="tmpfs" />
+ <mount special="objfs" directory="/system/object" type="objfs" />
+ <mount special="ctfs" directory="/system/contract" type="ctfs" />
+ <mount special="mnttab" directory="/etc/mnttab" type="mntfs" />
+ <mount special="lxproc" directory="/proc" type="lx_proc" />
+
+ <!-- Devices to create under /dev -->
+ <device match="arp" />
+ <device match="dtrace/*" />
+ <device match="dtrace/provider/*" />
+ <device match="eventfd" />
+ <device match="full" />
+ <device match="inotify" />
+ <device match="ipnet" />
+ <device match="kstat" />
+ <device match="lo0" />
+ <device match="null" />
+ <device match="poll" />
+ <device match="pts/*" />
+ <device match="random" />
+ <device match="signalfd" />
+ <device match="tcp" />
+ <device match="tcp6" />
+ <device match="ticotsord" />
+ <device match="timerfd" />
+ <device match="tty" />
+ <device match="udp" />
+ <device match="udp6" />
+ <device match="urandom" />
+ <device match="zero" />
+ <device match="zfs" />
+ <device match="zvol/dsk/%P/%z/*" />
+ <device match="zvol/rdsk/%P/%z/*" />
+
+ <!-- Devices to create in exclusive IP zone only -->
+ <device match="dld" ip-type="exclusive" />
+ <device match="icmp" ip-type="exclusive" />
+ <device match="icmp6" ip-type="exclusive" />
+ <device match="ip" ip-type="exclusive" />
+ <device match="ip6" ip-type="exclusive" />
+ <device match="ipauth" ip-type="exclusive" />
+ <device match="ipf" ip-type="exclusive" />
+ <device match="ipl" ip-type="exclusive" />
+ <device match="iplookup" ip-type="exclusive" />
+ <device match="ipmpstub" ip-type="exclusive" />
+ <device match="ipnat" ip-type="exclusive" />
+ <device match="ipscan" ip-type="exclusive" />
+ <device match="ipsecah" ip-type="exclusive" />
+ <device match="ipsecesp" ip-type="exclusive" />
+ <device match="ipstate" ip-type="exclusive" />
+ <device match="ipsync" ip-type="exclusive" />
+ <device match="keysock" ip-type="exclusive" />
+ <device match="net/*" ip-type="exclusive" />
+ <device match="rawip" ip-type="exclusive" />
+ <device match="rawip6" ip-type="exclusive" />
+ <device match="rts" ip-type="exclusive" />
+ <device match="sad/admin" ip-type="exclusive" />
+ <device match="sctp" ip-type="exclusive" />
+ <device match="sctp6" ip-type="exclusive" />
+ <device match="spdsock" ip-type="exclusive" />
+ <device match="sppp" ip-type="exclusive" />
+ <device match="sppptun" ip-type="exclusive" />
+ <device match="vni" ip-type="exclusive" />
+
+ <!-- Renamed devices to create under /dev -->
+ <device match="brand/lx/autofs" name="autofs" />
+ <device match="brand/lx/ptmx" name="ptmx" />
+ <device match="zcons/%z/zoneconsole" name="console" />
+ <device match="zfd/%z/slave/0" name="zfd/0" />
+ <device match="zfd/%z/slave/1" name="zfd/1" />
+ <device match="zfd/%z/slave/2" name="zfd/2" />
+ <device match="zfd/%z/slave/3" name="zfd/3" />
+ <device match="zfd/%z/slave/4" name="zfd/4" />
+
+ <!-- Audio devices to create under /dev -->
+ <device match="brand/lx/dsp" name="dsp" />
+ <device match="brand/lx/mixer" name="mixer" />
+
+ <!-- Symlinks to create under /dev -->
+ <symlink source="fd" target="../proc/self/fd" />
+ <symlink source="stderr" target="../proc/self/fd/2" />
+ <symlink source="stdin" target="../proc/self/fd/0" />
+ <symlink source="stdout" target="../proc/self/fd/1" />
+ <symlink source="systty" target="console" />
+ <symlink source="kmsg" target="console" />
+ <symlink source="conslog" target="console" />
+
+ <!-- Create a mount point for /dev/shm tmpfs (see shm_overview(7)) -->
+ <!-- We need to force a dir for the Linux mount to work ok -->
+ <symlink source="shm/loop" target="." />
+ <!-- Create a dummy /dev/xconsole -->
+ <device match="null" name="xconsole" />
+
+</platform>
diff --git a/usr/src/lib/brand/lx/zone/poststate.ksh b/usr/src/lib/brand/lx/zone/poststate.ksh
new file mode 100755
index 0000000000..49370fce60
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/poststate.ksh
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="lx"
+. /usr/lib/brand/jcommon/poststate
diff --git a/usr/src/lib/brand/lx/zone/prestate.ksh b/usr/src/lib/brand/lx/zone/prestate.ksh
new file mode 100755
index 0000000000..04d9969a65
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/prestate.ksh
@@ -0,0 +1,19 @@
+#!/bin/ksh -p
+#
+#
+# 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.
+#
+
+ps_brand="lx"
+. /usr/lib/brand/jcommon/prestate
diff --git a/usr/src/lib/brand/lx/zone/statechange.ksh b/usr/src/lib/brand/lx/zone/statechange.ksh
new file mode 100755
index 0000000000..d0bc11eee9
--- /dev/null
+++ b/usr/src/lib/brand/lx/zone/statechange.ksh
@@ -0,0 +1,41 @@
+#!/bin/ksh -p
+#
+# 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) 2018 Joyent, Inc. All rights reserved.
+#
+
+# Do we support vrrp?
+# jst_vrrp="yes"
+
+# Do we support unfiltered promiscuous mode
+# jst_ufpromisc="yes"
+
+# Do we use vnd devices
+# jst_createvnd="yes"
+
+# Do we not need to have various SMF and ccs stuff created for us?
+jst_simplefs="yes"
+
+# Should we show snapshots?
+jst_showsnap="yes"
+
+# Where does the mdata socket live?
+jst_mdatapath="/native/.zonecontrol/"
+
+# Ensure docker zfd(7D) log is not put in the wrong place
+if [[ "$_ZONECFG_attr_docker" == true ]]; then
+ typeset -A FORCED_ATTRS
+ FORCED_ATTRS["zlog-name"]=
+fi
+
+. /usr/lib/brand/jcommon/statechange
diff --git a/usr/src/lib/brand/shared/zone/common.ksh b/usr/src/lib/brand/shared/zone/common.ksh
index b2c2e052ef..b0a0cd88f7 100644
--- a/usr/src/lib/brand/shared/zone/common.ksh
+++ b/usr/src/lib/brand/shared/zone/common.ksh
@@ -19,6 +19,7 @@
# CDDL HEADER END
#
# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2014, Joyent, Inc. All rights reserved.
#
#
@@ -96,9 +97,27 @@ vlog()
safe_dir()
{
typeset dir="$1"
+ typeset pwd_dir=""
- if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
- fatal "$e_baddir" "$dir"
+ if [[ -d $ZONEROOT/$dir ]]; then
+ if [[ -h $ZONEROOT/$dir ]]; then
+ #
+ # When dir is a symlink to a directory, we 'cd' to that
+ # directory to ensure that's under $ZONEROOT. We use pwd
+ # from /usr/bin instead of built-in because they give
+ # different results.
+ #
+ pwd_dir=$(cd $ZONEROOT/$dir && /usr/bin/pwd)
+ if [[ $pwd_dir =~ "^$ZONEROOT" ]]; then
+ return;
+ else
+ fatal \
+ "$e_baddir: symlink out of zoneroot" "$dir"
+ fi
+ else
+ # it's a dir and not a symlink, so that's ok.
+ return
+ fi
fi
}
@@ -109,9 +128,7 @@ safe_opt_dir()
[[ ! -e $ZONEROOT/$dir ]] && return
- if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
- fatal "$e_baddir" "$dir"
- fi
+ safe_dir $dir
}
# Only make a copy if we haven't already done so.
@@ -187,7 +204,7 @@ safe_replace()
fi
cat <<-END >$filename || exit 1
- #!/bin/sh -p
+ #!/bin/sh
#
# Solaris Brand Replacement
#
@@ -333,10 +350,12 @@ post_unpack()
#
# Check if the image was created with a valid libc.so.1.
#
- hwcap=`moe -v -32 $ZONEROOT/lib/libc.so.1 2>&1`
- if (( $? != 0 )); then
- vlog "$f_hwcap_info" "$hwcap"
- fail_fatal "$f_sanity_hwcap"
+ if [[ -f $ZONEROOT/lib/libc.so.1 ]]; then
+ hwcap=`moe -v -32 $ZONEROOT/lib/libc.so.1 2>&1`
+ if (( $? != 0 )); then
+ vlog "$f_hwcap_info" "$hwcap"
+ fail_fatal "$f_sanity_hwcap"
+ fi
fi
( cd "$ZONEROOT" && \
@@ -1003,41 +1022,36 @@ install_image()
return 0
}
-# Setup i18n output
-TEXTDOMAIN="SUNW_OST_OSCMD"
-export TEXTDOMAIN
-
-e_cannot_wrap=$(gettext "%s: error: wrapper file already exists")
-e_baddir=$(gettext "Invalid '%s' directory within the zone")
-e_badfile=$(gettext "Invalid '%s' file within the zone")
-e_path_abs=$(gettext "Pathname specified to -a '%s' must be absolute.")
-e_not_found=$(gettext "%s: error: file or directory not found.")
-e_install_abort=$(gettext "Installation aborted.")
-e_not_readable=$(gettext "Cannot read directory '%s'")
-e_not_dir=$(gettext "Error: must be a directory")
-e_unknown_archive=$(gettext "Error: Unknown archive format. Must be a flash archive, a cpio archive (can also be gzipped or bzipped), a pax XUSTAR archive, or a level 0 ufsdump archive.")
-e_absolute_archive=$(gettext "Error: archive contains absolute paths instead of relative paths.")
-e_mismatch_archive=$(gettext "Error: the archive top-level directory (%s) does not match the zonepath (%s).")
-e_tmpfile=$(gettext "Unable to create temporary file")
-e_root_full=$(gettext "Zonepath root %s exists and contains data; remove or move aside prior to install.")
-f_mkdir=$(gettext "Unable to create directory %s.")
-f_chmod=$(gettext "Unable to chmod directory %s.")
-f_chown=$(gettext "Unable to chown directory %s.")
-f_hwcap_info=$(gettext "HWCAP: %s\n")
-f_sanity_hwcap=$(gettext \
-"The image was created with an incompatible libc.so.1 hwcap lofs mount.\n"\
+e_cannot_wrap="%s: error: wrapper file already exists"
+e_baddir="Invalid '%s' directory within the zone"
+e_badfile="Invalid '%s' file within the zone"
+e_path_abs="Pathname specified to -a '%s' must be absolute."
+e_not_found="%s: error: file or directory not found."
+e_install_abort="Installation aborted."
+e_not_readable="Cannot read directory '%s'"
+e_not_dir="Error: must be a directory"
+e_unknown_archive="Error: Unknown archive format. Must be a flash archive, a cpio archive (can also be gzipped or bzipped), a pax XUSTAR archive, or a level 0 ufsdump archive."
+e_absolute_archive="Error: archive contains absolute paths instead of relative paths."
+e_mismatch_archive="Error: the archive top-level directory (%s) does not match the zonepath (%s)."
+e_tmpfile="Unable to create temporary file"
+e_root_full="Zonepath root %s exists and contains data; remove or move aside prior to install."
+f_mkdir="Unable to create directory %s."
+f_chmod="Unable to chmod directory %s."
+f_chown="Unable to chown directory %s."
+f_hwcap_info="HWCAP: %s\n"
+f_sanity_hwcap="The image was created with an incompatible libc.so.1 hwcap lofs mount.\n"\
" The zone will not boot on this platform. See the zone's\n"\
-" documentation for the recommended way to create the archive.")
+" documentation for the recommended way to create the archive."
-m_analyse_archive=$(gettext "Analysing the archive")
+m_analyse_archive="Analysing the archive"
-not_readable=$(gettext "Cannot read file '%s'")
-not_flar=$(gettext "Input is not a flash archive")
-bad_flar=$(gettext "Flash archive is a corrupt")
-bad_zfs_flar=$(gettext "Flash archive contains a ZFS send stream.\n\tRecreate the flar using the -L option with cpio or pax.")
-f_unpack_failed=$(gettext "Unpacking the archive failed")
-unknown_archiver=$(gettext "Archiver %s is not supported")
-cmd_not_exec=$(gettext "Required command '%s' not executable!")
+not_readable="Cannot read file '%s'"
+not_flar="Input is not a flash archive"
+bad_flar="Flash archive is a corrupt"
+bad_zfs_flar="Flash archive contains a ZFS send stream.\n\tRecreate the flar using the -L option with cpio or pax."
+f_unpack_failed="Unpacking the archive failed"
+unknown_archiver="Archiver %s is not supported"
+cmd_not_exec="Required command '%s' not executable!"
#
# Exit values used by the script, as #defined in <sys/zone.h>
diff --git a/usr/src/lib/brand/shared/zone/uninstall.ksh b/usr/src/lib/brand/shared/zone/uninstall.ksh
index 468a7ed92f..923363c864 100644
--- a/usr/src/lib/brand/shared/zone/uninstall.ksh
+++ b/usr/src/lib/brand/shared/zone/uninstall.ksh
@@ -35,31 +35,31 @@ bname=`basename $0`
#
# error messages
#
-m_usage=$(gettext "Usage: %s: [-hFn]")
-
-m_1_zfs_promote=$(gettext "promoting '%s'.")
-m_1_zfs_destroy=$(gettext "destroying '%s'.")
-m_2_zfs_rename=$(gettext "renaming '%s' to '%s'.")
-m_3_zfs_set=$(gettext "setting property %s='%s' for '%s'.")
-m_rm_r=$(gettext "recursively deleting '%s'.")
-m_rm=$(gettext "deleting '%s'.")
-
-w_no_ds=$(gettext "Warning: no zonepath dataset found.")
-
-f_usage_err=$(gettext "Error: invalid usage")
-f_abort=$(gettext "Error: internal error detected, aborting.")
-f_1_zfs_promote=$(gettext "Error: promoting ZFS dataset '%s'.")
-f_2_zfs_rename=$(gettext "Error: renaming ZFS dataset '%s' to '%s'.")
-f_3_zfs_set=$(gettext "Error: setting ZFS propery %s='%s' for '%s'.")
-f_1_zfs_destroy=$(gettext "Error: destroying ZFS dataset.")
-f_2_zfs_get=$(gettext "Error: reading ZFS dataset property '%s' from '%s'.")
-f_user_snap=$(gettext "Error: user snapshot(s) detected.")
-f_stray_snap=$(gettext "Error: uncloned snapshot(s) detected.")
-f_stray_clone=$(gettext "Error: cloned zone datasets found outsize of zone.")
-f_rm_snap=$(gettext "Error: please delete snapshot(s) and retry uninstall.")
-f_rm_clone=$(gettext "Error: please delete clone(s) and retry uninstall.")
-f_iu_clone=$(gettext "Error: cloned zone dataset(s) in use.")
-f_dis_clone=$(gettext "Error: please stop using clone(s) and retry uninstall.")
+m_usage="Usage: %s: [-hFn]"
+
+m_1_zfs_promote="promoting '%s'."
+m_1_zfs_destroy="destroying '%s'."
+m_2_zfs_rename="renaming '%s' to '%s'."
+m_3_zfs_set="setting property %s='%s' for '%s'."
+m_rm_r="recursively deleting '%s'."
+m_rm="deleting '%s'."
+
+w_no_ds="Warning: no zonepath dataset found."
+
+f_usage_err="Error: invalid usage"
+f_abort="Error: internal error detected, aborting."
+f_1_zfs_promote="Error: promoting ZFS dataset '%s'."
+f_2_zfs_rename="Error: renaming ZFS dataset '%s' to '%s'."
+f_3_zfs_set="Error: setting ZFS propery %s='%s' for '%s'."
+f_1_zfs_destroy="Error: destroying ZFS dataset."
+f_2_zfs_get="Error: reading ZFS dataset property '%s' from '%s'."
+f_user_snap="Error: user snapshot(s) detected."
+f_stray_snap="Error: uncloned snapshot(s) detected."
+f_stray_clone="Error: cloned zone datasets found outsize of zone."
+f_rm_snap="Error: please delete snapshot(s) and retry uninstall."
+f_rm_clone="Error: please delete clone(s) and retry uninstall."
+f_iu_clone="Error: cloned zone dataset(s) in use."
+f_dis_clone="Error: please stop using clone(s) and retry uninstall."
#
# functions
diff --git a/usr/src/lib/fm/libfmd_adm/common/fmd_adm.c b/usr/src/lib/fm/libfmd_adm/common/fmd_adm.c
index 3c1c189c8d..d102735eb7 100644
--- a/usr/src/lib/fm/libfmd_adm/common/fmd_adm.c
+++ b/usr/src/lib/fm/libfmd_adm/common/fmd_adm.c
@@ -24,6 +24,10 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
#include <strings.h>
#include <stdlib.h>
#include <netdir.h>
@@ -1027,7 +1031,7 @@ fmd_adm_xprt_iter(fmd_adm_t *ap, fmd_adm_xprt_f *func, void *arg)
}
for (i = 0; i < rxl.rxl_len; i++)
- func(rxl.rxl_buf.rxl_buf_val[i], arg);
+ (void) func(rxl.rxl_buf.rxl_buf_val[i], arg);
xdr_free(xdr_fmd_rpc_xprtlist, (char *)&rxl);
return (0);
diff --git a/usr/src/lib/fm/libfmd_snmp/Makefile.com b/usr/src/lib/fm/libfmd_snmp/Makefile.com
index 99dd888363..e0abeeef0d 100644
--- a/usr/src/lib/fm/libfmd_snmp/Makefile.com
+++ b/usr/src/lib/fm/libfmd_snmp/Makefile.com
@@ -53,7 +53,7 @@ $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG
CFLAGS += $(CCVERBOSE) $(C_BIGPICFLAGS)
CFLAGS64 += $(CCVERBOSE) $(C_BIGPICFLAGS)
-SNMPLIBS = -lnetsnmp -lnetsnmpagent
+SNMPLIBS = -lnetsnmp -lnetsnmpagent -lnetsnmphelpers
NATIVE_LIBS += libnetsnmp.so libnetsnmpagent.so
LDLIBS += $(MACH_LDLIBS)
diff --git a/usr/src/lib/fm/topo/libtopo/common/libtopo.h b/usr/src/lib/fm/topo/libtopo/common/libtopo.h
index c8dcdb9cd4..93884718c6 100644
--- a/usr/src/lib/fm/topo/libtopo/common/libtopo.h
+++ b/usr/src/lib/fm/topo/libtopo/common/libtopo.h
@@ -410,6 +410,8 @@ extern void topo_hdl_free(topo_hdl_t *, void *, size_t);
extern int topo_hdl_nvalloc(topo_hdl_t *, nvlist_t **, uint_t);
extern int topo_hdl_nvdup(topo_hdl_t *, nvlist_t *, nvlist_t **);
extern char *topo_hdl_strdup(topo_hdl_t *, const char *);
+extern char *topo_hdl_strsplit(topo_hdl_t *, const char *, const char *,
+ char **);
/*
* Interfaces for interacting with directed graph topologies
diff --git a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers
index 5d1b53f1f6..ca33084ff2 100644
--- a/usr/src/lib/fm/topo/libtopo/common/mapfile-vers
+++ b/usr/src/lib/fm/topo/libtopo/common/mapfile-vers
@@ -86,6 +86,7 @@ SYMBOL_VERSION SUNWprivate {
topo_hdl_strdup;
topo_hdl_strfree;
topo_hdl_strfreev;
+ topo_hdl_strsplit;
topo_hdl_zalloc;
topo_led_state_name;
topo_led_type_name;
@@ -101,6 +102,8 @@ SYMBOL_VERSION SUNWprivate {
topo_mod_alloc;
topo_mod_auth;
topo_mod_clean_str;
+ topo_mod_create_ufm;
+ topo_mod_create_ufm_slot;
topo_mod_clrdebug;
topo_mod_cpufmri;
topo_mod_create_ufm;
@@ -137,6 +140,7 @@ SYMBOL_VERSION SUNWprivate {
topo_mod_strdup;
topo_mod_strfree;
topo_mod_strfreev;
+ topo_mod_strsplit;
topo_mod_unload;
topo_mod_unregister;
topo_mod_walk_init;
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h
index 593f8946fb..1e5589b78c 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_hc.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_hc.h
@@ -236,6 +236,27 @@ extern "C" {
*/
#define TOPO_PROP_IPMI_ENTITY_LIST "entity-list"
+/*
+ * These properties can be used to describe the network configuration of a
+ * given hardware components. They're currently used to describe the
+ * network config on the service processor (sp)
+ */
+#define TOPO_PGROUP_NETCFG "network-config"
+#define TOPO_PROP_NETCFG_MACADDR "mac-address"
+#define TOPO_PROP_NETCFG_VLAN_ID "vlan-id"
+#define TOPO_PROP_NETCFG_IPV4_ADDR "ipv4-address"
+#define TOPO_PROP_NETCFG_IPV4_SUBNET "ipv4-subnet"
+#define TOPO_PROP_NETCFG_IPV4_GATEWAY "ipv4-gateway"
+#define TOPO_PROP_NETCFG_IPV4_TYPE "ipv4-config-type"
+#define TOPO_PROP_NETCFG_IPV6_ADDR "ipv6-address"
+#define TOPO_PROP_NETCFG_IPV6_ROUTES "ipv6-routes"
+#define TOPO_PROP_NETCFG_IPV6_TYPE "ipv6-config-type"
+
+/* Possible values for TOPO_PROP_NETCFG_TYPE */
+#define TOPO_NETCFG_TYPE_UNKNOWN "unknown"
+#define TOPO_NETCFG_TYPE_STATIC "static"
+#define TOPO_NETCFG_TYPE_DHCP "dhcp"
+
#define TOPO_PGROUP_SLOT "slot"
#define TOPO_PROP_SLOT_TYPE "slot-type"
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
index 4e2ec816fc..772c51bb0a 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_mod.h
@@ -222,6 +222,8 @@ extern void topo_mod_free(topo_mod_t *, void *, size_t);
extern char *topo_mod_strdup(topo_mod_t *, const char *);
extern void topo_mod_strfree(topo_mod_t *, char *);
extern void topo_mod_strfreev(topo_mod_t *, char **, uint_t);
+extern char *topo_mod_strsplit(topo_mod_t *, const char *, const char *,
+ char **);
extern int topo_mod_nvalloc(topo_mod_t *, nvlist_t **, uint_t);
extern int topo_mod_nvdup(topo_mod_t *, nvlist_t *, nvlist_t **);
diff --git a/usr/src/lib/fm/topo/libtopo/common/topo_string.c b/usr/src/lib/fm/topo/libtopo/common/topo_string.c
index 53a6280647..bca2bdc66a 100644
--- a/usr/src/lib/fm/topo/libtopo/common/topo_string.c
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_string.c
@@ -66,6 +66,58 @@ topo_hdl_strfreev(topo_hdl_t *thp, char **strarr, uint_t nelem)
}
char *
+topo_hdl_strsplit(topo_hdl_t *hdl, const char *input, const char *sep,
+ char **lastp)
+{
+ size_t seplen = strlen(sep);
+ const char *scanstart;
+ char *token;
+ char *ret;
+
+ if (input != NULL) {
+ /*
+ * Start scanning at beginning of input:
+ */
+ scanstart = input;
+ } else if (*lastp == NULL) {
+ /*
+ * If we have already finished scanning, return NULL.
+ */
+ return (NULL);
+ } else {
+ /*
+ * Otherwise, start scanning where we left off:
+ */
+ scanstart = *lastp;
+ }
+
+ token = strstr(scanstart, sep);
+ if (token != NULL) {
+ /*
+ * We still have a separator, so advance the next-start
+ * pointer past it:
+ */
+ *lastp = token + seplen;
+ /*
+ * Copy out this element. The buffer must fit the string
+ * exactly, so that topo_hdl_strfree() can determine its
+ * size with strlen().
+ */
+ ret = topo_hdl_alloc(hdl, token - scanstart + 1);
+ (void) strncpy(ret, scanstart, token - scanstart);
+ ret[token - scanstart] = '\0';
+ } else {
+ /*
+ * We have no separator, so this is the last element:
+ */
+ *lastp = NULL;
+ ret = topo_hdl_strdup(hdl, scanstart);
+ }
+
+ return (ret);
+}
+
+char *
topo_mod_strdup(topo_mod_t *mod, const char *s)
{
return (topo_hdl_strdup(mod->tm_hdl, s));
@@ -83,6 +135,13 @@ topo_mod_strfreev(topo_mod_t *mod, char **strarr, uint_t nelem)
topo_hdl_strfreev(mod->tm_hdl, strarr, nelem);
}
+char *
+topo_mod_strsplit(topo_mod_t *mod, const char *input, const char *sep,
+ char **lastp)
+{
+ return (topo_hdl_strsplit(mod->tm_hdl, input, sep, lastp));
+}
+
const char *
topo_strbasename(const char *s)
{
diff --git a/usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Joyent-Compute-Platform-1101-disk-hc-topology.xmlgenksh b/usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Joyent-Compute-Platform-1101-disk-hc-topology.xmlgenksh
index 874bad142f..874bad142f 100644..100755
--- a/usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Joyent-Compute-Platform-1101-disk-hc-topology.xmlgenksh
+++ b/usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Joyent-Compute-Platform-1101-disk-hc-topology.xmlgenksh
diff --git a/usr/src/lib/fm/topo/maps/Joyent,Joyent-Storage-Platform-7001/Joyent-Storage-Platform-7001-slot-hc-topology.xml b/usr/src/lib/fm/topo/maps/Joyent,Joyent-Storage-Platform-7001/Joyent-Storage-Platform-7001-slot-hc-topology.xml
index 6b16063427..9362ae3e8d 100644
--- a/usr/src/lib/fm/topo/maps/Joyent,Joyent-Storage-Platform-7001/Joyent-Storage-Platform-7001-slot-hc-topology.xml
+++ b/usr/src/lib/fm/topo/maps/Joyent,Joyent-Storage-Platform-7001/Joyent-Storage-Platform-7001-slot-hc-topology.xml
@@ -11,7 +11,7 @@
source. A copy of the CDDL is also available via the Internet at
http://www.illumos.org/license/CDDL.
- Copyright (c) 2018, Joyent, Inc.
+ Copyright (c) 2019, Joyent, Inc.
-->
@@ -201,7 +201,7 @@
<propgroup name='ipmi' version='1'
name-stability='Private' data-stability='Private' >
<propval name='entity-list' type='string_array' >
- <propitem value='P2-DIMMF1 Temp' />
+ <propitem value='P2-DIMMF2 Temp' />
</propval>
</propgroup>
</node>
diff --git a/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-slot-hc-topology.xml b/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-slot-hc-topology.xml
index 6b16063427..9362ae3e8d 100644
--- a/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-slot-hc-topology.xml
+++ b/usr/src/lib/fm/topo/maps/SMCI,SSG-2028R-ACR24L/SSG-2028R-ACR24L-slot-hc-topology.xml
@@ -11,7 +11,7 @@
source. A copy of the CDDL is also available via the Internet at
http://www.illumos.org/license/CDDL.
- Copyright (c) 2018, Joyent, Inc.
+ Copyright (c) 2019, Joyent, Inc.
-->
@@ -201,7 +201,7 @@
<propgroup name='ipmi' version='1'
name-stability='Private' data-stability='Private' >
<propval name='entity-list' type='string_array' >
- <propitem value='P2-DIMMF1 Temp' />
+ <propitem value='P2-DIMMF2 Temp' />
</propval>
</propgroup>
</node>
diff --git a/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml b/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml
index 0ddcd1e0d8..f6b0a39e9e 100644
--- a/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml
+++ b/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml
@@ -83,6 +83,9 @@ Copyright 2019 Joyent, Inc.
<range name='hostbridge' min='0' max='254'>
<enum-method name='hostbridge' version='1' />
</range>
+ <range name='sp' min='0' max='0'>
+ <enum-method name='ipmi' version='1' />
+ </range>
<range name='usb-mobo' min='0' max='100'>
<enum-method name='usb' version='1' />
</range>
@@ -161,7 +164,7 @@ Copyright 2019 Joyent, Inc.
</range>
</set>
- <set type='product' setlist='Joyent-Compute-Platform-1101|Joyent-Compute-Platform-1102'>
+ <set type='product' setlist='Joyent-Compute-Platform-1101|Joyent-Compute-Platform-1102|Joyent-Compute-Platform-2101|Joyent-Compute-Platform-2102'>
<range name='psu' min='0' max='1'>
<enum-method name='ipmi' version='1' />
</range>
diff --git a/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c b/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c
index db853c6695..beae89f9f1 100644
--- a/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c
+++ b/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c
@@ -32,11 +32,18 @@
#include "disk.h"
#include "disk_drivers.h"
+/*
+ * Request the SAS address of the disk (if any) attached to this mpt_sas
+ * instance at (Enclosure Number, Slot Number). The function returns
+ * -1 on error and sets errno to ENOENT _only_ if the /devices node
+ * (*devctl) does not exist.
+ */
static int
get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure,
uint32_t slot, char **sas_address)
{
- int fd, err, i;
+ int ret = -1, en = ENXIO;
+ int fd, i;
mptsas_get_disk_info_t gdi;
mptsas_disk_info_t *di;
size_t disz;
@@ -44,16 +51,19 @@ get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure,
bzero(&gdi, sizeof (gdi));
if ((fd = open(devctl, O_RDWR)) == -1) {
+ en = errno;
topo_mod_dprintf(mod, "could not open '%s' for ioctl: %s\n",
devctl, strerror(errno));
+ errno = en;
return (-1);
}
if (ioctl(fd, MPTIOCTL_GET_DISK_INFO, &gdi) == -1) {
+ if (errno != ENOENT)
+ en = errno;
topo_mod_dprintf(mod, "ioctl 1 on '%s' failed: %s\n", devctl,
strerror(errno));
- (void) close(fd);
- return (-1);
+ goto out;
}
gdi.DiskInfoArraySize = disz = sizeof (mptsas_disk_info_t) *
@@ -61,19 +71,19 @@ get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure,
gdi.PtrDiskInfoArray = di = topo_mod_alloc(mod, disz);
if (di == NULL) {
topo_mod_dprintf(mod, "memory allocation failed\n");
- (void) close(fd);
- return (-1);
+ en = ENOMEM;
+ goto out;
}
if (ioctl(fd, MPTIOCTL_GET_DISK_INFO, &gdi) == -1) {
+ if (errno != ENOENT)
+ en = errno;
topo_mod_dprintf(mod, "ioctl 2 on '%s' failed: %s\n", devctl,
strerror(errno));
topo_mod_free(mod, di, disz);
- (void) close(fd);
- return (-1);
+ goto out;
}
- err = -1;
for (i = 0; i < gdi.DiskCount; i++) {
if (di[i].Enclosure == enclosure && di[i].Slot == slot) {
char sas[17]; /* 16 hex digits and NUL */
@@ -81,14 +91,16 @@ get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure,
topo_mod_dprintf(mod, "found mpt_sas disk (%d/%d) "
"with adddress %s\n", enclosure, slot, sas);
*sas_address = topo_mod_strdup(mod, sas);
- err = 0;
+ en = ret = 0;
break;
}
}
topo_mod_free(mod, di, disz);
+out:
(void) close(fd);
- return (err);
+ errno = en;
+ return (ret);
}
int
@@ -97,6 +109,8 @@ disk_mptsas_find_disk(topo_mod_t *mod, tnode_t *baynode, char **sas_address)
char *devctl = NULL;
uint32_t enclosure, slot;
int err;
+ char *elem, *lastp;
+ int ret = -1;
/*
* Get the required properties from the node. These come from
@@ -115,6 +129,35 @@ disk_mptsas_find_disk(topo_mod_t *mod, tnode_t *baynode, char **sas_address)
return (-1);
}
- return (get_sas_address(mod, devctl, enclosure, slot, sas_address));
+ /*
+ * devctl is a (potentially) pipe-separated list of different device
+ * paths to try.
+ */
+ if ((elem = topo_mod_strsplit(mod, devctl, "|", &lastp)) != NULL) {
+ boolean_t done = B_FALSE;
+ do {
+ topo_mod_dprintf(mod, "trying mpt_sas instance at %s\n",
+ elem);
+
+ ret = get_sas_address(mod, elem, enclosure,
+ slot, sas_address);
+
+ /*
+ * Only try further devctl paths from the list if this
+ * one was not found:
+ */
+ if (ret == 0 || errno != ENOENT) {
+ done = B_TRUE;
+ } else {
+ topo_mod_dprintf(mod, "instance not found\n");
+ }
+
+ topo_mod_strfree(mod, elem);
+
+ } while (!done && (elem = topo_mod_strsplit(mod, NULL, "|",
+ &lastp)) != NULL);
+ }
+ topo_mod_strfree(mod, devctl);
+ return (ret);
}
diff --git a/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c b/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c
index a49a131811..115e3b801d 100644
--- a/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c
+++ b/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c
@@ -72,6 +72,12 @@ _topo_fini(topo_mod_t *mod)
topo_mod_unregister(mod);
}
+/*
+ * Get or set LED state for a particular target attached to an mpt_sas
+ * instance at (Enclosure Number, Slot Number). The function returns
+ * -1 on error and sets errno to ENOENT _only_ if the /devices node
+ * (*devctl) does not exist.
+ */
static int
do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure,
uint16_t slot, uint8_t led, uint32_t *ledmode, boolean_t set)
@@ -88,8 +94,10 @@ do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure,
lc.LedStatus = *ledmode;
if ((fd = open(devctl, (set ? O_RDWR : O_RDONLY))) == -1) {
+ int en = errno;
topo_mod_dprintf(mod, "devctl open failed: %s",
strerror(errno));
+ errno = en;
return (-1);
}
@@ -103,9 +111,11 @@ do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure,
*/
lc.LedStatus = 0;
} else {
+ int en = errno;
topo_mod_dprintf(mod, "led control ioctl failed: %s",
strerror(errno));
(void) close(fd);
+ errno = en;
return (-1);
}
}
@@ -113,6 +123,7 @@ do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure,
*ledmode = lc.LedStatus ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF;
(void) close(fd);
+ errno = 0;
return (0);
}
@@ -127,7 +138,8 @@ mptsas_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
char *driver = NULL, *devctl = NULL;
uint32_t enclosure, slot;
uint8_t mptsas_led;
- boolean_t set;
+ boolean_t set, done;
+ char *elem, *lastp;
if (vers > TOPO_METH_MPTSAS_LED_MODE_VERSION)
return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
@@ -197,8 +209,41 @@ mptsas_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
topo_mod_dprintf(mod, "%s: Getting LED mode\n", __func__);
}
- if (do_led_control(mod, devctl, enclosure, slot, mptsas_led, &ledmode,
- set) != 0) {
+ /*
+ * devctl is a (potentially) pipe-separated list of different device
+ * paths to try.
+ */
+ if ((elem = topo_mod_strsplit(mod, devctl, "|", &lastp)) == NULL) {
+ topo_mod_dprintf(mod, "%s: could not parse devctl list",
+ __func__);
+ ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
+ goto out;
+ }
+ done = B_FALSE;
+ do {
+ topo_mod_dprintf(mod, "%s: trying mpt_sas instance at %s\n",
+ __func__, elem);
+
+ ret = do_led_control(mod, elem, enclosure, slot,
+ mptsas_led, &ledmode, set);
+
+ /*
+ * Only try further devctl paths from the list if this one
+ * was not found:
+ */
+ if (ret == 0 || errno != ENOENT) {
+ done = B_TRUE;
+ } else {
+ topo_mod_dprintf(mod, "%s: instance not found\n",
+ __func__);
+ }
+
+ topo_mod_strfree(mod, elem);
+
+ } while (!done && (elem = topo_mod_strsplit(mod, NULL, "|",
+ &lastp)) != NULL);
+
+ if (ret != 0) {
topo_mod_dprintf(mod, "%s: do_led_control failed", __func__);
ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
goto out;
diff --git a/usr/src/lib/fm/topo/modules/common/ipmi/Makefile b/usr/src/lib/fm/topo/modules/common/ipmi/Makefile
index b6f2839c42..4a0bf39401 100644
--- a/usr/src/lib/fm/topo/modules/common/ipmi/Makefile
+++ b/usr/src/lib/fm/topo/modules/common/ipmi/Makefile
@@ -22,7 +22,8 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-#ident "%Z%%M% %I% %E% SMI"
+# Copyright (c) 2018, Joyent, Inc.
+#
MODULE = ipmi
CLASS = common
@@ -31,4 +32,4 @@ MODULESRCS = ipmi_enum.c ipmi_methods.c
include ../../Makefile.plugin
-LDLIBS += -lipmi
+LDLIBS += -lipmi -lnsl
diff --git a/usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c b/usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c
index e0a768462d..96149ccc2e 100644
--- a/usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c
+++ b/usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2017, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2019 by Western Digital Corporation
*/
@@ -30,6 +30,9 @@
#include <fm/topo_mod.h>
#include <sys/fm/protocol.h>
#include <string.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
#define TOPO_PGROUP_IPMI "ipmi"
#define TOPO_PROP_IPMI_ENTITY_REF "entity_ref"
@@ -599,6 +602,278 @@ ipmi_check_entity(ipmi_handle_t *ihp, ipmi_entity_t *ep, void *data)
return (0);
}
+static const char *
+ipmi2toposrc(uint8_t ipmi_ip_src)
+{
+ char *cfgtype;
+
+ switch (ipmi_ip_src) {
+ case (IPMI_LAN_SRC_STATIC):
+ case (IPMI_LAN_SRC_BIOS):
+ cfgtype = TOPO_NETCFG_TYPE_STATIC;
+ break;
+ case (IPMI_LAN_SRC_DHCP):
+ cfgtype = TOPO_NETCFG_TYPE_DHCP;
+ break;
+ default:
+ cfgtype = TOPO_NETCFG_TYPE_UNKNOWN;
+ break;
+ }
+ return (cfgtype);
+}
+
+/*
+ * Channel related IPMI commands reserve 4 bits for the channel number.
+ */
+#define IPMI_MAX_CHANNEL 0xf
+
+static int
+ipmi_enum_sp(topo_mod_t *mod, tnode_t *pnode)
+{
+ ipmi_handle_t *ihp;
+ ipmi_channel_info_t *chinfo;
+ ipmi_lan_config_t lancfg = { 0 };
+ boolean_t found_lan = B_TRUE;
+ char ipv4_addr[INET_ADDRSTRLEN], subnet[INET_ADDRSTRLEN];
+ char gateway[INET_ADDRSTRLEN], macaddr[18];
+ char ipv6_addr[INET6_ADDRSTRLEN];
+ char **ipv6_routes = NULL;
+ const char *sp_rev, *ipv4_cfgtype, *ipv6_cfgtype;
+ nvlist_t *auth, *fmri;
+ tnode_t *sp_node;
+ topo_pgroup_info_t pgi;
+ topo_ufm_slot_info_t slotinfo = { 0 };
+ int err, ch, i, ret = -1;
+
+ if ((ihp = topo_mod_ipmi_hold(mod)) == NULL)
+ return (0);
+
+ /*
+ * If we're able to successfully get the service processor version by
+ * issuing a GET_DEVICE_ID IPMI command over the KCS interface, then we
+ * can say with certainty that a service processor exists. If not,
+ * then either the SP is unresponsive or one isn't present. In either
+ * case, we bail.
+ */
+ if ((sp_rev = ipmi_firmware_version(ihp)) == NULL) {
+ topo_mod_dprintf(mod, "failed to query SP");
+ topo_mod_ipmi_rele(mod);
+ return (0);
+ }
+
+ if ((auth = topo_mod_auth(mod, pnode)) == NULL) {
+ topo_mod_dprintf(mod, "topo_mod_auth() failed: %s",
+ topo_mod_errmsg(mod));
+ /* errno set */
+ goto out;
+ }
+ if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
+ SP, 0, NULL, auth, NULL, sp_rev, NULL)) == NULL) {
+ nvlist_free(auth);
+ topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
+ topo_mod_errmsg(mod));
+ /* errno set */
+ goto out;
+ }
+ nvlist_free(auth);
+
+ if ((sp_node = topo_node_bind(mod, pnode, SP, 0, fmri)) == NULL) {
+ nvlist_free(fmri);
+ topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
+ topo_mod_errmsg(mod));
+ /* errno set */
+ goto out;
+ }
+ nvlist_free(fmri);
+ fmri = NULL;
+
+ if (topo_node_label_set(sp_node, "service-processor", &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set label on %s=%d: %s", SP,
+ 0, topo_strerror(err));
+ (void) topo_mod_seterrno(mod, err);
+ goto out;
+ }
+
+ if (topo_node_fru(pnode, &fmri, NULL, &err) != 0 ||
+ topo_node_fru_set(sp_node, fmri, 0, &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set FRU on %s=%d: %s", SP, 0,
+ topo_strerror(err));
+ nvlist_free(fmri);
+ (void) topo_mod_seterrno(mod, err);
+ goto out;
+ }
+ nvlist_free(fmri);
+
+ /*
+ * Create UFM node to capture the SP LOM version
+ */
+ slotinfo.usi_version = sp_rev;
+ slotinfo.usi_active = B_TRUE;
+ slotinfo.usi_mode = TOPO_UFM_SLOT_MODE_NONE;
+ if (topo_node_range_create(mod, sp_node, UFM, 0, 0) != 0 ||
+ topo_mod_create_ufm(mod, sp_node,
+ "Baseboard Management Controller firmware", &slotinfo) == NULL) {
+ topo_mod_dprintf(mod, "failed to create %s node", UFM);
+ goto out;
+ }
+
+ /*
+ * Iterate through the channels to find the LAN channel.
+ */
+ for (ch = 0; ch <= IPMI_MAX_CHANNEL; ch++) {
+ if ((chinfo = ipmi_get_channel_info(ihp, ch)) != NULL &&
+ chinfo->ici_medium == IPMI_MEDIUM_8023LAN) {
+ found_lan = B_TRUE;
+ break;
+ }
+ }
+ /*
+ * If we found a LAN channel, look up its configuration so that we can
+ * expose it via node properties.
+ */
+ if (found_lan != B_TRUE ||
+ ipmi_lan_get_config(ihp, ch, &lancfg) != 0) {
+ (void) fprintf(stderr, "failed to get LAN config\n");
+ (void) topo_mod_seterrno(mod, EMOD_UNKNOWN);
+ goto out;
+ }
+
+ pgi.tpi_name = TOPO_PGROUP_NETCFG;
+ pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
+ pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
+ pgi.tpi_version = TOPO_VERSION;
+
+ if (topo_pgroup_create(sp_node, &pgi, &err) != 0) {
+ (void) topo_mod_seterrno(mod, err);
+ goto out;
+ }
+
+ /* Set MAC address property */
+ (void) sprintf(macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
+ lancfg.ilc_macaddr[0], lancfg.ilc_macaddr[1],
+ lancfg.ilc_macaddr[2], lancfg.ilc_macaddr[3],
+ lancfg.ilc_macaddr[4], lancfg.ilc_macaddr[5]);
+ macaddr[17] = '\0';
+
+ if (topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG,
+ TOPO_PROP_NETCFG_MACADDR, TOPO_PROP_IMMUTABLE, macaddr,
+ &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set properties on %s=%d: %s",
+ SP, 0, topo_strerror(err));
+ (void) topo_mod_seterrno(mod, err);
+ goto out;
+ }
+
+ /* Set VLAN ID property, if VLAN is enabled */
+ if (lancfg.ilc_vlan_enabled == B_TRUE &&
+ topo_prop_set_uint32(sp_node, TOPO_PGROUP_NETCFG,
+ TOPO_PROP_NETCFG_VLAN_ID, TOPO_PROP_IMMUTABLE, lancfg.ilc_vlan_id,
+ &err) != 0) {
+ topo_mod_dprintf(mod, "failed to set properties on %s=%d: %s",
+ SP, 0, topo_strerror(err));
+ (void) topo_mod_seterrno(mod, err);
+ goto out;
+ }
+
+ /* Set IPv4 configuration properties if IPv4 is enabled */
+ if (lancfg.ilc_ipv4_enabled == B_TRUE &&
+ (inet_ntop(AF_INET, &lancfg.ilc_ipaddr, ipv4_addr,
+ sizeof (ipv4_addr)) == NULL ||
+ inet_ntop(AF_INET, &lancfg.ilc_subnet, subnet,
+ sizeof (subnet)) == NULL ||
+ inet_ntop(AF_INET, &lancfg.ilc_gateway_addr, gateway,
+ sizeof (gateway)) == NULL)) {
+ (void) fprintf(stderr, "failed to convert IP addresses: %s\n",
+ strerror(errno));
+ (void) topo_mod_seterrno(mod, EMOD_UNKNOWN);
+ goto out;
+ }
+ ipv4_cfgtype = ipmi2toposrc(lancfg.ilc_ipaddr_source);
+ if (lancfg.ilc_ipv4_enabled == B_TRUE &&
+ (topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG,
+ TOPO_PROP_NETCFG_IPV4_ADDR, TOPO_PROP_IMMUTABLE, ipv4_addr,
+ &err) != 0 ||
+ topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG,
+ TOPO_PROP_NETCFG_IPV4_SUBNET, TOPO_PROP_IMMUTABLE, subnet,
+ &err) != 0 ||
+ topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG,
+ TOPO_PROP_NETCFG_IPV4_GATEWAY, TOPO_PROP_IMMUTABLE, gateway,
+ &err) != 0 ||
+ topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG,
+ TOPO_PROP_NETCFG_IPV4_TYPE, TOPO_PROP_IMMUTABLE, ipv4_cfgtype,
+ &err) != 0)) {
+ topo_mod_dprintf(mod, "failed to set properties on %s=%d: %s",
+ SP, 0, topo_strerror(err));
+ (void) topo_mod_seterrno(mod, err);
+ goto out;
+ }
+
+ /* Set IPv6 configuration properties if IPv6 is enabled */
+ if (lancfg.ilc_ipv6_enabled == B_TRUE) {
+ ipv6_cfgtype = ipmi2toposrc(lancfg.ilc_ipv6_source);
+
+ if (inet_ntop(AF_INET6, &lancfg.ilc_ipv6_addr, ipv6_addr,
+ sizeof (ipv6_addr)) == NULL) {
+ (void) fprintf(stderr, "failed to convert IPv6 "
+ "address: %s\n", strerror(errno));
+ (void) topo_mod_seterrno(mod, EMOD_UNKNOWN);
+ goto out;
+ }
+
+ /* allocate and populate ipv6-routes string array */
+ if ((ipv6_routes = topo_mod_zalloc(mod,
+ lancfg.ilc_ipv6_nroutes * sizeof (char *))) == NULL) {
+ /* errno set */
+ goto out;
+ }
+ for (i = 0; i < lancfg.ilc_ipv6_nroutes; i++) {
+ if ((ipv6_routes[i] = topo_mod_alloc(mod,
+ INET6_ADDRSTRLEN)) == NULL) {
+ /* errno set */
+ goto out;
+ }
+ }
+ for (i = 0; i < lancfg.ilc_ipv6_nroutes; i++) {
+ if (inet_ntop(AF_INET6, &lancfg.ilc_ipv6_routes[i],
+ ipv6_routes[i], sizeof (ipv6_routes[i])) == NULL) {
+ (void) fprintf(stderr, "failed to convert "
+ "IPv6 addresses: %s\n", strerror(errno));
+ (void) topo_mod_seterrno(mod, EMOD_UNKNOWN);
+ goto out;
+ }
+ }
+ }
+ if (lancfg.ilc_ipv6_enabled == B_TRUE &&
+ (topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG,
+ TOPO_PROP_NETCFG_IPV6_ADDR, TOPO_PROP_IMMUTABLE, ipv6_addr,
+ &err) != 0 ||
+ topo_prop_set_string_array(sp_node, TOPO_PGROUP_NETCFG,
+ TOPO_PROP_NETCFG_IPV6_ROUTES, TOPO_PROP_IMMUTABLE,
+ (const char **)ipv6_routes, lancfg.ilc_ipv6_nroutes, &err) != 0 ||
+ topo_prop_set_string(sp_node, TOPO_PGROUP_NETCFG,
+ TOPO_PROP_NETCFG_IPV6_TYPE, TOPO_PROP_IMMUTABLE, ipv6_cfgtype,
+ &err) != 0)) {
+ topo_mod_dprintf(mod, "failed to set properties on %s=%d: %s",
+ SP, 0, topo_strerror(err));
+ (void) topo_mod_seterrno(mod, err);
+ goto out;
+ }
+ ret = 0;
+out:
+ if (lancfg.ilc_ipv6_nroutes > 0 && ipv6_routes != NULL) {
+ for (i = 0; i < lancfg.ilc_ipv6_nroutes; i++) {
+ if (ipv6_routes[i] != NULL) {
+ topo_mod_free(mod, ipv6_routes[i],
+ INET6_ADDRSTRLEN);
+ }
+ }
+ topo_mod_free(mod, ipv6_routes,
+ lancfg.ilc_ipv6_nroutes * sizeof (char *));
+ }
+ topo_mod_ipmi_rele(mod);
+ return (ret);
+}
+
/*
* libtopo enumeration point. This simply iterates over entities looking for
* the appropriate type.
@@ -613,10 +888,11 @@ ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
int ret;
/*
- * If the node being passed in ISN'T the chassis node, then we're being
- * asked to post-process a statically defined node.
+ * If the node being passed in ISN'T the chassis or motherboard node,
+ * then we're being asked to post-process a statically defined node.
*/
- if (strcmp(topo_node_name(rnode), CHASSIS) != 0) {
+ if (strcmp(topo_node_name(rnode), CHASSIS) != 0 &&
+ strcmp(topo_node_name(rnode), MOTHERBOARD) != 0) {
if (ipmi_post_process(mod, rnode) != 0) {
topo_mod_dprintf(mod, "post processing of node %s=%d "
"failed!", topo_node_name(rnode),
@@ -626,6 +902,19 @@ ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
return (0);
}
+ /*
+ * For service processor enumeration we vector off into a special code
+ * path.
+ */
+ if (strcmp(name, SP) == 0) {
+ if (ipmi_enum_sp(mod, rnode) != 0) {
+ topo_mod_dprintf(mod, "failed to enumerate the "
+ "service-processor");
+ return (-1);
+ }
+ return (0);
+ }
+
if (strcmp(name, POWERMODULE) == 0) {
data.ed_entity = IPMI_ET_POWER_DOMAIN;
} else if (strcmp(name, PSU) == 0) {
diff --git a/usr/src/lib/fm/topo/modules/common/ses/ses.c b/usr/src/lib/fm/topo/modules/common/ses/ses.c
index 8d74b8aeb8..0273009bbe 100644
--- a/usr/src/lib/fm/topo/modules/common/ses/ses.c
+++ b/usr/src/lib/fm/topo/modules/common/ses/ses.c
@@ -340,6 +340,34 @@ static ses_open_fail_list_t *ses_sofh;
static pthread_mutex_t ses_sofmt;
static void ses_ct_print(char *ptr);
+/*
+ * Static list of enclosure manufacturers/models that we will skip enumerating.
+ * To skip all models from a particular vendor, set the seb_model field to "*".
+ */
+typedef struct ses_enc_blacklist {
+ const char *seb_manuf;
+ const char *seb_model;
+} ses_enc_blacklist_t;
+
+static const ses_enc_blacklist_t enc_blacklist[] = {
+ { "LSI", "VirtualSES" }
+};
+
+#define N_ENC_BLACKLIST (sizeof (enc_blacklist) / \
+ sizeof (enc_blacklist[0]))
+
+static boolean_t
+ses_is_blacklisted(ses_enc_blacklist_t *seb)
+{
+ for (uint_t i = 0; i < N_ENC_BLACKLIST; i++) {
+ if (strcmp(seb->seb_manuf, enc_blacklist[i].seb_manuf) == 0 &&
+ (strcmp(enc_blacklist[i].seb_model, "*") == 0 ||
+ strcmp(seb->seb_model, enc_blacklist[i].seb_model) == 0))
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
static void
ses_recheck_dir()
{
@@ -3298,9 +3326,24 @@ ses_enum_gather(ses_node_t *np, void *data)
uint64_t prevstatus, status;
boolean_t report;
uint64_t subchassis = NO_SUBCHASSIS;
+ ses_enc_blacklist_t seb;
if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
/*
+ * Compare the enclosure identity against the entries in the SES
+ * enclosure blacklist and ignore it, if found.
+ */
+ verify(nvlist_lookup_string(props, SES_EN_PROP_VID,
+ (char **)&seb.seb_manuf) == 0);
+ verify(nvlist_lookup_string(props, SES_EN_PROP_PID,
+ (char **)&seb.seb_model) == 0);
+ if (ses_is_blacklisted(&seb) == B_TRUE) {
+ topo_mod_dprintf(mod, "Skipping enclosure %s-%s: is "
+ "blacklisted", seb.seb_manuf, seb.seb_model);
+ return (SES_WALK_ACTION_TERMINATE);
+ }
+
+ /*
* If we have already identified the chassis for this target,
* then this is a secondary enclosure and we should ignore it,
* along with the rest of the tree (since this is depth-first).
diff --git a/usr/src/lib/json_nvlist/json_nvlist.c b/usr/src/lib/json_nvlist/json_nvlist.c
index f365c981f3..38d0508e1c 100644
--- a/usr/src/lib/json_nvlist/json_nvlist.c
+++ b/usr/src/lib/json_nvlist/json_nvlist.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2018 Joyent, Inc.
*/
#include <stdio.h>
diff --git a/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com b/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com
index 73f90c4777..82b2ef4ab5 100644
--- a/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com
+++ b/usr/src/lib/krb5/plugins/preauth/pkinit/Makefile.com
@@ -73,8 +73,7 @@ SMATCH=off
CFLAGS += $(CCVERBOSE) -I..
DYNFLAGS += $(KRUNPATH) $(KMECHLIB) $(ZNODELETE)
-LDLIBS += -L $(ROOTLIBDIR) -lcrypto -lc
-NATIVE_LIBS += libcrypto.so
+LDLIBS += -L $(ROOTLIBDIR) -lsunw_crypto -lc
ROOTLIBDIR= $(ROOT)/usr/lib/krb5/plugins/preauth
diff --git a/usr/src/lib/libbe/common/be_list.c b/usr/src/lib/libbe/common/be_list.c
index 17473e986c..85b62eff38 100644
--- a/usr/src/lib/libbe/common/be_list.c
+++ b/usr/src/lib/libbe/common/be_list.c
@@ -25,6 +25,7 @@
/*
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2011 Joyent, Inc. All rights reserved.
* Copyright 2015 Toomas Soome <tsoome@me.com>
* Copyright 2015 Gary Mills
* Copyright (c) 2016 Martin Matuska. All rights reserved.
diff --git a/usr/src/lib/libbrand/common/libbrand.c b/usr/src/lib/libbrand/common/libbrand.c
index 3112e31771..afd03abb02 100644
--- a/usr/src/lib/libbrand/common/libbrand.c
+++ b/usr/src/lib/libbrand/common/libbrand.c
@@ -326,6 +326,7 @@ i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size,
const char *curr_zone)
{
int dst, src;
+ static char *env_pool = NULL;
/*
* Walk through the characters, substituting values as needed.
@@ -342,6 +343,13 @@ i_substitute_tokens(const char *sbuf, char *dbuf, int dbuf_size,
case '%':
dst += strlcpy(dbuf + dst, "%", dbuf_size - dst);
break;
+ case 'P':
+ if (env_pool == NULL)
+ env_pool = getenv("_ZONEADMD_ZPOOL");
+ if (env_pool == NULL)
+ break;
+ dst += strlcpy(dbuf + dst, env_pool, dbuf_size - dst);
+ break;
case 'R':
if (zonepath == NULL)
break;
@@ -847,6 +855,7 @@ i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonename,
xmlNodePtr node;
xmlChar *special, *dir, *type, *opt;
char special_exp[MAXPATHLEN];
+ char dir_exp[MAXPATHLEN];
char opt_exp[MAXPATHLEN];
int ret;
@@ -873,6 +882,10 @@ i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonename,
special_exp, sizeof (special_exp),
zonename, zonepath, NULL, NULL)) != 0)
goto next;
+ if ((ret = i_substitute_tokens((char *)dir,
+ dir_exp, sizeof (dir_exp),
+ zonename, zonepath, NULL, NULL)) != 0)
+ goto next;
/* opt might not be defined */
if (strlen((const char *)opt) == 0) {
@@ -885,7 +898,7 @@ i_brand_platform_iter_mounts(struct brand_handle *bhp, const char *zonename,
goto next;
}
- ret = func(data, (char *)special_exp, (char *)dir,
+ ret = func(data, (char *)special_exp, (char *)dir_exp,
(char *)type, ((opt != NULL) ? opt_exp : NULL));
next:
diff --git a/usr/src/lib/libbsm/common/adt.c b/usr/src/lib/libbsm/common/adt.c
index 7ab93647a0..3e0f4ec2c5 100644
--- a/usr/src/lib/libbsm/common/adt.c
+++ b/usr/src/lib/libbsm/common/adt.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, Joyent, Inc. All rights reserved.
* Copyright 2017 OmniOS Community Edition (OmniOSce) Association.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
@@ -754,7 +755,37 @@ adt_get_hostIP(const char *hostname, au_tid_addr_t *p_term)
int tries = 3;
char msg[512];
int eai_err;
+ struct ifaddrlist al;
+ int family;
+ boolean_t found = B_FALSE;
+ /*
+ * getaddrinfo can take a long time to timeout if it can't map the
+ * hostname to an IP address so try to get an IP address from a local
+ * interface first.
+ */
+ family = AF_INET6;
+ if (adt_get_local_address(family, &al) == 0) {
+ found = B_TRUE;
+ } else {
+ family = AF_INET;
+ if (adt_get_local_address(family, &al) == 0)
+ found = B_TRUE;
+ }
+
+ if (found) {
+ if (family == AF_INET) {
+ p_term->at_type = AU_IPv4;
+ (void) memcpy(p_term->at_addr, &al.addr.addr, AU_IPv4);
+ } else {
+ p_term->at_type = AU_IPv6;
+ (void) memcpy(p_term->at_addr, &al.addr.addr6, AU_IPv6);
+ }
+
+ return (0);
+ }
+
+ /* Now try getaddrinfo */
while ((tries-- > 0) &&
((eai_err = getaddrinfo(hostname, NULL, NULL, &ai)) != 0)) {
/*
@@ -791,7 +822,9 @@ adt_get_hostIP(const char *hostname, au_tid_addr_t *p_term)
}
freeaddrinfo(ai);
return (0);
- } else if (auditstate & (AUC_AUDITING | AUC_NOSPACE)) {
+ }
+
+ if (auditstate & (AUC_AUDITING | AUC_NOSPACE)) {
auditinfo_addr_t audit_info;
/*
@@ -799,58 +832,23 @@ adt_get_hostIP(const char *hostname, au_tid_addr_t *p_term)
* kernel audit context
*/
if (auditon(A_GETKAUDIT, (caddr_t)&audit_info,
- sizeof (audit_info)) < 0) {
- adt_write_syslog("unable to get kernel audit context",
- errno);
- goto try_interface;
+ sizeof (audit_info)) >= 0) {
+ adt_write_syslog("setting Audit IP address to kernel",
+ 0);
+ *p_term = audit_info.ai_termid;
+ return (0);
}
- adt_write_syslog("setting Audit IP address to kernel", 0);
- *p_term = audit_info.ai_termid;
- return (0);
+ adt_write_syslog("unable to get kernel audit context", errno);
}
-try_interface:
- {
- struct ifaddrlist al;
- int family;
- char ntop[INET6_ADDRSTRLEN];
-
- /*
- * getaddrinfo has failed to map the hostname
- * to an IP address, try to get an IP address
- * from a local interface. If none up, default
- * to loopback.
- */
- family = AF_INET6;
- if (adt_get_local_address(family, &al) != 0) {
- family = AF_INET;
-
- if (adt_get_local_address(family, &al) != 0) {
- adt_write_syslog("adt_get_local_address "
- "failed, no Audit IP address available, "
- "faking loopback and error",
- errno);
- IN_SET_LOOPBACK_ADDR(
- (struct sockaddr_in *)&(al.addr.addr));
- (void) memcpy(p_term->at_addr, &al.addr.addr,
- AU_IPv4);
- p_term->at_type = AU_IPv4;
- return (-1);
- }
- }
- if (family == AF_INET) {
- p_term->at_type = AU_IPv4;
- (void) memcpy(p_term->at_addr, &al.addr.addr, AU_IPv4);
- } else {
- p_term->at_type = AU_IPv6;
- (void) memcpy(p_term->at_addr, &al.addr.addr6, AU_IPv6);
- }
- (void) snprintf(msg, sizeof (msg), "mapping %s to %s",
- hostname, inet_ntop(family, &(al.addr), ntop,
- sizeof (ntop)));
- adt_write_syslog(msg, 0);
- return (0);
- }
+ /* No mapping, default to loopback. */
+ errno = ENETDOWN;
+ adt_write_syslog("adt_get_local_address failed, no Audit IP address "
+ "available, faking loopback and error", errno);
+ IN_SET_LOOPBACK_ADDR((struct sockaddr_in *)&(al.addr.addr));
+ (void) memcpy(p_term->at_addr, &al.addr.addr, AU_IPv4);
+ p_term->at_type = AU_IPv4;
+ return (-1);
}
/*
@@ -2145,8 +2143,8 @@ adt_selected(struct adt_event_state *event, au_event_t actual_id, int status)
}
/*
- * Can't map the host name to an IP address in
- * adt_get_hostIP. Get something off an interface
+ * Before trying to map the host name to an IP address in
+ * adt_get_hostIP, get something off an interface
* to act as the hosts IP address for auditing.
*/
diff --git a/usr/src/lib/libbunyan/Makefile b/usr/src/lib/libbunyan/Makefile
new file mode 100644
index 0000000000..a59de91113
--- /dev/null
+++ b/usr/src/lib/libbunyan/Makefile
@@ -0,0 +1,42 @@
+#
+# 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.
+#
+
+include ../Makefile.lib
+
+HDRS = bunyan.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libbunyan/Makefile.com b/usr/src/lib/libbunyan/Makefile.com
new file mode 100644
index 0000000000..d0d00bf0e5
--- /dev/null
+++ b/usr/src/lib/libbunyan/Makefile.com
@@ -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 2020 Joyent, Inc.
+#
+
+LIBRARY = libbunyan.a
+VERS = .1
+OBJECTS = bunyan.o
+USDT_PROVIDERS = bunyan_provider.d
+
+include ../../Makefile.lib
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc -lumem -lnvpair
+CPPFLAGS += -I../common -I. -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
+
+SRCDIR = ../common
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
+include ../../Makefile.usdt
diff --git a/usr/src/lib/libbunyan/amd64/Makefile b/usr/src/lib/libbunyan/amd64/Makefile
new file mode 100644
index 0000000000..15d904c616
--- /dev/null
+++ b/usr/src/lib/libbunyan/amd64/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.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libbunyan/common/bunyan.c b/usr/src/lib/libbunyan/common/bunyan.c
new file mode 100644
index 0000000000..a442c33cec
--- /dev/null
+++ b/usr/src/lib/libbunyan/common/bunyan.c
@@ -0,0 +1,913 @@
+/*
+ * 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.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <umem.h>
+#include <netdb.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/sysmacros.h>
+#include <thread.h>
+#include <sys/debug.h>
+
+#include <bunyan.h>
+#include <bunyan_provider_impl.h>
+
+struct bunyan_key;
+struct bunyan_stream;
+struct bunyan;
+
+typedef struct bunyan_stream {
+ struct bunyan_stream *bs_next;
+ char *bs_name;
+ bunyan_level_t bs_level;
+ bunyan_stream_f bs_func;
+ void *bs_arg;
+ uint_t bs_count;
+} bunyan_stream_t;
+
+typedef struct bunyan_key {
+ struct bunyan_key *bk_next;
+ char *bk_name;
+ bunyan_type_t bk_type;
+ void *bk_data;
+ size_t bk_len;
+} bunyan_key_t;
+
+typedef struct bunyan {
+ pthread_mutex_t bun_lock;
+ bunyan_key_t *bun_keys;
+ bunyan_stream_t *bun_streams;
+ char *bun_name;
+ char bun_host[MAXHOSTNAMELEN+1];
+} bunyan_t;
+
+#define ISO_TIMELEN 25
+static const int bunyan_version = 0;
+
+static void
+bunyan_key_fini(bunyan_key_t *bkp)
+{
+ size_t nlen = strlen(bkp->bk_name) + 1;
+ umem_free(bkp->bk_data, bkp->bk_len);
+ umem_free(bkp->bk_name, nlen);
+ umem_free(bkp, sizeof (bunyan_key_t));
+}
+
+static void
+bunyan_stream_fini(bunyan_stream_t *bsp)
+{
+ size_t nlen = strlen(bsp->bs_name) + 1;
+ umem_free(bsp->bs_name, nlen);
+ umem_free(bsp, sizeof (bunyan_stream_t));
+}
+
+int
+bunyan_init(const char *name, bunyan_logger_t **bhp)
+{
+ int ret;
+ bunyan_t *b;
+ size_t nlen = strlen(name) + 1;
+
+ b = umem_zalloc(sizeof (bunyan_t), UMEM_DEFAULT);
+ if (b == NULL)
+ return (ENOMEM);
+
+ b->bun_name = umem_alloc(nlen, UMEM_DEFAULT);
+ if (b->bun_name == NULL) {
+ umem_free(b, sizeof (bunyan_t));
+ return (ENOMEM);
+ }
+ bcopy(name, b->bun_name, nlen);
+
+ if ((ret = pthread_mutex_init(&b->bun_lock, NULL)) != 0) {
+ umem_free(b->bun_name, nlen);
+ umem_free(b, sizeof (bunyan_t));
+ return (ret);
+ }
+
+ VERIFY(gethostname(b->bun_host, sizeof (b->bun_host)) == 0);
+ b->bun_host[MAXHOSTNAMELEN] = '\0';
+
+ *bhp = (bunyan_logger_t *)b;
+ return (0);
+}
+
+void
+bunyan_fini(bunyan_logger_t *bhp)
+{
+ bunyan_t *b = (bunyan_t *)bhp;
+ bunyan_key_t *bkp;
+ bunyan_stream_t *bsp;
+
+ while ((bkp = b->bun_keys) != NULL) {
+ b->bun_keys = bkp->bk_next;
+ bunyan_key_fini(bkp);
+ }
+
+ while ((bsp = b->bun_streams) != NULL) {
+ b->bun_streams = bsp->bs_next;
+ bunyan_stream_fini(bsp);
+ }
+
+ if (b->bun_name != NULL)
+ umem_free(b->bun_name, strlen(b->bun_name) + 1);
+
+ VERIFY(pthread_mutex_destroy(&b->bun_lock) == 0);
+ umem_free(b, sizeof (bunyan_t));
+}
+
+/* ARGSUSED */
+int
+bunyan_stream_fd(nvlist_t *nvl, const char *js, void *arg)
+{
+ uintptr_t fd = (uintptr_t)arg;
+ size_t jslen = strlen(js);
+ off_t off = 0;
+ ssize_t ret = 0;
+ static int maxbuf = -1;
+
+ if (maxbuf == -1)
+ maxbuf = getpagesize();
+
+ while (off != jslen) {
+ /*
+ * Write up to a page of data at a time. If for some reason an
+ * individual write fails, move on and try to still write a new
+ * line at least...
+ */
+ ret = write(fd, js + off, MIN(jslen - off, maxbuf));
+ if (ret < 0)
+ break;
+ off += ret;
+ }
+
+ if (ret < 0) {
+ (void) write(fd, "\n", 1);
+ } else {
+ ret = write(fd, "\n", 1);
+ }
+ return (ret < 0 ? 1: 0);
+}
+
+int
+bunyan_stream_add(bunyan_logger_t *bhp, const char *name, int level,
+ bunyan_stream_f func, void *arg)
+{
+ bunyan_stream_t *bs, *cur;
+ size_t nlen = strlen(name) + 1;
+ bunyan_t *b = (bunyan_t *)bhp;
+
+ if (level != BUNYAN_L_TRACE &&
+ level != BUNYAN_L_DEBUG &&
+ level != BUNYAN_L_INFO &&
+ level != BUNYAN_L_WARN &&
+ level != BUNYAN_L_ERROR &&
+ level != BUNYAN_L_FATAL)
+ return (EINVAL);
+
+ bs = umem_alloc(sizeof (bunyan_stream_t), UMEM_DEFAULT);
+ if (bs == NULL)
+ return (ENOMEM);
+
+ bs->bs_name = umem_alloc(nlen, UMEM_DEFAULT);
+ if (bs->bs_name == NULL) {
+ umem_free(bs, sizeof (bunyan_stream_t));
+ return (ENOMEM);
+ }
+ bcopy(name, bs->bs_name, nlen);
+ bs->bs_level = level;
+ bs->bs_func = func;
+ bs->bs_arg = arg;
+ bs->bs_count = 0;
+ (void) pthread_mutex_lock(&b->bun_lock);
+ cur = b->bun_streams;
+ while (cur != NULL) {
+ if (strcmp(name, cur->bs_name) == 0) {
+ (void) pthread_mutex_unlock(&b->bun_lock);
+ umem_free(bs->bs_name, nlen);
+ umem_free(bs, sizeof (bunyan_stream_t));
+ return (EEXIST);
+ }
+ cur = cur->bs_next;
+ }
+ bs->bs_next = b->bun_streams;
+ b->bun_streams = bs;
+ (void) pthread_mutex_unlock(&b->bun_lock);
+
+ return (0);
+}
+
+int
+bunyan_stream_remove(bunyan_logger_t *bhp, const char *name)
+{
+ bunyan_stream_t *cur, *prev;
+ bunyan_t *b = (bunyan_t *)bhp;
+
+ (void) pthread_mutex_lock(&b->bun_lock);
+ prev = NULL;
+ cur = b->bun_streams;
+ while (cur != NULL) {
+ if (strcmp(name, cur->bs_name) == 0)
+ break;
+ prev = cur;
+ cur = cur->bs_next;
+ }
+ if (cur == NULL) {
+ (void) pthread_mutex_unlock(&b->bun_lock);
+ return (ENOENT);
+ }
+ if (prev == NULL)
+ b->bun_streams = cur->bs_next;
+ else
+ prev->bs_next = cur->bs_next;
+ cur->bs_next = NULL;
+ (void) pthread_mutex_unlock(&b->bun_lock);
+
+ bunyan_stream_fini(cur);
+
+ return (0);
+}
+
+static int
+bunyan_key_add_one(bunyan_t *b, const char *name, bunyan_type_t type,
+ const void *arg)
+{
+ bunyan_key_t *bkp, *cur, *prev;
+ size_t nlen = strlen(name) + 1;
+ size_t blen;
+
+ bkp = umem_alloc(sizeof (bunyan_key_t), UMEM_DEFAULT);
+ if (bkp == NULL)
+ return (ENOMEM);
+ bkp->bk_name = umem_alloc(nlen, UMEM_DEFAULT);
+ if (bkp->bk_name == NULL) {
+ umem_free(bkp, sizeof (bunyan_key_t));
+ return (ENOMEM);
+ }
+ bcopy(name, bkp->bk_name, nlen);
+
+ switch (type) {
+ case BUNYAN_T_STRING:
+ blen = strlen(arg) + 1;
+ break;
+ case BUNYAN_T_POINTER:
+ blen = sizeof (uintptr_t);
+ break;
+ case BUNYAN_T_IP:
+ blen = sizeof (struct in_addr);
+ break;
+ case BUNYAN_T_IP6:
+ blen = sizeof (struct in6_addr);
+ break;
+ case BUNYAN_T_BOOLEAN:
+ blen = sizeof (boolean_t);
+ break;
+ case BUNYAN_T_INT32:
+ blen = sizeof (int32_t);
+ break;
+ case BUNYAN_T_INT64:
+ case BUNYAN_T_INT64STR:
+ blen = sizeof (int64_t);
+ break;
+ case BUNYAN_T_UINT32:
+ blen = sizeof (uint32_t);
+ break;
+ case BUNYAN_T_UINT64:
+ case BUNYAN_T_UINT64STR:
+ blen = sizeof (uint64_t);
+ break;
+ case BUNYAN_T_DOUBLE:
+ blen = sizeof (double);
+ break;
+ default:
+ umem_free(bkp->bk_name, nlen);
+ umem_free(bkp, sizeof (bunyan_key_t));
+ return (EINVAL);
+ }
+
+ bkp->bk_data = umem_alloc(blen, UMEM_DEFAULT);
+ if (bkp->bk_data == NULL) {
+ umem_free(bkp->bk_name, nlen);
+ umem_free(bkp, sizeof (bunyan_key_t));
+ return (ENOMEM);
+ }
+ bcopy(arg, bkp->bk_data, blen);
+ bkp->bk_len = blen;
+ bkp->bk_type = type;
+
+ (void) pthread_mutex_lock(&b->bun_lock);
+ prev = NULL;
+ cur = b->bun_keys;
+ while (cur != NULL) {
+ if (strcmp(name, cur->bk_name) == 0)
+ break;
+ prev = cur;
+ cur = cur->bk_next;
+ }
+ if (cur != NULL) {
+ if (prev == NULL)
+ b->bun_keys = cur->bk_next;
+ else
+ prev->bk_next = cur->bk_next;
+ bunyan_key_fini(cur);
+ }
+ bkp->bk_next = b->bun_keys;
+ b->bun_keys = bkp;
+ (void) pthread_mutex_unlock(&b->bun_lock);
+
+ return (0);
+}
+
+static int
+bunyan_key_vadd(bunyan_t *b, va_list *ap)
+{
+ int type, ret;
+ void *data;
+ boolean_t bt;
+ int32_t i32;
+ int64_t i64;
+ uint32_t ui32;
+ uint64_t ui64;
+ double d;
+ uintptr_t ptr;
+
+ while ((type = va_arg(*ap, int)) != BUNYAN_T_END) {
+ const char *name = va_arg(*ap, char *);
+
+ switch (type) {
+ case BUNYAN_T_STRING:
+ data = va_arg(*ap, char *);
+ break;
+ case BUNYAN_T_POINTER:
+ ptr = (uintptr_t)va_arg(*ap, void *);
+ data = &ptr;
+ break;
+ case BUNYAN_T_IP:
+ case BUNYAN_T_IP6:
+ data = va_arg(*ap, void *);
+ break;
+ case BUNYAN_T_BOOLEAN:
+ bt = va_arg(*ap, boolean_t);
+ data = &bt;
+ break;
+ case BUNYAN_T_INT32:
+ i32 = va_arg(*ap, int32_t);
+ data = &i32;
+ break;
+ case BUNYAN_T_INT64:
+ case BUNYAN_T_INT64STR:
+ i64 = va_arg(*ap, int64_t);
+ data = &i64;
+ break;
+ case BUNYAN_T_UINT32:
+ ui32 = va_arg(*ap, uint32_t);
+ data = &ui32;
+ break;
+ case BUNYAN_T_UINT64:
+ case BUNYAN_T_UINT64STR:
+ ui64 = va_arg(*ap, uint64_t);
+ data = &ui64;
+ break;
+ case BUNYAN_T_DOUBLE:
+ d = va_arg(*ap, double);
+ data = &d;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((ret = bunyan_key_add_one(b, name, type, data)) != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+int
+bunyan_key_add(bunyan_logger_t *bhp, ...)
+{
+ int ret;
+ va_list ap;
+ bunyan_t *b = (bunyan_t *)bhp;
+
+ va_start(ap, bhp);
+ ret = bunyan_key_vadd(b, &ap);
+ va_end(ap);
+
+ return (ret);
+}
+
+int
+bunyan_key_remove(bunyan_logger_t *bhp, const char *name)
+{
+ bunyan_t *b = (bunyan_t *)bhp;
+ bunyan_key_t *cur, *prev;
+
+ (void) pthread_mutex_lock(&b->bun_lock);
+ prev = NULL;
+ cur = b->bun_keys;
+ while (cur != NULL) {
+ if (strcmp(name, cur->bk_name) == 0)
+ break;
+ prev = cur;
+ cur = cur->bk_next;
+ }
+
+ if (cur == NULL) {
+ (void) pthread_mutex_unlock(&b->bun_lock);
+ return (ENOENT);
+ }
+
+ if (prev == NULL)
+ b->bun_keys = cur->bk_next;
+ else
+ prev->bk_next = cur->bk_next;
+ (void) pthread_mutex_unlock(&b->bun_lock);
+
+ bunyan_key_fini(cur);
+ return (0);
+}
+
+static bunyan_key_t *
+bunyan_key_dup(const bunyan_key_t *bkp)
+{
+ bunyan_key_t *nkp;
+ size_t nlen = strlen(bkp->bk_name) + 1;
+
+ nkp = umem_alloc(sizeof (bunyan_key_t), UMEM_DEFAULT);
+ if (nkp == NULL)
+ return (NULL);
+ nkp->bk_next = NULL;
+ nkp->bk_name = umem_alloc(nlen, UMEM_DEFAULT);
+ if (nkp->bk_name == NULL) {
+ umem_free(nkp, sizeof (bunyan_key_t));
+ return (NULL);
+ }
+ bcopy(bkp->bk_name, nkp->bk_name, nlen);
+ nkp->bk_type = bkp->bk_type;
+ nkp->bk_data = umem_alloc(bkp->bk_len, UMEM_DEFAULT);
+ if (nkp->bk_data == NULL) {
+ umem_free(nkp->bk_name, nlen);
+ umem_free(nkp, sizeof (bunyan_key_t));
+ return (NULL);
+ }
+ bcopy(bkp->bk_data, nkp->bk_data, bkp->bk_len);
+ nkp->bk_len = bkp->bk_len;
+
+ return (nkp);
+}
+
+static bunyan_stream_t *
+bunyan_stream_dup(const bunyan_stream_t *bsp)
+{
+ bunyan_stream_t *nsp;
+ size_t nlen = strlen(bsp->bs_name) + 1;
+
+ nsp = umem_alloc(sizeof (bunyan_stream_t), UMEM_DEFAULT);
+ if (nsp == NULL)
+ return (NULL);
+
+ nsp->bs_next = NULL;
+ nsp->bs_name = umem_alloc(nlen, UMEM_DEFAULT);
+ if (nsp->bs_name == NULL) {
+ umem_free(nsp, sizeof (bunyan_stream_t));
+ return (NULL);
+ }
+ bcopy(bsp->bs_name, nsp->bs_name, nlen);
+ nsp->bs_level = bsp->bs_level;
+ nsp->bs_func = bsp->bs_func;
+ nsp->bs_arg = bsp->bs_arg;
+ nsp->bs_count = 0;
+
+ return (nsp);
+}
+
+static bunyan_t *
+bunyan_dup(const bunyan_t *b)
+{
+ bunyan_t *n;
+ const bunyan_key_t *bkp;
+ const bunyan_stream_t *bsp;
+ size_t nlen;
+
+ n = umem_zalloc(sizeof (bunyan_t), UMEM_DEFAULT);
+ if (n == NULL)
+ return (NULL);
+
+ if (pthread_mutex_init(&n->bun_lock, NULL) != 0) {
+ umem_free(n, sizeof (bunyan_t));
+ return (NULL);
+ }
+
+ for (bkp = b->bun_keys; bkp != NULL; bkp = bkp->bk_next) {
+ bunyan_key_t *nkp;
+ nkp = bunyan_key_dup(bkp);
+ if (nkp == NULL) {
+ bunyan_fini((bunyan_logger_t *)n);
+ return (NULL);
+ }
+
+ nkp->bk_next = n->bun_keys;
+ n->bun_keys = nkp;
+ }
+
+ for (bsp = b->bun_streams; bsp != NULL; bsp = bsp->bs_next) {
+ bunyan_stream_t *nsp;
+ nsp = bunyan_stream_dup(bsp);
+ if (bsp == NULL) {
+ bunyan_fini((bunyan_logger_t *)n);
+ return (NULL);
+ }
+
+ nsp->bs_next = n->bun_streams;
+ n->bun_streams = nsp;
+ }
+
+ nlen = strlen(b->bun_name) + 1;
+ n->bun_name = umem_alloc(nlen, UMEM_DEFAULT);
+ if (n->bun_name == NULL) {
+ bunyan_fini((bunyan_logger_t *)n);
+ return (NULL);
+ }
+ bcopy(b->bun_name, n->bun_name, nlen);
+ bcopy(b->bun_host, n->bun_host, MAXHOSTNAMELEN+1);
+
+ return (n);
+}
+
+int
+bunyan_child(const bunyan_logger_t *bhp, bunyan_logger_t **outp, ...)
+{
+ const bunyan_t *b = (const bunyan_t *)bhp;
+ bunyan_t *n;
+ va_list ap;
+ int ret;
+
+ n = bunyan_dup(b);
+ if (n == NULL)
+ return (ENOMEM);
+
+ va_start(ap, outp);
+ ret = bunyan_key_vadd(n, &ap);
+ va_end(ap);
+
+ if (ret != 0)
+ bunyan_fini((bunyan_logger_t *)n);
+ else
+ *outp = (bunyan_logger_t *)n;
+
+ return (ret);
+}
+
+static int
+bunyan_iso_time(char *buf)
+{
+ struct timeval tv;
+ struct tm tm;
+
+ if (gettimeofday(&tv, NULL) != 0)
+ return (errno);
+
+ if (gmtime_r(&tv.tv_sec, &tm) == NULL)
+ return (errno);
+
+ VERIFY(strftime(buf, ISO_TIMELEN, "%FT%T", &tm) == 19);
+
+ (void) snprintf(&buf[19], 6, ".%03dZ", (int)(tv.tv_usec / 1000));
+
+ return (0);
+}
+
+/*
+ * Note, these fields are all required, so even if a user attempts to use one of
+ * them in their own fields, we'll override them and therefore, have it be the
+ * last one.
+ */
+static int
+bunyan_vlog_defaults(nvlist_t *nvl, bunyan_t *b, bunyan_level_t level,
+ const char *msg)
+{
+ int ret;
+ char tbuf[ISO_TIMELEN];
+
+ if ((ret = bunyan_iso_time(tbuf)) != 0)
+ return (ret);
+
+ if ((ret = nvlist_add_int32(nvl, "v", bunyan_version)) != 0 ||
+ (ret = nvlist_add_int32(nvl, "level", level) != 0) ||
+ (ret = nvlist_add_string(nvl, "name", b->bun_name) != 0) ||
+ (ret = nvlist_add_string(nvl, "hostname", b->bun_host) != 0) ||
+ (ret = nvlist_add_int32(nvl, "pid", getpid()) != 0) ||
+ (ret = nvlist_add_uint32(nvl, "tid", thr_self()) != 0) ||
+ (ret = nvlist_add_string(nvl, "time", tbuf) != 0) ||
+ (ret = nvlist_add_string(nvl, "msg", msg) != 0))
+ return (ret);
+
+ return (0);
+}
+
+static int
+bunyan_vlog_add(nvlist_t *nvl, const char *key, bunyan_type_t type, void *arg)
+{
+ int ret;
+ uintptr_t *up;
+ struct in_addr *v4;
+ struct in6_addr *v6;
+
+ /*
+ * Our buffer needs to hold the string forms of pointers, IPv6 strings,
+ * etc. INET6_ADDRSTRLEN is large enough for all of these.
+ */
+ char buf[INET6_ADDRSTRLEN];
+
+ switch (type) {
+ case BUNYAN_T_STRING:
+ ret = nvlist_add_string(nvl, key, (char *)arg);
+ break;
+ case BUNYAN_T_POINTER:
+ up = arg;
+ (void) snprintf(buf, sizeof (buf), "0x%p", *up);
+ ret = nvlist_add_string(nvl, key, buf);
+ break;
+ case BUNYAN_T_IP:
+ v4 = arg;
+ VERIFY(inet_ntop(AF_INET, v4, buf, sizeof (buf)) != NULL);
+ ret = nvlist_add_string(nvl, key, buf);
+ break;
+ case BUNYAN_T_IP6:
+ v6 = arg;
+ VERIFY(inet_ntop(AF_INET6, v6, buf, sizeof (buf)) != NULL);
+ ret = nvlist_add_string(nvl, key, buf);
+ break;
+ case BUNYAN_T_BOOLEAN:
+ ret = nvlist_add_boolean_value(nvl, key, *(boolean_t *)arg);
+ break;
+ case BUNYAN_T_INT32:
+ ret = nvlist_add_int32(nvl, key, *(int32_t *)arg);
+ break;
+ case BUNYAN_T_INT64:
+ ret = nvlist_add_int64(nvl, key, *(int64_t *)arg);
+ break;
+ case BUNYAN_T_UINT32:
+ ret = nvlist_add_uint32(nvl, key, *(uint32_t *)arg);
+ break;
+ case BUNYAN_T_UINT64:
+ ret = nvlist_add_uint64(nvl, key, *(uint32_t *)arg);
+ break;
+ case BUNYAN_T_DOUBLE:
+ ret = nvlist_add_double(nvl, key, *(double *)arg);
+ break;
+ case BUNYAN_T_INT64STR:
+ (void) snprintf(buf, sizeof (buf), "%lld", *(int64_t *)arg);
+ ret = nvlist_add_string(nvl, key, buf);
+ break;
+ case BUNYAN_T_UINT64STR:
+ (void) snprintf(buf, sizeof (buf), "%llu", *(uint64_t *)arg);
+ ret = nvlist_add_string(nvl, key, buf);
+ break;
+ default:
+ ret = EINVAL;
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+bunyan_vlog(bunyan_logger_t *bhp, bunyan_level_t level, const char *msg,
+ va_list *ap)
+{
+ nvlist_t *nvl = NULL;
+ int ret, type;
+ bunyan_key_t *bkp;
+ bunyan_stream_t *bsp;
+ char *buf = NULL;
+ bunyan_t *b = (bunyan_t *)bhp;
+
+ if (msg == NULL)
+ return (EINVAL);
+
+ (void) pthread_mutex_lock(&b->bun_lock);
+
+ if ((ret = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0)) != 0) {
+ (void) pthread_mutex_unlock(&b->bun_lock);
+ return (ret);
+ }
+
+ /*
+ * We add pre-defined keys, then go through and process the users keys,
+ * and finally go ahead and our defaults. If all that succeeds, then we
+ * can go ahead and call all the built-in logs.
+ */
+ for (bkp = b->bun_keys; bkp != NULL; bkp = bkp->bk_next) {
+ if ((ret = bunyan_vlog_add(nvl, bkp->bk_name, bkp->bk_type,
+ bkp->bk_data)) != 0)
+ goto out;
+ }
+
+ while ((type = va_arg(*ap, int)) != BUNYAN_T_END) {
+ void *data;
+ boolean_t bt;
+ int32_t i32;
+ int64_t i64;
+ uint32_t ui32;
+ uint64_t ui64;
+ double d;
+ uintptr_t ptr;
+ const char *key = va_arg(*ap, char *);
+
+ switch (type) {
+ case BUNYAN_T_STRING:
+ data = va_arg(*ap, char *);
+ break;
+ case BUNYAN_T_POINTER:
+ ptr = (uintptr_t)va_arg(*ap, void *);
+ data = &ptr;
+ break;
+ case BUNYAN_T_IP:
+ case BUNYAN_T_IP6:
+ data = va_arg(*ap, void *);
+ break;
+ case BUNYAN_T_BOOLEAN:
+ bt = va_arg(*ap, boolean_t);
+ data = &bt;
+ break;
+ case BUNYAN_T_INT32:
+ i32 = va_arg(*ap, int32_t);
+ data = &i32;
+ break;
+ case BUNYAN_T_INT64:
+ case BUNYAN_T_INT64STR:
+ i64 = va_arg(*ap, int64_t);
+ data = &i64;
+ break;
+ case BUNYAN_T_UINT32:
+ ui32 = va_arg(*ap, uint32_t);
+ data = &ui32;
+ break;
+ case BUNYAN_T_UINT64:
+ case BUNYAN_T_UINT64STR:
+ ui64 = va_arg(*ap, uint64_t);
+ data = &ui64;
+ break;
+ case BUNYAN_T_DOUBLE:
+ d = va_arg(*ap, double);
+ data = &d;
+ break;
+ default:
+ ret = EINVAL;
+ goto out;
+ }
+
+ if ((ret = bunyan_vlog_add(nvl, key, type, data)) != 0)
+ goto out;
+
+ }
+ /*
+ * This must be the last thing we do before we log to ensure that all of
+ * our defaults always make it out.
+ */
+ if ((ret = bunyan_vlog_defaults(nvl, b, level, msg)) != 0)
+ goto out;
+
+ if (nvlist_dump_json(nvl, &buf) < 0) {
+ ret = errno;
+ goto out;
+ }
+
+ /* Fire DTrace probes */
+ switch (level) {
+ case BUNYAN_L_TRACE:
+ BUNYAN_LOG_TRACE(buf);
+ break;
+ case BUNYAN_L_DEBUG:
+ BUNYAN_LOG_DEBUG(buf);
+ break;
+ case BUNYAN_L_INFO:
+ BUNYAN_LOG_INFO(buf);
+ break;
+ case BUNYAN_L_WARN:
+ BUNYAN_LOG_WARN(buf);
+ break;
+ case BUNYAN_L_ERROR:
+ BUNYAN_LOG_ERROR(buf);
+ break;
+ case BUNYAN_L_FATAL:
+ BUNYAN_LOG_FATAL(buf);
+ break;
+ }
+
+ for (bsp = b->bun_streams; bsp != NULL; bsp = bsp->bs_next) {
+ if (bsp->bs_level <= level)
+ if (bsp->bs_func(nvl, buf, bsp->bs_arg) != 0)
+ bsp->bs_count++;
+ }
+ ret = 0;
+out:
+ (void) pthread_mutex_unlock(&b->bun_lock);
+ if (buf != NULL)
+ nvlist_dump_json_free(nvl, buf);
+ if (nvl != NULL)
+ nvlist_free(nvl);
+ return (ret);
+}
+
+int
+bunyan_trace(bunyan_logger_t *bhp, const char *msg, ...)
+{
+ va_list va;
+ int ret;
+
+ va_start(va, msg);
+ ret = bunyan_vlog(bhp, BUNYAN_L_TRACE, msg, &va);
+ va_end(va);
+
+ return (ret);
+}
+
+int
+bunyan_debug(bunyan_logger_t *bhp, const char *msg, ...)
+{
+ va_list va;
+ int ret;
+
+ va_start(va, msg);
+ ret = bunyan_vlog(bhp, BUNYAN_L_DEBUG, msg, &va);
+ va_end(va);
+
+ return (ret);
+}
+
+int
+bunyan_info(bunyan_logger_t *bhp, const char *msg, ...)
+{
+ va_list va;
+ int ret;
+
+ va_start(va, msg);
+ ret = bunyan_vlog(bhp, BUNYAN_L_INFO, msg, &va);
+ va_end(va);
+
+ return (ret);
+}
+
+int
+bunyan_warn(bunyan_logger_t *bhp, const char *msg, ...)
+{
+ va_list va;
+ int ret;
+
+ va_start(va, msg);
+ ret = bunyan_vlog(bhp, BUNYAN_L_WARN, msg, &va);
+ va_end(va);
+
+ return (ret);
+}
+
+int
+bunyan_error(bunyan_logger_t *bhp, const char *msg, ...)
+{
+ va_list va;
+ int ret;
+
+ va_start(va, msg);
+ ret = bunyan_vlog(bhp, BUNYAN_L_ERROR, msg, &va);
+ va_end(va);
+
+ return (ret);
+}
+
+
+int
+bunyan_fatal(bunyan_logger_t *bhp, const char *msg, ...)
+{
+ va_list va;
+ int ret;
+
+ va_start(va, msg);
+ ret = bunyan_vlog(bhp, BUNYAN_L_FATAL, msg, &va);
+ va_end(va);
+
+ return (ret);
+}
diff --git a/usr/src/lib/libbunyan/common/bunyan.h b/usr/src/lib/libbunyan/common/bunyan.h
new file mode 100644
index 0000000000..9a01f6f6cd
--- /dev/null
+++ b/usr/src/lib/libbunyan/common/bunyan.h
@@ -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.
+ */
+
+#ifndef _BUNYAN_H
+#define _BUNYAN_H
+
+/*
+ * C version of the bunyan logging format.
+ */
+
+#include <limits.h>
+#include <libnvpair.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct bunyan_logger bunyan_logger_t;
+
+typedef enum bunyan_level {
+ BUNYAN_L_TRACE = 10,
+ BUNYAN_L_DEBUG = 20,
+ BUNYAN_L_INFO = 30,
+ BUNYAN_L_WARN = 40,
+ BUNYAN_L_ERROR = 50,
+ BUNYAN_L_FATAL = 60
+} bunyan_level_t;
+
+typedef enum bunyan_type {
+ BUNYAN_T_END = 0x0,
+ BUNYAN_T_STRING,
+ BUNYAN_T_POINTER,
+ BUNYAN_T_IP,
+ BUNYAN_T_IP6,
+ BUNYAN_T_BOOLEAN,
+ BUNYAN_T_INT32,
+ BUNYAN_T_INT64,
+ BUNYAN_T_UINT32,
+ BUNYAN_T_UINT64,
+ BUNYAN_T_DOUBLE,
+ BUNYAN_T_INT64STR,
+ BUNYAN_T_UINT64STR
+} bunyan_type_t;
+
+/*
+ * A handle is MT-safe, but not fork-safe.
+ */
+extern int bunyan_init(const char *, bunyan_logger_t **);
+extern int bunyan_child(const bunyan_logger_t *, bunyan_logger_t **, ...);
+extern void bunyan_fini(bunyan_logger_t *);
+
+/*
+ * Bunyan stream callbacks are guaranteed to be serialized.
+ */
+typedef int (*bunyan_stream_f)(nvlist_t *, const char *, void *);
+extern int bunyan_stream_fd(nvlist_t *, const char *, void *);
+
+extern int bunyan_stream_add(bunyan_logger_t *, const char *, int,
+ bunyan_stream_f, void *);
+extern int bunyan_stream_remove(bunyan_logger_t *, const char *);
+
+extern int bunyan_key_add(bunyan_logger_t *, ...);
+extern int bunyan_key_remove(bunyan_logger_t *, const char *);
+
+extern int bunyan_trace(bunyan_logger_t *, const char *msg, ...);
+extern int bunyan_debug(bunyan_logger_t *, const char *msg, ...);
+extern int bunyan_info(bunyan_logger_t *, const char *msg, ...);
+extern int bunyan_warn(bunyan_logger_t *, const char *msg, ...);
+extern int bunyan_error(bunyan_logger_t *, const char *msg, ...);
+extern int bunyan_fatal(bunyan_logger_t *, const char *msg, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BUNYAN_H */
diff --git a/usr/src/lib/libbunyan/common/bunyan_provider.d b/usr/src/lib/libbunyan/common/bunyan_provider.d
new file mode 100644
index 0000000000..d47ea75733
--- /dev/null
+++ b/usr/src/lib/libbunyan/common/bunyan_provider.d
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+/*
+ * Bunyan DTrace provider
+ */
+provider bunyan {
+ probe log__trace(char *);
+ probe log__debug(char *);
+ probe log__info(char *);
+ probe log__warn(char *);
+ probe log__error(char *);
+ probe log__fatal(char *);
+};
+
+#pragma D attributes Stable/Stable/ISA provider bunyan provider
+#pragma D attributes Private/Private/Unknown provider bunyan module
+#pragma D attributes Private/Private/Unknown provider bunyan function
+#pragma D attributes Stable/Stable/ISA provider bunyan name
+#pragma D attributes Stable/Stable/ISA provider bunyan args
diff --git a/usr/src/lib/libbunyan/common/llib-lbunyan b/usr/src/lib/libbunyan/common/llib-lbunyan
new file mode 100644
index 0000000000..31f6a52aba
--- /dev/null
+++ b/usr/src/lib/libbunyan/common/llib-lbunyan
@@ -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.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <bunyan.h>
diff --git a/usr/src/lib/libbunyan/common/mapfile-vers b/usr/src/lib/libbunyan/common/mapfile-vers
new file mode 100644
index 0000000000..775af4ab45
--- /dev/null
+++ b/usr/src/lib/libbunyan/common/mapfile-vers
@@ -0,0 +1,53 @@
+#
+# 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.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION ILLUMOS_1.0 {
+ global:
+ bunyan_init;
+ bunyan_child;
+ bunyan_fini;
+ bunyan_stream_fd;
+ bunyan_stream_add;
+ bunyan_stream_remove;
+ bunyan_key_add;
+ bunyan_key_remove;
+ bunyan_trace;
+ bunyan_debug;
+ bunyan_info;
+ bunyan_warn;
+ bunyan_error;
+ bunyan_fatal;
+};
+
+SYMBOL_VERSION ILLUMOSprivate {
+ local:
+ *;
+};
diff --git a/usr/src/lib/libbunyan/i386/Makefile b/usr/src/lib/libbunyan/i386/Makefile
new file mode 100644
index 0000000000..41e699e8f8
--- /dev/null
+++ b/usr/src/lib/libbunyan/i386/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.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libbunyan/sparc/Makefile b/usr/src/lib/libbunyan/sparc/Makefile
new file mode 100644
index 0000000000..41e699e8f8
--- /dev/null
+++ b/usr/src/lib/libbunyan/sparc/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.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libbunyan/sparcv9/Makefile b/usr/src/lib/libbunyan/sparcv9/Makefile
new file mode 100644
index 0000000000..15d904c616
--- /dev/null
+++ b/usr/src/lib/libbunyan/sparcv9/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.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libc/Makefile b/usr/src/lib/libc/Makefile
index 831425ecfd..ebc1338484 100644
--- a/usr/src/lib/libc/Makefile
+++ b/usr/src/lib/libc/Makefile
@@ -25,6 +25,8 @@
# Use is subject to license terms.
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
#
+# Copyright (c) 2018, Joyent, Inc.
+#
LIBBASENAME= libc
LIBRARY= $(LIBBASENAME:%=%.a)
@@ -130,7 +132,7 @@ all: all_h lib32 $(BUILD64) .WAIT lib64 .WAIT etc THIRDPARTYLICENSE
THIRDPARTYLICENSE: extract-copyright
$(RM) $@
- ./extract-copyright . > $@
+ $(PERL) ./extract-copyright . > $@
etc: $($(MACH)_ETC)
diff --git a/usr/src/lib/libc/Makefile.targ b/usr/src/lib/libc/Makefile.targ
index 326cca9bf4..fceb9b27b4 100644
--- a/usr/src/lib/libc/Makefile.targ
+++ b/usr/src/lib/libc/Makefile.targ
@@ -318,4 +318,25 @@ pics/%.o: $(LIBCBASE)/../port/threads/%.d $(THREADSOBJS:%=pics/%)
$(COMPILE.d) -C -xlazyload -s $< -o $@ $(THREADSOBJS:%=pics/%)
$(POST_PROCESS_O)
+# assym rules
+
+LDFLAGS.native = $(LDASSERTS) $(BDIRECT)
+
+#
+# genassym is a funny fish: it's run on the build machine, so should use the
+# native compilers, but its job is to reflect the proto area header definitions,
+# so we need to use $(CPPFLAGS) not $(CPPFLAGS.native) to make sure we pick up
+# those headers.
+#
+genassym: $(GENASSYM_C)
+ $(NATIVECC) $(NATIVE_CFLAGS) $(CPPFLAGS) $(LDFLAGS.native) \
+ -o $@ $(GENASSYM_C)
+
+# shared between 32- and 64-bit
+OFFSETS = $(LIBCDIR)/$(MACH)/offsets.in
+
+assym.h: $(OFFSETS) genassym
+ $(OFFSETS_CREATE) <$(OFFSETS) >$@
+ ./genassym >>$@
+
include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libc/amd64/Makefile b/usr/src/lib/libc/amd64/Makefile
index 65f78e3089..369df1cbc0 100644
--- a/usr/src/lib/libc/amd64/Makefile
+++ b/usr/src/lib/libc/amd64/Makefile
@@ -910,6 +910,7 @@ PORTSYS= \
fcntl.o \
getpagesizes.o \
getpeerucred.o \
+ inotify.o \
inst_sync.o \
issetugid.o \
label.o \
@@ -1193,19 +1194,6 @@ $(ASSYMDEP_OBJS:%=pics/%): assym.h
# assym.h build rules
GENASSYM_C = genassym.c
-LDFLAGS.native = $(LDASSERTS) $(BDIRECT)
-
-genassym := NATIVE_LIBS += libc.so
-
-genassym: $(GENASSYM_C)
- $(NATIVECC) $(NATIVE_CFLAGS) -Iinc -I$(LIBCDIR)/inc $(CPPFLAGS.native) \
- $(LDFLAGS.native) -o $@ $(GENASSYM_C)
-
-OFFSETS = $(LIBCDIR)/$(MACH)/offsets.in
-
-assym.h: $(OFFSETS) genassym
- $(OFFSETS_CREATE) <$(OFFSETS) >$@
- ./genassym >>$@
# derived C source and related explicit dependencies
$(LIBCDIR)/port/gen/errlst.c + \
diff --git a/usr/src/lib/libc/amd64/gen/siginfolst.c b/usr/src/lib/libc/amd64/gen/siginfolst.c
index 8451dfbb4f..8b8a1b4669 100644
--- a/usr/src/lib/libc/amd64/gen/siginfolst.c
+++ b/usr/src/lib/libc/amd64/gen/siginfolst.c
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1988 AT&T */
@@ -188,6 +189,7 @@ static const struct siginfolist _sys_siginfolist_data[NSIG-1] = {
0, 0,
0, 0,
0, 0, /* SIGRTMIN+15 */
+ 0, 0, /* SIGRTMIN+16 */
0, 0, /* SIGRTMAX-15 */
0, 0,
0, 0,
diff --git a/usr/src/lib/libc/i386/Makefile.com b/usr/src/lib/libc/i386/Makefile.com
index 74a3f63cc0..f49b58b058 100644
--- a/usr/src/lib/libc/i386/Makefile.com
+++ b/usr/src/lib/libc/i386/Makefile.com
@@ -949,6 +949,7 @@ PORTSYS= \
fcntl.o \
getpagesizes.o \
getpeerucred.o \
+ inotify.o \
inst_sync.o \
issetugid.o \
label.o \
@@ -1269,23 +1270,7 @@ $(ASSYMDEP_OBJS:%=pics/%) := CPPFLAGS += -I.
$(ASSYMDEP_OBJS:%=pics/%): assym.h
-# assym.h build rules
-
GENASSYM_C = $(LIBCDIR)/$(MACH)/genassym.c
-LDFLAGS.native = $(LDASSERTS) $(BDIRECT)
-
-genassym := NATIVE_LIBS += libc.so
-
-genassym: $(GENASSYM_C)
- $(NATIVECC) $(NATIVE_CFLAGS) -I$(LIBCBASE)/inc -I$(LIBCDIR)/inc \
- -D__EXTENSIONS__ $(CPPFLAGS.native) $(LDFLAGS.native) \
- -o $@ $(GENASSYM_C)
-
-OFFSETS = $(LIBCDIR)/$(MACH)/offsets.in
-
-assym.h: $(OFFSETS) genassym
- $(OFFSETS_CREATE) <$(OFFSETS) >$@
- ./genassym >>$@
# derived C source and related explicit dependencies
$(LIBCDIR)/port/gen/errlst.c + \
diff --git a/usr/src/lib/libc/i386/gen/siginfolst.c b/usr/src/lib/libc/i386/gen/siginfolst.c
index 8451dfbb4f..8b8a1b4669 100644
--- a/usr/src/lib/libc/i386/gen/siginfolst.c
+++ b/usr/src/lib/libc/i386/gen/siginfolst.c
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1988 AT&T */
@@ -188,6 +189,7 @@ static const struct siginfolist _sys_siginfolist_data[NSIG-1] = {
0, 0,
0, 0,
0, 0, /* SIGRTMIN+15 */
+ 0, 0, /* SIGRTMIN+16 */
0, 0, /* SIGRTMAX-15 */
0, 0,
0, 0,
diff --git a/usr/src/lib/libc/inc/thr_inlines.h b/usr/src/lib/libc/inc/thr_inlines.h
index 1259816e63..872b23c5de 100644
--- a/usr/src/lib/libc/inc/thr_inlines.h
+++ b/usr/src/lib/libc/inc/thr_inlines.h
@@ -51,17 +51,19 @@
extern __GNU_INLINE ulwp_t *
_curthread(void)
{
-#if defined(__amd64)
ulwp_t *__value;
- __asm__ __volatile__("movq %%fs:0, %0" : "=r" (__value));
+ __asm__ __volatile__(
+#if defined(__amd64)
+ "movq %%fs:0, %0\n\t"
#elif defined(__i386)
- ulwp_t *__value;
- __asm__ __volatile__("movl %%gs:0, %0" : "=r" (__value));
+ "movl %%gs:0, %0\n\t"
#elif defined(__sparc)
- register ulwp_t *__value __asm__("g7");
+ ".register %%g7, #scratch\n\t"
+ "mov %%g7, %0\n\t"
#else
#error "port me"
#endif
+ : "=r" (__value));
return (__value);
}
diff --git a/usr/src/lib/libc/inc/thr_uberdata.h b/usr/src/lib/libc/inc/thr_uberdata.h
index 7ad0447271..65d73f9db7 100644
--- a/usr/src/lib/libc/inc/thr_uberdata.h
+++ b/usr/src/lib/libc/inc/thr_uberdata.h
@@ -969,6 +969,7 @@ typedef struct uberdata {
int ndaemons; /* total number of THR_DAEMON threads/lwps */
pid_t pid; /* the current process's pid */
void (*sigacthandler)(int, siginfo_t *, void *);
+ int (*setctxt)(const ucontext_t *);
ulwp_t *lwp_stacks;
ulwp_t *lwp_laststack;
int nfreestack;
@@ -981,6 +982,7 @@ typedef struct uberdata {
robust_t **robustlocks; /* table of registered robust locks */
robust_t *robustlist; /* list of registered robust locks */
char *progname; /* the basename of the program, from argv[0] */
+ char *ub_broot; /* the root of the native code in the brand */
void *ub_comm_page; /* arch-specific comm page of kernel data */
struct uberdata **tdb_bootstrap;
tdb_t tdb; /* thread debug interfaces (for libc_db) */
@@ -1183,6 +1185,7 @@ typedef struct uberdata32 {
int ndaemons;
int pid;
caddr32_t sigacthandler;
+ caddr32_t setctxt;
caddr32_t lwp_stacks;
caddr32_t lwp_laststack;
int nfreestack;
@@ -1195,6 +1198,7 @@ typedef struct uberdata32 {
caddr32_t robustlocks;
caddr32_t robustlist;
caddr32_t progname;
+ caddr32_t ub_broot;
caddr32_t ub_comm_page;
caddr32_t tdb_bootstrap;
tdb32_t tdb;
@@ -1286,6 +1290,7 @@ extern void rwl_free(ulwp_t *);
extern void heldlock_exit(void);
extern void heldlock_free(ulwp_t *);
extern void sigacthandler(int, siginfo_t *, void *);
+extern int setctxt(const ucontext_t *);
extern void signal_init(void);
extern int sigequalset(const sigset_t *, const sigset_t *);
extern void mutex_setup(void);
diff --git a/usr/src/lib/libc/port/gen/arc4random.c b/usr/src/lib/libc/port/gen/arc4random.c
index d82f12bd2a..f60ed09afc 100644
--- a/usr/src/lib/libc/port/gen/arc4random.c
+++ b/usr/src/lib/libc/port/gen/arc4random.c
@@ -2,7 +2,7 @@
* Copyright (c) 1996, David Mazieres <dm@uun.org>
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
* Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
- * Copyright (c) 2015 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -60,7 +60,7 @@ arc4_init(uint8_t *buf, size_t n)
abort();
chacha_keysetup(&arc4->arc4_chacha, buf, ARC4_KEYSZ * 8, 0);
- chacha_ivsetup(&arc4->arc4_chacha, buf + ARC4_KEYSZ);
+ chacha_ivsetup(&arc4->arc4_chacha, buf + ARC4_KEYSZ, NULL);
}
static void
diff --git a/usr/src/lib/libc/port/gen/getauxv.c b/usr/src/lib/libc/port/gen/getauxv.c
index 500675719c..4356a01392 100644
--- a/usr/src/lib/libc/port/gen/getauxv.c
+++ b/usr/src/lib/libc/port/gen/getauxv.c
@@ -24,9 +24,8 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include "lint.h"
+#include "thr_uberdata.h"
#include <libc.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -38,6 +37,7 @@
#include <thread.h>
#include <synch.h>
#include <atomic.h>
+#include <limits.h>
static mutex_t auxlock = DEFAULTMUTEX;
@@ -59,11 +59,20 @@ _getaux(int type)
if (auxb == NULL) {
lmutex_lock(&auxlock);
if (auxb == NULL) {
+ uberdata_t *udp = curthread->ul_uberdata;
struct stat statb;
auxv_t *buf = NULL;
+ char *path = "/proc/self/auxv";
+ char pbuf[PATH_MAX];
int fd;
- if ((fd = open("/proc/self/auxv", O_RDONLY)) != -1 &&
+ if (udp->ub_broot != NULL) {
+ (void) snprintf(pbuf, sizeof (pbuf),
+ "%s/proc/self/auxv", udp->ub_broot);
+ path = pbuf;
+ }
+
+ if ((fd = open(path, O_RDONLY)) != -1 &&
fstat(fd, &statb) != -1)
buf = libc_malloc(
statb.st_size + sizeof (auxv_t));
diff --git a/usr/src/lib/libc/port/gen/sh_locks.c b/usr/src/lib/libc/port/gen/sh_locks.c
index 6583efbc9c..cf879195c6 100644
--- a/usr/src/lib/libc/port/gen/sh_locks.c
+++ b/usr/src/lib/libc/port/gen/sh_locks.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include "lint.h"
#include <mtlib.h>
#include <sys/types.h>
diff --git a/usr/src/lib/libc/port/gen/siglist.c b/usr/src/lib/libc/port/gen/siglist.c
index 441cc4c2c5..bc6dc1b731 100644
--- a/usr/src/lib/libc/port/gen/siglist.c
+++ b/usr/src/lib/libc/port/gen/siglist.c
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1988 AT&T */
@@ -128,6 +129,7 @@ static const char *_sys_siglist_data[NSIG] = {
"Fourteenth Realtime Signal", /* SIGRTMIN+13 */
"Fifteenth Realtime Signal", /* SIGRTMIN+14 */
"Sixteenth Realtime Signal", /* SIGRTMIN+15 */
+ "Seventeenth Realtime Signal", /* SIGRTMIN+16 */
"Sixteenth Last Realtime Signal", /* SIGRTMAX-15 */
"Fifteenth Last Realtime Signal", /* SIGRTMAX-14 */
"Fourteenth Last Realtime Signal", /* SIGRTMAX-13 */
diff --git a/usr/src/lib/libc/port/gen/str2sig.c b/usr/src/lib/libc/port/gen/str2sig.c
index e0c4e89d68..02c6f3cb65 100644
--- a/usr/src/lib/libc/port/gen/str2sig.c
+++ b/usr/src/lib/libc/port/gen/str2sig.c
@@ -22,7 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1988 AT&T */
@@ -102,6 +102,7 @@ static signame_t signames[] = {
{ "RTMIN+13", _SIGRTMIN+13 },
{ "RTMIN+14", _SIGRTMIN+14 },
{ "RTMIN+15", _SIGRTMIN+15 },
+ { "RTMIN+16", _SIGRTMIN+16 },
{ "RTMAX-15", _SIGRTMAX-15 },
{ "RTMAX-14", _SIGRTMAX-14 },
{ "RTMAX-13", _SIGRTMAX-13 },
diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers
index 0e1cafc04e..9ccd7c528e 100644
--- a/usr/src/lib/libc/port/mapfile-vers
+++ b/usr/src/lib/libc/port/mapfile-vers
@@ -3695,6 +3695,10 @@ $endif
__idmap_unreg;
__init_daemon_priv;
__init_suid_priv;
+ inotify_init;
+ inotify_init1;
+ inotify_add_watch;
+ inotify_rm_watch;
_insert;
inst_sync;
_iswctype;
@@ -3799,7 +3803,9 @@ $endif
psecflags_validate;
semctl64;
_semctl64;
+ set_escaped_context_cleanup;
set_setcontext_enforcement;
+ setcontext_sigmask;
_setbufend;
__set_errno;
setprojrctl;
@@ -3916,6 +3922,7 @@ $endif
zone_list;
zone_list_datalink;
zonept;
+ zone_get_nroot;
zone_remove_datalink;
zone_setattr;
zone_shutdown;
diff --git a/usr/src/lib/libc/port/stdio/system.c b/usr/src/lib/libc/port/stdio/system.c
index bc7e412d52..698d02b2ec 100644
--- a/usr/src/lib/libc/port/stdio/system.c
+++ b/usr/src/lib/libc/port/stdio/system.c
@@ -22,6 +22,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1988 AT&T */
@@ -43,6 +44,7 @@
#include <synch.h>
#include <spawn.h>
#include <paths.h>
+#include <zone.h>
#include "libc.h"
extern const char **_environ;
@@ -125,11 +127,18 @@ system(const char *cmd)
int error;
sigset_t mask;
struct stat64 buf;
- const char *shpath = _PATH_BSHELL;
+ char shpath[MAXPATHLEN];
+ const char *zroot = zone_get_nroot();
char *argv[4];
posix_spawnattr_t attr;
static const char *shell = "sh";
+ /*
+ * If executing in brand use native root.
+ */
+ (void) snprintf(shpath, sizeof (shpath), "%s%s",
+ zroot != NULL ? zroot : "", _PATH_BSHELL);
+
if (cmd == NULL) {
if (stat64(shpath, &buf) != 0) {
return (0);
diff --git a/usr/src/lib/libc/port/sys/inotify.c b/usr/src/lib/libc/port/sys/inotify.c
new file mode 100644
index 0000000000..90d04b5dd3
--- /dev/null
+++ b/usr/src/lib/libc/port/sys/inotify.c
@@ -0,0 +1,142 @@
+/*
+ * 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/inotify.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <dirent.h>
+
+int
+inotify_init()
+{
+ return (open("/dev/inotify", O_RDWR));
+}
+
+int
+inotify_init1(int flags)
+{
+ int oflags = O_RDWR;
+
+ if (flags & IN_NONBLOCK)
+ oflags |= O_NONBLOCK;
+
+ if (flags & IN_CLOEXEC)
+ oflags |= O_CLOEXEC;
+
+ return (open("/dev/inotify", oflags));
+}
+
+int
+inotify_add_watch(int fd, const char *pathname, uint32_t mask)
+{
+ inotify_addwatch_t ioc;
+ inotify_addchild_t cioc;
+ struct stat buf;
+ int dirfd, wd;
+ DIR *dir;
+ struct dirent *dp;
+ int oflags = O_RDONLY;
+
+ if (mask & IN_DONT_FOLLOW)
+ oflags |= O_NOFOLLOW;
+
+ if ((dirfd = open(pathname, oflags)) < 0)
+ return (-1);
+
+ if (fstat(dirfd, &buf) != 0) {
+ (void) close(dirfd);
+ return (-1);
+ }
+
+ if ((mask & IN_ONLYDIR) && !(buf.st_mode & S_IFDIR)) {
+ (void) close(dirfd);
+ errno = ENOTDIR;
+ return (-1);
+ }
+
+ bzero(&ioc, sizeof (ioc));
+ ioc.inaw_fd = dirfd;
+ ioc.inaw_mask = mask;
+
+ if ((wd = ioctl(fd, INOTIFYIOC_ADD_WATCH, &ioc)) < 0) {
+ (void) close(dirfd);
+ return (-1);
+ }
+
+ if (!(buf.st_mode & S_IFDIR) || !(mask & IN_CHILD_EVENTS)) {
+ (void) close(dirfd);
+ (void) ioctl(fd, INOTIFYIOC_ACTIVATE, wd);
+ return (wd);
+ }
+
+ /*
+ * If we have a directory and we have a mask that denotes child events,
+ * we need to manually add a child watch to every directory entry.
+ * (Because our watch is in place, it will automatically be added to
+ * files that are newly created after this point.)
+ */
+ if ((dir = fdopendir(dirfd)) == NULL) {
+ (void) inotify_rm_watch(fd, wd);
+ (void) close(dirfd);
+ return (-1);
+ }
+
+ bzero(&cioc, sizeof (cioc));
+ cioc.inac_fd = dirfd;
+
+ while ((dp = readdir(dir)) != NULL) {
+ if (strcmp(dp->d_name, ".") == 0)
+ continue;
+
+ if (strcmp(dp->d_name, "..") == 0)
+ continue;
+
+ cioc.inac_name = dp->d_name;
+
+ if (ioctl(fd, INOTIFYIOC_ADD_CHILD, &cioc) != 0) {
+ /*
+ * If we get an error that indicates clear internal
+ * malfunctioning, we propagate the error. Otherwise
+ * we eat it: this could be a file that no longer
+ * exists or a symlink or something else that we
+ * can't lookup.
+ */
+ switch (errno) {
+ case ENXIO:
+ case EFAULT:
+ case EBADF:
+ (void) closedir(dir);
+ (void) inotify_rm_watch(fd, wd);
+ return (-1);
+ default:
+ break;
+ }
+ }
+ }
+
+ (void) closedir(dir);
+ (void) ioctl(fd, INOTIFYIOC_ACTIVATE, wd);
+
+ return (wd);
+}
+
+int
+inotify_rm_watch(int fd, int wd)
+{
+ return (ioctl(fd, INOTIFYIOC_RM_WATCH, wd));
+}
diff --git a/usr/src/lib/libc/port/sys/zone.c b/usr/src/lib/libc/port/sys/zone.c
index 4a4c70043d..8cf28c3ccf 100644
--- a/usr/src/lib/libc/port/sys/zone.c
+++ b/usr/src/lib/libc/port/sys/zone.c
@@ -22,9 +22,11 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2011 Joyent Inc. All rights reserved.
*/
#include "lint.h"
+#include "thr_uberdata.h"
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/zone.h>
@@ -39,7 +41,8 @@
zoneid_t
zone_create(const char *name, const char *root, const struct priv_set *privs,
const char *rctls, size_t rctlsz, const char *zfs, size_t zfssz,
- int *extended_error, int match, int doi, const bslabel_t *label, int flags)
+ int *extended_error, int match, int doi, const bslabel_t *label, int flags,
+ zoneid_t req_zoneid)
{
zone_def zd;
priv_data_t *d;
@@ -59,6 +62,7 @@ zone_create(const char *name, const char *root, const struct priv_set *privs,
zd.doi = doi;
zd.label = label;
zd.flags = flags;
+ zd.zoneid = req_zoneid;
return ((zoneid_t)syscall(SYS_zone, ZONE_CREATE, &zd));
}
@@ -241,3 +245,10 @@ zone_list_datalink(zoneid_t zoneid, int *dlnump, datalink_id_t *linkids)
{
return (syscall(SYS_zone, ZONE_LIST_DATALINK, zoneid, dlnump, linkids));
}
+
+const char *
+zone_get_nroot()
+{
+ uberdata_t *udp = curthread->ul_uberdata;
+ return (udp->ub_broot);
+}
diff --git a/usr/src/lib/libc/port/threads/sigaction.c b/usr/src/lib/libc/port/threads/sigaction.c
index 1417c1e18a..8c48989a17 100644
--- a/usr/src/lib/libc/port/threads/sigaction.c
+++ b/usr/src/lib/libc/port/threads/sigaction.c
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
/*
@@ -288,6 +289,24 @@ take_deferred_signal(int sig)
thr_panic("take_deferred_signal(): __sigresend() failed");
}
+/*
+ * sigacthandler() attempts to clean up dangling uc_link pointers in
+ * signal handling contexts when libc believes us to have escaped
+ * a signal handler incorrectly in the past.
+ *
+ * Branded processes have a legitimate use for a chain including contexts
+ * other than those used for signal handling when tracking emulation
+ * requests from the kernel. We allow them to disable this cleanup
+ * behaviour.
+ */
+static int escaped_context_cleanup = 1;
+
+void
+set_escaped_context_cleanup(int on)
+{
+ escaped_context_cleanup = on;
+}
+
void
sigacthandler(int sig, siginfo_t *sip, void *uvp)
{
@@ -310,7 +329,7 @@ sigacthandler(int sig, siginfo_t *sip, void *uvp)
* we are actually executing at main level (self->ul_siglink == NULL).
* See the code for setjmp()/longjmp() for more details.
*/
- if (self->ul_siglink == NULL)
+ if (escaped_context_cleanup && self->ul_siglink == NULL)
ucp->uc_link = NULL;
/*
@@ -462,11 +481,12 @@ sigaction(int sig, const struct sigaction *nact, struct sigaction *oact)
}
/*
- * This is a private interface for the linux brand interface.
+ * This is a private interface for the lx brand.
*/
void
setsigacthandler(void (*nsigacthandler)(int, siginfo_t *, void *),
- void (**osigacthandler)(int, siginfo_t *, void *))
+ void (**osigacthandler)(int, siginfo_t *, void *),
+ int (*brsetctxt)(const ucontext_t *))
{
ulwp_t *self = curthread;
uberdata_t *udp = self->ul_uberdata;
@@ -475,6 +495,9 @@ setsigacthandler(void (*nsigacthandler)(int, siginfo_t *, void *),
*osigacthandler = udp->sigacthandler;
udp->sigacthandler = nsigacthandler;
+
+ if (brsetctxt != NULL)
+ udp->setctxt = brsetctxt;
}
/*
@@ -521,11 +544,39 @@ set_setcontext_enforcement(int on)
setcontext_enforcement = on;
}
+/*
+ * The LX brand emulation library implements an operation that is analogous to
+ * setcontext(), but takes a different path in to the kernel. So that it can
+ * correctly restore a signal mask, we expose just the signal mask handling
+ * part of the regular setcontext() routine as a private interface.
+ */
+void
+setcontext_sigmask(ucontext_t *ucp)
+{
+ ulwp_t *self = curthread;
+
+ if (ucp->uc_flags & UC_SIGMASK) {
+ block_all_signals(self);
+ delete_reserved_signals(&ucp->uc_sigmask);
+ self->ul_sigmask = ucp->uc_sigmask;
+ if (self->ul_cursig) {
+ /*
+ * We have a deferred signal present.
+ * The signal mask will be set when the
+ * signal is taken in take_deferred_signal().
+ */
+ ASSERT(self->ul_critical + self->ul_sigdefer != 0);
+ ucp->uc_flags &= ~UC_SIGMASK;
+ }
+ }
+}
+
#pragma weak _setcontext = setcontext
int
setcontext(const ucontext_t *ucp)
{
ulwp_t *self = curthread;
+ uberdata_t *udp = self->ul_uberdata;
int ret;
ucontext_t uc;
@@ -540,20 +591,7 @@ setcontext(const ucontext_t *ucp)
/*
* Restore previous signal mask and context link.
*/
- if (uc.uc_flags & UC_SIGMASK) {
- block_all_signals(self);
- delete_reserved_signals(&uc.uc_sigmask);
- self->ul_sigmask = uc.uc_sigmask;
- if (self->ul_cursig) {
- /*
- * We have a deferred signal present.
- * The signal mask will be set when the
- * signal is taken in take_deferred_signal().
- */
- ASSERT(self->ul_critical + self->ul_sigdefer != 0);
- uc.uc_flags &= ~UC_SIGMASK;
- }
- }
+ setcontext_sigmask(&uc);
self->ul_siglink = uc.uc_link;
/*
@@ -588,7 +626,7 @@ setcontext(const ucontext_t *ucp)
*/
set_parking_flag(self, 0);
self->ul_sp = 0;
- ret = __setcontext(&uc);
+ ret = udp->setctxt(&uc);
/*
* It is OK for setcontext() to return if the user has not specified
diff --git a/usr/src/lib/libc/port/threads/thr.c b/usr/src/lib/libc/port/threads/thr.c
index 0ee28c0c59..d8bf7799f4 100644
--- a/usr/src/lib/libc/port/threads/thr.c
+++ b/usr/src/lib/libc/port/threads/thr.c
@@ -127,6 +127,7 @@ uberdata_t __uberdata = {
0, /* ndaemons */
0, /* pid */
sigacthandler, /* sigacthandler */
+ __setcontext, /* setctxt */
NULL, /* lwp_stacks */
NULL, /* lwp_laststack */
0, /* nfreestack */
@@ -139,6 +140,7 @@ uberdata_t __uberdata = {
NULL, /* robustlocks */
NULL, /* robustlist */
NULL, /* progname */
+ NULL, /* ub_broot */
NULL, /* ub_comm_page */
NULL, /* __tdb_bootstrap */
{ /* tdb */
@@ -1232,13 +1234,19 @@ init_auxv_data(uberdata_t *udp)
{
Dl_argsinfo_t args;
+ udp->ub_broot = NULL;
udp->ub_comm_page = NULL;
if (dlinfo(RTLD_SELF, RTLD_DI_ARGSINFO, &args) < 0)
return;
while (args.dla_auxv->a_type != AT_NULL) {
- if (args.dla_auxv->a_type == AT_SUN_COMMPAGE) {
+ switch (args.dla_auxv->a_type) {
+ case AT_SUN_BRAND_NROOT:
+ udp->ub_broot = args.dla_auxv->a_un.a_ptr;
+ break;
+ case AT_SUN_COMMPAGE:
udp->ub_comm_page = args.dla_auxv->a_un.a_ptr;
+ break;
}
args.dla_auxv++;
}
@@ -1282,7 +1290,8 @@ libc_init(void)
/*
* Every libc, regardless of link map, needs to go through and check
* its aux vectors. Doing so will indicate whether or not this has
- * been given a comm page (to optimize certain system actions).
+ * been given a brand root (used to qualify various other data) or a
+ * comm page (to optimize certain system actions).
*/
init_auxv_data(udp);
diff --git a/usr/src/lib/libc/sparc/Makefile.com b/usr/src/lib/libc/sparc/Makefile.com
index 14a833bcc6..ea813fb1b1 100644
--- a/usr/src/lib/libc/sparc/Makefile.com
+++ b/usr/src/lib/libc/sparc/Makefile.com
@@ -21,10 +21,10 @@
#
# Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2019 Joyent, Inc.
# Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright 2013 Garrett D'Amore <garrett@damore.org>
# Copyright 2018 Nexenta Systems, Inc.
+# Copyright 2019 Joyent, Inc.
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
# Copyright 2019 Peter Tribble.
#
@@ -978,6 +978,7 @@ PORTSYS= \
fcntl.o \
getpagesizes.o \
getpeerucred.o \
+ inotify.o \
inst_sync.o \
issetugid.o \
label.o \
diff --git a/usr/src/lib/libc/sparc/gen/siginfolst.c b/usr/src/lib/libc/sparc/gen/siginfolst.c
index 8451dfbb4f..8b8a1b4669 100644
--- a/usr/src/lib/libc/sparc/gen/siginfolst.c
+++ b/usr/src/lib/libc/sparc/gen/siginfolst.c
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1988 AT&T */
@@ -188,6 +189,7 @@ static const struct siginfolist _sys_siginfolist_data[NSIG-1] = {
0, 0,
0, 0,
0, 0, /* SIGRTMIN+15 */
+ 0, 0, /* SIGRTMIN+16 */
0, 0, /* SIGRTMAX-15 */
0, 0,
0, 0,
diff --git a/usr/src/lib/libc/sparcv9/Makefile.com b/usr/src/lib/libc/sparcv9/Makefile.com
index ec8b35c83a..b7c3b49703 100644
--- a/usr/src/lib/libc/sparcv9/Makefile.com
+++ b/usr/src/lib/libc/sparcv9/Makefile.com
@@ -22,10 +22,10 @@
#
# Copyright 2016 Gary Mills
# Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright 2019 Joyent, Inc.
# Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright 2013 Garrett D'Amore <garrett@damore.org>
# Copyright 2018 Nexenta Systems, Inc.
+# Copyright 2019 Joyent, Inc.
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
# Copyright 2019 Peter Tribble.
#
@@ -915,6 +915,7 @@ PORTSYS= \
chmod.o \
chown.o \
corectl.o \
+ epoll.o \
eventfd.o \
epoll.o \
exacctsys.o \
diff --git a/usr/src/lib/libc/sparcv9/gen/siginfolst.c b/usr/src/lib/libc/sparcv9/gen/siginfolst.c
index 8451dfbb4f..8b8a1b4669 100644
--- a/usr/src/lib/libc/sparcv9/gen/siginfolst.c
+++ b/usr/src/lib/libc/sparcv9/gen/siginfolst.c
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1988 AT&T */
@@ -188,6 +189,7 @@ static const struct siginfolist _sys_siginfolist_data[NSIG-1] = {
0, 0,
0, 0,
0, 0, /* SIGRTMIN+15 */
+ 0, 0, /* SIGRTMIN+16 */
0, 0, /* SIGRTMAX-15 */
0, 0,
0, 0,
diff --git a/usr/src/lib/libctf/common/ctf_convert.c b/usr/src/lib/libctf/common/ctf_convert.c
index 6dff7994f8..31b2ed429d 100644
--- a/usr/src/lib/libctf/common/ctf_convert.c
+++ b/usr/src/lib/libctf/common/ctf_convert.c
@@ -111,7 +111,7 @@ ctf_has_c_source(Elf *elf, char *errmsg, size_t errlen)
return (ret);
}
-static ctf_file_t *
+ctf_file_t *
ctf_elfconvert(ctf_convert_t *cch, int fd, Elf *elf, int *errp, char *errbuf,
size_t errlen)
{
diff --git a/usr/src/lib/libctf/common/ctf_lib.c b/usr/src/lib/libctf/common/ctf_lib.c
index 4cb5ce0ac5..f885b3d1c1 100644
--- a/usr/src/lib/libctf/common/ctf_lib.c
+++ b/usr/src/lib/libctf/common/ctf_lib.c
@@ -37,6 +37,7 @@
#include <dlfcn.h>
#include <gelf.h>
#include <zlib.h>
+#include <zone.h>
#include <sys/debug.h>
#ifdef _LP64
@@ -92,15 +93,30 @@ _libctf_init(void)
void *
ctf_zopen(int *errp)
{
- ctf_dprintf("decompressing CTF data using %s\n", _libctf_zlib);
+ char buf[MAXPATHLEN];
+ const char *path = _libctf_zlib, *zroot;
if (zlib.z_dlp != NULL)
return (zlib.z_dlp); /* library is already loaded */
- if (access(_libctf_zlib, R_OK) == -1)
+ /*
+ * Get the zone native root. For the tools build, we don't NEED this,
+ * but since we don't allow build machines that are older that the
+ * notion of the native root anymore, we do it all the time anyway.
+ */
+ zroot = zone_get_nroot();
+
+ if (zroot != NULL) {
+ (void) snprintf(buf, MAXPATHLEN, "%s/%s", zroot, _libctf_zlib);
+ path = buf;
+ }
+
+ ctf_dprintf("decompressing CTF data using %s\n", path);
+
+ if (access(path, R_OK) == -1)
return (ctf_set_open_errno(errp, ECTF_ZMISSING));
- if ((zlib.z_dlp = dlopen(_libctf_zlib, RTLD_LAZY | RTLD_LOCAL)) == NULL)
+ if ((zlib.z_dlp = dlopen(path, RTLD_LAZY | RTLD_LOCAL)) == NULL)
return (ctf_set_open_errno(errp, ECTF_ZINIT));
zlib.z_uncompress = (int (*)()) dlsym(zlib.z_dlp, "uncompress");
diff --git a/usr/src/lib/libctf/common/libctf.h b/usr/src/lib/libctf/common/libctf.h
index 9412143a91..05224b1cd7 100644
--- a/usr/src/lib/libctf/common/libctf.h
+++ b/usr/src/lib/libctf/common/libctf.h
@@ -120,6 +120,8 @@ extern int ctf_convert_set_label(ctf_convert_t *, const char *);
extern int ctf_convert_set_nthreads(ctf_convert_t *, uint_t);
extern int ctf_convert_add_ignore(ctf_convert_t *, const char *);
+extern ctf_file_t *ctf_elfconvert(ctf_convert_t *, int, Elf *, int *, char *,
+ size_t);
extern ctf_file_t *ctf_fdconvert(ctf_convert_t *, int, int *, char *, size_t);
diff --git a/usr/src/lib/libctf/common/mapfile-vers b/usr/src/lib/libctf/common/mapfile-vers
index 09d4b8f084..a0fee17cac 100644
--- a/usr/src/lib/libctf/common/mapfile-vers
+++ b/usr/src/lib/libctf/common/mapfile-vers
@@ -87,6 +87,7 @@ SYMBOL_VERSION SUNWprivate_1.2 {
ctf_diff_types;
ctf_discard;
ctf_dup;
+ ctf_elfconvert;
ctf_elffdwrite;
ctf_elfwrite;
ctf_enum_value;
diff --git a/usr/src/lib/libcurses/screen/setupterm.c b/usr/src/lib/libcurses/screen/setupterm.c
index c1f4bdeb04..f67e8751b7 100644
--- a/usr/src/lib/libcurses/screen/setupterm.c
+++ b/usr/src/lib/libcurses/screen/setupterm.c
@@ -37,8 +37,6 @@
* contributors.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*LINTLIBRARY*/
#include <stdio.h>
@@ -48,6 +46,7 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
+#include <zone.h>
#include "curses_inc.h"
#define TERMPATH "/usr/share/lib/terminfo/"
@@ -272,9 +271,11 @@ setupterm(char *term, int filenum, int *errret)
}
if (tfd < 0) {
+ const char *zroot = zone_get_nroot();
/* /usr/share/lib/terminfo/?/$TERM */
if (snprintf(fname, sizeof (fname),
- "%s/%c/%s", TERMPATH, *term, term) >= sizeof (fname)) {
+ "%s/%s/%c/%s", zroot == NULL ? "" : zroot, TERMPATH,
+ *term, term) >= sizeof (fname)) {
term_errno = TERMINFO_TOO_LONG;
goto out_err;
}
diff --git a/usr/src/lib/libdevid/Makefile.com b/usr/src/lib/libdevid/Makefile.com
index f0e6af9ee5..7b69bdf822 100644
--- a/usr/src/lib/libdevid/Makefile.com
+++ b/usr/src/lib/libdevid/Makefile.com
@@ -22,6 +22,8 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2018 Joyent, Inc.
+#
LIBRARY= libdevid.a
VERS= .1
OBJECTS= deviceid.o devid.o devid_scsi.o devid_smp.o
diff --git a/usr/src/lib/libdhcpagent/common/dhcpagent_util.c b/usr/src/lib/libdhcpagent/common/dhcpagent_util.c
index 1c381fa08f..7a44ae5249 100644
--- a/usr/src/lib/libdhcpagent/common/dhcpagent_util.c
+++ b/usr/src/lib/libdhcpagent/common/dhcpagent_util.c
@@ -36,6 +36,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <zone.h>
#include "dhcpagent_ipc.h"
#include "dhcpagent_util.h"
@@ -125,6 +126,12 @@ dhcp_start_agent(int timeout)
int ctfd;
pid_t childpid;
ctid_t ct;
+ char dhcpcmd[MAXPATHLEN];
+ const char *zroot = zone_get_nroot();
+
+ /* Prepend the root of the native code in the brand to the command */
+ (void) snprintf(dhcpcmd, sizeof (dhcpcmd), "%s%s", zroot != NULL ?
+ zroot : "", DHCP_AGENT_PATH);
/*
* just send a dummy request to the agent to find out if it's
@@ -160,7 +167,7 @@ dhcp_start_agent(int timeout)
goto fail;
case 0:
- (void) execl(DHCP_AGENT_PATH, DHCP_AGENT_PATH, (char *)0);
+ (void) execl(dhcpcmd, dhcpcmd, (char *)0);
_exit(EXIT_FAILURE);
default:
diff --git a/usr/src/lib/libdiskmgt/common/findevs.c b/usr/src/lib/libdiskmgt/common/findevs.c
index d5de1af440..9e20eab703 100644
--- a/usr/src/lib/libdiskmgt/common/findevs.c
+++ b/usr/src/lib/libdiskmgt/common/findevs.c
@@ -27,6 +27,7 @@
/*
* Copyright (c) 2011 by Delphix. All rights reserved.
* Copyright 2017 Nexenta Systems, Inc.
+ * Copyright 2017 Joyent, Inc.
* Copyright 2021 Oxide Computer Company
*/
@@ -68,6 +69,7 @@
static char *ctrltypes[] = {
DDI_NT_FC_ATTACHMENT_POINT,
DDI_NT_NVME_ATTACHMENT_POINT,
+ DDI_NT_NVME_NEXUS,
DDI_NT_SATA_ATTACHMENT_POINT,
DDI_NT_SATA_NEXUS,
DDI_NT_SCSI_ATTACHMENT_POINT,
@@ -1063,8 +1065,10 @@ ctype(di_node_t node, di_minor_t minor)
libdiskmgt_str_eq(name, "fp")))
return (DM_CTYPE_FIBRE);
- if (libdiskmgt_str_eq(type, DDI_NT_NVME_ATTACHMENT_POINT))
+ if (libdiskmgt_str_eq(type, DDI_NT_NVME_NEXUS) ||
+ libdiskmgt_str_eq(type, DDI_NT_NVME_ATTACHMENT_POINT)) {
return (DM_CTYPE_NVME);
+ }
if (libdiskmgt_str_eq(type, DDI_NT_SATA_NEXUS) ||
libdiskmgt_str_eq(type, DDI_NT_SATA_ATTACHMENT_POINT))
diff --git a/usr/src/lib/libdiskmgt/common/libdiskmgt.h b/usr/src/lib/libdiskmgt/common/libdiskmgt.h
index 6c3a0d4325..4efc14ab44 100644
--- a/usr/src/lib/libdiskmgt/common/libdiskmgt.h
+++ b/usr/src/lib/libdiskmgt/common/libdiskmgt.h
@@ -25,8 +25,8 @@
*/
/*
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright 2017 Nexenta Systems, Inc.
+ * Copyright (c) 2017, Joyent, Inc. All rights reserved.
*/
#ifndef _LIBDISKMGT_H
diff --git a/usr/src/lib/libdladm/Makefile b/usr/src/lib/libdladm/Makefile
index e4825d91da..04d03c5f0f 100644
--- a/usr/src/lib/libdladm/Makefile
+++ b/usr/src/lib/libdladm/Makefile
@@ -73,6 +73,7 @@ TYPELIST = \
dlmgmt_door_zoneboot_t \
dlmgmt_remapid_retval_t \
dlmgmt_createid_retval_t \
+ mac_protect_t \
overlay_ioc_create_t \
overlay_ioc_activate_t \
overlay_ioc_delete_t \
diff --git a/usr/src/lib/libdladm/Makefile.com b/usr/src/lib/libdladm/Makefile.com
index d766a381c2..72f5a06d0f 100644
--- a/usr/src/lib/libdladm/Makefile.com
+++ b/usr/src/lib/libdladm/Makefile.com
@@ -20,7 +20,7 @@
#
#
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2018, Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
#
LIBRARY = libdladm.a
diff --git a/usr/src/lib/libdladm/common/libdladm.c b/usr/src/lib/libdladm/common/libdladm.c
index b7ea20ebac..eec92e09ba 100644
--- a/usr/src/lib/libdladm/common/libdladm.c
+++ b/usr/src/lib/libdladm/common/libdladm.c
@@ -23,7 +23,7 @@
*/
/*
- * Copyright (c) 2015, Joyent, Inc.
+ * Copyright 2017 Joyent, Inc.
* Copyright 2020 Peter Tribble.
* Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
*/
@@ -104,6 +104,18 @@ static link_protect_t link_protect_types[] = {
};
#define LPTYPES (sizeof (link_protect_types) / sizeof (link_protect_t))
+typedef struct {
+ uint32_t ld_type;
+ char *ld_name;
+} link_dynamic_t;
+
+static link_dynamic_t link_dynamic_types[] = {
+ { MPT_DYN_DHCPV4, "dhcpv4" },
+ { MPT_DYN_DHCPV6, "dhcpv6" },
+ { MPT_DYN_SLAAC, "slaac" },
+};
+#define DYNTYPES (sizeof (link_dynamic_types) / sizeof (link_dynamic_t))
+
dladm_status_t
dladm_open(dladm_handle_t *handle)
{
@@ -972,6 +984,28 @@ dladm_protect2str(uint32_t ptype, char *buf)
return (buf);
}
+
+/*
+ * Convert dynamic address method value to a string.
+ */
+const char *
+dladm_dynamic2str(uint32_t dtype, char *buf, size_t size)
+{
+ const char *s = "--";
+ link_dynamic_t *ld;
+ int i;
+
+ for (i = 0; i < DYNTYPES; i++) {
+ ld = &link_dynamic_types[i];
+ if (ld->ld_type == dtype) {
+ s = ld->ld_name;
+ break;
+ }
+ }
+ (void) snprintf(buf, size, "%s", dgettext(TEXT_DOMAIN, s));
+ return (buf);
+}
+
/*
* Convert an IPv4 address to/from a string.
*/
@@ -1113,8 +1147,8 @@ fail:
* is allocated here but should be freed by the caller.
*/
dladm_status_t
-dladm_strs2range(char **prop_val, uint_t val_cnt, mac_propval_type_t type,
- mac_propval_range_t **range)
+dladm_strs2range(char **prop_val, uint_t val_cnt,
+ mac_propval_type_t type, mac_propval_range_t **range)
{
int i;
char *endp;
diff --git a/usr/src/lib/libdladm/common/libdladm.h b/usr/src/lib/libdladm/common/libdladm.h
index 66804308d1..4a41dcf449 100644
--- a/usr/src/lib/libdladm/common/libdladm.h
+++ b/usr/src/lib/libdladm/common/libdladm.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2017, Joyent, Inc.
*/
/*
@@ -47,7 +48,7 @@ extern "C" {
#endif
#define LINKID_STR_WIDTH 10
-#define DLADM_STRSIZE 256
+#define DLADM_STRSIZE 2048
/*
* option flags taken by the libdladm functions
@@ -77,6 +78,10 @@ extern "C" {
* - DLADM_OPT_BOOT:
* Bypass check functions during boot (used by pool property since pools
* can come up after link properties are set)
+ *
+ * - DLADM_OPT_TRANSIENT:
+ * Indicates that the link assigned to a zone is transient and will be
+ * removed when the zone shuts down.
*/
#define DLADM_OPT_ACTIVE 0x00000001
#define DLADM_OPT_PERSIST 0x00000002
@@ -87,6 +92,7 @@ extern "C" {
#define DLADM_OPT_VLAN 0x00000040
#define DLADM_OPT_NOREFRESH 0x00000080
#define DLADM_OPT_BOOT 0x00000100
+#define DLADM_OPT_TRANSIENT 0x00000200
#define DLADM_WALK_TERMINATE 0
#define DLADM_WALK_CONTINUE -1
@@ -273,6 +279,7 @@ extern dladm_status_t dladm_str2pri(char *, mac_priority_level_t *);
extern const char *dladm_pri2str(mac_priority_level_t, char *);
extern dladm_status_t dladm_str2protect(char *, uint32_t *);
extern const char *dladm_protect2str(uint32_t, char *);
+extern const char *dladm_dynamic2str(uint32_t, char *, size_t);
extern dladm_status_t dladm_str2ipv4addr(char *, void *);
extern const char *dladm_ipv4addr2str(void *, char *);
extern dladm_status_t dladm_str2ipv6addr(char *, void *);
diff --git a/usr/src/lib/libdladm/common/libdladm_impl.h b/usr/src/lib/libdladm/common/libdladm_impl.h
index 1502095de2..7d7ee92695 100644
--- a/usr/src/lib/libdladm/common/libdladm_impl.h
+++ b/usr/src/lib/libdladm/common/libdladm_impl.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015, Joyent, Inc.
+ * Copyright 2017, Joyent, Inc.
*/
/*
@@ -161,10 +161,10 @@ extern dladm_status_t dladm_flow_proplist_extract(dladm_arg_list_t *,
* by the pd_check function.
*/
typedef dladm_status_t rp_extractf_t(val_desc_t *, uint_t, void *);
-extern rp_extractf_t extract_maxbw, extract_priority,
- extract_cpus, extract_protection,
- extract_allowedips, extract_allowedcids,
- extract_rxrings, extract_txrings, extract_pool;
+extern rp_extractf_t extract_dynamic_methods, extract_priority, extract_cpus,
+ extract_protection, extract_allowallcids, extract_pool,
+ extract_allowedips, extract_allowedcids, extract_maxbw,
+ extract_rxrings, extract_txrings;
typedef struct resource_prop_s {
/*
diff --git a/usr/src/lib/libdladm/common/libdllink.c b/usr/src/lib/libdladm/common/libdllink.c
index 78c22e7b43..ac1bcba556 100644
--- a/usr/src/lib/libdladm/common/libdllink.c
+++ b/usr/src/lib/libdladm/common/libdllink.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -386,10 +387,14 @@ dladm_linkduplex2str(link_duplex_t duplex, char *buf)
/*
* Case 1: rename an existing link1 to a link2 that does not exist.
* Result: <linkid1, link2>
+ * The zonename parameter is used to allow us to create a VNIC in the global
+ * zone which is assigned to a non-global zone. Since there is a race condition
+ * in the create process if two VNICs have the same name, we need to rename it
+ * after it has been assigned to the zone.
*/
static dladm_status_t
i_dladm_rename_link_c1(dladm_handle_t handle, datalink_id_t linkid1,
- const char *link1, const char *link2, uint32_t flags)
+ const char *link1, const char *link2, uint32_t flags, const char *zonename)
{
dld_ioc_rename_t dir;
dladm_status_t status = DLADM_STATUS_OK;
@@ -402,6 +407,10 @@ i_dladm_rename_link_c1(dladm_handle_t handle, datalink_id_t linkid1,
dir.dir_linkid1 = linkid1;
dir.dir_linkid2 = DATALINK_INVALID_LINKID;
(void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN);
+ if (zonename != NULL)
+ dir.dir_zoneinit = B_TRUE;
+ else
+ dir.dir_zoneinit = B_FALSE;
if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0) {
status = dladm_errno2status(errno);
@@ -412,6 +421,7 @@ i_dladm_rename_link_c1(dladm_handle_t handle, datalink_id_t linkid1,
status = dladm_remap_datalink_id(handle, linkid1, link2);
if (status != DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) {
(void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN);
+ dir.dir_zoneinit = B_FALSE;
(void) ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir);
}
return (status);
@@ -508,6 +518,7 @@ i_dladm_rename_link_c2(dladm_handle_t handle, datalink_id_t linkid1,
*/
dir.dir_linkid1 = linkid1;
dir.dir_linkid2 = linkid2;
+ dir.dir_zoneinit = B_FALSE;
if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0)
status = dladm_errno2status(errno);
@@ -616,7 +627,8 @@ done:
}
dladm_status_t
-dladm_rename_link(dladm_handle_t handle, const char *link1, const char *link2)
+dladm_rename_link(dladm_handle_t handle, const char *zonename,
+ const char *link1, const char *link2)
{
datalink_id_t linkid1 = DATALINK_INVALID_LINKID;
datalink_id_t linkid2 = DATALINK_INVALID_LINKID;
@@ -626,11 +638,11 @@ dladm_rename_link(dladm_handle_t handle, const char *link1, const char *link2)
boolean_t remphy2 = B_FALSE;
dladm_status_t status;
- (void) dladm_name2info(handle, link1, &linkid1, &flags1, &class1,
- &media1);
- if ((dladm_name2info(handle, link2, &linkid2, &flags2, &class2,
- &media2) == DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) &&
- (flags2 == DLADM_OPT_PERSIST)) {
+ (void) dladm_zname2info(handle, zonename, link1, &linkid1, &flags1,
+ &class1, &media1);
+ if ((dladm_zname2info(handle, zonename, link2, &linkid2, &flags2,
+ &class2, &media2) == DLADM_STATUS_OK) &&
+ (class2 == DATALINK_CLASS_PHYS) && (flags2 == DLADM_OPT_PERSIST)) {
/*
* see whether link2 is a removed physical link.
*/
@@ -644,7 +656,7 @@ dladm_rename_link(dladm_handle_t handle, const char *link1, const char *link2)
* does not exist.
*/
status = i_dladm_rename_link_c1(handle, linkid1, link1,
- link2, flags1);
+ link2, flags1, zonename);
} else if (remphy2) {
/*
* case 2: rename an available link to a REMOVED
diff --git a/usr/src/lib/libdladm/common/libdllink.h b/usr/src/lib/libdladm/common/libdllink.h
index a2830b5e37..a858e78aa3 100644
--- a/usr/src/lib/libdladm/common/libdllink.h
+++ b/usr/src/lib/libdladm/common/libdllink.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent Inc. All rights reserved.
*/
#ifndef _LIBDLLINK_H
@@ -121,7 +122,7 @@ extern dladm_status_t dladm_info(dladm_handle_t, datalink_id_t,
dladm_attr_t *);
extern dladm_status_t dladm_rename_link(dladm_handle_t, const char *,
- const char *);
+ const char *, const char *);
extern dladm_status_t dladm_set_linkprop(dladm_handle_t, datalink_id_t,
const char *, char **, uint_t, uint_t);
@@ -170,6 +171,9 @@ extern dladm_status_t dladm_up_datalink_id(dladm_handle_t, datalink_id_t);
extern dladm_status_t dladm_name2info(dladm_handle_t, const char *,
datalink_id_t *, uint32_t *, datalink_class_t *,
uint32_t *);
+extern dladm_status_t dladm_zname2info(dladm_handle_t, const char *,
+ const char *, datalink_id_t *, uint32_t *,
+ datalink_class_t *, uint32_t *);
extern dladm_status_t dladm_datalink_id2info(dladm_handle_t, datalink_id_t,
uint32_t *, datalink_class_t *, uint32_t *, char *,
size_t);
diff --git a/usr/src/lib/libdladm/common/libdlmgmt.c b/usr/src/lib/libdladm/common/libdlmgmt.c
index b9e1432f63..8d766ecb15 100644
--- a/usr/src/lib/libdladm/common/libdlmgmt.c
+++ b/usr/src/lib/libdladm/common/libdlmgmt.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <door.h>
@@ -124,6 +125,7 @@ dladm_create_datalink_id(dladm_handle_t handle, const char *link,
dlmgmt_flags = (flags & DLADM_OPT_ACTIVE) ? DLMGMT_ACTIVE : 0;
dlmgmt_flags |= (flags & DLADM_OPT_PERSIST) ? DLMGMT_PERSIST : 0;
+ dlmgmt_flags |= (flags & DLADM_OPT_TRANSIENT) ? DLMGMT_TRANSIENT : 0;
(void) strlcpy(createid.ld_link, link, MAXLINKNAMELEN);
createid.ld_class = class;
@@ -285,6 +287,7 @@ dladm_walk_datalink_id(int (*fn)(dladm_handle_t, datalink_id_t, void *),
dlmgmt_flags = (flags & DLADM_OPT_ACTIVE) ? DLMGMT_ACTIVE : 0;
dlmgmt_flags |= ((flags & DLADM_OPT_PERSIST) ? DLMGMT_PERSIST : 0);
+ dlmgmt_flags |= ((flags & DLADM_OPT_TRANSIENT) ? DLMGMT_TRANSIENT : 0);
getnext.ld_cmd = DLMGMT_CMD_GETNEXT;
getnext.ld_class = class;
@@ -528,12 +531,24 @@ dladm_getnext_conf_linkprop(dladm_handle_t handle, dladm_conf_t conf,
}
/*
- * Get the link ID that is associated with the given name.
+ * Get the link ID that is associated with the given name in the current zone.
*/
dladm_status_t
dladm_name2info(dladm_handle_t handle, const char *link, datalink_id_t *linkidp,
uint32_t *flagp, datalink_class_t *classp, uint32_t *mediap)
{
+ return (dladm_zname2info(handle, NULL, link, linkidp, flagp, classp,
+ mediap));
+}
+
+/*
+ * Get the link ID that is associated with the given zone/name pair.
+ */
+dladm_status_t
+dladm_zname2info(dladm_handle_t handle, const char *zonename, const char *link,
+ datalink_id_t *linkidp, uint32_t *flagp, datalink_class_t *classp,
+ uint32_t *mediap)
+{
dlmgmt_door_getlinkid_t getlinkid;
dlmgmt_getlinkid_retval_t retval;
datalink_id_t linkid;
@@ -542,6 +557,10 @@ dladm_name2info(dladm_handle_t handle, const char *link, datalink_id_t *linkidp,
getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID;
(void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN);
+ if (zonename != NULL)
+ getlinkid.ld_zoneid = getzoneidbyname(zonename);
+ else
+ getlinkid.ld_zoneid = -1;
if ((status = dladm_door_call(handle, &getlinkid, sizeof (getlinkid),
&retval, &sz)) != DLADM_STATUS_OK) {
@@ -621,10 +640,12 @@ dladm_datalink_id2info(dladm_handle_t handle, datalink_id_t linkid,
if (mediap != NULL)
*mediap = retval.lr_media;
if (flagp != NULL) {
- *flagp = retval.lr_flags & DLMGMT_ACTIVE ?
+ *flagp = (retval.lr_flags & DLMGMT_ACTIVE) ?
DLADM_OPT_ACTIVE : 0;
*flagp |= (retval.lr_flags & DLMGMT_PERSIST) ?
DLADM_OPT_PERSIST : 0;
+ *flagp |= (retval.lr_flags & DLMGMT_TRANSIENT) ?
+ DLADM_OPT_TRANSIENT : 0;
}
return (DLADM_STATUS_OK);
}
diff --git a/usr/src/lib/libdladm/common/libdlvnic.c b/usr/src/lib/libdladm/common/libdlvnic.c
index 30e9f1b986..f5b754b66f 100644
--- a/usr/src/lib/libdladm/common/libdlvnic.c
+++ b/usr/src/lib/libdladm/common/libdlvnic.c
@@ -564,21 +564,29 @@ dladm_vnic_create(dladm_handle_t handle, const char *vnic, datalink_id_t linkid,
vnic_created = B_TRUE;
/* Save vnic configuration and its properties */
- if (!(flags & DLADM_OPT_PERSIST))
- goto done;
+ if (flags & DLADM_OPT_PERSIST) {
+ status = dladm_vnic_persist_conf(handle, name, &attr, class);
+ if (status == DLADM_STATUS_OK)
+ conf_set = B_TRUE;
+ }
- status = dladm_vnic_persist_conf(handle, name, &attr, class);
- if (status != DLADM_STATUS_OK)
- goto done;
- conf_set = B_TRUE;
+done:
+ if (status == DLADM_STATUS_OK && proplist != NULL) {
+ uint32_t flg;
+
+ flg = flags & (DLADM_OPT_PERSIST | DLADM_OPT_ACTIVE);
- if (proplist != NULL) {
for (i = 0; i < proplist->al_count; i++) {
dladm_arg_info_t *aip = &proplist->al_info[i];
+ if (strcmp(aip->ai_name, "zone") == 0 &&
+ flags & DLADM_OPT_TRANSIENT)
+ flg |= DLADM_OPT_TRANSIENT;
+ else
+ flg &= ~DLADM_OPT_TRANSIENT;
+
status = dladm_set_linkprop(handle, vnic_id,
- aip->ai_name, aip->ai_val, aip->ai_count,
- DLADM_OPT_PERSIST);
+ aip->ai_name, aip->ai_val, aip->ai_count, flg);
if (status != DLADM_STATUS_OK) {
char errmsg[DLADM_STRSIZE];
(void) dladm_errlist_append(errs,
@@ -590,7 +598,6 @@ dladm_vnic_create(dladm_handle_t handle, const char *vnic, datalink_id_t linkid,
}
}
-done:
if (status != DLADM_STATUS_OK) {
if (conf_set)
(void) dladm_remove_conf(handle, vnic_id);
diff --git a/usr/src/lib/libdladm/common/linkprop.c b/usr/src/lib/libdladm/common/linkprop.c
index 5cfa61166b..3e6e8e0912 100644
--- a/usr/src/lib/libdladm/common/linkprop.c
+++ b/usr/src/lib/libdladm/common/linkprop.c
@@ -154,9 +154,9 @@ static pd_getf_t get_zone, get_autopush, get_rate_mod, get_rate,
get_tagmode, get_range, get_stp, get_bridge_forward,
get_bridge_pvid, get_protection, get_rxrings,
get_txrings, get_cntavail, get_secondary_macs,
- get_allowedips, get_allowedcids, get_pool,
- get_rings_range, get_linkmode_prop, get_bits,
- get_promisc_filtered;
+ get_allowallcids, get_allowedips, get_allowedcids,
+ get_pool, get_rings_range, get_linkmode_prop, get_bits,
+ get_promisc_filtered, get_dynamic_methods;
static pd_setf_t set_zone, set_rate, set_powermode, set_radio,
set_public_prop, set_resource, set_stp_prop,
@@ -462,6 +462,18 @@ static val_desc_t link_protect_vals[] = {
{ "dhcp-nospoof", MPT_DHCPNOSPOOF },
};
+static val_desc_t link_dynamic_method_vals[] = {
+ { "dhcpv4", MPT_DYN_DHCPV4 },
+ { "dhcpv6", MPT_DYN_DHCPV6 },
+ { "slaac", MPT_DYN_SLAAC },
+ { "addrconf", (MPT_DYN_SLAAC | MPT_DYN_DHCPV6) },
+};
+
+static val_desc_t dladm_bool_vals[] = {
+ { "false", MPT_FALSE },
+ { "true", MPT_TRUE },
+};
+
static val_desc_t link_promisc_filtered_vals[] = {
{ "off", B_FALSE },
{ "on", B_TRUE },
@@ -813,6 +825,11 @@ static prop_desc_t prop_table[] = {
set_resource, NULL, get_protection, check_prop, 0,
DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
+ { "dynamic-methods", { "--", RESET_VAL },
+ link_dynamic_method_vals, VALCNT(link_dynamic_method_vals),
+ set_resource, NULL, get_dynamic_methods, check_prop, 0,
+ DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
+
{ "promisc-filtered", { "on", 1 },
link_promisc_filtered_vals, VALCNT(link_promisc_filtered_vals),
set_promisc_filtered, NULL, get_promisc_filtered, check_prop, 0,
@@ -829,6 +846,11 @@ static prop_desc_t prop_table[] = {
get_allowedcids, check_allowedcids, PD_CHECK_ALLOC,
DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
+ { "allow-all-dhcp-cids", { "false", RESET_VAL },
+ dladm_bool_vals, VALCNT(dladm_bool_vals), set_resource, NULL,
+ get_allowallcids, check_prop, 0,
+ DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
+
{ "rxrings", { "--", RESET_VAL }, NULL, 0,
set_resource, get_rings_range, get_rxrings, check_rings, 0,
DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
@@ -875,8 +897,10 @@ static resource_prop_t rsrc_prop_table[] = {
{"pool", extract_pool},
{"pool-effective", extract_pool},
{"protection", extract_protection},
+ {"dynamic-methods", extract_dynamic_methods},
{"allowed-ips", extract_allowedips},
{"allowed-dhcp-cids", extract_allowedcids},
+ {"allow-all-dhcp-cids", extract_allowallcids},
{"rxrings", extract_rxrings},
{"rxrings-effective", extract_rxrings},
{"txrings", extract_txrings},
@@ -1693,6 +1717,9 @@ set_zone(dladm_handle_t handle, prop_desc_t *pdp, datalink_id_t linkid,
if (zid_new == zid_old)
return (DLADM_STATUS_OK);
+ if (flags & DLADM_OPT_TRANSIENT)
+ dzp->diz_transient = B_TRUE;
+
if ((status = set_public_prop(handle, pdp, linkid, vdp, val_cnt,
flags, media)) != DLADM_STATUS_OK)
return (status);
@@ -2957,6 +2984,91 @@ dladm_str2cid(char *buf, mac_dhcpcid_t *cid)
/* ARGSUSED */
static dladm_status_t
+get_dynamic_methods(dladm_handle_t handle, prop_desc_t *pdp,
+ datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
+ datalink_media_t media, uint_t flags, uint_t *perm_flags)
+{
+ mac_resource_props_t mrp;
+ mac_protect_t *p;
+ dladm_status_t status;
+ uint32_t i, cnt = 0, setbits[32];
+
+ status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
+ perm_flags, &mrp, sizeof (mrp));
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ p = &mrp.mrp_protect;
+ dladm_find_setbits32(p->mp_dynamic, setbits, &cnt);
+ if (cnt > *val_cnt)
+ return (DLADM_STATUS_BADVALCNT);
+
+ for (i = 0; i < cnt; i++)
+ (void) dladm_dynamic2str(
+ setbits[i], prop_val[i], DLADM_STRSIZE);
+
+ *val_cnt = cnt;
+ return (DLADM_STATUS_OK);
+}
+
+dladm_status_t
+extract_dynamic_methods(val_desc_t *vdp, uint_t cnt, void *arg)
+{
+ mac_resource_props_t *mrp = arg;
+ uint32_t methods = 0;
+ int i;
+
+ for (i = 0; i < cnt; i++)
+ methods |= (uint32_t)vdp[i].vd_val;
+
+ mrp->mrp_protect.mp_dynamic = methods;
+ mrp->mrp_mask |= MRP_PROTECT;
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
+get_allowallcids(dladm_handle_t handle, prop_desc_t *pdp,
+ datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
+ datalink_media_t media, uint_t flags, uint_t *perm_flags)
+{
+ mac_resource_props_t mrp;
+ mac_protect_t *p;
+ dladm_status_t status;
+
+ if (*val_cnt < 1)
+ return (DLADM_STATUS_BADVALCNT);
+
+ status = i_dladm_get_public_prop(handle, linkid, "resource", flags,
+ perm_flags, &mrp, sizeof (mrp));
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ p = &mrp.mrp_protect;
+ (void) snprintf(*prop_val, DLADM_STRSIZE,
+ p->mp_allcids ? "true" : "false");
+ *val_cnt = 1;
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+dladm_status_t
+extract_allowallcids(val_desc_t *vdp, uint_t cnt, void *arg)
+{
+ mac_resource_props_t *mrp = arg;
+ mac_protect_t *p = &mrp->mrp_protect;
+
+ if (vdp->vd_val == RESET_VAL || vdp->vd_val == MPT_FALSE) {
+ p->mp_allcids = (boolean_t)RESET_VAL;
+ } else {
+ p->mp_allcids = (boolean_t)vdp->vd_val;
+ }
+ mrp->mrp_mask |= MRP_PROTECT;
+ return (DLADM_STATUS_OK);
+}
+
+/* ARGSUSED */
+static dladm_status_t
get_allowedcids(dladm_handle_t handle, prop_desc_t *pdp,
datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
datalink_media_t media, uint_t flags, uint_t *perm_flags)
diff --git a/usr/src/lib/libdladm/common/mapfile-vers b/usr/src/lib/libdladm/common/mapfile-vers
index 9cf199b724..3425b93fa4 100644
--- a/usr/src/lib/libdladm/common/mapfile-vers
+++ b/usr/src/lib/libdladm/common/mapfile-vers
@@ -136,6 +136,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
dladm_remap_datalink_id;
dladm_up_datalink_id;
dladm_name2info;
+ dladm_zname2info;
dladm_datalink_id2info;
dladm_walk_datalink_id;
dladm_create_conf;
diff --git a/usr/src/lib/libdlpi/common/libdlpi.c b/usr/src/lib/libdlpi/common/libdlpi.c
index 7b09c4bf85..b1bebd6787 100644
--- a/usr/src/lib/libdlpi/common/libdlpi.c
+++ b/usr/src/lib/libdlpi/common/libdlpi.c
@@ -22,6 +22,9 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2017, Joyent, Inc.
+ */
/*
* Data-Link Provider Interface (Version 2)
@@ -51,7 +54,7 @@
#include "libdlpi_impl.h"
-static int i_dlpi_open(const char *, int *, uint_t, boolean_t);
+static int i_dlpi_open(const char *, const char *, int *, uint_t, boolean_t);
static int i_dlpi_style1_open(dlpi_impl_t *);
static int i_dlpi_style2_open(dlpi_impl_t *);
static int i_dlpi_checkstyle(dlpi_impl_t *, t_uscalar_t);
@@ -130,7 +133,8 @@ dlpi_walk(dlpi_walkfunc_t *fn, void *arg, uint_t flags)
}
int
-dlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags)
+dlpi_open_zone(const char *linkname, const char *zonename, dlpi_handle_t *dhp,
+ uint_t flags)
{
int retval, on = 1;
ifspec_t ifsp;
@@ -164,6 +168,16 @@ dlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags)
if (getenv("DLPI_DEVONLY") != NULL)
dip->dli_oflags |= DLPI_DEVONLY;
+ if (zonename == NULL) {
+ dip->dli_zonename[0] = '\0';
+ } else {
+ if (strlcpy(dip->dli_zonename, zonename,
+ sizeof (dip->dli_zonename)) >= sizeof (dip->dli_zonename)) {
+ free(dip);
+ return (DLPI_EZONENAMEINVAL);
+ }
+ }
+
/* Copy linkname provided to the function. */
if (strlcpy(dip->dli_linkname, linkname, sizeof (dip->dli_linkname)) >=
sizeof (dip->dli_linkname)) {
@@ -237,6 +251,12 @@ dlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags)
return (DLPI_SUCCESS);
}
+int
+dlpi_open(const char *linkname, dlpi_handle_t *dhp, uint_t flags)
+{
+ return (dlpi_open_zone(linkname, NULL, dhp, flags));
+}
+
void
dlpi_close(dlpi_handle_t dh)
{
@@ -1013,6 +1033,15 @@ dlpi_iftype(uint_t dlpitype)
* /dev - if DLPI_DEVONLY is specified, or if there is no
* data-link with the specified name (could be /dev/ip)
*
+ * If a zone's name has been specified, eg. via dlpi_open_zone, then we instead
+ * will check in:
+ *
+ * /dev/ipnet/zone/%z/ - if DLPI_DEVIPNET is specified
+ * /dev/net/zone/%z/ - if a data-link with the specified name exists.
+ *
+ * When a zone name is specified, all of the fallback procedures that we opt for
+ * in the normal case are not used.
+ *
* In particular, if DLPI_DEVIPNET is not specified, this function is used to
* open a data-link node, or "/dev/ip" node. It is usually be called firstly
* with style1 being B_TRUE, and if that fails and the return value is not
@@ -1040,7 +1069,8 @@ dlpi_iftype(uint_t dlpitype)
* the second style-2 open attempt.
*/
static int
-i_dlpi_open(const char *provider, int *fd, uint_t flags, boolean_t style1)
+i_dlpi_open(const char *provider, const char *zonename, int *fd, uint_t flags,
+ boolean_t style1)
{
char path[MAXPATHLEN];
int oflags;
@@ -1051,11 +1081,19 @@ i_dlpi_open(const char *provider, int *fd, uint_t flags, boolean_t style1)
oflags |= O_EXCL;
if (flags & DLPI_DEVIPNET) {
- (void) snprintf(path, sizeof (path), "/dev/ipnet/%s", provider);
- if ((*fd = open(path, oflags)) != -1)
+ if (*zonename != '\0') {
+ (void) snprintf(path, sizeof (path),
+ "/dev/ipnet/zone/%s/%s", zonename, provider);
+ } else {
+ (void) snprintf(path, sizeof (path), "/dev/ipnet/%s",
+ provider);
+ }
+ if ((*fd = open(path, oflags)) != -1) {
return (DLPI_SUCCESS);
- else
- return (errno == ENOENT ? DLPI_ENOLINK : DL_SYSERR);
+ } else {
+ return (errno == ENOENT || errno == EISDIR ?
+ DLPI_ENOLINK : DL_SYSERR);
+ }
} else if (style1 && !(flags & DLPI_DEVONLY)) {
char driver[DLPI_LINKNAME_MAX];
char device[DLPI_LINKNAME_MAX];
@@ -1070,7 +1108,13 @@ i_dlpi_open(const char *provider, int *fd, uint_t flags, boolean_t style1)
if (dlpi_parselink(provider, driver, &ppa) != DLPI_SUCCESS)
goto fallback;
- (void) snprintf(path, sizeof (path), "/dev/net/%s", provider);
+ if (*zonename != '\0') {
+ (void) snprintf(path, sizeof (path),
+ "/dev/net/zone/%s/%s", zonename, provider);
+ } else {
+ (void) snprintf(path, sizeof (path), "/dev/net/%s",
+ provider);
+ }
if ((*fd = open(path, oflags)) != -1)
return (DLPI_SUCCESS);
@@ -1118,7 +1162,7 @@ fallback:
if ((*fd = open(path, oflags)) != -1)
return (DLPI_SUCCESS);
- return (errno == ENOENT ? DLPI_ENOLINK : DL_SYSERR);
+ return (errno == ENOENT || errno == EISDIR ? DLPI_ENOLINK : DL_SYSERR);
}
/*
@@ -1130,7 +1174,8 @@ i_dlpi_style1_open(dlpi_impl_t *dip)
int retval, save_errno;
int fd;
- retval = i_dlpi_open(dip->dli_linkname, &fd, dip->dli_oflags, B_TRUE);
+ retval = i_dlpi_open(dip->dli_linkname, dip->dli_zonename, &fd,
+ dip->dli_oflags, B_TRUE);
if (retval != DLPI_SUCCESS)
return (retval);
dip->dli_fd = fd;
@@ -1153,7 +1198,8 @@ i_dlpi_style2_open(dlpi_impl_t *dip)
int fd;
int retval, save_errno;
- retval = i_dlpi_open(dip->dli_provider, &fd, dip->dli_oflags, B_FALSE);
+ retval = i_dlpi_open(dip->dli_provider, dip->dli_zonename, &fd,
+ dip->dli_oflags, B_FALSE);
if (retval != DLPI_SUCCESS)
return (retval);
dip->dli_fd = fd;
@@ -1570,7 +1616,8 @@ static const char *libdlpi_errlist[] = {
/* DLPI_ENOTENOTSUP */
"invalid DLPI notification type", /* DLPI_ENOTEINVAL */
"invalid DLPI notification id", /* DLPI_ENOTEIDINVAL */
- "DLPI_IPNETINFO not supported" /* DLPI_EIPNETINFONOTSUP */
+ "DLPI_IPNETINFO not supported", /* DLPI_EIPNETINFONOTSUP */
+ "invalid zone name" /* DLPI_EZONENAMEINVAL */
};
const char *
diff --git a/usr/src/lib/libdlpi/common/libdlpi.h b/usr/src/lib/libdlpi/common/libdlpi.h
index 993ac1b7a4..364413ee3a 100644
--- a/usr/src/lib/libdlpi/common/libdlpi.h
+++ b/usr/src/lib/libdlpi/common/libdlpi.h
@@ -93,6 +93,7 @@ enum {
DLPI_ENOTENOTSUP, /* DLPI notification not supported by link */
DLPI_ENOTEIDINVAL, /* invalid DLPI notification id */
DLPI_EIPNETINFONOTSUP, /* DLPI_IPNETINFO not supported */
+ DLPI_EZONENAMEINVAL, /* invalid zone name */
DLPI_ERRMAX /* Highest + 1 libdlpi error code */
};
@@ -184,6 +185,7 @@ typedef boolean_t dlpi_walkfunc_t(const char *, void *);
extern void dlpi_walk(dlpi_walkfunc_t *, void *, uint_t);
extern int dlpi_open(const char *, dlpi_handle_t *, uint_t);
+extern int dlpi_open_zone(const char *, const char *, dlpi_handle_t *, uint_t);
extern void dlpi_close(dlpi_handle_t);
extern int dlpi_info(dlpi_handle_t, dlpi_info_t *, uint_t);
extern int dlpi_bind(dlpi_handle_t, uint_t, uint_t *);
diff --git a/usr/src/lib/libdlpi/common/libdlpi_impl.h b/usr/src/lib/libdlpi/common/libdlpi_impl.h
index 24b02e1376..7e51ce6558 100644
--- a/usr/src/lib/libdlpi/common/libdlpi_impl.h
+++ b/usr/src/lib/libdlpi/common/libdlpi_impl.h
@@ -28,6 +28,7 @@
#include <libdlpi.h>
#include <sys/sysmacros.h>
+#include <sys/zone.h>
#ifdef __cplusplus
extern "C" {
@@ -112,6 +113,8 @@ typedef struct dlpi_impl_s {
/* full linkname including PPA */
char dli_provider[DLPI_LINKNAME_MAX];
/* only provider name */
+ char dli_zonename[ZONENAME_MAX];
+ /* optionally specified zone */
t_uscalar_t dli_style; /* style 1 or 2 */
uint_t dli_saplen; /* bound SAP length */
uint_t dli_sap; /* bound SAP value */
diff --git a/usr/src/lib/libdlpi/common/mapfile-vers b/usr/src/lib/libdlpi/common/mapfile-vers
index ed3231dc92..c818e5e660 100644
--- a/usr/src/lib/libdlpi/common/mapfile-vers
+++ b/usr/src/lib/libdlpi/common/mapfile-vers
@@ -67,6 +67,11 @@ SYMBOL_VERSION SUNW_1.1 { # first release of libdlpi, Solaris 11
SYMBOL_VERSION SUNWprivate {
global:
+ #
+ # dlpi_open_zone should be moved to a new public section once it is
+ # upstreamed into illumos-gate .
+ #
+ dlpi_open_zone;
dlpi_parselink;
dlpi_makelink;
dlpi_style;
diff --git a/usr/src/lib/libdtrace/Makefile.com b/usr/src/lib/libdtrace/Makefile.com
index 8a5c8df7bf..bba6526f16 100644
--- a/usr/src/lib/libdtrace/Makefile.com
+++ b/usr/src/lib/libdtrace/Makefile.com
@@ -88,6 +88,7 @@ DLIBSRCS += \
io.d \
ip.d \
iscsit.d \
+ mac.d \
net.d \
nfs.d \
nfssrv.d \
@@ -101,7 +102,8 @@ DLIBSRCS += \
sysevent.d \
tcp.d \
udp.d \
- unistd.d
+ unistd.d \
+ vnd.d
include ../../Makefile.lib
@@ -114,6 +116,7 @@ CLEANFILES += dt_lex.c dt_grammar.c dt_grammar.h y.output
CLEANFILES += ../common/procfs.sed ../common/procfs.d
CLEANFILES += ../common/io.sed ../common/io.d
CLEANFILES += ../common/ip.sed ../common/ip.d
+CLEANFILES += ../common/mac.sed ../common/mac.d
CLEANFILES += ../common/net.sed ../common/net.d
CLEANFILES += ../common/errno.d ../common/signal.d
CLEANFILES += ../common/dt_errtags.c ../common/dt_names.c
@@ -138,7 +141,7 @@ CERRWARN += -_gcc=-Wno-switch
SMATCH=off
YYCFLAGS =
-LDLIBS += -lgen -lproc -lrtld_db -lnsl -lsocket -lctf -lelf -lc
+LDLIBS += -lgen -lproc -lrtld_db -lnsl -lsocket -lctf -lelf -lc -lzonecfg
DRTILDLIBS = $(LDLIBS.lib) -lc
LIBDAUDITLIBS = $(LDLIBS.lib) -lmapmalloc -lc -lproc $(LDSTACKPROTECT)
@@ -212,6 +215,9 @@ pics/dt_lex.o pics/dt_grammar.o := CCVERBOSE =
../common/ip.d: ../common/ip.sed ../common/ip.d.in
sed -f ../common/ip.sed < ../common/ip.d.in > $@
+../common/mac.d: ../common/mac.sed ../common/mac.d.in
+ sed -f ../common/mac.sed < ../common/mac.d.in > $@
+
../common/net.d: ../common/net.sed ../common/net.d.in
sed -f ../common/net.sed < ../common/net.d.in > $@
diff --git a/usr/src/lib/libdtrace/common/dt_impl.h b/usr/src/lib/libdtrace/common/dt_impl.h
index 52e8bdcbf1..cdf70586b0 100644
--- a/usr/src/lib/libdtrace/common/dt_impl.h
+++ b/usr/src/lib/libdtrace/common/dt_impl.h
@@ -268,6 +268,7 @@ struct dtrace_hdl {
uint_t dt_droptags; /* boolean: set via -xdroptags */
uint_t dt_active; /* boolean: set once tracing is active */
uint_t dt_stopped; /* boolean: set once tracing is stopped */
+ uint_t dt_optset; /* boolean: set once options have been set */
processorid_t dt_beganon; /* CPU that executed BEGIN probe (if any) */
processorid_t dt_endedon; /* CPU that executed END probe (if any) */
uint_t dt_oflags; /* dtrace open-time options (see dtrace.h) */
diff --git a/usr/src/lib/libdtrace/common/dt_module.c b/usr/src/lib/libdtrace/common/dt_module.c
index cc4cdbaa19..be83e44938 100644
--- a/usr/src/lib/libdtrace/common/dt_module.c
+++ b/usr/src/lib/libdtrace/common/dt_module.c
@@ -1372,6 +1372,77 @@ dtrace_lookup_by_addr(dtrace_hdl_t *dtp, GElf_Addr addr,
return (0);
}
+/*
+ * We've been asked to look up something inside a pid related module and it has
+ * been qualified with a library name. In that case, we may have to split this
+ * up into the library and the type itself, which will be separated by an '`'
+ * character. This is complicated further by the fact that the keyword for a
+ * struct, union, or enum, will precede the library. Hence we may have something
+ * that looks like "struct libsocket.so.1`msghdr" in name and we need to
+ * transform that into "libsocket.so.1" and "struct msghdr".
+ */
+int
+dtrace_lookup_fixup_pidtype(const char *name, char **libp, char **typep)
+{
+ int len, i;
+ char *split = NULL, *lib, *buf;
+ char *base;
+ char *keywords[] = { "struct ", "union ", "enum ", NULL };
+
+ if (name == NULL)
+ return (-1);
+
+ *libp = NULL;
+ *typep = NULL;
+ buf = strdup(name);
+ if (buf == NULL)
+ return (-1);
+
+ i = 0;
+ lib = buf;
+ while (keywords[i] != NULL) {
+ base = keywords[i];
+ len = strlen(base);
+ if (strncmp(name, base, len) == 0) {
+ lib += len;
+ break;
+ }
+ i++;
+ }
+
+ split = strchr(buf, '`');
+ assert(split != NULL);
+ *split = '\0';
+ split++;
+ if (lib == buf) {
+ *libp = strdup(buf);
+ *typep = strdup(split);
+ if (*libp == NULL || *typep == NULL)
+ goto err;
+ free(buf);
+ return (0);
+ } else {
+ assert(len > 0);
+ assert(base != NULL);
+
+ *libp = strdup(lib);
+ if (*libp == NULL)
+ goto err;
+ if (asprintf(typep, "%s%s", base, split) == -1)
+ goto err;
+ free(buf);
+ return (0);
+ }
+
+err:
+ free(buf);
+ free(*libp);
+ *libp = NULL;
+ free(*typep);
+ *typep = NULL;
+ return (-1);
+}
+
int
dtrace_lookup_by_type(dtrace_hdl_t *dtp, const char *object, const char *name,
dtrace_typeinfo_t *tip)
@@ -1383,7 +1454,6 @@ dtrace_lookup_by_type(dtrace_hdl_t *dtp, const char *object, const char *name,
uint_t n, i;
int justone;
ctf_file_t *fp;
- char *buf, *p, *q;
uint_t mask = 0; /* mask of dt_module flags to match */
uint_t bits = 0; /* flag bits that must be present */
@@ -1437,19 +1507,19 @@ dtrace_lookup_by_type(dtrace_hdl_t *dtp, const char *object, const char *name,
id = ctf_lookup_by_name(dmp->dm_ctfp, name);
fp = dmp->dm_ctfp;
} else {
- if ((p = strchr(name, '`')) != NULL) {
- buf = strdup(name);
- if (buf == NULL)
+ dt_dprintf("Trying to find userland type: %s\n", name);
+ if (strchr(name, '`') != NULL) {
+ char *lib, *type;
+ if (dtrace_lookup_fixup_pidtype(name, &lib,
+ &type) != 0) {
return (dt_set_errno(dtp, EDT_NOMEM));
- p = strchr(buf, '`');
- if ((q = strchr(p + 1, '`')) != NULL)
- p = q;
- *p = '\0';
- fp = dt_module_getctflib(dtp, dmp, buf);
+ }
+ fp = dt_module_getctflib(dtp, dmp, lib);
if (fp == NULL || (id = ctf_lookup_by_name(fp,
- p + 1)) == CTF_ERR)
+ type)) == CTF_ERR)
id = CTF_ERR;
- free(buf);
+ free(lib);
+ free(type);
} else {
for (i = 0; i < dmp->dm_nctflibs; i++) {
fp = dmp->dm_libctfp[i];
diff --git a/usr/src/lib/libdtrace/common/dt_open.c b/usr/src/lib/libdtrace/common/dt_open.c
index 973ea3cfa9..f3d25f3499 100644
--- a/usr/src/lib/libdtrace/common/dt_open.c
+++ b/usr/src/lib/libdtrace/common/dt_open.c
@@ -40,6 +40,7 @@
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
+#include <zone.h>
#define _POSIX_PTHREAD_SEMANTICS
#include <dirent.h>
@@ -689,8 +690,8 @@ const dtrace_pattr_t _dtrace_prvdesc = {
{ DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON },
};
-const char *_dtrace_defcpp = "/usr/ccs/lib/cpp"; /* default cpp(1) to invoke */
-const char *_dtrace_defld = "/usr/ccs/bin/ld"; /* default ld(1) to invoke */
+const char *_dtrace_defcpp = "/usr/lib/cpp"; /* default cpp(1) to invoke */
+const char *_dtrace_defld = "/usr/bin/ld"; /* default ld(1) to invoke */
const char *_dtrace_libdir = "/usr/lib/dtrace"; /* default library directory */
const char *_dtrace_provdir = "/dev/dtrace/provider"; /* provider directory */
@@ -830,6 +831,8 @@ dt_vopen(int version, int flags, int *errp,
dt_provmod_t *provmod = NULL;
int i, err;
struct rlimit rl;
+ const char *zroot;
+ char *libpath = NULL;
const dt_intrinsic_t *dinp;
const dt_typedef_t *dtyp;
@@ -962,11 +965,19 @@ alloc:
dtp->dt_provs = calloc(dtp->dt_provbuckets, sizeof (dt_provider_t *));
dt_proc_init(dtp);
dtp->dt_vmax = DT_VERS_LATEST;
- dtp->dt_cpp_path = strdup(_dtrace_defcpp);
+ zroot = zone_get_nroot();
+ if (zroot != NULL) {
+ (void) asprintf(&dtp->dt_ld_path, "%s/%s", zroot,
+ _dtrace_defld);
+ (void) asprintf(&dtp->dt_cpp_path, "%s/%s", zroot,
+ _dtrace_defcpp);
+ } else {
+ dtp->dt_ld_path = strdup(_dtrace_defld);
+ dtp->dt_cpp_path = strdup(_dtrace_defcpp);
+ }
dtp->dt_cpp_argv = malloc(sizeof (char *));
dtp->dt_cpp_argc = 1;
dtp->dt_cpp_args = 1;
- dtp->dt_ld_path = strdup(_dtrace_defld);
dtp->dt_provmod = provmod;
dtp->dt_vector = vector;
dtp->dt_varg = arg;
@@ -1311,9 +1322,15 @@ alloc:
* compile, and to provide better error reporting (because the full
* reporting of compiler errors requires dtrace_open() to succeed).
*/
- if (dtrace_setopt(dtp, "libdir", _dtrace_libdir) != 0)
+ if (zroot != NULL)
+ (void) asprintf(&libpath, "%s/%s", zroot, _dtrace_libdir);
+ if (dtrace_setopt(dtp, "libdir",
+ libpath != NULL ? libpath : _dtrace_libdir) != 0)
return (set_open_errno(dtp, errp, dtp->dt_errno));
+ if (libpath != NULL)
+ free(libpath);
+
return (dtp);
}
diff --git a/usr/src/lib/libdtrace/common/dt_options.c b/usr/src/lib/libdtrace/common/dt_options.c
index 66366c65b4..991f197304 100644
--- a/usr/src/lib/libdtrace/common/dt_options.c
+++ b/usr/src/lib/libdtrace/common/dt_options.c
@@ -41,6 +41,8 @@
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
+#include <zone.h>
+#include <libzonecfg.h>
#include <dt_impl.h>
#include <dt_string.h>
@@ -854,6 +856,44 @@ dt_opt_bufresize(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
return (0);
}
+/*ARGSUSED*/
+static int
+dt_opt_zone(dtrace_hdl_t *dtp, const char *arg, uintptr_t option)
+{
+ zoneid_t z, did;
+
+ if (arg == NULL)
+ return (dt_set_errno(dtp, EDT_BADOPTVAL));
+
+ /*
+ * If the specified zone is currently running, we'll query the kernel
+ * for its debugger ID. If it doesn't appear to be running, we'll look
+ * for it for among all installed zones (thereby allowing a zdefs
+ * enabling against a halted zone).
+ */
+ if ((z = getzoneidbyname(arg)) != -1) {
+ if (zone_getattr(z, ZONE_ATTR_DID, &did, sizeof (did)) < 0)
+ return (dt_set_errno(dtp, EDT_BADOPTVAL));
+ } else {
+ zone_dochandle_t handle;
+
+ if ((handle = zonecfg_init_handle()) == NULL)
+ return (dt_set_errno(dtp, errno));
+
+ if (zonecfg_get_handle(arg, handle) != Z_OK) {
+ zonecfg_fini_handle(handle);
+ return (dt_set_errno(dtp, EDT_BADOPTVAL));
+ }
+
+ did = zonecfg_get_did(handle);
+ zonecfg_fini_handle(handle);
+ }
+
+ dtp->dt_options[DTRACEOPT_ZONE] = did;
+
+ return (0);
+}
+
int
dt_options_load(dtrace_hdl_t *dtp)
{
@@ -993,6 +1033,7 @@ static const dt_option_t _dtrace_rtoptions[] = {
{ "statusrate", dt_opt_rate, DTRACEOPT_STATUSRATE },
{ "strsize", dt_opt_strsize, DTRACEOPT_STRSIZE },
{ "ustackframes", dt_opt_runtime, DTRACEOPT_USTACKFRAMES },
+ { "zone", dt_opt_zone, DTRACEOPT_ZONE },
{ "temporal", dt_opt_runtime, DTRACEOPT_TEMPORAL },
{ NULL }
};
@@ -1073,9 +1114,41 @@ dtrace_setopt(dtrace_hdl_t *dtp, const char *opt, const char *val)
if (dtp->dt_active)
return (dt_set_errno(dtp, EDT_ACTIVE));
+ /*
+ * If our options had been previously ioctl'd down,
+ * clear dt_optset to indicate that a run-time option
+ * has since been set.
+ */
+ dtp->dt_optset = B_FALSE;
+
return (op->o_func(dtp, val, op->o_option));
}
}
return (dt_set_errno(dtp, EDT_BADOPTNAME));
}
+
+int
+dtrace_setopts(dtrace_hdl_t *dtp)
+{
+ void *dof;
+ int err;
+
+ if (dtp->dt_optset)
+ return (0);
+
+ if ((dof = dtrace_getopt_dof(dtp)) == NULL)
+ return (-1); /* dt_errno has been set for us */
+
+ if ((err = dt_ioctl(dtp, DTRACEIOC_ENABLE, dof)) == -1)
+ (void) dt_set_errno(dtp, errno);
+
+ dtrace_dof_destroy(dtp, dof);
+
+ if (err == -1)
+ return (-1);
+
+ dtp->dt_optset = B_TRUE;
+
+ return (0);
+}
diff --git a/usr/src/lib/libdtrace/common/dt_program.c b/usr/src/lib/libdtrace/common/dt_program.c
index 7d725bd0af..e4f9d8dd1c 100644
--- a/usr/src/lib/libdtrace/common/dt_program.c
+++ b/usr/src/lib/libdtrace/common/dt_program.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.
* Copyright (c) 2011 by Delphix. All rights reserved.
*/
@@ -154,6 +155,14 @@ dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
void *dof;
int n, err;
+ /*
+ * If we have not yet ioctl'd down our options DOF, we'll do that
+ * before enabling any probes (some options will affect which probes
+ * we match).
+ */
+ if (dtrace_setopts(dtp) != 0)
+ return (-1);
+
dtrace_program_info(dtp, pgp, pip);
if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL)
diff --git a/usr/src/lib/libdtrace/common/dt_work.c b/usr/src/lib/libdtrace/common/dt_work.c
index 97a7f62d69..c330394027 100644
--- a/usr/src/lib/libdtrace/common/dt_work.c
+++ b/usr/src/lib/libdtrace/common/dt_work.c
@@ -25,7 +25,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ */
#include <dt_impl.h>
#include <stddef.h>
@@ -164,13 +166,22 @@ dtrace_status(dtrace_hdl_t *dtp)
int
dtrace_go(dtrace_hdl_t *dtp)
{
- void *dof;
- int err;
-
if (dtp->dt_active)
return (dt_set_errno(dtp, EINVAL));
/*
+ * In most cases, we will have already ioctl'd down our options DOF
+ * by this point -- but if a libdtrace does a dtrace_setopt() after
+ * calling dtrace_program_exec() but before calling dtrace_go(),
+ * dt_optset will be cleared and we need to ioctl down the options
+ * DOF now.
+ */
+ if (dtrace_setopts(dtp) != 0 &&
+ (dtp->dt_errno != ENOTTY || dtp->dt_vector == NULL)) {
+ return (-1);
+ }
+
+ /*
* If a dtrace:::ERROR program and callback are registered, enable the
* program before we start tracing. If this fails for a vector open
* with ENOTTY, we permit dtrace_go() to succeed so that vector clients
@@ -178,19 +189,10 @@ dtrace_go(dtrace_hdl_t *dtp)
* though they do not provide support for the DTRACEIOC_ENABLE ioctl.
*/
if (dtp->dt_errprog != NULL &&
- dtrace_program_exec(dtp, dtp->dt_errprog, NULL) == -1 && (
- dtp->dt_errno != ENOTTY || dtp->dt_vector == NULL))
- return (-1); /* dt_errno has been set for us */
-
- if ((dof = dtrace_getopt_dof(dtp)) == NULL)
+ dtrace_program_exec(dtp, dtp->dt_errprog, NULL) == -1 &&
+ (dtp->dt_errno != ENOTTY || dtp->dt_vector == NULL))
return (-1); /* dt_errno has been set for us */
- err = dt_ioctl(dtp, DTRACEIOC_ENABLE, dof);
- dtrace_dof_destroy(dtp, dof);
-
- if (err == -1 && (errno != ENOTTY || dtp->dt_vector == NULL))
- return (dt_set_errno(dtp, errno));
-
if (dt_ioctl(dtp, DTRACEIOC_GO, &dtp->dt_beganon) == -1) {
if (errno == EACCES)
return (dt_set_errno(dtp, EDT_DESTRUCTIVE));
diff --git a/usr/src/lib/libdtrace/common/dtrace.h b/usr/src/lib/libdtrace/common/dtrace.h
index 7d86cdc7f2..73433e9354 100644
--- a/usr/src/lib/libdtrace/common/dtrace.h
+++ b/usr/src/lib/libdtrace/common/dtrace.h
@@ -83,6 +83,7 @@ extern const char *dtrace_subrstr(dtrace_hdl_t *, int);
extern int dtrace_setopt(dtrace_hdl_t *, const char *, const char *);
extern int dtrace_getopt(dtrace_hdl_t *, const char *, dtrace_optval_t *);
+extern int dtrace_setopts(dtrace_hdl_t *);
extern void dtrace_update(dtrace_hdl_t *);
extern int dtrace_ctlfd(dtrace_hdl_t *);
diff --git a/usr/src/lib/libdtrace/common/mac.d.in b/usr/src/lib/libdtrace/common/mac.d.in
new file mode 100644
index 0000000000..6263d51bdd
--- /dev/null
+++ b/usr/src/lib/libdtrace/common/mac.d.in
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#pragma D depends_on library ip.d
+
+inline int ETHERTYPE_PUP = @ETHERTYPE_PUP@;
+inline int ETHERTYPE_802_MIN = @ETHERTYPE_802_MIN@;
+inline int ETHERTYPE_IP = @ETHERTYPE_IP@;
+inline int ETHERTYPE_ARP = @ETHERTYPE_ARP@;
+inline int ETHERTYPE_REVARP = @ETHERTYPE_REVARP@;
+inline int ETHERTYPE_AT = @ETHERTYPE_AT@;
+inline int ETHERTYPE_AARP = @ETHERTYPE_AARP@;
+inline int ETHERTYPE_VLAN = @ETHERTYPE_VLAN@;
+inline int ETHERTYPE_IPV6 = @ETHERTYPE_IPV6@;
+inline int ETHERTYPE_SLOW = @ETHERTYPE_SLOW@;
+inline int ETHERTYPE_PPPOED = @ETHERTYPE_PPPOED@;
+inline int ETHERTYPE_PPPOES = @ETHERTYPE_PPPOES@;
+inline int ETHERTYPE_EAPOL = @ETHERTYPE_EAPOL@;
+inline int ETHERTYPE_RSN_PREAUTH = @ETHERTYPE_RSN_PREAUTH@;
+inline int ETHERTYPE_TRILL = @ETHERTYPE_TRILL@;
+inline int ETHERTYPE_FCOE = @ETHERTYPE_FCOE@;
+inline int ETHERTYPE_MAX = @ETHERTYPE_MAX@;
+
+
+typedef struct etherinfo {
+ uint8_t eth_dst[6]; /* Destination MAC addr */
+ uint8_t eth_src[6]; /* Source MAC addr */
+ uint16_t eth_type; /* Ethertype */
+ boolean_t eth_istagged; /* Is the VLAN tag present */
+ uint8_t eth_priority; /* Priority tag */
+ uint8_t eth_dei; /* drop eligible indicator */
+ uint16_t eth_vlanid; /* VLAN ID */
+ uintptr_t eth_header; /* Pointer to start of header */
+ uintptr_t eth_mblk; /* Pointer to the mblk containing header */
+} etherinfo_t;
+
+#pragma D binding "1.12.1" translator
+translator etherinfo_t < mblk_t *mp > {
+ eth_dst = mp->b_rptr;
+ eth_src = mp->b_rptr + 6;
+ eth_type = ntohs(*(uint16_t *)(mp->b_rptr + 12)) == ETHERTYPE_VLAN ?
+ ntohs(*(uint16_t *)(mp->b_rptr + 16)) :
+ ntohs(*(uint16_t *)(mp->b_rptr + 12));
+ eth_istagged = ntohs(*(uint16_t *)(mp->b_rptr + 12)) == ETHERTYPE_VLAN ?
+ 1 : 0;
+ eth_priority = ntohs(*(uint16_t *)(mp->b_rptr + 12)) == ETHERTYPE_VLAN ?
+ ntohs(*(uint16_t *)(mp->b_rptr + 14)) & 0xe000: 0;
+ eth_dei = ntohs(*(uint16_t *)(mp->b_rptr + 12)) == ETHERTYPE_VLAN ?
+ ntohs(*(uint16_t *)(mp->b_rptr + 14)) & 0x1000: 0;
+ eth_vlanid = ntohs(*(uint16_t *)(mp->b_rptr + 12)) == ETHERTYPE_VLAN ?
+ ntohs(*(uint16_t *)(mp->b_rptr + 14)) & 0x0fff: 0;
+ eth_header = (uintptr_t)mp->b_rptr;
+ eth_mblk = (uintptr_t)mp;
+};
diff --git a/usr/src/lib/libdtrace/common/mac.sed.in b/usr/src/lib/libdtrace/common/mac.sed.in
new file mode 100644
index 0000000000..00e149d000
--- /dev/null
+++ b/usr/src/lib/libdtrace/common/mac.sed.in
@@ -0,0 +1,45 @@
+/*
+ * 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 file is a sed script which is first preprocessed by cpp or cc -E to
+ * define a set of sed directives which replace #define tokens with their
+ * values. After preprocessing, the sed script is run over vnd.d.in to
+ * replace the #define tokens listed below to create the finished vnd.d.
+ * Refer to the rules in libdtrace/Makefile.com for more information.
+ */
+
+#include <sys/ethernet.h>
+
+#define SED_REPLACE(x) s/#x/x/g
+
+SED_REPLACE(ETHERTYPE_PUP)
+SED_REPLACE(ETHERTYPE_802_MIN)
+SED_REPLACE(ETHERTYPE_IP)
+SED_REPLACE(ETHERTYPE_ARP)
+SED_REPLACE(ETHERTYPE_REVARP)
+SED_REPLACE(ETHERTYPE_AT)
+SED_REPLACE(ETHERTYPE_AARP)
+SED_REPLACE(ETHERTYPE_VLAN)
+SED_REPLACE(ETHERTYPE_IPV6)
+SED_REPLACE(ETHERTYPE_SLOW)
+SED_REPLACE(ETHERTYPE_PPPOED)
+SED_REPLACE(ETHERTYPE_PPPOES)
+SED_REPLACE(ETHERTYPE_EAPOL)
+SED_REPLACE(ETHERTYPE_RSN_PREAUTH)
+SED_REPLACE(ETHERTYPE_TRILL)
+SED_REPLACE(ETHERTYPE_FCOE)
+SED_REPLACE(ETHERTYPE_MAX)
diff --git a/usr/src/lib/libdtrace/common/vnd.d b/usr/src/lib/libdtrace/common/vnd.d
new file mode 100644
index 0000000000..356c412150
--- /dev/null
+++ b/usr/src/lib/libdtrace/common/vnd.d
@@ -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.
+ */
+
+
+#pragma D depends_on module vnd
+#pragma D depends_on provider vnd
+#pragma D depends_on library ip.d
+#pragma D depends_on library mac.d
+
+#pragma D binding "1.6.3" translator
+translator ifinfo_t < vnd_str_t *vsp > {
+ if_name = vsp != NULL ? stringof(vsp->vns_dev->vdd_lname) : "<null>";
+ if_local = 0;
+ if_ipstack = vsp != NULL ? vsp->vns_nsd->vpnd_nsid : 0;
+ if_addr = (uintptr_t)vsp;
+};
diff --git a/usr/src/lib/libefi/common/crc32_efi.c b/usr/src/lib/libefi/common/crc32_efi.c
index 1dba28ad72..73039f1478 100644
--- a/usr/src/lib/libefi/common/crc32_efi.c
+++ b/usr/src/lib/libefi/common/crc32_efi.c
@@ -23,8 +23,9 @@
* Copyright 2002 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2019, Joyent, Inc.
+ */
#include <sys/crc32.h>
diff --git a/usr/src/lib/libefi/common/rdwr_efi.c b/usr/src/lib/libefi/common/rdwr_efi.c
index afbc4c4f00..74f65378b1 100644
--- a/usr/src/lib/libefi/common/rdwr_efi.c
+++ b/usr/src/lib/libefi/common/rdwr_efi.c
@@ -132,12 +132,98 @@ int efi_debug = 0;
extern unsigned int efi_crc32(const unsigned char *, unsigned int);
static int efi_read(int, struct dk_gpt *);
+/*
+ * In normal operation, libefi just passes everything down to the kernel driver
+ * (and - usually - cmlb), as that code needs to react to any partitioning
+ * changes by changing device nodes under /dev/?dsk/ and the like.
+ *
+ * However, if we are running against an un-labeled lofi device on an older
+ * version of illumos, these ioctl()s aren't emulated. This can be a problem if
+ * we're in a non-global zone, which doesn't support labeled lofi, and our
+ * kernel is downrev.
+ *
+ * In this case, we'll simply emulate the ioctl()s that libefi actually needs,
+ * except those for efi_type(). They basically boil down to simple reads and
+ * writes, though this does skip a bunch of error checking.
+ *
+ * As a final wrinkle, rather than rely on an updated libefi, smartos-live's
+ * format_image tool directly builds and uses this source.
+ */
+static int
+do_ioctl(int fd, int cmd, void *arg)
+{
+ struct dk_cinfo cinfo;
+ struct dk_minfo minfo;
+ dk_efi_t *efi = arg;
+ int saved_errno;
+ size_t len;
+ int error;
+
+ error = ioctl(fd, cmd, arg);
+
+ saved_errno = errno;
+
+ if (error != -1 || errno != ENOTTY ||
+ ioctl(fd, DKIOCINFO, (caddr_t)&cinfo) != 0 ||
+ strcmp(cinfo.dki_cname, "lofi") != 0 ||
+ ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&minfo) != 0) {
+ errno = saved_errno;
+ return (error);
+ }
+
+ switch (cmd) {
+ case DKIOCGMBOOT:
+ len = (size_t)pread(fd, arg, minfo.dki_lbsize, 0);
+ error = (len == minfo.dki_lbsize) ? 0 : -1;
+ break;
+
+ case DKIOCSMBOOT:
+ len = (size_t)pwrite(fd, arg, minfo.dki_lbsize, 0);
+ error = (len == minfo.dki_lbsize) ? 0 : -1;
+ break;
+
+ case DKIOCGETEFI:
+ len = (size_t)pread(fd, (caddr_t)(uintptr_t)efi->dki_data_64,
+ efi->dki_length, efi->dki_lba * minfo.dki_lbsize);
+ error = (len == efi->dki_length) ? 0 : -1;
+ break;
+
+ case DKIOCSETEFI:
+ len = (size_t)pwrite(fd, (caddr_t)(uintptr_t)efi->dki_data_64,
+ efi->dki_length, efi->dki_lba * minfo.dki_lbsize);
+ error = (len == efi->dki_length) ? 0 : -1;
+ break;
+
+ default:
+ errno = saved_errno;
+ break;
+ }
+
+ if (error == 0)
+ errno = 0;
+
+ return (error);
+}
+
+static int
+efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
+{
+ void *data = dk_ioc->dki_data;
+ int error;
+
+ dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data;
+ error = do_ioctl(fd, cmd, (void *)dk_ioc);
+ dk_ioc->dki_data = data;
+
+ return (error);
+}
+
static int
read_disk_info(int fd, diskaddr_t *capacity, uint_t *lbsize)
{
struct dk_minfo disk_info;
- if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1)
+ if ((do_ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1)
return (errno);
*capacity = disk_info.dki_capacity;
*lbsize = disk_info.dki_lbsize;
@@ -196,6 +282,7 @@ efi_alloc_and_init(int fd, uint32_t nparts, struct dk_gpt **vtoc)
"the maximum number of partitions supported is %lu\n",
MAX_PARTS);
}
+ errno = EINVAL;
return (-1);
}
@@ -246,7 +333,7 @@ efi_alloc_and_read(int fd, struct dk_gpt **vtoc)
if ((mbr = calloc(1, lbsize)) == NULL)
return (VT_ERROR);
- if ((ioctl(fd, DKIOCGMBOOT, (caddr_t)mbr)) == -1) {
+ if ((do_ioctl(fd, DKIOCGMBOOT, (caddr_t)mbr)) == -1) {
free(mbr);
return (VT_ERROR);
}
@@ -305,19 +392,6 @@ efi_alloc_and_read(int fd, struct dk_gpt **vtoc)
}
static int
-efi_ioctl(int fd, int cmd, dk_efi_t *dk_ioc)
-{
- void *data = dk_ioc->dki_data;
- int error;
-
- dk_ioc->dki_data_64 = (uint64_t)(uintptr_t)data;
- error = ioctl(fd, cmd, (void *)dk_ioc);
- dk_ioc->dki_data = data;
-
- return (error);
-}
-
-static int
check_label(int fd, dk_efi_t *dk_ioc)
{
efi_gpt_t *efi;
@@ -380,7 +454,7 @@ efi_read(int fd, struct dk_gpt *vtoc)
/*
* get the partition number for this file descriptor.
*/
- if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) {
+ if (do_ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) {
if (efi_debug) {
(void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno);
}
@@ -404,7 +478,7 @@ efi_read(int fd, struct dk_gpt *vtoc)
}
/* get the LBA size */
- if (ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) == -1) {
+ if (do_ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) == -1) {
if (efi_debug) {
(void) fprintf(stderr,
"assuming LBA 512 bytes %d\n",
@@ -997,7 +1071,7 @@ efi_write(int fd, struct dk_gpt *vtoc)
int nblocks;
diskaddr_t lba_backup_gpt_hdr;
- if (ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) {
+ if (do_ioctl(fd, DKIOCINFO, (caddr_t)&dki_info) == -1) {
if (efi_debug)
(void) fprintf(stderr, "DKIOCINFO errno 0x%x\n", errno);
switch (errno) {
@@ -1181,6 +1255,8 @@ efi_free(struct dk_gpt *ptr)
* Input: File descriptor
* Output: 1 if disk has an EFI label, or > 2TB with no VTOC or legacy MBR.
* Otherwise 0.
+ *
+ * This always returns 0 for an un-labeled lofi device.
*/
int
efi_type(int fd)
diff --git a/usr/src/lib/libfakekernel/common/kstat.c b/usr/src/lib/libfakekernel/common/kstat.c
index 849f0ddf0e..4b6d2baa08 100644
--- a/usr/src/lib/libfakekernel/common/kstat.c
+++ b/usr/src/lib/libfakekernel/common/kstat.c
@@ -12,6 +12,7 @@
/*
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
* Copyright 2017 RackTop Systems.
+ * Copyright 2019 Joyent, Inc.
*/
#include <sys/types.h>
@@ -42,6 +43,11 @@ kstat_named_init(kstat_named_t *knp, const char *name, uchar_t type)
/*ARGSUSED*/
void
+kstat_named_setstr(kstat_named_t *knp, const char *src)
+{}
+
+/*ARGSUSED*/
+void
kstat_install(kstat_t *ksp)
{}
diff --git a/usr/src/lib/libfakekernel/common/mapfile-vers b/usr/src/lib/libfakekernel/common/mapfile-vers
index d8de8b9fce..ac060b68b0 100644
--- a/usr/src/lib/libfakekernel/common/mapfile-vers
+++ b/usr/src/lib/libfakekernel/common/mapfile-vers
@@ -159,6 +159,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
kstat_create;
kstat_create_zone;
kstat_named_init;
+ kstat_named_setstr;
kstat_delete;
kstat_install;
kstat_waitq_enter;
@@ -219,6 +220,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
taskq_dispatch_ent;
taskq_empty;
taskq_member;
+ taskq_empty;
taskq_wait;
taskq_wait_id;
diff --git a/usr/src/lib/libfakekernel/common/taskq.c b/usr/src/lib/libfakekernel/common/taskq.c
index e3922f6c37..6fe2b995a0 100644
--- a/usr/src/lib/libfakekernel/common/taskq.c
+++ b/usr/src/lib/libfakekernel/common/taskq.c
@@ -25,6 +25,7 @@
/*
* Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
* Copyright 2017 RackTop Systems.
* Copyright 2018, Joyent, Inc.
*/
diff --git a/usr/src/lib/libgrubmgmt/common/libgrub_fs.c b/usr/src/lib/libgrubmgmt/common/libgrub_fs.c
index aa5faa6470..92078bccee 100644
--- a/usr/src/lib/libgrubmgmt/common/libgrub_fs.c
+++ b/usr/src/lib/libgrubmgmt/common/libgrub_fs.c
@@ -21,6 +21,8 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2011 Joyent, Inc. All rights reserved.
*/
/*
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
diff --git a/usr/src/lib/libidspace/Makefile.com b/usr/src/lib/libidspace/Makefile.com
index b7314fc37b..8a0f0c402e 100644
--- a/usr/src/lib/libidspace/Makefile.com
+++ b/usr/src/lib/libidspace/Makefile.com
@@ -22,6 +22,7 @@ COMDIR = $(SRC)/common/idspace
include ../../Makefile.lib
SRCDIR = ../common
+SRCS = ../../../common/idspace/id_space.c
LIBS = $(DYNLIB)
LDLIBS += -lumem
diff --git a/usr/src/lib/libidspace/common/mapfile-vers b/usr/src/lib/libidspace/common/mapfile-vers
index 4a7c94bfb7..248c75b2e3 100644
--- a/usr/src/lib/libidspace/common/mapfile-vers
+++ b/usr/src/lib/libidspace/common/mapfile-vers
@@ -30,15 +30,19 @@
$mapfile_version 2
-SYMBOL_VERSION ILLUMOSprivate {
+SYMBOL_VERSION ILLUMOS_1.0 { # first release of libidspace
global:
id_alloc;
id_allocff;
id_alloc_specific;
id_free;
- id_space_create;
+ id_space_create;
id_space_destroy;
- id_space_extend;
+ id_space_extend;
+};
+
+
+SYMBOL_VERSION ILLUMOSprivate {
local:
*;
};
diff --git a/usr/src/lib/libinetutil/common/mapfile-vers b/usr/src/lib/libinetutil/common/mapfile-vers
index a26d988990..c86fdd956d 100644
--- a/usr/src/lib/libinetutil/common/mapfile-vers
+++ b/usr/src/lib/libinetutil/common/mapfile-vers
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2017 Joyent, Inc.
#
#
diff --git a/usr/src/lib/libipadm/common/libipadm.c b/usr/src/lib/libipadm/common/libipadm.c
index e4ea402a59..da124f18b1 100644
--- a/usr/src/lib/libipadm/common/libipadm.c
+++ b/usr/src/lib/libipadm/common/libipadm.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
* Copyright (c) 2016, Chris Fraire <cfraire@me.com>.
* Copyright 2021 Tintri by DDN, Inc. All rights reserved.
*/
@@ -286,11 +287,19 @@ ipadm_close(ipadm_handle_t iph)
boolean_t
ipadm_check_auth(void)
{
+ int uid;
struct passwd pwd;
char buf[NSS_BUFLEN_PASSWD];
+ /*
+ * Branded zones may have different kinds of auth, but root always
+ * allowed.
+ */
+ if ((uid = getuid()) == 0)
+ return (B_TRUE);
+
/* get the password entry for the given user ID */
- if (getpwuid_r(getuid(), &pwd, buf, sizeof (buf)) == NULL)
+ if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == NULL)
return (B_FALSE);
/* check for presence of given authorization */
@@ -913,9 +922,21 @@ ipadm_door_call(ipadm_handle_t iph, void *arg, size_t asize, void **rbufp,
reopen:
(void) pthread_mutex_lock(&iph->iph_lock);
- /* The door descriptor is opened if it isn't already */
+ /*
+ * The door descriptor is opened if it isn't already.
+ */
if (iph->iph_door_fd == -1) {
- if ((iph->iph_door_fd = open(IPMGMT_DOOR, O_RDONLY)) < 0) {
+ char door[MAXPATHLEN];
+ const char *zroot = zone_get_nroot();
+
+ /*
+ * If this is a branded zone, make sure we use the "/native"
+ * prefix for the door path:
+ */
+ (void) snprintf(door, sizeof (door), "%s%s", zroot != NULL ?
+ zroot : "", IPMGMT_DOOR);
+
+ if ((iph->iph_door_fd = open(door, O_RDONLY)) < 0) {
err = errno;
(void) pthread_mutex_unlock(&iph->iph_lock);
return (err);
diff --git a/usr/src/lib/libipmi/common/ipmi_lancfg.c b/usr/src/lib/libipmi/common/ipmi_lancfg.c
index 3e3ebc6e81..b324891e3f 100644
--- a/usr/src/lib/libipmi/common/ipmi_lancfg.c
+++ b/usr/src/lib/libipmi/common/ipmi_lancfg.c
@@ -22,6 +22,9 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2018, Joyent, Inc.
+ */
/*
* Query and configure LAN interfaces over IPMI. This is done through the
@@ -34,6 +37,7 @@
#include <strings.h>
#include <libipmi.h>
+#include <netinet/in.h>
#include "ipmi_impl.h"
@@ -65,10 +69,26 @@ typedef struct ipmi_cmd_lan_set_config {
#define IPMI_LAN_PARAM_SUBNET_MASK 6
#define IPMI_LAN_PARAM_GATEWAY_ADDR 12
+/* VLAN/IPv6 parameters are currently only supported for GET operations */
+#define IPMI_LAN_PARAM_VLAN_ID 20
+#define IPMI_LAN_PARAM_IPVX_ENABLED 51
+#define IPMI_LAN_PARAM_IPV6_NUM_ADDRS 55
+#define IPMI_LAN_PARAM_IPV6_SADDR 56
+#define IPMI_LAN_PARAM_IPV6_DADDR 59
+#define IPMI_LAN_PARAM_IPV6_ROUTER_CONFIG 64
+#define IPMI_LAN_PARAM_IPV6_STATIC_ROUTE1 65
+#define IPMI_LAN_PARAM_IPV6_STATIC_ROUTE2 68
+#define IPMI_LAN_PARAM_IPV6_NUM_DYN_ROUTES 72
+#define IPMI_LAN_PARAM_IPV6_DYN_ROUTE 73
+
#define IPMI_LAN_SET_COMPLETE 0x0
#define IPMI_LAN_SET_INPROGRESS 0x1
#define IPMI_LAN_SET_COMMIT 0x2
+/* bitfield values of IPMI_LAN_PARAM_IPV6_ROUTER_CONFIG param */
+#define IPMI_LAN_IPV6_STATIC_ROUTES_ENABLED 0x1
+#define IPMI_LAN_IPV6_DYNAMIC_ROUTES_ENABLED 0x2
+
typedef struct ipmi_lan_entry {
int ile_param;
int ile_mask;
@@ -78,7 +98,7 @@ typedef struct ipmi_lan_entry {
size_t ile_len;
} ipmi_lan_entry_t;
-static ipmi_lan_entry_t ipmi_lan_table[] = {
+static ipmi_lan_entry_t ipmi_lan_ipv4_table[] = {
{ IPMI_LAN_PARAM_IP_ADDR, IPMI_LAN_SET_IPADDR, 0, 0,
offsetof(ipmi_lan_config_t, ilc_ipaddr), sizeof (uint32_t) },
{ IPMI_LAN_PARAM_IP_SOURCE, IPMI_LAN_SET_IPADDR_SOURCE, 0, 0,
@@ -91,8 +111,8 @@ static ipmi_lan_entry_t ipmi_lan_table[] = {
offsetof(ipmi_lan_config_t, ilc_gateway_addr), sizeof (uint32_t) }
};
-#define IPMI_LAN_NENTRIES \
- (sizeof (ipmi_lan_table) / sizeof (ipmi_lan_table[0]))
+#define IPMI_LAN_IPV4_NENTRIES \
+ (sizeof (ipmi_lan_ipv4_table) / sizeof (ipmi_lan_ipv4_table[0]))
static int
ipmi_lan_get_param(ipmi_handle_t *ihp, int channel, int param, int set,
@@ -129,15 +149,44 @@ ipmi_lan_get_param(ipmi_handle_t *ihp, int channel, int param, int set,
return (0);
}
+struct ipmi_lan_ipv6_addr {
+ uint8_t ipva_selector;
+ uint8_t ipva_source;
+ uint8_t ipva_addr[16];
+ uint8_t ipva_prefixlen;
+ uint8_t ipva_status;
+};
+
+struct ipmi_lan_ipv6_numaddrs {
+ uint8_t inva_num_saddrs;
+ uint8_t inva_num_daddrs;
+ uint8_t inva_support;
+};
+
+struct ipmi_lan_vlan_cfg {
+ uint8_t ivla_vlanid_lower;
+ DECL_BITFIELD3(
+ ivla_vlanid_upper :4,
+ __reserved :3,
+ ivla_vlan_enable :1);
+};
+
int
ipmi_lan_get_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp)
{
- uint8_t set;
- int i;
+ uint8_t set, enabled, route_cfg, ndynroutes = 0;
+ int i, j;
ipmi_lan_entry_t *lep;
+ struct ipmi_lan_ipv6_numaddrs numaddrs = { 0 };
+ struct ipmi_lan_ipv6_addr addrv6 = { 0 };
+ struct ipmi_lan_vlan_cfg vlancfg = { 0 };
+ struct in6_addr sroute1 = { 0 }, sroute2 = { 0 }, droute = { 0 };
+ boolean_t found_addr = B_FALSE;
+ boolean_t stat_routes_enabled = B_FALSE, dyn_routes_enabled = B_FALSE;
if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_SET_IN_PROGRESS, 0,
0, &set, sizeof (set)) != 0)
+ /* errno set */
return (-1);
if (set & IPMI_LAN_SET_INPROGRESS)
@@ -145,14 +194,186 @@ ipmi_lan_get_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp)
else
cfgp->ilc_set_in_progress = B_FALSE;
- for (i = 0; i < IPMI_LAN_NENTRIES; i++) {
- lep = &ipmi_lan_table[i];
- if (ipmi_lan_get_param(ihp, channel, lep->ile_param,
- lep->ile_set, lep->ile_block,
- (char *)cfgp + lep->ile_offset, lep->ile_len) != 0)
+ /*
+ * First determine which IP addressing modes (IPv4/6) are enabled. On
+ * service processors that don't support a version of IPMI that is
+ * IPv6-aware, this parameter won't exist. If we fail to look it up
+ * then we'll assume that only IPv4 is enabled.
+ */
+ if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPVX_ENABLED, 0, 0,
+ &enabled, sizeof (enabled)) != 0) {
+ cfgp->ilc_ipv4_enabled = B_TRUE;
+ cfgp->ilc_ipv6_enabled = B_FALSE;
+ } else {
+ switch (enabled) {
+ case 0:
+ cfgp->ilc_ipv4_enabled = B_TRUE;
+ cfgp->ilc_ipv6_enabled = B_FALSE;
+ break;
+ case 1:
+ cfgp->ilc_ipv4_enabled = B_FALSE;
+ cfgp->ilc_ipv6_enabled = B_TRUE;
+ break;
+ case 2:
+ cfgp->ilc_ipv4_enabled = B_TRUE;
+ cfgp->ilc_ipv6_enabled = B_TRUE;
+ break;
+ default:
+ return (ipmi_set_error(ihp, EIPMI_BAD_RESPONSE, NULL));
+ }
+ }
+
+ /* If IPv4 support is enabled, gather the current configuration. */
+ if (cfgp->ilc_ipv4_enabled == B_TRUE) {
+ for (i = 0; i < IPMI_LAN_IPV4_NENTRIES; i++) {
+ lep = &ipmi_lan_ipv4_table[i];
+ if (ipmi_lan_get_param(ihp, channel, lep->ile_param,
+ lep->ile_set, lep->ile_block,
+ (char *)cfgp + lep->ile_offset, lep->ile_len) != 0)
+ /* errno set */
+ return (-1);
+ }
+ }
+
+ /* Next check if VLAN is enabled, and if so, grab the VLAN ID. */
+ if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_VLAN_ID, 0,
+ 0, &vlancfg, sizeof (struct ipmi_lan_vlan_cfg)) != 0) {
+ /* errno set */
+ return (-1);
+ }
+ cfgp->ilc_vlan_enabled = vlancfg.ivla_vlan_enable;
+ if (cfgp->ilc_vlan_enabled == B_TRUE) {
+ cfgp->ilc_vlan_id = (vlancfg.ivla_vlanid_upper << 8) |
+ vlancfg.ivla_vlanid_lower;
+ }
+
+ /* If IPv6 support isn't enabled, then we're all done here. */
+ if (cfgp->ilc_ipv6_enabled != B_TRUE)
+ return (0);
+
+ /*
+ * First check for a static address. If we can't find one, we'll look
+ * for a dynamic address. The spec allows for multiple IPv6 static and
+ * dynamic addresses to exist in various states. For simplicity, we
+ * will search for the first address that is configured and active.
+ */
+ if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPV6_NUM_ADDRS, 0,
+ 0, &numaddrs, sizeof (numaddrs)) != 0) {
+ /* errno set */
+ return (-1);
+ }
+
+ for (i = 0; i < numaddrs.inva_num_saddrs; i++) {
+ if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPV6_SADDR,
+ i, 0, &addrv6, sizeof (addrv6)) == 0 &&
+ addrv6.ipva_status == 0) {
+ found_addr = B_TRUE;
+ cfgp->ilc_ipv6_source = IPMI_LAN_SRC_STATIC;
+ break;
+ }
+ }
+ for (i = 0; found_addr == B_FALSE && i < numaddrs.inva_num_daddrs;
+ i++) {
+ if (ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPV6_DADDR,
+ i, 0, &addrv6, sizeof (addrv6)) == 0 &&
+ addrv6.ipva_status == 0) {
+ found_addr = B_TRUE;
+ cfgp->ilc_ipv6_source = IPMI_LAN_SRC_DHCP;
+ break;
+ }
+ }
+
+ /*
+ * If we didn't find any active static or dynamic addresses, then
+ * while IPv6 support is enabled, no IPv6 interfaces have been
+ * configured. We reset ilc_ipv6_enabled back to false so that
+ * callers know that the other ilc_ipv6_* fields are not valid.
+ */
+ if (found_addr != B_TRUE) {
+ cfgp->ilc_ipv6_enabled = B_FALSE;
+ return (0);
+ }
+
+ (void) memcpy(cfgp->ilc_ipv6_addr, addrv6.ipva_addr,
+ sizeof (addrv6.ipva_addr));
+
+ /*
+ * For the case that static addressing was used for the SP IP then we
+ * need to get the IPMI_LAN_PARAM_IPV6_ROUTER_CONFIG parameter to
+ * determine if static or dynamic routes are enabled (or both).
+ *
+ * If DHCP was used to assign the SP IP, then only dynamic route
+ * discovery is supported.
+ */
+ if (cfgp->ilc_ipv6_source == IPMI_LAN_SRC_STATIC &&
+ ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPV6_ROUTER_CONFIG,
+ 0, 0, &route_cfg, sizeof (route_cfg)) != 0) {
+ /* errno set */
+ return (-1);
+ }
+
+ if (cfgp->ilc_ipv6_source == IPMI_LAN_SRC_STATIC) {
+ if (route_cfg & IPMI_LAN_IPV6_STATIC_ROUTES_ENABLED)
+ stat_routes_enabled = B_TRUE;
+ if (route_cfg & IPMI_LAN_IPV6_DYNAMIC_ROUTES_ENABLED)
+ dyn_routes_enabled = B_TRUE;
+ } else {
+ dyn_routes_enabled = B_TRUE;
+ }
+
+ /*
+ * The IPMI spec allows for a max of two static IPv6 routes to be
+ * configured.
+ */
+ j = cfgp->ilc_ipv6_nroutes = 0;
+ if (stat_routes_enabled == B_TRUE) {
+ cfgp->ilc_ipv6_nroutes = 2;
+ if (ipmi_lan_get_param(ihp, channel,
+ IPMI_LAN_PARAM_IPV6_STATIC_ROUTE1, 0, 0, &sroute1,
+ sizeof (sroute1)) != 0 ||
+ ipmi_lan_get_param(ihp, channel,
+ IPMI_LAN_PARAM_IPV6_STATIC_ROUTE2, 0, 0, &sroute1,
+ sizeof (sroute2)) != 0) {
+ /* errno set */
return (-1);
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(&sroute1)) {
+ cfgp->ilc_ipv6_nroutes++;
+ (void) memcpy(cfgp->ilc_ipv6_routes[j++], &sroute1,
+ sizeof (sroute1));
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(&sroute2) != B_TRUE) {
+ cfgp->ilc_ipv6_nroutes++;
+ (void) memcpy(cfgp->ilc_ipv6_routes[j++], &sroute2,
+ sizeof (sroute2));
+ }
}
+ /*
+ * RFC4861 states that if dynamic routing is used, a host should retain
+ * a minimum of two routes, though more is recommended. Retrieve the
+ * number of dynamic routes and then iterate through them and gather
+ * up to the first two addresses.
+ */
+ if (dyn_routes_enabled == B_TRUE &&
+ ipmi_lan_get_param(ihp, channel, IPMI_LAN_PARAM_IPV6_NUM_DYN_ROUTES,
+ 0, 0, &ndynroutes, sizeof (ndynroutes)) != 0) {
+ /* errno set */
+ return (-1);
+ }
+ for (i = 0; i < ndynroutes && i < 2; i++) {
+ if (ipmi_lan_get_param(ihp, channel,
+ IPMI_LAN_PARAM_IPV6_DYN_ROUTE, i, 0, &droute,
+ sizeof (droute)) != 0)
+ /* errno set */
+ return (-1);
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&droute) != B_TRUE) {
+ (void) memcpy(cfgp->ilc_ipv6_routes[j++], &droute,
+ sizeof (droute));
+ cfgp->ilc_ipv6_nroutes++;
+ }
+ }
return (0);
}
@@ -220,8 +441,8 @@ ipmi_lan_set_config(ipmi_handle_t *ihp, int channel, ipmi_lan_config_t *cfgp,
/*
* Iterate over all parameters and set them.
*/
- for (i = 0; i < IPMI_LAN_NENTRIES; i++) {
- lep = &ipmi_lan_table[i];
+ for (i = 0; i < IPMI_LAN_IPV4_NENTRIES; i++) {
+ lep = &ipmi_lan_ipv4_table[i];
if (!(lep->ile_mask & mask))
continue;
diff --git a/usr/src/lib/libipmi/common/libipmi.h b/usr/src/lib/libipmi/common/libipmi.h
index 1e69e57854..c3605616ed 100644
--- a/usr/src/lib/libipmi/common/libipmi.h
+++ b/usr/src/lib/libipmi/common/libipmi.h
@@ -297,6 +297,9 @@ extern ipmi_channel_info_t *ipmi_get_channel_info(ipmi_handle_t *, int);
* This can be expanded in the future as needed.
*/
+/* We'll return up to a maximum of two static routee + two dynamic routes */
+#define IPMI_LAN_IPV6_MAX_ROUTES 4
+
typedef struct ipmi_lan_config {
boolean_t ilc_set_in_progress;
uint32_t ilc_ipaddr;
@@ -304,8 +307,17 @@ typedef struct ipmi_lan_config {
uint8_t ilc_macaddr[6];
uint32_t ilc_subnet;
uint32_t ilc_gateway_addr;
+ uint8_t ilc_ipv6_source;
+ uint8_t ilc_ipv6_addr[16];
+ uint8_t ilc_ipv6_routes[IPMI_LAN_IPV6_MAX_ROUTES][16];
+ uint8_t ilc_ipv6_nroutes;
+ uint16_t ilc_vlan_id;
+ boolean_t ilc_ipv4_enabled;
+ boolean_t ilc_ipv6_enabled;
+ boolean_t ilc_vlan_enabled;
} ipmi_lan_config_t;
+/* values for ilc_ipaddr_source */
#define IPMI_LAN_SRC_UNSPECIFIED 0x0
#define IPMI_LAN_SRC_STATIC 0x1
#define IPMI_LAN_SRC_DHCP 0x2
diff --git a/usr/src/lib/libjedec/common/llib-ljedec b/usr/src/lib/libjedec/common/llib-ljedec
new file mode 100644
index 0000000000..332a64c742
--- /dev/null
+++ b/usr/src/lib/libjedec/common/llib-ljedec
@@ -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) 2018, Joyent, Inc.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <libjedec.h>
diff --git a/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com b/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com
index 05fe0a1e2a..1ff4abafdd 100644
--- a/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com
+++ b/usr/src/lib/libkmf/plugins/kmf_openssl/Makefile.com
@@ -36,10 +36,8 @@ KMFINC= -I../../../include -I../../../ber_der/inc
BERLIB= -lkmf -lkmfberder
BERLIB64= $(BERLIB)
-OPENSSLLIBS= $(BERLIB) -lcrypto -lcryptoutil -lc
-OPENSSLLIBS64= $(BERLIB64) -lcrypto -lcryptoutil -lc
-
-NATIVE_LIBS += libcrypto.so
+OPENSSLLIBS= $(BERLIB) -lsunw_crypto -lcryptoutil -lc
+OPENSSLLIBS64= $(BERLIB64) -lsunw_crypto -lcryptoutil -lc
SRCDIR= ../common
INCDIR= ../../include
diff --git a/usr/src/lib/libnisdb/db_mindex3.cc b/usr/src/lib/libnisdb/db_mindex3.cc
index 7f0d0c9449..66cdb10052 100644
--- a/usr/src/lib/libnisdb/db_mindex3.cc
+++ b/usr/src/lib/libnisdb/db_mindex3.cc
@@ -284,7 +284,7 @@ entriesFromLDAPthread(void *voidarg) {
/* Lock to prevent removal */
(void) __nis_lock_db_table(arg->tableName, 1, 0,
- "entriesFromLDAPthread");
+ (char *)"entriesFromLDAPthread");
/*
* It's possible that the db_mindex for the table has changed,
@@ -316,7 +316,7 @@ entriesFromLDAPthread(void *voidarg) {
(void) entriesFromLDAPreal(arg);
(void) __nis_ulock_db_table(arg->tableName, 1, 0,
- "entriesFromLDAPthread");
+ (char *)"entriesFromLDAPthread");
freeQuery(arg->q);
if (arg->dirObj != 0)
diff --git a/usr/src/lib/libnisdb/db_table.cc b/usr/src/lib/libnisdb/db_table.cc
index fe0fc88d23..37e78bd44b 100644
--- a/usr/src/lib/libnisdb/db_table.cc
+++ b/usr/src/lib/libnisdb/db_table.cc
@@ -602,7 +602,7 @@ db_table::setEntryExp(entryp where, entry_obj *obj, int initialLoad) {
if (o != 0) {
__nis_buffer_t b = {0, 0};
- bp2buf(myself, &b, "%s.%s",
+ bp2buf(myself, &b, (char *)"%s.%s",
o->zo_name, o->zo_domain);
t = getObjMapping(b.buf, 0, 1, 0, 0);
sfree(b.buf);
@@ -970,7 +970,7 @@ db_table::setEnumMode(long enumNum) {
if (stat != DB_SUCCESS) {
enumMode.flag = 0;
enumCount.flag = 0;
- logmsg(MSG_NOTIMECHECK, LOG_ERR,
+ logmsg(MSG_NOTIMECHECK, LOG_ERR, (char *)
"%s: No memory for enum check array; entry removal disabled",
myself);
}
diff --git a/usr/src/lib/libnisdb/nis_db.cc b/usr/src/lib/libnisdb/nis_db.cc
index c2264bd5f7..9a49fac7dc 100644
--- a/usr/src/lib/libnisdb/nis_db.cc
+++ b/usr/src/lib/libnisdb/nis_db.cc
@@ -528,7 +528,7 @@ dbFindObject(char *objName, db_status *statP) {
/* If not the root dir, find the directory where the entry lives */
sfree(table);
- name = entryName(myself, objName, &table);
+ name = entryName((char *)myself, objName, &table);
if (name == 0 || table == 0) {
sfree(name);
RETSTAT(0, DB_MEMORY_LIMIT);
@@ -738,7 +738,7 @@ dbDeleteObj(char *objName) {
nod->objType = o->zo_data.zo_type;
nis_destroy_object(o);
- nod->objName = sdup(myself, T, objName);
+ nod->objName = sdup((char *)myself, T, objName);
if (nod->objName == 0) {
sfree(nod);
return (DB_MEMORY_LIMIT);
@@ -790,7 +790,7 @@ dbTouchObj(char *objName) {
sfree(table);
table = 0;
- ent = entryName(myself, objName, &table);
+ ent = entryName((char *)myself, objName, &table);
if (ent == 0 || table == 0) {
sfree(ent);
return (DB_MEMORY_LIMIT);
@@ -990,7 +990,7 @@ dbRefreshObj(char *name, nis_object *o) {
int lstat;
/* Find parent */
- ent = entryName(myself, objName, &table);
+ ent = entryName((char *)myself, objName, &table);
if (ent == 0 || table == 0) {
sfree(b.buf);
sfree(objTable);
diff --git a/usr/src/lib/libnsl/common/mapfile-vers b/usr/src/lib/libnsl/common/mapfile-vers
index 9ed468583d..881a8b2afe 100644
--- a/usr/src/lib/libnsl/common/mapfile-vers
+++ b/usr/src/lib/libnsl/common/mapfile-vers
@@ -21,6 +21,7 @@
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2015 Joyent, Inc.
# Copyright 2018 Nexenta Systems, Inc.
#
diff --git a/usr/src/lib/libnsl/netselect/netselect.c b/usr/src/lib/libnsl/netselect/netselect.c
index 7225fa86a6..461840b695 100644
--- a/usr/src/lib/libnsl/netselect/netselect.c
+++ b/usr/src/lib/libnsl/netselect/netselect.c
@@ -22,6 +22,7 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -32,8 +33,6 @@
* under license from the Regents of the University of California.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include "mt.h"
#include "../rpc/rpc_mt.h" /* for MT declarations only */
#include <rpc/types.h>
@@ -45,6 +44,7 @@
#include <malloc.h>
#include <libintl.h>
#include <syslog.h>
+#include <zone.h>
#include "netcspace.h"
#define FAILURE (unsigned)(-1)
@@ -265,13 +265,22 @@ freenetconfigent(struct netconfig *netp)
static struct netconfig **
getnetlist(void)
{
- char line[BUFSIZ]; /* holds each line of NETCONFIG */
- FILE *fp; /* file stream for NETCONFIG */
+ FILE *fp = NULL; /* file stream for NETCONFIG */
struct netconfig **listpp; /* the beginning of the netconfig list */
struct netconfig **tpp; /* used to traverse the netconfig list */
int count; /* the number of entries in file */
+ char nc_path[MAXPATHLEN];
+ const char *zroot = zone_get_nroot();
+ char line[BUFSIZ]; /* holds each line of NETCONFIG */
+
+ /*
+ * If we are running in a branded zone, ensure we use the "/native"
+ * prefix when opening the netconfig file:
+ */
+ (void) snprintf(nc_path, sizeof (nc_path), "%s%s", zroot != NULL ?
+ zroot : "", NETCONFIG);
- if ((fp = fopen(NETCONFIG, "rF")) == NULL) {
+ if ((fp = fopen(nc_path, "rF")) == NULL) {
nc_error = NC_OPENFAIL;
return (NULL);
}
@@ -286,13 +295,16 @@ getnetlist(void)
if (count == 0) {
nc_error = NC_NOTFOUND;
- (void) fclose(fp);
+ if (fp != NULL)
+ (void) fclose(fp);
return (NULL);
}
+
if ((listpp = malloc((count + 1) *
sizeof (struct netconfig *))) == NULL) {
nc_error = NC_NOMEM;
- (void) fclose(fp);
+ if (fp != NULL)
+ (void) fclose(fp);
return (NULL);
}
@@ -311,6 +323,7 @@ getnetlist(void)
if (nc_error != NC_NOMOREENTRIES) /* Something is screwed up */
netlist_free(&listpp);
+
return (listpp);
}
diff --git a/usr/src/lib/libnvpair/libnvpair.h b/usr/src/lib/libnvpair/libnvpair.h
index b05669e506..197ec37f46 100644
--- a/usr/src/lib/libnvpair/libnvpair.h
+++ b/usr/src/lib/libnvpair/libnvpair.h
@@ -49,6 +49,8 @@ extern int nvpair_value_match_regex(nvpair_t *, int, char *, regex_t *,
extern void nvlist_print(FILE *, nvlist_t *);
extern int nvlist_print_json(FILE *, nvlist_t *);
extern void dump_nvlist(nvlist_t *, int);
+extern int nvlist_dump_json(nvlist_t *, char **);
+extern void nvlist_dump_json_free(nvlist_t *, char *);
/*
* Private nvlist printing interface that allows the caller some control
diff --git a/usr/src/lib/libnvpair/mapfile-vers b/usr/src/lib/libnvpair/mapfile-vers
index fb5abb3432..5754fd1715 100644
--- a/usr/src/lib/libnvpair/mapfile-vers
+++ b/usr/src/lib/libnvpair/mapfile-vers
@@ -254,6 +254,8 @@ SYMBOL_VERSION SUNWprivate_1.1 {
dump_nvlist;
nvlist_add_hrtime;
nvlist_lookup_hrtime;
+ nvlist_dump_json;
+ nvlist_dump_json_free;
nvlist_print;
nvlist_print_json;
nvlist_prt;
diff --git a/usr/src/lib/libnvpair/nvpair_json.c b/usr/src/lib/libnvpair/nvpair_json.c
index 6aa3e9c01c..3205d229ab 100644
--- a/usr/src/lib/libnvpair/nvpair_json.c
+++ b/usr/src/lib/libnvpair/nvpair_json.c
@@ -18,16 +18,71 @@
#include <strings.h>
#include <wchar.h>
#include <sys/debug.h>
+#include <stdarg.h>
+#include <assert.h>
#include "libnvpair.h"
-#define FPRINTF(fp, ...) \
+#define FPRINTF(bufp, blen, offp, ...) \
do { \
- if (fprintf(fp, __VA_ARGS__) < 0) \
+ if (nvlist_rasnprintf(bufp, blen, offp, \
+ __VA_ARGS__) < 0) \
return (-1); \
} while (0)
/*
+ * A realloc-aware snprintf/asprintf like function.
+ */
+/*PRINTFLIKE4*/
+static int
+nvlist_rasnprintf(char **bufp, size_t *blen, off_t *boff, char *input, ...)
+{
+ int ret;
+ va_list ap;
+ size_t size;
+ char *b;
+
+ if (*bufp == NULL) {
+ assert(*blen == 0);
+ assert(*boff == 0);
+ /* Pick a reasonable starting point, let's say 1k */
+ *blen = 1024;
+ *bufp = malloc(*blen);
+ if (*bufp == NULL)
+ return (-1);
+ }
+
+ size = *blen - *boff;
+ va_start(ap, input);
+ /* E_SEC_PRINTF_VAR_FMT */
+ ret = vsnprintf(*bufp + *boff, size, input, ap);
+ va_end(ap);
+ if (ret < 0)
+ return (-1);
+
+ if (ret >= size) {
+ size_t asize = *blen;
+ while (ret + *boff >= asize)
+ asize += 1024;
+ if ((b = realloc(*bufp, asize)) == NULL)
+ return (-1);
+ *bufp = b;
+ *blen = asize;
+ size = *blen - *boff;
+ va_start(ap, input);
+ /* E_SEC_PRINTF_VAR_FMT */
+ ret = vsnprintf(*bufp + *boff, size, input, ap);
+ va_end(ap);
+ if (ret < 0)
+ return (-1);
+ assert(ret < size);
+ }
+ *boff += ret;
+
+ return (0);
+}
+
+/*
* When formatting a string for JSON output we must escape certain characters,
* as described in RFC4627. This applies to both member names and
* DATA_TYPE_STRING values.
@@ -44,7 +99,8 @@
* representable Unicode characters included in their escaped numeric form.
*/
static int
-nvlist_print_json_string(FILE *fp, const char *input)
+nvlist_print_json_string(const char *input, char **bufp, size_t *blen,
+ off_t *offp)
{
mbstate_t mbr;
wchar_t c;
@@ -52,29 +108,29 @@ nvlist_print_json_string(FILE *fp, const char *input)
bzero(&mbr, sizeof (mbr));
- FPRINTF(fp, "\"");
+ FPRINTF(bufp, blen, offp, "\"");
while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) {
switch (c) {
case '"':
- FPRINTF(fp, "\\\"");
+ FPRINTF(bufp, blen, offp, "\\\"");
break;
case '\n':
- FPRINTF(fp, "\\n");
+ FPRINTF(bufp, blen, offp, "\\n");
break;
case '\r':
- FPRINTF(fp, "\\r");
+ FPRINTF(bufp, blen, offp, "\\r");
break;
case '\\':
- FPRINTF(fp, "\\\\");
+ FPRINTF(bufp, blen, offp, "\\\\");
break;
case '\f':
- FPRINTF(fp, "\\f");
+ FPRINTF(bufp, blen, offp, "\\f");
break;
case '\t':
- FPRINTF(fp, "\\t");
+ FPRINTF(bufp, blen, offp, "\\t");
break;
case '\b':
- FPRINTF(fp, "\\b");
+ FPRINTF(bufp, blen, offp, "\\b");
break;
default:
if ((c >= 0x00 && c <= 0x1f) ||
@@ -84,13 +140,15 @@ nvlist_print_json_string(FILE *fp, const char *input)
* characters in the Basic Multilingual Plane
* as JSON-escaped multibyte characters.
*/
- FPRINTF(fp, "\\u%04x", (int)(0xffff & c));
+ FPRINTF(bufp, blen, offp, "\\u%04x",
+ (int)(0xffff & c));
} else if (c >= 0x20 && c <= 0x7f) {
/*
* Render other 7-bit ASCII characters directly
* and drop other, unrepresentable characters.
*/
- FPRINTF(fp, "%c", (int)(0xff & c));
+ FPRINTF(bufp, blen, offp, "%c",
+ (int)(0xff & c));
}
break;
}
@@ -105,98 +163,103 @@ nvlist_print_json_string(FILE *fp, const char *input)
return (-1);
}
- FPRINTF(fp, "\"");
+ FPRINTF(bufp, blen, offp, "\"");
return (0);
}
-/*
- * Dump a JSON-formatted representation of an nvlist to the provided FILE *.
- * This routine does not output any new-lines or additional whitespace other
- * than that contained in strings, nor does it call fflush(3C).
- */
-int
-nvlist_print_json(FILE *fp, nvlist_t *nvl)
+static int
+nvlist_do_json(nvlist_t *nvl, char **bufp, size_t *blen, off_t *offp)
{
nvpair_t *curr;
boolean_t first = B_TRUE;
- FPRINTF(fp, "{");
+ FPRINTF(bufp, blen, offp, "{");
for (curr = nvlist_next_nvpair(nvl, NULL); curr;
curr = nvlist_next_nvpair(nvl, curr)) {
data_type_t type = nvpair_type(curr);
if (!first)
- FPRINTF(fp, ",");
+ FPRINTF(bufp, blen, offp, ",");
else
first = B_FALSE;
- if (nvlist_print_json_string(fp, nvpair_name(curr)) == -1)
+ if (nvlist_print_json_string(nvpair_name(curr), bufp, blen,
+ offp) == -1)
return (-1);
- FPRINTF(fp, ":");
+ FPRINTF(bufp, blen, offp, ":");
switch (type) {
case DATA_TYPE_STRING: {
char *string = fnvpair_value_string(curr);
- if (nvlist_print_json_string(fp, string) == -1)
+ if (nvlist_print_json_string(string, bufp, blen,
+ offp) == -1)
return (-1);
break;
}
case DATA_TYPE_BOOLEAN: {
- FPRINTF(fp, "true");
+ FPRINTF(bufp, blen, offp, "true");
break;
}
case DATA_TYPE_BOOLEAN_VALUE: {
- FPRINTF(fp, "%s", fnvpair_value_boolean_value(curr) ==
- B_TRUE ? "true" : "false");
+ FPRINTF(bufp, blen, offp, "%s",
+ fnvpair_value_boolean_value(curr) == B_TRUE ?
+ "true" : "false");
break;
}
case DATA_TYPE_BYTE: {
- FPRINTF(fp, "%hhu", fnvpair_value_byte(curr));
+ FPRINTF(bufp, blen, offp, "%hhu",
+ fnvpair_value_byte(curr));
break;
}
case DATA_TYPE_INT8: {
- FPRINTF(fp, "%hhd", fnvpair_value_int8(curr));
+ FPRINTF(bufp, blen, offp, "%hhd",
+ fnvpair_value_int8(curr));
break;
}
case DATA_TYPE_UINT8: {
- FPRINTF(fp, "%hhu", fnvpair_value_uint8_t(curr));
+ FPRINTF(bufp, blen, offp, "%hhu",
+ fnvpair_value_uint8_t(curr));
break;
}
case DATA_TYPE_INT16: {
- FPRINTF(fp, "%hd", fnvpair_value_int16(curr));
+ FPRINTF(bufp, blen, offp, "%hd",
+ fnvpair_value_int16(curr));
break;
}
case DATA_TYPE_UINT16: {
- FPRINTF(fp, "%hu", fnvpair_value_uint16(curr));
+ FPRINTF(bufp, blen, offp, "%hu",
+ fnvpair_value_uint16(curr));
break;
}
case DATA_TYPE_INT32: {
- FPRINTF(fp, "%d", fnvpair_value_int32(curr));
+ FPRINTF(bufp, blen, offp, "%d",
+ fnvpair_value_int32(curr));
break;
}
case DATA_TYPE_UINT32: {
- FPRINTF(fp, "%u", fnvpair_value_uint32(curr));
+ FPRINTF(bufp, blen, offp, "%u",
+ fnvpair_value_uint32(curr));
break;
}
case DATA_TYPE_INT64: {
- FPRINTF(fp, "%lld",
+ FPRINTF(bufp, blen, offp, "%lld",
(long long)fnvpair_value_int64(curr));
break;
}
case DATA_TYPE_UINT64: {
- FPRINTF(fp, "%llu",
+ FPRINTF(bufp, blen, offp, "%llu",
(unsigned long long)fnvpair_value_uint64(curr));
break;
}
@@ -204,20 +267,21 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
case DATA_TYPE_HRTIME: {
hrtime_t val;
VERIFY0(nvpair_value_hrtime(curr, &val));
- FPRINTF(fp, "%llu", (unsigned long long)val);
+ FPRINTF(bufp, blen, offp, "%llu",
+ (unsigned long long)val);
break;
}
case DATA_TYPE_DOUBLE: {
double val;
VERIFY0(nvpair_value_double(curr, &val));
- FPRINTF(fp, "%f", val);
+ FPRINTF(bufp, blen, offp, "%f", val);
break;
}
case DATA_TYPE_NVLIST: {
- if (nvlist_print_json(fp,
- fnvpair_value_nvlist(curr)) == -1)
+ if (nvlist_do_json(fnvpair_value_nvlist(curr), bufp,
+ blen, offp) == -1)
return (-1);
break;
}
@@ -226,14 +290,15 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
char **val;
uint_t valsz, i;
VERIFY0(nvpair_value_string_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- if (nvlist_print_json_string(fp, val[i]) == -1)
+ FPRINTF(bufp, blen, offp, ",");
+ if (nvlist_print_json_string(val[i], bufp,
+ blen, offp) == -1)
return (-1);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -241,14 +306,15 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
nvlist_t **val;
uint_t valsz, i;
VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- if (nvlist_print_json(fp, val[i]) == -1)
+ FPRINTF(bufp, blen, offp, ",");
+ if (nvlist_do_json(val[i], bufp, blen,
+ offp) == -1)
return (-1);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -256,14 +322,14 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
boolean_t *val;
uint_t valsz, i;
VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, val[i] == B_TRUE ?
+ FPRINTF(bufp, blen, offp, ",");
+ FPRINTF(bufp, blen, offp, val[i] == B_TRUE ?
"true" : "false");
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -271,13 +337,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
uchar_t *val;
uint_t valsz, i;
VERIFY0(nvpair_value_byte_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%hhu", val[i]);
+ FPRINTF(bufp, blen, offp, ",");
+ FPRINTF(bufp, blen, offp, "%hhu", val[i]);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -285,13 +351,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
uint8_t *val;
uint_t valsz, i;
VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%hhu", val[i]);
+ FPRINTF(bufp, blen, offp, ",");
+ FPRINTF(bufp, blen, offp, "%hhu", val[i]);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -299,13 +365,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
int8_t *val;
uint_t valsz, i;
VERIFY0(nvpair_value_int8_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%hd", val[i]);
+ FPRINTF(bufp, blen, offp, ",");
+ FPRINTF(bufp, blen, offp, "%hd", val[i]);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -313,13 +379,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
uint16_t *val;
uint_t valsz, i;
VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%hu", val[i]);
+ FPRINTF(bufp, blen, offp, ",");
+ FPRINTF(bufp, blen, offp, "%hu", val[i]);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -327,13 +393,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
int16_t *val;
uint_t valsz, i;
VERIFY0(nvpair_value_int16_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%hd", val[i]);
+ FPRINTF(bufp, blen, offp, ",");
+ FPRINTF(bufp, blen, offp, "%hd", val[i]);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -341,13 +407,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
uint32_t *val;
uint_t valsz, i;
VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%u", val[i]);
+ FPRINTF(bufp, blen, offp, ",");
+ FPRINTF(bufp, blen, offp, "%u", val[i]);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -355,13 +421,13 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
int32_t *val;
uint_t valsz, i;
VERIFY0(nvpair_value_int32_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%d", val[i]);
+ FPRINTF(bufp, blen, offp, ",");
+ FPRINTF(bufp, blen, offp, "%d", val[i]);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -369,14 +435,14 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
uint64_t *val;
uint_t valsz, i;
VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%llu",
+ FPRINTF(bufp, blen, offp, ",");
+ FPRINTF(bufp, blen, offp, "%llu",
(unsigned long long)val[i]);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -384,13 +450,14 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
int64_t *val;
uint_t valsz, i;
VERIFY0(nvpair_value_int64_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
+ FPRINTF(bufp, blen, offp, "[");
for (i = 0; i < valsz; i++) {
if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%lld", (long long)val[i]);
+ FPRINTF(bufp, blen, offp, ",");
+ FPRINTF(bufp, blen, offp, "%lld",
+ (long long)val[i]);
}
- FPRINTF(fp, "]");
+ FPRINTF(bufp, blen, offp, "]");
break;
}
@@ -401,6 +468,41 @@ nvlist_print_json(FILE *fp, nvlist_t *nvl)
}
- FPRINTF(fp, "}");
+ FPRINTF(bufp, blen, offp, "}");
return (0);
}
+
+int
+nvlist_dump_json(nvlist_t *nvl, char **bufp)
+{
+ off_t off = 0;
+ size_t l = 0;
+
+ *bufp = NULL;
+ return (nvlist_do_json(nvl, bufp, &l, &off));
+}
+
+/* ARGSUSED */
+void
+nvlist_dump_json_free(nvlist_t *nvl, char *buf)
+{
+ free(buf);
+}
+
+/*
+ * Dump a JSON-formatted representation of an nvlist to the provided FILE *.
+ * This routine does not output any new-lines or additional whitespace other
+ * than that contained in strings, nor does it call fflush(3C).
+ */
+int
+nvlist_print_json(FILE *fp, nvlist_t *nvl)
+{
+ int ret;
+ char *buf;
+
+ if ((ret = nvlist_dump_json(nvl, &buf)) < 0)
+ return (ret);
+ ret = fprintf(fp, "%s", buf);
+ nvlist_dump_json_free(nvl, buf);
+ return (ret);
+}
diff --git a/usr/src/lib/libppt/Makefile b/usr/src/lib/libppt/Makefile
index d8f34163ab..7119d6715c 100644
--- a/usr/src/lib/libppt/Makefile
+++ b/usr/src/lib/libppt/Makefile
@@ -10,7 +10,7 @@
#
#
-# Copyright 2018 Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
#
include $(SRC)/lib/Makefile.lib
@@ -20,6 +20,12 @@ SUBDIRS = $(MACH) $(BUILD64) $(MACH64)
HDRS = libppt.h
HDRDIR = common
+ETCFILES= ppt_matches
+ROOTETC= $(ROOT)/etc
+IETCFILES= $(ETCFILES:%=$(ROOTETC)/%)
+
+$(IETCFILES) := FILEMODE = 0444
+
all := TARGET= all
clean := TARGET= clean
clobber := TARGET= clobber
@@ -33,11 +39,16 @@ install_h: $(ROOTHDRS)
all install: install_h
+install: $(IETCFILES)
+
check: $(CHECKHDRS)
$(SUBDIRS): FRC
@cd $@; pwd; $(MAKE) $(TARGET)
+$(ROOTETC)/%: %
+ $(INS.file)
+
FRC:
include $(SRC)/lib/Makefile.targ
diff --git a/usr/src/lib/libppt/common/llib-lppt b/usr/src/lib/libppt/common/llib-lppt
new file mode 100644
index 0000000000..dadd992a31
--- /dev/null
+++ b/usr/src/lib/libppt/common/llib-lppt
@@ -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 2018 Joyent, Inc.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <libppt.h>
diff --git a/usr/src/lib/libppt/ppt_matches b/usr/src/lib/libppt/ppt_matches
new file mode 100644
index 0000000000..03ff7e03ba
--- /dev/null
+++ b/usr/src/lib/libppt/ppt_matches
@@ -0,0 +1 @@
+pci10de,15f0
diff --git a/usr/src/lib/libproc/common/Pcontrol.c b/usr/src/lib/libproc/common/Pcontrol.c
index 90ce97c191..7e7c254bfc 100644
--- a/usr/src/lib/libproc/common/Pcontrol.c
+++ b/usr/src/lib/libproc/common/Pcontrol.c
@@ -349,10 +349,16 @@ static const ps_ops_t P_live_ops = {
void
_libproc_init(void)
{
+ const char *root;
+
_libproc_debug = getenv("LIBPROC_DEBUG") != NULL;
_libproc_no_qsort = getenv("LIBPROC_NO_QSORT") != NULL;
_libproc_incore_elf = getenv("LIBPROC_INCORE_ELF") != NULL;
+ if ((root = zone_get_nroot()) != NULL)
+ (void) snprintf(procfs_path, sizeof (procfs_path), "%s/proc",
+ root);
+
(void) sigfillset(&blockable_sigs);
(void) sigdelset(&blockable_sigs, SIGKILL);
(void) sigdelset(&blockable_sigs, SIGSTOP);
@@ -1798,6 +1804,9 @@ prldump(const char *caller, lwpstatus_t *lsp)
case PR_SUSPENDED:
dprintf("%s: SUSPENDED\n", caller);
break;
+ case PR_BRAND:
+ dprintf("%s: BRANDPRIVATE (%d)\n", caller, lsp->pr_what);
+ break;
default:
dprintf("%s: Unknown\n", caller);
break;
@@ -1977,6 +1986,7 @@ Pstopstatus(struct ps_prochandle *P,
case PR_FAULTED:
case PR_JOBCONTROL:
case PR_SUSPENDED:
+ case PR_BRAND:
break;
default:
errno = EPROTO;
@@ -3551,6 +3561,7 @@ Lstopstatus(struct ps_lwphandle *L,
case PR_FAULTED:
case PR_JOBCONTROL:
case PR_SUSPENDED:
+ case PR_BRAND:
break;
default:
errno = EPROTO;
diff --git a/usr/src/lib/libproc/common/Pcontrol.h b/usr/src/lib/libproc/common/Pcontrol.h
index b5de8ccd50..d701140173 100644
--- a/usr/src/lib/libproc/common/Pcontrol.h
+++ b/usr/src/lib/libproc/common/Pcontrol.h
@@ -128,6 +128,7 @@ typedef struct file_info { /* symbol information for a mapped file */
size_t file_shstrsz; /* section header string table size */
uintptr_t *file_saddrs; /* section header addresses */
uint_t file_nsaddrs; /* number of section header addresses */
+ boolean_t file_cvt; /* Have we tried to convert this? */
} file_info_t;
typedef struct map_info { /* description of an address space mapping */
diff --git a/usr/src/lib/libproc/common/Psymtab.c b/usr/src/lib/libproc/common/Psymtab.c
index b92e5a7d56..7527c7e10c 100644
--- a/usr/src/lib/libproc/common/Psymtab.c
+++ b/usr/src/lib/libproc/common/Psymtab.c
@@ -742,6 +742,65 @@ Pname_to_loadobj(struct ps_prochandle *P, const char *name)
return (Plmid_to_loadobj(P, PR_LMID_EVERY, name));
}
+/*
+ * We've been given a file_info_t which doesn't have any CTF. However, it may
+ * have information that's in a format that we could convert if on the fly.
+ * We'll first try to convert the alternate debug file, if present, and then
+ * move onto the default file. The reason we prefer the alternate debug file is
+ * that if both exist, then it likely has any usable debugging information.
+ */
+static ctf_file_t *
+Pconvert_file_ctf(file_info_t *fptr)
+{
+ int err;
+ ctf_convert_t *cch;
+ ctf_file_t *fp;
+ char errmsg[1024];
+
+ /*
+ * Provide an opt in.
+ */
+ if (getenv("LIBPROC_CTFCONVERT") == NULL)
+ return (NULL);
+
+ /*
+ * If we've already attempted to call this, then that's it. No reason to
+ * pretend we'll be more successful again another time.
+ */
+ if (fptr->file_cvt == B_TRUE)
+ return (NULL);
+
+ cch = ctf_convert_init(&err);
+ if (cch == NULL)
+ return (NULL);
+
+ fptr->file_cvt = B_TRUE;
+
+ fp = NULL;
+ if (fptr->file_dbgelf != NULL) {
+ fp = ctf_elfconvert(cch, fptr->file_fd, fptr->file_dbgelf, &err,
+ errmsg, sizeof (errmsg));
+ if (fp == NULL) {
+ dprintf("failed to convert %s: %s\n", fptr->file_pname,
+ err == ECTF_CONVBKERR ? errmsg : ctf_errmsg(err));
+ }
+ }
+ if (fp == NULL) {
+ fp = ctf_elfconvert(cch, fptr->file_fd, fptr->file_elf, &err,
+ errmsg, sizeof (errmsg));
+ if (fp == NULL) {
+ dprintf("failed to convert %s: %s\n", fptr->file_pname,
+ err == ECTF_CONVBKERR ? errmsg : ctf_errmsg(err));
+ }
+ }
+ if (fp != NULL) {
+ fptr->file_ctfp = fp;
+ }
+
+ ctf_convert_fini(cch);
+ return (NULL);
+}
+
ctf_file_t *
Pbuild_file_ctf(struct ps_prochandle *P, file_info_t *fptr)
{
@@ -754,8 +813,9 @@ Pbuild_file_ctf(struct ps_prochandle *P, file_info_t *fptr)
Pbuild_file_symtab(P, fptr);
- if (fptr->file_ctf_size == 0)
- return (NULL);
+ if (fptr->file_ctf_size == 0) {
+ return (Pconvert_file_ctf(fptr));
+ }
symp = fptr->file_ctf_dyn ? &fptr->file_dynsym : &fptr->file_symtab;
if (symp->sym_data_pri == NULL)
diff --git a/usr/src/lib/librefhash/Makefile b/usr/src/lib/librefhash/Makefile
new file mode 100644
index 0000000000..b6853cb0c6
--- /dev/null
+++ b/usr/src/lib/librefhash/Makefile
@@ -0,0 +1,41 @@
+#
+# 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 2020 Joyent, Inc.
+#
+
+include ../Makefile.lib
+
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+
+.KEEP_STATE:
+
+all clean clobber install: $(SUBDIRS)
+
+install: install_h $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/librefhash/Makefile.com b/usr/src/lib/librefhash/Makefile.com
new file mode 100644
index 0000000000..1f914c46a6
--- /dev/null
+++ b/usr/src/lib/librefhash/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 2020 Joyent, Inc.
+#
+
+LIBRARY = librefhash.a
+VERS = .1
+OBJECTS = list.o \
+ refhash.o
+HASHCOMDIR = $(SRC)/common/refhash
+LISTCOMDIR = $(SRC)/common/list
+
+include ../../Makefile.lib
+
+SRCDIR = ../common
+SRCS = $(HASHCOMDIR)/refhash.c $(LISTCOMDIR)/list.c
+LIBS = $(DYNLIB)
+
+LDLIBS += -lc -lumem
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+include ../../Makefile.targ
+
+objs/%.o pics/%.o: $(LISTCOMDIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+objs/%.o pics/%.o: $(HASHCOMDIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/lib/librefhash/amd64/Makefile b/usr/src/lib/librefhash/amd64/Makefile
new file mode 100644
index 0000000000..5a2ea08b45
--- /dev/null
+++ b/usr/src/lib/librefhash/amd64/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 2020 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/librefhash/common/mapfile-vers b/usr/src/lib/librefhash/common/mapfile-vers
new file mode 100644
index 0000000000..f7f91ef84f
--- /dev/null
+++ b/usr/src/lib/librefhash/common/mapfile-vers
@@ -0,0 +1,48 @@
+#
+# 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.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION ILLUMOSprivate {
+ global:
+ refhash_create;
+ refhash_destroy;
+ refhash_insert;
+ refhash_remove;
+ refhash_lookup;
+ refhash_linear_search;
+ refhash_hold;
+ refhash_rele;
+ refhash_first;
+ refhash_next;
+ refhash_obj_valid;
+ local:
+ *;
+};
+
diff --git a/usr/src/lib/librefhash/i386/Makefile b/usr/src/lib/librefhash/i386/Makefile
new file mode 100644
index 0000000000..aeec694b08
--- /dev/null
+++ b/usr/src/lib/librefhash/i386/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 2020 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/librefhash/sparc/Makefile b/usr/src/lib/librefhash/sparc/Makefile
new file mode 100644
index 0000000000..aeec694b08
--- /dev/null
+++ b/usr/src/lib/librefhash/sparc/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 2020 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/librefhash/sparcv9/Makefile b/usr/src/lib/librefhash/sparcv9/Makefile
new file mode 100644
index 0000000000..5a2ea08b45
--- /dev/null
+++ b/usr/src/lib/librefhash/sparcv9/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 2020 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libresolv2_joy/Makefile b/usr/src/lib/libresolv2_joy/Makefile
new file mode 100644
index 0000000000..052dfdd092
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/Makefile
@@ -0,0 +1,76 @@
+#
+# 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) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+
+include ../../Makefile.master
+include ../Makefile.lib
+
+$(ROOTSVCMETHOD) := FILEMODE = 0555
+
+SUBDIRS= include $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+LIBRARY= libresolv_joy.a
+TEXT_DOMAIN= SUNW_OST_OSLIB
+XGETFLAGS= -a
+POFILE= $(LIBRARY:.a=.po)
+POFILES= generic.po
+
+SED= sed
+GREP= grep
+
+.KEEP_STATE:
+
+all clean clobber lint: $(SUBDIRS)
+
+install: $(SUBDIRS)
+
+check: $(CHECKHDRS) $(CHKMANIFEST)
+
+_msg: $(MSGDOMAIN) $(POFILE)
+ $(RM) $(MSGDOMAIN)/$(POFILE)
+ $(CP) $(POFILE) $(MSGDOMAIN)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+$(POFILES):
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) *.[ch]* */*.[ch]*
+ $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@
+ $(RM) messages.po
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET); echo
+
+FRC:
+
diff --git a/usr/src/lib/libresolv2_joy/Makefile.com b/usr/src/lib/libresolv2_joy/Makefile.com
new file mode 100644
index 0000000000..59eda28fed
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/Makefile.com
@@ -0,0 +1,165 @@
+#
+# 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 2019 Joyent, Inc.
+
+LIBRARY= libresolv_joy.a
+VERS= .2
+
+BSDOBJS= daemon.o putenv.o strcasecmp.o strsep.o \
+ ftruncate.o readv.o strdup.o strtoul.o \
+ gettimeofday.o setenv.o strerror.o utimes.o \
+ mktemp.o setitimer.o strpbrk.o writev.o
+
+DSTOBJS= dst_api.o support.o hmac_link.o
+
+# inet_addr, inet_pton, inet_ntop, and inet_ntoa removed due to overlap with
+# libnsl
+INETOBJS= inet_net_pton.o inet_neta.o inet_lnaof.o \
+ inet_netof.o nsap_addr.o inet_makeaddr.o \
+ inet_network.o inet_net_ntop.o inet_cidr_ntop.o \
+ inet_cidr_pton.o inet_data.o
+
+# build only the IRS objects that the ISC libbind's make would
+IRSTHROBJS= gethostent_r.o getnetent_r.o getnetgrent_r.o \
+ getprotoent_r.o getservent_r.o
+IRSOBJS= ${IRSTHROBJS} \
+ dns.o dns_ho.o dns_nw.o dns_pr.o \
+ dns_sv.o gai_strerror.o gen.o gen_ho.o \
+ gen_ng.o gen_nw.o gen_pr.o gen_sv.o \
+ getaddrinfo.o gethostent.o getnameinfo.o getnetent.o \
+ getnetgrent.o getprotoent.o getservent.o hesiod.o \
+ irp.o irp_ho.o irp_ng.o irp_nw.o \
+ irp_pr.o irp_sv.o irpmarshall.o irs_data.o \
+ lcl.o lcl_ho.o lcl_ng.o lcl_nw.o \
+ lcl_pr.o lcl_sv.o nis.o nul_ng.o \
+ util.o
+
+ISCOBJS= assertions.o base64.o bitncmp.o ctl_clnt.o \
+ ctl_p.o ctl_srvr.o ev_connects.o ev_files.o \
+ ev_streams.o ev_timers.o ev_waits.o eventlib.o \
+ heap.o hex.o logging.o memcluster.o \
+ movefile.o tree.o
+
+NAMESEROBJS= ns_date.o ns_name.o ns_netint.o ns_parse.o \
+ ns_print.o ns_samedomain.o ns_sign.o ns_ttl.o \
+ ns_verify.o ns_rdata.o ns_newmsg.o
+
+RESOLVOBJS= herror.o mtctxres.o res_comp.o res_data.o \
+ res_debug.o res_findzonecut.o res_init.o \
+ res_mkquery.o res_mkupdate.o res_query.o res_send.o \
+ res_sendsigned.o res_update.o
+
+SUNWOBJS= sunw_mtctxres.o sunw_updrec.o sunw_wrappers.o
+
+OBJECTS= $(BSDOBJS) $(DSTOBJS) $(INETOBJS) $(IRSOBJS) $(ISCOBJS) \
+ $(NAMESEROBJS) $(RESOLVOBJS) $(SUNWOBJS)
+
+# include library definitions
+include ../../Makefile.lib
+
+# install this library in the root filesystem
+include ../../Makefile.rootfs
+
+# CC -v complains about things we aren't going to change in the ISC code
+CCVERBOSE=
+
+SRCDIR = ../common
+SRCS= $(BSDOBJS:%.o=../common/bsd/%.c) \
+ $(DSTOBJS:%.o=../common/dst/%.c) \
+ $(INETOBJS:%.o=../common/inet/%.c) \
+ $(IRSOBJS:%.o=../common/irs/%.c) \
+ $(ISCOBJS:%.o=../common/isc/%.c) \
+ $(NAMESEROBJS:%.o=../common/nameser/%.c) \
+ $(RESOLVOBJS:%.o=../common/resolv/%.c) \
+ $(SUNWOBJS:%.o=../common/sunw/%.c)
+
+LIBS = $(DYNLIB) $(LINTLIB)
+
+$(LINTLIB):= SRCS = ../common/llib-lresolv_joy
+
+# Local Libresolv definitions
+
+SOLCOMPAT = -Dsocket=_socket
+CRYPTFLAGS= -DHMAC_MD5 -DUSE_MD5
+
+LOCFLAGS += $(CRYPTFLAGS)
+LOCFLAGS += -D_SYS_STREAM_H -D_REENTRANT -DSVR4 -DSUNW_OPTIONS \
+ $(SOLCOMPAT) -I../include -I../../common/inc
+
+CPPFLAGS += $(LOCFLAGS)
+
+CERRWARN += -_gcc=-Wno-implicit-function-declaration
+
+# not linted
+SMATCH=off
+
+DYNFLAGS += $(ZNODELETE)
+
+LDLIBS += -lsocket -lnsl -lc -lmd
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+# include library targets
+include ../../Makefile.targ
+
+pics/%.o: ../common/bsd/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+pics/%.o: ../common/dst/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+pics/%.o: ../common/inet/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+pics/%.o: ../common/irs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+pics/%.o: ../common/isc/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+pics/%.o: ../common/nameser/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+pics/%.o: ../common/resolv/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+pics/%.o: ../common/sunw/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+# install rule for lint library target
+$(ROOTLINTDIR)/%: ../common/%
+ $(INS.file)
diff --git a/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE b/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..719d77fea2
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE
@@ -0,0 +1,185 @@
+ * Copyright (c) 1995-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+
+ * Copyright (c) 1983, 1990, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+
+/* Copyright (C) RSA Data Security, Inc. created 1986-1987, 1990,
+ 1992-1994, 1996. This is an
+ unpublished work protected as such under copyright law. This work
+ contains proprietary, confidential, and trade secret information of
+ RSA Data Security, Inc. Use, disclosure or reproduction without the
+ express written authorization of RSA Data Security, Inc. is
+ prohibited.
+ */
+
+ DNSSAFE LICENSE TERMS
+
+ This BIND software includes the DNSsafe software from RSA Data
+ Security, Inc., which is copyrighted software that can only be
+ distributed under the terms of this license agreement.
+
+ The DNSsafe software cannot be used or distributed separately from the
+ BIND software. You only have the right to use it or distribute it as
+ a bundled, integrated product.
+
+ The DNSsafe software can ONLY be used to provide authentication for
+ resource records in the Domain Name System, as specified in RFC 2065
+ and successors. You cannot modify the BIND software to use the
+ DNSsafe software for other purposes, or to make its cryptographic
+ functions available to end-users for other uses.
+
+ If you modify the DNSsafe software itself, you cannot modify its
+ documented API, and you must grant RSA Data Security the right to use,
+ modify, and distribute your modifications, including the right to use
+ any patents or other intellectual property that your modifications
+ depend upon.
+
+ You must not remove, alter, or destroy any of RSA's copyright notices
+ or license information. When distributing the software to the Federal
+ Government, it must be licensed to them as "commercial computer
+ software" protected under 48 CFR 12.212 of the FAR, or 48 CFR
+ 227.7202.1 of the DFARS.
+
+ You must not violate United States export control laws by distributing
+ the DNSsafe software or information about it, when such distribution
+ is prohibited by law.
+
+ THE DNSSAFE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY
+ WHATSOEVER. RSA HAS NO OBLIGATION TO SUPPORT, CORRECT, UPDATE OR
+ MAINTAIN THE RSA SOFTWARE. RSA DISCLAIMS ALL WARRANTIES, EXPRESS,
+ IMPLIED OR STATUTORY, AS TO ANY MATTER WHATSOEVER, INCLUDING ALL
+ IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY RIGHTS.
+
+ If you desire to use DNSsafe in ways that these terms do not permit,
+ please contact RSA Data Security, Inc., 100 Marine Parkway, Redwood
+ City, California 94065, USA, to discuss alternate licensing
+ arrangements.
+
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ *
+ * Permission to use, copy modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THE SOFTWARE.
+
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by WIDE Project and
+ * its contributors.
+ * 4. Neither the name of the project 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 PROJECT 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 PROJECT 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/lib/libresolv2_joy/THIRDPARTYLICENSE.descrip b/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..67315d8f33
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+BIND SOFTWARE
diff --git a/usr/src/lib/libresolv2_joy/amd64/Makefile b/usr/src/lib/libresolv2_joy/amd64/Makefile
new file mode 100644
index 0000000000..2e8cdecf75
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/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 (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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/daemon.c b/usr/src/lib/libresolv2_joy/common/bsd/daemon.c
new file mode 100644
index 0000000000..54ff83b753
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/daemon.c
@@ -0,0 +1,81 @@
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93";
+static const char rcsid[] = "$Id: daemon.c,v 1.2 2005/04/27 04:56:10 sra Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Copyright (c) 1990, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#include "port_before.h"
+
+#include <fcntl.h>
+#include <paths.h>
+#include <unistd.h>
+
+#include "port_after.h"
+
+#ifndef NEED_DAEMON
+int __bind_daemon__;
+#else
+
+int
+daemon(int nochdir, int noclose) {
+ int fd;
+
+ switch (fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ _exit(0);
+ }
+
+ if (setsid() == -1)
+ return (-1);
+
+ if (!nochdir)
+ (void)chdir("/");
+
+ if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void)close (fd);
+ }
+ return (0);
+}
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/ftruncate.c b/usr/src/lib/libresolv2_joy/common/bsd/ftruncate.c
new file mode 100644
index 0000000000..744d205ce7
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/ftruncate.c
@@ -0,0 +1,60 @@
+/*! \file
+ * \brief
+ * ftruncate - set file size, BSD Style
+ *
+ * shortens or enlarges the file as neeeded
+ * uses some undocumented locking call. It is known to work on SCO unix,
+ * other vendors should try.
+ * The #error directive prevents unsupported OSes
+ */
+
+#include "port_before.h"
+
+#if defined(M_UNIX)
+#define OWN_FTRUNCATE
+#include <stdio.h>
+#ifdef _XOPEN_SOURCE
+#undef _XOPEN_SOURCE
+#endif
+#ifdef _POSIX_SOURCE
+#undef _POSIX_SOURCE
+#endif
+
+#include <fcntl.h>
+
+#include "port_after.h"
+
+int
+__ftruncate(int fd, long wantsize) {
+ long cursize;
+
+ /* determine current file size */
+ if ((cursize = lseek(fd, 0L, 2)) == -1)
+ return (-1);
+
+ /* maybe lengthen... */
+ if (cursize < wantsize) {
+ if (lseek(fd, wantsize - 1, 0) == -1 ||
+ write(fd, "", 1) == -1) {
+ return (-1);
+ }
+ return (0);
+ }
+
+ /* maybe shorten... */
+ if (wantsize < cursize) {
+ struct flock fl;
+
+ fl.l_whence = 0;
+ fl.l_len = 0;
+ fl.l_start = wantsize;
+ fl.l_type = F_WRLCK;
+ return (fcntl(fd, F_FREESP, &fl));
+ }
+ return (0);
+}
+#endif
+
+#ifndef OWN_FTRUNCATE
+int __bindcompat_ftruncate;
+#endif
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/gettimeofday.c b/usr/src/lib/libresolv2_joy/common/bsd/gettimeofday.c
new file mode 100644
index 0000000000..f5ff8c54f5
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/gettimeofday.c
@@ -0,0 +1,58 @@
+#include "port_before.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include "port_after.h"
+
+#if !defined(NEED_GETTIMEOFDAY)
+/*%
+ * gettimeofday() occasionally returns invalid tv_usec on some platforms.
+ */
+#define MILLION 1000000
+#undef gettimeofday
+
+int
+isc__gettimeofday(struct timeval *tp, struct timezone *tzp) {
+ int res;
+
+ res = gettimeofday(tp, tzp);
+ if (res < 0)
+ return (res);
+ if (tp == NULL)
+ return (res);
+ if (tp->tv_usec < 0) {
+ do {
+ tp->tv_usec += MILLION;
+ tp->tv_sec--;
+ } while (tp->tv_usec < 0);
+ goto log;
+ } else if (tp->tv_usec > MILLION) {
+ do {
+ tp->tv_usec -= MILLION;
+ tp->tv_sec++;
+ } while (tp->tv_usec > MILLION);
+ goto log;
+ }
+ return (res);
+ log:
+ syslog(LOG_ERR, "gettimeofday: tv_usec out of range\n");
+ return (res);
+}
+#else
+int
+gettimeofday(struct timeval *tvp, struct _TIMEZONE *tzp) {
+ time_t clock, time(time_t *);
+
+ if (time(&clock) == (time_t) -1)
+ return (-1);
+ if (tvp) {
+ tvp->tv_sec = clock;
+ tvp->tv_usec = 0;
+ }
+ if (tzp) {
+ tzp->tz_minuteswest = 0;
+ tzp->tz_dsttime = 0;
+ }
+ return (0);
+}
+#endif /*NEED_GETTIMEOFDAY*/
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/mktemp.c b/usr/src/lib/libresolv2_joy/common/bsd/mktemp.c
new file mode 100644
index 0000000000..2d41b9b0ee
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/mktemp.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1987, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "port_after.h"
+
+#if (!defined(NEED_MKTEMP)) && (!defined(NEED_MKSTEMP))
+int __mktemp_unneeded__;
+#else
+
+static int gettemp(char *path, int *doopen);
+
+#ifdef NEED_MKSTEMP
+mkstemp(char *path) {
+ int fd;
+
+ return (gettemp(path, &fd) ? fd : -1);
+}
+#endif
+
+#ifdef NEED_MKTEMP
+char *
+mktemp(char *path) {
+ return(gettemp(path, (int *)NULL) ? path : (char *)NULL);
+}
+#endif
+
+static int
+gettemp(char *path, int *doopen) {
+ char *start, *trv;
+ struct stat sbuf;
+ u_int pid;
+
+ pid = getpid();
+ for (trv = path; *trv; ++trv); /*%< extra X's get set to 0's */
+ while (*--trv == 'X') {
+ *trv = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+ /*
+ * check the target directory; if you have six X's and it
+ * doesn't exist this runs for a *very* long time.
+ */
+ for (start = trv + 1;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+ if (stat(path, &sbuf))
+ return(0);
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = ENOTDIR;
+ return(0);
+ }
+ *trv = '/';
+ break;
+ }
+ }
+
+ for (;;) {
+ if (doopen) {
+ if ((*doopen =
+ open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
+ return(1);
+ if (errno != EEXIST)
+ return(0);
+ }
+ else if (stat(path, &sbuf))
+ return(errno == ENOENT ? 1 : 0);
+
+ /* tricky little algorithm for backward compatibility */
+ for (trv = start;;) {
+ if (!*trv)
+ return(0);
+ if (*trv == 'z')
+ *trv++ = 'a';
+ else {
+ if (isdigit(*trv))
+ *trv = 'a';
+ else
+ ++*trv;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
+
+#endif /*NEED_MKTEMP*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/putenv.c b/usr/src/lib/libresolv2_joy/common/bsd/putenv.c
new file mode 100644
index 0000000000..de1b070de6
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/putenv.c
@@ -0,0 +1,23 @@
+#include "port_before.h"
+#include "port_after.h"
+
+/*%
+ * To give a little credit to Sun, SGI,
+ * and many vendors in the SysV world.
+ */
+
+#if !defined(NEED_PUTENV)
+int __bindcompat_putenv;
+#else
+int
+putenv(char *str) {
+ char *tmp;
+
+ for (tmp = str; *tmp && (*tmp != '='); tmp++)
+ ;
+
+ return (setenv(str, tmp, 1));
+}
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/readv.c b/usr/src/lib/libresolv2_joy/common/bsd/readv.c
new file mode 100644
index 0000000000..0a8ade0ec1
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/readv.c
@@ -0,0 +1,35 @@
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include "port_after.h"
+
+#ifndef NEED_READV
+int __bindcompat_readv;
+#else
+
+int
+__readv(fd, vp, vpcount)
+ int fd;
+ const struct iovec *vp;
+ int vpcount;
+{
+ int count = 0;
+
+ while (vpcount-- > 0) {
+ int bytes = read(fd, vp->iov_base, vp->iov_len);
+
+ if (bytes < 0)
+ return (-1);
+ count += bytes;
+ if (bytes != vp->iov_len)
+ break;
+ vp++;
+ }
+ return (count);
+}
+#endif /* NEED_READV */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/setenv.c b/usr/src/lib/libresolv2_joy/common/bsd/setenv.c
new file mode 100644
index 0000000000..f9fdbb7be4
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/setenv.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1987, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#include "port_before.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "port_after.h"
+
+#if !defined(NEED_SETENV)
+int __bindcompat_setenv;
+#else
+
+extern char **environ;
+
+static char *findenv(const char *name, int *offset);
+
+/*%
+ * setenv --
+ * Set the value of the environmental variable "name" to be
+ * "value". If rewrite is set, replace any current value.
+ */
+setenv(const char *name, const char *value, int rewrite) {
+ extern char **environ;
+ static int alloced; /*%< if allocated space before */
+ char *c;
+ int l_value, offset;
+
+ if (*value == '=') /*%< no `=' in value */
+ ++value;
+ l_value = strlen(value);
+ if ((c = findenv(name, &offset))) { /*%< find if already exists */
+ if (!rewrite)
+ return (0);
+ if (strlen(c) >= l_value) { /*%< old larger; copy over */
+ while (*c++ = *value++);
+ return (0);
+ }
+ } else { /*%< create new slot */
+ int cnt;
+ char **p;
+
+ for (p = environ, cnt = 0; *p; ++p, ++cnt);
+ if (alloced) { /*%< just increase size */
+ environ = (char **)realloc((char *)environ,
+ (size_t)(sizeof(char *) * (cnt + 2)));
+ if (!environ)
+ return (-1);
+ }
+ else { /*%< get new space */
+ alloced = 1; /*%< copy old entries into it */
+ p = malloc((size_t)(sizeof(char *) * (cnt + 2)));
+ if (!p)
+ return (-1);
+ memcpy(p, environ, cnt * sizeof(char *));
+ environ = p;
+ }
+ environ[cnt + 1] = NULL;
+ offset = cnt;
+ }
+ for (c = (char *)name; *c && *c != '='; ++c); /*%< no `=' in name */
+ if (!(environ[offset] = /*%< name + `=' + value */
+ malloc((size_t)((int)(c - name) + l_value + 2))))
+ return (-1);
+ for (c = environ[offset]; (*c = *name++) && *c != '='; ++c);
+ for (*c++ = '='; *c++ = *value++;);
+ return (0);
+}
+
+/*%
+ * unsetenv(name) --
+ * Delete environmental variable "name".
+ */
+void
+unsetenv(const char *name) {
+ char **p;
+ int offset;
+
+ while (findenv(name, &offset)) /*%< if set multiple times */
+ for (p = &environ[offset];; ++p)
+ if (!(*p = *(p + 1)))
+ break;
+}
+
+/*%
+ * findenv --
+ * Returns pointer to value associated with name, if any, else NULL.
+ * Sets offset to be the offset of the name/value combination in the
+ * environmental array, for use by setenv(3) and unsetenv(3).
+ * Explicitly removes '=' in argument name.
+ *
+ * This routine *should* be a static; don't use it.
+ */
+static char *
+findenv(const char *name, int *offset) {
+ const char *np;
+ char **p, *c;
+ int len;
+
+ if (name == NULL || environ == NULL)
+ return (NULL);
+ for (np = name; *np && *np != '='; ++np)
+ continue;
+ len = np - name;
+ for (p = environ; (c = *p) != NULL; ++p)
+ if (strncmp(c, name, len) == 0 && c[len] == '=') {
+ *offset = p - environ;
+ return (c + len + 1);
+ }
+ return (NULL);
+}
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/setitimer.c b/usr/src/lib/libresolv2_joy/common/bsd/setitimer.c
new file mode 100644
index 0000000000..d336ac80d9
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/setitimer.c
@@ -0,0 +1,25 @@
+#include "port_before.h"
+
+#include <sys/time.h>
+
+#include "port_after.h"
+
+/*%
+ * Setitimer emulation routine.
+ */
+#ifndef NEED_SETITIMER
+int __bindcompat_setitimer;
+#else
+
+int
+__setitimer(int which, const struct itimerval *value,
+ struct itimerval *ovalue)
+{
+ if (alarm(value->it_value.tv_sec) >= 0)
+ return (0);
+ else
+ return (-1);
+}
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strcasecmp.c b/usr/src/lib/libresolv2_joy/common/bsd/strcasecmp.c
new file mode 100644
index 0000000000..adf76ae483
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/strcasecmp.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 1987, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/cdefs.h>
+
+#include <string.h>
+
+#include "port_after.h"
+
+#ifndef NEED_STRCASECMP
+int __strcasecmp_unneeded__;
+#else
+
+/*%
+ * This array is designed for mapping upper and lower case letter
+ * together for a case independent comparison. The mappings are
+ * based upon ascii character sequences.
+ */
+static const u_char charmap[] = {
+ 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
+ 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
+ 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
+ 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
+ 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
+ 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
+ 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
+ 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
+ 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
+ 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
+ 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
+ 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
+ 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
+ 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
+ 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
+ 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
+ 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
+ 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
+ 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
+ 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
+ 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
+ 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
+ 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
+ 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
+ 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
+ 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
+ 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
+ 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
+ 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
+ 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
+ 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
+ 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
+};
+
+int
+strcasecmp(const char *s1, const char *s2) {
+ const u_char *cm = charmap,
+ *us1 = (const u_char *)s1,
+ *us2 = (const u_char *)s2;
+
+ while (cm[*us1] == cm[*us2++])
+ if (*us1++ == '\0')
+ return (0);
+ return (cm[*us1] - cm[*--us2]);
+}
+
+int
+strncasecmp(const char *s1, const char *s2, size_t n) {
+ if (n != 0) {
+ const u_char *cm = charmap,
+ *us1 = (const u_char *)s1,
+ *us2 = (const u_char *)s2;
+
+ do {
+ if (cm[*us1] != cm[*us2++])
+ return (cm[*us1] - cm[*--us2]);
+ if (*us1++ == '\0')
+ break;
+ } while (--n != 0);
+ }
+ return (0);
+}
+
+#endif /*NEED_STRCASECMP*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strdup.c b/usr/src/lib/libresolv2_joy/common/bsd/strdup.c
new file mode 100644
index 0000000000..a8d31e9587
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/strdup.c
@@ -0,0 +1,20 @@
+#include "port_before.h"
+
+#include <stdlib.h>
+
+#include "port_after.h"
+
+#ifndef NEED_STRDUP
+int __bind_strdup_unneeded;
+#else
+char *
+strdup(const char *src) {
+ char *dst = malloc(strlen(src) + 1);
+
+ if (dst)
+ strcpy(dst, src);
+ return (dst);
+}
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strerror.c b/usr/src/lib/libresolv2_joy/common/bsd/strerror.c
new file mode 100644
index 0000000000..107215c798
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/strerror.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1988, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "port_after.h"
+
+#ifndef NEED_STRERROR
+int __strerror_unneeded__;
+#else
+
+#ifdef USE_SYSERROR_LIST
+extern int sys_nerr;
+extern char *sys_errlist[];
+#endif
+
+const char *
+isc_strerror(int num) {
+#define UPREFIX "Unknown error: "
+ static char ebuf[40] = UPREFIX; /*%< 64-bit number + slop */
+ u_int errnum;
+ char *p, *t;
+#ifndef USE_SYSERROR_LIST
+ const char *ret;
+#endif
+ char tmp[40];
+
+ errnum = num; /*%< convert to unsigned */
+#ifdef USE_SYSERROR_LIST
+ if (errnum < (u_int)sys_nerr)
+ return (sys_errlist[errnum]);
+#else
+#undef strerror
+ ret = strerror(num); /*%< call strerror() in libc */
+ if (ret != NULL)
+ return(ret);
+#endif
+
+ /* Do this by hand, so we don't include stdio(3). */
+ t = tmp;
+ do {
+ *t++ = "0123456789"[errnum % 10];
+ } while (errnum /= 10);
+ for (p = ebuf + sizeof(UPREFIX) - 1;;) {
+ *p++ = *--t;
+ if (t <= tmp)
+ break;
+ }
+ return (ebuf);
+}
+
+#endif /*NEED_STRERROR*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strpbrk.c b/usr/src/lib/libresolv2_joy/common/bsd/strpbrk.c
new file mode 100644
index 0000000000..01e08dd5f4
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/strpbrk.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1985, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/cdefs.h>
+
+#include <string.h>
+
+#include "port_after.h"
+
+#ifndef NEED_STRPBRK
+int __strpbrk_unneeded__;
+#else
+
+/*%
+ * Find the first occurrence in s1 of a character in s2 (excluding NUL).
+ */
+char *
+strpbrk(const char *s1, const char *s2) {
+ const char *scanp;
+ int c, sc;
+
+ while ((c = *s1++) != 0) {
+ for (scanp = s2; (sc = *scanp++) != 0;)
+ if (sc == c)
+ return ((char *)(s1 - 1));
+ }
+ return (NULL);
+}
+
+#endif /*NEED_STRPBRK*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strsep.c b/usr/src/lib/libresolv2_joy/common/bsd/strsep.c
new file mode 100644
index 0000000000..0470733543
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/strsep.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1990, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#include "port_before.h"
+#include <sys/cdefs.h>
+#include <string.h>
+#include <stdio.h>
+#include "port_after.h"
+
+#ifndef NEED_STRSEP
+int __strsep_unneeded__;
+#else
+
+/*%
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+char *
+strsep(char **stringp, const char *delim) {
+ char *s;
+ const char *spanp;
+ int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+#endif /*NEED_STRSEP*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/strtoul.c b/usr/src/lib/libresolv2_joy/common/bsd/strtoul.c
new file mode 100644
index 0000000000..bc199708b7
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/strtoul.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1990, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "port_after.h"
+
+#ifndef NEED_STRTOUL
+int __strtoul_unneeded__;
+#else
+
+/*%
+ * Convert a string to an unsigned long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+u_long
+strtoul(const char *nptr, char **endptr, int base) {
+ const char *s = nptr;
+ u_long acc, cutoff;
+ int neg, c, any, cutlim;
+
+ neg = 0;
+
+ /*
+ * See strtol for comments as to the logic used.
+ */
+ do {
+ c = *(const unsigned char *)s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+ cutoff = (u_long)ULONG_MAX / (u_long)base;
+ cutlim = (u_long)ULONG_MAX % (u_long)base;
+ for (acc = 0, any = 0;; c = *(const unsigned char*)s++) {
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = ULONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ DE_CONST((any ? s - 1 : nptr), *endptr);
+ return (acc);
+}
+
+#endif /*NEED_STRTOUL*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/utimes.c b/usr/src/lib/libresolv2_joy/common/bsd/utimes.c
new file mode 100644
index 0000000000..2f65cffe25
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/utimes.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1997,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <utime.h>
+
+#include "port_after.h"
+
+#ifndef NEED_UTIMES
+int __bind_utimes_unneeded;
+#else
+
+int
+__utimes(char *filename, struct timeval *tvp) {
+ struct utimbuf utb;
+
+ utb.actime = (time_t)tvp[0].tv_sec;
+ utb.modtime = (time_t)tvp[1].tv_sec;
+ return (utime(filename, &utb));
+}
+
+#endif /* NEED_UTIMES */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/bsd/writev.c b/usr/src/lib/libresolv2_joy/common/bsd/writev.c
new file mode 100644
index 0000000000..086f93266b
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/bsd/writev.c
@@ -0,0 +1,85 @@
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include "port_after.h"
+
+#ifndef NEED_WRITEV
+int __bindcompat_writev;
+#else
+
+#ifdef _CRAY
+#define OWN_WRITEV
+int
+__writev(int fd, struct iovec *iov, int iovlen)
+{
+ struct stat statbuf;
+
+ if (fstat(fd, &statbuf) < 0)
+ return (-1);
+
+ /*
+ * Allow for atomic writes to network.
+ */
+ if (statbuf.st_mode & S_IFSOCK) {
+ struct msghdr mesg;
+
+ memset(&mesg, 0, sizeof(mesg));
+ mesg.msg_name = 0;
+ mesg.msg_namelen = 0;
+ mesg.msg_iov = iov;
+ mesg.msg_iovlen = iovlen;
+ mesg.msg_accrights = 0;
+ mesg.msg_accrightslen = 0;
+ return (sendmsg(fd, &mesg, 0));
+ } else {
+ struct iovec *tv;
+ int i, rcode = 0, count = 0;
+
+ for (i = 0, tv = iov; i <= iovlen; tv++) {
+ rcode = write(fd, tv->iov_base, tv->iov_len);
+
+ if (rcode < 0)
+ break;
+
+ count += rcode;
+ }
+
+ if (count == 0)
+ return (rcode);
+ else
+ return (count);
+ }
+}
+
+#else /*_CRAY*/
+
+int
+__writev(fd, vp, vpcount)
+ int fd;
+ const struct iovec *vp;
+ int vpcount;
+{
+ int count = 0;
+
+ while (vpcount-- > 0) {
+ int written = write(fd, vp->iov_base, vp->iov_len);
+
+ if (written < 0)
+ return (-1);
+ count += written;
+ if (written != vp->iov_len)
+ break;
+ vp++;
+ }
+ return (count);
+}
+
+#endif /*_CRAY*/
+
+#endif /*NEED_WRITEV*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/dst/dst_api.c b/usr/src/lib/libresolv2_joy/common/dst/dst_api.c
new file mode 100644
index 0000000000..7215772ce2
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/dst/dst_api.c
@@ -0,0 +1,1044 @@
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ *
+ * Permission to use, copy modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THE SOFTWARE.
+ */
+/*
+ * This file contains the interface between the DST API and the crypto API.
+ * This is the only file that needs to be changed if the crypto system is
+ * changed. Exported functions are:
+ * void dst_init() Initialize the toolkit
+ * int dst_check_algorithm() Function to determines if alg is suppored.
+ * int dst_compare_keys() Function to compare two keys for equality.
+ * int dst_sign_data() Incremental signing routine.
+ * int dst_verify_data() Incremental verify routine.
+ * int dst_generate_key() Function to generate new KEY
+ * DST_KEY *dst_read_key() Function to retrieve private/public KEY.
+ * void dst_write_key() Function to write out a key.
+ * DST_KEY *dst_dnskey_to_key() Function to convert DNS KEY RR to a DST
+ * KEY structure.
+ * int dst_key_to_dnskey() Function to return a public key in DNS
+ * format binary
+ * DST_KEY *dst_buffer_to_key() Converst a data in buffer to KEY
+ * int *dst_key_to_buffer() Writes out DST_KEY key matterial in buffer
+ * void dst_free_key() Releases all memory referenced by key structure
+ */
+
+#include "port_before.h"
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <memory.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include "dst_internal.h"
+#include "port_after.h"
+
+/* static variables */
+static int done_init = 0;
+dst_func *dst_t_func[DST_MAX_ALGS];
+const char *key_file_fmt_str = "Private-key-format: v%s\nAlgorithm: %d (%s)\n";
+const char *dst_path = "";
+
+/* internal I/O functions */
+static DST_KEY *dst_s_read_public_key(const char *in_name,
+ const u_int16_t in_id, int in_alg);
+static int dst_s_read_private_key_file(char *name, DST_KEY *pk_key,
+ u_int16_t in_id, int in_alg);
+static int dst_s_write_public_key(const DST_KEY *key);
+static int dst_s_write_private_key(const DST_KEY *key);
+
+/* internal function to set up data structure */
+static DST_KEY *dst_s_get_key_struct(const char *name, const int alg,
+ const int flags, const int protocol,
+ const int bits);
+
+/*%
+ * dst_init
+ * This function initializes the Digital Signature Toolkit.
+ * Right now, it just checks the DSTKEYPATH environment variable.
+ * Parameters
+ * none
+ * Returns
+ * none
+ */
+void
+dst_init()
+{
+ char *s;
+ int len;
+
+ if (done_init != 0)
+ return;
+ done_init = 1;
+
+ s = getenv("DSTKEYPATH");
+ len = 0;
+ if (s) {
+ struct stat statbuf;
+
+ len = strlen(s);
+ if (len > PATH_MAX) {
+ EREPORT(("%s is longer than %d characters, ignoring\n",
+ s, PATH_MAX));
+ } else if (stat(s, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) {
+ EREPORT(("%s is not a valid directory\n", s));
+ } else {
+ char *tmp;
+ tmp = (char *) malloc(len + 2);
+ memcpy(tmp, s, len + 1);
+ if (tmp[strlen(tmp) - 1] != '/') {
+ tmp[strlen(tmp) + 1] = 0;
+ tmp[strlen(tmp)] = '/';
+ }
+ dst_path = tmp;
+ }
+ }
+ memset(dst_t_func, 0, sizeof(dst_t_func));
+ /* first one is selected */
+ dst_hmac_md5_init();
+}
+
+/*%
+ * dst_check_algorithm
+ * This function determines if the crypto system for the specified
+ * algorithm is present.
+ * Parameters
+ * alg 1 KEY_RSA
+ * 3 KEY_DSA
+ * 157 KEY_HMAC_MD5
+ * future algorithms TBD and registered with IANA.
+ * Returns
+ * 1 - The algorithm is available.
+ * 0 - The algorithm is not available.
+ */
+int
+dst_check_algorithm(const int alg)
+{
+ return (dst_t_func[alg] != NULL);
+}
+
+/*%
+ * dst_s_get_key_struct
+ * This function allocates key structure and fills in some of the
+ * fields of the structure.
+ * Parameters:
+ * name: the name of the key
+ * alg: the algorithm number
+ * flags: the dns flags of the key
+ * protocol: the dns protocol of the key
+ * bits: the size of the key
+ * Returns:
+ * NULL if error
+ * valid pointer otherwise
+ */
+static DST_KEY *
+dst_s_get_key_struct(const char *name, const int alg, const int flags,
+ const int protocol, const int bits)
+{
+ DST_KEY *new_key = NULL;
+
+ if (dst_check_algorithm(alg)) /*%< make sure alg is available */
+ new_key = (DST_KEY *) malloc(sizeof(*new_key));
+ if (new_key == NULL)
+ return (NULL);
+
+ memset(new_key, 0, sizeof(*new_key));
+ new_key->dk_key_name = strdup(name);
+ if (new_key->dk_key_name == NULL) {
+ free(new_key);
+ return (NULL);
+ }
+ new_key->dk_alg = alg;
+ new_key->dk_flags = flags;
+ new_key->dk_proto = protocol;
+ new_key->dk_KEY_struct = NULL;
+ new_key->dk_key_size = bits;
+ new_key->dk_func = dst_t_func[alg];
+ return (new_key);
+}
+
+/*%
+ * dst_compare_keys
+ * Compares two keys for equality.
+ * Parameters
+ * key1, key2 Two keys to be compared.
+ * Returns
+ * 0 The keys are equal.
+ * non-zero The keys are not equal.
+ */
+
+int
+dst_compare_keys(const DST_KEY *key1, const DST_KEY *key2)
+{
+ if (key1 == key2)
+ return (0);
+ if (key1 == NULL || key2 == NULL)
+ return (4);
+ if (key1->dk_alg != key2->dk_alg)
+ return (1);
+ if (key1->dk_key_size != key2->dk_key_size)
+ return (2);
+ if (key1->dk_id != key2->dk_id)
+ return (3);
+ return (key1->dk_func->compare(key1, key2));
+}
+
+/*%
+ * dst_sign_data
+ * An incremental signing function. Data is signed in steps.
+ * First the context must be initialized (SIG_MODE_INIT).
+ * Then data is hashed (SIG_MODE_UPDATE). Finally the signature
+ * itself is created (SIG_MODE_FINAL). This function can be called
+ * once with INIT, UPDATE and FINAL modes all set, or it can be
+ * called separately with a different mode set for each step. The
+ * UPDATE step can be repeated.
+ * Parameters
+ * mode A bit mask used to specify operation(s) to be performed.
+ * SIG_MODE_INIT 1 Initialize digest
+ * SIG_MODE_UPDATE 2 Add data to digest
+ * SIG_MODE_FINAL 4 Generate signature
+ * from signature
+ * SIG_MODE_ALL (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL
+ * data Data to be signed.
+ * len The length in bytes of data to be signed.
+ * in_key Contains a private key to sign with.
+ * KEY structures should be handled (created, converted,
+ * compared, stored, freed) by the DST.
+ * signature
+ * The location to which the signature will be written.
+ * sig_len Length of the signature field in bytes.
+ * Return
+ * 0 Successfull INIT or Update operation
+ * &gt;0 success FINAL (sign) operation
+ * &lt;0 failure
+ */
+
+int
+dst_sign_data(const int mode, DST_KEY *in_key, void **context,
+ const u_char *data, const int len,
+ u_char *signature, const int sig_len)
+{
+ DUMP(data, mode, len, "dst_sign_data()");
+
+ if (mode & SIG_MODE_FINAL &&
+ (in_key->dk_KEY_struct == NULL || signature == NULL))
+ return (MISSING_KEY_OR_SIGNATURE);
+
+ if (in_key->dk_func && in_key->dk_func->sign)
+ return (in_key->dk_func->sign(mode, in_key, context, data, len,
+ signature, sig_len));
+ return (UNKNOWN_KEYALG);
+}
+
+/*%
+ * dst_verify_data
+ * An incremental verify function. Data is verified in steps.
+ * First the context must be initialized (SIG_MODE_INIT).
+ * Then data is hashed (SIG_MODE_UPDATE). Finally the signature
+ * is verified (SIG_MODE_FINAL). This function can be called
+ * once with INIT, UPDATE and FINAL modes all set, or it can be
+ * called separately with a different mode set for each step. The
+ * UPDATE step can be repeated.
+ * Parameters
+ * mode Operations to perform this time.
+ * SIG_MODE_INIT 1 Initialize digest
+ * SIG_MODE_UPDATE 2 add data to digest
+ * SIG_MODE_FINAL 4 verify signature
+ * SIG_MODE_ALL
+ * (SIG_MODE_INIT,SIG_MODE_UPDATE,SIG_MODE_FINAL)
+ * data Data to pass through the hash function.
+ * len Length of the data in bytes.
+ * in_key Key for verification.
+ * signature Location of signature.
+ * sig_len Length of the signature in bytes.
+ * Returns
+ * 0 Verify success
+ * Non-Zero Verify Failure
+ */
+
+int
+dst_verify_data(const int mode, DST_KEY *in_key, void **context,
+ const u_char *data, const int len,
+ const u_char *signature, const int sig_len)
+{
+ DUMP(data, mode, len, "dst_verify_data()");
+ if (mode & SIG_MODE_FINAL &&
+ (in_key->dk_KEY_struct == NULL || signature == NULL))
+ return (MISSING_KEY_OR_SIGNATURE);
+
+ if (in_key->dk_func == NULL || in_key->dk_func->verify == NULL)
+ return (UNSUPPORTED_KEYALG);
+ return (in_key->dk_func->verify(mode, in_key, context, data, len,
+ signature, sig_len));
+}
+
+/*%
+ * dst_read_private_key
+ * Access a private key. First the list of private keys that have
+ * already been read in is searched, then the key accessed on disk.
+ * If the private key can be found, it is returned. If the key cannot
+ * be found, a null pointer is returned. The options specify required
+ * key characteristics. If the private key requested does not have
+ * these characteristics, it will not be read.
+ * Parameters
+ * in_keyname The private key name.
+ * in_id The id of the private key.
+ * options DST_FORCE_READ Read from disk - don't use a previously
+ * read key.
+ * DST_CAN_SIGN The key must be useable for signing.
+ * DST_NO_AUTHEN The key must be useable for authentication.
+ * DST_STANDARD Return any key
+ * Returns
+ * NULL If there is no key found in the current directory or
+ * this key has not been loaded before.
+ * !NULL Success - KEY structure returned.
+ */
+
+DST_KEY *
+dst_read_key(const char *in_keyname, const u_int16_t in_id,
+ const int in_alg, const int type)
+{
+ char keyname[PATH_MAX];
+ DST_KEY *dg_key = NULL, *pubkey = NULL;
+
+ if (!dst_check_algorithm(in_alg)) { /*%< make sure alg is available */
+ EREPORT(("dst_read_private_key(): Algorithm %d not suppored\n",
+ in_alg));
+ return (NULL);
+ }
+ if ((type & (DST_PUBLIC | DST_PRIVATE)) == 0)
+ return (NULL);
+ if (in_keyname == NULL) {
+ EREPORT(("dst_read_private_key(): Null key name passed in\n"));
+ return (NULL);
+ } else if (strlen(in_keyname) >= sizeof(keyname)) {
+ EREPORT(("dst_read_private_key(): keyname too big\n"));
+ return (NULL);
+ } else
+ strcpy(keyname, in_keyname);
+
+ /* before I read in the public key, check if it is allowed to sign */
+ if ((pubkey = dst_s_read_public_key(keyname, in_id, in_alg)) == NULL)
+ return (NULL);
+
+ if (type == DST_PUBLIC)
+ return pubkey;
+
+ if (!(dg_key = dst_s_get_key_struct(keyname, pubkey->dk_alg,
+ pubkey->dk_flags, pubkey->dk_proto,
+ 0)))
+ return (dg_key);
+ /* Fill in private key and some fields in the general key structure */
+ if (dst_s_read_private_key_file(keyname, dg_key, pubkey->dk_id,
+ pubkey->dk_alg) == 0)
+ dg_key = dst_free_key(dg_key);
+
+ (void)dst_free_key(pubkey);
+ return (dg_key);
+}
+
+int
+dst_write_key(const DST_KEY *key, const int type)
+{
+ int pub = 0, priv = 0;
+
+ if (key == NULL)
+ return (0);
+ if (!dst_check_algorithm(key->dk_alg)) { /*%< make sure alg is available */
+ EREPORT(("dst_write_key(): Algorithm %d not suppored\n",
+ key->dk_alg));
+ return (UNSUPPORTED_KEYALG);
+ }
+ if ((type & (DST_PRIVATE|DST_PUBLIC)) == 0)
+ return (0);
+
+ if (type & DST_PUBLIC)
+ if ((pub = dst_s_write_public_key(key)) < 0)
+ return (pub);
+ if (type & DST_PRIVATE)
+ if ((priv = dst_s_write_private_key(key)) < 0)
+ return (priv);
+ return (priv+pub);
+}
+
+/*%
+ * dst_write_private_key
+ * Write a private key to disk. The filename will be of the form:
+ * K&lt;key-&gt;dk_name&gt;+&lt;key-&gt;dk_alg+&gt;&lt;key-d&gt;k_id.&gt;&lt;private key suffix&gt;.
+ * If there is already a file with this name, an error is returned.
+ *
+ * Parameters
+ * key A DST managed key structure that contains
+ * all information needed about a key.
+ * Return
+ * &gt;= 0 Correct behavior. Returns length of encoded key value
+ * written to disk.
+ * &lt; 0 error.
+ */
+
+static int
+dst_s_write_private_key(const DST_KEY *key)
+{
+ u_char encoded_block[RAW_KEY_SIZE];
+ char file[PATH_MAX];
+ int len;
+ FILE *fp;
+
+ /* First encode the key into the portable key format */
+ if (key == NULL)
+ return (-1);
+ if (key->dk_KEY_struct == NULL)
+ return (0); /*%< null key has no private key */
+ if (key->dk_func == NULL || key->dk_func->to_file_fmt == NULL) {
+ EREPORT(("dst_write_private_key(): Unsupported operation %d\n",
+ key->dk_alg));
+ return (-5);
+ } else if ((len = key->dk_func->to_file_fmt(key, (char *)encoded_block,
+ sizeof(encoded_block))) <= 0) {
+ EREPORT(("dst_write_private_key(): Failed encoding private RSA bsafe key %d\n", len));
+ return (-8);
+ }
+ /* Now I can create the file I want to use */
+ dst_s_build_filename(file, key->dk_key_name, key->dk_id, key->dk_alg,
+ PRIVATE_KEY, PATH_MAX);
+
+ /* Do not overwrite an existing file */
+ if ((fp = dst_s_fopen(file, "w", 0600)) != NULL) {
+ int nn;
+ if ((nn = fwrite(encoded_block, 1, len, fp)) != len) {
+ EREPORT(("dst_write_private_key(): Write failure on %s %d != %d errno=%d\n",
+ file, len, nn, errno));
+ fclose(fp);
+ return (-5);
+ }
+ fclose(fp);
+ } else {
+ EREPORT(("dst_write_private_key(): Can not create file %s\n"
+ ,file));
+ return (-6);
+ }
+ memset(encoded_block, 0, len);
+ return (len);
+}
+
+/*%
+*
+ * dst_read_public_key
+ * Read a public key from disk and store in a DST key structure.
+ * Parameters
+ * in_name K&lt;in_name&gt;&lt;in_id&gt;.&lt;public key suffix&gt; is the
+ * filename of the key file to be read.
+ * Returns
+ * NULL If the key does not exist or no name is supplied.
+ * NON-NULL Initialized key structure if the key exists.
+ */
+
+static DST_KEY *
+dst_s_read_public_key(const char *in_name, const u_int16_t in_id, int in_alg)
+{
+ int flags, proto, alg, len, dlen;
+ int c;
+ char name[PATH_MAX], enckey[RAW_KEY_SIZE], *notspace;
+ u_char deckey[RAW_KEY_SIZE];
+ FILE *fp;
+
+ if (in_name == NULL) {
+ EREPORT(("dst_read_public_key(): No key name given\n"));
+ return (NULL);
+ }
+ if (dst_s_build_filename(name, in_name, in_id, in_alg, PUBLIC_KEY,
+ PATH_MAX) == -1) {
+ EREPORT(("dst_read_public_key(): Cannot make filename from %s, %d, and %s\n",
+ in_name, in_id, PUBLIC_KEY));
+ return (NULL);
+ }
+ /*
+ * Open the file and read it's formatted contents up to key
+ * File format:
+ * domain.name [ttl] [IN] KEY &lt;flags&gt; &lt;protocol&gt; &lt;algorithm&gt; &lt;key&gt;
+ * flags, proto, alg stored as decimal (or hex numbers FIXME).
+ * (FIXME: handle parentheses for line continuation.)
+ */
+ if ((fp = dst_s_fopen(name, "r", 0)) == NULL) {
+ EREPORT(("dst_read_public_key(): Public Key not found %s\n",
+ name));
+ return (NULL);
+ }
+ /* Skip domain name, which ends at first blank */
+ while ((c = getc(fp)) != EOF)
+ if (isspace(c))
+ break;
+ /* Skip blank to get to next field */
+ while ((c = getc(fp)) != EOF)
+ if (!isspace(c))
+ break;
+
+ /* Skip optional TTL -- if initial digit, skip whole word. */
+ if (isdigit(c)) {
+ while ((c = getc(fp)) != EOF)
+ if (isspace(c))
+ break;
+ while ((c = getc(fp)) != EOF)
+ if (!isspace(c))
+ break;
+ }
+ /* Skip optional "IN" */
+ if (c == 'I' || c == 'i') {
+ while ((c = getc(fp)) != EOF)
+ if (isspace(c))
+ break;
+ while ((c = getc(fp)) != EOF)
+ if (!isspace(c))
+ break;
+ }
+ /* Locate and skip "KEY" */
+ if (c != 'K' && c != 'k') {
+ EREPORT(("\"KEY\" doesn't appear in file: %s", name));
+ return NULL;
+ }
+ while ((c = getc(fp)) != EOF)
+ if (isspace(c))
+ break;
+ while ((c = getc(fp)) != EOF)
+ if (!isspace(c))
+ break;
+ ungetc(c, fp); /*%< return the charcter to the input field */
+ /* Handle hex!! FIXME. */
+
+ if (fscanf(fp, "%d %d %d", &flags, &proto, &alg) != 3) {
+ EREPORT(("dst_read_public_key(): Can not read flag/proto/alg field from %s\n"
+ ,name));
+ return (NULL);
+ }
+ /* read in the key string */
+ fgets(enckey, sizeof(enckey), fp);
+
+ /* If we aren't at end-of-file, something is wrong. */
+ while ((c = getc(fp)) != EOF)
+ if (!isspace(c))
+ break;
+ if (!feof(fp)) {
+ EREPORT(("Key too long in file: %s", name));
+ return NULL;
+ }
+ fclose(fp);
+
+ if ((len = strlen(enckey)) <= 0)
+ return (NULL);
+
+ /* discard \n */
+ enckey[--len] = '\0';
+
+ /* remove leading spaces */
+ for (notspace = (char *) enckey; isspace((*notspace)&0xff); len--)
+ notspace++;
+
+ dlen = b64_pton(notspace, deckey, sizeof(deckey));
+ if (dlen < 0) {
+ EREPORT(("dst_read_public_key: bad return from b64_pton = %d",
+ dlen));
+ return (NULL);
+ }
+ /* store key and info in a key structure that is returned */
+/* return dst_store_public_key(in_name, alg, proto, 666, flags, deckey,
+ dlen);*/
+ return dst_buffer_to_key(in_name, alg, flags, proto, deckey, dlen);
+}
+
+/*%
+ * dst_write_public_key
+ * Write a key to disk in DNS format.
+ * Parameters
+ * key Pointer to a DST key structure.
+ * Returns
+ * 0 Failure
+ * 1 Success
+ */
+
+static int
+dst_s_write_public_key(const DST_KEY *key)
+{
+ FILE *fp;
+ char filename[PATH_MAX];
+ u_char out_key[RAW_KEY_SIZE];
+ char enc_key[RAW_KEY_SIZE];
+ int len = 0;
+ int mode;
+
+ memset(out_key, 0, sizeof(out_key));
+ if (key == NULL) {
+ EREPORT(("dst_write_public_key(): No key specified \n"));
+ return (0);
+ } else if ((len = dst_key_to_dnskey(key, out_key, sizeof(out_key)))< 0)
+ return (0);
+
+ /* Make the filename */
+ if (dst_s_build_filename(filename, key->dk_key_name, key->dk_id,
+ key->dk_alg, PUBLIC_KEY, PATH_MAX) == -1) {
+ EREPORT(("dst_write_public_key(): Cannot make filename from %s, %d, and %s\n",
+ key->dk_key_name, key->dk_id, PUBLIC_KEY));
+ return (0);
+ }
+ /* XXX in general this should be a check for symmetric keys */
+ mode = (key->dk_alg == KEY_HMAC_MD5) ? 0600 : 0644;
+ /* create public key file */
+ if ((fp = dst_s_fopen(filename, "w+", mode)) == NULL) {
+ EREPORT(("DST_write_public_key: open of file:%s failed (errno=%d)\n",
+ filename, errno));
+ return (0);
+ }
+ /*write out key first base64 the key data */
+ if (key->dk_flags & DST_EXTEND_FLAG)
+ b64_ntop(&out_key[6], len - 6, enc_key, sizeof(enc_key));
+ else
+ b64_ntop(&out_key[4], len - 4, enc_key, sizeof(enc_key));
+ fprintf(fp, "%s IN KEY %d %d %d %s\n",
+ key->dk_key_name,
+ key->dk_flags, key->dk_proto, key->dk_alg, enc_key);
+ fclose(fp);
+ return (1);
+}
+
+/*%
+ * dst_dnskey_to_public_key
+ * This function converts the contents of a DNS KEY RR into a DST
+ * key structure.
+ * Paramters
+ * len Length of the RDATA of the KEY RR RDATA
+ * rdata A pointer to the the KEY RR RDATA.
+ * in_name Key name to be stored in key structure.
+ * Returns
+ * NULL Failure
+ * NON-NULL Success. Pointer to key structure.
+ * Caller's responsibility to free() it.
+ */
+
+DST_KEY *
+dst_dnskey_to_key(const char *in_name, const u_char *rdata, const int len)
+{
+ DST_KEY *key_st;
+ int alg ;
+ int start = DST_KEY_START;
+
+ if (rdata == NULL || len <= DST_KEY_ALG) /*%< no data */
+ return (NULL);
+ alg = (u_int8_t) rdata[DST_KEY_ALG];
+ if (!dst_check_algorithm(alg)) { /*%< make sure alg is available */
+ EREPORT(("dst_dnskey_to_key(): Algorithm %d not suppored\n",
+ alg));
+ return (NULL);
+ }
+
+ if (in_name == NULL)
+ return (NULL);
+
+ if ((key_st = dst_s_get_key_struct(in_name, alg, 0, 0, 0)) == NULL)
+ return (NULL);
+
+ key_st->dk_id = dst_s_dns_key_id(rdata, len);
+ key_st->dk_flags = dst_s_get_int16(rdata);
+ key_st->dk_proto = (u_int16_t) rdata[DST_KEY_PROT];
+ if (key_st->dk_flags & DST_EXTEND_FLAG) {
+ u_int32_t ext_flags;
+ ext_flags = (u_int32_t) dst_s_get_int16(&rdata[DST_EXT_FLAG]);
+ key_st->dk_flags = key_st->dk_flags | (ext_flags << 16);
+ start += 2;
+ }
+ /*
+ * now point to the begining of the data representing the encoding
+ * of the key
+ */
+ if (key_st->dk_func && key_st->dk_func->from_dns_key) {
+ if (key_st->dk_func->from_dns_key(key_st, &rdata[start],
+ len - start) > 0)
+ return (key_st);
+ } else
+ EREPORT(("dst_dnskey_to_public_key(): unsuppored alg %d\n",
+ alg));
+
+ SAFE_FREE(key_st);
+ return (key_st);
+}
+
+/*%
+ * dst_public_key_to_dnskey
+ * Function to encode a public key into DNS KEY wire format
+ * Parameters
+ * key Key structure to encode.
+ * out_storage Location to write the encoded key to.
+ * out_len Size of the output array.
+ * Returns
+ * <0 Failure
+ * >=0 Number of bytes written to out_storage
+ */
+
+int
+dst_key_to_dnskey(const DST_KEY *key, u_char *out_storage,
+ const int out_len)
+{
+ u_int16_t val;
+ int loc = 0;
+ int enc_len = 0;
+ if (key == NULL)
+ return (-1);
+
+ if (!dst_check_algorithm(key->dk_alg)) { /*%< make sure alg is available */
+ EREPORT(("dst_key_to_dnskey(): Algorithm %d not suppored\n",
+ key->dk_alg));
+ return (UNSUPPORTED_KEYALG);
+ }
+ memset(out_storage, 0, out_len);
+ val = (u_int16_t)(key->dk_flags & 0xffff);
+ dst_s_put_int16(out_storage, val);
+ loc += 2;
+
+ out_storage[loc++] = (u_char) key->dk_proto;
+ out_storage[loc++] = (u_char) key->dk_alg;
+
+ if (key->dk_flags > 0xffff) { /*%< Extended flags */
+ val = (u_int16_t)((key->dk_flags >> 16) & 0xffff);
+ dst_s_put_int16(&out_storage[loc], val);
+ loc += 2;
+ }
+ if (key->dk_KEY_struct == NULL)
+ return (loc);
+ if (key->dk_func && key->dk_func->to_dns_key) {
+ enc_len = key->dk_func->to_dns_key(key,
+ (u_char *) &out_storage[loc],
+ out_len - loc);
+ if (enc_len > 0)
+ return (enc_len + loc);
+ else
+ return (-1);
+ } else
+ EREPORT(("dst_key_to_dnskey(): Unsupported ALG %d\n",
+ key->dk_alg));
+ return (-1);
+}
+
+/*%
+ * dst_buffer_to_key
+ * Function to encode a string of raw data into a DST key
+ * Parameters
+ * alg The algorithm (HMAC only)
+ * key A pointer to the data
+ * keylen The length of the data
+ * Returns
+ * NULL an error occurred
+ * NON-NULL the DST key
+ */
+DST_KEY *
+dst_buffer_to_key(const char *key_name, /*!< name of the key */
+ const int alg, /*!< algorithm */
+ const int flags, /*!< dns flags */
+ const int protocol, /*!< dns protocol */
+ const u_char *key_buf, /*!< key in dns wire fmt */
+ const int key_len) /*!< size of key */
+{
+
+ DST_KEY *dkey = NULL;
+ int dnslen;
+ u_char dns[2048];
+
+ if (!dst_check_algorithm(alg)) { /*%< make sure alg is available */
+ EREPORT(("dst_buffer_to_key(): Algorithm %d not suppored\n", alg));
+ return (NULL);
+ }
+
+ dkey = dst_s_get_key_struct(key_name, alg, flags, protocol, -1);
+
+ if (dkey == NULL || dkey->dk_func == NULL ||
+ dkey->dk_func->from_dns_key == NULL)
+ return (dst_free_key(dkey));
+
+ if (dkey->dk_func->from_dns_key(dkey, key_buf, key_len) < 0) {
+ EREPORT(("dst_buffer_to_key(): dst_buffer_to_hmac failed\n"));
+ return (dst_free_key(dkey));
+ }
+
+ dnslen = dst_key_to_dnskey(dkey, dns, sizeof(dns));
+ dkey->dk_id = dst_s_dns_key_id(dns, dnslen);
+ return (dkey);
+}
+
+int
+dst_key_to_buffer(DST_KEY *key, u_char *out_buff, int buf_len)
+{
+ int len;
+ /* this function will extrac the secret of HMAC into a buffer */
+ if (key == NULL)
+ return (0);
+ if (key->dk_func != NULL && key->dk_func->to_dns_key != NULL) {
+ len = key->dk_func->to_dns_key(key, out_buff, buf_len);
+ if (len < 0)
+ return (0);
+ return (len);
+ }
+ return (0);
+}
+
+/*%
+ * dst_s_read_private_key_file
+ * Function reads in private key from a file.
+ * Fills out the KEY structure.
+ * Parameters
+ * name Name of the key to be read.
+ * pk_key Structure that the key is returned in.
+ * in_id Key identifier (tag)
+ * Return
+ * 1 if everthing works
+ * 0 if there is any problem
+ */
+
+static int
+dst_s_read_private_key_file(char *name, DST_KEY *pk_key, u_int16_t in_id,
+ int in_alg)
+{
+ int cnt, alg, len, major, minor, file_major, file_minor;
+ int ret, id;
+ char filename[PATH_MAX];
+ u_char in_buff[RAW_KEY_SIZE], *p;
+ FILE *fp;
+ int dnslen;
+ u_char dns[2048];
+
+ if (name == NULL || pk_key == NULL) {
+ EREPORT(("dst_read_private_key_file(): No key name given\n"));
+ return (0);
+ }
+ /* Make the filename */
+ if (dst_s_build_filename(filename, name, in_id, in_alg, PRIVATE_KEY,
+ PATH_MAX) == -1) {
+ EREPORT(("dst_read_private_key(): Cannot make filename from %s, %d, and %s\n",
+ name, in_id, PRIVATE_KEY));
+ return (0);
+ }
+ /* first check if we can find the key file */
+ if ((fp = dst_s_fopen(filename, "r", 0)) == NULL) {
+ EREPORT(("dst_s_read_private_key_file: Could not open file %s in directory %s\n",
+ filename, dst_path[0] ? dst_path :
+ (char *) getcwd(NULL, PATH_MAX - 1)));
+ return (0);
+ }
+ /* now read the header info from the file */
+ if ((cnt = fread(in_buff, 1, sizeof(in_buff), fp)) < 5) {
+ fclose(fp);
+ EREPORT(("dst_s_read_private_key_file: error reading file %s (empty file)\n",
+ filename));
+ return (0);
+ }
+ /* decrypt key */
+ fclose(fp);
+ if (memcmp(in_buff, "Private-key-format: v", 20) != 0)
+ goto fail;
+ len = cnt;
+ p = in_buff;
+
+ if (!dst_s_verify_str((const char **) (void *)&p,
+ "Private-key-format: v")) {
+ EREPORT(("dst_s_read_private_key_file(): Not a Key file/Decrypt failed %s\n", name));
+ goto fail;
+ }
+ /* read in file format */
+ sscanf((char *)p, "%d.%d", &file_major, &file_minor);
+ sscanf(KEY_FILE_FORMAT, "%d.%d", &major, &minor);
+ if (file_major < 1) {
+ EREPORT(("dst_s_read_private_key_file(): Unknown keyfile %d.%d version for %s\n",
+ file_major, file_minor, name));
+ goto fail;
+ } else if (file_major > major || file_minor > minor)
+ EREPORT((
+ "dst_s_read_private_key_file(): Keyfile %s version higher than mine %d.%d MAY FAIL\n",
+ name, file_major, file_minor));
+
+ while (*p++ != '\n') ; /*%< skip to end of line */
+
+ if (!dst_s_verify_str((const char **) (void *)&p, "Algorithm: "))
+ goto fail;
+
+ if (sscanf((char *)p, "%d", &alg) != 1)
+ goto fail;
+ while (*p++ != '\n') ; /*%< skip to end of line */
+
+ if (pk_key->dk_key_name && !strcmp(pk_key->dk_key_name, name))
+ SAFE_FREE2(pk_key->dk_key_name, strlen(pk_key->dk_key_name));
+ pk_key->dk_key_name = (char *) strdup(name);
+
+ /* allocate and fill in key structure */
+ if (pk_key->dk_func == NULL || pk_key->dk_func->from_file_fmt == NULL)
+ goto fail;
+
+ ret = pk_key->dk_func->from_file_fmt(pk_key, (char *)p, &in_buff[len] - p);
+ if (ret < 0)
+ goto fail;
+
+ dnslen = dst_key_to_dnskey(pk_key, dns, sizeof(dns));
+ id = dst_s_dns_key_id(dns, dnslen);
+
+ /* Make sure the actual key tag matches the input tag used in the filename
+ */
+ if (id != in_id) {
+ EREPORT(("dst_s_read_private_key_file(): actual tag of key read %d != input tag used to build filename %d.\n", id, in_id));
+ goto fail;
+ }
+ pk_key->dk_id = (u_int16_t) id;
+ pk_key->dk_alg = alg;
+ memset(in_buff, 0, cnt);
+ return (1);
+
+ fail:
+ memset(in_buff, 0, cnt);
+ return (0);
+}
+
+/*%
+ * Generate and store a public/private keypair.
+ * Keys will be stored in formatted files.
+ *
+ * Parameters
+ &
+ *\par name Name of the new key. Used to create key files
+ *\li K&lt;name&gt;+&lt;alg&gt;+&lt;id&gt;.public and K&lt;name&gt;+&lt;alg&gt;+&lt;id&gt;.private.
+ *\par bits Size of the new key in bits.
+ *\par exp What exponent to use:
+ *\li 0 use exponent 3
+ *\li non-zero use Fermant4
+ *\par flags The default value of the DNS Key flags.
+ *\li The DNS Key RR Flag field is defined in RFC2065,
+ * section 3.3. The field has 16 bits.
+ *\par protocol
+ *\li Default value of the DNS Key protocol field.
+ *\li The DNS Key protocol field is defined in RFC2065,
+ * section 3.4. The field has 8 bits.
+ *\par alg What algorithm to use. Currently defined:
+ *\li KEY_RSA 1
+ *\li KEY_DSA 3
+ *\li KEY_HMAC 157
+ *\par out_id The key tag is returned.
+ *
+ * Return
+ *\li NULL Failure
+ *\li non-NULL the generated key pair
+ * Caller frees the result, and its dk_name pointer.
+ */
+DST_KEY *
+dst_generate_key(const char *name, const int bits, const int exp,
+ const int flags, const int protocol, const int alg)
+{
+ DST_KEY *new_key = NULL;
+ int dnslen;
+ u_char dns[2048];
+
+ if (name == NULL)
+ return (NULL);
+
+ if (!dst_check_algorithm(alg)) { /*%< make sure alg is available */
+ EREPORT(("dst_generate_key(): Algorithm %d not suppored\n", alg));
+ return (NULL);
+ }
+
+ new_key = dst_s_get_key_struct(name, alg, flags, protocol, bits);
+ if (new_key == NULL)
+ return (NULL);
+ if (bits == 0) /*%< null key we are done */
+ return (new_key);
+ if (new_key->dk_func == NULL || new_key->dk_func->generate == NULL) {
+ EREPORT(("dst_generate_key_pair():Unsupported algorithm %d\n",
+ alg));
+ return (dst_free_key(new_key));
+ }
+ if (new_key->dk_func->generate(new_key, exp) <= 0) {
+ EREPORT(("dst_generate_key_pair(): Key generation failure %s %d %d %d\n",
+ new_key->dk_key_name, new_key->dk_alg,
+ new_key->dk_key_size, exp));
+ return (dst_free_key(new_key));
+ }
+
+ dnslen = dst_key_to_dnskey(new_key, dns, sizeof(dns));
+ if (dnslen != UNSUPPORTED_KEYALG)
+ new_key->dk_id = dst_s_dns_key_id(dns, dnslen);
+ else
+ new_key->dk_id = 0;
+
+ return (new_key);
+}
+
+/*%
+ * Release all data structures pointed to by a key structure.
+ *
+ * Parameters
+ *\li f_key Key structure to be freed.
+ */
+
+DST_KEY *
+dst_free_key(DST_KEY *f_key)
+{
+
+ if (f_key == NULL)
+ return (f_key);
+ if (f_key->dk_func && f_key->dk_func->destroy)
+ f_key->dk_KEY_struct =
+ f_key->dk_func->destroy(f_key->dk_KEY_struct);
+ else {
+ EREPORT(("dst_free_key(): Unknown key alg %d\n",
+ f_key->dk_alg));
+ }
+ if (f_key->dk_KEY_struct) {
+ free(f_key->dk_KEY_struct);
+ f_key->dk_KEY_struct = NULL;
+ }
+ if (f_key->dk_key_name)
+ SAFE_FREE(f_key->dk_key_name);
+ SAFE_FREE(f_key);
+ return (NULL);
+}
+
+/*%
+ * Return the maximim size of signature from the key specified in bytes
+ *
+ * Parameters
+ *\li key
+ *
+ * Returns
+ * \li bytes
+ */
+int
+dst_sig_size(DST_KEY *key) {
+ switch (key->dk_alg) {
+ case KEY_HMAC_MD5:
+ return (16);
+ case KEY_HMAC_SHA1:
+ return (20);
+ case KEY_RSA:
+ return (key->dk_key_size + 7) / 8;
+ case KEY_DSA:
+ return (40);
+ default:
+ EREPORT(("dst_sig_size(): Unknown key alg %d\n", key->dk_alg));
+ return -1;
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/dst/dst_internal.h b/usr/src/lib/libresolv2_joy/common/dst/dst_internal.h
new file mode 100644
index 0000000000..e9bc6fc08d
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/dst/dst_internal.h
@@ -0,0 +1,155 @@
+#ifndef DST_INTERNAL_H
+#define DST_INTERNAL_H
+
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ *
+ * Permission to use, copy modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THE SOFTWARE.
+ */
+#include <limits.h>
+#include <sys/param.h>
+#if (!defined(BSD)) || (BSD < 199306)
+# include <sys/bitypes.h>
+#else
+# include <sys/types.h>
+#endif
+
+#ifndef PATH_MAX
+# ifdef POSIX_PATH_MAX
+# define PATH_MAX POSIX_PATH_MAX
+# else
+# define PATH_MAX 255 /*%< this is the value of POSIX_PATH_MAX */
+# endif
+#endif
+
+typedef struct dst_key {
+ char *dk_key_name; /*%< name of the key */
+ int dk_key_size; /*%< this is the size of the key in bits */
+ int dk_proto; /*%< what protocols this key can be used for */
+ int dk_alg; /*%< algorithm number from key record */
+ u_int32_t dk_flags; /*%< and the flags of the public key */
+ u_int16_t dk_id; /*%< identifier of the key */
+ void *dk_KEY_struct; /*%< pointer to key in crypto pkg fmt */
+ struct dst_func *dk_func; /*%< point to cryptto pgk specific function table */
+} DST_KEY;
+#define HAS_DST_KEY
+
+#include <isc/dst.h>
+/*
+ * define what crypto systems are supported for RSA,
+ * BSAFE is prefered over RSAREF; only one can be set at any time
+ */
+#if defined(BSAFE) && defined(RSAREF)
+# error "Cannot have both BSAFE and RSAREF defined"
+#endif
+
+/* Declare dst_lib specific constants */
+#define KEY_FILE_FORMAT "1.2"
+
+/* suffixes for key file names */
+#define PRIVATE_KEY "private"
+#define PUBLIC_KEY "key"
+
+/* error handling */
+#ifdef REPORT_ERRORS
+#define EREPORT(str) printf str
+#else
+#define EREPORT(str) (void)0
+#endif
+
+/* use our own special macro to FRRE memory */
+
+#ifndef SAFE_FREE
+#define SAFE_FREE(a) \
+do{if(a != NULL){memset(a,0, sizeof(*a)); free(a); a=NULL;}} while (0)
+#define SAFE_FREE2(a,s) if (a != NULL && (long)s > 0){memset(a,0, s);free(a); a=NULL;}
+#endif
+
+typedef struct dst_func {
+ int (*sign)(const int mode, DST_KEY *key, void **context,
+ const u_int8_t *data, const int len,
+ u_int8_t *signature, const int sig_len);
+ int (*verify)(const int mode, DST_KEY *key, void **context,
+ const u_int8_t *data, const int len,
+ const u_int8_t *signature, const int sig_len);
+ int (*compare)(const DST_KEY *key1, const DST_KEY *key2);
+ int (*generate)(DST_KEY *key, int parms);
+ void *(*destroy)(void *key);
+ /* conversion functions */
+ int (*to_dns_key)(const DST_KEY *key, u_int8_t *out,
+ const int out_len);
+ int (*from_dns_key)(DST_KEY *key, const u_int8_t *str,
+ const int str_len);
+ int (*to_file_fmt)(const DST_KEY *key, char *out,
+ const int out_len);
+ int (*from_file_fmt)(DST_KEY *key, const char *out,
+ const int out_len);
+
+} dst_func;
+
+extern dst_func *dst_t_func[DST_MAX_ALGS];
+extern const char *key_file_fmt_str;
+extern const char *dst_path;
+
+#ifndef DST_HASH_SIZE
+#define DST_HASH_SIZE 20 /*%< RIPEMD160 and SHA-1 are 20 bytes MD5 is 16 */
+#endif
+
+int dst_bsafe_init(void);
+
+int dst_rsaref_init(void);
+
+int dst_hmac_md5_init(void);
+
+int dst_cylink_init(void);
+
+int dst_eay_dss_init(void);
+
+/* from higher level support routines */
+int dst_s_calculate_bits( const u_int8_t *str, const int max_bits);
+int dst_s_verify_str( const char **buf, const char *str);
+
+
+/* conversion between dns names and key file names */
+size_t dst_s_filename_length( const char *name, const char *suffix);
+int dst_s_build_filename( char *filename, const char *name,
+ u_int16_t id, int alg, const char *suffix,
+ size_t filename_length);
+
+FILE *dst_s_fopen (const char *filename, const char *mode, int perm);
+
+/*%
+ * read and write network byte order into u_int?_t
+ * all of these should be retired
+ */
+u_int16_t dst_s_get_int16( const u_int8_t *buf);
+void dst_s_put_int16( u_int8_t *buf, const u_int16_t val);
+
+u_int32_t dst_s_get_int32( const u_int8_t *buf);
+void dst_s_put_int32( u_int8_t *buf, const u_int32_t val);
+
+#ifdef DUMP
+# undef DUMP
+# define DUMP(a,b,c,d) dst_s_dump(a,b,c,d)
+#else
+# define DUMP(a,b,c,d)
+#endif
+void
+dst_s_dump(const int mode, const u_char *data, const int size,
+ const char *msg);
+
+
+
+#endif /* DST_INTERNAL_H */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/dst/hmac_link.c b/usr/src/lib/libresolv2_joy/common/dst/hmac_link.c
new file mode 100644
index 0000000000..f005784e7b
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/dst/hmac_link.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#ifdef HMAC_MD5
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ *
+ * Permission to use, copy modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THE SOFTWARE.
+ */
+
+/*%
+ * This file contains an implementation of the HMAC-MD5 algorithm.
+ */
+#include "port_before.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include "dst_internal.h"
+
+#ifdef USE_MD5
+# ifndef HAVE_MD5
+# include "md5.h"
+# else
+# ifdef SOLARIS2
+# include <sys/md5.h>
+# endif
+# endif
+# ifndef _MD5_H_
+# define _MD5_H_ 1 /*%< make sure we do not include rsaref md5.h file */
+# endif
+#endif
+
+#include "port_after.h"
+
+
+#define HMAC_LEN 64
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5c
+#define MD5_LEN 16
+
+
+typedef struct hmackey {
+ u_char hk_ipad[64], hk_opad[64];
+} HMAC_Key;
+
+
+/**************************************************************************
+ * dst_hmac_md5_sign
+ * Call HMAC signing functions to sign a block of data.
+ * There are three steps to signing, INIT (initialize structures),
+ * UPDATE (hash (more) data), FINAL (generate a signature). This
+ * routine performs one or more of these steps.
+ * Parameters
+ * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL.
+ * priv_key key to use for signing.
+ * context the context to be used in this digest
+ * data data to be signed.
+ * len length in bytes of data.
+ * signature location to store signature.
+ * sig_len size of the signature location
+ * returns
+ * N Success on SIG_MODE_FINAL = returns signature length in bytes
+ * 0 Success on SIG_MODE_INIT and UPDATE
+ * <0 Failure
+ */
+
+static int
+dst_hmac_md5_sign(const int mode, DST_KEY *d_key, void **context,
+ const u_char *data, const int len,
+ u_char *signature, const int sig_len)
+{
+ HMAC_Key *key;
+ int sign_len = 0;
+ MD5_CTX *ctx = NULL;
+
+ if (d_key == NULL || d_key->dk_KEY_struct == NULL)
+ return (-1);
+
+ if (mode & SIG_MODE_INIT)
+ ctx = (MD5_CTX *) malloc(sizeof(*ctx));
+ else if (context)
+ ctx = (MD5_CTX *) *context;
+ if (ctx == NULL)
+ return (-1);
+
+ key = (HMAC_Key *) d_key->dk_KEY_struct;
+
+ if (mode & SIG_MODE_INIT) {
+ MD5Init(ctx);
+ MD5Update(ctx, key->hk_ipad, HMAC_LEN);
+ }
+
+ if ((mode & SIG_MODE_UPDATE) && (data && len > 0))
+ MD5Update(ctx, data, len);
+
+ if (mode & SIG_MODE_FINAL) {
+ if (signature == NULL || sig_len < MD5_LEN)
+ return (SIGN_FINAL_FAILURE);
+ MD5Final(signature, ctx);
+
+ /* perform outer MD5 */
+ MD5Init(ctx);
+ MD5Update(ctx, key->hk_opad, HMAC_LEN);
+ MD5Update(ctx, signature, MD5_LEN);
+ MD5Final(signature, ctx);
+ sign_len = MD5_LEN;
+ SAFE_FREE(ctx);
+ }
+ else {
+ if (context == NULL)
+ return (-1);
+ *context = (void *) ctx;
+ }
+ return (sign_len);
+}
+
+
+/**************************************************************************
+ * dst_hmac_md5_verify()
+ * Calls HMAC verification routines. There are three steps to
+ * verification, INIT (initialize structures), UPDATE (hash (more) data),
+ * FINAL (generate a signature). This routine performs one or more of
+ * these steps.
+ * Parameters
+ * mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL.
+ * dkey key to use for verify.
+ * data data signed.
+ * len length in bytes of data.
+ * signature signature.
+ * sig_len length in bytes of signature.
+ * returns
+ * 0 Success
+ * <0 Failure
+ */
+
+static int
+dst_hmac_md5_verify(const int mode, DST_KEY *d_key, void **context,
+ const u_char *data, const int len,
+ const u_char *signature, const int sig_len)
+{
+ HMAC_Key *key;
+ MD5_CTX *ctx = NULL;
+
+ if (d_key == NULL || d_key->dk_KEY_struct == NULL)
+ return (-1);
+
+ if (mode & SIG_MODE_INIT)
+ ctx = (MD5_CTX *) malloc(sizeof(*ctx));
+ else if (context)
+ ctx = (MD5_CTX *) *context;
+ if (ctx == NULL)
+ return (-1);
+
+ key = (HMAC_Key *) d_key->dk_KEY_struct;
+ if (mode & SIG_MODE_INIT) {
+ MD5Init(ctx);
+ MD5Update(ctx, key->hk_ipad, HMAC_LEN);
+ }
+ if ((mode & SIG_MODE_UPDATE) && (data && len > 0))
+ MD5Update(ctx, data, len);
+
+ if (mode & SIG_MODE_FINAL) {
+ u_char digest[MD5_LEN];
+ if (signature == NULL || key == NULL || sig_len != MD5_LEN)
+ return (VERIFY_FINAL_FAILURE);
+ MD5Final(digest, ctx);
+
+ /* perform outer MD5 */
+ MD5Init(ctx);
+ MD5Update(ctx, key->hk_opad, HMAC_LEN);
+ MD5Update(ctx, digest, MD5_LEN);
+ MD5Final(digest, ctx);
+
+ SAFE_FREE(ctx);
+ if (memcmp(digest, signature, MD5_LEN) != 0)
+ return (VERIFY_FINAL_FAILURE);
+ }
+ else {
+ if (context == NULL)
+ return (-1);
+ *context = (void *) ctx;
+ }
+ return (0);
+}
+
+
+/**************************************************************************
+ * dst_buffer_to_hmac_md5
+ * Converts key from raw data to an HMAC Key
+ * This function gets in a pointer to the data
+ * Parameters
+ * hkey the HMAC key to be filled in
+ * key the key in raw format
+ * keylen the length of the key
+ * Return
+ * 0 Success
+ * <0 Failure
+ */
+static int
+dst_buffer_to_hmac_md5(DST_KEY *dkey, const u_char *key, const int keylen)
+{
+ int i;
+ HMAC_Key *hkey = NULL;
+ MD5_CTX ctx;
+ int local_keylen = keylen;
+ u_char tk[MD5_LEN];
+
+ if (dkey == NULL || key == NULL || keylen < 0)
+ return (-1);
+
+ if ((hkey = (HMAC_Key *) malloc(sizeof(HMAC_Key))) == NULL)
+ return (-2);
+
+ memset(hkey->hk_ipad, 0, sizeof(hkey->hk_ipad));
+ memset(hkey->hk_opad, 0, sizeof(hkey->hk_opad));
+
+ /* if key is longer than HMAC_LEN bytes reset it to key=MD5(key) */
+ if (keylen > HMAC_LEN) {
+ MD5Init(&ctx);
+ MD5Update(&ctx, key, keylen);
+ MD5Final(tk, &ctx);
+ memset((void *) &ctx, 0, sizeof(ctx));
+ key = tk;
+ local_keylen = MD5_LEN;
+ }
+ /* start out by storing key in pads */
+ memcpy(hkey->hk_ipad, key, local_keylen);
+ memcpy(hkey->hk_opad, key, local_keylen);
+
+ /* XOR key with hk_ipad and opad values */
+ for (i = 0; i < HMAC_LEN; i++) {
+ hkey->hk_ipad[i] ^= HMAC_IPAD;
+ hkey->hk_opad[i] ^= HMAC_OPAD;
+ }
+ dkey->dk_key_size = local_keylen;
+ dkey->dk_KEY_struct = (void *) hkey;
+ return (1);
+}
+
+
+/**************************************************************************
+ * dst_hmac_md5_key_to_file_format
+ * Encodes an HMAC Key into the portable file format.
+ * Parameters
+ * hkey HMAC KEY structure
+ * buff output buffer
+ * buff_len size of output buffer
+ * Return
+ * 0 Failure - null input hkey
+ * -1 Failure - not enough space in output area
+ * N Success - Length of data returned in buff
+ */
+
+static int
+dst_hmac_md5_key_to_file_format(const DST_KEY *dkey, char *buff,
+ const int buff_len)
+{
+ char *bp;
+ int len, i, key_len;
+ u_char key[HMAC_LEN];
+ HMAC_Key *hkey;
+
+ if (dkey == NULL || dkey->dk_KEY_struct == NULL)
+ return (0);
+ /*
+ * Using snprintf() would be so much simpler here.
+ */
+ if (buff == NULL ||
+ buff_len <= (int)(strlen(key_file_fmt_str) +
+ strlen(KEY_FILE_FORMAT) + 4))
+ return (-1); /*%< no OR not enough space in output area */
+ hkey = (HMAC_Key *) dkey->dk_KEY_struct;
+ memset(buff, 0, buff_len); /*%< just in case */
+ /* write file header */
+ sprintf(buff, key_file_fmt_str, KEY_FILE_FORMAT, KEY_HMAC_MD5, "HMAC");
+
+ bp = buff + strlen(buff);
+
+ memset(key, 0, HMAC_LEN);
+ for (i = 0; i < HMAC_LEN; i++)
+ key[i] = hkey->hk_ipad[i] ^ HMAC_IPAD;
+ for (i = HMAC_LEN - 1; i >= 0; i--)
+ if (key[i] != 0)
+ break;
+ key_len = i + 1;
+
+ if (buff_len - (bp - buff) < 6)
+ return (-1);
+ strcat(bp, "Key: ");
+ bp += strlen("Key: ");
+
+ len = b64_ntop(key, key_len, bp, buff_len - (bp - buff));
+ if (len < 0)
+ return (-1);
+ bp += len;
+ if (buff_len - (bp - buff) < 2)
+ return (-1);
+ *(bp++) = '\n';
+ *bp = '\0';
+
+ return (bp - buff);
+}
+
+
+/**************************************************************************
+ * dst_hmac_md5_key_from_file_format
+ * Converts contents of a key file into an HMAC key.
+ * Parameters
+ * hkey structure to put key into
+ * buff buffer containing the encoded key
+ * buff_len the length of the buffer
+ * Return
+ * n >= 0 Foot print of the key converted
+ * n < 0 Error in conversion
+ */
+
+static int
+dst_hmac_md5_key_from_file_format(DST_KEY *dkey, const char *buff,
+ const int buff_len)
+{
+ const char *p = buff, *eol;
+ u_char key[HMAC_LEN+1]; /* b64_pton needs more than 64 bytes do decode
+ * it should probably be fixed rather than doing
+ * this
+ */
+ u_char *tmp;
+ int key_len, len;
+
+ if (dkey == NULL)
+ return (-2);
+ if (buff == NULL || buff_len < 0)
+ return (-1);
+
+ memset(key, 0, sizeof(key));
+
+ if (!dst_s_verify_str(&p, "Key: "))
+ return (-3);
+
+ eol = strchr(p, '\n');
+ if (eol == NULL)
+ return (-4);
+ len = eol - p;
+ tmp = malloc(len + 2);
+ if (tmp == NULL)
+ return (-5);
+ memcpy(tmp, p, len);
+ *(tmp + len) = 0x0;
+ key_len = b64_pton((char *)tmp, key, HMAC_LEN+1); /*%< see above */
+ SAFE_FREE2(tmp, len + 2);
+
+ if (dst_buffer_to_hmac_md5(dkey, key, key_len) < 0) {
+ return (-6);
+ }
+ return (0);
+}
+
+/*%
+ * dst_hmac_md5_to_dns_key()
+ * function to extract hmac key from DST_KEY structure
+ * intput:
+ * in_key: HMAC-MD5 key
+ * output:
+ * out_str: buffer to write ot
+ * out_len: size of output buffer
+ * returns:
+ * number of bytes written to output buffer
+ */
+static int
+dst_hmac_md5_to_dns_key(const DST_KEY *in_key, u_char *out_str,
+ const int out_len)
+{
+
+ HMAC_Key *hkey;
+ int i;
+
+ if (in_key == NULL || in_key->dk_KEY_struct == NULL ||
+ out_len <= in_key->dk_key_size || out_str == NULL)
+ return (-1);
+
+ hkey = (HMAC_Key *) in_key->dk_KEY_struct;
+ for (i = 0; i < in_key->dk_key_size; i++)
+ out_str[i] = hkey->hk_ipad[i] ^ HMAC_IPAD;
+ return (i);
+}
+
+/**************************************************************************
+ * dst_hmac_md5_compare_keys
+ * Compare two keys for equality.
+ * Return
+ * 0 The keys are equal
+ * NON-ZERO The keys are not equal
+ */
+
+static int
+dst_hmac_md5_compare_keys(const DST_KEY *key1, const DST_KEY *key2)
+{
+ HMAC_Key *hkey1 = (HMAC_Key *) key1->dk_KEY_struct;
+ HMAC_Key *hkey2 = (HMAC_Key *) key2->dk_KEY_struct;
+ return memcmp(hkey1->hk_ipad, hkey2->hk_ipad, HMAC_LEN);
+}
+
+/**************************************************************************
+ * dst_hmac_md5_free_key_structure
+ * Frees all (none) dynamically allocated structures in hkey
+ */
+
+static void *
+dst_hmac_md5_free_key_structure(void *key)
+{
+ HMAC_Key *hkey = key;
+ SAFE_FREE(hkey);
+ return (NULL);
+}
+
+
+/***************************************************************************
+ * dst_hmac_md5_generate_key
+ * Creates a HMAC key of size size with a maximum size of 63 bytes
+ * generating a HMAC key larger than 63 bytes makes no sense as that key
+ * is digested before use.
+ */
+
+static int
+dst_hmac_md5_generate_key(DST_KEY *key, const int nothing)
+{
+ (void)key;
+ (void)nothing;
+ return (-1);
+}
+
+/*%
+ * dst_hmac_md5_init() Function to answer set up function pointers for HMAC
+ * related functions
+ */
+int
+dst_hmac_md5_init()
+{
+ if (dst_t_func[KEY_HMAC_MD5] != NULL)
+ return (1);
+ dst_t_func[KEY_HMAC_MD5] = malloc(sizeof(struct dst_func));
+ if (dst_t_func[KEY_HMAC_MD5] == NULL)
+ return (0);
+ memset(dst_t_func[KEY_HMAC_MD5], 0, sizeof(struct dst_func));
+ dst_t_func[KEY_HMAC_MD5]->sign = dst_hmac_md5_sign;
+ dst_t_func[KEY_HMAC_MD5]->verify = dst_hmac_md5_verify;
+ dst_t_func[KEY_HMAC_MD5]->compare = dst_hmac_md5_compare_keys;
+ dst_t_func[KEY_HMAC_MD5]->generate = dst_hmac_md5_generate_key;
+ dst_t_func[KEY_HMAC_MD5]->destroy = dst_hmac_md5_free_key_structure;
+ dst_t_func[KEY_HMAC_MD5]->to_dns_key = dst_hmac_md5_to_dns_key;
+ dst_t_func[KEY_HMAC_MD5]->from_dns_key = dst_buffer_to_hmac_md5;
+ dst_t_func[KEY_HMAC_MD5]->to_file_fmt = dst_hmac_md5_key_to_file_format;
+ dst_t_func[KEY_HMAC_MD5]->from_file_fmt = dst_hmac_md5_key_from_file_format;
+ return (1);
+}
+
+#else
+#define dst_hmac_md5_init __dst_hmac_md5_init
+
+int
+dst_hmac_md5_init(){
+ return (0);
+}
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/dst/support.c b/usr/src/lib/libresolv2_joy/common/dst/support.c
new file mode 100644
index 0000000000..cc975284b6
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/dst/support.c
@@ -0,0 +1,339 @@
+/*
+ * Portions Copyright (c) 1995-1998 by Trusted Information Systems, Inc.
+ *
+ * Permission to use, copy modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND TRUSTED INFORMATION SYSTEMS
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * TRUSTED INFORMATION SYSTEMS BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THE SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <memory.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include "dst_internal.h"
+
+#include "port_after.h"
+
+/*%
+ * dst_s_verify_str()
+ * Validate that the input string(*str) is at the head of the input
+ * buffer(**buf). If so, move the buffer head pointer (*buf) to
+ * the first byte of data following the string(*str).
+ * Parameters
+ * buf Input buffer.
+ * str Input string.
+ * Return
+ * 0 *str is not the head of **buff
+ * 1 *str is the head of **buff, *buf is is advanced to
+ * the tail of **buf.
+ */
+
+int
+dst_s_verify_str(const char **buf, const char *str)
+{
+ int b, s;
+ if (*buf == NULL) /*%< error checks */
+ return (0);
+ if (str == NULL || *str == '\0')
+ return (1);
+
+ b = strlen(*buf); /*%< get length of strings */
+ s = strlen(str);
+ if (s > b || strncmp(*buf, str, s)) /*%< check if same */
+ return (0); /*%< not a match */
+ (*buf) += s; /*%< advance pointer */
+ return (1);
+}
+
+/*%
+ * dst_s_calculate_bits
+ * Given a binary number represented in a u_char[], determine
+ * the number of significant bits used.
+ * Parameters
+ * str An input character string containing a binary number.
+ * max_bits The maximum possible significant bits.
+ * Return
+ * N The number of significant bits in str.
+ */
+
+int
+dst_s_calculate_bits(const u_char *str, const int max_bits)
+{
+ const u_char *p = str;
+ u_char i, j = 0x80;
+ int bits;
+ for (bits = max_bits; *p == 0x00 && bits > 0; p++)
+ bits -= 8;
+ for (i = *p; (i & j) != j; j >>= 1)
+ bits--;
+ return (bits);
+}
+
+/*%
+ * calculates a checksum used in dst for an id.
+ * takes an array of bytes and a length.
+ * returns a 16 bit checksum.
+ */
+u_int16_t
+dst_s_id_calc(const u_char *key, const int keysize)
+{
+ u_int32_t ac;
+ const u_char *kp = key;
+ int size = keysize;
+
+ if (!key || (keysize <= 0))
+ return (0xffffU);
+
+ for (ac = 0; size > 1; size -= 2, kp += 2)
+ ac += ((*kp) << 8) + *(kp + 1);
+
+ if (size > 0)
+ ac += ((*kp) << 8);
+ ac += (ac >> 16) & 0xffff;
+
+ return (ac & 0xffff);
+}
+
+/*%
+ * dst_s_dns_key_id() Function to calculate DNSSEC footprint from KEY record
+ * rdata
+ * Input:
+ * dns_key_rdata: the raw data in wire format
+ * rdata_len: the size of the input data
+ * Output:
+ * the key footprint/id calculated from the key data
+ */
+u_int16_t
+dst_s_dns_key_id(const u_char *dns_key_rdata, const int rdata_len)
+{
+ if (!dns_key_rdata)
+ return 0;
+
+ /* compute id */
+ if (dns_key_rdata[3] == KEY_RSA) /*%< Algorithm RSA */
+ return dst_s_get_int16((const u_char *)
+ &dns_key_rdata[rdata_len - 3]);
+ else if (dns_key_rdata[3] == KEY_HMAC_MD5)
+ /* compatibility */
+ return 0;
+ else
+ /* compute a checksum on the key part of the key rr */
+ return dst_s_id_calc(dns_key_rdata, rdata_len);
+}
+
+/*%
+ * dst_s_get_int16
+ * This routine extracts a 16 bit integer from a two byte character
+ * string. The character string is assumed to be in network byte
+ * order and may be unaligned. The number returned is in host order.
+ * Parameter
+ * buf A two byte character string.
+ * Return
+ * The converted integer value.
+ */
+
+u_int16_t
+dst_s_get_int16(const u_char *buf)
+{
+ register u_int16_t a = 0;
+ a = ((u_int16_t)(buf[0] << 8)) | ((u_int16_t)(buf[1]));
+ return (a);
+}
+
+/*%
+ * dst_s_get_int32
+ * This routine extracts a 32 bit integer from a four byte character
+ * string. The character string is assumed to be in network byte
+ * order and may be unaligned. The number returned is in host order.
+ * Parameter
+ * buf A four byte character string.
+ * Return
+ * The converted integer value.
+ */
+
+u_int32_t
+dst_s_get_int32(const u_char *buf)
+{
+ register u_int32_t a = 0;
+ a = ((u_int32_t)(buf[0] << 24)) | ((u_int32_t)(buf[1] << 16)) |
+ ((u_int32_t)(buf[2] << 8)) | ((u_int32_t)(buf[3]));
+ return (a);
+}
+
+/*%
+ * dst_s_put_int16
+ * Take a 16 bit integer and store the value in a two byte
+ * character string. The integer is assumed to be in network
+ * order and the string is returned in host order.
+ *
+ * Parameters
+ * buf Storage for a two byte character string.
+ * val 16 bit integer.
+ */
+
+void
+dst_s_put_int16(u_int8_t *buf, const u_int16_t val)
+{
+ buf[0] = (u_int8_t)(val >> 8);
+ buf[1] = (u_int8_t)(val);
+}
+
+/*%
+ * dst_s_put_int32
+ * Take a 32 bit integer and store the value in a four byte
+ * character string. The integer is assumed to be in network
+ * order and the string is returned in host order.
+ *
+ * Parameters
+ * buf Storage for a four byte character string.
+ * val 32 bit integer.
+ */
+
+void
+dst_s_put_int32(u_int8_t *buf, const u_int32_t val)
+{
+ buf[0] = (u_int8_t)(val >> 24);
+ buf[1] = (u_int8_t)(val >> 16);
+ buf[2] = (u_int8_t)(val >> 8);
+ buf[3] = (u_int8_t)(val);
+}
+
+/*%
+ * dst_s_filename_length
+ *
+ * This function returns the number of bytes needed to hold the
+ * filename for a key file. '/', '\' and ':' are not allowed.
+ * form: K&lt;keyname&gt;+&lt;alg&gt;+&lt;id&gt;.&lt;suffix&gt;
+ *
+ * Returns 0 if the filename would contain either '\', '/' or ':'
+ */
+size_t
+dst_s_filename_length(const char *name, const char *suffix)
+{
+ if (name == NULL)
+ return (0);
+ if (strrchr(name, '\\'))
+ return (0);
+ if (strrchr(name, '/'))
+ return (0);
+ if (strrchr(name, ':'))
+ return (0);
+ if (suffix == NULL)
+ return (0);
+ if (strrchr(suffix, '\\'))
+ return (0);
+ if (strrchr(suffix, '/'))
+ return (0);
+ if (strrchr(suffix, ':'))
+ return (0);
+ return (1 + strlen(name) + 6 + strlen(suffix));
+}
+
+/*%
+ * dst_s_build_filename ()
+ * Builds a key filename from the key name, it's id, and a
+ * suffix. '\', '/' and ':' are not allowed. fA filename is of the
+ * form: K&lt;keyname&gt;&lt;id&gt;.&lt;suffix&gt;
+ * form: K&lt;keyname&gt;+&lt;alg&gt;+&lt;id&gt;.&lt;suffix&gt;
+ *
+ * Returns -1 if the conversion fails:
+ * if the filename would be too long for space allotted
+ * if the filename would contain a '\', '/' or ':'
+ * Returns 0 on success
+ */
+
+int
+dst_s_build_filename(char *filename, const char *name, u_int16_t id,
+ int alg, const char *suffix, size_t filename_length)
+{
+ u_int32_t my_id;
+ if (filename == NULL)
+ return (-1);
+ memset(filename, 0, filename_length);
+ if (name == NULL)
+ return (-1);
+ if (suffix == NULL)
+ return (-1);
+ if (filename_length < 1 + strlen(name) + 4 + 6 + 1 + strlen(suffix))
+ return (-1);
+ my_id = id;
+ sprintf(filename, "K%s+%03d+%05d.%s", name, alg, my_id,
+ (const char *) suffix);
+ if (strrchr(filename, '/'))
+ return (-1);
+ if (strrchr(filename, '\\'))
+ return (-1);
+ if (strrchr(filename, ':'))
+ return (-1);
+ return (0);
+}
+
+/*%
+ * dst_s_fopen ()
+ * Open a file in the dst_path directory. If perm is specified, the
+ * file is checked for existence first, and not opened if it exists.
+ * Parameters
+ * filename File to open
+ * mode Mode to open the file (passed directly to fopen)
+ * perm File permission, if creating a new file.
+ * Returns
+ * NULL Failure
+ * NON-NULL (FILE *) of opened file.
+ */
+FILE *
+dst_s_fopen(const char *filename, const char *mode, int perm)
+{
+ FILE *fp;
+ char pathname[PATH_MAX];
+
+ if (strlen(filename) + strlen(dst_path) >= sizeof(pathname))
+ return (NULL);
+
+ if (*dst_path != '\0') {
+ strcpy(pathname, dst_path);
+ strcat(pathname, filename);
+ } else
+ strcpy(pathname, filename);
+
+ fp = fopen(pathname, mode);
+ if (perm)
+ chmod(pathname, perm);
+ return (fp);
+}
+
+void
+dst_s_dump(const int mode, const u_char *data, const int size,
+ const char *msg)
+{
+ UNUSED(data);
+
+ if (size > 0) {
+#ifdef LONG_TEST
+ static u_char scratch[1000];
+ int n ;
+ n = b64_ntop(data, scratch, size, sizeof(scratch));
+ printf("%s: %x %d %s\n", msg, mode, n, scratch);
+#else
+ printf("%s,%x %d\n", msg, mode, size);
+#endif
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_ntop.c b/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_ntop.c
new file mode 100644
index 0000000000..bf960a8acc
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_ntop.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char rcsid[] = "$Id: inet_cidr_ntop.c,v 1.7 2006/10/11 02:18:18 marka Exp $";
+#endif
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+static char *
+inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size);
+static char *
+inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size);
+
+/*%
+ * char *
+ * inet_cidr_ntop(af, src, bits, dst, size)
+ * convert network address from network to presentation format.
+ * "src"'s size is determined from its "af".
+ * return:
+ * pointer to dst, or NULL if an error occurred (check errno).
+ * note:
+ * 192.5.5.1/28 has a nonzero host part, which means it isn't a network
+ * as called for by inet_net_ntop() but it can be a host address with
+ * an included netmask.
+ * author:
+ * Paul Vixie (ISC), October 1998
+ */
+char *
+inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size) {
+ switch (af) {
+ case AF_INET:
+ return (inet_cidr_ntop_ipv4(src, bits, dst, size));
+ case AF_INET6:
+ return (inet_cidr_ntop_ipv6(src, bits, dst, size));
+ default:
+ errno = EAFNOSUPPORT;
+ return (NULL);
+ }
+}
+
+static int
+decoct(const u_char *src, int bytes, char *dst, size_t size) {
+ char *odst = dst;
+ char *t;
+ int b;
+
+ for (b = 1; b <= bytes; b++) {
+ if (size < sizeof "255.")
+ return (0);
+ t = dst;
+ dst += SPRINTF((dst, "%u", *src++));
+ if (b != bytes) {
+ *dst++ = '.';
+ *dst = '\0';
+ }
+ size -= (size_t)(dst - t);
+ }
+ return (dst - odst);
+}
+
+/*%
+ * static char *
+ * inet_cidr_ntop_ipv4(src, bits, dst, size)
+ * convert IPv4 network address from network to presentation format.
+ * "src"'s size is determined from its "af".
+ * return:
+ * pointer to dst, or NULL if an error occurred (check errno).
+ * note:
+ * network byte order assumed. this means 192.5.5.240/28 has
+ * 0b11110000 in its fourth octet.
+ * author:
+ * Paul Vixie (ISC), October 1998
+ */
+static char *
+inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size) {
+ char *odst = dst;
+ size_t len = 4;
+ size_t b;
+ size_t bytes;
+
+ if ((bits < -1) || (bits > 32)) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /* Find number of significant bytes in address. */
+ if (bits == -1)
+ len = 4;
+ else
+ for (len = 1, b = 1 ; b < 4U; b++)
+ if (*(src + b))
+ len = b + 1;
+
+ /* Format whole octets plus nonzero trailing octets. */
+ bytes = (((bits <= 0) ? 1 : bits) + 7) / 8;
+ if (len > bytes)
+ bytes = len;
+ b = decoct(src, bytes, dst, size);
+ if (b == 0U)
+ goto emsgsize;
+ dst += b;
+ size -= b;
+
+ if (bits != -1) {
+ /* Format CIDR /width. */
+ if (size < sizeof "/32")
+ goto emsgsize;
+ dst += SPRINTF((dst, "/%u", bits));
+ }
+
+ return (odst);
+
+ emsgsize:
+ errno = EMSGSIZE;
+ return (NULL);
+}
+
+static char *
+inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) {
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"];
+ char *tp;
+ struct { int base, len; } best, cur;
+ u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
+ int i;
+
+ if ((bits < -1) || (bits > 128)) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /*
+ * Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ memset(words, '\0', sizeof words);
+ for (i = 0; i < NS_IN6ADDRSZ; i++)
+ words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+ best.base = -1;
+ best.len = 0;
+ cur.base = -1;
+ cur.len = 0;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ if (words[i] == 0) {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else
+ cur.len++;
+ } else {
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ }
+ if (cur.base != -1) {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ }
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /*
+ * Format the result.
+ */
+ tp = tmp;
+ for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+ /* Are we inside the best run of 0x00's? */
+ if (best.base != -1 && i >= best.base &&
+ i < (best.base + best.len)) {
+ if (i == best.base)
+ *tp++ = ':';
+ continue;
+ }
+ /* Are we following an initial run of 0x00s or any real hex? */
+ if (i != 0)
+ *tp++ = ':';
+ /* Is this address an encapsulated IPv4? */
+ if (i == 6 && best.base == 0 && (best.len == 6 ||
+ (best.len == 7 && words[7] != 0x0001) ||
+ (best.len == 5 && words[5] == 0xffff))) {
+ int n;
+
+ if (src[15] || bits == -1 || bits > 120)
+ n = 4;
+ else if (src[14] || bits > 112)
+ n = 3;
+ else
+ n = 2;
+ n = decoct(src+12, n, tp, sizeof tmp - (tp - tmp));
+ if (n == 0) {
+ errno = EMSGSIZE;
+ return (NULL);
+ }
+ tp += strlen(tp);
+ break;
+ }
+ tp += SPRINTF((tp, "%x", words[i]));
+ }
+
+ /* Was it a trailing run of 0x00's? */
+ if (best.base != -1 && (best.base + best.len) ==
+ (NS_IN6ADDRSZ / NS_INT16SZ))
+ *tp++ = ':';
+ *tp = '\0';
+
+ if (bits != -1)
+ tp += SPRINTF((tp, "/%u", bits));
+
+ /*
+ * Check for overflow, copy, and we're done.
+ */
+ if ((size_t)(tp - tmp) > size) {
+ errno = EMSGSIZE;
+ return (NULL);
+ }
+ strcpy(dst, tmp);
+ return (dst);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_pton.c b/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_pton.c
new file mode 100644
index 0000000000..07652af463
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/inet_cidr_pton.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char rcsid[] = "$Id: inet_cidr_pton.c,v 1.6 2005/04/27 04:56:19 sra Exp $";
+#endif
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <isc/assertions.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+static int inet_cidr_pton_ipv4 __P((const char *src, u_char *dst,
+ int *bits, int ipv6));
+static int inet_cidr_pton_ipv6 __P((const char *src, u_char *dst,
+ int *bits));
+
+static int getbits(const char *, int ipv6);
+
+/*%
+ * int
+ * inet_cidr_pton(af, src, dst, *bits)
+ * convert network address from presentation to network format.
+ * accepts inet_pton()'s input for this "af" plus trailing "/CIDR".
+ * "dst" is assumed large enough for its "af". "bits" is set to the
+ * /CIDR prefix length, which can have defaults (like /32 for IPv4).
+ * return:
+ * -1 if an error occurred (inspect errno; ENOENT means bad format).
+ * 0 if successful conversion occurred.
+ * note:
+ * 192.5.5.1/28 has a nonzero host part, which means it isn't a network
+ * as called for by inet_net_pton() but it can be a host address with
+ * an included netmask.
+ * author:
+ * Paul Vixie (ISC), October 1998
+ */
+int
+inet_cidr_pton(int af, const char *src, void *dst, int *bits) {
+ switch (af) {
+ case AF_INET:
+ return (inet_cidr_pton_ipv4(src, dst, bits, 0));
+ case AF_INET6:
+ return (inet_cidr_pton_ipv6(src, dst, bits));
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+}
+
+static const char digits[] = "0123456789";
+
+static int
+inet_cidr_pton_ipv4(const char *src, u_char *dst, int *pbits, int ipv6) {
+ const u_char *odst = dst;
+ int n, ch, tmp, bits;
+ size_t size = 4;
+
+ /* Get the mantissa. */
+ while (ch = *src++, (isascii(ch) && isdigit(ch))) {
+ tmp = 0;
+ do {
+ n = strchr(digits, ch) - digits;
+ INSIST(n >= 0 && n <= 9);
+ tmp *= 10;
+ tmp += n;
+ if (tmp > 255)
+ goto enoent;
+ } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch));
+ if (size-- == 0U)
+ goto emsgsize;
+ *dst++ = (u_char) tmp;
+ if (ch == '\0' || ch == '/')
+ break;
+ if (ch != '.')
+ goto enoent;
+ }
+
+ /* Get the prefix length if any. */
+ bits = -1;
+ if (ch == '/' && dst > odst) {
+ bits = getbits(src, ipv6);
+ if (bits == -2)
+ goto enoent;
+ } else if (ch != '\0')
+ goto enoent;
+
+ /* Prefix length can default to /32 only if all four octets spec'd. */
+ if (bits == -1) {
+ if (dst - odst == 4)
+ bits = ipv6 ? 128 : 32;
+ else
+ goto enoent;
+ }
+
+ /* If nothing was written to the destination, we found no address. */
+ if (dst == odst)
+ goto enoent;
+
+ /* If prefix length overspecifies mantissa, life is bad. */
+ if (((bits - (ipv6 ? 96 : 0)) / 8) > (dst - odst))
+ goto enoent;
+
+ /* Extend address to four octets. */
+ while (size-- > 0U)
+ *dst++ = 0;
+
+ *pbits = bits;
+ return (0);
+
+ enoent:
+ errno = ENOENT;
+ return (-1);
+
+ emsgsize:
+ errno = EMSGSIZE;
+ return (-1);
+}
+
+static int
+inet_cidr_pton_ipv6(const char *src, u_char *dst, int *pbits) {
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, saw_xdigit;
+ u_int val;
+ int bits;
+
+ memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+ endp = tp + NS_IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return (0);
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ bits = -1;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (val > 0xffff)
+ return (0);
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ } else if (*src == '\0') {
+ return (0);
+ }
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (u_char) (val >> 8) & 0xff;
+ *tp++ = (u_char) val & 0xff;
+ saw_xdigit = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+ inet_cidr_pton_ipv4(curtok, tp, &bits, 1) == 0) {
+ tp += NS_INADDRSZ;
+ saw_xdigit = 0;
+ break; /*%< '\\0' was seen by inet_pton4(). */
+ }
+ if (ch == '/') {
+ bits = getbits(src, 1);
+ if (bits == -2)
+ goto enoent;
+ break;
+ }
+ goto enoent;
+ }
+ if (saw_xdigit) {
+ if (tp + NS_INT16SZ > endp)
+ goto emsgsize;
+ *tp++ = (u_char) (val >> 8) & 0xff;
+ *tp++ = (u_char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ if (tp == endp)
+ goto enoent;
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+
+ memcpy(dst, tmp, NS_IN6ADDRSZ);
+
+ *pbits = bits;
+ return (0);
+
+ enoent:
+ errno = ENOENT;
+ return (-1);
+
+ emsgsize:
+ errno = EMSGSIZE;
+ return (-1);
+}
+
+static int
+getbits(const char *src, int ipv6) {
+ int bits = 0;
+ char *cp, ch;
+
+ if (*src == '\0') /*%< syntax */
+ return (-2);
+ do {
+ ch = *src++;
+ cp = strchr(digits, ch);
+ if (cp == NULL) /*%< syntax */
+ return (-2);
+ bits *= 10;
+ bits += cp - digits;
+ if (bits == 0 && *src != '\0') /*%< no leading zeros */
+ return (-2);
+ if (bits > (ipv6 ? 128 : 32)) /*%< range error */
+ return (-2);
+ } while (*src != '\0');
+
+ return (bits);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_data.c b/usr/src/lib/libresolv2_joy/common/inet/inet_data.c
new file mode 100644
index 0000000000..58b8c4342d
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/inet_data.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char rcsid[] = "$Id: inet_data.c,v 1.4 2005/04/27 04:56:19 sra Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "port_after.h"
+
+const struct in6_addr isc_in6addr_any = IN6ADDR_ANY_INIT;
+const struct in6_addr isc_in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_lnaof.c b/usr/src/lib/libresolv2_joy/common/inet/inet_lnaof.c
new file mode 100644
index 0000000000..70ac409512
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/inet_lnaof.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1983, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)inet_lnaof.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "port_after.h"
+
+/*%
+ * Return the local network address portion of an
+ * internet address; handles class a/b/c network
+ * number formats.
+ */
+u_long
+inet_lnaof(in)
+ struct in_addr in;
+{
+ register u_long i = ntohl(in.s_addr);
+
+ if (IN_CLASSA(i))
+ return ((i)&IN_CLASSA_HOST);
+ else if (IN_CLASSB(i))
+ return ((i)&IN_CLASSB_HOST);
+ else
+ return ((i)&IN_CLASSC_HOST);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_makeaddr.c b/usr/src/lib/libresolv2_joy/common/inet/inet_makeaddr.c
new file mode 100644
index 0000000000..c56cb3eaeb
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/inet_makeaddr.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1983, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)inet_makeaddr.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "port_after.h"
+
+/*%
+ * Formulate an Internet address from network + host. Used in
+ * building addresses stored in the ifnet structure.
+ */
+struct in_addr
+inet_makeaddr(net, host)
+ u_long net, host;
+{
+ struct in_addr a;
+
+ if (net < 128U)
+ a.s_addr = (net << IN_CLASSA_NSHIFT) | (host & IN_CLASSA_HOST);
+ else if (net < 65536U)
+ a.s_addr = (net << IN_CLASSB_NSHIFT) | (host & IN_CLASSB_HOST);
+ else if (net < 16777216L)
+ a.s_addr = (net << IN_CLASSC_NSHIFT) | (host & IN_CLASSC_HOST);
+ else
+ a.s_addr = net | host;
+ a.s_addr = htonl(a.s_addr);
+ return (a);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_net_ntop.c b/usr/src/lib/libresolv2_joy/common/inet/inet_net_ntop.c
new file mode 100644
index 0000000000..fb28e3cbe5
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/inet_net_ntop.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char rcsid[] = "$Id: inet_net_ntop.c,v 1.5 2006/06/20 02:50:14 marka Exp $";
+#endif
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+static char * inet_net_ntop_ipv4 __P((const u_char *src, int bits,
+ char *dst, size_t size));
+static char * inet_net_ntop_ipv6 __P((const u_char *src, int bits,
+ char *dst, size_t size));
+
+/*%
+ * char *
+ * inet_net_ntop(af, src, bits, dst, size)
+ * convert network number from network to presentation format.
+ * generates CIDR style result always.
+ * return:
+ * pointer to dst, or NULL if an error occurred (check errno).
+ * author:
+ * Paul Vixie (ISC), July 1996
+ */
+char *
+inet_net_ntop(af, src, bits, dst, size)
+ int af;
+ const void *src;
+ int bits;
+ char *dst;
+ size_t size;
+{
+ switch (af) {
+ case AF_INET:
+ return (inet_net_ntop_ipv4(src, bits, dst, size));
+ case AF_INET6:
+ return (inet_net_ntop_ipv6(src, bits, dst, size));
+ default:
+ errno = EAFNOSUPPORT;
+ return (NULL);
+ }
+}
+
+/*%
+ * static char *
+ * inet_net_ntop_ipv4(src, bits, dst, size)
+ * convert IPv4 network number from network to presentation format.
+ * generates CIDR style result always.
+ * return:
+ * pointer to dst, or NULL if an error occurred (check errno).
+ * note:
+ * network byte order assumed. this means 192.5.5.240/28 has
+ * 0b11110000 in its fourth octet.
+ * author:
+ * Paul Vixie (ISC), July 1996
+ */
+static char *
+inet_net_ntop_ipv4(src, bits, dst, size)
+ const u_char *src;
+ int bits;
+ char *dst;
+ size_t size;
+{
+ char *odst = dst;
+ char *t;
+ u_int m;
+ int b;
+
+ if (bits < 0 || bits > 32) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (bits == 0) {
+ if (size < sizeof "0")
+ goto emsgsize;
+ *dst++ = '0';
+ size--;
+ *dst = '\0';
+ }
+
+ /* Format whole octets. */
+ for (b = bits / 8; b > 0; b--) {
+ if (size <= sizeof "255.")
+ goto emsgsize;
+ t = dst;
+ dst += SPRINTF((dst, "%u", *src++));
+ if (b > 1) {
+ *dst++ = '.';
+ *dst = '\0';
+ }
+ size -= (size_t)(dst - t);
+ }
+
+ /* Format partial octet. */
+ b = bits % 8;
+ if (b > 0) {
+ if (size <= sizeof ".255")
+ goto emsgsize;
+ t = dst;
+ if (dst != odst)
+ *dst++ = '.';
+ m = ((1 << b) - 1) << (8 - b);
+ dst += SPRINTF((dst, "%u", *src & m));
+ size -= (size_t)(dst - t);
+ }
+
+ /* Format CIDR /width. */
+ if (size <= sizeof "/32")
+ goto emsgsize;
+ dst += SPRINTF((dst, "/%u", bits));
+ return (odst);
+
+ emsgsize:
+ errno = EMSGSIZE;
+ return (NULL);
+}
+
+/*%
+ * static char *
+ * inet_net_ntop_ipv6(src, bits, fakebits, dst, size)
+ * convert IPv6 network number from network to presentation format.
+ * generates CIDR style result always. Picks the shortest representation
+ * unless the IP is really IPv4.
+ * always prints specified number of bits (bits).
+ * return:
+ * pointer to dst, or NULL if an error occurred (check errno).
+ * note:
+ * network byte order assumed. this means 192.5.5.240/28 has
+ * 0x11110000 in its fourth octet.
+ * author:
+ * Vadim Kogan (UCB), June 2001
+ * Original version (IPv4) by Paul Vixie (ISC), July 1996
+ */
+
+static char *
+inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size) {
+ u_int m;
+ int b;
+ int p;
+ int zero_s, zero_l, tmp_zero_s, tmp_zero_l;
+ int i;
+ int is_ipv4 = 0;
+ unsigned char inbuf[16];
+ char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+ char *cp;
+ int words;
+ u_char *s;
+
+ if (bits < 0 || bits > 128) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ cp = outbuf;
+
+ if (bits == 0) {
+ *cp++ = ':';
+ *cp++ = ':';
+ *cp = '\0';
+ } else {
+ /* Copy src to private buffer. Zero host part. */
+ p = (bits + 7) / 8;
+ memcpy(inbuf, src, p);
+ memset(inbuf + p, 0, 16 - p);
+ b = bits % 8;
+ if (b != 0) {
+ m = ~0 << (8 - b);
+ inbuf[p-1] &= m;
+ }
+
+ s = inbuf;
+
+ /* how many words need to be displayed in output */
+ words = (bits + 15) / 16;
+ if (words == 1)
+ words = 2;
+
+ /* Find the longest substring of zero's */
+ zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
+ for (i = 0; i < (words * 2); i += 2) {
+ if ((s[i] | s[i+1]) == 0) {
+ if (tmp_zero_l == 0)
+ tmp_zero_s = i / 2;
+ tmp_zero_l++;
+ } else {
+ if (tmp_zero_l && zero_l < tmp_zero_l) {
+ zero_s = tmp_zero_s;
+ zero_l = tmp_zero_l;
+ tmp_zero_l = 0;
+ }
+ }
+ }
+
+ if (tmp_zero_l && zero_l < tmp_zero_l) {
+ zero_s = tmp_zero_s;
+ zero_l = tmp_zero_l;
+ }
+
+ if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
+ ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
+ ((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
+ is_ipv4 = 1;
+
+ /* Format whole words. */
+ for (p = 0; p < words; p++) {
+ if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) {
+ /* Time to skip some zeros */
+ if (p == zero_s)
+ *cp++ = ':';
+ if (p == words - 1)
+ *cp++ = ':';
+ s++;
+ s++;
+ continue;
+ }
+
+ if (is_ipv4 && p > 5 ) {
+ *cp++ = (p == 6) ? ':' : '.';
+ cp += SPRINTF((cp, "%u", *s++));
+ /* we can potentially drop the last octet */
+ if (p != 7 || bits > 120) {
+ *cp++ = '.';
+ cp += SPRINTF((cp, "%u", *s++));
+ }
+ } else {
+ if (cp != outbuf)
+ *cp++ = ':';
+ cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
+ s += 2;
+ }
+ }
+ }
+ /* Format CIDR /width. */
+ sprintf(cp, "/%u", bits);
+ if (strlen(outbuf) + 1 > size)
+ goto emsgsize;
+ strcpy(dst, outbuf);
+
+ return (dst);
+
+emsgsize:
+ errno = EMSGSIZE;
+ return (NULL);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_net_pton.c b/usr/src/lib/libresolv2_joy/common/inet/inet_net_pton.c
new file mode 100644
index 0000000000..8d8bfb1f64
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/inet_net_pton.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1996, 1998, 1999, 2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char rcsid[] = "$Id: inet_net_pton.c,v 1.10 2008/11/14 02:36:51 marka Exp $";
+#endif
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <isc/assertions.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/*%
+ * static int
+ * inet_net_pton_ipv4(src, dst, size)
+ * convert IPv4 network number from presentation to network format.
+ * accepts hex octets, hex strings, decimal octets, and /CIDR.
+ * "size" is in bytes and describes "dst".
+ * return:
+ * number of bits, either imputed classfully or specified with /CIDR,
+ * or -1 if some failure occurred (check errno). ENOENT means it was
+ * not an IPv4 network specification.
+ * note:
+ * network byte order assumed. this means 192.5.5.240/28 has
+ * 0b11110000 in its fourth octet.
+ * author:
+ * Paul Vixie (ISC), June 1996
+ */
+static int
+inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) {
+ static const char xdigits[] = "0123456789abcdef";
+ static const char digits[] = "0123456789";
+ int n, ch, tmp = 0, dirty, bits;
+ const u_char *odst = dst;
+
+ ch = *src++;
+ if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
+ && isascii((unsigned char)(src[1]))
+ && isxdigit((unsigned char)(src[1]))) {
+ /* Hexadecimal: Eat nybble string. */
+ if (size <= 0U)
+ goto emsgsize;
+ dirty = 0;
+ src++; /*%< skip x or X. */
+ while ((ch = *src++) != '\0' && isascii(ch) && isxdigit(ch)) {
+ if (isupper(ch))
+ ch = tolower(ch);
+ n = strchr(xdigits, ch) - xdigits;
+ INSIST(n >= 0 && n <= 15);
+ if (dirty == 0)
+ tmp = n;
+ else
+ tmp = (tmp << 4) | n;
+ if (++dirty == 2) {
+ if (size-- <= 0U)
+ goto emsgsize;
+ *dst++ = (u_char) tmp;
+ dirty = 0;
+ }
+ }
+ if (dirty) { /*%< Odd trailing nybble? */
+ if (size-- <= 0U)
+ goto emsgsize;
+ *dst++ = (u_char) (tmp << 4);
+ }
+ } else if (isascii(ch) && isdigit(ch)) {
+ /* Decimal: eat dotted digit string. */
+ for (;;) {
+ tmp = 0;
+ do {
+ n = strchr(digits, ch) - digits;
+ INSIST(n >= 0 && n <= 9);
+ tmp *= 10;
+ tmp += n;
+ if (tmp > 255)
+ goto enoent;
+ } while ((ch = *src++) != '\0' &&
+ isascii(ch) && isdigit(ch));
+ if (size-- <= 0U)
+ goto emsgsize;
+ *dst++ = (u_char) tmp;
+ if (ch == '\0' || ch == '/')
+ break;
+ if (ch != '.')
+ goto enoent;
+ ch = *src++;
+ if (!isascii(ch) || !isdigit(ch))
+ goto enoent;
+ }
+ } else
+ goto enoent;
+
+ bits = -1;
+ if (ch == '/' && isascii((unsigned char)(src[0])) &&
+ isdigit((unsigned char)(src[0])) && dst > odst) {
+ /* CIDR width specifier. Nothing can follow it. */
+ ch = *src++; /*%< Skip over the /. */
+ bits = 0;
+ do {
+ n = strchr(digits, ch) - digits;
+ INSIST(n >= 0 && n <= 9);
+ bits *= 10;
+ bits += n;
+ if (bits > 32)
+ goto enoent;
+ } while ((ch = *src++) != '\0' && isascii(ch) && isdigit(ch));
+ if (ch != '\0')
+ goto enoent;
+ }
+
+ /* Firey death and destruction unless we prefetched EOS. */
+ if (ch != '\0')
+ goto enoent;
+
+ /* If nothing was written to the destination, we found no address. */
+ if (dst == odst)
+ goto enoent;
+ /* If no CIDR spec was given, infer width from net class. */
+ if (bits == -1) {
+ if (*odst >= 240) /*%< Class E */
+ bits = 32;
+ else if (*odst >= 224) /*%< Class D */
+ bits = 8;
+ else if (*odst >= 192) /*%< Class C */
+ bits = 24;
+ else if (*odst >= 128) /*%< Class B */
+ bits = 16;
+ else /*%< Class A */
+ bits = 8;
+ /* If imputed mask is narrower than specified octets, widen. */
+ if (bits < ((dst - odst) * 8))
+ bits = (dst - odst) * 8;
+ /*
+ * If there are no additional bits specified for a class D
+ * address adjust bits to 4.
+ */
+ if (bits == 8 && *odst == 224)
+ bits = 4;
+ }
+ /* Extend network to cover the actual mask. */
+ while (bits > ((dst - odst) * 8)) {
+ if (size-- <= 0U)
+ goto emsgsize;
+ *dst++ = '\0';
+ }
+ return (bits);
+
+ enoent:
+ errno = ENOENT;
+ return (-1);
+
+ emsgsize:
+ errno = EMSGSIZE;
+ return (-1);
+}
+
+static int
+getbits(const char *src, int *bitsp) {
+ static const char digits[] = "0123456789";
+ int n;
+ int val;
+ char ch;
+
+ val = 0;
+ n = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ pch = strchr(digits, ch);
+ if (pch != NULL) {
+ if (n++ != 0 && val == 0) /*%< no leading zeros */
+ return (0);
+ val *= 10;
+ val += (pch - digits);
+ if (val > 128) /*%< range */
+ return (0);
+ continue;
+ }
+ return (0);
+ }
+ if (n == 0)
+ return (0);
+ *bitsp = val;
+ return (1);
+}
+
+static int
+getv4(const char *src, u_char *dst, int *bitsp) {
+ static const char digits[] = "0123456789";
+ u_char *odst = dst;
+ int n;
+ u_int val;
+ char ch;
+
+ val = 0;
+ n = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ pch = strchr(digits, ch);
+ if (pch != NULL) {
+ if (n++ != 0 && val == 0) /*%< no leading zeros */
+ return (0);
+ val *= 10;
+ val += (pch - digits);
+ if (val > 255) /*%< range */
+ return (0);
+ continue;
+ }
+ if (ch == '.' || ch == '/') {
+ if (dst - odst > 3) /*%< too many octets? */
+ return (0);
+ *dst++ = val;
+ if (ch == '/')
+ return (getbits(src, bitsp));
+ val = 0;
+ n = 0;
+ continue;
+ }
+ return (0);
+ }
+ if (n == 0)
+ return (0);
+ if (dst - odst > 3) /*%< too many octets? */
+ return (0);
+ *dst++ = val;
+ return (1);
+}
+
+static int
+inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) {
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, saw_xdigit;
+ u_int val;
+ int digits;
+ int bits;
+ size_t bytes;
+ int words;
+ int ipv4;
+
+ memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+ endp = tp + NS_IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ goto enoent;
+ curtok = src;
+ saw_xdigit = 0;
+ val = 0;
+ digits = 0;
+ bits = -1;
+ ipv4 = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (pch - xdigits);
+ if (++digits > 4)
+ goto enoent;
+ saw_xdigit = 1;
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!saw_xdigit) {
+ if (colonp)
+ goto enoent;
+ colonp = tp;
+ continue;
+ } else if (*src == '\0')
+ goto enoent;
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (u_char) (val >> 8) & 0xff;
+ *tp++ = (u_char) val & 0xff;
+ saw_xdigit = 0;
+ digits = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+ getv4(curtok, tp, &bits) > 0) {
+ tp += NS_INADDRSZ;
+ saw_xdigit = 0;
+ ipv4 = 1;
+ break; /*%< '\\0' was seen by inet_pton4(). */
+ }
+ if (ch == '/' && getbits(src, &bits) > 0)
+ break;
+ goto enoent;
+ }
+ if (saw_xdigit) {
+ if (tp + NS_INT16SZ > endp)
+ goto enoent;
+ *tp++ = (u_char) (val >> 8) & 0xff;
+ *tp++ = (u_char) val & 0xff;
+ }
+ if (bits == -1)
+ bits = 128;
+
+ words = (bits + 15) / 16;
+ if (words < 2)
+ words = 2;
+ if (ipv4)
+ words = 8;
+ endp = tmp + 2 * words;
+
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const int n = tp - colonp;
+ int i;
+
+ if (tp == endp)
+ goto enoent;
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ goto enoent;
+
+ bytes = (bits + 7) / 8;
+ if (bytes > size)
+ goto emsgsize;
+ memcpy(dst, tmp, bytes);
+ return (bits);
+
+ enoent:
+ errno = ENOENT;
+ return (-1);
+
+ emsgsize:
+ errno = EMSGSIZE;
+ return (-1);
+}
+
+/*%
+ * int
+ * inet_net_pton(af, src, dst, size)
+ * convert network number from presentation to network format.
+ * accepts hex octets, hex strings, decimal octets, and /CIDR.
+ * "size" is in bytes and describes "dst".
+ * return:
+ * number of bits, either imputed classfully or specified with /CIDR,
+ * or -1 if some failure occurred (check errno). ENOENT means it was
+ * not a valid network specification.
+ * author:
+ * Paul Vixie (ISC), June 1996
+ */
+int
+inet_net_pton(int af, const char *src, void *dst, size_t size) {
+ switch (af) {
+ case AF_INET:
+ return (inet_net_pton_ipv4(src, dst, size));
+ case AF_INET6:
+ return (inet_net_pton_ipv6(src, dst, size));
+ default:
+ errno = EAFNOSUPPORT;
+ return (-1);
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_neta.c b/usr/src/lib/libresolv2_joy/common/inet/inet_neta.c
new file mode 100644
index 0000000000..63a6c201a4
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/inet_neta.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char rcsid[] = "$Id: inet_neta.c,v 1.3 2005/04/27 04:56:20 sra Exp $";
+#endif
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/*%
+ * char *
+ * inet_neta(src, dst, size)
+ * format a u_long network number into presentation format.
+ * return:
+ * pointer to dst, or NULL if an error occurred (check errno).
+ * note:
+ * format of ``src'' is as for inet_network().
+ * author:
+ * Paul Vixie (ISC), July 1996
+ */
+char *
+inet_neta(src, dst, size)
+ u_long src;
+ char *dst;
+ size_t size;
+{
+ char *odst = dst;
+ char *tp;
+
+ while (src & 0xffffffff) {
+ u_char b = (src & 0xff000000) >> 24;
+
+ src <<= 8;
+ if (b) {
+ if (size < sizeof "255.")
+ goto emsgsize;
+ tp = dst;
+ dst += SPRINTF((dst, "%u", b));
+ if (src != 0L) {
+ *dst++ = '.';
+ *dst = '\0';
+ }
+ size -= (size_t)(dst - tp);
+ }
+ }
+ if (dst == odst) {
+ if (size < sizeof "0.0.0.0")
+ goto emsgsize;
+ strcpy(dst, "0.0.0.0");
+ }
+ return (odst);
+
+ emsgsize:
+ errno = EMSGSIZE;
+ return (NULL);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_netof.c b/usr/src/lib/libresolv2_joy/common/inet/inet_netof.c
new file mode 100644
index 0000000000..c228e3d818
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/inet_netof.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1983, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)inet_netof.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "port_after.h"
+
+/*%
+ * Return the network number from an internet
+ * address; handles class a/b/c network #'s.
+ */
+u_long
+inet_netof(in)
+ struct in_addr in;
+{
+ register u_long i = ntohl(in.s_addr);
+
+ if (IN_CLASSA(i))
+ return (((i)&IN_CLASSA_NET) >> IN_CLASSA_NSHIFT);
+ else if (IN_CLASSB(i))
+ return (((i)&IN_CLASSB_NET) >> IN_CLASSB_NSHIFT);
+ else
+ return (((i)&IN_CLASSC_NET) >> IN_CLASSC_NSHIFT);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/inet_network.c b/usr/src/lib/libresolv2_joy/common/inet/inet_network.c
new file mode 100644
index 0000000000..47976cff68
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/inet_network.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1983, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char sccsid[] = "@(#)inet_network.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+#include "port_after.h"
+
+/*%
+ * Internet network address interpretation routine.
+ * The library routines call this routine to interpret
+ * network numbers.
+ */
+u_long
+inet_network(cp)
+ register const char *cp;
+{
+ register u_long val, base, n, i;
+ register char c;
+ u_long parts[4], *pp = parts;
+ int digit;
+
+again:
+ val = 0; base = 10; digit = 0;
+ if (*cp == '0')
+ digit = 1, base = 8, cp++;
+ if (*cp == 'x' || *cp == 'X')
+ base = 16, cp++;
+ while ((c = *cp) != 0) {
+ if (isdigit((unsigned char)c)) {
+ if (base == 8U && (c == '8' || c == '9'))
+ return (INADDR_NONE);
+ val = (val * base) + (c - '0');
+ cp++;
+ digit = 1;
+ continue;
+ }
+ if (base == 16U && isxdigit((unsigned char)c)) {
+ val = (val << 4) +
+ (c + 10 - (islower((unsigned char)c) ? 'a' : 'A'));
+ cp++;
+ digit = 1;
+ continue;
+ }
+ break;
+ }
+ if (!digit)
+ return (INADDR_NONE);
+ if (pp >= parts + 4 || val > 0xffU)
+ return (INADDR_NONE);
+ if (*cp == '.') {
+ *pp++ = val, cp++;
+ goto again;
+ }
+ if (*cp && !isspace(*cp&0xff))
+ return (INADDR_NONE);
+ *pp++ = val;
+ n = pp - parts;
+ if (n > 4U)
+ return (INADDR_NONE);
+ for (val = 0, i = 0; i < n; i++) {
+ val <<= 8;
+ val |= parts[i] & 0xff;
+ }
+ return (val);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/inet/nsap_addr.c b/usr/src/lib/libresolv2_joy/common/inet/nsap_addr.c
new file mode 100644
index 0000000000..a9972e6e32
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/inet/nsap_addr.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static const char rcsid[] = "$Id: nsap_addr.c,v 1.5 2005/07/28 06:51:48 marka Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <resolv_joy.h>
+#include <resolv_mt.h>
+
+#include "port_after.h"
+
+static char
+xtob(int c) {
+ return (c - (((c >= '0') && (c <= '9')) ? '0' : '7'));
+}
+
+u_int
+inet_nsap_addr(const char *ascii, u_char *binary, int maxlen) {
+ u_char c, nib;
+ u_int len = 0;
+
+ if (ascii[0] != '0' || (ascii[1] != 'x' && ascii[1] != 'X'))
+ return (0);
+ ascii += 2;
+
+ while ((c = *ascii++) != '\0' && len < (u_int)maxlen) {
+ if (c == '.' || c == '+' || c == '/')
+ continue;
+ if (!isascii(c))
+ return (0);
+ if (islower(c))
+ c = toupper(c);
+ if (isxdigit(c)) {
+ nib = xtob(c);
+ c = *ascii++;
+ if (c != '\0') {
+ c = toupper(c);
+ if (isxdigit(c)) {
+ *binary++ = (nib << 4) | xtob(c);
+ len++;
+ } else
+ return (0);
+ }
+ else
+ return (0);
+ }
+ else
+ return (0);
+ }
+ return (len);
+}
+
+char *
+inet_nsap_ntoa(int binlen, const u_char *binary, char *ascii) {
+ int nib;
+ int i;
+ char *tmpbuf = inet_nsap_ntoa_tmpbuf;
+ char *start;
+
+ if (ascii)
+ start = ascii;
+ else {
+ ascii = tmpbuf;
+ start = tmpbuf;
+ }
+
+ *ascii++ = '0';
+ *ascii++ = 'x';
+
+ if (binlen > 255)
+ binlen = 255;
+
+ for (i = 0; i < binlen; i++) {
+ nib = *binary >> 4;
+ *ascii++ = nib + (nib < 10 ? '0' : '7');
+ nib = *binary++ & 0x0f;
+ *ascii++ = nib + (nib < 10 ? '0' : '7');
+ if (((i % 2) == 0 && (i + 1) < binlen))
+ *ascii++ = '.';
+ }
+ *ascii = '\0';
+ return (start);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns.c b/usr/src/lib/libresolv2_joy/common/irs/dns.c
new file mode 100644
index 0000000000..119308074e
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/dns.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*! \file
+ * \brief
+ * dns.c --- this is the top-level accessor function for the dns
+ */
+
+#include "port_before.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <resolv_joy.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "hesiod.h"
+#include "dns_p.h"
+
+/* forward */
+
+static void dns_close(struct irs_acc *);
+static struct __res_state * dns_res_get(struct irs_acc *);
+static void dns_res_set(struct irs_acc *, struct __res_state *,
+ void (*)(void *));
+
+/* public */
+
+struct irs_acc *
+irs_dns_acc(const char *options) {
+ struct irs_acc *acc;
+ struct dns_p *dns;
+
+ UNUSED(options);
+
+ if (!(acc = memget(sizeof *acc))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(acc, 0x5e, sizeof *acc);
+ if (!(dns = memget(sizeof *dns))) {
+ errno = ENOMEM;
+ memput(acc, sizeof *acc);
+ return (NULL);
+ }
+ memset(dns, 0x5e, sizeof *dns);
+ dns->res = NULL;
+ dns->free_res = NULL;
+ if (hesiod_init(&dns->hes_ctx) < 0) {
+ /*
+ * We allow the dns accessor class to initialize
+ * despite hesiod failing to initialize correctly,
+ * since dns host queries don't depend on hesiod.
+ */
+ dns->hes_ctx = NULL;
+ }
+ acc->private = dns;
+#ifdef WANT_IRS_GR
+ acc->gr_map = irs_dns_gr;
+#else
+ acc->gr_map = NULL;
+#endif
+#ifdef WANT_IRS_PW
+ acc->pw_map = irs_dns_pw;
+#else
+ acc->pw_map = NULL;
+#endif
+ acc->sv_map = irs_dns_sv;
+ acc->pr_map = irs_dns_pr;
+ acc->ho_map = irs_dns_ho;
+ acc->nw_map = irs_dns_nw;
+ acc->ng_map = irs_nul_ng;
+ acc->res_get = dns_res_get;
+ acc->res_set = dns_res_set;
+ acc->close = dns_close;
+ return (acc);
+}
+
+/* methods */
+static struct __res_state *
+dns_res_get(struct irs_acc *this) {
+ struct dns_p *dns = (struct dns_p *)this->private;
+
+ if (dns->res == NULL) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (res == NULL)
+ return (NULL);
+ memset(res, 0, sizeof *res);
+ dns_res_set(this, res, free);
+ }
+
+ if ((dns->res->options & RES_INIT) == 0U &&
+ res_ninit(dns->res) < 0)
+ return (NULL);
+
+ return (dns->res);
+}
+
+static void
+dns_res_set(struct irs_acc *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct dns_p *dns = (struct dns_p *)this->private;
+
+ if (dns->res && dns->free_res) {
+ res_nclose(dns->res);
+ (*dns->free_res)(dns->res);
+ }
+ dns->res = res;
+ dns->free_res = free_res;
+}
+
+static void
+dns_close(struct irs_acc *this) {
+ struct dns_p *dns;
+
+ dns = (struct dns_p *)this->private;
+ if (dns->res && dns->free_res)
+ (*dns->free_res)(dns->res);
+ if (dns->hes_ctx)
+ hesiod_end(dns->hes_ctx);
+ memput(dns, sizeof *dns);
+ memput(this, sizeof *this);
+}
+
diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns_ho.c b/usr/src/lib/libresolv2_joy/common/irs/dns_ho.c
new file mode 100644
index 0000000000..b47e26f21a
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/dns_ho.c
@@ -0,0 +1,1139 @@
+/*
+ * Portions Copyright (C) 2004-2006, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1996-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 1985, 1988, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+/* from gethostnamadr.c 8.1 (Berkeley) 6/4/93 */
+/* BIND Id: gethnamaddr.c,v 8.15 1996/05/22 04:56:30 vixie Exp $ */
+
+/* Imports. */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "dns_p.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) sprintf x
+#endif
+
+/* Definitions. */
+
+#define MAXALIASES 35
+#define MAXADDRS 35
+
+#define MAXPACKET (65535) /*%< Maximum TCP message size */
+#define BOUNDS_CHECK(ptr, count) \
+ if ((ptr) + (count) > eom) { \
+ had_error++; \
+ continue; \
+ } else (void)0
+
+typedef union {
+ HEADER hdr;
+ u_char buf[MAXPACKET];
+} querybuf;
+
+struct dns_res_target {
+ struct dns_res_target *next;
+ querybuf qbuf; /*%< query buffer */
+ u_char *answer; /*%< buffer to put answer */
+ int anslen; /*%< size of answer buffer */
+ int qclass, qtype; /*%< class and type of query */
+ int action; /*%< condition whether query is really issued */
+ char qname[MAXDNAME +1]; /*%< domain name */
+#if 0
+ int n; /*%< result length */
+#endif
+};
+enum {RESTGT_DOALWAYS, RESTGT_AFTERFAILURE, RESTGT_IGNORE};
+enum {RESQRY_SUCCESS, RESQRY_FAIL};
+
+struct pvt {
+ struct hostent host;
+ char * h_addr_ptrs[MAXADDRS + 1];
+ char * host_aliases[MAXALIASES];
+ char hostbuf[8*1024];
+ u_char host_addr[16]; /*%< IPv4 or IPv6 */
+ struct __res_state *res;
+ void (*free_res)(void *);
+};
+
+typedef union {
+ int32_t al;
+ char ac;
+} align;
+
+static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
+static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
+/* Note: the IPv6 loopback address is in the "tunnel" space */
+static const u_char v6local[] = { 0,0, 0,1 }; /*%< last 4 bytes of IPv6 addr */
+/* Forwards. */
+
+static void ho_close(struct irs_ho *this);
+static struct hostent * ho_byname(struct irs_ho *this, const char *name);
+static struct hostent * ho_byname2(struct irs_ho *this, const char *name,
+ int af);
+static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr,
+ int len, int af);
+static struct hostent * ho_next(struct irs_ho *this);
+static void ho_rewind(struct irs_ho *this);
+static void ho_minimize(struct irs_ho *this);
+static struct __res_state * ho_res_get(struct irs_ho *this);
+static void ho_res_set(struct irs_ho *this,
+ struct __res_state *res,
+ void (*free_res)(void *));
+static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name,
+ const struct addrinfo *pai);
+
+static void map_v4v6_hostent(struct hostent *hp, char **bp,
+ char *ep);
+static void addrsort(res_state, char **, int);
+static struct hostent * gethostans(struct irs_ho *this,
+ const u_char *ansbuf, int anslen,
+ const char *qname, int qtype,
+ int af, int size,
+ struct addrinfo **ret_aip,
+ const struct addrinfo *pai);
+static int add_hostent(struct pvt *pvt, char *bp, char **hap,
+ struct addrinfo *ai);
+static int init(struct irs_ho *this);
+
+/* Exports. */
+
+struct irs_ho *
+irs_dns_ho(struct irs_acc *this) {
+ struct irs_ho *ho;
+ struct pvt *pvt;
+
+ UNUSED(this);
+
+ if (!(pvt = memget(sizeof *pvt))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+
+ if (!(ho = memget(sizeof *ho))) {
+ memput(pvt, sizeof *pvt);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(ho, 0x5e, sizeof *ho);
+ ho->private = pvt;
+ ho->close = ho_close;
+ ho->byname = ho_byname;
+ ho->byname2 = ho_byname2;
+ ho->byaddr = ho_byaddr;
+ ho->next = ho_next;
+ ho->rewind = ho_rewind;
+ ho->minimize = ho_minimize;
+ ho->res_get = ho_res_get;
+ ho->res_set = ho_res_set;
+ ho->addrinfo = ho_addrinfo;
+ return (ho);
+}
+
+/* Methods. */
+
+static void
+ho_close(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ ho_minimize(this);
+ if (pvt->res && pvt->free_res)
+ (*pvt->free_res)(pvt->res);
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct hostent *
+ho_byname(struct irs_ho *this, const char *name) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct hostent *hp;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ if (pvt->res->options & RES_USE_INET6) {
+ hp = ho_byname2(this, name, AF_INET6);
+ if (hp)
+ return (hp);
+ }
+ return (ho_byname2(this, name, AF_INET));
+}
+
+static struct hostent *
+ho_byname2(struct irs_ho *this, const char *name, int af)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct hostent *hp = NULL;
+ int n, size;
+ char tmp[NS_MAXDNAME];
+ const char *cp;
+ struct addrinfo ai;
+ struct dns_res_target *q, *p;
+ int querystate = RESQRY_FAIL;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ q = memget(sizeof(*q));
+ if (q == NULL) {
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ memset(q, 0, sizeof(*q));
+
+ switch (af) {
+ case AF_INET:
+ size = INADDRSZ;
+ q->qclass = C_IN;
+ q->qtype = T_A;
+ q->answer = q->qbuf.buf;
+ q->anslen = sizeof(q->qbuf);
+ q->action = RESTGT_DOALWAYS;
+ break;
+ case AF_INET6:
+ size = IN6ADDRSZ;
+ q->qclass = C_IN;
+ q->qtype = T_AAAA;
+ q->answer = q->qbuf.buf;
+ q->anslen = sizeof(q->qbuf);
+ q->action = RESTGT_DOALWAYS;
+ break;
+ default:
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ errno = EAFNOSUPPORT;
+ hp = NULL;
+ goto cleanup;
+ }
+
+ /*
+ * if there aren't any dots, it could be a user-level alias.
+ * this is also done in res_nquery() since we are not the only
+ * function that looks up host names.
+ */
+ if (!strchr(name, '.') && (cp = res_hostalias(pvt->res, name,
+ tmp, sizeof tmp)))
+ name = cp;
+
+ for (p = q; p; p = p->next) {
+ switch(p->action) {
+ case RESTGT_DOALWAYS:
+ break;
+ case RESTGT_AFTERFAILURE:
+ if (querystate == RESQRY_SUCCESS)
+ continue;
+ break;
+ case RESTGT_IGNORE:
+ continue;
+ }
+
+ if ((n = res_nsearch(pvt->res, name, p->qclass, p->qtype,
+ p->answer, p->anslen)) < 0) {
+ querystate = RESQRY_FAIL;
+ continue;
+ }
+
+ memset(&ai, 0, sizeof(ai));
+ ai.ai_family = af;
+ if ((hp = gethostans(this, p->answer, n, name, p->qtype,
+ af, size, NULL,
+ (const struct addrinfo *)&ai)) != NULL)
+ goto cleanup; /*%< no more loop is necessary */
+ querystate = RESQRY_FAIL;
+ continue;
+ }
+
+ cleanup:
+ if (q != NULL)
+ memput(q, sizeof(*q));
+ return(hp);
+}
+
+static struct hostent *
+ho_byaddr(struct irs_ho *this, const void *addr, int len, int af)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ const u_char *uaddr = addr;
+ char *qp;
+ struct hostent *hp = NULL;
+ struct addrinfo ai;
+ struct dns_res_target *q, *q2, *p;
+ int n, size, i;
+ int querystate = RESQRY_FAIL;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ q = memget(sizeof(*q));
+ q2 = memget(sizeof(*q2));
+ if (q == NULL || q2 == NULL) {
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ memset(q, 0, sizeof(*q));
+ memset(q2, 0, sizeof(*q2));
+
+ if (af == AF_INET6 && len == IN6ADDRSZ &&
+ (!memcmp(uaddr, mapped, sizeof mapped) ||
+ (!memcmp(uaddr, tunnelled, sizeof tunnelled) &&
+ memcmp(&uaddr[sizeof tunnelled], v6local, sizeof(v6local))))) {
+ /* Unmap. */
+ addr = (const char *)addr + sizeof mapped;
+ uaddr += sizeof mapped;
+ af = AF_INET;
+ len = INADDRSZ;
+ }
+ switch (af) {
+ case AF_INET:
+ size = INADDRSZ;
+ q->qclass = C_IN;
+ q->qtype = T_PTR;
+ q->answer = q->qbuf.buf;
+ q->anslen = sizeof(q->qbuf);
+ q->action = RESTGT_DOALWAYS;
+ break;
+ case AF_INET6:
+ size = IN6ADDRSZ;
+ q->qclass = C_IN;
+ q->qtype = T_PTR;
+ q->answer = q->qbuf.buf;
+ q->anslen = sizeof(q->qbuf);
+ q->next = q2;
+ q->action = RESTGT_DOALWAYS;
+ q2->qclass = C_IN;
+ q2->qtype = T_PTR;
+ q2->answer = q2->qbuf.buf;
+ q2->anslen = sizeof(q2->qbuf);
+ if ((pvt->res->options & RES_NO_NIBBLE2) != 0U)
+ q2->action = RESTGT_IGNORE;
+ else
+ q2->action = RESTGT_AFTERFAILURE;
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ hp = NULL;
+ goto cleanup;
+ }
+ if (size > len) {
+ errno = EINVAL;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ hp = NULL;
+ goto cleanup;
+ }
+ switch (af) {
+ case AF_INET:
+ qp = q->qname;
+ (void) sprintf(qp, "%u.%u.%u.%u.in-addr.arpa",
+ (uaddr[3] & 0xff),
+ (uaddr[2] & 0xff),
+ (uaddr[1] & 0xff),
+ (uaddr[0] & 0xff));
+ break;
+ case AF_INET6:
+ if (q->action != RESTGT_IGNORE) {
+ const char *nibsuff = res_get_nibblesuffix(pvt->res);
+ qp = q->qname;
+ for (n = IN6ADDRSZ - 1; n >= 0; n--) {
+ i = SPRINTF((qp, "%x.%x.",
+ uaddr[n] & 0xf,
+ (uaddr[n] >> 4) & 0xf));
+ if (i != 4)
+ abort();
+ qp += i;
+ }
+ if (strlen(q->qname) + strlen(nibsuff) + 1 >
+ sizeof q->qname) {
+ errno = ENAMETOOLONG;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ hp = NULL;
+ goto cleanup;
+ }
+ strcpy(qp, nibsuff); /* (checked) */
+ }
+ if (q2->action != RESTGT_IGNORE) {
+ const char *nibsuff2 = res_get_nibblesuffix2(pvt->res);
+ qp = q2->qname;
+ for (n = IN6ADDRSZ - 1; n >= 0; n--) {
+ i = SPRINTF((qp, "%x.%x.",
+ uaddr[n] & 0xf,
+ (uaddr[n] >> 4) & 0xf));
+ if (i != 4)
+ abort();
+ qp += i;
+ }
+ if (strlen(q2->qname) + strlen(nibsuff2) + 1 >
+ sizeof q2->qname) {
+ errno = ENAMETOOLONG;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ hp = NULL;
+ goto cleanup;
+ }
+ strcpy(qp, nibsuff2); /* (checked) */
+ }
+ break;
+ default:
+ abort();
+ }
+
+ for (p = q; p; p = p->next) {
+ switch(p->action) {
+ case RESTGT_DOALWAYS:
+ break;
+ case RESTGT_AFTERFAILURE:
+ if (querystate == RESQRY_SUCCESS)
+ continue;
+ break;
+ case RESTGT_IGNORE:
+ continue;
+ }
+
+ if ((n = res_nquery(pvt->res, p->qname, p->qclass, p->qtype,
+ p->answer, p->anslen)) < 0) {
+ querystate = RESQRY_FAIL;
+ continue;
+ }
+
+ memset(&ai, 0, sizeof(ai));
+ ai.ai_family = af;
+ hp = gethostans(this, p->answer, n, p->qname, T_PTR, af, size,
+ NULL, (const struct addrinfo *)&ai);
+ if (!hp) {
+ querystate = RESQRY_FAIL;
+ continue;
+ }
+
+ memcpy(pvt->host_addr, addr, len);
+ pvt->h_addr_ptrs[0] = (char *)pvt->host_addr;
+ pvt->h_addr_ptrs[1] = NULL;
+ if (af == AF_INET && (pvt->res->options & RES_USE_INET6)) {
+ map_v4v6_address((char*)pvt->host_addr,
+ (char*)pvt->host_addr);
+ pvt->host.h_addrtype = AF_INET6;
+ pvt->host.h_length = IN6ADDRSZ;
+ }
+
+ RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS);
+ goto cleanup; /*%< no more loop is necessary. */
+ }
+ hp = NULL; /*%< H_ERRNO was set by subroutines */
+ cleanup:
+ if (q != NULL)
+ memput(q, sizeof(*q));
+ if (q2 != NULL)
+ memput(q2, sizeof(*q2));
+ return(hp);
+}
+
+static struct hostent *
+ho_next(struct irs_ho *this) {
+
+ UNUSED(this);
+
+ return (NULL);
+}
+
+static void
+ho_rewind(struct irs_ho *this) {
+
+ UNUSED(this);
+
+ /* NOOP */
+}
+
+static void
+ho_minimize(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->res)
+ res_nclose(pvt->res);
+}
+
+static struct __res_state *
+ho_res_get(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (!res) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(res, 0, sizeof *res);
+ ho_res_set(this, res, free);
+ }
+
+ return (pvt->res);
+}
+
+/* XXX */
+extern struct addrinfo *addr2addrinfo __P((const struct addrinfo *,
+ const char *));
+
+static struct addrinfo *
+ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ int n;
+ char tmp[NS_MAXDNAME];
+ const char *cp;
+ struct dns_res_target *q, *q2, *p;
+ struct addrinfo sentinel, *cur;
+ int querystate = RESQRY_FAIL;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ memset(&sentinel, 0, sizeof(sentinel));
+ cur = &sentinel;
+
+ q = memget(sizeof(*q));
+ q2 = memget(sizeof(*q2));
+ if (q == NULL || q2 == NULL) {
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ memset(q, 0, sizeof(*q2));
+ memset(q2, 0, sizeof(*q2));
+
+ switch (pai->ai_family) {
+ case AF_UNSPEC:
+ /* prefer IPv6 */
+ q->qclass = C_IN;
+ q->qtype = T_AAAA;
+ q->answer = q->qbuf.buf;
+ q->anslen = sizeof(q->qbuf);
+ q->next = q2;
+ q->action = RESTGT_DOALWAYS;
+ q2->qclass = C_IN;
+ q2->qtype = T_A;
+ q2->answer = q2->qbuf.buf;
+ q2->anslen = sizeof(q2->qbuf);
+ q2->action = RESTGT_DOALWAYS;
+ break;
+ case AF_INET:
+ q->qclass = C_IN;
+ q->qtype = T_A;
+ q->answer = q->qbuf.buf;
+ q->anslen = sizeof(q->qbuf);
+ q->action = RESTGT_DOALWAYS;
+ break;
+ case AF_INET6:
+ q->qclass = C_IN;
+ q->qtype = T_AAAA;
+ q->answer = q->qbuf.buf;
+ q->anslen = sizeof(q->qbuf);
+ q->action = RESTGT_DOALWAYS;
+ break;
+ default:
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); /*%< better error? */
+ goto cleanup;
+ }
+
+ /*
+ * if there aren't any dots, it could be a user-level alias.
+ * this is also done in res_nquery() since we are not the only
+ * function that looks up host names.
+ */
+ if (!strchr(name, '.') && (cp = res_hostalias(pvt->res, name,
+ tmp, sizeof tmp)))
+ name = cp;
+
+ for (p = q; p; p = p->next) {
+ struct addrinfo *ai;
+
+ switch(p->action) {
+ case RESTGT_DOALWAYS:
+ break;
+ case RESTGT_AFTERFAILURE:
+ if (querystate == RESQRY_SUCCESS)
+ continue;
+ break;
+ case RESTGT_IGNORE:
+ continue;
+ }
+
+ if ((n = res_nsearch(pvt->res, name, p->qclass, p->qtype,
+ p->answer, p->anslen)) < 0) {
+ querystate = RESQRY_FAIL;
+ continue;
+ }
+ (void)gethostans(this, p->answer, n, name, p->qtype,
+ pai->ai_family, /*%< XXX: meaningless */
+ 0, &ai, pai);
+ if (ai) {
+ querystate = RESQRY_SUCCESS;
+ cur->ai_next = ai;
+ while (cur->ai_next)
+ cur = cur->ai_next;
+ } else
+ querystate = RESQRY_FAIL;
+ }
+
+ cleanup:
+ if (q != NULL)
+ memput(q, sizeof(*q));
+ if (q2 != NULL)
+ memput(q2, sizeof(*q2));
+ return(sentinel.ai_next);
+}
+
+static void
+ho_res_set(struct irs_ho *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->res && pvt->free_res) {
+ res_nclose(pvt->res);
+ (*pvt->free_res)(pvt->res);
+ }
+
+ pvt->res = res;
+ pvt->free_res = free_res;
+}
+
+/* Private. */
+
+static struct hostent *
+gethostans(struct irs_ho *this,
+ const u_char *ansbuf, int anslen, const char *qname, int qtype,
+ int af, int size, /*!< meaningless for addrinfo cases */
+ struct addrinfo **ret_aip, const struct addrinfo *pai)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ int type, class, ancount, qdcount, n, haveanswer, had_error;
+ int error = NETDB_SUCCESS;
+ int (*name_ok)(const char *);
+ const HEADER *hp;
+ const u_char *eom;
+ const u_char *eor;
+ const u_char *cp;
+ const char *tname;
+ const char *hname;
+ char *bp, *ep, **ap, **hap;
+ char tbuf[MAXDNAME+1];
+ struct addrinfo sentinel, *cur, ai;
+
+ if (pai == NULL) abort();
+ if (ret_aip != NULL)
+ *ret_aip = NULL;
+ memset(&sentinel, 0, sizeof(sentinel));
+ cur = &sentinel;
+
+ tname = qname;
+ eom = ansbuf + anslen;
+ switch (qtype) {
+ case T_A:
+ case T_AAAA:
+ case T_ANY: /*%< use T_ANY only for T_A/T_AAAA lookup */
+ name_ok = res_hnok;
+ break;
+ case T_PTR:
+ name_ok = res_dnok;
+ break;
+ default:
+ abort();
+ }
+
+ pvt->host.h_addrtype = af;
+ pvt->host.h_length = size;
+ hname = pvt->host.h_name = NULL;
+
+ /*
+ * Find first satisfactory answer.
+ */
+ if (ansbuf + HFIXEDSZ > eom) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ hp = (const HEADER *)ansbuf;
+ ancount = ntohs(hp->ancount);
+ qdcount = ntohs(hp->qdcount);
+ bp = pvt->hostbuf;
+ ep = pvt->hostbuf + sizeof(pvt->hostbuf);
+ cp = ansbuf + HFIXEDSZ;
+ if (qdcount != 1) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ n = dn_expand(ansbuf, eom, cp, bp, ep - bp);
+ if (n < 0 || !maybe_ok(pvt->res, bp, name_ok)) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ cp += n + QFIXEDSZ;
+ if (cp > eom) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) {
+ /* res_nsend() has already verified that the query name is the
+ * same as the one we sent; this just gets the expanded name
+ * (i.e., with the succeeding search-domain tacked on).
+ */
+ n = strlen(bp) + 1; /*%< for the \\0 */
+ if (n > MAXHOSTNAMELEN) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ pvt->host.h_name = bp;
+ hname = bp;
+ bp += n;
+ /* The qname can be abbreviated, but hname is now absolute. */
+ qname = pvt->host.h_name;
+ }
+ ap = pvt->host_aliases;
+ *ap = NULL;
+ pvt->host.h_aliases = pvt->host_aliases;
+ hap = pvt->h_addr_ptrs;
+ *hap = NULL;
+ pvt->host.h_addr_list = pvt->h_addr_ptrs;
+ haveanswer = 0;
+ had_error = 0;
+ while (ancount-- > 0 && cp < eom && !had_error) {
+ n = dn_expand(ansbuf, eom, cp, bp, ep - bp);
+ if (n < 0 || !maybe_ok(pvt->res, bp, name_ok)) {
+ had_error++;
+ continue;
+ }
+ cp += n; /*%< name */
+ BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ);
+ type = ns_get16(cp);
+ cp += INT16SZ; /*%< type */
+ class = ns_get16(cp);
+ cp += INT16SZ + INT32SZ; /*%< class, TTL */
+ n = ns_get16(cp);
+ cp += INT16SZ; /*%< len */
+ BOUNDS_CHECK(cp, n);
+ if (class != C_IN) {
+ cp += n;
+ continue;
+ }
+ eor = cp + n;
+ if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) &&
+ type == T_CNAME) {
+ if (haveanswer) {
+ int level = LOG_CRIT;
+#ifdef LOG_SECURITY
+ level |= LOG_SECURITY;
+#endif
+ syslog(level,
+ "gethostans: possible attempt to exploit buffer overflow while looking up %s",
+ *qname ? qname : ".");
+ }
+ n = dn_expand(ansbuf, eor, cp, tbuf, sizeof tbuf);
+ if (n < 0 || !maybe_ok(pvt->res, tbuf, name_ok)) {
+ had_error++;
+ continue;
+ }
+ cp += n;
+ /* Store alias. */
+ if (ap >= &pvt->host_aliases[MAXALIASES-1])
+ continue;
+ *ap++ = bp;
+ n = strlen(bp) + 1; /*%< for the \\0 */
+ bp += n;
+ /* Get canonical name. */
+ n = strlen(tbuf) + 1; /*%< for the \\0 */
+ if (n > (ep - bp) || n > MAXHOSTNAMELEN) {
+ had_error++;
+ continue;
+ }
+ strcpy(bp, tbuf); /* (checked) */
+ pvt->host.h_name = bp;
+ hname = bp;
+ bp += n;
+ continue;
+ }
+ if (qtype == T_PTR && type == T_CNAME) {
+ n = dn_expand(ansbuf, eor, cp, tbuf, sizeof tbuf);
+ if (n < 0 || !maybe_dnok(pvt->res, tbuf)) {
+ had_error++;
+ continue;
+ }
+ cp += n;
+#ifdef RES_USE_DNAME
+ if ((pvt->res->options & RES_USE_DNAME) != 0U)
+#endif
+ {
+ /*
+ * We may be able to check this regardless
+ * of the USE_DNAME bit, but we add the check
+ * for now since the DNAME support is
+ * experimental.
+ */
+ if (ns_samename(tname, bp) != 1)
+ continue;
+ }
+ /* Get canonical name. */
+ n = strlen(tbuf) + 1; /*%< for the \\0 */
+ if (n > (ep - bp)) {
+ had_error++;
+ continue;
+ }
+ strcpy(bp, tbuf); /* (checked) */
+ tname = bp;
+ bp += n;
+ continue;
+ }
+ if (qtype == T_ANY) {
+ if (!(type == T_A || type == T_AAAA)) {
+ cp += n;
+ continue;
+ }
+ } else if (type != qtype) {
+ cp += n;
+ continue;
+ }
+ switch (type) {
+ case T_PTR:
+ if (ret_aip != NULL) {
+ /* addrinfo never needs T_PTR */
+ cp += n;
+ continue;
+ }
+ if (ns_samename(tname, bp) != 1) {
+ cp += n;
+ continue;
+ }
+ n = dn_expand(ansbuf, eor, cp, bp, ep - bp);
+ if (n < 0 || !maybe_hnok(pvt->res, bp) ||
+ n >= MAXHOSTNAMELEN) {
+ had_error++;
+ break;
+ }
+ cp += n;
+ if (!haveanswer) {
+ pvt->host.h_name = bp;
+ hname = bp;
+ }
+ else if (ap < &pvt->host_aliases[MAXALIASES-1])
+ *ap++ = bp;
+ else
+ n = -1;
+ if (n != -1) {
+ n = strlen(bp) + 1; /*%< for the \\0 */
+ bp += n;
+ }
+ break;
+ case T_A:
+ case T_AAAA:
+ if (ns_samename(hname, bp) != 1) {
+ cp += n;
+ continue;
+ }
+ if (type == T_A && n != INADDRSZ) {
+ cp += n;
+ continue;
+ }
+ if (type == T_AAAA && n != IN6ADDRSZ) {
+ cp += n;
+ continue;
+ }
+
+ /* make addrinfo. don't overwrite constant PAI */
+ ai = *pai;
+ ai.ai_family = (type == T_AAAA) ? AF_INET6 : AF_INET;
+ cur->ai_next = addr2addrinfo(
+ (const struct addrinfo *)&ai,
+ (const char *)cp);
+ if (cur->ai_next == NULL)
+ had_error++;
+
+ if (!haveanswer) {
+ int nn;
+
+ nn = strlen(bp) + 1; /*%< for the \\0 */
+ if (nn >= MAXHOSTNAMELEN) {
+ cp += n;
+ had_error++;
+ continue;
+ }
+ pvt->host.h_name = bp;
+ hname = bp;
+ bp += nn;
+ }
+ /* Ensure alignment. */
+ bp = (char *)(((u_long)bp + (sizeof(align) - 1)) &
+ ~(sizeof(align) - 1));
+ /* Avoid overflows. */
+ if (bp + n > &pvt->hostbuf[sizeof(pvt->hostbuf) - 1]) {
+ had_error++;
+ continue;
+ }
+ if (ret_aip) { /*%< need addrinfo. keep it. */
+ while (cur->ai_next)
+ cur = cur->ai_next;
+ } else if (cur->ai_next) { /*%< need hostent */
+ struct addrinfo *aip = cur->ai_next;
+
+ for (aip = cur->ai_next; aip;
+ aip = aip->ai_next) {
+ int m;
+
+ m = add_hostent(pvt, bp, hap, aip);
+ if (m < 0) {
+ had_error++;
+ break;
+ }
+ if (m == 0)
+ continue;
+ if (hap < &pvt->h_addr_ptrs[MAXADDRS])
+ hap++;
+ *hap = NULL;
+ bp += m;
+ }
+
+ freeaddrinfo(cur->ai_next);
+ cur->ai_next = NULL;
+ }
+ cp += n;
+ break;
+ default:
+ abort();
+ }
+ if (!had_error)
+ haveanswer++;
+ }
+ if (haveanswer) {
+ if (ret_aip == NULL) {
+ *ap = NULL;
+ *hap = NULL;
+
+ if (pvt->res->nsort && hap != pvt->h_addr_ptrs &&
+ qtype == T_A)
+ addrsort(pvt->res, pvt->h_addr_ptrs,
+ hap - pvt->h_addr_ptrs);
+ if (pvt->host.h_name == NULL) {
+ n = strlen(qname) + 1; /*%< for the \\0 */
+ if (n > (ep - bp) || n >= MAXHOSTNAMELEN)
+ goto no_recovery;
+ strcpy(bp, qname); /* (checked) */
+ pvt->host.h_name = bp;
+ bp += n;
+ }
+ if (pvt->res->options & RES_USE_INET6)
+ map_v4v6_hostent(&pvt->host, &bp, ep);
+ RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS);
+ return (&pvt->host);
+ } else {
+ if ((pai->ai_flags & AI_CANONNAME) != 0) {
+ if (pvt->host.h_name == NULL) {
+ sentinel.ai_next->ai_canonname =
+ strdup(qname);
+ }
+ else {
+ sentinel.ai_next->ai_canonname =
+ strdup(pvt->host.h_name);
+ }
+ }
+ *ret_aip = sentinel.ai_next;
+ return(NULL);
+ }
+ }
+ no_recovery:
+ if (sentinel.ai_next) {
+ /* this should be impossible, but check it for safety */
+ freeaddrinfo(sentinel.ai_next);
+ }
+ if (error == NETDB_SUCCESS)
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ else
+ RES_SET_H_ERRNO(pvt->res, error);
+ return(NULL);
+}
+
+static int
+add_hostent(struct pvt *pvt, char *bp, char **hap, struct addrinfo *ai)
+{
+ int addrlen;
+ char *addrp;
+ const char **tap;
+ char *obp = bp;
+
+ switch(ai->ai_addr->sa_family) {
+ case AF_INET6:
+ addrlen = IN6ADDRSZ;
+ addrp = (char *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
+ break;
+ case AF_INET:
+ addrlen = INADDRSZ;
+ addrp = (char *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr;
+ break;
+ default:
+ return(-1); /*%< abort? */
+ }
+
+ /* Ensure alignment. */
+ bp = (char *)(((u_long)bp + (sizeof(align) - 1)) &
+ ~(sizeof(align) - 1));
+ /* Avoid overflows. */
+ if (bp + addrlen > &pvt->hostbuf[sizeof(pvt->hostbuf) - 1])
+ return(-1);
+ if (hap >= &pvt->h_addr_ptrs[MAXADDRS])
+ return(0); /*%< fail, but not treat it as an error. */
+ /* Suppress duplicates. */
+ for (tap = (const char **)pvt->h_addr_ptrs;
+ *tap != NULL;
+ tap++)
+ if (memcmp(*tap, addrp, addrlen) == 0)
+ break;
+ if (*tap != NULL)
+ return (0);
+
+ memcpy(*hap = bp, addrp, addrlen);
+ return((bp + addrlen) - obp);
+}
+
+static void
+map_v4v6_hostent(struct hostent *hp, char **bpp, char *ep) {
+ char **ap;
+
+ if (hp->h_addrtype != AF_INET || hp->h_length != INADDRSZ)
+ return;
+ hp->h_addrtype = AF_INET6;
+ hp->h_length = IN6ADDRSZ;
+ for (ap = hp->h_addr_list; *ap; ap++) {
+ int i = (u_long)*bpp % sizeof(align);
+
+ if (i != 0)
+ i = sizeof(align) - i;
+
+ if ((ep - *bpp) < (i + IN6ADDRSZ)) {
+ /* Out of memory. Truncate address list here. */
+ *ap = NULL;
+ return;
+ }
+ *bpp += i;
+ map_v4v6_address(*ap, *bpp);
+ *ap = *bpp;
+ *bpp += IN6ADDRSZ;
+ }
+}
+
+static void
+addrsort(res_state statp, char **ap, int num) {
+ int i, j, needsort = 0, aval[MAXADDRS];
+ char **p;
+
+ p = ap;
+ for (i = 0; i < num; i++, p++) {
+ for (j = 0 ; (unsigned)j < statp->nsort; j++)
+ if (statp->sort_list[j].addr.s_addr ==
+ (((struct in_addr *)(*p))->s_addr &
+ statp->sort_list[j].mask))
+ break;
+ aval[i] = j;
+ if (needsort == 0 && i > 0 && j < aval[i-1])
+ needsort = i;
+ }
+ if (!needsort)
+ return;
+
+ while (needsort < num) {
+ for (j = needsort - 1; j >= 0; j--) {
+ if (aval[j] > aval[j+1]) {
+ char *hp;
+
+ i = aval[j];
+ aval[j] = aval[j+1];
+ aval[j+1] = i;
+
+ hp = ap[j];
+ ap[j] = ap[j+1];
+ ap[j+1] = hp;
+
+ } else
+ break;
+ }
+ needsort++;
+ }
+}
+
+static int
+init(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res && !ho_res_get(this))
+ return (-1);
+ if (((pvt->res->options & RES_INIT) == 0U) &&
+ res_ninit(pvt->res) == -1)
+ return (-1);
+ return (0);
+}
diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns_nw.c b/usr/src/lib/libresolv2_joy/common/irs/dns_nw.c
new file mode 100644
index 0000000000..9d9dbf9cd2
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/dns_nw.c
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports. */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "dns_p.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) sprintf x
+#endif
+
+/* Definitions. */
+
+#define MAXALIASES 35
+
+#define MAXPACKET (64*1024)
+
+struct pvt {
+ struct nwent net;
+ char * ali[MAXALIASES];
+ char buf[BUFSIZ+1];
+ struct __res_state * res;
+ void (*free_res)(void *);
+};
+
+typedef union {
+ long al;
+ char ac;
+} align;
+
+enum by_what { by_addr, by_name };
+
+/* Forwards. */
+
+static void nw_close(struct irs_nw *);
+static struct nwent * nw_byname(struct irs_nw *, const char *, int);
+static struct nwent * nw_byaddr(struct irs_nw *, void *, int, int);
+static struct nwent * nw_next(struct irs_nw *);
+static void nw_rewind(struct irs_nw *);
+static void nw_minimize(struct irs_nw *);
+static struct __res_state * nw_res_get(struct irs_nw *this);
+static void nw_res_set(struct irs_nw *this,
+ struct __res_state *res,
+ void (*free_res)(void *));
+
+static struct nwent * get1101byaddr(struct irs_nw *, u_char *, int);
+static struct nwent * get1101byname(struct irs_nw *, const char *);
+static struct nwent * get1101answer(struct irs_nw *,
+ u_char *ansbuf, int anslen,
+ enum by_what by_what,
+ int af, const char *name,
+ const u_char *addr, int addrlen);
+static struct nwent * get1101mask(struct irs_nw *this, struct nwent *);
+static int make1101inaddr(const u_char *, int, char *, int);
+static void normalize_name(char *name);
+static int init(struct irs_nw *this);
+
+/* Exports. */
+
+struct irs_nw *
+irs_dns_nw(struct irs_acc *this) {
+ struct irs_nw *nw;
+ struct pvt *pvt;
+
+ UNUSED(this);
+
+ if (!(pvt = memget(sizeof *pvt))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ if (!(nw = memget(sizeof *nw))) {
+ memput(pvt, sizeof *pvt);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(nw, 0x5e, sizeof *nw);
+ nw->private = pvt;
+ nw->close = nw_close;
+ nw->byname = nw_byname;
+ nw->byaddr = nw_byaddr;
+ nw->next = nw_next;
+ nw->rewind = nw_rewind;
+ nw->minimize = nw_minimize;
+ nw->res_get = nw_res_get;
+ nw->res_set = nw_res_set;
+ return (nw);
+}
+
+/* Methods. */
+
+static void
+nw_close(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ nw_minimize(this);
+
+ if (pvt->res && pvt->free_res)
+ (*pvt->free_res)(pvt->res);
+
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct nwent *
+nw_byname(struct irs_nw *this, const char *name, int af) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ switch (af) {
+ case AF_INET:
+ return (get1101byname(this, name));
+ default:
+ (void)NULL;
+ }
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ errno = EAFNOSUPPORT;
+ return (NULL);
+}
+
+static struct nwent *
+nw_byaddr(struct irs_nw *this, void *net, int len, int af) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ switch (af) {
+ case AF_INET:
+ return (get1101byaddr(this, net, len));
+ default:
+ (void)NULL;
+ }
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ errno = EAFNOSUPPORT;
+ return (NULL);
+}
+
+static struct nwent *
+nw_next(struct irs_nw *this) {
+
+ UNUSED(this);
+
+ return (NULL);
+}
+
+static void
+nw_rewind(struct irs_nw *this) {
+ UNUSED(this);
+ /* NOOP */
+}
+
+static void
+nw_minimize(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->res)
+ res_nclose(pvt->res);
+}
+
+static struct __res_state *
+nw_res_get(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (!res) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(res, 0, sizeof *res);
+ nw_res_set(this, res, free);
+ }
+
+ return (pvt->res);
+}
+
+static void
+nw_res_set(struct irs_nw *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->res && pvt->free_res) {
+ res_nclose(pvt->res);
+ (*pvt->free_res)(pvt->res);
+ }
+
+ pvt->res = res;
+ pvt->free_res = free_res;
+}
+
+/* Private. */
+
+static struct nwent *
+get1101byname(struct irs_nw *this, const char *name) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ u_char *ansbuf;
+ int anslen;
+ struct nwent *result;
+
+ ansbuf = memget(MAXPACKET);
+ if (ansbuf == NULL) {
+ errno = ENOMEM;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ anslen = res_nsearch(pvt->res, name, C_IN, T_PTR, ansbuf, MAXPACKET);
+ if (anslen < 0) {
+ memput(ansbuf, MAXPACKET);
+ return (NULL);
+ }
+ result = get1101mask(this, get1101answer(this, ansbuf, anslen, by_name,
+ AF_INET, name, NULL, 0));
+ memput(ansbuf, MAXPACKET);
+ return (result);
+}
+
+static struct nwent *
+get1101byaddr(struct irs_nw *this, u_char *net, int len) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char qbuf[sizeof "255.255.255.255.in-addr.arpa"];
+ struct nwent *result;
+ u_char *ansbuf;
+ int anslen;
+
+ if (len < 1 || len > 32) {
+ errno = EINVAL;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ if (make1101inaddr(net, len, qbuf, sizeof qbuf) < 0)
+ return (NULL);
+ ansbuf = memget(MAXPACKET);
+ if (ansbuf == NULL) {
+ errno = ENOMEM;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ anslen = res_nquery(pvt->res, qbuf, C_IN, T_PTR, ansbuf, MAXPACKET);
+ if (anslen < 0) {
+ memput(ansbuf, MAXPACKET);
+ return (NULL);
+ }
+ result = get1101mask(this, get1101answer(this, ansbuf, anslen, by_addr,
+ AF_INET, NULL, net, len));
+ memput(ansbuf, MAXPACKET);
+ return (result);
+}
+
+static struct nwent *
+get1101answer(struct irs_nw *this,
+ u_char *ansbuf, int anslen, enum by_what by_what,
+ int af, const char *name, const u_char *addr, int addrlen)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ int type, class, ancount, qdcount, haveanswer;
+ char *bp, *ep, **ap;
+ u_char *cp, *eom;
+ HEADER *hp;
+
+ /* Initialize, and parse header. */
+ eom = ansbuf + anslen;
+ if (ansbuf + HFIXEDSZ > eom) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ hp = (HEADER *)ansbuf;
+ cp = ansbuf + HFIXEDSZ;
+ qdcount = ntohs(hp->qdcount);
+ while (qdcount-- > 0) {
+ int n = dn_skipname(cp, eom);
+ cp += n + QFIXEDSZ;
+ if (n < 0 || cp > eom) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ }
+ ancount = ntohs(hp->ancount);
+ if (!ancount) {
+ if (hp->aa)
+ RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND);
+ else
+ RES_SET_H_ERRNO(pvt->res, TRY_AGAIN);
+ return (NULL);
+ }
+
+ /* Prepare a return structure. */
+ bp = pvt->buf;
+ ep = pvt->buf + sizeof(pvt->buf);
+ pvt->net.n_name = NULL;
+ pvt->net.n_aliases = pvt->ali;
+ pvt->net.n_addrtype = af;
+ pvt->net.n_addr = NULL;
+ pvt->net.n_length = addrlen;
+
+ /* Save input key if given. */
+ switch (by_what) {
+ case by_name:
+ if (name != NULL) {
+ int n = strlen(name) + 1;
+
+ if (n > (ep - bp)) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ pvt->net.n_name = strcpy(bp, name); /* (checked) */
+ bp += n;
+ }
+ break;
+ case by_addr:
+ if (addr != NULL && addrlen != 0) {
+ int n = addrlen / 8 + ((addrlen % 8) != 0);
+
+ if (INADDRSZ > (ep - bp)) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ memset(bp, 0, INADDRSZ);
+ memcpy(bp, addr, n);
+ pvt->net.n_addr = bp;
+ bp += INADDRSZ;
+ }
+ break;
+ default:
+ abort();
+ }
+
+ /* Parse the answer, collect aliases. */
+ ap = pvt->ali;
+ haveanswer = 0;
+ while (--ancount >= 0 && cp < eom) {
+ int n = dn_expand(ansbuf, eom, cp, bp, ep - bp);
+
+ cp += n; /*%< Owner */
+ if (n < 0 || !maybe_dnok(pvt->res, bp) ||
+ cp + 3 * INT16SZ + INT32SZ > eom) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ GETSHORT(type, cp); /*%< Type */
+ GETSHORT(class, cp); /*%< Class */
+ cp += INT32SZ; /*%< TTL */
+ GETSHORT(n, cp); /*%< RDLENGTH */
+ if (class == C_IN && type == T_PTR) {
+ int nn;
+
+ nn = dn_expand(ansbuf, eom, cp, bp, ep - bp);
+ if (nn < 0 || !maybe_hnok(pvt->res, bp) || nn != n) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ normalize_name(bp);
+ switch (by_what) {
+ case by_addr: {
+ if (pvt->net.n_name == NULL)
+ pvt->net.n_name = bp;
+ else if (ns_samename(pvt->net.n_name, bp) == 1)
+ break;
+ else
+ *ap++ = bp;
+ nn = strlen(bp) + 1;
+ bp += nn;
+ haveanswer++;
+ break;
+ }
+ case by_name: {
+ u_int b1, b2, b3, b4;
+
+ if (pvt->net.n_addr != NULL ||
+ sscanf(bp, "%u.%u.%u.%u.in-addr.arpa",
+ &b1, &b2, &b3, &b4) != 4)
+ break;
+ if ((ep - bp) < INADDRSZ) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ return (NULL);
+ }
+ pvt->net.n_addr = bp;
+ *bp++ = b4;
+ *bp++ = b3;
+ *bp++ = b2;
+ *bp++ = b1;
+ pvt->net.n_length = INADDRSZ * 8;
+ haveanswer++;
+ }
+ }
+ }
+ cp += n; /*%< RDATA */
+ }
+ if (!haveanswer) {
+ RES_SET_H_ERRNO(pvt->res, TRY_AGAIN);
+ return (NULL);
+ }
+ *ap = NULL;
+
+ return (&pvt->net);
+}
+
+static struct nwent *
+get1101mask(struct irs_nw *this, struct nwent *nwent) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char qbuf[sizeof "255.255.255.255.in-addr.arpa"], owner[MAXDNAME];
+ int anslen, type, class, ancount, qdcount;
+ u_char *ansbuf, *cp, *eom;
+ HEADER *hp;
+
+ if (!nwent)
+ return (NULL);
+ if (make1101inaddr(nwent->n_addr, nwent->n_length, qbuf, sizeof qbuf)
+ < 0) {
+ /* "First, do no harm." */
+ return (nwent);
+ }
+
+ ansbuf = memget(MAXPACKET);
+ if (ansbuf == NULL) {
+ errno = ENOMEM;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ /* Query for the A RR that would hold this network's mask. */
+ anslen = res_nquery(pvt->res, qbuf, C_IN, T_A, ansbuf, MAXPACKET);
+ if (anslen < HFIXEDSZ) {
+ memput(ansbuf, MAXPACKET);
+ return (nwent);
+ }
+
+ /* Initialize, and parse header. */
+ hp = (HEADER *)ansbuf;
+ cp = ansbuf + HFIXEDSZ;
+ eom = ansbuf + anslen;
+ qdcount = ntohs(hp->qdcount);
+ while (qdcount-- > 0) {
+ int n = dn_skipname(cp, eom);
+ cp += n + QFIXEDSZ;
+ if (n < 0 || cp > eom) {
+ memput(ansbuf, MAXPACKET);
+ return (nwent);
+ }
+ }
+ ancount = ntohs(hp->ancount);
+
+ /* Parse the answer, collect aliases. */
+ while (--ancount >= 0 && cp < eom) {
+ int n = dn_expand(ansbuf, eom, cp, owner, sizeof owner);
+
+ if (n < 0 || !maybe_dnok(pvt->res, owner))
+ break;
+ cp += n; /*%< Owner */
+ if (cp + 3 * INT16SZ + INT32SZ > eom)
+ break;
+ GETSHORT(type, cp); /*%< Type */
+ GETSHORT(class, cp); /*%< Class */
+ cp += INT32SZ; /*%< TTL */
+ GETSHORT(n, cp); /*%< RDLENGTH */
+ if (cp + n > eom)
+ break;
+ if (n == INADDRSZ && class == C_IN && type == T_A &&
+ ns_samename(qbuf, owner) == 1) {
+ /* This A RR indicates the actual netmask. */
+ int nn, mm;
+
+ nwent->n_length = 0;
+ for (nn = 0; nn < INADDRSZ; nn++)
+ for (mm = 7; mm >= 0; mm--)
+ if (cp[nn] & (1 << mm))
+ nwent->n_length++;
+ else
+ break;
+ }
+ cp += n; /*%< RDATA */
+ }
+ memput(ansbuf, MAXPACKET);
+ return (nwent);
+}
+
+static int
+make1101inaddr(const u_char *net, int bits, char *name, int size) {
+ int n, m;
+ char *ep;
+
+ ep = name + size;
+
+ /* Zero fill any whole bytes left out of the prefix. */
+ for (n = (32 - bits) / 8; n > 0; n--) {
+ if (ep - name < (int)(sizeof "0."))
+ goto emsgsize;
+ m = SPRINTF((name, "0."));
+ name += m;
+ }
+
+ /* Format the partial byte, if any, within the prefix. */
+ if ((n = bits % 8) != 0) {
+ if (ep - name < (int)(sizeof "255."))
+ goto emsgsize;
+ m = SPRINTF((name, "%u.",
+ net[bits / 8] & ~((1 << (8 - n)) - 1)));
+ name += m;
+ }
+
+ /* Format the whole bytes within the prefix. */
+ for (n = bits / 8; n > 0; n--) {
+ if (ep - name < (int)(sizeof "255."))
+ goto emsgsize;
+ m = SPRINTF((name, "%u.", net[n - 1]));
+ name += m;
+ }
+
+ /* Add the static text. */
+ if (ep - name < (int)(sizeof "in-addr.arpa"))
+ goto emsgsize;
+ (void) SPRINTF((name, "in-addr.arpa"));
+ return (0);
+
+ emsgsize:
+ errno = EMSGSIZE;
+ return (-1);
+}
+
+static void
+normalize_name(char *name) {
+ char *t;
+
+ /* Make lower case. */
+ for (t = name; *t; t++)
+ if (isascii((unsigned char)*t) && isupper((unsigned char)*t))
+ *t = tolower((*t)&0xff);
+
+ /* Remove trailing dots. */
+ while (t > name && t[-1] == '.')
+ *--t = '\0';
+}
+
+static int
+init(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res && !nw_res_get(this))
+ return (-1);
+ if (((pvt->res->options & RES_INIT) == 0U) &&
+ res_ninit(pvt->res) == -1)
+ return (-1);
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns_p.h b/usr/src/lib/libresolv2_joy/common/irs/dns_p.h
new file mode 100644
index 0000000000..d85ae2a238
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/dns_p.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: dns_p.h,v 1.4 2005/04/27 04:56:22 sra Exp $
+ */
+
+#ifndef _DNS_P_H_INCLUDED
+#define _DNS_P_H_INCLUDED
+
+#define maybe_ok(res, nm, ok) (((res)->options & RES_NOCHECKNAME) != 0U || \
+ (ok)(nm) != 0)
+#define maybe_hnok(res, hn) maybe_ok((res), (hn), res_hnok)
+#define maybe_dnok(res, dn) maybe_ok((res), (dn), res_dnok)
+
+/*%
+ * Object state.
+ */
+struct dns_p {
+ void *hes_ctx;
+ struct __res_state *res;
+ void (*free_res) __P((void *));
+};
+
+/*
+ * Methods.
+ */
+
+extern struct irs_gr * irs_dns_gr __P((struct irs_acc *));
+extern struct irs_pw * irs_dns_pw __P((struct irs_acc *));
+extern struct irs_sv * irs_dns_sv __P((struct irs_acc *));
+extern struct irs_pr * irs_dns_pr __P((struct irs_acc *));
+extern struct irs_ho * irs_dns_ho __P((struct irs_acc *));
+extern struct irs_nw * irs_dns_nw __P((struct irs_acc *));
+
+#endif /*_DNS_P_H_INCLUDED*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns_pr.c b/usr/src/lib/libresolv2_joy/common/irs/dns_pr.c
new file mode 100644
index 0000000000..0fb533e656
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/dns_pr.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "hesiod.h"
+#include "dns_p.h"
+
+/* Types. */
+
+struct pvt {
+ struct dns_p * dns;
+ struct protoent proto;
+ char * prbuf;
+};
+
+/* Forward. */
+
+static void pr_close(struct irs_pr *);
+static struct protoent * pr_byname(struct irs_pr *, const char *);
+static struct protoent * pr_bynumber(struct irs_pr *, int);
+static struct protoent * pr_next(struct irs_pr *);
+static void pr_rewind(struct irs_pr *);
+static void pr_minimize(struct irs_pr *);
+static struct __res_state * pr_res_get(struct irs_pr *);
+static void pr_res_set(struct irs_pr *,
+ struct __res_state *,
+ void (*)(void *));
+
+static struct protoent * parse_hes_list(struct irs_pr *, char **);
+
+/* Public. */
+
+struct irs_pr *
+irs_dns_pr(struct irs_acc *this) {
+ struct dns_p *dns = (struct dns_p *)this->private;
+ struct pvt *pvt;
+ struct irs_pr *pr;
+
+ if (!dns->hes_ctx) {
+ errno = ENODEV;
+ return (NULL);
+ }
+ if (!(pvt = memget(sizeof *pvt))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ if (!(pr = memget(sizeof *pr))) {
+ memput(pvt, sizeof *pvt);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pr, 0x5e, sizeof *pr);
+ pvt->dns = dns;
+ pr->private = pvt;
+ pr->byname = pr_byname;
+ pr->bynumber = pr_bynumber;
+ pr->next = pr_next;
+ pr->rewind = pr_rewind;
+ pr->close = pr_close;
+ pr->minimize = pr_minimize;
+ pr->res_get = pr_res_get;
+ pr->res_set = pr_res_set;
+ return (pr);
+}
+
+/* Methods. */
+
+static void
+pr_close(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->proto.p_aliases)
+ free(pvt->proto.p_aliases);
+ if (pvt->prbuf)
+ free(pvt->prbuf);
+
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct protoent *
+pr_byname(struct irs_pr *this, const char *name) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct dns_p *dns = pvt->dns;
+ struct protoent *proto;
+ char **hes_list;
+
+ if (!(hes_list = hesiod_resolve(dns->hes_ctx, name, "protocol")))
+ return (NULL);
+
+ proto = parse_hes_list(this, hes_list);
+ hesiod_free_list(dns->hes_ctx, hes_list);
+ return (proto);
+}
+
+static struct protoent *
+pr_bynumber(struct irs_pr *this, int num) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct dns_p *dns = pvt->dns;
+ struct protoent *proto;
+ char numstr[16];
+ char **hes_list;
+
+ sprintf(numstr, "%d", num);
+ if (!(hes_list = hesiod_resolve(dns->hes_ctx, numstr, "protonum")))
+ return (NULL);
+
+ proto = parse_hes_list(this, hes_list);
+ hesiod_free_list(dns->hes_ctx, hes_list);
+ return (proto);
+}
+
+static struct protoent *
+pr_next(struct irs_pr *this) {
+ UNUSED(this);
+ errno = ENODEV;
+ return (NULL);
+}
+
+static void
+pr_rewind(struct irs_pr *this) {
+ UNUSED(this);
+ /* NOOP */
+}
+
+static void
+pr_minimize(struct irs_pr *this) {
+ UNUSED(this);
+ /* NOOP */
+}
+
+static struct __res_state *
+pr_res_get(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct dns_p *dns = pvt->dns;
+
+ return (__hesiod_res_get(dns->hes_ctx));
+}
+
+static void
+pr_res_set(struct irs_pr *this, struct __res_state * res,
+ void (*free_res)(void *)) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct dns_p *dns = pvt->dns;
+
+ __hesiod_res_set(dns->hes_ctx, res, free_res);
+}
+
+/* Private. */
+
+static struct protoent *
+parse_hes_list(struct irs_pr *this, char **hes_list) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char *p, *cp, **cpp, **new;
+ int num = 0;
+ int max = 0;
+
+ for (cpp = hes_list; *cpp; cpp++) {
+ cp = *cpp;
+
+ /* Strip away comments, if any. */
+ if ((p = strchr(cp, '#')))
+ *p = 0;
+
+ /* Skip blank lines. */
+ p = cp;
+ while (*p && !isspace((unsigned char)*p))
+ p++;
+ if (!*p)
+ continue;
+
+ /* OK, we've got a live one. Let's parse it for real. */
+ if (pvt->prbuf)
+ free(pvt->prbuf);
+ pvt->prbuf = strdup(cp);
+
+ p = pvt->prbuf;
+ pvt->proto.p_name = p;
+ while (*p && !isspace((unsigned char)*p))
+ p++;
+ if (!*p)
+ continue;
+ *p++ = '\0';
+
+ pvt->proto.p_proto = atoi(p);
+ while (*p && !isspace((unsigned char)*p))
+ p++;
+ if (*p)
+ *p++ = '\0';
+
+ while (*p) {
+ if ((num + 1) >= max || !pvt->proto.p_aliases) {
+ max += 10;
+ new = realloc(pvt->proto.p_aliases,
+ max * sizeof(char *));
+ if (!new) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ pvt->proto.p_aliases = new;
+ }
+ pvt->proto.p_aliases[num++] = p;
+ while (*p && !isspace((unsigned char)*p))
+ p++;
+ if (*p)
+ *p++ = '\0';
+ }
+ if (!pvt->proto.p_aliases)
+ pvt->proto.p_aliases = malloc(sizeof(char *));
+ if (!pvt->proto.p_aliases)
+ goto cleanup;
+ pvt->proto.p_aliases[num] = NULL;
+ return (&pvt->proto);
+ }
+
+ cleanup:
+ if (pvt->proto.p_aliases) {
+ free(pvt->proto.p_aliases);
+ pvt->proto.p_aliases = NULL;
+ }
+ if (pvt->prbuf) {
+ free(pvt->prbuf);
+ pvt->prbuf = NULL;
+ }
+ return (NULL);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/dns_sv.c b/usr/src/lib/libresolv2_joy/common/irs/dns_sv.c
new file mode 100644
index 0000000000..720c230595
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/dns_sv.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "hesiod.h"
+#include "dns_p.h"
+
+/* Definitions */
+
+struct pvt {
+ struct dns_p * dns;
+ struct servent serv;
+ char * svbuf;
+ struct __res_state * res;
+ void (*free_res)(void *);
+};
+
+/* Forward. */
+
+static void sv_close(struct irs_sv *);
+static struct servent * sv_byname(struct irs_sv *,
+ const char *, const char *);
+static struct servent * sv_byport(struct irs_sv *, int, const char *);
+static struct servent * sv_next(struct irs_sv *);
+static void sv_rewind(struct irs_sv *);
+static void sv_minimize(struct irs_sv *);
+#ifdef SV_RES_SETGET
+static struct __res_state * sv_res_get(struct irs_sv *);
+static void sv_res_set(struct irs_sv *,
+ struct __res_state *,
+ void (*)(void *));
+#endif
+
+static struct servent * parse_hes_list(struct irs_sv *,
+ char **, const char *);
+
+/* Public */
+
+struct irs_sv *
+irs_dns_sv(struct irs_acc *this) {
+ struct dns_p *dns = (struct dns_p *)this->private;
+ struct irs_sv *sv;
+ struct pvt *pvt;
+
+ if (!dns || !dns->hes_ctx) {
+ errno = ENODEV;
+ return (NULL);
+ }
+ if (!(pvt = memget(sizeof *pvt))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ pvt->dns = dns;
+ if (!(sv = memget(sizeof *sv))) {
+ memput(pvt, sizeof *pvt);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(sv, 0x5e, sizeof *sv);
+ sv->private = pvt;
+ sv->byname = sv_byname;
+ sv->byport = sv_byport;
+ sv->next = sv_next;
+ sv->rewind = sv_rewind;
+ sv->close = sv_close;
+ sv->minimize = sv_minimize;
+#ifdef SV_RES_SETGET
+ sv->res_get = sv_res_get;
+ sv->res_set = sv_res_set;
+#else
+ sv->res_get = NULL; /*%< sv_res_get; */
+ sv->res_set = NULL; /*%< sv_res_set; */
+#endif
+ return (sv);
+}
+
+/* Methods */
+
+static void
+sv_close(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->serv.s_aliases)
+ free(pvt->serv.s_aliases);
+ if (pvt->svbuf)
+ free(pvt->svbuf);
+
+ if (pvt->res && pvt->free_res)
+ (*pvt->free_res)(pvt->res);
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct servent *
+sv_byname(struct irs_sv *this, const char *name, const char *proto) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct dns_p *dns = pvt->dns;
+ struct servent *s;
+ char **hes_list;
+
+ if (!(hes_list = hesiod_resolve(dns->hes_ctx, name, "service")))
+ return (NULL);
+
+ s = parse_hes_list(this, hes_list, proto);
+ hesiod_free_list(dns->hes_ctx, hes_list);
+ return (s);
+}
+
+static struct servent *
+sv_byport(struct irs_sv *this, int port, const char *proto) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct dns_p *dns = pvt->dns;
+ struct servent *s;
+ char portstr[16];
+ char **hes_list;
+
+ sprintf(portstr, "%d", ntohs(port));
+ if (!(hes_list = hesiod_resolve(dns->hes_ctx, portstr, "port")))
+ return (NULL);
+
+ s = parse_hes_list(this, hes_list, proto);
+ hesiod_free_list(dns->hes_ctx, hes_list);
+ return (s);
+}
+
+static struct servent *
+sv_next(struct irs_sv *this) {
+ UNUSED(this);
+ errno = ENODEV;
+ return (NULL);
+}
+
+static void
+sv_rewind(struct irs_sv *this) {
+ UNUSED(this);
+ /* NOOP */
+}
+
+/* Private */
+
+static struct servent *
+parse_hes_list(struct irs_sv *this, char **hes_list, const char *proto) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char *p, *cp, **cpp, **new;
+ int proto_len;
+ int num = 0;
+ int max = 0;
+
+ for (cpp = hes_list; *cpp; cpp++) {
+ cp = *cpp;
+
+ /* Strip away comments, if any. */
+ if ((p = strchr(cp, '#')))
+ *p = 0;
+
+ /* Check to make sure the protocol matches. */
+ p = cp;
+ while (*p && !isspace((unsigned char)*p))
+ p++;
+ if (!*p)
+ continue;
+ if (proto) {
+ proto_len = strlen(proto);
+ if (strncasecmp(++p, proto, proto_len) != 0)
+ continue;
+ if (p[proto_len] && !isspace(p[proto_len]&0xff))
+ continue;
+ }
+ /* OK, we've got a live one. Let's parse it for real. */
+ if (pvt->svbuf)
+ free(pvt->svbuf);
+ pvt->svbuf = strdup(cp);
+
+ p = pvt->svbuf;
+ pvt->serv.s_name = p;
+ while (*p && !isspace(*p&0xff))
+ p++;
+ if (!*p)
+ continue;
+ *p++ = '\0';
+
+ pvt->serv.s_proto = p;
+ while (*p && !isspace(*p&0xff))
+ p++;
+ if (!*p)
+ continue;
+ *p++ = '\0';
+
+ pvt->serv.s_port = htons((u_short) atoi(p));
+ while (*p && !isspace(*p&0xff))
+ p++;
+ if (*p)
+ *p++ = '\0';
+
+ while (*p) {
+ if ((num + 1) >= max || !pvt->serv.s_aliases) {
+ max += 10;
+ new = realloc(pvt->serv.s_aliases,
+ max * sizeof(char *));
+ if (!new) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ pvt->serv.s_aliases = new;
+ }
+ pvt->serv.s_aliases[num++] = p;
+ while (*p && !isspace(*p&0xff))
+ p++;
+ if (*p)
+ *p++ = '\0';
+ }
+ if (!pvt->serv.s_aliases)
+ pvt->serv.s_aliases = malloc(sizeof(char *));
+ if (!pvt->serv.s_aliases)
+ goto cleanup;
+ pvt->serv.s_aliases[num] = NULL;
+ return (&pvt->serv);
+ }
+
+ cleanup:
+ if (pvt->serv.s_aliases) {
+ free(pvt->serv.s_aliases);
+ pvt->serv.s_aliases = NULL;
+ }
+ if (pvt->svbuf) {
+ free(pvt->svbuf);
+ pvt->svbuf = NULL;
+ }
+ return (NULL);
+}
+
+static void
+sv_minimize(struct irs_sv *this) {
+ UNUSED(this);
+ /* NOOP */
+}
+
+#ifdef SV_RES_SETGET
+static struct __res_state *
+sv_res_get(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct dns_p *dns = pvt->dns;
+
+ return (__hesiod_res_get(dns->hes_ctx));
+}
+
+static void
+sv_res_set(struct irs_sv *this, struct __res_state * res,
+ void (*free_res)(void *)) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct dns_p *dns = pvt->dns;
+
+ __hesiod_res_set(dns->hes_ctx, res, free_res);
+}
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/gai_strerror.c b/usr/src/lib/libresolv2_joy/common/irs/gai_strerror.c
new file mode 100644
index 0000000000..9ca1c4bfe1
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/gai_strerror.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <port_before.h>
+#include <netdb.h>
+#include <port_after.h>
+
+#ifdef DO_PTHREADS
+#include <pthread.h>
+#include <stdlib.h>
+#endif
+
+static const char *gai_errlist[] = {
+ "no error",
+ "address family not supported for name",/*%< EAI_ADDRFAMILY */
+ "temporary failure", /*%< EAI_AGAIN */
+ "invalid flags", /*%< EAI_BADFLAGS */
+ "permanent failure", /*%< EAI_FAIL */
+ "address family not supported", /*%< EAI_FAMILY */
+ "memory failure", /*%< EAI_MEMORY */
+ "no address", /*%< EAI_NODATA */
+ "unknown name or service", /*%< EAI_NONAME */
+ "service not supported for socktype", /*%< EAI_SERVICE */
+ "socktype not supported", /*%< EAI_SOCKTYPE */
+ "system failure", /*%< EAI_SYSTEM */
+ "bad hints", /*%< EAI_BADHINTS */
+ "bad protocol", /*%< EAI_PROTOCOL */
+ "unknown error" /*%< Must be last. */
+};
+
+static const int gai_nerr = (sizeof(gai_errlist)/sizeof(*gai_errlist));
+
+#define EAI_BUFSIZE 128
+
+const char *
+gai_strerror(int ecode) {
+#ifndef DO_PTHREADS
+ static char buf[EAI_BUFSIZE];
+#else /* DO_PTHREADS */
+#ifndef LIBBIND_MUTEX_INITIALIZER
+#define LIBBIND_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#endif
+ static pthread_mutex_t lock = LIBBIND_MUTEX_INITIALIZER;
+ static pthread_key_t key;
+ static int once = 0;
+ char *buf;
+#endif
+
+ if (ecode >= 0 && ecode < (gai_nerr - 1))
+ return (gai_errlist[ecode]);
+
+#ifdef DO_PTHREADS
+ if (!once) {
+ if (pthread_mutex_lock(&lock) != 0)
+ goto unknown;
+ if (!once) {
+ if (pthread_key_create(&key, free) != 0) {
+ (void)pthread_mutex_unlock(&lock);
+ goto unknown;
+ }
+ once = 1;
+ }
+ if (pthread_mutex_unlock(&lock) != 0)
+ goto unknown;
+ }
+
+ buf = pthread_getspecific(key);
+ if (buf == NULL) {
+ buf = malloc(EAI_BUFSIZE);
+ if (buf == NULL)
+ goto unknown;
+ if (pthread_setspecific(key, buf) != 0) {
+ free(buf);
+ goto unknown;
+ }
+ }
+#endif
+ /*
+ * XXX This really should be snprintf(buf, EAI_BUFSIZE, ...).
+ * It is safe until message catalogs are used.
+ */
+ sprintf(buf, "%s: %d", gai_errlist[gai_nerr - 1], ecode);
+ return (buf);
+
+#ifdef DO_PTHREADS
+ unknown:
+ return ("unknown error");
+#endif
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen.c b/usr/src/lib/libresolv2_joy/common/irs/gen.c
new file mode 100644
index 0000000000..98203149e9
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/gen.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*! \file
+ * \brief
+ * this is the top level dispatcher
+ *
+ * The dispatcher is implemented as an accessor class; it is an
+ * accessor class that calls other accessor classes, as controlled by a
+ * configuration file.
+ *
+ * A big difference between this accessor class and others is that the
+ * map class initializers are NULL, and the map classes are already
+ * filled in with method functions that will do the right thing.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <isc/assertions.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "gen_p.h"
+
+#ifdef SUNW_HOSTS_FALLBACK
+extern int __res_no_hosts_fallback(void);
+#endif /* SUNW_HOSTS_FALLBACK */
+
+/* Definitions */
+
+struct nameval {
+ const char * name;
+ int val;
+};
+
+static const struct nameval acc_names[irs_nacc+1] = {
+ { "local", irs_lcl },
+ { "dns", irs_dns },
+ { "nis", irs_nis },
+ { "irp", irs_irp },
+ { NULL, irs_nacc }
+};
+
+typedef struct irs_acc *(*accinit) __P((const char *options));
+
+static const accinit accs[irs_nacc+1] = {
+ irs_lcl_acc,
+ irs_dns_acc,
+#ifdef WANT_IRS_NIS
+ irs_nis_acc,
+#else
+ NULL,
+#endif
+ irs_irp_acc,
+ NULL
+};
+
+static const struct nameval map_names[irs_nmap+1] = {
+ { "group", irs_gr },
+ { "passwd", irs_pw },
+ { "services", irs_sv },
+ { "protocols", irs_pr },
+ { "hosts", irs_ho },
+ { "networks", irs_nw },
+ { "netgroup", irs_ng },
+ { NULL, irs_nmap }
+};
+
+static const struct nameval option_names[] = {
+ { "merge", IRS_MERGE },
+ { "continue", IRS_CONTINUE },
+ { NULL, 0 }
+};
+
+/* Forward */
+
+static void gen_close(struct irs_acc *);
+static struct __res_state * gen_res_get(struct irs_acc *);
+static void gen_res_set(struct irs_acc *, struct __res_state *,
+ void (*)(void *));
+static int find_name(const char *, const struct nameval nv[]);
+static void init_map_rules(struct gen_p *, const char *conf_file);
+static struct irs_rule *release_rule(struct irs_rule *);
+static int add_rule(struct gen_p *,
+ enum irs_map_id, enum irs_acc_id,
+ const char *);
+
+/* Public */
+
+struct irs_acc *
+irs_gen_acc(const char *options, const char *conf_file) {
+ struct irs_acc *acc;
+ struct gen_p *irs;
+
+ if (!(acc = memget(sizeof *acc))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(acc, 0x5e, sizeof *acc);
+ if (!(irs = memget(sizeof *irs))) {
+ errno = ENOMEM;
+ memput(acc, sizeof *acc);
+ return (NULL);
+ }
+ memset(irs, 0x5e, sizeof *irs);
+ irs->options = strdup(options);
+ irs->res = NULL;
+ irs->free_res = NULL;
+ memset(irs->accessors, 0, sizeof irs->accessors);
+ memset(irs->map_rules, 0, sizeof irs->map_rules);
+ init_map_rules(irs, conf_file);
+ acc->private = irs;
+#ifdef WANT_IRS_GR
+ acc->gr_map = irs_gen_gr;
+#else
+ acc->gr_map = NULL;
+#endif
+#ifdef WANT_IRS_PW
+ acc->pw_map = irs_gen_pw;
+#else
+ acc->pw_map = NULL;
+#endif
+ acc->sv_map = irs_gen_sv;
+ acc->pr_map = irs_gen_pr;
+ acc->ho_map = irs_gen_ho;
+ acc->nw_map = irs_gen_nw;
+ acc->ng_map = irs_gen_ng;
+ acc->res_get = gen_res_get;
+ acc->res_set = gen_res_set;
+ acc->close = gen_close;
+ return (acc);
+}
+
+/* Methods */
+
+static struct __res_state *
+gen_res_get(struct irs_acc *this) {
+ struct gen_p *irs = (struct gen_p *)this->private;
+
+ if (irs->res == NULL) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (res == NULL)
+ return (NULL);
+ memset(res, 0, sizeof *res);
+ gen_res_set(this, res, free);
+ }
+
+ if (((irs->res->options & RES_INIT) == 0U) && res_ninit(irs->res) < 0)
+ return (NULL);
+
+ return (irs->res);
+}
+
+static void
+gen_res_set(struct irs_acc *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct gen_p *irs = (struct gen_p *)this->private;
+#if 0
+ struct irs_rule *rule;
+ struct irs_ho *ho;
+ struct irs_nw *nw;
+#endif
+
+ if (irs->res && irs->free_res) {
+ res_nclose(irs->res);
+ (*irs->free_res)(irs->res);
+ }
+
+ irs->res = res;
+ irs->free_res = free_res;
+
+#if 0
+ for (rule = irs->map_rules[irs_ho]; rule; rule = rule->next) {
+ ho = rule->inst->ho;
+
+ (*ho->res_set)(ho, res, NULL);
+ }
+ for (rule = irs->map_rules[irs_nw]; rule; rule = rule->next) {
+ nw = rule->inst->nw;
+
+ (*nw->res_set)(nw, res, NULL);
+ }
+#endif
+}
+
+static void
+gen_close(struct irs_acc *this) {
+ struct gen_p *irs = (struct gen_p *)this->private;
+ int n;
+
+ /* Search rules. */
+ for (n = 0; n < irs_nmap; n++)
+ while (irs->map_rules[n] != NULL)
+ irs->map_rules[n] = release_rule(irs->map_rules[n]);
+
+ /* Access methods. */
+ for (n = 0; n < irs_nacc; n++) {
+ /* Map objects. */
+ if (irs->accessors[n].gr != NULL)
+ (*irs->accessors[n].gr->close)(irs->accessors[n].gr);
+ if (irs->accessors[n].pw != NULL)
+ (*irs->accessors[n].pw->close)(irs->accessors[n].pw);
+ if (irs->accessors[n].sv != NULL)
+ (*irs->accessors[n].sv->close)(irs->accessors[n].sv);
+ if (irs->accessors[n].pr != NULL)
+ (*irs->accessors[n].pr->close)(irs->accessors[n].pr);
+ if (irs->accessors[n].ho != NULL)
+ (*irs->accessors[n].ho->close)(irs->accessors[n].ho);
+ if (irs->accessors[n].nw != NULL)
+ (*irs->accessors[n].nw->close)(irs->accessors[n].nw);
+ if (irs->accessors[n].ng != NULL)
+ (*irs->accessors[n].ng->close)(irs->accessors[n].ng);
+ /* Enclosing accessor. */
+ if (irs->accessors[n].acc != NULL)
+ (*irs->accessors[n].acc->close)(irs->accessors[n].acc);
+ }
+
+ /* The options string was strdup'd. */
+ free((void*)irs->options);
+
+ if (irs->res && irs->free_res)
+ (*irs->free_res)(irs->res);
+
+ /* The private data container. */
+ memput(irs, sizeof *irs);
+
+ /* The object. */
+ memput(this, sizeof *this);
+}
+
+/* Private */
+
+static int
+find_name(const char *name, const struct nameval names[]) {
+ int n;
+
+ for (n = 0; names[n].name != NULL; n++)
+ if (strcmp(name, names[n].name) == 0)
+ return (names[n].val);
+ return (-1);
+}
+
+static struct irs_rule *
+release_rule(struct irs_rule *rule) {
+ struct irs_rule *next = rule->next;
+
+ memput(rule, sizeof *rule);
+ return (next);
+}
+
+static int
+add_rule(struct gen_p *irs,
+ enum irs_map_id map, enum irs_acc_id acc,
+ const char *options)
+{
+ struct irs_rule **rules, *last, *tmp, *new;
+ struct irs_inst *inst;
+ const char *cp;
+ int n;
+
+#ifndef WANT_IRS_GR
+ if (map == irs_gr)
+ return (-1);
+#endif
+#ifndef WANT_IRS_PW
+ if (map == irs_pw)
+ return (-1);
+#endif
+#ifndef WANT_IRS_NIS
+ if (acc == irs_nis)
+ return (-1);
+#endif
+ new = memget(sizeof *new);
+ if (new == NULL)
+ return (-1);
+ memset(new, 0x5e, sizeof *new);
+ new->next = NULL;
+
+ new->inst = &irs->accessors[acc];
+
+ new->flags = 0;
+ cp = options;
+ while (cp && *cp) {
+ char option[50], *next;
+
+ next = strchr(cp, ',');
+ if (next)
+ n = next++ - cp;
+ else
+ n = strlen(cp);
+ if ((size_t)n > sizeof option - 1)
+ n = sizeof option - 1;
+ strncpy(option, cp, n);
+ option[n] = '\0';
+
+ n = find_name(option, option_names);
+ if (n >= 0)
+ new->flags |= n;
+
+ cp = next;
+ }
+
+ rules = &irs->map_rules[map];
+ for (last = NULL, tmp = *rules;
+ tmp != NULL;
+ last = tmp, tmp = tmp->next)
+ (void)NULL;
+ if (last == NULL)
+ *rules = new;
+ else
+ last->next = new;
+
+ /* Try to instantiate map accessors for this if necessary & approp. */
+ inst = &irs->accessors[acc];
+ if (inst->acc == NULL && accs[acc] != NULL)
+ inst->acc = (*accs[acc])(irs->options);
+ if (inst->acc != NULL) {
+ if (inst->gr == NULL && inst->acc->gr_map != NULL)
+ inst->gr = (*inst->acc->gr_map)(inst->acc);
+ if (inst->pw == NULL && inst->acc->pw_map != NULL)
+ inst->pw = (*inst->acc->pw_map)(inst->acc);
+ if (inst->sv == NULL && inst->acc->sv_map != NULL)
+ inst->sv = (*inst->acc->sv_map)(inst->acc);
+ if (inst->pr == NULL && inst->acc->pr_map != NULL)
+ inst->pr = (*inst->acc->pr_map)(inst->acc);
+ if (inst->ho == NULL && inst->acc->ho_map != NULL)
+ inst->ho = (*inst->acc->ho_map)(inst->acc);
+ if (inst->nw == NULL && inst->acc->nw_map != NULL)
+ inst->nw = (*inst->acc->nw_map)(inst->acc);
+ if (inst->ng == NULL && inst->acc->ng_map != NULL)
+ inst->ng = (*inst->acc->ng_map)(inst->acc);
+ }
+
+ return (0);
+}
+
+static void
+default_map_rules(struct gen_p *irs) {
+ /* Install time honoured and proved BSD style rules as default. */
+ add_rule(irs, irs_gr, irs_lcl, "");
+ add_rule(irs, irs_pw, irs_lcl, "");
+ add_rule(irs, irs_sv, irs_lcl, "");
+ add_rule(irs, irs_pr, irs_lcl, "");
+#ifdef SUNW_HOSTS_FALLBACK
+ if (__res_no_hosts_fallback())
+ add_rule(irs, irs_ho, irs_dns, "");
+ else {
+ add_rule(irs, irs_ho, irs_dns, "continue");
+ add_rule(irs, irs_ho, irs_lcl, "");
+ }
+#else /* SUNW_HOSTS_FALLBACK */
+ add_rule(irs, irs_ho, irs_dns, "continue");
+ add_rule(irs, irs_ho, irs_lcl, "");
+#endif /* SUNW_HOSTS_FALLBACK */
+ add_rule(irs, irs_nw, irs_dns, "continue");
+ add_rule(irs, irs_nw, irs_lcl, "");
+ add_rule(irs, irs_ng, irs_lcl, "");
+}
+
+static void
+init_map_rules(struct gen_p *irs, const char *conf_file) {
+ char line[1024], pattern[40], mapname[20], accname[20], options[100];
+ FILE *conf;
+
+#ifdef SUNW_HOSTS_FALLBACK
+ if (__res_no_hosts_fallback()) {
+ default_map_rules(irs);
+ return;
+ }
+#endif /* SUNW_HOSTS_FALLBACK */
+
+ if (conf_file == NULL)
+ conf_file = _PATH_IRS_CONF ;
+
+ /* A conf file of "" means compiled in defaults. Irpd wants this */
+ if (conf_file[0] == '\0' || (conf = fopen(conf_file, "r")) == NULL) {
+ default_map_rules(irs);
+ return;
+ }
+ (void) sprintf(pattern, "%%%lus %%%lus %%%lus\n",
+ (unsigned long)sizeof mapname,
+ (unsigned long)sizeof accname,
+ (unsigned long)sizeof options);
+ while (fgets(line, sizeof line, conf)) {
+ enum irs_map_id map;
+ enum irs_acc_id acc;
+ char *tmp;
+ int n;
+
+ for (tmp = line;
+ isascii((unsigned char)*tmp) &&
+ isspace((unsigned char)*tmp);
+ tmp++)
+ (void)NULL;
+ if (*tmp == '#' || *tmp == '\n' || *tmp == '\0')
+ continue;
+ n = sscanf(tmp, pattern, mapname, accname, options);
+ if (n < 2)
+ continue;
+ if (n < 3)
+ options[0] = '\0';
+
+ n = find_name(mapname, map_names);
+ INSIST(n < irs_nmap);
+ if (n < 0)
+ continue;
+ map = (enum irs_map_id) n;
+
+ n = find_name(accname, acc_names);
+ INSIST(n < irs_nacc);
+ if (n < 0)
+ continue;
+ acc = (enum irs_acc_id) n;
+
+ add_rule(irs, map, acc, options);
+ }
+ fclose(conf);
+}
diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_ho.c b/usr/src/lib/libresolv2_joy/common/irs/gen_ho.c
new file mode 100644
index 0000000000..f8eddd4c0e
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/gen_ho.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "gen_p.h"
+
+/* Definitions */
+
+struct pvt {
+ struct irs_rule * rules;
+ struct irs_rule * rule;
+ struct irs_ho * ho;
+ struct __res_state * res;
+ void (*free_res)(void *);
+};
+
+/* Forwards */
+
+static void ho_close(struct irs_ho *this);
+static struct hostent * ho_byname(struct irs_ho *this, const char *name);
+static struct hostent * ho_byname2(struct irs_ho *this, const char *name,
+ int af);
+static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr,
+ int len, int af);
+static struct hostent * ho_next(struct irs_ho *this);
+static void ho_rewind(struct irs_ho *this);
+static void ho_minimize(struct irs_ho *this);
+static struct __res_state * ho_res_get(struct irs_ho *this);
+static void ho_res_set(struct irs_ho *this,
+ struct __res_state *res,
+ void (*free_res)(void *));
+static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name,
+ const struct addrinfo *pai);
+
+static int init(struct irs_ho *this);
+
+/* Exports */
+
+struct irs_ho *
+irs_gen_ho(struct irs_acc *this) {
+ struct gen_p *accpvt = (struct gen_p *)this->private;
+ struct irs_ho *ho;
+ struct pvt *pvt;
+
+ if (!(pvt = memget(sizeof *pvt))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ if (!(ho = memget(sizeof *ho))) {
+ memput(pvt, sizeof *pvt);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(ho, 0x5e, sizeof *ho);
+ pvt->rules = accpvt->map_rules[irs_ho];
+ pvt->rule = pvt->rules;
+ ho->private = pvt;
+ ho->close = ho_close;
+ ho->byname = ho_byname;
+ ho->byname2 = ho_byname2;
+ ho->byaddr = ho_byaddr;
+ ho->next = ho_next;
+ ho->rewind = ho_rewind;
+ ho->minimize = ho_minimize;
+ ho->res_get = ho_res_get;
+ ho->res_set = ho_res_set;
+ ho->addrinfo = ho_addrinfo;
+ return (ho);
+}
+
+/* Methods. */
+
+static void
+ho_close(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ ho_minimize(this);
+ if (pvt->res && pvt->free_res)
+ (*pvt->free_res)(pvt->res);
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct hostent *
+ho_byname(struct irs_ho *this, const char *name) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct hostent *rval;
+ struct irs_ho *ho;
+ int therrno = NETDB_INTERNAL;
+ int softerror = 0;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ ho = rule->inst->ho;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ errno = 0;
+ rval = (*ho->byname)(ho, name);
+ if (rval != NULL)
+ return (rval);
+ if (softerror == 0 &&
+ pvt->res->res_h_errno != HOST_NOT_FOUND &&
+ pvt->res->res_h_errno != NETDB_INTERNAL) {
+ softerror = 1;
+ therrno = pvt->res->res_h_errno;
+ }
+ if (rule->flags & IRS_CONTINUE)
+ continue;
+ /*
+ * The value TRY_AGAIN can mean that the service
+ * is not available, or just that this particular name
+ * cannot be resolved now. We use the errno ECONNREFUSED
+ * to distinguish. If a lookup sets that errno when
+ * H_ERRNO is TRY_AGAIN, we continue to try other lookup
+ * functions, otherwise we return the TRY_AGAIN error.
+ */
+ if (pvt->res->res_h_errno != TRY_AGAIN || errno != ECONNREFUSED)
+ break;
+ }
+ if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND)
+ RES_SET_H_ERRNO(pvt->res, therrno);
+ return (NULL);
+}
+
+static struct hostent *
+ho_byname2(struct irs_ho *this, const char *name, int af) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct hostent *rval;
+ struct irs_ho *ho;
+ int therrno = NETDB_INTERNAL;
+ int softerror = 0;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ ho = rule->inst->ho;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ errno = 0;
+ rval = (*ho->byname2)(ho, name, af);
+ if (rval != NULL)
+ return (rval);
+ if (softerror == 0 &&
+ pvt->res->res_h_errno != HOST_NOT_FOUND &&
+ pvt->res->res_h_errno != NETDB_INTERNAL) {
+ softerror = 1;
+ therrno = pvt->res->res_h_errno;
+ }
+ if (rule->flags & IRS_CONTINUE)
+ continue;
+ /*
+ * See the comments in ho_byname() explaining
+ * the interpretation of TRY_AGAIN and ECONNREFUSED.
+ */
+ if (pvt->res->res_h_errno != TRY_AGAIN || errno != ECONNREFUSED)
+ break;
+ }
+ if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND)
+ RES_SET_H_ERRNO(pvt->res, therrno);
+ return (NULL);
+}
+
+static struct hostent *
+ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct hostent *rval;
+ struct irs_ho *ho;
+ int therrno = NETDB_INTERNAL;
+ int softerror = 0;
+
+
+ if (init(this) == -1)
+ return (NULL);
+
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ ho = rule->inst->ho;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ errno = 0;
+ rval = (*ho->byaddr)(ho, addr, len, af);
+ if (rval != NULL)
+ return (rval);
+ if (softerror == 0 &&
+ pvt->res->res_h_errno != HOST_NOT_FOUND &&
+ pvt->res->res_h_errno != NETDB_INTERNAL) {
+ softerror = 1;
+ therrno = pvt->res->res_h_errno;
+ }
+
+ if (rule->flags & IRS_CONTINUE)
+ continue;
+ /*
+ * See the comments in ho_byname() explaining
+ * the interpretation of TRY_AGAIN and ECONNREFUSED.
+ */
+ if (pvt->res->res_h_errno != TRY_AGAIN || errno != ECONNREFUSED)
+ break;
+ }
+ if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND)
+ RES_SET_H_ERRNO(pvt->res, therrno);
+ return (NULL);
+}
+
+static struct hostent *
+ho_next(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct hostent *rval;
+ struct irs_ho *ho;
+
+ while (pvt->rule) {
+ ho = pvt->rule->inst->ho;
+ rval = (*ho->next)(ho);
+ if (rval)
+ return (rval);
+ if (!(pvt->rule->flags & IRS_CONTINUE))
+ break;
+ pvt->rule = pvt->rule->next;
+ if (pvt->rule) {
+ ho = pvt->rule->inst->ho;
+ (*ho->rewind)(ho);
+ }
+ }
+ return (NULL);
+}
+
+static void
+ho_rewind(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_ho *ho;
+
+ pvt->rule = pvt->rules;
+ if (pvt->rule) {
+ ho = pvt->rule->inst->ho;
+ (*ho->rewind)(ho);
+ }
+}
+
+static void
+ho_minimize(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+
+ if (pvt->res)
+ res_nclose(pvt->res);
+ for (rule = pvt->rules; rule != NULL; rule = rule->next) {
+ struct irs_ho *ho = rule->inst->ho;
+
+ (*ho->minimize)(ho);
+ }
+}
+
+static struct __res_state *
+ho_res_get(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (!res) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(res, 0, sizeof *res);
+ ho_res_set(this, res, free);
+ }
+
+ return (pvt->res);
+}
+
+static void
+ho_res_set(struct irs_ho *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+
+ if (pvt->res && pvt->free_res) {
+ res_nclose(pvt->res);
+ (*pvt->free_res)(pvt->res);
+ }
+
+ pvt->res = res;
+ pvt->free_res = free_res;
+
+ for (rule = pvt->rules; rule != NULL; rule = rule->next) {
+ struct irs_ho *ho = rule->inst->ho;
+
+ (*ho->res_set)(ho, pvt->res, NULL);
+ }
+}
+
+static struct addrinfo *
+ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct addrinfo *rval = NULL;
+ struct irs_ho *ho;
+ int therrno = NETDB_INTERNAL;
+ int softerror = 0;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ ho = rule->inst->ho;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ errno = 0;
+ if (ho->addrinfo == NULL) /*%< for safety */
+ continue;
+ rval = (*ho->addrinfo)(ho, name, pai);
+ if (rval != NULL)
+ return (rval);
+ if (softerror == 0 &&
+ pvt->res->res_h_errno != HOST_NOT_FOUND &&
+ pvt->res->res_h_errno != NETDB_INTERNAL) {
+ softerror = 1;
+ therrno = pvt->res->res_h_errno;
+ }
+ if (rule->flags & IRS_CONTINUE)
+ continue;
+ /*
+ * See the comments in ho_byname() explaining
+ * the interpretation of TRY_AGAIN and ECONNREFUSED.
+ */
+ if (pvt->res->res_h_errno != TRY_AGAIN ||
+ errno != ECONNREFUSED)
+ break;
+ }
+ if (softerror != 0 && pvt->res->res_h_errno == HOST_NOT_FOUND)
+ RES_SET_H_ERRNO(pvt->res, therrno);
+ return (NULL);
+}
+
+static int
+init(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res && !ho_res_get(this))
+ return (-1);
+
+ if (((pvt->res->options & RES_INIT) == 0U) &&
+ (res_ninit(pvt->res) == -1))
+ return (-1);
+
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_ng.c b/usr/src/lib/libresolv2_joy/common/irs/gen_ng.c
new file mode 100644
index 0000000000..05c7d4dfb7
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/gen_ng.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "gen_p.h"
+
+/* Types */
+
+struct pvt {
+ struct irs_rule * rules;
+ struct irs_rule * rule;
+ char * curgroup;
+};
+
+/* Forward */
+
+static void ng_close(struct irs_ng *);
+static int ng_next(struct irs_ng *, const char **,
+ const char **, const char **);
+static int ng_test(struct irs_ng *, const char *,
+ const char *, const char *,
+ const char *);
+static void ng_rewind(struct irs_ng *, const char *);
+static void ng_minimize(struct irs_ng *);
+
+/* Public */
+
+struct irs_ng *
+irs_gen_ng(struct irs_acc *this) {
+ struct gen_p *accpvt = (struct gen_p *)this->private;
+ struct irs_ng *ng;
+ struct pvt *pvt;
+
+ if (!(ng = memget(sizeof *ng))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(ng, 0x5e, sizeof *ng);
+ if (!(pvt = memget(sizeof *pvt))) {
+ memput(ng, sizeof *ng);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ pvt->rules = accpvt->map_rules[irs_ng];
+ pvt->rule = pvt->rules;
+ ng->private = pvt;
+ ng->close = ng_close;
+ ng->next = ng_next;
+ ng->test = ng_test;
+ ng->rewind = ng_rewind;
+ ng->minimize = ng_minimize;
+ return (ng);
+}
+
+/* Methods */
+
+static void
+ng_close(struct irs_ng *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ ng_minimize(this);
+ if (pvt->curgroup)
+ free(pvt->curgroup);
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static int
+ng_next(struct irs_ng *this, const char **host, const char **user,
+ const char **domain)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_ng *ng;
+
+ while (pvt->rule) {
+ ng = pvt->rule->inst->ng;
+ if ((*ng->next)(ng, host, user, domain) == 1)
+ return (1);
+ if (!(pvt->rule->flags & IRS_CONTINUE))
+ break;
+ pvt->rule = pvt->rule->next;
+ if (pvt->rule) {
+ ng = pvt->rule->inst->ng;
+ (*ng->rewind)(ng, pvt->curgroup);
+ }
+ }
+ return (0);
+}
+
+static int
+ng_test(struct irs_ng *this, const char *name,
+ const char *user, const char *host, const char *domain)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct irs_ng *ng;
+ int rval;
+
+ rval = 0;
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ ng = rule->inst->ng;
+ rval = (*ng->test)(ng, name, user, host, domain);
+ if (rval || !(rule->flags & IRS_CONTINUE))
+ break;
+ }
+ return (rval);
+}
+
+static void
+ng_rewind(struct irs_ng *this, const char *group) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_ng *ng;
+
+ pvt->rule = pvt->rules;
+ if (pvt->rule) {
+ if (pvt->curgroup)
+ free(pvt->curgroup);
+ pvt->curgroup = strdup(group);
+ ng = pvt->rule->inst->ng;
+ (*ng->rewind)(ng, pvt->curgroup);
+ }
+}
+
+static void
+ng_minimize(struct irs_ng *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+
+ for (rule = pvt->rules; rule != NULL; rule = rule->next) {
+ struct irs_ng *ng = rule->inst->ng;
+
+ (*ng->minimize)(ng);
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_nw.c b/usr/src/lib/libresolv2_joy/common/irs/gen_nw.c
new file mode 100644
index 0000000000..a84a83af98
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/gen_nw.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <resolv_joy.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "gen_p.h"
+
+/* Types */
+
+struct pvt {
+ struct irs_rule * rules;
+ struct irs_rule * rule;
+ struct __res_state * res;
+ void (*free_res)(void *);
+};
+
+/* Forward */
+
+static void nw_close(struct irs_nw*);
+static struct nwent * nw_next(struct irs_nw *);
+static struct nwent * nw_byname(struct irs_nw *, const char *, int);
+static struct nwent * nw_byaddr(struct irs_nw *, void *, int, int);
+static void nw_rewind(struct irs_nw *);
+static void nw_minimize(struct irs_nw *);
+static struct __res_state * nw_res_get(struct irs_nw *this);
+static void nw_res_set(struct irs_nw *this,
+ struct __res_state *res,
+ void (*free_res)(void *));
+
+static int init(struct irs_nw *this);
+
+/* Public */
+
+struct irs_nw *
+irs_gen_nw(struct irs_acc *this) {
+ struct gen_p *accpvt = (struct gen_p *)this->private;
+ struct irs_nw *nw;
+ struct pvt *pvt;
+
+ if (!(pvt = memget(sizeof *pvt))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ if (!(nw = memget(sizeof *nw))) {
+ memput(pvt, sizeof *pvt);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(nw, 0x5e, sizeof *nw);
+ pvt->rules = accpvt->map_rules[irs_nw];
+ pvt->rule = pvt->rules;
+ nw->private = pvt;
+ nw->close = nw_close;
+ nw->next = nw_next;
+ nw->byname = nw_byname;
+ nw->byaddr = nw_byaddr;
+ nw->rewind = nw_rewind;
+ nw->minimize = nw_minimize;
+ nw->res_get = nw_res_get;
+ nw->res_set = nw_res_set;
+ return (nw);
+}
+
+/* Methods */
+
+static void
+nw_close(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ nw_minimize(this);
+
+ if (pvt->res && pvt->free_res)
+ (*pvt->free_res)(pvt->res);
+
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct nwent *
+nw_next(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct nwent *rval;
+ struct irs_nw *nw;
+
+ if (init(this) == -1)
+ return(NULL);
+
+ while (pvt->rule) {
+ nw = pvt->rule->inst->nw;
+ rval = (*nw->next)(nw);
+ if (rval)
+ return (rval);
+ if (!(pvt->rules->flags & IRS_CONTINUE))
+ break;
+ pvt->rule = pvt->rule->next;
+ if (pvt->rule) {
+ nw = pvt->rule->inst->nw;
+ (*nw->rewind)(nw);
+ }
+ }
+ return (NULL);
+}
+
+static struct nwent *
+nw_byname(struct irs_nw *this, const char *name, int type) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct nwent *rval;
+ struct irs_nw *nw;
+
+ if (init(this) == -1)
+ return(NULL);
+
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ nw = rule->inst->nw;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ rval = (*nw->byname)(nw, name, type);
+ if (rval != NULL)
+ return (rval);
+ if (pvt->res->res_h_errno != TRY_AGAIN &&
+ !(rule->flags & IRS_CONTINUE))
+ break;
+ }
+ return (NULL);
+}
+
+static struct nwent *
+nw_byaddr(struct irs_nw *this, void *net, int length, int type) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct nwent *rval;
+ struct irs_nw *nw;
+
+ if (init(this) == -1)
+ return(NULL);
+
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ nw = rule->inst->nw;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ rval = (*nw->byaddr)(nw, net, length, type);
+ if (rval != NULL)
+ return (rval);
+ if (pvt->res->res_h_errno != TRY_AGAIN &&
+ !(rule->flags & IRS_CONTINUE))
+ break;
+ }
+ return (NULL);
+}
+
+static void
+nw_rewind(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_nw *nw;
+
+ pvt->rule = pvt->rules;
+ if (pvt->rule) {
+ nw = pvt->rule->inst->nw;
+ (*nw->rewind)(nw);
+ }
+}
+
+static void
+nw_minimize(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+
+ if (pvt->res)
+ res_nclose(pvt->res);
+ for (rule = pvt->rules; rule != NULL; rule = rule->next) {
+ struct irs_nw *nw = rule->inst->nw;
+
+ (*nw->minimize)(nw);
+ }
+}
+
+static struct __res_state *
+nw_res_get(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (!res) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(res, 0, sizeof *res);
+ nw_res_set(this, res, free);
+ }
+
+ return (pvt->res);
+}
+
+static void
+nw_res_set(struct irs_nw *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+
+ if (pvt->res && pvt->free_res) {
+ res_nclose(pvt->res);
+ (*pvt->free_res)(pvt->res);
+ }
+
+ pvt->res = res;
+ pvt->free_res = free_res;
+
+ for (rule = pvt->rules; rule != NULL; rule = rule->next) {
+ struct irs_nw *nw = rule->inst->nw;
+
+ (*nw->res_set)(nw, pvt->res, NULL);
+ }
+}
+
+static int
+init(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res && !nw_res_get(this))
+ return (-1);
+ if (((pvt->res->options & RES_INIT) == 0U) &&
+ res_ninit(pvt->res) == -1)
+ return (-1);
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_p.h b/usr/src/lib/libresolv2_joy/common/irs/gen_p.h
new file mode 100644
index 0000000000..1adc5909bb
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/gen_p.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: gen_p.h,v 1.3 2005/04/27 04:56:23 sra Exp $
+ */
+
+/*! \file
+ * Notes:
+ * We hope to create a complete set of thread-safe entry points someday,
+ * which will mean a set of getXbyY() functions that take as an argument
+ * a pointer to the map class, which will have a pointer to the private
+ * data, which will be used preferentially to the static variables that
+ * are necessary to support the "classic" interface. This "classic"
+ * interface will then be reimplemented as stubs on top of the thread
+ * safe modules, and will keep the map class pointers as their only
+ * static data. HOWEVER, we are not there yet. So while we will call
+ * the just-barely-converted map class methods with map class pointers,
+ * right now they probably all still use statics. We're not fooling
+ * anybody, and we're not trying to (yet).
+ */
+
+#ifndef _GEN_P_H_INCLUDED
+#define _GEN_P_H_INCLUDED
+
+/*%
+ * These are the access methods.
+ */
+enum irs_acc_id {
+ irs_lcl, /*%< Local. */
+ irs_dns, /*%< DNS or Hesiod. */
+ irs_nis, /*%< Sun NIS ("YP"). */
+ irs_irp, /*%< IR protocol. */
+ irs_nacc
+};
+
+/*%
+ * These are the map types.
+ */
+enum irs_map_id {
+ irs_gr, /*%< "group" */
+ irs_pw, /*%< "passwd" */
+ irs_sv, /*%< "services" */
+ irs_pr, /*%< "protocols" */
+ irs_ho, /*%< "hosts" */
+ irs_nw, /*%< "networks" */
+ irs_ng, /*%< "netgroup" */
+ irs_nmap
+};
+
+/*%
+ * This is an accessor instance.
+ */
+struct irs_inst {
+ struct irs_acc *acc;
+ struct irs_gr * gr;
+ struct irs_pw * pw;
+ struct irs_sv * sv;
+ struct irs_pr * pr;
+ struct irs_ho * ho;
+ struct irs_nw * nw;
+ struct irs_ng * ng;
+};
+
+/*%
+ * This is a search rule for some map type.
+ */
+struct irs_rule {
+ struct irs_rule * next;
+ struct irs_inst * inst;
+ int flags;
+};
+#define IRS_MERGE 0x0001 /*%< Don't stop if acc. has data? */
+#define IRS_CONTINUE 0x0002 /*%< Don't stop if acc. has no data? */
+/*
+ * This is the private data for a search access class.
+ */
+struct gen_p {
+ char * options;
+ struct irs_rule * map_rules[(int)irs_nmap];
+ struct irs_inst accessors[(int)irs_nacc];
+ struct __res_state * res;
+ void (*free_res) __P((void *));
+};
+
+/*
+ * Externs.
+ */
+
+extern struct irs_acc * irs_gen_acc __P((const char *, const char *conf_file));
+extern struct irs_gr * irs_gen_gr __P((struct irs_acc *));
+extern struct irs_pw * irs_gen_pw __P((struct irs_acc *));
+extern struct irs_sv * irs_gen_sv __P((struct irs_acc *));
+extern struct irs_pr * irs_gen_pr __P((struct irs_acc *));
+extern struct irs_ho * irs_gen_ho __P((struct irs_acc *));
+extern struct irs_nw * irs_gen_nw __P((struct irs_acc *));
+extern struct irs_ng * irs_gen_ng __P((struct irs_acc *));
+
+#endif /*_IRS_P_H_INCLUDED*/
diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_pr.c b/usr/src/lib/libresolv2_joy/common/irs/gen_pr.c
new file mode 100644
index 0000000000..0349debe44
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/gen_pr.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <resolv_joy.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "gen_p.h"
+
+/* Types */
+
+struct pvt {
+ struct irs_rule * rules;
+ struct irs_rule * rule;
+ struct __res_state * res;
+ void (*free_res)(void *);
+};
+
+/* Forward */
+
+static void pr_close(struct irs_pr*);
+static struct protoent * pr_next(struct irs_pr *);
+static struct protoent * pr_byname(struct irs_pr *, const char *);
+static struct protoent * pr_bynumber(struct irs_pr *, int);
+static void pr_rewind(struct irs_pr *);
+static void pr_minimize(struct irs_pr *);
+static struct __res_state * pr_res_get(struct irs_pr *);
+static void pr_res_set(struct irs_pr *,
+ struct __res_state *,
+ void (*)(void *));
+
+/* Public */
+
+struct irs_pr *
+irs_gen_pr(struct irs_acc *this) {
+ struct gen_p *accpvt = (struct gen_p *)this->private;
+ struct irs_pr *pr;
+ struct pvt *pvt;
+
+ if (!(pr = memget(sizeof *pr))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pr, 0x5e, sizeof *pr);
+ if (!(pvt = memget(sizeof *pvt))) {
+ memput(pr, sizeof *pr);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ pvt->rules = accpvt->map_rules[irs_pr];
+ pvt->rule = pvt->rules;
+ pr->private = pvt;
+ pr->close = pr_close;
+ pr->next = pr_next;
+ pr->byname = pr_byname;
+ pr->bynumber = pr_bynumber;
+ pr->rewind = pr_rewind;
+ pr->minimize = pr_minimize;
+ pr->res_get = pr_res_get;
+ pr->res_set = pr_res_set;
+ return (pr);
+}
+
+/* Methods */
+
+static void
+pr_close(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct protoent *
+pr_next(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct protoent *rval;
+ struct irs_pr *pr;
+
+ while (pvt->rule) {
+ pr = pvt->rule->inst->pr;
+ rval = (*pr->next)(pr);
+ if (rval)
+ return (rval);
+ if (!(pvt->rules->flags & IRS_CONTINUE))
+ break;
+ pvt->rule = pvt->rule->next;
+ if (pvt->rule) {
+ pr = pvt->rule->inst->pr;
+ (*pr->rewind)(pr);
+ }
+ }
+ return (NULL);
+}
+
+static struct protoent *
+pr_byname(struct irs_pr *this, const char *name) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct protoent *rval;
+ struct irs_pr *pr;
+
+ rval = NULL;
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ pr = rule->inst->pr;
+ rval = (*pr->byname)(pr, name);
+ if (rval || !(rule->flags & IRS_CONTINUE))
+ break;
+ }
+ return (rval);
+}
+
+static struct protoent *
+pr_bynumber(struct irs_pr *this, int proto) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct protoent *rval;
+ struct irs_pr *pr;
+
+ rval = NULL;
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ pr = rule->inst->pr;
+ rval = (*pr->bynumber)(pr, proto);
+ if (rval || !(rule->flags & IRS_CONTINUE))
+ break;
+ }
+ return (rval);
+}
+
+static void
+pr_rewind(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_pr *pr;
+
+ pvt->rule = pvt->rules;
+ if (pvt->rule) {
+ pr = pvt->rule->inst->pr;
+ (*pr->rewind)(pr);
+ }
+}
+
+static void
+pr_minimize(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+
+ for (rule = pvt->rules; rule != NULL; rule = rule->next) {
+ struct irs_pr *pr = rule->inst->pr;
+
+ (*pr->minimize)(pr);
+ }
+}
+
+static struct __res_state *
+pr_res_get(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (!res) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(res, 0, sizeof *res);
+ pr_res_set(this, res, free);
+ }
+
+ return (pvt->res);
+}
+
+static void
+pr_res_set(struct irs_pr *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+
+ if (pvt->res && pvt->free_res) {
+ res_nclose(pvt->res);
+ (*pvt->free_res)(pvt->res);
+ }
+
+ pvt->res = res;
+ pvt->free_res = free_res;
+
+ for (rule = pvt->rules; rule != NULL; rule = rule->next) {
+ struct irs_pr *pr = rule->inst->pr;
+
+ if (pr->res_set)
+ (*pr->res_set)(pr, pvt->res, NULL);
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/gen_sv.c b/usr/src/lib/libresolv2_joy/common/irs/gen_sv.c
new file mode 100644
index 0000000000..a6f29d2a0f
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/gen_sv.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "gen_p.h"
+
+/* Types */
+
+struct pvt {
+ struct irs_rule * rules;
+ struct irs_rule * rule;
+ struct __res_state * res;
+ void (*free_res)(void *);
+};
+
+/* Forward */
+
+static void sv_close(struct irs_sv*);
+static struct servent * sv_next(struct irs_sv *);
+static struct servent * sv_byname(struct irs_sv *, const char *,
+ const char *);
+static struct servent * sv_byport(struct irs_sv *, int, const char *);
+static void sv_rewind(struct irs_sv *);
+static void sv_minimize(struct irs_sv *);
+static struct __res_state * sv_res_get(struct irs_sv *);
+static void sv_res_set(struct irs_sv *,
+ struct __res_state *,
+ void (*)(void *));
+
+/* Public */
+
+struct irs_sv *
+irs_gen_sv(struct irs_acc *this) {
+ struct gen_p *accpvt = (struct gen_p *)this->private;
+ struct irs_sv *sv;
+ struct pvt *pvt;
+
+ if (!(sv = memget(sizeof *sv))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(sv, 0x5e, sizeof *sv);
+ if (!(pvt = memget(sizeof *pvt))) {
+ memput(sv, sizeof *sv);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ pvt->rules = accpvt->map_rules[irs_sv];
+ pvt->rule = pvt->rules;
+ sv->private = pvt;
+ sv->close = sv_close;
+ sv->next = sv_next;
+ sv->byname = sv_byname;
+ sv->byport = sv_byport;
+ sv->rewind = sv_rewind;
+ sv->minimize = sv_minimize;
+ sv->res_get = sv_res_get;
+ sv->res_set = sv_res_set;
+ return (sv);
+}
+
+/* Methods */
+
+static void
+sv_close(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct servent *
+sv_next(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct servent *rval;
+ struct irs_sv *sv;
+
+ while (pvt->rule) {
+ sv = pvt->rule->inst->sv;
+ rval = (*sv->next)(sv);
+ if (rval)
+ return (rval);
+ if (!(pvt->rule->flags & IRS_CONTINUE))
+ break;
+ pvt->rule = pvt->rule->next;
+ if (pvt->rule) {
+ sv = pvt->rule->inst->sv;
+ (*sv->rewind)(sv);
+ }
+ }
+ return (NULL);
+}
+
+static struct servent *
+sv_byname(struct irs_sv *this, const char *name, const char *proto) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct servent *rval;
+ struct irs_sv *sv;
+
+ rval = NULL;
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ sv = rule->inst->sv;
+ rval = (*sv->byname)(sv, name, proto);
+ if (rval || !(rule->flags & IRS_CONTINUE))
+ break;
+ }
+ return (rval);
+}
+
+static struct servent *
+sv_byport(struct irs_sv *this, int port, const char *proto) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+ struct servent *rval;
+ struct irs_sv *sv;
+
+ rval = NULL;
+ for (rule = pvt->rules; rule; rule = rule->next) {
+ sv = rule->inst->sv;
+ rval = (*sv->byport)(sv, port, proto);
+ if (rval || !(rule->flags & IRS_CONTINUE))
+ break;
+ }
+ return (rval);
+}
+
+static void
+sv_rewind(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_sv *sv;
+
+ pvt->rule = pvt->rules;
+ if (pvt->rule) {
+ sv = pvt->rule->inst->sv;
+ (*sv->rewind)(sv);
+ }
+}
+
+static void
+sv_minimize(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+
+ for (rule = pvt->rules; rule != NULL; rule = rule->next) {
+ struct irs_sv *sv = rule->inst->sv;
+
+ (*sv->minimize)(sv);
+ }
+}
+
+static struct __res_state *
+sv_res_get(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (!res) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(res, 0, sizeof *res);
+ sv_res_set(this, res, free);
+ }
+
+ return (pvt->res);
+}
+
+static void
+sv_res_set(struct irs_sv *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct irs_rule *rule;
+
+ if (pvt->res && pvt->free_res) {
+ res_nclose(pvt->res);
+ (*pvt->free_res)(pvt->res);
+ }
+
+ pvt->res = res;
+ pvt->free_res = free_res;
+
+ for (rule = pvt->rules; rule != NULL; rule = rule->next) {
+ struct irs_sv *sv = rule->inst->sv;
+
+ if (sv->res_set)
+ (*sv->res_set)(sv, pvt->res, NULL);
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/getaddrinfo.c b/usr/src/lib/libresolv2_joy/common/irs/getaddrinfo.c
new file mode 100644
index 0000000000..19bbbea854
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/getaddrinfo.c
@@ -0,0 +1,1253 @@
+/* $KAME: getaddrinfo.c,v 1.14 2001/01/06 09:41:15 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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.
+ * 3. Neither the name of the project 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 PROJECT 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 PROJECT 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.
+ */
+
+/*! \file
+ * Issues to be discussed:
+ *\li Thread safe-ness must be checked.
+ *\li Return values. There are nonstandard return values defined and used
+ * in the source code. This is because RFC2553 is silent about which error
+ * code must be returned for which situation.
+ *\li IPv4 classful (shortened) form. RFC2553 is silent about it. XNET 5.2
+ * says to use inet_aton() to convert IPv4 numeric to binary (allows
+ * classful form as a result).
+ * current code - disallow classful form for IPv4 (due to use of inet_pton).
+ *\li freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is
+ * invalid.
+ * current code - SEGV on freeaddrinfo(NULL)
+ * Note:
+ *\li We use getipnodebyname() just for thread-safeness. There's no intent
+ * to let it do PF_UNSPEC (actually we never pass PF_UNSPEC to
+ * getipnodebyname().
+ *\li The code filters out AFs that are not supported by the kernel,
+ * when globbing NULL hostname (to loopback, or wildcard). Is it the right
+ * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG
+ * in ai_flags?
+ *\li (post-2553) semantics of AI_ADDRCONFIG itself is too vague.
+ * (1) what should we do against numeric hostname (2) what should we do
+ * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready?
+ * non-loopback address configured? global address configured?
+ * \par Additional Issue:
+ * To avoid search order issue, we have a big amount of code duplicate
+ * from gethnamaddr.c and some other places. The issues that there's no
+ * lower layer function to lookup "IPv4 or IPv6" record. Calling
+ * gethostbyname2 from getaddrinfo will end up in wrong search order, as
+ * follows:
+ * \li The code makes use of following calls when asked to resolver with
+ * ai_family = PF_UNSPEC:
+ *\code getipnodebyname(host, AF_INET6);
+ * getipnodebyname(host, AF_INET);
+ *\endcode
+ * \li This will result in the following queries if the node is configure to
+ * prefer /etc/hosts than DNS:
+ *\code
+ * lookup /etc/hosts for IPv6 address
+ * lookup DNS for IPv6 address
+ * lookup /etc/hosts for IPv4 address
+ * lookup DNS for IPv4 address
+ *\endcode
+ * which may not meet people's requirement.
+ * \li The right thing to happen is to have underlying layer which does
+ * PF_UNSPEC lookup (lookup both) and return chain of addrinfos.
+ * This would result in a bit of code duplicate with _dns_ghbyname() and
+ * friends.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <stdarg.h>
+
+#include <irs.h>
+#include <isc/assertions.h>
+
+#include "port_after.h"
+
+#include "irs_data.h"
+
+#define SUCCESS 0
+#define ANY 0
+#define YES 1
+#define NO 0
+
+static const char in_addrany[] = { 0, 0, 0, 0 };
+static const char in6_addrany[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+static const char in_loopback[] = { 127, 0, 0, 1 };
+static const char in6_loopback[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
+};
+
+static const struct afd {
+ int a_af;
+ int a_addrlen;
+ int a_socklen;
+ int a_off;
+ const char *a_addrany;
+ const char *a_loopback;
+ int a_scoped;
+} afdl [] = {
+ {PF_INET6, sizeof(struct in6_addr),
+ sizeof(struct sockaddr_in6),
+ offsetof(struct sockaddr_in6, sin6_addr),
+ in6_addrany, in6_loopback, 1},
+ {PF_INET, sizeof(struct in_addr),
+ sizeof(struct sockaddr_in),
+ offsetof(struct sockaddr_in, sin_addr),
+ in_addrany, in_loopback, 0},
+ {0, 0, 0, 0, NULL, NULL, 0},
+};
+
+struct explore {
+ int e_af;
+ int e_socktype;
+ int e_protocol;
+ const char *e_protostr;
+ int e_wild;
+#define WILD_AF(ex) ((ex)->e_wild & 0x01)
+#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02)
+#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04)
+};
+
+static const struct explore explore[] = {
+#if 0
+ { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 },
+#endif
+ { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
+ { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
+ { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 },
+ { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
+ { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
+ { PF_INET, SOCK_RAW, ANY, NULL, 0x05 },
+ { -1, 0, 0, NULL, 0 },
+};
+
+#define PTON_MAX 16
+
+static int str_isnumber __P((const char *));
+static int explore_fqdn __P((const struct addrinfo *, const char *,
+ const char *, struct addrinfo **));
+static int explore_copy __P((const struct addrinfo *, const struct addrinfo *,
+ struct addrinfo **));
+static int explore_null __P((const struct addrinfo *,
+ const char *, struct addrinfo **));
+static int explore_numeric __P((const struct addrinfo *, const char *,
+ const char *, struct addrinfo **));
+static int explore_numeric_scope __P((const struct addrinfo *, const char *,
+ const char *, struct addrinfo **));
+static int get_canonname __P((const struct addrinfo *,
+ struct addrinfo *, const char *));
+static struct addrinfo *get_ai __P((const struct addrinfo *,
+ const struct afd *, const char *));
+static struct addrinfo *copy_ai __P((const struct addrinfo *));
+static int get_portmatch __P((const struct addrinfo *, const char *));
+static int get_port __P((const struct addrinfo *, const char *, int));
+static const struct afd *find_afd __P((int));
+static int addrconfig __P((int));
+static int ip6_str2scopeid __P((char *, struct sockaddr_in6 *,
+ u_int32_t *scopeidp));
+static struct net_data *init __P((void));
+
+struct addrinfo *hostent2addrinfo __P((struct hostent *,
+ const struct addrinfo *));
+struct addrinfo *addr2addrinfo __P((const struct addrinfo *,
+ const char *));
+
+#if 0
+static const char *ai_errlist[] = {
+ "Success",
+ "Address family for hostname not supported", /*%< EAI_ADDRFAMILY */
+ "Temporary failure in name resolution", /*%< EAI_AGAIN */
+ "Invalid value for ai_flags", /*%< EAI_BADFLAGS */
+ "Non-recoverable failure in name resolution", /*%< EAI_FAIL */
+ "ai_family not supported", /*%< EAI_FAMILY */
+ "Memory allocation failure", /*%< EAI_MEMORY */
+ "No address associated with hostname", /*%< EAI_NODATA */
+ "hostname nor servname provided, or not known", /*%< EAI_NONAME */
+ "servname not supported for ai_socktype", /*%< EAI_SERVICE */
+ "ai_socktype not supported", /*%< EAI_SOCKTYPE */
+ "System error returned in errno", /*%< EAI_SYSTEM */
+ "Invalid value for hints", /*%< EAI_BADHINTS */
+ "Resolved protocol is unknown", /*%< EAI_PROTOCOL */
+ "Unknown error", /*%< EAI_MAX */
+};
+#endif
+
+/* XXX macros that make external reference is BAD. */
+
+#define GET_AI(ai, afd, addr) \
+do { \
+ /* external reference: pai, error, and label free */ \
+ (ai) = get_ai(pai, (afd), (addr)); \
+ if ((ai) == NULL) { \
+ error = EAI_MEMORY; \
+ goto free; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define GET_PORT(ai, serv) \
+do { \
+ /* external reference: error and label free */ \
+ error = get_port((ai), (serv), 0); \
+ if (error != 0) \
+ goto free; \
+} while (/*CONSTCOND*/0)
+
+#define GET_CANONNAME(ai, str) \
+do { \
+ /* external reference: pai, error and label free */ \
+ error = get_canonname(pai, (ai), (str)); \
+ if (error != 0) \
+ goto free; \
+} while (/*CONSTCOND*/0)
+
+#ifndef SOLARIS2
+#define SETERROR(err) \
+do { \
+ /* external reference: error, and label bad */ \
+ error = (err); \
+ goto bad; \
+ /*NOTREACHED*/ \
+} while (/*CONSTCOND*/0)
+#else
+#define SETERROR(err) \
+do { \
+ /* external reference: error, and label bad */ \
+ error = (err); \
+ if (error == error) \
+ goto bad; \
+} while (/*CONSTCOND*/0)
+#endif
+
+
+#define MATCH_FAMILY(x, y, w) \
+ ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC)))
+#define MATCH(x, y, w) \
+ ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY)))
+
+#if 0 /*%< bind8 has its own version */
+char *
+gai_strerror(ecode)
+ int ecode;
+{
+ if (ecode < 0 || ecode > EAI_MAX)
+ ecode = EAI_MAX;
+ return ai_errlist[ecode];
+}
+#endif
+
+void
+freeaddrinfo(ai)
+ struct addrinfo *ai;
+{
+ struct addrinfo *next;
+
+ do {
+ next = ai->ai_next;
+ if (ai->ai_canonname)
+ free(ai->ai_canonname);
+ /* no need to free(ai->ai_addr) */
+ free(ai);
+ ai = next;
+ } while (ai);
+}
+
+static int
+str_isnumber(p)
+ const char *p;
+{
+ char *ep;
+
+ if (*p == '\0')
+ return NO;
+ ep = NULL;
+ errno = 0;
+ (void)strtoul(p, &ep, 10);
+ if (errno == 0 && ep && *ep == '\0')
+ return YES;
+ else
+ return NO;
+}
+
+int
+getaddrinfo(hostname, servname, hints, res)
+ const char *hostname, *servname;
+ const struct addrinfo *hints;
+ struct addrinfo **res;
+{
+ struct addrinfo sentinel;
+ struct addrinfo *cur;
+ int error = 0;
+ struct addrinfo ai, ai0, *afai = NULL;
+ struct addrinfo *pai;
+ const struct explore *ex;
+
+ memset(&sentinel, 0, sizeof(sentinel));
+ cur = &sentinel;
+ pai = &ai;
+ pai->ai_flags = 0;
+ pai->ai_family = PF_UNSPEC;
+ pai->ai_socktype = ANY;
+ pai->ai_protocol = ANY;
+#if defined(sun) && defined(_SOCKLEN_T) && defined(__sparcv9)
+ /*
+ * clear _ai_pad to preserve binary
+ * compatibility with previously compiled 64-bit
+ * applications in a pre-SUSv3 environment by
+ * guaranteeing the upper 32-bits are empty.
+ */
+ pai->_ai_pad = 0;
+#endif
+ pai->ai_addrlen = 0;
+ pai->ai_canonname = NULL;
+ pai->ai_addr = NULL;
+ pai->ai_next = NULL;
+
+ if (hostname == NULL && servname == NULL)
+ return EAI_NONAME;
+ if (hints) {
+ /* error check for hints */
+ if (hints->ai_addrlen || hints->ai_canonname ||
+ hints->ai_addr || hints->ai_next)
+ SETERROR(EAI_BADHINTS); /*%< xxx */
+ if (hints->ai_flags & ~AI_MASK)
+ SETERROR(EAI_BADFLAGS);
+ switch (hints->ai_family) {
+ case PF_UNSPEC:
+ case PF_INET:
+ case PF_INET6:
+ break;
+ default:
+ SETERROR(EAI_FAMILY);
+ }
+ memcpy(pai, hints, sizeof(*pai));
+
+#if defined(sun) && defined(_SOCKLEN_T) && defined(__sparcv9)
+ /*
+ * We need to clear _ai_pad to preserve binary
+ * compatibility. See prior comment.
+ */
+ pai->_ai_pad = 0;
+#endif
+ /*
+ * if both socktype/protocol are specified, check if they
+ * are meaningful combination.
+ */
+ if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
+ for (ex = explore; ex->e_af >= 0; ex++) {
+ if (pai->ai_family != ex->e_af)
+ continue;
+ if (ex->e_socktype == ANY)
+ continue;
+ if (ex->e_protocol == ANY)
+ continue;
+ if (pai->ai_socktype == ex->e_socktype &&
+ pai->ai_protocol != ex->e_protocol) {
+ SETERROR(EAI_BADHINTS);
+ }
+ }
+ }
+ }
+
+ /*
+ * post-2553: AI_ALL and AI_V4MAPPED are effective only against
+ * AF_INET6 query. They needs to be ignored if specified in other
+ * occassions.
+ */
+ switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) {
+ case AI_V4MAPPED:
+ case AI_ALL | AI_V4MAPPED:
+ if (pai->ai_family != AF_INET6)
+ pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED);
+ break;
+ case AI_ALL:
+#if 1
+ /* illegal */
+ SETERROR(EAI_BADFLAGS);
+#else
+ pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED);
+ break;
+#endif
+ }
+
+ /*
+ * check for special cases. (1) numeric servname is disallowed if
+ * socktype/protocol are left unspecified. (2) servname is disallowed
+ * for raw and other inet{,6} sockets.
+ */
+ if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)
+#ifdef PF_INET6
+ || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
+#endif
+ ) {
+ ai0 = *pai; /* backup *pai */
+
+ if (pai->ai_family == PF_UNSPEC) {
+#ifdef PF_INET6
+ pai->ai_family = PF_INET6;
+#else
+ pai->ai_family = PF_INET;
+#endif
+ }
+ error = get_portmatch(pai, servname);
+ if (error)
+ SETERROR(error);
+
+ *pai = ai0;
+ }
+
+ ai0 = *pai;
+
+ /* NULL hostname, or numeric hostname */
+ for (ex = explore; ex->e_af >= 0; ex++) {
+ *pai = ai0;
+
+ if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
+ continue;
+ if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex)))
+ continue;
+ if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex)))
+ continue;
+
+ if (pai->ai_family == PF_UNSPEC)
+ pai->ai_family = ex->e_af;
+ if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
+ pai->ai_socktype = ex->e_socktype;
+ if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
+ pai->ai_protocol = ex->e_protocol;
+
+ /*
+ * if the servname does not match socktype/protocol, ignore it.
+ */
+ if (get_portmatch(pai, servname) != 0)
+ continue;
+
+ if (hostname == NULL) {
+ /*
+ * filter out AFs that are not supported by the kernel
+ * XXX errno?
+ */
+ if (!addrconfig(pai->ai_family))
+ continue;
+ error = explore_null(pai, servname, &cur->ai_next);
+ } else
+ error = explore_numeric_scope(pai, hostname, servname,
+ &cur->ai_next);
+
+ if (error)
+ goto free;
+
+ while (cur && cur->ai_next)
+ cur = cur->ai_next;
+ }
+
+ /*
+ * XXX
+ * If numreic representation of AF1 can be interpreted as FQDN
+ * representation of AF2, we need to think again about the code below.
+ */
+ if (sentinel.ai_next)
+ goto good;
+
+ if (pai->ai_flags & AI_NUMERICHOST)
+ SETERROR(EAI_NONAME);
+ if (hostname == NULL)
+ SETERROR(EAI_NONAME);
+
+ /*
+ * hostname as alphabetical name.
+ * We'll make sure that
+ * - if returning addrinfo list is empty, return non-zero error
+ * value (already known one or EAI_NONAME).
+ * - otherwise,
+ * + if we haven't had any errors, return 0 (i.e. success).
+ * + if we've had an error, free the list and return the error.
+ * without any assumption on the behavior of explore_fqdn().
+ */
+
+ /* first, try to query DNS for all possible address families. */
+ *pai = ai0;
+ error = explore_fqdn(pai, hostname, servname, &afai);
+ if (error) {
+ if (afai != NULL)
+ freeaddrinfo(afai);
+ goto free;
+ }
+ if (afai == NULL) {
+ error = EAI_NONAME; /*%< we've had no errors. */
+ goto free;
+ }
+
+ /*
+ * we would like to prefer AF_INET6 than AF_INET, so we'll make an
+ * outer loop by AFs.
+ */
+ for (ex = explore; ex->e_af >= 0; ex++) {
+ *pai = ai0;
+
+ if (pai->ai_family == PF_UNSPEC)
+ pai->ai_family = ex->e_af;
+
+ if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
+ continue;
+ if (!MATCH(pai->ai_socktype, ex->e_socktype,
+ WILD_SOCKTYPE(ex))) {
+ continue;
+ }
+ if (!MATCH(pai->ai_protocol, ex->e_protocol,
+ WILD_PROTOCOL(ex))) {
+ continue;
+ }
+
+#ifdef AI_ADDRCONFIG
+ /*
+ * If AI_ADDRCONFIG is specified, check if we are
+ * expected to return the address family or not.
+ */
+ if ((pai->ai_flags & AI_ADDRCONFIG) != 0 &&
+ !addrconfig(pai->ai_family))
+ continue;
+#endif
+
+ if (pai->ai_family == PF_UNSPEC)
+ pai->ai_family = ex->e_af;
+ if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
+ pai->ai_socktype = ex->e_socktype;
+ if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
+ pai->ai_protocol = ex->e_protocol;
+
+ /*
+ * if the servname does not match socktype/protocol, ignore it.
+ */
+ if (get_portmatch(pai, servname) != 0)
+ continue;
+
+ if ((error = explore_copy(pai, afai, &cur->ai_next)) != 0) {
+ freeaddrinfo(afai);
+ goto free;
+ }
+
+ while (cur && cur->ai_next)
+ cur = cur->ai_next;
+ }
+
+ freeaddrinfo(afai); /*%< afai must not be NULL at this point. */
+
+ if (sentinel.ai_next) {
+good:
+ *res = sentinel.ai_next;
+ return(SUCCESS);
+ } else {
+ /*
+ * All the process succeeded, but we've had an empty list.
+ * This can happen if the given hints do not match our
+ * candidates.
+ */
+ error = EAI_NONAME;
+ }
+
+free:
+bad:
+ if (sentinel.ai_next)
+ freeaddrinfo(sentinel.ai_next);
+ *res = NULL;
+ return(error);
+}
+
+/*%
+ * FQDN hostname, DNS lookup
+ */
+static int
+explore_fqdn(pai, hostname, servname, res)
+ const struct addrinfo *pai;
+ const char *hostname;
+ const char *servname;
+ struct addrinfo **res;
+{
+ struct addrinfo *result;
+ struct addrinfo *cur;
+ struct net_data *net_data = init();
+ struct irs_ho *ho;
+ int error = 0;
+ char tmp[NS_MAXDNAME];
+ const char *cp;
+
+ INSIST(res != NULL && *res == NULL);
+
+ /*
+ * if the servname does not match socktype/protocol, ignore it.
+ */
+ if (get_portmatch(pai, servname) != 0)
+ return(0);
+
+ if (!net_data || !(ho = net_data->ho))
+ return(0);
+#if 0 /*%< XXX (notyet) */
+ if (net_data->ho_stayopen && net_data->ho_last &&
+ net_data->ho_last->h_addrtype == af) {
+ if (ns_samename(name, net_data->ho_last->h_name) == 1)
+ return (net_data->ho_last);
+ for (hap = net_data->ho_last->h_aliases; hap && *hap; hap++)
+ if (ns_samename(name, *hap) == 1)
+ return (net_data->ho_last);
+ }
+#endif
+ if (!strchr(hostname, '.') &&
+ (cp = res_hostalias(net_data->res, hostname,
+ tmp, sizeof(tmp))))
+ hostname = cp;
+ result = (*ho->addrinfo)(ho, hostname, pai);
+ if (!net_data->ho_stayopen) {
+ (*ho->minimize)(ho);
+ }
+ if (result == NULL) {
+ int e = h_errno;
+
+ switch(e) {
+ case NETDB_INTERNAL:
+ error = EAI_SYSTEM;
+ break;
+ case TRY_AGAIN:
+ error = EAI_AGAIN;
+ break;
+ case NO_RECOVERY:
+ error = EAI_FAIL;
+ break;
+ case HOST_NOT_FOUND:
+ case NO_DATA:
+ error = EAI_NONAME;
+ break;
+ default:
+ case NETDB_SUCCESS: /*%< should be impossible... */
+ error = EAI_NONAME;
+ break;
+ }
+ goto free;
+ }
+
+ for (cur = result; cur; cur = cur->ai_next) {
+ GET_PORT(cur, servname); /*%< XXX: redundant lookups... */
+ /* canonname should already be filled. */
+ }
+
+ *res = result;
+
+ return(0);
+
+free:
+ if (result)
+ freeaddrinfo(result);
+ return error;
+}
+
+static int
+explore_copy(pai, src0, res)
+ const struct addrinfo *pai; /*%< seed */
+ const struct addrinfo *src0; /*%< source */
+ struct addrinfo **res;
+{
+ int error;
+ struct addrinfo sentinel, *cur;
+ const struct addrinfo *src;
+
+ error = 0;
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+
+ for (src = src0; src != NULL; src = src->ai_next) {
+ if (src->ai_family != pai->ai_family)
+ continue;
+
+ cur->ai_next = copy_ai(src);
+ if (!cur->ai_next) {
+ error = EAI_MEMORY;
+ goto fail;
+ }
+
+ cur->ai_next->ai_socktype = pai->ai_socktype;
+ cur->ai_next->ai_protocol = pai->ai_protocol;
+ cur = cur->ai_next;
+ }
+
+ *res = sentinel.ai_next;
+ return 0;
+
+fail:
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+}
+
+/*%
+ * hostname == NULL.
+ * passive socket -> anyaddr (0.0.0.0 or ::)
+ * non-passive socket -> localhost (127.0.0.1 or ::1)
+ */
+static int
+explore_null(pai, servname, res)
+ const struct addrinfo *pai;
+ const char *servname;
+ struct addrinfo **res;
+{
+ const struct afd *afd;
+ struct addrinfo *cur;
+ struct addrinfo sentinel;
+ int error;
+
+ *res = NULL;
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL)
+ return 0;
+
+ if (pai->ai_flags & AI_PASSIVE) {
+ GET_AI(cur->ai_next, afd, afd->a_addrany);
+ /* xxx meaningless?
+ * GET_CANONNAME(cur->ai_next, "anyaddr");
+ */
+ GET_PORT(cur->ai_next, servname);
+ } else {
+ GET_AI(cur->ai_next, afd, afd->a_loopback);
+ /* xxx meaningless?
+ * GET_CANONNAME(cur->ai_next, "localhost");
+ */
+ GET_PORT(cur->ai_next, servname);
+ }
+ cur = cur->ai_next;
+
+ *res = sentinel.ai_next;
+ return 0;
+
+free:
+ if (sentinel.ai_next)
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+}
+
+/*%
+ * numeric hostname
+ */
+static int
+explore_numeric(pai, hostname, servname, res)
+ const struct addrinfo *pai;
+ const char *hostname;
+ const char *servname;
+ struct addrinfo **res;
+{
+ const struct afd *afd;
+ struct addrinfo *cur;
+ struct addrinfo sentinel;
+ int error;
+ char pton[PTON_MAX];
+
+ *res = NULL;
+ sentinel.ai_next = NULL;
+ cur = &sentinel;
+
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL)
+ return 0;
+
+ switch (afd->a_af) {
+#if 0 /*X/Open spec*/
+ case AF_INET:
+ if (inet_aton(hostname, (struct in_addr *)pton) == 1) {
+ if (pai->ai_family == afd->a_af ||
+ pai->ai_family == PF_UNSPEC /*?*/) {
+ GET_AI(cur->ai_next, afd, pton);
+ GET_PORT(cur->ai_next, servname);
+ while (cur->ai_next)
+ cur = cur->ai_next;
+ } else
+ SETERROR(EAI_FAMILY); /*xxx*/
+ }
+ break;
+#endif
+ default:
+ if (inet_pton(afd->a_af, hostname, pton) == 1) {
+ if (pai->ai_family == afd->a_af ||
+ pai->ai_family == PF_UNSPEC /*?*/) {
+ GET_AI(cur->ai_next, afd, pton);
+ GET_PORT(cur->ai_next, servname);
+ while (cur->ai_next)
+ cur = cur->ai_next;
+ } else
+ SETERROR(EAI_FAMILY); /*xxx*/
+ }
+ break;
+ }
+
+ *res = sentinel.ai_next;
+ return 0;
+
+free:
+bad:
+ if (sentinel.ai_next)
+ freeaddrinfo(sentinel.ai_next);
+ return error;
+}
+
+/*%
+ * numeric hostname with scope
+ */
+static int
+explore_numeric_scope(pai, hostname, servname, res)
+ const struct addrinfo *pai;
+ const char *hostname;
+ const char *servname;
+ struct addrinfo **res;
+{
+#ifndef SCOPE_DELIMITER
+ return explore_numeric(pai, hostname, servname, res);
+#else
+ const struct afd *afd;
+ struct addrinfo *cur;
+ int error;
+ char *cp, *hostname2 = NULL, *scope, *addr;
+ struct sockaddr_in6 *sin6;
+
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL)
+ return 0;
+
+ if (!afd->a_scoped)
+ return explore_numeric(pai, hostname, servname, res);
+
+ cp = strchr(hostname, SCOPE_DELIMITER);
+ if (cp == NULL)
+ return explore_numeric(pai, hostname, servname, res);
+
+ /*
+ * Handle special case of <scoped_address><delimiter><scope id>
+ */
+ hostname2 = strdup(hostname);
+ if (hostname2 == NULL)
+ return EAI_MEMORY;
+ /* terminate at the delimiter */
+ hostname2[cp - hostname] = '\0';
+ addr = hostname2;
+ scope = cp + 1;
+
+ error = explore_numeric(pai, addr, servname, res);
+ if (error == 0) {
+ u_int32_t scopeid = 0;
+
+ for (cur = *res; cur; cur = cur->ai_next) {
+ if (cur->ai_family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr;
+ if (!ip6_str2scopeid(scope, sin6, &scopeid)) {
+ free(hostname2);
+ return(EAI_NONAME); /*%< XXX: is return OK? */
+ }
+#ifdef HAVE_SIN6_SCOPE_ID
+ sin6->sin6_scope_id = scopeid;
+#endif
+ }
+ }
+
+ free(hostname2);
+
+ return error;
+#endif
+}
+
+static int
+get_canonname(pai, ai, str)
+ const struct addrinfo *pai;
+ struct addrinfo *ai;
+ const char *str;
+{
+ if ((pai->ai_flags & AI_CANONNAME) != 0) {
+ ai->ai_canonname = (char *)malloc(strlen(str) + 1);
+ if (ai->ai_canonname == NULL)
+ return EAI_MEMORY;
+ strcpy(ai->ai_canonname, str);
+ }
+ return 0;
+}
+
+static struct addrinfo *
+get_ai(pai, afd, addr)
+ const struct addrinfo *pai;
+ const struct afd *afd;
+ const char *addr;
+{
+ char *p;
+ struct addrinfo *ai;
+
+ ai = (struct addrinfo *)malloc(sizeof(struct addrinfo)
+ + (afd->a_socklen));
+ if (ai == NULL)
+ return NULL;
+
+ memcpy(ai, pai, sizeof(struct addrinfo));
+ ai->ai_addr = (struct sockaddr *)(void *)(ai + 1);
+ memset(ai->ai_addr, 0, (size_t)afd->a_socklen);
+#ifdef HAVE_SA_LEN
+ ai->ai_addr->sa_len = afd->a_socklen;
+#endif
+ ai->ai_addrlen = afd->a_socklen;
+ ai->ai_addr->sa_family = ai->ai_family = afd->a_af;
+ p = (char *)(void *)(ai->ai_addr);
+ memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen);
+ return ai;
+}
+
+/* XXX need to malloc() the same way we do from other functions! */
+static struct addrinfo *
+copy_ai(pai)
+ const struct addrinfo *pai;
+{
+ struct addrinfo *ai;
+ size_t l;
+
+ l = sizeof(*ai) + pai->ai_addrlen;
+ if ((ai = (struct addrinfo *)malloc(l)) == NULL)
+ return NULL;
+ memset(ai, 0, l);
+ memcpy(ai, pai, sizeof(*ai));
+ ai->ai_addr = (struct sockaddr *)(void *)(ai + 1);
+ memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen);
+
+ if (pai->ai_canonname) {
+ l = strlen(pai->ai_canonname) + 1;
+ if ((ai->ai_canonname = malloc(l)) == NULL) {
+ free(ai);
+ return NULL;
+ }
+ strcpy(ai->ai_canonname, pai->ai_canonname); /* (checked) */
+ } else {
+ /* just to make sure */
+ ai->ai_canonname = NULL;
+ }
+
+ ai->ai_next = NULL;
+
+ return ai;
+}
+
+static int
+get_portmatch(const struct addrinfo *ai, const char *servname) {
+
+ /* get_port does not touch first argument. when matchonly == 1. */
+ /* LINTED const cast */
+ return get_port((const struct addrinfo *)ai, servname, 1);
+}
+
+static int
+get_port(const struct addrinfo *ai, const char *servname, int matchonly) {
+ const char *proto;
+ struct servent *sp;
+ int port;
+ int allownumeric;
+
+ if (servname == NULL)
+ return 0;
+ switch (ai->ai_family) {
+ case AF_INET:
+#ifdef AF_INET6
+ case AF_INET6:
+#endif
+ break;
+ default:
+ return 0;
+ }
+
+ switch (ai->ai_socktype) {
+ case SOCK_RAW:
+ return EAI_SERVICE;
+ case SOCK_DGRAM:
+ case SOCK_STREAM:
+ allownumeric = 1;
+ break;
+ case ANY:
+ switch (ai->ai_family) {
+ case AF_INET:
+#ifdef AF_INET6
+ case AF_INET6:
+#endif
+ allownumeric = 1;
+ break;
+ default:
+ allownumeric = 0;
+ break;
+ }
+ break;
+ default:
+ return EAI_SOCKTYPE;
+ }
+
+ if (str_isnumber(servname)) {
+ if (!allownumeric)
+ return EAI_SERVICE;
+ port = atoi(servname);
+ if (port < 0 || port > 65535)
+ return EAI_SERVICE;
+ port = htons(port);
+ } else {
+ switch (ai->ai_socktype) {
+ case SOCK_DGRAM:
+ proto = "udp";
+ break;
+ case SOCK_STREAM:
+ proto = "tcp";
+ break;
+ default:
+ proto = NULL;
+ break;
+ }
+
+ if ((sp = getservbyname(servname, proto)) == NULL)
+ return EAI_SERVICE;
+ port = sp->s_port;
+ }
+
+ if (!matchonly) {
+ switch (ai->ai_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)(void *)
+ ai->ai_addr)->sin_port = port;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)(void *)
+ ai->ai_addr)->sin6_port = port;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct afd *
+find_afd(af)
+ int af;
+{
+ const struct afd *afd;
+
+ if (af == PF_UNSPEC)
+ return NULL;
+ for (afd = afdl; afd->a_af; afd++) {
+ if (afd->a_af == af)
+ return afd;
+ }
+ return NULL;
+}
+
+/*%
+ * post-2553: AI_ADDRCONFIG check. if we use getipnodeby* as backend, backend
+ * will take care of it.
+ * the semantics of AI_ADDRCONFIG is not defined well. we are not sure
+ * if the code is right or not.
+ */
+static int
+addrconfig(af)
+ int af;
+{
+ int s;
+
+ /* XXX errno */
+ s = socket(af, SOCK_DGRAM, 0);
+ if (s < 0) {
+ if (errno != EMFILE)
+ return 0;
+ } else
+ close(s);
+ return 1;
+}
+
+/* convert a string to a scope identifier. XXX: IPv6 specific */
+static int
+ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6,
+ u_int32_t *scopeidp)
+{
+ u_int32_t scopeid;
+ u_long lscopeid;
+ struct in6_addr *a6 = &sin6->sin6_addr;
+ char *ep;
+
+ /* empty scopeid portion is invalid */
+ if (*scope == '\0')
+ return (0);
+
+#ifdef USE_IFNAMELINKID
+ if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) ||
+ IN6_IS_ADDR_MC_NODELOCAL(a6)) {
+ /*
+ * Using interface names as link indices can be allowed
+ * only when we can assume a one-to-one mappings between
+ * links and interfaces. See comments in getnameinfo.c.
+ */
+ scopeid = if_nametoindex(scope);
+ if (scopeid == 0)
+ goto trynumeric;
+ *scopeidp = scopeid;
+ return (1);
+ }
+#endif
+
+ /* still unclear about literal, allow numeric only - placeholder */
+ if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6))
+ goto trynumeric;
+ if (IN6_IS_ADDR_MC_ORGLOCAL(a6))
+ goto trynumeric;
+ else
+ goto trynumeric; /*%< global */
+ /* try to convert to a numeric id as a last resort */
+trynumeric:
+ errno = 0;
+ lscopeid = strtoul(scope, &ep, 10);
+ scopeid = lscopeid & 0xffffffff;
+ if (errno == 0 && ep && *ep == '\0' && scopeid == lscopeid) {
+ *scopeidp = scopeid;
+ return (1);
+ } else
+ return (0);
+}
+
+struct addrinfo *
+hostent2addrinfo(hp, pai)
+ struct hostent *hp;
+ const struct addrinfo *pai;
+{
+ int i, af, error = 0;
+ char **aplist = NULL, *ap;
+ struct addrinfo sentinel, *cur;
+ const struct afd *afd;
+
+ af = hp->h_addrtype;
+ if (pai->ai_family != AF_UNSPEC && af != pai->ai_family)
+ return(NULL);
+
+ afd = find_afd(af);
+ if (afd == NULL)
+ return(NULL);
+
+ aplist = hp->h_addr_list;
+
+ memset(&sentinel, 0, sizeof(sentinel));
+ cur = &sentinel;
+
+ for (i = 0; (ap = aplist[i]) != NULL; i++) {
+#if 0 /*%< the trick seems too much */
+ af = hp->h_addr_list;
+ if (af == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) {
+ af = AF_INET;
+ ap = ap + sizeof(struct in6_addr)
+ - sizeof(struct in_addr);
+ }
+ afd = find_afd(af);
+ if (afd == NULL)
+ continue;
+#endif /* 0 */
+
+ GET_AI(cur->ai_next, afd, ap);
+
+ /* GET_PORT(cur->ai_next, servname); */
+ if ((pai->ai_flags & AI_CANONNAME) != 0) {
+ /*
+ * RFC2553 says that ai_canonname will be set only for
+ * the first element. we do it for all the elements,
+ * just for convenience.
+ */
+ GET_CANONNAME(cur->ai_next, hp->h_name);
+ }
+ while (cur->ai_next) /*%< no need to loop, actually. */
+ cur = cur->ai_next;
+ continue;
+
+ free:
+ if (cur->ai_next)
+ freeaddrinfo(cur->ai_next);
+ cur->ai_next = NULL;
+ /* continue, without tht pointer CUR advanced. */
+ }
+
+ return(sentinel.ai_next);
+}
+
+struct addrinfo *
+addr2addrinfo(pai, cp)
+ const struct addrinfo *pai;
+ const char *cp;
+{
+ const struct afd *afd;
+
+ afd = find_afd(pai->ai_family);
+ if (afd == NULL)
+ return(NULL);
+
+ return(get_ai(pai, afd, cp));
+}
+
+static struct net_data *
+init()
+{
+ struct net_data *net_data;
+
+ if (!(net_data = net_data_init(NULL)))
+ goto error;
+ if (!net_data->ho) {
+ net_data->ho = (*net_data->irs->ho_map)(net_data->irs);
+ if (!net_data->ho || !net_data->res) {
+error:
+ errno = EIO;
+ if (net_data && net_data->res)
+ RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+
+ (*net_data->ho->res_set)(net_data->ho, net_data->res, NULL);
+ }
+
+ return (net_data);
+}
diff --git a/usr/src/lib/libresolv2_joy/common/irs/gethostent.c b/usr/src/lib/libresolv2_joy/common/irs/gethostent.c
new file mode 100644
index 0000000000..77c3e0a890
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/gethostent.c
@@ -0,0 +1,1092 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#if !defined(__BIND_NOSTATIC)
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <irs.h>
+#include <isc/memcluster.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "irs_data.h"
+
+/* Definitions */
+
+struct pvt {
+ char * aliases[1];
+ char * addrs[2];
+ char addr[NS_IN6ADDRSZ];
+ char name[NS_MAXDNAME + 1];
+ struct hostent host;
+};
+
+/* Forward */
+
+static struct net_data *init(void);
+static void freepvt(struct net_data *);
+static struct hostent *fakeaddr(const char *, int, struct net_data *);
+
+#ifdef SUNW_OVERRIDE_RETRY
+extern int __res_retry(int);
+extern int __res_retry_reset(void);
+#endif /* SUNW_OVERRIDE_RETRY */
+
+
+/* Public */
+
+struct hostent *
+gethostbyname(const char *name) {
+ struct net_data *net_data = init();
+
+ return (gethostbyname_p(name, net_data));
+}
+
+struct hostent *
+gethostbyname2(const char *name, int af) {
+ struct net_data *net_data = init();
+
+ return (gethostbyname2_p(name, af, net_data));
+}
+
+struct hostent *
+gethostbyaddr(const char *addr, int len, int af) {
+ struct net_data *net_data = init();
+
+ return (gethostbyaddr_p(addr, len, af, net_data));
+}
+
+struct hostent *
+gethostent() {
+ struct net_data *net_data = init();
+
+ return (gethostent_p(net_data));
+}
+
+void
+sethostent(int stayopen) {
+ struct net_data *net_data = init();
+ sethostent_p(stayopen, net_data);
+}
+
+
+void
+endhostent() {
+ struct net_data *net_data = init();
+ endhostent_p(net_data);
+}
+
+/* Shared private. */
+
+struct hostent *
+gethostbyname_p(const char *name, struct net_data *net_data) {
+ struct hostent *hp;
+
+ if (!net_data)
+ return (NULL);
+
+ if (net_data->res->options & RES_USE_INET6) {
+ hp = gethostbyname2_p(name, AF_INET6, net_data);
+ if (hp)
+ return (hp);
+ }
+ return (gethostbyname2_p(name, AF_INET, net_data));
+}
+
+struct hostent *
+gethostbyname2_p(const char *name, int af, struct net_data *net_data) {
+ struct irs_ho *ho;
+ char tmp[NS_MAXDNAME];
+ struct hostent *hp;
+ const char *cp;
+ char **hap;
+
+ if (!net_data || !(ho = net_data->ho))
+ return (NULL);
+ if (net_data->ho_stayopen && net_data->ho_last &&
+ net_data->ho_last->h_addrtype == af) {
+ if (ns_samename(name, net_data->ho_last->h_name) == 1)
+ return (net_data->ho_last);
+ for (hap = net_data->ho_last->h_aliases; hap && *hap; hap++)
+ if (ns_samename(name, *hap) == 1)
+ return (net_data->ho_last);
+ }
+ if (!strchr(name, '.') && (cp = res_hostalias(net_data->res, name,
+ tmp, sizeof tmp)))
+ name = cp;
+ if ((hp = fakeaddr(name, af, net_data)) != NULL)
+ return (hp);
+#ifdef SUNW_OVERRIDE_RETRY
+ net_data->res->retry = __res_retry(net_data->res->retry);
+#endif /* SUNW_OVERRIDE_RETRY */
+ net_data->ho_last = (*ho->byname2)(ho, name, af);
+#ifdef SUNW_OVERRIDE_RETRY
+ net_data->res->retry = __res_retry_reset();
+#endif /* SUNW_OVERRIDE_RETRY */
+ if (!net_data->ho_stayopen)
+ endhostent();
+ return (net_data->ho_last);
+}
+
+struct hostent *
+gethostbyaddr_p(const char *addr, int len, int af, struct net_data *net_data) {
+ struct irs_ho *ho;
+ char **hap;
+
+ if (!net_data || !(ho = net_data->ho))
+ return (NULL);
+ if (net_data->ho_stayopen && net_data->ho_last &&
+ net_data->ho_last->h_length == len)
+ for (hap = net_data->ho_last->h_addr_list;
+ hap && *hap;
+ hap++)
+ if (!memcmp(addr, *hap, len))
+ return (net_data->ho_last);
+ net_data->ho_last = (*ho->byaddr)(ho, addr, len, af);
+ if (!net_data->ho_stayopen)
+ endhostent();
+ return (net_data->ho_last);
+}
+
+
+struct hostent *
+gethostent_p(struct net_data *net_data) {
+ struct irs_ho *ho;
+ struct hostent *hp;
+
+ if (!net_data || !(ho = net_data->ho))
+ return (NULL);
+ while ((hp = (*ho->next)(ho)) != NULL &&
+ hp->h_addrtype == AF_INET6 &&
+ (net_data->res->options & RES_USE_INET6) == 0U)
+ continue;
+ net_data->ho_last = hp;
+ return (net_data->ho_last);
+}
+
+
+void
+sethostent_p(int stayopen, struct net_data *net_data) {
+ struct irs_ho *ho;
+
+ if (!net_data || !(ho = net_data->ho))
+ return;
+ freepvt(net_data);
+ (*ho->rewind)(ho);
+ net_data->ho_stayopen = (stayopen != 0);
+ if (stayopen == 0)
+ net_data_minimize(net_data);
+}
+
+void
+endhostent_p(struct net_data *net_data) {
+ struct irs_ho *ho;
+
+ if ((net_data != NULL) && ((ho = net_data->ho) != NULL))
+ (*ho->minimize)(ho);
+}
+
+#ifndef IN6_IS_ADDR_V4COMPAT
+static const unsigned char in6addr_compat[12] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#define IN6_IS_ADDR_V4COMPAT(x) (!memcmp((x)->s6_addr, in6addr_compat, 12) && \
+ ((x)->s6_addr[12] != 0 || \
+ (x)->s6_addr[13] != 0 || \
+ (x)->s6_addr[14] != 0 || \
+ ((x)->s6_addr[15] != 0 && \
+ (x)->s6_addr[15] != 1)))
+#endif
+#ifndef IN6_IS_ADDR_V4MAPPED
+#define IN6_IS_ADDR_V4MAPPED(x) (!memcmp((x)->s6_addr, in6addr_mapped, 12))
+#endif
+
+static const unsigned char in6addr_mapped[12] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
+
+static int scan_interfaces(int *, int *);
+static struct hostent *copyandmerge(struct hostent *, struct hostent *, int, int *);
+
+/*%
+ * Public functions
+ */
+
+/*%
+ * AI_V4MAPPED + AF_INET6
+ * If no IPv6 address then a query for IPv4 and map returned values.
+ *
+ * AI_ALL + AI_V4MAPPED + AF_INET6
+ * Return IPv6 and IPv4 mapped.
+ *
+ * AI_ADDRCONFIG
+ * Only return IPv6 / IPv4 address if there is an interface of that
+ * type active.
+ */
+
+struct hostent *
+getipnodebyname(const char *name, int af, int flags, int *error_num) {
+ int have_v4 = 1, have_v6 = 1;
+ struct in_addr in4;
+ struct in6_addr in6;
+ struct hostent he, *he1 = NULL, *he2 = NULL, *he3;
+ int v4 = 0, v6 = 0;
+ struct net_data *net_data = init();
+ u_long options;
+ int tmp_err;
+
+ if (net_data == NULL) {
+ *error_num = NO_RECOVERY;
+ return (NULL);
+ }
+
+ /* If we care about active interfaces then check. */
+ if ((flags & AI_ADDRCONFIG) != 0)
+ if (scan_interfaces(&have_v4, &have_v6) == -1) {
+ *error_num = NO_RECOVERY;
+ return (NULL);
+ }
+
+ /* Check for literal address. */
+ if ((v4 = inet_pton(AF_INET, name, &in4)) != 1)
+ v6 = inet_pton(AF_INET6, name, &in6);
+
+ /* Impossible combination? */
+
+ if ((af == AF_INET6 && (flags & AI_V4MAPPED) == 0 && v4 == 1) ||
+ (af == AF_INET && v6 == 1) ||
+ (have_v4 == 0 && v4 == 1) ||
+ (have_v6 == 0 && v6 == 1) ||
+ (have_v4 == 0 && af == AF_INET) ||
+ (have_v6 == 0 && af == AF_INET6)) {
+ *error_num = HOST_NOT_FOUND;
+ return (NULL);
+ }
+
+ /* Literal address? */
+ if (v4 == 1 || v6 == 1) {
+ char *addr_list[2];
+ char *aliases[1];
+
+ DE_CONST(name, he.h_name);
+ he.h_addr_list = addr_list;
+ he.h_addr_list[0] = (v4 == 1) ? (char *)&in4 : (char *)&in6;
+ he.h_addr_list[1] = NULL;
+ he.h_aliases = aliases;
+ he.h_aliases[0] = NULL;
+ he.h_length = (v4 == 1) ? INADDRSZ : IN6ADDRSZ;
+ he.h_addrtype = (v4 == 1) ? AF_INET : AF_INET6;
+ return (copyandmerge(&he, NULL, af, error_num));
+ }
+
+ options = net_data->res->options;
+ net_data->res->options &= ~RES_USE_INET6;
+
+ tmp_err = NO_RECOVERY;
+ if (have_v6 && af == AF_INET6) {
+ he2 = gethostbyname2_p(name, AF_INET6, net_data);
+ if (he2 != NULL) {
+ he1 = copyandmerge(he2, NULL, af, error_num);
+ if (he1 == NULL)
+ return (NULL);
+ he2 = NULL;
+ } else {
+ tmp_err = net_data->res->res_h_errno;
+ }
+ }
+
+ if (have_v4 &&
+ ((af == AF_INET) ||
+ (af == AF_INET6 && (flags & AI_V4MAPPED) != 0 &&
+ (he1 == NULL || (flags & AI_ALL) != 0)))) {
+ he2 = gethostbyname2_p(name, AF_INET, net_data);
+ if (he1 == NULL && he2 == NULL) {
+ *error_num = net_data->res->res_h_errno;
+ return (NULL);
+ }
+ } else
+ *error_num = tmp_err;
+
+ net_data->res->options = options;
+
+ he3 = copyandmerge(he1, he2, af, error_num);
+
+ if (he1 != NULL)
+ freehostent(he1);
+ return (he3);
+}
+
+struct hostent *
+getipnodebyaddr(const void *src, size_t len, int af, int *error_num) {
+ struct hostent *he1, *he2;
+ struct net_data *net_data = init();
+
+ /* Sanity Checks. */
+#ifdef ORIGINAL_ISC_CODE
+ if (src == NULL) {
+#else
+ /* this change was added circa May 2009, but not in ISC libbind 6.0 */
+ if (src == NULL|| net_data == NULL) {
+#endif /* ORIGINAL_ISC_CODE */
+ *error_num = NO_RECOVERY;
+ return (NULL);
+ }
+
+ switch (af) {
+ case AF_INET:
+ if (len != (size_t)INADDRSZ) {
+ *error_num = NO_RECOVERY;
+ return (NULL);
+ }
+ break;
+ case AF_INET6:
+ if (len != (size_t)IN6ADDRSZ) {
+ *error_num = NO_RECOVERY;
+ return (NULL);
+ }
+ break;
+ default:
+ *error_num = NO_RECOVERY;
+ return (NULL);
+ }
+
+ /*
+ * Lookup IPv4 and IPv4 mapped/compatible addresses
+ */
+ if ((af == AF_INET6 &&
+ IN6_IS_ADDR_V4COMPAT((const struct in6_addr *)src)) ||
+ (af == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED((const struct in6_addr *)src)) ||
+ (af == AF_INET)) {
+ const char *cp = src;
+
+ if (af == AF_INET6)
+ cp += 12;
+ he1 = gethostbyaddr_p(cp, 4, AF_INET, net_data);
+ if (he1 == NULL) {
+ *error_num = net_data->res->res_h_errno;
+ return (NULL);
+ }
+ he2 = copyandmerge(he1, NULL, af, error_num);
+ if (he2 == NULL)
+ return (NULL);
+ /*
+ * Restore original address if mapped/compatible.
+ */
+ if (af == AF_INET6)
+ memcpy(he1->h_addr, src, len);
+ return (he2);
+ }
+
+ /*
+ * Lookup IPv6 address.
+ */
+ if (memcmp((const struct in6_addr *)src, &in6addr_any, 16) == 0) {
+ *error_num = HOST_NOT_FOUND;
+ return (NULL);
+ }
+
+ he1 = gethostbyaddr_p(src, 16, AF_INET6, net_data);
+ if (he1 == NULL) {
+ *error_num = net_data->res->res_h_errno;
+ return (NULL);
+ }
+ return (copyandmerge(he1, NULL, af, error_num));
+}
+
+void
+freehostent(struct hostent *he) {
+ char **cpp;
+ int names = 1;
+ int addresses = 1;
+
+ memput(he->h_name, strlen(he->h_name) + 1);
+
+ cpp = he->h_addr_list;
+ while (*cpp != NULL) {
+ memput(*cpp, (he->h_addrtype == AF_INET) ?
+ INADDRSZ : IN6ADDRSZ);
+ *cpp = NULL;
+ cpp++;
+ addresses++;
+ }
+
+ cpp = he->h_aliases;
+ while (*cpp != NULL) {
+ memput(*cpp, strlen(*cpp) + 1);
+ cpp++;
+ names++;
+ }
+
+ memput(he->h_aliases, sizeof(char *) * (names));
+ memput(he->h_addr_list, sizeof(char *) * (addresses));
+ memput(he, sizeof *he);
+}
+
+/*%
+ * Private
+ */
+
+/*%
+ * Scan the interface table and set have_v4 and have_v6 depending
+ * upon whether there are IPv4 and IPv6 interface addresses.
+ *
+ * Returns:
+ * 0 on success
+ * -1 on failure.
+ */
+
+#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \
+ !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF)
+
+#ifdef __hpux
+#define lifc_len iflc_len
+#define lifc_buf iflc_buf
+#define lifc_req iflc_req
+#define LIFCONF if_laddrconf
+#else
+#define SETFAMILYFLAGS
+#define LIFCONF lifconf
+#endif
+
+#ifdef __hpux
+#define lifr_addr iflr_addr
+#define lifr_name iflr_name
+#define lifr_dstaddr iflr_dstaddr
+#define lifr_flags iflr_flags
+#define ss_family sa_family
+#define LIFREQ if_laddrreq
+#else
+#define LIFREQ lifreq
+#endif
+
+static void
+scan_interfaces6(int *have_v4, int *have_v6) {
+ struct LIFCONF lifc;
+ struct LIFREQ lifreq;
+ struct in_addr in4;
+ struct in6_addr in6;
+ char *buf = NULL, *cp, *cplim;
+ static unsigned int bufsiz = 4095;
+ int s, cpsize, n;
+
+ /* Get interface list from system. */
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
+ goto cleanup;
+
+ /*
+ * Grow buffer until large enough to contain all interface
+ * descriptions.
+ */
+ for (;;) {
+ buf = memget(bufsiz);
+ if (buf == NULL)
+ goto cleanup;
+#ifdef SETFAMILYFLAGS
+ lifc.lifc_family = AF_UNSPEC; /*%< request all families */
+ lifc.lifc_flags = 0;
+#endif
+ lifc.lifc_len = bufsiz;
+ lifc.lifc_buf = buf;
+ if ((n = ioctl(s, SIOCGLIFCONF, (char *)&lifc)) != -1) {
+ /*
+ * Some OS's just return what will fit rather
+ * than set EINVAL if the buffer is too small
+ * to fit all the interfaces in. If
+ * lifc.lifc_len is too near to the end of the
+ * buffer we will grow it just in case and
+ * retry.
+ */
+ if (lifc.lifc_len + 2 * sizeof(lifreq) < bufsiz)
+ break;
+ }
+ if ((n == -1) && errno != EINVAL)
+ goto cleanup;
+
+ if (bufsiz > 1000000)
+ goto cleanup;
+
+ memput(buf, bufsiz);
+ bufsiz += 4096;
+ }
+
+ /* Parse system's interface list. */
+ cplim = buf + lifc.lifc_len; /*%< skip over if's with big ifr_addr's */
+ for (cp = buf;
+ (*have_v4 == 0 || *have_v6 == 0) && cp < cplim;
+ cp += cpsize) {
+ memcpy(&lifreq, cp, sizeof lifreq);
+#ifdef HAVE_SA_LEN
+#ifdef FIX_ZERO_SA_LEN
+ if (lifreq.lifr_addr.sa_len == 0)
+ lifreq.lifr_addr.sa_len = 16;
+#endif
+#ifdef HAVE_MINIMUM_IFREQ
+ cpsize = sizeof lifreq;
+ if (lifreq.lifr_addr.sa_len > sizeof (struct sockaddr))
+ cpsize += (int)lifreq.lifr_addr.sa_len -
+ (int)(sizeof (struct sockaddr));
+#else
+ cpsize = sizeof lifreq.lifr_name + lifreq.lifr_addr.sa_len;
+#endif /* HAVE_MINIMUM_IFREQ */
+#elif defined SIOCGIFCONF_ADDR
+ cpsize = sizeof lifreq;
+#else
+ cpsize = sizeof lifreq.lifr_name;
+ /* XXX maybe this should be a hard error? */
+ if (ioctl(s, SIOCGLIFADDR, (char *)&lifreq) < 0)
+ continue;
+#endif
+ switch (lifreq.lifr_addr.ss_family) {
+ case AF_INET:
+ if (*have_v4 == 0) {
+ memcpy(&in4,
+ &((struct sockaddr_in *)
+ &lifreq.lifr_addr)->sin_addr,
+ sizeof in4);
+ if (in4.s_addr == INADDR_ANY)
+ break;
+ n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq);
+ if (n < 0)
+ break;
+ if ((lifreq.lifr_flags & IFF_UP) == 0)
+ break;
+ *have_v4 = 1;
+ }
+ break;
+ case AF_INET6:
+ if (*have_v6 == 0) {
+ memcpy(&in6,
+ &((struct sockaddr_in6 *)
+ &lifreq.lifr_addr)->sin6_addr, sizeof in6);
+ if (memcmp(&in6, &in6addr_any, sizeof in6) == 0)
+ break;
+ n = ioctl(s, SIOCGLIFFLAGS, (char *)&lifreq);
+ if (n < 0)
+ break;
+ if ((lifreq.lifr_flags & IFF_UP) == 0)
+ break;
+ *have_v6 = 1;
+ }
+ break;
+ }
+ }
+ if (buf != NULL)
+ memput(buf, bufsiz);
+ close(s);
+ /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
+ return;
+ cleanup:
+ if (buf != NULL)
+ memput(buf, bufsiz);
+ if (s != -1)
+ close(s);
+ /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
+ return;
+}
+#endif
+
+#if ( defined(__linux__) || defined(__linux) || defined(LINUX) )
+#ifndef IF_NAMESIZE
+# ifdef IFNAMSIZ
+# define IF_NAMESIZE IFNAMSIZ
+# else
+# define IF_NAMESIZE 16
+# endif
+#endif
+static void
+scan_linux6(int *have_v6) {
+ FILE *proc = NULL;
+ char address[33];
+ char name[IF_NAMESIZE+1];
+ int ifindex, prefix, flag3, flag4;
+
+ proc = fopen("/proc/net/if_inet6", "r");
+ if (proc == NULL)
+ return;
+
+ if (fscanf(proc, "%32[a-f0-9] %x %x %x %x %16s\n",
+ address, &ifindex, &prefix, &flag3, &flag4, name) == 6)
+ *have_v6 = 1;
+ fclose(proc);
+ return;
+}
+#endif
+
+static int
+scan_interfaces(int *have_v4, int *have_v6) {
+ struct ifconf ifc;
+ union {
+ char _pad[256]; /*%< leave space for IPv6 addresses */
+ struct ifreq ifreq;
+ } u;
+ struct in_addr in4;
+ struct in6_addr in6;
+ char *buf = NULL, *cp, *cplim;
+ static unsigned int bufsiz = 4095;
+ int s, n;
+ size_t cpsize;
+
+ /* Set to zero. Used as loop terminators below. */
+ *have_v4 = *have_v6 = 0;
+
+#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) && \
+ !defined(IRIX_EMUL_IOCTL_SIOCGIFCONF)
+ /*
+ * Try to scan the interfaces using IPv6 ioctls().
+ */
+ scan_interfaces6(have_v4, have_v6);
+ if (*have_v4 != 0 && *have_v6 != 0)
+ return (0);
+#endif
+#ifdef __linux
+ scan_linux6(have_v6);
+#endif
+
+ /* Get interface list from system. */
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ goto err_ret;
+
+ /*
+ * Grow buffer until large enough to contain all interface
+ * descriptions.
+ */
+ for (;;) {
+ buf = memget(bufsiz);
+ if (buf == NULL)
+ goto err_ret;
+ ifc.ifc_len = bufsiz;
+ ifc.ifc_buf = buf;
+#ifdef IRIX_EMUL_IOCTL_SIOCGIFCONF
+ /*
+ * This is a fix for IRIX OS in which the call to ioctl with
+ * the flag SIOCGIFCONF may not return an entry for all the
+ * interfaces like most flavors of Unix.
+ */
+ if (emul_ioctl(&ifc) >= 0)
+ break;
+#else
+ if ((n = ioctl(s, SIOCGIFCONF, (char *)&ifc)) != -1) {
+ /*
+ * Some OS's just return what will fit rather
+ * than set EINVAL if the buffer is too small
+ * to fit all the interfaces in. If
+ * ifc.ifc_len is too near to the end of the
+ * buffer we will grow it just in case and
+ * retry.
+ */
+ if (ifc.ifc_len + 2 * sizeof(u.ifreq) < bufsiz)
+ break;
+ }
+#endif
+ if ((n == -1) && errno != EINVAL)
+ goto err_ret;
+
+ if (bufsiz > 1000000)
+ goto err_ret;
+
+ memput(buf, bufsiz);
+ bufsiz += 4096;
+ }
+
+ /* Parse system's interface list. */
+ cplim = buf + ifc.ifc_len; /*%< skip over if's with big ifr_addr's */
+ for (cp = buf;
+ (*have_v4 == 0 || *have_v6 == 0) && cp < cplim;
+ cp += cpsize) {
+ memcpy(&u.ifreq, cp, sizeof u.ifreq);
+#ifdef HAVE_SA_LEN
+#ifdef FIX_ZERO_SA_LEN
+ if (u.ifreq.ifr_addr.sa_len == 0)
+ u.ifreq.ifr_addr.sa_len = 16;
+#endif
+#ifdef HAVE_MINIMUM_IFREQ
+ cpsize = sizeof u.ifreq;
+ if (u.ifreq.ifr_addr.sa_len > sizeof (struct sockaddr))
+ cpsize += (int)u.ifreq.ifr_addr.sa_len -
+ (int)(sizeof (struct sockaddr));
+#else
+ cpsize = sizeof u.ifreq.ifr_name + u.ifreq.ifr_addr.sa_len;
+#endif /* HAVE_MINIMUM_IFREQ */
+ if (cpsize > sizeof u.ifreq && cpsize <= sizeof u)
+ memcpy(&u.ifreq, cp, cpsize);
+#elif defined SIOCGIFCONF_ADDR
+ cpsize = sizeof u.ifreq;
+#else
+ cpsize = sizeof u.ifreq.ifr_name;
+ /* XXX maybe this should be a hard error? */
+ if (ioctl(s, SIOCGIFADDR, (char *)&u.ifreq) < 0)
+ continue;
+#endif
+ switch (u.ifreq.ifr_addr.sa_family) {
+ case AF_INET:
+ if (*have_v4 == 0) {
+ memcpy(&in4,
+ &((struct sockaddr_in *)
+ &u.ifreq.ifr_addr)->sin_addr,
+ sizeof in4);
+ if (in4.s_addr == INADDR_ANY)
+ break;
+ n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq);
+ if (n < 0)
+ break;
+ if ((u.ifreq.ifr_flags & IFF_UP) == 0)
+ break;
+ *have_v4 = 1;
+ }
+ break;
+ case AF_INET6:
+ if (*have_v6 == 0) {
+ memcpy(&in6,
+ &((struct sockaddr_in6 *)
+ &u.ifreq.ifr_addr)->sin6_addr,
+ sizeof in6);
+ if (memcmp(&in6, &in6addr_any, sizeof in6) == 0)
+ break;
+ n = ioctl(s, SIOCGIFFLAGS, (char *)&u.ifreq);
+ if (n < 0)
+ break;
+ if ((u.ifreq.ifr_flags & IFF_UP) == 0)
+ break;
+ *have_v6 = 1;
+ }
+ break;
+ }
+ }
+ if (buf != NULL)
+ memput(buf, bufsiz);
+ close(s);
+ /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
+ return (0);
+ err_ret:
+ if (buf != NULL)
+ memput(buf, bufsiz);
+ if (s != -1)
+ close(s);
+ /* printf("scan interface -> 4=%d 6=%d\n", *have_v4, *have_v6); */
+ return (-1);
+}
+
+static struct hostent *
+copyandmerge(struct hostent *he1, struct hostent *he2, int af, int *error_num) {
+ struct hostent *he = NULL;
+ int addresses = 1; /*%< NULL terminator */
+ int names = 1; /*%< NULL terminator */
+ int len = 0;
+ char **cpp, **npp;
+
+ /*
+ * Work out array sizes;
+ */
+ if (he1 != NULL) {
+ cpp = he1->h_addr_list;
+ while (*cpp != NULL) {
+ addresses++;
+ cpp++;
+ }
+ cpp = he1->h_aliases;
+ while (*cpp != NULL) {
+ names++;
+ cpp++;
+ }
+ }
+
+ if (he2 != NULL) {
+ cpp = he2->h_addr_list;
+ while (*cpp != NULL) {
+ addresses++;
+ cpp++;
+ }
+ if (he1 == NULL) {
+ cpp = he2->h_aliases;
+ while (*cpp != NULL) {
+ names++;
+ cpp++;
+ }
+ }
+ }
+
+ if (addresses == 1) {
+ *error_num = NO_ADDRESS;
+ return (NULL);
+ }
+
+ he = memget(sizeof *he);
+ if (he == NULL)
+ goto no_recovery;
+
+ he->h_addr_list = memget(sizeof(char *) * (addresses));
+ if (he->h_addr_list == NULL)
+ goto cleanup0;
+ memset(he->h_addr_list, 0, sizeof(char *) * (addresses));
+
+ /* copy addresses */
+ npp = he->h_addr_list;
+ if (he1 != NULL) {
+ cpp = he1->h_addr_list;
+ while (*cpp != NULL) {
+ *npp = memget((af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
+ if (*npp == NULL)
+ goto cleanup1;
+ /* convert to mapped if required */
+ if (af == AF_INET6 && he1->h_addrtype == AF_INET) {
+ memcpy(*npp, in6addr_mapped,
+ sizeof in6addr_mapped);
+ memcpy(*npp + sizeof in6addr_mapped, *cpp,
+ INADDRSZ);
+ } else {
+ memcpy(*npp, *cpp,
+ (af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
+ }
+ cpp++;
+ npp++;
+ }
+ }
+
+ if (he2 != NULL) {
+ cpp = he2->h_addr_list;
+ while (*cpp != NULL) {
+ *npp = memget((af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
+ if (*npp == NULL)
+ goto cleanup1;
+ /* convert to mapped if required */
+ if (af == AF_INET6 && he2->h_addrtype == AF_INET) {
+ memcpy(*npp, in6addr_mapped,
+ sizeof in6addr_mapped);
+ memcpy(*npp + sizeof in6addr_mapped, *cpp,
+ INADDRSZ);
+ } else {
+ memcpy(*npp, *cpp,
+ (af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
+ }
+ cpp++;
+ npp++;
+ }
+ }
+
+ he->h_aliases = memget(sizeof(char *) * (names));
+ if (he->h_aliases == NULL)
+ goto cleanup1;
+ memset(he->h_aliases, 0, sizeof(char *) * (names));
+
+ /* copy aliases */
+ npp = he->h_aliases;
+ cpp = (he1 != NULL) ? he1->h_aliases : he2->h_aliases;
+ while (*cpp != NULL) {
+ len = strlen (*cpp) + 1;
+ *npp = memget(len);
+ if (*npp == NULL)
+ goto cleanup2;
+ strcpy(*npp, *cpp);
+ npp++;
+ cpp++;
+ }
+
+ /* copy hostname */
+ he->h_name = memget(strlen((he1 != NULL) ?
+ he1->h_name : he2->h_name) + 1);
+ if (he->h_name == NULL)
+ goto cleanup2;
+ strcpy(he->h_name, (he1 != NULL) ? he1->h_name : he2->h_name);
+
+ /* set address type and length */
+ he->h_addrtype = af;
+ he->h_length = (af == AF_INET) ? INADDRSZ : IN6ADDRSZ;
+ return(he);
+
+ cleanup2:
+ cpp = he->h_aliases;
+ while (*cpp != NULL) {
+ memput(*cpp, strlen(*cpp) + 1);
+ cpp++;
+ }
+ memput(he->h_aliases, sizeof(char *) * (names));
+
+ cleanup1:
+ cpp = he->h_addr_list;
+ while (*cpp != NULL) {
+ memput(*cpp, (af == AF_INET) ? INADDRSZ : IN6ADDRSZ);
+ *cpp = NULL;
+ cpp++;
+ }
+ memput(he->h_addr_list, sizeof(char *) * (addresses));
+
+ cleanup0:
+ memput(he, sizeof *he);
+
+ no_recovery:
+ *error_num = NO_RECOVERY;
+ return (NULL);
+}
+
+static struct net_data *
+init() {
+ struct net_data *net_data;
+
+ if (!(net_data = net_data_init(NULL)))
+ goto error;
+ if (!net_data->ho) {
+ net_data->ho = (*net_data->irs->ho_map)(net_data->irs);
+ if (!net_data->ho || !net_data->res) {
+ error:
+ errno = EIO;
+
+#ifdef SUNW_SETHERRNO
+ h_errno = NETDB_INTERNAL;
+#endif /* SUNW_SETHERRNO */
+ if (net_data && net_data->res)
+ RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+
+ (*net_data->ho->res_set)(net_data->ho, net_data->res, NULL);
+ }
+
+ return (net_data);
+}
+
+static void
+freepvt(struct net_data *net_data) {
+ if (net_data->ho_data) {
+ free(net_data->ho_data);
+ net_data->ho_data = NULL;
+ }
+}
+
+static struct hostent *
+fakeaddr(const char *name, int af, struct net_data *net_data) {
+ struct pvt *pvt;
+
+ freepvt(net_data);
+ net_data->ho_data = malloc(sizeof (struct pvt));
+ if (!net_data->ho_data) {
+ errno = ENOMEM;
+ RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ pvt = net_data->ho_data;
+#ifndef __bsdi__
+ /*
+ * Unlike its forebear(inet_aton), our friendly inet_pton() is strict
+ * in its interpretation of its input, and it will only return "1" if
+ * the input string is a formally valid(and thus unambiguous with
+ * respect to host names) internet address specification for this AF.
+ *
+ * This means "telnet 0xdeadbeef" and "telnet 127.1" are dead now.
+ */
+ if (inet_pton(af, name, pvt->addr) != 1) {
+#else
+ /* BSDI XXX
+ * We put this back to inet_aton -- we really want the old behavior
+ * Long live 127.1...
+ */
+ if ((af != AF_INET ||
+ inet_aton(name, (struct in_addr *)pvt->addr) != 1) &&
+ inet_pton(af, name, pvt->addr) != 1) {
+#endif
+ RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND);
+ return (NULL);
+ }
+ strncpy(pvt->name, name, NS_MAXDNAME);
+ pvt->name[NS_MAXDNAME] = '\0';
+ if (af == AF_INET && (net_data->res->options & RES_USE_INET6) != 0U) {
+ map_v4v6_address(pvt->addr, pvt->addr);
+ af = AF_INET6;
+ }
+ pvt->host.h_addrtype = af;
+ switch(af) {
+ case AF_INET:
+ pvt->host.h_length = NS_INADDRSZ;
+ break;
+ case AF_INET6:
+ pvt->host.h_length = NS_IN6ADDRSZ;
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ pvt->host.h_name = pvt->name;
+ pvt->host.h_aliases = pvt->aliases;
+ pvt->aliases[0] = NULL;
+ pvt->addrs[0] = (char *)pvt->addr;
+ pvt->addrs[1] = NULL;
+ pvt->host.h_addr_list = pvt->addrs;
+ RES_SET_H_ERRNO(net_data->res, NETDB_SUCCESS);
+ return (&pvt->host);
+}
+
+#ifdef grot /*%< for future use in gethostbyaddr(), for "SUNSECURITY" */
+ struct hostent *rhp;
+ char **haddr;
+ u_long old_options;
+ char hname2[MAXDNAME+1];
+
+ if (af == AF_INET) {
+ /*
+ * turn off search as the name should be absolute,
+ * 'localhost' should be matched by defnames
+ */
+ strncpy(hname2, hp->h_name, MAXDNAME);
+ hname2[MAXDNAME] = '\0';
+ old_options = net_data->res->options;
+ net_data->res->options &= ~RES_DNSRCH;
+ net_data->res->options |= RES_DEFNAMES;
+ if (!(rhp = gethostbyname(hname2))) {
+ net_data->res->options = old_options;
+ RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND);
+ return (NULL);
+ }
+ net_data->res->options = old_options;
+ for (haddr = rhp->h_addr_list; *haddr; haddr++)
+ if (!memcmp(*haddr, addr, INADDRSZ))
+ break;
+ if (!*haddr) {
+ RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND);
+ return (NULL);
+ }
+ }
+#endif /* grot */
+#endif /*__BIND_NOSTATIC*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/gethostent_r.c b/usr/src/lib/libresolv2_joy/common/irs/gethostent_r.c
new file mode 100644
index 0000000000..50e03f5773
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/gethostent_r.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <port_before.h>
+#if !defined(_REENTRANT) || !defined(DO_PTHREADS)
+ static int gethostent_r_not_required = 0;
+#else
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <port_after.h>
+
+#pragma redefine extname res_gethostbyname joy_res_gethostbyname
+#pragma redefine extname res_gethostbyaddr joy_res_gethostbyaddr
+
+#ifdef HOST_R_RETURN
+
+static HOST_R_RETURN
+copy_hostent(struct hostent *, struct hostent *, HOST_R_COPY_ARGS);
+
+HOST_R_RETURN
+gethostbyname_r(const char *name, struct hostent *hptr, HOST_R_ARGS) {
+ struct hostent *he = gethostbyname(name);
+#ifdef HOST_R_SETANSWER
+ int n = 0;
+#endif
+
+#ifdef HOST_R_ERRNO
+ HOST_R_ERRNO;
+#endif
+
+#ifdef HOST_R_SETANSWER
+ if (he == NULL || (n = copy_hostent(he, hptr, HOST_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = hptr;
+
+ return (n);
+#else
+ if (he == NULL)
+ return (HOST_R_BAD);
+
+ return (copy_hostent(he, hptr, HOST_R_COPY));
+#endif
+}
+
+HOST_R_RETURN
+gethostbyaddr_r(const char *addr, int len, int type,
+ struct hostent *hptr, HOST_R_ARGS) {
+ struct hostent *he = gethostbyaddr(addr, len, type);
+#ifdef HOST_R_SETANSWER
+ int n = 0;
+#endif
+
+#ifdef HOST_R_ERRNO
+ HOST_R_ERRNO;
+#endif
+
+#ifdef HOST_R_SETANSWER
+ if (he == NULL || (n = copy_hostent(he, hptr, HOST_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = hptr;
+
+ return (n);
+#else
+ if (he == NULL)
+ return (HOST_R_BAD);
+
+ return (copy_hostent(he, hptr, HOST_R_COPY));
+#endif
+}
+
+/*%
+ * These assume a single context is in operation per thread.
+ * If this is not the case we will need to call irs directly
+ * rather than through the base functions.
+ */
+
+HOST_R_RETURN
+gethostent_r(struct hostent *hptr, HOST_R_ARGS) {
+ struct hostent *he = gethostent();
+#ifdef HOST_R_SETANSWER
+ int n = 0;
+#endif
+
+#ifdef HOST_R_ERRNO
+ HOST_R_ERRNO;
+#endif
+
+#ifdef HOST_R_SETANSWER
+ if (he == NULL || (n = copy_hostent(he, hptr, HOST_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = hptr;
+
+ return (n);
+#else
+ if (he == NULL)
+ return (HOST_R_BAD);
+
+ return (copy_hostent(he, hptr, HOST_R_COPY));
+#endif
+}
+
+HOST_R_SET_RETURN
+#ifdef HOST_R_ENT_ARGS
+sethostent_r(int stay_open, HOST_R_ENT_ARGS)
+#else
+sethostent_r(int stay_open)
+#endif
+{
+#ifdef HOST_R_ENT_ARGS
+ UNUSED(hdptr);
+#endif
+ sethostent(stay_open);
+#ifdef HOST_R_SET_RESULT
+ return (HOST_R_SET_RESULT);
+#endif
+}
+
+HOST_R_END_RETURN
+#ifdef HOST_R_ENT_ARGS
+endhostent_r(HOST_R_ENT_ARGS)
+#else
+endhostent_r(void)
+#endif
+{
+#ifdef HOST_R_ENT_ARGS
+ UNUSED(hdptr);
+#endif
+ endhostent();
+ HOST_R_END_RESULT(HOST_R_OK);
+}
+
+/* Private */
+
+#ifndef HOSTENT_DATA
+static HOST_R_RETURN
+copy_hostent(struct hostent *he, struct hostent *hptr, HOST_R_COPY_ARGS) {
+ char *cp;
+ char **ptr;
+ int i, n;
+ int nptr, len;
+
+ /* Find out the amount of space required to store the answer. */
+ nptr = 2; /*%< NULL ptrs */
+ len = (char *)ALIGN(buf) - buf;
+ for (i = 0; he->h_addr_list[i]; i++, nptr++) {
+ len += he->h_length;
+ }
+ for (i = 0; he->h_aliases[i]; i++, nptr++) {
+ len += strlen(he->h_aliases[i]) + 1;
+ }
+ len += strlen(he->h_name) + 1;
+ len += nptr * sizeof(char*);
+
+ if (len > buflen) {
+ errno = ERANGE;
+ return (HOST_R_BAD);
+ }
+
+ /* copy address size and type */
+ hptr->h_addrtype = he->h_addrtype;
+ n = hptr->h_length = he->h_length;
+
+ ptr = (char **)ALIGN(buf);
+ cp = (char *)ALIGN(buf) + nptr * sizeof(char *);
+
+ /* copy address list */
+ hptr->h_addr_list = ptr;
+ for (i = 0; he->h_addr_list[i]; i++ , ptr++) {
+ memcpy(cp, he->h_addr_list[i], n);
+ hptr->h_addr_list[i] = cp;
+ cp += n;
+ }
+ hptr->h_addr_list[i] = NULL;
+ ptr++;
+
+ /* copy official name */
+ n = strlen(he->h_name) + 1;
+ strcpy(cp, he->h_name);
+ hptr->h_name = cp;
+ cp += n;
+
+ /* copy aliases */
+ hptr->h_aliases = ptr;
+ for (i = 0 ; he->h_aliases[i]; i++) {
+ n = strlen(he->h_aliases[i]) + 1;
+ strcpy(cp, he->h_aliases[i]);
+ hptr->h_aliases[i] = cp;
+ cp += n;
+ }
+ hptr->h_aliases[i] = NULL;
+
+ return (HOST_R_OK);
+}
+#else /* !HOSTENT_DATA */
+static int
+copy_hostent(struct hostent *he, struct hostent *hptr, HOST_R_COPY_ARGS) {
+ char *cp, *eob;
+ int i, n;
+
+ /* copy address size and type */
+ hptr->h_addrtype = he->h_addrtype;
+ n = hptr->h_length = he->h_length;
+
+ /* copy up to first 35 addresses */
+ i = 0;
+ cp = hdptr->hostbuf;
+ eob = hdptr->hostbuf + sizeof(hdptr->hostbuf);
+ hptr->h_addr_list = hdptr->h_addr_ptrs;
+ while (he->h_addr_list[i] && i < (_MAXADDRS)) {
+ if (n < (eob - cp)) {
+ memcpy(cp, he->h_addr_list[i], n);
+ hptr->h_addr_list[i] = cp;
+ cp += n;
+ } else {
+ break;
+ }
+ i++;
+ }
+ hptr->h_addr_list[i] = NULL;
+
+ /* copy official name */
+ if ((n = strlen(he->h_name) + 1) < (eob - cp)) {
+ strcpy(cp, he->h_name);
+ hptr->h_name = cp;
+ cp += n;
+ } else {
+ return (-1);
+ }
+
+ /* copy aliases */
+ i = 0;
+ hptr->h_aliases = hdptr->host_aliases;
+ while (he->h_aliases[i] && i < (_MAXALIASES-1)) {
+ if ((n = strlen(he->h_aliases[i]) + 1) < (eob - cp)) {
+ strcpy(cp, he->h_aliases[i]);
+ hptr->h_aliases[i] = cp;
+ cp += n;
+ } else {
+ break;
+ }
+ i++;
+ }
+ hptr->h_aliases[i] = NULL;
+
+ return (HOST_R_OK);
+}
+#endif /* !HOSTENT_DATA */
+#else /* HOST_R_RETURN */
+ static int gethostent_r_unknown_system = 0;
+#endif /* HOST_R_RETURN */
+#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/getnameinfo.c b/usr/src/lib/libresolv2_joy/common/irs/getnameinfo.c
new file mode 100644
index 0000000000..360bda8bfe
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/getnameinfo.c
@@ -0,0 +1,334 @@
+/*
+ * Issues to be discussed:
+ * - Thread safe-ness must be checked
+ */
+
+#if ( defined(__linux__) || defined(__linux) || defined(LINUX) )
+#ifndef IF_NAMESIZE
+# ifdef IFNAMSIZ
+# define IF_NAMESIZE IFNAMSIZ
+# else
+# define IF_NAMESIZE 16
+# endif
+#endif
+#endif
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by WIDE Project and
+ * its contributors.
+ * 4. Neither the name of the project 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 PROJECT 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 PROJECT 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.
+ */
+
+#include <port_before.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <string.h>
+#include <stddef.h>
+
+#include <port_after.h>
+
+/*%
+ * Note that a_off will be dynamically adjusted so that to be consistent
+ * with the definition of sockaddr_in{,6}.
+ * The value presented below is just a guess.
+ */
+static struct afd {
+ int a_af;
+ int a_addrlen;
+ size_t a_socklen;
+ int a_off;
+} afdl [] = {
+ /* first entry is linked last... */
+ {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in),
+ offsetof(struct sockaddr_in, sin_addr)},
+ {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6),
+ offsetof(struct sockaddr_in6, sin6_addr)},
+ {0, 0, 0, 0},
+};
+
+struct sockinet {
+#ifdef HAVE_SA_LEN
+ u_char si_len;
+#endif
+ u_char si_family;
+ u_short si_port;
+};
+
+static int ip6_parsenumeric __P((const struct sockaddr *, const char *, char *,
+ size_t, int));
+#ifdef HAVE_SIN6_SCOPE_ID
+static int ip6_sa2str __P((const struct sockaddr_in6 *, char *, size_t, int));
+#endif
+
+int
+getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
+ const struct sockaddr *sa;
+ size_t salen;
+ char *host;
+ size_t hostlen;
+ char *serv;
+ size_t servlen;
+ int flags;
+{
+ struct afd *afd;
+ struct servent *sp;
+ struct hostent *hp;
+ u_short port;
+#ifdef HAVE_SA_LEN
+ size_t len;
+#endif
+ int family, i;
+ const char *addr;
+ char *p;
+ char numserv[512];
+ char numaddr[512];
+ const struct sockaddr_in6 *sin6;
+
+ if (sa == NULL)
+ return EAI_FAIL;
+
+#ifdef HAVE_SA_LEN
+ len = sa->sa_len;
+ if (len != salen) return EAI_FAIL;
+#endif
+
+ family = sa->sa_family;
+ for (i = 0; afdl[i].a_af; i++)
+ if (afdl[i].a_af == family) {
+ afd = &afdl[i];
+ goto found;
+ }
+ return EAI_FAMILY;
+
+ found:
+ if (salen != afd->a_socklen) return EAI_FAIL;
+
+ port = ((const struct sockinet *)sa)->si_port; /*%< network byte order */
+ addr = (const char *)sa + afd->a_off;
+
+ if (serv == NULL || servlen == 0U) {
+ /*
+ * rfc2553bis says that serv == NULL or servlen == 0 means that
+ * the caller does not want the result.
+ */
+ } else if (flags & NI_NUMERICSERV) {
+ sprintf(numserv, "%d", ntohs(port));
+ if (strlen(numserv) > servlen)
+ return EAI_MEMORY;
+ strcpy(serv, numserv);
+ } else {
+ sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp");
+ if (sp) {
+ if (strlen(sp->s_name) + 1 > servlen)
+ return EAI_MEMORY;
+ strcpy(serv, sp->s_name);
+ } else
+ return EAI_NONAME;
+ }
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (ntohl(*(const u_int32_t *)addr) >> IN_CLASSA_NSHIFT == 0)
+ flags |= NI_NUMERICHOST;
+ break;
+ case AF_INET6:
+ sin6 = (const struct sockaddr_in6 *)sa;
+ switch (sin6->sin6_addr.s6_addr[0]) {
+ case 0x00:
+ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+ ;
+ else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
+ ;
+ else
+ flags |= NI_NUMERICHOST;
+ break;
+ default:
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ flags |= NI_NUMERICHOST;
+ else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ flags |= NI_NUMERICHOST;
+ break;
+ }
+ break;
+ }
+ if (host == NULL || hostlen == 0U) {
+ /*
+ * rfc2553bis says that host == NULL or hostlen == 0 means that
+ * the caller does not want the result.
+ */
+ } else if (flags & NI_NUMERICHOST) {
+ goto numeric;
+ } else {
+ hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af);
+
+ if (hp) {
+ if (flags & NI_NOFQDN) {
+ p = strchr(hp->h_name, '.');
+ if (p) *p = '\0';
+ }
+ if (strlen(hp->h_name) + 1 > hostlen)
+ return EAI_MEMORY;
+ strcpy(host, hp->h_name);
+ } else {
+ if (flags & NI_NAMEREQD)
+ return EAI_NONAME;
+ numeric:
+ switch(afd->a_af) {
+ case AF_INET6:
+ {
+ int error;
+
+ if ((error = ip6_parsenumeric(sa, addr, host,
+ hostlen,
+ flags)) != 0)
+ return(error);
+ break;
+ }
+
+ default:
+ if (inet_ntop(afd->a_af, addr, numaddr,
+ sizeof(numaddr)) == NULL)
+ return EAI_NONAME;
+ if (strlen(numaddr) + 1 > hostlen)
+ return EAI_MEMORY;
+ strcpy(host, numaddr);
+ }
+ }
+ }
+ return(0);
+}
+
+static int
+ip6_parsenumeric(const struct sockaddr *sa, const char *addr, char *host,
+ size_t hostlen, int flags)
+{
+ size_t numaddrlen;
+ char numaddr[512];
+
+#ifndef HAVE_SIN6_SCOPE_ID
+ UNUSED(sa);
+ UNUSED(flags);
+#endif
+
+ if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr))
+ == NULL)
+ return EAI_SYSTEM;
+
+ numaddrlen = strlen(numaddr);
+ if (numaddrlen + 1 > hostlen) /*%< don't forget terminator */
+ return EAI_MEMORY;
+ strcpy(host, numaddr);
+
+#ifdef HAVE_SIN6_SCOPE_ID
+ if (((const struct sockaddr_in6 *)sa)->sin6_scope_id) {
+ char scopebuf[MAXHOSTNAMELEN]; /*%< XXX */
+ int scopelen;
+
+ /* ip6_sa2str never fails */
+ scopelen = ip6_sa2str((const struct sockaddr_in6 *)sa,
+ scopebuf, sizeof(scopebuf), flags);
+
+ if (scopelen + 1 + numaddrlen + 1 > hostlen)
+ return EAI_MEMORY;
+
+ /* construct <numeric-addr><delim><scopeid> */
+ memcpy(host + numaddrlen + 1, scopebuf,
+ scopelen);
+ host[numaddrlen] = SCOPE_DELIMITER;
+ host[numaddrlen + 1 + scopelen] = '\0';
+ }
+#endif
+
+ return 0;
+}
+
+#ifdef HAVE_SIN6_SCOPE_ID
+/* ARGSUSED */
+static int
+ip6_sa2str(const struct sockaddr_in6 *sa6, char *buf,
+ size_t bufsiz, int flags)
+{
+#ifdef USE_IFNAMELINKID
+ unsigned int ifindex = (unsigned int)sa6->sin6_scope_id;
+ const struct in6_addr *a6 = &sa6->sin6_addr;
+#endif
+ char tmp[64];
+
+#ifdef NI_NUMERICSCOPE
+ if (flags & NI_NUMERICSCOPE) {
+ sprintf(tmp, "%u", sa6->sin6_scope_id);
+ if (bufsiz != 0U) {
+ strncpy(buf, tmp, bufsiz - 1);
+ buf[bufsiz - 1] = '\0';
+ }
+ return(strlen(tmp));
+ }
+#endif
+
+#ifdef USE_IFNAMELINKID
+ /*
+ * For a link-local address, convert the index to an interface
+ * name, assuming a one-to-one mapping between links and interfaces.
+ * Note, however, that this assumption is stronger than the
+ * specification of the scoped address architecture; the
+ * specficication says that more than one interfaces can belong to
+ * a single link.
+ */
+
+ /* if_indextoname() does not take buffer size. not a good api... */
+ if ((IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) &&
+ bufsiz >= IF_NAMESIZE) {
+ char *p = if_indextoname(ifindex, buf);
+ if (p) {
+ return(strlen(p));
+ }
+ }
+#endif
+
+ /* last resort */
+ sprintf(tmp, "%u", sa6->sin6_scope_id);
+ if (bufsiz != 0U) {
+ strncpy(buf, tmp, bufsiz - 1);
+ buf[bufsiz - 1] = '\0';
+ }
+ return(strlen(tmp));
+}
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/getnetent.c b/usr/src/lib/libresolv2_joy/common/irs/getnetent.c
new file mode 100644
index 0000000000..b3ba37547b
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/getnetent.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#if !defined(__BIND_NOSTATIC)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "irs_data.h"
+
+/* Definitions */
+
+struct pvt {
+ struct netent netent;
+ char * aliases[1];
+ char name[MAXDNAME + 1];
+};
+
+/* Forward */
+
+static struct net_data *init(void);
+static struct netent *nw_to_net(struct nwent *, struct net_data *);
+static void freepvt(struct net_data *);
+static struct netent *fakeaddr(const char *, int af, struct net_data *);
+
+/* Portability */
+
+#ifndef INADDR_NONE
+# define INADDR_NONE 0xffffffff
+#endif
+
+/* Public */
+
+struct netent *
+getnetent() {
+ struct net_data *net_data = init();
+
+ return (getnetent_p(net_data));
+}
+
+struct netent *
+getnetbyname(const char *name) {
+ struct net_data *net_data = init();
+
+ return (getnetbyname_p(name, net_data));
+}
+
+struct netent *
+getnetbyaddr(unsigned long net, int type) {
+ struct net_data *net_data = init();
+
+ return (getnetbyaddr_p(net, type, net_data));
+}
+
+void
+setnetent(int stayopen) {
+ struct net_data *net_data = init();
+
+ setnetent_p(stayopen, net_data);
+}
+
+
+void
+endnetent() {
+ struct net_data *net_data = init();
+
+ endnetent_p(net_data);
+}
+
+/* Shared private. */
+
+struct netent *
+getnetent_p(struct net_data *net_data) {
+ struct irs_nw *nw;
+
+ if (!net_data || !(nw = net_data->nw))
+ return (NULL);
+ net_data->nww_last = (*nw->next)(nw);
+ net_data->nw_last = nw_to_net(net_data->nww_last, net_data);
+ return (net_data->nw_last);
+}
+
+struct netent *
+getnetbyname_p(const char *name, struct net_data *net_data) {
+ struct irs_nw *nw;
+ struct netent *np;
+ char **nap;
+
+ if (!net_data || !(nw = net_data->nw))
+ return (NULL);
+ if (net_data->nw_stayopen && net_data->nw_last) {
+ if (!strcmp(net_data->nw_last->n_name, name))
+ return (net_data->nw_last);
+ for (nap = net_data->nw_last->n_aliases; nap && *nap; nap++)
+ if (!strcmp(name, *nap))
+ return (net_data->nw_last);
+ }
+ if ((np = fakeaddr(name, AF_INET, net_data)) != NULL)
+ return (np);
+ net_data->nww_last = (*nw->byname)(nw, name, AF_INET);
+ net_data->nw_last = nw_to_net(net_data->nww_last, net_data);
+ if (!net_data->nw_stayopen)
+ endnetent();
+ return (net_data->nw_last);
+}
+
+struct netent *
+getnetbyaddr_p(unsigned long net, int type, struct net_data *net_data) {
+ struct irs_nw *nw;
+ u_char addr[4];
+ int bits;
+
+ if (!net_data || !(nw = net_data->nw))
+ return (NULL);
+ if (net_data->nw_stayopen && net_data->nw_last)
+ if (type == net_data->nw_last->n_addrtype &&
+ net == net_data->nw_last->n_net)
+ return (net_data->nw_last);
+
+ /* cannonize net(host order) */
+ if (net < 256UL) {
+ net <<= 24;
+ bits = 8;
+ } else if (net < 65536UL) {
+ net <<= 16;
+ bits = 16;
+ } else if (net < 16777216UL) {
+ net <<= 8;
+ bits = 24;
+ } else
+ bits = 32;
+
+ /* convert to net order */
+ addr[0] = (0xFF000000 & net) >> 24;
+ addr[1] = (0x00FF0000 & net) >> 16;
+ addr[2] = (0x0000FF00 & net) >> 8;
+ addr[3] = (0x000000FF & net);
+
+ /* reduce bits to as close to natural number as possible */
+ if ((bits == 32) && (addr[0] < 224) && (addr[3] == 0)) {
+ if ((addr[0] < 192) && (addr[2] == 0)) {
+ if ((addr[0] < 128) && (addr[1] == 0))
+ bits = 8;
+ else
+ bits = 16;
+ } else {
+ bits = 24;
+ }
+ }
+
+ net_data->nww_last = (*nw->byaddr)(nw, addr, bits, AF_INET);
+ net_data->nw_last = nw_to_net(net_data->nww_last, net_data);
+ if (!net_data->nw_stayopen)
+ endnetent();
+ return (net_data->nw_last);
+}
+
+
+
+
+void
+setnetent_p(int stayopen, struct net_data *net_data) {
+ struct irs_nw *nw;
+
+ if (!net_data || !(nw = net_data->nw))
+ return;
+ freepvt(net_data);
+ (*nw->rewind)(nw);
+ net_data->nw_stayopen = (stayopen != 0);
+ if (stayopen == 0)
+ net_data_minimize(net_data);
+}
+
+void
+endnetent_p(struct net_data *net_data) {
+ struct irs_nw *nw;
+
+ if ((net_data != NULL) && ((nw = net_data->nw) != NULL))
+ (*nw->minimize)(nw);
+}
+
+/* Private */
+
+static struct net_data *
+init() {
+ struct net_data *net_data;
+
+ if (!(net_data = net_data_init(NULL)))
+ goto error;
+ if (!net_data->nw) {
+ net_data->nw = (*net_data->irs->nw_map)(net_data->irs);
+
+ if (!net_data->nw || !net_data->res) {
+ error:
+ errno = EIO;
+ return (NULL);
+ }
+ (*net_data->nw->res_set)(net_data->nw, net_data->res, NULL);
+ }
+
+ return (net_data);
+}
+
+static void
+freepvt(struct net_data *net_data) {
+ if (net_data->nw_data) {
+ free(net_data->nw_data);
+ net_data->nw_data = NULL;
+ }
+}
+
+static struct netent *
+fakeaddr(const char *name, int af, struct net_data *net_data) {
+ struct pvt *pvt;
+ const char *cp;
+ u_long tmp;
+
+ if (af != AF_INET) {
+ /* XXX should support IPv6 some day */
+ errno = EAFNOSUPPORT;
+ RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ if (!isascii((unsigned char)(name[0])) ||
+ !isdigit((unsigned char)(name[0])))
+ return (NULL);
+ for (cp = name; *cp; ++cp)
+ if (!isascii(*cp) || (!isdigit((unsigned char)*cp) && *cp != '.'))
+ return (NULL);
+ if (*--cp == '.')
+ return (NULL);
+
+ /* All-numeric, no dot at the end. */
+
+ tmp = inet_network(name);
+ if (tmp == INADDR_NONE) {
+ RES_SET_H_ERRNO(net_data->res, HOST_NOT_FOUND);
+ return (NULL);
+ }
+
+ /* Valid network number specified.
+ * Fake up a netent as if we'd actually
+ * done a lookup.
+ */
+ freepvt(net_data);
+ net_data->nw_data = malloc(sizeof (struct pvt));
+ if (!net_data->nw_data) {
+ errno = ENOMEM;
+ RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ pvt = net_data->nw_data;
+
+ strncpy(pvt->name, name, MAXDNAME);
+ pvt->name[MAXDNAME] = '\0';
+ pvt->netent.n_name = pvt->name;
+ pvt->netent.n_addrtype = AF_INET;
+ pvt->netent.n_aliases = pvt->aliases;
+ pvt->aliases[0] = NULL;
+ pvt->netent.n_net = tmp;
+
+ return (&pvt->netent);
+}
+
+static struct netent *
+nw_to_net(struct nwent *nwent, struct net_data *net_data) {
+ struct pvt *pvt;
+ u_long addr = 0;
+ int i;
+ int msbyte;
+
+ if (!nwent || nwent->n_addrtype != AF_INET)
+ return (NULL);
+ freepvt(net_data);
+ net_data->nw_data = malloc(sizeof (struct pvt));
+ if (!net_data->nw_data) {
+ errno = ENOMEM;
+ RES_SET_H_ERRNO(net_data->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ pvt = net_data->nw_data;
+ pvt->netent.n_name = nwent->n_name;
+ pvt->netent.n_aliases = nwent->n_aliases;
+ pvt->netent.n_addrtype = nwent->n_addrtype;
+
+/*%
+ * What this code does: Converts net addresses from network to host form.
+ *
+ * msbyte: the index of the most significant byte in the n_addr array.
+ *
+ * Shift bytes in significant order into addr. When all signicant
+ * bytes are in, zero out bits in the LSB that are not part of the network.
+ */
+ msbyte = nwent->n_length / 8 +
+ ((nwent->n_length % 8) != 0 ? 1 : 0) - 1;
+ for (i = 0; i <= msbyte; i++)
+ addr = (addr << 8) | ((unsigned char *)nwent->n_addr)[i];
+ i = (32 - nwent->n_length) % 8;
+ if (i != 0)
+ addr &= ~((1 << (i + 1)) - 1);
+ pvt->netent.n_net = addr;
+ return (&pvt->netent);
+}
+
+#endif /*__BIND_NOSTATIC*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/getnetent_r.c b/usr/src/lib/libresolv2_joy/common/irs/getnetent_r.c
new file mode 100644
index 0000000000..df6831ff12
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/getnetent_r.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <port_before.h>
+#if !defined(_REENTRANT) || !defined(DO_PTHREADS)
+ static int getnetent_r_not_required = 0;
+#else
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <port_after.h>
+
+#ifdef NET_R_RETURN
+
+static NET_R_RETURN
+copy_netent(struct netent *, struct netent *, NET_R_COPY_ARGS);
+
+NET_R_RETURN
+getnetbyname_r(const char *name, struct netent *nptr, NET_R_ARGS) {
+ struct netent *ne = getnetbyname(name);
+#ifdef NET_R_SETANSWER
+ int n = 0;
+
+ if (ne == NULL || (n = copy_netent(ne, nptr, NET_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = ne;
+ if (ne == NULL)
+ *h_errnop = h_errno;
+ return (n);
+#else
+ if (ne == NULL)
+ return (NET_R_BAD);
+
+ return (copy_netent(ne, nptr, NET_R_COPY));
+#endif
+}
+
+#ifndef GETNETBYADDR_ADDR_T
+#define GETNETBYADDR_ADDR_T long
+#endif
+NET_R_RETURN
+getnetbyaddr_r(GETNETBYADDR_ADDR_T addr, int type, struct netent *nptr, NET_R_ARGS) {
+ struct netent *ne = getnetbyaddr(addr, type);
+#ifdef NET_R_SETANSWER
+ int n = 0;
+
+ if (ne == NULL || (n = copy_netent(ne, nptr, NET_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = ne;
+ if (ne == NULL)
+ *h_errnop = h_errno;
+ return (n);
+#else
+
+ if (ne == NULL)
+ return (NET_R_BAD);
+
+ return (copy_netent(ne, nptr, NET_R_COPY));
+#endif
+}
+
+/*%
+ * These assume a single context is in operation per thread.
+ * If this is not the case we will need to call irs directly
+ * rather than through the base functions.
+ */
+
+NET_R_RETURN
+getnetent_r(struct netent *nptr, NET_R_ARGS) {
+ struct netent *ne = getnetent();
+#ifdef NET_R_SETANSWER
+ int n = 0;
+
+ if (ne == NULL || (n = copy_netent(ne, nptr, NET_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = ne;
+ if (ne == NULL)
+ *h_errnop = h_errno;
+ return (n);
+#else
+
+ if (ne == NULL)
+ return (NET_R_BAD);
+
+ return (copy_netent(ne, nptr, NET_R_COPY));
+#endif
+}
+
+NET_R_SET_RETURN
+#ifdef NET_R_ENT_ARGS
+setnetent_r(int stay_open, NET_R_ENT_ARGS)
+#else
+setnetent_r(int stay_open)
+#endif
+{
+#ifdef NET_R_ENT_ARGS
+ UNUSED(ndptr);
+#endif
+ setnetent(stay_open);
+#ifdef NET_R_SET_RESULT
+ return (NET_R_SET_RESULT);
+#endif
+}
+
+NET_R_END_RETURN
+#ifdef NET_R_ENT_ARGS
+endnetent_r(NET_R_ENT_ARGS)
+#else
+endnetent_r()
+#endif
+{
+#ifdef NET_R_ENT_ARGS
+ UNUSED(ndptr);
+#endif
+ endnetent();
+ NET_R_END_RESULT(NET_R_OK);
+}
+
+/* Private */
+
+#ifndef NETENT_DATA
+static NET_R_RETURN
+copy_netent(struct netent *ne, struct netent *nptr, NET_R_COPY_ARGS) {
+ char *cp;
+ int i, n;
+ int numptr, len;
+
+ /* Find out the amount of space required to store the answer. */
+ numptr = 1; /*%< NULL ptr */
+ len = (char *)ALIGN(buf) - buf;
+ for (i = 0; ne->n_aliases[i]; i++, numptr++) {
+ len += strlen(ne->n_aliases[i]) + 1;
+ }
+ len += strlen(ne->n_name) + 1;
+ len += numptr * sizeof(char*);
+
+ if (len > (int)buflen) {
+ errno = ERANGE;
+ return (NET_R_BAD);
+ }
+
+ /* copy net value and type */
+ nptr->n_addrtype = ne->n_addrtype;
+ nptr->n_net = ne->n_net;
+
+ cp = (char *)ALIGN(buf) + numptr * sizeof(char *);
+
+ /* copy official name */
+ n = strlen(ne->n_name) + 1;
+ strcpy(cp, ne->n_name);
+ nptr->n_name = cp;
+ cp += n;
+
+ /* copy aliases */
+ nptr->n_aliases = (char **)ALIGN(buf);
+ for (i = 0 ; ne->n_aliases[i]; i++) {
+ n = strlen(ne->n_aliases[i]) + 1;
+ strcpy(cp, ne->n_aliases[i]);
+ nptr->n_aliases[i] = cp;
+ cp += n;
+ }
+ nptr->n_aliases[i] = NULL;
+
+ return (NET_R_OK);
+}
+#else /* !NETENT_DATA */
+static int
+copy_netent(struct netent *ne, struct netent *nptr, NET_R_COPY_ARGS) {
+ char *cp, *eob;
+ int i, n;
+
+ /* copy net value and type */
+ nptr->n_addrtype = ne->n_addrtype;
+ nptr->n_net = ne->n_net;
+
+ /* copy official name */
+ cp = ndptr->line;
+ eob = ndptr->line + sizeof(ndptr->line);
+ if ((n = strlen(ne->n_name) + 1) < (eob - cp)) {
+ strcpy(cp, ne->n_name);
+ nptr->n_name = cp;
+ cp += n;
+ } else {
+ return (-1);
+ }
+
+ /* copy aliases */
+ i = 0;
+ nptr->n_aliases = ndptr->net_aliases;
+ while (ne->n_aliases[i] && i < (_MAXALIASES-1)) {
+ if ((n = strlen(ne->n_aliases[i]) + 1) < (eob - cp)) {
+ strcpy(cp, ne->n_aliases[i]);
+ nptr->n_aliases[i] = cp;
+ cp += n;
+ } else {
+ break;
+ }
+ i++;
+ }
+ nptr->n_aliases[i] = NULL;
+
+ return (NET_R_OK);
+}
+#endif /* !NETENT_DATA */
+#else /* NET_R_RETURN */
+ static int getnetent_r_unknown_system = 0;
+#endif /* NET_R_RETURN */
+#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/getnetgrent.c b/usr/src/lib/libresolv2_joy/common/irs/getnetgrent.c
new file mode 100644
index 0000000000..dbcd1da486
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/getnetgrent.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1996-1999, 2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#if !defined(__BIND_NOSTATIC)
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_data.h"
+
+/* Forward */
+
+static struct net_data *init(void);
+
+
+/* Public */
+
+#ifndef SETNETGRENT_ARGS
+#define SETNETGRENT_ARGS const char *netgroup
+#endif
+void
+setnetgrent(SETNETGRENT_ARGS) {
+ struct net_data *net_data = init();
+
+ setnetgrent_p(netgroup, net_data);
+}
+
+void
+endnetgrent(void) {
+ struct net_data *net_data = init();
+
+ endnetgrent_p(net_data);
+}
+
+#ifndef INNETGR_ARGS
+#define INNETGR_ARGS const char *netgroup, const char *host, \
+ const char *user, const char *domain
+#endif
+int
+innetgr(INNETGR_ARGS) {
+ struct net_data *net_data = init();
+
+ return (innetgr_p(netgroup, host, user, domain, net_data));
+}
+
+int
+getnetgrent(NGR_R_CONST char **host, NGR_R_CONST char **user,
+ NGR_R_CONST char **domain)
+{
+ struct net_data *net_data = init();
+ const char *ch, *cu, *cd;
+ int ret;
+
+ ret = getnetgrent_p(&ch, &cu, &cd, net_data);
+ if (ret != 1)
+ return (ret);
+
+ DE_CONST(ch, *host);
+ DE_CONST(cu, *user);
+ DE_CONST(cd, *domain);
+ return (ret);
+}
+
+/* Shared private. */
+
+void
+setnetgrent_p(const char *netgroup, struct net_data *net_data) {
+ struct irs_ng *ng;
+
+ if ((net_data != NULL) && ((ng = net_data->ng) != NULL))
+ (*ng->rewind)(ng, netgroup);
+}
+
+void
+endnetgrent_p(struct net_data *net_data) {
+ struct irs_ng *ng;
+
+ if (!net_data)
+ return;
+ if ((ng = net_data->ng) != NULL)
+ (*ng->close)(ng);
+ net_data->ng = NULL;
+}
+
+int
+innetgr_p(const char *netgroup, const char *host,
+ const char *user, const char *domain,
+ struct net_data *net_data) {
+ struct irs_ng *ng;
+
+ if (!net_data || !(ng = net_data->ng))
+ return (0);
+ return ((*ng->test)(ng, netgroup, host, user, domain));
+}
+
+int
+getnetgrent_p(const char **host, const char **user, const char **domain,
+ struct net_data *net_data ) {
+ struct irs_ng *ng;
+
+ if (!net_data || !(ng = net_data->ng))
+ return (0);
+ return ((*ng->next)(ng, host, user, domain));
+}
+
+/* Private */
+
+static struct net_data *
+init(void) {
+ struct net_data *net_data;
+
+ if (!(net_data = net_data_init(NULL)))
+ goto error;
+ if (!net_data->ng) {
+ net_data->ng = (*net_data->irs->ng_map)(net_data->irs);
+ if (!net_data->ng) {
+ error:
+ errno = EIO;
+ return (NULL);
+ }
+ }
+
+ return (net_data);
+}
+
+#endif /*__BIND_NOSTATIC*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/getnetgrent_r.c b/usr/src/lib/libresolv2_joy/common/irs/getnetgrent_r.c
new file mode 100644
index 0000000000..2cc697910e
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/getnetgrent_r.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998, 1999, 2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <port_before.h>
+#if !defined(_REENTRANT) || !defined(DO_PTHREADS)
+ static int getnetgrent_r_not_required = 0;
+#else
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <port_after.h>
+
+#ifdef NGR_R_RETURN
+#ifndef NGR_R_PRIVATE
+#define NGR_R_PRIVATE 0
+#endif
+
+static NGR_R_RETURN
+copy_protoent(NGR_R_CONST char **, NGR_R_CONST char **, NGR_R_CONST char **,
+ const char *, const char *, const char *, NGR_R_COPY_ARGS);
+
+NGR_R_RETURN
+innetgr_r(const char *netgroup, const char *host, const char *user,
+ const char *domain) {
+ char *ng, *ho, *us, *dom;
+
+ DE_CONST(netgroup, ng);
+ DE_CONST(host, ho);
+ DE_CONST(user, us);
+ DE_CONST(domain, dom);
+
+ return (innetgr(ng, ho, us, dom));
+}
+
+/*%
+ * These assume a single context is in operation per thread.
+ * If this is not the case we will need to call irs directly
+ * rather than through the base functions.
+ */
+
+NGR_R_RETURN
+getnetgrent_r(NGR_R_CONST char **machinep, NGR_R_CONST char **userp,
+ NGR_R_CONST char **domainp, NGR_R_ARGS)
+{
+ NGR_R_CONST char *mp, *up, *dp;
+ int res = getnetgrent(&mp, &up, &dp);
+
+ if (res != 1)
+ return (res);
+
+ return (copy_protoent(machinep, userp, domainp,
+ mp, up, dp, NGR_R_COPY));
+}
+
+#if NGR_R_PRIVATE == 2
+struct private {
+ char *buf;
+};
+
+#endif
+NGR_R_SET_RETURN
+#ifdef NGR_R_SET_ARGS
+setnetgrent_r(NGR_R_SET_CONST char *netgroup, NGR_R_SET_ARGS)
+#else
+setnetgrent_r(NGR_R_SET_CONST char *netgroup)
+#endif
+{
+#if NGR_R_PRIVATE == 2
+ struct private *p;
+#endif
+ char *tmp;
+#if defined(NGR_R_SET_ARGS) && NGR_R_PRIVATE == 0
+ UNUSED(buf);
+ UNUSED(buflen);
+#endif
+
+ DE_CONST(netgroup, tmp);
+ setnetgrent(tmp);
+
+#if NGR_R_PRIVATE == 1
+ *buf = NULL;
+#elif NGR_R_PRIVATE == 2
+ *buf = p = malloc(sizeof(struct private));
+ if (p == NULL)
+#ifdef NGR_R_SET_RESULT
+ return (NGR_R_BAD);
+#else
+ return;
+#endif
+ p->buf = NULL;
+#endif
+#ifdef NGR_R_SET_RESULT
+ return (NGR_R_SET_RESULT);
+#endif
+}
+
+NGR_R_END_RETURN
+#ifdef NGR_R_END_ARGS
+endnetgrent_r(NGR_R_END_ARGS)
+#else
+endnetgrent_r(void)
+#endif
+{
+#if NGR_R_PRIVATE == 2
+ struct private *p = buf;
+#endif
+#if defined(NGR_R_SET_ARGS) && NGR_R_PRIVATE == 0
+ UNUSED(buf);
+ UNUSED(buflen);
+#endif
+
+ endnetgrent();
+#if NGR_R_PRIVATE == 1
+ if (*buf != NULL)
+ free(*buf);
+ *buf = NULL;
+#elif NGR_R_PRIVATE == 2
+ if (p->buf != NULL)
+ free(p->buf);
+ free(p);
+#endif
+ NGR_R_END_RESULT(NGR_R_OK);
+}
+
+/* Private */
+
+static int
+copy_protoent(NGR_R_CONST char **machinep, NGR_R_CONST char **userp,
+ NGR_R_CONST char **domainp, const char *mp, const char *up,
+ const char *dp, NGR_R_COPY_ARGS)
+{
+#if NGR_R_PRIVATE == 2
+ struct private *p = buf;
+#endif
+ char *cp;
+ int n;
+ int len;
+
+ /* Find out the amount of space required to store the answer. */
+ len = 0;
+ if (mp != NULL) len += strlen(mp) + 1;
+ if (up != NULL) len += strlen(up) + 1;
+ if (dp != NULL) len += strlen(dp) + 1;
+
+#if NGR_R_PRIVATE == 1
+ if (*buf != NULL)
+ free(*buf);
+ *buf = malloc(len);
+ if (*buf == NULL)
+ return(NGR_R_BAD);
+ cp = *buf;
+#elif NGR_R_PRIVATE == 2
+ if (p->buf)
+ free(p->buf);
+ p->buf = malloc(len);
+ if (p->buf == NULL)
+ return(NGR_R_BAD);
+ cp = p->buf;
+#else
+ if (len > (int)buflen) {
+ errno = ERANGE;
+ return (NGR_R_BAD);
+ }
+ cp = buf;
+#endif
+
+ if (mp != NULL) {
+ n = strlen(mp) + 1;
+ strcpy(cp, mp);
+ *machinep = cp;
+ cp += n;
+ } else
+ *machinep = NULL;
+
+ if (up != NULL) {
+ n = strlen(up) + 1;
+ strcpy(cp, up);
+ *userp = cp;
+ cp += n;
+ } else
+ *userp = NULL;
+
+ if (dp != NULL) {
+ n = strlen(dp) + 1;
+ strcpy(cp, dp);
+ *domainp = cp;
+ cp += n;
+ } else
+ *domainp = NULL;
+
+ return (NGR_R_OK);
+}
+#else /* NGR_R_RETURN */
+ static int getnetgrent_r_unknown_system = 0;
+#endif /* NGR_R_RETURN */
+#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/getprotoent.c b/usr/src/lib/libresolv2_joy/common/irs/getprotoent.c
new file mode 100644
index 0000000000..e56ad4ab5d
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/getprotoent.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#if !defined(__BIND_NOSTATIC)
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_data.h"
+
+/* Forward */
+
+static struct net_data *init(void);
+
+/* Public */
+
+struct protoent *
+getprotoent() {
+ struct net_data *net_data = init();
+
+ return (getprotoent_p(net_data));
+}
+
+struct protoent *
+getprotobyname(const char *name) {
+ struct net_data *net_data = init();
+
+ return (getprotobyname_p(name, net_data));
+}
+
+struct protoent *
+getprotobynumber(int proto) {
+ struct net_data *net_data = init();
+
+ return (getprotobynumber_p(proto, net_data));
+}
+
+void
+setprotoent(int stayopen) {
+ struct net_data *net_data = init();
+
+ setprotoent_p(stayopen, net_data);
+}
+
+void
+endprotoent() {
+ struct net_data *net_data = init();
+
+ endprotoent_p(net_data);
+}
+
+/* Shared private. */
+
+struct protoent *
+getprotoent_p(struct net_data *net_data) {
+ struct irs_pr *pr;
+
+ if (!net_data || !(pr = net_data->pr))
+ return (NULL);
+ net_data->pr_last = (*pr->next)(pr);
+ return (net_data->pr_last);
+}
+
+struct protoent *
+getprotobyname_p(const char *name, struct net_data *net_data) {
+ struct irs_pr *pr;
+ char **pap;
+
+ if (!net_data || !(pr = net_data->pr))
+ return (NULL);
+ if (net_data->pr_stayopen && net_data->pr_last) {
+ if (!strcmp(net_data->pr_last->p_name, name))
+ return (net_data->pr_last);
+ for (pap = net_data->pr_last->p_aliases; pap && *pap; pap++)
+ if (!strcmp(name, *pap))
+ return (net_data->pr_last);
+ }
+ net_data->pr_last = (*pr->byname)(pr, name);
+ if (!net_data->pr_stayopen)
+ endprotoent();
+ return (net_data->pr_last);
+}
+
+struct protoent *
+getprotobynumber_p(int proto, struct net_data *net_data) {
+ struct irs_pr *pr;
+
+ if (!net_data || !(pr = net_data->pr))
+ return (NULL);
+ if (net_data->pr_stayopen && net_data->pr_last)
+ if (net_data->pr_last->p_proto == proto)
+ return (net_data->pr_last);
+ net_data->pr_last = (*pr->bynumber)(pr, proto);
+ if (!net_data->pr_stayopen)
+ endprotoent();
+ return (net_data->pr_last);
+}
+
+void
+setprotoent_p(int stayopen, struct net_data *net_data) {
+ struct irs_pr *pr;
+
+ if (!net_data || !(pr = net_data->pr))
+ return;
+ (*pr->rewind)(pr);
+ net_data->pr_stayopen = (stayopen != 0);
+ if (stayopen == 0)
+ net_data_minimize(net_data);
+}
+
+void
+endprotoent_p(struct net_data *net_data) {
+ struct irs_pr *pr;
+
+ if ((net_data != NULL) && ((pr = net_data->pr) != NULL))
+ (*pr->minimize)(pr);
+}
+
+/* Private */
+
+static struct net_data *
+init() {
+ struct net_data *net_data;
+
+ if (!(net_data = net_data_init(NULL)))
+ goto error;
+ if (!net_data->pr) {
+ net_data->pr = (*net_data->irs->pr_map)(net_data->irs);
+
+ if (!net_data->pr || !net_data->res) {
+ error:
+ errno = EIO;
+ return (NULL);
+ }
+ (*net_data->pr->res_set)(net_data->pr, net_data->res, NULL);
+ }
+
+ return (net_data);
+}
+
+#endif /*__BIND_NOSTATIC*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/getprotoent_r.c b/usr/src/lib/libresolv2_joy/common/irs/getprotoent_r.c
new file mode 100644
index 0000000000..b9a2623bf1
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/getprotoent_r.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <port_before.h>
+#if !defined(_REENTRANT) || !defined(DO_PTHREADS)
+ static int getprotoent_r_not_required = 0;
+#else
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <port_after.h>
+
+#ifdef PROTO_R_RETURN
+
+static PROTO_R_RETURN
+copy_protoent(struct protoent *, struct protoent *, PROTO_R_COPY_ARGS);
+
+PROTO_R_RETURN
+getprotobyname_r(const char *name, struct protoent *pptr, PROTO_R_ARGS) {
+ struct protoent *pe = getprotobyname(name);
+#ifdef PROTO_R_SETANSWER
+ int n = 0;
+
+ if (pe == NULL || (n = copy_protoent(pe, pptr, PROTO_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = pptr;
+
+ return (n);
+#else
+ if (pe == NULL)
+ return (PROTO_R_BAD);
+
+ return (copy_protoent(pe, pptr, PROTO_R_COPY));
+#endif
+}
+
+PROTO_R_RETURN
+getprotobynumber_r(int proto, struct protoent *pptr, PROTO_R_ARGS) {
+ struct protoent *pe = getprotobynumber(proto);
+#ifdef PROTO_R_SETANSWER
+ int n = 0;
+
+ if (pe == NULL || (n = copy_protoent(pe, pptr, PROTO_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = pptr;
+
+ return (n);
+#else
+ if (pe == NULL)
+ return (PROTO_R_BAD);
+
+ return (copy_protoent(pe, pptr, PROTO_R_COPY));
+#endif
+}
+
+/*%
+ * These assume a single context is in operation per thread.
+ * If this is not the case we will need to call irs directly
+ * rather than through the base functions.
+ */
+
+PROTO_R_RETURN
+getprotoent_r(struct protoent *pptr, PROTO_R_ARGS) {
+ struct protoent *pe = getprotoent();
+#ifdef PROTO_R_SETANSWER
+ int n = 0;
+
+ if (pe == NULL || (n = copy_protoent(pe, pptr, PROTO_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = pptr;
+
+ return (n);
+#else
+ if (pe == NULL)
+ return (PROTO_R_BAD);
+
+ return (copy_protoent(pe, pptr, PROTO_R_COPY));
+#endif
+}
+
+PROTO_R_SET_RETURN
+#ifdef PROTO_R_ENT_ARGS
+setprotoent_r(int stay_open, PROTO_R_ENT_ARGS)
+#else
+setprotoent_r(int stay_open)
+#endif
+{
+#ifdef PROTO_R_ENT_UNUSED
+ PROTO_R_ENT_UNUSED;
+#endif
+ setprotoent(stay_open);
+#ifdef PROTO_R_SET_RESULT
+ return (PROTO_R_SET_RESULT);
+#endif
+}
+
+PROTO_R_END_RETURN
+#ifdef PROTO_R_ENT_ARGS
+endprotoent_r(PROTO_R_ENT_ARGS)
+#else
+endprotoent_r()
+#endif
+{
+#ifdef PROTO_R_ENT_UNUSED
+ PROTO_R_ENT_UNUSED;
+#endif
+ endprotoent();
+ PROTO_R_END_RESULT(PROTO_R_OK);
+}
+
+/* Private */
+
+#ifndef PROTOENT_DATA
+static PROTO_R_RETURN
+copy_protoent(struct protoent *pe, struct protoent *pptr, PROTO_R_COPY_ARGS) {
+ char *cp;
+ int i, n;
+ int numptr, len;
+
+ /* Find out the amount of space required to store the answer. */
+ numptr = 1; /*%< NULL ptr */
+ len = (char *)ALIGN(buf) - buf;
+ for (i = 0; pe->p_aliases[i]; i++, numptr++) {
+ len += strlen(pe->p_aliases[i]) + 1;
+ }
+ len += strlen(pe->p_name) + 1;
+ len += numptr * sizeof(char*);
+
+ if (len > (int)buflen) {
+ errno = ERANGE;
+ return (PROTO_R_BAD);
+ }
+
+ /* copy protocol value*/
+ pptr->p_proto = pe->p_proto;
+
+ cp = (char *)ALIGN(buf) + numptr * sizeof(char *);
+
+ /* copy official name */
+ n = strlen(pe->p_name) + 1;
+ strcpy(cp, pe->p_name);
+ pptr->p_name = cp;
+ cp += n;
+
+ /* copy aliases */
+ pptr->p_aliases = (char **)ALIGN(buf);
+ for (i = 0 ; pe->p_aliases[i]; i++) {
+ n = strlen(pe->p_aliases[i]) + 1;
+ strcpy(cp, pe->p_aliases[i]);
+ pptr->p_aliases[i] = cp;
+ cp += n;
+ }
+ pptr->p_aliases[i] = NULL;
+
+ return (PROTO_R_OK);
+}
+#else /* !PROTOENT_DATA */
+static int
+copy_protoent(struct protoent *pe, struct protoent *pptr, PROTO_R_COPY_ARGS) {
+ char *cp, *eob;
+ int i, n;
+
+ /* copy protocol value */
+ pptr->p_proto = pe->p_proto;
+
+ /* copy official name */
+ cp = pdptr->line;
+ eob = pdptr->line + sizeof(pdptr->line);
+ if ((n = strlen(pe->p_name) + 1) < (eob - cp)) {
+ strcpy(cp, pe->p_name);
+ pptr->p_name = cp;
+ cp += n;
+ } else {
+ return (-1);
+ }
+
+ /* copy aliases */
+ i = 0;
+ pptr->p_aliases = pdptr->proto_aliases;
+ while (pe->p_aliases[i] && i < (_MAXALIASES-1)) {
+ if ((n = strlen(pe->p_aliases[i]) + 1) < (eob - cp)) {
+ strcpy(cp, pe->p_aliases[i]);
+ pptr->p_aliases[i] = cp;
+ cp += n;
+ } else {
+ break;
+ }
+ i++;
+ }
+ pptr->p_aliases[i] = NULL;
+
+ return (PROTO_R_OK);
+}
+#endif /* PROTOENT_DATA */
+#else /* PROTO_R_RETURN */
+ static int getprotoent_r_unknown_system = 0;
+#endif /* PROTO_R_RETURN */
+#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/getservent.c b/usr/src/lib/libresolv2_joy/common/irs/getservent.c
new file mode 100644
index 0000000000..3cc2422b98
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/getservent.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#if !defined(__BIND_NOSTATIC)
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_data.h"
+
+/* Forward */
+
+static struct net_data *init(void);
+
+/* Public */
+
+struct servent *
+getservent(void) {
+ struct net_data *net_data = init();
+
+ return (getservent_p(net_data));
+}
+
+struct servent *
+getservbyname(const char *name, const char *proto) {
+ struct net_data *net_data = init();
+
+ return (getservbyname_p(name, proto, net_data));
+}
+
+struct servent *
+getservbyport(int port, const char *proto) {
+ struct net_data *net_data = init();
+
+ return (getservbyport_p(port, proto, net_data));
+}
+
+void
+setservent(int stayopen) {
+ struct net_data *net_data = init();
+
+ setservent_p(stayopen, net_data);
+}
+
+void
+endservent() {
+ struct net_data *net_data = init();
+
+ endservent_p(net_data);
+}
+
+/* Shared private. */
+
+struct servent *
+getservent_p(struct net_data *net_data) {
+ struct irs_sv *sv;
+
+ if (!net_data || !(sv = net_data->sv))
+ return (NULL);
+ net_data->sv_last = (*sv->next)(sv);
+ return (net_data->sv_last);
+}
+
+struct servent *
+getservbyname_p(const char *name, const char *proto,
+ struct net_data *net_data) {
+ struct irs_sv *sv;
+ char **sap;
+
+ if (!net_data || !(sv = net_data->sv))
+ return (NULL);
+ if (net_data->sv_stayopen && net_data->sv_last)
+ if (!proto || !strcmp(net_data->sv_last->s_proto, proto)) {
+ if (!strcmp(net_data->sv_last->s_name, name))
+ return (net_data->sv_last);
+ for (sap = net_data->sv_last->s_aliases;
+ sap && *sap; sap++)
+ if (!strcmp(name, *sap))
+ return (net_data->sv_last);
+ }
+ net_data->sv_last = (*sv->byname)(sv, name, proto);
+ if (!net_data->sv_stayopen)
+ endservent();
+ return (net_data->sv_last);
+}
+
+struct servent *
+getservbyport_p(int port, const char *proto, struct net_data *net_data) {
+ struct irs_sv *sv;
+
+ if (!net_data || !(sv = net_data->sv))
+ return (NULL);
+ if (net_data->sv_stayopen && net_data->sv_last)
+ if (port == net_data->sv_last->s_port &&
+ ( !proto ||
+ !strcmp(net_data->sv_last->s_proto, proto)))
+ return (net_data->sv_last);
+ net_data->sv_last = (*sv->byport)(sv, port, proto);
+ return (net_data->sv_last);
+}
+
+void
+setservent_p(int stayopen, struct net_data *net_data) {
+ struct irs_sv *sv;
+
+ if (!net_data || !(sv = net_data->sv))
+ return;
+ (*sv->rewind)(sv);
+ net_data->sv_stayopen = (stayopen != 0);
+ if (stayopen == 0)
+ net_data_minimize(net_data);
+}
+
+void
+endservent_p(struct net_data *net_data) {
+ struct irs_sv *sv;
+
+ if ((net_data != NULL) && ((sv = net_data->sv) != NULL))
+ (*sv->minimize)(sv);
+}
+
+/* Private */
+
+static struct net_data *
+init() {
+ struct net_data *net_data;
+
+ if (!(net_data = net_data_init(NULL)))
+ goto error;
+ if (!net_data->sv) {
+ net_data->sv = (*net_data->irs->sv_map)(net_data->irs);
+
+ if (!net_data->sv || !net_data->res) {
+ error:
+ errno = EIO;
+ return (NULL);
+ }
+ (*net_data->sv->res_set)(net_data->sv, net_data->res, NULL);
+ }
+
+ return (net_data);
+}
+
+#endif /*__BIND_NOSTATIC*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/getservent_r.c b/usr/src/lib/libresolv2_joy/common/irs/getservent_r.c
new file mode 100644
index 0000000000..91cebcf480
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/getservent_r.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <port_before.h>
+#if !defined(_REENTRANT) || !defined(DO_PTHREADS)
+ static int getservent_r_not_required = 0;
+#else
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <port_after.h>
+
+#ifdef SERV_R_RETURN
+
+static SERV_R_RETURN
+copy_servent(struct servent *, struct servent *, SERV_R_COPY_ARGS);
+
+SERV_R_RETURN
+getservbyname_r(const char *name, const char *proto,
+ struct servent *sptr, SERV_R_ARGS) {
+ struct servent *se = getservbyname(name, proto);
+#ifdef SERV_R_SETANSWER
+ int n = 0;
+
+ if (se == NULL || (n = copy_servent(se, sptr, SERV_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = sptr;
+
+ return (n);
+#else
+ if (se == NULL)
+ return (SERV_R_BAD);
+
+ return (copy_servent(se, sptr, SERV_R_COPY));
+#endif
+}
+
+SERV_R_RETURN
+getservbyport_r(int port, const char *proto,
+ struct servent *sptr, SERV_R_ARGS) {
+ struct servent *se = getservbyport(port, proto);
+#ifdef SERV_R_SETANSWER
+ int n = 0;
+
+ if (se == NULL || (n = copy_servent(se, sptr, SERV_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = sptr;
+
+ return (n);
+#else
+ if (se == NULL)
+ return (SERV_R_BAD);
+
+ return (copy_servent(se, sptr, SERV_R_COPY));
+#endif
+}
+
+/*%
+ * These assume a single context is in operation per thread.
+ * If this is not the case we will need to call irs directly
+ * rather than through the base functions.
+ */
+
+SERV_R_RETURN
+getservent_r(struct servent *sptr, SERV_R_ARGS) {
+ struct servent *se = getservent();
+#ifdef SERV_R_SETANSWER
+ int n = 0;
+
+ if (se == NULL || (n = copy_servent(se, sptr, SERV_R_COPY)) != 0)
+ *answerp = NULL;
+ else
+ *answerp = sptr;
+
+ return (n);
+#else
+ if (se == NULL)
+ return (SERV_R_BAD);
+
+ return (copy_servent(se, sptr, SERV_R_COPY));
+#endif
+}
+
+SERV_R_SET_RETURN
+#ifdef SERV_R_ENT_ARGS
+setservent_r(int stay_open, SERV_R_ENT_ARGS)
+#else
+setservent_r(int stay_open)
+#endif
+{
+#ifdef SERV_R_ENT_UNUSED
+ SERV_R_ENT_UNUSED;
+#endif
+ setservent(stay_open);
+#ifdef SERV_R_SET_RESULT
+ return (SERV_R_SET_RESULT);
+#endif
+}
+
+SERV_R_END_RETURN
+#ifdef SERV_R_ENT_ARGS
+endservent_r(SERV_R_ENT_ARGS)
+#else
+endservent_r()
+#endif
+{
+#ifdef SERV_R_ENT_UNUSED
+ SERV_R_ENT_UNUSED;
+#endif
+ endservent();
+ SERV_R_END_RESULT(SERV_R_OK);
+}
+
+/* Private */
+
+#ifndef SERVENT_DATA
+static SERV_R_RETURN
+copy_servent(struct servent *se, struct servent *sptr, SERV_R_COPY_ARGS) {
+ char *cp;
+ int i, n;
+ int numptr, len;
+
+ /* Find out the amount of space required to store the answer. */
+ numptr = 1; /*%< NULL ptr */
+ len = (char *)ALIGN(buf) - buf;
+ for (i = 0; se->s_aliases[i]; i++, numptr++) {
+ len += strlen(se->s_aliases[i]) + 1;
+ }
+ len += strlen(se->s_name) + 1;
+ len += strlen(se->s_proto) + 1;
+ len += numptr * sizeof(char*);
+
+ if (len > (int)buflen) {
+ errno = ERANGE;
+ return (SERV_R_BAD);
+ }
+
+ /* copy port value */
+ sptr->s_port = se->s_port;
+
+ cp = (char *)ALIGN(buf) + numptr * sizeof(char *);
+
+ /* copy official name */
+ n = strlen(se->s_name) + 1;
+ strcpy(cp, se->s_name);
+ sptr->s_name = cp;
+ cp += n;
+
+ /* copy aliases */
+ sptr->s_aliases = (char **)ALIGN(buf);
+ for (i = 0 ; se->s_aliases[i]; i++) {
+ n = strlen(se->s_aliases[i]) + 1;
+ strcpy(cp, se->s_aliases[i]);
+ sptr->s_aliases[i] = cp;
+ cp += n;
+ }
+ sptr->s_aliases[i] = NULL;
+
+ /* copy proto */
+ n = strlen(se->s_proto) + 1;
+ strcpy(cp, se->s_proto);
+ sptr->s_proto = cp;
+ cp += n;
+
+ return (SERV_R_OK);
+}
+#else /* !SERVENT_DATA */
+static int
+copy_servent(struct servent *se, struct servent *sptr, SERV_R_COPY_ARGS) {
+ char *cp, *eob;
+ int i, n;
+
+ /* copy port value */
+ sptr->s_port = se->s_port;
+
+ /* copy official name */
+ cp = sdptr->line;
+ eob = sdptr->line + sizeof(sdptr->line);
+ if ((n = strlen(se->s_name) + 1) < (eob - cp)) {
+ strcpy(cp, se->s_name);
+ sptr->s_name = cp;
+ cp += n;
+ } else {
+ return (-1);
+ }
+
+ /* copy aliases */
+ i = 0;
+ sptr->s_aliases = sdptr->serv_aliases;
+ while (se->s_aliases[i] && i < (_MAXALIASES-1)) {
+ if ((n = strlen(se->s_aliases[i]) + 1) < (eob - cp)) {
+ strcpy(cp, se->s_aliases[i]);
+ sptr->s_aliases[i] = cp;
+ cp += n;
+ } else {
+ break;
+ }
+ i++;
+ }
+ sptr->s_aliases[i] = NULL;
+
+ /* copy proto */
+ if ((n = strlen(se->s_proto) + 1) < (eob - cp)) {
+ strcpy(cp, se->s_proto);
+ sptr->s_proto = cp;
+ cp += n;
+ } else {
+ return (-1);
+ }
+
+ return (SERV_R_OK);
+}
+#endif /* !SERVENT_DATA */
+#else /*SERV_R_RETURN */
+ static int getservent_r_unknown_system = 0;
+#endif /*SERV_R_RETURN */
+#endif /* !defined(_REENTRANT) || !defined(DO_PTHREADS) */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/hesiod.c b/usr/src/lib/libresolv2_joy/common/irs/hesiod.c
new file mode 100644
index 0000000000..2dd05431a5
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/hesiod.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+/*! \file
+ * \brief
+ * hesiod.c --- the core portion of the hesiod resolver.
+ *
+ * This file is derived from the hesiod library from Project Athena;
+ * It has been extensively rewritten by Theodore Ts'o to have a more
+ * thread-safe interface.
+ * \author
+ * This file is primarily maintained by &lt;tytso@mit.edu&gt; and &lt;ghudson@mit.edu&gt;.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "port_after.h"
+
+#include "pathnames.h"
+#include "hesiod.h"
+#include "hesiod_p.h"
+
+/* Forward */
+
+int hesiod_init(void **context);
+void hesiod_end(void *context);
+char * hesiod_to_bind(void *context, const char *name,
+ const char *type);
+char ** hesiod_resolve(void *context, const char *name,
+ const char *type);
+void hesiod_free_list(void *context, char **list);
+
+static int parse_config_file(struct hesiod_p *ctx, const char *filename);
+static char ** get_txt_records(struct hesiod_p *ctx, int class,
+ const char *name);
+static int init(struct hesiod_p *ctx);
+
+/* Public */
+
+/*%
+ * This function is called to initialize a hesiod_p.
+ */
+int
+hesiod_init(void **context) {
+ struct hesiod_p *ctx;
+ char *cp;
+
+ ctx = malloc(sizeof(struct hesiod_p));
+ if (ctx == 0) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ memset(ctx, 0, sizeof (*ctx));
+
+ if (parse_config_file(ctx, _PATH_HESIOD_CONF) < 0) {
+#ifdef DEF_RHS
+ /*
+ * Use compiled in defaults.
+ */
+ ctx->LHS = malloc(strlen(DEF_LHS) + 1);
+ ctx->RHS = malloc(strlen(DEF_RHS) + 1);
+ if (ctx->LHS == NULL || ctx->RHS == NULL) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ strcpy(ctx->LHS, DEF_LHS); /* (checked) */
+ strcpy(ctx->RHS, DEF_RHS); /* (checked) */
+#else
+ goto cleanup;
+#endif
+ }
+ /*
+ * The default RHS can be overridden by an environment
+ * variable.
+ */
+ if ((cp = getenv("HES_DOMAIN")) != NULL) {
+ size_t RHSlen = strlen(cp) + 2;
+ if (ctx->RHS)
+ free(ctx->RHS);
+ ctx->RHS = malloc(RHSlen);
+ if (!ctx->RHS) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ if (cp[0] == '.') {
+ strcpy(ctx->RHS, cp); /* (checked) */
+ } else {
+ strcpy(ctx->RHS, "."); /* (checked) */
+ strcat(ctx->RHS, cp); /* (checked) */
+ }
+ }
+
+ /*
+ * If there is no default hesiod realm set, we return an
+ * error.
+ */
+ if (!ctx->RHS) {
+ errno = ENOEXEC;
+ goto cleanup;
+ }
+
+#if 0
+ if (res_ninit(ctx->res) < 0)
+ goto cleanup;
+#endif
+
+ *context = ctx;
+ return (0);
+
+ cleanup:
+ hesiod_end(ctx);
+ return (-1);
+}
+
+/*%
+ * This function deallocates the hesiod_p
+ */
+void
+hesiod_end(void *context) {
+ struct hesiod_p *ctx = (struct hesiod_p *) context;
+ int save_errno = errno;
+
+ if (ctx->res)
+ res_nclose(ctx->res);
+ if (ctx->RHS)
+ free(ctx->RHS);
+ if (ctx->LHS)
+ free(ctx->LHS);
+ if (ctx->res && ctx->free_res)
+ (*ctx->free_res)(ctx->res);
+ free(ctx);
+ errno = save_errno;
+}
+
+/*%
+ * This function takes a hesiod (name, type) and returns a DNS
+ * name which is to be resolved.
+ */
+char *
+hesiod_to_bind(void *context, const char *name, const char *type) {
+ struct hesiod_p *ctx = (struct hesiod_p *) context;
+ char *bindname;
+ char **rhs_list = NULL;
+ const char *RHS, *cp;
+
+ /* Decide what our RHS is, and set cp to the end of the actual name. */
+ if ((cp = strchr(name, '@')) != NULL) {
+ if (strchr(cp + 1, '.'))
+ RHS = cp + 1;
+ else if ((rhs_list = hesiod_resolve(context, cp + 1,
+ "rhs-extension")) != NULL)
+ RHS = *rhs_list;
+ else {
+ errno = ENOENT;
+ return (NULL);
+ }
+ } else {
+ RHS = ctx->RHS;
+ cp = name + strlen(name);
+ }
+
+ /*
+ * Allocate the space we need, including up to three periods and
+ * the terminating NUL.
+ */
+ if ((bindname = malloc((cp - name) + strlen(type) + strlen(RHS) +
+ (ctx->LHS ? strlen(ctx->LHS) : 0) + 4)) == NULL) {
+ errno = ENOMEM;
+ if (rhs_list)
+ hesiod_free_list(context, rhs_list);
+ return NULL;
+ }
+
+ /* Now put together the DNS name. */
+ memcpy(bindname, name, cp - name);
+ bindname[cp - name] = '\0';
+ strcat(bindname, ".");
+ strcat(bindname, type);
+ if (ctx->LHS) {
+ if (ctx->LHS[0] != '.')
+ strcat(bindname, ".");
+ strcat(bindname, ctx->LHS);
+ }
+ if (RHS[0] != '.')
+ strcat(bindname, ".");
+ strcat(bindname, RHS);
+
+ if (rhs_list)
+ hesiod_free_list(context, rhs_list);
+
+ return (bindname);
+}
+
+/*%
+ * This is the core function. Given a hesiod (name, type), it
+ * returns an array of strings returned by the resolver.
+ */
+char **
+hesiod_resolve(void *context, const char *name, const char *type) {
+ struct hesiod_p *ctx = (struct hesiod_p *) context;
+ char *bindname = hesiod_to_bind(context, name, type);
+ char **retvec;
+
+ if (bindname == NULL)
+ return (NULL);
+ if (init(ctx) == -1) {
+ free(bindname);
+ return (NULL);
+ }
+
+ if ((retvec = get_txt_records(ctx, C_IN, bindname))) {
+ free(bindname);
+ return (retvec);
+ }
+
+ if (errno != ENOENT)
+ return (NULL);
+
+ retvec = get_txt_records(ctx, C_HS, bindname);
+ free(bindname);
+ return (retvec);
+}
+
+void
+hesiod_free_list(void *context, char **list) {
+ char **p;
+
+ UNUSED(context);
+
+ for (p = list; *p; p++)
+ free(*p);
+ free(list);
+}
+
+/*%
+ * This function parses the /etc/hesiod.conf file
+ */
+static int
+parse_config_file(struct hesiod_p *ctx, const char *filename) {
+ char *key, *data, *cp, **cpp;
+ char buf[MAXDNAME+7];
+ FILE *fp;
+
+ /*
+ * Clear the existing configuration variable, just in case
+ * they're set.
+ */
+ if (ctx->RHS)
+ free(ctx->RHS);
+ if (ctx->LHS)
+ free(ctx->LHS);
+ ctx->RHS = ctx->LHS = 0;
+
+ /*
+ * Now open and parse the file...
+ */
+ if (!(fp = fopen(filename, "r")))
+ return (-1);
+
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ cp = buf;
+ if (*cp == '#' || *cp == '\n' || *cp == '\r')
+ continue;
+ while(*cp == ' ' || *cp == '\t')
+ cp++;
+ key = cp;
+ while(*cp != ' ' && *cp != '\t' && *cp != '=')
+ cp++;
+ *cp++ = '\0';
+
+ while(*cp == ' ' || *cp == '\t' || *cp == '=')
+ cp++;
+ data = cp;
+ while(*cp != ' ' && *cp != '\n' && *cp != '\r')
+ cp++;
+ *cp++ = '\0';
+
+ if (strcmp(key, "lhs") == 0)
+ cpp = &ctx->LHS;
+ else if (strcmp(key, "rhs") == 0)
+ cpp = &ctx->RHS;
+ else
+ continue;
+
+ *cpp = malloc(strlen(data) + 1);
+ if (!*cpp) {
+ errno = ENOMEM;
+ goto cleanup;
+ }
+ strcpy(*cpp, data);
+ }
+ fclose(fp);
+ return (0);
+
+ cleanup:
+ fclose(fp);
+ if (ctx->RHS)
+ free(ctx->RHS);
+ if (ctx->LHS)
+ free(ctx->LHS);
+ ctx->RHS = ctx->LHS = 0;
+ return (-1);
+}
+
+/*%
+ * Given a DNS class and a DNS name, do a lookup for TXT records, and
+ * return a list of them.
+ */
+static char **
+get_txt_records(struct hesiod_p *ctx, int class, const char *name) {
+ struct {
+ int type; /*%< RR type */
+ int class; /*%< RR class */
+ int dlen; /*%< len of data section */
+ u_char *data; /*%< pointer to data */
+ } rr;
+ HEADER *hp;
+ u_char qbuf[MAX_HESRESP], abuf[MAX_HESRESP];
+ u_char *cp, *erdata, *eom;
+ char *dst, *edst, **list;
+ int ancount, qdcount;
+ int i, j, n, skip;
+
+ /*
+ * Construct the query and send it.
+ */
+ n = res_nmkquery(ctx->res, QUERY, name, class, T_TXT, NULL, 0,
+ NULL, qbuf, MAX_HESRESP);
+ if (n < 0) {
+ errno = EMSGSIZE;
+ return (NULL);
+ }
+ n = res_nsend(ctx->res, qbuf, n, abuf, MAX_HESRESP);
+ if (n < 0) {
+ errno = ECONNREFUSED;
+ return (NULL);
+ }
+ if (n < HFIXEDSZ) {
+ errno = EMSGSIZE;
+ return (NULL);
+ }
+
+ /*
+ * OK, parse the result.
+ */
+ hp = (HEADER *) abuf;
+ ancount = ntohs(hp->ancount);
+ qdcount = ntohs(hp->qdcount);
+ cp = abuf + sizeof(HEADER);
+ eom = abuf + n;
+
+ /* Skip query, trying to get to the answer section which follows. */
+ for (i = 0; i < qdcount; i++) {
+ skip = dn_skipname(cp, eom);
+ if (skip < 0 || cp + skip + QFIXEDSZ > eom) {
+ errno = EMSGSIZE;
+ return (NULL);
+ }
+ cp += skip + QFIXEDSZ;
+ }
+
+ list = malloc((ancount + 1) * sizeof(char *));
+ if (!list) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ j = 0;
+ for (i = 0; i < ancount; i++) {
+ skip = dn_skipname(cp, eom);
+ if (skip < 0) {
+ errno = EMSGSIZE;
+ goto cleanup;
+ }
+ cp += skip;
+ if (cp + 3 * INT16SZ + INT32SZ > eom) {
+ errno = EMSGSIZE;
+ goto cleanup;
+ }
+ rr.type = ns_get16(cp);
+ cp += INT16SZ;
+ rr.class = ns_get16(cp);
+ cp += INT16SZ + INT32SZ; /*%< skip the ttl, too */
+ rr.dlen = ns_get16(cp);
+ cp += INT16SZ;
+ if (cp + rr.dlen > eom) {
+ errno = EMSGSIZE;
+ goto cleanup;
+ }
+ rr.data = cp;
+ cp += rr.dlen;
+ if (rr.class != class || rr.type != T_TXT)
+ continue;
+ if (!(list[j] = malloc(rr.dlen)))
+ goto cleanup;
+ dst = list[j++];
+ edst = dst + rr.dlen;
+ erdata = rr.data + rr.dlen;
+ cp = rr.data;
+ while (cp < erdata) {
+ n = (unsigned char) *cp++;
+ if (cp + n > eom || dst + n > edst) {
+ errno = EMSGSIZE;
+ goto cleanup;
+ }
+ memcpy(dst, cp, n);
+ cp += n;
+ dst += n;
+ }
+ if (cp != erdata) {
+ errno = EMSGSIZE;
+ goto cleanup;
+ }
+ *dst = '\0';
+ }
+ list[j] = NULL;
+ if (j == 0) {
+ errno = ENOENT;
+ goto cleanup;
+ }
+ return (list);
+
+ cleanup:
+ for (i = 0; i < j; i++)
+ free(list[i]);
+ free(list);
+ return (NULL);
+}
+
+struct __res_state *
+__hesiod_res_get(void *context) {
+ struct hesiod_p *ctx = context;
+
+ if (!ctx->res) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (res == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(res, 0, sizeof *res);
+ __hesiod_res_set(ctx, res, free);
+ }
+
+ return (ctx->res);
+}
+
+void
+__hesiod_res_set(void *context, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct hesiod_p *ctx = context;
+
+ if (ctx->res && ctx->free_res) {
+ res_nclose(ctx->res);
+ (*ctx->free_res)(ctx->res);
+ }
+
+ ctx->res = res;
+ ctx->free_res = free_res;
+}
+
+static int
+init(struct hesiod_p *ctx) {
+
+ if (!ctx->res && !__hesiod_res_get(ctx))
+ return (-1);
+
+ if (((ctx->res->options & RES_INIT) == 0U) &&
+ (res_ninit(ctx->res) == -1))
+ return (-1);
+
+ return (0);
+}
diff --git a/usr/src/lib/libresolv2_joy/common/irs/hesiod_p.h b/usr/src/lib/libresolv2_joy/common/irs/hesiod_p.h
new file mode 100644
index 0000000000..99da15d0cd
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/hesiod_p.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: hesiod_p.h,v 1.3 2005/04/27 04:56:27 sra Exp $
+ */
+
+#ifndef _HESIOD_P_H_INCLUDED
+#define _HESIOD_P_H_INCLUDED
+
+/** \file
+ * \brief
+ * hesiod_p.h -- private definitions for the hesiod library.
+ *
+ * \author
+ * This file is primarily maintained by tytso@mit.edu and ghudson@mit.edu.
+ */
+
+#define DEF_RHS ".Athena.MIT.EDU" /*%< Defaults if HESIOD_CONF */
+#define DEF_LHS ".ns" /*%< file is not */
+ /*%< present. */
+struct hesiod_p {
+ char * LHS; /*%< normally ".ns" */
+ char * RHS; /*%< AKA the default hesiod domain */
+ struct __res_state * res; /*%< resolver context */
+ void (*free_res)(void *);
+ void (*res_set)(struct hesiod_p *, struct __res_state *,
+ void (*)(void *));
+ struct __res_state * (*res_get)(struct hesiod_p *);
+};
+
+#define MAX_HESRESP 1024
+
+#endif /*_HESIOD_P_H_INCLUDED*/
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp.c b/usr/src/lib/libresolv2_joy/common/irs/irp.c
new file mode 100644
index 0000000000..07659c49af
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irp.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright (C) 2004-2006, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1996, 1998-2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <isc/memcluster.h>
+
+#include <irs.h>
+#include <irp.h>
+
+#include "irs_p.h"
+#include "irp_p.h"
+
+#include "port_after.h"
+
+/* Forward. */
+
+static void irp_close(struct irs_acc *);
+
+#define LINEINCR 128
+
+#if !defined(SUN_LEN)
+#define SUN_LEN(su) \
+ (sizeof (*(su)) - sizeof ((su)->sun_path) + strlen((su)->sun_path))
+#endif
+
+
+/* Public */
+
+
+/* send errors to syslog if true. */
+int irp_log_errors = 1;
+
+/*%
+ * This module handles the irp module connection to irpd.
+ *
+ * The client expects a synchronous interface to functions like
+ * getpwnam(3), so we can't use the ctl_* i/o library on this end of
+ * the wire (it's used in the server).
+ */
+
+/*%
+ * irs_acc *irs_irp_acc(const char *options);
+ *
+ * Initialize the irp module.
+ */
+struct irs_acc *
+irs_irp_acc(const char *options) {
+ struct irs_acc *acc;
+ struct irp_p *irp;
+
+ UNUSED(options);
+
+ if (!(acc = memget(sizeof *acc))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(acc, 0x5e, sizeof *acc);
+ if (!(irp = memget(sizeof *irp))) {
+ errno = ENOMEM;
+ free(acc);
+ return (NULL);
+ }
+ irp->inlast = 0;
+ irp->incurr = 0;
+ irp->fdCxn = -1;
+ acc->private = irp;
+
+#ifdef WANT_IRS_GR
+ acc->gr_map = irs_irp_gr;
+#else
+ acc->gr_map = NULL;
+#endif
+#ifdef WANT_IRS_PW
+ acc->pw_map = irs_irp_pw;
+#else
+ acc->pw_map = NULL;
+#endif
+ acc->sv_map = irs_irp_sv;
+ acc->pr_map = irs_irp_pr;
+ acc->ho_map = irs_irp_ho;
+ acc->nw_map = irs_irp_nw;
+ acc->ng_map = irs_irp_ng;
+ acc->close = irp_close;
+ return (acc);
+}
+
+
+int
+irs_irp_connection_setup(struct irp_p *cxndata, int *warned) {
+ if (irs_irp_is_connected(cxndata)) {
+ return (0);
+ } else if (irs_irp_connect(cxndata) != 0) {
+ if (warned != NULL && !*warned) {
+ syslog(LOG_ERR, "irpd connection failed: %m\n");
+ (*warned)++;
+ }
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*%
+ * int irs_irp_connect(void);
+ *
+ * Sets up the connection to the remote irpd server.
+ *
+ * Returns:
+ *
+ * 0 on success, -1 on failure.
+ *
+ */
+int
+irs_irp_connect(struct irp_p *pvt) {
+ int flags;
+ struct sockaddr *addr;
+ struct sockaddr_in iaddr;
+#ifndef NO_SOCKADDR_UN
+ struct sockaddr_un uaddr;
+#endif
+ long ipaddr;
+ const char *irphost;
+ int code;
+ char text[256];
+ int socklen = 0;
+
+ if (pvt->fdCxn != -1) {
+ perror("fd != 1");
+ return (-1);
+ }
+
+#ifndef NO_SOCKADDR_UN
+ memset(&uaddr, 0, sizeof uaddr);
+#endif
+ memset(&iaddr, 0, sizeof iaddr);
+
+ irphost = getenv(IRPD_HOST_ENV);
+ if (irphost == NULL) {
+ irphost = "127.0.0.1";
+ }
+
+#ifndef NO_SOCKADDR_UN
+ if (irphost[0] == '/') {
+ addr = (struct sockaddr *)&uaddr;
+ strncpy(uaddr.sun_path, irphost, sizeof uaddr.sun_path);
+ uaddr.sun_family = AF_UNIX;
+ socklen = SUN_LEN(&uaddr);
+#ifdef HAVE_SA_LEN
+ uaddr.sun_len = socklen;
+#endif
+ } else
+#endif
+ {
+ if (inet_pton(AF_INET, irphost, &ipaddr) != 1) {
+ errno = EADDRNOTAVAIL;
+ perror("inet_pton");
+ return (-1);
+ }
+
+ addr = (struct sockaddr *)&iaddr;
+ socklen = sizeof iaddr;
+#ifdef HAVE_SA_LEN
+ iaddr.sin_len = socklen;
+#endif
+ iaddr.sin_family = AF_INET;
+ iaddr.sin_port = htons(IRPD_PORT);
+ iaddr.sin_addr.s_addr = ipaddr;
+ }
+
+
+ pvt->fdCxn = socket(addr->sa_family, SOCK_STREAM, PF_UNSPEC);
+ if (pvt->fdCxn < 0) {
+ perror("socket");
+ return (-1);
+ }
+
+ if (connect(pvt->fdCxn, addr, socklen) != 0) {
+ perror("connect");
+ return (-1);
+ }
+
+ flags = fcntl(pvt->fdCxn, F_GETFL, 0);
+ if (flags < 0) {
+ close(pvt->fdCxn);
+ perror("close");
+ return (-1);
+ }
+
+#if 0
+ flags |= O_NONBLOCK;
+ if (fcntl(pvt->fdCxn, F_SETFL, flags) < 0) {
+ close(pvt->fdCxn);
+ perror("fcntl");
+ return (-1);
+ }
+#endif
+
+ code = irs_irp_read_response(pvt, text, sizeof text);
+ if (code != IRPD_WELCOME_CODE) {
+ if (irp_log_errors) {
+ syslog(LOG_WARNING, "Connection failed: %s", text);
+ }
+ irs_irp_disconnect(pvt);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*%
+ * int irs_irp_is_connected(struct irp_p *pvt);
+ *
+ * Returns:
+ *
+ * Non-zero if streams are setup to remote.
+ *
+ */
+
+int
+irs_irp_is_connected(struct irp_p *pvt) {
+ return (pvt->fdCxn >= 0);
+}
+
+/*%
+ * void
+ * irs_irp_disconnect(struct irp_p *pvt);
+ *
+ * Closes streams to remote.
+ */
+
+void
+irs_irp_disconnect(struct irp_p *pvt) {
+ if (pvt->fdCxn != -1) {
+ close(pvt->fdCxn);
+ pvt->fdCxn = -1;
+ }
+}
+
+
+
+int
+irs_irp_read_line(struct irp_p *pvt, char *buffer, int len) {
+ char *realstart = &pvt->inbuffer[0];
+ char *p, *start, *end;
+ int spare;
+ int i;
+ int buffpos = 0;
+ int left = len - 1;
+
+ while (left > 0) {
+ start = p = &pvt->inbuffer[pvt->incurr];
+ end = &pvt->inbuffer[pvt->inlast];
+
+ while (p != end && *p != '\n')
+ p++;
+
+ if (p == end) {
+ /* Found no newline so shift data down if necessary
+ * and append new data to buffer
+ */
+ if (start > realstart) {
+ memmove(realstart, start, end - start);
+ pvt->inlast = end - start;
+ start = realstart;
+ pvt->incurr = 0;
+ end = &pvt->inbuffer[pvt->inlast];
+ }
+
+ spare = sizeof (pvt->inbuffer) - pvt->inlast;
+
+ p = end;
+ i = read(pvt->fdCxn, end, spare);
+ if (i < 0) {
+ close(pvt->fdCxn);
+ pvt->fdCxn = -1;
+ return (buffpos > 0 ? buffpos : -1);
+ } else if (i == 0) {
+ return (buffpos);
+ }
+
+ end += i;
+ pvt->inlast += i;
+
+ while (p != end && *p != '\n')
+ p++;
+ }
+
+ if (p == end) {
+ /* full buffer and still no newline */
+ i = sizeof pvt->inbuffer;
+ } else {
+ /* include newline */
+ i = p - start + 1;
+ }
+
+ if (i > left)
+ i = left;
+ memcpy(buffer + buffpos, start, i);
+ pvt->incurr += i;
+ buffpos += i;
+ buffer[buffpos] = '\0';
+
+ if (p != end) {
+ left = 0;
+ } else {
+ left -= i;
+ }
+ }
+
+#if 0
+ fprintf(stderr, "read line: %s\n", buffer);
+#endif
+ return (buffpos);
+}
+
+/*%
+ * int irp_read_response(struct irp_p *pvt);
+ *
+ * Returns:
+ *
+ * The number found at the beginning of the line read from
+ * FP. 0 on failure(0 is not a legal response code). The
+ * rest of the line is discarded.
+ *
+ */
+
+int
+irs_irp_read_response(struct irp_p *pvt, char *text, size_t textlen) {
+ char line[1024];
+ int code;
+ char *p;
+
+ if (irs_irp_read_line(pvt, line, sizeof line) <= 0) {
+ return (0);
+ }
+
+ p = strchr(line, '\n');
+ if (p == NULL) {
+ return (0);
+ }
+
+ if (sscanf(line, "%d", &code) != 1) {
+ code = 0;
+ } else if (text != NULL && textlen > 0U) {
+ p = line;
+ while (isspace((unsigned char)*p)) p++;
+ while (isdigit((unsigned char)*p)) p++;
+ while (isspace((unsigned char)*p)) p++;
+ strncpy(text, p, textlen - 1);
+ p[textlen - 1] = '\0';
+ }
+
+ return (code);
+}
+
+/*%
+ * char *irp_read_body(struct irp_p *pvt, size_t *size);
+ *
+ * Read in the body of a response. Terminated by a line with
+ * just a dot on it. Lines should be terminated with a CR-LF
+ * sequence, but we're nt piccky if the CR is missing.
+ * No leading dot escaping is done as the protcol doesn't
+ * use leading dots anywhere.
+ *
+ * Returns:
+ *
+ * Pointer to null-terminated buffer allocated by memget.
+ * *SIZE is set to the length of the buffer.
+ *
+ */
+
+char *
+irs_irp_read_body(struct irp_p *pvt, size_t *size) {
+ char line[1024];
+ u_int linelen;
+ size_t len = LINEINCR;
+ char *buffer = memget(len);
+ int idx = 0;
+
+ if (buffer == NULL)
+ return (NULL);
+
+ for (;;) {
+ if (irs_irp_read_line(pvt, line, sizeof line) <= 0 ||
+ strchr(line, '\n') == NULL)
+ goto death;
+
+ linelen = strlen(line);
+
+ if (line[linelen - 1] != '\n')
+ goto death;
+
+ /* We're not strict about missing \r. Should we be?? */
+ if (linelen > 2 && line[linelen - 2] == '\r') {
+ line[linelen - 2] = '\n';
+ line[linelen - 1] = '\0';
+ linelen--;
+ }
+
+ if (linelen == 2 && line[0] == '.') {
+ *size = len;
+ buffer[idx] = '\0';
+
+ return (buffer);
+ }
+
+ if (linelen > (len - (idx + 1))) {
+ char *p = memget(len + LINEINCR);
+
+ if (p == NULL)
+ goto death;
+ memcpy(p, buffer, len);
+ memput(buffer, len);
+ buffer = p;
+ len += LINEINCR;
+ }
+
+ memcpy(buffer + idx, line, linelen);
+ idx += linelen;
+ }
+ death:
+ memput(buffer, len);
+ return (NULL);
+}
+
+/*%
+ * int irs_irp_get_full_response(struct irp_p *pvt, int *code,
+ * char **body, size_t *bodylen);
+ *
+ * Gets the response to a command. If the response indicates
+ * there's a body to follow(code % 10 == 1), then the
+ * body buffer is allcoated with memget and stored in
+ * *BODY. The length of the allocated body buffer is stored
+ * in *BODY. The caller must give the body buffer back to
+ * memput when done. The results code is stored in *CODE.
+ *
+ * Returns:
+ *
+ * 0 if a result was read. -1 on some sort of failure.
+ *
+ */
+
+int
+irs_irp_get_full_response(struct irp_p *pvt, int *code, char *text,
+ size_t textlen, char **body, size_t *bodylen) {
+ int result = irs_irp_read_response(pvt, text, textlen);
+
+ *body = NULL;
+
+ if (result == 0) {
+ return (-1);
+ }
+
+ *code = result;
+
+ /* Code that matches 2xx is a good result code.
+ * Code that matches xx1 means there's a response body coming.
+ */
+ if ((result / 100) == 2 && (result % 10) == 1) {
+ *body = irs_irp_read_body(pvt, bodylen);
+ if (*body == NULL) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*%
+ * int irs_irp_send_command(struct irp_p *pvt, const char *fmt, ...);
+ *
+ * Sends command to remote connected via the PVT
+ * structure. FMT and args after it are fprintf-like
+ * arguments for formatting.
+ *
+ * Returns:
+ *
+ * 0 on success, -1 on failure.
+ */
+
+int
+irs_irp_send_command(struct irp_p *pvt, const char *fmt, ...) {
+ va_list ap;
+ char buffer[1024];
+ int pos = 0;
+ int i, todo;
+
+
+ if (pvt->fdCxn < 0) {
+ return (-1);
+ }
+
+ va_start(ap, fmt);
+ (void) vsprintf(buffer, fmt, ap);
+ todo = strlen(buffer);
+ va_end(ap);
+ if (todo > (int)sizeof(buffer) - 3) {
+ syslog(LOG_CRIT, "memory overrun in irs_irp_send_command()");
+ exit(1);
+ }
+ strcat(buffer, "\r\n");
+ todo = strlen(buffer);
+
+ while (todo > 0) {
+ i = write(pvt->fdCxn, buffer + pos, todo);
+#if 0
+ /* XXX brister */
+ fprintf(stderr, "Wrote: \"");
+ fwrite(buffer + pos, sizeof (char), todo, stderr);
+ fprintf(stderr, "\"\n");
+#endif
+ if (i < 0) {
+ close(pvt->fdCxn);
+ pvt->fdCxn = -1;
+ return (-1);
+ }
+ todo -= i;
+ }
+
+ return (0);
+}
+
+
+/* Methods */
+
+/*%
+ * void irp_close(struct irs_acc *this)
+ *
+ */
+
+static void
+irp_close(struct irs_acc *this) {
+ struct irp_p *irp = (struct irp_p *)this->private;
+
+ if (irp != NULL) {
+ irs_irp_disconnect(irp);
+ memput(irp, sizeof *irp);
+ }
+
+ memput(this, sizeof *this);
+}
+
+
+
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_ho.c b/usr/src/lib/libresolv2_joy/common/irs/irp_ho.c
new file mode 100644
index 0000000000..7a13d2dfc6
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irp_ho.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996,1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports. */
+
+#include "port_before.h"
+
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <irs.h>
+#include <irp.h>
+#include <isc/irpmarshall.h>
+#include <isc/memcluster.h>
+
+#include "irs_p.h"
+#include "dns_p.h"
+#include "irp_p.h"
+
+#include "port_after.h"
+
+/* Definitions. */
+
+#define MAXALIASES 35
+#define MAXADDRS 35
+#define Max(a,b) ((a) > (b) ? (a) : (b))
+
+
+struct pvt {
+ struct irp_p *girpdata;
+ int warned;
+ struct hostent host;
+};
+
+/* Forward. */
+
+static void ho_close(struct irs_ho *this);
+static struct hostent * ho_byname(struct irs_ho *this, const char *name);
+static struct hostent * ho_byname2(struct irs_ho *this, const char *name,
+ int af);
+static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr,
+ int len, int af);
+static struct hostent * ho_next(struct irs_ho *this);
+static void ho_rewind(struct irs_ho *this);
+static void ho_minimize(struct irs_ho *this);
+
+static void free_host(struct hostent *ho);
+static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name,
+ const struct addrinfo *pai);
+
+/* Public. */
+
+/*%
+ * struct irs_ho * irs_irp_ho(struct irs_acc *this)
+ *
+ * Notes:
+ *
+ * Initializes the irp_ho module.
+ *
+ */
+
+struct irs_ho *
+irs_irp_ho(struct irs_acc *this) {
+ struct irs_ho *ho;
+ struct pvt *pvt;
+
+ if (!(ho = memget(sizeof *ho))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(ho, 0x0, sizeof *ho);
+
+ if (!(pvt = memget(sizeof *pvt))) {
+ memput(ho, sizeof *ho);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ pvt->girpdata = this->private;
+
+ ho->private = pvt;
+ ho->close = ho_close;
+ ho->byname = ho_byname;
+ ho->byname2 = ho_byname2;
+ ho->byaddr = ho_byaddr;
+ ho->next = ho_next;
+ ho->rewind = ho_rewind;
+ ho->minimize = ho_minimize;
+ ho->addrinfo = ho_addrinfo;
+
+ return (ho);
+}
+
+/* Methods. */
+
+/*%
+ * Closes down the module.
+ *
+ */
+
+static void
+ho_close(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ ho_minimize(this);
+
+ free_host(&pvt->host);
+
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+
+
+/*
+ * struct hostent * ho_byname(struct irs_ho *this, const char *name)
+ *
+ */
+
+static struct hostent *
+ho_byname(struct irs_ho *this, const char *name) {
+ return (ho_byname2(this, name, AF_INET));
+}
+
+
+
+
+
+/*
+ * struct hostent * ho_byname2(struct irs_ho *this, const char *name, int af)
+ *
+ */
+
+static struct hostent *
+ho_byname2(struct irs_ho *this, const char *name, int af) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct hostent *ho = &pvt->host;
+ char *body = NULL;
+ size_t bodylen;
+ int code;
+ char text[256];
+
+ if (ho->h_name != NULL &&
+ strcmp(name, ho->h_name) == 0 &&
+ af == ho->h_addrtype) {
+ return (ho);
+ }
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "gethostbyname2 %s %s",
+ name, ADDR_T_STR(af)) != 0)
+ return (NULL);
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETHOST_OK) {
+ free_host(ho);
+ if (irp_unmarshall_ho(ho, body) != 0) {
+ ho = NULL;
+ }
+ } else {
+ ho = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (ho);
+}
+
+
+
+/*
+ * struct hostent * ho_byaddr(struct irs_ho *this, const void *addr,
+ * int len, int af)
+ *
+ */
+
+static struct hostent *
+ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct hostent *ho = &pvt->host;
+ char *body = NULL;
+ size_t bodylen;
+ int code;
+ char **p;
+ char paddr[MAXPADDRSIZE];
+ char text[256];
+
+ if (ho->h_name != NULL &&
+ af == ho->h_addrtype &&
+ len == ho->h_length) {
+ for (p = ho->h_addr_list ; *p != NULL ; p++) {
+ if (memcmp(*p, addr, len) == 0)
+ return (ho);
+ }
+ }
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ if (inet_ntop(af, addr, paddr, sizeof paddr) == NULL) {
+ return (NULL);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "gethostbyaddr %s %s",
+ paddr, ADDR_T_STR(af)) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETHOST_OK) {
+ free_host(ho);
+ if (irp_unmarshall_ho(ho, body) != 0) {
+ ho = NULL;
+ }
+ } else {
+ ho = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (ho);
+}
+
+/*%
+ * The implementation for gethostent(3). The first time it's
+ * called all the data is pulled from the remote(i.e. what
+ * the maximum number of gethostent(3) calls would return)
+ * and that data is cached.
+ *
+ */
+
+static struct hostent *
+ho_next(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct hostent *ho = &pvt->host;
+ char *body;
+ size_t bodylen;
+ int code;
+ char text[256];
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "gethostent") != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETHOST_OK) {
+ free_host(ho);
+ if (irp_unmarshall_ho(ho, body) != 0) {
+ ho = NULL;
+ }
+ } else {
+ ho = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (ho);
+}
+
+/*%
+ * void ho_rewind(struct irs_ho *this)
+ *
+ */
+
+static void
+ho_rewind(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char text[256];
+ int code;
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return;
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "sethostent") != 0) {
+ return;
+ }
+
+ code = irs_irp_read_response(pvt->girpdata, text, sizeof text);
+ if (code != IRPD_GETHOST_SETOK) {
+ if (irp_log_errors) {
+ syslog(LOG_WARNING, "sethostent failed: %s", text);
+ }
+ }
+
+ return;
+}
+
+/*%
+ * void ho_minimize(struct irs_ho *this)
+ *
+ */
+
+static void
+ho_minimize(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ free_host(&pvt->host);
+
+ irs_irp_disconnect(pvt->girpdata);
+}
+
+/*%
+ * void free_host(struct hostent *ho)
+ *
+ */
+
+static void
+free_host(struct hostent *ho) {
+ char **p;
+
+ if (ho == NULL) {
+ return;
+ }
+
+ if (ho->h_name != NULL)
+ free(ho->h_name);
+
+ if (ho->h_aliases != NULL) {
+ for (p = ho->h_aliases ; *p != NULL ; p++)
+ free(*p);
+ free(ho->h_aliases);
+ }
+
+ if (ho->h_addr_list != NULL) {
+ for (p = ho->h_addr_list ; *p != NULL ; p++)
+ free(*p);
+ free(ho->h_addr_list);
+ }
+}
+
+/* dummy */
+static struct addrinfo *
+ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai)
+{
+ UNUSED(this);
+ UNUSED(name);
+ UNUSED(pai);
+ return(NULL);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_ng.c b/usr/src/lib/libresolv2_joy/common/irs/irp_ng.c
new file mode 100644
index 0000000000..ef5e5db67c
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irp_ng.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996, 1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include <irs.h>
+#include <irp.h>
+#include <isc/memcluster.h>
+#include <isc/irpmarshall.h>
+
+#include "irs_p.h"
+#include "irp_p.h"
+
+#include "port_after.h"
+
+/* Definitions */
+
+struct pvt {
+ struct irp_p *girpdata;
+ int warned;
+};
+
+
+/* Forward */
+
+static void ng_rewind(struct irs_ng *, const char*);
+static void ng_close(struct irs_ng *);
+static int ng_next(struct irs_ng *, const char **, const char **,
+ const char **);
+static int ng_test(struct irs_ng *, const char *,
+ const char *, const char *,
+ const char *);
+static void ng_minimize(struct irs_ng *);
+
+
+/* Public */
+
+/*%
+ * Intialize the irp netgroup module.
+ *
+ */
+
+struct irs_ng *
+irs_irp_ng(struct irs_acc *this) {
+ struct irs_ng *ng;
+ struct pvt *pvt;
+
+ if (!(ng = memget(sizeof *ng))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(ng, 0x5e, sizeof *ng);
+
+ if (!(pvt = memget(sizeof *pvt))) {
+ memput(ng, sizeof *ng);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ pvt->girpdata = this->private;
+
+ ng->private = pvt;
+ ng->close = ng_close;
+ ng->next = ng_next;
+ ng->test = ng_test;
+ ng->rewind = ng_rewind;
+ ng->minimize = ng_minimize;
+ return (ng);
+}
+
+/* Methods */
+
+
+
+/*
+ * void ng_close(struct irs_ng *this)
+ *
+ */
+
+static void
+ng_close(struct irs_ng *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ ng_minimize(this);
+
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+
+
+
+/*
+ * void ng_rewind(struct irs_ng *this, const char *group)
+ *
+ *
+ */
+
+static void
+ng_rewind(struct irs_ng *this, const char *group) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char text[256];
+ int code;
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return;
+ }
+
+ if (irs_irp_send_command(pvt->girpdata,
+ "setnetgrent %s", group) != 0) {
+ return;
+ }
+
+ code = irs_irp_read_response(pvt->girpdata, text, sizeof text);
+ if (code != IRPD_GETNETGR_SETOK) {
+ if (irp_log_errors) {
+ syslog(LOG_WARNING, "setnetgrent(%s) failed: %s",
+ group, text);
+ }
+ }
+
+ return;
+}
+
+/*
+ * Get the next netgroup item from the cache.
+ *
+ */
+
+static int
+ng_next(struct irs_ng *this, const char **host, const char **user,
+ const char **domain)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ int code;
+ char *body = NULL;
+ size_t bodylen;
+ int rval = 0;
+ char text[256];
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (0);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "getnetgrent") != 0)
+ return (0);
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (0);
+ }
+
+ if (code == IRPD_GETNETGR_OK) {
+ if (irp_unmarshall_ng(host, user, domain, body) == 0) {
+ rval = 1;
+ }
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (rval);
+}
+
+/*
+ * Search for a match in a netgroup.
+ *
+ */
+
+static int
+ng_test(struct irs_ng *this, const char *name,
+ const char *host, const char *user, const char *domain)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ char *body = NULL;
+ size_t bodylen = 0;
+ int code;
+ char text[256];
+ int rval = 0;
+
+ UNUSED(name);
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (0);
+ }
+
+ if (irp_marshall_ng(host, user, domain, &body, &bodylen) != 0) {
+ return (0);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "innetgr %s", body) == 0) {
+ code = irs_irp_read_response(pvt->girpdata, text, sizeof text);
+ if (code == IRPD_GETNETGR_MATCHES) {
+ rval = 1;
+ }
+ }
+
+ memput(body, bodylen);
+
+ return (rval);
+}
+
+
+
+
+/*
+ * void ng_minimize(struct irs_ng *this)
+ *
+ */
+
+static void
+ng_minimize(struct irs_ng *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ irs_irp_disconnect(pvt->girpdata);
+}
+
+
+
+
+/* Private */
+
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_nw.c b/usr/src/lib/libresolv2_joy/common/irs/irp_nw.c
new file mode 100644
index 0000000000..d032f6a608
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irp_nw.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996,1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <irs.h>
+#include <irp.h>
+#include <isc/irpmarshall.h>
+
+#include <isc/memcluster.h>
+#include <isc/misc.h>
+
+#include "irs_p.h"
+#include "lcl_p.h"
+#include "irp_p.h"
+
+#include "port_after.h"
+
+#define MAXALIASES 35
+#define MAXADDRSIZE 4
+
+struct pvt {
+ struct irp_p *girpdata;
+ int warned;
+ struct nwent net;
+};
+
+/* Forward */
+
+static void nw_close(struct irs_nw *);
+static struct nwent * nw_byname(struct irs_nw *, const char *, int);
+static struct nwent * nw_byaddr(struct irs_nw *, void *, int, int);
+static struct nwent * nw_next(struct irs_nw *);
+static void nw_rewind(struct irs_nw *);
+static void nw_minimize(struct irs_nw *);
+
+static void free_nw(struct nwent *nw);
+
+
+/* Public */
+
+/*%
+ * struct irs_nw * irs_irp_nw(struct irs_acc *this)
+ *
+ */
+
+struct irs_nw *
+irs_irp_nw(struct irs_acc *this) {
+ struct irs_nw *nw;
+ struct pvt *pvt;
+
+ if (!(pvt = memget(sizeof *pvt))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+
+ if (!(nw = memget(sizeof *nw))) {
+ memput(pvt, sizeof *pvt);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(nw, 0x0, sizeof *nw);
+ pvt->girpdata = this->private;
+
+ nw->private = pvt;
+ nw->close = nw_close;
+ nw->byname = nw_byname;
+ nw->byaddr = nw_byaddr;
+ nw->next = nw_next;
+ nw->rewind = nw_rewind;
+ nw->minimize = nw_minimize;
+ return (nw);
+}
+
+/* Methods */
+
+/*%
+ * void nw_close(struct irs_nw *this)
+ *
+ */
+
+static void
+nw_close(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ nw_minimize(this);
+
+ free_nw(&pvt->net);
+
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+/*%
+ * struct nwent * nw_byaddr(struct irs_nw *this, void *net,
+ * int length, int type)
+ *
+ */
+
+static struct nwent *
+nw_byaddr(struct irs_nw *this, void *net, int length, int type) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct nwent *nw = &pvt->net;
+ char *body = NULL;
+ size_t bodylen;
+ int code;
+ char paddr[24]; /*%< bigenough for ip4 w/ cidr spec. */
+ char text[256];
+
+ if (inet_net_ntop(type, net, length, paddr, sizeof paddr) == NULL) {
+ return (NULL);
+ }
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "getnetbyaddr %s %s",
+ paddr, ADDR_T_STR(type)) != 0)
+ return (NULL);
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETNET_OK) {
+ free_nw(nw);
+ if (irp_unmarshall_nw(nw, body) != 0) {
+ nw = NULL;
+ }
+ } else {
+ nw = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (nw);
+}
+
+/*%
+ * struct nwent * nw_byname(struct irs_nw *this, const char *name, int type)
+ *
+ */
+
+static struct nwent *
+nw_byname(struct irs_nw *this, const char *name, int type) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct nwent *nw = &pvt->net;
+ char *body = NULL;
+ size_t bodylen;
+ int code;
+ char text[256];
+
+ if (nw->n_name != NULL &&
+ strcmp(name, nw->n_name) == 0 &&
+ nw->n_addrtype == type) {
+ return (nw);
+ }
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "getnetbyname %s", name) != 0)
+ return (NULL);
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETNET_OK) {
+ free_nw(nw);
+ if (irp_unmarshall_nw(nw, body) != 0) {
+ nw = NULL;
+ }
+ } else {
+ nw = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (nw);
+}
+
+/*%
+ * void nw_rewind(struct irs_nw *this)
+ *
+ */
+
+static void
+nw_rewind(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char text[256];
+ int code;
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return;
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "setnetent") != 0) {
+ return;
+ }
+
+ code = irs_irp_read_response(pvt->girpdata, text, sizeof text);
+ if (code != IRPD_GETNET_SETOK) {
+ if (irp_log_errors) {
+ syslog(LOG_WARNING, "setnetent failed: %s", text);
+ }
+ }
+
+ return;
+}
+
+/*%
+ * Prepares the cache if necessary and returns the first, or
+ * next item from it.
+ */
+
+static struct nwent *
+nw_next(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct nwent *nw = &pvt->net;
+ char *body;
+ size_t bodylen;
+ int code;
+ char text[256];
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "getnetent") != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETNET_OK) {
+ free_nw(nw);
+ if (irp_unmarshall_nw(nw, body) != 0) {
+ nw = NULL;
+ }
+ } else {
+ nw = NULL;
+ }
+
+ if (body != NULL)
+ memput(body, bodylen);
+ return (nw);
+}
+
+/*%
+ * void nw_minimize(struct irs_nw *this)
+ *
+ */
+
+static void
+nw_minimize(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ irs_irp_disconnect(pvt->girpdata);
+}
+
+
+
+
+/* private. */
+
+/*%
+ * deallocate all the memory irp_unmarshall_pw allocated.
+ *
+ */
+
+static void
+free_nw(struct nwent *nw) {
+ char **p;
+
+ if (nw == NULL)
+ return;
+
+ if (nw->n_name != NULL)
+ free(nw->n_name);
+
+ if (nw->n_aliases != NULL) {
+ for (p = nw->n_aliases ; *p != NULL ; p++) {
+ free(*p);
+ }
+ free(nw->n_aliases);
+ }
+
+ if (nw->n_addr != NULL)
+ free(nw->n_addr);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_p.h b/usr/src/lib/libresolv2_joy/common/irs/irp_p.h
new file mode 100644
index 0000000000..4f943f81bd
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irp_p.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: irp_p.h,v 1.5 2005/04/27 04:56:28 sra Exp $
+ */
+
+#ifndef _IRP_P_H_INCLUDED
+#define _IRP_P_H_INCLUDED
+
+#include <stdio.h>
+
+struct irp_p {
+ char inbuffer[1024];
+ int inlast; /*%< index of one past the last char in buffer */
+ int incurr; /*%< index of the next char to be read from buffer */
+ int fdCxn;
+};
+
+/*
+ * Externs.
+ */
+
+extern struct irs_acc * irs_irp_acc __P((const char *));
+extern struct irs_gr * irs_irp_gr __P((struct irs_acc *));
+extern struct irs_pw * irs_irp_pw __P((struct irs_acc *));
+extern struct irs_sv * irs_irp_sv __P((struct irs_acc *));
+extern struct irs_pr * irs_irp_pr __P((struct irs_acc *));
+extern struct irs_ho * irs_irp_ho __P((struct irs_acc *));
+extern struct irs_nw * irs_irp_nw __P((struct irs_acc *));
+extern struct irs_ng * irs_irp_ng __P((struct irs_acc *));
+
+int irs_irp_connect(struct irp_p *pvt);
+int irs_irp_is_connected(struct irp_p *pvt);
+void irs_irp_disconnect(struct irp_p *pvt);
+int irs_irp_read_response(struct irp_p *pvt, char *text, size_t textlen);
+char *irs_irp_read_body(struct irp_p *pvt, size_t *size);
+int irs_irp_get_full_response(struct irp_p *pvt, int *code,
+ char *text, size_t textlen,
+ char **body, size_t *bodylen);
+
+extern int irp_log_errors;
+
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_pr.c b/usr/src/lib/libresolv2_joy/common/irs/irp_pr.c
new file mode 100644
index 0000000000..a78f5b2b73
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irp_pr.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <syslog.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <syslog.h>
+
+#include <irs.h>
+#include <irp.h>
+#include <isc/memcluster.h>
+#include <isc/irpmarshall.h>
+
+#include "irs_p.h"
+#include "lcl_p.h"
+#include "irp_p.h"
+
+#include "port_after.h"
+
+
+#define MAXALIASES 35
+
+/* Types */
+
+struct pvt {
+ struct irp_p *girpdata;
+ int warned;
+ struct protoent proto;
+};
+
+/* Forward */
+
+static void pr_close(struct irs_pr *);
+static struct protoent * pr_next(struct irs_pr *);
+static struct protoent * pr_byname(struct irs_pr *, const char *);
+static struct protoent * pr_bynumber(struct irs_pr *, int);
+static void pr_rewind(struct irs_pr *);
+static void pr_minimize(struct irs_pr *);
+
+static void free_proto(struct protoent *pr);
+
+/* Public */
+
+/*%
+ * struct irs_pr * irs_irp_pr(struct irs_acc *this)
+ *
+ */
+
+struct irs_pr *
+irs_irp_pr(struct irs_acc *this) {
+ struct irs_pr *pr;
+ struct pvt *pvt;
+
+ if (!(pr = memget(sizeof *pr))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pr, 0x0, sizeof *pr);
+
+ if (!(pvt = memget(sizeof *pvt))) {
+ memput(pr, sizeof *pr);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ pvt->girpdata = this->private;
+
+ pr->private = pvt;
+ pr->close = pr_close;
+ pr->byname = pr_byname;
+ pr->bynumber = pr_bynumber;
+ pr->next = pr_next;
+ pr->rewind = pr_rewind;
+ pr->minimize = pr_minimize;
+ return (pr);
+}
+
+/* Methods */
+
+/*%
+ * void pr_close(struct irs_pr *this)
+ *
+ */
+
+static void
+pr_close(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ pr_minimize(this);
+
+ free_proto(&pvt->proto);
+
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+/*%
+ * struct protoent * pr_byname(struct irs_pr *this, const char *name)
+ *
+ */
+
+static struct protoent *
+pr_byname(struct irs_pr *this, const char *name) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct protoent *pr = &pvt->proto;
+ char *body = NULL;
+ size_t bodylen;
+ int code;
+ int i;
+ char text[256];
+
+ if (pr->p_name != NULL && strcmp(name, pr->p_name) == 0) {
+ return (pr);
+ }
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ i = irs_irp_send_command(pvt->girpdata, "getprotobyname %s", name);
+ if (i != 0)
+ return (NULL);
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETPROTO_OK) {
+ free_proto(pr);
+ if (irp_unmarshall_pr(pr, body) != 0) {
+ pr = NULL;
+ }
+ } else {
+ pr = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (pr);
+}
+
+/*%
+ * struct protoent * pr_bynumber(struct irs_pr *this, int proto)
+ *
+ */
+
+static struct protoent *
+pr_bynumber(struct irs_pr *this, int proto) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct protoent *pr = &pvt->proto;
+ char *body = NULL;
+ size_t bodylen;
+ int code;
+ int i;
+ char text[256];
+
+ if (pr->p_name != NULL && proto == pr->p_proto) {
+ return (pr);
+ }
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ i = irs_irp_send_command(pvt->girpdata, "getprotobynumber %d", proto);
+ if (i != 0)
+ return (NULL);
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETPROTO_OK) {
+ free_proto(pr);
+ if (irp_unmarshall_pr(pr, body) != 0) {
+ pr = NULL;
+ }
+ } else {
+ pr = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (pr);
+}
+
+/*%
+ * void pr_rewind(struct irs_pr *this)
+ *
+ */
+
+static void
+pr_rewind(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char text[256];
+ int code;
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return;
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "setprotoent") != 0) {
+ return;
+ }
+
+ code = irs_irp_read_response(pvt->girpdata, text, sizeof text);
+ if (code != IRPD_GETPROTO_SETOK) {
+ if (irp_log_errors) {
+ syslog(LOG_WARNING, "setprotoent failed: %s", text);
+ }
+ }
+
+ return;
+}
+
+/*%
+ * Prepares the cache if necessary and returns the next item in it.
+ *
+ */
+
+static struct protoent *
+pr_next(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct protoent *pr = &pvt->proto;
+ char *body;
+ size_t bodylen;
+ int code;
+ char text[256];
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "getprotoent") != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETPROTO_OK) {
+ free_proto(pr);
+ if (irp_unmarshall_pr(pr, body) != 0) {
+ pr = NULL;
+ }
+ } else {
+ pr = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (pr);
+}
+
+/*%
+ * void pr_minimize(struct irs_pr *this)
+ *
+ */
+
+static void
+pr_minimize(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ irs_irp_disconnect(pvt->girpdata);
+}
+
+/*%
+ * Deallocate all the memory irp_unmarshall_pr allocated.
+ *
+ */
+
+static void
+free_proto(struct protoent *pr) {
+ char **p;
+
+ if (pr == NULL)
+ return;
+
+ if (pr->p_name != NULL)
+ free(pr->p_name);
+
+ for (p = pr->p_aliases ; p != NULL && *p != NULL ; p++)
+ free(*p);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irp_sv.c b/usr/src/lib/libresolv2_joy/common/irs/irp_sv.c
new file mode 100644
index 0000000000..5cd3e74032
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irp_sv.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996,1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifdef IRS_LCL_SV_DB
+#include <db.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <irs.h>
+#include <irp.h>
+#include <isc/irpmarshall.h>
+#include <isc/memcluster.h>
+
+#include "irs_p.h"
+#include "lcl_p.h"
+#include "irp_p.h"
+
+#include "port_after.h"
+
+/* Types */
+
+struct pvt {
+ struct irp_p *girpdata;
+ int warned;
+ struct servent service;
+};
+
+/* Forward */
+
+static void sv_close(struct irs_sv*);
+static struct servent * sv_next(struct irs_sv *);
+static struct servent * sv_byname(struct irs_sv *, const char *,
+ const char *);
+static struct servent * sv_byport(struct irs_sv *, int, const char *);
+static void sv_rewind(struct irs_sv *);
+static void sv_minimize(struct irs_sv *);
+
+static void free_service(struct servent *sv);
+
+
+
+/* Public */
+
+/*%
+ * struct irs_sv * irs_irp_sv(struct irs_acc *this)
+ *
+ */
+
+struct irs_sv *
+irs_irp_sv(struct irs_acc *this) {
+ struct irs_sv *sv;
+ struct pvt *pvt;
+
+ if ((sv = memget(sizeof *sv)) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(sv, 0x0, sizeof *sv);
+
+ if ((pvt = memget(sizeof *pvt)) == NULL) {
+ memput(sv, sizeof *sv);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ pvt->girpdata = this->private;
+
+ sv->private = pvt;
+ sv->close = sv_close;
+ sv->next = sv_next;
+ sv->byname = sv_byname;
+ sv->byport = sv_byport;
+ sv->rewind = sv_rewind;
+ sv->minimize = sv_minimize;
+
+ return (sv);
+}
+
+/* Methods */
+
+/*%
+ * void sv_close(struct irs_sv *this)
+ *
+ */
+
+static void
+sv_close(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ sv_minimize(this);
+
+ free_service(&pvt->service);
+
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+/*%
+ * Fills the cache if necessary and returns the next item from it.
+ *
+ */
+
+static struct servent *
+sv_next(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct servent *sv = &pvt->service;
+ char *body;
+ size_t bodylen;
+ int code;
+ char text[256];
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "getservent") != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETSERVICE_OK) {
+ free_service(sv);
+ if (irp_unmarshall_sv(sv, body) != 0) {
+ sv = NULL;
+ }
+ } else {
+ sv = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (sv);
+}
+
+/*%
+ * struct servent * sv_byname(struct irs_sv *this, const char *name,
+ * const char *proto)
+ *
+ */
+
+static struct servent *
+sv_byname(struct irs_sv *this, const char *name, const char *proto) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct servent *sv = &pvt->service;
+ char *body;
+ char text[256];
+ size_t bodylen;
+ int code;
+
+ if (sv->s_name != NULL &&
+ strcmp(name, sv->s_name) == 0 &&
+ strcasecmp(proto, sv->s_proto) == 0) {
+ return (sv);
+ }
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "getservbyname %s %s",
+ name, proto) != 0)
+ return (NULL);
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETSERVICE_OK) {
+ free_service(sv);
+ if (irp_unmarshall_sv(sv, body) != 0) {
+ sv = NULL;
+ }
+ } else {
+ sv = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (sv);
+}
+
+/*%
+ * struct servent * sv_byport(struct irs_sv *this, int port,
+ * const char *proto)
+ *
+ */
+
+static struct servent *
+sv_byport(struct irs_sv *this, int port, const char *proto) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct servent *sv = &pvt->service;
+ char *body;
+ size_t bodylen;
+ char text[256];
+ int code;
+
+ if (sv->s_name != NULL &&
+ port == sv->s_port &&
+ strcasecmp(proto, sv->s_proto) == 0) {
+ return (sv);
+ }
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "getservbyport %d %s",
+ ntohs((short)port), proto) != 0) {
+ return (NULL);
+ }
+
+ if (irs_irp_get_full_response(pvt->girpdata, &code,
+ text, sizeof text,
+ &body, &bodylen) != 0) {
+ return (NULL);
+ }
+
+ if (code == IRPD_GETSERVICE_OK) {
+ free_service(sv);
+ if (irp_unmarshall_sv(sv, body) != 0) {
+ sv = NULL;
+ }
+ } else {
+ sv = NULL;
+ }
+
+ if (body != NULL) {
+ memput(body, bodylen);
+ }
+
+ return (sv);
+}
+
+/*%
+ * void sv_rewind(struct irs_sv *this)
+ *
+ */
+
+static void
+sv_rewind(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char text[256];
+ int code;
+
+ if (irs_irp_connection_setup(pvt->girpdata, &pvt->warned) != 0) {
+ return;
+ }
+
+ if (irs_irp_send_command(pvt->girpdata, "setservent") != 0) {
+ return;
+ }
+
+ code = irs_irp_read_response(pvt->girpdata, text, sizeof text);
+ if (code != IRPD_GETSERVICE_SETOK) {
+ if (irp_log_errors) {
+ syslog(LOG_WARNING, "setservent failed: %s", text);
+ }
+ }
+
+ return;
+}
+
+/*%
+ * void sv_minimize(struct irs_sv *this)
+ *
+ */
+
+static void
+sv_minimize(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ irs_irp_disconnect(pvt->girpdata);
+}
+
+
+
+
+
+
+static void
+free_service(struct servent *sv) {
+ char **p;
+
+ if (sv == NULL) {
+ return;
+ }
+
+ if (sv->s_name != NULL) {
+ free(sv->s_name);
+ }
+
+ for (p = sv->s_aliases ; p != NULL && *p != NULL ; p++) {
+ free(*p);
+ }
+
+ if (sv->s_proto != NULL) {
+ free(sv->s_proto);
+ }
+}
+
+
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irpmarshall.c b/usr/src/lib/libresolv2_joy/common/irs/irpmarshall.c
new file mode 100644
index 0000000000..a33bb5bfbd
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irpmarshall.c
@@ -0,0 +1,2297 @@
+/*
+ * Copyright(c) 1989, 1993, 1995
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if 0
+
+Check values are in approrpriate endian order.
+
+Double check memory allocations on unmarhsalling
+
+#endif
+
+
+/* Extern */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <utmp.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <irs.h>
+#include <isc/memcluster.h>
+#include <isc/irpmarshall.h>
+
+#include "port_after.h"
+
+
+#ifndef HAVE_STRNDUP
+static char *strndup(const char *str, size_t len);
+#endif
+
+static char **splitarray(const char *buffer, const char *buffend, char delim);
+static int joinarray(char * const * argv, char *buffer, char delim);
+static char *getfield(char **res, size_t reslen, char **buffer, char delim);
+static size_t joinlength(char * const *argv);
+static void free_array(char **argv, size_t entries);
+
+#define ADDR_T_STR(x) (x == AF_INET ? "AF_INET" :\
+ (x == AF_INET6 ? "AF_INET6" : "UNKNOWN"))
+
+#define MAXPADDRSIZE (sizeof "255.255.255.255" + 1)
+
+static char COMMA = ',';
+
+static const char *COMMASTR = ",";
+static const char *COLONSTR = ":";
+
+
+
+/* See big comment at bottom of irpmarshall.h for description. */
+
+
+#ifdef WANT_IRS_PW
+/* +++++++++++++++++++++++++ struct passwd +++++++++++++++++++++++++ */
+
+/*%
+ * int irp_marshall_pw(const struct passwd *pw, char **buffer, size_t *len)
+ *
+ * notes: \li
+ *
+ * See irpmarshall.h
+ *
+ * return: \li
+ *
+ * 0 on sucess, -1 on failure.
+ *
+ */
+
+int
+irp_marshall_pw(const struct passwd *pw, char **buffer, size_t *len) {
+ size_t need = 1 ; /*%< for null byte */
+ char pwUid[24];
+ char pwGid[24];
+ char pwChange[24];
+ char pwExpire[24];
+ const char *pwClass;
+ const char *fieldsep = COLONSTR;
+
+ if (pw == NULL || len == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ sprintf(pwUid, "%ld", (long)pw->pw_uid);
+ sprintf(pwGid, "%ld", (long)pw->pw_gid);
+
+#ifdef HAVE_PW_CHANGE
+ sprintf(pwChange, "%ld", (long)pw->pw_change);
+#else
+ pwChange[0] = '0';
+ pwChange[1] = '\0';
+#endif
+
+#ifdef HAVE_PW_EXPIRE
+ sprintf(pwExpire, "%ld", (long)pw->pw_expire);
+#else
+ pwExpire[0] = '0';
+ pwExpire[1] = '\0';
+#endif
+
+#ifdef HAVE_PW_CLASS
+ pwClass = pw->pw_class;
+#else
+ pwClass = "";
+#endif
+
+ need += strlen(pw->pw_name) + 1; /*%< one for fieldsep */
+ need += strlen(pw->pw_passwd) + 1;
+ need += strlen(pwUid) + 1;
+ need += strlen(pwGid) + 1;
+ need += strlen(pwClass) + 1;
+ need += strlen(pwChange) + 1;
+ need += strlen(pwExpire) + 1;
+ need += strlen(pw->pw_gecos) + 1;
+ need += strlen(pw->pw_dir) + 1;
+ need += strlen(pw->pw_shell) + 1;
+
+ if (buffer == NULL) {
+ *len = need;
+ return (0);
+ }
+
+ if (*buffer != NULL && need > *len) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (*buffer == NULL) {
+ need += 2; /*%< for CRLF */
+ *buffer = memget(need);
+ if (*buffer == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ *len = need;
+ }
+
+ strcpy(*buffer, pw->pw_name); strcat(*buffer, fieldsep);
+ strcat(*buffer, pw->pw_passwd); strcat(*buffer, fieldsep);
+ strcat(*buffer, pwUid); strcat(*buffer, fieldsep);
+ strcat(*buffer, pwGid); strcat(*buffer, fieldsep);
+ strcat(*buffer, pwClass); strcat(*buffer, fieldsep);
+ strcat(*buffer, pwChange); strcat(*buffer, fieldsep);
+ strcat(*buffer, pwExpire); strcat(*buffer, fieldsep);
+ strcat(*buffer, pw->pw_gecos); strcat(*buffer, fieldsep);
+ strcat(*buffer, pw->pw_dir); strcat(*buffer, fieldsep);
+ strcat(*buffer, pw->pw_shell); strcat(*buffer, fieldsep);
+
+ return (0);
+}
+
+/*%
+ * int irp_unmarshall_pw(struct passwd *pw, char *buffer)
+ *
+ * notes: \li
+ *
+ * See irpmarshall.h
+ *
+ * return: \li
+ *
+ * 0 on success, -1 on failure
+ *
+ */
+
+int
+irp_unmarshall_pw(struct passwd *pw, char *buffer) {
+ char *name, *pass, *class, *gecos, *dir, *shell;
+ uid_t pwuid;
+ gid_t pwgid;
+ time_t pwchange;
+ time_t pwexpire;
+ char *p;
+ long t;
+ char tmpbuf[24];
+ char *tb = &tmpbuf[0];
+ char fieldsep = ':';
+ int myerrno = EINVAL;
+
+ name = pass = class = gecos = dir = shell = NULL;
+ p = buffer;
+
+ /* pw_name field */
+ name = NULL;
+ if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0) {
+ goto error;
+ }
+
+ /* pw_passwd field */
+ pass = NULL;
+ if (getfield(&pass, 0, &p, fieldsep) == NULL) { /*%< field can be empty */
+ goto error;
+ }
+
+
+ /* pw_uid field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0) {
+ goto error;
+ }
+ t = strtol(tmpbuf, &tb, 10);
+ if (*tb) {
+ goto error; /*%< junk in value */
+ }
+ pwuid = (uid_t)t;
+ if ((long) pwuid != t) { /*%< value must have been too big. */
+ goto error;
+ }
+
+
+
+ /* pw_gid field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0) {
+ goto error;
+ }
+ t = strtol(tmpbuf, &tb, 10);
+ if (*tb) {
+ goto error; /*%< junk in value */
+ }
+ pwgid = (gid_t)t;
+ if ((long)pwgid != t) { /*%< value must have been too big. */
+ goto error;
+ }
+
+
+
+ /* pw_class field */
+ class = NULL;
+ if (getfield(&class, 0, &p, fieldsep) == NULL) {
+ goto error;
+ }
+
+
+
+ /* pw_change field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0) {
+ goto error;
+ }
+ t = strtol(tmpbuf, &tb, 10);
+ if (*tb) {
+ goto error; /*%< junk in value */
+ }
+ pwchange = (time_t)t;
+ if ((long)pwchange != t) { /*%< value must have been too big. */
+ goto error;
+ }
+
+
+
+ /* pw_expire field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0) {
+ goto error;
+ }
+ t = strtol(tmpbuf, &tb, 10);
+ if (*tb) {
+ goto error; /*%< junk in value */
+ }
+ pwexpire = (time_t)t;
+ if ((long) pwexpire != t) { /*%< value must have been too big. */
+ goto error;
+ }
+
+
+
+ /* pw_gecos field */
+ gecos = NULL;
+ if (getfield(&gecos, 0, &p, fieldsep) == NULL) {
+ goto error;
+ }
+
+
+
+ /* pw_dir field */
+ dir = NULL;
+ if (getfield(&dir, 0, &p, fieldsep) == NULL) {
+ goto error;
+ }
+
+
+
+ /* pw_shell field */
+ shell = NULL;
+ if (getfield(&shell, 0, &p, fieldsep) == NULL) {
+ goto error;
+ }
+
+
+
+ pw->pw_name = name;
+ pw->pw_passwd = pass;
+ pw->pw_uid = pwuid;
+ pw->pw_gid = pwgid;
+ pw->pw_gecos = gecos;
+ pw->pw_dir = dir;
+ pw->pw_shell = shell;
+
+#ifdef HAVE_PW_CHANGE
+ pw->pw_change = pwchange;
+#endif
+#ifdef HAVE_PW_CLASS
+ pw->pw_class = class;
+#endif
+#ifdef HAVE_PW_EXPIRE
+ pw->pw_expire = pwexpire;
+#endif
+
+ return (0);
+
+ error:
+ errno = myerrno;
+
+ if (name != NULL) free(name);
+ if (pass != NULL) free(pass);
+ if (gecos != NULL) free(gecos);
+ if (dir != NULL) free(dir);
+ if (shell != NULL) free(shell);
+
+ return (-1);
+}
+
+/* ------------------------- struct passwd ------------------------- */
+#endif /* WANT_IRS_PW */
+/* +++++++++++++++++++++++++ struct group +++++++++++++++++++++++++ */
+
+/*%
+ * int irp_marshall_gr(const struct group *gr, char **buffer, size_t *len)
+ *
+ * notes: \li
+ *
+ * See irpmarshall.h.
+ *
+ * return: \li
+ *
+ * 0 on success, -1 on failure
+ */
+
+int
+irp_marshall_gr(const struct group *gr, char **buffer, size_t *len) {
+ size_t need = 1; /*%< for null byte */
+ char grGid[24];
+ const char *fieldsep = COLONSTR;
+
+ if (gr == NULL || len == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ sprintf(grGid, "%ld", (long)gr->gr_gid);
+
+ need += strlen(gr->gr_name) + 1;
+#ifndef MISSING_GR_PASSWD
+ need += strlen(gr->gr_passwd) + 1;
+#else
+ need++;
+#endif
+ need += strlen(grGid) + 1;
+ need += joinlength(gr->gr_mem) + 1;
+
+ if (buffer == NULL) {
+ *len = need;
+ return (0);
+ }
+
+ if (*buffer != NULL && need > *len) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (*buffer == NULL) {
+ need += 2; /*%< for CRLF */
+ *buffer = memget(need);
+ if (*buffer == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ *len = need;
+ }
+
+ strcpy(*buffer, gr->gr_name); strcat(*buffer, fieldsep);
+#ifndef MISSING_GR_PASSWD
+ strcat(*buffer, gr->gr_passwd);
+#endif
+ strcat(*buffer, fieldsep);
+ strcat(*buffer, grGid); strcat(*buffer, fieldsep);
+ joinarray(gr->gr_mem, *buffer, COMMA) ; strcat(*buffer, fieldsep);
+
+ return (0);
+}
+
+/*%
+ * int irp_unmarshall_gr(struct group *gr, char *buffer)
+ *
+ * notes: \li
+ *
+ * See irpmarshall.h
+ *
+ * return: \li
+ *
+ * 0 on success and -1 on failure.
+ *
+ */
+
+int
+irp_unmarshall_gr(struct group *gr, char *buffer) {
+ char *p, *q;
+ gid_t grgid;
+ long t;
+ char *name = NULL;
+ char *pass = NULL;
+ char **members = NULL;
+ char tmpbuf[24];
+ char *tb;
+ char fieldsep = ':';
+ int myerrno = EINVAL;
+
+ if (gr == NULL || buffer == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ p = buffer;
+
+ /* gr_name field */
+ name = NULL;
+ if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
+ goto error;
+ }
+
+
+ /* gr_passwd field */
+ pass = NULL;
+ if (getfield(&pass, 0, &p, fieldsep) == NULL) {
+ goto error;
+ }
+
+
+ /* gr_gid field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0U) {
+ goto error;
+ }
+ t = strtol(tmpbuf, &tb, 10);
+ if (*tb) {
+ goto error; /*%< junk in value */
+ }
+ grgid = (gid_t)t;
+ if ((long) grgid != t) { /*%< value must have been too big. */
+ goto error;
+ }
+
+
+ /* gr_mem field. Member names are separated by commas */
+ q = strchr(p, fieldsep);
+ if (q == NULL) {
+ goto error;
+ }
+ members = splitarray(p, q, COMMA);
+ if (members == NULL) {
+ myerrno = errno;
+ goto error;
+ }
+ p = q + 1;
+
+
+ gr->gr_name = name;
+#ifndef MISSING_GR_PASSWD
+ gr->gr_passwd = pass;
+#endif
+ gr->gr_gid = grgid;
+ gr->gr_mem = members;
+
+ return (0);
+
+ error:
+ errno = myerrno;
+
+ if (name != NULL) free(name);
+ if (pass != NULL) free(pass);
+
+ return (-1);
+}
+
+
+/* ------------------------- struct group ------------------------- */
+
+
+
+
+/* +++++++++++++++++++++++++ struct servent +++++++++++++++++++++++++ */
+
+/*%
+ * int irp_marshall_sv(const struct servent *sv, char **buffer, size_t *len)
+ *
+ * notes: \li
+ *
+ * See irpmarshall.h
+ *
+ * return: \li
+ *
+ * 0 on success, -1 on failure.
+ *
+ */
+
+int
+irp_marshall_sv(const struct servent *sv, char **buffer, size_t *len) {
+ size_t need = 1; /*%< for null byte */
+ char svPort[24];
+ const char *fieldsep = COLONSTR;
+ short realport;
+
+ if (sv == NULL || len == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* the int s_port field is actually a short in network order. We
+ want host order to make the marshalled data look correct */
+ realport = ntohs((short)sv->s_port);
+ sprintf(svPort, "%d", realport);
+
+ need += strlen(sv->s_name) + 1;
+ need += joinlength(sv->s_aliases) + 1;
+ need += strlen(svPort) + 1;
+ need += strlen(sv->s_proto) + 1;
+
+ if (buffer == NULL) {
+ *len = need;
+ return (0);
+ }
+
+ if (*buffer != NULL && need > *len) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (*buffer == NULL) {
+ need += 2; /*%< for CRLF */
+ *buffer = memget(need);
+ if (*buffer == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ *len = need;
+ }
+
+ strcpy(*buffer, sv->s_name); strcat(*buffer, fieldsep);
+ joinarray(sv->s_aliases, *buffer, COMMA); strcat(*buffer, fieldsep);
+ strcat(*buffer, svPort); strcat(*buffer, fieldsep);
+ strcat(*buffer, sv->s_proto); strcat(*buffer, fieldsep);
+
+ return (0);
+}
+
+/*%
+ * int irp_unmarshall_sv(struct servent *sv, char *buffer)
+ *
+ * notes: \li
+ *
+ * See irpmarshall.h
+ *
+ * return: \li
+ *
+ * 0 on success, -1 on failure.
+ *
+ */
+
+int
+irp_unmarshall_sv(struct servent *sv, char *buffer) {
+ char *p, *q;
+ short svport;
+ long t;
+ char *name = NULL;
+ char *proto = NULL;
+ char **aliases = NULL;
+ char tmpbuf[24];
+ char *tb;
+ char fieldsep = ':';
+ int myerrno = EINVAL;
+
+ if (sv == NULL || buffer == NULL)
+ return (-1);
+
+ p = buffer;
+
+
+ /* s_name field */
+ name = NULL;
+ if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
+ goto error;
+ }
+
+
+ /* s_aliases field */
+ q = strchr(p, fieldsep);
+ if (q == NULL) {
+ goto error;
+ }
+ aliases = splitarray(p, q, COMMA);
+ if (aliases == NULL) {
+ myerrno = errno;
+ goto error;
+ }
+ p = q + 1;
+
+
+ /* s_port field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0U) {
+ goto error;
+ }
+ t = strtol(tmpbuf, &tb, 10);
+ if (*tb) {
+ goto error; /*%< junk in value */
+ }
+ svport = (short)t;
+ if ((long) svport != t) { /*%< value must have been too big. */
+ goto error;
+ }
+ svport = htons(svport);
+
+ /* s_proto field */
+ proto = NULL;
+ if (getfield(&proto, 0, &p, fieldsep) == NULL) {
+ goto error;
+ }
+
+ sv->s_name = name;
+ sv->s_aliases = aliases;
+ sv->s_port = svport;
+ sv->s_proto = proto;
+
+ return (0);
+
+ error:
+ errno = myerrno;
+
+ if (name != NULL) free(name);
+ if (proto != NULL) free(proto);
+ free_array(aliases, 0);
+
+ return (-1);
+}
+
+
+/* ------------------------- struct servent ------------------------- */
+
+/* +++++++++++++++++++++++++ struct protoent +++++++++++++++++++++++++ */
+
+/*%
+ * int irp_marshall_pr(struct protoent *pr, char **buffer, size_t *len)
+ *
+ * notes: \li
+ *
+ * See irpmarshall.h
+ *
+ * return: \li
+ *
+ * 0 on success and -1 on failure.
+ *
+ */
+
+int
+irp_marshall_pr(struct protoent *pr, char **buffer, size_t *len) {
+ size_t need = 1; /*%< for null byte */
+ char prProto[24];
+ const char *fieldsep = COLONSTR;
+
+ if (pr == NULL || len == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ sprintf(prProto, "%d", (int)pr->p_proto);
+
+ need += strlen(pr->p_name) + 1;
+ need += joinlength(pr->p_aliases) + 1;
+ need += strlen(prProto) + 1;
+
+ if (buffer == NULL) {
+ *len = need;
+ return (0);
+ }
+
+ if (*buffer != NULL && need > *len) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (*buffer == NULL) {
+ need += 2; /*%< for CRLF */
+ *buffer = memget(need);
+ if (*buffer == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ *len = need;
+ }
+
+ strcpy(*buffer, pr->p_name); strcat(*buffer, fieldsep);
+ joinarray(pr->p_aliases, *buffer, COMMA); strcat(*buffer, fieldsep);
+ strcat(*buffer, prProto); strcat(*buffer, fieldsep);
+
+ return (0);
+
+}
+
+/*%
+ * int irp_unmarshall_pr(struct protoent *pr, char *buffer)
+ *
+ * notes: \li
+ *
+ * See irpmarshall.h
+ *
+ * return: \li
+ *
+ * 0 on success, -1 on failure
+ *
+ */
+
+int irp_unmarshall_pr(struct protoent *pr, char *buffer) {
+ char *p, *q;
+ int prproto;
+ long t;
+ char *name = NULL;
+ char **aliases = NULL;
+ char tmpbuf[24];
+ char *tb;
+ char fieldsep = ':';
+ int myerrno = EINVAL;
+
+ if (pr == NULL || buffer == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ p = buffer;
+
+ /* p_name field */
+ name = NULL;
+ if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
+ goto error;
+ }
+
+
+ /* p_aliases field */
+ q = strchr(p, fieldsep);
+ if (q == NULL) {
+ goto error;
+ }
+ aliases = splitarray(p, q, COMMA);
+ if (aliases == NULL) {
+ myerrno = errno;
+ goto error;
+ }
+ p = q + 1;
+
+
+ /* p_proto field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0U) {
+ goto error;
+ }
+ t = strtol(tmpbuf, &tb, 10);
+ if (*tb) {
+ goto error; /*%< junk in value */
+ }
+ prproto = (int)t;
+ if ((long) prproto != t) { /*%< value must have been too big. */
+ goto error;
+ }
+
+ pr->p_name = name;
+ pr->p_aliases = aliases;
+ pr->p_proto = prproto;
+
+ return (0);
+
+ error:
+ errno = myerrno;
+
+ if (name != NULL) free(name);
+ free_array(aliases, 0);
+
+ return (-1);
+}
+
+/* ------------------------- struct protoent ------------------------- */
+
+
+
+/* +++++++++++++++++++++++++ struct hostent +++++++++++++++++++++++++ */
+
+/*%
+ * int irp_marshall_ho(struct hostent *ho, char **buffer, size_t *len)
+ *
+ * notes: \li
+ *
+ * See irpmarshall.h.
+ *
+ * return: \li
+ *
+ * 0 on success, -1 on failure.
+ *
+ */
+
+int
+irp_marshall_ho(struct hostent *ho, char **buffer, size_t *len) {
+ size_t need = 1; /*%< for null byte */
+ char hoaddrtype[24];
+ char holength[24];
+ char **av;
+ char *p;
+ int addrlen;
+ int malloced = 0;
+ size_t remlen;
+ const char *fieldsep = "@";
+
+ if (ho == NULL || len == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ switch(ho->h_addrtype) {
+ case AF_INET:
+ strcpy(hoaddrtype, "AF_INET");
+ break;
+
+ case AF_INET6:
+ strcpy(hoaddrtype, "AF_INET6");
+ break;
+
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ sprintf(holength, "%d", ho->h_length);
+
+ need += strlen(ho->h_name) + 1;
+ need += joinlength(ho->h_aliases) + 1;
+ need += strlen(hoaddrtype) + 1;
+ need += strlen(holength) + 1;
+
+ /* we determine an upper bound on the string length needed, not an
+ exact length. */
+ addrlen = (ho->h_addrtype == AF_INET ? 16 : 46) ; /*%< XX other AF's?? */
+ for (av = ho->h_addr_list; av != NULL && *av != NULL ; av++)
+ need += addrlen;
+
+ if (buffer == NULL) {
+ *len = need;
+ return (0);
+ }
+
+ if (*buffer != NULL && need > *len) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (*buffer == NULL) {
+ need += 2; /*%< for CRLF */
+ *buffer = memget(need);
+ if (*buffer == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ *len = need;
+ malloced = 1;
+ }
+
+ strcpy(*buffer, ho->h_name); strcat(*buffer, fieldsep);
+ joinarray(ho->h_aliases, *buffer, COMMA); strcat(*buffer, fieldsep);
+ strcat(*buffer, hoaddrtype); strcat(*buffer, fieldsep);
+ strcat(*buffer, holength); strcat(*buffer, fieldsep);
+
+ p = *buffer + strlen(*buffer);
+ remlen = need - strlen(*buffer);
+ for (av = ho->h_addr_list ; av != NULL && *av != NULL ; av++) {
+ if (inet_ntop(ho->h_addrtype, *av, p, remlen) == NULL) {
+ goto error;
+ }
+ if (*(av + 1) != NULL)
+ strcat(p, COMMASTR);
+ remlen -= strlen(p);
+ p += strlen(p);
+ }
+ strcat(*buffer, fieldsep);
+
+ return (0);
+
+ error:
+ if (malloced) {
+ memput(*buffer, need);
+ }
+
+ return (-1);
+}
+
+/*%
+ * int irp_unmarshall_ho(struct hostent *ho, char *buffer)
+ *
+ * notes: \li
+ *
+ * See irpmarshall.h.
+ *
+ * return: \li
+ *
+ * 0 on success, -1 on failure.
+ *
+ */
+
+int
+irp_unmarshall_ho(struct hostent *ho, char *buffer) {
+ char *p, *q, *r;
+ int hoaddrtype;
+ int holength;
+ long t;
+ char *name;
+ char **aliases = NULL;
+ char **hohaddrlist = NULL;
+ size_t hoaddrsize;
+ char tmpbuf[24];
+ char *tb;
+ char **alist;
+ int addrcount;
+ char fieldsep = '@';
+ int myerrno = EINVAL;
+
+ if (ho == NULL || buffer == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ p = buffer;
+
+ /* h_name field */
+ name = NULL;
+ if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
+ goto error;
+ }
+
+
+ /* h_aliases field */
+ q = strchr(p, fieldsep);
+ if (q == NULL) {
+ goto error;
+ }
+ aliases = splitarray(p, q, COMMA);
+ if (aliases == NULL) {
+ myerrno = errno;
+ goto error;
+ }
+ p = q + 1;
+
+
+ /* h_addrtype field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0U) {
+ goto error;
+ }
+ if (strcmp(tmpbuf, "AF_INET") == 0)
+ hoaddrtype = AF_INET;
+ else if (strcmp(tmpbuf, "AF_INET6") == 0)
+ hoaddrtype = AF_INET6;
+ else
+ goto error;
+
+
+ /* h_length field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0U) {
+ goto error;
+ }
+ t = strtol(tmpbuf, &tb, 10);
+ if (*tb) {
+ goto error; /*%< junk in value */
+ }
+ holength = (int)t;
+ if ((long) holength != t) { /*%< value must have been too big. */
+ goto error;
+ }
+
+
+ /* h_addr_list field */
+ q = strchr(p, fieldsep);
+ if (q == NULL)
+ goto error;
+
+ /* count how many addresss are in there */
+ if (q > p + 1) {
+ for (addrcount = 1, r = p ; r != q ; r++) {
+ if (*r == COMMA)
+ addrcount++;
+ }
+ } else {
+ addrcount = 0;
+ }
+
+ hoaddrsize = (addrcount + 1) * sizeof (char *);
+ hohaddrlist = malloc(hoaddrsize);
+ if (hohaddrlist == NULL) {
+ myerrno = ENOMEM;
+ goto error;
+ }
+
+ memset(hohaddrlist, 0x0, hoaddrsize);
+
+ alist = hohaddrlist;
+ for (t = 0, r = p ; r != q ; p = r + 1, t++) {
+ char saved;
+ while (r != q && *r != COMMA) r++;
+ saved = *r;
+ *r = 0x0;
+
+ alist[t] = malloc(hoaddrtype == AF_INET ? 4 : 16);
+ if (alist[t] == NULL) {
+ myerrno = ENOMEM;
+ goto error;
+ }
+
+ if (inet_pton(hoaddrtype, p, alist[t]) == -1)
+ goto error;
+ *r = saved;
+ }
+ alist[t] = NULL;
+
+ ho->h_name = name;
+ ho->h_aliases = aliases;
+ ho->h_addrtype = hoaddrtype;
+ ho->h_length = holength;
+ ho->h_addr_list = hohaddrlist;
+
+ return (0);
+
+ error:
+ errno = myerrno;
+
+ if (name != NULL) free(name);
+ free_array(hohaddrlist, 0);
+ free_array(aliases, 0);
+
+ return (-1);
+}
+
+/* ------------------------- struct hostent------------------------- */
+
+
+
+/* +++++++++++++++++++++++++ struct netgrp +++++++++++++++++++++++++ */
+
+/*%
+ * int irp_marshall_ng(const char *host, const char *user,
+ * const char *domain, char *buffer, size_t *len)
+ *
+ * notes: \li
+ *
+ * See note for irp_marshall_ng_start
+ *
+ * return: \li
+ *
+ * 0 on success, 0 on failure.
+ *
+ */
+
+int
+irp_marshall_ng(const char *host, const char *user, const char *domain,
+ char **buffer, size_t *len) {
+ size_t need = 1; /*%< for nul byte */
+ const char *fieldsep = ",";
+
+ if (len == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ need += 4; /*%< two parens and two commas */
+ need += (host == NULL ? 0 : strlen(host));
+ need += (user == NULL ? 0 : strlen(user));
+ need += (domain == NULL ? 0 : strlen(domain));
+
+ if (buffer == NULL) {
+ *len = need;
+ return (0);
+ } else if (*buffer != NULL && need > *len) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (*buffer == NULL) {
+ need += 2; /*%< for CRLF */
+ *buffer = memget(need);
+ if (*buffer == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ *len = need;
+ }
+
+ (*buffer)[0] = '(';
+ (*buffer)[1] = '\0';
+
+ if (host != NULL)
+ strcat(*buffer, host);
+ strcat(*buffer, fieldsep);
+
+ if (user != NULL)
+ strcat(*buffer, user);
+ strcat(*buffer, fieldsep);
+
+ if (domain != NULL)
+ strcat(*buffer, domain);
+ strcat(*buffer, ")");
+
+ return (0);
+}
+
+
+
+/* ---------- */
+
+/*%
+ * int irp_unmarshall_ng(const char **host, const char **user,
+ * const char **domain, char *buffer)
+ *
+ * notes: \li
+ *
+ * Unpacks the BUFFER into 3 character arrays it allocates and assigns
+ * to *HOST, *USER and *DOMAIN. If any field of the value is empty,
+ * then the corresponding paramater value will be set to NULL.
+ *
+ * return: \li
+ *
+ * 0 on success and -1 on failure.
+ */
+
+int
+irp_unmarshall_ng(const char **hostp, const char **userp, const char **domainp,
+ char *buffer)
+{
+ char *p, *q;
+ char fieldsep = ',';
+ int myerrno = EINVAL;
+ char *host, *user, *domain;
+
+ if (userp == NULL || hostp == NULL ||
+ domainp == NULL || buffer == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ host = user = domain = NULL;
+
+ p = buffer;
+ while (isspace((unsigned char)*p)) {
+ p++;
+ }
+ if (*p != '(') {
+ goto error;
+ }
+
+ q = p + 1;
+ while (*q && *q != fieldsep)
+ q++;
+ if (!*q) {
+ goto error;
+ } else if (q > p + 1) {
+ host = strndup(p, q - p);
+ }
+
+ p = q + 1;
+ if (!*p) {
+ goto error;
+ } else if (*p != fieldsep) {
+ q = p + 1;
+ while (*q && *q != fieldsep)
+ q++;
+ if (!*q) {
+ goto error;
+ }
+ user = strndup(p, q - p);
+ } else {
+ p++;
+ }
+
+ if (!*p) {
+ goto error;
+ } else if (*p != ')') {
+ q = p + 1;
+ while (*q && *q != ')')
+ q++;
+ if (!*q) {
+ goto error;
+ }
+ domain = strndup(p, q - p);
+ }
+ *hostp = host;
+ *userp = user;
+ *domainp = domain;
+
+ return (0);
+
+ error:
+ errno = myerrno;
+
+ if (host != NULL) free(host);
+ if (user != NULL) free(user);
+
+ return (-1);
+}
+
+/* ------------------------- struct netgrp ------------------------- */
+
+
+
+
+/* +++++++++++++++++++++++++ struct nwent +++++++++++++++++++++++++ */
+
+/*%
+ * int irp_marshall_nw(struct nwent *ne, char **buffer, size_t *len)
+ *
+ * notes: \li
+ *
+ * See at top.
+ *
+ * return: \li
+ *
+ * 0 on success and -1 on failure.
+ *
+ */
+
+int
+irp_marshall_nw(struct nwent *ne, char **buffer, size_t *len) {
+ size_t need = 1; /*%< for null byte */
+ char nAddrType[24];
+ char nNet[MAXPADDRSIZE];
+ const char *fieldsep = COLONSTR;
+
+ if (ne == NULL || len == NULL) {
+ return (-1);
+ }
+
+ strcpy(nAddrType, ADDR_T_STR(ne->n_addrtype));
+
+ if (inet_net_ntop(ne->n_addrtype, ne->n_addr, ne->n_length,
+ nNet, sizeof nNet) == NULL) {
+ return (-1);
+ }
+
+
+ need += strlen(ne->n_name) + 1;
+ need += joinlength(ne->n_aliases) + 1;
+ need += strlen(nAddrType) + 1;
+ need += strlen(nNet) + 1;
+
+ if (buffer == NULL) {
+ *len = need;
+ return (0);
+ }
+
+ if (*buffer != NULL && need > *len) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (*buffer == NULL) {
+ need += 2; /*%< for CRLF */
+ *buffer = memget(need);
+ if (*buffer == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ *len = need;
+ }
+
+ strcpy(*buffer, ne->n_name); strcat(*buffer, fieldsep);
+ joinarray(ne->n_aliases, *buffer, COMMA) ; strcat(*buffer, fieldsep);
+ strcat(*buffer, nAddrType); strcat(*buffer, fieldsep);
+ strcat(*buffer, nNet); strcat(*buffer, fieldsep);
+
+ return (0);
+}
+
+/*%
+ * int irp_unmarshall_nw(struct nwent *ne, char *buffer)
+ *
+ * notes: \li
+ *
+ * See note up top.
+ *
+ * return: \li
+ *
+ * 0 on success and -1 on failure.
+ *
+ */
+
+int
+irp_unmarshall_nw(struct nwent *ne, char *buffer) {
+ char *p, *q;
+ int naddrtype;
+ long nnet;
+ int bits;
+ char *name = NULL;
+ char **aliases = NULL;
+ char tmpbuf[24];
+ char *tb;
+ char fieldsep = ':';
+ int myerrno = EINVAL;
+
+ if (ne == NULL || buffer == NULL) {
+ goto error;
+ }
+
+ p = buffer;
+
+ /* n_name field */
+ name = NULL;
+ if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
+ goto error;
+ }
+
+
+ /* n_aliases field. Aliases are separated by commas */
+ q = strchr(p, fieldsep);
+ if (q == NULL) {
+ goto error;
+ }
+ aliases = splitarray(p, q, COMMA);
+ if (aliases == NULL) {
+ myerrno = errno;
+ goto error;
+ }
+ p = q + 1;
+
+
+ /* h_addrtype field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0U) {
+ goto error;
+ }
+ if (strcmp(tmpbuf, "AF_INET") == 0)
+ naddrtype = AF_INET;
+ else if (strcmp(tmpbuf, "AF_INET6") == 0)
+ naddrtype = AF_INET6;
+ else
+ goto error;
+
+
+ /* n_net field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0U) {
+ goto error;
+ }
+ nnet = 0;
+ bits = inet_net_pton(naddrtype, tmpbuf, &nnet, sizeof nnet);
+ if (bits < 0) {
+ goto error;
+ }
+
+ /* nnet = ntohl(nnet); */ /* keep in network order for nwent */
+
+ ne->n_name = name;
+ ne->n_aliases = aliases;
+ ne->n_addrtype = naddrtype;
+ ne->n_length = bits;
+ ne->n_addr = malloc(sizeof nnet);
+ if (ne->n_addr == NULL) {
+ goto error;
+ }
+
+ memcpy(ne->n_addr, &nnet, sizeof nnet);
+
+ return (0);
+
+ error:
+ errno = myerrno;
+
+ if (name != NULL) free(name);
+ free_array(aliases, 0);
+
+ return (-1);
+}
+
+
+/* ------------------------- struct nwent ------------------------- */
+
+
+/* +++++++++++++++++++++++++ struct netent +++++++++++++++++++++++++ */
+
+/*%
+ * int irp_marshall_ne(struct netent *ne, char **buffer, size_t *len)
+ *
+ * notes: \li
+ *
+ * See at top.
+ *
+ * return: \li
+ *
+ * 0 on success and -1 on failure.
+ *
+ */
+
+int
+irp_marshall_ne(struct netent *ne, char **buffer, size_t *len) {
+ size_t need = 1; /*%< for null byte */
+ char nAddrType[24];
+ char nNet[MAXPADDRSIZE];
+ const char *fieldsep = COLONSTR;
+ long nval;
+
+ if (ne == NULL || len == NULL) {
+ return (-1);
+ }
+
+ strcpy(nAddrType, ADDR_T_STR(ne->n_addrtype));
+
+ nval = htonl(ne->n_net);
+ if (inet_ntop(ne->n_addrtype, &nval, nNet, sizeof nNet) == NULL) {
+ return (-1);
+ }
+
+ need += strlen(ne->n_name) + 1;
+ need += joinlength(ne->n_aliases) + 1;
+ need += strlen(nAddrType) + 1;
+ need += strlen(nNet) + 1;
+
+ if (buffer == NULL) {
+ *len = need;
+ return (0);
+ }
+
+ if (*buffer != NULL && need > *len) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (*buffer == NULL) {
+ need += 2; /*%< for CRLF */
+ *buffer = memget(need);
+ if (*buffer == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ *len = need;
+ }
+
+ strcpy(*buffer, ne->n_name); strcat(*buffer, fieldsep);
+ joinarray(ne->n_aliases, *buffer, COMMA) ; strcat(*buffer, fieldsep);
+ strcat(*buffer, nAddrType); strcat(*buffer, fieldsep);
+ strcat(*buffer, nNet); strcat(*buffer, fieldsep);
+
+ return (0);
+}
+
+/*%
+ * int irp_unmarshall_ne(struct netent *ne, char *buffer)
+ *
+ * notes: \li
+ *
+ * See note up top.
+ *
+ * return: \li
+ *
+ * 0 on success and -1 on failure.
+ *
+ */
+
+int
+irp_unmarshall_ne(struct netent *ne, char *buffer) {
+ char *p, *q;
+ int naddrtype;
+ long nnet;
+ int bits;
+ char *name = NULL;
+ char **aliases = NULL;
+ char tmpbuf[24];
+ char *tb;
+ char fieldsep = ':';
+ int myerrno = EINVAL;
+
+ if (ne == NULL || buffer == NULL) {
+ goto error;
+ }
+
+ p = buffer;
+
+ /* n_name field */
+ name = NULL;
+ if (getfield(&name, 0, &p, fieldsep) == NULL || strlen(name) == 0U) {
+ goto error;
+ }
+
+
+ /* n_aliases field. Aliases are separated by commas */
+ q = strchr(p, fieldsep);
+ if (q == NULL) {
+ goto error;
+ }
+ aliases = splitarray(p, q, COMMA);
+ if (aliases == NULL) {
+ myerrno = errno;
+ goto error;
+ }
+ p = q + 1;
+
+
+ /* h_addrtype field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0U) {
+ goto error;
+ }
+ if (strcmp(tmpbuf, "AF_INET") == 0)
+ naddrtype = AF_INET;
+ else if (strcmp(tmpbuf, "AF_INET6") == 0)
+ naddrtype = AF_INET6;
+ else
+ goto error;
+
+
+ /* n_net field */
+ tb = tmpbuf;
+ if (getfield(&tb, sizeof tmpbuf, &p, fieldsep) == NULL ||
+ strlen(tb) == 0U) {
+ goto error;
+ }
+ bits = inet_net_pton(naddrtype, tmpbuf, &nnet, sizeof nnet);
+ if (bits < 0) {
+ goto error;
+ }
+ nnet = ntohl(nnet);
+
+ ne->n_name = name;
+ ne->n_aliases = aliases;
+ ne->n_addrtype = naddrtype;
+ ne->n_net = nnet;
+
+ return (0);
+
+ error:
+ errno = myerrno;
+
+ if (name != NULL) free(name);
+ free_array(aliases, 0);
+
+ return (-1);
+}
+
+
+/* ------------------------- struct netent ------------------------- */
+
+
+/* =========================================================================== */
+
+/*%
+ * static char ** splitarray(const char *buffer, const char *buffend, char delim)
+ *
+ * notes: \li
+ *
+ * Split a delim separated astring. Not allowed
+ * to have two delims next to each other. BUFFER points to begining of
+ * string, BUFFEND points to one past the end of the string
+ * (i.e. points at where the null byte would be if null
+ * terminated).
+ *
+ * return: \li
+ *
+ * Returns a malloced array of pointers, each pointer pointing to a
+ * malloced string. If BUFEER is an empty string, then return values is
+ * array of 1 pointer that is NULL. Returns NULL on failure.
+ *
+ */
+
+static char **
+splitarray(const char *buffer, const char *buffend, char delim) {
+ const char *p, *q;
+ int count = 0;
+ char **arr = NULL;
+ char **aptr;
+
+ if (buffend < buffer)
+ return (NULL);
+ else if (buffend > buffer && *buffer == delim)
+ return (NULL);
+ else if (buffend > buffer && *(buffend - 1) == delim)
+ return (NULL);
+
+ /* count the number of field and make sure none are empty */
+ if (buffend > buffer + 1) {
+ for (count = 1, q = buffer ; q != buffend ; q++) {
+ if (*q == delim) {
+ if (q > buffer && (*(q - 1) == delim)) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ count++;
+ }
+ }
+ }
+
+ if (count > 0) {
+ count++ ; /*%< for NULL at end */
+ aptr = arr = malloc(count * sizeof (char *));
+ if (aptr == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ memset(arr, 0x0, count * sizeof (char *));
+ for (p = buffer ; p < buffend ; p++) {
+ for (q = p ; *q != delim && q != buffend ; q++)
+ /* nothing */;
+ *aptr = strndup(p, q - p);
+
+ p = q;
+ aptr++;
+ }
+ *aptr = NULL;
+ } else {
+ arr = malloc(sizeof (char *));
+ if (arr == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ *arr = NULL;
+ }
+
+ return (arr);
+}
+
+/*%
+ * static size_t joinlength(char * const *argv)
+ *
+ * return: \li
+ *
+ * the number of bytes in all the arrays pointed at
+ * by argv, including their null bytes(which will usually be turned
+ * into commas).
+ *
+ *
+ */
+
+static size_t
+joinlength(char * const *argv) {
+ int len = 0;
+
+ while (argv && *argv) {
+ len += (strlen(*argv) + 1);
+ argv++;
+ }
+
+ return (len);
+}
+
+/*%
+ * int joinarray(char * const *argv, char *buffer, char delim)
+ *
+ * notes: \li
+ *
+ * Copy all the ARGV strings into the end of BUFFER
+ * separating them with DELIM. BUFFER is assumed to have
+ * enough space to hold everything and to be already null-terminated.
+ *
+ * return: \li
+ *
+ * 0 unless argv or buffer is NULL.
+ *
+ *
+ */
+
+static int
+joinarray(char * const *argv, char *buffer, char delim) {
+ char * const *p;
+ char sep[2];
+
+ if (argv == NULL || buffer == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ sep[0] = delim;
+ sep[1] = 0x0;
+
+ for (p = argv ; *p != NULL ; p++) {
+ strcat(buffer, *p);
+ if (*(p + 1) != NULL) {
+ strcat(buffer, sep);
+ }
+ }
+
+ return (0);
+}
+
+/*%
+ * static char * getfield(char **res, size_t reslen, char **ptr, char delim)
+ *
+ * notes: \li
+ *
+ * Stores in *RES, which is a buffer of length RESLEN, a
+ * copy of the bytes from *PTR up to and including the first
+ * instance of DELIM. If *RES is NULL, then it will be
+ * assigned a malloced buffer to hold the copy. *PTR is
+ * modified to point at the found delimiter.
+ *
+ * return: \li
+ *
+ * If there was no delimiter, then NULL is returned,
+ * otherewise *RES is returned.
+ *
+ */
+
+static char *
+getfield(char **res, size_t reslen, char **ptr, char delim) {
+ char *q;
+
+ if (res == NULL || ptr == NULL || *ptr == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ q = strchr(*ptr, delim);
+
+ if (q == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ } else {
+ if (*res == NULL) {
+ *res = strndup(*ptr, q - *ptr);
+ } else {
+ if ((size_t)(q - *ptr + 1) > reslen) { /*%< to big for res */
+ errno = EINVAL;
+ return (NULL);
+ } else {
+ strncpy(*res, *ptr, q - *ptr);
+ (*res)[q - *ptr] = 0x0;
+ }
+ }
+ *ptr = q + 1;
+ }
+
+ return (*res);
+}
+
+
+
+
+
+#ifndef HAVE_STRNDUP
+/*
+ * static char * strndup(const char *str, size_t len)
+ *
+ * notes: \li
+ *
+ * like strdup, except do len bytes instead of the whole string. Always
+ * null-terminates.
+ *
+ * return: \li
+ *
+ * The newly malloced string.
+ *
+ */
+
+static char *
+strndup(const char *str, size_t len) {
+ char *p = malloc(len + 1);
+
+ if (p == NULL)
+ return (NULL);
+ strncpy(p, str, len);
+ p[len] = 0x0;
+ return (p);
+}
+#endif
+
+#if WANT_MAIN
+
+/*%
+ * static int strcmp_nws(const char *a, const char *b)
+ *
+ * notes: \li
+ *
+ * do a strcmp, except uneven lengths of whitespace compare the same
+ *
+ * return: \li
+ *
+ */
+
+static int
+strcmp_nws(const char *a, const char *b) {
+ while (*a && *b) {
+ if (isspace(*a) && isspace(*b)) {
+ do {
+ a++;
+ } while (isspace(*a));
+ do {
+ b++;
+ } while (isspace(*b));
+ }
+ if (*a < *b)
+ return (-1);
+ else if (*a > *b)
+ return (1);
+
+ a++;
+ b++;;
+ }
+
+ if (*a == *b)
+ return (0);
+ else if (*a > *b)
+ return (1);
+ else
+ return (-1);
+}
+
+#endif
+
+/*%
+ * static void free_array(char **argv, size_t entries)
+ *
+ * notes: \li
+ *
+ * Free argv and each of the pointers inside it. The end of
+ * the array is when a NULL pointer is found inside. If
+ * entries is > 0, then NULL pointers inside the array do
+ * not indicate the end of the array.
+ *
+ */
+
+static void
+free_array(char **argv, size_t entries) {
+ char **p = argv;
+ int useEntries = (entries > 0U);
+
+ if (argv == NULL)
+ return;
+
+ while ((useEntries && entries > 0U) || *p) {
+ if (*p)
+ free(*p);
+ p++;
+ if (useEntries)
+ entries--;
+ }
+ free(argv);
+}
+
+
+
+
+
+/* ************************************************** */
+
+#if WANT_MAIN
+
+/*% takes an option to indicate what sort of marshalling(read the code) and
+ an argument. If the argument looks like a marshalled buffer(has a ':'
+ embedded) then it's unmarshalled and the remarshalled and the new string
+ is compared to the old one.
+*/
+
+int
+main(int argc, char **argv) {
+ char buffer[1024];
+ char *b = &buffer[0];
+ size_t len = sizeof buffer;
+ char option;
+
+ if (argc < 2 || argv[1][0] != '-')
+ exit(1);
+
+ option = argv[1][1];
+ argv++;
+ argc--;
+
+
+#if 0
+ {
+ char buff[10];
+ char *p = argv[1], *q = &buff[0];
+
+ while (getfield(&q, sizeof buff, &p, ':') != NULL) {
+ printf("field: \"%s\"\n", q);
+ p++;
+ }
+ printf("p is now \"%s\"\n", p);
+ }
+#endif
+
+#if 0
+ {
+ char **x = splitarray(argv[1], argv[1] + strlen(argv[1]),
+ argv[2][0]);
+ char **p;
+
+ if (x == NULL)
+ printf("split failed\n");
+
+ for (p = x ; p != NULL && *p != NULL ; p++) {
+ printf("\"%s\"\n", *p);
+ }
+ }
+#endif
+
+#if 1
+ switch(option) {
+ case 'n': {
+ struct nwent ne;
+ int i;
+
+ if (strchr(argv[1], ':') != NULL) {
+ if (irp_unmarshall_nw(&ne, argv[1]) != 0) {
+ printf("Unmarhsalling failed\n");
+ exit(1);
+ }
+
+ printf("Name: \"%s\"\n", ne.n_name);
+ printf("Aliases:");
+ for (i = 0 ; ne.n_aliases[i] != NULL ; i++)
+ printf("\n\t\"%s\"", ne.n_aliases[i]);
+ printf("\nAddrtype: %s\n", ADDR_T_STR(ne.n_addrtype));
+ inet_net_ntop(ne.n_addrtype, ne.n_addr, ne.n_length,
+ buffer, sizeof buffer);
+ printf("Net: \"%s\"\n", buffer);
+ *((long*)ne.n_addr) = htonl(*((long*)ne.n_addr));
+ inet_net_ntop(ne.n_addrtype, ne.n_addr, ne.n_length,
+ buffer, sizeof buffer);
+ printf("Corrected Net: \"%s\"\n", buffer);
+ } else {
+ struct netent *np1 = getnetbyname(argv[1]);
+ ne.n_name = np1->n_name;
+ ne.n_aliases = np1->n_aliases;
+ ne.n_addrtype = np1->n_addrtype;
+ ne.n_addr = &np1->n_net;
+ ne.n_length = (IN_CLASSA(np1->n_net) ?
+ 8 :
+ (IN_CLASSB(np1->n_net) ?
+ 16 :
+ (IN_CLASSC(np1->n_net) ?
+ 24 : -1)));
+ np1->n_net = htonl(np1->n_net);
+ if (irp_marshall_nw(&ne, &b, &len) != 0) {
+ printf("Marshalling failed\n");
+ }
+ printf("%s\n", b);
+ }
+ break;
+ }
+
+
+ case 'r': {
+ char **hosts, **users, **domains;
+ size_t entries;
+ int i;
+ char *buff;
+ size_t size;
+ char *ngname;
+
+ if (strchr(argv[1], '(') != NULL) {
+ if (irp_unmarshall_ng(&ngname, &entries,
+ &hosts, &users, &domains,
+ argv[1]) != 0) {
+ printf("unmarshall failed\n");
+ exit(1);
+ }
+
+#define STRVAL(x) (x == NULL ? "*" : x)
+
+ printf("%s {\n", ngname);
+ for (i = 0 ; i < entries ; i++)
+ printf("\t\"%s\" : \"%s\" : \"%s\"\n",
+ STRVAL(hosts[i]),
+ STRVAL(users[i]),
+ STRVAL(domains[i]));
+ printf("}\n\n\n");
+
+
+ irp_marshall_ng_start(ngname, NULL, &size);
+ for (i = 0 ; i < entries ; i++)
+ irp_marshall_ng_next(hosts[i], users[i],
+ domains[i], NULL, &size);
+ irp_marshall_ng_end(NULL, &size);
+
+ buff = malloc(size);
+
+ irp_marshall_ng_start(ngname, buff, &size);
+ for (i = 0 ; i < entries ; i++) {
+ if (irp_marshall_ng_next(hosts[i], users[i],
+ domains[i], buff,
+ &size) != 0)
+ printf("next marshalling failed.\n");
+ }
+ irp_marshall_ng_end(buff, &size);
+
+ if (strcmp_nws(argv[1], buff) != 0) {
+ printf("compare failed:\n\t%s\n\t%s\n",
+ buffer, argv[1]);
+ } else {
+ printf("compare ok\n");
+ }
+ } else {
+ char *h, *u, *d, *buff;
+ size_t size;
+
+ /* run through two times. First to figure out how
+ much of a buffer we need. Second to do the
+ actual marshalling */
+
+ setnetgrent(argv[1]);
+ irp_marshall_ng_start(argv[1], NULL, &size);
+ while (getnetgrent(&h, &u, &d) == 1)
+ irp_marshall_ng_next(h, u, d, NULL, &size);
+ irp_marshall_ng_end(NULL, &size);
+ endnetgrent(argv[1]);
+
+ buff = malloc(size);
+
+ setnetgrent(argv[1]);
+ if (irp_marshall_ng_start(argv[1], buff, &size) != 0)
+ printf("Marshalling start failed\n");
+
+ while (getnetgrent(&h, &u, &d) == 1) {
+ if (irp_marshall_ng_next(h, u, d, buff, &size)
+ != 0) {
+ printf("Marshalling failed\n");
+ }
+ }
+
+ irp_marshall_ng_end(buff, &size);
+ endnetgrent();
+
+ printf("success: %s\n", buff);
+ }
+ break;
+ }
+
+
+
+ case 'h': {
+ struct hostent he, *hp;
+ int i;
+
+
+ if (strchr(argv[1], '@') != NULL) {
+ if (irp_unmarshall_ho(&he, argv[1]) != 0) {
+ printf("unmarshall failed\n");
+ exit(1);
+ }
+
+ printf("Host: \"%s\"\nAliases:", he.h_name);
+ for (i = 0 ; he.h_aliases[i] != NULL ; i++)
+ printf("\n\t\t\"%s\"", he.h_aliases[i]);
+ printf("\nAddr Type: \"%s\"\n",
+ ADDR_T_STR(he.h_addrtype));
+ printf("Length: %d\nAddresses:", he.h_length);
+ for (i = 0 ; he.h_addr_list[i] != 0 ; i++) {
+ inet_ntop(he.h_addrtype, he.h_addr_list[i],
+ buffer, sizeof buffer);
+ printf("\n\t\"%s\"\n", buffer);
+ }
+ printf("\n\n");
+
+ irp_marshall_ho(&he, &b, &len);
+ if (strcmp(argv[1], buffer) != 0) {
+ printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n",
+ buffer, argv[1]);
+ } else {
+ printf("compare ok\n");
+ }
+ } else {
+ if ((hp = gethostbyname(argv[1])) == NULL) {
+ perror("gethostbyname");
+ printf("\"%s\"\n", argv[1]);
+ exit(1);
+ }
+
+ if (irp_marshall_ho(hp, &b, &len) != 0) {
+ printf("irp_marshall_ho failed\n");
+ exit(1);
+ }
+
+ printf("success: \"%s\"\n", buffer);
+ }
+ break;
+ }
+
+
+ case 's': {
+ struct servent *sv;
+ struct servent sv1;
+
+ if (strchr(argv[1], ':') != NULL) {
+ sv = &sv1;
+ memset(sv, 0xef, sizeof (struct servent));
+ if (irp_unmarshall_sv(sv, argv[1]) != 0) {
+ printf("unmarshall failed\n");
+
+ }
+
+ irp_marshall_sv(sv, &b, &len);
+ if (strcmp(argv[1], buffer) != 0) {
+ printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n",
+ buffer, argv[1]);
+ } else {
+ printf("compare ok\n");
+ }
+ } else {
+ if ((sv = getservbyname(argv[1], argv[2])) == NULL) {
+ perror("getservent");
+ exit(1);
+ }
+
+ if (irp_marshall_sv(sv, &b, &len) != 0) {
+ printf("irp_marshall_sv failed\n");
+ exit(1);
+ }
+
+ printf("success: \"%s\"\n", buffer);
+ }
+ break;
+ }
+
+ case 'g': {
+ struct group *gr;
+ struct group gr1;
+
+ if (strchr(argv[1], ':') != NULL) {
+ gr = &gr1;
+ memset(gr, 0xef, sizeof (struct group));
+ if (irp_unmarshall_gr(gr, argv[1]) != 0) {
+ printf("unmarshall failed\n");
+
+ }
+
+ irp_marshall_gr(gr, &b, &len);
+ if (strcmp(argv[1], buffer) != 0) {
+ printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n",
+ buffer, argv[1]);
+ } else {
+ printf("compare ok\n");
+ }
+ } else {
+ if ((gr = getgrnam(argv[1])) == NULL) {
+ perror("getgrnam");
+ exit(1);
+ }
+
+ if (irp_marshall_gr(gr, &b, &len) != 0) {
+ printf("irp_marshall_gr failed\n");
+ exit(1);
+ }
+
+ printf("success: \"%s\"\n", buffer);
+ }
+ break;
+ }
+
+
+ case 'p': {
+ struct passwd *pw;
+ struct passwd pw1;
+
+ if (strchr(argv[1], ':') != NULL) {
+ pw = &pw1;
+ memset(pw, 0xef, sizeof (*pw));
+ if (irp_unmarshall_pw(pw, argv[1]) != 0) {
+ printf("unmarshall failed\n");
+ exit(1);
+ }
+
+ printf("User: \"%s\"\nPasswd: \"%s\"\nUid: %ld\nGid: %ld\n",
+ pw->pw_name, pw->pw_passwd, (long)pw->pw_uid,
+ (long)pw->pw_gid);
+ printf("Class: \"%s\"\nChange: %ld\nGecos: \"%s\"\n",
+ pw->pw_class, (long)pw->pw_change, pw->pw_gecos);
+ printf("Shell: \"%s\"\nDirectory: \"%s\"\n",
+ pw->pw_shell, pw->pw_dir);
+
+ pw = getpwnam(pw->pw_name);
+ irp_marshall_pw(pw, &b, &len);
+ if (strcmp(argv[1], buffer) != 0) {
+ printf("compare failed:\n\t\"%s\"\n\t\"%s\"\n",
+ buffer, argv[1]);
+ } else {
+ printf("compare ok\n");
+ }
+ } else {
+ if ((pw = getpwnam(argv[1])) == NULL) {
+ perror("getpwnam");
+ exit(1);
+ }
+
+ if (irp_marshall_pw(pw, &b, &len) != 0) {
+ printf("irp_marshall_pw failed\n");
+ exit(1);
+ }
+
+ printf("success: \"%s\"\n", buffer);
+ }
+ break;
+ }
+
+ default:
+ printf("Wrong option: %c\n", option);
+ break;
+ }
+
+#endif
+
+ return (0);
+}
+
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irs_data.c b/usr/src/lib/libresolv2_joy/common/irs/irs_data.c
new file mode 100644
index 0000000000..3087b1189d
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irs_data.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#ifndef __BIND_NOSTATIC
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <string.h>
+#include <isc/memcluster.h>
+
+#ifdef DO_PTHREADS
+#include <pthread.h>
+#endif
+
+#include <irs.h>
+#include <stdlib.h>
+
+#include "port_after.h"
+
+#include "irs_data.h"
+#undef _res
+#if !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)
+#undef h_errno
+extern int h_errno;
+#endif
+
+extern struct __res_state _res;
+
+#ifdef DO_PTHREADS
+static pthread_key_t key;
+static int once = 0;
+#else
+static struct net_data *net_data;
+#endif
+
+void
+irs_destroy(void) {
+#ifndef DO_PTHREADS
+ if (net_data != NULL)
+ net_data_destroy(net_data);
+ net_data = NULL;
+#endif
+}
+
+void
+net_data_destroy(void *p) {
+ struct net_data *net_data = p;
+
+ res_ndestroy(net_data->res);
+ if (net_data->gr != NULL) {
+ (*net_data->gr->close)(net_data->gr);
+ net_data->gr = NULL;
+ }
+ if (net_data->pw != NULL) {
+ (*net_data->pw->close)(net_data->pw);
+ net_data->pw = NULL;
+ }
+ if (net_data->sv != NULL) {
+ (*net_data->sv->close)(net_data->sv);
+ net_data->sv = NULL;
+ }
+ if (net_data->pr != NULL) {
+ (*net_data->pr->close)(net_data->pr);
+ net_data->pr = NULL;
+ }
+ if (net_data->ho != NULL) {
+ (*net_data->ho->close)(net_data->ho);
+ net_data->ho = NULL;
+ }
+ if (net_data->nw != NULL) {
+ (*net_data->nw->close)(net_data->nw);
+ net_data->nw = NULL;
+ }
+ if (net_data->ng != NULL) {
+ (*net_data->ng->close)(net_data->ng);
+ net_data->ng = NULL;
+ }
+ if (net_data->ho_data != NULL) {
+ free(net_data->ho_data);
+ net_data->ho_data = NULL;
+ }
+ if (net_data->nw_data != NULL) {
+ free(net_data->nw_data);
+ net_data->nw_data = NULL;
+ }
+
+ (*net_data->irs->close)(net_data->irs);
+ memput(net_data, sizeof *net_data);
+}
+
+/*%
+ * applications that need a specific config file other than
+ * _PATH_IRS_CONF should call net_data_init directly rather than letting
+ * the various wrapper functions make the first call. - brister
+ */
+
+struct net_data *
+net_data_init(const char *conf_file) {
+#ifdef DO_PTHREADS
+#ifndef LIBBIND_MUTEX_INITIALIZER
+#define LIBBIND_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#endif
+ static pthread_mutex_t keylock = LIBBIND_MUTEX_INITIALIZER;
+ struct net_data *net_data;
+
+ if (!once) {
+ if (pthread_mutex_lock(&keylock) != 0)
+ return (NULL);
+ if (!once) {
+ if (pthread_key_create(&key, net_data_destroy) != 0) {
+ (void)pthread_mutex_unlock(&keylock);
+ return (NULL);
+ }
+ once = 1;
+ }
+ if (pthread_mutex_unlock(&keylock) != 0)
+ return (NULL);
+ }
+ net_data = pthread_getspecific(key);
+#endif
+
+ if (net_data == NULL) {
+ net_data = net_data_create(conf_file);
+ if (net_data == NULL)
+ return (NULL);
+#ifdef DO_PTHREADS
+ if (pthread_setspecific(key, net_data) != 0) {
+ net_data_destroy(net_data);
+ return (NULL);
+ }
+#endif
+ }
+
+ return (net_data);
+}
+
+struct net_data *
+net_data_create(const char *conf_file) {
+ struct net_data *net_data;
+
+ net_data = memget(sizeof (struct net_data));
+ if (net_data == NULL)
+ return (NULL);
+ memset(net_data, 0, sizeof (struct net_data));
+
+ if ((net_data->irs = irs_gen_acc("", conf_file)) == NULL) {
+ memput(net_data, sizeof (struct net_data));
+ return (NULL);
+ }
+#ifndef DO_PTHREADS
+ (*net_data->irs->res_set)(net_data->irs, &_res, NULL);
+#endif
+
+ net_data->res = (*net_data->irs->res_get)(net_data->irs);
+ if (net_data->res == NULL) {
+ (*net_data->irs->close)(net_data->irs);
+ memput(net_data, sizeof (struct net_data));
+ return (NULL);
+ }
+
+ if ((net_data->res->options & RES_INIT) == 0U &&
+ res_ninit(net_data->res) == -1) {
+ (*net_data->irs->close)(net_data->irs);
+ memput(net_data, sizeof (struct net_data));
+ return (NULL);
+ }
+
+ return (net_data);
+}
+
+void
+net_data_minimize(struct net_data *net_data) {
+ res_nclose(net_data->res);
+}
+
+#ifdef _REENTRANT
+struct __res_state *
+__res_state(void) {
+ /* NULL param here means use the default config file. */
+ struct net_data *net_data = net_data_init(NULL);
+ if (net_data && net_data->res)
+ return (net_data->res);
+
+ return (&_res);
+}
+#else
+#ifdef __linux
+struct __res_state *
+__res_state(void) {
+ return (&_res);
+}
+#endif
+#endif
+
+int *
+__h_errno(void) {
+ /* NULL param here means use the default config file. */
+ struct net_data *net_data = net_data_init(NULL);
+ if (net_data && net_data->res)
+ return (&net_data->res->res_h_errno);
+#ifdef ORIGINAL_ISC_CODE
+#if !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)
+ return(&_res.res_h_errno);
+#else
+ return (&h_errno);
+#endif
+#else
+ return (&h_errno);
+#endif /* ORIGINAL_ISC_CODE */
+}
+
+void
+__h_errno_set(struct __res_state *res, int err) {
+
+
+#if (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)
+ res->res_h_errno = err;
+#else
+ h_errno = res->res_h_errno = err;
+#endif
+}
+
+#endif /*__BIND_NOSTATIC*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irs_data.h b/usr/src/lib/libresolv2_joy/common/irs/irs_data.h
new file mode 100644
index 0000000000..cb814fd8b1
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irs_data.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: irs_data.h,v 1.3 2005/04/27 04:56:30 sra Exp $
+ */
+
+#ifndef __BIND_NOSTATIC
+
+#define net_data_init __net_data_init
+
+struct net_data {
+ struct irs_acc * irs;
+
+ struct irs_gr * gr;
+ struct irs_pw * pw;
+ struct irs_sv * sv;
+ struct irs_pr * pr;
+ struct irs_ho * ho;
+ struct irs_nw * nw;
+ struct irs_ng * ng;
+
+ struct group * gr_last;
+ struct passwd * pw_last;
+ struct servent * sv_last;
+ struct protoent * pr_last;
+ struct netent * nw_last; /*%< should have been ne_last */
+ struct nwent * nww_last;
+ struct hostent * ho_last;
+
+ unsigned int gr_stayopen :1;
+ unsigned int pw_stayopen :1;
+ unsigned int sv_stayopen :1;
+ unsigned int pr_stayopen :1;
+ unsigned int ho_stayopen :1;
+ unsigned int nw_stayopen :1;
+
+ void * nw_data;
+ void * ho_data;
+
+ struct __res_state * res; /*%< for gethostent.c */
+};
+
+extern struct net_data * net_data_init(const char *conf_file);
+extern void net_data_minimize(struct net_data *);
+
+#endif /*__BIND_NOSTATIC*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/irs_p.h b/usr/src/lib/libresolv2_joy/common/irs/irs_p.h
new file mode 100644
index 0000000000..2a0a933fce
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/irs_p.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: irs_p.h,v 1.3 2005/04/27 04:56:30 sra Exp $
+ */
+
+#ifndef _IRS_P_H_INCLUDED
+#define _IRS_P_H_INCLUDED
+
+#include <stdio.h>
+
+#include "pathnames.h"
+
+#define IRS_SV_MAXALIASES 35
+
+struct lcl_sv {
+ FILE * fp;
+ char line[BUFSIZ+1];
+ struct servent serv;
+ char * serv_aliases[IRS_SV_MAXALIASES];
+};
+
+#define irs_nul_ng __irs_nul_ng
+#define map_v4v6_address __map_v4v6_address
+#define make_group_list __make_group_list
+#define irs_lclsv_fnxt __irs_lclsv_fnxt
+
+extern void map_v4v6_address(const char *src, char *dst);
+extern int make_group_list(struct irs_gr *, const char *,
+ gid_t, gid_t *, int *);
+extern struct irs_ng * irs_nul_ng(struct irs_acc *);
+extern struct servent * irs_lclsv_fnxt(struct lcl_sv *);
+
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl.c b/usr/src/lib/libresolv2_joy/common/irs/lcl.c
new file mode 100644
index 0000000000..3a34b75c6d
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/lcl.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <isc/memcluster.h>
+
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "lcl_p.h"
+
+/* Forward. */
+
+static void lcl_close(struct irs_acc *);
+static struct __res_state * lcl_res_get(struct irs_acc *);
+static void lcl_res_set(struct irs_acc *, struct __res_state *,
+ void (*)(void *));
+
+/* Public */
+
+struct irs_acc *
+irs_lcl_acc(const char *options) {
+ struct irs_acc *acc;
+ struct lcl_p *lcl;
+
+ UNUSED(options);
+
+ if (!(acc = memget(sizeof *acc))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(acc, 0x5e, sizeof *acc);
+ if (!(lcl = memget(sizeof *lcl))) {
+ errno = ENOMEM;
+ free(acc);
+ return (NULL);
+ }
+ memset(lcl, 0x5e, sizeof *lcl);
+ lcl->res = NULL;
+ lcl->free_res = NULL;
+ acc->private = lcl;
+#ifdef WANT_IRS_GR
+ acc->gr_map = irs_lcl_gr;
+#else
+ acc->gr_map = NULL;
+#endif
+#ifdef WANT_IRS_PW
+ acc->pw_map = irs_lcl_pw;
+#else
+ acc->pw_map = NULL;
+#endif
+ acc->sv_map = irs_lcl_sv;
+ acc->pr_map = irs_lcl_pr;
+ acc->ho_map = irs_lcl_ho;
+ acc->nw_map = irs_lcl_nw;
+ acc->ng_map = irs_lcl_ng;
+ acc->res_get = lcl_res_get;
+ acc->res_set = lcl_res_set;
+ acc->close = lcl_close;
+ return (acc);
+}
+
+/* Methods */
+static struct __res_state *
+lcl_res_get(struct irs_acc *this) {
+ struct lcl_p *lcl = (struct lcl_p *)this->private;
+
+ if (lcl->res == NULL) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (res == NULL)
+ return (NULL);
+ memset(res, 0, sizeof *res);
+ lcl_res_set(this, res, free);
+ }
+
+ if ((lcl->res->options & RES_INIT) == 0U &&
+ res_ninit(lcl->res) < 0)
+ return (NULL);
+
+ return (lcl->res);
+}
+
+static void
+lcl_res_set(struct irs_acc *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct lcl_p *lcl = (struct lcl_p *)this->private;
+
+ if (lcl->res && lcl->free_res) {
+ res_nclose(lcl->res);
+ (*lcl->free_res)(lcl->res);
+ }
+
+ lcl->res = res;
+ lcl->free_res = free_res;
+}
+
+static void
+lcl_close(struct irs_acc *this) {
+ struct lcl_p *lcl = (struct lcl_p *)this->private;
+
+ if (lcl) {
+ if (lcl->free_res)
+ (*lcl->free_res)(lcl->res);
+ memput(lcl, sizeof *lcl);
+ }
+ memput(this, sizeof *this);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_ho.c b/usr/src/lib/libresolv2_joy/common/irs/lcl_ho.c
new file mode 100644
index 0000000000..e8893bf095
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_ho.c
@@ -0,0 +1,574 @@
+/*
+ * Copyright (c) 1985, 1988, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* from gethostnamadr.c 8.1 (Berkeley) 6/4/93 */
+/* BIND Id: gethnamaddr.c,v 8.15 1996/05/22 04:56:30 vixie Exp $ */
+
+/* Imports. */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <irs.h>
+#include <isc/memcluster.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "dns_p.h"
+#include "lcl_p.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) sprintf x
+#endif
+
+/* Definitions. */
+
+#define MAXALIASES 35
+#define MAXADDRS 35
+#define Max(a,b) ((a) > (b) ? (a) : (b))
+
+#if PACKETSZ > 1024
+#define MAXPACKET PACKETSZ
+#else
+#define MAXPACKET 1024
+#endif
+
+struct pvt {
+ FILE * fp;
+ struct hostent host;
+ char * h_addr_ptrs[MAXADDRS + 1];
+ char * host_aliases[MAXALIASES];
+ char hostbuf[8*1024];
+ u_char host_addr[16]; /*%< IPv4 or IPv6 */
+ struct __res_state *res;
+ void (*free_res)(void *);
+};
+
+typedef union {
+ int32_t al;
+ char ac;
+} align;
+
+static const u_char mapped[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0xff,0xff };
+static const u_char tunnelled[] = { 0,0, 0,0, 0,0, 0,0, 0,0, 0,0 };
+
+/* Forward. */
+
+static void ho_close(struct irs_ho *this);
+static struct hostent * ho_byname(struct irs_ho *this, const char *name);
+static struct hostent * ho_byname2(struct irs_ho *this, const char *name,
+ int af);
+static struct hostent * ho_byaddr(struct irs_ho *this, const void *addr,
+ int len, int af);
+static struct hostent * ho_next(struct irs_ho *this);
+static void ho_rewind(struct irs_ho *this);
+static void ho_minimize(struct irs_ho *this);
+static struct __res_state * ho_res_get(struct irs_ho *this);
+static void ho_res_set(struct irs_ho *this,
+ struct __res_state *res,
+ void (*free_res)(void *));
+static struct addrinfo * ho_addrinfo(struct irs_ho *this, const char *name,
+ const struct addrinfo *pai);
+
+static size_t ns_namelen(const char *);
+static int init(struct irs_ho *this);
+
+/* Portability. */
+
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+
+/* Public. */
+
+struct irs_ho *
+irs_lcl_ho(struct irs_acc *this) {
+ struct irs_ho *ho;
+ struct pvt *pvt;
+
+ UNUSED(this);
+
+ if (!(pvt = memget(sizeof *pvt))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ if (!(ho = memget(sizeof *ho))) {
+ memput(pvt, sizeof *pvt);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(ho, 0x5e, sizeof *ho);
+ ho->private = pvt;
+ ho->close = ho_close;
+ ho->byname = ho_byname;
+ ho->byname2 = ho_byname2;
+ ho->byaddr = ho_byaddr;
+ ho->next = ho_next;
+ ho->rewind = ho_rewind;
+ ho->minimize = ho_minimize;
+ ho->res_get = ho_res_get;
+ ho->res_set = ho_res_set;
+ ho->addrinfo = ho_addrinfo;
+ return (ho);
+}
+
+/* Methods. */
+
+static void
+ho_close(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ ho_minimize(this);
+ if (pvt->fp)
+ (void) fclose(pvt->fp);
+ if (pvt->res && pvt->free_res)
+ (*pvt->free_res)(pvt->res);
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct hostent *
+ho_byname(struct irs_ho *this, const char *name) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct hostent *hp;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ if (pvt->res->options & RES_USE_INET6) {
+ hp = ho_byname2(this, name, AF_INET6);
+ if (hp)
+ return (hp);
+ }
+ return (ho_byname2(this, name, AF_INET));
+}
+
+static struct hostent *
+ho_byname2(struct irs_ho *this, const char *name, int af) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct hostent *hp;
+ char **hap;
+ size_t n;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ ho_rewind(this);
+ n = ns_namelen(name);
+ while ((hp = ho_next(this)) != NULL) {
+ size_t nn;
+
+ if (hp->h_addrtype != af)
+ continue;
+ nn = ns_namelen(hp->h_name);
+ if (strncasecmp(hp->h_name, name, Max(n, nn)) == 0)
+ goto found;
+ for (hap = hp->h_aliases; *hap; hap++) {
+ nn = ns_namelen(*hap);
+ if (strncasecmp(*hap, name, Max(n, nn)) == 0)
+ goto found;
+ }
+ }
+ found:
+ if (!hp) {
+ RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND);
+ return (NULL);
+ }
+ RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS);
+ return (hp);
+}
+
+static struct hostent *
+ho_byaddr(struct irs_ho *this, const void *addr, int len, int af) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ const u_char *uaddr = addr;
+ struct hostent *hp;
+ int size;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ if (af == AF_INET6 && len == IN6ADDRSZ &&
+ (!memcmp(uaddr, mapped, sizeof mapped) ||
+ !memcmp(uaddr, tunnelled, sizeof tunnelled))) {
+ /* Unmap. */
+ addr = (const u_char *)addr + sizeof mapped;
+ uaddr += sizeof mapped;
+ af = AF_INET;
+ len = INADDRSZ;
+ }
+ switch (af) {
+ case AF_INET:
+ size = INADDRSZ;
+ break;
+ case AF_INET6:
+ size = IN6ADDRSZ;
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ if (size > len) {
+ errno = EINVAL;
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+
+ /*
+ * Do the search.
+ */
+ ho_rewind(this);
+ while ((hp = ho_next(this)) != NULL) {
+ char **hap;
+
+ for (hap = hp->h_addr_list; *hap; hap++) {
+ const u_char *taddr = (const u_char *)*hap;
+ int taf = hp->h_addrtype;
+ int tlen = hp->h_length;
+
+ if (taf == AF_INET6 && tlen == IN6ADDRSZ &&
+ (!memcmp(taddr, mapped, sizeof mapped) ||
+ !memcmp(taddr, tunnelled, sizeof tunnelled))) {
+ /* Unmap. */
+ taddr += sizeof mapped;
+ taf = AF_INET;
+ tlen = INADDRSZ;
+ }
+ if (taf == af && tlen == len &&
+ !memcmp(taddr, uaddr, tlen))
+ goto found;
+ }
+ }
+ found:
+ if (!hp) {
+ RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND);
+ return (NULL);
+ }
+ RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS);
+ return (hp);
+}
+
+static struct hostent *
+ho_next(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char *cp, **q, *p;
+ char *bufp, *ndbuf, *dbuf = NULL;
+ int c, af, len, bufsiz, offset;
+
+ if (init(this) == -1)
+ return (NULL);
+
+ if (!pvt->fp)
+ ho_rewind(this);
+ if (!pvt->fp) {
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ bufp = pvt->hostbuf;
+ bufsiz = sizeof pvt->hostbuf;
+ offset = 0;
+ again:
+ if (!(p = fgets(bufp + offset, bufsiz - offset, pvt->fp))) {
+ RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND);
+ if (dbuf)
+ free(dbuf);
+ return (NULL);
+ }
+ if (!strchr(p, '\n') && !feof(pvt->fp)) {
+#define GROWBUF 1024
+ /* allocate space for longer line */
+ if (dbuf == NULL) {
+ if ((ndbuf = malloc(bufsiz + GROWBUF)) != NULL)
+ strcpy(ndbuf, bufp);
+ } else
+ ndbuf = realloc(dbuf, bufsiz + GROWBUF);
+ if (ndbuf) {
+ dbuf = ndbuf;
+ bufp = dbuf;
+ bufsiz += GROWBUF;
+ offset = strlen(dbuf);
+ } else {
+ /* allocation failed; skip this long line */
+ while ((c = getc(pvt->fp)) != EOF)
+ if (c == '\n')
+ break;
+ if (c != EOF)
+ ungetc(c, pvt->fp);
+ }
+ goto again;
+ }
+
+ p -= offset;
+ offset = 0;
+
+ if (*p == '#')
+ goto again;
+ if ((cp = strpbrk(p, "#\n")) != NULL)
+ *cp = '\0';
+ if (!(cp = strpbrk(p, " \t")))
+ goto again;
+ *cp++ = '\0';
+ if (inet_pton(AF_INET6, p, pvt->host_addr) > 0) {
+ af = AF_INET6;
+ len = IN6ADDRSZ;
+ } else if (inet_aton(p, (struct in_addr *)pvt->host_addr) > 0) {
+ if (pvt->res->options & RES_USE_INET6) {
+ map_v4v6_address((char*)pvt->host_addr,
+ (char*)pvt->host_addr);
+ af = AF_INET6;
+ len = IN6ADDRSZ;
+ } else {
+ af = AF_INET;
+ len = INADDRSZ;
+ }
+ } else {
+ goto again;
+ }
+ pvt->h_addr_ptrs[0] = (char *)pvt->host_addr;
+ pvt->h_addr_ptrs[1] = NULL;
+ pvt->host.h_addr_list = pvt->h_addr_ptrs;
+ pvt->host.h_length = len;
+ pvt->host.h_addrtype = af;
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ pvt->host.h_name = cp;
+ q = pvt->host.h_aliases = pvt->host_aliases;
+ if ((cp = strpbrk(cp, " \t")) != NULL)
+ *cp++ = '\0';
+ while (cp && *cp) {
+ if (*cp == ' ' || *cp == '\t') {
+ cp++;
+ continue;
+ }
+ if (q < &pvt->host_aliases[MAXALIASES - 1])
+ *q++ = cp;
+ if ((cp = strpbrk(cp, " \t")) != NULL)
+ *cp++ = '\0';
+ }
+ *q = NULL;
+ if (dbuf)
+ free(dbuf);
+ RES_SET_H_ERRNO(pvt->res, NETDB_SUCCESS);
+ return (&pvt->host);
+}
+
+static void
+ho_rewind(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->fp) {
+ if (fseek(pvt->fp, 0L, SEEK_SET) == 0)
+ return;
+ (void)fclose(pvt->fp);
+ }
+ if (!(pvt->fp = fopen(_PATH_HOSTS, "r")))
+ return;
+ if (fcntl(fileno(pvt->fp), F_SETFD, 1) < 0) {
+ (void)fclose(pvt->fp);
+ pvt->fp = NULL;
+ }
+}
+
+static void
+ho_minimize(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->fp != NULL) {
+ (void)fclose(pvt->fp);
+ pvt->fp = NULL;
+ }
+ if (pvt->res)
+ res_nclose(pvt->res);
+}
+
+static struct __res_state *
+ho_res_get(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (!res) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(res, 0, sizeof *res);
+ ho_res_set(this, res, free);
+ }
+
+ return (pvt->res);
+}
+
+static void
+ho_res_set(struct irs_ho *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->res && pvt->free_res) {
+ res_nclose(pvt->res);
+ (*pvt->free_res)(pvt->res);
+ }
+
+ pvt->res = res;
+ pvt->free_res = free_res;
+}
+
+struct lcl_res_target {
+ struct lcl_res_target *next;
+ int family;
+};
+
+/* XXX */
+extern struct addrinfo *hostent2addrinfo __P((struct hostent *,
+ const struct addrinfo *pai));
+
+static struct addrinfo *
+ho_addrinfo(struct irs_ho *this, const char *name, const struct addrinfo *pai)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct hostent *hp;
+ struct lcl_res_target q, q2, *p;
+ struct addrinfo sentinel, *cur;
+
+ memset(&q, 0, sizeof(q2));
+ memset(&q2, 0, sizeof(q2));
+ memset(&sentinel, 0, sizeof(sentinel));
+ cur = &sentinel;
+
+ switch(pai->ai_family) {
+ case AF_UNSPEC: /*%< INET6 then INET4 */
+ q.family = AF_INET6;
+ q.next = &q2;
+ q2.family = AF_INET;
+ break;
+ case AF_INET6:
+ q.family = AF_INET6;
+ break;
+ case AF_INET:
+ q.family = AF_INET;
+ break;
+ default:
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY); /*%< ??? */
+ return(NULL);
+ }
+
+ for (p = &q; p; p = p->next) {
+ struct addrinfo *ai;
+
+ hp = (*this->byname2)(this, name, p->family);
+ if (hp == NULL) {
+ /* byname2 should've set an appropriate error */
+ continue;
+ }
+ if ((hp->h_name == NULL) || (hp->h_name[0] == 0) ||
+ (hp->h_addr_list[0] == NULL)) {
+ RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
+ continue;
+ }
+
+ ai = hostent2addrinfo(hp, pai);
+ if (ai) {
+ cur->ai_next = ai;
+ while (cur->ai_next)
+ cur = cur->ai_next;
+ }
+ }
+
+ if (sentinel.ai_next == NULL)
+ RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND);
+
+ return(sentinel.ai_next);
+}
+
+/* Private. */
+
+static size_t
+ns_namelen(const char *s) {
+ int i;
+
+ for (i = strlen(s); i > 0 && s[i-1] == '.'; i--)
+ (void)NULL;
+ return ((size_t) i);
+}
+
+static int
+init(struct irs_ho *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res && !ho_res_get(this))
+ return (-1);
+ if (((pvt->res->options & RES_INIT) == 0U) &&
+ res_ninit(pvt->res) == -1)
+ return (-1);
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_ng.c b/usr/src/lib/libresolv2_joy/common/irs/lcl_ng.c
new file mode 100644
index 0000000000..089a3c437e
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_ng.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <irs.h>
+#include <isc/memcluster.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "lcl_p.h"
+
+/* Definitions */
+
+#define NG_HOST 0 /*%< Host name */
+#define NG_USER 1 /*%< User name */
+#define NG_DOM 2 /*%< and Domain name */
+#define LINSIZ 1024 /*%< Length of netgroup file line */
+/*
+ * XXX Warning XXX
+ * This code is a hack-and-slash special. It realy needs to be
+ * rewritten with things like strdup, and realloc in mind.
+ * More reasonable data structures would not be a bad thing.
+ */
+
+/*%
+ * Static Variables and functions used by setnetgrent(), getnetgrent() and
+ * endnetgrent().
+ *
+ * There are two linked lists:
+ * \li linelist is just used by setnetgrent() to parse the net group file via.
+ * parse_netgrp()
+ * \li netgrp is the list of entries for the current netgroup
+ */
+struct linelist {
+ struct linelist *l_next; /*%< Chain ptr. */
+ int l_parsed; /*%< Flag for cycles */
+ char * l_groupname; /*%< Name of netgroup */
+ char * l_line; /*%< Netgroup entrie(s) to be parsed */
+};
+
+struct ng_old_struct {
+ struct ng_old_struct *ng_next; /*%< Chain ptr */
+ char * ng_str[3]; /*%< Field pointers, see below */
+};
+
+struct pvt {
+ FILE *fp;
+ struct linelist *linehead;
+ struct ng_old_struct *nextgrp;
+ struct {
+ struct ng_old_struct *gr;
+ char *grname;
+ } grouphead;
+};
+
+/* Forward */
+
+static void ng_rewind(struct irs_ng *, const char*);
+static void ng_close(struct irs_ng *);
+static int ng_next(struct irs_ng *, const char **,
+ const char **, const char **);
+static int ng_test(struct irs_ng *, const char *,
+ const char *, const char *,
+ const char *);
+static void ng_minimize(struct irs_ng *);
+
+static int parse_netgrp(struct irs_ng *, const char*);
+static struct linelist *read_for_group(struct irs_ng *, const char *);
+static void freelists(struct irs_ng *);
+
+/* Public */
+
+struct irs_ng *
+irs_lcl_ng(struct irs_acc *this) {
+ struct irs_ng *ng;
+ struct pvt *pvt;
+
+ UNUSED(this);
+
+ if (!(ng = memget(sizeof *ng))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(ng, 0x5e, sizeof *ng);
+ if (!(pvt = memget(sizeof *pvt))) {
+ memput(ng, sizeof *ng);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ ng->private = pvt;
+ ng->close = ng_close;
+ ng->next = ng_next;
+ ng->test = ng_test;
+ ng->rewind = ng_rewind;
+ ng->minimize = ng_minimize;
+ return (ng);
+}
+
+/* Methods */
+
+static void
+ng_close(struct irs_ng *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->fp != NULL)
+ fclose(pvt->fp);
+ freelists(this);
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+/*%
+ * Parse the netgroup file looking for the netgroup and build the list
+ * of netgrp structures. Let parse_netgrp() and read_for_group() do
+ * most of the work.
+ */
+static void
+ng_rewind(struct irs_ng *this, const char *group) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->fp != NULL && fseek(pvt->fp, SEEK_CUR, 0L) == -1) {
+ fclose(pvt->fp);
+ pvt->fp = NULL;
+ }
+
+ if (pvt->fp == NULL || pvt->grouphead.gr == NULL ||
+ strcmp(group, pvt->grouphead.grname)) {
+ freelists(this);
+ if (pvt->fp != NULL)
+ fclose(pvt->fp);
+ pvt->fp = fopen(_PATH_NETGROUP, "r");
+ if (pvt->fp != NULL) {
+ if (parse_netgrp(this, group))
+ freelists(this);
+ if (!(pvt->grouphead.grname = strdup(group)))
+ freelists(this);
+ fclose(pvt->fp);
+ pvt->fp = NULL;
+ }
+ }
+ pvt->nextgrp = pvt->grouphead.gr;
+}
+
+/*%
+ * Get the next netgroup off the list.
+ */
+static int
+ng_next(struct irs_ng *this, const char **host, const char **user,
+ const char **domain)
+{
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->nextgrp) {
+ *host = pvt->nextgrp->ng_str[NG_HOST];
+ *user = pvt->nextgrp->ng_str[NG_USER];
+ *domain = pvt->nextgrp->ng_str[NG_DOM];
+ pvt->nextgrp = pvt->nextgrp->ng_next;
+ return (1);
+ }
+ return (0);
+}
+
+/*%
+ * Search for a match in a netgroup.
+ */
+static int
+ng_test(struct irs_ng *this, const char *name,
+ const char *host, const char *user, const char *domain)
+{
+ const char *ng_host, *ng_user, *ng_domain;
+
+ ng_rewind(this, name);
+ while (ng_next(this, &ng_host, &ng_user, &ng_domain))
+ if ((host == NULL || ng_host == NULL ||
+ !strcmp(host, ng_host)) &&
+ (user == NULL || ng_user == NULL ||
+ !strcmp(user, ng_user)) &&
+ (domain == NULL || ng_domain == NULL ||
+ !strcmp(domain, ng_domain))) {
+ freelists(this);
+ return (1);
+ }
+ freelists(this);
+ return (0);
+}
+
+static void
+ng_minimize(struct irs_ng *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->fp != NULL) {
+ (void)fclose(pvt->fp);
+ pvt->fp = NULL;
+ }
+}
+
+/* Private */
+
+/*%
+ * endnetgrent() - cleanup
+ */
+static void
+freelists(struct irs_ng *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct linelist *lp, *olp;
+ struct ng_old_struct *gp, *ogp;
+
+ lp = pvt->linehead;
+ while (lp) {
+ olp = lp;
+ lp = lp->l_next;
+ free(olp->l_groupname);
+ free(olp->l_line);
+ free((char *)olp);
+ }
+ pvt->linehead = NULL;
+ if (pvt->grouphead.grname) {
+ free(pvt->grouphead.grname);
+ pvt->grouphead.grname = NULL;
+ }
+ gp = pvt->grouphead.gr;
+ while (gp) {
+ ogp = gp;
+ gp = gp->ng_next;
+ if (ogp->ng_str[NG_HOST])
+ free(ogp->ng_str[NG_HOST]);
+ if (ogp->ng_str[NG_USER])
+ free(ogp->ng_str[NG_USER]);
+ if (ogp->ng_str[NG_DOM])
+ free(ogp->ng_str[NG_DOM]);
+ free((char *)ogp);
+ }
+ pvt->grouphead.gr = NULL;
+}
+
+/*%
+ * Parse the netgroup file setting up the linked lists.
+ */
+static int
+parse_netgrp(struct irs_ng *this, const char *group) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char *spos, *epos;
+ int len, strpos;
+ char *pos, *gpos;
+ struct ng_old_struct *grp;
+ struct linelist *lp = pvt->linehead;
+
+ /*
+ * First, see if the line has already been read in.
+ */
+ while (lp) {
+ if (!strcmp(group, lp->l_groupname))
+ break;
+ lp = lp->l_next;
+ }
+ if (lp == NULL &&
+ (lp = read_for_group(this, group)) == NULL)
+ return (1);
+ if (lp->l_parsed) {
+ /*fprintf(stderr, "Cycle in netgroup %s\n", lp->l_groupname);*/
+ return (1);
+ } else
+ lp->l_parsed = 1;
+ pos = lp->l_line;
+ while (*pos != '\0') {
+ if (*pos == '(') {
+ if (!(grp = malloc(sizeof (struct ng_old_struct)))) {
+ freelists(this);
+ errno = ENOMEM;
+ return (1);
+ }
+ memset(grp, 0, sizeof (struct ng_old_struct));
+ grp->ng_next = pvt->grouphead.gr;
+ pvt->grouphead.gr = grp;
+ pos++;
+ gpos = strsep(&pos, ")");
+ for (strpos = 0; strpos < 3; strpos++) {
+ if ((spos = strsep(&gpos, ","))) {
+ while (*spos == ' ' || *spos == '\t')
+ spos++;
+ if ((epos = strpbrk(spos, " \t"))) {
+ *epos = '\0';
+ len = epos - spos;
+ } else
+ len = strlen(spos);
+ if (len > 0) {
+ if(!(grp->ng_str[strpos]
+ = (char *)
+ malloc(len + 1))) {
+ freelists(this);
+ return (1);
+ }
+ memcpy(grp->ng_str[strpos],
+ spos,
+ len + 1);
+ }
+ } else
+ goto errout;
+ }
+ } else {
+ spos = strsep(&pos, ", \t");
+ if (spos != NULL && parse_netgrp(this, spos)) {
+ freelists(this);
+ return (1);
+ }
+ }
+ if (pos == NULL)
+ break;
+ while (*pos == ' ' || *pos == ',' || *pos == '\t')
+ pos++;
+ }
+ return (0);
+ errout:
+ /*fprintf(stderr, "Bad netgroup %s at ..%s\n", lp->l_groupname,
+ spos);*/
+ return (1);
+}
+
+/*%
+ * Read the netgroup file and save lines until the line for the netgroup
+ * is found. Return 1 if eof is encountered.
+ */
+static struct linelist *
+read_for_group(struct irs_ng *this, const char *group) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char *pos, *spos, *linep = NULL, *olinep;
+ int len, olen, cont;
+ struct linelist *lp;
+ char line[LINSIZ + 1];
+
+ while (fgets(line, LINSIZ, pvt->fp) != NULL) {
+ pos = line;
+ if (*pos == '#')
+ continue;
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+ spos = pos;
+ while (*pos != ' ' && *pos != '\t' && *pos != '\n' &&
+ *pos != '\0')
+ pos++;
+ len = pos - spos;
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+ if (*pos != '\n' && *pos != '\0') {
+ if (!(lp = malloc(sizeof (*lp)))) {
+ freelists(this);
+ return (NULL);
+ }
+ lp->l_parsed = 0;
+ if (!(lp->l_groupname = malloc(len + 1))) {
+ free(lp);
+ freelists(this);
+ return (NULL);
+ }
+ memcpy(lp->l_groupname, spos, len);
+ *(lp->l_groupname + len) = '\0';
+ len = strlen(pos);
+ olen = 0;
+ olinep = NULL;
+
+ /*
+ * Loop around handling line continuations.
+ */
+ do {
+ if (*(pos + len - 1) == '\n')
+ len--;
+ if (*(pos + len - 1) == '\\') {
+ len--;
+ cont = 1;
+ } else
+ cont = 0;
+ if (len > 0) {
+ if (!(linep = malloc(olen + len + 1))){
+ if (olen > 0)
+ free(olinep);
+ free(lp->l_groupname);
+ free(lp);
+ freelists(this);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ if (olen > 0) {
+ memcpy(linep, olinep, olen);
+ free(olinep);
+ }
+ memcpy(linep + olen, pos, len);
+ olen += len;
+ *(linep + olen) = '\0';
+ olinep = linep;
+ }
+ if (cont) {
+ if (fgets(line, LINSIZ, pvt->fp)) {
+ pos = line;
+ len = strlen(pos);
+ } else
+ cont = 0;
+ }
+ } while (cont);
+ lp->l_line = linep;
+ lp->l_next = pvt->linehead;
+ pvt->linehead = lp;
+
+ /*
+ * If this is the one we wanted, we are done.
+ */
+ if (!strcmp(lp->l_groupname, group))
+ return (lp);
+ }
+ }
+ return (NULL);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_nw.c b/usr/src/lib/libresolv2_joy/common/irs/lcl_nw.c
new file mode 100644
index 0000000000..e1c5837944
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_nw.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 1989, 1993, 1995
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* from getgrent.c 8.2 (Berkeley) 3/21/94"; */
+/* from BSDI Id: getgrent.c,v 2.8 1996/05/28 18:15:14 bostic Exp $ */
+
+/* Imports */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <irs.h>
+#include <isc/memcluster.h>
+
+#include "port_after.h"
+
+#include <isc/misc.h>
+#include "irs_p.h"
+#include "lcl_p.h"
+
+#define MAXALIASES 35
+#define MAXADDRSIZE 4
+
+struct pvt {
+ FILE * fp;
+ char line[BUFSIZ+1];
+ struct nwent net;
+ char * aliases[MAXALIASES];
+ char addr[MAXADDRSIZE];
+ struct __res_state * res;
+ void (*free_res)(void *);
+};
+
+/* Forward */
+
+static void nw_close(struct irs_nw *);
+static struct nwent * nw_byname(struct irs_nw *, const char *, int);
+static struct nwent * nw_byaddr(struct irs_nw *, void *, int, int);
+static struct nwent * nw_next(struct irs_nw *);
+static void nw_rewind(struct irs_nw *);
+static void nw_minimize(struct irs_nw *);
+static struct __res_state * nw_res_get(struct irs_nw *this);
+static void nw_res_set(struct irs_nw *this,
+ struct __res_state *res,
+ void (*free_res)(void *));
+
+static int init(struct irs_nw *this);
+
+/* Portability. */
+
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+
+/* Public */
+
+struct irs_nw *
+irs_lcl_nw(struct irs_acc *this) {
+ struct irs_nw *nw;
+ struct pvt *pvt;
+
+ UNUSED(this);
+
+ if (!(pvt = memget(sizeof *pvt))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ if (!(nw = memget(sizeof *nw))) {
+ memput(pvt, sizeof *pvt);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(nw, 0x5e, sizeof *nw);
+ nw->private = pvt;
+ nw->close = nw_close;
+ nw->byname = nw_byname;
+ nw->byaddr = nw_byaddr;
+ nw->next = nw_next;
+ nw->rewind = nw_rewind;
+ nw->minimize = nw_minimize;
+ nw->res_get = nw_res_get;
+ nw->res_set = nw_res_set;
+ return (nw);
+}
+
+/* Methods */
+
+static void
+nw_close(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ nw_minimize(this);
+ if (pvt->res && pvt->free_res)
+ (*pvt->free_res)(pvt->res);
+ if (pvt->fp)
+ (void)fclose(pvt->fp);
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct nwent *
+nw_byaddr(struct irs_nw *this, void *net, int length, int type) {
+ struct nwent *p;
+
+ if (init(this) == -1)
+ return(NULL);
+
+ nw_rewind(this);
+ while ((p = nw_next(this)) != NULL)
+ if (p->n_addrtype == type && p->n_length == length)
+ if (bitncmp(p->n_addr, net, length) == 0)
+ break;
+ return (p);
+}
+
+static struct nwent *
+nw_byname(struct irs_nw *this, const char *name, int type) {
+ struct nwent *p;
+ char **ap;
+
+ if (init(this) == -1)
+ return(NULL);
+
+ nw_rewind(this);
+ while ((p = nw_next(this)) != NULL) {
+ if (ns_samename(p->n_name, name) == 1 &&
+ p->n_addrtype == type)
+ break;
+ for (ap = p->n_aliases; *ap; ap++)
+ if ((ns_samename(*ap, name) == 1) &&
+ (p->n_addrtype == type))
+ goto found;
+ }
+ found:
+ return (p);
+}
+
+static void
+nw_rewind(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->fp) {
+ if (fseek(pvt->fp, 0L, SEEK_SET) == 0)
+ return;
+ (void)fclose(pvt->fp);
+ }
+ if (!(pvt->fp = fopen(_PATH_NETWORKS, "r")))
+ return;
+ if (fcntl(fileno(pvt->fp), F_SETFD, 1) < 0) {
+ (void)fclose(pvt->fp);
+ pvt->fp = NULL;
+ }
+}
+
+static struct nwent *
+nw_next(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ struct nwent *ret = NULL;
+ char *p, *cp, **q;
+ char *bufp, *ndbuf, *dbuf = NULL;
+ int c, bufsiz, offset = 0;
+
+ if (init(this) == -1)
+ return(NULL);
+
+ if (pvt->fp == NULL)
+ nw_rewind(this);
+ if (pvt->fp == NULL) {
+ RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
+ return (NULL);
+ }
+ bufp = pvt->line;
+ bufsiz = sizeof(pvt->line);
+
+ again:
+ p = fgets(bufp + offset, bufsiz - offset, pvt->fp);
+ if (p == NULL)
+ goto cleanup;
+ if (!strchr(p, '\n') && !feof(pvt->fp)) {
+#define GROWBUF 1024
+ /* allocate space for longer line */
+ if (dbuf == NULL) {
+ if ((ndbuf = malloc(bufsiz + GROWBUF)) != NULL)
+ strcpy(ndbuf, bufp);
+ } else
+ ndbuf = realloc(dbuf, bufsiz + GROWBUF);
+ if (ndbuf) {
+ dbuf = ndbuf;
+ bufp = dbuf;
+ bufsiz += GROWBUF;
+ offset = strlen(dbuf);
+ } else {
+ /* allocation failed; skip this long line */
+ while ((c = getc(pvt->fp)) != EOF)
+ if (c == '\n')
+ break;
+ if (c != EOF)
+ ungetc(c, pvt->fp);
+ }
+ goto again;
+ }
+
+ p -= offset;
+ offset = 0;
+
+ if (*p == '#')
+ goto again;
+
+ cp = strpbrk(p, "#\n");
+ if (cp != NULL)
+ *cp = '\0';
+ pvt->net.n_name = p;
+ cp = strpbrk(p, " \t");
+ if (cp == NULL)
+ goto again;
+ *cp++ = '\0';
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ p = strpbrk(cp, " \t");
+ if (p != NULL)
+ *p++ = '\0';
+ pvt->net.n_length = inet_net_pton(AF_INET, cp, pvt->addr,
+ sizeof pvt->addr);
+ if (pvt->net.n_length < 0)
+ goto again;
+ pvt->net.n_addrtype = AF_INET;
+ pvt->net.n_addr = pvt->addr;
+ q = pvt->net.n_aliases = pvt->aliases;
+ if (p != NULL) {
+ cp = p;
+ while (cp && *cp) {
+ if (*cp == ' ' || *cp == '\t') {
+ cp++;
+ continue;
+ }
+ if (q < &pvt->aliases[MAXALIASES - 1])
+ *q++ = cp;
+ cp = strpbrk(cp, " \t");
+ if (cp != NULL)
+ *cp++ = '\0';
+ }
+ }
+ *q = NULL;
+ ret = &pvt->net;
+
+ cleanup:
+ if (dbuf)
+ free(dbuf);
+
+ return (ret);
+}
+
+static void
+nw_minimize(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->res)
+ res_nclose(pvt->res);
+ if (pvt->fp != NULL) {
+ (void)fclose(pvt->fp);
+ pvt->fp = NULL;
+ }
+}
+
+static struct __res_state *
+nw_res_get(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (!res) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(res, 0, sizeof *res);
+ nw_res_set(this, res, free);
+ }
+
+ return (pvt->res);
+}
+
+static void
+nw_res_set(struct irs_nw *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->res && pvt->free_res) {
+ res_nclose(pvt->res);
+ (*pvt->free_res)(pvt->res);
+ }
+
+ pvt->res = res;
+ pvt->free_res = free_res;
+}
+
+static int
+init(struct irs_nw *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (!pvt->res && !nw_res_get(this))
+ return (-1);
+ if (((pvt->res->options & RES_INIT) == 0U) &&
+ res_ninit(pvt->res) == -1)
+ return (-1);
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_p.h b/usr/src/lib/libresolv2_joy/common/irs/lcl_p.h
new file mode 100644
index 0000000000..e3f4f009cb
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_p.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: lcl_p.h,v 1.3 2005/04/27 04:56:31 sra Exp $
+ */
+
+/*! \file
+ * \brief
+ * lcl_p.h - private include file for the local accessor functions.
+ */
+
+#ifndef _LCL_P_H_INCLUDED
+#define _LCL_P_H_INCLUDED
+
+/*%
+ * Object state.
+ */
+struct lcl_p {
+ struct __res_state * res;
+ void (*free_res) __P((void *));
+};
+
+/*
+ * Externs.
+ */
+
+extern struct irs_acc * irs_lcl_acc __P((const char *));
+extern struct irs_gr * irs_lcl_gr __P((struct irs_acc *));
+extern struct irs_pw * irs_lcl_pw __P((struct irs_acc *));
+extern struct irs_sv * irs_lcl_sv __P((struct irs_acc *));
+extern struct irs_pr * irs_lcl_pr __P((struct irs_acc *));
+extern struct irs_ho * irs_lcl_ho __P((struct irs_acc *));
+extern struct irs_nw * irs_lcl_nw __P((struct irs_acc *));
+extern struct irs_ng * irs_lcl_ng __P((struct irs_acc *));
+
+#endif /*_LCL_P_H_INCLUDED*/
diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_pr.c b/usr/src/lib/libresolv2_joy/common/irs/lcl_pr.c
new file mode 100644
index 0000000000..22720f3e99
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_pr.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 1989, 1993, 1995
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <irs.h>
+#include <isc/memcluster.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "lcl_p.h"
+
+#ifndef _PATH_PROTOCOLS
+#define _PATH_PROTOCOLS "/etc/protocols"
+#endif
+#define MAXALIASES 35
+
+/* Types */
+
+struct pvt {
+ FILE * fp;
+ char line[BUFSIZ+1];
+ char * dbuf;
+ struct protoent proto;
+ char * proto_aliases[MAXALIASES];
+};
+
+/* Forward */
+
+static void pr_close(struct irs_pr *);
+static struct protoent * pr_next(struct irs_pr *);
+static struct protoent * pr_byname(struct irs_pr *, const char *);
+static struct protoent * pr_bynumber(struct irs_pr *, int);
+static void pr_rewind(struct irs_pr *);
+static void pr_minimize(struct irs_pr *);
+
+/* Portability. */
+
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+
+/* Public */
+
+struct irs_pr *
+irs_lcl_pr(struct irs_acc *this) {
+ struct irs_pr *pr;
+ struct pvt *pvt;
+
+ if (!(pr = memget(sizeof *pr))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ if (!(pvt = memget(sizeof *pvt))) {
+ memput(pr, sizeof *this);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ pr->private = pvt;
+ pr->close = pr_close;
+ pr->byname = pr_byname;
+ pr->bynumber = pr_bynumber;
+ pr->next = pr_next;
+ pr->rewind = pr_rewind;
+ pr->minimize = pr_minimize;
+ pr->res_get = NULL;
+ pr->res_set = NULL;
+ return (pr);
+}
+
+/* Methods */
+
+static void
+pr_close(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->fp)
+ (void) fclose(pvt->fp);
+ if (pvt->dbuf)
+ free(pvt->dbuf);
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct protoent *
+pr_byname(struct irs_pr *this, const char *name) {
+
+ struct protoent *p;
+ char **cp;
+
+ pr_rewind(this);
+ while ((p = pr_next(this))) {
+ if (!strcmp(p->p_name, name))
+ goto found;
+ for (cp = p->p_aliases; *cp; cp++)
+ if (!strcmp(*cp, name))
+ goto found;
+ }
+ found:
+ return (p);
+}
+
+static struct protoent *
+pr_bynumber(struct irs_pr *this, int proto) {
+ struct protoent *p;
+
+ pr_rewind(this);
+ while ((p = pr_next(this)))
+ if (p->p_proto == proto)
+ break;
+ return (p);
+}
+
+static void
+pr_rewind(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->fp) {
+ if (fseek(pvt->fp, 0L, SEEK_SET) == 0)
+ return;
+ (void)fclose(pvt->fp);
+ }
+ if (!(pvt->fp = fopen(_PATH_PROTOCOLS, "r" )))
+ return;
+ if (fcntl(fileno(pvt->fp), F_SETFD, 1) < 0) {
+ (void)fclose(pvt->fp);
+ pvt->fp = NULL;
+ }
+}
+
+static struct protoent *
+pr_next(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+ char *p, *cp, **q;
+ char *bufp, *ndbuf, *dbuf = NULL;
+ int c, bufsiz, offset;
+
+ if (!pvt->fp)
+ pr_rewind(this);
+ if (!pvt->fp)
+ return (NULL);
+ if (pvt->dbuf) {
+ free(pvt->dbuf);
+ pvt->dbuf = NULL;
+ }
+ bufp = pvt->line;
+ bufsiz = BUFSIZ;
+ offset = 0;
+ again:
+ if ((p = fgets(bufp + offset, bufsiz - offset, pvt->fp)) == NULL) {
+ if (dbuf)
+ free(dbuf);
+ return (NULL);
+ }
+ if (!strchr(p, '\n') && !feof(pvt->fp)) {
+#define GROWBUF 1024
+ /* allocate space for longer line */
+ if (dbuf == NULL) {
+ if ((ndbuf = malloc(bufsiz + GROWBUF)) != NULL)
+ strcpy(ndbuf, bufp);
+ } else
+ ndbuf = realloc(dbuf, bufsiz + GROWBUF);
+ if (ndbuf) {
+ dbuf = ndbuf;
+ bufp = dbuf;
+ bufsiz += GROWBUF;
+ offset = strlen(dbuf);
+ } else {
+ /* allocation failed; skip this long line */
+ while ((c = getc(pvt->fp)) != EOF)
+ if (c == '\n')
+ break;
+ if (c != EOF)
+ ungetc(c, pvt->fp);
+ }
+ goto again;
+ }
+
+ p -= offset;
+ offset = 0;
+
+ if (*p == '#')
+ goto again;
+ cp = strpbrk(p, "#\n");
+ if (cp != NULL)
+ *cp = '\0';
+ pvt->proto.p_name = p;
+ cp = strpbrk(p, " \t");
+ if (cp == NULL)
+ goto again;
+ *cp++ = '\0';
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ p = strpbrk(cp, " \t");
+ if (p != NULL)
+ *p++ = '\0';
+ pvt->proto.p_proto = atoi(cp);
+ q = pvt->proto.p_aliases = pvt->proto_aliases;
+ if (p != NULL) {
+ cp = p;
+ while (cp && *cp) {
+ if (*cp == ' ' || *cp == '\t') {
+ cp++;
+ continue;
+ }
+ if (q < &pvt->proto_aliases[MAXALIASES - 1])
+ *q++ = cp;
+ cp = strpbrk(cp, " \t");
+ if (cp != NULL)
+ *cp++ = '\0';
+ }
+ }
+ *q = NULL;
+ pvt->dbuf = dbuf;
+ return (&pvt->proto);
+}
+
+static void
+pr_minimize(struct irs_pr *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->fp != NULL) {
+ (void)fclose(pvt->fp);
+ pvt->fp = NULL;
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/lcl_sv.c b/usr/src/lib/libresolv2_joy/common/irs/lcl_sv.c
new file mode 100644
index 0000000000..a1ef71be2a
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/lcl_sv.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 1989, 1993, 1995
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#ifdef IRS_LCL_SV_DB
+#include <db.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <irs.h>
+#include <isc/memcluster.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "lcl_p.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/* Types */
+
+struct pvt {
+#ifdef IRS_LCL_SV_DB
+ DB * dbh;
+ int dbf;
+#endif
+ struct lcl_sv sv;
+};
+
+/* Forward */
+
+static void sv_close(struct irs_sv*);
+static struct servent * sv_next(struct irs_sv *);
+static struct servent * sv_byname(struct irs_sv *, const char *,
+ const char *);
+static struct servent * sv_byport(struct irs_sv *, int, const char *);
+static void sv_rewind(struct irs_sv *);
+static void sv_minimize(struct irs_sv *);
+/*global*/ struct servent * irs_lclsv_fnxt(struct lcl_sv *);
+#ifdef IRS_LCL_SV_DB
+static struct servent * sv_db_rec(struct lcl_sv *, DBT *, DBT *);
+#endif
+
+/* Portability */
+
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+
+/* Public */
+
+struct irs_sv *
+irs_lcl_sv(struct irs_acc *this) {
+ struct irs_sv *sv;
+ struct pvt *pvt;
+
+ UNUSED(this);
+
+ if ((sv = memget(sizeof *sv)) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(sv, 0x5e, sizeof *sv);
+ if ((pvt = memget(sizeof *pvt)) == NULL) {
+ memput(sv, sizeof *sv);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(pvt, 0, sizeof *pvt);
+ sv->private = pvt;
+ sv->close = sv_close;
+ sv->next = sv_next;
+ sv->byname = sv_byname;
+ sv->byport = sv_byport;
+ sv->rewind = sv_rewind;
+ sv->minimize = sv_minimize;
+ sv->res_get = NULL;
+ sv->res_set = NULL;
+#ifdef IRS_LCL_SV_DB
+ pvt->dbf = R_FIRST;
+#endif
+ return (sv);
+}
+
+/* Methods */
+
+static void
+sv_close(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+#ifdef IRS_LCL_SV_DB
+ if (pvt->dbh != NULL)
+ (*pvt->dbh->close)(pvt->dbh);
+#endif
+ if (pvt->sv.fp)
+ fclose(pvt->sv.fp);
+ memput(pvt, sizeof *pvt);
+ memput(this, sizeof *this);
+}
+
+static struct servent *
+sv_byname(struct irs_sv *this, const char *name, const char *proto) {
+#ifdef IRS_LCL_SV_DB
+ struct pvt *pvt = (struct pvt *)this->private;
+#endif
+ struct servent *p;
+ char **cp;
+
+ sv_rewind(this);
+#ifdef IRS_LCL_SV_DB
+ if (pvt->dbh != NULL) {
+ DBT key, data;
+
+ /* Note that (sizeof "/") == 2. */
+ if ((strlen(name) + sizeof "/" + proto ? strlen(proto) : 0)
+ > sizeof pvt->sv.line)
+ goto try_local;
+ key.data = pvt->sv.line;
+ key.size = SPRINTF((pvt->sv.line, "%s/%s", name,
+ proto ? proto : "")) + 1;
+ if (proto != NULL) {
+ if ((*pvt->dbh->get)(pvt->dbh, &key, &data, 0) != 0)
+ return (NULL);
+ } else if ((*pvt->dbh->seq)(pvt->dbh, &key, &data, R_CURSOR)
+ != 0)
+ return (NULL);
+ return (sv_db_rec(&pvt->sv, &key, &data));
+ }
+ try_local:
+#endif
+
+ while ((p = sv_next(this))) {
+ if (strcmp(name, p->s_name) == 0)
+ goto gotname;
+ for (cp = p->s_aliases; *cp; cp++)
+ if (strcmp(name, *cp) == 0)
+ goto gotname;
+ continue;
+ gotname:
+ if (proto == NULL || strcmp(p->s_proto, proto) == 0)
+ break;
+ }
+ return (p);
+}
+
+static struct servent *
+sv_byport(struct irs_sv *this, int port, const char *proto) {
+#ifdef IRS_LCL_SV_DB
+ struct pvt *pvt = (struct pvt *)this->private;
+#endif
+ struct servent *p;
+
+ sv_rewind(this);
+#ifdef IRS_LCL_SV_DB
+ if (pvt->dbh != NULL) {
+ DBT key, data;
+ u_short *ports;
+
+ ports = (u_short *)pvt->sv.line;
+ ports[0] = 0;
+ ports[1] = port;
+ key.data = ports;
+ key.size = sizeof(u_short) * 2;
+ if (proto && *proto) {
+ strncpy((char *)ports + key.size, proto,
+ BUFSIZ - key.size);
+ key.size += strlen((char *)ports + key.size) + 1;
+ if ((*pvt->dbh->get)(pvt->dbh, &key, &data, 0) != 0)
+ return (NULL);
+ } else {
+ if ((*pvt->dbh->seq)(pvt->dbh, &key, &data, R_CURSOR)
+ != 0)
+ return (NULL);
+ }
+ return (sv_db_rec(&pvt->sv, &key, &data));
+ }
+#endif
+ while ((p = sv_next(this))) {
+ if (p->s_port != port)
+ continue;
+ if (proto == NULL || strcmp(p->s_proto, proto) == 0)
+ break;
+ }
+ return (p);
+}
+
+static void
+sv_rewind(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+ if (pvt->sv.fp) {
+ if (fseek(pvt->sv.fp, 0L, SEEK_SET) == 0)
+ return;
+ (void)fclose(pvt->sv.fp);
+ pvt->sv.fp = NULL;
+ }
+#ifdef IRS_LCL_SV_DB
+ pvt->dbf = R_FIRST;
+ if (pvt->dbh != NULL)
+ return;
+ pvt->dbh = dbopen(_PATH_SERVICES_DB, O_RDONLY,O_RDONLY,DB_BTREE, NULL);
+ if (pvt->dbh != NULL) {
+ if (fcntl((*pvt->dbh->fd)(pvt->dbh), F_SETFD, 1) < 0) {
+ (*pvt->dbh->close)(pvt->dbh);
+ pvt->dbh = NULL;
+ }
+ return;
+ }
+#endif
+ if ((pvt->sv.fp = fopen(_PATH_SERVICES, "r")) == NULL)
+ return;
+ if (fcntl(fileno(pvt->sv.fp), F_SETFD, 1) < 0) {
+ (void)fclose(pvt->sv.fp);
+ pvt->sv.fp = NULL;
+ }
+}
+
+static struct servent *
+sv_next(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+#ifdef IRS_LCL_SV_DB
+ if (pvt->dbh == NULL && pvt->sv.fp == NULL)
+#else
+ if (pvt->sv.fp == NULL)
+#endif
+ sv_rewind(this);
+
+#ifdef IRS_LCL_SV_DB
+ if (pvt->dbh != NULL) {
+ DBT key, data;
+
+ while ((*pvt->dbh->seq)(pvt->dbh, &key, &data, pvt->dbf) == 0){
+ pvt->dbf = R_NEXT;
+ if (((char *)key.data)[0])
+ continue;
+ return (sv_db_rec(&pvt->sv, &key, &data));
+ }
+ }
+#endif
+
+ if (pvt->sv.fp == NULL)
+ return (NULL);
+ return (irs_lclsv_fnxt(&pvt->sv));
+}
+
+static void
+sv_minimize(struct irs_sv *this) {
+ struct pvt *pvt = (struct pvt *)this->private;
+
+#ifdef IRS_LCL_SV_DB
+ if (pvt->dbh != NULL) {
+ (*pvt->dbh->close)(pvt->dbh);
+ pvt->dbh = NULL;
+ }
+#endif
+ if (pvt->sv.fp != NULL) {
+ (void)fclose(pvt->sv.fp);
+ pvt->sv.fp = NULL;
+ }
+}
+
+/* Quasipublic. */
+
+struct servent *
+irs_lclsv_fnxt(struct lcl_sv *sv) {
+ char *p, *cp, **q;
+
+ again:
+ if ((p = fgets(sv->line, BUFSIZ, sv->fp)) == NULL)
+ return (NULL);
+ if (*p == '#')
+ goto again;
+ sv->serv.s_name = p;
+ while (*p && *p != '\n' && *p != ' ' && *p != '\t' && *p != '#')
+ ++p;
+ if (*p == '\0' || *p == '#' || *p == '\n')
+ goto again;
+ *p++ = '\0';
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '\0' || *p == '#' || *p == '\n')
+ goto again;
+ sv->serv.s_port = htons((u_short)strtol(p, &cp, 10));
+ if (cp == p || (*cp != '/' && *cp != ','))
+ goto again;
+ p = cp + 1;
+ sv->serv.s_proto = p;
+
+ q = sv->serv.s_aliases = sv->serv_aliases;
+
+ while (*p && *p != '\n' && *p != ' ' && *p != '\t' && *p != '#')
+ ++p;
+
+ while (*p == ' ' || *p == '\t') {
+ *p++ = '\0';
+ while (*p == ' ' || *p == '\t')
+ ++p;
+ if (*p == '\0' || *p == '#' || *p == '\n')
+ break;
+ if (q < &sv->serv_aliases[IRS_SV_MAXALIASES - 1])
+ *q++ = p;
+ while (*p && *p != '\n' && *p != ' ' && *p != '\t' && *p != '#')
+ ++p;
+ }
+
+ *p = '\0';
+ *q = NULL;
+ return (&sv->serv);
+}
+
+/* Private. */
+
+#ifdef IRS_LCL_SV_DB
+static struct servent *
+sv_db_rec(struct lcl_sv *sv, DBT *key, DBT *data) {
+ char *p, **q;
+ int n;
+
+ p = data->data;
+ p[data->size - 1] = '\0'; /*%< should be, but we depend on it */
+ if (((char *)key->data)[0] == '\0') {
+ if (key->size < sizeof(u_short)*2 || data->size < 2)
+ return (NULL);
+ sv->serv.s_port = ((u_short *)key->data)[1];
+ n = strlen(p) + 1;
+ if ((size_t)n > sizeof(sv->line)) {
+ n = sizeof(sv->line);
+ }
+ memcpy(sv->line, p, n);
+ sv->serv.s_name = sv->line;
+ if ((sv->serv.s_proto = strchr(sv->line, '/')) != NULL)
+ *(sv->serv.s_proto)++ = '\0';
+ p += n;
+ data->size -= n;
+ } else {
+ if (data->size < sizeof(u_short) + 1)
+ return (NULL);
+ if (key->size > sizeof(sv->line))
+ key->size = sizeof(sv->line);
+ ((char *)key->data)[key->size - 1] = '\0';
+ memcpy(sv->line, key->data, key->size);
+ sv->serv.s_name = sv->line;
+ if ((sv->serv.s_proto = strchr(sv->line, '/')) != NULL)
+ *(sv->serv.s_proto)++ = '\0';
+ sv->serv.s_port = *(u_short *)data->data;
+ p += sizeof(u_short);
+ data->size -= sizeof(u_short);
+ }
+ q = sv->serv.s_aliases = sv->serv_aliases;
+ while (data->size > 0 && q < &sv->serv_aliases[IRS_SV_MAXALIASES - 1]) {
+
+ *q++ = p;
+ n = strlen(p) + 1;
+ data->size -= n;
+ p += n;
+ }
+ *q = NULL;
+ return (&sv->serv);
+}
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/nis.c b/usr/src/lib/libresolv2_joy/common/irs/nis.c
new file mode 100644
index 0000000000..0325c47a1c
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/nis.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#ifdef WANT_IRS_NIS
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#ifdef T_NULL
+#undef T_NULL /* Silence re-definition warning of T_NULL. */
+#endif
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <isc/memcluster.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "hesiod.h"
+#include "nis_p.h"
+
+/* Forward */
+
+static void nis_close(struct irs_acc *);
+static struct __res_state * nis_res_get(struct irs_acc *);
+static void nis_res_set(struct irs_acc *, struct __res_state *,
+ void (*)(void *));
+
+/* Public */
+
+struct irs_acc *
+irs_nis_acc(const char *options) {
+ struct nis_p *nis;
+ struct irs_acc *acc;
+ char *domain;
+
+ UNUSED(options);
+
+ if (yp_get_default_domain(&domain) != 0)
+ return (NULL);
+ if (!(nis = memget(sizeof *nis))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(nis, 0, sizeof *nis);
+ if (!(acc = memget(sizeof *acc))) {
+ memput(nis, sizeof *nis);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(acc, 0x5e, sizeof *acc);
+ acc->private = nis;
+ nis->domain = strdup(domain);
+#ifdef WANT_IRS_GR
+ acc->gr_map = irs_nis_gr;
+#else
+ acc->gr_map = NULL;
+#endif
+#ifdef WANT_IRS_PW
+ acc->pw_map = irs_nis_pw;
+#else
+ acc->pw_map = NULL;
+#endif
+ acc->sv_map = irs_nis_sv;
+ acc->pr_map = irs_nis_pr;
+ acc->ho_map = irs_nis_ho;
+ acc->nw_map = irs_nis_nw;
+ acc->ng_map = irs_nis_ng;
+ acc->res_get = nis_res_get;
+ acc->res_set = nis_res_set;
+ acc->close = nis_close;
+ return (acc);
+}
+
+/* Methods */
+
+static struct __res_state *
+nis_res_get(struct irs_acc *this) {
+ struct nis_p *nis = (struct nis_p *)this->private;
+
+ if (nis->res == NULL) {
+ struct __res_state *res;
+ res = (struct __res_state *)malloc(sizeof *res);
+ if (res == NULL)
+ return (NULL);
+ memset(res, 0, sizeof *res);
+ nis_res_set(this, res, free);
+ }
+
+ if ((nis->res->options & RES_INIT) == 0 &&
+ res_ninit(nis->res) < 0)
+ return (NULL);
+
+ return (nis->res);
+}
+
+static void
+nis_res_set(struct irs_acc *this, struct __res_state *res,
+ void (*free_res)(void *)) {
+ struct nis_p *nis = (struct nis_p *)this->private;
+
+ if (nis->res && nis->free_res) {
+ res_nclose(nis->res);
+ (*nis->free_res)(nis->res);
+ }
+
+ nis->res = res;
+ nis->free_res = free_res;
+}
+
+static void
+nis_close(struct irs_acc *this) {
+ struct nis_p *nis = (struct nis_p *)this->private;
+
+ if (nis->res && nis->free_res)
+ (*nis->free_res)(nis->res);
+ free(nis->domain);
+ memput(nis, sizeof *nis);
+ memput(this, sizeof *this);
+}
+
+#endif /*WANT_IRS_NIS*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/nis_p.h b/usr/src/lib/libresolv2_joy/common/irs/nis_p.h
new file mode 100644
index 0000000000..70e2948d67
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/nis_p.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: nis_p.h,v 1.3 2005/04/27 04:56:33 sra Exp $
+ */
+
+/*! \file
+ * \brief
+ * nis_p.h - private include file for the NIS functions.
+ */
+
+/*%
+ * Object state.
+ */
+struct nis_p {
+ char * domain;
+ struct __res_state * res;
+ void (*free_res) __P((void *));
+};
+
+
+/*
+ * Methods.
+ */
+
+extern struct irs_gr * irs_nis_gr __P((struct irs_acc *));
+extern struct irs_pw * irs_nis_pw __P((struct irs_acc *));
+extern struct irs_sv * irs_nis_sv __P((struct irs_acc *));
+extern struct irs_pr * irs_nis_pr __P((struct irs_acc *));
+extern struct irs_ho * irs_nis_ho __P((struct irs_acc *));
+extern struct irs_nw * irs_nis_nw __P((struct irs_acc *));
+extern struct irs_ng * irs_nis_ng __P((struct irs_acc *));
diff --git a/usr/src/lib/libresolv2_joy/common/irs/nul_ng.c b/usr/src/lib/libresolv2_joy/common/irs/nul_ng.c
new file mode 100644
index 0000000000..a16ff9c2f0
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/nul_ng.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*! \file
+ * \brief
+ * nul_ng.c - the netgroup accessor null map
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <irs.h>
+#include <isc/memcluster.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+#include "hesiod.h"
+#include "dns_p.h"
+
+/* Forward. */
+
+static void ng_close(struct irs_ng *);
+static int ng_next(struct irs_ng *, const char **,
+ const char **, const char **);
+static int ng_test(struct irs_ng *,
+ const char *, const char *,
+ const char *, const char *);
+static void ng_rewind(struct irs_ng *, const char *);
+static void ng_minimize(struct irs_ng *);
+
+/* Public. */
+
+struct irs_ng *
+irs_nul_ng(struct irs_acc *this) {
+ struct irs_ng *ng;
+
+ UNUSED(this);
+
+ if (!(ng = memget(sizeof *ng))) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ memset(ng, 0x5e, sizeof *ng);
+ ng->private = NULL;
+ ng->close = ng_close;
+ ng->next = ng_next;
+ ng->test = ng_test;
+ ng->rewind = ng_rewind;
+ ng->minimize = ng_minimize;
+ return (ng);
+}
+
+/* Methods. */
+
+static void
+ng_close(struct irs_ng *this) {
+ memput(this, sizeof *this);
+}
+
+/* ARGSUSED */
+static int
+ng_next(struct irs_ng *this, const char **host, const char **user,
+ const char **domain)
+{
+ UNUSED(this);
+ UNUSED(host);
+ UNUSED(user);
+ UNUSED(domain);
+ errno = ENOENT;
+ return (-1);
+}
+
+static int
+ng_test(struct irs_ng *this, const char *name,
+ const char *user, const char *host, const char *domain)
+{
+ UNUSED(this);
+ UNUSED(name);
+ UNUSED(user);
+ UNUSED(host);
+ UNUSED(domain);
+ errno = ENODEV;
+ return (-1);
+}
+
+static void
+ng_rewind(struct irs_ng *this, const char *netgroup) {
+ UNUSED(this);
+ UNUSED(netgroup);
+ /* NOOP */
+}
+
+static void
+ng_minimize(struct irs_ng *this) {
+ UNUSED(this);
+ /* NOOP */
+}
diff --git a/usr/src/lib/libresolv2_joy/common/irs/pathnames.h b/usr/src/lib/libresolv2_joy/common/irs/pathnames.h
new file mode 100644
index 0000000000..1646842155
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/pathnames.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: pathnames.h,v 1.3 2005/04/27 04:56:34 sra Exp $
+ */
+
+#ifndef _PATH_IRS_CONF
+#define _PATH_IRS_CONF "/etc/irs.conf"
+#endif
+
+#ifndef _PATH_NETWORKS
+#define _PATH_NETWORKS "/etc/networks"
+#endif
+
+#ifndef _PATH_GROUP
+#define _PATH_GROUP "/etc/group"
+#endif
+
+#ifndef _PATH_NETGROUP
+#define _PATH_NETGROUP "/etc/netgroup"
+#endif
+
+#ifndef _PATH_SERVICES
+#define _PATH_SERVICES "/etc/services"
+#endif
+
+#ifdef IRS_LCL_SV_DB
+#ifndef _PATH_SERVICES_DB
+#define _PATH_SERVICES_DB _PATH_SERVICES ".db"
+#endif
+#endif
+
+#ifndef _PATH_HESIOD_CONF
+#define _PATH_HESIOD_CONF "/etc/hesiod.conf"
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/irs/util.c b/usr/src/lib/libresolv2_joy/common/irs/util.c
new file mode 100644
index 0000000000..4afc530fb0
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/irs/util.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <irs.h>
+
+#include "port_after.h"
+
+#include "irs_p.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) sprintf x
+#endif
+
+void
+map_v4v6_address(const char *src, char *dst) {
+ u_char *p = (u_char *)dst;
+ char tmp[NS_INADDRSZ];
+ int i;
+
+ /* Stash a temporary copy so our caller can update in place. */
+ memcpy(tmp, src, NS_INADDRSZ);
+ /* Mark this ipv6 addr as a mapped ipv4. */
+ for (i = 0; i < 10; i++)
+ *p++ = 0x00;
+ *p++ = 0xff;
+ *p++ = 0xff;
+ /* Retrieve the saved copy and we're done. */
+ memcpy((void*)p, tmp, NS_INADDRSZ);
+}
+
+int
+make_group_list(struct irs_gr *this, const char *name,
+ gid_t basegid, gid_t *groups, int *ngroups)
+{
+ struct group *grp;
+ int i, ng;
+ int ret, maxgroups;
+
+ ret = -1;
+ ng = 0;
+ maxgroups = *ngroups;
+ /*
+ * When installing primary group, duplicate it;
+ * the first element of groups is the effective gid
+ * and will be overwritten when a setgid file is executed.
+ */
+ if (ng >= maxgroups)
+ goto done;
+ groups[ng++] = basegid;
+ if (ng >= maxgroups)
+ goto done;
+ groups[ng++] = basegid;
+ /*
+ * Scan the group file to find additional groups.
+ */
+ (*this->rewind)(this);
+ while ((grp = (*this->next)(this)) != NULL) {
+ if ((gid_t)grp->gr_gid == basegid)
+ continue;
+ for (i = 0; grp->gr_mem[i]; i++) {
+ if (!strcmp(grp->gr_mem[i], name)) {
+ if (ng >= maxgroups)
+ goto done;
+ groups[ng++] = grp->gr_gid;
+ break;
+ }
+ }
+ }
+ ret = 0;
+ done:
+ *ngroups = ng;
+ return (ret);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/assertions.c b/usr/src/lib/libresolv2_joy/common/isc/assertions.c
new file mode 100644
index 0000000000..6516d66cc8
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/assertions.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1997, 1999, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/assertions.h>
+
+#include "port_after.h"
+
+/*
+ * Forward.
+ */
+
+static void default_assertion_failed(const char *, int, assertion_type,
+ const char *, int);
+
+/*
+ * Public.
+ */
+
+assertion_failure_callback __assertion_failed = default_assertion_failed;
+
+void
+set_assertion_failure_callback(assertion_failure_callback f) {
+ if (f == NULL)
+ __assertion_failed = default_assertion_failed;
+ else
+ __assertion_failed = f;
+}
+
+const char *
+assertion_type_to_text(assertion_type type) {
+ const char *result;
+
+ switch (type) {
+ case assert_require:
+ result = "REQUIRE";
+ break;
+ case assert_ensure:
+ result = "ENSURE";
+ break;
+ case assert_insist:
+ result = "INSIST";
+ break;
+ case assert_invariant:
+ result = "INVARIANT";
+ break;
+ default:
+ result = NULL;
+ }
+ return (result);
+}
+
+/*
+ * Private.
+ */
+
+/* coverity[+kill] */
+static void
+default_assertion_failed(const char *file, int line, assertion_type type,
+ const char *cond, int print_errno)
+{
+ fprintf(stderr, "%s:%d: %s(%s)%s%s failed.\n",
+ file, line, assertion_type_to_text(type), cond,
+ (print_errno) ? ": " : "",
+ (print_errno) ? strerror(errno) : "");
+ abort();
+ /* NOTREACHED */
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/base64.c b/usr/src/lib/libresolv2_joy/common/isc/base64.c
new file mode 100644
index 0000000000..a819fd97bf
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/base64.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "port_after.h"
+
+#ifndef ORIGINAL_ISC_CODE
+#pragma weak __b64_ntop = b64_ntop
+#pragma weak __b64_pton = b64_pton
+#endif /* ORIGINAL_ISC_CODE */
+
+#define Assert(Cond) if (!(Cond)) abort()
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
+ The following encoding technique is taken from RFC1521 by Borenstein
+ and Freed. It is reproduced here in a slightly edited form for
+ convenience.
+
+ A 65-character subset of US-ASCII is used, enabling 6 bits to be
+ represented per printable character. (The extra 65th character, "=",
+ is used to signify a special processing function.)
+
+ The encoding process represents 24-bit groups of input bits as output
+ strings of 4 encoded characters. Proceeding from left to right, a
+ 24-bit input group is formed by concatenating 3 8-bit input groups.
+ These 24 bits are then treated as 4 concatenated 6-bit groups, each
+ of which is translated into a single digit in the base64 alphabet.
+
+ Each 6-bit group is used as an index into an array of 64 printable
+ characters. The character referenced by the index is placed in the
+ output string.
+
+ Table 1: The Base64 Alphabet
+
+ Value Encoding Value Encoding Value Encoding Value Encoding
+ 0 A 17 R 34 i 51 z
+ 1 B 18 S 35 j 52 0
+ 2 C 19 T 36 k 53 1
+ 3 D 20 U 37 l 54 2
+ 4 E 21 V 38 m 55 3
+ 5 F 22 W 39 n 56 4
+ 6 G 23 X 40 o 57 5
+ 7 H 24 Y 41 p 58 6
+ 8 I 25 Z 42 q 59 7
+ 9 J 26 a 43 r 60 8
+ 10 K 27 b 44 s 61 9
+ 11 L 28 c 45 t 62 +
+ 12 M 29 d 46 u 63 /
+ 13 N 30 e 47 v
+ 14 O 31 f 48 w (pad) =
+ 15 P 32 g 49 x
+ 16 Q 33 h 50 y
+
+ Special processing is performed if fewer than 24 bits are available
+ at the end of the data being encoded. A full encoding quantum is
+ always completed at the end of a quantity. When fewer than 24 input
+ bits are available in an input group, zero bits are added (on the
+ right) to form an integral number of 6-bit groups. Padding at the
+ end of the data is performed using the '=' character.
+
+ Since all base64 input is an integral number of octets, only the
+ -------------------------------------------------
+ following cases can arise:
+
+ (1) the final quantum of encoding input is an integral
+ multiple of 24 bits; here, the final unit of encoded
+ output will be an integral multiple of 4 characters
+ with no "=" padding,
+ (2) the final quantum of encoding input is exactly 8 bits;
+ here, the final unit of encoded output will be two
+ characters followed by two "=" padding characters, or
+ (3) the final quantum of encoding input is exactly 16 bits;
+ here, the final unit of encoded output will be three
+ characters followed by one "=" padding character.
+ */
+
+int
+b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) {
+ size_t datalength = 0;
+ u_char input[3];
+ u_char output[4];
+ size_t i;
+
+ while (2U < srclength) {
+ input[0] = *src++;
+ input[1] = *src++;
+ input[2] = *src++;
+ srclength -= 3;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ output[3] = input[2] & 0x3f;
+ Assert(output[0] < 64);
+ Assert(output[1] < 64);
+ Assert(output[2] < 64);
+ Assert(output[3] < 64);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Base64[output[3]];
+ }
+
+ /* Now we worry about padding. */
+ if (0U != srclength) {
+ /* Get what's left. */
+ input[0] = input[1] = input[2] = '\0';
+ for (i = 0; i < srclength; i++)
+ input[i] = *src++;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ Assert(output[0] < 64);
+ Assert(output[1] < 64);
+ Assert(output[2] < 64);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ if (srclength == 1U)
+ target[datalength++] = Pad64;
+ else
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Pad64;
+ }
+ if (datalength >= targsize)
+ return (-1);
+ target[datalength] = '\0'; /*%< Returned value doesn't count \\0. */
+ return (datalength);
+}
+
+/* skips all whitespace anywhere.
+ converts characters, four at a time, starting at (or after)
+ src from base - 64 numbers into three 8 bit bytes in the target area.
+ it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+int
+b64_pton(src, target, targsize)
+ char const *src;
+ u_char *target;
+ size_t targsize;
+{
+ int tarindex, state, ch;
+ char *pos;
+
+ state = 0;
+ tarindex = 0;
+
+ while ((ch = *src++) != '\0') {
+ if (isspace(ch)) /*%< Skip whitespace anywhere. */
+ continue;
+
+ if (ch == Pad64)
+ break;
+
+ pos = strchr(Base64, ch);
+ if (pos == 0) /*%< A non-base64 character. */
+ return (-1);
+
+ switch (state) {
+ case 0:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] = (pos - Base64) << 2;
+ }
+ state = 1;
+ break;
+ case 1:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64) >> 4;
+ target[tarindex+1] = ((pos - Base64) & 0x0f)
+ << 4 ;
+ }
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64) >> 2;
+ target[tarindex+1] = ((pos - Base64) & 0x03)
+ << 6;
+ }
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64);
+ }
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /*%< We got a pad char. */
+ ch = *src++; /*%< Skip it, get next. */
+ switch (state) {
+ case 0: /*%< Invalid = in first position */
+ case 1: /*%< Invalid = in second position */
+ return (-1);
+
+ case 2: /*%< Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (!isspace(ch))
+ break;
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64)
+ return (-1);
+ ch = *src++; /*%< Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /*%< Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (!isspace(ch))
+ return (-1);
+
+ /*
+ * Now make sure for cases 2 and 3 that the "extra"
+ * bits that slopped past the last full byte were
+ * zeros. If we don't check them, they become a
+ * subliminal channel.
+ */
+ if (target && target[tarindex] != 0)
+ return (-1);
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0)
+ return (-1);
+ }
+
+ return (tarindex);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/bitncmp.c b/usr/src/lib/libresolv2_joy/common/isc/bitncmp.c
new file mode 100644
index 0000000000..d229e04c89
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/bitncmp.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1996, 1999, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+
+#include <string.h>
+
+#include "port_after.h"
+
+#include <isc/misc.h>
+
+/*%
+ * int
+ * bitncmp(l, r, n)
+ * compare bit masks l and r, for n bits.
+ * return:
+ * -1, 1, or 0 in the libc tradition.
+ * note:
+ * network byte order assumed. this means 192.5.5.240/28 has
+ * 0x11110000 in its fourth octet.
+ * author:
+ * Paul Vixie (ISC), June 1996
+ */
+int
+bitncmp(const void *l, const void *r, int n) {
+ u_int lb, rb;
+ int x, b;
+
+ b = n / 8;
+ x = memcmp(l, r, b);
+ if (x || (n % 8) == 0)
+ return (x);
+
+ lb = ((const u_char *)l)[b];
+ rb = ((const u_char *)r)[b];
+ for (b = n % 8; b > 0; b--) {
+ if ((lb & 0x80) != (rb & 0x80)) {
+ if (lb & 0x80)
+ return (1);
+ return (-1);
+ }
+ lb <<= 1;
+ rb <<= 1;
+ }
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/ctl_clnt.c b/usr/src/lib/libresolv2_joy/common/isc/ctl_clnt.c
new file mode 100644
index 0000000000..eca06b2ee2
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_clnt.c
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+
+#include <isc/assertions.h>
+#include <isc/ctl.h>
+#include <isc/eventlib.h>
+#include <isc/list.h>
+#include <isc/memcluster.h>
+
+#include "ctl_p.h"
+
+#include "port_after.h"
+
+/* Constants. */
+
+
+/* Macros. */
+
+#define donefunc_p(ctx) ((ctx).donefunc != NULL)
+#define arpacode_p(line) (isdigit((unsigned char)(line[0])) && \
+ isdigit((unsigned char)(line[1])) && \
+ isdigit((unsigned char)(line[2])))
+#define arpacont_p(line) (line[3] == '-')
+#define arpadone_p(line) (line[3] == ' ' || line[3] == '\t' || \
+ line[3] == '\r' || line[3] == '\0')
+
+/* Types. */
+
+enum state {
+ initializing = 0, connecting, connected, destroyed
+};
+
+struct ctl_tran {
+ LINK(struct ctl_tran) link;
+ LINK(struct ctl_tran) wlink;
+ struct ctl_cctx * ctx;
+ struct ctl_buf outbuf;
+ ctl_clntdone donefunc;
+ void * uap;
+};
+
+struct ctl_cctx {
+ enum state state;
+ evContext ev;
+ int sock;
+ ctl_logfunc logger;
+ ctl_clntdone donefunc;
+ void * uap;
+ evConnID coID;
+ evTimerID tiID;
+ evFileID rdID;
+ evStreamID wrID;
+ struct ctl_buf inbuf;
+ struct timespec timeout;
+ LIST(struct ctl_tran) tran;
+ LIST(struct ctl_tran) wtran;
+};
+
+/* Forward. */
+
+static struct ctl_tran *new_tran(struct ctl_cctx *, ctl_clntdone, void *, int);
+static void start_write(struct ctl_cctx *);
+static void destroy(struct ctl_cctx *, int);
+static void error(struct ctl_cctx *);
+static void new_state(struct ctl_cctx *, enum state);
+static void conn_done(evContext, void *, int,
+ const void *, int,
+ const void *, int);
+static void write_done(evContext, void *, int, int);
+static void start_read(struct ctl_cctx *);
+static void stop_read(struct ctl_cctx *);
+static void readable(evContext, void *, int, int);
+static void start_timer(struct ctl_cctx *);
+static void stop_timer(struct ctl_cctx *);
+static void touch_timer(struct ctl_cctx *);
+static void timer(evContext, void *,
+ struct timespec, struct timespec);
+
+#ifndef HAVE_MEMCHR
+static void *
+memchr(const void *b, int c, size_t len) {
+ const unsigned char *p = b;
+ size_t i;
+
+ for (i = 0; i < len; i++, p++)
+ if (*p == (unsigned char)c)
+ return ((void *)p);
+ return (NULL);
+}
+#endif
+
+/* Private data. */
+
+static const char * const state_names[] = {
+ "initializing", "connecting", "connected", "destroyed"
+};
+
+/* Public. */
+
+/*%
+ * void
+ * ctl_client()
+ * create, condition, and connect to a listener on the control port.
+ */
+struct ctl_cctx *
+ctl_client(evContext lev, const struct sockaddr *cap, size_t cap_len,
+ const struct sockaddr *sap, size_t sap_len,
+ ctl_clntdone donefunc, void *uap,
+ u_int timeout, ctl_logfunc logger)
+{
+ static const char me[] = "ctl_client";
+ static const int on = 1;
+ struct ctl_cctx *ctx;
+ struct sockaddr *captmp;
+
+ if (logger == NULL)
+ logger = ctl_logger;
+ ctx = memget(sizeof *ctx);
+ if (ctx == NULL) {
+ (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
+ goto fatal;
+ }
+ ctx->state = initializing;
+ ctx->ev = lev;
+ ctx->logger = logger;
+ ctx->timeout = evConsTime(timeout, 0);
+ ctx->donefunc = donefunc;
+ ctx->uap = uap;
+ ctx->coID.opaque = NULL;
+ ctx->tiID.opaque = NULL;
+ ctx->rdID.opaque = NULL;
+ ctx->wrID.opaque = NULL;
+ buffer_init(ctx->inbuf);
+ INIT_LIST(ctx->tran);
+ INIT_LIST(ctx->wtran);
+ ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC);
+ if (ctx->sock > evHighestFD(ctx->ev)) {
+ ctx->sock = -1;
+ errno = ENOTSOCK;
+ }
+ if (ctx->sock < 0) {
+ (*ctx->logger)(ctl_error, "%s: socket: %s",
+ me, strerror(errno));
+ goto fatal;
+ }
+ if (cap != NULL) {
+ if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&on, sizeof on) != 0) {
+ (*ctx->logger)(ctl_warning,
+ "%s: setsockopt(REUSEADDR): %s",
+ me, strerror(errno));
+ }
+ DE_CONST(cap, captmp);
+ if (bind(ctx->sock, captmp, cap_len) < 0) {
+ (*ctx->logger)(ctl_error, "%s: bind: %s", me,
+ strerror(errno));
+ goto fatal;
+ }
+ }
+ if (evConnect(lev, ctx->sock, (const struct sockaddr *)sap, sap_len,
+ conn_done, ctx, &ctx->coID) < 0) {
+ (*ctx->logger)(ctl_error, "%s: evConnect(fd %d): %s",
+ me, ctx->sock, strerror(errno));
+ fatal:
+ if (ctx != NULL) {
+ if (ctx->sock >= 0)
+ close(ctx->sock);
+ memput(ctx, sizeof *ctx);
+ }
+ return (NULL);
+ }
+ new_state(ctx, connecting);
+ return (ctx);
+}
+
+/*%
+ * void
+ * ctl_endclient(ctx)
+ * close a client and release all of its resources.
+ */
+void
+ctl_endclient(struct ctl_cctx *ctx) {
+ if (ctx->state != destroyed)
+ destroy(ctx, 0);
+ memput(ctx, sizeof *ctx);
+}
+
+/*%
+ * int
+ * ctl_command(ctx, cmd, len, donefunc, uap)
+ * Queue a transaction, which will begin with sending cmd
+ * and complete by calling donefunc with the answer.
+ */
+int
+ctl_command(struct ctl_cctx *ctx, const char *cmd, size_t len,
+ ctl_clntdone donefunc, void *uap)
+{
+ struct ctl_tran *tran;
+ char *pc;
+ unsigned int n;
+
+ switch (ctx->state) {
+ case destroyed:
+ errno = ENOTCONN;
+ return (-1);
+ case connecting:
+ case connected:
+ break;
+ default:
+ abort();
+ }
+ if (len >= (size_t)MAX_LINELEN) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ tran = new_tran(ctx, donefunc, uap, 1);
+ if (tran == NULL)
+ return (-1);
+ if (ctl_bufget(&tran->outbuf, ctx->logger) < 0)
+ return (-1);
+ memcpy(tran->outbuf.text, cmd, len);
+ tran->outbuf.used = len;
+ for (pc = tran->outbuf.text, n = 0; n < tran->outbuf.used; pc++, n++)
+ if (!isascii((unsigned char)*pc) ||
+ !isprint((unsigned char)*pc))
+ *pc = '\040';
+ start_write(ctx);
+ return (0);
+}
+
+/* Private. */
+
+static struct ctl_tran *
+new_tran(struct ctl_cctx *ctx, ctl_clntdone donefunc, void *uap, int w) {
+ struct ctl_tran *new = memget(sizeof *new);
+
+ if (new == NULL)
+ return (NULL);
+ new->ctx = ctx;
+ buffer_init(new->outbuf);
+ new->donefunc = donefunc;
+ new->uap = uap;
+ INIT_LINK(new, link);
+ INIT_LINK(new, wlink);
+ APPEND(ctx->tran, new, link);
+ if (w)
+ APPEND(ctx->wtran, new, wlink);
+ return (new);
+}
+
+static void
+start_write(struct ctl_cctx *ctx) {
+ static const char me[] = "isc/ctl_clnt::start_write";
+ struct ctl_tran *tran;
+ struct iovec iov[2], *iovp = iov;
+ char * tmp;
+
+ REQUIRE(ctx->state == connecting || ctx->state == connected);
+ /* If there is a write in progress, don't try to write more yet. */
+ if (ctx->wrID.opaque != NULL)
+ return;
+ /* If there are no trans, make sure timer is off, and we're done. */
+ if (EMPTY(ctx->wtran)) {
+ if (ctx->tiID.opaque != NULL)
+ stop_timer(ctx);
+ return;
+ }
+ /* Pull it off the head of the write queue. */
+ tran = HEAD(ctx->wtran);
+ UNLINK(ctx->wtran, tran, wlink);
+ /* Since there are some trans, make sure timer is successfully "on". */
+ if (ctx->tiID.opaque != NULL)
+ touch_timer(ctx);
+ else
+ start_timer(ctx);
+ if (ctx->state == destroyed)
+ return;
+ /* Marshall a newline-terminated message and clock it out. */
+ *iovp++ = evConsIovec(tran->outbuf.text, tran->outbuf.used);
+ DE_CONST("\r\n", tmp);
+ *iovp++ = evConsIovec(tmp, 2);
+ if (evWrite(ctx->ev, ctx->sock, iov, iovp - iov,
+ write_done, tran, &ctx->wrID) < 0) {
+ (*ctx->logger)(ctl_error, "%s: evWrite: %s", me,
+ strerror(errno));
+ error(ctx);
+ return;
+ }
+ if (evTimeRW(ctx->ev, ctx->wrID, ctx->tiID) < 0) {
+ (*ctx->logger)(ctl_error, "%s: evTimeRW: %s", me,
+ strerror(errno));
+ error(ctx);
+ return;
+ }
+}
+
+static void
+destroy(struct ctl_cctx *ctx, int notify) {
+ struct ctl_tran *this, *next;
+
+ if (ctx->sock != -1) {
+ (void) close(ctx->sock);
+ ctx->sock = -1;
+ }
+ switch (ctx->state) {
+ case connecting:
+ REQUIRE(ctx->wrID.opaque == NULL);
+ REQUIRE(EMPTY(ctx->tran));
+ /*
+ * This test is nec'y since destroy() can be called from
+ * start_read() while the state is still "connecting".
+ */
+ if (ctx->coID.opaque != NULL) {
+ (void)evCancelConn(ctx->ev, ctx->coID);
+ ctx->coID.opaque = NULL;
+ }
+ break;
+ case connected:
+ REQUIRE(ctx->coID.opaque == NULL);
+ if (ctx->wrID.opaque != NULL) {
+ (void)evCancelRW(ctx->ev, ctx->wrID);
+ ctx->wrID.opaque = NULL;
+ }
+ if (ctx->rdID.opaque != NULL)
+ stop_read(ctx);
+ break;
+ case destroyed:
+ break;
+ default:
+ abort();
+ }
+ if (allocated_p(ctx->inbuf))
+ ctl_bufput(&ctx->inbuf);
+ for (this = HEAD(ctx->tran); this != NULL; this = next) {
+ next = NEXT(this, link);
+ if (allocated_p(this->outbuf))
+ ctl_bufput(&this->outbuf);
+ if (notify && this->donefunc != NULL)
+ (*this->donefunc)(ctx, this->uap, NULL, 0);
+ memput(this, sizeof *this);
+ }
+ if (ctx->tiID.opaque != NULL)
+ stop_timer(ctx);
+ new_state(ctx, destroyed);
+}
+
+static void
+error(struct ctl_cctx *ctx) {
+ REQUIRE(ctx->state != destroyed);
+ destroy(ctx, 1);
+}
+
+static void
+new_state(struct ctl_cctx *ctx, enum state new_state) {
+ static const char me[] = "isc/ctl_clnt::new_state";
+
+ (*ctx->logger)(ctl_debug, "%s: %s -> %s", me,
+ state_names[ctx->state], state_names[new_state]);
+ ctx->state = new_state;
+}
+
+static void
+conn_done(evContext ev, void *uap, int fd,
+ const void *la, int lalen,
+ const void *ra, int ralen)
+{
+ static const char me[] = "isc/ctl_clnt::conn_done";
+ struct ctl_cctx *ctx = uap;
+ struct ctl_tran *tran;
+
+ UNUSED(ev);
+ UNUSED(la);
+ UNUSED(lalen);
+ UNUSED(ra);
+ UNUSED(ralen);
+
+ ctx->coID.opaque = NULL;
+ if (fd < 0) {
+ (*ctx->logger)(ctl_error, "%s: evConnect: %s", me,
+ strerror(errno));
+ error(ctx);
+ return;
+ }
+ new_state(ctx, connected);
+ tran = new_tran(ctx, ctx->donefunc, ctx->uap, 0);
+ if (tran == NULL) {
+ (*ctx->logger)(ctl_error, "%s: new_tran failed: %s", me,
+ strerror(errno));
+ error(ctx);
+ return;
+ }
+ start_read(ctx);
+ if (ctx->state == destroyed) {
+ (*ctx->logger)(ctl_error, "%s: start_read failed: %s",
+ me, strerror(errno));
+ error(ctx);
+ return;
+ }
+}
+
+static void
+write_done(evContext lev, void *uap, int fd, int bytes) {
+ struct ctl_tran *tran = (struct ctl_tran *)uap;
+ struct ctl_cctx *ctx = tran->ctx;
+
+ UNUSED(lev);
+ UNUSED(fd);
+
+ ctx->wrID.opaque = NULL;
+ if (ctx->tiID.opaque != NULL)
+ touch_timer(ctx);
+ ctl_bufput(&tran->outbuf);
+ start_write(ctx);
+ if (bytes < 0)
+ destroy(ctx, 1);
+ else
+ start_read(ctx);
+}
+
+static void
+start_read(struct ctl_cctx *ctx) {
+ static const char me[] = "isc/ctl_clnt::start_read";
+
+ REQUIRE(ctx->state == connecting || ctx->state == connected);
+ REQUIRE(ctx->rdID.opaque == NULL);
+ if (evSelectFD(ctx->ev, ctx->sock, EV_READ, readable, ctx,
+ &ctx->rdID) < 0)
+ {
+ (*ctx->logger)(ctl_error, "%s: evSelect(fd %d): %s", me,
+ ctx->sock, strerror(errno));
+ error(ctx);
+ return;
+ }
+}
+
+static void
+stop_read(struct ctl_cctx *ctx) {
+ REQUIRE(ctx->coID.opaque == NULL);
+ REQUIRE(ctx->rdID.opaque != NULL);
+ (void)evDeselectFD(ctx->ev, ctx->rdID);
+ ctx->rdID.opaque = NULL;
+}
+
+static void
+readable(evContext ev, void *uap, int fd, int evmask) {
+ static const char me[] = "isc/ctl_clnt::readable";
+ struct ctl_cctx *ctx = uap;
+ struct ctl_tran *tran;
+ ssize_t n;
+ char *eos;
+
+ UNUSED(ev);
+
+ REQUIRE(ctx != NULL);
+ REQUIRE(fd >= 0);
+ REQUIRE(evmask == EV_READ);
+ REQUIRE(ctx->state == connected);
+ REQUIRE(!EMPTY(ctx->tran));
+ tran = HEAD(ctx->tran);
+ if (!allocated_p(ctx->inbuf) &&
+ ctl_bufget(&ctx->inbuf, ctx->logger) < 0) {
+ (*ctx->logger)(ctl_error, "%s: can't get an input buffer", me);
+ error(ctx);
+ return;
+ }
+ n = read(ctx->sock, ctx->inbuf.text + ctx->inbuf.used,
+ MAX_LINELEN - ctx->inbuf.used);
+ if (n <= 0) {
+ (*ctx->logger)(ctl_warning, "%s: read: %s", me,
+ (n == 0) ? "Unexpected EOF" : strerror(errno));
+ error(ctx);
+ return;
+ }
+ if (ctx->tiID.opaque != NULL)
+ touch_timer(ctx);
+ ctx->inbuf.used += n;
+ (*ctx->logger)(ctl_debug, "%s: read %d, used %d", me,
+ n, ctx->inbuf.used);
+ again:
+ eos = memchr(ctx->inbuf.text, '\n', ctx->inbuf.used);
+ if (eos != NULL && eos != ctx->inbuf.text && eos[-1] == '\r') {
+ int done = 0;
+
+ eos[-1] = '\0';
+ if (!arpacode_p(ctx->inbuf.text)) {
+ /* XXX Doesn't FTP do this sometimes? Is it legal? */
+ (*ctx->logger)(ctl_error, "%s: no arpa code (%s)", me,
+ ctx->inbuf.text);
+ error(ctx);
+ return;
+ }
+ if (arpadone_p(ctx->inbuf.text))
+ done = 1;
+ else if (arpacont_p(ctx->inbuf.text))
+ done = 0;
+ else {
+ /* XXX Doesn't FTP do this sometimes? Is it legal? */
+ (*ctx->logger)(ctl_error, "%s: no arpa flag (%s)", me,
+ ctx->inbuf.text);
+ error(ctx);
+ return;
+ }
+ (*tran->donefunc)(ctx, tran->uap, ctx->inbuf.text,
+ (done ? 0 : CTL_MORE));
+ ctx->inbuf.used -= ((eos - ctx->inbuf.text) + 1);
+ if (ctx->inbuf.used == 0U)
+ ctl_bufput(&ctx->inbuf);
+ else
+ memmove(ctx->inbuf.text, eos + 1, ctx->inbuf.used);
+ if (done) {
+ UNLINK(ctx->tran, tran, link);
+ memput(tran, sizeof *tran);
+ stop_read(ctx);
+ start_write(ctx);
+ return;
+ }
+ if (allocated_p(ctx->inbuf))
+ goto again;
+ return;
+ }
+ if (ctx->inbuf.used == (size_t)MAX_LINELEN) {
+ (*ctx->logger)(ctl_error, "%s: line too long (%-10s...)", me,
+ ctx->inbuf.text);
+ error(ctx);
+ }
+}
+
+/* Timer related stuff. */
+
+static void
+start_timer(struct ctl_cctx *ctx) {
+ static const char me[] = "isc/ctl_clnt::start_timer";
+
+ REQUIRE(ctx->tiID.opaque == NULL);
+ if (evSetIdleTimer(ctx->ev, timer, ctx, ctx->timeout, &ctx->tiID) < 0){
+ (*ctx->logger)(ctl_error, "%s: evSetIdleTimer: %s", me,
+ strerror(errno));
+ error(ctx);
+ return;
+ }
+}
+
+static void
+stop_timer(struct ctl_cctx *ctx) {
+ static const char me[] = "isc/ctl_clnt::stop_timer";
+
+ REQUIRE(ctx->tiID.opaque != NULL);
+ if (evClearIdleTimer(ctx->ev, ctx->tiID) < 0) {
+ (*ctx->logger)(ctl_error, "%s: evClearIdleTimer: %s", me,
+ strerror(errno));
+ error(ctx);
+ return;
+ }
+ ctx->tiID.opaque = NULL;
+}
+
+static void
+touch_timer(struct ctl_cctx *ctx) {
+ REQUIRE(ctx->tiID.opaque != NULL);
+
+ evTouchIdleTimer(ctx->ev, ctx->tiID);
+}
+
+static void
+timer(evContext ev, void *uap, struct timespec due, struct timespec itv) {
+ static const char me[] = "isc/ctl_clnt::timer";
+ struct ctl_cctx *ctx = uap;
+
+ UNUSED(ev);
+ UNUSED(due);
+ UNUSED(itv);
+
+ ctx->tiID.opaque = NULL;
+ (*ctx->logger)(ctl_error, "%s: timeout after %u seconds while %s", me,
+ ctx->timeout.tv_sec, state_names[ctx->state]);
+ error(ctx);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/ctl_p.c b/usr/src/lib/libresolv2_joy/common/isc/ctl_p.c
new file mode 100644
index 0000000000..3c0ebf4037
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_p.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Extern. */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <isc/assertions.h>
+#include <isc/eventlib.h>
+#include <isc/logging.h>
+#include <isc/memcluster.h>
+#include <isc/ctl.h>
+
+#include "ctl_p.h"
+
+#include "port_after.h"
+
+/* Constants. */
+
+const char * const ctl_sevnames[] = {
+ "debug", "warning", "error"
+};
+
+/* Public. */
+
+/*%
+ * ctl_logger()
+ * if ctl_startup()'s caller didn't specify a logger, this one
+ * is used. this pollutes stderr with all kinds of trash so it will
+ * probably never be used in real applications.
+ */
+void
+ctl_logger(enum ctl_severity severity, const char *format, ...) {
+ va_list ap;
+ static const char me[] = "ctl_logger";
+
+ fprintf(stderr, "%s(%s): ", me, ctl_sevnames[severity]);
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+int
+ctl_bufget(struct ctl_buf *buf, ctl_logfunc logger) {
+ static const char me[] = "ctl_bufget";
+
+ REQUIRE(!allocated_p(*buf) && buf->used == 0U);
+ buf->text = memget(MAX_LINELEN);
+ if (!allocated_p(*buf)) {
+ (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
+ return (-1);
+ }
+ buf->used = 0;
+ return (0);
+}
+
+void
+ctl_bufput(struct ctl_buf *buf) {
+
+ REQUIRE(allocated_p(*buf));
+ memput(buf->text, MAX_LINELEN);
+ buf->text = NULL;
+ buf->used = 0;
+}
+
+const char *
+ctl_sa_ntop(const struct sockaddr *sa,
+ char *buf, size_t size,
+ ctl_logfunc logger)
+{
+ static const char me[] = "ctl_sa_ntop";
+ static const char punt[] = "[0].-1";
+ char tmp[INET6_ADDRSTRLEN];
+
+ switch (sa->sa_family) {
+ case AF_INET6: {
+ const struct sockaddr_in6 *in6 =
+ (const struct sockaddr_in6 *) sa;
+
+ if (inet_ntop(in6->sin6_family, &in6->sin6_addr, tmp, sizeof tmp)
+ == NULL) {
+ (*logger)(ctl_error, "%s: inet_ntop(%u %04x): %s",
+ me, in6->sin6_family,
+ in6->sin6_port, strerror(errno));
+ return (punt);
+ }
+ if (strlen(tmp) + sizeof "[].65535" > size) {
+ (*logger)(ctl_error, "%s: buffer overflow", me);
+ return (punt);
+ }
+ (void) sprintf(buf, "[%s].%u", tmp, ntohs(in6->sin6_port));
+ return (buf);
+ }
+ case AF_INET: {
+ const struct sockaddr_in *in =
+ (const struct sockaddr_in *) sa;
+
+ if (inet_ntop(in->sin_family, &in->sin_addr, tmp, sizeof tmp)
+ == NULL) {
+ (*logger)(ctl_error, "%s: inet_ntop(%u %04x %08x): %s",
+ me, in->sin_family,
+ in->sin_port, in->sin_addr.s_addr,
+ strerror(errno));
+ return (punt);
+ }
+ if (strlen(tmp) + sizeof "[].65535" > size) {
+ (*logger)(ctl_error, "%s: buffer overflow", me);
+ return (punt);
+ }
+ (void) sprintf(buf, "[%s].%u", tmp, ntohs(in->sin_port));
+ return (buf);
+ }
+#ifndef NO_SOCKADDR_UN
+ case AF_UNIX: {
+ const struct sockaddr_un *un =
+ (const struct sockaddr_un *) sa;
+ unsigned int x = sizeof un->sun_path;
+
+ if (x > size)
+ x = size;
+ strncpy(buf, un->sun_path, x - 1);
+ buf[x - 1] = '\0';
+ return (buf);
+ }
+#endif
+ default:
+ return (punt);
+ }
+}
+
+void
+ctl_sa_copy(const struct sockaddr *src, struct sockaddr *dst) {
+ switch (src->sa_family) {
+ case AF_INET6:
+ *((struct sockaddr_in6 *)dst) =
+ *((const struct sockaddr_in6 *)src);
+ break;
+ case AF_INET:
+ *((struct sockaddr_in *)dst) =
+ *((const struct sockaddr_in *)src);
+ break;
+#ifndef NO_SOCKADDR_UN
+ case AF_UNIX:
+ *((struct sockaddr_un *)dst) =
+ *((const struct sockaddr_un *)src);
+ break;
+#endif
+ default:
+ *dst = *src;
+ break;
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/ctl_p.h b/usr/src/lib/libresolv2_joy/common/isc/ctl_p.h
new file mode 100644
index 0000000000..18a52ae39c
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_p.h
@@ -0,0 +1,28 @@
+struct ctl_buf {
+ char * text;
+ size_t used;
+};
+
+#define MAX_LINELEN 990 /*%< Like SMTP. */
+#ifndef NO_SOCKADDR_UN
+#define MAX_NTOP PATH_MAX
+#else
+#define MAX_NTOP (sizeof "[255.255.255.255].65535")
+#endif
+
+#define allocated_p(Buf) ((Buf).text != NULL)
+#define buffer_init(Buf) ((Buf).text = 0, (Buf.used) = 0)
+
+#define ctl_bufget __ctl_bufget
+#define ctl_bufput __ctl_bufput
+#define ctl_sa_ntop __ctl_sa_ntop
+#define ctl_sa_copy __ctl_sa_copy
+
+int ctl_bufget(struct ctl_buf *, ctl_logfunc);
+void ctl_bufput(struct ctl_buf *);
+const char * ctl_sa_ntop(const struct sockaddr *, char *, size_t,
+ ctl_logfunc);
+void ctl_sa_copy(const struct sockaddr *,
+ struct sockaddr *);
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/ctl_srvr.c b/usr/src/lib/libresolv2_joy/common/isc/ctl_srvr.c
new file mode 100644
index 0000000000..12191cf0b0
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/ctl_srvr.c
@@ -0,0 +1,781 @@
+/*
+ * Copyright (C) 2004-2006, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+
+#include <isc/assertions.h>
+#include <isc/ctl.h>
+#include <isc/eventlib.h>
+#include <isc/list.h>
+#include <isc/logging.h>
+#include <isc/memcluster.h>
+
+#include "ctl_p.h"
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/* Macros. */
+
+#define lastverb_p(verb) (verb->name == NULL || verb->func == NULL)
+#define address_expr ctl_sa_ntop((struct sockaddr *)&sess->sa, \
+ tmp, sizeof tmp, ctx->logger)
+
+/* Types. */
+
+enum state {
+ available = 0, initializing, writing, reading, reading_data,
+ processing, idling, quitting, closing
+};
+
+union sa_un {
+ struct sockaddr_in in;
+#ifndef NO_SOCKADDR_UN
+ struct sockaddr_un un;
+#endif
+};
+
+struct ctl_sess {
+ LINK(struct ctl_sess) link;
+ struct ctl_sctx * ctx;
+ enum state state;
+ int sock;
+ union sa_un sa;
+ evFileID rdID;
+ evStreamID wrID;
+ evTimerID rdtiID;
+ evTimerID wrtiID;
+ struct ctl_buf inbuf;
+ struct ctl_buf outbuf;
+ const struct ctl_verb * verb;
+ u_int helpcode;
+ const void * respctx;
+ u_int respflags;
+ ctl_srvrdone donefunc;
+ void * uap;
+ void * csctx;
+};
+
+struct ctl_sctx {
+ evContext ev;
+ void * uctx;
+ u_int unkncode;
+ u_int timeoutcode;
+ const struct ctl_verb * verbs;
+ const struct ctl_verb * connverb;
+ int sock;
+ int max_sess;
+ int cur_sess;
+ struct timespec timeout;
+ ctl_logfunc logger;
+ evConnID acID;
+ LIST(struct ctl_sess) sess;
+};
+
+/* Forward. */
+
+static void ctl_accept(evContext, void *, int,
+ const void *, int,
+ const void *, int);
+static void ctl_close(struct ctl_sess *);
+static void ctl_new_state(struct ctl_sess *,
+ enum state,
+ const char *);
+static void ctl_start_read(struct ctl_sess *);
+static void ctl_stop_read(struct ctl_sess *);
+static void ctl_readable(evContext, void *, int, int);
+static void ctl_rdtimeout(evContext, void *,
+ struct timespec,
+ struct timespec);
+static void ctl_wrtimeout(evContext, void *,
+ struct timespec,
+ struct timespec);
+static void ctl_docommand(struct ctl_sess *);
+static void ctl_writedone(evContext, void *, int, int);
+static void ctl_morehelp(struct ctl_sctx *,
+ struct ctl_sess *,
+ const struct ctl_verb *,
+ const char *,
+ u_int, const void *, void *);
+static void ctl_signal_done(struct ctl_sctx *,
+ struct ctl_sess *);
+
+/* Private data. */
+
+static const char * state_names[] = {
+ "available", "initializing", "writing", "reading",
+ "reading_data", "processing", "idling", "quitting", "closing"
+};
+
+static const char space[] = " ";
+
+static const struct ctl_verb fakehelpverb = {
+ "fakehelp", ctl_morehelp , NULL
+};
+
+/* Public. */
+
+/*%
+ * void
+ * ctl_server()
+ * create, condition, and start a listener on the control port.
+ */
+struct ctl_sctx *
+ctl_server(evContext lev, const struct sockaddr *sap, size_t sap_len,
+ const struct ctl_verb *verbs,
+ u_int unkncode, u_int timeoutcode,
+ u_int timeout, int backlog, int max_sess,
+ ctl_logfunc logger, void *uctx)
+{
+ static const char me[] = "ctl_server";
+ static const int on = 1;
+ const struct ctl_verb *connverb;
+ struct ctl_sctx *ctx;
+ int save_errno;
+
+ if (logger == NULL)
+ logger = ctl_logger;
+ for (connverb = verbs;
+ connverb->name != NULL && connverb->func != NULL;
+ connverb++)
+ if (connverb->name[0] == '\0')
+ break;
+ if (connverb->func == NULL) {
+ (*logger)(ctl_error, "%s: no connection verb found", me);
+ return (NULL);
+ }
+ ctx = memget(sizeof *ctx);
+ if (ctx == NULL) {
+ (*logger)(ctl_error, "%s: getmem: %s", me, strerror(errno));
+ return (NULL);
+ }
+ ctx->ev = lev;
+ ctx->uctx = uctx;
+ ctx->unkncode = unkncode;
+ ctx->timeoutcode = timeoutcode;
+ ctx->verbs = verbs;
+ ctx->timeout = evConsTime(timeout, 0);
+ ctx->logger = logger;
+ ctx->connverb = connverb;
+ ctx->max_sess = max_sess;
+ ctx->cur_sess = 0;
+ INIT_LIST(ctx->sess);
+ ctx->sock = socket(sap->sa_family, SOCK_STREAM, PF_UNSPEC);
+ if (ctx->sock > evHighestFD(ctx->ev)) {
+ ctx->sock = -1;
+ errno = ENOTSOCK;
+ }
+ if (ctx->sock < 0) {
+ save_errno = errno;
+ (*ctx->logger)(ctl_error, "%s: socket: %s",
+ me, strerror(errno));
+ memput(ctx, sizeof *ctx);
+ errno = save_errno;
+ return (NULL);
+ }
+ if (ctx->sock > evHighestFD(lev)) {
+ close(ctx->sock);
+ (*ctx->logger)(ctl_error, "%s: file descriptor > evHighestFD");
+ errno = ENFILE;
+ memput(ctx, sizeof *ctx);
+ return (NULL);
+ }
+#ifdef NO_UNIX_REUSEADDR
+ if (sap->sa_family != AF_UNIX)
+#endif
+ if (setsockopt(ctx->sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char *)&on, sizeof on) != 0) {
+ (*ctx->logger)(ctl_warning,
+ "%s: setsockopt(REUSEADDR): %s",
+ me, strerror(errno));
+ }
+ if (bind(ctx->sock, sap, sap_len) < 0) {
+ char tmp[MAX_NTOP];
+ save_errno = errno;
+ (*ctx->logger)(ctl_error, "%s: bind: %s: %s",
+ me, ctl_sa_ntop((const struct sockaddr *)sap,
+ tmp, sizeof tmp, ctx->logger),
+ strerror(save_errno));
+ close(ctx->sock);
+ memput(ctx, sizeof *ctx);
+ errno = save_errno;
+ return (NULL);
+ }
+ if (fcntl(ctx->sock, F_SETFD, 1) < 0) {
+ (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me,
+ strerror(errno));
+ }
+ if (evListen(lev, ctx->sock, backlog, ctl_accept, ctx,
+ &ctx->acID) < 0) {
+ save_errno = errno;
+ (*ctx->logger)(ctl_error, "%s: evListen(fd %d): %s",
+ me, ctx->sock, strerror(errno));
+ close(ctx->sock);
+ memput(ctx, sizeof *ctx);
+ errno = save_errno;
+ return (NULL);
+ }
+ (*ctx->logger)(ctl_debug, "%s: new ctx %p, sock %d",
+ me, ctx, ctx->sock);
+ return (ctx);
+}
+
+/*%
+ * void
+ * ctl_endserver(ctx)
+ * if the control listener is open, close it. clean out all eventlib
+ * stuff. close all active sessions.
+ */
+void
+ctl_endserver(struct ctl_sctx *ctx) {
+ static const char me[] = "ctl_endserver";
+ struct ctl_sess *this, *next;
+
+ (*ctx->logger)(ctl_debug, "%s: ctx %p, sock %d, acID %p, sess %p",
+ me, ctx, ctx->sock, ctx->acID.opaque, ctx->sess);
+ if (ctx->acID.opaque != NULL) {
+ (void)evCancelConn(ctx->ev, ctx->acID);
+ ctx->acID.opaque = NULL;
+ }
+ if (ctx->sock != -1) {
+ (void) close(ctx->sock);
+ ctx->sock = -1;
+ }
+ for (this = HEAD(ctx->sess); this != NULL; this = next) {
+ next = NEXT(this, link);
+ ctl_close(this);
+ }
+ memput(ctx, sizeof *ctx);
+}
+
+/*%
+ * If body is non-NULL then it we add a "." line after it.
+ * Caller must have escaped lines with leading ".".
+ */
+void
+ctl_response(struct ctl_sess *sess, u_int code, const char *text,
+ u_int flags, const void *respctx, ctl_srvrdone donefunc,
+ void *uap, const char *body, size_t bodylen)
+{
+ static const char me[] = "ctl_response";
+ struct iovec iov[3], *iovp = iov;
+ struct ctl_sctx *ctx = sess->ctx;
+ char tmp[MAX_NTOP], *pc;
+ int n;
+
+ REQUIRE(sess->state == initializing ||
+ sess->state == processing ||
+ sess->state == reading_data ||
+ sess->state == writing);
+ REQUIRE(sess->wrtiID.opaque == NULL);
+ REQUIRE(sess->wrID.opaque == NULL);
+ ctl_new_state(sess, writing, me);
+ sess->donefunc = donefunc;
+ sess->uap = uap;
+ if (!allocated_p(sess->outbuf) &&
+ ctl_bufget(&sess->outbuf, ctx->logger) < 0) {
+ (*ctx->logger)(ctl_error, "%s: %s: cant get an output buffer",
+ me, address_expr);
+ goto untimely;
+ }
+ if (sizeof "000-\r\n" + strlen(text) > (size_t)MAX_LINELEN) {
+ (*ctx->logger)(ctl_error, "%s: %s: output buffer ovf, closing",
+ me, address_expr);
+ goto untimely;
+ }
+ sess->outbuf.used = SPRINTF((sess->outbuf.text, "%03d%c%s\r\n",
+ code, (flags & CTL_MORE) != 0 ? '-' : ' ',
+ text));
+ for (pc = sess->outbuf.text, n = 0;
+ n < (int)sess->outbuf.used-2; pc++, n++)
+ if (!isascii((unsigned char)*pc) ||
+ !isprint((unsigned char)*pc))
+ *pc = '\040';
+ *iovp++ = evConsIovec(sess->outbuf.text, sess->outbuf.used);
+ if (body != NULL) {
+ char *tmp;
+ DE_CONST(body, tmp);
+ *iovp++ = evConsIovec(tmp, bodylen);
+ DE_CONST(".\r\n", tmp);
+ *iovp++ = evConsIovec(tmp, 3);
+ }
+ (*ctx->logger)(ctl_debug, "%s: [%d] %s", me,
+ sess->outbuf.used, sess->outbuf.text);
+ if (evWrite(ctx->ev, sess->sock, iov, iovp - iov,
+ ctl_writedone, sess, &sess->wrID) < 0) {
+ (*ctx->logger)(ctl_error, "%s: %s: evWrite: %s", me,
+ address_expr, strerror(errno));
+ goto untimely;
+ }
+ if (evSetIdleTimer(ctx->ev, ctl_wrtimeout, sess, ctx->timeout,
+ &sess->wrtiID) < 0)
+ {
+ (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me,
+ address_expr, strerror(errno));
+ goto untimely;
+ }
+ if (evTimeRW(ctx->ev, sess->wrID, sess->wrtiID) < 0) {
+ (*ctx->logger)(ctl_error, "%s: %s: evTimeRW: %s", me,
+ address_expr, strerror(errno));
+ untimely:
+ ctl_signal_done(ctx, sess);
+ ctl_close(sess);
+ return;
+ }
+ sess->respctx = respctx;
+ sess->respflags = flags;
+}
+
+void
+ctl_sendhelp(struct ctl_sess *sess, u_int code) {
+ static const char me[] = "ctl_sendhelp";
+ struct ctl_sctx *ctx = sess->ctx;
+
+ sess->helpcode = code;
+ sess->verb = &fakehelpverb;
+ ctl_morehelp(ctx, sess, NULL, me, CTL_MORE,
+ (const void *)ctx->verbs, NULL);
+}
+
+void *
+ctl_getcsctx(struct ctl_sess *sess) {
+ return (sess->csctx);
+}
+
+void *
+ctl_setcsctx(struct ctl_sess *sess, void *csctx) {
+ void *old = sess->csctx;
+
+ sess->csctx = csctx;
+ return (old);
+}
+
+/* Private functions. */
+
+static void
+ctl_accept(evContext lev, void *uap, int fd,
+ const void *lav, int lalen,
+ const void *rav, int ralen)
+{
+ static const char me[] = "ctl_accept";
+ struct ctl_sctx *ctx = uap;
+ struct ctl_sess *sess = NULL;
+ char tmp[MAX_NTOP];
+
+ UNUSED(lev);
+ UNUSED(lalen);
+ UNUSED(ralen);
+
+ if (fd < 0) {
+ (*ctx->logger)(ctl_error, "%s: accept: %s",
+ me, strerror(errno));
+ return;
+ }
+ if (ctx->cur_sess == ctx->max_sess) {
+ (*ctx->logger)(ctl_error, "%s: %s: too many control sessions",
+ me, ctl_sa_ntop((const struct sockaddr *)rav,
+ tmp, sizeof tmp,
+ ctx->logger));
+ (void) close(fd);
+ return;
+ }
+ sess = memget(sizeof *sess);
+ if (sess == NULL) {
+ (*ctx->logger)(ctl_error, "%s: memget: %s", me,
+ strerror(errno));
+ (void) close(fd);
+ return;
+ }
+ if (fcntl(fd, F_SETFD, 1) < 0) {
+ (*ctx->logger)(ctl_warning, "%s: fcntl: %s", me,
+ strerror(errno));
+ }
+ ctx->cur_sess++;
+ INIT_LINK(sess, link);
+ APPEND(ctx->sess, sess, link);
+ sess->ctx = ctx;
+ sess->sock = fd;
+ sess->wrID.opaque = NULL;
+ sess->rdID.opaque = NULL;
+ sess->wrtiID.opaque = NULL;
+ sess->rdtiID.opaque = NULL;
+ sess->respctx = NULL;
+ sess->csctx = NULL;
+ if (((const struct sockaddr *)rav)->sa_family == AF_UNIX)
+ ctl_sa_copy((const struct sockaddr *)lav,
+ (struct sockaddr *)&sess->sa);
+ else
+ ctl_sa_copy((const struct sockaddr *)rav,
+ (struct sockaddr *)&sess->sa);
+ sess->donefunc = NULL;
+ buffer_init(sess->inbuf);
+ buffer_init(sess->outbuf);
+ sess->state = available;
+ ctl_new_state(sess, initializing, me);
+ sess->verb = ctx->connverb;
+ (*ctx->logger)(ctl_debug, "%s: %s: accepting (fd %d)",
+ me, address_expr, sess->sock);
+ (*ctx->connverb->func)(ctx, sess, ctx->connverb, "", 0,
+ (const struct sockaddr *)rav, ctx->uctx);
+}
+
+static void
+ctl_new_state(struct ctl_sess *sess, enum state new_state, const char *reason)
+{
+ static const char me[] = "ctl_new_state";
+ struct ctl_sctx *ctx = sess->ctx;
+ char tmp[MAX_NTOP];
+
+ (*ctx->logger)(ctl_debug, "%s: %s: %s -> %s (%s)",
+ me, address_expr,
+ state_names[sess->state],
+ state_names[new_state], reason);
+ sess->state = new_state;
+}
+
+static void
+ctl_close(struct ctl_sess *sess) {
+ static const char me[] = "ctl_close";
+ struct ctl_sctx *ctx = sess->ctx;
+ char tmp[MAX_NTOP];
+
+ REQUIRE(sess->state == initializing ||
+ sess->state == writing ||
+ sess->state == reading ||
+ sess->state == processing ||
+ sess->state == reading_data ||
+ sess->state == idling);
+ REQUIRE(sess->sock != -1);
+ if (sess->state == reading || sess->state == reading_data)
+ ctl_stop_read(sess);
+ else if (sess->state == writing) {
+ if (sess->wrID.opaque != NULL) {
+ (void) evCancelRW(ctx->ev, sess->wrID);
+ sess->wrID.opaque = NULL;
+ }
+ if (sess->wrtiID.opaque != NULL) {
+ (void) evClearIdleTimer(ctx->ev, sess->wrtiID);
+ sess->wrtiID.opaque = NULL;
+ }
+ }
+ ctl_new_state(sess, closing, me);
+ (void) close(sess->sock);
+ if (allocated_p(sess->inbuf))
+ ctl_bufput(&sess->inbuf);
+ if (allocated_p(sess->outbuf))
+ ctl_bufput(&sess->outbuf);
+ (*ctx->logger)(ctl_debug, "%s: %s: closed (fd %d)",
+ me, address_expr, sess->sock);
+ UNLINK(ctx->sess, sess, link);
+ memput(sess, sizeof *sess);
+ ctx->cur_sess--;
+}
+
+static void
+ctl_start_read(struct ctl_sess *sess) {
+ static const char me[] = "ctl_start_read";
+ struct ctl_sctx *ctx = sess->ctx;
+ char tmp[MAX_NTOP];
+
+ REQUIRE(sess->state == initializing ||
+ sess->state == writing ||
+ sess->state == processing ||
+ sess->state == idling);
+ REQUIRE(sess->rdtiID.opaque == NULL);
+ REQUIRE(sess->rdID.opaque == NULL);
+ sess->inbuf.used = 0;
+ if (evSetIdleTimer(ctx->ev, ctl_rdtimeout, sess, ctx->timeout,
+ &sess->rdtiID) < 0)
+ {
+ (*ctx->logger)(ctl_error, "%s: %s: evSetIdleTimer: %s", me,
+ address_expr, strerror(errno));
+ ctl_close(sess);
+ return;
+ }
+ if (evSelectFD(ctx->ev, sess->sock, EV_READ,
+ ctl_readable, sess, &sess->rdID) < 0) {
+ (*ctx->logger)(ctl_error, "%s: %s: evSelectFD: %s", me,
+ address_expr, strerror(errno));
+ return;
+ }
+ ctl_new_state(sess, reading, me);
+}
+
+static void
+ctl_stop_read(struct ctl_sess *sess) {
+ static const char me[] = "ctl_stop_read";
+ struct ctl_sctx *ctx = sess->ctx;
+
+ REQUIRE(sess->state == reading || sess->state == reading_data);
+ REQUIRE(sess->rdID.opaque != NULL);
+ (void) evDeselectFD(ctx->ev, sess->rdID);
+ sess->rdID.opaque = NULL;
+ if (sess->rdtiID.opaque != NULL) {
+ (void) evClearIdleTimer(ctx->ev, sess->rdtiID);
+ sess->rdtiID.opaque = NULL;
+ }
+ ctl_new_state(sess, idling, me);
+}
+
+static void
+ctl_readable(evContext lev, void *uap, int fd, int evmask) {
+ static const char me[] = "ctl_readable";
+ struct ctl_sess *sess = uap;
+ struct ctl_sctx *ctx;
+ char *eos, tmp[MAX_NTOP];
+ ssize_t n;
+
+ REQUIRE(sess != NULL);
+ REQUIRE(fd >= 0);
+ REQUIRE(evmask == EV_READ);
+ REQUIRE(sess->state == reading || sess->state == reading_data);
+
+ ctx = sess->ctx;
+ evTouchIdleTimer(lev, sess->rdtiID);
+ if (!allocated_p(sess->inbuf) &&
+ ctl_bufget(&sess->inbuf, ctx->logger) < 0) {
+ (*ctx->logger)(ctl_error, "%s: %s: cant get an input buffer",
+ me, address_expr);
+ ctl_close(sess);
+ return;
+ }
+ n = read(sess->sock, sess->inbuf.text + sess->inbuf.used,
+ MAX_LINELEN - sess->inbuf.used);
+ if (n <= 0) {
+ (*ctx->logger)(ctl_debug, "%s: %s: read: %s",
+ me, address_expr,
+ (n == 0) ? "Unexpected EOF" : strerror(errno));
+ ctl_close(sess);
+ return;
+ }
+ sess->inbuf.used += n;
+ eos = memchr(sess->inbuf.text, '\n', sess->inbuf.used);
+ if (eos != NULL && eos != sess->inbuf.text && eos[-1] == '\r') {
+ eos[-1] = '\0';
+ if ((sess->respflags & CTL_DATA) != 0) {
+ INSIST(sess->verb != NULL);
+ (*sess->verb->func)(sess->ctx, sess, sess->verb,
+ sess->inbuf.text,
+ CTL_DATA, sess->respctx,
+ sess->ctx->uctx);
+ } else {
+ ctl_stop_read(sess);
+ ctl_docommand(sess);
+ }
+ sess->inbuf.used -= ((eos - sess->inbuf.text) + 1);
+ if (sess->inbuf.used == 0U)
+ ctl_bufput(&sess->inbuf);
+ else
+ memmove(sess->inbuf.text, eos + 1, sess->inbuf.used);
+ return;
+ }
+ if (sess->inbuf.used == (size_t)MAX_LINELEN) {
+ (*ctx->logger)(ctl_error, "%s: %s: line too long, closing",
+ me, address_expr);
+ ctl_close(sess);
+ }
+}
+
+static void
+ctl_wrtimeout(evContext lev, void *uap,
+ struct timespec due,
+ struct timespec itv)
+{
+ static const char me[] = "ctl_wrtimeout";
+ struct ctl_sess *sess = uap;
+ struct ctl_sctx *ctx = sess->ctx;
+ char tmp[MAX_NTOP];
+
+ UNUSED(lev);
+ UNUSED(due);
+ UNUSED(itv);
+
+ REQUIRE(sess->state == writing);
+ sess->wrtiID.opaque = NULL;
+ (*ctx->logger)(ctl_warning, "%s: %s: write timeout, closing",
+ me, address_expr);
+ if (sess->wrID.opaque != NULL) {
+ (void) evCancelRW(ctx->ev, sess->wrID);
+ sess->wrID.opaque = NULL;
+ }
+ ctl_signal_done(ctx, sess);
+ ctl_new_state(sess, processing, me);
+ ctl_close(sess);
+}
+
+static void
+ctl_rdtimeout(evContext lev, void *uap,
+ struct timespec due,
+ struct timespec itv)
+{
+ static const char me[] = "ctl_rdtimeout";
+ struct ctl_sess *sess = uap;
+ struct ctl_sctx *ctx = sess->ctx;
+ char tmp[MAX_NTOP];
+
+ UNUSED(lev);
+ UNUSED(due);
+ UNUSED(itv);
+
+ REQUIRE(sess->state == reading);
+ sess->rdtiID.opaque = NULL;
+ (*ctx->logger)(ctl_warning, "%s: %s: timeout, closing",
+ me, address_expr);
+ if (sess->state == reading || sess->state == reading_data)
+ ctl_stop_read(sess);
+ ctl_signal_done(ctx, sess);
+ ctl_new_state(sess, processing, me);
+ ctl_response(sess, ctx->timeoutcode, "Timeout.", CTL_EXIT, NULL,
+ NULL, NULL, NULL, 0);
+}
+
+static void
+ctl_docommand(struct ctl_sess *sess) {
+ static const char me[] = "ctl_docommand";
+ char *name, *rest, tmp[MAX_NTOP];
+ struct ctl_sctx *ctx = sess->ctx;
+ const struct ctl_verb *verb;
+
+ REQUIRE(allocated_p(sess->inbuf));
+ (*ctx->logger)(ctl_debug, "%s: %s: \"%s\" [%u]",
+ me, address_expr,
+ sess->inbuf.text, (u_int)sess->inbuf.used);
+ ctl_new_state(sess, processing, me);
+ name = sess->inbuf.text + strspn(sess->inbuf.text, space);
+ rest = name + strcspn(name, space);
+ if (*rest != '\0') {
+ *rest++ = '\0';
+ rest += strspn(rest, space);
+ }
+ for (verb = ctx->verbs;
+ verb != NULL && verb->name != NULL && verb->func != NULL;
+ verb++)
+ if (verb->name[0] != '\0' && strcasecmp(name, verb->name) == 0)
+ break;
+ if (verb != NULL && verb->name != NULL && verb->func != NULL) {
+ sess->verb = verb;
+ (*verb->func)(ctx, sess, verb, rest, 0, NULL, ctx->uctx);
+ } else {
+ char buf[1100];
+
+ if (sizeof "Unrecognized command \"\" (args \"\")" +
+ strlen(name) + strlen(rest) > sizeof buf)
+ strcpy(buf, "Unrecognized command (buf ovf)");
+ else
+ sprintf(buf,
+ "Unrecognized command \"%s\" (args \"%s\")",
+ name, rest);
+ ctl_response(sess, ctx->unkncode, buf, 0, NULL, NULL, NULL,
+ NULL, 0);
+ }
+}
+
+static void
+ctl_writedone(evContext lev, void *uap, int fd, int bytes) {
+ static const char me[] = "ctl_writedone";
+ struct ctl_sess *sess = uap;
+ struct ctl_sctx *ctx = sess->ctx;
+ char tmp[MAX_NTOP];
+ int save_errno = errno;
+
+ UNUSED(lev);
+ UNUSED(uap);
+
+ REQUIRE(sess->state == writing);
+ REQUIRE(fd == sess->sock);
+ REQUIRE(sess->wrtiID.opaque != NULL);
+ sess->wrID.opaque = NULL;
+ (void) evClearIdleTimer(ctx->ev, sess->wrtiID);
+ sess->wrtiID.opaque = NULL;
+ if (bytes < 0) {
+ (*ctx->logger)(ctl_error, "%s: %s: %s",
+ me, address_expr, strerror(save_errno));
+ ctl_close(sess);
+ return;
+ }
+
+ INSIST(allocated_p(sess->outbuf));
+ ctl_bufput(&sess->outbuf);
+ if ((sess->respflags & CTL_EXIT) != 0) {
+ ctl_signal_done(ctx, sess);
+ ctl_close(sess);
+ return;
+ } else if ((sess->respflags & CTL_MORE) != 0) {
+ INSIST(sess->verb != NULL);
+ (*sess->verb->func)(sess->ctx, sess, sess->verb, "",
+ CTL_MORE, sess->respctx, sess->ctx->uctx);
+ } else {
+ ctl_signal_done(ctx, sess);
+ ctl_start_read(sess);
+ }
+}
+
+static void
+ctl_morehelp(struct ctl_sctx *ctx, struct ctl_sess *sess,
+ const struct ctl_verb *verb, const char *text,
+ u_int respflags, const void *respctx, void *uctx)
+{
+ const struct ctl_verb *this = respctx, *next = this + 1;
+
+ UNUSED(ctx);
+ UNUSED(verb);
+ UNUSED(text);
+ UNUSED(uctx);
+
+ REQUIRE(!lastverb_p(this));
+ REQUIRE((respflags & CTL_MORE) != 0);
+ if (lastverb_p(next))
+ respflags &= ~CTL_MORE;
+ ctl_response(sess, sess->helpcode, this->help, respflags, next,
+ NULL, NULL, NULL, 0);
+}
+
+static void
+ctl_signal_done(struct ctl_sctx *ctx, struct ctl_sess *sess) {
+ if (sess->donefunc != NULL) {
+ (*sess->donefunc)(ctx, sess, sess->uap);
+ sess->donefunc = NULL;
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/ev_connects.c b/usr/src/lib/libresolv2_joy/common/isc/ev_connects.c
new file mode 100644
index 0000000000..821eb8ebff
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/ev_connects.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-1999 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* ev_connects.c - implement asynch connect/accept for the eventlib
+ * vix 16sep96 [initial]
+ */
+
+#include "port_before.h"
+#include "fd_setsize.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <unistd.h>
+
+#include <isc/eventlib.h>
+#include <isc/assertions.h>
+#include "eventlib_p.h"
+
+#include "port_after.h"
+
+/* Macros. */
+
+#define GETXXXNAME(f, s, sa, len) ( \
+ (f((s), (&sa), (&len)) >= 0) ? 0 : \
+ (errno != EAFNOSUPPORT && errno != EOPNOTSUPP) ? -1 : ( \
+ memset(&(sa), 0, sizeof (sa)), \
+ (len) = sizeof (sa), \
+ (sa).sa_family = AF_UNIX, \
+ 0 \
+ ) \
+ )
+
+/* Forward. */
+
+static void listener(evContext ctx, void *uap, int fd, int evmask);
+static void connector(evContext ctx, void *uap, int fd, int evmask);
+
+/* Public. */
+
+int
+evListen(evContext opaqueCtx, int fd, int maxconn,
+ evConnFunc func, void *uap, evConnID *id)
+{
+ evContext_p *ctx = opaqueCtx.opaque;
+ evConn *new;
+ int mode;
+
+ OKNEW(new);
+ new->flags = EV_CONN_LISTEN;
+ OKFREE(mode = fcntl(fd, F_GETFL, NULL), new); /*%< side effect: validate fd. */
+ /*
+ * Remember the nonblocking status. We assume that either evSelectFD
+ * has not been done to this fd, or that if it has then the caller
+ * will evCancelConn before they evDeselectFD. If our assumptions
+ * are not met, then we might restore the old nonblocking status
+ * incorrectly.
+ */
+ if ((mode & PORT_NONBLOCK) == 0) {
+#ifdef USE_FIONBIO_IOCTL
+ int on = 1;
+ OKFREE(ioctl(fd, FIONBIO, (char *)&on), new);
+#else
+ OKFREE(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK), new);
+#endif
+ new->flags |= EV_CONN_BLOCK;
+ }
+ OKFREE(listen(fd, maxconn), new);
+ if (evSelectFD(opaqueCtx, fd, EV_READ, listener, new, &new->file) < 0){
+ int save = errno;
+
+ FREE(new);
+ errno = save;
+ return (-1);
+ }
+ new->flags |= EV_CONN_SELECTED;
+ new->func = func;
+ new->uap = uap;
+ new->fd = fd;
+ if (ctx->conns != NULL)
+ ctx->conns->prev = new;
+ new->prev = NULL;
+ new->next = ctx->conns;
+ ctx->conns = new;
+ if (id)
+ id->opaque = new;
+ return (0);
+}
+
+int
+evConnect(evContext opaqueCtx, int fd, const void *ra, int ralen,
+ evConnFunc func, void *uap, evConnID *id)
+{
+ evContext_p *ctx = opaqueCtx.opaque;
+ evConn *new;
+
+ OKNEW(new);
+ new->flags = 0;
+ /* Do the select() first to get the socket into nonblocking mode. */
+ if (evSelectFD(opaqueCtx, fd, EV_MASK_ALL,
+ connector, new, &new->file) < 0) {
+ int save = errno;
+
+ FREE(new);
+ errno = save;
+ return (-1);
+ }
+ new->flags |= EV_CONN_SELECTED;
+ if (connect(fd, ra, ralen) < 0 &&
+ errno != EWOULDBLOCK &&
+ errno != EAGAIN &&
+ errno != EINPROGRESS) {
+ int save = errno;
+
+ (void) evDeselectFD(opaqueCtx, new->file);
+ FREE(new);
+ errno = save;
+ return (-1);
+ }
+ /* No error, or EWOULDBLOCK. select() tells when it's ready. */
+ new->func = func;
+ new->uap = uap;
+ new->fd = fd;
+ if (ctx->conns != NULL)
+ ctx->conns->prev = new;
+ new->prev = NULL;
+ new->next = ctx->conns;
+ ctx->conns = new;
+ if (id)
+ id->opaque = new;
+ return (0);
+}
+
+int
+evCancelConn(evContext opaqueCtx, evConnID id) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evConn *this = id.opaque;
+ evAccept *acc, *nxtacc;
+ int mode;
+
+ if ((this->flags & EV_CONN_SELECTED) != 0)
+ (void) evDeselectFD(opaqueCtx, this->file);
+ if ((this->flags & EV_CONN_BLOCK) != 0) {
+ mode = fcntl(this->fd, F_GETFL, NULL);
+ if (mode == -1) {
+ if (errno != EBADF)
+ return (-1);
+ } else {
+#ifdef USE_FIONBIO_IOCTL
+ int off = 0;
+ OK(ioctl(this->fd, FIONBIO, (char *)&off));
+#else
+ OK(fcntl(this->fd, F_SETFL, mode & ~PORT_NONBLOCK));
+#endif
+ }
+ }
+
+ /* Unlink from ctx->conns. */
+ if (this->prev != NULL)
+ this->prev->next = this->next;
+ else
+ ctx->conns = this->next;
+ if (this->next != NULL)
+ this->next->prev = this->prev;
+
+ /*
+ * Remove `this' from the ctx->accepts list (zero or more times).
+ */
+ for (acc = HEAD(ctx->accepts), nxtacc = NULL;
+ acc != NULL;
+ acc = nxtacc)
+ {
+ nxtacc = NEXT(acc, link);
+ if (acc->conn == this) {
+ UNLINK(ctx->accepts, acc, link);
+ close(acc->fd);
+ FREE(acc);
+ }
+ }
+
+ /* Wrap up and get out. */
+ FREE(this);
+ return (0);
+}
+
+int evHold(evContext opaqueCtx, evConnID id) {
+ evConn *this = id.opaque;
+
+ if ((this->flags & EV_CONN_LISTEN) == 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if ((this->flags & EV_CONN_SELECTED) == 0)
+ return (0);
+ this->flags &= ~EV_CONN_SELECTED;
+ return (evDeselectFD(opaqueCtx, this->file));
+}
+
+int evUnhold(evContext opaqueCtx, evConnID id) {
+ evConn *this = id.opaque;
+ int ret;
+
+ if ((this->flags & EV_CONN_LISTEN) == 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if ((this->flags & EV_CONN_SELECTED) != 0)
+ return (0);
+ ret = evSelectFD(opaqueCtx, this->fd, EV_READ, listener, this,
+ &this->file);
+ if (ret == 0)
+ this->flags |= EV_CONN_SELECTED;
+ return (ret);
+}
+
+int
+evTryAccept(evContext opaqueCtx, evConnID id, int *sys_errno) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evConn *conn = id.opaque;
+ evAccept *new;
+
+ if ((conn->flags & EV_CONN_LISTEN) == 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ OKNEW(new);
+ new->conn = conn;
+ new->ralen = sizeof new->ra;
+ new->fd = accept(conn->fd, &new->ra.sa, &new->ralen);
+ if (new->fd > ctx->highestFD) {
+ close(new->fd);
+ new->fd = -1;
+ new->ioErrno = ENOTSOCK;
+ }
+ if (new->fd >= 0) {
+ new->lalen = sizeof new->la;
+ if (GETXXXNAME(getsockname, new->fd, new->la.sa, new->lalen) < 0) {
+ new->ioErrno = errno;
+ (void) close(new->fd);
+ new->fd = -1;
+ } else
+ new->ioErrno = 0;
+ } else {
+ new->ioErrno = errno;
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ FREE(new);
+ return (-1);
+ }
+ }
+ INIT_LINK(new, link);
+ APPEND(ctx->accepts, new, link);
+ *sys_errno = new->ioErrno;
+ return (0);
+}
+
+/* Private. */
+
+static void
+listener(evContext opaqueCtx, void *uap, int fd, int evmask) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evConn *conn = uap;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+#ifndef NO_SOCKADDR_UN
+ struct sockaddr_un un;
+#endif
+ } la, ra;
+ int new;
+ ISC_SOCKLEN_T lalen = 0, ralen;
+
+ REQUIRE((evmask & EV_READ) != 0);
+ ralen = sizeof ra;
+ new = accept(fd, &ra.sa, &ralen);
+ if (new > ctx->highestFD) {
+ close(new);
+ new = -1;
+ errno = ENOTSOCK;
+ }
+ if (new >= 0) {
+ lalen = sizeof la;
+ if (GETXXXNAME(getsockname, new, la.sa, lalen) < 0) {
+ int save = errno;
+
+ (void) close(new);
+ errno = save;
+ new = -1;
+ }
+ } else if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return;
+ (*conn->func)(opaqueCtx, conn->uap, new, &la.sa, lalen, &ra.sa, ralen);
+}
+
+static void
+connector(evContext opaqueCtx, void *uap, int fd, int evmask) {
+ evConn *conn = uap;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+#ifndef NO_SOCKADDR_UN
+ struct sockaddr_un un;
+#endif
+ } la, ra;
+ ISC_SOCKLEN_T lalen, ralen;
+#ifndef NETREAD_BROKEN
+ char buf[1];
+#endif
+ void *conn_uap;
+ evConnFunc conn_func;
+ evConnID id;
+ int socket_errno = 0;
+ ISC_SOCKLEN_T optlen;
+
+ UNUSED(evmask);
+
+ lalen = sizeof la;
+ ralen = sizeof ra;
+ conn_uap = conn->uap;
+ conn_func = conn->func;
+ id.opaque = conn;
+#ifdef SO_ERROR
+ optlen = sizeof socket_errno;
+ if (fd < 0 &&
+ getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, (char *)&socket_errno,
+ &optlen) < 0)
+ socket_errno = errno;
+ else
+ errno = socket_errno;
+#endif
+ if (evCancelConn(opaqueCtx, id) < 0 ||
+ socket_errno ||
+#ifdef NETREAD_BROKEN
+ 0 ||
+#else
+ read(fd, buf, 0) < 0 ||
+#endif
+ GETXXXNAME(getsockname, fd, la.sa, lalen) < 0 ||
+ GETXXXNAME(getpeername, fd, ra.sa, ralen) < 0) {
+ int save = errno;
+
+ (void) close(fd); /*%< XXX closing caller's fd */
+ errno = save;
+ fd = -1;
+ }
+ (*conn_func)(opaqueCtx, conn_uap, fd, &la.sa, lalen, &ra.sa, ralen);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/ev_files.c b/usr/src/lib/libresolv2_joy/common/isc/ev_files.c
new file mode 100644
index 0000000000..f75ff6b646
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/ev_files.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-1999 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* ev_files.c - implement asynch file IO for the eventlib
+ * vix 11sep95 [initial]
+ */
+
+#include "port_before.h"
+#include "fd_setsize.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <isc/eventlib.h>
+#include "eventlib_p.h"
+
+#include "port_after.h"
+
+static evFile *FindFD(const evContext_p *ctx, int fd, int eventmask);
+
+int
+evSelectFD(evContext opaqueCtx,
+ int fd,
+ int eventmask,
+ evFileFunc func,
+ void *uap,
+ evFileID *opaqueID
+) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evFile *id;
+ int mode;
+
+ evPrintf(ctx, 1,
+ "evSelectFD(ctx %p, fd %d, mask 0x%x, func %p, uap %p)\n",
+ ctx, fd, eventmask, func, uap);
+ if (eventmask == 0 || (eventmask & ~EV_MASK_ALL) != 0)
+ EV_ERR(EINVAL);
+#ifndef USE_POLL
+ if (fd > ctx->highestFD)
+ EV_ERR(EINVAL);
+#endif
+ OK(mode = fcntl(fd, F_GETFL, NULL)); /*%< side effect: validate fd. */
+ /*
+ * The first time we touch a file descriptor, we need to check to see
+ * if the application already had it in O_NONBLOCK mode and if so, all
+ * of our deselect()'s have to leave it in O_NONBLOCK. If not, then
+ * all but our last deselect() has to leave it in O_NONBLOCK.
+ */
+#ifdef USE_POLL
+ /* Make sure both ctx->pollfds[] and ctx->fdTable[] are large enough */
+ if (fd >= ctx->maxnfds && evPollfdRealloc(ctx, 1, fd) != 0)
+ EV_ERR(ENOMEM);
+#endif /* USE_POLL */
+ id = FindFD(ctx, fd, EV_MASK_ALL);
+ if (id == NULL) {
+ if (mode & PORT_NONBLOCK)
+ FD_SET(fd, &ctx->nonblockBefore);
+ else {
+#ifdef USE_FIONBIO_IOCTL
+ int on = 1;
+ OK(ioctl(fd, FIONBIO, (char *)&on));
+#else
+ OK(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK));
+#endif
+ FD_CLR(fd, &ctx->nonblockBefore);
+ }
+ }
+
+ /*
+ * If this descriptor is already in use, search for it again to see
+ * if any of the eventmask bits we want to set are already captured.
+ * We cannot usefully capture the same fd event more than once in the
+ * same context.
+ */
+ if (id != NULL && FindFD(ctx, fd, eventmask) != NULL)
+ EV_ERR(ETOOMANYREFS);
+
+ /* Allocate and fill. */
+ OKNEW(id);
+ id->func = func;
+ id->uap = uap;
+ id->fd = fd;
+ id->eventmask = eventmask;
+
+ /*
+ * Insert at head. Order could be important for performance if we
+ * believe that evGetNext()'s accesses to the fd_sets will be more
+ * serial and therefore more cache-lucky if the list is ordered by
+ * ``fd.'' We do not believe these things, so we don't do it.
+ *
+ * The interesting sequence is where GetNext() has cached a select()
+ * result and the caller decides to evSelectFD() on some descriptor.
+ * Since GetNext() starts at the head, it can miss new entries we add
+ * at the head. This is not a serious problem since the event being
+ * evSelectFD()'d for has to occur before evSelectFD() is called for
+ * the file event to be considered "missed" -- a real corner case.
+ * Maintaining a "tail" pointer for ctx->files would fix this, but I'm
+ * not sure it would be ``more correct.''
+ */
+ if (ctx->files != NULL)
+ ctx->files->prev = id;
+ id->prev = NULL;
+ id->next = ctx->files;
+ ctx->files = id;
+
+ /* Insert into fd table. */
+ if (ctx->fdTable[fd] != NULL)
+ ctx->fdTable[fd]->fdprev = id;
+ id->fdprev = NULL;
+ id->fdnext = ctx->fdTable[fd];
+ ctx->fdTable[fd] = id;
+
+ /* Turn on the appropriate bits in the {rd,wr,ex}Next fd_set's. */
+ if (eventmask & EV_READ)
+ FD_SET(fd, &ctx->rdNext);
+ if (eventmask & EV_WRITE)
+ FD_SET(fd, &ctx->wrNext);
+ if (eventmask & EV_EXCEPT)
+ FD_SET(fd, &ctx->exNext);
+
+ /* Update fdMax. */
+ if (fd > ctx->fdMax)
+ ctx->fdMax = fd;
+
+ /* Remember the ID if the caller provided us a place for it. */
+ if (opaqueID)
+ opaqueID->opaque = id;
+
+ return (0);
+}
+
+int
+evDeselectFD(evContext opaqueCtx, evFileID opaqueID) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evFile *del = opaqueID.opaque;
+ evFile *cur;
+ int mode, eventmask;
+
+ if (!del) {
+ evPrintf(ctx, 11, "evDeselectFD(NULL) ignored\n");
+ errno = EINVAL;
+ return (-1);
+ }
+
+ evPrintf(ctx, 1, "evDeselectFD(fd %d, mask 0x%x)\n",
+ del->fd, del->eventmask);
+
+ /* Get the mode. Unless the file has been closed, errors are bad. */
+ mode = fcntl(del->fd, F_GETFL, NULL);
+ if (mode == -1 && errno != EBADF)
+ EV_ERR(errno);
+
+ /* Remove from the list of files. */
+ if (del->prev != NULL)
+ del->prev->next = del->next;
+ else
+ ctx->files = del->next;
+ if (del->next != NULL)
+ del->next->prev = del->prev;
+
+ /* Remove from the fd table. */
+ if (del->fdprev != NULL)
+ del->fdprev->fdnext = del->fdnext;
+ else
+ ctx->fdTable[del->fd] = del->fdnext;
+ if (del->fdnext != NULL)
+ del->fdnext->fdprev = del->fdprev;
+
+ /*
+ * If the file descriptor does not appear in any other select() entry,
+ * and if !EV_WASNONBLOCK, and if we got no EBADF when we got the mode
+ * earlier, then: restore the fd to blocking status.
+ */
+ if (!(cur = FindFD(ctx, del->fd, EV_MASK_ALL)) &&
+ !FD_ISSET(del->fd, &ctx->nonblockBefore) &&
+ mode != -1) {
+ /*
+ * Note that we won't return an error status to the caller if
+ * this fcntl() fails since (a) we've already done the work
+ * and (b) the caller didn't ask us anything about O_NONBLOCK.
+ */
+#ifdef USE_FIONBIO_IOCTL
+ int off = 0;
+ (void) ioctl(del->fd, FIONBIO, (char *)&off);
+#else
+ (void) fcntl(del->fd, F_SETFL, mode & ~PORT_NONBLOCK);
+#endif
+ }
+
+ /*
+ * Now find all other uses of this descriptor and OR together an event
+ * mask so that we don't turn off {rd,wr,ex}Next bits that some other
+ * file event is using. As an optimization, stop if the event mask
+ * fills.
+ */
+ eventmask = 0;
+ for ((void)NULL;
+ cur != NULL && eventmask != EV_MASK_ALL;
+ cur = cur->next)
+ if (cur->fd == del->fd)
+ eventmask |= cur->eventmask;
+
+ /* OK, now we know which bits we can clear out. */
+ if (!(eventmask & EV_READ)) {
+ FD_CLR(del->fd, &ctx->rdNext);
+ if (FD_ISSET(del->fd, &ctx->rdLast)) {
+ FD_CLR(del->fd, &ctx->rdLast);
+ ctx->fdCount--;
+ }
+ }
+ if (!(eventmask & EV_WRITE)) {
+ FD_CLR(del->fd, &ctx->wrNext);
+ if (FD_ISSET(del->fd, &ctx->wrLast)) {
+ FD_CLR(del->fd, &ctx->wrLast);
+ ctx->fdCount--;
+ }
+ }
+ if (!(eventmask & EV_EXCEPT)) {
+ FD_CLR(del->fd, &ctx->exNext);
+ if (FD_ISSET(del->fd, &ctx->exLast)) {
+ FD_CLR(del->fd, &ctx->exLast);
+ ctx->fdCount--;
+ }
+ }
+
+ /* If this was the maxFD, find the new one. */
+ if (del->fd == ctx->fdMax) {
+ ctx->fdMax = -1;
+ for (cur = ctx->files; cur; cur = cur->next)
+ if (cur->fd > ctx->fdMax)
+ ctx->fdMax = cur->fd;
+ }
+
+ /* If this was the fdNext, cycle that to the next entry. */
+ if (del == ctx->fdNext)
+ ctx->fdNext = del->next;
+
+ /* Couldn't free it before now since we were using fields out of it. */
+ FREE(del);
+
+ return (0);
+}
+
+static evFile *
+FindFD(const evContext_p *ctx, int fd, int eventmask) {
+ evFile *id;
+
+ for (id = ctx->fdTable[fd]; id != NULL; id = id->fdnext)
+ if (id->fd == fd && (id->eventmask & eventmask) != 0)
+ break;
+ return (id);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/ev_streams.c b/usr/src/lib/libresolv2_joy/common/isc/ev_streams.c
new file mode 100644
index 0000000000..824b4e25c1
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/ev_streams.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* ev_streams.c - implement asynch stream file IO for the eventlib
+ * vix 04mar96 [initial]
+ */
+
+#include "port_before.h"
+#include "fd_setsize.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+
+#include <isc/eventlib.h>
+#include <isc/assertions.h>
+#include "eventlib_p.h"
+
+#include "port_after.h"
+
+static int copyvec(evStream *str, const struct iovec *iov, int iocnt);
+static void consume(evStream *str, size_t bytes);
+static void done(evContext opaqueCtx, evStream *str);
+static void writable(evContext opaqueCtx, void *uap, int fd, int evmask);
+static void readable(evContext opaqueCtx, void *uap, int fd, int evmask);
+
+struct iovec
+evConsIovec(void *buf, size_t cnt) {
+ struct iovec ret;
+
+ memset(&ret, 0xf5, sizeof ret);
+ ret.iov_base = buf;
+ ret.iov_len = cnt;
+ return (ret);
+}
+
+int
+evWrite(evContext opaqueCtx, int fd, const struct iovec *iov, int iocnt,
+ evStreamFunc func, void *uap, evStreamID *id)
+{
+ evContext_p *ctx = opaqueCtx.opaque;
+ evStream *new;
+ int save;
+
+ OKNEW(new);
+ new->func = func;
+ new->uap = uap;
+ new->fd = fd;
+ new->flags = 0;
+ if (evSelectFD(opaqueCtx, fd, EV_WRITE, writable, new, &new->file) < 0)
+ goto free;
+ if (copyvec(new, iov, iocnt) < 0)
+ goto free;
+ new->prevDone = NULL;
+ new->nextDone = NULL;
+ if (ctx->streams != NULL)
+ ctx->streams->prev = new;
+ new->prev = NULL;
+ new->next = ctx->streams;
+ ctx->streams = new;
+ if (id != NULL)
+ id->opaque = new;
+ return (0);
+ free:
+ save = errno;
+ FREE(new);
+ errno = save;
+ return (-1);
+}
+
+int
+evRead(evContext opaqueCtx, int fd, const struct iovec *iov, int iocnt,
+ evStreamFunc func, void *uap, evStreamID *id)
+{
+ evContext_p *ctx = opaqueCtx.opaque;
+ evStream *new;
+ int save;
+
+ OKNEW(new);
+ new->func = func;
+ new->uap = uap;
+ new->fd = fd;
+ new->flags = 0;
+ if (evSelectFD(opaqueCtx, fd, EV_READ, readable, new, &new->file) < 0)
+ goto free;
+ if (copyvec(new, iov, iocnt) < 0)
+ goto free;
+ new->prevDone = NULL;
+ new->nextDone = NULL;
+ if (ctx->streams != NULL)
+ ctx->streams->prev = new;
+ new->prev = NULL;
+ new->next = ctx->streams;
+ ctx->streams = new;
+ if (id)
+ id->opaque = new;
+ return (0);
+ free:
+ save = errno;
+ FREE(new);
+ errno = save;
+ return (-1);
+}
+
+int
+evTimeRW(evContext opaqueCtx, evStreamID id, evTimerID timer) /*ARGSUSED*/ {
+ evStream *str = id.opaque;
+
+ UNUSED(opaqueCtx);
+
+ str->timer = timer;
+ str->flags |= EV_STR_TIMEROK;
+ return (0);
+}
+
+int
+evUntimeRW(evContext opaqueCtx, evStreamID id) /*ARGSUSED*/ {
+ evStream *str = id.opaque;
+
+ UNUSED(opaqueCtx);
+
+ str->flags &= ~EV_STR_TIMEROK;
+ return (0);
+}
+
+int
+evCancelRW(evContext opaqueCtx, evStreamID id) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evStream *old = id.opaque;
+
+ /*
+ * The streams list is doubly threaded. First, there's ctx->streams
+ * that's used by evDestroy() to find and cancel all streams. Second,
+ * there's ctx->strDone (head) and ctx->strLast (tail) which thread
+ * through the potentially smaller number of "IO completed" streams,
+ * used in evGetNext() to avoid scanning the entire list.
+ */
+
+ /* Unlink from ctx->streams. */
+ if (old->prev != NULL)
+ old->prev->next = old->next;
+ else
+ ctx->streams = old->next;
+ if (old->next != NULL)
+ old->next->prev = old->prev;
+
+ /*
+ * If 'old' is on the ctx->strDone list, remove it. Update
+ * ctx->strLast if necessary.
+ */
+ if (old->prevDone == NULL && old->nextDone == NULL) {
+ /*
+ * Either 'old' is the only item on the done list, or it's
+ * not on the done list. If the former, then we unlink it
+ * from the list. If the latter, we leave the list alone.
+ */
+ if (ctx->strDone == old) {
+ ctx->strDone = NULL;
+ ctx->strLast = NULL;
+ }
+ } else {
+ if (old->prevDone != NULL)
+ old->prevDone->nextDone = old->nextDone;
+ else
+ ctx->strDone = old->nextDone;
+ if (old->nextDone != NULL)
+ old->nextDone->prevDone = old->prevDone;
+ else
+ ctx->strLast = old->prevDone;
+ }
+
+ /* Deallocate the stream. */
+ if (old->file.opaque)
+ evDeselectFD(opaqueCtx, old->file);
+ memput(old->iovOrig, sizeof (struct iovec) * old->iovOrigCount);
+ FREE(old);
+ return (0);
+}
+
+/* Copy a scatter/gather vector and initialize a stream handler's IO. */
+static int
+copyvec(evStream *str, const struct iovec *iov, int iocnt) {
+ int i;
+
+ str->iovOrig = (struct iovec *)memget(sizeof(struct iovec) * iocnt);
+ if (str->iovOrig == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ str->ioTotal = 0;
+ for (i = 0; i < iocnt; i++) {
+ str->iovOrig[i] = iov[i];
+ str->ioTotal += iov[i].iov_len;
+ }
+ str->iovOrigCount = iocnt;
+ str->iovCur = str->iovOrig;
+ str->iovCurCount = str->iovOrigCount;
+ str->ioDone = 0;
+ return (0);
+}
+
+/* Pull off or truncate lead iovec(s). */
+static void
+consume(evStream *str, size_t bytes) {
+ while (bytes > 0U) {
+ if (bytes < (size_t)str->iovCur->iov_len) {
+ str->iovCur->iov_len -= bytes;
+ str->iovCur->iov_base = (void *)
+ ((u_char *)str->iovCur->iov_base + bytes);
+ str->ioDone += bytes;
+ bytes = 0;
+ } else {
+ bytes -= str->iovCur->iov_len;
+ str->ioDone += str->iovCur->iov_len;
+ str->iovCur++;
+ str->iovCurCount--;
+ }
+ }
+}
+
+/* Add a stream to Done list and deselect the FD. */
+static void
+done(evContext opaqueCtx, evStream *str) {
+ evContext_p *ctx = opaqueCtx.opaque;
+
+ if (ctx->strLast != NULL) {
+ str->prevDone = ctx->strLast;
+ ctx->strLast->nextDone = str;
+ ctx->strLast = str;
+ } else {
+ INSIST(ctx->strDone == NULL);
+ ctx->strDone = ctx->strLast = str;
+ }
+ evDeselectFD(opaqueCtx, str->file);
+ str->file.opaque = NULL;
+ /* evDrop() will call evCancelRW() on us. */
+}
+
+/* Dribble out some bytes on the stream. (Called by evDispatch().) */
+static void
+writable(evContext opaqueCtx, void *uap, int fd, int evmask) {
+ evStream *str = uap;
+ int bytes;
+
+ UNUSED(evmask);
+
+ bytes = writev(fd, str->iovCur, str->iovCurCount);
+ if (bytes > 0) {
+ if ((str->flags & EV_STR_TIMEROK) != 0)
+ evTouchIdleTimer(opaqueCtx, str->timer);
+ consume(str, bytes);
+ } else {
+ if (bytes < 0 && errno != EINTR) {
+ str->ioDone = -1;
+ str->ioErrno = errno;
+ }
+ }
+ if (str->ioDone == -1 || str->ioDone == str->ioTotal)
+ done(opaqueCtx, str);
+}
+
+/* Scoop up some bytes from the stream. (Called by evDispatch().) */
+static void
+readable(evContext opaqueCtx, void *uap, int fd, int evmask) {
+ evStream *str = uap;
+ int bytes;
+
+ UNUSED(evmask);
+
+ bytes = readv(fd, str->iovCur, str->iovCurCount);
+ if (bytes > 0) {
+ if ((str->flags & EV_STR_TIMEROK) != 0)
+ evTouchIdleTimer(opaqueCtx, str->timer);
+ consume(str, bytes);
+ } else {
+ if (bytes == 0)
+ str->ioDone = 0;
+ else {
+ if (errno != EINTR) {
+ str->ioDone = -1;
+ str->ioErrno = errno;
+ }
+ }
+ }
+ if (str->ioDone <= 0 || str->ioDone == str->ioTotal)
+ done(opaqueCtx, str);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/ev_timers.c b/usr/src/lib/libresolv2_joy/common/isc/ev_timers.c
new file mode 100644
index 0000000000..4aab57f5ff
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/ev_timers.c
@@ -0,0 +1,493 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-1999 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* ev_timers.c - implement timers for the eventlib
+ * vix 09sep95 [initial]
+ */
+
+#include "port_before.h"
+#include "fd_setsize.h"
+
+#include <errno.h>
+
+#include <isc/assertions.h>
+#include <isc/eventlib.h>
+#include "eventlib_p.h"
+
+#include "port_after.h"
+
+/* Constants. */
+
+#define MILLION 1000000
+#define BILLION 1000000000
+
+/* Forward. */
+
+static int due_sooner(void *, void *);
+static void set_index(void *, int);
+static void free_timer(void *, void *);
+static void print_timer(void *, void *);
+static void idle_timeout(evContext, void *, struct timespec, struct timespec);
+
+/* Private type. */
+
+typedef struct {
+ evTimerFunc func;
+ void * uap;
+ struct timespec lastTouched;
+ struct timespec max_idle;
+ evTimer * timer;
+} idle_timer;
+
+/* Public. */
+
+struct timespec
+evConsTime(time_t sec, long nsec) {
+ struct timespec x;
+
+ x.tv_sec = sec;
+ x.tv_nsec = nsec;
+ return (x);
+}
+
+struct timespec
+evAddTime(struct timespec addend1, struct timespec addend2) {
+ struct timespec x;
+
+ x.tv_sec = addend1.tv_sec + addend2.tv_sec;
+ x.tv_nsec = addend1.tv_nsec + addend2.tv_nsec;
+ if (x.tv_nsec >= BILLION) {
+ x.tv_sec++;
+ x.tv_nsec -= BILLION;
+ }
+ return (x);
+}
+
+struct timespec
+evSubTime(struct timespec minuend, struct timespec subtrahend) {
+ struct timespec x;
+
+ x.tv_sec = minuend.tv_sec - subtrahend.tv_sec;
+ if (minuend.tv_nsec >= subtrahend.tv_nsec)
+ x.tv_nsec = minuend.tv_nsec - subtrahend.tv_nsec;
+ else {
+ x.tv_nsec = BILLION - subtrahend.tv_nsec + minuend.tv_nsec;
+ x.tv_sec--;
+ }
+ return (x);
+}
+
+int
+evCmpTime(struct timespec a, struct timespec b) {
+ long x = a.tv_sec - b.tv_sec;
+
+ if (x == 0L)
+ x = a.tv_nsec - b.tv_nsec;
+ return (x < 0L ? (-1) : x > 0L ? (1) : (0));
+}
+
+struct timespec
+evNowTime() {
+ struct timeval now;
+#ifdef CLOCK_REALTIME
+ struct timespec tsnow;
+ int m = CLOCK_REALTIME;
+
+#ifdef CLOCK_MONOTONIC
+ if (__evOptMonoTime)
+ m = CLOCK_MONOTONIC;
+#endif
+ if (clock_gettime(m, &tsnow) == 0)
+ return (tsnow);
+#endif
+ if (gettimeofday(&now, NULL) < 0)
+ return (evConsTime(0, 0));
+ return (evTimeSpec(now));
+}
+
+struct timespec
+evUTCTime() {
+ struct timeval now;
+#ifdef CLOCK_REALTIME
+ struct timespec tsnow;
+ if (clock_gettime(CLOCK_REALTIME, &tsnow) == 0)
+ return (tsnow);
+#endif
+ if (gettimeofday(&now, NULL) < 0)
+ return (evConsTime(0, 0));
+ return (evTimeSpec(now));
+}
+
+struct timespec
+evLastEventTime(evContext opaqueCtx) {
+ evContext_p *ctx = opaqueCtx.opaque;
+
+ return (ctx->lastEventTime);
+}
+
+struct timespec
+evTimeSpec(struct timeval tv) {
+ struct timespec ts;
+
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+ return (ts);
+}
+
+struct timeval
+evTimeVal(struct timespec ts) {
+ struct timeval tv;
+
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = ts.tv_nsec / 1000;
+ return (tv);
+}
+
+int
+evSetTimer(evContext opaqueCtx,
+ evTimerFunc func,
+ void *uap,
+ struct timespec due,
+ struct timespec inter,
+ evTimerID *opaqueID
+) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evTimer *id;
+
+ evPrintf(ctx, 1,
+"evSetTimer(ctx %p, func %p, uap %p, due %ld.%09ld, inter %ld.%09ld)\n",
+ ctx, func, uap,
+ (long)due.tv_sec, due.tv_nsec,
+ (long)inter.tv_sec, inter.tv_nsec);
+
+#ifdef __hpux
+ /*
+ * tv_sec and tv_nsec are unsigned.
+ */
+ if (due.tv_nsec >= BILLION)
+ EV_ERR(EINVAL);
+
+ if (inter.tv_nsec >= BILLION)
+ EV_ERR(EINVAL);
+#else
+ if (due.tv_sec < 0 || due.tv_nsec < 0 || due.tv_nsec >= BILLION)
+ EV_ERR(EINVAL);
+
+ if (inter.tv_sec < 0 || inter.tv_nsec < 0 || inter.tv_nsec >= BILLION)
+ EV_ERR(EINVAL);
+#endif
+
+ /* due={0,0} is a magic cookie meaning "now." */
+ if (due.tv_sec == (time_t)0 && due.tv_nsec == 0L)
+ due = evNowTime();
+
+ /* Allocate and fill. */
+ OKNEW(id);
+ id->func = func;
+ id->uap = uap;
+ id->due = due;
+ id->inter = inter;
+
+ if (heap_insert(ctx->timers, id) < 0)
+ return (-1);
+
+ /* Remember the ID if the caller provided us a place for it. */
+ if (opaqueID)
+ opaqueID->opaque = id;
+
+ if (ctx->debug > 7) {
+ evPrintf(ctx, 7, "timers after evSetTimer:\n");
+ (void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
+ }
+
+ return (0);
+}
+
+int
+evClearTimer(evContext opaqueCtx, evTimerID id) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evTimer *del = id.opaque;
+
+ if (ctx->cur != NULL &&
+ ctx->cur->type == Timer &&
+ ctx->cur->u.timer.this == del) {
+ evPrintf(ctx, 8, "deferring delete of timer (executing)\n");
+ /*
+ * Setting the interval to zero ensures that evDrop() will
+ * clean up the timer.
+ */
+ del->inter = evConsTime(0, 0);
+ return (0);
+ }
+
+ if (heap_element(ctx->timers, del->index) != del)
+ EV_ERR(ENOENT);
+
+ if (heap_delete(ctx->timers, del->index) < 0)
+ return (-1);
+ FREE(del);
+
+ if (ctx->debug > 7) {
+ evPrintf(ctx, 7, "timers after evClearTimer:\n");
+ (void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
+ }
+
+ return (0);
+}
+
+int
+evConfigTimer(evContext opaqueCtx,
+ evTimerID id,
+ const char *param,
+ int value
+) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evTimer *timer = id.opaque;
+ int result=0;
+
+ UNUSED(value);
+
+ if (heap_element(ctx->timers, timer->index) != timer)
+ EV_ERR(ENOENT);
+
+ if (strcmp(param, "rate") == 0)
+ timer->mode |= EV_TMR_RATE;
+ else if (strcmp(param, "interval") == 0)
+ timer->mode &= ~EV_TMR_RATE;
+ else
+ EV_ERR(EINVAL);
+
+ return (result);
+}
+
+int
+evResetTimer(evContext opaqueCtx,
+ evTimerID id,
+ evTimerFunc func,
+ void *uap,
+ struct timespec due,
+ struct timespec inter
+) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evTimer *timer = id.opaque;
+ struct timespec old_due;
+ int result=0;
+
+ if (heap_element(ctx->timers, timer->index) != timer)
+ EV_ERR(ENOENT);
+
+#ifdef __hpux
+ /*
+ * tv_sec and tv_nsec are unsigned.
+ */
+ if (due.tv_nsec >= BILLION)
+ EV_ERR(EINVAL);
+
+ if (inter.tv_nsec >= BILLION)
+ EV_ERR(EINVAL);
+#else
+ if (due.tv_sec < 0 || due.tv_nsec < 0 || due.tv_nsec >= BILLION)
+ EV_ERR(EINVAL);
+
+ if (inter.tv_sec < 0 || inter.tv_nsec < 0 || inter.tv_nsec >= BILLION)
+ EV_ERR(EINVAL);
+#endif
+
+ old_due = timer->due;
+
+ timer->func = func;
+ timer->uap = uap;
+ timer->due = due;
+ timer->inter = inter;
+
+ switch (evCmpTime(due, old_due)) {
+ case -1:
+ result = heap_increased(ctx->timers, timer->index);
+ break;
+ case 0:
+ result = 0;
+ break;
+ case 1:
+ result = heap_decreased(ctx->timers, timer->index);
+ break;
+ }
+
+ if (ctx->debug > 7) {
+ evPrintf(ctx, 7, "timers after evResetTimer:\n");
+ (void) heap_for_each(ctx->timers, print_timer, (void *)ctx);
+ }
+
+ return (result);
+}
+
+int
+evSetIdleTimer(evContext opaqueCtx,
+ evTimerFunc func,
+ void *uap,
+ struct timespec max_idle,
+ evTimerID *opaqueID
+) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ idle_timer *tt;
+
+ /* Allocate and fill. */
+ OKNEW(tt);
+ tt->func = func;
+ tt->uap = uap;
+ tt->lastTouched = ctx->lastEventTime;
+ tt->max_idle = max_idle;
+
+ if (evSetTimer(opaqueCtx, idle_timeout, tt,
+ evAddTime(ctx->lastEventTime, max_idle),
+ max_idle, opaqueID) < 0) {
+ FREE(tt);
+ return (-1);
+ }
+
+ tt->timer = opaqueID->opaque;
+
+ return (0);
+}
+
+int
+evClearIdleTimer(evContext opaqueCtx, evTimerID id) {
+ evTimer *del = id.opaque;
+ idle_timer *tt = del->uap;
+
+ FREE(tt);
+ return (evClearTimer(opaqueCtx, id));
+}
+
+int
+evResetIdleTimer(evContext opaqueCtx,
+ evTimerID opaqueID,
+ evTimerFunc func,
+ void *uap,
+ struct timespec max_idle
+) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evTimer *timer = opaqueID.opaque;
+ idle_timer *tt = timer->uap;
+
+ tt->func = func;
+ tt->uap = uap;
+ tt->lastTouched = ctx->lastEventTime;
+ tt->max_idle = max_idle;
+
+ return (evResetTimer(opaqueCtx, opaqueID, idle_timeout, tt,
+ evAddTime(ctx->lastEventTime, max_idle),
+ max_idle));
+}
+
+int
+evTouchIdleTimer(evContext opaqueCtx, evTimerID id) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evTimer *t = id.opaque;
+ idle_timer *tt = t->uap;
+
+ tt->lastTouched = ctx->lastEventTime;
+
+ return (0);
+}
+
+/* Public to the rest of eventlib. */
+
+heap_context
+evCreateTimers(const evContext_p *ctx) {
+
+ UNUSED(ctx);
+
+ return (heap_new(due_sooner, set_index, 2048));
+}
+
+void
+evDestroyTimers(const evContext_p *ctx) {
+ (void) heap_for_each(ctx->timers, free_timer, NULL);
+ (void) heap_free(ctx->timers);
+}
+
+/* Private. */
+
+static int
+due_sooner(void *a, void *b) {
+ evTimer *a_timer, *b_timer;
+
+ a_timer = a;
+ b_timer = b;
+ return (evCmpTime(a_timer->due, b_timer->due) < 0);
+}
+
+static void
+set_index(void *what, int index) {
+ evTimer *timer;
+
+ timer = what;
+ timer->index = index;
+}
+
+static void
+free_timer(void *what, void *uap) {
+ evTimer *t = what;
+
+ UNUSED(uap);
+
+ FREE(t);
+}
+
+static void
+print_timer(void *what, void *uap) {
+ evTimer *cur = what;
+ evContext_p *ctx = uap;
+
+ cur = what;
+ evPrintf(ctx, 7,
+ " func %p, uap %p, due %ld.%09ld, inter %ld.%09ld\n",
+ cur->func, cur->uap,
+ (long)cur->due.tv_sec, cur->due.tv_nsec,
+ (long)cur->inter.tv_sec, cur->inter.tv_nsec);
+}
+
+static void
+idle_timeout(evContext opaqueCtx,
+ void *uap,
+ struct timespec due,
+ struct timespec inter
+) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ idle_timer *this = uap;
+ struct timespec idle;
+
+ UNUSED(due);
+ UNUSED(inter);
+
+ idle = evSubTime(ctx->lastEventTime, this->lastTouched);
+ if (evCmpTime(idle, this->max_idle) >= 0) {
+ (this->func)(opaqueCtx, this->uap, this->timer->due,
+ this->max_idle);
+ /*
+ * Setting the interval to zero will cause the timer to
+ * be cleaned up in evDrop().
+ */
+ this->timer->inter = evConsTime(0, 0);
+ FREE(this);
+ } else {
+ /* evDrop() will reschedule the timer. */
+ this->timer->inter = evSubTime(this->max_idle, idle);
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/ev_waits.c b/usr/src/lib/libresolv2_joy/common/isc/ev_waits.c
new file mode 100644
index 0000000000..9c4e819eb8
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/ev_waits.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* ev_waits.c - implement deferred function calls for the eventlib
+ * vix 05dec95 [initial]
+ */
+
+#include "port_before.h"
+#include "fd_setsize.h"
+
+#include <errno.h>
+
+#include <isc/eventlib.h>
+#include <isc/assertions.h>
+#include "eventlib_p.h"
+
+#include "port_after.h"
+
+/* Forward. */
+
+static void print_waits(evContext_p *ctx);
+static evWaitList * evNewWaitList(evContext_p *);
+static void evFreeWaitList(evContext_p *, evWaitList *);
+static evWaitList * evGetWaitList(evContext_p *, const void *, int);
+
+
+/* Public. */
+
+/*%
+ * Enter a new wait function on the queue.
+ */
+int
+evWaitFor(evContext opaqueCtx, const void *tag,
+ evWaitFunc func, void *uap, evWaitID *id)
+{
+ evContext_p *ctx = opaqueCtx.opaque;
+ evWait *new;
+ evWaitList *wl = evGetWaitList(ctx, tag, 1);
+
+ OKNEW(new);
+ new->func = func;
+ new->uap = uap;
+ new->tag = tag;
+ new->next = NULL;
+ if (wl->last != NULL)
+ wl->last->next = new;
+ else
+ wl->first = new;
+ wl->last = new;
+ if (id != NULL)
+ id->opaque = new;
+ if (ctx->debug >= 9)
+ print_waits(ctx);
+ return (0);
+}
+
+/*%
+ * Mark runnable all waiting functions having a certain tag.
+ */
+int
+evDo(evContext opaqueCtx, const void *tag) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evWaitList *wl = evGetWaitList(ctx, tag, 0);
+ evWait *first;
+
+ if (!wl) {
+ errno = ENOENT;
+ return (-1);
+ }
+
+ first = wl->first;
+ INSIST(first != NULL);
+
+ if (ctx->waitDone.last != NULL)
+ ctx->waitDone.last->next = first;
+ else
+ ctx->waitDone.first = first;
+ ctx->waitDone.last = wl->last;
+ evFreeWaitList(ctx, wl);
+
+ return (0);
+}
+
+/*%
+ * Remove a waiting (or ready to run) function from the queue.
+ */
+int
+evUnwait(evContext opaqueCtx, evWaitID id) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evWait *this, *prev;
+ evWaitList *wl;
+ int found = 0;
+
+ this = id.opaque;
+ INSIST(this != NULL);
+ wl = evGetWaitList(ctx, this->tag, 0);
+ if (wl != NULL) {
+ for (prev = NULL, this = wl->first;
+ this != NULL;
+ prev = this, this = this->next)
+ if (this == (evWait *)id.opaque) {
+ found = 1;
+ if (prev != NULL)
+ prev->next = this->next;
+ else
+ wl->first = this->next;
+ if (wl->last == this)
+ wl->last = prev;
+ if (wl->first == NULL)
+ evFreeWaitList(ctx, wl);
+ break;
+ }
+ }
+
+ if (!found) {
+ /* Maybe it's done */
+ for (prev = NULL, this = ctx->waitDone.first;
+ this != NULL;
+ prev = this, this = this->next)
+ if (this == (evWait *)id.opaque) {
+ found = 1;
+ if (prev != NULL)
+ prev->next = this->next;
+ else
+ ctx->waitDone.first = this->next;
+ if (ctx->waitDone.last == this)
+ ctx->waitDone.last = prev;
+ break;
+ }
+ }
+
+ if (!found) {
+ errno = ENOENT;
+ return (-1);
+ }
+
+ FREE(this);
+
+ if (ctx->debug >= 9)
+ print_waits(ctx);
+
+ return (0);
+}
+
+int
+evDefer(evContext opaqueCtx, evWaitFunc func, void *uap) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evWait *new;
+
+ OKNEW(new);
+ new->func = func;
+ new->uap = uap;
+ new->tag = NULL;
+ new->next = NULL;
+ if (ctx->waitDone.last != NULL)
+ ctx->waitDone.last->next = new;
+ else
+ ctx->waitDone.first = new;
+ ctx->waitDone.last = new;
+ if (ctx->debug >= 9)
+ print_waits(ctx);
+ return (0);
+}
+
+/* Private. */
+
+static void
+print_waits(evContext_p *ctx) {
+ evWaitList *wl;
+ evWait *this;
+
+ evPrintf(ctx, 9, "wait waiting:\n");
+ for (wl = ctx->waitLists; wl != NULL; wl = wl->next) {
+ INSIST(wl->first != NULL);
+ evPrintf(ctx, 9, " tag %p:", wl->first->tag);
+ for (this = wl->first; this != NULL; this = this->next)
+ evPrintf(ctx, 9, " %p", this);
+ evPrintf(ctx, 9, "\n");
+ }
+ evPrintf(ctx, 9, "wait done:");
+ for (this = ctx->waitDone.first; this != NULL; this = this->next)
+ evPrintf(ctx, 9, " %p", this);
+ evPrintf(ctx, 9, "\n");
+}
+
+static evWaitList *
+evNewWaitList(evContext_p *ctx) {
+ evWaitList *new;
+
+ NEW(new);
+ if (new == NULL)
+ return (NULL);
+ new->first = new->last = NULL;
+ new->prev = NULL;
+ new->next = ctx->waitLists;
+ if (new->next != NULL)
+ new->next->prev = new;
+ ctx->waitLists = new;
+ return (new);
+}
+
+static void
+evFreeWaitList(evContext_p *ctx, evWaitList *this) {
+
+ INSIST(this != NULL);
+
+ if (this->prev != NULL)
+ this->prev->next = this->next;
+ else
+ ctx->waitLists = this->next;
+ if (this->next != NULL)
+ this->next->prev = this->prev;
+ FREE(this);
+}
+
+static evWaitList *
+evGetWaitList(evContext_p *ctx, const void *tag, int should_create) {
+ evWaitList *this;
+
+ for (this = ctx->waitLists; this != NULL; this = this->next) {
+ if (this->first != NULL && this->first->tag == tag)
+ break;
+ }
+ if (this == NULL && should_create)
+ this = evNewWaitList(ctx);
+ return (this);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/eventlib.c b/usr/src/lib/libresolv2_joy/common/isc/eventlib.c
new file mode 100644
index 0000000000..f25c2098ab
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/eventlib.c
@@ -0,0 +1,932 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-1999 by Internet Software Consortium
+ * Copyright 2018 Joyent, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* eventlib.c - implement glue for the eventlib
+ * vix 09sep95 [initial]
+ */
+
+#include "port_before.h"
+#include "fd_setsize.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#ifdef SOLARIS2
+#include <limits.h>
+#endif /* SOLARIS2 */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <isc/eventlib.h>
+#include <isc/assertions.h>
+#include "eventlib_p.h"
+
+#include "port_after.h"
+
+int __evOptMonoTime;
+
+#ifdef USE_POLL
+#define pselect Pselect
+#endif /* USE_POLL */
+
+/* Forward. */
+
+#if defined(NEED_PSELECT) || defined(USE_POLL)
+static int pselect(int, void *, void *, void *,
+ struct timespec *,
+ const sigset_t *);
+#endif
+
+int __evOptMonoTime;
+
+/* Public. */
+
+int
+evCreate(evContext *opaqueCtx) {
+ evContext_p *ctx;
+
+ /* Make sure the memory heap is initialized. */
+ if (meminit(0, 0) < 0 && errno != EEXIST)
+ return (-1);
+
+ OKNEW(ctx);
+
+ /* Global. */
+ ctx->cur = NULL;
+
+ /* Debugging. */
+ ctx->debug = 0;
+ ctx->output = NULL;
+
+ /* Connections. */
+ ctx->conns = NULL;
+ INIT_LIST(ctx->accepts);
+
+ /* Files. */
+ ctx->files = NULL;
+#ifdef USE_POLL
+ ctx->pollfds = NULL;
+ ctx->maxnfds = 0;
+ ctx->firstfd = 0;
+ emulMaskInit(ctx, rdLast, EV_READ, 1);
+ emulMaskInit(ctx, rdNext, EV_READ, 0);
+ emulMaskInit(ctx, wrLast, EV_WRITE, 1);
+ emulMaskInit(ctx, wrNext, EV_WRITE, 0);
+ emulMaskInit(ctx, exLast, EV_EXCEPT, 1);
+ emulMaskInit(ctx, exNext, EV_EXCEPT, 0);
+ emulMaskInit(ctx, nonblockBefore, EV_WASNONBLOCKING, 0);
+#endif /* USE_POLL */
+ FD_ZERO(&ctx->rdNext);
+ FD_ZERO(&ctx->wrNext);
+ FD_ZERO(&ctx->exNext);
+ FD_ZERO(&ctx->nonblockBefore);
+ ctx->fdMax = -1;
+ ctx->fdNext = NULL;
+ ctx->fdCount = 0; /*%< Invalidate {rd,wr,ex}Last. */
+#ifndef USE_POLL
+ ctx->highestFD = FD_SETSIZE - 1;
+ memset(ctx->fdTable, 0, sizeof ctx->fdTable);
+#else
+ ctx->highestFD = INT_MAX / sizeof(struct pollfd);
+ ctx->fdTable = NULL;
+#endif /* USE_POLL */
+#ifdef EVENTLIB_TIME_CHECKS
+ ctx->lastFdCount = 0;
+#endif
+
+ /* Streams. */
+ ctx->streams = NULL;
+ ctx->strDone = NULL;
+ ctx->strLast = NULL;
+
+ /* Timers. */
+ ctx->lastEventTime = evNowTime();
+#ifdef EVENTLIB_TIME_CHECKS
+ ctx->lastSelectTime = ctx->lastEventTime;
+#endif
+ ctx->timers = evCreateTimers(ctx);
+ if (ctx->timers == NULL)
+ return (-1);
+
+ /* Waits. */
+ ctx->waitLists = NULL;
+ ctx->waitDone.first = ctx->waitDone.last = NULL;
+ ctx->waitDone.prev = ctx->waitDone.next = NULL;
+
+ opaqueCtx->opaque = ctx;
+ return (0);
+}
+
+void
+evSetDebug(evContext opaqueCtx, int level, FILE *output) {
+ evContext_p *ctx = opaqueCtx.opaque;
+
+ ctx->debug = level;
+ ctx->output = output;
+}
+
+int
+evDestroy(evContext opaqueCtx) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ int revs = 424242; /*%< Doug Adams. */
+ evWaitList *this_wl, *next_wl;
+ evWait *this_wait, *next_wait;
+
+ /* Connections. */
+ while (revs-- > 0 && ctx->conns != NULL) {
+ evConnID id;
+
+ id.opaque = ctx->conns;
+ (void) evCancelConn(opaqueCtx, id);
+ }
+ INSIST(revs >= 0);
+
+ /* Streams. */
+ while (revs-- > 0 && ctx->streams != NULL) {
+ evStreamID id;
+
+ id.opaque = ctx->streams;
+ (void) evCancelRW(opaqueCtx, id);
+ }
+
+ /* Files. */
+ while (revs-- > 0 && ctx->files != NULL) {
+ evFileID id;
+
+ id.opaque = ctx->files;
+ (void) evDeselectFD(opaqueCtx, id);
+ }
+ INSIST(revs >= 0);
+
+ /* Timers. */
+ evDestroyTimers(ctx);
+
+ /* Waits. */
+ for (this_wl = ctx->waitLists;
+ revs-- > 0 && this_wl != NULL;
+ this_wl = next_wl) {
+ next_wl = this_wl->next;
+ for (this_wait = this_wl->first;
+ revs-- > 0 && this_wait != NULL;
+ this_wait = next_wait) {
+ next_wait = this_wait->next;
+ FREE(this_wait);
+ }
+ FREE(this_wl);
+ }
+ for (this_wait = ctx->waitDone.first;
+ revs-- > 0 && this_wait != NULL;
+ this_wait = next_wait) {
+ next_wait = this_wait->next;
+ FREE(this_wait);
+ }
+
+ FREE(ctx);
+ return (0);
+}
+
+int
+evGetNext(evContext opaqueCtx, evEvent *opaqueEv, int options) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ struct timespec nextTime;
+ evTimer *nextTimer;
+ evEvent_p *new;
+ int x, pselect_errno, timerPast;
+#ifdef EVENTLIB_TIME_CHECKS
+ struct timespec interval;
+#endif
+
+ /* Ensure that exactly one of EV_POLL or EV_WAIT was specified. */
+ x = ((options & EV_POLL) != 0) + ((options & EV_WAIT) != 0);
+ if (x != 1)
+ EV_ERR(EINVAL);
+
+ /* Get the time of day. We'll do this again after select() blocks. */
+ ctx->lastEventTime = evNowTime();
+
+ again:
+ /* Finished accept()'s do not require a select(). */
+ if (!EMPTY(ctx->accepts)) {
+ OKNEW(new);
+ new->type = Accept;
+ new->u.accept.this = HEAD(ctx->accepts);
+ UNLINK(ctx->accepts, HEAD(ctx->accepts), link);
+ opaqueEv->opaque = new;
+ return (0);
+ }
+
+ /* Stream IO does not require a select(). */
+ if (ctx->strDone != NULL) {
+ OKNEW(new);
+ new->type = Stream;
+ new->u.stream.this = ctx->strDone;
+ ctx->strDone = ctx->strDone->nextDone;
+ if (ctx->strDone == NULL)
+ ctx->strLast = NULL;
+ opaqueEv->opaque = new;
+ return (0);
+ }
+
+ /* Waits do not require a select(). */
+ if (ctx->waitDone.first != NULL) {
+ OKNEW(new);
+ new->type = Wait;
+ new->u.wait.this = ctx->waitDone.first;
+ ctx->waitDone.first = ctx->waitDone.first->next;
+ if (ctx->waitDone.first == NULL)
+ ctx->waitDone.last = NULL;
+ opaqueEv->opaque = new;
+ return (0);
+ }
+
+ /* Get the status and content of the next timer. */
+ if ((nextTimer = heap_element(ctx->timers, 1)) != NULL) {
+ nextTime = nextTimer->due;
+ timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0);
+ } else
+ timerPast = 0; /*%< Make gcc happy. */
+ evPrintf(ctx, 9, "evGetNext: fdCount %d\n", ctx->fdCount);
+ if (ctx->fdCount == 0) {
+ static const struct timespec NoTime = {0, 0L};
+ enum { JustPoll, Block, Timer } m;
+ struct timespec t, *tp;
+
+ /* Are there any events at all? */
+ if ((options & EV_WAIT) != 0 && !nextTimer && ctx->fdMax == -1)
+ EV_ERR(ENOENT);
+
+ /* Figure out what select()'s timeout parameter should be. */
+ if ((options & EV_POLL) != 0) {
+ m = JustPoll;
+ t = NoTime;
+ tp = &t;
+ } else if (nextTimer == NULL) {
+ m = Block;
+ /* ``t'' unused. */
+ tp = NULL;
+ } else if (timerPast) {
+ m = JustPoll;
+ t = NoTime;
+ tp = &t;
+ } else {
+ m = Timer;
+ /* ``t'' filled in later. */
+ tp = &t;
+ }
+#ifdef EVENTLIB_TIME_CHECKS
+ if (ctx->debug > 0) {
+ interval = evSubTime(ctx->lastEventTime,
+ ctx->lastSelectTime);
+ if (interval.tv_sec > 0 || interval.tv_nsec > 0)
+ evPrintf(ctx, 1,
+ "time between pselect() %u.%09u count %d\n",
+ interval.tv_sec, interval.tv_nsec,
+ ctx->lastFdCount);
+ }
+#endif
+ do {
+#ifndef USE_POLL
+ /* XXX need to copy only the bits we are using. */
+ ctx->rdLast = ctx->rdNext;
+ ctx->wrLast = ctx->wrNext;
+ ctx->exLast = ctx->exNext;
+#else
+ /*
+ * The pollfd structure uses separate fields for
+ * the input and output events (corresponding to
+ * the ??Next and ??Last fd sets), so there's no
+ * need to copy one to the other.
+ */
+#endif /* USE_POLL */
+ if (m == Timer) {
+ INSIST(tp == &t);
+ t = evSubTime(nextTime, ctx->lastEventTime);
+ }
+
+ /* XXX should predict system's earliness and adjust. */
+ x = pselect(ctx->fdMax+1,
+ &ctx->rdLast, &ctx->wrLast, &ctx->exLast,
+ tp, NULL);
+ pselect_errno = errno;
+
+#ifndef USE_POLL
+ evPrintf(ctx, 4, "select() returns %d (err: %s)\n",
+ x, (x == -1) ? strerror(errno) : "none");
+#else
+ evPrintf(ctx, 4, "poll() returns %d (err: %s)\n",
+ x, (x == -1) ? strerror(errno) : "none");
+#endif /* USE_POLL */
+ /* Anything but a poll can change the time. */
+ if (m != JustPoll)
+ ctx->lastEventTime = evNowTime();
+
+ /* Select() likes to finish about 10ms early. */
+ } while (x == 0 && m == Timer &&
+ evCmpTime(ctx->lastEventTime, nextTime) < 0);
+#ifdef EVENTLIB_TIME_CHECKS
+ ctx->lastSelectTime = ctx->lastEventTime;
+#endif
+ if (x < 0) {
+ if (pselect_errno == EINTR) {
+ if ((options & EV_NULL) != 0)
+ goto again;
+ OKNEW(new);
+ new->type = Null;
+ /* No data. */
+ opaqueEv->opaque = new;
+ return (0);
+ }
+ if (pselect_errno == EBADF) {
+ for (x = 0; x <= ctx->fdMax; x++) {
+ struct stat sb;
+
+ if (FD_ISSET(x, &ctx->rdNext) == 0 &&
+ FD_ISSET(x, &ctx->wrNext) == 0 &&
+ FD_ISSET(x, &ctx->exNext) == 0)
+ continue;
+ if (fstat(x, &sb) == -1 &&
+ errno == EBADF)
+ evPrintf(ctx, 1, "EBADF: %d\n",
+ x);
+ }
+ abort();
+ }
+ EV_ERR(pselect_errno);
+ }
+ if (x == 0 && (nextTimer == NULL || !timerPast) &&
+ (options & EV_POLL))
+ EV_ERR(EWOULDBLOCK);
+ ctx->fdCount = x;
+#ifdef EVENTLIB_TIME_CHECKS
+ ctx->lastFdCount = x;
+#endif
+ }
+ INSIST(nextTimer || ctx->fdCount);
+
+ /* Timers go first since we'd like them to be accurate. */
+ if (nextTimer && !timerPast) {
+ /* Has anything happened since we blocked? */
+ timerPast = (evCmpTime(nextTime, ctx->lastEventTime) <= 0);
+ }
+ if (nextTimer && timerPast) {
+ OKNEW(new);
+ new->type = Timer;
+ new->u.timer.this = nextTimer;
+ opaqueEv->opaque = new;
+ return (0);
+ }
+
+ /* No timers, so there should be a ready file descriptor. */
+ x = 0;
+ while (ctx->fdCount > 0) {
+ evFile *fid;
+ int fd, eventmask;
+
+ if (ctx->fdNext == NULL) {
+ if (++x == 2) {
+ /*
+ * Hitting the end twice means that the last
+ * select() found some FD's which have since
+ * been deselected.
+ *
+ * On some systems, the count returned by
+ * selects is the total number of bits in
+ * all masks that are set, and on others it's
+ * the number of fd's that have some bit set,
+ * and on others, it's just broken. We
+ * always assume that it's the number of
+ * bits set in all masks, because that's what
+ * the man page says it should do, and
+ * the worst that can happen is we do an
+ * extra select().
+ */
+ ctx->fdCount = 0;
+ break;
+ }
+ ctx->fdNext = ctx->files;
+ }
+ fid = ctx->fdNext;
+ ctx->fdNext = fid->next;
+
+ fd = fid->fd;
+ eventmask = 0;
+ if (FD_ISSET(fd, &ctx->rdLast))
+ eventmask |= EV_READ;
+ if (FD_ISSET(fd, &ctx->wrLast))
+ eventmask |= EV_WRITE;
+ if (FD_ISSET(fd, &ctx->exLast))
+ eventmask |= EV_EXCEPT;
+ eventmask &= fid->eventmask;
+ if (eventmask != 0) {
+ if ((eventmask & EV_READ) != 0) {
+ FD_CLR(fd, &ctx->rdLast);
+ ctx->fdCount--;
+ }
+ if ((eventmask & EV_WRITE) != 0) {
+ FD_CLR(fd, &ctx->wrLast);
+ ctx->fdCount--;
+ }
+ if ((eventmask & EV_EXCEPT) != 0) {
+ FD_CLR(fd, &ctx->exLast);
+ ctx->fdCount--;
+ }
+ OKNEW(new);
+ new->type = File;
+ new->u.file.this = fid;
+ new->u.file.eventmask = eventmask;
+ opaqueEv->opaque = new;
+ return (0);
+ }
+ }
+ if (ctx->fdCount < 0) {
+ /*
+ * select()'s count is off on a number of systems, and
+ * can result in fdCount < 0.
+ */
+ evPrintf(ctx, 4, "fdCount < 0 (%d)\n", ctx->fdCount);
+ ctx->fdCount = 0;
+ }
+
+ /* We get here if the caller deselect()'s an FD. Gag me with a goto. */
+ goto again;
+}
+
+int
+evDispatch(evContext opaqueCtx, evEvent opaqueEv) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evEvent_p *ev = opaqueEv.opaque;
+#ifdef EVENTLIB_TIME_CHECKS
+ void *func;
+ struct timespec start_time;
+ struct timespec interval;
+#endif
+
+#ifdef EVENTLIB_TIME_CHECKS
+ if (ctx->debug > 0)
+ start_time = evNowTime();
+#endif
+ ctx->cur = ev;
+ switch (ev->type) {
+ case Accept: {
+ evAccept *this = ev->u.accept.this;
+
+ evPrintf(ctx, 5,
+ "Dispatch.Accept: fd %d -> %d, func %p, uap %p\n",
+ this->conn->fd, this->fd,
+ this->conn->func, this->conn->uap);
+ errno = this->ioErrno;
+ (this->conn->func)(opaqueCtx, this->conn->uap, this->fd,
+ &this->la, this->lalen,
+ &this->ra, this->ralen);
+#ifdef EVENTLIB_TIME_CHECKS
+ func = this->conn->func;
+#endif
+ break;
+ }
+ case File: {
+ evFile *this = ev->u.file.this;
+ int eventmask = ev->u.file.eventmask;
+
+ evPrintf(ctx, 5,
+ "Dispatch.File: fd %d, mask 0x%x, func %p, uap %p\n",
+ this->fd, this->eventmask, this->func, this->uap);
+ (this->func)(opaqueCtx, this->uap, this->fd, eventmask);
+#ifdef EVENTLIB_TIME_CHECKS
+ func = this->func;
+#endif
+ break;
+ }
+ case Stream: {
+ evStream *this = ev->u.stream.this;
+
+ evPrintf(ctx, 5,
+ "Dispatch.Stream: fd %d, func %p, uap %p\n",
+ this->fd, this->func, this->uap);
+ errno = this->ioErrno;
+ (this->func)(opaqueCtx, this->uap, this->fd, this->ioDone);
+#ifdef EVENTLIB_TIME_CHECKS
+ func = this->func;
+#endif
+ break;
+ }
+ case Timer: {
+ evTimer *this = ev->u.timer.this;
+
+ evPrintf(ctx, 5, "Dispatch.Timer: func %p, uap %p\n",
+ this->func, this->uap);
+ (this->func)(opaqueCtx, this->uap, this->due, this->inter);
+#ifdef EVENTLIB_TIME_CHECKS
+ func = this->func;
+#endif
+ break;
+ }
+ case Wait: {
+ evWait *this = ev->u.wait.this;
+
+ evPrintf(ctx, 5,
+ "Dispatch.Wait: tag %p, func %p, uap %p\n",
+ this->tag, this->func, this->uap);
+ (this->func)(opaqueCtx, this->uap, this->tag);
+#ifdef EVENTLIB_TIME_CHECKS
+ func = this->func;
+#endif
+ break;
+ }
+ case Null: {
+ /* No work. */
+#ifdef EVENTLIB_TIME_CHECKS
+ func = NULL;
+#endif
+ break;
+ }
+ default: {
+ abort();
+ }
+ }
+#ifdef EVENTLIB_TIME_CHECKS
+ if (ctx->debug > 0) {
+ interval = evSubTime(evNowTime(), start_time);
+ /*
+ * Complain if it took longer than 50 milliseconds.
+ *
+ * We call getuid() to make an easy to find mark in a kernel
+ * trace.
+ */
+ if (interval.tv_sec > 0 || interval.tv_nsec > 50000000)
+ evPrintf(ctx, 1,
+ "dispatch interval %u.%09u uid %d type %d func %p\n",
+ interval.tv_sec, interval.tv_nsec,
+ getuid(), ev->type, func);
+ }
+#endif
+ ctx->cur = NULL;
+ evDrop(opaqueCtx, opaqueEv);
+ return (0);
+}
+
+void
+evDrop(evContext opaqueCtx, evEvent opaqueEv) {
+ evContext_p *ctx = opaqueCtx.opaque;
+ evEvent_p *ev = opaqueEv.opaque;
+
+ switch (ev->type) {
+ case Accept: {
+ FREE(ev->u.accept.this);
+ break;
+ }
+ case File: {
+ /* No work. */
+ break;
+ }
+ case Stream: {
+ evStreamID id;
+
+ id.opaque = ev->u.stream.this;
+ (void) evCancelRW(opaqueCtx, id);
+ break;
+ }
+ case Timer: {
+ evTimer *this = ev->u.timer.this;
+ evTimerID opaque;
+
+ /* Check to see whether the user func cleared the timer. */
+ if (heap_element(ctx->timers, this->index) != this) {
+ evPrintf(ctx, 5, "Dispatch.Timer: timer rm'd?\n");
+ break;
+ }
+ /*
+ * Timer is still there. Delete it if it has expired,
+ * otherwise set it according to its next interval.
+ */
+ if (this->inter.tv_sec == (time_t)0 &&
+ this->inter.tv_nsec == 0L) {
+ opaque.opaque = this;
+ (void) evClearTimer(opaqueCtx, opaque);
+ } else {
+ opaque.opaque = this;
+ (void) evResetTimer(opaqueCtx, opaque, this->func,
+ this->uap,
+ evAddTime((this->mode & EV_TMR_RATE) ?
+ this->due :
+ ctx->lastEventTime,
+ this->inter),
+ this->inter);
+ }
+ break;
+ }
+ case Wait: {
+ FREE(ev->u.wait.this);
+ break;
+ }
+ case Null: {
+ /* No work. */
+ break;
+ }
+ default: {
+ abort();
+ }
+ }
+ FREE(ev);
+}
+
+int
+evMainLoop(evContext opaqueCtx) {
+ evEvent event;
+ int x;
+
+ while ((x = evGetNext(opaqueCtx, &event, EV_WAIT)) == 0)
+ if ((x = evDispatch(opaqueCtx, event)) < 0)
+ break;
+ return (x);
+}
+
+int
+evHighestFD(evContext opaqueCtx) {
+ evContext_p *ctx = opaqueCtx.opaque;
+
+ return (ctx->highestFD);
+}
+
+void
+evPrintf(const evContext_p *ctx, int level, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (ctx->output != NULL && ctx->debug >= level) {
+ vfprintf(ctx->output, fmt, ap);
+ fflush(ctx->output);
+ }
+ va_end(ap);
+}
+
+int
+evSetOption(evContext *opaqueCtx, const char *option, int value) {
+ /* evContext_p *ctx = opaqueCtx->opaque; */
+
+ UNUSED(opaqueCtx);
+ UNUSED(value);
+#ifndef CLOCK_MONOTONIC
+ UNUSED(option);
+#endif
+
+#ifdef CLOCK_MONOTONIC
+ if (strcmp(option, "monotime") == 0) {
+ if (opaqueCtx != NULL)
+ errno = EINVAL;
+ if (value == 0 || value == 1) {
+ __evOptMonoTime = value;
+ return (0);
+ } else {
+ errno = EINVAL;
+ return (-1);
+ }
+ }
+#endif
+ errno = ENOENT;
+ return (-1);
+}
+
+int
+evGetOption(evContext *opaqueCtx, const char *option, int *value) {
+ /* evContext_p *ctx = opaqueCtx->opaque; */
+
+ UNUSED(opaqueCtx);
+#ifndef CLOCK_MONOTONIC
+ UNUSED(value);
+ UNUSED(option);
+#endif
+
+#ifdef CLOCK_MONOTONIC
+ if (strcmp(option, "monotime") == 0) {
+ if (opaqueCtx != NULL)
+ errno = EINVAL;
+ *value = __evOptMonoTime;
+ return (0);
+ }
+#endif
+ errno = ENOENT;
+ return (-1);
+}
+
+#if defined(NEED_PSELECT) || defined(USE_POLL)
+/* XXX needs to move to the porting library. */
+static int
+pselect(int nfds, void *rfds, void *wfds, void *efds,
+ struct timespec *tsp,
+ const sigset_t *sigmask)
+{
+ struct timeval tv;
+ sigset_t sigs;
+ int n;
+#ifdef USE_POLL
+ int polltimeout = INFTIM;
+ evContext_p *ctx;
+ struct pollfd *fds;
+ nfds_t pnfds;
+
+ UNUSED(nfds);
+#else
+ struct timeval *tvp = NULL;
+#endif /* USE_POLL */
+
+ if (tsp) {
+ tv = evTimeVal(*tsp);
+#ifdef USE_POLL
+ polltimeout = 1000 * tv.tv_sec + tv.tv_usec / 1000;
+#else
+ tvp = &tv;
+#endif /* USE_POLL */
+ }
+ if (sigmask)
+ sigprocmask(SIG_SETMASK, sigmask, &sigs);
+#ifndef USE_POLL
+ n = select(nfds, rfds, wfds, efds, tvp);
+#else
+ /*
+ * rfds, wfds, and efds should all be from the same evContext_p,
+ * so any of them will do. If they're all NULL, the caller is
+ * presumably calling us to block.
+ */
+ if (rfds != NULL)
+ ctx = ((__evEmulMask *)rfds)->ctx;
+ else if (wfds != NULL)
+ ctx = ((__evEmulMask *)wfds)->ctx;
+ else if (efds != NULL)
+ ctx = ((__evEmulMask *)efds)->ctx;
+ else
+ ctx = NULL;
+ if (ctx != NULL && ctx->fdMax != -1) {
+ fds = &(ctx->pollfds[ctx->firstfd]);
+ pnfds = ctx->fdMax - ctx->firstfd + 1;
+ } else {
+ fds = NULL;
+ pnfds = 0;
+ }
+ n = poll(fds, pnfds, polltimeout);
+ if (n > 0) {
+ int i, e;
+
+ INSIST(ctx != NULL);
+ for (e = 0, i = ctx->firstfd; i <= ctx->fdMax; i++) {
+ if (ctx->pollfds[i].fd < 0)
+ continue;
+ if (FD_ISSET(i, &ctx->rdLast))
+ e++;
+ if (FD_ISSET(i, &ctx->wrLast))
+ e++;
+ if (FD_ISSET(i, &ctx->exLast))
+ e++;
+ }
+ n = e;
+ }
+#endif /* USE_POLL */
+ if (sigmask)
+ sigprocmask(SIG_SETMASK, &sigs, NULL);
+ if (tsp)
+ *tsp = evTimeSpec(tv);
+ return (n);
+}
+#endif
+
+#ifdef USE_POLL
+int
+evPollfdRealloc(evContext_p *ctx, int pollfd_chunk_size, int fd) {
+
+ int i, maxnfds;
+ void *pollfds, *fdTable;
+
+ if (fd < ctx->maxnfds)
+ return (0);
+
+ /* Don't allow ridiculously small values for pollfd_chunk_size */
+ if (pollfd_chunk_size < 20)
+ pollfd_chunk_size = 20;
+
+ maxnfds = (1 + (fd/pollfd_chunk_size)) * pollfd_chunk_size;
+
+ pollfds = realloc(ctx->pollfds, maxnfds * sizeof(*ctx->pollfds));
+ if (pollfds != NULL)
+ ctx->pollfds = pollfds;
+ fdTable = realloc(ctx->fdTable, maxnfds * sizeof(*ctx->fdTable));
+ if (fdTable != NULL)
+ ctx->fdTable = fdTable;
+
+ if (pollfds == NULL || fdTable == NULL) {
+ evPrintf(ctx, 2, "pollfd() realloc (%ld) failed\n",
+ (long)maxnfds*sizeof(struct pollfd));
+ return (-1);
+ }
+
+ for (i = ctx->maxnfds; i < maxnfds; i++) {
+ ctx->pollfds[i].fd = -1;
+ ctx->pollfds[i].events = 0;
+ ctx->fdTable[i] = 0;
+ }
+
+ ctx->maxnfds = maxnfds;
+
+ return (0);
+}
+
+/* Find the appropriate 'events' or 'revents' field in the pollfds array */
+short *
+__fd_eventfield(int fd, __evEmulMask *maskp) {
+
+ evContext_p *ctx = (evContext_p *)maskp->ctx;
+
+ if (!maskp->result || maskp->type == EV_WASNONBLOCKING)
+ return (&(ctx->pollfds[fd].events));
+ else
+ return (&(ctx->pollfds[fd].revents));
+}
+
+/* Translate to poll(2) event */
+short
+__poll_event(__evEmulMask *maskp) {
+
+ switch ((maskp)->type) {
+ case EV_READ:
+ return (POLLRDNORM);
+ case EV_WRITE:
+ return (POLLWRNORM);
+ case EV_EXCEPT:
+ return (POLLRDBAND | POLLPRI | POLLWRBAND);
+ case EV_WASNONBLOCKING:
+ return (POLLHUP);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Clear the events corresponding to the specified mask. If this leaves
+ * the events mask empty (apart from the POLLHUP bit), set the fd field
+ * to -1 so that poll(2) will ignore this fd.
+ */
+void
+__fd_clr(int fd, __evEmulMask *maskp) {
+
+ evContext_p *ctx = maskp->ctx;
+
+ *__fd_eventfield(fd, maskp) &= ~__poll_event(maskp);
+ if ((ctx->pollfds[fd].events & ~POLLHUP) == 0) {
+ ctx->pollfds[fd].fd = -1;
+ if (fd == ctx->fdMax)
+ while (ctx->fdMax > ctx->firstfd &&
+ ctx->pollfds[ctx->fdMax].fd < 0)
+ ctx->fdMax--;
+ if (fd == ctx->firstfd)
+ while (ctx->firstfd <= ctx->fdMax &&
+ ctx->pollfds[ctx->firstfd].fd < 0)
+ ctx->firstfd++;
+ /*
+ * Do we have a empty set of descriptors?
+ */
+ if (ctx->firstfd > ctx->fdMax) {
+ ctx->fdMax = -1;
+ ctx->firstfd = 0;
+ }
+ }
+}
+
+/*
+ * Set the events bit(s) corresponding to the specified mask. If the events
+ * field has any other bits than POLLHUP set, also set the fd field so that
+ * poll(2) will watch this fd.
+ */
+void
+__fd_set(int fd, __evEmulMask *maskp) {
+
+ evContext_p *ctx = maskp->ctx;
+
+ *__fd_eventfield(fd, maskp) |= __poll_event(maskp);
+ if ((ctx->pollfds[fd].events & ~POLLHUP) != 0) {
+ ctx->pollfds[fd].fd = fd;
+ if (fd < ctx->firstfd || ctx->fdMax == -1)
+ ctx->firstfd = fd;
+ if (fd > ctx->fdMax)
+ ctx->fdMax = fd;
+ }
+}
+#endif /* USE_POLL */
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/eventlib_p.h b/usr/src/lib/libresolv2_joy/common/isc/eventlib_p.h
new file mode 100644
index 0000000000..0a3614ab23
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/eventlib_p.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2005 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-1999 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*! \file
+ * \brief private interfaces for eventlib
+ * \author vix 09sep95 [initial]
+ *
+ * $Id: eventlib_p.h,v 1.9 2006/03/09 23:57:56 marka Exp $
+ */
+
+#ifndef _EVENTLIB_P_H
+#define _EVENTLIB_P_H
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+
+#define EVENTLIB_DEBUG 1
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/heap.h>
+#include <isc/list.h>
+#include <isc/memcluster.h>
+
+#define EV_MASK_ALL (EV_READ | EV_WRITE | EV_EXCEPT)
+#define EV_ERR(e) return (errno = (e), -1)
+#define OK(x) if ((x) < 0) EV_ERR(errno); else (void)NULL
+#define OKFREE(x, y) if ((x) < 0) { FREE((y)); EV_ERR(errno); } \
+ else (void)NULL
+
+#define NEW(p) if (((p) = memget(sizeof *(p))) != NULL) \
+ FILL(p); \
+ else \
+ (void)NULL;
+#define OKNEW(p) if (!((p) = memget(sizeof *(p)))) { \
+ errno = ENOMEM; \
+ return (-1); \
+ } else \
+ FILL(p)
+#define FREE(p) memput((p), sizeof *(p))
+
+#if EVENTLIB_DEBUG
+#define FILL(p) memset((p), 0xF5, sizeof *(p))
+#else
+#define FILL(p)
+#endif
+
+#ifdef USE_POLL
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+#include <poll.h>
+#endif /* USE_POLL */
+
+typedef struct evConn {
+ evConnFunc func;
+ void * uap;
+ int fd;
+ int flags;
+#define EV_CONN_LISTEN 0x0001 /*%< Connection is a listener. */
+#define EV_CONN_SELECTED 0x0002 /*%< evSelectFD(conn->file). */
+#define EV_CONN_BLOCK 0x0004 /*%< Listener fd was blocking. */
+ evFileID file;
+ struct evConn * prev;
+ struct evConn * next;
+} evConn;
+
+typedef struct evAccept {
+ int fd;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+#ifndef NO_SOCKADDR_UN
+ struct sockaddr_un un;
+#endif
+ } la;
+ ISC_SOCKLEN_T lalen;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+#ifndef NO_SOCKADDR_UN
+ struct sockaddr_un un;
+#endif
+ } ra;
+ ISC_SOCKLEN_T ralen;
+ int ioErrno;
+ evConn * conn;
+ LINK(struct evAccept) link;
+} evAccept;
+
+typedef struct evFile {
+ evFileFunc func;
+ void * uap;
+ int fd;
+ int eventmask;
+ int preemptive;
+ struct evFile * prev;
+ struct evFile * next;
+ struct evFile * fdprev;
+ struct evFile * fdnext;
+} evFile;
+
+typedef struct evStream {
+ evStreamFunc func;
+ void * uap;
+ evFileID file;
+ evTimerID timer;
+ int flags;
+#define EV_STR_TIMEROK 0x0001 /*%< IFF timer valid. */
+ int fd;
+ struct iovec * iovOrig;
+ int iovOrigCount;
+ struct iovec * iovCur;
+ int iovCurCount;
+ int ioTotal;
+ int ioDone;
+ int ioErrno;
+ struct evStream *prevDone, *nextDone;
+ struct evStream *prev, *next;
+} evStream;
+
+typedef struct evTimer {
+ evTimerFunc func;
+ void * uap;
+ struct timespec due, inter;
+ int index;
+ int mode;
+#define EV_TMR_RATE 1
+} evTimer;
+
+typedef struct evWait {
+ evWaitFunc func;
+ void * uap;
+ const void * tag;
+ struct evWait * next;
+} evWait;
+
+typedef struct evWaitList {
+ evWait * first;
+ evWait * last;
+ struct evWaitList * prev;
+ struct evWaitList * next;
+} evWaitList;
+
+typedef struct evEvent_p {
+ enum { Accept, File, Stream, Timer, Wait, Free, Null } type;
+ union {
+ struct { evAccept *this; } accept;
+ struct { evFile *this; int eventmask; } file;
+ struct { evStream *this; } stream;
+ struct { evTimer *this; } timer;
+ struct { evWait *this; } wait;
+ struct { struct evEvent_p *next; } free;
+ struct { const void *placeholder; } null;
+ } u;
+} evEvent_p;
+
+#ifdef USE_POLL
+typedef struct {
+ void *ctx; /* pointer to the evContext_p */
+ uint32_t type; /* READ, WRITE, EXCEPT, nonblk */
+ uint32_t result; /* 1 => revents, 0 => events */
+} __evEmulMask;
+
+#define emulMaskInit(ctx, field, ev, lastnext) \
+ ctx->field.ctx = ctx; \
+ ctx->field.type = ev; \
+ ctx->field.result = lastnext;
+
+extern short *__fd_eventfield(int fd, __evEmulMask *maskp);
+extern short __poll_event(__evEmulMask *maskp);
+extern void __fd_clr(int fd, __evEmulMask *maskp);
+extern void __fd_set(int fd, __evEmulMask *maskp);
+
+#undef FD_ZERO
+#define FD_ZERO(maskp)
+
+#undef FD_SET
+#define FD_SET(fd, maskp) \
+ __fd_set(fd, maskp)
+
+#undef FD_CLR
+#define FD_CLR(fd, maskp) \
+ __fd_clr(fd, maskp)
+
+#undef FD_ISSET
+#define FD_ISSET(fd, maskp) \
+ ((*__fd_eventfield(fd, maskp) & __poll_event(maskp)) != 0)
+
+#endif /* USE_POLL */
+
+typedef struct {
+ /* Global. */
+ const evEvent_p *cur;
+ /* Debugging. */
+ int debug;
+ FILE *output;
+ /* Connections. */
+ evConn *conns;
+ LIST(evAccept) accepts;
+ /* Files. */
+ evFile *files, *fdNext;
+#ifndef USE_POLL
+ fd_set rdLast, rdNext;
+ fd_set wrLast, wrNext;
+ fd_set exLast, exNext;
+ fd_set nonblockBefore;
+ int fdMax, fdCount, highestFD;
+ evFile *fdTable[FD_SETSIZE];
+#else
+ struct pollfd *pollfds; /* Allocated as needed */
+ evFile **fdTable; /* Ditto */
+ int maxnfds; /* # elements in above */
+ int firstfd; /* First active fd */
+ int fdMax; /* Last active fd */
+ int fdCount; /* # fd:s with I/O */
+ int highestFD; /* max fd allowed by OS */
+ __evEmulMask rdLast, rdNext;
+ __evEmulMask wrLast, wrNext;
+ __evEmulMask exLast, exNext;
+ __evEmulMask nonblockBefore;
+#endif /* USE_POLL */
+#ifdef EVENTLIB_TIME_CHECKS
+ struct timespec lastSelectTime;
+ int lastFdCount;
+#endif
+ /* Streams. */
+ evStream *streams;
+ evStream *strDone, *strLast;
+ /* Timers. */
+ struct timespec lastEventTime;
+ heap_context timers;
+ /* Waits. */
+ evWaitList *waitLists;
+ evWaitList waitDone;
+} evContext_p;
+
+/* eventlib.c */
+#define evPrintf __evPrintf
+void evPrintf(const evContext_p *ctx, int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+
+#ifdef USE_POLL
+extern int evPollfdRealloc(evContext_p *ctx, int pollfd_chunk_size, int fd);
+#endif /* USE_POLL */
+
+/* ev_timers.c */
+#define evCreateTimers __evCreateTimers
+heap_context evCreateTimers(const evContext_p *);
+#define evDestroyTimers __evDestroyTimers
+void evDestroyTimers(const evContext_p *);
+
+/* ev_waits.c */
+#define evFreeWait __evFreeWait
+evWait *evFreeWait(evContext_p *ctx, evWait *old);
+
+/* Global options */
+extern int __evOptMonoTime;
+
+#endif /*_EVENTLIB_P_H*/
diff --git a/usr/src/lib/libresolv2_joy/common/isc/heap.c b/usr/src/lib/libresolv2_joy/common/isc/heap.c
new file mode 100644
index 0000000000..3e48d244e2
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/heap.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1997,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*%
+ * Heap implementation of priority queues adapted from the following:
+ *
+ * _Introduction to Algorithms_, Cormen, Leiserson, and Rivest,
+ * MIT Press / McGraw Hill, 1990, ISBN 0-262-03141-8, chapter 7.
+ *
+ * _Algorithms_, Second Edition, Sedgewick, Addison-Wesley, 1988,
+ * ISBN 0-201-06673-4, chapter 11.
+ */
+
+#include "port_before.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "port_after.h"
+
+#include <isc/heap.h>
+
+/*%
+ * Note: to make heap_parent and heap_left easy to compute, the first
+ * element of the heap array is not used; i.e. heap subscripts are 1-based,
+ * not 0-based.
+ */
+#define heap_parent(i) ((i) >> 1)
+#define heap_left(i) ((i) << 1)
+
+#define ARRAY_SIZE_INCREMENT 512
+
+heap_context
+heap_new(heap_higher_priority_func higher_priority, heap_index_func index,
+ int array_size_increment) {
+ heap_context ctx;
+
+ if (higher_priority == NULL)
+ return (NULL);
+
+ ctx = (heap_context)malloc(sizeof (struct heap_context));
+ if (ctx == NULL)
+ return (NULL);
+
+ ctx->array_size = 0;
+ if (array_size_increment == 0)
+ ctx->array_size_increment = ARRAY_SIZE_INCREMENT;
+ else
+ ctx->array_size_increment = array_size_increment;
+ ctx->heap_size = 0;
+ ctx->heap = NULL;
+ ctx->higher_priority = higher_priority;
+ ctx->index = index;
+ return (ctx);
+}
+
+int
+heap_free(heap_context ctx) {
+ if (ctx == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (ctx->heap != NULL)
+ free(ctx->heap);
+ free(ctx);
+
+ return (0);
+}
+
+static int
+heap_resize(heap_context ctx) {
+ void **new_heap;
+
+ ctx->array_size += ctx->array_size_increment;
+ new_heap = (void **)realloc(ctx->heap,
+ (ctx->array_size) * (sizeof (void *)));
+ if (new_heap == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ ctx->heap = new_heap;
+ return (0);
+}
+
+static void
+float_up(heap_context ctx, int i, void *elt) {
+ int p;
+
+ for ( p = heap_parent(i);
+ i > 1 && ctx->higher_priority(elt, ctx->heap[p]);
+ i = p, p = heap_parent(i) ) {
+ ctx->heap[i] = ctx->heap[p];
+ if (ctx->index != NULL)
+ (ctx->index)(ctx->heap[i], i);
+ }
+ ctx->heap[i] = elt;
+ if (ctx->index != NULL)
+ (ctx->index)(ctx->heap[i], i);
+}
+
+static void
+sink_down(heap_context ctx, int i, void *elt) {
+ int j, size, half_size;
+
+ size = ctx->heap_size;
+ half_size = size / 2;
+ while (i <= half_size) {
+ /* find smallest of the (at most) two children */
+ j = heap_left(i);
+ if (j < size && ctx->higher_priority(ctx->heap[j+1],
+ ctx->heap[j]))
+ j++;
+ if (ctx->higher_priority(elt, ctx->heap[j]))
+ break;
+ ctx->heap[i] = ctx->heap[j];
+ if (ctx->index != NULL)
+ (ctx->index)(ctx->heap[i], i);
+ i = j;
+ }
+ ctx->heap[i] = elt;
+ if (ctx->index != NULL)
+ (ctx->index)(ctx->heap[i], i);
+}
+
+int
+heap_insert(heap_context ctx, void *elt) {
+ int i;
+
+ if (ctx == NULL || elt == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ i = ++ctx->heap_size;
+ if (ctx->heap_size >= ctx->array_size && heap_resize(ctx) < 0)
+ return (-1);
+
+ float_up(ctx, i, elt);
+
+ return (0);
+}
+
+int
+heap_delete(heap_context ctx, int i) {
+ void *elt;
+ int less;
+
+ if (ctx == NULL || i < 1 || i > ctx->heap_size) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (i == ctx->heap_size) {
+ ctx->heap_size--;
+ } else {
+ elt = ctx->heap[ctx->heap_size--];
+ less = ctx->higher_priority(elt, ctx->heap[i]);
+ ctx->heap[i] = elt;
+ if (less)
+ float_up(ctx, i, ctx->heap[i]);
+ else
+ sink_down(ctx, i, ctx->heap[i]);
+ }
+
+ return (0);
+}
+
+int
+heap_increased(heap_context ctx, int i) {
+ if (ctx == NULL || i < 1 || i > ctx->heap_size) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ float_up(ctx, i, ctx->heap[i]);
+
+ return (0);
+}
+
+int
+heap_decreased(heap_context ctx, int i) {
+ if (ctx == NULL || i < 1 || i > ctx->heap_size) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ sink_down(ctx, i, ctx->heap[i]);
+
+ return (0);
+}
+
+void *
+heap_element(heap_context ctx, int i) {
+ if (ctx == NULL || i < 1 || i > ctx->heap_size) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ return (ctx->heap[i]);
+}
+
+int
+heap_for_each(heap_context ctx, heap_for_each_func action, void *uap) {
+ int i;
+
+ if (ctx == NULL || action == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ for (i = 1; i <= ctx->heap_size; i++)
+ (action)(ctx->heap[i], uap);
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/hex.c b/usr/src/lib/libresolv2_joy/common/isc/hex.c
new file mode 100644
index 0000000000..e43be4f3b5
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/hex.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2001 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <port_before.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <isc/misc.h>
+#include <port_after.h>
+
+static const char hex[17] = "0123456789abcdef";
+
+int
+isc_gethexstring(unsigned char *buf, size_t len, int count, FILE *fp,
+ int *multiline)
+{
+ int c, n;
+ unsigned char x;
+ char *s;
+ int result = count;
+
+ x = 0; /*%< silence compiler */
+ n = 0;
+ while (count > 0) {
+ c = fgetc(fp);
+
+ if ((c == EOF) ||
+ (c == '\n' && !*multiline) ||
+ (c == '(' && *multiline) ||
+ (c == ')' && !*multiline))
+ goto formerr;
+ /* comment */
+ if (c == ';') {
+ do {
+ c = fgetc(fp);
+ } while (c != EOF && c != '\n');
+ if (c == '\n' && *multiline)
+ continue;
+ goto formerr;
+ }
+ /* white space */
+ if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+ continue;
+ /* multiline */
+ if ('(' == c || c == ')') {
+ *multiline = (c == '(' /*)*/);
+ continue;
+ }
+ if ((s = strchr(hex, tolower(c))) == NULL)
+ goto formerr;
+ x = (x<<4) | (s - hex);
+ if (++n == 2) {
+ if (len > 0U) {
+ *buf++ = x;
+ len--;
+ } else
+ result = -1;
+ count--;
+ n = 0;
+ }
+ }
+ return (result);
+
+ formerr:
+ if (c == '\n')
+ ungetc(c, fp);
+ return (-1);
+}
+
+void
+isc_puthexstring(FILE *fp, const unsigned char *buf, size_t buflen,
+ size_t len1, size_t len2, const char *sep)
+{
+ size_t i = 0;
+
+ if (len1 < 4U)
+ len1 = 4;
+ if (len2 < 4U)
+ len2 = 4;
+ while (buflen > 0U) {
+ fputc(hex[(buf[0]>>4)&0xf], fp);
+ fputc(hex[buf[0]&0xf], fp);
+ i += 2;
+ buflen--;
+ buf++;
+ if (i >= len1 && sep != NULL) {
+ fputs(sep, fp);
+ i = 0;
+ len1 = len2;
+ }
+ }
+}
+
+void
+isc_tohex(const unsigned char *buf, size_t buflen, char *t) {
+ while (buflen > 0U) {
+ *t++ = hex[(buf[0]>>4)&0xf];
+ *t++ = hex[buf[0]&0xf];
+ buf++;
+ buflen--;
+ }
+ *t = '\0';
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/logging.c b/usr/src/lib/libresolv2_joy/common/isc/logging.c
new file mode 100644
index 0000000000..ac4806b4f8
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/logging.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1996-1999, 2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/assertions.h>
+#include <isc/logging.h>
+#include <isc/memcluster.h>
+#include <isc/misc.h>
+
+#include "port_after.h"
+
+#include "logging_p.h"
+
+static const int syslog_priority[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE,
+ LOG_WARNING, LOG_ERR, LOG_CRIT };
+
+static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+static const char *level_text[] = {
+ "info: ", "notice: ", "warning: ", "error: ", "critical: "
+};
+
+static void
+version_rename(log_channel chan) {
+ unsigned int ver;
+ char old_name[PATH_MAX+1];
+ char new_name[PATH_MAX+1];
+
+ ver = chan->out.file.versions;
+ if (ver < 1)
+ return;
+ if (ver > LOG_MAX_VERSIONS)
+ ver = LOG_MAX_VERSIONS;
+ /*
+ * Need to have room for '.nn' (XXX assumes LOG_MAX_VERSIONS < 100)
+ */
+ if (strlen(chan->out.file.name) > (size_t)(PATH_MAX-3))
+ return;
+ for (ver--; ver > 0; ver--) {
+ sprintf(old_name, "%s.%d", chan->out.file.name, ver-1);
+ sprintf(new_name, "%s.%d", chan->out.file.name, ver);
+ (void)isc_movefile(old_name, new_name);
+ }
+ sprintf(new_name, "%s.0", chan->out.file.name);
+ (void)isc_movefile(chan->out.file.name, new_name);
+}
+
+FILE *
+log_open_stream(log_channel chan) {
+ FILE *stream;
+ int fd, flags;
+ struct stat sb;
+ int regular;
+
+ if (chan == NULL || chan->type != log_file) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /*
+ * Don't open already open streams
+ */
+ if (chan->out.file.stream != NULL)
+ return (chan->out.file.stream);
+
+ if (stat(chan->out.file.name, &sb) < 0) {
+ if (errno != ENOENT) {
+ syslog(LOG_ERR,
+ "log_open_stream: stat of %s failed: %s",
+ chan->out.file.name, strerror(errno));
+ chan->flags |= LOG_CHANNEL_BROKEN;
+ return (NULL);
+ }
+ regular = 1;
+ } else
+ regular = (sb.st_mode & S_IFREG);
+
+ if (chan->out.file.versions) {
+ if (!regular) {
+ syslog(LOG_ERR,
+ "log_open_stream: want versions but %s isn't a regular file",
+ chan->out.file.name);
+ chan->flags |= LOG_CHANNEL_BROKEN;
+ errno = EINVAL;
+ return (NULL);
+ }
+ }
+
+ flags = O_WRONLY|O_CREAT|O_APPEND;
+
+ if ((chan->flags & LOG_TRUNCATE) != 0) {
+ if (regular) {
+ (void)unlink(chan->out.file.name);
+ flags |= O_EXCL;
+ } else {
+ syslog(LOG_ERR,
+ "log_open_stream: want truncation but %s isn't a regular file",
+ chan->out.file.name);
+ chan->flags |= LOG_CHANNEL_BROKEN;
+ errno = EINVAL;
+ return (NULL);
+ }
+ }
+
+ fd = open(chan->out.file.name, flags,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+ if (fd < 0) {
+ syslog(LOG_ERR, "log_open_stream: open(%s) failed: %s",
+ chan->out.file.name, strerror(errno));
+ chan->flags |= LOG_CHANNEL_BROKEN;
+ return (NULL);
+ }
+ stream = fdopen(fd, "a");
+ if (stream == NULL) {
+ syslog(LOG_ERR, "log_open_stream: fdopen() failed");
+ chan->flags |= LOG_CHANNEL_BROKEN;
+ return (NULL);
+ }
+ (void) fchown(fd, chan->out.file.owner, chan->out.file.group);
+
+ chan->out.file.stream = stream;
+ return (stream);
+}
+
+int
+log_close_stream(log_channel chan) {
+ FILE *stream;
+
+ if (chan == NULL || chan->type != log_file) {
+ errno = EINVAL;
+ return (0);
+ }
+ stream = chan->out.file.stream;
+ chan->out.file.stream = NULL;
+ if (stream != NULL && fclose(stream) == EOF)
+ return (-1);
+ return (0);
+}
+
+void
+log_close_debug_channels(log_context lc) {
+ log_channel_list lcl;
+ int i;
+
+ for (i = 0; i < lc->num_categories; i++)
+ for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl->next)
+ if (lcl->channel->type == log_file &&
+ lcl->channel->out.file.stream != NULL &&
+ lcl->channel->flags & LOG_REQUIRE_DEBUG)
+ (void)log_close_stream(lcl->channel);
+}
+
+FILE *
+log_get_stream(log_channel chan) {
+ if (chan == NULL || chan->type != log_file) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ return (chan->out.file.stream);
+}
+
+char *
+log_get_filename(log_channel chan) {
+ if (chan == NULL || chan->type != log_file) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ return (chan->out.file.name);
+}
+
+int
+log_check_channel(log_context lc, int level, log_channel chan) {
+ int debugging, chan_level;
+
+ REQUIRE(lc != NULL);
+
+ debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0);
+
+ /*
+ * If not debugging, short circuit debugging messages very early.
+ */
+ if (level > 0 && !debugging)
+ return (0);
+
+ if ((chan->flags & (LOG_CHANNEL_BROKEN|LOG_CHANNEL_OFF)) != 0)
+ return (0);
+
+ /* Some channels only log when debugging is on. */
+ if ((chan->flags & LOG_REQUIRE_DEBUG) && !debugging)
+ return (0);
+
+ /* Some channels use the global level. */
+ if ((chan->flags & LOG_USE_CONTEXT_LEVEL) != 0) {
+ chan_level = lc->level;
+ } else
+ chan_level = chan->level;
+
+ if (level > chan_level)
+ return (0);
+
+ return (1);
+}
+
+int
+log_check(log_context lc, int category, int level) {
+ log_channel_list lcl;
+ int debugging;
+
+ REQUIRE(lc != NULL);
+
+ debugging = ((lc->flags & LOG_OPTION_DEBUG) != 0);
+
+ /*
+ * If not debugging, short circuit debugging messages very early.
+ */
+ if (level > 0 && !debugging)
+ return (0);
+
+ if (category < 0 || category > lc->num_categories)
+ category = 0; /*%< use default */
+ lcl = lc->categories[category];
+ if (lcl == NULL) {
+ category = 0;
+ lcl = lc->categories[0];
+ }
+
+ for ( /* nothing */; lcl != NULL; lcl = lcl->next) {
+ if (log_check_channel(lc, level, lcl->channel))
+ return (1);
+ }
+ return (0);
+}
+
+void
+log_vwrite(log_context lc, int category, int level, const char *format,
+ va_list args) {
+ log_channel_list lcl;
+ int pri, debugging, did_vsprintf = 0;
+ int original_category;
+ FILE *stream;
+ log_channel chan;
+ struct timeval tv;
+ struct tm *local_tm;
+#ifdef HAVE_TIME_R
+ struct tm tm_tmp;
+#endif
+ time_t tt;
+ const char *category_name;
+ const char *level_str;
+ char time_buf[256];
+ char level_buf[256];
+
+ REQUIRE(lc != NULL);
+
+ debugging = (lc->flags & LOG_OPTION_DEBUG);
+
+ /*
+ * If not debugging, short circuit debugging messages very early.
+ */
+ if (level > 0 && !debugging)
+ return;
+
+ if (category < 0 || category > lc->num_categories)
+ category = 0; /*%< use default */
+ original_category = category;
+ lcl = lc->categories[category];
+ if (lcl == NULL) {
+ category = 0;
+ lcl = lc->categories[0];
+ }
+
+ /*
+ * Get the current time and format it.
+ */
+ time_buf[0]='\0';
+ if (gettimeofday(&tv, NULL) < 0) {
+ syslog(LOG_INFO, "gettimeofday failed in log_vwrite()");
+ } else {
+ tt = tv.tv_sec;
+#ifdef HAVE_TIME_R
+ local_tm = localtime_r(&tt, &tm_tmp);
+#else
+ local_tm = localtime(&tt);
+#endif
+ if (local_tm != NULL) {
+ sprintf(time_buf, "%02d-%s-%4d %02d:%02d:%02d.%03ld ",
+ local_tm->tm_mday, months[local_tm->tm_mon],
+ local_tm->tm_year+1900, local_tm->tm_hour,
+ local_tm->tm_min, local_tm->tm_sec,
+ (long)tv.tv_usec/1000);
+ }
+ }
+
+ /*
+ * Make a string representation of the current category and level
+ */
+
+ if (lc->category_names != NULL &&
+ lc->category_names[original_category] != NULL)
+ category_name = lc->category_names[original_category];
+ else
+ category_name = "";
+
+ if (level >= log_critical) {
+ if (level >= 0) {
+ sprintf(level_buf, "debug %d: ", level);
+ level_str = level_buf;
+ } else
+ level_str = level_text[-level-1];
+ } else {
+ sprintf(level_buf, "level %d: ", level);
+ level_str = level_buf;
+ }
+
+ /*
+ * Write the message to channels.
+ */
+ for ( /* nothing */; lcl != NULL; lcl = lcl->next) {
+ chan = lcl->channel;
+
+ if (!log_check_channel(lc, level, chan))
+ continue;
+
+ if (!did_vsprintf) {
+ (void)vsprintf(lc->buffer, format, args);
+ if (strlen(lc->buffer) > (size_t)LOG_BUFFER_SIZE) {
+ syslog(LOG_CRIT,
+ "memory overrun in log_vwrite()");
+ exit(1);
+ }
+ did_vsprintf = 1;
+ }
+
+ switch (chan->type) {
+ case log_syslog:
+ if (level >= log_critical)
+ pri = (level >= 0) ? 0 : -level;
+ else
+ pri = -log_critical;
+ syslog(chan->out.facility|syslog_priority[pri],
+ "%s%s%s%s",
+ (chan->flags & LOG_TIMESTAMP) ? time_buf : "",
+ (chan->flags & LOG_PRINT_CATEGORY) ?
+ category_name : "",
+ (chan->flags & LOG_PRINT_LEVEL) ?
+ level_str : "",
+ lc->buffer);
+ break;
+ case log_file:
+ stream = chan->out.file.stream;
+ if (stream == NULL) {
+ stream = log_open_stream(chan);
+ if (stream == NULL)
+ break;
+ }
+ if (chan->out.file.max_size != ULONG_MAX) {
+ long pos;
+
+ pos = ftell(stream);
+ if (pos >= 0 &&
+ (unsigned long)pos >
+ chan->out.file.max_size) {
+ /*
+ * try to roll over the log files,
+ * ignoring all all return codes
+ * except the open (we don't want
+ * to write any more anyway)
+ */
+ log_close_stream(chan);
+ version_rename(chan);
+ stream = log_open_stream(chan);
+ if (stream == NULL)
+ break;
+ }
+ }
+ fprintf(stream, "%s%s%s%s\n",
+ (chan->flags & LOG_TIMESTAMP) ? time_buf : "",
+ (chan->flags & LOG_PRINT_CATEGORY) ?
+ category_name : "",
+ (chan->flags & LOG_PRINT_LEVEL) ?
+ level_str : "",
+ lc->buffer);
+ fflush(stream);
+ break;
+ case log_null:
+ break;
+ default:
+ syslog(LOG_ERR,
+ "unknown channel type in log_vwrite()");
+ }
+ }
+}
+
+void
+log_write(log_context lc, int category, int level, const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ log_vwrite(lc, category, level, format, args);
+ va_end(args);
+}
+
+/*%
+ * Functions to create, set, or destroy contexts
+ */
+
+int
+log_new_context(int num_categories, char **category_names, log_context *lc) {
+ log_context nlc;
+
+ nlc = memget(sizeof (struct log_context));
+ if (nlc == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ nlc->num_categories = num_categories;
+ nlc->category_names = category_names;
+ nlc->categories = memget(num_categories * sizeof (log_channel_list));
+ if (nlc->categories == NULL) {
+ memput(nlc, sizeof (struct log_context));
+ errno = ENOMEM;
+ return (-1);
+ }
+ memset(nlc->categories, '\0',
+ num_categories * sizeof (log_channel_list));
+ nlc->flags = 0U;
+ nlc->level = 0;
+ *lc = nlc;
+ return (0);
+}
+
+void
+log_free_context(log_context lc) {
+ log_channel_list lcl, lcl_next;
+ log_channel chan;
+ int i;
+
+ REQUIRE(lc != NULL);
+
+ for (i = 0; i < lc->num_categories; i++)
+ for (lcl = lc->categories[i]; lcl != NULL; lcl = lcl_next) {
+ lcl_next = lcl->next;
+ chan = lcl->channel;
+ (void)log_free_channel(chan);
+ memput(lcl, sizeof (struct log_channel_list));
+ }
+ memput(lc->categories,
+ lc->num_categories * sizeof (log_channel_list));
+ memput(lc, sizeof (struct log_context));
+}
+
+int
+log_add_channel(log_context lc, int category, log_channel chan) {
+ log_channel_list lcl;
+
+ if (lc == NULL || category < 0 || category >= lc->num_categories) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ lcl = memget(sizeof (struct log_channel_list));
+ if (lcl == NULL) {
+ errno = ENOMEM;
+ return(-1);
+ }
+ lcl->channel = chan;
+ lcl->next = lc->categories[category];
+ lc->categories[category] = lcl;
+ chan->references++;
+ return (0);
+}
+
+int
+log_remove_channel(log_context lc, int category, log_channel chan) {
+ log_channel_list lcl, prev_lcl, next_lcl;
+ int found = 0;
+
+ if (lc == NULL || category < 0 || category >= lc->num_categories) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ for (prev_lcl = NULL, lcl = lc->categories[category];
+ lcl != NULL;
+ lcl = next_lcl) {
+ next_lcl = lcl->next;
+ if (lcl->channel == chan) {
+ log_free_channel(chan);
+ if (prev_lcl != NULL)
+ prev_lcl->next = next_lcl;
+ else
+ lc->categories[category] = next_lcl;
+ memput(lcl, sizeof (struct log_channel_list));
+ /*
+ * We just set found instead of returning because
+ * the channel might be on the list more than once.
+ */
+ found = 1;
+ } else
+ prev_lcl = lcl;
+ }
+ if (!found) {
+ errno = ENOENT;
+ return (-1);
+ }
+ return (0);
+}
+
+int
+log_option(log_context lc, int option, int value) {
+ if (lc == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ switch (option) {
+ case LOG_OPTION_DEBUG:
+ if (value)
+ lc->flags |= option;
+ else
+ lc->flags &= ~option;
+ break;
+ case LOG_OPTION_LEVEL:
+ lc->level = value;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+ return (0);
+}
+
+int
+log_category_is_active(log_context lc, int category) {
+ if (lc == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ if (category >= 0 && category < lc->num_categories &&
+ lc->categories[category] != NULL)
+ return (1);
+ return (0);
+}
+
+log_channel
+log_new_syslog_channel(unsigned int flags, int level, int facility) {
+ log_channel chan;
+
+ chan = memget(sizeof (struct log_channel));
+ if (chan == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ chan->type = log_syslog;
+ chan->flags = flags;
+ chan->level = level;
+ chan->out.facility = facility;
+ chan->references = 0;
+ return (chan);
+}
+
+log_channel
+log_new_file_channel(unsigned int flags, int level,
+ const char *name, FILE *stream, unsigned int versions,
+ unsigned long max_size) {
+ log_channel chan;
+
+ chan = memget(sizeof (struct log_channel));
+ if (chan == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ chan->type = log_file;
+ chan->flags = flags;
+ chan->level = level;
+ if (name != NULL) {
+ size_t len;
+
+ len = strlen(name);
+ /*
+ * Quantize length to a multiple of 256. There's space for the
+ * NUL, since if len is a multiple of 256, the size chosen will
+ * be the next multiple.
+ */
+ chan->out.file.name_size = ((len / 256) + 1) * 256;
+ chan->out.file.name = memget(chan->out.file.name_size);
+ if (chan->out.file.name == NULL) {
+ memput(chan, sizeof (struct log_channel));
+ errno = ENOMEM;
+ return (NULL);
+ }
+ /* This is safe. */
+ strcpy(chan->out.file.name, name);
+ } else {
+ chan->out.file.name_size = 0;
+ chan->out.file.name = NULL;
+ }
+ chan->out.file.stream = stream;
+ chan->out.file.versions = versions;
+ chan->out.file.max_size = max_size;
+ chan->out.file.owner = getuid();
+ chan->out.file.group = getgid();
+ chan->references = 0;
+ return (chan);
+}
+
+int
+log_set_file_owner(log_channel chan, uid_t owner, gid_t group) {
+ if (chan->type != log_file) {
+ errno = EBADF;
+ return (-1);
+ }
+ chan->out.file.owner = owner;
+ chan->out.file.group = group;
+ return (0);
+}
+
+log_channel
+log_new_null_channel() {
+ log_channel chan;
+
+ chan = memget(sizeof (struct log_channel));
+ if (chan == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ chan->type = log_null;
+ chan->flags = LOG_CHANNEL_OFF;
+ chan->level = log_info;
+ chan->references = 0;
+ return (chan);
+}
+
+int
+log_inc_references(log_channel chan) {
+ if (chan == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+ chan->references++;
+ return (0);
+}
+
+int
+log_dec_references(log_channel chan) {
+ if (chan == NULL || chan->references <= 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ chan->references--;
+ return (0);
+}
+
+log_channel_type
+log_get_channel_type(log_channel chan) {
+ REQUIRE(chan != NULL);
+
+ return (chan->type);
+}
+
+int
+log_free_channel(log_channel chan) {
+ if (chan == NULL || chan->references <= 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ chan->references--;
+ if (chan->references == 0) {
+ if (chan->type == log_file) {
+ if ((chan->flags & LOG_CLOSE_STREAM) &&
+ chan->out.file.stream != NULL)
+ (void)fclose(chan->out.file.stream);
+ if (chan->out.file.name != NULL)
+ memput(chan->out.file.name,
+ chan->out.file.name_size);
+ }
+ memput(chan, sizeof (struct log_channel));
+ }
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/logging_p.h b/usr/src/lib/libresolv2_joy/common/isc/logging_p.h
new file mode 100644
index 0000000000..5e6314f190
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/logging_p.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LOGGING_P_H
+#define LOGGING_P_H
+
+typedef struct log_file_desc {
+ char *name;
+ size_t name_size;
+ FILE *stream;
+ unsigned int versions;
+ unsigned long max_size;
+ uid_t owner;
+ gid_t group;
+} log_file_desc;
+
+typedef union log_output {
+ int facility;
+ log_file_desc file;
+} log_output;
+
+struct log_channel {
+ int level; /*%< don't log messages > level */
+ log_channel_type type;
+ log_output out;
+ unsigned int flags;
+ int references;
+};
+
+typedef struct log_channel_list {
+ log_channel channel;
+ struct log_channel_list *next;
+} *log_channel_list;
+
+#define LOG_BUFFER_SIZE 20480
+
+struct log_context {
+ int num_categories;
+ char **category_names;
+ log_channel_list *categories;
+ int flags;
+ int level;
+ char buffer[LOG_BUFFER_SIZE];
+};
+
+#endif /* !LOGGING_P_H */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/memcluster.c b/usr/src/lib/libresolv2_joy/common/isc/memcluster.c
new file mode 100644
index 0000000000..1a2db0605b
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/memcluster.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 2005 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1997,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+/* When this symbol is defined allocations via memget are made slightly
+ bigger and some debugging info stuck before and after the region given
+ back to the caller. */
+/* #define DEBUGGING_MEMCLUSTER */
+#define MEMCLUSTER_ATEND
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <isc/memcluster.h>
+#include <isc/assertions.h>
+
+#include "port_after.h"
+
+#ifdef MEMCLUSTER_RECORD
+#ifndef DEBUGGING_MEMCLUSTER
+#define DEBUGGING_MEMCLUSTER
+#endif
+#endif
+
+#define DEF_MAX_SIZE 1100
+#define DEF_MEM_TARGET 4096
+
+typedef u_int32_t fence_t;
+
+typedef struct {
+ void * next;
+#if defined(DEBUGGING_MEMCLUSTER)
+#if defined(MEMCLUSTER_RECORD)
+ const char * file;
+ int line;
+#endif
+ size_t size;
+ fence_t fencepost;
+#endif
+} memcluster_element;
+
+#define SMALL_SIZE_LIMIT sizeof(memcluster_element)
+#define P_SIZE sizeof(void *)
+#define FRONT_FENCEPOST 0xfebafeba
+#define BACK_FENCEPOST 0xabefabef
+#define FENCEPOST_SIZE 4
+
+#ifndef MEMCLUSTER_LITTLE_MALLOC
+#define MEMCLUSTER_BIG_MALLOC 1
+#define NUM_BASIC_BLOCKS 64
+#endif
+
+struct stats {
+ u_long gets;
+ u_long totalgets;
+ u_long blocks;
+ u_long freefrags;
+};
+
+#ifdef DO_PTHREADS
+#include <pthread.h>
+static pthread_mutex_t memlock = PTHREAD_MUTEX_INITIALIZER;
+#define MEMLOCK (void)pthread_mutex_lock(&memlock)
+#define MEMUNLOCK (void)pthread_mutex_unlock(&memlock)
+#else
+/*
+ * Catch bad lock usage in non threaded build.
+ */
+static unsigned int memlock = 0;
+#define MEMLOCK do { INSIST(memlock == 0); memlock = 1; } while (0)
+#define MEMUNLOCK do { INSIST(memlock == 1); memlock = 0; } while (0)
+#endif /* DO_PTHEADS */
+
+/* Private data. */
+
+static size_t max_size;
+static size_t mem_target;
+#ifndef MEMCLUSTER_BIG_MALLOC
+static size_t mem_target_half;
+static size_t mem_target_fudge;
+#endif
+static memcluster_element ** freelists;
+#ifdef MEMCLUSTER_RECORD
+static memcluster_element ** activelists;
+#endif
+#ifdef MEMCLUSTER_BIG_MALLOC
+static memcluster_element * basic_blocks;
+#endif
+static struct stats * stats;
+
+/* Forward. */
+
+static size_t quantize(size_t);
+#if defined(DEBUGGING_MEMCLUSTER)
+static void check(unsigned char *, int, size_t);
+#endif
+
+/* Public. */
+
+int
+meminit(size_t init_max_size, size_t target_size) {
+
+#if defined(DEBUGGING_MEMCLUSTER)
+ INSIST(sizeof(fence_t) == FENCEPOST_SIZE);
+#endif
+ if (freelists != NULL) {
+ errno = EEXIST;
+ return (-1);
+ }
+ if (init_max_size == 0U)
+ max_size = DEF_MAX_SIZE;
+ else
+ max_size = init_max_size;
+ if (target_size == 0U)
+ mem_target = DEF_MEM_TARGET;
+ else
+ mem_target = target_size;
+#ifndef MEMCLUSTER_BIG_MALLOC
+ mem_target_half = mem_target / 2;
+ mem_target_fudge = mem_target + mem_target / 4;
+#endif
+ freelists = malloc(max_size * sizeof (memcluster_element *));
+ stats = malloc((max_size+1) * sizeof (struct stats));
+ if (freelists == NULL || stats == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ memset(freelists, 0,
+ max_size * sizeof (memcluster_element *));
+ memset(stats, 0, (max_size + 1) * sizeof (struct stats));
+#ifdef MEMCLUSTER_RECORD
+ activelists = malloc((max_size + 1) * sizeof (memcluster_element *));
+ if (activelists == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ memset(activelists, 0,
+ (max_size + 1) * sizeof (memcluster_element *));
+#endif
+#ifdef MEMCLUSTER_BIG_MALLOC
+ basic_blocks = NULL;
+#endif
+ return (0);
+}
+
+void *
+__memget(size_t size) {
+ return (__memget_record(size, NULL, 0));
+}
+
+void *
+__memget_record(size_t size, const char *file, int line) {
+ size_t new_size = quantize(size);
+#if defined(DEBUGGING_MEMCLUSTER)
+ memcluster_element *e;
+ char *p;
+ fence_t fp = BACK_FENCEPOST;
+#endif
+ void *ret;
+
+ MEMLOCK;
+
+#if !defined(MEMCLUSTER_RECORD)
+ UNUSED(file);
+ UNUSED(line);
+#endif
+ if (freelists == NULL) {
+ if (meminit(0, 0) == -1) {
+ MEMUNLOCK;
+ return (NULL);
+ }
+ }
+ if (size == 0U) {
+ MEMUNLOCK;
+ errno = EINVAL;
+ return (NULL);
+ }
+ if (size >= max_size || new_size >= max_size) {
+ /* memget() was called on something beyond our upper limit. */
+ stats[max_size].gets++;
+ stats[max_size].totalgets++;
+#if defined(DEBUGGING_MEMCLUSTER)
+ e = malloc(new_size);
+ if (e == NULL) {
+ MEMUNLOCK;
+ errno = ENOMEM;
+ return (NULL);
+ }
+ e->next = NULL;
+ e->size = size;
+#ifdef MEMCLUSTER_RECORD
+ e->file = file;
+ e->line = line;
+ e->next = activelists[max_size];
+ activelists[max_size] = e;
+#endif
+ MEMUNLOCK;
+ e->fencepost = FRONT_FENCEPOST;
+ p = (char *)e + sizeof *e + size;
+ memcpy(p, &fp, sizeof fp);
+ return ((char *)e + sizeof *e);
+#else
+ MEMUNLOCK;
+ return (malloc(size));
+#endif
+ }
+
+ /*
+ * If there are no blocks in the free list for this size, get a chunk
+ * of memory and then break it up into "new_size"-sized blocks, adding
+ * them to the free list.
+ */
+ if (freelists[new_size] == NULL) {
+ int i, frags;
+ size_t total_size;
+ void *new;
+ char *curr, *next;
+
+#ifdef MEMCLUSTER_BIG_MALLOC
+ if (basic_blocks == NULL) {
+ new = malloc(NUM_BASIC_BLOCKS * mem_target);
+ if (new == NULL) {
+ MEMUNLOCK;
+ errno = ENOMEM;
+ return (NULL);
+ }
+ curr = new;
+ next = curr + mem_target;
+ for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) {
+ ((memcluster_element *)curr)->next = next;
+ curr = next;
+ next += mem_target;
+ }
+ /*
+ * curr is now pointing at the last block in the
+ * array.
+ */
+ ((memcluster_element *)curr)->next = NULL;
+ basic_blocks = new;
+ }
+ total_size = mem_target;
+ new = basic_blocks;
+ basic_blocks = basic_blocks->next;
+#else
+ if (new_size > mem_target_half)
+ total_size = mem_target_fudge;
+ else
+ total_size = mem_target;
+ new = malloc(total_size);
+ if (new == NULL) {
+ MEMUNLOCK;
+ errno = ENOMEM;
+ return (NULL);
+ }
+#endif
+ frags = total_size / new_size;
+ stats[new_size].blocks++;
+ stats[new_size].freefrags += frags;
+ /* Set up a linked-list of blocks of size "new_size". */
+ curr = new;
+ next = curr + new_size;
+ for (i = 0; i < (frags - 1); i++) {
+#if defined (DEBUGGING_MEMCLUSTER)
+ memset(curr, 0xa5, new_size);
+#endif
+ ((memcluster_element *)curr)->next = next;
+ curr = next;
+ next += new_size;
+ }
+ /* curr is now pointing at the last block in the array. */
+#if defined (DEBUGGING_MEMCLUSTER)
+ memset(curr, 0xa5, new_size);
+#endif
+ ((memcluster_element *)curr)->next = freelists[new_size];
+ freelists[new_size] = new;
+ }
+
+ /* The free list uses the "rounded-up" size "new_size". */
+#if defined (DEBUGGING_MEMCLUSTER)
+ e = freelists[new_size];
+ ret = (char *)e + sizeof *e;
+ /*
+ * Check to see if this buffer has been written to while on free list.
+ */
+ check(ret, 0xa5, new_size - sizeof *e);
+ /*
+ * Mark memory we are returning.
+ */
+ memset(ret, 0xe5, size);
+#else
+ ret = freelists[new_size];
+#endif
+ freelists[new_size] = freelists[new_size]->next;
+#if defined(DEBUGGING_MEMCLUSTER)
+ e->next = NULL;
+ e->size = size;
+ e->fencepost = FRONT_FENCEPOST;
+#ifdef MEMCLUSTER_RECORD
+ e->file = file;
+ e->line = line;
+ e->next = activelists[size];
+ activelists[size] = e;
+#endif
+ p = (char *)e + sizeof *e + size;
+ memcpy(p, &fp, sizeof fp);
+#endif
+
+ /*
+ * The stats[] uses the _actual_ "size" requested by the
+ * caller, with the caveat (in the code above) that "size" >= the
+ * max. size (max_size) ends up getting recorded as a call to
+ * max_size.
+ */
+ stats[size].gets++;
+ stats[size].totalgets++;
+ stats[new_size].freefrags--;
+ MEMUNLOCK;
+#if defined(DEBUGGING_MEMCLUSTER)
+ return ((char *)e + sizeof *e);
+#else
+ return (ret);
+#endif
+}
+
+/*%
+ * This is a call from an external caller,
+ * so we want to count this as a user "put".
+ */
+void
+__memput(void *mem, size_t size) {
+ __memput_record(mem, size, NULL, 0);
+}
+
+void
+__memput_record(void *mem, size_t size, const char *file, int line) {
+ size_t new_size = quantize(size);
+#if defined (DEBUGGING_MEMCLUSTER)
+ memcluster_element *e;
+ memcluster_element *el;
+#ifdef MEMCLUSTER_RECORD
+ memcluster_element *prev;
+#endif
+ fence_t fp;
+ char *p;
+#endif
+
+ MEMLOCK;
+
+#if !defined (MEMCLUSTER_RECORD)
+ UNUSED(file);
+ UNUSED(line);
+#endif
+
+ REQUIRE(freelists != NULL);
+
+ if (size == 0U) {
+ MEMUNLOCK;
+ errno = EINVAL;
+ return;
+ }
+
+#if defined (DEBUGGING_MEMCLUSTER)
+ e = (memcluster_element *) ((char *)mem - sizeof *e);
+ INSIST(e->fencepost == FRONT_FENCEPOST);
+ INSIST(e->size == size);
+ p = (char *)e + sizeof *e + size;
+ memcpy(&fp, p, sizeof fp);
+ INSIST(fp == BACK_FENCEPOST);
+ INSIST(((u_long)mem % 4) == 0);
+#ifdef MEMCLUSTER_RECORD
+ prev = NULL;
+ if (size == max_size || new_size >= max_size)
+ el = activelists[max_size];
+ else
+ el = activelists[size];
+ while (el != NULL && el != e) {
+ prev = el;
+ el = el->next;
+ }
+ INSIST(el != NULL); /*%< double free */
+ if (prev == NULL) {
+ if (size == max_size || new_size >= max_size)
+ activelists[max_size] = el->next;
+ else
+ activelists[size] = el->next;
+ } else
+ prev->next = el->next;
+#endif
+#endif
+
+ if (size == max_size || new_size >= max_size) {
+ /* memput() called on something beyond our upper limit */
+#if defined(DEBUGGING_MEMCLUSTER)
+ free(e);
+#else
+ free(mem);
+#endif
+
+ INSIST(stats[max_size].gets != 0U);
+ stats[max_size].gets--;
+ MEMUNLOCK;
+ return;
+ }
+
+ /* The free list uses the "rounded-up" size "new_size": */
+#if defined(DEBUGGING_MEMCLUSTER)
+ memset(mem, 0xa5, new_size - sizeof *e); /*%< catch write after free */
+ e->size = 0; /*%< catch double memput() */
+#ifdef MEMCLUSTER_RECORD
+ e->file = file;
+ e->line = line;
+#endif
+#ifdef MEMCLUSTER_ATEND
+ e->next = NULL;
+ el = freelists[new_size];
+ while (el != NULL && el->next != NULL)
+ el = el->next;
+ if (el)
+ el->next = e;
+ else
+ freelists[new_size] = e;
+#else
+ e->next = freelists[new_size];
+ freelists[new_size] = (void *)e;
+#endif
+#else
+ ((memcluster_element *)mem)->next = freelists[new_size];
+ freelists[new_size] = (memcluster_element *)mem;
+#endif
+
+ /*
+ * The stats[] uses the _actual_ "size" requested by the
+ * caller, with the caveat (in the code above) that "size" >= the
+ * max. size (max_size) ends up getting recorded as a call to
+ * max_size.
+ */
+ INSIST(stats[size].gets != 0U);
+ stats[size].gets--;
+ stats[new_size].freefrags++;
+ MEMUNLOCK;
+}
+
+void *
+__memget_debug(size_t size, const char *file, int line) {
+ void *ptr;
+ ptr = __memget_record(size, file, line);
+ fprintf(stderr, "%s:%d: memget(%lu) -> %p\n", file, line,
+ (u_long)size, ptr);
+ return (ptr);
+}
+
+void
+__memput_debug(void *ptr, size_t size, const char *file, int line) {
+ fprintf(stderr, "%s:%d: memput(%p, %lu)\n", file, line, ptr,
+ (u_long)size);
+ __memput_record(ptr, size, file, line);
+}
+
+/*%
+ * Print the stats[] on the stream "out" with suitable formatting.
+ */
+void
+memstats(FILE *out) {
+ size_t i;
+#ifdef MEMCLUSTER_RECORD
+ memcluster_element *e;
+#endif
+
+ MEMLOCK;
+
+ if (freelists == NULL) {
+ MEMUNLOCK;
+ return;
+ }
+ for (i = 1; i <= max_size; i++) {
+ const struct stats *s = &stats[i];
+
+ if (s->totalgets == 0U && s->gets == 0U)
+ continue;
+ fprintf(out, "%s%5lu: %11lu gets, %11lu rem",
+ (i == max_size) ? ">=" : " ",
+ (unsigned long)i, s->totalgets, s->gets);
+ if (s->blocks != 0U)
+ fprintf(out, " (%lu bl, %lu ff)",
+ s->blocks, s->freefrags);
+ fputc('\n', out);
+ }
+#ifdef MEMCLUSTER_RECORD
+ fprintf(out, "Active Memory:\n");
+ for (i = 1; i <= max_size; i++) {
+ if ((e = activelists[i]) != NULL)
+ while (e != NULL) {
+ fprintf(out, "%s:%d %p:%lu\n",
+ e->file != NULL ? e->file :
+ "<UNKNOWN>", e->line,
+ (char *)e + sizeof *e,
+ (u_long)e->size);
+ e = e->next;
+ }
+ }
+#endif
+ MEMUNLOCK;
+}
+
+int
+memactive(void) {
+ size_t i;
+
+ if (stats == NULL)
+ return (0);
+ for (i = 1; i <= max_size; i++)
+ if (stats[i].gets != 0U)
+ return (1);
+ return (0);
+}
+
+/* Private. */
+
+/*%
+ * Round up size to a multiple of sizeof(void *). This guarantees that a
+ * block is at least sizeof void *, and that we won't violate alignment
+ * restrictions, both of which are needed to make lists of blocks.
+ */
+static size_t
+quantize(size_t size) {
+ int remainder;
+ /*
+ * If there is no remainder for the integer division of
+ *
+ * (rightsize/P_SIZE)
+ *
+ * then we already have a good size; if not, then we need
+ * to round up the result in order to get a size big
+ * enough to satisfy the request _and_ aligned on P_SIZE boundaries.
+ */
+ remainder = size % P_SIZE;
+ if (remainder != 0)
+ size += P_SIZE - remainder;
+#if defined(DEBUGGING_MEMCLUSTER)
+ return (size + SMALL_SIZE_LIMIT + sizeof (int));
+#else
+ return (size);
+#endif
+}
+
+#if defined(DEBUGGING_MEMCLUSTER)
+static void
+check(unsigned char *a, int value, size_t len) {
+ size_t i;
+ for (i = 0; i < len; i++)
+ INSIST(a[i] == value);
+}
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/movefile.c b/usr/src/lib/libresolv2_joy/common/isc/movefile.c
new file mode 100644
index 0000000000..0ffc7047e2
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/movefile.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2000 by Internet Software Consortium, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#include <port_before.h>
+#include <stdio.h>
+#include <isc/misc.h>
+#include <port_after.h>
+#ifndef HAVE_MOVEFILE
+/*
+ * rename() is lame (can't overwrite an existing file) on some systems.
+ * use movefile() instead, and let lame OS ports do what they need to.
+ */
+
+int
+isc_movefile(const char *oldname, const char *newname) {
+ return (rename(oldname, newname));
+}
+#else
+ static int os_port_has_isc_movefile = 1;
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/isc/tree.c b/usr/src/lib/libresolv2_joy/common/isc/tree.c
new file mode 100644
index 0000000000..404014602a
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/isc/tree.c
@@ -0,0 +1,530 @@
+/*%
+ * tree - balanced binary tree library
+ *
+ * vix 05apr94 [removed vixie.h dependencies; cleaned up formatting, names]
+ * vix 22jan93 [revisited; uses RCS, ANSI, POSIX; has bug fixes]
+ * vix 23jun86 [added delete uar to add for replaced nodes]
+ * vix 20jun86 [added tree_delete per wirth a+ds (mod2 v.) p. 224]
+ * vix 06feb86 [added tree_mung()]
+ * vix 02feb86 [added tree balancing from wirth "a+ds=p" p. 220-221]
+ * vix 14dec85 [written]
+ */
+
+/*%
+ * This program text was created by Paul Vixie using examples from the book:
+ * "Algorithms & Data Structures," Niklaus Wirth, Prentice-Hall, 1986, ISBN
+ * 0-13-022005-1. Any errors in the conversion from Modula-2 to C are Paul
+ * Vixie's.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*#define DEBUG "tree"*/
+
+#include "port_before.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "port_after.h"
+
+#include <isc/memcluster.h>
+#include <isc/tree.h>
+
+#ifdef DEBUG
+static int debugDepth = 0;
+static char *debugFuncs[256];
+# define ENTER(proc) { \
+ debugFuncs[debugDepth] = proc; \
+ fprintf(stderr, "ENTER(%d:%s.%s)\n", \
+ debugDepth, DEBUG, \
+ debugFuncs[debugDepth]); \
+ debugDepth++; \
+ }
+# define RET(value) { \
+ debugDepth--; \
+ fprintf(stderr, "RET(%d:%s.%s)\n", \
+ debugDepth, DEBUG, \
+ debugFuncs[debugDepth]); \
+ return (value); \
+ }
+# define RETV { \
+ debugDepth--; \
+ fprintf(stderr, "RETV(%d:%s.%s)\n", \
+ debugDepth, DEBUG, \
+ debugFuncs[debugDepth]); \
+ return; \
+ }
+# define MSG(msg) fprintf(stderr, "MSG(%s)\n", msg);
+#else
+# define ENTER(proc) ;
+# define RET(value) return (value);
+# define RETV return;
+# define MSG(msg) ;
+#endif
+
+#ifndef TRUE
+# define TRUE 1
+# define FALSE 0
+#endif
+
+static tree * sprout(tree **, tree_t, int *, int (*)(), void (*)());
+static int delete(tree **, int (*)(), tree_t, void (*)(), int *, int *);
+static void del(tree **, int *, tree **, void (*)(), int *);
+static void bal_L(tree **, int *);
+static void bal_R(tree **, int *);
+
+void
+tree_init(tree **ppr_tree) {
+ ENTER("tree_init")
+ *ppr_tree = NULL;
+ RETV
+}
+
+tree_t
+tree_srch(tree **ppr_tree, int (*pfi_compare)(tree_t, tree_t), tree_t p_user) {
+ ENTER("tree_srch")
+
+ if (*ppr_tree) {
+ int i_comp = (*pfi_compare)(p_user, (**ppr_tree).data);
+
+ if (i_comp > 0)
+ RET(tree_srch(&(**ppr_tree).right,
+ pfi_compare,
+ p_user))
+
+ if (i_comp < 0)
+ RET(tree_srch(&(**ppr_tree).left,
+ pfi_compare,
+ p_user))
+
+ /* not higher, not lower... this must be the one.
+ */
+ RET((**ppr_tree).data)
+ }
+
+ /* grounded. NOT found.
+ */
+ RET(NULL)
+}
+
+tree_t
+tree_add(tree **ppr_tree, int (*pfi_compare)(tree_t, tree_t),
+ tree_t p_user, void (*pfv_uar)())
+{
+ int i_balance = FALSE;
+
+ ENTER("tree_add")
+ if (!sprout(ppr_tree, p_user, &i_balance, pfi_compare, pfv_uar))
+ RET(NULL)
+ RET(p_user)
+}
+
+int
+tree_delete(tree **ppr_p, int (*pfi_compare)(tree_t, tree_t),
+ tree_t p_user, void (*pfv_uar)())
+{
+ int i_balance = FALSE, i_uar_called = FALSE;
+
+ ENTER("tree_delete");
+ RET(delete(ppr_p, pfi_compare, p_user, pfv_uar,
+ &i_balance, &i_uar_called))
+}
+
+int
+tree_trav(tree **ppr_tree, int (*pfi_uar)(tree_t)) {
+ ENTER("tree_trav")
+
+ if (!*ppr_tree)
+ RET(TRUE)
+
+ if (!tree_trav(&(**ppr_tree).left, pfi_uar))
+ RET(FALSE)
+ if (!(*pfi_uar)((**ppr_tree).data))
+ RET(FALSE)
+ if (!tree_trav(&(**ppr_tree).right, pfi_uar))
+ RET(FALSE)
+ RET(TRUE)
+}
+
+void
+tree_mung(tree **ppr_tree, void (*pfv_uar)(tree_t)) {
+ ENTER("tree_mung")
+ if (*ppr_tree) {
+ tree_mung(&(**ppr_tree).left, pfv_uar);
+ tree_mung(&(**ppr_tree).right, pfv_uar);
+ if (pfv_uar)
+ (*pfv_uar)((**ppr_tree).data);
+ memput(*ppr_tree, sizeof(tree));
+ *ppr_tree = NULL;
+ }
+ RETV
+}
+
+static tree *
+sprout(tree **ppr, tree_t p_data, int *pi_balance,
+ int (*pfi_compare)(tree_t, tree_t), void (*pfv_delete)(tree_t))
+{
+ tree *p1, *p2, *sub;
+ int cmp;
+
+ ENTER("sprout")
+
+ /* are we grounded? if so, add the node "here" and set the rebalance
+ * flag, then exit.
+ */
+ if (!*ppr) {
+ MSG("grounded. adding new node, setting h=true")
+ *ppr = (tree *) memget(sizeof(tree));
+ if (*ppr) {
+ (*ppr)->left = NULL;
+ (*ppr)->right = NULL;
+ (*ppr)->bal = 0;
+ (*ppr)->data = p_data;
+ *pi_balance = TRUE;
+ }
+ RET(*ppr);
+ }
+
+ /* compare the data using routine passed by caller.
+ */
+ cmp = (*pfi_compare)(p_data, (*ppr)->data);
+
+ /* if LESS, prepare to move to the left.
+ */
+ if (cmp < 0) {
+ MSG("LESS. sprouting left.")
+ sub = sprout(&(*ppr)->left, p_data, pi_balance,
+ pfi_compare, pfv_delete);
+ if (sub && *pi_balance) { /*%< left branch has grown */
+ MSG("LESS: left branch has grown")
+ switch ((*ppr)->bal) {
+ case 1:
+ /* right branch WAS longer; bal is ok now */
+ MSG("LESS: case 1.. bal restored implicitly")
+ (*ppr)->bal = 0;
+ *pi_balance = FALSE;
+ break;
+ case 0:
+ /* balance WAS okay; now left branch longer */
+ MSG("LESS: case 0.. balnce bad but still ok")
+ (*ppr)->bal = -1;
+ break;
+ case -1:
+ /* left branch was already too long. rebal */
+ MSG("LESS: case -1: rebalancing")
+ p1 = (*ppr)->left;
+ if (p1->bal == -1) { /*%< LL */
+ MSG("LESS: single LL")
+ (*ppr)->left = p1->right;
+ p1->right = *ppr;
+ (*ppr)->bal = 0;
+ *ppr = p1;
+ } else { /*%< double LR */
+ MSG("LESS: double LR")
+
+ p2 = p1->right;
+ p1->right = p2->left;
+ p2->left = p1;
+
+ (*ppr)->left = p2->right;
+ p2->right = *ppr;
+
+ if (p2->bal == -1)
+ (*ppr)->bal = 1;
+ else
+ (*ppr)->bal = 0;
+
+ if (p2->bal == 1)
+ p1->bal = -1;
+ else
+ p1->bal = 0;
+ *ppr = p2;
+ } /*else*/
+ (*ppr)->bal = 0;
+ *pi_balance = FALSE;
+ } /*switch*/
+ } /*if*/
+ RET(sub)
+ } /*if*/
+
+ /* if MORE, prepare to move to the right.
+ */
+ if (cmp > 0) {
+ MSG("MORE: sprouting to the right")
+ sub = sprout(&(*ppr)->right, p_data, pi_balance,
+ pfi_compare, pfv_delete);
+ if (sub && *pi_balance) {
+ MSG("MORE: right branch has grown")
+
+ switch ((*ppr)->bal) {
+ case -1:
+ MSG("MORE: balance was off, fixed implicitly")
+ (*ppr)->bal = 0;
+ *pi_balance = FALSE;
+ break;
+ case 0:
+ MSG("MORE: balance was okay, now off but ok")
+ (*ppr)->bal = 1;
+ break;
+ case 1:
+ MSG("MORE: balance was off, need to rebalance")
+ p1 = (*ppr)->right;
+ if (p1->bal == 1) { /*%< RR */
+ MSG("MORE: single RR")
+ (*ppr)->right = p1->left;
+ p1->left = *ppr;
+ (*ppr)->bal = 0;
+ *ppr = p1;
+ } else { /*%< double RL */
+ MSG("MORE: double RL")
+
+ p2 = p1->left;
+ p1->left = p2->right;
+ p2->right = p1;
+
+ (*ppr)->right = p2->left;
+ p2->left = *ppr;
+
+ if (p2->bal == 1)
+ (*ppr)->bal = -1;
+ else
+ (*ppr)->bal = 0;
+
+ if (p2->bal == -1)
+ p1->bal = 1;
+ else
+ p1->bal = 0;
+
+ *ppr = p2;
+ } /*else*/
+ (*ppr)->bal = 0;
+ *pi_balance = FALSE;
+ } /*switch*/
+ } /*if*/
+ RET(sub)
+ } /*if*/
+
+ /* not less, not more: this is the same key! replace...
+ */
+ MSG("FOUND: Replacing data value")
+ *pi_balance = FALSE;
+ if (pfv_delete)
+ (*pfv_delete)((*ppr)->data);
+ (*ppr)->data = p_data;
+ RET(*ppr)
+}
+
+static int
+delete(tree **ppr_p, int (*pfi_compare)(tree_t, tree_t), tree_t p_user,
+ void (*pfv_uar)(tree_t), int *pi_balance, int *pi_uar_called)
+{
+ tree *pr_q;
+ int i_comp, i_ret;
+
+ ENTER("delete")
+
+ if (*ppr_p == NULL) {
+ MSG("key not in tree")
+ RET(FALSE)
+ }
+
+ i_comp = (*pfi_compare)((*ppr_p)->data, p_user);
+ if (i_comp > 0) {
+ MSG("too high - scan left")
+ i_ret = delete(&(*ppr_p)->left, pfi_compare, p_user, pfv_uar,
+ pi_balance, pi_uar_called);
+ if (*pi_balance)
+ bal_L(ppr_p, pi_balance);
+ } else if (i_comp < 0) {
+ MSG("too low - scan right")
+ i_ret = delete(&(*ppr_p)->right, pfi_compare, p_user, pfv_uar,
+ pi_balance, pi_uar_called);
+ if (*pi_balance)
+ bal_R(ppr_p, pi_balance);
+ } else {
+ MSG("equal")
+ pr_q = *ppr_p;
+ if (pr_q->right == NULL) {
+ MSG("right subtree null")
+ *ppr_p = pr_q->left;
+ *pi_balance = TRUE;
+ } else if (pr_q->left == NULL) {
+ MSG("right subtree non-null, left subtree null")
+ *ppr_p = pr_q->right;
+ *pi_balance = TRUE;
+ } else {
+ MSG("neither subtree null")
+ del(&pr_q->left, pi_balance, &pr_q,
+ pfv_uar, pi_uar_called);
+ if (*pi_balance)
+ bal_L(ppr_p, pi_balance);
+ }
+ if (!*pi_uar_called && pfv_uar)
+ (*pfv_uar)(pr_q->data);
+ /* Thanks to wuth@castrov.cuc.ab.ca for the following stmt. */
+ memput(pr_q, sizeof(tree));
+ i_ret = TRUE;
+ }
+ RET(i_ret)
+}
+
+static void
+del(tree **ppr_r, int *pi_balance, tree **ppr_q,
+ void (*pfv_uar)(tree_t), int *pi_uar_called)
+{
+ ENTER("del")
+
+ if ((*ppr_r)->right != NULL) {
+ del(&(*ppr_r)->right, pi_balance, ppr_q,
+ pfv_uar, pi_uar_called);
+ if (*pi_balance)
+ bal_R(ppr_r, pi_balance);
+ } else {
+ if (pfv_uar)
+ (*pfv_uar)((*ppr_q)->data);
+ *pi_uar_called = TRUE;
+ (*ppr_q)->data = (*ppr_r)->data;
+ *ppr_q = *ppr_r;
+ *ppr_r = (*ppr_r)->left;
+ *pi_balance = TRUE;
+ }
+
+ RETV
+}
+
+static void
+bal_L(tree **ppr_p, int *pi_balance) {
+ tree *p1, *p2;
+ int b1, b2;
+
+ ENTER("bal_L")
+ MSG("left branch has shrunk")
+
+ switch ((*ppr_p)->bal) {
+ case -1:
+ MSG("was imbalanced, fixed implicitly")
+ (*ppr_p)->bal = 0;
+ break;
+ case 0:
+ MSG("was okay, is now one off")
+ (*ppr_p)->bal = 1;
+ *pi_balance = FALSE;
+ break;
+ case 1:
+ MSG("was already off, this is too much")
+ p1 = (*ppr_p)->right;
+ b1 = p1->bal;
+ if (b1 >= 0) {
+ MSG("single RR")
+ (*ppr_p)->right = p1->left;
+ p1->left = *ppr_p;
+ if (b1 == 0) {
+ MSG("b1 == 0")
+ (*ppr_p)->bal = 1;
+ p1->bal = -1;
+ *pi_balance = FALSE;
+ } else {
+ MSG("b1 != 0")
+ (*ppr_p)->bal = 0;
+ p1->bal = 0;
+ }
+ *ppr_p = p1;
+ } else {
+ MSG("double RL")
+ p2 = p1->left;
+ b2 = p2->bal;
+ p1->left = p2->right;
+ p2->right = p1;
+ (*ppr_p)->right = p2->left;
+ p2->left = *ppr_p;
+ if (b2 == 1)
+ (*ppr_p)->bal = -1;
+ else
+ (*ppr_p)->bal = 0;
+ if (b2 == -1)
+ p1->bal = 1;
+ else
+ p1->bal = 0;
+ *ppr_p = p2;
+ p2->bal = 0;
+ }
+ }
+ RETV
+}
+
+static void
+bal_R(tree **ppr_p, int *pi_balance) {
+ tree *p1, *p2;
+ int b1, b2;
+
+ ENTER("bal_R")
+ MSG("right branch has shrunk")
+ switch ((*ppr_p)->bal) {
+ case 1:
+ MSG("was imbalanced, fixed implicitly")
+ (*ppr_p)->bal = 0;
+ break;
+ case 0:
+ MSG("was okay, is now one off")
+ (*ppr_p)->bal = -1;
+ *pi_balance = FALSE;
+ break;
+ case -1:
+ MSG("was already off, this is too much")
+ p1 = (*ppr_p)->left;
+ b1 = p1->bal;
+ if (b1 <= 0) {
+ MSG("single LL")
+ (*ppr_p)->left = p1->right;
+ p1->right = *ppr_p;
+ if (b1 == 0) {
+ MSG("b1 == 0")
+ (*ppr_p)->bal = -1;
+ p1->bal = 1;
+ *pi_balance = FALSE;
+ } else {
+ MSG("b1 != 0")
+ (*ppr_p)->bal = 0;
+ p1->bal = 0;
+ }
+ *ppr_p = p1;
+ } else {
+ MSG("double LR")
+ p2 = p1->right;
+ b2 = p2->bal;
+ p1->right = p2->left;
+ p2->left = p1;
+ (*ppr_p)->left = p2->right;
+ p2->right = *ppr_p;
+ if (b2 == -1)
+ (*ppr_p)->bal = 1;
+ else
+ (*ppr_p)->bal = 0;
+ if (b2 == 1)
+ p1->bal = -1;
+ else
+ p1->bal = 0;
+ *ppr_p = p2;
+ p2->bal = 0;
+ }
+ }
+ RETV
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/llib-lresolv_joy b/usr/src/lib/libresolv2_joy/common/llib-lresolv_joy
new file mode 100644
index 0000000000..aedd06a0fa
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/llib-lresolv_joy
@@ -0,0 +1,59 @@
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+/*
+ * Copyright (c) 1997-1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+/*
+ * usr/src/lib/libresolv2 routines
+ */
+
+int dn_skipname(const uchar_t *, const uchar_t *);
+void fp_query(const u_char *, FILE *);
+const uchar_t * p_cdname(const uchar_t *, const uchar_t *, FILE *);
+const char * p_class(int);
+void p_query(const u_char *);
+const char * p_time(unsigned int);
+const char * p_type(int);
+void putlong(unsigned int, uchar_t *);
+uint32_t _getlong(const u_char *);
+uint16_t _getshort(const u_char *);
+const char * hstrerror(int);
+int res_init(void);
+int res_mkquery(int, const char *, int, int, const u_char *,
+ int, const u_char *, u_char *, int);
+int res_query(const char *, int, int, u_char *, int);
+int res_querydomain(const char *, const char *, int, int,
+ u_char *, int);
+int res_search(const char *, int, int, u_char *, int);
+int res_send(const u_char *, int, u_char *, int);
+int res_update(ns_updrec *);
+int res_ninit(res_state);
+void fp_resstat(const res_state, FILE *);
+const char * res_hostalias(const res_state, const char *, char *, size_t);
+int res_nquery(res_state, const char *, int, int, u_char *, int);
+int res_nsearch(res_state, const char *, int, int, u_char *, int);
+int res_nquerydomain(res_state, const char *, const char *,
+ int, int, u_char *, int);
+int res_nmkquery(res_state, int, const char *, int, int,
+ const u_char *, int, const u_char *,
+ u_char *, int);
+int res_nsend(res_state, const u_char *, int, u_char *, int);
+int res_nmkupdate(res_state, ns_updrec *, u_char *, int);
+void res_nclose(res_state);
+int res_nsendsigned(res_state, const u_char *, int, ns_tsig_key *,
+ u_char *, int);
+int dn_comp(const char *, u_char *, int, u_char **, u_char **);
+int dn_expand(const u_char *, const u_char *, const u_char *,
+ char *, int);
diff --git a/usr/src/lib/libresolv2_joy/common/mapfile-vers b/usr/src/lib/libresolv2_joy/common/mapfile-vers
new file mode 100644
index 0000000000..e0c66a1c96
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/mapfile-vers
@@ -0,0 +1,60 @@
+#
+# 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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate {
+ global:
+ joy_dn_expand;
+ joy_res_nsearch;
+ joy_res_ninit;
+ joy_res_ndestroy;
+ joy_res_gethostbyaddr;
+ joy_res_gethostbyname;
+ joy_res_gethostbyname2;
+ joy_res_sethostent;
+ joy_res_endhostent;
+ __joy_res_override_retry;
+ __joy_res_unset_no_hosts_fallback;
+ __joy_res_set_no_hosts_fallback;
+ __joy_h_errno;
+ __joy_ns_get16;
+ __joy_ns_get32;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_date.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_date.c
new file mode 100644
index 0000000000..5802172a03
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_date.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/* Forward. */
+
+static int datepart(const char *, int, int, int, int *);
+
+/* Public. */
+
+/*%
+ * Convert a date in ASCII into the number of seconds since
+ * 1 January 1970 (GMT assumed). Format is yyyymmddhhmmss, all
+ * digits required, no spaces allowed.
+ */
+
+u_int32_t
+ns_datetosecs(const char *cp, int *errp) {
+ struct tm time;
+ u_int32_t result;
+ int mdays, i;
+ static const int days_per_month[12] =
+ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ if (strlen(cp) != 14U) {
+ *errp = 1;
+ return (0);
+ }
+ *errp = 0;
+
+ memset(&time, 0, sizeof time);
+ time.tm_year = datepart(cp + 0, 4, 1990, 9999, errp) - 1900;
+ time.tm_mon = datepart(cp + 4, 2, 01, 12, errp) - 1;
+ time.tm_mday = datepart(cp + 6, 2, 01, 31, errp);
+ time.tm_hour = datepart(cp + 8, 2, 00, 23, errp);
+ time.tm_min = datepart(cp + 10, 2, 00, 59, errp);
+ time.tm_sec = datepart(cp + 12, 2, 00, 59, errp);
+ if (*errp) /*%< Any parse errors? */
+ return (0);
+
+ /*
+ * OK, now because timegm() is not available in all environments,
+ * we will do it by hand. Roll up sleeves, curse the gods, begin!
+ */
+
+#define SECS_PER_DAY ((u_int32_t)24*60*60)
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+
+ result = time.tm_sec; /*%< Seconds */
+ result += time.tm_min * 60; /*%< Minutes */
+ result += time.tm_hour * (60*60); /*%< Hours */
+ result += (time.tm_mday - 1) * SECS_PER_DAY; /*%< Days */
+ /* Months are trickier. Look without leaping, then leap */
+ mdays = 0;
+ for (i = 0; i < time.tm_mon; i++)
+ mdays += days_per_month[i];
+ result += mdays * SECS_PER_DAY; /*%< Months */
+ if (time.tm_mon > 1 && isleap(1900+time.tm_year))
+ result += SECS_PER_DAY; /*%< Add leapday for this year */
+ /* First figure years without leapdays, then add them in. */
+ /* The loop is slow, FIXME, but simple and accurate. */
+ result += (time.tm_year - 70) * (SECS_PER_DAY*365); /*%< Years */
+ for (i = 70; i < time.tm_year; i++)
+ if (isleap(1900+i))
+ result += SECS_PER_DAY; /*%< Add leapday for prev year */
+ return (result);
+}
+
+/* Private. */
+
+/*%
+ * Parse part of a date. Set error flag if any error.
+ * Don't reset the flag if there is no error.
+ */
+static int
+datepart(const char *buf, int size, int min, int max, int *errp) {
+ int result = 0;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (!isdigit((unsigned char)(buf[i])))
+ *errp = 1;
+ result = (result * 10) + buf[i] - '0';
+ }
+ if (result < min)
+ *errp = 1;
+ if (result > max)
+ *errp = 1;
+ return (result);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_name.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_name.c
new file mode 100644
index 0000000000..a2bf99f1c1
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_name.c
@@ -0,0 +1,1149 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <resolv_joy.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+#define NS_TYPE_ELT 0x40 /*%< EDNS0 extended label type */
+#define DNS_LABELTYPE_BITSTRING 0x41
+
+/* Data. */
+
+static const char digits[] = "0123456789";
+
+static const char digitvalue[256] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/
+};
+
+/* Forward. */
+
+static int special(int);
+static int printable(int);
+static int dn_find(const u_char *, const u_char *,
+ const u_char * const *,
+ const u_char * const *);
+static int encode_bitsring(const char **, const char *,
+ unsigned char **, unsigned char **,
+ unsigned const char *);
+static int labellen(const u_char *);
+static int decode_bitstring(const unsigned char **,
+ char *, const char *);
+
+/* Public. */
+
+/*%
+ * Convert an encoded domain name to printable ascii as per RFC1035.
+
+ * return:
+ *\li Number of bytes written to buffer, or -1 (with errno set)
+ *
+ * notes:
+ *\li The root is returned as "."
+ *\li All other domains are returned in non absolute form
+ */
+int
+ns_name_ntop(const u_char *src, char *dst, size_t dstsiz)
+{
+ const u_char *cp;
+ char *dn, *eom;
+ u_char c;
+ u_int n;
+ int l;
+
+ cp = src;
+ dn = dst;
+ eom = dst + dstsiz;
+
+ while ((n = *cp++) != 0) {
+ if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+ /* Some kind of compression pointer. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (dn != dst) {
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '.';
+ }
+ if ((l = labellen(cp - 1)) < 0) {
+ errno = EMSGSIZE; /*%< XXX */
+ return (-1);
+ }
+ if (dn + l >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) {
+ int m;
+
+ if (n != DNS_LABELTYPE_BITSTRING) {
+ /* XXX: labellen should reject this case */
+ errno = EINVAL;
+ return (-1);
+ }
+ if ((m = decode_bitstring(&cp, dn, eom)) < 0)
+ {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ dn += m;
+ continue;
+ }
+ for ((void)NULL; l > 0; l--) {
+ c = *cp++;
+ if (special(c)) {
+ if (dn + 1 >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '\\';
+ *dn++ = (char)c;
+ } else if (!printable(c)) {
+ if (dn + 3 >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '\\';
+ *dn++ = digits[c / 100];
+ *dn++ = digits[(c % 100) / 10];
+ *dn++ = digits[c % 10];
+ } else {
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = (char)c;
+ }
+ }
+ }
+ if (dn == dst) {
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '.';
+ }
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = '\0';
+ return (dn - dst);
+}
+
+/*%
+ * Convert a ascii string into an encoded domain name as per RFC1035.
+ *
+ * return:
+ *
+ *\li -1 if it fails
+ *\li 1 if string was fully qualified
+ *\li 0 is string was not fully qualified
+ *
+ * notes:
+ *\li Enforces label and domain length limits.
+ */
+int
+ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
+ return (ns_name_pton2(src, dst, dstsiz, NULL));
+}
+
+/*
+ * ns_name_pton2(src, dst, dstsiz, *dstlen)
+ * Convert a ascii string into an encoded domain name as per RFC1035.
+ * return:
+ * -1 if it fails
+ * 1 if string was fully qualified
+ * 0 is string was not fully qualified
+ * side effects:
+ * fills in *dstlen (if non-NULL)
+ * notes:
+ * Enforces label and domain length limits.
+ */
+int
+ns_name_pton2(const char *src, u_char *dst, size_t dstsiz, size_t *dstlen) {
+ u_char *label, *bp, *eom;
+ int c, n, escaped, e = 0;
+ char *cp;
+
+ escaped = 0;
+ bp = dst;
+ eom = dst + dstsiz;
+ label = bp++;
+
+ while ((c = *src++) != 0) {
+ if (escaped) {
+ if (c == '[') { /*%< start a bit string label */
+ if ((cp = strchr(src, ']')) == NULL) {
+ errno = EINVAL; /*%< ??? */
+ return (-1);
+ }
+ if ((e = encode_bitsring(&src, cp + 2,
+ &label, &bp, eom))
+ != 0) {
+ errno = e;
+ return (-1);
+ }
+ escaped = 0;
+ label = bp++;
+ if ((c = *src++) == 0)
+ goto done;
+ else if (c != '.') {
+ errno = EINVAL;
+ return (-1);
+ }
+ continue;
+ }
+ else if ((cp = strchr(digits, c)) != NULL) {
+ n = (cp - digits) * 100;
+ if ((c = *src++) == 0 ||
+ (cp = strchr(digits, c)) == NULL) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ n += (cp - digits) * 10;
+ if ((c = *src++) == 0 ||
+ (cp = strchr(digits, c)) == NULL) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ n += (cp - digits);
+ if (n > 255) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ c = n;
+ }
+ escaped = 0;
+ } else if (c == '\\') {
+ escaped = 1;
+ continue;
+ } else if (c == '.') {
+ c = (bp - label - 1);
+ if ((c & NS_CMPRSFLGS) != 0) { /*%< Label too big. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (label >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *label = c;
+ /* Fully qualified ? */
+ if (*src == '\0') {
+ if (c != 0) {
+ if (bp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *bp++ = '\0';
+ }
+ if ((bp - dst) > MAXCDNAME) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (dstlen != NULL)
+ *dstlen = (bp - dst);
+ return (1);
+ }
+ if (c == 0 || *src == '.') {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ label = bp++;
+ continue;
+ }
+ if (bp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *bp++ = (u_char)c;
+ }
+ c = (bp - label - 1);
+ if ((c & NS_CMPRSFLGS) != 0) { /*%< Label too big. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ done:
+ if (label >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *label = c;
+ if (c != 0) {
+ if (bp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *bp++ = 0;
+ }
+ if ((bp - dst) > MAXCDNAME) { /*%< src too big */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (dstlen != NULL)
+ *dstlen = (bp - dst);
+ return (0);
+}
+
+/*%
+ * Convert a network strings labels into all lowercase.
+ *
+ * return:
+ *\li Number of bytes written to buffer, or -1 (with errno set)
+ *
+ * notes:
+ *\li Enforces label and domain length limits.
+ */
+
+int
+ns_name_ntol(const u_char *src, u_char *dst, size_t dstsiz)
+{
+ const u_char *cp;
+ u_char *dn, *eom;
+ u_char c;
+ u_int n;
+ int l;
+
+ cp = src;
+ dn = dst;
+ eom = dst + dstsiz;
+
+ if (dn >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ while ((n = *cp++) != 0) {
+ if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+ /* Some kind of compression pointer. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *dn++ = n;
+ if ((l = labellen(cp - 1)) < 0) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (dn + l >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ for ((void)NULL; l > 0; l--) {
+ c = *cp++;
+ if (isascii(c) && isupper(c))
+ *dn++ = tolower(c);
+ else
+ *dn++ = c;
+ }
+ }
+ *dn++ = '\0';
+ return (dn - dst);
+}
+
+/*%
+ * Unpack a domain name from a message, source may be compressed.
+ *
+ * return:
+ *\li -1 if it fails, or consumed octets if it succeeds.
+ */
+int
+ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
+ u_char *dst, size_t dstsiz)
+{
+ return (ns_name_unpack2(msg, eom, src, dst, dstsiz, NULL));
+}
+
+/*
+ * ns_name_unpack2(msg, eom, src, dst, dstsiz, *dstlen)
+ * Unpack a domain name from a message, source may be compressed.
+ * return:
+ * -1 if it fails, or consumed octets if it succeeds.
+ * side effect:
+ * fills in *dstlen (if non-NULL).
+ */
+int
+ns_name_unpack2(const u_char *msg, const u_char *eom, const u_char *src,
+ u_char *dst, size_t dstsiz, size_t *dstlen)
+{
+ const u_char *srcp, *dstlim;
+ u_char *dstp;
+ int n, len, checked, l;
+
+ len = -1;
+ checked = 0;
+ dstp = dst;
+ srcp = src;
+ dstlim = dst + dstsiz;
+ if (srcp < msg || srcp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ /* Fetch next label in domain name. */
+ while ((n = *srcp++) != 0) {
+ /* Check for indirection. */
+ switch (n & NS_CMPRSFLGS) {
+ case 0:
+ case NS_TYPE_ELT:
+ /* Limit checks. */
+ if ((l = labellen(srcp - 1)) < 0) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (dstp + l + 1 >= dstlim || srcp + l >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ checked += l + 1;
+ *dstp++ = n;
+ memcpy(dstp, srcp, l);
+ dstp += l;
+ srcp += l;
+ break;
+
+ case NS_CMPRSFLGS:
+ if (srcp >= eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (len < 0)
+ len = srcp - src + 1;
+ srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
+ if (srcp < msg || srcp >= eom) { /*%< Out of range. */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ checked += 2;
+ /*
+ * Check for loops in the compressed name;
+ * if we've looked at the whole message,
+ * there must be a loop.
+ */
+ if (checked >= eom - msg) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ break;
+
+ default:
+ errno = EMSGSIZE;
+ return (-1); /*%< flag error */
+ }
+ }
+ *dstp++ = 0;
+ if (dstlen != NULL)
+ *dstlen = dstp - dst;
+ if (len < 0)
+ len = srcp - src;
+ return (len);
+}
+
+/*%
+ * Pack domain name 'domain' into 'comp_dn'.
+ *
+ * return:
+ *\li Size of the compressed name, or -1.
+ *
+ * notes:
+ *\li 'dnptrs' is an array of pointers to previous compressed names.
+ *\li dnptrs[0] is a pointer to the beginning of the message. The array
+ * ends with NULL.
+ *\li 'lastdnptr' is a pointer to the end of the array pointed to
+ * by 'dnptrs'.
+ *
+ * Side effects:
+ *\li The list of pointers in dnptrs is updated for labels inserted into
+ * the message as we compress the name. If 'dnptr' is NULL, we don't
+ * try to compress names. If 'lastdnptr' is NULL, we don't update the
+ * list.
+ */
+int
+ns_name_pack(const u_char *src, u_char *dst, int dstsiz,
+ const u_char **dnptrs, const u_char **lastdnptr)
+{
+ u_char *dstp;
+ const u_char **cpp, **lpp, *eob, *msg;
+ const u_char *srcp;
+ int n, l, first = 1;
+
+ srcp = src;
+ dstp = dst;
+ eob = dstp + dstsiz;
+ lpp = cpp = NULL;
+ if (dnptrs != NULL) {
+ if ((msg = *dnptrs++) != NULL) {
+ for (cpp = dnptrs; *cpp != NULL; cpp++)
+ (void)NULL;
+ lpp = cpp; /*%< end of list to search */
+ }
+ } else
+ msg = NULL;
+
+ /* make sure the domain we are about to add is legal */
+ l = 0;
+ do {
+ int l0;
+
+ n = *srcp;
+ if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if ((l0 = labellen(srcp)) < 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+ l += l0 + 1;
+ if (l > MAXCDNAME) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ srcp += l0 + 1;
+ } while (n != 0);
+
+ /* from here on we need to reset compression pointer array on error */
+ srcp = src;
+ do {
+ /* Look to see if we can use pointers. */
+ n = *srcp;
+ if (n != 0 && msg != NULL) {
+ l = dn_find(srcp, msg, (const u_char * const *)dnptrs,
+ (const u_char * const *)lpp);
+ if (l >= 0) {
+ if (dstp + 1 >= eob) {
+ goto cleanup;
+ }
+ *dstp++ = (l >> 8) | NS_CMPRSFLGS;
+ *dstp++ = l % 256;
+ return (dstp - dst);
+ }
+ /* Not found, save it. */
+ if (lastdnptr != NULL && cpp < lastdnptr - 1 &&
+ (dstp - msg) < 0x4000 && first) {
+ *cpp++ = dstp;
+ *cpp = NULL;
+ first = 0;
+ }
+ }
+ /* copy label to buffer */
+ if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+ /* Should not happen. */
+ goto cleanup;
+ }
+ n = labellen(srcp);
+ if (dstp + 1 + n >= eob) {
+ goto cleanup;
+ }
+ memcpy(dstp, srcp, n + 1);
+ srcp += n + 1;
+ dstp += n + 1;
+ } while (n != 0);
+
+ if (dstp > eob) {
+cleanup:
+ if (msg != NULL)
+ *lpp = NULL;
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ return (dstp - dst);
+}
+
+/*%
+ * Expand compressed domain name to presentation format.
+ *
+ * return:
+ *\li Number of bytes read out of `src', or -1 (with errno set).
+ *
+ * note:
+ *\li Root domain returns as "." not "".
+ */
+int
+ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
+ char *dst, size_t dstsiz)
+{
+ u_char tmp[NS_MAXCDNAME];
+ int n;
+
+ if ((n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
+ return (-1);
+ if (ns_name_ntop(tmp, dst, dstsiz) == -1)
+ return (-1);
+ return (n);
+}
+
+/*%
+ * Compress a domain name into wire format, using compression pointers.
+ *
+ * return:
+ *\li Number of bytes consumed in `dst' or -1 (with errno set).
+ *
+ * notes:
+ *\li 'dnptrs' is an array of pointers to previous compressed names.
+ *\li dnptrs[0] is a pointer to the beginning of the message.
+ *\li The list ends with NULL. 'lastdnptr' is a pointer to the end of the
+ * array pointed to by 'dnptrs'. Side effect is to update the list of
+ * pointers for labels inserted into the message as we compress the name.
+ *\li If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr'
+ * is NULL, we don't update the list.
+ */
+int
+ns_name_compress(const char *src, u_char *dst, size_t dstsiz,
+ const u_char **dnptrs, const u_char **lastdnptr)
+{
+ u_char tmp[NS_MAXCDNAME];
+
+ if (ns_name_pton(src, tmp, sizeof tmp) == -1)
+ return (-1);
+ return (ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr));
+}
+
+/*%
+ * Reset dnptrs so that there are no active references to pointers at or
+ * after src.
+ */
+void
+ns_name_rollback(const u_char *src, const u_char **dnptrs,
+ const u_char **lastdnptr)
+{
+ while (dnptrs < lastdnptr && *dnptrs != NULL) {
+ if (*dnptrs >= src) {
+ *dnptrs = NULL;
+ break;
+ }
+ dnptrs++;
+ }
+}
+
+/*%
+ * Advance *ptrptr to skip over the compressed name it points at.
+ *
+ * return:
+ *\li 0 on success, -1 (with errno set) on failure.
+ */
+int
+ns_name_skip(const u_char **ptrptr, const u_char *eom)
+{
+ const u_char *cp;
+ u_int n;
+ int l;
+
+ cp = *ptrptr;
+ while (cp < eom && (n = *cp++) != 0) {
+ /* Check for indirection. */
+ switch (n & NS_CMPRSFLGS) {
+ case 0: /*%< normal case, n == len */
+ cp += n;
+ continue;
+ case NS_TYPE_ELT: /*%< EDNS0 extended label */
+ if ((l = labellen(cp - 1)) < 0) {
+ errno = EMSGSIZE; /*%< XXX */
+ return (-1);
+ }
+ cp += l;
+ continue;
+ case NS_CMPRSFLGS: /*%< indirection */
+ cp++;
+ break;
+ default: /*%< illegal type */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ break;
+ }
+ if (cp > eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ *ptrptr = cp;
+ return (0);
+}
+
+/* Find the number of octets an nname takes up, including the root label.
+ * (This is basically ns_name_skip() without compression-pointer support.)
+ * ((NOTE: can only return zero if passed-in namesiz argument is zero.))
+ */
+ssize_t
+ns_name_length(ns_nname_ct nname, size_t namesiz) {
+ ns_nname_ct orig = nname;
+ u_int n;
+
+ while (namesiz-- > 0 && (n = *nname++) != 0) {
+ if ((n & NS_CMPRSFLGS) != 0) {
+ errno = EISDIR;
+ return (-1);
+ }
+ if (n > namesiz) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ nname += n;
+ namesiz -= n;
+ }
+ return (nname - orig);
+}
+
+/* Compare two nname's for equality. Return -1 on error (setting errno).
+ */
+int
+ns_name_eq(ns_nname_ct a, size_t as, ns_nname_ct b, size_t bs) {
+ ns_nname_ct ae = a + as, be = b + bs;
+ int ac, bc;
+
+ while (ac = *a, bc = *b, ac != 0 && bc != 0) {
+ if ((ac & NS_CMPRSFLGS) != 0 || (bc & NS_CMPRSFLGS) != 0) {
+ errno = EISDIR;
+ return (-1);
+ }
+ if (a + ac >= ae || b + bc >= be) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (ac != bc || strncasecmp((const char *) ++a,
+ (const char *) ++b, ac) != 0)
+ return (0);
+ a += ac, b += bc;
+ }
+ return (ac == 0 && bc == 0);
+}
+
+/* Is domain "A" owned by (at or below) domain "B"?
+ */
+int
+ns_name_owned(ns_namemap_ct a, int an, ns_namemap_ct b, int bn) {
+ /* If A is shorter, it cannot be owned by B. */
+ if (an < bn)
+ return (0);
+
+ /* If they are unequal before the length of the shorter, A cannot... */
+ while (bn > 0) {
+ if (a->len != b->len ||
+ strncasecmp((const char *) a->base,
+ (const char *) b->base, a->len) != 0)
+ return (0);
+ a++, an--;
+ b++, bn--;
+ }
+
+ /* A might be longer or not, but either way, B owns it. */
+ return (1);
+}
+
+/* Build an array of <base,len> tuples from an nname, top-down order.
+ * Return the number of tuples (labels) thus discovered.
+ */
+int
+ns_name_map(ns_nname_ct nname, size_t namelen, ns_namemap_t map, int mapsize) {
+ u_int n;
+ int l;
+
+ n = *nname++;
+ namelen--;
+
+ /* Root zone? */
+ if (n == 0) {
+ /* Extra data follows name? */
+ if (namelen > 0) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ return (0);
+ }
+
+ /* Compression pointer? */
+ if ((n & NS_CMPRSFLGS) != 0) {
+ errno = EISDIR;
+ return (-1);
+ }
+
+ /* Label too long? */
+ if (n > namelen) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+
+ /* Recurse to get rest of name done first. */
+ l = ns_name_map(nname + n, namelen - n, map, mapsize);
+ if (l < 0)
+ return (-1);
+
+ /* Too many labels? */
+ if (l >= mapsize) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+
+ /* We're on our way back up-stack, store current map data. */
+ map[l].base = nname;
+ map[l].len = n;
+ return (l + 1);
+}
+
+/* Count the labels in a domain name. Root counts, so COM. has two. This
+ * is to make the result comparable to the result of ns_name_map().
+ */
+int
+ns_name_labels(ns_nname_ct nname, size_t namesiz) {
+ int ret = 0;
+ u_int n;
+
+ while (namesiz-- > 0 && (n = *nname++) != 0) {
+ if ((n & NS_CMPRSFLGS) != 0) {
+ errno = EISDIR;
+ return (-1);
+ }
+ if (n > namesiz) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ nname += n;
+ namesiz -= n;
+ ret++;
+ }
+ return (ret + 1);
+}
+
+/* Private. */
+
+/*%
+ * Thinking in noninternationalized USASCII (per the DNS spec),
+ * is this characted special ("in need of quoting") ?
+ *
+ * return:
+ *\li boolean.
+ */
+static int
+special(int ch) {
+ switch (ch) {
+ case 0x22: /*%< '"' */
+ case 0x2E: /*%< '.' */
+ case 0x3B: /*%< ';' */
+ case 0x5C: /*%< '\\' */
+ case 0x28: /*%< '(' */
+ case 0x29: /*%< ')' */
+ /* Special modifiers in zone files. */
+ case 0x40: /*%< '@' */
+ case 0x24: /*%< '$' */
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+/*%
+ * Thinking in noninternationalized USASCII (per the DNS spec),
+ * is this character visible and not a space when printed ?
+ *
+ * return:
+ *\li boolean.
+ */
+static int
+printable(int ch) {
+ return (ch > 0x20 && ch < 0x7f);
+}
+
+/*%
+ * Thinking in noninternationalized USASCII (per the DNS spec),
+ * convert this character to lower case if it's upper case.
+ */
+static int
+mklower(int ch) {
+ if (ch >= 0x41 && ch <= 0x5A)
+ return (ch + 0x20);
+ return (ch);
+}
+
+/*%
+ * Search for the counted-label name in an array of compressed names.
+ *
+ * return:
+ *\li offset from msg if found, or -1.
+ *
+ * notes:
+ *\li dnptrs is the pointer to the first name on the list,
+ *\li not the pointer to the start of the message.
+ */
+static int
+dn_find(const u_char *domain, const u_char *msg,
+ const u_char * const *dnptrs,
+ const u_char * const *lastdnptr)
+{
+ const u_char *dn, *cp, *sp;
+ const u_char * const *cpp;
+ u_int n;
+
+ for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
+ sp = *cpp;
+ /*
+ * terminate search on:
+ * root label
+ * compression pointer
+ * unusable offset
+ */
+ while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 &&
+ (sp - msg) < 0x4000) {
+ dn = domain;
+ cp = sp;
+ while ((n = *cp++) != 0) {
+ /*
+ * check for indirection
+ */
+ switch (n & NS_CMPRSFLGS) {
+ case 0: /*%< normal case, n == len */
+ n = labellen(cp - 1); /*%< XXX */
+ if (n != *dn++)
+ goto next;
+
+ for ((void)NULL; n > 0; n--)
+ if (mklower(*dn++) !=
+ mklower(*cp++))
+ goto next;
+ /* Is next root for both ? */
+ if (*dn == '\0' && *cp == '\0')
+ return (sp - msg);
+ if (*dn)
+ continue;
+ goto next;
+ case NS_CMPRSFLGS: /*%< indirection */
+ cp = msg + (((n & 0x3f) << 8) | *cp);
+ break;
+
+ default: /*%< illegal type */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ }
+ next: ;
+ sp += *sp + 1;
+ }
+ }
+ errno = ENOENT;
+ return (-1);
+}
+
+static int
+decode_bitstring(const unsigned char **cpp, char *dn, const char *eom)
+{
+ const unsigned char *cp = *cpp;
+ char *beg = dn, tc;
+ int b, blen, plen, i;
+
+ if ((blen = (*cp & 0xff)) == 0)
+ blen = 256;
+ plen = (blen + 3) / 4;
+ plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1);
+ if (dn + plen >= eom)
+ return (-1);
+
+ cp++;
+ i = SPRINTF((dn, "\\[x"));
+ if (i < 0)
+ return (-1);
+ dn += i;
+ for (b = blen; b > 7; b -= 8, cp++) {
+ i = SPRINTF((dn, "%02x", *cp & 0xff));
+ if (i < 0)
+ return (-1);
+ dn += i;
+ }
+ if (b > 4) {
+ tc = *cp++;
+ i = SPRINTF((dn, "%02x", tc & (0xff << (8 - b))));
+ if (i < 0)
+ return (-1);
+ dn += i;
+ } else if (b > 0) {
+ tc = *cp++;
+ i = SPRINTF((dn, "%1x",
+ ((tc >> 4) & 0x0f) & (0x0f << (4 - b))));
+ if (i < 0)
+ return (-1);
+ dn += i;
+ }
+ i = SPRINTF((dn, "/%d]", blen));
+ if (i < 0)
+ return (-1);
+ dn += i;
+
+ *cpp = cp;
+ return (dn - beg);
+}
+
+static int
+encode_bitsring(const char **bp, const char *end, unsigned char **labelp,
+ unsigned char ** dst, unsigned const char *eom)
+{
+ int afterslash = 0;
+ const char *cp = *bp;
+ unsigned char *tp;
+ char c;
+ const char *beg_blen;
+ char *end_blen = NULL;
+ int value = 0, count = 0, tbcount = 0, blen = 0;
+
+ beg_blen = end_blen = NULL;
+
+ /* a bitstring must contain at least 2 characters */
+ if (end - cp < 2)
+ return (EINVAL);
+
+ /* XXX: currently, only hex strings are supported */
+ if (*cp++ != 'x')
+ return (EINVAL);
+ if (!isxdigit((*cp) & 0xff)) /*%< reject '\[x/BLEN]' */
+ return (EINVAL);
+
+ for (tp = *dst + 1; cp < end && tp < eom; cp++) {
+ switch((c = *cp)) {
+ case ']': /*%< end of the bitstring */
+ if (afterslash) {
+ if (beg_blen == NULL)
+ return (EINVAL);
+ blen = (int)strtol(beg_blen, &end_blen, 10);
+ if (*end_blen != ']')
+ return (EINVAL);
+ }
+ if (count)
+ *tp++ = ((value << 4) & 0xff);
+ cp++; /*%< skip ']' */
+ goto done;
+ case '/':
+ afterslash = 1;
+ break;
+ default:
+ if (afterslash) {
+ if (!isdigit(c&0xff))
+ return (EINVAL);
+ if (beg_blen == NULL) {
+
+ if (c == '0') {
+ /* blen never begings with 0 */
+ return (EINVAL);
+ }
+ beg_blen = cp;
+ }
+ } else {
+ if (!isxdigit(c&0xff))
+ return (EINVAL);
+ value <<= 4;
+ value += digitvalue[(int)c];
+ count += 4;
+ tbcount += 4;
+ if (tbcount > 256)
+ return (EINVAL);
+ if (count == 8) {
+ *tp++ = value;
+ count = 0;
+ }
+ }
+ break;
+ }
+ }
+ done:
+ if (cp >= end || tp >= eom)
+ return (EMSGSIZE);
+
+ /*
+ * bit length validation:
+ * If a <length> is present, the number of digits in the <bit-data>
+ * MUST be just sufficient to contain the number of bits specified
+ * by the <length>. If there are insignificant bits in a final
+ * hexadecimal or octal digit, they MUST be zero.
+ * RFC2673, Section 3.2.
+ */
+ if (blen > 0) {
+ int traillen;
+
+ if (((blen + 3) & ~3) != tbcount)
+ return (EINVAL);
+ traillen = tbcount - blen; /*%< between 0 and 3 */
+ if (((value << (8 - traillen)) & 0xff) != 0)
+ return (EINVAL);
+ }
+ else
+ blen = tbcount;
+ if (blen == 256)
+ blen = 0;
+
+ /* encode the type and the significant bit fields */
+ **labelp = DNS_LABELTYPE_BITSTRING;
+ **dst = blen;
+
+ *bp = cp;
+ *dst = tp;
+
+ return (0);
+}
+
+static int
+labellen(const u_char *lp)
+{
+ int bitlen;
+ u_char l = *lp;
+
+ if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+ /* should be avoided by the caller */
+ return (-1);
+ }
+
+ if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) {
+ if (l == DNS_LABELTYPE_BITSTRING) {
+ if ((bitlen = *(lp + 1)) == 0)
+ bitlen = 256;
+ return ((bitlen + 7 ) / 8 + 1);
+ }
+ return (-1); /*%< unknwon ELT */
+ }
+ return (l);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_netint.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_netint.c
new file mode 100644
index 0000000000..f57edeb8fd
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_netint.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <arpa/nameser.h>
+
+#include "port_after.h"
+
+#pragma redefine_extname __ns_get16 __joy_ns_get16
+#pragma redefine_extname __ns_get32 __joy_ns_get32
+
+/* Public. */
+
+u_int
+ns_get16(const u_char *src) {
+ u_int dst;
+
+ NS_GET16(dst, src);
+ return (dst);
+}
+
+u_long
+ns_get32(const u_char *src) {
+ u_long dst;
+
+ NS_GET32(dst, src);
+ return (dst);
+}
+
+void
+ns_put16(u_int src, u_char *dst) {
+ NS_PUT16(src, dst);
+}
+
+void
+ns_put32(u_long src, u_char *dst) {
+ NS_PUT32(src, dst);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_newmsg.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_newmsg.c
new file mode 100644
index 0000000000..870a4608e2
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_newmsg.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <port_before.h>
+
+#include <arpa/nameser.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include <port_after.h>
+
+static int rdcpy(ns_newmsg *, ns_type, const u_char *, size_t);
+
+/* Initialize a "newmsg" object to empty.
+ */
+int
+ns_newmsg_init(u_char *buffer, size_t bufsiz, ns_newmsg *handle) {
+ ns_msg *msg = &handle->msg;
+
+ memset(handle, 0, sizeof *handle);
+ msg->_msg = buffer;
+ msg->_eom = buffer + bufsiz;
+ msg->_sect = ns_s_qd;
+ msg->_rrnum = 0;
+ msg->_msg_ptr = buffer + NS_HFIXEDSZ;
+ handle->dnptrs[0] = msg->_msg;
+ handle->dnptrs[1] = NULL;
+ handle->lastdnptr = &handle->dnptrs[sizeof handle->dnptrs /
+ sizeof handle->dnptrs[0] - 1];
+ return (0);
+}
+
+/* Initialize a "newmsg" object by copying an existing parsed message.
+ */
+int
+ns_newmsg_copy(ns_newmsg *handle, ns_msg *msg) {
+ ns_flag flag;
+ ns_sect sect;
+
+ ns_newmsg_id(handle, ns_msg_id(*msg));
+ for (flag = ns_f_qr; flag < ns_f_max; flag++)
+ ns_newmsg_flag(handle, flag, ns_msg_getflag(*msg, flag));
+ for (sect = ns_s_qd; sect < ns_s_max; sect++) {
+ int i, count;
+
+ count = ns_msg_count(*msg, sect);
+ for (i = 0; i < count; i++) {
+ ns_rr2 rr;
+ int x;
+
+ if (ns_parserr2(msg, sect, i, &rr) < 0)
+ return (-1);
+ if (sect == ns_s_qd)
+ x = ns_newmsg_q(handle,
+ ns_rr_nname(rr),
+ ns_rr_type(rr),
+ ns_rr_class(rr));
+ else
+ x = ns_newmsg_rr(handle, sect,
+ ns_rr_nname(rr),
+ ns_rr_type(rr),
+ ns_rr_class(rr),
+ ns_rr_ttl(rr),
+ ns_rr_rdlen(rr),
+ ns_rr_rdata(rr));
+ if (x < 0)
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/* Set the message-ID in a "newmsg" object.
+ */
+void
+ns_newmsg_id(ns_newmsg *handle, u_int16_t id) {
+ ns_msg *msg = &handle->msg;
+
+ msg->_id = id;
+}
+
+/* Set a flag (including rcode or opcode) in a "newmsg" object.
+ */
+void
+ns_newmsg_flag(ns_newmsg *handle, ns_flag flag, u_int value) {
+ extern struct _ns_flagdata _ns_flagdata[16];
+ struct _ns_flagdata *fd = &_ns_flagdata[flag];
+ ns_msg *msg = &handle->msg;
+
+ assert(flag < ns_f_max);
+ msg->_flags &= (~fd->mask);
+ msg->_flags |= (value << fd->shift);
+}
+
+/* Add a question (or zone, if it's an update) to a "newmsg" object.
+ */
+int
+ns_newmsg_q(ns_newmsg *handle, ns_nname_ct qname,
+ ns_type qtype, ns_class qclass)
+{
+ ns_msg *msg = &handle->msg;
+ u_char *t;
+ int n;
+
+ if (msg->_sect != ns_s_qd) {
+ errno = ENODEV;
+ return (-1);
+ }
+ t = (u_char *) (unsigned long) msg->_msg_ptr;
+ if (msg->_rrnum == 0)
+ msg->_sections[ns_s_qd] = t;
+ n = ns_name_pack(qname, t, msg->_eom - t,
+ handle->dnptrs, handle->lastdnptr);
+ if (n < 0)
+ return (-1);
+ t += n;
+ if (t + QFIXEDSZ >= msg->_eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ NS_PUT16(qtype, t);
+ NS_PUT16(qclass, t);
+ msg->_msg_ptr = t;
+ msg->_counts[ns_s_qd] = ++msg->_rrnum;
+ return (0);
+}
+
+/* Add an RR to a "newmsg" object.
+ */
+int
+ns_newmsg_rr(ns_newmsg *handle, ns_sect sect,
+ ns_nname_ct name, ns_type type,
+ ns_class rr_class, u_int32_t ttl,
+ u_int16_t rdlen, const u_char *rdata)
+{
+ ns_msg *msg = &handle->msg;
+ u_char *t;
+ int n;
+
+ if (sect < msg->_sect) {
+ errno = ENODEV;
+ return (-1);
+ }
+ t = (u_char *) (unsigned long) msg->_msg_ptr;
+ if (sect > msg->_sect) {
+ msg->_sect = sect;
+ msg->_sections[sect] = t;
+ msg->_rrnum = 0;
+ }
+ n = ns_name_pack(name, t, msg->_eom - t,
+ handle->dnptrs, handle->lastdnptr);
+ if (n < 0)
+ return (-1);
+ t += n;
+ if (t + RRFIXEDSZ + rdlen >= msg->_eom) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ NS_PUT16(type, t);
+ NS_PUT16(rr_class, t);
+ NS_PUT32(ttl, t);
+ msg->_msg_ptr = t;
+ if (rdcpy(handle, type, rdata, rdlen) < 0)
+ return (-1);
+ msg->_counts[sect] = ++msg->_rrnum;
+ return (0);
+}
+
+/* Complete a "newmsg" object and return its size for use in write().
+ * (Note: the "newmsg" object is also made ready for ns_parserr() etc.)
+ */
+size_t
+ns_newmsg_done(ns_newmsg *handle) {
+ ns_msg *msg = &handle->msg;
+ ns_sect sect;
+ u_char *t;
+
+ t = (u_char *) (unsigned long) msg->_msg;
+ NS_PUT16(msg->_id, t);
+ NS_PUT16(msg->_flags, t);
+ for (sect = 0; sect < ns_s_max; sect++)
+ NS_PUT16(msg->_counts[sect], t);
+ msg->_eom = msg->_msg_ptr;
+ msg->_sect = ns_s_max;
+ msg->_rrnum = -1;
+ msg->_msg_ptr = NULL;
+ return (msg->_eom - msg->_msg);
+}
+
+/* Private. */
+
+/* Copy an RDATA, using compression pointers where RFC1035 permits.
+ */
+static int
+rdcpy(ns_newmsg *handle, ns_type type, const u_char *rdata, size_t rdlen) {
+ ns_msg *msg = &handle->msg;
+ u_char *p = (u_char *) (unsigned long) msg->_msg_ptr;
+ u_char *t = p + NS_INT16SZ;
+ u_char *s = t;
+ int n;
+
+ switch (type) {
+ case ns_t_soa:
+ /* MNAME. */
+ n = ns_name_pack(rdata, t, msg->_eom - t,
+ handle->dnptrs, handle->lastdnptr);
+ if (n < 0)
+ return (-1);
+ t += n;
+ if (ns_name_skip(&rdata, msg->_eom) < 0)
+ return (-1);
+
+ /* ANAME. */
+ n = ns_name_pack(rdata, t, msg->_eom - t,
+ handle->dnptrs, handle->lastdnptr);
+ if (n < 0)
+ return (-1);
+ t += n;
+ if (ns_name_skip(&rdata, msg->_eom) < 0)
+ return (-1);
+
+ /* Serial, Refresh, Retry, Expiry, and Minimum. */
+ if ((msg->_eom - t) < (NS_INT32SZ * 5)) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ memcpy(t, rdata, NS_INT32SZ * 5);
+ t += (NS_INT32SZ * 5);
+ break;
+ case ns_t_ptr:
+ case ns_t_cname:
+ case ns_t_ns:
+ /* PTRDNAME, CNAME, or NSDNAME. */
+ n = ns_name_pack(rdata, t, msg->_eom - t,
+ handle->dnptrs, handle->lastdnptr);
+ if (n < 0)
+ return (-1);
+ t += n;
+ break;
+ default:
+ memcpy(t, rdata, rdlen);
+ t += rdlen;
+ }
+ NS_PUT16(t - s, p);
+ msg->_msg_ptr = t;
+ return (0);
+}
+
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_parse.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_parse.c
new file mode 100644
index 0000000000..a8909c2984
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_parse.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <resolv_joy.h>
+#include <string.h>
+
+#include "port_after.h"
+
+/* Forward. */
+
+static void setsection(ns_msg *msg, ns_sect sect);
+
+/* Macros. */
+
+#if !defined(SOLARIS2) || defined(__COVERITY__)
+#define RETERR(err) do { errno = (err); return (-1); } while (0)
+#else
+#define RETERR(err) \
+ do { errno = (err); if (errno == errno) return (-1); } while (0)
+#endif
+
+#define PARSE_FMT_PRESO 0 /* Parse using presentation-format names */
+#define PARSE_FMT_WIRE 1 /* Parse using network-format names */
+
+/* Public. */
+
+/* These need to be in the same order as the nres.h:ns_flag enum. */
+struct _ns_flagdata _ns_flagdata[16] = {
+ { 0x8000, 15 }, /*%< qr. */
+ { 0x7800, 11 }, /*%< opcode. */
+ { 0x0400, 10 }, /*%< aa. */
+ { 0x0200, 9 }, /*%< tc. */
+ { 0x0100, 8 }, /*%< rd. */
+ { 0x0080, 7 }, /*%< ra. */
+ { 0x0040, 6 }, /*%< z. */
+ { 0x0020, 5 }, /*%< ad. */
+ { 0x0010, 4 }, /*%< cd. */
+ { 0x000f, 0 }, /*%< rcode. */
+ { 0x0000, 0 }, /*%< expansion (1/6). */
+ { 0x0000, 0 }, /*%< expansion (2/6). */
+ { 0x0000, 0 }, /*%< expansion (3/6). */
+ { 0x0000, 0 }, /*%< expansion (4/6). */
+ { 0x0000, 0 }, /*%< expansion (5/6). */
+ { 0x0000, 0 }, /*%< expansion (6/6). */
+};
+
+int ns_msg_getflag(ns_msg handle, int flag) {
+ return(((handle)._flags & _ns_flagdata[flag].mask) >> _ns_flagdata[flag].shift);
+}
+
+int
+ns_skiprr(const u_char *ptr, const u_char *eom, ns_sect section, int count) {
+ const u_char *optr = ptr;
+
+ for ((void)NULL; count > 0; count--) {
+ int b, rdlength;
+
+ b = dn_skipname(ptr, eom);
+ if (b < 0)
+ RETERR(EMSGSIZE);
+ ptr += b/*Name*/ + NS_INT16SZ/*Type*/ + NS_INT16SZ/*Class*/;
+ if (section != ns_s_qd) {
+ if (ptr + NS_INT32SZ + NS_INT16SZ > eom)
+ RETERR(EMSGSIZE);
+ ptr += NS_INT32SZ/*TTL*/;
+ NS_GET16(rdlength, ptr);
+ ptr += rdlength/*RData*/;
+ }
+ }
+ if (ptr > eom)
+ RETERR(EMSGSIZE);
+ return (ptr - optr);
+}
+
+int
+ns_initparse(const u_char *msg, int msglen, ns_msg *handle) {
+ const u_char *eom = msg + msglen;
+ int i;
+
+ handle->_msg = msg;
+ handle->_eom = eom;
+ if (msg + NS_INT16SZ > eom)
+ RETERR(EMSGSIZE);
+ NS_GET16(handle->_id, msg);
+ if (msg + NS_INT16SZ > eom)
+ RETERR(EMSGSIZE);
+ NS_GET16(handle->_flags, msg);
+ for (i = 0; i < ns_s_max; i++) {
+ if (msg + NS_INT16SZ > eom)
+ RETERR(EMSGSIZE);
+ NS_GET16(handle->_counts[i], msg);
+ }
+ for (i = 0; i < ns_s_max; i++)
+ if (handle->_counts[i] == 0)
+ handle->_sections[i] = NULL;
+ else {
+ int b = ns_skiprr(msg, eom, (ns_sect)i,
+ handle->_counts[i]);
+
+ if (b < 0)
+ return (-1);
+ handle->_sections[i] = msg;
+ msg += b;
+ }
+ if (msg != eom)
+ RETERR(EMSGSIZE);
+ setsection(handle, ns_s_max);
+ return (0);
+}
+
+int
+ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr) {
+ int b;
+ int tmp;
+
+ /* Make section right. */
+ tmp = section;
+ if (tmp < 0 || section >= ns_s_max)
+ RETERR(ENODEV);
+ if (section != handle->_sect)
+ setsection(handle, section);
+
+ /* Make rrnum right. */
+ if (rrnum == -1)
+ rrnum = handle->_rrnum;
+ if (rrnum < 0 || rrnum >= handle->_counts[(int)section])
+ RETERR(ENODEV);
+ if (rrnum < handle->_rrnum)
+ setsection(handle, section);
+ if (rrnum > handle->_rrnum) {
+ b = ns_skiprr(handle->_msg_ptr, handle->_eom, section,
+ rrnum - handle->_rrnum);
+
+ if (b < 0)
+ return (-1);
+ handle->_msg_ptr += b;
+ handle->_rrnum = rrnum;
+ }
+
+ /* Do the parse. */
+ b = dn_expand(handle->_msg, handle->_eom,
+ handle->_msg_ptr, rr->name, NS_MAXDNAME);
+ if (b < 0)
+ return (-1);
+ handle->_msg_ptr += b;
+ if (handle->_msg_ptr + NS_INT16SZ + NS_INT16SZ > handle->_eom)
+ RETERR(EMSGSIZE);
+ NS_GET16(rr->type, handle->_msg_ptr);
+ NS_GET16(rr->rr_class, handle->_msg_ptr);
+ if (section == ns_s_qd) {
+ rr->ttl = 0;
+ rr->rdlength = 0;
+ rr->rdata = NULL;
+ } else {
+ if (handle->_msg_ptr + NS_INT32SZ + NS_INT16SZ > handle->_eom)
+ RETERR(EMSGSIZE);
+ NS_GET32(rr->ttl, handle->_msg_ptr);
+ NS_GET16(rr->rdlength, handle->_msg_ptr);
+ if (handle->_msg_ptr + rr->rdlength > handle->_eom)
+ RETERR(EMSGSIZE);
+ rr->rdata = handle->_msg_ptr;
+ handle->_msg_ptr += rr->rdlength;
+ }
+ if (++handle->_rrnum > handle->_counts[(int)section])
+ setsection(handle, (ns_sect)((int)section + 1));
+
+ /* All done. */
+ return (0);
+}
+
+/*
+ * This is identical to the above but uses network-format (uncompressed) names.
+ */
+int
+ns_parserr2(ns_msg *handle, ns_sect section, int rrnum, ns_rr2 *rr) {
+ int b;
+ int tmp;
+
+ /* Make section right. */
+ if ((tmp = section) < 0 || section >= ns_s_max)
+ RETERR(ENODEV);
+ if (section != handle->_sect)
+ setsection(handle, section);
+
+ /* Make rrnum right. */
+ if (rrnum == -1)
+ rrnum = handle->_rrnum;
+ if (rrnum < 0 || rrnum >= handle->_counts[(int)section])
+ RETERR(ENODEV);
+ if (rrnum < handle->_rrnum)
+ setsection(handle, section);
+ if (rrnum > handle->_rrnum) {
+ b = ns_skiprr(handle->_msg_ptr, handle->_eom, section,
+ rrnum - handle->_rrnum);
+
+ if (b < 0)
+ return (-1);
+ handle->_msg_ptr += b;
+ handle->_rrnum = rrnum;
+ }
+
+ /* Do the parse. */
+ b = ns_name_unpack2(handle->_msg, handle->_eom, handle->_msg_ptr,
+ rr->nname, NS_MAXNNAME, &rr->nnamel);
+ if (b < 0)
+ return (-1);
+ handle->_msg_ptr += b;
+ if (handle->_msg_ptr + NS_INT16SZ + NS_INT16SZ > handle->_eom)
+ RETERR(EMSGSIZE);
+ NS_GET16(rr->type, handle->_msg_ptr);
+ NS_GET16(rr->rr_class, handle->_msg_ptr);
+ if (section == ns_s_qd) {
+ rr->ttl = 0;
+ rr->rdlength = 0;
+ rr->rdata = NULL;
+ } else {
+ if (handle->_msg_ptr + NS_INT32SZ + NS_INT16SZ > handle->_eom)
+ RETERR(EMSGSIZE);
+ NS_GET32(rr->ttl, handle->_msg_ptr);
+ NS_GET16(rr->rdlength, handle->_msg_ptr);
+ if (handle->_msg_ptr + rr->rdlength > handle->_eom)
+ RETERR(EMSGSIZE);
+ rr->rdata = handle->_msg_ptr;
+ handle->_msg_ptr += rr->rdlength;
+ }
+ if (++handle->_rrnum > handle->_counts[(int)section])
+ setsection(handle, (ns_sect)((int)section + 1));
+
+ /* All done. */
+ return (0);
+}
+
+/* Private. */
+
+static void
+setsection(ns_msg *msg, ns_sect sect) {
+ msg->_sect = sect;
+ if (sect == ns_s_max) {
+ msg->_rrnum = -1;
+ msg->_msg_ptr = NULL;
+ } else {
+ msg->_rrnum = 0;
+ msg->_msg_ptr = msg->_sections[(int)sect];
+ }
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_print.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_print.c
new file mode 100644
index 0000000000..2624c63732
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_print.c
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ * Copyright 2018 Joyent, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <isc/assertions.h>
+#include <isc/dst.h>
+#include <errno.h>
+#include <resolv_joy.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/* Forward. */
+
+static size_t prune_origin(const char *name, const char *origin);
+static int charstr(const u_char *rdata, const u_char *edata,
+ char **buf, size_t *buflen);
+static int addname(const u_char *msg, size_t msglen,
+ const u_char **p, const char *origin,
+ char **buf, size_t *buflen);
+static void addlen(size_t len, char **buf, size_t *buflen);
+static int addstr(const char *src, size_t len,
+ char **buf, size_t *buflen);
+static int addtab(size_t len, size_t target, int spaced,
+ char **buf, size_t *buflen);
+
+/* Macros. */
+
+#define T(x) \
+ do { \
+ if ((x) < 0) \
+ return (-1); \
+ } while (0)
+
+static const char base32hex[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUV=0123456789abcdefghijklmnopqrstuv";
+
+/* Public. */
+
+/*%
+ * Convert an RR to presentation format.
+ *
+ * return:
+ *\li Number of characters written to buf, or -1 (check errno).
+ */
+int
+ns_sprintrr(const ns_msg *handle, const ns_rr *rr,
+ const char *name_ctx, const char *origin,
+ char *buf, size_t buflen)
+{
+ int n;
+
+ n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle),
+ ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr),
+ ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr),
+ name_ctx, origin, buf, buflen);
+ return (n);
+}
+
+/*%
+ * Convert the fields of an RR into presentation format.
+ *
+ * return:
+ *\li Number of characters written to buf, or -1 (check errno).
+ */
+int
+ns_sprintrrf(const u_char *msg, size_t msglen,
+ const char *name, ns_class class, ns_type type,
+ u_long ttl, const u_char *rdata, size_t rdlen,
+ const char *name_ctx, const char *origin,
+ char *buf, size_t buflen)
+{
+ const char *obuf = buf;
+ const u_char *edata = rdata + rdlen;
+ int spaced = 0;
+
+ const char *comment;
+ char tmp[100];
+ int len, x;
+
+ /*
+ * Owner.
+ */
+ if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) {
+ T(addstr("\t\t\t", 3, &buf, &buflen));
+ } else {
+ len = prune_origin(name, origin);
+ if (*name == '\0') {
+ goto root;
+ } else if (len == 0) {
+ T(addstr("@\t\t\t", 4, &buf, &buflen));
+ } else {
+ T(addstr(name, len, &buf, &buflen));
+ /* Origin not used or not root, and no trailing dot? */
+ if (((origin == NULL || origin[0] == '\0') ||
+ (origin[0] != '.' && origin[1] != '\0' &&
+ name[len] == '\0')) && name[len - 1] != '.') {
+ root:
+ T(addstr(".", 1, &buf, &buflen));
+ len++;
+ }
+ T(spaced = addtab(len, 24, spaced, &buf, &buflen));
+ }
+ }
+
+ /*
+ * TTL, Class, Type.
+ */
+ T(x = ns_format_ttl(ttl, buf, buflen));
+ addlen(x, &buf, &buflen);
+ len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type)));
+ T(addstr(tmp, len, &buf, &buflen));
+ T(spaced = addtab(x + len, 16, spaced, &buf, &buflen));
+
+ /*
+ * RData.
+ */
+ switch (type) {
+ case ns_t_a:
+ if (rdlen != (size_t)NS_INADDRSZ)
+ goto formerr;
+ (void) inet_ntop(AF_INET, rdata, buf, buflen);
+ addlen(strlen(buf), &buf, &buflen);
+ break;
+
+ case ns_t_cname:
+ case ns_t_mb:
+ case ns_t_mg:
+ case ns_t_mr:
+ case ns_t_ns:
+ case ns_t_ptr:
+ case ns_t_dname:
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+ break;
+
+ case ns_t_hinfo:
+ case ns_t_isdn:
+ /* First word. */
+ T(len = charstr(rdata, edata, &buf, &buflen));
+ if (len == 0)
+ goto formerr;
+ rdata += len;
+ T(addstr(" ", 1, &buf, &buflen));
+
+
+ /* Second word, optional in ISDN records. */
+ if (type == ns_t_isdn && rdata == edata)
+ break;
+
+ T(len = charstr(rdata, edata, &buf, &buflen));
+ if (len == 0)
+ goto formerr;
+ rdata += len;
+ break;
+
+ case ns_t_soa: {
+ u_long t;
+
+ /* Server name. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+ T(addstr(" ", 1, &buf, &buflen));
+
+ /* Administrator name. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+ T(addstr(" (\n", 3, &buf, &buflen));
+ spaced = 0;
+
+ if ((edata - rdata) != 5*NS_INT32SZ)
+ goto formerr;
+
+ /* Serial number. */
+ t = ns_get32(rdata); rdata += NS_INT32SZ;
+ T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
+ len = SPRINTF((tmp, "%lu", t));
+ T(addstr(tmp, len, &buf, &buflen));
+ T(spaced = addtab(len, 16, spaced, &buf, &buflen));
+ T(addstr("; serial\n", 9, &buf, &buflen));
+ spaced = 0;
+
+ /* Refresh interval. */
+ t = ns_get32(rdata); rdata += NS_INT32SZ;
+ T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
+ T(len = ns_format_ttl(t, buf, buflen));
+ addlen(len, &buf, &buflen);
+ T(spaced = addtab(len, 16, spaced, &buf, &buflen));
+ T(addstr("; refresh\n", 10, &buf, &buflen));
+ spaced = 0;
+
+ /* Retry interval. */
+ t = ns_get32(rdata); rdata += NS_INT32SZ;
+ T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
+ T(len = ns_format_ttl(t, buf, buflen));
+ addlen(len, &buf, &buflen);
+ T(spaced = addtab(len, 16, spaced, &buf, &buflen));
+ T(addstr("; retry\n", 8, &buf, &buflen));
+ spaced = 0;
+
+ /* Expiry. */
+ t = ns_get32(rdata); rdata += NS_INT32SZ;
+ T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
+ T(len = ns_format_ttl(t, buf, buflen));
+ addlen(len, &buf, &buflen);
+ T(spaced = addtab(len, 16, spaced, &buf, &buflen));
+ T(addstr("; expiry\n", 9, &buf, &buflen));
+ spaced = 0;
+
+ /* Minimum TTL. */
+ t = ns_get32(rdata); rdata += NS_INT32SZ;
+ T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
+ T(len = ns_format_ttl(t, buf, buflen));
+ addlen(len, &buf, &buflen);
+ T(addstr(" )", 2, &buf, &buflen));
+ T(spaced = addtab(len, 16, spaced, &buf, &buflen));
+ T(addstr("; minimum\n", 10, &buf, &buflen));
+
+ break;
+ }
+
+ case ns_t_mx:
+ case ns_t_afsdb:
+ case ns_t_rt:
+ case ns_t_kx: {
+ u_int t;
+
+ if (rdlen < (size_t)NS_INT16SZ)
+ goto formerr;
+
+ /* Priority. */
+ t = ns_get16(rdata);
+ rdata += NS_INT16SZ;
+ len = SPRINTF((tmp, "%u ", t));
+ T(addstr(tmp, len, &buf, &buflen));
+
+ /* Target. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+ break;
+ }
+
+ case ns_t_px: {
+ u_int t;
+
+ if (rdlen < (size_t)NS_INT16SZ)
+ goto formerr;
+
+ /* Priority. */
+ t = ns_get16(rdata);
+ rdata += NS_INT16SZ;
+ len = SPRINTF((tmp, "%u ", t));
+ T(addstr(tmp, len, &buf, &buflen));
+
+ /* Name1. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+ T(addstr(" ", 1, &buf, &buflen));
+
+ /* Name2. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+ break;
+ }
+
+ case ns_t_x25:
+ T(len = charstr(rdata, edata, &buf, &buflen));
+ if (len == 0)
+ goto formerr;
+ rdata += len;
+ break;
+
+ case ns_t_txt:
+ case ns_t_spf:
+ while (rdata < edata) {
+ T(len = charstr(rdata, edata, &buf, &buflen));
+ if (len == 0)
+ goto formerr;
+ rdata += len;
+ if (rdata < edata)
+ T(addstr(" ", 1, &buf, &buflen));
+ }
+ break;
+
+ case ns_t_nsap: {
+ char t[2+255*3];
+
+ (void) inet_nsap_ntoa(rdlen, rdata, t);
+ T(addstr(t, strlen(t), &buf, &buflen));
+ break;
+ }
+
+ case ns_t_aaaa:
+ if (rdlen != (size_t)NS_IN6ADDRSZ)
+ goto formerr;
+ (void) inet_ntop(AF_INET6, rdata, buf, buflen);
+ addlen(strlen(buf), &buf, &buflen);
+ break;
+
+ case ns_t_loc: {
+ char t[255];
+
+ /* XXX protocol format checking? */
+ (void) loc_ntoa(rdata, t);
+ T(addstr(t, strlen(t), &buf, &buflen));
+ break;
+ }
+
+ case ns_t_naptr: {
+ u_int order, preference;
+ char t[50];
+
+ if (rdlen < 2U*NS_INT16SZ)
+ goto formerr;
+
+ /* Order, Precedence. */
+ order = ns_get16(rdata); rdata += NS_INT16SZ;
+ preference = ns_get16(rdata); rdata += NS_INT16SZ;
+ len = SPRINTF((t, "%u %u ", order, preference));
+ T(addstr(t, len, &buf, &buflen));
+
+ /* Flags. */
+ T(len = charstr(rdata, edata, &buf, &buflen));
+ if (len == 0)
+ goto formerr;
+ rdata += len;
+ T(addstr(" ", 1, &buf, &buflen));
+
+ /* Service. */
+ T(len = charstr(rdata, edata, &buf, &buflen));
+ if (len == 0)
+ goto formerr;
+ rdata += len;
+ T(addstr(" ", 1, &buf, &buflen));
+
+ /* Regexp. */
+ T(len = charstr(rdata, edata, &buf, &buflen));
+ if (len < 0)
+ return (-1);
+ if (len == 0)
+ goto formerr;
+ rdata += len;
+ T(addstr(" ", 1, &buf, &buflen));
+
+ /* Server. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+ break;
+ }
+
+ case ns_t_srv: {
+ u_int priority, weight, port;
+ char t[50];
+
+ if (rdlen < 3U*NS_INT16SZ)
+ goto formerr;
+
+ /* Priority, Weight, Port. */
+ priority = ns_get16(rdata); rdata += NS_INT16SZ;
+ weight = ns_get16(rdata); rdata += NS_INT16SZ;
+ port = ns_get16(rdata); rdata += NS_INT16SZ;
+ len = SPRINTF((t, "%u %u %u ", priority, weight, port));
+ T(addstr(t, len, &buf, &buflen));
+
+ /* Server. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+ break;
+ }
+
+ case ns_t_minfo:
+ case ns_t_rp:
+ /* Name1. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+ T(addstr(" ", 1, &buf, &buflen));
+
+ /* Name2. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+ break;
+
+ case ns_t_wks: {
+ int n, lcnt;
+
+ if (rdlen < 1U + NS_INT32SZ)
+ goto formerr;
+
+ /* Address. */
+ (void) inet_ntop(AF_INET, rdata, buf, buflen);
+ addlen(strlen(buf), &buf, &buflen);
+ rdata += NS_INADDRSZ;
+
+ /* Protocol. */
+ len = SPRINTF((tmp, " %u ( ", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata += NS_INT8SZ;
+
+ /* Bit map. */
+ n = 0;
+ lcnt = 0;
+ while (rdata < edata) {
+ u_int c = *rdata++;
+ do {
+ if (c & 0200) {
+ if (lcnt == 0) {
+ T(addstr("\n\t\t\t\t", 5,
+ &buf, &buflen));
+ lcnt = 10;
+ spaced = 0;
+ }
+ len = SPRINTF((tmp, "%d ", n));
+ T(addstr(tmp, len, &buf, &buflen));
+ lcnt--;
+ }
+ c <<= 1;
+ } while (++n & 07);
+ }
+ T(addstr(")", 1, &buf, &buflen));
+
+ break;
+ }
+
+ case ns_t_key:
+ case ns_t_dnskey: {
+ char base64_key[NS_MD5RSA_MAX_BASE64];
+ u_int keyflags, protocol, algorithm, key_id;
+ const char *leader;
+ int n;
+
+ if (rdlen < 0U + NS_INT16SZ + NS_INT8SZ + NS_INT8SZ)
+ goto formerr;
+
+ /* Key flags, Protocol, Algorithm. */
+ key_id = dst_s_dns_key_id(rdata, edata-rdata);
+ keyflags = ns_get16(rdata); rdata += NS_INT16SZ;
+ protocol = *rdata++;
+ algorithm = *rdata++;
+ len = SPRINTF((tmp, "0x%04x %u %u",
+ keyflags, protocol, algorithm));
+ T(addstr(tmp, len, &buf, &buflen));
+
+ /* Public key data. */
+ len = b64_ntop(rdata, edata - rdata,
+ base64_key, sizeof base64_key);
+ if (len < 0)
+ goto formerr;
+ if (len > 15) {
+ T(addstr(" (", 2, &buf, &buflen));
+ leader = "\n\t\t";
+ spaced = 0;
+ } else
+ leader = " ";
+ for (n = 0; n < len; n += 48) {
+ T(addstr(leader, strlen(leader), &buf, &buflen));
+ T(addstr(base64_key + n, MIN(len - n, 48),
+ &buf, &buflen));
+ }
+ if (len > 15)
+ T(addstr(" )", 2, &buf, &buflen));
+ n = SPRINTF((tmp, " ; key_tag= %u", key_id));
+ T(addstr(tmp, n, &buf, &buflen));
+
+ break;
+ }
+
+ case ns_t_sig:
+ case ns_t_rrsig: {
+ char base64_key[NS_MD5RSA_MAX_BASE64];
+ u_int type, algorithm, labels, footprint;
+ const char *leader;
+ u_long t;
+ int n;
+
+ if (rdlen < 22U)
+ goto formerr;
+
+ /* Type covered, Algorithm, Label count, Original TTL. */
+ type = ns_get16(rdata); rdata += NS_INT16SZ;
+ algorithm = *rdata++;
+ labels = *rdata++;
+ t = ns_get32(rdata); rdata += NS_INT32SZ;
+ len = SPRINTF((tmp, "%s %d %d %lu ",
+ p_type(type), algorithm, labels, t));
+ T(addstr(tmp, len, &buf, &buflen));
+ if (labels > (u_int)dn_count_labels(name))
+ goto formerr;
+
+ /* Signature expiry. */
+ t = ns_get32(rdata); rdata += NS_INT32SZ;
+ len = SPRINTF((tmp, "%s ", p_secstodate(t)));
+ T(addstr(tmp, len, &buf, &buflen));
+
+ /* Time signed. */
+ t = ns_get32(rdata); rdata += NS_INT32SZ;
+ len = SPRINTF((tmp, "%s ", p_secstodate(t)));
+ T(addstr(tmp, len, &buf, &buflen));
+
+ /* Signature Footprint. */
+ footprint = ns_get16(rdata); rdata += NS_INT16SZ;
+ len = SPRINTF((tmp, "%u ", footprint));
+ T(addstr(tmp, len, &buf, &buflen));
+
+ /* Signer's name. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+ /* Signature. */
+ len = b64_ntop(rdata, edata - rdata,
+ base64_key, sizeof base64_key);
+ if (len > 15) {
+ T(addstr(" (", 2, &buf, &buflen));
+ leader = "\n\t\t";
+ spaced = 0;
+ } else
+ leader = " ";
+ if (len < 0)
+ goto formerr;
+ for (n = 0; n < len; n += 48) {
+ T(addstr(leader, strlen(leader), &buf, &buflen));
+ T(addstr(base64_key + n, MIN(len - n, 48),
+ &buf, &buflen));
+ }
+ if (len > 15)
+ T(addstr(" )", 2, &buf, &buflen));
+ break;
+ }
+
+ case ns_t_nxt: {
+ int n, c;
+
+ /* Next domain name. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+ /* Type bit map. */
+ n = edata - rdata;
+ for (c = 0; c < n*8; c++)
+ if (NS_NXT_BIT_ISSET(c, rdata)) {
+ len = SPRINTF((tmp, " %s", p_type(c)));
+ T(addstr(tmp, len, &buf, &buflen));
+ }
+ break;
+ }
+
+ case ns_t_cert: {
+ u_int c_type, key_tag, alg;
+ int n;
+ unsigned int siz;
+ char base64_cert[8192], tmp[40];
+ const char *leader;
+
+ c_type = ns_get16(rdata); rdata += NS_INT16SZ;
+ key_tag = ns_get16(rdata); rdata += NS_INT16SZ;
+ alg = (u_int) *rdata++;
+
+ len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg));
+ T(addstr(tmp, len, &buf, &buflen));
+ siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
+ if (siz > sizeof(base64_cert) * 3/4) {
+ const char *str = "record too long to print";
+ T(addstr(str, strlen(str), &buf, &buflen));
+ }
+ else {
+ len = b64_ntop(rdata, edata-rdata, base64_cert, siz);
+
+ if (len < 0)
+ goto formerr;
+ else if (len > 15) {
+ T(addstr(" (", 2, &buf, &buflen));
+ leader = "\n\t\t";
+ spaced = 0;
+ }
+ else
+ leader = " ";
+
+ for (n = 0; n < len; n += 48) {
+ T(addstr(leader, strlen(leader),
+ &buf, &buflen));
+ T(addstr(base64_cert + n, MIN(len - n, 48),
+ &buf, &buflen));
+ }
+ if (len > 15)
+ T(addstr(" )", 2, &buf, &buflen));
+ }
+ break;
+ }
+
+ case ns_t_tkey: {
+ /* KJD - need to complete this */
+ u_long t;
+ int mode, err, keysize;
+
+ /* Algorithm name. */
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+ T(addstr(" ", 1, &buf, &buflen));
+
+ /* Inception. */
+ t = ns_get32(rdata); rdata += NS_INT32SZ;
+ len = SPRINTF((tmp, "%s ", p_secstodate(t)));
+ T(addstr(tmp, len, &buf, &buflen));
+
+ /* Experation. */
+ t = ns_get32(rdata); rdata += NS_INT32SZ;
+ len = SPRINTF((tmp, "%s ", p_secstodate(t)));
+ T(addstr(tmp, len, &buf, &buflen));
+
+ /* Mode , Error, Key Size. */
+ /* Priority, Weight, Port. */
+ mode = ns_get16(rdata); rdata += NS_INT16SZ;
+ err = ns_get16(rdata); rdata += NS_INT16SZ;
+ keysize = ns_get16(rdata); rdata += NS_INT16SZ;
+ len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize));
+ T(addstr(tmp, len, &buf, &buflen));
+
+ /* XXX need to dump key, print otherdata length & other data */
+ break;
+ }
+
+ case ns_t_tsig: {
+ /* BEW - need to complete this */
+ int n;
+
+ T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen));
+ T(addstr(" ", 1, &buf, &buflen));
+ rdata += 8; /*%< time */
+ n = ns_get16(rdata); rdata += INT16SZ;
+ rdata += n; /*%< sig */
+ n = ns_get16(rdata); rdata += INT16SZ; /*%< original id */
+ sprintf(buf, "%d", ns_get16(rdata));
+ rdata += INT16SZ;
+ addlen(strlen(buf), &buf, &buflen);
+ break;
+ }
+
+ case ns_t_a6: {
+ struct in6_addr a;
+ int pbyte, pbit;
+
+ /* prefix length */
+ if (rdlen == 0U) goto formerr;
+ len = SPRINTF((tmp, "%d ", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ pbit = *rdata;
+ if (pbit > 128) goto formerr;
+ pbyte = (pbit & ~7) / 8;
+ rdata++;
+
+ /* address suffix: provided only when prefix len != 128 */
+ if (pbit < 128) {
+ if (rdata + pbyte >= edata) goto formerr;
+ memset(&a, 0, sizeof(a));
+ memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte);
+ (void) inet_ntop(AF_INET6, &a, buf, buflen);
+ addlen(strlen(buf), &buf, &buflen);
+ rdata += sizeof(a) - pbyte;
+ }
+
+ /* prefix name: provided only when prefix len > 0 */
+ if (pbit == 0)
+ break;
+ if (rdata >= edata) goto formerr;
+ T(addstr(" ", 1, &buf, &buflen));
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+ break;
+ }
+
+ case ns_t_opt: {
+ len = SPRINTF((tmp, "%u bytes", class));
+ T(addstr(tmp, len, &buf, &buflen));
+ break;
+ }
+
+ case ns_t_ds:
+ case ns_t_dlv:
+ case ns_t_sshfp: {
+ u_int t;
+
+ if (type == ns_t_ds || type == ns_t_dlv) {
+ if (rdlen < 4U) goto formerr;
+ t = ns_get16(rdata);
+ rdata += NS_INT16SZ;
+ len = SPRINTF((tmp, "%u ", t));
+ T(addstr(tmp, len, &buf, &buflen));
+ } else
+ if (rdlen < 2U) goto formerr;
+
+ len = SPRINTF((tmp, "%u ", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata++;
+
+ len = SPRINTF((tmp, "%u ", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata++;
+
+ while (rdata < edata) {
+ len = SPRINTF((tmp, "%02X", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata++;
+ }
+ break;
+ }
+
+ case ns_t_nsec3:
+ case ns_t_nsec3param: {
+ u_int t, w, l, j, k, c;
+
+ len = SPRINTF((tmp, "%u ", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata++;
+
+ len = SPRINTF((tmp, "%u ", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata++;
+
+ t = ns_get16(rdata);
+ rdata += NS_INT16SZ;
+ len = SPRINTF((tmp, "%u ", t));
+ T(addstr(tmp, len, &buf, &buflen));
+
+ t = *rdata++;
+ if (t == 0) {
+ T(addstr("-", 1, &buf, &buflen));
+ } else {
+ while (t-- > 0) {
+ len = SPRINTF((tmp, "%02X", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata++;
+ }
+ }
+ if (type == ns_t_nsec3param)
+ break;
+ T(addstr(" ", 1, &buf, &buflen));
+
+ t = *rdata++;
+ while (t > 0) {
+ switch (t) {
+ case 1:
+ tmp[0] = base32hex[((rdata[0]>>3)&0x1f)];
+ tmp[1] = base32hex[((rdata[0]<<2)&0x1c)];
+ tmp[2] = tmp[3] = tmp[4] = '=';
+ tmp[5] = tmp[6] = tmp[7] = '=';
+ break;
+ case 2:
+ tmp[0] = base32hex[((rdata[0]>>3)&0x1f)];
+ tmp[1] = base32hex[((rdata[0]<<2)&0x1c)|
+ ((rdata[1]>>6)&0x03)];
+ tmp[2] = base32hex[((rdata[1]>>1)&0x1f)];
+ tmp[3] = base32hex[((rdata[1]<<4)&0x10)];
+ tmp[4] = tmp[5] = tmp[6] = tmp[7] = '=';
+ break;
+ case 3:
+ tmp[0] = base32hex[((rdata[0]>>3)&0x1f)];
+ tmp[1] = base32hex[((rdata[0]<<2)&0x1c)|
+ ((rdata[1]>>6)&0x03)];
+ tmp[2] = base32hex[((rdata[1]>>1)&0x1f)];
+ tmp[3] = base32hex[((rdata[1]<<4)&0x10)|
+ ((rdata[2]>>4)&0x0f)];
+ tmp[4] = base32hex[((rdata[2]<<1)&0x1e)];
+ tmp[5] = tmp[6] = tmp[7] = '=';
+ break;
+ case 4:
+ tmp[0] = base32hex[((rdata[0]>>3)&0x1f)];
+ tmp[1] = base32hex[((rdata[0]<<2)&0x1c)|
+ ((rdata[1]>>6)&0x03)];
+ tmp[2] = base32hex[((rdata[1]>>1)&0x1f)];
+ tmp[3] = base32hex[((rdata[1]<<4)&0x10)|
+ ((rdata[2]>>4)&0x0f)];
+ tmp[4] = base32hex[((rdata[2]<<1)&0x1e)|
+ ((rdata[3]>>7)&0x01)];
+ tmp[5] = base32hex[((rdata[3]>>2)&0x1f)];
+ tmp[6] = base32hex[(rdata[3]<<3)&0x18];
+ tmp[7] = '=';
+ break;
+ default:
+ tmp[0] = base32hex[((rdata[0]>>3)&0x1f)];
+ tmp[1] = base32hex[((rdata[0]<<2)&0x1c)|
+ ((rdata[1]>>6)&0x03)];
+ tmp[2] = base32hex[((rdata[1]>>1)&0x1f)];
+ tmp[3] = base32hex[((rdata[1]<<4)&0x10)|
+ ((rdata[2]>>4)&0x0f)];
+ tmp[4] = base32hex[((rdata[2]<<1)&0x1e)|
+ ((rdata[3]>>7)&0x01)];
+ tmp[5] = base32hex[((rdata[3]>>2)&0x1f)];
+ tmp[6] = base32hex[((rdata[3]<<3)&0x18)|
+ ((rdata[4]>>5)&0x07)];
+ tmp[7] = base32hex[(rdata[4]&0x1f)];
+ break;
+ }
+ T(addstr(tmp, 8, &buf, &buflen));
+ if (t >= 5) {
+ rdata += 5;
+ t -= 5;
+ } else {
+ rdata += t;
+ t -= t;
+ }
+ }
+
+ while (rdata < edata) {
+ w = *rdata++;
+ l = *rdata++;
+ for (j = 0; j < l; j++) {
+ if (rdata[j] == 0)
+ continue;
+ for (k = 0; k < 8; k++) {
+ if ((rdata[j] & (0x80 >> k)) == 0)
+ continue;
+ c = w * 256 + j * 8 + k;
+ len = SPRINTF((tmp, " %s", p_type(c)));
+ T(addstr(tmp, len, &buf, &buflen));
+ }
+ }
+ rdata += l;
+ }
+ break;
+ }
+
+ case ns_t_nsec: {
+ u_int w, l, j, k, c;
+
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+
+ while (rdata < edata) {
+ w = *rdata++;
+ l = *rdata++;
+ for (j = 0; j < l; j++) {
+ if (rdata[j] == 0)
+ continue;
+ for (k = 0; k < 8; k++) {
+ if ((rdata[j] & (0x80 >> k)) == 0)
+ continue;
+ c = w * 256 + j * 8 + k;
+ len = SPRINTF((tmp, " %s", p_type(c)));
+ T(addstr(tmp, len, &buf, &buflen));
+ }
+ }
+ rdata += l;
+ }
+ break;
+ }
+
+ case ns_t_dhcid: {
+ int n;
+ unsigned int siz;
+ char base64_dhcid[8192];
+ const char *leader;
+
+ siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
+ if (siz > sizeof(base64_dhcid) * 3/4) {
+ const char *str = "record too long to print";
+ T(addstr(str, strlen(str), &buf, &buflen));
+ } else {
+ len = b64_ntop(rdata, edata-rdata, base64_dhcid, siz);
+
+ if (len < 0)
+ goto formerr;
+
+ else if (len > 15) {
+ T(addstr(" (", 2, &buf, &buflen));
+ leader = "\n\t\t";
+ spaced = 0;
+ }
+ else
+ leader = " ";
+
+ for (n = 0; n < len; n += 48) {
+ T(addstr(leader, strlen(leader),
+ &buf, &buflen));
+ T(addstr(base64_dhcid + n, MIN(len - n, 48),
+ &buf, &buflen));
+ }
+ if (len > 15)
+ T(addstr(" )", 2, &buf, &buflen));
+ }
+ break;
+ }
+
+ case ns_t_ipseckey: {
+ int n;
+ unsigned int siz;
+ char base64_key[8192];
+ const char *leader;
+
+ if (rdlen < 2)
+ goto formerr;
+
+ switch (rdata[1]) {
+ case 0:
+ case 3:
+ if (rdlen < 3)
+ goto formerr;
+ break;
+ case 1:
+ if (rdlen < 7)
+ goto formerr;
+ break;
+ case 2:
+ if (rdlen < 19)
+ goto formerr;
+ break;
+ default:
+ comment = "unknown IPSECKEY gateway type";
+ goto hexify;
+ }
+
+ len = SPRINTF((tmp, "%u ", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata++;
+
+ len = SPRINTF((tmp, "%u ", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata++;
+
+ len = SPRINTF((tmp, "%u ", *rdata));
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata++;
+
+ switch (rdata[-2]) {
+ case 0:
+ T(addstr(".", 1, &buf, &buflen));
+ break;
+ case 1:
+ (void) inet_ntop(AF_INET, rdata, buf, buflen);
+ addlen(strlen(buf), &buf, &buflen);
+ rdata += 4;
+ break;
+ case 2:
+ (void) inet_ntop(AF_INET6, rdata, buf, buflen);
+ addlen(strlen(buf), &buf, &buflen);
+ rdata += 16;
+ break;
+ case 3:
+ T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
+ break;
+ }
+
+ if (rdata >= edata)
+ break;
+
+ siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
+ if (siz > sizeof(base64_key) * 3/4) {
+ const char *str = "record too long to print";
+ T(addstr(str, strlen(str), &buf, &buflen));
+ } else {
+ len = b64_ntop(rdata, edata-rdata, base64_key, siz);
+
+ if (len < 0)
+ goto formerr;
+
+ else if (len > 15) {
+ T(addstr(" (", 2, &buf, &buflen));
+ leader = "\n\t\t";
+ spaced = 0;
+ }
+ else
+ leader = " ";
+
+ for (n = 0; n < len; n += 48) {
+ T(addstr(leader, strlen(leader),
+ &buf, &buflen));
+ T(addstr(base64_key + n, MIN(len - n, 48),
+ &buf, &buflen));
+ }
+ if (len > 15)
+ T(addstr(" )", 2, &buf, &buflen));
+ }
+ break;
+ }
+
+ case ns_t_hip: {
+ unsigned int i, hip_len, algorithm, key_len;
+ char base64_key[NS_MD5RSA_MAX_BASE64];
+ unsigned int siz;
+ const char *leader = "\n\t\t\t\t\t";
+
+ hip_len = *rdata++;
+ algorithm = *rdata++;
+ key_len = ns_get16(rdata);
+ rdata += NS_INT16SZ;
+
+ siz = key_len*4/3 + 4; /* "+4" accounts for trailing \0 */
+ if (siz > sizeof(base64_key) * 3/4) {
+ const char *str = "record too long to print";
+ T(addstr(str, strlen(str), &buf, &buflen));
+ } else {
+ len = sprintf(tmp, "( %u ", algorithm);
+ T(addstr(tmp, len, &buf, &buflen));
+
+ for (i = 0; i < hip_len; i++) {
+ len = sprintf(tmp, "%02X", *rdata);
+ T(addstr(tmp, len, &buf, &buflen));
+ rdata++;
+ }
+ T(addstr(leader, strlen(leader), &buf, &buflen));
+
+ len = b64_ntop(rdata, key_len, base64_key, siz);
+ if (len < 0)
+ goto formerr;
+
+ T(addstr(base64_key, len, &buf, &buflen));
+
+ rdata += key_len;
+ while (rdata < edata) {
+ T(addstr(leader, strlen(leader), &buf, &buflen));
+ T(addname(msg, msglen, &rdata, origin,
+ &buf, &buflen));
+ }
+ T(addstr(" )", 2, &buf, &buflen));
+ }
+ break;
+ }
+
+ default:
+ comment = "unknown RR type";
+ goto hexify;
+ }
+ return (buf - obuf);
+ formerr:
+ comment = "RR format error";
+ hexify: {
+ int n, m;
+ char *p;
+
+ len = SPRINTF((tmp, "\\# %u%s\t; %s", (unsigned)(edata - rdata),
+ rdlen != 0U ? " (" : "", comment));
+ T(addstr(tmp, len, &buf, &buflen));
+ while (rdata < edata) {
+ p = tmp;
+ p += SPRINTF((p, "\n\t"));
+ spaced = 0;
+ n = MIN(16, edata - rdata);
+ for (m = 0; m < n; m++)
+ p += SPRINTF((p, "%02x ", rdata[m]));
+ T(addstr(tmp, p - tmp, &buf, &buflen));
+ if (n < 16) {
+ T(addstr(")", 1, &buf, &buflen));
+ T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen));
+ }
+ p = tmp;
+ p += SPRINTF((p, "; "));
+ for (m = 0; m < n; m++)
+ *p++ = (isascii(rdata[m]) && isprint(rdata[m]))
+ ? rdata[m]
+ : '.';
+ T(addstr(tmp, p - tmp, &buf, &buflen));
+ rdata += n;
+ }
+ return (buf - obuf);
+ }
+}
+
+/* Private. */
+
+/*%
+ * size_t
+ * prune_origin(name, origin)
+ * Find out if the name is at or under the current origin.
+ * return:
+ * Number of characters in name before start of origin,
+ * or length of name if origin does not match.
+ * notes:
+ * This function should share code with samedomain().
+ */
+static size_t
+prune_origin(const char *name, const char *origin) {
+ const char *oname = name;
+
+ while (*name != '\0') {
+ if (origin != NULL && ns_samename(name, origin) == 1)
+ return (name - oname - (name > oname));
+ while (*name != '\0') {
+ if (*name == '\\') {
+ name++;
+ /* XXX need to handle \nnn form. */
+ if (*name == '\0')
+ break;
+ } else if (*name == '.') {
+ name++;
+ break;
+ }
+ name++;
+ }
+ }
+ return (name - oname);
+}
+
+/*%
+ * int
+ * charstr(rdata, edata, buf, buflen)
+ * Format a <character-string> into the presentation buffer.
+ * return:
+ * Number of rdata octets consumed
+ * 0 for protocol format error
+ * -1 for output buffer error
+ * side effects:
+ * buffer is advanced on success.
+ */
+static int
+charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) {
+ const u_char *odata = rdata;
+ size_t save_buflen = *buflen;
+ char *save_buf = *buf;
+
+ if (addstr("\"", 1, buf, buflen) < 0)
+ goto enospc;
+ if (rdata < edata) {
+ int n = *rdata;
+
+ if (rdata + 1 + n <= edata) {
+ rdata++;
+ while (n-- > 0) {
+ if (strchr("\n\"\\", *rdata) != NULL)
+ if (addstr("\\", 1, buf, buflen) < 0)
+ goto enospc;
+ if (addstr((const char *)rdata, 1,
+ buf, buflen) < 0)
+ goto enospc;
+ rdata++;
+ }
+ }
+ }
+ if (addstr("\"", 1, buf, buflen) < 0)
+ goto enospc;
+ return (rdata - odata);
+ enospc:
+ errno = ENOSPC;
+ *buf = save_buf;
+ *buflen = save_buflen;
+ return (-1);
+}
+
+static int
+addname(const u_char *msg, size_t msglen,
+ const u_char **pp, const char *origin,
+ char **buf, size_t *buflen)
+{
+ size_t newlen, save_buflen = *buflen;
+ char *save_buf = *buf;
+ int n;
+
+ n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen);
+ if (n < 0)
+ goto enospc; /*%< Guess. */
+ newlen = prune_origin(*buf, origin);
+ if (**buf == '\0') {
+ goto root;
+ } else if (newlen == 0U) {
+ /* Use "@" instead of name. */
+ if (newlen + 2 > *buflen)
+ goto enospc; /* No room for "@\0". */
+ (*buf)[newlen++] = '@';
+ (*buf)[newlen] = '\0';
+ } else {
+ if (((origin == NULL || origin[0] == '\0') ||
+ (origin[0] != '.' && origin[1] != '\0' &&
+ (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') {
+ /* No trailing dot. */
+ root:
+ if (newlen + 2 > *buflen)
+ goto enospc; /* No room for ".\0". */
+ (*buf)[newlen++] = '.';
+ (*buf)[newlen] = '\0';
+ }
+ }
+ *pp += n;
+ addlen(newlen, buf, buflen);
+ **buf = '\0';
+ return (newlen);
+ enospc:
+ errno = ENOSPC;
+ *buf = save_buf;
+ *buflen = save_buflen;
+ return (-1);
+}
+
+static void
+addlen(size_t len, char **buf, size_t *buflen) {
+ INSIST(len <= *buflen);
+ *buf += len;
+ *buflen -= len;
+}
+
+static int
+addstr(const char *src, size_t len, char **buf, size_t *buflen) {
+ if (len >= *buflen) {
+ errno = ENOSPC;
+ return (-1);
+ }
+ memcpy(*buf, src, len);
+ addlen(len, buf, buflen);
+ **buf = '\0';
+ return (0);
+}
+
+static int
+addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) {
+ size_t save_buflen = *buflen;
+ char *save_buf = *buf;
+ int t;
+
+ if (spaced || len >= target - 1) {
+ T(addstr(" ", 2, buf, buflen));
+ spaced = 1;
+ } else {
+ for (t = (target - len - 1) / 8; t >= 0; t--)
+ if (addstr("\t", 1, buf, buflen) < 0) {
+ *buflen = save_buflen;
+ *buf = save_buf;
+ return (-1);
+ }
+ spaced = 0;
+ }
+ return (spaced);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_rdata.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_rdata.c
new file mode 100644
index 0000000000..76617de9ad
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_rdata.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#if __OpenBSD__
+#include <sys/types.h>
+#endif
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <resolv_joy.h>
+#include <string.h>
+
+#include "port_after.h"
+
+#define CONSUME_SRC \
+ do { \
+ rdata += n, rdlen -= n; \
+ } while (0)
+
+#define CONSUME_DST \
+ do { \
+ nrdata += n, nrdsiz -= n, nrdlen += n; \
+ } while (0)
+
+#define UNPACK_DNAME \
+ do { \
+ size_t t; \
+ \
+ if ((n = ns_name_unpack2(msg,eom,rdata,nrdata,nrdsiz,&t))<0) {\
+ errno = EMSGSIZE; \
+ return (-1); \
+ } \
+ CONSUME_SRC; \
+ n = t; \
+ CONSUME_DST; \
+ } while (0)
+
+#define UNPACK_SOME(x) \
+ do { \
+ n = (x); \
+ if ((size_t)n > rdlen || (size_t)n > nrdsiz) { \
+ errno = EMSGSIZE; \
+ return (-1); \
+ } \
+ memcpy(nrdata, rdata, n); \
+ CONSUME_SRC; CONSUME_DST; \
+ } while (0)
+
+#define UNPACK_REST(x) \
+ do { \
+ n = (x); \
+ if ((size_t)n != rdlen) { \
+ errno = EMSGSIZE; \
+ return (-1); \
+ } \
+ memcpy(nrdata, rdata, n); \
+ CONSUME_SRC; CONSUME_DST; \
+ } while (0)
+
+ssize_t
+ns_rdata_unpack(const u_char *msg, const u_char *eom,
+ ns_type type, const u_char *rdata, size_t rdlen,
+ u_char *nrdata, size_t nrdsiz)
+{
+ size_t nrdlen = 0;
+ int n;
+
+ switch (type) {
+ case ns_t_a:
+ UNPACK_REST(NS_INADDRSZ);
+ break;
+ case ns_t_aaaa:
+ UNPACK_REST(NS_IN6ADDRSZ);
+ break;
+ case ns_t_cname:
+ case ns_t_mb:
+ case ns_t_mg:
+ case ns_t_mr:
+ case ns_t_ns:
+ case ns_t_ptr:
+ case ns_t_dname:
+ UNPACK_DNAME;
+ break;
+ case ns_t_soa:
+ UNPACK_DNAME;
+ UNPACK_DNAME;
+ UNPACK_SOME(NS_INT32SZ * 5);
+ break;
+ case ns_t_mx:
+ case ns_t_afsdb:
+ case ns_t_rt:
+ UNPACK_SOME(NS_INT16SZ);
+ UNPACK_DNAME;
+ break;
+ case ns_t_px:
+ UNPACK_SOME(NS_INT16SZ);
+ UNPACK_DNAME;
+ UNPACK_DNAME;
+ break;
+ case ns_t_srv:
+ UNPACK_SOME(NS_INT16SZ * 3);
+ UNPACK_DNAME;
+ break;
+ case ns_t_minfo:
+ case ns_t_rp:
+ UNPACK_DNAME;
+ UNPACK_DNAME;
+ break;
+ default:
+ UNPACK_SOME(rdlen);
+ break;
+ }
+ if (rdlen > 0) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ return (nrdlen);
+}
+
+#define EQUAL_CONSUME \
+ do { \
+ rdata1 += n, rdlen1 -= n; \
+ rdata2 += n, rdlen2 -= n; \
+ } while (0)
+
+#define EQUAL_DNAME \
+ do { \
+ ssize_t n; \
+ \
+ if (rdlen1 != rdlen2) \
+ return (0); \
+ n = ns_name_eq(rdata1, rdlen1, rdata2, rdlen2); \
+ if (n <= 0) \
+ return (n); \
+ n = rdlen1; \
+ EQUAL_CONSUME; \
+ } while (0)
+
+#define EQUAL_SOME(x) \
+ do { \
+ size_t n = (x); \
+ \
+ if (n > rdlen1 || n > rdlen2) { \
+ errno = EMSGSIZE; \
+ return (-1); \
+ } \
+ if (memcmp(rdata1, rdata2, n) != 0) \
+ return (0); \
+ EQUAL_CONSUME; \
+ } while (0)
+
+int
+ns_rdata_equal(ns_type type,
+ const u_char *rdata1, size_t rdlen1,
+ const u_char *rdata2, size_t rdlen2)
+{
+ switch (type) {
+ case ns_t_cname:
+ case ns_t_mb:
+ case ns_t_mg:
+ case ns_t_mr:
+ case ns_t_ns:
+ case ns_t_ptr:
+ case ns_t_dname:
+ EQUAL_DNAME;
+ break;
+ case ns_t_soa:
+ /* "There can be only one." --Highlander */
+ break;
+ case ns_t_mx:
+ case ns_t_afsdb:
+ case ns_t_rt:
+ EQUAL_SOME(NS_INT16SZ);
+ EQUAL_DNAME;
+ break;
+ case ns_t_px:
+ EQUAL_SOME(NS_INT16SZ);
+ EQUAL_DNAME;
+ EQUAL_DNAME;
+ break;
+ case ns_t_srv:
+ EQUAL_SOME(NS_INT16SZ * 3);
+ EQUAL_DNAME;
+ break;
+ case ns_t_minfo:
+ case ns_t_rp:
+ EQUAL_DNAME;
+ EQUAL_DNAME;
+ break;
+ default:
+ EQUAL_SOME(rdlen1);
+ break;
+ }
+ if (rdlen1 != 0 || rdlen2 != 0)
+ return (0);
+ return (1);
+}
+
+#define REFERS_DNAME \
+ do { \
+ int n; \
+ \
+ n = ns_name_eq(rdata, rdlen, nname, NS_MAXNNAME); \
+ if (n < 0) \
+ return (-1); \
+ if (n > 0) \
+ return (1); \
+ n = dn_skipname(rdata, rdata + rdlen); \
+ if (n < 0) \
+ return (-1); \
+ CONSUME_SRC; \
+ } while (0)
+
+#define REFERS_SOME(x) \
+ do { \
+ size_t n = (x); \
+ \
+ if (n > rdlen) { \
+ errno = EMSGSIZE; \
+ return (-1); \
+ } \
+ CONSUME_SRC; \
+ } while (0)
+
+int
+ns_rdata_refers(ns_type type,
+ const u_char *rdata, size_t rdlen,
+ const u_char *nname)
+{
+ switch (type) {
+ case ns_t_cname:
+ case ns_t_mb:
+ case ns_t_mg:
+ case ns_t_mr:
+ case ns_t_ns:
+ case ns_t_ptr:
+ case ns_t_dname:
+ REFERS_DNAME;
+ break;
+ case ns_t_soa:
+ REFERS_DNAME;
+ REFERS_DNAME;
+ REFERS_SOME(NS_INT32SZ * 5);
+ break;
+ case ns_t_mx:
+ case ns_t_afsdb:
+ case ns_t_rt:
+ REFERS_SOME(NS_INT16SZ);
+ REFERS_DNAME;
+ break;
+ case ns_t_px:
+ REFERS_SOME(NS_INT16SZ);
+ REFERS_DNAME;
+ REFERS_DNAME;
+ break;
+ case ns_t_srv:
+ REFERS_SOME(NS_INT16SZ * 3);
+ REFERS_DNAME;
+ break;
+ case ns_t_minfo:
+ case ns_t_rp:
+ REFERS_DNAME;
+ REFERS_DNAME;
+ break;
+ default:
+ REFERS_SOME(rdlen);
+ break;
+ }
+ if (rdlen != 0) {
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ return (0);
+}
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_samedomain.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_samedomain.c
new file mode 100644
index 0000000000..b4c4ae68db
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_samedomain.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <arpa/nameser.h>
+#include <errno.h>
+#include <string.h>
+
+#include "port_after.h"
+
+/*%
+ * Check whether a name belongs to a domain.
+ *
+ * Inputs:
+ *\li a - the domain whose ancestory is being verified
+ *\li b - the potential ancestor we're checking against
+ *
+ * Return:
+ *\li boolean - is a at or below b?
+ *
+ * Notes:
+ *\li Trailing dots are first removed from name and domain.
+ * Always compare complete subdomains, not only whether the
+ * domain name is the trailing string of the given name.
+ *
+ *\li "host.foobar.top" lies in "foobar.top" and in "top" and in ""
+ * but NOT in "bar.top"
+ */
+
+int
+ns_samedomain(const char *a, const char *b) {
+ size_t la, lb;
+ int diff, i, escaped;
+ const char *cp;
+
+ la = strlen(a);
+ lb = strlen(b);
+
+ /* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */
+ if (la != 0U && a[la - 1] == '.') {
+ escaped = 0;
+ /* Note this loop doesn't get executed if la==1. */
+ for (i = la - 2; i >= 0; i--)
+ if (a[i] == '\\') {
+ if (escaped)
+ escaped = 0;
+ else
+ escaped = 1;
+ } else
+ break;
+ if (!escaped)
+ la--;
+ }
+
+ /* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */
+ if (lb != 0U && b[lb - 1] == '.') {
+ escaped = 0;
+ /* note this loop doesn't get executed if lb==1 */
+ for (i = lb - 2; i >= 0; i--)
+ if (b[i] == '\\') {
+ if (escaped)
+ escaped = 0;
+ else
+ escaped = 1;
+ } else
+ break;
+ if (!escaped)
+ lb--;
+ }
+
+ /* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */
+ if (lb == 0U)
+ return (1);
+
+ /* 'b' longer than 'a' means 'a' can't be in 'b'. */
+ if (lb > la)
+ return (0);
+
+ /* 'a' and 'b' being equal at this point indicates sameness. */
+ if (lb == la)
+ return (strncasecmp(a, b, lb) == 0);
+
+ /* Ok, we know la > lb. */
+
+ diff = la - lb;
+
+ /*
+ * If 'a' is only 1 character longer than 'b', then it can't be
+ * a subdomain of 'b' (because of the need for the '.' label
+ * separator).
+ */
+ if (diff < 2)
+ return (0);
+
+ /*
+ * If the character before the last 'lb' characters of 'b'
+ * isn't '.', then it can't be a match (this lets us avoid
+ * having "foobar.com" match "bar.com").
+ */
+ if (a[diff - 1] != '.')
+ return (0);
+
+ /*
+ * We're not sure about that '.', however. It could be escaped
+ * and thus not a really a label separator.
+ */
+ escaped = 0;
+ for (i = diff - 2; i >= 0; i--)
+ if (a[i] == '\\') {
+ if (escaped)
+ escaped = 0;
+ else
+ escaped = 1;
+ } else
+ break;
+ if (escaped)
+ return (0);
+
+ /* Now compare aligned trailing substring. */
+ cp = a + diff;
+ return (strncasecmp(cp, b, lb) == 0);
+}
+
+/*%
+ * is "a" a subdomain of "b"?
+ */
+int
+ns_subdomain(const char *a, const char *b) {
+ return (ns_samename(a, b) != 1 && ns_samedomain(a, b));
+}
+
+/*%
+ * make a canonical copy of domain name "src"
+ *
+ * notes:
+ * \code
+ * foo -> foo.
+ * foo. -> foo.
+ * foo.. -> foo.
+ * foo\. -> foo\..
+ * foo\\. -> foo\\.
+ * \endcode
+ */
+
+int
+ns_makecanon(const char *src, char *dst, size_t dstsize) {
+ size_t n = strlen(src);
+
+ if (n + sizeof "." > dstsize) { /*%< Note: sizeof == 2 */
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ strcpy(dst, src);
+ while (n >= 1U && dst[n - 1] == '.') /*%< Ends in "." */
+ if (n >= 2U && dst[n - 2] == '\\' && /*%< Ends in "\." */
+ (n < 3U || dst[n - 3] != '\\')) /*%< But not "\\." */
+ break;
+ else
+ dst[--n] = '\0';
+ dst[n++] = '.';
+ dst[n] = '\0';
+ return (0);
+}
+
+/*%
+ * determine whether domain name "a" is the same as domain name "b"
+ *
+ * return:
+ *\li -1 on error
+ *\li 0 if names differ
+ *\li 1 if names are the same
+ */
+
+int
+ns_samename(const char *a, const char *b) {
+ char ta[NS_MAXDNAME], tb[NS_MAXDNAME];
+
+ if (ns_makecanon(a, ta, sizeof ta) < 0 ||
+ ns_makecanon(b, tb, sizeof tb) < 0)
+ return (-1);
+ if (strcasecmp(ta, tb) == 0)
+ return (1);
+ else
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_sign.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_sign.c
new file mode 100644
index 0000000000..4db8d33679
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_sign.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999 by Internet Software Consortium, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+#include "fd_setsize.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/dst.h>
+#include <isc/assertions.h>
+
+#include "port_after.h"
+
+#define BOUNDS_CHECK(ptr, count) \
+ do { \
+ if ((ptr) + (count) > eob) { \
+ errno = EMSGSIZE; \
+ return(NS_TSIG_ERROR_NO_SPACE); \
+ } \
+ } while (0)
+
+/*%
+ * ns_sign
+ *
+ * Parameters:
+ *\li msg message to be sent
+ *\li msglen input - length of message
+ * output - length of signed message
+ *\li msgsize length of buffer containing message
+ *\li error value to put in the error field
+ *\li key tsig key used for signing
+ *\li querysig (response), the signature in the query
+ *\li querysiglen (response), the length of the signature in the query
+ *\li sig a buffer to hold the generated signature
+ *\li siglen input - length of signature buffer
+ * output - length of signature
+ *
+ * Errors:
+ *\li - bad input data (-1)
+ *\li - bad key / sign failed (-BADKEY)
+ *\li - not enough space (NS_TSIG_ERROR_NO_SPACE)
+ */
+int
+ns_sign(u_char *msg, int *msglen, int msgsize, int error, void *k,
+ const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
+ time_t in_timesigned)
+{
+ return(ns_sign2(msg, msglen, msgsize, error, k,
+ querysig, querysiglen, sig, siglen,
+ in_timesigned, NULL, NULL));
+}
+
+int
+ns_sign2(u_char *msg, int *msglen, int msgsize, int error, void *k,
+ const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
+ time_t in_timesigned, u_char **dnptrs, u_char **lastdnptr)
+{
+ HEADER *hp = (HEADER *)msg;
+ DST_KEY *key = (DST_KEY *)k;
+ u_char *cp, *eob;
+ u_char *lenp;
+ u_char *alg;
+ int n;
+ time_t timesigned;
+ u_char name[NS_MAXCDNAME];
+
+ dst_init();
+ if (msg == NULL || msglen == NULL || sig == NULL || siglen == NULL)
+ return (-1);
+
+ cp = msg + *msglen;
+ eob = msg + msgsize;
+
+ /* Name. */
+ if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
+ n = ns_name_pton(key->dk_key_name, name, sizeof name);
+ if (n != -1)
+ n = ns_name_pack(name, cp, eob - cp,
+ (const u_char **)dnptrs,
+ (const u_char **)lastdnptr);
+
+ } else {
+ n = ns_name_pton("", name, sizeof name);
+ if (n != -1)
+ n = ns_name_pack(name, cp, eob - cp, NULL, NULL);
+ }
+ if (n < 0)
+ return (NS_TSIG_ERROR_NO_SPACE);
+ cp += n;
+
+ /* Type, class, ttl, length (not filled in yet). */
+ BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ);
+ PUTSHORT(ns_t_tsig, cp);
+ PUTSHORT(ns_c_any, cp);
+ PUTLONG(0, cp); /*%< TTL */
+ lenp = cp;
+ cp += 2;
+
+ /* Alg. */
+ if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
+ if (key->dk_alg != KEY_HMAC_MD5)
+ return (-ns_r_badkey);
+ n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL);
+ }
+ else
+ n = dn_comp("", cp, eob - cp, NULL, NULL);
+ if (n < 0)
+ return (NS_TSIG_ERROR_NO_SPACE);
+ alg = cp;
+ cp += n;
+
+ /* Time. */
+ BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
+ PUTSHORT(0, cp);
+ timesigned = time(NULL);
+ if (error != ns_r_badtime)
+ PUTLONG(timesigned, cp);
+ else
+ PUTLONG(in_timesigned, cp);
+ PUTSHORT(NS_TSIG_FUDGE, cp);
+
+ /* Compute the signature. */
+ if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
+ void *ctx;
+ u_char buf[NS_MAXCDNAME], *cp2;
+ int n;
+
+ dst_sign_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0);
+
+ /* Digest the query signature, if this is a response. */
+ if (querysiglen > 0 && querysig != NULL) {
+ u_int16_t len_n = htons(querysiglen);
+ dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
+ (u_char *)&len_n, INT16SZ, NULL, 0);
+ dst_sign_data(SIG_MODE_UPDATE, key, &ctx,
+ querysig, querysiglen, NULL, 0);
+ }
+
+ /* Digest the message. */
+ dst_sign_data(SIG_MODE_UPDATE, key, &ctx, msg, *msglen,
+ NULL, 0);
+
+ /* Digest the key name. */
+ n = ns_name_ntol(name, buf, sizeof(buf));
+ INSIST(n > 0);
+ dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
+
+ /* Digest the class and TTL. */
+ cp2 = buf;
+ PUTSHORT(ns_c_any, cp2);
+ PUTLONG(0, cp2);
+ dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf,
+ NULL, 0);
+
+ /* Digest the algorithm. */
+ n = ns_name_ntol(alg, buf, sizeof(buf));
+ INSIST(n > 0);
+ dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
+
+ /* Digest the time signed, fudge, error, and other data */
+ cp2 = buf;
+ PUTSHORT(0, cp2); /*%< Top 16 bits of time */
+ if (error != ns_r_badtime)
+ PUTLONG(timesigned, cp2);
+ else
+ PUTLONG(in_timesigned, cp2);
+ PUTSHORT(NS_TSIG_FUDGE, cp2);
+ PUTSHORT(error, cp2); /*%< Error */
+ if (error != ns_r_badtime)
+ PUTSHORT(0, cp2); /*%< Other data length */
+ else {
+ PUTSHORT(INT16SZ+INT32SZ, cp2); /*%< Other data length */
+ PUTSHORT(0, cp2); /*%< Top 16 bits of time */
+ PUTLONG(timesigned, cp2);
+ }
+ dst_sign_data(SIG_MODE_UPDATE, key, &ctx, buf, cp2-buf,
+ NULL, 0);
+
+ n = dst_sign_data(SIG_MODE_FINAL, key, &ctx, NULL, 0,
+ sig, *siglen);
+ if (n < 0)
+ return (-ns_r_badkey);
+ *siglen = n;
+ } else
+ *siglen = 0;
+
+ /* Add the signature. */
+ BOUNDS_CHECK(cp, INT16SZ + (*siglen));
+ PUTSHORT(*siglen, cp);
+ memcpy(cp, sig, *siglen);
+ cp += (*siglen);
+
+ /* The original message ID & error. */
+ BOUNDS_CHECK(cp, INT16SZ + INT16SZ);
+ PUTSHORT(ntohs(hp->id), cp); /*%< already in network order */
+ PUTSHORT(error, cp);
+
+ /* Other data. */
+ BOUNDS_CHECK(cp, INT16SZ);
+ if (error != ns_r_badtime)
+ PUTSHORT(0, cp); /*%< Other data length */
+ else {
+ PUTSHORT(INT16SZ+INT32SZ, cp); /*%< Other data length */
+ BOUNDS_CHECK(cp, INT32SZ+INT16SZ);
+ PUTSHORT(0, cp); /*%< Top 16 bits of time */
+ PUTLONG(timesigned, cp);
+ }
+
+ /* Go back and fill in the length. */
+ PUTSHORT(cp - lenp - INT16SZ, lenp);
+
+ hp->arcount = htons(ntohs(hp->arcount) + 1);
+ *msglen = (cp - msg);
+ return (0);
+}
+
+int
+ns_sign_tcp_init(void *k, const u_char *querysig, int querysiglen,
+ ns_tcp_tsig_state *state)
+{
+ dst_init();
+ if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0)
+ return (-1);
+ state->counter = -1;
+ state->key = k;
+ if (state->key->dk_alg != KEY_HMAC_MD5)
+ return (-ns_r_badkey);
+ if (querysiglen > (int)sizeof(state->sig))
+ return (-1);
+ memcpy(state->sig, querysig, querysiglen);
+ state->siglen = querysiglen;
+ return (0);
+}
+
+int
+ns_sign_tcp(u_char *msg, int *msglen, int msgsize, int error,
+ ns_tcp_tsig_state *state, int done)
+{
+ return (ns_sign_tcp2(msg, msglen, msgsize, error, state,
+ done, NULL, NULL));
+}
+
+int
+ns_sign_tcp2(u_char *msg, int *msglen, int msgsize, int error,
+ ns_tcp_tsig_state *state, int done,
+ u_char **dnptrs, u_char **lastdnptr)
+{
+ u_char *cp, *eob, *lenp;
+ u_char buf[MAXDNAME], *cp2;
+ HEADER *hp = (HEADER *)msg;
+ time_t timesigned;
+ int n;
+
+ if (msg == NULL || msglen == NULL || state == NULL)
+ return (-1);
+
+ state->counter++;
+ if (state->counter == 0)
+ return (ns_sign2(msg, msglen, msgsize, error, state->key,
+ state->sig, state->siglen,
+ state->sig, &state->siglen, 0,
+ dnptrs, lastdnptr));
+
+ if (state->siglen > 0) {
+ u_int16_t siglen_n = htons(state->siglen);
+ dst_sign_data(SIG_MODE_INIT, state->key, &state->ctx,
+ NULL, 0, NULL, 0);
+ dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+ (u_char *)&siglen_n, INT16SZ, NULL, 0);
+ dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+ state->sig, state->siglen, NULL, 0);
+ state->siglen = 0;
+ }
+
+ dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx, msg, *msglen,
+ NULL, 0);
+
+ if (done == 0 && (state->counter % 100 != 0))
+ return (0);
+
+ cp = msg + *msglen;
+ eob = msg + msgsize;
+
+ /* Name. */
+ n = dn_comp(state->key->dk_key_name, cp, eob - cp, dnptrs, lastdnptr);
+ if (n < 0)
+ return (NS_TSIG_ERROR_NO_SPACE);
+ cp += n;
+
+ /* Type, class, ttl, length (not filled in yet). */
+ BOUNDS_CHECK(cp, INT16SZ + INT16SZ + INT32SZ + INT16SZ);
+ PUTSHORT(ns_t_tsig, cp);
+ PUTSHORT(ns_c_any, cp);
+ PUTLONG(0, cp); /*%< TTL */
+ lenp = cp;
+ cp += 2;
+
+ /* Alg. */
+ n = dn_comp(NS_TSIG_ALG_HMAC_MD5, cp, eob - cp, NULL, NULL);
+ if (n < 0)
+ return (NS_TSIG_ERROR_NO_SPACE);
+ cp += n;
+
+ /* Time. */
+ BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
+ PUTSHORT(0, cp);
+ timesigned = time(NULL);
+ PUTLONG(timesigned, cp);
+ PUTSHORT(NS_TSIG_FUDGE, cp);
+
+ /*
+ * Compute the signature.
+ */
+
+ /* Digest the time signed and fudge. */
+ cp2 = buf;
+ PUTSHORT(0, cp2); /*%< Top 16 bits of time */
+ PUTLONG(timesigned, cp2);
+ PUTSHORT(NS_TSIG_FUDGE, cp2);
+
+ dst_sign_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+ buf, cp2 - buf, NULL, 0);
+
+ n = dst_sign_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0,
+ state->sig, sizeof(state->sig));
+ if (n < 0)
+ return (-ns_r_badkey);
+ state->siglen = n;
+
+ /* Add the signature. */
+ BOUNDS_CHECK(cp, INT16SZ + state->siglen);
+ PUTSHORT(state->siglen, cp);
+ memcpy(cp, state->sig, state->siglen);
+ cp += state->siglen;
+
+ /* The original message ID & error. */
+ BOUNDS_CHECK(cp, INT16SZ + INT16SZ);
+ PUTSHORT(ntohs(hp->id), cp); /*%< already in network order */
+ PUTSHORT(error, cp);
+
+ /* Other data. */
+ BOUNDS_CHECK(cp, INT16SZ);
+ PUTSHORT(0, cp);
+
+ /* Go back and fill in the length. */
+ PUTSHORT(cp - lenp - INT16SZ, lenp);
+
+ hp->arcount = htons(ntohs(hp->arcount) + 1);
+ *msglen = (cp - msg);
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_ttl.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_ttl.c
new file mode 100644
index 0000000000..635a11905e
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_ttl.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ * Copyright 2018 Joyent, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) ((size_t)sprintf x)
+#endif
+
+/* Forward. */
+
+static int fmt1(int t, char s, char **buf, size_t *buflen);
+
+/* Macros. */
+
+#define T(x) if ((x) < 0) return (-1); else (void)NULL
+
+/* Public. */
+
+int
+ns_format_ttl(u_long src, char *dst, size_t dstlen) {
+ char *odst = dst;
+ int secs, mins, hours, days, weeks, x;
+ char *p;
+
+ secs = src % 60; src /= 60;
+ mins = src % 60; src /= 60;
+ hours = src % 24; src /= 24;
+ days = src % 7; src /= 7;
+ weeks = src; src = 0;
+
+ x = 0;
+ if (weeks) {
+ T(fmt1(weeks, 'W', &dst, &dstlen));
+ x++;
+ }
+ if (days) {
+ T(fmt1(days, 'D', &dst, &dstlen));
+ x++;
+ }
+ if (hours) {
+ T(fmt1(hours, 'H', &dst, &dstlen));
+ x++;
+ }
+ if (mins) {
+ T(fmt1(mins, 'M', &dst, &dstlen));
+ x++;
+ }
+ if (secs || !(weeks || days || hours || mins)) {
+ T(fmt1(secs, 'S', &dst, &dstlen));
+ x++;
+ }
+
+ if (x > 1) {
+ int ch;
+
+ for (p = odst; (ch = *p) != '\0'; p++)
+ if (isascii(ch) && isupper(ch))
+ *p = tolower(ch);
+ }
+
+ return (dst - odst);
+}
+
+int
+ns_parse_ttl(const char *src, u_long *dst) {
+ u_long ttl, tmp;
+ int ch, digits, dirty;
+
+ ttl = 0;
+ tmp = 0;
+ digits = 0;
+ dirty = 0;
+ while ((ch = *src++) != '\0') {
+ if (!isascii(ch) || !isprint(ch))
+ goto einval;
+ if (isdigit(ch)) {
+ tmp *= 10;
+ tmp += (ch - '0');
+ digits++;
+ continue;
+ }
+ if (digits == 0)
+ goto einval;
+ if (islower(ch))
+ ch = toupper(ch);
+ switch (ch) {
+ case 'W': tmp *= 7;
+ /* FALLTHROUGH */
+ case 'D': tmp *= 24;
+ /* FALLTHROUGH */
+ case 'H': tmp *= 60;
+ /* FALLTHROUGH */
+ case 'M': tmp *= 60;
+ /* FALLTHROUGH */
+ case 'S': break;
+ default: goto einval;
+ }
+ ttl += tmp;
+ tmp = 0;
+ digits = 0;
+ dirty = 1;
+ }
+ if (digits > 0) {
+ if (dirty)
+ goto einval;
+ else
+ ttl += tmp;
+ } else if (!dirty)
+ goto einval;
+ *dst = ttl;
+ return (0);
+
+ einval:
+ errno = EINVAL;
+ return (-1);
+}
+
+/* Private. */
+
+static int
+fmt1(int t, char s, char **buf, size_t *buflen) {
+ char tmp[50];
+ size_t len;
+
+ len = SPRINTF((tmp, "%d%c", t, s));
+ if (len + 1 > *buflen)
+ return (-1);
+ strcpy(*buf, tmp);
+ *buf += len;
+ *buflen -= len;
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/nameser/ns_verify.c b/usr/src/lib/libresolv2_joy/common/nameser/ns_verify.c
new file mode 100644
index 0000000000..93d8d59865
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/nameser/ns_verify.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999 by Internet Software Consortium, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+#include "fd_setsize.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <isc/dst.h>
+
+#include "port_after.h"
+
+/* Private. */
+
+#define BOUNDS_CHECK(ptr, count) \
+ do { \
+ if ((ptr) + (count) > eom) { \
+ return (NS_TSIG_ERROR_FORMERR); \
+ } \
+ } while (0)
+
+/* Public. */
+
+u_char *
+ns_find_tsig(u_char *msg, u_char *eom) {
+ HEADER *hp = (HEADER *)msg;
+ int n, type;
+ u_char *cp = msg, *start;
+
+ if (msg == NULL || eom == NULL || msg > eom)
+ return (NULL);
+
+ if (cp + HFIXEDSZ >= eom)
+ return (NULL);
+
+ if (hp->arcount == 0)
+ return (NULL);
+
+ cp += HFIXEDSZ;
+
+ n = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount));
+ if (n < 0)
+ return (NULL);
+ cp += n;
+
+ n = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount));
+ if (n < 0)
+ return (NULL);
+ cp += n;
+
+ n = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount));
+ if (n < 0)
+ return (NULL);
+ cp += n;
+
+ n = ns_skiprr(cp, eom, ns_s_ar, ntohs(hp->arcount) - 1);
+ if (n < 0)
+ return (NULL);
+ cp += n;
+
+ start = cp;
+ n = dn_skipname(cp, eom);
+ if (n < 0)
+ return (NULL);
+ cp += n;
+ if (cp + INT16SZ >= eom)
+ return (NULL);
+
+ GETSHORT(type, cp);
+ if (type != ns_t_tsig)
+ return (NULL);
+ return (start);
+}
+
+/* ns_verify
+ *
+ * Parameters:
+ *\li statp res stuff
+ *\li msg received message
+ *\li msglen length of message
+ *\li key tsig key used for verifying.
+ *\li querysig (response), the signature in the query
+ *\li querysiglen (response), the length of the signature in the query
+ *\li sig (query), a buffer to hold the signature
+ *\li siglen (query), input - length of signature buffer
+ * output - length of signature
+ *
+ * Errors:
+ *\li - bad input (-1)
+ *\li - invalid dns message (NS_TSIG_ERROR_FORMERR)
+ *\li - TSIG is not present (NS_TSIG_ERROR_NO_TSIG)
+ *\li - key doesn't match (-ns_r_badkey)
+ *\li - TSIG verification fails with BADKEY (-ns_r_badkey)
+ *\li - TSIG verification fails with BADSIG (-ns_r_badsig)
+ *\li - TSIG verification fails with BADTIME (-ns_r_badtime)
+ *\li - TSIG verification succeeds, error set to BAKEY (ns_r_badkey)
+ *\li - TSIG verification succeeds, error set to BADSIG (ns_r_badsig)
+ *\li - TSIG verification succeeds, error set to BADTIME (ns_r_badtime)
+ */
+int
+ns_verify(u_char *msg, int *msglen, void *k,
+ const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
+ time_t *timesigned, int nostrip)
+{
+ HEADER *hp = (HEADER *)msg;
+ DST_KEY *key = (DST_KEY *)k;
+ u_char *cp = msg, *eom;
+ char name[MAXDNAME], alg[MAXDNAME];
+ u_char *recstart, *rdatastart;
+ u_char *sigstart, *otherstart;
+ int n;
+ int error;
+ u_int16_t type, length;
+ u_int16_t fudge, sigfieldlen, otherfieldlen;
+
+ dst_init();
+ if (msg == NULL || msglen == NULL || *msglen < 0)
+ return (-1);
+
+ eom = msg + *msglen;
+
+ recstart = ns_find_tsig(msg, eom);
+ if (recstart == NULL)
+ return (NS_TSIG_ERROR_NO_TSIG);
+
+ cp = recstart;
+
+ /* Read the key name. */
+ n = dn_expand(msg, eom, cp, name, MAXDNAME);
+ if (n < 0)
+ return (NS_TSIG_ERROR_FORMERR);
+ cp += n;
+
+ /* Read the type. */
+ BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ);
+ GETSHORT(type, cp);
+ if (type != ns_t_tsig)
+ return (NS_TSIG_ERROR_NO_TSIG);
+
+ /* Skip the class and TTL, save the length. */
+ cp += INT16SZ + INT32SZ;
+ GETSHORT(length, cp);
+ if (eom - cp != length)
+ return (NS_TSIG_ERROR_FORMERR);
+
+ /* Read the algorithm name. */
+ rdatastart = cp;
+ n = dn_expand(msg, eom, cp, alg, MAXDNAME);
+ if (n < 0)
+ return (NS_TSIG_ERROR_FORMERR);
+ if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1)
+ return (-ns_r_badkey);
+ cp += n;
+
+ /* Read the time signed and fudge. */
+ BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
+ cp += INT16SZ;
+ GETLONG((*timesigned), cp);
+ GETSHORT(fudge, cp);
+
+ /* Read the signature. */
+ BOUNDS_CHECK(cp, INT16SZ);
+ GETSHORT(sigfieldlen, cp);
+ BOUNDS_CHECK(cp, sigfieldlen);
+ sigstart = cp;
+ cp += sigfieldlen;
+
+ /* Skip id and read error. */
+ BOUNDS_CHECK(cp, 2*INT16SZ);
+ cp += INT16SZ;
+ GETSHORT(error, cp);
+
+ /* Parse the other data. */
+ BOUNDS_CHECK(cp, INT16SZ);
+ GETSHORT(otherfieldlen, cp);
+ BOUNDS_CHECK(cp, otherfieldlen);
+ otherstart = cp;
+ cp += otherfieldlen;
+
+ if (cp != eom)
+ return (NS_TSIG_ERROR_FORMERR);
+
+ /* Verify that the key used is OK. */
+ if (key != NULL) {
+ if (key->dk_alg != KEY_HMAC_MD5)
+ return (-ns_r_badkey);
+ if (error != ns_r_badsig && error != ns_r_badkey) {
+ if (ns_samename(key->dk_key_name, name) != 1)
+ return (-ns_r_badkey);
+ }
+ }
+
+ hp->arcount = htons(ntohs(hp->arcount) - 1);
+
+ /*
+ * Do the verification.
+ */
+
+ if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
+ void *ctx;
+ u_char buf[MAXDNAME];
+ u_char buf2[MAXDNAME];
+
+ /* Digest the query signature, if this is a response. */
+ dst_verify_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0);
+ if (querysiglen > 0 && querysig != NULL) {
+ u_int16_t len_n = htons(querysiglen);
+ dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
+ (u_char *)&len_n, INT16SZ, NULL, 0);
+ dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
+ querysig, querysiglen, NULL, 0);
+ }
+
+ /* Digest the message. */
+ dst_verify_data(SIG_MODE_UPDATE, key, &ctx, msg, recstart - msg,
+ NULL, 0);
+
+ /* Digest the key name. */
+ n = ns_name_pton(name, buf2, sizeof(buf2));
+ if (n < 0)
+ return (-1);
+ n = ns_name_ntol(buf2, buf, sizeof(buf));
+ if (n < 0)
+ return (-1);
+ dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
+
+ /* Digest the class and TTL. */
+ dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
+ recstart + dn_skipname(recstart, eom) + INT16SZ,
+ INT16SZ + INT32SZ, NULL, 0);
+
+ /* Digest the algorithm. */
+ n = ns_name_pton(alg, buf2, sizeof(buf2));
+ if (n < 0)
+ return (-1);
+ n = ns_name_ntol(buf2, buf, sizeof(buf));
+ if (n < 0)
+ return (-1);
+ dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
+
+ /* Digest the time signed and fudge. */
+ dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
+ rdatastart + dn_skipname(rdatastart, eom),
+ INT16SZ + INT32SZ + INT16SZ, NULL, 0);
+
+ /* Digest the error and other data. */
+ dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
+ otherstart - INT16SZ - INT16SZ,
+ otherfieldlen + INT16SZ + INT16SZ, NULL, 0);
+
+ n = dst_verify_data(SIG_MODE_FINAL, key, &ctx, NULL, 0,
+ sigstart, sigfieldlen);
+
+ if (n < 0)
+ return (-ns_r_badsig);
+
+ if (sig != NULL && siglen != NULL) {
+ if (*siglen < sigfieldlen)
+ return (NS_TSIG_ERROR_NO_SPACE);
+ memcpy(sig, sigstart, sigfieldlen);
+ *siglen = sigfieldlen;
+ }
+ } else {
+ if (sigfieldlen > 0)
+ return (NS_TSIG_ERROR_FORMERR);
+ if (sig != NULL && siglen != NULL)
+ *siglen = 0;
+ }
+
+ /* Reset the counter, since we still need to check for badtime. */
+ hp->arcount = htons(ntohs(hp->arcount) + 1);
+
+ /* Verify the time. */
+ if (abs((*timesigned) - time(NULL)) > fudge)
+ return (-ns_r_badtime);
+
+ if (nostrip == 0) {
+ *msglen = recstart - msg;
+ hp->arcount = htons(ntohs(hp->arcount) - 1);
+ }
+
+ if (error != NOERROR)
+ return (error);
+
+ return (0);
+}
+
+int
+ns_verify_tcp_init(void *k, const u_char *querysig, int querysiglen,
+ ns_tcp_tsig_state *state)
+{
+ dst_init();
+ if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0)
+ return (-1);
+ state->counter = -1;
+ state->key = k;
+ if (state->key->dk_alg != KEY_HMAC_MD5)
+ return (-ns_r_badkey);
+ if (querysiglen > (int)sizeof(state->sig))
+ return (-1);
+ memcpy(state->sig, querysig, querysiglen);
+ state->siglen = querysiglen;
+ return (0);
+}
+
+int
+ns_verify_tcp(u_char *msg, int *msglen, ns_tcp_tsig_state *state,
+ int required)
+{
+ HEADER *hp = (HEADER *)msg;
+ u_char *recstart, *sigstart;
+ unsigned int sigfieldlen, otherfieldlen;
+ u_char *cp, *eom, *cp2;
+ char name[MAXDNAME], alg[MAXDNAME];
+ u_char buf[MAXDNAME];
+ int n, type, length, fudge, error;
+ time_t timesigned;
+
+ if (msg == NULL || msglen == NULL || state == NULL)
+ return (-1);
+
+ eom = msg + *msglen;
+
+ state->counter++;
+ if (state->counter == 0)
+ return (ns_verify(msg, msglen, state->key,
+ state->sig, state->siglen,
+ state->sig, &state->siglen, &timesigned, 0));
+
+ if (state->siglen > 0) {
+ u_int16_t siglen_n = htons(state->siglen);
+
+ dst_verify_data(SIG_MODE_INIT, state->key, &state->ctx,
+ NULL, 0, NULL, 0);
+ dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+ (u_char *)&siglen_n, INT16SZ, NULL, 0);
+ dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+ state->sig, state->siglen, NULL, 0);
+ state->siglen = 0;
+ }
+
+ cp = recstart = ns_find_tsig(msg, eom);
+
+ if (recstart == NULL) {
+ if (required)
+ return (NS_TSIG_ERROR_NO_TSIG);
+ dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+ msg, *msglen, NULL, 0);
+ return (0);
+ }
+
+ hp->arcount = htons(ntohs(hp->arcount) - 1);
+ dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+ msg, recstart - msg, NULL, 0);
+
+ /* Read the key name. */
+ n = dn_expand(msg, eom, cp, name, MAXDNAME);
+ if (n < 0)
+ return (NS_TSIG_ERROR_FORMERR);
+ cp += n;
+
+ /* Read the type. */
+ BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ);
+ GETSHORT(type, cp);
+ if (type != ns_t_tsig)
+ return (NS_TSIG_ERROR_NO_TSIG);
+
+ /* Skip the class and TTL, save the length. */
+ cp += INT16SZ + INT32SZ;
+ GETSHORT(length, cp);
+ if (eom - cp != length)
+ return (NS_TSIG_ERROR_FORMERR);
+
+ /* Read the algorithm name. */
+ n = dn_expand(msg, eom, cp, alg, MAXDNAME);
+ if (n < 0)
+ return (NS_TSIG_ERROR_FORMERR);
+ if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1)
+ return (-ns_r_badkey);
+ cp += n;
+
+ /* Verify that the key used is OK. */
+ if ((ns_samename(state->key->dk_key_name, name) != 1 ||
+ state->key->dk_alg != KEY_HMAC_MD5))
+ return (-ns_r_badkey);
+
+ /* Read the time signed and fudge. */
+ BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
+ cp += INT16SZ;
+ GETLONG(timesigned, cp);
+ GETSHORT(fudge, cp);
+
+ /* Read the signature. */
+ BOUNDS_CHECK(cp, INT16SZ);
+ GETSHORT(sigfieldlen, cp);
+ BOUNDS_CHECK(cp, sigfieldlen);
+ sigstart = cp;
+ cp += sigfieldlen;
+
+ /* Skip id and read error. */
+ BOUNDS_CHECK(cp, 2*INT16SZ);
+ cp += INT16SZ;
+ GETSHORT(error, cp);
+
+ /* Parse the other data. */
+ BOUNDS_CHECK(cp, INT16SZ);
+ GETSHORT(otherfieldlen, cp);
+ BOUNDS_CHECK(cp, otherfieldlen);
+ cp += otherfieldlen;
+
+ if (cp != eom)
+ return (NS_TSIG_ERROR_FORMERR);
+
+ /*
+ * Do the verification.
+ */
+
+ /* Digest the time signed and fudge. */
+ cp2 = buf;
+ PUTSHORT(0, cp2); /*%< Top 16 bits of time. */
+ PUTLONG(timesigned, cp2);
+ PUTSHORT(NS_TSIG_FUDGE, cp2);
+
+ dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
+ buf, cp2 - buf, NULL, 0);
+
+ n = dst_verify_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0,
+ sigstart, sigfieldlen);
+ if (n < 0)
+ return (-ns_r_badsig);
+
+ if (sigfieldlen > sizeof(state->sig))
+ return (NS_TSIG_ERROR_NO_SPACE);
+
+ memcpy(state->sig, sigstart, sigfieldlen);
+ state->siglen = sigfieldlen;
+
+ /* Verify the time. */
+ if (abs(timesigned - time(NULL)) > fudge)
+ return (-ns_r_badtime);
+
+ *msglen = recstart - msg;
+
+ if (error != NOERROR)
+ return (error);
+
+ return (0);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/herror.c b/usr/src/lib/libresolv2_joy/common/resolv/herror.c
new file mode 100644
index 0000000000..5181c5792c
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/herror.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1987, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <string.h>
+#include <unistd.h>
+#include <irs.h>
+
+#include "port_after.h"
+
+const char *h_errlist[] = {
+ "Resolver Error 0 (no error)",
+ "Unknown host", /*%< 1 HOST_NOT_FOUND */
+ "Host name lookup failure", /*%< 2 TRY_AGAIN */
+ "Unknown server error", /*%< 3 NO_RECOVERY */
+ "No address associated with name", /*%< 4 NO_ADDRESS */
+};
+int h_nerr = { sizeof h_errlist / sizeof h_errlist[0] };
+
+#if !(__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)
+#undef h_errno
+int h_errno;
+#endif
+
+/*%
+ * herror --
+ * print the error indicated by the h_errno value.
+ */
+void
+herror(const char *s) {
+ struct iovec iov[4], *v = iov;
+ char *t;
+
+ if (s != NULL && *s != '\0') {
+ DE_CONST(s, t);
+ v->iov_base = t;
+ v->iov_len = strlen(t);
+ v++;
+ DE_CONST(": ", t);
+ v->iov_base = t;
+ v->iov_len = 2;
+ v++;
+ }
+ DE_CONST(hstrerror(*__h_errno()), t);
+ v->iov_base = t;
+ v->iov_len = strlen(v->iov_base);
+ v++;
+ DE_CONST("\n", t);
+ v->iov_base = t;
+ v->iov_len = 1;
+ writev(STDERR_FILENO, iov, (v - iov) + 1);
+}
+
+/*%
+ * hstrerror --
+ * return the string associated with a given "host" errno value.
+ */
+const char *
+hstrerror(int err) {
+ if (err < 0)
+ return ("Resolver internal error");
+ else if (err < h_nerr)
+ return (h_errlist[err]);
+ return ("Unknown resolver error");
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/mtctxres.c b/usr/src/lib/libresolv2_joy/common/resolv/mtctxres.c
new file mode 100644
index 0000000000..2e79b1e7b4
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/mtctxres.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <port_before.h>
+#ifdef DO_PTHREADS
+#include <pthread.h>
+#endif
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <resolv_mt.h>
+#include <irs.h>
+#include <port_after.h>
+
+#ifdef DO_PTHREADS
+static pthread_key_t key;
+static int mt_key_initialized = 0;
+
+static int __res_init_ctx(void);
+static void __res_destroy_ctx(void *);
+
+#if defined(sun) && !defined(__GNUC__)
+#pragma init (_mtctxres_init)
+#endif
+#endif
+
+static mtctxres_t sharedctx;
+
+#ifdef DO_PTHREADS
+/*
+ * Initialize the TSD key. By doing this at library load time, we're
+ * implicitly running without interference from other threads, so there's
+ * no need for locking.
+ */
+static void
+_mtctxres_init(void) {
+ int pthread_keycreate_ret;
+
+ pthread_keycreate_ret = pthread_key_create(&key, __res_destroy_ctx);
+ if (pthread_keycreate_ret == 0)
+ mt_key_initialized = 1;
+}
+#endif
+
+/*
+ * To support binaries that used the private MT-safe interface in
+ * Solaris 8, we still need to provide the __res_enable_mt()
+ * and __res_disable_mt() entry points. They're do-nothing routines.
+ */
+int
+__res_enable_mt(void) {
+ return (-1);
+}
+
+int
+__res_disable_mt(void) {
+ return (0);
+}
+
+#ifdef DO_PTHREADS
+static int
+__res_init_ctx(void) {
+
+ mtctxres_t *mt;
+ int ret;
+
+
+ if (pthread_getspecific(key) != 0) {
+ /* Already exists */
+ return (0);
+ }
+
+ if ((mt = malloc(sizeof (mtctxres_t))) == 0) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ memset(mt, 0, sizeof (mtctxres_t));
+
+ if ((ret = pthread_setspecific(key, mt)) != 0) {
+ free(mt);
+ errno = ret;
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void
+__res_destroy_ctx(void *value) {
+
+ mtctxres_t *mt = (mtctxres_t *)value;
+
+ if (mt != 0)
+ free(mt);
+}
+#endif
+
+mtctxres_t *
+___mtctxres(void) {
+#ifdef DO_PTHREADS
+ mtctxres_t *mt;
+
+ /*
+ * This if clause should only be executed if we are linking
+ * statically. When linked dynamically _mtctxres_init() should
+ * be called at binding time due the #pragma above.
+ */
+ if (!mt_key_initialized) {
+ static pthread_mutex_t keylock = PTHREAD_MUTEX_INITIALIZER;
+ if (pthread_mutex_lock(&keylock) == 0) {
+ _mtctxres_init();
+ (void) pthread_mutex_unlock(&keylock);
+ }
+ }
+
+ /*
+ * If we have already been called in this thread return the existing
+ * context. Otherwise recreat a new context and return it. If
+ * that fails return a global context.
+ */
+ if (mt_key_initialized) {
+ if (((mt = pthread_getspecific(key)) != 0) ||
+ (__res_init_ctx() == 0 &&
+ (mt = pthread_getspecific(key)) != 0)) {
+ return (mt);
+ }
+ }
+#endif
+ return (&sharedctx);
+}
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_comp.c b/usr/src/lib/libresolv2_joy/common/resolv/res_comp.c
new file mode 100644
index 0000000000..abe9b55155
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_comp.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (c) 1985, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <ctype.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "port_after.h"
+
+#ifndef ORIGINAL_ISC_CODE
+#pragma weak __dn_skipname = dn_skipname
+#pragma weak __res_dnok = res_dnok
+#pragma weak __res_hnok = res_hnok
+#pragma weak __res_mailok = res_mailok
+#pragma weak __res_ownok = res_ownok
+#endif /* ORIGINAL_ISC_CODE */
+
+/*%
+ * Expand compressed domain name 'src' to full domain name.
+ *
+ * \li 'msg' is a pointer to the begining of the message,
+ * \li 'eom' points to the first location after the message,
+ * \li 'dst' is a pointer to a buffer of size 'dstsiz' for the result.
+ * \li Return size of compressed name or -1 if there was an error.
+ */
+int
+dn_expand(const u_char *msg, const u_char *eom, const u_char *src,
+ char *dst, int dstsiz)
+{
+ int n = ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz);
+
+ if (n > 0 && dst[0] == '.')
+ dst[0] = '\0';
+ return (n);
+}
+
+/*%
+ * Pack domain name 'exp_dn' in presentation form into 'comp_dn'.
+ *
+ * \li Return the size of the compressed name or -1.
+ * \li 'length' is the size of the array pointed to by 'comp_dn'.
+ */
+int
+dn_comp(const char *src, u_char *dst, int dstsiz,
+ u_char **dnptrs, u_char **lastdnptr)
+{
+ return (ns_name_compress(src, dst, (size_t)dstsiz,
+ (const u_char **)dnptrs,
+ (const u_char **)lastdnptr));
+}
+
+
+/*%
+ * Skip over a compressed domain name. Return the size or -1.
+ */
+int
+dn_skipname(const u_char *ptr, const u_char *eom) {
+ const u_char *saveptr = ptr;
+
+ if (ns_name_skip(&ptr, eom) == -1)
+ return (-1);
+ return (ptr - saveptr);
+}
+
+/*%
+ * Verify that a domain name uses an acceptable character set.
+ *
+ * Note the conspicuous absence of ctype macros in these definitions. On
+ * non-ASCII hosts, we can't depend on string literals or ctype macros to
+ * tell us anything about network-format data. The rest of the BIND system
+ * is not careful about this, but for some reason, we're doing it right here.
+ */
+#define PERIOD 0x2e
+#define hyphenchar(c) ((c) == 0x2d)
+#define bslashchar(c) ((c) == 0x5c)
+#ifdef SUNW_HNOK_UNDERSCORE
+#define underscorechar(c) ((c) == 0x5f)
+#endif /* SUNW_HNOK_UNDERSCORE */
+#define periodchar(c) ((c) == PERIOD)
+#define asterchar(c) ((c) == 0x2a)
+#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \
+ || ((c) >= 0x61 && (c) <= 0x7a))
+#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
+
+#define borderchar(c) (alphachar(c) || digitchar(c))
+#ifdef SUNW_HNOK_UNDERSCORE
+#define middlechar(c) (borderchar(c) || hyphenchar(c) || underscorechar(c))
+#else
+#define middlechar(c) (borderchar(c) || hyphenchar(c))
+#endif /* SUNW_HNOK_UNDERSCORE */
+#define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
+
+int
+res_hnok(const char *dn) {
+ int pch = PERIOD, ch = *dn++;
+
+ while (ch != '\0') {
+ int nch = *dn++;
+
+ if (periodchar(ch)) {
+ (void)NULL;
+ } else if (periodchar(pch)) {
+ if (!borderchar(ch))
+ return (0);
+ } else if (periodchar(nch) || nch == '\0') {
+ if (!borderchar(ch))
+ return (0);
+ } else {
+ if (!middlechar(ch))
+ return (0);
+ }
+ pch = ch, ch = nch;
+ }
+ return (1);
+}
+
+/*%
+ * hostname-like (A, MX, WKS) owners can have "*" as their first label
+ * but must otherwise be as a host name.
+ */
+int
+res_ownok(const char *dn) {
+ if (asterchar(dn[0])) {
+ if (periodchar(dn[1]))
+ return (res_hnok(dn+2));
+ if (dn[1] == '\0')
+ return (1);
+ }
+ return (res_hnok(dn));
+}
+
+/*%
+ * SOA RNAMEs and RP RNAMEs can have any printable character in their first
+ * label, but the rest of the name has to look like a host name.
+ */
+int
+res_mailok(const char *dn) {
+ int ch, escaped = 0;
+
+ /* "." is a valid missing representation */
+ if (*dn == '\0')
+ return (1);
+
+ /* otherwise <label>.<hostname> */
+ while ((ch = *dn++) != '\0') {
+ if (!domainchar(ch))
+ return (0);
+ if (!escaped && periodchar(ch))
+ break;
+ if (escaped)
+ escaped = 0;
+ else if (bslashchar(ch))
+ escaped = 1;
+ }
+ if (periodchar(ch))
+ return (res_hnok(dn));
+ return (0);
+}
+
+/*%
+ * This function is quite liberal, since RFC1034's character sets are only
+ * recommendations.
+ */
+int
+res_dnok(const char *dn) {
+ int ch;
+
+ while ((ch = *dn++) != '\0')
+ if (!domainchar(ch))
+ return (0);
+ return (1);
+}
+
+#ifdef BIND_4_COMPAT
+/*%
+ * This module must export the following externally-visible symbols:
+ * ___putlong
+ * ___putshort
+ * __getlong
+ * __getshort
+ * Note that one _ comes from C and the others come from us.
+ */
+
+#ifdef SOLARIS2
+#ifdef __putlong
+#undef __putlong
+#endif
+#ifdef __putshort
+#undef __putshort
+#endif
+#pragma weak putlong = __putlong
+#pragma weak putshort = __putshort
+#endif /* SOLARIS2 */
+
+void __putlong(u_int32_t src, u_char *dst) { ns_put32(src, dst); }
+void __putshort(u_int16_t src, u_char *dst) { ns_put16(src, dst); }
+#ifndef __ultrix__
+u_int32_t _getlong(const u_char *src) { return (ns_get32(src)); }
+u_int16_t _getshort(const u_char *src) { return (ns_get16(src)); }
+#endif /*__ultrix__*/
+#endif /*BIND_4_COMPAT*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_data.c b/usr/src/lib/libresolv2_joy/common/resolv/res_data.c
new file mode 100644
index 0000000000..514a9e56a1
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_data.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <res_update.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "port_after.h"
+
+#ifndef ORIGINAL_ISC_CODE
+#pragma weak __fp_nquery = fp_nquery
+#pragma weak __fp_query = fp_query
+#pragma weak __p_query = p_query
+#pragma weak __hostalias = hostalias
+#pragma weak __res_randomid = res_randomid
+#endif
+
+const char *_res_opcodes[] = {
+ "QUERY",
+ "IQUERY",
+ "CQUERYM",
+ "CQUERYU", /*%< experimental */
+ "NOTIFY", /*%< experimental */
+ "UPDATE",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ "11",
+ "12",
+ "13",
+ "ZONEINIT",
+ "ZONEREF",
+};
+
+#ifdef BIND_UPDATE
+const char *_res_sectioncodes[] = {
+ "ZONE",
+ "PREREQUISITES",
+ "UPDATE",
+ "ADDITIONAL",
+};
+#endif
+
+#undef _res
+#ifndef __BIND_NOSTATIC
+struct __res_state _res
+# if defined(__BIND_RES_TEXT)
+ = { RES_TIMEOUT, } /*%< Motorola, et al. */
+# endif
+ ;
+
+#ifdef ORIGINAL_ISC_CODE
+#if defined(DO_PTHREADS) || defined(__linux)
+#define _res (*__res_state())
+#endif
+#endif
+
+/* Proto. */
+
+int res_ourserver_p(const res_state, const struct sockaddr_in *);
+
+int
+res_init(void) {
+ extern int __res_vinit(res_state, int);
+
+ /*
+ * These three fields used to be statically initialized. This made
+ * it hard to use this code in a shared library. It is necessary,
+ * now that we're doing dynamic initialization here, that we preserve
+ * the old semantics: if an application modifies one of these three
+ * fields of _res before res_init() is called, res_init() will not
+ * alter them. Of course, if an application is setting them to
+ * _zero_ before calling res_init(), hoping to override what used
+ * to be the static default, we can't detect it and unexpected results
+ * will follow. Zero for any of these fields would make no sense,
+ * so one can safely assume that the applications were already getting
+ * unexpected results.
+ *
+ * _res.options is tricky since some apps were known to diddle the bits
+ * before res_init() was first called. We can't replicate that semantic
+ * with dynamic initialization (they may have turned bits off that are
+ * set in RES_DEFAULT). Our solution is to declare such applications
+ * "broken". They could fool us by setting RES_INIT but none do (yet).
+ */
+ if (!_res.retrans)
+ _res.retrans = RES_TIMEOUT;
+ if (!_res.retry)
+ _res.retry = 4;
+ if (!(_res.options & RES_INIT))
+ _res.options = RES_DEFAULT;
+
+ /*
+ * This one used to initialize implicitly to zero, so unless the app
+ * has set it to something in particular, we can randomize it now.
+ */
+ if (!_res.id)
+ _res.id = res_nrandomid(&_res);
+
+ return (__res_vinit(&_res, 1));
+}
+
+void
+p_query(const u_char *msg) {
+ fp_query(msg, stdout);
+}
+
+void
+fp_query(const u_char *msg, FILE *file) {
+ fp_nquery(msg, PACKETSZ, file);
+}
+
+void
+fp_nquery(const u_char *msg, int len, FILE *file) {
+ if ((_res.options & RES_INIT) == 0U && res_init() == -1)
+ return;
+
+ res_pquery(&_res, msg, len, file);
+}
+
+int
+res_mkquery(int op, /*!< opcode of query */
+ const char *dname, /*!< domain name */
+ int class, int type, /*!< class and type of query */
+ const u_char *data, /*!< resource record data */
+ int datalen, /*!< length of data */
+ const u_char *newrr_in, /*!< new rr for modify or append */
+ u_char *buf, /*!< buffer to put query */
+ int buflen) /*!< size of buffer */
+{
+ if ((_res.options & RES_INIT) == 0U && res_init() == -1) {
+ RES_SET_H_ERRNO(&_res, NETDB_INTERNAL);
+ return (-1);
+ }
+ return (res_nmkquery(&_res, op, dname, class, type,
+ data, datalen,
+ newrr_in, buf, buflen));
+}
+
+int
+res_mkupdate(ns_updrec *rrecp_in, u_char *buf, int buflen) {
+ if ((_res.options & RES_INIT) == 0U && res_init() == -1) {
+ RES_SET_H_ERRNO(&_res, NETDB_INTERNAL);
+ return (-1);
+ }
+
+ return (res_nmkupdate(&_res, rrecp_in, buf, buflen));
+}
+
+int
+res_query(const char *name, /*!< domain name */
+ int class, int type, /*!< class and type of query */
+ u_char *answer, /*!< buffer to put answer */
+ int anslen) /*!< size of answer buffer */
+{
+ if ((_res.options & RES_INIT) == 0U && res_init() == -1) {
+ RES_SET_H_ERRNO(&_res, NETDB_INTERNAL);
+ return (-1);
+ }
+ return (res_nquery(&_res, name, class, type, answer, anslen));
+}
+
+void
+res_send_setqhook(res_send_qhook hook) {
+ _res.qhook = hook;
+}
+
+void
+res_send_setrhook(res_send_rhook hook) {
+ _res.rhook = hook;
+}
+
+int
+res_isourserver(const struct sockaddr_in *inp) {
+ return (res_ourserver_p(&_res, inp));
+}
+
+int
+res_send(const u_char *buf, int buflen, u_char *ans, int anssiz) {
+ if ((_res.options & RES_INIT) == 0U && res_init() == -1) {
+ /* errno should have been set by res_init() in this case. */
+ return (-1);
+ }
+
+ return (res_nsend(&_res, buf, buflen, ans, anssiz));
+}
+
+int
+res_sendsigned(const u_char *buf, int buflen, ns_tsig_key *key,
+ u_char *ans, int anssiz)
+{
+ if ((_res.options & RES_INIT) == 0U && res_init() == -1) {
+ /* errno should have been set by res_init() in this case. */
+ return (-1);
+ }
+
+ return (res_nsendsigned(&_res, buf, buflen, key, ans, anssiz));
+}
+
+void
+res_close(void) {
+ res_nclose(&_res);
+}
+
+int
+res_update(ns_updrec *rrecp_in) {
+ if ((_res.options & RES_INIT) == 0U && res_init() == -1) {
+ RES_SET_H_ERRNO(&_res, NETDB_INTERNAL);
+ return (-1);
+ }
+
+ return (res_nupdate(&_res, rrecp_in, NULL));
+}
+
+int
+res_search(const char *name, /*!< domain name */
+ int class, int type, /*!< class and type of query */
+ u_char *answer, /*!< buffer to put answer */
+ int anslen) /*!< size of answer */
+{
+ if ((_res.options & RES_INIT) == 0U && res_init() == -1) {
+ RES_SET_H_ERRNO(&_res, NETDB_INTERNAL);
+ return (-1);
+ }
+
+ return (res_nsearch(&_res, name, class, type, answer, anslen));
+}
+
+int
+res_querydomain(const char *name,
+ const char *domain,
+ int class, int type, /*!< class and type of query */
+ u_char *answer, /*!< buffer to put answer */
+ int anslen) /*!< size of answer */
+{
+ if ((_res.options & RES_INIT) == 0U && res_init() == -1) {
+ RES_SET_H_ERRNO(&_res, NETDB_INTERNAL);
+ return (-1);
+ }
+
+ return (res_nquerydomain(&_res, name, domain,
+ class, type,
+ answer, anslen));
+}
+
+u_int
+res_randomid(void) {
+ if ((_res.options & RES_INIT) == 0U && res_init() == -1) {
+ RES_SET_H_ERRNO(&_res, NETDB_INTERNAL);
+ return (-1);
+ }
+
+ return (res_nrandomid(&_res));
+}
+
+const char *
+hostalias(const char *name) {
+ static char abuf[MAXDNAME];
+
+ return (res_hostalias(&_res, name, abuf, sizeof abuf));
+}
+
+#ifdef ultrix
+int
+local_hostname_length(const char *hostname) {
+ int len_host, len_domain;
+
+ if (!*_res.defdname)
+ res_init();
+ len_host = strlen(hostname);
+ len_domain = strlen(_res.defdname);
+ if (len_host > len_domain &&
+ !strcasecmp(hostname + len_host - len_domain, _res.defdname) &&
+ hostname[len_host - len_domain - 1] == '.')
+ return (len_host - len_domain - 1);
+ return (0);
+}
+#endif /*ultrix*/
+
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_debug.c b/usr/src/lib/libresolv2_joy/common/resolv/res_debug.c
new file mode 100644
index 0000000000..2fcc1ad2ca
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_debug.c
@@ -0,0 +1,1247 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Portions Copyright (C) 2004, 2005, 2008, 2009 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1996-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 1985
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Portions Copyright (c) 1995 by International Business Machines, Inc.
+ *
+ * International Business Machines, Inc. (hereinafter called IBM) grants
+ * permission under its copyrights to use, copy, modify, and distribute this
+ * Software with or without fee, provided that the above copyright notice and
+ * all paragraphs of this notice appear in all copies, and that the name of IBM
+ * not be used in connection with the marketing of any product incorporating
+ * the Software or modifications thereof, without specific, written prior
+ * permission.
+ *
+ * To the extent it has a right to do so, IBM grants an immunity from suit
+ * under its patents, if any, for the use, sale or manufacture of products to
+ * the extent that such products are used for performing Domain Name System
+ * dynamic updates in TCP/IP networks by means of the Software. No immunity is
+ * granted for any product per se or for any other function of any product.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
+ * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
+ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <resolv_mt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "port_after.h"
+
+#ifdef SPRINTF_CHAR
+# define SPRINTF(x) strlen(sprintf/**/x)
+#else
+# define SPRINTF(x) sprintf x
+#endif
+
+extern const char *_res_opcodes[];
+extern const char *_res_sectioncodes[];
+
+#ifndef ORIGINAL_ISC_CODE
+#pragma weak __dn_count_labels = dn_count_labels
+#pragma weak __fp_resstat = fp_resstat
+#pragma weak __loc_aton = loc_aton
+#pragma weak __loc_ntoa = loc_ntoa
+#pragma weak __p_cdname = p_cdname
+#pragma weak __p_class = p_class
+#pragma weak __p_section = p_section
+#pragma weak __p_time = p_time
+#pragma weak __p_type = p_type
+#pragma weak __sym_ntop = sym_ntop
+#pragma weak __sym_ntos = sym_ntos
+#pragma weak __sym_ston = sym_ston
+#endif /* ORIGINAL_ISC_CODE */
+
+/*%
+ * Print the current options.
+ */
+void
+fp_resstat(const res_state statp, FILE *file) {
+ u_long mask;
+
+ fprintf(file, ";; res options:");
+ for (mask = 1; mask != 0U; mask <<= 1)
+ if (statp->options & mask)
+ fprintf(file, " %s", p_option(mask));
+ putc('\n', file);
+}
+
+static void
+do_section(const res_state statp,
+ ns_msg *handle, ns_sect section,
+ int pflag, FILE *file)
+{
+ int n, sflag, rrnum;
+ static int buflen = 2048;
+ char *buf;
+ ns_opcode opcode;
+ ns_rr rr;
+
+ /*
+ * Print answer records.
+ */
+ sflag = (statp->pfcode & pflag);
+ if (statp->pfcode && !sflag)
+ return;
+
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ fprintf(file, ";; memory allocation failure\n");
+ return;
+ }
+
+ opcode = (ns_opcode) ns_msg_getflag(*handle, ns_f_opcode);
+ rrnum = 0;
+ for (;;) {
+ if (ns_parserr(handle, section, rrnum, &rr)) {
+ if (errno != ENODEV)
+ fprintf(file, ";; ns_parserr: %s\n",
+ strerror(errno));
+ else if (rrnum > 0 && sflag != 0 &&
+ (statp->pfcode & RES_PRF_HEAD1))
+ putc('\n', file);
+ goto cleanup;
+ }
+ if (rrnum == 0 && sflag != 0 && (statp->pfcode & RES_PRF_HEAD1))
+ fprintf(file, ";; %s SECTION:\n",
+ p_section(section, opcode));
+ if (section == ns_s_qd)
+ fprintf(file, ";;\t%s, type = %s, class = %s\n",
+ ns_rr_name(rr),
+ p_type(ns_rr_type(rr)),
+ p_class(ns_rr_class(rr)));
+ else if (section == ns_s_ar && ns_rr_type(rr) == ns_t_opt) {
+ u_int16_t optcode, optlen, rdatalen = ns_rr_rdlen(rr);
+ u_int32_t ttl = ns_rr_ttl(rr);
+
+ fprintf(file,
+ "; EDNS: version: %u, udp=%u, flags=%04x\n",
+ (ttl>>16)&0xff, ns_rr_class(rr), ttl&0xffff);
+
+ while (rdatalen >= 4) {
+ const u_char *cp = ns_rr_rdata(rr);
+ int i;
+
+ GETSHORT(optcode, cp);
+ GETSHORT(optlen, cp);
+
+ if (optcode == NS_OPT_NSID) {
+ fputs("; NSID: ", file);
+ if (optlen == 0) {
+ fputs("; NSID\n", file);
+ } else {
+ fputs("; NSID: ", file);
+ for (i = 0; i < optlen; i++)
+ fprintf(file, "%02x ",
+ cp[i]);
+ fputs(" (",file);
+ for (i = 0; i < optlen; i++)
+ fprintf(file, "%c",
+ isprint(cp[i])?
+ cp[i] : '.');
+ fputs(")\n", file);
+ }
+ } else {
+ if (optlen == 0) {
+ fprintf(file, "; OPT=%u\n",
+ optcode);
+ } else {
+ fprintf(file, "; OPT=%u: ",
+ optcode);
+ for (i = 0; i < optlen; i++)
+ fprintf(file, "%02x ",
+ cp[i]);
+ fputs(" (",file);
+ for (i = 0; i < optlen; i++)
+ fprintf(file, "%c",
+ isprint(cp[i]) ?
+ cp[i] : '.');
+ fputs(")\n", file);
+ }
+ }
+ rdatalen -= 4 + optlen;
+ }
+ } else {
+ n = ns_sprintrr(handle, &rr, NULL, NULL,
+ buf, buflen);
+ if (n < 0) {
+ if (errno == ENOSPC) {
+ free(buf);
+ buf = NULL;
+ if (buflen < 131072)
+ buf = malloc(buflen += 1024);
+ if (buf == NULL) {
+ fprintf(file,
+ ";; memory allocation failure\n");
+ return;
+ }
+ continue;
+ }
+ fprintf(file, ";; ns_sprintrr: %s\n",
+ strerror(errno));
+ goto cleanup;
+ }
+ fputs(buf, file);
+ fputc('\n', file);
+ }
+ rrnum++;
+ }
+ cleanup:
+ if (buf != NULL)
+ free(buf);
+}
+
+/*%
+ * Print the contents of a query.
+ * This is intended to be primarily a debugging routine.
+ */
+void
+res_pquery(const res_state statp, const u_char *msg, int len, FILE *file) {
+ ns_msg handle;
+ int qdcount, ancount, nscount, arcount;
+ u_int opcode, rcode, id;
+
+ if (ns_initparse(msg, len, &handle) < 0) {
+ fprintf(file, ";; ns_initparse: %s\n", strerror(errno));
+ return;
+ }
+ opcode = ns_msg_getflag(handle, ns_f_opcode);
+ rcode = ns_msg_getflag(handle, ns_f_rcode);
+ id = ns_msg_id(handle);
+ qdcount = ns_msg_count(handle, ns_s_qd);
+ ancount = ns_msg_count(handle, ns_s_an);
+ nscount = ns_msg_count(handle, ns_s_ns);
+ arcount = ns_msg_count(handle, ns_s_ar);
+
+ /*
+ * Print header fields.
+ */
+ if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEADX) || rcode)
+ fprintf(file,
+ ";; ->>HEADER<<- opcode: %s, status: %s, id: %d\n",
+ _res_opcodes[opcode], p_rcode(rcode), id);
+ if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEADX))
+ putc(';', file);
+ if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEAD2)) {
+ fprintf(file, "; flags:");
+ if (ns_msg_getflag(handle, ns_f_qr))
+ fprintf(file, " qr");
+ if (ns_msg_getflag(handle, ns_f_aa))
+ fprintf(file, " aa");
+ if (ns_msg_getflag(handle, ns_f_tc))
+ fprintf(file, " tc");
+ if (ns_msg_getflag(handle, ns_f_rd))
+ fprintf(file, " rd");
+ if (ns_msg_getflag(handle, ns_f_ra))
+ fprintf(file, " ra");
+ if (ns_msg_getflag(handle, ns_f_z))
+ fprintf(file, " ??");
+ if (ns_msg_getflag(handle, ns_f_ad))
+ fprintf(file, " ad");
+ if (ns_msg_getflag(handle, ns_f_cd))
+ fprintf(file, " cd");
+ }
+ if ((!statp->pfcode) || (statp->pfcode & RES_PRF_HEAD1)) {
+ fprintf(file, "; %s: %d",
+ p_section(ns_s_qd, opcode), qdcount);
+ fprintf(file, ", %s: %d",
+ p_section(ns_s_an, opcode), ancount);
+ fprintf(file, ", %s: %d",
+ p_section(ns_s_ns, opcode), nscount);
+ fprintf(file, ", %s: %d",
+ p_section(ns_s_ar, opcode), arcount);
+ }
+ if ((!statp->pfcode) || (statp->pfcode &
+ (RES_PRF_HEADX | RES_PRF_HEAD2 | RES_PRF_HEAD1))) {
+ putc('\n',file);
+ }
+ /*
+ * Print the various sections.
+ */
+ do_section(statp, &handle, ns_s_qd, RES_PRF_QUES, file);
+ do_section(statp, &handle, ns_s_an, RES_PRF_ANS, file);
+ do_section(statp, &handle, ns_s_ns, RES_PRF_AUTH, file);
+ do_section(statp, &handle, ns_s_ar, RES_PRF_ADD, file);
+ if (qdcount == 0 && ancount == 0 &&
+ nscount == 0 && arcount == 0)
+ putc('\n', file);
+}
+
+const u_char *
+p_cdnname(const u_char *cp, const u_char *msg, int len, FILE *file) {
+ char name[MAXDNAME];
+ int n;
+
+ if ((n = dn_expand(msg, msg + len, cp, name, sizeof name)) < 0)
+ return (NULL);
+ if (name[0] == '\0')
+ putc('.', file);
+ else
+ fputs(name, file);
+ return (cp + n);
+}
+
+const u_char *
+p_cdname(const u_char *cp, const u_char *msg, FILE *file) {
+ return (p_cdnname(cp, msg, PACKETSZ, file));
+}
+
+/*%
+ * Return a fully-qualified domain name from a compressed name (with
+ length supplied). */
+
+const u_char *
+p_fqnname(cp, msg, msglen, name, namelen)
+ const u_char *cp, *msg;
+ int msglen;
+ char *name;
+ int namelen;
+{
+ int n, newlen;
+
+ if ((n = dn_expand(msg, cp + msglen, cp, name, namelen)) < 0)
+ return (NULL);
+ newlen = strlen(name);
+ if (newlen == 0 || name[newlen - 1] != '.') {
+ if (newlen + 1 >= namelen) /*%< Lack space for final dot */
+ return (NULL);
+ else
+ strcpy(name + newlen, ".");
+ }
+ return (cp + n);
+}
+
+/* XXX: the rest of these functions need to become length-limited, too. */
+
+const u_char *
+p_fqname(const u_char *cp, const u_char *msg, FILE *file) {
+ char name[MAXDNAME];
+ const u_char *n;
+
+ n = p_fqnname(cp, msg, MAXCDNAME, name, sizeof name);
+ if (n == NULL)
+ return (NULL);
+ fputs(name, file);
+ return (n);
+}
+
+/*%
+ * Names of RR classes and qclasses. Classes and qclasses are the same, except
+ * that C_ANY is a qclass but not a class. (You can ask for records of class
+ * C_ANY, but you can't have any records of that class in the database.)
+ */
+const struct res_sym __p_class_syms[] = {
+ {C_IN, "IN", (char *)0},
+ {C_CHAOS, "CH", (char *)0},
+ {C_CHAOS, "CHAOS", (char *)0},
+ {C_HS, "HS", (char *)0},
+ {C_HS, "HESIOD", (char *)0},
+ {C_ANY, "ANY", (char *)0},
+ {C_NONE, "NONE", (char *)0},
+ {C_IN, (char *)0, (char *)0}
+};
+
+/*%
+ * Names of message sections.
+ */
+const struct res_sym __p_default_section_syms[] = {
+ {ns_s_qd, "QUERY", (char *)0},
+ {ns_s_an, "ANSWER", (char *)0},
+ {ns_s_ns, "AUTHORITY", (char *)0},
+ {ns_s_ar, "ADDITIONAL", (char *)0},
+ {0, (char *)0, (char *)0}
+};
+
+const struct res_sym __p_update_section_syms[] = {
+ {S_ZONE, "ZONE", (char *)0},
+ {S_PREREQ, "PREREQUISITE", (char *)0},
+ {S_UPDATE, "UPDATE", (char *)0},
+ {S_ADDT, "ADDITIONAL", (char *)0},
+ {0, (char *)0, (char *)0}
+};
+
+const struct res_sym __p_key_syms[] = {
+ {NS_ALG_MD5RSA, "RSA", "RSA KEY with MD5 hash"},
+ {NS_ALG_DH, "DH", "Diffie Hellman"},
+ {NS_ALG_DSA, "DSA", "Digital Signature Algorithm"},
+ {NS_ALG_EXPIRE_ONLY, "EXPIREONLY", "No algorithm"},
+ {NS_ALG_PRIVATE_OID, "PRIVATE", "Algorithm obtained from OID"},
+ {0, NULL, NULL}
+};
+
+const struct res_sym __p_cert_syms[] = {
+ {cert_t_pkix, "PKIX", "PKIX (X.509v3) Certificate"},
+ {cert_t_spki, "SPKI", "SPKI certificate"},
+ {cert_t_pgp, "PGP", "PGP certificate"},
+ {cert_t_url, "URL", "URL Private"},
+ {cert_t_oid, "OID", "OID Private"},
+ {0, NULL, NULL}
+};
+
+/*%
+ * Names of RR types and qtypes. Types and qtypes are the same, except
+ * that T_ANY is a qtype but not a type. (You can ask for records of type
+ * T_ANY, but you can't have any records of that type in the database.)
+ */
+const struct res_sym __p_type_syms[] = {
+ {ns_t_a, "A", "address"},
+ {ns_t_ns, "NS", "name server"},
+ {ns_t_md, "MD", "mail destination (deprecated)"},
+ {ns_t_mf, "MF", "mail forwarder (deprecated)"},
+ {ns_t_cname, "CNAME", "canonical name"},
+ {ns_t_soa, "SOA", "start of authority"},
+ {ns_t_mb, "MB", "mailbox"},
+ {ns_t_mg, "MG", "mail group member"},
+ {ns_t_mr, "MR", "mail rename"},
+ {ns_t_null, "NULL", "null"},
+ {ns_t_wks, "WKS", "well-known service (deprecated)"},
+ {ns_t_ptr, "PTR", "domain name pointer"},
+ {ns_t_hinfo, "HINFO", "host information"},
+ {ns_t_minfo, "MINFO", "mailbox information"},
+ {ns_t_mx, "MX", "mail exchanger"},
+ {ns_t_txt, "TXT", "text"},
+ {ns_t_rp, "RP", "responsible person"},
+ {ns_t_afsdb, "AFSDB", "DCE or AFS server"},
+ {ns_t_x25, "X25", "X25 address"},
+ {ns_t_isdn, "ISDN", "ISDN address"},
+ {ns_t_rt, "RT", "router"},
+ {ns_t_nsap, "NSAP", "nsap address"},
+ {ns_t_nsap_ptr, "NSAP_PTR", "domain name pointer"},
+ {ns_t_sig, "SIG", "signature"},
+ {ns_t_key, "KEY", "key"},
+ {ns_t_px, "PX", "mapping information"},
+ {ns_t_gpos, "GPOS", "geographical position (withdrawn)"},
+ {ns_t_aaaa, "AAAA", "IPv6 address"},
+ {ns_t_loc, "LOC", "location"},
+ {ns_t_nxt, "NXT", "next valid name (unimplemented)"},
+ {ns_t_eid, "EID", "endpoint identifier (unimplemented)"},
+ {ns_t_nimloc, "NIMLOC", "NIMROD locator (unimplemented)"},
+ {ns_t_srv, "SRV", "server selection"},
+ {ns_t_atma, "ATMA", "ATM address (unimplemented)"},
+ {ns_t_naptr, "NAPTR", "naptr"},
+ {ns_t_kx, "KX", "key exchange"},
+ {ns_t_cert, "CERT", "certificate"},
+ {ns_t_a6, "A", "IPv6 address (experminental)"},
+ {ns_t_dname, "DNAME", "non-terminal redirection"},
+ {ns_t_opt, "OPT", "opt"},
+ {ns_t_apl, "apl", "apl"},
+ {ns_t_ds, "DS", "delegation signer"},
+ {ns_t_sshfp, "SSFP", "SSH fingerprint"},
+ {ns_t_ipseckey, "IPSECKEY", "IPSEC key"},
+ {ns_t_rrsig, "RRSIG", "rrsig"},
+ {ns_t_nsec, "NSEC", "nsec"},
+ {ns_t_dnskey, "DNSKEY", "DNS key"},
+ {ns_t_dhcid, "DHCID", "dynamic host configuration identifier"},
+ {ns_t_nsec3, "NSEC3", "nsec3"},
+ {ns_t_nsec3param, "NSEC3PARAM", "NSEC3 parameters"},
+ {ns_t_hip, "HIP", "host identity protocol"},
+ {ns_t_spf, "SPF", "sender policy framework"},
+ {ns_t_tkey, "TKEY", "tkey"},
+ {ns_t_tsig, "TSIG", "transaction signature"},
+ {ns_t_ixfr, "IXFR", "incremental zone transfer"},
+ {ns_t_axfr, "AXFR", "zone transfer"},
+ {ns_t_zxfr, "ZXFR", "compressed zone transfer"},
+ {ns_t_mailb, "MAILB", "mailbox-related data (deprecated)"},
+ {ns_t_maila, "MAILA", "mail agent (deprecated)"},
+ {ns_t_naptr, "NAPTR", "URN Naming Authority"},
+ {ns_t_kx, "KX", "Key Exchange"},
+ {ns_t_cert, "CERT", "Certificate"},
+ {ns_t_a6, "A6", "IPv6 Address"},
+ {ns_t_dname, "DNAME", "dname"},
+ {ns_t_sink, "SINK", "Kitchen Sink (experimental)"},
+ {ns_t_opt, "OPT", "EDNS Options"},
+ {ns_t_any, "ANY", "\"any\""},
+ {ns_t_dlv, "DLV", "DNSSEC look-aside validation"},
+ {0, NULL, NULL}
+};
+
+/*%
+ * Names of DNS rcodes.
+ */
+const struct res_sym __p_rcode_syms[] = {
+ {ns_r_noerror, "NOERROR", "no error"},
+ {ns_r_formerr, "FORMERR", "format error"},
+ {ns_r_servfail, "SERVFAIL", "server failed"},
+ {ns_r_nxdomain, "NXDOMAIN", "no such domain name"},
+ {ns_r_notimpl, "NOTIMP", "not implemented"},
+ {ns_r_refused, "REFUSED", "refused"},
+ {ns_r_yxdomain, "YXDOMAIN", "domain name exists"},
+ {ns_r_yxrrset, "YXRRSET", "rrset exists"},
+ {ns_r_nxrrset, "NXRRSET", "rrset doesn't exist"},
+ {ns_r_notauth, "NOTAUTH", "not authoritative"},
+ {ns_r_notzone, "NOTZONE", "Not in zone"},
+ {ns_r_max, "", ""},
+ {ns_r_badsig, "BADSIG", "bad signature"},
+ {ns_r_badkey, "BADKEY", "bad key"},
+ {ns_r_badtime, "BADTIME", "bad time"},
+ {0, NULL, NULL}
+};
+
+int
+sym_ston(const struct res_sym *syms, const char *name, int *success) {
+ for ((void)NULL; syms->name != 0; syms++) {
+ if (strcasecmp (name, syms->name) == 0) {
+ if (success)
+ *success = 1;
+ return (syms->number);
+ }
+ }
+ if (success)
+ *success = 0;
+ return (syms->number); /*%< The default value. */
+}
+
+const char *
+sym_ntos(const struct res_sym *syms, int number, int *success) {
+ char *unname = sym_ntos_unname;
+
+ for ((void)NULL; syms->name != 0; syms++) {
+ if (number == syms->number) {
+ if (success)
+ *success = 1;
+ return (syms->name);
+ }
+ }
+
+ sprintf(unname, "%d", number); /*%< XXX nonreentrant */
+ if (success)
+ *success = 0;
+ return (unname);
+}
+
+const char *
+sym_ntop(const struct res_sym *syms, int number, int *success) {
+ char *unname = sym_ntop_unname;
+
+ for ((void)NULL; syms->name != 0; syms++) {
+ if (number == syms->number) {
+ if (success)
+ *success = 1;
+ return (syms->humanname);
+ }
+ }
+ sprintf(unname, "%d", number); /*%< XXX nonreentrant */
+ if (success)
+ *success = 0;
+ return (unname);
+}
+
+/*%
+ * Return a string for the type.
+ */
+const char *
+p_type(int type) {
+ int success;
+ const char *result;
+ static char typebuf[20];
+
+ result = sym_ntos(__p_type_syms, type, &success);
+ if (success)
+ return (result);
+ if (type < 0 || type > 0xffff)
+ return ("BADTYPE");
+ sprintf(typebuf, "TYPE%d", type);
+ return (typebuf);
+}
+
+/*%
+ * Return a string for the type.
+ */
+const char *
+p_section(int section, int opcode) {
+ const struct res_sym *symbols;
+
+ switch (opcode) {
+ case ns_o_update:
+ symbols = __p_update_section_syms;
+ break;
+ default:
+ symbols = __p_default_section_syms;
+ break;
+ }
+ return (sym_ntos(symbols, section, (int *)0));
+}
+
+/*%
+ * Return a mnemonic for class.
+ */
+const char *
+p_class(int class) {
+ int success;
+ const char *result;
+ static char classbuf[20];
+
+ result = sym_ntos(__p_class_syms, class, &success);
+ if (success)
+ return (result);
+ if (class < 0 || class > 0xffff)
+ return ("BADCLASS");
+ sprintf(classbuf, "CLASS%d", class);
+ return (classbuf);
+}
+
+/*%
+ * Return a mnemonic for an option
+ */
+const char *
+p_option(u_long option) {
+ char *nbuf = p_option_nbuf;
+
+ switch (option) {
+ case RES_INIT: return "init";
+ case RES_DEBUG: return "debug";
+ case RES_AAONLY: return "aaonly(unimpl)";
+ case RES_USEVC: return "usevc";
+ case RES_PRIMARY: return "primry(unimpl)";
+ case RES_IGNTC: return "igntc";
+ case RES_RECURSE: return "recurs";
+ case RES_DEFNAMES: return "defnam";
+ case RES_STAYOPEN: return "styopn";
+ case RES_DNSRCH: return "dnsrch";
+ case RES_INSECURE1: return "insecure1";
+ case RES_INSECURE2: return "insecure2";
+ case RES_NOALIASES: return "noaliases";
+ case RES_USE_INET6: return "inet6";
+#ifdef RES_USE_EDNS0 /*%< KAME extension */
+ case RES_USE_EDNS0: return "edns0";
+ case RES_NSID: return "nsid";
+#endif
+#ifdef RES_USE_DNAME
+ case RES_USE_DNAME: return "dname";
+#endif
+#ifdef RES_USE_DNSSEC
+ case RES_USE_DNSSEC: return "dnssec";
+#endif
+#ifdef RES_NOTLDQUERY
+ case RES_NOTLDQUERY: return "no-tld-query";
+#endif
+#ifdef RES_NO_NIBBLE2
+ case RES_NO_NIBBLE2: return "no-nibble2";
+#endif
+ /* XXX nonreentrant */
+ default: sprintf(nbuf, "?0x%lx?", (u_long)option);
+ return (nbuf);
+ }
+}
+
+/*%
+ * Return a mnemonic for a time to live.
+ */
+const char *
+p_time(u_int32_t value) {
+ char *nbuf = p_time_nbuf;
+
+ if (ns_format_ttl(value, nbuf, sizeof nbuf) < 0)
+ sprintf(nbuf, "%u", value);
+ return (nbuf);
+}
+
+/*%
+ * Return a string for the rcode.
+ */
+const char *
+p_rcode(int rcode) {
+ return (sym_ntos(__p_rcode_syms, rcode, (int *)0));
+}
+
+/*%
+ * Return a string for a res_sockaddr_union.
+ */
+const char *
+p_sockun(union res_sockaddr_union u, char *buf, size_t size) {
+ char ret[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:123.123.123.123"];
+
+ switch (u.sin.sin_family) {
+ case AF_INET:
+ inet_ntop(AF_INET, &u.sin.sin_addr, ret, sizeof ret);
+ break;
+#ifdef HAS_INET6_STRUCTS
+ case AF_INET6:
+ inet_ntop(AF_INET6, &u.sin6.sin6_addr, ret, sizeof ret);
+ break;
+#endif
+ default:
+ sprintf(ret, "[af%d]", u.sin.sin_family);
+ break;
+ }
+ if (size > 0U) {
+ strncpy(buf, ret, size - 1);
+ buf[size - 1] = '0';
+ }
+ return (buf);
+}
+
+/*%
+ * routines to convert between on-the-wire RR format and zone file format.
+ * Does not contain conversion to/from decimal degrees; divide or multiply
+ * by 60*60*1000 for that.
+ */
+
+static unsigned int poweroften[10] = {1, 10, 100, 1000, 10000, 100000,
+ 1000000,10000000,100000000,1000000000};
+
+/*% takes an XeY precision/size value, returns a string representation. */
+static const char *
+precsize_ntoa(prec)
+ u_int8_t prec;
+{
+ char *retbuf = precsize_ntoa_retbuf;
+ unsigned long val;
+ int mantissa, exponent;
+
+ mantissa = (int)((prec >> 4) & 0x0f) % 10;
+ exponent = (int)((prec >> 0) & 0x0f) % 10;
+
+ val = mantissa * poweroften[exponent];
+
+ (void) sprintf(retbuf, "%lu.%.2lu", val/100, val%100);
+ return (retbuf);
+}
+
+/*% converts ascii size/precision X * 10**Y(cm) to 0xXY. moves pointer. */
+static u_int8_t
+precsize_aton(const char **strptr) {
+ unsigned int mval = 0, cmval = 0;
+ u_int8_t retval = 0;
+ const char *cp;
+ int exponent;
+ int mantissa;
+
+ cp = *strptr;
+
+ while (isdigit((unsigned char)*cp))
+ mval = mval * 10 + (*cp++ - '0');
+
+ if (*cp == '.') { /*%< centimeters */
+ cp++;
+ if (isdigit((unsigned char)*cp)) {
+ cmval = (*cp++ - '0') * 10;
+ if (isdigit((unsigned char)*cp)) {
+ cmval += (*cp++ - '0');
+ }
+ }
+ }
+ cmval = (mval * 100) + cmval;
+
+ for (exponent = 0; exponent < 9; exponent++)
+ if (cmval < poweroften[exponent+1])
+ break;
+
+ mantissa = cmval / poweroften[exponent];
+ if (mantissa > 9)
+ mantissa = 9;
+
+ retval = (mantissa << 4) | exponent;
+
+ *strptr = cp;
+
+ return (retval);
+}
+
+/*% converts ascii lat/lon to unsigned encoded 32-bit number. moves pointer. */
+static u_int32_t
+latlon2ul(const char **latlonstrptr, int *which) {
+ const char *cp;
+ u_int32_t retval;
+ int deg = 0, min = 0, secs = 0, secsfrac = 0;
+
+ cp = *latlonstrptr;
+
+ while (isdigit((unsigned char)*cp))
+ deg = deg * 10 + (*cp++ - '0');
+
+ while (isspace((unsigned char)*cp))
+ cp++;
+
+ if (!(isdigit((unsigned char)*cp)))
+ goto fndhemi;
+
+ while (isdigit((unsigned char)*cp))
+ min = min * 10 + (*cp++ - '0');
+
+ while (isspace((unsigned char)*cp))
+ cp++;
+
+ if (!(isdigit((unsigned char)*cp)))
+ goto fndhemi;
+
+ while (isdigit((unsigned char)*cp))
+ secs = secs * 10 + (*cp++ - '0');
+
+ if (*cp == '.') { /*%< decimal seconds */
+ cp++;
+ if (isdigit((unsigned char)*cp)) {
+ secsfrac = (*cp++ - '0') * 100;
+ if (isdigit((unsigned char)*cp)) {
+ secsfrac += (*cp++ - '0') * 10;
+ if (isdigit((unsigned char)*cp)) {
+ secsfrac += (*cp++ - '0');
+ }
+ }
+ }
+ }
+
+ while (!isspace((unsigned char)*cp)) /*%< if any trailing garbage */
+ cp++;
+
+ while (isspace((unsigned char)*cp))
+ cp++;
+
+ fndhemi:
+ switch (*cp) {
+ case 'N': case 'n':
+ case 'E': case 'e':
+ retval = ((unsigned)1<<31)
+ + (((((deg * 60) + min) * 60) + secs) * 1000)
+ + secsfrac;
+ break;
+ case 'S': case 's':
+ case 'W': case 'w':
+ retval = ((unsigned)1<<31)
+ - (((((deg * 60) + min) * 60) + secs) * 1000)
+ - secsfrac;
+ break;
+ default:
+ retval = 0; /*%< invalid value -- indicates error */
+ break;
+ }
+
+ switch (*cp) {
+ case 'N': case 'n':
+ case 'S': case 's':
+ *which = 1; /*%< latitude */
+ break;
+ case 'E': case 'e':
+ case 'W': case 'w':
+ *which = 2; /*%< longitude */
+ break;
+ default:
+ *which = 0; /*%< error */
+ break;
+ }
+
+ cp++; /*%< skip the hemisphere */
+ while (!isspace((unsigned char)*cp)) /*%< if any trailing garbage */
+ cp++;
+
+ while (isspace((unsigned char)*cp)) /*%< move to next field */
+ cp++;
+
+ *latlonstrptr = cp;
+
+ return (retval);
+}
+
+/*%
+ * converts a zone file representation in a string to an RDATA on-the-wire
+ * representation. */
+int
+loc_aton(ascii, binary)
+ const char *ascii;
+ u_char *binary;
+{
+ const char *cp, *maxcp;
+ u_char *bcp;
+
+ u_int32_t latit = 0, longit = 0, alt = 0;
+ u_int32_t lltemp1 = 0, lltemp2 = 0;
+ int altmeters = 0, altfrac = 0, altsign = 1;
+ u_int8_t hp = 0x16; /*%< default = 1e6 cm = 10000.00m = 10km */
+ u_int8_t vp = 0x13; /*%< default = 1e3 cm = 10.00m */
+ u_int8_t siz = 0x12; /*%< default = 1e2 cm = 1.00m */
+ int which1 = 0, which2 = 0;
+
+ cp = ascii;
+ maxcp = cp + strlen(ascii);
+
+ lltemp1 = latlon2ul(&cp, &which1);
+
+ lltemp2 = latlon2ul(&cp, &which2);
+
+ switch (which1 + which2) {
+ case 3: /*%< 1 + 2, the only valid combination */
+ if ((which1 == 1) && (which2 == 2)) { /*%< normal case */
+ latit = lltemp1;
+ longit = lltemp2;
+ } else if ((which1 == 2) && (which2 == 1)) { /*%< reversed */
+ longit = lltemp1;
+ latit = lltemp2;
+ } else { /*%< some kind of brokenness */
+ return (0);
+ }
+ break;
+ default: /*%< we didn't get one of each */
+ return (0);
+ }
+
+ /* altitude */
+ if (*cp == '-') {
+ altsign = -1;
+ cp++;
+ }
+
+ if (*cp == '+')
+ cp++;
+
+ while (isdigit((unsigned char)*cp))
+ altmeters = altmeters * 10 + (*cp++ - '0');
+
+ if (*cp == '.') { /*%< decimal meters */
+ cp++;
+ if (isdigit((unsigned char)*cp)) {
+ altfrac = (*cp++ - '0') * 10;
+ if (isdigit((unsigned char)*cp)) {
+ altfrac += (*cp++ - '0');
+ }
+ }
+ }
+
+ alt = (10000000 + (altsign * (altmeters * 100 + altfrac)));
+
+ while (!isspace((unsigned char)*cp) && (cp < maxcp)) /*%< if trailing garbage or m */
+ cp++;
+
+ while (isspace((unsigned char)*cp) && (cp < maxcp))
+ cp++;
+
+ if (cp >= maxcp)
+ goto defaults;
+
+ siz = precsize_aton(&cp);
+
+ while (!isspace((unsigned char)*cp) && (cp < maxcp)) /*%< if trailing garbage or m */
+ cp++;
+
+ while (isspace((unsigned char)*cp) && (cp < maxcp))
+ cp++;
+
+ if (cp >= maxcp)
+ goto defaults;
+
+ hp = precsize_aton(&cp);
+
+ while (!isspace((unsigned char)*cp) && (cp < maxcp)) /*%< if trailing garbage or m */
+ cp++;
+
+ while (isspace((unsigned char)*cp) && (cp < maxcp))
+ cp++;
+
+ if (cp >= maxcp)
+ goto defaults;
+
+ vp = precsize_aton(&cp);
+
+ defaults:
+
+ bcp = binary;
+ *bcp++ = (u_int8_t) 0; /*%< version byte */
+ *bcp++ = siz;
+ *bcp++ = hp;
+ *bcp++ = vp;
+ PUTLONG(latit,bcp);
+ PUTLONG(longit,bcp);
+ PUTLONG(alt,bcp);
+
+ return (16); /*%< size of RR in octets */
+}
+
+/*% takes an on-the-wire LOC RR and formats it in a human readable format. */
+const char *
+loc_ntoa(binary, ascii)
+ const u_char *binary;
+ char *ascii;
+{
+ static const char *error = "?";
+ static char tmpbuf[sizeof
+"1000 60 60.000 N 1000 60 60.000 W -12345678.00m 90000000.00m 90000000.00m 90000000.00m"];
+ const u_char *cp = binary;
+
+ int latdeg, latmin, latsec, latsecfrac;
+ int longdeg, longmin, longsec, longsecfrac;
+ char northsouth, eastwest;
+ const char *altsign;
+ int altmeters, altfrac;
+
+ const u_int32_t referencealt = 100000 * 100;
+
+ int32_t latval, longval, altval;
+ u_int32_t templ;
+ u_int8_t sizeval, hpval, vpval, versionval;
+
+ char *sizestr, *hpstr, *vpstr;
+
+ versionval = *cp++;
+
+ if (ascii == NULL)
+ ascii = tmpbuf;
+
+ if (versionval) {
+ (void) sprintf(ascii, "; error: unknown LOC RR version");
+ return (ascii);
+ }
+
+ sizeval = *cp++;
+
+ hpval = *cp++;
+ vpval = *cp++;
+
+ GETLONG(templ, cp);
+ latval = (templ - ((unsigned)1<<31));
+
+ GETLONG(templ, cp);
+ longval = (templ - ((unsigned)1<<31));
+
+ GETLONG(templ, cp);
+ if (templ < referencealt) { /*%< below WGS 84 spheroid */
+ altval = referencealt - templ;
+ altsign = "-";
+ } else {
+ altval = templ - referencealt;
+ altsign = "";
+ }
+
+ if (latval < 0) {
+ northsouth = 'S';
+ latval = -latval;
+ } else
+ northsouth = 'N';
+
+ latsecfrac = latval % 1000;
+ latval = latval / 1000;
+ latsec = latval % 60;
+ latval = latval / 60;
+ latmin = latval % 60;
+ latval = latval / 60;
+ latdeg = latval;
+
+ if (longval < 0) {
+ eastwest = 'W';
+ longval = -longval;
+ } else
+ eastwest = 'E';
+
+ longsecfrac = longval % 1000;
+ longval = longval / 1000;
+ longsec = longval % 60;
+ longval = longval / 60;
+ longmin = longval % 60;
+ longval = longval / 60;
+ longdeg = longval;
+
+ altfrac = altval % 100;
+ altmeters = (altval / 100);
+
+ sizestr = strdup(precsize_ntoa(sizeval));
+ hpstr = strdup(precsize_ntoa(hpval));
+ vpstr = strdup(precsize_ntoa(vpval));
+
+ sprintf(ascii,
+ "%d %.2d %.2d.%.3d %c %d %.2d %.2d.%.3d %c %s%d.%.2dm %sm %sm %sm",
+ latdeg, latmin, latsec, latsecfrac, northsouth,
+ longdeg, longmin, longsec, longsecfrac, eastwest,
+ altsign, altmeters, altfrac,
+ (sizestr != NULL) ? sizestr : error,
+ (hpstr != NULL) ? hpstr : error,
+ (vpstr != NULL) ? vpstr : error);
+
+ if (sizestr != NULL)
+ free(sizestr);
+ if (hpstr != NULL)
+ free(hpstr);
+ if (vpstr != NULL)
+ free(vpstr);
+
+ return (ascii);
+}
+
+
+/*% Return the number of DNS hierarchy levels in the name. */
+int
+dn_count_labels(const char *name) {
+ int i, len, count;
+
+ len = strlen(name);
+ for (i = 0, count = 0; i < len; i++) {
+ /* XXX need to check for \. or use named's nlabels(). */
+ if (name[i] == '.')
+ count++;
+ }
+
+ /* don't count initial wildcard */
+ if (name[0] == '*')
+ if (count)
+ count--;
+
+ /* don't count the null label for root. */
+ /* if terminating '.' not found, must adjust */
+ /* count to include last label */
+ if (len > 0 && name[len-1] != '.')
+ count++;
+ return (count);
+}
+
+/*%
+ * Make dates expressed in seconds-since-Jan-1-1970 easy to read.
+ * SIG records are required to be printed like this, by the Secure DNS RFC.
+ */
+char *
+p_secstodate (u_long secs) {
+ char *output = p_secstodate_output;
+ time_t clock = secs;
+ struct tm *time;
+#ifdef HAVE_TIME_R
+ struct tm res;
+
+ time = gmtime_r(&clock, &res);
+#else
+ time = gmtime(&clock);
+#endif
+ time->tm_year += 1900;
+ time->tm_mon += 1;
+ sprintf(output, "%04d%02d%02d%02d%02d%02d",
+ time->tm_year, time->tm_mon, time->tm_mday,
+ time->tm_hour, time->tm_min, time->tm_sec);
+ return (output);
+}
+
+u_int16_t
+res_nametoclass(const char *buf, int *successp) {
+ unsigned long result;
+ char *endptr;
+ int success;
+
+ result = sym_ston(__p_class_syms, buf, &success);
+ if (success)
+ goto done;
+
+ if (strncasecmp(buf, "CLASS", 5) != 0 ||
+ !isdigit((unsigned char)buf[5]))
+ goto done;
+ errno = 0;
+ result = strtoul(buf + 5, &endptr, 10);
+ if (errno == 0 && *endptr == '\0' && result <= 0xffffU)
+ success = 1;
+ done:
+ if (successp)
+ *successp = success;
+ return (result);
+}
+
+u_int16_t
+res_nametotype(const char *buf, int *successp) {
+ unsigned long result;
+ char *endptr;
+ int success;
+
+ result = sym_ston(__p_type_syms, buf, &success);
+ if (success)
+ goto done;
+
+ if (strncasecmp(buf, "type", 4) != 0 ||
+ !isdigit((unsigned char)buf[4]))
+ goto done;
+ errno = 0;
+ result = strtoul(buf + 4, &endptr, 10);
+ if (errno == 0 && *endptr == '\0' && result <= 0xffffU)
+ success = 1;
+ done:
+ if (successp)
+ *successp = success;
+ return (result);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_debug.h b/usr/src/lib/libresolv2_joy/common/resolv/res_debug.h
new file mode 100644
index 0000000000..c28171d7c8
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_debug.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _RES_DEBUG_H_
+#define _RES_DEBUG_H_
+
+#ifndef DEBUG
+# define Dprint(cond, args) /*empty*/
+# define DprintQ(cond, args, query, size) /*empty*/
+# define Aerror(statp, file, string, error, address) /*empty*/
+# define Perror(statp, file, string, error) /*empty*/
+#else
+# define Dprint(cond, args) if (cond) {fprintf args;} else {}
+# define DprintQ(cond, args, query, size) if (cond) {\
+ fprintf args;\
+ res_pquery(statp, query, size, stdout);\
+ } else {}
+#endif
+
+#endif /* _RES_DEBUG_H_ */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_findzonecut.c b/usr/src/lib/libresolv2_joy/common/resolv/res_findzonecut.c
new file mode 100644
index 0000000000..c84bd0dec7
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_findzonecut.c
@@ -0,0 +1,718 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Import. */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/list.h>
+
+#include "port_after.h"
+
+#include <resolv_joy.h>
+
+/* Data structures. */
+
+typedef struct rr_a {
+ LINK(struct rr_a) link;
+ union res_sockaddr_union addr;
+} rr_a;
+typedef LIST(rr_a) rrset_a;
+
+typedef struct rr_ns {
+ LINK(struct rr_ns) link;
+ const char * name;
+ unsigned int flags;
+ rrset_a addrs;
+} rr_ns;
+typedef LIST(rr_ns) rrset_ns;
+
+#define RR_NS_HAVE_V4 0x01
+#define RR_NS_HAVE_V6 0x02
+
+/* Forward. */
+
+static int satisfy(res_state, const char *, rrset_ns *,
+ union res_sockaddr_union *, int);
+static int add_addrs(res_state, rr_ns *,
+ union res_sockaddr_union *, int);
+static int get_soa(res_state, const char *, ns_class, int,
+ char *, size_t, char *, size_t,
+ rrset_ns *);
+static int get_ns(res_state, const char *, ns_class, int, rrset_ns *);
+static int get_glue(res_state, ns_class, int, rrset_ns *);
+static int save_ns(res_state, ns_msg *, ns_sect,
+ const char *, ns_class, int, rrset_ns *);
+static int save_a(res_state, ns_msg *, ns_sect,
+ const char *, ns_class, int, rr_ns *);
+static void free_nsrrset(rrset_ns *);
+static void free_nsrr(rrset_ns *, rr_ns *);
+static rr_ns * find_ns(rrset_ns *, const char *);
+static int do_query(res_state, const char *, ns_class, ns_type,
+ u_char *, ns_msg *);
+static void res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2);
+
+/* Macros. */
+
+#define DPRINTF(x) do {\
+ int save_errno = errno; \
+ if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \
+ errno = save_errno; \
+ } while (0)
+
+/* Public. */
+
+/*%
+ * find enclosing zone for a <dname,class>, and some server addresses
+ *
+ * parameters:
+ *\li res - resolver context to work within (is modified)
+ *\li dname - domain name whose enclosing zone is desired
+ *\li class - class of dname (and its enclosing zone)
+ *\li zname - found zone name
+ *\li zsize - allocated size of zname
+ *\li addrs - found server addresses
+ *\li naddrs - max number of addrs
+ *
+ * return values:
+ *\li < 0 - an error occurred (check errno)
+ *\li = 0 - zname is now valid, but addrs[] wasn't changed
+ *\li > 0 - zname is now valid, and return value is number of addrs[] found
+ *
+ * notes:
+ *\li this function calls res_nsend() which means it depends on correctly
+ * functioning recursive nameservers (usually defined in /etc/resolv.conf
+ * or its local equivilent).
+ *
+ *\li we start by asking for an SOA<dname,class>. if we get one as an
+ * answer, that just means <dname,class> is a zone top, which is fine.
+ * more than likely we'll be told to go pound sand, in the form of a
+ * negative answer.
+ *
+ *\li note that we are not prepared to deal with referrals since that would
+ * only come from authority servers and our correctly functioning local
+ * recursive server would have followed the referral and got us something
+ * more definite.
+ *
+ *\li if the authority section contains an SOA, this SOA should also be the
+ * closest enclosing zone, since any intermediary zone cuts would've been
+ * returned as referrals and dealt with by our correctly functioning local
+ * recursive name server. but an SOA in the authority section should NOT
+ * match our dname (since that would have been returned in the answer
+ * section). an authority section SOA has to be "above" our dname.
+ *
+ *\li however, since authority section SOA's were once optional, it's
+ * possible that we'll have to go hunting for the enclosing SOA by
+ * ripping labels off the front of our dname -- this is known as "doing
+ * it the hard way."
+ *
+ *\li ultimately we want some server addresses, which are ideally the ones
+ * pertaining to the SOA.MNAME, but only if there is a matching NS RR.
+ * so the second phase (after we find an SOA) is to go looking for the
+ * NS RRset for that SOA's zone.
+ *
+ *\li no answer section processed by this code is allowed to contain CNAME
+ * or DNAME RR's. for the SOA query this means we strip a label and
+ * keep going. for the NS and A queries this means we just give up.
+ */
+
+int
+res_findzonecut(res_state statp, const char *dname, ns_class class, int opts,
+ char *zname, size_t zsize, struct in_addr *addrs, int naddrs)
+{
+ int result, i;
+ union res_sockaddr_union *u;
+
+
+ opts |= RES_IPV4ONLY;
+ opts &= ~RES_IPV6ONLY;
+
+ u = calloc(naddrs, sizeof(*u));
+ if (u == NULL)
+ return(-1);
+
+ result = res_findzonecut2(statp, dname, class, opts, zname, zsize,
+ u, naddrs);
+
+ for (i = 0; i < result; i++) {
+ addrs[i] = u[i].sin.sin_addr;
+ }
+ free(u);
+ return (result);
+}
+
+int
+res_findzonecut2(res_state statp, const char *dname, ns_class class, int opts,
+ char *zname, size_t zsize, union res_sockaddr_union *addrs,
+ int naddrs)
+{
+ char mname[NS_MAXDNAME];
+ u_long save_pfcode;
+ rrset_ns nsrrs;
+ int n;
+
+ DPRINTF(("START dname='%s' class=%s, zsize=%ld, naddrs=%d",
+ dname, p_class(class), (long)zsize, naddrs));
+ save_pfcode = statp->pfcode;
+ statp->pfcode |= RES_PRF_HEAD2 | RES_PRF_HEAD1 | RES_PRF_HEADX |
+ RES_PRF_QUES | RES_PRF_ANS |
+ RES_PRF_AUTH | RES_PRF_ADD;
+ INIT_LIST(nsrrs);
+
+ DPRINTF(("get the soa, and see if it has enough glue"));
+ if ((n = get_soa(statp, dname, class, opts, zname, zsize,
+ mname, sizeof mname, &nsrrs)) < 0 ||
+ ((opts & RES_EXHAUSTIVE) == 0 &&
+ (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0))
+ goto done;
+
+ DPRINTF(("get the ns rrset and see if it has enough glue"));
+ if ((n = get_ns(statp, zname, class, opts, &nsrrs)) < 0 ||
+ ((opts & RES_EXHAUSTIVE) == 0 &&
+ (n = satisfy(statp, mname, &nsrrs, addrs, naddrs)) > 0))
+ goto done;
+
+ DPRINTF(("get the missing glue and see if it's finally enough"));
+ if ((n = get_glue(statp, class, opts, &nsrrs)) >= 0)
+ n = satisfy(statp, mname, &nsrrs, addrs, naddrs);
+
+ done:
+ DPRINTF(("FINISH n=%d (%s)", n, (n < 0) ? strerror(errno) : "OK"));
+ free_nsrrset(&nsrrs);
+ statp->pfcode = save_pfcode;
+ return (n);
+}
+
+/* Private. */
+
+static int
+satisfy(res_state statp, const char *mname, rrset_ns *nsrrsp,
+ union res_sockaddr_union *addrs, int naddrs)
+{
+ rr_ns *nsrr;
+ int n, x;
+
+ n = 0;
+ nsrr = find_ns(nsrrsp, mname);
+ if (nsrr != NULL) {
+ x = add_addrs(statp, nsrr, addrs, naddrs);
+ addrs += x;
+ naddrs -= x;
+ n += x;
+ }
+ for (nsrr = HEAD(*nsrrsp);
+ nsrr != NULL && naddrs > 0;
+ nsrr = NEXT(nsrr, link))
+ if (ns_samename(nsrr->name, mname) != 1) {
+ x = add_addrs(statp, nsrr, addrs, naddrs);
+ addrs += x;
+ naddrs -= x;
+ n += x;
+ }
+ DPRINTF(("satisfy(%s): %d", mname, n));
+ return (n);
+}
+
+static int
+add_addrs(res_state statp, rr_ns *nsrr,
+ union res_sockaddr_union *addrs, int naddrs)
+{
+ rr_a *arr;
+ int n = 0;
+
+ for (arr = HEAD(nsrr->addrs); arr != NULL; arr = NEXT(arr, link)) {
+ if (naddrs <= 0)
+ return (0);
+ *addrs++ = arr->addr;
+ naddrs--;
+ n++;
+ }
+ DPRINTF(("add_addrs: %d", n));
+ return (n);
+}
+
+static int
+get_soa(res_state statp, const char *dname, ns_class class, int opts,
+ char *zname, size_t zsize, char *mname, size_t msize,
+ rrset_ns *nsrrsp)
+{
+ char tname[NS_MAXDNAME];
+ u_char *resp = NULL;
+ int n, i, ancount, nscount;
+ ns_sect sect;
+ ns_msg msg;
+ u_int rcode;
+
+ /*
+ * Find closest enclosing SOA, even if it's for the root zone.
+ */
+
+ /* First canonicalize dname (exactly one unescaped trailing "."). */
+ if (ns_makecanon(dname, tname, sizeof tname) < 0)
+ goto cleanup;
+ dname = tname;
+
+ resp = malloc(NS_MAXMSG);
+ if (resp == NULL)
+ goto cleanup;
+
+ /* Now grovel the subdomains, hunting for an SOA answer or auth. */
+ for (;;) {
+ /* Leading or inter-label '.' are skipped here. */
+ while (*dname == '.')
+ dname++;
+
+ /* Is there an SOA? */
+ n = do_query(statp, dname, class, ns_t_soa, resp, &msg);
+ if (n < 0) {
+ DPRINTF(("get_soa: do_query('%s', %s) failed (%d)",
+ dname, p_class(class), n));
+ goto cleanup;
+ }
+ if (n > 0) {
+ DPRINTF(("get_soa: CNAME or DNAME found"));
+ sect = ns_s_max, n = 0;
+ } else {
+ rcode = ns_msg_getflag(msg, ns_f_rcode);
+ ancount = ns_msg_count(msg, ns_s_an);
+ nscount = ns_msg_count(msg, ns_s_ns);
+ if (ancount > 0 && rcode == ns_r_noerror)
+ sect = ns_s_an, n = ancount;
+ else if (nscount > 0)
+ sect = ns_s_ns, n = nscount;
+ else
+ sect = ns_s_max, n = 0;
+ }
+ for (i = 0; i < n; i++) {
+ const char *t;
+ const u_char *rdata;
+ ns_rr rr;
+
+ if (ns_parserr(&msg, sect, i, &rr) < 0) {
+ DPRINTF(("get_soa: ns_parserr(%s, %d) failed",
+ p_section(sect, ns_o_query), i));
+ goto cleanup;
+ }
+ if (ns_rr_type(rr) == ns_t_cname ||
+ ns_rr_type(rr) == ns_t_dname)
+ break;
+ if (ns_rr_type(rr) != ns_t_soa ||
+ ns_rr_class(rr) != class)
+ continue;
+ t = ns_rr_name(rr);
+ switch (sect) {
+ case ns_s_an:
+ if (ns_samedomain(dname, t) == 0) {
+ DPRINTF(
+ ("get_soa: ns_samedomain('%s', '%s') == 0",
+ dname, t)
+ );
+ errno = EPROTOTYPE;
+ goto cleanup;
+ }
+ break;
+ case ns_s_ns:
+ if (ns_samename(dname, t) == 1 ||
+ ns_samedomain(dname, t) == 0) {
+ DPRINTF(
+ ("get_soa: ns_samename() || !ns_samedomain('%s', '%s')",
+ dname, t)
+ );
+ errno = EPROTOTYPE;
+ goto cleanup;
+ }
+ break;
+ default:
+ abort();
+ }
+ if (strlen(t) + 1 > zsize) {
+ DPRINTF(("get_soa: zname(%lu) too small (%lu)",
+ (unsigned long)zsize,
+ (unsigned long)strlen(t) + 1));
+ errno = EMSGSIZE;
+ goto cleanup;
+ }
+ strcpy(zname, t);
+ rdata = ns_rr_rdata(rr);
+ if (ns_name_uncompress(resp, ns_msg_end(msg), rdata,
+ mname, msize) < 0) {
+ DPRINTF(("get_soa: ns_name_uncompress failed")
+ );
+ goto cleanup;
+ }
+ if (save_ns(statp, &msg, ns_s_ns,
+ zname, class, opts, nsrrsp) < 0) {
+ DPRINTF(("get_soa: save_ns failed"));
+ goto cleanup;
+ }
+ free(resp);
+ return (0);
+ }
+
+ /* If we're out of labels, then not even "." has an SOA! */
+ if (*dname == '\0')
+ break;
+
+ /* Find label-terminating "."; top of loop will skip it. */
+ while (*dname != '.') {
+ if (*dname == '\\')
+ if (*++dname == '\0') {
+ errno = EMSGSIZE;
+ goto cleanup;
+ }
+ dname++;
+ }
+ }
+ DPRINTF(("get_soa: out of labels"));
+ errno = EDESTADDRREQ;
+ cleanup:
+ if (resp != NULL)
+ free(resp);
+ return (-1);
+}
+
+static int
+get_ns(res_state statp, const char *zname, ns_class class, int opts,
+ rrset_ns *nsrrsp)
+{
+ u_char *resp;
+ ns_msg msg;
+ int n;
+
+ resp = malloc(NS_MAXMSG);
+ if (resp == NULL)
+ return (-1);
+
+ /* Go and get the NS RRs for this zone. */
+ n = do_query(statp, zname, class, ns_t_ns, resp, &msg);
+ if (n != 0) {
+ DPRINTF(("get_ns: do_query('%s', %s) failed (%d)",
+ zname, p_class(class), n));
+ free(resp);
+ return (-1);
+ }
+
+ /* Remember the NS RRs and associated A RRs that came back. */
+ if (save_ns(statp, &msg, ns_s_an, zname, class, opts, nsrrsp) < 0) {
+ DPRINTF(("get_ns save_ns('%s', %s) failed",
+ zname, p_class(class)));
+ free(resp);
+ return (-1);
+ }
+
+ free(resp);
+ return (0);
+}
+
+static int
+get_glue(res_state statp, ns_class class, int opts, rrset_ns *nsrrsp) {
+ rr_ns *nsrr, *nsrr_n;
+ u_char *resp;
+
+ resp = malloc(NS_MAXMSG);
+ if (resp == NULL)
+ return(-1);
+
+ /* Go and get the A RRs for each empty NS RR on our list. */
+ for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = nsrr_n) {
+ ns_msg msg;
+ int n;
+
+ nsrr_n = NEXT(nsrr, link);
+
+ if ((nsrr->flags & RR_NS_HAVE_V4) == 0) {
+ n = do_query(statp, nsrr->name, class, ns_t_a,
+ resp, &msg);
+ if (n < 0) {
+ DPRINTF(
+ ("get_glue: do_query('%s', %s') failed",
+ nsrr->name, p_class(class)));
+ goto cleanup;
+ }
+ if (n > 0) {
+ DPRINTF((
+ "get_glue: do_query('%s', %s') CNAME or DNAME found",
+ nsrr->name, p_class(class)));
+ }
+ if (save_a(statp, &msg, ns_s_an, nsrr->name, class,
+ opts, nsrr) < 0) {
+ DPRINTF(("get_glue: save_r('%s', %s) failed",
+ nsrr->name, p_class(class)));
+ goto cleanup;
+ }
+ }
+
+ if ((nsrr->flags & RR_NS_HAVE_V6) == 0) {
+ n = do_query(statp, nsrr->name, class, ns_t_aaaa,
+ resp, &msg);
+ if (n < 0) {
+ DPRINTF(
+ ("get_glue: do_query('%s', %s') failed",
+ nsrr->name, p_class(class)));
+ goto cleanup;
+ }
+ if (n > 0) {
+ DPRINTF((
+ "get_glue: do_query('%s', %s') CNAME or DNAME found",
+ nsrr->name, p_class(class)));
+ }
+ if (save_a(statp, &msg, ns_s_an, nsrr->name, class,
+ opts, nsrr) < 0) {
+ DPRINTF(("get_glue: save_r('%s', %s) failed",
+ nsrr->name, p_class(class)));
+ goto cleanup;
+ }
+ }
+
+ /* If it's still empty, it's just chaff. */
+ if (EMPTY(nsrr->addrs)) {
+ DPRINTF(("get_glue: removing empty '%s' NS",
+ nsrr->name));
+ free_nsrr(nsrrsp, nsrr);
+ }
+ }
+ free(resp);
+ return (0);
+
+ cleanup:
+ free(resp);
+ return (-1);
+}
+
+static int
+save_ns(res_state statp, ns_msg *msg, ns_sect sect,
+ const char *owner, ns_class class, int opts,
+ rrset_ns *nsrrsp)
+{
+ int i;
+
+ for (i = 0; i < ns_msg_count(*msg, sect); i++) {
+ char tname[MAXDNAME];
+ const u_char *rdata;
+ rr_ns *nsrr;
+ ns_rr rr;
+
+ if (ns_parserr(msg, sect, i, &rr) < 0) {
+ DPRINTF(("save_ns: ns_parserr(%s, %d) failed",
+ p_section(sect, ns_o_query), i));
+ return (-1);
+ }
+ if (ns_rr_type(rr) != ns_t_ns ||
+ ns_rr_class(rr) != class ||
+ ns_samename(ns_rr_name(rr), owner) != 1)
+ continue;
+ nsrr = find_ns(nsrrsp, ns_rr_name(rr));
+ if (nsrr == NULL) {
+ nsrr = malloc(sizeof *nsrr);
+ if (nsrr == NULL) {
+ DPRINTF(("save_ns: malloc failed"));
+ return (-1);
+ }
+ rdata = ns_rr_rdata(rr);
+ if (ns_name_uncompress(ns_msg_base(*msg),
+ ns_msg_end(*msg), rdata,
+ tname, sizeof tname) < 0) {
+ DPRINTF(("save_ns: ns_name_uncompress failed")
+ );
+ free(nsrr);
+ return (-1);
+ }
+ nsrr->name = strdup(tname);
+ if (nsrr->name == NULL) {
+ DPRINTF(("save_ns: strdup failed"));
+ free(nsrr);
+ return (-1);
+ }
+ INIT_LINK(nsrr, link);
+ INIT_LIST(nsrr->addrs);
+ nsrr->flags = 0;
+ APPEND(*nsrrsp, nsrr, link);
+ }
+ if (save_a(statp, msg, ns_s_ar,
+ nsrr->name, class, opts, nsrr) < 0) {
+ DPRINTF(("save_ns: save_r('%s', %s) failed",
+ nsrr->name, p_class(class)));
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+static int
+save_a(res_state statp, ns_msg *msg, ns_sect sect,
+ const char *owner, ns_class class, int opts,
+ rr_ns *nsrr)
+{
+ int i;
+
+ for (i = 0; i < ns_msg_count(*msg, sect); i++) {
+ ns_rr rr;
+ rr_a *arr;
+
+ if (ns_parserr(msg, sect, i, &rr) < 0) {
+ DPRINTF(("save_a: ns_parserr(%s, %d) failed",
+ p_section(sect, ns_o_query), i));
+ return (-1);
+ }
+ if ((ns_rr_type(rr) != ns_t_a &&
+ ns_rr_type(rr) != ns_t_aaaa) ||
+ ns_rr_class(rr) != class ||
+ ns_samename(ns_rr_name(rr), owner) != 1 ||
+ ns_rr_rdlen(rr) != NS_INADDRSZ)
+ continue;
+ if ((opts & RES_IPV6ONLY) != 0 && ns_rr_type(rr) != ns_t_aaaa)
+ continue;
+ if ((opts & RES_IPV4ONLY) != 0 && ns_rr_type(rr) != ns_t_a)
+ continue;
+ arr = malloc(sizeof *arr);
+ if (arr == NULL) {
+ DPRINTF(("save_a: malloc failed"));
+ return (-1);
+ }
+ INIT_LINK(arr, link);
+ memset(&arr->addr, 0, sizeof(arr->addr));
+ switch (ns_rr_type(rr)) {
+ case ns_t_a:
+ arr->addr.sin.sin_family = AF_INET;
+#ifdef HAVE_SA_LEN
+ arr->addr.sin.sin_len = sizeof(arr->addr.sin);
+#endif
+ memcpy(&arr->addr.sin.sin_addr, ns_rr_rdata(rr),
+ NS_INADDRSZ);
+ arr->addr.sin.sin_port = htons(NAMESERVER_PORT);
+ nsrr->flags |= RR_NS_HAVE_V4;
+ break;
+ case ns_t_aaaa:
+ arr->addr.sin6.sin6_family = AF_INET6;
+#ifdef HAVE_SA_LEN
+ arr->addr.sin6.sin6_len = sizeof(arr->addr.sin6);
+#endif
+ memcpy(&arr->addr.sin6.sin6_addr, ns_rr_rdata(rr), 16);
+ arr->addr.sin.sin_port = htons(NAMESERVER_PORT);
+ nsrr->flags |= RR_NS_HAVE_V6;
+ break;
+ default:
+ abort();
+ }
+ APPEND(nsrr->addrs, arr, link);
+ }
+ return (0);
+}
+
+static void
+free_nsrrset(rrset_ns *nsrrsp) {
+ rr_ns *nsrr;
+
+ while ((nsrr = HEAD(*nsrrsp)) != NULL)
+ free_nsrr(nsrrsp, nsrr);
+}
+
+static void
+free_nsrr(rrset_ns *nsrrsp, rr_ns *nsrr) {
+ rr_a *arr;
+ char *tmp;
+
+ while ((arr = HEAD(nsrr->addrs)) != NULL) {
+ UNLINK(nsrr->addrs, arr, link);
+ free(arr);
+ }
+ DE_CONST(nsrr->name, tmp);
+ free(tmp);
+ UNLINK(*nsrrsp, nsrr, link);
+ free(nsrr);
+}
+
+static rr_ns *
+find_ns(rrset_ns *nsrrsp, const char *dname) {
+ rr_ns *nsrr;
+
+ for (nsrr = HEAD(*nsrrsp); nsrr != NULL; nsrr = NEXT(nsrr, link))
+ if (ns_samename(nsrr->name, dname) == 1)
+ return (nsrr);
+ return (NULL);
+}
+
+static int
+do_query(res_state statp, const char *dname, ns_class class, ns_type qtype,
+ u_char *resp, ns_msg *msg)
+{
+ u_char req[NS_PACKETSZ];
+ int i, n;
+
+ n = res_nmkquery(statp, ns_o_query, dname, class, qtype,
+ NULL, 0, NULL, req, NS_PACKETSZ);
+ if (n < 0) {
+ DPRINTF(("do_query: res_nmkquery failed"));
+ return (-1);
+ }
+ n = res_nsend(statp, req, n, resp, NS_MAXMSG);
+ if (n < 0) {
+ DPRINTF(("do_query: res_nsend failed"));
+ return (-1);
+ }
+ if (n == 0) {
+ DPRINTF(("do_query: res_nsend returned 0"));
+ errno = EMSGSIZE;
+ return (-1);
+ }
+ if (ns_initparse(resp, n, msg) < 0) {
+ DPRINTF(("do_query: ns_initparse failed"));
+ return (-1);
+ }
+ n = 0;
+ for (i = 0; i < ns_msg_count(*msg, ns_s_an); i++) {
+ ns_rr rr;
+
+ if (ns_parserr(msg, ns_s_an, i, &rr) < 0) {
+ DPRINTF(("do_query: ns_parserr failed"));
+ return (-1);
+ }
+ n += (ns_rr_class(rr) == class &&
+ (ns_rr_type(rr) == ns_t_cname ||
+ ns_rr_type(rr) == ns_t_dname));
+ }
+ return (n);
+}
+
+static void
+res_dprintf(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ fputs(";; res_findzonecut: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_init.c b/usr/src/lib/libresolv2_joy/common/resolv/res_init.c
new file mode 100644
index 0000000000..4a7724645f
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_init.c
@@ -0,0 +1,953 @@
+/*
+ * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+
+/*
+ * Copyright (c) 1985, 1989, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#ifndef HAVE_MD5
+# include "../dst/md5.h"
+#else
+# ifdef SOLARIS2
+# include <sys/md5.h>
+# endif
+#endif
+#ifndef _MD5_H_
+# define _MD5_H_ 1 /*%< make sure we do not include rsaref md5.h file */
+#endif
+
+
+#include "port_after.h"
+
+/* ensure that sockaddr_in6 and IN6ADDR_ANY_INIT are declared / defined */
+#include <resolv_joy.h>
+
+/* ISC purposely put port_after.h before <resolv.h> to force in6 stuff
+ * (above) so we explicitly include port_resolv.h here */
+#include "port_resolv.h"
+
+#include "res_private.h"
+
+/*% Options. Should all be left alone. */
+#define RESOLVSORT
+#define DEBUG
+
+#ifdef SUNW_INITCHKIF
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <sys/sockio.h>
+#define MAXIFS 8192
+#endif /* SUNW_INITCHKIF */
+
+#ifdef SOLARIS2
+#include <sys/systeminfo.h>
+#endif
+
+static void res_setoptions __P((res_state, const char *, const char *));
+
+#ifdef RESOLVSORT
+static const char sort_mask[] = "/&";
+#define ISSORTMASK(ch) (strchr(sort_mask, ch) != NULL)
+static u_int32_t net_mask __P((struct in_addr));
+#endif
+
+#if !defined(isascii) /*%< XXX - could be a function */
+# define isascii(c) (!(c & 0200))
+#endif
+
+/*
+ * Resolver state default settings.
+ */
+
+/*%
+ * Set up default settings. If the configuration file exist, the values
+ * there will have precedence. Otherwise, the server address is set to
+ * INADDR_ANY and the default domain name comes from the gethostname().
+ *
+ * An interrim version of this code (BIND 4.9, pre-4.4BSD) used 127.0.0.1
+ * rather than INADDR_ANY ("0.0.0.0") as the default name server address
+ * since it was noted that INADDR_ANY actually meant ``the first interface
+ * you "ifconfig"'d at boot time'' and if this was a SLIP or PPP interface,
+ * it had to be "up" in order for you to reach your own name server. It
+ * was later decided that since the recommended practice is to always
+ * install local static routes through 127.0.0.1 for all your network
+ * interfaces, that we could solve this problem without a code change.
+ *
+ * The configuration file should always be used, since it is the only way
+ * to specify a default domain. If you are running a server on your local
+ * machine, you should say "nameserver 0.0.0.0" or "nameserver 127.0.0.1"
+ * in the configuration file.
+ *
+ * Return 0 if completes successfully, -1 on error
+ */
+int
+res_ninit(res_state statp) {
+ extern int __res_vinit(res_state, int);
+ return (__res_vinit(statp, 0));
+}
+
+/*% This function has to be reachable by res_data.c but not publically. */
+int
+__res_vinit(res_state statp, int preinit) {
+ register FILE *fp;
+ register char *cp, **pp;
+ register int n;
+ char buf[BUFSIZ];
+ int nserv = 0; /*%< number of nameserver records read from file */
+ int haveenv = 0;
+ int havesearch = 0;
+#ifdef RESOLVSORT
+ int nsort = 0;
+ char *net;
+#endif
+ int dots;
+ union res_sockaddr_union u[2];
+ int maxns = MAXNS;
+
+ RES_SET_H_ERRNO(statp, 0);
+ if (statp->_u._ext.ext != NULL)
+ res_ndestroy(statp);
+
+ if (!preinit) {
+ statp->retrans = RES_TIMEOUT;
+ statp->retry = RES_DFLRETRY;
+ statp->options = RES_DEFAULT;
+ res_rndinit(statp);
+ statp->id = res_nrandomid(statp);
+ }
+
+ memset(u, 0, sizeof(u));
+#ifdef USELOOPBACK
+ u[nserv].sin.sin_addr = inet_makeaddr(IN_LOOPBACKNET, 1);
+#else
+ u[nserv].sin.sin_addr.s_addr = INADDR_ANY;
+#endif
+ u[nserv].sin.sin_family = AF_INET;
+ u[nserv].sin.sin_port = htons(NAMESERVER_PORT);
+#ifdef HAVE_SA_LEN
+ u[nserv].sin.sin_len = sizeof(struct sockaddr_in);
+#endif
+ nserv++;
+#ifdef HAS_INET6_STRUCTS
+#ifdef USELOOPBACK
+ u[nserv].sin6.sin6_addr = in6addr_loopback;
+#else
+ u[nserv].sin6.sin6_addr = in6addr_any;
+#endif
+ u[nserv].sin6.sin6_family = AF_INET6;
+ u[nserv].sin6.sin6_port = htons(NAMESERVER_PORT);
+#ifdef HAVE_SA_LEN
+ u[nserv].sin6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ nserv++;
+#endif
+ statp->nscount = 0;
+ statp->ndots = 1;
+ statp->pfcode = 0;
+ statp->_vcsock = -1;
+ statp->_flags = 0;
+ statp->qhook = NULL;
+ statp->rhook = NULL;
+ statp->_u._ext.nscount = 0;
+ statp->_u._ext.ext = malloc(sizeof(*statp->_u._ext.ext));
+ if (statp->_u._ext.ext != NULL) {
+ memset(statp->_u._ext.ext, 0, sizeof(*statp->_u._ext.ext));
+ statp->_u._ext.ext->nsaddrs[0].sin = statp->nsaddr;
+ strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa");
+ strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int");
+ } else {
+ /*
+ * Historically res_init() rarely, if at all, failed.
+ * Examples and applications exist which do not check
+ * our return code. Furthermore several applications
+ * simply call us to get the systems domainname. So
+ * rather then immediately fail here we store the
+ * failure, which is returned later, in h_errno. And
+ * prevent the collection of 'nameserver' information
+ * by setting maxns to 0. Thus applications that fail
+ * to check our return code wont be able to make
+ * queries anyhow.
+ */
+ RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
+ maxns = 0;
+ }
+#ifdef RESOLVSORT
+ statp->nsort = 0;
+#endif
+ res_setservers(statp, u, nserv);
+
+#ifdef SUNW_INITCHKIF
+/*
+ * Short circuit res_init() if no non-loopback interfaces are up. This is
+ * done to avoid boot delays if "dns" comes before "files" in nsswitch.conf.
+ * An additional fix has been added to this code, to count all external
+ * interfaces, which includes the IPv6 interfaces. If no external interfaces
+ * are found, an additional check is carried out to determine if any deprecated
+ * interfaces are up.
+ */
+ {
+ int s;
+ struct lifnum lifn;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("res_init: socket");
+ goto freedata;
+ }
+ lifn.lifn_family = AF_UNSPEC;
+ lifn.lifn_flags = LIFC_EXTERNAL_SOURCE;
+ if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) {
+ close(s);
+ goto freedata;
+ }
+ if (lifn.lifn_count == 0) {
+ /*
+ * Check if there are any deprecated interfaces up
+ */
+ struct lifconf lifc;
+ uchar_t *buf;
+ int buflen, i, int_up = 0;
+
+ lifn.lifn_flags = 0;
+ if ((ioctl(s, SIOCGLIFNUM, (char *)&lifn) < 0) ||
+ (lifn.lifn_count < 1)) {
+ close(s);
+ goto freedata;
+ }
+
+ buflen = lifn.lifn_count * sizeof (struct lifreq);
+ buf = (uchar_t *)malloc(buflen);
+ if (buf == NULL) {
+ close(s);
+ goto freedata;
+ }
+
+ lifc.lifc_family = AF_UNSPEC;
+ lifc.lifc_flags = 0;
+ lifc.lifc_len = buflen;
+ lifc.lifc_lifcu.lifcu_buf = (caddr_t)buf;
+ if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0) {
+ close(s);
+ free(buf);
+ goto freedata;
+ }
+
+ for (i = 0; i < lifn.lifn_count; ++i) {
+ struct lifreq *lreqp, lreq;
+
+ lreqp = (struct lifreq *)&lifc.lifc_req[i];
+ strlcpy(lreq.lifr_name, lreqp->lifr_name,
+ sizeof (lreq.lifr_name));
+ if (ioctl(s, SIOCGLIFFLAGS, &lreq) < 0) {
+ close(s);
+ free(buf);
+ goto freedata;
+ }
+ if ((lreq.lifr_flags & IFF_UP) &&
+ !(lreq.lifr_flags & IFF_NOLOCAL) &&
+ !(lreq.lifr_flags & IFF_NOXMIT) &&
+ !(lreq.lifr_flags & IFF_LOOPBACK)) {
+ int_up = 1;
+ break;
+ }
+ }
+ free(buf);
+
+ if (!int_up) {
+ close(s);
+ goto freedata;
+ }
+ }
+ close(s);
+ }
+#endif /* SUNW_INITCHKIF */
+
+#ifdef SOLARIS2
+ /*
+ * The old libresolv derived the defaultdomain from NIS/NIS+.
+ * We want to keep this behaviour
+ */
+ {
+ char buf[sizeof(statp->defdname)], *cp;
+ int ret;
+
+ if ((ret = sysinfo(SI_SRPC_DOMAIN, buf, sizeof(buf))) > 0 &&
+ (unsigned int)ret <= sizeof(buf)) {
+ if (buf[0] == '+')
+ buf[0] = '.';
+ cp = strchr(buf, '.');
+ cp = (cp == NULL) ? buf : (cp + 1);
+ strncpy(statp->defdname, cp,
+ sizeof(statp->defdname) - 1);
+ statp->defdname[sizeof(statp->defdname) - 1] = '\0';
+ }
+ }
+#endif /* SOLARIS2 */
+
+ /* Allow user to override the local domain definition */
+ if ((cp = getenv("LOCALDOMAIN")) != NULL) {
+ (void)strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1);
+ statp->defdname[sizeof(statp->defdname) - 1] = '\0';
+ haveenv++;
+
+ /*
+ * Set search list to be blank-separated strings
+ * from rest of env value. Permits users of LOCALDOMAIN
+ * to still have a search list, and anyone to set the
+ * one that they want to use as an individual (even more
+ * important now that the rfc1535 stuff restricts searches)
+ */
+ cp = statp->defdname;
+ pp = statp->dnsrch;
+ *pp++ = cp;
+ for (n = 0; *cp && pp < statp->dnsrch + MAXDNSRCH; cp++) {
+ if (*cp == '\n') /*%< silly backwards compat */
+ break;
+ else if (*cp == ' ' || *cp == '\t') {
+ *cp = 0;
+ n = 1;
+ } else if (n) {
+ *pp++ = cp;
+ n = 0;
+ havesearch = 1;
+ }
+ }
+ /* null terminate last domain if there are excess */
+ while (*cp != '\0' && *cp != ' ' && *cp != '\t' && *cp != '\n')
+ cp++;
+ *cp = '\0';
+ *pp++ = 0;
+ }
+
+#define MATCH(line, name) \
+ (!strncmp(line, name, sizeof(name) - 1) && \
+ (line[sizeof(name) - 1] == ' ' || \
+ line[sizeof(name) - 1] == '\t'))
+
+ nserv = 0;
+ if ((fp = fopen(_PATH_RESCONF, "r")) != NULL) {
+ /* read the config file */
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ /* skip comments */
+ if (*buf == ';' || *buf == '#')
+ continue;
+ /* read default domain name */
+ if (MATCH(buf, "domain")) {
+ if (haveenv) /*%< skip if have from environ */
+ continue;
+ cp = buf + sizeof("domain") - 1;
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if ((*cp == '\0') || (*cp == '\n'))
+ continue;
+ strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1);
+ statp->defdname[sizeof(statp->defdname) - 1] = '\0';
+ if ((cp = strpbrk(statp->defdname, " \t\n")) != NULL)
+ *cp = '\0';
+ havesearch = 0;
+ continue;
+ }
+ /* set search list */
+ if (MATCH(buf, "search")) {
+ if (haveenv) /*%< skip if have from environ */
+ continue;
+ cp = buf + sizeof("search") - 1;
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if ((*cp == '\0') || (*cp == '\n'))
+ continue;
+ strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1);
+ statp->defdname[sizeof(statp->defdname) - 1] = '\0';
+ if ((cp = strchr(statp->defdname, '\n')) != NULL)
+ *cp = '\0';
+ /*
+ * Set search list to be blank-separated strings
+ * on rest of line.
+ */
+ cp = statp->defdname;
+ pp = statp->dnsrch;
+ *pp++ = cp;
+ for (n = 0; *cp && pp < statp->dnsrch + MAXDNSRCH; cp++) {
+ if (*cp == ' ' || *cp == '\t') {
+ *cp = 0;
+ n = 1;
+ } else if (n) {
+ *pp++ = cp;
+ n = 0;
+ }
+ }
+ /* null terminate last domain if there are excess */
+ while (*cp != '\0' && *cp != ' ' && *cp != '\t')
+ cp++;
+ *cp = '\0';
+ *pp++ = 0;
+ havesearch = 1;
+ continue;
+ }
+ /* read nameservers to query */
+ if (MATCH(buf, "nameserver") && nserv < maxns) {
+ struct addrinfo hints, *ai;
+ char sbuf[NI_MAXSERV];
+ const size_t minsiz =
+ sizeof(statp->_u._ext.ext->nsaddrs[0]);
+
+ cp = buf + sizeof("nameserver") - 1;
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ cp[strcspn(cp, ";# \t\n")] = '\0';
+ if ((*cp != '\0') && (*cp != '\n')) {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ sprintf(sbuf, "%u", NAMESERVER_PORT);
+ if (getaddrinfo(cp, sbuf, &hints, &ai) == 0 &&
+ ai->ai_addrlen <= minsiz) {
+ if (statp->_u._ext.ext != NULL) {
+ memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
+ ai->ai_addr, ai->ai_addrlen);
+ }
+ if (ai->ai_addrlen <=
+ sizeof(statp->nsaddr_list[nserv])) {
+ memcpy(&statp->nsaddr_list[nserv],
+ ai->ai_addr, ai->ai_addrlen);
+ } else
+ statp->nsaddr_list[nserv].sin_family = 0;
+ freeaddrinfo(ai);
+ nserv++;
+ }
+ }
+ continue;
+ }
+#ifdef RESOLVSORT
+ if (MATCH(buf, "sortlist")) {
+ struct in_addr a;
+
+ cp = buf + sizeof("sortlist") - 1;
+ while (nsort < MAXRESOLVSORT) {
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == '\0' || *cp == '\n' || *cp == ';')
+ break;
+ net = cp;
+ while (*cp && !ISSORTMASK(*cp) && *cp != ';' &&
+ isascii(*cp) && !isspace((unsigned char)*cp))
+ cp++;
+ n = *cp;
+ *cp = 0;
+ if (inet_aton(net, &a)) {
+ statp->sort_list[nsort].addr = a;
+ if (ISSORTMASK(n)) {
+ *cp++ = n;
+ net = cp;
+ while (*cp && *cp != ';' &&
+ isascii(*cp) &&
+ !isspace((unsigned char)*cp))
+ cp++;
+ n = *cp;
+ *cp = 0;
+ if (inet_aton(net, &a)) {
+ statp->sort_list[nsort].mask = a.s_addr;
+ } else {
+ statp->sort_list[nsort].mask =
+ net_mask(statp->sort_list[nsort].addr);
+ }
+ } else {
+ statp->sort_list[nsort].mask =
+ net_mask(statp->sort_list[nsort].addr);
+ }
+ nsort++;
+ }
+ *cp = n;
+ }
+ continue;
+ }
+#endif
+ if (MATCH(buf, "options")) {
+ res_setoptions(statp, buf + sizeof("options") - 1, "conf");
+ continue;
+ }
+ }
+ if (nserv > 0)
+ statp->nscount = nserv;
+#ifdef RESOLVSORT
+ statp->nsort = nsort;
+#endif
+ (void) fclose(fp);
+ }
+/*
+ * Last chance to get a nameserver. This should not normally
+ * be necessary
+ */
+#ifdef NO_RESOLV_CONF
+ if(nserv == 0)
+ nserv = get_nameservers(statp);
+#endif
+
+ if (statp->defdname[0] == 0 &&
+ gethostname(buf, sizeof(statp->defdname) - 1) == 0 &&
+ (cp = strchr(buf, '.')) != NULL)
+ strcpy(statp->defdname, cp + 1);
+
+ /* find components of local domain that might be searched */
+ if (havesearch == 0) {
+ pp = statp->dnsrch;
+ *pp++ = statp->defdname;
+ *pp = NULL;
+
+ dots = 0;
+ for (cp = statp->defdname; *cp; cp++)
+ dots += (*cp == '.');
+
+ cp = statp->defdname;
+ while (pp < statp->dnsrch + MAXDFLSRCH) {
+ if (dots < LOCALDOMAINPARTS)
+ break;
+ cp = strchr(cp, '.') + 1; /*%< we know there is one */
+ *pp++ = cp;
+ dots--;
+ }
+ *pp = NULL;
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG) {
+ printf(";; res_init()... default dnsrch list:\n");
+ for (pp = statp->dnsrch; *pp; pp++)
+ printf(";;\t%s\n", *pp);
+ printf(";;\t..END..\n");
+ }
+#endif
+ }
+
+ if ((cp = getenv("RES_OPTIONS")) != NULL)
+ res_setoptions(statp, cp, "env");
+ statp->options |= RES_INIT;
+ return (statp->res_h_errno);
+#ifdef SUNW_INITCHKIF
+freedata:
+ RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
+ if (statp->_u._ext.ext != NULL) {
+ free(statp->_u._ext.ext);
+ statp->_u._ext.ext = NULL;
+ }
+ return (-1);
+#endif /* SUNW_INITCHKIF */
+
+}
+
+static void
+res_setoptions(res_state statp, const char *options, const char *source)
+{
+ const char *cp = options;
+ int i;
+ struct __res_state_ext *ext = statp->_u._ext.ext;
+
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";; res_setoptions(\"%s\", \"%s\")...\n",
+ options, source);
+#endif
+ while (*cp) {
+ /* skip leading and inner runs of spaces */
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ /* search for and process individual options */
+ if (!strncmp(cp, "ndots:", sizeof("ndots:") - 1)) {
+ i = atoi(cp + sizeof("ndots:") - 1);
+ if (i <= RES_MAXNDOTS)
+ statp->ndots = i;
+ else
+ statp->ndots = RES_MAXNDOTS;
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";;\tndots=%d\n", statp->ndots);
+#endif
+ } else if (!strncmp(cp, "timeout:", sizeof("timeout:") - 1)) {
+ i = atoi(cp + sizeof("timeout:") - 1);
+ if (i <= RES_MAXRETRANS)
+ statp->retrans = i;
+ else
+ statp->retrans = RES_MAXRETRANS;
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";;\ttimeout=%d\n", statp->retrans);
+#endif
+#ifdef SOLARIS2
+ } else if (!strncmp(cp, "retrans:", sizeof("retrans:") - 1)) {
+ /*
+ * For backward compatibility, 'retrans' is
+ * supported as an alias for 'timeout', though
+ * without an imposed maximum.
+ */
+ statp->retrans = atoi(cp + sizeof("retrans:") - 1);
+ } else if (!strncmp(cp, "retry:", sizeof("retry:") - 1)){
+ /*
+ * For backward compatibility, 'retry' is
+ * supported as an alias for 'attempts', though
+ * without an imposed maximum.
+ */
+ statp->retry = atoi(cp + sizeof("retry:") - 1);
+#endif /* SOLARIS2 */
+ } else if (!strncmp(cp, "attempts:", sizeof("attempts:") - 1)){
+ i = atoi(cp + sizeof("attempts:") - 1);
+ if (i <= RES_MAXRETRY)
+ statp->retry = i;
+ else
+ statp->retry = RES_MAXRETRY;
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";;\tattempts=%d\n", statp->retry);
+#endif
+ } else if (!strncmp(cp, "debug", sizeof("debug") - 1)) {
+#ifdef DEBUG
+ if (!(statp->options & RES_DEBUG)) {
+ printf(";; res_setoptions(\"%s\", \"%s\")..\n",
+ options, source);
+ statp->options |= RES_DEBUG;
+ }
+ printf(";;\tdebug\n");
+#endif
+ } else if (!strncmp(cp, "no_tld_query",
+ sizeof("no_tld_query") - 1) ||
+ !strncmp(cp, "no-tld-query",
+ sizeof("no-tld-query") - 1)) {
+ statp->options |= RES_NOTLDQUERY;
+ } else if (!strncmp(cp, "inet6", sizeof("inet6") - 1)) {
+ statp->options |= RES_USE_INET6;
+ } else if (!strncmp(cp, "rotate", sizeof("rotate") - 1)) {
+ statp->options |= RES_ROTATE;
+ } else if (!strncmp(cp, "no-check-names",
+ sizeof("no-check-names") - 1)) {
+ statp->options |= RES_NOCHECKNAME;
+ }
+#ifdef RES_USE_EDNS0
+ else if (!strncmp(cp, "edns0", sizeof("edns0") - 1)) {
+ statp->options |= RES_USE_EDNS0;
+ }
+#endif
+ else if (!strncmp(cp, "dname", sizeof("dname") - 1)) {
+ statp->options |= RES_USE_DNAME;
+ }
+ else if (!strncmp(cp, "nibble:", sizeof("nibble:") - 1)) {
+ if (ext == NULL)
+ goto skip;
+ cp += sizeof("nibble:") - 1;
+ i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix) - 1);
+ strncpy(ext->nsuffix, cp, i);
+ ext->nsuffix[i] = '\0';
+ }
+ else if (!strncmp(cp, "nibble2:", sizeof("nibble2:") - 1)) {
+ if (ext == NULL)
+ goto skip;
+ cp += sizeof("nibble2:") - 1;
+ i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix2) - 1);
+ strncpy(ext->nsuffix2, cp, i);
+ ext->nsuffix2[i] = '\0';
+ }
+ else if (!strncmp(cp, "v6revmode:", sizeof("v6revmode:") - 1)) {
+ cp += sizeof("v6revmode:") - 1;
+ /* "nibble" and "bitstring" used to be valid */
+ if (!strncmp(cp, "single", sizeof("single") - 1)) {
+ statp->options |= RES_NO_NIBBLE2;
+ } else if (!strncmp(cp, "both", sizeof("both") - 1)) {
+ statp->options &=
+ ~RES_NO_NIBBLE2;
+ }
+ }
+ else {
+ /* XXX - print a warning here? */
+ }
+ skip:
+ /* skip to next run of spaces */
+ while (*cp && *cp != ' ' && *cp != '\t')
+ cp++;
+ }
+}
+
+#ifdef RESOLVSORT
+/* XXX - should really support CIDR which means explicit masks always. */
+static u_int32_t
+net_mask(in) /*!< XXX - should really use system's version of this */
+ struct in_addr in;
+{
+ register u_int32_t i = ntohl(in.s_addr);
+
+ if (IN_CLASSA(i))
+ return (htonl(IN_CLASSA_NET));
+ else if (IN_CLASSB(i))
+ return (htonl(IN_CLASSB_NET));
+ return (htonl(IN_CLASSC_NET));
+}
+#endif
+
+void
+res_rndinit(res_state statp)
+{
+ struct timeval now;
+ u_int32_t u32;
+ u_int16_t u16;
+
+ gettimeofday(&now, NULL);
+ u32 = now.tv_sec;
+ memcpy(statp->_u._ext._rnd, &u32, 4);
+ u32 = now.tv_usec;
+ memcpy(statp->_u._ext._rnd + 4, &u32, 4);
+ u32 += now.tv_sec;
+ memcpy(statp->_u._ext._rnd + 8, &u32, 4);
+ u16 = getpid();
+ memcpy(statp->_u._ext._rnd + 12, &u16, 2);
+
+}
+
+u_int
+res_nrandomid(res_state statp) {
+ struct timeval now;
+ u_int16_t u16;
+ MD5_CTX ctx;
+
+ gettimeofday(&now, NULL);
+ u16 = (u_int16_t) (now.tv_sec ^ now.tv_usec);
+
+ memcpy(statp->_u._ext._rnd + 14, &u16, 2);
+#ifndef HAVE_MD5
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, statp->_u._ext._rnd, 16);
+ MD5_Final(statp->_u._ext._rnd, &ctx);
+#else
+ MD5Init(&ctx);
+ MD5Update(&ctx, statp->_u._ext._rnd, 16);
+ MD5Final(statp->_u._ext._rnd, &ctx);
+#endif
+ memcpy(&u16, statp->_u._ext._rnd + 14, 2);
+ return ((u_int) u16);
+}
+
+/*%
+ * This routine is for closing the socket if a virtual circuit is used and
+ * the program wants to close it. This provides support for endhostent()
+ * which expects to close the socket.
+ *
+ * This routine is not expected to be user visible.
+ */
+void
+res_nclose(res_state statp) {
+ int ns;
+
+ if (statp->_vcsock >= 0) {
+ (void) close(statp->_vcsock);
+ statp->_vcsock = -1;
+ statp->_flags &= ~(RES_F_VC | RES_F_CONN);
+ }
+ for (ns = 0; ns < statp->_u._ext.nscount; ns++) {
+ if (statp->_u._ext.nssocks[ns] != -1) {
+ (void) close(statp->_u._ext.nssocks[ns]);
+ statp->_u._ext.nssocks[ns] = -1;
+ }
+ }
+}
+
+void
+res_ndestroy(res_state statp) {
+ res_nclose(statp);
+ if (statp->_u._ext.ext != NULL)
+ free(statp->_u._ext.ext);
+ statp->options &= ~RES_INIT;
+ statp->_u._ext.ext = NULL;
+}
+
+const char *
+res_get_nibblesuffix(res_state statp) {
+ if (statp->_u._ext.ext)
+ return (statp->_u._ext.ext->nsuffix);
+ return ("ip6.arpa");
+}
+
+const char *
+res_get_nibblesuffix2(res_state statp) {
+ if (statp->_u._ext.ext)
+ return (statp->_u._ext.ext->nsuffix2);
+ return ("ip6.int");
+}
+
+void
+res_setservers(res_state statp, const union res_sockaddr_union *set, int cnt) {
+ int i, nserv;
+ size_t size;
+
+ /* close open servers */
+ res_nclose(statp);
+
+ /* cause rtt times to be forgotten */
+ statp->_u._ext.nscount = 0;
+
+ nserv = 0;
+ for (i = 0; i < cnt && nserv < MAXNS; i++) {
+ switch (set->sin.sin_family) {
+ case AF_INET:
+ size = sizeof(set->sin);
+ if (statp->_u._ext.ext)
+ memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
+ &set->sin, size);
+ if (size <= sizeof(statp->nsaddr_list[nserv]))
+ memcpy(&statp->nsaddr_list[nserv],
+ &set->sin, size);
+ else
+ statp->nsaddr_list[nserv].sin_family = 0;
+ nserv++;
+ break;
+
+#ifdef HAS_INET6_STRUCTS
+ case AF_INET6:
+ size = sizeof(set->sin6);
+ if (statp->_u._ext.ext)
+ memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
+ &set->sin6, size);
+ if (size <= sizeof(statp->nsaddr_list[nserv]))
+ memcpy(&statp->nsaddr_list[nserv],
+ &set->sin6, size);
+ else
+ statp->nsaddr_list[nserv].sin_family = 0;
+ nserv++;
+ break;
+#endif
+
+ default:
+ break;
+ }
+ set++;
+ }
+ statp->nscount = nserv;
+
+}
+
+int
+res_getservers(res_state statp, union res_sockaddr_union *set, int cnt) {
+ int i;
+ size_t size;
+ u_int16_t family;
+
+ for (i = 0; i < statp->nscount && i < cnt; i++) {
+ if (statp->_u._ext.ext)
+ family = statp->_u._ext.ext->nsaddrs[i].sin.sin_family;
+ else
+ family = statp->nsaddr_list[i].sin_family;
+
+ switch (family) {
+ case AF_INET:
+ size = sizeof(set->sin);
+ if (statp->_u._ext.ext)
+ memcpy(&set->sin,
+ &statp->_u._ext.ext->nsaddrs[i],
+ size);
+ else
+ memcpy(&set->sin, &statp->nsaddr_list[i],
+ size);
+ break;
+
+#ifdef HAS_INET6_STRUCTS
+ case AF_INET6:
+ size = sizeof(set->sin6);
+ if (statp->_u._ext.ext)
+ memcpy(&set->sin6,
+ &statp->_u._ext.ext->nsaddrs[i],
+ size);
+ else
+ memcpy(&set->sin6, &statp->nsaddr_list[i],
+ size);
+ break;
+#endif
+
+ default:
+ set->sin.sin_family = 0;
+ break;
+ }
+ set++;
+ }
+ return (statp->nscount);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_mkquery.c b/usr/src/lib/libresolv2_joy/common/resolv/res_mkquery.c
new file mode 100644
index 0000000000..29cfea2786
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_mkquery.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Portions Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1996, 1997, 1988, 1999, 2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 1985, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "port_before.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+
+#ifdef SUNW_CONFCHECK
+#include <sys/socket.h>
+#include <errno.h>
+#include <sys/stat.h>
+#endif /* SUNW_CONFCHECK */
+
+
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <string.h>
+#include "port_after.h"
+
+/* Options. Leave them on. */
+#define DEBUG
+
+extern const char *_res_opcodes[];
+
+#ifdef SUNW_CONFCHECK
+static int _confcheck(res_state statp);
+#endif /* SUNW_CONFCHECK */
+
+
+/*%
+ * Form all types of queries.
+ * Returns the size of the result or -1.
+ */
+int
+res_nmkquery(res_state statp,
+ int op, /*!< opcode of query */
+ const char *dname, /*!< domain name */
+ int class, int type, /*!< class and type of query */
+ const u_char *data, /*!< resource record data */
+ int datalen, /*!< length of data */
+ const u_char *newrr_in, /*!< new rr for modify or append */
+ u_char *buf, /*!< buffer to put query */
+ int buflen) /*!< size of buffer */
+{
+ register HEADER *hp;
+ register u_char *cp, *ep;
+ register int n;
+ u_char *dnptrs[20], **dpp, **lastdnptr;
+
+ UNUSED(newrr_in);
+
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";; res_nmkquery(%s, %s, %s, %s)\n",
+ _res_opcodes[op], dname, p_class(class), p_type(type));
+#endif
+
+#ifdef SUNW_CONFCHECK
+ /*
+ * 1247019, 1265838, and 4034368: Check to see if we can
+ * bailout quickly.
+ */
+ if (_confcheck(statp) == -1) {
+ RES_SET_H_ERRNO(statp, NO_RECOVERY);
+ return(-1);
+ }
+#endif /* SUNW_CONFCHECK */
+
+ /*
+ * Initialize header fields.
+ */
+ if ((buf == NULL) || (buflen < HFIXEDSZ))
+ return (-1);
+ memset(buf, 0, HFIXEDSZ);
+ hp = (HEADER *) buf;
+ statp->id = res_nrandomid(statp);
+ hp->id = htons(statp->id);
+ hp->opcode = op;
+ hp->rd = (statp->options & RES_RECURSE) != 0U;
+ hp->rcode = NOERROR;
+ cp = buf + HFIXEDSZ;
+ ep = buf + buflen;
+ dpp = dnptrs;
+ *dpp++ = buf;
+ *dpp++ = NULL;
+ lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
+ /*
+ * perform opcode specific processing
+ */
+ switch (op) {
+ case QUERY: /*FALLTHROUGH*/
+ case NS_NOTIFY_OP:
+ if (ep - cp < QFIXEDSZ)
+ return (-1);
+ if ((n = dn_comp(dname, cp, ep - cp - QFIXEDSZ, dnptrs,
+ lastdnptr)) < 0)
+ return (-1);
+ cp += n;
+ ns_put16(type, cp);
+ cp += INT16SZ;
+ ns_put16(class, cp);
+ cp += INT16SZ;
+ hp->qdcount = htons(1);
+ if (op == QUERY || data == NULL)
+ break;
+ /*
+ * Make an additional record for completion domain.
+ */
+ if ((ep - cp) < RRFIXEDSZ)
+ return (-1);
+ n = dn_comp((const char *)data, cp, ep - cp - RRFIXEDSZ,
+ dnptrs, lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ns_put16(T_NULL, cp);
+ cp += INT16SZ;
+ ns_put16(class, cp);
+ cp += INT16SZ;
+ ns_put32(0, cp);
+ cp += INT32SZ;
+ ns_put16(0, cp);
+ cp += INT16SZ;
+ hp->arcount = htons(1);
+ break;
+
+ case IQUERY:
+ /*
+ * Initialize answer section
+ */
+ if (ep - cp < 1 + RRFIXEDSZ + datalen)
+ return (-1);
+ *cp++ = '\0'; /*%< no domain name */
+ ns_put16(type, cp);
+ cp += INT16SZ;
+ ns_put16(class, cp);
+ cp += INT16SZ;
+ ns_put32(0, cp);
+ cp += INT32SZ;
+ ns_put16(datalen, cp);
+ cp += INT16SZ;
+ if (datalen) {
+ memcpy(cp, data, datalen);
+ cp += datalen;
+ }
+ hp->ancount = htons(1);
+ break;
+
+ default:
+ return (-1);
+ }
+ return (cp - buf);
+}
+
+#ifdef RES_USE_EDNS0
+/* attach OPT pseudo-RR, as documented in RFC2671 (EDNS0). */
+
+int
+res_nopt(res_state statp,
+ int n0, /*%< current offset in buffer */
+ u_char *buf, /*%< buffer to put query */
+ int buflen, /*%< size of buffer */
+ int anslen) /*%< UDP answer buffer size */
+{
+ register HEADER *hp;
+ register u_char *cp, *ep;
+ u_int16_t flags = 0;
+
+#ifdef DEBUG
+ if ((statp->options & RES_DEBUG) != 0U)
+ printf(";; res_nopt()\n");
+#endif
+
+ hp = (HEADER *) buf;
+ cp = buf + n0;
+ ep = buf + buflen;
+
+ if ((ep - cp) < 1 + RRFIXEDSZ)
+ return (-1);
+
+ *cp++ = 0; /*%< "." */
+ ns_put16(ns_t_opt, cp); /*%< TYPE */
+ cp += INT16SZ;
+ ns_put16(anslen & 0xffff, cp); /*%< CLASS = UDP payload size */
+ cp += INT16SZ;
+ *cp++ = NOERROR; /*%< extended RCODE */
+ *cp++ = 0; /*%< EDNS version */
+
+ if (statp->options & RES_USE_DNSSEC) {
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";; res_opt()... ENDS0 DNSSEC\n");
+#endif
+ flags |= NS_OPT_DNSSEC_OK;
+ }
+ ns_put16(flags, cp);
+ cp += INT16SZ;
+
+ ns_put16(0U, cp); /*%< RDLEN */
+ cp += INT16SZ;
+
+ hp->arcount = htons(ntohs(hp->arcount) + 1);
+
+ return (cp - buf);
+}
+
+/*
+ * Construct variable data (RDATA) block for OPT psuedo-RR, append it
+ * to the buffer, then update the RDLEN field (previously set to zero by
+ * res_nopt()) with the new RDATA length.
+ */
+int
+res_nopt_rdata(res_state statp,
+ int n0, /*%< current offset in buffer */
+ u_char *buf, /*%< buffer to put query */
+ int buflen, /*%< size of buffer */
+ u_char *rdata, /*%< ptr to start of opt rdata */
+ u_short code, /*%< OPTION-CODE */
+ u_short len, /*%< OPTION-LENGTH */
+ u_char *data) /*%< OPTION_DATA */
+{
+ register u_char *cp, *ep;
+
+#ifdef DEBUG
+ if ((statp->options & RES_DEBUG) != 0U)
+ printf(";; res_nopt_rdata()\n");
+#endif
+
+ cp = buf + n0;
+ ep = buf + buflen;
+
+ if ((ep - cp) < (4 + len))
+ return (-1);
+
+ if (rdata < (buf + 2) || rdata >= ep)
+ return (-1);
+
+ ns_put16(code, cp);
+ cp += INT16SZ;
+
+ ns_put16(len, cp);
+ cp += INT16SZ;
+
+ memcpy(cp, data, len);
+ cp += len;
+
+ len = cp - rdata;
+ ns_put16(len, rdata - 2); /* Update RDLEN field */
+
+ return (cp - buf);
+}
+#endif
+
+#ifdef SUNW_CONFCHECK
+
+/*
+ * Time out quickly if there is no /etc/resolv.conf and a TCP connection
+ * to the local DNS server fails.
+ */
+static int _confcheck(res_state statp)
+{
+ int ns;
+ struct stat rc_stat;
+ struct sockaddr_in ns_sin;
+
+ /* First, we check to see if /etc/resolv.conf exists.
+ * If it doesn't, then it is likely that the localhost is
+ * the nameserver.
+ */
+ if (stat(_PATH_RESCONF, &rc_stat) == -1 && errno == ENOENT) {
+
+ /* Next, we check to see if _res.nsaddr is set to loopback.
+ * If it isn't, it has been altered by the application
+ * explicitly and we then want to bail with success.
+ */
+ if (statp->nsaddr.sin_addr.S_un.S_addr ==
+ htonl(INADDR_LOOPBACK)) {
+
+ /* Lastly, we try to connect to the TCP port of the
+ * nameserver. If this fails, then we know that
+ * DNS is misconfigured and we can quickly exit.
+ */
+ ns = socket(AF_INET, SOCK_STREAM, 0);
+ IN_SET_LOOPBACK_ADDR(&ns_sin);
+ ns_sin.sin_port = htons(NAMESERVER_PORT);
+ if (connect(ns, (struct sockaddr *) &ns_sin,
+ sizeof ns_sin) == -1) {
+ close(ns);
+ return(-1);
+ }
+ else {
+ close(ns);
+
+ return(0);
+ }
+ }
+
+ return(0);
+ }
+
+ return (0);
+}
+#endif /* SUNW_CONFCHECK */
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.c b/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.c
new file mode 100644
index 0000000000..686718b42f
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.c
@@ -0,0 +1,1159 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*! \file
+ * \brief
+ * Based on the Dynamic DNS reference implementation by Viraj Bais
+ * &lt;viraj_bais@ccm.fm.intel.com>
+ */
+
+#include "port_before.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <res_update.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "port_after.h"
+
+/* Options. Leave them on. */
+#define DEBUG
+#define MAXPORT 1024
+
+static int getnum_str(u_char **, u_char *);
+static int gethexnum_str(u_char **, u_char *);
+static int getword_str(char *, int, u_char **, u_char *);
+static int getstr_str(char *, int, u_char **, u_char *);
+
+#define ShrinkBuffer(x) if ((buflen -= x) < 0) return (-2);
+
+/* Forward. */
+
+int res_protocolnumber(const char *);
+int res_servicenumber(const char *);
+
+/*%
+ * Form update packets.
+ * Returns the size of the resulting packet if no error
+ *
+ * On error,
+ * returns
+ *\li -1 if error in reading a word/number in rdata
+ * portion for update packets
+ *\li -2 if length of buffer passed is insufficient
+ *\li -3 if zone section is not the first section in
+ * the linked list, or section order has a problem
+ *\li -4 on a number overflow
+ *\li -5 unknown operation or no records
+ */
+int
+res_nmkupdate(res_state statp, ns_updrec *rrecp_in, u_char *buf, int buflen) {
+ ns_updrec *rrecp_start = rrecp_in;
+ HEADER *hp;
+ u_char *cp, *sp2, *startp, *endp;
+ int n, i, soanum, multiline;
+ ns_updrec *rrecp;
+ struct in_addr ina;
+ struct in6_addr in6a;
+ char buf2[MAXDNAME];
+ u_char buf3[MAXDNAME];
+ int section, numrrs = 0, counts[ns_s_max];
+ u_int16_t rtype, rclass;
+ u_int32_t n1, rttl;
+ u_char *dnptrs[20], **dpp, **lastdnptr;
+ int siglen, keylen, certlen;
+
+ /*
+ * Initialize header fields.
+ */
+ if ((buf == NULL) || (buflen < HFIXEDSZ))
+ return (-1);
+ memset(buf, 0, HFIXEDSZ);
+ hp = (HEADER *) buf;
+ statp->id = res_nrandomid(statp);
+ hp->id = htons(statp->id);
+ hp->opcode = ns_o_update;
+ hp->rcode = NOERROR;
+ cp = buf + HFIXEDSZ;
+ buflen -= HFIXEDSZ;
+ dpp = dnptrs;
+ *dpp++ = buf;
+ *dpp++ = NULL;
+ lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];
+
+ if (rrecp_start == NULL)
+ return (-5);
+ else if (rrecp_start->r_section != S_ZONE)
+ return (-3);
+
+ memset(counts, 0, sizeof counts);
+ for (rrecp = rrecp_start; rrecp; rrecp = NEXT(rrecp, r_glink)) {
+ numrrs++;
+ section = rrecp->r_section;
+ if (section < 0 || section >= ns_s_max)
+ return (-1);
+ counts[section]++;
+ for (i = section + 1; i < ns_s_max; i++)
+ if (counts[i])
+ return (-3);
+ rtype = rrecp->r_type;
+ rclass = rrecp->r_class;
+ rttl = rrecp->r_ttl;
+ /* overload class and type */
+ if (section == S_PREREQ) {
+ rttl = 0;
+ switch (rrecp->r_opcode) {
+ case YXDOMAIN:
+ rclass = C_ANY;
+ rtype = T_ANY;
+ rrecp->r_size = 0;
+ break;
+ case NXDOMAIN:
+ rclass = C_NONE;
+ rtype = T_ANY;
+ rrecp->r_size = 0;
+ break;
+ case NXRRSET:
+ rclass = C_NONE;
+ rrecp->r_size = 0;
+ break;
+ case YXRRSET:
+ if (rrecp->r_size == 0)
+ rclass = C_ANY;
+ break;
+ default:
+ fprintf(stderr,
+ "res_mkupdate: incorrect opcode: %d\n",
+ rrecp->r_opcode);
+ fflush(stderr);
+ return (-1);
+ }
+ } else if (section == S_UPDATE) {
+ switch (rrecp->r_opcode) {
+ case DELETE:
+ rclass = rrecp->r_size == 0 ? C_ANY : C_NONE;
+ break;
+ case ADD:
+ break;
+ default:
+ fprintf(stderr,
+ "res_mkupdate: incorrect opcode: %d\n",
+ rrecp->r_opcode);
+ fflush(stderr);
+ return (-1);
+ }
+ }
+
+ /*
+ * XXX appending default domain to owner name is omitted,
+ * fqdn must be provided
+ */
+ if ((n = dn_comp(rrecp->r_dname, cp, buflen, dnptrs,
+ lastdnptr)) < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n + 2*INT16SZ);
+ PUTSHORT(rtype, cp);
+ PUTSHORT(rclass, cp);
+ if (section == S_ZONE) {
+ if (numrrs != 1 || rrecp->r_type != T_SOA)
+ return (-3);
+ continue;
+ }
+ ShrinkBuffer(INT32SZ + INT16SZ);
+ PUTLONG(rttl, cp);
+ sp2 = cp; /*%< save pointer to length byte */
+ cp += INT16SZ;
+ if (rrecp->r_size == 0) {
+ if (section == S_UPDATE && rclass != C_ANY)
+ return (-1);
+ else {
+ PUTSHORT(0, sp2);
+ continue;
+ }
+ }
+ startp = rrecp->r_data;
+ endp = startp + rrecp->r_size - 1;
+ /* XXX this should be done centrally. */
+ switch (rrecp->r_type) {
+ case T_A:
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ if (!inet_aton(buf2, &ina))
+ return (-1);
+ n1 = ntohl(ina.s_addr);
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(n1, cp);
+ break;
+ case T_CNAME:
+ case T_MB:
+ case T_MG:
+ case T_MR:
+ case T_NS:
+ case T_PTR:
+ case ns_t_dname:
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ break;
+ case T_MINFO:
+ case T_SOA:
+ case T_RP:
+ for (i = 0; i < 2; i++) {
+ if (!getword_str(buf2, sizeof buf2, &startp,
+ endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen,
+ dnptrs, lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ }
+ if (rrecp->r_type == T_SOA) {
+ ShrinkBuffer(5 * INT32SZ);
+ while (isspace(*startp) || !*startp)
+ startp++;
+ if (*startp == '(') {
+ multiline = 1;
+ startp++;
+ } else
+ multiline = 0;
+ /* serial, refresh, retry, expire, minimum */
+ for (i = 0; i < 5; i++) {
+ soanum = getnum_str(&startp, endp);
+ if (soanum < 0)
+ return (-1);
+ PUTLONG(soanum, cp);
+ }
+ if (multiline) {
+ while (isspace(*startp) || !*startp)
+ startp++;
+ if (*startp != ')')
+ return (-1);
+ }
+ }
+ break;
+ case T_MX:
+ case T_AFSDB:
+ case T_RT:
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ break;
+ case T_SRV:
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, NULL, NULL);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ break;
+ case T_PX:
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ PUTSHORT(n, cp);
+ ShrinkBuffer(INT16SZ);
+ for (i = 0; i < 2; i++) {
+ if (!getword_str(buf2, sizeof buf2, &startp,
+ endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, dnptrs,
+ lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ }
+ break;
+ case T_WKS: {
+ char bm[MAXPORT/8];
+ unsigned int maxbm = 0;
+
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ if (!inet_aton(buf2, &ina))
+ return (-1);
+ n1 = ntohl(ina.s_addr);
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(n1, cp);
+
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ if ((i = res_protocolnumber(buf2)) < 0)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = i & 0xff;
+
+ for (i = 0; i < MAXPORT/8 ; i++)
+ bm[i] = 0;
+
+ while (getword_str(buf2, sizeof buf2, &startp, endp)) {
+ if ((n = res_servicenumber(buf2)) <= 0)
+ return (-1);
+
+ if (n < MAXPORT) {
+ bm[n/8] |= (0x80>>(n%8));
+ if ((unsigned)n > maxbm)
+ maxbm = n;
+ } else
+ return (-1);
+ }
+ maxbm = maxbm/8 + 1;
+ ShrinkBuffer(maxbm);
+ memcpy(cp, bm, maxbm);
+ cp += maxbm;
+ break;
+ }
+ case T_HINFO:
+ for (i = 0; i < 2; i++) {
+ if ((n = getstr_str(buf2, sizeof buf2,
+ &startp, endp)) < 0)
+ return (-1);
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ }
+ break;
+ case T_TXT:
+ for (;;) {
+ if ((n = getstr_str(buf2, sizeof buf2,
+ &startp, endp)) < 0) {
+ if (cp != (sp2 + INT16SZ))
+ break;
+ return (-1);
+ }
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ }
+ break;
+ case T_X25:
+ /* RFC1183 */
+ if ((n = getstr_str(buf2, sizeof buf2, &startp,
+ endp)) < 0)
+ return (-1);
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ break;
+ case T_ISDN:
+ /* RFC1183 */
+ if ((n = getstr_str(buf2, sizeof buf2, &startp,
+ endp)) < 0)
+ return (-1);
+ if ((n > 255) || (n == 0))
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ if ((n = getstr_str(buf2, sizeof buf2, &startp,
+ endp)) < 0)
+ n = 0;
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ break;
+ case T_NSAP:
+ if ((n = inet_nsap_addr((char *)startp, (u_char *)buf2, sizeof(buf2))) != 0) {
+ ShrinkBuffer(n);
+ memcpy(cp, buf2, n);
+ cp += n;
+ } else {
+ return (-1);
+ }
+ break;
+ case T_LOC:
+ if ((n = loc_aton((char *)startp, (u_char *)buf2)) != 0) {
+ ShrinkBuffer(n);
+ memcpy(cp, buf2, n);
+ cp += n;
+ } else
+ return (-1);
+ break;
+ case ns_t_sig:
+ {
+ int sig_type, success, dateerror;
+ u_int32_t exptime, timesigned;
+
+ /* type */
+ if ((n = getword_str(buf2, sizeof buf2,
+ &startp, endp)) < 0)
+ return (-1);
+ sig_type = sym_ston(__p_type_syms, buf2, &success);
+ if (!success || sig_type == ns_t_any)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(sig_type, cp);
+ /* alg */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = n;
+ /* labels */
+ n = getnum_str(&startp, endp);
+ if (n <= 0 || n > 255)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = n;
+ /* ottl & expire */
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ exptime = ns_datetosecs(buf2, &dateerror);
+ if (!dateerror) {
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(rttl, cp);
+ }
+ else {
+ char *ulendp;
+ u_int32_t ottl;
+
+ errno = 0;
+ ottl = strtoul(buf2, &ulendp, 10);
+ if (errno != 0 ||
+ (ulendp != NULL && *ulendp != '\0'))
+ return (-1);
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(ottl, cp);
+ if (!getword_str(buf2, sizeof buf2, &startp,
+ endp))
+ return (-1);
+ exptime = ns_datetosecs(buf2, &dateerror);
+ if (dateerror)
+ return (-1);
+ }
+ /* expire */
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(exptime, cp);
+ /* timesigned */
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ timesigned = ns_datetosecs(buf2, &dateerror);
+ if (!dateerror) {
+ ShrinkBuffer(INT32SZ);
+ PUTLONG(timesigned, cp);
+ }
+ else
+ return (-1);
+ /* footprint */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* signer name */
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, dnptrs, lastdnptr);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ /* sig */
+ if ((n = getword_str(buf2, sizeof buf2,
+ &startp, endp)) < 0)
+ return (-1);
+ siglen = b64_pton(buf2, buf3, sizeof(buf3));
+ if (siglen < 0)
+ return (-1);
+ ShrinkBuffer(siglen);
+ memcpy(cp, buf3, siglen);
+ cp += siglen;
+ break;
+ }
+ case ns_t_key:
+ /* flags */
+ n = gethexnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* proto */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = n;
+ /* alg */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = n;
+ /* key */
+ if ((n = getword_str(buf2, sizeof buf2,
+ &startp, endp)) < 0)
+ return (-1);
+ keylen = b64_pton(buf2, buf3, sizeof(buf3));
+ if (keylen < 0)
+ return (-1);
+ ShrinkBuffer(keylen);
+ memcpy(cp, buf3, keylen);
+ cp += keylen;
+ break;
+ case ns_t_nxt:
+ {
+ int success, nxt_type;
+ u_char data[32];
+ int maxtype;
+
+ /* next name */
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, NULL, NULL);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ maxtype = 0;
+ memset(data, 0, sizeof data);
+ for (;;) {
+ if (!getword_str(buf2, sizeof buf2, &startp,
+ endp))
+ break;
+ nxt_type = sym_ston(__p_type_syms, buf2,
+ &success);
+ if (!success || !ns_t_rr_p(nxt_type))
+ return (-1);
+ NS_NXT_BIT_SET(nxt_type, data);
+ if (nxt_type > maxtype)
+ maxtype = nxt_type;
+ }
+ n = maxtype/NS_NXT_BITS+1;
+ ShrinkBuffer(n);
+ memcpy(cp, data, n);
+ cp += n;
+ break;
+ }
+ case ns_t_cert:
+ /* type */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* key tag */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* alg */
+ n = getnum_str(&startp, endp);
+ if (n < 0)
+ return (-1);
+ ShrinkBuffer(1);
+ *cp++ = n;
+ /* cert */
+ if ((n = getword_str(buf2, sizeof buf2,
+ &startp, endp)) < 0)
+ return (-1);
+ certlen = b64_pton(buf2, buf3, sizeof(buf3));
+ if (certlen < 0)
+ return (-1);
+ ShrinkBuffer(certlen);
+ memcpy(cp, buf3, certlen);
+ cp += certlen;
+ break;
+ case ns_t_aaaa:
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ if (inet_pton(AF_INET6, buf2, &in6a) <= 0)
+ return (-1);
+ ShrinkBuffer(NS_IN6ADDRSZ);
+ memcpy(cp, &in6a, NS_IN6ADDRSZ);
+ cp += NS_IN6ADDRSZ;
+ break;
+ case ns_t_naptr:
+ /* Order Preference Flags Service Replacement Regexp */
+ /* Order */
+ n = getnum_str(&startp, endp);
+ if (n < 0 || n > 65535)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* Preference */
+ n = getnum_str(&startp, endp);
+ if (n < 0 || n > 65535)
+ return (-1);
+ ShrinkBuffer(INT16SZ);
+ PUTSHORT(n, cp);
+ /* Flags */
+ if ((n = getstr_str(buf2, sizeof buf2,
+ &startp, endp)) < 0) {
+ return (-1);
+ }
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ /* Service Classes */
+ if ((n = getstr_str(buf2, sizeof buf2,
+ &startp, endp)) < 0) {
+ return (-1);
+ }
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ /* Pattern */
+ if ((n = getstr_str(buf2, sizeof buf2,
+ &startp, endp)) < 0) {
+ return (-1);
+ }
+ if (n > 255)
+ return (-1);
+ ShrinkBuffer(n+1);
+ *cp++ = n;
+ memcpy(cp, buf2, n);
+ cp += n;
+ /* Replacement */
+ if (!getword_str(buf2, sizeof buf2, &startp, endp))
+ return (-1);
+ n = dn_comp(buf2, cp, buflen, NULL, NULL);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ ShrinkBuffer(n);
+ break;
+ default:
+ return (-1);
+ } /*switch*/
+ n = (u_int16_t)((cp - sp2) - INT16SZ);
+ PUTSHORT(n, sp2);
+ } /*for*/
+
+ hp->qdcount = htons(counts[0]);
+ hp->ancount = htons(counts[1]);
+ hp->nscount = htons(counts[2]);
+ hp->arcount = htons(counts[3]);
+ return (cp - buf);
+}
+
+/*%
+ * Get a whitespace delimited word from a string (not file)
+ * into buf. modify the start pointer to point after the
+ * word in the string.
+ */
+static int
+getword_str(char *buf, int size, u_char **startpp, u_char *endp) {
+ char *cp;
+ int c;
+
+ for (cp = buf; *startpp <= endp; ) {
+ c = **startpp;
+ if (isspace(c) || c == '\0') {
+ if (cp != buf) /*%< trailing whitespace */
+ break;
+ else { /*%< leading whitespace */
+ (*startpp)++;
+ continue;
+ }
+ }
+ (*startpp)++;
+ if (cp >= buf+size-1)
+ break;
+ *cp++ = (u_char)c;
+ }
+ *cp = '\0';
+ return (cp != buf);
+}
+
+/*%
+ * get a white spae delimited string from memory. Process quoted strings
+ * and \\DDD escapes. Return length or -1 on error. Returned string may
+ * contain nulls.
+ */
+static char digits[] = "0123456789";
+static int
+getstr_str(char *buf, int size, u_char **startpp, u_char *endp) {
+ char *cp;
+ int c, c1 = 0;
+ int inquote = 0;
+ int seen_quote = 0;
+ int escape = 0;
+ int dig = 0;
+
+ for (cp = buf; *startpp <= endp; ) {
+ if ((c = **startpp) == '\0')
+ break;
+ /* leading white space */
+ if ((cp == buf) && !seen_quote && isspace(c)) {
+ (*startpp)++;
+ continue;
+ }
+
+ switch (c) {
+ case '\\':
+ if (!escape) {
+ escape = 1;
+ dig = 0;
+ c1 = 0;
+ (*startpp)++;
+ continue;
+ }
+ goto do_escape;
+ case '"':
+ if (!escape) {
+ inquote = !inquote;
+ seen_quote = 1;
+ (*startpp)++;
+ continue;
+ }
+ /* fall through */
+ default:
+ do_escape:
+ if (escape) {
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ c1 = c1 * 10 +
+ (strchr(digits, c) - digits);
+
+ if (++dig == 3) {
+ c = c1 &0xff;
+ break;
+ }
+ (*startpp)++;
+ continue;
+ }
+ escape = 0;
+ } else if (!inquote && isspace(c))
+ goto done;
+ if (cp >= buf+size-1)
+ goto done;
+ *cp++ = (u_char)c;
+ (*startpp)++;
+ }
+ }
+ done:
+ *cp = '\0';
+ return ((cp == buf)? (seen_quote? 0: -1): (cp - buf));
+}
+
+/*%
+ * Get a whitespace delimited base 16 number from a string (not file) into buf
+ * update the start pointer to point after the number in the string.
+ */
+static int
+gethexnum_str(u_char **startpp, u_char *endp) {
+ int c, n;
+ int seendigit = 0;
+ int m = 0;
+
+ if (*startpp + 2 >= endp || strncasecmp((char *)*startpp, "0x", 2) != 0)
+ return getnum_str(startpp, endp);
+ (*startpp)+=2;
+ for (n = 0; *startpp <= endp; ) {
+ c = **startpp;
+ if (isspace(c) || c == '\0') {
+ if (seendigit) /*%< trailing whitespace */
+ break;
+ else { /*%< leading whitespace */
+ (*startpp)++;
+ continue;
+ }
+ }
+ if (c == ';') {
+ while ((*startpp <= endp) &&
+ ((c = **startpp) != '\n'))
+ (*startpp)++;
+ if (seendigit)
+ break;
+ continue;
+ }
+ if (!isxdigit(c)) {
+ if (c == ')' && seendigit) {
+ (*startpp)--;
+ break;
+ }
+ return (-1);
+ }
+ (*startpp)++;
+ if (isdigit(c))
+ n = n * 16 + (c - '0');
+ else
+ n = n * 16 + (tolower(c) - 'a' + 10);
+ seendigit = 1;
+ }
+ return (n + m);
+}
+
+/*%
+ * Get a whitespace delimited base 10 number from a string (not file) into buf
+ * update the start pointer to point after the number in the string.
+ */
+static int
+getnum_str(u_char **startpp, u_char *endp) {
+ int c, n;
+ int seendigit = 0;
+ int m = 0;
+
+ for (n = 0; *startpp <= endp; ) {
+ c = **startpp;
+ if (isspace(c) || c == '\0') {
+ if (seendigit) /*%< trailing whitespace */
+ break;
+ else { /*%< leading whitespace */
+ (*startpp)++;
+ continue;
+ }
+ }
+ if (c == ';') {
+ while ((*startpp <= endp) &&
+ ((c = **startpp) != '\n'))
+ (*startpp)++;
+ if (seendigit)
+ break;
+ continue;
+ }
+ if (!isdigit(c)) {
+ if (c == ')' && seendigit) {
+ (*startpp)--;
+ break;
+ }
+ return (-1);
+ }
+ (*startpp)++;
+ n = n * 10 + (c - '0');
+ seendigit = 1;
+ }
+ return (n + m);
+}
+
+/*%
+ * Allocate a resource record buffer & save rr info.
+ */
+ns_updrec *
+res_mkupdrec(int section, const char *dname,
+ u_int class, u_int type, u_long ttl) {
+ ns_updrec *rrecp = (ns_updrec *)calloc(1, sizeof(ns_updrec));
+
+ if (!rrecp || !(rrecp->r_dname = strdup(dname))) {
+ if (rrecp)
+ free((char *)rrecp);
+ return (NULL);
+ }
+ INIT_LINK(rrecp, r_link);
+ INIT_LINK(rrecp, r_glink);
+ rrecp->r_class = (ns_class)class;
+ rrecp->r_type = (ns_type)type;
+ rrecp->r_ttl = ttl;
+ rrecp->r_section = (ns_sect)section;
+ return (rrecp);
+}
+
+/*%
+ * Free a resource record buffer created by res_mkupdrec.
+ */
+void
+res_freeupdrec(ns_updrec *rrecp) {
+ /* Note: freeing r_dp is the caller's responsibility. */
+ if (rrecp->r_dname != NULL)
+ free(rrecp->r_dname);
+ free(rrecp);
+}
+
+struct valuelist {
+ struct valuelist * next;
+ struct valuelist * prev;
+ char * name;
+ char * proto;
+ int port;
+};
+static struct valuelist *servicelist, *protolist;
+
+static void
+res_buildservicelist() {
+ struct servent *sp;
+ struct valuelist *slp;
+
+#ifdef MAYBE_HESIOD
+ setservent(0);
+#else
+ setservent(1);
+#endif
+ while ((sp = getservent()) != NULL) {
+ slp = (struct valuelist *)malloc(sizeof(struct valuelist));
+ if (!slp)
+ break;
+ slp->name = strdup(sp->s_name);
+ slp->proto = strdup(sp->s_proto);
+ if ((slp->name == NULL) || (slp->proto == NULL)) {
+ if (slp->name) free(slp->name);
+ if (slp->proto) free(slp->proto);
+ free(slp);
+ break;
+ }
+ slp->port = ntohs((u_int16_t)sp->s_port); /*%< host byt order */
+ slp->next = servicelist;
+ slp->prev = NULL;
+ if (servicelist)
+ servicelist->prev = slp;
+ servicelist = slp;
+ }
+ endservent();
+}
+
+void
+res_destroyservicelist() {
+ struct valuelist *slp, *slp_next;
+
+ for (slp = servicelist; slp != NULL; slp = slp_next) {
+ slp_next = slp->next;
+ free(slp->name);
+ free(slp->proto);
+ free(slp);
+ }
+ servicelist = (struct valuelist *)0;
+}
+
+void
+res_buildprotolist(void) {
+ struct protoent *pp;
+ struct valuelist *slp;
+
+#ifdef MAYBE_HESIOD
+ setprotoent(0);
+#else
+ setprotoent(1);
+#endif
+ while ((pp = getprotoent()) != NULL) {
+ slp = (struct valuelist *)malloc(sizeof(struct valuelist));
+ if (!slp)
+ break;
+ slp->name = strdup(pp->p_name);
+ if (slp->name == NULL) {
+ free(slp);
+ break;
+ }
+ slp->port = pp->p_proto; /*%< host byte order */
+ slp->next = protolist;
+ slp->prev = NULL;
+ if (protolist)
+ protolist->prev = slp;
+ protolist = slp;
+ }
+ endprotoent();
+}
+
+void
+res_destroyprotolist(void) {
+ struct valuelist *plp, *plp_next;
+
+ for (plp = protolist; plp != NULL; plp = plp_next) {
+ plp_next = plp->next;
+ free(plp->name);
+ free(plp);
+ }
+ protolist = (struct valuelist *)0;
+}
+
+static int
+findservice(const char *s, struct valuelist **list) {
+ struct valuelist *lp = *list;
+ int n;
+
+ for (; lp != NULL; lp = lp->next)
+ if (strcasecmp(lp->name, s) == 0) {
+ if (lp != *list) {
+ lp->prev->next = lp->next;
+ if (lp->next)
+ lp->next->prev = lp->prev;
+ (*list)->prev = lp;
+ lp->next = *list;
+ *list = lp;
+ }
+ return (lp->port); /*%< host byte order */
+ }
+ if (sscanf(s, "%d", &n) != 1 || n <= 0)
+ n = -1;
+ return (n);
+}
+
+/*%
+ * Convert service name or (ascii) number to int.
+ */
+int
+res_servicenumber(const char *p) {
+ if (servicelist == (struct valuelist *)0)
+ res_buildservicelist();
+ return (findservice(p, &servicelist));
+}
+
+/*%
+ * Convert protocol name or (ascii) number to int.
+ */
+int
+res_protocolnumber(const char *p) {
+ if (protolist == (struct valuelist *)0)
+ res_buildprotolist();
+ return (findservice(p, &protolist));
+}
+
+static struct servent *
+cgetservbyport(u_int16_t port, const char *proto) { /*%< Host byte order. */
+ struct valuelist **list = &servicelist;
+ struct valuelist *lp = *list;
+ static struct servent serv;
+
+ port = ntohs(port);
+ for (; lp != NULL; lp = lp->next) {
+ if (port != (u_int16_t)lp->port) /*%< Host byte order. */
+ continue;
+ if (strcasecmp(lp->proto, proto) == 0) {
+ if (lp != *list) {
+ lp->prev->next = lp->next;
+ if (lp->next)
+ lp->next->prev = lp->prev;
+ (*list)->prev = lp;
+ lp->next = *list;
+ *list = lp;
+ }
+ serv.s_name = lp->name;
+ serv.s_port = htons((u_int16_t)lp->port);
+ serv.s_proto = lp->proto;
+ return (&serv);
+ }
+ }
+ return (0);
+}
+
+static struct protoent *
+cgetprotobynumber(int proto) { /*%< Host byte order. */
+ struct valuelist **list = &protolist;
+ struct valuelist *lp = *list;
+ static struct protoent prot;
+
+ for (; lp != NULL; lp = lp->next)
+ if (lp->port == proto) { /*%< Host byte order. */
+ if (lp != *list) {
+ lp->prev->next = lp->next;
+ if (lp->next)
+ lp->next->prev = lp->prev;
+ (*list)->prev = lp;
+ lp->next = *list;
+ *list = lp;
+ }
+ prot.p_name = lp->name;
+ prot.p_proto = lp->port; /*%< Host byte order. */
+ return (&prot);
+ }
+ return (0);
+}
+
+const char *
+res_protocolname(int num) {
+ static char number[8];
+ struct protoent *pp;
+
+ if (protolist == (struct valuelist *)0)
+ res_buildprotolist();
+ pp = cgetprotobynumber(num);
+ if (pp == 0) {
+ (void) sprintf(number, "%d", num);
+ return (number);
+ }
+ return (pp->p_name);
+}
+
+const char *
+res_servicename(u_int16_t port, const char *proto) { /*%< Host byte order. */
+ static char number[8];
+ struct servent *ss;
+
+ if (servicelist == (struct valuelist *)0)
+ res_buildservicelist();
+ ss = cgetservbyport(htons(port), proto);
+ if (ss == 0) {
+ (void) sprintf(number, "%d", port);
+ return (number);
+ }
+ return (ss->s_name);
+}
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.h b/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.h
new file mode 100644
index 0000000000..96c452d89e
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_mkupdate.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _RES_MKUPDATE_H_
+#define _RES_MKUPDATE_H_
+
+__BEGIN_DECLS
+__END_DECLS
+
+#endif /* _RES_MKUPDATE_H_ */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_private.h b/usr/src/lib/libresolv2_joy/common/resolv/res_private.h
new file mode 100644
index 0000000000..4e98157ced
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_private.h
@@ -0,0 +1,22 @@
+#ifndef res_private_h
+#define res_private_h
+
+struct __res_state_ext {
+ union res_sockaddr_union nsaddrs[MAXNS];
+ struct sort_list {
+ int af;
+ union {
+ struct in_addr ina;
+ struct in6_addr in6a;
+ } addr, mask;
+ } sort_list[MAXRESOLVSORT];
+ char nsuffix[64];
+ char nsuffix2[64];
+};
+
+extern int
+res_ourserver_p(const res_state statp, const struct sockaddr *sa);
+
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_query.c b/usr/src/lib/libresolv2_joy/common/resolv/res_query.c
new file mode 100644
index 0000000000..222e4d36ef
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_query.c
@@ -0,0 +1,435 @@
+/*
+ * Portions Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1996-2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 1988, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "port_before.h"
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "port_after.h"
+
+/* Options. Leave them on. */
+#define DEBUG
+
+#if PACKETSZ > 1024
+#define MAXPACKET PACKETSZ
+#else
+#define MAXPACKET 1024
+#endif
+
+/*%
+ * Formulate a normal query, send, and await answer.
+ * Returned answer is placed in supplied buffer "answer".
+ * Perform preliminary check of answer, returning success only
+ * if no error is indicated and the answer count is nonzero.
+ * Return the size of the response on success, -1 on error.
+ * Error number is left in H_ERRNO.
+ *
+ * Caller must parse answer and determine whether it answers the question.
+ */
+int
+res_nquery(res_state statp,
+ const char *name, /*%< domain name */
+ int class, int type, /*%< class and type of query */
+ u_char *answer, /*%< buffer to put answer */
+ int anslen) /*%< size of answer buffer */
+{
+ u_char buf[MAXPACKET];
+ HEADER *hp = (HEADER *) answer;
+ u_int oflags;
+ u_char *rdata;
+ int n;
+
+ oflags = statp->_flags;
+
+again:
+ hp->rcode = NOERROR; /*%< default */
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";; res_query(%s, %d, %d)\n", name, class, type);
+#endif
+
+ n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL,
+ buf, sizeof(buf));
+#ifdef RES_USE_EDNS0
+ if (n > 0 && (statp->_flags & RES_F_EDNS0ERR) == 0 &&
+ (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC|RES_NSID))) {
+ n = res_nopt(statp, n, buf, sizeof(buf), anslen);
+ rdata = &buf[n];
+ if (n > 0 && (statp->options & RES_NSID) != 0U) {
+ n = res_nopt_rdata(statp, n, buf, sizeof(buf), rdata,
+ NS_OPT_NSID, 0, NULL);
+ }
+ }
+#endif
+ if (n <= 0) {
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";; res_query: mkquery failed\n");
+#endif
+ RES_SET_H_ERRNO(statp, NO_RECOVERY);
+ return (n);
+ }
+
+ n = res_nsend(statp, buf, n, answer, anslen);
+ if (n < 0) {
+#ifdef RES_USE_EDNS0
+ /* if the query choked with EDNS0, retry without EDNS0 */
+ if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U &&
+ ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0) {
+ statp->_flags |= RES_F_EDNS0ERR;
+ if (statp->options & RES_DEBUG)
+ printf(";; res_nquery: retry without EDNS0\n");
+ goto again;
+ }
+#endif
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";; res_query: send error\n");
+#endif
+ RES_SET_H_ERRNO(statp, TRY_AGAIN);
+ return (n);
+ }
+
+ if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";; rcode = (%s), counts = an:%d ns:%d ar:%d\n",
+ p_rcode(hp->rcode),
+ ntohs(hp->ancount),
+ ntohs(hp->nscount),
+ ntohs(hp->arcount));
+#endif
+ switch (hp->rcode) {
+ case NXDOMAIN:
+ RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
+ break;
+ case SERVFAIL:
+ RES_SET_H_ERRNO(statp, TRY_AGAIN);
+ break;
+ case NOERROR:
+ RES_SET_H_ERRNO(statp, NO_DATA);
+ break;
+ case FORMERR:
+ case NOTIMP:
+ case REFUSED:
+ default:
+ RES_SET_H_ERRNO(statp, NO_RECOVERY);
+ break;
+ }
+ return (-1);
+ }
+ return (n);
+}
+
+/*%
+ * Formulate a normal query, send, and retrieve answer in supplied buffer.
+ * Return the size of the response on success, -1 on error.
+ * If enabled, implement search rules until answer or unrecoverable failure
+ * is detected. Error code, if any, is left in H_ERRNO.
+ */
+int
+res_nsearch(res_state statp,
+ const char *name, /*%< domain name */
+ int class, int type, /*%< class and type of query */
+ u_char *answer, /*%< buffer to put answer */
+ int anslen) /*%< size of answer */
+{
+ const char *cp, * const *domain;
+ HEADER *hp = (HEADER *) answer;
+ char tmp[NS_MAXDNAME];
+ u_int dots;
+ int trailing_dot, ret, saved_herrno;
+ int got_nodata = 0, got_servfail = 0, root_on_list = 0;
+ int tried_as_is = 0;
+ int searched = 0;
+
+ errno = 0;
+ RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); /*%< True if we never query. */
+ dots = 0;
+ for (cp = name; *cp != '\0'; cp++)
+ dots += (*cp == '.');
+ trailing_dot = 0;
+ if (cp > name && *--cp == '.')
+ trailing_dot++;
+
+ /* If there aren't any dots, it could be a user-level alias. */
+ if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
+ return (res_nquery(statp, cp, class, type, answer, anslen));
+
+ /*
+ * If there are enough dots in the name, let's just give it a
+ * try 'as is'. The threshold can be set with the "ndots" option.
+ * Also, query 'as is', if there is a trailing dot in the name.
+ */
+ saved_herrno = -1;
+ if (dots >= statp->ndots || trailing_dot) {
+ ret = res_nquerydomain(statp, name, NULL, class, type,
+ answer, anslen);
+ if (ret > 0 || trailing_dot)
+ return (ret);
+ saved_herrno = statp->res_h_errno;
+ tried_as_is++;
+ }
+
+ /*
+ * We do at least one level of search if
+ * - there is no dot and RES_DEFNAME is set, or
+ * - there is at least one dot, there is no trailing dot,
+ * and RES_DNSRCH is set.
+ */
+ if ((!dots && (statp->options & RES_DEFNAMES) != 0U) ||
+ (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0U)) {
+ int done = 0;
+
+ for (domain = (const char * const *)statp->dnsrch;
+ *domain && !done;
+ domain++) {
+ searched = 1;
+
+ if (domain[0][0] == '\0' ||
+ (domain[0][0] == '.' && domain[0][1] == '\0'))
+ root_on_list++;
+
+ ret = res_nquerydomain(statp, name, *domain,
+ class, type,
+ answer, anslen);
+ if (ret > 0)
+ return (ret);
+
+ /*
+ * If no server present, give up.
+ * If name isn't found in this domain,
+ * keep trying higher domains in the search list
+ * (if that's enabled).
+ * On a NO_DATA error, keep trying, otherwise
+ * a wildcard entry of another type could keep us
+ * from finding this entry higher in the domain.
+ * If we get some other error (negative answer or
+ * server failure), then stop searching up,
+ * but try the input name below in case it's
+ * fully-qualified.
+ */
+ if (errno == ECONNREFUSED) {
+ RES_SET_H_ERRNO(statp, TRY_AGAIN);
+ return (-1);
+ }
+
+ switch (statp->res_h_errno) {
+ case NO_DATA:
+ got_nodata++;
+ /* FALLTHROUGH */
+ case HOST_NOT_FOUND:
+ /* keep trying */
+ break;
+ case TRY_AGAIN:
+ if (hp->rcode == SERVFAIL) {
+ /* try next search element, if any */
+ got_servfail++;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ /* anything else implies that we're done */
+ done++;
+ }
+
+ /* if we got here for some reason other than DNSRCH,
+ * we only wanted one iteration of the loop, so stop.
+ */
+ if ((statp->options & RES_DNSRCH) == 0U)
+ done++;
+ }
+ }
+
+ /*
+ * If the query has not already been tried as is then try it
+ * unless RES_NOTLDQUERY is set and there were no dots.
+ */
+ if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0U) &&
+ !(tried_as_is || root_on_list)) {
+ ret = res_nquerydomain(statp, name, NULL, class, type,
+ answer, anslen);
+ if (ret > 0)
+ return (ret);
+ }
+
+ /* if we got here, we didn't satisfy the search.
+ * if we did an initial full query, return that query's H_ERRNO
+ * (note that we wouldn't be here if that query had succeeded).
+ * else if we ever got a nodata, send that back as the reason.
+ * else send back meaningless H_ERRNO, that being the one from
+ * the last DNSRCH we did.
+ */
+ if (saved_herrno != -1)
+ RES_SET_H_ERRNO(statp, saved_herrno);
+ else if (got_nodata)
+ RES_SET_H_ERRNO(statp, NO_DATA);
+ else if (got_servfail)
+ RES_SET_H_ERRNO(statp, TRY_AGAIN);
+ return (-1);
+}
+
+/*%
+ * Perform a call on res_query on the concatenation of name and domain,
+ * removing a trailing dot from name if domain is NULL.
+ */
+int
+res_nquerydomain(res_state statp,
+ const char *name,
+ const char *domain,
+ int class, int type, /*%< class and type of query */
+ u_char *answer, /*%< buffer to put answer */
+ int anslen) /*%< size of answer */
+{
+ char nbuf[MAXDNAME];
+ const char *longname = nbuf;
+ int n, d;
+
+#ifdef DEBUG
+ if (statp->options & RES_DEBUG)
+ printf(";; res_nquerydomain(%s, %s, %d, %d)\n",
+ name, domain?domain:"<Nil>", class, type);
+#endif
+ if (domain == NULL) {
+ /*
+ * Check for trailing '.';
+ * copy without '.' if present.
+ */
+ n = strlen(name);
+ if (n >= MAXDNAME) {
+ RES_SET_H_ERRNO(statp, NO_RECOVERY);
+ return (-1);
+ }
+ n--;
+ if (n >= 0 && name[n] == '.') {
+ strncpy(nbuf, name, n);
+ nbuf[n] = '\0';
+ } else
+ longname = name;
+ } else {
+ n = strlen(name);
+ d = strlen(domain);
+ if (n + d + 1 >= MAXDNAME) {
+ RES_SET_H_ERRNO(statp, NO_RECOVERY);
+ return (-1);
+ }
+ sprintf(nbuf, "%s.%s", name, domain);
+ }
+ return (res_nquery(statp, longname, class, type, answer, anslen));
+}
+
+const char *
+res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) {
+ char *file, *cp1, *cp2;
+ char buf[BUFSIZ];
+ FILE *fp;
+
+ if (statp->options & RES_NOALIASES)
+ return (NULL);
+ file = getenv("HOSTALIASES");
+ if (file == NULL || (fp = fopen(file, "r")) == NULL)
+ return (NULL);
+ setbuf(fp, NULL);
+ buf[sizeof(buf) - 1] = '\0';
+ while (fgets(buf, sizeof(buf), fp)) {
+ for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1)
+ ;
+ if (!*cp1)
+ break;
+ *cp1 = '\0';
+ if (ns_samename(buf, name) == 1) {
+ while (isspace((unsigned char)*++cp1))
+ ;
+ if (!*cp1)
+ break;
+ for (cp2 = cp1 + 1; *cp2 &&
+ !isspace((unsigned char)*cp2); ++cp2)
+ ;
+ *cp2 = '\0';
+ strncpy(dst, cp1, siz - 1);
+ dst[siz - 1] = '\0';
+ fclose(fp);
+ return (dst);
+ }
+ }
+ fclose(fp);
+ return (NULL);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_send.c b/usr/src/lib/libresolv2_joy/common/resolv/res_send.c
new file mode 100644
index 0000000000..764e5c6016
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_send.c
@@ -0,0 +1,1115 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Portions Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
+ * Portions Copyright (C) 1996-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 1985, 1989, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*! \file
+ * \brief
+ * Send query to name server and wait for reply.
+ */
+
+#include "port_before.h"
+#include "fd_setsize.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <isc/eventlib.h>
+
+#include "port_after.h"
+
+#ifdef USE_POLL
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+#include <poll.h>
+#endif /* USE_POLL */
+
+/* Options. Leave them on. */
+#define DEBUG
+#include "res_debug.h"
+#include "res_private.h"
+
+#define EXT(res) ((res)->_u._ext)
+
+#ifndef USE_POLL
+static const int highestFD = FD_SETSIZE - 1;
+#else
+static int highestFD = 0;
+#endif
+
+/* Forward. */
+
+static int get_salen __P((const struct sockaddr *));
+static struct sockaddr * get_nsaddr __P((res_state, size_t));
+static int send_vc(res_state, const u_char *, int,
+ u_char *, int, int *, int);
+static int send_dg(res_state, const u_char *, int,
+ u_char *, int, int *, int, int,
+ int *, int *);
+static void Aerror(const res_state, FILE *, const char *, int,
+ const struct sockaddr *, int);
+static void Perror(const res_state, FILE *, const char *, int);
+static int sock_eq(struct sockaddr *, struct sockaddr *);
+#if defined(NEED_PSELECT) && !defined(USE_POLL)
+static int pselect(int, void *, void *, void *,
+ struct timespec *,
+ const sigset_t *);
+#endif
+void res_pquery(const res_state, const u_char *, int, FILE *);
+
+#ifndef ORIGINAL_ISC_CODE
+#pragma weak __res_nameinquery = res_nameinquery
+#pragma weak __res_queriesmatch = res_queriesmatch
+#pragma weak res_nisourserver = res_ourserver_p
+#endif /* ORIGINAL_ISC_CODE */
+
+static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+
+/* Public. */
+
+/*%
+ * looks up "ina" in _res.ns_addr_list[]
+ *
+ * returns:
+ *\li 0 : not found
+ *\li >0 : found
+ *
+ * author:
+ *\li paul vixie, 29may94
+ */
+int
+res_ourserver_p(const res_state statp, const struct sockaddr *sa) {
+ const struct sockaddr_in *inp, *srv;
+ const struct sockaddr_in6 *in6p, *srv6;
+ int ns;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ inp = (const struct sockaddr_in *)sa;
+ for (ns = 0; ns < statp->nscount; ns++) {
+ srv = (struct sockaddr_in *)get_nsaddr(statp, ns);
+ if (srv->sin_family == inp->sin_family &&
+ srv->sin_port == inp->sin_port &&
+ (srv->sin_addr.s_addr == INADDR_ANY ||
+ srv->sin_addr.s_addr == inp->sin_addr.s_addr))
+ return (1);
+ }
+ break;
+ case AF_INET6:
+ if (EXT(statp).ext == NULL)
+ break;
+ in6p = (const struct sockaddr_in6 *)sa;
+ for (ns = 0; ns < statp->nscount; ns++) {
+ srv6 = (struct sockaddr_in6 *)get_nsaddr(statp, ns);
+ if (srv6->sin6_family == in6p->sin6_family &&
+ srv6->sin6_port == in6p->sin6_port &&
+#ifdef HAVE_SIN6_SCOPE_ID
+ (srv6->sin6_scope_id == 0 ||
+ srv6->sin6_scope_id == in6p->sin6_scope_id) &&
+#endif
+ (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) ||
+ IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr, &in6p->sin6_addr)))
+ return (1);
+ }
+ break;
+ default:
+ break;
+ }
+ return (0);
+}
+
+/*%
+ * look for (name,type,class) in the query section of packet (buf,eom)
+ *
+ * requires:
+ *\li buf + HFIXEDSZ <= eom
+ *
+ * returns:
+ *\li -1 : format error
+ *\li 0 : not found
+ *\li >0 : found
+ *
+ * author:
+ *\li paul vixie, 29may94
+ */
+int
+res_nameinquery(const char *name, int type, int class,
+ const u_char *buf, const u_char *eom)
+{
+ const u_char *cp = buf + HFIXEDSZ;
+ int qdcount = ntohs(((const HEADER*)buf)->qdcount);
+
+ while (qdcount-- > 0) {
+ char tname[MAXDNAME+1];
+ int n, ttype, tclass;
+
+ n = dn_expand(buf, eom, cp, tname, sizeof tname);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ if (cp + 2 * INT16SZ > eom)
+ return (-1);
+ ttype = ns_get16(cp); cp += INT16SZ;
+ tclass = ns_get16(cp); cp += INT16SZ;
+ if (ttype == type && tclass == class &&
+ ns_samename(tname, name) == 1)
+ return (1);
+ }
+ return (0);
+}
+
+/*%
+ * is there a 1:1 mapping of (name,type,class)
+ * in (buf1,eom1) and (buf2,eom2)?
+ *
+ * returns:
+ *\li -1 : format error
+ *\li 0 : not a 1:1 mapping
+ *\li >0 : is a 1:1 mapping
+ *
+ * author:
+ *\li paul vixie, 29may94
+ */
+int
+res_queriesmatch(const u_char *buf1, const u_char *eom1,
+ const u_char *buf2, const u_char *eom2)
+{
+ const u_char *cp = buf1 + HFIXEDSZ;
+ int qdcount = ntohs(((const HEADER*)buf1)->qdcount);
+
+ if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2)
+ return (-1);
+
+ /*
+ * Only header section present in replies to
+ * dynamic update packets.
+ */
+ if ((((const HEADER *)buf1)->opcode == ns_o_update) &&
+ (((const HEADER *)buf2)->opcode == ns_o_update))
+ return (1);
+
+ if (qdcount != ntohs(((const HEADER*)buf2)->qdcount))
+ return (0);
+ while (qdcount-- > 0) {
+ char tname[MAXDNAME+1];
+ int n, ttype, tclass;
+
+ n = dn_expand(buf1, eom1, cp, tname, sizeof tname);
+ if (n < 0)
+ return (-1);
+ cp += n;
+ if (cp + 2 * INT16SZ > eom1)
+ return (-1);
+ ttype = ns_get16(cp); cp += INT16SZ;
+ tclass = ns_get16(cp); cp += INT16SZ;
+ if (!res_nameinquery(tname, ttype, tclass, buf2, eom2))
+ return (0);
+ }
+ return (1);
+}
+
+int
+res_nsend(res_state statp,
+ const u_char *buf, int buflen, u_char *ans, int anssiz)
+{
+ int gotsomewhere, terrno, tries, v_circuit, resplen, ns, n;
+ char abuf[NI_MAXHOST];
+
+#ifdef USE_POLL
+ highestFD = sysconf(_SC_OPEN_MAX) - 1;
+#endif
+
+ /* No name servers or res_init() failure */
+ if (statp->nscount == 0 || EXT(statp).ext == NULL) {
+ errno = ESRCH;
+ return (-1);
+ }
+ if (anssiz < HFIXEDSZ) {
+ errno = EINVAL;
+ return (-1);
+ }
+ DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY),
+ (stdout, ";; res_send()\n"), buf, buflen);
+ v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ;
+ gotsomewhere = 0;
+ terrno = ETIMEDOUT;
+
+ /*
+ * If the ns_addr_list in the resolver context has changed, then
+ * invalidate our cached copy and the associated timing data.
+ */
+ if (EXT(statp).nscount != 0) {
+ int needclose = 0;
+ struct sockaddr_storage peer;
+ ISC_SOCKLEN_T peerlen;
+
+ if (EXT(statp).nscount != statp->nscount)
+ needclose++;
+ else
+ for (ns = 0; ns < statp->nscount; ns++) {
+ if (statp->nsaddr_list[ns].sin_family &&
+ !sock_eq((struct sockaddr *)&statp->nsaddr_list[ns],
+ (struct sockaddr *)&EXT(statp).ext->nsaddrs[ns])) {
+ needclose++;
+ break;
+ }
+
+ if (EXT(statp).nssocks[ns] == -1)
+ continue;
+ peerlen = sizeof(peer);
+ if (getpeername(EXT(statp).nssocks[ns],
+ (struct sockaddr *)&peer, &peerlen) < 0) {
+ needclose++;
+ break;
+ }
+ if (!sock_eq((struct sockaddr *)&peer,
+ get_nsaddr(statp, ns))) {
+ needclose++;
+ break;
+ }
+ }
+ if (needclose) {
+ res_nclose(statp);
+ EXT(statp).nscount = 0;
+ }
+ }
+
+ /*
+ * Maybe initialize our private copy of the ns_addr_list.
+ */
+ if (EXT(statp).nscount == 0) {
+ for (ns = 0; ns < statp->nscount; ns++) {
+ EXT(statp).nstimes[ns] = RES_MAXTIME;
+ EXT(statp).nssocks[ns] = -1;
+ if (!statp->nsaddr_list[ns].sin_family)
+ continue;
+ EXT(statp).ext->nsaddrs[ns].sin =
+ statp->nsaddr_list[ns];
+ }
+ EXT(statp).nscount = statp->nscount;
+ }
+
+ /*
+ * Some resolvers want to even out the load on their nameservers.
+ * Note that RES_BLAST overrides RES_ROTATE.
+ */
+ if ((statp->options & RES_ROTATE) != 0U &&
+ (statp->options & RES_BLAST) == 0U) {
+ union res_sockaddr_union inu;
+ struct sockaddr_in ina;
+ int lastns = statp->nscount - 1;
+ int fd;
+ u_int16_t nstime;
+
+ if (EXT(statp).ext != NULL)
+ inu = EXT(statp).ext->nsaddrs[0];
+ ina = statp->nsaddr_list[0];
+ fd = EXT(statp).nssocks[0];
+ nstime = EXT(statp).nstimes[0];
+ for (ns = 0; ns < lastns; ns++) {
+ if (EXT(statp).ext != NULL)
+ EXT(statp).ext->nsaddrs[ns] =
+ EXT(statp).ext->nsaddrs[ns + 1];
+ statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1];
+ EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1];
+ EXT(statp).nstimes[ns] = EXT(statp).nstimes[ns + 1];
+ }
+ if (EXT(statp).ext != NULL)
+ EXT(statp).ext->nsaddrs[lastns] = inu;
+ statp->nsaddr_list[lastns] = ina;
+ EXT(statp).nssocks[lastns] = fd;
+ EXT(statp).nstimes[lastns] = nstime;
+ }
+
+ /*
+ * Send request, RETRY times, or until successful.
+ */
+ for (tries = 0; tries < statp->retry; tries++) {
+ for (ns = 0; ns < statp->nscount; ns++) {
+ struct sockaddr *nsap;
+ int nsaplen;
+ nsap = get_nsaddr(statp, ns);
+ nsaplen = get_salen(nsap);
+ statp->_flags &= ~RES_F_LASTMASK;
+ statp->_flags |= (ns << RES_F_LASTSHIFT);
+ same_ns:
+ if (statp->qhook) {
+ int done = 0, loops = 0;
+
+ do {
+ res_sendhookact act;
+
+ act = (*statp->qhook)(&nsap, &buf, &buflen,
+ ans, anssiz, &resplen);
+ switch (act) {
+ case res_goahead:
+ done = 1;
+ break;
+ case res_nextns:
+ res_nclose(statp);
+ goto next_ns;
+ case res_done:
+ return (resplen);
+ case res_modified:
+ /* give the hook another try */
+ if (++loops < 42) /*doug adams*/
+ break;
+ /*FALLTHROUGH*/
+ case res_error:
+ /*FALLTHROUGH*/
+ default:
+ goto fail;
+ }
+ } while (!done);
+ }
+
+ Dprint(((statp->options & RES_DEBUG) &&
+ getnameinfo(nsap, nsaplen, abuf, sizeof(abuf),
+ NULL, 0, niflags) == 0),
+ (stdout, ";; Querying server (# %d) address = %s\n",
+ ns + 1, abuf));
+
+
+ if (v_circuit) {
+ /* Use VC; at most one attempt per server. */
+ tries = statp->retry;
+ n = send_vc(statp, buf, buflen, ans, anssiz, &terrno,
+ ns);
+ if (n < 0)
+ goto fail;
+ if (n == 0)
+ goto next_ns;
+ resplen = n;
+ } else {
+ /* Use datagrams. */
+ n = send_dg(statp, buf, buflen, ans, anssiz, &terrno,
+ ns, tries, &v_circuit, &gotsomewhere);
+ if (n < 0)
+ goto fail;
+ if (n == 0)
+ goto next_ns;
+ if (v_circuit)
+ goto same_ns;
+ resplen = n;
+ }
+
+ Dprint((statp->options & RES_DEBUG) ||
+ ((statp->pfcode & RES_PRF_REPLY) &&
+ (statp->pfcode & RES_PRF_HEAD1)),
+ (stdout, ";; got answer:\n"));
+
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, "%s", ""),
+ ans, (resplen > anssiz) ? anssiz : resplen);
+
+ /*
+ * If we have temporarily opened a virtual circuit,
+ * or if we haven't been asked to keep a socket open,
+ * close the socket.
+ */
+ if ((v_circuit && (statp->options & RES_USEVC) == 0U) ||
+ (statp->options & RES_STAYOPEN) == 0U) {
+ res_nclose(statp);
+ }
+ if (statp->rhook) {
+ int done = 0, loops = 0;
+
+ do {
+ res_sendhookact act;
+
+ act = (*statp->rhook)(nsap, buf, buflen,
+ ans, anssiz, &resplen);
+ switch (act) {
+ case res_goahead:
+ case res_done:
+ done = 1;
+ break;
+ case res_nextns:
+ res_nclose(statp);
+ goto next_ns;
+ case res_modified:
+ /* give the hook another try */
+ if (++loops < 42) /*doug adams*/
+ break;
+ /*FALLTHROUGH*/
+ case res_error:
+ /*FALLTHROUGH*/
+ default:
+ goto fail;
+ }
+ } while (!done);
+
+ }
+ return (resplen);
+ next_ns: ;
+ } /*foreach ns*/
+ } /*foreach retry*/
+ res_nclose(statp);
+ if (!v_circuit) {
+ if (!gotsomewhere)
+ errno = ECONNREFUSED; /*%< no nameservers found */
+ else
+ errno = ETIMEDOUT; /*%< no answer obtained */
+ } else
+ errno = terrno;
+ return (-1);
+ fail:
+ res_nclose(statp);
+ return (-1);
+}
+
+/* Private */
+
+static int
+get_salen(sa)
+ const struct sockaddr *sa;
+{
+
+#ifdef HAVE_SA_LEN
+ /* There are people do not set sa_len. Be forgiving to them. */
+ if (sa->sa_len)
+ return (sa->sa_len);
+#endif
+
+ if (sa->sa_family == AF_INET)
+ return (sizeof(struct sockaddr_in));
+ else if (sa->sa_family == AF_INET6)
+ return (sizeof(struct sockaddr_in6));
+ else
+ return (0); /*%< unknown, die on connect */
+}
+
+/*%
+ * pick appropriate nsaddr_list for use. see res_init() for initialization.
+ */
+static struct sockaddr *
+get_nsaddr(statp, n)
+ res_state statp;
+ size_t n;
+{
+
+ if (!statp->nsaddr_list[n].sin_family && EXT(statp).ext) {
+ /*
+ * - EXT(statp).ext->nsaddrs[n] holds an address that is larger
+ * than struct sockaddr, and
+ * - user code did not update statp->nsaddr_list[n].
+ */
+ return (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[n];
+ } else {
+ /*
+ * - user code updated statp->nsaddr_list[n], or
+ * - statp->nsaddr_list[n] has the same content as
+ * EXT(statp).ext->nsaddrs[n].
+ */
+ return (struct sockaddr *)(void *)&statp->nsaddr_list[n];
+ }
+}
+
+static int
+send_vc(res_state statp,
+ const u_char *buf, int buflen, u_char *ans, int anssiz,
+ int *terrno, int ns)
+{
+ const HEADER *hp = (const HEADER *) buf;
+ HEADER *anhp = (HEADER *) ans;
+ struct sockaddr *nsap;
+ int nsaplen;
+ int truncating, connreset, resplen, n;
+ struct iovec iov[2];
+ u_short len;
+ u_char *cp;
+ void *tmp;
+#ifdef SO_NOSIGPIPE
+ int on = 1;
+#endif
+
+ nsap = get_nsaddr(statp, ns);
+ nsaplen = get_salen(nsap);
+
+ connreset = 0;
+ same_ns:
+ truncating = 0;
+
+ /* Are we still talking to whom we want to talk to? */
+ if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) {
+ struct sockaddr_storage peer;
+ ISC_SOCKLEN_T size = sizeof peer;
+
+ if (getpeername(statp->_vcsock,
+ (struct sockaddr *)&peer, &size) < 0 ||
+ !sock_eq((struct sockaddr *)&peer, nsap)) {
+ res_nclose(statp);
+ statp->_flags &= ~RES_F_VC;
+ }
+ }
+
+ if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) {
+ if (statp->_vcsock >= 0)
+ res_nclose(statp);
+
+ statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM, 0);
+ if (statp->_vcsock > highestFD) {
+ res_nclose(statp);
+ errno = ENOTSOCK;
+ }
+ if (statp->_vcsock < 0) {
+ switch (errno) {
+ case EPROTONOSUPPORT:
+#ifdef EPFNOSUPPORT
+ case EPFNOSUPPORT:
+#endif
+ case EAFNOSUPPORT:
+ Perror(statp, stderr, "socket(vc)", errno);
+ return (0);
+ default:
+ *terrno = errno;
+ Perror(statp, stderr, "socket(vc)", errno);
+ return (-1);
+ }
+ }
+#ifdef SO_NOSIGPIPE
+ /*
+ * Disable generation of SIGPIPE when writing to a closed
+ * socket. Write should return -1 and set errno to EPIPE
+ * instead.
+ *
+ * Push on even if setsockopt(SO_NOSIGPIPE) fails.
+ */
+ (void)setsockopt(statp->_vcsock, SOL_SOCKET, SO_NOSIGPIPE, &on,
+ sizeof(on));
+#endif
+ errno = 0;
+ if (connect(statp->_vcsock, nsap, nsaplen) < 0) {
+ *terrno = errno;
+ Aerror(statp, stderr, "connect/vc", errno, nsap,
+ nsaplen);
+ res_nclose(statp);
+ return (0);
+ }
+ statp->_flags |= RES_F_VC;
+ }
+
+ /*
+ * Send length & message
+ */
+ ns_put16((u_short)buflen, (u_char*)&len);
+ iov[0] = evConsIovec(&len, INT16SZ);
+ DE_CONST(buf, tmp);
+ iov[1] = evConsIovec(tmp, buflen);
+ if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) {
+ *terrno = errno;
+ Perror(statp, stderr, "write failed", errno);
+ res_nclose(statp);
+ return (0);
+ }
+ /*
+ * Receive length & response
+ */
+ read_len:
+ cp = ans;
+ len = INT16SZ;
+ while ((n = read(statp->_vcsock, (char *)cp, (int)len)) > 0) {
+ cp += n;
+ if ((len -= n) == 0)
+ break;
+ }
+ if (n <= 0) {
+ *terrno = errno;
+ Perror(statp, stderr, "read failed", errno);
+ res_nclose(statp);
+ /*
+ * A long running process might get its TCP
+ * connection reset if the remote server was
+ * restarted. Requery the server instead of
+ * trying a new one. When there is only one
+ * server, this means that a query might work
+ * instead of failing. We only allow one reset
+ * per query to prevent looping.
+ */
+ if (*terrno == ECONNRESET && !connreset) {
+ connreset = 1;
+ res_nclose(statp);
+ goto same_ns;
+ }
+ res_nclose(statp);
+ return (0);
+ }
+ resplen = ns_get16(ans);
+ if (resplen > anssiz) {
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; response truncated\n")
+ );
+ truncating = 1;
+ len = anssiz;
+ } else
+ len = resplen;
+ if (len < HFIXEDSZ) {
+ /*
+ * Undersized message.
+ */
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; undersized: %d\n", len));
+ *terrno = EMSGSIZE;
+ res_nclose(statp);
+ return (0);
+ }
+ cp = ans;
+ while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){
+ cp += n;
+ len -= n;
+ }
+ if (n <= 0) {
+ *terrno = errno;
+ Perror(statp, stderr, "read(vc)", errno);
+ res_nclose(statp);
+ return (0);
+ }
+ if (truncating) {
+ /*
+ * Flush rest of answer so connection stays in synch.
+ */
+ anhp->tc = 1;
+ len = resplen - anssiz;
+ while (len != 0) {
+ char junk[PACKETSZ];
+
+ n = read(statp->_vcsock, junk,
+ (len > sizeof junk) ? sizeof junk : len);
+ if (n > 0)
+ len -= n;
+ else
+ break;
+ }
+ }
+ /*
+ * If the calling applicating has bailed out of
+ * a previous call and failed to arrange to have
+ * the circuit closed or the server has got
+ * itself confused, then drop the packet and
+ * wait for the correct one.
+ */
+ if (hp->id != anhp->id) {
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, ";; old answer (unexpected):\n"),
+ ans, (resplen > anssiz) ? anssiz: resplen);
+ goto read_len;
+ }
+
+ /*
+ * All is well, or the error is fatal. Signal that the
+ * next nameserver ought not be tried.
+ */
+ return (resplen);
+}
+
+static int
+send_dg(res_state statp, const u_char *buf, int buflen, u_char *ans,
+ int anssiz, int *terrno, int ns, int tries, int *v_circuit,
+ int *gotsomewhere)
+{
+ const HEADER *hp = (const HEADER *) buf;
+ HEADER *anhp = (HEADER *) ans;
+ const struct sockaddr *nsap;
+ int nsaplen;
+ struct timespec now, timeout, finish;
+ struct sockaddr_storage from;
+ ISC_SOCKLEN_T fromlen;
+ int resplen, seconds, n, s;
+#ifdef USE_POLL
+ int polltimeout;
+ struct pollfd pollfd;
+#else
+ fd_set dsmask;
+#endif
+
+ nsap = get_nsaddr(statp, ns);
+ nsaplen = get_salen(nsap);
+ if (EXT(statp).nssocks[ns] == -1) {
+ EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM, 0);
+ if (EXT(statp).nssocks[ns] > highestFD) {
+ res_nclose(statp);
+ errno = ENOTSOCK;
+ }
+ if (EXT(statp).nssocks[ns] < 0) {
+ switch (errno) {
+ case EPROTONOSUPPORT:
+#ifdef EPFNOSUPPORT
+ case EPFNOSUPPORT:
+#endif
+ case EAFNOSUPPORT:
+ Perror(statp, stderr, "socket(dg)", errno);
+ return (0);
+ default:
+ *terrno = errno;
+ Perror(statp, stderr, "socket(dg)", errno);
+ return (-1);
+ }
+ }
+#ifndef CANNOT_CONNECT_DGRAM
+ /*
+ * On a 4.3BSD+ machine (client and server,
+ * actually), sending to a nameserver datagram
+ * port with no nameserver will cause an
+ * ICMP port unreachable message to be returned.
+ * If our datagram socket is "connected" to the
+ * server, we get an ECONNREFUSED error on the next
+ * socket operation, and select returns if the
+ * error message is received. We can thus detect
+ * the absence of a nameserver without timing out.
+ */
+ if (connect(EXT(statp).nssocks[ns], nsap, nsaplen) < 0) {
+ Aerror(statp, stderr, "connect(dg)", errno, nsap,
+ nsaplen);
+ res_nclose(statp);
+ return (0);
+ }
+#endif /* !CANNOT_CONNECT_DGRAM */
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; new DG socket\n"))
+ }
+ s = EXT(statp).nssocks[ns];
+#ifndef CANNOT_CONNECT_DGRAM
+ if (send(s, (const char*)buf, buflen, 0) != buflen) {
+ Perror(statp, stderr, "send", errno);
+ res_nclose(statp);
+ return (0);
+ }
+#else /* !CANNOT_CONNECT_DGRAM */
+ if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen)
+ {
+ Aerror(statp, stderr, "sendto", errno, nsap, nsaplen);
+ res_nclose(statp);
+ return (0);
+ }
+#endif /* !CANNOT_CONNECT_DGRAM */
+
+ /*
+ * Wait for reply.
+ */
+ seconds = (statp->retrans << tries);
+ if (ns > 0)
+ seconds /= statp->nscount;
+ if (seconds <= 0)
+ seconds = 1;
+ now = evNowTime();
+ timeout = evConsTime(seconds, 0);
+ finish = evAddTime(now, timeout);
+ goto nonow;
+ wait:
+ now = evNowTime();
+ nonow:
+#ifndef USE_POLL
+ FD_ZERO(&dsmask);
+ FD_SET(s, &dsmask);
+ if (evCmpTime(finish, now) > 0)
+ timeout = evSubTime(finish, now);
+ else
+ timeout = evConsTime(0, 0);
+ n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL);
+#else
+ timeout = evSubTime(finish, now);
+ if (timeout.tv_sec < 0)
+ timeout = evConsTime(0, 0);
+ polltimeout = 1000*timeout.tv_sec +
+ timeout.tv_nsec/1000000;
+ pollfd.fd = s;
+ pollfd.events = POLLRDNORM;
+ n = poll(&pollfd, 1, polltimeout);
+#endif /* USE_POLL */
+
+ if (n == 0) {
+ Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n"));
+ *gotsomewhere = 1;
+ return (0);
+ }
+ if (n < 0) {
+ if (errno == EINTR)
+ goto wait;
+#ifndef USE_POLL
+ Perror(statp, stderr, "select", errno);
+#else
+ Perror(statp, stderr, "poll", errno);
+#endif /* USE_POLL */
+ res_nclose(statp);
+ return (0);
+ }
+ errno = 0;
+ fromlen = sizeof(from);
+ resplen = recvfrom(s, (char*)ans, anssiz,0,
+ (struct sockaddr *)&from, &fromlen);
+ if (resplen <= 0) {
+ Perror(statp, stderr, "recvfrom", errno);
+ res_nclose(statp);
+ return (0);
+ }
+ *gotsomewhere = 1;
+ if (resplen < HFIXEDSZ) {
+ /*
+ * Undersized message.
+ */
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; undersized: %d\n",
+ resplen));
+ *terrno = EMSGSIZE;
+ res_nclose(statp);
+ return (0);
+ }
+ if (hp->id != anhp->id) {
+ /*
+ * response from old query, ignore it.
+ * XXX - potential security hazard could
+ * be detected here.
+ */
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, ";; old answer:\n"),
+ ans, (resplen > anssiz) ? anssiz : resplen);
+ goto wait;
+ }
+ if (!(statp->options & RES_INSECURE1) &&
+ !res_ourserver_p(statp, (struct sockaddr *)&from)) {
+ /*
+ * response from wrong server? ignore it.
+ * XXX - potential security hazard could
+ * be detected here.
+ */
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, ";; not our server:\n"),
+ ans, (resplen > anssiz) ? anssiz : resplen);
+ goto wait;
+ }
+#ifdef RES_USE_EDNS0
+ if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) {
+ /*
+ * Do not retry if the server do not understand EDNS0.
+ * The case has to be captured here, as FORMERR packet do not
+ * carry query section, hence res_queriesmatch() returns 0.
+ */
+ DprintQ(statp->options & RES_DEBUG,
+ (stdout, "server rejected query with EDNS0:\n"),
+ ans, (resplen > anssiz) ? anssiz : resplen);
+ /* record the error */
+ statp->_flags |= RES_F_EDNS0ERR;
+ res_nclose(statp);
+ return (0);
+ }
+#endif
+ if (!(statp->options & RES_INSECURE2) &&
+ !res_queriesmatch(buf, buf + buflen,
+ ans, ans + anssiz)) {
+ /*
+ * response contains wrong query? ignore it.
+ * XXX - potential security hazard could
+ * be detected here.
+ */
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, ";; wrong query name:\n"),
+ ans, (resplen > anssiz) ? anssiz : resplen);
+ goto wait;
+ }
+ if (anhp->rcode == SERVFAIL ||
+ anhp->rcode == NOTIMP ||
+ anhp->rcode == REFUSED) {
+ DprintQ(statp->options & RES_DEBUG,
+ (stdout, "server rejected query:\n"),
+ ans, (resplen > anssiz) ? anssiz : resplen);
+ res_nclose(statp);
+ /* don't retry if called from dig */
+ if (!statp->pfcode)
+ return (0);
+ }
+ if (!(statp->options & RES_IGNTC) && anhp->tc) {
+ /*
+ * To get the rest of answer,
+ * use TCP with same server.
+ */
+ Dprint(statp->options & RES_DEBUG,
+ (stdout, ";; truncated answer\n"));
+ *v_circuit = 1;
+ res_nclose(statp);
+ return (1);
+ }
+ /*
+ * All is well, or the error is fatal. Signal that the
+ * next nameserver ought not be tried.
+ */
+ return (resplen);
+}
+
+static void
+Aerror(const res_state statp, FILE *file, const char *string, int error,
+ const struct sockaddr *address, int alen)
+{
+ int save = errno;
+ char hbuf[NI_MAXHOST];
+ char sbuf[NI_MAXSERV];
+
+ alen = alen;
+
+ if ((statp->options & RES_DEBUG) != 0U) {
+ if (getnameinfo(address, alen, hbuf, sizeof(hbuf),
+ sbuf, sizeof(sbuf), niflags)) {
+ strncpy(hbuf, "?", sizeof(hbuf) - 1);
+ hbuf[sizeof(hbuf) - 1] = '\0';
+ strncpy(sbuf, "?", sizeof(sbuf) - 1);
+ sbuf[sizeof(sbuf) - 1] = '\0';
+ }
+ fprintf(file, "res_send: %s ([%s].%s): %s\n",
+ string, hbuf, sbuf, strerror(error));
+ }
+ errno = save;
+}
+
+static void
+Perror(const res_state statp, FILE *file, const char *string, int error) {
+ int save = errno;
+
+ if ((statp->options & RES_DEBUG) != 0U)
+ fprintf(file, "res_send: %s: %s\n",
+ string, strerror(error));
+ errno = save;
+}
+
+static int
+sock_eq(struct sockaddr *a, struct sockaddr *b) {
+ struct sockaddr_in *a4, *b4;
+ struct sockaddr_in6 *a6, *b6;
+
+ if (a->sa_family != b->sa_family)
+ return 0;
+ switch (a->sa_family) {
+ case AF_INET:
+ a4 = (struct sockaddr_in *)a;
+ b4 = (struct sockaddr_in *)b;
+ return a4->sin_port == b4->sin_port &&
+ a4->sin_addr.s_addr == b4->sin_addr.s_addr;
+ case AF_INET6:
+ a6 = (struct sockaddr_in6 *)a;
+ b6 = (struct sockaddr_in6 *)b;
+ return a6->sin6_port == b6->sin6_port &&
+#ifdef HAVE_SIN6_SCOPE_ID
+ a6->sin6_scope_id == b6->sin6_scope_id &&
+#endif
+ IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr);
+ default:
+ return 0;
+ }
+}
+
+#if defined(NEED_PSELECT) && !defined(USE_POLL)
+/* XXX needs to move to the porting library. */
+static int
+pselect(int nfds, void *rfds, void *wfds, void *efds,
+ struct timespec *tsp, const sigset_t *sigmask)
+{
+ struct timeval tv, *tvp;
+ sigset_t sigs;
+ int n;
+
+ if (tsp) {
+ tvp = &tv;
+ tv = evTimeVal(*tsp);
+ } else
+ tvp = NULL;
+ if (sigmask)
+ sigprocmask(SIG_SETMASK, sigmask, &sigs);
+ n = select(nfds, rfds, wfds, efds, tvp);
+ if (sigmask)
+ sigprocmask(SIG_SETMASK, &sigs, NULL);
+ if (tsp)
+ *tsp = evTimeSpec(tv);
+ return (n);
+}
+#endif
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_sendsigned.c b/usr/src/lib/libresolv2_joy/common/resolv/res_sendsigned.c
new file mode 100644
index 0000000000..5ebc1a70eb
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_sendsigned.c
@@ -0,0 +1,170 @@
+#include "port_before.h"
+#include "fd_setsize.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+
+#include <isc/dst.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "port_after.h"
+
+#define DEBUG
+#include "res_debug.h"
+
+
+/*% res_nsendsigned */
+int
+res_nsendsigned(res_state statp, const u_char *msg, int msglen,
+ ns_tsig_key *key, u_char *answer, int anslen)
+{
+ res_state nstatp;
+ DST_KEY *dstkey;
+ int usingTCP = 0;
+ u_char *newmsg;
+ int newmsglen, bufsize, siglen;
+ u_char sig[64];
+ HEADER *hp;
+ time_t tsig_time;
+ int ret;
+ int len;
+
+ dst_init();
+
+ nstatp = (res_state) malloc(sizeof(*statp));
+ if (nstatp == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ memcpy(nstatp, statp, sizeof(*statp));
+
+ bufsize = msglen + 1024;
+ newmsg = (u_char *) malloc(bufsize);
+ if (newmsg == NULL) {
+ free(nstatp);
+ errno = ENOMEM;
+ return (-1);
+ }
+ memcpy(newmsg, msg, msglen);
+ newmsglen = msglen;
+
+ if (ns_samename(key->alg, NS_TSIG_ALG_HMAC_MD5) != 1)
+ dstkey = NULL;
+ else
+ dstkey = dst_buffer_to_key(key->name, KEY_HMAC_MD5,
+ NS_KEY_TYPE_AUTH_ONLY,
+ NS_KEY_PROT_ANY,
+ key->data, key->len);
+ if (dstkey == NULL) {
+ errno = EINVAL;
+ free(nstatp);
+ free(newmsg);
+ return (-1);
+ }
+
+ nstatp->nscount = 1;
+ siglen = sizeof(sig);
+ ret = ns_sign(newmsg, &newmsglen, bufsize, NOERROR, dstkey, NULL, 0,
+ sig, &siglen, 0);
+ if (ret < 0) {
+ free (nstatp);
+ free (newmsg);
+ dst_free_key(dstkey);
+ if (ret == NS_TSIG_ERROR_NO_SPACE)
+ errno = EMSGSIZE;
+ else if (ret == -1)
+ errno = EINVAL;
+ return (ret);
+ }
+
+ if (newmsglen > PACKETSZ || nstatp->options & RES_USEVC)
+ usingTCP = 1;
+ if (usingTCP == 0)
+ nstatp->options |= RES_IGNTC;
+ else
+ nstatp->options |= RES_USEVC;
+ /*
+ * Stop res_send printing the answer.
+ */
+ nstatp->options &= ~RES_DEBUG;
+ nstatp->pfcode &= ~RES_PRF_REPLY;
+
+retry:
+
+ len = res_nsend(nstatp, newmsg, newmsglen, answer, anslen);
+ if (len < 0) {
+ free (nstatp);
+ free (newmsg);
+ dst_free_key(dstkey);
+ return (len);
+ }
+
+ ret = ns_verify(answer, &len, dstkey, sig, siglen,
+ NULL, NULL, &tsig_time, nstatp->options & RES_KEEPTSIG);
+ if (ret != 0) {
+ Dprint((statp->options & RES_DEBUG) ||
+ ((statp->pfcode & RES_PRF_REPLY) &&
+ (statp->pfcode & RES_PRF_HEAD1)),
+ (stdout, ";; got answer:\n"));
+
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, "%s", ""),
+ answer, (anslen > len) ? len : anslen);
+
+ if (ret > 0) {
+ Dprint(statp->pfcode & RES_PRF_REPLY,
+ (stdout, ";; server rejected TSIG (%s)\n",
+ p_rcode(ret)));
+ } else {
+ Dprint(statp->pfcode & RES_PRF_REPLY,
+ (stdout, ";; TSIG invalid (%s)\n",
+ p_rcode(-ret)));
+ }
+
+ free (nstatp);
+ free (newmsg);
+ dst_free_key(dstkey);
+ if (ret == -1)
+ errno = EINVAL;
+ else
+ errno = ENOTTY;
+ return (-1);
+ }
+
+ hp = (HEADER *) answer;
+ if (hp->tc && !usingTCP && (statp->options & RES_IGNTC) == 0U) {
+ nstatp->options &= ~RES_IGNTC;
+ usingTCP = 1;
+ goto retry;
+ }
+ Dprint((statp->options & RES_DEBUG) ||
+ ((statp->pfcode & RES_PRF_REPLY) &&
+ (statp->pfcode & RES_PRF_HEAD1)),
+ (stdout, ";; got answer:\n"));
+
+ DprintQ((statp->options & RES_DEBUG) ||
+ (statp->pfcode & RES_PRF_REPLY),
+ (stdout, "%s", ""),
+ answer, (anslen > len) ? len : anslen);
+
+ Dprint(statp->pfcode & RES_PRF_REPLY, (stdout, ";; TSIG ok\n"));
+
+ free (nstatp);
+ free (newmsg);
+ dst_free_key(dstkey);
+ return (len);
+}
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/common/resolv/res_update.c b/usr/src/lib/libresolv2_joy/common/resolv/res_update.c
new file mode 100644
index 0000000000..bf95aef48d
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/resolv/res_update.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*! \file
+ * \brief
+ * Based on the Dynamic DNS reference implementation by Viraj Bais
+ * &lt;viraj_bais@ccm.fm.intel.com>
+ */
+
+#include "port_before.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <res_update.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/list.h>
+#include <resolv_joy.h>
+
+#include "port_after.h"
+#include "res_private.h"
+
+/*%
+ * Separate a linked list of records into groups so that all records
+ * in a group will belong to a single zone on the nameserver.
+ * Create a dynamic update packet for each zone and send it to the
+ * nameservers for that zone, and await answer.
+ * Abort if error occurs in updating any zone.
+ * Return the number of zones updated on success, < 0 on error.
+ *
+ * On error, caller must deal with the unsynchronized zones
+ * eg. an A record might have been successfully added to the forward
+ * zone but the corresponding PTR record would be missing if error
+ * was encountered while updating the reverse zone.
+ */
+
+struct zonegrp {
+ char z_origin[MAXDNAME];
+ ns_class z_class;
+ union res_sockaddr_union z_nsaddrs[MAXNS];
+ int z_nscount;
+ int z_flags;
+ LIST(ns_updrec) z_rrlist;
+ LINK(struct zonegrp) z_link;
+};
+
+#define ZG_F_ZONESECTADDED 0x0001
+
+/* Forward. */
+
+static void res_dprintf(const char *, ...) ISC_FORMAT_PRINTF(1, 2);
+
+/* Macros. */
+
+#define DPRINTF(x) do {\
+ int save_errno = errno; \
+ if ((statp->options & RES_DEBUG) != 0U) res_dprintf x; \
+ errno = save_errno; \
+ } while (0)
+
+/* Public. */
+
+int
+res_nupdate(res_state statp, ns_updrec *rrecp_in, ns_tsig_key *key) {
+ ns_updrec *rrecp;
+ u_char answer[PACKETSZ];
+ u_char *packet;
+ struct zonegrp *zptr, tgrp;
+ LIST(struct zonegrp) zgrps;
+ int nzones = 0, nscount = 0, n;
+ union res_sockaddr_union nsaddrs[MAXNS];
+
+ packet = malloc(NS_MAXMSG);
+ if (packet == NULL) {
+ DPRINTF(("malloc failed"));
+ return (0);
+ }
+ /* Thread all of the updates onto a list of groups. */
+ INIT_LIST(zgrps);
+ memset(&tgrp, 0, sizeof (tgrp));
+ for (rrecp = rrecp_in; rrecp;
+ rrecp = LINKED(rrecp, r_link) ? NEXT(rrecp, r_link) : NULL) {
+ int nscnt;
+ /* Find the origin for it if there is one. */
+ tgrp.z_class = rrecp->r_class;
+ nscnt = res_findzonecut2(statp, rrecp->r_dname, tgrp.z_class,
+ RES_EXHAUSTIVE, tgrp.z_origin,
+ sizeof tgrp.z_origin,
+ tgrp.z_nsaddrs, MAXNS);
+ if (nscnt <= 0) {
+ DPRINTF(("res_findzonecut failed (%d)", nscnt));
+ goto done;
+ }
+ tgrp.z_nscount = nscnt;
+ /* Find the group for it if there is one. */
+ for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link))
+ if (ns_samename(tgrp.z_origin, zptr->z_origin) == 1 &&
+ tgrp.z_class == zptr->z_class)
+ break;
+ /* Make a group for it if there isn't one. */
+ if (zptr == NULL) {
+ zptr = malloc(sizeof *zptr);
+ if (zptr == NULL) {
+ DPRINTF(("malloc failed"));
+ goto done;
+ }
+ *zptr = tgrp;
+ zptr->z_flags = 0;
+ INIT_LINK(zptr, z_link);
+ INIT_LIST(zptr->z_rrlist);
+ APPEND(zgrps, zptr, z_link);
+ }
+ /* Thread this rrecp onto the right group. */
+ APPEND(zptr->z_rrlist, rrecp, r_glink);
+ }
+
+ for (zptr = HEAD(zgrps); zptr != NULL; zptr = NEXT(zptr, z_link)) {
+ /* Construct zone section and prepend it. */
+ rrecp = res_mkupdrec(ns_s_zn, zptr->z_origin,
+ zptr->z_class, ns_t_soa, 0);
+ if (rrecp == NULL) {
+ DPRINTF(("res_mkupdrec failed"));
+ goto done;
+ }
+ PREPEND(zptr->z_rrlist, rrecp, r_glink);
+ zptr->z_flags |= ZG_F_ZONESECTADDED;
+
+ /* Marshall the update message. */
+ n = res_nmkupdate(statp, HEAD(zptr->z_rrlist),
+ packet, NS_MAXMSG);
+ DPRINTF(("res_mkupdate -> %d", n));
+ if (n < 0)
+ goto done;
+
+ /* Temporarily replace the resolver's nameserver set. */
+ nscount = res_getservers(statp, nsaddrs, MAXNS);
+ res_setservers(statp, zptr->z_nsaddrs, zptr->z_nscount);
+
+ /* Send the update and remember the result. */
+ if (key != NULL)
+ n = res_nsendsigned(statp, packet, n, key,
+ answer, sizeof answer);
+ else
+ n = res_nsend(statp, packet, n, answer, sizeof answer);
+ if (n < 0) {
+ DPRINTF(("res_nsend: send error, n=%d (%s)\n",
+ n, strerror(errno)));
+ goto done;
+ }
+ if (((HEADER *)answer)->rcode == NOERROR)
+ nzones++;
+
+ /* Restore resolver's nameserver set. */
+ res_setservers(statp, nsaddrs, nscount);
+ nscount = 0;
+ }
+ done:
+ while (!EMPTY(zgrps)) {
+ zptr = HEAD(zgrps);
+ if ((zptr->z_flags & ZG_F_ZONESECTADDED) != 0)
+ res_freeupdrec(HEAD(zptr->z_rrlist));
+ UNLINK(zgrps, zptr, z_link);
+ free(zptr);
+ }
+ if (nscount != 0)
+ res_setservers(statp, nsaddrs, nscount);
+
+ free(packet);
+ return (nzones);
+}
+
+/* Private. */
+
+static void
+res_dprintf(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ fputs(";; res_nupdate: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+}
diff --git a/usr/src/lib/libresolv2_joy/common/sunw/sunw_mtctxres.c b/usr/src/lib/libresolv2_joy/common/sunw/sunw_mtctxres.c
new file mode 100644
index 0000000000..cc2a485ede
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/sunw/sunw_mtctxres.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <port_before.h>
+#include <thread.h>
+#include <errno.h>
+#include <netdb.h>
+#include <malloc.h>
+#include <string.h>
+#include <resolv_mt.h>
+#include <irs.h>
+#include <port_after.h>
+
+#pragma redefine_extname __h_errno __joy_h_errno
+
+/*
+ * much of the original version of sunw_mtxtxres.c was incorporated into
+ * ISC libbind as resolv/mtctxres.c. The following bits have not yet made
+ * it into ISC libbind.
+ */
+
+/*
+ * There used to be a private, MT-safe resolver interface that used TSD
+ * to store per-thread _res, h_errno, etc. We continue to provide the
+ * access functions __res_get_res() and __res_get_h_errno() so that binaries
+ * that used the private interface will continue to work.
+ */
+
+#ifdef _res
+#undef _res
+#endif
+
+extern struct __res_state *__res_state(void);
+
+struct __res_state *
+__res_get_res(void) {
+ return (__res_state());
+}
+
+
+#ifdef h_errno
+#undef h_errno
+#endif
+
+extern int *__h_errno(void);
+
+int *
+__res_get_h_errno(void) {
+ return (__h_errno());
+}
+
+
+#ifdef SUNW_HOSTS_FALLBACK
+
+/*
+ * When the name service switch calls libresolv, it doesn't want fallback
+ * to /etc/hosts, so we provide a method to turn it off.
+ */
+
+void
+__joy_res_set_no_hosts_fallback(void) {
+ ___mtctxres()->no_hosts_fallback_private = 1;
+}
+
+void
+__joy_res_unset_no_hosts_fallback(void) {
+ ___mtctxres()->no_hosts_fallback_private = 0;
+}
+
+int
+__res_no_hosts_fallback(void) {
+ return (___mtctxres()->no_hosts_fallback_private);
+}
+
+#endif /* SUNW_HOSTS_FALLBACK */
+
+#ifdef SUNW_OVERRIDE_RETRY
+
+/*
+ * The NS switch wants to be able to override the number of retries.
+ */
+
+int
+__joy_res_override_retry(int retry) {
+ ___mtctxres()->retry_private = retry;
+ /*
+ * This function doesn't really need a return value; saving the
+ * old retry setting, and restoring it, is handled by __res_retry()
+ * and __res_retry_reset() below. However, the nss_dns library
+ * must have a private version of this function to be used when
+ * running with an old libresolv. That private nss_dns function
+ * needs a return value, and a function pointer is used to select
+ * the right function at runtime. Thus, __res_override_retry
+ * must have a function prototype consistent with the private
+ * nss_dns function, i.e., one that returns an int.
+ *
+ * Given that we do have a return value, that value must be zero.
+ * That's because retry_private == 0 is used to indicate that
+ * no override retry value is in effect, and the way we expect
+ * nss_dns to call us is:
+ *
+ * int oldretry = __res_override_retry(N);
+ * <whatever>
+ * (void)__res_override_retry(old_retry);
+ */
+ return (0);
+}
+
+int
+__res_retry(int retry) {
+ mtctxres_t *mt = ___mtctxres();
+
+ mt->retry_save = retry;
+ return ((mt->retry_private != 0) ? mt->retry_private : retry);
+}
+
+int
+__res_retry_reset(void) {
+ mtctxres_t *mt = ___mtctxres();
+
+ return (mt->retry_save);
+}
+
+#endif /* SUNW_OVERRIDE_RETRY */
diff --git a/usr/src/lib/libresolv2_joy/common/sunw/sunw_updrec.c b/usr/src/lib/libresolv2_joy/common/sunw/sunw_updrec.c
new file mode 100644
index 0000000000..3b0ffc49df
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/sunw/sunw_updrec.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * As of BIND 8.2.2, ISC (a) removed res_mkupdate(), res_update(), and
+ * res_mkupdrec() from what they consider the supported interface. The
+ * functions still exist, but their calling interface has changed, since
+ * the ns_updrec structure has changed.
+ *
+ * It seems probable that res_mkupdate() etc. will return, though possibly
+ * with other changes, in some future BIND release. In order to avoid
+ * going to PSARC twice (once to remove the functions, and then again to
+ * add them back), we retain the old interface as a wrapper around the
+ * new one.
+ */
+
+#include <port_before.h>
+
+#include <malloc.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+/* get the Solaris ns_updrec before any renaming happens */
+#include <arpa/nameser.h>
+
+/* get the __ISC_ns_updrec */
+#include <res_update.h>
+
+#include <port_after.h>
+
+/* un-rename ns_updrec and res_* functions so we can wrap them */
+#undef ns_updrec
+#undef res_mkupdate
+#undef res_update
+#undef res_mkupdrec
+#undef res_freeupdrec
+#undef res_nmkupdate
+#undef res_nupdate
+
+void res_freeupdrec(ns_updrec *);
+
+static int
+old2new(ns_updrec *old, __ISC_ns_updrec *new) {
+
+ if (old->r_dname != 0) {
+ if ((new->r_dname = strdup(old->r_dname)) == 0)
+ return (-1);
+ } else {
+ new->r_dname = 0;
+ }
+
+ new->r_glink.prev =
+ new->r_glink.next =
+ new->r_link.prev =
+ new->r_link.next = 0;
+
+ new->r_section = old->r_section;
+ new->r_class = old->r_class;
+ new->r_type = old->r_type;
+ new->r_ttl = old->r_ttl;
+ new->r_data = old->r_data;
+ new->r_size = old->r_size;
+ new->r_opcode = old->r_opcode;
+ new->r_dp = old->r_dp;
+ new->r_deldp = old->r_deldp;
+ new->r_zone = old->r_zone;
+
+ return (0);
+}
+
+
+static int
+new2old(__ISC_ns_updrec *new, ns_updrec *old) {
+ /* XXX r_prev and r_next unchanged */
+ if (new->r_dname != 0) {
+ if ((old->r_dname = strdup(new->r_dname)) == 0)
+ return (-1);
+ } else {
+ old->r_dname = 0;
+ }
+ old->r_section = new->r_section;
+ old->r_class = new->r_class;
+ old->r_type = new->r_type;
+ old->r_ttl = new->r_ttl;
+ old->r_data = new->r_data;
+ old->r_size = new->r_size;
+ old->r_opcode = new->r_opcode;
+ old->r_grpnext = 0; /* XXX */
+ old->r_dp = new->r_dp;
+ old->r_deldp = new->r_deldp;
+ old->r_zone = new->r_zone;
+
+ return (0);
+}
+
+
+static void
+delete_list(__ISC_ns_updrec *list) {
+
+ __ISC_ns_updrec *next;
+
+ for (; list != 0; list = next) {
+ next = list->r_link.next;
+ __ISC_res_freeupdrec(list);
+ }
+}
+
+
+static __ISC_ns_updrec *
+copy_list(ns_updrec *old, int do_glink) {
+
+ __ISC_ns_updrec *list = 0, *r, *p;
+
+ if (old == 0)
+ return (0);
+
+ for (p = 0; old != 0; old = old->r_next, p = r) {
+ if ((r = calloc(1, sizeof (*r))) == 0 ||
+ old2new(old, r) != 0) {
+ free(r);
+ delete_list(list);
+ return (0);
+ }
+ r->r_link.prev = p;
+ r->r_link.next = 0;
+ /* res_update and res_nupdate want r_glink set up like this */
+ if (do_glink) {
+ r->r_glink.prev = p;
+ r->r_glink.next = 0;
+ } else {
+ r->r_glink.prev = (void *)-1;
+ r->r_glink.next = (void *)-1;
+ }
+ if (p != 0) {
+ p->r_link.next = r;
+ if (do_glink) {
+ p->r_glink.next = r;
+ }
+ } else {
+ list = r;
+ }
+ }
+ return (list);
+}
+
+
+int
+res_mkupdate(ns_updrec *rrecp_in, uchar_t *buf, int length) {
+
+ __ISC_ns_updrec *r;
+ int ret;
+
+ if ((r = copy_list(rrecp_in, 1)) == 0)
+ return (-1);
+
+ ret = __ISC_res_mkupdate(r, buf, length);
+
+ delete_list(r);
+
+ return (ret);
+}
+
+int
+res_nmkupdate(res_state statp, ns_updrec *rrecp_in, uchar_t *buf, int length) {
+
+ __ISC_ns_updrec *r;
+ int ret;
+
+ if ((r = copy_list(rrecp_in, 1)) == 0)
+ return (-1);
+
+ ret = __ISC_res_nmkupdate(statp, r, buf, length);
+
+ delete_list(r);
+
+ return (ret);
+}
+
+
+int
+res_update(ns_updrec *rrecp_in) {
+
+ __ISC_ns_updrec *r;
+ int ret;
+
+ if ((r = copy_list(rrecp_in, 0)) == 0)
+ return (-1);
+
+ ret = __ISC_res_update(r);
+
+ delete_list(r);
+
+ return (ret);
+}
+
+int
+res_nupdate(res_state statp, ns_updrec *rrecp_in, ns_tsig_key *key) {
+
+ __ISC_ns_updrec *r;
+ int ret;
+
+ if ((r = copy_list(rrecp_in, 0)) == 0)
+ return (-1);
+
+ ret = __ISC_res_nupdate(statp, r, key);
+
+ delete_list(r);
+
+ return (ret);
+}
+
+
+
+ns_updrec *
+res_mkupdrec(int section, const char *dname, uint_t class, uint_t type,
+ uint_t ttl) {
+
+ __ISC_ns_updrec *n;
+ ns_updrec *o;
+
+ n = __ISC_res_mkupdrec(section, dname, class, type, ttl);
+ if (n == 0)
+ return (0);
+
+ if ((o = calloc(1, sizeof (*o))) != 0) {
+ if (new2old(n, o) != 0) {
+ res_freeupdrec(o);
+ o = 0;
+ }
+ }
+
+ __ISC_res_freeupdrec(n);
+
+ return (o);
+}
+
+
+void
+res_freeupdrec(ns_updrec *rrecp) {
+ if (rrecp == 0)
+ return;
+ /* Note: freeing r_dp is the caller's responsibility. */
+ if (rrecp->r_dname != NULL)
+ free(rrecp->r_dname);
+ free(rrecp);
+}
diff --git a/usr/src/lib/libresolv2_joy/common/sunw/sunw_wrappers.c b/usr/src/lib/libresolv2_joy/common/sunw/sunw_wrappers.c
new file mode 100644
index 0000000000..55bbe07024
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/common/sunw/sunw_wrappers.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <port_before.h>
+#include <resolv_joy.h>
+#include <arpa/inet.h>
+#include <port_after.h>
+
+#undef p_option
+/* extern const char * isc_p_option(); */
+const char *p_option(uint_t option) {
+ return (isc_p_option((ulong_t)option));
+}
+#pragma weak __p_option = p_option
+
+#undef p_secstodate
+/* extern char * isc_p_secstodate (); */
+char *p_secstodate(uint_t secs) {
+ return (isc_p_secstodate((ulong_t)secs));
+}
+#pragma weak __p_secstodate = p_secstodate
diff --git a/usr/src/lib/libresolv2_joy/i386/Makefile b/usr/src/lib/libresolv2_joy/i386/Makefile
new file mode 100644
index 0000000000..a333224278
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/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 (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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libresolv2_joy/include/Makefile b/usr/src/lib/libresolv2_joy/include/Makefile
new file mode 100644
index 0000000000..8bff3c3188
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/Makefile
@@ -0,0 +1,60 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../../../Makefile.master
+
+HDRS= os_version.h port_ipv6.h
+TMPHDRS= new_os_version.h new_port_ipv6.h
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+
+.KEEP_STATE:
+
+all lint: $(HDRS)
+
+install: all
+
+clean:
+ $(RM) $(HDRS) $(TMPHDRS)
+
+clobber: clean
+
+# os_version.h and port_ipv6.h should be rebuilt when you change OS
+# revision. Since that's not easily expressed as a dependency, we
+# rebuild them every time.
+
+os_version.h: make_os_version FRC
+ ./make_os_version
+
+port_ipv6.h: probe_ipv6 FRC
+ CC="$(CC)" ./probe_ipv6
+
+FRC:
diff --git a/usr/src/lib/libresolv2_joy/include/arpa/port_inet.h b/usr/src/lib/libresolv2_joy/include/arpa/port_inet.h
new file mode 100644
index 0000000000..5eb1787f56
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/arpa/port_inet.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ *
+ * All rights reserved.
+ */
+
+#ifndef _ARPA_PORT_INET_H
+#define _ARPA_PORT_INET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * these are libresolv2 functions that were made local in previous versions
+ * we rename them res_* because they conflict with libnsl or libsocket
+ */
+
+#define inet_lnaof res_inet_lnaof /* libsocket */
+ulong_t inet_lnaof(struct in_addr in);
+
+#define inet_makeaddr res_inet_makeaddr /* libsocket */
+struct in_addr inet_makeaddr(ulong_t net, ulong_t host);
+
+#define inet_netof res_inet_netof /* libnsl */
+ulong_t inet_netof(struct in_addr in);
+
+#define inet_network res_inet_network /* libsocket */
+ulong_t inet_network(register const char *cp);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#endif /* _ARPA_PORT_INET_H */
diff --git a/usr/src/lib/libresolv2_joy/include/arpa/port_nameser.h b/usr/src/lib/libresolv2_joy/include/arpa/port_nameser.h
new file mode 100644
index 0000000000..b40ea0d163
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/arpa/port_nameser.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ARPA_PORT_NAMESER_H
+#define _ARPA_PORT_NAMESER_H
+
+/*
+ * ISC changed the ns_updrec structure. However, it's a public interface
+ * in Solaris, so we rename it here and wrap in sunw_updrec.c
+ */
+#define ns_updrec __ISC_ns_updrec
+
+
+/*
+ * Due to the above, the following functions need to be renamed and
+ * wrapped in sunw_updrec.c.
+ *
+ * For BIND 8.2.2, ISC removed the dynamic update functions, and the
+ * definition of the ns_updrec structure, from the public include files
+ * (<resolv.h>, <arpa/nameser.h>. However, res_update(), res_mkupdate(),
+ * and res_mkupdrec() are in the public libresolv interface in Solaris,
+ * so we can't easily remove them. Thus, ISC's new versions of res_mkupdate()
+ * etc. can't be exposed under their original names.
+ *
+ * res_nmkupdate() and res_nupdate are new. We could either change them
+ * to accept the <arpa/nameser.h> ns_updrec, or leave them unchanged and
+ * undocumented. Since ISC may change ns_updrec again, we pick the latter
+ * solution for now.
+ */
+#define res_mkupdate __ISC_res_mkupdate
+#define res_update __ISC_res_update
+#define res_mkupdrec __ISC_res_mkupdrec
+#define res_freeupdrec __ISC_res_freeupdrec
+#define res_nmkupdate __ISC_res_nmkupdate
+#define res_nupdate __ISC_res_nupdate
+
+
+#endif /* _ARPA_PORT_NAMESER_H */
diff --git a/usr/src/lib/libresolv2_joy/include/conf/sunoptions.h b/usr/src/lib/libresolv2_joy/include/conf/sunoptions.h
new file mode 100644
index 0000000000..b75ff9d878
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/conf/sunoptions.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SUNOPTIONS_H
+#define _SUNOPTIONS_H
+
+#define USELOOPBACK /* Resolver library defaults to 127.0.0.1 */
+
+/* Additions for Solaris 2 */
+
+#define SUNW_INITCHKIF /* Check if any non-loopback interface is up */
+#define SUNW_CONFCHECK /* Abort quickly if no /etc/resolv.conf or */
+ /* local named */
+#define SUNW_HOSTS_FALLBACK /* Configurable /etc/hosts fallback */
+#define SUNW_HNOK_UNDERSCORE /* Allow underscore in hostnames (libresolv) */
+#define SUNW_MT_RESOLVER /* MT hot extensions (libresolv) */
+#define SUNW_SETHERRNO /* ISC does not set h_errno in gethostbyname */
+#define SUNW_OVERRIDE_RETRY /* Allow NS switch to override res->retry */
+#define SUNW_LIBMD5 /* Use md5(3EXT) instead of internal implementation */
+
+/* If compiling an MT warm libresolv, we also need reentrancy */
+#if defined(SUNW_MT_RESOLVER) && !defined(_REENTRANT)
+#define _REENTRANT
+#endif
+
+/* End additions for Solaris 2 */
+
+#endif /* _SUNOPTIONS_H */
diff --git a/usr/src/lib/libresolv2_joy/include/config.h b/usr/src/lib/libresolv2_joy/include/config.h
new file mode 100644
index 0000000000..35fb115a0f
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/config.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/* config.h. Generated from config.h.in by configure. */
+/* #undef _SOCKADDR_LEN */
+#define HAVE_FCNTL_H 1
+/* #undef HAVE_PATHS_H */
+#define HAVE_INTTYPES_H 1
+#define HAVE_STROPTS_H 1
+/* #undef HAVE_SYS_TIMERS_H */
+#define HAVE_SYS_SELECT_H 1
+#define HAVE_MEMORY_H 1
+/* #undef SYS_CDEFS_H */
+#define _POSIX_PTHREAD_SEMANTICS 1
+#define POSIX_GETPWUID_R 1
+#define POSIX_GETPWNAM_R 1
+#define POSIX_GETGRGID_R 1
+#define POSIX_GETGRNAM_R 1
+#define HAVE_MEMMOVE 1
+#define HAVE_MEMCHR 1
+/* #undef SPRINTF_CHAR */
+/* #undef VSPRINTF_CHAR */
+#define USE_SYSERROR_LIST 1
+/* #undef NEED_STRTOUL */
+/* #undef NEED_SUN4PROTOS */
+/* #undef REENABLE_SEND */
+
+#define NEED_SETGROUPENT 1
+#define NEED_GETGROUPLIST 1
+
+/* define if prototype for getgrnam_r() is required */
+/* #undef NEED_GETGRNAM_R */
+/* #undef NEED_GETGRGID_R */
+/* #undef NEED_GETGRENT_R */
+#define NEED_SETGRENT_R 1
+#define NEED_ENDGRENT_R 1
+
+#define NEED_INNETGR_R 1
+/* #undef NEED_SETNETGRENT_R */
+#define NEED_ENDNETGRENT_R 1
+
+/* #undef NEED_GETPWNAM_R */
+/* #undef NEED_GETPWUID_R */
+#define NEED_SETPWENT_R 1
+#define NEED_SETPASSENT_R 1
+#define NEED_SETPWENT_R 1
+/* #undef NEED_GETPWENT_R */
+#define NEED_ENDPWENT_R 1
+
+#define NEED_SETPASSENT 1
+
+/* #undef HAS_PW_CLASS */
+
+/* #undef ssize_t */
+/* #undef uintptr_t */
+
+/* Shut up warnings about sputaux in stdio.h on BSD/OS pre-4.1 */
+/* #undef SHUTUP_SPUTAUX */
+#ifdef SHUTUP_SPUTAUX
+struct __sFILE;
+extern __inline int __sputaux(int _c, struct __sFILE *_p);
+#endif
+#define BROKEN_IN6ADDR_INIT_MACROS 1
+#define HAVE_STRLCAT 1
+/* Shut up warnings about missing braces */
+/* #undef SHUTUP_MUTEX_INITIALIZER */
+#ifdef SHUTUP_MUTEX_INITIALIZER
+#define LIBBIND_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
+#else
+#define LIBBIND_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
+#endif
+
diff --git a/usr/src/lib/libresolv2_joy/include/err.h b/usr/src/lib/libresolv2_joy/include/err.h
new file mode 100644
index 0000000000..45992ea336
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/err.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/*-
+ * Copyright (c) 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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.
+ *
+ * @(#)err.h 8.1 (Berkeley) 6/2/93
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef _ERR_H_
+#define _ERR_H_
+
+#include <sys/cdefs.h>
+#include <stdarg.h>
+
+__BEGIN_DECLS
+__dead void err __P((int, const char *, ...)) __attribute__((__volatile));
+__dead void verr __P((int, const char *, va_list))
+ __attribute__((__volatile));
+__dead void errx __P((int, const char *, ...)) __attribute__((__volatile));
+__dead void verrx __P((int, const char *, va_list))
+ __attribute__((__volatile));
+void warn __P((const char *, ...));
+void vwarn __P((const char *, va_list));
+void warnx __P((const char *, ...));
+void vwarnx __P((const char *, va_list));
+__END_DECLS
+
+#endif /* !_ERR_H_ */
diff --git a/usr/src/lib/libresolv2_joy/include/fd_setsize.h b/usr/src/lib/libresolv2_joy/include/fd_setsize.h
new file mode 100644
index 0000000000..0e21049742
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/fd_setsize.h
@@ -0,0 +1,10 @@
+#ifndef _FD_SETSIZE_H
+#define _FD_SETSIZE_H
+
+/*%
+ * If you need a bigger FD_SETSIZE, this is NOT the place to set it.
+ * This file is a fallback for BIND ports which don't specify their own.
+ */
+
+#endif /* _FD_SETSIZE_H */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/hesiod.h b/usr/src/lib/libresolv2_joy/include/hesiod.h
new file mode 100644
index 0000000000..d64c0c5e80
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/hesiod.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*! \file
+ * \brief
+ * This file is primarily maintained by <tytso@mit.edu> and <ghudson@mit.edu>.
+ */
+
+/*
+ * $Id: hesiod.h,v 1.4 2005/04/27 04:56:14 sra Exp $
+ */
+
+#ifndef _HESIOD_H_INCLUDED
+#define _HESIOD_H_INCLUDED
+
+int hesiod_init __P((void **));
+void hesiod_end __P((void *));
+char * hesiod_to_bind __P((void *, const char *, const char *));
+char ** hesiod_resolve __P((void *, const char *, const char *));
+void hesiod_free_list __P((void *, char **));
+struct __res_state * __hesiod_res_get __P((void *));
+void __hesiod_res_set __P((void *, struct __res_state *,
+ void (*)(void *)));
+
+#endif /*_HESIOD_H_INCLUDED*/
diff --git a/usr/src/lib/libresolv2_joy/include/irp.h b/usr/src/lib/libresolv2_joy/include/irp.h
new file mode 100644
index 0000000000..1290bd068f
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/irp.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: irp.h,v 1.4 2005/04/27 04:56:15 sra Exp $
+ */
+
+#ifndef _IRP_H_INCLUDED
+#define _IRP_H_INCLUDED
+
+/*! \file */
+
+#define IRPD_TIMEOUT 30 /*%< seconds */
+#define IRPD_MAXSESS 50 /*%< number of simultaneous sessions. */
+#define IRPD_PORT 6660 /*%< 10 times the number of the beast. */
+#define IRPD_PATH "/var/run/irpd" /*%< af_unix socket path */
+
+/* If sets the environment variable IRPDSERVER to an IP address
+ (e.g. "192.5.5.1"), then that's the host the client expects irpd to be
+ running on. */
+#define IRPD_HOST_ENV "IRPDSERVER"
+
+/* Protocol response codes. */
+#define IRPD_WELCOME_CODE 200
+#define IRPD_NOT_WELCOME_CODE 500
+
+#define IRPD_GETHOST_ERROR 510
+#define IRPD_GETHOST_NONE 210
+#define IRPD_GETHOST_OK 211
+#define IRPD_GETHOST_SETOK 212
+
+#define IRPD_GETNET_ERROR 520
+#define IRPD_GETNET_NONE 220
+#define IRPD_GETNET_OK 221
+#define IRPD_GETNET_SETOK 222
+
+#define IRPD_GETUSER_ERROR 530
+#define IRPD_GETUSER_NONE 230
+#define IRPD_GETUSER_OK 231
+#define IRPD_GETUSER_SETOK 232
+
+#define IRPD_GETGROUP_ERROR 540
+#define IRPD_GETGROUP_NONE 240
+#define IRPD_GETGROUP_OK 241
+#define IRPD_GETGROUP_SETOK 242
+
+#define IRPD_GETSERVICE_ERROR 550
+#define IRPD_GETSERVICE_NONE 250
+#define IRPD_GETSERVICE_OK 251
+#define IRPD_GETSERVICE_SETOK 252
+
+#define IRPD_GETPROTO_ERROR 560
+#define IRPD_GETPROTO_NONE 260
+#define IRPD_GETPROTO_OK 261
+#define IRPD_GETPROTO_SETOK 262
+
+#define IRPD_GETNETGR_ERROR 570
+#define IRPD_GETNETGR_NONE 270
+#define IRPD_GETNETGR_OK 271
+#define IRPD_GETNETGR_NOMORE 272
+#define IRPD_GETNETGR_MATCHES 273
+#define IRPD_GETNETGR_NOMATCH 274
+#define IRPD_GETNETGR_SETOK 275
+#define IRPD_GETNETGR_SETERR 276
+
+#define irs_irp_read_body __irs_irp_read_body
+#define irs_irp_read_response __irs_irp_read_response
+#define irs_irp_disconnect __irs_irp_disconnect
+#define irs_irp_connect __irs_irp_connect
+#define irs_irp_connection_setup __irs_irp_connection_setup
+#define irs_irp_send_command __irs_irp_send_command
+
+struct irp_p;
+
+char *irs_irp_read_body(struct irp_p *, size_t *);
+int irs_irp_read_response(struct irp_p *, char *, size_t);
+void irs_irp_disconnect(struct irp_p *);
+int irs_irp_connect(struct irp_p *);
+int irs_irp_is_connected(struct irp_p *);
+int irs_irp_connection_setup(struct irp_p *, int *);
+#ifdef __GNUC__
+int irs_irp_send_command(struct irp_p *, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+#else
+int irs_irp_send_command(struct irp_p *, const char *, ...);
+#endif
+int irs_irp_get_full_response(struct irp_p *, int *, char *, size_t,
+ char **, size_t *);
+int irs_irp_read_line(struct irp_p *, char *, int);
+
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/irs.h b/usr/src/lib/libresolv2_joy/include/irs.h
new file mode 100644
index 0000000000..386e3cb3f6
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/irs.h
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: irs.h,v 1.5 2005/04/27 04:56:15 sra Exp $
+ */
+
+#ifndef _IRS_H_INCLUDED
+#define _IRS_H_INCLUDED
+
+/*! \file */
+
+#include <sys/types.h>
+
+#include <arpa/nameser.h>
+
+#include <grp.h>
+#include <netdb.h>
+#include <resolv_joy.h>
+#include <pwd.h>
+
+/*%
+ * This is the group map class.
+ */
+struct irs_gr {
+ void * private;
+ void (*close) __P((struct irs_gr *));
+ struct group * (*next) __P((struct irs_gr *));
+ struct group * (*byname) __P((struct irs_gr *, const char *));
+ struct group * (*bygid) __P((struct irs_gr *, gid_t));
+ int (*list) __P((struct irs_gr *, const char *,
+ gid_t, gid_t *, int *));
+ void (*rewind) __P((struct irs_gr *));
+ void (*minimize) __P((struct irs_gr *));
+ struct __res_state * (*res_get) __P((struct irs_gr *));
+ void (*res_set) __P((struct irs_gr *, res_state,
+ void (*)(void *)));
+};
+
+/*%
+ * This is the password map class.
+ */
+struct irs_pw {
+ void * private;
+ void (*close) __P((struct irs_pw *));
+ struct passwd * (*next) __P((struct irs_pw *));
+ struct passwd * (*byname) __P((struct irs_pw *, const char *));
+ struct passwd * (*byuid) __P((struct irs_pw *, uid_t));
+ void (*rewind) __P((struct irs_pw *));
+ void (*minimize) __P((struct irs_pw *));
+ struct __res_state * (*res_get) __P((struct irs_pw *));
+ void (*res_set) __P((struct irs_pw *, res_state,
+ void (*)(void *)));
+};
+
+/*%
+ * This is the service map class.
+ */
+struct irs_sv {
+ void * private;
+ void (*close) __P((struct irs_sv *));
+ struct servent *(*byname) __P((struct irs_sv *,
+ const char *, const char *));
+ struct servent *(*byport) __P((struct irs_sv *, int, const char *));
+ struct servent *(*next) __P((struct irs_sv *));
+ void (*rewind) __P((struct irs_sv *));
+ void (*minimize) __P((struct irs_sv *));
+ struct __res_state * (*res_get) __P((struct irs_sv *));
+ void (*res_set) __P((struct irs_sv *, res_state,
+ void (*)(void *)));
+};
+
+/*%
+ * This is the protocols map class.
+ */
+struct irs_pr {
+ void * private;
+ void (*close) __P((struct irs_pr *));
+ struct protoent *(*byname) __P((struct irs_pr *, const char *));
+ struct protoent *(*bynumber) __P((struct irs_pr *, int));
+ struct protoent *(*next) __P((struct irs_pr *));
+ void (*rewind) __P((struct irs_pr *));
+ void (*minimize) __P((struct irs_pr *));
+ struct __res_state * (*res_get) __P((struct irs_pr *));
+ void (*res_set) __P((struct irs_pr *, res_state,
+ void (*)(void *)));
+};
+
+/*%
+ * This is the hosts map class.
+ */
+struct irs_ho {
+ void * private;
+ void (*close) __P((struct irs_ho *));
+ struct hostent *(*byname) __P((struct irs_ho *, const char *));
+ struct hostent *(*byname2) __P((struct irs_ho *, const char *, int));
+ struct hostent *(*byaddr) __P((struct irs_ho *,
+ const void *, int, int));
+ struct hostent *(*next) __P((struct irs_ho *));
+ void (*rewind) __P((struct irs_ho *));
+ void (*minimize) __P((struct irs_ho *));
+ struct __res_state * (*res_get) __P((struct irs_ho *));
+ void (*res_set) __P((struct irs_ho *, res_state,
+ void (*)(void *)));
+ struct addrinfo *(*addrinfo) __P((struct irs_ho *, const char *,
+ const struct addrinfo *));
+};
+
+/*%
+ * This is the networks map class.
+ */
+struct irs_nw {
+ void * private;
+ void (*close) __P((struct irs_nw *));
+ struct nwent * (*byname) __P((struct irs_nw *, const char *, int));
+ struct nwent * (*byaddr) __P((struct irs_nw *, void *, int, int));
+ struct nwent * (*next) __P((struct irs_nw *));
+ void (*rewind) __P((struct irs_nw *));
+ void (*minimize) __P((struct irs_nw *));
+ struct __res_state * (*res_get) __P((struct irs_nw *));
+ void (*res_set) __P((struct irs_nw *, res_state,
+ void (*)(void *)));
+};
+
+/*%
+ * This is the netgroups map class.
+ */
+struct irs_ng {
+ void * private;
+ void (*close) __P((struct irs_ng *));
+ int (*next) __P((struct irs_ng *, const char **,
+ const char **, const char **));
+ int (*test) __P((struct irs_ng *, const char *,
+ const char *, const char *,
+ const char *));
+ void (*rewind) __P((struct irs_ng *, const char *));
+ void (*minimize) __P((struct irs_ng *));
+};
+
+/*%
+ * This is the generic map class, which copies the front of all others.
+ */
+struct irs_map {
+ void * private;
+ void (*close) __P((void *));
+};
+
+/*%
+ * This is the accessor class. It contains pointers to all of the
+ * initializers for the map classes for a particular accessor.
+ */
+struct irs_acc {
+ void * private;
+ void (*close) __P((struct irs_acc *));
+ struct irs_gr * (*gr_map) __P((struct irs_acc *));
+ struct irs_pw * (*pw_map) __P((struct irs_acc *));
+ struct irs_sv * (*sv_map) __P((struct irs_acc *));
+ struct irs_pr * (*pr_map) __P((struct irs_acc *));
+ struct irs_ho * (*ho_map) __P((struct irs_acc *));
+ struct irs_nw * (*nw_map) __P((struct irs_acc *));
+ struct irs_ng * (*ng_map) __P((struct irs_acc *));
+ struct __res_state * (*res_get) __P((struct irs_acc *));
+ void (*res_set) __P((struct irs_acc *, res_state,
+ void (*)(void *)));
+};
+
+/*%
+ * This is because the official definition of "struct netent" has no
+ * concept of CIDR even though it allows variant address families (on
+ * output but not input). The compatibility stubs convert the structs
+ * below into "struct netent"'s.
+ */
+struct nwent {
+ char *n_name; /*%< official name of net */
+ char **n_aliases; /*%< alias list */
+ int n_addrtype; /*%< net address type */
+ void *n_addr; /*%< network address */
+ int n_length; /*%< address length, in bits */
+};
+
+/*%
+ * Hide external function names from POSIX.
+ */
+#define irs_gen_acc __irs_gen_acc
+#define irs_lcl_acc __irs_lcl_acc
+#define irs_dns_acc __irs_dns_acc
+#define irs_nis_acc __irs_nis_acc
+#define irs_irp_acc __irs_irp_acc
+#define irs_destroy __irs_destroy
+#define irs_dns_gr __irs_dns_gr
+#define irs_dns_ho __irs_dns_ho
+#define irs_dns_nw __irs_dns_nw
+#define irs_dns_pr __irs_dns_pr
+#define irs_dns_pw __irs_dns_pw
+#define irs_dns_sv __irs_dns_sv
+#define irs_gen_gr __irs_gen_gr
+#define irs_gen_ho __irs_gen_ho
+#define irs_gen_ng __irs_gen_ng
+#define irs_gen_nw __irs_gen_nw
+#define irs_gen_pr __irs_gen_pr
+#define irs_gen_pw __irs_gen_pw
+#define irs_gen_sv __irs_gen_sv
+#define irs_irp_get_full_response __irs_irp_get_full_response
+#define irs_irp_gr __irs_irp_gr
+#define irs_irp_ho __irs_irp_ho
+#define irs_irp_is_connected __irs_irp_is_connected
+#define irs_irp_ng __irs_irp_ng
+#define irs_irp_nw __irs_irp_nw
+#define irs_irp_pr __irs_irp_pr
+#define irs_irp_pw __irs_irp_pw
+#define irs_irp_read_line __irs_irp_read_line
+#define irs_irp_sv __irs_irp_sv
+#define irs_lcl_gr __irs_lcl_gr
+#define irs_lcl_ho __irs_lcl_ho
+#define irs_lcl_ng __irs_lcl_ng
+#define irs_lcl_nw __irs_lcl_nw
+#define irs_lcl_pr __irs_lcl_pr
+#define irs_lcl_pw __irs_lcl_pw
+#define irs_lcl_sv __irs_lcl_sv
+#define irs_nis_gr __irs_nis_gr
+#define irs_nis_ho __irs_nis_ho
+#define irs_nis_ng __irs_nis_ng
+#define irs_nis_nw __irs_nis_nw
+#define irs_nis_pr __irs_nis_pr
+#define irs_nis_pw __irs_nis_pw
+#define irs_nis_sv __irs_nis_sv
+#define net_data_create __net_data_create
+#define net_data_destroy __net_data_destroy
+#define net_data_minimize __net_data_minimize
+
+/*%
+ * Externs.
+ */
+extern struct irs_acc * irs_gen_acc __P((const char *, const char *));
+extern struct irs_acc * irs_lcl_acc __P((const char *));
+extern struct irs_acc * irs_dns_acc __P((const char *));
+extern struct irs_acc * irs_nis_acc __P((const char *));
+extern struct irs_acc * irs_irp_acc __P((const char *));
+
+extern void irs_destroy __P((void));
+
+/*%
+ * These forward declarations are for the semi-private functions in
+ * the get*.c files. Each of these funcs implements the real get*
+ * functionality and the standard versions are just wrappers that
+ * call these. Apart from the wrappers, only irpd is expected to
+ * call these directly, hence these decls are put here and not in
+ * the /usr/include replacements.
+ */
+
+struct net_data; /*%< forward */
+/*
+ * net_data_create gets a singleton net_data object. net_data_init
+ * creates as many net_data objects as times it is called. Clients using
+ * the default interface will use net_data_create by default. Servers will
+ * probably want net_data_init (one call per client)
+ */
+struct net_data *net_data_create __P((const char *));
+struct net_data *net_data_init __P((const char *));
+void net_data_destroy __P((void *));
+
+extern struct group *getgrent_p __P((struct net_data *));
+extern struct group *getgrnam_p __P((const char *, struct net_data *));
+extern struct group *getgrgid_p __P((gid_t, struct net_data *));
+extern int setgroupent_p __P((int, struct net_data *));
+extern void endgrent_p __P((struct net_data *));
+extern int getgrouplist_p __P((const char *, gid_t, gid_t *, int *,
+ struct net_data *));
+
+#ifdef SETGRENT_VOID
+extern void setgrent_p __P((struct net_data *));
+#else
+extern int setgrent_p __P((struct net_data *));
+#endif
+
+extern struct hostent *gethostbyname_p __P((const char *,
+ struct net_data *));
+extern struct hostent *gethostbyname2_p __P((const char *, int,
+ struct net_data *));
+extern struct hostent *gethostbyaddr_p __P((const char *, int, int,
+ struct net_data *));
+extern struct hostent *gethostent_p __P((struct net_data *));
+extern void sethostent_p __P((int, struct net_data *));
+extern void endhostent_p __P((struct net_data *));
+extern struct hostent *getipnodebyname_p __P((const char *, int, int, int *,
+ struct net_data *));
+extern struct hostent *getipnodebyaddr_p __P((const void *, size_t,
+ int, int *, struct net_data *));
+
+extern struct netent *getnetent_p __P((struct net_data *));
+extern struct netent *getnetbyname_p __P((const char *, struct net_data *));
+extern struct netent *getnetbyaddr_p __P((unsigned long, int,
+ struct net_data *));
+extern void setnetent_p __P((int, struct net_data *));
+extern void endnetent_p __P((struct net_data *));
+
+extern void setnetgrent_p __P((const char *, struct net_data *));
+extern void endnetgrent_p __P((struct net_data *));
+extern int innetgr_p __P((const char *, const char *, const char *,
+ const char *, struct net_data *));
+extern int getnetgrent_p __P((const char **, const char **,
+ const char **, struct net_data *));
+
+extern struct protoent *getprotoent_p __P((struct net_data *));
+extern struct protoent *getprotobyname_p __P((const char *,
+ struct net_data *));
+extern struct protoent *getprotobynumber_p __P((int, struct net_data *));
+extern void setprotoent_p __P((int, struct net_data *));
+extern void endprotoent_p __P((struct net_data *));
+
+
+extern struct passwd *getpwent_p __P((struct net_data *));
+extern struct passwd *getpwnam_p __P((const char *, struct net_data *));
+extern struct passwd *getpwuid_p __P((uid_t, struct net_data *));
+extern int setpassent_p __P((int, struct net_data *));
+extern void endpwent_p __P((struct net_data *));
+
+#ifdef SETPWENT_VOID
+extern void setpwent_p __P((struct net_data *));
+#else
+extern int setpwent_p __P((struct net_data *));
+#endif
+
+extern struct servent *getservent_p __P((struct net_data *));
+extern struct servent *getservbyname_p __P((const char *, const char *,
+ struct net_data *));
+extern struct servent *getservbyport_p __P((int, const char *,
+ struct net_data *));
+extern void setservent_p __P((int, struct net_data *));
+extern void endservent_p __P((struct net_data *));
+
+#endif /*_IRS_H_INCLUDED*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/isc/assertions.h b/usr/src/lib/libresolv2_joy/include/isc/assertions.h
new file mode 100644
index 0000000000..68925e73b3
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/assertions.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1997-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: assertions.h,v 1.5 2008/11/14 02:36:51 marka Exp $
+ */
+
+#ifndef ASSERTIONS_H
+#define ASSERTIONS_H 1
+
+typedef enum {
+ assert_require, assert_ensure, assert_insist, assert_invariant
+} assertion_type;
+
+typedef void (*assertion_failure_callback)(const char *, int, assertion_type,
+ const char *, int);
+
+/* coverity[+kill] */
+extern assertion_failure_callback __assertion_failed;
+void set_assertion_failure_callback(assertion_failure_callback f);
+const char *assertion_type_to_text(assertion_type type);
+
+#if defined(CHECK_ALL) || defined(__COVERITY__)
+#define CHECK_REQUIRE 1
+#define CHECK_ENSURE 1
+#define CHECK_INSIST 1
+#define CHECK_INVARIANT 1
+#endif
+
+#if defined(CHECK_NONE) && !defined(__COVERITY__)
+#define CHECK_REQUIRE 0
+#define CHECK_ENSURE 0
+#define CHECK_INSIST 0
+#define CHECK_INVARIANT 0
+#endif
+
+#ifndef CHECK_REQUIRE
+#define CHECK_REQUIRE 1
+#endif
+
+#ifndef CHECK_ENSURE
+#define CHECK_ENSURE 1
+#endif
+
+#ifndef CHECK_INSIST
+#define CHECK_INSIST 1
+#endif
+
+#ifndef CHECK_INVARIANT
+#define CHECK_INVARIANT 1
+#endif
+
+#if CHECK_REQUIRE != 0
+#define REQUIRE(cond) \
+ ((void) ((cond) || \
+ ((__assertion_failed)(__FILE__, __LINE__, assert_require, \
+ #cond, 0), 0)))
+#define REQUIRE_ERR(cond) \
+ ((void) ((cond) || \
+ ((__assertion_failed)(__FILE__, __LINE__, assert_require, \
+ #cond, 1), 0)))
+#else
+#define REQUIRE(cond) ((void) (cond))
+#define REQUIRE_ERR(cond) ((void) (cond))
+#endif /* CHECK_REQUIRE */
+
+#if CHECK_ENSURE != 0
+#define ENSURE(cond) \
+ ((void) ((cond) || \
+ ((__assertion_failed)(__FILE__, __LINE__, assert_ensure, \
+ #cond, 0), 0)))
+#define ENSURE_ERR(cond) \
+ ((void) ((cond) || \
+ ((__assertion_failed)(__FILE__, __LINE__, assert_ensure, \
+ #cond, 1), 0)))
+#else
+#define ENSURE(cond) ((void) (cond))
+#define ENSURE_ERR(cond) ((void) (cond))
+#endif /* CHECK_ENSURE */
+
+#if CHECK_INSIST != 0
+#define INSIST(cond) \
+ ((void) ((cond) || \
+ ((__assertion_failed)(__FILE__, __LINE__, assert_insist, \
+ #cond, 0), 0)))
+#define INSIST_ERR(cond) \
+ ((void) ((cond) || \
+ ((__assertion_failed)(__FILE__, __LINE__, assert_insist, \
+ #cond, 1), 0)))
+#else
+#define INSIST(cond) ((void) (cond))
+#define INSIST_ERR(cond) ((void) (cond))
+#endif /* CHECK_INSIST */
+
+#if CHECK_INVARIANT != 0
+#define INVARIANT(cond) \
+ ((void) ((cond) || \
+ ((__assertion_failed)(__FILE__, __LINE__, assert_invariant, \
+ #cond, 0), 0)))
+#define INVARIANT_ERR(cond) \
+ ((void) ((cond) || \
+ ((__assertion_failed)(__FILE__, __LINE__, assert_invariant, \
+ #cond, 1), 0)))
+#else
+#define INVARIANT(cond) ((void) (cond))
+#define INVARIANT_ERR(cond) ((void) (cond))
+#endif /* CHECK_INVARIANT */
+#endif /* ASSERTIONS_H */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/isc/ctl.h b/usr/src/lib/libresolv2_joy/include/isc/ctl.h
new file mode 100644
index 0000000000..e2ba20201d
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/ctl.h
@@ -0,0 +1,112 @@
+#ifndef ISC_CTL_H
+#define ISC_CTL_H
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1998,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: ctl.h,v 1.5 2005/04/27 04:56:17 sra Exp $
+ */
+
+/*! \file */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <isc/eventlib.h>
+
+/* Macros. */
+
+#define CTL_MORE 0x0001 /*%< More will be / should be sent. */
+#define CTL_EXIT 0x0002 /*%< Close connection after this. */
+#define CTL_DATA 0x0004 /*%< Go into / this is DATA mode. */
+/* Types. */
+
+struct ctl_cctx;
+struct ctl_sctx;
+struct ctl_sess;
+struct ctl_verb;
+
+enum ctl_severity { ctl_debug, ctl_warning, ctl_error };
+
+typedef void (*ctl_logfunc)(enum ctl_severity, const char *, ...);
+
+typedef void (*ctl_verbfunc)(struct ctl_sctx *, struct ctl_sess *,
+ const struct ctl_verb *, const char *,
+ u_int, const void *, void *);
+
+typedef void (*ctl_srvrdone)(struct ctl_sctx *, struct ctl_sess *, void *);
+
+typedef void (*ctl_clntdone)(struct ctl_cctx *, void *, const char *, u_int);
+
+struct ctl_verb {
+ const char * name;
+ ctl_verbfunc func;
+ const char * help;
+};
+
+/* General symbols. */
+
+#define ctl_logger __ctl_logger
+
+#ifdef __GNUC__
+void ctl_logger(enum ctl_severity, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+#else
+void ctl_logger(enum ctl_severity, const char *, ...);
+#endif
+
+/* Client symbols. */
+
+#define ctl_client __ctl_client
+#define ctl_endclient __ctl_endclient
+#define ctl_command __ctl_command
+
+struct ctl_cctx * ctl_client(evContext, const struct sockaddr *, size_t,
+ const struct sockaddr *, size_t,
+ ctl_clntdone, void *,
+ u_int, ctl_logfunc);
+void ctl_endclient(struct ctl_cctx *);
+int ctl_command(struct ctl_cctx *, const char *, size_t,
+ ctl_clntdone, void *);
+
+/* Server symbols. */
+
+#define ctl_server __ctl_server
+#define ctl_endserver __ctl_endserver
+#define ctl_response __ctl_response
+#define ctl_sendhelp __ctl_sendhelp
+#define ctl_getcsctx __ctl_getcsctx
+#define ctl_setcsctx __ctl_setcsctx
+
+struct ctl_sctx * ctl_server(evContext, const struct sockaddr *, size_t,
+ const struct ctl_verb *,
+ u_int, u_int,
+ u_int, int, int,
+ ctl_logfunc, void *);
+void ctl_endserver(struct ctl_sctx *);
+void ctl_response(struct ctl_sess *, u_int,
+ const char *, u_int, const void *,
+ ctl_srvrdone, void *,
+ const char *, size_t);
+void ctl_sendhelp(struct ctl_sess *, u_int);
+void * ctl_getcsctx(struct ctl_sess *);
+void * ctl_setcsctx(struct ctl_sess *, void *);
+
+#endif /*ISC_CTL_H*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/isc/dst.h b/usr/src/lib/libresolv2_joy/include/isc/dst.h
new file mode 100644
index 0000000000..90a9e67468
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/dst.h
@@ -0,0 +1,168 @@
+#ifndef DST_H
+#define DST_H
+
+#ifndef HAS_DST_KEY
+typedef struct dst_key {
+ char *dk_key_name; /*%< name of the key */
+ int dk_key_size; /*%< this is the size of the key in bits */
+ int dk_proto; /*%< what protocols this key can be used for */
+ int dk_alg; /*%< algorithm number from key record */
+ u_int32_t dk_flags; /*%< and the flags of the public key */
+ u_int16_t dk_id; /*%< identifier of the key */
+} DST_KEY;
+#endif /* HAS_DST_KEY */
+/*
+ * do not taint namespace
+ */
+#define dst_bsafe_init __dst_bsafe_init
+#define dst_buffer_to_key __dst_buffer_to_key
+#define dst_check_algorithm __dst_check_algorithm
+#define dst_compare_keys __dst_compare_keys
+#define dst_cylink_init __dst_cylink_init
+#define dst_dnskey_to_key __dst_dnskey_to_key
+#define dst_eay_dss_init __dst_eay_dss_init
+#define dst_free_key __dst_free_key
+#define dst_generate_key __dst_generate_key
+#define dst_hmac_md5_init __dst_hmac_md5_init
+#define dst_init __dst_init
+#define dst_key_to_buffer __dst_key_to_buffer
+#define dst_key_to_dnskey __dst_key_to_dnskey
+#define dst_read_key __dst_read_key
+#define dst_rsaref_init __dst_rsaref_init
+#define dst_s_build_filename __dst_s_build_filename
+#define dst_s_calculate_bits __dst_s_calculate_bits
+#define dst_s_conv_bignum_b64_to_u8 __dst_s_conv_bignum_b64_to_u8
+#define dst_s_conv_bignum_u8_to_b64 __dst_s_conv_bignum_u8_to_b64
+#define dst_s_dns_key_id __dst_s_dns_key_id
+#define dst_s_dump __dst_s_dump
+#define dst_s_filename_length __dst_s_filename_length
+#define dst_s_fopen __dst_s_fopen
+#define dst_s_get_int16 __dst_s_get_int16
+#define dst_s_get_int32 __dst_s_get_int32
+#define dst_s_id_calc __dst_s_id_calc
+#define dst_s_put_int16 __dst_s_put_int16
+#define dst_s_put_int32 __dst_s_put_int32
+#define dst_s_quick_random __dst_s_quick_random
+#define dst_s_quick_random_set __dst_s_quick_random_set
+#define dst_s_random __dst_s_random
+#define dst_s_semi_random __dst_s_semi_random
+#define dst_s_verify_str __dst_s_verify_str
+#define dst_sig_size __dst_sig_size
+#define dst_sign_data __dst_sign_data
+#define dst_verify_data __dst_verify_data
+#define dst_write_key __dst_write_key
+
+/*
+ * DST Crypto API defintions
+ */
+void dst_init(void);
+int dst_check_algorithm(const int);
+
+
+int dst_sign_data(const int, /*!< specifies INIT/UPDATE/FINAL/ALL */
+ DST_KEY *, /*!< the key to use */
+ void **, /*!< pointer to state structure */
+ const u_char *, /*!< data to be signed */
+ const int, /*!< length of input data */
+ u_char *, /*!< buffer to write signature to */
+ const int); /*!< size of output buffer */
+int dst_verify_data(const int, /*!< specifies INIT/UPDATE/FINAL/ALL */
+ DST_KEY *, /*!< the key to use */
+ void **, /*!< pointer to state structure */
+ const u_char *, /*!< data to be verified */
+ const int, /*!< length of input data */
+ const u_char *, /*!< buffer containing signature */
+ const int); /*!< length of signature */
+DST_KEY *dst_read_key(const char *, /*!< name of key */
+ const u_int16_t, /*!< key tag identifier */
+ const int, /*!< key algorithm */
+ const int); /*!< Private/PublicKey wanted */
+int dst_write_key(const DST_KEY *, /*!< key to write out */
+ const int); /*!< Public/Private */
+DST_KEY *dst_dnskey_to_key(const char *, /*!< KEY record name */
+ const u_char *, /*!< KEY RDATA */
+ const int); /*!< size of input buffer */
+int dst_key_to_dnskey(const DST_KEY *, /*!< key to translate */
+ u_char *, /*!< output buffer */
+ const int); /*!< size of out_storage */
+DST_KEY *dst_buffer_to_key(const char *, /*!< name of the key */
+ const int, /*!< algorithm */
+ const int, /*!< dns flags */
+ const int, /*!< dns protocol */
+ const u_char *, /*!< key in dns wire fmt */
+ const int); /*!< size of key */
+int dst_key_to_buffer(DST_KEY *, u_char *, int);
+
+DST_KEY *dst_generate_key(const char *, /*!< name of new key */
+ const int, /*!< key algorithm to generate */
+ const int, /*!< size of new key */
+ const int, /*!< alg dependent parameter */
+ const int, /*!< key DNS flags */
+ const int); /*!< key DNS protocol */
+DST_KEY *dst_free_key(DST_KEY *);
+int dst_compare_keys(const DST_KEY *, const DST_KEY *);
+
+int dst_sig_size(DST_KEY *);
+
+
+/* support for dns key tags/ids */
+u_int16_t dst_s_dns_key_id(const u_char *, const int);
+u_int16_t dst_s_id_calc(const u_char *, const int);
+
+/* Used by callers as well as by the library. */
+#define RAW_KEY_SIZE 8192 /*%< large enough to store any key */
+/* DST_API control flags */
+/* These are used used in functions dst_sign_data and dst_verify_data */
+#define SIG_MODE_INIT 1 /*%< initialize digest */
+#define SIG_MODE_UPDATE 2 /*%< add data to digest */
+#define SIG_MODE_FINAL 4 /*%< generate/verify signature */
+#define SIG_MODE_ALL (SIG_MODE_INIT|SIG_MODE_UPDATE|SIG_MODE_FINAL)
+
+/* Flags for dst_read_private_key() */
+#define DST_FORCE_READ 0x1000000
+#define DST_CAN_SIGN 0x010F
+#define DST_NO_AUTHEN 0x8000
+#define DST_EXTEND_FLAG 0x1000
+#define DST_STANDARD 0
+#define DST_PRIVATE 0x2000000
+#define DST_PUBLIC 0x4000000
+#define DST_RAND_SEMI 1
+#define DST_RAND_STD 2
+#define DST_RAND_KEY 3
+#define DST_RAND_DSS 4
+
+
+/* DST algorithm codes */
+#define KEY_RSA 1
+#define KEY_DH 2
+#define KEY_DSA 3
+#define KEY_PRIVATE 254
+#define KEY_EXPAND 255
+#define KEY_HMAC_MD5 157
+#define KEY_HMAC_SHA1 158
+#define UNKNOWN_KEYALG 0
+#define DST_MAX_ALGS KEY_HMAC_SHA1
+
+/* DST constants to locations in KEY record changes in new KEY record */
+#define DST_FLAGS_SIZE 2
+#define DST_KEY_PROT 2
+#define DST_KEY_ALG 3
+#define DST_EXT_FLAG 4
+#define DST_KEY_START 4
+
+#ifndef SIGN_F_NOKEY
+#define SIGN_F_NOKEY 0xC000
+#endif
+
+/* error codes from dst routines */
+#define SIGN_INIT_FAILURE (-23)
+#define SIGN_UPDATE_FAILURE (-24)
+#define SIGN_FINAL_FAILURE (-25)
+#define VERIFY_INIT_FAILURE (-26)
+#define VERIFY_UPDATE_FAILURE (-27)
+#define VERIFY_FINAL_FAILURE (-28)
+#define MISSING_KEY_OR_SIGNATURE (-30)
+#define UNSUPPORTED_KEYALG (-31)
+
+#endif /* DST_H */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/isc/eventlib.h b/usr/src/lib/libresolv2_joy/include/isc/eventlib.h
new file mode 100644
index 0000000000..a4cfdf9092
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/eventlib.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1995-1999, 2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* eventlib.h - exported interfaces for eventlib
+ * vix 09sep95 [initial]
+ *
+ * $Id: eventlib.h,v 1.7 2008/11/14 02:36:51 marka Exp $
+ */
+
+#ifndef _EVENTLIB_H
+#define _EVENTLIB_H
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <stdio.h>
+
+#include <isc/platform.h>
+
+#ifndef __P
+# define __EVENTLIB_P_DEFINED
+# ifdef __STDC__
+# define __P(x) x
+# else
+# define __P(x) ()
+# endif
+#endif
+
+/* In the absence of branded types... */
+typedef struct { void *opaque; } evConnID;
+typedef struct { void *opaque; } evFileID;
+typedef struct { void *opaque; } evStreamID;
+typedef struct { void *opaque; } evTimerID;
+typedef struct { void *opaque; } evWaitID;
+typedef struct { void *opaque; } evContext;
+typedef struct { void *opaque; } evEvent;
+
+#define evInitID(id) ((id)->opaque = NULL)
+#define evTestID(id) ((id).opaque != NULL)
+
+typedef void (*evConnFunc)__P((evContext, void *, int, const void *, int,
+ const void *, int));
+typedef void (*evFileFunc)__P((evContext, void *, int, int));
+typedef void (*evStreamFunc)__P((evContext, void *, int, int));
+typedef void (*evTimerFunc)__P((evContext, void *,
+ struct timespec, struct timespec));
+typedef void (*evWaitFunc)__P((evContext, void *, const void *));
+
+typedef struct { unsigned char mask[256/8]; } evByteMask;
+#define EV_BYTEMASK_BYTE(b) ((b) / 8)
+#define EV_BYTEMASK_MASK(b) (1 << ((b) % 8))
+#define EV_BYTEMASK_SET(bm, b) \
+ ((bm).mask[EV_BYTEMASK_BYTE(b)] |= EV_BYTEMASK_MASK(b))
+#define EV_BYTEMASK_CLR(bm, b) \
+ ((bm).mask[EV_BYTEMASK_BYTE(b)] &= ~EV_BYTEMASK_MASK(b))
+#define EV_BYTEMASK_TST(bm, b) \
+ ((bm).mask[EV_BYTEMASK_BYTE(b)] & EV_BYTEMASK_MASK(b))
+
+#define EV_POLL 1
+#define EV_WAIT 2
+#define EV_NULL 4
+
+#define EV_READ 1
+#define EV_WRITE 2
+#define EV_EXCEPT 4
+
+#define EV_WASNONBLOCKING 8 /* Internal library use. */
+
+/* eventlib.c */
+#define evCreate __evCreate
+#define evSetDebug __evSetDebug
+#define evDestroy __evDestroy
+#define evGetNext __evGetNext
+#define evDispatch __evDispatch
+#define evDrop __evDrop
+#define evMainLoop __evMainLoop
+#define evHighestFD __evHighestFD
+#define evGetOption __evGetOption
+#define evSetOption __evSetOption
+
+int evCreate __P((evContext *));
+void evSetDebug __P((evContext, int, FILE *));
+int evDestroy __P((evContext));
+int evGetNext __P((evContext, evEvent *, int));
+int evDispatch __P((evContext, evEvent));
+void evDrop __P((evContext, evEvent));
+int evMainLoop __P((evContext));
+int evHighestFD __P((evContext));
+int evGetOption __P((evContext *, const char *, int *));
+int evSetOption __P((evContext *, const char *, int));
+
+/* ev_connects.c */
+#define evListen __evListen
+#define evConnect __evConnect
+#define evCancelConn __evCancelConn
+#define evHold __evHold
+#define evUnhold __evUnhold
+#define evTryAccept __evTryAccept
+
+int evListen __P((evContext, int, int, evConnFunc, void *, evConnID *));
+int evConnect __P((evContext, int, const void *, int,
+ evConnFunc, void *, evConnID *));
+int evCancelConn __P((evContext, evConnID));
+int evHold __P((evContext, evConnID));
+int evUnhold __P((evContext, evConnID));
+int evTryAccept __P((evContext, evConnID, int *));
+
+/* ev_files.c */
+#define evSelectFD __evSelectFD
+#define evDeselectFD __evDeselectFD
+
+int evSelectFD __P((evContext, int, int, evFileFunc, void *, evFileID *));
+int evDeselectFD __P((evContext, evFileID));
+
+/* ev_streams.c */
+#define evConsIovec __evConsIovec
+#define evWrite __evWrite
+#define evRead __evRead
+#define evTimeRW __evTimeRW
+#define evUntimeRW __evUntimeRW
+#define evCancelRW __evCancelRW
+
+struct iovec evConsIovec __P((void *, size_t));
+int evWrite __P((evContext, int, const struct iovec *, int,
+ evStreamFunc func, void *, evStreamID *));
+int evRead __P((evContext, int, const struct iovec *, int,
+ evStreamFunc func, void *, evStreamID *));
+int evTimeRW __P((evContext, evStreamID, evTimerID timer));
+int evUntimeRW __P((evContext, evStreamID));
+int evCancelRW __P((evContext, evStreamID));
+
+/* ev_timers.c */
+#define evConsTime __evConsTime
+#define evAddTime __evAddTime
+#define evSubTime __evSubTime
+#define evCmpTime __evCmpTime
+#define evTimeSpec __evTimeSpec
+#define evTimeVal __evTimeVal
+
+#define evNowTime __evNowTime
+#define evUTCTime __evUTCTime
+#define evLastEventTime __evLastEventTime
+#define evSetTimer __evSetTimer
+#define evClearTimer __evClearTimer
+#define evConfigTimer __evConfigTimer
+#define evResetTimer __evResetTimer
+#define evSetIdleTimer __evSetIdleTimer
+#define evClearIdleTimer __evClearIdleTimer
+#define evResetIdleTimer __evResetIdleTimer
+#define evTouchIdleTimer __evTouchIdleTimer
+
+struct timespec evConsTime __P((time_t sec, long nsec));
+struct timespec evAddTime __P((struct timespec, struct timespec));
+struct timespec evSubTime __P((struct timespec, struct timespec));
+struct timespec evNowTime __P((void));
+struct timespec evUTCTime __P((void));
+struct timespec evLastEventTime __P((evContext));
+struct timespec evTimeSpec __P((struct timeval));
+struct timeval evTimeVal __P((struct timespec));
+int evCmpTime __P((struct timespec, struct timespec));
+int evSetTimer __P((evContext, evTimerFunc, void *, struct timespec,
+ struct timespec, evTimerID *));
+int evClearTimer __P((evContext, evTimerID));
+int evConfigTimer __P((evContext, evTimerID, const char *param,
+ int value));
+int evResetTimer __P((evContext, evTimerID, evTimerFunc, void *,
+ struct timespec, struct timespec));
+int evSetIdleTimer __P((evContext, evTimerFunc, void *, struct timespec,
+ evTimerID *));
+int evClearIdleTimer __P((evContext, evTimerID));
+int evResetIdleTimer __P((evContext, evTimerID, evTimerFunc, void *,
+ struct timespec));
+int evTouchIdleTimer __P((evContext, evTimerID));
+
+/* ev_waits.c */
+#define evWaitFor __evWaitFor
+#define evDo __evDo
+#define evUnwait __evUnwait
+#define evDefer __evDefer
+
+int evWaitFor __P((evContext, const void *, evWaitFunc, void *, evWaitID *));
+int evDo __P((evContext, const void *));
+int evUnwait __P((evContext, evWaitID));
+int evDefer __P((evContext, evWaitFunc, void *));
+
+#ifdef __EVENTLIB_P_DEFINED
+# undef __P
+#endif
+
+#endif /*_EVENTLIB_H*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/isc/heap.h b/usr/src/lib/libresolv2_joy/include/isc/heap.h
new file mode 100644
index 0000000000..384d507cf5
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/heap.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1997,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+typedef int (*heap_higher_priority_func)(void *, void *);
+typedef void (*heap_index_func)(void *, int);
+typedef void (*heap_for_each_func)(void *, void *);
+
+typedef struct heap_context {
+ int array_size;
+ int array_size_increment;
+ int heap_size;
+ void **heap;
+ heap_higher_priority_func higher_priority;
+ heap_index_func index;
+} *heap_context;
+
+#define heap_new __heap_new
+#define heap_free __heap_free
+#define heap_insert __heap_insert
+#define heap_delete __heap_delete
+#define heap_increased __heap_increased
+#define heap_decreased __heap_decreased
+#define heap_element __heap_element
+#define heap_for_each __heap_for_each
+
+heap_context heap_new(heap_higher_priority_func, heap_index_func, int);
+int heap_free(heap_context);
+int heap_insert(heap_context, void *);
+int heap_delete(heap_context, int);
+int heap_increased(heap_context, int);
+int heap_decreased(heap_context, int);
+void * heap_element(heap_context, int);
+int heap_for_each(heap_context, heap_for_each_func, void *);
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/isc/irpmarshall.h b/usr/src/lib/libresolv2_joy/include/isc/irpmarshall.h
new file mode 100644
index 0000000000..244b3e3460
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/irpmarshall.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: irpmarshall.h,v 1.4 2005/04/27 04:56:17 sra Exp $
+ */
+
+#ifndef _IRPMARSHALL_H_INCLUDED
+#define _IRPMARSHALL_H_INCLUDED
+
+/* Hide function names */
+#define irp_marshall_gr __irp_marshall_gr
+#define irp_marshall_ho __irp_marshall_ho
+#define irp_marshall_ne __irp_marshall_ne
+#define irp_marshall_ng __irp_marshall_ng
+#define irp_marshall_nw __irp_marshall_nw
+#define irp_marshall_pr __irp_marshall_pr
+#define irp_marshall_pw __irp_marshall_pw
+#define irp_marshall_sv __irp_marshall_sv
+#define irp_unmarshall_gr __irp_unmarshall_gr
+#define irp_unmarshall_ho __irp_unmarshall_ho
+#define irp_unmarshall_ne __irp_unmarshall_ne
+#define irp_unmarshall_ng __irp_unmarshall_ng
+#define irp_unmarshall_nw __irp_unmarshall_nw
+#define irp_unmarshall_pr __irp_unmarshall_pr
+#define irp_unmarshall_pw __irp_unmarshall_pw
+#define irp_unmarshall_sv __irp_unmarshall_sv
+
+#define MAXPADDRSIZE (sizeof "255.255.255.255" + 1)
+#define ADDR_T_STR(x) (x == AF_INET ? "AF_INET" :\
+ (x == AF_INET6 ? "AF_INET6" : "UNKNOWN"))
+
+/* See comment below on usage */
+int irp_marshall_pw(const struct passwd *, char **, size_t *);
+int irp_unmarshall_pw(struct passwd *, char *);
+int irp_marshall_gr(const struct group *, char **, size_t *);
+int irp_unmarshall_gr(struct group *, char *);
+int irp_marshall_sv(const struct servent *, char **, size_t *);
+int irp_unmarshall_sv(struct servent *, char *);
+int irp_marshall_pr(struct protoent *, char **, size_t *);
+int irp_unmarshall_pr(struct protoent *, char *);
+int irp_marshall_ho(struct hostent *, char **, size_t *);
+int irp_unmarshall_ho(struct hostent *, char *);
+int irp_marshall_ng(const char *, const char *, const char *,
+ char **, size_t *);
+int irp_unmarshall_ng(const char **, const char **, const char **, char *);
+int irp_marshall_nw(struct nwent *, char **, size_t *);
+int irp_unmarshall_nw(struct nwent *, char *);
+int irp_marshall_ne(struct netent *, char **, size_t *);
+int irp_unmarshall_ne(struct netent *, char *);
+
+/*! \file
+ * \brief
+ * Functions to marshall and unmarshall various system data structures. We
+ * use a printable ascii format that is as close to various system config
+ * files as reasonable (e.g. /etc/passwd format).
+ *
+ * We are not forgiving with unmarhsalling misformatted buffers. In
+ * particular whitespace in fields is not ignored. So a formatted password
+ * entry "brister :1364:100:...." will yield a username of "brister "
+ *
+ * We potentially do a lot of mallocs to fill fields that are of type
+ * (char **) like a hostent h_addr field. Building (for example) the
+ * h_addr field and its associated addresses all in one buffer is
+ * certainly possible, but not done here.
+ *
+ * The following description is true for all the marshalling functions:
+ *
+ * int irp_marshall_XX(struct yyyy *XX, char **buffer, size_t *len);
+ *
+ * The argument XX (of type struct passwd for example) is marshalled in the
+ * buffer pointed at by *BUFFER, which is of length *LEN. Returns 0
+ * on success and -1 on failure. Failure will occur if *LEN is
+ * smaller than needed.
+ *
+ * If BUFFER is NULL, then *LEN is set to the size of the buffer
+ * needed to marshall the data and no marshalling is actually done.
+ *
+ * If *BUFFER is NULL, then a buffer large enough will be allocated
+ * with memget() and the size allocated will be stored in *LEN. An extra 2
+ * bytes will be allocated for the client to append CRLF if wanted. The
+ * value of *LEN will include these two bytes.
+ *
+ * All the marshalling functions produce a buffer with the fields
+ * separated by colons (except for the hostent marshalling, which uses '@'
+ * to separate fields). Fields that have multiple subfields (like the
+ * gr_mem field in struct group) have their subparts separated by
+ * commas.
+ *
+ * int irp_unmarshall_XX(struct YYYYY *XX, char *buffer);
+ *
+ * The unmashalling functions break apart the buffer and store the
+ * values in the struct pointed to by XX. All pointer values inside
+ * XX are allocated with malloc. All arrays of pointers have a NULL
+ * as the last element.
+ */
+
+#endif
diff --git a/usr/src/lib/libresolv2_joy/include/isc/list.h b/usr/src/lib/libresolv2_joy/include/isc/list.h
new file mode 100644
index 0000000000..5fe9031141
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/list.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1997,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LIST_H
+#define LIST_H 1
+#include <isc/assertions.h>
+
+#define LIST(type) struct { type *head, *tail; }
+#define INIT_LIST(list) \
+ do { (list).head = NULL; (list).tail = NULL; } while (0)
+
+#define LINK(type) struct { type *prev, *next; }
+#define INIT_LINK_TYPE(elt, link, type) \
+ do { \
+ (elt)->link.prev = (type *)(-1); \
+ (elt)->link.next = (type *)(-1); \
+ } while (0)
+#define INIT_LINK(elt, link) \
+ INIT_LINK_TYPE(elt, link, void)
+#define LINKED(elt, link) ((void *)((elt)->link.prev) != (void *)(-1) && \
+ (void *)((elt)->link.next) != (void *)(-1))
+
+#define HEAD(list) ((list).head)
+#define TAIL(list) ((list).tail)
+#define EMPTY(list) ((list).head == NULL)
+
+#define PREPEND(list, elt, link) \
+ do { \
+ INSIST(!LINKED(elt, link));\
+ if ((list).head != NULL) \
+ (list).head->link.prev = (elt); \
+ else \
+ (list).tail = (elt); \
+ (elt)->link.prev = NULL; \
+ (elt)->link.next = (list).head; \
+ (list).head = (elt); \
+ } while (0)
+
+#define APPEND(list, elt, link) \
+ do { \
+ INSIST(!LINKED(elt, link));\
+ if ((list).tail != NULL) \
+ (list).tail->link.next = (elt); \
+ else \
+ (list).head = (elt); \
+ (elt)->link.prev = (list).tail; \
+ (elt)->link.next = NULL; \
+ (list).tail = (elt); \
+ } while (0)
+
+#define UNLINK_TYPE(list, elt, link, type) \
+ do { \
+ INSIST(LINKED(elt, link));\
+ if ((elt)->link.next != NULL) \
+ (elt)->link.next->link.prev = (elt)->link.prev; \
+ else { \
+ INSIST((list).tail == (elt)); \
+ (list).tail = (elt)->link.prev; \
+ } \
+ if ((elt)->link.prev != NULL) \
+ (elt)->link.prev->link.next = (elt)->link.next; \
+ else { \
+ INSIST((list).head == (elt)); \
+ (list).head = (elt)->link.next; \
+ } \
+ INIT_LINK_TYPE(elt, link, type); \
+ } while (0)
+#define UNLINK(list, elt, link) \
+ UNLINK_TYPE(list, elt, link, void)
+
+#define PREV(elt, link) ((elt)->link.prev)
+#define NEXT(elt, link) ((elt)->link.next)
+
+#define INSERT_BEFORE(list, before, elt, link) \
+ do { \
+ INSIST(!LINKED(elt, link));\
+ if ((before)->link.prev == NULL) \
+ PREPEND(list, elt, link); \
+ else { \
+ (elt)->link.prev = (before)->link.prev; \
+ (before)->link.prev = (elt); \
+ (elt)->link.prev->link.next = (elt); \
+ (elt)->link.next = (before); \
+ } \
+ } while (0)
+
+#define INSERT_AFTER(list, after, elt, link) \
+ do { \
+ INSIST(!LINKED(elt, link));\
+ if ((after)->link.next == NULL) \
+ APPEND(list, elt, link); \
+ else { \
+ (elt)->link.next = (after)->link.next; \
+ (after)->link.next = (elt); \
+ (elt)->link.next->link.prev = (elt); \
+ (elt)->link.prev = (after); \
+ } \
+ } while (0)
+
+#define ENQUEUE(list, elt, link) APPEND(list, elt, link)
+#define DEQUEUE(list, elt, link) UNLINK(list, elt, link)
+
+#endif /* LIST_H */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/isc/logging.h b/usr/src/lib/libresolv2_joy/include/isc/logging.h
new file mode 100644
index 0000000000..c539443ff8
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/logging.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996-1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef LOGGING_H
+#define LOGGING_H
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#define log_critical (-5)
+#define log_error (-4)
+#define log_warning (-3)
+#define log_notice (-2)
+#define log_info (-1)
+#define log_debug(level) (level)
+
+typedef enum { log_syslog, log_file, log_null } log_channel_type;
+
+#define LOG_MAX_VERSIONS 99
+
+#define LOG_CLOSE_STREAM 0x0001
+#define LOG_TIMESTAMP 0x0002
+#define LOG_TRUNCATE 0x0004
+#define LOG_USE_CONTEXT_LEVEL 0x0008
+#define LOG_PRINT_LEVEL 0x0010
+#define LOG_REQUIRE_DEBUG 0x0020
+#define LOG_CHANNEL_BROKEN 0x0040
+#define LOG_PRINT_CATEGORY 0x0080
+#define LOG_CHANNEL_OFF 0x0100
+
+typedef struct log_context *log_context;
+typedef struct log_channel *log_channel;
+
+#define LOG_OPTION_DEBUG 0x01
+#define LOG_OPTION_LEVEL 0x02
+
+#define log_open_stream __log_open_stream
+#define log_close_stream __log_close_stream
+#define log_get_stream __log_get_stream
+#define log_get_filename __log_get_filename
+#define log_check_channel __log_check_channel
+#define log_check __log_check
+#define log_vwrite __log_vwrite
+#define log_write __log_write
+#define log_new_context __log_new_context
+#define log_free_context __log_free_context
+#define log_add_channel __log_add_channel
+#define log_remove_channel __log_remove_channel
+#define log_option __log_option
+#define log_category_is_active __log_category_is_active
+#define log_new_syslog_channel __log_new_syslog_channel
+#define log_new_file_channel __log_new_file_channel
+#define log_set_file_owner __log_set_file_owner
+#define log_new_null_channel __log_new_null_channel
+#define log_inc_references __log_inc_references
+#define log_dec_references __log_dec_references
+#define log_get_channel_type __log_get_channel_type
+#define log_free_channel __log_free_channel
+#define log_close_debug_channels __log_close_debug_channels
+
+FILE * log_open_stream(log_channel);
+int log_close_stream(log_channel);
+FILE * log_get_stream(log_channel);
+char * log_get_filename(log_channel);
+int log_check_channel(log_context, int, log_channel);
+int log_check(log_context, int, int);
+#ifdef __GNUC__
+void log_vwrite(log_context, int, int, const char *,
+ va_list args)
+ __attribute__((__format__(__printf__, 4, 0)));
+void log_write(log_context, int, int, const char *, ...)
+ __attribute__((__format__(__printf__, 4, 5)));
+#else
+void log_vwrite(log_context, int, int, const char *,
+ va_list args);
+void log_write(log_context, int, int, const char *, ...);
+#endif
+int log_new_context(int, char **, log_context *);
+void log_free_context(log_context);
+int log_add_channel(log_context, int, log_channel);
+int log_remove_channel(log_context, int, log_channel);
+int log_option(log_context, int, int);
+int log_category_is_active(log_context, int);
+log_channel log_new_syslog_channel(unsigned int, int, int);
+log_channel log_new_file_channel(unsigned int, int, const char *,
+ FILE *, unsigned int,
+ unsigned long);
+int log_set_file_owner(log_channel, uid_t, gid_t);
+log_channel log_new_null_channel(void);
+int log_inc_references(log_channel);
+int log_dec_references(log_channel);
+log_channel_type log_get_channel_type(log_channel);
+int log_free_channel(log_channel);
+void log_close_debug_channels(log_context);
+
+#endif /* !LOGGING_H */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/isc/memcluster.h b/usr/src/lib/libresolv2_joy/include/isc/memcluster.h
new file mode 100644
index 0000000000..0923deb5e7
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/memcluster.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1997,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MEMCLUSTER_H
+#define MEMCLUSTER_H
+
+#include <stdio.h>
+
+#define meminit __meminit
+#ifdef MEMCLUSTER_DEBUG
+#define memget(s) __memget_debug(s, __FILE__, __LINE__)
+#define memput(p, s) __memput_debug(p, s, __FILE__, __LINE__)
+#else /*MEMCLUSTER_DEBUG*/
+#ifdef MEMCLUSTER_RECORD
+#define memget(s) __memget_record(s, __FILE__, __LINE__)
+#define memput(p, s) __memput_record(p, s, __FILE__, __LINE__)
+#else /*MEMCLUSTER_RECORD*/
+#define memget __memget
+#define memput __memput
+#endif /*MEMCLUSTER_RECORD*/
+#endif /*MEMCLUSTER_DEBUG*/
+#define memstats __memstats
+#define memactive __memactive
+
+int meminit(size_t, size_t);
+void * __memget(size_t);
+void __memput(void *, size_t);
+void * __memget_debug(size_t, const char *, int);
+void __memput_debug(void *, size_t, const char *, int);
+void * __memget_record(size_t, const char *, int);
+void __memput_record(void *, size_t, const char *, int);
+void memstats(FILE *);
+int memactive(void);
+
+#endif /* MEMCLUSTER_H */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/isc/misc.h b/usr/src/lib/libresolv2_joy/include/isc/misc.h
new file mode 100644
index 0000000000..b54f4ee6ed
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/misc.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1995-2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: misc.h,v 1.7 2008/11/14 02:36:51 marka Exp $
+ */
+
+#ifndef _ISC_MISC_H
+#define _ISC_MISC_H
+
+/*! \file */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#define bitncmp __bitncmp
+/*#define isc_movefile __isc_movefile */
+
+extern int bitncmp(const void *, const void *, int);
+extern int isc_movefile(const char *, const char *);
+
+extern int isc_gethexstring(unsigned char *, size_t, int, FILE *,
+ int *);
+extern void isc_puthexstring(FILE *, const unsigned char *, size_t,
+ size_t, size_t, const char *);
+extern void isc_tohex(const unsigned char *, size_t, char *);
+
+#endif /*_ISC_MISC_H*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/isc/platform.h b/usr/src/lib/libresolv2_joy/include/isc/platform.h
new file mode 100644
index 0000000000..2fc59b61a8
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/platform.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (C) 2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: platform.h.in,v 1.3 2008/01/23 02:15:56 tbox Exp $ */
+
+/*! \file */
+
+#ifndef ISC_PLATFORM_H
+#define ISC_PLATFORM_H
+
+/*
+ * Define if the OS does not define struct timespec.
+ */
+#undef ISC_PLATFORM_NEEDTIMESPEC
+#ifdef ISC_PLATFORM_NEEDTIMESPEC
+#include <time.h> /* For time_t */
+struct timespec {
+ time_t tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+};
+#endif
+
+#endif
diff --git a/usr/src/lib/libresolv2_joy/include/isc/tree.h b/usr/src/lib/libresolv2_joy/include/isc/tree.h
new file mode 100644
index 0000000000..96feaca68d
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/isc/tree.h
@@ -0,0 +1,59 @@
+/* tree.h - declare structures used by tree library
+ *
+ * vix 22jan93 [revisited; uses RCS, ANSI, POSIX; has bug fixes]
+ * vix 27jun86 [broken out of tree.c]
+ *
+ * $Id: tree.h,v 1.3 2005/04/27 04:56:18 sra Exp $
+ */
+
+
+#ifndef _TREE_H_INCLUDED
+#define _TREE_H_INCLUDED
+
+
+#ifndef __P
+# if defined(__STDC__) || defined(__GNUC__)
+# define __P(x) x
+# else
+# define __P(x) ()
+# endif
+#endif
+
+/*%
+ * tree_t is our package-specific anonymous pointer.
+ */
+#if defined(__STDC__) || defined(__GNUC__)
+typedef void *tree_t;
+#else
+typedef char *tree_t;
+#endif
+
+/*%
+ * Do not taint namespace
+ */
+#define tree_add __tree_add
+#define tree_delete __tree_delete
+#define tree_init __tree_init
+#define tree_mung __tree_mung
+#define tree_srch __tree_srch
+#define tree_trav __tree_trav
+
+
+typedef struct tree_s {
+ tree_t data;
+ struct tree_s *left, *right;
+ short bal;
+ }
+ tree;
+
+
+void tree_init __P((tree **));
+tree_t tree_srch __P((tree **, int (*)(), tree_t));
+tree_t tree_add __P((tree **, int (*)(), tree_t, void (*)()));
+int tree_delete __P((tree **, int (*)(), tree_t, void (*)()));
+int tree_trav __P((tree **, int (*)()));
+void tree_mung __P((tree **, void (*)()));
+
+
+#endif /* _TREE_H_INCLUDED */
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/make_os_version b/usr/src/lib/libresolv2_joy/include/make_os_version
new file mode 100755
index 0000000000..3654490fee
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/make_os_version
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+UNAME_R=`/usr/bin/uname -r`
+
+OS_MAJOR=`echo $UNAME_R | /usr/bin/sed -e 's/^\([^.]*\).*/\1/'`
+OS_MINOR=`echo $UNAME_R | /usr/bin/sed -e 's/^[^.]*\.\([^.]*\).*/\1/'`
+OS_VERSION=`echo $UNAME_R | tr '.' '_'`
+
+cat <<EOF > new_os_version.h
+#ifndef OS_VERSION_H
+#define OS_VERSION_H
+
+#define SUNOS_$OS_VERSION
+#define OS_MAJOR $OS_MAJOR
+#define OS_MINOR $OS_MINOR
+
+#endif
+EOF
+
+if [ -f os_version.h ]; then
+ if /usr/bin/cmp -s new_os_version.h os_version.h; then
+ /usr/bin/rm -f new_os_version.h
+ else
+ /usr/bin/rm -f os_version.h
+ /usr/bin/mv new_os_version.h os_version.h
+ fi
+else
+ /usr/bin/mv new_os_version.h os_version.h
+fi
diff --git a/usr/src/lib/libresolv2_joy/include/make_os_version.sh b/usr/src/lib/libresolv2_joy/include/make_os_version.sh
new file mode 100644
index 0000000000..3654490fee
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/make_os_version.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# Copyright (c) 1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+UNAME_R=`/usr/bin/uname -r`
+
+OS_MAJOR=`echo $UNAME_R | /usr/bin/sed -e 's/^\([^.]*\).*/\1/'`
+OS_MINOR=`echo $UNAME_R | /usr/bin/sed -e 's/^[^.]*\.\([^.]*\).*/\1/'`
+OS_VERSION=`echo $UNAME_R | tr '.' '_'`
+
+cat <<EOF > new_os_version.h
+#ifndef OS_VERSION_H
+#define OS_VERSION_H
+
+#define SUNOS_$OS_VERSION
+#define OS_MAJOR $OS_MAJOR
+#define OS_MINOR $OS_MINOR
+
+#endif
+EOF
+
+if [ -f os_version.h ]; then
+ if /usr/bin/cmp -s new_os_version.h os_version.h; then
+ /usr/bin/rm -f new_os_version.h
+ else
+ /usr/bin/rm -f os_version.h
+ /usr/bin/mv new_os_version.h os_version.h
+ fi
+else
+ /usr/bin/mv new_os_version.h os_version.h
+fi
diff --git a/usr/src/lib/libresolv2_joy/include/port_after.h b/usr/src/lib/libresolv2_joy/include/port_after.h
new file mode 100644
index 0000000000..c3abf4b334
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/port_after.h
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: port_after.h.in,v 1.60 2008/02/28 05:34:17 marka Exp $ */
+
+#ifndef port_after_h
+#define port_after_h
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#if (!defined(BSD)) || (BSD < 199306)
+#include <sys/bitypes.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif /* HAVE_SYS_SELECT_H */
+
+#ifdef REENABLE_SEND
+#undef send
+#endif
+
+#undef NEED_PSELECT
+#undef HAVE_SA_LEN
+#undef HAVE_MINIMUM_IFREQ
+#define NEED_DAEMON 1
+#undef NEED_STRSEP
+#undef NEED_STRERROR
+#ifdef NEED_STRERROR
+const char *isc_strerror(int);
+#define strerror isc_strerror
+#endif
+/* HAS_INET6_STRUCTS and HAVE_SIN6_SCOPE_ID are defined by port_ipv6.h
+ * #define HAS_INET6_STRUCTS 1
+ * #define HAVE_SIN6_SCOPE_ID 1
+ */
+#include <port_ipv6.h>
+
+#undef NEED_IN6ADDR_ANY
+#undef HAS_IN_ADDR6
+#define HAVE_SOCKADDR_STORAGE 1
+#undef NEED_GETTIMEOFDAY
+#define HAVE_STRNDUP
+#undef USE_FIONBIO_IOCTL
+#undef INNETGR_ARGS
+
+#undef USE_IFNAMELINKID
+#define PORT_NONBLOCK O_NONBLOCK
+
+#ifndef _POSIX_PATH_MAX
+#define _POSIX_PATH_MAX 255
+#endif
+#ifndef PATH_MAX
+#define PATH_MAX _POSIX_PATH_MAX
+#endif
+
+/*
+ * We need to know the IPv6 address family number even on IPv4-only systems.
+ * Note that this is NOT a protocol constant, and that if the system has its
+ * own AF_INET6, different from ours below, all of BIND's libraries and
+ * executables will need to be recompiled after the system <sys/socket.h>
+ * has had this type added. The type number below is correct on most BSD-
+ * derived systems for which AF_INET6 is defined.
+ */
+#ifndef AF_INET6
+#define AF_INET6 24
+#endif
+
+#ifndef PF_INET6
+#define PF_INET6 AF_INET6
+#endif
+
+#ifdef HAS_IN_ADDR6
+/* Map to pre-RFC structure. */
+#define in6_addr in_addr6
+#endif
+
+#ifndef HAS_INET6_STRUCTS
+/* Replace with structure from later rev of O/S if known. */
+struct in6_addr {
+ u_int8_t s6_addr[16];
+};
+
+#define IN6ADDR_ANY_INIT \
+ {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}
+
+#define IN6ADDR_LOOPBACK_INIT \
+ {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}
+
+/* Replace with structure from later rev of O/S if known. */
+struct sockaddr_in6 {
+#ifdef HAVE_SA_LEN
+ u_int8_t sin6_len; /* length of this struct */
+ u_int8_t sin6_family; /* AF_INET6 */
+#else
+ u_int16_t sin6_family; /* AF_INET6 */
+#endif
+ u_int16_t sin6_port; /* transport layer port # */
+ u_int32_t sin6_flowinfo; /* IPv6 flow information */
+ struct in6_addr sin6_addr; /* IPv6 address */
+ u_int32_t sin6_scope_id; /* set of interfaces for a scope */
+};
+#endif /* HAS_INET6_STRUCTS */
+
+#ifdef BROKEN_IN6ADDR_INIT_MACROS
+#undef IN6ADDR_ANY_INIT
+#undef IN6ADDR_LOOPBACK_INIT
+#endif
+
+#ifdef _AIX
+#ifndef IN6ADDR_ANY_INIT
+#define IN6ADDR_ANY_INIT {{{ 0, 0, 0, 0 }}}
+#endif
+#ifndef IN6ADDR_LOOPBACK_INIT
+#if BYTE_ORDER == BIG_ENDIAN
+#define IN6ADDR_LOOPBACK_INIT {{{ 0, 0, 0, 1 }}}
+#else
+#define IN6ADDR_LOOPBACK_INIT {{{0, 0, 0, 0x01000000}}}
+#endif
+#endif
+#endif
+
+#ifndef IN6ADDR_ANY_INIT
+#ifdef s6_addr
+#define IN6ADDR_ANY_INIT \
+ {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}
+#else
+#define IN6ADDR_ANY_INIT \
+ {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}
+#endif
+
+#endif
+#ifndef IN6ADDR_LOOPBACK_INIT
+#ifdef s6_addr
+#define IN6ADDR_LOOPBACK_INIT \
+ {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}
+#else
+#define IN6ADDR_LOOPBACK_INIT \
+ {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}
+#endif
+#endif
+
+#ifndef HAVE_SOCKADDR_STORAGE
+#define __SS_MAXSIZE 128
+#define __SS_ALLIGSIZE (sizeof (long))
+
+struct sockaddr_storage {
+#ifdef HAVE_SA_LEN
+ u_int8_t ss_len; /* address length */
+ u_int8_t ss_family; /* address family */
+ char __ss_pad1[__SS_ALLIGSIZE - 2 * sizeof(u_int8_t)];
+ long __ss_align;
+ char __ss_pad2[__SS_MAXSIZE - 2 * __SS_ALLIGSIZE];
+#else
+ u_int16_t ss_family; /* address family */
+ char __ss_pad1[__SS_ALLIGSIZE - sizeof(u_int16_t)];
+ long __ss_align;
+ char __ss_pad2[__SS_MAXSIZE - 2 * __SS_ALLIGSIZE];
+#endif
+};
+#endif
+
+
+#if !defined(HAS_INET6_STRUCTS) || defined(NEED_IN6ADDR_ANY)
+#define in6addr_any isc_in6addr_any
+extern const struct in6_addr in6addr_any;
+#endif
+
+/*
+ * IN6_ARE_ADDR_EQUAL, IN6_IS_ADDR_UNSPECIFIED, IN6_IS_ADDR_V4COMPAT and
+ * IN6_IS_ADDR_V4MAPPED are broken in glibc 2.1.
+ */
+#ifdef __GLIBC__
+#if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 2)
+#undef IN6_ARE_ADDR_EQUAL
+#undef IN6_IS_ADDR_UNSPECIFIED
+#undef IN6_IS_ADDR_V4COMPAT
+#undef IN6_IS_ADDR_V4MAPPED
+#endif
+#endif
+
+#ifndef IN6_ARE_ADDR_EQUAL
+#define IN6_ARE_ADDR_EQUAL(a,b) \
+ (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0)
+#endif
+
+#ifndef IN6_IS_ADDR_UNSPECIFIED
+#define IN6_IS_ADDR_UNSPECIFIED(a) \
+ IN6_ARE_ADDR_EQUAL(a, &in6addr_any)
+#endif
+
+#ifndef IN6_IS_ADDR_LOOPBACK
+extern const struct in6_addr isc_in6addr_loopback;
+#define IN6_IS_ADDR_LOOPBACK(a) \
+ IN6_ARE_ADDR_EQUAL(a, &isc_in6addr_loopback)
+#endif
+
+#ifndef IN6_IS_ADDR_V4MAPPED
+#define IN6_IS_ADDR_V4MAPPED(a) \
+ ((a)->s6_addr[0] == 0x00 && (a)->s6_addr[1] == 0x00 && \
+ (a)->s6_addr[2] == 0x00 && (a)->s6_addr[3] == 0x00 && \
+ (a)->s6_addr[4] == 0x00 && (a)->s6_addr[5] == 0x00 && \
+ (a)->s6_addr[6] == 0x00 && (a)->s6_addr[9] == 0x00 && \
+ (a)->s6_addr[8] == 0x00 && (a)->s6_addr[9] == 0x00 && \
+ (a)->s6_addr[10] == 0xff && (a)->s6_addr[11] == 0xff)
+#endif
+
+#ifndef IN6_IS_ADDR_SITELOCAL
+#define IN6_IS_ADDR_SITELOCAL(a) \
+ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0))
+#endif
+
+#ifndef IN6_IS_ADDR_LINKLOCAL
+#define IN6_IS_ADDR_LINKLOCAL(a) \
+ (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80))
+#endif
+
+#ifndef IN6_IS_ADDR_MULTICAST
+#define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xff)
+#endif
+
+#ifndef __IPV6_ADDR_MC_SCOPE
+#define __IPV6_ADDR_MC_SCOPE(a) ((a)->s6_addr[1] & 0x0f)
+#endif
+
+#ifndef __IPV6_ADDR_SCOPE_SITELOCAL
+#define __IPV6_ADDR_SCOPE_SITELOCAL 0x05
+#endif
+#ifndef __IPV6_ADDR_SCOPE_ORGLOCAL
+#define __IPV6_ADDR_SCOPE_ORGLOCAL 0x08
+#endif
+
+#ifndef IN6_IS_ADDR_MC_SITELOCAL
+#define IN6_IS_ADDR_MC_SITELOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL))
+#endif
+
+#ifndef IN6_IS_ADDR_MC_ORGLOCAL
+#define IN6_IS_ADDR_MC_ORGLOCAL(a) \
+ (IN6_IS_ADDR_MULTICAST(a) && \
+ (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL))
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+#ifndef INET6_ADDRSTRLEN
+/* sizeof("aaaa:bbbb:cccc:dddd:eeee:ffff:123.123.123.123") */
+#define INET6_ADDRSTRLEN 46
+#endif
+
+#ifndef MIN
+#define MIN(x,y) (((x) <= (y)) ? (x) : (y))
+#endif
+
+#ifndef MAX
+#define MAX(x,y) (((x) >= (y)) ? (x) : (y))
+#endif
+
+#ifdef NEED_DAEMON
+int daemon(int nochdir, int noclose);
+#endif
+
+#ifdef NEED_STRSEP
+char * strsep(char **stringp, const char *delim);
+#endif
+
+#ifndef ALIGN
+#define ALIGN(p) (((uintptr_t)(p) + (sizeof(long) - 1)) & ~(sizeof(long) - 1))
+#endif
+
+#ifdef NEED_SETGROUPENT
+int setgroupent(int stayopen);
+#endif
+
+#ifdef NEED_GETGROUPLIST
+int getgrouplist(GETGROUPLIST_ARGS);
+#endif
+
+#ifdef POSIX_GETGRNAM_R
+int
+__posix_getgrnam_r(const char *, struct group *, char *, int, struct group **);
+#endif
+
+#ifdef NEED_GETGRNAM_R
+int
+getgrnam_r(const char *, struct group *, char *, size_t, struct group **);
+#endif
+
+#ifdef POSIX_GETGRGID_R
+int
+__posix_getgrgid_r(gid_t, struct group *, char *, int, struct group **) ;
+#endif
+
+#ifdef NEED_GETGRGID_R
+int
+getgrgid_r(gid_t, struct group *, char *, size_t, struct group **);
+#endif
+
+#ifdef NEED_GETGRENT_R
+GROUP_R_RETURN getgrent_r(struct group *gptr, GROUP_R_ARGS);
+#endif
+
+#ifdef NEED_SETGRENT_R
+GROUP_R_SET_RETURN setgrent_r(GROUP_R_ENT_ARGS);
+#endif
+
+#ifdef NEED_ENDGRENT_R
+GROUP_R_END_RETURN endgrent_r(GROUP_R_ENT_ARGS);
+#endif
+
+#if defined(NEED_INNETGR_R) && defined(NGR_R_RETURN)
+NGR_R_RETURN
+innetgr_r(const char *, const char *, const char *, const char *);
+#endif
+
+#ifdef NEED_SETNETGRENT_R
+#ifdef NGR_R_SET_ARGS
+NGR_R_SET_RETURN setnetgrent_r(NGR_R_SET_CONST char *netgroup, NGR_R_SET_ARGS);
+#else
+NGR_R_SET_RETURN setnetgrent_r(NGR_R_SET_CONST char *netgroup);
+#endif
+#endif
+
+#ifdef NEED_ENDNETGRENT_R
+#ifdef NGR_R_END_ARGS
+NGR_R_END_RETURN endnetgrent_r(NGR_R_END_ARGS);
+#else
+NGR_R_END_RETURN endnetgrent_r(void);
+#endif
+#endif
+
+#ifdef POSIX_GETPWNAM_R
+int
+__posix_getpwnam_r(const char *login, struct passwd *pwptr,
+ char *buf, size_t buflen, struct passwd **result);
+#endif
+
+#ifdef NEED_GETPWNAM_R
+int
+getpwnam_r(const char *login, struct passwd *pwptr,
+ char *buf, size_t buflen, struct passwd **result);
+#endif
+
+#ifdef POSIX_GETPWUID_R
+int
+__posix_getpwuid_r(uid_t uid, struct passwd *pwptr,
+ char *buf, int buflen, struct passwd **result);
+#endif
+
+#ifdef NEED_GETPWUID_R
+int
+getpwuid_r(uid_t uid, struct passwd *pwptr,
+ char *buf, size_t buflen, struct passwd **result);
+#endif
+
+#ifdef NEED_SETPWENT_R
+#ifdef PASS_R_ENT_ARGS
+PASS_R_SET_RETURN setpwent_r(PASS_R_ENT_ARGS);
+#else
+PASS_R_SET_RETURN setpwent_r(void);
+#endif
+
+#endif
+
+#ifdef NEED_SETPASSENT_R
+#ifdef PASS_R_ENT_ARGS
+PASS_R_SET_RETURN setpassent_r(int stayopen, PASS_R_ENT_ARGS);
+#else
+PASS_R_SET_RETURN setpassent_r(int stayopen);
+#endif
+#endif
+
+#ifdef NEED_GETPWENT_R
+PASS_R_RETURN getpwent_r(struct passwd *pwptr, PASS_R_ARGS);
+#endif
+
+#ifdef NEED_ENDPWENT_R
+void endpwent_r(void);
+#endif
+
+#ifdef NEED_SETPASSENT
+int setpassent(int stayopen);
+#endif
+
+#define gettimeofday isc__gettimeofday
+#ifdef NEED_GETTIMEOFDAY
+int isc__gettimeofday(struct timeval *tvp, struct _TIMEZONE *tzp);
+#else
+int isc__gettimeofday(struct timeval *tp, struct timezone *tzp);
+#endif
+
+int getnetgrent(NGR_R_CONST char **machinep, NGR_R_CONST char **userp,
+ NGR_R_CONST char **domainp);
+
+#ifdef NGR_R_ARGS
+int getnetgrent_r(NGR_R_CONST char **machinep, NGR_R_CONST char **userp,
+ NGR_R_CONST char **domainp, NGR_R_ARGS);
+#endif
+
+/* setnetgrent and endnetgrent are defined in sunw_port_after.h
+#ifdef SETNETGRENT_ARGS
+void setnetgrent(SETNETGRENT_ARGS);
+#else
+void setnetgrent(const char *netgroup);
+#endif
+
+void endnetgrent(void);
+*/
+
+#ifdef INNETGR_ARGS
+int innetgr(INNETGR_ARGS);
+#else
+int innetgr(const char *netgroup, const char *machine,
+ const char *user, const char *domain);
+#endif
+
+#ifdef NGR_R_SET_ARGS
+NGR_R_SET_RETURN
+setnetgrent_r(NGR_R_SET_CONST char *netgroup, NGR_R_SET_ARGS);
+#else
+NGR_R_SET_RETURN
+setnetgrent_r(NGR_R_SET_CONST char *netgroup);
+#endif
+
+#ifdef NEED_STRTOUL
+unsigned long strtoul(const char *, char **, int);
+#endif
+
+#ifdef NEED_SUN4PROTOS
+#include <stdarg.h>
+#ifndef __SIZE_TYPE__
+#define __SIZE_TYPE__ int
+#endif
+struct sockaddr;
+struct iovec;
+struct timeval;
+struct timezone;
+int fprintf(FILE *, const char *, ...);
+int getsockname(int, struct sockaddr *, int *);
+int getpeername(int, struct sockaddr *, int *);
+int socket(int, int, int);
+int connect(int, const struct sockaddr *, int);
+int writev(int, struct iovec *, int);
+int readv(int, struct iovec *, int);
+int send(int, const char *, int, int);
+void bzero(char *, int);
+int recvfrom(int, char *, int, int, struct sockaddr *, int *);
+int syslog(int, const char *, ... );
+int printf(const char *, ...);
+__SIZE_TYPE__ fread(void *, __SIZE_TYPE__, __SIZE_TYPE__, FILE *);
+__SIZE_TYPE__ fwrite(const void *, __SIZE_TYPE__, __SIZE_TYPE__, FILE *);
+int fclose(FILE *);
+int ungetc(int, FILE *);
+int scanf(const char *, ...);
+int sscanf(const char *, const char *, ... );
+int tolower(int);
+int toupper(int);
+int strcasecmp(const char *, const char *);
+int strncasecmp(const char *, const char *, int);
+int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
+#ifdef gettimeofday
+#undef gettimeofday
+int gettimeofday(struct timeval *, struct timezone *);
+#define gettimeofday isc__gettimeofday
+#else
+int gettimeofday(struct timeval *, struct timezone *);
+#endif
+long strtol(const char*, char **, int);
+int fseek(FILE *, long, int);
+int setsockopt(int, int, int, const char *, int);
+int bind(int, const struct sockaddr *, int);
+void bcopy(char *, char *, int);
+int fputc(char, FILE *);
+int listen(int, int);
+int accept(int, struct sockaddr *, int *);
+int getsockopt(int, int, int, char *, int *);
+int vfprintf(FILE *, const char *, va_list);
+int fflush(FILE *);
+int fgetc(FILE *);
+int fputs(const char *, FILE *);
+int fchown(int, int, int);
+void setbuf(FILE *, char *);
+int gethostname(char *, int);
+int rename(const char *, const char *);
+time_t time(time_t *);
+int fscanf(FILE *, const char *, ...);
+int sscanf(const char *, const char *, ...);
+int ioctl(int, int, caddr_t);
+void perror(const char *);
+
+#if !defined(__USE_FIXED_PROTOTYPES__) && !defined(__cplusplus) && !defined(__STRICT_ANSI__)
+/*
+ * 'gcc -ansi' changes the prototype for vsprintf().
+ * Use this prototype when 'gcc -ansi' is not in effect.
+ */
+char *vsprintf(char *, const char *, va_list);
+#endif
+#endif
+
+/* Solaris-specific changes */
+#include "sunw_port_after.h"
+
+#endif /* port_after_h */
diff --git a/usr/src/lib/libresolv2_joy/include/port_before.h b/usr/src/lib/libresolv2_joy/include/port_before.h
new file mode 100644
index 0000000000..2801139223
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/port_before.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (C) 2005-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: port_before.h.in,v 1.31 2008/02/28 05:36:10 marka Exp $ */
+
+#ifndef port_before_h
+#define port_before_h
+/* Solaris-specific changes */
+#include "sunw_port_before.h"
+#include <config.h>
+
+#ifdef NEED_SUN4PROTOS
+#define _PARAMS(x) x
+#endif
+
+struct group; /* silence warning */
+struct passwd; /* silence warning */
+struct timeval; /* silence warning */
+struct timezone; /* silence warning */
+
+#ifdef HAVE_SYS_TIMERS_H
+#include <sys/timers.h>
+#endif
+#include <limits.h>
+
+#ifdef ISC_PLATFORM_NEEDTIMESPEC
+#include <time.h> /* For time_t */
+struct timespec {
+ time_t tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+};
+#endif
+#ifndef HAVE_MEMMOVE
+#define memmove(a,b,c) bcopy(b,a,c)
+#endif
+
+#undef WANT_IRS_GR
+#undef WANT_IRS_NIS
+#undef WANT_IRS_PW
+
+#define BSD_COMP 1
+#define USE_POLL 1
+#define HAVE_MD5 1
+#define SOLARIS2 1
+
+/* DO_PTHREADS is conditionally defined in sunw_port_before.h
+ * #define DO_PTHREADS 1 */
+#define GETGROUPLIST_ARGS const char *name, gid_t basegid, gid_t *groups, int *ngroups
+#define GETNETBYADDR_ADDR_T long
+#define SETPWENT_VOID 1
+#define SETGRENT_VOID 1
+
+#define NET_R_ARGS char *buf, int buflen
+#define NET_R_BAD NULL
+#define NET_R_COPY buf, buflen
+#define NET_R_COPY_ARGS NET_R_ARGS
+#define NET_R_END_RESULT(x) /*empty*/
+#define NET_R_END_RETURN void
+#undef NET_R_ENT_ARGS /*empty*/
+#define NET_R_OK nptr
+#define NET_R_RETURN struct netent *
+#undef NET_R_SET_RESULT /*empty*/
+#undef NET_R_SETANSWER
+#define NET_R_SET_RETURN void
+#undef NETENT_DATA
+
+#define GROUP_R_RETURN struct group *
+#define GROUP_R_SET_RETURN void
+#undef GROUP_R_SET_RESULT /*empty*/
+#define GROUP_R_END_RETURN void
+#define GROUP_R_END_RESULT(x) /*empty*/
+#define GROUP_R_ARGS char *buf, int buflen
+#define GROUP_R_ENT_ARGS void
+#define GROUP_R_OK gptr
+#define GROUP_R_BAD NULL
+
+#define HOST_R_ARGS char *buf, int buflen, int *h_errnop
+#define HOST_R_BAD NULL
+#define HOST_R_COPY buf, buflen
+#define HOST_R_COPY_ARGS char *buf, int buflen
+#define HOST_R_END_RESULT(x) /*empty*/
+#define HOST_R_END_RETURN void
+#undef HOST_R_ENT_ARGS /*empty*/
+#define HOST_R_ERRNO *h_errnop = h_errno
+#define HOST_R_OK hptr
+#define HOST_R_RETURN struct hostent *
+#undef HOST_R_SETANSWER
+#undef HOST_R_SET_RESULT
+#define HOST_R_SET_RETURN void
+#undef HOSTENT_DATA
+
+#define NGR_R_ARGS char *buf, int buflen
+#define NGR_R_BAD (0)
+#define NGR_R_COPY buf, buflen
+#define NGR_R_COPY_ARGS NGR_R_ARGS
+#define NGR_R_CONST
+#define NGR_R_END_RESULT(x) /*empty*/
+#define NGR_R_END_RETURN void
+#undef NGR_R_END_ARGS /*empty*/
+#define NGR_R_OK 1
+#define NGR_R_RETURN int
+#define NGR_R_SET_CONST const
+#undef NGR_R_SET_RESULT /*empty*/
+#define NGR_R_SET_RETURN void
+#undef NGR_R_SET_ARGS
+
+
+#if !defined(NGR_R_SET_ARGS) && defined(NGR_R_END_ARGS)
+#define NGR_R_SET_ARGS NGR_R_END_ARGS
+#endif
+
+#define PROTO_R_ARGS char *buf, int buflen
+#define PROTO_R_BAD NULL
+#define PROTO_R_COPY buf, buflen
+#define PROTO_R_COPY_ARGS PROTO_R_ARGS
+#define PROTO_R_END_RESULT(x) /*empty*/
+#define PROTO_R_END_RETURN void
+#undef PROTO_R_ENT_ARGS /*empty*/
+#undef PROTO_R_ENT_UNUSED
+#define PROTO_R_OK pptr
+#undef PROTO_R_SETANSWER
+#define PROTO_R_RETURN struct protoent *
+#undef PROTO_R_SET_RESULT
+#define PROTO_R_SET_RETURN void
+#undef PROTOENT_DATA
+
+#define PASS_R_ARGS char *buf, int buflen
+#define PASS_R_BAD NULL
+#define PASS_R_COPY buf, buflen
+#define PASS_R_COPY_ARGS PASS_R_ARGS
+#define PASS_R_END_RESULT(x) /*empty*/
+#define PASS_R_END_RETURN void
+#undef PASS_R_ENT_ARGS
+#define PASS_R_OK pwptr
+#define PASS_R_RETURN struct passwd *
+#undef PASS_R_SET_RESULT /*empty*/
+#define PASS_R_SET_RETURN void
+
+#define SERV_R_ARGS char *buf, int buflen
+#define SERV_R_BAD NULL
+#define SERV_R_COPY buf, buflen
+#define SERV_R_COPY_ARGS SERV_R_ARGS
+#define SERV_R_END_RESULT(x) /*empty*/
+#define SERV_R_END_RETURN void
+#undef SERV_R_ENT_ARGS /*empty*/
+#undef SERV_R_ENT_UNUSED /*empty*/
+#define SERV_R_OK sptr
+#undef SERV_R_SETANSWER
+#define SERV_R_RETURN struct servent *
+#undef SERV_R_SET_RESULT
+#define SERV_R_SET_RETURN void
+
+
+
+#define DE_CONST(konst, var) \
+ do { \
+ union { const void *k; void *v; } _u; \
+ _u.k = konst; \
+ var = _u.v; \
+ } while (0)
+
+#define UNUSED(x) (x) = (x)
+
+#undef NEED_SOLARIS_BITTYPES
+#define ISC_SOCKLEN_T int
+
+#ifdef __GNUC__
+#define ISC_FORMAT_PRINTF(fmt, args) \
+ __attribute__((__format__(__printf__, fmt, args)))
+#else
+#define ISC_FORMAT_PRINTF(fmt, args)
+#endif
+
+/* Pull in host order macros when _XOPEN_SOURCE_EXTENDED is defined. */
+#if defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)
+#include <sys/byteorder.h>
+#endif
+
+#endif
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/port_netdb.h b/usr/src/lib/libresolv2_joy/include/port_netdb.h
new file mode 100644
index 0000000000..a308cc7efa
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/port_netdb.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#ifndef _PORT_NETDB_H
+#define _PORT_NETDB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* AI_NUMERICSERV is not a valid flag for getaddrinfo */
+#define AI_MASK 0x0038 /* mask of valid flags */
+
+/* EAI_OVERFLOW was removed from ISC */
+#define EAI_BADHINTS 12
+
+/*
+ * these are libresolv2 functions that were renamed in previous versions to
+ * res_* because they conflict with libnsl or libsocket
+ */
+
+#define endhostent joy_res_endhostent /* libnsl */
+void endhostent __P((void));
+#define endnetent res_endnetent /* libsocket */
+void endnetent __P((void));
+#define freeaddrinfo res_freeaddrinfo /* libsocket */
+void freeaddrinfo __P((struct addrinfo *));
+#define freehostent res_freehostent /* libsocket and libnsl */
+void freehostent __P((struct hostent *));
+#define getaddrinfo res_getaddrinfo /* libsocket */
+int getaddrinfo __P((const char *, const char *,
+ const struct addrinfo *, struct addrinfo **));
+#define gethostbyaddr joy_res_gethostbyaddr /* libnsl */
+struct hostent *gethostbyaddr __P((const char *, int, int));
+#define gethostbyname joy_res_gethostbyname /* libnsl */
+struct hostent *gethostbyname __P((const char *));
+#define gethostbyname2 joy_res_gethostbyname2 /* lib/nsswitch/dns */
+struct hostent *gethostbyname2 __P((const char *, int));
+#define gethostent res_gethostent /* libnsl */
+struct hostent *gethostent __P((void));
+#define getipnodebyaddr res_getipnodebyaddr /* libnsl and libsocket */
+struct hostent *getipnodebyaddr __P((const void *, size_t, int, int *));
+#define getipnodebyname res_getipnodebyname /* libnsl and libsocket */
+struct hostent *getipnodebyname __P((const char *, int, int, int *));
+
+#define getnetbyaddr res_getnetbyaddr /* libsocket */
+struct netent *getnetbyaddr __P((unsigned long, int));
+#define getnetbyname res_getnetbyname /* libsocket */
+struct netent *getnetbyname __P((const char *));
+#define getnetent res_getnetent /* libsocket */
+struct netent *getnetent __P((void));
+#define sethostent joy_res_sethostent /* libnsl */
+void sethostent __P((int));
+#define setnetent res_setnetent /* libsocket */
+void setnetent __P((int));
+
+/*
+ * these are other irs functions now included in libresolv.so.2. We rename the
+ * ones that overlap with libsocket or libnsl
+ */
+
+/* endprotoent is in libsocket.so.1 */
+#define endprotoent res_endprotoent
+void endprotoent __P((void));
+
+/* endservent is in libsocket.so.1 */
+#define endservent res_endservent
+void endservent __P((void));
+
+/* note: the next two symbols are variables, not functions */
+
+/* gai_errlist is in libsocket.so.1 */
+#define gai_errlist res_gai_errlist
+
+/* gai_nerr is in libsocket.so.1 */
+#define gai_nerr res_gai_nerr
+
+/* gai_strerror is in libsocket.so.1 */
+#define gai_strerror res_gai_strerror
+const char *gai_strerror __P((int ecode));
+
+/* gethostbyaddr_r is in libnsl.so.1 */
+#define gethostbyaddr_r res_gethostbyaddr_r
+struct hostent *gethostbyaddr_r __P((const char *addr, int len, int type,
+ struct hostent *hptr, char *buf,
+ int buflen, int *h_errnop));
+
+/* gethostbyname_r is in libnsl.so.1 */
+#define gethostbyname_r res_gethostbyname_r
+struct hostent *gethostbyname_r __P((const char *name, struct hostent *hptr,
+ char *buf, int buflen, int *h_errnop));
+
+/* gethostent_r is in libnsl.so.1 */
+#define gethostent_r res_gethostent_r
+struct hostent *gethostent_r __P((struct hostent *hptr, char *buf, int buflen,
+ int *h_errnop));
+
+/* getnameinfo is in libsocket.so.1 */
+#define getnameinfo res_getnameinfo
+int getnameinfo __P((const struct sockaddr *, size_t, char *,
+ size_t, char *, size_t, int));
+
+/* getnetbyaddr_r is in libsocket.so.1 */
+#define getnetbyaddr_r res_getnetbyaddr_r
+struct netent *getnetbyaddr_r __P((long, int, struct netent *, char *, int));
+
+/* getnetbyname_r is in libsocket.so.1 */
+#define getnetbyname_r res_getnetbyname_r
+struct netent *getnetbyname_r __P((const char *, struct netent *, char *, int));
+
+/* getnetent_r is in libsocket.so.1 */
+#define getnetent_r res_getnetent_r
+struct netent *getnetent_r __P((struct netent *, char *, int));
+
+/* getprotobyname is in libsocket.so.1 */
+#define getprotobyname res_getprotobyname
+struct protoent *getprotobyname __P((const char *));
+
+/* getprotobyname_r is in libsocket.so.1 */
+#define getprotobyname_r res_getprotobyname_r
+struct protoent *getprotobyname_r __P((const char *, struct protoent *,
+ char *, int));
+
+/* getprotobynumber is in libsocket.so.1 */
+#define getprotobynumber res_getprotobynumber
+struct protoent *getprotobynumber __P((int));
+
+/* getprotobynumber_r is in libsocket.so.1 */
+#define getprotobynumber_r res_getprotobynumber_r
+struct protoent *getprotobynumber_r __P((int,
+ struct protoent *, char *, int));
+
+/* getprotoent is in libsocket.so.1 */
+#define getprotoent res_getprotoent
+struct protoent *getprotoent __P((void));
+
+/* getprotoent_r is in libsocket.so.1 */
+#define getprotoent_r res_getprotoent_r
+struct protoent *getprotoent_r __P((struct protoent *, char *, int));
+
+/* getservbyname is in libsocket.so.1 and libnsl.so.1 */
+#define getservbyname res_getservbyname
+struct servent *getservbyname __P((const char *, const char *));
+
+/* getservbyname_r is in libsocket.so.1 and libnsl.so.1 */
+#define getservbyname_r res_getservbyname_r
+struct servent *getservbyname_r __P((const char *name, const char *,
+ struct servent *, char *, int));
+
+/* getservbyport is in libsocket.so.1 and libnsl.so.1 */
+#define getservbyport res_getservbyport
+struct servent *getservbyport __P((int, const char *));
+
+/* getservbyport_r is in libsocket.so.1 and libnsl.so.1 */
+#define getservbyport_r res_getservbyport_r
+struct servent *getservbyport_r __P((int port, const char *,
+ struct servent *, char *, int));
+
+/* getservent is in libsocket.so.1 */
+#define getservent res_getservent
+struct servent *getservent __P((void));
+
+/* getservent_r is in libsocket.so.1 */
+#define getservent_r res_getservent_r
+struct servent *getservent_r __P((struct servent *, char *, int));
+
+/* innetgr is in libsocket.so.1 */
+#define innetgr res_innetgr
+int innetgr __P((const char *, const char *, const char *, const char *));
+
+/* setprotoent is in libsocket.so.1 */
+#define setprotoent res_setprotoent
+void setprotoent __P((int));
+
+/* setservent is in libsocket.so.1 */
+#define setservent res_setservent
+void setservent __P((int));
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PORT_NETDB_H */
diff --git a/usr/src/lib/libresolv2_joy/include/port_resolv.h b/usr/src/lib/libresolv2_joy/include/port_resolv.h
new file mode 100644
index 0000000000..cd1a97d40c
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/port_resolv.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PORT_RESOLV_H
+#define _PORT_RESOLV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* RES_NSID has the same value as RES_NO_NIBBLE, which has been deleted */
+#define RES_NSID 0x00040000 /* request name server ID */
+
+/* RES_DEFAULT has a new value in libbind-6.0 */
+#undef RES_DEFAULT
+#define RES_DEFAULT (RES_RECURSE | RES_DEFNAMES | \
+ RES_DNSRCH | RES_NO_NIBBLE2)
+
+#ifndef __ultrix__
+u_int16_t _getshort __P((const uchar_t *));
+u_int32_t _getlong __P((const uchar_t *));
+#endif
+
+/* rename functions so they can be wrapped (see sunw/sunw_wrappers.c */
+#define p_option isc_p_option
+const char *p_option(ulong_t option);
+#define p_secstodate isc_p_secstodate
+char *p_secstodate(ulong_t secs);
+
+/* prevent namespace pollution */
+#define res_protocolnumber __res_protocolnumber
+#define res_servicenumber __res_servicenumber
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PORT_RESOLV_H */
diff --git a/usr/src/lib/libresolv2_joy/include/probe_ipv6 b/usr/src/lib/libresolv2_joy/include/probe_ipv6
new file mode 100755
index 0000000000..371ac96c55
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/probe_ipv6
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+# Copyright 2003 by Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+set -e
+PATH=/bin:/usr/bin:$PATH; export PATH
+trap "rm -f tmp$$[abc].[oc]" 0
+target=port_ipv6
+new=new_${target}.h
+old=${target}.h
+
+cat > tmp$$a.c <<EOF
+#include <sys/types.h>
+#include <netinet/in.h>
+struct sockaddr_in6 xx;
+EOF
+
+cat > tmp$$b.c <<EOF
+#include <sys/types.h>
+#include <netinet/in.h>
+struct in6_addr xx;
+EOF
+
+cat > tmp$$c.c <<EOF
+#include <sys/types.h>
+#include <netinet/in.h>
+struct sockaddr_in6 xx;
+main() { xx.sin6_scope_id = 0; }
+EOF
+
+cat > ${new} <<EOF
+
+/* This file is automatically generated. Do Not Edit. */
+
+#ifndef ${target}_h
+#define ${target}_h
+
+EOF
+
+if ${CC} -c tmp$$a.c > /dev/null 2>&1
+then
+ echo "#define HAS_INET6_STRUCTS" >> ${new}
+ if ${CC} -c tmp$$b.c > /dev/null 2>&1
+ then
+ :
+ else
+ echo "#define in6_addr in_addr6" >> ${new}
+ fi
+ if ${CC} -c tmp$$c.c > /dev/null 2>&1
+ then
+ echo "#define HAVE_SIN6_SCOPE_ID" >> ${new}
+ else
+ echo "#undef HAVE_SIN6_SCOPE_ID" >> ${new}
+ fi
+else
+ echo "#undef HAS_INET6_STRUCTS" >> ${new}
+fi
+echo >> ${new}
+echo "#endif" >> ${new}
+if [ -f ${old} ]; then
+ if cmp -s ${new} ${old} ; then
+ rm -f ${new}
+ else
+ rm -f ${old}
+ mv ${new} ${old}
+ fi
+else
+ mv ${new} ${old}
+fi
+exit 0
diff --git a/usr/src/lib/libresolv2_joy/include/probe_ipv6.sh b/usr/src/lib/libresolv2_joy/include/probe_ipv6.sh
new file mode 100644
index 0000000000..371ac96c55
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/probe_ipv6.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+# Copyright 2003 by Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+set -e
+PATH=/bin:/usr/bin:$PATH; export PATH
+trap "rm -f tmp$$[abc].[oc]" 0
+target=port_ipv6
+new=new_${target}.h
+old=${target}.h
+
+cat > tmp$$a.c <<EOF
+#include <sys/types.h>
+#include <netinet/in.h>
+struct sockaddr_in6 xx;
+EOF
+
+cat > tmp$$b.c <<EOF
+#include <sys/types.h>
+#include <netinet/in.h>
+struct in6_addr xx;
+EOF
+
+cat > tmp$$c.c <<EOF
+#include <sys/types.h>
+#include <netinet/in.h>
+struct sockaddr_in6 xx;
+main() { xx.sin6_scope_id = 0; }
+EOF
+
+cat > ${new} <<EOF
+
+/* This file is automatically generated. Do Not Edit. */
+
+#ifndef ${target}_h
+#define ${target}_h
+
+EOF
+
+if ${CC} -c tmp$$a.c > /dev/null 2>&1
+then
+ echo "#define HAS_INET6_STRUCTS" >> ${new}
+ if ${CC} -c tmp$$b.c > /dev/null 2>&1
+ then
+ :
+ else
+ echo "#define in6_addr in_addr6" >> ${new}
+ fi
+ if ${CC} -c tmp$$c.c > /dev/null 2>&1
+ then
+ echo "#define HAVE_SIN6_SCOPE_ID" >> ${new}
+ else
+ echo "#undef HAVE_SIN6_SCOPE_ID" >> ${new}
+ fi
+else
+ echo "#undef HAS_INET6_STRUCTS" >> ${new}
+fi
+echo >> ${new}
+echo "#endif" >> ${new}
+if [ -f ${old} ]; then
+ if cmp -s ${new} ${old} ; then
+ rm -f ${new}
+ else
+ rm -f ${old}
+ mv ${new} ${old}
+ fi
+else
+ mv ${new} ${old}
+fi
+exit 0
diff --git a/usr/src/lib/libresolv2_joy/include/res_update.h b/usr/src/lib/libresolv2_joy/include/res_update.h
new file mode 100644
index 0000000000..0c6967db56
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/res_update.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999 by Internet Software Consortium, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * $Id: res_update.h,v 1.3 2005/04/27 04:56:15 sra Exp $
+ */
+
+#ifndef __RES_UPDATE_H
+#define __RES_UPDATE_H
+
+/*! \file */
+
+#include <sys/types.h>
+#include <arpa/nameser.h>
+#include <isc/list.h>
+#include <resolv_joy.h>
+
+#ifndef ORIGINAL_ISC_CODE
+/* definition of u_int32_t needed on Solaris */
+#include <sys/bitypes.h>
+/* need to rename ns_updrec before we define it here */
+#include "arpa/port_nameser.h"
+#endif /* ORIGINAL_ISC_CODE */
+
+
+/*%
+ * This RR-like structure is particular to UPDATE.
+ */
+struct ns_updrec {
+ LINK(struct ns_updrec) r_link, r_glink;
+ ns_sect r_section; /*%< ZONE/PREREQUISITE/UPDATE */
+ char * r_dname; /*%< owner of the RR */
+ ns_class r_class; /*%< class number */
+ ns_type r_type; /*%< type number */
+ u_int32_t r_ttl; /*%< time to live */
+ u_char * r_data; /*%< rdata fields as text string */
+ u_int r_size; /*%< size of r_data field */
+ int r_opcode; /*%< type of operation */
+ /* following fields for private use by the resolver/server routines */
+ struct databuf *r_dp; /*%< databuf to process */
+ struct databuf *r_deldp; /*%< databuf's deleted/overwritten */
+ u_int r_zone; /*%< zone number on server */
+};
+typedef struct ns_updrec ns_updrec;
+typedef LIST(ns_updrec) ns_updque;
+
+#ifdef ORIGINAL_ISC_CODE
+#define res_mkupdate __res_mkupdate
+#define res_update __res_update
+#define res_mkupdrec __res_mkupdrec
+#define res_freeupdrec __res_freeupdrec
+#define res_nmkupdate __res_nmkupdate
+#define res_nupdate __res_nupdate
+#else
+/* these are renamed in "port_nameser.h" */
+#endif /* ORIGINAL_ISC_CODE */
+
+
+int res_mkupdate __P((ns_updrec *, u_char *, int));
+int res_update __P((ns_updrec *));
+ns_updrec * res_mkupdrec __P((int, const char *, u_int, u_int, u_long));
+void res_freeupdrec __P((ns_updrec *));
+int res_nmkupdate __P((res_state, ns_updrec *, u_char *, int));
+int res_nupdate __P((res_state, ns_updrec *, ns_tsig_key *));
+
+#endif /*__RES_UPDATE_H*/
+
+/*! \file */
diff --git a/usr/src/lib/libresolv2_joy/include/resolv_mt.h b/usr/src/lib/libresolv2_joy/include/resolv_mt.h
new file mode 100644
index 0000000000..500d4d764c
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/resolv_mt.h
@@ -0,0 +1,47 @@
+#ifndef _RESOLV_MT_H
+#define _RESOLV_MT_H
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv_joy.h>
+
+/* Access functions for the libresolv private interface */
+
+int __res_enable_mt(void);
+int __res_disable_mt(void);
+
+/* Per-thread context */
+
+typedef struct {
+int no_hosts_fallback_private;
+int retry_save;
+int retry_private;
+char inet_nsap_ntoa_tmpbuf[255*3];
+char sym_ntos_unname[20];
+char sym_ntop_unname[20];
+char p_option_nbuf[40];
+char p_time_nbuf[40];
+char precsize_ntoa_retbuf[sizeof "90000000.00"];
+char loc_ntoa_tmpbuf[sizeof
+"1000 60 60.000 N 1000 60 60.000 W -12345678.00m 90000000.00m 90000000.00m 90000000.00m"];
+char p_secstodate_output[15];
+} mtctxres_t;
+
+/* Thread-specific data (TSD) */
+
+mtctxres_t *___mtctxres(void);
+#define mtctxres (___mtctxres())
+
+/* Various static data that should be TSD */
+
+#define sym_ntos_unname (mtctxres->sym_ntos_unname)
+#define sym_ntop_unname (mtctxres->sym_ntop_unname)
+#define inet_nsap_ntoa_tmpbuf (mtctxres->inet_nsap_ntoa_tmpbuf)
+#define p_option_nbuf (mtctxres->p_option_nbuf)
+#define p_time_nbuf (mtctxres->p_time_nbuf)
+#define precsize_ntoa_retbuf (mtctxres->precsize_ntoa_retbuf)
+#define loc_ntoa_tmpbuf (mtctxres->loc_ntoa_tmpbuf)
+#define p_secstodate_output (mtctxres->p_secstodate_output)
+
+#endif /* _RESOLV_MT_H */
diff --git a/usr/src/lib/libresolv2_joy/include/sunw_port_after.h b/usr/src/lib/libresolv2_joy/include/sunw_port_after.h
new file mode 100644
index 0000000000..bff64a74c1
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/sunw_port_after.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SUNW_PORT_AFTER_H
+#define _SUNW_PORT_AFTER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * rename setnetgrent and endnetgrent which were formerly in a separate irs
+ * shared library. These functions should come from libc.so
+ */
+#define setnetgrent res_setnetgrent
+#ifdef SETNETGRENT_ARGS
+void setnetgrent(SETNETGRENT_ARGS);
+#else
+void setnetgrent(const char *netgroup);
+#endif
+
+#define endnetgrent res_endnetgrent
+void endnetgrent(void);
+
+
+/*
+ * include ports for the public header files. ISC's versions are quite different
+ * from those currently in OpenSolaris.
+ */
+
+#ifdef _RESOLV_JOY_H
+#include <port_resolv.h>
+#endif /* _RESOLV_JOY_H */
+
+#ifdef _NETDB_H
+#include <port_netdb.h>
+#endif /* _NETDB_H */
+
+#ifdef _ARPA_INET_H
+#include <arpa/port_inet.h>
+#endif /* _ARPA_INET_H */
+
+#ifdef _ARPA_NAMESER_H
+#include <arpa/port_nameser.h>
+#endif /* _ARPA_NAMESER_H */
+
+
+#ifdef _ARPA_NAMESER_COMPAT_H
+/* no changes */
+#endif /* _ARPA_NAMESER_COMPAT_H */
+
+/* version-specific defines */
+#include <os_version.h>
+
+/*
+ * Prior to 2.6, Solaris needs a prototype for gethostname().
+ */
+#if (OS_MAJOR == 5 && OS_MINOR < 6)
+extern int gethostname(char *, size_t);
+#endif
+/*
+ * gethostid() was not available until 2.5
+ * setsockopt(SO_REUSEADDR) fails on unix domain sockets before 2.5
+ * use ioctl(FIONBIO) rather than fcntl() calls to set/clear non-blocking i/o.
+ */
+#if (OS_MAJOR == 5 && OS_MINOR < 5)
+#define GET_HOST_ID_MISSING
+#define NO_UNIX_REUSEADDR
+#define USE_FIONBIO_IOCTL
+#endif
+
+#if (OS_MAJOR == 5 && OS_MINOR < 11)
+#define NEED_STRSEP
+extern char *strsep(char **, const char *);
+#endif
+
+
+/*
+ * Solaris 2.5 and later have getrlimit(), setrlimit() and getrusage().
+ */
+#if (OS_MAJOR > 5 || (OS_MAJOR == 5 && OS_MINOR >= 5))
+#include <sys/resource.h>
+#define HAVE_GETRUSAGE
+#define RLIMIT_TYPE rlim_t
+#define RLIMIT_FILE_INFINITY
+#endif
+
+/* the default syslog facility of named/lwresd. */
+#ifndef ISC_FACILITY
+#define ISC_FACILITY LOG_DAEMON
+#endif
+
+
+/*
+ * Solaris 8 has if_nametoindex().
+ */
+#if (OS_MAJOR > 5 || (OS_MAJOR == 5 && OS_MINOR >= 8))
+#define USE_IFNAMELINKID
+#endif
+
+#undef ALIGN
+#if (OS_MAJOR == 5 && OS_MINOR > 8)
+#define ALIGN(x) (((uintptr_t)(x) + (sizeof (char *) - 1UL)) & \
+ ~(sizeof (char *) - 1UL))
+#else
+#define ALIGN(x) (((unsigned long)(x) + (sizeof (char *) - 1UL)) & \
+ ~(sizeof (char *) - 1UL))
+#endif
+
+#if (OS_MAJOR == 5 && OS_MINOR < 5)
+#ifndef USE_FIONBIO_IOCTL
+#define USE_FIONBIO_IOCTL 1
+#endif
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SUNW_PORT_AFTER_H */
diff --git a/usr/src/lib/libresolv2_joy/include/sunw_port_before.h b/usr/src/lib/libresolv2_joy/include/sunw_port_before.h
new file mode 100644
index 0000000000..776e311fcc
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/sunw_port_before.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SUNW_PORT_BEFORE_H
+#define _SUNW_PORT_BEFORE_H
+
+#ifdef SUNW_OPTIONS
+#include <conf/sunoptions.h>
+#endif
+
+/* version-specific defines */
+#include <os_version.h>
+#if (OS_MAJOR == 5 && OS_MINOR < 6)
+#ifndef SOLARIS_BITTYPES
+#define NEED_SOLARIS_BITTYPES 1
+#endif
+#endif
+
+#if (OS_MAJOR == 5 && OS_MINOR < 5)
+#undef HAS_PTHREADS
+#else
+#define HAS_PTHREADS
+#endif
+
+#if defined(HAS_PTHREADS) && defined(_REENTRANT)
+#define DO_PTHREADS
+#endif
+
+/*
+ * need these if we are using public versions of nameser.h, resolv.h, and
+ * inet.h
+ */
+#include <sys/param.h>
+#if (!defined(BSD)) || (BSD < 199306)
+#include <sys/bitypes.h>
+#else
+#include <sys/types.h>
+#endif
+#include <sys/cdefs.h>
+
+#endif /* _SUNW_PORT_BEFORE_H */
diff --git a/usr/src/lib/libresolv2_joy/include/sys/bitypes.h b/usr/src/lib/libresolv2_joy/include/sys/bitypes.h
new file mode 100644
index 0000000000..54fb42bad7
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/sys/bitypes.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2004, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998, 1999, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: bitypes.h,v 1.7 2008/11/14 02:54:35 tbox Exp $ */
+
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+
+ /*
+ * Basic integral types. Omit the typedef if
+ * not possible for a machine/compiler combination.
+ */
+
+#ifdef NEED_SOLARIS_BITTYPES
+ typedef /*signed*/ char int8_t;
+ typedef short int16_t;
+ typedef int int32_t;
+#endif
+ typedef unsigned char u_int8_t;
+ typedef unsigned short u_int16_t;
+ typedef unsigned int u_int32_t;
+
+#endif /* __BIT_TYPES_DEFINED__ */
diff --git a/usr/src/lib/libresolv2_joy/include/sys/cdefs.h b/usr/src/lib/libresolv2_joy/include/sys/cdefs.h
new file mode 100644
index 0000000000..67aac00cc7
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/include/sys/cdefs.h
@@ -0,0 +1,144 @@
+/*
+ * ++Copyright++ 1991, 1993
+ * -
+ * Copyright (c) 1991, 1993
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 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) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+/*
+ * @(#)cdefs.h 8.1 (Berkeley) 6/2/93
+ * $Id: cdefs.h,v 1.2 2004/07/19 05:54:07 marka Exp $
+ */
+
+#ifndef _CDEFS_H_
+#define _CDEFS_H_
+
+#if defined(__cplusplus)
+#define __BEGIN_DECLS extern "C" {
+#define __END_DECLS };
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names, e.g.
+ * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
+ * The __CONCAT macro is a bit tricky -- make sure you don't put spaces
+ * in between its arguments. __CONCAT can also concatenate double-quoted
+ * strings produced by the __STRING macro, but this only works with ANSI C.
+ */
+#if defined(__STDC__) || defined(__cplusplus)
+#define __P(protos) protos /* full-blown ANSI C */
+#define __CONCAT(x,y) x ## y
+#define __STRING(x) #x
+
+#define __const const /* define reserved names to standard */
+#define __signed signed
+#define __volatile volatile
+#if defined(__cplusplus)
+#define __inline inline /* convert to C++ keyword */
+#else
+#ifndef __GNUC__
+#define __inline /* delete GCC keyword */
+#endif /* !__GNUC__ */
+#endif /* !__cplusplus */
+
+#else /* !(__STDC__ || __cplusplus) */
+#define __P(protos) () /* traditional C preprocessor */
+#define __CONCAT(x,y) x/**/y
+#define __STRING(x) "x"
+
+#ifndef __GNUC__
+#define __const /* delete pseudo-ANSI C keywords */
+#define __inline
+#define __signed
+#define __volatile
+/*
+ * In non-ANSI C environments, new programs will want ANSI-only C keywords
+ * deleted from the program and old programs will want them left alone.
+ * When using a compiler other than gcc, programs using the ANSI C keywords
+ * const, inline etc. as normal identifiers should define -DNO_ANSI_KEYWORDS.
+ * When using "gcc -traditional", we assume that this is the intent; if
+ * __GNUC__ is defined but __STDC__ is not, we leave the new keywords alone.
+ */
+#ifndef NO_ANSI_KEYWORDS
+#define const /* delete ANSI C keywords */
+#define inline
+#define signed
+#define volatile
+#endif
+#endif /* !__GNUC__ */
+#endif /* !(__STDC__ || __cplusplus) */
+
+/*
+ * GCC1 and some versions of GCC2 declare dead (non-returning) and
+ * pure (no side effects) functions using "volatile" and "const";
+ * unfortunately, these then cause warnings under "-ansi -pedantic".
+ * GCC2 uses a new, peculiar __attribute__((attrs)) style. All of
+ * these work for GNU C++ (modulo a slight glitch in the C++ grammar
+ * in the distribution version of 2.5.5).
+ */
+#if !defined(__GNUC__) || __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
+#define __attribute__(x) /* delete __attribute__ if non-gcc or gcc1 */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define __dead __volatile
+#define __pure __const
+#endif
+#endif
+
+/* Delete pseudo-keywords wherever they are not available or needed. */
+#ifndef __dead
+#define __dead
+#define __pure
+#endif
+
+#endif /* !_CDEFS_H_ */
diff --git a/usr/src/lib/libresolv2_joy/sparc/Makefile b/usr/src/lib/libresolv2_joy/sparc/Makefile
new file mode 100644
index 0000000000..a333224278
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/sparc/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 (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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libresolv2_joy/sparcv9/Makefile b/usr/src/lib/libresolv2_joy/sparcv9/Makefile
new file mode 100644
index 0000000000..ceed393e0d
--- /dev/null
+++ b/usr/src/lib/libresolv2_joy/sparcv9/Makefile
@@ -0,0 +1,38 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+# With the adition of BIND 8.3.3, the symbol table for 64 bit went over
+# the limit for Kpic, so we've added -KPIC here, for just the 64 bit
+# library. This avoids compiling the 32-bit library with PIC unnecessarily.
+
+sparcv9_C_PICFLAGS = -K PIC
+sparcv9_CC_PICFLAGS = -KPIC
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libscf/inc/libscf.h b/usr/src/lib/libscf/inc/libscf.h
index b2730eb810..a30d374ae3 100644
--- a/usr/src/lib/libscf/inc/libscf.h
+++ b/usr/src/lib/libscf/inc/libscf.h
@@ -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 2016 RackTop Systems.
* Copyright 2020 Joyent, Inc.
*/
diff --git a/usr/src/lib/libsecdb/user_attr.txt b/usr/src/lib/libsecdb/user_attr.txt
index 576028804e..2960468e49 100644
--- a/usr/src/lib/libsecdb/user_attr.txt
+++ b/usr/src/lib/libsecdb/user_attr.txt
@@ -1,5 +1,6 @@
#
# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, Joyent, Inc. All rights reserved.
#
# CDDL HEADER START
#
@@ -25,11 +26,12 @@
# user attributes. see user_attr(5)
#
#
-root::::auths=solaris.*,solaris.grant;profiles=All;audit_flags=lo\:no;lock_after_retries=no;min_label=admin_low;clearance=admin_high
-lp::::profiles=Printer Management
adm::::profiles=Log Management
-dladm::::auths=solaris.smf.manage.wpa,solaris.smf.modify
+admin::::profiles=Primary Administrator;roles=root
daemon::::auths=solaris.smf.manage.ilb,solaris.smf.modify.application
+dladm::::auths=solaris.smf.manage.wpa,solaris.smf.modify
+lp::::profiles=Printer Management
netadm::::type=role;project=default;profiles=Network Autoconf Admin,Network Management,Service Management
netcfg::::type=role;project=default;profiles=Network Autoconf User;auths=solaris.network.autoconf.write
+root::::auths=solaris.*,solaris.grant;profiles=All;audit_flags=lo\:no;lock_after_retries=no;min_label=admin_low;clearance=admin_high
zfssnap::::type=role;auths=solaris.smf.manage.zfs-auto-snapshot;profiles=ZFS File System Management
diff --git a/usr/src/lib/libshare/nfs/libshare_nfs.c b/usr/src/lib/libshare/nfs/libshare_nfs.c
index 76aef178d3..7c630e37a2 100644
--- a/usr/src/lib/libshare/nfs/libshare_nfs.c
+++ b/usr/src/lib/libshare/nfs/libshare_nfs.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright (c) 2014, 2016 by Delphix. All rights reserved.
* Copyright 2018 Nexenta Systems, Inc.
* Copyright 2022 RackTop Systems.
@@ -2794,9 +2795,16 @@ nfs_init()
ret = initprotofromsmf();
if (ret != SA_OK) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "NFS plugin problem with SMF repository: %s\n"),
- sa_errorstr(ret));
+ /*
+ * This is a workaround. See the comment in
+ * cmd/fs.d/nfs/lib/smfcfg.c for an explanation.
+ */
+ if (getzoneid() == GLOBAL_ZONEID ||
+ ret != SCF_ERROR_NOT_FOUND) {
+ (void) printf(dgettext(TEXT_DOMAIN,
+ "NFS plugin problem with SMF repository: %s\n"),
+ sa_errorstr(ret));
+ }
ret = SA_OK;
}
add_defaults();
diff --git a/usr/src/lib/libsmartsshd/Makefile b/usr/src/lib/libsmartsshd/Makefile
new file mode 100644
index 0000000000..50d9545ef1
--- /dev/null
+++ b/usr/src/lib/libsmartsshd/Makefile
@@ -0,0 +1,48 @@
+#
+# 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 2011 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.lib
+
+SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+_msg:
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libsmartsshd/Makefile.com b/usr/src/lib/libsmartsshd/Makefile.com
new file mode 100644
index 0000000000..dc75c96925
--- /dev/null
+++ b/usr/src/lib/libsmartsshd/Makefile.com
@@ -0,0 +1,46 @@
+#
+# 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 2020 Joyent, Inc.
+#
+
+LIBRARY= libsmartsshd.a
+VERS= .1
+OBJECTS= sshd-plugin.o
+
+include ../../Makefile.lib
+include ../../Makefile.rootfs
+
+SRCDIR = ../common
+SRCS = $(OBJECTS:%.o=$(SRCDIR)/%.c)
+
+CPPFLAGS += -I$(SRCDIR) -D_REENTRANT -D_FILE_OFFSET_BITS=64
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc -lmd
+
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libsmartsshd/amd64/Makefile b/usr/src/lib/libsmartsshd/amd64/Makefile
new file mode 100644
index 0000000000..31be0ef7e6
--- /dev/null
+++ b/usr/src/lib/libsmartsshd/amd64/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 2011 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libsmartsshd/common/llib-lsmartsshd b/usr/src/lib/libsmartsshd/common/llib-lsmartsshd
new file mode 100644
index 0000000000..ace7017d41
--- /dev/null
+++ b/usr/src/lib/libsmartsshd/common/llib-lsmartsshd
@@ -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
+ */
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+/*
+ *
+ * Copyright 2011 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
diff --git a/usr/src/lib/libsmartsshd/common/mapfile-vers b/usr/src/lib/libsmartsshd/common/mapfile-vers
new file mode 100644
index 0000000000..c3c6fe4946
--- /dev/null
+++ b/usr/src/lib/libsmartsshd/common/mapfile-vers
@@ -0,0 +1,48 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate_1.1 {
+ global:
+ sshd_user_rsa_key_allowed;
+ sshd_user_dsa_key_allowed;
+ sshd_user_ecdsa_key_allowed;
+ sshd_user_key_allowed;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libsmartsshd/common/sshd-plugin.c b/usr/src/lib/libsmartsshd/common/sshd-plugin.c
new file mode 100644
index 0000000000..a6463cea12
--- /dev/null
+++ b/usr/src/lib/libsmartsshd/common/sshd-plugin.c
@@ -0,0 +1,193 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <alloca.h>
+#include <door.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/rsa.h>
+
+#include <md5.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LOG_OOM(SZ) (void) fprintf(stderr, "Cannot alloca %d bytes\n", SZ)
+
+static const char *DOOR = "/var/tmp/._joyent_sshd_key_is_authorized";
+static const char *REQ_FMT_STR = "%s %d %s"; /* name uid fp */
+static const int RETURN_SZ = 2;
+
+static const int MAX_ATTEMPTS = 2;
+static const int SLEEP_PERIOD = 1;
+
+static int
+sshd_allowed_in_capi(struct passwd *pw, const char *fp)
+{
+ int allowed = 0;
+ int fd = -1;
+ int blen = 0;
+ int attempts = 0;
+ char *buf = NULL;
+ door_arg_t door_args = {0};
+
+ if (pw == NULL || fp == NULL)
+ return (0);
+
+ blen = snprintf(NULL, 0, REQ_FMT_STR, pw->pw_name, pw->pw_uid, fp) + 1;
+
+ buf = (char *)alloca(blen);
+ if (buf == NULL) {
+ LOG_OOM(blen);
+ return (0);
+ }
+
+ (void) snprintf(buf, blen, REQ_FMT_STR, pw->pw_name, pw->pw_uid, fp);
+ door_args.data_ptr = buf;
+ door_args.data_size = blen;
+
+ door_args.rsize = RETURN_SZ;
+ door_args.rbuf = alloca(RETURN_SZ);
+ if (door_args.rbuf == NULL) {
+ LOG_OOM(RETURN_SZ);
+ return (0);
+ }
+ (void) memset(door_args.rbuf, 0, RETURN_SZ);
+
+ do {
+ fd = open(DOOR, O_RDWR);
+ if (fd < 0) {
+ if (errno == ENOENT) {
+ /*
+ * On systems which are not running SmartLogin,
+ * such as vanilla SmartOS, the door will be
+ * completely absent. The sleep/retry loop is
+ * skipped in this case to keep the login
+ * process more lively.
+ */
+ perror("smartplugin: door does not exist");
+ return (0);
+ }
+ perror("smartplugin: open (of door FD) failed");
+ } else if (door_call(fd, &door_args) < 0) {
+ perror("smartplugin: door_call failed");
+ } else {
+ allowed = atoi(door_args.rbuf);
+ if (door_args.rsize > RETURN_SZ) {
+ /*
+ * Given what we know about the SmartLogin
+ * daemon on the other end of the door, this
+ * should never occur. An assert might be
+ * preferable, but that is avoided since the
+ * error can be handled.
+ */
+ (void) munmap(door_args.rbuf, door_args.rsize);
+ }
+ return (allowed);
+ }
+ if (++attempts < MAX_ATTEMPTS) {
+ (void) sleep(SLEEP_PERIOD);
+ }
+ } while (attempts < MAX_ATTEMPTS);
+
+ return (0);
+}
+
+static int
+tohexstr(uchar_t *bytes, size_t blen, char *hexstr, size_t hexlen)
+{
+ size_t i, j;
+ const char hexlist[] = "0123456789abcdef";
+
+ if (hexlen < 1)
+ return (-1);
+ for (i = 0, j = 0; i < blen; i++) {
+ /*
+ * We need 3 bytes output per input byte -- the third byte is
+ * either for a : or the \0 at the end.
+ */
+ if (hexlen < (j + 3))
+ return (-1);
+ hexstr[j++] = hexlist[(bytes[i] >> 4) & 0xf];
+ hexstr[j++] = hexlist[bytes[i] & 0xf];
+ if (i + 1 < blen)
+ hexstr[j++] = ':';
+ }
+ hexstr[j] = '\0';
+ return (0);
+}
+
+/* ARGSUSED */
+int
+sshd_user_key_allowed(struct passwd *pw, const char *type,
+ const unsigned char *buf, size_t size)
+{
+ unsigned char md5buf[MD5_DIGEST_LENGTH];
+ /*
+ * Will contain the fingerprint MD5 in colonhex format. Need 3 bytes
+ * per MD5 byte: two for hex digits, plus one for either ':' or '\0'.
+ */
+ char hex[MD5_DIGEST_LENGTH * 3];
+
+ md5_calc(md5buf, buf, size);
+ if (tohexstr(md5buf, sizeof (md5buf), hex, sizeof (hex)) != 0)
+ return (0);
+ return (sshd_allowed_in_capi(pw, hex));
+}
+
+/* ARGSUSED */
+int
+sshd_user_rsa_key_allowed(struct passwd *pw, RSA *key, const char *fp)
+{
+ return (sshd_allowed_in_capi(pw, fp));
+}
+
+/* ARGSUSED */
+int
+sshd_user_dsa_key_allowed(struct passwd *pw, DSA *key, const char *fp)
+{
+ return (sshd_allowed_in_capi(pw, fp));
+}
+
+/* ARGSUSED */
+int
+sshd_user_ecdsa_key_allowed(struct passwd *pw, DSA *key, const char *fp)
+{
+ return (sshd_allowed_in_capi(pw, fp));
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/usr/src/lib/libsmartsshd/i386/Makefile b/usr/src/lib/libsmartsshd/i386/Makefile
new file mode 100644
index 0000000000..2bfe8001d9
--- /dev/null
+++ b/usr/src/lib/libsmartsshd/i386/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 2011 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libsmedia/library/Makefile.com b/usr/src/lib/libsmedia/library/Makefile.com
index 1dabc5ae8d..cdaf3d7624 100644
--- a/usr/src/lib/libsmedia/library/Makefile.com
+++ b/usr/src/lib/libsmedia/library/Makefile.com
@@ -22,6 +22,8 @@
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
+# Copyright 2018 Joyent, Inc.
+#
LIBRARY= libsmedia.a
VERS=.1
diff --git a/usr/src/lib/libumem/amd64/umem_genasm.c b/usr/src/lib/libumem/amd64/umem_genasm.c
index 39c3a14832..c5bc6048eb 100644
--- a/usr/src/lib/libumem/amd64/umem_genasm.c
+++ b/usr/src/lib/libumem/amd64/umem_genasm.c
@@ -75,7 +75,6 @@
#include <errno.h>
-#include <stdio.h>
const int umem_genasm_supported = 1;
static uintptr_t umem_genasm_mptr = (uintptr_t)&_malloc;
diff --git a/usr/src/lib/libvnd/Makefile b/usr/src/lib/libvnd/Makefile
new file mode 100644
index 0000000000..1352adb8e4
--- /dev/null
+++ b/usr/src/lib/libvnd/Makefile
@@ -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 ../Makefile.lib
+
+HDRS = libvnd.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+TYPECHECK_LIB = libvnd.so.1
+TYPELIST = \
+ vnd_ioc_attach_t \
+ vnd_ioc_link_t \
+ vnd_ioc_unlink_t \
+ vnd_ioc_buf_t \
+ vnd_ioc_info_t
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS) $(TYPECHECK)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libvnd/Makefile.com b/usr/src/lib/libvnd/Makefile.com
new file mode 100644
index 0000000000..c567a9b942
--- /dev/null
+++ b/usr/src/lib/libvnd/Makefile.com
@@ -0,0 +1,42 @@
+#
+# 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 2019 Joyent, Inc.
+#
+
+include ../../Makefile.lib
+
+LIBRARY = libvnd.a
+VERS = .1
+OBJECTS = libvnd.o
+
+include ../../Makefile.lib
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc
+CPPFLAGS += -I../common
+
+# false positives in vnd_create()
+SMOFF += free
+
+SRCDIR = ../common
+
+C99MODE= -xc99=%all
+C99LMODE= -Xc99=%all
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libvnd/amd64/Makefile b/usr/src/lib/libvnd/amd64/Makefile
new file mode 100644
index 0000000000..15d904c616
--- /dev/null
+++ b/usr/src/lib/libvnd/amd64/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.
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libvnd/common/libvnd.c b/usr/src/lib/libvnd/common/libvnd.c
new file mode 100644
index 0000000000..8972f6cf5a
--- /dev/null
+++ b/usr/src/lib/libvnd/common/libvnd.c
@@ -0,0 +1,550 @@
+/*
+ * 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 <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <stdio.h>
+#include <zone.h>
+#include <assert.h>
+#include <sys/sysmacros.h>
+
+#include <sys/vnd.h>
+#include <libvnd.h>
+
+struct vnd_handle {
+ int vh_fd;
+ uint32_t vh_errno;
+ int vh_syserr;
+};
+
+static const char *vnd_strerror_tbl[] = {
+ "no error", /* VND_E_SUCCESS */
+ "not enough memory available", /* VND_E_NOMEM */
+ "no such datalink", /* VND_E_NODATALINK */
+ "datalink not of type DL_ETHER", /* VND_E_NOTETHER */
+ "unknown dlpi failure", /* VND_E_DLPIINVAL */
+ "DL_ATTACH_REQ failed", /* VND_E_ATTACHFAIL */
+ "DL_BIND_REQ failed", /* VND_E_PROMISCFAIL */
+ "DL_PROMISCON_REQ failed", /* VND_E_PROMISCFAIL */
+ "DLD_CAPAB_DIRECT enable failed", /* VND_E_DIRECTFAIL */
+ "bad datalink capability", /* VND_E_CAPACKINVAL */
+ "bad datalink subcapability", /* VND_E_SUBCAPINVAL */
+ "bad dld version", /* VND_E_DLDBADVERS */
+ "failed to create kstats", /* VND_E_KSTATCREATE */
+ "no such vnd link", /* VND_E_NODEV */
+ "netstack doesn't exist", /* VND_E_NONETSTACK */
+ "device already associated", /* VND_E_ASSOCIATED */
+ "device already attached", /* VND_E_ATTACHED */
+ "device already linked", /* VND_E_LINKED */
+ "invalid name", /* VND_E_BADNAME */
+ "permission denied", /* VND_E_PERM */
+ "no such zone", /* VND_E_NOZONE */
+ "failed to initialize vnd stream module", /* VND_E_STRINIT */
+ "device not attached", /* VND_E_NOTATTACHED */
+ "device not linked", /* VND_E_NOTLINKED */
+ "another device has the same link name", /* VND_E_LINKEXISTS */
+ "failed to create minor node", /* VND_E_MINORNODE */
+ "requested buffer size is too large", /* VND_E_BUFTOOBIG */
+ "requested buffer size is too small", /* VND_E_TOOSMALL */
+ "unable to obtain exclusive access to dlpi link, link busy",
+ /* VND_E_DLEXCL */
+ "DLD direct capability not supported over data link",
+ /* VND_E_DIRECTNOTSUP */
+ "invalid property size", /* VND_E_BADPROPSIZE */
+ "invalid property", /* VND_E_BADPROP */
+ "property is read only", /* VND_E_PROPRDONLY */
+ "unexpected system error", /* VND_E_SYS */
+ "capabilities invalid, pass-through module detected",
+ /* VND_E_CAPABPASS */
+ "unknown error" /* VND_E_UNKNOWN */
+};
+
+vnd_errno_t
+vnd_errno(vnd_handle_t *vhp)
+{
+ return (vhp->vh_errno);
+}
+
+const char *
+vnd_strerror(vnd_errno_t err)
+{
+ if (err >= VND_E_UNKNOWN)
+ err = VND_E_UNKNOWN;
+ return (vnd_strerror_tbl[err]);
+}
+
+int
+vnd_syserrno(vnd_handle_t *vhp)
+{
+ return (vhp->vh_syserr);
+}
+
+const char *
+vnd_strsyserror(int err)
+{
+ return (strerror(err));
+}
+
+static int
+vnd_ioc_return(vnd_handle_t *vhp, uint32_t err)
+{
+ if (err != VND_E_SUCCESS) {
+ vhp->vh_errno = err;
+ vhp->vh_syserr = 0;
+ } else {
+ if (errno == EFAULT)
+ abort();
+ vhp->vh_errno = VND_E_SYS;
+ vhp->vh_syserr = errno;
+ }
+ return (-1);
+}
+
+void
+vnd_close(vnd_handle_t *vhp)
+{
+ int ret;
+
+ if (vhp->vh_fd >= 0) {
+ ret = close(vhp->vh_fd);
+ assert(ret == 0);
+ }
+ free(vhp);
+}
+
+static int
+vnd_link(vnd_handle_t *vhp, const char *name)
+{
+ vnd_ioc_link_t vil;
+
+ if (strlen(name) >= VND_NAMELEN) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+
+ (void) strlcpy(vil.vil_name, name, sizeof (vil.vil_name));
+ vil.vil_errno = VND_E_SUCCESS;
+ if (ioctl(vhp->vh_fd, VND_IOC_LINK, &vil) != 0)
+ return (vnd_ioc_return(vhp, vil.vil_errno));
+
+ return (0);
+}
+
+static vnd_handle_t *
+vnd_open_ctl(vnd_errno_t *vnderr, int *syserr)
+{
+ int fd;
+ vnd_handle_t *vhp;
+
+ vhp = malloc(sizeof (vnd_handle_t));
+ if (vhp == NULL) {
+ if (vnderr != NULL)
+ *vnderr = VND_E_NOMEM;
+ if (syserr != NULL)
+ *syserr = 0;
+ return (NULL);
+ }
+ bzero(vhp, sizeof (vnd_handle_t));
+
+ fd = open("/dev/vnd/ctl", O_RDWR);
+ if (fd < 0) {
+ if (vnderr != NULL)
+ *vnderr = VND_E_SYS;
+ if (syserr != NULL)
+ *syserr = errno;
+ free(vhp);
+ return (NULL);
+ }
+
+ vhp->vh_fd = fd;
+ return (vhp);
+}
+
+vnd_handle_t *
+vnd_create(const char *zonename, const char *datalink, const char *linkname,
+ vnd_errno_t *vnderr, int *syserr)
+{
+ int ret;
+ vnd_handle_t *vhp;
+ vnd_ioc_attach_t via;
+ zoneid_t zid;
+
+ if (strlen(datalink) >= VND_NAMELEN) {
+ if (vnderr != NULL)
+ *vnderr = VND_E_BADNAME;
+ if (syserr != NULL)
+ *syserr = 0;
+ return (NULL);
+ }
+
+ vhp = vnd_open_ctl(vnderr, syserr);
+ if (vhp == NULL)
+ return (NULL); /* errno set for us */
+
+ if (zonename != NULL) {
+ zid = getzoneidbyname(zonename);
+ if (zid == -1) {
+ vnd_close(vhp);
+ if (vnderr != NULL)
+ *vnderr = VND_E_NOZONE;
+ if (syserr != NULL)
+ *syserr = 0;
+ return (NULL);
+ }
+ via.via_zoneid = zid;
+ } else {
+ via.via_zoneid = -1;
+ }
+
+ (void) strlcpy(via.via_name, datalink, sizeof (via.via_name));
+ via.via_errno = VND_E_SUCCESS;
+ if (ioctl(vhp->vh_fd, VND_IOC_ATTACH, &via) != 0) {
+ if (via.via_errno != VND_E_SUCCESS) {
+ if (vnderr != NULL)
+ *vnderr = via.via_errno;
+ if (syserr != NULL)
+ *syserr = 0;
+ } else {
+ if (vnderr != NULL)
+ *vnderr = VND_E_SYS;
+ if (syserr != NULL)
+ *syserr = errno;
+ }
+ vnd_close(vhp);
+ return (NULL);
+ }
+
+ ret = vnd_link(vhp, linkname);
+ if (ret != 0) {
+ if (vnderr != NULL)
+ *vnderr = vhp->vh_errno;
+ if (syserr != NULL)
+ *syserr = vhp->vh_syserr;
+ vnd_close(vhp);
+ return (NULL);
+ }
+
+ if (vnderr != NULL)
+ *vnderr = VND_E_SUCCESS;
+ if (syserr != NULL)
+ *syserr = 0;
+
+ return (vhp);
+}
+
+vnd_handle_t *
+vnd_open(const char *zone, const char *link, vnd_errno_t *vnderr, int *syserr)
+{
+ int fd, ret;
+ char path[MAXPATHLEN];
+ vnd_handle_t *vhp;
+
+ if (zone != NULL)
+ ret = snprintf(path, sizeof (path), "/dev/vnd/zone/%s/%s",
+ zone, link);
+ else
+ ret = snprintf(path, sizeof (path), "/dev/vnd/%s", link);
+
+ if (ret >= sizeof (path)) {
+ if (vnderr != NULL)
+ *vnderr = VND_E_BADNAME;
+ if (syserr != NULL)
+ *syserr = 0;
+ return (NULL);
+ }
+
+ fd = open(path, O_RDWR);
+ if (fd < 0) {
+ if (vnderr != NULL)
+ *vnderr = VND_E_SYS;
+ if (syserr != NULL)
+ *syserr = errno;
+ return (NULL);
+ }
+
+ vhp = malloc(sizeof (vnd_handle_t));
+ if (vhp == NULL) {
+ if (vnderr != NULL)
+ *vnderr = VND_E_NOMEM;
+ if (syserr != NULL)
+ *syserr = 0;
+ ret = close(fd);
+ assert(ret == 0);
+ return (NULL);
+ }
+
+ bzero(vhp, sizeof (vnd_handle_t));
+ vhp->vh_fd = fd;
+
+ return (vhp);
+}
+
+int
+vnd_unlink(vnd_handle_t *vhp)
+{
+ vnd_ioc_unlink_t viu;
+ viu.viu_errno = VND_E_SUCCESS;
+
+ if (ioctl(vhp->vh_fd, VND_IOC_UNLINK, &viu) != 0)
+ return (vnd_ioc_return(vhp, viu.viu_errno));
+
+ return (0);
+}
+
+int
+vnd_pollfd(vnd_handle_t *vhp)
+{
+ return (vhp->vh_fd);
+}
+
+int
+vnd_walk(vnd_walk_cb_t func, void *arg, vnd_errno_t *vnderr, int *syserr)
+{
+ vnd_handle_t *vhp;
+ vnd_ioc_list_t vl;
+ vnd_ioc_info_t *viip;
+ int i, ret;
+
+ vl.vl_nents = 0;
+ vl.vl_ents = NULL;
+
+ vhp = vnd_open_ctl(vnderr, syserr);
+ if (vhp == NULL)
+ return (-1); /* errno is set for us */
+
+ /* VND_IOC_LIST only returns generic errnos */
+ if (ioctl(vhp->vh_fd, VND_IOC_LIST, &vl) != 0) {
+ if (vnderr != NULL)
+ *vnderr = VND_E_SYS;
+ if (syserr != NULL)
+ *syserr = errno;
+ (void) vnd_ioc_return(vhp, VND_E_SUCCESS);
+ vnd_close(vhp);
+
+ return (-1);
+ }
+
+ if (vl.vl_actents == 0) {
+ vnd_close(vhp);
+ return (0);
+ }
+
+ viip = malloc(sizeof (vnd_ioc_info_t) * vl.vl_actents);
+ if (viip == NULL) {
+ if (vnderr != NULL)
+ *vnderr = VND_E_NOMEM;
+ if (syserr != NULL)
+ *syserr = 0;
+ vnd_close(vhp);
+ return (-1);
+ }
+
+ vl.vl_nents = vl.vl_actents;
+ vl.vl_ents = viip;
+
+ if (ioctl(vhp->vh_fd, VND_IOC_LIST, &vl) != 0) {
+ if (vnderr != NULL)
+ *vnderr = VND_E_SYS;
+ if (syserr != NULL)
+ *syserr = errno;
+ (void) vnd_ioc_return(vhp, VND_E_SUCCESS);
+ free(viip);
+ vnd_close(vhp);
+ return (-1);
+ }
+
+ ret = 0;
+ for (i = 0; i < MIN(vl.vl_nents, vl.vl_actents); i++) {
+ if (func((vnd_info_t *)(viip + i), arg) != 0) {
+ ret = 1;
+ break;
+ }
+ }
+
+ free(viip);
+ vnd_close(vhp);
+
+ return (ret);
+}
+
+static int
+vnd_prop_readonly(vnd_handle_t *vhp)
+{
+ vhp->vh_syserr = 0;
+ vhp->vh_errno = VND_E_PROPRDONLY;
+ return (-1);
+}
+
+/*ARGSUSED*/
+static int
+vnd_prop_getbuf(vnd_handle_t *vhp, int cmd, void *buf, size_t len)
+{
+ vnd_ioc_buf_t vib;
+ vnd_prop_buf_t *vpbp = (vnd_prop_buf_t *)buf;
+ vib.vib_errno = 0;
+
+ if (ioctl(vhp->vh_fd, cmd, &vib) != 0)
+ return (vnd_ioc_return(vhp, vib.vib_errno));
+
+ vpbp->vpb_size = vib.vib_size;
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+vnd_prop_setbuf(vnd_handle_t *vhp, int cmd, void *buf, size_t len)
+{
+ vnd_ioc_buf_t vib;
+ vnd_prop_buf_t *vpbp = (vnd_prop_buf_t *)buf;
+
+ vib.vib_errno = 0;
+ vib.vib_size = vpbp->vpb_size;
+ if (ioctl(vhp->vh_fd, cmd, &vib) != 0)
+ return (vnd_ioc_return(vhp, vib.vib_errno));
+
+ return (0);
+}
+
+typedef int (*vpt_prop_f)(vnd_handle_t *, int, void *, size_t);
+typedef struct vnd_prop_tab {
+ vnd_prop_t vpt_prop;
+ size_t vpt_size;
+ int vpt_ioctl_get;
+ int vpt_ioctl_set;
+ vpt_prop_f vpt_get;
+ vpt_prop_f vpt_set;
+} vnd_prop_tab_t;
+
+static vnd_prop_tab_t vnd_props[] = {
+ { VND_PROP_RXBUF, sizeof (vnd_prop_buf_t), VND_IOC_GETRXBUF,
+ VND_IOC_SETRXBUF, vnd_prop_getbuf, vnd_prop_setbuf},
+ { VND_PROP_TXBUF, sizeof (vnd_prop_buf_t), VND_IOC_GETTXBUF,
+ VND_IOC_SETTXBUF, vnd_prop_getbuf, vnd_prop_setbuf },
+ { VND_PROP_MAXBUF, sizeof (vnd_prop_buf_t), VND_IOC_GETMAXBUF,
+ -1, vnd_prop_getbuf, NULL },
+ { VND_PROP_MINTU, sizeof (vnd_prop_buf_t), VND_IOC_GETMINTU,
+ -1, vnd_prop_getbuf, NULL },
+ { VND_PROP_MAXTU, sizeof (vnd_prop_buf_t), VND_IOC_GETMAXTU,
+ -1, vnd_prop_getbuf, NULL },
+ { VND_PROP_MAX }
+};
+
+static int
+vnd_prop(vnd_handle_t *vhp, vnd_prop_t prop, void *buf, size_t len,
+ boolean_t get)
+{
+ vnd_prop_tab_t *vpt;
+
+ for (vpt = vnd_props; vpt->vpt_prop != VND_PROP_MAX; vpt++) {
+ if (vpt->vpt_prop != prop)
+ continue;
+
+ if (len != vpt->vpt_size) {
+ vhp->vh_errno = VND_E_BADPROPSIZE;
+ vhp->vh_syserr = 0;
+ return (-1);
+ }
+
+ if (get == B_TRUE) {
+ return (vpt->vpt_get(vhp, vpt->vpt_ioctl_get, buf,
+ len));
+ } else {
+ if (vpt->vpt_set == NULL)
+ return (vnd_prop_readonly(vhp));
+ return (vpt->vpt_set(vhp, vpt->vpt_ioctl_set, buf,
+ len));
+ }
+ }
+
+ vhp->vh_errno = VND_E_BADPROP;
+ vhp->vh_syserr = 0;
+ return (-1);
+}
+
+int
+vnd_prop_get(vnd_handle_t *vhp, vnd_prop_t prop, void *buf, size_t len)
+{
+ return (vnd_prop(vhp, prop, buf, len, B_TRUE));
+}
+
+int
+vnd_prop_set(vnd_handle_t *vhp, vnd_prop_t prop, void *buf, size_t len)
+{
+ return (vnd_prop(vhp, prop, buf, len, B_FALSE));
+}
+
+int
+vnd_prop_writeable(vnd_prop_t prop, boolean_t *write)
+{
+ vnd_prop_tab_t *vpt;
+
+ for (vpt = vnd_props; vpt->vpt_prop != VND_PROP_MAX; vpt++) {
+ if (vpt->vpt_prop != prop)
+ continue;
+
+ *write = (vpt->vpt_set != NULL);
+ return (0);
+ }
+
+ return (-1);
+}
+
+int
+vnd_prop_iter(vnd_handle_t *vhp, vnd_prop_iter_f func, void *arg)
+{
+ int i;
+
+ for (i = 0; i < VND_PROP_MAX; i++) {
+ if (func(vhp, i, arg) != 0)
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+vnd_frameio_read(vnd_handle_t *vhp, frameio_t *fiop)
+{
+ int ret;
+
+ ret = ioctl(vhp->vh_fd, VND_IOC_FRAMEIO_READ, fiop);
+ if (ret == -1) {
+ vhp->vh_errno = VND_E_SYS;
+ vhp->vh_syserr = errno;
+ }
+
+ return (ret);
+}
+
+int
+vnd_frameio_write(vnd_handle_t *vhp, frameio_t *fiop)
+{
+ int ret;
+
+ ret = ioctl(vhp->vh_fd, VND_IOC_FRAMEIO_WRITE, fiop);
+ if (ret == -1) {
+ vhp->vh_errno = VND_E_SYS;
+ vhp->vh_syserr = errno;
+ }
+
+ return (ret);
+}
diff --git a/usr/src/lib/libvnd/common/libvnd.h b/usr/src/lib/libvnd/common/libvnd.h
new file mode 100644
index 0000000000..ea92f113b6
--- /dev/null
+++ b/usr/src/lib/libvnd/common/libvnd.h
@@ -0,0 +1,84 @@
+/*
+ * 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 _LIBVND_H
+#define _LIBVND_H
+
+/*
+ * libvnd interfaces
+ */
+
+#include <stdint.h>
+#include <sys/vnd_errno.h>
+#include <sys/frameio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LIBVND_NAMELEN 32
+
+typedef struct vnd_handle vnd_handle_t;
+
+extern vnd_handle_t *vnd_create(const char *, const char *, const char *,
+ vnd_errno_t *, int *);
+extern vnd_handle_t *vnd_open(const char *, const char *, vnd_errno_t *, int *);
+extern int vnd_unlink(vnd_handle_t *);
+extern void vnd_close(vnd_handle_t *);
+extern vnd_errno_t vnd_errno(vnd_handle_t *);
+extern int vnd_syserrno(vnd_handle_t *);
+extern const char *vnd_strerror(vnd_errno_t);
+extern const char *vnd_strsyserror(int);
+
+extern int vnd_pollfd(vnd_handle_t *);
+
+typedef struct vnd_info {
+ uint32_t vi_version;
+ zoneid_t vi_zone;
+ char vi_name[LIBVND_NAMELEN];
+ char vi_datalink[LIBVND_NAMELEN];
+} vnd_info_t;
+
+typedef int (*vnd_walk_cb_t)(vnd_info_t *, void *);
+extern int vnd_walk(vnd_walk_cb_t, void *, vnd_errno_t *, int *);
+
+typedef enum vnd_prop {
+ VND_PROP_RXBUF = 0,
+ VND_PROP_TXBUF,
+ VND_PROP_MAXBUF,
+ VND_PROP_MINTU,
+ VND_PROP_MAXTU,
+ VND_PROP_MAX
+} vnd_prop_t;
+
+typedef struct vnd_prop_buf {
+ uint64_t vpb_size;
+} vnd_prop_buf_t;
+
+extern int vnd_prop_get(vnd_handle_t *, vnd_prop_t, void *, size_t);
+extern int vnd_prop_set(vnd_handle_t *, vnd_prop_t, void *, size_t);
+extern int vnd_prop_writeable(vnd_prop_t, boolean_t *);
+
+typedef int (*vnd_prop_iter_f)(vnd_handle_t *, vnd_prop_t, void *);
+extern int vnd_prop_iter(vnd_handle_t *, vnd_prop_iter_f, void *);
+
+extern int vnd_frameio_read(vnd_handle_t *, frameio_t *);
+extern int vnd_frameio_write(vnd_handle_t *, frameio_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBVND_H */
diff --git a/usr/src/lib/libvnd/common/llib-lvnd b/usr/src/lib/libvnd/common/llib-lvnd
new file mode 100644
index 0000000000..80a4229e32
--- /dev/null
+++ b/usr/src/lib/libvnd/common/llib-lvnd
@@ -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.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <libvnd.h>
diff --git a/usr/src/lib/libvnd/common/mapfile-vers b/usr/src/lib/libvnd/common/mapfile-vers
new file mode 100644
index 0000000000..0eb862ab60
--- /dev/null
+++ b/usr/src/lib/libvnd/common/mapfile-vers
@@ -0,0 +1,55 @@
+#
+# 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.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+#
+# TODO When this makes it into illumos we should make it a public interface
+#
+SYMBOL_VERSION ILLUMOSprivate {
+ global:
+ vnd_create;
+ vnd_close;
+ vnd_errno;
+ vnd_frameio_read;
+ vnd_frameio_write;
+ vnd_open;
+ vnd_pollfd;
+ vnd_prop_get;
+ vnd_prop_iter;
+ vnd_prop_set;
+ vnd_prop_writeable;
+ vnd_strerror;
+ vnd_strsyserror;
+ vnd_syserrno;
+ vnd_unlink;
+ vnd_walk;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libvnd/i386/Makefile b/usr/src/lib/libvnd/i386/Makefile
new file mode 100644
index 0000000000..41e699e8f8
--- /dev/null
+++ b/usr/src/lib/libvnd/i386/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.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libzdoor/Makefile b/usr/src/lib/libzdoor/Makefile
new file mode 100644
index 0000000000..50d9545ef1
--- /dev/null
+++ b/usr/src/lib/libzdoor/Makefile
@@ -0,0 +1,48 @@
+#
+# 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 2011 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.lib
+
+SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+_msg:
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libzdoor/Makefile.com b/usr/src/lib/libzdoor/Makefile.com
new file mode 100644
index 0000000000..09bde0f1b4
--- /dev/null
+++ b/usr/src/lib/libzdoor/Makefile.com
@@ -0,0 +1,50 @@
+#
+# 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 2011 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY= libzdoor.a
+VERS= .1
+OBJECTS= zdoor.o \
+ zdoor-int.o \
+ ztree.o \
+ zerror.o
+
+include ../../Makefile.lib
+include ../../Makefile.rootfs
+
+SRCDIR = ../common
+SRCS = $(OBJECTS:%.o=$(SRCDIR)/%.c)
+
+CPPFLAGS += -I$(SRCDIR) -D_REENTRANT -D_FILE_OFFSET_BITS=64
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc -lzonecfg -lcontract
+
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libzdoor/amd64/Makefile b/usr/src/lib/libzdoor/amd64/Makefile
new file mode 100644
index 0000000000..31be0ef7e6
--- /dev/null
+++ b/usr/src/lib/libzdoor/amd64/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 2011 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libzdoor/common/llib-lzdoor b/usr/src/lib/libzdoor/common/llib-lzdoor
new file mode 100644
index 0000000000..a21a904b11
--- /dev/null
+++ b/usr/src/lib/libzdoor/common/llib-lzdoor
@@ -0,0 +1,34 @@
+/*
+ * 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
+ */
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+/*
+ *
+ * Copyright 2011 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <zdoor.h>
diff --git a/usr/src/lib/libzdoor/common/mapfile-vers b/usr/src/lib/libzdoor/common/mapfile-vers
new file mode 100644
index 0000000000..38eba57ee2
--- /dev/null
+++ b/usr/src/lib/libzdoor/common/mapfile-vers
@@ -0,0 +1,48 @@
+#
+# 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.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate_1.1 {
+ global:
+ zdoor_handle_init;
+ zdoor_handle_destroy;
+ zdoor_open;
+ zdoor_close;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libzdoor/common/zdoor-int.c b/usr/src/lib/libzdoor/common/zdoor-int.c
new file mode 100644
index 0000000000..f77c1453d4
--- /dev/null
+++ b/usr/src/lib/libzdoor/common/zdoor-int.c
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ * Use is subject to license terms.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/fork.h>
+#include <libcontract.h>
+#include <libzonecfg.h>
+#include <sys/contract/process.h>
+#include <sys/ctfs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "zdoor-int.h"
+#include "zerror.h"
+
+#define ZDOOR_FMT_STR "/var/tmp/.%s"
+
+
+static int
+init_template(void)
+{
+ int fd = 0;
+ int err = 0;
+
+ fd = open64(CTFS_ROOT "/process/template", O_RDWR);
+ if (fd == -1)
+ return (-1);
+
+ err |= ct_tmpl_set_critical(fd, 0);
+ err |= ct_tmpl_set_informative(fd, 0);
+ err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
+ err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
+ if (err || ct_tmpl_activate(fd)) {
+ (void) close(fd);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+static int
+contract_latest(ctid_t *id)
+{
+ int cfd = 0;
+ int r = 0;
+ ct_stathdl_t st = {0};
+ ctid_t result = {0};
+
+ if ((cfd = open64(CTFS_ROOT "/process/latest", O_RDONLY)) == -1)
+ return (errno);
+ if ((r = ct_status_read(cfd, CTD_COMMON, &st)) != 0) {
+ (void) close(cfd);
+ return (r);
+ }
+
+ result = ct_status_get_id(st);
+ ct_status_free(st);
+ (void) close(cfd);
+
+ *id = result;
+ return (0);
+}
+
+static int
+close_on_exec(int fd)
+{
+ int flags = fcntl(fd, F_GETFD, 0);
+ if ((flags != -1) && (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1))
+ return (0);
+ return (-1);
+}
+
+static int
+contract_open(ctid_t ctid, const char *type, const char *file, int oflag)
+{
+ char path[PATH_MAX];
+ int n = 0;
+ int fd = 0;
+
+ if (type == NULL)
+ type = "all";
+
+ n = snprintf(path, PATH_MAX, CTFS_ROOT "/%s/%ld/%s", type, ctid, file);
+ if (n >= sizeof (path)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+
+ fd = open64(path, oflag);
+ if (fd != -1) {
+ if (close_on_exec(fd) == -1) {
+ int err = errno;
+ (void) close(fd);
+ errno = err;
+ return (-1);
+ }
+ }
+ return (fd);
+}
+
+static int
+contract_abandon_id(ctid_t ctid)
+{
+ int fd = 0;
+ int err = 0;
+
+ fd = contract_open(ctid, "all", "ctl", O_WRONLY);
+ if (fd == -1)
+ return (errno);
+
+ err = ct_ctl_abandon(fd);
+ (void) close(fd);
+
+ return (err);
+}
+
+/*
+ * zdoor_fattach(zone,service,door,detach_only) is heavily borrowed from
+ * zonestatd. Basically this forks, zone_enter's the targeted zone,
+ * fattaches to /var/tmp/.<service> with the door you've opened.
+ * detach_only gets passed in on door_stop to fdetach in the targeted zone.
+ * Note that this code really does require all the contract calls, which are
+ * all the static functions preceding this (have a look at zone_enter; without
+ * that code zone_enter will kick back EINVAL).
+ */
+int
+zdoor_fattach(zoneid_t zoneid, const char *service, int door, int detach_only)
+{
+ int fd = 0;
+ int len = 0;
+ int pid = 0;
+ int stat = 0;
+ int tmpl_fd = 0;
+ char path[MAXPATHLEN] = {0};
+ ctid_t ct = -1;
+
+ if (zoneid < 0) {
+ zdoor_debug("zdoor_fattach: zoneid < 0");
+ return (ZDOOR_ARGS_ERROR);
+ }
+
+ if (service == NULL) {
+ zdoor_debug("zdoor_fattach: NULL service");
+ return (ZDOOR_ARGS_ERROR);
+ }
+
+ if ((tmpl_fd = init_template()) < 0) {
+ zdoor_warn("zdoor_fattach: init contract for %d:%s failed",
+ zoneid, service);
+ return (ZDOOR_ERROR);
+ }
+
+ len = snprintf(NULL, 0, ZDOOR_FMT_STR, service) + 1;
+ if (len > MAXPATHLEN)
+ return (ZDOOR_ARGS_ERROR);
+ (void) snprintf(path, len, ZDOOR_FMT_STR, service);
+
+ zdoor_info("zdoor_fattach: ensuring %s", path);
+
+ pid = fork();
+ if (pid < 0) {
+ (void) ct_tmpl_clear(tmpl_fd);
+ zdoor_error("zdoor_fattach: unable to fork for zone_enter: %s",
+ strerror(errno));
+ return (ZDOOR_OK);
+ }
+
+ if (pid == 0) {
+ zdoor_debug("zdoor_fattach(CHILD): starting");
+ (void) ct_tmpl_clear(tmpl_fd);
+ (void) close(tmpl_fd);
+ if (zone_enter(zoneid) != 0) {
+ zdoor_debug("zdoor_fattach(CHILD): zone_enter fail %s",
+ strerror(errno));
+ if (errno == EINVAL) {
+ _exit(0);
+ }
+ _exit(1);
+ }
+ (void) fdetach(path);
+ (void) unlink(path);
+ if (detach_only) {
+ zdoor_debug("zdoor_fattach(CHILD): detach only, done");
+ _exit(0);
+ }
+ fd = open(path, O_CREAT|O_RDWR, 0644);
+ if (fd < 0) {
+ zdoor_debug("zdoor_fattach(CHILD): open failed: %s",
+ strerror(errno));
+ _exit(2);
+ }
+ if (fattach(door, path) != 0) {
+ zdoor_debug("zdoor_fattach(CHILD): fattach failed: %s",
+ strerror(errno));
+ _exit(3);
+ }
+ _exit(0);
+ }
+ if (contract_latest(&ct) == -1)
+ ct = -1;
+ (void) ct_tmpl_clear(tmpl_fd);
+ (void) close(tmpl_fd);
+ (void) contract_abandon_id(ct);
+
+ zdoor_debug("zdoor_fattach: waiting for child...");
+ while (waitpid(pid, &stat, 0) != pid)
+ ;
+ if (WIFEXITED(stat) && WEXITSTATUS(stat) == 0) {
+ zdoor_debug(" child exited with success");
+ zdoor_debug("zdoor_fattach: returning ZDOOR_OK");
+ return (ZDOOR_OK);
+ }
+
+ zdoor_debug(" child exited with %d", WEXITSTATUS(stat));
+ zdoor_debug("zdoor_fattach: returning ZDOOR_ERROR");
+ return (ZDOOR_ERROR);
+}
+
+/*
+ * zdoor_zone_is_running(zone) returns 1 if the specified zone is running, or 0
+ * if it is any other state. It additionally eats any other errors it
+ * encounters and returns 0 upon encountering them.
+ */
+boolean_t
+zdoor_zone_is_running(zoneid_t zoneid)
+{
+ zone_state_t state;
+ char zone[ZONENAME_MAX];
+ if (zoneid < 0)
+ return (B_FALSE);
+
+ if (getzonenamebyid(zoneid, zone, ZONENAME_MAX) < 0)
+ return (B_FALSE);
+
+ if (!zone_get_state((char *)zone, &state) == Z_OK)
+ return (B_FALSE);
+
+ return (state == ZONE_STATE_RUNNING);
+}
+
+/*
+ * zdoor_cookie_create simply allocates and initializes
+ * memory. Returns NULL on any error.
+ */
+zdoor_cookie_t *
+zdoor_cookie_create(const char *zonename, const char *service,
+const void *biscuit)
+{
+ zdoor_cookie_t *cookie = NULL;
+
+ if (zonename == NULL || service == NULL)
+ return (NULL);
+
+ cookie = (zdoor_cookie_t *)calloc(1, sizeof (zdoor_cookie_t));
+ if (cookie == NULL) {
+ OUT_OF_MEMORY();
+ return (NULL);
+ }
+ cookie->zdc_biscuit = (void *)biscuit;
+ cookie->zdc_zonename = strdup((char *)zonename);
+ if (cookie->zdc_zonename == NULL) {
+ zdoor_cookie_free(cookie);
+ OUT_OF_MEMORY();
+ return (NULL);
+ }
+ cookie->zdc_service = strdup((char *)service);
+ if (cookie->zdc_service == NULL) {
+ zdoor_cookie_free(cookie);
+ OUT_OF_MEMORY();
+ return (NULL);
+ }
+
+ return (cookie);
+}
+
+/*
+ * zdoor_cookie_free(cookie) cleans up any memory associated with the
+ * specified cookie.
+ */
+void
+zdoor_cookie_free(zdoor_cookie_t *cookie)
+{
+ if (cookie == NULL)
+ return;
+
+ if (cookie->zdc_zonename != NULL) {
+ free(cookie->zdc_zonename);
+ cookie->zdc_zonename = NULL;
+ }
+
+ if (cookie->zdc_service != NULL) {
+ free(cookie->zdc_service);
+ cookie->zdc_service = NULL;
+ }
+
+ free(cookie);
+}
diff --git a/usr/src/lib/libzdoor/common/zdoor-int.h b/usr/src/lib/libzdoor/common/zdoor-int.h
new file mode 100644
index 0000000000..782452c426
--- /dev/null
+++ b/usr/src/lib/libzdoor/common/zdoor-int.h
@@ -0,0 +1,64 @@
+/*
+ * 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 2011 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ZDOOR_INT_H
+#define _ZDOOR_INT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <pthread.h>
+#include <zdoor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum zdoor_action_t {
+ ZDOOR_ACTION_NOOP,
+ ZDOOR_ACTION_STOP,
+ ZDOOR_ACTION_START
+} zdoor_action_t;
+
+struct zdoor_handle {
+ pthread_mutex_t zdh_lock;
+ void *zdh_zonecfg_handle;
+ void *zdh_ztree;
+};
+
+zdoor_cookie_t *zdoor_cookie_create(const char *zonename, const char *service,
+ const void *biscuit);
+
+void zdoor_cookie_free(zdoor_cookie_t *cookie);
+
+boolean_t zdoor_zone_is_running(zoneid_t zoneid);
+
+int zdoor_fattach(zoneid_t zoneid, const char *service, int door,
+ int detach_only);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZDOOR_INT_H */
diff --git a/usr/src/lib/libzdoor/common/zdoor.c b/usr/src/lib/libzdoor/common/zdoor.c
new file mode 100644
index 0000000000..aa52458f90
--- /dev/null
+++ b/usr/src/lib/libzdoor/common/zdoor.c
@@ -0,0 +1,431 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <alloca.h>
+#include <door.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libzonecfg.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <unistd.h>
+#include <zdoor.h>
+#include <zone.h>
+
+#include "zdoor-int.h"
+#include "zerror.h"
+#include "ztree.h"
+
+extern void *
+zonecfg_notify_bind(int(*func)(const char *zonename, zoneid_t zid,
+ const char *newstate, const char *oldstate, hrtime_t when,
+ void *p), void *p);
+
+extern void
+zonecfg_notify_unbind(void *handle);
+
+/*
+ * _callback(cookie, door_args...) is our private function that we tell
+ * the Solaris door API about. This function does some sanity checking on
+ * arguments and issues a callback to the owner of this door. That API
+ * will return us memory that needs to be sent back to the client on the
+ * other end of the door, but since the door_return API never gives you
+ * back control of the function, this does a simple alloca/memcpy and
+ * frees up the memory pointed to by the parent. While this really doesn't
+ * let a client do much other than pass a simple struct of primitives (or
+ * more likely more common a char *), that's the way the door API works,
+ * and so this isn't really imposing any restriction that didn't already
+ * need to be dealt with by someone. This is why the zdoor_result structure
+ * takes a char *, rather than a void * for the data pointer.
+ */
+static void
+_callback(void *cookie, char *argp, size_t arg_size, door_desc_t *dp,
+ uint_t n_desc)
+{
+ zdoor_result_t *result = NULL;
+ void *door_response = NULL;
+ int size = 0;
+ dtree_entry_t *entry = (dtree_entry_t *)cookie;
+
+ if (entry == NULL) {
+ zdoor_warn("_callback: NULL cookie? door_returning");
+ (void) door_return(NULL, 0, NULL, 0);
+ }
+
+ (void) pthread_mutex_lock(&entry->dte_parent->zte_parent->zdh_lock);
+ zdoor_debug("_callback: calling back with %p", entry->dte_cookie);
+ result = entry->dte_callback(entry->dte_cookie, argp, arg_size);
+ zdoor_debug("_callback: app callback returned %p", result);
+ (void) pthread_mutex_unlock(&entry->dte_parent->zte_parent->zdh_lock);
+
+ if (result == NULL) {
+ zdoor_debug("_callback: door_returning NULL");
+ (void) door_return(NULL, 0, NULL, 0);
+ }
+
+ if (result->zdr_data != NULL && result->zdr_size > 0) {
+ door_response = alloca(result->zdr_size);
+ if (door_response != NULL) {
+ size = result->zdr_size;
+ (void) memcpy(door_response,
+ (void *) result->zdr_data, size);
+ }
+ }
+
+ if (result->zdr_data != NULL)
+ free(result->zdr_data);
+ free(result);
+
+ zdoor_debug("_callback: door_returning %p, %d", door_response, size);
+ (void) door_return(door_response, size, NULL, 0);
+}
+
+static void
+zdoor_stop(dtree_entry_t *entry)
+{
+ zoneid_t zid = -1;
+
+ if (entry == NULL) {
+ zdoor_debug("zdoor_stop: NULL arguments");
+ return;
+ }
+
+ zdoor_debug("zdoor_stop: entry=%p, zone=%s, service=%s",
+ entry, entry->dte_parent->zte_zonename, entry->dte_service);
+
+ zid = getzoneidbyname(entry->dte_parent->zte_zonename);
+ (void) zdoor_fattach(zid, entry->dte_service, entry->dte_door, 1);
+ (void) door_revoke(entry->dte_door);
+ entry->dte_door = -1;
+
+ zdoor_debug("zdoor_stop returning");
+}
+
+/*
+ * zdoor_create is called both by the main API
+ * call zdoor_open, as well as by the zone_monitor code upon a zone restart
+ * (assuming it already has a door in it). This code assumes that the
+ * permissions were correct (e.g., the target door is not a GZ, that this
+ * program is being run out of the GZ), but does not assume that the target
+ * door file has not changed out from under us, so that is explicitly rechecked.
+ *
+ * This also assumes the parent has already locked handle.
+ */
+static int
+zdoor_create(dtree_entry_t *entry)
+{
+ int status = ZDOOR_OK;
+ zoneid_t zid = -1;
+
+ if (entry == NULL) {
+ zdoor_debug("zdoor_create: NULL arguments");
+ return (ZDOOR_ARGS_ERROR);
+ }
+
+ zdoor_debug("zdoor_create: entry=%p, zone=%s, service=%s",
+ entry, entry->dte_parent->zte_zonename, entry->dte_service);
+
+ zid = getzoneidbyname(entry->dte_parent->zte_zonename);
+ if (zid < 0) {
+ zdoor_info("zdoor_create: %s is a non-existient zone",
+ entry->dte_parent->zte_zonename);
+ return (ZDOOR_ERROR);
+ }
+ if (!zdoor_zone_is_running(zid)) {
+ zdoor_debug("zdoor_create: %s is not running",
+ entry->dte_parent->zte_zonename);
+ return (ZDOOR_ZONE_NOT_RUNNING);
+ }
+
+ entry->dte_door = door_create(_callback, entry, 0);
+ zdoor_info("zdoor_create: door_create returned %d", entry->dte_door);
+ if (entry->dte_door < 0) {
+ zdoor_stop(entry);
+ return (ZDOOR_ERROR);
+ }
+
+ status = zdoor_fattach(zid, entry->dte_service, entry->dte_door, 0);
+
+ zdoor_debug("zdoor_create: returning %d", status);
+ return (status);
+}
+
+
+/*
+ * door_visitor(entry) is a callback from the ztree code that checks whether
+ * or not we should be taking some action on a given door. Note that the
+ * callpath to this API is:
+ * SYSTEM ->
+ * zone_monitor ->
+ * ztree_walk ->
+ * door_visitor
+ *
+ * Which is important to note that this API assumes that all things needing
+ * locking are locked by a parent caller (which is the zone_monitor).
+ */
+static void
+zdoor_visitor(dtree_entry_t *entry)
+{
+ if (entry == NULL) {
+ zdoor_info("zdoor_visitor: entered with NULL entry");
+ return;
+ }
+
+ zdoor_debug("zdoor_visitor: entered for entry=%p, service=%s",
+ entry, entry->dte_service);
+
+ if (entry->dte_parent->zte_action == ZDOOR_ACTION_STOP) {
+ zdoor_debug(" stopping zdoor");
+ zdoor_stop(entry);
+ } else if (entry->dte_parent->zte_action == ZDOOR_ACTION_START) {
+ zdoor_debug(" starting zdoor");
+ if (zdoor_create(entry) != ZDOOR_OK) {
+ zdoor_error("door_visitor: Unable to restart zdoor\n");
+ }
+ }
+}
+
+/*
+ * zone_monitor(zonename, zid, newstate, oldstate, when, cookie) is our
+ * registered callback with libzonecfg to notify us of any changes to a
+ * given zone. This activates a walk on all doors for a zone iff the state
+ * is changing from running or into running.
+ */
+static int
+zone_monitor(const char *zonename, zoneid_t zid, const char *newstate,
+ const char *oldstate, hrtime_t when, void *p)
+{
+ zdoor_handle_t handle = (zdoor_handle_t)p;
+ ztree_entry_t *entry = NULL;
+
+ if (handle == NULL) {
+ zdoor_warn("zone_monitor: entered with NULL handle?");
+ return (-1);
+ }
+
+ zdoor_info("zone_monitor: zone=%s, zid=%d, newst=%s, oldst=%s, p=%p",
+ zonename, zid, newstate, oldstate, p);
+
+ (void) pthread_mutex_lock(&(handle->zdh_lock));
+ entry = ztree_zone_find(handle, zonename);
+ if (entry != NULL) {
+ zdoor_debug(" found entry in ztree");
+ entry->zte_action = ZDOOR_ACTION_NOOP;
+ if (strcmp("running", newstate) == 0) {
+ if (strcmp("ready", oldstate) == 0)
+ entry->zte_action = ZDOOR_ACTION_START;
+ } else if (strcmp("shutting_down", newstate) == 0) {
+ if (strcmp("running", oldstate) == 0)
+ entry->zte_action = ZDOOR_ACTION_STOP;
+ }
+ zdoor_debug(" set state to: %d", entry->zte_action);
+ if (entry->zte_action != ZDOOR_ACTION_NOOP)
+ ztree_walk_doors(handle, zonename);
+ }
+ (void) pthread_mutex_unlock(&(handle->zdh_lock));
+
+ zdoor_info("zone_monitor: returning");
+ return (0);
+}
+
+zdoor_handle_t
+zdoor_handle_init()
+{
+ zdoor_handle_t handle = NULL;
+
+ zdoor_debug("zdoor_handle_init entered");
+
+ handle = (zdoor_handle_t)calloc(1, sizeof (struct zdoor_handle));
+ if (handle == NULL) {
+ OUT_OF_MEMORY();
+ return (NULL);
+ }
+
+ (void) pthread_mutex_init(&(handle->zdh_lock), NULL);
+ handle->zdh_zonecfg_handle = zonecfg_notify_bind(zone_monitor, handle);
+ if (handle->zdh_zonecfg_handle == NULL) {
+ zdoor_error("zonecfg_notify_bind failure: %s", strerror(errno));
+ return (NULL);
+ }
+
+ zdoor_debug("zdoor_handle_init returning %p", handle);
+ return (handle);
+}
+
+void
+zdoor_handle_destroy(zdoor_handle_t handle)
+{
+ if (handle == NULL) {
+ zdoor_debug("zdoor_handle_destroy: NULL arguments");
+ return;
+ }
+
+ zdoor_debug("zdoor_handle_destroy: handle=%p", handle);
+
+ (void) pthread_mutex_lock(&(handle->zdh_lock));
+ zonecfg_notify_unbind(handle->zdh_zonecfg_handle);
+ (void) pthread_mutex_unlock(&(handle->zdh_lock));
+ (void) pthread_mutex_destroy(&(handle->zdh_lock));
+ free(handle);
+}
+
+/*
+ * zdoor_open(zone, service, biscuit, callback) is the main public facing API in
+ * libzdoor. It will open a door with the name .[service] under
+ * [zonepath]/root/var/tmp, where [zonepath] is resolved on the fly. Note this
+ * API can only be invoked from the global zone, and will not allow you to open
+ * a zdoor in the global zone.
+ */
+int
+zdoor_open(zdoor_handle_t handle, const char *zonename, const char *service,
+ void *biscuit, zdoor_callback callback)
+{
+ zdoor_cookie_t *zdoor_cookie = NULL;
+ int rc = -1;
+ int status = ZDOOR_OK;
+ zoneid_t zid = -1;
+ dtree_entry_t *entry = NULL;
+
+ if (handle == NULL || zonename == NULL ||
+ service == NULL || callback == NULL) {
+ zdoor_debug("zdoor_open: NULL arguments");
+ return (ZDOOR_ARGS_ERROR);
+ }
+ zdoor_debug("zdoor_open: entered: handle=%p, zone=%s, service=%s",
+ handle, zonename, service);
+
+ if (getzoneid() != GLOBAL_ZONEID) {
+ zdoor_warn("zdoor_open: not invoked from global zone");
+ return (ZDOOR_NOT_GLOBAL_ZONE);
+ }
+
+
+ zid = getzoneidbyname(zonename);
+ if (zid < 0) {
+ zdoor_info("zdoor_open: %s is a non-existent zone", zonename);
+ return (ZDOOR_ARGS_ERROR);
+ }
+
+ if (zid == GLOBAL_ZONEID) {
+ zdoor_warn("zdoor_open: zdoors not allowed in global zone");
+ return (ZDOOR_ZONE_FORBIDDEN);
+ }
+
+ if (!zdoor_zone_is_running(zid)) {
+ zdoor_info("zdoor_open: %s is not running", zonename);
+ return (ZDOOR_ZONE_NOT_RUNNING);
+ }
+
+ zdoor_cookie = zdoor_cookie_create(zonename, service, biscuit);
+ if (zdoor_cookie == NULL) {
+ OUT_OF_MEMORY();
+ return (ZDOOR_OUT_OF_MEMORY);
+ }
+
+ (void) pthread_mutex_lock(&(handle->zdh_lock));
+ rc = ztree_zone_add(handle, zonename, zdoor_visitor);
+ if (rc != ZTREE_SUCCESS && rc != ZTREE_ALREADY_EXISTS) {
+ zdoor_debug("zdoor_open: unable to add zone to ztree: %d", rc);
+ status = ZDOOR_ERROR;
+ goto out;
+ }
+ rc = ztree_door_add(handle, zonename, service, callback,
+ zdoor_cookie);
+ if (rc != ZTREE_SUCCESS) {
+ zdoor_debug("zdoor_open: unable to add door to ztree: %d", rc);
+ if (rc == ZTREE_ALREADY_EXISTS) {
+ zdoor_warn("service %s already has a zdoor", service);
+ }
+ status = ZDOOR_ERROR;
+ goto out;
+ }
+
+ entry = ztree_door_find(handle, zonename, service);
+ if (entry == NULL) {
+ zdoor_debug("zdoor_open: unable to find door in ztree?");
+ status = ZDOOR_ERROR;
+ goto out;
+ }
+ if (zdoor_create(entry) != ZDOOR_OK) {
+ zdoor_info("zdoor_open: zdoor_create failed.");
+ status = ZDOOR_ERROR;
+ goto out;
+ }
+out:
+ if (status != ZDOOR_OK) {
+ zdoor_debug("zdoor_open: status not ok, stopping and cleaning");
+ zdoor_stop(entry);
+ (void) ztree_door_remove(handle, entry);
+ zdoor_cookie_free(zdoor_cookie);
+ }
+ (void) pthread_mutex_unlock(&(handle->zdh_lock));
+ zdoor_debug("zdoor_open: returning %d", status);
+ return (status);
+}
+
+/*
+ * zdoor_close(zone, service) unregisters a previously created zdoor, and
+ * returns the biscuit provided at creation time, so the caller can free it.
+ * Returns NULL on any error.
+ */
+void *
+zdoor_close(zdoor_handle_t handle, const char *zonename, const char *service)
+{
+ dtree_entry_t *entry = NULL;
+ zdoor_cookie_t *cookie = NULL;
+ void *biscuit = NULL;
+
+ if (handle == NULL || zonename == NULL || service == NULL) {
+ zdoor_debug("zdoor_close: NULL arguments");
+ return (NULL);
+ }
+
+ zdoor_debug("zdoor_close: entered handle=%p, zone=%s, service=%s",
+ handle, zonename, service);
+
+ (void) pthread_mutex_lock(&(handle->zdh_lock));
+
+ entry = ztree_door_find(handle, zonename, service);
+ if (entry != NULL) {
+ zdoor_debug("zdoor_close: found door in ztree, stopping");
+ zdoor_stop(entry);
+ cookie = ztree_door_remove(handle, entry);
+ if (cookie != NULL) {
+ biscuit = cookie->zdc_biscuit;
+ zdoor_cookie_free(cookie);
+ }
+ } else {
+ zdoor_debug("zdoor_close: didn't find door in ztree");
+ }
+
+ (void) pthread_mutex_unlock(&(handle->zdh_lock));
+
+ zdoor_debug("zdoor_close: returning %p", biscuit);
+ return (biscuit);
+}
diff --git a/usr/src/lib/libzdoor/common/zerror.c b/usr/src/lib/libzdoor/common/zerror.c
new file mode 100644
index 0000000000..5ccb449e1b
--- /dev/null
+++ b/usr/src/lib/libzdoor/common/zerror.c
@@ -0,0 +1,117 @@
+/*
+ * 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 2011 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "zerror.h"
+
+static const char *PREFIX = "%s ZDOOR:%s:T(%d): ";
+
+static const char *DEBUG_ENV_VAR = "ZDOOR_TRACE";
+
+static boolean_t
+is_debug_enabled()
+{
+ boolean_t enabled = B_FALSE;
+ const char *_envp = getenv(DEBUG_ENV_VAR);
+ if (_envp != NULL && atoi(_envp) >= 2)
+ enabled = B_TRUE;
+
+ return (enabled);
+}
+
+static boolean_t
+is_info_enabled()
+{
+ boolean_t enabled = B_FALSE;
+ const char *_envp = getenv(DEBUG_ENV_VAR);
+ if (_envp != NULL && atoi(_envp) >= 1)
+ enabled = B_TRUE;
+
+ return (enabled);
+}
+
+void
+zdoor_debug(const char *fmt, ...)
+{
+ va_list alist;
+
+ if (!is_debug_enabled())
+ return;
+
+ va_start(alist, fmt);
+
+ (void) fprintf(stderr, PREFIX, __TIME__, "DEBUG", pthread_self());
+ (void) vfprintf(stderr, fmt, alist);
+ (void) fprintf(stderr, "\n");
+ va_end(alist);
+}
+
+void
+zdoor_info(const char *fmt, ...)
+{
+ va_list alist;
+
+ if (!is_info_enabled())
+ return;
+
+ va_start(alist, fmt);
+
+ (void) fprintf(stderr, PREFIX, __TIME__, "INFO", pthread_self());
+ (void) vfprintf(stderr, fmt, alist);
+ (void) fprintf(stderr, "\n");
+ va_end(alist);
+}
+
+void
+zdoor_warn(const char *fmt, ...)
+{
+ va_list alist;
+
+ va_start(alist, fmt);
+
+ (void) fprintf(stderr, PREFIX, __TIME__, "WARN", pthread_self());
+ (void) vfprintf(stderr, fmt, alist);
+ (void) fprintf(stderr, "\n");
+ va_end(alist);
+}
+
+void
+zdoor_error(const char *fmt, ...)
+{
+ va_list alist;
+
+ va_start(alist, fmt);
+
+ (void) fprintf(stderr, PREFIX, __TIME__, "ERROR", pthread_self());
+ (void) vfprintf(stderr, fmt, alist);
+ (void) fprintf(stderr, "\n");
+ va_end(alist);
+}
diff --git a/usr/src/lib/libzdoor/common/zerror.h b/usr/src/lib/libzdoor/common/zerror.h
new file mode 100644
index 0000000000..afc848fcea
--- /dev/null
+++ b/usr/src/lib/libzdoor/common/zerror.h
@@ -0,0 +1,48 @@
+/*
+ * 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 2011 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ZERROR_H
+#define _ZERROR_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void zdoor_debug(const char *fmt, ...);
+extern void zdoor_info(const char *fmt, ...);
+extern void zdoor_warn(const char *fmt, ...);
+extern void zdoor_error(const char *fmt, ...);
+
+#define OUT_OF_MEMORY() \
+ zdoor_error("Out of Memory at %s:%d", __FILE__, __LINE__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZERROR_H */
diff --git a/usr/src/lib/libzdoor/common/ztree.c b/usr/src/lib/libzdoor/common/ztree.c
new file mode 100644
index 0000000000..22ef0ad94f
--- /dev/null
+++ b/usr/src/lib/libzdoor/common/ztree.c
@@ -0,0 +1,343 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <search.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "zerror.h"
+#include "ztree.h"
+
+
+/*
+ * ztree is just a helpful wrapper over a tsearch binary tree that deals with
+ * all of the libzdoor types.
+ *
+ * So what this ztree actually is is a tree of trees. The outer tree is a tree
+ * of zones, and each node holds a tree of doors.
+ */
+
+/*
+ * _ztree_compare(p1, p2) is the tsearch callback for comparing the "outer"
+ * tree (e.g., the one of zones).
+ */
+static int
+_ztree_compare(const void *p1, const void *p2)
+{
+ ztree_entry_t *z1 = (ztree_entry_t *)p1;
+ ztree_entry_t *z2 = (ztree_entry_t *)p2;
+
+ if (z1 == NULL && z2 == NULL)
+ return (0);
+ if (z1 == NULL && z2 != NULL)
+ return (-1);
+ if (z1 != NULL && z2 == NULL)
+ return (1);
+
+ return (strcmp(z1->zte_zonename, z2->zte_zonename));
+}
+
+/*
+ * _dtree_compare(p1, p2) is the tsearch callback for comparing the "inner"
+ * tree (e.g., the one of doors).
+ */
+static int
+_dtree_compare(const void *p1, const void *p2)
+{
+ dtree_entry_t *d1 = (dtree_entry_t *)p1;
+ dtree_entry_t *d2 = (dtree_entry_t *)p2;
+
+ if (d1 == NULL && d2 == NULL)
+ return (0);
+ if (d1 == NULL && d2 != NULL)
+ return (-1);
+ if (d1 != NULL && d2 == NULL)
+ return (1);
+
+ return (strcmp(d1->dte_service, d2->dte_service));
+}
+
+static void
+ztree_entry_free(ztree_entry_t *entry)
+{
+ if (entry == NULL)
+ return;
+
+ if (entry->zte_zonename != NULL)
+ free(entry->zte_zonename);
+
+ free(entry);
+}
+
+static void
+dtree_entry_free(dtree_entry_t *entry)
+{
+ if (entry == NULL)
+ return;
+
+ if (entry->dte_service)
+ free(entry->dte_service);
+
+ free(entry);
+}
+
+
+/*
+ * ztree_zone_add inserts a new zone into the tree iff
+ * there is not already an entry for that zone. This method returns one of
+ * four possible return codes, ZTREE_SUCCESS on :), ZTREE_ARGUMENT_ERROR if
+ * zone is NULL, ZTREE_ERROR if there is internal failure (e.g., OOM), and
+ * ZTREE_ALREADY_EXISTS if the zone is already in the tree.
+ */
+int
+ztree_zone_add(struct zdoor_handle *handle, const char *zonename,
+ ztree_door_visitor visitor)
+{
+ ztree_entry_t *entry = NULL;
+ void *ret = NULL;
+ int status = ZTREE_SUCCESS;
+
+ if (handle == NULL || zonename == NULL)
+ return (ZTREE_ARGUMENT_ERROR);
+
+ entry = (ztree_entry_t *)calloc(1, sizeof (ztree_entry_t));
+ if (entry == NULL) {
+ OUT_OF_MEMORY();
+ return (ZTREE_ERROR);
+ }
+ entry->zte_zonename = strdup(zonename);
+ if (entry->zte_zonename == NULL) {
+ ztree_entry_free(entry);
+ OUT_OF_MEMORY();
+ return (ZTREE_ERROR);
+ }
+ entry->zte_action = ZDOOR_ACTION_NOOP;
+ entry->zte_parent = handle;
+ entry->zte_visitor = visitor;
+
+ ret = tsearch(entry, &(handle->zdh_ztree), _ztree_compare);
+ if (ret == NULL) {
+ ztree_entry_free(entry);
+ status = ZTREE_ERROR;
+ OUT_OF_MEMORY();
+ } else if ((*(ztree_entry_t **)ret) != entry) {
+ ztree_entry_free(entry);
+ status = ZTREE_ALREADY_EXISTS;
+ }
+
+ return (status);
+}
+
+
+/*
+ * ztree_zone_find returns the entry in the "outer" tree representing
+ * this zone, if it exists, NULL otherwise.
+ */
+ztree_entry_t *
+ztree_zone_find(struct zdoor_handle *handle, const char *zonename)
+{
+ ztree_entry_t key = {0};
+ void *ret = NULL;
+
+ if (handle == NULL || zonename == NULL)
+ return (NULL);
+
+ key.zte_zonename = (char *)zonename;
+ ret = tfind(&key, &(handle->zdh_ztree), _ztree_compare);
+
+ return (ret != NULL ? *(ztree_entry_t **)ret : NULL);
+}
+
+
+/*
+ * ztree_zone_remove removes an entry from the "outer" zone iff the
+ * zone exists. The cookie set by the creator is returned.
+ */
+void
+ztree_zone_remove(struct zdoor_handle *handle, ztree_entry_t *entry)
+{
+ if (handle == NULL || entry == NULL)
+ return;
+
+ (void) tdelete(entry, &(handle->zdh_ztree), _ztree_compare);
+ ztree_entry_free(entry);
+}
+
+
+/*
+ * ztree_door_add inserts a new door into the inner tree iff
+ * there is not already an entry for that door. This method returns one of
+ * four possible return codes, ZTREE_SUCCESS on :), ZTREE_ARGUMENT_ERROR if
+ * zone is NULL, ZTREE_ERROR if there is internal failure (e.g., OOM), and
+ * ZTREE_ALREADY_EXISTS if the door is already in the tree.
+ */
+int
+ztree_door_add(struct zdoor_handle *handle, const char *zonename,
+ const char *service, zdoor_callback callback, zdoor_cookie_t *cookie)
+{
+ dtree_entry_t *entry = NULL;
+ ztree_entry_t *znode = NULL;
+ void *ret = NULL;
+ int status = ZTREE_SUCCESS;
+
+ if (handle == NULL || zonename == NULL || service == NULL)
+ return (ZTREE_ARGUMENT_ERROR);
+
+ znode = ztree_zone_find(handle, zonename);
+ if (znode == NULL)
+ return (ZTREE_NOT_FOUND);
+
+ entry = (dtree_entry_t *)calloc(1, sizeof (dtree_entry_t));
+ if (entry == NULL) {
+ OUT_OF_MEMORY();
+ return (ZTREE_ERROR);
+ }
+ entry->dte_parent = znode;
+ entry->dte_callback = callback;
+ entry->dte_cookie = cookie;
+ entry->dte_service = strdup(service);
+ if (entry->dte_service == NULL) {
+ free(entry);
+ OUT_OF_MEMORY();
+ return (ZTREE_ERROR);
+ }
+
+ ret = tsearch(entry, &(znode->zte_door_tree), _dtree_compare);
+ if (ret == NULL) {
+ dtree_entry_free(entry);
+ OUT_OF_MEMORY();
+ status = ZTREE_ERROR;
+ } else if ((*(dtree_entry_t **)ret) != entry) {
+ dtree_entry_free(entry);
+ status = ZTREE_ALREADY_EXISTS;
+ } else {
+ znode->zte_num_doors++;
+ }
+
+ return (status);
+}
+
+
+/*
+ * ztree_door_find returns the entry in the "inner" tree
+ * representing this zone, if it exists, NULL otherwise.
+ */
+dtree_entry_t *
+ztree_door_find(struct zdoor_handle *handle, const char *zonename,
+ const char *service)
+{
+ dtree_entry_t key = {0};
+ ztree_entry_t *znode = NULL;
+ void *ret = NULL;
+
+ if (handle == NULL || zonename == NULL || service == NULL)
+ return (NULL);
+
+ znode = ztree_zone_find(handle, zonename);
+ if (znode == NULL)
+ return (NULL);
+
+ key.dte_service = (char *)service;
+ ret = tfind(&key, &(znode->zte_door_tree), _dtree_compare);
+
+ return (ret != NULL ? *(dtree_entry_t **)ret : NULL);
+}
+
+
+/*
+ * ztree_door_remove(zone, door) removes a node from the inner tree iff
+ * both the door and zone exist. Note this frees the node as well. The
+ * cookie set by the creator is returned.
+ */
+zdoor_cookie_t *
+ztree_door_remove(struct zdoor_handle *handle, dtree_entry_t *entry)
+{
+ zdoor_cookie_t *cookie = NULL;
+ ztree_entry_t *znode = NULL;
+
+ if (handle == NULL || entry == NULL)
+ return (NULL);
+
+ znode = entry->dte_parent;
+ cookie = entry->dte_cookie;
+
+ (void) tdelete(entry, &(znode->zte_door_tree), _dtree_compare);
+ dtree_entry_free(entry);
+
+ znode->zte_num_doors--;
+ if (znode->zte_num_doors == 0) {
+ zdoor_debug("ztree: zone %s has no doors left, removing",
+ znode->zte_zonename);
+ ztree_zone_remove(handle, znode);
+ }
+
+ return (cookie);
+}
+
+
+/*
+ * _ztree_door_visitor(nodep, which, depth) is the private function we use
+ * to wrap up tsearch's goofy API. We're really just using this to ensure
+ * zdoor doesn't get called > 1 times for a given entity in the ztree.
+ */
+static void
+_ztree_door_visitor(const void *nodep, const VISIT which, const int depth)
+{
+ dtree_entry_t *entry = *(dtree_entry_t **)nodep;
+
+ if (entry == NULL)
+ return;
+
+ switch (which) {
+ case preorder:
+ case endorder:
+ break;
+ case postorder:
+ case leaf:
+ if (entry->dte_parent->zte_visitor != NULL)
+ entry->dte_parent->zte_visitor(entry);
+ break;
+ }
+}
+
+
+/*
+ * ztree_walk_doors(zone) will proceed to visit every node in the "inner" tree
+ * for this zone, and callback the visitor that was registered on tree creation.
+ */
+void
+ztree_walk_doors(struct zdoor_handle *handle, const char *zonename)
+{
+ ztree_entry_t *znode = NULL;
+
+ if (handle == NULL || zonename == NULL)
+ return;
+
+ znode = ztree_zone_find(handle, zonename);
+ if (znode == NULL)
+ return;
+
+ twalk(znode->zte_door_tree, _ztree_door_visitor);
+}
diff --git a/usr/src/lib/libzdoor/common/ztree.h b/usr/src/lib/libzdoor/common/ztree.h
new file mode 100644
index 0000000000..b46dace287
--- /dev/null
+++ b/usr/src/lib/libzdoor/common/ztree.h
@@ -0,0 +1,88 @@
+/*
+ * 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 2011 Joyent, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ZTREE_H
+#define _ZTREE_H
+
+#include <zdoor.h>
+#include <zone.h>
+#include "zdoor-int.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dtree_entry;
+
+typedef void (*ztree_door_visitor)(struct dtree_entry *entry);
+
+typedef struct ztree_entry {
+ char *zte_zonename;
+ zdoor_action_t zte_action;
+ int zte_num_doors;
+ void *zte_door_tree;
+ ztree_door_visitor zte_visitor;
+ struct zdoor_handle *zte_parent;
+} ztree_entry_t;
+
+typedef struct dtree_entry {
+ char *dte_service;
+ int dte_door;
+ zdoor_callback dte_callback;
+ zdoor_cookie_t *dte_cookie;
+ ztree_entry_t *dte_parent;
+} dtree_entry_t;
+
+#define ZTREE_SUCCESS 0
+#define ZTREE_ERROR -1
+#define ZTREE_ARGUMENT_ERROR -2
+#define ZTREE_ALREADY_EXISTS -3
+#define ZTREE_NOT_FOUND -4
+
+extern int ztree_zone_add(struct zdoor_handle *handle,
+ const char *zonename, ztree_door_visitor visitor);
+
+extern ztree_entry_t *ztree_zone_find(struct zdoor_handle *handle,
+ const char *zonename);
+
+extern void ztree_zone_remove(struct zdoor_handle *handle,
+ ztree_entry_t *entry);
+
+extern int ztree_door_add(struct zdoor_handle *handle, const char *zonename,
+ const char *service, zdoor_callback callback, zdoor_cookie_t *cookie);
+
+extern dtree_entry_t *ztree_door_find(struct zdoor_handle *handle,
+ const char *zonename, const char *service);
+
+extern zdoor_cookie_t *ztree_door_remove(struct zdoor_handle *handle,
+ dtree_entry_t *entry);
+
+extern void ztree_walk_doors(struct zdoor_handle *handle, const char *zonename);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZTREE_H */
diff --git a/usr/src/lib/libzdoor/i386/Makefile b/usr/src/lib/libzdoor/i386/Makefile
new file mode 100644
index 0000000000..2bfe8001d9
--- /dev/null
+++ b/usr/src/lib/libzdoor/i386/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 2011 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
index df89daf19a..9938b97dea 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -65,6 +65,7 @@ typedef enum zfs_error {
EZFS_PROPTYPE, /* property does not apply to dataset type */
EZFS_PROPNONINHERIT, /* property is not inheritable */
EZFS_PROPSPACE, /* bad quota or reservation */
+ EZFS_PROPCACHED, /* prop unavail since cachedprops flag set */
EZFS_BADTYPE, /* dataset is not of appropriate type */
EZFS_BUSY, /* pool or dataset is busy */
EZFS_EXISTS, /* pool or dataset already exists */
@@ -215,6 +216,7 @@ extern libzfs_handle_t *zpool_get_handle(zpool_handle_t *);
extern libzfs_handle_t *zfs_get_handle(zfs_handle_t *);
extern void libzfs_print_on_error(libzfs_handle_t *, boolean_t);
+extern void libzfs_set_cachedprops(libzfs_handle_t *, boolean_t);
extern void zfs_save_arguments(int argc, char **, char *, int);
extern int zpool_log_history(libzfs_handle_t *, const char *);
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index 49d4283d19..095a940493 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -321,6 +321,10 @@ get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
{
libzfs_handle_t *hdl = zhp->zfs_hdl;
+ if (hdl->libzfs_cachedprops &&
+ libzfs_cmd_set_cachedprops(hdl, zc) != 0)
+ return (-1);
+
(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) {
@@ -2291,6 +2295,11 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
case ZFS_PROP_NORMALIZE:
case ZFS_PROP_UTF8ONLY:
case ZFS_PROP_CASE:
+ if (zhp->zfs_hdl->libzfs_cachedprops) {
+ return (zfs_error(zhp->zfs_hdl, EZFS_PROPCACHED,
+ "property unavailable since not cached"));
+ }
+
if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
return (-1);
@@ -2602,6 +2611,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
const char *str;
const char *strval;
boolean_t received = zfs_is_recvd_props_mode(zhp);
+ boolean_t printerr;
/*
* Check to see if this property applies to our object
@@ -2612,6 +2622,16 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
if (received && zfs_prop_readonly(prop))
return (-1);
+ if (zhp->zfs_hdl->libzfs_cachedprops &&
+ zfs_prop_cacheable(prop)) {
+ printerr = zhp->zfs_hdl->libzfs_printerr;
+ libzfs_print_on_error(zhp->zfs_hdl, B_FALSE);
+ (void) zfs_error(zhp->zfs_hdl, EZFS_PROPCACHED,
+ "property unavailable since not cached");
+ libzfs_print_on_error(zhp->zfs_hdl, printerr);
+ return (-1);
+ }
+
if (src)
*src = ZPROP_SRC_NONE;
diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h
index 9facac8a93..bec66afc5a 100644
--- a/usr/src/lib/libzfs/common/libzfs_impl.h
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 Pawel Jakub Dawidek. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
* Copyright 2020 Joyent, Inc.
*/
@@ -90,6 +91,7 @@ struct libzfs_handle {
libzfs_fru_t **libzfs_fru_hash;
libzfs_fru_t *libzfs_fru_list;
char libzfs_chassis_id[256];
+ boolean_t libzfs_cachedprops;
boolean_t libzfs_prop_debug;
di_devlink_handle_t libzfs_devlink;
regex_t libzfs_urire;
@@ -209,6 +211,7 @@ int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
size_t *);
zfs_handle_t *make_dataset_handle_zc(libzfs_handle_t *, zfs_cmd_t *);
zfs_handle_t *make_dataset_simple_handle_zc(zfs_handle_t *, zfs_cmd_t *);
+int libzfs_cmd_set_cachedprops(libzfs_handle_t *, zfs_cmd_t *);
int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t,
nvlist_t *, char **, uint64_t *, const char *);
diff --git a/usr/src/lib/libzfs/common/libzfs_iter.c b/usr/src/lib/libzfs/common/libzfs_iter.c
index b90293fedd..9e160087b3 100644
--- a/usr/src/lib/libzfs/common/libzfs_iter.c
+++ b/usr/src/lib/libzfs/common/libzfs_iter.c
@@ -24,6 +24,7 @@
* Copyright (c) 2013, 2015 by Delphix. All rights reserved.
* Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved.
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#include <stdio.h>
@@ -112,6 +113,10 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
return (0);
+ if (zhp->zfs_hdl->libzfs_cachedprops &&
+ libzfs_cmd_set_cachedprops(zhp->zfs_hdl, &zc) != 0)
+ return (-1);
+
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
return (-1);
@@ -122,9 +127,8 @@ zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
* that the pool has since been removed.
*/
if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
- &zc)) == NULL) {
+ &zc)) == NULL)
continue;
- }
if ((ret = func(nzhp, data)) != 0) {
zcmd_free_nvlists(&zc);
@@ -150,10 +154,17 @@ zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
zhp->zfs_type == ZFS_TYPE_BOOKMARK)
return (0);
+
+ if (zhp->zfs_hdl->libzfs_cachedprops &&
+ libzfs_cmd_set_cachedprops(zhp->zfs_hdl, &zc) != 0)
+ return (-1);
+
zc.zc_simple = simple;
+
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
return (-1);
+
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
&zc)) == 0) {
diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c
index 6f072e4935..d7060981fe 100644
--- a/usr/src/lib/libzfs/common/libzfs_pool.c
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c
@@ -384,6 +384,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
default:
(void) snprintf(buf, len, "%llu", intval);
}
+
break;
case PROP_TYPE_INDEX:
@@ -1991,6 +1992,8 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
"corrupted, use '-m' to import the pool "
"anyway:\n"));
print_vdev_tree(hdl, NULL, missing, 2);
+ zpool_rewind_exclaim(hdl, newname ? origname :
+ thename, B_TRUE, nv);
(void) printf("\n");
}
(void) zpool_standard_error(hdl, error, desc);
@@ -2024,8 +2027,9 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
ret = -1;
else if (zhp != NULL)
zpool_close(zhp);
- if (policy.zlp_rewind &
- (ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) {
+ if ((policy.zlp_rewind &
+ (ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) ||
+ (flags & ZFS_IMPORT_MISSING_LOG)) {
zpool_rewind_exclaim(hdl, newname ? origname : thename,
((policy.zlp_rewind & ZPOOL_TRY_REWIND) != 0), nv);
}
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index 8b5ea59c0e..87ccf2492e 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -87,6 +87,9 @@ libzfs_error_description(libzfs_handle_t *hdl)
return (dgettext(TEXT_DOMAIN, "property cannot be inherited"));
case EZFS_PROPSPACE:
return (dgettext(TEXT_DOMAIN, "invalid quota or reservation"));
+ case EZFS_PROPCACHED:
+ return (dgettext(TEXT_DOMAIN, "property unavailable since "
+ "cachedprops flag set"));
case EZFS_BADTYPE:
return (dgettext(TEXT_DOMAIN, "operation not applicable to "
"datasets of this type"));
@@ -671,6 +674,42 @@ libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
hdl->libzfs_printerr = printerr;
}
+/*
+ * Set the value of the cachedprops flag. If the cachedprops flag is set,
+ * operations which get ZFS properties will only retrieve a property if the
+ * property is cached somewhere in memory.
+ *
+ * Consumers of libzfs should take care when setting this flag, as they will
+ * prevent themselves from listing the full set of ZFS properties.
+ *
+ * ZFS properties which always require disk I/O are ZPL properties (utf8only,
+ * normalization, etc.) and the volsize and volblocksize properties for volumes.
+ */
+void
+libzfs_set_cachedprops(libzfs_handle_t *hdl, boolean_t cachedprops)
+{
+ hdl->libzfs_cachedprops = cachedprops;
+}
+
+/*
+ * Adds a src nvlist to a zfs_cmd_t which specifies that only cached (i.e., will
+ * not require a disk access) properties should be retrieved.
+ */
+int
+libzfs_cmd_set_cachedprops(libzfs_handle_t *hdl, zfs_cmd_t *zc)
+{
+ nvlist_t *nvl;
+ int ret;
+
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
+ nvlist_add_boolean_value(nvl, "cachedpropsonly", B_TRUE) != 0)
+ return (no_memory(hdl));
+
+ ret = zcmd_write_src_nvlist(hdl, zc, nvl);
+ nvlist_free(nvl);
+ return (ret);
+}
+
libzfs_handle_t *
libzfs_init(void)
{
@@ -716,6 +755,7 @@ libzfs_init(void)
zpool_feature_init();
libzfs_mnttab_init(hdl);
+ hdl->libzfs_cachedprops = B_FALSE;
if (getenv("ZFS_PROP_DEBUG") != NULL) {
hdl->libzfs_prop_debug = B_TRUE;
}
diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers
index 4c2c6e67fe..b437322adf 100644
--- a/usr/src/lib/libzfs/common/mapfile-vers
+++ b/usr/src/lib/libzfs/common/mapfile-vers
@@ -21,6 +21,7 @@
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, Joyent, Inc. All rights reserved.
# Copyright (c) 2011, 2017 by Delphix. All rights reserved.
# Copyright 2016 Nexenta Systems, Inc.
# Copyright 2020 Joyent, Inc.
@@ -69,6 +70,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
libzfs_init;
libzfs_mnttab_cache;
libzfs_print_on_error;
+ libzfs_set_cachedprops;
spa_feature_table {
ASSERT = {
TYPE = OBJECT;
@@ -131,6 +133,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zfs_path_to_zhandle;
zfs_promote;
zfs_prop_align_right;
+ zfs_prop_cacheable;
zfs_prop_column_name;
zfs_prop_default_numeric;
zfs_prop_default_string;
diff --git a/usr/src/lib/libzonecfg/Makefile.com b/usr/src/lib/libzonecfg/Makefile.com
index d73f6cf6d6..ca1194bf8e 100644
--- a/usr/src/lib/libzonecfg/Makefile.com
+++ b/usr/src/lib/libzonecfg/Makefile.com
@@ -20,12 +20,15 @@
#
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2015 Joyent, Inc.
# Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
#
LIBRARY= libzonecfg.a
VERS= .1
-OBJECTS= libzonecfg.o getzoneent.o scratchops.o definit.o
+LIB_OBJS= libzonecfg.o getzoneent.o scratchops.o definit.o
+XML_OBJS= os_dtd.o
+OBJECTS= $(LIB_OBJS) $(XML_OBJS)
include ../../Makefile.lib
@@ -37,15 +40,27 @@ $(DYNLIB) := LDLIBS += -lxml2
NATIVE_LIBS += libxml2.so
SRCDIR = ../common
+
+XMLDIR = $(SRC)/lib/xml
+SRCS = \
+ $(LIB_OBJS:%.o=$(SRCDIR)/%.c) \
+ $(XML_OBJS:%.o=$(XMLDIR)/%.c) \
+
CPPFLAGS += -I$(ADJUNCT_PROTO)/usr/include/libxml2 -I$(SRCDIR) -D_REENTRANT
CPPFLAGS += -I$(SRC)/common/definit
CERRWARN += $(CNOWARN_UNINIT)
CERRWARN += -_gcc=-Wno-parentheses
+CPPFLAGS += -I$(XMLDIR)
+
.KEEP_STATE:
all: $(LIBS)
+pics/%.o: $(XMLDIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
pics/%.o: $(SRC)/common/definit/%.c
$(COMPILE.c) -o $@ $<
$(POST_PROCESS_O)
diff --git a/usr/src/lib/libzonecfg/common/getzoneent.c b/usr/src/lib/libzonecfg/common/getzoneent.c
index 8155f7272a..c9f1c12bcf 100644
--- a/usr/src/lib/libzonecfg/common/getzoneent.c
+++ b/usr/src/lib/libzonecfg/common/getzoneent.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
@@ -127,6 +128,8 @@ getzoneent_private(FILE *cookie)
/* skip comment lines */
continue;
}
+
+ /* zonename */
p = gettok(&cp);
if (*p == '\0' || strlen(p) >= ZONENAME_MAX) {
/*
@@ -136,6 +139,7 @@ getzoneent_private(FILE *cookie)
}
(void) strlcpy(ze->zone_name, p, ZONENAME_MAX);
+ /* state */
p = gettok(&cp);
if (*p == '\0') {
/* state field should not be empty */
@@ -152,6 +156,7 @@ getzoneent_private(FILE *cookie)
continue;
}
+ /* zonepath */
p = gettok(&cp);
if (strlen(p) >= MAXPATHLEN) {
/* very long paths are not allowed */
@@ -159,10 +164,35 @@ getzoneent_private(FILE *cookie)
}
(void) strlcpy(ze->zone_path, p, MAXPATHLEN);
+ /* uuid */
p = gettok(&cp);
if (uuid_parse(p, ze->zone_uuid) == -1)
uuid_clear(ze->zone_uuid);
+ /* brand [optional] */
+ p = gettok(&cp);
+ if (strlen(p) >= MAXNAMELEN) {
+ /* very long names are not allowed */
+ continue;
+ }
+ (void) strlcpy(ze->zone_brand, p, MAXNAMELEN);
+
+ /* IP type [optional] */
+ p = gettok(&cp);
+ if (strlen(p) >= MAXNAMELEN) {
+ /* very long names are not allowed */
+ continue;
+ }
+ ze->zone_iptype = ZS_SHARED;
+ if (*p == 'e') {
+ ze->zone_iptype = ZS_EXCLUSIVE;
+ }
+
+ /* debug ID [optional] */
+ p = gettok(&cp);
+ if (*p != '\0')
+ ze->zone_did = atoi(p);
+
break;
}
@@ -170,10 +200,32 @@ getzoneent_private(FILE *cookie)
}
static boolean_t
-get_index_path(char *path)
+path_common(char *path, size_t path_size, const char *stem)
{
- return (snprintf(path, MAXPATHLEN, "%s%s", zonecfg_root,
- ZONE_INDEX_FILE) < MAXPATHLEN);
+ const char *native_root = zone_get_nroot();
+
+ if (native_root == NULL || zonecfg_in_alt_root()) {
+ /*
+ * Do not prepend the native system root (e.g. "/native") if an
+ * alternative configuration root has been selected.
+ */
+ native_root = "";
+ }
+
+ return (snprintf(path, path_size, "%s%s%s", native_root, zonecfg_root,
+ stem) < path_size);
+}
+
+static boolean_t
+get_index_path(char *path, size_t path_size)
+{
+ return (path_common(path, path_size, ZONE_INDEX_FILE));
+}
+
+static boolean_t
+get_temp_path(char *path, size_t path_size)
+{
+ return (path_common(path, path_size, _PATH_TMPFILE));
}
FILE *
@@ -181,7 +233,7 @@ setzoneent(void)
{
char path[MAXPATHLEN];
- if (!get_index_path(path)) {
+ if (!get_index_path(path, sizeof (path))) {
errno = EINVAL;
return (NULL);
}
@@ -202,8 +254,7 @@ lock_index_file(void)
struct flock lock;
char path[MAXPATHLEN];
- if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
- ZONE_INDEX_LOCK_DIR) >= sizeof (path))
+ if (!path_common(path, sizeof (path), ZONE_INDEX_LOCK_DIR))
return (-1);
if ((mkdir(path, S_IRWXU) == -1) && errno != EEXIST)
return (-1);
@@ -269,14 +320,17 @@ int
putzoneent(struct zoneent *ze, zoneent_op_t operation)
{
FILE *index_file, *tmp_file;
- char *tmp_file_name, buf[MAX_INDEX_LEN];
+ char buf[MAX_INDEX_LEN];
int tmp_file_desc, lock_fd, err;
boolean_t exist, need_quotes;
- char *cp;
+ char *cp, *tmpp;
+ char tmp_path[MAXPATHLEN];
char path[MAXPATHLEN];
char uuidstr[UUID_PRINTABLE_STRING_LENGTH];
- size_t tlen, namelen;
- const char *zone_name, *zone_state, *zone_path, *zone_uuid;
+ size_t namelen;
+ const char *zone_name, *zone_state, *zone_path, *zone_uuid,
+ *zone_brand = "", *zone_iptype;
+ zoneid_t zone_did;
assert(ze != NULL);
@@ -299,20 +353,14 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation)
if ((lock_fd = lock_index_file()) == -1)
return (Z_LOCKING_FILE);
- /* using sizeof gives us room for the terminating NUL byte as well */
- tlen = sizeof (_PATH_TMPFILE) + strlen(zonecfg_root);
- tmp_file_name = malloc(tlen);
- if (tmp_file_name == NULL) {
+ if (!get_temp_path(tmp_path, sizeof (tmp_path))) {
(void) unlock_index_file(lock_fd);
return (Z_NOMEM);
}
- (void) snprintf(tmp_file_name, tlen, "%s%s", zonecfg_root,
- _PATH_TMPFILE);
- tmp_file_desc = mkstemp(tmp_file_name);
+ tmp_file_desc = mkstemp(tmp_path);
if (tmp_file_desc == -1) {
- (void) unlink(tmp_file_name);
- free(tmp_file_name);
+ (void) unlink(tmp_path);
(void) unlock_index_file(lock_fd);
return (Z_TEMP_FILE);
}
@@ -323,7 +371,7 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation)
err = Z_MISC_FS;
goto error;
}
- if (!get_index_path(path)) {
+ if (!get_index_path(path, sizeof (path))) {
err = Z_MISC_FS;
goto error;
}
@@ -335,6 +383,9 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation)
exist = B_FALSE;
zone_name = ze->zone_name;
namelen = strlen(zone_name);
+ zone_brand = ze->zone_brand;
+ zone_iptype = (ze->zone_iptype == ZS_SHARED ? "sh" : "ex");
+ zone_did = ze->zone_did;
for (;;) {
if (fgets(buf, sizeof (buf), index_file) == NULL) {
if (operation == PZE_ADD && !exist) {
@@ -389,6 +440,11 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation)
}
zone_path = gettok(&cp);
zone_uuid = gettok(&cp);
+ zone_brand = gettok(&cp);
+ zone_iptype = gettok(&cp);
+ tmpp = gettok(&cp);
+ if (*tmpp != '\0')
+ zone_did = atoi(tmpp);
switch (operation) {
case PZE_ADD:
@@ -403,14 +459,6 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation)
*/
if (ze->zone_state >= 0) {
zone_state = zone_state_str(ze->zone_state);
-
- /*
- * If the caller is uninstalling this zone,
- * then wipe out the uuid. The zone's contents
- * are no longer known.
- */
- if (ze->zone_state < ZONE_STATE_INSTALLED)
- zone_uuid = "";
}
/* If a new name is supplied, use it. */
@@ -419,6 +467,27 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation)
if (ze->zone_path[0] != '\0')
zone_path = ze->zone_path;
+
+ /* If new UUID provided, replace it */
+ if (!uuid_is_null(ze->zone_uuid)) {
+ uuid_unparse(ze->zone_uuid, uuidstr);
+ zone_uuid = uuidstr;
+ }
+
+ /* If a brand is supplied, use it. */
+ if (ze->zone_brand[0] != '\0') {
+ zone_brand = ze->zone_brand;
+
+ /*
+ * Since the brand, iptype and did are optional,
+ * we we only reset the iptype and did if the
+ * brand is provided.
+ */
+ zone_iptype = (ze->zone_iptype == ZS_SHARED ?
+ "sh" : "ex");
+ zone_did = ze->zone_did;
+ }
+
break;
case PZE_REMOVE:
@@ -450,9 +519,17 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation)
* method for escaping them.
*/
need_quotes = (strchr(zone_path, ':') != NULL);
- (void) fprintf(tmp_file, "%s:%s:%s%s%s:%s\n", zone_name,
- zone_state, need_quotes ? "\"" : "", zone_path,
- need_quotes ? "\"" : "", zone_uuid);
+
+ if (*zone_brand != '\0') {
+ (void) fprintf(tmp_file, "%s:%s:%s%s%s:%s:%s:%s:%d\n",
+ zone_name, zone_state, need_quotes ? "\"" : "",
+ zone_path, need_quotes ? "\"" : "", zone_uuid,
+ zone_brand, zone_iptype, zone_did);
+ } else {
+ (void) fprintf(tmp_file, "%s:%s:%s%s%s:%s\n", zone_name,
+ zone_state, need_quotes ? "\"" : "", zone_path,
+ need_quotes ? "\"" : "", zone_uuid);
+ }
exist = B_TRUE;
}
@@ -464,11 +541,10 @@ putzoneent(struct zoneent *ze, zoneent_op_t operation)
goto error;
}
tmp_file = NULL;
- if (rename(tmp_file_name, path) == -1) {
+ if (rename(tmp_path, path) == -1) {
err = errno == EACCES ? Z_ACCES : Z_MISC_FS;
goto error;
}
- free(tmp_file_name);
if (unlock_index_file(lock_fd) != Z_OK)
return (Z_UNLOCKING_FILE);
return (Z_OK);
@@ -478,8 +554,7 @@ error:
(void) fclose(index_file);
if (tmp_file != NULL)
(void) fclose(tmp_file);
- (void) unlink(tmp_file_name);
- free(tmp_file_name);
+ (void) unlink(tmp_path);
(void) unlock_index_file(lock_fd);
return (err);
}
diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c
index c1500aa184..2ba7196967 100644
--- a/usr/src/lib/libzonecfg/common/libzonecfg.c
+++ b/usr/src/lib/libzonecfg/common/libzonecfg.c
@@ -22,7 +22,7 @@
/*
* Copyright 2014 Gary Mills
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
*/
@@ -62,6 +62,8 @@
#include <user_attr.h>
#include <prof_attr.h>
#include <definit.h>
+#include <sys/debug.h>
+#include <os_dtd.h>
#include <arpa/inet.h>
#include <netdb.h>
@@ -82,6 +84,8 @@
#define ZONE_EVENT_PING_SUBCLASS "ping"
#define ZONE_EVENT_PING_PUBLISHER "solaris"
+#define DEBUGID_FILE "/etc/zones/did.txt"
+
/* Hard-code the DTD element/attribute/entity names just once, here. */
#define DTD_ELEM_ATTR (const xmlChar *) "attr"
#define DTD_ELEM_COMMENT (const xmlChar *) "comment"
@@ -89,6 +93,7 @@
#define DTD_ELEM_FS (const xmlChar *) "filesystem"
#define DTD_ELEM_FSOPTION (const xmlChar *) "fsoption"
#define DTD_ELEM_NET (const xmlChar *) "network"
+#define DTD_ELEM_NETATTR (const xmlChar *) "net-attr"
#define DTD_ELEM_RCTL (const xmlChar *) "rctl"
#define DTD_ELEM_RCTLVALUE (const xmlChar *) "rctl-value"
#define DTD_ELEM_ZONE (const xmlChar *) "zone"
@@ -109,10 +114,12 @@
#define DTD_ATTR_IPTYPE (const xmlChar *) "ip-type"
#define DTD_ATTR_DEFROUTER (const xmlChar *) "defrouter"
#define DTD_ATTR_DIR (const xmlChar *) "directory"
+#define DTD_ATTR_GNIC (const xmlChar *) "global-nic"
#define DTD_ATTR_LIMIT (const xmlChar *) "limit"
#define DTD_ATTR_LIMITPRIV (const xmlChar *) "limitpriv"
#define DTD_ATTR_BOOTARGS (const xmlChar *) "bootargs"
#define DTD_ATTR_SCHED (const xmlChar *) "scheduling-class"
+#define DTD_ATTR_MAC (const xmlChar *) "mac-addr"
#define DTD_ATTR_MATCH (const xmlChar *) "match"
#define DTD_ATTR_NAME (const xmlChar *) "name"
#define DTD_ATTR_PHYSICAL (const xmlChar *) "physical"
@@ -122,6 +129,7 @@
#define DTD_ATTR_SPECIAL (const xmlChar *) "special"
#define DTD_ATTR_TYPE (const xmlChar *) "type"
#define DTD_ATTR_VALUE (const xmlChar *) "value"
+#define DTD_ATTR_VLANID (const xmlChar *) "vlan-id"
#define DTD_ATTR_ZONEPATH (const xmlChar *) "zonepath"
#define DTD_ATTR_NCPU_MIN (const xmlChar *) "ncpu_min"
#define DTD_ATTR_NCPU_MAX (const xmlChar *) "ncpu_max"
@@ -134,6 +142,7 @@
#define DTD_ATTR_MODE (const xmlChar *) "mode"
#define DTD_ATTR_ACL (const xmlChar *) "acl"
#define DTD_ATTR_BRAND (const xmlChar *) "brand"
+#define DTD_ATTR_DID (const xmlChar *) "debugid"
#define DTD_ATTR_HOSTID (const xmlChar *) "hostid"
#define DTD_ATTR_USER (const xmlChar *) "user"
#define DTD_ATTR_AUTHS (const xmlChar *) "auths"
@@ -184,9 +193,12 @@ static struct alias {
{ALIAS_MAXSEMIDS, "zone.max-sem-ids", "privileged", "deny", 0},
{ALIAS_MAXLOCKEDMEM, "zone.max-locked-memory", "privileged", "deny", 0},
{ALIAS_MAXSWAP, "zone.max-swap", "privileged", "deny", 0},
+ {ALIAS_MAXPHYSMEM, "zone.max-physical-memory", "privileged", "deny",
+ 1048576},
{ALIAS_SHARES, "zone.cpu-shares", "privileged", "none", 0},
{ALIAS_CPUCAP, "zone.cpu-cap", "privileged", "deny", 0},
{ALIAS_MAXPROCS, "zone.max-processes", "privileged", "deny", 100},
+ {ALIAS_ZFSPRI, "zone.zfs-io-priority", "privileged", "none", 0},
{NULL, NULL, NULL, NULL, 0}
};
@@ -234,6 +246,8 @@ static int zone_lock_cnt = 0;
static char zoneadm_lock_held[] = LOCK_ENV_VAR"=1";
static char zoneadm_lock_not_held[] = LOCK_ENV_VAR"=0";
+static void zonecfg_notify_delete(const char *);
+
char *zonecfg_root = "";
/*
@@ -277,17 +291,35 @@ zonecfg_in_alt_root(void)
*/
static boolean_t
-config_file_path(const char *zonename, char *answer)
+file_path_common(const char *zonename, const char *subdir, const char *stem,
+ char *answer, size_t answer_size)
{
- return (snprintf(answer, MAXPATHLEN, "%s%s/%s.xml", zonecfg_root,
- ZONE_CONFIG_ROOT, zonename) < MAXPATHLEN);
+ const char *native_root = zone_get_nroot();
+
+ if (native_root == NULL || zonecfg_in_alt_root()) {
+ /*
+ * Do not prepend the native system root (e.g. "/native") if an
+ * alternative configuration root has been selected.
+ */
+ native_root = "";
+ }
+
+ return (snprintf(answer, answer_size, "%s%s%s/%s.%s", native_root,
+ zonecfg_root, subdir, zonename, stem) < answer_size);
}
static boolean_t
-snap_file_path(const char *zonename, char *answer)
+config_file_path(const char *zonename, char *answer, size_t answer_size)
{
- return (snprintf(answer, MAXPATHLEN, "%s%s/%s.snapshot.xml",
- zonecfg_root, ZONE_SNAPSHOT_ROOT, zonename) < MAXPATHLEN);
+ return (file_path_common(zonename, ZONE_CONFIG_ROOT, "xml", answer,
+ answer_size));
+}
+
+static boolean_t
+snap_file_path(const char *zonename, char *answer, size_t answer_size)
+{
+ return (file_path_common(zonename, ZONE_SNAPSHOT_ROOT, "snapshot.xml",
+ answer, answer_size));
}
/*ARGSUSED*/
@@ -358,7 +390,7 @@ zonecfg_destroy(const char *zonename, boolean_t force)
int err, state_err;
zone_state_t state;
- if (!config_file_path(zonename, path))
+ if (!config_file_path(zonename, path, sizeof (path)))
return (Z_MISC_FS);
state_err = zone_get_state((char *)zonename, &state);
@@ -405,8 +437,10 @@ zonecfg_destroy(const char *zonename, boolean_t force)
* Treat failure to find the XML file silently, since, well, it's
* gone, and with the index file cleaned up, we're done.
*/
- if (err == Z_OK || err == Z_NO_ZONE)
+ if (err == Z_OK || err == Z_NO_ZONE) {
+ zonecfg_notify_delete(zonename);
return (Z_OK);
+ }
return (err);
}
@@ -415,7 +449,7 @@ zonecfg_destroy_snapshot(const char *zonename)
{
char path[MAXPATHLEN];
- if (!snap_file_path(zonename, path))
+ if (!snap_file_path(zonename, path, sizeof (path)))
return (Z_MISC_FS);
return (zonecfg_destroy_impl(path));
}
@@ -582,9 +616,8 @@ static int
zonecfg_get_handle_impl(const char *zonename, const char *filename,
zone_dochandle_t handle)
{
- xmlValidCtxtPtr cvp;
struct stat statbuf;
- int valid;
+ boolean_t valid;
if (zonename == NULL)
return (Z_NO_ZONE);
@@ -595,14 +628,13 @@ zonecfg_get_handle_impl(const char *zonename, const char *filename,
return (Z_INVALID_DOCUMENT);
return (Z_NO_ZONE);
}
- if ((cvp = xmlNewValidCtxt()) == NULL)
+
+ if (os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid) != 0) {
return (Z_NOMEM);
- cvp->error = zonecfg_error_func;
- cvp->warning = zonecfg_error_func;
- valid = xmlValidateDocument(cvp, handle->zone_dh_doc);
- xmlFreeValidCtxt(cvp);
- if (valid == 0)
+ }
+ if (!valid) {
return (Z_INVALID_DOCUMENT);
+ }
/* delete any comments such as inherited Sun copyright / ident str */
stripcomments(handle);
@@ -614,7 +646,7 @@ zonecfg_get_handle(const char *zonename, zone_dochandle_t handle)
{
char path[MAXPATHLEN];
- if (!config_file_path(zonename, path))
+ if (!config_file_path(zonename, path, sizeof (path)))
return (Z_MISC_FS);
handle->zone_dh_newzone = B_FALSE;
@@ -658,7 +690,7 @@ zonecfg_get_snapshot_handle(const char *zonename, zone_dochandle_t handle)
{
char path[MAXPATHLEN];
- if (!snap_file_path(zonename, path))
+ if (!snap_file_path(zonename, path, sizeof (path)))
return (Z_MISC_FS);
handle->zone_dh_newzone = B_FALSE;
return (zonecfg_get_handle_impl(zonename, path, handle));
@@ -671,7 +703,7 @@ zonecfg_get_template_handle(const char *template, const char *zonename,
char path[MAXPATHLEN];
int err;
- if (!config_file_path(template, path))
+ if (!config_file_path(template, path, sizeof (path)))
return (Z_MISC_FS);
if ((err = zonecfg_get_handle_impl(template, path, handle)) != Z_OK)
@@ -705,21 +737,19 @@ int
zonecfg_attach_manifest(int fd, zone_dochandle_t local_handle,
zone_dochandle_t rem_handle)
{
- xmlValidCtxtPtr cvp;
- int valid;
+ boolean_t valid;
/* load the manifest into the handle for the remote system */
if ((rem_handle->zone_dh_doc = xmlReadFd(fd, NULL, NULL, 0)) == NULL) {
return (Z_INVALID_DOCUMENT);
}
- if ((cvp = xmlNewValidCtxt()) == NULL)
+
+ if (os_dtd_validate(rem_handle->zone_dh_doc, B_FALSE, &valid) != 0) {
return (Z_NOMEM);
- cvp->error = zonecfg_error_func;
- cvp->warning = zonecfg_error_func;
- valid = xmlValidateDocument(cvp, rem_handle->zone_dh_doc);
- xmlFreeValidCtxt(cvp);
- if (valid == 0)
+ }
+ if (!valid) {
return (Z_INVALID_DOCUMENT);
+ }
/* delete any comments such as inherited Sun copyright / ident str */
stripcomments(rem_handle);
@@ -740,14 +770,12 @@ zonecfg_attach_manifest(int fd, zone_dochandle_t local_handle,
* We need to re-run xmlValidateDocument on local_handle to properly
* update the in-core representation of the configuration.
*/
- if ((cvp = xmlNewValidCtxt()) == NULL)
+ if (os_dtd_validate(local_handle->zone_dh_doc, B_FALSE, &valid) != 0) {
return (Z_NOMEM);
- cvp->error = zonecfg_error_func;
- cvp->warning = zonecfg_error_func;
- valid = xmlValidateDocument(cvp, local_handle->zone_dh_doc);
- xmlFreeValidCtxt(cvp);
- if (valid == 0)
+ }
+ if (!valid) {
return (Z_INVALID_DOCUMENT);
+ }
strip_sw_inv(local_handle);
@@ -1109,7 +1137,7 @@ zonecfg_set_sched(zone_dochandle_t handle, char *sched)
* In general, the operation of this function should succeed or fail as
* a unit.
*/
-int
+static int
zonecfg_refresh_index_file(zone_dochandle_t handle)
{
char name[ZONENAME_MAX], zonepath[MAXPATHLEN];
@@ -1131,6 +1159,15 @@ zonecfg_refresh_index_file(zone_dochandle_t handle)
(void) strlcpy(ze.zone_path, zonepath + strlen(zonecfg_root),
sizeof (ze.zone_path));
+ if ((err = zonecfg_get_brand(handle, ze.zone_brand,
+ sizeof (ze.zone_brand))) != 0)
+ return (err);
+
+ if ((err = zonecfg_get_iptype(handle, &ze.zone_iptype)) != Z_OK)
+ return (err);
+
+ ze.zone_did = zonecfg_get_did(handle);
+
if (is_renaming(handle)) {
opcode = PZE_MODIFY;
(void) strlcpy(ze.zone_name, handle->zone_dh_delete_name,
@@ -1201,9 +1238,9 @@ zonecfg_save_impl(zone_dochandle_t handle, char *filename)
{
char tmpfile[MAXPATHLEN];
char bakdir[MAXPATHLEN], bakbase[MAXPATHLEN], bakfile[MAXPATHLEN];
- int tmpfd, err, valid;
- xmlValidCtxt cvp = { NULL };
+ int tmpfd, err;
boolean_t backup;
+ boolean_t valid;
(void) strlcpy(tmpfile, filename, sizeof (tmpfile));
(void) dirname(tmpfile);
@@ -1216,16 +1253,13 @@ zonecfg_save_impl(zone_dochandle_t handle, char *filename)
}
(void) close(tmpfd);
- cvp.error = zonecfg_error_func;
- cvp.warning = zonecfg_error_func;
-
/*
* We do a final validation of the document. Since the library has
* malfunctioned if it fails to validate, we follow-up with an
* assert() that the doc is valid.
*/
- valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
- assert(valid != 0);
+ VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid));
+ VERIFY(valid == B_TRUE);
if (xmlSaveFormatFile(tmpfile, handle->zone_dh_doc, 1) <= 0)
goto err;
@@ -1280,7 +1314,6 @@ zonecfg_save_impl(zone_dochandle_t handle, char *filename)
/*
* Try to restore from our backup.
*/
- (void) unlink(filename);
(void) rename(bakfile, filename);
} else {
/*
@@ -1323,7 +1356,7 @@ zonecfg_save(zone_dochandle_t handle)
if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK)
return (err);
- if (!config_file_path(zname, path))
+ if (!config_file_path(zname, path, sizeof (path)))
return (Z_MISC_FS);
addcomment(handle, "\n DO NOT EDIT THIS "
@@ -1344,8 +1377,10 @@ zonecfg_save(zone_dochandle_t handle)
handle->zone_dh_newzone = B_FALSE;
if (is_renaming(handle)) {
- if (config_file_path(handle->zone_dh_delete_name, delpath))
+ if (config_file_path(handle->zone_dh_delete_name, delpath,
+ sizeof (delpath))) {
(void) unlink(delpath);
+ }
handle->zone_dh_delete_name[0] = '\0';
}
@@ -1355,23 +1390,18 @@ zonecfg_save(zone_dochandle_t handle)
int
zonecfg_verify_save(zone_dochandle_t handle, char *filename)
{
- int valid;
-
- xmlValidCtxt cvp = { NULL };
+ boolean_t valid;
if (zonecfg_check_handle(handle) != Z_OK)
return (Z_BAD_HANDLE);
- cvp.error = zonecfg_error_func;
- cvp.warning = zonecfg_error_func;
-
/*
* We do a final validation of the document. Since the library has
* malfunctioned if it fails to validate, we follow-up with an
* assert() that the doc is valid.
*/
- valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
- assert(valid != 0);
+ VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid));
+ VERIFY(valid == B_TRUE);
if (xmlSaveFormatFile(filename, handle->zone_dh_doc, 1) <= 0)
return (Z_SAVING_FILE);
@@ -1385,9 +1415,8 @@ zonecfg_detach_save(zone_dochandle_t handle, uint_t flags)
char zname[ZONENAME_MAX];
char path[MAXPATHLEN];
char migpath[MAXPATHLEN];
- xmlValidCtxt cvp = { NULL };
int err = Z_SAVING_FILE;
- int valid;
+ boolean_t valid;
if (zonecfg_check_handle(handle) != Z_OK)
return (Z_BAD_HANDLE);
@@ -1414,16 +1443,13 @@ zonecfg_detach_save(zone_dochandle_t handle, uint_t flags)
addcomment(handle, "\n DO NOT EDIT THIS FILE. "
"Use zonecfg(8) and zoneadm(8) attach.\n");
- cvp.error = zonecfg_error_func;
- cvp.warning = zonecfg_error_func;
-
/*
* We do a final validation of the document. Since the library has
* malfunctioned if it fails to validate, we follow-up with an
* assert() that the doc is valid.
*/
- valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
- assert(valid != 0);
+ VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid));
+ VERIFY(valid == B_TRUE);
if (xmlSaveFormatFile(migpath, handle->zone_dh_doc, 1) <= 0)
return (Z_SAVING_FILE);
@@ -1484,6 +1510,56 @@ zonecfg_rm_detached(zone_dochandle_t handle, boolean_t forced)
}
}
+static void
+zonecfg_notify_conf_change(const char *zname, char *os, char *ns)
+{
+ evchan_t *ze_chan;
+ struct timeval now;
+ uint64_t t;
+ nvlist_t *nvl = NULL;
+
+ if (sysevent_evc_bind(ZONE_EVENT_CHANNEL, &ze_chan, 0) != 0)
+ return;
+
+ /* Current time since Jan 1 1970 but consumers expect NS */
+ (void) gettimeofday(&now, NULL);
+ t = (now.tv_sec * NANOSEC) + (now.tv_usec * 1000);
+
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0 &&
+ nvlist_add_string(nvl, ZONE_CB_NAME, zname) == 0 &&
+ nvlist_add_string(nvl, ZONE_CB_NEWSTATE, ns) == 0 &&
+ nvlist_add_string(nvl, ZONE_CB_OLDSTATE, os) == 0 &&
+ nvlist_add_int32(nvl, ZONE_CB_ZONEID, -1) == 0 &&
+ nvlist_add_uint64(nvl, ZONE_CB_TIMESTAMP, t) == 0) {
+ (void) sysevent_evc_publish(ze_chan, ZONE_EVENT_STATUS_CLASS,
+ ZONE_EVENT_STATUS_SUBCLASS, "sun.com", "zonecfg", nvl,
+ EVCH_SLEEP);
+ }
+
+ nvlist_free(nvl);
+ (void) sysevent_evc_unbind(ze_chan);
+}
+
+void
+zonecfg_notify_create(zone_dochandle_t handle)
+{
+ char zname[ZONENAME_MAX];
+
+ if (zonecfg_check_handle(handle) != Z_OK)
+ return;
+
+ if (zonecfg_get_name(handle, zname, sizeof (zname)) != Z_OK)
+ return;
+
+ zonecfg_notify_conf_change(zname, "", ZONE_STATE_STR_CONFIGURED);
+}
+
+static void
+zonecfg_notify_delete(const char *zname)
+{
+ zonecfg_notify_conf_change(zname, ZONE_STATE_STR_CONFIGURED, "");
+}
+
/*
* Special case: if access(2) fails with ENOENT, then try again using
* ZONE_CONFIG_ROOT instead of config_file_path(zonename). This is how we
@@ -1496,7 +1572,7 @@ zonecfg_access(const char *zonename, int amode)
{
char path[MAXPATHLEN];
- if (!config_file_path(zonename, path))
+ if (!config_file_path(zonename, path, sizeof (path)))
return (Z_INVAL);
if (access(path, amode) == 0)
return (Z_OK);
@@ -1561,7 +1637,7 @@ zonecfg_create_snapshot(const char *zonename)
goto out;
}
- if (!snap_file_path(zonename, path)) {
+ if (!snap_file_path(zonename, path, sizeof (path))) {
error = Z_MISC_FS;
goto out;
}
@@ -2074,6 +2150,32 @@ zonecfg_ifname_exists(sa_family_t af, char *ifname)
}
/*
+ * 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 != '\0' || n > 0xff)
+ return;
+ (void) snprintf(tmp, sizeof (tmp), "%s%02x", sep, n);
+ (void) strlcat(dst, tmp, len);
+
+ sep = ":";
+ p = strtok(NULL, ":");
+ }
+}
+
+/*
* Determines whether there is a net resource with the physical interface, IP
* address, and default router specified by 'tabptr' in the zone configuration
* to which 'handle' refers. 'tabptr' must have an interface, an address, a
@@ -2092,13 +2194,18 @@ zonecfg_ifname_exists(sa_family_t af, char *ifname)
int
zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
{
- xmlNodePtr cur;
+ xmlNodePtr cur, val;
xmlNodePtr firstmatch;
int err;
char address[INET6_ADDRSTRLEN];
char physical[LIFNAMSIZ];
+ char mac[MAXMACADDRLEN];
+ char norm_mac[MAXMACADDRLEN];
+ char gnic[LIFNAMSIZ];
size_t addrspec; /* nonzero if tabptr has IP addr */
size_t physspec; /* nonzero if tabptr has interface */
+ size_t macspec; /* nonzero if tabptr has mac addr */
+ size_t gnicspec; /* nonzero if tabptr has gnic */
size_t defrouterspec; /* nonzero if tabptr has def. router */
size_t allowed_addrspec;
zone_iptype_t iptype;
@@ -2110,17 +2217,20 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
* Determine the fields that will be searched. There must be at least
* one.
*
- * zone_nwif_address, zone_nwif_physical, and zone_nwif_defrouter are
+ * zone_nwif_address, zone_nwif_physical, zone_nwif_defrouter,
+ * zone_nwif_mac, zone_nwif_vlan_id and zone_nwif_gnic are
* arrays, so no NULL checks are necessary.
*/
addrspec = strlen(tabptr->zone_nwif_address);
physspec = strlen(tabptr->zone_nwif_physical);
+ macspec = strlen(tabptr->zone_nwif_mac);
+ gnicspec = strlen(tabptr->zone_nwif_gnic);
defrouterspec = strlen(tabptr->zone_nwif_defrouter);
allowed_addrspec = strlen(tabptr->zone_nwif_allowed_address);
if (addrspec != 0 && allowed_addrspec != 0)
return (Z_INVAL); /* can't specify both */
if (addrspec == 0 && physspec == 0 && defrouterspec == 0 &&
- allowed_addrspec == 0)
+ allowed_addrspec == 0 && macspec == 0 && gnicspec == 0)
return (Z_INSUFFICIENT_SPEC);
if ((err = operation_prep(handle)) != Z_OK)
@@ -2147,6 +2257,19 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
physical, sizeof (physical)) != Z_OK ||
strcmp(tabptr->zone_nwif_physical, physical) != 0))
continue;
+ if (iptype == ZS_EXCLUSIVE && macspec != 0) {
+ if (fetchprop(cur, DTD_ATTR_MAC, mac, sizeof (mac)) !=
+ Z_OK)
+ continue;
+ normalize_mac_addr(norm_mac, mac, sizeof (norm_mac));
+ if (strcmp(tabptr->zone_nwif_mac, norm_mac) != 0)
+ continue;
+ }
+ if (iptype == ZS_EXCLUSIVE && gnicspec != 0 &&
+ (fetchprop(cur, DTD_ATTR_GNIC, gnic,
+ sizeof (gnic)) != Z_OK ||
+ strcmp(tabptr->zone_nwif_gnic, gnic) != 0))
+ continue;
if (iptype == ZS_SHARED && addrspec != 0 &&
(fetchprop(cur, DTD_ATTR_ADDRESS, address,
sizeof (address)) != Z_OK ||
@@ -2189,6 +2312,21 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
return (err);
if (iptype == ZS_EXCLUSIVE &&
+ (err = fetchprop(cur, DTD_ATTR_MAC, tabptr->zone_nwif_mac,
+ sizeof (tabptr->zone_nwif_mac))) != Z_OK)
+ return (err);
+
+ if (iptype == ZS_EXCLUSIVE &&
+ (err = fetchprop(cur, DTD_ATTR_VLANID, tabptr->zone_nwif_vlan_id,
+ sizeof (tabptr->zone_nwif_vlan_id))) != Z_OK)
+ return (err);
+
+ if (iptype == ZS_EXCLUSIVE &&
+ (err = fetchprop(cur, DTD_ATTR_GNIC, tabptr->zone_nwif_gnic,
+ sizeof (tabptr->zone_nwif_gnic))) != Z_OK)
+ return (err);
+
+ if (iptype == ZS_EXCLUSIVE &&
(err = fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS,
tabptr->zone_nwif_allowed_address,
sizeof (tabptr->zone_nwif_allowed_address))) != Z_OK)
@@ -2199,13 +2337,40 @@ zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
sizeof (tabptr->zone_nwif_defrouter))) != Z_OK)
return (err);
+ tabptr->zone_nwif_attrp = NULL;
+ for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+ struct zone_res_attrtab *valptr;
+
+ valptr = (struct zone_res_attrtab *)malloc(
+ sizeof (struct zone_res_attrtab));
+ if (valptr == NULL)
+ return (Z_NOMEM);
+
+ valptr->zone_res_attr_name[0] =
+ valptr->zone_res_attr_value[0] = '\0';
+ if (zonecfg_add_res_attr(&(tabptr->zone_nwif_attrp), valptr)
+ != Z_OK) {
+ free(valptr);
+ break;
+ }
+
+ if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+ sizeof (valptr->zone_res_attr_name)) != Z_OK))
+ break;
+ if ((fetchprop(val, DTD_ATTR_VALUE,
+ valptr->zone_res_attr_value,
+ sizeof (valptr->zone_res_attr_value)) != Z_OK))
+ break;
+ }
+
return (Z_OK);
}
static int
zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
{
- xmlNodePtr newnode, cur = handle->zone_dh_cur;
+ xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
+ struct zone_res_attrtab *valptr;
int err;
newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL);
@@ -2221,13 +2386,40 @@ zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
tabptr->zone_nwif_physical)) != Z_OK)
return (err);
/*
- * Do not add this property when it is not set, for backwards
- * compatibility and because it is optional.
+ * Do not add these properties when they are not set, for backwards
+ * compatibility and because they are optional.
*/
if ((strlen(tabptr->zone_nwif_defrouter) > 0) &&
((err = newprop(newnode, DTD_ATTR_DEFROUTER,
tabptr->zone_nwif_defrouter)) != Z_OK))
return (err);
+ if (strlen(tabptr->zone_nwif_mac) > 0 &&
+ (err = newprop(newnode, DTD_ATTR_MAC,
+ tabptr->zone_nwif_mac)) != Z_OK)
+ return (err);
+ if (strlen(tabptr->zone_nwif_vlan_id) > 0 &&
+ (err = newprop(newnode, DTD_ATTR_VLANID,
+ tabptr->zone_nwif_vlan_id)) != Z_OK)
+ return (err);
+ if (strlen(tabptr->zone_nwif_gnic) > 0 &&
+ (err = newprop(newnode, DTD_ATTR_GNIC,
+ tabptr->zone_nwif_gnic)) != Z_OK)
+ return (err);
+
+ for (valptr = tabptr->zone_nwif_attrp; valptr != NULL;
+ valptr = valptr->zone_res_attr_next) {
+ valnode = xmlNewTextChild(newnode, NULL, DTD_ELEM_NETATTR,
+ NULL);
+ err = newprop(valnode, DTD_ATTR_NAME,
+ valptr->zone_res_attr_name);
+ if (err != Z_OK)
+ return (err);
+ err = newprop(valnode, DTD_ATTR_VALUE,
+ valptr->zone_res_attr_value);
+ if (err != Z_OK)
+ return (err);
+ }
+
return (Z_OK);
}
@@ -2252,7 +2444,8 @@ static int
zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
{
xmlNodePtr cur = handle->zone_dh_cur;
- boolean_t addr_match, phys_match, allowed_addr_match;
+ boolean_t addr_match, phys_match, allowed_addr_match, mac_match,
+ gnic_match;
for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
if (xmlStrcmp(cur->name, DTD_ELEM_NET))
@@ -2264,8 +2457,13 @@ zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
tabptr->zone_nwif_allowed_address);
phys_match = match_prop(cur, DTD_ATTR_PHYSICAL,
tabptr->zone_nwif_physical);
+ mac_match = match_prop(cur, DTD_ATTR_MAC,
+ tabptr->zone_nwif_mac);
+ gnic_match = match_prop(cur, DTD_ATTR_GNIC,
+ tabptr->zone_nwif_gnic);
- if (addr_match && allowed_addr_match && phys_match) {
+ if (((addr_match && allowed_addr_match) || mac_match ||
+ gnic_match) && phys_match) {
xmlUnlinkNode(cur);
xmlFreeNode(cur);
return (Z_OK);
@@ -2314,6 +2512,58 @@ zonecfg_modify_nwif(
return (Z_OK);
}
+void
+zonecfg_free_res_attr_list(struct zone_res_attrtab *valtab)
+{
+ if (valtab == NULL)
+ return;
+ zonecfg_free_res_attr_list(valtab->zone_res_attr_next);
+ free(valtab);
+}
+
+int
+zonecfg_add_res_attr(struct zone_res_attrtab **headptr,
+ struct zone_res_attrtab *valtabptr)
+{
+ struct zone_res_attrtab *last, *old, *new;
+
+ last = *headptr;
+ for (old = last; old != NULL; old = old->zone_res_attr_next)
+ last = old; /* walk to the end of the list */
+ new = valtabptr; /* alloc'd by caller */
+ new->zone_res_attr_next = NULL;
+ if (last == NULL)
+ *headptr = new;
+ else
+ last->zone_res_attr_next = new;
+ return (Z_OK);
+}
+
+int
+zonecfg_remove_res_attr(struct zone_res_attrtab **headptr,
+ struct zone_res_attrtab *valtabptr)
+{
+ struct zone_res_attrtab *last, *this, *next;
+
+ last = *headptr;
+ for (this = last; this != NULL; this = this->zone_res_attr_next) {
+ if (strcmp(this->zone_res_attr_name,
+ valtabptr->zone_res_attr_name) == 0 &&
+ strcmp(this->zone_res_attr_value,
+ valtabptr->zone_res_attr_value) == 0) {
+ next = this->zone_res_attr_next;
+ if (this == *headptr)
+ *headptr = next;
+ else
+ last->zone_res_attr_next = next;
+ free(this);
+ return (Z_OK);
+ } else
+ last = this;
+ }
+ return (Z_NO_PROPERTY_ID);
+}
+
/*
* Must be a comma-separated list of alpha-numeric file system names.
*/
@@ -2463,7 +2713,7 @@ zonecfg_set_hostid(zone_dochandle_t handle, const char *hostidp)
int
zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
{
- xmlNodePtr cur, firstmatch;
+ xmlNodePtr cur, val, firstmatch;
int err;
char match[MAXPATHLEN];
@@ -2508,13 +2758,40 @@ zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
sizeof (tabptr->zone_dev_match))) != Z_OK)
return (err);
+ tabptr->zone_dev_attrp = NULL;
+ for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+ struct zone_res_attrtab *valptr;
+
+ valptr = (struct zone_res_attrtab *)malloc(
+ sizeof (struct zone_res_attrtab));
+ if (valptr == NULL)
+ return (Z_NOMEM);
+
+ valptr->zone_res_attr_name[0] =
+ valptr->zone_res_attr_value[0] = '\0';
+ if (zonecfg_add_res_attr(&(tabptr->zone_dev_attrp), valptr)
+ != Z_OK) {
+ free(valptr);
+ break;
+ }
+
+ if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+ sizeof (valptr->zone_res_attr_name)) != Z_OK))
+ break;
+ if ((fetchprop(val, DTD_ATTR_VALUE,
+ valptr->zone_res_attr_value,
+ sizeof (valptr->zone_res_attr_value)) != Z_OK))
+ break;
+ }
+
return (Z_OK);
}
static int
zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
{
- xmlNodePtr newnode, cur = handle->zone_dh_cur;
+ xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
+ struct zone_res_attrtab *valptr;
int err;
newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEVICE, NULL);
@@ -2523,6 +2800,21 @@ zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
tabptr->zone_dev_match)) != Z_OK)
return (err);
+ for (valptr = tabptr->zone_dev_attrp; valptr != NULL;
+ valptr = valptr->zone_res_attr_next) {
+ valnode = xmlNewTextChild(newnode, NULL, DTD_ELEM_NETATTR,
+ NULL);
+ err = newprop(valnode, DTD_ATTR_NAME,
+ valptr->zone_res_attr_name);
+ if (err != Z_OK)
+ return (err);
+ err = newprop(valnode, DTD_ATTR_VALUE,
+ valptr->zone_res_attr_value);
+ if (err != Z_OK)
+ return (err);
+ }
+
+
return (Z_OK);
}
@@ -4737,7 +5029,7 @@ get_pool_sched_class(char *poolname, char *class, int clsize)
pool_conf_t *poolconf;
pool_t *pool;
pool_elem_t *pe;
- pool_value_t *pv = pool_value_alloc();
+ pool_value_t *pv;
const char *sched_str;
if (pool_get_status(&status) != PO_SUCCESS || status != POOL_ENABLED)
@@ -4758,15 +5050,23 @@ get_pool_sched_class(char *poolname, char *class, int clsize)
return (Z_NO_POOL);
}
+ if ((pv = pool_value_alloc()) == NULL) {
+ (void) pool_conf_close(poolconf);
+ pool_conf_free(poolconf);
+ return (Z_NO_POOL);
+ }
+
pe = pool_to_elem(poolconf, pool);
if (pool_get_property(poolconf, pe, "pool.scheduler", pv) !=
POC_STRING) {
(void) pool_conf_close(poolconf);
+ pool_value_free(pv);
pool_conf_free(poolconf);
return (Z_NO_ENTRY);
}
(void) pool_value_get_string(pv, &sched_str);
(void) pool_conf_close(poolconf);
+ pool_value_free(pv);
pool_conf_free(poolconf);
if (strlcpy(class, sched_str, clsize) >= clsize)
return (Z_TOO_BIG);
@@ -4875,7 +5175,8 @@ zonecfg_setnwifent(zone_dochandle_t handle)
int
zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
{
- xmlNodePtr cur;
+ xmlNodePtr cur, val;
+ struct zone_res_attrtab *valptr;
int err;
if (handle == NULL)
@@ -4911,6 +5212,24 @@ zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
return (err);
}
+ if ((err = fetchprop(cur, DTD_ATTR_MAC, tabptr->zone_nwif_mac,
+ sizeof (tabptr->zone_nwif_mac))) != Z_OK) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (err);
+ }
+
+ if ((err = fetchprop(cur, DTD_ATTR_VLANID, tabptr->zone_nwif_vlan_id,
+ sizeof (tabptr->zone_nwif_vlan_id))) != Z_OK) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (err);
+ }
+
+ if ((err = fetchprop(cur, DTD_ATTR_GNIC, tabptr->zone_nwif_gnic,
+ sizeof (tabptr->zone_nwif_gnic))) != Z_OK) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (err);
+ }
+
if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
tabptr->zone_nwif_defrouter,
sizeof (tabptr->zone_nwif_defrouter))) != Z_OK) {
@@ -4918,6 +5237,29 @@ zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
return (err);
}
+ tabptr->zone_nwif_attrp = NULL;
+ for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+ valptr = (struct zone_res_attrtab *)malloc(
+ sizeof (struct zone_res_attrtab));
+ if (valptr == NULL)
+ return (Z_NOMEM);
+
+ valptr->zone_res_attr_name[0] =
+ valptr->zone_res_attr_value[0] = '\0';
+ if (zonecfg_add_res_attr(&(tabptr->zone_nwif_attrp), valptr)
+ != Z_OK) {
+ free(valptr);
+ break;
+ }
+
+ if (fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+ sizeof (valptr->zone_res_attr_name)) != Z_OK)
+ break;
+ if (fetchprop(val, DTD_ATTR_VALUE, valptr->zone_res_attr_value,
+ sizeof (valptr->zone_res_attr_value)) != Z_OK)
+ break;
+ }
+
handle->zone_dh_cur = cur->next;
return (Z_OK);
}
@@ -4937,7 +5279,7 @@ zonecfg_setdevent(zone_dochandle_t handle)
int
zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
{
- xmlNodePtr cur;
+ xmlNodePtr cur, val;
int err;
if (handle == NULL)
@@ -4960,6 +5302,31 @@ zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
return (err);
}
+ tabptr->zone_dev_attrp = NULL;
+ for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+ struct zone_res_attrtab *valptr;
+
+ valptr = (struct zone_res_attrtab *)malloc(
+ sizeof (struct zone_res_attrtab));
+ if (valptr == NULL)
+ return (Z_NOMEM);
+
+ valptr->zone_res_attr_name[0] =
+ valptr->zone_res_attr_value[0] = '\0';
+ if (zonecfg_add_res_attr(&(tabptr->zone_dev_attrp), valptr)
+ != Z_OK) {
+ free(valptr);
+ break;
+ }
+
+ if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+ sizeof (valptr->zone_res_attr_name)) != Z_OK))
+ break;
+ if ((fetchprop(val, DTD_ATTR_VALUE, valptr->zone_res_attr_value,
+ sizeof (valptr->zone_res_attr_value)) != Z_OK))
+ break;
+ }
+
handle->zone_dh_cur = cur->next;
return (Z_OK);
}
@@ -5688,6 +6055,171 @@ zone_get_brand(char *zone_name, char *brandname, size_t rp_sz)
}
/*
+ * Atomically get a new zone_did value. The currently allocated value
+ * is stored in /etc/zones/did.txt. Lock the file, read the current value,
+ * increment, save the new value and unlock the file. Return the new value
+ * or -1 if there was an error. The ID namespace is large enough that we
+ * don't worry about recycling an ID when a zone is deleted.
+ */
+static zoneid_t
+new_zone_did()
+{
+ int fd;
+ int len;
+ int val;
+ struct flock lck;
+ char pathbuf[PATH_MAX];
+ char buf[80];
+
+ if (snprintf(pathbuf, sizeof (pathbuf), "%s%s", zonecfg_get_root(),
+ DEBUGID_FILE) >= sizeof (pathbuf)) {
+ printf(gettext("alternate root path is too long"));
+ return (-1);
+ }
+
+ if ((fd = open(pathbuf, O_RDWR | O_CREAT,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
+ perror("new_zone_did open failed");
+ return (-1);
+ }
+
+ /* Initialize the lock. */
+ lck.l_whence = SEEK_SET;
+ lck.l_start = 0;
+ lck.l_len = 0;
+
+ /* Wait until we acquire an exclusive lock on the file. */
+ lck.l_type = F_WRLCK;
+ if (fcntl(fd, F_SETLKW, &lck) == -1) {
+ perror("new_zone_did lock failed");
+ (void) close(fd);
+ return (-1);
+ }
+
+ /* Get currently allocated value */
+ len = read(fd, buf, sizeof (buf));
+ if (len == -1) {
+ perror("new_zone_did read failed");
+ val = -1;
+ } else {
+ if (lseek(fd, 0L, SEEK_SET) == -1) {
+ perror("new_zone_did seek failed");
+ val = -1;
+ } else {
+ if (len == 0) {
+ /* Just created the file, initialize at 1 */
+ val = 1;
+ } else {
+ val = atoi(buf);
+ val++;
+ }
+
+ (void) snprintf(buf, sizeof (buf), "%d\n", val);
+ len = strlen(buf);
+
+ /* Save newly allocated value */
+ if (write(fd, buf, len) == -1) {
+ perror("new_zone_did write failed");
+ val = -1;
+ }
+ }
+ }
+
+ /* Release the file lock. */
+ lck.l_type = F_UNLCK;
+ if (fcntl(fd, F_SETLK, &lck) == -1) {
+ perror("new_zone_did unlock failed");
+ val = -1;
+ }
+
+ if (close(fd) != 0)
+ perror("new_zone_did close failed");
+
+ return (val);
+}
+
+/*
+ * Called by zoneadmd to get the zone's debug ID.
+ * If the zone doesn't already have an ID, a new one is generated and
+ * persistently saved onto the zone. Normally either zoneadm or zonecfg
+ * will assign a new ID for the zone, so zoneadmd should never have to
+ * generate one, but we also handle that here just to be paranoid.
+ */
+zoneid_t
+zone_get_did(char *zone_name)
+{
+ int res;
+ zoneid_t new_did;
+ zone_dochandle_t handle;
+ char did_str[80];
+
+ if ((handle = zonecfg_init_handle()) == NULL)
+ return (getpid());
+
+ if (zonecfg_get_handle((char *)zone_name, handle) != Z_OK)
+ return (getpid());
+
+ res = getrootattr(handle, DTD_ATTR_DID, did_str, sizeof (did_str));
+
+ /* If the zone already has an assigned debug ID, return it. */
+ if (res == Z_OK && did_str[0] != '\0') {
+ zonecfg_fini_handle(handle);
+ return (atoi(did_str));
+ }
+
+ /*
+ * The zone doesn't have an assigned debug ID yet, generate one and
+ * save it as part of the zone definition.
+ */
+ if ((new_did = new_zone_did()) == -1) {
+ /*
+ * We should really never hit this block of code.
+ * Generating a new ID failed for some reason. Use the current
+ * pid as a temporary ID so that the zone can continue to boot
+ * but we don't persistently save this temporary ID on the zone.
+ */
+ zonecfg_fini_handle(handle);
+ return (getpid());
+ }
+
+ /* Now persistently save this new ID onto the zone. */
+ (void) snprintf(did_str, sizeof (did_str), "%d", new_did);
+ (void) setrootattr(handle, DTD_ATTR_DID, did_str);
+ (void) zonecfg_save(handle);
+
+ zonecfg_fini_handle(handle);
+ return (new_did);
+}
+
+zoneid_t
+zonecfg_get_did(zone_dochandle_t handle)
+{
+ char did_str[80];
+ int err;
+ zoneid_t did;
+
+ err = getrootattr(handle, DTD_ATTR_DID, did_str, sizeof (did_str));
+ if (err == Z_OK && did_str[0] != '\0')
+ did = atoi(did_str);
+ else
+ did = -1;
+
+ return (did);
+}
+
+void
+zonecfg_set_did(zone_dochandle_t handle)
+{
+ zoneid_t new_did;
+ char did_str[80];
+
+ if ((new_did = new_zone_did()) == -1)
+ return;
+ (void) snprintf(did_str, sizeof (did_str), "%d", new_did);
+ (void) setrootattr(handle, DTD_ATTR_DID, did_str);
+}
+
+/*
* Return the appropriate root for the active /dev.
* For normal zone, the path is $ZONEPATH/root;
* for scratch zone, the dev path is $ZONEPATH/lu.
@@ -5811,16 +6343,27 @@ int
zone_set_state(char *zone, zone_state_t state)
{
struct zoneent ze;
+ int res;
+ zone_state_t oldst = (zone_state_t)-1;
if (state != ZONE_STATE_CONFIGURED && state != ZONE_STATE_INSTALLED &&
state != ZONE_STATE_INCOMPLETE)
return (Z_INVAL);
+ (void) zone_get_state(zone, &oldst);
+
bzero(&ze, sizeof (ze));
(void) strlcpy(ze.zone_name, zone, sizeof (ze.zone_name));
ze.zone_state = state;
(void) strlcpy(ze.zone_path, "", sizeof (ze.zone_path));
- return (putzoneent(&ze, PZE_MODIFY));
+ res = putzoneent(&ze, PZE_MODIFY);
+
+ if (res == Z_OK) {
+ zonecfg_notify_conf_change(zone, zone_state_str(oldst),
+ zone_state_str(state));
+ }
+
+ return (res);
}
/*
@@ -5970,6 +6513,30 @@ zonecfg_get_uuid(const char *zonename, uuid_t uuid)
}
/*
+ * Changes a zone's UUID to the given value. Returns an error if the UUID is
+ * malformed or if the zone cannot be located.
+ */
+int
+zonecfg_set_uuid(const char *zonename, const char *zonepath,
+ const char *uuid)
+{
+ int err;
+ struct zoneent ze;
+
+ bzero(&ze, sizeof (ze));
+ ze.zone_state = -1; /* Preserve existing state in index */
+ (void) strlcpy(ze.zone_name, zonename, sizeof (ze.zone_name));
+ (void) strlcpy(ze.zone_path, zonepath, sizeof (ze.zone_path));
+ if (uuid_parse((char *)uuid, ze.zone_uuid) == -1)
+ return (Z_INVALID_PROPERTY);
+
+ if ((err = putzoneent(&ze, PZE_MODIFY)) != Z_OK)
+ return (err);
+
+ return (Z_OK);
+}
+
+/*
* File-system convenience functions.
*/
boolean_t
@@ -7002,86 +7569,49 @@ zonecfg_getpsetent(zone_dochandle_t handle, struct zone_psettab *tabptr)
return (err);
}
-static int
-add_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
-{
- xmlNodePtr newnode, cur = handle->zone_dh_cur;
- int err;
-
- newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_MCAP, NULL);
- if ((err = newprop(newnode, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap))
- != Z_OK)
- return (err);
-
- return (Z_OK);
-}
-
+/*
+ * Cleanup obsolete constructs in the configuration.
+ * Return true of the config has been updated and must be commited.
+ */
int
-zonecfg_delete_mcap(zone_dochandle_t handle)
+zonecfg_fix_obsolete(zone_dochandle_t handle)
{
- int err;
- xmlNodePtr cur = handle->zone_dh_cur;
+ int res = 0;
+ int add_physmem_rctl = 0;
+ xmlNodePtr cur;
+ char zone_physmem_cap[MAXNAMELEN];
- if ((err = operation_prep(handle)) != Z_OK)
- return (err);
+ if (operation_prep(handle) != Z_OK)
+ return (res);
+ /*
+ * If an obsolete mcap entry exists, convert it to the rctl.
+ */
+ cur = handle->zone_dh_cur;
for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
continue;
+ if (fetchprop(cur, DTD_ATTR_PHYSCAP,
+ zone_physmem_cap, sizeof (zone_physmem_cap)) == Z_OK) {
+ res = 1;
+ add_physmem_rctl = 1;
+ }
+
xmlUnlinkNode(cur);
xmlFreeNode(cur);
- return (Z_OK);
+ break;
}
- return (Z_NO_RESOURCE_ID);
-}
-
-int
-zonecfg_modify_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
-{
- int err;
- if (tabptr == NULL)
- return (Z_INVAL);
-
- err = zonecfg_delete_mcap(handle);
- /* it is ok if there is no mcap entry */
- if (err != Z_OK && err != Z_NO_RESOURCE_ID)
- return (err);
-
- if ((err = add_mcap(handle, tabptr)) != Z_OK)
- return (err);
-
- return (Z_OK);
-}
+ if (add_physmem_rctl) {
+ uint64_t cap;
+ char *endp;
-int
-zonecfg_lookup_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
-{
- xmlNodePtr cur;
- int err;
-
- if (tabptr == NULL)
- return (Z_INVAL);
-
- if ((err = operation_prep(handle)) != Z_OK)
- return (err);
-
- cur = handle->zone_dh_cur;
- for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
- if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
- continue;
- if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP,
- tabptr->zone_physmem_cap,
- sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
- handle->zone_dh_cur = handle->zone_dh_top;
- return (err);
- }
-
- return (Z_OK);
+ cap = strtoull(zone_physmem_cap, &endp, 10);
+ (void) zonecfg_set_aliased_rctl(handle, ALIAS_MAXPHYSMEM, cap);
}
- return (Z_NO_ENTRY);
+ return (res);
}
int
@@ -7139,51 +7669,6 @@ zonecfg_getsecflagsent(zone_dochandle_t handle,
return (err);
}
-static int
-getmcapent_core(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
-{
- xmlNodePtr cur;
- int err;
-
- if (handle == NULL)
- return (Z_INVAL);
-
- if ((cur = handle->zone_dh_cur) == NULL)
- return (Z_NO_ENTRY);
-
- for (; cur != NULL; cur = cur->next)
- if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) == 0)
- break;
- if (cur == NULL) {
- handle->zone_dh_cur = handle->zone_dh_top;
- return (Z_NO_ENTRY);
- }
-
- if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap,
- sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
- handle->zone_dh_cur = handle->zone_dh_top;
- return (err);
- }
-
- handle->zone_dh_cur = cur->next;
- return (Z_OK);
-}
-
-int
-zonecfg_getmcapent(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
-{
- int err;
-
- if ((err = zonecfg_setent(handle)) != Z_OK)
- return (err);
-
- err = getmcapent_core(handle, tabptr);
-
- (void) zonecfg_endent(handle);
-
- return (err);
-}
-
/*
* Get the full tree of pkg metadata in a set of nested AVL trees.
* pkgs_avl is an AVL tree of pkgs.
@@ -7962,7 +8447,7 @@ zonecfg_update_userauths(zone_dochandle_t handle, char *zonename)
(void) fclose(uaf);
return (Z_MISC_FS);
}
- if (!config_file_path(zonename, config_file)) {
+ if (!config_file_path(zonename, config_file, sizeof (config_file))) {
(void) fclose(uaf);
return (Z_MISC_FS);
}
diff --git a/usr/src/lib/libzonecfg/common/mapfile-vers b/usr/src/lib/libzonecfg/common/mapfile-vers
index c73533b97f..da4009d2fa 100644
--- a/usr/src/lib/libzonecfg/common/mapfile-vers
+++ b/usr/src/lib/libzonecfg/common/mapfile-vers
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2015, Joyent Inc.
#
#
@@ -53,6 +54,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_add_fs_option;
zonecfg_add_admin;
zonecfg_add_nwif;
+ zonecfg_add_res_attr;
zonecfg_add_pkg;
zonecfg_add_pset;
zonecfg_add_rctl;
@@ -80,7 +82,6 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_delete_dev;
zonecfg_delete_ds;
zonecfg_delete_filesystem;
- zonecfg_delete_mcap;
zonecfg_delete_nwif;
zonecfg_delete_pset;
zonecfg_delete_rctl;
@@ -106,7 +107,9 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_find_mounts;
zonecfg_find_scratch;
zonecfg_fini_handle;
+ zonecfg_fix_obsolete;
zonecfg_free_fs_option_list;
+ zonecfg_free_res_attr_list;
zonecfg_free_rctl_value_list;
zonecfg_get_aliased_rctl;
zonecfg_get_attach_handle;
@@ -120,6 +123,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_get_bootargs;
zonecfg_get_brand;
zonecfg_get_dflt_sched_class;
+ zonecfg_get_did;
zonecfg_getdevent;
zonecfg_getdevperment;
zonecfg_getdsent;
@@ -129,7 +133,6 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_get_hostid;
zonecfg_get_iptype;
zonecfg_get_limitpriv;
- zonecfg_getmcapent;
zonecfg_get_name;
zonecfg_get_name_by_uuid;
zonecfg_getnwifent;
@@ -163,7 +166,6 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_lookup_dev;
zonecfg_lookup_ds;
zonecfg_lookup_filesystem;
- zonecfg_lookup_mcap;
zonecfg_lookup_nwif;
zonecfg_lookup_pset;
zonecfg_lookup_rctl;
@@ -173,12 +175,12 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_modify_dev;
zonecfg_modify_ds;
zonecfg_modify_filesystem;
- zonecfg_modify_mcap;
zonecfg_modify_nwif;
zonecfg_modify_pset;
zonecfg_modify_rctl;
zonecfg_modify_secflags;
zonecfg_notify_bind;
+ zonecfg_notify_create;
zonecfg_notify_critical_abort;
zonecfg_notify_critical_enter;
zonecfg_notify_critical_exit;
@@ -188,6 +190,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_ping_zoneadmd;
zonecfg_release_lock_file;
zonecfg_remove_fs_option;
+ zonecfg_remove_res_attr;
zonecfg_remove_rctl_value;
zonecfg_remove_userauths;
zonecfg_reverse_scratch;
@@ -201,6 +204,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_set_autoboot;
zonecfg_set_bootargs;
zonecfg_set_brand;
+ zonecfg_set_did;
zonecfg_setdevent;
zonecfg_setdevperment;
zonecfg_setdsent;
@@ -216,6 +220,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_set_root;
zonecfg_set_sched;
zonecfg_set_swinv;
+ zonecfg_set_uuid;
zonecfg_set_zonepath;
zonecfg_strerror;
zonecfg_str_to_bytes;
@@ -234,6 +239,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zonecfg_verify_save;
zonecfg_warn_poold;
zone_get_brand;
+ zone_get_did;
zone_get_devroot;
zone_get_id;
zone_get_rootpath;
diff --git a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
index 03be1a2bf5..228bb8ace2 100644
--- a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
+++ b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
@@ -21,6 +21,7 @@
CDDL HEADER END
Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2011, Joyent Inc. All rights reserved.
-->
@@ -46,14 +47,21 @@
<!ATTLIST inherited-pkg-dir directory CDATA #REQUIRED>
-<!ELEMENT network EMPTY>
+<!ELEMENT net-attr EMPTY>
+<!ATTLIST net-attr name CDATA #REQUIRED
+ value CDATA #REQUIRED>
+
+<!ELEMENT network (net-attr)*>
<!ATTLIST network address CDATA ""
allowed-address CDATA ""
defrouter CDATA ""
- physical CDATA #REQUIRED>
+ global-nic CDATA ""
+ mac-addr CDATA ""
+ physical CDATA #REQUIRED
+ vlan-id CDATA "">
-<!ELEMENT device EMPTY>
+<!ELEMENT device (net-attr)*>
<!ATTLIST device match CDATA #REQUIRED>
@@ -162,6 +170,7 @@
limitpriv CDATA ""
bootargs CDATA ""
brand CDATA ""
+ debugid CDATA ""
scheduling-class CDATA ""
fs-allowed CDATA ""
version NMTOKEN #FIXED '1'>
diff --git a/usr/src/lib/libzpool/Makefile.com b/usr/src/lib/libzpool/Makefile.com
index 47ff1d0b76..ca5aeed189 100644
--- a/usr/src/lib/libzpool/Makefile.com
+++ b/usr/src/lib/libzpool/Makefile.com
@@ -75,6 +75,11 @@ CPPFLAGS.first = -I$(SRC)/lib/libfakekernel/common
CPPFLAGS += $(INCS) -DDEBUG -D_FAKE_KERNEL
+# The following is needed to fix the SmartOS build; see OS-6582. We cannot do
+# a conditional appendage to INCS, since that breaks the lint build.
+CFLAGS += -isystem $(ROOT)/usr/include
+CFLAGS64 += -isystem $(ROOT)/usr/include
+
CERRWARN += -_gcc=-Wno-parentheses
CERRWARN += -_gcc=-Wno-switch
CERRWARN += -_gcc=-Wno-type-limits
diff --git a/usr/src/lib/nsswitch/dns/Makefile.com b/usr/src/lib/nsswitch/dns/Makefile.com
index e1a02743d1..bc66038274 100644
--- a/usr/src/lib/nsswitch/dns/Makefile.com
+++ b/usr/src/lib/nsswitch/dns/Makefile.com
@@ -52,5 +52,5 @@ $(ROOT)/usr/lib/$(MACH64)/$(DYNLIB1):= \
CPPFLAGS += -DNSS_DNS_LIBRESOLV=\"libresolv.so.2\"
-LDLIBS += -lnsl -lsocket
-
+LDLIBS += -lnsl -lresolv_joy -lsocket
+DYNLIB1 = nss_dns.so$(VERS)
diff --git a/usr/src/lib/nsswitch/dns/common/dns_common.h b/usr/src/lib/nsswitch/dns/common/dns_common.h
index 717f56d70f..83d486cf57 100644
--- a/usr/src/lib/nsswitch/dns/common/dns_common.h
+++ b/usr/src/lib/nsswitch/dns/common/dns_common.h
@@ -31,8 +31,6 @@
#ifndef _DNS_COMMON_H
#define _DNS_COMMON_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
@@ -43,7 +41,7 @@
#include <thread.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
-#include <resolv.h>
+#include <resolv_joy.h>
#include <syslog.h>
#include <nsswitch.h>
#include <nss_common.h>
diff --git a/usr/src/lib/nsswitch/dns/common/dns_mt.c b/usr/src/lib/nsswitch/dns/common/dns_mt.c
index 128b1bde75..4af3f671c0 100644
--- a/usr/src/lib/nsswitch/dns/common/dns_mt.c
+++ b/usr/src/lib/nsswitch/dns/common/dns_mt.c
@@ -23,8 +23,9 @@
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ */
/*
* dns_mt.c
@@ -49,52 +50,41 @@
static void _nss_dns_init(void);
extern struct hostent *res_gethostbyname(const char *);
-#pragma weak res_gethostbyname
-#define RES_SET_NO_HOSTS_FALLBACK "__res_set_no_hosts_fallback"
-extern void __res_set_no_hosts_fallback(void);
-#pragma weak __res_set_no_hosts_fallback
+#define RES_SET_NO_HOSTS_FALLBACK "__joy_res_set_no_hosts_fallback"
+extern void __joy_res_set_no_hosts_fallback(void);
-#define RES_UNSET_NO_HOSTS_FALLBACK "__res_unset_no_hosts_fallback"
-extern void __res_unset_no_hosts_fallback(void);
-#pragma weak __res_unset_no_hosts_fallback
+#define RES_UNSET_NO_HOSTS_FALLBACK "__joy_res_unset_no_hosts_fallback"
+extern void __joy_res_unset_no_hosts_fallback(void);
#define RES_GET_RES "__res_get_res"
extern struct __res_state *__res_get_res(void);
-#pragma weak __res_get_res
#define RES_ENABLE_MT "__res_enable_mt"
extern int __res_enable_mt(void);
-#pragma weak __res_enable_mt
#define RES_DISABLE_MT "__res_disable_mt"
extern int __res_disable_mt(void);
-#pragma weak __res_disable_mt
#define RES_GET_H_ERRNO "__res_get_h_errno"
extern int *__res_get_h_errno();
-#pragma weak __res_get_h_errno
-#define __H_ERRNO "__h_errno"
-extern int *__h_errno(void);
-#pragma weak __h_errno
+#define __H_ERRNO "__joy_h_errno"
+extern int *__joy_h_errno(void);
-#define RES_OVERRIDE_RETRY "__res_override_retry"
-extern int __res_override_retry(int);
-#pragma weak __res_override_retry
+#define RES_OVERRIDE_RETRY "__joy_res_override_retry"
+extern int __joy_res_override_retry(int);
static void __fallback_set_no_hosts(void);
-static int *__fallback_h_errno(void);
-static int __fallback_override_retry(int);
static int __is_mt_safe(void);
void (*set_no_hosts_fallback)(void) = __fallback_set_no_hosts;
void (*unset_no_hosts_fallback)(void) = __fallback_set_no_hosts;
struct __res_state *(*set_res_retry)() = 0;
-int (*enable_mt)() = 0;
-int (*disable_mt)() = 0;
-int *(*get_h_errno)(void) = 0;
-int (*override_retry)(int) = 0;
+int (*enable_mt)() = __is_mt_safe;
+int (*disable_mt)() = __is_mt_safe;
+int *(*get_h_errno)(void) = __joy_h_errno;
+int (*override_retry)(int) = __joy_res_override_retry;
/* Usually set from the Makefile */
#ifndef NSS_DNS_LIBRESOLV
@@ -106,91 +96,12 @@ extern int h_errno;
mutex_t one_lane = DEFAULTMUTEX;
+/* Because we link against libresolv_joy.so.2, this is relatively easy. */
void
_nss_dns_init(void)
{
- void *reslib, (*f_void_ptr)();
-
- /* If no libresolv library, then load one */
- if (res_gethostbyname == 0) {
- if ((reslib =
- dlopen(NSS_DNS_LIBRESOLV, RTLD_LAZY|RTLD_GLOBAL)) != 0) {
- /* Turn off /etc/hosts fall back in libresolv */
- if ((f_void_ptr = (void (*)(void))dlsym(reslib,
- RES_SET_NO_HOSTS_FALLBACK)) != 0) {
- set_no_hosts_fallback = f_void_ptr;
- }
- if ((f_void_ptr = (void (*)(void))dlsym(reslib,
- RES_SET_NO_HOSTS_FALLBACK)) != 0) {
- unset_no_hosts_fallback = f_void_ptr;
- }
- /* Set number of resolver retries */
- if ((override_retry = (int (*)(int))dlsym(reslib,
- RES_OVERRIDE_RETRY)) == 0) {
- set_res_retry =
- (struct __res_state *(*)(void))dlsym(reslib,
- RES_GET_RES);
- override_retry = __fallback_override_retry;
- }
- /*
- * Select h_errno retrieval function. A BIND 8.2.2
- * libresolv.so.2 will have __h_errno, a BIND 8.1.2
- * one will have __res_get_h_errno, and other
- * versions may have nothing at all.
- *
- * Also try to bind to the relevant MT enable/disable
- * functions which are also dependent on the version
- * of the BIND libresolv.so.2 being used.
- */
- if ((get_h_errno = (int *(*)(void))dlsym(reslib,
- __H_ERRNO)) != 0) {
- /* BIND 8.2.2 libresolv.so.2 is MT safe. */
- enable_mt = __is_mt_safe;
- disable_mt = __is_mt_safe;
- } else {
- if ((get_h_errno =
- (int *(*)(void))dlsym(reslib,
- RES_GET_H_ERRNO)) == 0) {
- get_h_errno = __fallback_h_errno;
- }
- /*
- * Pre-BIND 8.2.2 was not MT safe. Try to
- * bind the MT enable/disable functions.
- */
- if ((enable_mt = (int (*)(void))dlsym(reslib,
- RES_ENABLE_MT)) != 0 &&
- (disable_mt = (int (*)(void))dlsym(reslib,
- RES_DISABLE_MT)) == 0) {
- enable_mt = 0;
- }
- }
- }
- } else {
- /* Libresolv already loaded */
- if ((f_void_ptr = __res_set_no_hosts_fallback) != 0) {
- set_no_hosts_fallback = f_void_ptr;
- }
- if ((f_void_ptr = __res_unset_no_hosts_fallback) != 0) {
- unset_no_hosts_fallback = f_void_ptr;
- }
- if ((override_retry = __res_override_retry) == 0) {
- set_res_retry = __res_get_res;
- override_retry = __fallback_override_retry;
- }
- if ((get_h_errno = __h_errno) == 0 &&
- (get_h_errno = __res_get_h_errno) == 0) {
- get_h_errno = __fallback_h_errno;
- }
- if (get_h_errno == __h_errno) {
- enable_mt = __is_mt_safe;
- disable_mt = __is_mt_safe;
- } else {
- if ((enable_mt = __res_enable_mt) != 0 &&
- (disable_mt = __res_disable_mt) == 0) {
- enable_mt = 0;
- }
- }
- }
+ enable_mt = __is_mt_safe;
+ disable_mt = __is_mt_safe;
}
@@ -228,36 +139,6 @@ __is_mt_safe(void) {
}
-/*
- * Return pointer to the global h_errno variable
- */
-static int *
-__fallback_h_errno(void) {
- return (&h_errno);
-}
-
-
-/*
- * This function is called when the resolver library doesn't provide its
- * own function to establish an override retry. If we can get a pointer
- * to the per-thread _res (i.e., set_res_retry != 0), we set the retries
- * directly, and return the previous number of retries. Otherwise, there's
- * nothing to do.
- */
-static int
-__fallback_override_retry(int retry) {
- struct __res_state *res;
- int old_retry = 0;
-
- if (set_res_retry != 0) {
- res = set_res_retry();
- old_retry = res->retry;
- res->retry = retry;
- }
- return (old_retry);
-}
-
-
static void
__fallback_set_no_hosts(void) {
}
diff --git a/usr/src/lib/nsswitch/dns/common/gethostent.c b/usr/src/lib/nsswitch/dns/common/gethostent.c
index 648ea8ba01..d321dd24c6 100644
--- a/usr/src/lib/nsswitch/dns/common/gethostent.c
+++ b/usr/src/lib/nsswitch/dns/common/gethostent.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* gethostent.c
*
@@ -53,12 +51,6 @@ static struct hostent *_gethostbyaddr(int *h_errnop, const char *addr,
int len, int type);
struct hostent *_nss_dns_gethostbyname2(int *h_errnop, const char *name);
-#pragma weak res_gethostbyname
-#pragma weak res_gethostbyname2
-#pragma weak res_gethostbyaddr
-#pragma weak res_sethostent
-#pragma weak res_endhostent
-
nss_backend_t *_nss_dns_constr(dns_backend_op_t ops[], int n_ops);
nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *);
diff --git a/usr/src/lib/nsswitch/dns/common/gethostent6.c b/usr/src/lib/nsswitch/dns/common/gethostent6.c
index ee85832073..f3efc4eae6 100644
--- a/usr/src/lib/nsswitch/dns/common/gethostent6.c
+++ b/usr/src/lib/nsswitch/dns/common/gethostent6.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* This is the DNS backend for IPv6 addresses.
* getbyname() is a local routine, but getbyaddr() actually shares the
@@ -50,8 +48,6 @@
*/
-#pragma weak res_endhostent
-
extern struct hostent *_gethostbyname(int *, const char *);
extern struct hostent *_nss_dns_gethostbyname2(int *, const char *);
diff --git a/usr/src/lib/pysolaris/Makefile.com b/usr/src/lib/pysolaris/Makefile.com
index 4a65fe3976..ac9b344e40 100644
--- a/usr/src/lib/pysolaris/Makefile.com
+++ b/usr/src/lib/pysolaris/Makefile.com
@@ -45,6 +45,7 @@ LIBS = $(DYNLIB)
LDLIBS += -lc -lsec -lidmap -lpython$(PYVER)$(PYSUFFIX)
NATIVE_LIBS += libpython$(PYVER)$(PYSUFFIX).so
CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(ADJUNCT_PROTO)/usr/include/python2.6
CERRWARN += -_gcc=-Wno-unused-variable
CPPFLAGS += \
-I$(ADJUNCT_PROTO)/usr/include/python$(PYVER)$(PYSUFFIX)
diff --git a/usr/src/lib/varpd/Makefile b/usr/src/lib/varpd/Makefile
index 0962119d1c..7c78fb3145 100644
--- a/usr/src/lib/varpd/Makefile
+++ b/usr/src/lib/varpd/Makefile
@@ -10,10 +10,10 @@
#
#
-# Copyright 2018 Joyent, Inc.
+# Copyright 2021 Joyent, Inc.
#
-SUBDIRS = libvarpd direct files
+SUBDIRS = libvarpd direct files svp
all := TARGET = all
clean := TARGET = clean
diff --git a/usr/src/lib/varpd/direct/Makefile.com b/usr/src/lib/varpd/direct/Makefile.com
index 4e8564bae0..9d62140620 100644
--- a/usr/src/lib/varpd/direct/Makefile.com
+++ b/usr/src/lib/varpd/direct/Makefile.com
@@ -10,7 +10,7 @@
#
#
-# Copyright 2015 Joyent, Inc.
+# Copyright 2021 Joyent, Inc.
#
LIBRARY = libvarpd_direct.a
diff --git a/usr/src/lib/varpd/direct/common/llib-lvarpd_direct b/usr/src/lib/varpd/direct/common/llib-lvarpd_direct
new file mode 100644
index 0000000000..03c34f4fcb
--- /dev/null
+++ b/usr/src/lib/varpd/direct/common/llib-lvarpd_direct
@@ -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 2015 Joyent, Inc.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
diff --git a/usr/src/lib/varpd/direct/sparc/Makefile b/usr/src/lib/varpd/direct/sparc/Makefile
new file mode 100644
index 0000000000..f2b4f63da5
--- /dev/null
+++ b/usr/src/lib/varpd/direct/sparc/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 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/varpd/direct/sparcv9/Makefile b/usr/src/lib/varpd/direct/sparcv9/Makefile
new file mode 100644
index 0000000000..d552642882
--- /dev/null
+++ b/usr/src/lib/varpd/direct/sparcv9/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 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/varpd/files/Makefile b/usr/src/lib/varpd/files/Makefile
index 511ea1f94d..6253d3c242 100644
--- a/usr/src/lib/varpd/files/Makefile
+++ b/usr/src/lib/varpd/files/Makefile
@@ -10,7 +10,7 @@
#
#
-# Copyright 2015 Joyent, Inc.
+# Copyright 2021 Joyent, Inc.
#
include ../../Makefile.lib
diff --git a/usr/src/lib/varpd/files/Makefile.com b/usr/src/lib/varpd/files/Makefile.com
index 13ff2149ce..dc24395673 100644
--- a/usr/src/lib/varpd/files/Makefile.com
+++ b/usr/src/lib/varpd/files/Makefile.com
@@ -10,7 +10,7 @@
#
#
-# Copyright 2018 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
#
LIBRARY = libvarpd_files.a
diff --git a/usr/src/lib/varpd/files/common/llib-lvarpd_files b/usr/src/lib/varpd/files/common/llib-lvarpd_files
new file mode 100644
index 0000000000..03c34f4fcb
--- /dev/null
+++ b/usr/src/lib/varpd/files/common/llib-lvarpd_files
@@ -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 2015 Joyent, Inc.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
diff --git a/usr/src/lib/varpd/files/sparc/Makefile b/usr/src/lib/varpd/files/sparc/Makefile
new file mode 100644
index 0000000000..f2b4f63da5
--- /dev/null
+++ b/usr/src/lib/varpd/files/sparc/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 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/varpd/files/sparcv9/Makefile b/usr/src/lib/varpd/files/sparcv9/Makefile
new file mode 100644
index 0000000000..d552642882
--- /dev/null
+++ b/usr/src/lib/varpd/files/sparcv9/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 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/varpd/libvarpd/Makefile b/usr/src/lib/varpd/libvarpd/Makefile
index 034ba30c1d..2dbc8d7c5d 100644
--- a/usr/src/lib/varpd/libvarpd/Makefile
+++ b/usr/src/lib/varpd/libvarpd/Makefile
@@ -10,7 +10,7 @@
#
#
-# Copyright 2015 Joyent, Inc.
+# Copyright 2021 Joyent, Inc.
#
include ../../Makefile.lib
diff --git a/usr/src/lib/varpd/libvarpd/Makefile.com b/usr/src/lib/varpd/libvarpd/Makefile.com
index dc51193367..7f45bd30e2 100644
--- a/usr/src/lib/varpd/libvarpd/Makefile.com
+++ b/usr/src/lib/varpd/libvarpd/Makefile.com
@@ -10,7 +10,7 @@
#
#
-# Copyright 2015 Joyent, Inc.
+# Copyright 2021 Joyent, Inc.
#
LIBRARY = libvarpd.a
@@ -32,7 +32,8 @@ include ../../../Makefile.lib
include ../../../Makefile.rootfs
LIBS = $(DYNLIB)
-LDLIBS += -lc -lavl -lumem -lidspace -lnvpair -lmd -lrename
+LDLIBS += -lc -lavl -lumem -lidspace -lnvpair -lmd -lrename \
+ -lbunyan
CPPFLAGS += -I../common
CERRWARN += -erroff=E_STRUCT_DERIVED_FROM_FLEX_MBR
diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd.c b/usr/src/lib/varpd/libvarpd/common/libvarpd.c
index 4e4c189a43..e4460089cc 100644
--- a/usr/src/lib/varpd/libvarpd/common/libvarpd.c
+++ b/usr/src/lib/varpd/libvarpd/common/libvarpd.c
@@ -98,6 +98,14 @@ libvarpd_create(varpd_handle_t **vphp)
return (ret);
}
+ if ((ret = bunyan_init("varpd", &vip->vdi_bunyan)) != 0) {
+ libvarpd_overlay_fini(vip);
+ umem_cache_destroy(vip->vdi_qcache);
+ id_space_destroy(vip->vdi_idspace);
+ umem_free(vip, sizeof (varpd_impl_t));
+ return (ret);
+ }
+
libvarpd_persist_init(vip);
avl_create(&vip->vdi_plugins, libvarpd_plugin_comparator,
@@ -315,6 +323,13 @@ out:
return (ret);
}
+const bunyan_logger_t *
+libvarpd_plugin_bunyan(varpd_provider_handle_t *vhp)
+{
+ varpd_instance_t *inst = (varpd_instance_t *)vhp;
+ return (inst->vri_impl->vdi_bunyan);
+}
+
static void
libvarpd_prefork(void)
{
diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_impl.h b/usr/src/lib/varpd/libvarpd/common/libvarpd_impl.h
index f8530a7112..60f0dc5fff 100644
--- a/usr/src/lib/varpd/libvarpd/common/libvarpd_impl.h
+++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_impl.h
@@ -53,6 +53,7 @@ typedef struct varpd_impl {
avl_tree_t vdi_linstances; /* vdi_lock */
id_space_t *vdi_idspace; /* RO */
umem_cache_t *vdi_qcache; /* RO */
+ bunyan_logger_t *vdi_bunyan; /* RO */
int vdi_overlayfd; /* RO */
int vdi_doorfd; /* vdi_lock */
int vdi_persistfd; /* vdi_plock */
diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_overlay.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_overlay.c
index 167c004a90..f314440056 100644
--- a/usr/src/lib/varpd/libvarpd/common/libvarpd_overlay.c
+++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_overlay.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2021 Joyent, Inc.
*/
/*
diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_plugin.c b/usr/src/lib/varpd/libvarpd/common/libvarpd_plugin.c
index 176306a3f7..ac73286fdd 100644
--- a/usr/src/lib/varpd/libvarpd/common/libvarpd_plugin.c
+++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_plugin.c
@@ -25,6 +25,7 @@
#include <dlfcn.h>
#include <link.h>
#include <stdio.h>
+#include <bunyan.h>
static varpd_impl_t *varpd_load_handle;
static const char *varpd_load_path;
@@ -58,18 +59,21 @@ libvarpd_plugin_alloc(uint_t version, int *errp)
errp = &err;
if (version != VARPD_VERSION_ONE) {
- (void) fprintf(stderr,
- "unsupported registration version %u - %s\n",
- version, varpd_load_path);
+ (void) bunyan_warn(varpd_load_handle->vdi_bunyan,
+ "unsupported registration version",
+ BUNYAN_T_STRING, "module_path", varpd_load_path,
+ BUNYAN_T_INT32, "module_version", version,
+ BUNYAN_T_END);
*errp = EINVAL;
return (NULL);
}
vprp = umem_alloc(sizeof (varpd_plugin_register_t), UMEM_DEFAULT);
if (vprp == NULL) {
- (void) fprintf(stderr,
- "failed to allocate registration handle - %s\n",
- varpd_load_path);
+ (void) bunyan_warn(varpd_load_handle->vdi_bunyan,
+ "failed to allocate registration handle",
+ BUNYAN_T_STRING, "module_path", varpd_load_path,
+ BUNYAN_T_END);
*errp = ENOMEM;
return (NULL);
}
@@ -93,17 +97,20 @@ libvarpd_plugin_register(varpd_plugin_register_t *vprp)
vpp = umem_alloc(sizeof (varpd_plugin_t), UMEM_DEFAULT);
if (vpp == NULL) {
- (void) fprintf(stderr,
- "failed to allocate memory for the varpd_plugin_t - %s\n",
- varpd_load_path);
+ (void) bunyan_warn(varpd_load_handle->vdi_bunyan,
+ "failed to allocate memory for the varpd_plugin_t",
+ BUNYAN_T_STRING, "module_path", varpd_load_path,
+ BUNYAN_T_END);
return (ENOMEM);
}
/* Watch out for an evil plugin */
if (vprp->vpr_version != VARPD_VERSION_ONE) {
- (void) fprintf(stderr,
- "unsupported registration version %u - %s\n",
- vprp->vpr_version, varpd_load_path);
+ (void) bunyan_warn(varpd_load_handle->vdi_bunyan,
+ "unsupported registration version",
+ BUNYAN_T_STRING, "module_path", varpd_load_path,
+ BUNYAN_T_INT32, "module_version", vprp->vpr_version,
+ BUNYAN_T_END);
return (EINVAL);
}
@@ -114,9 +121,11 @@ libvarpd_plugin_register(varpd_plugin_register_t *vprp)
mutex_enter(&varpd_load_handle->vdi_lock);
lookup.vpp_name = vprp->vpr_name;
if (avl_find(&varpd_load_handle->vdi_plugins, &lookup, NULL) != NULL) {
- (void) fprintf(stderr,
- "module already exists with requested name '%s' - %s\n",
- vprp->vpr_name, varpd_load_path);
+ (void) bunyan_warn(varpd_load_handle->vdi_bunyan,
+ "module already exists with requested name",
+ BUNYAN_T_STRING, "module_path", varpd_load_path,
+ BUNYAN_T_STRING, "name", vprp->vpr_name,
+ BUNYAN_T_END);
mutex_exit(&varpd_load_handle->vdi_lock);
mutex_exit(&varpd_load_lock);
umem_free(vpp, sizeof (varpd_plugin_t));
@@ -124,9 +133,10 @@ libvarpd_plugin_register(varpd_plugin_register_t *vprp)
}
vpp->vpp_name = strdup(vprp->vpr_name);
if (vpp->vpp_name == NULL) {
- (void) fprintf(stderr,
- "failed to allocate memory to duplicate name - %s\n",
- varpd_load_path);
+ (void) bunyan_warn(varpd_load_handle->vdi_bunyan,
+ "failed to allocate memory to duplicate name",
+ BUNYAN_T_STRING, "module_path", varpd_load_path,
+ BUNYAN_T_END);
mutex_exit(&varpd_load_handle->vdi_lock);
mutex_exit(&varpd_load_lock);
umem_free(vpp, sizeof (varpd_plugin_t));
@@ -167,8 +177,11 @@ libvarpd_plugin_load_cb(varpd_impl_t *vip, const char *path, void *unused)
varpd_load_path = path;
dlp = dlopen(path, RTLD_LOCAL | RTLD_NOW);
- if (dlp == NULL)
- (void) fprintf(stderr, "dlopen failed - %s\n", path);
+ if (dlp == NULL) {
+ (void) bunyan_error(vip->vdi_bunyan, "dlopen failed",
+ BUNYAN_T_STRING, "module path", path,
+ BUNYAN_T_END);
+ }
path = NULL;
return (0);
diff --git a/usr/src/lib/varpd/libvarpd/common/libvarpd_provider.h b/usr/src/lib/varpd/libvarpd/common/libvarpd_provider.h
index b6910b9ed5..050b9e600a 100644
--- a/usr/src/lib/varpd/libvarpd/common/libvarpd_provider.h
+++ b/usr/src/lib/varpd/libvarpd/common/libvarpd_provider.h
@@ -36,8 +36,8 @@
* succeeds, then it should proceed to fill out the registration and then call,
* libvarpd_plugin_register() with it. Regardless of whether it succeeds or
* fails, it should call libvarpd_plugin_free(). In the case of failure, there
- * is not much that the module should do, other than log some message to
- * stderr.
+ * is not much that the module should do, other than log some message to the
+ * standard bunyan logger that exists.
*
* Once libvarpd_plugin_register() returns, the module should assume that any
* of the operations it defined in the operation vector may be called and
@@ -287,6 +287,7 @@
* context, including from the operation vectors.
*/
+#include <bunyan.h>
#include <libvarpd.h>
#include <libnvpair.h>
#include <sys/socket.h>
@@ -366,6 +367,7 @@ extern int libvarpd_plugin_register(varpd_plugin_register_t *);
* Blowing up and logging
*/
extern void libvarpd_panic(const char *, ...) __NORETURN;
+extern const bunyan_logger_t *libvarpd_plugin_bunyan(varpd_provider_handle_t *);
/*
* Misc. Information APIs
diff --git a/usr/src/lib/varpd/libvarpd/common/llib-lvarpd b/usr/src/lib/varpd/libvarpd/common/llib-lvarpd
new file mode 100644
index 0000000000..85150d3463
--- /dev/null
+++ b/usr/src/lib/varpd/libvarpd/common/llib-lvarpd
@@ -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 2015 Joyent, Inc.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <libvarpd.h>
diff --git a/usr/src/lib/varpd/libvarpd/sparc/Makefile b/usr/src/lib/varpd/libvarpd/sparc/Makefile
new file mode 100644
index 0000000000..f2b4f63da5
--- /dev/null
+++ b/usr/src/lib/varpd/libvarpd/sparc/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 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/varpd/libvarpd/sparcv9/Makefile b/usr/src/lib/varpd/libvarpd/sparcv9/Makefile
new file mode 100644
index 0000000000..d552642882
--- /dev/null
+++ b/usr/src/lib/varpd/libvarpd/sparcv9/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 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/varpd/svp/Makefile b/usr/src/lib/varpd/svp/Makefile
new file mode 100644
index 0000000000..48417965b4
--- /dev/null
+++ b/usr/src/lib/varpd/svp/Makefile
@@ -0,0 +1,42 @@
+#
+# 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 2018 Joyent, Inc.
+#
+
+include ../../Makefile.lib
+
+HDRS = libvarpd_svp_prot.h # For snoop
+HDRDIR = common
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+
+.KEEP_STATE:
+
+all clean clobber install: $(SUBDIRS)
+
+install: install_h
+install_h: $(ROOTHDRS)
+
+check:
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/varpd/svp/Makefile.com b/usr/src/lib/varpd/svp/Makefile.com
new file mode 100644
index 0000000000..91baacb430
--- /dev/null
+++ b/usr/src/lib/varpd/svp/Makefile.com
@@ -0,0 +1,52 @@
+#
+# 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 2020 Joyent, Inc.
+#
+
+LIBRARY = libvarpd_svp.a
+VERS = .1
+OBJECTS = libvarpd_svp.o \
+ libvarpd_svp_conn.o \
+ libvarpd_svp_crc.o \
+ libvarpd_svp_host.o \
+ libvarpd_svp_loop.o \
+ libvarpd_svp_remote.o \
+ libvarpd_svp_shootdown.o \
+ libvarpd_svp_timer.o
+
+include ../../../Makefile.lib
+include ../../Makefile.plugin
+
+LIBS = $(DYNLIB)
+
+#
+# Yes, this isn't a command, but libcmdutils does have the list(9F)
+# functions and better to use that then compile list.o yet again
+# ourselves... probably.
+#
+LDLIBS += -lc -lumem -lnvpair -lsocket -lavl \
+ -lcmdutils -lidspace -lbunyan
+CPPFLAGS += -I../common
+
+LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN
+LINTFLAGS64 += -erroff=E_BAD_PTR_CAST_ALIGN
+SRCDIR = ../common
+
+C99MODE= -xc99=%all
+C99LMODE= -Xc99=%all
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+include ../../../Makefile.targ
diff --git a/usr/src/lib/varpd/svp/amd64/Makefile b/usr/src/lib/varpd/svp/amd64/Makefile
new file mode 100644
index 0000000000..d552642882
--- /dev/null
+++ b/usr/src/lib/varpd/svp/amd64/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 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp.c b/usr/src/lib/varpd/svp/common/libvarpd_svp.c
new file mode 100644
index 0000000000..2483cb6214
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/libvarpd_svp.c
@@ -0,0 +1,1140 @@
+/*
+ * 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.
+ * Copyright 2022 MNX Cloud, Inc.
+ */
+
+/*
+ * This plugin implements the SDC VXLAN Protocol (SVP).
+ *
+ * This plugin is designed to work with a broader distributed system that
+ * mainains a database of mappings and provides a means of looking up data and
+ * provides a stream of updates. While it is named after VXLAN, there isn't
+ * anything specific to VXLAN baked into the protocol at this time, other than
+ * that it requires both an IP address and a port; however, if there's a good
+ * reason to support others here, we can modify that.
+ *
+ * -----------
+ * Terminology
+ * -----------
+ *
+ * Throughout this module we refer to a few different kinds of addresses:
+ *
+ * VL3
+ *
+ * A VL3 address, or virtual layer 3, refers to the layer three addreses
+ * that are used by entities on an overlay network. As far as we're
+ * concerned that means that this is the IP address of an interface on an
+ * overlay network.
+ *
+ * VL2
+ *
+ * A VL2 address, or a virtual layer 2, referes to the link-layer addresses
+ * that are used by entities on an overlay network. As far as we're
+ * concerned that means that this is the MAC addresses of an interface on
+ * an overlay network.
+ *
+ * UL3
+ *
+ * A UL3, or underlay layer 3, refers to the layer three (IP) address on
+ * the underlay network.
+ *
+ * The svp plugin provides lookups from VL3->VL2, eg. the equivalent of an ARP
+ * or NDP query, and then also provides VL2->UL3 lookups.
+ *
+ * -------------------
+ * Protocol Operations
+ * -------------------
+ *
+ * The svp protocol is defined in lib/varpd/svp/common/libvarpd_svp_prot.h. It
+ * defines the basic TCP protocol that we use to communicate to hosts. At this
+ * time, it is not quite 100% implemented in both this plug-in and our primary
+ * server, sdc-portolan (see https://github.com/joyent/sdc-portolan).
+ *
+ * At this time, we don't quite support everything that we need to. Including
+ * the SVP_R_BULK_REQ and SVP_R_SHOOTDOWN.
+ *
+ * ---------------------------------
+ * General Design and Considerations
+ * ---------------------------------
+ *
+ * Every instance of the svp plugin requires the hostname and port of a server
+ * to contact. Though, we have co-opted the port 1296 (the year of the oldest
+ * extant portolan) as our default port.
+ *
+ * Each of the different instance of the plugins has a corresponding remote
+ * backend. The remote backend represents the tuple of the [ host, port ].
+ * Different instances that share the same host and port tuple will use the same
+ * backend.
+ *
+ * The backend is actually in charge of performing lookups, resolving and
+ * updating the set of remote hosts based on the DNS resolution we've been
+ * provided, and taking care of things like shootdowns.
+ *
+ * The whole plugin itself maintains an event loop and a number of threads to
+ * service that event loop. On top of that event loop, we have a simple timer
+ * backend that ticks at one second intervals and performs various callbacks,
+ * such as idle query timers, DNS resolution, connection backoff, etc. Each of
+ * the remote hosts that we obtain is wrapped up in an svp_conn_t, which manages
+ * the connection state, reconnecting, etc.
+ *
+ * All in all, the general way that this all looks like is:
+ *
+ * +----------------------------+
+ * | Plugin Instance |
+ * | svp_t |
+ * | |
+ * | varpd_provider_handle_t * -+-> varpd handle
+ * | uint64_t ----+-> varpd ID
+ * | char * ----+-> remote host
+ * | uint16_t ----+-> remote port
+ * | svp_remote_t * ---+------+-> remote backend
+ * +---------------------+------+
+ * |
+ * v
+ * +----------------------+ +----------------+
+ * | Remote backend |------------------>| Remove Backend |---> ...
+ * | svp_remote_t | | svp_remote_t |
+ * | | +----------------+
+ * | svp_remote_state_t --+-> state flags
+ * | svp_degrade_state_t -+-> degraded reason
+ * | struct addrinfo * --+-> resolved hosts
+ * | uint_t ---+-> active hosts
+ * | uint_t ---+-> DNS generation
+ * | uint_t ---+-> Reference count
+ * | uint_t ---+-> active conns
+ * | uint_t ---+-> degraded conns
+ * | list_t ---+---+-> connection list
+ * +------------------+---+
+ * |
+ * +------------------------------+-----------------+
+ * | | |
+ * v v v
+ * +-------------------+ +----------------
+ * | SVP Connection | | SVP connection | ...
+ * | svp_conn_t | | svp_conn_t |
+ * | | +----------------+
+ * | svp_event_t ----+-> event loop handle
+ * | svp_timer_t ----+-> backoff timer
+ * | svp_timer_t ----+-> query timer
+ * | int ----+-> socket fd
+ * | uint_t ----+-> generation
+ * | uint_t ----+-> current backoff
+ * | svp_conn_flags_t -+-> connection flags
+ * | svp_conn_state_t -+-> connection state
+ * | svp_conn_error_t -+-> connection error
+ * | int ---+-> last errrno
+ * | hrtime_t ---+-> activity timestamp
+ * | svp_conn_out_t ---+-> outgoing data state
+ * | svp_conn_in_t ---+-> incoming data state
+ * | list_t ---+--+-> active queries
+ * +----------------+--+
+ * |
+ * +----------------------------------+-----------------+
+ * | | |
+ * v v v
+ * +--------------------+ +-------------+
+ * | SVP Query | | SVP Query | ...
+ * | svp_query_t | | svp_query_t |
+ * | | +-------------+
+ * | svp_query_f ---+-> callback function
+ * | void * ---+-> callback arg
+ * | svp_query_state_t -+-> state flags
+ * | svp_req_t ---+-> svp prot. header
+ * | svp_query_data_t --+-> read data
+ * | svp_query_data_t --+-> write data
+ * | svp_status_t ---+-> request status
+ * +--------------------+
+ *
+ * The svp_t is the instance that we assoicate with varpd. The instance itself
+ * maintains properties and then when it's started associates with an
+ * svp_remote_t, which is the remote backend. The remote backend itself,
+ * maintains the DNS state and spins up and downs connections based on the
+ * results from DNS. By default, we query DNS every 30 seconds. For more on the
+ * connection life cycle, see the next section.
+ *
+ * By default, each connection maintains its own back off timer and list of
+ * queries it's servicing. Only one request is generally outstanding at a time
+ * and requests are round robined across the various connections.
+ *
+ * The query itself represents the svp request that's going on and keep track of
+ * its state and is a place for data that's read and written to as part of the
+ * request.
+ *
+ * Connections maintain a query timer such that if we have not received data on
+ * a socket for a certain amount of time, we kill that socket and begin a
+ * reconnection cycle with backoff.
+ *
+ * ------------------------
+ * Connection State Machine
+ * ------------------------
+ *
+ * We have a connection pool that's built upon DNS records. DNS describes the
+ * membership of the set of remote peers that make up our pool and we maintain
+ * one connection to each of them. In addition, we maintain an exponential
+ * backoff for each peer and will attempt to reconect immediately before backing
+ * off. The following are the valid states that a connection can be in:
+ *
+ * SVP_CS_ERROR An OS error has occurred on this connection,
+ * such as failure to create a socket or associate
+ * the socket with an event port. We also
+ * transition all connections to this state before
+ * we destroy them.
+ *
+ * SVP_CS_INITIAL This is the initial state of a connection, all
+ * that should exist is an unbound socket.
+ *
+ * SVP_CS_CONNECTING A call to connect has been made and we are
+ * polling for it to complete.
+ *
+ * SVP_CS_BACKOFF A connect attempt has failed and we are
+ * currently backing off, waiting to try again.
+ *
+ * SVP_CS_ACTIVE We have successfully connected to the remote
+ * system.
+ *
+ * SVP_CS_WINDDOWN This connection is going to valhalla. In other
+ * words, a previously active connection is no
+ * longer valid in DNS, so we should curb our use
+ * of it, and reap it as soon as we have other
+ * active connections.
+ *
+ * The following diagram attempts to describe our state transition scheme, and
+ * when we transition from one state to the next.
+ *
+ * |
+ * * New remote IP from DNS resolution,
+ * | not currently active in the system.
+ * |
+ * v Socket Error,
+ * +----------------+ still in DNS
+ * +----------------<---| SVP_CS_INITIAL |<----------------------*-----+
+ * | +----------------+ |
+ * | System | |
+ * | Connection . . . . . success * Successful |
+ * | failed . | connect() |
+ * | +----*---------+ | +-----------*--+ |
+ * | | | | | | |
+ * | V ^ v ^ V ^
+ * | +----------------+ +-------------------+ +---------------+
+ * +<-| SVP_CS_BACKOFF | | SVP_CS_CONNECTING | | SVP_CS_ACTIVE |
+ * | +----------------+ +-------------------+ +---------------+
+ * | V ^ V V V
+ * | Backoff wait * | | | * Removed
+ * v interval +--------------+ +-----------------<-----+ | from DNS
+ * | finished | |
+ * | V |
+ * | | V
+ * | | +-----------------+
+ * +----------------+----------<-----+-------<----| SVP_CS_WINDDOWN |
+ * | +-----------------+
+ * * . . . Fatal system, not
+ * | socket error or
+ * V quiesced after
+ * +--------------+ removal from DNS
+ * | SVP_CS_ERROR |
+ * +--------------+
+ * |
+ * * . . . Removed from DNS
+ * v
+ * +------------+
+ * | Connection |
+ * | Destroyed |
+ * +------------+
+ *
+ * --------------------------
+ * Connection Event Injection
+ * --------------------------
+ *
+ * For each connection that exists in the system, we have a timer in place that
+ * is in charge of performing timeout activity. It fires once every thirty
+ * seconds or so for a given connection and checks to ensure that we have had
+ * activity for the most recent query on the connection. If not, it terminates
+ * the connection. This is important as if we have sent all our data and are
+ * waiting for the remote end to reply, without enabling something like TCP
+ * keep-alive, we will not be notified that anything that has happened to the
+ * remote connection, for example a panic. In addition, this also protects
+ * against a server that is up, but a portolan that is not making forward
+ * progress.
+ *
+ * When a timeout occurs, we first try to disassociate any active events, which
+ * by definition must exist. Once that's done, we inject a port source user
+ * event. Now, there is a small gotcha. Let's assume for a moment that we have a
+ * pathological portolan. That means that it knows to inject activity right at
+ * the time out window. That means, that the event may be disassociated before
+ * we could get to it. If that's the case, we must _not_ inject the user event
+ * and instead, we'll let the pending event take care of it. We know that the
+ * pending event hasn't hit the main part of the loop yet, otherwise, it would
+ * have released the lock protecting our state and associated the event.
+ *
+ * ------------
+ * Notes on DNS
+ * ------------
+ *
+ * Unfortunately, doing host name resolution in a way that allows us to leverage
+ * the system's resolvers and the system's caching, require us to make blocking
+ * calls in libc via getaddrinfo(3SOCKET). If we can't reach a given server,
+ * that will tie up a thread for quite some time. To work around that fact,
+ * we're going to create a fixed number of threads and we'll use them to service
+ * our DNS requests. While this isn't ideal, until we have a sane means of
+ * integrating a DNS resolution into an event loop with say portfs, it's not
+ * going to be a fun day no matter what we do.
+ *
+ * ------
+ * Timers
+ * ------
+ *
+ * We maintain a single timer based on CLOCK_REALTIME. It's designed to fire
+ * every second. While we'd rather use CLOCK_HIGHRES just to alleviate ourselves
+ * from timer drift; however, as zones may not actually have CLOCK_HIGHRES
+ * access, we don't want them to end up in there. The timer itself is just a
+ * simple avl tree sorted by expiration time, which is stored as a tick in the
+ * future, a tick is just one second.
+ *
+ * ----------
+ * Shootdowns
+ * ----------
+ *
+ * As part of the protocol, we need to be able to handle shootdowns that inform
+ * us some of the information in the system is out of date. This information
+ * needs to be processed promptly; however, the information is hopefully going
+ * to be relatively infrequent relative to the normal flow of information.
+ *
+ * The shoot down information needs to be done on a per-backend basis. The
+ * general design is that we'll have a single query for this which can fire on a
+ * 5-10s period, we randmoize the latter part to give us a bit more load
+ * spreading. If we complete because there's no work to do, then we wait the
+ * normal period. If we complete, but there's still work to do, we'll go again
+ * after a second.
+ *
+ * A shootdown has a few different parts. We first receive a list of items to
+ * shootdown. After performing all of those, we need to acknowledge them. When
+ * that's been done successfully, we can move onto the next part. From a
+ * protocol perspective, we make a SVP_R_LOG_REQ, we get a reply, and then after
+ * processing them, send an SVP_R_LOG_RM. Only once that's been acked do we
+ * continue.
+ *
+ * However, one of the challenges that we have is that these invalidations are
+ * just that, an invalidation. For a virtual layer two request, that's fine,
+ * because the kernel supports that. However, for virtual layer three
+ * invalidations, we have a bit more work to do. These protocols, ARP and NDP,
+ * don't really support a notion of just an invalidation, instead you have to
+ * inject the new data in a gratuitous fashion.
+ *
+ * To that end, what we instead do is when we receive a VL3 invalidation, we
+ * turn that info a VL3 request. We hold the general request as outstanding
+ * until we receive all of the callbacks for the VL3 invalidations, at which
+ * point we go through and do the log removal request.
+ */
+
+#include <umem.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <libnvpair.h>
+#include <strings.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include <libvarpd_provider.h>
+#include "libvarpd_svp.h"
+
+bunyan_logger_t *svp_bunyan;
+static int svp_defport = 1296;
+static int svp_defuport = 1339;
+static umem_cache_t *svp_lookup_cache;
+
+typedef enum svp_lookup_type {
+ SVP_L_UNKNOWN = 0x0,
+ SVP_L_VL2 = 0x1,
+ SVP_L_VL3 = 0x2
+} svp_lookup_type_t;
+
+typedef struct svp_lookup {
+ int svl_type;
+ union {
+ struct svl_lookup_vl2 {
+ varpd_query_handle_t *svl_handle;
+ overlay_target_point_t *svl_point;
+ } svl_vl2;
+ struct svl_lookup_vl3 {
+ varpd_arp_handle_t *svl_vah;
+ uint8_t *svl_out;
+ } svl_vl3;
+ } svl_u;
+ svp_query_t svl_query;
+} svp_lookup_t;
+
+static const char *varpd_svp_props[] = {
+ "svp/host",
+ "svp/port",
+ "svp/underlay_ip",
+ "svp/underlay_port"
+};
+
+static const uint8_t svp_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+int
+svp_comparator(const void *l, const void *r)
+{
+ const svp_t *ls = l;
+ const svp_t *rs = r;
+
+ if (ls->svp_vid > rs->svp_vid)
+ return (1);
+ if (ls->svp_vid < rs->svp_vid)
+ return (-1);
+ return (0);
+}
+
+static void
+svp_vl2_lookup_cb(svp_t *svp, svp_status_t status, const struct in6_addr *uip,
+ const uint16_t uport, void *arg)
+{
+ svp_lookup_t *svl = arg;
+ overlay_target_point_t *otp;
+
+ assert(svp != NULL);
+ assert(arg != NULL);
+
+ if (status != SVP_S_OK) {
+ libvarpd_plugin_query_reply(svl->svl_u.svl_vl2.svl_handle,
+ VARPD_LOOKUP_DROP);
+ umem_cache_free(svp_lookup_cache, svl);
+ return;
+ }
+
+ otp = svl->svl_u.svl_vl2.svl_point;
+ bcopy(uip, &otp->otp_ip, sizeof (struct in6_addr));
+ otp->otp_port = uport;
+ libvarpd_plugin_query_reply(svl->svl_u.svl_vl2.svl_handle,
+ VARPD_LOOKUP_OK);
+ umem_cache_free(svp_lookup_cache, svl);
+}
+
+static void
+svp_vl3_lookup_cb(svp_t *svp, svp_status_t status, const uint8_t *vl2mac,
+ const struct in6_addr *uip, const uint16_t uport, void *arg)
+{
+ overlay_target_point_t point;
+ svp_lookup_t *svl = arg;
+
+ assert(svp != NULL);
+ assert(svl != NULL);
+
+ if (status != SVP_S_OK) {
+ libvarpd_plugin_arp_reply(svl->svl_u.svl_vl3.svl_vah,
+ VARPD_LOOKUP_DROP);
+ umem_cache_free(svp_lookup_cache, svl);
+ return;
+ }
+
+ /* Inject the L2 mapping before the L3 */
+ bcopy(uip, &point.otp_ip, sizeof (struct in6_addr));
+ point.otp_port = uport;
+ libvarpd_inject_varp(svp->svp_hdl, vl2mac, &point);
+
+ bcopy(vl2mac, svl->svl_u.svl_vl3.svl_out, ETHERADDRL);
+ libvarpd_plugin_arp_reply(svl->svl_u.svl_vl3.svl_vah,
+ VARPD_LOOKUP_OK);
+ umem_cache_free(svp_lookup_cache, svl);
+}
+
+static void
+svp_vl2_invalidate_cb(svp_t *svp, const uint8_t *vl2mac)
+{
+ libvarpd_inject_varp(svp->svp_hdl, vl2mac, NULL);
+}
+
+static void
+svp_vl3_inject_cb(svp_t *svp, const uint16_t vlan, const struct in6_addr *vl3ip,
+ const uint8_t *vl2mac, const uint8_t *targmac)
+{
+ struct in_addr v4;
+
+ /*
+ * At the moment we don't support any IPv6 related log entries, this
+ * will change soon as we develop a bit more of the IPv6 related
+ * infrastructure so we can properly test the injection.
+ */
+ if (IN6_IS_ADDR_V4MAPPED(vl3ip) == 0) {
+ return;
+ } else {
+ IN6_V4MAPPED_TO_INADDR(vl3ip, &v4);
+ if (targmac == NULL)
+ targmac = svp_bcast;
+ libvarpd_inject_arp(svp->svp_hdl, vlan, vl2mac, &v4, targmac);
+ }
+}
+
+/* ARGSUSED */
+static void
+svp_shootdown_cb(svp_t *svp, const uint8_t *vl2mac, const struct in6_addr *uip,
+ const uint16_t uport)
+{
+ /*
+ * We should probably do a conditional invlaidation here.
+ */
+ libvarpd_inject_varp(svp->svp_hdl, vl2mac, NULL);
+}
+
+static svp_cb_t svp_defops = {
+ svp_vl2_lookup_cb,
+ svp_vl3_lookup_cb,
+ svp_vl2_invalidate_cb,
+ svp_vl3_inject_cb,
+ svp_shootdown_cb
+};
+
+static boolean_t
+varpd_svp_valid_dest(overlay_plugin_dest_t dest)
+{
+ if (dest != (OVERLAY_PLUGIN_D_IP | OVERLAY_PLUGIN_D_PORT))
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+static int
+varpd_svp_create(varpd_provider_handle_t *hdl, void **outp,
+ overlay_plugin_dest_t dest)
+{
+ int ret;
+ svp_t *svp;
+
+ if (varpd_svp_valid_dest(dest) == B_FALSE)
+ return (ENOTSUP);
+
+ svp = umem_zalloc(sizeof (svp_t), UMEM_DEFAULT);
+ if (svp == NULL)
+ return (ENOMEM);
+
+ if ((ret = mutex_init(&svp->svp_lock, USYNC_THREAD | LOCK_ERRORCHECK,
+ NULL)) != 0) {
+ umem_free(svp, sizeof (svp_t));
+ return (ret);
+ }
+
+ svp->svp_port = svp_defport;
+ svp->svp_uport = svp_defuport;
+ svp->svp_cb = svp_defops;
+ svp->svp_hdl = hdl;
+ svp->svp_vid = libvarpd_plugin_vnetid(svp->svp_hdl);
+ *outp = svp;
+ return (0);
+}
+
+static int
+varpd_svp_start(void *arg)
+{
+ int ret;
+ svp_remote_t *srp;
+ svp_t *svp = arg;
+
+ mutex_enter(&svp->svp_lock);
+ if (svp->svp_host == NULL || svp->svp_port == 0 ||
+ svp->svp_huip == B_FALSE || svp->svp_uport == 0) {
+ mutex_exit(&svp->svp_lock);
+ return (EAGAIN);
+ }
+ mutex_exit(&svp->svp_lock);
+
+ if ((ret = svp_remote_find(svp->svp_host, svp->svp_port, &svp->svp_uip,
+ &srp)) != 0)
+ return (ret);
+
+ if ((ret = svp_remote_attach(srp, svp)) != 0) {
+ svp_remote_release(srp);
+ return (ret);
+ }
+
+ return (0);
+}
+
+static void
+varpd_svp_stop(void *arg)
+{
+ svp_t *svp = arg;
+
+ svp_remote_detach(svp);
+}
+
+static void
+varpd_svp_destroy(void *arg)
+{
+ svp_t *svp = arg;
+
+ if (svp->svp_host != NULL)
+ umem_free(svp->svp_host, strlen(svp->svp_host) + 1);
+
+ if (mutex_destroy(&svp->svp_lock) != 0)
+ libvarpd_panic("failed to destroy svp_t`svp_lock");
+
+ umem_free(svp, sizeof (svp_t));
+}
+
+static void
+varpd_svp_lookup(void *arg, varpd_query_handle_t *vqh,
+ const overlay_targ_lookup_t *otl, overlay_target_point_t *otp)
+{
+ svp_lookup_t *slp;
+ svp_t *svp = arg;
+
+ /*
+ * Check if this is something that we need to proxy, eg. arp or ndp.
+ */
+ if (otl->otl_sap == ETHERTYPE_ARP) {
+ libvarpd_plugin_proxy_arp(svp->svp_hdl, vqh, otl);
+ return;
+ }
+
+ if (otl->otl_dstaddr[0] == 0x33 &&
+ otl->otl_dstaddr[1] == 0x33) {
+ if (otl->otl_sap == ETHERTYPE_IPV6) {
+ libvarpd_plugin_proxy_ndp(svp->svp_hdl, vqh, otl);
+ } else {
+ libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
+ }
+ return;
+ }
+
+ /*
+ * Watch out for various multicast and broadcast addresses. We've
+ * already taken care of the IPv6 range above. Now we just need to
+ * handle broadcast and if the multicast bit is set, lowest bit of the
+ * first octet of the MAC, then we drop it now.
+ */
+ if (bcmp(otl->otl_dstaddr, svp_bcast, ETHERADDRL) == 0 ||
+ (otl->otl_dstaddr[0] & 0x01) == 0x01) {
+ libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
+ return;
+ }
+
+ /*
+ * If we have a failure to allocate memory for this, that's not good.
+ * However, telling the kernel to just drop this packet is much better
+ * than the alternative at this moment. At least we'll try again and we
+ * may have something more available to us in a little bit.
+ */
+ slp = umem_cache_alloc(svp_lookup_cache, UMEM_DEFAULT);
+ if (slp == NULL) {
+ libvarpd_plugin_query_reply(vqh, VARPD_LOOKUP_DROP);
+ return;
+ }
+
+ slp->svl_type = SVP_L_VL2;
+ slp->svl_u.svl_vl2.svl_handle = vqh;
+ slp->svl_u.svl_vl2.svl_point = otp;
+
+ svp_remote_vl2_lookup(svp, &slp->svl_query, otl->otl_dstaddr, slp);
+}
+
+/* ARGSUSED */
+static int
+varpd_svp_nprops(void *arg, uint_t *nprops)
+{
+ *nprops = sizeof (varpd_svp_props) / sizeof (char *);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+varpd_svp_propinfo(void *arg, uint_t propid, varpd_prop_handle_t *vph)
+{
+ switch (propid) {
+ case 0:
+ /* svp/host */
+ libvarpd_prop_set_name(vph, varpd_svp_props[0]);
+ libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW);
+ libvarpd_prop_set_type(vph, OVERLAY_PROP_T_STRING);
+ libvarpd_prop_set_nodefault(vph);
+ break;
+ case 1:
+ /* svp/port */
+ libvarpd_prop_set_name(vph, varpd_svp_props[1]);
+ libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW);
+ libvarpd_prop_set_type(vph, OVERLAY_PROP_T_UINT);
+ (void) libvarpd_prop_set_default(vph, &svp_defport,
+ sizeof (svp_defport));
+ libvarpd_prop_set_range_uint32(vph, 1, UINT16_MAX);
+ break;
+ case 2:
+ /* svp/underlay_ip */
+ libvarpd_prop_set_name(vph, varpd_svp_props[2]);
+ libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW);
+ libvarpd_prop_set_type(vph, OVERLAY_PROP_T_IP);
+ libvarpd_prop_set_nodefault(vph);
+ break;
+ case 3:
+ /* svp/underlay_port */
+ libvarpd_prop_set_name(vph, varpd_svp_props[3]);
+ libvarpd_prop_set_prot(vph, OVERLAY_PROP_PERM_RRW);
+ libvarpd_prop_set_type(vph, OVERLAY_PROP_T_UINT);
+ (void) libvarpd_prop_set_default(vph, &svp_defuport,
+ sizeof (svp_defuport));
+ libvarpd_prop_set_range_uint32(vph, 1, UINT16_MAX);
+ break;
+ default:
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+varpd_svp_getprop(void *arg, const char *pname, void *buf, uint32_t *sizep)
+{
+ svp_t *svp = arg;
+
+ /* svp/host */
+ if (strcmp(pname, varpd_svp_props[0]) == 0) {
+ size_t len;
+
+ mutex_enter(&svp->svp_lock);
+ if (svp->svp_host == NULL) {
+ *sizep = 0;
+ } else {
+ len = strlen(svp->svp_host) + 1;
+ if (*sizep < len) {
+ mutex_exit(&svp->svp_lock);
+ return (EOVERFLOW);
+ }
+ *sizep = len;
+ (void) strlcpy(buf, svp->svp_host, *sizep);
+ }
+ mutex_exit(&svp->svp_lock);
+ return (0);
+ }
+
+ /* svp/port */
+ if (strcmp(pname, varpd_svp_props[1]) == 0) {
+ uint64_t val;
+
+ if (*sizep < sizeof (uint64_t))
+ return (EOVERFLOW);
+
+ mutex_enter(&svp->svp_lock);
+ if (svp->svp_port == 0) {
+ *sizep = 0;
+ } else {
+ val = svp->svp_port;
+ bcopy(&val, buf, sizeof (uint64_t));
+ *sizep = sizeof (uint64_t);
+ }
+
+ mutex_exit(&svp->svp_lock);
+ return (0);
+ }
+
+ /* svp/underlay_ip */
+ if (strcmp(pname, varpd_svp_props[2]) == 0) {
+ if (*sizep > sizeof (struct in6_addr))
+ return (EOVERFLOW);
+ mutex_enter(&svp->svp_lock);
+ if (svp->svp_huip == B_FALSE) {
+ *sizep = 0;
+ } else {
+ bcopy(&svp->svp_uip, buf, sizeof (struct in6_addr));
+ *sizep = sizeof (struct in6_addr);
+ }
+ mutex_exit(&svp->svp_lock);
+ return (0);
+ }
+
+ /* svp/underlay_port */
+ if (strcmp(pname, varpd_svp_props[3]) == 0) {
+ uint64_t val;
+
+ if (*sizep < sizeof (uint64_t))
+ return (EOVERFLOW);
+
+ mutex_enter(&svp->svp_lock);
+ if (svp->svp_uport == 0) {
+ *sizep = 0;
+ } else {
+ val = svp->svp_uport;
+ bcopy(&val, buf, sizeof (uint64_t));
+ *sizep = sizeof (uint64_t);
+ }
+
+ mutex_exit(&svp->svp_lock);
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static int
+varpd_svp_setprop(void *arg, const char *pname, const void *buf,
+ const uint32_t size)
+{
+ svp_t *svp = arg;
+
+ /* svp/host */
+ if (strcmp(pname, varpd_svp_props[0]) == 0) {
+ char *dup;
+ dup = umem_alloc(size, UMEM_DEFAULT);
+ (void) strlcpy(dup, buf, size);
+ if (dup == NULL)
+ return (ENOMEM);
+ mutex_enter(&svp->svp_lock);
+ if (svp->svp_host != NULL)
+ umem_free(svp->svp_host, strlen(svp->svp_host) + 1);
+ svp->svp_host = dup;
+ mutex_exit(&svp->svp_lock);
+ return (0);
+ }
+
+ /* svp/port */
+ if (strcmp(pname, varpd_svp_props[1]) == 0) {
+ const uint64_t *valp = buf;
+ if (size < sizeof (uint64_t))
+ return (EOVERFLOW);
+
+ if (*valp == 0 || *valp > UINT16_MAX)
+ return (EINVAL);
+
+ mutex_enter(&svp->svp_lock);
+ svp->svp_port = (uint16_t)*valp;
+ mutex_exit(&svp->svp_lock);
+ return (0);
+ }
+
+ /* svp/underlay_ip */
+ if (strcmp(pname, varpd_svp_props[2]) == 0) {
+ const struct in6_addr *ipv6 = buf;
+
+ if (size < sizeof (struct in6_addr))
+ return (EOVERFLOW);
+
+ if (IN6_IS_ADDR_V4COMPAT(ipv6))
+ return (EINVAL);
+
+ if (IN6_IS_ADDR_MULTICAST(ipv6))
+ return (EINVAL);
+
+ if (IN6_IS_ADDR_6TO4(ipv6))
+ return (EINVAL);
+
+ if (IN6_IS_ADDR_V4MAPPED(ipv6)) {
+ ipaddr_t v4;
+ IN6_V4MAPPED_TO_IPADDR(ipv6, v4);
+ if (IN_MULTICAST(ntohl(v4)))
+ return (EINVAL);
+ }
+
+ mutex_enter(&svp->svp_lock);
+ bcopy(buf, &svp->svp_uip, sizeof (struct in6_addr));
+ svp->svp_huip = B_TRUE;
+ mutex_exit(&svp->svp_lock);
+ return (0);
+ }
+
+ /* svp/underlay_port */
+ if (strcmp(pname, varpd_svp_props[3]) == 0) {
+ const uint64_t *valp = buf;
+ if (size < sizeof (uint64_t))
+ return (EOVERFLOW);
+
+ if (*valp == 0 || *valp > UINT16_MAX)
+ return (EINVAL);
+
+ mutex_enter(&svp->svp_lock);
+ svp->svp_uport = (uint16_t)*valp;
+ mutex_exit(&svp->svp_lock);
+
+ return (0);
+ }
+
+ return (EINVAL);
+}
+
+static int
+varpd_svp_save(void *arg, nvlist_t *nvp)
+{
+ int ret;
+ svp_t *svp = arg;
+
+ mutex_enter(&svp->svp_lock);
+ if (svp->svp_host != NULL) {
+ if ((ret = nvlist_add_string(nvp, varpd_svp_props[0],
+ svp->svp_host)) != 0) {
+ mutex_exit(&svp->svp_lock);
+ return (ret);
+ }
+ }
+
+ if (svp->svp_port != 0) {
+ if ((ret = nvlist_add_uint16(nvp, varpd_svp_props[1],
+ svp->svp_port)) != 0) {
+ mutex_exit(&svp->svp_lock);
+ return (ret);
+ }
+ }
+
+ if (svp->svp_huip == B_TRUE) {
+ char buf[INET6_ADDRSTRLEN];
+
+ if (inet_ntop(AF_INET6, &svp->svp_uip, buf, sizeof (buf)) ==
+ NULL)
+ libvarpd_panic("unexpected inet_ntop failure: %d",
+ errno);
+
+ if ((ret = nvlist_add_string(nvp, varpd_svp_props[2],
+ buf)) != 0) {
+ mutex_exit(&svp->svp_lock);
+ return (ret);
+ }
+ }
+
+ if (svp->svp_uport != 0) {
+ if ((ret = nvlist_add_uint16(nvp, varpd_svp_props[3],
+ svp->svp_uport)) != 0) {
+ mutex_exit(&svp->svp_lock);
+ return (ret);
+ }
+ }
+
+ mutex_exit(&svp->svp_lock);
+ return (0);
+}
+
+static int
+varpd_svp_restore(nvlist_t *nvp, varpd_provider_handle_t *hdl,
+ overlay_plugin_dest_t dest, void **outp)
+{
+ int ret;
+ svp_t *svp;
+ char *ipstr, *hstr;
+
+ if (varpd_svp_valid_dest(dest) == B_FALSE)
+ return (ENOTSUP);
+
+ if ((ret = varpd_svp_create(hdl, (void **)&svp, dest)) != 0)
+ return (ret);
+
+ if ((ret = nvlist_lookup_string(nvp, varpd_svp_props[0],
+ &hstr)) != 0) {
+ if (ret != ENOENT) {
+ varpd_svp_destroy(svp);
+ return (ret);
+ }
+ svp->svp_host = NULL;
+ } else {
+ size_t blen = strlen(hstr) + 1;
+ svp->svp_host = umem_alloc(blen, UMEM_DEFAULT);
+ (void) strlcpy(svp->svp_host, hstr, blen);
+ }
+
+ if ((ret = nvlist_lookup_uint16(nvp, varpd_svp_props[1],
+ &svp->svp_port)) != 0) {
+ if (ret != ENOENT) {
+ varpd_svp_destroy(svp);
+ return (ret);
+ }
+ svp->svp_port = 0;
+ }
+
+ if ((ret = nvlist_lookup_string(nvp, varpd_svp_props[2],
+ &ipstr)) != 0) {
+ if (ret != ENOENT) {
+ varpd_svp_destroy(svp);
+ return (ret);
+ }
+ svp->svp_huip = B_FALSE;
+ } else {
+ ret = inet_pton(AF_INET6, ipstr, &svp->svp_uip);
+ if (ret == -1) {
+ assert(errno == EAFNOSUPPORT);
+ libvarpd_panic("unexpected inet_pton failure: %d",
+ errno);
+ }
+
+ if (ret == 0) {
+ varpd_svp_destroy(svp);
+ return (EINVAL);
+ }
+ svp->svp_huip = B_TRUE;
+ }
+
+ if ((ret = nvlist_lookup_uint16(nvp, varpd_svp_props[3],
+ &svp->svp_uport)) != 0) {
+ if (ret != ENOENT) {
+ varpd_svp_destroy(svp);
+ return (ret);
+ }
+ svp->svp_uport = 0;
+ }
+
+ svp->svp_hdl = hdl;
+ *outp = svp;
+ return (0);
+}
+
+static void
+varpd_svp_arp(void *arg, varpd_arp_handle_t *vah, int type,
+ const struct sockaddr *sock, uint8_t *out)
+{
+ svp_t *svp = arg;
+ svp_lookup_t *svl;
+
+ if (type != VARPD_QTYPE_ETHERNET) {
+ libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP);
+ return;
+ }
+
+ svl = umem_cache_alloc(svp_lookup_cache, UMEM_DEFAULT);
+ if (svl == NULL) {
+ libvarpd_plugin_arp_reply(vah, VARPD_LOOKUP_DROP);
+ return;
+ }
+
+ svl->svl_type = SVP_L_VL3;
+ svl->svl_u.svl_vl3.svl_vah = vah;
+ svl->svl_u.svl_vl3.svl_out = out;
+ svp_remote_vl3_lookup(svp, &svl->svl_query, sock, svl);
+}
+
+static const varpd_plugin_ops_t varpd_svp_ops = {
+ 0,
+ varpd_svp_create,
+ varpd_svp_start,
+ varpd_svp_stop,
+ varpd_svp_destroy,
+ NULL,
+ varpd_svp_lookup,
+ varpd_svp_nprops,
+ varpd_svp_propinfo,
+ varpd_svp_getprop,
+ varpd_svp_setprop,
+ varpd_svp_save,
+ varpd_svp_restore,
+ varpd_svp_arp,
+ NULL
+};
+
+static int
+svp_bunyan_init(void)
+{
+ int ret;
+
+ if ((ret = bunyan_init("svp", &svp_bunyan)) != 0)
+ return (ret);
+ ret = bunyan_stream_add(svp_bunyan, "stderr", BUNYAN_L_INFO,
+ bunyan_stream_fd, (void *)STDERR_FILENO);
+ if (ret != 0)
+ bunyan_fini(svp_bunyan);
+ return (ret);
+}
+
+static void
+svp_bunyan_fini(void)
+{
+ if (svp_bunyan != NULL)
+ bunyan_fini(svp_bunyan);
+}
+
+#pragma init(varpd_svp_init)
+static void
+varpd_svp_init(void)
+{
+ int err;
+ varpd_plugin_register_t *vpr;
+
+ if (svp_bunyan_init() != 0)
+ return;
+
+ if ((err = svp_host_init()) != 0) {
+ (void) bunyan_error(svp_bunyan, "failed to init host subsystem",
+ BUNYAN_T_INT32, "error", err,
+ BUNYAN_T_END);
+ svp_bunyan_fini();
+ return;
+ }
+
+ svp_lookup_cache = umem_cache_create("svp_lookup",
+ sizeof (svp_lookup_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
+ if (svp_lookup_cache == NULL) {
+ (void) bunyan_error(svp_bunyan,
+ "failed to create svp_lookup cache",
+ BUNYAN_T_INT32, "error", errno,
+ BUNYAN_T_END);
+ svp_bunyan_fini();
+ return;
+ }
+
+ if ((err = svp_event_init()) != 0) {
+ (void) bunyan_error(svp_bunyan,
+ "failed to init event subsystem",
+ BUNYAN_T_INT32, "error", err,
+ BUNYAN_T_END);
+ svp_bunyan_fini();
+ umem_cache_destroy(svp_lookup_cache);
+ return;
+ }
+
+ if ((err = svp_timer_init()) != 0) {
+ (void) bunyan_error(svp_bunyan,
+ "failed to init timer subsystem",
+ BUNYAN_T_INT32, "error", err,
+ BUNYAN_T_END);
+ svp_event_fini();
+ umem_cache_destroy(svp_lookup_cache);
+ svp_bunyan_fini();
+ return;
+ }
+
+ if ((err = svp_remote_init()) != 0) {
+ (void) bunyan_error(svp_bunyan,
+ "failed to init remote subsystem",
+ BUNYAN_T_INT32, "error", err,
+ BUNYAN_T_END);
+ svp_event_fini();
+ umem_cache_destroy(svp_lookup_cache);
+ svp_bunyan_fini();
+ return;
+ }
+
+ vpr = libvarpd_plugin_alloc(VARPD_CURRENT_VERSION, &err);
+ if (vpr == NULL) {
+ (void) bunyan_error(svp_bunyan,
+ "failed to alloc varpd plugin",
+ BUNYAN_T_INT32, "error", err,
+ BUNYAN_T_END);
+ svp_remote_fini();
+ svp_event_fini();
+ umem_cache_destroy(svp_lookup_cache);
+ svp_bunyan_fini();
+ return;
+ }
+
+ vpr->vpr_mode = OVERLAY_TARGET_DYNAMIC;
+ vpr->vpr_name = "svp";
+ vpr->vpr_ops = &varpd_svp_ops;
+
+ if ((err = libvarpd_plugin_register(vpr)) != 0) {
+ (void) bunyan_error(svp_bunyan,
+ "failed to register varpd plugin",
+ BUNYAN_T_INT32, "error", err,
+ BUNYAN_T_END);
+ svp_remote_fini();
+ svp_event_fini();
+ umem_cache_destroy(svp_lookup_cache);
+ svp_bunyan_fini();
+
+ }
+ libvarpd_plugin_free(vpr);
+}
diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp.h b/usr/src/lib/varpd/svp/common/libvarpd_svp.h
new file mode 100644
index 0000000000..8192b842ce
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/libvarpd_svp.h
@@ -0,0 +1,357 @@
+/*
+ * 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 _LIBVARPD_SVP_H
+#define _LIBVARPD_SVP_H
+
+/*
+ * Implementation details of the SVP plugin and the SVP protocol.
+ */
+
+#include <netinet/in.h>
+#include <sys/ethernet.h>
+#include <thread.h>
+#include <synch.h>
+#include <libvarpd_provider.h>
+#include <sys/avl.h>
+#include <port.h>
+#include <sys/list.h>
+#include <bunyan.h>
+
+#include <libvarpd_svp_prot.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct svp svp_t;
+typedef struct svp_remote svp_remote_t;
+typedef struct svp_conn svp_conn_t;
+typedef struct svp_query svp_query_t;
+
+typedef void (*svp_event_f)(port_event_t *, void *);
+
+typedef struct svp_event {
+ svp_event_f se_func;
+ void *se_arg;
+ int se_events;
+} svp_event_t;
+
+typedef void (*svp_timer_f)(void *);
+
+typedef struct svp_timer {
+ svp_timer_f st_func; /* Timer callback function */
+ void *st_arg; /* Timer callback arg */
+ boolean_t st_oneshot; /* Is timer a one shot? */
+ uint32_t st_value; /* periodic or one-shot time */
+ /* Fields below here are private to the svp_timer implementaiton */
+ uint64_t st_expire; /* Next expiration */
+ boolean_t st_delivering; /* Are we currently delivering this */
+ avl_node_t st_link;
+} svp_timer_t;
+
+/*
+ * Note, both the svp_log_ack_t and svp_lrm_req_t are not part of this structure
+ * as they are rather variable sized data and we don't want to constrain their
+ * size. Instead, the rdata and wdata members must be set appropriately.
+ */
+typedef union svp_query_data {
+ svp_vl2_req_t sqd_vl2r;
+ svp_vl2_ack_t sqd_vl2a;
+ svp_vl3_req_t sdq_vl3r;
+ svp_vl3_ack_t sdq_vl3a;
+ svp_log_req_t sdq_logr;
+ svp_lrm_ack_t sdq_lrma;
+} svp_query_data_t;
+
+typedef void (*svp_query_f)(svp_query_t *, void *);
+
+typedef enum svp_query_state {
+ SVP_QUERY_INIT = 0x00,
+ SVP_QUERY_WRITING = 0x01,
+ SVP_QUERY_READING = 0x02,
+ SVP_QUERY_FINISHED = 0x03
+} svp_query_state_t;
+
+/*
+ * The query structure is usable for all forms of svp queries that end up
+ * getting passed across. Right now it's optimized for the fixed size data
+ * requests as opposed to requests whose responses will always be streaming in
+ * nature. Though, the streaming requests are the less common ones we have. We
+ * may need to make additional changes for those.
+ */
+struct svp_query {
+ list_node_t sq_lnode; /* List entry */
+ svp_query_f sq_func; /* Callback function */
+ svp_query_state_t sq_state; /* Query state */
+ void *sq_arg; /* Callback function arg */
+ svp_t *sq_svp; /* Pointer back to svp_t */
+ svp_req_t sq_header; /* Header for the query */
+ svp_query_data_t sq_rdun; /* Union for read data */
+ svp_query_data_t sq_wdun; /* Union for write data */
+ svp_status_t sq_status; /* Query response status */
+ size_t sq_size; /* Query response size */
+ void *sq_rdata; /* Read data pointer */
+ size_t sq_rsize; /* Read data size */
+ void *sq_wdata; /* Write data pointer */
+ size_t sq_wsize; /* Write data size */
+ hrtime_t sq_acttime; /* Last I/O activity time */
+};
+
+typedef enum svp_conn_state {
+ SVP_CS_ERROR = 0x00,
+ SVP_CS_INITIAL = 0x01,
+ SVP_CS_CONNECTING = 0x02,
+ SVP_CS_BACKOFF = 0x03,
+ SVP_CS_ACTIVE = 0x04,
+ SVP_CS_WINDDOWN = 0x05
+} svp_conn_state_t;
+
+typedef enum svp_conn_error {
+ SVP_CE_NONE = 0x00,
+ SVP_CE_ASSOCIATE = 0x01,
+ SVP_CE_NOPOLLOUT = 0x02,
+ SVP_CE_SOCKET = 0x03
+} svp_conn_error_t;
+
+typedef enum svp_conn_flags {
+ SVP_CF_ADDED = 0x01,
+ SVP_CF_DEGRADED = 0x02,
+ SVP_CF_REAP = 0x04,
+ SVP_CF_TEARDOWN = 0x08,
+ SVP_CF_UFLAG = 0x0c,
+ SVP_CF_USER = 0x10
+} svp_conn_flags_t;
+
+typedef struct svp_conn_out {
+ svp_query_t *sco_query;
+ size_t sco_offset;
+} svp_conn_out_t;
+
+typedef struct svp_conn_in {
+ svp_query_t *sci_query;
+ svp_req_t sci_req;
+ size_t sci_offset;
+} svp_conn_in_t;
+
+struct svp_conn {
+ svp_remote_t *sc_remote; /* RO */
+ struct in6_addr sc_addr; /* RO */
+ list_node_t sc_rlist; /* svp_remote_t`sr_lock */
+ mutex_t sc_lock;
+ svp_event_t sc_event;
+ svp_timer_t sc_btimer;
+ svp_timer_t sc_qtimer;
+ int sc_socket;
+ uint_t sc_gen;
+ uint_t sc_nbackoff;
+ svp_conn_flags_t sc_flags;
+ svp_conn_state_t sc_cstate;
+ svp_conn_error_t sc_error;
+ int sc_errno;
+ list_t sc_queries;
+ svp_conn_out_t sc_output;
+ svp_conn_in_t sc_input;
+};
+
+typedef enum svp_remote_state {
+ SVP_RS_LOOKUP_SCHEDULED = 0x01, /* On the DNS Queue */
+ SVP_RS_LOOKUP_INPROGRESS = 0x02, /* Doing a DNS lookup */
+ SVP_RS_LOOKUP_VALID = 0x04 /* addrinfo valid */
+} svp_remote_state_t;
+
+/*
+ * These series of bit-based flags should be ordered such that the most severe
+ * is first. We only can set one message that user land can see, so if more than
+ * one is set we want to make sure that one is there.
+ */
+typedef enum svp_degrade_state {
+ SVP_RD_DNS_FAIL = 0x01, /* DNS Resolution Failure */
+ SVP_RD_REMOTE_FAIL = 0x02, /* cannot reach any remote peers */
+ SVP_RD_ALL = 0x03 /* Only suitable for restore */
+} svp_degrade_state_t;
+
+typedef enum svp_shootdown_flags {
+ SVP_SD_RUNNING = 0x01,
+ SVP_SD_QUIESCE = 0x02,
+ SVP_SD_DORM = 0x04
+} svp_shootdown_flags_t;
+
+/*
+ * There is a single svp_sdlog_t per svp_remote_t. It maintains its own lock and
+ * condition variables. See the big theory statement for more information on how
+ * it's used.
+ */
+typedef struct svp_sdlog {
+ mutex_t sdl_lock;
+ cond_t sdl_cond;
+ uint_t sdl_ref;
+ svp_timer_t sdl_timer;
+ svp_shootdown_flags_t sdl_flags;
+ svp_query_t sdl_query;
+ void *sdl_logack;
+ void *sdl_logrm;
+ void *sdl_remote;
+} svp_sdlog_t;
+
+struct svp_remote {
+ char *sr_hostname; /* RO */
+ uint16_t sr_rport; /* RO */
+ struct in6_addr sr_uip; /* RO */
+ avl_node_t sr_gnode; /* svp_remote_lock */
+ svp_remote_t *sr_nexthost; /* svp_host_lock */
+ mutex_t sr_lock;
+ cond_t sr_cond;
+ svp_remote_state_t sr_state;
+ svp_degrade_state_t sr_degrade;
+ struct addrinfo *sr_addrinfo;
+ avl_tree_t sr_tree;
+ uint_t sr_count; /* active count */
+ uint_t sr_gen;
+ uint_t sr_tconns; /* total conns + dconns */
+ uint_t sr_ndconns; /* number of degraded conns */
+ list_t sr_conns; /* all conns */
+ svp_sdlog_t sr_shoot;
+};
+
+/*
+ * We have a bunch of different things that we get back from the API at the
+ * plug-in layer. These include:
+ *
+ * o OOB Shootdowns
+ * o VL3->VL2 Lookups
+ * o VL2->UL3 Lookups
+ * o VL2 Log invalidations
+ * o VL3 Log injections
+ */
+typedef void (*svp_vl2_lookup_f)(svp_t *, svp_status_t, const struct in6_addr *,
+ const uint16_t, void *);
+typedef void (*svp_vl3_lookup_f)(svp_t *, svp_status_t, const uint8_t *,
+ const struct in6_addr *, const uint16_t, void *);
+typedef void (*svp_vl2_invalidation_f)(svp_t *, const uint8_t *);
+typedef void (*svp_vl3_inject_f)(svp_t *, const uint16_t,
+ const struct in6_addr *, const uint8_t *, const uint8_t *);
+typedef void (*svp_shootdown_f)(svp_t *, const uint8_t *,
+ const struct in6_addr *, const uint16_t uport);
+
+typedef struct svp_cb {
+ svp_vl2_lookup_f scb_vl2_lookup;
+ svp_vl3_lookup_f scb_vl3_lookup;
+ svp_vl2_invalidation_f scb_vl2_invalidate;
+ svp_vl3_inject_f scb_vl3_inject;
+ svp_shootdown_f scb_shootdown;
+} svp_cb_t;
+
+/*
+ * Core implementation structure.
+ */
+struct svp {
+ overlay_plugin_dest_t svp_dest; /* RO */
+ varpd_provider_handle_t *svp_hdl; /* RO */
+ svp_cb_t svp_cb; /* RO */
+ uint64_t svp_vid; /* RO */
+ avl_node_t svp_rlink; /* Owned by svp_remote */
+ svp_remote_t *svp_remote; /* RO iff started */
+ mutex_t svp_lock;
+ char *svp_host; /* svp_lock */
+ uint16_t svp_port; /* svp_lock */
+ uint16_t svp_uport; /* svp_lock */
+ boolean_t svp_huip; /* svp_lock */
+ struct in6_addr svp_uip; /* svp_lock */
+};
+
+extern bunyan_logger_t *svp_bunyan;
+
+extern int svp_remote_find(char *, uint16_t, struct in6_addr *,
+ svp_remote_t **);
+extern int svp_remote_attach(svp_remote_t *, svp_t *);
+extern void svp_remote_detach(svp_t *);
+extern void svp_remote_release(svp_remote_t *);
+extern void svp_remote_vl3_lookup(svp_t *, svp_query_t *,
+ const struct sockaddr *, void *);
+extern void svp_remote_vl2_lookup(svp_t *, svp_query_t *, const uint8_t *,
+ void *);
+
+/*
+ * Init functions
+ */
+extern int svp_remote_init(void);
+extern void svp_remote_fini(void);
+extern int svp_event_init(void);
+extern int svp_event_timer_init(svp_event_t *);
+extern void svp_event_fini(void);
+extern int svp_host_init(void);
+extern int svp_timer_init(void);
+
+/*
+ * Timers
+ */
+extern int svp_tickrate;
+extern void svp_timer_add(svp_timer_t *);
+extern void svp_timer_remove(svp_timer_t *);
+
+/*
+ * Event loop management
+ */
+extern int svp_event_associate(svp_event_t *, int);
+extern int svp_event_dissociate(svp_event_t *, int);
+extern int svp_event_inject(svp_event_t *);
+
+/*
+ * Connection manager
+ */
+extern int svp_conn_create(svp_remote_t *, const struct in6_addr *);
+extern void svp_conn_destroy(svp_conn_t *);
+extern void svp_conn_fallout(svp_conn_t *);
+extern void svp_conn_queue(svp_conn_t *, svp_query_t *);
+
+/*
+ * FMA related
+ */
+extern void svp_remote_degrade(svp_remote_t *, svp_degrade_state_t);
+extern void svp_remote_restore(svp_remote_t *, svp_degrade_state_t);
+
+/*
+ * Misc.
+ */
+extern int svp_comparator(const void *, const void *);
+extern void svp_remote_reassign(svp_remote_t *, svp_conn_t *);
+extern void svp_remote_resolved(svp_remote_t *, struct addrinfo *);
+extern void svp_host_queue(svp_remote_t *);
+extern void svp_query_release(svp_query_t *);
+extern void svp_query_crc32(svp_req_t *, void *, size_t);
+
+/*
+ * Shootdown related
+ */
+extern void svp_remote_shootdown_vl3(svp_remote_t *, svp_log_vl3_t *,
+ svp_sdlog_t *);
+extern void svp_remote_shootdown_vl2(svp_remote_t *, svp_log_vl2_t *);
+extern void svp_remote_log_request(svp_remote_t *, svp_query_t *, void *,
+ size_t);
+extern void svp_remote_lrm_request(svp_remote_t *, svp_query_t *, void *,
+ size_t);
+extern void svp_shootdown_logr_cb(svp_remote_t *, svp_status_t, void *, size_t);
+extern void svp_shootdown_lrm_cb(svp_remote_t *, svp_status_t);
+extern void svp_shootdown_vl3_cb(svp_status_t, svp_log_vl3_t *, svp_sdlog_t *);
+extern int svp_shootdown_init(svp_remote_t *);
+extern void svp_shootdown_fini(svp_remote_t *);
+extern void svp_shootdown_start(svp_remote_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBVARPD_SVP_H */
diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_conn.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_conn.c
new file mode 100644
index 0000000000..4d10d1dba4
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_conn.c
@@ -0,0 +1,1030 @@
+/*
+ * 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.
+ */
+
+/*
+ * Logic to manage an individual connection to a remote host.
+ *
+ * For more information, see the big theory statement in
+ * lib/varpd/svp/common/libvarpd_svp.c.
+ */
+
+#include <assert.h>
+#include <umem.h>
+#include <errno.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/uio.h>
+#include <sys/debug.h>
+
+#include <libvarpd_svp.h>
+
+int svp_conn_query_timeout = 30;
+static int svp_conn_backoff_tbl[] = { 1, 2, 4, 8, 16, 32 };
+static int svp_conn_nbackoff = sizeof (svp_conn_backoff_tbl) / sizeof (int);
+
+typedef enum svp_conn_act {
+ SVP_RA_NONE = 0x00,
+ SVP_RA_DEGRADE = 0x01,
+ SVP_RA_RESTORE = 0x02,
+ SVP_RA_ERROR = 0x03,
+ SVP_RA_CLEANUP = 0x04
+} svp_conn_act_t;
+
+static void
+svp_conn_inject(svp_conn_t *scp)
+{
+ int ret;
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ if (scp->sc_flags & SVP_CF_USER)
+ return;
+ scp->sc_flags |= SVP_CF_USER;
+ if ((ret = svp_event_inject(&scp->sc_event)) != 0)
+ libvarpd_panic("failed to inject event: %d\n", ret);
+}
+
+static void
+svp_conn_degrade(svp_conn_t *scp)
+{
+ svp_remote_t *srp = scp->sc_remote;
+
+ assert(MUTEX_HELD(&srp->sr_lock));
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ if (scp->sc_flags & SVP_CF_DEGRADED)
+ return;
+
+ scp->sc_flags |= SVP_CF_DEGRADED;
+ srp->sr_ndconns++;
+ if (srp->sr_ndconns == srp->sr_tconns)
+ svp_remote_degrade(srp, SVP_RD_REMOTE_FAIL);
+}
+
+static void
+svp_conn_restore(svp_conn_t *scp)
+{
+ svp_remote_t *srp = scp->sc_remote;
+
+ assert(MUTEX_HELD(&srp->sr_lock));
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ if (!(scp->sc_flags & SVP_CF_DEGRADED))
+ return;
+
+ scp->sc_flags &= ~SVP_CF_DEGRADED;
+ if (srp->sr_ndconns == srp->sr_tconns)
+ svp_remote_restore(srp, SVP_RD_REMOTE_FAIL);
+ srp->sr_ndconns--;
+}
+
+static void
+svp_conn_add(svp_conn_t *scp)
+{
+ svp_remote_t *srp = scp->sc_remote;
+
+ assert(MUTEX_HELD(&srp->sr_lock));
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ if (scp->sc_flags & SVP_CF_ADDED)
+ return;
+
+ list_insert_tail(&srp->sr_conns, scp);
+ scp->sc_flags |= SVP_CF_ADDED;
+ srp->sr_tconns++;
+}
+
+static void
+svp_conn_remove(svp_conn_t *scp)
+{
+ svp_remote_t *srp = scp->sc_remote;
+
+ assert(MUTEX_HELD(&srp->sr_lock));
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ if (!(scp->sc_flags & SVP_CF_ADDED))
+ return;
+
+ scp->sc_flags &= ~SVP_CF_ADDED;
+ if (scp->sc_flags & SVP_CF_DEGRADED)
+ srp->sr_ndconns--;
+ srp->sr_tconns--;
+ if (srp->sr_tconns == srp->sr_ndconns)
+ svp_remote_degrade(srp, SVP_RD_REMOTE_FAIL);
+}
+
+static svp_query_t *
+svp_conn_query_find(svp_conn_t *scp, uint32_t id)
+{
+ svp_query_t *sqp;
+
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ for (sqp = list_head(&scp->sc_queries); sqp != NULL;
+ sqp = list_next(&scp->sc_queries, sqp)) {
+ if (sqp->sq_header.svp_id == id)
+ break;
+ }
+
+ return (sqp);
+}
+
+static svp_conn_act_t
+svp_conn_backoff(svp_conn_t *scp)
+{
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ if (close(scp->sc_socket) != 0)
+ libvarpd_panic("failed to close socket %d: %d\n",
+ scp->sc_socket, errno);
+ scp->sc_socket = -1;
+
+ scp->sc_cstate = SVP_CS_BACKOFF;
+ scp->sc_nbackoff++;
+ if (scp->sc_nbackoff >= svp_conn_nbackoff) {
+ scp->sc_btimer.st_value =
+ svp_conn_backoff_tbl[svp_conn_nbackoff - 1];
+ } else {
+ scp->sc_btimer.st_value =
+ svp_conn_backoff_tbl[scp->sc_nbackoff - 1];
+ }
+ svp_timer_add(&scp->sc_btimer);
+
+ if (scp->sc_nbackoff > svp_conn_nbackoff)
+ return (SVP_RA_DEGRADE);
+ return (SVP_RA_NONE);
+}
+
+static svp_conn_act_t
+svp_conn_connect(svp_conn_t *scp)
+{
+ int ret;
+ struct sockaddr_in6 in6;
+
+ assert(MUTEX_HELD(&scp->sc_lock));
+ assert(scp->sc_cstate == SVP_CS_BACKOFF ||
+ scp->sc_cstate == SVP_CS_INITIAL);
+ assert(scp->sc_socket == -1);
+ if (scp->sc_cstate == SVP_CS_INITIAL)
+ scp->sc_nbackoff = 0;
+
+ scp->sc_socket = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ if (scp->sc_socket == -1) {
+ scp->sc_error = SVP_CE_SOCKET;
+ scp->sc_errno = errno;
+ scp->sc_cstate = SVP_CS_ERROR;
+ return (SVP_RA_DEGRADE);
+ }
+
+ bzero(&in6, sizeof (struct sockaddr_in6));
+ in6.sin6_family = AF_INET6;
+ in6.sin6_port = htons(scp->sc_remote->sr_rport);
+ bcopy(&scp->sc_addr, &in6.sin6_addr, sizeof (struct in6_addr));
+ ret = connect(scp->sc_socket, (struct sockaddr *)&in6,
+ sizeof (struct sockaddr_in6));
+ if (ret != 0) {
+ boolean_t async = B_FALSE;
+
+ switch (errno) {
+ case EACCES:
+ case EADDRINUSE:
+ case EAFNOSUPPORT:
+ case EALREADY:
+ case EBADF:
+ case EISCONN:
+ case ELOOP:
+ case ENOENT:
+ case ENOSR:
+ case EWOULDBLOCK:
+ libvarpd_panic("unanticipated connect errno %d", errno);
+ break;
+ case EINPROGRESS:
+ case EINTR:
+ async = B_TRUE;
+ default:
+ break;
+ }
+
+ /*
+ * So, we will be connecting to this in the future, advance our
+ * state and make sure that we poll for the next round.
+ */
+ if (async == B_TRUE) {
+ scp->sc_cstate = SVP_CS_CONNECTING;
+ scp->sc_event.se_events = POLLOUT | POLLHUP;
+ ret = svp_event_associate(&scp->sc_event,
+ scp->sc_socket);
+ if (ret == 0)
+ return (SVP_RA_NONE);
+ scp->sc_error = SVP_CE_ASSOCIATE;
+ scp->sc_errno = ret;
+ scp->sc_cstate = SVP_CS_ERROR;
+ return (SVP_RA_DEGRADE);
+ } else {
+ /*
+ * This call failed, which means that we obtained one of
+ * the following:
+ *
+ * EADDRNOTAVAIL
+ * ECONNREFUSED
+ * EIO
+ * ENETUNREACH
+ * EHOSTUNREACH
+ * ENXIO
+ * ETIMEDOUT
+ *
+ * Therefore we need to set ourselves into backoff and
+ * wait for that to clear up.
+ */
+ return (svp_conn_backoff(scp));
+ }
+ }
+
+ /*
+ * We've connected. Successfully move ourselves to the bound
+ * state and start polling.
+ */
+ scp->sc_cstate = SVP_CS_ACTIVE;
+ scp->sc_event.se_events = POLLIN | POLLRDNORM | POLLHUP;
+ ret = svp_event_associate(&scp->sc_event, scp->sc_socket);
+ if (ret == 0)
+ return (SVP_RA_RESTORE);
+ scp->sc_error = SVP_CE_ASSOCIATE;
+ scp->sc_cstate = SVP_CS_ERROR;
+
+ return (SVP_RA_DEGRADE);
+}
+
+/*
+ * This should be the first call we get after a connect. If we have successfully
+ * connected, we should see a writeable event. We may also see an error or a
+ * hang up. In either of these cases, we transition to error mode. If there is
+ * also a readable event, we ignore it at the moment and just let a
+ * reassociation pick it up so we can simplify the set of state transitions that
+ * we have.
+ */
+static svp_conn_act_t
+svp_conn_poll_connect(port_event_t *pe, svp_conn_t *scp)
+{
+ int ret, err;
+ socklen_t sl = sizeof (err);
+ if (!(pe->portev_events & POLLOUT)) {
+ scp->sc_errno = 0;
+ scp->sc_error = SVP_CE_NOPOLLOUT;
+ scp->sc_cstate = SVP_CS_ERROR;
+ return (SVP_RA_DEGRADE);
+ }
+
+ ret = getsockopt(scp->sc_socket, SOL_SOCKET, SO_ERROR, &err, &sl);
+ if (ret != 0)
+ libvarpd_panic("unanticipated getsockopt error");
+ if (err != 0) {
+ return (svp_conn_backoff(scp));
+ }
+
+ scp->sc_cstate = SVP_CS_ACTIVE;
+ scp->sc_event.se_events = POLLIN | POLLRDNORM | POLLHUP;
+ ret = svp_event_associate(&scp->sc_event, scp->sc_socket);
+ if (ret == 0)
+ return (SVP_RA_RESTORE);
+ scp->sc_error = SVP_CE_ASSOCIATE;
+ scp->sc_errno = ret;
+ scp->sc_cstate = SVP_CS_ERROR;
+ return (SVP_RA_DEGRADE);
+}
+
+static svp_conn_act_t
+svp_conn_pollout(svp_conn_t *scp)
+{
+ svp_query_t *sqp;
+ svp_req_t *req;
+ size_t off;
+ struct iovec iov[2];
+ int nvecs = 0;
+ ssize_t ret;
+
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ /*
+ * We need to find a query and start writing it out.
+ */
+ if (scp->sc_output.sco_query == NULL) {
+ for (sqp = list_head(&scp->sc_queries); sqp != NULL;
+ sqp = list_next(&scp->sc_queries, sqp)) {
+ if (sqp->sq_state != SVP_QUERY_INIT)
+ continue;
+ break;
+ }
+
+ if (sqp == NULL) {
+ scp->sc_event.se_events &= ~POLLOUT;
+ return (SVP_RA_NONE);
+ }
+
+ scp->sc_output.sco_query = sqp;
+ scp->sc_output.sco_offset = 0;
+ sqp->sq_state = SVP_QUERY_WRITING;
+ svp_query_crc32(&sqp->sq_header, sqp->sq_rdata, sqp->sq_rsize);
+ }
+
+ sqp = scp->sc_output.sco_query;
+ req = &sqp->sq_header;
+ off = scp->sc_output.sco_offset;
+ if (off < sizeof (svp_req_t)) {
+ iov[nvecs].iov_base = (void *)((uintptr_t)req + off);
+ iov[nvecs].iov_len = sizeof (svp_req_t) - off;
+ nvecs++;
+ off = 0;
+ } else {
+ off -= sizeof (svp_req_t);
+ }
+
+ iov[nvecs].iov_base = (void *)((uintptr_t)sqp->sq_rdata + off);
+ iov[nvecs].iov_len = sqp->sq_rsize - off;
+ nvecs++;
+
+ do {
+ ret = writev(scp->sc_socket, iov, nvecs);
+ } while (ret == -1 && errno == EAGAIN);
+ if (ret == -1) {
+ switch (errno) {
+ case EAGAIN:
+ scp->sc_event.se_events |= POLLOUT;
+ return (SVP_RA_NONE);
+ case EIO:
+ case ENXIO:
+ case ECONNRESET:
+ return (SVP_RA_ERROR);
+ default:
+ libvarpd_panic("unexpected errno: %d", errno);
+ }
+ }
+
+ sqp->sq_acttime = gethrtime();
+ scp->sc_output.sco_offset += ret;
+ if (ret >= sizeof (svp_req_t) + sqp->sq_rsize) {
+ sqp->sq_state = SVP_QUERY_READING;
+ scp->sc_output.sco_query = NULL;
+ scp->sc_output.sco_offset = 0;
+ scp->sc_event.se_events |= POLLOUT;
+ }
+ return (SVP_RA_NONE);
+}
+
+static boolean_t
+svp_conn_pollin_validate(svp_conn_t *scp)
+{
+ svp_query_t *sqp;
+ uint32_t nsize;
+ uint16_t nvers, nop;
+ svp_req_t *resp = &scp->sc_input.sci_req;
+
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ nvers = ntohs(resp->svp_ver);
+ nop = ntohs(resp->svp_op);
+ nsize = ntohl(resp->svp_size);
+
+ if (nvers != SVP_CURRENT_VERSION) {
+ (void) bunyan_warn(svp_bunyan, "unsupported version",
+ BUNYAN_T_IP, "remote_ip", &scp->sc_addr,
+ BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport,
+ BUNYAN_T_INT32, "version", nvers,
+ BUNYAN_T_INT32, "operation", nop,
+ BUNYAN_T_INT32, "response_id", resp->svp_id,
+ BUNYAN_T_END);
+ return (B_FALSE);
+ }
+
+ if (nop != SVP_R_VL2_ACK && nop != SVP_R_VL3_ACK &&
+ nop != SVP_R_LOG_ACK && nop != SVP_R_LOG_RM_ACK) {
+ (void) bunyan_warn(svp_bunyan, "unsupported operation",
+ BUNYAN_T_IP, "remote_ip", &scp->sc_addr,
+ BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport,
+ BUNYAN_T_INT32, "version", nvers,
+ BUNYAN_T_INT32, "operation", nop,
+ BUNYAN_T_INT32, "response_id", resp->svp_id,
+ BUNYAN_T_END);
+ return (B_FALSE);
+ }
+
+ sqp = svp_conn_query_find(scp, resp->svp_id);
+ if (sqp == NULL) {
+ (void) bunyan_warn(svp_bunyan, "unknown response id",
+ BUNYAN_T_IP, "remote_ip", &scp->sc_addr,
+ BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport,
+ BUNYAN_T_INT32, "version", nvers,
+ BUNYAN_T_INT32, "operation", nop,
+ BUNYAN_T_INT32, "response_id", resp->svp_id,
+ BUNYAN_T_END);
+ return (B_FALSE);
+ }
+
+ if (sqp->sq_state != SVP_QUERY_READING) {
+ (void) bunyan_warn(svp_bunyan,
+ "got response for unexpecting query",
+ BUNYAN_T_IP, "remote_ip", &scp->sc_addr,
+ BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport,
+ BUNYAN_T_INT32, "version", nvers,
+ BUNYAN_T_INT32, "operation", nop,
+ BUNYAN_T_INT32, "response_id", resp->svp_id,
+ BUNYAN_T_INT32, "query_state", sqp->sq_state,
+ BUNYAN_T_END);
+ return (B_FALSE);
+ }
+
+ if ((nop == SVP_R_VL2_ACK && nsize != sizeof (svp_vl2_ack_t)) ||
+ (nop == SVP_R_VL3_ACK && nsize != sizeof (svp_vl3_ack_t)) ||
+ (nop == SVP_R_LOG_RM_ACK && nsize != sizeof (svp_lrm_ack_t))) {
+ (void) bunyan_warn(svp_bunyan, "response size too large",
+ BUNYAN_T_IP, "remote_ip", &scp->sc_addr,
+ BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport,
+ BUNYAN_T_INT32, "version", nvers,
+ BUNYAN_T_INT32, "operation", nop,
+ BUNYAN_T_INT32, "response_id", resp->svp_id,
+ BUNYAN_T_INT32, "response_size", nsize,
+ BUNYAN_T_INT32, "expected_size", nop == SVP_R_VL2_ACK ?
+ sizeof (svp_vl2_ack_t) : sizeof (svp_vl3_ack_t),
+ BUNYAN_T_INT32, "query_state", sqp->sq_state,
+ BUNYAN_T_END);
+ return (B_FALSE);
+ }
+
+ /*
+ * The valid size is anything <= to what the user requested, but at
+ * least svp_log_ack_t bytes large.
+ */
+ if (nop == SVP_R_LOG_ACK) {
+ const char *msg = NULL;
+ if (nsize < sizeof (svp_log_ack_t))
+ msg = "response size too small";
+ else if (nsize > ((svp_log_req_t *)sqp->sq_rdata)->svlr_count)
+ msg = "response size too large";
+ if (msg != NULL) {
+ (void) bunyan_warn(svp_bunyan, msg,
+ BUNYAN_T_IP, "remote_ip", &scp->sc_addr,
+ BUNYAN_T_INT32, "remote_port",
+ scp->sc_remote->sr_rport,
+ BUNYAN_T_INT32, "version", nvers,
+ BUNYAN_T_INT32, "operation", nop,
+ BUNYAN_T_INT32, "response_id", resp->svp_id,
+ BUNYAN_T_INT32, "response_size", nsize,
+ BUNYAN_T_INT32, "expected_size",
+ ((svp_log_req_t *)sqp->sq_rdata)->svlr_count,
+ BUNYAN_T_INT32, "query_state", sqp->sq_state,
+ BUNYAN_T_END);
+ return (B_FALSE);
+ }
+ }
+
+ sqp->sq_size = nsize;
+ scp->sc_input.sci_query = sqp;
+ if (nop == SVP_R_VL2_ACK || nop == SVP_R_VL3_ACK ||
+ nop == SVP_R_LOG_RM_ACK) {
+ sqp->sq_wdata = &sqp->sq_wdun;
+ sqp->sq_wsize = sizeof (svp_query_data_t);
+ } else {
+ VERIFY(nop == SVP_R_LOG_ACK);
+ assert(sqp->sq_wdata != NULL);
+ assert(sqp->sq_wsize != 0);
+ }
+
+ return (B_TRUE);
+}
+
+static svp_conn_act_t
+svp_conn_pollin(svp_conn_t *scp)
+{
+ size_t off, total;
+ ssize_t ret;
+ svp_query_t *sqp;
+ uint32_t crc;
+ uint16_t nop;
+
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ /*
+ * No query implies that we're reading in the header and that the offset
+ * is associted with it.
+ */
+ off = scp->sc_input.sci_offset;
+ sqp = scp->sc_input.sci_query;
+ if (scp->sc_input.sci_query == NULL) {
+ svp_req_t *resp = &scp->sc_input.sci_req;
+
+ assert(off < sizeof (svp_req_t));
+
+ do {
+ ret = read(scp->sc_socket,
+ (void *)((uintptr_t)resp + off),
+ sizeof (svp_req_t) - off);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1) {
+ switch (errno) {
+ case EAGAIN:
+ scp->sc_event.se_events |= POLLIN | POLLRDNORM;
+ return (SVP_RA_NONE);
+ case EIO:
+ case ECONNRESET:
+ return (SVP_RA_ERROR);
+ break;
+ default:
+ libvarpd_panic("unexpeted read errno: %d",
+ errno);
+ }
+ } else if (ret == 0) {
+ /* Try to reconnect to the remote host */
+ return (SVP_RA_ERROR);
+ }
+
+ /* Didn't get all the data we need */
+ if (off + ret < sizeof (svp_req_t)) {
+ scp->sc_input.sci_offset += ret;
+ scp->sc_event.se_events |= POLLIN | POLLRDNORM;
+ return (SVP_RA_NONE);
+ }
+
+ if (svp_conn_pollin_validate(scp) != B_TRUE)
+ return (SVP_RA_ERROR);
+ }
+
+ sqp = scp->sc_input.sci_query;
+ assert(sqp != NULL);
+ sqp->sq_acttime = gethrtime();
+ total = ntohl(scp->sc_input.sci_req.svp_size);
+ do {
+ ret = read(scp->sc_socket,
+ (void *)((uintptr_t)sqp->sq_wdata + off),
+ total - off);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret == -1) {
+ switch (errno) {
+ case EAGAIN:
+ scp->sc_event.se_events |= POLLIN | POLLRDNORM;
+ return (SVP_RA_NONE);
+ case EIO:
+ case ECONNRESET:
+ return (SVP_RA_ERROR);
+ break;
+ default:
+ libvarpd_panic("unexpeted read errno: %d", errno);
+ }
+ } else if (ret == 0) {
+ /* Try to reconnect to the remote host */
+ return (SVP_RA_ERROR);
+ }
+
+ if (ret + off < total) {
+ scp->sc_input.sci_offset += ret;
+ return (SVP_RA_NONE);
+ }
+
+ nop = ntohs(scp->sc_input.sci_req.svp_op);
+ crc = scp->sc_input.sci_req.svp_crc32;
+ svp_query_crc32(&scp->sc_input.sci_req, sqp->sq_wdata, total);
+ if (crc != scp->sc_input.sci_req.svp_crc32) {
+ (void) bunyan_info(svp_bunyan, "crc32 mismatch",
+ BUNYAN_T_IP, "remote ip", &scp->sc_addr,
+ BUNYAN_T_INT32, "remote port", scp->sc_remote->sr_rport,
+ BUNYAN_T_INT32, "version",
+ ntohs(scp->sc_input.sci_req.svp_ver),
+ BUNYAN_T_INT32, "operation", nop,
+ BUNYAN_T_INT32, "response id",
+ ntohl(scp->sc_input.sci_req.svp_id),
+ BUNYAN_T_INT32, "query state", sqp->sq_state,
+ BUNYAN_T_UINT32, "msg_crc", ntohl(crc),
+ BUNYAN_T_UINT32, "calc_crc",
+ ntohl(scp->sc_input.sci_req.svp_crc32),
+ BUNYAN_T_END);
+ return (SVP_RA_ERROR);
+ }
+ scp->sc_input.sci_query = NULL;
+ scp->sc_input.sci_offset = 0;
+
+ if (nop == SVP_R_VL2_ACK) {
+ svp_vl2_ack_t *sl2a = sqp->sq_wdata;
+ sqp->sq_status = ntohl(sl2a->sl2a_status);
+ } else if (nop == SVP_R_VL3_ACK) {
+ svp_vl3_ack_t *sl3a = sqp->sq_wdata;
+ sqp->sq_status = ntohl(sl3a->sl3a_status);
+ } else if (nop == SVP_R_LOG_ACK) {
+ svp_log_ack_t *svla = sqp->sq_wdata;
+ sqp->sq_status = ntohl(svla->svla_status);
+ } else if (nop == SVP_R_LOG_RM_ACK) {
+ svp_lrm_ack_t *svra = sqp->sq_wdata;
+ sqp->sq_status = ntohl(svra->svra_status);
+ } else {
+ libvarpd_panic("unhandled nop: %d", nop);
+ }
+
+ list_remove(&scp->sc_queries, sqp);
+ mutex_exit(&scp->sc_lock);
+
+ /*
+ * We have to release all of our resources associated with this entry
+ * before we call the callback. After we call it, the memory will be
+ * lost to time.
+ */
+ svp_query_release(sqp);
+ sqp->sq_func(sqp, sqp->sq_arg);
+ mutex_enter(&scp->sc_lock);
+ scp->sc_event.se_events |= POLLIN | POLLRDNORM;
+
+ return (SVP_RA_NONE);
+}
+
+static svp_conn_act_t
+svp_conn_reset(svp_conn_t *scp)
+{
+ svp_remote_t *srp = scp->sc_remote;
+
+ assert(MUTEX_HELD(&srp->sr_lock));
+ assert(MUTEX_HELD(&scp->sc_lock));
+
+ assert(svp_event_dissociate(&scp->sc_event, scp->sc_socket) ==
+ ENOENT);
+ if (close(scp->sc_socket) != 0)
+ libvarpd_panic("failed to close socket %d: %d", scp->sc_socket,
+ errno);
+ scp->sc_flags &= ~SVP_CF_TEARDOWN;
+ scp->sc_socket = -1;
+ scp->sc_cstate = SVP_CS_INITIAL;
+ scp->sc_input.sci_query = NULL;
+ scp->sc_output.sco_query = NULL;
+
+ svp_remote_reassign(srp, scp);
+
+ return (svp_conn_connect(scp));
+}
+
+/*
+ * This is our general state transition function. We're called here when we want
+ * to advance part of our state machine as well as to re-arm ourselves. We can
+ * also end up here from the standard event loop as a result of having a user
+ * event posted.
+ */
+static void
+svp_conn_handler(port_event_t *pe, void *arg)
+{
+ svp_conn_t *scp = arg;
+ svp_remote_t *srp = scp->sc_remote;
+ svp_conn_act_t ret = SVP_RA_NONE;
+ svp_conn_state_t oldstate;
+
+ mutex_enter(&scp->sc_lock);
+
+ /*
+ * Check if one of our event interrupts is set. An event interrupt, such
+ * as having to be reaped or be torndown is notified by a
+ * PORT_SOURCE_USER event that tries to take care of this. However,
+ * because of the fact that the event loop can be ongoing despite this,
+ * we may get here before the PORT_SOURCE_USER has casued us to get
+ * here. In such a case, if the PORT_SOURCE_USER event is tagged, then
+ * we're going to opt to do nothing here and wait for it to come and
+ * tear us down. That will also indicate to us that we have nothing to
+ * worry about as far as general timing and the like goes.
+ */
+ if ((scp->sc_flags & SVP_CF_UFLAG) != 0 &&
+ (scp->sc_flags & SVP_CF_USER) != 0 &&
+ pe != NULL &&
+ pe->portev_source != PORT_SOURCE_USER) {
+ mutex_exit(&scp->sc_lock);
+ return;
+ }
+
+ if (pe != NULL && pe->portev_source == PORT_SOURCE_USER) {
+ scp->sc_flags &= ~SVP_CF_USER;
+ if ((scp->sc_flags & SVP_CF_UFLAG) == 0) {
+ mutex_exit(&scp->sc_lock);
+ return;
+ }
+ }
+
+ /* Check if this needs to be freed */
+ if (scp->sc_flags & SVP_CF_REAP) {
+ mutex_exit(&scp->sc_lock);
+ svp_conn_destroy(scp);
+ return;
+ }
+
+ /* Check if this needs to be reset */
+ if (scp->sc_flags & SVP_CF_TEARDOWN) {
+ /* Make sure any other users of this are disassociated */
+ ret = SVP_RA_ERROR;
+ goto out;
+ }
+
+ switch (scp->sc_cstate) {
+ case SVP_CS_INITIAL:
+ case SVP_CS_BACKOFF:
+ assert(pe == NULL);
+ ret = svp_conn_connect(scp);
+ break;
+ case SVP_CS_CONNECTING:
+ assert(pe != NULL);
+ ret = svp_conn_poll_connect(pe, scp);
+ break;
+ case SVP_CS_ACTIVE:
+ case SVP_CS_WINDDOWN:
+ assert(pe != NULL);
+ oldstate = scp->sc_cstate;
+ if (pe->portev_events & POLLOUT)
+ ret = svp_conn_pollout(scp);
+ if (ret == SVP_RA_NONE && (pe->portev_events & POLLIN))
+ ret = svp_conn_pollin(scp);
+
+ if (oldstate == SVP_CS_WINDDOWN &&
+ (list_is_empty(&scp->sc_queries) || ret != SVP_RA_NONE)) {
+ ret = SVP_RA_CLEANUP;
+ }
+
+ if (ret == SVP_RA_NONE) {
+ int err;
+ if ((err = svp_event_associate(&scp->sc_event,
+ scp->sc_socket)) != 0) {
+ scp->sc_error = SVP_CE_ASSOCIATE;
+ scp->sc_errno = err;
+ scp->sc_cstate = SVP_CS_ERROR;
+ ret = SVP_RA_DEGRADE;
+ }
+ }
+ break;
+ default:
+ libvarpd_panic("svp_conn_handler encountered unexpected "
+ "state: %d", scp->sc_cstate);
+ }
+out:
+ mutex_exit(&scp->sc_lock);
+
+ if (ret == SVP_RA_NONE)
+ return;
+
+ mutex_enter(&srp->sr_lock);
+ mutex_enter(&scp->sc_lock);
+ if (ret == SVP_RA_ERROR)
+ ret = svp_conn_reset(scp);
+
+ if (ret == SVP_RA_DEGRADE)
+ svp_conn_degrade(scp);
+ if (ret == SVP_RA_RESTORE)
+ svp_conn_restore(scp);
+
+ if (ret == SVP_RA_CLEANUP) {
+ svp_conn_remove(scp);
+ scp->sc_flags |= SVP_CF_REAP;
+ svp_conn_inject(scp);
+ }
+ mutex_exit(&scp->sc_lock);
+ mutex_exit(&srp->sr_lock);
+}
+
+static void
+svp_conn_backtimer(void *arg)
+{
+ svp_conn_t *scp = arg;
+
+ svp_conn_handler(NULL, scp);
+}
+
+/*
+ * This fires every svp_conn_query_timeout seconds. Its purpos is to determine
+ * if we haven't heard back on a request with in svp_conn_query_timeout seconds.
+ * If any of the svp_conn_query_t's that have been started (indicated by
+ * svp_query_t`sq_acttime != -1), and more than svp_conn_query_timeout seconds
+ * have passed, we basically tear this connection down and reassign outstanding
+ * queries.
+ */
+static void
+svp_conn_querytimer(void *arg)
+{
+ int ret;
+ svp_query_t *sqp;
+ svp_conn_t *scp = arg;
+ hrtime_t now = gethrtime();
+
+ mutex_enter(&scp->sc_lock);
+
+ /*
+ * If we're not in the active state, then we don't care about this as
+ * we're already either going to die or we have no connections to worry
+ * about.
+ */
+ if (scp->sc_cstate != SVP_CS_ACTIVE) {
+ mutex_exit(&scp->sc_lock);
+ return;
+ }
+
+ for (sqp = list_head(&scp->sc_queries); sqp != NULL;
+ sqp = list_next(&scp->sc_queries, sqp)) {
+ if (sqp->sq_acttime == -1)
+ continue;
+ if ((now - sqp->sq_acttime) / NANOSEC > svp_conn_query_timeout)
+ break;
+ }
+
+ /* Nothing timed out, we're good here */
+ if (sqp == NULL) {
+ mutex_exit(&scp->sc_lock);
+ return;
+ }
+
+ (void) bunyan_warn(svp_bunyan, "query timed out on connection",
+ BUNYAN_T_IP, "remote_ip", &scp->sc_addr,
+ BUNYAN_T_INT32, "remote_port", scp->sc_remote->sr_rport,
+ BUNYAN_T_INT32, "operation", ntohs(sqp->sq_header.svp_op),
+ BUNYAN_T_END);
+
+ /*
+ * Begin the tear down process for this connect. If we lose the
+ * disassociate, then we don't inject an event. See the big theory
+ * statement in libvarpd_svp.c for more information.
+ */
+ scp->sc_flags |= SVP_CF_TEARDOWN;
+
+ ret = svp_event_dissociate(&scp->sc_event, scp->sc_socket);
+ if (ret == 0)
+ svp_conn_inject(scp);
+ else
+ VERIFY(ret == ENOENT);
+
+ mutex_exit(&scp->sc_lock);
+}
+
+/*
+ * This connection has fallen out of DNS, figure out what we need to do with it.
+ */
+void
+svp_conn_fallout(svp_conn_t *scp)
+{
+ svp_remote_t *srp = scp->sc_remote;
+
+ assert(MUTEX_HELD(&srp->sr_lock));
+
+ mutex_enter(&scp->sc_lock);
+ switch (scp->sc_cstate) {
+ case SVP_CS_ERROR:
+ /*
+ * Connection is already inactive, so it's safe to tear down.
+ * Fire it off through the state machine to tear down via the
+ * backoff timer.
+ */
+ svp_conn_remove(scp);
+ scp->sc_flags |= SVP_CF_REAP;
+ svp_conn_inject(scp);
+ break;
+ case SVP_CS_INITIAL:
+ case SVP_CS_BACKOFF:
+ case SVP_CS_CONNECTING:
+ /*
+ * Here, we have something actively going on, so we'll let it be
+ * clean up the next time we hit the event loop by the event
+ * loop itself. As it has no connections, there isn't much to
+ * really do, though we'll take this chance to go ahead and
+ * remove it from the remote.
+ */
+ svp_conn_remove(scp);
+ scp->sc_flags |= SVP_CF_REAP;
+ svp_conn_inject(scp);
+ break;
+ case SVP_CS_ACTIVE:
+ case SVP_CS_WINDDOWN:
+ /*
+ * If there are no outstanding queries, then we should simply
+ * clean this up now,t he same way we would with the others.
+ * Othewrise, as we know the event loop is ongoing, we'll make
+ * sure that these entries get cleaned up once they're done.
+ */
+ scp->sc_cstate = SVP_CS_WINDDOWN;
+ if (list_is_empty(&scp->sc_queries)) {
+ svp_conn_remove(scp);
+ scp->sc_flags |= SVP_CF_REAP;
+ svp_conn_inject(scp);
+ }
+ break;
+ default:
+ libvarpd_panic("svp_conn_fallout encountered"
+ "unkonwn state");
+ }
+ mutex_exit(&scp->sc_lock);
+}
+
+int
+svp_conn_create(svp_remote_t *srp, const struct in6_addr *addr)
+{
+ int ret;
+ svp_conn_t *scp;
+
+ assert(MUTEX_HELD(&srp->sr_lock));
+ scp = umem_zalloc(sizeof (svp_conn_t), UMEM_DEFAULT);
+ if (scp == NULL)
+ return (ENOMEM);
+
+ if ((ret = mutex_init(&scp->sc_lock, USYNC_THREAD | LOCK_ERRORCHECK,
+ NULL)) != 0) {
+ umem_free(scp, sizeof (svp_conn_t));
+ return (ret);
+ }
+
+ scp->sc_remote = srp;
+ scp->sc_event.se_func = svp_conn_handler;
+ scp->sc_event.se_arg = scp;
+ scp->sc_btimer.st_func = svp_conn_backtimer;
+ scp->sc_btimer.st_arg = scp;
+ scp->sc_btimer.st_oneshot = B_TRUE;
+ scp->sc_btimer.st_value = 1;
+
+ scp->sc_qtimer.st_func = svp_conn_querytimer;
+ scp->sc_qtimer.st_arg = scp;
+ scp->sc_qtimer.st_oneshot = B_FALSE;
+ scp->sc_qtimer.st_value = svp_conn_query_timeout;
+
+ scp->sc_socket = -1;
+
+ list_create(&scp->sc_queries, sizeof (svp_query_t),
+ offsetof(svp_query_t, sq_lnode));
+ scp->sc_gen = srp->sr_gen;
+ bcopy(addr, &scp->sc_addr, sizeof (struct in6_addr));
+ scp->sc_cstate = SVP_CS_INITIAL;
+ mutex_enter(&scp->sc_lock);
+ svp_conn_add(scp);
+ mutex_exit(&scp->sc_lock);
+
+ /* Now that we're locked and loaded, add our timers */
+ svp_timer_add(&scp->sc_qtimer);
+ svp_timer_add(&scp->sc_btimer);
+
+ return (0);
+}
+
+/*
+ * At the time of calling, the entry has been removed from all lists. In
+ * addition, the entries state should be SVP_CS_ERROR, therefore, we know that
+ * the fd should not be associated with the event loop. We'll double check that
+ * just in case. We should also have already been removed from the remote's
+ * list.
+ */
+void
+svp_conn_destroy(svp_conn_t *scp)
+{
+ int ret;
+
+ mutex_enter(&scp->sc_lock);
+ if (scp->sc_cstate != SVP_CS_ERROR)
+ libvarpd_panic("asked to tear down an active connection");
+ if (scp->sc_flags & SVP_CF_ADDED)
+ libvarpd_panic("asked to remove a connection still in "
+ "the remote list\n");
+ if (!list_is_empty(&scp->sc_queries))
+ libvarpd_panic("asked to remove a connection with non-empty "
+ "query list");
+
+ if ((ret = svp_event_dissociate(&scp->sc_event, scp->sc_socket)) !=
+ ENOENT) {
+ libvarpd_panic("dissociate failed or was actually "
+ "associated: %d", ret);
+ }
+ mutex_exit(&scp->sc_lock);
+
+ /* Verify our timers are killed */
+ svp_timer_remove(&scp->sc_btimer);
+ svp_timer_remove(&scp->sc_qtimer);
+
+ if (scp->sc_socket != -1 && close(scp->sc_socket) != 0)
+ libvarpd_panic("failed to close svp_conn_t`scp_socket fd "
+ "%d: %d", scp->sc_socket, errno);
+
+ list_destroy(&scp->sc_queries);
+ umem_free(scp, sizeof (svp_conn_t));
+}
+
+void
+svp_conn_queue(svp_conn_t *scp, svp_query_t *sqp)
+{
+ assert(MUTEX_HELD(&scp->sc_lock));
+ assert(scp->sc_cstate == SVP_CS_ACTIVE);
+
+ sqp->sq_acttime = -1;
+ list_insert_tail(&scp->sc_queries, sqp);
+ if (!(scp->sc_event.se_events & POLLOUT)) {
+ scp->sc_event.se_events |= POLLOUT;
+ /*
+ * If this becomes frequent, we should instead give up on this
+ * set of connections instead of aborting.
+ */
+ if (svp_event_associate(&scp->sc_event, scp->sc_socket) != 0)
+ libvarpd_panic("svp_event_associate failed somehow");
+ }
+}
diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_crc.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_crc.c
new file mode 100644
index 0000000000..ade47ff998
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_crc.c
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+/*
+ * Perform standard crc32 functions.
+ *
+ * For more information, see the big theory statement in
+ * lib/varpd/svp/common/libvarpd_svp.c.
+ *
+ * Really, this should just be a libcrc.
+ */
+
+#include <sys/crc32.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <inttypes.h>
+#include <libvarpd_svp.h>
+
+static uint32_t svp_crc32_tab[] = { CRC32_TABLE };
+
+static uint32_t
+svp_crc32(uint32_t old, const uint8_t *buf, size_t len)
+{
+ uint32_t out;
+
+ CRC32(out, buf, len, old, svp_crc32_tab);
+ return (out);
+}
+
+void
+svp_query_crc32(svp_req_t *shp, void *buf, size_t data)
+{
+ uint32_t crc = -1U;
+
+ shp->svp_crc32 = 0;
+ crc = svp_crc32(crc, (uint8_t *)shp, sizeof (svp_req_t));
+ crc = svp_crc32(crc, buf, data);
+ crc = ~crc;
+ shp->svp_crc32 = htonl(crc);
+}
diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_host.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_host.c
new file mode 100644
index 0000000000..e91cc30e9d
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_host.c
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+/*
+ * DNS Host-name related functions.
+ *
+ * For more information, see the big theory statement in
+ * lib/varpd/svp/common/libvarpd_svp.c.
+ */
+
+#include <sys/socket.h>
+#include <netdb.h>
+#include <thread.h>
+#include <synch.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <libvarpd_svp.h>
+
+int svp_host_nthreads = 8;
+
+static mutex_t svp_host_lock = ERRORCHECKMUTEX;
+static cond_t svp_host_cv = DEFAULTCV;
+static svp_remote_t *svp_host_head;
+
+/* ARGSUSED */
+static void *
+svp_host_loop(void *unused)
+{
+ for (;;) {
+ int err;
+ svp_remote_t *srp;
+ struct addrinfo *addrs;
+
+ mutex_enter(&svp_host_lock);
+ while (svp_host_head == NULL)
+ (void) cond_wait(&svp_host_cv, &svp_host_lock);
+ srp = svp_host_head;
+ svp_host_head = srp->sr_nexthost;
+ if (svp_host_head != NULL)
+ (void) cond_signal(&svp_host_cv);
+ mutex_exit(&svp_host_lock);
+
+ mutex_enter(&srp->sr_lock);
+ assert(srp->sr_state & SVP_RS_LOOKUP_SCHEDULED);
+ srp->sr_state &= ~SVP_RS_LOOKUP_SCHEDULED;
+ if (srp->sr_state & SVP_RS_LOOKUP_INPROGRESS) {
+ mutex_exit(&srp->sr_lock);
+ continue;
+ }
+ srp->sr_state |= SVP_RS_LOOKUP_INPROGRESS;
+ mutex_exit(&srp->sr_lock);
+
+ for (;;) {
+ err = getaddrinfo(srp->sr_hostname, NULL, NULL, &addrs);
+ if (err == 0)
+ break;
+ if (err != 0) {
+ switch (err) {
+ case EAI_ADDRFAMILY:
+ case EAI_BADFLAGS:
+ case EAI_FAMILY:
+ case EAI_SERVICE:
+ case EAI_SOCKTYPE:
+ case EAI_OVERFLOW:
+ default:
+ libvarpd_panic("unexpected getaddrinfo "
+ "failure: %d", err);
+ break;
+ case EAI_AGAIN:
+ case EAI_MEMORY:
+ case EAI_SYSTEM:
+ continue;
+ case EAI_FAIL:
+ case EAI_NODATA:
+ case EAI_NONAME:
+ /*
+ * At this point in time we have
+ * something which isn't very good. This
+ * may have been a typo or something may
+ * have been destroyed. We should go
+ * ahead and degrade this overall
+ * instance, because we're not going to
+ * make much forward progress... It'd be
+ * great if we could actually issue more
+ * of an EREPORT to describe what
+ * happened, some day.
+ */
+ mutex_enter(&srp->sr_lock);
+ svp_remote_degrade(srp,
+ SVP_RD_DNS_FAIL);
+ mutex_exit(&srp->sr_lock);
+ break;
+ }
+ }
+ break;
+ }
+
+ if (err == 0) {
+ /*
+ * We've successfully resolved something, mark this
+ * degredation over for now.
+ */
+ mutex_enter(&srp->sr_lock);
+ svp_remote_restore(srp, SVP_RD_DNS_FAIL);
+ mutex_exit(&srp->sr_lock);
+ svp_remote_resolved(srp, addrs);
+ }
+
+ mutex_enter(&srp->sr_lock);
+ srp->sr_state &= ~SVP_RS_LOOKUP_INPROGRESS;
+ (void) cond_broadcast(&srp->sr_cond);
+ mutex_exit(&srp->sr_lock);
+ }
+
+ /* LINTED: E_STMT_NOT_REACHED */
+ return (NULL);
+}
+
+void
+svp_host_queue(svp_remote_t *srp)
+{
+ svp_remote_t *s;
+ mutex_enter(&svp_host_lock);
+ mutex_enter(&srp->sr_lock);
+ if (srp->sr_state & SVP_RS_LOOKUP_SCHEDULED) {
+ mutex_exit(&srp->sr_lock);
+ mutex_exit(&svp_host_lock);
+ return;
+ }
+ srp->sr_state |= SVP_RS_LOOKUP_SCHEDULED;
+ s = svp_host_head;
+ while (s != NULL && s->sr_nexthost != NULL)
+ s = s->sr_nexthost;
+ if (s == NULL) {
+ assert(s == svp_host_head);
+ svp_host_head = srp;
+ } else {
+ s->sr_nexthost = srp;
+ }
+ srp->sr_nexthost = NULL;
+ (void) cond_signal(&svp_host_cv);
+ mutex_exit(&srp->sr_lock);
+ mutex_exit(&svp_host_lock);
+}
+
+int
+svp_host_init(void)
+{
+ int i;
+
+ for (i = 0; i < svp_host_nthreads; i++) {
+ if (thr_create(NULL, 0, svp_host_loop, NULL,
+ THR_DETACHED | THR_DAEMON, NULL) != 0)
+ return (errno);
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_loop.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_loop.c
new file mode 100644
index 0000000000..18a79b9dff
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_loop.c
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ */
+
+/*
+ * Event loop mechanism for our backend.
+ *
+ * For more information, see the big theory statement in
+ * lib/varpd/svp/common/libvarpd_svp.c.
+ */
+
+#include <unistd.h>
+#include <thread.h>
+#include <port.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <umem.h>
+
+#include <libvarpd_svp.h>
+
+typedef struct svp_event_loop {
+ int sel_port; /* RO */
+ int sel_nthread; /* RO */
+ thread_t *sel_threads; /* RO */
+ boolean_t sel_stop; /* svp_elock */
+ timer_t sel_hosttimer;
+} svp_event_loop_t;
+
+static svp_event_loop_t svp_event;
+static mutex_t svp_elock = ERRORCHECKMUTEX;
+
+/* ARGSUSED */
+static void *
+svp_event_thr(void *arg)
+{
+ for (;;) {
+ int ret;
+ port_event_t pe;
+ svp_event_t *sep;
+
+ mutex_enter(&svp_elock);
+ if (svp_event.sel_stop == B_TRUE) {
+ mutex_exit(&svp_elock);
+ break;
+ }
+ mutex_exit(&svp_elock);
+
+ ret = port_get(svp_event.sel_port, &pe, NULL);
+ if (ret != 0) {
+ switch (errno) {
+ case EFAULT:
+ case EBADF:
+ case EINVAL:
+ libvarpd_panic("unexpected port_get errno: %d",
+ errno);
+ default:
+ break;
+ }
+ }
+
+ if (pe.portev_user == NULL)
+ libvarpd_panic("received event (%p) without "
+ "protev_user set", &pe);
+ sep = (svp_event_t *)pe.portev_user;
+ sep->se_func(&pe, sep->se_arg);
+ }
+
+ return (NULL);
+}
+
+int
+svp_event_associate(svp_event_t *sep, int fd)
+{
+ int ret;
+
+ ret = port_associate(svp_event.sel_port, PORT_SOURCE_FD, fd,
+ sep->se_events, sep);
+ if (ret != 0) {
+ switch (errno) {
+ case EBADF:
+ case EBADFD:
+ case EINVAL:
+ case EAGAIN:
+ libvarpd_panic("unexpected port_associate error: %d",
+ errno);
+ default:
+ ret = errno;
+ break;
+ }
+ }
+
+ return (ret);
+}
+
+/* ARGSUSED */
+int
+svp_event_dissociate(svp_event_t *sep, int fd)
+{
+ int ret;
+
+ ret = port_dissociate(svp_event.sel_port, PORT_SOURCE_FD, fd);
+ if (ret != 0) {
+ if (errno != ENOENT)
+ libvarpd_panic("unexpected port_dissociate error: %d",
+ errno);
+ ret = errno;
+ }
+ return (ret);
+}
+
+int
+svp_event_inject(svp_event_t *user)
+{
+ return (port_send(svp_event.sel_port, 0, user));
+}
+
+int
+svp_event_timer_init(svp_event_t *sep)
+{
+ port_notify_t pn;
+ struct sigevent evp;
+ struct itimerspec ts;
+
+ pn.portnfy_port = svp_event.sel_port;
+ pn.portnfy_user = sep;
+ evp.sigev_notify = SIGEV_PORT;
+ evp.sigev_value.sival_ptr = &pn;
+
+ if (timer_create(CLOCK_REALTIME, &evp, &svp_event.sel_hosttimer) != 0)
+ return (errno);
+
+ ts.it_value.tv_sec = svp_tickrate;
+ ts.it_value.tv_nsec = 0;
+ ts.it_interval.tv_sec = svp_tickrate;
+ ts.it_interval.tv_nsec = 0;
+
+ if (timer_settime(svp_event.sel_hosttimer, TIMER_RELTIME, &ts,
+ NULL) != 0) {
+ int ret = errno;
+ (void) timer_delete(svp_event.sel_hosttimer);
+ return (ret);
+ }
+
+ return (0);
+}
+
+int
+svp_event_init(void)
+{
+ long i, ncpus;
+
+ svp_event.sel_port = port_create();
+ if (svp_event.sel_port == -1)
+ return (errno);
+
+ ncpus = sysconf(_SC_NPROCESSORS_ONLN) * 2 + 1;
+ if (ncpus <= 0)
+ libvarpd_panic("sysconf for nprocs failed... %d/%d",
+ ncpus, errno);
+
+ svp_event.sel_threads = umem_alloc(sizeof (thread_t) * ncpus,
+ UMEM_DEFAULT);
+ if (svp_event.sel_threads == NULL) {
+ int ret = errno;
+ (void) timer_delete(svp_event.sel_hosttimer);
+ (void) close(svp_event.sel_port);
+ svp_event.sel_port = -1;
+ return (ret);
+ }
+
+ for (i = 0; i < ncpus; i++) {
+ int ret;
+ thread_t *thr = &svp_event.sel_threads[i];
+
+ ret = thr_create(NULL, 0, svp_event_thr, NULL,
+ THR_DETACHED | THR_DAEMON, thr);
+ if (ret != 0) {
+ ret = errno;
+ (void) timer_delete(svp_event.sel_hosttimer);
+ (void) close(svp_event.sel_port);
+ svp_event.sel_port = -1;
+ return (errno);
+ }
+ }
+
+ return (0);
+}
+
+void
+svp_event_fini(void)
+{
+ mutex_enter(&svp_elock);
+ svp_event.sel_stop = B_TRUE;
+ mutex_exit(&svp_elock);
+
+ (void) timer_delete(svp_event.sel_hosttimer);
+ (void) close(svp_event.sel_port);
+}
diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_prot.h b/usr/src/lib/varpd/svp/common/libvarpd_svp_prot.h
new file mode 100644
index 0000000000..16dbdbec05
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_prot.h
@@ -0,0 +1,236 @@
+/*
+ * 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 _LIBVARPD_SVP_PROT_H
+#define _LIBVARPD_SVP_PROT_H
+
+/*
+ * SVP protocol Definitions
+ */
+
+#include <sys/types.h>
+#include <inttypes.h>
+#include <sys/ethernet.h>
+#include <netinet/in.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * SDC VXLAN Protocol Definitions
+ */
+
+#define SVP_VERSION_ONE 1
+#define SVP_CURRENT_VERSION SVP_VERSION_ONE
+
+typedef struct svp_req {
+ uint16_t svp_ver;
+ uint16_t svp_op;
+ uint32_t svp_size;
+ uint32_t svp_id;
+ uint32_t svp_crc32;
+} svp_req_t;
+
+typedef enum svp_op {
+ SVP_R_UNKNOWN = 0x00,
+ SVP_R_PING = 0x01,
+ SVP_R_PONG = 0x02,
+ SVP_R_VL2_REQ = 0x03,
+ SVP_R_VL2_ACK = 0x04,
+ SVP_R_VL3_REQ = 0x05,
+ SVP_R_VL3_ACK = 0x06,
+ SVP_R_BULK_REQ = 0x07,
+ SVP_R_BULK_ACK = 0x08,
+ SVP_R_LOG_REQ = 0x09,
+ SVP_R_LOG_ACK = 0x0A,
+ SVP_R_LOG_RM = 0x0B,
+ SVP_R_LOG_RM_ACK = 0x0C,
+ SVP_R_SHOOTDOWN = 0x0D
+} svp_op_t;
+
+typedef enum svp_status {
+ SVP_S_OK = 0x00, /* Everything OK */
+ SVP_S_FATAL = 0x01, /* Fatal error, close connection */
+ SVP_S_NOTFOUND = 0x02, /* Entry not found */
+ SVP_S_BADL3TYPE = 0x03, /* Unknown svp_vl3_type_t */
+ SVP_S_BADBULK = 0x04 /* Unknown svp_bulk_type_t */
+} svp_status_t;
+
+/*
+ * A client issues the SVP_R_VL2_REQ whenever it needs to perform a VLS->UL3
+ * lookup. Requests have the following structure:
+ */
+typedef struct svp_vl2_req {
+ uint8_t sl2r_mac[ETHERADDRL];
+ uint8_t sl2r_pad[2];
+ uint32_t sl2r_vnetid;
+} svp_vl2_req_t;
+
+/*
+ * This is the message a server uses to reply to the SVP_R_VL2_REQ. If the
+ * destination on the underlay is an IPv4 address, it should be encoded as an
+ * IPv4-mapped IPv6 address.
+ */
+typedef struct svp_vl2_ack {
+ uint16_t sl2a_status;
+ uint16_t sl2a_port;
+ uint8_t sl2a_addr[16];
+} svp_vl2_ack_t;
+
+
+/*
+ * A client issues the SVP_R_VL3_REQ request whenever it needs to perform a
+ * VL3->VL2 lookup. Note, that this also implicitly performs a VL2->UL3 lookup
+ * as well. The sl3r_type member is used to indicate the kind of lookup type
+ * that we're performing, eg. is it a L3 or L2.
+ */
+typedef enum svp_vl3_type {
+ SVP_VL3_IP = 0x01,
+ SVP_VL3_IPV6 = 0x02
+} svp_vl3_type_t;
+
+typedef struct svp_vl3_req {
+ uint8_t sl3r_ip[16];
+ uint32_t sl3r_type;
+ uint32_t sl3r_vnetid;
+} svp_vl3_req_t;
+
+/*
+ * This response, corresponding to the SVP_R_VL3_ACK, includes an answer to both
+ * the VL3->VL2 and the VL2->UL3 requests.
+ */
+typedef struct svp_vl3_ack {
+ uint32_t sl3a_status;
+ uint8_t sl3a_mac[ETHERADDRL];
+ uint16_t sl3a_uport;
+ uint8_t sl3a_uip[16];
+} svp_vl3_ack_t;
+
+/*
+ * SVP_R_BULK_REQ requests a bulk dump of data. Currently we have two kinds of
+ * data tables that we need to dump: VL3->VL2 mappings and VL2->UL3 mappings.
+ * The kind that we want is indicated using the svbr_type member.
+ */
+typedef enum svp_bulk_type {
+ SVP_BULK_VL2 = 0x01,
+ SVP_BULK_VL3 = 0x02
+} svp_bulk_type_t;
+
+typedef struct svp_bulk_req {
+ uint32_t svbr_type;
+} svp_bulk_req_t;
+
+/*
+ * When replying to a bulk request (SVP_R_BULK_ACK), data is streamed back
+ * across. The format of the data is currently undefined and as we work on the
+ * system, we'll get a better understanding of what this should look like. A
+ * client may need to stream such a request to disk, or the format will need to
+ * be in a streamable format that allows the client to construct data.
+ */
+typedef struct svp_bulk_ack {
+ uint32_t svba_status;
+ uint32_t svba_type;
+ uint8_t svba_data[];
+} svp_bulk_ack_t;
+
+/*
+ * SVP_R_LOG_REQ requests a log entries from the specified log from the server.
+ * The total number of bytes that the user is ready to receive is in svlr_count.
+ * However, the server should not block for data if none is available and thus
+ * may return less than svlr_count bytes back. We identify the IP address of the
+ * underlay to use here explicitly.
+ */
+typedef struct svp_log_req {
+ uint32_t svlr_count;
+ uint8_t svlr_ip[16];
+} svp_log_req_t;
+
+/*
+ * The server replies to a log request by sending a series of log entries.
+ * These log entries may be a mixture of both vl2 and vl3 records. The reply is
+ * a stream of bytes after the status message whose length is determined baseed
+ * on the header itself. Each entry begins with a uint32_t that describes its
+ * type and then is followed by the remaining data payload. The next entry
+ * follows immediately which again begins with the uint32_t word that describes
+ * what it should be.
+ */
+typedef enum svp_log_type {
+ SVP_LOG_VL2 = 0x01,
+ SVP_LOG_VL3 = 0x02
+} svp_log_type_t;
+
+typedef struct svp_log_vl2 {
+ uint32_t svl2_type; /* Should be SVP_LOG_VL2 */
+ uint8_t svl2_id[16]; /* 16-byte UUID */
+ uint8_t svl2_mac[ETHERADDRL];
+ uint8_t svl2_pad[2];
+ uint32_t svl2_vnetid;
+} svp_log_vl2_t;
+
+typedef struct svp_log_vl3 {
+ uint32_t svl3_type; /* Should be SVP_LOG_VL3 */
+ uint8_t svl3_id[16]; /* 16-byte UUID */
+ uint8_t svl3_ip[16];
+ uint8_t svl3_pad[2];
+ uint16_t svl3_vlan;
+ uint32_t svl3_vnetid;
+} svp_log_vl3_t;
+
+typedef struct svp_log_ack {
+ uint32_t svla_status;
+ uint8_t svla_data[];
+} svp_log_ack_t;
+
+/*
+ * SVP_R_LOG_RM is used after the client successfully processes a series of the
+ * log stream. It replies to tell the server that it can remove those IDs from
+ * processing. The IDs used are the same IDs that were in the individual
+ * SVP_R_LOG_ACK entries.
+ */
+typedef struct svp_lrm_req {
+ uint32_t svrr_count;
+ uint8_t svrr_ids[];
+} svp_lrm_req_t;
+
+/*
+ * SVP_R_LOG_RM_ACK is used to indicate that a log entry has been successfully
+ * deleted and at this point it makes sense to go and ask for another
+ * SVP_R_LOG_REQ.
+ */
+typedef struct svp_lrm_ack {
+ uint32_t svra_status;
+} svp_lrm_ack_t;
+
+/*
+ * A shootdown (SVP_R_SHOOTDOWN) is used by a CN to reply to another CN that it
+ * sent an invalid entry that could not be processed. This should be a
+ * relatively infrequent occurrence. Unlike the rest of the messages, there is
+ * no reply to it. It's a single request to try and help get us out there. When
+ * a node receives this, it will issue a conditional revocation ioctl, that
+ * removes the entry if and only if, it matches the IP. That way if we've
+ * already gotten an updated entry for this, we don't remove it again.
+ */
+typedef struct svp_shootdown {
+ uint8_t svsd_mac[ETHERADDRL];
+ uint8_t svsd_pad[2];
+ uint32_t svsd_vnetid;
+} svp_shootdown_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBVARPD_SVP_PROT_H */
diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_remote.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_remote.c
new file mode 100644
index 0000000000..99775f93c0
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_remote.c
@@ -0,0 +1,821 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+/*
+ * Remote backend management
+ *
+ * For more information, see the big theory statement in
+ * lib/varpd/svp/common/libvarpd_svp.c.
+ */
+
+#include <umem.h>
+#include <strings.h>
+#include <string.h>
+#include <stddef.h>
+#include <thread.h>
+#include <synch.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <errno.h>
+#include <libidspace.h>
+
+#include <libvarpd_provider.h>
+#include <libvarpd_svp.h>
+
+typedef struct svp_shoot_vl3 {
+ svp_query_t ssv_query;
+ struct sockaddr_in6 ssv_sock;
+ svp_log_vl3_t *ssv_vl3;
+ svp_sdlog_t *ssv_log;
+} svp_shoot_vl3_t;
+
+static mutex_t svp_remote_lock = ERRORCHECKMUTEX;
+static avl_tree_t svp_remote_tree;
+static svp_timer_t svp_dns_timer;
+static id_space_t *svp_idspace;
+static int svp_dns_timer_rate = 30; /* seconds */
+
+static void
+svp_remote_mkfmamsg(svp_remote_t *srp, svp_degrade_state_t state, char *buf,
+ size_t buflen)
+{
+ switch (state) {
+ case SVP_RD_DNS_FAIL:
+ (void) snprintf(buf, buflen, "failed to resolve or find "
+ "entries for hostname %s", srp->sr_hostname);
+ break;
+ case SVP_RD_REMOTE_FAIL:
+ (void) snprintf(buf, buflen, "cannot reach any remote peers");
+ break;
+ default:
+ (void) snprintf(buf, buflen, "unkonwn error state: %d", state);
+ }
+}
+
+static int
+svp_remote_comparator(const void *l, const void *r)
+{
+ int ret;
+ const svp_remote_t *lr = l, *rr = r;
+
+ ret = strcmp(lr->sr_hostname, rr->sr_hostname);
+ if (ret > 0)
+ return (1);
+ else if (ret < 0)
+ return (-1);
+
+ if (lr->sr_rport > rr->sr_rport)
+ return (1);
+ else if (lr->sr_rport < rr->sr_rport)
+ return (-1);
+
+ return (memcmp(&lr->sr_uip, &rr->sr_uip, sizeof (struct in6_addr)));
+}
+
+void
+svp_query_release(svp_query_t *sqp)
+{
+ id_free(svp_idspace, sqp->sq_header.svp_id);
+}
+
+static void
+svp_remote_destroy(svp_remote_t *srp)
+{
+ size_t len;
+
+ /*
+ * Clean up any unrelated DNS information. At this point we know that
+ * we're not in the remote tree. That means, that svp_remote_dns_timer
+ * cannot queue us. However, if any of our DNS related state flags are
+ * set, we have to hang out.
+ */
+ mutex_enter(&srp->sr_lock);
+ while (srp->sr_state &
+ (SVP_RS_LOOKUP_SCHEDULED | SVP_RS_LOOKUP_INPROGRESS)) {
+ (void) cond_wait(&srp->sr_cond, &srp->sr_lock);
+ }
+ mutex_exit(&srp->sr_lock);
+ svp_shootdown_fini(srp);
+
+ if (cond_destroy(&srp->sr_cond) != 0)
+ libvarpd_panic("failed to destroy cond sr_cond");
+
+ if (mutex_destroy(&srp->sr_lock) != 0)
+ libvarpd_panic("failed to destroy mutex sr_lock");
+
+ if (srp->sr_addrinfo != NULL)
+ freeaddrinfo(srp->sr_addrinfo);
+ len = strlen(srp->sr_hostname) + 1;
+ umem_free(srp->sr_hostname, len);
+ umem_free(srp, sizeof (svp_remote_t));
+}
+
+static int
+svp_remote_create(const char *host, uint16_t port, struct in6_addr *uip,
+ svp_remote_t **outp)
+{
+ size_t hlen;
+ svp_remote_t *remote;
+
+ assert(MUTEX_HELD(&svp_remote_lock));
+
+ remote = umem_zalloc(sizeof (svp_remote_t), UMEM_DEFAULT);
+ if (remote == NULL) {
+ mutex_exit(&svp_remote_lock);
+ return (ENOMEM);
+ }
+
+ if (svp_shootdown_init(remote) != 0) {
+ umem_free(remote, sizeof (svp_remote_t));
+ mutex_exit(&svp_remote_lock);
+ return (ENOMEM);
+ }
+
+ hlen = strlen(host) + 1;
+ remote->sr_hostname = umem_alloc(hlen, UMEM_DEFAULT);
+ if (remote->sr_hostname == NULL) {
+ svp_shootdown_fini(remote);
+ umem_free(remote, sizeof (svp_remote_t));
+ mutex_exit(&svp_remote_lock);
+ return (ENOMEM);
+ }
+ remote->sr_rport = port;
+ if (mutex_init(&remote->sr_lock,
+ USYNC_THREAD | LOCK_ERRORCHECK, NULL) != 0)
+ libvarpd_panic("failed to create mutex sr_lock");
+ if (cond_init(&remote->sr_cond, USYNC_PROCESS, NULL) != 0)
+ libvarpd_panic("failed to create cond sr_cond");
+ list_create(&remote->sr_conns, sizeof (svp_conn_t),
+ offsetof(svp_conn_t, sc_rlist));
+ avl_create(&remote->sr_tree, svp_comparator, sizeof (svp_t),
+ offsetof(svp_t, svp_rlink));
+ (void) strlcpy(remote->sr_hostname, host, hlen);
+ remote->sr_count = 1;
+ remote->sr_uip = *uip;
+
+ svp_shootdown_start(remote);
+
+ *outp = remote;
+ return (0);
+}
+
+int
+svp_remote_find(char *host, uint16_t port, struct in6_addr *uip,
+ svp_remote_t **outp)
+{
+ int ret;
+ svp_remote_t lookup, *remote;
+
+ lookup.sr_hostname = host;
+ lookup.sr_rport = port;
+ lookup.sr_uip = *uip;
+ mutex_enter(&svp_remote_lock);
+ remote = avl_find(&svp_remote_tree, &lookup, NULL);
+ if (remote != NULL) {
+ assert(remote->sr_count > 0);
+ remote->sr_count++;
+ *outp = remote;
+ mutex_exit(&svp_remote_lock);
+ return (0);
+ }
+
+ if ((ret = svp_remote_create(host, port, uip, outp)) != 0) {
+ mutex_exit(&svp_remote_lock);
+ return (ret);
+ }
+
+ avl_add(&svp_remote_tree, *outp);
+ mutex_exit(&svp_remote_lock);
+
+ /* Make sure DNS is up to date */
+ svp_host_queue(*outp);
+
+ return (0);
+}
+
+void
+svp_remote_release(svp_remote_t *srp)
+{
+ mutex_enter(&svp_remote_lock);
+ mutex_enter(&srp->sr_lock);
+ srp->sr_count--;
+ if (srp->sr_count != 0) {
+ mutex_exit(&srp->sr_lock);
+ mutex_exit(&svp_remote_lock);
+ return;
+ }
+ mutex_exit(&srp->sr_lock);
+
+ avl_remove(&svp_remote_tree, srp);
+ mutex_exit(&svp_remote_lock);
+ svp_remote_destroy(srp);
+}
+
+int
+svp_remote_attach(svp_remote_t *srp, svp_t *svp)
+{
+ svp_t check;
+ avl_index_t where;
+
+ mutex_enter(&srp->sr_lock);
+ if (svp->svp_remote != NULL)
+ libvarpd_panic("failed to create mutex sr_lock");
+
+ /*
+ * We require everything except shootdowns
+ */
+ if (svp->svp_cb.scb_vl2_lookup == NULL)
+ libvarpd_panic("missing callback scb_vl2_lookup");
+ if (svp->svp_cb.scb_vl3_lookup == NULL)
+ libvarpd_panic("missing callback scb_vl3_lookup");
+ if (svp->svp_cb.scb_vl2_invalidate == NULL)
+ libvarpd_panic("missing callback scb_vl2_invalidate");
+ if (svp->svp_cb.scb_vl3_inject == NULL)
+ libvarpd_panic("missing callback scb_vl3_inject");
+
+ check.svp_vid = svp->svp_vid;
+ if (avl_find(&srp->sr_tree, &check, &where) != NULL)
+ libvarpd_panic("found duplicate entry with vid %ld",
+ svp->svp_vid);
+ avl_insert(&srp->sr_tree, svp, where);
+ svp->svp_remote = srp;
+ mutex_exit(&srp->sr_lock);
+
+ return (0);
+}
+
+void
+svp_remote_detach(svp_t *svp)
+{
+ svp_t *lookup;
+ svp_remote_t *srp = svp->svp_remote;
+
+ if (srp == NULL)
+ libvarpd_panic("trying to detach remote when none exists");
+
+ mutex_enter(&srp->sr_lock);
+ lookup = avl_find(&srp->sr_tree, svp, NULL);
+ if (lookup == NULL || lookup != svp)
+ libvarpd_panic("inconsitent remote avl tree...");
+ avl_remove(&srp->sr_tree, svp);
+ svp->svp_remote = NULL;
+ mutex_exit(&srp->sr_lock);
+ svp_remote_release(srp);
+}
+
+/*
+ * Walk the list of connections and find the first one that's available, the
+ * move it to the back of the list so it's less likely to be used again.
+ */
+static boolean_t
+svp_remote_conn_queue(svp_remote_t *srp, svp_query_t *sqp)
+{
+ svp_conn_t *scp;
+
+ assert(MUTEX_HELD(&srp->sr_lock));
+ for (scp = list_head(&srp->sr_conns); scp != NULL;
+ scp = list_next(&srp->sr_conns, scp)) {
+ mutex_enter(&scp->sc_lock);
+ if (scp->sc_cstate != SVP_CS_ACTIVE) {
+ mutex_exit(&scp->sc_lock);
+ continue;
+ }
+ svp_conn_queue(scp, sqp);
+ mutex_exit(&scp->sc_lock);
+ list_remove(&srp->sr_conns, scp);
+ list_insert_tail(&srp->sr_conns, scp);
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+static void
+svp_remote_vl2_lookup_cb(svp_query_t *sqp, void *arg)
+{
+ svp_t *svp = sqp->sq_svp;
+ svp_vl2_ack_t *vl2a = (svp_vl2_ack_t *)sqp->sq_wdata;
+
+ if (sqp->sq_status == SVP_S_OK)
+ svp->svp_cb.scb_vl2_lookup(svp, sqp->sq_status,
+ (struct in6_addr *)vl2a->sl2a_addr, ntohs(vl2a->sl2a_port),
+ arg);
+ else
+ svp->svp_cb.scb_vl2_lookup(svp, sqp->sq_status, NULL, 0, arg);
+}
+
+void
+svp_remote_vl2_lookup(svp_t *svp, svp_query_t *sqp, const uint8_t *mac,
+ void *arg)
+{
+ svp_remote_t *srp;
+ svp_vl2_req_t *vl2r = &sqp->sq_rdun.sqd_vl2r;
+
+ srp = svp->svp_remote;
+ sqp->sq_func = svp_remote_vl2_lookup_cb;
+ sqp->sq_arg = arg;
+ sqp->sq_svp = svp;
+ sqp->sq_state = SVP_QUERY_INIT;
+ sqp->sq_header.svp_ver = htons(SVP_CURRENT_VERSION);
+ sqp->sq_header.svp_op = htons(SVP_R_VL2_REQ);
+ sqp->sq_header.svp_size = htonl(sizeof (svp_vl2_req_t));
+ sqp->sq_header.svp_id = id_alloc(svp_idspace);
+ if (sqp->sq_header.svp_id == (id_t)-1)
+ libvarpd_panic("failed to allcoate from svp_idspace: %d",
+ errno);
+ sqp->sq_header.svp_crc32 = htonl(0);
+ sqp->sq_rdata = vl2r;
+ sqp->sq_rsize = sizeof (svp_vl2_req_t);
+ sqp->sq_wdata = NULL;
+ sqp->sq_wsize = 0;
+
+ bcopy(mac, vl2r->sl2r_mac, ETHERADDRL);
+ vl2r->sl2r_vnetid = ntohl(svp->svp_vid);
+
+ mutex_enter(&srp->sr_lock);
+ if (svp_remote_conn_queue(srp, sqp) == B_FALSE)
+ svp->svp_cb.scb_vl2_lookup(svp, SVP_S_FATAL, NULL, 0, arg);
+ mutex_exit(&srp->sr_lock);
+}
+
+static void
+svp_remote_vl3_lookup_cb(svp_query_t *sqp, void *arg)
+{
+ svp_t *svp = sqp->sq_svp;
+ svp_vl3_ack_t *vl3a = (svp_vl3_ack_t *)sqp->sq_wdata;
+
+ if (sqp->sq_status == SVP_S_OK)
+ svp->svp_cb.scb_vl3_lookup(svp, sqp->sq_status, vl3a->sl3a_mac,
+ (struct in6_addr *)vl3a->sl3a_uip, ntohs(vl3a->sl3a_uport),
+ arg);
+ else
+ svp->svp_cb.scb_vl3_lookup(svp, sqp->sq_status, NULL, NULL, 0,
+ arg);
+}
+
+static void
+svp_remote_vl3_common(svp_remote_t *srp, svp_query_t *sqp,
+ const struct sockaddr *addr, svp_query_f func, void *arg, uint32_t vid)
+{
+ svp_vl3_req_t *vl3r = &sqp->sq_rdun.sdq_vl3r;
+
+ if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6)
+ libvarpd_panic("unexpected sa_family for the vl3 lookup");
+
+ sqp->sq_func = func;
+ sqp->sq_arg = arg;
+ sqp->sq_state = SVP_QUERY_INIT;
+ sqp->sq_header.svp_ver = htons(SVP_CURRENT_VERSION);
+ sqp->sq_header.svp_op = htons(SVP_R_VL3_REQ);
+ sqp->sq_header.svp_size = htonl(sizeof (svp_vl3_req_t));
+ sqp->sq_header.svp_id = id_alloc(svp_idspace);
+ if (sqp->sq_header.svp_id == (id_t)-1)
+ libvarpd_panic("failed to allcoate from svp_idspace: %d",
+ errno);
+ sqp->sq_header.svp_crc32 = htonl(0);
+ sqp->sq_rdata = vl3r;
+ sqp->sq_rsize = sizeof (svp_vl3_req_t);
+ sqp->sq_wdata = NULL;
+ sqp->sq_wsize = 0;
+
+ if (addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)addr;
+ vl3r->sl3r_type = htonl(SVP_VL3_IPV6);
+ bcopy(&s6->sin6_addr, vl3r->sl3r_ip,
+ sizeof (struct in6_addr));
+ } else {
+ struct sockaddr_in *s4 = (struct sockaddr_in *)addr;
+ struct in6_addr v6;
+
+ vl3r->sl3r_type = htonl(SVP_VL3_IP);
+ IN6_INADDR_TO_V4MAPPED(&s4->sin_addr, &v6);
+ bcopy(&v6, vl3r->sl3r_ip, sizeof (struct in6_addr));
+ }
+ vl3r->sl3r_vnetid = htonl(vid);
+
+ mutex_enter(&srp->sr_lock);
+ if (svp_remote_conn_queue(srp, sqp) == B_FALSE) {
+ sqp->sq_status = SVP_S_FATAL;
+ sqp->sq_func(sqp, arg);
+ }
+ mutex_exit(&srp->sr_lock);
+}
+
+/*
+ * This is a request to do a VL3 look-up that originated internally as opposed
+ * to coming from varpd. As such we need a slightly different query callback
+ * function upon completion and don't go through the normal path with the svp_t.
+ */
+void
+svp_remote_vl3_logreq(svp_remote_t *srp, svp_query_t *sqp, uint32_t vid,
+ const struct sockaddr *addr, svp_query_f func, void *arg)
+{
+ svp_remote_vl3_common(srp, sqp, addr, func, arg, vid);
+}
+
+void
+svp_remote_vl3_lookup(svp_t *svp, svp_query_t *sqp,
+ const struct sockaddr *addr, void *arg)
+{
+ svp_remote_t *srp = svp->svp_remote;
+
+ sqp->sq_svp = svp;
+ svp_remote_vl3_common(srp, sqp, addr, svp_remote_vl3_lookup_cb,
+ arg, svp->svp_vid);
+}
+
+static void
+svp_remote_log_request_cb(svp_query_t *sqp, void *arg)
+{
+ svp_remote_t *srp = sqp->sq_arg;
+
+ assert(sqp->sq_wdata != NULL);
+ if (sqp->sq_status == SVP_S_OK)
+ svp_shootdown_logr_cb(srp, sqp->sq_status, sqp->sq_wdata,
+ sqp->sq_size);
+ else
+ svp_shootdown_logr_cb(srp, sqp->sq_status, NULL, 0);
+}
+
+void
+svp_remote_log_request(svp_remote_t *srp, svp_query_t *sqp, void *buf,
+ size_t buflen)
+{
+ svp_log_req_t *logr = &sqp->sq_rdun.sdq_logr;
+ boolean_t queued;
+
+ sqp->sq_func = svp_remote_log_request_cb;
+ sqp->sq_state = SVP_QUERY_INIT;
+ sqp->sq_arg = srp;
+ sqp->sq_header.svp_ver = htons(SVP_CURRENT_VERSION);
+ sqp->sq_header.svp_op = htons(SVP_R_LOG_REQ);
+ sqp->sq_header.svp_size = htonl(sizeof (svp_log_req_t));
+ sqp->sq_header.svp_id = id_alloc(svp_idspace);
+ if (sqp->sq_header.svp_id == (id_t)-1)
+ libvarpd_panic("failed to allcoate from svp_idspace: %d",
+ errno);
+ sqp->sq_header.svp_crc32 = htonl(0);
+ sqp->sq_rdata = logr;
+ sqp->sq_rsize = sizeof (svp_log_req_t);
+ sqp->sq_wdata = buf;
+ sqp->sq_wsize = buflen;
+
+ logr->svlr_count = htonl(buflen);
+ bcopy(&srp->sr_uip, logr->svlr_ip, sizeof (struct in6_addr));
+
+ /*
+ * If this fails, there isn't much that we can't do. Give the callback
+ * with a fatal status.
+ */
+ mutex_enter(&srp->sr_lock);
+ queued = svp_remote_conn_queue(srp, sqp);
+ mutex_exit(&srp->sr_lock);
+
+ if (queued == B_FALSE)
+ svp_shootdown_logr_cb(srp, SVP_S_FATAL, NULL, 0);
+}
+
+static void
+svp_remote_lrm_request_cb(svp_query_t *sqp, void *arg)
+{
+ svp_remote_t *srp = arg;
+
+ svp_shootdown_lrm_cb(srp, sqp->sq_status);
+}
+
+void
+svp_remote_lrm_request(svp_remote_t *srp, svp_query_t *sqp, void *buf,
+ size_t buflen)
+{
+ boolean_t queued;
+ svp_lrm_req_t *svrr = buf;
+
+ sqp->sq_func = svp_remote_lrm_request_cb;
+ sqp->sq_state = SVP_QUERY_INIT;
+ sqp->sq_arg = srp;
+ sqp->sq_header.svp_ver = htons(SVP_CURRENT_VERSION);
+ sqp->sq_header.svp_op = htons(SVP_R_LOG_RM);
+ sqp->sq_header.svp_size = htonl(buflen);
+ sqp->sq_header.svp_id = id_alloc(svp_idspace);
+ if (sqp->sq_header.svp_id == (id_t)-1)
+ libvarpd_panic("failed to allcoate from svp_idspace: %d",
+ errno);
+ sqp->sq_header.svp_crc32 = htonl(0);
+ sqp->sq_rdata = buf;
+ sqp->sq_rsize = buflen;
+ sqp->sq_wdata = NULL;
+ sqp->sq_wsize = 0;
+
+ /*
+ * We need to fix up the count to be in proper network order.
+ */
+ svrr->svrr_count = htonl(svrr->svrr_count);
+
+ /*
+ * If this fails, there isn't much that we can't do. Give the callback
+ * with a fatal status.
+ */
+ mutex_enter(&srp->sr_lock);
+ queued = svp_remote_conn_queue(srp, sqp);
+ mutex_exit(&srp->sr_lock);
+
+ if (queued == B_FALSE)
+ svp_shootdown_logr_cb(srp, SVP_S_FATAL, NULL, 0);
+}
+
+/* ARGSUSED */
+void
+svp_remote_dns_timer(void *unused)
+{
+ svp_remote_t *s;
+ mutex_enter(&svp_remote_lock);
+ for (s = avl_first(&svp_remote_tree); s != NULL;
+ s = AVL_NEXT(&svp_remote_tree, s)) {
+ svp_host_queue(s);
+ }
+ mutex_exit(&svp_remote_lock);
+}
+
+void
+svp_remote_resolved(svp_remote_t *srp, struct addrinfo *newaddrs)
+{
+ struct addrinfo *a;
+ svp_conn_t *scp;
+ int ngen;
+
+ mutex_enter(&srp->sr_lock);
+ srp->sr_gen++;
+ ngen = srp->sr_gen;
+ mutex_exit(&srp->sr_lock);
+
+ for (a = newaddrs; a != NULL; a = a->ai_next) {
+ struct in6_addr in6;
+ struct in6_addr *addrp;
+
+ if (a->ai_family != AF_INET && a->ai_family != AF_INET6)
+ continue;
+
+ if (a->ai_family == AF_INET) {
+ struct sockaddr_in *v4;
+ v4 = (struct sockaddr_in *)a->ai_addr;
+ addrp = &in6;
+ IN6_INADDR_TO_V4MAPPED(&v4->sin_addr, addrp);
+ } else {
+ struct sockaddr_in6 *v6;
+ v6 = (struct sockaddr_in6 *)a->ai_addr;
+ addrp = &v6->sin6_addr;
+ }
+
+ mutex_enter(&srp->sr_lock);
+ for (scp = list_head(&srp->sr_conns); scp != NULL;
+ scp = list_next(&srp->sr_conns, scp)) {
+ mutex_enter(&scp->sc_lock);
+ if (bcmp(addrp, &scp->sc_addr,
+ sizeof (struct in6_addr)) == 0) {
+ scp->sc_gen = ngen;
+ mutex_exit(&scp->sc_lock);
+ break;
+ }
+ mutex_exit(&scp->sc_lock);
+ }
+
+ /*
+ * We need to be careful in the assumptions that we make here,
+ * as there's a good chance that svp_conn_create will
+ * drop the svp_remote_t`sr_lock to kick off its effective event
+ * loop.
+ */
+ if (scp == NULL)
+ (void) svp_conn_create(srp, addrp);
+ mutex_exit(&srp->sr_lock);
+ }
+
+ /*
+ * Now it's time to clean things up. We do not actively clean up the
+ * current connections that we have, instead allowing them to stay
+ * around assuming that they're still useful. Instead, we go through and
+ * purge the degraded list for anything that's from an older generation.
+ */
+ mutex_enter(&srp->sr_lock);
+ for (scp = list_head(&srp->sr_conns); scp != NULL;
+ scp = list_next(&srp->sr_conns, scp)) {
+ boolean_t fall = B_FALSE;
+ mutex_enter(&scp->sc_lock);
+ if (scp->sc_gen < srp->sr_gen)
+ fall = B_TRUE;
+ mutex_exit(&scp->sc_lock);
+ if (fall == B_TRUE)
+ svp_conn_fallout(scp);
+ }
+ mutex_exit(&srp->sr_lock);
+}
+
+/*
+ * This connection is in the process of being reset, we need to reassign all of
+ * its queries to other places or mark them as fatal. Note that the first
+ * connection was the one in flight when this failed. We always mark it as
+ * failed to avoid trying to reset its state.
+ */
+void
+svp_remote_reassign(svp_remote_t *srp, svp_conn_t *scp)
+{
+ boolean_t first = B_TRUE;
+ assert(MUTEX_HELD(&srp->sr_lock));
+ assert(MUTEX_HELD(&srp->sr_lock));
+ svp_query_t *sqp;
+
+ /*
+ * As we try to reassigning all of its queries, remove it from the list.
+ */
+ list_remove(&srp->sr_conns, scp);
+
+ while ((sqp = list_remove_head(&scp->sc_queries)) != NULL) {
+
+ if (first == B_TRUE) {
+ sqp->sq_status = SVP_S_FATAL;
+ sqp->sq_func(sqp, sqp->sq_arg);
+ continue;
+ }
+
+ sqp->sq_acttime = -1;
+
+ /*
+ * We may want to maintain a queue of these for some time rather
+ * than just failing them all.
+ */
+ if (svp_remote_conn_queue(srp, sqp) == B_FALSE) {
+ sqp->sq_status = SVP_S_FATAL;
+ sqp->sq_func(sqp, sqp->sq_arg);
+ }
+ }
+
+ /*
+ * Now that we're done, go ahead and re-insert.
+ */
+ list_insert_tail(&srp->sr_conns, scp);
+}
+
+void
+svp_remote_degrade(svp_remote_t *srp, svp_degrade_state_t flag)
+{
+ int sf, nf;
+ char buf[256];
+
+ assert(MUTEX_HELD(&srp->sr_lock));
+
+ if (flag == SVP_RD_ALL || flag == 0)
+ libvarpd_panic("invalid flag passed to degrade");
+
+ if ((flag & srp->sr_degrade) != 0) {
+ return;
+ }
+
+ sf = ffs(srp->sr_degrade);
+ nf = ffs(flag);
+ srp->sr_degrade |= flag;
+ if (sf == 0 || sf > nf) {
+ svp_t *svp;
+ svp_remote_mkfmamsg(srp, flag, buf, sizeof (buf));
+
+ for (svp = avl_first(&srp->sr_tree); svp != NULL;
+ svp = AVL_NEXT(&srp->sr_tree, svp)) {
+ libvarpd_fma_degrade(svp->svp_hdl, buf);
+ }
+ }
+}
+
+void
+svp_remote_restore(svp_remote_t *srp, svp_degrade_state_t flag)
+{
+ int sf, nf;
+
+ assert(MUTEX_HELD(&srp->sr_lock));
+ sf = ffs(srp->sr_degrade);
+ if ((srp->sr_degrade & flag) != flag)
+ return;
+ srp->sr_degrade &= ~flag;
+ nf = ffs(srp->sr_degrade);
+
+ /*
+ * If we're now empty, restore the device. If we still are degraded, but
+ * we now have a higher base than we used to, change the message.
+ */
+ if (srp->sr_degrade == 0) {
+ svp_t *svp;
+ for (svp = avl_first(&srp->sr_tree); svp != NULL;
+ svp = AVL_NEXT(&srp->sr_tree, svp)) {
+ libvarpd_fma_restore(svp->svp_hdl);
+ }
+ } else if (nf != sf) {
+ svp_t *svp;
+ char buf[256];
+
+ svp_remote_mkfmamsg(srp, 1U << (nf - 1), buf, sizeof (buf));
+ for (svp = avl_first(&srp->sr_tree); svp != NULL;
+ svp = AVL_NEXT(&srp->sr_tree, svp)) {
+ libvarpd_fma_degrade(svp->svp_hdl, buf);
+ }
+ }
+}
+
+void
+svp_remote_shootdown_vl3_cb(svp_query_t *sqp, void *arg)
+{
+ svp_shoot_vl3_t *squery = arg;
+ svp_log_vl3_t *svl3 = squery->ssv_vl3;
+ svp_sdlog_t *sdl = squery->ssv_log;
+
+ if (sqp->sq_status == SVP_S_OK) {
+ svp_t *svp, lookup;
+
+ svp_remote_t *srp = sdl->sdl_remote;
+ svp_vl3_ack_t *vl3a = (svp_vl3_ack_t *)sqp->sq_wdata;
+
+ lookup.svp_vid = ntohl(svl3->svl3_vnetid);
+ mutex_enter(&srp->sr_lock);
+ if ((svp = avl_find(&srp->sr_tree, &lookup, NULL)) != NULL) {
+ svp->svp_cb.scb_vl3_inject(svp, ntohs(svl3->svl3_vlan),
+ (struct in6_addr *)svl3->svl3_ip, vl3a->sl3a_mac,
+ NULL);
+ }
+ mutex_exit(&srp->sr_lock);
+
+ }
+
+ svp_shootdown_vl3_cb(sqp->sq_status, svl3, sdl);
+
+ umem_free(squery, sizeof (svp_shoot_vl3_t));
+}
+
+void
+svp_remote_shootdown_vl3(svp_remote_t *srp, svp_log_vl3_t *svl3,
+ svp_sdlog_t *sdl)
+{
+ svp_shoot_vl3_t *squery;
+
+ squery = umem_zalloc(sizeof (svp_shoot_vl3_t), UMEM_DEFAULT);
+ if (squery == NULL) {
+ svp_shootdown_vl3_cb(SVP_S_FATAL, svl3, sdl);
+ return;
+ }
+
+ squery->ssv_vl3 = svl3;
+ squery->ssv_log = sdl;
+ squery->ssv_sock.sin6_family = AF_INET6;
+ bcopy(svl3->svl3_ip, &squery->ssv_sock.sin6_addr,
+ sizeof (svl3->svl3_ip));
+ svp_remote_vl3_logreq(srp, &squery->ssv_query, ntohl(svl3->svl3_vnetid),
+ (struct sockaddr *)&squery->ssv_sock, svp_remote_shootdown_vl3_cb,
+ squery);
+}
+
+void
+svp_remote_shootdown_vl2(svp_remote_t *srp, svp_log_vl2_t *svl2)
+{
+ svp_t *svp, lookup;
+
+ lookup.svp_vid = ntohl(svl2->svl2_vnetid);
+ mutex_enter(&srp->sr_lock);
+ if ((svp = avl_find(&srp->sr_tree, &lookup, NULL)) != NULL) {
+ svp->svp_cb.scb_vl2_invalidate(svp, svl2->svl2_mac);
+ }
+ mutex_exit(&srp->sr_lock);
+}
+
+int
+svp_remote_init(void)
+{
+ svp_idspace = id_space_create("svp_req_ids", 1, INT32_MAX);
+ if (svp_idspace == NULL)
+ return (errno);
+ avl_create(&svp_remote_tree, svp_remote_comparator,
+ sizeof (svp_remote_t), offsetof(svp_remote_t, sr_gnode));
+ svp_dns_timer.st_func = svp_remote_dns_timer;
+ svp_dns_timer.st_arg = NULL;
+ svp_dns_timer.st_oneshot = B_FALSE;
+ svp_dns_timer.st_value = svp_dns_timer_rate;
+ svp_timer_add(&svp_dns_timer);
+ return (0);
+}
+
+void
+svp_remote_fini(void)
+{
+ svp_timer_remove(&svp_dns_timer);
+ avl_destroy(&svp_remote_tree);
+ if (svp_idspace == NULL)
+ id_space_destroy(svp_idspace);
+}
diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_shootdown.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_shootdown.c
new file mode 100644
index 0000000000..76afb2519f
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_shootdown.c
@@ -0,0 +1,474 @@
+/*
+ * 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.
+ */
+
+/*
+ * Shootdown processing logic.
+ *
+ * For more information, see the big theory statement in
+ * lib/varpd/svp/common/libvarpd_svp.c.
+ */
+
+#include <umem.h>
+#include <sys/uuid.h>
+#include <assert.h>
+#include <strings.h>
+#include <errno.h>
+#include <sys/debug.h>
+
+#include <libvarpd_provider.h>
+#include <libvarpd_svp.h>
+
+/*
+ * When we've determined that there's nothing left for us to do, then we go
+ * ahead and wait svp_shootdown_base seconds + up to an additional
+ * svp_shootdown_base seconds before asking again. However, if there is actually
+ * some work going on, just use the svp_shootdown_cont time.
+ */
+static int svp_shootdown_base = 5;
+static int svp_shootdown_cont = 1;
+
+/*
+ * These are sizes for our logack and logrm buffers. The sizing of the shootdown
+ * buffere would give us approximately 18 or so VL3 entries and 32 VL2 entries
+ * or some combination thereof. While it's a bit of overkill, we just use the
+ * same sized buffer for the list of uuids that we pass to remove log entries
+ * that we've acted upon.
+ */
+static int svp_shootdown_buf = 1024;
+
+static void
+svp_shootdown_schedule(svp_sdlog_t *sdl, boolean_t cont)
+{
+ assert(MUTEX_HELD(&sdl->sdl_lock));
+
+ if (cont == B_TRUE) {
+ sdl->sdl_timer.st_value = svp_shootdown_cont;
+ } else {
+ sdl->sdl_timer.st_value = svp_shootdown_base +
+ arc4random_uniform(svp_shootdown_base + 1);
+ }
+ svp_timer_add(&sdl->sdl_timer);
+}
+
+void
+svp_shootdown_lrm_cb(svp_remote_t *srp, svp_status_t status)
+{
+ svp_sdlog_t *sdl = &srp->sr_shoot;
+
+ mutex_enter(&sdl->sdl_lock);
+ sdl->sdl_flags &= ~SVP_SD_RUNNING;
+ svp_shootdown_schedule(sdl, B_TRUE);
+ mutex_exit(&sdl->sdl_lock);
+
+ if (status != SVP_S_OK) {
+ (void) bunyan_warn(svp_bunyan, "SVP_R_LOG_RM failed",
+ BUNYAN_T_STRING, "remote_host", srp->sr_hostname,
+ BUNYAN_T_INT32, "remote_port", srp->sr_rport,
+ BUNYAN_T_INT32, "status", status,
+ BUNYAN_T_END);
+ }
+}
+
+static void
+svp_shootdown_ref(svp_sdlog_t *sdl)
+{
+ mutex_enter(&sdl->sdl_lock);
+ sdl->sdl_ref++;
+ mutex_exit(&sdl->sdl_lock);
+}
+
+static void
+svp_shootdown_rele(svp_sdlog_t *sdl)
+{
+ svp_lrm_req_t *svrr = sdl->sdl_logrm;
+ boolean_t next;
+
+ mutex_enter(&sdl->sdl_lock);
+ VERIFY(sdl->sdl_ref > 0);
+ sdl->sdl_ref--;
+ if (sdl->sdl_ref > 0) {
+ mutex_exit(&sdl->sdl_lock);
+ return;
+ }
+
+ /*
+ * At this point we know that we hold the last reference, therefore it's
+ * safe for us to go ahead and clean up and move on and attempt to
+ * deliver the reply. We always deliver the reply by going through the
+ * timer. This can be rather important as the final reference may be
+ * coming through a failed query and it's not always safe for us to
+ * callback into the remote routines from this context.
+ *
+ * We should only do this if we have a non-zero number of entries to
+ * take down.
+ */
+ sdl->sdl_flags &= ~SVP_SD_RUNNING;
+ if (svrr->svrr_count > 0) {
+ sdl->sdl_flags |= SVP_SD_DORM;
+ next = B_TRUE;
+ } else {
+ next = B_FALSE;
+ }
+ svp_shootdown_schedule(sdl, next);
+ mutex_exit(&sdl->sdl_lock);
+}
+
+/*
+ * This is a callback used to indicate that the VL3 lookup has completed and an
+ * entry, if any, has been injected. If the command succeeded, eg. we got that
+ * the status was OK or that it was not found, then we will add it to he list to
+ * shoot down. Otherwise, there's nothing else for us to really do here.
+ */
+void
+svp_shootdown_vl3_cb(svp_status_t status, svp_log_vl3_t *vl3, svp_sdlog_t *sdl)
+{
+ svp_lrm_req_t *svrr = sdl->sdl_logrm;
+
+ mutex_enter(&sdl->sdl_lock);
+ if (status == SVP_S_OK || status == SVP_S_NOTFOUND) {
+ bcopy(vl3->svl3_id, &svrr->svrr_ids[svrr->svrr_count * 16],
+ UUID_LEN);
+ svrr->svrr_count++;
+ }
+ mutex_exit(&sdl->sdl_lock);
+
+ svp_shootdown_rele(sdl);
+}
+
+static int
+svp_shootdown_logr_shoot(void *data, svp_log_type_t type, void *arg)
+{
+ svp_sdlog_t *sdl = arg;
+ svp_remote_t *srp = sdl->sdl_remote;
+ svp_lrm_req_t *svrr = sdl->sdl_logrm;
+
+ if (type != SVP_LOG_VL2 && type != SVP_LOG_VL3)
+ libvarpd_panic("encountered unknown type: %d\n", type);
+
+ if (type == SVP_LOG_VL2) {
+ svp_log_vl2_t *svl2 = data;
+ svp_remote_shootdown_vl2(srp, svl2);
+ mutex_enter(&sdl->sdl_lock);
+ bcopy(svl2->svl2_id, &svrr->svrr_ids[svrr->svrr_count * 16],
+ UUID_LEN);
+ svrr->svrr_count++;
+ mutex_exit(&sdl->sdl_lock);
+ } else {
+ svp_log_vl3_t *svl3 = data;
+
+ /* Take a hold for the duration of this request */
+ svp_shootdown_ref(sdl);
+ svp_remote_shootdown_vl3(srp, svl3, sdl);
+ }
+
+ return (0);
+}
+
+static int
+svp_shootdown_logr_count(void *data, svp_log_type_t type, void *arg)
+{
+ uint_t *u = arg;
+ *u = *u + 1;
+ return (0);
+}
+
+
+static int
+svp_shootdown_logr_iter(svp_remote_t *srp, void *buf, size_t len,
+ int (*cb)(void *, svp_log_type_t, void *), void *arg)
+{
+ int ret;
+ off_t cboff = 0;
+ uint32_t *typep, type;
+ svp_log_vl2_t *svl2;
+ svp_log_vl3_t *svl3;
+
+ /* Adjust for initial status word */
+ assert(len >= sizeof (uint32_t));
+ len -= sizeof (uint32_t);
+ cboff += sizeof (uint32_t);
+
+ while (len > 0) {
+ size_t opsz;
+
+ if (len < sizeof (uint32_t)) {
+ (void) bunyan_warn(svp_bunyan,
+ "failed to get initial shootdown tag",
+ BUNYAN_T_STRING, "remote_host", srp->sr_hostname,
+ BUNYAN_T_INT32, "remote_port", srp->sr_rport,
+ BUNYAN_T_INT32, "response_size", cboff + len,
+ BUNYAN_T_INT32, "response_offset", cboff,
+ BUNYAN_T_END);
+ return (-1);
+ }
+
+ typep = buf + cboff;
+ type = ntohl(*typep);
+ if (type == SVP_LOG_VL2) {
+ opsz = sizeof (svp_log_vl2_t);
+ if (len < opsz) {
+ (void) bunyan_warn(svp_bunyan,
+ "not enough data for svp_log_vl2_t",
+ BUNYAN_T_STRING, "remote_host",
+ srp->sr_hostname,
+ BUNYAN_T_INT32, "remote_port",
+ srp->sr_rport,
+ BUNYAN_T_INT32, "response_size",
+ cboff + len,
+ BUNYAN_T_INT32, "response_offset", cboff,
+ BUNYAN_T_END);
+ return (-1);
+ }
+ svl2 = (void *)typep;
+ if ((ret = cb(svl2, type, arg)) != 0)
+ return (ret);
+ } else if (type == SVP_LOG_VL3) {
+
+ opsz = sizeof (svp_log_vl3_t);
+ if (len < opsz) {
+ (void) bunyan_warn(svp_bunyan,
+ "not enough data for svp_log_vl3_t",
+ BUNYAN_T_STRING, "remote_host",
+ srp->sr_hostname,
+ BUNYAN_T_INT32, "remote_port",
+ srp->sr_rport,
+ BUNYAN_T_INT32, "response_size",
+ cboff + len,
+ BUNYAN_T_INT32, "response_offset", cboff,
+ BUNYAN_T_END);
+ return (-1);
+ }
+ svl3 = (void *)typep;
+ if ((ret = cb(svl3, type, arg)) != 0)
+ return (ret);
+ } else {
+ (void) bunyan_warn(svp_bunyan,
+ "unknown log structure type",
+ BUNYAN_T_STRING, "remote_host",
+ srp->sr_hostname,
+ BUNYAN_T_INT32, "remote_port", srp->sr_rport,
+ BUNYAN_T_INT32, "response_size", cboff + len,
+ BUNYAN_T_INT32, "response_offset", cboff,
+ BUNYAN_T_INT32, "structure_type", type,
+ BUNYAN_T_END);
+ return (-1);
+ }
+ len -= opsz;
+ cboff += opsz;
+ }
+
+ return (0);
+}
+
+void
+svp_shootdown_logr_cb(svp_remote_t *srp, svp_status_t status, void *cbdata,
+ size_t cbsize)
+{
+ uint_t count;
+ svp_sdlog_t *sdl = &srp->sr_shoot;
+
+ if (status != SVP_S_OK) {
+ (void) bunyan_warn(svp_bunyan,
+ "log request not OK",
+ BUNYAN_T_STRING, "remote_host", srp->sr_hostname,
+ BUNYAN_T_INT32, "remote_port", srp->sr_rport,
+ BUNYAN_T_INT32, "response_size", cbsize,
+ BUNYAN_T_INT32, "status", status,
+ BUNYAN_T_END);
+ mutex_enter(&sdl->sdl_lock);
+ sdl->sdl_flags &= ~SVP_SD_RUNNING;
+ svp_shootdown_schedule(sdl, B_FALSE);
+ mutex_exit(&sdl->sdl_lock);
+ return;
+ }
+
+ /*
+ * First go ahead and count the number of entries. This effectively
+ * allows us to validate that all the data is valid, if this fails, then
+ * we fail the request.
+ */
+ count = 0;
+ if ((svp_shootdown_logr_iter(srp, cbdata, cbsize,
+ svp_shootdown_logr_count, &count)) != 0) {
+ mutex_enter(&sdl->sdl_lock);
+ sdl->sdl_flags &= ~SVP_SD_RUNNING;
+ svp_shootdown_schedule(sdl, B_FALSE);
+ mutex_exit(&sdl->sdl_lock);
+ return;
+ }
+
+ /*
+ * If we have no entries, then we're also done.
+ */
+ if (count == 0) {
+ mutex_enter(&sdl->sdl_lock);
+ sdl->sdl_flags &= ~SVP_SD_RUNNING;
+ svp_shootdown_schedule(sdl, B_FALSE);
+ mutex_exit(&sdl->sdl_lock);
+ return;
+ }
+
+ /*
+ * We have work to do. Because we may have asynchronous VL3 tasks, we're
+ * going to first grab a reference before we do the iteration. Then, for
+ * each asynchronous VL3 request we make, that'll also grab a hold. Once
+ * we're done with the iteration, we'll drop our hold. If that's the
+ * last one, it'll move on accordingly.
+ */
+ svp_shootdown_ref(sdl);
+ bzero(sdl->sdl_logrm, svp_shootdown_buf);
+
+ /*
+ * If this fails, we're going to determine what to do next based on the
+ * number of entries that were entered into the log removal. At this
+ * point success or failure don't really look different, all it changes
+ * is how many entries we have to remove.
+ */
+ (void) svp_shootdown_logr_iter(srp, cbdata, cbsize,
+ svp_shootdown_logr_shoot, sdl);
+
+ /*
+ * Now that we're done with our work, release the hold. If we don't have
+ * any vl3 tasks outstanding, this'll trigger the next phase of the log
+ * removals.
+ */
+ svp_shootdown_rele(sdl);
+}
+
+static void
+svp_shootdown_timer(void *arg)
+{
+ svp_sdlog_t *sdl = arg;
+ svp_remote_t *srp = sdl->sdl_remote;
+ boolean_t init = B_TRUE;
+
+ mutex_enter(&sdl->sdl_lock);
+
+ /*
+ * If we've been asked to quiesce, we're done.
+ */
+ if ((sdl->sdl_flags & SVP_SD_QUIESCE) != 0) {
+ mutex_exit(&sdl->sdl_lock);
+ return;
+ }
+
+ /*
+ * We shouldn't be able to have ourselves currently be running and reach
+ * here. If that's the case, we should immediately panic.
+ */
+ if ((sdl->sdl_flags & SVP_SD_RUNNING) != 0) {
+ libvarpd_panic("remote %p shootdown timer fired while still "
+ "running", srp);
+ }
+
+ if ((sdl->sdl_flags & SVP_SD_DORM) != 0) {
+ sdl->sdl_flags &= ~SVP_SD_DORM;
+ init = B_FALSE;
+ }
+
+ sdl->sdl_flags |= SVP_SD_RUNNING;
+ mutex_exit(&sdl->sdl_lock);
+
+ if (init == B_FALSE) {
+ svp_lrm_req_t *svrr = sdl->sdl_logrm;
+
+ bzero(&sdl->sdl_query, sizeof (svp_query_t));
+ svp_remote_lrm_request(sdl->sdl_remote, &sdl->sdl_query, svrr,
+ sizeof (*svrr) + 16 * svrr->svrr_count);
+ } else {
+ bzero(&sdl->sdl_query, sizeof (svp_query_t));
+ svp_remote_log_request(srp, &sdl->sdl_query, sdl->sdl_logack,
+ svp_shootdown_buf);
+ }
+}
+
+void
+svp_shootdown_fini(svp_remote_t *srp)
+{
+ svp_sdlog_t *sdl = &srp->sr_shoot;
+
+ mutex_enter(&sdl->sdl_lock);
+ sdl->sdl_flags |= SVP_SD_QUIESCE;
+ mutex_exit(&sdl->sdl_lock);
+
+ svp_timer_remove(&sdl->sdl_timer);
+
+ mutex_enter(&sdl->sdl_lock);
+
+ /*
+ * Normally svp_timer_remove would be enough. However, the query could
+ * have been put out again outside of the svp_timer interface. Therefore
+ * we still need to check for SVP_SD_RUNNING.
+ */
+ while (sdl->sdl_flags & SVP_SD_RUNNING)
+ (void) cond_wait(&sdl->sdl_cond, &sdl->sdl_lock);
+ mutex_exit(&sdl->sdl_lock);
+
+ umem_free(sdl->sdl_logack, svp_shootdown_buf);
+ umem_free(sdl->sdl_logrm, svp_shootdown_buf);
+ sdl->sdl_logack = NULL;
+ sdl->sdl_logrm = NULL;
+ (void) cond_destroy(&sdl->sdl_cond);
+ (void) mutex_destroy(&sdl->sdl_lock);
+}
+
+void
+svp_shootdown_start(svp_remote_t *srp)
+{
+ svp_sdlog_t *sdl = &srp->sr_shoot;
+
+ mutex_enter(&sdl->sdl_lock);
+ svp_shootdown_schedule(sdl, B_FALSE);
+ mutex_exit(&sdl->sdl_lock);
+}
+
+int
+svp_shootdown_init(svp_remote_t *srp)
+{
+ int ret;
+ svp_sdlog_t *sdl = &srp->sr_shoot;
+ if ((ret = mutex_init(&sdl->sdl_lock, USYNC_THREAD | LOCK_ERRORCHECK,
+ NULL)) != 0)
+ return (ret);
+
+ if ((ret = cond_init(&sdl->sdl_cond, USYNC_THREAD, NULL)) != 0) {
+ (void) mutex_destroy(&sdl->sdl_lock);
+ return (ret);
+ }
+
+ if ((sdl->sdl_logack = umem_alloc(svp_shootdown_buf, UMEM_DEFAULT)) ==
+ NULL) {
+ ret = errno;
+ (void) cond_destroy(&sdl->sdl_cond);
+ (void) mutex_destroy(&sdl->sdl_lock);
+ return (ret);
+ }
+
+ if ((sdl->sdl_logrm = umem_alloc(svp_shootdown_buf, UMEM_DEFAULT)) ==
+ NULL) {
+ ret = errno;
+ umem_free(sdl->sdl_logack, svp_shootdown_buf);
+ (void) cond_destroy(&sdl->sdl_cond);
+ (void) mutex_destroy(&sdl->sdl_lock);
+ return (ret);
+ }
+
+ sdl->sdl_remote = srp;
+ sdl->sdl_timer.st_oneshot = B_TRUE;
+ sdl->sdl_timer.st_func = svp_shootdown_timer;
+ sdl->sdl_timer.st_arg = sdl;
+
+ return (0);
+}
diff --git a/usr/src/lib/varpd/svp/common/libvarpd_svp_timer.c b/usr/src/lib/varpd/svp/common/libvarpd_svp_timer.c
new file mode 100644
index 0000000000..10b02748f3
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/libvarpd_svp_timer.c
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+#include <stddef.h>
+#include <libvarpd_svp.h>
+
+/*
+ * svp timer backend
+ *
+ * This implements all of the logic of maintaining a timer for the svp backend.
+ * We have a timer that fires at a one second tick. We maintain all of our
+ * events in avl tree, sorted by the tick that they need to be processed at.
+ *
+ * For more information, see the big theory statement in
+ * lib/varpd/svp/common/libvarpd_svp.c.
+ */
+
+int svp_tickrate = 1;
+static svp_event_t svp_timer_event;
+static mutex_t svp_timer_lock = ERRORCHECKMUTEX;
+static cond_t svp_timer_cv = DEFAULTCV;
+static avl_tree_t svp_timer_tree;
+static uint64_t svp_timer_nticks;
+
+static int
+svp_timer_comparator(const void *l, const void *r)
+{
+ const svp_timer_t *lt, *rt;
+
+ lt = l;
+ rt = r;
+
+ if (lt->st_expire > rt->st_expire)
+ return (1);
+ else if (lt->st_expire < rt->st_expire)
+ return (-1);
+
+ /*
+ * Multiple timers can have the same delivery time, so sort within that
+ * by the address of the timer itself.
+ */
+ if ((uintptr_t)lt > (uintptr_t)rt)
+ return (1);
+ else if ((uintptr_t)lt < (uintptr_t)rt)
+ return (-1);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+svp_timer_tick(port_event_t *pe, void *arg)
+{
+ mutex_enter(&svp_timer_lock);
+ svp_timer_nticks++;
+
+ for (;;) {
+ svp_timer_t *t;
+
+ t = avl_first(&svp_timer_tree);
+ if (t == NULL || t->st_expire > svp_timer_nticks)
+ break;
+
+ avl_remove(&svp_timer_tree, t);
+
+ /*
+ * We drop this while performing an operation so that way state
+ * can advance in the face of a long-running callback.
+ */
+ t->st_delivering = B_TRUE;
+ mutex_exit(&svp_timer_lock);
+ t->st_func(t->st_arg);
+ mutex_enter(&svp_timer_lock);
+ t->st_delivering = B_FALSE;
+ (void) cond_broadcast(&svp_timer_cv);
+ if (t->st_oneshot == B_FALSE) {
+ t->st_expire += t->st_value;
+ avl_add(&svp_timer_tree, t);
+ }
+ }
+ mutex_exit(&svp_timer_lock);
+}
+
+void
+svp_timer_add(svp_timer_t *stp)
+{
+ if (stp->st_value == 0)
+ libvarpd_panic("tried to add svp timer with zero value");
+
+ mutex_enter(&svp_timer_lock);
+ stp->st_delivering = B_FALSE;
+ stp->st_expire = svp_timer_nticks + stp->st_value;
+ avl_add(&svp_timer_tree, stp);
+ mutex_exit(&svp_timer_lock);
+}
+
+void
+svp_timer_remove(svp_timer_t *stp)
+{
+ mutex_enter(&svp_timer_lock);
+
+ /*
+ * If the event in question is not currently being delivered, then we
+ * can stop it before it next fires. If it is currently being delivered,
+ * we need to wait for that to finish. Because we hold the timer lock,
+ * we know that it cannot be rearmed. Therefore, we make sure the one
+ * shot is set to zero, and wait until it's no longer set to delivering.
+ */
+ if (stp->st_delivering == B_FALSE) {
+ avl_remove(&svp_timer_tree, stp);
+ mutex_exit(&svp_timer_lock);
+ return;
+ }
+
+ stp->st_oneshot = B_TRUE;
+ while (stp->st_delivering == B_TRUE)
+ (void) cond_wait(&svp_timer_cv, &svp_timer_lock);
+
+ mutex_exit(&svp_timer_lock);
+}
+
+int
+svp_timer_init(void)
+{
+ int ret;
+
+ svp_timer_event.se_func = svp_timer_tick;
+ svp_timer_event.se_arg = NULL;
+
+ avl_create(&svp_timer_tree, svp_timer_comparator, sizeof (svp_timer_t),
+ offsetof(svp_timer_t, st_link));
+
+ if ((ret = svp_event_timer_init(&svp_timer_event)) != 0) {
+ avl_destroy(&svp_timer_tree);
+ }
+
+ return (ret);
+}
diff --git a/usr/src/lib/varpd/svp/common/llib-lvarpd_svp b/usr/src/lib/varpd/svp/common/llib-lvarpd_svp
new file mode 100644
index 0000000000..03c34f4fcb
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/llib-lvarpd_svp
@@ -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 2015 Joyent, Inc.
+ */
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
diff --git a/usr/src/lib/varpd/svp/common/mapfile-vers b/usr/src/lib/varpd/svp/common/mapfile-vers
new file mode 100644
index 0000000000..6b7c5a5067
--- /dev/null
+++ b/usr/src/lib/varpd/svp/common/mapfile-vers
@@ -0,0 +1,35 @@
+#
+# 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.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING: STOP NOW. DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+# usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION SUNWprivate {
+ local:
+ *;
+};
diff --git a/usr/src/lib/varpd/svp/i386/Makefile b/usr/src/lib/varpd/svp/i386/Makefile
new file mode 100644
index 0000000000..f2b4f63da5
--- /dev/null
+++ b/usr/src/lib/varpd/svp/i386/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 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/varpd/svp/sparc/Makefile b/usr/src/lib/varpd/svp/sparc/Makefile
new file mode 100644
index 0000000000..f2b4f63da5
--- /dev/null
+++ b/usr/src/lib/varpd/svp/sparc/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 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/varpd/svp/sparcv9/Makefile b/usr/src/lib/varpd/svp/sparcv9/Makefile
new file mode 100644
index 0000000000..d552642882
--- /dev/null
+++ b/usr/src/lib/varpd/svp/sparcv9/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 2015 Joyent, Inc.
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/xml/os_dtd.c b/usr/src/lib/xml/os_dtd.c
new file mode 100644
index 0000000000..579e99ba3c
--- /dev/null
+++ b/usr/src/lib/xml/os_dtd.c
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ */
+
+/*
+ * Utility functions for working with XML documents that are validated against
+ * Document Type Definitions (DTD) shipped in the operating system.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <zone.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include "os_dtd.h"
+
+struct os_dtd_path {
+ os_dtd_id_t odp_id;
+ const char *odp_name;
+ const char *odp_public_ident;
+ const char *odp_path;
+};
+
+static os_dtd_path_t os_dtd_paths[] = {
+ /*
+ * DTDs for Zones infrastructure:
+ */
+ { OS_DTD_ZONES_BRAND, "brand",
+ "-//Sun Microsystems Inc//DTD Brands//EN",
+ "/usr/share/lib/xml/dtd/brand.dtd.1" },
+ { OS_DTD_ZONES_ZONE, "zone",
+ "-//Sun Microsystems Inc//DTD Zones//EN",
+ "/usr/share/lib/xml/dtd/zonecfg.dtd.1" },
+ { OS_DTD_ZONES_PLATFORM, "platform",
+ "-//Sun Microsystems Inc//Zones Platform//EN",
+ "/usr/share/lib/xml/dtd/zone_platform.dtd.1" },
+
+ /*
+ * DTDs for smf(5):
+ */
+ { OS_DTD_SMF_SERVICE_BUNDLE, "service_bundle",
+ NULL,
+ "/usr/share/lib/xml/dtd/service_bundle.dtd.1" },
+
+ { OS_DTD_UNKNOWN, NULL, NULL, NULL }
+};
+
+/*
+ * Check this document to see if it references the public identifier of a
+ * well-known DTD that we ship with the operating system. If there is no DTD,
+ * or the public identifier is unknown to us, return OS_DTD_UNKNOWN.
+ */
+os_dtd_id_t
+os_dtd_identify(xmlDocPtr doc)
+{
+ xmlDtdPtr dp;
+ int i;
+
+ if ((dp = xmlGetIntSubset(doc)) == NULL) {
+ /*
+ * This document does not have an internal subset pointing
+ * to a DTD.
+ */
+ errno = EIO;
+ return (OS_DTD_UNKNOWN);
+ }
+
+ /*
+ * The use of a symbolic name via the public identifier is preferred.
+ * Check to see if the document refers to a public identifier for any
+ * well-known DTD:
+ */
+ for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) {
+ os_dtd_path_t *odp = &os_dtd_paths[i];
+ const xmlChar *pubid = (const xmlChar *)odp->odp_public_ident;
+
+ if (dp->ExternalID == NULL || odp->odp_public_ident == NULL) {
+ continue;
+ }
+
+ if (xmlStrEqual(pubid, dp->ExternalID)) {
+ return (odp->odp_id);
+ }
+ }
+
+ /*
+ * If a public identifier is not present, or does not match any known
+ * DTD, fall back to inspecting the system identifier.
+ */
+ for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) {
+ os_dtd_path_t *odp = &os_dtd_paths[i];
+ char uri[sizeof ("file://") + MAXPATHLEN];
+ const xmlChar *path = (const xmlChar *)odp->odp_path;
+
+ if (dp->SystemID == NULL || odp->odp_path == NULL) {
+ continue;
+ }
+
+ /*
+ * The system identifier may be a regular path.
+ */
+ if (xmlStrEqual(path, dp->SystemID)) {
+ return (odp->odp_id);
+ }
+
+ /*
+ * The system identifier may also be a "file://"
+ * URI referring to a path:
+ */
+ (void) snprintf(uri, sizeof (uri), "file://%s", odp->odp_path);
+ if (xmlStrEqual((const xmlChar *)uri, dp->SystemID)) {
+ return (odp->odp_id);
+ }
+ }
+
+ errno = ENOENT;
+ return (OS_DTD_UNKNOWN);
+}
+
+static os_dtd_path_t *
+os_dtd_lookup(os_dtd_id_t id)
+{
+ int i;
+
+ for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) {
+ os_dtd_path_t *odp = &os_dtd_paths[i];
+
+ if (odp->odp_id == id) {
+ return (odp);
+ }
+ }
+
+ errno = ENOENT;
+ return (NULL);
+}
+
+/*
+ * If this document references a DTD, remove that reference (the "internal
+ * subset"). Install a new internal subset reference to the well-known
+ * operating system DTD passed by the caller. The URI in this reference will
+ * respect the current native system prefix (e.g. "/native") if there is one,
+ * such as when running in a branded zone.
+ */
+int
+os_dtd_attach(xmlDocPtr doc, os_dtd_id_t id)
+{
+ xmlDtdPtr dp;
+ os_dtd_path_t *odp;
+ const char *zroot = zone_get_nroot();
+ char uri[sizeof ("file://") + MAXPATHLEN];
+
+ if ((odp = os_dtd_lookup(id)) == NULL) {
+ return (-1);
+ }
+
+ if ((dp = xmlGetIntSubset(doc)) != NULL) {
+ /*
+ * This document already has an internal subset. Remove it
+ * before attaching the new one.
+ */
+ xmlUnlinkNode((xmlNodePtr)dp);
+ xmlFreeNode((xmlNodePtr)dp);
+ }
+
+ /*
+ * The "system identifier" of this internal subset must refer to the
+ * path in the filesystem where the DTD file (the external subset) is
+ * stored. If we are running in a branded zone, that file may be at a
+ * different path (e.g. under "/native").
+ */
+ (void) snprintf(uri, sizeof (uri), "file://%s%s", zroot != NULL ?
+ zroot : "", odp->odp_path);
+
+ if (xmlCreateIntSubset(doc, (const xmlChar *)odp->odp_name,
+ (const xmlChar *)odp->odp_public_ident,
+ (const xmlChar *)uri) == NULL) {
+ errno = EIO;
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static void
+os_dtd_print_nothing(void *ctx, const char *msg, ...)
+{
+}
+
+int
+os_dtd_validate(xmlDocPtr doc, boolean_t emit_messages, boolean_t *valid)
+{
+ int ret;
+ xmlValidCtxtPtr cvp;
+ os_dtd_id_t dtdid;
+
+ if ((dtdid = os_dtd_identify(doc)) != OS_DTD_UNKNOWN) {
+ /*
+ * This document refers to a well-known DTD shipped with
+ * the operating system. Ensure that it points to the
+ * correct local path for validation in the current context.
+ */
+ if (os_dtd_attach(doc, dtdid) != 0) {
+ return (-1);
+ }
+ }
+
+ if ((cvp = xmlNewValidCtxt()) == NULL) {
+ return (-1);
+ }
+
+ if (!emit_messages) {
+ cvp->error = os_dtd_print_nothing;
+ cvp->warning = os_dtd_print_nothing;
+ }
+
+ ret = xmlValidateDocument(cvp, doc);
+ xmlFreeValidCtxt(cvp);
+
+ *valid = (ret == 1 ? B_TRUE : B_FALSE);
+ return (0);
+}
diff --git a/usr/src/lib/xml/os_dtd.h b/usr/src/lib/xml/os_dtd.h
new file mode 100644
index 0000000000..c6fe24a293
--- /dev/null
+++ b/usr/src/lib/xml/os_dtd.h
@@ -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 2015 Joyent, Inc.
+ */
+
+#ifndef _OS_DTD_H
+#define _OS_DTD_H
+
+/*
+ * Utility functions for working with XML documents that are validated against
+ * Document Type Definitions (DTD) shipped in the operating system.
+ */
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum os_dtd_id {
+ OS_DTD_UNKNOWN = 0,
+ OS_DTD_ZONES_BRAND,
+ OS_DTD_ZONES_ZONE,
+ OS_DTD_ZONES_PLATFORM,
+ OS_DTD_SMF_SERVICE_BUNDLE
+} os_dtd_id_t;
+
+typedef struct os_dtd_path os_dtd_path_t;
+
+extern os_dtd_id_t os_dtd_identify(xmlDocPtr);
+extern int os_dtd_attach(xmlDocPtr, os_dtd_id_t);
+extern int os_dtd_validate(xmlDocPtr, boolean_t, boolean_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OS_DTD_H */
diff --git a/usr/src/man/Makefile b/usr/src/man/Makefile
index fd58b2ab30..d629a7cd39 100644
--- a/usr/src/man/Makefile
+++ b/usr/src/man/Makefile
@@ -84,6 +84,7 @@ SUBDIRS= man1 \
man3utempter \
man3uuid \
man3volmgt \
+ man3vnd \
man3xcurses \
man3xnet \
man4 \
diff --git a/usr/src/man/man1/Makefile b/usr/src/man/man1/Makefile
index df95ce213d..68647ffc09 100644
--- a/usr/src/man/man1/Makefile
+++ b/usr/src/man/man1/Makefile
@@ -74,6 +74,7 @@ MANFILES= acctcom.1 \
clear.1 \
cmp.1 \
col.1 \
+ column.1 \
comm.1 \
command.1 \
compress.1 \
@@ -220,6 +221,7 @@ MANFILES= acctcom.1 \
m4.1 \
mac.1 \
mach.1 \
+ machid.1 \
madv.so.1.1 \
mail.1 \
mailcompat.1 \
@@ -467,6 +469,9 @@ MANLINKS= batch.1 \
helpuid.1 \
helpyorn.1 \
hist.1 \
+ i286.1 \
+ i386.1 \
+ i486.1 \
if.1 \
intro.1 \
jsh.1 \
@@ -514,9 +519,11 @@ MANLINKS= batch.1 \
settime.1 \
sh.1 \
source.1 \
+ sparc.1 \
spellin.1 \
stop.1 \
strconf.1 \
+ sun.1 \
switch.1 \
ulimit.1 \
unalias.1 \
@@ -655,6 +662,12 @@ unlimit.1 := LINKSRC = limit.1
dumpkeys.1 := LINKSRC = loadkeys.1
+i286.1 := LINKSRC = machid.1
+i386.1 := LINKSRC = machid.1
+i486.1 := LINKSRC = machid.1
+sparc.1 := LINKSRC = machid.1
+sun.1 := LINKSRC = machid.1
+
rmail.1 := LINKSRC = mail.1
page.1 := LINKSRC = more.1
@@ -710,7 +723,6 @@ hashcheck.1 := LINKSRC = spell.1
hashmake.1 := LINKSRC = spell.1
spellin.1 := LINKSRC = spell.1
-
strconf.1 := LINKSRC = strchg.1
settime.1 := LINKSRC = touch.1
diff --git a/usr/src/man/man1/column.1 b/usr/src/man/man1/column.1
new file mode 100644
index 0000000000..a8c23310ba
--- /dev/null
+++ b/usr/src/man/man1/column.1
@@ -0,0 +1,129 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" 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.
+.\"
+.\" @(#)column.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.\" Portions Copyright (c) 2013 Joyent, Inc. All rights reserved.
+.\"
+.TH COLUMN 1 "Jan 10, 2013"
+.SH NAME
+column \- columnate lists
+.SH SYNOPSIS
+.LP
+.nf
+\fBcolumn\fR [\fB-tx\fR] [\fB-c\fR \fIcolumns\fR] [\fB-s\fR \fIsep\fR] [\fIfile\fR ... ]
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The \fBcolumn\fR
+utility formats its input into multiple columns.
+Rows are filled before columns.
+Input is taken from
+\fIfile\fR
+operands, or, by default, from the standard input.
+Empty lines are ignored.
+.SH OPTIONS
+.sp
+.LP
+The options are as follows:
+.sp
+.ne 2
+.na
+\fB\fB-c\fR \fIcolumns\fR\fR
+.ad
+.RS 17n
+Output is formatted for a display \fIcolumns\fR
+wide.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-s\fR \fIsep\fR\fR
+.ad
+.RS 17n
+Specify a set of characters to be used to delimit columns for the
+\fB-t\fR option.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-t\fR\fR
+.ad
+.RS 17n
+Determine the number of columns the input contains and create a table.
+Columns are delimited with whitespace, by default, or with the characters
+supplied using the \fBs\fR
+option.
+Useful for pretty-printing displays.
+.RE
+
+.sp
+.ne 2
+.na
+\fB-x\fR
+.ad
+.RS 17n
+Fill columns before filling rows.
+.RE
+
+.SH ENVIRONMENT
+The COLUMNS , LANG , LC_ALL
+and
+LC_CTYPE
+environment variables affect the execution of
+\fBcolumn\fR
+as described in
+\fBenviron\fR(5).
+
+.SH EXIT STATUS
+The \fBcolumn\fR utility exits 0 on success and >0 if an error occurs.
+
+.SH EXAMPLES
+.sp
+.in +2
+.nf
+(printf \&"PERM LINKS OWNER GROUP SIZE MONTH DAY \&"\ \&;\ \&\e
+printf \&"HH:MM/YEAR NAME\en\&"\ \&;\ \&\e
+ls -l \&| sed 1d) \&| column -t
+.fi
+.in -2
+.sp
+
+
+.SH SEE ALSO
+\fBls\fR(1), \fBpaste\fR(1), \fBsort\fR(1)
+
+.SH HISTORY
+The \fBcolumn\fR command appeared in 4.3BSD-Reno.
+
+.SH BUGS
+Input lines are limited to LINE_MAX bytes in length.
diff --git a/usr/src/man/man1/crontab.1 b/usr/src/man/man1/crontab.1
index 5017199ae9..4a5d196e7c 100644
--- a/usr/src/man/man1/crontab.1
+++ b/usr/src/man/man1/crontab.1
@@ -41,6 +41,8 @@
.\"
.\"
.\" Copyright 1989 AT&T
+.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright (c) 2011, Joyent, Inc. All Rights Reserved
.\" Portions Copyright (c) 1992, X/Open Company Limited All Rights Reserved
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
.\" Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
@@ -62,7 +64,7 @@ crontab \- user crontab file
.LP
.nf
-\fB/usr/bin/crontab\fR \fB-u\fR \fIusername\fR \fB{ -e | -l | -r }\fR
+\fB/usr/bin/crontab\fR \fB-u\fR \fIusername\fR \fB{ -e | -g | -l | -r }\fR
.fi
.LP
@@ -72,7 +74,7 @@ crontab \- user crontab file
.LP
.nf
-\fB/usr/xpg4/bin/crontab\fR \fB{ -e | -l | -r }\fR [\fIusername\fR]
+\fB/usr/xpg4/bin/crontab\fR \fB{ -e | -g | -l | -r }\fR [\fIusername\fR]
.fi
.LP
@@ -87,7 +89,7 @@ crontab \- user crontab file
.LP
.nf
-\fB/usr/xpg6/bin/crontab\fR \fB{ -e | -l | -r }\fR [\fIusername\fR]
+\fB/usr/xpg6/bin/crontab\fR \fB{ -e | -g | -l | -r }\fR [\fIusername\fR]
.fi
.LP
@@ -106,6 +108,19 @@ users' crontabs.
If \fBcrontab\fR is invoked with \fIfilename\fR, this overwrites an existing
\fBcrontab\fR entry for the user that invokes it, or for the user specified
with the \fB-u\fR option.
+.sp
+.LP
+Cron supports a merged crontab with entries coming from either the user's
+\fB/var/spool/cron/crontabs\fR file or from the user's
+\fB/etc/cron.d/crontabs\fR file. The entries in the user's
+\fB/var/spool/cron/crontabs\fR file are editable whereas those in
+\fB/etc/cron.d/crontabs\fR are system-defined entries which may not
+be customized by the user. The dual set of crontab entries is only
+of interest to system-defined users such as \fBroot\fR. Except where
+otherwise explicitly indicated, all variants of the \fBcrontab\fR command
+act only on the editable crontab files found in \fB/var/spool/cron/crontabs\fR.
+.sp
+.LP
.SS "\fBcrontab\fR Access Control"
Users: Access to \fBcrontab\fR is allowed:
.RS +4
@@ -382,6 +397,9 @@ file using the \fB-r\fR option.
If \fIusername\fR is specified, the specified user's \fBcrontab\fR file is
edited, rather than the current user's \fBcrontab\fR file. This can only be
done by root or by a user with the \fBsolaris.jobs.admin\fR authorization.
+.sp
+Only the entries in the user's \fB/var/spool/cron/crontabs\fR file are
+editable.
.RE
.sp
@@ -393,6 +411,22 @@ done by root or by a user with the \fBsolaris.jobs.admin\fR authorization.
Lists the \fBcrontab\fR file for the invoking user. Only root or a user with
the \fBsolaris.jobs.admin\fR authorization can specify a username following the
\fB-l\fR option to list the \fBcrontab\fR file of the specified user.
+.sp
+Entries from the user's \fB/var/spool/cron/crontabs\fR file are listed, unless
+the \fB-g\fR option is given, in which case only entries from the user's
+\fB/etc/cron.d/crontabs\fR file are listed.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-g\fR\fR
+.ad
+.RS 6n
+In conjunction with the \fB-l\fR option, lists the global \fBcrontab\fR file
+for the invoking or specified user (if authorized) instead of the editable
+\fBcrontab\fR file. This option is not valid unless the \fB-l\fR option is
+also given.
.RE
.sp
@@ -651,6 +685,15 @@ list of denied users
.sp
.ne 2
.na
+\fB\fB/etc/cron.d/crontabs\fR\fR
+.ad
+.RS 28n
+system spool area for \fBcrontab\fR
+.RE
+
+.sp
+.ne 2
+.na
\fB/var/cron/log\fR
.ad
.RS 28n
diff --git a/usr/src/man/man1/diff.1 b/usr/src/man/man1/diff.1
index 3069149285..11be316331 100644
--- a/usr/src/man/man1/diff.1
+++ b/usr/src/man/man1/diff.1
@@ -473,6 +473,7 @@ or
options are na\(:ive about creating lines consisting of a single dot
.Sq \&. .
.Pp
+>>>>>>> master
Missing NEWLINE at end of file indicates that the last line of the file in
question did not have a NEWLINE.
If the lines are different, they will be flagged and output, although the
diff --git a/usr/src/man/man1/hostname.1 b/usr/src/man/man1/hostname.1
index 3d44d816fb..661ca1c048 100644
--- a/usr/src/man/man1/hostname.1
+++ b/usr/src/man/man1/hostname.1
@@ -1,8 +1,9 @@
.\" Copyright (c) 1992, Sun Microsystems, Inc.
+.\" Copyright 2016 Joyent, Inc.
.\" 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]
-.Dd May 12, 2016
+.Dd Aug 26, 2016
.Dt HOSTNAME 1
.Os
.Sh NAME
@@ -20,6 +21,19 @@ command prints the name of the current host, as given before the
prompt.
The super-user can set the hostname by giving
.Ar name-of-host .
+.Pp
+While setting the hostname changes the hostname for the running system,
+it does not persist the change.
+The proper way to update the hostname will vary depending on how the
+system is configured.
+For most systems, the persistent hostname is stored in the file
+.Pa /etc/nodename .
+See
+.Xr nodename 5
+for more information on how to update it.
+Note, the
+.Pa /etc/nodename
+file may be ignored in cases where the system is configured to use DHCP.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl s
@@ -27,4 +41,5 @@ Prints only the part of the hostname preceding the first dot.
.El
.Sh SEE ALSO
.Xr uname 1 ,
+.Xr nodename 5 ,
.Xr attributes 7
diff --git a/usr/src/man/man1/ld.so.1.1 b/usr/src/man/man1/ld.so.1.1
index 76e7262919..a36df012d6 100644
--- a/usr/src/man/man1/ld.so.1.1
+++ b/usr/src/man/man1/ld.so.1.1
@@ -1,5 +1,6 @@
'\"
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright (c) 2014, Joyent, Inc. All Rights Reserved
.\" 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]
@@ -567,6 +568,24 @@ aid debugging. See also the \fBRTLD_DI_SETSIGNAL\fR request to
.RE
.sp
+.ne 2
+.na
+.BR LD_TOXIC_PATH,
+.BR LD_TOXIC_PATH_32,
+.BR LD_TOXIC_PATH_64,
+.ad
+.sp .6
+.RS 4n
+The toxic path refers to a set of paths where by, if
+.B ld.so.1
+were to load a dependency on that path, rather than loading it, it
+should kill the process. This is useful when having built libraries that
+while matching the native architecture of the system, are not suitable
+to be used, for example, libraries that that correspond to an alternate
+release of an operating system.
+.RE
+
+.sp
.LP
Notice that environment variable names beginning with the
characters '\fBLD_\fR' are reserved for possible future enhancements to \fBld\fR(1) and
diff --git a/usr/src/man/man1/machid.1 b/usr/src/man/man1/machid.1
new file mode 100644
index 0000000000..cb95fa36b6
--- /dev/null
+++ b/usr/src/man/man1/machid.1
@@ -0,0 +1,25 @@
+'\" te
+.\" Copyright 1989 AT&T
+.\" Copyright (c) 1999, Sun Microsystems, Inc.
+.\" All Rights Reserved
+.\" Copyright 2015, Joyent, Inc.
+.\" 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]
+.TH MACHID 1 "Feb 27, 2015"
+.SH NAME
+machid, sun, i286, i386, i486, sparc \- get processor type truth value
+
+.SH DESCRIPTION
+.sp
+.LP
+These commands are obsolete and may be removed in a future version of the
+software.
+.sp
+.ne 2
+.na
+.SH NOTES
+.sp
+.LP
+The \fBmachid\fR family of commands is obsolete. Use \fBuname\fR \fB-p\fR and
+\fBuname\fR \fB-m\fR instead.
diff --git a/usr/src/man/man1/pgrep.1 b/usr/src/man/man1/pgrep.1
index a5482fc992..6a02aeff3e 100644
--- a/usr/src/man/man1/pgrep.1
+++ b/usr/src/man/man1/pgrep.1
@@ -1,13 +1,13 @@
'\" te
-.\" Copyright (c) 2004, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright (c) 2004, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright 2019 Joyent, Inc.
.\" 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]
-.TH PGREP 1 "Jan 17, 2014"
+.TH PGREP 1 "Aug 13, 2019"
.SH NAME
pgrep, pkill \- find or signal processes by name and other attributes
.SH SYNOPSIS
-.LP
.nf
\fBpgrep\fR [\fB-flvx\fR] [\fB-n\fR | \fB-o\fR] [\fB-d\fR \fIdelim\fR] [\fB-P\fR \fIppidlist\fR]
[\fB-g\fR \fIpgrplist\fR] [\fB-s\fR \fIsidlist\fR] [\fB-u\fR \fIeuidlist\fR] [\fB-U\fR \fIuidlist\fR]
@@ -26,8 +26,6 @@ pgrep, pkill \- find or signal processes by name and other attributes
.fi
.SH DESCRIPTION
-.sp
-.LP
The \fBpgrep\fR utility examines the active processes on the system and reports
the process \fBID\fRs of the processes whose attributes match the criteria
specified on the command line. Each process \fBID\fR is printed as a decimal
@@ -74,8 +72,6 @@ process is signaled as if by \fBkill\fR(1) instead of having its process
\fBID\fR printed. A signal name or number may be specified as the first command
line option to \fBpkill\fR.
.SH OPTIONS
-.sp
-.LP
The following options are supported:
.sp
.ne 2
@@ -104,11 +100,11 @@ character. The \fB-d\fR option is only valid when specified as an option to
\fB\fB-f\fR\fR
.ad
.RS 17n
-The regular expression \fIpattern\fR should be matched against the full process
-argument string (obtained from the \fBpr_psargs\fR field of the
-\fB/proc/\fInnnnn\fR/psinfo\fR file). If no \fB-f\fR option is specified, the
-expression is matched only against the name of the executable file (obtained
-from the \fBpr_fname\fR field of the \fB/proc/\fInnnnn\fR/psinfo\fR file).
+The regular expression \fIpattern\fR should be matched against all the process
+arguments, not just the executable file name.
+The process arguments are read from \fB\fB/proc/\fInnnnn\fR\fB/cmdline\fR: this
+does not truncate the arguments, but reflects the current value, which may have
+been changed.
.RE
.sp
@@ -150,11 +146,9 @@ project \fBID\fR.
\fB\fB-l\fR\fR
.ad
.RS 17n
-Long output format. Prints the process name along with the process \fBID\fR of
-each matching process. The process name is obtained from the \fBpr_psargs\fR or
-\fBpr_fname\fR field, depending on whether the \fB-f\fR option was specified
-(see above). The \fB-l\fR option is only valid when specified as an option to
-\fBpgrep\fR.
+Long output format. Prints the process arguments along with the process \fBID\fR of
+each matching process, depending on how the \fB-f\fR option was specified.
+The \fB-l\fR option is only valid when specified as an option to \fBpgrep\fR.
.RE
.sp
@@ -258,8 +252,8 @@ which meet the specified matching criteria.
.RS 17n
Matches only processes whose executable file name (ignoring any path)
\fBexactly\fR matches the specified \fIpattern\fR. However, when used with -f,
-the \fIpattern\fR should be matched against the full process argument
-string. For example if there exists a process `/bin/ls /home' then:
+the \fIpattern\fR is matched against the full process argument
+string. For example, if there exists a process `/bin/ls /home' then:
.sp
.in +2
.nf
@@ -308,8 +302,6 @@ one of the symbolic names defined in \fBsignal.h\fR(3HEAD) without the
.RE
.SH OPERANDS
-.sp
-.LP
The following operand is supported:
.sp
.ne 2
@@ -323,7 +315,6 @@ either the executable file name or full process argument string. See
.RE
.SH EXAMPLES
-.LP
\fBExample 1 \fRObtaining a Process ID
.sp
.LP
@@ -352,9 +343,20 @@ example% \fBpkill -n xterm\fR
.in -2
.sp
-.SH EXIT STATUS
+.LP
+\fBExample 3 \fRMatching against all process arguments
.sp
.LP
+Match against any process argument and report the arguments:
+
+.sp
+.in +2
+.nf
+example% \fBpgrep -fl myfile.txt\fR
+.fi
+.in -2
+.sp
+.SH EXIT STATUS
The following exit values are returned:
.sp
.ne 2
@@ -393,17 +395,22 @@ A fatal error occurred.
.RE
.SH FILES
-.sp
.ne 2
.na
-\fB\fB/proc/\fInnnnn\fR/psinfo\fR\fR
+\fB\fB/proc/\fInnnnn\fR\fB/psinfo\fR\fR
.ad
.RS 22n
Process information files
.RE
-.SH SEE ALSO
.sp
+.ne 2
+.na
+\fB\fB/proc/\fInnnnn\fR\fB/cmdline\fR\fR
+.ad
+.RS 22n
+Process arguments.
+.RE
.LP
.BR kill (1),
.BR proc (1),
@@ -417,14 +424,11 @@ Process information files
.BR regex (7),
.BR zones (7)
.SH NOTES
-.sp
-.LP
Both utilities match the \fBERE\fR \fIpattern\fR argument against either the
-\fBpr_fname\fR or \fBpr_psargs\fR fields of the
-\fB/proc/\fR\fInnnnn\fR\fB/psinfo\fR files. The lengths of these strings are
-limited according to definitions in \fB<sys/procfs.h>\fR\&. Patterns which can
-match strings longer than the current limits may fail to match the intended set
-of processes.
+\fBpr_fname\fR field of the \fB/proc/\fR\fInnnnn\fR\fB/psinfo\fR file, or
+\fB/proc/\fR\fInnnnn\fR\fB/cmdline\fR, and may be truncated.
+Patterns which can match strings longer than the current limits may fail to
+match the intended set of processes.
.sp
.LP
If the \fIpattern\fR argument contains \fBERE\fR meta-characters which are also
diff --git a/usr/src/man/man1/ps.1 b/usr/src/man/man1/ps.1
index 5eb0b70f3b..43a8b5b0a4 100644
--- a/usr/src/man/man1/ps.1
+++ b/usr/src/man/man1/ps.1
@@ -44,13 +44,12 @@
.\" Portions Copyright (c) 1992, X/Open Company Limited All Rights Reserved
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
.\" Copyright (c) 2013 Gary Mills
-.\" Copyright (c) 2018, Joyent, Inc.
+.\" Copyright 2019 Joyent, Inc.
.\"
-.TH PS 1 "August 22, 2018"
+.TH PS 1 "August 28, 2019"
.SH NAME
ps \- report process status
.SH SYNOPSIS
-.LP
.nf
\fBps\fR [\fB-aAcdefjHlLPWyZ\fR] [\fB-g\fR \fIgrplist\fR] [\fB-h\fR \fIlgrplist\fR]
[\fB-n\fR \fInamelist\fR] [\fB-o\fR \fIformat\fR]... [\fB-p\fR \fIproclist\fR]
@@ -59,7 +58,6 @@ ps \- report process status
.fi
.SH DESCRIPTION
-.LP
The \fBps\fR command prints information about active processes. Without
options, \fBps\fR prints information about processes that have the same
effective user \fBID\fR and the same controlling terminal as the invoker. The
@@ -71,8 +69,10 @@ displayed is controlled by the options.
Some options accept lists as arguments. Items in a list can be either separated
by commas or else enclosed in quotes and separated by commas or spaces. Values
for \fIproclist\fR and \fIgrplist\fR must be numeric.
-.SH OPTIONS
+.sp
.LP
+The \fBps\fR command also accepts BSD-style options. See \fBps\fR(1b).
+.SH OPTIONS
The following options are supported:
.sp
.ne 2
@@ -372,9 +372,9 @@ Many of the options shown are used to select processes to list. If any are
specified, the default list is ignored and \fBps\fR selects the processes
represented by the inclusive OR of all the selection-criteria options.
.SH DISPLAY FORMATS
-.LP
-Under the \fB-f\fR option, \fBps\fR tries to determine the command name and
-arguments given when the process was created by examining the user block.
+Under the \fB-f\fR option, \fBps\fR uses the \fB\fB/proc/\fInnnnn\fR\fB/cmdline\fR\fR
+file to read the full process arguments. The process may have changed these
+since it was started.
Failing this, the command name is printed, as it would have appeared without
the \fB-f\fR option, in square brackets.
.sp
@@ -605,8 +605,8 @@ The execution time for the lwp being reported.
\fB\fBCMD\fR(all)\fR
.ad
.RS 14n
-The command name (the full command name and its arguments, up to a limit of 80
-characters, are printed under the \fB-f\fR option).
+The command name (or, with the \fB-f\fR option, the full current process
+arguments).
.RE
.sp
@@ -664,7 +664,6 @@ lwp.
A process that has exited and has a parent, but has not yet been waited for by
the parent, is marked \fB<defunct>\fR\&.
.SS "\fB-o\fR format"
-.LP
The \fB-o\fR option allows the output format to be specified under user
control.
.sp
@@ -882,9 +881,9 @@ is a version of the argument list as it was passed to the command when it
started, or is a version of the arguments as they might have been modified by
the application. Applications cannot depend on being able to modify their
argument list and having that modification be reflected in the output of
-\fBps\fR. The illumos implementation limits the string to 80 bytes; the string
-is the version of the argument list as it was passed to the command when it
-started.
+\fBps\fR. However, the current implementation will display the full modified
+process arguments when given the \fB-f\fR argument; otherwise this is the
+initial process arguments, but limited to 80 bytes.
.RE
.sp
@@ -1241,7 +1240,6 @@ ctid CTID
.TE
.SH EXAMPLES
-.LP
\fBExample 1 \fRUsing \fBps\fR Command
.sp
.LP
@@ -1289,7 +1287,6 @@ number of text columns to display.
.RE
.SH EXIT STATUS
-.LP
The following exit values are returned:
.sp
.ne 2
@@ -1382,7 +1379,6 @@ Standard See \fBstandards\fR(7).
.BR zones (7),
.BR getty (8)
.SH NOTES
-.LP
Things can change while \fBps\fR is running. The snapshot it gives is true only
for a split-second, and it might not be accurate by the time you see it. Some
data printed for defunct processes is irrelevant.
diff --git a/usr/src/man/man1/sed.1 b/usr/src/man/man1/sed.1
index 4261c2df83..3eca2b5418 100644
--- a/usr/src/man/man1/sed.1
+++ b/usr/src/man/man1/sed.1
@@ -45,7 +45,8 @@
[\fB\-Ealnr\fP]
[\fB\-e\fP \fIcommand\fP]
[\fB\-f\fP \fIcommand_file\fP]
-[\fB\-I\fP[\fIextension\fP] | \fB\-i\fP[\fIextension\fP]]
+[\fB\-I\fP \fIextension\fP]
+[\fB\-i\fP \fIextension\fP]
[\fIfile ...\fP]
.SH DESCRIPTION
The
@@ -98,11 +99,16 @@ Append the editing commands found in the file
to the list of commands.
The editing commands should each be listed on a separate line.
.TP
-\fB\-I\fP[\fIextension\fP]
-Edit files in-place, saving backups if \fIextension\fP was specified.
-It is not recommended to omit saving backups when in-place editing files,
-as you risk corruption or partial content in situations where disk
-space is exhausted, etc.
+\fB\-I\fP \fIextension\fP
+Edit files in-place, saving backups with the specified
+\fIextension\fP.
+If a zero-length
+\fIextension\fP
+is given, no backup will be saved.
+It is not recommended to give a zero-length
+\fIextension\fP
+when in-place editing files, as you risk corruption or partial content
+in situations where disk space is exhausted, etc.
Note that in-place editing with
\fB\-I\fP
@@ -120,7 +126,7 @@ where using
\fB\-i\fP
is desired.
.TP
-\fB\-i\fP[\fIextension\fP]
+\fB\-i\fP \fIextension\fP
Edit files in-place similarly to
\fB\-I\fP,
but treat each file independently from other files.
diff --git a/usr/src/man/man1/zlogin.1 b/usr/src/man/man1/zlogin.1
index 0b7c9d2cc1..8d12560b96 100644
--- a/usr/src/man/man1/zlogin.1
+++ b/usr/src/man/man1/zlogin.1
@@ -13,43 +13,43 @@
.\" Portions Copyright [yyyy] [name of copyright owner]
.\" Copyright 2013 DEY Storage Systems, Inc.
.\" Copyright (c) 2014 Gary Mills
+.\" Copyright (c) 2015, Joyent, Inc. All Rights Reserved
.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
-.TH ZLOGIN 1 "Mar 17, 2015"
+.TH ZLOGIN 1 "Mar 30, 2015"
.SH NAME
zlogin \- enter a zone
.SH SYNOPSIS
.LP
.nf
-\fBzlogin\fR [\fB-dCEQ\fR] [\fB-e\fR \fIc\fR] [\fB-l\fR \fIusername\fR] \fIzonename\fR
+\fBzlogin\fR [\fB-dCEINQ\fR] [\fB-e\fR \fIc\fR] [\fB-l\fR \fIusername\fR] \fIzonename\fR
.fi
.LP
.nf
-\fBzlogin\fR [\fB-nEQS\fR] [\fB-e\fR \fIc\fR] [\fB-l\fR \fIusername\fR] \fIzonename\fR \fIutility\fR
+\fBzlogin\fR [\fB-inEQS\fR] [\fB-e\fR \fIc\fR] [\fB-l\fR \fIusername\fR] \fIzonename\fR \fIutility\fR
[\fIargument\fR]...
.fi
.SH DESCRIPTION
-.sp
.LP
The \fBzlogin\fR utility is used by the administrator to enter an operating
system zone. Only a superuser operating in the global system zone can use this
utility.
.sp
.LP
-\fBzlogin\fR operates in one of three modes:
+\fBzlogin\fR operates in one of four modes:
.sp
.ne 2
.na
\fBInteractive Mode\fR
.ad
.RS 24n
-If no utility argument is given and the stdin file descriptor for the
-\fBzlogin\fR process is a tty device, \fBzlogin\fR operates in \fBinteractive
-mode\fR. In this mode, \fBzlogin\fR creates a new pseudo terminal for use
-within the login session. Programs requiring a tty device, for example,
-\fBvi\fR(1), work properly in this mode. In this mode, \fBzlogin\fR invokes
-\fBlogin\fR(1) to provide a suitable login session.
+If no utility argument is given or if the \fB-i\fR option is specified, and the
+stdin file descriptor for the \fBzlogin\fR process is a tty device, \fBzlogin\fR
+operates in \fBinteractive mode\fR. In this mode, \fBzlogin\fR creates a new
+pseudo terminal for use within the login session. Programs requiring a tty
+device, for example, \fBvi\fR(1), work properly in this mode. In this mode,
+\fBzlogin\fR invokes \fBlogin\fR(1) to provide a suitable login session.
.RE
.sp
@@ -58,11 +58,12 @@ within the login session. Programs requiring a tty device, for example,
\fBNon-Interactive Mode\fR
.ad
.RS 24n
-If a utility is specified, \fBzlogin\fR operates in \fBnon-interactive mode\fR.
-This mode can be useful for script authors since stdin, stdout, and stderr are
-preserved and the exit status of \fIutility\fR is returned upon termination. In
-this mode, \fBzlogin\fR invokes \fBsu\fR(8) in order to set up the user's
-environment and to provide a login environment.
+If a utility is specified and the \fB-i\fR option is not specified, \fBzlogin\fR
+operates in \fBnon-interactive mode\fR. This mode can be useful for script
+authors since stdin, stdout, and stderr are preserved and the exit status of
+\fIutility\fR is returned upon termination. In this mode, \fBzlogin\fR invokes
+\fBsu\fR(8) in order to set up the user's environment and to provide a login
+environment.
.sp
The specified command is passed as a string and interpreted by a shell running
in the non-global zone. See \fBrsh\fR(1).
@@ -80,8 +81,17 @@ available once the zone is in the installed state. Connections to the console
are persistent across reboot of the zone.
.RE
-.SH OPTIONS
.sp
+.ne 2
+.na
+\fBStandalone-processs Interactive Mode\fR
+.ad
+.RS 24n
+If the \fB-I\fR option is specified the user is connected to the zone's stdin,
+stdout and stderr \fBzfd(7D)\fR devices.
+.RE
+
+.SH OPTIONS
.LP
The following options are supported:
.sp
@@ -127,6 +137,25 @@ login by using the escape sequence character.
.sp
.ne 2
.na
+\fB\fB-i\fR\fR
+.ad
+.RS 15n
+Forces interactive mode when a utility argument is specified.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-I\fR\fR
+.ad
+.RS 15n
+Connects to the zone's \fBzfd(7D)\fR devices.
+.RE
+
+.sp
+.sp
+.ne 2
+.na
\fB\fB-l\fR \fIusername\fR\fR
.ad
.RS 15n
@@ -149,6 +178,17 @@ and the shell which invokes \fBzlogin\fR both read from standard input.
.sp
.ne 2
.na
+\fB-N\fR
+.ad
+.RS 15n
+Nohup. This may only be used with the -I option to avoid sending EOF to the zfd
+device when zlogin's stdin receives EOF. It can also be toggled by sending
+\fBSIGUSR1\fR to an attached zlogin process.
+.RE
+
+.sp
+.ne 2
+.na
\fB-Q\fR
.ad
.RS 15n
@@ -172,7 +212,6 @@ other forms of login have become impossible.
.RE
.SS "Escape Sequences"
-.sp
.LP
Lines that you type that start with the tilde character (\fB~\fR) are "escape
sequences". The escape character can be changed using the \fB-e\fR option.
@@ -187,12 +226,10 @@ host breaks the connection with no warning to the zone's end.
.RE
.SH SECURITY
-.sp
.LP
Once a process has been placed in a zone other than the global zone, the
process cannot change zone again, nor can any of its children.
.SH OPERANDS
-.sp
.LP
The following operands are supported:
.sp
@@ -223,7 +260,6 @@ Arguments passed to the utility.
.RE
.SH EXIT STATUS
-.sp
.LP
In interactive and non-interactive modes, the \fBzlogin\fR utility exits when
the command or shell in the non-global zone exits. In non-interactive mode, the
@@ -268,7 +304,6 @@ mode.
.RE
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(7) for descriptions of the following attributes:
.sp
@@ -284,7 +319,6 @@ Interface Stability Evolving
.TE
.SH SEE ALSO
-.sp
.LP
.BR login (1),
.BR rsh (1),
@@ -295,7 +329,6 @@ Interface Stability Evolving
.BR zoneadm (8),
.BR zonecfg (8)
.SH NOTES
-.sp
.LP
\fBzlogin\fR fails if its open files or any portion of its address space
corresponds to an NFS file. This includes the executable itself or the shared
diff --git a/usr/src/man/man1b/ps.1b b/usr/src/man/man1b/ps.1b
index afedd42f79..db60e40828 100644
--- a/usr/src/man/man1b/ps.1b
+++ b/usr/src/man/man1b/ps.1b
@@ -1,17 +1,16 @@
'\" te
.\" Copyright (c) 2002, Sun Microsystems, Inc. - All Rights Reserved.
.\" Copyright (c), 1980 Regents of the University of California. All rights reserved. The Berkeley software License Agreement specifies the terms and conditions for redistribution.
-.TH PS 1B "May 13, 2017"
+.\" Copyright 2019 Joyent, Inc.
+.TH PS 1B "Aug 13, 2019"
.SH NAME
ps \- display the status of current processes
.SH SYNOPSIS
-.LP
.nf
\fB/usr/ucb/ps\fR [\fB-aceglnrSuUvwx\fR] [\fB-t\fR \fIterm\fR] [\fInum\fR]
.fi
.SH DESCRIPTION
-.LP
The \fBps\fR command displays information about processes. Normally, only those
processes that are running with your effective user \fBID\fR and are attached
to a controlling terminal (see \fBtermio\fR(4I)) are shown. Additional
@@ -78,7 +77,6 @@ Traced. Process stopped by a signal because parent is tracing it.
.RE
.SH OPTIONS
-.LP
The following options must all be combined to form the first argument:
.sp
.ne 2
@@ -206,11 +204,10 @@ fields \fBSIZE\fR, \fB%CPU\fR, \fB%MEM\fR, and \fBRSS\fR, described below.
\fB\fB-w\fR\fR
.ad
.RS 11n
-Uses a wide output format, that is, 132 columns rather than 80. If the option
+Uses a wide output format, that is, truncate process arguments at 132 columns
+rather than 80. If the option
letter is repeated, that is, \fB-ww\fR, this option uses arbitrarily wide
output. This information is used to decide how much of long commands to print.
-\fBNote:\fR The wide output option can be viewed only by a superuser or the
-user who owns the process.
.RE
.sp
@@ -233,7 +230,6 @@ process. This option must be supplied last.
.RE
.SH DISPLAY FORMATS
-.LP
Fields that are not common to all output formats:
.sp
.ne 2
@@ -376,7 +372,6 @@ the parent, is marked <\fBdefunct\fR>\|; otherwise, \fBps\fR tries to determine
the command name and arguments given when the process was created by examining
the user block.
.SH FILES
-.LP
\fB/dev/tty*\fR
.sp
.ne 2
@@ -398,7 +393,6 @@ the user block.
.BR attributes (7),
.BR whodo (8)
.SH NOTES
-.LP
Things can change while \fBps\fR is running. The picture \fBps\fR gives is only
a close approximation to the current state. Some data printed for defunct
processes is irrelevant.
diff --git a/usr/src/man/man3c/Makefile b/usr/src/man/man3c/Makefile
index 59ceb21817..baa4ac8105 100644
--- a/usr/src/man/man3c/Makefile
+++ b/usr/src/man/man3c/Makefile
@@ -219,6 +219,9 @@ MANFILES= __fbufsize.3c \
index.3c \
inet.3c \
initgroups.3c \
+ inotify_init.3c \
+ inotify_add_watch.3c \
+ inotify_rm_watch.3c \
insque.3c \
is_system_labeled.3c \
isaexec.3c \
diff --git a/usr/src/man/man3c/inotify_add_watch.3c b/usr/src/man/man3c/inotify_add_watch.3c
new file mode 100644
index 0000000000..4f79e03c82
--- /dev/null
+++ b/usr/src/man/man3c/inotify_add_watch.3c
@@ -0,0 +1,120 @@
+'\" te
+.\" Copyright (c) 2014, Joyent, Inc. All Rights Reserved.
+.\" 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.
+.TH INOTIFY_ADD_WATCH 3C "Sep 17, 2014"
+.SH NAME
+inotify_add_watch \- add a watch to an inotify instance
+.SH SYNOPSIS
+
+.LP
+.nf
+#include <sys/inotify.h>
+
+\fBint\fR \fBinotify_add_watch\fR(\fBint\fR \fIfd\fR, \fBconst char *\fR\fIpathname\fR, \fBuint32_t\fR \fImask\fR);
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The \fBinotify_add_watch()\fR function adds a watch for the file or
+directory specified by \fIpathname\fR to the inotify instance
+specified by \fIfd\fR for the events specified by \fImask\fR. See
+\fBinotify\fR(5) for details on the meaning of \fImask\fR, how
+it affects the interpretation of \fIpathname\fR, and how
+events for the watched file or directory are subsequently
+retrieved via \fBread\fR(2).
+
+.SH RETURN VALUES
+.sp
+.LP
+Upon succesful completion, \fBinotify_add_watch()\fR returns the
+watch descriptor associated with the new watch.
+If an error occurs, -1 is returned and errno is set to indicate
+the error.
+
+.SH ERRORS
+.sp
+.LP
+\fBinotify_add_watch()\fR will fail if:
+.sp
+.ne 2
+.na
+\fB\fBEACCES\fR\fR
+.ad
+.RS 10n
+\fIpathname\fR could not be opened for reading.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBEBADF\fR\fR
+.ad
+.RS 10n
+The \fIfd\fR argument is not a valid open file descriptor.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBEFAULT\fR\fR
+.ad
+.RS 10n
+The memory associated with \fIpathname\fR was not mapped.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBEINVAL\fR\fR
+.ad
+.RS 10n
+The \fIfd\fR argument does not correspond to an
+\fBinotify\fR(5) instance as initialized with
+\fBinotify_init\fR(3C) or \fBinotify_init1\fR(3C).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBENOSPC\fR\fR
+.ad
+.RS 10n
+The number of watches on the specified instance would exceed the
+maximum number of watches per \fBinotify\fR(5) instance.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBENOTDIR\fR\fR
+.ad
+.RS 10n
+\fIpathname\fR does not correspond to a directory and
+\fBIN_ONLYDIR\fR was specified in \fImask\fR.
+.RE
+
+.sp
+.SH NOTES
+.sp
+.LP
+
+While the \fBinotify\fR(5) facility is implemented for purposes of
+offering compatibility for Linux-borne applications, native
+applications may opt to use it instead of (or in addition to) the
+\fBPORT_SOURCE_FILE\fR capability of event ports. See
+\fBinotify\fR(5) for details and restrictions.
+
+.SH SEE ALSO
+.sp
+.LP
+\fBinotify_init\fR(3C), \fBinotify_init1\fR(3C),
+\fBport_create\fR(3C), \fBport_associate\fR(3C), \fBport_get\fR(3C),
+\fBinotify\fR(5)
diff --git a/usr/src/man/man3c/inotify_init.3c b/usr/src/man/man3c/inotify_init.3c
new file mode 100644
index 0000000000..551a2ca798
--- /dev/null
+++ b/usr/src/man/man3c/inotify_init.3c
@@ -0,0 +1,107 @@
+'\" te
+.\" Copyright (c) 2014, Joyent, Inc. All Rights Reserved.
+.\" 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.
+.TH INOTIFY_INIT 3C "Sep 17, 2014"
+.SH NAME
+inotify_init, inotify_init1 \- initialize an inotify instance
+.SH SYNOPSIS
+
+.LP
+.nf
+#include <sys/inotify.h>
+
+\fBint\fR \fBinotify_init\fR(\fBvoid\fR);
+.fi
+
+.LP
+.nf
+\fBint\fR \fBinotify_init1\fR(\fBint\fR \fIflags\fR);
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The \fBinotify_init()\fR and \fBinotify_init1()\fR functions both create an
+\fBinotify\fR(5) instance that can be operated upon via
+\fBinotify_add_watch\fR(3C), \fBinotify_rm_watch\fR(3C) and \fBread\fR(2).
+\fBinotify\fR instances are
+represented as file descriptors, and should be closed via \fBclose\fR(2).
+
+The only difference between the two functions is their signature;
+\fBinotify_init()\fR takes no arguments,
+while \fBinotify_init1()\fR takes a \fIflags\fR argument that can have
+any of the following values:
+
+.sp
+.ne 2
+.na
+\fBIN_CLOEXEC\fR
+.ad
+.RS 12n
+Instance should be closed upon an
+\fBexec\fR(2); see \fBopen\fR(2)'s description of \fBO_CLOEXEC\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_NONBLOCK\fR
+.ad
+.RS 12n
+Instance will be set to be non-blocking. A \fBread\fR(2) on an
+\fBinotify\fR instance that has been initialized with
+\fBIN_NONBLOCK\fR will return \fBEAGAIN\fR if there are
+no events enqueued in lieu of blocking.
+.RE
+
+.SH RETURN VALUES
+.sp
+.LP
+Upon succesful completion, 0 is returned. Otherwise, -1 is returned and errno
+is set to indicate the error.
+.SH ERRORS
+.sp
+.LP
+The \fBinotify_init()\fR and \fBinotify_init1()\fR functions will fail if:
+.sp
+.ne 2
+.na
+\fB\fBEINVAL\fR\fR
+.ad
+.RS 10n
+The \fIflags\fR are invalid (\fBinotify_init1()\fR).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBEMFILE\fR\fR
+.ad
+.RS 10n
+There are currently {\fBOPEN_MAX\fR} file descriptors open in the calling
+process, or the maximum number of \fBinotify\fR instances for the user
+would be exceeded.
+.RE
+
+.sp
+.SH NOTES
+.sp
+.LP
+
+While the \fBinotify\fR(5) facility is implemented for purposes of
+offering compatibility for Linux-borne applications, native
+applications may opt to use it instead of (or in addition to) the
+\fBPORT_SOURCE_FILE\fR capability of event ports. See
+\fBinotify\fR(5) for details and restrictions.
+
+.SH SEE ALSO
+.sp
+.LP
+\fBinotiy_add_watch\fR(3C), \fBinotify_rm_watch\fR(3C), \fBinotify\fR(5)
diff --git a/usr/src/man/man3c/inotify_rm_watch.3c b/usr/src/man/man3c/inotify_rm_watch.3c
new file mode 100644
index 0000000000..de568f8e24
--- /dev/null
+++ b/usr/src/man/man3c/inotify_rm_watch.3c
@@ -0,0 +1,81 @@
+'\" te
+.\" Copyright (c) 2014, Joyent, Inc. All Rights Reserved.
+.\" 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.
+.TH INOTIFY_RM_WATCH 3C "Sep 17, 2014"
+.SH NAME
+inotify_rm_watch \- remove a watch from an inotify instance
+.SH SYNOPSIS
+
+.LP
+.nf
+#include <sys/inotify.h>
+
+\fBint\fR \fBinotify_rm_watch\fR(\fBint\fR \fIfd\fR, \fBint\fR \fIwd\fR);
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The \fBinotify_rm_watch()\fR function removes the watch specified
+by \fIwd\fR from the inotify instance associated with \fIfd\fR.
+Removing a watch will induce an \fBIN_IGNORED\fR event; see
+\fBinotify\fR(5) for details.
+
+.SH RETURN VALUES
+.sp
+.LP
+Upon succesful completion, \fBinotify_add_watch()\fR returns the
+watch descriptor associated with the new watch.
+If an error occurs, -1 is returned and errno is set to indicate
+the error.
+
+.SH ERRORS
+.sp
+.LP
+\fBinotify_rm_watch()\fR will fail if:
+.sp
+.ne 2
+.na
+\fB\fBEBADF\fR\fR
+.ad
+.RS 10n
+The \fIfd\fR argument is not a valid open file descriptor.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBEINVAL\fR\fR
+.ad
+.RS 10n
+The \fIfd\fR argument does not correspond to an
+\fBinotify\fR(5) instance as initialized with
+\fBinotify_init\fR(3C) or \fBinotify_init1\fR(3C), or
+\fIwd\fR is not a valid watch for the specified inotify
+instance.
+.RE
+
+.sp
+.SH NOTES
+.sp
+.LP
+
+While the \fBinotify\fR(5) facility is implemented for purposes of
+offering compatibility for Linux-borne applications, native
+applications may opt to use it instead of (or in addition to) the
+\fBPORT_SOURCE_FILE\fR capability of event ports. See
+\fBinotify\fR(5) for details and restrictions.
+
+.SH SEE ALSO
+.sp
+.LP
+\fBinotify_init\fR(3C), \fBinotify_init1\fR(3C),
+\fBport_create\fR(3C), \fBport_associate\fR(3C), \fBport_get\fR(3C),
+\fBinotify\fR(5)
diff --git a/usr/src/man/man3c/psignal.3c b/usr/src/man/man3c/psignal.3c
index 5078225b5c..cf647521dc 100644
--- a/usr/src/man/man3c/psignal.3c
+++ b/usr/src/man/man3c/psignal.3c
@@ -11,7 +11,7 @@ psignal, psiginfo \- system signal messages
.SH SYNOPSIS
.LP
.nf
-#include <siginfo.h>
+#include <signal.h>
\fBvoid\fR \fBpsignal\fR(\fBint\fR \fIsig\fR, \fBconst char *\fR\fIs\fR);
.fi
diff --git a/usr/src/man/man3dlpi/Makefile b/usr/src/man/man3dlpi/Makefile
index cdd24216bd..4c5448f0be 100644
--- a/usr/src/man/man3dlpi/Makefile
+++ b/usr/src/man/man3dlpi/Makefile
@@ -41,10 +41,12 @@ MANFILES= dlpi_arptype.3dlpi \
dlpi_walk.3dlpi
MANLINKS= dlpi_disabmulti.3dlpi \
+ dlpi_open_zone.3dlpi \
dlpi_promiscoff.3dlpi
dlpi_disabmulti.3dlpi := LINKSRC = dlpi_enabmulti.3dlpi
+dlpi_open_zone.3dlpi := LINKSRC = man3dlpi/dlpi_open.3dlpi
dlpi_promiscoff.3dlpi := LINKSRC = dlpi_promiscon.3dlpi
.KEEP_STATE:
diff --git a/usr/src/man/man3dlpi/dlpi_open.3dlpi b/usr/src/man/man3dlpi/dlpi_open.3dlpi
index 546cedd5da..28bc7f66cf 100644
--- a/usr/src/man/man3dlpi/dlpi_open.3dlpi
+++ b/usr/src/man/man3dlpi/dlpi_open.3dlpi
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright (c) 2014, Joyent, Inc. All rights reserved.
.\" 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]
-.TH DLPI_OPEN 3DLPI "Nov 17, 2008"
+.TH DLPI_OPEN 3DLPI "Feb 24, 2014"
.SH NAME
dlpi_open \- open DLPI link
.SH SYNOPSIS
@@ -14,6 +15,9 @@ dlpi_open \- open DLPI link
\fBint\fR \fBdlpi_open\fR(\fBconst char *\fR\fIlinkname\fR, \fBdlpi_handle_t *\fR\fIdhp\fR,
\fBuint_t\fR \fIflags\fR);
+
+\fBint\fR \fBdlpi_open_zone\fR(\fBconst char *\fR\fIlinkname\fR, \fBconst char *\fR
+ \fIzonename\fR, \fBdlpi_handle_t *\fR\fIdhp\fR, \fBuint_t\fR \fIflags\fR);
.fi
.SH DESCRIPTION
@@ -114,6 +118,18 @@ value ensures that \fBDLPI_ETIMEDOUT\fR is returned from a \fBlibdlpi\fR
operation only in the event that the \fBDLPI\fR link becomes unresponsive. The
timeout value can be changed with \fBdlpi_set_timeout\fR(3DLPI), although this
should seldom be necessary.
+
+.sp
+.LP
+The \fBdlpi_open_zone()\fR function behaves as \fBdlpi_open()\fR, except that it
+looks for the link specified by \fBlinkname\fR in the specified zone
+\fBzonename\fR as opposed to the current zone. This function is only meaningful
+from the global zone. Instead of scanning \fB/dev/net\fR, \fBdlpi_open_zone()\fR
+scans \fB/dev/net/zone/<\fIzonename\fR> for the data link and
+\fB/dev/ipnet/zone/<\fIzonename\fR> when DLPI_DEVIPNET is present in
+\fBflags\fR. If a NULL or empty string is passed into \fBdlpi_open_zone()\fR, it
+will behave as though \fBdlpi_open\fR has been called.
+
.SH RETURN VALUES
.sp
.LP
@@ -124,7 +140,7 @@ section is returned.
.SH ERRORS
.sp
.LP
-The \fBdlpi_open()\fR function will fail if:
+The \fBdlpi_open()\fR and \fBdlpi_open_zone()\fR function will fail if:
.sp
.ne 2
.na
@@ -195,6 +211,17 @@ DLPI operation failed
See \fBattributes\fR(7) for description of the following attributes:
.sp
+.LP
+The \fBdlpi_open_zone()\fR function will fail if:
+.sp
+.ne 2
+.na
+\fB\fBDLPI_EZONENAMEINVAL\fR\fR
+.ad
+.RS 25n
+Invalid \fIzonename\fR argument
+.RE
+
.sp
.TS
box;
diff --git a/usr/src/man/man3lib/Makefile b/usr/src/man/man3lib/Makefile
index 1f698ad311..badfeadc6f 100644
--- a/usr/src/man/man3lib/Makefile
+++ b/usr/src/man/man3lib/Makefile
@@ -105,6 +105,7 @@ MANFILES= libMPAPI.3lib \
libtsol.3lib \
libumem.3lib \
libuuid.3lib \
+ libvnd.3lib \
libvolmgt.3lib \
libw.3lib \
libxnet.3lib \
diff --git a/usr/src/man/man3lib/libvnd.3lib b/usr/src/man/man3lib/libvnd.3lib
new file mode 100644
index 0000000000..ead69ff82e
--- /dev/null
+++ b/usr/src/man/man3lib/libvnd.3lib
@@ -0,0 +1,690 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH LIBVND 3LIB "Mar 06, 2014"
+.SH NAME
+libvnd \- vnd library
+
+.SH SYNOPSIS
+.LP
+.nf
+cc [ flag... ] file... -lvnd [ library... ]
+#include <libvnd.h>
+.fi
+
+.SH DESCRIPTION
+.LP
+The libvnd library provides a stable and programmatic interface to
+vnd(7D) devices. vnd devices provide the means for creating a layer two
+interface over a data link, similar to the use of libdlpi(3LIB) and
+IP(7P). In dlpi parlance, a vnd device obtains data from all service
+attachment points (SAP). For ethernet devices, this means that a vnd
+device sends and receives traffic for all ethertypes. It is intended to
+be used for services such as virtual machines which emulate layer two
+devices.
+
+.LP
+Handles to vnd(7D) devices are obtained through the use of vnd_create
+and vnd_open. With a handle, I/O can be performed and properties on the
+device can be set and retrieved. I/O on devices should be performed
+through the vnd_frameio_read and vnd_frameio_write functions. A file
+descriptor suitable for use with event ports and polling may be obtained
+through vnd_pollfd. Handles are relinquished through calls to vnd_close;
+however, devices will persist until vnd_unlink has been called.
+
+.LP
+The rest of this manual documents the interfaces, properties, errors,
+and threading model for libvnd. The in-depth description of individual
+interfaces, their arguments, and examples, are in manual pages for each
+provided interface.
+
+
+.SH INTERFACES
+.sp
+.LP
+
+The shared object libvnd.so.1 provides the public interfaces defined
+below. See Intro(3) for additional information on shared object
+interfaces. Individual functions are documented in their own manual
+pages.
+
+.sp
+.TS
+l l
+l l .
+vnd_create vnd_errno
+vnd_open vnd_syserrno
+vnd_unlink vnd_strerror
+vnd_close vnd_strsyserror
+vnd_pollfd vnd_walk
+vnd_prop_get vnd_prop_set
+vnd_prop_iter vnd_prop_writeable
+vnd_frameio_read vnd_frameio_write
+.TE
+
+.SH PROPERTIES
+
+.LP
+The following table summarizes properties of a vnd device. The
+properties can be retrieved and set with the functions
+vnd_prop_get(3VND) and vnd_prop_set(3VND). Following the table, the
+structures and properties are described in greater detail.
+
+.nf
+ +-------------------+---------------------+-------+
+ | PROPERTY | STRUCTURE | PERM |
+ +-------------------+---------------------+-------+
+ | VND_PROP_RXBUF | vnd_prop_buf_t | R/W |
+ +-------------------+---------------------+-------+
+ | VND_PROP_TXBUF | vnd_prop_buf_t | R/W |
+ +-------------------+---------------------+-------+
+ | VND_PROP_MAXBUF | vnd_prop_buf_t | R/- |
+ +-------------------+---------------------+-------+
+ | VND_PROP_MINTU | vnd_prop_buf_t | R/- |
+ +-------------------+---------------------+-------+
+ | VND_PROP_MAXTU | vnd_prop_buf_t | R/- |
+ +-------------------+---------------------+-------+
+.fi
+
+.SS Structures
+
+.LP
+The vnd_prop_buf_t structure has the following members:
+
+.in +2
+.nf
+uint64_t vpb_size;
+.fi
+.in -2
+
+.LP
+The vpb_size member refers to a size in bytes. When getting a property,
+it represents the size of that property, when setting a property, it is
+the size to set the property to.
+
+
+.SS Property Descriptions
+.sp
+.ne 2
+.na
+rxbuf
+.ad
+.sp .6
+.RS 4n
+A read/write property that controls the size of the receive buffer for
+the device. All received data enters the receive buffer until a consumer
+consumes it. If adding a received frame would exceed the size of the
+receive buffer, then that frame will be dropped. The maximum size of the
+buffer is limited by the 'maxsize' property.
+.RE
+
+.sp
+.ne 2
+.na
+txbuf
+.ad
+.sp .6
+.RS 4n
+A read/write property that controls the size of the transmit buffer. All
+in-flight transmitted data must be able to fix into the transmit buffer
+to deal with potential flow control events. If there is not enough space
+in the transmit buffer, transmit related I/O operations will either
+block or fail based on whether or not O_NONBLOCK or O_NDELAY were set
+with fcntl(2).
+.RE
+
+.sp
+.ne 2
+.na
+maxsize
+.ad
+.sp .6
+.RS 4n
+A read only property that describes the maximum size of buffers in the
+system. Properties such as rxbuf and txbuf cannot be set beyond this.
+.RE
+
+.sp
+.ne 2
+.na
+mintu
+.ad
+.sp .6
+.RS 4n
+A read only property that describes the minimum size of a frame
+transmitted to the underlying data link. Note that the minimum listed
+here may be less than the size of a valid layer two frame and therefore
+may be dropped. A frame smaller than this value will be rejected by vnd.
+.RE
+
+.sp
+.ne 2
+.na
+maxtu
+.ad
+.sp .6
+.RS 4n
+A read only property that describes the maximum size of
+a frame transmitted to the underlying data link. A frame
+larger than this value will be rejected by vnd.
+.RE
+
+
+.SH ERRORS
+.sp
+.LP
+Most interfaces provided by libvnd provide a means to retrieve a
+vnd_errno_t that describes an error that has occurred. The manuals for
+individual interfaces describe whether or not this additional error
+information is available and how to retrieve it. The following is a
+complete list of the error numbers and their names as defined in
+<sys/vnd_errno.h>. Any entries not listed here are private to the
+implementation and may change at any time.
+
+.sp
+.ne 2
+.na
+0 VND_E_SUCCESS
+.ad
+.RS 23n
+no error
+.sp
+This indicates that the operation completed successfully.
+.RE
+
+.sp
+.ne 2
+.na
+1 VND_E_NOMEM
+.ad
+.RS 23n
+not enough memory available
+.sp
+Insufficient memory was available. This is the equivalent of the
+standard system errno ENOMEM.
+.RE
+
+.sp
+.ne 2
+.na
+2 VND_E_NODATALINK
+.ad
+.RS 23n
+no such datalink
+.sp
+The data link requested to be used as part of vnd_create does not exist
+in the requested zone.
+.RE
+
+.sp
+.ne 2
+.na
+3 VND_E_NOTETHER
+.ad
+.RS 23n
+datalink not of type DL_ETHER
+.sp
+The data link used as part of a call to vnd_create is not an Ethernet
+device. vnd_create only works with Ethernet devices at this time.
+.RE
+
+.sp
+.ne 2
+.na
+4 VND_E_DLPIINVAL
+.ad
+.RS 23n
+unknown dlpi failure
+.sp
+An unexpected DLPI message was received during vnd device
+initialization.
+.RE
+
+.sp
+.ne 2
+.na
+5 VND_E_ATTACHFAIL
+.ad
+.RS 23n
+DL_ATTACH_REQ failed
+.sp
+During vnd device initialization, the dlpi call to attach to the
+requested data link failed.
+.RE
+
+.sp
+.ne 2
+.na
+6 VND_E_BINDFAIL
+.ad
+.RS 23n
+DL_BIND_REQ failed
+.sp
+
+During vnd device initialization, the dlpi call to bind to a service
+attachment point on the data link failed.
+.RE
+
+.sp
+.ne 2
+.na
+7 VND_E_PROMISCFAIL
+.ad
+.RS 23n
+DL_PROMISCON_REQ failed
+.sp
+
+During vnd device initialization, the dlpi call to enable promiscuous
+mode on the underlying device failed.
+.RE
+
+.sp
+.ne 2
+.na
+8 VND_E_DIRECTFAIL
+.ad
+.RS 23n
+DLD_CAPAB_DIRECT enable failed
+.sp
+During vnd device initialization, the dlpi call to enable the DLD fast
+path failed.
+.RE
+
+.sp
+.ne 2
+.na
+9 VND_E_CAPACKINVAL
+.ad
+.RS 23n
+bad datalink capability
+.sp
+During vnd device initialization, the kernel responded with an invalid
+capability acknowledgement.
+.RE
+
+.sp
+.ne 2
+.na
+10 VND_E_SUBCAPINVAL
+.ad
+.RS 23n
+bad datalink subcapability
+.sp
+During vnd device initialization, the kernel responded with an invalid
+sub-capability.
+.RE
+
+.sp
+.ne 2
+.na
+11 VND_E_DLDBADVERS
+.ad
+.RS 23n
+bad dld version
+.sp
+The vnd(7D) module does not support the version of the dld capability
+that the kernel sent. As such, the data path could not be brought up and
+the device could not be fully initialized.
+.RE
+
+.sp
+.ne 2
+.na
+12 VND_E_KSTATCREATE
+.ad
+.RS 23n
+failed to create kstats
+.sp
+During vnd device initialization, the necessary kstats could not be
+created.
+.RE
+
+.sp
+.ne 2
+.na
+13 VND_E_NODEV
+.ad
+.RS 23n
+no such vnd link
+.sp
+During device initialization, the requested character device did not
+exist.
+.RE
+
+.sp
+.ne 2
+.na
+14 VND_E_NONETSTACK
+.ad
+.RS 23n
+netstack doesn't exist
+.sp
+During device initialization, the networking stack for the device did
+not exist.
+.RE
+
+.sp
+.ne 2
+.na
+15 VND_E_ASSOCIATED
+.ad
+.RS 23n
+device already associated
+.sp
+During vnd device initialization, the vnd STREAMS device was already
+associated with another vnd device.
+.RE
+
+.sp
+.ne 2
+.na
+16 VND_E_ATTACHED
+.ad
+.RS 23n
+device already attached
+.sp
+The given vnd device has already been created over a data link and
+cannot be created over another one.
+.RE
+
+.sp
+.ne 2
+.na
+17 VND_E_LINKED
+.ad
+.RS 23n
+device already linked
+.sp
+The given vnd device has already been given a name and bound into the
+file system name space.
+.RE
+
+.sp
+.ne 2
+.na
+18 VND_E_BADNAME
+.ad
+.RS 23n
+invalid name
+.sp
+The requested name is not a valid name. Valid names are alphanumeric
+ascii names, along with the following ascii characters: ':', '\-', and
+\'_'. Names must be less than LIBVND_NAMELEN bytes including the null
+terminator.
+.RE
+
+.sp
+.ne 2
+.na
+19 VND_E_PERM
+.ad
+.RS 23n
+permission denied
+.sp
+A request was made from a non-global zone to manipulate a vnd device
+that belongs to a different zone.
+.RE
+
+.sp
+.ne 2
+.na
+20 VND_E_NOZONE
+.ad
+.RS 23n
+no such zone
+.sp
+A request was made which targeted a zone that did not exist.
+.RE
+
+.sp
+.ne 2
+.na
+21 VND_E_STRINIT
+.ad
+.RS 23n
+failed to initialize vnd stream module
+.sp
+During vnd device initialization, the vnd STREAMS module could not be
+pushed onto the data link's stream head.
+.RE
+
+.sp
+.ne 2
+.na
+22 VND_E_NOTATTACHED
+.ad
+.RS 23n
+device not attached
+.sp
+A request was made that requires a vnd device be attached to a data
+link, such as a call to change a property. The device was not attached
+to a data link.
+.RE
+
+.sp
+.ne 2
+.na
+23 VND_E_NOTLINKED
+.ad
+.RS 23n
+device not linked
+.sp
+A request was made to a vnd device that requires the vnd device to be
+named and present in /dev. The given device was not linked into /dev at
+the time of the call.
+.RE
+
+.sp
+.ne 2
+.na
+24 VND_E_LINKEXISTS
+.ad
+.RS 23n
+another device has the same link name
+.sp
+When trying to link a given vnd device into a zones /dev name space,
+another device already exists with the same name.
+.RE
+
+.sp
+.ne 2
+.na
+25 VND_E_MINORNODE
+.ad
+.RS 23n
+failed to create minor node
+.sp
+While trying to link a vnd device into the /devices and /dev name space,
+the call to ddi_create_minor_node() failed.
+.RE
+
+.sp
+.ne 2
+.na
+26 VND_E_BUFTOOBIG
+.ad
+.RS 23n
+requested buffer size is too large
+.sp
+The requested buffer size exceeds the maximum valid value for the given
+property.
+.RE
+
+.sp
+.ne 2
+.na
+27 VND_E_BUFTOOSMALL
+.ad
+.RS 23n
+requested buffer size is too small
+.sp
+The requested buffer size is less than the minimum buffer size. This
+generally occurs when making the buffer size less than the maximum
+transmission unit.
+.RE
+
+.sp
+.ne 2
+.na
+28 VND_E_DLEXCL
+.ad
+.RS 23n
+unable to obtain exclusive access to dlpi link, link busy
+.sp
+When a vnd device is created, it expects exclusive active access to the
+device. If any other active dlpi consumers, such as IP, are already
+using the device, then the vnd device will not be created. Passive
+consumers, such as snoop, can still use a device that has been
+exclusively opened.
+.RE
+
+.sp
+.ne 2
+.na
+28 VND_E_DIRECTNOTSUP
+.ad
+.RS 23n
+DLD direct capability not supported over data link
+.sp
+The data link that the vnd device was created over does not supported
+the DLD Direct capability. As such, the data path could not be
+initialized.
+.RE
+
+.sp
+.ne 2
+.na
+30 VND_E_BADPROPSIZE
+.ad
+.RS 23n
+invalid property size
+.sp
+The size of the data passed into vnd_prop_get or vnd_prop_set is
+incorrect and does not match the expected data size.
+.RE
+
+.sp
+.ne 2
+.na
+31 VND_E_BADPROP
+.ad
+.RS 23n
+invalid property
+.sp
+An unknown property identifier was specified. For a list of valid
+properties, see the section above entitled "PROPERTIES".
+.RE
+
+.sp
+.ne 2
+.na
+32 VND_E_PROPRDONLY
+.ad
+.RS 23n
+property is read only
+.sp
+An operation tried to update the value of a read only property. For a
+list of which properties are read only and which are readable and
+writeable, see the section above entitled "PROPERTIES".
+.RE
+
+.sp
+.ne 2
+.na
+33 VND_E_SYS
+.ad
+.RS 23n
+unexpected system error
+.sp
+This indicates that there is no vnd specific error available and that
+the system errno is valid. The system errno can be obtained and printed
+through vnd_syserrno and vnd_strsyserror. The possible values and their
+meanings are documented in Intro(2).
+.RE
+
+.sp
+.ne 2
+.na
+34 VND_E_CAPABPASS
+.ad
+.RS 23n
+capabilities invalid, pass-through module detected
+.sp
+While negotiating capabilities, a pass-through module was detected and
+the capability had to be discarded. Because of this, the data path could
+not be initialized.
+.RE
+
+
+.SH THREADING
+
+.LP
+The libvnd library is not truly MT-safe. MT-safety is provided on
+the granularity of a given vnd_handle_t. Operations on a single
+vnd_handle_t are unsafe; however, operations on different handles are
+MT-safe. If a single vnd_handle_t is used by multiple threads, it
+is the caller's responsibility to provide locking to ensure that
+multiple threads aren't simultaneously calling into libvnd on a
+single handle.
+
+
+.SH FILES
+.sp
+.ne 2
+.na
+/usr/lib/libvnd.so.1
+.ad
+.RS 27n
+shared object
+.RE
+
+.sp
+.ne 2
+.na
+/usr/lib/64/libvnd.so.1
+.ad
+.RS 27n
+64-bit shared object
+.RE
+
+.SH ATTRIBUTES
+
+.sp
+.LP
+See attributes(5) for descriptions of the following attributes:
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Stability Committed
+_
+MT-Level See "THREADING"
+.TE
+
+.SH SEE ALSO
+
+.sp
+.LP
+attributes(5), Intro(2), fcntl(2), Intro(3), fcntl.h(3HEAD), libdlpi(3LIB), port_create(3C), vnd(7D)
+.sp
+.LP
+vnd_close(3VND), vnd_create(3VND), vnd_errno(3VND),
+vnd_frameio_read(3VND), vnd_frameio_write(3VND), vnd_open(3VND)
+vnd_pollfd(3VND), vnd_prop_get(3VND), vnd_prop_iter(3VND),
+vnd_prop_set(3VND),
+vnd_prop_writeable(3VND), vnd_walk(3VND)
diff --git a/usr/src/man/man3vnd/Makefile b/usr/src/man/man3vnd/Makefile
new file mode 100644
index 0000000000..64abf9dcd6
--- /dev/null
+++ b/usr/src/man/man3vnd/Makefile
@@ -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.
+#
+
+include $(SRC)/Makefile.master
+
+MANSECT= 3vnd
+
+MANFILES= vnd_create.3vnd \
+ vnd_errno.3vnd \
+ vnd_frameio_read.3vnd \
+ vnd_pollfd.3vnd \
+ vnd_prop_get.3vnd \
+ vnd_prop_iter.3vnd \
+ vnd_prop_writeable.3vnd \
+ vnd_walk.3vnd
+
+MANLINKS= frameio_t.3vnd \
+ framevec_t.3vnd \
+ vnd_close.3vnd \
+ vnd_frameio_write.3vnd \
+ vnd_open.3vnd \
+ vnd_prop_set.3vnd \
+ vnd_prop_iter_f.3vnd \
+ vnd_strerror.3vnd \
+ vnd_strsyserror.3vnd \
+ vnd_syserrno.3vnd \
+ vnd_unlink.3vnd \
+ vnd_walk_cb_f.3vnd
+
+# vnd_create.3vnd
+vnd_open.3vnd := LINKSRC = vnd_create.3vnd
+vnd_unlink.3vnd := LINKSRC = vnd_create.3vnd
+vnd_close.3vnd := LINKSRC = vnd_create.3vnd
+
+# vnd_errno.3vnd
+vnd_strerror.3vnd := LINKSRC = vnd_errno.3vnd
+vnd_syserrno.3vnd := LINKSRC = vnd_errno.3vnd
+vnd_strsyserror.3vnd := LINKSRC = vnd_errno.3vnd
+
+# vnd_frameio_read.3vnd
+vnd_frameio_write.3vnd := LINKSRC = vnd_frameio_read.3vnd
+framevec_t.3vnd := LINKSRC = vnd_frameio_read.3vnd
+frameio_t.3vnd := LINKSRC = vnd_frameio_read.3vnd
+
+# vnd_prop_get.3vnd
+vnd_prop_set.3vnd := LINKSRC = vnd_prop_get.3vnd
+
+# vnd_prop_iter.3vnd
+vnd_prop_iter_f.3vnd := LINKSRC = vnd_prop_iter.3vnd
+
+# vnd_walk.3vnd
+vnd_walk_cb_f.3vnd := LINKSRC = vnd_walk.3vnd
+
+.KEEP_STATE:
+
+include $(SRC)/man/Makefile.man
+
+install: $(ROOTMANFILES) $(ROOTMANLINKS)
diff --git a/usr/src/man/man3vnd/vnd_create.3vnd b/usr/src/man/man3vnd/vnd_create.3vnd
new file mode 100644
index 0000000000..d29237a60c
--- /dev/null
+++ b/usr/src/man/man3vnd/vnd_create.3vnd
@@ -0,0 +1,280 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VND_CREATE 3VND "Feb 21, 2014"
+
+.SH NAME
+
+vnd_create, vnd_open, vnd_unlink, vnd_close \- create, open, and destroy
+vnd devices
+
+.SH SYNOPSIS
+
+.LP
+.nf
+cc [ flag... ] file... -lvnd [ library... ]
+#include <libvnd.h>
+
+vnd_handle_t *vnd_create(const char *zonename, const char *datalink,
+ const char *linkname, vnd_errno_t *vnderr, int *syserr);
+
+vnd_handle_t *vnd_open(const char *zonename, const char *linkname,
+ vnd_errno_t *vnderr, int *syserr);
+
+int vnd_unlink(vnd_handle_t *vhp);
+
+void vnd_close(vnd_handle_t *vhp);
+.fi
+
+
+.SH DESCRIPTION
+.LP
+These functions create vnd devices, obtain handles to extant vnd
+devices, and close handles to vnd devices, for use with the rest of
+libvnd(3LIB).
+
+.LP
+The vnd_create function creates a new vnd device in the zone specified
+by zonename. The zone name argument may be null, in which case the
+caller's current zone is used instead. The vnd device and data link it
+is created over must both be in the same zone. The datalink argument
+indicates the name of the DLPI data link to create the vnd device over.
+The linkname argument indicates the name of the new vnd device. The
+linkname argument must be less than VND_NAMELEN characters long,
+excluding the null terminator. It should be an alphanumeric string. The
+only non-alphanumeric characters allowed are ':', '-', and \'_'.
+Neither the datalink argument nor linkname argument may be NULL. A
+handle to the created device is returned to the caller. Once the
+vnd_create function returns, the device can be subsequently opened with
+a call to vnd_open. The named device persists until a call to vnd_unlink
+or the containing zone is halted. Creating a vnd device requires
+PRIV_SYS_NET_CONFIG as well as PRIV_RAWACCESS. The arguments vnderr and
+syserr are used to obtain errors in the cases where the call to
+vnd_create fails. Both arguments may be NULL pointers, in which case the
+more detailed error information is discarded.
+
+.LP
+The vnd_open function opens an existing vnd device and returns a
+unique handle to that device. The vnd device to open is specified by
+both zonename and linkname. The zonename argument specifies what zone
+to look for the vnd device in. The linkname specifies the name of the
+link. The zonename argument may be NULL. If it is, the current zone is
+used. Similar to vnd_create, the integer values pointed to by the
+arguments vnderr and syserr will be filled in with additional error
+information in the cases where a call to vnd_open fails. Both
+arguments may be NULL to indicate that the error information is not
+requested, though this is not recommended.
+
+.LP
+The vnd_unlink function unlinks the vnd device specified by the vnd
+handle vhp. This unlink is similar to the use of unlink in a file
+system. After a call to unlink, the vnd device will no longer be
+accessible by callers to vnd_open and the name will be available for
+use in vnd_create. However, the device will continue to exist until
+all handles to the device have been closed.
+
+.LP
+The vnd_close function relinquishes the vnd device referenced by the
+handle vhp. After a call to vnd_close, the handle is invalidated and
+must not be used by the consumer again. The act of calling vnd_close
+on a handle does not remove the device. The device is persisted as
+long as vnd_unlink has not been called on the device or the containing
+zone has not been destroyed.
+
+.SH RETURN VALUES
+
+.LP
+Upon successful completion, the functions vnd_create and vnd_open
+return a pointer to a vnd_handle_t. This handle is used for all
+subsequent library operations. If either function fails, then a NULL
+pointer is returned and more detailed error information is filled into
+the integers pointed to by vnderr and syserr. The vnderr and syserr
+correspond to the values that would normally be returned by a call to
+vnd_errno(3VND) and vnd_syserrno(3VND). For the full list of possible
+errors see libvnd(3LIB).
+
+.LP
+The vnd_unlink function returns zero on success and -1 on failure. On
+failure, the vnd and system errnos are updated and available through
+the vnd_errno(3VND) and vnd_syserrno(3VND) functions.
+
+.LP
+The vnd_close function does not return any values nor does it set
+vnderr or syserr. The handle passed to vnd_close can no longer be
+used.
+
+.SH EXAMPLES
+.LP
+Example 1 Creating a device
+.sp
+.LP
+
+The following sample C program shows how to create a vnd device over
+an existing datalink named "net0" that other applications can open
+and use as "vnd0".
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr;
+
+ /* Errors are considered fatal */
+ vhp = vnd_create(NULL, "net0", "vnd0", &vnderr, &syserr);
+
+ if (vhp == NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to create device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to create device: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ (void) printf("successfully created vnd0\n");
+ vnd_close(vhp);
+ return (0);
+}
+.fi
+.in -2
+
+.LP
+Example 2 Opening an existing device in another zone
+.sp
+.LP
+
+The following sample C program opens the device named "vnd1" in the zone
+named "turin" for further use.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr, ret;
+
+ vhp = vnd_open("turin", "vnd1", &vnderr, &syserr);
+ if (vhp != NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ /*
+ * Use the device vnd1 with the handle vhp with any of
+ * the other interfaces documented in libvnd(3LIB) here.
+ *
+ * After an arbitrary amount of code, the program will
+ * set the variable ret with the exit code for the
+ * program and should execute the following code before
+ * returning.
+ */
+ vnd_close(vhp);
+ return (ret);
+}
+.fi
+.in -2
+
+
+.LP
+Example 3 Removing a device
+.sp
+.LP
+
+The following sample C program removes a vnd device named vnd0. This
+program makes it so no additional programs can access the device.
+However, if anyone is actively using it, it will still exist,
+similar to calling unlink(2).
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr, ret;
+
+ vhp = vnd_open(NULL, "vnd0", &vnderr, &syserr);
+ if (vhp != NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ if (vnd_unlink(vhp) != 0) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to unlink device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to unlink device: %s",
+ vnd_strerror(vnderr));
+ ret = 1;
+ } else {
+ (void) printf("successfully unlinked vnd0!\n");
+ ret = 0;
+ }
+
+ vnd_close(vhp);
+ return (ret);
+}
+.fi
+.in -2
+
+.SH ATTRIBUTES
+.sp
+.LP
+See attributes(5) for descriptions of the following attributes:
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Stability Committed
+_
+MT-Level See "THREADING" in libvnd(3LIB)
+.TE
+
+.SH SEE ALSO
+
+libvnd(3LIB), vnd_errno(3VND), vnd_syserrno(3VND), attributes(5), privileges(5)
diff --git a/usr/src/man/man3vnd/vnd_errno.3vnd b/usr/src/man/man3vnd/vnd_errno.3vnd
new file mode 100644
index 0000000000..ddd6126dd1
--- /dev/null
+++ b/usr/src/man/man3vnd/vnd_errno.3vnd
@@ -0,0 +1,170 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VND_ERRNO 3VND "Feb 21, 2014"
+
+.SH NAME
+
+vnd_errno, vnd_syserrno, vnd_strerror, vnd_strsyserror \- obtain and
+translate vnd errors
+
+
+.SH SYNOPSIS
+
+.LP
+.nf
+cc [ flag... ] file... -lvnd [ library... ]
+#include <libvnd.h>
+
+uint32_t vnd_errno(vnd_handle_t *vhp);
+
+const char *vnd_strerror(vnd_errno_t err);
+
+int vnd_syserrno(vnd_handle_t *vhp);
+
+const char *vnd_strsyserror(int syserr);
+.fi
+
+.SH DESCRIPTION
+
+.LP
+The libvnd(3LIB) library supports a complementary array of errors that
+give more specific error information than the traditional set of
+system errors available via errno(3C). When an error occurs, consumers
+should call the vnd_errno function first and check its value. If the
+value of the vnd_errno_t is VND_E_SYS, then the system errno should be
+checked. If the vnd_errno_t is not VND_E_SYS, then the contents of the
+system errno returned from vnd_syserrno are undefined. Both the vnd
+and system errors are only valid for a given handle after a libvnd
+library function returned an error and before another libvnd library
+function is called on the same handle. The act of making an additional
+function call with the same vnd_handle_t invalidates any prior vnd or
+system error numbers. For the full list of valid vnd errors see
+libvnd(3LIB). For the full list of valid system errors, see Intro(2).
+
+.LP
+The vnd_errno and vnd_syserrno functions retrieve the most recent vnd
+and syserr error number respectively from a vnd handle vhp.
+
+.LP
+The vnd_strerror function translates a vnd_errno_t err to a
+corresponding string and returns a pointer to that constant string.
+
+.LP
+The vnd_syserrno function is analogous to the vnd_strerror function,
+except that it translates a system error back to a string.
+
+
+.SH RETURN VALUES
+
+.LP
+The vnd_errno function returns a vnd_errno_t which contains the vnd
+error information.
+
+.LP
+The vnd_syserror function returns an integer which contains the system
+error information. These values are the same as those returned by
+errno(3C).
+
+.LP
+The vnd_strerror function returns a pointer to a constant string. If
+the error passed in is unknown, the string "unknown error" is
+returned.
+
+.LP
+The vnd_strsyserror function returns a pointer to the translated
+constant string. If an unknown error number is passed, it returns the
+string "Unknown error". If an error occurs, it returns a NULL pointer.
+
+.SH EXAMPLES
+
+.LP
+Example 1 Obtaining errors from a vnd_handle_t
+
+.sp
+.LP
+The following sample C function, which can be incorporated into a larger
+program, shows how to obtain the vnd and system errors from a
+vnd_handle_t after a vnd interface on a handle failed.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+
+static void
+print_errnos(vnd_handle_t *vhp)
+{
+ vnd_errno_t vnderr;
+ int syserr;
+
+ vnderr = vnd_errno(vhp);
+ syserr = vnd_syserrno(vhp);
+
+ (void) printf("vnd err: %d, sys err: %d\n",
+ vnderr, syserr);
+}
+.fi
+.in -2
+
+.LP
+Example 2 A perror-like function
+
+.sp
+.LP
+The following sample C function which can be incorporated into a
+larger program shows how to write a perror-like function to print
+out error messages for a vnd device.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+
+static void
+sample_perror(const char *msg, vnd_error_t vnderr, int syserr)
+{
+ (void) fprintf(stderr, "%s: %s", msg,
+ vnderr != VND_E_SYS ? vnd_strerror(vnderr) :
+ vnd_strsyserror(syserr));
+}
+.fi
+.in -2
+
+.SH ATTRIBUTES
+.sp
+.LP
+See attributes(5) for descriptions of the following attributes:
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Stability Committed
+_
+MT-Level See below
+.TE
+
+.LP
+The MT-Level of the functions vnd_strerror and vnd_strsyserror is
+MT-Safe. See "THREADING" in libvnd(3LIB) for a discussion of the
+MT-Level of vnd_errno and vnd_syserrno.
+
+
+.SH SEE ALSO
+
+Intro(2), errno(3C), libvnd(3LIB), attributes(5)
diff --git a/usr/src/man/man3vnd/vnd_frameio_read.3vnd b/usr/src/man/man3vnd/vnd_frameio_read.3vnd
new file mode 100644
index 0000000000..5fc65c96a3
--- /dev/null
+++ b/usr/src/man/man3vnd/vnd_frameio_read.3vnd
@@ -0,0 +1,705 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VND_FRAMEIO_READ 3VND "Mar 06, 2014"
+
+.SH NAME
+
+vnd_frameio_read, vnd_frameio_write \- perform framed I/O to a vnd device
+
+.SH SYNOPSIS
+
+.LP
+.nf
+cc [ flag... ] file... -lvnd [ library... ]
+#include <libvnd.h>
+
+int vnd_frameio_read(vnd_handle_t *vhp, frameio_t *fiop);
+
+int vnd_frameio_write(vnd_handle_t *vhp, frameio_t *fiop);
+.fi
+
+.SH DESCRIPTION
+.LP
+Framed I/O is a general means to manipulate data that is inherently
+framed, meaning that there is a maximum frame size, but the data may
+often be less than that size. As an example, an Ethernet device's MTU
+describes the maximum frame size, but the size of an individual frame
+is often much less. You can read a single frame at a time, or you can
+read multiple frames in a single call.
+
+In addition, framed I/O allows the consumer to break individual frames
+into a series of vectors. This is analogous to the use of an iovec(9S)
+with readv(2) and writev(2).
+
+vnd_frameio_read performs a framed I/O read of the device represented by
+the handle vhp, with the framed I/O data described by fiop.
+vnd_frameio_write works in the same manner, except performing a write
+instead of a read.
+
+.LP
+The basic vector component of the frameio_t is the framevec_t. Each
+framevec_t represents a single vector entry. An array of these is
+present in the frameio_t. The framevec_t structure has the following
+members:
+
+.in +2
+.nf
+void *fv_buf /* data buffer */
+size_t fv_buflen; /* total size of buffer */
+size_t fv_actlen; /* amount of buffer consumed */
+.fi
+.in -2
+
+.LP
+The fv_buf member points to a buffer which contains the data for this
+individual vector. When reading, data is consumed from fv_buf. When
+writing, data is written into fv_buf.
+
+The fv_buflen should indicate the total amount of data that is in the
+buffer. When reading, it indicates the size of the buffer. It must be
+set prior to calling vnd_frameio_read(). When writing, it indicates the
+amount of data that is valid in the buffer.
+
+The fv_actlen is a read-only member. It is set on successful return of
+the functions vnd_frameio_read and vnd_frameio_write. When reading, it
+is updated with the amount of data that was read into fv_buf. When
+writing, it is instead updated with the amount of data from fv_buf that
+was actually consumed. Generally when writing data, a framevec_t will
+either be entirely consumed or it will not be consumed at all.
+
+
+.LP
+A series of framevec_t's is encapsulated in a frameio_t. The frameio_t
+structure has the following members:
+
+.in +2
+.nf
+uint_t fio_version; /* current version */
+uint_t fio_nvpf; /* number of vectors in one frame */
+uint_t fio_nvecs; /* The total number of vectors */
+framevec_t fio_vecs[]; /* vectors */
+.fi
+.in -2
+
+.LP
+The fio_version member represents the current version of the frameio_t.
+The fio_version should be set to the macro FRAMEIO_CURRENT_VERSION,
+which is currently 1.
+
+The members fio_nvpf and fio_nvecs describe the number of frames that
+exist. fio_nvecs describes the total number of vectors that are present
+in fio_vecs. The upper bound on this is described by FRAMEIO_NVECS_MAX
+which is currently 32. fio_nvpf describe the number of vectors that
+should be used to make up each frame. By setting fio_vecs to be an even
+multiple of fio_nvpf, multiple frames can be read or written in a single
+call.
+
+After a call to vnd_frameio_read or vnd_frameio_write fio_nvecs is
+updated with total number of vectors read or written to. This value can
+be divided by fio_nvpf to determine the total number of frames that were
+written or read.
+
+.LP
+Each frame can be broken down into a series of multiple vectors. As an
+example, someone might want to break Ethernet frames into mac headers
+and payloads. The value of fio_nvpf would be set to two, to indicate
+that a single frame consists of two different vector components. The
+member fio_nvecs describes the total number of frames. As such, the
+value of fio_vecs divided by fio_nvpf describes the total number of
+frames that can be consumed in one call. As a result of this, fio_nvpf
+must evenly divide fio_vecs. If fio_nvpf is set to two and
+fio_nvecs is set to ten, then a total of five frames can be processed
+at once, each frame being broken down into two different vector
+components.
+
+A given frame will never overflow the number of vectors described by
+fio_nvpf. Consider the case where each vector component has a buffer
+sized to 1518 bytes, fio_nvpf is set to one, and fio_nvecs is set to
+three. If a call to vnd_frameio_read is made and four 500 byte Ethernet
+frames come in, then each frame will be mapped to a single vector. The
+500 bytes will be copied into fio_nvecs[i]->fio_buf and
+fio_nvecs[i]->fio_actlen will be set to 500. To contrast this, if
+readv(2) had been called, the first three frames would all be in the
+first iov and the fourth frame's first eight bytes would be in the first
+iov and the remaining in the second.
+
+.LP
+The user must properly initialize fio_nvecs framevec_t's worth of the
+fio_vecs array. When multiple vectors comprise a frame, fv_buflen data
+is consumed before moving onto the next vector. Consider the case
+where the user wants to break a vector into three different
+components, an 18 byte vector for an Ethernet VLAN header, a 20 byte
+vector for an IPv4 header, and a third 1500 byte vector for the
+remaining payload. If a frame was received that only had 30 bytes,
+then the first 18 bytes would fill up the first vector, the remaining
+12 bytes would fill up the IPv4 header. If instead a 524 byte frame
+came in, then the first 18 bytes would be placed in the first vector,
+the next 24 bytes would be placed in the next vector, and the remaining
+500 bytes in the third.
+
+.LP
+The functions vnd_frameio_read and vnd_frameio_write operate in both
+blocking and non-blocking mode. If either O_NONBLOCK or O_NDELAY have
+been set on the file descriptor, then the I/O will behave in
+non-blocking mode. When in non-blocking mode, if no data is available
+when vnd_frameio_read is called, EAGAIN is returned. When
+vnd_frameio_write is called in non-blocking mode, if sufficient buffer
+space to hold all of the output frames is not available, then
+vnd_frameio_write will return EAGAIN. To know when the given vnd device
+has sufficient space, the device fires POLLIN/POLLRDNORM when data is
+available for read and POLLOUT/POLLRDOUT when space in the buffer has
+opened up for write. These events can be watched for through
+port_associate(3C) and similar routines with a file descriptor returned
+from vnd_polfd(3VND).
+
+.LP
+When non-blocking mode is disabled, calls to vnd_frameio_read will
+block until some amount of data is available. Calls to
+vnd_frameio_write will block until sufficient buffer space is
+available.
+
+.LP
+Similar to read(2) and write(2), vnd_frameio_read and
+vnd_frameio_write make no guarantees about the ordering of data when
+multiple threads simultaneously call the interface. While the data
+itself will be atomic, the ordering of multiple simultaneous calls is
+not defined.
+
+.SH RETURN VALUES
+
+.LP
+The vnd_frameio_read function returns zero on success. The member
+fio_nvecs of fiop is updated with the total number of vectors that had
+data read into them. Each updated framevec_t will have the buffer
+pointed to by fv_buf filled in with data, and fv_actlen will be
+updated with the amount of valid data in fv_buf.
+
+.LP
+The vnd_frameio_write function returns zero on success. The member
+fio_nvecs of fiop is updated with the total number of vectors that
+were written out to the underlying datalink. The fv_actlen of each
+vector is updated to indicate the amount of data that was written from
+that buffer.
+
+.LP
+On failure, both vnd_frameio_read and vnd_frameio_write return -1. The
+vnd and system error numbers are updated and available via
+vnd_errno(3VND) and vnd_syserrno(3VND). See ERRORS below for a list of
+errors and their meaning.
+
+
+.SH ERRORS
+.LP
+The functions vnd_frameio_read and vnd_frameio_write always set the
+vnd error to VND_E_SYS. The following system errors will be
+encountered:
+
+.sp
+.ne 2
+.na
+EAGAIN
+.ad
+.RS 10n
+Insufficient system memory was available for the operation.
+.sp
+Non-blocking mode was enabled and during the call to vnd_frameio_read,
+no data was available. Non-blocking mode was enabled and during the call
+to vnd_frameio_write, insufficient buffer space was available.
+.RE
+
+.sp
+.ne 2
+.na
+ENXIO
+.ad
+.RS 10n
+The vnd device referred to by vhp is not currently attached to an
+underlying data link and cannot send data.
+.RE
+
+.sp
+.ne 2
+.na
+EFAULT
+.ad
+.RS 10n
+The fiop argument points to an illegal address or the fv_buf members of
+the framevec_t's associated with the fiop member fio_vecs point to
+illegal addresses.
+.RE
+
+.sp
+.ne 2
+.na
+EINVAL
+.ad
+.RS 10n
+The fio_version member of fiop was unknown, the number of vectors
+specified by fio_nvecs is zero or greater than FRAMEIO_NVECS_MAX,
+fio_nvpf equals zero, fio_nvecs is not evenly divisible by fio_nvpf, or
+a buffer in fio_vecs[] has set fv_buf or fv_buflen to zero.
+.RE
+
+
+.sp
+.ne 2
+.na
+EINTR
+.ad
+.RS 10n
+A signal was caught during vnd_frameio_read or vnd_frameio_write, and no
+data was transferred.
+.RE
+
+
+.sp
+.ne 2
+.na
+EOVERFLOW
+.ad
+.RS 10n
+During vnd_frameio_read, the size of a frame specified by fiop->fio_nvpf
+and fiop->fio_vecs[].fv_buflen cannot contain a frame.
+.sp
+In a ILP32 environment, more data than UINT_MAX would be set in
+fv_actlen.
+.RE
+
+
+.sp
+.ne 2
+.na
+ERANGE
+.ad
+.RS 10n
+During vnd_frameio_write, the size of a frame is less than the device's
+minimum transmission unit or it is larger than the size of the maximum
+transmission unit.
+.RE
+
+
+.SH EXAMPLES
+
+.LP
+Example 1 Read a single frame with a single vector
+
+.sp
+.LP
+The following sample C program opens an existing vnd device named
+"vnd0" in the current zone and performs a blocking read of a single
+frame from it.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr, i;
+ frameio_t *fiop;
+
+ fiop = malloc(sizeof (frameio_t) + sizeof (framevec_t));
+ if (fiop == NULL) {
+ perror("malloc frameio_t");
+ return (1);
+ }
+ fiop->fio_version = FRAMEIO_CURRENT_VERSION;
+ fiop->fio_nvpf = 1;
+ fiop->fio_nvecs = 1;
+ fiop->fio_vecs[0].fv_buf = malloc(1518);
+ fiop->fio_vecs[0].fv_buflen = 1518;
+ if (fiop->fio_vecs[0].fv_buf == NULL) {
+ perror("malloc framevec_t.fv_buf");
+ free(fiop);
+ return (1);
+ }
+
+ vhp = vnd_open(NULL, "vnd1", &vnderr, &syserr);
+ if (vhp != NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ free(fiop->fio_vecs[0].fv_buf);
+ free(fiop);
+ return (1);
+ }
+
+ if (frameio_read(vhp, fiop) != 0) {
+ vnd_errno_t vnderr = vnd_errno(vhp);
+ int syserr = vnd_syserrno(vhp);
+
+ /* Most consumers should retry on EINTR */
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to read: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to read: %s",
+ vnd_strerror(vnderr));
+ vnd_close(vhp);
+ free(fiop->fio_vecs[0].fv_buf);
+ free(fiop);
+ return (1);
+ }
+
+
+ /* Consume the data however it's desired */
+ (void) printf("received %d bytes\n", fiop->fio_vecs[0].fv_actlen);
+ for (i = 0; i < fiop->fio_vecs[0].fv_actlen)
+ (void) printf("%x ", fiop->fio_vecs[0].fv_buf[i]);
+
+ vnd_close(vhp);
+ free(fiop->fio_vecs[0].fv_buf);
+ free(viop);
+ return (0);
+}
+.fi
+.in -2
+
+.LP
+Example 2 Write a single frame with a single vector
+.sp
+.LP
+The following sample C program opens an existing vnd device named
+"vnd0" in the current zone and performs a blocking write of a single
+frame to it.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+#include <string.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr;
+ frameio_t *fiop;
+
+ fiop = malloc(sizeof (frameio_t) + sizeof (framevec_t));
+ if (fiop == NULL) {
+ perror("malloc frameio_t");
+ return (1);
+ }
+ fiop->fio_version = FRAMEIO_CURRENT_VERSION;
+ fiop->fio_nvpf = 1;
+ fiop->fio_nvecs = 1;
+ fiop->fio_vecs[0].fv_buf = malloc(1518);
+ if (fiop->fio_vecs[0].fv_buf == NULL) {
+ perror("malloc framevec_t.fv_buf");
+ free(fiop);
+ return (1);
+ }
+
+ /*
+ * Fill in your data however you desire. This is an entirely
+ * invalid frame and while the frameio write may succeed, the
+ * networking stack will almost certainly drop it.
+ */
+ (void) memset(fiop->fio_vecs[0].fv_buf, 'r', 1518);
+ fiop->fio_vecs[0].fv_buflen = 1518;
+
+ vhp = vnd_open(NULL, "vnd0", &vnderr, &syserr);
+ if (vhp != NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ free(fiop->fio_vecs[0].fv_buf);
+ free(fiop);
+ return (1);
+ }
+
+ if (frameio_write(vhp, fiop) != 0) {
+ /* Most consumers should retry on EINTR */
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to write: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to write: %s",
+ vnd_strerror(vnderr));
+ vnd_close(vhp);
+ free(fiop->fio_vecs[0].fv_buf);
+ free(fiop);
+ return (1);
+ }
+
+
+ (void) printf("wrote %d bytes\n", fiop->fio_vecs[0].fv_actlen);
+
+ vnd_close(vhp);
+ free(fiop->fio_vecs[0].fv_buf);
+ free(viop);
+ return (0);
+}
+.fi
+.in -2
+
+.LP
+Example 3 Read frames comprised of multiple vectors
+.sp
+.LP
+The following sample C program is similar to example 1, except instead
+of reading a single frame consisting of a single vector it reads
+multiple frames consisting of two vectors. The first vector has room for
+an 18 byte VLAN enabled Ethernet header and the second vector has room
+for a 1500 byte payload.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr, i, nframes;
+ frameio_t *fiop;
+
+ /* Allocate enough framevec_t's for 5 frames */
+ fiop = malloc(sizeof (frameio_t) + sizeof (framevec_t) * 10);
+ if (fiop == NULL) {
+ perror("malloc frameio_t");
+ return (1);
+ }
+ fiop->fio_version = FRAMEIO_CURRENT_VERSION;
+ fiop->fio_nvpf = 2;
+ fiop->fio_nvecs = 10;
+ for (i = 0; i < 10; i += 2) {
+ fiop->fio_vecs[i].fv_buf = malloc(18);
+ fiop->fio_vecs[i].fv_buflen = 18;
+ if (fiop->fio_vecs[i].fv_buf == NULL) {
+ perror("malloc framevec_t.fv_buf");
+ /* Perform appropriate memory cleanup */
+ return (1);
+ }
+ fiop->fio_vecs[i+1].fv_buf = malloc(1500);
+ fiop->fio_vecs[i+1].fv_buflen = 1500;
+ if (fiop->fio_vecs[i+1].fv_buf == NULL) {
+ perror("malloc framevec_t.fv_buf");
+ /* Perform appropriate memory cleanup */
+ return (1);
+ }
+ }
+
+ vhp = vnd_open(NULL, "vnd1", &vnderr, &syserr);
+ if (vhp != NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ /* Perform appropriate memory cleanup */
+ return (1);
+ }
+
+ if (frameio_read(vhp, fiop) != 0) {
+ /* Most consumers should retry on EINTR */
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to read: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to read: %s",
+ vnd_strerror(vnderr));
+ vnd_close(vhp);
+ /* Perform appropriate memory cleanup */
+ return (1);
+ }
+
+ /* Consume the data however it's desired */
+ nframes = fiop->fio_nvecs / fiop->fio_nvpf;
+ (void) printf("consumed %d frames!\n", nframes);
+ for (i = 0; i < nframes; i++) {
+ (void) printf("received %d bytes of Ethernet Header\n",
+ fiop->fio_vecs[i].fv_actlen);
+ (void) printf("received %d bytes of payload\n",
+ fiop->fio_vecs[i+1].fv_actlen);
+ }
+
+ vnd_close(vhp);
+ /* Do proper memory cleanup */
+ return (0);
+}
+.fi
+.in -2
+
+.LP
+Example 4 Perform non-blocking reads of multiple frames with a
+single vector
+.sp
+.LP
+In this sample C program, opens an existing vnd device named "vnd0" in
+the current zone, ensures that it is in non-blocking mode, and uses
+event ports to do device reads.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+#include <port.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/tpyes.h>
+#include <fcntl.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr, i, nframes, port, vfd;
+ frameio_t *fiop;
+
+ port = port_create();
+ if (port < 0) {
+ perror("port_create");
+ return (1);
+ }
+ /* Allocate enough framevec_t's for 10 frames */
+ fiop = malloc(sizeof (frameio_t) + sizeof (framevec_t) * 10);
+ if (fiop == NULL) {
+ perror("malloc frameio_t");
+ (void) close(port);
+ return (1);
+ }
+ fiop->fio_version = FRAMEIO_CURRENT_VERSION;
+ fiop->fio_nvpf = 1;
+ fiop->fio_nvecs = 10;
+ for (i = 0; i < 10; i++) {
+ fiop->fio_vecs[i].fv_buf = malloc(1518);
+ fiop->fio_vecs[i].fv_buflen = 1518;
+ if (fiop->fio_vecs[i].fv_buf == NULL) {
+ perror("malloc framevec_t.fv_buf");
+ /* Perform appropriate memory cleanup */
+ (void) close(port);
+ return (1);
+ }
+ }
+
+ vhp = vnd_open(NULL, "vnd1", &vnderr, &syserr);
+ if (vhp != NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ /* Perform appropriate memory cleanup */
+ (void) close(port);
+ return (1);
+ }
+ vfd = vnd_pollfd(vhp);
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
+ (void) fprintf(stderr, "failed to enable non-blocking mode: %s",
+ strerrror(errno));
+ }
+
+ for (;;) {
+ port_event_t pe;
+
+ if (port_associate(port, PORT_SOURCE_FD, vfd, POLLIN,
+ vhp) != 0) {
+ perror("port_associate");
+ vnd_close(vhp);
+ /* Perform appropriate memory cleanup */
+ (void) close(port);
+ return (1);
+ }
+
+ if (port_get(port, &pe, NULL) != 0) {
+ if (errno == EINTR)
+ continue;
+ perror("port_associate");
+ vnd_close(vhp);
+ /* Perform appropriate memory cleanup */
+ (void) close(port);
+ return (1);
+ }
+
+ /*
+ * Most real applications will need to compare the file
+ * descriptor and switch on it. In this case, assume
+ * that the fd in question that is readable is 'vfd'.
+ */
+ if (frameio_read(pe.portev_user, fiop) != 0) {
+ vnd_errno_t vnderr = vnd_errno(vhp);
+ int syserr = vnd_syserrno(vhp);
+
+ if (vnderr == VND_E_SYS && (syserr == EINTR ||
+ syserr == EAGAIN))
+ continue;
+ (void) fprintf(stderr, "failed to get read: %s",
+ vnd_strsyserror(vnderr));
+ vnd_close(vhp);
+ /* Perform appropriate memory cleanup */
+ (void) close(port);
+ return (1);
+ }
+
+ /* Consume the data however it's desired */
+ nframes = fiop->fio_nvecs / fiop->fio_nvpf;
+ for (i = 0; i < nframes; i++) {
+ (void) printf("frame %d is %d bytes large\n", i,
+ fiop->fio_vecs[i].fv_actlen);
+ }
+
+ }
+
+ vnd_close(vhp);
+ /* Do proper memory cleanup */
+ return (0);
+}
+.fi
+.in -2
+
+.SH ATTRIBUTES
+.LP
+See attributes(5) for descriptions of the following attributes:
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Stability Committed
+_
+MT-Level See "THREADING" in libvnd(3LIB)
+.TE
+
+
+.SH SEE ALSO
+
+Intro(2), getmsg(2), read(2), readv(2), write(2), writev(2),
+libvnd(3VND), vnd_errno(3VND), vnd_pollfd(3VND), vnd_syserrno(3VND),
+iovec(9S)
diff --git a/usr/src/man/man3vnd/vnd_pollfd.3vnd b/usr/src/man/man3vnd/vnd_pollfd.3vnd
new file mode 100644
index 0000000000..500d3bac99
--- /dev/null
+++ b/usr/src/man/man3vnd/vnd_pollfd.3vnd
@@ -0,0 +1,155 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VND_POLLFD 3VND "Feb 21, 2014"
+
+.SH NAME
+
+vnd_pollfd \- get file descriptor for polling
+
+.SH SYNOPSIS
+
+.LP
+.nf
+cc [ flag... ] file... -lvnd [ library... ]
+#include <libvnd.h>
+
+int vnd_pollfd(vnd_handle_t *vhp);
+.fi
+
+.SH DESCRIPTION
+.LP
+The vnd_pollfd() function returns an integer id which corresponds to
+the file descriptor that represents the underlying device that is
+associated with the vnd handle vhp. This file descriptor is suitable
+for use with port_associate(3C) and similar polling techniques such as
+poll(2). Use of the file descriptor outside of these uses may cause
+undocumented behavior from the rest of the library.
+
+.LP
+The file descriptor in question is still managed by libvnd. The caller
+must not call close(2) on it. Once vnd_close(3VND) has been called,
+any further use of the file descriptor is undefined behavior.
+
+
+.SH RETURN VALUES
+.LP
+The function returns the integer id of the file descriptor that
+corresponds to the underlying vnd device.
+
+.SH EXAMPLES
+
+.LP
+Example 1 Use event ports for vnd notifications
+.sp
+.LP
+The following sample C program shows how to use the vnd_pollfd
+function with event ports to be notified whenever there is data
+available to be read. This program assumes that a vnd device named
+"vnd0" exists in the current zone. For an example of creating the
+device, see Example 1 in vnd_create(3VND).
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <port.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int port, syserr, vfd, ret;
+
+ port = port_create();
+ if (port < 0) {
+ perror("port_create");
+ return (1);
+ }
+
+ vhp = vnd_open(NULL, "vnd0", &vnderr, &syserr);
+ if (vhp == NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ (void) close(port);
+ return (1);
+ }
+
+ vfd = vnd_pollfd(vhp);
+ if (fcntl(vfd, F_SETFL, O_NONBLOCK) != 0) {
+ perror("fcntl");
+ vnd_close(vhp);
+ (void) close(port);
+ return (1);
+ }
+
+ if (port_associate(port, PORT_SOURCE_FD, vfd, POLLIN, NULL) != 0) {
+ perror("port_associate");
+ vnd_close(vhp);
+ (void) close(port);
+ return (1);
+ }
+
+ for (;;) {
+ port_event_t pe;
+
+
+ if (port_get(port, &pe, NULL) != 0) {
+ if (errno == EINTR)
+ continue;
+ perror("port_get");
+ vnd_close(vhp);
+ (void) close(port);
+ return (1);
+ }
+
+ /*
+ * Read the data with vnd_frameio_read(3VND) and
+ * optionally break out of the loop or continue to the
+ * next iteration and reassociate vfd with the event
+ * port.
+ */
+ }
+}
+.fi
+.in -2
+
+.SH ATTRIBUTES
+.sp
+.LP
+See attributes(5) for descriptions of the following attributes:
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Stability Committed
+_
+MT-Level See "THREADING" in libvnd(3LIB)
+.TE
+
+.SH SEE ALSO
+
+close(2), poll(2), port_create(3C), libvnd(3LIB), vnd_close(3VND)
diff --git a/usr/src/man/man3vnd/vnd_prop_get.3vnd b/usr/src/man/man3vnd/vnd_prop_get.3vnd
new file mode 100644
index 0000000000..e47698c85e
--- /dev/null
+++ b/usr/src/man/man3vnd/vnd_prop_get.3vnd
@@ -0,0 +1,242 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VND_PROP_GET 3VND "Feb 21, 2014"
+
+.SH NAME
+
+vnd_prop_get, vnd_prop_set \- get and set vnd properties
+
+.SH SYNOPSIS
+
+.LP
+.nf
+cc [ flag... ] file... -lvnd [ library... ]
+#include <libvnd.h>
+
+int vnd_prop_get(vnd_handle_t *vhp, vnd_prop_t prop, void *buf, size_t len);
+
+int vnd_prop_set(vnd_handle_t *vhp, vnd_prop_t prop, void *buf, size_t len);
+.fi
+
+.SH DESCRIPTION
+.LP
+The vnd_prop_get and vnd_prop_set functions are used to retrieve
+and set property values on the vnd_handle_t referred to by vhp. The
+property to get or set is specified by the argument prop. The
+argument buf and the size of buf, in len, should be a pointer to the
+appropriate structure for the property as defined in libvnd(3LIB).
+
+.LP
+All of the supported properties are listed and described in the
+libvnd(3LIB) manual page.
+
+
+.SH RETURN VALUES
+.LP
+On success, the vnd_prop_get and vnd_prop_set functions return zero.
+On failure, they return -1 and additional error information is
+available through vnd_errno(3VND) and vnd_syserrno(3VND).
+
+.LP
+When vnd_prop_get returns successfully, the contents of buf are
+filled in with the value of the corresponding property. The contents
+of buf should not change across a call to vnd_prop_set.
+
+.SH EXAMPLES
+
+.LP
+Example 1 Getting the value of the rxbuf property
+.LP
+The following sample C program retrieves the value of the
+rxbuf property and prints it to standard out.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr;
+ vnd_prop_buf_t vpb;
+
+ vhp = vnd_open(NULL, "vnd1", &vnderr, &syserr);
+ if (vhp != NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ if (vnd_prop_get(vhp, VND_PROP_RXBUF, &vpn, sizeof (vpn)) != 0) {
+ vnderr = vnd_errno(vhp);
+ syserr = vnd_syserrno(vhp);
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to get VND_PROP_RXBUF: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to get VND_PROP_RXBUF: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ (void) printf("recieve buffer size is %d bytes\n", vpb.vpb_size);
+
+ vnd_close(vnd);
+ return (0);
+}
+.fi
+.in -2
+
+.LP
+EXAMPLE 2 Setting a property
+.LP
+This sample C program sets the property VND_PROP_RXBUF to the value of
+4200 bytes.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr;
+ vnd_prop_buf_t vpb;
+
+ vhp = vnd_open(NULL, "vnd1", &vnderr, &syserr);
+ if (vhp != NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ vpb.vpb_size = 4200;
+ if (vnd_prop_set(vhp, VND_PROP_RXBUF, &vpb, sizeof (vpb)) != 0) {
+ vnderr = vnd_errno(vhp);
+ syserr = vnd_syserrno(vhp);
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to set VND_PROP_RXBUF: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to set VND_PROP_RXBUF: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ (void) printf("successfully set VND_PROP_RXBUF to 4200\n");
+
+ vnd_close(vnd);
+ return (0);
+}
+.fi
+.in -2
+
+.LP
+Example 3 Setting a property to the value of another.
+.LP
+In this sample C program, we set the VND_PROP_TXBUF to the maximum
+allowable size as determined by the read-only property VND_PROP_MAXBUF.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr;
+ vnd_prop_buf_t vpb;
+
+ vhp = vnd_open(NULL, "vnd1", &vnderr, &syserr);
+ if (vhp != NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ if (vnd_prop_get(vhp, VND_PROP_MAXBUF, &vpb, sizeof (vpb)) != 0) {
+ vnderr = vnd_errno(vhp);
+ syserr = vnd_syserrno(vhp);
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to get VND_PROP_MAXBUF: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to get VND_PROP_MAXBUF: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ if (vnd_prop_set(vhp, VND_PROP_TXBUF, &vpb, sizeof (vpb)) != 0) {
+ vnderr = vnd_errno(vhp);
+ syserr = vnd_syserrno(vhp);
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to set VND_PROP_TXBUF: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to set VND_PROP_TXBUF: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ (void) printf("successfully set VND_PROP_TXBUF to %d\n", vpb.vpb_size);
+
+ vnd_close(vnd);
+ return (0);
+}
+.fi
+
+.SH ATTRIBUTES
+.sp
+.LP
+See attributes(5) for descriptions of the following attributes:
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Stability Committed
+_
+MT-Level See "THREADING" in libvnd(3LIB)
+.TE
+
+.SH SEE ALSO
+libvnd(3VND), vnd_errno(3VND, vnd_syserrno(3VND)
diff --git a/usr/src/man/man3vnd/vnd_prop_iter.3vnd b/usr/src/man/man3vnd/vnd_prop_iter.3vnd
new file mode 100644
index 0000000000..29221734c5
--- /dev/null
+++ b/usr/src/man/man3vnd/vnd_prop_iter.3vnd
@@ -0,0 +1,148 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VND_PROP_ITER 3VND "Feb 21, 2014"
+
+.SH NAME
+
+vnd_prop_iter \- iterate vnd properties
+
+.SH SYNOPSIS
+
+.LP
+.nf
+cc [ flag... ] file... -lvnd [ library... ]
+#include <libvnd.h>
+
+typedef int (vnd_prop_iter_f)(vnd_handle_t *vhp, vnd_prop_t prop,
+ void *cbarg);
+
+int vnd_prop_iter(vnd_handle_t *vhp, vnd_prop_iter_f cb,
+ void *arg);
+.fi
+
+.SH DESCRIPTION
+.LP
+The vnd_prop_iter function iterates over all the available properties
+for the vnd handle vhp and calls the user supplied callback function
+cb. The argument arg is passed directly to the callback function.
+
+.LP
+The function specified by cb receives three arguments. The first, vhp,
+is the same vnd library handle that was passed to vnd_prop_iter. During
+the callback, the consumer should not call vnd_close(3VND). Doing so
+will lead to undefined and undocumented behavior. The second argument,
+prop, is the current property. While vnd_prop_iter guarantees that all
+properties will be recieved, it does not guarantee the order of them.
+The final argument, cbarg, is the same argument that the caller passed
+in during arg.
+
+.LP
+The return value of the callback function cb indicates whether or not
+property iteration should continue. To continue iteration, the
+function cb should return zero. Otherwise, to stop property iteration
+it should return non-zero.
+
+.SH RETURN VALUES
+
+.LP
+On success, the function vnd_prop_iter returns zero. If the callback
+function returned non-zero to terminate iteration, vnd_prop_iter will
+instead return one. In the case of library failure, vnd_prop_iter will
+return -1. In such cases, the vnd and system errors will be updated
+and available via vnd_errno(3VND) and vnd_syserrno(3VND).
+
+.SH EXAMPLES
+
+.LP
+Example 1 Print writeable properties
+
+.LP
+The following sample C program walks over every vnd property and
+prints out whether the property is read-only or read-write for the
+vnd device "vnd1" in the current zone.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static int
+print_prop(vnd_handle_t *vhp, vnd_prop_t prop, void *unused)
+{
+ boolean_t canwrite;
+
+ if (vnd_prop_writeable(vhp, &canwrite) != 0)
+ abort();
+
+ (void) printf("prop %d is %s", prop, canwrite == B_TRUE ? "rw" : "r-");
+ return (0);
+}
+
+int
+main(void)
+{
+ vnd_handle_t *vhp;
+ vnd_errno_t vnderr;
+ int syserr;
+
+ vhp = vnd_open(NULL, "vnd1", &vnderr, &syserr);
+ if (vhp != NULL) {
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ if (vnd_prop_iter(vhp, print_prop, NULL) != 0) {
+ vnderr = vnd_errno(vhp);
+ syserr = vnd_syserrno(vhp);
+ if (vnderr == VND_E_SYS)
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strsyserror(syserr));
+ else
+ (void) fprintf(stderr, "failed to open device: %s",
+ vnd_strerror(vnderr));
+ return (1);
+ }
+
+ vnd_close(vnd);
+ return (0);
+}
+.fi
+.in -2
+
+.SH ATTRIBUTES
+.sp
+.LP
+See attributes(5) for descriptions of the following attributes:
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Stability Committed
+_
+MT-Level See "THREADING" in libvnd(3LIB)
+.TE
+
+libvnd(3LIB), vnd_close(3VND), vnd_errno(3VND), vnd_syserrno(3VND)
diff --git a/usr/src/man/man3vnd/vnd_prop_writeable.3vnd b/usr/src/man/man3vnd/vnd_prop_writeable.3vnd
new file mode 100644
index 0000000000..c23414718b
--- /dev/null
+++ b/usr/src/man/man3vnd/vnd_prop_writeable.3vnd
@@ -0,0 +1,101 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VND_PROP_WRITEABLE 3VND "Feb 21, 2014"
+
+.SH NAME
+
+vnd_prop_writeable \- determine if a vnd property can be updated
+
+.SH SYNOPSIS
+
+.LP
+.nf
+cc [ flag... ] file... -lvnd [ library... ]
+#include <libvnd.h>
+
+int vnd_prop_writeable(vnd_prop_t prop, boolean_t *wp);
+.fi
+
+
+.SH DESCRIPTION
+.LP
+The vnd_prop_writeable function is used as a programmatic means to
+determine whether a given vnd property is writeable or not. The
+property to check is specified in prop and should be from the list
+described in libvnd(3VND). The argument wp is a pointer to a boolean_t
+which will be updated upon the successful completion of the function.
+The argument wp must be a valid pointer. If a property is writeable
+than the value pointed to by wp is set to B_TRUE. If the property is
+read-only, then the value is set to B_FALSE.
+
+
+.SH RETURN VALUES
+.LP
+On success, vnd_prop_writeable returns zero and the value pointed to
+by wp is updated with whether the property is writeable. If the
+property prop does not exist, then vnd_prop_writeable will return -1.
+
+.SH EXAMPLES
+.LP
+Example 1 Check whether the property VND_PROP_TXBUF is writable
+.LP
+The following sample C program checks whether the vnd property
+VND_PROP_TXBUF is writeable or not.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(void)
+{
+ boolean_t canwrite;
+
+ if (vnd_prop_writeable(VND_PROP_TXBUF, &prop) != 0)
+ abort();
+
+ if (canwrite == B_TRUE)
+ (void) printf("VND_PROP_TXBUF is writeable\n");
+ else
+ (void) printf("VND_PROP_TXBUF is read only\n");
+
+ return (0);
+}
+.fi
+.in -2
+
+.SH ATTRIBUTES
+.sp
+.LP
+See attributes(5) for descriptions of the following attributes:
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Stability Committed
+_
+MT-Level MT-Safe
+.TE
+
+.SH SEE ALSO
+
+vndadm(1M), libvnd(3VND)
diff --git a/usr/src/man/man3vnd/vnd_walk.3vnd b/usr/src/man/man3vnd/vnd_walk.3vnd
new file mode 100644
index 0000000000..bed53130d3
--- /dev/null
+++ b/usr/src/man/man3vnd/vnd_walk.3vnd
@@ -0,0 +1,155 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VND_CREATE 3VND "Feb 21, 2014"
+
+.SH NAME
+
+vnd_walk \- walk all vnd devices
+
+
+.SH SYNOPSIS
+
+.LP
+.nf
+cc [ flag... ] file... -lvnd [ library... ]
+#include <libvnd.h>
+
+typedef int (*vnd_walk_cb_f)(vnd_info_t *viip, void *cbarg);
+
+int vnd_walk(vnd_walk_cb_t cb, void *arg, vnd_errno_t *vnderr, int *syserr);
+.fi
+
+
+.SH DESCRIPTION
+.LP
+The vnd_walk() function fires the callback function cb once for every
+vnd device that is visible in the current zone. If the caller is in
+the global zone, then all vnd devices in all zones will be walked. If
+the caller is in a non-global zone, then only the devices in that zone
+will be visible.
+
+.LP
+The function cb will be called with two arguments. The first argument,
+viip, is a pointer to a structure that contains information about the
+link. The second argument to the function cb, cbarg, is the same
+argument that is passed to the function vnd_walk as arg. To continue
+the function cb should return zero. If the function cb returns
+non-zero the walk will terminate.
+
+.LP
+As the vnd_walk function does not have a handle, errors are returned
+in vnderr and syserr. Both vnderr and syserr are allowed to be NULL
+pointers. If either one is a NULL pointer, then error information for
+that class of error will not be returned. It is not recommended that
+consumers supply NULL pointers.
+
+.LP
+The vnd_info_t structure contains the following members:
+
+.in +2
+.nf
+uint32_t vi_version
+zoneid_t vi_zone
+char vi_name[LIBVND_NAMELEN];
+char vi_datalink[LIBVND_NAMELEN];
+.fi
+.in -2
+
+.LP
+The member vi_version is guaranteed to be the first member of the
+structure. This number indicates the current revision of the structure
+and is set to the integer value 1. More properties may be added in
+future releases. Those properties will be tied to a greater version
+number so software knows whether or not it is legal to access them.
+
+.LP
+The vi_zone field indicates the zone id that the vnd device exists in.
+The vi_name field is the name of the vnd device. If the vnd_device is
+not linked, the name field is set to "<unknown>". The vi_datalink
+field is filled in with the name of the data link the vnd device is on
+top of.
+
+
+.SH RETURN VALUES
+
+.LP
+The vnd_walk function will return zero on success. If the consumer
+supplied callback function returned non-zero, then the vnd_walk
+function will return 1. If an error occurred, -1 is returned, and if
+vnderr and syserr are non-null, they are filled in with their
+respective error values. See vnd_errno(3VND) for more information on
+these errors.
+
+.SH EXAMPLES
+
+.LP
+Example 1 Walk all devices and print information about them
+
+.LP
+The following sample C program walks every vnd device and prints out
+information about them.
+
+.sp
+.in +2
+.nf
+#include <libvnd.h>
+#include <stdio.h>
+
+static int
+print_entry(vnd_info_t *viip, void *unused)
+{
+ (void) printf("device %s over data link %s in zone %d\n",
+ viip->vi_name, viip->vi_datalink, viip->vi_zone);
+ return (0);
+}
+
+int
+main(void)
+{
+ vnd_errno_t vnderr;
+ int syserr;
+
+ if (vnd_walk(print_entry, NULL, &vnderr, &syserr) != 0) {
+ (void) fprintf(stderr, "failed to walk vnd devices: %s\n",
+ vnderr != VND_E_SYS ? vnd_strerror(vnderr) :
+ vnd_strsyserror(syserr));
+ return (1);
+ }
+
+ return (0);
+}
+.fi
+.in -2
+
+.SH ATTRIBUTES
+.sp
+.LP
+See attributes(5) for descriptions of the following attributes:
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Stability Committed
+_
+MT-Level MT-Safe
+.TE
+
+.SH SEE ALSO
+
+libvnd(3VND), vnd_errno(3VND), attributes(5), zones(5)
diff --git a/usr/src/man/man3xnet/Makefile b/usr/src/man/man3xnet/Makefile
index 81cd96cba3..cc231f60c4 100644
--- a/usr/src/man/man3xnet/Makefile
+++ b/usr/src/man/man3xnet/Makefile
@@ -62,6 +62,7 @@ MANLINKS= getaddrinfo.3xnet \
getservbyname.3xnet \
getservbyport.3xnet \
getservent.3xnet \
+ htonll.3xnet \
htons.3xnet \
if_freenameindex.3xnet \
if_indextoname.3xnet \
@@ -73,6 +74,7 @@ MANLINKS= getaddrinfo.3xnet \
inet_ntoa.3xnet \
inet_pton.3xnet \
ntohl.3xnet \
+ ntohll.3xnet \
ntohs.3xnet \
sethostent.3xnet \
setnetent.3xnet \
@@ -97,6 +99,7 @@ getservbyname.3xnet := LINKSRC = endservent.3xnet
getservbyport.3xnet := LINKSRC = endservent.3xnet
getservent.3xnet := LINKSRC = endservent.3xnet
+htonll.3xnet := LINKSRC = htonl.3xnet
htons.3xnet := LINKSRC = htonl.3xnet
if_freenameindex.3xnet := LINKSRC = if_nametoindex.3xnet
@@ -112,6 +115,7 @@ inet_ntoa.3xnet := LINKSRC = inet_addr.3xnet
inet_pton.3xnet := LINKSRC = inet_ntop.3xnet
ntohl.3xnet := LINKSRC = htonl.3xnet
+ntohll.3xnet := LINKSRC = htonl.3xnet
ntohs.3xnet := LINKSRC = htonl.3xnet
sethostent.3xnet := LINKSRC = endhostent.3xnet
diff --git a/usr/src/man/man3xnet/htonl.3xnet b/usr/src/man/man3xnet/htonl.3xnet
index ad927ec91d..dac5df1dd6 100644
--- a/usr/src/man/man3xnet/htonl.3xnet
+++ b/usr/src/man/man3xnet/htonl.3xnet
@@ -58,6 +58,11 @@ order
.LP
.nf
+\fBuint64_t\fR \fBhtonll\fR(\fBuint64_t\fR \fIhostlonglong\fR);
+.fi
+
+.LP
+.nf
\fBuint16_t\fR \fBhtons\fR(\fBuint16_t\fR \fIhostshort\fR);
.fi
@@ -68,18 +73,23 @@ order
.LP
.nf
+\fBuint64_t\fR \fBntohll\fR(\fBuint64_t\fR \fInetlonglong\fR);
+.fi
+
+.LP
+.nf
\fBuint16_t\fR \fBntohs\fR(\fBuint16_t\fR \fI netshort\fR);
.fi
.SH DESCRIPTION
.sp
.LP
-These functions convert 16-bit and 32-bit quantities between network byte order
-and host byte order.
+These functions convert 16-bit, 32-bit, and 64-bit quantities between network
+byte order and host byte order.
.sp
.LP
-The \fBuint32_t\fR and \fBuint16_t\fR types are made available by inclusion
-of \fB<inttypes.h>\fR\&.
+The \fBuint32_t\fR, \fBuint16_t\fR, and \fBuint64_t\fR types are made available
+by inclusion of \fB<inttypes.h>\fR\&.
.SH USAGE
.sp
.LP
@@ -92,12 +102,12 @@ value of their argument.
.SH RETURN VALUES
.sp
.LP
-The \fBhtonl()\fR and \fBhtons()\fR functions return the argument value
-converted from host to network byte order.
+The \fBhtonl()\fR, \fBhtonll()\fR, and \fBhtons()\fR functions return the
+argument value converted from host to network byte order.
.sp
.LP
-The \fBntohl()\fR and \fBntohs()\fR functions return the argument value
-converted from network to host byte order.
+The \fBntohl()\fR, \fBntohll()\fR, and \fBntohs()\fR functions return the
+argument value converted from network to host byte order.
.SH ERRORS
.sp
.LP
diff --git a/usr/src/man/man4/Makefile b/usr/src/man/man4/Makefile
index ae3db792b6..c367547dff 100644
--- a/usr/src/man/man4/Makefile
+++ b/usr/src/man/man4/Makefile
@@ -21,7 +21,8 @@ MANSECT= 4
MANFILES= FSS.4 \
Intro.4 \
cpr.4 \
- ibmf.4
+ ibmf.4 \
+ swap.4
MANLINKS= intro.4
diff --git a/usr/src/man/man4/swap.4 b/usr/src/man/man4/swap.4
new file mode 100644
index 0000000000..0d2faddf6d
--- /dev/null
+++ b/usr/src/man/man4/swap.4
@@ -0,0 +1,92 @@
+.\"
+.\" 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 2017, Joyent, Inc.
+.\"
+.Dd Aug 14, 2017
+.Dt SWAP 4
+.Os
+.Sh NAME
+.Nm swap
+.Nd swap space
+.Sh DESCRIPTION
+The operating system uses demand paging as the primary mechanism to implement
+virtual memory.
+The system can also use traditional swapping, wherein an entire process state
+is moved between physical memory and swap space on disk, but it is very rare
+for the system to actually swap an entire process out to disk.
+A system which is swapping does not have enough enough physical memory to
+support the workload on the machine.
+In this case, work should be reduced or more memory added.
+.Pp
+Given that the system rarely, if ever, swaps, but still has swap space
+configured, the question arises as to what this space is for?
+.Pp
+In a demand paged virtual memory system, every page mapped by a process is
+backed by some object.
+For the actual program code used by the process, the backing objects are the
+underlying files in the filesystem for the program's binary, and any
+dynamically linked libraries.
+The portions of a process that are not backed by a named file, including its
+stack and its heap, are called anonymous memory.
+The system uses the swap space as the backing store for these pages.
+When the system determines that it needs to page out one of the anonymous pages,
+the swap space is where that page is written.
+.Pp
+Unlike some other operating systems, illumos reserves the backing storage
+space for anonymous memory at the time of allocation.
+For example, if a process asks for more heap space, the total size of the
+swap allocation that may be required to store the new pages if they need to
+be paged out is reserved at that time.
+This does not mean that anything is written to the swap space, but simply that
+the space is reserved for the entire allocation.
+Thus, a process will always get a correct error from the
+.Xr sbrk 2
+system call if swap space is unavailable.
+Some other operating systems don't allocate backing store for anonymous memory
+until it is used, so the error handling when space is not available can be
+complex or problematic on those systems.
+.Pp
+The
+.Xr vmstat 8
+command can be used to monitor swap and paging activity.
+The
+.Xr pmap 1
+command can be used to inspect all of the mappings in a process address space,
+and their backing objects.
+The
+.Xr swap 8
+command can be used to monitor, add and remove swap space.
+.Pp
+The operating system provides the
+.Ql zone.max-swap
+resource control to limit the amount of
+anonymous memory used by all of the processes within a zone.
+This resource control can also be configured under the
+.Xr capped-memory 2
+setting for a zone.
+See the
+.Xr prctl 1
+and
+.Xr zonecfg 8
+man pages for information on setting this limit.
+The zone's usage against this resource control can be seen using the
+.Ql swapresv_zone_{zoneid}
+kstat.
+.Sh SEE ALSO
+.Xr pmap 1 ,
+.Xr prctl 1 ,
+.Xr sbrk 2 ,
+.Xr resource_controls 7 ,
+.Xr kstat 8 ,
+.Xr swap 8 ,
+.Xr vmstat 8 ,
+.Xr zonecfg 8
diff --git a/usr/src/man/man4d/Makefile b/usr/src/man/man4d/Makefile
index 0eb6bab297..88a0b92076 100644
--- a/usr/src/man/man4d/Makefile
+++ b/usr/src/man/man4d/Makefile
@@ -146,11 +146,13 @@ _MANFILES= aac.4d \
virtualkm.4d \
vni.4d \
vr.4d \
+ vnd.4d \
wscons.4d \
xge.4d \
yge.4d \
zcons.4d \
- zero.4d
+ zero.4d \
+ zfd.4d
sparc_MANFILES= audiocs.4d \
bbc_beep.4d \
diff --git a/usr/src/man/man4d/cpuid.4d b/usr/src/man/man4d/cpuid.4d
index 95b19bc834..7e70907ee3 100644
--- a/usr/src/man/man4d/cpuid.4d
+++ b/usr/src/man/man4d/cpuid.4d
@@ -1,5 +1,6 @@
'\" te
.\" Copyright (c) 2004, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright 2015, Joyent, Inc.
.\" 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]
@@ -43,6 +44,10 @@ the years).
See the processor manufacturers documentation for further information about the
syntax and semantics of the wide variety of information available from this
instruction.
+.LP
+Some systems can be configured to limit the cpuid opcodes which are accessible.
+While illumos handles this condition, other software may malfunction when such
+limits are enabled. Those settings are typically manipulated in the BIOS.
.SH EXAMPLE
.LP
This example allows you to determine if the current x86 processor supports
diff --git a/usr/src/man/man4d/vnd.4d b/usr/src/man/man4d/vnd.4d
new file mode 100644
index 0000000000..e80a991d5c
--- /dev/null
+++ b/usr/src/man/man4d/vnd.4d
@@ -0,0 +1,118 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VND 4D "Feb 11, 2014"
+.SH NAME
+vnd \- virtual layer two network driver
+
+.SH SYNOPSIS
+.nf
+.LP
+/dev/vnd/ctl
+.LP
+/dev/vnd/*
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The vnd driver provides support for a layer two datapath in an
+analogous way that IP(4P) provides a support for an IP-based layer
+three datapath. Both devices operate exclusively on datalinks. A
+datalink that has been plumbed up with IP via ifconfig(1M) or
+ipadm(1M) cannot be used with vnd or vice-versa.
+.sp
+.LP
+The vnd driver supports and takes advantage of the the following
+illumos features:
+.RS
+.sp
+.LP
+Supports dld/dls feature negotation of GLDv3 features, such
+as direct calls, flow control, checksum offloading, and more.
+.sp
+.LP
+All IP and IPv6 based traffic is sent through ipfilter(7),
+allowing packet filtering.
+.sp
+.LP
+Better control over vectored reads and writes in a frame-centric manner
+through framed I/O. See libvnd(3LIB) for more information on these
+interfaces.
+.RE
+.sp
+.LP
+The vnd driver exposes two different kinds of device nodes. The first is
+a self-cloning control node which can be used to create vnd devices on
+top of datalinks. Those devices can optionally be bound into the file
+system namespace under /dev/vnd. Control operations on the control node
+or named devices are private to the implementation. Instead,
+libvnd(3LIB) provides a stable interfaces for using, creating, and
+manipulating vnd devices.
+.sp
+.SH FILES
+.sp
+.ne 2
+.na
+/dev/vnd/ctl
+.ad
+.RS 16n
+vnd self-cloning control node
+.RE
+
+.sp
+.ne 2
+.na
+/dev/vnd/%link
+.ad
+.RS 16n
+Character device that corresponds to the vnd device of the given
+name (%link). A given device will appear for each actively linked device
+in the current zone.
+.RE
+
+.sp
+.ne 2
+.na
+/dev/vnd/zone/%zone/%link
+.ad
+.RS 16n
+These are character devices that correspond to the vnd device of
+the given name (%link). They are organized based on the zone that they
+appear in. Thus if a zone named foo has a vnd device named
+bar, then the global zone will have the file
+/dev/vnd/zone/foo/bar. Note, these only occur in the global zone.
+.RE
+
+.SH ATTRIBUTES
+.sp
+.LP
+See attributes(5) for descriptions of the following attributes:
+.sp
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Interface Stability Evolving
+.TE
+
+.SH SEE ALSO
+.sp
+.LP
+libvnd(3LIB), ipfilter(7), vndstat(8), dladm(8), vndadm(8),
+
diff --git a/usr/src/man/man4d/zfd.4d b/usr/src/man/man4d/zfd.4d
new file mode 100644
index 0000000000..556fd8e371
--- /dev/null
+++ b/usr/src/man/man4d/zfd.4d
@@ -0,0 +1,81 @@
+.\"
+.\" 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.
+.\"
+.Dd "Oct 16, 2015"
+.Dt ZFD 4D
+.Os
+.Sh NAME
+.Nm zfd
+.Nd Zone file descriptor driver
+.Sh DESCRIPTION
+The
+.Nm zfd
+character driver exports devices into the zone which can be used by a
+a standalone process within the zone as
+.Vt stdin ,
+.Vt stdout ,
+and
+.Vt stderr .
+The
+.Nm zfd
+driver behaves in a similar manner as the
+.Nm zcons(4D)
+device.
+Inside a zone, the slave side devices appear as
+.Nm /dev/zfd/[0-4] .
+.sp
+The zone's zfd device configuration is driven by
+.Nm zoneadmd
+and a zone attribute
+.Nm zlog-mode
+which is somewhat of a misnomer since its purpose has evolved.
+The attribute can have a variety of values, but the lowest two positions
+in the value string are used to control how many zfd devices are created
+inside the zone and if the primary stream is a tty.
+.sp
+.Dl --
+.Dl -n
+.Dl t-
+.Dl tn
+.sp
+With the
+.Nm t
+flag set,
+.Vt stdin ,
+.Vt stdout ,
+and
+.Vt stderr ,
+are multiplexed onto a single full-duplex stream which is configured as a tty.
+That is,
+.Nm ptem ,
+.Nm ldterm
+and
+.Nm ttycompat
+are autopushed onto the stream when the slave side is opened.
+There is only a single zfd device (0) needed for the primary stream.
+.sp
+When the
+.Nm n
+flag is set, it is assumed that output logging will be done within the zone
+itself.
+In this configuration 1 or 2 additional zfd devices, depending on tty mode
+.Nm ( t
+flag), are created within the zone.
+An application can then configure the zfd streams driver into a multiplexer.
+Output from the stdout/stderr zfd(s) will be teed into the correspond
+logging zfd(s) within the zone.
+.Sh SEE ALSO
+.Xr zlogin 1 ,
+.Xr zcons 4D ,
+.Xr zoneadmd 8 ,
+.Xr zonecfg 8
diff --git a/usr/src/man/man4fs/Makefile b/usr/src/man/man4fs/Makefile
index 92d22cc776..7f6b480297 100644
--- a/usr/src/man/man4fs/Makefile
+++ b/usr/src/man/man4fs/Makefile
@@ -17,23 +17,25 @@
include $(SRC)/Makefile.master
-MANSECT= 4fs
+MANSECT= 4fs
-MANFILES= bootfs.4fs \
+MANFILES= bootfs.4fs \
ctfs.4fs \
- dcfs.4fs \
- dev.4fs \
- devfs.4fs \
+ dcfs.4fs \
+ dev.4fs \
+ devfs.4fs \
fd.4fs \
- hsfs.4fs \
- lofs.4fs \
- objfs.4fs \
- pcfs.4fs \
- sharefs.4fs \
- smbfs.4fs \
- tmpfs.4fs \
- udfs.4fs \
- ufs.4fs
+ hsfs.4fs \
+ hyprlofs.4fs \
+ lofs.4fs \
+ lxproc.4fs \
+ objfs.4fs \
+ pcfs.4fs \
+ sharefs.4fs \
+ smbfs.4fs \
+ tmpfs.4fs \
+ udfs.4fs \
+ ufs.4fs
MANLINKS= stderr.4fs \
stdin.4fs \
diff --git a/usr/src/man/man4fs/hyprlofs.4fs b/usr/src/man/man4fs/hyprlofs.4fs
new file mode 100644
index 0000000000..6d0849cfe5
--- /dev/null
+++ b/usr/src/man/man4fs/hyprlofs.4fs
@@ -0,0 +1,62 @@
+'\" te
+.\" Copyright (c) 2012, Joyent, Inc.
+.\" 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]
+.TH HYPRLOFS 4FS "March 7, 2012"
+.SH NAME
+hyprlofs \- fast name space virtual file system
+.SH SYNOPSIS
+.LP
+.nf
+#include <sys/fs/hyprlofs.h>
+
+\fB\fR\fBmount\fR (\fB\fR\fIspecial\fR, \fB\fR\fIdirectory\fR, \fB\fR\fIMS_DATA\fR, \fB\fR\fI"hyprlofs"\fR, \fB\fR\fINULL\fR, \fB\fR\fI0\fR);
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+\fBhyprlofs\fR is a hybrid file system combining features from
+\fBtmpfs(4FS)\fR and \fBlofs(4FS)\fR.
+As with \fBlofs\fR, the \fBhyprlofs\fR file system allows new, virtual file
+systems to be created which provide access to existing files using alternate
+pathnames. In addition, the files themselves may have alternate names and
+paths within the mount.
+Unlike \fBlofs\fR, files cannot be created and backing files cannot be removed.
+The name space is completely managed through ioctls on the mount.
+Entries in the name space are not mounts and thus, they will not appear in the
+mnttab. The file system is designed to provide a very fast name space to the
+backing files. The name space can be modified very quickly through the ioctl
+interface.
+.sp
+.LP
+\fBhyprlofs\fR file systems can be mounted with the command:
+.sp
+.in +2
+.nf
+\fBmount \fR\fB-F\fR\fB hyprlofs swap \fR\fIdirectory\fR
+.fi
+.in -2
+
+.sp
+.LP
+The name space used by \fBhyprlofs\fR exists only in-memory so it will consume
+a small amount of the system's virtual memory. The files themselves are backed
+by the original file as with \fBlofs\fR.
+
+.SH SEE ALSO
+.sp
+.LP
+\fBmount\fR(2), \fBumount\fR(2),
+\fBdf\fR(8), \fBmount\fR(8), \fBswap\fR(8)
+.sp
+.LP
+\fISystem Administration Guide: Basic Administration\fR
+.SH DIAGNOSTICS
+.sp
+.LP
+\fBdf\fR(8) output is of limited accuracy since
+the space available to \fBhyprlofs\fR is dependent on the swap
+space demands of the entire system and the files in the name space are not
+included.
diff --git a/usr/src/man/man4fs/lxproc.4fs b/usr/src/man/man4fs/lxproc.4fs
new file mode 100644
index 0000000000..25e5a3d9fc
--- /dev/null
+++ b/usr/src/man/man4fs/lxproc.4fs
@@ -0,0 +1,115 @@
+'\" te
+.\" Copyright (c) 2012, Joyent, Inc.
+.\" 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]
+.TH LXPROC 4FS "April 25, 2012"
+.SH NAME
+lxproc \- a loosely Linux-compatible /proc
+.SH SYNOPSIS
+.LP
+.nf
+\fB\fR\fBmount\fR (\fB\fR\fI"lxproc"\fR, \fB\fR\fIdirectory\fR, \fB\fR\fIMS_DATA\fR, \fB\fR\fI"lxproc"\fR, \fB\fR\fINULL\fR, \fB\fR\fI0\fR);
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+\fBlxproc\fR is an implementation of the \fB/proc\fR filesystem that
+loosely matches the Linux semantics of providing human-readable text files
+that correspond to elements of the system.
+As with both \fBproc\fR(5) and Linux \fB/proc\fR, \fBlxproc\fR makes available
+a directory for every process, with each directory containing a number
+of files; like Linux \fB/proc\fR but unlike \fBproc\fR(5), \fBlxproc\fR also
+makes available a number of files related to system-wide information.
+To ascertain the meaning and structure of the files provided via
+\fBlxproc\fR, users should consult the Linux documentation.
+.sp
+.LP
+The \fBlxproc\fR compatibility layer is
+provided only as a best-effort for simple Linux \fB/proc\fR readers; it
+is not intended to exactly mimic Linux semantics and nor does it attempt to
+somehow fool a consumer into believing that it is operating within a Linux
+environment. As such, \fBlxproc\fR should only be used by Linux-specific
+programs that are willing to trade precision in understanding the
+system in return for Linux compatibility. To programmatically understand
+the system precisely and in terms of its native constructs,
+one should not use \fBlxproc\fR, but rather \fBproc\fR(5) or
+\fBkstat\fR(3KSTAT).
+To understand
+a process or group of processes from either a shell script or the command line,
+one should not use \fBlxproc\fR, but rather \fBproc\fR(5)-based tools like
+\fBprstat\fR(8),
+\fBpfiles\fR(1),
+\fBpargs\fR(1),
+\fBpmap\fR(1),
+\fBptree\fR(1),
+\fBplimit\fR(1),
+\fBpflags\fR(1),
+\fBpcred\fR(1),
+\fBpstack\fR(1),
+\fBpldd\fR(1),
+\fBpsig\fR(1),
+or
+\fBpwdx\fR(1).
+To understand system-wide constructs from either a shell script or the
+command line, one should not use \fBlxproc\fR, but rather
+\fBkstat\fR(3KSTAT)-based tools like
+\fBkstat\fR(8),
+\fBmpstat\fR(8),
+\fBiostat\fR(8),
+\fBnetstat\fR(8) or
+\fBpsrinfo\fR(8).
+.sp
+.LP
+Like \fB/proc\fR, \fBlxproc\fR can be mounted on any mount point, but the
+preferred mount point is \fB/system/lxproc\fR; if a zone brand elects to
+mount it by default, this will (or should) generally be the mount point.
+.sp
+.LP
+\fBlxproc\fR can be mounted with the command:
+.sp
+.in +2
+.nf
+\fBmount \fR\fB-F\fR\fB lxproc lxproc \fR\fIdirectory\fR
+.fi
+.in -2
+
+.SH SEE ALSO
+.sp
+.LP
+\fBdf\fR(8),
+\fBiostat\fR(8),
+\fBkstat\fR(8),
+\fBmpstat\fR(8),
+\fBmount\fR(8),
+\fBnetstat\fR(8),
+\fBpargs\fR(1),
+\fBpcred\fR(1),
+\fBpfiles\fR(1),
+\fBpflags\fR(1),
+\fBpldd\fR(1),
+\fBplimit\fR(1),
+\fBpmap\fR(1),
+\fBprstat\fR(8),
+\fBpsig\fR(1),
+\fBpsrinfo\fR(8),
+\fBpstack\fR(1),
+\fBptree\fR(1),
+\fBpwdx\fR(1),
+\fBmount\fR(2), \fBumount\fR(2), \fBkstat\fR(3KSTAT), \fBproc\fR(5),
+\fBkstat\fR(9S)
+
+.SH NOTES
+.sp
+.LP
+When choosing between offering
+Linux compatibility and telling the truth, \fBlxproc\fR emphatically picks
+the truth. A particular glaring example of this is the Linux notion of
+"tasks" (that is, threads), which -- due to historical misadventures on
+Linux -- allocate their identifiers from the process identifier space.
+(That is, each thread has in effect a pid.) Some Linux \fB/proc\fR readers
+have come to depend on this attribute, and become confused when threads
+appear with proper identifiers, so \fBlxproc\fR simply opts for the pre-2.6
+behavior, and does not present the tasks directory at all.
+
diff --git a/usr/src/man/man4i/Makefile b/usr/src/man/man4i/Makefile
index 4c149ea3ac..4c8d7406af 100644
--- a/usr/src/man/man4i/Makefile
+++ b/usr/src/man/man4i/Makefile
@@ -41,6 +41,10 @@ MANFILES= audio.4i \
visual_io.4i \
vt.4i
+MANLINKS= uccid.4i
+
+uccid.7i := LINKSRC = ../man4d/ccid.4d
+
.KEEP_STATE:
include $(SRC)/man/Makefile.man
diff --git a/usr/src/man/man4m/Makefile b/usr/src/man/man4m/Makefile
index 4fc26d5bb9..a32665e699 100644
--- a/usr/src/man/man4m/Makefile
+++ b/usr/src/man/man4m/Makefile
@@ -12,6 +12,7 @@
#
# Copyright 2011, Richard Lowe
# Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2015 Joyent, Inc.
#
include $(SRC)/Makefile.master
@@ -20,6 +21,7 @@ MANSECT= 4m
_MANFILES = bufmod.4m \
connld.4m \
+ datafilt.4m \
ldterm.4m \
pckt.4m \
pfmod.4m \
diff --git a/usr/src/man/man4m/datafilt.4m b/usr/src/man/man4m/datafilt.4m
new file mode 100644
index 0000000000..ab35965eeb
--- /dev/null
+++ b/usr/src/man/man4m/datafilt.4m
@@ -0,0 +1,48 @@
+.\"
+.\" 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 2014 Ryan Zezeski
+.\" Copyright 2015 Joyent, Inc.
+.\"
+.Dd Apr 21, 2015
+.Dt DATAFILT 4M
+.Os
+.Sh NAME
+.Nm datafilt
+.Nd socket filter module for deferred TCP connections
+.Sh DESCRIPTION
+The
+.Nm datafilt
+socket filter provides deferment of
+.Xr accept 3SOCKET
+for TCP connections.
+The accept call will not return until at least one byte has been
+buffered by the kernel.
+Deferment assures the application that the first call to
+.Xr read 2
+or
+.Xr recv 3SOCKET
+will not block.
+It reduces unnecessary switching between user and kernel.
+.Sh EXAMPLES
+.Ss Example 1
+Enable deferment on the listening socket.
+.Bd -literal
+ setsockopt(lsock, SOL_FILTER, FIL_ATTACH, "datafilt", 8);
+.Ed
+.Ss Example 2
+Disable deferment on the listening socket.
+.Bd -literal
+ char filt[] = "datafilt";
+ setsockopt(lsock, SOL_FILTER, FIL_DETACH, filt, strlen(filt) + 1);
+.Ed
+.Sh SEE ALSO
+.Xr setsockopt 3SOCKET
diff --git a/usr/src/man/man4p/vxlan.4p b/usr/src/man/man4p/vxlan.4p
index dbace910b4..d3b70b80da 100644
--- a/usr/src/man/man4p/vxlan.4p
+++ b/usr/src/man/man4p/vxlan.4p
@@ -9,9 +9,9 @@
.\" http://www.illumos.org/license/CDDL.
.\"
.\"
-.\" Copyright 2015 Joyent, Inc.
+.\" Copyright 2018 Joyent, Inc.
.\"
-.Dd Apr 10, 2015
+.Dd December 20, 2018
.Dt VXLAN 4P
.Os
.Sh NAME
@@ -96,11 +96,18 @@ validate_vxlan(void *buf, int len, uint32_t *vidp)
vxlan_hdr_t *hdr;
if (len < sizeof (vxlan_hdr_t))
- return (EINAVL);
+ return (EINVAL);
hdr = buf;
- if ((ntohl(hdr->vxlan_flags) & VXLAN_MAGIC) == 0)
- return (EINAVL);
+
+ /*
+ * This verifies that the required flag is set; however, it does
+ * not look at any of the other bist that are reserved in the
+ * header. Software needs to evaluate how it should handle other
+ * bits being set in the vxlan_flags member.
+ */
+ if ((ntohl(hdr->vxlan_flags) & VXLAN_F_VDI) == 0)
+ return (EINVAL);
*vidp = ntohl(vxlan->vxlan_id) >> VXLAN_ID_SHIFT;
diff --git a/usr/src/man/man5/proc.5 b/usr/src/man/man5/proc.5
index 55fa331d08..80adab2f4b 100644
--- a/usr/src/man/man5/proc.5
+++ b/usr/src/man/man5/proc.5
@@ -712,6 +712,17 @@ indicates that the lwp stopped due to internal synchronization of lwps within
the process.
.Sy pr_what
is unused in this case.
+.It Sy PR_BRAND
+indicates that the lwp stopped for a brand-specific reason.
+Interpretation of the value of
+.Sy pr_what
+depends on which zone brand is in use.
+It is not generally expected that an lwp stopped in this state will be
+restarted by native
+.\" mandoc(1) doesn't like .Xr macros referring to itself, so this is
+.\" a bit of a hack.
+.Nm Ns Pq 4
+consumers.
.El
.Pp
.Sy pr_cursig
@@ -1023,6 +1034,20 @@ the right of the high-order bit (1.0 == 0x8000).
.Sy pr_pctcpu
is the summation over all lwps in the process.
.Pp
+The
+.Sy pr_fname
+and
+.Sy pr_psargs
+are writable by the owner of the process.
+To write to them, the
+.Sy psinfo
+file should be open for writing and the desired value for the field should be
+written at the file offset that corresponds to the member of structure.
+No other entry may be written to; if a write is attempted to an offset that
+does not represent one of these two memers, or if the size of the write is not
+exactly the size of the member being written, no bytes will be written and
+zero will be returned.
+.Pp
.Sy pr_lwp
contains the
.Xr ps 1
@@ -1177,6 +1202,15 @@ structures (see
.In sys/auxv.h ) .
The values are those that were passed by the operating system as startup
information to the dynamic linker.
+.Ss argv
+Contains the concatenation of each of the argument strings, including their
+.Sy NUL
+terminators, in the argument vector
+.Pq Va argv
+for the process.
+If the process has modified either its argument vector, or the contents of
+any of the strings referenced by that vector, those changes will be visible
+here.
.Ss ldt
This file exists only on x86-based machines.
It is non-empty only if the process has established a local descriptor table
@@ -2916,6 +2950,8 @@ process privileges
process signal actions
.It Pa /proc/ Ns Em pid Ns Pa /auxv
process aux vector
+.It Pa /proc/ Ns Em pid Ns Pa /argv
+process argument vector
.It Pa /proc/ Ns Em pid Ns Pa /ldt
process
.Sy LDT
diff --git a/usr/src/man/man5/process.5 b/usr/src/man/man5/process.5
index 8cab5f5e0d..15ebea98c2 100644
--- a/usr/src/man/man5/process.5
+++ b/usr/src/man/man5/process.5
@@ -1,5 +1,6 @@
'\" te
.\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright 2016, Joyent, Inc.
.\" 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]
@@ -196,6 +197,21 @@ exits.
.sp
.ne 2
.na
+\fB\fBCT_PR_KEEP_EXEC\fR\fR
+.ad
+.sp .6
+.RS 4n
+If set, the process contract template remains active across \fBexec\fR(2).
+This can be used to setup a contract for children of an application which
+is not contract-aware. If this is not set then the system clears the active
+template when the process execs. Because this option is intended for an
+application which is not contract-aware, new child process contracts will be
+automatically abandoned by the parent.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBCT_PR_NOORPHAN\fR\fR
.ad
.sp .6
diff --git a/usr/src/man/man7/Makefile b/usr/src/man/man7/Makefile
index 2527fc1008..6ce3008a0c 100644
--- a/usr/src/man/man7/Makefile
+++ b/usr/src/man/man7/Makefile
@@ -14,7 +14,7 @@
# Copyright (c) 2012 by Delphix. All rights reserved.
# Copyright 2014 Nexenta Systems, Inc.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
-# Copyright (c) 2015, Joyent, Inc. All rights reserved.
+# Copyright 2017 Joyent, Inc.
# Copyright 2018 Gary Mills
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
# Copyright 2019 Peter Tribble
@@ -62,6 +62,7 @@ _MANFILES= Intro.7 \
iconv_unicode.7 \
ieee802.3.7 \
ieee802.11.7 \
+ inotify.7 \
ipfilter.7 \
isalist.7 \
kerberos.7 \
@@ -72,6 +73,7 @@ _MANFILES= Intro.7 \
lfcompile.7 \
lfcompile64.7 \
locale.7 \
+ lx.7 \
man.7 \
mandoc_char.7 \
mandoc_roff.7 \
@@ -135,8 +137,6 @@ _MANFILES= Intro.7 \
zones.7 \
zpool-features.7
-sparc_MANFILES=
-
i386_MANFILES= beastie.4th.7 \
brand.4th.7 \
check-password.4th.7 \
diff --git a/usr/src/man/man7/inotify.7 b/usr/src/man/man7/inotify.7
new file mode 100644
index 0000000000..13ea10cf2b
--- /dev/null
+++ b/usr/src/man/man7/inotify.7
@@ -0,0 +1,305 @@
+'\" te
+.\" Copyright (c) 2014, Joyent, Inc. All Rights Reserved.
+.\" 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.
+.TH INOTIFY 7 "Sep 17, 2014"
+.SH NAME
+inotify \- Linux-compatible file event notification facility
+.SH SYNOPSIS
+
+.LP
+.nf
+#include <sys/inotify.h>
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+
+\fBinotify\fR is a facility for receiving file system events on specified
+files or directories. When monitoring a directory, \fBinotify\fR can be
+used to retrieve events not only on the directory, but also on any files
+that the directory contains. \fBinotify\fR originated with Linux, and
+this facility is designed to be binary-compatible with the Linux facility,
+including the following interfaces:
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+\fBinotify_init\fR(3C) creates an \fBinotify\fR instance, returning a file
+descriptor associated with the in-kernel event queue.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+\fBinotify_init1\fR(3C) also creates an \fBinotify\fR instance, but allows
+for a flags argument that controls some attributes of the returned file
+descriptor.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+\fBinotify_add_watch\fR(3C) allows a watch of a particular file or directory
+to be added to a watch list associated with the specified \fBinotify\fR
+instance. \fBinotify_add_watch\fR(3C) returns a watch descriptor that will
+be reflected in the \fIwd\fR member of the \fIinotify_event\fR structure
+returned via a \fBread\fR(2) of the instance.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+\fBinotify_rm_watch\fR(3C) removes the watch that corresponds to the specified
+watch descriptor.
+.RE
+
+When all file descriptors referring to a particular \fBinotify\fR instance
+are closed, the instance and all watches associated with that instance are
+freed.
+
+To consume events on an \fBinotify\fR instance, an application should
+issue a \fBread\fR(2) to the instance. If no events are available
+(and the \fBinotify\fR instance has not been explicitly made non-blocking
+via \fBinotify_init1\fR(3C)) the \fBread\fR(2) will block until a
+watched event occurs. If and when events are available, \fBread\fR(2) will
+return an array of the following structures:
+
+.sp
+.in +2
+.nf
+struct inotify_event {
+ int wd; /* watch descriptor */
+ uint32_t mask; /* mask of event */
+ uint32_t cookie; /* cookie for associating renames */
+ uint32_t len; /* size of name field */
+ char name[]; /* optional name */
+};
+.fi
+.in -2
+
+\fIwd\fR contains the watch descriptor that corresponds to the event,
+as returned by \fBinotify_add_watch\fR(3C).
+
+\fImask\fR is a bitwise \fBOR\fR of event masks (see below) that
+describes the event.
+
+\fIcookie\fR is an opaque value that can be used to associate different
+events into a single logical event. In particular, it allows consumers to
+associate \fBIN_MOVED_FROM\fR events with subsequent \fBIN_MOVED_TO\fR
+events.
+
+\fIlen\fR denotes the length of the \fIname\fR field, including any padding
+required for trailing null bytes and alignment. The size of the entire
+event is therefore the size of the \fIinotify_event\fR structure plus the
+value of \fIlen\fR.
+
+\fIname\fR contains the name of the file associated with the event, if any.
+This field is only present when the watched entity is a directory and
+the event corresponds to a file that was contained by the watched directory
+(though see \fBNOTES\fR and \fBWARNINGS\fR for details and limitations).
+When present, \fIname\fR is null terminated, and may contain additional
+zero bytes
+to pad for alignment. (The length of this field -- including any bytes
+for alignment -- is denoted by the \fIlen\fR field.)
+
+.SS "Events"
+
+The events that can be generated on a watched entity are as follows:
+
+.sp
+.in +2
+.TS
+c c
+l l .
+\fIEvent\fR \fIDescription\fR
+\fBIN_ACCESS\fR File/directory was accessed
+\fBIN_ATTRIB\fR File/directory attributes were changed
+\fBIN_CLOSE_WRITE\fR File/directory opened for writing was closed
+\fBIN_CLOSE_NOWRITE\fR File/directory not opened for writing was closed
+\fBIN_CREATE\fR File/directory created in watched directory
+\fBIN_DELETE\fR File/directory deleted from watched directory
+\fBIN_DELETE_SELF\fR Watched file/directory was deleted
+\fBIN_MODIFY\fR File/directory was modified
+\fBIN_MODIFY_SELF\fR Watched file/directory was modified
+\fBIN_MOVED_FROM\fR File was renamed from entity in watched directory
+\fBIN_MOVED_TO\fR File was renamed to entity in watched directory
+\fBIN_OPEN\fR File/directory was opened
+.TE
+.in -2
+
+Of these, all events except \fBIN_MOVE_SELF\fR and \fBIN_DELETE_SELF\fR
+can refer to either the watched entity or (if the watched entity
+is a directory) a file or directory contained by the watched directory.
+(See \fBNOTES\fR and \fBWARNINGS\fR, below for details on this
+mechanism and its limitations.)
+If the event corresponds to a contained entity,
+\fIname\fR will be set to the name of the affected
+entity.
+
+In addition to speciyfing events of interest, watched events may
+be modified by potentially setting any of the following when adding a
+watch via \fBinotify_add_watch\fR(3C):
+
+.sp
+.ne 2
+.na
+\fBIN_DONT_FOLLOW\fR
+.ad
+.RS 12n
+Don't follow the specified pathname if it is a symbolic link.
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_EXCL_UNLINK\fR
+.ad
+.RS 12n
+If watching a directory and a contained entity becomes unlinked, cease
+generating events for that entity. (By default, contained entities will
+continue to generate events on their former parent directory.)
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_MASK_ADD\fR
+.ad
+.RS 12n
+If the specified pathname is already being watched, the specified events
+will be added to the watched events instead of the default behavior of
+replacing them. (If one
+may forgive the editorializing, this particular interface gewgaw
+seems entirely superfluous, and a canonical example of
+feasibility trumping wisdom.)
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_ONESHOT\fR
+.ad
+.RS 12n
+Once an event has been generated for the watched entity, remove the
+watch from the watch list as if \fBinotify_rm_watch\fR(3C) had been called
+on it (thereby inducing an \fBIN_IGNORED\fR event).
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_ONLYDIR\fR
+.ad
+.RS 12n
+Only watch the specified pathname if it is a directory.
+.RE
+
+In addition to the specified events, the following bits may be specified
+in the \fImask\fR field as returned from \fBread\fR(2):
+
+.sp
+.ne 2
+.na
+\fBIN_IGNORED\fR
+.ad
+.RS 12n
+A watch was removed explicitly (i.e, via \fBinotify_rm_watch\fR(3C)) or
+implicitly (e.g., because \fBIN_ONESHOT\fR was set or because the watched
+entity was deleted).
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_ISDIR\fR
+.ad
+.RS 12n
+The entity inducing the event is a directory.
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_Q_OVERFLOW\fR
+.ad
+.RS 12n
+The event queue exceeded the maximum event queue length per instance.
+(By default, this is 16384, but it can be tuned by setting
+\fBinotify_maxevents\fR via \fB/etc/system\fR.)
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_UNMOUNT\fR
+.ad
+.RS 12n
+The filesystem containing the watched entity was unmounted.
+.RE
+
+.sp
+.SH NOTES
+.sp
+.LP
+
+\fBinotify\fR instances can be monitored via \fBpoll\fR(2),
+\fBport_get\fR(3C), \fBepoll\fR(7), etc.
+
+The event queue associated with an \fBinotify\fR instance is serialized
+and ordered: events will be placed on the tail of the queue in the order
+that they occur.
+
+If at the time an event occurs the tail of the event queue is identical
+to the newly received event, the newly received event will be dropped,
+effectively coalescing the two events.
+
+When watching a directory and receieving events on contained elements
+(i.e., a contained file or subdirectory), note that the information
+received in the \fIname\fR field may be stale: the file may have been
+renamed between the event and its processing. If a file has been unlinked
+(and if \fBIN_EXCL_UNLINK\fR has not been set),
+the \fIname\fR will reflect the last name that resolved to the file.
+If a new file is created in the same directory with the old name, events
+on the new file and the old (unlinked) file will become undistinguishable.
+
+The number of bytes that are available to be read on an \fBinotify\fR
+instance can be determined via a \fBFIONREAD\fR \fBioctl\fR(2).
+
+.sp
+.SH WARNINGS
+.sp
+.LP
+
+While a best effort has been made to mimic the Linux semantics, there
+remains a fundamental difference with respect to hard links: on Linux,
+if a file has multiple hard links to it, a notification on a watched
+directory or file will be received if and only if that event was received
+via the watched path. For events that are induced by open files
+(such as \fBIN_MODIFY\fR), these semantics seem peculiar: the watched
+file is in fact changing, but because it is not changing via the watched
+path, no notification is received. By contrast, the implementation here
+will always yield an event in this case -- even if the event was induced
+by an \fBopen\fR(2) via an unwatched path. If an event occurs within a
+watched directory on a file for which there exist multiple hard links within
+the same (watched) directory, the event's \fIname\fR will correspond to one
+of the links to the file. If multiple hard links exist to the
+same file in the same watched directory and one of the links is removed,
+notifications may not necessarily continue to be received for the file,
+despite the (remaining) link in the watched directory; users of
+\fBinotify\fR should exercise extreme caution when watching directories
+that contain files with multiple hard links in the same directory.
+
+.SH SEE ALSO
+.sp
+.LP
+\fBinotify_init\fR(3C), \fBinotify_init1\fR(3C), \fBinotify_add_watch\fR(3C),
+\fBinotify_rm_watch\fR(3C), \fBport_get\fR(3C), \fBepoll\fR(7)
diff --git a/usr/src/man/man7/lx.7 b/usr/src/man/man7/lx.7
new file mode 100644
index 0000000000..a45ead418c
--- /dev/null
+++ b/usr/src/man/man7/lx.7
@@ -0,0 +1,113 @@
+.\"
+.\" 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 2016, Joyent, Inc.
+.\"
+.Dd February 5, 2106
+.Dt LX 7
+.Os
+.Sh NAME
+.Nm lx
+.Nd zone brand for running a GNU/Linux user-level environment
+.Sh DESCRIPTION
+The
+.Em lx
+brand
+uses the
+.Xr brands 7
+framework to provide an environment for running binary applications built
+for GNU/Linux.
+User-level code, including an entire Linux distribution, can run inside the
+zone.
+Both 32-bit and 64-bit applications are supported.
+The majority of Linux system calls are provided, along with emulation for a
+variety of Linux file systems, such as
+.Em proc ,
+.Em cgroup
+and
+.Em sysfs .
+.Pp
+The
+.Em /proc
+file system within the zone is a subset of a full Linux
+.Em /proc .
+Most kernel-level tuning applied to
+.Em /proc
+is unavailable or ignored.
+Some tuning can be performed, but only to reduce the overall limits that have
+been specified on the zone's configuration.
+That is, within the zone there is no way to increase the resource limits set on
+the zone itself.
+.Pp
+The zone must be installed using a clone of a
+.Xr zfs 8
+dataset which contains an image of the software to be run in the zone.
+.Pp
+Example:
+.Dl zoneadm -z myzone install -x nodataset -t debian7
+.Pp
+Applications provided by the base SunOS operating system are also available
+within the zone under the
+.Em /native
+mount point.
+This allows the use of various native tools such as
+.Xr dtrace 8 ,
+.Xr mdb 1 ,
+or the
+.Xr proc 1
+tools on GNU/Linux applications.
+However, not every native tool will work properly within an
+.Em lx
+zone.
+.Sh CONFIGURATION
+The
+.Em kernel-version
+attribute can be included in the zone's
+.Xr zonecfg 8
+settings as a way to specify the Linux version that the zone is emulating.
+For example, the value could be
+.Em 3.13.0 .
+.Sh LIMITATIONS
+The brand only supports the exclusive IP stack zone configuration.
+.Pp
+Most modern GNU/Linux application software runs on
+.Em lx ,
+but because there are some system calls or file systems which are not currently
+implemented, it's possible that an application won't run.
+This does not preclude the application running in the future as the
+.Em lx
+brand adds new capabilities.
+.Pp
+Because there is only the single SunOS kernel running on the system, there
+is no support for any Linux kernel-level modules.
+That is, there is no support for add-on drivers or any other modules that are
+part of the Linux kernel itself.
+If that is required, a full virtual machine should be used instead of an
+.Em lx
+branded zone.
+.Pp
+Any core files produced within the zone are in the native SunOS format.
+.Pp
+As with any zone, the normal security mechanisms and privileges apply.
+Thus, certain operations (for example, changing the system time), will not be
+allowed unless the zone has been configured with the appropriate additional
+privileges.
+.Sh SEE ALSO
+.Xr mdb 1 ,
+.Xr proc 1 ,
+.Xr brands 7 ,
+.Xr privileges 7 ,
+.Xr resource_controls 7 ,
+.Xr zones 7 ,
+.Xr dtrace 8 ,
+.Xr zfs 8 ,
+.Xr zoneadm 8 ,
+.Xr zonecfg 8
diff --git a/usr/src/man/man7/privileges.7 b/usr/src/man/man7/privileges.7
index aade5c020c..b2d51cccf8 100644
--- a/usr/src/man/man7/privileges.7
+++ b/usr/src/man/man7/privileges.7
@@ -307,6 +307,16 @@ Allow a process to perform privileged mappings through a graphics device.
.sp
.ne 2
.na
+\fB\fBPRIV_HYPRLOFS_CONTROL\fR\fR
+.ad
+.sp .6
+.RS 4n
+Allow a process to perform hyprlofs name space management.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBPRIV_IPC_DAC_READ\fR\fR
.ad
.sp .6
@@ -697,6 +707,16 @@ Allow a process to configure a system's datalink interfaces.
.sp
.ne 2
.na
+\fB\fBPRIV_SYS_FS_IMPORT\fR\fR
+.ad
+.sp .6
+.RS 4n
+Allow a process to import a potentially untrusted file system (e.g. ZFS recv).
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBPRIV_SYS_IP_CONFIG\fR\fR
.ad
.sp .6
diff --git a/usr/src/man/man7/resource_controls.7 b/usr/src/man/man7/resource_controls.7
index e9928399aa..34392538bc 100644
--- a/usr/src/man/man7/resource_controls.7
+++ b/usr/src/man/man7/resource_controls.7
@@ -1,12 +1,13 @@
'\" te
.\" Copyright (c) 2007, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright 2017, Joyent, Inc.
.\" Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
.\" 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]
.TH RESOURCE_CONTROLS 7 "Jan 23, 2021"
.SH NAME
-resource_controls \- resource controls available through project database
+resource_controls \- resource controls available through projects and zones
.SH DESCRIPTION
The resource controls facility is configured through the project database. See
\fBproject\fR(5). You can set and modify resource controls through the
@@ -35,6 +36,12 @@ following utilities:
.el o
.BR rctladm (8)
.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+\fBzonecfg\fR(8)
+.RE
.sp
.LP
In a program, you use \fBsetrctl\fR(2) to set resource control values.
@@ -407,6 +414,33 @@ The following zone-wide resource controls are available:
.sp
.ne 2
.na
+\fBzone.cpu-baseline\fR
+.ad
+.sp .6
+.RS 4n
+Sets a baseline amount of CPU time that a zone can use before it is considered
+to be bursting. The unit used is the percentage of a single CPU that is being
+used by all user threads in a zone. The value should be less than the
+\fBzone.cpu-cap\fR rctl value and is expressed as an integer.
+This resource control does not support the \fBsyslog\fR action.
+.RE
+
+.sp
+.ne 2
+.na
+\fBzone.cpu-burst-time\fR
+.ad
+.sp .6
+.RS 4n
+Sets the number of seconds that a zone can exceed the \fBzone.cpu-baseline\fR
+rctl value before being cpu-capped down to the \fBzone.cpu-baseline\fR.
+A value of 0 means that \fBzone.cpu-baseline\fR can be exceeded indefinitely.
+This resource control does not support the \fBsyslog\fR action.
+.RE
+
+.sp
+.ne 2
+.na
\fBzone.cpu-cap\fR
.ad
.sp .6
@@ -425,7 +459,7 @@ not support the \fBsyslog\fR action.
.ad
.sp .6
.RS 4n
-Sets a limit on the number of fair share scheduler (FSS) CPU shares for a zone.
+Sets a value on the number of fair share scheduler (FSS) CPU shares for a zone.
CPU shares are first allocated to the zone, and then further subdivided among
projects within the zone as specified in the \fBproject.cpu-shares\fR entries.
Expressed as an integer. This resource control does not support the
@@ -445,6 +479,15 @@ Total amount of physical locked memory available to a zone.
.sp
.ne 2
.na
+\fBzone.max-lofi\fR
+.ad
+.sp .6
+.RS 4n
+Sets a limit on the number of \fBLOFI\fR(4D) devices that can be created in a
+zone. Expressed as an integer. This resource control does not support the
+\fBsyslog\fR action.
+.RE
+
\fBzone.max-lwps\fR
.ad
.sp .6
@@ -469,6 +512,14 @@ integer.
.sp
.ne 2
.na
+\fBzone.max-physical-memory\fR
+.ad
+.sp .6
+.RS 4n
+Sets a limit on the amount of physical memory (RSS) that can be used by a zone
+before resident pages start being forcibly paged out. The unit used is bytes.
+Expressed as an integer. This resource control does not support the
+\fBsyslog\fR action.
\fBzone.max-processes\fR
.ad
.sp .6
@@ -482,6 +533,11 @@ Expressed as an integer.
.sp
.ne 2
.na
+.RE
+
+.sp
+.ne 2
+.na
\fBzone.max-sem-ids\fR
.ad
.sp .6
@@ -523,6 +579,18 @@ mappings and \fBtmpfs\fR mounts for this zone.
.RE
.sp
+.ne 2
+.na
+\fB\fBzone.zfs-io-priority\fR\fR
+.ad
+.sp .6
+.RS 4n
+Sets a value for the \fBzfs\fR(8) I/O priority for a zone. This is used as
+one of the inputs to determine if a zone's I/O should be throttled. Expressed
+as an integer. This resource control does not support the \fBsyslog\fR action.
+.RE
+
+.sp
.LP
See \fBzones\fR(7).
.SS "Units Used in Resource Controls"
@@ -1032,6 +1100,8 @@ Interface Stability Evolving
.BR FSS (4),
.BR project (5),
.BR attributes (7),
+.BR privileges (7),
+.BR zones (7),
.BR pooladm (8),
.BR poolcfg (8),
.BR projadd (8),
diff --git a/usr/src/man/man8/Makefile b/usr/src/man/man8/Makefile
index 8d683fd99f..b8768faed3 100644
--- a/usr/src/man/man8/Makefile
+++ b/usr/src/man/man8/Makefile
@@ -508,7 +508,10 @@ _MANFILES= 6to4relay.8 \
uucleanup.8 \
uusched.8 \
uuxqt.8 \
+ vfsstat.8 \
vmstat.8 \
+ vndadm.8 \
+ vndstat.8 \
volcopy.8 \
volcopy_ufs.8 \
vscanadm.8 \
diff --git a/usr/src/man/man8/connstat.8 b/usr/src/man/man8/connstat.8
index 686f3c4648..cffc654e02 100644
--- a/usr/src/man/man8/connstat.8
+++ b/usr/src/man/man8/connstat.8
@@ -14,8 +14,9 @@
.\"
.\"
.\" Copyright (c) 2016 by Delphix. All rights reserved.
+.\" Copyright 2019 Joyent, Inc.
.\"
-.Dd July 5, 2016
+.Dd August 15, 2019
.Dt CONNSTAT 8
.Os
.Sh NAME
@@ -165,6 +166,9 @@ The current retransmission timeout in milliseconds.
.It Sy rtt
The current smoothed round-trip time to the peer in microseconds.
The smoothed RTT average algorithm used is as described in RFC 6298.
+.It Sy rttvar
+The current smoothed variation of the round-trip time to the peer in
+microseconds.
.It Sy rttc
The number of times that a round-trip sample was added to
.Sy rtts .
diff --git a/usr/src/man/man8/dladm.8 b/usr/src/man/man8/dladm.8
index b18a9d7f50..f6ecd15b82 100644
--- a/usr/src/man/man8/dladm.8
+++ b/usr/src/man/man8/dladm.8
@@ -41,7 +41,7 @@
.\"
.\"
.\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved
-.\" Copyright 2016 Joyent, Inc.
+.\" Copyright 2017 Joyent, Inc.
.\" Copyright 2020 RackTop Systems, Inc.
.\" Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
.\"
@@ -54,7 +54,7 @@ dladm \- administer data links
.LP
.nf
\fBdladm show-link\fR [\fB-P\fR] [\fB-s\fR [\fB-i\fR \fIinterval\fR]] [[\fB-p\fR] \fB-o\fR \fIfield\fR[,...]] [\fIlink\fR]
-\fBdladm rename-link\fR [\fB-R\fR \fIroot-dir\fR] \fIlink\fR \fInew-link\fR
+\fBdladm rename-link\fR [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR] \fIlink\fR \fInew-link\fR
.fi
.LP
@@ -137,9 +137,11 @@ dladm \- administer data links
.LP
.nf
-\fBdladm set-linkprop\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] \fB-p\fR \fIprop\fR=\fIvalue\fR[,...] \fIlink\fR
-\fBdladm reset-linkprop\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-p\fR \fIprop\fR[,...]] \fIlink\fR
-\fBdladm show-linkprop\fR [\fB-P\fR] [[\fB-c\fR] \fB-o\fR \fIfield\fR[,...]] [\fB-p\fR \fIprop\fR[,...]] [\fIlink\fR]
+\fBdladm set-linkprop\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR] \fB-p\fR \fIprop\fR=\fIvalue\fR[,...]
+ \fIlink\fR
+\fBdladm reset-linkprop\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR] [\fB-p\fR \fIprop\fR[,...]] \fIlink\fR
+\fBdladm show-linkprop\fR [\fB-P\fR] [\fB-z\fR \fIzonename\fR] [[\fB-c\fR] \fB-o\fR \fIfield\fR[,...]]
+ [\fB-p\fR \fIprop\fR[,...]] [\fIlink\fR]
.fi
.LP
@@ -154,9 +156,9 @@ dladm \- administer data links
\fBdladm create-vnic\fR [\fB-t\fR] \fB-l\fR \fIlink\fR [\fB-R\fR \fIroot-dir\fR] [\fB-m\fR \fIvalue\fR | auto |
{factory \fB-n\fR \fIslot-identifier\fR]} | {random [\fB-r\fR \fIprefix\fR]}]
[\fB-v\fR \fIvlan-id\fR] [\fB-p\fR \fIprop\fR=\fIvalue\fR[,...]] \fIvnic-link\fR
-\fBdladm delete-vnic\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] \fIvnic-link\fR
+\fBdladm delete-vnic\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR] \fIvnic-link\fR
\fBdladm show-vnic\fR [\fB-pP\fR] [\fB-s\fR [\fB-i\fR \fIinterval\fR]] [\fB-o\fR \fIfield\fR[,...]]
- [\fB-l\fR \fIlink\fR] [\fIvnic-link\fR]
+ [\fB-l\fR \fIlink\fR] [\fB-z\fR \fIzonename\fR] [\fIvnic-link\fR]
.fi
.LP
@@ -471,6 +473,19 @@ A virtual network interface. The \fBshow-vnic\fR subcommand displays more
detail for this class of datalink.
.RE
+.sp
+.ne 2
+.na
+\fB\fBoverlay\fR\fR
+.ad
+.sp .6
+.RS 4n
+A virtual device that is used to create or join a software defined
+network. The \fBshow-overlay\fR subcommand displays more detail for this
+class of datalink.
+.RE
+
+
.RE
.sp
@@ -640,8 +655,7 @@ will be displayed only once.
.sp
.ne 2
.na
-\fB\fBdladm rename-link\fR [\fB-R\fR \fIroot-dir\fR] \fIlink\fR
-\fInew-link\fR\fR
+\fB\fBdladm rename-link\fR [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR] \fIlink\fR \fInew-link\fR\fR
.ad
.sp .6
.RS 4n
@@ -659,6 +673,16 @@ examples of how this subcommand is used.
See "Options," above.
.RE
+.sp
+.ne 2
+.na
+\fB\fB-z\fR \fIzonename\fR
+.ad
+.sp .6
+.RS 4n
+A link assigned to a zone can only be renamed while the zone is in the ready state.
+.RE
+
.RE
.sp
@@ -3264,8 +3288,7 @@ Extended output is displayed for \fBPTYPE\fR values of \fBcurrent\fR,
.sp
.ne 2
.na
-\fB\fBdladm set-linkprop\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] \fB-p\fR
-\fIprop\fR=\fIvalue\fR[,...] \fIlink\fR\fR
+\fB\fBdladm set-linkprop\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR] \fB-p\fR \fIprop\fR=\fIvalue\fR[,...] \fIlink\fR\fR
.ad
.sp .6
.RS 4n
@@ -3297,6 +3320,16 @@ See "Options," above.
.sp
.ne 2
.na
+\fB\fB-z\fR \fIzonename\fR
+.ad
+.sp .6
+.RS 4n
+Operate on a link that has been delegated to the specified zone.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fB-p\fR \fIprop\fR=\fIvalue\fR[,...], \fB--prop\fR
\fIprop\fR=\fIvalue\fR[,...]\fR
.ad
@@ -3316,8 +3349,7 @@ same value.
.sp
.ne 2
.na
-\fB\fBdladm reset-linkprop\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-p\fR
-\fIprop\fR,...] \fIlink\fR\fR
+\fB\fBdladm reset-linkprop\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR] [\fB-p\fR \fIprop\fR,...] \fIlink\fR\fR
.ad
.sp .6
.RS 4n
@@ -3349,6 +3381,16 @@ See "Options," above.
.sp
.ne 2
.na
+\fB\fB-z\fR \fIzonename\fR
+.ad
+.sp .6
+.RS 4n
+Operate on a link that has been delegated to the specified zone.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fB-p\fR \fIprop, ...\fR, \fB--prop\fR=\fIprop, ...\fR\fR
.ad
.sp .6
@@ -3363,8 +3405,7 @@ the same value.
.sp
.ne 2
.na
-\fB\fBdladm show-linkprop\fR [\fB-P\fR] [[\fB-c\fR] \fB-o\fR
-\fIfield\fR[,...]][\fB-p\fR \fIprop\fR[,...]] [\fIlink\fR]\fR
+\fB\fBdladm show-linkprop\fR [\fB-P\fR] [\fB-z\fR \fIzonename\fR] [[\fB-c\fR] \fB-o\fR \fIfield\fR[,...]][\fB-p\fR \fIprop\fR[,...]] [\fIlink\fR]\fR
.ad
.sp .6
.RS 4n
@@ -3482,6 +3523,16 @@ Display persistent link property information
.sp
.ne 2
.na
+\fB\fB-z\fR \fIzonename\fR
+.ad
+.sp .6
+.RS 4n
+Operate on a link that has been delegated to the specified zone.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fB-p\fR \fIprop, ...\fR, \fB--prop\fR=\fIprop, ...\fR\fR
.ad
.sp .6
@@ -3799,8 +3850,7 @@ A comma-separated list of properties to set to the specified values.
.sp
.ne 2
.na
-\fB\fBdladm delete-vnic\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR]
-\fIvnic-link\fR\fR
+\fB\fBdladm delete-vnic\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR] \fIvnic-link\fR\fR
.ad
.sp .6
.RS 4n
@@ -3826,13 +3876,22 @@ next reboot.
See "Options," above.
.RE
+.sp
+.ne 2
+.na
+\fB\fB-z\fR \fIzonename\fR
+.ad
+.sp .6
+.RS 4n
+Operate on a link that has been delegated to the specified zone.
+.RE
+
.RE
.sp
.ne 2
.na
-\fB\fBdladm show-vnic\fR [\fB-pP\fR] [\fB-s\fR [\fB-i\fR \fIinterval\fR]]
-[\fB-o\fR \fIfield\fR[,...]] [\fB-l\fR \fIlink\fR] [\fIvnic-link\fR]\fR
+\fB\fBdladm show-vnic\fR [\fB-pP\fR] [\fB-s\fR [\fB-i\fR \fIinterval\fR]] [\fB-o\fR \fIfield\fR[,...]] [\fB-l\fR \fIlink\fR] [\fB-z\fR \fIzonename\fR] [\fIvnic-link\fR]\fR
.ad
.sp .6
.RS 4n
@@ -3975,6 +4034,16 @@ will be displayed only once.
Display information for all VNICs on the named link.
.RE
+.sp
+.ne 2
+.na
+\fB\fB-z\fR \fIzonename\fR
+.ad
+.sp .6
+.RS 4n
+Operate on a link that has been delegated to the specified zone.
+.RE
+
.RE
.sp
@@ -4844,6 +4913,43 @@ The following general link properties are supported:
.sp
.ne 2
.na
+\fB\fBallow-all-dhcp-cids\fR\fR
+.ad
+.sp .6
+.RS 4n
+One of \fBtrue\fR or \fBfalse\fR, to indicate whether or not all DHCP Client
+Identifiers should be permitted on this interface when DHCP spoofing protection
+is being used. This can be useful in cases where a DHCP client is using RFC
+4361-style Client Identifiers, which are based on a value that is opaque to the
+Global Zone, but enforcement of MAC addresses in DHCP packets is still desired.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBallowed-dhcp-cids\fR\fR
+.ad
+.sp .6
+.RS 4n
+A comma-separated list of DHCP Client Identifiers that are allowed on the
+interface.
+.sp
+Client identifiers can be written in three different formats: a string of
+hexadecimal characters prefixed by \fB0x\fR, indicating the exact bytes used in
+the Client Identifier; an RFC 3315 DUID of the form
+"1.<hardware\ type>.<time>.<link-layer\ address>" (DUID-LLT),
+"2.<enterprise\ number>.<hex\ string>" (DUID-EN), or
+"3.<hardware\ type>.<link-layer\ address>" (DUID-LL); or a string of characters
+whose byte values should be used as the Client Identifier.
+.sp
+When specifying a string of hexadecimal characters prefixed by \fB0x\fR or as
+part of a DUID-EN string, an even number of hexadecimal characters must be
+provided in order to fully specify each byte.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBallowed-ips\fR\fR
.ad
.sp .6
@@ -4905,6 +5011,24 @@ is not bound to any specific processor or processor set.
.sp
.ne 2
.na
+\fB\fBdynamic-methods\fR\fR
+.ad
+.sp .6
+.RS 4n
+When using IP spoofing protection (see \fBprotection\fR), addresses can be
+learned dynamically by monitoring certain network traffic, like DHCP
+transactions or IPv6 Stateless Address Autoconfiguration (SLAAC). By default,
+all learning methods are permitted, but if \fBallowed-ips\fR contains any
+addresses, then all methods are disabled, and any packets sent from addresses
+previously learned will be dropped. This property allows selecting which ones
+are re-enabled, where valid options are \fBdhcpv4\fR, \fBdhcpv6\fR, and
+\fBslaac\fR. \fBaddrconf\fR is available as an alias for enabling both
+\fBdhcpv6\fR and \fBslaac\fR.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBlearn_limit\fR\fR
.ad
.sp .6
@@ -4957,6 +5081,67 @@ tokens \fBhigh\fR, \fBmedium\fR, or \fBlow\fR. The default is \fBhigh\fR.
.sp
.ne 2
.na
+\fB\fBprotection\fR\fR
+.ad
+.sp .6
+.RS 4n
+This property enables various forms of link protections, which prevent sending
+applicable traffic out of this link. Note that since this enforcement happens
+late in the networking stack, some observability tools like \fBsnoop\fR(1M) may
+still see dropped outbound packets.
+
+This property should be set to a comma-separated list of protections to enable
+on this link, where available protections are:
+.sp
+.ne 2
+.na
+\fBip-nospoof\fR
+.ad
+.sp .6
+.RS 4n
+Prevents sending from IPv4 and IPv6 addresses that have not been permitted
+over the NIC. Addresses can be learned dynamically (see \fBdynamic-methods\fR)
+or specified explicitly (see \fBallowed-ips\fR).
+.RE
+.sp
+.ne 2
+.na
+\fBdhcp-nospoof\fR
+.ad
+.sp .6
+.RS 4n
+Prevents sending DHCP packets whose client hardware address
+(CHADDR) field differs from the link-layer address, or from using a Client
+Identifier whose value cannot be confirmed to be derived from the link-layer
+address. Additional Client Identifiers can be permitted through the
+\fBallowed-dhcp-cids\fR and \fBallow-all-dhcp-cids\fR link properties.
+.RE
+.sp
+.ne 2
+.na
+\fBmac-nospoof\fR
+.ad
+.sp .6
+.RS 4n
+Prevents sending packets with a link-layer address that differs from the one
+associated with the NIC. Additional addresses to allow can be added using the
+\fBseconday-macs\fR property.
+.RE
+.sp
+.ne 2
+.na
+\fBrestricted\fR
+.ad
+.sp .6
+.RS 4n
+Prevents using a VLAN ID not associated with the NIC and sending packets that
+are not IPv4, IPv6 or ARP.
+.RE
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBstp\fR\fR
.ad
.sp .6
@@ -5988,6 +6173,16 @@ Interface Stability Committed
.BR psrset (8),
.BR wpad (8),
.BR zonecfg (8)
+.sp
+.LP
+R. Droms, Ed., J. Bound, B. Volz, T. Lemon, C. Perkins, M. Carney. \fIRFC 3315:
+Dynamic Host Configuration Protocol for IPv6 (DHCPv6)\fR. The Internet Society.
+July 2003.
+.sp
+.LP
+T. Lemon, B. Sommerfeld. February 2006. \fIRFC 4361: Node-specific Client
+Identifiers for Dynamic Host Configuration Protocol Version Four (DHCPv4)\fR.
+The Internet Society. January 2006.
.SH NOTES
The preferred method of referring to an aggregation in the aggregation
subcommands is by its link name. Referring to an aggregation by its integer
diff --git a/usr/src/man/man8/dumpadm.8 b/usr/src/man/man8/dumpadm.8
index e05fe800e9..52270045bd 100644
--- a/usr/src/man/man8/dumpadm.8
+++ b/usr/src/man/man8/dumpadm.8
@@ -2,17 +2,18 @@
.\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved
.\" Copyright 2015 Nexenta Systems, Inc. All Rights Reserved.
.\" Copyright (c) 2013 by Delphix. All rights reserved.
+.\" Copyright 2019 Joyent, Inc.
.\" 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]
-.TH DUMPADM 8 "Apr 09, 2015"
+.TH DUMPADM 8 "Jun 15, 2019"
.SH NAME
dumpadm \- configure operating system crash dump
.SH SYNOPSIS
.LP
.nf
\fB/usr/sbin/dumpadm\fR [\fB-enuy\fR] [\fB-c\fR \fIcontent-type\fR] [\fB-d\fR \fIdump-device\fR]
- [\fB-m\fR \fImin\fRk | \fImin\fRm | \fImin\fR%] [\fB-s\fR \fIsavecore-dir\fR]
+ [\fB-k\fR \fIkey-file\fR] [\fB-m\fR \fImin\fRk | \fImin\fRm | \fImin\fR%] [\fB-s\fR \fIsavecore-dir\fR]
[\fB-r\fR \fIroot-dir\fR] [\fB-z\fR on | off]
.fi
@@ -41,8 +42,10 @@ write it to the file system. The directory in which the crash
dump is saved on reboot can also be configured using \fBdumpadm\fR.
.sp
.LP
-When the operating system takes a crash dump the default behavior is to
-compress the crash dump. This behavior is controlled by the \fB-z\fR option.
+A crash dump is always compressed on the dump device. The dump is decompressed
+by the \fBsavecore\fR(8) utility, which can optionally store the dump in its
+compressed state, thereby deferring decompression to a subsequent invocation
+of \fBsavecore\fR. This behavior is controlled by the \fB-z\fR option.
When compression is turned on, the \fBsavecore\fR(8) utility writes one file
to the file system named \fIvmdump.X\fR. If compression is disabled, it instead
writes two files named \fIunix.X\fR and \fIvmcore.X\fR. In the uncompressed
@@ -50,6 +53,21 @@ case, both data files form the \fIsaved crash dump\fR. In both cases X is an
integer identifying the dump.
.sp
.LP
+Crash dump encryption may be optionally enabled via the \fB-k\fR option, which
+specifies a file that contains an encryption key. When crash dump encryption
+is enabled, the contents of kernel memory as stored in the dump device will be
+encrypted. Decryption of a kernel crash dump must occur when the dump is
+extracted via \fBsavecore\fR (to which the encryption key must be separately
+provided). Decompression can only occur on a decrypted dump; when dump
+encryption is enabled, \fBsavecore\fR must store the dump in its compressed
+state. Note that \fBsavecore\fR cannot extract an encrypted dump without also
+decrypting it; when dump encryption is enabled, the operator should be sure
+to only operate \fBsavecore\fR on a directory that is separately encrypted
+or otherwise secured. Finally, note that \fBdumpadm\fR does not store the
+crash dump encryption key persistently: upon system reset, crash dump
+encryption is always disabled.
+.sp
+.LP
For systems with a UFS root file system, the default dump device is configured
to be an appropriate swap partition. Swap partitions are disk partitions
reserved as virtual memory backing store for the operating system. Thus, no
@@ -68,6 +86,7 @@ example# \fBdumpadm\fR
Savecore directory: /var/crash/saturn
Savecore enabled: yes
Save compressed: on
+ Dump encrypted: no
.fi
.in -2
.sp
@@ -199,6 +218,18 @@ Estimates the size of the dump for the current running system.
.sp
.ne 2
.na
+\fB\fB-k\fR \fIkey-file\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specifies that the dump should be encrypted based on the key found in
+\fIkey-file\fR. Note that any invocations of \fBsavecore\fR will need to
+specify the same key to be able to correctly retrieve the dump.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fB-m\fR \fImin\fR\fBk\fR | \fImin\fR\fBm\fR | \fImin\fR\fB%\fR\fR
.ad
.sp .6
@@ -346,6 +377,7 @@ example# dumpadm -d /dev/dsk/c0t2d0s2
Savecore directory: /var/crash/saturn
Savecore enabled: yes
Save compressed: on
+ Dump encrypted: no
.fi
.in -2
.sp
diff --git a/usr/src/man/man8/flowadm.8 b/usr/src/man/man8/flowadm.8
index 420835b7aa..561a24b17a 100644
--- a/usr/src/man/man8/flowadm.8
+++ b/usr/src/man/man8/flowadm.8
@@ -1,19 +1,20 @@
'\" te
.\" Copyright 2020 Peter Tribble
+.\" Copyright 2022 Joyent, Inc.
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved
.\" 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]
-.TH FLOWADM 8 "February 5, 2022"
+.TH FLOWADM 8 "March 15, 2022"
.SH NAME
flowadm \- administer bandwidth resource control and priority for protocols,
services, containers, and virtual machines
.SH SYNOPSIS
.nf
-\fBflowadm add-flow\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] \fB-l\fR \fIlink\fR \fB-a\fR \fIattr\fR=\fIvalue\fR[,...]
- [\fB-p\fR \fIprop\fR=\fIvalue\fR[,...]] \fIflow\fR
-\fBflowadm remove-flow\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] {\fB-l\fR \fIlink\fR | \fIflow\fR}
-\fBflowadm show-flow\fR [\fB-p\fR] [\fB-l\fR \fIlink\fR] [\fB-o\fR \fIfield\fR[,...]] [\fIflow\fR]
+\fBflowadm add-flow\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR] \fB-l\fR \fIlink\fR \fB-a\fR
+ \fIattr\fR=\fIvalue\fR[,...] [\fB-p\fR \fIprop\fR=\fIvalue\fR[,...]] \fIflow\fR
+\fBflowadm remove-flow\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR] {\fB-l\fR \fIlink\fR | \fIflow\fR}
+\fBflowadm show-flow\fR [\fB-p\fR] [\fB-l\fR \fIlink\fR] [\fB-o\fR \fIfield\fR[,...]] [\fB-z\fR \fIzonename\fR] [\fIflow\fR]
.fi
.LP
@@ -63,8 +64,8 @@ The following subcommands are supported:
.ne 2
.na
\fB\fBflowadm add-flow\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] \fB-l\fR
-\fIlink\fR \fB-a\fR \fIattr\fR=\fIvalue\fR[,...] [\fB-p\fR
-\fIprop\fR=\fIvalue\fR[,...]] \fIflow\fR\fR
+[\fB-z\fR \fIzonename\fR] \fIlink\fR \fB-a\fR \fIattr\fR=\fIvalue\fR[,...]
+[\fB-p\fR \fIprop\fR=\fIvalue\fR[,...]] \fIflow\fR\fR
.ad
.sp .6
.RS 4n
@@ -102,6 +103,16 @@ persistent creation.
.sp
.ne 2
.na
+\fB\fB-z\fR \fIzonename\fR
+.ad
+.sp .6
+.RS 4n
+Operate on a link that has been delegated to the specified zone.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fB-l\fR \fIlink\fR, \fB--link\fR=\fIlink\fR\fR
.ad
.sp .6
@@ -136,8 +147,8 @@ values. Flow properties are documented in the "Flow Properties" section, below.
.sp
.ne 2
.na
-\fB\fBflowadm remove-flow\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] \fB-l\fR
-{\fIlink\fR | \fIflow\fR}\fR
+\fB\fBflowadm remove-flow\fR [\fB-t\fR] [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzonename\fR]
+\fB-l\fR {\fIlink\fR | \fIflow\fR}\fR
.ad
.sp .6
.RS 4n
@@ -175,13 +186,23 @@ If a link is specified, remove all flows from that link. If a single flow is
specified, remove only that flow.
.RE
+.sp
+.ne 2
+.na
+\fB\fB-z\fR \fIzonename\fR
+.ad
+.sp .6
+.RS 4n
+Operate on a link that has been delegated to the specified zone.
+.RE
+
.RE
.sp
.ne 2
.na
\fB\fBflowadm show-flow\fR [\fB-pP\fR] [\fB-s\fR [\fB-i\fR \fIinterval\fR]]
-[\fB-o\fR \fIfield\fR[,...]] [\fB-l\fR \fIlink\fR] [\fIflow\fR]\fR
+[\fB-o\fR \fIfield\fR[,...]] [\fB-l\fR \fIlink\fR] [\fB-z\fR \fIzonename\fR] [\fIflow\fR]\fR
.ad
.sp .6
.RS 4n
@@ -220,6 +241,16 @@ The name of the link the flow is on.
.sp
.ne 2
.na
+\fB\fB-z\fR \fIzonename\fR
+.ad
+.sp .6
+.RS 4n
+Operate on a link that has been delegated to the specified zone.
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBipaddr\fR\fR
.ad
.sp .6
diff --git a/usr/src/man/man8/prstat.8 b/usr/src/man/man8/prstat.8
index bb1875f144..9eecb2febd 100644
--- a/usr/src/man/man8/prstat.8
+++ b/usr/src/man/man8/prstat.8
@@ -14,7 +14,7 @@ prstat \- report active process statistics
\fBprstat\fR [\fB-acHJLmRrtTvWZ\fR] [\fB-d\fR u | d] [\fB-C\fR \fIpsrsetlist\fR] [\fB-h\fR \fIlgrplist\fR]
[\fB-j\fR \fIprojlist\fR] [\fB-k\fR \fItasklist\fR] [\fB-n\fR \fIntop\fR[,\fInbottom\fR]]
[\fB-p\fR \fIpidlist\fR] [\fB-P\fR \fIcpulist\fR] [\fB-s\fR \fIkey\fR | \fB-S\fR \fIkey\fR ]
- [\fB-u\fR \fIeuidlist\fR] [\fB-U\fR \fIuidlist\fR] [\fB-z\fR \fIzoneidlist\fR]
+ [\fB-u\fR \fIeuidlist\fR] [\fB-U\fR \fIuidlist\fR] [\fB-z\fR \fIzoneidlist\fR] [\fB-Z\fR]
[\fIinterval\fR [\fIcount\fR]]
.fi
@@ -446,9 +446,11 @@ devices, in kilobytes (\fBK\fR), megabytes (\fBM\fR), or gigabytes (\fBG\fR).
.RS 4n
The resident set size of the process (\fBRSS\fR), in kilobytes (\fBK\fR),
megabytes (\fBM\fR), or gigabytes (\fBG\fR). The RSS value is an estimate
-provided by \fBproc\fR(5) that might underestimate the actual resident set
-size. Users who want to get more accurate usage information for capacity
-planning should use the \fB-x\fR option to \fBpmap\fR(1) instead.
+provided by \fBproc\fR(5) that might underestimate the actual
+per-process resident set size, but is generally accurate for the aggregated
+resident set size. Users who want to get more accurate usage information for
+capacity planning should use the \fB-x\fR option to \fBpmap\fR(1) for
+per-process results instead.
.RE
.sp
diff --git a/usr/src/man/man8/reboot.8 b/usr/src/man/man8/reboot.8
index 8115fe5adc..88f46c2db8 100644
--- a/usr/src/man/man8/reboot.8
+++ b/usr/src/man/man8/reboot.8
@@ -139,8 +139,7 @@ This option is currently available only on x86 systems. The \fB-p\fR and
.ad
.sp .6
.RS 4n
-Quick. Reboot quickly and ungracefully, without shutting down running processes
-first.
+Quick. Reboot quickly without halting running zones first.
.RE
.SH OPERANDS
diff --git a/usr/src/man/man8/route.8 b/usr/src/man/man8/route.8
index 3c60c4a0d6..41c0595c0d 100644
--- a/usr/src/man/man8/route.8
+++ b/usr/src/man/man8/route.8
@@ -9,34 +9,34 @@
route \- manually manipulate the routing tables
.SH SYNOPSIS
.nf
-\fBroute\fR [\fB-fnvq\fR] \fIsub-command\fR [ [\fImodifiers\fR] \fIargs\fR]
+\fBroute\fR [\fB-fnvq\fR] [\fB-z\fR \fIzone\fR] \fIsub-command\fR [ [\fImodifiers\fR] \fIargs\fR]
.fi
.LP
.nf
-\fBroute\fR [\fB-fnvq\fR] [\fB-p\fR [\fB-R\fR \fIroot-dir\fR]] add | delete [\fImodifiers\fR] \fIdestination\fR \fIgateway\fR
+\fBroute\fR [\fB-fnvq\fR] [\fB-z\fR \fIzone\fR] [\fB-p\fR [\fB-R\fR \fIroot-dir\fR]] add | delete [\fImodifiers\fR] \fIdestination\fR \fIgateway\fR
[\fIargs\fR]
.fi
.LP
.nf
-\fBroute\fR [\fB-fnvq\fR] change | get [\fImodifiers\fR] \fIdestination\fR
+\fBroute\fR [\fB-fnvq\fR] [\fB-z\fR \fIzone\fR] change | get [\fImodifiers\fR] \fIdestination\fR
[\fIgateway\fR [\fIargs\fR]]
.fi
.LP
.nf
-\fBroute\fR [\fB-fn\fR] monitor [\fImodifiers\fR]
+\fBroute\fR [\fB-fn\fR] [\fB-z\fR \fIzone\fR] monitor [\fImodifiers\fR]
.fi
.LP
.nf
-\fBroute\fR [\fB-fnvq\fR] flush [\fImodifiers\fR]
+\fBroute\fR [\fB-fnvq\fR] [\fB-z\fR \fIzone\fR] flush [\fImodifiers\fR]
.fi
.LP
.nf
-\fBroute\fR \fB-p\fR [\fB-R\fR \fIroot-dir\fR] show
+\fBroute\fR \fB-p\fR [\fB-R\fR \fIroot-dir\fR] [\fB-z\fR \fIzone\fR] show
.fi
.SH DESCRIPTION
@@ -127,6 +127,16 @@ addition, certain checks, such as the existence of network interfaces used with
Print additional details in verbose mode.
.RE
+.sp
+.ne 2
+.na
+\fB\fB-z\fR \fIzone\fR\fR
+.ad
+.RS 15n
+Apply commands to the zone \fIzone\fR. The zone must be running and must not
+be a shared-\fBIP\fR zone.
+.RE
+
.SS "Subcommands"
The following subcommands are supported:
.sp
diff --git a/usr/src/man/man8/savecore.8 b/usr/src/man/man8/savecore.8
index a8ce5071e5..099d3671e4 100644
--- a/usr/src/man/man8/savecore.8
+++ b/usr/src/man/man8/savecore.8
@@ -3,13 +3,13 @@
.\" Copyright (c) 1983 Regents of the University of California. All rights reserved. The Berkeley software License Agreement specifies the terms and conditions for redistribution.
.\" Copyright 2013 Nexenta Systems, Inc. All Rights Reserved.
.\" Copyright 2019 Joyent, Inc.
-.TH SAVECORE 8 "February 22, 2019"
+.TH SAVECORE 8 "Jun 15, 2019"
.SH NAME
savecore \- save a crash dump of the operating system
.SH SYNOPSIS
.LP
.nf
-\fB/usr/bin/savecore\fR [\fB-L\fR | \fB-r\fR] [\fB-vd\fR] [\fB-f\fR \fIdumpfile\fR] [\fIdirectory\fR]
+\fB/usr/bin/savecore\fR [\fB-L\fR | \fB-r\fR] [\fB-vd\fR] [\fB-k\fR \fIkeyfile\fR] [\fB-f\fR \fIdumpfile\fR] [\fIdirectory\fR]
.fi
.SH DESCRIPTION
@@ -61,10 +61,21 @@ the dump has already been saved.
\fB\fB-f\fR \fIdumpfile\fR\fR
.ad
.RS 15n
-Attempt to save a crash dump from the specified file instead of from the
-system's current dump device. This option may be useful if the information
-stored on the dump device has been copied to an on-disk file by means of the
-\fBdd\fR(8) command.
+Uncompress and save a crash dump and kernel namelist data from the specified
+compressed dump file.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-k\fR \fIkeyfile\fR\fR
+.ad
+.RS 15n
+Specifies that the dump should be decrypted based on the key found
+in \fIkeyfile\fR. Encrypted dumps are always decrypted before being stored
+in the file system, and must always be stored compressed. (That is,
+decompression can only occur on a decrypted dump.) The key must match the
+key specified when dump encryption is enabled via \fBdumpadm\fR.
.RE
.sp
@@ -73,7 +84,7 @@ stored on the dump device has been copied to an on-disk file by means of the
\fB\fB-L\fR\fR
.ad
.RS 15n
-Save a crash dump of the live running Solaris system, without actually
+Save a crash dump of the live running system, without actually
rebooting or altering the system in any way. This option forces \fBsavecore\fR
to save a live snapshot of the system to the dump device, and then immediately
to retrieve the data and to write it out to a new set of crash dump files in
@@ -183,7 +194,6 @@ default crash dump directory
.BR syslog (3C),
.BR attributes (7),
.BR smf (7),
-.BR dd (8),
.BR dumpadm (8),
.BR svcadm (8)
.SH NOTES
diff --git a/usr/src/man/man8/smbios.8 b/usr/src/man/man8/smbios.8
index 565a12018a..122527bc35 100644
--- a/usr/src/man/man8/smbios.8
+++ b/usr/src/man/man8/smbios.8
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (c) 2005, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright 2018 Joyent, Inc.
.\" 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]
-.TH SMBIOS 8 "Aug 31, 2005"
+.TH SMBIOS 8 "March 29, 2018"
.SH NAME
smbios \- display the contents of a System Management BIOS image
.SH SYNOPSIS
@@ -13,7 +14,6 @@ smbios \- display the contents of a System Management BIOS image
.fi
.SH DESCRIPTION
-.sp
.LP
The \fBsmbios\fR utility displays the contents of the System Management BIOS
(SMBIOS) image exported by the current system or stored in a file. SMBIOS is an
@@ -37,7 +37,6 @@ applied to the resulting file to display its content.
readable fashion. If \fBsmbios\fR does not recognize a structure's type or
content, the raw hexadecimal data for the structure is displayed.
.SH OPTIONS
-.sp
.LP
The following options are supported:
.sp
@@ -131,7 +130,6 @@ human-readable output for the selected structures.
.RE
.SH OPERANDS
-.sp
.LP
The following operands are supported:
.sp
@@ -145,7 +143,6 @@ SMBIOS image.
.RE
.SH EXIT STATUS
-.sp
.LP
The following exit values are returned:
.sp
@@ -178,7 +175,6 @@ Invalid command-line options were specified.
.RE
.SH FILES
-.sp
.ne 2
.na
\fB\fB/dev/smbios\fR \fR
@@ -189,7 +185,6 @@ snapshot of the current system SMBIOS image.
.RE
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(7) for descriptions of the following attributes:
.sp
@@ -208,7 +203,6 @@ Interface Stability See below.
.LP
The command-line options are Evolving. The human-readable output is Unstable.
.SH SEE ALSO
-.sp
.LP
.BR smbios (4D),
.BR attributes (7),
@@ -217,7 +211,6 @@ The command-line options are Evolving. The human-readable output is Unstable.
.LP
\fISystem Management BIOS Reference Specification\fR (see http://www.dmtf.org)
.SH NOTES
-.sp
.LP
The implementation of a System Management BIOS image is entirely at the
discretion of the system and BIOS vendors. Not all systems export an SMBIOS.
@@ -225,3 +218,8 @@ The SMBIOS structure content varies widely between systems and BIOS vendors and
frequently does not comply with the guidelines included in the specification.
Some structure fields might not be filled in by the BIOS at all, and others
might be filled inwith non-conforming values.
+.sp
+.LP
+This utility incorrectly interprets the first three fields of the system
+information UUID field as network-endian; the SMBIOS specification defines them
+as little-endian. The "UUID (Endian-corrected)" field has the correct value.
diff --git a/usr/src/man/man8/snoop.8 b/usr/src/man/man8/snoop.8
index 433353a3fc..5593bcc480 100644
--- a/usr/src/man/man8/snoop.8
+++ b/usr/src/man/man8/snoop.8
@@ -1,6 +1,7 @@
'\" te
.\" Copyright 2021 Joyent, Inc.
.\" Copyright (C) 2009, Sun Microsystems, Inc. All Rights Reserved
+.\" Copyright (c) 2014, Joyent, Inc. All rights reserved.
.\" 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]
@@ -12,7 +13,7 @@ snoop \- capture and inspect network packets
\fBsnoop\fR [\fB-afqrCDINPSvV\fR] [\fB-t\fR [r | a | d]] [\fB-c\fR \fImaxcount\fR]
[\fB-d\fR \fIdevice\fR] [\fB-i\fR \fIfilename\fR] [\fB-n\fR \fIfilename\fR] [\fB-o\fR \fIfilename\fR]
[\fB-p\fR \fIfirst\fR [, \fIlast\fR]] [\fB-s\fR \fIsnaplen\fR] [\fB-x\fR \fIoffset\fR [, \fIlength\fR]]
- [\fIexpression\fR]
+ [\fB-z\fR \fIzonename\fR] [\fIexpression\fR]
.fi
.SH DESCRIPTION
@@ -307,6 +308,22 @@ the whole packet, use an \fIoffset\fR of 0. If a \fIlength\fR value is not
provided, the rest of the packet is displayed.
.RE
+.sp
+.ne 2
+.na
+.BI -z zonename
+.ad
+.sp .6
+.RS 4n
+Open an earlier datalink specified via
+.B -d
+or
+.B -I
+in the specified zone \fIzonename\fR.
+This option is only meaningful in the global zone and
+allows the global zone to inspect datalinks of non-global zones.
+.RE
+
.SH OPERANDS
.ne 2
.na
diff --git a/usr/src/man/man8/svc.startd.8 b/usr/src/man/man8/svc.startd.8
index 7467e05035..2fcf917fde 100644
--- a/usr/src/man/man8/svc.startd.8
+++ b/usr/src/man/man8/svc.startd.8
@@ -361,10 +361,13 @@ properties listed below in the \fBstartd\fR property group.
.RS 4n
The \fBcritical_failure_count\fR and \fBcritical_failure_period\fR properties
together specify the maximum number of service failures allowed in a given
-time interval before \fBsvc.startd\fR transitions the service to maintenance.
+number of seconds before \fBsvc.startd\fR transitions the service to
+maintenance.
If the number of failures exceeds \fBcritical_failure_count\fR in any period of
\fBcritical_failure_period\fR seconds, \fBsvc.startd\fR will transition the
-service to maintenance.
+service to maintenance. The \fBcritical_failure_count\fR value is limited
+to the range 1-10 and defaults to 10. The \fBcritical_failure_period\fR
+defaults to 600 seconds.
.RE
.sp
diff --git a/usr/src/man/man8/tunefs.8 b/usr/src/man/man8/tunefs.8
index ea24a86629..16b04b85fa 100644
--- a/usr/src/man/man8/tunefs.8
+++ b/usr/src/man/man8/tunefs.8
@@ -3,7 +3,7 @@
.\" 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]
-.TH TUNEFS 8 "Dec 5, 2003"
+.TH TUNEFS 8 "Sep 19, 2013"
.SH NAME
tunefs \- tune an existing UFS file system
.SH SYNOPSIS
@@ -123,3 +123,8 @@ encountering files greater than or equal to 2 Gbyte ( 2^31 bytes).
.BR largefile (7),
.BR mkfs_ufs (8),
.BR newfs (8)
+.\" Take this out and a Unix Demon will dog your steps from now until
+.\" the time_t's wrap around.
+.SH BUGS
+.sp
+You can tune a file system, but you can't tune a fish.
diff --git a/usr/src/man/man8/vfsstat.8 b/usr/src/man/man8/vfsstat.8
new file mode 100644
index 0000000000..0a4c3a1a9e
--- /dev/null
+++ b/usr/src/man/man8/vfsstat.8
@@ -0,0 +1,213 @@
+'\" te
+.\"
+.\" 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 2014 Joyent, Inc. All rights reserved.
+.\"
+.TH "VFSSTAT" "8" "May 1, 2014" "" ""
+.
+.SH "NAME"
+\fBvfsstat\fR \-\- Report VFS read and write activity
+.
+.SH "SYNOPSIS"
+.
+.nf
+vfsstat [\-hIMrzZ] [interval [count]]
+.
+.fi
+.
+.SH "DESCRIPTION"
+The vfsstat utility reports a summary of VFS read and write
+activity per zone\. It first prints all activity since boot, then
+reports activity over a specified interval\.
+.
+.P
+When run from a non\-global zone (NGZ), only activity from that NGZ
+can be observed\. When run from a the global zone (GZ), activity
+from the GZ and all other NGZs can be observed\.
+.
+.P
+This tool is convenient for examining I/O performance as
+experienced by a particular zone or application\. Other tools
+which examine solely disk I/O do not report reads and writes which
+may use the filesystem\'s cache\. Since all read and write system
+calls pass through the VFS layer, even those which are satisfied
+by the filesystem cache, this tool is a useful starting point when
+looking at a potential I/O performance problem\. The vfsstat
+command reports the most accurate reading of I/O performance as
+experienced by an application or zone\.
+.
+.P
+One additional feature is that ZFS zvol performance is also reported
+by this tool, even though zvol I/O does not go through the VFS
+layer\. This is done so that this single tool can be used to monitor
+I/O performance and because its not unreasonable to think of zvols
+as being included along with other ZFS filesystems\.
+.
+.P
+The calculations and output fields emulate those from iostat(8)
+as closely as possible\. When only one zone is actively performing
+disk I/O, the results from iostat(8) in the global zone and
+vfsstat in the local zone should be almost identical\. Note that
+many VFS read operations may be handled by the filesystem cache,
+so vfsstat and iostat(8) will be similar only when most
+operations require a disk access\.
+.
+.P
+As with iostat(8), a result of 100% for VFS read and write
+utilization does not mean that the VFS layer is fully saturated\.
+Instead, that measurement just shows that at least one operation
+was pending over the last interval 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\.
+.
+.SH "OUTPUT"
+The vfsstat utility reports the following information:
+.
+.IP "" 4
+.
+.nf
+r/s
+.RS
+reads per second
+.RE
+
+w/s
+.RS
+writes per second
+.RE
+
+kr/s
+.RS
+kilobytes read per second
+.RE
+
+kw/s
+.RS
+kilobytes written per second
+.RE
+
+ractv
+.RS
+average number of read operations actively being serviced by the VFS layer
+.RE
+
+wactv
+.RS
+average number of write operations actively being serviced by the VFS layer
+.RE
+
+read_t
+.RS
+average VFS read latency, in microseconds
+.RE
+
+writ_t
+.RS
+average VFS write latency, in microseconds
+.RE
+
+%r
+.RS
+percent of time there is a VFS read operation pending
+.RE
+
+%w
+.RS
+percent of time there is a VFS write operation pending
+.RE
+
+d/s
+.RS
+VFS operations per second delayed by the ZFS I/O throttle
+.RE
+
+del_t
+.RS
+average ZFS I/O throttle delay, in microseconds
+.RE
+.
+.fi
+.
+.IP "" 0
+.
+.SH "OPTIONS"
+The following options are supported:
+.
+.P
+\-h
+.RS
+Show help message and exit
+.RE
+.
+.P
+\-I
+.RS
+Print results per interval, rather than per second (where applicable)
+.RE
+.
+.P
+\-M
+.RS
+Print results in MB/s instead of KB/s
+.RE
+.
+.P
+\-r
+.RS
+Show results in a comma\-separated format
+.RE
+.
+.P
+\-z
+.RS
+Hide zones with no VFS activity
+.RE
+.
+.P
+\-Z
+.RS
+Print results for all zones, not just the current zone
+.RE
+.
+.SH "OPERANDS"
+interval
+.
+.P
+Specifies the length in seconds to pause between each interval
+report\. If not specified, vfsstat will print a summary since boot
+and exit\.
+.
+.P
+count
+.
+.P
+Specifies the number of intervals to report\. Defaults to
+unlimited if not specified\.
+.
+.SH "SEE ALSO"
+.
+.nf
+iostat(8), ziostat(8), mpstat(8)
+.
+.fi
+.
+.SH "NOTES"
+This command does not examine readdir or any other VFS operations,
+only read and write operations\.
+.
+.P
+This command does not look at network I/O, only I/O operations to
+or from a file\.
+.
+.P
+The output format from vfsstat may change over time; use the
+comma\-separated output for a stable output format\.
diff --git a/usr/src/man/man8/vndadm.8 b/usr/src/man/man8/vndadm.8
new file mode 100644
index 0000000000..8bb79bb81a
--- /dev/null
+++ b/usr/src/man/man8/vndadm.8
@@ -0,0 +1,650 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VNDADM 8 "Mar 06, 2014"
+.SH NAME
+vndadm \- administer vnd devices
+
+.SH SYNOPSIS
+
+.nf
+vndadm create [-z zonename] [-l datalink] device
+vndadm destroy [-z zonename] device...
+vndadm list [-p] [-d delim] [-o field,...] [-z zonename] [device]...
+vndadm get [-p] [-d delim] [-z zonename] device [prop]...
+vndadm set [-z zonename] device prop=val...
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The vndadm command is used to administer vnd devices. A vnd device is
+similar to an IP network interface, except that the vnd device operates
+at layer two. A vnd device is created over a data link (see dladm(8))
+and its address is that of the underlying data link. For ethernet based
+devices, that address would be the MAC address of the data link. vnd
+devices are character devices which may be used to send and receive
+layer two packets. When reading or writing to a vnd device, the full
+frame must be present. This is useful for working with virtual machines,
+or other environments where you need to manipulate the entire layer two
+frame.
+
+.sp
+.LP
+Every command takes a device as an argument. To specify a vnd device,
+you just use the name of the device. Devices are scoped to zones. If no
+zone is specified, the current zone is assumed. A device name can be any
+series of alphanumeric ascii characters which typically match the name
+of the underlying data link. A given vnd device name must be unique in a
+given zone, but the same name can be used across zones.
+.sp
+.SH OPTIONS
+.sp
+.LP
+All vndadm subcommands have the following common option:
+.sp
+.ne 2
+.na
+-z zonename
+.ad
+.sp .6
+.RS 4n
+Operate in the context of the specified zone. When creating a vnd
+device, the named device is created in the specified zone. All other
+operations scope the device lookup to the specified zone. If the user is
+not in the global zone, the use of -z will not work.
+
+.sp
+.LP
+When -z is used and multiple devices are specified, then
+the use of -z applies to all of the devices.
+.RE
+
+.SH SUBCOMMANDS
+.sp
+.ne 2
+.na
+vndadm create [-z zonename] [-l datalink] device
+.ad
+.sp
+.RS 4n
+Creates a vnd device with the specified name device. If -l datalink is
+not specified, it is assumed that the data link and the device share the
+same name. The created device will exist for as long as the zone exists
+or until a call to vndadm destroy. vnd devices do not persist across
+system reboots. Note, if an IP interface or another libdlpi(3LIB)
+consumer is already using the data link, then vnd will fail.
+
+.sp
+The maximum length of the name of device is 31 characters. The allowed
+set of characters is alphanumberic characters, ':', \'-', and \'_'. The
+names 'zone' and 'ctl' are reserved and may not be used.
+
+.sp
+.ne 2
+.na
+-l datalink
+.ad
+.sp .6
+.RS 4n
+Specifies the name of the data link to create the device over. This
+allows the vnd device name to be different from the data link's name.
+.RE
+.sp
+.ne 2
+.na
+-z zonename
+.ad
+.sp .6
+.RS 4n
+See OPTIONS above.
+.RE
+
+.RE
+
+.sp
+.ne 2
+.na
+vndadm destroy [-z zonename] device...
+.ad
+.sp
+.RS 4n
+Destroys the specified device. The destruction is analogous to
+unlink(2). If the device is still open and used by applications, the
+device will continue to exist, but it will no longer be accessible by
+the name device.
+.sp
+.ne 2
+.na
+-z zonename
+.ad
+.sp .6
+.RS 4n
+See OPTIONS above.
+.RE
+.RE
+
+.sp
+.ne 2
+.na
+vndadm list [-p] [-d delim] [-o field,...] [-z zonename] [device]...
+.ad
+.sp
+.RS 4n
+Lists active vnd devices. By default, vnadm list lists all devices in
+every zone that the caller is allowed to see; the current zone if in the
+non-global zone, and all zones in the global zone. If device is
+specified one or more times, then output will be limited to the
+specified devices.
+.sp
+.ne 2
+.na
+-o field[,...]
+.ad
+.sp .6
+.RS 4n
+A case-insensitive, comma-separated list of output fields. When -o is
+not used, all of the fields listed below are shown. The field name must
+be one of the following fields:
+
+.sp
+.ne 2
+.na
+NAME
+.ad
+.sp .6
+.RS 4n
+The name of the vnd device.
+.RE
+
+.sp
+.ne 2
+.na
+DATALINK
+.ad
+.sp .6
+.RS 4n
+The name of the data link the vnd device was created over.
+.RE
+
+.sp
+.ne 2
+.na
+ZONENAME
+.ad
+.sp .6
+.RS 4n
+The name of the zone that the vnd device exists in.
+.RE
+.RE
+
+.sp
+.ne 2
+.na
+-p
+.ad
+.sp .6
+.RS 4n
+Display the output in a stable machine parseable format. The -o option
+is required with the -p option. See "Parseable Output Format" below.
+.RE
+
+.sp
+.ne 2
+.na
+-d delim
+.ad
+.sp .6
+.RS 4n
+Change the delimiter used in conjunction with generating parseable
+output. This option may only be specified when -p is also specified.
+.RE
+
+.sp
+.ne 2
+.na
+-z zonename
+.ad
+.sp .6
+.RS 4n
+See OPTIONS above.
+.RE
+
+.RE
+
+
+.sp
+.ne 2
+.na
+vndadm get [-p] [-d delim] [-z zonename] device [prop]...
+.ad
+.sp
+.RS 4n
+Displays the properties for the specified device. By default, all
+properties of a given device are displayed. If prop is specified one or
+more times, then only the specified properties will be displayed for
+device. For a list of properties, see the section "Properties" below.
+The property output consists of the following four columns:
+.sp
+.ne 2
+.na
+LINK
+.ad
+.sp .6
+.RS 4n
+The name of the device
+.RE
+
+.sp
+.ne 2
+.na
+PROPERTY
+.ad
+.sp .6
+.RS 4n
+The name of the property. Note that some properties that are private to
+the implementation may be displayed. Those properties begin with a
+leading underscore.
+.RE
+
+.sp
+.ne 2
+.na
+PERM
+.ad
+.sp .6
+.RS 4n
+Describes whether the property is read-only or
+if it is read-write. This field does not
+indicate if the current user has permission, but
+lists permissions for a privileged user.
+.RE
+
+.sp
+.ne 2
+.na
+VALUE
+.ad
+.sp .6
+.RS 4n
+The value of the property.
+.RE
+
+.sp
+.ne 2
+.na
+-p
+.ad
+.sp .6
+.RS 4n
+Display the output in a stable machine parseable format. See "Parseable
+Output Format" below.
+.RE
+
+.sp
+.ne 2
+.na
+-d delim
+.ad
+.sp .6
+.RS 4n
+Change the delimiter used in conjunction with generating parseable
+output. This option may only be specified when -p is also specified.
+.RE
+
+.sp
+.ne 2
+.na
+-z zonename
+.ad
+.sp .6
+.RS 4n
+See OPTIONS above.
+.RE
+.RE
+
+.sp
+.ne 2
+.na
+vndadm set [-z zonename] device prop=val...
+.ad
+.sp
+.RS 4n
+Sets properties on the named device. Setting a property takes effect for
+all operations on the device, after the program returns. Multiple
+properties can be set at once; however, properties are applied one at a
+time to the device. Property names and values must be separated with an
+equals sign. Additional property and value pairs should be separated by
+white space. For a list of properties, see the section "Properties"
+below.
+
+.sp
+.ne 2
+.na
+-z zonename
+.ad
+.sp .6
+.RS 4n
+See OPTIONS above.
+.RE
+.RE
+
+.SS Parseable Output Format
+.sp
+.LP
+The default output for parseable data is to be separated with a single
+ascii space character. The delimiter may be changed with the -d
+option. When parseable output is requested, no numbers that represent
+sizes will be displayed in human readable form, they will be fully
+expanded. eg. the number 42K will instead be 43008.
+
+.SS Properties
+.sp
+.LP
+The following are supported and stable properties. Note that any
+properties that starts with a leading underscore are not a stable
+property and may be removed at any time.
+
+.sp
+.ne 2
+.na
+rxbuf
+.ad
+.sp .6
+.RS 4n
+A read/write property that controls the size of the receive buffer for
+the device. All received data enters the receive buffer until a consumer
+consumes it. If adding a received frame would exceed the size of the
+receive buffer, then that frame will be dropped. The maximum size of the
+buffer is limited by the 'maxsize' property. The minimum size of the
+buffer is the value of the 'maxtu' property. The property's value may be
+anything between that maximum and minimum. When setting this property,
+standard size suffixes such as 'K' and 'M' may be used.
+.RE
+
+.sp
+.ne 2
+.na
+txbuf
+.ad
+.sp .6
+.RS 4n
+A read/write property that controls the size of the transmit buffer. All
+in-flight transmitted data must be able to fit into the transmit buffer
+to account for potential flow control events. If there is not enough
+space in the transmit buffer, transmit related I/O operations will
+either block or fail based on whether the file has been put into
+non-blocking mode by setting O_NONBLOCK or O_NDELAY with fcntl(2). The
+maximum size of the buffer is limited by the 'maxsize' property. The
+minimum size of the buffer is the value of the 'maxtu' property. The
+property's value may be anything between that maximum and minimum. When
+setting this property, standard size suffixes such as 'K' and 'M' may be
+used.
+
+.RE
+
+.sp
+.ne 2
+.na
+maxsize
+.ad
+.sp .6
+.RS 4n
+A read-only property that describes the maximum size of buffers in the
+system. Properties such as rxbuf and txbuf cannot be set beyond this.
+.RE
+
+.sp
+.ne 2
+.na
+mintu
+.ad
+.sp .6
+.RS 4n
+A read-only property that describes the minimum size of a frame
+transmitted to the underlying data link. Note that the minimum listed
+here may be less than the size of a valid layer two frame and therefore
+may be dropped. A frame smaller than this value will be rejected by vnd.
+.RE
+
+.sp
+.ne 2
+.na
+maxtu
+.ad
+.sp .6
+.RS 4n
+A read-only property that describes the maximum size of a frame
+transmitted to the underlying data link. A frame larger than this value
+will be rejected by vnd.
+.RE
+
+.SH EXAMPLES
+.LP
+Example 1 Creating a vnd device
+.sp
+.LP
+To create a vnd device over the VNIC named net0, enter the following
+command:
+
+.sp
+.in +2
+.nf
+# vndadm create net0
+.fi
+.in -2
+.sp
+
+.LP
+Example 2 Creating a vnd device in another zone
+.sp
+.LP
+
+To create a vnd device over the VNIC named net1 in the zone
+1b7155a4-aef9-e7f0-d33c-9705e4b8b525, enter the following command:
+
+.sp
+.in +2
+.nf
+# vndadm create -z 1b7155a4-aef9-e7f0-d33c-9705e4b8b525 net1
+.fi
+.in -2
+.sp
+
+.LP
+Example 3 Destroying a vnd device
+.sp
+.LP
+
+To destroy the vnd device named net0, enter the following command:
+
+.sp
+.in +2
+.nf
+# vndadm destroy net0
+.fi
+.in -2
+.sp
+
+.LP
+Example 4 Destroying a vnd device in another zone
+.sp
+.LP
+
+To destroy the vnd device named net1 in the zone
+1b7155a4-aef9-e7f0-d33c-9705e4b8b525, enter the following command:
+
+.sp
+.in +2
+.nf
+# vndadm destroy -z 1b7155a4-aef9-e7f0-d33c-9705e4b8b525 net1
+.fi
+.in -2
+.sp
+
+.LP
+Example 5 List all vnd devices
+.sp
+.LP
+
+To list all devices, run the following command:
+
+.sp
+.in +2
+.nf
+# vndadm list
+NAME DATALINK ZONENAME
+net0 net0 global
+net0 net0 1b7155a4-aef9-e7f0-d33c-9705e4b8b525
+.fi
+.in -2
+.sp
+
+.LP
+Example 6 Listing devices in a specific zone
+.sp
+.LP
+
+To list devices in a specific zone, run the following command:
+
+.sp
+.in +2
+.nf
+# vndadm list -z 1b7155a4-aef9-e7f0-d33c-9705e4b8b525
+
+NAME DATALINK ZONENAME
+net0 net0 1b7155a4-aef9-e7f0-d33c-9705e4b8b525
+.fi
+.in -2
+.sp
+
+.LP
+Example 7 List all devices in a parseable format
+.sp
+.LP
+
+To list all devices in a parseable format with the delimiter of ':', run
+the following command:
+
+.sp
+.in +2
+.nf
+# vndadm list -p -d: -o name,datalink,zone
+net0:net0:global
+net0:net0:1b7155a4-aef9-e7f0-d33c-9705e4b8b525
+.fi
+.in -2
+.sp
+
+.LP
+Example 8 Retrieving all properties for a device
+.sp
+.LP
+
+To retrieve all of the properties for the vnd device foo0, run the
+following command:
+
+.sp
+.in +2
+.nf
+# vndadm get foo0
+LINK PROPERTY PERM VALUE
+foo0 rxbuf rw 65536
+foo0 txbuf rw 65536
+foo0 maxsize r- 4194304
+foo0 mintu r- 0
+foo0 maxtu r- 1518
+foo0 _nflush rw 10
+foo0 _burstsz rw 10
+.fi
+.in -2
+.sp
+
+.LP
+Example 9 Retrieving specific properties for a device
+.sp
+.LP
+
+To retrieve just the rxbuf and txbuf properties for the vnd device foo0,
+run the following command:
+
+.sp
+.in +2
+.nf
+# vndadm get foo0 rxbuf txbuf
+LINK PROPERTY PERM VALUE
+foo0 rxbuf rw 65536
+foo0 txbuf rw 65536
+.fi
+.in -2
+.sp
+
+.LP
+Example 10 Retrieving properties for a device in a parseable format
+.sp
+.LP
+
+To retrieve all properties for the vnd device foo0 in a parseable
+format, run the following command:
+
+.sp
+.in +2
+.nf
+# vndadm get -p foo0
+foo0 rxbuf rw 65536
+foo0 txbuf rw 65536
+foo0 maxsize r- 4194304
+foo0 mintu r- 0
+foo0 maxtu r- 1518
+foo0 _nflush rw 10
+foo0 _burstsz rw 10
+.fi
+.in -2
+.sp
+
+.LP
+Example 11 Setting a property on a device
+.sp
+.LP
+
+To set the receive buffer size to one megabyte on the device foo0, run
+the following command:
+
+.sp
+.in +2
+.nf
+# vndadm set foo0 rxbuf=1M
+.fi
+.in -2
+.sp
+
+.LP
+Example 12 Setting multiple properties on a device
+.sp
+.LP
+
+To set the transmit buffer to 300 Kb and the receive buffer to 1 Mb, run
+the following command:
+
+.sp
+.in +2
+.nf
+# vndadm set foo0 rxbuf=300K txbuf=1M
+.fi
+.in -2
+.sp
+
+.SH SEE ALSO
+
+fcntl(2), fcntl.h(3HEAD), libvnd(3LIB), vnd(7D), dladm(8), ipadm(8), vndstat(8)
diff --git a/usr/src/man/man8/vndstat.8 b/usr/src/man/man8/vndstat.8
new file mode 100644
index 0000000000..826ab8cd97
--- /dev/null
+++ b/usr/src/man/man8/vndstat.8
@@ -0,0 +1,163 @@
+'\" te
+.\"
+.\" 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.
+.\"
+.TH VNDSTAT 8 "Mar 06, 2014"
+.SH NAME
+vndstat \- report vnd activity
+
+.SH SYNOPSIS
+
+vndstat [interval [count]]
+
+.SH DESCRIPTION
+.sp
+.LP
+The vndstat command reports a summary of per-device vnd
+activity. Once per interval it prints a table of statistics per
+device. In the global zone, vndstat reports on all devices in the
+system. From the non-global zone, it only reports on devices that are
+present in that zone. vndstat reports on all vnd devices
+that exist, including anonymous devices which are not linked into the
+file system.
+.sp
+.LP
+The vndstat command's output includes the following information:
+.sp
+.ne 2
+.na
+.B name
+.ad
+.RS 14n
+The name of the device, if bound. If a given vnd device is not
+bound into the file system, hence considered anonymous, then there will
+be no name for the device.
+.RE
+
+.sp
+.ne 2
+.na
+.B rx B/s
+.ad
+.RS 14n
+The number of bytes received by the device during interval.
+.RE
+
+.sp
+.ne 2
+.na
+.B tx B/s
+.ad
+.RS 14n
+The number of bytes transmitted by the device during interval.
+.RE
+
+.sp
+.ne 2
+.na
+.B drops
+.ad
+.RS 14n
+The number of packets and messages which have been dropped. This
+includes all drops due to insufficient buffer space, IP hooks, and
+unknown or malformed DLPI messages.
+.RE
+
+.sp
+.ne 2
+.na
+.B txfc
+.ad
+.RS 14n
+The number of flow control events that have occurred. A flow control
+event occurs when the layers below vnd request that all transmits
+be paused until a future call resumes the flow. This statistic is
+incremented when the flow is resumed. It is not incremented when it is
+first paused.
+.RE
+
+.sp
+.ne 2
+.na
+.B zone
+.ad
+.RS 14n
+The name of the zone the device is located in.
+.RE
+
+.SH OPTIONS
+
+.sp
+.ne 2
+.na
+interval
+.ad
+.RS 13n
+Report once each interval seconds. interval may not be
+fractional.
+.RE
+
+.sp
+.ne 2
+.na
+count
+.ad
+.RS 13n
+Only print count reports, then exit.
+.RE
+.sp
+.LP
+When no arguments are given to vndstat, it will always print at an
+interval of one second. Reports will continue until vndstat
+is terminated.
+
+.SH EXAMPLES
+.LP
+Example 1 Print five seconds of data
+
+.sp
+.in +2
+.nf
+example% vndstat 1 5
+ name | rx B/s | tx B/s | drops txfc | zone
+ net0 | 1.45MB/s | 14.1KB/s | 0 0 | 1b7155a4-aef9-e7f0-d33c-9705e4b8b525
+ net0 | 3.50MB/s | 19.5KB/s | 0 0 | 1b7155a4-aef9-e7f0-d33c-9705e4b8b525
+ net0 | 2.83MB/s | 30.8KB/s | 0 0 | 1b7155a4-aef9-e7f0-d33c-9705e4b8b525
+ net0 | 3.08MB/s | 30.6KB/s | 0 0 | 1b7155a4-aef9-e7f0-d33c-9705e4b8b525
+ net0 | 3.21MB/s | 30.6KB/s | 0 0 | 1b7155a4-aef9-e7f0-d33c-9705e4b8b525
+.fi
+.in -2
+.sp
+
+.SH ATTRIBUTES
+.sp
+.LP
+See attributes(7) for descriptions of the following attributes:
+.sp
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE ATTRIBUTE VALUE
+_
+Interface Stability See below.
+.TE
+
+.sp
+.LP
+Invocation is evolving. Human readable output is unstable.
+.SH SEE ALSO
+
+vnd(4M), dlstat(8), nicstat(8), vndadm(8)
diff --git a/usr/src/man/man8/zfs.8 b/usr/src/man/man8/zfs.8
index 316082a0cd..dd357ee5ac 100644
--- a/usr/src/man/man8/zfs.8
+++ b/usr/src/man/man8/zfs.8
@@ -29,7 +29,7 @@
.\" Copyright 2019 Joyent, Inc.
.\" Copyright (c) 2018 Datto Inc.
.\"
-.Dd February 26, 2019
+.Dd Jul 22, 2019
.Dt ZFS 8
.Os
.Sh NAME
@@ -40,12 +40,12 @@
.Op Fl \&?
.Nm
.Cm create
-.Op Fl p
+.Op Fl Pnpv
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Ar filesystem
.Nm
.Cm create
-.Op Fl ps
+.Op Fl Pnpsv
.Op Fl b Ar blocksize
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Fl V Ar size Ar volume
@@ -2225,7 +2225,7 @@ Displays a help message.
.It Xo
.Nm
.Cm create
-.Op Fl p
+.Op Fl Pnpv
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Ar filesystem
.Xc
@@ -2254,6 +2254,48 @@ Any property specified on the command line using the
.Fl o
option is ignored.
If the target filesystem already exists, the operation completes successfully.
+.It Fl n
+Do a dry-run
+.Pq Qq No-op
+creation.
+No datasets will be created.
+This is useful in conjunction with the
+.Fl v
+or
+.Fl P
+flags to validate properties that are passed via
+.Fl o
+options and those implied by other options.
+The actual dataset creation can still fail due to insufficient privileges or
+available capacity.
+.It Fl P
+Print machine-parsable verbose information about the created dataset.
+Each line of output contains a key and one or two values, all separated by tabs.
+The
+.Sy create_ancestors
+and
+.Sy create
+keys have
+.Em filesystem
+as their only value.
+The
+.Sy create_ancestors
+key only appears if the
+.Fl p
+option is used.
+The
+.Sy property
+key has two values, a property name and that property's value.
+The
+.Sy property
+key may appear zero or more times, once for each property that will be set local
+to
+.Em filesystem
+due to the use of the
+.Fl o
+option.
+.It Fl v
+Print verbose information about the created dataset.
.El
.It Xo
.Nm
@@ -2310,6 +2352,52 @@ See
in the
.Sx Native Properties
section for more information about sparse volumes.
+.It Fl n
+Do a dry-run
+.Pq Qq No-op
+creation.
+No datasets will be created.
+This is useful in conjunction with the
+.Fl v
+or
+.Fl P
+flags to validate properties that are passed via
+.Fl o
+options and those implied by other options.
+The actual dataset creation can still fail due to insufficient privileges or
+available capacity.
+.It Fl P
+Print machine-parsable verbose information about the created dataset.
+Each line of output contains a key and one or two values, all separated by tabs.
+The
+.Sy create_ancestors
+and
+.Sy create
+keys have
+.Em volume
+as their only value.
+The
+.Sy create_ancestors
+key only appears if the
+.Fl p
+option is used.
+The
+.Sy property
+key has two values, a property name and that property's value.
+The
+.Sy property
+key may appear zero or more times, once for each property that will be set local
+to
+.Em volume
+due to the use of the
+.Fl b
+or
+.Fl o
+options, as well as
+.Sy refreservation
+if the volume is not sparse.
+.It Fl v
+Print verbose information about the created dataset.
.El
.It Xo
.Nm
diff --git a/usr/src/man/man8/zoneadm.8 b/usr/src/man/man8/zoneadm.8
index 645da71e4d..92feec2693 100644
--- a/usr/src/man/man8/zoneadm.8
+++ b/usr/src/man/man8/zoneadm.8
@@ -1,6 +1,7 @@
'\" te
.\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
.\" Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright (c) 2011 Joyent, Inc. All Rights Reserved.
.\" 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]
@@ -128,12 +129,14 @@ Use the following command to attach a zone:
.sp
.ne 2
.na
-\fB\fBboot\fR [\fB--\fR \fIboot_options\fR]\fR
+\fB\fBboot\fR [\fB-X\fR] [\fB--\fR \fIboot_options\fR]\fR
.ad
.sp .6
.RS 4n
Boot (or activate) the specified zones.
.sp
+The \fI-X\fR option enables debug for the zone's brand while booting.
+.sp
The following \fIboot_options\fR are supported:
.sp
.ne 2
@@ -248,12 +251,25 @@ The source zone must be halted before this subcommand can be used.
.sp
.ne 2
.na
-\fB\fBhalt\fR\fR
+\fB\fBhalt [\fB-X\fR]\fR\fR
.ad
.sp .6
.RS 4n
Halt the specified zones. \fBhalt\fR bypasses running the shutdown scripts
inside the zone. It also removes run time resources of the zone.
+.sp
+The \fI-X\fR option enables debug for the zone's brand while halting.
+.sp
+Use:
+.sp
+.in +2
+.nf
+zlogin \fIzone\fR shutdown
+.fi
+.in -2
+.sp
+
+to cleanly shutdown the zone by running the shutdown scripts.
.RE
.sp
@@ -413,24 +429,28 @@ and normal restrictions for \fIzonepath\fR apply.
.sp
.ne 2
.na
-\fB\fBready\fR\fR
+\fB\fBready [\fB-X\fR]\fR\fR
.ad
.sp .6
.RS 4n
Prepares a zone for running applications but does not start any user processes
in the zone.
+.sp
+The \fI-X\fR option enables debug for the zone's brand while readying.
.RE
.sp
.ne 2
.na
-\fB\fBreboot\fR\ [\fB--\fR \fIboot_options\fR]]\fR
+\fB\fBreboot\fR\ [\fB-X\fR] [\fB--\fR \fIboot_options\fR]]\fR
.ad
.sp .6
.RS 4n
Restart the zones. This is equivalent to a \fBhalt\fR \fBboot\fR sequence. This
subcommand fails if the specified zones are not active. See \fIboot\fR subcommand
for the boot options.
+.sp
+The \fI-X\fR option enables debug for the zone's brand while rebooting.
.RE
.sp
diff --git a/usr/src/man/man8/zonecfg.8 b/usr/src/man/man8/zonecfg.8
index 13f5a47b68..f5c245b77a 100644
--- a/usr/src/man/man8/zonecfg.8
+++ b/usr/src/man/man8/zonecfg.8
@@ -1,6 +1,6 @@
'\" te
.\" Copyright (c) 2004, 2009 Sun Microsystems, Inc. All Rights Reserved.
-.\" Copyright 2013 Joyent, Inc. All Rights Reserved.
+.\" Copyright 2015 Joyent, Inc.
.\" Copyright 2017 Peter Tribble
.\" Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
.\" 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.
@@ -11,17 +11,17 @@
zonecfg \- set up zone configuration
.SH SYNOPSIS
.nf
-\fBzonecfg\fR \fB-z\fR \fIzonename\fR
+\fBzonecfg\fR {\fB-z\fR \fIzonename\fR | \fB-u\fR \fIuuid\fR}
.fi
.LP
.nf
-\fBzonecfg\fR \fB-z\fR \fIzonename\fR \fIsubcommand\fR
+\fBzonecfg\fR {\fB-z\fR \fIzonename\fR | \fB-u\fR \fIuuid\fR} \fIsubcommand\fR
.fi
.LP
.nf
-\fBzonecfg\fR \fB-z\fR \fIzonename\fR \fB-f\fR \fIcommand_file\fR
+\fBzonecfg\fR {\fB-z\fR \fIzonename\fR | \fB-u\fR \fIuuid\fR} \fB-f\fR \fIcommand_file\fR
.fi
.LP
@@ -42,7 +42,8 @@ The following synopsis of the \fBzonecfg\fR command is for interactive usage:
.sp
.in +2
.nf
-zonecfg \fB-z\fR \fIzonename subcommand\fR
+{\fB-z\fR \fIzonename\fR | \fB-u\fR \fIuuid\fR}
+zonecfg {\fB-z\fR \fIzonename | \fB-u\fR \fIuuid} subcommand\fR
.fi
.in -2
.sp
@@ -362,6 +363,16 @@ The following properties are supported:
.sp
.ne 2
.na
+\fB(global)\fR
+.ad
+.sp .6
+.RS 4n
+\fBzfs-io-priority\fR
+.RE
+
+.sp
+.ne 2
+.na
\fB\fBfs\fR\fR
.ad
.sp .6
@@ -376,7 +387,7 @@ The following properties are supported:
.ad
.sp .6
.RS 4n
-\fBaddress\fR, \fBallowed-address\fR, \fBphysical\fR, \fBdefrouter\fR
+\fBaddress\fR, \fBallowed-address\fR, \fBdefrouter\fR, \fBglobal-nic\fR, \fBmac-addr\fR, \fBphysical\fR, \fBproperty\fR, \fBvlan-id\fR
.RE
.sp
@@ -659,7 +670,17 @@ Values needed to determine how, where, and so forth to mount file systems. See
.sp
.ne 2
.na
-\fB\fBnet\fR: address, allowed-address, physical, defrouter\fR
+\fB\fBinherit-pkg-dir\fR: dir\fR
+.ad
+.sp .6
+.RS 4n
+The directory path.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBnet\fR: address, allowed-address, defrouter, global-nic, mac-addr, physical, property, vlan-id\fR
.ad
.sp .6
.RS 4n
@@ -698,6 +719,10 @@ zone. However, if the interface is not used by the global zone, it should be
configured \fBdown\fR in the global zone, and the default router for the
interface should be specified here.
.sp
+The global-nic is used for exclusive stack zones which will use a VNIC on-demand. When the zone boots, a VNIC named using the physical property will be created on the global NIC. If provided, the mac-addr and vlan-id will be set on this VNIC.
+.sp
+The \fBproperty\fR setting is a resource which can be used to set arbitrary name/value pairs on the network. These name/value pairs are made available to the zone's brand, which can use them as needed to set up the network interface.
+.sp
For an exclusive-IP zone, the physical property must be set and the address and
default router properties cannot be set.
.sp
@@ -851,7 +876,7 @@ property is not specified, the scheduling class is established as follows:
.ie t \(bu
.el o
If the \fBcpu-shares\fR property or equivalent rctl is set, the scheduling
-class FSS is used.
+class \fBFSS\fR is used.
.RE
.RS +4
.TP
@@ -867,10 +892,15 @@ used.
.el o
Under any other conditions, the system default scheduling class is used.
.RE
+.sp
+If the \fBFX\fR scheduling class is specified, then the optional
+\fBfixed-hi-pri\fR attribute can be set to \fBtrue\fR. This causes all of the
+processes in the zone to run at the highest \fBFX\fR priority. By default
+processes under \fBFX\fR run at the lowest priority. See \fBpriocntl\fR(2)
+for details on each scheduling class.
.RE
-
.sp
.ne 2
.na
@@ -980,6 +1010,16 @@ is not supported.
.RE
.sp
+.ne 2
+.na
+\fBglobal: \fBzfs-io-priority\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specifies a priority for this zone's ZFS I/O. The priority is used by the ZFS I/O scheduler as in input to determine how to schedule I/O across zones. By default all zones have a priority of 1. The value can be increased for zones whose I/O is more critical. This property is the preferred way to set the \fBzone.zfs-io-priority\fR rctl.
+.RE
+
+.sp
.LP
The following table summarizes resources, property-names, and types:
.sp
@@ -1003,13 +1043,22 @@ resource property-name type
(global) max-shm-ids simple
(global) max-shm-memory simple
(global) scheduling-class simple
+(global) zfs-io-priority simple
fs dir simple
special simple
raw simple
type simple
options list of simple
net address simple
+ allowed-address simple
+ defrouter simple
+ global-nic simple
+ mac-addr simple
physical simple
+ property list of complex
+ name simple
+ value simple
+ vlan-id simple
device match simple
rctl name simple
value list of complex
@@ -1223,6 +1272,16 @@ name \fBglobal\fR and all names beginning with \fBSUNW\fR are reserved and
cannot be used.
.RE
+.sp
+.ne 2
+.na
+\fB\fB-u\fR \fIuuid\fR\fR
+.ad
+.sp .6
+.RS 4n
+Specify the uuid of a zone instead of the Zone name.
+.RE
+
.SH SUBCOMMANDS
You can use the \fBadd\fR and \fBselect\fR subcommands to select a specific
resource, at which point the scope changes to that resource. The \fBend\fR and
@@ -1311,8 +1370,7 @@ correct to be committed, this operation automatically does a verify.
.sp
.ne 2
.na
-\fB\fBcreate [\fR\fB-F\fR\fB] [\fR \fB-a\fR \fIpath\fR |\fB-b\fR \fB|\fR
-\fB-t\fR \fItemplate\fR\fB]\fR\fR
+\fB\fBcreate [\fR\fB-F\fR\fB] [\fR \fB-a\fR \fIpath\fR |\fB-b\fR \fB|\fR \fB-t\fR \fItemplate\fR\fB] [\fR\fB-X\fR\fB]\fR\fR
.ad
.sp .6
.RS 4n
@@ -1334,6 +1392,8 @@ configured, it should be installed using the "\fBzoneadm attach\fR" command
.sp
Use the \fB-b\fR option to create a blank configuration. Without arguments,
\fBcreate\fR applies the Sun default settings.
+.sp
+Use the \fB-X\fR option to facilitate creating a zone whose XML definition already exists on the host. The zone will be atomically added to the zone index file.
.RE
.sp
@@ -1413,18 +1473,21 @@ which is currently being added or modified.
.sp
.ne 2
.na
-\fB\fBremove\fR \fIresource-type\fR\fB{\fR\fIproperty-name\fR\fB=\fR\fIproperty
--value\fR\fB}\fR(global scope)\fR
+\fB\fBremove\fR [\fR\fB-F\fR\fB] \fIresource-type\fR\fB [\fR\fIproperty-name\fR\fB=\fR\fIproperty-value\fR\fB]* \fR(global scope)\fR
+.br
+\fB\fBremove\fR \fR\fIproperty-name\fR\fB \fR\fIproperty-value\fR\fB \fR(resource scope)\fR
.ad
.sp .6
.RS 4n
In the global scope, removes the specified resource. The \fB[]\fR syntax means
-0 or more of whatever is inside the square braces. If you want only to remove a
+0 or more property name-value pairs. If you want to only remove a
single instance of the resource, you must specify enough property name-value
pairs for the resource to be uniquely identified. If no property name-value
pairs are specified, all instances will be removed. If there is more than one
-pair is specified, a confirmation is required, unless you use the \fB-F\fR
-option.
+pair specified, a confirmation is required, unless you use the \fB-F\fR
+option. Likewise, the \fB-F\fR option can be used to remove a resource that
+does not exist (that is, no error will occur). In the resource scope, remove
+the specified name-value pair.
.RE
.sp
diff --git a/usr/src/man/man9e/ddi_ufm.9e b/usr/src/man/man9e/ddi_ufm.9e
index 4b26534f63..57ca747048 100644
--- a/usr/src/man/man9e/ddi_ufm.9e
+++ b/usr/src/man/man9e/ddi_ufm.9e
@@ -19,6 +19,7 @@
.Nm ddi_ufm ,
.Nm ddi_ufm_op_nimages ,
.Nm ddi_ufm_op_fill_image ,
+.Nm ddi_ufm_op_nslots ,
.Nm ddi_ufm_op_fill_slot ,
.Nm ddi_ufm_op_getcaps
.Nd DDI upgradable firmware module entry points
diff --git a/usr/src/man/man9f/Makefile b/usr/src/man/man9f/Makefile
index 13158caa25..f2a8ba5f80 100644
--- a/usr/src/man/man9f/Makefile
+++ b/usr/src/man/man9f/Makefile
@@ -778,7 +778,9 @@ MANLINKS= AVL_NEXT.9f \
ddi_dmae_release.9f \
ddi_dmae_stop.9f \
ddi_exit_critical.9f \
+ ddi_ffsll.9f \
ddi_fls.9f \
+ ddi_flsll.9f \
ddi_fm_capable.9f \
ddi_fm_dma_err_clear.9f \
ddi_fm_dma_err_get.9f \
@@ -1552,7 +1554,9 @@ ddi_dmae_stop.9f := LINKSRC = ddi_dmae.9f
ddi_exit_critical.9f := LINKSRC = ddi_enter_critical.9f
+ddi_ffsll.9f := LINKSRC = ddi_ffs.9f
ddi_fls.9f := LINKSRC = ddi_ffs.9f
+ddi_flsll.9f := LINKSRC = ddi_ffs.9f
ddi_fm_dma_err_clear.9f := LINKSRC = ddi_fm_acc_err_clear.9f
diff --git a/usr/src/man/man9f/bzero.9f b/usr/src/man/man9f/bzero.9f
index 94ffbaeb40..365362a76d 100644
--- a/usr/src/man/man9f/bzero.9f
+++ b/usr/src/man/man9f/bzero.9f
@@ -10,20 +10,15 @@ bzero \- clear memory for a given number of bytes
.SH SYNOPSIS
.LP
.nf
-#include <sys/types.h>
-#include <sys/ddi.h>
-
-
+#include <sys/sunddi.h>
\fBvoid\fR \fBbzero\fR(\fBvoid *\fR\fIaddr\fR, \fBsize_t\fR \fIbytes\fR);
.fi
.SH INTERFACE LEVEL
-.sp
.LP
Architecture independent level 1 (DDI/DKI).
.SH PARAMETERS
-.sp
.ne 2
.na
\fB\fIaddr\fR\fR
@@ -42,17 +37,14 @@ The number of bytes to clear starting at \fIaddr\fR.
.RE
.SH DESCRIPTION
-.sp
.LP
The \fBbzero()\fR function clears a contiguous portion of memory by filling it
with zeros.
.SH CONTEXT
-.sp
.LP
The \fBbzero()\fR function can be called from user, interrupt, or kernel
context.
.SH SEE ALSO
-.sp
.LP
.BR bcopy (9F),
.BR clrbuf (9F),
@@ -61,7 +53,6 @@ context.
.LP
\fIWriting Device Drivers\fR
.SH WARNINGS
-.sp
.LP
The address range specified must be within the kernel space. No range checking
is done. If an address outside of the kernel space is selected, the driver may
diff --git a/usr/src/man/man9f/ddi_fm_init.9f b/usr/src/man/man9f/ddi_fm_init.9f
index 767c1cea65..459ff4e8fd 100644
--- a/usr/src/man/man9f/ddi_fm_init.9f
+++ b/usr/src/man/man9f/ddi_fm_init.9f
@@ -1,175 +1,164 @@
-'\" te
+.\"
.\" Copyright (c) 2007, Sun Microsystems, Inc. All Rights Reserved.
-.\" 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]
-.TH DDI_FM_INIT 9F "Aug 08, 2022"
-.SH NAME
-ddi_fm_init, ddi_fm_fini, ddi_fm_capable \- initialize and get the FM
+.\" 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]
+.\"
+.\" Copyright (c) 2018, Joyent, INc.
+.\"
+.Dd September 13, 2018
+.Dt DDI_FM_INIT 9F
+.Os
+.Sh NAME
+.Nm ddi_fm_init ,
+.Nm ddi_fm_fini ,
+.Nm ddi_fm_capable
+.Nd initialize and get the FM capabilities for a device instance
capabilities for a device instance
-.SH SYNOPSIS
-.nf
-#include <sys/ddifm.h>
-
-\fBvoid\fR \fBddi_fm_init\fR(\fBdev_info_t\fR *\fIdip\fR, \fBint\fR *\fIfm_capability\fR,
- \fBddi_iblock_cookie_t\fR *\fIibcp\fR);
-.fi
-
-.LP
-.nf
-\fBvoid\fR \fBddi_fm_fini\fR(\fBdev_info_t\fR *\fIdip\fR);
-.fi
-
-.LP
-.nf
-\fBint\fR \fBddi_fm_capable\fR(\fBdev_info_t\fR *\fIdip\fR);
-.fi
-
-.SH INTERFACE LEVEL
-illumos DDI specific (illumos DDI)
-.SH PARAMETERS
-\fBddi_fm_init()\fR
-.sp
-.ne 2
-.na
-\fB\fIdip\fR\fR
-.ad
-.RS 17n
-Pointer to the \fBdev_info\fR structure
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fIfm_capability\fR\fR
-.ad
-.RS 17n
+.Sh SYNOPSIS
+.In sys/ddifm.h
+.Ft void
+.Fo ddi_fm_init
+.Fa "dev_info_t *dip"
+.Fa "int *fm_capability"
+.Fa "ddi_iblock_cookie_t *ibcp"
+.Fc
+.Ft void
+.Fo ddi_fm_fini
+.Fa "dev_info_t *dip"
+.Fc
+.Ft int
+.Fo ddi_fm_capable
+.Fa "dev_info_t *dip"
+.Fc
+.Sh INTERFACE LEVEL
+.Sy Committed -
+illumos DDI specific
+.Sh Parameters
+.Bl -tag -width Fa
+.It Fa dip
+Pointer to the driver's
+.Sy dev_info
+structure.
+.It Fa fm_capability
Fault Management capability bit mask
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fIibcp\fR\fR
-.ad
-.RS 17n
+.It Fa ibcp
Pointer to where the interrupt block cookie should be returned.
-.RE
-
-.SH DESCRIPTION
-A device driver can declare its fault management capabilities to the \fBI/O\fR
-Fault Management framework by calling \fBddi_fm_init()\fR. The
-\fBddi_fm_init()\fR function allocates and initializes resources according to
+.El
+.Sh DESCRIPTION
+A device driver can declare its fault management capabilities to the I/O
+Fault Management framework by calling the
+.Fn ddi_fm_init
+function.
+The
+.Fn ddi_fm_init
+function allocates and initializes resources according to
the bitwise-inclusive-OR of the fault management capabilities, described in the
following and supported by the driver's immediate nexus parent.
-.sp
-.ne 2
-.na
-\fB\fBDDI_FM_NOT_CAPABLE\fR\fR
-.ad
-.RS 26n
-The driver does not support any \fBFMA\fR features. This is the default value
-assigned to device drivers.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBDDI_FM_EREPORT_CAPABLE\fR\fR
-.ad
-.RS 26n
-The driver generates \fBFMA\fR protocol error events (\fBereports\fR) upon the
-detection of an error condition.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBDDI_FM_ACCCHK_CAPABLE\fR\fR
-.ad
-.RS 26n
+.Bl -tag -width Vt
+.It Vt DDI_FM_NOT_CAPABLE
+The driver does not support any FMA features.
+This is the default value assigned to device drivers.
+.Pp
+If the value of the capability bits is equal to
+.Vt DDI_FM_NOT_CAPABLE ,
+then no capability bits will be set.
+Conversely, if a capability bit is set, then the capability value will
+not be equal to
+.Vt DDI_FM_NOT_CAPABLE .
+.It Vt DDI_FM_EREPORT_CAPABLE
+The driver generates FMA protocol error events
+.Pq Sy ereports
+upon the detection of an error condition.
+.It Vt DDI_FM_ACCCHK_CAPABLE
The driver checks for errors upon the completion of one or more access
-\fBI/O\fR transactions.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBDDI_FM_DMACHK_CAPABLE\fR\fR
-.ad
-.RS 26n
-The driver checks for errors upon the completion of one or more \fBDMA\fR
-\fBI/O\fR transactions.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBDDI_FM_ERRCB_CAPABLE\fR\fR
-.ad
-.RS 26n
+I/O transactions.
+.It Vt DDI_FM_DMACHK_CAPABLE
+The driver checks for errors upon the completion of one or more DMA
+I/O transactions.
+.It Vt DDI_FM_ERRCB_CAPABLE
The driver is capable of error handler callback registration.
-.RE
-
-.sp
-.LP
+.El
+.Pp
If the parent nexus is not capable of supporting any one of the requested
capabilities, the associated bit will not be set and returned as such to the
-driver. Before returning from \fBddi_fm_init()\fR, the \fBI/O\fR Fault
-Management framework creates a set of fault management capability properties:
-\fBfm-ereport-capable\fR, \fBfm-errcb-capable\fR, \fBfm-accchk-capable\fR, and
-\fBfm-dmachk-capable\fR. The current supported fault management capability
-levels are observable via \fBprtconf\fR(8).
-.sp
-.LP
+driver.
+Before returning from
+.Fn ddi_fm_init ,
+the I/O Fault Management framework creates a set of fault management capability
+properties:
+.Sy fm-ereport-capable ,
+.Sy fm-errcb-capable ,
+.Sy fm-accchk-capable ,
+and
+.Sy fm-dmachk-capable .
+The current supported fault management capability levels are observable via
+.Xr prtconf 8 .
+.Pp
A driver can support the administrative selection of fault management
capabilities by exporting and setting a fault management capability level
-property in its \fBdriver.conf\fR(5) file to the values described above. The
-\fBfm_capable\fR properties must be set and read prior to calling
-\fBddi_fm_init()\fR with the desired capability list.
-.sp
-.ne 2
-.na
-\fB\fBddi_fm_fini()\fR\fR
-.ad
-.RS 17n
-This function cleans up resources allocated to support fault management for the
-\fIdip\fR structure.
-.RE
-
-.sp
-.ne 2
-.na
-\fB\fBddi_fm_capable()\fR\fR
-.ad
-.RS 20n
-This function returns the capability bit mask currently set for the \fIdip\fR
-structure.
-.RE
-
-.SH CONTEXT
-These functions can be called from kernel context in a driver \fBattach\fR(9E)
-or \fBdetach\fR(9E) operation.
-.SH ATTRIBUTES
-See \fBattributes\fR(7) for descriptions of the following attributes:
-.sp
-
-.sp
-.TS
-box;
-c | c
-l | l .
-ATTRIBUTE TYPE ATTRIBUTE VALUE
-_
-Interface Stability Committed
-.TE
-
-.SH SEE ALSO
-.BR driver.conf (5),
-.BR attributes (7),
-.BR prtconf (8),
-.BR attach (9E),
-.BR detach (9E)
-.sp
-.LP
-\fIWriting Device Drivers\fR
+property in its
+.Xr driver.conf 5
+file to the values described above.
+The
+.Sy fm_capable
+properties must be set and read prior to calling
+.Fn ddi_fm_init
+with the desired capability list.
+.Pp
+The
+.Fn ddi_fm_fini
+function cleans up the kernel infrastructure set up during a call to the
+.Fn ddi_fm_init
+function.
+If the driver did not call
+.Fn ddi_fm_init
+or the capability returned in
+.Fa fm_capability
+was
+.Vt DDI_FM_NOT_CAPABLE
+then the driver must not call
+.Fn ddi_fm_fini .
+.Pp
+The
+.Fn ddi_fm_capable
+function returns the capability bit mask currently set for the device
+instance identified by
+.Fa dip .
+.Sh CONTEXT
+The
+.Fn ddi_fm_init
+and
+.Fn ddi_fm_fini
+functions may only be called from kernel context during a driver's
+.Xr attach 9E
+and
+.Xr detach 9E
+entry points.
+.Sh RETURN VALUES
+Upon successful completion, the
+.Fn ddi_fm_capable
+function returns the FM capability bit mask currently in use.
+If no capabilities are in use then
+.Vt DDI_FM_NOT_CAPABLE
+is returned.
+.Sh SEE ALSO
+.Xr driver.conf 5 ,
+.Xr prtconf 8 ,
+.Xr attach 9E ,
+.Xr detach 9E
+.Pp
+.Rs
+.%B Writing Device Drivers
+.Re
diff --git a/usr/src/pkg/manifests/developer-build-make.p5m b/usr/src/pkg/manifests/developer-build-make.p5m
index fe293ebe4b..2524715e66 100644
--- a/usr/src/pkg/manifests/developer-build-make.p5m
+++ b/usr/src/pkg/manifests/developer-build-make.p5m
@@ -10,6 +10,7 @@
#
# Copyright 2015, Richard Lowe.
+# Copyright 2019 Joyent, Inc.
set name=pkg.fmri value=pkg:/developer/build/make@$(PKGVERS)
set name=pkg.summary value="Parallel make(1) build tool"
@@ -19,14 +20,21 @@ set name=info.classification \
set name=variant.arch value=$(ARCH)
link path=usr/bin/dmake target=make
file path=usr/bin/make mode=0555
+dir path=usr/ccs
+dir path=usr/ccs/bin
link path=usr/ccs/bin/make target=../../bin/make
+dir path=usr/ccs/lib
link path=usr/ccs/lib/svr4.make target=../../bin/make
+dir path=usr/lib/$(ARCH64)
file path=usr/lib/$(ARCH64)/libmakestate.so.1
file path=usr/lib/libmakestate.so.1
link path=usr/lib/svr4.make target=../bin/make
+dir path=usr/share
+dir path=usr/share/lib
dir path=usr/share/lib/make
file path=usr/share/lib/make/make.rules
file path=usr/share/lib/make/svr4.make.rules
+dir path=usr/share/man
dir path=usr/share/man/man1
link path=usr/share/man/man1/dmake.1 target=make.1
file path=usr/share/man/man1/make.1
@@ -34,4 +42,6 @@ file path=usr/share/man/man1/sysV-make.1
dir path=usr/share/man/man1s
link path=usr/share/man/man1s/dmake.1s target=../man1/make.1
link path=usr/share/man/man1s/make.1s target=../man1/make.1
+dir path=usr/xpg4
+dir path=usr/xpg4/bin
link path=usr/xpg4/bin/make target=../../bin/make
diff --git a/usr/src/pkg/manifests/driver-crypto-nfp.p5m b/usr/src/pkg/manifests/driver-crypto-nfp.p5m
new file mode 100644
index 0000000000..5db2c3d1b3
--- /dev/null
+++ b/usr/src/pkg/manifests/driver-crypto-nfp.p5m
@@ -0,0 +1,32 @@
+#
+# 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 2014 <contributor>
+#
+
+set name=pkg.fmri value=pkg:/driver/crypto/nfp@$(PKGVERS)
+set name=pkg.summary value="nfast Crypto Accelerator"
+set name=pkg.description value="nfast Crypto Accelerator Driver"
+set name=info.classification value=org.opensolaris.category.2008:System/Hardware
+set name=variant.arch value=i386
+dir path=kernel group=sys
+dir path=kernel/drv group=sys
+dir path=kernel/drv/$(ARCH64) group=sys
+file path=kernel/drv/$(ARCH64)/nfp group=sys
+$(i386_ONLY)file path=kernel/drv/nfp group=sys
+driver name=nfp \
+ alias=pci1011,1065.100.100 \
+ alias=pci8086,b555.100.100 \
+ alias=pciex1011,1065.100.100 \
+ alias=pciex8086,b555.100.100
+license usr/src/uts/common/io/nfp/THIRDPARTYLICENSE \
+ license=usr/src/uts/common/io/nfp/THIRDPARTYLICENSE
diff --git a/usr/src/pkg/manifests/service-fault-management.p5m b/usr/src/pkg/manifests/service-fault-management.p5m
index 0e094d88ea..3cc7595610 100644
--- a/usr/src/pkg/manifests/service-fault-management.p5m
+++ b/usr/src/pkg/manifests/service-fault-management.p5m
@@ -484,9 +484,9 @@ $(i386_ONLY)file path=usr/platform/i86pc/lib/fm/eft/gcpu_amd.eft mode=0444
$(i386_ONLY)file path=usr/platform/i86pc/lib/fm/eft/intel.eft mode=0444
$(i386_ONLY)dir path=usr/platform/i86pc/lib/fm/topo
$(i386_ONLY)dir path=usr/platform/i86pc/lib/fm/topo/maps
-$(i386_ONLY)file \
- path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-1101-disk-hc-topology.xml \
- mode=0444
+$(i386_ONLY)link \
+ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3101-hc-topology.xml \
+ target=./SSG-2028R-ACR24L-hc-topology.xml
$(i386_ONLY)link \
path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3101-hc-topology.xml \
target=SSG-2028R-ACR24L-hc-topology.xml
@@ -499,6 +499,9 @@ $(i386_ONLY)link \
target=SSG-2028R-ACR24L-usb.usbtopo
$(i386_ONLY)link \
path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3102-hc-topology.xml \
+ target=./SSG-2028R-ACR24L-hc-topology.xml
+$(i386_ONLY)link \
+ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3102-hc-topology.xml \
target=SSG-2028R-ACR24L-hc-topology.xml
$(i386_ONLY)link \
path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3102-usb.usbtopo \
@@ -506,6 +509,12 @@ $(i386_ONLY)link \
$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3301-hc-topology.xml \
mode=0444
+$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3301-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)link \
+ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3302-hc-topology.xml \
+ target=./Joyent-Compute-Platform-3301-hc-topology.xml
$(i386_ONLY)link \
path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-3302-hc-topology.xml \
target=Joyent-Compute-Platform-3301-hc-topology.xml
@@ -513,6 +522,12 @@ $(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-330x-chassis-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-330x-chassis-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-330x-fan-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-330x-fan-hc-topology.xml \
mode=0444
$(i386_ONLY)link \
@@ -531,24 +546,45 @@ $(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Storage-Platform-7001-chassis-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Storage-Platform-7001-chassis-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Storage-Platform-7001-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Storage-Platform-7001-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Storage-Platform-7001-slot-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/Joyent-Storage-Platform-7001-slot-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/Netra-X4200-M2-disk-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-chassis-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-chassis-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-disk-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-disk-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-slot-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2028R-ACR24L-slot-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
@@ -558,12 +594,24 @@ $(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-chassis-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-chassis-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-disk-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-disk-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
+ path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-slot-hc-topology.xml \
+ mode=0444
+$(i386_ONLY)file \
path=usr/platform/i86pc/lib/fm/topo/maps/SSG-2029P-ACR24L-slot-hc-topology.xml \
mode=0444
$(i386_ONLY)file \
diff --git a/usr/src/pkg/manifests/system-bhyve-tests.p5m b/usr/src/pkg/manifests/system-bhyve-tests.p5m
index 92985ee1cb..fe3225dd14 100644
--- a/usr/src/pkg/manifests/system-bhyve-tests.p5m
+++ b/usr/src/pkg/manifests/system-bhyve-tests.p5m
@@ -70,12 +70,12 @@ file path=opt/bhyve-tests/tests/vmm/mem_partial mode=0555
file path=opt/bhyve-tests/tests/vmm/mem_seg_map mode=0555
file path=opt/bhyve-tests/tests/vmm/pause_resume mode=0555
file path=opt/bhyve-tests/tests/vmm/self_destruct mode=0555
+file path=opt/bhyve-tests/tests/vmm/vmm_drv_test_fini mode=0555
+file path=opt/bhyve-tests/tests/vmm/vmm_drv_test_init mode=0555
dir path=usr/kernel/drv group=sys
dir path=usr/kernel/drv/$(ARCH64) group=sys
file path=usr/kernel/drv/$(ARCH64)/vmm_drv_test
file path=usr/kernel/drv/vmm_drv_test.conf
-driver name=vmm_drv_test perms="* 0666 root sys" \
- policy="read_priv_set=sys_devices write_priv_set=sys_devices"
license lic_CDDL license=lic_CDDL
depend type=require fmri=system/bhyve
depend type=require fmri=system/test/testrunner
diff --git a/usr/src/pkg/manifests/system-dtrace-tests.p5m b/usr/src/pkg/manifests/system-dtrace-tests.p5m
index 9185e36a0a..3edfb242dc 100644
--- a/usr/src/pkg/manifests/system-dtrace-tests.p5m
+++ b/usr/src/pkg/manifests/system-dtrace-tests.p5m
@@ -22,7 +22,7 @@
#
# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2012, 2016 by Delphix. All rights reserved.
-# Copyright 2018 Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
#
set name=pkg.fmri value=pkg:/system/dtrace/tests@$(PKGVERS)
diff --git a/usr/src/pkg/manifests/system-header.p5m b/usr/src/pkg/manifests/system-header.p5m
index 8410cf4a52..c49aba97b4 100644
--- a/usr/src/pkg/manifests/system-header.p5m
+++ b/usr/src/pkg/manifests/system-header.p5m
@@ -757,6 +757,7 @@ file path=usr/include/sys/cpc_pcbe.h
file path=usr/include/sys/cpr.h
file path=usr/include/sys/cpu.h
file path=usr/include/sys/cpu_uarray.h
+file path=usr/include/sys/cpu_uarray.h
file path=usr/include/sys/cpucaps.h
file path=usr/include/sys/cpucaps_impl.h
file path=usr/include/sys/cpupart.h
diff --git a/usr/src/pkg/manifests/system-kernel.man9f.inc b/usr/src/pkg/manifests/system-kernel.man9f.inc
index 607c139131..62a8927bae 100644
--- a/usr/src/pkg/manifests/system-kernel.man9f.inc
+++ b/usr/src/pkg/manifests/system-kernel.man9f.inc
@@ -403,7 +403,9 @@ file path=usr/share/man/man9f/ddi_driver_name.9f
file path=usr/share/man/man9f/ddi_enter_critical.9f
link path=usr/share/man/man9f/ddi_exit_critical.9f target=ddi_enter_critical.9f
file path=usr/share/man/man9f/ddi_ffs.9f
+link path=usr/share/man/man9f/ddi_ffsll.9f target=ddi_ffs.9f
link path=usr/share/man/man9f/ddi_fls.9f target=ddi_ffs.9f
+link path=usr/share/man/man9f/ddi_flsll.9f target=ddi_ffs.9f
file path=usr/share/man/man9f/ddi_fm_acc_err_clear.9f
file path=usr/share/man/man9f/ddi_fm_acc_err_get.9f
link path=usr/share/man/man9f/ddi_fm_capable.9f target=ddi_fm_init.9f
diff --git a/usr/src/pkg/manifests/system-library-bhyve.p5m b/usr/src/pkg/manifests/system-library-bhyve.p5m
index 254d3da975..f8a972d112 100644
--- a/usr/src/pkg/manifests/system-library-bhyve.p5m
+++ b/usr/src/pkg/manifests/system-library-bhyve.p5m
@@ -20,8 +20,6 @@
set name=pkg.fmri value=pkg:/system/library/bhyve@$(PKGVERS)
set name=pkg.summary value="BSD hypervisor (libraries)"
set name=pkg.description value="BSD hypervisor (libraries)"
-set name=info.classification \
- value=org.opensolaris.category.2008:System/Virtualization
set name=variant.arch value=i386
dir path=lib group=bin
dir path=lib/$(ARCH64) group=bin
diff --git a/usr/src/pkg/manifests/system-test-libctest.p5m b/usr/src/pkg/manifests/system-test-libctest.p5m
index 10f847a18b..763ae9b093 100644
--- a/usr/src/pkg/manifests/system-test-libctest.p5m
+++ b/usr/src/pkg/manifests/system-test-libctest.p5m
@@ -77,6 +77,8 @@ file path=opt/libc-tests/tests/endian.32 mode=0555
file path=opt/libc-tests/tests/endian.64 mode=0555
file path=opt/libc-tests/tests/env-7076.32 mode=0555
file path=opt/libc-tests/tests/env-7076.64 mode=0555
+file path=opt/libc-tests/tests/env-OS-4089.32 mode=0555
+file path=opt/libc-tests/tests/env-OS-4089.64 mode=0555
dir path=opt/libc-tests/tests/err
dir path=opt/libc-tests/tests/err/data
file path=opt/libc-tests/tests/err/data/E.0.3.4 mode=0444
diff --git a/usr/src/pkg/manifests/system-test-nettest.p5m b/usr/src/pkg/manifests/system-test-nettest.p5m
index 1e2579073e..8c6f71a111 100644
--- a/usr/src/pkg/manifests/system-test-nettest.p5m
+++ b/usr/src/pkg/manifests/system-test-nettest.p5m
@@ -10,6 +10,7 @@
#
#
+# Copyright 2019 Joyent, Inc.
# Copyright 2020 Oxide Computer Company
#
diff --git a/usr/src/pkg/manifests/system-test-ostest.p5m b/usr/src/pkg/manifests/system-test-ostest.p5m
index 9ee06d5fbc..f0c61d7872 100644
--- a/usr/src/pkg/manifests/system-test-ostest.p5m
+++ b/usr/src/pkg/manifests/system-test-ostest.p5m
@@ -36,6 +36,8 @@ file path=opt/os-tests/bin/ostest mode=0555
dir path=opt/os-tests/runfiles
file path=opt/os-tests/runfiles/default.run mode=0444
dir path=opt/os-tests/tests
+file path=opt/os-tests/tests/OS-6097.32 mode=0555
+file path=opt/os-tests/tests/OS-6097.64 mode=0555
file path=opt/os-tests/tests/clock_gettime.32 mode=0555
file path=opt/os-tests/tests/clock_gettime.64 mode=0555
dir path=opt/os-tests/tests/cores
@@ -57,7 +59,6 @@ file path=opt/os-tests/tests/definit/definit_test.32 mode=0555
file path=opt/os-tests/tests/definit/definit_test.64 mode=0555
file path=opt/os-tests/tests/definit/init.data mode=0444
file path=opt/os-tests/tests/definit/init.expected mode=0444
-file path=opt/os-tests/tests/epoll_test mode=0555
file path=opt/os-tests/tests/eventfd.32 mode=0555
file path=opt/os-tests/tests/eventfd.64 mode=0555
dir path=opt/os-tests/tests/file-locking
@@ -69,6 +70,7 @@ $(i386_ONLY)dir path=opt/os-tests/tests/i386
$(i386_ONLY)file path=opt/os-tests/tests/i386/badseg mode=0555
$(i386_ONLY)file path=opt/os-tests/tests/i386/badseg_exec mode=0555
$(i386_ONLY)file path=opt/os-tests/tests/i386/ldt mode=0555
+file path=opt/os-tests/tests/imc_test mode=0555
$(i386_ONLY)file path=opt/os-tests/tests/imc_test mode=0555
dir path=opt/os-tests/tests/ksensor
file path=opt/os-tests/tests/ksensor/ksensor_basic.32 mode=0555
@@ -97,7 +99,9 @@ file path=opt/os-tests/tests/pf_key/acquire-spray mode=0555
file path=opt/os-tests/tests/pf_key/eacq-enabler mode=0555
file path=opt/os-tests/tests/pf_key/kmc-update mode=0555
file path=opt/os-tests/tests/pf_key/kmc-updater mode=0555
-file path=opt/os-tests/tests/poll_test mode=0555
+dir path=opt/os-tests/tests/poll
+file path=opt/os-tests/tests/poll/epoll_test mode=0555
+file path=opt/os-tests/tests/poll/poll_test mode=0555
dir path=opt/os-tests/tests/portfs
file path=opt/os-tests/tests/portfs/file_assoc.32 mode=0555
file path=opt/os-tests/tests/portfs/file_assoc.64 mode=0555
diff --git a/usr/src/pkg/manifests/system-test-smartostest.p5m b/usr/src/pkg/manifests/system-test-smartostest.p5m
new file mode 100644
index 0000000000..c9472f52fe
--- /dev/null
+++ b/usr/src/pkg/manifests/system-test-smartostest.p5m
@@ -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 2019 Joyent, Inc.
+#
+
+#
+# The SmartOS test package is expressly intended *not* to be
+# installable on other distributions, and should not be
+# up-streamed to the illumos-gate. It redelivers isaexec,
+# ctfconvert and ctfmerge, which violates IPS requirements
+# that only a single package deliver a given file,
+# unless IPS variants are used.
+#
+# This package is used as part of the generation of the
+# test archive .tgz during the smartos-live build in
+# order to ensure files not normally delivered by SmartOS
+# will appear in /usr when the smartos-test script has
+# configured the system for testing.
+#
+set name=pkg.fmri value=pkg:/system/test/smartostest@$(PKGVERS)
+set name=pkg.summary value="SmartOS test execution"
+set name=pkg.description value="SmartOS test setup and execution"
+set name=info.classification \
+ value=org.opensolaris.category.2008:Development/System
+set name=variant.arch value=$(ARCH)
+dir path=opt owner=root group=sys
+dir path=opt/smartos-test
+file path=opt/smartos-test/README mode=0444
+dir path=opt/smartos-test/bin
+file path=opt/smartos-test/bin/smartos-test mode=0555
+# Needed by the /opt/utils-test suite
+file path=usr/bin/ctfconvert mode=0555
+file path=usr/bin/ctfmerge mode=0555
+# The libc-tests attempt to forge a hardlink to isaexec
+# so we need to redeliver isaexec in order to create the
+# proto area correctly.
+file path=usr/lib/isaexec mode=0555
+license cr_Sun license=cr_Sun
+license lic_CDDL license=lic_CDDL
+depend type=require fmri=system/test/testrunner
diff --git a/usr/src/pkg/manifests/system-test-utiltest.p5m b/usr/src/pkg/manifests/system-test-utiltest.p5m
index 9c779d6e3d..0d9a3bab21 100644
--- a/usr/src/pkg/manifests/system-test-utiltest.p5m
+++ b/usr/src/pkg/manifests/system-test-utiltest.p5m
@@ -28,6 +28,7 @@ set name=variant.arch value=$(ARCH)
dir path=opt/util-tests
file path=opt/util-tests/README mode=0444
dir path=opt/util-tests/bin
+file path=opt/util-tests/bin/btest mode=0555
file path=opt/util-tests/bin/print_json mode=0555
file path=opt/util-tests/bin/utiltest mode=0555
dir path=opt/util-tests/runfiles
@@ -39,6 +40,7 @@ file path=opt/util-tests/tests/ar/ar_test0.o mode=0444
file path=opt/util-tests/tests/ar/ar_test1.o mode=0444
file path=opt/util-tests/tests/ar/artest mode=0555
dir path=opt/util-tests/tests/awk
+dir path=opt/util-tests/tests/awk
dir path=opt/util-tests/tests/awk/bugs-fixed
file path=opt/util-tests/tests/awk/bugs-fixed/a-format.awk mode=0444
file path=opt/util-tests/tests/awk/bugs-fixed/a-format.ok mode=0444
@@ -75,9 +77,12 @@ file path=opt/util-tests/tests/awk/bugs-fixed/system-status.ok mode=0444
file path=opt/util-tests/tests/awk/bugs-fixed/unary-plus.awk mode=0444
file path=opt/util-tests/tests/awk/bugs-fixed/unary-plus.ok mode=0444
dir path=opt/util-tests/tests/awk/data
+dir path=opt/util-tests/tests/awk/data
file path=opt/util-tests/tests/awk/data/test.countries mode=0444
file path=opt/util-tests/tests/awk/data/test.data mode=0444
dir path=opt/util-tests/tests/awk/examples
+dir path=opt/util-tests/tests/awk/examples
+dir path=opt/util-tests/tests/awk/examples/awk
dir path=opt/util-tests/tests/awk/examples/awk
file path=opt/util-tests/tests/awk/examples/awk/p.1 mode=0444
file path=opt/util-tests/tests/awk/examples/awk/p.10 mode=0444
@@ -304,6 +309,7 @@ file path=opt/util-tests/tests/awk/examples/awk/t.vf2 mode=0444
file path=opt/util-tests/tests/awk/examples/awk/t.vf3 mode=0444
file path=opt/util-tests/tests/awk/examples/awk/t.x mode=0444
dir path=opt/util-tests/tests/awk/examples/out
+dir path=opt/util-tests/tests/awk/examples/out
file path=opt/util-tests/tests/awk/examples/out/p.1 mode=0444
file path=opt/util-tests/tests/awk/examples/out/p.10 mode=0444
file path=opt/util-tests/tests/awk/examples/out/p.11 mode=0444
@@ -529,6 +535,7 @@ file path=opt/util-tests/tests/awk/examples/out/t.vf2 mode=0444
file path=opt/util-tests/tests/awk/examples/out/t.vf3 mode=0444
file path=opt/util-tests/tests/awk/examples/out/t.x mode=0444
dir path=opt/util-tests/tests/awk/gnu
+dir path=opt/util-tests/tests/awk/gnu
file path=opt/util-tests/tests/awk/gnu/anchgsub.awk mode=0444
file path=opt/util-tests/tests/awk/gnu/anchgsub.in mode=0444
file path=opt/util-tests/tests/awk/gnu/anchgsub.ok mode=0444
@@ -960,6 +967,7 @@ file path=opt/util-tests/tests/awk/gnu/zeroflag.awk mode=0444
file path=opt/util-tests/tests/awk/gnu/zeroflag.ok mode=0444
file path=opt/util-tests/tests/awk/runtests.sh mode=0555
dir path=opt/util-tests/tests/awk/syn
+dir path=opt/util-tests/tests/awk/syn
file path=opt/util-tests/tests/awk/syn/arrayparm.awk mode=0444
file path=opt/util-tests/tests/awk/syn/arrayparm.ok mode=0444
file path=opt/util-tests/tests/awk/syn/arryref3.awk mode=0444
@@ -1027,6 +1035,7 @@ file path=opt/util-tests/tests/awk/syn/tradanch1.ok mode=0444
file path=opt/util-tests/tests/awk/syn/unterm.awk mode=0444
file path=opt/util-tests/tests/awk/syn/unterm.ok mode=0444
dir path=opt/util-tests/tests/awk/tests
+dir path=opt/util-tests/tests/awk/tests
file path=opt/util-tests/tests/awk/tests/T.-f-f mode=0555
file path=opt/util-tests/tests/awk/tests/T.argv mode=0555
file path=opt/util-tests/tests/awk/tests/T.builtin mode=0555
@@ -1064,6 +1073,8 @@ file path=opt/util-tests/tests/awk/tests/penicil.ok mode=0444
file path=opt/util-tests/tests/awk/tests/penicil.p mode=0444
file path=opt/util-tests/tests/awk/tests/res.ok mode=0444
file path=opt/util-tests/tests/awk/tests/res.p mode=0444
+dir path=opt/util-tests/tests/bunyan
+file path=opt/util-tests/tests/bunyan/bunyan mode=0555
file path=opt/util-tests/tests/chown_test mode=0555
dir path=opt/util-tests/tests/ctf
file path=opt/util-tests/tests/ctf/Makefile.ctftest.com mode=0555
@@ -1847,6 +1858,7 @@ file path=opt/util-tests/tests/sleep/sleep.d mode=0444
file path=opt/util-tests/tests/sleep/sleeptest mode=0555
file path=opt/util-tests/tests/smbios mode=0555
file path=opt/util-tests/tests/svr4pkg_test mode=0555
+file path=opt/util-tests/tests/vnic-mtu mode=0555
file path=opt/util-tests/tests/xargs_test mode=0555
license lic_CDDL license=lic_CDDL
license usr/src/lib/libdemangle/THIRDPARTYLICENSE \
diff --git a/usr/src/pkg/manifests/system-test-vndtest.p5m b/usr/src/pkg/manifests/system-test-vndtest.p5m
new file mode 100644
index 0000000000..fc5284e47e
--- /dev/null
+++ b/usr/src/pkg/manifests/system-test-vndtest.p5m
@@ -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 2019 Joyent, Inc.
+#
+
+set name=pkg.fmri value=pkg:/system/test/vndtest@$(PKGVERS)
+set name=pkg.summary value="vndtest execution"
+set name=pkg.description value=vndtest
+set name=info.classification \
+ value=org.opensolaris.category.2008:Development/System
+set name=variant.arch value=$(ARCH)
+dir path=opt/vndtest
+dir path=opt/vndtest/bin
+file path=opt/vndtest/bin/vndtest mode=0555
+dir path=opt/vndtest/tst
+dir path=opt/vndtest/tst/cmd
+file path=opt/vndtest/tst/cmd/cmd.common.ksh mode=0555
+file path=opt/vndtest/tst/cmd/create.list.ksh mode=0555
+file path=opt/vndtest/tst/cmd/create.list.ksh.out mode=0555
+file path=opt/vndtest/tst/cmd/create.sdev.ksh mode=0555
+file path=opt/vndtest/tst/cmd/create.setbuf.ksh mode=0555
+file path=opt/vndtest/tst/cmd/ecreate.destroy.ksh mode=0555
+file path=opt/vndtest/tst/cmd/ecreate.setbadprop.ksh mode=0555
+file path=opt/vndtest/tst/cmd/ecreate.setbadvalue.ksh mode=0555
+file path=opt/vndtest/tst/cmd/ecreate.setbuftoobig.ksh mode=0555
+file path=opt/vndtest/tst/cmd/ecreate.setrdonlyprop.ksh mode=0555
+dir path=opt/vndtest/tst/dld
+file path=opt/vndtest/tst/dld/create.reuse.ksh mode=0555
+file path=opt/vndtest/tst/dld/dld.common.ksh mode=0555
+file path=opt/vndtest/tst/dld/ecreate.ipfirst.ksh mode=0555
+file path=opt/vndtest/tst/dld/ecreate.vndfirst.ksh mode=0555
+dir path=opt/vndtest/tst/ioctl
+file path=opt/vndtest/tst/ioctl/create.attach.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.attachnolink.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.badlinkname.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.doublelink.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.gioctlattach.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.link.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.linkexists.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.ngioctlfault.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.nopriv1.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.nopriv2.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.nopriv3.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.nopriv4.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.olink.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.olinknopriv.exe mode=0555
+file path=opt/vndtest/tst/ioctl/create.rmenolink.exe mode=0555
+file path=opt/vndtest/tst/ioctl/tst.attachrdonly.exe mode=0555
+file path=opt/vndtest/tst/ioctl/tst.badioctl.exe mode=0555
+file path=opt/vndtest/tst/ioctl/tst.basicopenctl.exe mode=0555
+file path=opt/vndtest/tst/ioctl/tst.gioctlfault.exe mode=0555
+file path=opt/vndtest/tst/ioctl/tst.gioctlnattach.exe mode=0555
+file path=opt/vndtest/tst/ioctl/tst.iocsize.ksh mode=0555
+file path=opt/vndtest/tst/ioctl/tst.openctlbadflags.exe mode=0555
+dir path=opt/vndtest/tst/lib
+file path=opt/vndtest/tst/lib/create.badlink.exe mode=0555
+file path=opt/vndtest/tst/lib/create.badpropid.exe mode=0555
+file path=opt/vndtest/tst/lib/create.badpropsize.exe mode=0555
+file path=opt/vndtest/tst/lib/create.badzone.exe mode=0555
+file path=opt/vndtest/tst/lib/create.basic.exe mode=0555
+file path=opt/vndtest/tst/lib/create.enomem.exe mode=0555
+file path=opt/vndtest/tst/lib/create.frameioeagain.exe mode=0555
+file path=opt/vndtest/tst/lib/create.open.exe mode=0555
+file path=opt/vndtest/tst/lib/create.propiter.exe mode=0555
+file path=opt/vndtest/tst/lib/create.proprdonly.exe mode=0555
+file path=opt/vndtest/tst/lib/err.badclose.exe mode=0555
+file path=opt/vndtest/tst/lib/tst.badopen.exe mode=0555
+file path=opt/vndtest/tst/lib/tst.strerror.exe mode=0555
+file path=opt/vndtest/tst/lib/tst.strerror.exe.out mode=0555
+file path=opt/vndtest/tst/lib/tst.strsyserror.exe mode=0555
+license cr_Sun license=cr_Sun
+license lic_CDDL license=lic_CDDL
diff --git a/usr/src/pkg/manifests/system-test-zfstest.p5m b/usr/src/pkg/manifests/system-test-zfstest.p5m
index 372a2a1a3a..d7d3830215 100644
--- a/usr/src/pkg/manifests/system-test-zfstest.p5m
+++ b/usr/src/pkg/manifests/system-test-zfstest.p5m
@@ -50,6 +50,7 @@ file path=opt/zfs-tests/bin/randwritecomp mode=0555
file path=opt/zfs-tests/bin/readmmap mode=0555
file path=opt/zfs-tests/bin/rename_dir mode=0555
file path=opt/zfs-tests/bin/rm_lnkcnt_zero_file mode=0555
+file path=opt/zfs-tests/bin/watch_dir mode=0555
file path=opt/zfs-tests/bin/zfstest mode=0555
dir path=opt/zfs-tests/callbacks
file path=opt/zfs-tests/callbacks/zfs_dbgmsg mode=0555
@@ -722,6 +723,8 @@ file \
file \
path=opt/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_crypt_combos \
mode=0555
+file path=opt/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_dryrun \
+ mode=0555
file \
path=opt/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_encrypted \
mode=0555
@@ -904,6 +907,12 @@ file \
file \
path=opt/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_encrypted \
mode=0555
+file \
+ path=opt/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_inotify \
+ mode=0555
+file \
+ path=opt/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_portfs \
+ mode=0555
dir path=opt/zfs-tests/tests/functional/cli_root/zfs_program
file path=opt/zfs-tests/tests/functional/cli_root/zfs_program/cleanup mode=0555
file path=opt/zfs-tests/tests/functional/cli_root/zfs_program/setup mode=0555
diff --git a/usr/src/pkg/manifests/system-zones-brand-joyent.p5m b/usr/src/pkg/manifests/system-zones-brand-joyent.p5m
new file mode 100644
index 0000000000..398980ddde
--- /dev/null
+++ b/usr/src/pkg/manifests/system-zones-brand-joyent.p5m
@@ -0,0 +1,54 @@
+#
+# 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 2010 Joyent, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+set name=pkg.fmri value=pkg:/system/zones/brand/joyent@$(PKGVERS)
+set name=pkg.summary value="Joyent Containers: joyent brand support"
+set name=pkg.description value="Support for the 'joyent' Brand"
+set name=info.classification \
+ value=org.opensolaris.category.2008:System/Virtualization
+set name=variant.arch value=$(ARCH)
+dir path=etc group=sys
+dir path=etc/zones group=sys
+file path=etc/zones/Joyent.xml mode=0444
+dir path=lib variant.opensolaris.zone=global
+dir path=lib/svc variant.opensolaris.zone=global
+dir path=lib/svc/manifest group=sys variant.opensolaris.zone=global
+dir path=lib/svc/manifest/system group=sys variant.opensolaris.zone=global
+file path=lib/svc/manifest/system/joyinit.xml group=sys mode=0444 \
+ variant.opensolaris.zone=global
+dir path=lib/svc/method variant.opensolaris.zone=global
+file path=lib/svc/method/svc-joyinit mode=0555 variant.opensolaris.zone=global
+dir path=usr group=sys
+dir path=usr/lib
+dir path=usr/lib/brand
+dir path=usr/lib/brand/joyent group=sys
+file path=usr/lib/brand/joyent/config.xml mode=0444
+file path=usr/lib/brand/joyent/jinstall mode=0755
+file path=usr/lib/brand/joyent/juninstall mode=0755
+file path=usr/lib/brand/joyent/pinstall mode=0755
+file path=usr/lib/brand/joyent/platform.xml mode=0444
+file path=usr/lib/brand/joyent/prestate mode=0755
+license lic_CDDL license=lic_CDDL
diff --git a/usr/src/pkg/manifests/system-zones-brand-lx.p5m b/usr/src/pkg/manifests/system-zones-brand-lx.p5m
index ca3a8cc541..58a016fa9c 100644
--- a/usr/src/pkg/manifests/system-zones-brand-lx.p5m
+++ b/usr/src/pkg/manifests/system-zones-brand-lx.p5m
@@ -20,9 +20,104 @@
#
#
-# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+# Copyright 2015 Joyent, Inc.
#
-set name=pkg.fmri value=pkg:/system/zones/brand/lx@0.5.11,5.11-0.143
-set name=pkg.obsolete value=true
+#
+# This package will install successfully into any zone, global or
+# non-global. The files, directories, links, and hardlinks, however,
+# will only be installed into the global zone.
+#
+<include global_zone_only_component>
+set name=pkg.fmri value=pkg:/system/zones/brand/lx@$(PKGVERS)
+set name=pkg.description value="Support for the 'lx' Brand"
+set name=pkg.summary value="lx Brand"
+set name=info.classification \
+ value="org.opensolaris.category.2008:Applications/System Utilities"
set name=variant.arch value=i386
+dir path=etc group=sys
+dir path=etc/zones group=sys
+dir path=usr group=sys
+dir path=usr/kernel group=sys
+dir path=usr/kernel/brand group=sys
+dir path=usr/kernel/brand/$(ARCH64) group=sys
+dir path=usr/kernel/drv group=sys
+dir path=usr/kernel/drv/$(ARCH64) group=sys
+dir path=usr/kernel/dtrace group=sys
+dir path=usr/kernel/dtrace/$(ARCH64) group=sys
+dir path=usr/kernel/fs group=sys
+dir path=usr/kernel/fs/$(ARCH64) group=sys
+dir path=usr/kernel/strmod group=sys
+dir path=usr/kernel/strmod/$(ARCH64) group=sys
+dir path=usr/lib
+dir path=usr/lib/brand
+dir path=usr/lib/brand/lx
+dir path=usr/lib/brand/lx/$(ARCH64)
+dir path=usr/lib/brand/lx/distros
+dir path=usr/lib/devfsadm group=sys
+dir path=usr/lib/devfsadm/linkmod group=sys
+driver name=lx_audio
+driver name=lx_ptm perms="lx_ptmajor 0666 root sys"
+driver name=lx_systrace perms="* 0644 root sys"
+file path=etc/zones/SUNWlx.xml mode=0444
+file path=etc/zones/SUNWlx26.xml mode=0444
+file path=usr/kernel/brand/$(ARCH64)/lx_brand group=sys mode=0755
+file path=usr/kernel/brand/lx_brand group=sys mode=0755
+file path=usr/kernel/drv/$(ARCH64)/lx_audio group=sys
+file path=usr/kernel/drv/$(ARCH64)/lx_ptm group=sys
+file path=usr/kernel/drv/$(ARCH64)/lx_systrace group=sys
+file path=usr/kernel/drv/lx_audio group=sys
+file path=usr/kernel/drv/lx_audio.conf group=sys
+file path=usr/kernel/drv/lx_ptm group=sys
+file path=usr/kernel/drv/lx_ptm.conf group=sys
+file path=usr/kernel/drv/lx_systrace group=sys
+file path=usr/kernel/drv/lx_systrace.conf group=sys
+file path=usr/kernel/fs/$(ARCH64)/lx_afs group=sys mode=0755
+file path=usr/kernel/fs/$(ARCH64)/lx_proc group=sys mode=0755
+file path=usr/kernel/fs/lx_afs group=sys mode=0755
+file path=usr/kernel/fs/lx_proc group=sys mode=0755
+file path=usr/kernel/strmod/$(ARCH64)/ldlinux group=sys mode=0755
+file path=usr/kernel/strmod/ldlinux group=sys mode=0755
+file path=usr/lib/brand/lx/$(ARCH64)/lx_librtld_db.so.1
+file path=usr/lib/brand/lx/config.xml mode=0444
+file path=usr/lib/brand/lx/distros/centos35.distro mode=0444
+file path=usr/lib/brand/lx/distros/centos36.distro mode=0444
+file path=usr/lib/brand/lx/distros/centos37.distro mode=0444
+file path=usr/lib/brand/lx/distros/centos38.distro mode=0444
+file path=usr/lib/brand/lx/distros/rhel35.distro mode=0444
+file path=usr/lib/brand/lx/distros/rhel36.distro mode=0444
+file path=usr/lib/brand/lx/distros/rhel37.distro mode=0444
+file path=usr/lib/brand/lx/distros/rhel38.distro mode=0444
+file path=usr/lib/brand/lx/distros/rhel_centos_common mode=0444
+file path=usr/lib/brand/lx/etc_default_nfs group=sys mode=0444
+file path=usr/lib/brand/lx/lx_distro_install mode=0755
+file path=usr/lib/brand/lx/lxinit mode=0755
+file path=usr/lib/brand/lx/lx_init_zone mode=0755
+file path=usr/lib/brand/lx/lx_init_zone_debian mode=0755
+file path=usr/lib/brand/lx/lx_init_zone_redhat mode=0755
+file path=usr/lib/brand/lx/lx_install mode=0755
+file path=usr/lib/brand/lx/lx_librtld_db.so.1
+file path=usr/lib/brand/lx/lx_native mode=0755
+file path=usr/lib/brand/lx/lx_support mode=0755
+file path=usr/lib/brand/lx/platform.xml mode=0444
+file path=usr/lib/devfsadm/linkmod/SUNW_lx_link_$(ARCH).so group=sys
+file path=usr/lib/lx_brand.so.1
+hardlink path=usr/kernel/dtrace/$(ARCH64)/lx_systrace \
+ target=../../../kernel/drv/$(ARCH64)/lx_systrace
+hardlink path=usr/kernel/dtrace/lx_systrace \
+ target=../../kernel/drv/lx_systrace
+legacy pkg=SUNWlxr arch=$(ARCH) category=system \
+ desc="Support for the 'lx' Brand" \
+ hotline="Please contact your local service provider" \
+ name="lx Brand (Root)" vendor="Sun Microsystems, Inc." \
+ version=11.11,REV=2009.11.11
+legacy pkg=SUNWlxu arch=$(ARCH) category=system \
+ desc="Support for the 'lx' Brand" \
+ hotline="Please contact your local service provider" \
+ name="lx Brand (Usr)" vendor="Sun Microsystems, Inc." \
+ version=11.11,REV=2009.11.11
+license cr_Sun license=cr_Sun
+license lic_CDDL license=lic_CDDL
+link path=usr/lib/brand/lx/64 target=$(ARCH64)
diff --git a/usr/src/req.flg b/usr/src/req.flg
index 9c992b1120..f1e17bbd52 100644
--- a/usr/src/req.flg
+++ b/usr/src/req.flg
@@ -22,6 +22,7 @@
#
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2018 Joyent, Inc
#
# Copyright 2019 Joyent, Inc.
#
diff --git a/usr/src/stand/lib/fs/hsfs/hsfsops.c b/usr/src/stand/lib/fs/hsfs/hsfsops.c
index 0d908640f3..e6b0c1b6e3 100644
--- a/usr/src/stand/lib/fs/hsfs/hsfsops.c
+++ b/usr/src/stand/lib/fs/hsfs/hsfsops.c
@@ -1026,6 +1026,7 @@ boot_hsfs_getdents(int fd, struct dirent *dep, unsigned size)
* alignment.
*/
n = strlen(hdp->hs_ufs_dir.d_name);
+
n = roundup((sizeof (struct dirent) + ((n > SLOP) ? n : 0)),
sizeof (off_t));
diff --git a/usr/src/test/Makefile b/usr/src/test/Makefile
index c951638a63..412288d483 100644
--- a/usr/src/test/Makefile
+++ b/usr/src/test/Makefile
@@ -27,6 +27,7 @@ SUBDIRS = \
libmlrpc-tests \
net-tests \
os-tests \
+ smartos-test \
smbclient-tests \
smbsrv-tests \
test-runner \
diff --git a/usr/src/test/bhyve-tests/runfiles/default.run b/usr/src/test/bhyve-tests/runfiles/default.run
index 93101811a5..b59d10ba67 100644
--- a/usr/src/test/bhyve-tests/runfiles/default.run
+++ b/usr/src/test/bhyve-tests/runfiles/default.run
@@ -9,7 +9,10 @@
# http://www.illumos.org/license/CDDL.
#
+#
# Copyright 2022 Oxide Computer Company
+# Copyright 2022 MNX Cloud, Inc.
+#
[DEFAULT]
pre =
@@ -21,6 +24,7 @@ outputdir = /var/tmp/test_results
[/opt/bhyve-tests/tests/vmm]
user = root
+pre = vmm_drv_test_init
tests = [
'auto_destruct',
'cpuid_ioctl',
@@ -36,6 +40,7 @@ tests = [
'pause_resume',
'self_destruct'
]
+post = vmm_drv_test_fini
[/opt/bhyve-tests/tests/kdev]
user = root
diff --git a/usr/src/test/bhyve-tests/tests/vmm/Makefile b/usr/src/test/bhyve-tests/tests/vmm/Makefile
index e557aa2407..658a6b6b64 100644
--- a/usr/src/test/bhyve-tests/tests/vmm/Makefile
+++ b/usr/src/test/bhyve-tests/tests/vmm/Makefile
@@ -9,7 +9,10 @@
# http://www.illumos.org/license/CDDL.
#
+#
# Copyright 2022 Oxide Computer Company
+# Copyright 2022 MNX Cloud, Inc.
+#
include $(SRC)/cmd/Makefile.cmd
include $(SRC)/cmd/Makefile.cmd.64
@@ -30,6 +33,8 @@ PROG = mem_partial \
datarw_constraints \
pause_resume
+SCRIPT = vmm_drv_test_fini vmm_drv_test_init
+
COMMON_OBJS = common.o
CLEAN_OBJS = $(PROG:%=%.o)
@@ -37,6 +42,7 @@ ROOTOPTPKG = $(ROOT)/opt/bhyve-tests
TESTDIR = $(ROOTOPTPKG)/tests/vmm
CMDS = $(PROG:%=$(TESTDIR)/%)
+CMDS += $(SCRIPT:%=$(TESTDIR)/%)
$(CMDS) := FILEMODE = 0555
CSTD= $(CSTD_GNU99)
diff --git a/usr/src/test/bhyve-tests/tests/vmm/vmm_drv_test_fini b/usr/src/test/bhyve-tests/tests/vmm/vmm_drv_test_fini
new file mode 100755
index 0000000000..79f9b6bcfb
--- /dev/null
+++ b/usr/src/test/bhyve-tests/tests/vmm/vmm_drv_test_fini
@@ -0,0 +1,23 @@
+#!/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 2022 MNX Cloud, Inc.
+#
+
+if ! rem_drv vmm_drv_test; then
+ printf "failed to remove vmm_drv_test driver\n" 2>&1
+ exit 1
+fi
+
+exit 0
diff --git a/usr/src/test/bhyve-tests/tests/vmm/vmm_drv_test_init b/usr/src/test/bhyve-tests/tests/vmm/vmm_drv_test_init
new file mode 100755
index 0000000000..277538432b
--- /dev/null
+++ b/usr/src/test/bhyve-tests/tests/vmm/vmm_drv_test_init
@@ -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 2022 MNX Cloud, Inc.
+#
+
+if ! add_drv -m '* 0666 root sys' \
+ -p 'read_priv_set=sys_devices write_priv_set=sys_devices' vmm_drv_test; then
+ printf "failed to add vmm_drv_test driver\n" 2>&1
+ exit 1
+fi
+
+exit 0
diff --git a/usr/src/test/libc-tests/runfiles/default.run b/usr/src/test/libc-tests/runfiles/default.run
index 9f6269ec71..d5783fcc72 100644
--- a/usr/src/test/libc-tests/runfiles/default.run
+++ b/usr/src/test/libc-tests/runfiles/default.run
@@ -106,6 +106,8 @@ timeout = 600
[/opt/libc-tests/tests/catopen]
[/opt/libc-tests/tests/endian.32]
[/opt/libc-tests/tests/endian.64]
+[/opt/libc-tests/tests/env-OS-4089.32]
+[/opt/libc-tests/tests/env-OS-4089.64]
[/opt/libc-tests/tests/env-7076.32]
[/opt/libc-tests/tests/env-7076.64]
[/opt/libc-tests/tests/err/err.ksh]
diff --git a/usr/src/test/libc-tests/tests/Makefile b/usr/src/test/libc-tests/tests/Makefile
index 16c002e0ef..e78af2e316 100644
--- a/usr/src/test/libc-tests/tests/Makefile
+++ b/usr/src/test/libc-tests/tests/Makefile
@@ -36,6 +36,7 @@ SUBDIRS = \
wctype
PROGS = \
+ env-OS-4089 \
aligned_alloc \
asprintf-14933 \
c11_threads \
diff --git a/usr/src/test/libc-tests/tests/env-OS-4089.c b/usr/src/test/libc-tests/tests/env-OS-4089.c
new file mode 100644
index 0000000000..0f52201c79
--- /dev/null
+++ b/usr/src/test/libc-tests/tests/env-OS-4089.c
@@ -0,0 +1,73 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * Regression test for OS-4089 where doing a putenv() call without an '=' sign
+ * may lead to a segmentation fault when doing a getenv() depending on the
+ * circumstances of the environment's layout. Verify putenv() mimics
+ * unsetenv().
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/debug.h>
+
+int
+main(void)
+{
+ if (putenv("FOO=bar") != 0) {
+ fprintf(stderr, "failed to put FOO into the environment: %s\n",
+ strerror(errno));
+ return (1);
+ }
+
+ if (getenv("FOO") == NULL) {
+ fprintf(stderr, "failed to retrieve FOO from the "
+ "environment!\n");
+ return (1);
+ }
+
+ VERIFY0(unsetenv("FOO"));
+
+ if (getenv("FOO") != NULL) {
+ fprintf(stderr, "Somehow retrieved FOO from the "
+ "environment after unsetenv()!\n");
+ return (1);
+ }
+
+ if (putenv("FOO=bar") != 0) {
+ fprintf(stderr, "failed to put FOO into the environment: %s\n",
+ strerror(errno));
+ return (1);
+ }
+
+ if (getenv("FOO") == NULL) {
+ fprintf(stderr, "failed to retrieve FOO from the "
+ "environment!\n");
+ return (1);
+ }
+
+ VERIFY0(putenv("FOO"));
+
+ if (getenv("FOO") != NULL) {
+ fprintf(stderr, "Somehow retrieved FOO from the "
+ "environment after putenv()!\n");
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/libc-tests/tests/random/Makefile b/usr/src/test/libc-tests/tests/random/Makefile
index ed480dacb9..c1a1a18e1f 100644
--- a/usr/src/test/libc-tests/tests/random/Makefile
+++ b/usr/src/test/libc-tests/tests/random/Makefile
@@ -17,7 +17,6 @@ include $(SRC)/Makefile.master
ROOTOPTPKG = $(ROOT)/opt/libc-tests
TESTDIR = $(ROOTOPTPKG)/tests/random
-ROOTBINDIR = $(ROOTOPTPKG)/bin
PROGS = arc4random \
arc4random_prefork \
@@ -74,17 +73,12 @@ arc4random_preforksig: arc4random_forksig.c
$(POST_PROCESS)
chacha: chacha_tv.c
- $(COMPILE.c) -DKEYSTREAM_ONLY -I$(SRC)/common/crypto/chacha -o chacha.o -c $(SRC)/common/crypto/chacha/chacha.c
+ $(COMPILE.c) -DKEYSTREAM_ONLY -I$(SRC)/common/crypto/chacha \
+ -o chacha.o -c $(SRC)/common/crypto/chacha/chacha.c
$(COMPILE.c) -I$(SRC)/common/crypto/chacha -o chacha_tv.o -c chacha_tv.c
$(LINK.c) -o $@ chacha_tv.o chacha.o $(LDLIBS)
$(POST_PROCESS)
-$(ROOTBINDIR):
- $(INS.dir)
-
-$(ROOTBINDIR)/%: %
- $(INS.file)
-
$(TESTDIR):
$(INS.dir)
diff --git a/usr/src/test/libc-tests/tests/random/chacha_tv.c b/usr/src/test/libc-tests/tests/random/chacha_tv.c
index 636ee114c8..a02c2905ab 100644
--- a/usr/src/test/libc-tests/tests/random/chacha_tv.c
+++ b/usr/src/test/libc-tests/tests/random/chacha_tv.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright (c) 2015, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -285,7 +285,7 @@ chacha_test(uint8_t *key, uint8_t keylen, uint8_t *iv, uint8_t *bl0,
bzero(res0, sizeof (res0));
bzero(res1, sizeof (res1));
chacha_keysetup(&ctx, key, keylen * 8, 0);
- chacha_ivsetup(&ctx, iv);
+ chacha_ivsetup(&ctx, iv, NULL);
chacha_encrypt_bytes(&ctx, res0, res0, sizeof (res0));
chacha_encrypt_bytes(&ctx, res1, res1, sizeof (res1));
ret = bcmp(res0, bl0, sizeof (res0));
diff --git a/usr/src/test/os-tests/runfiles/default.run b/usr/src/test/os-tests/runfiles/default.run
index de2b29f2f1..d432e7be17 100644
--- a/usr/src/test/os-tests/runfiles/default.run
+++ b/usr/src/test/os-tests/runfiles/default.run
@@ -26,7 +26,7 @@ timeout = 60
post =
outputdir = /var/tmp/test_results
-[/opt/os-tests/tests/poll_test]
+[/opt/os-tests/tests/poll]
user = root
tests = ['poll_test', 'epoll_test']
@@ -64,6 +64,10 @@ tests = ['sigqueue_queue_size']
user = root
tests = ['sdevfs_eisdir']
+[/opt/os-tests/tests/tmpfs]
+user = root
+tests = ['tmpfs_badmount', 'tmpfs_enospc']
+
[/opt/os-tests/tests/stress]
user = root
tests = ['dladm-kstat']
@@ -88,6 +92,9 @@ user = root
timeout = 180
tests = ['acquire-compare', 'kmc-update', '15146']
+[/opt/os-tests/tests/OS-6097.32]
+[/opt/os-tests/tests/OS-6097.64]
+
[/opt/os-tests/tests/ddi_ufm]
user = root
tests = ['ufm-test-setup', 'ufm-test', 'ufm-test-cleanup']
diff --git a/usr/src/test/os-tests/tests/Makefile b/usr/src/test/os-tests/tests/Makefile
index a30832e8ee..613dd50f8c 100644
--- a/usr/src/test/os-tests/tests/Makefile
+++ b/usr/src/test/os-tests/tests/Makefile
@@ -39,6 +39,7 @@ SUBDIRS = \
stress \
syscall \
timer \
+ tmpfs \
uccid \
$(SUBDIRS_$(MACH))
@@ -46,6 +47,7 @@ PROGS = \
clock_gettime \
eventfd \
odirectory \
+ OS-6097 \
ucontext \
writev
@@ -61,6 +63,9 @@ ROOTOPTPROGS = $(PROGS32:%=$(ROOTOPTDIR)/%) \
odirectory.32 := LDLIBS += -lsocket
odirectory.64 := LDLIBS64 += -lsocket
+OS-6097.32 := LDLIBS += -ldlpi
+OS-6097.64 := LDLIBS64 += -ldlpi
+
clock_gettime.32 := LDLIBS += -lproc
clock_gettime.32 := CSTD = $(CSTD_GNU99)
clock_gettime.64 := LDLIBS64 += -lproc
diff --git a/usr/src/test/os-tests/tests/OS-6097.c b/usr/src/test/os-tests/tests/OS-6097.c
new file mode 100644
index 0000000000..160c01e9c7
--- /dev/null
+++ b/usr/src/test/os-tests/tests/OS-6097.c
@@ -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 (c) 2017, Joyent, Inc.
+ */
+
+/*
+ * Regression test for OS-6097.
+ */
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <libdlpi.h>
+#include <sys/debug.h>
+
+int
+main(void)
+{
+ int ret;
+ char path[4096];
+ uint_t num = 4294967294U;
+ dlpi_handle_t dh;
+
+ /*
+ * First, we need to determine a path that doesn't exist to trigger this
+ * bug. We start with the highest possible number and just decrement
+ * until we find something.
+ */
+
+ while (num > 0) {
+ struct stat st;
+
+ (void) snprintf(path, sizeof (path), "/dev/net/net%u", num);
+
+ ret = stat(path, &st);
+ if (ret == -1 && errno == ENOENT)
+ break;
+ if (ret == -1) {
+ (void) fprintf(stderr, "test failed: unexpected error "
+ "running stat(2) on %s: %s\n", path,
+ strerror(errno));
+ return (1);
+ }
+ num--;
+ }
+
+ /*
+ * While technically this is a valid entry that we could try, at this
+ * point we've exhausted so many NICs, there's likely a bug.
+ */
+ if (num == 0) {
+ (void) fprintf(stderr, "failed to construct a non-existent "
+ "NIC with a name starting with 'net'\n");
+ return (1);
+ }
+
+ (void) snprintf(path, sizeof (path), "net%u", num);
+ ret = dlpi_open(path, &dh, 0);
+ VERIFY3U(ret, ==, DLPI_ENOLINK);
+ return (0);
+}
diff --git a/usr/src/test/os-tests/tests/file-locking/Makefile b/usr/src/test/os-tests/tests/file-locking/Makefile
index 2f8c6762ad..cd0562777d 100644
--- a/usr/src/test/os-tests/tests/file-locking/Makefile
+++ b/usr/src/test/os-tests/tests/file-locking/Makefile
@@ -10,7 +10,7 @@
#
#
-# Copyright 2016 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
#
include $(SRC)/cmd/Makefile.cmd
@@ -28,22 +28,21 @@ SRCS = $(PROGS:%=%.c) $(UTILS:%.o=%.c)
PROGS32 = $(PROGS:%=%.32)
PROGS64 = $(PROGS:%=%.64)
-LINTS = $(PROGS:%=%.ln)
-LINTFLAGS += -erroff=E_NAME_DEF_NOT_USED2
-LINTFLAGS += -erroff=E_NAME_USED_NOT_DEF2
-
ROOTOPTDIR = $(ROOT)/opt/os-tests/tests/file-locking
ROOTOPTPROGS = $(PROGS32:%=$(ROOTOPTDIR)/%) \
$(PROGS64:%=$(ROOTOPTDIR)/%)
+all := TARGET = all
+install := TARGET = install
+clean := TARGET = clean
+clobber := TARGET = clobber
+
.KEEP_STATE:
install: $(ROOTOPTPROGS)
all: $(PROGS32) $(PROGS64)
-lint: $(LINTS)
-
clean:
-rm $(PROGS32) $(PROGS64)
@@ -58,9 +57,6 @@ $(ROOTOPTDIR)/%: %
$(ROOTOPTDIR)/%: %.ksh
$(INS.rename)
-%.ln: %.c
- $(LINT.c) $< $(UTILS) $(LDLIBS)
-
%.64.o: %.c
$(COMPILE64.c) $< -o $@
diff --git a/usr/src/test/os-tests/tests/poll/Makefile b/usr/src/test/os-tests/tests/poll/Makefile
index ae416f9628..feffc744fb 100644
--- a/usr/src/test/os-tests/tests/poll/Makefile
+++ b/usr/src/test/os-tests/tests/poll/Makefile
@@ -11,7 +11,7 @@
#
# Copyright (c) 2012 by Delphix. All rights reserved.
-# Copyright 2016 Joyent, Inc.
+# Copyright 2017 Joyent, Inc.
#
include $(SRC)/cmd/Makefile.cmd
@@ -26,7 +26,7 @@ poll_test.ln := LDLIBS += -lsocket
CSTD = $(CSTD_GNU99)
ROOTOPTPKG = $(ROOT)/opt/os-tests
-TESTDIR = $(ROOTOPTPKG)/tests
+TESTDIR = $(ROOTOPTPKG)/tests/poll
CMDS = $(PROG:%=$(TESTDIR)/%)
$(CMDS) := FILEMODE = 0555
diff --git a/usr/src/test/os-tests/tests/secflags/secflags_zonecfg.sh b/usr/src/test/os-tests/tests/secflags/secflags_zonecfg.sh
index 3ea807f9ae..699b4fe04a 100644
--- a/usr/src/test/os-tests/tests/secflags/secflags_zonecfg.sh
+++ b/usr/src/test/os-tests/tests/secflags/secflags_zonecfg.sh
@@ -12,14 +12,20 @@
#
# Copyright 2015, Richard Lowe.
+# Copyright 2019 Joyent, Inc.
# Verify that zones can be configured with security-flags
LC_ALL=C # Collation is important
+IS_SMARTOS=$(uname -v | grep ^joyent_)
+if [[ -z "$IS_SMARTOS" ]]; then
+ create_flag="-b"
+fi
+
expect_success() {
name=$1
- (echo "create -b";
+ (echo "create $create_flag";
echo "set zonepath=/$name.$$";
cat /dev/stdin;
echo "verify";
@@ -46,7 +52,7 @@ expect_fail() {
name=$1
expect=$2
- (echo "create -b";
+ (echo "create $create_flag";
echo "set zonepath=/$name.$$";
cat /dev/stdin;
echo "verify";
diff --git a/usr/src/test/os-tests/tests/tmpfs/Makefile b/usr/src/test/os-tests/tests/tmpfs/Makefile
new file mode 100644
index 0000000000..d6515b38fa
--- /dev/null
+++ b/usr/src/test/os-tests/tests/tmpfs/Makefile
@@ -0,0 +1,52 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/os-tests
+TESTDIR = $(ROOTOPTPKG)/tests/tmpfs
+
+PROGS = tmpfs_full
+SCRIPTS = tmpfs_badmount \
+ tmpfs_enospc
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+CMDS = $(PROGS:%=$(TESTDIR)/%) $(SCRIPTS:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+all: $(PROGS)
+
+install: all $(CMDS)
+
+lint:
+
+clobber: clean
+ -$(RM) $(PROGS)
+
+clean:
+ -$(RM) *.o
+
+$(CMDS): $(TESTDIR) $(PROGS)
+
+$(TESTDIR):
+ $(INS.dir)
+
+$(TESTDIR)/%: %.ksh
+ $(INS.rename)
+
+$(TESTDIR)/%: %
+ $(INS.file)
diff --git a/usr/src/test/os-tests/tests/tmpfs/tmpfs_badmount.ksh b/usr/src/test/os-tests/tests/tmpfs/tmpfs_badmount.ksh
new file mode 100644
index 0000000000..7e2c4a6095
--- /dev/null
+++ b/usr/src/test/os-tests/tests/tmpfs/tmpfs_badmount.ksh
@@ -0,0 +1,114 @@
+#!/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 2016 Joyent, Inc.
+#
+
+#
+# Test various options to try and mount a tmpfs. Aside from the first to
+# verify that we can mount tmpfs at all, these should all fail.
+#
+
+tb_arg0=$(basename $0)
+tb_mountpoint="/var/tmp/$0.$$"
+tb_mount=/usr/sbin/mount
+tb_umount=/usr/sbin/umount
+
+function fatal
+{
+ rmdir $tb_mountpoint
+ typeset msg="$*"
+ [[ -z "$msg" ]] && msg="test failed"
+ echo "$tb_arg0: test failed $msg" >&2
+ exit 1
+}
+
+function check_mount
+{
+ mkdir -p $tb_mountpoint || fatal \
+ "failed to make mountpoint $tb_mountpoint"
+ $tb_mount -F tmpfs swap $tb_mountpoint || fatal \
+ "failed to mount tmpfs, check user perms"
+ $tb_umount $tb_mountpoint || fatal \
+ "failed to unmount test point"
+}
+
+function test_one
+{
+ typeset opts=$1
+
+ [[ -z "$opts" ]] && fatal "missing required opts"
+ $tb_mount -F tmpfs -o $opts swap $tb_mountpoint 2>/dev/null
+ if [[ $? -eq 0 ]]; then
+ $tb_umount $tb_mountpoint
+ fatal "successfully mounted with opts $opts, expected failure"
+ fi
+}
+
+check_mount
+
+#
+# Test invalid percentages.
+#
+test_one "size=-5%"
+test_one "size=200%"
+test_one "size=55.55555%"
+test_one "size=100.0%"
+test_one "size=bad%"
+test_one "size=30g%"
+test_one "size=%"
+test_one "size=%wat"
+
+#
+# Test invalid sizes. Only kmg are valid prefixes.
+#
+test_one "size=hello;world"
+test_one "size=0xnope"
+test_one "size=3.14g"
+test_one "size=3;14"
+test_one "size=thisisanormalsize"
+test_one "size="
+test_one "size=100mtry"
+
+#
+# Now, we need to try and trigger a bunch of overflow. We're going to do
+# this assuming we're on a 64-bit kernel (which will always overflow a
+# 32-bit kernel).
+#
+test_one "size=20000000000000000000"
+test_one "size=1ggggggggggggggggggg"
+test_one "size=1mmmmmmmmmmmmmmmmmmm"
+test_one "size=1kkkkkkkkkkkkkkkkkkk"
+test_one "size=1kkkkkkkkkkkkkkkkkkk"
+test_one "size=18014398509481983k"
+test_one "size=17592186044416m"
+test_one "size=17179869185g"
+test_one "size=17179869184g"
+
+#
+# Let's throw a couple bad modes around while we're here.
+#
+test_one "mode=17777"
+test_one "mode=27777"
+test_one "mode=37777"
+test_one "mode=47777"
+test_one "mode=57777"
+test_one "mode=67777"
+test_one "mode=77777"
+test_one "mode=87777"
+test_one "mode=97777"
+test_one "mode=asdf"
+test_one "mode=deadbeef"
+test_one "mode=kefka"
+
+rmdir $tb_mountpoint
diff --git a/usr/src/test/os-tests/tests/tmpfs/tmpfs_enospc.ksh b/usr/src/test/os-tests/tests/tmpfs/tmpfs_enospc.ksh
new file mode 100644
index 0000000000..a285f306e2
--- /dev/null
+++ b/usr/src/test/os-tests/tests/tmpfs/tmpfs_enospc.ksh
@@ -0,0 +1,74 @@
+#!/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 2016 Joyent, Inc.
+#
+
+#
+# Verify that if we fill up a tmpfs that we can't then perform
+# additional things to it that would result in the creation or use of
+# kernel memory.
+#
+
+te_arg0=$(basename $0)
+te_root=$(dirname $0)
+te_bin=$te_root/tmpfs_full
+te_mountpoint="/var/tmp/$0.$$"
+te_mount=/usr/sbin/mount
+te_umount=/usr/sbin/umount
+te_testfile=1m
+te_mounted=
+te_exit=1
+
+function fatal
+{
+ [[ -n "$te_mounted" ]] && $te_umount $te_mountpoint
+ rmdir $te_mountpoint
+ typeset msg="$*"
+ [[ -z "$msg" ]] && msg="test failed"
+ echo "$te_arg0: test failed $msg" >&2
+ exit 1
+}
+
+function setup
+{
+ typeset ofile=$te_mountpoint/$te_testfile
+
+ mkdir -p $te_mountpoint || fatal \
+ "failed to make mountpoint $te_mountpoint"
+ $te_mount -F tmpfs swap $te_mountpoint || fatal \
+ "failed to mount tmpfs, check user perms"
+ te_mounted=1
+ dd if=/dev/zero of=$ofile bs=1M count=1 2>/dev/null || fatal \
+ "failed to create a 1 MB file"
+ $te_mount -F tmpfs -o remount,size=512k swap $te_mountpoint ||
+ fatal "failed to remount tmpfs"
+}
+
+function run_test
+{
+ $te_bin $te_mountpoint $te_testfile || fatal "$te_bin failed"
+}
+
+function cleanup
+{
+ te_mounted=
+ $te_umount $te_mountpoint || fatal "failed to unmount $te_mountpoint"
+ rmdir $te_mountpoint || fatal "failed to remove $te_mountpoint"
+}
+
+setup
+run_test
+cleanup
+
+exit 0
diff --git a/usr/src/test/os-tests/tests/tmpfs/tmpfs_full.c b/usr/src/test/os-tests/tests/tmpfs/tmpfs_full.c
new file mode 100644
index 0000000000..6c6037710b
--- /dev/null
+++ b/usr/src/test/os-tests/tests/tmpfs/tmpfs_full.c
@@ -0,0 +1,94 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * Given a path to a tmpfs that has already been marked as full, attempt to
+ * perform certain activities on it, all of which should fail with ENOSPC.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <strings.h>
+#include <sys/debug.h>
+#include <unistd.h>
+
+int
+main(int argc, const char *argv[])
+{
+ int fd, ret;
+ struct statvfs vfs;
+
+ if (argc != 3) {
+ fprintf(stderr, "test failed: missing path or file\n");
+ return (1);
+ }
+
+ if ((fd = open(argv[1], O_RDONLY)) < 0) {
+ fprintf(stderr, "test failed: failed to open root %s: %s\n",
+ argv[1], strerror(errno));
+ return (1);
+ }
+
+ if (fstatvfs(fd, &vfs) != 0) {
+ fprintf(stderr, "test failed: failed to stat vfs for %s: %s\n",
+ argv[1], strerror(errno));
+ return (1);
+ }
+
+ if (strncmp("tmpfs", vfs.f_basetype, FSTYPSZ) != 0) {
+ fprintf(stderr, "test failed: asked to run on non-tmpfs\n");
+ return (1);
+ }
+
+ /*
+ * Once a few additional bugs in tmpfs are fixed, we should double check
+ * and make sure that the free space here is actually zero before
+ * continuing.
+ */
+
+ /*
+ * Go through operations that would create nodes and make sure that they
+ * all fail.
+ */
+
+ ret = openat(fd, "Mnemosyne", O_RDWR | O_CREAT, 0755);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, ENOSPC);
+
+ ret = mkdirat(fd, "Euterpe", 0775);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, ENOSPC);
+
+ ret = symlinkat("/dev/null", fd, "Melpomene");
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, ENOSPC);
+
+ ret = linkat(fd, argv[2], fd, "Urania", 0);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, ENOSPC);
+
+ /*
+ * Make sure we can't create open extended attributes.
+ */
+ ret = openat(fd, "Lethe", O_RDWR | O_CREAT | O_XATTR);
+ VERIFY3S(ret, ==, -1);
+ VERIFY3S(errno, ==, ENOSPC);
+
+ return (0);
+}
diff --git a/usr/src/test/smartos-test/Makefile b/usr/src/test/smartos-test/Makefile
new file mode 100644
index 0000000000..1277987fac
--- /dev/null
+++ b/usr/src/test/smartos-test/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 2019 Joyent, Inc.
+#
+
+include $(SRC)/Makefile.master
+include ../Makefile.com
+
+PROGS= smartos-test
+FILES= README
+
+ROOTOPTPKG = $(ROOT)/opt/smartos-test
+ROOTBIN = $(ROOTOPTPKG)/bin
+
+ROOTPROGS= $(PROGS:%=$(ROOTBIN)/%)
+$(ROOTPROGS) := FILEMODE = 0555
+
+ROOTFILES= $(FILES:%=$(ROOTOPTPKG)/%)
+$(ROOTFILES) := FILEMODE = 0444
+
+all clean clobber:
+
+install: $(ROOTPROGS) $(ROOTFILES)
+
+$(ROOTPROGS): $(ROOTBIN)
+
+$(ROOTFILES): $(ROOTOPTPKG)
+
+$(ROOTBIN):
+ $(INS.dir)
+
+$(ROOTOPTPKG):
+ $(INS.dir)
+
+$(ROOTOPTPKG)/%: %
+ $(INS.file)
+
+$(ROOTBIN)/%: %.sh
+ $(INS.rename)
diff --git a/usr/src/test/smartos-test/README b/usr/src/test/smartos-test/README
new file mode 100644
index 0000000000..7e38905729
--- /dev/null
+++ b/usr/src/test/smartos-test/README
@@ -0,0 +1,103 @@
+#
+# 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 2019 Joyent. Inc.
+#
+
+Strictly speaking, this is not a set of tests. Rather, it is a wrapper that
+automates the configuration of a SmartOS system to prepare it for test
+execution, optionally running those tests.
+
+The smartos-test script should be extracted from the test archive to ensure
+the correct version is being executed.
+
+For example:
+
+[root@kura /var/tmp]# tar zvxf tests-test_archive-master-20191001T134222Z.tgz ./opt/smartos-test
+Decompressing 'tests-test_archive-master-20191001T134222Z.tgz' with '/usr/bin/gzcat'...
+x ./opt/smartos-test, 0 bytes, 0 tape blocks
+x ./opt/smartos-test/README, 958 bytes, 2 tape blocks
+x ./opt/smartos-test/bin, 0 bytes, 0 tape blocks
+x ./opt/smartos-test/bin/smartos-test, 10062 bytes, 20 tape blocks
+[root@kura /var/tmp]# ./opt/smartos-test/bin/smartos-test -h
+Usage: smartos-test [-h] [-c] [-e] [-r] [-w] <path to tests.tgz>
+
+At least one of -c, -e, -r is required.
+
+ -h print usage
+ -c configure the system for testing
+ -e execute known tests
+ -f skip the check to ensure platform version == test version
+ -r snapshot or rollback to zones/opt@system-test-smartos-test
+ before doing any system configuration or test execution
+ -w when mounting the lofs /usr, make it writable
+
+
+Specifically, the script will:
+
+* verify that the user has declared that no production data exists on the
+ test system
+* verify we're running in the global zone
+* verify the test archive version matches the version of the platform we're
+ running
+* optionally snapshot or rollback /opt to "zones/opt@system-test-smartos-test"
+* setup loopback mounts for any files from the smartos "tests-[stamp].tgz"
+ file that need to be installed to a normally read-only location, and
+ extract the portions of test archive that must appear in /usr
+* extract the remaining test archive contents to /opt and /kernel
+* configure pkgsrc
+* install required test packages
+* execute tests that should all pass
+
+Over time, we hope to add to the set of tests that are executed.
+
+After configuring the system for testing, you may choose to run individual
+test suites, for example:
+
+ # /opt/util-tests/bin/utiltest
+or
+ # /opt/os-tests/bin/ostest
+
+
+A note on ZFS testing:
+
+At present, this wrapper does *not* execute the ZFS tests, since they're
+more invasive than the other packaged tests. To run those, after configuring
+the system for testing, change to the 'ztest' user and set environment
+variables.
+
+In this example, DISKS is the list of three unused disks that will be used
+to run the tests. KEEP is a list of zpools that the tests should *not*
+destroy:
+
+ # su ztest
+ $ export DISKS='c2t1d0 c2t2d0 c2t3d0'
+ $ export KEEP='zones'
+ $ /opt/zfs-tests/bin/zfstest
+
+Beware that data loss will occur for any data residing on DISKS and failure to
+specify a KEEP pool may also lead to data loss.
+
+
+A note for SmartOS developers:
+
+The test archive .tgz file is built by the 'tests-tar' Makefile target in
+smartos-live.git.
+
+The manifest of files included in the archive is generated at build-time by
+parsing the IPS package manifests from $SRC/pkg/manifests that are listed in
+$SRC/Makefile.testarchive in this repository.
+
+It is important when adding or removing tests that the IPS manifests are
+updated. If new test packages are created, they should be added to
+$SRC/Makefile.testarchive.
+
diff --git a/usr/src/test/smartos-test/smartos-test.sh b/usr/src/test/smartos-test/smartos-test.sh
new file mode 100755
index 0000000000..cc2055dc8f
--- /dev/null
+++ b/usr/src/test/smartos-test/smartos-test.sh
@@ -0,0 +1,444 @@
+#! /usr/bin/bash
+#
+#
+# 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 2020 Joyent, Inc.
+# Copyright 2022 MNX Cloud, Inc.
+#
+
+#
+# This script is designed to run on an (effectively) disposable SmartOS
+# install to configure the system, install a series of tests from the
+# smartos-gate, and execute them.
+# It exits 1 if any configuration, setup or test fails.
+#
+
+export PATH=/usr/bin:/usr/sbin:/opt/tools/sbin:/opt/tools/bin:$PATH
+
+# The pkgsrc packages we will install.
+export SMARTOS_TEST_PKGS="
+ python39
+ sudo
+ coreutils
+ gcc10
+ gmake
+"
+
+#
+# Set $KEEP as a precaution in case we ever end up running the zfs-test suite
+# by accident or design. This ensures it never attempts to destroy the 'zones'
+# zpool. Note that the ZFS test suite also wants DISKS set to the disks which
+# it can create/destroy pools on, but we're not computing that here.
+#
+if [[ -z "$KEEP" ]]; then
+ export KEEP="zones"
+fi
+
+#
+# Accumulate test suite exit codes and a list of failed tests
+#
+RESULT=0
+FAILED_TESTS=""
+
+function fatal {
+ echo "ERROR: $@"
+ exit 1
+}
+
+function warn {
+ echo "WARNING: $@"
+}
+
+function log {
+ echo "$@"
+}
+
+function log_must {
+ echo "Running $@"
+ $@ || fatal "Error running command."
+}
+
+function log_test {
+ echo ""
+ TEST_NAME=$1
+ shift
+ echo "Starting test for $TEST_NAME with $@"
+ $@
+ TEST_RESULT=$?
+ if [[ $TEST_RESULT -ne 0 ]]; then
+ FAILED_TESTS="$FAILED_TESTS $TEST_NAME"
+ fi
+ RESULT=$(( $RESULT + $TEST_RESULT ))
+}
+
+function log_testrunner {
+ echo ""
+ TEST_NAME=$1
+ shift
+ echo "Starting test-runner for $TEST_NAME with $@"
+ /opt/test-runner/bin/run -c $@
+ TEST_RESULT=$?
+ if [[ $TEST_RESULT -ne 0 ]]; then
+ FAILED_TESTS="$FAILED_TESTS $TEST_NAME"
+ fi
+ RESULT=$(( $RESULT + $TEST_RESULT ))
+ # test-runner's default log dirs use a timestamp at per-second granularity.
+ # Sleep here to ensure a unique timestamp per run if consecutive tests
+ # bail out early.
+ sleep 1
+}
+
+function guard_production_data {
+
+ if [[ ! -f "/lib/sdc/.sdc-test-no-production-data" ]]; then
+ cat <<EOF
+To setup and run these tests you must create the file:
+ /lib/sdc/.sdc-test-no-production-data
+after ensuring you have no production data on this system.
+EOF
+ exit 1
+ fi
+}
+
+function zone_check {
+ if [[ $(zonename) != "global" ]]; then
+ fatal "these tests must be run from the global zone."
+ fi
+}
+
+#
+# Check that the tests.buildstamp file in the test archive matches
+# the current platform stamp. Running tests designed for a platform
+# that we're not running is a bad idea.
+#
+function version_check {
+ PLATFORM_VERSION=$(uname -v | sed -e 's/^joyent_//g')
+ mkdir -p /tmp/version_check.$$
+ tar xzf $1 -C /tmp/version_check.$$ ./tests.buildstamp
+ TESTS_VERSION=$(cat /tmp/version_check.$$/tests.buildstamp)
+ rm -rf /tmp/version_check.$$
+ log "Platform version: $PLATFORM_VERSION"
+ log " Tests version: $TESTS_VERSION"
+ if [[ "$PLATFORM_VERSION" != "$TESTS_VERSION" ]]; then
+ fatal "mismatched platform version and tests version!"
+ fi
+}
+
+function snapshot_rollback_opt {
+ snapshot="system-test-smartos-test"
+ has_snapshot=$(zfs list zones/opt@$snapshot 2> /dev/null)
+ if [[ -n "$has_snapshot" ]]; then
+ log_must zfs rollback zones/opt@$snapshot
+ else
+ log_must zfs snapshot zones/opt@$snapshot
+ fi
+}
+
+#
+# Since some tests want to deliver to /usr which is read-only on SmartOS,
+# we make a temporary directory, dump the current /usr there, extract our
+# content to it, then lofs-mount it over the real thing.
+#
+function add_loopback_mounts {
+ test_archive=$1
+ lofs_home=/var/tmp/smartos-test-loopback
+
+ # If /usr is already lofs mounted, and pointing at $lofs_home, just
+ # extract our new test bits on top. Ideally we'd just unmount it,
+ # but while running this script, there's a good chance that the dataset
+ # will be busy and the umount would fail.
+ FS=$(/bin/df -n /usr | awk '{print $NF'})
+ if [[ "$FS" == "lofs" ]]; then
+ is_test_lofs=$(mount | grep ^/usr | grep "$lofs_home/usr ")
+ if [[ -z "$is_test_lofs" ]]; then
+ fatal "unsupported: existing lofs mount for /usr is not $lofs_home"
+ else
+ log "Extracting new test archive to lofs-mounted /usr"
+ # extract the current test archive to it
+ log_must tar -xzf $test_archive -C $lofs_home ./usr
+ fi
+ # Otherwise, setup a lofs mount for it.
+ else
+ log "Creating new lofs mount for /usr on $lofs_home"
+ rm -rf $lofs_home
+ mkdir -p $lofs_home
+ find /usr | cpio -pdum $lofs_home
+ log_must tar -xzf $test_archive -C $lofs_home ./usr
+ # keep /usr read-only in an attempt to preserve smartos behaviour
+ # unless specifically asked to
+ if [[ "$mount_usr_rw" = "true" ]]; then
+ mount_opts="-o rw"
+ else
+ mount_opts="-o ro"
+ fi
+ log_must mount -O -F lofs $mount_opts $lofs_home/usr /usr
+ fi
+}
+
+#
+# Extract the non-/usr parts of the test archive
+#
+function extract_remaining_test_bits {
+ log_must tar -xzf $1 -C / \
+ ./opt ./kernel ./tests.manifest.gen ./tests.buildstamp
+}
+
+function setup_pkgsrc {
+
+ if [[ -f /opt/tools/etc/pkgin/repositories.conf ]]; then
+ log "Pkgsrc bootstrap already setup, continuing"
+ return
+ fi
+
+ # We should always use the same pkgsrc version as we have installed
+ # on the build machine in case any of our tests link against libraries
+ # in /opt/tools
+ PKGSRC_STEM="https://pkgsrc.smartos.org/packages/SmartOS/bootstrap"
+ BOOTSTRAP_TAR="bootstrap-2021Q4-tools.tar.gz"
+ BOOTSTRAP_SHA="c427cb1ed664fd161d8e12c5191adcae7aee68b4"
+
+ # Ensure we are in a directory with enough space for the bootstrap
+ # download, by default the SmartOS /root directory is limited to the size
+ # of the ramdisk.
+ cd /var/tmp
+
+ # Download the bootstrap kit to the current directory. Note that we
+ # currently pass "-k" to skip SSL certificate checks as the GZ doesn't
+ # install them.
+ log_must curl -kO ${PKGSRC_STEM}/${BOOTSTRAP_TAR}
+
+ # Verify the SHA1 checksum.
+ [[ "${BOOTSTRAP_SHA}" = "$(/bin/digest -a sha1 ${BOOTSTRAP_TAR})" ]] || \
+ fatal "checksum failure for ${BOOTSTRAP_TAR}, expected ${BOOTSTRAP_SHA}"
+
+ # Install bootstrap kit to /opt/tools
+ log_must tar -zxpf ${BOOTSTRAP_TAR} -C /
+}
+
+function install_required_pkgs {
+
+ log_must pkgin -y in ${SMARTOS_TEST_PKGS}
+}
+
+function add_test_accounts {
+
+ grep -q '^cyrus:' /etc/passwd
+ if [[ $? -ne 0 ]]; then
+ log "Adding cyrus user"
+ echo "cyrus:x:977:1::/zones/global/cyrus:/bin/sh" >> /etc/passwd
+ if ! grep -q '^cyrus:' /etc/shadow; then
+ echo "cyrus:*LK*:::::::" >> /etc/shadow
+ fi
+ mkdir -p /zones/global/cyrus
+ chown cyrus /zones/global/cyrus
+ fi
+ grep -q '^ztest:' /etc/passwd
+ if [[ $? -ne 0 ]]; then
+ log "Adding ztest user"
+ echo "ztest:x:978:1::/zones/global/ztest:/bin/sh" >> /etc/passwd
+ if ! grep -q '^ztest:' /etc/shadow; then
+ # For sudo to work, the ztest account must not be locked
+ echo "ztest:NP:::::::" >> /etc/shadow
+ fi
+ mkdir -p /zones/global/ztest
+ chown ztest /zones/global/ztest
+ zprofile=/zones/global/ztest/.profile
+ if [[ ! -f $zprofile ]]; then
+ cat > $zprofile <<-EOF
+PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/tools/bin:/opt/tools/sbin:/opt/zfs-tests/bin
+export PATH
+
+KEEP="zones"
+export KEEP
+EOF
+
+ if [[ -n "$DISKS" ]]; then
+ # NOTE: This will be enough to make this script's execute-tests
+ # invocation run the ZFS test suite.
+ echo "DISKS=\"$DISKS\"" >> $zprofile
+ echo "export DISKS" >> $zprofile
+ else
+ msg='echo Please set \$DISKS appropriate before running zfstest'
+ echo $msg >> $zprofile
+ fi
+
+ chown ztest $zprofile
+ fi
+ fi
+ if [[ ! -f /opt/tools/etc/sudoers.d/ztest ]]; then
+ mkdir -p /opt/tools/etc/sudoers.d
+ echo "ztest ALL=(ALL) NOPASSWD: ALL" >> /opt/tools/etc/sudoers.d/ztest
+ fi
+}
+
+function zfs_test_check {
+ # DISKS is set either in our environment, or in the .profile of ~ztest.
+ zprofile=/zones/global/ztest/.profile
+ zdisksvar=$(su - ztest -c 'echo $DISKS' | tail -1)
+
+ # Check for KEEP too.
+ grep -q ^KEEP= $zprofile || \
+ fatal "Cannot run ZFS test, you need KEEP set in your ztest's environment"
+
+ # If neither are set DO NOT RUN the ztests.
+ if [[ -z $DISKS && -z $zdisksvar ]]; then
+ fatal "Cannot run ZFS test, you need DISKS set in your or ztest's environment"
+ fi
+
+ # Check if they are both non-zero and different.
+ if [[ -n "$DISKS" && -n "$zdisksvar" && "$DISKS" != "$zdisksvar" ]]; then
+ log "DISKS in current root environment: $DISKS"
+ log "DISKS in user ztest's environment: $zdisksvar"
+ fatal "Pleast reconcile these two before running the ZFS tests."
+ fi
+
+ if [[ -z "$zdisksvar" ]]; then
+ # put DISKS into ztest's .profile.
+ echo "DISKS=\"$DISKS\"" >> $zprofile
+ echo "export DISKS" >> $zprofile
+ fi
+
+ # OKAY, now we can run it!
+ log_test zfstest su - ztest -c /opt/zfs-tests/bin/zfstest
+}
+
+#
+# By using log_test or log_testrunner, we accumulate the exit codes from each
+# test run to $RESULT.
+#
+# We don't - yet - run net-tests, smbclient-tests, zfs-tests, or the dtrace
+# suite.
+#
+function execute_tests {
+
+ log "Starting test runs"
+ log_test bhyvetest /opt/bhyve-tests/bin/bhyvetest
+ log_testrunner crypto-tests /opt/crypto-tests/runfiles/default.run
+ log_testrunner elf-tests /opt/elf-tests/runfiles/default.run
+ log_testrunner libc-tests /opt/libc-tests/runfiles/default.run
+ log_test vndtest /opt/vndtest/bin/vndtest -a
+ log_testrunner util-tests /opt/util-tests/runfiles/default.run
+ log_testrunner os-tests /opt/os-tests/runfiles/default.run
+ zfs_test_check
+
+ if [[ -n "$FAILED_TESTS" ]]; then
+ echo ""
+ log "Failures were seen in the following test suites: $FAILED_TESTS"
+ fi
+
+}
+
+function usage {
+ echo "Usage: smartos-test [-h] [-c] [-e] [-r] [-w] <path to tests.tgz>"
+ echo ""
+ echo "At least one of -c, -e, -r is required."
+ echo ""
+ echo " -h print usage"
+ echo " -c configure the system for testing"
+ echo " -e execute known tests"
+ echo " -f skip the check to ensure platform version == test version"
+ echo " -r snapshot or rollback to zones/opt@system-test-smartos-test"
+ echo " before doing any system configuration or test execution"
+ echo " -w when mounting the lofs /usr, make it writable"
+}
+
+mount_usr_rw=false
+skip_version_check=false
+do_configure=false
+do_execute=false
+do_rollback=false
+
+#
+# Main
+#
+while getopts "cefrwh" opt; do
+ case "${opt}" in
+ c)
+ do_configure=true
+ ;;
+ e)
+ do_execute=true
+ ;;
+ f)
+ skip_version_check=true
+ ;;
+ r)
+ do_rollback=true
+ ;;
+ h)
+ usage
+ exit 2
+ ;;
+ w)
+ mount_usr_rw=true
+ ;;
+ *)
+ log "unknown argument ${opt}"
+ usage
+ exit 2
+ esac
+done
+shift $((OPTIND - 1))
+
+test_archive=$1
+
+if [[ -z "$test_archive" ]]; then
+ log "missing test archive argument."
+ usage
+ exit 1
+fi
+
+if [[ ! -f "$test_archive" ]]; then
+ usage
+ fatal "unable to access test archive at $test_archive"
+fi
+
+if [[ "$do_rollback" = false && \
+ "$do_configure" = false && \
+ "$do_execute" = false ]]; then
+ log "nothing to do: use at least one of -r -e -c"
+ usage
+ exit 2
+fi
+
+if [[ "$skip_version_check" = false ]]; then
+ version_check $1
+fi
+
+guard_production_data
+zone_check
+
+if [[ $do_rollback = true ]]; then
+ snapshot_rollback_opt
+fi
+
+if [[ $do_configure = true ]]; then
+ add_loopback_mounts $test_archive
+ extract_remaining_test_bits $test_archive
+ add_test_accounts
+ setup_pkgsrc
+ install_required_pkgs
+ # Enable per-process coredumps, some tests assume they're pre-set.
+ log_must coreadm -e process
+ log "This system is now configured to run the SmartOS tests."
+fi
+
+if [[ "$do_execute" = true ]]; then
+ execute_tests
+fi
+
+if [[ $RESULT -gt 0 ]]; then
+ exit 1
+else
+ exit 0
+fi
diff --git a/usr/src/test/test-runner/cmd/run b/usr/src/test/test-runner/cmd/run
index 8399c3203a..189be04d4d 100644
--- a/usr/src/test/test-runner/cmd/run
+++ b/usr/src/test/test-runner/cmd/run
@@ -1,4 +1,4 @@
-#!@PYTHON@
+#!/usr/bin/env python
#
# This file and its contents are supplied under the terms of the
@@ -16,8 +16,13 @@
# Copyright (c) 2017, Chris Fraire <cfraire@me.com>.
# Copyright 2019 Joyent, Inc.
# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2022 MNX Cloud, Inc.
#
+# NOTE: For SmartOS, the @PYTHON@ macro defaults to /opt/local, which is
+# not the case for the SmartOS global zone's pkgsrc installation (/opt/tools).
+# So use /usr/bin/env because the smartos-test script sets PATH appropriately.
+
from __future__ import print_function
import sys
PY3 = sys.version_info[0] == 3
@@ -51,6 +56,14 @@ TESTDIR = '/opt/zfs-tests/'
KILL = '/usr/bin/kill'
TRUE = '/usr/bin/true'
SUDO = '/usr/bin/sudo'
+SU = '/usr/bin/su'
+
+# The location of sudo(1) varies by illumos distribution.
+if os.path.isfile('/usr/bin/sudo'):
+ SUDO = '/usr/bin/sudo'
+else:
+ SUDO = '/opt/tools/bin/sudo'
+
retcode = 0
@@ -196,7 +209,21 @@ User: %s
sudo is required, this user was verified previously.
"""
self.killed = True
- do_sudo = len(self.user) != 0
+ cur_user = getpwuid(os.getuid()).pw_name
+
+ #
+ # The test runs in a child process of the process performing
+ # the kill and this child process may be owned by a different
+ # user (specified by self.user). If this is the case, and the
+ # parent process is not running as root, then sudo must be
+ # used. The verify_user function should prevent us from ever
+ # getting here on a system which doesn't have sudo.
+ #
+ if len(self.user) != 0 and self.user != cur_user and cur_user != 'root':
+ do_sudo = True
+ else:
+ do_sudo = False
+
signal = '-TERM'
cmd = [SUDO, KILL, signal, str(proc.pid)]
@@ -212,15 +239,23 @@ User: %s
def update_cmd_privs(self, cmd, user):
"""
If a user has been specified to run this Cmd and we're not already
- running as that user, prepend the appropriate sudo command to run
- as that user.
+ running as that user, prepend the appropriate sudo command to
+ run as that user. If running as root use su instead. The
+ verify_user function should prevent us from ever getting here
+ on a system which doesn't have sudo.
+
"""
- me = getpwuid(os.getuid())
- if not user or user == me:
+ cur_user = getpwuid(os.getuid()).pw_name
+
+ if not user or user == cur_user:
return cmd
- ret = '%s -E -u %s %s' % (SUDO, user, cmd)
+ if cur_user == 'root':
+ ret = '%s %s -c %s' % (SU, user, cmd)
+ else:
+ ret = '%s -E -u %s %s' % (SUDO, user, cmd)
+
return ret.split(' ')
def collect_output(self, proc):
@@ -865,14 +900,28 @@ def verify_file(pathname):
def verify_user(user, logger):
"""
- Verify that the specified user exists on this system, and can execute
- sudo without being prompted for a password.
+ Verify that the specified user exists on this system, and can
+ execute sudo without being prompted for a password. If the user
+ executing the test is the same as the requested user then this
+ check is skipped. If the user executing the test is root this
+ check is skipped as su can be used instead.
"""
+
testcmd = [SUDO, '-n', '-u', user, TRUE]
if user in Cmd.verified_users:
return True
+ cur_user = getpwuid(os.getuid()).pw_name
+
+ if user == cur_user or cur_user == 'root':
+ Cmd.verified_users.append(user)
+ return True
+
+ if not os.path.isfile(SUDO):
+ logger.info("Warning: this test requires sudo but it doesn't exist.")
+ return False
+
try:
_ = getpwnam(user)
except KeyError:
diff --git a/usr/src/test/util-tests/runfiles/default.run b/usr/src/test/util-tests/runfiles/default.run
index 4d9acd9d47..362c3a49a9 100644
--- a/usr/src/test/util-tests/runfiles/default.run
+++ b/usr/src/test/util-tests/runfiles/default.run
@@ -28,11 +28,13 @@ outputdir = /var/tmp/test_results
[/opt/util-tests/tests/printf_test]
[/opt/util-tests/tests/allowed-ips]
[/opt/util-tests/tests/set-linkprop]
+[/opt/util-tests/tests/show-overlay-exit]
+[/opt/util-tests/tests/vnic-mtu]
+[/opt/util-tests/tests/bunyan/bunyan]
[/opt/util-tests/tests/libsff/libsff]
[/opt/util-tests/tests/libjedec_test]
[/opt/util-tests/tests/smbios]
-[/opt/util-tests/tests/show-overlay-exit]
user = root
#
@@ -47,6 +49,8 @@ user = root
[/opt/util-tests/tests/mergeq/mqt]
[/opt/util-tests/tests/mergeq/wqt]
+[/opt/util-tests/tests/dis/distest]
+
[/opt/util-tests/tests/libnvpair_json]
tests = ['json_00_blank', 'json_01_boolean', 'json_02_numbers',
'json_03_empty_arrays', 'json_04_number_arrays', 'json_05_strings',
diff --git a/usr/src/test/util-tests/tests/Makefile b/usr/src/test/util-tests/tests/Makefile
index c75f26cf85..f26494e0ce 100644
--- a/usr/src/test/util-tests/tests/Makefile
+++ b/usr/src/test/util-tests/tests/Makefile
@@ -21,6 +21,7 @@
SUBDIRS = \
ar \
awk \
+ bunyan \
chown \
ctf \
date \
diff --git a/usr/src/test/util-tests/tests/bunyan/Makefile b/usr/src/test/util-tests/tests/bunyan/Makefile
new file mode 100644
index 0000000000..5f58bf1614
--- /dev/null
+++ b/usr/src/test/util-tests/tests/bunyan/Makefile
@@ -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.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/util-tests
+TESTDIR = $(ROOTOPTPKG)/tests/bunyan
+TESTBIN = $(ROOTOPTPKG)/bin
+
+PROG = btest
+
+SCRIPTS = \
+ bunyan
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+OBJS = $(PROG:%=%.o)
+SRCS = $(OBJS:%.o=%.c)
+
+CMDS = $(PROG:%=$(TESTBIN)/%) $(SCRIPTS:%=$(TESTDIR)/%)
+$(CMDS) := FILEMODE = 0555
+
+DIRS = $(TESTDIR) $(TESTBIN)
+
+LDLIBS += -lbunyan
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(CMDS)
+
+lint:
+
+clobber: clean
+ -$(RM) $(PROG)
+
+clean:
+ -$(RM) $(OBJS)
+
+$(CMDS): $(DIRS)
+
+$(DIRS):
+ $(INS.dir)
+
+$(TESTDIR)/%: %.ksh
+ $(INS.rename)
+
+$(TESTDIR)/%: %
+ $(INS.file)
+
+$(TESTBIN)/%: %
+ $(INS.file)
diff --git a/usr/src/test/util-tests/tests/bunyan/btest.c b/usr/src/test/util-tests/tests/bunyan/btest.c
new file mode 100644
index 0000000000..f6be13afa2
--- /dev/null
+++ b/usr/src/test/util-tests/tests/bunyan/btest.c
@@ -0,0 +1,316 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <bunyan.h>
+#include <netinet/in.h>
+#include <strings.h>
+
+static void
+create_handles(void)
+{
+ bunyan_logger_t *a, *b, *c;
+
+ assert(bunyan_init("foo", &a) == 0);
+ assert(bunyan_init("foo", &b) == 0);
+ assert(bunyan_init("foo", &c) == 0);
+ bunyan_fini(a);
+ bunyan_fini(b);
+ bunyan_fini(c);
+}
+
+static void
+create_stream(void)
+{
+ bunyan_logger_t *a;
+
+ assert(bunyan_init("foo", &a) == 0);
+ assert(bunyan_stream_add(a, "bar", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_stream_add(a, "baz", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_stream_add(a, "bar", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == EEXIST);
+ assert(bunyan_stream_add(a, "baz", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == EEXIST);
+ assert(bunyan_stream_remove(a, "baz") == 0);
+ assert(bunyan_stream_remove(a, "baz") == ENOENT);
+ assert(bunyan_stream_remove(a, "foobaz") == ENOENT);
+ assert(bunyan_stream_remove(a, "blah") == ENOENT);
+ assert(bunyan_stream_add(a, "bar", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == EEXIST);
+ assert(bunyan_stream_add(a, "baz", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_stream_add(a, "debug", BUNYAN_L_DEBUG,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_stream_add(a, "info", BUNYAN_L_INFO,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_stream_add(a, "warn", BUNYAN_L_WARN,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_stream_add(a, "error", BUNYAN_L_ERROR,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_stream_add(a, "fatal", BUNYAN_L_FATAL,
+ bunyan_stream_fd, (void *)1) == 0);
+
+ bunyan_fini(a);
+}
+
+static void
+create_key(void)
+{
+ bunyan_logger_t *a;
+ struct in_addr v4;
+ struct in6_addr v6;
+
+ assert(bunyan_init("foo", &a) == 0);
+ assert(bunyan_key_remove(a, "blah") == ENOENT);
+ assert(bunyan_key_add(a, BUNYAN_T_END) == 0);
+
+ assert(bunyan_key_add(a, BUNYAN_T_STRING, "s", "foo",
+ BUNYAN_T_POINTER, "p", (void *)a, BUNYAN_T_IP, "v4", &v4,
+ BUNYAN_T_IP6, "v6", &v6, BUNYAN_T_BOOLEAN, "b", B_TRUE,
+ BUNYAN_T_INT32, "i32", 69, BUNYAN_T_INT64, "i64", (uint64_t)6969,
+ BUNYAN_T_UINT32, "u32", 23, BUNYAN_T_UINT64, "u64", (uint64_t)2323,
+ BUNYAN_T_DOUBLE, "d", 3.14,
+ BUNYAN_T_INT64STR, "i64s", (uint64_t)12345,
+ BUNYAN_T_UINT64STR, "u64s", (uint64_t)54321, BUNYAN_T_END) == 0);
+
+ assert(bunyan_key_remove(a, "s") == 0);
+ assert(bunyan_key_remove(a, "s") == ENOENT);
+ assert(bunyan_key_remove(a, "p") == 0);
+ assert(bunyan_key_remove(a, "p") == ENOENT);
+ assert(bunyan_key_remove(a, "v4") == 0);
+ assert(bunyan_key_remove(a, "v4") == ENOENT);
+ assert(bunyan_key_remove(a, "v6") == 0);
+ assert(bunyan_key_remove(a, "v6") == ENOENT);
+ assert(bunyan_key_remove(a, "b") == 0);
+ assert(bunyan_key_remove(a, "b") == ENOENT);
+ assert(bunyan_key_remove(a, "i32") == 0);
+ assert(bunyan_key_remove(a, "i32") == ENOENT);
+ assert(bunyan_key_remove(a, "i64") == 0);
+ assert(bunyan_key_remove(a, "i64") == ENOENT);
+ assert(bunyan_key_remove(a, "u32") == 0);
+ assert(bunyan_key_remove(a, "u32") == ENOENT);
+ assert(bunyan_key_remove(a, "u64") == 0);
+ assert(bunyan_key_remove(a, "u64") == ENOENT);
+ assert(bunyan_key_remove(a, "d") == 0);
+ assert(bunyan_key_remove(a, "d") == ENOENT);
+ assert(bunyan_key_remove(a, "i64s") == 0);
+ assert(bunyan_key_remove(a, "i64s") == ENOENT);
+ assert(bunyan_key_remove(a, "u64s") == 0);
+ assert(bunyan_key_remove(a, "u64s") == ENOENT);
+
+ bunyan_fini(a);
+}
+
+static void
+bad_level(void)
+{
+ bunyan_logger_t *a;
+
+ assert(bunyan_init("bad level", &a) == 0);
+ assert(bunyan_stream_add(a, "bar", BUNYAN_L_TRACE - 1,
+ bunyan_stream_fd, (void *)1) == EINVAL);
+ assert(bunyan_stream_add(a, "bar", BUNYAN_L_TRACE + 1,
+ bunyan_stream_fd, (void *)1) == EINVAL);
+ assert(bunyan_stream_add(a, "bar", BUNYAN_L_DEBUG - 1,
+ bunyan_stream_fd, (void *)1) == EINVAL);
+ assert(bunyan_stream_add(a, "bar", BUNYAN_L_INFO + 1,
+ bunyan_stream_fd, (void *)1) == EINVAL);
+ assert(bunyan_stream_add(a, "bar", BUNYAN_L_WARN - 1,
+ bunyan_stream_fd, (void *)1) == EINVAL);
+ assert(bunyan_stream_add(a, "bar", BUNYAN_L_ERROR + 1,
+ bunyan_stream_fd, (void *)1) == EINVAL);
+ assert(bunyan_stream_add(a, "bar", BUNYAN_L_FATAL - 1,
+ bunyan_stream_fd, (void *)1) == EINVAL);
+ assert(bunyan_stream_add(a, "bar", -5,
+ bunyan_stream_fd, (void *)1) == EINVAL);
+
+ bunyan_fini(a);
+}
+
+static void
+basic_log(void)
+{
+ bunyan_logger_t *a;
+
+ assert(bunyan_init("basic", &a) == 0);
+ assert(bunyan_stream_add(a, "foo", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_trace(a, "trace", BUNYAN_T_END) == 0);
+ assert(bunyan_debug(a, "debug", BUNYAN_T_END) == 0);
+ assert(bunyan_info(a, "info", BUNYAN_T_END) == 0);
+ assert(bunyan_warn(a, "warn", BUNYAN_T_END) == 0);
+ assert(bunyan_error(a, "error", BUNYAN_T_END) == 0);
+ assert(bunyan_fatal(a, "fatal", BUNYAN_T_END) == 0);
+
+ bunyan_fini(a);
+}
+
+static void
+crazy_log(void)
+{
+ bunyan_logger_t *a;
+ struct in_addr v4;
+ struct in6_addr v6;
+
+ bzero(&v4, sizeof (struct in_addr));
+ bzero(&v6, sizeof (struct in6_addr));
+
+ assert(bunyan_init("basic", &a) == 0);
+ assert(bunyan_stream_add(a, "foo", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_trace(a, "trace", BUNYAN_T_STRING, "s", "foo",
+ BUNYAN_T_POINTER, "p", (void *)a, BUNYAN_T_IP, "v4", &v4,
+ BUNYAN_T_IP6, "v6", &v6, BUNYAN_T_BOOLEAN, "b", B_TRUE,
+ BUNYAN_T_INT32, "i32", 69, BUNYAN_T_INT64, "i64", (uint64_t)6969,
+ BUNYAN_T_UINT32, "u32", 23, BUNYAN_T_UINT64, "u64", (uint64_t)2323,
+ BUNYAN_T_DOUBLE, "d", 3.14,
+ BUNYAN_T_INT64STR, "i64s", (uint64_t)12345,
+ BUNYAN_T_UINT64STR, "u64s", (uint64_t)54321, BUNYAN_T_END) == 0);
+ assert(bunyan_debug(a, "debug", BUNYAN_T_STRING, "s", "foo",
+ BUNYAN_T_POINTER, "p", (void *)a, BUNYAN_T_IP, "v4", &v4,
+ BUNYAN_T_IP6, "v6", &v6, BUNYAN_T_BOOLEAN, "b", B_TRUE,
+ BUNYAN_T_INT32, "i32", 69, BUNYAN_T_INT64, "i64", (uint64_t)6969,
+ BUNYAN_T_UINT32, "u32", 23, BUNYAN_T_UINT64, "u64", (uint64_t)2323,
+ BUNYAN_T_DOUBLE, "d", 3.14,
+ BUNYAN_T_INT64STR, "i64s", (uint64_t)12345,
+ BUNYAN_T_UINT64STR, "u64s", (uint64_t)54321, BUNYAN_T_END) == 0);
+ assert(bunyan_info(a, "info", BUNYAN_T_STRING, "s", "foo",
+ BUNYAN_T_POINTER, "p", (void *)a, BUNYAN_T_IP, "v4", &v4,
+ BUNYAN_T_IP6, "v6", &v6, BUNYAN_T_BOOLEAN, "b", B_TRUE,
+ BUNYAN_T_INT32, "i32", 69, BUNYAN_T_INT64, "i64", (uint64_t)6969,
+ BUNYAN_T_UINT32, "u32", 23, BUNYAN_T_UINT64, "u64", (uint64_t)2323,
+ BUNYAN_T_DOUBLE, "d", 3.14,
+ BUNYAN_T_INT64STR, "i64s", (uint64_t)12345,
+ BUNYAN_T_UINT64STR, "u64s", (uint64_t)54321, BUNYAN_T_END) == 0);
+ assert(bunyan_warn(a, "warn", BUNYAN_T_STRING, "s", "foo",
+ BUNYAN_T_POINTER, "p", (void *)a, BUNYAN_T_IP, "v4", &v4,
+ BUNYAN_T_IP6, "v6", &v6, BUNYAN_T_BOOLEAN, "b", B_TRUE,
+ BUNYAN_T_INT32, "i32", 69, BUNYAN_T_INT64, "i64", (uint64_t)6969,
+ BUNYAN_T_UINT32, "u32", 23, BUNYAN_T_UINT64, "u64", (uint64_t)2323,
+ BUNYAN_T_DOUBLE, "d", 3.14,
+ BUNYAN_T_INT64STR, "i64s", (uint64_t)12345,
+ BUNYAN_T_UINT64STR, "u64s", (uint64_t)54321, BUNYAN_T_END) == 0);
+ assert(bunyan_error(a, "error", BUNYAN_T_STRING, "s", "foo",
+ BUNYAN_T_POINTER, "p", (void *)a, BUNYAN_T_IP, "v4", &v4,
+ BUNYAN_T_IP6, "v6", &v6, BUNYAN_T_BOOLEAN, "b", B_TRUE,
+ BUNYAN_T_INT32, "i32", 69, BUNYAN_T_INT64, "i64", (uint64_t)6969,
+ BUNYAN_T_UINT32, "u32", 23, BUNYAN_T_UINT64, "u64", (uint64_t)2323,
+ BUNYAN_T_DOUBLE, "d", 3.14,
+ BUNYAN_T_INT64STR, "i64s", (uint64_t)12345,
+ BUNYAN_T_UINT64STR, "u64s", (uint64_t)54321, BUNYAN_T_END) == 0);
+ assert(bunyan_fatal(a, "fatal", BUNYAN_T_STRING, "s", "foo",
+ BUNYAN_T_POINTER, "p", (void *)a, BUNYAN_T_IP, "v4", &v4,
+ BUNYAN_T_IP6, "v6", &v6, BUNYAN_T_BOOLEAN, "b", B_TRUE,
+ BUNYAN_T_INT32, "i32", 69, BUNYAN_T_INT64, "i64", (uint64_t)6969,
+ BUNYAN_T_UINT32, "u32", 23, BUNYAN_T_UINT64, "u64", (uint64_t)2323,
+ BUNYAN_T_DOUBLE, "d", 3.14,
+ BUNYAN_T_INT64STR, "i64s", (uint64_t)12345,
+ BUNYAN_T_UINT64STR, "u64s", (uint64_t)54321, BUNYAN_T_END) == 0);
+
+ bunyan_fini(a);
+}
+
+static void
+child_log(void)
+{
+ bunyan_logger_t *a, *child;
+ struct in_addr v4;
+ struct in6_addr v6;
+
+ bzero(&v4, sizeof (struct in_addr));
+ bzero(&v6, sizeof (struct in6_addr));
+
+ assert(bunyan_init("child", &a) == 0);
+ assert(bunyan_stream_add(a, "foo", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_child(a, &child, BUNYAN_T_STRING, "s", "foo",
+ BUNYAN_T_POINTER, "p", (void *)a, BUNYAN_T_IP, "v4", &v4,
+ BUNYAN_T_IP6, "v6", &v6, BUNYAN_T_BOOLEAN, "b", B_TRUE,
+ BUNYAN_T_INT32, "i32", 69, BUNYAN_T_INT64, "i64", (uint64_t)6969,
+ BUNYAN_T_UINT32, "u32", 23, BUNYAN_T_UINT64, "u64", (uint64_t)2323,
+ BUNYAN_T_DOUBLE, "d", 3.14,
+ BUNYAN_T_INT64STR, "i64s", (uint64_t)12345,
+ BUNYAN_T_UINT64STR, "u64s", (uint64_t)54321, BUNYAN_T_END) == 0);
+
+ assert(bunyan_key_remove(a, "p") == ENOENT);
+
+ bunyan_fini(a);
+ assert(bunyan_trace(child, "trace", BUNYAN_T_END) == 0);
+ assert(bunyan_debug(child, "debug", BUNYAN_T_END) == 0);
+ assert(bunyan_info(child, "info", BUNYAN_T_END) == 0);
+ assert(bunyan_warn(child, "warn", BUNYAN_T_END) == 0);
+ assert(bunyan_error(child, "error", BUNYAN_T_END) == 0);
+ assert(bunyan_fatal(child, "fatal", BUNYAN_T_END) == 0);
+
+ assert(bunyan_key_remove(child, "p") == 0);
+
+ bunyan_fini(child);
+}
+
+static void
+crazy_child(void)
+{
+ bunyan_logger_t *a, *child;
+ struct in_addr v4;
+ struct in6_addr v6;
+
+ bzero(&v4, sizeof (struct in_addr));
+ bzero(&v6, sizeof (struct in6_addr));
+
+ assert(bunyan_init("crazy child", &a) == 0);
+ assert(bunyan_stream_add(a, "foo", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_key_add(a, BUNYAN_T_STRING, "s", "foo",
+ BUNYAN_T_POINTER, "p", (void *)a, BUNYAN_T_IP, "v4", &v4,
+ BUNYAN_T_IP6, "v6", &v6, BUNYAN_T_BOOLEAN, "b", B_TRUE,
+ BUNYAN_T_INT32, "i32", 69, BUNYAN_T_INT64, "i64", (uint64_t)6969,
+ BUNYAN_T_UINT32, "u32", 23, BUNYAN_T_UINT64, "u64", (uint64_t)2323,
+ BUNYAN_T_DOUBLE, "d", 3.14,
+ BUNYAN_T_INT64STR, "i64s", (uint64_t)12345,
+ BUNYAN_T_UINT64STR, "u64s", (uint64_t)54321, BUNYAN_T_END) == 0);
+ assert(bunyan_child(a, &child, BUNYAN_T_END) == 0);
+ bunyan_fini(a);
+
+ assert(bunyan_stream_remove(child, "foo") == 0);
+ assert(bunyan_stream_add(child, "bar", BUNYAN_L_TRACE,
+ bunyan_stream_fd, (void *)1) == 0);
+ assert(bunyan_key_remove(child, "u64s") == 0);
+ assert(bunyan_trace(child, "trace", BUNYAN_T_END) == 0);
+ assert(bunyan_debug(child, "debug", BUNYAN_T_END) == 0);
+ assert(bunyan_info(child, "info", BUNYAN_T_END) == 0);
+ assert(bunyan_warn(child, "warn", BUNYAN_T_END) == 0);
+ assert(bunyan_error(child, "error", BUNYAN_T_END) == 0);
+ assert(bunyan_fatal(child, "fatal", BUNYAN_T_END) == 0);
+
+ bunyan_fini(child);
+}
+
+int
+main(void)
+{
+ create_handles();
+ create_stream();
+ create_key();
+ bad_level();
+ basic_log();
+ crazy_log();
+ child_log();
+ crazy_child();
+
+ return (0);
+}
diff --git a/usr/src/test/util-tests/tests/bunyan/bunyan.ksh b/usr/src/test/util-tests/tests/bunyan/bunyan.ksh
new file mode 100644
index 0000000000..4ec0f4bd62
--- /dev/null
+++ b/usr/src/test/util-tests/tests/bunyan/bunyan.ksh
@@ -0,0 +1,26 @@
+#! /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.
+#
+
+#
+# Simple wrapper for the classic test.out
+#
+
+set -o errexit
+
+btest_root=$(dirname $0)/../..
+btest_bin=$btest_root/bin/btest
+
+LD_PRELOAD=libumem.so UMEM_DEBUG=default $btest_bin >/dev/null
diff --git a/usr/src/test/util-tests/tests/dis/distest.ksh b/usr/src/test/util-tests/tests/dis/distest.ksh
index 37a5dd231a..f32f858ef1 100644
--- a/usr/src/test/util-tests/tests/dis/distest.ksh
+++ b/usr/src/test/util-tests/tests/dis/distest.ksh
@@ -11,7 +11,7 @@
#
#
-# Copyright 2018 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
#
#
@@ -50,6 +50,7 @@ dt_nodefault=
dt_tests=
dt_tnum=0
dt_tfail=0
+dt_txfail=0
dt_tsuc=0
dt_origwd=
dt_root=
@@ -141,10 +142,25 @@ handle_failure()
mv $dir $faildir
cp $source $faildir/
cp $out $faildir/
- printf "%s " "failed "
+
+ #
+ # Our pkgsrc gas is too old (2.26.1) to assemble these.
+ #
+ xfails=("32.vaes.s" "32.gfni.s" "32.avx512_vpclmulqdq.s"
+ "64.vaes.s" "64.gfni.s" "64.avx512_vpclmulqdq.s")
+ testname=$(basename $source)
+ printf '%s\n' ${xfails[@]} | grep "^$testname$" >/dev/null
+
+ if [[ $? -eq 0 ]]; then
+ printf "%s " "expected fail "
+ ((dt_txfail++))
+ else
+ printf "%s " "failed "
+ ((dt_tfail++))
+ fi
+
[[ -n $reason ]] && printf "%s " $reason
printf "%s\n" "$faildir"
- ((dt_tfail++))
}
#
@@ -271,6 +287,7 @@ libdis Results
Tests passed: $dt_tsuc
Tests failed: $dt_tfail
+Tests expected to fail: $dt_txfail
Tests ran: $dt_tnum
EOF
}
diff --git a/usr/src/test/util-tests/tests/dladm/Makefile b/usr/src/test/util-tests/tests/dladm/Makefile
index 50ef1b876a..8bbc8e3841 100644
--- a/usr/src/test/util-tests/tests/dladm/Makefile
+++ b/usr/src/test/util-tests/tests/dladm/Makefile
@@ -17,7 +17,7 @@ include $(SRC)/cmd/Makefile.cmd
include $(SRC)/test/Makefile.com
ROOTOPTPKG = $(ROOT)/opt/util-tests/tests
-PROG = allowed-ips set-linkprop show-overlay-exit
+PROG = allowed-ips set-linkprop show-overlay-exit vnic-mtu
ROOTPROG = $(PROG:%=$(ROOTOPTPKG)/%)
diff --git a/usr/src/test/util-tests/tests/dladm/allowed-cids.ksh b/usr/src/test/util-tests/tests/dladm/allowed-cids.ksh
new file mode 100644
index 0000000000..9b62962baf
--- /dev/null
+++ b/usr/src/test/util-tests/tests/dladm/allowed-cids.ksh
@@ -0,0 +1,95 @@
+#!/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 2017 Joyent, Inc.
+#
+
+source ./common.ksh
+
+property="allowed-dhcp-cids"
+
+setup
+
+# Valid hexadecimal strings
+epass 0x1234
+epass 0x123456789abcdef0
+epass 0x123456789abcdef0
+
+# Hex strings w/ an odd number of characters are not allowed
+efail 0x0
+efail 0x1
+efail 0x1fa
+efail 0xfba39e2
+
+# Invalid hexadecimal strings
+efail 0xz
+efail 0x01234567q12
+efail 0x=+
+efail 0x-1
+efail 0x1,2,3
+
+# Valid RFC 3315 DUID strings
+
+## DUID-LLT
+epass 1.1.1234.90:b8:d0:81:91:30
+epass 1.1.1512434853.90:b8:d0:81:91:30
+epass 1.1.28530123.90:b8:d0:81:91:30
+epass 1.6.1512434853.14:10:9f:d0:5b:d3
+
+## DUID-EN
+epass 2.9.0CC084D303000912
+epass 2.9.0cc084d303000912
+epass 2.32473.45ab
+epass 2.38678.0123abcd
+
+## DUID-LL
+epass 3.1.90:b8:d0:81:91:30
+epass 3.1.90:b8:d0:4b:c7:3b
+epass 3.1.2:8:20:a4:4d:ee
+epass 3.6.14:10:9f:d0:5b:d3
+
+# Invalid RFC 3315 DUID strings
+
+## DUID-LLT
+efail 1.1.12a34.90:b8:d0:81:91:30
+efail 1.1.15-33.90:b8:d0:81:91:30
+efail 1.1.98+123.90:b8:d0:81:91:30
+efail 3.z.1512434853.14:10:9f:d0:5b:d3
+efail 3.6.1512434853.q4:10:9f:d0:5b:d3
+
+## DUID-EN
+efail 2.32473.45a
+efail 2.9.Z
+efail 2.9.-12
+efail 2.QZ4.45a
+efail 2.38d78.0123abcd
+
+## DUID-LL
+efail 3.wy.90:b8:d0:81:91:30
+efail 3.1.90:z8:di:ob:c7:3b
+efail 3.1.5.2:8:20:a4:4d:ee
+
+## Uknown DUID forms
+efail 4.1.45a
+efail 9.1.45a
+efail 23.1.45a
+
+# Random strings of bytes are also accepted,
+# if they don't have the above prefixes.
+epass 1234
+epass abcdef
+epass qsxasdasdfgfdgkj123455
+epass 0x
+
+cleanup
+printf "TEST PASS: $ai_arg0\n"
diff --git a/usr/src/test/util-tests/tests/dladm/allowed-ips.ksh b/usr/src/test/util-tests/tests/dladm/allowed-ips.ksh
index 866b8c7966..4802f86286 100644
--- a/usr/src/test/util-tests/tests/dladm/allowed-ips.ksh
+++ b/usr/src/test/util-tests/tests/dladm/allowed-ips.ksh
@@ -11,47 +11,12 @@
#
#
-# Copyright (c) 2014, Joyent, Inc.
+# Copyright 2016 Joyent, Inc.
#
-ai_arg0="$(basename $0)"
-ai_stub="teststub$$"
-ai_vnic="testvnic$$"
+source ./common.ksh
-function fatal
-{
- typeset msg="$*"
- [[ -z "$msg" ]] && msg="failed"
- echo "TEST_FAIL: $vt_arg0: $msg" >&2
- exit 1
-}
-
-function setup
-{
- dladm create-etherstub $ai_stub || fatal "failed to create etherstub"
- dladm create-vnic -l $ai_stub $ai_vnic || fatal "failed to create vnic"
-}
-
-function cleanup
-{
- dladm delete-vnic $ai_vnic || fatal "failed to remove vnic"
- dladm delete-etherstub $ai_stub || fatal "failed to remove etherstub"
-}
-
-function runtest
-{
- dladm set-linkprop -p allowed-ips="$@" $ai_vnic 2>/dev/null
-}
-
-function epass
-{
- runtest $* || fatal "allowed-ips=$* failed, expected success\n"
-}
-
-function efail
-{
- runtest $* && fatal "allowed-ips=$* succeeded, expected failure\n"
-}
+property="allowed-ips"
#
# Run through all IPv6 prefixes for validity with a token prefix
@@ -204,4 +169,4 @@ epass fe80::/15
epass fe82::/15
cleanup
-printf "TEST PASS: $ai_arg0"
+printf "TEST PASS: $ai_arg0\n"
diff --git a/usr/src/test/util-tests/tests/dladm/common.ksh b/usr/src/test/util-tests/tests/dladm/common.ksh
new file mode 100644
index 0000000000..e5301d0a52
--- /dev/null
+++ b/usr/src/test/util-tests/tests/dladm/common.ksh
@@ -0,0 +1,57 @@
+#!/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 2017 Joyent, Inc.
+#
+
+ai_arg0="$(basename $0)"
+ai_stub="teststub$$"
+ai_vnic="testvnic$$"
+
+typeset property
+
+function fatal
+{
+ typeset msg="$*"
+ [[ -z "$msg" ]] && msg="failed"
+ echo "TEST_FAIL: $ai_arg0: $msg" >&2
+ exit 1
+}
+
+function setup
+{
+ dladm create-etherstub $ai_stub || fatal "failed to create etherstub"
+ dladm create-vnic -l $ai_stub $ai_vnic || fatal "failed to create vnic"
+}
+
+function cleanup
+{
+ dladm delete-vnic $ai_vnic || fatal "failed to remove vnic"
+ dladm delete-etherstub $ai_stub || fatal "failed to remove etherstub"
+}
+
+function runtest
+{
+ [[ -z "$property" ]] && fatal "missing property to set"
+ dladm set-linkprop -p $property="$@" $ai_vnic 2>/dev/null
+}
+
+function epass
+{
+ runtest $* || fatal "$property=$* failed, expected success\n"
+}
+
+function efail
+{
+ runtest $* && fatal "$property=$* succeeded, expected failure\n"
+}
diff --git a/usr/src/test/util-tests/tests/dladm/dynamic-methods.ksh b/usr/src/test/util-tests/tests/dladm/dynamic-methods.ksh
new file mode 100644
index 0000000000..4ed64a92b4
--- /dev/null
+++ b/usr/src/test/util-tests/tests/dladm/dynamic-methods.ksh
@@ -0,0 +1,45 @@
+#!/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 2017 Joyent, Inc.
+#
+
+source ./common.ksh
+
+property="dynamic-methods"
+
+setup
+
+# All valid values on their own
+epass slaac
+epass dhcpv4
+epass dhcpv6
+epass addrconf
+
+# Combinations of values
+epass slaac,dhcpv4
+epass slaac,dhcpv6
+epass dhcpv4,dhcpv6
+epass dhcpv4,addrconf
+epass dhcpv4,dhcpv6,slaac
+
+# Illegal values
+efail dhcpv8
+efail slaac,dhcpv8
+efail slack
+efail ipv6
+efail dhcp
+efail dhcpv
+
+cleanup
+printf "TEST PASS: $ai_arg0\n"
diff --git a/usr/src/test/util-tests/tests/dladm/vnic-mtu.ksh b/usr/src/test/util-tests/tests/dladm/vnic-mtu.ksh
new file mode 100644
index 0000000000..cfb48e2e65
--- /dev/null
+++ b/usr/src/test/util-tests/tests/dladm/vnic-mtu.ksh
@@ -0,0 +1,116 @@
+#!/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 2015 Joyent, Inc.
+#
+
+#
+# The purpose of this is to test the MTU property on VNICs, using both
+# temporary and persistent properties. To do this, we create an
+# Etherstub and then create various VNICs on top of it.
+#
+
+vm_arg0="$(basename $0)"
+vm_stub="teststub$$"
+vm_vnic="testvnic$$"
+
+VM_MTU_MIN=576
+VM_MTU_MAX=9000
+
+fatal()
+{
+ typeset msg="$*"
+ [[ -z "$msg" ]] && msg="failed"
+ echo "TEST_FAIL: $vm_arg0: $msg" >&2
+
+ # Try to clean up just in case
+ dladm delete-vnic $vm_vnic 2>/dev/null
+ dladm delete-etherstub $vm_stub 2>/dev/null
+ exit 1
+}
+
+#
+# Validate that the MTU of the datalink dev has the MTU that we expect
+#
+validate_mtu()
+{
+ typeset dev=$1
+ typeset mtu=$2
+ typeset val
+
+ [[ -z "$dev" ]] && fatal "missing required device"
+ [[ -z "$mtu" ]] && fatal "missing required mtu"
+ val=$(dladm show-linkprop -c -p mtu -o value $dev)
+ [[ $? -eq 0 ]] || fatal "failed to get MTU for $dev"
+ (( $val == $mtu )) || fatal \
+ "mtu mismatch on $dev: expected $mtu, got $val"
+}
+
+delete_stub()
+{
+ dladm delete-etherstub $vm_stub || fatal \
+ "failed to delete stub $vm_stub"
+}
+
+create_stub()
+{
+ dladm create-etherstub $vm_stub || fatal \
+ "failed to create stub"
+ validate_mtu $vm_stub $VM_MTU_MAX
+}
+
+delete_vnic()
+{
+ dladm delete-vnic $vm_vnic || fatal "failed to delete vnic $vm_vnic"
+}
+
+test_vnic_pass()
+{
+ typeset mtu=$1
+ typeset flags=$2
+
+ [[ -z "$mtu" ]] && fatal "missing required mtu"
+ dladm create-vnic $flags -l $vm_stub -p mtu=$mtu $vm_vnic || fatal \
+ "failed to create vnic: $vm_vnic"
+ validate_mtu "$vm_vnic" "$mtu"
+ delete_vnic
+}
+
+test_vnic_fail()
+{
+ typeset mtu=$1
+ typeset flags=$2
+
+ [[ -z "$mtu" ]] && fatal "missing required mtu"
+ dladm create-vnic $flags -l $vm_stub -p mtu=$mtu \
+ $vm_vnic 2>/dev/null && fatal \
+ "created vnic with mtu $mtu, but failure expected"
+}
+
+test_pass()
+{
+ typeset flags=$1
+
+ create_stub
+ test_vnic_pass 1500 $flags
+ test_vnic_pass 1400 $flags
+ test_vnic_pass $VM_MTU_MIN $flags
+ test_vnic_pass $VM_MTU_MAX $flags
+ test_vnic_fail $((($VM_MTU_MIN - 1))) $flags
+ test_vnic_fail $((($VM_MTU_MAX + 1))) $flags
+ delete_stub
+}
+
+test_pass "-t"
+test_pass
+echo "TEST PASS: $vm_arg0"
diff --git a/usr/src/test/util-tests/tests/grep_xpg4/grep_test.ksh b/usr/src/test/util-tests/tests/grep_xpg4/grep_test.ksh
index 2456da0ffe..8724cbcb01 100644
--- a/usr/src/test/util-tests/tests/grep_xpg4/grep_test.ksh
+++ b/usr/src/test/util-tests/tests/grep_xpg4/grep_test.ksh
@@ -18,6 +18,8 @@
#
XGREP=${XGREP:=/usr/bin/grep}
+
+MY_TESTS=${MY_TESTS:=/opt/util-tests}
FILEDIR=$MY_TESTS/tests/files/grep
OUTFILE=/tmp/grep_test.out.$$
FLAGSFILE=/tmp/grep_flags.$$
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/Makefile b/usr/src/test/util-tests/tests/libnvpair_json/Makefile
index faa76094fe..633b515f19 100644
--- a/usr/src/test/util-tests/tests/libnvpair_json/Makefile
+++ b/usr/src/test/util-tests/tests/libnvpair_json/Makefile
@@ -17,7 +17,7 @@ include $(SRC)/Makefile.master
ROOTOPTPKG = $(ROOT)/opt/util-tests
TESTDIR = $(ROOTOPTPKG)/tests/libnvpair_json
-ROOTBINDIR = $(ROOTOPTPKG)/bin
+TESTBIN = $(ROOTOPTPKG)/bin
PROG = print_json
@@ -38,9 +38,11 @@ include $(SRC)/test/Makefile.com
OBJS = $(PROG:%=%.o)
SRCS = $(OBJS:%.o=%.c)
-CMDS = $(PROG:%=$(ROOTBINDIR)/%) $(SCRIPTS:%=$(TESTDIR)/%)
+CMDS = $(PROG:%=$(TESTBIN)/%) $(SCRIPTS:%=$(TESTDIR)/%)
$(CMDS) := FILEMODE = 0555
+DIRS = $(TESTDIR) $(TESTBIN)
+
LDLIBS += -lnvpair
# intentional abort()
@@ -62,16 +64,13 @@ clobber: clean
clean:
-$(RM) $(OBJS)
-$(CMDS): $(TESTDIR) $(PROG)
+$(CMDS): $(DIRS)
-$(ROOTBINDIR):
- $(INS.dir)
-
-$(ROOTBINDIR)/%: %
- $(INS.file)
-
-$(TESTDIR):
+$(DIRS):
$(INS.dir)
$(TESTDIR)/%: %.ksh
$(INS.rename)
+
+$(TESTBIN)/%: %
+ $(INS.file)
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/json_08_large_data.ksh b/usr/src/test/util-tests/tests/libnvpair_json/json_08_large_data.ksh
new file mode 100644
index 0000000000..ca19e10d06
--- /dev/null
+++ b/usr/src/test/util-tests/tests/libnvpair_json/json_08_large_data.ksh
@@ -0,0 +1,37 @@
+#!/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 2015, Joyent, Inc.
+#
+
+DIR=$(dirname $(whence $0))
+. ${DIR}/json_common
+
+BASELINE="$(cat <<EOF
+{\
+"o":[\
+$(for i in {1..50}; do
+ printf '"%s",' 'some_very_big_string_that_takes_up_space'
+done)\
+"end"\
+]\
+}
+EOF)"
+
+OUTPUT="$(${DIR}/../../bin/print_json <<-EOF
+add_string_array "o" $(for i in {1..50}; do
+ printf '"%s" ' 'some_very_big_string_that_takes_up_space'
+done)"end";
+EOF)"
+
+complete
diff --git a/usr/src/test/util-tests/tests/libnvpair_json/print_json.c b/usr/src/test/util-tests/tests/libnvpair_json/print_json.c
index e34ae8f7b1..9ac19b1bd1 100644
--- a/usr/src/test/util-tests/tests/libnvpair_json/print_json.c
+++ b/usr/src/test/util-tests/tests/libnvpair_json/print_json.c
@@ -814,7 +814,7 @@ main(int argc, char **argv)
/*
* Print the resultant list, and a terminating newline:
*/
- if (nvlist_print_json(stdout, lw->lw_nvl[0]) != 0 ||
+ if (nvlist_print_json(stdout, lw->lw_nvl[0]) < 0 ||
fprintf(stdout, "\n") < 0)
goto out;
diff --git a/usr/src/test/util-tests/tests/mdb/typedef/tst.dellist.mdb.out b/usr/src/test/util-tests/tests/mdb/typedef/tst.dellist.mdb.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/usr/src/test/util-tests/tests/mdb/typedef/tst.dellist.mdb.out
diff --git a/usr/src/test/util-tests/tests/mdb/typedef/tst.emptylist.mdb.out b/usr/src/test/util-tests/tests/mdb/typedef/tst.emptylist.mdb.out
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/usr/src/test/util-tests/tests/mdb/typedef/tst.emptylist.mdb.out
diff --git a/usr/src/test/zfs-tests/cmd/scripts/zfstest.ksh b/usr/src/test/zfs-tests/cmd/scripts/zfstest.ksh
index 3d53ef5fae..527c89163f 100644
--- a/usr/src/test/zfs-tests/cmd/scripts/zfstest.ksh
+++ b/usr/src/test/zfs-tests/cmd/scripts/zfstest.ksh
@@ -14,8 +14,10 @@
#
# Copyright (c) 2012, 2016 by Delphix. All rights reserved.
# Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
+# Copyright 2019 Joyent, Inc.
# Copyright 2021 Tintri by DDN, Inc. All rights reserved.
# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+# Copyright 2022 MNX Cloud, Inc.
#
export PATH="/usr/bin"
@@ -72,6 +74,8 @@ function find_runfile
distro=openindiana
elif [[ 0 -ne $(grep -c OmniOS /etc/release 2>/dev/null) ]]; then
distro=omnios
+ elif [[ 0 -ne $(grep -c SmartOS /etc/release 2>/dev/null) ]]; then
+ distro=smartos
fi
[[ -n $distro ]] && echo $COMMON,$STF_SUITE/runfiles/$distro.run
@@ -128,6 +132,18 @@ function constrain_path
# Special case links
ln -s /usr/gnu/bin/dd $PATHDIR/gnu_dd
+
+ # SmartOS does not ship some required commands by default.
+ # Link to them in the package manager's namespace.
+ pkgsrc_bin=/opt/tools/bin
+ pkgsrc_packages="sudo truncate python base64 shuf sha256sum"
+ for pkg in $pkgsrc_packages; do
+ if [[ ! -x $PATHDIR/$pkg ]]; then
+ rm $PATHDIR/$pkg &&
+ ln -s $pkgsrc_bin/$pkg $PATHDIR/$pkg ||
+ fail "Couldn't link $pkg"
+ fi
+ done
}
constrain_path
diff --git a/usr/src/test/zfs-tests/cmd/watch_dir/Makefile b/usr/src/test/zfs-tests/cmd/watch_dir/Makefile
new file mode 100644
index 0000000000..517dff64af
--- /dev/null
+++ b/usr/src/test/zfs-tests/cmd/watch_dir/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 2020 Joyent, Inc.
+#
+
+PROG = watch_dir
+
+include $(SRC)/cmd/Makefile.cmd
+include ../Makefile.subdirs
diff --git a/usr/src/test/zfs-tests/cmd/watch_dir/watch_dir.c b/usr/src/test/zfs-tests/cmd/watch_dir/watch_dir.c
new file mode 100644
index 0000000000..f6d8445162
--- /dev/null
+++ b/usr/src/test/zfs-tests/cmd/watch_dir/watch_dir.c
@@ -0,0 +1,151 @@
+/*
+ * 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 2020 Joyent, Inc.
+ */
+
+/*
+ * This program watches a directory with portfs or inotify, exiting when the
+ * directory is removed. It is useful in tests that ensure that watching a
+ * directory does not prevent it from being used as a mount point.
+ */
+#include <limits.h>
+#include <port.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+
+void
+fail_usage(void)
+{
+ (void) fprintf(stderr, "Usage: watch <portfs|inotify> directory\n");
+ exit(1);
+}
+
+#define MAX_PES 8
+
+void
+watch_port(const char *path)
+{
+ int port;
+ struct file_obj fobj = {0};
+
+ if ((port = port_create()) < 0) {
+ perror("port_create");
+ exit(1);
+ }
+
+ fobj.fo_name = (char *)path;
+ for (;;) {
+ timespec_t ts = {300, 0};
+ port_event_t pe;
+
+ if (port_associate(port, PORT_SOURCE_FILE, (uintptr_t)&fobj,
+ 0, (char *)path) != 0) {
+ perror("port_associate");
+ exit(1);
+ }
+
+ if (port_get(port, &pe, &ts) != 0) {
+ perror("port_get");
+ exit(1);
+ }
+
+ if (pe.portev_events & FILE_DELETE) {
+ (void) printf("DELETE\t%s\n", path);
+ exit(0);
+ }
+ if (pe.portev_events & MOUNTEDOVER) {
+ (void) printf("MOUNTEDOVER\t%s\n", path);
+ }
+ }
+}
+
+void
+watch_inotify(const char *path)
+{
+ int in, wd;
+ struct inotify_event ev;
+
+ if ((in = inotify_init()) < 0) {
+ perror("inotify_init");
+ exit(1);
+ }
+ if ((wd = inotify_add_watch(in, path, IN_DELETE_SELF)) == -1) {
+ perror("inotify_add_watch");
+ exit(1);
+ }
+
+ for (;;) {
+ ssize_t cnt;
+ char evpath[PATH_MAX];
+
+ cnt = read(in, &ev, sizeof (ev));
+ if (cnt != sizeof (ev)) {
+ (void) fprintf(stderr,
+ "read: expected %ld bytes got %ld\n",
+ sizeof (ev), cnt);
+ exit(1);
+ }
+ if (ev.len != 0) {
+ if (ev.len > sizeof (evpath)) {
+ (void) fprintf(stderr, "read: oversize "
+ "path (%u bytes)\n", ev.len);
+ exit(1);
+ }
+ cnt = read(in, evpath, ev.len);
+ if (cnt != ev.len) {
+ (void) fprintf(stderr, "read: expected %ld "
+ "bytes for path, got %ld\n", ev.len, cnt);
+ exit(1);
+ }
+ evpath[ev.len - 1] = '\0';
+ } else {
+ evpath[0] = '\0';
+ }
+ if (ev.mask & IN_DELETE_SELF) {
+ /*
+ * IN_DELETE_SELF events don't appear to include
+ * the path in the event.
+ */
+ (void) printf("DELETE_SELF\n");
+ exit(0);
+ } else {
+ (void) printf("EVENT_%08x\t%s\n", ev.mask, evpath);
+ }
+
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *watcher, *path;
+
+ if (argc != 3) {
+ fail_usage();
+ }
+ watcher = argv[1];
+ path = argv[2];
+
+ if (strcmp(watcher, "portfs") == 0) {
+ watch_port(path);
+ } else if (strcmp(watcher, "inotify") == 0) {
+ watch_inotify(path);
+ } else {
+ fail_usage();
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/zfs-tests/include/commands.cfg b/usr/src/test/zfs-tests/include/commands.cfg
index c4769cbc71..dad9372554 100644
--- a/usr/src/test/zfs-tests/include/commands.cfg
+++ b/usr/src/test/zfs-tests/include/commands.cfg
@@ -198,4 +198,5 @@ export ZFSTEST_FILES='btree_test
randwritecomp
readmmap
rename_dir
- rm_lnkcnt_zero_file'
+ rm_lnkcnt_zero_file
+ watch_dir'
diff --git a/usr/src/test/zfs-tests/runfiles/common.run b/usr/src/test/zfs-tests/runfiles/common.run
index 82672045ce..f58eecec40 100644
--- a/usr/src/test/zfs-tests/runfiles/common.run
+++ b/usr/src/test/zfs-tests/runfiles/common.run
@@ -156,7 +156,8 @@ tests = ['zfs_create_001_pos', 'zfs_create_002_pos', 'zfs_create_003_pos',
'zfs_create_004_pos', 'zfs_create_005_pos', 'zfs_create_006_pos',
'zfs_create_007_pos', 'zfs_create_008_neg', 'zfs_create_009_neg',
'zfs_create_010_neg', 'zfs_create_011_pos', 'zfs_create_012_pos',
- 'zfs_create_013_pos', 'zfs_create_encrypted', 'zfs_create_crypt_combos']
+ 'zfs_create_013_pos', 'zfs_create_encrypted', 'zfs_create_crypt_combos',
+ 'zfs_create_dryrun', 'zfs_create_verbose']
tags = ['functional', 'cli_root', 'zfs_create']
[tests/functional/cli_root/zfs_destroy]
@@ -194,7 +195,8 @@ tests = ['zfs_mount_001_pos', 'zfs_mount_002_pos', 'zfs_mount_003_pos',
'zfs_mount_007_pos', 'zfs_mount_008_pos', 'zfs_mount_009_neg',
'zfs_mount_010_neg', 'zfs_mount_011_neg', 'zfs_mount_012_neg',
'zfs_mount_all_001_pos', 'zfs_mount_all_fail', 'zfs_mount_all_mountpoints',
- 'zfs_mount_encrypted']
+ 'zfs_mount_encrypted', 'zfs_mount_watched_inotify',
+ 'zfs_mount_watched_portfs']
tags = ['functional', 'cli_root', 'zfs_mount']
[tests/functional/cli_root/zfs_program]
diff --git a/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_dryrun.ksh b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_dryrun.ksh
new file mode 100644
index 0000000000..64b8296f46
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_dryrun.ksh
@@ -0,0 +1,169 @@
+#!/bin/ksh -p
+#
+# 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 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zfs_create/zfs_create_common.kshlib
+
+#
+# DESCRIPTION:
+# zfs create -n should perform basic sanity checking but should never create a
+# dataset. If -v and/or -P are used, it should verbose about what would be
+# created if sanity checks pass.
+#
+# STRATEGY:
+# 1. Attempt to create a file system and a volume using various combinations of
+# -n with -v and -P.
+#
+
+verify_runnable "both"
+
+#
+# Verifies that valid commands with -n and without -[vP]:
+# - succeed
+# - do not create a dataset
+# - do not generate output
+#
+function dry_create_no_output
+{
+ typeset -a cmd=(zfs create -n "$@")
+
+ log_note "$0: ${cmd[@]}"
+ log_must "${cmd[@]}"
+ datasetexists "$TESTPOOL/$TESTFS1" &&
+ log_fail "$TESTPOOL/$TESTFS1 unexpectedly created by '${cmd[@]}'"
+ typeset out=$("${cmd[@]}" 2>&1)
+ [[ -z "$out" ]] ||
+ log_fail "unexpected output '$out' from '${cmd[@]}'"
+}
+
+#
+# Verifies that commands with invalid properties or invalid property values
+# - fail
+# - do not create a dataset
+# - generate a message on stderr
+#
+function dry_create_error
+{
+ typeset -a cmd=(zfs create -n "$@")
+
+ log_note "$0: ${cmd[@]}"
+ log_mustnot "${cmd[@]}"
+ datasetexists "$TESTPOOL/$TESTFS1" &&
+ log_fail "$TESTPOOL/$TESTFS1 unexpectedly created by '${cmd[@]}'"
+ typeset out=$("${cmd[@]}" 2>&1 >/dev/null)
+ [[ -z "$out" ]] &&
+ log_fail "expected an error message but got none from '${cmd[@]}'"
+}
+
+#
+# Verifies that dry-run commands with parseable output
+# - succeed
+# - do not create datasets
+# - generate parseable output on stdout
+# - output matches expectations
+#
+function dry_create_parseable
+{
+ typeset -n exp=$1
+ shift
+ typeset -a cmd=(zfs create -Pn "$@")
+ typeset ds=${cmd[${#cmd[@]} - 1]}
+ typeset out
+ typeset -a toks
+ typeset -a props
+ typeset found_create=false
+
+ log_note "$0: ${cmd[@]}"
+ out=$("${cmd[@]}")
+ (( $? == 0 )) ||
+ log_fail "unexpected failure getting stdout from '${cmd[@]}'"
+ datasetexists "$TESTPOOL/$TESTFS1" &&
+ log_fail "$TESTPOOL/$TESTFS1 unexpectedly created by '${cmd[@]}'"
+ echo "$out" | while IFS=$'\t' read -A toks; do
+ log_note "verifying ${toks[@]}"
+ case ${toks[0]} in
+ create)
+ log_must test "${#toks[@]}" -eq 2
+ log_must test "${toks[1]}" == "$ds"
+ found_create="yes, I found create"
+ ;;
+ property)
+ log_must test "${#toks[@]}" -eq 3
+ typeset prop=${toks[1]}
+ typeset val=${toks[2]}
+ if [[ -z "${exp[$prop]}" ]]; then
+ log_fail "unexpectedly got property '$prop'"
+ fi
+ # We may not know the exact value a property will take
+ # on. This is the case for at least refreservation.
+ if [[ ${exp[$prop]} != "*" ]]; then
+ log_must test "${exp[$prop]}" == "$val"
+ fi
+ unset exp[$prop]
+ ;;
+ *)
+ log_fail "Unexpected line ${toks[@]}"
+ ;;
+ esac
+ done
+
+ log_must test "$found_create" == "yes, I found create"
+ log_must test "extra props: ${!exp[@]}" == "extra props: "
+}
+
+function cleanup
+{
+ if datasetexists "$TESTPOOL/$TESTFS1"; then
+ log_must zfs destroy -r "$TESTPOOL/$TESTFS1"
+ fi
+}
+log_onexit cleanup
+
+log_assert "zfs create -n creates nothing but can describe what would be" \
+ "created"
+
+# Typical creations should succeed
+dry_create_no_output "$TESTPOOL/$TESTFS1"
+dry_create_no_output -V 10m "$TESTPOOL/$TESTFS1"
+# It shouldn't do a space check right now
+dry_create_no_output -V 100t "$TESTPOOL/$TESTFS1"
+# It shouldn't create parent datasets either
+dry_create_no_output -p "$TESTPOOL/$TESTFS1/$TESTFS2"
+dry_create_no_output -pV 10m "$TESTPOOL/$TESTFS1/$TESTFS2"
+
+# Various invalid properties should be recognized and result in an error
+dry_create_error -o nosuchprop=42 "$TESTPOOL/$TESTFS1"
+dry_create_error -b 1234 -V 10m "$TESTPOOL/$TESTFS1"
+
+# Parseable output should be parseable.
+typeset -A expect
+expect=([compression]=on)
+dry_create_parseable expect -o compression=on "$TESTPOOL/$TESTFS1"
+
+# Sparse volumes should not get a gratuitous refreservation
+expect=([volblocksize]=4096 [volsize]=$((1024 * 1024 * 10)))
+dry_create_parseable expect -b 4k -V 10m -s "$TESTPOOL/$TESTFS1"
+
+# Non-sparse volumes should have refreservation
+expect=(
+ [volblocksize]=4096
+ [volsize]=$((1024 * 1024 * 10))
+ [refreservation]="*"
+)
+dry_create_parseable expect -b 4k -V 10m "$TESTPOOL/$TESTFS1"
+
+log_pass "zfs create -n creates nothing but can describe what would be" \
+ "created"
diff --git a/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_inotify.ksh b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_inotify.ksh
new file mode 100644
index 0000000000..0f181621e8
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_inotify.ksh
@@ -0,0 +1,74 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# 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.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2020 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs mount' should not get EBUSY due to inotify(5) watching a directory
+#
+# STRATEGY:
+# 1. Create a directory
+# 2. Start watching the directory with inotify(5).
+# 3. Create a filesystem
+# 4. Mount the filesystem at the directory created in step 1
+# 5. Destroy the filesystem
+# 6. Remove the directory
+# 7. Verify the watcher saw the directory removal
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTFS1 && \
+ log_must zfs destroy -f $TESTPOOL/$TESTFS1
+ log_must rm -rf "$TESTDIR/mntpt" "$TESTDIR/watch_dir.log"
+}
+
+log_onexit cleanup
+
+log_assert "'zfs mount' should not get EBUSY due to inotify(5) watching a directory"
+
+# 1. Create a directory.
+log_must mkdir -p "$TESTDIR/mntpt"
+
+# 2. Start watching the directory with inotify(5).
+watch_dir inotify $TESTDIR/mntpt > $TESTDIR/watch_dir.log &
+
+# 3. Create a filesystem
+log_must zfs create $TESTPOOL/$TESTFS1
+
+# 4. Mount the file system at the directory created in step 1
+log_must zfs set mountpoint=$TESTDIR/mntpt $TESTPOOL/$TESTFS1
+
+# 5. Destroy the filesystem
+log_must zfs destroy $TESTPOOL/$TESTFS1
+
+# 6. Remove the directory. The corresponding inotify event will cause the
+# watcher to exit.
+log_must rmdir $TESTDIR/mntpt
+
+# 7. Verify the watcher saw the directory removal. This ensures that the watcher
+# was watching the directory we are interested in.
+wait
+log_must grep -q DELETE_SELF $TESTDIR/watch_dir.log
+
+log_pass "'zfs mount' should not get EBUSY due to inotify(5) watching a directory"
diff --git a/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_portfs.ksh b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_portfs.ksh
new file mode 100644
index 0000000000..3135203973
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_portfs.ksh
@@ -0,0 +1,74 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# 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.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2020 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs mount' should not get EBUSY due to portfs watching a directory
+#
+# STRATEGY:
+# 1. Create a directory
+# 2. Start watching the directory with port_associate
+# 3. Create a filesystem
+# 4. Mount the filesystem at the directory created in step 1
+# 5. Destroy the filesystem
+# 6. Remove the directory
+# 7. Verify the watcher saw the directory removal
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTFS1 && \
+ log_must zfs destroy -f $TESTPOOL/$TESTFS1
+ log_must rm -rf "$TESTDIR/mntpt" "$TESTDIR/watch_dir.log"
+}
+
+log_onexit cleanup
+
+log_assert "'zfs mount' should not get EBUSY due to portfs watching a directory"
+
+# 1. Create a directory.
+log_must mkdir -p "$TESTDIR/mntpt"
+
+# 2. Start watching the directory with port_associate
+watch_dir portfs $TESTDIR/mntpt > $TESTDIR/watch_dir.log &
+
+# 3. Create a filesystem
+log_must zfs create $TESTPOOL/$TESTFS1
+
+# 4. Mount the file system at the directory created in step 1
+log_must zfs set mountpoint=$TESTDIR/mntpt $TESTPOOL/$TESTFS1
+
+# 5. Destroy the filesystem
+log_must zfs destroy $TESTPOOL/$TESTFS1
+
+# 6. Remove the directory. The corresponding portfs event will cause the
+# watcher to exit.
+log_must rmdir $TESTDIR/mntpt
+
+# 7. Verify the watcher saw the directory removal. This ensures that the watcher
+# was watching the directory we are interested in.
+wait
+log_must grep -q DELETE.$TESTDIR/mntpt $TESTDIR/watch_dir.log
+
+log_pass "'zfs mount' should not get EBUSY due to portfs watching a directory"
diff --git a/usr/src/test/zfs-tests/tests/functional/mmp/mmp.cfg b/usr/src/test/zfs-tests/tests/functional/mmp/mmp.cfg
index 9f7e76e270..a58346dcae 100644
--- a/usr/src/test/zfs-tests/tests/functional/mmp/mmp.cfg
+++ b/usr/src/test/zfs-tests/tests/functional/mmp/mmp.cfg
@@ -15,6 +15,7 @@
#
# Copyright (c) 2017 by Lawrence Livermore National Security, LLC.
+# Copyright 2019 Joyent, Inc.
#
export PREV_UBER="$TEST_BASE_DIR/mmp-uber-prev.txt"
diff --git a/usr/src/test/zfs-tests/tests/functional/nopwrite/nopwrite_sync.ksh b/usr/src/test/zfs-tests/tests/functional/nopwrite/nopwrite_sync.ksh
index 415c8bdc5e..0ff8fcf97b 100644
--- a/usr/src/test/zfs-tests/tests/functional/nopwrite/nopwrite_sync.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/nopwrite/nopwrite_sync.ksh
@@ -13,6 +13,7 @@
#
# Copyright (c) 2012, 2016 by Delphix. All rights reserved.
+# Copyright 2019 Joyent, Inc.
#
. $STF_SUITE/include/libtest.shlib
@@ -42,12 +43,12 @@ log_assert "nopwrite works for sync writes"
log_must zfs set compress=on $origin
log_must zfs set checksum=sha256 $origin
-gnu_dd if=/dev/urandom of=$TESTDIR/file bs=1024k count=$MEGS oflag=sync \
+dd if=/dev/urandom of=$TESTDIR/file bs=1024k count=$MEGS oflag=sync \
conv=notrunc >/dev/null 2>&1 || log_fail "dd into $TESTDIR/file failed."
zfs snapshot $origin@a || log_fail "zfs snap failed"
log_must zfs clone $origin@a $origin/clone
-gnu_dd if=/$TESTDIR/file of=/$TESTDIR/clone/file bs=1024k count=$MEGS \
+dd if=/$TESTDIR/file of=/$TESTDIR/clone/file bs=1024k count=$MEGS \
oflag=sync conv=notrunc >/dev/null 2>&1 || log_fail "dd failed."
log_must verify_nopwrite $origin $origin@a $origin/clone
diff --git a/usr/src/test/zfs-tests/tests/functional/slog/slog.cfg b/usr/src/test/zfs-tests/tests/functional/slog/slog.cfg
index 6a28880ade..17f28abaac 100644
--- a/usr/src/test/zfs-tests/tests/functional/slog/slog.cfg
+++ b/usr/src/test/zfs-tests/tests/functional/slog/slog.cfg
@@ -26,10 +26,11 @@
#
# Copyright (c) 2013, 2015 by Delphix. All rights reserved.
+# Copyright 2019 Joyent, Inc.
#
-export VDIR=/disk-slog
-export VDIR2=/disk2-slog
+export VDIR=/var/tmp/disk-slog
+export VDIR2=/var/tmp/disk2-slog
export VDEV="$VDIR/a $VDIR/b $VDIR/c"
export SDEV="$VDIR/d"
diff --git a/usr/src/test/zfs-tests/tests/functional/slog/slog_014_pos.ksh b/usr/src/test/zfs-tests/tests/functional/slog/slog_014_pos.ksh
index efd876d554..63bd52288a 100644
--- a/usr/src/test/zfs-tests/tests/functional/slog/slog_014_pos.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/slog/slog_014_pos.ksh
@@ -27,6 +27,7 @@
#
# Copyright (c) 2013, 2018 by Delphix. All rights reserved.
+# Copyright 2019 Joyent, Inc.
# Copyright 2019 RackTop Systems.
#
diff --git a/usr/src/tools/Makefile b/usr/src/tools/Makefile
index e9acd189d8..68bd0e252a 100644
--- a/usr/src/tools/Makefile
+++ b/usr/src/tools/Makefile
@@ -69,19 +69,20 @@ COMMON_SUBDIRS= \
#
# special versions of commands for use only in build
#
-UNSHIPPED_SUBDIRS = \
- $(SGSMSG) \
- $(SGSLIBCONV) \
- $(SGSLIBELF) \
- $(SGSLIBLDDBG) \
- $(SGSLIBLD) \
- $(SGSLD) \
- geniconvtbl \
- localedef \
- mandoc \
- rpcgen \
- tic \
- vtfontcvt \
+UNSHIPPED_SUBDIRS = \
+ $(SGSMSG) \
+ $(SGSLIBCONV) \
+ $(SGSLIBELF) \
+ $(SGSLIBLDDBG) \
+ $(SGSLIBLD) \
+ $(SGSLD) \
+ geniconvtbl \
+ localedef \
+ man \
+ mandoc \
+ rpcgen \
+ tic \
+ vtfontcvt \
zic
sparc_SUBDIRS= \
diff --git a/usr/src/tools/README.tools b/usr/src/tools/README.tools
index b4d825a39f..33da97204b 100644
--- a/usr/src/tools/README.tools
+++ b/usr/src/tools/README.tools
@@ -147,6 +147,9 @@ findunref
sort > ~/unref-sparc.out
$ comm -12 ~/unref-i386.out ~/unref-sparc.out > ~/unref.out
+git-active
+ helper used by webrev to generate file lists for Git workspaces.
+
hdrchk
checks headers for compliance with OS/Net standards (form, includes,
C++ guards).
diff --git a/usr/src/tools/cpcgen/cpcgen.c b/usr/src/tools/cpcgen/cpcgen.c
index 779a782cc6..08384a20d2 100644
--- a/usr/src/tools/cpcgen/cpcgen.c
+++ b/usr/src/tools/cpcgen/cpcgen.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2019, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2021 Oxide Computer Company
*/
@@ -1836,7 +1836,7 @@ cpcgen_gen(int dirfd)
"name");
}
- if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0444)) < 0) {
+ if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) {
err(EXIT_FAILURE, "failed to create temporary file %s",
tmpname);
}
diff --git a/usr/src/tools/ctf/Makefile b/usr/src/tools/ctf/Makefile
index 172509dc2f..b8346d3eff 100644
--- a/usr/src/tools/ctf/Makefile
+++ b/usr/src/tools/ctf/Makefile
@@ -23,7 +23,7 @@
# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright 2019 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
#
include ../Makefile.tools
@@ -36,7 +36,6 @@ all := TARGET= all
install := TARGET= install
clean := TARGET= clean
clobber := TARGET= clobber
-lint := TARGET= lint
.KEEP_STATE:
@@ -45,9 +44,29 @@ ctfdiff: libctf
ctfdump: libctf
ctfconvert: libctf
-all clean clobber install lint: dwarf .WAIT $(SUBDIRS)
+all clean clobber install: dwarf .WAIT $(SUBDIRS)
dwarf $(SUBDIRS): FRC
@cd $@; pwd; $(MAKE) $(TARGET)
+#
+# This is a somewhat arbitrary list.
+#
+CTFTOOLS_FILES = \
+ bin/i386/ctfconvert \
+ bin/i386/ctfdiff \
+ bin/i386/ctfdump \
+ bin/i386/ctfstabs \
+ bin/i386/ctfstrip \
+ lib/i386/libctf.so \
+ lib/i386/libctf.so.1 \
+ lib/i386/libdwarf.so \
+ lib/i386/libdwarf.so.1 \
+ lib/i386/libelf.so \
+ lib/i386/libelf.so.1
+
+# CTFTOOLS_TARBALL should be set in the environment
+ctftools: install
+ cd $(ROOTONBLD) && $(TAR) cvzf $(CTFTOOLS_TARBALL) $(CTFTOOLS_FILES)
+
FRC:
diff --git a/usr/src/tools/ctf/Makefile.ctf b/usr/src/tools/ctf/Makefile.ctf
index c07310eddf..1fc0b707db 100644
--- a/usr/src/tools/ctf/Makefile.ctf
+++ b/usr/src/tools/ctf/Makefile.ctf
@@ -22,7 +22,7 @@
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright (c) 2018, Joyent, Inc.
+# Copyright (c) 2019, Joyent, Inc.
include ../../../Makefile.tools
diff --git a/usr/src/tools/ctf/common/utils.h b/usr/src/tools/ctf/common/utils.h
index 9b07361a53..4ae2dd0917 100644
--- a/usr/src/tools/ctf/common/utils.h
+++ b/usr/src/tools/ctf/common/utils.h
@@ -27,9 +27,8 @@
#ifndef _UTILS_H
#define _UTILS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <stdarg.h>
+#include <ctf_headers.h>
#ifdef __cplusplus
extern "C" {
diff --git a/usr/src/tools/ctf/dwarf/Makefile.com b/usr/src/tools/ctf/dwarf/Makefile.com
index 3f043681cb..886be7d853 100644
--- a/usr/src/tools/ctf/dwarf/Makefile.com
+++ b/usr/src/tools/ctf/dwarf/Makefile.com
@@ -96,7 +96,7 @@ FILEMODE = 0755
SRCDIR = $(SRC)/lib/libdwarf/common/
SRCS = $(PICS:%.o=$(SRCDIR)/%.c)
-CPPFLAGS += -I$(SRCDIR) -DELF_TARGET_ALL=1
+CPPFLAGS += -I$(ADJUNCT_PROTO)/usr/include -I$(SRCDIR) -DELF_TARGET_ALL=1
CERRWARN += -_gcc=-Wno-unused
CERRWARN += -_gcc=-Wno-implicit-function-declaration
@@ -104,7 +104,7 @@ CERRWARN += -_gcc=-Wno-implicit-function-declaration
SMATCH=off
DYNFLAGS += '-R$$ORIGIN/../../lib/$(MACH)'
-LDLIBS = -lelf -lc -lz
+LDLIBS = -L$(ADJUNCT_PROTO)/lib -R$(ADJUNCT_PROTO)/lib -lelf -lc -lz
NATIVE_LIBS += libelf.so libc.so libz.so
.KEEP_STATE:
diff --git a/usr/src/tools/cw/Makefile b/usr/src/tools/cw/Makefile
index 105c1a8c00..a3f2f8faa0 100644
--- a/usr/src/tools/cw/Makefile
+++ b/usr/src/tools/cw/Makefile
@@ -22,7 +22,7 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright 2018 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
PROG = cw
diff --git a/usr/src/tools/env/illumos.sh b/usr/src/tools/env/illumos.sh
index bde0dfc79f..25e897154f 100644
--- a/usr/src/tools/env/illumos.sh
+++ b/usr/src/tools/env/illumos.sh
@@ -287,6 +287,12 @@ export SPRO_VROOT="$SPRO_ROOT"
# if the 'N' option is not specified, is to run this test.
#CHECK_PATHS='y'
+#
+# These checks ensure that if we accidentally run a program linked against the
+# proto area, that we then fail the build.
+#
+export LD_TOXIC_PATH="$ROOT/lib:$ROOT/usr/lib"
+
if [[ "$ENABLE_SMATCH" == "1" ]]; then
SMATCHBIN=$CODEMGR_WS/usr/src/tools/proto/root_$MACH-nd/opt/onbld/bin/$MACH/smatch
export SHADOW_CCS="$SHADOW_CCS smatch,$SMATCHBIN,smatch"
diff --git a/usr/src/tools/findunref/exception_list.git b/usr/src/tools/findunref/exception_list.git
new file mode 100644
index 0000000000..d14dbd22f0
--- /dev/null
+++ b/usr/src/tools/findunref/exception_list.git
@@ -0,0 +1,39 @@
+#
+# 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 Cyril Plisko. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Git-specific exception list
+#
+# See README.exception_lists for details
+#
+
+#
+# Without nested repositories, this list could be empty, because ON
+# checks for unref relative to usr, and the git files are all in the
+# root of the repository.
+#
+
+*/.git
diff --git a/usr/src/tools/findunref/exception_list.unknown b/usr/src/tools/findunref/exception_list.unknown
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/usr/src/tools/findunref/exception_list.unknown
diff --git a/usr/src/tools/man/Makefile b/usr/src/tools/man/Makefile
new file mode 100644
index 0000000000..5f0e685323
--- /dev/null
+++ b/usr/src/tools/man/Makefile
@@ -0,0 +1,37 @@
+#
+# 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.
+#
+
+include $(SRC)/cmd/man/Makefile.com
+include ../Makefile.tools
+
+include ../Makefile.targ
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: $(SRC)/cmd/man/%.c
+ $(COMPILE.c) $<
+
+$(ROOTONBLDMACHPROG): $(PROG)
+
+install: $(ROOTONBLDMACHPROG)
+
+clean:
+ $(RM) $(OBJS) $(LINTFILES)
+
+include $(SRC)/tools/Makefile.targ
diff --git a/usr/src/tools/mandoc/Makefile b/usr/src/tools/mandoc/Makefile
index 5a0d085b3c..89afe86788 100644
--- a/usr/src/tools/mandoc/Makefile
+++ b/usr/src/tools/mandoc/Makefile
@@ -11,7 +11,7 @@
#
# Copyright 2017 Nexenta Systems, Inc.
-# Copyright 2018 Joyent, Inc.
+# Copyright 2019 Joyent, Inc.
#
CMDDIR= $(SRC)/cmd/mandoc
@@ -20,9 +20,9 @@ LCDIR= $(SRC)/lib/libc/port
include $(SRC)/tools/Makefile.tools
include $(CMDDIR)/Makefile.common
-CPPFLAGS += -_gcc=-nostdinc
-CPPFLAGS += -I$(NATIVE_ADJUNCT)/include
-LDFLAGS += -L$(NATIVE_ADJUNCT)/lib -R$(NATIVE_ADJUNCT)/lib
+CPPFLAGS += -_gcc=-nostdinc -I/usr/include -I$(ADJUNCT_PROTO)/usr/include
+CPPFLAGS += -D_FILE_OFFSET_BITS=64
+LDFLAGS += -L$(ADJUNCT_PROTO)/lib -R$(ADJUNCT_PROTO)/lib
# 3rd party code
SMATCH=off
diff --git a/usr/src/tools/onbld/Checks/Cddl.py b/usr/src/tools/onbld/Checks/Cddl.py
index e2bbf09c02..42cbca2f5a 100644
--- a/usr/src/tools/onbld/Checks/Cddl.py
+++ b/usr/src/tools/onbld/Checks/Cddl.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!ON_PYTHON
CDDL = '''
CDDL HEADER START
diff --git a/usr/src/tools/onbld/Checks/CmtBlk.py b/usr/src/tools/onbld/Checks/CmtBlk.py
index 57eb5704c1..9400fd7c80 100644
--- a/usr/src/tools/onbld/Checks/CmtBlk.py
+++ b/usr/src/tools/onbld/Checks/CmtBlk.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!ON_PYTHON
#
# CDDL HEADER START
diff --git a/usr/src/tools/onbld/Checks/Comments.py b/usr/src/tools/onbld/Checks/Comments.py
index 8285be2daa..0715bb1b40 100644
--- a/usr/src/tools/onbld/Checks/Comments.py
+++ b/usr/src/tools/onbld/Checks/Comments.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!ON_PYTHON
#
# CDDL HEADER START
#
@@ -41,7 +41,7 @@ from onbld.Checks.DbLookups import BugDB
from onbld.Checks.SpellCheck import spellcheck_line
-bugre = re.compile(r'^(\d{2,7}) (.*)$')
+bugre = re.compile(r'^(\d{2,7}|[A-Z]{1,7}-\d{1,7}) (.*)$')
def isBug(comment):
diff --git a/usr/src/tools/onbld/Checks/Copyright.py b/usr/src/tools/onbld/Checks/Copyright.py
index 81a80058aa..48b44e59ae 100644
--- a/usr/src/tools/onbld/Checks/Copyright.py
+++ b/usr/src/tools/onbld/Checks/Copyright.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!ON_PYTHON
#
# CDDL HEADER START
#
@@ -25,6 +25,8 @@
#
# Copyright 2008, 2010, Richard Lowe
+# Copyright 2019 Joyent, Inc.
+# Copyright 2022 MNX Cloud, Inc.
# Make sure there is a copyright claim for the current year.
@@ -37,7 +39,7 @@ def is_copyright(line):
return re.search(r'Copyright (?!\[yyyy\])', line)
def is_current_copyright(line):
- return re.search(r'Copyright.*\b%s\b' % time.strftime('%Y'), line)
+ return re.search(r'Copyright %s MNX Cloud, Inc.$' % time.strftime('%Y'), line)
def copyright(fh, filename=None, output=sys.stderr):
ret = rights = goodrights = 0
@@ -56,8 +58,8 @@ def copyright(fh, filename=None, output=sys.stderr):
err(output, "no copyright message found", filename)
ret = 1
elif goodrights == 0:
- err(output, "no copyright claim for current year found",
- filename)
+ err(output, "'Copyright %s MNX Cloud, Inc.' not found" %
+ time.strftime('%Y'), filename)
ret = 1
return ret
diff --git a/usr/src/tools/onbld/Checks/DbLookups.py b/usr/src/tools/onbld/Checks/DbLookups.py
index 84cbbe96bc..d396a68cbc 100644
--- a/usr/src/tools/onbld/Checks/DbLookups.py
+++ b/usr/src/tools/onbld/Checks/DbLookups.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!ON_PYTHON
#
# CDDL HEADER START
#
@@ -26,15 +26,16 @@
# Copyright 2010, Richard Lowe
# Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
+# Copyright (c) 2019, Joyent, Inc.
#
# Various database lookup classes/methods, i.e.:
-# * monaco
-# * bugs.opensolaris.org (b.o.o.)
# * redmine (illumos.org)
+# * smartos
#
import re
+import json
try:
from urllib.request import urlopen, Request
from urllib.error import HTTPError
@@ -66,9 +67,9 @@ class BugDB(object):
print r["6505625"]["synopsis"]
"""
- VALID_DBS = ["illumos"]
+ VALID_DBS = ["illumos", "smartos"]
- def __init__(self, priority = ["illumos"]):
+ def __init__(self, priority = VALID_DBS):
"""Create a BugDB object.
Keyword argument:
@@ -79,6 +80,24 @@ class BugDB(object):
raise BugDBException(database)
self.__priority = priority
+ def __smartosbug(self, cr):
+ url = "http://smartos.org/bugview/json/%s" % cr
+ req = Request(url)
+
+ try:
+ data = urlopen(req)
+ except HTTPError as e:
+ if e.code == 404 or e.code == 403 or e.code == 400:
+ raise NonExistentBug(cr)
+ else:
+ raise
+
+ bug = json.load(data)
+
+ return {'cr_number': bug['id'],
+ 'synopsis': bug['summary']
+ }
+
def __illbug(self, cr):
url = "http://illumos.org/issues/%s.xml" % cr
@@ -121,6 +140,12 @@ class BugDB(object):
results[str(cr)] = self.__illbug(cr)
except NonExistentBug:
continue
+ elif database == "smartos":
+ for cr in crs:
+ try:
+ results[str(cr)] = self.__smartosbug(cr)
+ except NonExistentBug:
+ continue
# the CR has already been found by one bug database
# so don't bother looking it up in the others
diff --git a/usr/src/tools/onbld/Checks/HdrChk.py b/usr/src/tools/onbld/Checks/HdrChk.py
index c2697dcaf2..8f7b946d12 100644
--- a/usr/src/tools/onbld/Checks/HdrChk.py
+++ b/usr/src/tools/onbld/Checks/HdrChk.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!ON_PYTHON
#
# CDDL HEADER START
#
diff --git a/usr/src/tools/onbld/Checks/Keywords.py b/usr/src/tools/onbld/Checks/Keywords.py
index cd4dda0b69..0866178756 100644
--- a/usr/src/tools/onbld/Checks/Keywords.py
+++ b/usr/src/tools/onbld/Checks/Keywords.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!ON_PYTHON
#
# CDDL HEADER START
#
diff --git a/usr/src/tools/onbld/Checks/Mapfile.py b/usr/src/tools/onbld/Checks/Mapfile.py
index 5b40d26005..e4166c5792 100644
--- a/usr/src/tools/onbld/Checks/Mapfile.py
+++ b/usr/src/tools/onbld/Checks/Mapfile.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!ON_PYTHON
#
# CDDL HEADER START
#
diff --git a/usr/src/tools/onbld/Checks/__init__.py b/usr/src/tools/onbld/Checks/__init__.py
index 775c5a6e66..afde98c7da 100644
--- a/usr/src/tools/onbld/Checks/__init__.py
+++ b/usr/src/tools/onbld/Checks/__init__.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!ON_PYTHON
#
# CDDL HEADER START
#
diff --git a/usr/src/tools/onbld/Scm/__init__.py b/usr/src/tools/onbld/Scm/__init__.py
index f45ecbc95f..8934eb3942 100644
--- a/usr/src/tools/onbld/Scm/__init__.py
+++ b/usr/src/tools/onbld/Scm/__init__.py
@@ -1,4 +1,4 @@
-#! /usr/bin/python
+#!ON_PYTHON
#
# CDDL HEADER START
#
@@ -24,5 +24,3 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
-#
diff --git a/usr/src/tools/protocmp/stdusers.c b/usr/src/tools/protocmp/stdusers.c
index a82b2e0b6d..5564062f1b 100644
--- a/usr/src/tools/protocmp/stdusers.c
+++ b/usr/src/tools/protocmp/stdusers.c
@@ -39,6 +39,7 @@ const struct stdlist usernames[] = {
{ "dladm", 15 },
{ "netadm", 16 },
{ "netcfg", 17 },
+ { "smmsp", 25 },
{ "listen", 37 },
{ "gdm", 50 },
{ "lp", 71 },
@@ -67,6 +68,7 @@ const struct stdlist groupnames[] = {
{ "daemon", 12 },
{ "sysadmin", 14 },
{ "games", 20 },
+ { "smmsp", 25 },
{ "gdm", 50 },
{ "netadm", 65 },
{ "mysql", 70 },
diff --git a/usr/src/tools/scripts/webrev.sh b/usr/src/tools/scripts/webrev.sh
index a28e90eb02..66fd2ab854 100644
--- a/usr/src/tools/scripts/webrev.sh
+++ b/usr/src/tools/scripts/webrev.sh
@@ -2884,7 +2884,12 @@ done
#
# Output directory.
#
-WDIR=${WDIR:-$CWS/webrev}
+if [[ $SCM_MODE == "git" ]]; then
+ ws_top_dir=$(dirname $CWS)
+ WDIR=${WDIR:-$ws_top_dir/webrev}
+else
+ WDIR=${WDIR:-$CWS/webrev}
+fi
#
# Name of the webrev, derived from the workspace name or output directory;
diff --git a/usr/src/tools/smatch/Makefile b/usr/src/tools/smatch/Makefile
index 2d0365c458..4e59279962 100644
--- a/usr/src/tools/smatch/Makefile
+++ b/usr/src/tools/smatch/Makefile
@@ -48,7 +48,15 @@ CFLAGS += -DMULTIARCH_TRIPLET=NULL
LDCHECKS = $(ZASSERTDEFLIB) $(ZGUIDANCE) $(ZFATALWARNINGS)
LDLIBS += $(ZLAZYLOAD) $(ZDIRECT) -lsqlite3 -lmd -lgcc -lm -lc
LDFLAGS = $(MAPFILE.NES:%=-Wl,-M%)
+
+#
+# To allow linking to $(NATIVE_ADJUNCT) libraries on both multiarch (where
+# 64-bit libraries will be found in $(NATIVE_ADJUNCT)/lib/$(MACH64)) and 64-bit
+# images (where 64-bit libraries will be found in $(NATIVE_ADJUNCT)/lib/), we
+# set our library and run paths to both locations.
+#
LDFLAGS += -L$(NATIVE_ADJUNCT)/lib -R$(NATIVE_ADJUNCT)/lib
+LDFLAGS += -L$(NATIVE_ADJUNCT)/lib/$(MACH64) -R$(NATIVE_ADJUNCT)/lib/$(MACH64)
NATIVE_LIBS += libsqlite3.so libmd.so libm.so libc.so
LDFLAGS += $(NATIVE_LIBS:%=$(ZASSERTDEFLIB)=%)
@@ -56,6 +64,7 @@ LDFLAGS += $(NATIVE_LIBS:%=$(ZASSERTDEFLIB)=%)
CPPFLAGS += -nostdinc
CPPFLAGS += -Isrc/
CPPFLAGS += -I$(NATIVE_ADJUNCT)/include
+CPPFLAGS += -I/usr/include
# no install.bin
INS.file = $(RM) $@; $(CP) $< $(@D); $(CHMOD) $(FILEMODE) $@
diff --git a/usr/src/tools/vtfontcvt/Makefile b/usr/src/tools/vtfontcvt/Makefile
index 49e996a908..166022685a 100644
--- a/usr/src/tools/vtfontcvt/Makefile
+++ b/usr/src/tools/vtfontcvt/Makefile
@@ -11,6 +11,7 @@
#
# Copyright 2017 Toomas Soome <tsoome@me.com>
+# Copyright 2019 Joyent, Inc.
#
CMDDIR= $(SRC)/cmd/vtfontcvt
@@ -33,7 +34,13 @@ $(PROG): $(OBJS)
$(LINK.c) $(OBJS) -o $@ $(LDLIBS)
$(POST_PROCESS)
+#
+# In order to build on older platforms, we specifically use the
+# source-tree version of queue.h. When building as part of
+# $SRC/cmd, we'll be using the $PROTO copy of queue.h anyway,
+# so this is a $SRC/tools workaround only.
+#
%.o: $(CMDDIR)/%.c
- $(COMPILE.c) -o $@ $<
+ $(COMPILE.c) -I$(SRC)/uts/common -o $@ $<
include ../Makefile.targ
diff --git a/usr/src/ucbhead/Makefile b/usr/src/ucbhead/Makefile
new file mode 100644
index 0000000000..ef9fc7f2fe
--- /dev/null
+++ b/usr/src/ucbhead/Makefile
@@ -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 1989-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ucbhead/Makefile
+#
+# include global definitions
+include ../Makefile.master
+
+LN= ln
+
+HDRS= curses.h dbm.h setjmp.h sgtty.h signal.h stdio.h strings.h unistd.h
+
+SYSHDRS= \
+dir.h fcntl.h file.h ioctl.h \
+param.h resource.h rusage.h signal.h \
+sysmacros.h ttychars.h types.h vfs.h \
+wait.h
+
+ROOTHDRS= $(HDRS:%=$(ROOT)/usr/ucbinclude/%) \
+ $(SYSHDRS:%=$(ROOT)/usr/ucbinclude/sys/%)
+
+DIRS= sys
+ROOTDIRS= $(ROOT)/usr/ucbinclude $(DIRS:%=$(ROOT)/usr/ucbinclude/%)
+
+FCNTLH= $(ROOT)/usr/ucbinclude/fcntl.h
+SYSFCNTLH= $(ROOT)/usr/ucbinclude/sys/fcntl.h
+
+INS.FCNTLH= $(RM) $@; $(SYMLINK) sys/fcntl.h $@
+
+# install rules
+$(ROOT)/usr/ucbinclude/sys/%: sys/%
+ $(INS.file)
+
+$(ROOT)/usr/ucbinclude/%: %
+ $(INS.file)
+
+.KEEP_STATE:
+
+install_h: $(ROOTDIRS) $(ROOTHDRS) $(FCNTLH)
+
+$(FCNTLH): $(SYSFCNTLH)
+ $(INS.FCNTLH)
+
+$(ROOTDIRS):
+ $(INS.dir)
+
diff --git a/usr/src/ucbhead/sys/file.h b/usr/src/ucbhead/sys/file.h
index 10863380dd..152141fcd7 100644
--- a/usr/src/ucbhead/sys/file.h
+++ b/usr/src/ucbhead/sys/file.h
@@ -77,6 +77,10 @@ typedef struct file
#include <sys/fcntl.h>
#endif
+#if !defined(__XOPEN_OR_POSIX) || defined(__EXTENSIONS__)
+int flock(int, int);
+#endif
+
/* flags - see also fcntl.h */
#ifndef FOPEN
diff --git a/usr/src/ucblib/libucb/port/sys/flock.c b/usr/src/ucblib/libucb/port/sys/flock.c
index d29a07eac0..364d4f1f5f 100644
--- a/usr/src/ucblib/libucb/port/sys/flock.c
+++ b/usr/src/ucblib/libucb/port/sys/flock.c
@@ -34,9 +34,9 @@
/*LINTLIBRARY*/
+#include <sys/fcntl.h>
#include <sys/types.h>
#include <sys/file.h>
-#include <sys/fcntl.h>
#include <errno.h>
int
diff --git a/usr/src/uts/Makefile.targ b/usr/src/uts/Makefile.targ
index 0807ad70dc..c7d771d6e5 100644
--- a/usr/src/uts/Makefile.targ
+++ b/usr/src/uts/Makefile.targ
@@ -20,6 +20,7 @@
#
#
# Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2019 Joyent, Inc.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
# Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
# Copyright (c) 2017 by Delphix. All rights reserved.
diff --git a/usr/src/uts/Makefile.uts b/usr/src/uts/Makefile.uts
index db66dd229d..3c4a307c4c 100644
--- a/usr/src/uts/Makefile.uts
+++ b/usr/src/uts/Makefile.uts
@@ -184,6 +184,15 @@ CERRWARN += -_smatch=-p=illumos_kernel
include $(SRC)/Makefile.smatch
#
+# Add specific compiler options that are required based on the
+# architecture in question.
+#
+CFLAGS_uts_i386 += -_gcc7=-mindirect-branch=thunk-extern
+CFLAGS_uts_i386 += -_gcc7=-mindirect-branch-register
+CFLAGS_uts_i386 += -_gcc8=-mindirect-branch=thunk-extern
+CFLAGS_uts_i386 += -_gcc8=-mindirect-branch-register
+
+#
# Ensure that the standard function prologue remains at the very start
# of a function, so DTrace fbt will instrument the right place.
#
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index 51392a6b4f..0c60127800 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -25,7 +25,7 @@
# Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
# Copyright 2018 Nexenta Systems, Inc.
# Copyright 2022 Garrett D'Amore
-# Copyright 2020 Joyent, Inc.
+# Copyright 2021 Joyent, Inc.
# Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
# Copyright 2022 RackTop Systems, Inc.
@@ -124,6 +124,7 @@ GENUNIX_OBJS += \
bz2huffman.o \
callb.o \
callout.o \
+ chacha.o \
chdir.o \
chmod.o \
chown.o \
@@ -445,6 +446,8 @@ PROFILE_OBJS += profile.o
SYSTRACE_OBJS += systrace.o
+LX_SYSTRACE_OBJS += lx_systrace.o
+
LOCKSTAT_OBJS += lockstat.o
FASTTRAP_OBJS += fasttrap.o fasttrap_isa.o
@@ -515,6 +518,10 @@ PTSL_OBJS += tty_pts.o
PTM_OBJS += ptm.o
+LX_PTM_OBJS += lx_ptm.o
+
+LX_NETLINK_OBJS += lx_netlink.o
+
MII_OBJS += mii.o mii_cicada.o mii_natsemi.o mii_intel.o mii_qualsemi.o \
mii_marvell.o mii_realtek.o mii_other.o
@@ -583,6 +590,7 @@ IP_OBJS += igmp.o ipmp.o ip.o ip6.o ip6_asp.o ip6_if.o ip6_ire.o \
ip_helper_stream.o ip_tunables.o \
ip_output.o ip_input.o ip6_input.o ip6_output.o ip_arp.o \
conn_opt.o ip_attr.o ip_dce.o \
+ bpf_filter.o \
$(IP_ICMP_OBJS) \
$(IP_RTS_OBJS) \
$(IP_TCP_OBJS) \
@@ -607,6 +615,8 @@ IPSECESP_OBJS += ipsecespddi.o ipsecesp.o
IPSECAH_OBJS += ipsecahddi.o ipsecah.o sadb.o
+DATAFILT_OBJS += datafilt.o
+
SPPP_OBJS += sppp.o sppp_dlpi.o sppp_mod.o s_common.o
SPPPTUN_OBJS += sppptun.o sppptun_mod.o
@@ -658,7 +668,7 @@ TL_OBJS += tl.o
DUMP_OBJS += dump.o
-BPF_OBJS += bpf.o bpf_filter.o bpf_mod.o bpf_dlt.o bpf_mac.o
+BPF_OBJS += bpf.o bpf_wrap.o bpf_mod.o bpf_dlt.o bpf_mac.o
CLONE_OBJS += clone.o
@@ -707,6 +717,10 @@ OVERLAY_OBJS += overlay.o overlay_fm.o overlay_mux.o overlay_plugin.o \
OVERLAY_VXLAN_OBJS += overlay_vxlan.o
+VND_OBJS += vnd.o frameio.o
+
+GSQUEUE_OBJS += gsqueue.o
+
SIMNET_OBJS += simnet.o
IB_OBJS += ibnex.o ibnex_ioctl.o ibnex_hca.o
@@ -957,6 +971,8 @@ SIGNALFD_OBJS += signalfd.o
I8042_OBJS += i8042.o
+INOTIFY_OBJS += inotify.o
+
KB8042_OBJS += \
at_keyprocess.o \
kb8042.o \
@@ -1031,6 +1047,8 @@ QLGE_OBJS += qlge.o qlge_dbg.o qlge_flash.o qlge_fm.o qlge_gld.o qlge_mpi.o
ZCONS_OBJS += zcons.o
+ZFD_OBJS += zfd.o
+
NV_SATA_OBJS += nv_sata.o
SI3124_OBJS += si3124.o
@@ -1095,8 +1113,13 @@ PIPE_OBJS += pipe.o
HSFS_OBJS += hsfs_node.o hsfs_subr.o hsfs_vfsops.o hsfs_vnops.o \
hsfs_susp.o hsfs_rrip.o hsfs_susp_subr.o
+HYPRLOFS_OBJS += hyprlofs_dir.o hyprlofs_subr.o \
+ hyprlofs_vnops.o hyprlofs_vfsops.o
+
LOFS_OBJS += lofs_subr.o lofs_vfsops.o lofs_vnops.o
+LXPROC_OBJS += lxpr_subr.o lxpr_vfsops.o lxpr_vnops.o
+
NAMEFS_OBJS += namevfs.o namevno.o
NFS_OBJS += nfs_client.o nfs_common.o nfs_dump.o \
@@ -1262,8 +1285,8 @@ SMBSRV_OBJS += $(SMBSRV_SHARED_OBJS) \
PCFS_OBJS += pc_alloc.o pc_dir.o pc_node.o pc_subr.o \
pc_vfsops.o pc_vnops.o
-PROC_OBJS += prcontrol.o prioctl.o prsubr.o prusrio.o \
- prvfsops.o prvnops.o
+PROC_OBJS += prargv.o prcontrol.o prioctl.o prsubr.o \
+ prusrio.o prvfsops.o prvnops.o
MNTFS_OBJS += mntvfsops.o mntvnops.o
@@ -1444,6 +1467,7 @@ ZFS_COMMON_OBJS += \
zfs_fuid.o \
zfs_sa.o \
zfs_znode.o \
+ zfs_zone.o \
zil.o \
zio.o \
zio_checksum.o \
@@ -1805,8 +1829,8 @@ SCSA2USB_OBJS += scsa2usb.o usb_ms_bulkonly.o usb_ms_cbi.o
CCID_OBJS += ccid.o atr.o
-IPF_OBJS += ip_fil_solaris.o fil.o solaris.o ip_state.o ip_frag.o ip_nat.o \
- ip_proxy.o ip_auth.o ip_pool.o ip_htable.o ip_lookup.o \
+IPF_OBJS += cfw.o ip_fil_solaris.o fil.o solaris.o ip_state.o ip_frag.o \
+ ip_nat.o ip_proxy.o ip_auth.o ip_pool.o ip_htable.o ip_lookup.o \
ip_log.o misc.o ip_compat.o ip_nat6.o drand48.o
IPD_OBJS += ipd.o
@@ -2209,6 +2233,11 @@ URF_OBJS = urf_usbgem.o
UPF_OBJS = upf_usbgem.o
#
+# NFP objects
+#
+NFP_OBJS = hostif.o osif.o drvlist.o i21555.o i21285.o i21555d.o
+
+#
# BNXE objects
#
BNXE_OBJS += bnxe_cfg.o \
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index 83d58e522b..705d794670 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -23,7 +23,7 @@
# Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2022 Garrett D'Amore <garrett@damore.org>
# Copyright 2013 Saso Kiselkov. All rights reserved.
-# Copyright 2019 Joyent, Inc.
+# Copyright 2021 Joyent, Inc.
# Copyright 2018 Nexenta Systems, Inc.
# Copyright (c) 2017 by Delphix. All rights reserved.
# Copyright 2022 Oxide Computer Company
@@ -280,10 +280,18 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/hsfs/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/hyprlofs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/lofs/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/lxproc/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/fs/mntfs/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -986,6 +994,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/net80211/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/nfp/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/nge/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1161,6 +1173,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/sdcard/targets/sdcard/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/gsqueue/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/sfe/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1173,6 +1189,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/softmac/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/vnd/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/uath/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1525,6 +1545,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/vioblk/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(COMMONBASE)/idspace/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/vioif/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -1589,6 +1613,10 @@ $(OBJS_DIR)/%.o: $(COMMONBASE)/nvpair/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(COMMONBASE)/refhash/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/bootbanner.o := CPPFLAGS += \
-DBOOTBANNER1='"$(BOOTBANNER1)"' \
-DBOOTBANNER2='"$(BOOTBANNER2)"' \
@@ -1632,6 +1660,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/rpc/sec_gss/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(COMMONBASE)/crypto/chacha/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(COMMONBASE)/crypto/edonr/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
diff --git a/usr/src/uts/common/brand/lx/autofs/lx_autofs.c b/usr/src/uts/common/brand/lx/autofs/lx_autofs.c
new file mode 100644
index 0000000000..364215d026
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/autofs/lx_autofs.c
@@ -0,0 +1,3177 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * See the big theory statement in ../sys/lx_autofs.h
+ */
+
+#include <fs/fs_subr.h>
+#include <sys/stat.h>
+#include <sys/atomic.h>
+#include <sys/cmn_err.h>
+#include <sys/dirent.h>
+#include <sys/fs/fifonode.h>
+#include <sys/modctl.h>
+#include <sys/mount.h>
+#include <sys/policy.h>
+#include <sys/sunddi.h>
+#include <sys/conf.h>
+#include <sys/sdt.h>
+
+#include <sys/sysmacros.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+
+#include <sys/dnlc.h>
+#include <nfs/rnode.h>
+#include <nfs/rnode4.h>
+#include <sys/lx_autofs_impl.h>
+#include <sys/lx_types.h>
+
+/*
+ * External functions
+ */
+extern uintptr_t space_fetch(char *key);
+extern int space_store(char *key, uintptr_t ptr);
+extern int umount2_engine(vfs_t *, int, cred_t *, int);
+
+/*
+ * Globals
+ */
+static vfsops_t *lx_autofs_vfsops;
+static vnodeops_t *lx_autofs_vn_ops = NULL;
+static int lx_autofs_fstype;
+static major_t lx_autofs_major;
+static minor_t lx_autofs_minor = 0;
+static dev_info_t *lx_autofs_dip = NULL;
+
+#define LX_AUTOFS_DEV_VERSION_MAJOR 1
+#define LX_AUTOFS_DEV_VERSION_MINOR 0
+
+/* The Linux autofs superblock magic number */
+#define LX_AUTOFS_SB_MAGIC 0x0187
+
+/* Linux autofs mount types */
+#define LX_AUTOFS_TYPE_INDIRECT 1
+#define LX_AUTOFS_TYPE_DIRECT 2
+#define LX_AUTOFS_TYPE_OFFSET 4
+
+/* Structure passed for autofs dev ioctls */
+typedef struct lx_autofs_dv_ioctl {
+ uint32_t lad_ver_major;
+ uint32_t lad_ver_minor;
+ uint32_t lad_size;
+ uint32_t lad_ioctlfd;
+ uint32_t lad_arg1;
+ uint32_t lad_arg2;
+ char lad_path[0];
+} lx_autofs_dv_ioctl_t;
+
+/*
+ * Support functions
+ */
+static void
+lx_autofs_strfree(char *str)
+{
+ kmem_free(str, strlen(str) + 1);
+}
+
+static char *
+lx_autofs_strdup(char *str)
+{
+ int n = strlen(str);
+ char *ptr = kmem_alloc(n + 1, KM_SLEEP);
+ bcopy(str, ptr, n + 1);
+ return (ptr);
+}
+
+static int
+lx_autofs_str_to_int(char *str, int *val)
+{
+ long res;
+
+ if (str == NULL)
+ return (-1);
+
+ if ((ddi_strtol(str, NULL, 10, &res) != 0) ||
+ (res < INT_MIN) || (res > INT_MAX))
+ return (-1);
+
+ *val = res;
+ return (0);
+}
+
+static void
+ls_autofs_stack_init(list_t *lp)
+{
+ list_create(lp,
+ sizeof (stack_elem_t), offsetof(stack_elem_t, se_list));
+}
+
+static void
+lx_autofs_stack_fini(list_t *lp)
+{
+ ASSERT(list_head(lp) == NULL);
+ list_destroy(lp);
+}
+
+static void
+lx_autofs_stack_push(list_t *lp, caddr_t ptr1, caddr_t ptr2, caddr_t ptr3)
+{
+ stack_elem_t *se;
+
+ se = kmem_alloc(sizeof (*se), KM_SLEEP);
+ se->se_ptr1 = ptr1;
+ se->se_ptr2 = ptr2;
+ se->se_ptr3 = ptr3;
+ list_insert_head(lp, se);
+}
+
+static int
+lx_autofs_stack_pop(list_t *lp, caddr_t *ptr1, caddr_t *ptr2, caddr_t *ptr3)
+{
+ stack_elem_t *se;
+
+ if ((se = list_head(lp)) == NULL)
+ return (-1);
+ list_remove(lp, se);
+ if (ptr1 != NULL)
+ *ptr1 = se->se_ptr1;
+ if (ptr2 != NULL)
+ *ptr2 = se->se_ptr2;
+ if (ptr3 != NULL)
+ *ptr3 = se->se_ptr3;
+ kmem_free(se, sizeof (*se));
+ return (0);
+}
+
+static vnode_t *
+lx_autofs_fifo_peer_vp(vnode_t *vp)
+{
+ fifonode_t *fnp = VTOF(vp);
+ fifonode_t *fn_dest = fnp->fn_dest;
+ return (FTOV(fn_dest));
+}
+
+static vnode_t *
+lx_autofs_vn_alloc(vfs_t *vfsp, vnode_t *uvp)
+{
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ vnode_t *vp, *vp_old;
+
+ /* Allocate a new vnode structure in case we need it. */
+ vp = vn_alloc(KM_SLEEP);
+ vn_setops(vp, lx_autofs_vn_ops);
+ VN_SET_VFS_TYPE_DEV(vp, vfsp, uvp->v_type, uvp->v_rdev);
+ vp->v_data = uvp;
+ ASSERT(vp->v_count == 1);
+
+ /*
+ * Take a hold on the vfs structure. This is how unmount will
+ * determine if there are any active vnodes in the file system.
+ */
+ VFS_HOLD(vfsp);
+
+ /*
+ * Check if we already have a vnode allocated for this underlying
+ * vnode_t.
+ */
+ mutex_enter(&data->lav_lock);
+ if (mod_hash_find(data->lav_vn_hash,
+ (mod_hash_key_t)uvp, (mod_hash_val_t *)&vp_old) != 0) {
+
+ /*
+ * Didn't find an existing node.
+ * Add this node to the hash and return.
+ */
+ VERIFY(mod_hash_insert(data->lav_vn_hash,
+ (mod_hash_key_t)uvp,
+ (mod_hash_val_t)vp) == 0);
+ mutex_exit(&data->lav_lock);
+ return (vp);
+ }
+
+ /* Get a hold on the existing vnode and free up the one we allocated. */
+ VN_HOLD(vp_old);
+ mutex_exit(&data->lav_lock);
+
+ /* Free up the new vnode we allocated. */
+ VN_RELE(uvp);
+ VFS_RELE(vfsp);
+ vn_invalid(vp);
+ vn_free(vp);
+
+ return (vp_old);
+}
+
+static void
+lx_autofs_vn_free(vnode_t *vp)
+{
+ vfs_t *vfsp = vp->v_vfsp;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ vnode_t *uvp = vp->v_data;
+ vnode_t *vp_tmp;
+
+ ASSERT(MUTEX_HELD((&data->lav_lock)));
+ ASSERT(MUTEX_HELD((&vp->v_lock)));
+
+ ASSERT(vp->v_count == 0);
+
+ /* We're about to free this vnode so take it out of the hash. */
+ (void) mod_hash_remove(data->lav_vn_hash,
+ (mod_hash_key_t)uvp, (mod_hash_val_t)&vp_tmp);
+
+ /*
+ * No one else can lookup this vnode any more so there's no need
+ * to hold locks.
+ */
+ mutex_exit(&data->lav_lock);
+ mutex_exit(&vp->v_lock);
+
+ /* Release the underlying vnode. */
+ VN_RELE(uvp);
+ VFS_RELE(vfsp);
+ vn_invalid(vp);
+ vn_free(vp);
+}
+
+static lx_autofs_automnt_req_t *
+lx_autofs_la_alloc(lx_autofs_vfs_t *data, boolean_t *is_dup, boolean_t expire,
+ char *nm)
+{
+ lx_autofs_automnt_req_t *laar, *laar_dup;
+
+ /* Pre-allocate a new automounter request before grabbing locks. */
+ laar = kmem_zalloc(sizeof (*laar), KM_SLEEP);
+ mutex_init(&laar->laar_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&laar->laar_cv, NULL, CV_DEFAULT, NULL);
+ laar->laar_ref = 1;
+
+ if (data->lav_min_proto == 5) {
+ laar->laar_pkt.lap_protover = LX_AUTOFS_PROTO_VERS5;
+
+ if (data->lav_mnttype == LXAMT_INDIR) {
+ if (expire) {
+ laar->laar_pkt.lap_type =
+ LX_AUTOFS_PTYPE_EXPIRE_INDIR;
+ } else {
+ laar->laar_pkt.lap_type =
+ LX_AUTOFS_PTYPE_MISSING_INDIR;
+ }
+ } else {
+ if (expire) {
+ laar->laar_pkt.lap_type =
+ LX_AUTOFS_PTYPE_EXPIRE_DIRECT;
+ } else {
+ laar->laar_pkt.lap_type =
+ LX_AUTOFS_PTYPE_MISSING_DIRECT;
+ }
+ }
+ laar->laar_pkt_size = sizeof (lx_autofs_v5_pkt_t);
+
+ laar->laar_pkt.lap_v5.lap_dev = data->lav_dev;
+ laar->laar_pkt.lap_v5.lap_ino = data->lav_ino;
+ /*
+ * Note that we're currently not filling in the other v5 pkt
+ * fields (pid, uid, etc.) since they don't appear to be used
+ * by the automounter. We can fill those in later if it proves
+ * necessary.
+ */
+
+ /*
+ * For indirect mounts the token expected by the automounter is
+ * the name of the directory entry to look up (not the entire
+ * path that is being accessed.) For direct mounts the Linux
+ * kernel passes a dummy name, so this is just as good.
+ */
+ laar->laar_pkt.lap_v5.lap_name_len = strlen(nm);
+ if (laar->laar_pkt.lap_v5.lap_name_len >
+ (sizeof (laar->laar_pkt.lap_v5.lap_name) - 1)) {
+ zcmn_err(getzoneid(), CE_NOTE,
+ "invalid autofs automnt req: \"%s\"", nm);
+ kmem_free(laar, sizeof (*laar));
+ return (NULL);
+ }
+ (void) strlcpy(laar->laar_pkt.lap_v5.lap_name, nm,
+ sizeof (laar->laar_pkt.lap_v5.lap_name));
+
+ } else if (expire) {
+ zcmn_err(getzoneid(), CE_WARN,
+ "unsupported expire protocol request: \"%s\"", nm);
+ kmem_free(laar, sizeof (*laar));
+ return (NULL);
+
+ } else {
+ ASSERT(expire == B_FALSE);
+
+ /* Older protocol pkt (really v2) */
+ laar->laar_pkt.lap_protover = LX_AUTOFS_PROTO_VERS2;
+ laar->laar_pkt.lap_type = LX_AUTOFS_PTYPE_MISSING;
+ laar->laar_pkt_size = sizeof (lx_autofs_v2_pkt_t);
+
+ /*
+ * The token expected by the linux automount is the name of
+ * the directory entry to look up. (And not the entire
+ * path that is being accessed.)
+ */
+ laar->laar_pkt.lap_v2.lap_name_len = strlen(nm);
+ if (laar->laar_pkt.lap_v2.lap_name_len >
+ (sizeof (laar->laar_pkt.lap_v2.lap_name) - 1)) {
+ zcmn_err(getzoneid(), CE_NOTE,
+ "invalid autofs lookup: \"%s\"", nm);
+ kmem_free(laar, sizeof (*laar));
+ return (NULL);
+ }
+ (void) strlcpy(laar->laar_pkt.lap_v2.lap_name, nm,
+ sizeof (laar->laar_pkt.lap_v2.lap_name));
+ }
+
+ /* Assign a unique id for this request. */
+ laar->laar_pkt.lap_id = id_alloc(data->lav_ids);
+
+ /* Check for an outstanding request for this path. */
+ mutex_enter(&data->lav_lock);
+ if (mod_hash_find(data->lav_path_hash,
+ (mod_hash_key_t)nm, (mod_hash_val_t *)&laar_dup) == 0) {
+ /*
+ * There's already an outstanding request for this
+ * path so we don't need a new one.
+ */
+ id_free(data->lav_ids, laar->laar_pkt.lap_id);
+ kmem_free(laar, sizeof (*laar));
+ laar = laar_dup;
+
+ /* Bump the ref count on the old request. */
+ atomic_add_int(&laar->laar_ref, 1);
+
+ *is_dup = 1;
+ } else {
+ /* Add it to the hashes. */
+ VERIFY(mod_hash_insert(data->lav_id_hash,
+ (mod_hash_key_t)(uintptr_t)laar->laar_pkt.lap_id,
+ (mod_hash_val_t)laar) == 0);
+ VERIFY(mod_hash_insert(data->lav_path_hash,
+ (mod_hash_key_t)lx_autofs_strdup(nm),
+ (mod_hash_val_t)laar) == 0);
+
+ *is_dup = 0;
+ }
+ mutex_exit(&data->lav_lock);
+
+ return (laar);
+}
+
+static lx_autofs_automnt_req_t *
+lx_autofs_la_find(lx_autofs_vfs_t *data, int id)
+{
+ lx_autofs_automnt_req_t *laar;
+
+ /* Check for an outstanding request for this id. */
+ mutex_enter(&data->lav_lock);
+ if (mod_hash_find(data->lav_id_hash, (mod_hash_key_t)(uintptr_t)id,
+ (mod_hash_val_t *)&laar) != 0) {
+ mutex_exit(&data->lav_lock);
+ return (NULL);
+ }
+ atomic_add_int(&laar->laar_ref, 1);
+ mutex_exit(&data->lav_lock);
+ return (laar);
+}
+
+static void
+lx_autofs_la_complete(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laar)
+{
+ lx_autofs_automnt_req_t *laar_tmp;
+
+ /* Remove this request from the hashes so no one can look it up. */
+ mutex_enter(&data->lav_lock);
+ (void) mod_hash_remove(data->lav_id_hash,
+ (mod_hash_key_t)(uintptr_t)laar->laar_pkt.lap_id,
+ (mod_hash_val_t)&laar_tmp);
+ if (data->lav_min_proto == 5) {
+ (void) mod_hash_remove(data->lav_path_hash,
+ (mod_hash_key_t)laar->laar_pkt.lap_v5.lap_name,
+ (mod_hash_val_t)&laar_tmp);
+ } else {
+ (void) mod_hash_remove(data->lav_path_hash,
+ (mod_hash_key_t)laar->laar_pkt.lap_v2.lap_name,
+ (mod_hash_val_t)&laar_tmp);
+ }
+ mutex_exit(&data->lav_lock);
+
+ /* Mark this requst as complete and wakeup anyone waiting on it. */
+ mutex_enter(&laar->laar_lock);
+ laar->laar_complete = 1;
+ cv_broadcast(&laar->laar_cv);
+ mutex_exit(&laar->laar_lock);
+}
+
+static void
+lx_autofs_la_release(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laar)
+{
+ ASSERT(!MUTEX_HELD(&laar->laar_lock));
+ if (atomic_add_int_nv(&laar->laar_ref, -1) > 0)
+ return;
+ ASSERT(laar->laar_ref == 0);
+ id_free(data->lav_ids, laar->laar_pkt.lap_id);
+ kmem_free(laar, sizeof (*laar));
+}
+
+static void
+lx_autofs_la_abort(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laar)
+{
+ lx_autofs_automnt_req_t *laar_tmp;
+
+ /*
+ * This is a little tricky. We're aborting the wait for this
+ * request. So if anyone else is waiting for this request we
+ * can't free it, but if no one else is waiting for the request
+ * we should free it.
+ */
+ mutex_enter(&data->lav_lock);
+ if (atomic_add_int_nv(&laar->laar_ref, -1) > 0) {
+ mutex_exit(&data->lav_lock);
+ return;
+ }
+ ASSERT(laar->laar_ref == 0);
+
+ /* Remove this request from the hashes so no one can look it up. */
+ (void) mod_hash_remove(data->lav_id_hash,
+ (mod_hash_key_t)(uintptr_t)laar->laar_pkt.lap_id,
+ (mod_hash_val_t)&laar_tmp);
+ if (data->lav_min_proto == 5) {
+ (void) mod_hash_remove(data->lav_path_hash,
+ (mod_hash_key_t)laar->laar_pkt.lap_v5.lap_name,
+ (mod_hash_val_t)&laar_tmp);
+ } else {
+ (void) mod_hash_remove(data->lav_path_hash,
+ (mod_hash_key_t)laar->laar_pkt.lap_v2.lap_name,
+ (mod_hash_val_t)&laar_tmp);
+ }
+ mutex_exit(&data->lav_lock);
+
+ /* It's ok to free this now because the ref count was zero. */
+ id_free(data->lav_ids, laar->laar_pkt.lap_id);
+ kmem_free(laar, sizeof (*laar));
+}
+
+static int
+lx_autofs_fifo_lookup(pid_t pgrp, int fd, file_t **fpp_wr, file_t **fpp_rd)
+{
+ proc_t *prp;
+ uf_info_t *fip;
+ uf_entry_t *ufp_wr, *ufp_rd = NULL;
+ file_t *fp_wr, *fp_rd = NULL;
+ vnode_t *vp_wr, *vp_rd;
+ int i;
+
+ /*
+ * sprlock() is zone aware, so assuming this mount call was
+ * initiated by a process in a zone, if it tries to specify
+ * a pgrp outside of it's zone this call will fail.
+ *
+ * Also, we want to grab hold of the main automounter process
+ * and its going to be the group leader for pgrp, so its
+ * pid will be equal to pgrp.
+ */
+ prp = sprlock(pgrp);
+ if (prp == NULL)
+ return (-1);
+ mutex_exit(&prp->p_lock);
+
+ /* Now we want to access the processes open file descriptors. */
+ fip = P_FINFO(prp);
+ mutex_enter(&fip->fi_lock);
+
+ /* Sanity check fifo write fd. */
+ if (fd >= fip->fi_nfiles) {
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+ return (-1);
+ }
+
+ /* Get a pointer to the write fifo. */
+ UF_ENTER(ufp_wr, fip, fd);
+ if (((fp_wr = ufp_wr->uf_file) == NULL) ||
+ ((vp_wr = fp_wr->f_vnode) == NULL) || (vp_wr->v_type != VFIFO)) {
+ /* Invalid fifo fd. */
+ UF_EXIT(ufp_wr);
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+ return (-1);
+ }
+
+ /*
+ * Now we need to find the read end of the fifo (for reasons
+ * explained below.) We assume that the read end of the fifo
+ * is in the same process as the write end.
+ */
+ vp_rd = lx_autofs_fifo_peer_vp(fp_wr->f_vnode);
+ for (i = 0; i < fip->fi_nfiles; i++) {
+ if (i == fd)
+ continue;
+ UF_ENTER(ufp_rd, fip, i);
+ if (((fp_rd = ufp_rd->uf_file) != NULL) &&
+ (fp_rd->f_vnode == vp_rd))
+ break;
+ UF_EXIT(ufp_rd);
+ }
+ if (i == fip->fi_nfiles) {
+ /* Didn't find it. */
+ UF_EXIT(ufp_wr);
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+ return (-1);
+ }
+
+ /*
+ * We need to drop fi_lock before we can try to acquire f_tlock
+ * the good news is that the file pointers are protected because
+ * we're still holding uf_lock.
+ */
+ mutex_exit(&fip->fi_lock);
+
+ /*
+ * Here we bump the open counts on the fifos. The reason
+ * that we do this is because when we go to write to the
+ * fifo we want to ensure that they are actually open (and
+ * not in the process of being closed) without having to
+ * stop the automounter. (If the write end of the fifo
+ * were closed and we tried to write to it we would panic.
+ * If the read end of the fifo was closed and we tried to
+ * write to the other end, the process that invoked the
+ * lookup operation would get an unexpected SIGPIPE.)
+ */
+ mutex_enter(&fp_wr->f_tlock);
+ fp_wr->f_count++;
+ ASSERT(fp_wr->f_count >= 2);
+ mutex_exit(&fp_wr->f_tlock);
+
+ mutex_enter(&fp_rd->f_tlock);
+ fp_rd->f_count++;
+ ASSERT(fp_rd->f_count >= 2);
+ mutex_exit(&fp_rd->f_tlock);
+
+ /* Release all our locks. */
+ UF_EXIT(ufp_wr);
+ UF_EXIT(ufp_rd);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+
+ /* Return the file pointers. */
+ *fpp_rd = fp_rd;
+ *fpp_wr = fp_wr;
+ return (0);
+}
+
+static uint_t
+/*ARGSUSED*/
+lx_autofs_fifo_close_cb(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
+{
+ int *id = (int *)arg;
+ /* Return the key and terminate the walk. */
+ *id = (uintptr_t)key;
+ return (MH_WALK_TERMINATE);
+}
+
+static void
+lx_autofs_fifo_close(lx_autofs_vfs_t *data)
+{
+ /*
+ * Close the fifo to prevent any future requests from
+ * getting sent to the automounter.
+ */
+ mutex_enter(&data->lav_lock);
+ if (data->lav_fifo_wr != NULL) {
+ (void) closef(data->lav_fifo_wr);
+ data->lav_fifo_wr = NULL;
+ }
+ if (data->lav_fifo_rd != NULL) {
+ (void) closef(data->lav_fifo_rd);
+ data->lav_fifo_rd = NULL;
+ }
+ mutex_exit(&data->lav_lock);
+
+ /*
+ * Wakeup any threads currently waiting for the automounter
+ * note that it's possible for multiple threads to have entered
+ * this function and to be doing the work below simultaneously.
+ */
+ for (;;) {
+ lx_autofs_automnt_req_t *laar;
+ int id;
+
+ /* Lookup the first entry in the hash. */
+ id = -1;
+ mod_hash_walk(data->lav_id_hash,
+ lx_autofs_fifo_close_cb, &id);
+ if (id == -1) {
+ /* No more id's in the hash. */
+ break;
+ }
+ if ((laar = lx_autofs_la_find(data, id)) == NULL) {
+ /* Someone else beat us to it. */
+ continue;
+ }
+
+ /* Mark the request as complete and release it. */
+ lx_autofs_la_complete(data, laar);
+ lx_autofs_la_release(data, laar);
+ }
+}
+
+static int
+lx_autofs_fifo_verify_rd(lx_autofs_vfs_t *data)
+{
+ proc_t *prp;
+ uf_info_t *fip;
+ uf_entry_t *ufp_rd = NULL;
+ file_t *fp_rd = NULL;
+ vnode_t *vp_rd;
+ int i;
+
+ ASSERT(MUTEX_HELD((&data->lav_lock)));
+
+ /* Check if we've already been shut down. */
+ if (data->lav_fifo_wr == NULL) {
+ ASSERT(data->lav_fifo_rd == NULL);
+ return (-1);
+ }
+ vp_rd = lx_autofs_fifo_peer_vp(data->lav_fifo_wr->f_vnode);
+
+ /*
+ * sprlock() is zone aware, so assuming this mount call was
+ * initiated by a process in a zone, if it tries to specify
+ * a pgrp outside of it's zone this call will fail.
+ *
+ * Also, we want to grab hold of the main automounter process
+ * and its going to be the group leader for pgrp, so its
+ * pid will be equal to pgrp.
+ */
+ prp = sprlock(data->lav_pgrp);
+ if (prp == NULL)
+ return (-1);
+ mutex_exit(&prp->p_lock);
+
+ /* Now we want to access the processes open file descriptors. */
+ fip = P_FINFO(prp);
+ mutex_enter(&fip->fi_lock);
+
+ /*
+ * Now we need to find the read end of the fifo (for reasons
+ * explained below.) We assume that the read end of the fifo
+ * is in the same process as the write end.
+ */
+ for (i = 0; i < fip->fi_nfiles; i++) {
+ UF_ENTER(ufp_rd, fip, i);
+ if (((fp_rd = ufp_rd->uf_file) != NULL) &&
+ (fp_rd->f_vnode == vp_rd))
+ break;
+ UF_EXIT(ufp_rd);
+ }
+ if (i == fip->fi_nfiles) {
+ /* Didn't find it. */
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+ return (-1);
+ }
+
+ /*
+ * Seems the automounter still has the read end of the fifo
+ * open, we're done here. Release all our locks and exit.
+ */
+ mutex_exit(&fip->fi_lock);
+ UF_EXIT(ufp_rd);
+ mutex_enter(&prp->p_lock);
+ sprunlock(prp);
+
+ return (0);
+}
+
+static int
+lx_autofs_fifo_write(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laarp)
+{
+ struct uio uio;
+ struct iovec iov;
+ file_t *fp_wr, *fp_rd;
+ int error;
+
+ /*
+ * The catch here is we need to make sure _we_ don't close
+ * the the fifo while writing to it. (Another thread could come
+ * along and realize the automounter process is gone and close
+ * the fifo. To do this we bump the open count before we
+ * write to the fifo.
+ */
+ mutex_enter(&data->lav_lock);
+ if (data->lav_fifo_wr == NULL) {
+ ASSERT(data->lav_fifo_rd == NULL);
+ mutex_exit(&data->lav_lock);
+ return (ENOENT);
+ }
+ fp_wr = data->lav_fifo_wr;
+ fp_rd = data->lav_fifo_rd;
+
+ /* Bump the open count on the write fifo. */
+ mutex_enter(&fp_wr->f_tlock);
+ fp_wr->f_count++;
+ mutex_exit(&fp_wr->f_tlock);
+
+ /* Bump the open count on the read fifo. */
+ mutex_enter(&fp_rd->f_tlock);
+ fp_rd->f_count++;
+ mutex_exit(&fp_rd->f_tlock);
+
+ mutex_exit(&data->lav_lock);
+
+ iov.iov_base = (caddr_t)&laarp->laar_pkt;
+ iov.iov_len = laarp->laar_pkt_size;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_loffset = 0;
+ uio.uio_segflg = (short)UIO_SYSSPACE;
+ uio.uio_resid = laarp->laar_pkt_size;
+ uio.uio_llimit = 0;
+ uio.uio_fmode = FWRITE | FNDELAY | FNONBLOCK;
+
+ error = VOP_WRITE(fp_wr->f_vnode, &uio, 0, kcred, NULL);
+ (void) closef(fp_wr);
+ (void) closef(fp_rd);
+
+ /*
+ * After every write we verify that the automounter still has
+ * these files open.
+ */
+ mutex_enter(&data->lav_lock);
+ if (lx_autofs_fifo_verify_rd(data) != 0) {
+ /*
+ * Something happened to the automounter.
+ * Close down the communication pipe we setup.
+ */
+ mutex_exit(&data->lav_lock);
+ lx_autofs_fifo_close(data);
+ if (error != 0)
+ return (error);
+ return (ENOENT);
+ }
+ mutex_exit(&data->lav_lock);
+
+ return (error);
+}
+
+static int
+lx_autofs_bs_readdir(vnode_t *dvp, list_t *dir_stack, list_t *file_stack)
+{
+ struct iovec iov;
+ struct uio uio;
+ dirent64_t *dp, *dbuf;
+ vnode_t *vp;
+ size_t dlen, dbuflen;
+ int eof, error, ndirents = 64;
+ char *nm;
+
+ dlen = ndirents * (sizeof (*dbuf));
+ dbuf = kmem_alloc(dlen, KM_SLEEP);
+
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_fmode = 0;
+ uio.uio_extflg = UIO_COPY_CACHED;
+ uio.uio_loffset = 0;
+ uio.uio_llimit = MAXOFFSET_T;
+
+ eof = 0;
+ error = 0;
+ while (!error && !eof) {
+ uio.uio_resid = dlen;
+ iov.iov_base = (char *)dbuf;
+ iov.iov_len = dlen;
+
+ (void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+ if (VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0) != 0) {
+ VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+ kmem_free(dbuf, dlen);
+ return (-1);
+ }
+ VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+
+ if ((dbuflen = dlen - uio.uio_resid) == 0) {
+ /* We're done. */
+ break;
+ }
+
+ for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen);
+ dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
+
+ nm = dp->d_name;
+
+ if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0)
+ continue;
+
+ if (VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, kcred,
+ NULL, NULL, NULL) != 0) {
+ kmem_free(dbuf, dlen);
+ return (-1);
+ }
+ if (vp->v_type == VDIR) {
+ if (dir_stack != NULL) {
+ lx_autofs_stack_push(dir_stack,
+ (caddr_t)dvp,
+ (caddr_t)vp, lx_autofs_strdup(nm));
+ } else {
+ VN_RELE(vp);
+ }
+ } else {
+ if (file_stack != NULL) {
+ lx_autofs_stack_push(file_stack,
+ (caddr_t)dvp,
+ (caddr_t)vp, lx_autofs_strdup(nm));
+ } else {
+ VN_RELE(vp);
+ }
+ }
+ }
+ }
+ kmem_free(dbuf, dlen);
+ return (0);
+}
+
+static void
+lx_autofs_bs_destroy(vnode_t *dvp, char *path)
+{
+ list_t search_stack;
+ list_t dir_stack;
+ list_t file_stack;
+ vnode_t *pdvp, *vp;
+ char *dpath, *fpath;
+ int ret;
+
+ if (VOP_LOOKUP(dvp, path, &vp, NULL, 0, NULL, kcred,
+ NULL, NULL, NULL) != 0) {
+ /* A directory entry with this name doesn't actually exist. */
+ return;
+ }
+
+ if ((vp->v_type & VDIR) == 0) {
+ /* Easy, the directory entry is a file so delete it. */
+ VN_RELE(vp);
+ (void) VOP_REMOVE(dvp, path, kcred, NULL, 0);
+ return;
+ }
+
+ /*
+ * The directory entry is a subdirectory, now we have a bit more
+ * work to do. (We'll have to recurse into the sub directory.)
+ * It would have been much easier to do this recursively but kernel
+ * stacks are notoriously small.
+ */
+ ls_autofs_stack_init(&search_stack);
+ ls_autofs_stack_init(&dir_stack);
+ ls_autofs_stack_init(&file_stack);
+
+ /* Save our newfound subdirectory into a list. */
+ lx_autofs_stack_push(&search_stack, (caddr_t)dvp, (caddr_t)vp,
+ lx_autofs_strdup(path));
+
+ /* Do a recursive depth first search into the subdirectories. */
+ while (lx_autofs_stack_pop(&search_stack,
+ (caddr_t *)&pdvp, (caddr_t *)&dvp, &dpath) == 0) {
+
+ /* Get a list of the subdirectories in this directory. */
+ if (lx_autofs_bs_readdir(dvp, &search_stack, NULL) != 0)
+ goto exit;
+
+ /* Save the current directory a separate stack. */
+ lx_autofs_stack_push(&dir_stack, (caddr_t)pdvp, (caddr_t)dvp,
+ dpath);
+ }
+
+ /*
+ * Now dir_stack contains a list of directories, the deepest paths
+ * are at the top of the list. So let's go through and process them.
+ */
+ while (lx_autofs_stack_pop(&dir_stack,
+ (caddr_t *)&pdvp, (caddr_t *)&dvp, &dpath) == 0) {
+
+ /* Get a list of the files in this directory. */
+ if (lx_autofs_bs_readdir(dvp, NULL, &file_stack) != 0) {
+ VN_RELE(dvp);
+ lx_autofs_strfree(dpath);
+ goto exit;
+ }
+
+ /* Delete all the files in this directory. */
+ while (lx_autofs_stack_pop(&file_stack,
+ NULL, (caddr_t *)&vp, &fpath) == 0) {
+ VN_RELE(vp)
+ ret = VOP_REMOVE(dvp, fpath, kcred, NULL, 0);
+ lx_autofs_strfree(fpath);
+ if (ret != 0) {
+ lx_autofs_strfree(dpath);
+ goto exit;
+ }
+ }
+
+ /* Delete this directory. */
+ VN_RELE(dvp);
+ ret = VOP_RMDIR(pdvp, dpath, pdvp, kcred, NULL, 0);
+ lx_autofs_strfree(dpath);
+ if (ret != 0)
+ goto exit;
+ }
+
+exit:
+ while (
+ (lx_autofs_stack_pop(&search_stack, NULL, (caddr_t *)&vp,
+ &path) == 0) ||
+ (lx_autofs_stack_pop(&dir_stack, NULL, (caddr_t *)&vp,
+ &path) == 0) ||
+ (lx_autofs_stack_pop(&file_stack, NULL, (caddr_t *)&vp,
+ &path) == 0)) {
+ VN_RELE(vp);
+ lx_autofs_strfree(path);
+ }
+ lx_autofs_stack_fini(&search_stack);
+ lx_autofs_stack_fini(&dir_stack);
+ lx_autofs_stack_fini(&file_stack);
+}
+
+static vnode_t *
+lx_autofs_bs_create(vnode_t *dvp, char *bs_name)
+{
+ vnode_t *vp;
+ vattr_t vattr;
+
+ /*
+ * After looking at the mkdir syscall path it seems we don't need
+ * to initialize all of the vattr_t structure.
+ */
+ bzero(&vattr, sizeof (vattr));
+ vattr.va_type = VDIR;
+ vattr.va_mode = 0755; /* u+rwx,og=rx */
+ vattr.va_mask = AT_TYPE|AT_MODE;
+
+ if (VOP_MKDIR(dvp, bs_name, &vattr, &vp, kcred, NULL, 0, NULL) != 0)
+ return (NULL);
+ return (vp);
+}
+
+static int
+lx_autofs_automounter_call(vnode_t *dvp, char *nm)
+{
+ lx_autofs_automnt_req_t *laar;
+ lx_autofs_vfs_t *data;
+ int error;
+ boolean_t is_dup;
+
+ /* Get a pointer to the vfs mount data. */
+ data = (lx_autofs_vfs_t *)dvp->v_vfsp->vfs_data;
+
+ /* The automounter only supports queries in the root directory. */
+ if (dvp != data->lav_root)
+ return (ENOENT);
+
+ /*
+ * Check if the current process is in the automounters process
+ * group. (If it is, the current process is either the autmounter
+ * itself or one of it's forked child processes.) If so, don't
+ * redirect this call back into the automounter because we'll
+ * hang.
+ */
+ mutex_enter(&pidlock);
+ if (data->lav_pgrp == curproc->p_pgrp) {
+ mutex_exit(&pidlock);
+ return (ENOENT);
+ }
+ mutex_exit(&pidlock);
+
+ /* Verify that the automount process pipe still exists. */
+ mutex_enter(&data->lav_lock);
+ if (data->lav_fifo_wr == NULL) {
+ ASSERT(data->lav_fifo_rd == NULL);
+ mutex_exit(&data->lav_lock);
+ return (ENOENT);
+ }
+ mutex_exit(&data->lav_lock);
+
+ /* Allocate an automounter request structure. */
+ if ((laar = lx_autofs_la_alloc(data, &is_dup, B_FALSE,
+ nm)) == NULL)
+ return (ENOENT);
+
+ /*
+ * If we were the first one to allocate this request then we
+ * need to send it to the automounter.
+ */
+ if ((!is_dup) &&
+ ((error = lx_autofs_fifo_write(data, laar)) != 0)) {
+ /*
+ * Unable to send the request to the automounter.
+ * Unblock any other threads waiting on the request
+ * and release the request.
+ */
+ lx_autofs_la_complete(data, laar);
+ lx_autofs_la_release(data, laar);
+ return (error);
+ }
+
+ /* Wait for someone to signal us that this request has completed. */
+ mutex_enter(&laar->laar_lock);
+ while (!laar->laar_complete) {
+ if (cv_wait_sig(&laar->laar_cv, &laar->laar_lock) == 0) {
+ /* We got a signal, abort this call. */
+ mutex_exit(&laar->laar_lock);
+ lx_autofs_la_abort(data, laar);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&laar->laar_lock);
+
+ if (laar->laar_result == LXACR_READY) {
+ /*
+ * Mount succeeded, keep track for future expire calls.
+ *
+ * See vfs lav_vn_hash. Is this something we could use for
+ * iterating mounts under this autofs? Used by
+ * lx_autofs_vn_alloc
+ */
+ lx_autofs_mntent_t *mp;
+
+ mp = kmem_zalloc(sizeof (lx_autofs_mntent_t), KM_SLEEP);
+ mp->lxafme_len = strlen(nm) + 1;
+ mp->lxafme_path = kmem_zalloc(mp->lxafme_len, KM_SLEEP);
+ mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+ (void) strlcpy(mp->lxafme_path, nm, mp->lxafme_len);
+
+ mutex_enter(&data->lav_lock);
+ list_insert_tail(&data->lav_mnt_list, mp);
+ mutex_exit(&data->lav_lock);
+ }
+
+ lx_autofs_la_release(data, laar);
+
+ return (0);
+}
+
+/*
+ * Same preliminary checks as in lx_autofs_unmount.
+ */
+static boolean_t
+lx_autofs_may_unmount(vfs_t *vfsp, struct cred *cr)
+{
+ lx_autofs_vfs_t *data;
+
+ if (secpolicy_fs_unmount(cr, vfsp) != 0)
+ return (B_FALSE);
+
+ /*
+ * We should never have a reference count of less than 2: one for the
+ * caller, one for the root vnode.
+ */
+ ASSERT(vfsp->vfs_count >= 2);
+
+ /* If there are any outstanding vnodes, we can't unmount. */
+ if (vfsp->vfs_count > 2)
+ return (B_FALSE);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ ASSERT(data->lav_root->v_vfsp == vfsp);
+
+ /* Check for any remaining holds on the root vnode. */
+ if (data->lav_root->v_count > 1)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
+
+static vfs_t *
+lx_autofs_get_mountvfs(char *fs_mntpt, int *cnt)
+{
+ struct vfs *vfsp;
+ struct vfs *vfslist;
+ vfs_t *fnd_vfs = NULL;
+ int fsmplen;
+ int acnt = 0;
+
+ fsmplen = strlen(fs_mntpt);
+
+ vfs_list_read_lock();
+
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ *cnt = 0;
+ return (NULL);
+ }
+
+ do {
+ /* Skip mounts we shouldn't show. */
+ if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+ char *mntpt;
+
+ mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+ if (strncmp(fs_mntpt, mntpt, fsmplen) == 0 &&
+ (mntpt[fsmplen] == '\0' || mntpt[fsmplen] == '/')) {
+ /*
+ * We'll return the first one we find but don't
+ * return a mount that is actually autofs (i.e.
+ * autofs direct or offset mount).
+ */
+ if (vfsp->vfs_op == lx_autofs_vfsops) {
+ acnt++;
+ } else if (fnd_vfs == NULL) {
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+ }
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+ *cnt = acnt;
+ return (fnd_vfs);
+}
+
+/*
+ * Unmount all autofs offset mounts below the given path.
+ */
+static boolean_t
+lx_autofs_umount_offset(char *fs_mntpt, struct cred *cr)
+{
+ struct vfs *vfsp;
+ struct vfs *vfslist;
+ boolean_t busy = B_FALSE;
+ int fsmplen = strlen(fs_mntpt);
+
+restart:
+ vfs_list_read_lock();
+
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ return (B_FALSE);
+ }
+
+ do {
+ char *mntpt;
+ lx_autofs_vfs_t *data;
+
+ /* Skip mounts we should ignore. */
+ if ((vfsp->vfs_flag & VFS_NOMNTTAB)) {
+ vfsp = vfsp->vfs_zone_next;
+ continue;
+ }
+
+ mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+ if (strncmp(fs_mntpt, mntpt, fsmplen) != 0 ||
+ (mntpt[fsmplen] != '\0' && mntpt[fsmplen] != '/')) {
+ vfsp = vfsp->vfs_zone_next;
+ continue;
+ }
+
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ /*
+ * Something got mounted over the autofs mountpoint
+ * after we checked that this inidrect hierarchy was
+ * not busy.
+ */
+ busy = B_TRUE;
+ break;
+ }
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ if (data->lav_mnttype != LXAMT_OFFSET) {
+ /*
+ * Something mounted a non-offset autofs fs under this
+ * indirect mnt!
+ */
+ busy = B_TRUE;
+ break;
+ }
+
+ /*
+ * Attempt to umount - set busy if fails.
+ *
+ * umount2_engine will call VFS_RELE, so we need to take an
+ * extra hold to match the behavior during the normal umount
+ * path.
+ *
+ * We also need to drop the list lock to prevent deadlock
+ * during umount.
+ */
+ VFS_HOLD(vfsp);
+ vfs_list_unlock();
+ if (umount2_engine(vfsp, 0, cr, 0) != 0) {
+ busy = B_TRUE;
+ goto errexit;
+ }
+
+ /* Retake list lock and look for more. */
+ goto restart;
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+errexit:
+ return (busy);
+}
+
+
+/*
+ * Note that lx_autofs_automounter_call() only supports queries in the root
+ * directory, so all mntent names are relative to that.
+ */
+static int
+lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
+{
+ lx_autofs_vfs_t *data;
+ lx_autofs_mntent_t *mp;
+ lx_autofs_automnt_req_t *laar;
+ boolean_t is_dup;
+ vfs_t *fnd_vfs;
+ int autofs_cnt;
+ boolean_t busy = B_FALSE;
+ char exp_path[MAXPATHLEN];
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+ /*
+ * We process only the first element (i.e. do not do multi). This
+ * works fine for the automounter.
+ */
+ mutex_enter(&data->lav_lock);
+ mp = (lx_autofs_mntent_t *)list_remove_head(&data->lav_mnt_list);
+ mutex_exit(&data->lav_lock);
+ if (mp == NULL) {
+ if (data->lav_mnttype == LXAMT_OFFSET) {
+ /*
+ * During restart the automounter will openmount each
+ * offset mount for management. It won't closemount the
+ * offset mount until we expire it, even though nothing
+ * is mounted over that offset. We handle this as a
+ * special expiration case.
+ */
+ int cnt;
+
+ mutex_enter(&data->lav_lock);
+ cnt = data->lav_openmnt_cnt;
+ mutex_exit(&data->lav_lock);
+
+ if (cnt == 1 && vn_ismntpt(data->lav_root) == 0) {
+ char *mntpt = (char *)
+ refstr_value(vfsp->vfs_mntpt);
+ char *nm = ZONE_PATH_TRANSLATE(mntpt, curzone);
+
+ mp = kmem_zalloc(sizeof (lx_autofs_mntent_t),
+ KM_SLEEP);
+ mp->lxafme_len = strlen(nm) + 1;
+ mp->lxafme_path = kmem_zalloc(mp->lxafme_len,
+ KM_SLEEP);
+ mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+ (void) strlcpy(mp->lxafme_path, nm,
+ mp->lxafme_len);
+
+ goto exp_offset;
+ }
+ }
+
+ return (EAGAIN);
+ }
+
+ /*
+ * We only return an expired mount if it is inactive for the full
+ * timeout. This reduces overly aggressive umount/mount activity.
+ */
+ if (data->lav_timeout > 0) {
+ uint64_t now = TICK_TO_SEC(ddi_get_lbolt64());
+
+ if ((now - mp->lxafme_ts) < data->lav_timeout) {
+ /* put it back at the end of the line */
+ mutex_enter(&data->lav_lock);
+ list_insert_tail(&data->lav_mnt_list, mp);
+ mutex_exit(&data->lav_lock);
+ return (EAGAIN);
+ }
+ }
+
+ if (data->lav_mnttype == LXAMT_INDIR) {
+ (void) snprintf(exp_path, sizeof (exp_path), "%s/%s",
+ (char *)refstr_value(vfsp->vfs_mntpt), mp->lxafme_path);
+ } else {
+ (void) strlcpy(exp_path, (char *)refstr_value(vfsp->vfs_mntpt),
+ sizeof (exp_path));
+ }
+
+ fnd_vfs = lx_autofs_get_mountvfs(exp_path, &autofs_cnt);
+ if (fnd_vfs != NULL) {
+ boolean_t skip = B_FALSE;
+ vfssw_t *vfssw;
+
+ /*
+ * If it's an NFS file system (typical) then we check in
+ * advance to see if it can be unmounted, otherwise, proceed.
+ * The fs-specific umount attempted by the automounter will
+ * either succeed or fail. Both are valid outcomes but checking
+ * now for nfs will save a bunch of work by the automounter
+ * if the fs is busy.
+ *
+ * Unfortunately, for NFS the vfs_fstype is the same for all
+ * versions of NFS, so we need to check the vfs_op member to
+ * determine which version of NFS we're dealing with.
+ */
+ if (!skip && (vfssw = vfs_getvfssw("nfs4")) != NULL) {
+ if (vfs_matchops(fnd_vfs, &vfssw->vsw_vfsops)) {
+ (void) dnlc_purge_vfsp(fnd_vfs, 0);
+ if (check_rtable4(fnd_vfs))
+ busy = B_TRUE;
+ skip = B_TRUE;
+ }
+ vfs_unrefvfssw(vfssw);
+ }
+
+ if (!skip && (vfssw = vfs_getvfssw("nfs3")) != NULL) {
+ if (vfs_matchops(fnd_vfs, &vfssw->vsw_vfsops)) {
+ (void) dnlc_purge_vfsp(fnd_vfs, 0);
+ if (check_rtable(fnd_vfs))
+ busy = B_TRUE;
+ }
+ vfs_unrefvfssw(vfssw);
+ }
+
+ VFS_RELE(fnd_vfs);
+
+ } else if (autofs_cnt > 0) {
+ /*
+ * The automounter is asking us to expire and we pulled this
+ * name from our vfs mountpoint list, but if
+ * lx_autofs_get_mountvfs returns null then that means we
+ * didn't find a non-autofs mount under this name. Thus, the
+ * name could be a subdirectory under an autofs toplevel
+ * indirect mount with one or more offset mounts below.
+ * autofs_cnt will indicate how many autofs mounts exist below
+ * this subdirectory name.
+ *
+ * The automounter will take care of unmounting any fs mounted
+ * over one of these offset mounts (i.e. offset is like a
+ * direct mount which the automounter will manage) but the
+ * automounter will not unmount the actual autofs offset mount
+ * itself, so we have to do that before we can expire the
+ * top-level subrectory name.
+ */
+ busy = lx_autofs_umount_offset(exp_path, cr);
+ }
+
+ if (busy) {
+ /*
+ * Can't unmount this one right now, put it at the end of the
+ * list and return. The caller will return EAGAIN for the
+ * expire ioctl and the automounter will check again later.
+ */
+ mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+ mutex_enter(&data->lav_lock);
+ list_insert_tail(&data->lav_mnt_list, mp);
+ mutex_exit(&data->lav_lock);
+ return (EAGAIN);
+ }
+
+ /*
+ * See lx_autofs_automounter_call. We want to send a msg up the pipe
+ * to the automounter in a similar way.
+ */
+
+exp_offset:
+ /* Verify that the automount process pipe still exists. */
+ mutex_enter(&data->lav_lock);
+ if (data->lav_fifo_wr == NULL) {
+ ASSERT(data->lav_fifo_rd == NULL);
+ mutex_exit(&data->lav_lock);
+ goto err_free;
+ }
+ mutex_exit(&data->lav_lock);
+
+ /* Allocate an automounter expire structure. */
+ if ((laar = lx_autofs_la_alloc(data, &is_dup, B_TRUE,
+ mp->lxafme_path)) == NULL)
+ goto err_free;
+
+ /*
+ * If we were the first one to allocate this request then we
+ * need to send it to the automounter.
+ */
+ if (!is_dup && lx_autofs_fifo_write(data, laar) != 0) {
+ /*
+ * Unable to send the request to the automounter.
+ * Unblock any other threads waiting on the request
+ * and release the request.
+ */
+ lx_autofs_la_complete(data, laar);
+ lx_autofs_la_release(data, laar);
+ goto err_free;
+ }
+
+ /* Wait for someone to signal us that this request has completed. */
+ mutex_enter(&laar->laar_lock);
+ while (!laar->laar_complete) {
+ if (cv_wait_sig(&laar->laar_cv, &laar->laar_lock) == 0) {
+ /* We got a signal, abort this request. */
+ mutex_exit(&laar->laar_lock);
+ lx_autofs_la_abort(data, laar);
+ goto err_free;
+ }
+ }
+ mutex_exit(&laar->laar_lock);
+
+ /*
+ * If it failed or if the file system is still mounted after we get the
+ * response from our expire msg, then that means the automounter tried
+ * to unmount it but failed because the file system is busy, so we put
+ * this entry back on our list to try to expire it again later.
+ */
+ fnd_vfs = NULL;
+ if (laar->laar_result == LXACR_FAIL ||
+ (fnd_vfs = lx_autofs_get_mountvfs(exp_path, &autofs_cnt)) != NULL ||
+ autofs_cnt > 0) {
+ if (fnd_vfs != NULL)
+ VFS_RELE(fnd_vfs);
+ mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+ mutex_enter(&data->lav_lock);
+ list_insert_tail(&data->lav_mnt_list, mp);
+ mutex_exit(&data->lav_lock);
+ } else {
+ kmem_free(mp->lxafme_path, mp->lxafme_len);
+ kmem_free(mp, sizeof (lx_autofs_mntent_t));
+ }
+
+ lx_autofs_la_release(data, laar);
+ return (0);
+
+err_free:
+ kmem_free(mp->lxafme_path, mp->lxafme_len);
+ kmem_free(mp, sizeof (lx_autofs_mntent_t));
+ return (EAGAIN);
+}
+
+static int
+lx_autofs_ack(int reqid, vfs_t *vfsp, enum lx_autofs_callres result)
+{
+ lx_autofs_vfs_t *data;
+ lx_autofs_automnt_req_t *laar;
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ if ((laar = lx_autofs_la_find(data, reqid)) == NULL)
+ return (ENXIO);
+
+ /* Mark the request as complete and release it. */
+ laar->laar_result = result;
+ lx_autofs_la_complete(data, laar);
+ lx_autofs_la_release(data, laar);
+ return (0);
+}
+
+static int
+lx_autofs_automounter_ioctl(vnode_t *vp, int cmd, intptr_t arg, cred_t *cr)
+{
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
+ int id = arg;
+ int v;
+ int err;
+
+ /*
+ * Be strict.
+ * We only accept ioctls from the automounter process group.
+ */
+ mutex_enter(&pidlock);
+ if (data->lav_pgrp != curproc->p_pgrp) {
+ mutex_exit(&pidlock);
+ return (ENOENT);
+ }
+ mutex_exit(&pidlock);
+
+ switch (cmd) {
+ case LX_AUTOFS_IOC_READY:
+ if ((err = lx_autofs_ack(id, vp->v_vfsp, LXACR_READY)) != 0)
+ return (err);
+ return (0);
+
+ case LX_AUTOFS_IOC_FAIL:
+ if ((err = lx_autofs_ack(id, vp->v_vfsp, LXACR_FAIL)) != 0)
+ return (err);
+ return (0);
+
+ case LX_AUTOFS_IOC_CATATONIC:
+ /* The automounter is shutting down. */
+ lx_autofs_fifo_close(data);
+ return (0);
+
+ case LX_AUTOFS_IOC_PROTOVER:
+ v = LX_AUTOFS_PROTO_VERS5;
+ if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+ return (EFAULT);
+ return (0);
+
+ case LX_AUTOFS_IOC_PROTOSUBVER:
+ v = LX_AUTOFS_PROTO_SUBVERSION;
+ if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+ return (EFAULT);
+ return (0);
+
+ case LX_AUTOFS_IOC_ASKUMOUNT:
+ /*
+ * This is asking if autofs can be unmounted, not asking to
+ * actually unmount it. We return 1 if it is busy or 0 if it
+ * can be unmounted.
+ */
+ v = 1;
+ if (lx_autofs_may_unmount(vp->v_vfsp, cr))
+ v = 0;
+
+ if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+ return (EFAULT);
+ return (0);
+
+ case LX_AUTOFS_IOC_SETTIMEOUT:
+ if (copyin((caddr_t)arg, &data->lav_timeout, sizeof (ulong_t))
+ != 0)
+ return (EFAULT);
+ return (0);
+
+ case LX_AUTOFS_IOC_EXPIRE:
+ return (ENOTSUP);
+
+ case LX_AUTOFS_IOC_EXPIRE_MULTI:
+ lx_autofs_expire(vp->v_vfsp, cr);
+ return (EAGAIN);
+
+ default:
+ ASSERT(0);
+ return (ENOTSUP);
+ }
+}
+
+static int
+lx_autofs_parse_mntopt(vfs_t *vfsp, lx_autofs_vfs_t *data)
+{
+ char *fd_str, *pgrp_str, *minproto_str, *maxproto_str;
+ int fd, pgrp, minproto, maxproto;
+ file_t *fp_wr, *fp_rd;
+
+ /* Require these options to be present. */
+ if ((vfs_optionisset(vfsp, LX_MNTOPT_FD, &fd_str) != 1) ||
+ (vfs_optionisset(vfsp, LX_MNTOPT_PGRP, &pgrp_str) != 1) ||
+ (vfs_optionisset(vfsp, LX_MNTOPT_MINPROTO, &minproto_str) != 1) ||
+ (vfs_optionisset(vfsp, LX_MNTOPT_MAXPROTO, &maxproto_str) != 1))
+ return (EINVAL);
+
+ /* Get the values for each parameter. */
+ if ((lx_autofs_str_to_int(fd_str, &fd) != 0) ||
+ (lx_autofs_str_to_int(pgrp_str, &pgrp) != 0) ||
+ (lx_autofs_str_to_int(minproto_str, &minproto) != 0) ||
+ (lx_autofs_str_to_int(maxproto_str, &maxproto) != 0))
+ return (EINVAL);
+
+ /*
+ * We primarily support v2 & v5 of the linux kernel automounter
+ * protocol. The userland daemon typically needs v5. We'll reject
+ * unsupported ioctls later if we get one.
+ */
+ if ((minproto > 5) || (maxproto < 2))
+ return (EINVAL);
+
+ /*
+ * Now we need to lookup the fifos we'll be using
+ * to talk to the userland automounter process.
+ */
+ if (lx_autofs_fifo_lookup(pgrp, fd, &fp_wr, &fp_rd) != 0) {
+ /*
+ * The automounter doesn't always have the same id as the pgrp.
+ * This happens when it is started via one of the various
+ * service managers. In this case the fifo lookup will fail
+ * so we retry with our own pid.
+ */
+ int pid = (int)curproc->p_pid;
+
+ if (lx_autofs_fifo_lookup(pid, fd, &fp_wr, &fp_rd) != 0)
+ return (EINVAL);
+ }
+
+ if (vfs_optionisset(vfsp, LX_MNTOPT_INDIRECT, NULL)) {
+ data->lav_mnttype = LXAMT_INDIR;
+ }
+ if (vfs_optionisset(vfsp, LX_MNTOPT_DIRECT, NULL)) {
+ if (data->lav_mnttype != LXAMT_NONE)
+ return (EINVAL);
+ data->lav_mnttype = LXAMT_DIRECT;
+ }
+ if (vfs_optionisset(vfsp, LX_MNTOPT_OFFSET, NULL)) {
+ if (data->lav_mnttype != LXAMT_NONE)
+ return (EINVAL);
+ data->lav_mnttype = LXAMT_OFFSET;
+ }
+ /* The automounter does test mounts with none of the options */
+ if (data->lav_mnttype == LXAMT_NONE)
+ data->lav_mnttype = LXAMT_DIRECT;
+
+ /* Save the mount options and fifo pointers. */
+ data->lav_fd = fd;
+ data->lav_min_proto = minproto;
+ data->lav_pgrp = pgrp;
+ data->lav_fifo_rd = fp_rd;
+ data->lav_fifo_wr = fp_wr;
+ return (0);
+}
+
+static uint64_t
+s2l_dev(dev_t dev)
+{
+ major_t maj = getmajor(dev);
+ minor_t min = getminor(dev);
+
+ return (LX_MAKEDEVICE(maj, min));
+}
+
+/*
+ * VFS entry points
+ */
+static int
+lx_autofs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+ lx_autofs_vfs_t *data;
+ dev_t dev;
+ char name[40];
+ int error;
+ vattr_t va;
+
+ if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+ return (EPERM);
+
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT)))
+ return (EBUSY);
+
+ /* We don't support mounts in the global zone. */
+ if (getzoneid() == GLOBAL_ZONEID)
+ return (EPERM);
+
+ /*
+ * Offset mounts will occur below the top-level mountpoint so we
+ * need to allow for autofs mounts even though mvp is an autofs.
+ */
+
+ /* Allocate a vfs struct. */
+ data = kmem_zalloc(sizeof (lx_autofs_vfs_t), KM_SLEEP);
+
+ /* Parse mount options. */
+ if ((error = lx_autofs_parse_mntopt(vfsp, data)) != 0) {
+ kmem_free(data, sizeof (lx_autofs_vfs_t));
+ return (error);
+ }
+
+ /* Initialize the backing store. */
+ lx_autofs_bs_destroy(mvp, LX_AUTOFS_BS_DIR);
+ data->lav_bs_vp = lx_autofs_bs_create(mvp, LX_AUTOFS_BS_DIR);
+ if (data->lav_bs_vp == NULL) {
+ kmem_free(data, sizeof (lx_autofs_vfs_t));
+ return (EBUSY);
+ }
+ data->lav_bs_name = LX_AUTOFS_BS_DIR;
+
+ /* Get the backing store inode for use in v5 protocol msgs */
+ va.va_mask = AT_STAT;
+ if ((error = VOP_GETATTR(data->lav_bs_vp, &va, 0, cr, NULL)) != 0) {
+ kmem_free(data, sizeof (lx_autofs_vfs_t));
+ return (error);
+ }
+ data->lav_ino = va.va_nodeid;
+
+ /* We have to hold the underlying vnode we're mounted on. */
+ data->lav_mvp = mvp;
+ VN_HOLD(mvp);
+
+ /* Initialize vfs fields */
+ vfsp->vfs_bsize = DEV_BSIZE;
+ vfsp->vfs_fstype = lx_autofs_fstype;
+ vfsp->vfs_data = data;
+
+ /* Invent a dev_t (sigh) */
+ do {
+ dev = makedevice(lx_autofs_major,
+ atomic_add_32_nv(&lx_autofs_minor, 1) & L_MAXMIN32);
+ } while (vfs_devismounted(dev));
+ vfsp->vfs_dev = dev;
+ vfs_make_fsid(&vfsp->vfs_fsid, dev, lx_autofs_fstype);
+
+ data->lav_dev = s2l_dev(vfsp->vfs_dev);
+
+ /* Create an id space arena for automounter requests. */
+ (void) snprintf(name, sizeof (name), "lx_autofs_id_%d",
+ getminor(vfsp->vfs_dev));
+ data->lav_ids = id_space_create(name, 1, INT_MAX);
+
+ /* Create hashes to keep track of automounter requests. */
+ mutex_init(&data->lav_lock, NULL, MUTEX_DEFAULT, NULL);
+ (void) snprintf(name, sizeof (name), "lx_autofs_path_hash_%d",
+ getminor(vfsp->vfs_dev));
+ data->lav_path_hash = mod_hash_create_strhash(name,
+ LX_AUTOFS_VFS_PATH_HASH_SIZE, mod_hash_null_valdtor);
+ (void) snprintf(name, sizeof (name), "lx_autofs_id_hash_%d",
+ getminor(vfsp->vfs_dev));
+ data->lav_id_hash = mod_hash_create_idhash(name,
+ LX_AUTOFS_VFS_ID_HASH_SIZE, mod_hash_null_valdtor);
+
+ /* Create a hash to keep track of vnodes. */
+ (void) snprintf(name, sizeof (name), "lx_autofs_vn_hash_%d",
+ getminor(vfsp->vfs_dev));
+ data->lav_vn_hash = mod_hash_create_ptrhash(name,
+ LX_AUTOFS_VFS_VN_HASH_SIZE, mod_hash_null_valdtor,
+ sizeof (vnode_t));
+
+ list_create(&data->lav_mnt_list, sizeof (lx_autofs_mntent_t),
+ offsetof(lx_autofs_mntent_t, lxafme_lst));
+
+ /* Create root vnode */
+ data->lav_root = lx_autofs_vn_alloc(vfsp, data->lav_bs_vp);
+
+ data->lav_root->v_flag |= VROOT | VNOCACHE | VNOMAP | VNOSWAP;
+
+ /*
+ * For a direct mountpoint we need to allow a filesystem to be
+ * mounted overtop of this autofs mount. Otherwise, disallow that.
+ */
+ if (data->lav_mnttype == LXAMT_INDIR)
+ data->lav_root->v_flag |= VNOMOUNT;
+
+ return (0);
+}
+
+static int
+lx_autofs_unmount(vfs_t *vfsp, int flag, struct cred *cr)
+{
+ lx_autofs_vfs_t *data;
+
+ if (secpolicy_fs_unmount(cr, vfsp) != 0)
+ return (EPERM);
+
+ /* We do not currently support forced unmounts. */
+ if (flag & MS_FORCE)
+ return (ENOTSUP);
+
+ /*
+ * We should never have a reference count of less than 2: one for the
+ * caller, one for the root vnode.
+ */
+ ASSERT(vfsp->vfs_count >= 2);
+
+ /* If there are any outstanding vnodes, we can't unmount. */
+ if (vfsp->vfs_count > 2)
+ return (EBUSY);
+
+ /* Check for any remaining holds on the root vnode. */
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ ASSERT(data->lav_root->v_vfsp == vfsp);
+ if (data->lav_root->v_count > 1)
+ return (EBUSY);
+
+ /* Close the fifo to the automount process. */
+ if (data->lav_fifo_wr != NULL)
+ (void) closef(data->lav_fifo_wr);
+ if (data->lav_fifo_rd != NULL)
+ (void) closef(data->lav_fifo_rd);
+
+ /*
+ * We have to release our hold on our root vnode before we can
+ * delete the backing store. (Since the root vnode is linked
+ * to the backing store.)
+ */
+ VN_RELE(data->lav_root);
+
+ /* Cleanup the backing store. */
+ lx_autofs_bs_destroy(data->lav_mvp, data->lav_bs_name);
+ VN_RELE(data->lav_mvp);
+
+ /*
+ * Delete all listed mounts.
+ */
+ for (;;) {
+ lx_autofs_mntent_t *mp;
+
+ mp = list_remove_head(&data->lav_mnt_list);
+ if (mp == NULL)
+ break;
+ kmem_free(mp->lxafme_path, mp->lxafme_len);
+ kmem_free(mp, sizeof (lx_autofs_mntent_t));
+ }
+
+ /* Cleanup out remaining data structures. */
+ mod_hash_destroy_strhash(data->lav_path_hash);
+ mod_hash_destroy_idhash(data->lav_id_hash);
+ mod_hash_destroy_ptrhash(data->lav_vn_hash);
+ id_space_destroy(data->lav_ids);
+ list_destroy(&data->lav_mnt_list);
+ kmem_free(data, sizeof (lx_autofs_vfs_t));
+
+ return (0);
+}
+
+static int
+lx_autofs_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+ *vpp = data->lav_root;
+ VN_HOLD(*vpp);
+
+ return (0);
+}
+
+static int
+lx_autofs_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ vnode_t *urvp = data->lav_root->v_data;
+ dev32_t d32;
+ int error;
+
+ if ((error = VFS_STATVFS(urvp->v_vfsp, sp)) != 0)
+ return (error);
+
+ /* Update some of values before returning. */
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sp->f_fsid = d32;
+ (void) strlcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name,
+ sizeof (sp->f_basetype));
+ sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ bzero(sp->f_fstr, sizeof (sp->f_fstr));
+ return (0);
+}
+
+static const fs_operation_def_t lx_autofs_vfstops[] = {
+ { VFSNAME_MOUNT, { .vfs_mount = lx_autofs_mount } },
+ { VFSNAME_UNMOUNT, { .vfs_unmount = lx_autofs_unmount } },
+ { VFSNAME_ROOT, { .vfs_root = lx_autofs_root } },
+ { VFSNAME_STATVFS, { .vfs_statvfs = lx_autofs_statvfs } },
+ { NULL, NULL }
+};
+
+/*
+ * VOP entry points - simple passthrough
+ *
+ * For most VOP entry points we can simply pass the request on to
+ * the underlying filesystem we're mounted on.
+ */
+static int
+lx_autofs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+ return (VOP_CLOSE(uvp, flag, count, offset, cr, ctp));
+}
+
+static int
+lx_autofs_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ctp, int flags)
+{
+ vnode_t *uvp = vp->v_data;
+ return (VOP_READDIR(uvp, uiop, cr, eofp, ctp, flags));
+}
+
+static int
+lx_autofs_access(vnode_t *vp, int mode, int flags, cred_t *cr,
+ caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+ return (VOP_ACCESS(uvp, mode, flags, cr, ctp));
+}
+
+static int
+lx_autofs_rwlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+ return (VOP_RWLOCK(uvp, write_lock, ctp));
+}
+
+static void
+lx_autofs_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+ VOP_RWUNLOCK(uvp, write_lock, ctp);
+}
+
+/*
+ * Check if attempting to access a 'direct' mount and if so, call the
+ * automounter to perform the mount. Once the mount occurs, the new filesystem
+ * will be mounted overtop of this autofs mountpoint and we will no longer
+ * come through this path.
+ */
+static vnode_t *
+lx_autofs_do_direct(vnode_t *vp)
+{
+ vfs_t *vfsp = vp->v_vfsp;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ vnode_t *nvp;
+ boolean_t skip_am_call = B_FALSE;
+
+ if (data->lav_mnttype == LXAMT_INDIR)
+ return (NULL);
+
+ /*
+ * Check if the current process is in the automounter's process group.
+ * If it is, the current process is either the automounter itself or
+ * one of it's children. If so, don't call back into the automounter.
+ */
+ mutex_enter(&pidlock);
+ if (data->lav_pgrp == curproc->p_pgrp) {
+ skip_am_call = B_TRUE;
+ }
+ mutex_exit(&pidlock);
+
+ /*
+ * It is possible there is already a new fs mounted on top of our vnode.
+ * This can happen if the caller first did a lookup of a file name
+ * using our vnode as the directory vp. The lookup would trigger the
+ * autofs mount on top of ourself, but if the caller then uses our
+ * vnode to do a getattr on the directory, it will use the autofs
+ * vnode and not the newly mounted vnode. We need to skip re-calling
+ * the automounter for this case.
+ */
+ if (!skip_am_call && vn_mountedvfs(vp) == NULL) {
+ char tbuf[MAXPATHLEN];
+ char *nm;
+
+ (void) strlcpy(tbuf, (char *)refstr_value(vfsp->vfs_mntpt),
+ sizeof (tbuf));
+ nm = tbuf + strlen(tbuf);
+ while (*nm != '/' && nm != tbuf)
+ nm--;
+ if (*nm == '/')
+ nm++;
+ (void) lx_autofs_automounter_call(vp, nm);
+ }
+
+ /*
+ * We need to take an extra hold on our vp (which is the autofs
+ * root vp) to account for the rele done in traverse. traverse will
+ * take a hold on the new vp so the caller is responsible for calling
+ * VN_RELE on the returned vp.
+ */
+ VN_HOLD(vp);
+ nvp = vp;
+ if (traverse(&nvp) != 0) {
+ VN_RELE(nvp);
+ return (NULL);
+ }
+
+ /* Confirm that we have a non-autofs fs mounted now */
+ if (nvp->v_op == lx_autofs_vn_ops) {
+ VN_RELE(nvp);
+ return (NULL);
+ }
+
+ return (nvp);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
+ caller_context_t *ctp, int flags)
+{
+ vnode_t *udvp = dvp->v_data;
+ vnode_t *nvp;
+
+ /* handle direct mount here */
+ if ((nvp = lx_autofs_do_direct(dvp)) != NULL) {
+ int error;
+
+ error = VOP_RMDIR(nvp, nm, cdir, cr, ctp, flags);
+ VN_RELE(nvp);
+ return (error);
+ }
+
+ /*
+ * cdir is the calling processes current directory.
+ * If cdir is lx_autofs vnode then get its real underlying
+ * vnode ptr. (It seems like the only thing cdir is
+ * ever used for is to make sure the user doesn't delete
+ * their current directory.)
+ */
+ if (vn_matchops(cdir, lx_autofs_vn_ops)) {
+ vnode_t *ucdir = cdir->v_data;
+ return (VOP_RMDIR(udvp, nm, ucdir, cr, ctp, flags));
+ }
+
+ return (VOP_RMDIR(udvp, nm, cdir, cr, ctp, flags));
+}
+
+/*
+ * VOP entry points - special passthrough
+ *
+ * For some VOP entry points we will first pass the request on to
+ * the underlying filesystem we're mounted on. If there's an error
+ * then we immediately return the error, but if the request succeeds
+ * we have to do some extra work before returning.
+ */
+static int
+lx_autofs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ctp)
+{
+ vnode_t *ovp = *vpp;
+ vnode_t *uvp = ovp->v_data;
+ int error;
+
+ /* direct mounts were handled by the lookup to get *vpp */
+
+ if ((error = VOP_OPEN(&uvp, flag, cr, ctp)) != 0)
+ return (error);
+
+ /* Check for clone opens. */
+ if (uvp == ovp->v_data)
+ return (0);
+
+ /* Deal with clone opens by returning a new vnode. */
+ *vpp = lx_autofs_vn_alloc(ovp->v_vfsp, uvp);
+ VN_RELE(ovp);
+ return (0);
+}
+
+/*
+ * Internally, we have already converted our autofs vfs device number into a
+ * Linux-format device during lx_autofs_mount and stored that device number
+ * in data->lav_dev. However, our lx emulation for the various stat() syscalls
+ * also wants to convert the fsid the same way. That obviously will be
+ * incorrect if we pass along an fsid that is already converted, so we always
+ * pass along the original vfs fsid here. Both lav_dev and lav_ino are passed
+ * in messages to the automounter, and these must match the values obtained by
+ * stat().
+ */
+static int
+lx_autofs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+ vnode_t *dvp;
+ int error;
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
+ dev_t autofs_fsid = vp->v_vfsp->vfs_dev;
+
+ if ((dvp = lx_autofs_do_direct(vp)) != NULL) {
+ uvp = dvp;
+ }
+
+ error = VOP_GETATTR(uvp, vap, flags, cr, ctp);
+
+ if (dvp != NULL) {
+ /* we operated on the direct mounted fs */
+ VN_RELE(dvp);
+ if (error == 0) {
+ /*
+ * During automounter restart recovery, the automounter
+ * will fstat the fd provided in the setpipe ioctl. It
+ * uses the resulting inode & dev to correlate future
+ * autofs fifo requests to the correct entry. Thus, we
+ * have to update the attributes with the proper IDs.
+ */
+ vap->va_fsid = autofs_fsid;
+ vap->va_nodeid = data->lav_ino;
+ }
+ } else if (error == 0) {
+ /* Update the attributes with our filesystem id. */
+ vap->va_fsid = autofs_fsid;
+ }
+
+ return (error);
+}
+
+static int
+lx_autofs_mkdir(vnode_t *dvp, char *nm, struct vattr *vap, vnode_t **vpp,
+ cred_t *cr, caller_context_t *ctp, int flags, vsecattr_t *vsecp)
+{
+ vnode_t *udvp = dvp->v_data;
+ vnode_t *nvp;
+ int error;
+
+ if ((nvp = lx_autofs_do_direct(dvp)) != NULL) {
+ udvp = nvp;
+ }
+
+ error = VOP_MKDIR(udvp, nm, vap, vpp, cr, ctp, flags, vsecp);
+
+ if (nvp != NULL) {
+ /* we operated on the direct mounted fs */
+ VN_RELE(nvp);
+ } else if (error == 0) {
+ vnode_t *uvp = NULL;
+
+ /* Update the attributes with our filesystem id. */
+ vap->va_fsid = dvp->v_vfsp->vfs_dev;
+
+ /* Allocate our new vnode. */
+ uvp = *vpp;
+ *vpp = lx_autofs_vn_alloc(dvp->v_vfsp, uvp);
+ }
+
+ return (error);
+}
+
+/*
+ * VOP entry points - custom
+ */
+/*ARGSUSED*/
+static void
+lx_autofs_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ctp)
+{
+ lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
+
+ /*
+ * We need to hold the vfs lock because if we're going to free
+ * this vnode we have to prevent anyone from looking it up
+ * in the vnode hash.
+ */
+ mutex_enter(&data->lav_lock);
+ mutex_enter(&vp->v_lock);
+
+ if (vp->v_count < 1) {
+ panic("lx_autofs_inactive: bad v_count");
+ /*NOTREACHED*/
+ }
+
+ /* Drop the temporary hold by vn_rele now. */
+ if (--vp->v_count > 0) {
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&data->lav_lock);
+ return;
+ }
+
+ /*
+ * No one should have been blocked on this lock because we're
+ * about to free this vnode.
+ */
+ lx_autofs_vn_free(vp);
+}
+
+static int
+lx_autofs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ctp,
+ int *direntflags, pathname_t *realpnp)
+{
+ vnode_t *udvp = dvp->v_data;
+ vnode_t *uvp = NULL;
+ lx_autofs_vfs_t *data;
+ int error = ENOENT;
+
+ data = (lx_autofs_vfs_t *)dvp->v_vfsp->vfs_data;
+
+ /*
+ * For an indirect mount first try to lookup if this path component
+ * already exists.
+ */
+ if (data->lav_mnttype == LXAMT_INDIR) {
+ if ((error = VOP_LOOKUP(udvp, nm, &uvp, pnp, flags, rdir, cr,
+ ctp, direntflags, realpnp)) == 0) {
+ *vpp = lx_autofs_vn_alloc(dvp->v_vfsp, uvp);
+ return (0);
+ }
+ }
+
+ /* Only query the automounter if the path does not exist. */
+ if (error != ENOENT)
+ return (error);
+
+ if (flags & __FLXNOAUTO)
+ return (ENOENT);
+
+ if (data->lav_catatonic)
+ return (ENOENT);
+
+ /* Save the uid/gid for the requestor ioctl. */
+ data->lav_uid = crgetuid(cr);
+ data->lav_gid = crgetgid(cr);
+
+ /* Refer the lookup to the automounter. */
+ if ((error = lx_autofs_automounter_call(dvp, nm)) != 0)
+ return (error);
+
+ if (data->lav_mnttype == LXAMT_INDIR) {
+ /*
+ * Indirect mount. The automounter call should have mounted
+ * something on nm. Retry the lookup operation.
+ */
+ if ((error = VOP_LOOKUP(udvp, nm, &uvp, pnp, flags, rdir, cr,
+ ctp, direntflags, realpnp)) == 0) {
+ *vpp = lx_autofs_vn_alloc(dvp->v_vfsp, uvp);
+ return (0);
+ }
+ } else {
+ /*
+ * Direct or offset mount. The automounter call should have
+ * covered our 'dvp' with a new filesystem. Traverse into the
+ * new mount and retry the lookup.
+ *
+ * We need to take an extra hold on our vp (which is the autofs
+ * root vp) to acount for the rele done in traverse. Our caller
+ * will also do a rele on the original dvp and that would leave
+ * us one ref short on our autofs root vnode.
+ */
+ vnode_t *orig_dvp = dvp;
+
+ VN_HOLD(dvp);
+ if ((error = traverse(&dvp)) != 0) {
+ VN_RELE(dvp);
+ return (error);
+ }
+
+ if (dvp == orig_dvp) {
+ /*
+ * For some reason the automountd did not actually
+ * mount the new filesystem. Return an error.
+ */
+ VN_RELE(dvp);
+ return (ENOENT);
+ }
+
+ error = VOP_LOOKUP(dvp, nm, vpp, pnp, flags, rdir, cr, ctp,
+ direntflags, realpnp);
+
+ /* release the traverse hold */
+ VN_RELE(dvp);
+ }
+ return (error);
+}
+
+static int
+lx_autofs_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr,
+ int *rvalp, caller_context_t *ctp)
+{
+ vnode_t *uvp = vp->v_data;
+
+ /* Intercept our ioctls. */
+ switch ((uint_t)cmd) {
+ case LX_AUTOFS_IOC_READY:
+ case LX_AUTOFS_IOC_FAIL:
+ case LX_AUTOFS_IOC_CATATONIC:
+ case LX_AUTOFS_IOC_PROTOVER:
+ case LX_AUTOFS_IOC_SETTIMEOUT:
+ case LX_AUTOFS_IOC_EXPIRE:
+ case LX_AUTOFS_IOC_EXPIRE_MULTI:
+ case LX_AUTOFS_IOC_PROTOSUBVER:
+ case LX_AUTOFS_IOC_ASKUMOUNT:
+ return (lx_autofs_automounter_ioctl(vp, cmd, arg, cr));
+ }
+
+ /* Pass any remaining ioctl on. */
+ return (VOP_IOCTL(uvp, cmd, arg, mode, cr, rvalp, ctp));
+}
+
+/*
+ * VOP entry points definitions
+ */
+static const fs_operation_def_t lx_autofs_tops_root[] = {
+ { VOPNAME_OPEN, { .vop_open = lx_autofs_open } },
+ { VOPNAME_CLOSE, { .vop_close = lx_autofs_close } },
+ { VOPNAME_IOCTL, { .vop_ioctl = lx_autofs_ioctl } },
+ { VOPNAME_RWLOCK, { .vop_rwlock = lx_autofs_rwlock } },
+ { VOPNAME_RWUNLOCK, { .vop_rwunlock = lx_autofs_rwunlock } },
+ { VOPNAME_GETATTR, { .vop_getattr = lx_autofs_getattr } },
+ { VOPNAME_ACCESS, { .vop_access = lx_autofs_access } },
+ { VOPNAME_READDIR, { .vop_readdir = lx_autofs_readdir } },
+ { VOPNAME_LOOKUP, { .vop_lookup = lx_autofs_lookup } },
+ { VOPNAME_INACTIVE, { .vop_inactive = lx_autofs_inactive } },
+ { VOPNAME_MKDIR, { .vop_mkdir = lx_autofs_mkdir } },
+ { VOPNAME_RMDIR, { .vop_rmdir = lx_autofs_rmdir } },
+ { NULL }
+};
+
+/*
+ * DEV-specific entry points
+ */
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_close(dev_t dev, int flags, int otyp, cred_t *credp)
+{
+ return (0);
+}
+
+static int
+lx_autofs_dev_validate_cmd(intptr_t arg, lx_autofs_dv_ioctl_t *dcmd)
+{
+ if (copyin((caddr_t)arg, dcmd, sizeof (lx_autofs_dv_ioctl_t)) != 0)
+ return (EFAULT);
+
+ if (dcmd->lad_ver_major != LX_AUTOFS_DEV_VERSION_MAJOR ||
+ dcmd->lad_ver_minor > LX_AUTOFS_DEV_VERSION_MINOR)
+ return (EINVAL);
+
+ DTRACE_PROBE1(lx__dev__cmd, void *, dcmd);
+
+ /* Fill in the version for return */
+ dcmd->lad_ver_major = LX_AUTOFS_DEV_VERSION_MAJOR;
+ dcmd->lad_ver_minor = LX_AUTOFS_DEV_VERSION_MINOR;
+ return (0);
+}
+
+static vfs_t *
+lx_autofs_dev_getvfs_bypath(char *fs_mntpt)
+{
+ struct vfs *vfsp;
+ struct vfs *vfslist;
+ vfs_t *fnd_vfs = NULL;
+ zone_t *zone = curzone;
+
+ vfs_list_read_lock();
+
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ return (NULL);
+ }
+
+ do {
+ if (vfsp->vfs_op == lx_autofs_vfsops) {
+ char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+ if (strcmp(fs_mntpt, ZONE_PATH_TRANSLATE(mntpt, zone))
+ == 0) {
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+ break;
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+ return (fnd_vfs);
+}
+
+static int
+lx_autofs_dev_fd_preamble(intptr_t arg, lx_autofs_dv_ioctl_t *dc, vfs_t **vfspp)
+{
+ int err;
+ lx_autofs_vfs_t *data;
+ file_t *fp;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, dc)) != 0)
+ return (err);
+
+ if ((fp = getf(dc->lad_ioctlfd)) == NULL)
+ return (EBADF);
+
+ vfsp = fp->f_vnode->v_vfsp;
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ releasef(dc->lad_ioctlfd);
+ return (EBADF);
+ }
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ if (data->lav_root->v_count <= 1) {
+ releasef(dc->lad_ioctlfd);
+ return (EBADF);
+ }
+
+ VFS_HOLD(vfsp);
+ *vfspp = vfsp;
+
+ releasef(dc->lad_ioctlfd);
+ return (0);
+}
+
+static int
+lx_autofs_dev_vers(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_protver(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ dcmd.lad_arg1 = LX_AUTOFS_PROTO_VERS5;
+
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_protosubver(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ dcmd.lad_arg1 = LX_AUTOFS_PROTO_SUBVERSION;
+
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_get_path_cmd(intptr_t arg, lx_autofs_dv_ioctl_t **dcp)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd, *dc;
+
+ if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+ return (err);
+
+ if (dcmd.lad_size <= sizeof (dcmd) ||
+ dcmd.lad_size > (sizeof (dcmd) + MAXPATHLEN))
+ return (EINVAL);
+
+ dc = kmem_alloc(dcmd.lad_size, KM_SLEEP);
+
+ /* re-copyin the full struct with the path */
+ if (copyin((caddr_t)arg, dc, dcmd.lad_size) != 0) {
+ kmem_free(dc, dcmd.lad_size);
+ return (EFAULT);
+ }
+ dc->lad_size = dcmd.lad_size;
+
+ if (dc->lad_path[0] != '/' ||
+ dc->lad_path[dcmd.lad_size - sizeof (dcmd) - 1] != '\0') {
+ kmem_free(dc, dcmd.lad_size);
+ return (EINVAL);
+ }
+
+ *dcp = dc;
+ return (0);
+}
+
+static int
+lx_autofs_dev_openmount(intptr_t arg)
+{
+ int err;
+ int fd;
+ lx_autofs_dv_ioctl_t *dc;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+ return (err);
+
+ if ((vfsp = lx_autofs_dev_getvfs_bypath(dc->lad_path)) == NULL) {
+ kmem_free(dc, dc->lad_size);
+ return (EINVAL);
+ }
+
+ /* lad_arg1 is the dev number of the mnt but we don't check that */
+
+ /*
+ * Do an "open" on the root vnode. To fully simulate "open" we also add
+ * a hold on the root vnode itself since lx_autofs_open will only open
+ * (and hold) the underlying vnode.
+ */
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ VN_HOLD(data->lav_root);
+ if ((err = fassign(&data->lav_root, FWRITE|FREAD, &fd)) != 0) {
+ VN_RELE(data->lav_root);
+ VFS_RELE(vfsp);
+ kmem_free(dc, dc->lad_size);
+ return (err);
+ }
+
+ mutex_enter(&data->lav_lock);
+ data->lav_openmnt_cnt++;
+ mutex_exit(&data->lav_lock);
+
+ dc->lad_ioctlfd = fd;
+
+ if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+ mutex_enter(&data->lav_lock);
+ data->lav_openmnt_cnt--;
+ mutex_exit(&data->lav_lock);
+ (void) closeandsetf(fd, NULL);
+ VFS_RELE(vfsp);
+ kmem_free(dc, dc->lad_size);
+ return (EFAULT);
+ }
+ VFS_RELE(vfsp);
+
+ kmem_free(dc, dc->lad_size);
+ return (0);
+}
+
+static int
+lx_autofs_dev_closemount(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+ /* "close" the vnode */
+ if ((err = closeandsetf(dcmd.lad_ioctlfd, NULL)) != 0) {
+ VFS_RELE(vfsp);
+ return (err);
+ }
+
+ mutex_enter(&data->lav_lock);
+ ASSERT(data->lav_openmnt_cnt > 0);
+ data->lav_openmnt_cnt--;
+ mutex_exit(&data->lav_lock);
+
+ VFS_RELE(vfsp);
+ return (0);
+}
+
+static int
+lx_autofs_dev_ready(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ if ((err = lx_autofs_ack(dcmd.lad_arg1, vfsp, LXACR_READY)) != 0) {
+ VFS_RELE(vfsp);
+ return (err);
+ }
+
+ VFS_RELE(vfsp);
+ return (0);
+}
+
+static int
+lx_autofs_dev_fail(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ if ((err = lx_autofs_ack(dcmd.lad_arg1, vfsp, LXACR_FAIL)) != 0) {
+ VFS_RELE(vfsp);
+ return (err);
+ }
+
+ VFS_RELE(vfsp);
+ return (0);
+}
+
+/*
+ * Update the fifo pipe information we use to talk to the automounter. The
+ * ioctl is used when the automounter restarts. This logic is similar to the
+ * handling done in lx_autofs_parse_mntopt() when the filesytem is first
+ * mounted.
+ */
+static int
+lx_autofs_dev_setpipefd(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+ int fd, pgrp;
+ file_t *fp_wr, *fp_rd;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ mutex_enter(&pidlock);
+ pgrp = curproc->p_pgrp;
+ mutex_exit(&pidlock);
+ fd = dcmd.lad_arg1;
+
+ /* Lookup the new fifos. See comment in lx_autofs_parse_mntopt. */
+ if (lx_autofs_fifo_lookup(pgrp, fd, &fp_wr, &fp_rd) != 0) {
+ int pid = (int)curproc->p_pid;
+
+ if (lx_autofs_fifo_lookup(pid, fd, &fp_wr, &fp_rd) != 0) {
+ VFS_RELE(vfsp);
+ return (EINVAL);
+ }
+ }
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+ /* Close the old fifos. */
+ if (data->lav_fifo_wr != NULL)
+ (void) closef(data->lav_fifo_wr);
+ if (data->lav_fifo_rd != NULL)
+ (void) closef(data->lav_fifo_rd);
+
+ data->lav_fd = fd;
+ data->lav_pgrp = pgrp;
+ data->lav_fifo_rd = fp_rd;
+ data->lav_fifo_wr = fp_wr;
+ /*
+ * Not explicitly in the ioctl spec. but necessary for correct recovery
+ */
+ data->lav_catatonic = B_FALSE;
+
+ VFS_RELE(vfsp);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_catatonic(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ data->lav_catatonic = B_TRUE;
+ VFS_RELE(vfsp);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_expire(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ /* If it succeeds in expiring then we don't want to return EAGAIN */
+ if ((err = lx_autofs_expire(vfsp, kcred)) == 0) {
+ VFS_RELE(vfsp);
+ return (0);
+ }
+
+ VFS_RELE(vfsp);
+ return (EAGAIN);
+}
+
+static int
+lx_autofs_dev_timeout(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+ lx_autofs_vfs_t *data;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+ data->lav_timeout = dcmd.lad_arg1;
+ VFS_RELE(vfsp);
+
+ return (0);
+}
+
+static int
+lx_autofs_dev_requestor(intptr_t arg)
+{
+ int err;
+ lx_autofs_dv_ioctl_t *dc;
+ vfs_t *vfsp;
+ vfs_t *fnd_vfs = NULL;
+ struct vfs *vfslist;
+ zone_t *zone = curzone;
+ lx_autofs_vfs_t *data;
+ uid_t uid;
+ gid_t gid;
+
+ if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+ return (err);
+
+ vfs_list_read_lock();
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ kmem_free(dc, dc->lad_size);
+ return (EINVAL);
+ }
+
+ do {
+ /* Skip mounts we shouldn't show. */
+ if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+ char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+ if (strcmp(dc->lad_path,
+ ZONE_PATH_TRANSLATE(mntpt, zone)) == 0) {
+
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ /*
+ * Found an indirect mount (probably
+ * NFS) so we need to get the vfs it's
+ * mounted onto.
+ */
+ vnode_t *vn = vfsp->vfs_vnodecovered;
+ vfsp = vn->v_vfsp;
+
+ if (vfsp->vfs_op != lx_autofs_vfsops) {
+ /*
+ * autofs doesn't manage this
+ * path.
+ */
+ break;
+ }
+ }
+
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+ break;
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+ vfs_list_unlock();
+
+ if (fnd_vfs == NULL) {
+ kmem_free(dc, dc->lad_size);
+ return (EINVAL);
+ }
+
+ data = (lx_autofs_vfs_t *)fnd_vfs->vfs_data;
+ uid = data->lav_uid;
+ gid = data->lav_gid;
+ VFS_RELE(fnd_vfs);
+
+ dc->lad_arg1 = uid;
+ dc->lad_arg2 = gid;
+
+ if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+ kmem_free(dc, dc->lad_size);
+ return (EFAULT);
+ }
+
+ kmem_free(dc, dc->lad_size);
+ return (0);
+}
+
+static int
+lx_autofs_dev_ismntpt(intptr_t arg)
+{
+ int err = 0;
+ lx_autofs_dv_ioctl_t *dc;
+ struct vfs *vfslist;
+ vfs_t *vfsp;
+ vfs_t *fnd_vfs = NULL;
+ zone_t *zone = curzone;
+
+ if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+ return (err);
+
+ /*
+ * The automounter will always pass a path. It can also either pass an
+ * ioctlfd or, if it's -1, arg1 can be an LX_AUTOFS_TYPE_* value. We
+ * currently don't need those for our algorithm.
+ */
+
+ vfs_list_read_lock();
+ vfsp = vfslist = curzone->zone_vfslist;
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ kmem_free(dc, dc->lad_size);
+ return (0); /* return 0 if not a mount point */
+ }
+
+ do {
+ if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+ char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+ if (strcmp(dc->lad_path,
+ ZONE_PATH_TRANSLATE(mntpt, zone)) == 0) {
+
+ /*
+ * To handle direct mounts (on top of an autofs
+ * mount), we must prefer non-autofs vfs for
+ * this request.
+ */
+ if (fnd_vfs != NULL)
+ VFS_RELE(fnd_vfs);
+
+ fnd_vfs = vfsp;
+ VFS_HOLD(fnd_vfs)
+
+ if (fnd_vfs->vfs_op != lx_autofs_vfsops)
+ break;
+ }
+ }
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+ vfs_list_unlock();
+
+ if (fnd_vfs == NULL) {
+ kmem_free(dc, dc->lad_size);
+ return (0); /* return 0 if not a mount point */
+ }
+
+ /*
+ * arg1 is device number, arg2 is superblock magic number
+ * The superblock value only matters if autofs or not.
+ */
+ dc->lad_arg1 = fnd_vfs->vfs_dev;
+ if (fnd_vfs->vfs_op == lx_autofs_vfsops) {
+ dc->lad_arg2 = LX_AUTOFS_SB_MAGIC;
+ } else {
+ dc->lad_arg2 = ~LX_AUTOFS_SB_MAGIC;
+ }
+
+ VFS_RELE(fnd_vfs);
+
+ if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+ kmem_free(dc, dc->lad_size);
+ return (EFAULT);
+ }
+
+ kmem_free(dc, dc->lad_size);
+
+ /*
+ * We have to return 1 if it is a mount point. The lx ioctl autofs
+ * translator will convert a negative value back to a positive,
+ * non-error return value.
+ */
+ return (-1);
+}
+
+static int
+lx_autofs_dev_askumount(intptr_t arg)
+{
+ int err;
+ int v;
+ lx_autofs_dv_ioctl_t dcmd;
+ vfs_t *vfsp;
+
+ if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+ return (err);
+
+ if (lx_autofs_may_unmount(vfsp, kcred)) {
+ v = 0;
+ } else {
+ v = 1;
+ }
+ VFS_RELE(vfsp);
+
+ dcmd.lad_arg1 = v;
+ if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ switch (cmd) {
+ case LX_AUTOFS_DEV_IOC_VERSION_CMD:
+ return (lx_autofs_dev_vers(arg));
+
+ case LX_AUTOFS_DEV_IOC_PROTOVER_CMD:
+ return (lx_autofs_dev_protver(arg));
+
+ case LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD:
+ return (lx_autofs_dev_protosubver(arg));
+
+ case LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD:
+ return (lx_autofs_dev_openmount(arg));
+
+ case LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD:
+ return (lx_autofs_dev_closemount(arg));
+
+ case LX_AUTOFS_DEV_IOC_READY_CMD:
+ return (lx_autofs_dev_ready(arg));
+
+ case LX_AUTOFS_DEV_IOC_FAIL_CMD:
+ return (lx_autofs_dev_fail(arg));
+
+ case LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD:
+ return (lx_autofs_dev_setpipefd(arg));
+
+ case LX_AUTOFS_DEV_IOC_CATATONIC_CMD:
+ return (lx_autofs_dev_catatonic(arg));
+
+ case LX_AUTOFS_DEV_IOC_TIMEOUT_CMD:
+ return (lx_autofs_dev_timeout(arg));
+
+ case LX_AUTOFS_DEV_IOC_REQUESTER_CMD:
+ return (lx_autofs_dev_requestor(arg));
+
+ case LX_AUTOFS_DEV_IOC_EXPIRE_CMD:
+ return (lx_autofs_dev_expire(arg));
+
+ case LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD:
+ return (lx_autofs_dev_askumount(arg));
+
+ case LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD:
+ return (lx_autofs_dev_ismntpt(arg));
+ }
+
+ return (EINVAL);
+}
+
+/*
+ * lx_autofs_init() gets invoked via the mod_install() call in
+ * this module's _init() routine. Therefore, the code that cleans
+ * up the structures we allocate below is actually found in
+ * our _fini() routine.
+ */
+/* ARGSUSED */
+static int
+lx_autofs_init(int fstype, char *name)
+{
+ int error;
+
+ lx_autofs_major = ddi_name_to_major(LX_AUTOFS_NAME);
+
+ lx_autofs_fstype = fstype;
+ if ((error = vfs_setfsops(fstype, lx_autofs_vfstops,
+ &lx_autofs_vfsops)) != 0) {
+ cmn_err(CE_WARN, "lx_autofs_init: bad vfs ops template");
+ return (error);
+ }
+
+ if ((error = vn_make_ops(name, lx_autofs_tops_root,
+ &lx_autofs_vn_ops)) != 0) {
+ VERIFY(vfs_freevfsops_by_type(fstype) == 0);
+ lx_autofs_vn_ops = NULL;
+ return (error);
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int instance = ddi_get_instance(dip);
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ ASSERT(instance == 0);
+ if (instance != 0)
+ return (DDI_FAILURE);
+
+ /* create our minor node */
+ if (ddi_create_minor_node(dip, LX_AUTOFS_MINORNAME, S_IFCHR, 0,
+ DDI_PSEUDO, 0) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ lx_autofs_dip = dip;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ lx_autofs_dip = NULL;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+ void **resultp)
+{
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *resultp = lx_autofs_dip;
+ return (DDI_SUCCESS);
+
+ case DDI_INFO_DEVT2INSTANCE:
+ *resultp = (void *)0;
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+}
+
+/*
+ * Driver flags
+ */
+static struct cb_ops lx_autofs_cb_ops = {
+ lx_autofs_dev_open, /* open */
+ lx_autofs_dev_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ lx_autofs_dev_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* vb_prop_op */
+ NULL, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+/*
+ * Module linkage
+ */
+static mntopt_t lx_autofs_mntopt[] = {
+ { LX_MNTOPT_FD, NULL, 0, MO_HASVALUE },
+ { LX_MNTOPT_PGRP, NULL, 0, MO_HASVALUE },
+ { LX_MNTOPT_MINPROTO, NULL, 0, MO_HASVALUE },
+ { LX_MNTOPT_MAXPROTO, NULL, 0, MO_HASVALUE },
+ { LX_MNTOPT_INDIRECT, NULL, 0, 0 },
+ { LX_MNTOPT_DIRECT, NULL, 0, 0 },
+ { LX_MNTOPT_OFFSET, NULL, 0, 0 }
+};
+
+static mntopts_t lx_autofs_mntopts = {
+ sizeof (lx_autofs_mntopt) / sizeof (mntopt_t),
+ lx_autofs_mntopt
+};
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ LX_AUTOFS_NAME,
+ lx_autofs_init,
+ VSW_HASPROTO | VSW_VOLATILEDEV | VSW_ZMOUNT,
+ &lx_autofs_mntopts
+};
+
+static struct dev_ops lx_autofs_dev_ops = {
+ DEVO_REV, /* version */
+ 0, /* refcnt */
+ lx_autofs_info, /* info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ lx_autofs_attach, /* attach */
+ lx_autofs_detach, /* detach */
+ nodev, /* reset */
+ &lx_autofs_cb_ops, /* driver operations */
+ NULL, /* no bus operations */
+ NULL, /* power */
+ ddi_quiesce_not_needed /* quiesce */
+};
+
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+ &mod_fsops, "lx autofs filesystem", &vfw
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, "lx autofs driver", &lx_autofs_dev_ops
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modlfs,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ int error;
+
+ if ((error = mod_install(&modlinkage)) != 0) {
+ return (error);
+ }
+
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ if ((error = mod_remove(&modlinkage)) != 0)
+ return (error);
+
+ if (lx_autofs_vn_ops != NULL) {
+ vn_freevnodeops(lx_autofs_vn_ops);
+ lx_autofs_vn_ops = NULL;
+ }
+
+ /*
+ * In our init routine, if we get an error after calling
+ * vfs_setfsops() we cleanup by calling vfs_freevfsops_by_type().
+ * But we don't need to call vfs_freevfsops_by_type() here
+ * because the fs framework did this for us as part of the
+ * mod_remove() call above.
+ */
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/autofs/lxautofs.conf b/usr/src/uts/common/brand/lx/autofs/lxautofs.conf
new file mode 100644
index 0000000000..36e0119e33
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/autofs/lxautofs.conf
@@ -0,0 +1,14 @@
+#
+# 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 2016 Joyent, Inc.
+#
+
+name="lxautofs" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/brand/lx/cgroups/cgrps.h b/usr/src/uts/common/brand/lx/cgroups/cgrps.h
new file mode 100644
index 0000000000..46e2cdd886
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/cgroups/cgrps.h
@@ -0,0 +1,222 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _LXCGRPS_H
+#define _LXCGRPS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * cgrps.h: declarations, data structures and macros for lx_cgroup
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/atomic.h>
+#include <vm/anon.h>
+
+/*
+ * cgrpmgr ioctl interface.
+ */
+#define CGRPFS_IOC ('C' << 16 | 'G' << 8)
+#define CGRPFS_GETEVNT (CGRPFS_IOC | 1)
+
+typedef struct cgrpmgr_info {
+ pid_t cgmi_pid;
+ char *cgmi_rel_agent_path;
+ char *cgmi_cgroup_path;
+} cgrpmgr_info_t;
+
+#if defined(_KERNEL)
+
+#include <sys/lx_brand.h>
+
+typedef struct cgrpmgr_info32 {
+ pid_t cgmi_pid;
+ caddr32_t cgmi_rel_agent_path;
+ caddr32_t cgmi_cgroup_path;
+} cgrpmgr_info32_t;
+
+#define CG_PSNSIZE 256 /* max size of pseudo file name entries */
+#define CG_PSDSIZE 16 /* pretend that a dir entry takes 16 bytes */
+
+/*
+ * The order of these entries must be in sync with the cg_ssde_dir array.
+ */
+typedef enum cgrp_ssid {
+ CG_SSID_GENERIC = 1,
+ CG_SSID_NUM /* last ssid for range checking */
+} cgrp_ssid_t;
+
+typedef enum cgrp_nodetype {
+ CG_CGROUP_DIR = 1, /* cgroup directory entry */
+ CG_NOTIFY, /* notify_on_release file */
+ CG_PROCS, /* cgroup.procs file */
+ CG_REL_AGENT, /* release_agent file */
+ CG_TASKS, /* tasks file */
+} cgrp_nodetype_t;
+
+typedef struct cgrp_subsys_dirent {
+ cgrp_nodetype_t cgrp_ssd_type;
+ char *cgrp_ssd_name;
+} cgrp_subsys_dirent_t;
+
+#define N_DIRENTS(m) (cgrp_num_pseudo_ents((m)->cg_ssid) + 2)
+
+/*
+ * A modern systemd-based Linux system typically has 50-60 cgroups so
+ * we size the hash for 2x that number.
+ */
+#define CGRP_HASH_SZ 128
+#define CGRP_AGENT_LEN (MAXPATHLEN + 1)
+
+/*
+ * cgroups per-mount data structure.
+ *
+ * All but the event related fields are protected by cg_contents.
+ * The evnt_list and counter is protected by cg_events.
+ */
+typedef struct cgrp_mnt {
+ struct vfs *cg_vfsp; /* filesystem's vfs struct */
+ struct cgrp_node *cg_rootnode; /* root cgrp_node */
+ char *cg_mntpath; /* name of cgroup mount point */
+ cgrp_ssid_t cg_ssid; /* subsystem type */
+ dev_t cg_dev; /* unique dev # of mounted `device' */
+ uint_t cg_gen; /* node ID source for files */
+ uint_t cg_grp_gen; /* ID source for cgroups */
+ kmutex_t cg_contents; /* global lock for most fs activity */
+ char cg_agent[CGRP_AGENT_LEN]; /* release_agent path */
+ /* ptr to zone data for containing zone */
+ lx_zone_data_t *cg_lxzdata;
+ struct cgrp_node **cg_grp_hash; /* hash list of cgroups in the fs */
+} cgrp_mnt_t;
+
+/*
+ * cgrp_node is the file system dependent node for cgroups.
+ *
+ * The node is used to represent both directories (a cgroup) and pseudo files
+ * within the directory.
+ *
+ * Members are tagged in the comment to note which type of node they apply to:
+ * A - all
+ * D - dir (i.e. a cgroup)
+ * F - pseudo file
+ */
+
+typedef struct cgrp_node {
+ struct cgrp_node *cgn_back; /* A lnked lst of cgrp_nodes */
+ struct cgrp_node *cgn_forw; /* A lnked lst of cgrp_nodes */
+ struct cgrp_dirent *cgn_dir; /* D dirent list */
+ struct cgrp_node *cgn_parent; /* A dir containing this node */
+ struct cgrp_node *cgn_next; /* D link in per-mount cgroup */
+ /* hash table */
+ uint_t cgn_dirents; /* D number of dirents */
+ cgrp_nodetype_t cgn_type; /* A type for this node */
+ uint_t cgn_notify; /* D notify_on_release value */
+ uint_t cgn_task_cnt; /* D number of threads in grp */
+ struct vnode *cgn_vnode; /* A vnode for this cgrp_node */
+ uint_t cgn_id; /* D ID number for the cgroup */
+ struct vattr cgn_attr; /* A attributes */
+} cgrp_node_t;
+
+/*
+ * File system independent to cgroups conversion macros
+ */
+#define VFSTOCGM(vfsp) ((cgrp_mnt_t *)(vfsp)->vfs_data)
+#define VTOCGM(vp) ((cgrp_mnt_t *)(vp)->v_vfsp->vfs_data)
+#define VTOCGN(vp) ((struct cgrp_node *)(vp)->v_data)
+#define CGNTOV(cn) ((cn)->cgn_vnode)
+#define cgnode_hold(cn) VN_HOLD(CGNTOV(cn))
+#define cgnode_rele(cn) VN_RELE(CGNTOV(cn))
+
+/*
+ * Attributes
+ */
+#define cgn_mask cgn_attr.va_mask
+#define cgn_mode cgn_attr.va_mode
+#define cgn_uid cgn_attr.va_uid
+#define cgn_gid cgn_attr.va_gid
+#define cgn_fsid cgn_attr.va_fsid
+#define cgn_nodeid cgn_attr.va_nodeid
+#define cgn_nlink cgn_attr.va_nlink
+#define cgn_size cgn_attr.va_size
+#define cgn_atime cgn_attr.va_atime
+#define cgn_mtime cgn_attr.va_mtime
+#define cgn_ctime cgn_attr.va_ctime
+#define cgn_rdev cgn_attr.va_rdev
+#define cgn_blksize cgn_attr.va_blksize
+#define cgn_nblocks cgn_attr.va_nblocks
+#define cgn_seq cgn_attr.va_seq
+
+/*
+ * cgroup directories are made up of a linked list of cg_dirent structures
+ * hanging off directory cgrp_nodes. File names are not fixed length,
+ * but are null terminated.
+ */
+typedef struct cgrp_dirent {
+ struct cgrp_node *cgd_cgrp_node; /* cg node for this file */
+ struct cgrp_dirent *cgd_next; /* next directory entry */
+ struct cgrp_dirent *cgd_prev; /* prev directory entry */
+ uint_t cgd_offset; /* "offset" of dir entry */
+ uint_t cgd_hash; /* a hash of cgd_name */
+ struct cgrp_dirent *cgd_link; /* linked via hash table */
+ struct cgrp_node *cgd_parent; /* parent, dir we are in */
+ char *cgd_name; /* null terminated */
+} cgrp_dirent_t;
+
+enum de_op { DE_CREATE, DE_MKDIR, DE_RENAME }; /* direnter ops */
+enum dr_op { DR_REMOVE, DR_RMDIR, DR_RENAME }; /* dirremove ops */
+
+extern struct vnodeops *cgrp_vnodeops;
+
+int cgrp_dirdelete(cgrp_node_t *, cgrp_node_t *, char *, enum dr_op, cred_t *);
+int cgrp_direnter(cgrp_mnt_t *, cgrp_node_t *, char *, enum de_op,
+ cgrp_node_t *, struct vattr *, cgrp_node_t **, cred_t *);
+void cgrp_dirinit(cgrp_node_t *, cgrp_node_t *, cred_t *);
+int cgrp_dirlookup(cgrp_node_t *, char *, cgrp_node_t **, cred_t *);
+void cgrp_dirtrunc(cgrp_node_t *);
+void cgrp_node_init(cgrp_mnt_t *, cgrp_node_t *, vattr_t *, cred_t *);
+int cgrp_taccess(void *, int, cred_t *);
+ino_t cgrp_inode(cgrp_nodetype_t, unsigned int);
+int cgrp_num_pseudo_ents(cgrp_ssid_t);
+cgrp_node_t *cgrp_cg_hash_lookup(cgrp_mnt_t *, uint_t);
+void cgrp_rel_agent_event(cgrp_mnt_t *, cgrp_node_t *, boolean_t);
+
+#endif /* KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LXCGRPS_H */
diff --git a/usr/src/uts/common/brand/lx/cgroups/cgrps_node.c b/usr/src/uts/common/brand/lx/cgroups/cgrps_node.c
new file mode 100644
index 0000000000..d9b7e443dd
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/cgroups/cgrps_node.c
@@ -0,0 +1,1023 @@
+/*
+ * 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 2021 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+
+#include "cgrps.h"
+
+static int cgrp_dirmakecgnode(cgrp_node_t *, cgrp_mnt_t *, struct vattr *,
+ enum de_op, cgrp_node_t **, struct cred *);
+static int cgrp_diraddentry(cgrp_node_t *, cgrp_node_t *, char *);
+
+static cgrp_subsys_dirent_t cgrp_generic_dir[] = {
+ { CG_PROCS, "cgroup.procs" },
+ { CG_NOTIFY, "notify_on_release" },
+ { CG_TASKS, "tasks" }
+};
+
+typedef struct cgrp_ssde {
+ cgrp_subsys_dirent_t *cg_ssde_files;
+ int cg_ssde_nfiles;
+} cgrp_ssde_t;
+
+#define CGDIRLISTSZ(l) (sizeof (l) / sizeof ((l)[0]))
+
+/*
+ * Note, these entries must be in the same order as the cgrp_ssid_t entries.
+ */
+static cgrp_ssde_t cg_ssde_dir[] = {
+ /* subsystems start at 1 */
+ {NULL, 0},
+
+ /* CG_SSID_GENERIC */
+ {cgrp_generic_dir, CGDIRLISTSZ(cgrp_generic_dir)},
+};
+
+
+#define CG_HASH_SIZE 8192 /* must be power of 2 */
+#define CG_MUTEX_SIZE 64
+
+static cgrp_dirent_t *cg_hashtable[CG_HASH_SIZE];
+static kmutex_t cg_hashmutex[CG_MUTEX_SIZE];
+
+#define CG_HASH_INDEX(a) ((a) & (CG_HASH_SIZE-1))
+#define CG_MUTEX_INDEX(a) ((a) & (CG_MUTEX_SIZE-1))
+
+#define CG_HASH(cp, name, hash) \
+ { \
+ char Xc, *Xcp; \
+ hash = (uint_t)(uintptr_t)(cp) >> 8; \
+ for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++) \
+ hash = (hash << 4) + hash + (uint_t)Xc; \
+ }
+
+#define MODESHIFT 3
+
+typedef enum cgrp_nodehold {
+ NOHOLD,
+ HOLD
+} cgrp_nodehold_t;
+
+void
+cgrp_hash_init(void)
+{
+ int i;
+
+ for (i = 0; i < CG_MUTEX_SIZE; i++)
+ mutex_init(&cg_hashmutex[i], NULL, MUTEX_DEFAULT, NULL);
+}
+
+static void
+cgrp_hash_in(cgrp_dirent_t *c)
+{
+ uint_t hash;
+ cgrp_dirent_t **prevpp;
+ kmutex_t *cg_hmtx;
+
+ CG_HASH(c->cgd_parent, c->cgd_name, hash);
+ c->cgd_hash = hash;
+ prevpp = &cg_hashtable[CG_HASH_INDEX(hash)];
+ cg_hmtx = &cg_hashmutex[CG_MUTEX_INDEX(hash)];
+ mutex_enter(cg_hmtx);
+ c->cgd_link = *prevpp;
+ *prevpp = c;
+ mutex_exit(cg_hmtx);
+}
+
+static void
+cgrp_hash_out(cgrp_dirent_t *c)
+{
+ uint_t hash;
+ cgrp_dirent_t **prevpp;
+ kmutex_t *cg_hmtx;
+
+ hash = c->cgd_hash;
+ prevpp = &cg_hashtable[CG_HASH_INDEX(hash)];
+ cg_hmtx = &cg_hashmutex[CG_MUTEX_INDEX(hash)];
+ mutex_enter(cg_hmtx);
+ while (*prevpp != c)
+ prevpp = &(*prevpp)->cgd_link;
+ *prevpp = c->cgd_link;
+ mutex_exit(cg_hmtx);
+}
+
+static cgrp_dirent_t *
+cgrp_hash_lookup(char *name, cgrp_node_t *parent, cgrp_nodehold_t hold,
+ cgrp_node_t **found)
+{
+ cgrp_dirent_t *l;
+ uint_t hash;
+ kmutex_t *cg_hmtx;
+ cgrp_node_t *cnp;
+
+ CG_HASH(parent, name, hash);
+ cg_hmtx = &cg_hashmutex[CG_MUTEX_INDEX(hash)];
+ mutex_enter(cg_hmtx);
+ l = cg_hashtable[CG_HASH_INDEX(hash)];
+ while (l) {
+ if ((l->cgd_hash == hash) &&
+ (l->cgd_parent == parent) &&
+ (strcmp(l->cgd_name, name) == 0)) {
+ /*
+ * We need to make sure that the cgrp_node that
+ * we put a hold on is the same one that we pass back.
+ * Hence, temporary variable cnp is necessary.
+ */
+ cnp = l->cgd_cgrp_node;
+ if (hold == HOLD) {
+ ASSERT(cnp);
+ cgnode_hold(cnp);
+ }
+ if (found)
+ *found = cnp;
+ mutex_exit(cg_hmtx);
+ return (l);
+ } else {
+ l = l->cgd_link;
+ }
+ }
+ mutex_exit(cg_hmtx);
+ return (NULL);
+}
+
+/*
+ * The following functions maintain the per-mount cgroup hash table.
+ */
+static void
+cgrp_cg_hash_insert(cgrp_mnt_t *cgm, cgrp_node_t *cn)
+{
+ uint_t cgid;
+ int hsh;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ cgid = cn->cgn_id;
+ hsh = cgid % CGRP_HASH_SZ;
+
+ cn->cgn_next = cgm->cg_grp_hash[hsh];
+ cgm->cg_grp_hash[hsh] = cn;
+}
+
+static void
+cgrp_cg_hash_remove(cgrp_mnt_t *cgm, cgrp_node_t *cn)
+{
+ uint_t cgid;
+ int hsh;
+ cgrp_node_t *np = NULL, *curp, *prevp = NULL;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ cgid = cn->cgn_id;
+ hsh = cgid % CGRP_HASH_SZ;
+
+ for (curp = cgm->cg_grp_hash[hsh]; curp != NULL;
+ curp = curp->cgn_next) {
+ if (curp->cgn_id == cgid) {
+ if (prevp == NULL) {
+ cgm->cg_grp_hash[hsh] = curp->cgn_next;
+ } else {
+ prevp->cgn_next = curp->cgn_next;
+ }
+ np = curp;
+ np->cgn_next = NULL;
+ break;
+ }
+
+ prevp = curp;
+ }
+
+ ASSERT(np != NULL);
+ ASSERT(np->cgn_task_cnt == 0);
+}
+
+/*
+ * Count up the number of threads already running in the zone and initialize the
+ * first cgroup's task counter.
+ *
+ * We have to look at all of the processes to find applicable ones.
+ */
+static void
+cgrp_cg_hash_init(cgrp_node_t *cn)
+{
+ int i;
+ int cnt = 0;
+ zoneid_t zoneid = curproc->p_zone->zone_id;
+ pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+
+ /* Scan all of the process entries */
+ mutex_enter(&pidlock);
+ for (i = 1; i < v.v_proc; i++) {
+ proc_t *p;
+
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, system processes,
+ * a PID of 0, the pid for our zsched process, anything the
+ * security policy doesn't allow us to look at, its not an
+ * lx-branded process and processes that are not in the zone.
+ */
+ if ((p = pid_entry(i)) == NULL ||
+ p->p_stat == SIDL ||
+ (p->p_flag & SSYS) != 0 ||
+ p->p_pid == 0 ||
+ p->p_pid == schedpid ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+ p->p_zone->zone_id != zoneid) {
+ continue;
+ }
+
+ mutex_enter(&p->p_lock);
+ if (p->p_brand != &lx_brand) {
+ mutex_exit(&p->p_lock);
+ continue;
+ }
+ cnt += p->p_lwpcnt;
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * There should be at least the init process with 1 thread in the zone
+ */
+ ASSERT(cnt > 0);
+ cn->cgn_task_cnt = cnt;
+
+ DTRACE_PROBE2(cgrp__grp__init, void *, cn, int, cnt);
+
+ mutex_exit(&pidlock);
+}
+
+cgrp_node_t *
+cgrp_cg_hash_lookup(cgrp_mnt_t *cgm, uint_t cgid)
+{
+ int hsh = cgid % CGRP_HASH_SZ;
+ cgrp_node_t *curp;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ for (curp = cgm->cg_grp_hash[hsh]; curp != NULL;
+ curp = curp->cgn_next) {
+ if (curp->cgn_id == cgid) {
+ return (curp);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them to give the inode number for
+ * a cgrp pseudo file node.
+ */
+ino_t
+cgrp_inode(cgrp_nodetype_t type, unsigned int cgrpid)
+{
+ /*
+ * cgroup inode format:
+ * 00000000AABBBBBB
+ *
+ * AA - node type (from subsystem list)
+ * BBBBBB - id of the cgroup
+ */
+
+ return ((ino_t)(type << 24) | (cgrpid & 0xffffff));
+}
+
+/*
+ * Return the number of pseudo file entries in a cgroup directory for the
+ * given subsystem.
+ */
+int
+cgrp_num_pseudo_ents(cgrp_ssid_t ssid)
+{
+ cgrp_ssde_t *ssdp = &cg_ssde_dir[ssid];
+
+ return (ssdp->cg_ssde_nfiles);
+}
+
+int
+cgrp_taccess(void *vcp, int mode, cred_t *cred)
+{
+ cgrp_node_t *cn = vcp;
+ int shift = 0;
+ /*
+ * Check access based on owner, group and public perms in cgrp_node.
+ */
+ if (crgetuid(cred) != cn->cgn_uid) {
+ shift += MODESHIFT;
+ if (groupmember(cn->cgn_gid, cred) == 0)
+ shift += MODESHIFT;
+ }
+
+ return (secpolicy_vnode_access2(cred, CGNTOV(cn), cn->cgn_uid,
+ cn->cgn_mode << shift, mode));
+}
+
+/*
+ * Search directory 'parent' for entry 'name'.
+ *
+ * 0 is returned on success and *foundcp points
+ * to the found cgrp_node with its vnode held.
+ */
+int
+cgrp_dirlookup(cgrp_node_t *parent, char *name, cgrp_node_t **foundcp,
+ cred_t *cred)
+{
+ int error;
+
+ ASSERT(MUTEX_HELD(&VTOCGM(parent->cgn_vnode)->cg_contents));
+ *foundcp = NULL;
+ if (parent->cgn_type != CG_CGROUP_DIR)
+ return (ENOTDIR);
+
+ if ((error = cgrp_taccess(parent, VEXEC, cred)))
+ return (error);
+
+ if (*name == '\0') {
+ cgnode_hold(parent);
+ *foundcp = parent;
+ return (0);
+ }
+
+ /*
+ * Search the directory for the matching name
+ * We need the lock protecting the cgn_dir list
+ * so that it doesn't change out from underneath us.
+ * cgrp_hash_lookup() will pass back the cgrp_node
+ * with a hold on it.
+ */
+
+ if (cgrp_hash_lookup(name, parent, HOLD, foundcp) != NULL) {
+ ASSERT(*foundcp);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Enter a directory entry for 'name' and 'cp' into directory 'dir'
+ *
+ * Returns 0 on success.
+ */
+int
+cgrp_direnter(
+ cgrp_mnt_t *cgm,
+ cgrp_node_t *dir, /* target directory to make entry in */
+ char *name, /* name of entry */
+ enum de_op op, /* entry operation */
+ cgrp_node_t *cn, /* existing cgrp_node, if rename */
+ struct vattr *va,
+ cgrp_node_t **cnp, /* return cgrp_node, if create/mkdir */
+ cred_t *cred)
+{
+ cgrp_dirent_t *cdp;
+ cgrp_node_t *found = NULL;
+ int error = 0;
+ char *s;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+ ASSERT(dir->cgn_type == CG_CGROUP_DIR);
+
+ /*
+ * Don't allow '/' characters in pathname component,
+ */
+ for (s = name; *s; s++)
+ if (*s == '/')
+ return (EACCES);
+
+ if (name[0] == '\0')
+ panic("cgrp_direnter: NULL name");
+
+ /*
+ * For rename lock the source entry and check the link count
+ * to see if it has been removed while it was unlocked.
+ * Remember that we can only rename within the same directory.
+ */
+ if (op == DE_RENAME) {
+ if (cn->cgn_nlink == 0) {
+ return (ENOENT);
+ }
+
+ if (cn->cgn_nlink == MAXLINK) {
+ return (EMLINK);
+ }
+ cn->cgn_nlink++;
+ gethrestime(&cn->cgn_ctime);
+ }
+
+ /*
+ * This might be a "dangling detached directory".
+ * it could have been removed, but a reference
+ * to it kept in u_cwd. don't bother searching
+ * it, and with any luck the user will get tired
+ * of dealing with us and cd to some absolute
+ * pathway. *sigh*, thus in ufs, too.
+ */
+ if (dir->cgn_nlink == 0) {
+ error = ENOENT;
+ goto out;
+ }
+
+ /*
+ * Search for the entry. In all cases it is an error if it exists.
+ */
+ cdp = cgrp_hash_lookup(name, dir, HOLD, &found);
+
+ if (cdp) {
+ ASSERT(found != NULL);
+ error = EEXIST;
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(found);
+ mutex_enter(&cgm->cg_contents);
+ } else {
+
+ /*
+ * The entry does not exist. Check write permission in
+ * directory to see if entry can be created.
+ */
+ if ((error = cgrp_taccess(dir, VWRITE, cred)) != 0)
+ goto out;
+ if (op == DE_CREATE || op == DE_MKDIR) {
+ /*
+ * Make new cgrp_node and directory entry as required.
+ */
+ error = cgrp_dirmakecgnode(dir, cgm, va, op, &cn, cred);
+ if (error)
+ goto out;
+
+ if (op == DE_MKDIR) {
+ /*
+ * inherit notify_on_release value from parent
+ */
+ cn->cgn_notify = dir->cgn_notify;
+ }
+ }
+
+ error = cgrp_diraddentry(dir, cn, name);
+ if (error != 0) {
+ if (op == DE_CREATE || op == DE_MKDIR) {
+ /*
+ * Unmake the inode we just made.
+ */
+ if ((cn->cgn_type) == CG_CGROUP_DIR) {
+ ASSERT(cdp == NULL);
+ /*
+ * cleanup allocs made by cgrp_dirinit
+ */
+ cgrp_dirtrunc(cn);
+ }
+ cn->cgn_nlink = 0;
+ gethrestime(&cn->cgn_ctime);
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(cn);
+ mutex_enter(&cgm->cg_contents);
+ cn = NULL;
+ }
+ } else if (cnp) {
+ *cnp = cn;
+ } else if (op == DE_CREATE || op == DE_MKDIR) {
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(cn);
+ mutex_enter(&cgm->cg_contents);
+ }
+ }
+
+out:
+ if (error && op == DE_RENAME) {
+ /* Undo bumped link count. */
+ cn->cgn_nlink--;
+ gethrestime(&cn->cgn_ctime);
+ }
+ return (error);
+}
+
+/*
+ * Delete entry cn of name "nm" from parent dir. This is used to both remove
+ * a cgroup directory and to remove the pseudo file nodes within the cgroup
+ * directory (by recursively calling itself). It frees the dir entry space
+ * and decrements link count on cgrp_node(s).
+ *
+ * Return 0 on success.
+ */
+int
+cgrp_dirdelete(cgrp_node_t *dir, cgrp_node_t *cn, char *nm, enum dr_op op,
+ cred_t *cred)
+{
+ cgrp_mnt_t *cgm = VTOCGM(cn->cgn_vnode);
+ cgrp_dirent_t *cndp;
+ int error;
+ size_t namelen;
+ cgrp_node_t *cnnp;
+ timestruc_t now;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ if (nm[0] == '\0')
+ panic("cgrp_dirdelete: empty name for 0x%p", (void *)cn);
+
+ /*
+ * return error when removing . and ..
+ */
+ if (nm[0] == '.') {
+ if (nm[1] == '\0')
+ return (EINVAL);
+ if (nm[1] == '.' && nm[2] == '\0')
+ return (EEXIST); /* thus in ufs */
+ }
+
+ if ((error = cgrp_taccess(dir, VEXEC|VWRITE, cred)) != 0)
+ return (error);
+
+ if (dir->cgn_dir == NULL)
+ return (ENOENT);
+
+ if (op == DR_RMDIR) {
+ /*
+ * This is the top-level removal of a cgroup dir. Start by
+ * removing the fixed pseudo file entries from the dir. We do
+ * this by recursively calling back into this function with
+ * a different op code. The caller of this function has
+ * already verified that it is safe to remove this directory.
+ */
+ cgrp_dirent_t *cdp;
+
+ ASSERT(cn->cgn_type == CG_CGROUP_DIR);
+
+ cdp = cn->cgn_dir;
+ while (cdp) {
+ cgrp_node_t *pseudo_node;
+ cgrp_dirent_t *nextp;
+
+ if (strcmp(cdp->cgd_name, ".") == 0 ||
+ strcmp(cdp->cgd_name, "..") == 0) {
+ cdp = cdp->cgd_next;
+ continue;
+ }
+
+ pseudo_node = cdp->cgd_cgrp_node;
+ nextp = cdp->cgd_next;
+
+ cgnode_hold(pseudo_node);
+ error = cgrp_dirdelete(cn, pseudo_node,
+ cdp->cgd_name, DR_REMOVE, cred);
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(pseudo_node);
+ if (error != 0)
+ return (error);
+ mutex_enter(&cgm->cg_contents);
+
+ cdp = nextp;
+ }
+
+ cgrp_cg_hash_remove(cgm, cn);
+ }
+
+ cndp = cgrp_hash_lookup(nm, dir, NOHOLD, &cnnp);
+ /* These used to be VERIFY(), but in racy conditions they can fail. */
+ if (cndp == NULL) {
+ /* Can't find the directory entry at all now! */
+ return (ENOENT);
+ }
+ if (cn != cnnp) {
+ /* Returned cnnp isn't our original, so it's also not-there. */
+ return (ENOENT);
+ }
+
+ cgrp_hash_out(cndp);
+
+ /* Take cndp out of the directory list. */
+ ASSERT(cndp->cgd_next != cndp);
+ ASSERT(cndp->cgd_prev != cndp);
+ if (cndp->cgd_prev) {
+ cndp->cgd_prev->cgd_next = cndp->cgd_next;
+ }
+ if (cndp->cgd_next) {
+ cndp->cgd_next->cgd_prev = cndp->cgd_prev;
+ }
+
+ /*
+ * If the roving slot pointer happens to match cndp,
+ * point it at the previous dirent.
+ */
+ if (dir->cgn_dir->cgd_prev == cndp) {
+ dir->cgn_dir->cgd_prev = cndp->cgd_prev;
+ }
+ ASSERT(cndp->cgd_next != cndp);
+ ASSERT(cndp->cgd_prev != cndp);
+
+ /* cndp points to the correct directory entry */
+ namelen = strlen(cndp->cgd_name) + 1;
+
+ kmem_free(cndp, sizeof (cgrp_dirent_t) + namelen);
+ dir->cgn_size -= (sizeof (cgrp_dirent_t) + namelen);
+ dir->cgn_dirents--;
+
+ gethrestime(&now);
+ dir->cgn_mtime = now;
+ dir->cgn_ctime = now;
+ cn->cgn_ctime = now;
+
+ ASSERT(cn->cgn_nlink > 0);
+ cn->cgn_nlink--;
+ if (op == DR_RMDIR && cn->cgn_type == CG_CGROUP_DIR) {
+ cgrp_dirtrunc(cn);
+ ASSERT(cn->cgn_nlink == 0);
+ }
+ return (0);
+}
+
+/*
+ * Initialize a cgrp_node and add it to file list under mount point.
+ */
+void
+cgrp_node_init(cgrp_mnt_t *cgm, cgrp_node_t *cn, vattr_t *vap, cred_t *cred)
+{
+ struct vnode *vp;
+ timestruc_t now;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+ ASSERT(vap != NULL);
+
+ cn->cgn_mode = MAKEIMODE(vap->va_type, vap->va_mode);
+ cn->cgn_mask = 0;
+ cn->cgn_attr.va_type = vap->va_type;
+ cn->cgn_nlink = 1;
+ cn->cgn_size = 0;
+
+ if (cred == NULL) {
+ cn->cgn_uid = vap->va_uid;
+ cn->cgn_gid = vap->va_gid;
+ } else {
+ cn->cgn_uid = crgetuid(cred);
+ cn->cgn_gid = crgetgid(cred);
+ }
+
+ cn->cgn_fsid = cgm->cg_dev;
+ cn->cgn_rdev = vap->va_rdev;
+ cn->cgn_blksize = PAGESIZE;
+ cn->cgn_nblocks = 0;
+ gethrestime(&now);
+ cn->cgn_atime = now;
+ cn->cgn_mtime = now;
+ cn->cgn_ctime = now;
+ cn->cgn_seq = 0;
+ cn->cgn_dir = NULL;
+
+ cn->cgn_vnode = vn_alloc(KM_SLEEP);
+ vp = CGNTOV(cn);
+ vn_setops(vp, cgrp_vnodeops);
+ vp->v_vfsp = cgm->cg_vfsp;
+ vp->v_type = vap->va_type;
+ vp->v_rdev = vap->va_rdev;
+ vp->v_data = (caddr_t)cn;
+
+ cn->cgn_nodeid = cgm->cg_gen++;
+
+ /*
+ * Add new cgrp_node to end of linked list of cgrp_nodes for this
+ * cgroup fs. Root directory is handled specially in cgrp_mount.
+ */
+ if (cgm->cg_rootnode != (cgrp_node_t *)NULL) {
+ cn->cgn_forw = NULL;
+ cn->cgn_back = cgm->cg_rootnode->cgn_back;
+ cn->cgn_back->cgn_forw = cgm->cg_rootnode->cgn_back = cn;
+ }
+ vn_exists(vp);
+}
+
+void
+cgrp_addnode(cgrp_mnt_t *cgm, cgrp_node_t *dir, char *name,
+ cgrp_nodetype_t type, struct vattr *nattr, cred_t *cr)
+{
+ cgrp_node_t *ncn;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ VERIFY0(cgrp_direnter(cgm, dir, name, DE_CREATE, (cgrp_node_t *)NULL,
+ nattr, &ncn, cr));
+
+ /*
+ * Fix the inode and assign the pseudo file type to be correct.
+ */
+ ncn->cgn_nodeid = cgrp_inode(type, dir->cgn_nodeid);
+ ncn->cgn_type = type;
+
+ /*
+ * Since we're creating these entries here and not via the
+ * normal VOP_CREATE code path, we need to do the rele to drop
+ * our hold. This will leave the vnode v_count at 0 when we
+ * come out of cgrp_inactive but we won't reclaim the vnode
+ * there since the cgn_nlink value will still be 1.
+ */
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(ncn);
+ mutex_enter(&cgm->cg_contents);
+}
+
+/*
+ * cgrp_dirinit is used internally to initialize a directory (dir)
+ * with '.' and '..' entries without checking permissions and locking
+ * It also creates the entries for the pseudo file nodes that reside in the
+ * directory.
+ */
+void
+cgrp_dirinit(cgrp_node_t *parent, cgrp_node_t *dir, cred_t *cr)
+{
+ cgrp_dirent_t *dot, *dotdot;
+ timestruc_t now;
+ cgrp_mnt_t *cgm = VTOCGM(dir->cgn_vnode);
+ cgrp_ssde_t *ssdp;
+ cgrp_subsys_dirent_t *pseudo_files;
+ struct vattr nattr;
+ int i;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+ ASSERT(dir->cgn_type == CG_CGROUP_DIR);
+
+ ASSERT(cgm->cg_ssid > 0 && cgm->cg_ssid < CG_SSID_NUM);
+ ssdp = &cg_ssde_dir[cgm->cg_ssid];
+
+ /*
+ * If this is the top-level cgroup created by the mount then we need to
+ * count up the number of procs and tasks already running in the zone.
+ */
+
+ /*
+ * Set the cgroup ID for this cgrp_node by using a counter on each
+ * mount.
+ */
+ dir->cgn_id = cgm->cg_grp_gen++;
+ cgrp_cg_hash_insert(cgm, dir);
+ /* Initialise the first cgroup if this is top-level group */
+ if (parent == dir)
+ cgrp_cg_hash_init(dir);
+
+ /*
+ * Initialize the entries
+ */
+ dot = kmem_zalloc(sizeof (cgrp_dirent_t) + 2, KM_SLEEP);
+ dot->cgd_cgrp_node = dir;
+ dot->cgd_offset = 0;
+ dot->cgd_name = (char *)dot + sizeof (cgrp_dirent_t);
+ dot->cgd_name[0] = '.';
+ dot->cgd_parent = dir;
+ cgrp_hash_in(dot);
+
+ dotdot = kmem_zalloc(sizeof (cgrp_dirent_t) + 3, KM_SLEEP);
+ dotdot->cgd_cgrp_node = parent;
+ dotdot->cgd_offset = 1;
+ dotdot->cgd_name = (char *)dotdot + sizeof (cgrp_dirent_t);
+ dotdot->cgd_name[0] = '.';
+ dotdot->cgd_name[1] = '.';
+ dotdot->cgd_parent = dir;
+ cgrp_hash_in(dotdot);
+
+ /*
+ * Initialize directory entry list.
+ */
+ dot->cgd_next = dotdot;
+ dot->cgd_prev = dotdot; /* dot's cgd_prev holds roving slot pointer */
+ dotdot->cgd_next = NULL;
+ dotdot->cgd_prev = dot;
+
+ gethrestime(&now);
+ dir->cgn_mtime = now;
+ dir->cgn_ctime = now;
+
+ parent->cgn_nlink++;
+ parent->cgn_ctime = now;
+
+ dir->cgn_dir = dot;
+ dir->cgn_size = 2 * sizeof (cgrp_dirent_t) + 5; /* dot and dotdot */
+ dir->cgn_dirents = 2;
+ dir->cgn_nlink = 2;
+
+ bzero(&nattr, sizeof (struct vattr));
+ nattr.va_mode = (mode_t)(0644);
+ nattr.va_type = VREG;
+ nattr.va_rdev = 0;
+
+ /*
+ * If this is the top-level dir in the file system then it always
+ * has a release_agent pseudo file. Only the top-level dir has this
+ * file.
+ */
+ if (parent == dir) {
+ cgrp_addnode(cgm, dir, "release_agent", CG_REL_AGENT, &nattr,
+ cr);
+ }
+
+ pseudo_files = ssdp->cg_ssde_files;
+ for (i = 0; i < ssdp->cg_ssde_nfiles; i++) {
+ cgrp_addnode(cgm, dir, pseudo_files[i].cgrp_ssd_name,
+ pseudo_files[i].cgrp_ssd_type, &nattr, cr);
+ }
+}
+
+/*
+ * cgrp_dirtrunc is called to remove all directory entries under this directory.
+ */
+void
+cgrp_dirtrunc(cgrp_node_t *dir)
+{
+ cgrp_dirent_t *cgdp;
+ timestruc_t now;
+
+ ASSERT(MUTEX_HELD(&VTOCGM(dir->cgn_vnode)->cg_contents));
+ ASSERT(dir->cgn_type == CG_CGROUP_DIR);
+
+ for (cgdp = dir->cgn_dir; cgdp; cgdp = dir->cgn_dir) {
+ size_t namelen;
+ cgrp_node_t *cn;
+
+ ASSERT(cgdp->cgd_next != cgdp);
+ ASSERT(cgdp->cgd_prev != cgdp);
+ ASSERT(cgdp->cgd_cgrp_node);
+
+ dir->cgn_dir = cgdp->cgd_next;
+ namelen = strlen(cgdp->cgd_name) + 1;
+
+ /*
+ * Adjust the link counts to account for this directory entry
+ * removal. We do hold/rele operations to free up these nodes.
+ */
+ cn = cgdp->cgd_cgrp_node;
+ ASSERT(cn->cgn_nlink > 0);
+ cn->cgn_nlink--;
+
+ cgrp_hash_out(cgdp);
+ kmem_free(cgdp, sizeof (cgrp_dirent_t) + namelen);
+ dir->cgn_size -= (sizeof (cgrp_dirent_t) + namelen);
+ dir->cgn_dirents--;
+ }
+
+ gethrestime(&now);
+ dir->cgn_mtime = now;
+ dir->cgn_ctime = now;
+
+ ASSERT(dir->cgn_dir == NULL);
+ ASSERT(dir->cgn_size == 0);
+ ASSERT(dir->cgn_dirents == 0);
+}
+
+static int
+cgrp_diraddentry(cgrp_node_t *dir, cgrp_node_t *cn, char *name)
+{
+ cgrp_dirent_t *cdp, *cpdp;
+ size_t namelen, alloc_size;
+ timestruc_t now;
+
+ /*
+ * Make sure the parent directory wasn't removed from
+ * underneath the caller.
+ */
+ if (dir->cgn_dir == NULL)
+ return (ENOENT);
+
+ /* Check that everything is on the same filesystem. */
+ if (cn->cgn_vnode->v_vfsp != dir->cgn_vnode->v_vfsp)
+ return (EXDEV);
+
+ /* Allocate and initialize directory entry */
+ namelen = strlen(name) + 1;
+ alloc_size = namelen + sizeof (cgrp_dirent_t);
+ cdp = kmem_zalloc(alloc_size, KM_NOSLEEP_LAZY);
+ if (cdp == NULL)
+ return (ENOSPC);
+
+ cn->cgn_parent = dir;
+
+ dir->cgn_size += alloc_size;
+ dir->cgn_dirents++;
+ cdp->cgd_cgrp_node = cn;
+ cdp->cgd_parent = dir;
+
+ /* The directory entry and its name were allocated sequentially. */
+ cdp->cgd_name = (char *)cdp + sizeof (cgrp_dirent_t);
+ (void) strcpy(cdp->cgd_name, name);
+
+ cgrp_hash_in(cdp);
+
+ /*
+ * Some utilities expect the size of a directory to remain
+ * somewhat static. For example, a routine which removes
+ * subdirectories between calls to readdir(); the size of the
+ * directory changes from underneath it and so the real
+ * directory offset in bytes is invalid. To circumvent
+ * this problem, we initialize a directory entry with an
+ * phony offset, and use this offset to determine end of
+ * file in cgrp_readdir.
+ */
+ cpdp = dir->cgn_dir->cgd_prev;
+ /*
+ * Install at first empty "slot" in directory list.
+ */
+ while (cpdp->cgd_next != NULL && (cpdp->cgd_next->cgd_offset -
+ cpdp->cgd_offset) <= 1) {
+ ASSERT(cpdp->cgd_next != cpdp);
+ ASSERT(cpdp->cgd_prev != cpdp);
+ ASSERT(cpdp->cgd_next->cgd_offset > cpdp->cgd_offset);
+ cpdp = cpdp->cgd_next;
+ }
+ cdp->cgd_offset = cpdp->cgd_offset + 1;
+
+ /*
+ * If we're at the end of the dirent list and the offset (which
+ * is necessarily the largest offset in this directory) is more
+ * than twice the number of dirents, that means the directory is
+ * 50% holes. At this point we reset the slot pointer back to
+ * the beginning of the directory so we start using the holes.
+ * The idea is that if there are N dirents, there must also be
+ * N holes, so we can satisfy the next N creates by walking at
+ * most 2N entries; thus the average cost of a create is constant.
+ * Note that we use the first dirent's cgd_prev as the roving
+ * slot pointer; it's ugly, but it saves a word in every dirent.
+ */
+ if (cpdp->cgd_next == NULL && cpdp->cgd_offset > 2 * dir->cgn_dirents)
+ dir->cgn_dir->cgd_prev = dir->cgn_dir->cgd_next;
+ else
+ dir->cgn_dir->cgd_prev = cdp;
+
+ ASSERT(cpdp->cgd_next != cpdp);
+ ASSERT(cpdp->cgd_prev != cpdp);
+
+ cdp->cgd_next = cpdp->cgd_next;
+ if (cdp->cgd_next) {
+ cdp->cgd_next->cgd_prev = cdp;
+ }
+ cdp->cgd_prev = cpdp;
+ cpdp->cgd_next = cdp;
+
+ ASSERT(cdp->cgd_next != cdp);
+ ASSERT(cdp->cgd_prev != cdp);
+ ASSERT(cpdp->cgd_next != cpdp);
+ ASSERT(cpdp->cgd_prev != cpdp);
+
+ gethrestime(&now);
+ dir->cgn_mtime = now;
+ dir->cgn_ctime = now;
+
+ return (0);
+}
+
+static int
+cgrp_dirmakecgnode(cgrp_node_t *dir, cgrp_mnt_t *cgm, struct vattr *va,
+ enum de_op op, cgrp_node_t **newnode, struct cred *cred)
+{
+ cgrp_node_t *cn;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+ ASSERT(va != NULL);
+
+ if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) ||
+ ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
+ return (EOVERFLOW);
+
+ cn = kmem_zalloc(sizeof (cgrp_node_t), KM_SLEEP);
+ cgrp_node_init(cgm, cn, va, cred);
+
+ cn->cgn_vnode->v_rdev = cn->cgn_rdev = NODEV;
+ cn->cgn_vnode->v_type = va->va_type;
+ cn->cgn_uid = crgetuid(cred);
+ cn->cgn_gid = crgetgid(cred);
+
+ if (va->va_mask & AT_ATIME)
+ cn->cgn_atime = va->va_atime;
+ if (va->va_mask & AT_MTIME)
+ cn->cgn_mtime = va->va_mtime;
+
+ if (op == DE_MKDIR) {
+ cn->cgn_type = CG_CGROUP_DIR;
+ cgrp_dirinit(dir, cn, cred);
+ }
+
+ *newnode = cn;
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/cgroups/cgrps_vfsops.c b/usr/src/uts/common/brand/lx/cgroups/cgrps_vfsops.c
new file mode 100644
index 0000000000..7805c3f2bd
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/cgroups/cgrps_vfsops.c
@@ -0,0 +1,1071 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * The cgroup file system implements a subset of the Linux cgroup functionality
+ * for use by lx-branded zones. On Linux, cgroups are a generic process grouping
+ * mechanism which is used to apply various behaviors to the processes within
+ * the group, although it's primary purpose is for resource management.
+ *
+ * In Linux, the cgroup file system provides two pieces of functionality:
+ * 1) A per-mount set of cgroups arranged in a tree, such that every task in
+ * the system is in one, and only one, of the cgroups in the tree.
+ * 2) A set of subsystems; each subsystem has subsystem-specific state and
+ * behavior and is associated with a cgroup mount. This provides a way to
+ * apply arbitrary functionality (but generally resource management related)
+ * to the processes associated with the nodes in the tree at that mount
+ * point.
+ *
+ * For example, it is common to see cgroup trees (each is its own mount with a
+ * different subsystem controller) for blkio, cpuset, memory, systemd (has no
+ * controller), etc. Within each tree there is a top-level directory with at
+ * least a cgroup.procs, notify_on_release, release_agent, and tasks file.
+ * The cgroup.procs file lists the processes within that group and the tasks
+ * file lists the threads in the group. There could be subdirectories, which
+ * define new cgroups, that then contain a subset of the processes. Each
+ * subdirectory also has, at a minimum, a cgroup.procs, notify_on_release, and
+ * tasks file.
+ *
+ * Since we're using lx to run user-level code within zones, the majority (all?)
+ * of the cgroup resource management functionality simply doesn't apply to us.
+ * The primary need for cgroups is to support the init program 'systemd' as the
+ * consumer. systemd only requires the process grouping hierarchy of cgroups,
+ * although it can also use the resource management features if they are
+ * available. Given this, our cgroup file system only implements the process
+ * hierarchy and does not report that any resource management controllers are
+ * available for separate mounts.
+ *
+ * In addition to the hierarchy, the other important component of cgroups that
+ * is used by systemd is the 'release_agent'. This provides a mechanism to
+ * run a command when a cgroup becomes empty (the last task in the group
+ * leaves, either by exit or move, and there are no more sub-cgroups). The
+ * 'release_agent' file only exists in the top-level cgroup of the mounted
+ * file system and holds the path to a command to run. The 'notify_on_release'
+ * file exists in each cgroup dir. If that file contains a '1' then the agent
+ * is run when that group becomes empty. The agent is passed a path string of
+ * the cgroup, relative to the file system mount point (e.g. a mount on
+ * /sys/fs/cgroups/systemd with a sub-cgroup of /sys/fs/cgroups/systemd/foo/bar
+ * gets the arg /foo/bar).
+ *
+ * Cgroup membership is implemented via hooks into the lx brand code. When
+ * the cgroup file system loads it installs callbacks for:
+ * lx_cgrp_initlwp
+ * lx_cgrp_freelwp
+ * and when it unloads it clears those hooks. The lx brand code calls those
+ * hooks when a lwp starts and when it exits. Internally we use a
+ * simple reference counter (cgn_task_cnt) on the cgroup node to track how many
+ * threads are in the group, so we can tell when a group becomes empty.
+ * To make this quick, a hash table (cg_grp_hash) is maintained on the
+ * cgrp_mnt_t struct to allow quick lookups by cgroup ID. The hash table is
+ * sized so that there should typically only be 0 or 1 cgroups per bucket.
+ * We also keep a reference to the file system in the zone-specific brand data
+ * (lxzd_cgroup) so that the lx brand code can pass in the correct vfs_t
+ * when it runs the hook.
+ *
+ * Once a cgroup is about to become empty, the final process exiting the cgroup
+ * will launch a new user-level process which execs the release agent. The new
+ * process is created as a child of zsched (indicated by the -1 pid argument
+ * to newproc) and is not associated with the exiting process in any way.
+ *
+ * This file system is similar to tmpfs in that directories only exist in
+ * memory. Each subdirectory represents a different cgroup. Within the cgroup
+ * there are pseudo files (see cg_ssde_dir) with well-defined names which
+ * control the configuration and behavior of the cgroup (see cgrp_nodetype_t).
+ * The primary files within every cgroup are named 'cgroup.procs',
+ * 'notify_on_release', and 'tasks' (as well as 'release_agent' in the
+ * top-level cgroup). The cgroup.procs and tasks files are used to control and
+ * list which processes/threads belong to the cgroup. In the general case there
+ * could be additional files in the cgroup, which defined additional behavior
+ * (i.e. subsystem specific pseudo files), although none exist at this time.
+ *
+ * Each cgroup node has a unique ID (cgn_nodeid) within the mount. This ID is
+ * used to correlate with the threads to determine cgroup membership. When
+ * assigning a PID to a cgroup (via write) the code updates the br_cgroupid
+ * member in the brand-specific lx_lwp_data structure to control which cgroup
+ * the thread belongs to. Note that because the br_cgroupid lives in
+ * lx_lwp_data, native processes will not appear in the cgroup hierarchy.
+ *
+ * An overview of the behavior for the various vnode operations is:
+ * - no hardlinks or symlinks
+ * - no file create (the subsystem-specific files are a fixed list of
+ * pseudo-files accessible within the directory)
+ * - no file remove
+ * - no file rename, but a directory (i.e. a cgroup) can be renamed within the
+ * containing directory, but not into a different directory
+ * - can mkdir and rmdir to create/destroy cgroups
+ * - cannot rmdir while it contains tasks or a subdir (i.e. a sub-cgroup)
+ * - open, read/write, close on the subsytem-specific pseudo files is
+ * allowed, as this is the interface to configure and report on the cgroup.
+ * The pseudo file's mode controls write access and cannot be changed.
+ *
+ * The locking in this file system is simple since the file system is not
+ * subjected to heavy I/O activity and all data is in-memory. There is a single
+ * global mutex for each mount (cg_contents). This mutex is held for the life
+ * of most vnode operations. The most active path is probably the LWP start and
+ * exit hooks which increment/decrement the reference counter on the cgroup
+ * node. The lock is important for this case since we don't want concurrent
+ * activity (such as moving the process into another cgroup) while we're trying
+ * to lookup the cgroup from the mount's hash table. We must be careful to
+ * avoid a deadlock while reading or writing since that code can take pidlock
+ * and p_lock, but the cgrp_lwp_fork_helper can also be called while one of
+ * those is held. To prevent deadlock we always take cg_contents after pidlock
+ * and p_lock.
+ *
+ * EXTENDING THE FILE SYSTEM
+ *
+ * When adding support for a new subsystem, be sure to also update the
+ * lxpr_read_cgroups function in lx_procfs so that the subsystem is reported
+ * by proc.
+ *
+ * Although we don't currently support any subsystem controllers, the design
+ * allows for the file system to be extended to add controller emulation
+ * if needed. New controller IDs (i.e. different subsystems) for a mount can
+ * be defined in the cgrp_ssid_t enum (e.g. CG_SSID_CPUSET or CG_SSID_MEMORY)
+ * and new node types for additional pseudo files in the tree can be defined in
+ * the cgrp_nodetype_t enum (e.g. CG_CPUSET_CPUS or CG_MEMORY_USAGE_IN_BYTES).
+ * The cg_ssde_dir array would need a new entry for the new subsystem to
+ * control which nodes are visible in a directory for the new subsystem.
+ *
+ * New emulation would then need to be written to manage the behavior on the
+ * new pseudo file(s) associated with new cgrp_nodetype_t types.
+ *
+ * Within lx procfs the lxpr_read_pid_cgroup() function would need to be
+ * updated so that it reported the various subsystems used by the different
+ * mounts.
+ *
+ * In addition, in order to support more than one cgroup mount we would need a
+ * list of cgroup IDs associated with every thread, instead of just one ID
+ * (br_cgroupid). The thread data would need to become a struct which held
+ * both an ID and an indication as to which mounted cgroup file system instance
+ * the ID was associated with. We would also need a list of cgroup mounts per
+ * zone, instead the current single zone reference.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/time.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/statvfs.h>
+#include <sys/mount.h>
+#include <sys/systm.h>
+#include <sys/mntent.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+#include <sys/vmparam.h>
+#include <sys/corectl.h>
+#include <sys/contract_impl.h>
+#include <sys/pool.h>
+#include <sys/stack.h>
+#include <sys/rt.h>
+#include <sys/fx.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+#include "cgrps.h"
+
+/* Module level parameters */
+static int cgrp_fstype;
+static dev_t cgrp_dev;
+
+#define MAX_AGENT_EVENTS 32 /* max num queued events */
+
+#define UMNT_DELAY_TIME drv_usectohz(50000) /* 500th of a second */
+#define UMNT_RETRY_MAX 100 /* 100 times - 2 secs */
+
+/*
+ * cgrp_mountcount is used to prevent module unloads while there is still
+ * state from a former mount hanging around. The filesystem module must not be
+ * allowed to go away before the last VFS_FREEVFS() call has been made. Since
+ * this is just an atomic counter, there's no need for locking.
+ */
+static uint32_t cgrp_mountcount;
+
+/*
+ * cgrp_minfree is the minimum amount of swap space that cgroups leaves for
+ * the rest of the zone. In other words, if the amount of free swap space
+ * in the zone drops below cgrp_minfree, cgroup anon allocations will fail.
+ * This number is only likely to become factor when DRAM and swap have both
+ * been capped low to allow for maximum tenancy.
+ */
+size_t cgrp_minfree = 0;
+
+/*
+ * CGMINFREE -- the value from which cgrp_minfree is derived -- should be
+ * configured to a value that is roughly the smallest practical value for
+ * memory + swap minus the largest reasonable size for cgroups in such
+ * a configuration. As of this writing, the smallest practical memory + swap
+ * configuration is 128MB, and it seems reasonable to allow cgroups to consume
+ * no more than half of this, yielding a CGMINFREE of 64MB.
+ */
+#define CGMINFREE 64 * 1024 * 1024 /* 64 Megabytes */
+
+extern pgcnt_t swapfs_minfree;
+
+/*
+ * cgroup vfs operations.
+ */
+static int cgrp_init(int, char *);
+static int cgrp_mount(struct vfs *, struct vnode *,
+ struct mounta *, struct cred *);
+static int cgrp_unmount(struct vfs *, int, struct cred *);
+static int cgrp_root(struct vfs *, struct vnode **);
+static int cgrp_statvfs(struct vfs *, struct statvfs64 *);
+static void cgrp_freevfs(vfs_t *vfsp);
+
+/* Forward declarations for hooks */
+static void cgrp_lwp_fork_helper(vfs_t *, uint_t, id_t, pid_t);
+static void cgrp_lwp_exit_helper(vfs_t *, uint_t, id_t, pid_t);
+
+/*
+ * Loadable module wrapper
+ */
+#include <sys/modctl.h>
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "lx_cgroup",
+ cgrp_init,
+ VSW_ZMOUNT,
+ NULL
+};
+
+/*
+ * Module linkage information
+ */
+static struct modlfs modlfs = {
+ &mod_fsops, "lx brand cgroups", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modlfs, NULL
+};
+
+int
+_init()
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini()
+{
+ int error;
+
+ if (cgrp_mountcount)
+ return (EBUSY);
+
+ if ((error = mod_remove(&modlinkage)) != 0)
+ return (error);
+
+ /* Disable hooks used by the lx brand module. */
+ lx_cgrp_initlwp = NULL;
+ lx_cgrp_freelwp = NULL;
+
+ /*
+ * Tear down the operations vectors
+ */
+ (void) vfs_freevfsops_by_type(cgrp_fstype);
+ vn_freevnodeops(cgrp_vnodeops);
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * Initialize global locks, etc. Called when loading cgroup module.
+ */
+static int
+cgrp_init(int fstype, char *name)
+{
+ static const fs_operation_def_t cgrp_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = cgrp_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = cgrp_unmount },
+ VFSNAME_ROOT, { .vfs_root = cgrp_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = cgrp_statvfs },
+ VFSNAME_FREEVFS, { .vfs_freevfs = cgrp_freevfs },
+ NULL, NULL
+ };
+ extern const struct fs_operation_def cgrp_vnodeops_template[];
+ int error;
+ extern void cgrp_hash_init();
+ major_t dev;
+
+ cgrp_hash_init();
+ cgrp_fstype = fstype;
+ ASSERT(cgrp_fstype != 0);
+
+ error = vfs_setfsops(fstype, cgrp_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "cgrp_init: bad vfs ops template");
+ return (error);
+ }
+
+ error = vn_make_ops(name, cgrp_vnodeops_template, &cgrp_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "cgrp_init: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * cgrp_minfree doesn't need to be some function of configured
+ * swap space since it really is an absolute limit of swap space
+ * which still allows other processes to execute.
+ */
+ if (cgrp_minfree == 0) {
+ /* Set if not patched */
+ cgrp_minfree = btopr(CGMINFREE);
+ }
+
+ if ((dev = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "cgrp_init: Can't get unique device number.");
+ dev = 0;
+ }
+
+ /*
+ * Make the pseudo device
+ */
+ cgrp_dev = makedevice(dev, 0);
+
+ /* Install the hooks used by the lx brand module. */
+ lx_cgrp_initlwp = cgrp_lwp_fork_helper;
+ lx_cgrp_freelwp = cgrp_lwp_exit_helper;
+
+ return (0);
+}
+
+static int
+cgrp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+ cgrp_mnt_t *cgm = NULL;
+ struct cgrp_node *cp;
+ struct pathname dpn;
+ int error;
+ struct vattr rattr;
+ cgrp_ssid_t ssid = CG_SSID_GENERIC;
+ lx_zone_data_t *lxzdata;
+
+ if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
+ return (error);
+
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /*
+ * Since we depend on per-thread lx brand data, only allow mounting
+ * within lx zones.
+ */
+ if (curproc->p_zone->zone_brand != &lx_brand)
+ return (EINVAL);
+
+ /*
+ * Ensure we don't allow overlaying mounts
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /*
+ * Having the resource be anything but "swap" doesn't make sense.
+ */
+ vfs_setresource(vfsp, "swap", 0);
+
+ /* cgroups don't support read-only mounts */
+ if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * Here is where we could support subsystem-specific controller
+ * mounting. For example, if mounting a cgroup fs with the 'cpuset'
+ * option to specify that particular controller.
+ *
+ * char *argstr;
+ * if (vfs_optionisset(vfsp, "cpuset", &argstr)) {
+ * if (ssid != CG_SSID_GENERIC) {
+ * error = EINVAL;
+ * goto out;
+ * }
+ * ssid = CG_SSID_CPUSET;
+ * }
+ */
+
+ error = pn_get(uap->dir,
+ (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn);
+ if (error != 0)
+ goto out;
+
+ /*
+ * We currently only support one mount per zone.
+ */
+ lxzdata = ztolxzd(curproc->p_zone);
+ mutex_enter(&lxzdata->lxzd_lock);
+ if (lxzdata->lxzd_cgroup != NULL) {
+ mutex_exit(&lxzdata->lxzd_lock);
+ return (EINVAL);
+ }
+
+ cgm = kmem_zalloc(sizeof (*cgm), KM_SLEEP);
+
+ /* Set but don't bother entering the mutex (not on mount list yet) */
+ mutex_init(&cgm->cg_contents, NULL, MUTEX_DEFAULT, NULL);
+
+ cgm->cg_vfsp = lxzdata->lxzd_cgroup = vfsp;
+ mutex_exit(&lxzdata->lxzd_lock);
+
+ cgm->cg_lxzdata = lxzdata;
+ cgm->cg_ssid = ssid;
+
+ vfsp->vfs_data = (caddr_t)cgm;
+ vfsp->vfs_fstype = cgrp_fstype;
+ vfsp->vfs_dev = cgrp_dev;
+ vfsp->vfs_bsize = PAGESIZE;
+ vfsp->vfs_flag |= VFS_NOTRUNC;
+ vfs_make_fsid(&vfsp->vfs_fsid, cgrp_dev, cgrp_fstype);
+ cgm->cg_mntpath = kmem_zalloc(dpn.pn_pathlen + 1, KM_SLEEP);
+ (void) strcpy(cgm->cg_mntpath, dpn.pn_path);
+
+ cgm->cg_grp_hash = kmem_zalloc(sizeof (cgrp_node_t *) * CGRP_HASH_SZ,
+ KM_SLEEP);
+
+ /* allocate and initialize root cgrp_node structure */
+ bzero(&rattr, sizeof (struct vattr));
+ rattr.va_mode = (mode_t)(S_IFDIR | 0755);
+ rattr.va_type = VDIR;
+ rattr.va_rdev = 0;
+ cp = kmem_zalloc(sizeof (struct cgrp_node), KM_SLEEP);
+
+ mutex_enter(&cgm->cg_contents);
+ cgrp_node_init(cgm, cp, &rattr, cr);
+
+ CGNTOV(cp)->v_flag |= VROOT;
+
+ /*
+ * initialize linked list of cgrp_nodes so that the back pointer of
+ * the root cgrp_node always points to the last one on the list
+ * and the forward pointer of the last node is null
+ */
+ cp->cgn_back = cp;
+ cp->cgn_forw = NULL;
+ cp->cgn_nlink = 0;
+ cgm->cg_rootnode = cp;
+
+ cp->cgn_type = CG_CGROUP_DIR;
+ cp->cgn_nodeid = cgrp_inode(CG_CGROUP_DIR, cgm->cg_gen);
+
+ /*
+ * This initial cgrp_node will have an ID of 0. All existing processes
+ * inside the zone will have been started with, or inherited, a
+ * br_cgroupid of 0. The cgrp_cg_hash_init function will initialize the
+ * cgn_task_cnt for cgroup 0 to reflect the number of tasks already in
+ * the group.
+ *
+ * Because we must hold cg_contents in cgrp_lwp_fork_helper and
+ * cgrp_lwp_exit_helper, no process can be creating or exiting another
+ * thread (although that is unlikely anyway since the cgroup filesystem
+ * is normally mounted at the start of zone bootup, before anything
+ * else is started).
+ */
+ cgrp_dirinit(cp, cp, cr);
+
+ mutex_exit(&cgm->cg_contents);
+
+ pn_free(&dpn);
+ error = 0;
+ atomic_inc_32(&cgrp_mountcount);
+
+out:
+ if (error == 0)
+ vfs_set_feature(vfsp, VFSFT_SYSATTR_VIEWS);
+
+ return (error);
+}
+
+static int
+cgrp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ cgrp_node_t *cgnp, *cancel;
+ struct vnode *vp;
+ int error;
+ uint_t cnt;
+ int retry_cnt = 0;
+
+ if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
+ return (error);
+
+retry:
+ mutex_enter(&cgm->cg_contents);
+
+ /*
+ * In the normal unmount case, if there were no open files, only the
+ * root node would have a reference count. However, the user-level
+ * agent manager should have the root vnode open and be waiting in
+ * ioctl. We need to wake the manager and it may take some retries
+ * before it closes its file descriptor.
+ *
+ * With cg_contents held, nothing can be added or removed.
+ * There may be some dirty pages. To prevent fsflush from
+ * disrupting the unmount, put a hold on each node while scanning.
+ * If we find a previously referenced node, undo the holds we have
+ * placed and fail EBUSY.
+ */
+ cgnp = cgm->cg_rootnode;
+
+ ASSERT(cgm->cg_lxzdata->lxzd_cgroup != NULL);
+
+ vp = CGNTOV(cgnp);
+ mutex_enter(&vp->v_lock);
+
+ if (flag & MS_FORCE) {
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&cgm->cg_contents);
+ return (EINVAL);
+ }
+
+
+ cnt = vp->v_count;
+ if (cnt > 1) {
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&cgm->cg_contents);
+ /* Likely because the user-level manager hasn't exited yet */
+ if (retry_cnt++ < UMNT_RETRY_MAX) {
+ delay(UMNT_DELAY_TIME);
+ goto retry;
+ }
+ return (EBUSY);
+ }
+
+ mutex_exit(&vp->v_lock);
+
+ /*
+ * Check for open files. An open file causes everything to unwind.
+ */
+ for (cgnp = cgnp->cgn_forw; cgnp; cgnp = cgnp->cgn_forw) {
+ vp = CGNTOV(cgnp);
+ mutex_enter(&vp->v_lock);
+ cnt = vp->v_count;
+ if (cnt > 0) {
+ /* An open file; unwind the holds we've been adding. */
+ mutex_exit(&vp->v_lock);
+ cancel = cgm->cg_rootnode->cgn_forw;
+ while (cancel != cgnp) {
+ vp = CGNTOV(cancel);
+ ASSERT(vp->v_count > 0);
+ VN_RELE(vp);
+ cancel = cancel->cgn_forw;
+ }
+ mutex_exit(&cgm->cg_contents);
+ return (EBUSY);
+ } else {
+ /* directly add a VN_HOLD since we have the lock */
+ vp->v_count++;
+ mutex_exit(&vp->v_lock);
+ }
+ }
+
+ mutex_enter(&cgm->cg_lxzdata->lxzd_lock);
+ cgm->cg_lxzdata->lxzd_cgroup = NULL;
+ mutex_exit(&cgm->cg_lxzdata->lxzd_lock);
+ kmem_free(cgm->cg_grp_hash, sizeof (cgrp_node_t *) * CGRP_HASH_SZ);
+
+ /*
+ * We can drop the mutex now because
+ * no one can find this mount anymore
+ */
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+ mutex_exit(&cgm->cg_contents);
+
+ return (0);
+}
+
+/*
+ * Implementation of VFS_FREEVFS(). This is called by the vfs framework after
+ * umount and the last VFS_RELE, to trigger the release of any resources still
+ * associated with the given vfs_t. This is normally called immediately after
+ * cgrp_umount.
+ */
+void
+cgrp_freevfs(vfs_t *vfsp)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ cgrp_node_t *cn;
+ struct vnode *vp;
+
+ /*
+ * Free all kmemalloc'd and anonalloc'd memory associated with
+ * this filesystem. To do this, we go through the file list twice,
+ * once to remove all the directory entries, and then to remove
+ * all the pseudo files.
+ */
+
+ /*
+ * Now that we are tearing ourselves down we need to remove the
+ * UNMOUNTED flag. If we don't, we'll later hit a VN_RELE when we remove
+ * files from the system causing us to have a negative value. Doing this
+ * seems a bit better than trying to set a flag on the tmount that says
+ * we're tearing down.
+ */
+ vfsp->vfs_flag &= ~VFS_UNMOUNTED;
+
+ /*
+ * Remove all directory entries
+ */
+ for (cn = cgm->cg_rootnode; cn; cn = cn->cgn_forw) {
+ mutex_enter(&cgm->cg_contents);
+ if (cn->cgn_type == CG_CGROUP_DIR)
+ cgrp_dirtrunc(cn);
+ mutex_exit(&cgm->cg_contents);
+ }
+
+ ASSERT(cgm->cg_rootnode);
+
+ /*
+ * All links are gone, v_count is keeping nodes in place.
+ * VN_RELE should make the node disappear, unless somebody
+ * is holding pages against it. Nap and retry until it disappears.
+ *
+ * We re-acquire the lock to prevent others who have a HOLD on
+ * a cgrp_node via its pages or anon slots from blowing it away
+ * (in cgrp_inactive) while we're trying to get to it here. Once
+ * we have a HOLD on it we know it'll stick around.
+ *
+ */
+ mutex_enter(&cgm->cg_contents);
+
+ /* Remove all the files (except the rootnode) backwards. */
+ while ((cn = cgm->cg_rootnode->cgn_back) != cgm->cg_rootnode) {
+ mutex_exit(&cgm->cg_contents);
+ /*
+ * All nodes will be released here. Note we handled the link
+ * count above.
+ */
+ vp = CGNTOV(cn);
+ VN_RELE(vp);
+ mutex_enter(&cgm->cg_contents);
+ /*
+ * It's still there after the RELE. Someone else like pageout
+ * has a hold on it so wait a bit and then try again - we know
+ * they'll give it up soon.
+ */
+ if (cn == cgm->cg_rootnode->cgn_back) {
+ VN_HOLD(vp);
+ mutex_exit(&cgm->cg_contents);
+ delay(hz / 4);
+ mutex_enter(&cgm->cg_contents);
+ }
+ }
+ mutex_exit(&cgm->cg_contents);
+
+ VN_RELE(CGNTOV(cgm->cg_rootnode));
+
+ ASSERT(cgm->cg_mntpath);
+
+ kmem_free(cgm->cg_mntpath, strlen(cgm->cg_mntpath) + 1);
+
+ mutex_destroy(&cgm->cg_contents);
+ kmem_free(cgm, sizeof (cgrp_mnt_t));
+
+ /* Allow _fini() to succeed now */
+ atomic_dec_32(&cgrp_mountcount);
+}
+
+/*
+ * return root cgnode for given vnode
+ */
+static int
+cgrp_root(struct vfs *vfsp, struct vnode **vpp)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ cgrp_node_t *cp = cgm->cg_rootnode;
+ struct vnode *vp;
+
+ ASSERT(cp);
+
+ vp = CGNTOV(cp);
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+cgrp_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ ulong_t blocks;
+ dev32_t d32;
+ zoneid_t eff_zid;
+ struct zone *zp;
+
+ zp = cgm->cg_vfsp->vfs_zone;
+
+ if (zp == NULL)
+ eff_zid = GLOBAL_ZONEUNIQID;
+ else
+ eff_zid = zp->zone_id;
+
+ sbp->f_bsize = PAGESIZE;
+ sbp->f_frsize = PAGESIZE;
+
+ /*
+ * Find the amount of available physical and memory swap
+ */
+ mutex_enter(&anoninfo_lock);
+ ASSERT(k_anoninfo.ani_max >= k_anoninfo.ani_phys_resv);
+ blocks = (ulong_t)CURRENT_TOTAL_AVAILABLE_SWAP;
+ mutex_exit(&anoninfo_lock);
+
+ if (blocks > cgrp_minfree)
+ sbp->f_bfree = blocks - cgrp_minfree;
+ else
+ sbp->f_bfree = 0;
+
+ sbp->f_bavail = sbp->f_bfree;
+
+ /*
+ * Total number of blocks is just what's available
+ */
+ sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree);
+
+ if (eff_zid != GLOBAL_ZONEUNIQID &&
+ zp->zone_max_swap_ctl != UINT64_MAX) {
+ /*
+ * If the fs is used by a zone with a swap cap,
+ * then report the capped size.
+ */
+ rctl_qty_t cap, used;
+ pgcnt_t pgcap, pgused;
+
+ mutex_enter(&zp->zone_mem_lock);
+ cap = zp->zone_max_swap_ctl;
+ used = zp->zone_max_swap;
+ mutex_exit(&zp->zone_mem_lock);
+
+ pgcap = btop(cap);
+ pgused = btop(used);
+
+ sbp->f_bfree = MIN(pgcap - pgused, sbp->f_bfree);
+ sbp->f_bavail = sbp->f_bfree;
+ sbp->f_blocks = MIN(pgcap, sbp->f_blocks);
+ }
+
+ /*
+ * The maximum number of files available is approximately the number
+ * of cgrp_nodes we can allocate from the remaining kernel memory
+ * available to cgroups. This is fairly inaccurate since it doesn't
+ * take into account the names stored in the directory entries.
+ */
+ sbp->f_ffree = sbp->f_files = ptob(availrmem) /
+ (sizeof (cgrp_node_t) + sizeof (cgrp_dirent_t));
+ sbp->f_favail = (fsfilcnt64_t)(sbp->f_ffree);
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sbp->f_fsid = d32;
+ (void) strcpy(sbp->f_basetype, vfssw[cgrp_fstype].vsw_name);
+ (void) strncpy(sbp->f_fstr, cgm->cg_mntpath, sizeof (sbp->f_fstr));
+ /* ensure null termination */
+ sbp->f_fstr[sizeof (sbp->f_fstr) - 1] = '\0';
+ sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sbp->f_namemax = MAXNAMELEN - 1;
+ return (0);
+}
+
+static int
+cgrp_get_dirname(cgrp_node_t *cn, char *buf, int blen)
+{
+ cgrp_node_t *parent;
+ cgrp_dirent_t *dp;
+
+ buf[0] = '\0';
+
+ parent = cn->cgn_parent;
+ if (parent == NULL || parent == cn) {
+ (void) strlcpy(buf, ".", blen);
+ return (0);
+ }
+
+ /*
+ * Search the parent dir list to find this cn's name.
+ */
+ for (dp = parent->cgn_dir; dp != NULL; dp = dp->cgd_next) {
+ if (dp->cgd_cgrp_node->cgn_id == cn->cgn_id) {
+ (void) strlcpy(buf, dp->cgd_name, blen);
+ return (0);
+ }
+ }
+
+ return (-1);
+}
+
+typedef struct cgrp_rra_arg {
+ char *crraa_agent_path;
+ char *crraa_event_path;
+} cgrp_rra_arg_t;
+
+static void
+cgrp_run_rel_agent(void *a)
+{
+ cgrp_rra_arg_t *rarg = a;
+ proc_t *p = ttoproc(curthread);
+ zone_t *z = p->p_zone;
+ struct core_globals *cg;
+ int res;
+
+ ASSERT(!INGLOBALZONE(curproc));
+
+ /* The following block is derived from start_init_common */
+ ASSERT_STACK_ALIGNED();
+
+ p->p_cstime = p->p_stime = p->p_cutime = p->p_utime = 0;
+ p->p_usrstack = (caddr_t)USRSTACK32;
+ p->p_model = DATAMODEL_ILP32;
+ p->p_stkprot = PROT_ZFOD & ~PROT_EXEC;
+ p->p_datprot = PROT_ZFOD & ~PROT_EXEC;
+ p->p_stk_ctl = INT32_MAX;
+
+ p->p_as = as_alloc();
+ p->p_as->a_proc = p;
+ p->p_as->a_userlimit = (caddr_t)USERLIMIT32;
+ (void) hat_setup(p->p_as->a_hat, HAT_INIT);
+
+ VERIFY((cg = zone_getspecific(core_zone_key, z)) != NULL);
+
+ corectl_path_hold(cg->core_default_path);
+ corectl_content_hold(cg->core_default_content);
+
+ curproc->p_corefile = cg->core_default_path;
+ curproc->p_content = cg->core_default_content;
+
+ init_mstate(curthread, LMS_SYSTEM);
+ res = exec_init(rarg->crraa_agent_path, rarg->crraa_event_path);
+
+ /* End of code derived from start_init_common */
+
+ kmem_free(rarg->crraa_event_path, MAXPATHLEN);
+ kmem_free(rarg->crraa_agent_path, CGRP_AGENT_LEN);
+ kmem_free(rarg, sizeof (cgrp_rra_arg_t));
+
+ /* The following is derived from zone_start_init - see comments there */
+ if (res != 0 || zone_status_get(global_zone) >= ZONE_IS_SHUTTING_DOWN) {
+ if (proc_exit(CLD_EXITED, res) != 0) {
+ mutex_enter(&p->p_lock);
+ ASSERT(p->p_flag & SEXITLWPS);
+ lwp_exit();
+ }
+ } else {
+ id_t cid = curthread->t_cid;
+
+ mutex_enter(&class_lock);
+ ASSERT(cid < loaded_classes);
+ if (strcmp(sclass[cid].cl_name, "FX") == 0 &&
+ z->zone_fixed_hipri) {
+ pcparms_t pcparms;
+
+ pcparms.pc_cid = cid;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_upri = FXMAXUPRI;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_uprilim =
+ FXMAXUPRI;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_cflags =
+ FX_DOUPRILIM | FX_DOUPRI;
+
+ mutex_enter(&pidlock);
+ mutex_enter(&curproc->p_lock);
+ (void) parmsset(&pcparms, curthread);
+ mutex_exit(&curproc->p_lock);
+ mutex_exit(&pidlock);
+ } else if (strcmp(sclass[cid].cl_name, "RT") == 0) {
+ curthread->t_pri = RTGPPRIO0;
+ }
+ mutex_exit(&class_lock);
+
+ /* cause the process to return to userland. */
+ lwp_rtt();
+ }
+}
+
+/*
+ * Launch the user-level release_agent manager. The event data is the
+ * pathname (relative to the mount point of the file system) of the newly empty
+ * cgroup.
+ *
+ * The cg_contents mutex is held on entry and dropped before returning.
+ */
+void
+cgrp_rel_agent_event(cgrp_mnt_t *cgm, cgrp_node_t *cn, boolean_t is_exit)
+{
+ cgrp_node_t *parent;
+ char nm[MAXNAMELEN];
+ char *argstr, *oldstr, *tmp;
+ id_t cid;
+ proc_t *p = ttoproc(curthread);
+ zone_t *z = p->p_zone;
+ lx_lwp_data_t *plwpd = ttolxlwp(curthread);
+ cgrp_rra_arg_t *rarg;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+ /* Nothing to do if the agent is not set */
+ if (cgm->cg_agent[0] == '\0') {
+ mutex_exit(&cgm->cg_contents);
+ return;
+ }
+
+ parent = cn->cgn_parent;
+ /* Cannot remove the top-level cgroup (only via unmount) */
+ if (parent == cn) {
+ mutex_exit(&cgm->cg_contents);
+ return;
+ }
+
+ argstr = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+ oldstr = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+ *argstr = '\0';
+
+ /*
+ * Iterate up the directory tree to construct the agent argument string.
+ */
+ do {
+ VERIFY0(cgrp_get_dirname(cn, nm, sizeof (nm)));
+ DTRACE_PROBE1(cgrp__dir__name, char *, nm);
+ if (*argstr == '\0') {
+ (void) snprintf(argstr, MAXPATHLEN, "/%s", nm);
+ } else {
+ tmp = oldstr;
+ oldstr = argstr;
+ argstr = tmp;
+ (void) snprintf(argstr, MAXPATHLEN, "/%s%s", nm,
+ oldstr);
+ }
+
+ if (cn->cgn_parent == NULL)
+ break;
+ cn = cn->cgn_parent;
+ parent = cn->cgn_parent;
+
+ /*
+ * The arg path is relative to the mountpoint so we stop when
+ * we get to the top level.
+ */
+ if (parent == NULL || parent == cn)
+ break;
+ } while (parent != cn);
+
+ kmem_free(oldstr, MAXPATHLEN);
+
+ rarg = kmem_alloc(sizeof (cgrp_rra_arg_t), KM_SLEEP);
+ rarg->crraa_agent_path = kmem_alloc(sizeof (cgm->cg_agent), KM_SLEEP);
+ (void) strlcpy(rarg->crraa_agent_path, cgm->cg_agent,
+ sizeof (cgm->cg_agent));
+ rarg->crraa_event_path = argstr;
+
+ DTRACE_PROBE2(cgrp__agent__event, cgrp_rra_arg_t *, rarg,
+ int, plwpd->br_cgroupid);
+
+ /*
+ * When we're exiting, the release agent process cannot belong to our
+ * cgroup. When the release agent is called for a move or rmdir, then
+ * we do not change our cgroupid.
+ */
+ if (is_exit) {
+ plwpd->br_cgroupid = 0;
+ }
+
+ /*
+ * The cg_contents mutex cannot be held while taking the pool lock
+ * or calling newproc.
+ */
+ mutex_exit(&cgm->cg_contents);
+
+ if (z->zone_defaultcid > 0) {
+ cid = z->zone_defaultcid;
+ } else {
+ pool_lock();
+ cid = pool_get_class(z->zone_pool);
+ pool_unlock();
+ }
+ if (cid == -1)
+ cid = defaultcid;
+
+ if (newproc(cgrp_run_rel_agent, (void *)rarg, cid, minclsyspri - 1,
+ NULL, -1) != 0) {
+ /* There's nothing we can do if creating the proc fails. */
+ kmem_free(rarg->crraa_event_path, MAXPATHLEN);
+ kmem_free(rarg->crraa_agent_path, sizeof (cgm->cg_agent));
+ kmem_free(rarg, sizeof (cgrp_rra_arg_t));
+ }
+}
+
+/*ARGSUSED*/
+static void
+cgrp_lwp_fork_helper(vfs_t *vfsp, uint_t cg_id, id_t tid, pid_t tpid)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ cgrp_node_t *cn;
+
+ mutex_enter(&cgm->cg_contents);
+ cn = cgrp_cg_hash_lookup(cgm, cg_id);
+ ASSERT(cn != NULL);
+ cn->cgn_task_cnt++;
+ mutex_exit(&cgm->cg_contents);
+
+ DTRACE_PROBE1(cgrp__lwp__fork, void *, cn);
+}
+
+/*ARGSUSED*/
+static void
+cgrp_lwp_exit_helper(vfs_t *vfsp, uint_t cg_id, id_t tid, pid_t tpid)
+{
+ cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+ cgrp_node_t *cn;
+
+ mutex_enter(&cgm->cg_contents);
+ cn = cgrp_cg_hash_lookup(cgm, cg_id);
+ ASSERT(cn != NULL);
+ if (cn->cgn_task_cnt == 0) {
+ /* top-level cgroup cnt can be 0 during reboot */
+ mutex_exit(&cgm->cg_contents);
+ return;
+ }
+ cn->cgn_task_cnt--;
+ DTRACE_PROBE1(cgrp__lwp__exit, void *, cn);
+
+ if (cn->cgn_task_cnt == 0 && cn->cgn_dirents == N_DIRENTS(cgm) &&
+ cn->cgn_notify == 1) {
+ cgrp_rel_agent_event(cgm, cn, B_TRUE);
+ ASSERT(MUTEX_NOT_HELD(&cgm->cg_contents));
+ } else {
+ mutex_exit(&cgm->cg_contents);
+ }
+}
diff --git a/usr/src/uts/common/brand/lx/cgroups/cgrps_vnops.c b/usr/src/uts/common/brand/lx/cgroups/cgrps_vnops.c
new file mode 100644
index 0000000000..0078ad7876
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/cgroups/cgrps_vnops.c
@@ -0,0 +1,1552 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/dirent.h>
+#include <sys/pathname.h>
+#include <vm/seg_vn.h>
+#include <sys/cmn_err.h>
+#include <sys/buf.h>
+#include <sys/vm.h>
+#include <sys/prsystm.h>
+#include <sys/policy.h>
+#include <fs/fs_subr.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+#include "cgrps.h"
+
+typedef enum cgrp_wr_type {
+ CG_WR_PROCS = 1,
+ CG_WR_TASKS
+} cgrp_wr_type_t;
+
+/* ARGSUSED1 */
+static int
+cgrp_open(struct vnode **vpp, int flag, struct cred *cred, caller_context_t *ct)
+{
+ /*
+ * swapon to a cgrp file is not supported so access is denied on open
+ * if VISSWAP is set.
+ */
+ if ((*vpp)->v_flag & VISSWAP)
+ return (EINVAL);
+
+ return (0);
+}
+
+/* ARGSUSED1 */
+static int
+cgrp_close(struct vnode *vp, int flag, int count, offset_t offset,
+ struct cred *cred, caller_context_t *ct)
+{
+ cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
+ cleanshares(vp, ttoproc(curthread)->p_pid);
+ return (0);
+}
+
+/*
+ * Lookup proc or task based on pid and typ.
+ */
+static proc_t *
+cgrp_p_for_wr(pid_t pid, cgrp_wr_type_t typ)
+{
+ int i;
+ zoneid_t zoneid = curproc->p_zone->zone_id;
+ pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+
+ ASSERT(MUTEX_HELD(&pidlock));
+
+ /* getting a proc from a pid is easy */
+ if (typ == CG_WR_PROCS)
+ return (prfind(pid));
+
+ ASSERT(typ == CG_WR_TASKS);
+
+ /*
+ * We have to scan all of the process entries to find the proc
+ * containing this task.
+ */
+ mutex_exit(&pidlock);
+ for (i = 1; i < v.v_proc; i++) {
+ proc_t *p;
+ kthread_t *t;
+
+ mutex_enter(&pidlock);
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, system processes,
+ * a PID of 0, the pid for our zsched process, anything the
+ * security policy doesn't allow us to look at, its not an
+ * lx-branded process and processes that are not in the zone.
+ */
+ if ((p = pid_entry(i)) == NULL ||
+ p->p_stat == SIDL ||
+ (p->p_flag & SSYS) != 0 ||
+ p->p_pid == 0 ||
+ p->p_pid == schedpid ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+ p->p_brand != &lx_brand ||
+ p->p_zone->zone_id != zoneid) {
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ mutex_enter(&p->p_lock);
+ if ((t = p->p_tlist) == NULL) {
+ /* no threads, skip it */
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ /*
+ * Check all threads in this proc.
+ */
+ do {
+ lx_lwp_data_t *plwpd = ttolxlwp(t);
+ if (plwpd != NULL && plwpd->br_pid == pid) {
+ mutex_exit(&p->p_lock);
+ return (p);
+ }
+
+ t = t->t_forw;
+ } while (t != p->p_tlist);
+
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+ }
+
+ mutex_enter(&pidlock);
+ return (NULL);
+}
+
+/*
+ * Move a thread from one cgroup to another. If the old cgroup is empty
+ * we queue up an agent event. We return true in that case since we've
+ * dropped the locks and the caller needs to reacquire them.
+ */
+static boolean_t
+cgrp_thr_move(cgrp_mnt_t *cgm, lx_lwp_data_t *plwpd, cgrp_node_t *ncn,
+ uint_t cg_id, proc_t *p)
+{
+ cgrp_node_t *ocn;
+
+ ASSERT(MUTEX_HELD(&cgm->cg_contents));
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ ocn = cgrp_cg_hash_lookup(cgm, plwpd->br_cgroupid);
+ VERIFY(ocn != NULL);
+
+ ASSERT(ocn->cgn_task_cnt > 0);
+ atomic_dec_32(&ocn->cgn_task_cnt);
+ atomic_inc_32(&ncn->cgn_task_cnt);
+ plwpd->br_cgroupid = cg_id;
+
+ if (ocn->cgn_task_cnt == 0 && ocn->cgn_dirents == N_DIRENTS(cgm) &&
+ ocn->cgn_notify == 1) {
+ /*
+ * We want to drop p_lock before queuing the event since
+ * that might sleep. Dropping p_lock might cause the caller to
+ * have to restart the move process from the beginning.
+ */
+ mutex_exit(&p->p_lock);
+ cgrp_rel_agent_event(cgm, ocn, B_FALSE);
+ ASSERT(MUTEX_NOT_HELD(&cgm->cg_contents));
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Assign either all of the threads, or a single thread, for the specified pid
+ * to the new cgroup. Controlled by the typ argument.
+ */
+static int
+cgrp_proc_set_id(cgrp_mnt_t *cgm, uint_t cg_id, pid_t pid, cgrp_wr_type_t typ)
+{
+ proc_t *p;
+ kthread_t *t;
+ int error;
+ cgrp_node_t *ncn;
+
+ if (pid == 1)
+ pid = curproc->p_zone->zone_proc_initpid;
+
+ /*
+ * Move one or all threads to this cgroup.
+ */
+ if (typ == CG_WR_TASKS) {
+ error = ESRCH;
+ } else {
+ error = 0;
+ }
+
+restart:
+ mutex_enter(&pidlock);
+
+ p = cgrp_p_for_wr(pid, typ);
+ if (p == NULL) {
+ mutex_exit(&pidlock);
+ return (ESRCH);
+ }
+
+ /*
+ * Fail writes for pids for which there is no corresponding process,
+ * system processes, a pid of 0, the pid for our zsched process,
+ * anything the security policy doesn't allow us to look at, and
+ * processes that are not in the zone.
+ */
+ if (p->p_stat == SIDL ||
+ (p->p_flag & SSYS) != 0 ||
+ p->p_pid == 0 ||
+ p->p_pid == curproc->p_zone->zone_zsched->p_pid ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+ p->p_zone->zone_id != curproc->p_zone->zone_id) {
+ mutex_exit(&pidlock);
+ return (ESRCH);
+ }
+
+ /*
+ * Ignore writes for PID which is not an lx-branded process or with
+ * no threads.
+ */
+
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+ if (p->p_brand != &lx_brand || (t = p->p_tlist) == NULL ||
+ p->p_flag & SEXITING) {
+ mutex_exit(&p->p_lock);
+ return (0);
+ }
+
+ mutex_enter(&cgm->cg_contents);
+
+ ncn = cgrp_cg_hash_lookup(cgm, cg_id);
+ VERIFY(ncn != NULL);
+
+ do {
+ lx_lwp_data_t *plwpd = ttolxlwp(t);
+ if (plwpd != NULL && plwpd->br_cgroupid != cg_id) {
+ if (typ == CG_WR_PROCS) {
+ if (cgrp_thr_move(cgm, plwpd, ncn, cg_id, p)) {
+ /*
+ * We dropped all of the locks so we
+ * need to start over.
+ */
+ goto restart;
+ }
+
+ } else if (plwpd->br_pid == pid) {
+ /* type is CG_WR_TASKS and we found the task */
+ error = 0;
+ if (cgrp_thr_move(cgm, plwpd, ncn, cg_id, p)) {
+ goto done;
+ } else {
+ break;
+ }
+ }
+ }
+ t = t->t_forw;
+ } while (t != p->p_tlist);
+
+ mutex_exit(&cgm->cg_contents);
+ mutex_exit(&p->p_lock);
+done:
+
+ return (error);
+}
+
+/*
+ * User-level is writing a pid string. We need to get that string and convert
+ * it to a pid. The user-level code has to completely write an entire pid
+ * string at once. The user-level code could write multiple strings (delimited
+ * by newline) although that is frowned upon. However, we must handle this
+ * case too. Thus we consume the input one byte at a time until we get a whole
+ * pid string. We can't consume more than a byte at a time since otherwise we
+ * might be left with a partial pid string.
+ */
+static int
+cgrp_get_pid_str(struct uio *uio, pid_t *pid)
+{
+ char buf[16]; /* big enough for a pid string */
+ int i;
+ int error;
+ char *p = &buf[0];
+ char *ep;
+ long pidnum;
+
+ bzero(buf, sizeof (buf));
+ for (i = 0; uio->uio_resid > 0 && i < sizeof (buf); i++, p++) {
+ error = uiomove(p, 1, UIO_WRITE, uio);
+ if (error != 0)
+ return (error);
+ if (buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+
+ if (buf[0] == '\0' || i >= sizeof (buf)) /* no input or too long */
+ return (EINVAL);
+
+ error = ddi_strtol(buf, &ep, 10, &pidnum);
+ if (error != 0 || *ep != '\0' || pidnum > maxpid || pidnum < 0)
+ return (EINVAL);
+
+ *pid = (pid_t)pidnum;
+ return (0);
+}
+
+static int
+cgrp_wr_notify(cgrp_node_t *cn, struct uio *uio)
+{
+ int error;
+ uint_t value;
+
+ /*
+ * This is cheesy but since we only take a 0 or 1 value we can
+ * let the pid_str function do the uio string conversion.
+ */
+ error = cgrp_get_pid_str(uio, (pid_t *)&value);
+ if (error != 0)
+ return (error);
+
+ if (value != 0 && value != 1)
+ return (EINVAL);
+
+ /*
+ * The flag is on the containing dir. We don't bother taking the
+ * cg_contents lock since this is a simple assignment.
+ */
+ cn->cgn_parent->cgn_notify = value;
+ return (0);
+}
+
+static int
+cgrp_wr_rel_agent(cgrp_mnt_t *cgm, struct uio *uio)
+{
+ int error;
+ int len;
+ char *wrp;
+
+ len = uio->uio_offset + uio->uio_resid;
+ if (len > MAXPATHLEN)
+ return (EFBIG);
+
+ mutex_enter(&cgm->cg_contents);
+
+ wrp = &cgm->cg_agent[uio->uio_offset];
+ error = uiomove(wrp, uio->uio_resid, UIO_WRITE, uio);
+ cgm->cg_agent[len] = '\0';
+ if (len > 1 && cgm->cg_agent[len - 1] == '\n')
+ cgm->cg_agent[len - 1] = '\0';
+
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+}
+
+static int
+cgrp_wr_proc_or_task(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio,
+ cgrp_wr_type_t typ)
+{
+ /* the cgroup ID is on the containing dir */
+ uint_t cg_id = cn->cgn_parent->cgn_id;
+ int error;
+ pid_t pidnum;
+
+ while (uio->uio_resid > 0) {
+ error = cgrp_get_pid_str(uio, &pidnum);
+ if (error != 0)
+ return (error);
+
+ error = cgrp_proc_set_id(cgm, cg_id, pidnum, typ);
+ if (error != 0)
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+cgrp_wr(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+ int error = 0;
+ rlim64_t limit = uio->uio_llimit;
+
+ ASSERT(CGNTOV(cn)->v_type == VREG);
+
+ if (uio->uio_loffset < 0)
+ return (EINVAL);
+
+ if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
+ limit = MAXOFFSET_T;
+
+ if (uio->uio_loffset >= MAXOFF_T)
+ return (EFBIG);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ if (limit > MAXOFF_T)
+ limit = MAXOFF_T;
+
+ switch (cn->cgn_type) {
+ case CG_NOTIFY:
+ error = cgrp_wr_notify(cn, uio);
+ break;
+ case CG_PROCS:
+ error = cgrp_wr_proc_or_task(cgm, cn, uio, CG_WR_PROCS);
+ break;
+ case CG_REL_AGENT:
+ error = cgrp_wr_rel_agent(cgm, uio);
+ break;
+ case CG_TASKS:
+ error = cgrp_wr_proc_or_task(cgm, cn, uio, CG_WR_TASKS);
+ break;
+ default:
+ VERIFY(0);
+ }
+
+ return (error);
+}
+
+/*
+ * Read value from the notify_on_release pseudo file on the parent node
+ * (which is the actual cgroup node). We don't bother taking the cg_contents
+ * lock since it's a single instruction so an empty group action/read will
+ * only see one value or the other.
+ */
+/* ARGSUSED */
+static int
+cgrp_rd_notify(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+ int len;
+ int error = 0;
+ char buf[16];
+ char *rdp;
+ /* the flag is on the containing dir */
+ uint_t value = cn->cgn_parent->cgn_notify;
+
+ len = snprintf(buf, sizeof (buf), "%u\n", value);
+ if (uio->uio_offset > len)
+ return (0);
+
+ len -= uio->uio_offset;
+ rdp = &buf[uio->uio_offset];
+ len = (uio->uio_resid < len) ? uio->uio_resid : len;
+
+ error = uiomove(rdp, len, UIO_READ, uio);
+ return (error);
+}
+
+/*
+ * Read value from the release_agent pseudo file.
+ */
+static int
+cgrp_rd_rel_agent(cgrp_mnt_t *cgm, struct uio *uio)
+{
+ int len;
+ int error = 0;
+ char *rdp;
+
+ mutex_enter(&cgm->cg_contents);
+
+ if (cgm->cg_agent[0] == '\0') {
+ mutex_exit(&cgm->cg_contents);
+ return (0);
+ }
+
+ len = strlen(cgm->cg_agent);
+ if (uio->uio_offset > len) {
+ mutex_exit(&cgm->cg_contents);
+ return (0);
+ }
+
+ len -= uio->uio_offset;
+ rdp = &cgm->cg_agent[uio->uio_offset];
+ len = (uio->uio_resid < len) ? uio->uio_resid : len;
+
+ error = uiomove(rdp, len, UIO_READ, uio);
+
+ mutex_exit(&cgm->cg_contents);
+
+ return (error);
+}
+
+/*
+ * Read pids from the cgroup.procs pseudo file. We have to look at all of the
+ * processes to find applicable ones, then report pids for any process which
+ * has all of its threads in the same cgroup.
+ */
+static int
+cgrp_rd_procs(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+ int i;
+ ssize_t offset = 0;
+ ssize_t uresid;
+ zoneid_t zoneid = curproc->p_zone->zone_id;
+ int error = 0;
+ pid_t initpid = curproc->p_zone->zone_proc_initpid;
+ pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+ /* the cgroup ID is on the containing dir */
+ uint_t cg_id = cn->cgn_parent->cgn_id;
+
+ /* Scan all of the process entries */
+ for (i = 1; i < v.v_proc && (uresid = uio->uio_resid) > 0; i++) {
+ proc_t *p;
+ ssize_t len;
+ pid_t pid;
+ char buf[16];
+ char *rdp;
+ kthread_t *t;
+ boolean_t in_cg;
+
+ mutex_enter(&pidlock);
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, system processes,
+ * a PID of 0, the pid for our zsched process, anything the
+ * security policy doesn't allow us to look at, its not an
+ * lx-branded process and processes that are not in the zone.
+ */
+ if ((p = pid_entry(i)) == NULL ||
+ p->p_stat == SIDL ||
+ (p->p_flag & SSYS) != 0 ||
+ p->p_pid == 0 ||
+ p->p_pid == schedpid ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+ p->p_brand != &lx_brand ||
+ p->p_zone->zone_id != zoneid) {
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ mutex_enter(&p->p_lock);
+ if ((t = p->p_tlist) == NULL) {
+ /* no threads, skip it */
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ /*
+ * Check if all threads are in this cgroup.
+ */
+ in_cg = B_TRUE;
+ mutex_enter(&cgm->cg_contents);
+ do {
+ lx_lwp_data_t *plwpd = ttolxlwp(t);
+ if (plwpd == NULL || plwpd->br_cgroupid != cg_id) {
+ in_cg = B_FALSE;
+ break;
+ }
+
+ t = t->t_forw;
+ } while (t != p->p_tlist);
+ mutex_exit(&cgm->cg_contents);
+
+ mutex_exit(&p->p_lock);
+ if (!in_cg) {
+ /*
+ * This proc, or at least one of its threads, is not
+ * in this cgroup.
+ */
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ /*
+ * Convert pid to the Linux default of 1 if we're the zone's
+ * init process, otherwise use the value from the proc struct
+ */
+ if (p->p_pid == initpid) {
+ pid = 1;
+ } else {
+ pid = p->p_pid;
+ }
+
+ mutex_exit(&pidlock);
+
+ /*
+ * Generate pid line and write all or part of it if we're
+ * in the right spot within the pseudo file.
+ */
+ len = snprintf(buf, sizeof (buf), "%u\n", pid);
+ if ((offset + len) > uio->uio_offset) {
+ int diff = (int)(uio->uio_offset - offset);
+
+ ASSERT(diff < len);
+ offset += diff;
+ rdp = &buf[diff];
+ len -= diff;
+ if (len > uresid)
+ len = uresid;
+
+ error = uiomove(rdp, len, UIO_READ, uio);
+ if (error != 0)
+ return (error);
+ }
+ offset += len;
+ }
+
+ return (0);
+}
+
+/*
+ * We are given a locked process we know is valid, report on any of its thresds
+ * that are in the cgroup.
+ */
+static int
+cgrp_rd_proc_tasks(uint_t cg_id, proc_t *p, pid_t initpid, ssize_t *offset,
+ struct uio *uio)
+{
+ int error = 0;
+ uint_t tid;
+ char buf[16];
+ char *rdp;
+ kthread_t *t;
+
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+
+ /*
+ * Report all threads in this cgroup.
+ */
+ t = p->p_tlist;
+ do {
+ lx_lwp_data_t *plwpd = ttolxlwp(t);
+ if (plwpd == NULL) {
+ t = t->t_forw;
+ continue;
+ }
+
+ if (plwpd->br_cgroupid == cg_id) {
+ int len;
+
+ /*
+ * Convert taskid to the Linux default of 1 if
+ * we're the zone's init process.
+ */
+ tid = plwpd->br_pid;
+ if (tid == initpid)
+ tid = 1;
+
+ len = snprintf(buf, sizeof (buf), "%u\n", tid);
+ if ((*offset + len) > uio->uio_offset) {
+ int diff;
+
+ diff = (int)(uio->uio_offset - *offset);
+ ASSERT(diff < len);
+ *offset = *offset + diff;
+ rdp = &buf[diff];
+ len -= diff;
+ if (len > uio->uio_resid)
+ len = uio->uio_resid;
+
+ error = uiomove(rdp, len, UIO_READ, uio);
+ if (error != 0)
+ return (error);
+ }
+ *offset = *offset + len;
+ }
+
+ t = t->t_forw;
+ } while (t != p->p_tlist && uio->uio_resid > 0);
+
+ return (0);
+}
+
+/*
+ * Read PIDs from the tasks pseudo file. In order to do this, the process
+ * table is walked, searching for entries which are in the correct state and
+ * match this zone. The LX emulated PIDs will be reported from branded entries
+ * which fulfill the criteria. Since records are being emulated for every task
+ * in the process, PR_LOCK is acquired to prevent changes during output.
+ *
+ * Note: If the buffer is filled and the accessing process is forced into a
+ * subsequent read, the reported threads may changes while locks are dropped in
+ * the mean time.
+ */
+static int
+cgrp_rd_tasks(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+ int i;
+ ssize_t offset = 0;
+ zoneid_t zoneid = curproc->p_zone->zone_id;
+ cred_t *cred = CRED();
+ int error = 0;
+ pid_t initpid = curproc->p_zone->zone_proc_initpid;
+ /* the cgroup ID is on the containing dir */
+ uint_t cg_id = cn->cgn_parent->cgn_id;
+
+ /* Scan all of the process entries */
+ for (i = 1; i < v.v_proc && uio->uio_resid > 0; i++) {
+ proc_t *p;
+
+ mutex_enter(&pidlock);
+ for (;;) {
+ if ((p = pid_entry(i)) == NULL) {
+ /* Quickly move onto the next slot */
+ if (++i < v.v_proc) {
+ continue;
+ } else {
+ mutex_exit(&pidlock);
+ break;
+ }
+ }
+
+ /*
+ * Check if this process would even be of interest to
+ * cgroupfs before attempting to acquire its PR_LOCK.
+ */
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+ if (p->p_brand != &lx_brand ||
+ p->p_zone->zone_id != zoneid) {
+ mutex_exit(&p->p_lock);
+ p = NULL;
+ break;
+ }
+
+ /* Attempt to grab P_PR_LOCK. */
+ error = sprtrylock_proc(p);
+ if (error == 0) {
+ /* Success */
+ break;
+ } else if (error < 0) {
+ /*
+ * This process is not in a state where
+ * P_PR_LOCK can be acquired. It either
+ * belongs to the system or is a zombie.
+ * Regardless, give up and move on.
+ */
+ mutex_exit(&p->p_lock);
+ p = NULL;
+ break;
+ } else {
+ /*
+ * Wait until P_PR_LOCK is no longer contended
+ * and attempt to acquire it again. Since the
+ * process may have changed state, the entry
+ * lookup must be repeated.
+ */
+ sprwaitlock_proc(p);
+ mutex_enter(&pidlock);
+ }
+ }
+
+ if (p == NULL) {
+ continue;
+ } else if (secpolicy_basic_procinfo(cred, p, curproc) != 0) {
+ sprunlock(p);
+ continue;
+ }
+
+ /* Shuffle locks and output the entry. */
+ mutex_exit(&p->p_lock);
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_rd_proc_tasks(cg_id, p, initpid, &offset, uio);
+ mutex_exit(&cgm->cg_contents);
+ mutex_enter(&p->p_lock);
+
+ sprunlock(p);
+ if (error != 0) {
+ return (error);
+ }
+ }
+
+ return (0);
+}
+
+static int
+cgrp_rd(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+ int error = 0;
+
+ if (uio->uio_loffset >= MAXOFF_T)
+ return (0);
+ if (uio->uio_loffset < 0)
+ return (EINVAL);
+ if (uio->uio_resid == 0)
+ return (0);
+
+ switch (cn->cgn_type) {
+ case CG_NOTIFY:
+ error = cgrp_rd_notify(cgm, cn, uio);
+ break;
+ case CG_PROCS:
+ error = cgrp_rd_procs(cgm, cn, uio);
+ break;
+ case CG_REL_AGENT:
+ error = cgrp_rd_rel_agent(cgm, uio);
+ break;
+ case CG_TASKS:
+ error = cgrp_rd_tasks(cgm, cn, uio);
+ break;
+ default:
+ VERIFY(0);
+ }
+
+ return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_read(struct vnode *vp, struct uio *uiop, int ioflag, cred_t *cred,
+ struct caller_context *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm = VTOCGM(vp);
+ int error;
+
+ /*
+ * We don't support reading non-regular files
+ */
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+ if (vp->v_type != VREG)
+ return (EINVAL);
+ error = cgrp_rd(cgm, cn, uiop);
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+cgrp_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cred,
+ struct caller_context *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm = VTOCGM(vp);
+ int error;
+
+ /*
+ * We don't support writing to non-regular files
+ */
+ if (vp->v_type != VREG)
+ return (EINVAL);
+
+ if (ioflag & FAPPEND) {
+ /* In append mode start at end of file. */
+ uiop->uio_loffset = cn->cgn_size;
+ }
+
+ error = cgrp_wr(cgm, cn, uiop);
+
+ return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cred,
+ caller_context_t *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm;
+
+ cgm = VTOCGM(cn->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+ vap->va_type = vp->v_type;
+ vap->va_mode = cn->cgn_mode & MODEMASK;
+ vap->va_uid = cn->cgn_uid;
+ vap->va_gid = cn->cgn_gid;
+ vap->va_fsid = cn->cgn_fsid;
+ vap->va_nodeid = (ino64_t)cn->cgn_nodeid;
+ vap->va_nlink = cn->cgn_nlink;
+ vap->va_size = (u_offset_t)cn->cgn_size;
+ vap->va_atime = cn->cgn_atime;
+ vap->va_mtime = cn->cgn_mtime;
+ vap->va_ctime = cn->cgn_ctime;
+ vap->va_blksize = PAGESIZE;
+ vap->va_rdev = cn->cgn_rdev;
+ vap->va_seq = cn->cgn_seq;
+
+ vap->va_nblocks = (fsblkcnt64_t)btodb(ptob(btopr(vap->va_size)));
+ mutex_exit(&cgm->cg_contents);
+ return (0);
+}
+
+/*ARGSUSED4*/
+static int
+cgrp_setattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cred,
+ caller_context_t *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm;
+ int error = 0;
+ struct vattr *get;
+ long mask;
+
+ /*
+ * Cannot set these attributes
+ */
+ if ((vap->va_mask & AT_NOSET) || (vap->va_mask & AT_XVATTR) ||
+ (vap->va_mode & (S_ISUID | S_ISGID)) || (vap->va_mask & AT_SIZE))
+ return (EINVAL);
+
+ cgm = VTOCGM(cn->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+
+ get = &cn->cgn_attr;
+ /*
+ * Change file access modes. Must be owner or have sufficient
+ * privileges.
+ */
+ error = secpolicy_vnode_setattr(cred, vp, vap, get, flags, cgrp_taccess,
+ cn);
+
+ if (error)
+ goto out;
+
+ mask = vap->va_mask;
+
+ if (mask & AT_MODE) {
+ get->va_mode &= S_IFMT;
+ get->va_mode |= vap->va_mode & ~S_IFMT;
+ }
+
+ if (mask & AT_UID)
+ get->va_uid = vap->va_uid;
+ if (mask & AT_GID)
+ get->va_gid = vap->va_gid;
+ if (mask & AT_ATIME)
+ get->va_atime = vap->va_atime;
+ if (mask & AT_MTIME)
+ get->va_mtime = vap->va_mtime;
+
+ if (mask & (AT_UID | AT_GID | AT_MODE | AT_MTIME))
+ gethrestime(&cn->cgn_ctime);
+
+out:
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_access(struct vnode *vp, int mode, int flags, struct cred *cred,
+ caller_context_t *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm;
+ int error;
+
+ cgm = VTOCGM(cn->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_taccess(cn, mode, cred);
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+}
+
+/* ARGSUSED3 */
+static int
+cgrp_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
+ struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
+ caller_context_t *ct, int *direntflags, pathname_t *realpnp)
+{
+ cgrp_node_t *cn = VTOCGN(dvp);
+ cgrp_mnt_t *cgm;
+ cgrp_node_t *ncn = NULL;
+ int error;
+
+ /* disallow extended attrs */
+ if (flags & LOOKUP_XATTR)
+ return (EINVAL);
+
+ /*
+ * Null component name is a synonym for directory being searched.
+ */
+ if (*nm == '\0') {
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+ ASSERT(cn);
+
+ cgm = VTOCGM(cn->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_dirlookup(cn, nm, &ncn, cred);
+ mutex_exit(&cgm->cg_contents);
+
+ if (error == 0) {
+ ASSERT(ncn);
+ *vpp = CGNTOV(ncn);
+ }
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+cgrp_create(struct vnode *dvp, char *nm, struct vattr *vap,
+ enum vcexcl exclusive, int mode, struct vnode **vpp, struct cred *cred,
+ int flag, caller_context_t *ct, vsecattr_t *vsecp)
+{
+ cgrp_node_t *parent = VTOCGN(dvp);
+ cgrp_node_t *cn = NULL;
+ cgrp_mnt_t *cgm;
+ int error;
+
+ if (*nm == '\0')
+ return (EPERM);
+
+ cgm = VTOCGM(parent->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_dirlookup(parent, nm, &cn, cred);
+ if (error == 0) { /* name found */
+ ASSERT(cn);
+
+ mutex_exit(&cgm->cg_contents);
+ /*
+ * Creating an existing file, allow it except for the following
+ * errors.
+ */
+ if (exclusive == EXCL) {
+ error = EEXIST;
+ } else if ((CGNTOV(cn)->v_type == VDIR) && (mode & VWRITE)) {
+ error = EISDIR;
+ } else {
+ error = cgrp_taccess(cn, mode, cred);
+ }
+ if (error != 0) {
+ cgnode_rele(cn);
+ return (error);
+ }
+ *vpp = CGNTOV(cn);
+ return (0);
+ }
+ mutex_exit(&cgm->cg_contents);
+
+ /*
+ * cgroups doesn't allow creation of additional, non-subsystem specific
+ * files in a dir
+ */
+ return (EPERM);
+}
+
+/* ARGSUSED3 */
+static int
+cgrp_remove(struct vnode *dvp, char *nm, struct cred *cred,
+ caller_context_t *ct, int flags)
+{
+ cgrp_node_t *parent = VTOCGN(dvp);
+ int error;
+ cgrp_node_t *cn = NULL;
+ cgrp_mnt_t *cgm;
+
+ /*
+ * Removal of subsystem-specific files is not allowed but we need
+ * to return the correct error if they try to remove a non-existent
+ * file.
+ */
+
+ cgm = VTOCGM(parent->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_dirlookup(parent, nm, &cn, cred);
+ mutex_exit(&cgm->cg_contents);
+ if (error)
+ return (error);
+
+ ASSERT(cn);
+ cgnode_rele(cn);
+ return (EPERM);
+}
+
+/* ARGSUSED */
+static int
+cgrp_link(struct vnode *dvp, struct vnode *srcvp, char *cnm, struct cred *cred,
+ caller_context_t *ct, int flags)
+{
+ /* cgroups doesn't support hard links */
+ return (EPERM);
+}
+
+/*
+ * Rename of subsystem-specific files is not allowed but we can rename
+ * directories (i.e. sub-groups). We cannot mv subdirs from one group to
+ * another so the src and dest vnode must be the same.
+ */
+/* ARGSUSED5 */
+static int
+cgrp_rename(
+ struct vnode *odvp, /* source parent vnode */
+ char *onm, /* source name */
+ struct vnode *ndvp, /* destination parent vnode */
+ char *nnm, /* destination name */
+ struct cred *cred,
+ caller_context_t *ct,
+ int flags)
+{
+ cgrp_node_t *fromparent;
+ cgrp_node_t *toparent;
+ cgrp_node_t *fromcn = NULL; /* source cgrp_node */
+ cgrp_mnt_t *cgm = VTOCGM(odvp);
+ int error, err;
+
+ fromparent = VTOCGN(odvp);
+ toparent = VTOCGN(ndvp);
+
+ if (fromparent != toparent)
+ return (EIO);
+
+ /* discourage additional use of toparent */
+ toparent = NULL;
+
+ mutex_enter(&cgm->cg_contents);
+
+ /*
+ * Look up cgrp_node of file we're supposed to rename.
+ */
+ error = cgrp_dirlookup(fromparent, onm, &fromcn, cred);
+ if (error) {
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+ }
+
+ if (fromcn->cgn_type != CG_CGROUP_DIR) {
+ error = EPERM;
+ goto done;
+ }
+
+ /*
+ * Make sure we can delete the old (source) entry. This
+ * requires write permission on the containing directory.
+ */
+ if (((error = cgrp_taccess(fromparent, VWRITE, cred)) != 0))
+ goto done;
+
+ /*
+ * Check for renaming to or from '.' or '..' or that
+ * fromcn == fromparent
+ */
+ if ((onm[0] == '.' &&
+ (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) ||
+ (nnm[0] == '.' &&
+ (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0'))) ||
+ (fromparent == fromcn)) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /*
+ * Link source to new target
+ */
+ error = cgrp_direnter(cgm, fromparent, nnm, DE_RENAME,
+ fromcn, (struct vattr *)NULL,
+ (cgrp_node_t **)NULL, cred);
+
+ if (error)
+ goto done;
+
+ /*
+ * Unlink from source.
+ */
+ error = err = cgrp_dirdelete(fromparent, fromcn, onm, DR_RENAME, cred);
+
+ /*
+ * The following handles the case where our source cgrp_node was
+ * removed before we got to it.
+ */
+ if (error == ENOENT)
+ error = 0;
+
+ if (err == 0) {
+ vnevent_rename_src(CGNTOV(fromcn), odvp, onm, ct);
+ vnevent_rename_dest_dir(ndvp, CGNTOV(fromcn), nnm, ct);
+ }
+
+done:
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(fromcn);
+
+ return (error);
+}
+
+/* ARGSUSED5 */
+static int
+cgrp_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp,
+ struct cred *cred, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+ cgrp_node_t *parent = VTOCGN(dvp);
+ cgrp_node_t *self = NULL;
+ cgrp_mnt_t *cgm = VTOCGM(dvp);
+ int error;
+
+ /*
+ * Might be dangling directory. Catch it here, because a ENOENT
+ * return from cgrp_dirlookup() is an "ok return".
+ */
+ if (parent->cgn_nlink == 0)
+ return (ENOENT);
+
+ mutex_enter(&cgm->cg_contents);
+ error = cgrp_dirlookup(parent, nm, &self, cred);
+ if (error == 0) {
+ ASSERT(self != NULL);
+ mutex_exit(&cgm->cg_contents);
+ cgnode_rele(self);
+ return (EEXIST);
+ }
+ if (error != ENOENT) {
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+ }
+
+ error = cgrp_direnter(cgm, parent, nm, DE_MKDIR, (cgrp_node_t *)NULL,
+ va, &self, cred);
+ if (error) {
+ mutex_exit(&cgm->cg_contents);
+ if (self != NULL)
+ cgnode_rele(self);
+ return (error);
+ }
+ mutex_exit(&cgm->cg_contents);
+ *vpp = CGNTOV(self);
+ return (0);
+}
+
+/* ARGSUSED4 */
+static int
+cgrp_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred,
+ caller_context_t *ct, int flags)
+{
+ cgrp_node_t *parent = VTOCGN(dvp);
+ cgrp_mnt_t *cgm;
+ cgrp_node_t *self = NULL;
+ struct vnode *vp;
+ int error = 0;
+
+ /*
+ * Return error when removing . and ..
+ */
+ if (strcmp(nm, ".") == 0)
+ return (EINVAL);
+ if (strcmp(nm, "..") == 0)
+ return (EEXIST); /* Should be ENOTEMPTY */
+
+ cgm = VTOCGM(parent->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+
+ error = cgrp_dirlookup(parent, nm, &self, cred);
+ if (error) {
+ mutex_exit(&cgm->cg_contents);
+ return (error);
+ }
+
+ vp = CGNTOV(self);
+ if (vp == dvp || vp == cdir) {
+ error = EINVAL;
+ goto done;
+ }
+ if (self->cgn_type != CG_CGROUP_DIR) {
+ error = ENOTDIR;
+ goto done;
+ }
+
+ cgm = (cgrp_mnt_t *)VFSTOCGM(self->cgn_vnode->v_vfsp);
+
+ /*
+ * Check for the existence of any sub-cgroup directories or tasks in
+ * the cgroup.
+ */
+ if (self->cgn_task_cnt > 0 || self->cgn_dirents > N_DIRENTS(cgm)) {
+ error = EEXIST;
+ /*
+ * Update atime because checking cn_dirents is logically
+ * equivalent to reading the directory
+ */
+ gethrestime(&self->cgn_atime);
+ goto done;
+ }
+
+ if (vn_vfswlock(vp)) {
+ error = EBUSY;
+ goto done;
+ }
+ if (vn_mountedvfs(vp) != NULL) {
+ error = EBUSY;
+ } else {
+ error = cgrp_dirdelete(parent, self, nm, DR_RMDIR, cred);
+ }
+
+ vn_vfsunlock(vp);
+
+ if (parent->cgn_task_cnt == 0 &&
+ parent->cgn_dirents == N_DIRENTS(cgm) && parent->cgn_notify == 1) {
+ cgrp_rel_agent_event(cgm, parent, B_FALSE);
+ ASSERT(MUTEX_NOT_HELD(&cgm->cg_contents));
+ goto dropped;
+ }
+
+done:
+ mutex_exit(&cgm->cg_contents);
+dropped:
+ vnevent_rmdir(CGNTOV(self), dvp, nm, ct);
+ cgnode_rele(self);
+
+ return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_readdir(struct vnode *vp, struct uio *uiop, struct cred *cred, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm;
+ cgrp_dirent_t *cdp;
+ int error = 0;
+ size_t namelen;
+ struct dirent64 *dp;
+ ulong_t offset;
+ ulong_t total_bytes_wanted;
+ long outcount = 0;
+ long bufsize;
+ int reclen;
+ caddr_t outbuf;
+
+ if (uiop->uio_loffset >= MAXOFF_T) {
+ if (eofp)
+ *eofp = 1;
+ return (0);
+ }
+
+ if (uiop->uio_iovcnt != 1)
+ return (EINVAL);
+
+ if (vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ cgm = VTOCGM(cn->cgn_vnode);
+ mutex_enter(&cgm->cg_contents);
+
+ if (cn->cgn_dir == NULL) {
+ VERIFY(cn->cgn_nlink == 0);
+ mutex_exit(&cgm->cg_contents);
+ return (0);
+ }
+
+ /*
+ * Get space for multiple directory entries
+ */
+ total_bytes_wanted = uiop->uio_iov->iov_len;
+ bufsize = total_bytes_wanted + sizeof (struct dirent64);
+ outbuf = kmem_alloc(bufsize, KM_SLEEP);
+
+ /* LINTED: alignment */
+ dp = (struct dirent64 *)outbuf;
+
+ offset = 0;
+ cdp = cn->cgn_dir;
+ while (cdp) {
+ namelen = strlen(cdp->cgd_name); /* no +1 needed */
+ offset = cdp->cgd_offset;
+ if (offset >= uiop->uio_offset) {
+ reclen = (int)DIRENT64_RECLEN(namelen);
+ if (outcount + reclen > total_bytes_wanted) {
+ if (!outcount) {
+ /* Buffer too small for any entries. */
+ error = EINVAL;
+ }
+ break;
+ }
+ ASSERT(cdp->cgd_cgrp_node != NULL);
+
+ /* use strncpy(9f) to zero out uninitialized bytes */
+
+ (void) strncpy(dp->d_name, cdp->cgd_name,
+ DIRENT64_NAMELEN(reclen));
+ dp->d_reclen = (ushort_t)reclen;
+ dp->d_ino = (ino64_t)cdp->cgd_cgrp_node->cgn_nodeid;
+ dp->d_off = (offset_t)cdp->cgd_offset + 1;
+ dp = (struct dirent64 *)((uintptr_t)dp + dp->d_reclen);
+ outcount += reclen;
+ ASSERT(outcount <= bufsize);
+ }
+ cdp = cdp->cgd_next;
+ }
+
+ if (!error)
+ error = uiomove(outbuf, outcount, UIO_READ, uiop);
+
+ if (!error) {
+ /*
+ * If we reached the end of the list our offset should now be
+ * just past the end.
+ */
+ if (!cdp) {
+ offset += 1;
+ if (eofp)
+ *eofp = 1;
+ } else if (eofp)
+ *eofp = 0;
+ uiop->uio_offset = offset;
+ }
+ gethrestime(&cn->cgn_atime);
+
+ mutex_exit(&cgm->cg_contents);
+
+ kmem_free(outbuf, bufsize);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+cgrp_symlink(struct vnode *dvp, char *lnm, struct vattr *cva, char *cnm,
+ struct cred *cred, caller_context_t *ct, int flags)
+{
+ /* cgroups doesn't support symlinks */
+ return (EPERM);
+}
+
+/* ARGSUSED */
+static void
+cgrp_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct)
+{
+ cgrp_node_t *cn = VTOCGN(vp);
+ cgrp_mnt_t *cgm = VFSTOCGM(vp->v_vfsp);
+
+ mutex_enter(&cgm->cg_contents);
+ mutex_enter(&vp->v_lock);
+ ASSERT(vp->v_count >= 1);
+
+ /*
+ * If we don't have the last hold or the link count is non-zero,
+ * there's little to do -- just drop our hold.
+ */
+ if (vp->v_count > 1 || cn->cgn_nlink != 0) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&cgm->cg_contents);
+ return;
+ }
+
+ if (cn->cgn_forw == NULL)
+ cgm->cg_rootnode->cgn_back = cn->cgn_back;
+ else
+ cn->cgn_forw->cgn_back = cn->cgn_back;
+ cn->cgn_back->cgn_forw = cn->cgn_forw;
+
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&cgm->cg_contents);
+
+ /* Here's our chance to send invalid event */
+ vn_invalid(CGNTOV(cn));
+
+ vn_free(CGNTOV(cn));
+ kmem_free(cn, sizeof (cgrp_node_t));
+}
+
+/* ARGSUSED */
+static int
+cgrp_seek(struct vnode *vp, offset_t ooff, offset_t *noffp,
+ caller_context_t *ct)
+{
+ return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
+}
+
+/* ARGSUSED */
+static int
+cgrp_rwlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+ return (write_lock);
+}
+
+/* ARGSUSED */
+static void
+cgrp_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+}
+
+static int
+cgrp_pathconf(struct vnode *vp, int cmd, ulong_t *valp, cred_t *cr,
+ caller_context_t *ct)
+{
+ int error;
+
+ switch (cmd) {
+ case _PC_XATTR_EXISTS:
+ if (vp->v_vfsp->vfs_flag & VFS_XATTR) {
+ *valp = 0; /* assume no attributes */
+ error = 0; /* okay to ask */
+ } else {
+ error = EINVAL;
+ }
+ break;
+ case _PC_SATTR_ENABLED:
+ case _PC_SATTR_EXISTS:
+ *valp = vfs_has_feature(vp->v_vfsp, VFSFT_SYSATTR_VIEWS) &&
+ (vp->v_type == VREG || vp->v_type == VDIR);
+ error = 0;
+ break;
+ case _PC_TIMESTAMP_RESOLUTION:
+ /* nanosecond timestamp resolution */
+ *valp = 1L;
+ error = 0;
+ break;
+ default:
+ error = fs_pathconf(vp, cmd, valp, cr, ct);
+ }
+ return (error);
+}
+
+
+struct vnodeops *cgrp_vnodeops;
+
+const fs_operation_def_t cgrp_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = cgrp_open },
+ VOPNAME_CLOSE, { .vop_close = cgrp_close },
+ VOPNAME_READ, { .vop_read = cgrp_read },
+ VOPNAME_WRITE, { .vop_write = cgrp_write },
+ VOPNAME_GETATTR, { .vop_getattr = cgrp_getattr },
+ VOPNAME_SETATTR, { .vop_setattr = cgrp_setattr },
+ VOPNAME_ACCESS, { .vop_access = cgrp_access },
+ VOPNAME_LOOKUP, { .vop_lookup = cgrp_lookup },
+ VOPNAME_CREATE, { .vop_create = cgrp_create },
+ VOPNAME_REMOVE, { .vop_remove = cgrp_remove },
+ VOPNAME_LINK, { .vop_link = cgrp_link },
+ VOPNAME_RENAME, { .vop_rename = cgrp_rename },
+ VOPNAME_MKDIR, { .vop_mkdir = cgrp_mkdir },
+ VOPNAME_RMDIR, { .vop_rmdir = cgrp_rmdir },
+ VOPNAME_READDIR, { .vop_readdir = cgrp_readdir },
+ VOPNAME_SYMLINK, { .vop_symlink = cgrp_symlink },
+ VOPNAME_INACTIVE, { .vop_inactive = cgrp_inactive },
+ VOPNAME_RWLOCK, { .vop_rwlock = cgrp_rwlock },
+ VOPNAME_RWUNLOCK, { .vop_rwunlock = cgrp_rwunlock },
+ VOPNAME_SEEK, { .vop_seek = cgrp_seek },
+ VOPNAME_PATHCONF, { .vop_pathconf = cgrp_pathconf },
+ VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support },
+ NULL, NULL
+};
diff --git a/usr/src/uts/common/brand/lx/devfs/lxd.h b/usr/src/uts/common/brand/lx/devfs/lxd.h
new file mode 100644
index 0000000000..437b0b6162
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/devfs/lxd.h
@@ -0,0 +1,244 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#ifndef _LXD_H
+#define _LXD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * lxd.h: declarations, data structures and macros for lxd (lxd devfs).
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/atomic.h>
+#include <vm/anon.h>
+#include <sys/lx_types.h>
+
+#if defined(_KERNEL)
+
+#include <sys/lx_brand.h>
+
+/*
+ * It's unlikely that we need to create more than 50-60 subdirs/symlinks
+ * as front files so we size the file system hash for 2x that number.
+ * The back devfs typically has ~80 nodes so this is also a comfortable size
+ * for the back hash table.
+ */
+#define LXD_HASH_SZ 128
+
+#define LXD_BACK_HASH(v) ((((intptr_t)(v)) >> 10) & ((LXD_HASH_SZ) - 1))
+
+#define LXD_NM_HASH(ldn, name, hash) \
+ { \
+ char Xc, *Xcp; \
+ hash = (uint_t)(uintptr_t)(ldn) >> 8; \
+ for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++) \
+ hash = (hash << 4) + hash + (uint_t)Xc; \
+ hash &= (LXD_HASH_SZ - 1); \
+ }
+
+
+enum lxd_node_type { LXDNT_NONE, LXDNT_BACK, LXDNT_FRONT };
+
+typedef struct lxd_dev_attr {
+ list_node_t lxda_link;
+ char lxda_name[MAXPATHLEN];
+ uid_t lxda_uid;
+ gid_t lxda_gid;
+ mode_t lxda_mode;
+} lxd_dev_attr_t;
+
+/*
+ * lxd per-mount data structure.
+ *
+ * All fields are protected by lxd_contents.
+ * File renames on a specific file system are protected lxdm_renamelck.
+ */
+typedef struct lxd_mnt {
+ struct vfs *lxdm_vfsp; /* filesystem's vfs struct */
+ struct lxd_node *lxdm_rootnode; /* root lxd_node */
+ char *lxdm_mntpath; /* name of lxd mount point */
+ dev_t lxdm_dev; /* unique dev # of mounted `device' */
+ kmutex_t lxdm_contents; /* per-mount lock */
+ kmutex_t lxdm_renamelck; /* rename lock for this mount */
+ kmutex_t lxdm_attrlck; /* per-mount attr. file lock */
+ list_t lxdm_devattrs; /* list of device attr. settings */
+ uint_t lxdm_gen; /* node ID source for files */
+
+ /* protects buckets in both "dir ent" and "back" hash tables */
+ kmutex_t lxdm_hash_mutex[LXD_HASH_SZ];
+
+ /* per-mount data for "back" vnodes in the fs */
+ uint_t lxdm_back_refcnt; /* # outstanding "back" vnodes */
+ struct lxd_node *lxdm_back_htable[LXD_HASH_SZ];
+
+ /*
+ * Per-mount directory data for "front" nodes in the fs.
+ * Each front node has a directory entry but directory entries can live
+ * on either front or back nodes.
+ */
+ uint_t lxdm_dent_refcnt; /* # outstanding dir ents */
+ struct lxd_dirent *lxdm_dent_htable[LXD_HASH_SZ];
+} lxd_mnt_t;
+
+/*
+ * lxd_node is the file system dependent node for lxd.
+ *
+ * The node is used to represent both front and back files. For front files
+ * the node can represent either a directory or symlink.
+ */
+typedef struct lxd_node {
+ enum lxd_node_type lxdn_type;
+
+ /* Data for "front" nodes */
+ struct lxd_node *lxdn_prev; /* lnked lst of lxd nodes */
+ struct lxd_node *lxdn_next; /* lnked lst of lxd nodes */
+ struct lxd_node *lxdn_parent; /* dir containing this node */
+ krwlock_t lxdn_rwlock; /* serialize mods/dir updates */
+ kmutex_t lxdn_tlock; /* time, flag, and nlink lock */
+
+ /* these could be in a union ala tmpfs but not really necessary */
+ uint_t lxdn_dirents; /* number of dirents */
+ struct lxd_dirent *lxdn_dir; /* dirent list */
+ char *lxdn_symlink; /* pointer to symlink */
+ struct vattr lxdn_attr; /* attributes */
+
+ /* Hash table link */
+ struct lxd_node *lxdn_hnxt; /* link in per-mount entry */
+ /* hash table */
+ vnode_t *lxdn_vnode; /* vnode for this lxd_node */
+
+ vnode_t *lxdn_real_vp; /* back file - real vnode */
+} lxd_node_t;
+
+/*
+ * Attributes
+ */
+#define lxdn_mask lxdn_attr.va_mask
+#define lxdn_mode lxdn_attr.va_mode
+#define lxdn_uid lxdn_attr.va_uid
+#define lxdn_gid lxdn_attr.va_gid
+#define lxdn_fsid lxdn_attr.va_fsid
+#define lxdn_nodeid lxdn_attr.va_nodeid
+#define lxdn_nlink lxdn_attr.va_nlink
+#define lxdn_size lxdn_attr.va_size
+#define lxdn_atime lxdn_attr.va_atime
+#define lxdn_mtime lxdn_attr.va_mtime
+#define lxdn_ctime lxdn_attr.va_ctime
+#define lxdn_rdev lxdn_attr.va_rdev
+#define lxdn_blksize lxdn_attr.va_blksize
+#define lxdn_nblocks lxdn_attr.va_nblocks
+#define lxdn_seq lxdn_attr.va_seq
+
+/*
+ * lx devfs conversion macros
+ */
+#define VFSTOLXDM(vfsp) ((lxd_mnt_t *)(vfsp)->vfs_data)
+#define VTOLXDM(vp) ((lxd_mnt_t *)(vp)->v_vfsp->vfs_data)
+#define VTOLDN(vp) ((lxd_node_t *)(vp)->v_data)
+#define LDNTOV(ln) ((ln)->lxdn_vnode)
+#define ldnode_hold(ln) VN_HOLD(LDNTOV(ln))
+#define ldnode_rele(ln) VN_RELE(LDNTOV(ln))
+
+#define REALVP(vp) (VTOLDN(vp)->lxdn_real_vp)
+
+/*
+ * front directories are made up of a linked list of lxd_dirent structures
+ * hanging off directory lxdn_nodes. File names are not fixed length, but are
+ * null terminated.
+ */
+typedef struct lxd_dirent {
+ lxd_node_t *lddir_node; /* lxd node for this file */
+ struct lxd_dirent *lddir_next; /* next directory entry */
+ struct lxd_dirent *lddir_prev; /* prev directory entry */
+ uint_t lddir_offset; /* "offset" of dir entry */
+ uint_t lddir_hash; /* a hash of lddir_name */
+ struct lxd_dirent *lddir_link; /* linked via hash table */
+ lxd_node_t *lddir_parent; /* parent, dir we are in */
+ char *lddir_name; /* null terminated */
+} lxd_dirent_t;
+
+enum de_op { DE_CREATE, DE_MKDIR, DE_RENAME }; /* direnter ops */
+enum dr_op { DR_REMOVE, DR_RMDIR, DR_RENAME }; /* dirremove ops */
+
+typedef struct lxd_minor_translator {
+ char *lxd_mt_path; /* illumos minor node path */
+ minor_t lxd_mt_minor; /* illumos minor node number */
+ int lxd_mt_lx_major; /* linux major node number */
+ int lxd_mt_lx_minor; /* linux minor node number */
+} lxd_minor_translator_t;
+
+enum lxd_xl_tp { DTT_INVALID, DTT_LIST, DTT_CUSTOM };
+
+#define xl_list lxd_xl_minor.lxd_xl_list
+#define xl_custom lxd_xl_minor.lxd_xl_custom
+
+typedef struct lxd_devt_translator {
+ char *lxd_xl_driver; /* driver name */
+ major_t lxd_xl_major; /* driver number */
+
+ enum lxd_xl_tp lxd_xl_type; /* dictates how we intrep. xl_minor */
+ union {
+ uintptr_t lxd_xl_foo; /* required to compile */
+ lxd_minor_translator_t *lxd_xl_list;
+ void (*lxd_xl_custom)(dev_t, dev_t *);
+ } lxd_xl_minor;
+} lxd_devt_translator_t;
+
+extern struct vnodeops *lxd_vnodeops;
+extern lxd_devt_translator_t lxd_devt_translators[];
+
+vnode_t *lxd_make_back_node(vnode_t *, lxd_mnt_t *);
+void lxd_free_back_node(lxd_node_t *);
+int lxd_dirdelete(lxd_node_t *, lxd_node_t *, char *, enum dr_op, cred_t *);
+int lxd_direnter(lxd_mnt_t *, lxd_node_t *, char *, enum de_op, lxd_node_t *,
+ lxd_node_t *, struct vattr *, lxd_node_t **, cred_t *);
+void lxd_dirinit(lxd_node_t *, lxd_node_t *);
+int lxd_dirlookup(lxd_node_t *, char *, lxd_node_t **, cred_t *);
+void lxd_dirtrunc(lxd_node_t *);
+void lxd_node_init(lxd_mnt_t *, lxd_node_t *, vnode_t *, vattr_t *, cred_t *);
+int lxd_naccess(void *, int, cred_t *);
+
+void lxd_save_attrs(lxd_mnt_t *, vnode_t *);
+void lxd_apply_db(lxd_mnt_t *);
+
+#endif /* KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LXD_H */
diff --git a/usr/src/uts/common/brand/lx/devfs/lxd_attrdb.c b/usr/src/uts/common/brand/lx/devfs/lxd_attrdb.c
new file mode 100644
index 0000000000..02d396a36d
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/devfs/lxd_attrdb.c
@@ -0,0 +1,368 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/cred.h>
+#include <sys/pathname.h>
+#include <sys/debug.h>
+#include <sys/sdt.h>
+#include <fs/fs_subr.h>
+
+#include "lxd.h"
+
+#define LX_ATTR_FILE "/etc/.lxd_dev_attr"
+
+#define RD_BUFSIZE MAXPATHLEN
+#define ENTRY_BUFSIZE (MAXPATHLEN + 32)
+
+static int
+lxd_db_open(int fmode, vnode_t **vpp)
+{
+ return (vn_open(LX_ATTR_FILE, UIO_SYSSPACE, fmode,
+ (int)(0644 & MODEMASK), vpp, CRCREAT, PTOU(curproc)->u_cmask));
+}
+
+static int
+lxd_wr_entry(vnode_t *wvn, off_t offset, char *entry)
+{
+ int len, err;
+ struct uio auio;
+ struct iovec aiov;
+
+ len = strlen(entry);
+ aiov.iov_base = entry;
+ aiov.iov_len = len;
+
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = offset;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_resid = len;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = FWRITE;
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ (void) VOP_RWLOCK(wvn, V_WRITELOCK_TRUE, NULL);
+ err = VOP_WRITE(wvn, &auio, FAPPEND, CRED(), NULL);
+ VOP_RWUNLOCK(wvn, V_WRITELOCK_TRUE, NULL);
+
+ if (err != 0)
+ return (0);
+ return (len);
+}
+
+/*
+ * Given an entry, apply a uid, gid and mode change to the given device. There
+ * is no strtok in the kernel but it's easy to tokenize the entry ourselves.
+ *
+ * entries have the form (newline removed by caller):
+ * path uid gid mode\0
+ */
+static int
+lxd_apply_entry(char *entry, char **dpath, uid_t *uidp, gid_t *gidp,
+ mode_t *modep)
+{
+ char *dp, *up, *gp, *mp, *ep;
+ long uid, gid, mode;
+ int error, res = 0;
+ vnode_t *vp;
+ vattr_t va;
+
+ dp = entry;
+
+ /* find and delimit the first field (device name) */
+ for (up = dp; *up != ' ' && *up != '\0'; up++)
+ ;
+ if (*up != ' ')
+ return (-1);
+ *up++ = '\0';
+
+ /* find and delimit the second field (uid) */
+ for (gp = up; *gp != ' ' && *gp != '\0'; gp++)
+ ;
+ if (*gp != ' ')
+ return (-1);
+ *gp++ = '\0';
+
+ /* find and delimit the third field (gid) */
+ for (mp = gp; *mp != ' ' && *mp != '\0'; mp++)
+ ;
+ if (*mp != ' ')
+ return (-1);
+ *mp++ = '\0';
+
+ /* validate the fourth field (mode) */
+ ep = mp + strlen(mp);
+ if (*ep != '\0')
+ return (-1);
+
+ if (*dp != '/')
+ return (-1);
+
+ error = ddi_strtol(up, &ep, 10, &uid);
+ if (error != 0 || *ep != '\0' || uid > MAXUID || uid < 0)
+ return (-1);
+
+ error = ddi_strtol(gp, &ep, 10, &gid);
+ if (error != 0 || *ep != '\0' || gid > MAXUID || gid < 0)
+ return (-1);
+
+ /* note that the mode is octal */
+ error = ddi_strtol(mp, &ep, 8, &mode);
+ if (error != 0 || *ep != '\0' || mode > 0777 || mode < 0)
+ return (-1);
+
+ if (lookupname(dp, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp) != 0) {
+ /*
+ * It's likely the device is no longer visible to the zone.
+ * No matter the reason, we indicate failure.
+ */
+ return (-1);
+ }
+
+ va.va_mask = AT_UID | AT_GID | AT_MODE;
+ va.va_uid = (uid_t)uid;
+ va.va_gid = (gid_t)gid;
+ va.va_mode = (mode_t)mode;
+
+ if (VOP_SETATTR(vp, &va, 0, CRED(), NULL) != 0)
+ res = -1;
+
+ VN_RELE(vp);
+
+ *dpath = dp;
+ *uidp = (uid_t)uid;
+ *gidp = (gid_t)gid;
+ *modep = (mode_t)mode;
+ return (res);
+}
+
+/*
+ * Return true if this is a pre-existing record.
+ */
+static boolean_t
+lxd_save_devattr(lxd_mnt_t *lxdm, char *dpath, uid_t uid, gid_t gid,
+ mode_t mode)
+{
+ lxd_dev_attr_t *da;
+
+ da = list_head(&lxdm->lxdm_devattrs);
+ while (da != NULL) {
+ if (strcmp(dpath, da->lxda_name) == 0) {
+ da->lxda_uid = uid;
+ da->lxda_gid = gid;
+ da->lxda_mode = mode;
+ return (B_TRUE);
+ }
+ da = list_next(&lxdm->lxdm_devattrs, da);
+ }
+
+ da = kmem_zalloc(sizeof (lxd_dev_attr_t), KM_SLEEP);
+ (void) strlcpy(da->lxda_name, dpath, sizeof (da->lxda_name));
+ da->lxda_uid = uid;
+ da->lxda_gid = gid;
+ da->lxda_mode = mode;
+
+ list_insert_tail(&lxdm->lxdm_devattrs, da);
+ return (B_FALSE);
+}
+
+static void
+lxd_save_db(lxd_mnt_t *lxdm)
+{
+ lxd_dev_attr_t *da;
+ char *entry;
+ vnode_t *wvn;
+ off_t woff = 0;
+
+ if (list_is_empty(&lxdm->lxdm_devattrs)) {
+ /* The attribute file is no longer needed. */
+ (void) vn_remove(LX_ATTR_FILE, UIO_SYSSPACE, RMFILE);
+ return;
+ }
+
+ if (lxd_db_open(FWRITE | FCREAT | FTRUNC, &wvn) != 0)
+ return;
+
+ entry = kmem_alloc(ENTRY_BUFSIZE, KM_SLEEP);
+
+ woff = lxd_wr_entry(wvn, woff, "# DO NOT EDIT: this file is "
+ "automatically maintained for lx container devices\n");
+
+ da = list_head(&lxdm->lxdm_devattrs);
+ while (da != NULL) {
+ (void) snprintf(entry, ENTRY_BUFSIZE, "%s %d %d %o\n",
+ da->lxda_name, da->lxda_uid, da->lxda_gid,
+ da->lxda_mode & 0777);
+ woff += lxd_wr_entry(wvn, woff, entry);
+ da = list_next(&lxdm->lxdm_devattrs, da);
+ }
+
+ (void) VOP_CLOSE(wvn, FWRITE, 1, woff, CRED(), NULL);
+
+ kmem_free(entry, ENTRY_BUFSIZE);
+}
+
+/*
+ * This function records the uid, gid and mode information for an lx devfs
+ * block device node after a chown/chmod setattr operation so that these
+ * changes can be persistent across reboots. Since the actual setattr has
+ * already suceeded, the tracking of these changes is done on a "best effort"
+ * basis. That is, if we fail to record the change for some reason, the setattr
+ * will still return success. The vp passed in is the "real vp" for the back
+ * device node.
+ */
+void
+lxd_save_attrs(lxd_mnt_t *lxdm, vnode_t *vp)
+{
+ vattr_t va;
+ char devpath[MAXPATHLEN];
+
+ /* the path returned is relative to the zone's root */
+ if (vnodetopath(curproc->p_zone->zone_rootvp, vp, devpath,
+ sizeof (devpath), CRED()) != 0)
+ return;
+
+ va.va_mask = AT_MODE | AT_UID | AT_GID;
+
+ /*
+ * We just set attrs, so the getattr shouldn't fail. If the device
+ * is not a block device we don't persist the change.
+ */
+ if (VOP_GETATTR(vp, &va, 0, CRED(), NULL) != 0 ||
+ ((va.va_mode & S_IFBLK) != S_IFBLK))
+ return;
+
+ /*
+ * We serialize all updates to the attribute DB file. In practice this
+ * should not be a problem since there is rarely concurrent device
+ * file mode changes.
+ */
+ mutex_enter(&lxdm->lxdm_attrlck);
+
+ (void) lxd_save_devattr(lxdm, devpath, va.va_uid, va.va_gid,
+ va.va_mode & 0777);
+ lxd_save_db(lxdm);
+
+ mutex_exit(&lxdm->lxdm_attrlck);
+}
+
+/*
+ * Re-apply the persistent attribute settings to the devices when this lx
+ * devfs is mounted. As with lxd_save_attrs, this is done on a best effort and
+ * we won't prevent the mount if there is a problem. No locking is needed
+ * while reading the DB file since this action is performed during the
+ * mount of the devfs.
+ */
+void
+lxd_apply_db(lxd_mnt_t *lxdm)
+{
+ vnode_t *rvn;
+ char *buf, *entry, *bp, *ep;
+ struct uio auio;
+ struct iovec aiov;
+ size_t cnt, len, ecnt, roff;
+ char *devpath;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ boolean_t needs_update = B_FALSE;
+
+ if (lxd_db_open(FREAD, &rvn) != 0)
+ return;
+
+ buf = kmem_alloc(RD_BUFSIZE, KM_SLEEP);
+ entry = kmem_alloc(ENTRY_BUFSIZE, KM_SLEEP);
+
+ roff = 0;
+ ep = entry;
+ ecnt = 0;
+ (void) VOP_RWLOCK(rvn, V_WRITELOCK_FALSE, NULL);
+loop:
+ aiov.iov_base = buf;
+ aiov.iov_len = RD_BUFSIZE;
+
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = roff;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_resid = RD_BUFSIZE;
+ auio.uio_fmode = 0;
+ auio.uio_extflg = UIO_COPY_CACHED;
+
+ (void) VOP_READ(rvn, &auio, 0, CRED(), NULL);
+
+ len = RD_BUFSIZE - auio.uio_resid;
+ roff += len;
+
+ if (len > 0) {
+ for (bp = buf, cnt = 0; cnt < len; bp++, cnt++) {
+
+ /*
+ * We have an improperly formed entry in the file (too
+ * long). In an attempt to recover we reset the entry
+ * pointer so we can read the rest of the line and try
+ * to absorb the bad line. The code in lxd_apply_entry
+ * will handle any malformed or inapplicable entries.
+ */
+ if (ecnt >= (ENTRY_BUFSIZE - 1)) {
+ ep = entry;
+ ecnt = 0;
+ needs_update = B_TRUE;
+ }
+
+ if (*bp == '\n') {
+ *ep = '\0';
+
+ /* skip comments */
+ if (entry[0] != '#') {
+ if (lxd_apply_entry(entry, &devpath,
+ &uid, &gid, &mode) != 0 ||
+ lxd_save_devattr(lxdm, devpath,
+ uid, gid, mode)) {
+ /*
+ * An invalid entry, a
+ * non-existent device node or
+ * a duplicate entry.
+ */
+ needs_update = B_TRUE;
+ }
+ }
+ ep = entry;
+ ecnt = 0;
+ } else {
+ *ep++ = *bp;
+ ecnt++;
+ }
+ }
+ goto loop;
+ }
+ VOP_RWUNLOCK(rvn, V_WRITELOCK_FALSE, NULL);
+
+ kmem_free(buf, RD_BUFSIZE);
+ kmem_free(entry, ENTRY_BUFSIZE);
+
+ (void) VOP_CLOSE(rvn, FREAD, 1, 0, CRED(), NULL);
+
+ if (needs_update)
+ lxd_save_db(lxdm);
+}
diff --git a/usr/src/uts/common/brand/lx/devfs/lxd_node.c b/usr/src/uts/common/brand/lx/devfs/lxd_node.c
new file mode 100644
index 0000000000..30fdeb82a6
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/devfs/lxd_node.c
@@ -0,0 +1,1012 @@
+/*
+ * 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 2021 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+
+#include "lxd.h"
+
+#define LXD_HASH_SIZE 8192 /* must be power of 2 */
+#define LXD_MUTEX_SIZE 64
+
+
+#define MODESHIFT 3
+
+typedef enum lxd_nodehold {
+ NOHOLD,
+ HOLD
+} lxd_nodehold_t;
+
+/*
+ * The following functions maintain the per-mount "front" files.
+ */
+static void
+lxd_save_dirent(lxd_dirent_t *de)
+{
+ lxd_mnt_t *lxdm = VTOLXDM(LDNTOV(de->lddir_parent));
+ uint_t hash;
+ kmutex_t *hmtx;
+
+ LXD_NM_HASH(de->lddir_parent, de->lddir_name, hash);
+ de->lddir_hash = hash;
+
+ hmtx = &lxdm->lxdm_hash_mutex[hash];
+
+ mutex_enter(hmtx);
+ ASSERT(de->lddir_link == NULL);
+ de->lddir_link = lxdm->lxdm_dent_htable[hash];
+ lxdm->lxdm_dent_htable[hash] = de;
+ mutex_exit(hmtx);
+
+ atomic_inc_32(&lxdm->lxdm_dent_refcnt);
+}
+
+static void
+lxd_rm_dirent(lxd_dirent_t *de)
+{
+ lxd_mnt_t *lxdm = VTOLXDM(LDNTOV(de->lddir_parent));
+ uint_t hash;
+ lxd_dirent_t **prevpp;
+ kmutex_t *hmtx;
+
+ hash = de->lddir_hash;
+ hmtx = &lxdm->lxdm_hash_mutex[hash];
+
+ mutex_enter(hmtx);
+ prevpp = &lxdm->lxdm_dent_htable[hash];
+ while (*prevpp != de)
+ prevpp = &(*prevpp)->lddir_link;
+ *prevpp = de->lddir_link;
+ de->lddir_link = NULL;
+ mutex_exit(hmtx);
+
+ ASSERT(lxdm->lxdm_dent_refcnt > 0);
+ atomic_dec_32(&lxdm->lxdm_dent_refcnt);
+}
+
+static lxd_dirent_t *
+lxd_find_dirent(char *name, lxd_node_t *parent, lxd_nodehold_t do_hold,
+ lxd_node_t **found)
+{
+ lxd_mnt_t *lxdm = VTOLXDM(LDNTOV(parent));
+ lxd_dirent_t *de;
+ uint_t hash;
+ kmutex_t *hmtx;
+
+ LXD_NM_HASH(parent, name, hash);
+ hmtx = &lxdm->lxdm_hash_mutex[hash];
+
+ mutex_enter(hmtx);
+ de = lxdm->lxdm_dent_htable[hash];
+ while (de) {
+ if (de->lddir_hash == hash && de->lddir_parent == parent &&
+ strcmp(de->lddir_name, name) == 0) {
+ lxd_node_t *ldn = de->lddir_node;
+
+ if (do_hold == HOLD) {
+ ASSERT(ldn != NULL);
+ ldnode_hold(ldn);
+ }
+ if (found != NULL)
+ *found = ldn;
+ mutex_exit(hmtx);
+ return (de);
+ }
+
+ de = de->lddir_link;
+ }
+ mutex_exit(hmtx);
+ return (NULL);
+}
+
+int
+lxd_naccess(void *vcp, int mode, cred_t *cr)
+{
+ lxd_node_t *ldn = vcp;
+ int shift = 0;
+ /*
+ * Check access based on owner, group and public perms in lxd_node.
+ */
+ if (crgetuid(cr) != ldn->lxdn_uid) {
+ shift += MODESHIFT;
+ if (groupmember(ldn->lxdn_gid, cr) == 0)
+ shift += MODESHIFT;
+ }
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (secpolicy_vnode_access2(cr, LDNTOV(ldn),
+ ldn->lxdn_uid, ldn->lxdn_mode << shift, mode));
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ return (VOP_ACCESS(ldn->lxdn_real_vp, mode, 0, cr, NULL));
+}
+
+static lxd_node_t *
+lxd_find_back(struct vnode *vp, uint_t hash, lxd_mnt_t *lxdm)
+{
+ lxd_node_t *l;
+
+ ASSERT(MUTEX_HELD(&lxdm->lxdm_hash_mutex[hash]));
+
+ for (l = lxdm->lxdm_back_htable[hash]; l != NULL; l = l->lxdn_hnxt) {
+ if (l->lxdn_real_vp == vp) {
+ ASSERT(l->lxdn_type == LXDNT_BACK);
+
+ VN_HOLD(LDNTOV(l));
+ return (l);
+ }
+ }
+ return (NULL);
+}
+
+static void
+lxd_save_back(lxd_node_t *l, uint_t hash, lxd_mnt_t *lxdm)
+{
+ ASSERT(l->lxdn_type == LXDNT_BACK);
+ ASSERT(l->lxdn_real_vp != NULL);
+ ASSERT(MUTEX_HELD(&lxdm->lxdm_hash_mutex[hash]));
+
+ atomic_inc_32(&lxdm->lxdm_back_refcnt);
+
+ l->lxdn_hnxt = lxdm->lxdm_back_htable[hash];
+ lxdm->lxdm_back_htable[hash] = l;
+}
+
+
+struct vnode *
+lxd_make_back_node(struct vnode *vp, lxd_mnt_t *lxdm)
+{
+ uint_t hash;
+ kmutex_t *hmtx;
+ lxd_node_t *l;
+
+ hash = LXD_BACK_HASH(vp); /* Note: hashing with realvp */
+ hmtx = &lxdm->lxdm_hash_mutex[hash];
+ mutex_enter(hmtx);
+
+ l = lxd_find_back(vp, hash, lxdm);
+ if (l == NULL) {
+ vnode_t *nvp;
+
+ l = kmem_zalloc(sizeof (lxd_node_t), KM_SLEEP);
+ nvp = vn_alloc(KM_SLEEP);
+
+ rw_init(&l->lxdn_rwlock, NULL, RW_DEFAULT, NULL);
+ mutex_init(&l->lxdn_tlock, NULL, MUTEX_DEFAULT, NULL);
+
+ l->lxdn_vnode = nvp;
+ l->lxdn_type = LXDNT_BACK;
+ l->lxdn_real_vp = vp;
+
+ VN_SET_VFS_TYPE_DEV(nvp, lxdm->lxdm_vfsp, vp->v_type,
+ vp->v_rdev);
+ nvp->v_flag |= (vp->v_flag & (VNOMOUNT|VNOMAP|VDIROPEN));
+ vn_setops(nvp, lxd_vnodeops);
+ nvp->v_data = (caddr_t)l;
+
+ lxd_save_back(l, hash, lxdm);
+ vn_exists(vp);
+ } else {
+ VN_RELE(vp);
+ }
+
+ mutex_exit(hmtx);
+ return (LDNTOV(l));
+}
+
+void
+lxd_free_back_node(lxd_node_t *lp)
+{
+ uint_t hash;
+ kmutex_t *hmtx;
+ lxd_node_t *l;
+ lxd_node_t *lprev = NULL;
+ vnode_t *vp = LDNTOV(lp);
+ vnode_t *realvp = REALVP(vp);
+ lxd_mnt_t *lxdm = VTOLXDM(vp);
+
+ /* in lxd_make_back_node we call lxd_find_back with the realvp */
+ hash = LXD_BACK_HASH(realvp);
+ hmtx = &lxdm->lxdm_hash_mutex[hash];
+ mutex_enter(hmtx);
+
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count > 1) {
+ vp->v_count--; /* release our hold from vn_rele */
+ mutex_exit(&vp->v_lock);
+ mutex_exit(hmtx);
+ return;
+ }
+ mutex_exit(&vp->v_lock);
+
+ for (l = lxdm->lxdm_back_htable[hash]; l != NULL;
+ lprev = l, l = l->lxdn_hnxt) {
+
+ if (l != lp)
+ continue;
+
+ ASSERT(l->lxdn_type == LXDNT_BACK);
+ ASSERT(lxdm->lxdm_back_refcnt > 0);
+
+ atomic_dec_32(&lxdm->lxdm_back_refcnt);
+ vn_invalid(vp);
+
+ if (lprev == NULL) {
+ lxdm->lxdm_back_htable[hash] = l->lxdn_hnxt;
+ } else {
+ lprev->lxdn_hnxt = l->lxdn_hnxt;
+ }
+
+ mutex_exit(hmtx);
+ rw_destroy(&l->lxdn_rwlock);
+ mutex_destroy(&l->lxdn_tlock);
+ kmem_free(l, sizeof (lxd_node_t));
+ vn_free(vp);
+ VN_RELE(realvp);
+ return;
+ }
+
+ panic("lxd_free_back_node");
+ /*NOTREACHED*/
+}
+/*
+ * Search directory 'parent' for entry 'name'.
+ *
+ * 0 is returned on success and *foundcp points
+ * to the found lxd_node with its vnode held.
+ */
+int
+lxd_dirlookup(lxd_node_t *parent, char *name, lxd_node_t **foundnp, cred_t *cr)
+{
+ int error;
+
+ *foundnp = NULL;
+ if (parent->lxdn_vnode->v_type != VDIR)
+ return (ENOTDIR);
+
+ if ((error = lxd_naccess(parent, VEXEC, cr)))
+ return (error);
+
+ if (*name == '\0') {
+ ldnode_hold(parent);
+ *foundnp = parent;
+ return (0);
+ }
+
+ /*
+ * Search the directory for the matching name
+ * We need the lock protecting the lxdn_dir list
+ * so that it doesn't change out from underneath us.
+ * lxd_find_dirent() will pass back the lxd_node
+ * with a hold on it.
+ */
+
+ if (lxd_find_dirent(name, parent, HOLD, foundnp) != NULL) {
+ ASSERT(*foundnp);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Check if the source directory is in the path of the target directory.
+ * The target directory is locked by the caller.
+ */
+static int
+lxd_dircheckpath(lxd_node_t *fromnode, lxd_node_t *toparent)
+{
+ int error = 0;
+ lxd_node_t *dir, *dotdot;
+
+ ASSERT(RW_WRITE_HELD(&toparent->lxdn_rwlock));
+ ASSERT(toparent->lxdn_vnode->v_type == VDIR);
+
+ dotdot = toparent->lxdn_parent;
+ if (dotdot == NULL)
+ return (ENOENT);
+ ldnode_hold(dotdot);
+
+ if (dotdot == toparent) {
+ /* root of fs. search trivially satisfied. */
+ ldnode_rele(dotdot);
+ return (0);
+ }
+
+ for (;;) {
+ /*
+ * Return error for cases like "mv c c/d",
+ * "mv c c/d/e" and so on.
+ */
+ if (dotdot == fromnode) {
+ ldnode_rele(dotdot);
+ error = EINVAL;
+ break;
+ }
+
+ dir = dotdot;
+ dotdot = dir->lxdn_parent;
+ if (dotdot == NULL) {
+ ldnode_rele(dir);
+ error = ENOENT;
+ break;
+ }
+ ldnode_hold(dotdot);
+
+ /*
+ * We're okay if we traverse the directory tree up to
+ * the root directory and don't run into the
+ * parent directory.
+ */
+ if (dir == dotdot) {
+ ldnode_rele(dir);
+ ldnode_rele(dotdot);
+ break;
+ }
+ ldnode_rele(dir);
+ }
+
+ return (error);
+}
+
+static int
+lxd_dir_make_node(lxd_node_t *dir, lxd_mnt_t *lxdm, struct vattr *va,
+ enum de_op op, lxd_node_t **newnode, struct cred *cred)
+{
+ lxd_node_t *ldn;
+
+ ASSERT(va != NULL);
+
+ if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) ||
+ ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
+ return (EOVERFLOW);
+
+ ldn = kmem_zalloc(sizeof (lxd_node_t), KM_SLEEP);
+
+ ldn->lxdn_type = LXDNT_FRONT;
+ lxd_node_init(lxdm, ldn, NULL, va, cred);
+
+ ldn->lxdn_vnode->v_rdev = ldn->lxdn_rdev = NODEV;
+ ldn->lxdn_vnode->v_type = va->va_type;
+ ldn->lxdn_uid = crgetuid(cred);
+ ldn->lxdn_gid = crgetgid(cred);
+ ldn->lxdn_nodeid = lxdm->lxdm_gen++;
+
+ if (va->va_mask & AT_ATIME)
+ ldn->lxdn_atime = va->va_atime;
+ if (va->va_mask & AT_MTIME)
+ ldn->lxdn_mtime = va->va_mtime;
+
+ if (op == DE_MKDIR) {
+ lxd_dirinit(dir, ldn);
+ }
+
+ *newnode = ldn;
+ return (0);
+}
+
+static int
+lxd_diraddentry(lxd_node_t *dir, lxd_node_t *ldn, char *name)
+{
+ lxd_dirent_t *dp, *pdp;
+ size_t namelen, alloc_size;
+ timestruc_t now;
+
+ /*
+ * Make sure the parent directory wasn't removed from
+ * underneath the caller.
+ */
+ if (dir->lxdn_dir == NULL)
+ return (ENOENT);
+
+ /* Check that everything is on the same filesystem. */
+ if (ldn->lxdn_vnode->v_vfsp != dir->lxdn_vnode->v_vfsp)
+ return (EXDEV);
+
+ /* Allocate and initialize directory entry */
+ namelen = strlen(name) + 1;
+ alloc_size = namelen + sizeof (lxd_dirent_t);
+ dp = kmem_zalloc(alloc_size, KM_NOSLEEP_LAZY);
+ if (dp == NULL)
+ return (ENOSPC);
+
+ ldn->lxdn_parent = dir;
+
+ dir->lxdn_size += alloc_size;
+ dir->lxdn_dirents++;
+ dp->lddir_node = ldn;
+ dp->lddir_parent = dir;
+
+ /* The directory entry and its name were allocated sequentially. */
+ dp->lddir_name = (char *)dp + sizeof (lxd_dirent_t);
+ (void) strcpy(dp->lddir_name, name);
+
+ lxd_save_dirent(dp);
+
+ /*
+ * Some utilities expect the size of a directory to remain
+ * somewhat static. For example, a routine which removes
+ * subdirectories between calls to readdir(); the size of the
+ * directory changes from underneath it and so the real
+ * directory offset in bytes is invalid. To circumvent
+ * this problem, we initialize a directory entry with an
+ * phony offset, and use this offset to determine end of
+ * file in lxd_readdir.
+ */
+ pdp = dir->lxdn_dir->lddir_prev;
+ /*
+ * Install at first empty "slot" in directory list.
+ */
+ while (pdp->lddir_next != NULL &&
+ (pdp->lddir_next->lddir_offset - pdp->lddir_offset) <= 1) {
+ ASSERT(pdp->lddir_next != pdp);
+ ASSERT(pdp->lddir_prev != pdp);
+ ASSERT(pdp->lddir_next->lddir_offset > pdp->lddir_offset);
+ pdp = pdp->lddir_next;
+ }
+ dp->lddir_offset = pdp->lddir_offset + 1;
+
+ /*
+ * If we're at the end of the dirent list and the offset (which
+ * is necessarily the largest offset in this directory) is more
+ * than twice the number of dirents, that means the directory is
+ * 50% holes. At this point we reset the slot pointer back to
+ * the beginning of the directory so we start using the holes.
+ * The idea is that if there are N dirents, there must also be
+ * N holes, so we can satisfy the next N creates by walking at
+ * most 2N entries; thus the average cost of a create is constant.
+ * Note that we use the first dirent's lddir_prev as the roving
+ * slot pointer; it's ugly, but it saves a word in every dirent.
+ */
+ if (pdp->lddir_next == NULL &&
+ pdp->lddir_offset > 2 * dir->lxdn_dirents)
+ dir->lxdn_dir->lddir_prev = dir->lxdn_dir->lddir_next;
+ else
+ dir->lxdn_dir->lddir_prev = dp;
+
+ ASSERT(pdp->lddir_next != pdp);
+ ASSERT(pdp->lddir_prev != pdp);
+
+ dp->lddir_next = pdp->lddir_next;
+ if (dp->lddir_next) {
+ dp->lddir_next->lddir_prev = dp;
+ }
+ dp->lddir_prev = pdp;
+ pdp->lddir_next = dp;
+
+ ASSERT(dp->lddir_next != dp);
+ ASSERT(dp->lddir_prev != dp);
+ ASSERT(pdp->lddir_next != pdp);
+ ASSERT(pdp->lddir_prev != pdp);
+
+ gethrestime(&now);
+ dir->lxdn_mtime = now;
+ dir->lxdn_ctime = now;
+
+ return (0);
+}
+
+/*
+ * Enter a directory entry for 'name' into directory 'dir'
+ *
+ * Returns 0 on success.
+ */
+int
+lxd_direnter(
+ lxd_mnt_t *lxdm,
+ lxd_node_t *dir, /* target directory to make entry in */
+ char *name, /* name of entry */
+ enum de_op op, /* entry operation */
+ lxd_node_t *fromparent, /* original directory if rename */
+ lxd_node_t *ldn, /* existing lxd_node, if rename */
+ struct vattr *va,
+ lxd_node_t **rnp, /* return lxd_node, if create/mkdir */
+ cred_t *cr)
+{
+ lxd_dirent_t *dirp;
+ lxd_node_t *found = NULL;
+ int error = 0;
+ char *s;
+
+ /* lxdn_rwlock is held to serialize direnter and dirdeletes */
+ ASSERT(RW_WRITE_HELD(&dir->lxdn_rwlock));
+ ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+ /*
+ * Don't allow '/' characters in pathname component,
+ */
+ for (s = name; *s; s++)
+ if (*s == '/')
+ return (EACCES);
+
+ if (name[0] == '\0')
+ panic("lxd_direnter: NULL name");
+
+ /*
+ * For rename lock the source entry and check the link count
+ * to see if it has been removed while it was unlocked.
+ */
+ if (op == DE_RENAME) {
+ mutex_enter(&ldn->lxdn_tlock);
+ if (ldn->lxdn_nlink == 0) {
+ mutex_exit(&ldn->lxdn_tlock);
+ return (ENOENT);
+ }
+
+ if (ldn->lxdn_nlink == MAXLINK) {
+ mutex_exit(&ldn->lxdn_tlock);
+ return (EMLINK);
+ }
+ ldn->lxdn_nlink++;
+ gethrestime(&ldn->lxdn_ctime);
+ mutex_exit(&ldn->lxdn_tlock);
+ }
+
+ /*
+ * This might be a "dangling detached directory" (it could have been
+ * removed, but a reference to it kept in u_cwd). Don't bother
+ * searching it, and with any luck the user will get tired of dealing
+ * with us and cd to some absolute pathway (thus in ufs, too).
+ */
+ if (dir->lxdn_nlink == 0) {
+ error = ENOENT;
+ goto out;
+ }
+
+ /*
+ * If this is a rename of a directory and the parent is different
+ * (".." must be changed), then the source directory must not be in the
+ * directory hierarchy above the target, as this would orphan
+ * everything below the source directory.
+ */
+ if (op == DE_RENAME) {
+ if (ldn == dir) {
+ error = EINVAL;
+ goto out;
+ }
+ if ((ldn->lxdn_vnode->v_type) == VDIR) {
+ if ((fromparent != dir) &&
+ (error = lxd_dircheckpath(ldn, dir)) != 0) {
+ goto out;
+ }
+ }
+ }
+
+ /* Search for an existing entry. */
+ dirp = lxd_find_dirent(name, dir, HOLD, &found);
+ if (dirp != NULL) {
+ ASSERT(found != NULL);
+ switch (op) {
+ case DE_CREATE:
+ case DE_MKDIR:
+ if (rnp != NULL) {
+ *rnp = found;
+ error = EEXIST;
+ } else {
+ ldnode_rele(found);
+ }
+ break;
+
+ case DE_RENAME:
+ /*
+ * Note that we only hit this path when we're renaming
+ * a symlink from one directory to another and there is
+ * a pre-existing symlink as the target. lxd_rename
+ * will unlink the src from the original directory but
+ * here we need to unlink the dest that we collided
+ * with, then create the new directory entry as we do
+ * below when there is no pre-existing symlink.
+ */
+ if ((error = lxd_naccess(dir, VWRITE, cr)) != 0)
+ goto out;
+
+ ASSERT(found->lxdn_vnode->v_type == VLNK);
+ /* dir rw lock is already held and asserted above */
+ rw_enter(&found->lxdn_rwlock, RW_WRITER);
+ error = lxd_dirdelete(dir, found, name, DR_RENAME, cr);
+ rw_exit(&found->lxdn_rwlock);
+ ldnode_rele(found);
+ if (error != 0)
+ goto out;
+
+ error = lxd_diraddentry(dir, ldn, name);
+ if (error == 0 && rnp != NULL)
+ *rnp = ldn;
+ break;
+ }
+ } else {
+
+ /*
+ * The directory entry does not exist, but the node might if
+ * this is a rename. Check write permission in directory to
+ * see if entry can be created.
+ */
+ if ((error = lxd_naccess(dir, VWRITE, cr)) != 0)
+ goto out;
+ if (op == DE_CREATE || op == DE_MKDIR) {
+ /*
+ * Make new lxd_node and directory entry as required.
+ */
+ error = lxd_dir_make_node(dir, lxdm, va, op, &ldn, cr);
+ if (error)
+ goto out;
+ }
+
+ error = lxd_diraddentry(dir, ldn, name);
+ if (error != 0) {
+ if (op == DE_CREATE || op == DE_MKDIR) {
+ /*
+ * Unmake the inode we just made.
+ */
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+ if ((ldn->lxdn_vnode->v_type) == VDIR) {
+ ASSERT(dirp == NULL);
+ /*
+ * cleanup allocs made by lxd_dirinit
+ */
+ lxd_dirtrunc(ldn);
+ }
+ mutex_enter(&ldn->lxdn_tlock);
+ ldn->lxdn_nlink = 0;
+ gethrestime(&ldn->lxdn_ctime);
+ mutex_exit(&ldn->lxdn_tlock);
+ rw_exit(&ldn->lxdn_rwlock);
+ ldnode_rele(ldn);
+ ldn = NULL;
+ }
+ } else if (rnp != NULL) {
+ *rnp = ldn;
+ } else if (op == DE_CREATE || op == DE_MKDIR) {
+ ldnode_rele(ldn);
+ }
+ }
+
+out:
+ if (error && op == DE_RENAME) {
+ /* Undo bumped link count. */
+ mutex_enter(&ldn->lxdn_tlock);
+ ldn->lxdn_nlink--;
+ gethrestime(&ldn->lxdn_ctime);
+ mutex_exit(&ldn->lxdn_tlock);
+ }
+ return (error);
+}
+
+/*
+ * Delete entry ldn of name "nm" from parent dir. This is used to both remove
+ * a directory and to remove file nodes within the directory (by recursively
+ * calling itself). It frees the dir entry space and decrements link count on
+ * lxd_node(s).
+ *
+ * Return 0 on success.
+ */
+int
+lxd_dirdelete(lxd_node_t *dir, lxd_node_t *ldn, char *nm, enum dr_op op,
+ cred_t *cred)
+{
+ lxd_dirent_t *dirp;
+ int error;
+ size_t namelen;
+ lxd_node_t *fndnp;
+ timestruc_t now;
+
+ ASSERT(RW_WRITE_HELD(&dir->lxdn_rwlock));
+ ASSERT(RW_WRITE_HELD(&ldn->lxdn_rwlock));
+ ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+ if (nm[0] == '\0')
+ panic("lxd_dirdelete: empty name for 0x%p", (void *)ldn);
+
+ /*
+ * return error when removing . and ..
+ */
+ if (nm[0] == '.') {
+ if (nm[1] == '\0')
+ return (EINVAL);
+ if (nm[1] == '.' && nm[2] == '\0')
+ return (EEXIST); /* thus in ufs */
+ }
+
+ if ((error = lxd_naccess(dir, VEXEC|VWRITE, cred)) != 0)
+ return (error);
+
+ if (dir->lxdn_dir == NULL)
+ return (ENOENT);
+
+ if (op == DR_RMDIR) {
+ /*
+ * This is the top-level removal of a directory. Start by
+ * removing any file entries from the dir. We do this by
+ * recursively calling back into this function with a different
+ * op code. The caller of this function has already verified
+ * that it is safe to remove this directory.
+ */
+ lxd_dirent_t *dirp;
+
+ ASSERT(ldn->lxdn_vnode->v_type == VDIR);
+
+ dirp = ldn->lxdn_dir;
+ while (dirp) {
+ lxd_node_t *dn;
+ lxd_dirent_t *nextp;
+
+ if (strcmp(dirp->lddir_name, ".") == 0 ||
+ strcmp(dirp->lddir_name, "..") == 0) {
+ dirp = dirp->lddir_next;
+ continue;
+ }
+
+ dn = dirp->lddir_node;
+ nextp = dirp->lddir_next;
+
+ ldnode_hold(dn);
+ error = lxd_dirdelete(ldn, dn, dirp->lddir_name,
+ DR_REMOVE, cred);
+ ldnode_rele(dn);
+ if (error != 0)
+ return (error);
+
+ dirp = nextp;
+ }
+ }
+
+ dirp = lxd_find_dirent(nm, dir, NOHOLD, &fndnp);
+ /* These used to be VERIFY(), but in racy conditions they can fail. */
+ if (dirp == NULL) {
+ /* Can't find the directory entry at all now! */
+ return (ENOENT);
+ }
+ if (ldn != fndnp) {
+ /* Returned fndnp isn't our original, so it's also not-there. */
+ return (ENOENT);
+ }
+
+ lxd_rm_dirent(dirp);
+
+ /* Take dirp out of the directory list. */
+ ASSERT(dirp->lddir_next != dirp);
+ ASSERT(dirp->lddir_prev != dirp);
+ if (dirp->lddir_prev) {
+ dirp->lddir_prev->lddir_next = dirp->lddir_next;
+ }
+ if (dirp->lddir_next) {
+ dirp->lddir_next->lddir_prev = dirp->lddir_prev;
+ }
+
+ /*
+ * If the roving slot pointer happens to match dirp,
+ * point it at the previous dirent.
+ */
+ if (dir->lxdn_dir->lddir_prev == dirp) {
+ dir->lxdn_dir->lddir_prev = dirp->lddir_prev;
+ }
+ ASSERT(dirp->lddir_next != dirp);
+ ASSERT(dirp->lddir_prev != dirp);
+
+ /* dirp points to the correct directory entry */
+ namelen = strlen(dirp->lddir_name) + 1;
+
+ kmem_free(dirp, sizeof (lxd_dirent_t) + namelen);
+ dir->lxdn_size -= (sizeof (lxd_dirent_t) + namelen);
+ dir->lxdn_dirents--;
+
+ gethrestime(&now);
+ dir->lxdn_mtime = now;
+ dir->lxdn_ctime = now;
+ ldn->lxdn_ctime = now;
+
+ ASSERT(ldn->lxdn_nlink > 0);
+ mutex_enter(&ldn->lxdn_tlock);
+ ldn->lxdn_nlink--;
+ mutex_exit(&ldn->lxdn_tlock);
+ if (op == DR_RMDIR && ldn->lxdn_vnode->v_type == VDIR) {
+ lxd_dirtrunc(ldn);
+ ASSERT(ldn->lxdn_nlink == 0);
+ }
+ return (0);
+}
+
+/*
+ * Initialize a lxd_node and add it to file list under mount point.
+ */
+void
+lxd_node_init(lxd_mnt_t *lxdm, lxd_node_t *ldn, vnode_t *realvp, vattr_t *vap,
+ cred_t *cred)
+{
+ struct vnode *vp;
+ timestruc_t now;
+
+ ASSERT(vap != NULL);
+
+ rw_init(&ldn->lxdn_rwlock, NULL, RW_DEFAULT, NULL);
+ mutex_init(&ldn->lxdn_tlock, NULL, MUTEX_DEFAULT, NULL);
+ ldn->lxdn_mode = MAKEIMODE(vap->va_type, vap->va_mode);
+ ldn->lxdn_mask = 0;
+ ldn->lxdn_attr.va_type = vap->va_type;
+ ldn->lxdn_nlink = 1;
+ ldn->lxdn_size = 0;
+
+ if (cred == NULL) {
+ ldn->lxdn_uid = vap->va_uid;
+ ldn->lxdn_gid = vap->va_gid;
+ } else {
+ ldn->lxdn_uid = crgetuid(cred);
+ ldn->lxdn_gid = crgetgid(cred);
+ }
+
+ ldn->lxdn_fsid = lxdm->lxdm_dev;
+ ldn->lxdn_rdev = vap->va_rdev;
+ ldn->lxdn_blksize = PAGESIZE;
+ ldn->lxdn_nblocks = 0;
+ gethrestime(&now);
+ ldn->lxdn_atime = now;
+ ldn->lxdn_mtime = now;
+ ldn->lxdn_ctime = now;
+ ldn->lxdn_seq = 0;
+ ldn->lxdn_dir = NULL;
+
+ ldn->lxdn_real_vp = realvp;
+
+ ldn->lxdn_vnode = vn_alloc(KM_SLEEP);
+ vp = LDNTOV(ldn);
+ vn_setops(vp, lxd_vnodeops);
+ vp->v_vfsp = lxdm->lxdm_vfsp;
+ vp->v_type = vap->va_type;
+ vp->v_rdev = vap->va_rdev;
+ vp->v_data = (caddr_t)ldn;
+
+ mutex_enter(&lxdm->lxdm_contents);
+ ldn->lxdn_nodeid = lxdm->lxdm_gen++;
+
+ /*
+ * Add new lxd_node to end of linked list of lxd_nodes for this
+ * lxdevfs. Root directory is handled specially in lxd_mount.
+ */
+ if (lxdm->lxdm_rootnode != (lxd_node_t *)NULL) {
+ ldn->lxdn_next = NULL;
+ ldn->lxdn_prev = lxdm->lxdm_rootnode->lxdn_prev;
+ ldn->lxdn_prev->lxdn_next = lxdm->lxdm_rootnode->lxdn_prev =
+ ldn;
+ }
+ mutex_exit(&lxdm->lxdm_contents);
+ vn_exists(vp);
+}
+
+/*
+ * lxd_dirinit is used internally to initialize a directory (dir)
+ * with '.' and '..' entries without checking permissions and locking
+ * It also creates the entries for the pseudo file nodes that reside in the
+ * directory.
+ */
+void
+lxd_dirinit(lxd_node_t *parent, lxd_node_t *dir)
+{
+ lxd_dirent_t *dot, *dotdot;
+ timestruc_t now;
+ lxd_mnt_t *lxdm = VTOLXDM(dir->lxdn_vnode);
+ struct vattr nattr;
+
+ ASSERT(RW_WRITE_HELD(&parent->lxdn_rwlock));
+ ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+ dir->lxdn_nodeid = lxdm->lxdm_gen++;
+
+ /*
+ * Initialize the entries
+ */
+ dot = kmem_zalloc(sizeof (lxd_dirent_t) + 2, KM_SLEEP);
+ dot->lddir_node = dir;
+ dot->lddir_offset = 0;
+ dot->lddir_name = (char *)dot + sizeof (lxd_dirent_t);
+ dot->lddir_name[0] = '.';
+ dot->lddir_parent = dir;
+ lxd_save_dirent(dot);
+
+ dotdot = kmem_zalloc(sizeof (lxd_dirent_t) + 3, KM_SLEEP);
+ dotdot->lddir_node = parent;
+ dotdot->lddir_offset = 1;
+ dotdot->lddir_name = (char *)dotdot + sizeof (lxd_dirent_t);
+ dotdot->lddir_name[0] = '.';
+ dotdot->lddir_name[1] = '.';
+ dotdot->lddir_parent = dir;
+ lxd_save_dirent(dotdot);
+
+ /*
+ * Initialize directory entry list.
+ */
+ dot->lddir_next = dotdot;
+ dot->lddir_prev = dotdot; /* dot's lddir_prev holds roving slot ptr */
+ dotdot->lddir_next = NULL;
+ dotdot->lddir_prev = dot;
+
+ gethrestime(&now);
+ dir->lxdn_mtime = now;
+ dir->lxdn_ctime = now;
+
+ parent->lxdn_nlink++;
+ parent->lxdn_ctime = now;
+
+ dir->lxdn_dir = dot;
+ dir->lxdn_size = 2 * sizeof (lxd_dirent_t) + 5; /* dot and dotdot */
+ dir->lxdn_dirents = 2;
+ dir->lxdn_nlink = 2;
+ dir->lxdn_parent = parent;
+
+ bzero(&nattr, sizeof (struct vattr));
+ nattr.va_mode = (mode_t)(0644);
+ nattr.va_type = VREG;
+ nattr.va_rdev = 0;
+}
+
+/*
+ * lxd_dirtrunc is called to remove all directory entries under this directory.
+ */
+void
+lxd_dirtrunc(lxd_node_t *dir)
+{
+ lxd_dirent_t *ldp;
+ timestruc_t now;
+
+ ASSERT(RW_WRITE_HELD(&dir->lxdn_rwlock));
+ ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+ for (ldp = dir->lxdn_dir; ldp; ldp = dir->lxdn_dir) {
+ size_t namelen;
+ lxd_node_t *ldn;
+
+ ASSERT(ldp->lddir_next != ldp);
+ ASSERT(ldp->lddir_prev != ldp);
+ ASSERT(ldp->lddir_node);
+
+ dir->lxdn_dir = ldp->lddir_next;
+ namelen = strlen(ldp->lddir_name) + 1;
+
+ /*
+ * Adjust the link counts to account for this directory entry
+ * removal. We do hold/rele operations to free up these nodes.
+ */
+ ldn = ldp->lddir_node;
+
+ ASSERT(ldn->lxdn_nlink > 0);
+ mutex_enter(&ldn->lxdn_tlock);
+ ldn->lxdn_nlink--;
+ mutex_exit(&ldn->lxdn_tlock);
+
+ lxd_rm_dirent(ldp);
+ kmem_free(ldp, sizeof (lxd_dirent_t) + namelen);
+ dir->lxdn_size -= (sizeof (lxd_dirent_t) + namelen);
+ dir->lxdn_dirents--;
+ }
+
+ gethrestime(&now);
+ dir->lxdn_mtime = now;
+ dir->lxdn_ctime = now;
+
+ ASSERT(dir->lxdn_dir == NULL);
+ ASSERT(dir->lxdn_size == 0);
+ ASSERT(dir->lxdn_dirents == 0);
+}
diff --git a/usr/src/uts/common/brand/lx/devfs/lxd_vfsops.c b/usr/src/uts/common/brand/lx/devfs/lxd_vfsops.c
new file mode 100644
index 0000000000..69c131d886
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/devfs/lxd_vfsops.c
@@ -0,0 +1,860 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+/*
+ * The lx devfs (lxd) file system is used within lx branded zones to provide
+ * the Linux view of /dev.
+ *
+ * In the past, the Linux /dev was simply a lofs mount pointing at /native/dev.
+ * lxd now provides the Linux /dev.
+ *
+ * The lxd file system is a hybrid of lofs and tmpfs. It supports a "back" file
+ * system which is the special device and corresponds to the special device in
+ * a lofs mount. As with lofs, all files in the special device are accessible
+ * through the lxd mount. Because the zone's devfs is not directly modifiable
+ * within the zone (also mknod(2) is not generally allowed within a zone) it is
+ * impossible to create files in devfs. For lx, in some cases it's useful to be
+ * able to make new symlinks or new directories under /dev. lxd implements
+ * these operations by creating "files" in memory in the same way as tmpfs
+ * does. Within lxd these are referred to as "front" files. For operations such
+ * as lookup or readdir, lxd provides a merged view of both the front and back
+ * files. lxd does not support regular front files or simple I/O (read/write)
+ * to front files, since there is no need for that. For back files, all
+ * operations are simply passed through to the real vnode, as is done with
+ * lofs. Front files are not allowed to mask back files.
+ *
+ * The Linux /dev is now a lxd mount with the special file (i.e. the back
+ * file system) as /native/dev.
+ *
+ * In addition, lx has a need for some illumos/Linux translation for the
+ * various *stat(2) system calls when used on a device. This translation can
+ * be centralized within lxd's getattr vnode entry point.
+ *
+ * Because the front file system only exists in memory and the back file
+ * system is the zone's devfs, which is not persistent across reboots, we
+ * track any device uid/gid/mode changes in a per-zone /etc/.lxd_dev_attr
+ * file and re-apply those changes when the lx devfs file system is mounted.
+ * Currently only changes to block device nodes are persistent.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/time.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/statvfs.h>
+#include <sys/mount.h>
+#include <sys/systm.h>
+#include <sys/mntent.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_ptm.h>
+#include <sys/lx_impl.h>
+
+#include "lxd.h"
+
+/* Module level parameters */
+static int lxd_fstype;
+static dev_t lxd_dev;
+
+/*
+ * lxd_mountcount is used to prevent module unloads while there is still
+ * state from a former mount hanging around. The filesystem module must not be
+ * allowed to go away before the last VFS_FREEVFS() call has been made. Since
+ * this is just an atomic counter, there's no need for locking.
+ */
+static uint32_t lxd_mountcount;
+
+/*
+ * lxd_minfree is the minimum amount of swap space that lx devfs leaves for
+ * the rest of the zone.
+ */
+size_t lxd_minfree = 0;
+
+/*
+ * LXDMINFREE -- the value from which lxd_minfree is derived -- should be
+ * configured to a value that is roughly the smallest practical value for
+ * memory + swap minus the largest reasonable size for lxd in such
+ * a configuration. As of this writing, the smallest practical memory + swap
+ * configuration is 128MB, and it seems reasonable to allow lxd to consume
+ * no more than ~10% of this, yielding a LXDMINFREE of 12MB.
+ */
+#define LXDMINFREE 12 * 1024 * 1024 /* 12 Megabytes */
+
+extern pgcnt_t swapfs_minfree;
+
+extern int lxd_symlink(vnode_t *, char *, struct vattr *, char *, cred_t *,
+ caller_context_t *, int);
+extern int stat64(char *, struct stat64 *);
+
+/*
+ * lxd vfs operations.
+ */
+static int lxd_init(int, char *);
+static int lxd_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
+static int lxd_unmount(vfs_t *, int, cred_t *);
+static int lxd_root(vfs_t *, vnode_t **);
+static int lxd_statvfs(vfs_t *, statvfs64_t *);
+static void lxd_freevfs(vfs_t *vfsp);
+
+/*
+ * Loadable module wrapper
+ */
+#include <sys/modctl.h>
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "lx_devfs",
+ lxd_init,
+ VSW_ZMOUNT,
+ NULL
+};
+
+/*
+ * Module linkage information
+ */
+static struct modlfs modlfs = {
+ &mod_fsops, "lx brand devfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modlfs, NULL
+};
+
+/*
+ * Definitions and translators for devt's.
+ */
+static void lxd_pts_devt_translator(dev_t, dev_t *);
+static void lxd_ptm_devt_translator(dev_t, dev_t *);
+
+static kmutex_t lxd_xlate_lock;
+static boolean_t lxd_xlate_initialized = B_FALSE;
+
+static lxd_minor_translator_t lxd_mtranslator_mm[] = {
+ { "/dev/null", 0, 1, 3 },
+ { "/dev/zero", 0, 1, 5 },
+ { NULL, 0, 0, 0 }
+};
+static lxd_minor_translator_t lxd_mtranslator_random[] = {
+ { "/dev/random", 0, 1, 8 },
+ { "/dev/urandom", 0, 1, 9 },
+ { NULL, 0, 0, 0 }
+};
+static lxd_minor_translator_t lxd_mtranslator_sy[] = {
+ { "/dev/tty", 0, LX_TTY_MAJOR, 0 },
+ { NULL, 0, 0, 0 }
+};
+static lxd_minor_translator_t lxd_mtranslator_zcons[] = {
+ { "/dev/console", 0, LX_TTY_MAJOR, 1 },
+ { NULL, 0, 0, 0 }
+};
+lxd_devt_translator_t lxd_devt_translators[] = {
+ { "mm", 0, DTT_LIST, (uintptr_t)&lxd_mtranslator_mm },
+ { "random", 0, DTT_LIST, (uintptr_t)&lxd_mtranslator_random },
+ { "sy", 0, DTT_LIST, (uintptr_t)&lxd_mtranslator_sy },
+ { "zcons", 0, DTT_LIST, (uintptr_t)&lxd_mtranslator_zcons },
+ { LX_PTM_DRV, 0, DTT_CUSTOM, (uintptr_t)lxd_ptm_devt_translator },
+ { "pts", 0, DTT_CUSTOM, (uintptr_t)lxd_pts_devt_translator },
+ { NULL, 0, DTT_INVALID, (uintptr_t)NULL }
+};
+
+int
+_init()
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini()
+{
+ int error;
+
+ if (lxd_mountcount > 0)
+ return (EBUSY);
+
+ if ((error = mod_remove(&modlinkage)) != 0)
+ return (error);
+
+ /*
+ * Tear down the operations vectors
+ */
+ (void) vfs_freevfsops_by_type(lxd_fstype);
+ vn_freevnodeops(lxd_vnodeops);
+ mutex_destroy(&lxd_xlate_lock);
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * Initialize global locks, etc. Called when loading lxd module.
+ */
+static int
+lxd_init(int fstype, char *name)
+{
+ static const fs_operation_def_t lxd_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = lxd_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = lxd_unmount },
+ VFSNAME_ROOT, { .vfs_root = lxd_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = lxd_statvfs },
+ VFSNAME_FREEVFS, { .vfs_freevfs = lxd_freevfs },
+ NULL, NULL
+ };
+ extern const struct fs_operation_def lxd_vnodeops_template[];
+ int error;
+ major_t dev;
+
+ lxd_fstype = fstype;
+ ASSERT(lxd_fstype != 0);
+
+ error = vfs_setfsops(fstype, lxd_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "lxd_init: bad vfs ops template");
+ return (error);
+ }
+
+ error = vn_make_ops(name, lxd_vnodeops_template, &lxd_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "lxd_init: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * lxd_minfree doesn't need to be some function of configured
+ * swap space since it really is an absolute limit of swap space
+ * which still allows other processes to execute.
+ */
+ if (lxd_minfree == 0) {
+ /* Set if not patched */
+ lxd_minfree = btopr(LXDMINFREE);
+ }
+
+ if ((dev = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "lxd_init: Can't get unique device number.");
+ dev = 0;
+ }
+
+ /*
+ * Make the pseudo device
+ */
+ lxd_dev = makedevice(dev, 0);
+
+ mutex_init(&lxd_xlate_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ return (0);
+}
+
+/*
+ * Initialize device translator mapping table.
+ *
+ * Note that we cannot do this in lxd_init since that can lead to a recursive
+ * rw_enter while we're doing lookupnameat (via sdev_lookup/prof_make_maps/
+ * devi_attach_node/modload). Thus we do it in the mount path and keep track
+ * so that we only initialize the table once.
+ */
+static void
+lxd_xlate_init()
+{
+ int i;
+
+ mutex_enter(&lxd_xlate_lock);
+ if (lxd_xlate_initialized) {
+ mutex_exit(&lxd_xlate_lock);
+ return;
+ }
+
+ for (i = 0; lxd_devt_translators[i].lxd_xl_driver != NULL; i++) {
+ lxd_minor_translator_t *mt;
+ int j;
+
+ lxd_devt_translators[i].lxd_xl_major =
+ mod_name_to_major(lxd_devt_translators[i].lxd_xl_driver);
+
+ /* if this translator doesn't use a list mapping we're done. */
+ if (lxd_devt_translators[i].lxd_xl_type != DTT_LIST)
+ continue;
+
+ /* for each device listed, lookup the minor node number */
+ mt = lxd_devt_translators[i].xl_list;
+ for (j = 0; mt[j].lxd_mt_path != NULL; j++) {
+ vnode_t *vp;
+ struct vattr va;
+ char *tpath;
+ char tnm[MAXPATHLEN];
+
+ /*
+ * The attach might be triggered in either the global
+ * zone or in a non-global zone, so we may need to
+ * adjust the path if we're in a NGZ.
+ */
+ if (curproc->p_zone->zone_id == GLOBAL_ZONEUNIQID) {
+ tpath = mt[j].lxd_mt_path;
+ } else {
+ (void) snprintf(tnm, sizeof (tnm), "/native%s",
+ mt[j].lxd_mt_path);
+ tpath = tnm;
+ }
+
+ if (lookupnameat(tpath, UIO_SYSSPACE, FOLLOW, NULL,
+ &vp, NULL) != 0) {
+ mt[j].lxd_mt_minor = UINT_MAX;
+ continue;
+ }
+
+ va.va_mask = AT_RDEV;
+ if (VOP_GETATTR(vp, &va, 0, kcred, NULL) != 0) {
+ va.va_rdev = NODEV;
+ } else {
+ ASSERT(getmajor(va.va_rdev) ==
+ lxd_devt_translators[i].lxd_xl_major);
+ ASSERT(mt[j].lxd_mt_lx_minor < LX_MAXMIN);
+ }
+
+ mt[j].lxd_mt_minor = getminor(va.va_rdev);
+
+ VN_RELE(vp);
+ }
+ }
+
+ lxd_xlate_initialized = B_TRUE;
+ mutex_exit(&lxd_xlate_lock);
+}
+
+static int
+lxd_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+ lxd_mnt_t *lxdm = NULL;
+ struct lxd_node *ldn;
+ struct pathname dpn;
+ int error;
+ int i;
+ int nodev;
+ struct vattr rattr;
+ vnode_t *realrootvp;
+ vnode_t *tvp;
+ lx_zone_data_t *lxzdata;
+ lx_virt_disk_t *vd;
+ vattr_t vattr;
+
+ nodev = vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL);
+
+ if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
+ return (error);
+
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ lxd_xlate_init();
+
+ /*
+ * This is the same behavior as with lofs.
+ * Loopback devices which get "nodevices" added can be done without
+ * "nodevices" set because we cannot import devices into a zone
+ * with loopback. Note that we have all zone privileges when
+ * this happens; if not, we'd have gotten "nosuid".
+ */
+ if (!nodev && vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL))
+ vfs_setmntopt(vfsp, MNTOPT_DEVICES, NULL, VFS_NODISPLAY);
+
+ /*
+ * Only allow mounting within lx zones.
+ */
+ if (curproc->p_zone->zone_brand != &lx_brand)
+ return (EINVAL);
+
+ /*
+ * Ensure we don't allow overlaying mounts
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /* lxd doesn't support read-only mounts */
+ if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ error = pn_get(uap->dir,
+ (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn);
+ if (error != 0)
+ goto out;
+
+ /*
+ * Find real root
+ */
+ if ((error = lookupname(uap->spec, (uap->flags & MS_SYSSPACE) ?
+ UIO_SYSSPACE : UIO_USERSPACE, FOLLOW, NULLVPP, &realrootvp))) {
+ pn_free(&dpn);
+ return (error);
+ }
+
+ if ((error = VOP_ACCESS(realrootvp, 0, 0, cr, NULL)) != 0) {
+ pn_free(&dpn);
+ VN_RELE(realrootvp);
+ return (error);
+ }
+
+ /* If realroot is not a devfs, error out */
+ if (strcmp(realrootvp->v_op->vnop_name, "dev") != 0) {
+ pn_free(&dpn);
+ VN_RELE(realrootvp);
+ return (EINVAL);
+ }
+
+ lxdm = kmem_zalloc(sizeof (*lxdm), KM_SLEEP);
+
+ /* init but don't bother entering the mutex (not on mount list yet) */
+ mutex_init(&lxdm->lxdm_contents, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&lxdm->lxdm_renamelck, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&lxdm->lxdm_attrlck, NULL, MUTEX_DEFAULT, NULL);
+
+ list_create(&lxdm->lxdm_devattrs, sizeof (lxd_dev_attr_t),
+ offsetof(lxd_dev_attr_t, lxda_link));
+
+ /* Initialize the hash table mutexes */
+ for (i = 0; i < LXD_HASH_SZ; i++) {
+ mutex_init(&lxdm->lxdm_hash_mutex[i], NULL, MUTEX_DEFAULT,
+ NULL);
+ }
+
+ lxdm->lxdm_vfsp = vfsp;
+ lxdm->lxdm_gen = 1; /* start inode counter at 1 */
+
+ vfsp->vfs_data = (caddr_t)lxdm;
+ vfsp->vfs_fstype = lxd_fstype;
+ vfsp->vfs_dev = lxd_dev;
+ vfsp->vfs_bsize = PAGESIZE;
+ vfsp->vfs_flag |= VFS_NOTRUNC;
+ vfs_make_fsid(&vfsp->vfs_fsid, lxd_dev, lxd_fstype);
+ lxdm->lxdm_mntpath = kmem_zalloc(dpn.pn_pathlen + 1, KM_SLEEP);
+ (void) strcpy(lxdm->lxdm_mntpath, dpn.pn_path);
+
+ /* allocate and initialize root lxd_node structure */
+ bzero(&rattr, sizeof (struct vattr));
+ rattr.va_mode = (mode_t)(S_IFDIR | 0755);
+ rattr.va_type = VDIR;
+ rattr.va_rdev = 0;
+
+ tvp = lxd_make_back_node(realrootvp, lxdm);
+ ldn = VTOLDN(tvp);
+
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+ LDNTOV(ldn)->v_flag |= VROOT;
+
+ /*
+ * initialize linked list of lxd_nodes so that the back pointer of
+ * the root lxd_node always points to the last one on the list
+ * and the forward pointer of the last node is null
+ */
+ ldn->lxdn_prev = ldn;
+ ldn->lxdn_next = NULL;
+ ldn->lxdn_nlink = 0;
+ lxdm->lxdm_rootnode = ldn;
+
+ ldn->lxdn_nodeid = lxdm->lxdm_gen++;
+ lxd_dirinit(ldn, ldn);
+
+ rw_exit(&ldn->lxdn_rwlock);
+
+ pn_free(&dpn);
+ error = 0;
+ atomic_inc_32(&lxd_mountcount);
+
+ lxzdata = ztolxzd(curproc->p_zone);
+ ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+ vattr.va_mask = AT_TYPE | AT_MODE;
+ vattr.va_type = VLNK;
+ vattr.va_mode = 0777;
+
+ vd = list_head(lxzdata->lxzd_vdisks);
+ while (vd != NULL) {
+ if (vd->lxvd_type == LXVD_ZVOL) {
+ char lnknm[MAXPATHLEN];
+
+ /* Create a symlink for the actual zvol. */
+ (void) snprintf(lnknm, sizeof (lnknm),
+ "./zvol/dsk/%s", vd->lxvd_real_name);
+ (void) lxd_symlink(LDNTOV(ldn), vd->lxvd_name, &vattr,
+ lnknm, cr, NULL, 0);
+ } else if (vd->lxvd_type == LXVD_ZFS_DS) {
+ /*
+ * Create a symlink for the root "disk" using /dev/zfs
+ * as the target device.
+ */
+ (void) lxd_symlink(LDNTOV(ldn), vd->lxvd_name, &vattr,
+ "./zfs", cr, NULL, 0);
+ }
+
+ vd = list_next(lxzdata->lxzd_vdisks, vd);
+ }
+
+ /* Apply any persistent attribute changes. */
+ lxd_apply_db(lxdm);
+
+out:
+ if (error == 0)
+ vfs_set_feature(vfsp, VFSFT_SYSATTR_VIEWS);
+
+ return (error);
+}
+
+static int
+lxd_unmount(struct vfs *vfsp, int flag, struct cred *cr)
+{
+ lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+ lxd_node_t *ldn, *cancel;
+ struct vnode *vp;
+ int error;
+ uint_t cnt;
+
+ if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
+ return (error);
+
+ mutex_enter(&lxdm->lxdm_contents);
+
+ /*
+ * In the normal unmount case only the root node would have a reference
+ * count.
+ *
+ * With lxdm_contents held, nothing can be added or removed.
+ * If we find a previously referenced node, undo the holds we have
+ * placed and fail EBUSY.
+ */
+ ldn = lxdm->lxdm_rootnode;
+
+ vp = LDNTOV(ldn);
+ mutex_enter(&vp->v_lock);
+
+ if (flag & MS_FORCE) {
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&lxdm->lxdm_contents);
+ return (EINVAL);
+ }
+
+ cnt = vp->v_count;
+ if (cnt > 1) {
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&lxdm->lxdm_contents);
+ return (EBUSY);
+ }
+
+ mutex_exit(&vp->v_lock);
+
+ /*
+ * Check for open files. An open file causes everything to unwind.
+ */
+ for (ldn = ldn->lxdn_next; ldn; ldn = ldn->lxdn_next) {
+ vp = LDNTOV(ldn);
+ mutex_enter(&vp->v_lock);
+ cnt = vp->v_count;
+ if (cnt > 0) {
+ /* An open file; unwind the holds we've been adding. */
+ mutex_exit(&vp->v_lock);
+ cancel = lxdm->lxdm_rootnode->lxdn_next;
+ while (cancel != ldn) {
+ vp = LDNTOV(cancel);
+ ASSERT(vp->v_count > 0);
+ VN_RELE(vp);
+ cancel = cancel->lxdn_next;
+ }
+ mutex_exit(&lxdm->lxdm_contents);
+ return (EBUSY);
+ } else {
+ /*
+ * It may seem incorrect for us to have a vnode with
+ * a count of 0, but this is modeled on tmpfs and works
+ * the same way. See lxd_front_inactive. There we allow
+ * the v_count to go to 0 but rely on the link count to
+ * keep the vnode alive. Since we now want to cleanup
+ * these vnodes we manually add a VN_HOLD so that the
+ * VN_RELEs that occur in the lxd_freevfs() cleanup
+ * will take us down the lxd_inactive code path. We
+ * can directly add a VN_HOLD since we have the lock.
+ */
+ vp->v_count++;
+ mutex_exit(&vp->v_lock);
+ }
+ }
+
+ /*
+ * We can drop the mutex now because
+ * no one can find this mount anymore
+ */
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+ mutex_exit(&lxdm->lxdm_contents);
+
+ return (0);
+}
+
+/*
+ * Implementation of VFS_FREEVFS(). This is called by the vfs framework after
+ * umount and the last VFS_RELE, to trigger the release of any resources still
+ * associated with the given vfs_t. This is normally called immediately after
+ * lxd_unmount.
+ */
+void
+lxd_freevfs(vfs_t *vfsp)
+{
+ lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+ lxd_node_t *ldn;
+ struct vnode *vp;
+ lxd_dev_attr_t *da;
+
+ /*
+ * Free all kmemalloc'd and anonalloc'd memory associated with
+ * this filesystem. To do this, we go through the file list twice,
+ * once to remove all the directory entries, and then to remove
+ * all the pseudo files.
+ */
+
+ /*
+ * Now that we are tearing ourselves down we need to remove the
+ * UNMOUNTED flag. If we don't, we'll later hit a VN_RELE when we remove
+ * files from the system causing us to have a negative value. Doing this
+ * seems a bit better than trying to set a flag on the lxd_mnt_t that
+ * says we're tearing down.
+ */
+ vfsp->vfs_flag &= ~VFS_UNMOUNTED;
+
+ /*
+ * Remove all directory entries (this doesn't remove top-level dirs).
+ */
+ for (ldn = lxdm->lxdm_rootnode; ldn; ldn = ldn->lxdn_next) {
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+ if (ldn->lxdn_vnode->v_type == VDIR)
+ lxd_dirtrunc(ldn);
+ rw_exit(&ldn->lxdn_rwlock);
+ }
+
+ ASSERT(lxdm->lxdm_rootnode != NULL);
+
+ /*
+ * All links are gone, v_count is keeping nodes in place.
+ * VN_RELE should make the node disappear, unless somebody
+ * is holding pages against it. Nap and retry until it disappears.
+ *
+ * We re-acquire the lock to prevent others who have a HOLD on a
+ * lxd_node from blowing it away (in lxd_inactive) while we're trying
+ * to get to it here. Once we have a HOLD on it we know it'll stick
+ * around.
+ */
+ mutex_enter(&lxdm->lxdm_contents);
+
+ /*
+ * Remove all the files (except the rootnode) backwards.
+ */
+ while ((ldn = lxdm->lxdm_rootnode->lxdn_prev) != lxdm->lxdm_rootnode) {
+ mutex_exit(&lxdm->lxdm_contents);
+ /*
+ * All nodes will be released here. Note we handled the link
+ * count above.
+ */
+ vp = LDNTOV(ldn);
+ ASSERT(vp->v_type == VLNK || vp->v_type == VDIR ||
+ vp->v_type == VSOCK);
+ VN_RELE(vp);
+ mutex_enter(&lxdm->lxdm_contents);
+ /*
+ * It's still there after the RELE. Someone else like pageout
+ * has a hold on it so wait a bit and then try again - we know
+ * they'll give it up soon.
+ */
+ if (ldn == lxdm->lxdm_rootnode->lxdn_prev) {
+ VN_HOLD(vp);
+ mutex_exit(&lxdm->lxdm_contents);
+ delay(hz / 4);
+ mutex_enter(&lxdm->lxdm_contents);
+ }
+ }
+ mutex_exit(&lxdm->lxdm_contents);
+
+ ASSERT(lxdm->lxdm_back_refcnt == 1);
+ ASSERT(lxdm->lxdm_dent_refcnt == 0);
+
+ VN_RELE(LDNTOV(lxdm->lxdm_rootnode));
+
+ ASSERT(lxdm->lxdm_mntpath != NULL);
+ kmem_free(lxdm->lxdm_mntpath, strlen(lxdm->lxdm_mntpath) + 1);
+
+ da = list_remove_head(&lxdm->lxdm_devattrs);
+ while (da != NULL) {
+ kmem_free(da, sizeof (lxd_dev_attr_t));
+ da = list_remove_head(&lxdm->lxdm_devattrs);
+ }
+ list_destroy(&lxdm->lxdm_devattrs);
+
+ mutex_destroy(&lxdm->lxdm_contents);
+ mutex_destroy(&lxdm->lxdm_renamelck);
+ mutex_destroy(&lxdm->lxdm_attrlck);
+ kmem_free(lxdm, sizeof (lxd_mnt_t));
+
+ /* Allow _fini() to succeed now */
+ atomic_dec_32(&lxd_mountcount);
+}
+
+/*
+ * return root lxdnode for given vnode
+ */
+static int
+lxd_root(struct vfs *vfsp, struct vnode **vpp)
+{
+ lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+ lxd_node_t *ldn = lxdm->lxdm_rootnode;
+ struct vnode *vp;
+
+ ASSERT(ldn != NULL);
+
+ vp = LDNTOV(ldn);
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+lxd_statvfs(struct vfs *vfsp, statvfs64_t *sbp)
+{
+ lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+ ulong_t blocks;
+ dev32_t d32;
+ zoneid_t eff_zid;
+ struct zone *zp;
+
+ zp = lxdm->lxdm_vfsp->vfs_zone;
+
+ if (zp == NULL)
+ eff_zid = GLOBAL_ZONEUNIQID;
+ else
+ eff_zid = zp->zone_id;
+
+ sbp->f_bsize = PAGESIZE;
+ sbp->f_frsize = PAGESIZE;
+
+ /*
+ * Find the amount of available physical and memory swap
+ */
+ mutex_enter(&anoninfo_lock);
+ ASSERT(k_anoninfo.ani_max >= k_anoninfo.ani_phys_resv);
+ blocks = (ulong_t)CURRENT_TOTAL_AVAILABLE_SWAP;
+ mutex_exit(&anoninfo_lock);
+
+ if (blocks > lxd_minfree)
+ sbp->f_bfree = blocks - lxd_minfree;
+ else
+ sbp->f_bfree = 0;
+
+ sbp->f_bavail = sbp->f_bfree;
+
+ /*
+ * Total number of blocks is just what's available
+ */
+ sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree);
+
+ if (eff_zid != GLOBAL_ZONEUNIQID &&
+ zp->zone_max_swap_ctl != UINT64_MAX) {
+ /*
+ * If the fs is used by a zone with a swap cap,
+ * then report the capped size.
+ */
+ rctl_qty_t cap, used;
+ pgcnt_t pgcap, pgused;
+
+ mutex_enter(&zp->zone_mem_lock);
+ cap = zp->zone_max_swap_ctl;
+ used = zp->zone_max_swap;
+ mutex_exit(&zp->zone_mem_lock);
+
+ pgcap = btop(cap);
+ pgused = btop(used);
+
+ sbp->f_bfree = MIN(pgcap - pgused, sbp->f_bfree);
+ sbp->f_bavail = sbp->f_bfree;
+ sbp->f_blocks = MIN(pgcap, sbp->f_blocks);
+ }
+
+ /*
+ * The maximum number of files available is approximately the number
+ * of lxd_nodes we can allocate from the remaining kernel memory
+ * available to lxdevfs in this zone. This is fairly inaccurate since
+ * it doesn't take into account the names stored in the directory
+ * entries.
+ */
+ sbp->f_ffree = sbp->f_files = ptob(availrmem) /
+ (sizeof (lxd_node_t) + sizeof (lxd_dirent_t));
+ sbp->f_favail = (fsfilcnt64_t)(sbp->f_ffree);
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sbp->f_fsid = d32;
+ (void) strcpy(sbp->f_basetype, vfssw[lxd_fstype].vsw_name);
+ (void) strncpy(sbp->f_fstr, lxdm->lxdm_mntpath, sizeof (sbp->f_fstr));
+ /* ensure null termination */
+ sbp->f_fstr[sizeof (sbp->f_fstr) - 1] = '\0';
+ sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sbp->f_namemax = MAXNAMELEN - 1;
+ return (0);
+}
+
+static void
+lxd_pts_devt_translator(dev_t dev, dev_t *jdev)
+{
+ minor_t min = getminor(dev);
+ int lx_maj, lx_min;
+
+ /*
+ * Linux uses a range of major numbers for pts devices to address the
+ * relatively small minor number space (20 bits).
+ */
+
+ lx_maj = LX_PTS_MAJOR_MIN + (min / LX_MAXMIN);
+ lx_min = min % LX_MAXMIN;
+ if (lx_maj > LX_PTS_MAJOR_MAX) {
+ /*
+ * The major is outside the acceptable range but there's little
+ * we can presently do about it short of overhauling the
+ * translation logic.
+ */
+ lx_unsupported("pts major out of translation range");
+ }
+
+ *jdev = LX_MAKEDEVICE(lx_maj, lx_min);
+}
+
+/* ARGSUSED */
+static void
+lxd_ptm_devt_translator(dev_t dev, dev_t *jdev)
+{
+ *jdev = LX_MAKEDEVICE(LX_PTM_MAJOR, LX_PTM_MINOR);
+}
diff --git a/usr/src/uts/common/brand/lx/devfs/lxd_vnops.c b/usr/src/uts/common/brand/lx/devfs/lxd_vnops.c
new file mode 100644
index 0000000000..c291e25797
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/devfs/lxd_vnops.c
@@ -0,0 +1,1520 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/cred.h>
+#include <sys/pathname.h>
+#include <sys/debug.h>
+#include <sys/sdt.h>
+#include <fs/fs_subr.h>
+#include <vm/as.h>
+#include <vm/seg.h>
+#include <sys/lx_brand.h>
+#include <sys/brand.h>
+
+#include "lxd.h"
+
+static int
+lxd_open(vnode_t **vpp, int flag, struct cred *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(*vpp);
+ vnode_t *vp = *vpp;
+ vnode_t *rvp;
+ vnode_t *oldvp;
+ int error;
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (0);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ oldvp = vp;
+ vp = rvp = REALVP(vp);
+ /*
+ * Need to hold new reference to vp since VOP_OPEN() may
+ * decide to release it.
+ */
+ VN_HOLD(vp);
+ error = VOP_OPEN(&rvp, flag, cr, ct);
+
+ if (!error && rvp != vp) {
+ /*
+ * the FS which we called should have released the
+ * new reference on vp
+ */
+ *vpp = lxd_make_back_node(rvp, VFSTOLXDM(oldvp->v_vfsp));
+
+ if (IS_DEVVP(*vpp)) {
+ vnode_t *svp;
+
+ svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
+ VN_RELE(*vpp);
+ if (svp == NULL)
+ error = ENOSYS;
+ else
+ *vpp = svp;
+ }
+ VN_RELE(oldvp);
+ } else {
+ ASSERT(rvp->v_count > 1);
+ VN_RELE(rvp);
+ }
+
+ return (error);
+}
+
+static int
+lxd_close(vnode_t *vp, int flag, int count, offset_t offset, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (0);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_CLOSE(vp, flag, count, offset, cr, ct));
+}
+
+static int
+lxd_read(vnode_t *vp, struct uio *uiop, int ioflag, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (ENOTSUP);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_READ(vp, uiop, ioflag, cr, ct));
+}
+
+static int
+lxd_write(vnode_t *vp, struct uio *uiop, int ioflag, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (ENOTSUP);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_WRITE(vp, uiop, ioflag, cr, ct));
+}
+
+static int
+lxd_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, struct cred *cr,
+ int *rvalp, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (ENOTSUP);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_IOCTL(vp, cmd, arg, flag, cr, rvalp, ct));
+}
+
+static int
+lxd_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (ENOTSUP);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_SETFL(vp, oflags, nflags, cr, ct));
+}
+
+/*
+ * Translate SunOS devt to Linux devt.
+ */
+static void
+lxd_s2l_devt(dev_t dev, dev_t *rdev)
+{
+ lxd_minor_translator_t *mt;
+ int i, j;
+ major_t maj = getmajor(dev);
+ minor_t min = getminor(dev);
+
+ /* look for a devt translator for this major number */
+ for (i = 0; lxd_devt_translators[i].lxd_xl_driver != NULL; i++) {
+ if (lxd_devt_translators[i].lxd_xl_major == maj)
+ break;
+ }
+
+ if (lxd_devt_translators[i].lxd_xl_driver != NULL) {
+ /* try to translate the illumos devt to a linux devt */
+ switch (lxd_devt_translators[i].lxd_xl_type) {
+ case DTT_INVALID:
+ ASSERT(0);
+ break;
+
+ case DTT_LIST:
+ mt = lxd_devt_translators[i].xl_list;
+ for (j = 0; mt[j].lxd_mt_path != NULL; j++) {
+ if (mt[j].lxd_mt_minor == min) {
+ ASSERT(mt[j].lxd_mt_minor < LX_MAXMIN);
+
+ /* found a translation */
+ *rdev = LX_MAKEDEVICE(
+ mt[j].lxd_mt_lx_major,
+ mt[j].lxd_mt_lx_minor);
+ return;
+ }
+ }
+ break;
+
+ case DTT_CUSTOM:
+ lxd_devt_translators[i].xl_custom(dev, rdev);
+ return;
+ }
+ }
+
+ /* we don't have a translator for this device */
+ *rdev = LX_MAKEDEVICE(maj, min);
+}
+
+static int
+lxd_getattr(vnode_t *vp, struct vattr *vap, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ int error;
+ vnode_t *rvp;
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ mutex_enter(&ldn->lxdn_tlock);
+
+ vap->va_type = vp->v_type;
+ vap->va_mode = ldn->lxdn_mode & MODEMASK;
+ vap->va_uid = ldn->lxdn_uid;
+ vap->va_gid = ldn->lxdn_gid;
+ vap->va_fsid = ldn->lxdn_fsid;
+ vap->va_nodeid = (ino64_t)ldn->lxdn_nodeid;
+ vap->va_nlink = ldn->lxdn_nlink;
+ vap->va_size = (u_offset_t)ldn->lxdn_size;
+ vap->va_atime = ldn->lxdn_atime;
+ vap->va_mtime = ldn->lxdn_mtime;
+ vap->va_ctime = ldn->lxdn_ctime;
+ vap->va_blksize = PAGESIZE;
+ vap->va_rdev = 0; /* no devs in front */
+ vap->va_seq = ldn->lxdn_seq;
+
+ vap->va_nblocks = (fsblkcnt64_t)btodb(ptob(btopr(
+ vap->va_size)));
+ mutex_exit(&ldn->lxdn_tlock);
+ return (0);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ rvp = REALVP(vp);
+ if ((error = VOP_GETATTR(rvp, vap, flags, cr, ct)))
+ return (error);
+
+ /* Skip devt translation for native programs */
+ if (curproc->p_brand != &lx_brand) {
+ return (0);
+ } else {
+ /*
+ * We also skip translation when called from the user-land
+ * emulation code.
+ */
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+
+ if (lwpd == NULL || lwpd->br_stack_mode != LX_STACK_MODE_BRAND)
+ return (0);
+ }
+
+ if (rvp->v_type == VCHR) {
+ dev_t ldev;
+
+ lxd_s2l_devt(vap->va_rdev, &ldev);
+ DTRACE_PROBE3(lxd__devxl, void *, rvp, void *, vap, int, ldev);
+ vap->va_rdev = ldev;
+ }
+
+ return (0);
+}
+
+static int
+lxd_setattr(vnode_t *vp, struct vattr *vap, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ lxd_mnt_t *lxdm = VTOLXDM(vp);
+ int res;
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ int error = 0;
+ struct vattr *set;
+ long mask = vap->va_mask;
+
+ /* Cannot set these attributes */
+ if ((mask & AT_NOSET) || (mask & AT_XVATTR) ||
+ (mask & AT_MODE && vap->va_mode & (S_ISUID | S_ISGID)) ||
+ (mask & AT_SIZE))
+ return (EINVAL);
+
+ mutex_enter(&ldn->lxdn_tlock);
+
+ set = &ldn->lxdn_attr;
+ /*
+ * Change file access modes. Must be owner or have sufficient
+ * privileges.
+ */
+ error = secpolicy_vnode_setattr(cr, vp, vap, set, flags,
+ lxd_naccess, ldn);
+ if (error) {
+ mutex_exit(&ldn->lxdn_tlock);
+ return (error);
+ }
+
+ if (mask & AT_MODE) {
+ set->va_mode &= S_IFMT;
+ set->va_mode |= vap->va_mode & ~S_IFMT;
+ }
+
+ if (mask & AT_UID)
+ set->va_uid = vap->va_uid;
+ if (mask & AT_GID)
+ set->va_gid = vap->va_gid;
+ if (mask & AT_ATIME)
+ set->va_atime = vap->va_atime;
+ if (mask & AT_MTIME)
+ set->va_mtime = vap->va_mtime;
+
+ if (mask & (AT_UID | AT_GID | AT_MODE | AT_MTIME))
+ gethrestime(&ldn->lxdn_ctime);
+
+ mutex_exit(&ldn->lxdn_tlock);
+ return (error);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ res = VOP_SETATTR(vp, vap, flags, cr, ct);
+ if (res == 0 && (vap->va_mask & (AT_MODE | AT_UID | AT_GID))) {
+ lxd_save_attrs(lxdm, vp);
+ }
+ return (res);
+}
+
+static int
+lxd_access(vnode_t *vp, int mode, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ int error;
+
+ mutex_enter(&ldn->lxdn_tlock);
+ error = lxd_naccess(ldn, mode, cr);
+ mutex_exit(&ldn->lxdn_tlock);
+ return (error);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ if (mode & VWRITE) {
+ if (vp->v_type == VREG && vn_is_readonly(vp))
+ return (EROFS);
+ }
+ vp = REALVP(vp);
+ return (VOP_ACCESS(vp, mode, flags, cr, ct));
+}
+
+static int
+lxd_fsync(vnode_t *vp, int syncflag, struct cred *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (0);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_FSYNC(vp, syncflag, cr, ct));
+}
+
+/* ARGSUSED */
+static void
+lxd_front_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ lxd_mnt_t *lxdm = VTOLXDM(vp);
+
+ ASSERT(ldn->lxdn_type == LXDNT_FRONT);
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+
+ mutex_enter(&ldn->lxdn_tlock);
+ mutex_enter(&vp->v_lock);
+ ASSERT(vp->v_count >= 1);
+
+ /*
+ * If we don't have the last hold or the link count is non-zero,
+ * there's little to do -- just drop our hold.
+ */
+ if (vp->v_count > 1 || ldn->lxdn_nlink != 0) {
+ vp->v_count--;
+
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&ldn->lxdn_tlock);
+ rw_exit(&ldn->lxdn_rwlock);
+ return;
+ }
+
+ /*
+ * We have the last hold *and* the link count is zero, so this node is
+ * dead from the filesystem's viewpoint.
+ */
+ if (ldn->lxdn_size != 0) {
+ if (ldn->lxdn_vnode->v_type == VLNK)
+ kmem_free(ldn->lxdn_symlink, ldn->lxdn_size + 1);
+ }
+
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&ldn->lxdn_tlock);
+
+ vn_invalid(LDNTOV(ldn));
+
+ mutex_enter(&lxdm->lxdm_contents);
+ if (ldn->lxdn_next == NULL)
+ lxdm->lxdm_rootnode->lxdn_prev = ldn->lxdn_prev;
+ else
+ ldn->lxdn_next->lxdn_prev = ldn->lxdn_prev;
+ ldn->lxdn_prev->lxdn_next = ldn->lxdn_next;
+
+ mutex_exit(&lxdm->lxdm_contents);
+ rw_exit(&ldn->lxdn_rwlock);
+ rw_destroy(&ldn->lxdn_rwlock);
+ mutex_destroy(&ldn->lxdn_tlock);
+
+ vn_free(LDNTOV(ldn));
+ kmem_free(ldn, sizeof (lxd_node_t));
+}
+
+/*ARGSUSED*/
+static void
+lxd_inactive(vnode_t *vp, struct cred *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ lxd_front_inactive(vp, cr, ct);
+ return;
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ lxd_free_back_node(ldn);
+}
+
+/* ARGSUSED */
+static int
+lxd_fid(vnode_t *vp, struct fid *fidp, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT)
+ return (ENOTSUP);
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_FID(vp, fidp, ct));
+}
+
+/*
+ * For a front node lookup in the dirent hash table and return a shadow vnode
+ * (lxd_node_t type) of type LXDNT_FRONT.
+ *
+ * For a back node, lookup nm name and return a shadow vnode (lxd_node_t type)
+ * of the real vnode found.
+ */
+static int
+lxd_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
+ int flags, vnode_t *rdir, struct cred *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ vnode_t *vp = NULL;
+ int error;
+ vnode_t *realdvp;
+ lxd_mnt_t *lxdm = VTOLXDM(dvp);
+ int doingdotdot = 0;
+ lxd_node_t *ldn = VTOLDN(dvp);
+ lxd_node_t *nldn = NULL;
+
+ /*
+ * First check for front file which could be instantiated on either a
+ * front or back node (e.g. the top-level moint point directory node is
+ * a back node which can have front files created in it).
+ */
+
+ /* disallow extended attrs */
+ if (flags & LOOKUP_XATTR)
+ return (EINVAL);
+
+ /* Null component name is a synonym for dir being searched. */
+ if (*nm == '\0') {
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+
+ rw_enter(&ldn->lxdn_rwlock, RW_READER);
+ error = lxd_dirlookup(ldn, nm, &nldn, cr);
+ rw_exit(&ldn->lxdn_rwlock);
+
+ if (error == 0) {
+ /* found */
+ ASSERT(nldn != NULL);
+ *vpp = LDNTOV(nldn);
+ return (0);
+ }
+
+ /* At this point, if dir node is a front node, error */
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (ENOENT);
+ }
+
+ realdvp = REALVP(dvp);
+
+ if (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0') {
+ doingdotdot++;
+ /*
+ * Handle ".." out of mounted filesystem
+ */
+ while ((realdvp->v_flag & VROOT) && realdvp != rootdir) {
+ realdvp = realdvp->v_vfsp->vfs_vnodecovered;
+ ASSERT(realdvp != NULL);
+ }
+ }
+
+ *vpp = NULL; /* default(error) case */
+
+ /*
+ * Do the normal lookup
+ */
+ if ((error = VOP_LOOKUP(realdvp, nm, &vp, pnp, flags, rdir, cr,
+ ct, direntflags, realpnp)) != 0) {
+ vp = NULL;
+ goto out;
+ }
+
+ /*
+ * We do this check here to avoid returning a stale file handle to the
+ * caller.
+ */
+ if (nm[0] == '.' && nm[1] == '\0') {
+ ASSERT(vp == realdvp);
+ VN_HOLD(dvp);
+ VN_RELE(vp);
+ *vpp = dvp;
+ return (0);
+ }
+
+ if (doingdotdot) {
+ *vpp = lxd_make_back_node(vp, lxdm);
+ return (0);
+ }
+
+ /*
+ * If this vnode is mounted on, then we
+ * traverse to the vnode which is the root of
+ * the mounted file system.
+ */
+ if ((error = traverse(&vp)) != 0)
+ goto out;
+
+ /*
+ * Make a lxd node for the real vnode.
+ */
+ *vpp = lxd_make_back_node(vp, lxdm);
+ if (vp->v_type != VDIR) {
+ if (IS_DEVVP(*vpp)) {
+ vnode_t *svp;
+
+ svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
+ VN_RELE(*vpp);
+ if (svp == NULL) {
+ VN_RELE(vp);
+ error = ENOSYS;
+ } else {
+ *vpp = svp;
+ }
+ }
+ return (error);
+ }
+
+out:
+ if (error != 0 && vp != NULL)
+ VN_RELE(vp);
+
+ return (error);
+}
+
+/*ARGSUSED*/
+static int
+lxd_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
+ int mode, vnode_t **vpp, struct cred *cr, int flag, caller_context_t *ct,
+ vsecattr_t *vsecp)
+{
+ int error;
+ lxd_node_t *parent = VTOLDN(dvp);
+ lxd_node_t *lnp = NULL;
+
+ rw_enter(&parent->lxdn_rwlock, RW_READER);
+ error = lxd_dirlookup(parent, nm, &lnp, cr);
+ rw_exit(&parent->lxdn_rwlock);
+
+ /*
+ * If a back node already exists then there is no need to pass
+ * the create to native devfs -- just set the vpp to the back
+ * vnode. If the front node already exists then fail because
+ * it can't represent a regular file. In both cases, enforce
+ * open(2)'s EEXIST and EISDIR semantics.
+ */
+ if (error == 0) {
+ if (exclusive == EXCL) {
+ error = EEXIST;
+ } else if (LDNTOV(lnp)->v_type == VDIR &&
+ (mode & S_IWRITE)) {
+ error = EISDIR;
+ } else if (lnp->lxdn_type == LXDNT_FRONT) {
+ error = ENOTSUP;
+ }
+
+ if (error != 0) {
+ ldnode_rele(lnp);
+ return (error);
+ }
+
+ VERIFY3S(lnp->lxdn_type, ==, LXDNT_BACK);
+ *vpp = lnp->lxdn_vnode;
+
+ return (error);
+ }
+
+ /*
+ * We cannot create files in the back devfs but we want to allow for
+ * O_CREAT on existing files. Pass this through and let the back file
+ * system allow or deny it.
+ */
+ if (parent->lxdn_type == LXDNT_BACK) {
+ vnode_t *vp = NULL;
+
+ if (*nm == '\0') {
+ ASSERT(vpp && dvp == *vpp);
+ vp = REALVP(*vpp);
+ }
+ if ((error = VOP_CREATE(REALVP(dvp), nm, va, exclusive, mode,
+ &vp, cr, flag, ct, vsecp)) == 0) {
+ *vpp = lxd_make_back_node(vp, VFSTOLXDM(dvp->v_vfsp));
+ if (IS_DEVVP(*vpp)) {
+ vnode_t *svp;
+
+ svp = specvp(*vpp, (*vpp)->v_rdev,
+ (*vpp)->v_type, cr);
+ VN_RELE(*vpp);
+ if (svp == NULL) {
+ return (ENOSYS);
+ }
+ *vpp = svp;
+ }
+ return (0);
+ }
+ /*
+ * If we were unable to perform the VOP_CREATE for any reason
+ * other than sdev being read-only, we should bail.
+ */
+ if (error != ENOTSUP && error != EROFS) {
+ return (error);
+ }
+ }
+
+ /*
+ * While we don't allow creating data-containing files under
+ * lx devfs, we must allow VSOCK front nodes to be created so
+ * that paths such as /dev/log can be used as AF_UNIX sockets.
+ */
+ if (va->va_type == VSOCK) {
+ lxd_mnt_t *lxdm = VTOLXDM(parent->lxdn_vnode);
+
+ lnp = NULL;
+ rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+ error = lxd_direnter(lxdm, parent, nm, DE_CREATE, NULL, NULL,
+ va, &lnp, cr);
+ rw_exit(&parent->lxdn_rwlock);
+
+ if (error == 0) {
+ *vpp = LDNTOV(lnp);
+ } else if (lnp != NULL) {
+ /*
+ * It's possible that a racing process created an entry
+ * at this name since we last performed the lookup.
+ */
+ ldnode_rele(lnp);
+ }
+ } else {
+ error = ENOTSUP;
+ }
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+lxd_remove(vnode_t *dvp, char *nm, struct cred *cr, caller_context_t *ct,
+ int flags)
+{
+ lxd_node_t *parent = VTOLDN(dvp);
+ lxd_node_t *ldn = NULL;
+ int error;
+
+ /* can only remove existing front nodes */
+ error = lxd_dirlookup(parent, nm, &ldn, cr);
+ if (error) {
+ return (error);
+ }
+
+ ASSERT(ldn != NULL);
+ ASSERT(ldn->lxdn_type == LXDNT_FRONT);
+ rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+
+ error = lxd_dirdelete(parent, ldn, nm, DR_REMOVE, cr);
+
+ rw_exit(&ldn->lxdn_rwlock);
+ rw_exit(&parent->lxdn_rwlock);
+
+ ldnode_rele(ldn);
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+lxd_link(vnode_t *tdvp, vnode_t *vp, char *tnm, struct cred *cr,
+ caller_context_t *ct, int flags)
+{
+ return (ENOTSUP);
+}
+
+/* ARGSUSED */
+static int
+lxd_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, struct cred *cr,
+ caller_context_t *ct, int flags)
+{
+ lxd_node_t *oldparent = VTOLDN(odvp);
+ lxd_node_t *newparent;
+ lxd_mnt_t *lxdm = VTOLXDM(oldparent->lxdn_vnode);
+ lxd_node_t *fromnode = NULL;
+ int error;
+ int samedir = 0;
+
+ if (!vn_matchops(ndvp, lxd_vnodeops)) {
+ /* cannot rename out of this file system */
+ return (EACCES);
+ }
+
+ mutex_enter(&lxdm->lxdm_renamelck);
+
+ newparent = VTOLDN(ndvp);
+
+ /*
+ * We can only rename front nodes.
+ */
+ error = lxd_dirlookup(oldparent, onm, &fromnode, cr);
+ if (error != 0) {
+ /* not found in front */
+ mutex_exit(&lxdm->lxdm_renamelck);
+ return (error);
+ }
+
+ /*
+ * Make sure we can delete the old (source) entry. This
+ * requires write permission on the containing directory. If
+ * that directory is "sticky" it requires further checks.
+ */
+ if ((error = lxd_naccess(oldparent, VWRITE, cr)) != 0)
+ goto done;
+
+ /*
+ * Check for renaming to or from '.' or '..' or that
+ * fromnode == oldparent
+ */
+ if ((onm[0] == '.' &&
+ (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) ||
+ (nnm[0] == '.' &&
+ (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0'))) ||
+ (oldparent == fromnode)) {
+ error = EINVAL;
+ goto done;
+ }
+
+ samedir = (oldparent == newparent);
+
+ /*
+ * Make sure we can search and rename into the destination directory.
+ */
+ if (!samedir) {
+ if ((error = lxd_naccess(newparent, VEXEC|VWRITE, cr)) != 0)
+ goto done;
+ }
+
+ /*
+ * Link source to new target
+ */
+ rw_enter(&newparent->lxdn_rwlock, RW_WRITER);
+ error = lxd_direnter(lxdm, newparent, nnm, DE_RENAME,
+ oldparent, fromnode, (struct vattr *)NULL, (lxd_node_t **)NULL,
+ cr);
+ rw_exit(&newparent->lxdn_rwlock);
+
+ if (error)
+ goto done;
+
+ /*
+ * Unlink from source.
+ */
+ rw_enter(&oldparent->lxdn_rwlock, RW_WRITER);
+ rw_enter(&fromnode->lxdn_rwlock, RW_WRITER);
+
+ error = lxd_dirdelete(oldparent, fromnode, onm, DR_RENAME, cr);
+
+ /*
+ * The following handles the case where our source node was
+ * removed before we got to it.
+ */
+ if (error == ENOENT)
+ error = 0;
+
+ rw_exit(&fromnode->lxdn_rwlock);
+ rw_exit(&oldparent->lxdn_rwlock);
+
+done:
+ ldnode_rele(fromnode);
+ mutex_exit(&lxdm->lxdm_renamelck);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+lxd_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
+ struct cred *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+ int error;
+ vnode_t *tvp;
+ lxd_node_t *ndir = NULL;
+ lxd_node_t *parent = VTOLDN(dvp);
+ lxd_mnt_t *lxdm = VTOLXDM(parent->lxdn_vnode);
+
+ /* check for existence in both front and back */
+ if (lxd_lookup(dvp, nm, &tvp, NULL, 0, NULL, cr, ct, NULL, NULL) == 0) {
+ /* The entry already exists */
+ VN_RELE(tvp);
+ return (EEXIST);
+ }
+
+ /* make front directory */
+ rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+ error = lxd_direnter(lxdm, parent, nm, DE_MKDIR, NULL, NULL,
+ va, &ndir, cr);
+ rw_exit(&parent->lxdn_rwlock);
+
+ if (error != 0) {
+ if (ndir != NULL)
+ ldnode_rele(ndir);
+ } else {
+ *vpp = LDNTOV(ndir);
+ }
+
+ return (error);
+}
+
+static int
+lxd_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ *vpp = vp;
+ return (0);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ while (vn_matchops(vp, lxd_vnodeops))
+ vp = REALVP(vp);
+
+ if (VOP_REALVP(vp, vpp, ct) != 0)
+ *vpp = vp;
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+lxd_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, struct cred *cr,
+ caller_context_t *ct, int flags)
+{
+ int error;
+ lxd_node_t *ldn;
+ struct vnode *vp;
+ lxd_node_t *parent = VTOLDN(dvp);
+
+ /*
+ * Return error if trying to remove . or ..
+ */
+ if (strcmp(nm, ".") == 0)
+ return (EINVAL);
+ if (strcmp(nm, "..") == 0)
+ return (EEXIST);
+
+ error = lxd_dirlookup(VTOLDN(dvp), nm, &ldn, cr);
+ if (error != 0) {
+ /* not found in front */
+ return (error);
+ }
+
+ rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+
+ vp = LDNTOV(ldn);
+ if (vp == dvp || vp == cdir) {
+ error = EINVAL;
+ goto err;
+ }
+
+ if (ldn->lxdn_vnode->v_type != VDIR) {
+ error = ENOTDIR;
+ goto err;
+ }
+
+ mutex_enter(&ldn->lxdn_tlock);
+ if (ldn->lxdn_nlink > 2) {
+ mutex_exit(&ldn->lxdn_tlock);
+ error = EEXIST;
+ goto err;
+ }
+ mutex_exit(&ldn->lxdn_tlock);
+
+ /* Check for an empty directory */
+ if (ldn->lxdn_dirents > 2) {
+ error = EEXIST;
+ gethrestime(&ldn->lxdn_atime);
+ goto err;
+ }
+
+ if (vn_vfswlock(vp)) {
+ error = EBUSY;
+ goto err;
+ }
+ if (vn_mountedvfs(vp) != NULL) {
+ error = EBUSY;
+ vn_vfsunlock(vp);
+ goto err;
+ }
+
+ error = lxd_dirdelete(parent, ldn, nm, DR_RMDIR, cr);
+ vn_vfsunlock(vp);
+
+err:
+ rw_exit(&ldn->lxdn_rwlock);
+ rw_exit(&parent->lxdn_rwlock);
+ ldnode_rele(ldn);
+
+ return (error);
+}
+
+/* Not static so it can be used during mount. */
+/* ARGSUSED */
+int
+lxd_symlink(vnode_t *dvp, char *nm, struct vattr *tva, char *tnm,
+ struct cred *cr, caller_context_t *ct, int flags)
+{
+ lxd_node_t *parent = VTOLDN(dvp);
+ lxd_mnt_t *lxdm = VTOLXDM(parent->lxdn_vnode);
+ lxd_node_t *self = NULL;
+ vnode_t *tvp;
+ char *cp = NULL;
+ int error;
+ size_t len;
+
+ /* this will check for existence in both front and back */
+ if (lxd_lookup(dvp, nm, &tvp, NULL, 0, NULL, cr, ct, NULL, NULL) == 0) {
+ /* The entry already exists */
+ VN_RELE(tvp);
+ return (EEXIST);
+ }
+
+ /* make symlink in the front */
+ rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+ error = lxd_direnter(lxdm, parent, nm, DE_CREATE, NULL, NULL,
+ tva, &self, cr);
+ rw_exit(&parent->lxdn_rwlock);
+
+ if (error) {
+ if (self != NULL)
+ ldnode_rele(self);
+ return (error);
+ }
+
+ len = strlen(tnm) + 1;
+ cp = kmem_alloc(len, KM_NOSLEEP_LAZY);
+ if (cp == NULL) {
+ ldnode_rele(self);
+ return (ENOSPC);
+ }
+ (void) strcpy(cp, tnm);
+
+ self->lxdn_symlink = cp;
+ self->lxdn_size = len - 1;
+ ldnode_rele(self);
+
+ return (error);
+}
+
+static int
+lxd_readlink(vnode_t *vp, struct uio *uiop, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ int error;
+
+ if (vp->v_type != VLNK)
+ return (EINVAL);
+
+ rw_enter(&ldn->lxdn_rwlock, RW_READER);
+ error = uiomove(ldn->lxdn_symlink, ldn->lxdn_size, UIO_READ,
+ uiop);
+ gethrestime(&ldn->lxdn_atime);
+ rw_exit(&ldn->lxdn_rwlock);
+ return (error);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_READLINK(vp, uiop, cr, ct));
+}
+
+static int
+lx_merge_front(vnode_t *vp, struct uio *uiop, off_t req_off, int *eofp)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ struct dirent *sd;
+ lxd_dirent_t *ldp;
+ enum lxd_node_type type = ldn->lxdn_type;
+ ssize_t uresid;
+ off_t front_off;
+ int error = 0;
+ int sdlen;
+
+ /* skip the front entries if the back read was incomplete */
+ if (*eofp == 0)
+ return (0);
+
+ /*
+ * If this was a back node then reading that node has completed and we
+ * may have a partially full uio struct. eof should be set to true.
+ * Leave it set since we're likely to hit eof for the front nodes (if
+ * any).
+ */
+
+ front_off = uiop->uio_offset + 1;
+ sdlen = sizeof (struct dirent) + MAXPATHLEN;
+ /* zalloc to ensure we don't have anything in the d_name buffer */
+ sd = (struct dirent *)kmem_zalloc(sdlen, KM_SLEEP);
+ ldp = ldn->lxdn_dir;
+ while (ldp != NULL && (uresid = uiop->uio_resid) > 0) {
+ int namelen;
+ int reclen;
+
+ /*
+ * Skip dot and dotdot for back nodes since we have them
+ * already.
+ */
+ if (type == LXDNT_BACK &&
+ (strcmp(ldp->lddir_name, ".") == 0 ||
+ strcmp(ldp->lddir_name, "..") == 0)) {
+ ldp = ldp->lddir_next;
+ continue;
+ }
+
+ /*
+ * Might have previously had a partial readdir of the front
+ * nodes, and now we're back for more, or we may just be
+ * be doing a follow-up readdir after we've previously
+ * returned all front and back nodes.
+ */
+ if (front_off > req_off) {
+ namelen = strlen(ldp->lddir_name); /* no +1 needed */
+ reclen = (int)DIRENT64_RECLEN(namelen);
+
+ /*
+ * If the size of the data to transfer is greater
+ * than that requested, then we can't do it this
+ * transfer.
+ */
+ if (reclen > uresid) {
+ *eofp = 0;
+ /* Buffer too small for any entries. */
+ if (front_off == 0)
+ error = EINVAL;
+ break;
+ }
+
+ (void) strncpy(sd->d_name, ldp->lddir_name,
+ DIRENT64_NAMELEN(reclen));
+ sd->d_reclen = (ushort_t)reclen;
+ sd->d_ino = (ino_t)ldp->lddir_node->lxdn_nodeid;
+ sd->d_off = front_off;
+
+ /* uiomove will adjust iov_base properly */
+ if ((error = uiomove((caddr_t)sd, reclen, UIO_READ,
+ uiop)) != 0) {
+ *eofp = 0;
+ break;
+ }
+ }
+
+ /*
+ * uiomove() above updates both uio_resid and uio_offset by the
+ * same amount but we want uio_offset to change in increments
+ * of 1, which is different from the number of bytes being
+ * returned to the caller, so we set uio_offset explicitly,
+ * ignoring what uiomove() did.
+ */
+ uiop->uio_offset = front_off;
+ front_off++;
+
+ ldp = ldp->lddir_next;
+ }
+
+ kmem_free(sd, sdlen);
+ return (error);
+}
+
+static int
+lxd_readdir(vnode_t *vp, struct uio *uiop, struct cred *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ vnode_t *rvp;
+ int res;
+ off_t req_off;
+
+ if (uiop->uio_iovcnt != 1)
+ return (EINVAL);
+
+ if (vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ req_off = uiop->uio_offset;
+
+ /* First read the back node (if it is one) */
+ if (ldn->lxdn_type == LXDNT_BACK) {
+ rvp = REALVP(vp);
+ res = VOP_READDIR(rvp, uiop, cr, eofp, ct, flags);
+ if (res != 0)
+ return (res);
+ } else {
+ /* setup for merge_front */
+ ASSERT(ldn->lxdn_type == LXDNT_FRONT);
+ /* caller should have already called lxd_rwlock */
+ ASSERT(RW_READ_HELD(&ldn->lxdn_rwlock));
+
+ *eofp = 1;
+ /*
+ * The merge code starts the offset calculation from uio_offset,
+ * which is normally already set to the high value by the back
+ * code, but in this case we need to count up from 0.
+ */
+ uiop->uio_offset = 0;
+ }
+
+ /*
+ * Our back nodes can also have front entries hanging on them so we
+ * need to merge those in. Or, we may simply have a front node (i.e. a
+ * front subdir).
+ */
+ res = lx_merge_front(vp, uiop, req_off, eofp);
+ return (res);
+}
+
+static int
+lxd_rwlock(vnode_t *vp, int write_lock, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ if (write_lock) {
+ rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+ } else {
+ rw_enter(&ldn->lxdn_rwlock, RW_READER);
+ }
+ return (write_lock);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_RWLOCK(vp, write_lock, ct));
+}
+
+static void
+lxd_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ rw_exit(&ldn->lxdn_rwlock);
+ return;
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ VOP_RWUNLOCK(vp, write_lock, ct);
+}
+
+static int
+lxd_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_SEEK(vp, ooff, noffp, ct));
+}
+
+static int
+lxd_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+ while (vn_matchops(vp1, lxd_vnodeops) &&
+ VTOLDN(vp1)->lxdn_type == LXDNT_BACK) {
+ vp1 = REALVP(vp1);
+ }
+ while (vn_matchops(vp2, lxd_vnodeops) &&
+ VTOLDN(vp2)->lxdn_type == LXDNT_BACK) {
+ vp2 = REALVP(vp2);
+ }
+
+ if (vn_matchops(vp1, lxd_vnodeops) || vn_matchops(vp2, lxd_vnodeops))
+ return (vp1 == vp2);
+
+ return (VOP_CMP(vp1, vp2, ct));
+}
+
+static int
+lxd_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, offset_t offset,
+ struct flk_callback *flk_cbp, cred_t *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_FRLOCK(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct));
+}
+
+static int
+lxd_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, offset_t offset,
+ struct cred *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_SPACE(vp, cmd, bfp, flag, offset, cr, ct));
+}
+
+static int
+lxd_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *prot,
+ struct page *parr[], size_t psz, struct seg *seg, caddr_t addr,
+ enum seg_rw rw, struct cred *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_GETPAGE(vp, off, len, prot, parr, psz, seg, addr, rw, cr,
+ ct));
+}
+
+static int
+lxd_putpage(vnode_t *vp, offset_t off, size_t len, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_PUTPAGE(vp, off, len, flags, cr, ct));
+}
+
+static int
+lxd_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, size_t len,
+ uchar_t prot, uchar_t maxprot, uint_t flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_MAP(vp, off, as, addrp, len, prot, maxprot, flags, cr, ct));
+}
+
+static int
+lxd_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len,
+ uchar_t prot, uchar_t maxprot, uint_t flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_ADDMAP(vp, off, as, addr, len, prot, maxprot, flags, cr,
+ ct));
+}
+
+static int
+lxd_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len,
+ uint_t prot, uint_t maxprot, uint_t flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_DELMAP(vp, off, as, addr, len, prot, maxprot, flags, cr,
+ ct));
+}
+
+static int
+lxd_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_POLL(vp, events, anyyet, reventsp, phpp, ct));
+}
+
+static int
+lxd_dump(vnode_t *vp, caddr_t addr, offset_t bn, offset_t count,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_DUMP(vp, addr, bn, count, ct));
+}
+
+static int
+lxd_pathconf(vnode_t *vp, int cmd, ulong_t *valp, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_PATHCONF(vp, cmd, valp, cr, ct));
+}
+
+static int
+lxd_pageio(vnode_t *vp, struct page *pp, u_offset_t io_off, size_t io_len,
+ int flags, cred_t *cr, caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_PAGEIO(vp, pp, io_off, io_len, flags, cr, ct));
+}
+
+static void
+lxd_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return;
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ if (vp != NULL && !VN_ISKAS(vp))
+ VOP_DISPOSE(vp, pp, fl, dn, cr, ct);
+}
+
+static int
+lxd_setsecattr(vnode_t *vp, vsecattr_t *secattr, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (ENOSYS);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ if (vn_is_readonly(vp))
+ return (EROFS);
+
+ vp = REALVP(vp);
+ return (VOP_SETSECATTR(vp, secattr, flags, cr, ct));
+}
+
+static int
+lxd_getsecattr(vnode_t *vp, vsecattr_t *secattr, int flags, struct cred *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (ENOSYS);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_GETSECATTR(vp, secattr, flags, cr, ct));
+}
+
+static int
+lxd_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxd_node_t *ldn = VTOLDN(vp);
+
+ if (ldn->lxdn_type == LXDNT_FRONT) {
+ return (EINVAL);
+ }
+
+ ASSERT(ldn->lxdn_type == LXDNT_BACK);
+ vp = REALVP(vp);
+ return (VOP_SHRLOCK(vp, cmd, shr, flag, cr, ct));
+}
+
+/*
+ * Loopback vnode operations vector.
+ */
+
+struct vnodeops *lxd_vnodeops;
+
+const fs_operation_def_t lxd_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = lxd_open },
+ VOPNAME_CLOSE, { .vop_close = lxd_close },
+ VOPNAME_READ, { .vop_read = lxd_read },
+ VOPNAME_WRITE, { .vop_write = lxd_write },
+ VOPNAME_IOCTL, { .vop_ioctl = lxd_ioctl },
+ VOPNAME_SETFL, { .vop_setfl = lxd_setfl },
+ VOPNAME_GETATTR, { .vop_getattr = lxd_getattr },
+ VOPNAME_SETATTR, { .vop_setattr = lxd_setattr },
+ VOPNAME_ACCESS, { .vop_access = lxd_access },
+ VOPNAME_LOOKUP, { .vop_lookup = lxd_lookup },
+ VOPNAME_CREATE, { .vop_create = lxd_create },
+ VOPNAME_REMOVE, { .vop_remove = lxd_remove },
+ VOPNAME_LINK, { .vop_link = lxd_link },
+ VOPNAME_RENAME, { .vop_rename = lxd_rename },
+ VOPNAME_MKDIR, { .vop_mkdir = lxd_mkdir },
+ VOPNAME_RMDIR, { .vop_rmdir = lxd_rmdir },
+ VOPNAME_READDIR, { .vop_readdir = lxd_readdir },
+ VOPNAME_SYMLINK, { .vop_symlink = lxd_symlink },
+ VOPNAME_READLINK, { .vop_readlink = lxd_readlink },
+ VOPNAME_FSYNC, { .vop_fsync = lxd_fsync },
+ VOPNAME_INACTIVE, { .vop_inactive = lxd_inactive },
+ VOPNAME_FID, { .vop_fid = lxd_fid },
+ VOPNAME_RWLOCK, { .vop_rwlock = lxd_rwlock },
+ VOPNAME_RWUNLOCK, { .vop_rwunlock = lxd_rwunlock },
+ VOPNAME_SEEK, { .vop_seek = lxd_seek },
+ VOPNAME_CMP, { .vop_cmp = lxd_cmp },
+ VOPNAME_FRLOCK, { .vop_frlock = lxd_frlock },
+ VOPNAME_SPACE, { .vop_space = lxd_space },
+ VOPNAME_REALVP, { .vop_realvp = lxd_realvp },
+ VOPNAME_GETPAGE, { .vop_getpage = lxd_getpage },
+ VOPNAME_PUTPAGE, { .vop_putpage = lxd_putpage },
+ VOPNAME_MAP, { .vop_map = lxd_map },
+ VOPNAME_ADDMAP, { .vop_addmap = lxd_addmap },
+ VOPNAME_DELMAP, { .vop_delmap = lxd_delmap },
+ VOPNAME_POLL, { .vop_poll = lxd_poll },
+ VOPNAME_DUMP, { .vop_dump = lxd_dump },
+ VOPNAME_DUMPCTL, { .error = fs_error },
+ VOPNAME_PATHCONF, { .vop_pathconf = lxd_pathconf },
+ VOPNAME_PAGEIO, { .vop_pageio = lxd_pageio },
+ VOPNAME_DISPOSE, { .vop_dispose = lxd_dispose },
+ VOPNAME_SETSECATTR, { .vop_setsecattr = lxd_setsecattr },
+ VOPNAME_GETSECATTR, { .vop_getsecattr = lxd_getsecattr },
+ VOPNAME_SHRLOCK, { .vop_shrlock = lxd_shrlock },
+ NULL, NULL
+};
diff --git a/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c b/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c
new file mode 100644
index 0000000000..a55b436088
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c
@@ -0,0 +1,499 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/frame.h>
+#include <sys/dtrace.h>
+#include <sys/dtrace_impl.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+
+/*
+ * We store the syscall number in the low 16 bits (which limits us to 64k
+ * syscalls). The next bit indicates entry/return probe and the next bit
+ * indicates 64bit/32bit syscall.
+ */
+#define SCALL_MASK 0xffff
+#define ENTRY_FLAG 0x10000
+#define SYSC_64_BIT 0x100000
+
+#define LX_SYSTRACE_IS64BIT(x) ((int)(x) & SYSC_64_BIT)
+#define LX_SYSTRACE_ISENTRY(x) ((int)(x) & ENTRY_FLAG)
+#define LX_SYSTRACE_SYSNUM(x) ((int)(x) & SCALL_MASK)
+
+#define LX_SYSTRACE32_ENTRY(id) (ENTRY_FLAG | (id))
+#define LX_SYSTRACE32_RETURN(id) (id)
+
+#define LX_SYSTRACE64_ENTRY(id) (SYSC_64_BIT | ENTRY_FLAG | (id))
+#define LX_SYSTRACE64_RETURN(id) (SYSC_64_BIT | id)
+
+#define LX_SYSTRACE_ENTRY_AFRAMES 2
+#define LX_SYSTRACE_RETURN_AFRAMES 4
+
+typedef struct lx_systrace_sysent {
+ const char *lss_name;
+ dtrace_id_t lss_entry;
+ dtrace_id_t lss_return;
+} lx_systrace_sysent_t;
+
+static dev_info_t *lx_systrace_devi;
+static dtrace_provider_id_t lx_systrace_id;
+static kmutex_t lx_systrace_lock;
+static uint_t lx_systrace_nenabled;
+
+static int lx_systrace_nsysent32;
+static lx_systrace_sysent_t *lx_systrace_sysent32;
+
+#if defined(_LP64)
+static int lx_systrace_nsysent64;
+static lx_systrace_sysent_t *lx_systrace_sysent64;
+#endif
+
+/*ARGSUSED*/
+static void
+lx_systrace_entry(ulong_t sysnum, ulong_t arg0, ulong_t arg1, ulong_t arg2,
+ ulong_t arg3, ulong_t arg4, ulong_t arg5)
+{
+ dtrace_id_t id;
+
+#if defined(_LP64)
+ if ((ttoproc(curthread))->p_model == DATAMODEL_NATIVE) {
+ if (sysnum >= lx_systrace_nsysent64)
+ return;
+ id = lx_systrace_sysent64[sysnum].lss_entry;
+ } else
+#endif
+ {
+ if (sysnum >= lx_systrace_nsysent32)
+ return;
+ id = lx_systrace_sysent32[sysnum].lss_entry;
+ }
+
+ if (id == DTRACE_IDNONE)
+ return;
+ dtrace_probe(id, arg0, arg1, arg2, arg3, arg4);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_return(ulong_t sysnum, ulong_t arg0, ulong_t arg1, ulong_t arg2,
+ ulong_t arg3, ulong_t arg4, ulong_t arg5)
+{
+ dtrace_id_t id;
+
+#if defined(_LP64)
+ if ((ttoproc(curthread))->p_model == DATAMODEL_NATIVE) {
+ if (sysnum >= lx_systrace_nsysent64)
+ return;
+ id = lx_systrace_sysent64[sysnum].lss_return;
+ } else
+#endif
+ {
+ if (sysnum >= lx_systrace_nsysent32)
+ return;
+ id = lx_systrace_sysent32[sysnum].lss_return;
+ }
+
+ if (id == DTRACE_IDNONE)
+ return;
+ dtrace_probe(id, arg0, arg1, arg2, arg3, arg4);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_provide(void *arg, const dtrace_probedesc_t *desc)
+{
+ int i;
+
+ if (desc != NULL)
+ return;
+
+ for (i = 0; i < lx_systrace_nsysent32; i++) {
+ if (dtrace_probe_lookup(lx_systrace_id, "sys32",
+ lx_systrace_sysent32[i].lss_name, "entry") != 0)
+ continue;
+
+ (void) dtrace_probe_create(lx_systrace_id, "sys32",
+ lx_systrace_sysent32[i].lss_name, "entry",
+ LX_SYSTRACE_ENTRY_AFRAMES,
+ (void *)((uintptr_t)LX_SYSTRACE32_ENTRY(i)));
+
+ (void) dtrace_probe_create(lx_systrace_id, "sys32",
+ lx_systrace_sysent32[i].lss_name, "return",
+ LX_SYSTRACE_RETURN_AFRAMES,
+ (void *)((uintptr_t)LX_SYSTRACE32_RETURN(i)));
+
+ lx_systrace_sysent32[i].lss_entry = DTRACE_IDNONE;
+ lx_systrace_sysent32[i].lss_return = DTRACE_IDNONE;
+ }
+
+#if defined(_LP64)
+ for (i = 0; i < lx_systrace_nsysent64; i++) {
+ if (dtrace_probe_lookup(lx_systrace_id, "sys64",
+ lx_systrace_sysent64[i].lss_name, "entry") != 0)
+ continue;
+
+ (void) dtrace_probe_create(lx_systrace_id, "sys64",
+ lx_systrace_sysent64[i].lss_name, "entry",
+ LX_SYSTRACE_ENTRY_AFRAMES,
+ (void *)((uintptr_t)LX_SYSTRACE64_ENTRY(i)));
+
+ (void) dtrace_probe_create(lx_systrace_id, "sys64",
+ lx_systrace_sysent64[i].lss_name, "return",
+ LX_SYSTRACE_RETURN_AFRAMES,
+ (void *)((uintptr_t)LX_SYSTRACE64_RETURN(i)));
+
+ lx_systrace_sysent64[i].lss_entry = DTRACE_IDNONE;
+ lx_systrace_sysent64[i].lss_return = DTRACE_IDNONE;
+ }
+#endif
+}
+
+/*ARGSUSED*/
+static int
+lx_systrace_enable(void *arg, dtrace_id_t id, void *parg)
+{
+ int sysnum = LX_SYSTRACE_SYSNUM((uintptr_t)parg);
+
+ mutex_enter(&lx_systrace_lock);
+ if (lx_systrace_nenabled++ == 0)
+ lx_brand_systrace_enable();
+ mutex_exit(&lx_systrace_lock);
+
+#if defined(_LP64)
+ if (LX_SYSTRACE_IS64BIT((uintptr_t)parg)) {
+ ASSERT(sysnum < lx_systrace_nsysent64);
+
+ if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ lx_systrace_sysent64[sysnum].lss_entry = id;
+ } else {
+ lx_systrace_sysent64[sysnum].lss_return = id;
+ }
+ } else
+#endif
+ {
+ ASSERT(sysnum < lx_systrace_nsysent32);
+
+ if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ lx_systrace_sysent32[sysnum].lss_entry = id;
+ } else {
+ lx_systrace_sysent32[sysnum].lss_return = id;
+ }
+ }
+ return (0);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_disable(void *arg, dtrace_id_t id, void *parg)
+{
+ int sysnum = LX_SYSTRACE_SYSNUM((uintptr_t)parg);
+
+#if defined(_LP64)
+ if (LX_SYSTRACE_IS64BIT((uintptr_t)parg)) {
+ ASSERT(sysnum < lx_systrace_nsysent64);
+
+ if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ lx_systrace_sysent64[sysnum].lss_entry = DTRACE_IDNONE;
+ } else {
+ lx_systrace_sysent64[sysnum].lss_return = DTRACE_IDNONE;
+ }
+ } else
+#endif
+ {
+ ASSERT(sysnum < lx_systrace_nsysent32);
+
+ if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+ lx_systrace_sysent32[sysnum].lss_entry = DTRACE_IDNONE;
+ } else {
+ lx_systrace_sysent32[sysnum].lss_return = DTRACE_IDNONE;
+ }
+ }
+
+ mutex_enter(&lx_systrace_lock);
+ if (--lx_systrace_nenabled == 0)
+ lx_brand_systrace_disable();
+ mutex_exit(&lx_systrace_lock);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+}
+
+/*ARGSUSED*/
+static uint64_t
+lx_systrace_getarg(void *arg, dtrace_id_t id, void *parg, int argno,
+ int aframes)
+{
+ struct frame *fp = (struct frame *)dtrace_getfp();
+ uintptr_t *stack;
+ uint64_t val = 0;
+ int i;
+
+ if (argno >= 6)
+ return (0);
+
+ /*
+ * Walk the four frames down the stack to the entry or return callback.
+ * Our callback calls dtrace_probe() which calls dtrace_dif_variable()
+ * which invokes this function to get the extended arguments. We get
+ * the frame pointer in via call to dtrace_getfp() above which makes for
+ * four frames.
+ */
+ for (i = 0; i < 4; i++) {
+ fp = (struct frame *)fp->fr_savfp;
+ }
+
+ stack = (uintptr_t *)&fp[1];
+
+ /*
+ * Skip the first argument to the callback -- the system call number.
+ */
+ argno++;
+
+#ifdef __amd64
+ /*
+ * On amd64, the first 6 arguments are passed in registers while
+ * subsequent arguments are on the stack.
+ */
+ argno -= 6;
+#endif
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ val = stack[argno];
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ return (val);
+}
+
+
+static const dtrace_pattr_t lx_systrace_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pops_t lx_systrace_pops = {
+ lx_systrace_provide,
+ NULL,
+ lx_systrace_enable,
+ lx_systrace_disable,
+ NULL,
+ NULL,
+ NULL,
+ lx_systrace_getarg,
+ NULL,
+ lx_systrace_destroy
+};
+
+static int
+lx_systrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ int i;
+
+ switch (cmd) {
+ case DDI_ATTACH:
+ break;
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(devi, "lx_systrace", S_IFCHR,
+ 0, DDI_PSEUDO, 0) == DDI_FAILURE ||
+ dtrace_register("lx-syscall", &lx_systrace_attr,
+ DTRACE_PRIV_USER, 0, &lx_systrace_pops, NULL,
+ &lx_systrace_id) != 0) {
+ ddi_remove_minor_node(devi, NULL);
+ return (DDI_FAILURE);
+ }
+
+ ddi_report_dev(devi);
+ lx_systrace_devi = devi;
+
+ /*
+ * Initialize the 32-bit table.
+ */
+ VERIFY(lx_nsysent32 > 0);
+ lx_systrace_nsysent32 = lx_nsysent32;
+ lx_systrace_sysent32 = kmem_zalloc(lx_systrace_nsysent32 *
+ sizeof (lx_systrace_sysent_t), KM_SLEEP);
+
+ for (i = 0; i < lx_systrace_nsysent32; i++) {
+ lx_systrace_sysent32[i].lss_name = lx_sysent32[i].sy_name;
+ lx_systrace_sysent32[i].lss_entry = DTRACE_IDNONE;
+ lx_systrace_sysent32[i].lss_return = DTRACE_IDNONE;
+ }
+
+#if defined(_LP64)
+ /*
+ * Initialize the 64-bit table.
+ */
+ VERIFY(lx_nsysent64 > 0);
+ lx_systrace_nsysent64 = lx_nsysent64;
+ lx_systrace_sysent64 = kmem_zalloc(lx_systrace_nsysent64 *
+ sizeof (lx_systrace_sysent_t), KM_SLEEP);
+
+ for (i = 0; i < lx_systrace_nsysent64; i++) {
+ lx_systrace_sysent64[i].lss_name = lx_sysent64[i].sy_name;
+ lx_systrace_sysent64[i].lss_entry = DTRACE_IDNONE;
+ lx_systrace_sysent64[i].lss_return = DTRACE_IDNONE;
+ }
+#endif
+
+ /*
+ * Install probe triggers.
+ */
+ lx_systrace_entry_ptr = lx_systrace_entry;
+ lx_systrace_return_ptr = lx_systrace_return;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_systrace_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (dtrace_unregister(lx_systrace_id) != 0)
+ return (DDI_FAILURE);
+
+ /*
+ * Free tables.
+ */
+ kmem_free(lx_systrace_sysent32, lx_systrace_nsysent32 *
+ sizeof (lx_systrace_sysent_t));
+ lx_systrace_sysent32 = NULL;
+ lx_systrace_nsysent32 = 0;
+
+#if defined(_LP64)
+ kmem_free(lx_systrace_sysent64, lx_systrace_nsysent64 *
+ sizeof (lx_systrace_sysent_t));
+ lx_systrace_sysent64 = NULL;
+ lx_systrace_nsysent64 = 0;
+#endif
+
+ /*
+ * Reset probe triggers.
+ */
+ lx_systrace_entry_ptr = NULL;
+ lx_systrace_return_ptr = NULL;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_systrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+ return (0);
+}
+
+static struct cb_ops lx_systrace_cb_ops = {
+ lx_systrace_open, /* open */
+ nodev, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ nodev, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+static struct dev_ops lx_systrace_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ ddi_getinfo_1to1, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ lx_systrace_attach, /* attach */
+ lx_systrace_detach, /* detach */
+ nodev, /* reset */
+ &lx_systrace_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev, /* dev power */
+ ddi_quiesce_not_needed, /* quiesce */
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modldrv modldrv = {
+ &mod_driverops, /* module type (this is a pseudo driver) */
+ "Linux Brand System Call Tracing", /* name of module */
+ &lx_systrace_ops /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/usr/src/uts/common/brand/lx/dtrace/lx_systrace.conf b/usr/src/uts/common/brand/lx/dtrace/lx_systrace.conf
new file mode 100644
index 0000000000..e4499c8a5b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/dtrace/lx_systrace.conf
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+name="lx_systrace" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/brand/lx/io/lx_netlink.c b/usr/src/uts/common/brand/lx/io/lx_netlink.c
new file mode 100644
index 0000000000..b2c7589abc
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/io/lx_netlink.c
@@ -0,0 +1,2234 @@
+/*
+ * 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 2018 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+/*
+ * Compatibility for the Linux netlink(7) kernel/user transport, as well as
+ * for in-kernel netlink(7) providers like rtnetlink(7). See RFC 3549 for
+ * details of the protocol, and the Linux man pages for details of the Linux
+ * implementation that we're mimicking.
+ */
+
+#include <sys/strsubr.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/strsun.h>
+#include <sys/tihdr.h>
+#include <sys/sockio.h>
+#include <sys/brand.h>
+#include <sys/debug.h>
+#include <sys/ucred.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <inet/ip_impl.h>
+#include <inet/ip_ire.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_socket.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_audit.h>
+#include <sys/ethernet.h>
+#include <sys/dlpi.h>
+#include <sys/policy.h>
+#include <sys/ddi.h>
+
+/*
+ * Flags in netlink header
+ * See Linux include/uapi/linux/netlink.h
+ * Additional flags for "GET" requests
+ */
+#define LX_NETLINK_NLM_F_REQUEST 1
+#define LX_NETLINK_NLM_F_MULTI 2
+#define LX_NETLINK_NLM_F_ACK 4
+#define LX_NETLINK_NLM_F_ECHO 8
+#define LX_NETLINK_NLM_F_DUMP_INTR 16
+#define LX_NETLINK_NLM_F_ROOT 0x100
+#define LX_NETLINK_NLM_F_MATCH 0x200
+#define LX_NETLINK_NLM_F_ATOMIC 0x400
+
+/*
+ * Generic message type constants
+ */
+#define LX_NETLINK_NLMSG_NONE 0
+#define LX_NETLINK_NLMSG_NOOP 1
+#define LX_NETLINK_NLMSG_ERROR 2
+#define LX_NETLINK_NLMSG_DONE 3
+#define LX_NETLINK_NLMSG_OVERRUN 4
+
+/*
+ * Protocol constants.
+ */
+#define LX_NETLINK_ROUTE 0
+#define LX_NETLINK_UNUSED 1
+#define LX_NETLINK_USERSOCK 2
+#define LX_NETLINK_FIREWALL 3
+#define LX_NETLINK_SOCK_DIAG 4
+#define LX_NETLINK_NFLOG 5
+#define LX_NETLINK_XFRM 6
+#define LX_NETLINK_SELINUX 7
+#define LX_NETLINK_ISCSI 8
+#define LX_NETLINK_AUDIT 9
+#define LX_NETLINK_FIB_LOOKUP 10
+#define LX_NETLINK_CONNECTOR 11
+#define LX_NETLINK_NETFILTER 12
+#define LX_NETLINK_IP6_FW 13
+#define LX_NETLINK_DNRTMSG 14
+#define LX_NETLINK_KOBJECT_UEVENT 15
+#define LX_NETLINK_GENERIC 16
+#define LX_NETLINK_SCSITRANSPORT 18
+#define LX_NETLINK_ECRYPTFS 19
+#define LX_NETLINK_RDMA 20
+#define LX_NETLINK_CRYPTO 21
+
+/*
+ * rtnetlink(7) attribute-related constants
+ */
+#define LX_NETLINK_NLA_ALIGNTO 4
+
+#define LX_NETLINK_RTM_NEWLINK 16
+#define LX_NETLINK_RTM_DELLINK 17
+#define LX_NETLINK_RTM_GETLINK 18
+#define LX_NETLINK_RTM_SETLINK 19
+#define LX_NETLINK_RTM_NEWADDR 20
+#define LX_NETLINK_RTM_DELADDR 21
+#define LX_NETLINK_RTM_GETADDR 22
+#define LX_NETLINK_RTM_NEWROUTE 24
+#define LX_NETLINK_RTM_DELROUTE 25
+#define LX_NETLINK_RTM_GETROUTE 26
+#define LX_NETLINK_RTM_NEWNEIGH 28
+#define LX_NETLINK_RTM_DELNEIGH 29
+#define LX_NETLINK_RTM_GETNEIGH 30
+#define LX_NETLINK_RTM_NEWRULE 32
+#define LX_NETLINK_RTM_DELRULE 33
+#define LX_NETLINK_RTM_GETRULE 34
+#define LX_NETLINK_RTM_NEWQDISC 36
+#define LX_NETLINK_RTM_DELQDISC 37
+#define LX_NETLINK_RTM_GETQDISC 38
+#define LX_NETLINK_RTM_NEWTCLASS 40
+#define LX_NETLINK_RTM_DELTCLASS 41
+#define LX_NETLINK_RTM_GETTCLASS 42
+#define LX_NETLINK_RTM_NEWTFILTER 44
+#define LX_NETLINK_RTM_DELTFILTER 45
+#define LX_NETLINK_RTM_GETTFILTER 46
+#define LX_NETLINK_RTM_NEWACTION 48
+#define LX_NETLINK_RTM_DELACTION 49
+#define LX_NETLINK_RTM_GETACTION 50
+#define LX_NETLINK_RTM_NEWPREFIX 52
+#define LX_NETLINK_RTM_GETMULTICAST 58
+#define LX_NETLINK_RTM_GETANYCAST 62
+#define LX_NETLINK_RTM_NEWNEIGHTBL 64
+#define LX_NETLINK_RTM_GETNEIGHTBL 66
+#define LX_NETLINK_RTM_SETNEIGHTBL 67
+#define LX_NETLINK_RTM_NEWNDUSEROPT 68
+#define LX_NETLINK_RTM_NEWADDRLABEL 72
+#define LX_NETLINK_RTM_DELADDRLABEL 73
+#define LX_NETLINK_RTM_GETADDRLABEL 74
+#define LX_NETLINK_RTM_GETDCB 78
+#define LX_NETLINK_RTM_SETDCB 79
+#define LX_NETLINK_RTM_NEWNETCONF 80
+#define LX_NETLINK_RTM_GETNETCONF 82
+#define LX_NETLINK_RTM_NEWMDB 84
+#define LX_NETLINK_RTM_DELMDB 85
+#define LX_NETLINK_RTM_GETMDB 86
+#define LX_NETLINK_RTM_MAX 87
+
+/*
+ * rtnetlink(7) attribute constants
+ */
+#define LX_NETLINK_RTA_UNSPEC 0
+#define LX_NETLINK_RTA_DST 1
+#define LX_NETLINK_RTA_SRC 2
+#define LX_NETLINK_RTA_IIF 3
+#define LX_NETLINK_RTA_OIF 4
+#define LX_NETLINK_RTA_GATEWAY 5
+#define LX_NETLINK_RTA_PRIORITY 6
+#define LX_NETLINK_RTA_PREFSRC 7
+#define LX_NETLINK_RTA_METRICS 8
+#define LX_NETLINK_RTA_MULTIPATH 9
+#define LX_NETLINK_RTA_PROTOINFO 10
+#define LX_NETLINK_RTA_FLOW 11
+#define LX_NETLINK_RTA_CACHEINFO 12
+#define LX_NETLINK_RTA_SESSION 13
+#define LX_NETLINK_RTA_MP_ALGO 14
+#define LX_NETLINK_RTA_TABLE 15
+#define LX_NETLINK_RTA_MARK 16
+#define LX_NETLINK_RTA_MFC_STATS 17
+#define LX_NETLINK_MAX_RTA LX_NETLINK_RTA_MFC_STATS
+
+/*
+ * rtnetlink(7) NEWLINK/DELLINK/GETLINK constants
+ */
+#define LX_NETLINK_IFLA_UNSPEC 0
+#define LX_NETLINK_IFLA_ADDRESS 1
+#define LX_NETLINK_IFLA_BROADCAST 2
+#define LX_NETLINK_IFLA_IFNAME 3
+#define LX_NETLINK_IFLA_MTU 4
+#define LX_NETLINK_IFLA_LINK 5
+#define LX_NETLINK_IFLA_QDISC 6
+#define LX_NETLINK_IFLA_STATS 7
+#define LX_NETLINK_IFLA_COST 8
+#define LX_NETLINK_IFLA_PRIORITY 9
+#define LX_NETLINK_IFLA_MASTER 10
+#define LX_NETLINK_IFLA_WIRELESS 11
+#define LX_NETLINK_IFLA_PROTINFO 12
+#define LX_NETLINK_IFLA_TXQLEN 13
+#define LX_NETLINK_IFLA_MAP 14
+#define LX_NETLINK_IFLA_WEIGHT 15
+#define LX_NETLINK_IFLA_OPERSTATE 16
+#define LX_NETLINK_IFLA_LINKMODE 17
+#define LX_NETLINK_IFLA_LINKINFO 18
+#define LX_NETLINK_IFLA_NET_NS_PID 19
+#define LX_NETLINK_IFLA_IFALIAS 20
+#define LX_NETLINK_IFLA_NUM_VF 21
+#define LX_NETLINK_IFLA_VFINFO_LIST 22
+#define LX_NETLINK_IFLA_STATS64 23
+#define LX_NETLINK_IFLA_VF_PORTS 24
+#define LX_NETLINK_IFLA_PORT_SELF 25
+#define LX_NETLINK_IFLA_AF_SPEC 26
+#define LX_NETLINK_IFLA_GROUP 27
+#define LX_NETLINK_IFLA_NET_NS_FD 28
+#define LX_NETLINK_IFLA_EXT_MASK 29
+#define LX_NETLINK_IFLA_PROMISCUITY 30
+#define LX_NETLINK_IFLA_NUM_TX_QUEUES 31
+#define LX_NETLINK_IFLA_NUM_RX_QUEUES 32
+#define LX_NETLINK_IFLA_CARRIER 33
+#define LX_NETLINK_IFLA_PHYS_PORT_ID 34
+#define LX_NETLINK_IFLA_CARRIER_CHANGES 35
+#define LX_NETLINK_IFLA_MAX 36
+
+/*
+ * rtnetlink(7) NEWADDR/DELADDR/GETADDR constants
+ */
+#define LX_NETLINK_IFA_UNSPEC 0
+#define LX_NETLINK_IFA_ADDRESS 1
+#define LX_NETLINK_IFA_LOCAL 2
+#define LX_NETLINK_IFA_LABEL 3
+#define LX_NETLINK_IFA_BROADCAST 4
+#define LX_NETLINK_IFA_ANYCAST 5
+#define LX_NETLINK_IFA_CACHEINFO 6
+#define LX_NETLINK_IFA_MULTICAST 7
+#define LX_NETLINK_IFA_FLAGS 8
+#define LX_NETLINK_IFA_MAX 9
+
+#define LX_NETLINK_IFA_F_SECONDARY 0x01
+#define LX_NETLINK_IFA_F_TEMPORARY LX_NETLINK_IFA_F_SECONDARY
+#define LX_NETLINK_IFA_F_NODAD 0x02
+#define LX_NETLINK_IFA_F_OPTIMISTIC 0x04
+#define LX_NETLINK_IFA_F_DADFAILED 0x08
+#define LX_NETLINK_IFA_F_HOMEADDRESS 0x10
+#define LX_NETLINK_IFA_F_DEPRECATED 0x20
+#define LX_NETLINK_IFA_F_TENTATIVE 0x40
+#define LX_NETLINK_IFA_F_PERMANENT 0x80
+#define LX_NETLINK_IFA_F_MANAGETEMPADDR 0x100
+#define LX_NETLINK_IFA_F_NOPREFIXROUTE 0x200
+
+/*
+ * Linux interface flags.
+ */
+#define LX_IFF_UP (1<<0)
+#define LX_IFF_BROADCAST (1<<1)
+#define LX_IFF_DEBUG (1<<2)
+#define LX_IFF_LOOPBACK (1<<3)
+#define LX_IFF_POINTOPOINT (1<<4)
+#define LX_IFF_NOTRAILERS (1<<5)
+#define LX_IFF_RUNNING (1<<6)
+#define LX_IFF_NOARP (1<<7)
+#define LX_IFF_PROMISC (1<<8)
+#define LX_IFF_ALLMULTI (1<<9)
+#define LX_IFF_MASTER (1<<10)
+#define LX_IFF_SLAVE (1<<11)
+#define LX_IFF_MULTICAST (1<<12)
+#define LX_IFF_PORTSEL (1<<13)
+#define LX_IFF_AUTOMEDIA (1<<14)
+#define LX_IFF_DYNAMIC (1<<15)
+#define LX_IFF_LOWER_UP (1<<16)
+#define LX_IFF_DORMANT (1<<17)
+#define LX_IFF_ECHO (1<<18)
+
+/* rtm_table */
+#define LX_ROUTE_TABLE_MAIN 254
+
+/* rtm_type */
+#define LX_RTN_UNSPEC 0
+#define LX_RTN_UNICAST 1
+#define LX_RTN_LOCAL 2
+#define LX_RTN_BROADCAST 3
+#define LX_RTN_ANYCAST 4
+#define LX_RTN_MULTICAST 5
+#define LX_RTN_BLACKHOLE 6
+#define LX_RTN_UNREACHABLE 7
+#define LX_RTN_PROHIBIT 8
+#define LX_RTN_THROW 9
+#define LX_RTN_NAT 10
+#define LX_RTN_XRESOLVE 11
+
+/* rtm_protocol */
+#define LX_RTPROT_UNSPEC 0
+#define LX_RTPROT_REDIRECT 1 /* From ICMP redir */
+#define LX_RTPROT_KERNEL 2 /* From kernel */
+#define LX_RTPROT_BOOT 3 /* From boot */
+#define LX_RTPROT_STATIC 4 /* From administrator */
+#define LX_RTPROT_NULL 0xff /* Uninitialized */
+
+/* rtm_scope */
+#define LX_RTSCOPE_UNIVERSE 0
+#define LX_RTSCOPE_SITE 200
+#define LX_RTSCOPE_LINK 253
+#define LX_RTSCOPE_HOST 254
+#define LX_RTSCOPE_NOWHERE 255
+
+/*
+ * Audit message types (lxnh_type in the lx_netlink_hdr_t msg header)
+ * See Linux include/uapi/linux/audit.h and user-level auditd source
+ * lib/libaudit.h.
+ *
+ * The types fall into range blocks:
+ * 1000-1099 is for audit system control commands
+ * 1100-2999 various messages, as detailed in include/uapi/linux/audit.h
+ */
+#define LX_AUDIT_GET 1000 /* get audit system status */
+#define LX_AUDIT_SET 1001 /* set audit system status */
+#define LX_AUDIT_WATCH_INS 1007 /* insert file watch */
+#define LX_AUDIT_WATCH_REM 1008 /* remove file watch */
+#define LX_AUDIT_WATCH_LIST 1009 /* list file watchs */
+#define LX_AUDIT_ADD_RULE 1011 /* add syscall rule */
+#define LX_AUDIT_DEL_RULE 1012 /* del syscall rule */
+#define LX_AUDIT_LIST_RULES 1013 /* list syscall rules */
+#define LX_AUDIT_SET_FEATURE 1018
+#define LX_AUDIT_GET_FEATURE 1019
+#define LX_AUDIT_USER_MSG_START 1100
+
+/*
+ * Netlink sockopts
+ */
+#define SOL_LX_NETLINK 270
+
+/* See Linux include/uapi/linux/netlink.h */
+#define LX_NETLINK_SO_ADD_MEMBERSHIP 1
+#define LX_NETLINK_SO_DROP_MEMBERSHIP 2
+#define LX_NETLINK_SO_PKTINFO 3
+#define LX_NETLINK_SO_BROADCAST_ERROR 4
+#define LX_NETLINK_SO_NO_ENOBUFS 5
+#define LX_NETLINK_SO_RX_RING 6
+#define LX_NETLINK_SO_TX_RING 7
+#define LX_NETLINK_SO_LISTEN_ALL_NSID 8
+#define LX_NETLINK_SO_LIST_MEMBERSHIPS 9
+#define LX_NETLINK_SO_CAP_ACK 10
+
+/* Internal socket flags */
+#define LXNLF_RECVUCRED 0x1
+#define LXNLF_AUDITD 0x2
+
+/* nlmsg structure macros */
+#define LXNLMSG_ALIGNTO 4
+#define LXNLMSG_ALIGN(len) \
+ (((len) + LXNLMSG_ALIGNTO - 1) & ~(LXNLMSG_ALIGNTO - 1))
+#define LXNLMSG_HDRLEN \
+ ((int)LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t)))
+#define LXNLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
+#define LXNLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define LXNLMSG_DATA(nlh) ((void*)(((char *)nlh) + NLMSG_LENGTH(0)))
+#define LXNLMSG_PAYLOAD(nlh, len) \
+ ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define LXATTR_PAYLOAD(lxa) \
+ ((void*)((caddr_t)(lxa) + sizeof (lx_netlink_attr_t)))
+#define LXATTR_HDRLEN LXNLMSG_ALIGN(sizeof (lx_netlink_attr_t))
+#define LXATTR_LEN(len) (LXATTR_HDRLEN + LXNLMSG_ALIGN(len))
+
+typedef struct lx_netlink_hdr {
+ uint32_t lxnh_len; /* length of message */
+ uint16_t lxnh_type; /* type of message */
+ uint16_t lxnh_flags; /* flags */
+ uint32_t lxnh_seq; /* sequence number */
+ uint32_t lxnh_pid; /* sending pid */
+} lx_netlink_hdr_t;
+
+typedef struct lx_netlink_err {
+ lx_netlink_hdr_t lxne_hdr; /* header */
+ int32_t lxne_errno; /* errno */
+ lx_netlink_hdr_t lxne_failed; /* header of err */
+} lx_netlink_err_t;
+
+typedef struct lx_netlink_attr {
+ uint16_t lxna_len; /* length of attribute */
+ uint16_t lxna_type; /* type of attribute */
+} lx_netlink_attr_t;
+
+typedef struct lx_netlink_ifinfomsg {
+ uint8_t lxnl_ifi_family; /* family: AF_UNSPEC */
+ uint8_t lxnl_ifi__pad;
+ uint16_t lxnl_ifi_type; /* device type */
+ uint32_t lxnl_ifi_index; /* interface index */
+ uint32_t lxnl_ifi_flags; /* device flags */
+ uint32_t lxnl_ifi_change; /* unused; must be -1 */
+} lx_netlink_ifinfomsg_t;
+
+typedef struct lx_netlink_ifaddrmsg {
+ uint8_t lxnl_ifa_family; /* address type */
+ uint8_t lxnl_ifa_prefixlen; /* prefix length of address */
+ uint8_t lxnl_ifa_flags; /* address flags */
+ uint8_t lxnl_ifa_scope; /* address scope */
+ uint8_t lxnl_ifa_index; /* interface index */
+} lx_netlink_ifaddrmsg_t;
+
+typedef struct lx_netlink_rtmsg {
+ uint8_t rtm_family; /* route AF */
+ uint8_t rtm_dst_len; /* destination addr length */
+ uint8_t rtm_src_len; /* source addr length */
+ uint8_t rtm_tos; /* TOS filter */
+ uint8_t rtm_table; /* routing table ID */
+ uint8_t rtm_protocol; /* routing protocol */
+ uint8_t rtm_scope;
+ uint8_t rtm_type;
+ uint32_t rtm_flags;
+} lx_netlink_rtmsg_t;
+
+typedef struct lx_netlink_sockaddr {
+ sa_family_t lxnl_family; /* AF_LX_NETLINK */
+ uint16_t lxnl_pad; /* padding */
+ uint32_t lxnl_port; /* port id */
+ uint32_t lxnl_groups; /* multicast groups mask */
+} lx_netlink_sockaddr_t;
+
+typedef struct lx_netlink_sock {
+ struct lx_netlink_sock *lxns_next; /* list of lx_netlink sockets */
+ sock_upcalls_t *lxns_upcalls; /* pointer to socket upcalls */
+ sock_upper_handle_t lxns_uphandle; /* socket upcall handle */
+ ldi_handle_t lxns_iphandle; /* handle to /dev/ip */
+ ldi_handle_t lxns_ip6handle; /* handle to /dev/ip6 */
+ ldi_handle_t lxns_current; /* current ip handle */
+ int lxns_proto; /* protocol */
+ uint32_t lxns_port; /* port identifier */
+ uint32_t lxns_groups; /* group subscriptions */
+ uint32_t lxns_bufsize; /* buffer size */
+ uint32_t lxns_flags; /* socket flags */
+ kmutex_t lxns_flowctl_mtx; /* protects lxns_flowctrled */
+ boolean_t lxns_flowctrled; /* sock is flow-controlled */
+} lx_netlink_sock_t;
+
+typedef struct lx_netlink_reply {
+ lx_netlink_hdr_t lxnr_hdr; /* header that we're reply to */
+ lx_netlink_sock_t *lxnr_sock; /* socket */
+ uint32_t lxnr_seq; /* sequence number */
+ uint16_t lxnr_type; /* type of reply */
+ mblk_t *lxnr_mp; /* current mblk */
+ mblk_t *lxnr_err; /* error mblk */
+ mblk_t *lxnr_mp1; /* T_UNITDATA_IND mblk */
+ int lxnr_errno; /* errno, if any */
+} lx_netlink_reply_t;
+
+static lx_netlink_sock_t *lx_netlink_head; /* head of lx_netlink sockets */
+static uint_t lx_netlink_audit_cnt; /* prevent unload for audit */
+static kmutex_t lx_netlink_lock; /* lock to protect state */
+static ldi_ident_t lx_netlink_ldi; /* LDI handle */
+static int lx_netlink_bufsize = 4096; /* default buffer size */
+static int lx_netlink_flowctrld; /* # of times flow controlled */
+
+typedef enum {
+ LXNL_BIND,
+ LXNL_SENDMSG
+} lx_netlink_action_t;
+
+#define LX_UNSUP_BUFSZ 64
+
+/*
+ * On Linux, CAP_NET_ADMIN is required to take certain netlink actions. This
+ * restriction is loosened for certain protocol types, provided the activity is
+ * limited to communicating directly with the kernel (rather than transmitting
+ * to the various multicast groups)
+ */
+static int
+lx_netlink_access(lx_netlink_sock_t *lns, cred_t *cr, lx_netlink_action_t act)
+{
+ /* Simple actions are allowed on these netlink protocols. */
+ if (act != LXNL_SENDMSG) {
+ switch (lns->lxns_proto) {
+ case LX_NETLINK_ROUTE:
+ case LX_NETLINK_AUDIT:
+ case LX_NETLINK_KOBJECT_UEVENT:
+ return (0);
+ default:
+ break;
+ }
+ }
+
+ /* CAP_NET_ADMIN roughly maps to PRIV_SYS_IP_CONFIG. */
+ if (secpolicy_ip_config(cr, B_FALSE) != 0) {
+ return (EACCES);
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static void
+lx_netlink_activate(sock_lower_handle_t handle,
+ sock_upper_handle_t sock_handle, sock_upcalls_t *sock_upcalls,
+ int flags, cred_t *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+ struct sock_proto_props sopp;
+
+ sopp.sopp_flags = SOCKOPT_WROFF | SOCKOPT_RCVHIWAT |
+ SOCKOPT_RCVLOWAT | SOCKOPT_MAXADDRLEN | SOCKOPT_MAXPSZ |
+ SOCKOPT_MAXBLK | SOCKOPT_MINPSZ;
+ sopp.sopp_wroff = 0;
+ sopp.sopp_rxhiwat = SOCKET_RECVHIWATER;
+ sopp.sopp_rxlowat = SOCKET_RECVLOWATER;
+ sopp.sopp_maxaddrlen = sizeof (struct sockaddr_dl);
+ sopp.sopp_maxpsz = INFPSZ;
+ sopp.sopp_maxblk = INFPSZ;
+ sopp.sopp_minpsz = 0;
+
+ lxsock->lxns_upcalls = sock_upcalls;
+ lxsock->lxns_uphandle = sock_handle;
+
+ sock_upcalls->su_set_proto_props(sock_handle, &sopp);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_setsockopt(sock_lower_handle_t handle, int level,
+ int option_name, const void *optval, socklen_t optlen, struct cred *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+
+ if (level == SOL_SOCKET && option_name == SO_RECVUCRED) {
+ int *ival;
+ if (optlen != sizeof (int)) {
+ return (EINVAL);
+ }
+ ival = (int *)optval;
+ if (*ival == 0) {
+ lxsock->lxns_flags &= ~LXNLF_RECVUCRED;
+ } else {
+ lxsock->lxns_flags |= LXNLF_RECVUCRED;
+ }
+ return (0);
+ } else if (level == SOL_SOCKET) {
+ /* Punt on the other SOL_SOCKET options */
+ return (0);
+ } else if (level != SOL_LX_NETLINK) {
+ return (EOPNOTSUPP);
+ }
+
+ switch (option_name) {
+ case LX_NETLINK_SO_ADD_MEMBERSHIP:
+ case LX_NETLINK_SO_DROP_MEMBERSHIP:
+ case LX_NETLINK_SO_PKTINFO:
+ case LX_NETLINK_SO_BROADCAST_ERROR:
+ case LX_NETLINK_SO_NO_ENOBUFS:
+ case LX_NETLINK_SO_RX_RING:
+ case LX_NETLINK_SO_TX_RING:
+ /* Blatant lie */
+ return (0);
+ default:
+ return (EINVAL);
+ }
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getsockopt(sock_lower_handle_t handle, int level,
+ int option_name, void *optval, socklen_t *optlen, cred_t *cr)
+{
+ if (level != SOL_LX_NETLINK) {
+ return (EOPNOTSUPP);
+ }
+
+ switch (option_name) {
+ case LX_NETLINK_SO_LIST_MEMBERSHIPS:
+ /* Report that we have 0 members to allow systemd to proceed. */
+ *optlen = 0;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_bind(sock_lower_handle_t handle, struct sockaddr *name,
+ socklen_t namelen, struct cred *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+ lx_netlink_sockaddr_t *lxsa = (lx_netlink_sockaddr_t *)name;
+
+ if (namelen < sizeof (lx_netlink_sockaddr_t) ||
+ lxsa->lxnl_family != AF_LX_NETLINK) {
+ return (EINVAL);
+ }
+
+ /*
+ * Perform access checks if attempting to bind on any multicast groups.
+ */
+ if (lxsa->lxnl_groups != 0) {
+ int err;
+
+ if ((err = lx_netlink_access(lxsock, cr, LXNL_BIND)) != 0) {
+ return (err);
+ }
+
+ /* Lie about group subscription for now */
+ lxsock->lxns_groups = lxsa->lxnl_groups;
+ }
+
+ /*
+ * Linux netlink uses nl_port to identify distinct netlink sockets.
+ * Binding to an address of nl_port=0 triggers the kernel to
+ * automatically assign a free nl_port identifier. Originally,
+ * consumers of lx_netlink were required to bind with that automatic
+ * address. We now support non-zero values for nl_port although strict
+ * checking to identify conflicts is not performed. Use of the
+ * id_space facility could be a convenient solution, if a need arose.
+ */
+ if (lxsa->lxnl_port == 0) {
+ /*
+ * Because we are not doing conflict detection, there is no
+ * need to expend effort selecting a unique port for automatic
+ * addressing during bind.
+ */
+ lxsock->lxns_port = curproc->p_pid;
+ } else {
+ lxsock->lxns_port = lxsa->lxnl_port;
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getsockname(sock_lower_handle_t handle, struct sockaddr *sa,
+ socklen_t *len, struct cred *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+ lx_netlink_sockaddr_t *lxsa = (lx_netlink_sockaddr_t *)sa;
+
+ if (*len < sizeof (lx_netlink_sockaddr_t))
+ return (EINVAL);
+
+ lxsa->lxnl_family = AF_LX_NETLINK;
+ lxsa->lxnl_pad = 0;
+ lxsa->lxnl_port = lxsock->lxns_port;
+ lxsa->lxnl_groups = lxsock->lxns_groups;
+
+ *len = sizeof (lx_netlink_sockaddr_t);
+
+ return (0);
+}
+
+static mblk_t *
+lx_netlink_alloc_mp1(lx_netlink_sock_t *lxsock)
+{
+ mblk_t *mp;
+ size_t size;
+ struct T_unitdata_ind *tunit;
+ lx_netlink_sockaddr_t *lxsa;
+ boolean_t send_ucred;
+
+ /*
+ * Certain netlink clients (such as systemd) will set SO_RECVUCRED
+ * (via the Linux SCM_CREDENTIALS) on the expectation that all replies
+ * will contain credentials passed via cmsg. They require this to
+ * authenticate those messages as having originated in the kernel by
+ * checking uc_pid == 0.
+ */
+ VERIFY(lxsock != NULL);
+ send_ucred = ((lxsock->lxns_flags & LXNLF_RECVUCRED) != 0);
+
+ /*
+ * Message structure:
+ * +----------------------------+
+ * | struct T_unit_data_ind |
+ * +----------------------------+
+ * | lx_netlink_sockaddr_t |
+ * +----------------------------+ -+
+ * | struct cmsghdr (SCM_UCRED) | |
+ * +----------------------------+ +-(optional)
+ * | struct ucred_s (cmsg data) | |
+ * +----------------------------+ -+
+ */
+ size = sizeof (*tunit) + sizeof (*lxsa);
+ if (send_ucred) {
+ size += sizeof (struct cmsghdr) +
+ ROUNDUP_cmsglen(sizeof (struct ucred_s));
+ }
+ mp = allocb(size, 0);
+ if (mp == NULL) {
+ return (NULL);
+ }
+
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ tunit = (struct T_unitdata_ind *)mp->b_rptr;
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ lxsa = (lx_netlink_sockaddr_t *)((caddr_t)tunit + sizeof (*tunit));
+ mp->b_wptr += size;
+
+ mp->b_datap->db_type = M_PROTO;
+ tunit->PRIM_type = T_UNITDATA_IND;
+ tunit->SRC_length = sizeof (*lxsa);
+ tunit->SRC_offset = sizeof (*tunit);
+
+ lxsa->lxnl_family = AF_LX_NETLINK;
+ lxsa->lxnl_port = 0;
+ lxsa->lxnl_groups = 0;
+ lxsa->lxnl_pad = 0;
+
+ if (send_ucred) {
+ struct cmsghdr *cmsg;
+ struct ucred_s *ucred;
+
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ cmsg = (struct cmsghdr *)((caddr_t)lxsa + sizeof (*lxsa));
+ ucred = (struct ucred_s *)CMSG_CONTENT(cmsg);
+ cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*ucred);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_UCRED;
+ bzero(ucred, sizeof (*ucred));
+ ucred->uc_size = sizeof (*ucred);
+ ucred->uc_zoneid = getzoneid();
+
+ tunit->OPT_length = sizeof (*cmsg) +
+ ROUNDUP_cmsglen(sizeof (*ucred));
+ tunit->OPT_offset = tunit->SRC_offset + tunit->SRC_length;
+ } else {
+ tunit->OPT_length = 0;
+ tunit->OPT_offset = 0;
+ }
+
+ return (mp);
+}
+
+static lx_netlink_reply_t *
+lx_netlink_reply(lx_netlink_sock_t *lxsock,
+ lx_netlink_hdr_t *hdr, uint16_t type)
+{
+ lx_netlink_reply_t *reply;
+ mblk_t *err, *mp1;
+
+ /*
+ * We always allocate an error block to assure that even if subsequent
+ * allocations fail, we can return an error.
+ */
+ if ((err = allocb(sizeof (lx_netlink_err_t), 0)) == NULL)
+ return (NULL);
+
+ if ((mp1 = lx_netlink_alloc_mp1(lxsock)) == NULL) {
+ freeb(err);
+ return (NULL);
+ }
+
+ reply = kmem_zalloc(sizeof (lx_netlink_reply_t), KM_SLEEP);
+ reply->lxnr_err = err;
+ reply->lxnr_sock = lxsock;
+ reply->lxnr_hdr = *hdr;
+ reply->lxnr_type = type;
+ reply->lxnr_mp1 = mp1;
+
+ return (reply);
+}
+
+static void
+lx_netlink_reply_add(lx_netlink_reply_t *reply, void *payload, uint32_t size)
+{
+ lx_netlink_hdr_t *hdr;
+ lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+ uint32_t aligned;
+ mblk_t *mp = reply->lxnr_mp;
+
+ if (reply->lxnr_errno)
+ return;
+
+ aligned = LXNLMSG_ALIGN(size);
+ hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+
+ if (hdr->lxnh_len + aligned > lxsock->lxns_bufsize) {
+ reply->lxnr_errno = E2BIG;
+ return;
+ }
+
+ bcopy(payload, mp->b_wptr, size);
+ hdr->lxnh_len += aligned;
+ mp->b_wptr += aligned;
+}
+
+static void
+lx_netlink_reply_msg(lx_netlink_reply_t *reply, void *payload, uint32_t size)
+{
+ lx_netlink_hdr_t *hdr;
+ lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+ mblk_t *mp;
+
+ if (reply->lxnr_errno)
+ return;
+
+ VERIFY(reply->lxnr_mp == NULL);
+
+ if ((reply->lxnr_mp = mp = allocb(lxsock->lxns_bufsize, 0)) == NULL) {
+ reply->lxnr_errno = ENOMEM;
+ return;
+ }
+
+ bzero(mp->b_rptr, lxsock->lxns_bufsize);
+ hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+ hdr->lxnh_flags = LX_NETLINK_NLM_F_MULTI;
+ hdr->lxnh_len = LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t));
+ hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+ hdr->lxnh_pid = lxsock->lxns_port;
+
+ mp->b_wptr += LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t));
+
+ if (payload == NULL) {
+ /*
+ * A NULL payload denotes a "done" message.
+ */
+ hdr->lxnh_type = LX_NETLINK_NLMSG_DONE;
+ } else {
+ hdr->lxnh_type = reply->lxnr_type;
+ lx_netlink_reply_add(reply, payload, size);
+ }
+}
+
+static void
+lx_netlink_reply_attr(lx_netlink_reply_t *reply, uint16_t type,
+ void *payload, uint32_t size)
+{
+ lx_netlink_attr_t attr;
+
+ attr.lxna_len = size + sizeof (lx_netlink_attr_t);
+ attr.lxna_type = type;
+
+ lx_netlink_reply_add(reply, &attr, sizeof (attr));
+ lx_netlink_reply_add(reply, payload, size);
+}
+
+static void
+lx_netlink_reply_attr_string(lx_netlink_reply_t *reply,
+ uint16_t type, const char *str)
+{
+ lx_netlink_reply_attr(reply, type, (void *)str, strlen(str) + 1);
+}
+
+static void
+lx_netlink_reply_attr_int32(lx_netlink_reply_t *reply,
+ uint16_t type, int32_t val)
+{
+ int32_t v = val;
+
+ lx_netlink_reply_attr(reply, type, &v, sizeof (int32_t));
+}
+
+static int
+lx_netlink_reply_ioctl(lx_netlink_reply_t *reply, int cmd, void *arg)
+{
+ int rval;
+
+ if (reply->lxnr_errno != 0)
+ return (reply->lxnr_errno);
+
+ if ((rval = ldi_ioctl(reply->lxnr_sock->lxns_current,
+ cmd, (intptr_t)arg, FKIOCTL, kcred, NULL)) != 0) {
+ reply->lxnr_errno = rval;
+ }
+
+ return (rval);
+}
+
+static void
+lx_netlink_reply_sendup(lx_netlink_reply_t *reply, mblk_t *mp, mblk_t *mp1)
+{
+ lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+ int error;
+
+ /*
+ * To prevent the stream head from coalescing messages and to indicate
+ * their origin, we send them as T_UNITDATA_IND messages, not as raw
+ * M_DATA.
+ */
+ mp1->b_cont = mp;
+
+ lxsock->lxns_upcalls->su_recv(lxsock->lxns_uphandle, mp1,
+ msgdsize(mp1), 0, &error, NULL);
+
+ if (error != 0)
+ lx_netlink_flowctrld++;
+}
+
+static void
+lx_netlink_reply_send(lx_netlink_reply_t *reply)
+{
+ mblk_t *mp1;
+
+ if (reply->lxnr_errno)
+ return;
+
+ if ((mp1 = lx_netlink_alloc_mp1(reply->lxnr_sock)) == NULL) {
+ reply->lxnr_errno = ENOMEM;
+ return;
+ }
+
+ lx_netlink_reply_sendup(reply, reply->lxnr_mp, mp1);
+ reply->lxnr_mp = NULL;
+}
+
+static void
+lx_netlink_reply_done(lx_netlink_reply_t *reply)
+{
+ lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+ mblk_t *mp;
+
+ /*
+ * Denote that we're done via a message with a NULL payload.
+ */
+ lx_netlink_reply_msg(reply, NULL, 0);
+
+ if (reply->lxnr_errno) {
+ /*
+ * If anything failed, we'll send up an error message.
+ */
+ lx_netlink_hdr_t *hdr;
+ lx_netlink_err_t *err;
+
+ if (reply->lxnr_mp != NULL) {
+ freeb(reply->lxnr_mp);
+ reply->lxnr_mp = NULL;
+ }
+
+ mp = reply->lxnr_err;
+ VERIFY(mp != NULL);
+ reply->lxnr_err = NULL;
+ err = (lx_netlink_err_t *)mp->b_rptr;
+ hdr = &err->lxne_hdr;
+ mp->b_wptr += sizeof (lx_netlink_err_t);
+
+ err->lxne_failed = reply->lxnr_hdr;
+ err->lxne_errno = reply->lxnr_errno;
+ hdr->lxnh_type = LX_NETLINK_NLMSG_ERROR;
+ hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+ hdr->lxnh_len = sizeof (lx_netlink_err_t);
+ hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+ hdr->lxnh_pid = lxsock->lxns_port;
+ hdr->lxnh_flags = 0;
+ } else {
+ uint32_t status = 0;
+
+ /*
+ * More recent versions of the iproute2 utils expect a status
+ * value after the header, even in the absence of errors.
+ */
+ lx_netlink_reply_add(reply, &status, sizeof (status));
+
+ /*
+ * "done" is also the most minimal response possible. If
+ * lx_netlink_reply_msg() does not set lxnr_errno, we should
+ * be guaranteed enough room to hold this (i.e. our
+ * lx_netlink_reply_add() call should never end up setting
+ * lxnr_errno).
+ */
+ VERIFY0(reply->lxnr_errno);
+
+ mp = reply->lxnr_mp;
+ VERIFY(mp != NULL);
+ reply->lxnr_mp = NULL;
+ }
+
+ lx_netlink_reply_sendup(reply, mp, reply->lxnr_mp1);
+
+ if (reply->lxnr_mp != NULL)
+ freeb(reply->lxnr_mp);
+
+ if (reply->lxnr_err != NULL)
+ freeb(reply->lxnr_err);
+
+ kmem_free(reply, sizeof (lx_netlink_reply_t));
+}
+
+static int
+lx_netlink_reply_error(lx_netlink_sock_t *lxsock,
+ lx_netlink_hdr_t *hdr, int errno)
+{
+ /*
+ * The type of the message doesn't matter, as we're going to explicitly
+ * set lxnr_errno and therefore send only an error message.
+ */
+ lx_netlink_reply_t *reply = lx_netlink_reply(lxsock, hdr, 0);
+
+ if (reply == NULL)
+ return (ENOMEM);
+
+ reply->lxnr_errno = errno;
+ lx_netlink_reply_done(reply);
+
+ return (0);
+}
+
+/*
+ * Send an ack message with an explicit errno of 0.
+ * TODO: this needs more work
+ */
+/*
+ * static void
+ * lx_netlink_reply_ack(lx_netlink_reply_t *reply)
+ * {
+ * lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+ * mblk_t *mp;
+ * lx_netlink_hdr_t *hdr;
+ * lx_netlink_err_t *err;
+ *
+ * lx_netlink_reply_msg(reply, NULL, 0);
+ *
+ * mp = reply->lxnr_err;
+ * VERIFY(mp != NULL);
+ * reply->lxnr_err = NULL;
+ * err = (lx_netlink_err_t *)mp->b_rptr;
+ * hdr = &err->lxne_hdr;
+ *
+ * err->lxne_failed = reply->lxnr_hdr;
+ * err->lxne_errno = 0;
+ * hdr->lxnh_type = LX_NETLINK_NLMSG_ERROR;
+ * hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+ * hdr->lxnh_len = sizeof (lx_netlink_err_t);
+ * hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+ * hdr->lxnh_pid = lxsock->lxns_port;
+ *
+ * lx_netlink_reply_sendup(reply, mp, reply->lxnr_mp1);
+ *
+ * kmem_free(reply, sizeof (lx_netlink_reply_t));
+ * }
+ */
+
+static int
+lx_netlink_parse_msg_attrs(mblk_t *mp, void **msgp, unsigned int msg_size,
+ lx_netlink_attr_t **attrp, unsigned int *attr_max)
+{
+ lx_netlink_hdr_t *hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+ lx_netlink_attr_t *lxa;
+ unsigned char *buf = mp->b_rptr + LXNLMSG_HDRLEN;
+ unsigned int i;
+ uint32_t buf_left = MBLKL(mp) - LXNLMSG_HDRLEN;
+ uint32_t msg_left = hdr->lxnh_len;
+
+ msg_size = LXNLMSG_ALIGN(msg_size);
+ if (msg_size > buf_left || msg_size > msg_left) {
+ return (-1);
+ }
+
+ *msgp = (void *)buf;
+ buf += msg_size;
+ buf_left -= msg_size;
+ msg_left -= msg_size;
+
+ /* Do not bother with attr parsing if not requested */
+ if (attrp == NULL || *attr_max == 0) {
+ return (0);
+ }
+
+ for (i = 0; i < *attr_max; i++) {
+ if (buf_left < LXATTR_HDRLEN || msg_left < LXATTR_HDRLEN) {
+ break;
+ }
+
+ lxa = (lx_netlink_attr_t *)buf;
+ if (lxa->lxna_len > buf_left || lxa->lxna_len > msg_left) {
+ return (-1);
+ }
+
+ attrp[i] = lxa;
+ buf += lxa->lxna_len;
+ buf_left -= lxa->lxna_len;
+ msg_left -= lxa->lxna_len;
+ }
+ *attr_max = i;
+
+ return (0);
+}
+
+/*
+ * Takes an IPv4 address (in network byte order) and returns the address scope.
+ */
+static uint8_t
+lx_ipv4_rtscope(in_addr_t nbo_addr)
+{
+ in_addr_t addr = ntohl(nbo_addr);
+ if ((addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
+ return (LX_RTSCOPE_HOST);
+ } else if ((addr & IN_AUTOCONF_MASK) == IN_AUTOCONF_NET) {
+ return (LX_RTSCOPE_LINK);
+ } else if ((addr & IN_PRIVATE8_MASK) == IN_PRIVATE8_NET ||
+ (addr & IN_PRIVATE12_MASK) == IN_PRIVATE12_NET ||
+ (addr & IN_PRIVATE16_MASK) == IN_PRIVATE16_NET) {
+ return (LX_RTSCOPE_SITE);
+ } else {
+ return (LX_RTSCOPE_UNIVERSE);
+ }
+}
+
+/*
+ * Takes an IPv6 address and returns the address scope.
+ */
+static uint8_t
+lx_ipv6_rtscope(const in6_addr_t *addr)
+{
+ if (IN6_ARE_ADDR_EQUAL(addr, &ipv6_loopback)) {
+ return (LX_RTSCOPE_HOST);
+ } else if (IN6_IS_ADDR_LINKLOCAL(addr)) {
+ return (LX_RTSCOPE_LINK);
+ } else if (IN6_IS_ADDR_SITELOCAL(addr)) {
+ return (LX_RTSCOPE_SITE);
+ } else {
+ return (LX_RTSCOPE_UNIVERSE);
+ }
+}
+
+static void
+lx_netlink_getlink_lifreq(lx_netlink_reply_t *reply, struct lifreq *lifr)
+{
+ lx_netlink_ifinfomsg_t ifi;
+ int i;
+ char if_name[IFNAMSIZ];
+ struct sockaddr_dl *sdl;
+ struct sockaddr hwaddr;
+ int hwaddr_size;
+ boolean_t is_loopback;
+
+ struct {
+ int native;
+ int lx;
+ } flags[] = {
+ { IFF_UP, LX_IFF_UP },
+ { IFF_BROADCAST, LX_IFF_BROADCAST },
+ { IFF_DEBUG, LX_IFF_DEBUG },
+ { IFF_LOOPBACK, LX_IFF_LOOPBACK },
+ { IFF_POINTOPOINT, LX_IFF_POINTOPOINT },
+ { IFF_NOTRAILERS, LX_IFF_NOTRAILERS },
+ { IFF_RUNNING, LX_IFF_RUNNING },
+ { IFF_NOARP, LX_IFF_NOARP },
+ { IFF_PROMISC, LX_IFF_PROMISC },
+ { IFF_ALLMULTI, LX_IFF_ALLMULTI },
+ { IFF_MULTICAST, LX_IFF_MULTICAST },
+ { 0 }
+ };
+
+ /*
+ * illumos interfaces that contain a ':' are non-zero logical
+ * interfaces. We should only emit the name of the zeroth logical
+ * interface, since RTM_GETLINK only expects to see the name of
+ * devices. The addresses of all logical devices will be
+ * returned via an RTM_GETADDR.
+ */
+ if (strchr(lifr->lifr_name, ':') != NULL)
+ return;
+
+ /*
+ * Most of the lx_netlink module is architected to emit information in
+ * an illumos-native manner. Socket syscalls such as getsockname will
+ * not translate fields to values Linux programs would expect since
+ * that conversion is performed by the generic socket emulation.
+ *
+ * This is _not_ true of the actual protocol output from lx_netlink.
+ * Since translating it at the socket layer would be onerous, all
+ * output (including constants and names) is pre-translated to values
+ * valid for Linux.
+ */
+
+ bzero(&ifi, sizeof (ifi));
+ ifi.lxnl_ifi_family = AF_UNSPEC;
+ ifi.lxnl_ifi_change = (uint32_t)-1;
+
+ /* Convert the name to be Linux-friendly */
+ (void) strlcpy(if_name, lifr->lifr_name, IFNAMSIZ);
+ lx_ifname_convert(if_name, LX_IF_FROMNATIVE);
+ is_loopback = (strncmp(if_name, "lo", 2) == 0);
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFINDEX, lifr) != 0)
+ return;
+
+ ifi.lxnl_ifi_index = lifr->lifr_index;
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFFLAGS, lifr) != 0)
+ return;
+
+ for (i = 0; flags[i].native; i++) {
+ if (lifr->lifr_flags & flags[i].native)
+ ifi.lxnl_ifi_flags |= flags[i].lx;
+ }
+
+ /*
+ * Query the datalink address.
+ * The interface type will be included in the outgoing infomsg while
+ * the address itself will be output separately.
+ */
+ sdl = (struct sockaddr_dl *)&lifr->lifr_addr;
+ bzero(sdl, sizeof (*sdl));
+ if (!is_loopback) {
+ (void) lx_netlink_reply_ioctl(reply, SIOCGLIFHWADDR, lifr);
+ } else {
+ /* Simulate an empty hwaddr for loopback */
+ sdl->sdl_type = DL_LOOP;
+ sdl->sdl_alen = ETHERADDRL;
+ }
+ lx_stol_hwaddr(sdl, &hwaddr, &hwaddr_size);
+
+ ifi.lxnl_ifi_type = hwaddr.sa_family;
+ lx_netlink_reply_msg(reply, &ifi, sizeof (lx_netlink_ifinfomsg_t));
+
+ lx_netlink_reply_attr_string(reply, LX_NETLINK_IFLA_IFNAME, if_name);
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFMTU, lifr) != 0)
+ return;
+
+ lx_netlink_reply_attr_int32(reply, LX_NETLINK_IFLA_MTU, lifr->lifr_mtu);
+
+ if (hwaddr_size != 0) {
+ lx_netlink_reply_attr(reply, LX_NETLINK_IFLA_ADDRESS,
+ hwaddr.sa_data, hwaddr_size);
+ }
+
+ /* Emulate a txqlen of 1. (0 for loopbacks) */
+ lx_netlink_reply_attr_int32(reply, LX_NETLINK_IFLA_TXQLEN,
+ (is_loopback) ? 0 : 1);
+
+ lx_netlink_reply_send(reply);
+}
+
+static void
+lx_netlink_reply_eachfamily(lx_netlink_reply_t *reply,
+ void (*func)(lx_netlink_reply_t *, struct lifreq *), boolean_t distinct)
+{
+ lx_netlink_sock_t *sock = reply->lxnr_sock;
+ int nlifr, i;
+
+ struct {
+ int family;
+ ldi_handle_t handle;
+ struct lifconf lifc;
+ struct lifnum lifn;
+ } families[] = {
+ { AF_INET, sock->lxns_iphandle },
+ { AF_INET6, sock->lxns_ip6handle },
+ { AF_UNSPEC }
+ }, *family, *check;
+
+ for (family = families; family->family != AF_UNSPEC; family++) {
+ struct lifconf *lifc = &family->lifc;
+ struct lifnum *lifn = &family->lifn;
+
+ lifn->lifn_family = family->family;
+ sock->lxns_current = family->handle;
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFNUM, lifn) != 0)
+ break;
+
+ lifc->lifc_family = lifn->lifn_family;
+ lifc->lifc_flags = 0;
+ lifc->lifc_len = lifn->lifn_count * sizeof (struct lifreq);
+ if (lifn->lifn_count == 0) {
+ lifc->lifc_buf = NULL;
+ continue;
+ }
+ lifc->lifc_buf = kmem_alloc(lifc->lifc_len, KM_SLEEP);
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFCONF, lifc) != 0)
+ break;
+
+ nlifr = lifc->lifc_len / sizeof (lifc->lifc_req[0]);
+
+ for (i = 0; i < nlifr; i++) {
+ if (!distinct) {
+ func(reply, &lifc->lifc_req[i]);
+ continue;
+ }
+
+ /*
+ * If we have been asked to provide each interface
+ * exactly once, we need to (annoyingly) check this
+ * name against others that we've already processed for
+ * other families. Yes, this is quadratic time -- but
+ * the number of interfaces per family is expected to
+ * be very small.
+ */
+ for (check = families; check != family; check++) {
+ struct lifconf *clifc = &check->lifc;
+ int cnlifr = clifc->lifc_len /
+ sizeof (clifc->lifc_req[0]), j;
+ char *nm = lifc->lifc_req[i].lifr_name, *cnm;
+
+ for (j = 0; j < cnlifr; j++) {
+ cnm = clifc->lifc_req[j].lifr_name;
+
+ if (strcmp(nm, cnm) == 0)
+ break;
+ }
+
+ if (j != cnlifr)
+ break;
+ }
+
+ if (check != family)
+ continue;
+
+ func(reply, &lifc->lifc_req[i]);
+ }
+ }
+
+ for (family = families; family->family != AF_UNSPEC; family++) {
+ struct lifconf *lifc = &family->lifc;
+
+ if (lifc->lifc_buf != NULL)
+ kmem_free(lifc->lifc_buf, lifc->lifc_len);
+ }
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getlink(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ lx_netlink_reply_t *reply;
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_NETLINK_RTM_NEWLINK);
+
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_netlink_reply_eachfamily(reply, lx_netlink_getlink_lifreq, B_TRUE);
+ lx_netlink_reply_done(reply);
+
+ return (0);
+}
+
+static void
+lx_netlink_getaddr_lifreq(lx_netlink_reply_t *reply, struct lifreq *lifr)
+{
+ lx_netlink_ifaddrmsg_t ifa;
+
+ bzero(&ifa, sizeof (ifa));
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFINDEX, lifr) != 0)
+ return;
+
+ ifa.lxnl_ifa_index = lifr->lifr_index;
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFFLAGS, lifr) != 0)
+ return;
+
+ /*
+ * Don't report on-link subnets
+ */
+ if ((lifr->lifr_flags & IFF_NOLOCAL) != 0)
+ return;
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFSUBNET, lifr) != 0)
+ return;
+
+ ifa.lxnl_ifa_prefixlen = lifr->lifr_addrlen;
+
+ if (lx_netlink_reply_ioctl(reply, SIOCGLIFADDR, lifr) != 0)
+ return;
+
+ if (lifr->lifr_addr.ss_family == AF_INET) {
+ struct sockaddr_in *sin;
+
+ ifa.lxnl_ifa_family = LX_AF_INET;
+
+ sin = (struct sockaddr_in *)&lifr->lifr_addr;
+ ifa.lxnl_ifa_scope = lx_ipv4_rtscope(
+ sin->sin_addr.s_addr);
+
+ lx_netlink_reply_msg(reply, &ifa,
+ sizeof (lx_netlink_ifaddrmsg_t));
+
+ lx_netlink_reply_attr_int32(reply,
+ LX_NETLINK_IFA_ADDRESS, sin->sin_addr.s_addr);
+ } else {
+ struct sockaddr_in6 *sin;
+
+ ifa.lxnl_ifa_family = LX_AF_INET6;
+
+ sin = (struct sockaddr_in6 *)&lifr->lifr_addr;
+ ifa.lxnl_ifa_scope = lx_ipv6_rtscope(&sin->sin6_addr);
+
+ lx_netlink_reply_msg(reply, &ifa,
+ sizeof (lx_netlink_ifaddrmsg_t));
+
+ lx_netlink_reply_attr(reply, LX_NETLINK_IFA_ADDRESS,
+ &sin->sin6_addr, sizeof (sin->sin6_addr));
+ }
+
+ lx_netlink_reply_send(reply);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getaddr(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ lx_netlink_reply_t *reply;
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_NETLINK_RTM_NEWADDR);
+
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_netlink_reply_eachfamily(reply, lx_netlink_getaddr_lifreq, B_FALSE);
+ lx_netlink_reply_done(reply);
+
+ return (0);
+}
+
+struct lx_getroute_ctx {
+ lx_netlink_reply_t *lgrtctx_reply;
+ lx_netlink_rtmsg_t *lgrtctx_rtmsg;
+ lx_netlink_attr_t *lgrtctx_attrs[LX_NETLINK_MAX_RTA];
+ unsigned int lgrtctx_max_attr;
+ lx_netlink_attr_t *lgrtctx_rtadst;
+};
+
+static void
+lx_netlink_getroute_ipv4(ire_t *ire, struct lx_getroute_ctx *ctx)
+{
+ lx_netlink_reply_t *reply = ctx->lgrtctx_reply;
+ lx_netlink_rtmsg_t *rtmsg = ctx->lgrtctx_rtmsg;
+ lx_netlink_attr_t *rtadst = ctx->lgrtctx_rtadst;
+ lx_netlink_rtmsg_t res;
+ ill_t *ill = NULL;
+
+ /* Certain IREs are too specific for netlink */
+ if ((ire->ire_type & (IRE_BROADCAST | IRE_MULTICAST | IRE_NOROUTE |
+ IRE_LOOPBACK | IRE_LOCAL)) != 0 || ire->ire_testhidden != 0) {
+ return;
+ }
+ /*
+ * When listing routes, CLONE entries are undesired.
+ * They are required for 'ip route get' on a local address.
+ */
+ if (rtmsg->rtm_dst_len == 0 && (ire->ire_type & IRE_IF_CLONE) != 0) {
+ return;
+ }
+
+ bzero(&res, sizeof (res));
+ res.rtm_family = LX_AF_INET;
+ res.rtm_table = LX_ROUTE_TABLE_MAIN;
+ res.rtm_type = LX_RTN_UNICAST;
+ res.rtm_dst_len = ire->ire_masklen;
+
+ if (ire->ire_type & (IRE_IF_NORESOLVER|IRE_IF_RESOLVER)) {
+ /* Interface-local networks considered kernel-created */
+ res.rtm_protocol = LX_RTPROT_KERNEL;
+ res.rtm_scope = LX_RTSCOPE_LINK;
+ } else if (ire->ire_flags & RTF_STATIC) {
+ res.rtm_protocol = LX_RTPROT_STATIC;
+ }
+
+ if (rtmsg->rtm_dst_len == 0x20 && rtadst != NULL) {
+ /*
+ * SpecifY single-destination route.
+ * RTA_DST details will be added later
+ */
+ res.rtm_dst_len = rtmsg->rtm_dst_len;
+ }
+
+
+ lx_netlink_reply_msg(reply, &res, sizeof (res));
+
+ if (rtmsg->rtm_dst_len == 0x20 && rtadst != NULL) {
+ /* Add RTA_DST details for single-destination route. */
+ lx_netlink_reply_attr(reply, LX_NETLINK_RTA_DST,
+ LXATTR_PAYLOAD(rtadst), sizeof (ipaddr_t));
+ } else if (ire->ire_masklen != 0) {
+ lx_netlink_reply_attr(reply, LX_NETLINK_RTA_DST,
+ &ire->ire_addr, sizeof (ire->ire_addr));
+ }
+
+ if (ire->ire_ill != NULL) {
+ ill = ire->ire_ill;
+ } else if (ire->ire_dep_parent != NULL) {
+ ill = ire->ire_dep_parent->ire_ill;
+ }
+
+ if (ill != NULL) {
+ uint32_t ifindex, addr_src;
+
+ ifindex = ill->ill_phyint->phyint_ifindex;
+ lx_netlink_reply_attr(reply, LX_NETLINK_RTA_OIF,
+ &ifindex, sizeof (ifindex));
+
+ addr_src = ill->ill_ipif->ipif_lcl_addr;
+ lx_netlink_reply_attr(reply, LX_NETLINK_RTA_PREFSRC,
+ &addr_src, sizeof (addr_src));
+ }
+
+ if (ire->ire_flags & RTF_GATEWAY) {
+ lx_netlink_reply_attr(reply, LX_NETLINK_RTA_GATEWAY,
+ &ire->ire_gateway_addr, sizeof (ire->ire_gateway_addr));
+ }
+
+ lx_netlink_reply_send(reply);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getroute(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr,
+ mblk_t *mp)
+{
+ struct lx_getroute_ctx ctx;
+ lx_netlink_reply_t *reply;
+ lx_netlink_rtmsg_t rtmsg, *rtmsgp;
+ int rtmsg_size = sizeof (rtmsg);
+ netstack_t *ns;
+ int i;
+
+ bzero(&ctx, sizeof (ctx));
+ ctx.lgrtctx_max_attr = LX_NETLINK_MAX_RTA;
+
+ if (lx_netlink_parse_msg_attrs(mp, (void **)&rtmsgp,
+ rtmsg_size, ctx.lgrtctx_attrs, &ctx.lgrtctx_max_attr) != 0) {
+ return (EPROTO);
+ }
+
+ /*
+ * Older version of libnetlink send a truncated rtmsg struct for
+ * certain RTM_GETROUTE queries. We must detect this condition and
+ * truncate our input to prevent later confusion.
+ */
+ if (curproc->p_zone->zone_brand == &lx_brand &&
+ lx_kern_release_cmp(curproc->p_zone, "2.6.32") <= 0 &&
+ rtmsgp->rtm_dst_len == 0) {
+ rtmsg_size = sizeof (rtmsg.rtm_family);
+ }
+ bzero(&rtmsg, sizeof (rtmsg));
+ bcopy(rtmsgp, &rtmsg, rtmsg_size);
+ ctx.lgrtctx_rtmsg = &rtmsg;
+
+ /* If RTA_DST was passed, it effects later decisions */
+ for (i = 0; i < ctx.lgrtctx_max_attr; i++) {
+ lx_netlink_attr_t *attr = ctx.lgrtctx_attrs[i];
+
+ if (attr->lxna_type == LX_NETLINK_RTA_DST &&
+ attr->lxna_len == LXATTR_LEN(sizeof (ipaddr_t))) {
+ ctx.lgrtctx_rtadst = attr;
+ break;
+ }
+ }
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_NETLINK_RTM_NEWROUTE);
+ if (reply == NULL) {
+ return (ENOMEM);
+ }
+ ctx.lgrtctx_reply = reply;
+
+ /* Do not report anything outside the main table */
+ if (rtmsg.rtm_table != LX_ROUTE_TABLE_MAIN &&
+ rtmsg.rtm_table != 0) {
+ lx_netlink_reply_done(reply);
+ return (0);
+ }
+
+ ns = netstack_get_current();
+ if (ns == NULL) {
+ lx_netlink_reply_done(reply);
+ return (0);
+ }
+ if (rtmsg.rtm_family == LX_AF_INET || rtmsg.rtm_family == 0) {
+ if (rtmsg.rtm_dst_len == 0x20 && ctx.lgrtctx_rtadst != NULL) {
+ /* resolve route for host */
+ ipaddr_t *dst = LXATTR_PAYLOAD(ctx.lgrtctx_rtadst);
+ ire_t *ire_dst;
+
+ ire_dst = ire_route_recursive_dstonly_v4(*dst, 0, 0,
+ ns->netstack_ip);
+ lx_netlink_getroute_ipv4(ire_dst, &ctx);
+ ire_refrele(ire_dst);
+ } else {
+ /* get route listing */
+ ire_walk_v4(&lx_netlink_getroute_ipv4, &ctx, ALL_ZONES,
+ ns->netstack_ip);
+ }
+ }
+ if (rtmsg.rtm_family == LX_AF_INET6) {
+ /* punt on ipv6 for now */
+ netstack_rele(ns);
+ lx_netlink_reply_done(reply);
+ return (EPROTO);
+ }
+ netstack_rele(ns);
+
+ lx_netlink_reply_done(reply);
+ return (0);
+}
+
+/*
+ * Auditing callback to emit response.
+ */
+static void
+lx_netlink_au_cb(void *r, void *b, uint_t blen)
+{
+ lx_netlink_reply_t *reply = (lx_netlink_reply_t *)r;
+
+ lx_netlink_reply_msg(reply, b, blen);
+}
+
+/*
+ * Audit get
+ */
+static int
+lx_netlink_au_get(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr)
+{
+ lx_netlink_reply_t *reply;
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_AUDIT_GET);
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_audit_get(reply, lx_netlink_au_cb);
+ lx_netlink_reply_send(reply);
+ lx_netlink_reply_done(reply);
+ return (0);
+}
+
+/*
+ * Set or clear flag indicating socket is being used to communicate with the
+ * user-level auditd. Also update the counter which prevents this module
+ * from unloading while auditing is using the socket to the auditd.
+ */
+static void
+lx_netlink_au_sock_cb(void *s, boolean_t set)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)s;
+
+ if (set) {
+ lxsock->lxns_flags |= LXNLF_AUDITD;
+ mutex_enter(&lx_netlink_lock);
+ lx_netlink_audit_cnt++;
+ mutex_exit(&lx_netlink_lock);
+ } else {
+ lxsock->lxns_flags &= ~LXNLF_AUDITD;
+ mutex_enter(&lx_netlink_lock);
+ VERIFY(lx_netlink_audit_cnt > 0);
+ lx_netlink_audit_cnt--;
+ mutex_exit(&lx_netlink_lock);
+ }
+}
+
+static int
+lx_netlink_au_set(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ lx_netlink_reply_t *reply;
+ void *datap;
+ size_t datalen;
+ int err;
+
+ datap = (void *)(mp->b_rptr + sizeof (lx_netlink_hdr_t));
+ datalen = MBLKL(mp) - sizeof (lx_netlink_hdr_t);
+
+ err = lx_audit_set(lxsock, datap, datalen, lx_netlink_au_sock_cb);
+ if (err != 0)
+ return (err);
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_AUDIT_SET);
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_netlink_reply_done(reply);
+ return (0);
+}
+
+/*
+ * Audit append rule
+ */
+static int
+lx_netlink_au_ar(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ lx_netlink_reply_t *reply;
+ void *datap;
+ size_t datalen;
+ int err;
+
+ /*
+ * TODO: At this time, everything we support fits in a single mblk,
+ * but as we add additional field support, eventually we might need
+ * to handle an mblk chain for really long string data in the
+ * rulep->lxar_buf.
+ */
+ if (mp->b_cont != NULL)
+ return (EINVAL);
+
+ datap = (void *)(mp->b_rptr + sizeof (lx_netlink_hdr_t));
+ datalen = MBLKL(mp) - sizeof (lx_netlink_hdr_t);
+
+ if ((err = lx_audit_append_rule(datap, datalen)) != 0)
+ return (err);
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_AUDIT_ADD_RULE);
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_netlink_reply_done(reply);
+ return (0);
+}
+
+/*
+ * Audit delete rule
+ */
+static int
+lx_netlink_au_dr(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ lx_netlink_reply_t *reply;
+ void *datap;
+ size_t datalen;
+ int err;
+
+ /*
+ * TODO: At this time, everything we support fits in a single mblk,
+ * but as we add additional field support, eventually we might need
+ * to handle an mblk chain for really long string data in the
+ * rulep->lxar_buf.
+ */
+ if (mp->b_cont != NULL)
+ return (EINVAL);
+
+ datap = (void *)(mp->b_rptr + sizeof (lx_netlink_hdr_t));
+ datalen = MBLKL(mp) - sizeof (lx_netlink_hdr_t);
+
+ if ((err = lx_audit_delete_rule(datap, datalen)) != 0)
+ return (err);
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_AUDIT_DEL_RULE);
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_netlink_reply_done(reply);
+ return (0);
+}
+
+/*
+ * Auditing callback to emit rule list.
+ */
+static void
+lx_netlink_au_lr_cb(void *r, void *b0, uint_t b0_len, void *b1, uint_t b1_len)
+{
+ lx_netlink_reply_t *reply = (lx_netlink_reply_t *)r;
+
+ lx_netlink_reply_msg(reply, b0, b0_len);
+ lx_netlink_reply_add(reply, b1, b1_len);
+ lx_netlink_reply_send(reply);
+}
+
+/*
+ * Audit list rules
+ */
+static int
+lx_netlink_au_lr(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr)
+{
+ lx_netlink_reply_t *reply;
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_AUDIT_LIST_RULES);
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_audit_list_rules(reply, lx_netlink_au_lr_cb);
+ lx_netlink_reply_done(reply);
+ return (0);
+}
+
+/*
+ * Audit get feature
+ */
+static int
+lx_netlink_au_gf(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr)
+{
+ lx_netlink_reply_t *reply;
+
+ reply = lx_netlink_reply(lxsock, hdr, LX_AUDIT_GET_FEATURE);
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_audit_get_feature(reply, lx_netlink_au_cb);
+ lx_netlink_reply_send(reply);
+ lx_netlink_reply_done(reply);
+ return (0);
+}
+
+/*
+ * Audit user message
+ * User messages are submitted as free-form messages which need to get sent
+ * back up to the auditd. This includes informative messages such as starting
+ * or stopping auditing.
+ */
+static int
+lx_netlink_au_um(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ lx_netlink_reply_t *reply;
+ size_t datalen;
+ void *bp;
+
+ bp = mp->b_rptr + sizeof (lx_netlink_hdr_t);
+ datalen = MBLKL(mp) - (sizeof (lx_netlink_hdr_t));
+
+ /*
+ * TODO: At this time, everything we support fits in a single mblk,
+ * but eventually we might need to handle an mblk chain for a really
+ * long user message.
+ */
+ if (mp->b_cont != NULL)
+ return (EINVAL);
+
+ lx_audit_emit_user_msg(hdr->lxnh_type, datalen, bp);
+
+ if (hdr->lxnh_flags & LX_NETLINK_NLM_F_ACK) {
+ reply = lx_netlink_reply(lxsock, hdr, hdr->lxnh_type);
+ if (reply == NULL)
+ return (ENOMEM);
+
+ lx_netlink_reply_done(reply);
+ }
+ return (0);
+}
+
+static int
+lx_netlink_au_emit_cb(void *s, uint_t type, const char *msg, uint_t size)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)s;
+ lx_netlink_hdr_t *hdr;
+ mblk_t *mp, *mp1;
+ int error;
+ uint32_t len;
+
+ len = LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t));
+ if (msg != NULL) {
+ len += LXNLMSG_ALIGN(size);
+ if (len > lxsock->lxns_bufsize)
+ return (E2BIG);
+ }
+
+ if ((mp = allocb(lxsock->lxns_bufsize, 0)) == NULL) {
+ return (ENOMEM);
+ }
+
+ bzero(mp->b_rptr, lxsock->lxns_bufsize);
+ /* LINTED: E_BAD_PTR_CAST_ALIGN */
+ hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+ hdr->lxnh_flags = LX_NETLINK_NLM_F_MULTI;
+ hdr->lxnh_len = len;
+ hdr->lxnh_type = (msg == NULL ? LX_NETLINK_NLMSG_DONE : type);
+ hdr->lxnh_seq = 0;
+ hdr->lxnh_pid = 0;
+
+ mp->b_wptr += LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t));
+ if (msg != NULL) {
+ bcopy(msg, mp->b_wptr, size);
+ mp->b_wptr += LXNLMSG_ALIGN(size);
+ }
+
+ /* As in lx_netlink_reply_sendup, send as T_UNITDATA_IND message. */
+ if ((mp1 = lx_netlink_alloc_mp1(lxsock)) == NULL) {
+ freeb(mp);
+ return (ENOMEM);
+ }
+ mp1->b_cont = mp;
+
+ /*
+ * If the socket is currently flow-controlled, do not allow further
+ * data to be sent out. Messages of the NLMSG_DONE type, triggered by
+ * passing msg == NULL, are excempt from this restriction.
+ */
+ mutex_enter(&lxsock->lxns_flowctl_mtx);
+ if (lxsock->lxns_flowctrled && msg != NULL) {
+ mutex_exit(&lxsock->lxns_flowctl_mtx);
+ freemsg(mp1);
+ return (ENOSPC);
+ }
+
+ lxsock->lxns_upcalls->su_recv(lxsock->lxns_uphandle, mp1,
+ msgdsize(mp1), 0, &error, NULL);
+
+ /*
+ * The socket indicated that it is now flow-controlled. That said, it
+ * still queued the last message, so indicated success (but track the
+ * flow-controlled state).
+ */
+ if (error == ENOSPC) {
+ lxsock->lxns_flowctrled = B_TRUE;
+ lx_netlink_flowctrld++;
+ error = 0;
+ }
+ mutex_exit(&lxsock->lxns_flowctl_mtx);
+
+ return (error);
+}
+
+static int
+lx_netlink_audit(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ /*
+ * This is paranoia, in case our socket somehow escaped the zone.
+ */
+ if (curproc->p_zone->zone_brand != &lx_brand)
+ return (ECONNREFUSED);
+
+ if (MBLKL(mp) < sizeof (lx_netlink_hdr_t))
+ return (EINVAL);
+
+ /*
+ * Ensure audit state is setup whenever we get an audit control msg.
+ * However, we skip initialization for user messages since some apps
+ * (e.g. systemd) blindly send audit messages, even though auditing
+ * is not installed or in use. Uninitialized state is handled in
+ * lx_audit_user_msg().
+ */
+ if (hdr->lxnh_type < LX_AUDIT_USER_MSG_START)
+ lx_audit_init(lx_netlink_au_emit_cb);
+
+ /*
+ * Within Linux, when a netlink message requests an ack, the code
+ * first sends the ack as an error response (NLMSG_ERROR) with an
+ * error code of 0.
+ *
+ * TODO: this needs more work, but is unnecessary for now.
+ * if (hdr->lxnh_flags & LX_NETLINK_NLM_F_ACK) {
+ * reply = lx_netlink_reply(lxsock, hdr, LX_NETLINK_NLMSG_ERROR);
+ * if (reply == NULL)
+ * return (ENOMEM);
+ * lx_netlink_reply_ack(reply);
+ * }
+ */
+
+ if (hdr->lxnh_type >= LX_AUDIT_USER_MSG_START) {
+ return (lx_netlink_au_um(lxsock, hdr, mp));
+ }
+
+ switch (hdr->lxnh_type) {
+ case LX_AUDIT_GET:
+ return (lx_netlink_au_get(lxsock, hdr));
+ case LX_AUDIT_SET:
+ return (lx_netlink_au_set(lxsock, hdr, mp));
+ case LX_AUDIT_ADD_RULE:
+ return (lx_netlink_au_ar(lxsock, hdr, mp));
+ case LX_AUDIT_DEL_RULE:
+ return (lx_netlink_au_dr(lxsock, hdr, mp));
+ case LX_AUDIT_LIST_RULES:
+ return (lx_netlink_au_lr(lxsock, hdr));
+ case LX_AUDIT_GET_FEATURE:
+ return (lx_netlink_au_gf(lxsock, hdr));
+ }
+
+ /*
+ * For all other auditing messages (i.e. one we don't yet support), we
+ * return ECONNREFUSED.
+ */
+ return (ECONNREFUSED);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_kobject_uevent(lx_netlink_sock_t *lxsock,
+ lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+ /*
+ * For udev, we just silently accept all writes and never actually
+ * reply with anything -- which appears to be sufficient for things
+ * to work.
+ */
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_send(sock_lower_handle_t handle, mblk_t *mp,
+ struct nmsghdr *msg, cred_t *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+ lx_netlink_hdr_t *hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+ int i, rval;
+
+ static struct {
+ int proto;
+ uint16_t type;
+ int (*func)(lx_netlink_sock_t *, lx_netlink_hdr_t *, mblk_t *);
+ } handlers[] = {
+ { LX_NETLINK_ROUTE,
+ LX_NETLINK_RTM_GETLINK, lx_netlink_getlink },
+ { LX_NETLINK_ROUTE,
+ LX_NETLINK_RTM_GETADDR, lx_netlink_getaddr },
+ { LX_NETLINK_ROUTE,
+ LX_NETLINK_RTM_GETROUTE, lx_netlink_getroute },
+ { LX_NETLINK_AUDIT,
+ LX_NETLINK_NLMSG_NONE, lx_netlink_audit },
+ { LX_NETLINK_KOBJECT_UEVENT,
+ LX_NETLINK_NLMSG_NONE, lx_netlink_kobject_uevent },
+ { LX_NETLINK_NLMSG_NOOP, LX_NETLINK_NLMSG_NONE, NULL }
+ };
+
+ if (msg->msg_name != NULL) {
+ lx_netlink_sockaddr_t *lxsa =
+ (lx_netlink_sockaddr_t *)msg->msg_name;
+
+ if (msg->msg_namelen < sizeof (lx_netlink_sockaddr_t) ||
+ lxsa->lxnl_family != AF_LX_NETLINK) {
+ return (EINVAL);
+ }
+
+ /*
+ * If this message is targeted beyond just the OS kernel, an
+ * access check must be made.
+ */
+ if (lxsa->lxnl_port != 0 || lxsa->lxnl_groups != 0) {
+ int err;
+ char buf[LX_UNSUP_BUFSZ];
+
+ err = lx_netlink_access(lxsock, cr, LXNL_SENDMSG);
+ if (err != 0) {
+ return (err);
+ }
+
+ /*
+ * Support for netlink messages beyond rtnetlink(7) is
+ * non-existent at this time. These messages are
+ * tolerated, rather than tossing a potentially fatal
+ * error to the application.
+ */
+ (void) snprintf(buf, LX_UNSUP_BUFSZ,
+ "netlink sendmsg addr port:%X groups:%08X",
+ lxsa->lxnl_port, lxsa->lxnl_groups);
+ lx_unsupported(buf);
+ }
+ }
+
+ if (DB_TYPE(mp) != M_DATA || MBLKL(mp) < sizeof (lx_netlink_hdr_t)) {
+ freemsg(mp);
+ return (EPROTO);
+ }
+
+ for (i = 0; handlers[i].func != NULL; i++) {
+ if (lxsock->lxns_proto != handlers[i].proto)
+ continue;
+
+ if (handlers[i].type != LX_NETLINK_NLMSG_NONE &&
+ hdr->lxnh_type != handlers[i].type)
+ continue;
+
+ rval = handlers[i].func(lxsock, hdr, mp);
+ freemsg(mp);
+
+ return (rval);
+ }
+
+ /*
+ * An unrecognized message. We will bounce up an EOPNOTSUPP reply.
+ */
+ rval = lx_netlink_reply_error(lxsock, hdr, EOPNOTSUPP);
+ freemsg(mp);
+
+ return (rval);
+}
+
+static void
+lx_netlink_clr_flowctrl(sock_lower_handle_t handle)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+
+ mutex_enter(&lxsock->lxns_flowctl_mtx);
+ lxsock->lxns_flowctrled = B_FALSE;
+ mutex_exit(&lxsock->lxns_flowctl_mtx);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_close(sock_lower_handle_t handle, int flags, cred_t *cr)
+{
+ lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle, *sock, **prev;
+
+ if (lxsock->lxns_flags & LXNLF_AUDITD)
+ lx_audit_stop_worker(lxsock, lx_netlink_au_sock_cb);
+
+ mutex_enter(&lx_netlink_lock);
+
+ prev = &lx_netlink_head;
+
+ for (sock = *prev; sock != lxsock; sock = sock->lxns_next)
+ prev = &sock->lxns_next;
+
+ *prev = sock->lxns_next;
+
+ mutex_exit(&lx_netlink_lock);
+
+ (void) ldi_close(lxsock->lxns_iphandle, FREAD, kcred);
+ (void) ldi_close(lxsock->lxns_ip6handle, FREAD, kcred);
+ mutex_destroy(&lxsock->lxns_flowctl_mtx);
+ kmem_free(lxsock, sizeof (lx_netlink_sock_t));
+
+ return (0);
+}
+
+static sock_downcalls_t sock_lx_netlink_downcalls = {
+ lx_netlink_activate, /* sd_activate */
+ sock_accept_notsupp, /* sd_accept */
+ lx_netlink_bind, /* sd_bind */
+ sock_listen_notsupp, /* sd_listen */
+ sock_connect_notsupp, /* sd_connect */
+ sock_getpeername_notsupp, /* sd_getpeername */
+ lx_netlink_getsockname, /* sd_getsockname */
+ lx_netlink_getsockopt, /* sd_getsockopt */
+ lx_netlink_setsockopt, /* sd_setsockopt */
+ lx_netlink_send, /* sd_send */
+ NULL, /* sd_send_uio */
+ NULL, /* sd_recv_uio */
+ NULL, /* sd_poll */
+ sock_shutdown_notsupp, /* sd_shutdown */
+ lx_netlink_clr_flowctrl, /* sd_clr_flowctrl */
+ sock_ioctl_notsupp, /* sd_ioctl */
+ lx_netlink_close /* sd_close */
+};
+
+/*ARGSUSED*/
+static sock_lower_handle_t
+lx_netlink_create(int family, int type, int proto,
+ sock_downcalls_t **sock_downcalls, uint_t *smodep, int *errorp,
+ int flags, cred_t *credp)
+{
+ lx_netlink_sock_t *lxsock;
+ ldi_handle_t handle, handle6;
+ cred_t *kcred = zone_kcred();
+ int err;
+
+ if (family != AF_LX_NETLINK ||
+ (type != SOCK_DGRAM && type != SOCK_RAW)) {
+ *errorp = EPROTONOSUPPORT;
+ return (NULL);
+ }
+
+ switch (proto) {
+ case LX_NETLINK_ROUTE:
+ case LX_NETLINK_AUDIT:
+ case LX_NETLINK_KOBJECT_UEVENT:
+ break;
+
+ default:
+ *errorp = EPROTONOSUPPORT;
+ return (NULL);
+ }
+
+ if ((err = ldi_open_by_name(DEV_IP, FREAD, kcred,
+ &handle, lx_netlink_ldi)) != 0) {
+ *errorp = err;
+ return (NULL);
+ }
+
+ if ((err = ldi_open_by_name(DEV_IP6, FREAD, kcred,
+ &handle6, lx_netlink_ldi)) != 0) {
+ (void) ldi_close(handle, FREAD, kcred);
+ *errorp = err;
+ return (NULL);
+ }
+
+ *sock_downcalls = &sock_lx_netlink_downcalls;
+ *smodep = SM_ATOMIC;
+
+ lxsock = kmem_zalloc(sizeof (lx_netlink_sock_t), KM_SLEEP);
+ lxsock->lxns_iphandle = handle;
+ lxsock->lxns_ip6handle = handle6;
+ lxsock->lxns_bufsize = lx_netlink_bufsize;
+ lxsock->lxns_proto = proto;
+ mutex_init(&lxsock->lxns_flowctl_mtx, NULL, MUTEX_DEFAULT, NULL);
+
+ mutex_enter(&lx_netlink_lock);
+
+ lxsock->lxns_next = lx_netlink_head;
+ lx_netlink_head = lxsock;
+
+ mutex_exit(&lx_netlink_lock);
+
+ return ((sock_lower_handle_t)lxsock);
+}
+
+static void
+lx_netlink_init(void)
+{
+ major_t major = mod_name_to_major("ip");
+ int err;
+
+ VERIFY(major != DDI_MAJOR_T_NONE);
+
+ err = ldi_ident_from_major(major, &lx_netlink_ldi);
+ VERIFY(err == 0);
+}
+
+static void
+lx_netlink_fini(void)
+{
+ ldi_ident_release(lx_netlink_ldi);
+}
+
+static smod_reg_t sinfo = {
+ SOCKMOD_VERSION,
+ "lx_netlink",
+ SOCK_UC_VERSION,
+ SOCK_DC_VERSION,
+ lx_netlink_create,
+ NULL
+};
+
+/* modldrv structure */
+static struct modlsockmod sockmod = {
+ &mod_sockmodops, "AF_LX_NETLINK socket module", &sinfo
+};
+
+/* modlinkage structure */
+static struct modlinkage ml = {
+ MODREV_1,
+ &sockmod,
+ NULL
+};
+
+int
+_init(void)
+{
+ int err;
+
+ lx_netlink_init();
+
+ if ((err = mod_install(&ml)) != 0)
+ lx_netlink_fini();
+
+ return (err);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&ml, modinfop));
+}
+
+int
+_fini(void)
+{
+ int err = 0;
+
+ mutex_enter(&lx_netlink_lock);
+
+ if (lx_netlink_head != NULL || lx_netlink_audit_cnt != 0)
+ err = EBUSY;
+
+ mutex_exit(&lx_netlink_lock);
+
+ if (err == 0) {
+ lx_audit_cleanup();
+ if ((err = mod_remove(&ml)) == 0)
+ lx_netlink_fini();
+ }
+
+ return (err);
+}
diff --git a/usr/src/uts/common/brand/lx/io/lx_ptm.c b/usr/src/uts/common/brand/lx/io/lx_ptm.c
new file mode 100644
index 0000000000..23e0c6f459
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/io/lx_ptm.c
@@ -0,0 +1,1188 @@
+/*
+ * 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 2016 Joyent, Inc. All rights reserved.
+ */
+
+
+/*
+ * This driver attempts to emulate some of the the behaviors of
+ * Linux terminal devices (/dev/ptmx and /dev/pts/[0-9][0-9]*) on Solaris
+ *
+ * It does this by layering over the /dev/ptmx device and intercepting
+ * opens to it.
+ *
+ * This driver makes the following assumptions about the way the ptm/pts
+ * drivers on Solaris work:
+ *
+ * - all opens of the /dev/ptmx device node return a unique dev_t.
+ *
+ * - the dev_t minor node value for each open ptm instance corrospondes
+ * to it's associated slave terminal device number. ie. the path to
+ * the slave terminal device associated with an open ptm instance
+ * who's dev_t minor node vaue is 5, is /dev/pts/5.
+ *
+ * - the ptm driver always allocates the lowest numbered slave terminal
+ * device possible.
+ */
+
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/devops.h>
+#include <sys/file.h>
+#include <sys/filio.h>
+#include <sys/kstr.h>
+#include <sys/lx_ptm.h>
+#include <sys/modctl.h>
+#include <sys/pathname.h>
+#include <sys/ptms.h>
+#include <sys/ptyvar.h>
+#include <sys/stat.h>
+#include <sys/stropts.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/sdt.h>
+
+#define LP_PTM_PATH "/dev/ptmx"
+#define LP_PTS_PATH "/dev/pts/"
+#define LP_PTS_DRV_NAME "pts"
+#define LP_PTS_USEC_DELAY (5 * 1000) /* 5 ms */
+#define LP_PTS_USEC_DELAY_MAX (5 * MILLISEC) /* 5 ms */
+
+/*
+ * this driver is layered on top of the ptm driver. we'd like to
+ * make this drivers minor name space a mirror of the ptm drivers
+ * namespace, but we can't actually do this. the reason is that the
+ * ptm driver is opened via the clone driver. there for no minor nodes
+ * of the ptm driver are actually accessible via the filesystem.
+ * since we're not a streams device we can't be opened by the clone
+ * driver. there for we need to have at least minor node accessible
+ * via the filesystem so that consumers can open it. we use the device
+ * node with a minor number of 0 for this purpose. what this means is
+ * that minor node 0 can't be used to map ptm minor node 0. since this
+ * minor node is now reserved we need to shift our ptm minor node
+ * mappings by one. ie. a ptm minor node with a value of 0 will
+ * corrospond to our minor node with a value of 1. these mappings are
+ * managed with the following macros.
+ */
+#define DEVT_TO_INDEX(x) LX_PTM_DEV_TO_PTS(x)
+#define INDEX_TO_MINOR(x) ((x) + 1)
+
+/*
+ * grow our layered handle array by the same size increment that the ptm
+ * driver uses to grow the pty device space - PTY_MAXDELTA
+ */
+#define LP_PTY_INC 128
+
+/*
+ * lx_ptm_ops contains state information about outstanding operations on the
+ * underlying master terminal device. Currently we only track information
+ * for read operations.
+ *
+ * Note that this data has not been rolled directly into the lx_ptm_handle
+ * structure because we can't put mutex's of condition variables into
+ * lx_ptm_handle structure. The reason is that the array of lx_ptm_handle
+ * structures linked to from the global lx_ptm state can be resized
+ * dynamically, and when it's resized, the new array is at a different
+ * memory location and the old array memory is discarded. Mutexs and cvs
+ * are accessed based off their address, so if this array was re-sized while
+ * there were outstanding operations on any mutexs or cvs in the array
+ * then the system would tip over. In the future the lx_ptm_handle structure
+ * array should probably be replaced with either an array of pointers to
+ * lx_ptm_handle structures or some other kind of data structure containing
+ * pointers to lx_ptm_handle structures. Then the lx_ptm_ops structure
+ * could be folded directly into the lx_ptm_handle structures. (This will
+ * also require the definition of a new locking mechanism to protect the
+ * contents of lx_ptm_handle structures.)
+ */
+typedef struct lx_ptm_ops {
+ int lpo_rops;
+ kcondvar_t lpo_rops_cv;
+ kmutex_t lpo_rops_lock;
+} lx_ptm_ops_t;
+
+/*
+ * Every open of the master terminal device in a zone results in a new
+ * lx_ptm_handle handle allocation. These handles are stored in an array
+ * hanging off the lx_ptm_state structure.
+ */
+typedef struct lx_ptm_handle {
+ /* Device handle to the underlying real /dev/ptmx master terminal. */
+ ldi_handle_t lph_handle;
+
+ /* Flag to indicate if TIOCPKT mode has been enabled. */
+ int lph_pktio;
+
+ /* Number of times the slave device has been opened/closed. */
+ int lph_eofed;
+
+ /* Callback handler in the ptm driver to check if slave is open. */
+ ptmptsopencb_t lph_ppocb;
+
+ /* Pointer to state for operations on underlying device. */
+ lx_ptm_ops_t *lph_lpo;
+} lx_ptm_handle_t;
+
+/*
+ * Global state for the lx_ptm driver.
+ */
+typedef struct lx_ptm_state {
+ /* lx_ptm device devinfo pointer */
+ dev_info_t *lps_dip;
+
+ /* LDI ident used to open underlying real /dev/ptmx master terminals. */
+ ldi_ident_t lps_li;
+
+ /* pts drivers major number */
+ major_t lps_pts_major;
+
+ /* rw lock used to manage access and growth of lps_lh_array */
+ krwlock_t lps_lh_rwlock;
+
+ /* number of elements in lps_lh_array */
+ uint_t lps_lh_count;
+
+ /* Array of handles to underlying real /dev/ptmx master terminals. */
+ lx_ptm_handle_t *lps_lh_array;
+} lx_ptm_state_t;
+
+/* Pointer to the lx_ptm global state structure. */
+static lx_ptm_state_t lps;
+
+/*
+ * List of modules to be autopushed onto slave terminal devices when they
+ * are opened in an lx branded zone.
+ */
+static char *lx_pts_mods[] = {
+ "ptem",
+ "ldterm",
+ "ttcompat",
+ NULL
+};
+
+static void
+lx_ptm_lh_grow(uint_t index)
+{
+ uint_t new_lh_count, old_lh_count;
+ lx_ptm_handle_t *new_lh_array, *old_lh_array;
+
+ /*
+ * allocate a new array. we drop the rw lock on the array so that
+ * readers can still access devices in case our memory allocation
+ * blocks.
+ */
+ new_lh_count = MAX(lps.lps_lh_count + LP_PTY_INC, index + 1);
+ new_lh_array =
+ kmem_zalloc(sizeof (lx_ptm_handle_t) * new_lh_count, KM_SLEEP);
+
+ /*
+ * double check that we still actually need to increase the size
+ * of the array
+ */
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+ if (index < lps.lps_lh_count) {
+ /* someone beat us to it so there's nothing more to do */
+ rw_exit(&lps.lps_lh_rwlock);
+ kmem_free(new_lh_array,
+ sizeof (lx_ptm_handle_t) * new_lh_count);
+ return;
+ }
+
+ /* copy the existing data into the new array */
+ ASSERT((lps.lps_lh_count != 0) || (lps.lps_lh_array == NULL));
+ ASSERT((lps.lps_lh_count == 0) || (lps.lps_lh_array != NULL));
+ if (lps.lps_lh_count != 0) {
+ bcopy(lps.lps_lh_array, new_lh_array,
+ sizeof (lx_ptm_handle_t) * lps.lps_lh_count);
+ }
+
+ /* save info on the old array */
+ old_lh_array = lps.lps_lh_array;
+ old_lh_count = lps.lps_lh_count;
+
+ /* install the new array */
+ lps.lps_lh_array = new_lh_array;
+ lps.lps_lh_count = new_lh_count;
+
+ rw_exit(&lps.lps_lh_rwlock);
+
+ /* free the old array */
+ if (old_lh_array != NULL) {
+ kmem_free(old_lh_array,
+ sizeof (lx_ptm_handle_t) * old_lh_count);
+ }
+}
+
+static void
+lx_ptm_lh_insert(uint_t index, ldi_handle_t lh)
+{
+ lx_ptm_ops_t *lpo;
+
+ ASSERT(lh != NULL);
+
+ /* Allocate and initialize the ops structure */
+ lpo = kmem_zalloc(sizeof (lx_ptm_ops_t), KM_SLEEP);
+ mutex_init(&lpo->lpo_rops_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&lpo->lpo_rops_cv, NULL, CV_DEFAULT, NULL);
+
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ /* check if we need to grow the size of the layered handle array */
+ if (index >= lps.lps_lh_count) {
+ rw_exit(&lps.lps_lh_rwlock);
+ lx_ptm_lh_grow(index);
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+ }
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle == NULL);
+ ASSERT(lps.lps_lh_array[index].lph_pktio == 0);
+ ASSERT(lps.lps_lh_array[index].lph_eofed == 0);
+ ASSERT(lps.lps_lh_array[index].lph_lpo == NULL);
+
+ /* insert the new handle and return */
+ lps.lps_lh_array[index].lph_handle = lh;
+ lps.lps_lh_array[index].lph_pktio = 0;
+ lps.lps_lh_array[index].lph_eofed = 0;
+ lps.lps_lh_array[index].lph_lpo = lpo;
+
+ rw_exit(&lps.lps_lh_rwlock);
+}
+
+static ldi_handle_t
+lx_ptm_lh_remove(uint_t index)
+{
+ ldi_handle_t lh;
+
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+ ASSERT(lps.lps_lh_array[index].lph_lpo->lpo_rops == 0);
+ ASSERT(!MUTEX_HELD(&lps.lps_lh_array[index].lph_lpo->lpo_rops_lock));
+
+ /* free the write handle */
+ kmem_free(lps.lps_lh_array[index].lph_lpo, sizeof (lx_ptm_ops_t));
+ lps.lps_lh_array[index].lph_lpo = NULL;
+
+ /* remove the handle and return it */
+ lh = lps.lps_lh_array[index].lph_handle;
+ lps.lps_lh_array[index].lph_handle = NULL;
+ lps.lps_lh_array[index].lph_pktio = 0;
+ lps.lps_lh_array[index].lph_eofed = 0;
+ rw_exit(&lps.lps_lh_rwlock);
+ return (lh);
+}
+
+static void
+lx_ptm_lh_get_ppocb(uint_t index, ptmptsopencb_t *ppocb)
+{
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ *ppocb = lps.lps_lh_array[index].lph_ppocb;
+ rw_exit(&lps.lps_lh_rwlock);
+}
+
+static void
+lx_ptm_lh_set_ppocb(uint_t index, ptmptsopencb_t *ppocb)
+{
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ lps.lps_lh_array[index].lph_ppocb = *ppocb;
+ rw_exit(&lps.lps_lh_rwlock);
+}
+
+static ldi_handle_t
+lx_ptm_lh_lookup(uint_t index)
+{
+ ldi_handle_t lh;
+
+ rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ /* return the handle */
+ lh = lps.lps_lh_array[index].lph_handle;
+ rw_exit(&lps.lps_lh_rwlock);
+ return (lh);
+}
+
+static lx_ptm_ops_t *
+lx_ptm_lpo_lookup(uint_t index)
+{
+ lx_ptm_ops_t *lpo;
+
+ rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_lpo != NULL);
+
+ /* return the handle */
+ lpo = lps.lps_lh_array[index].lph_lpo;
+ rw_exit(&lps.lps_lh_rwlock);
+ return (lpo);
+}
+
+static int
+lx_ptm_lh_pktio_get(uint_t index)
+{
+ int pktio;
+
+ rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ /* return the pktio state */
+ pktio = lps.lps_lh_array[index].lph_pktio;
+ rw_exit(&lps.lps_lh_rwlock);
+ return (pktio);
+}
+
+static void
+lx_ptm_lh_pktio_set(uint_t index, int pktio)
+{
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ /* set the pktio state */
+ lps.lps_lh_array[index].lph_pktio = pktio;
+ rw_exit(&lps.lps_lh_rwlock);
+}
+
+static int
+lx_ptm_lh_eofed_get(uint_t index)
+{
+ int eofed;
+
+ rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ /* return the eofed state */
+ eofed = lps.lps_lh_array[index].lph_eofed;
+ rw_exit(&lps.lps_lh_rwlock);
+ return (eofed);
+}
+
+static void
+lx_ptm_lh_eofed_set(uint_t index)
+{
+ rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+ ASSERT(index < lps.lps_lh_count);
+ ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+ /* set the eofed state */
+ lps.lps_lh_array[index].lph_eofed++;
+ rw_exit(&lps.lps_lh_rwlock);
+}
+
+static int
+lx_ptm_read_start(dev_t dev)
+{
+ lx_ptm_ops_t *lpo = lx_ptm_lpo_lookup(DEVT_TO_INDEX(dev));
+
+ mutex_enter(&lpo->lpo_rops_lock);
+ ASSERT(lpo->lpo_rops >= 0);
+
+ /* Wait for other read operations to finish */
+ while (lpo->lpo_rops != 0) {
+ if (cv_wait_sig(&lpo->lpo_rops_cv, &lpo->lpo_rops_lock) == 0) {
+ mutex_exit(&lpo->lpo_rops_lock);
+ return (-1);
+ }
+ }
+
+ /* Start a read operation */
+ VERIFY(++lpo->lpo_rops == 1);
+ mutex_exit(&lpo->lpo_rops_lock);
+ return (0);
+}
+
+static void
+lx_ptm_read_end(dev_t dev)
+{
+ lx_ptm_ops_t *lpo = lx_ptm_lpo_lookup(DEVT_TO_INDEX(dev));
+
+ mutex_enter(&lpo->lpo_rops_lock);
+ ASSERT(lpo->lpo_rops >= 0);
+
+ /* End a read operation */
+ VERIFY(--lpo->lpo_rops == 0);
+ cv_signal(&lpo->lpo_rops_cv);
+
+ mutex_exit(&lpo->lpo_rops_lock);
+}
+
+static int
+lx_ptm_pts_isopen(dev_t dev)
+{
+ ptmptsopencb_t ppocb;
+
+ lx_ptm_lh_get_ppocb(DEVT_TO_INDEX(dev), &ppocb);
+ return (ppocb.ppocb_func(ppocb.ppocb_arg));
+}
+
+static void
+lx_ptm_eof_read(ldi_handle_t lh)
+{
+ struct uio uio;
+ iovec_t iov;
+ char junk[1];
+
+ /*
+ * We can remove any EOF message from the head of the stream by
+ * doing a zero byte read from the stream.
+ */
+ iov.iov_len = 0;
+ iov.iov_base = junk;
+ uio.uio_iovcnt = 1;
+ uio.uio_iov = &iov;
+ uio.uio_resid = iov.iov_len;
+ uio.uio_offset = 0;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_fmode = 0;
+ uio.uio_extflg = 0;
+ uio.uio_llimit = MAXOFFSET_T;
+ (void) ldi_read(lh, &uio, kcred);
+}
+
+static int
+lx_ptm_eof_drop_1(dev_t dev, int *rvalp)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ int err, msg_size, msg_count;
+
+ *rvalp = 0;
+
+ /*
+ * Check if there is an EOF message (represented by a zero length
+ * data message) at the head of the stream. Note that the
+ * I_NREAD ioctl is a streams framework ioctl so it will succeed
+ * even if there have been previous write errors on this stream.
+ */
+ if ((err = ldi_ioctl(lh, I_NREAD, (intptr_t)&msg_size,
+ FKIOCTL, kcred, &msg_count)) != 0)
+ return (err);
+
+ if ((msg_count == 0) || (msg_size != 0)) {
+ /* No EOF message found */
+ return (0);
+ }
+
+ /* Record the fact that the slave device has been closed. */
+ lx_ptm_lh_eofed_set(DEVT_TO_INDEX(dev));
+
+ /* drop the EOF */
+ lx_ptm_eof_read(lh);
+ *rvalp = 1;
+ return (0);
+}
+
+static int
+lx_ptm_eof_drop(dev_t dev, int *rvalp)
+{
+ int rval, err;
+
+ if (rvalp != NULL)
+ *rvalp = 0;
+ for (;;) {
+ if ((err = lx_ptm_eof_drop_1(dev, &rval)) != 0)
+ return (err);
+ if (rval == 0)
+ return (0);
+ if (rvalp != NULL)
+ *rvalp = 1;
+ }
+}
+
+static int
+lx_ptm_data_check(dev_t dev, int ignore_eof, int *rvalp)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ int err;
+
+ *rvalp = 0;
+ if (ignore_eof) {
+ int size, rval;
+
+ if ((err = ldi_ioctl(lh, FIONREAD, (intptr_t)&size,
+ FKIOCTL, kcred, &rval)) != 0)
+ return (err);
+ if (size != 0)
+ *rvalp = 1;
+ } else {
+ int msg_size, msg_count;
+
+ if ((err = ldi_ioctl(lh, I_NREAD, (intptr_t)&msg_size,
+ FKIOCTL, kcred, &msg_count)) != 0)
+ return (err);
+ if (msg_count != 0)
+ *rvalp = 1;
+ }
+ return (0);
+}
+
+static int
+lx_ptm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int err;
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ if (ddi_create_minor_node(dip, LX_PTM_MINOR_NODE, S_IFCHR,
+ ddi_get_instance(dip), DDI_PSEUDO, 0) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ err = ldi_ident_from_dip(dip, &lps.lps_li);
+ if (err != 0) {
+ ddi_remove_minor_node(dip, ddi_get_name(dip));
+ return (DDI_FAILURE);
+ }
+
+ lps.lps_dip = dip;
+ lps.lps_pts_major = ddi_name_to_major(LP_PTS_DRV_NAME);
+
+ rw_init(&lps.lps_lh_rwlock, NULL, RW_DRIVER, NULL);
+ lps.lps_lh_count = 0;
+ lps.lps_lh_array = NULL;
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_ptm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ ldi_ident_release(lps.lps_li);
+ lps.lps_dip = NULL;
+
+ ASSERT((lps.lps_lh_count != 0) || (lps.lps_lh_array == NULL));
+ ASSERT((lps.lps_lh_count == 0) || (lps.lps_lh_array != NULL));
+ if (lps.lps_lh_array != NULL) {
+ kmem_free(lps.lps_lh_array,
+ sizeof (lx_ptm_handle_t) * lps.lps_lh_count);
+ lps.lps_lh_array = NULL;
+ lps.lps_lh_count = 0;
+ }
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_ptm_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+ struct strioctl iocb;
+ ptmptsopencb_t ppocb = { NULL, NULL };
+ ldi_handle_t lh;
+ major_t maj, our_major = getmajor(*devp);
+ minor_t min, lastmin;
+ uint_t index, anchor = 1;
+ dev_t ptm_dev;
+ int err, rval = 0;
+
+ /*
+ * Don't support the FNDELAY flag and FNONBLOCK until we either
+ * find a Linux app that opens /dev/ptmx with the O_NDELAY
+ * or O_NONBLOCK flags explicitly, or until we create test cases
+ * to determine how reads of master terminal devices opened with
+ * these flags behave in different situations on Linux. Supporting
+ * these flags will involve enhancing our read implementation
+ * and changing the way it deals with EOF notifications.
+ */
+ if (flag & (FNDELAY | FNONBLOCK))
+ return (ENOTSUP);
+
+ /*
+ * we're layered on top of the ptm driver so open that driver
+ * first. (note that we're opening /dev/ptmx in the global
+ * zone, not ourselves in the lx zone.)
+ */
+ err = ldi_open_by_name(LP_PTM_PATH, flag, credp, &lh, lps.lps_li);
+ if (err != 0)
+ return (err);
+
+ /* get the devt returned by the ptmx open */
+ err = ldi_get_dev(lh, &ptm_dev);
+ if (err != 0) {
+ (void) ldi_close(lh, flag, credp);
+ return (err);
+ }
+
+ /*
+ * we're a cloning driver so here's where we'll change the devt that we
+ * return. the ptmx is also a cloning driver so we'll just use
+ * it's minor number as our minor number (it already manages it's
+ * minor name space so no reason to duplicate the effort.)
+ */
+ index = getminor(ptm_dev);
+ *devp = makedevice(our_major, INDEX_TO_MINOR(index));
+
+ /* Get a callback function to query if the pts device is open. */
+ iocb.ic_cmd = PTMPTSOPENCB;
+ iocb.ic_timout = 0;
+ iocb.ic_len = sizeof (ppocb);
+ iocb.ic_dp = (char *)&ppocb;
+
+ err = ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, kcred, &rval);
+ if ((err != 0) || (rval != 0)) {
+ (void) ldi_close(lh, flag, credp);
+ return (EIO); /* XXX return something else here? */
+ }
+ ASSERT(ppocb.ppocb_func != NULL);
+
+ /*
+ * now setup autopush for the terminal slave device. this is
+ * necessary so that when a Linux program opens the device we
+ * can push required strmod modules onto the stream. in Solaris
+ * this is normally done by the application that actually
+ * allocates the terminal.
+ */
+ maj = lps.lps_pts_major;
+ min = index;
+ lastmin = 0;
+ err = kstr_autopush(SET_AUTOPUSH, &maj, &min, &lastmin,
+ &anchor, lx_pts_mods);
+ if (err != 0 && err != EEXIST) {
+ (void) ldi_close(lh, flag, credp);
+ return (EIO); /* XXX return something else here? */
+ }
+
+ /* save off this layered handle for future accesses */
+ lx_ptm_lh_insert(index, lh);
+ lx_ptm_lh_set_ppocb(index, &ppocb);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_ptm_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+ ldi_handle_t lh;
+ major_t maj;
+ minor_t min, lastmin;
+ uint_t index;
+ int err;
+ int i;
+
+ index = DEVT_TO_INDEX(dev);
+
+ /*
+ * we must cleanup all the state associated with this major/minor
+ * terminal pair before actually closing the ptm master device.
+ * this is required because once the close of the ptm device is
+ * complete major/minor terminal pair is immediatly available for
+ * re-use in any zone.
+ */
+
+ /* free up our saved reference for this layered handle */
+ lh = lx_ptm_lh_remove(index);
+
+ /* unconfigure autopush for the associated terminal slave device */
+ maj = lps.lps_pts_major;
+ min = index;
+ lastmin = 0;
+ for (i = 0; i < 5; i++) {
+ /*
+ * we loop here because we don't want to release this ptm
+ * node if autopush can't be disabled on the associated
+ * slave device because then bad things could happen if
+ * another brand were to get this terminal allocated
+ * to them. If we keep failing we eventually drive on so that
+ * things don't hang.
+ */
+ err = kstr_autopush(CLR_AUTOPUSH, &maj, &min, &lastmin,
+ 0, NULL);
+ if (err == 0)
+ break;
+
+ cmn_err(CE_WARN, "lx zoneid %d: error %d on kstr_autopush",
+ getzoneid(), err);
+
+ /* wait one second and try again */
+ delay(drv_usectohz(1000000));
+ }
+
+ err = ldi_close(lh, flag, credp);
+
+ /*
+ * note that we don't have to bother with changing the permissions
+ * on the associated slave device here. the reason is that no one
+ * can actually open the device untill it's associated master
+ * device is re-opened, which will result in the permissions on
+ * it being reset.
+ */
+ return (err);
+}
+
+static int
+lx_ptm_read_loop(dev_t dev, struct uio *uiop, cred_t *credp, int *loop)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ int err, rval;
+ struct uio uio = *uiop;
+
+ *loop = 0;
+
+ /*
+ * Here's another way that Linux master terminals behave differently
+ * from Solaris master terminals. If you do a read on a Linux
+ * master terminal (that was opened witout NDELAY and NONBLOCK)
+ * who's corrosponding slave terminal is currently closed and
+ * has been opened and closed at least once, Linux return -1 and
+ * set errno to EIO where as Solaris blocks.
+ */
+ if (lx_ptm_lh_eofed_get(DEVT_TO_INDEX(dev))) {
+ /* Slave has been opened and closed at least once. */
+ if (lx_ptm_pts_isopen(dev) == 0) {
+ /*
+ * Slave is closed. Make sure that data is avaliable
+ * before attempting a read.
+ */
+ if ((err = lx_ptm_data_check(dev, 0, &rval)) != 0)
+ return (err);
+
+ /* If there is no data available then return. */
+ if (rval == 0)
+ return (EIO);
+ }
+ }
+
+ /* Actually do the read operation. */
+ if ((err = ldi_read(lh, uiop, credp)) != 0)
+ return (err);
+
+ /* If read returned actual data then return. */
+ if (uio.uio_resid != uiop->uio_resid)
+ return (0);
+
+ /*
+ * This was a zero byte read (ie, an EOF). This indicates
+ * that the slave terinal device has been closed. Record
+ * the fact that the slave device has been closed and retry
+ * the read operation.
+ */
+ lx_ptm_lh_eofed_set(DEVT_TO_INDEX(dev));
+ *loop = 1;
+ return (0);
+}
+
+static int
+lx_ptm_read(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ int pktio = lx_ptm_lh_pktio_get(DEVT_TO_INDEX(dev));
+ int err, loop;
+ struct uio uio;
+ struct iovec iovp;
+
+ ASSERT(uiop->uio_iovcnt > 0);
+
+ /*
+ * If packet mode has been enabled (via TIOCPKT) we need to pad
+ * all read requests with a leading byte that indicates any
+ * relevant control status information.
+ */
+ if (pktio != 0) {
+ /*
+ * We'd like to write the control information into
+ * the current buffer but we can't yet. We don't
+ * want to modify userspace memory here only to have
+ * the read operation fail later. So instead
+ * what we'll do here is read one character from the
+ * beginning of the memory pointed to by the uio
+ * structure. This will advance the output pointer
+ * by one. Then when the read completes successfully
+ * we can update the byte that we passed over. Before
+ * we do the read make a copy of the current uiop and
+ * iovec structs so we can write to them later.
+ */
+ uio = *uiop;
+ iovp = *uiop->uio_iov;
+ uio.uio_iov = &iovp;
+
+ if (uwritec(uiop) == -1)
+ return (EFAULT);
+ }
+
+ do {
+ /*
+ * Before we actually attempt a read operation we need
+ * to make sure there's some buffer space to actually
+ * read in some data. We do this because if we're in
+ * pktio mode and the caller only requested one byte,
+ * then we've already used up that one byte and we
+ * don't want to pass this read request. Doing a 0
+ * byte read (unless there is a problem with the stream
+ * head) always returns succcess. Normally when a streams
+ * read returns 0 bytes we interpret that as an EOF on
+ * the stream (ie, the slave side has been opened and
+ * closed) and we ignore it and re-try the read operation.
+ * So if we pass on a 0 byte read here lx_ptm_read_loop()
+ * will tell us to loop around and we'll end up in an
+ * infinite loop.
+ */
+ if (uiop->uio_resid == 0)
+ break;
+
+ /*
+ * Serialize all reads. We need to do this so that we can
+ * properly emulate the behavior of master terminals on Linux.
+ * In reality this serializaion should not pose any kind of
+ * performance problem since it would be very strange to have
+ * multiple threads trying to read from the same master
+ * terminal device concurrently.
+ */
+ if (lx_ptm_read_start(dev) != 0)
+ return (EINTR);
+
+ err = lx_ptm_read_loop(dev, uiop, credp, &loop);
+ lx_ptm_read_end(dev);
+ if (err != 0)
+ return (err);
+ } while (loop != 0);
+
+ if (pktio != 0) {
+ uint8_t pktio_data = TIOCPKT_DATA;
+
+ /*
+ * Note that the control status information we
+ * pass back is faked up in the sense that we
+ * don't actually report any events, we always
+ * report a status of 0.
+ */
+ if (uiomove(&pktio_data, 1, UIO_READ, &uio) != 0)
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+static int
+lx_ptm_write(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ int err;
+
+ err = ldi_write(lh, uiop, credp);
+
+ return (err);
+}
+
+static int
+lx_ptm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ int err;
+
+ /*
+ * here we need to make sure that we never allow the
+ * I_SETSIG and I_ESETSIG ioctls to pass through. we
+ * do this because we can't support them.
+ *
+ * the native Solaris ptm device supports these ioctls because
+ * they are streams framework ioctls and all streams devices
+ * support them by default. these ioctls cause the current
+ * process to be registered with a stream and receive signals
+ * when certain stream events occur.
+ *
+ * a problem arises with cleanup of these registrations
+ * for layered drivers.
+ *
+ * normally the streams framework is notified whenever a
+ * process closes any reference to a stream and it goes ahead
+ * and cleans up these registrations. but actual device drivers
+ * are not notified when a process performs a close operation
+ * unless the process is closing the last opened reference to
+ * the device on the entire system.
+ *
+ * so while we could pass these ioctls on and allow processes
+ * to register for signal delivery, we would never receive
+ * any notification when those processes exit (or close a
+ * stream) and we wouldn't be able to unregister them.
+ *
+ * luckily these operations are streams specific and Linux
+ * doesn't support streams devices. so it doesn't actually
+ * seem like we need to support these ioctls. if it turns
+ * out that we do need to support them for some reason in
+ * the future, the current driver model will have to be
+ * enhanced to better support streams device layering.
+ */
+ if ((cmd == I_SETSIG) || (cmd == I_ESETSIG))
+ return (EINVAL);
+
+ /*
+ * here we fake up support for TIOCPKT. Linux applications expect
+ * /etc/ptmx to support this ioctl, but on Solaris it doesn't.
+ * (it is supported on older bsd style ptys.) so we'll fake
+ * up support for it here.
+ *
+ * the reason that this ioctl is emulated here instead of in
+ * userland is that this ioctl affects the results returned
+ * from read() operations. if this ioctl was emulated in
+ * userland the brand library would need to intercept all
+ * read operations and check to see if pktio was enabled
+ * for the fd being read from. since this ioctl only needs
+ * to be supported on the ptmx device it makes more sense
+ * to support it here where we can easily update the results
+ * returned for read() operations performed on ourselves.
+ */
+ if (cmd == TIOCPKT) {
+ int pktio;
+
+ if (ddi_copyin((void *)arg, &pktio, sizeof (pktio),
+ mode) != DDI_SUCCESS)
+ return (EFAULT);
+
+ if (pktio == 0)
+ lx_ptm_lh_pktio_set(DEVT_TO_INDEX(dev), 0);
+ else
+ lx_ptm_lh_pktio_set(DEVT_TO_INDEX(dev), 1);
+
+ return (0);
+ }
+
+ err = ldi_ioctl(lh, cmd, arg, mode, credp, rvalp);
+
+ /*
+ * On recent versions of Linux some apps issue the following ioctls to
+ * the master side of the ptm before opening the slave side. Because
+ * our streams modules (specifically ptem) aren't autopushed until the
+ * slave side has been opened, these ioctls will fail. To alleviate the
+ * issue we simply pretend that these ioctls have succeeded.
+ *
+ * We could push our own "lx_ptem" module onto the master side of the
+ * stream in lx_ptm_open if we need better emulation, but that would
+ * require an "lx_ptem" module which duplicates most of ptem. ptem
+ * doesn't work properly when pushed on the master side.
+ */
+ if (err == EINVAL && (cmd == TIOCSWINSZ || cmd == TCSETS) &&
+ lx_ptm_pts_isopen(dev) == 0) {
+ /* slave side not open, assume we need to succeed */
+ DTRACE_PROBE1(lx_ptm_ioctl__override, int, cmd);
+ return (0);
+ }
+
+ return (err);
+}
+
+static int
+lx_ptm_poll_loop(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp, int *loop)
+{
+ ldi_handle_t lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+ short reventsp2;
+ int err, rval;
+
+ *loop = 0;
+
+ /*
+ * If the slave device has been opened and closed at least
+ * once and the slave device is currently closed, then poll
+ * always needs to returns immediatly.
+ */
+ if ((lx_ptm_lh_eofed_get(DEVT_TO_INDEX(dev)) != 0) &&
+ (lx_ptm_pts_isopen(dev) == 0)) {
+ /* In this case always return POLLHUP */
+ *reventsp = POLLHUP;
+
+ /*
+ * Check if there really is data on the stream.
+ * If so set the correct return flags.
+ */
+ if ((err = lx_ptm_data_check(dev, 1, &rval)) != 0) {
+ /* Something went wrong. */
+ return (err);
+ }
+ if (rval != 0)
+ *reventsp |= (events & (POLLIN | POLLRDNORM));
+
+ /*
+ * Is the user checking for writability? Note that for ptm
+ * devices Linux seems to ignore the POLLWRBAND write flag.
+ */
+ if ((events & POLLWRNORM) == 0)
+ return (0);
+
+ /*
+ * To check if the stream is writable we have to actually
+ * call poll, but make sure to set anyyet to 1 to prevent
+ * the streams framework from setting up callbacks.
+ */
+ if ((err = ldi_poll(lh, POLLWRNORM, 1, &reventsp2, NULL)) != 0)
+ return (err);
+
+ *reventsp |= (reventsp2 & POLLWRNORM);
+ } else {
+ int lockstate;
+
+ /* The slave device is open, do the poll */
+ if ((err = ldi_poll(lh, events, anyyet, reventsp, phpp)) != 0)
+ return (err);
+
+ /*
+ * Drop any leading EOFs on the stream.
+ *
+ * Note that we have to use pollunlock() here to avoid
+ * recursive mutex enters in the poll framework. The
+ * reason is that if there is an EOF message on the stream
+ * then the act of reading from the queue to remove the
+ * message can cause the ptm drivers event service
+ * routine to be invoked, and if there is no open
+ * slave device then the ptm driver may generate
+ * error messages and put them on the stream. This
+ * in turn will generate a poll event and the poll
+ * framework will try to invoke any poll callbacks
+ * associated with the stream. In the process of
+ * doing that the poll framework will try to aquire
+ * locks that we are already holding. So we need to
+ * drop those locks here before we do our read.
+ */
+ if (pollunlock(&lockstate) != 0) {
+ *reventsp = POLLNVAL;
+ return (0);
+ }
+ err = lx_ptm_eof_drop(dev, &rval);
+ pollrelock(lockstate);
+ if (err)
+ return (err);
+
+ /* If no EOF was dropped then return */
+ if (rval == 0)
+ return (0);
+
+ /*
+ * An EOF was removed from the stream. Retry the entire
+ * poll operation from the top because polls on the ptm
+ * device should behave differently now.
+ */
+ *loop = 1;
+ }
+ return (0);
+}
+
+static int
+lx_ptm_poll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp)
+{
+ int loop, err;
+
+ do {
+ /* Serialize ourself wrt read operations. */
+ if (lx_ptm_read_start(dev) != 0)
+ return (EINTR);
+
+ err = lx_ptm_poll_loop(dev,
+ events, anyyet, reventsp, phpp, &loop);
+ lx_ptm_read_end(dev);
+ if (err != 0)
+ return (err);
+ } while (loop != 0);
+ return (0);
+}
+
+static struct cb_ops lx_ptm_cb_ops = {
+ lx_ptm_open, /* open */
+ lx_ptm_close, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ lx_ptm_read, /* read */
+ lx_ptm_write, /* write */
+ lx_ptm_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ lx_ptm_poll, /* chpoll */
+ ddi_prop_op, /* prop_op */
+ NULL, /* cb_str */
+ D_NEW | D_MP,
+ CB_REV,
+ NULL,
+ NULL
+};
+
+static struct dev_ops lx_ptm_ops = {
+ DEVO_REV,
+ 0,
+ ddi_getinfo_1to1,
+ nulldev,
+ nulldev,
+ lx_ptm_attach,
+ lx_ptm_detach,
+ nodev,
+ &lx_ptm_cb_ops,
+ NULL,
+ NULL,
+ ddi_quiesce_not_needed, /* quiesce */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* type of module */
+ "Linux master terminal driver", /* description of module */
+ &lx_ptm_ops /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ &modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/usr/src/uts/common/brand/lx/io/lx_ptm.conf b/usr/src/uts/common/brand/lx/io/lx_ptm.conf
new file mode 100644
index 0000000000..481b4e3c74
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/io/lx_ptm.conf
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+name="lx_ptm" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/brand/lx/os/lx_acct.c b/usr/src/uts/common/brand/lx/os/lx_acct.c
new file mode 100644
index 0000000000..7f38a240ab
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_acct.c
@@ -0,0 +1,198 @@
+/*
+ * 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 2017 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/acct.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/cred.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/session.h>
+#include <sys/wait.h>
+#include <sys/ddi.h>
+#include <sys/zone.h>
+#include <sys/lx_types.h>
+
+/*
+ * Based on the Linux acct(5) man page, their comp_t definition is the same
+ * as ours. lxac_etime is encoded as a float for v3 accounting records.
+ */
+
+#define LX_ACCT_VERSION 3
+
+/*
+ * Bit flags in lxac_flag. The Linux AFORK and ASU match native. The rest of
+ * the flags diverge.
+ */
+#define LX_AFORK 0x01 /* executed fork, but no exec */
+#define LX_ASU 0x02 /* used superuser privileges */
+#define LX_ACORE 0x08 /* dumped core */
+#define LX_AXSIG 0x10 /* killed by a signal */
+
+typedef struct lx_acct {
+ char lxac_flag;
+ char lxac_version;
+ uint16_t lxac_tty;
+ uint32_t lxac_exitcode;
+ uint32_t lxac_uid;
+ uint32_t lxac_gid;
+ uint32_t lxac_pid;
+ uint32_t lxac_ppid;
+ uint32_t lxac_btime; /* seconds since the epoch */
+ uint32_t lxac_etime; /* float representation of ticks */
+ comp_t lxac_utime;
+ comp_t lxac_stime;
+ comp_t lxac_mem; /* kb */
+ comp_t lxac_io; /* unused */
+ comp_t lxac_rw; /* unused */
+ comp_t lxac_minflt;
+ comp_t lxac_majflt;
+ comp_t lxac_swaps; /* unused */
+ char lxac_comm[16];
+} lx_acct_t;
+
+/*
+ * Same functionality as acct_compress(). Produce a pseudo-floating point
+ * representation with 3 bits base-8 exponent, 13 bits fraction.
+ */
+static comp_t
+lx_acct_compt(ulong_t t)
+{
+ int exp = 0, round = 0;
+
+ while (t >= 8192) {
+ exp++;
+ round = t & 04;
+ t >>= 3;
+ }
+ if (round) {
+ t++;
+ if (t >= 8192) {
+ t >>= 3;
+ exp++;
+ }
+ }
+#ifdef _LP64
+ if (exp > 7) {
+ /* prevent wraparound */
+ t = 8191;
+ exp = 7;
+ }
+#endif
+ return ((exp << 13) + t);
+}
+
+/*
+ * 32-bit IEEE float encoding as-per Linux.
+ */
+static uint32_t
+lx_acct_float(int64_t t)
+{
+ uint32_t val, exp = 190;
+
+ if (t == 0)
+ return (0);
+
+ while (t > 0) {
+ t <<= 1;
+ exp--;
+ }
+ val = (uint32_t)(t >> 40) & 0x7fffffu;
+
+ return (val | (exp << 23));
+}
+
+/*
+ * Write a Linux-formatted record to the accounting file.
+ */
+void
+lx_acct_out(vnode_t *vp, int exit_status)
+{
+ struct proc *p;
+ user_t *ua;
+ struct cred *cr;
+ dev_t d;
+ pid_t pid, ppid;
+ struct vattr va;
+ ssize_t resid = 0;
+ int err;
+ lx_acct_t a;
+
+ p = curproc;
+ ua = PTOU(p);
+ cr = CRED();
+
+ bzero(&a, sizeof (a));
+
+ a.lxac_flag = ua->u_acflag & (LX_AFORK | LX_ASU);
+ a.lxac_version = LX_ACCT_VERSION;
+ d = cttydev(p);
+ a.lxac_tty = LX_MAKEDEVICE(getmajor(d), getminor(d));
+ if (WIFEXITED(exit_status)) {
+ a.lxac_exitcode = WEXITSTATUS(exit_status);
+ } else if (WIFSIGNALED(exit_status)) {
+ a.lxac_flag |= LX_AXSIG;
+ if (WCOREDUMP(exit_status)) {
+ a.lxac_flag |= LX_ACORE;
+ }
+ }
+ a.lxac_uid = crgetruid(cr);
+ a.lxac_gid = crgetrgid(cr);
+ pid = p->p_pid;
+ ppid = p->p_ppid;
+ /* Perform pid translation ala lxpr_fixpid(). */
+ if (pid == curzone->zone_proc_initpid) {
+ pid = 1;
+ ppid = 0;
+ } else {
+ if (ppid == curzone->zone_proc_initpid) {
+ ppid = 1;
+ } else if (ppid == curzone->zone_zsched->p_pid ||
+ (p->p_flag & SZONETOP) != 0) {
+ ppid = 1;
+ }
+ }
+ a.lxac_pid = pid;
+ a.lxac_ppid = ppid;
+ a.lxac_btime = ua->u_start.tv_sec;
+ /* For Linux v3 accounting record, this is an encoded float. */
+ a.lxac_etime = lx_acct_float(ddi_get_lbolt() - ua->u_ticks);
+ a.lxac_utime = lx_acct_compt(NSEC_TO_TICK(p->p_acct[LMS_USER]));
+ a.lxac_stime = lx_acct_compt(
+ NSEC_TO_TICK(p->p_acct[LMS_SYSTEM] + p->p_acct[LMS_TRAP]));
+ a.lxac_mem = lx_acct_compt((ulong_t)(ptob(ua->u_mem) / 1024));
+ /* a.lxac_io unused */
+ /* a.lxac_rw unused */
+ a.lxac_minflt = lx_acct_compt((ulong_t)p->p_ru.minflt);
+ a.lxac_majflt = lx_acct_compt((ulong_t)p->p_ru.majflt);
+ /* a.lxac_swaps unused */
+ bcopy(ua->u_comm, a.lxac_comm, sizeof (a.lxac_comm));
+
+ /*
+ * As with the native acct() handling, we save the size so that if the
+ * write fails, we can reset the size to avoid corrupting the accounting
+ * file.
+ */
+ va.va_mask = AT_SIZE;
+ if (VOP_GETATTR(vp, &va, 0, kcred, NULL) == 0) {
+ err = vn_rdwr(UIO_WRITE, vp, (caddr_t)&a, sizeof (a), 0LL,
+ UIO_SYSSPACE, FAPPEND, (rlim64_t)MAXOFF_T, kcred, &resid);
+ if (err != 0 || resid != 0)
+ (void) VOP_SETATTR(vp, &va, 0, kcred, NULL);
+ }
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_acl.c b/usr/src/uts/common/brand/lx/os/lx_acl.c
new file mode 100644
index 0000000000..184f05b6ed
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_acl.c
@@ -0,0 +1,213 @@
+/*
+ * 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) 2018, Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/errno.h>
+#include <sys/cred.h>
+#include <sys/sunddi.h>
+#include <sys/pathname.h>
+#include <sys/acl.h>
+#include <acl/acl_common.h>
+#include <sys/lx_acl.h>
+
+
+typedef struct {
+ uint16_t lpaxe_tag;
+ uint16_t lpaxe_perm;
+ uint32_t lpaxe_id;
+} lx_posix_acl_xattr_entry_t;
+
+typedef struct {
+ uint32_t lpaxh_version;
+ lx_posix_acl_xattr_entry_t lpaxh_entries[];
+} lx_posix_acl_xattr_header_t;
+
+#define LX_POSIX_ACL_XATTR_VERSION 0x0002
+
+/* e_tag entry in struct posix_acl_entry */
+#define LX_ACL_USER_OBJ 0x01 /* USER_OBJ */
+#define LX_ACL_USER 0x02 /* USER */
+#define LX_ACL_GROUP_OBJ 0x04 /* GROUP_OBJ */
+#define LX_ACL_GROUP 0x08 /* GROUP */
+#define LX_ACL_MASK 0x10 /* CLASS_OBJ */
+#define LX_ACL_OTHER 0x20 /* OTHER_OBJ */
+
+
+static int
+lx_acl_from_xattr(enum lx_acl_type atype, void *xattr, uint_t xlen,
+ acl_t **aclpp)
+{
+ lx_posix_acl_xattr_header_t *head = xattr;
+ lx_posix_acl_xattr_entry_t *entry;
+ int err = 0;
+ uint_t count, sz = xlen;
+ const uint_t mask = (atype == LX_ACL_DEFAULT) ? ACL_DEFAULT : 0;
+ acl_t *acl;
+ aclent_t *acle;
+
+ if (xattr == NULL) {
+ /* Handle zero-length set operations */
+ acl = acl_alloc(ACLENT_T);
+ *aclpp = acl;
+ return (0);
+ }
+
+ if (xlen < sizeof (*head)) {
+ return (EINVAL);
+ } else if (head->lpaxh_version != LX_POSIX_ACL_XATTR_VERSION) {
+ return (EOPNOTSUPP);
+ }
+
+ sz -= sizeof (lx_posix_acl_xattr_header_t);
+ if (sz % sizeof (lx_posix_acl_xattr_entry_t) != 0) {
+ return (EINVAL);
+ }
+ count = sz / sizeof (lx_posix_acl_xattr_entry_t);
+
+ acl = acl_alloc(ACLENT_T);
+ if (count == 0) {
+ *aclpp = acl;
+ return (0);
+ }
+
+ acle = kmem_alloc(count * sizeof (aclent_t), KM_SLEEP);
+ acl->acl_cnt = count;
+ acl->acl_aclp = acle;
+ entry = head->lpaxh_entries;
+ for (uint_t i = 0; i < count && err == 0; i++, entry++, acle++) {
+ switch (entry->lpaxe_tag) {
+ case LX_ACL_USER_OBJ:
+ case LX_ACL_GROUP_OBJ:
+ case LX_ACL_OTHER:
+ case LX_ACL_MASK:
+ break;
+ case LX_ACL_USER:
+ case LX_ACL_GROUP:
+ if (entry->lpaxe_id > MAXUID) {
+ err = EINVAL;
+ }
+ break;
+ default:
+ err = EINVAL;
+ break;
+ }
+ acle->a_id = entry->lpaxe_id | mask;
+ acle->a_type = entry->lpaxe_tag;
+ acle->a_perm = entry->lpaxe_perm;
+ }
+ if (err != 0) {
+ acl_free(acl);
+ return (err);
+ }
+
+ *aclpp = acl;
+ return (0);
+}
+
+/* ARGSUSED */
+int
+lx_acl_setxattr(vnode_t *vp, enum lx_acl_type atype, void *data, size_t len)
+{
+ const boolean_t is_dir = (vp->v_type == VDIR);
+ acl_t *acl = NULL;
+ cred_t *cr = CRED();
+ int err;
+
+ if (vp->v_type == VLNK) {
+ return (ENOTSUP);
+ } else if (atype == LX_ACL_DEFAULT && !is_dir) {
+ return (EACCES);
+ }
+
+ /*
+ * Copyin and verify the input, even through there is little to be done
+ * with the result.
+ */
+ if ((err = lx_acl_from_xattr(atype, data, len, &acl)) != 0) {
+ return (err);
+ }
+
+ /*
+ * Because systemd has decided to scope-creep its way into a position
+ * of moribund domination over all things system software, there exist
+ * work-arounds which are required to address its numerous bugs and
+ * shortcomings. One such case involves the FreeIPA installer needing
+ * to perform setfacl(3) on /run/systemd/ask-password.
+ *
+ * Between the fact that meaningful ACL translation can be challenging
+ * and that the path in question resides on tmpfs (which doesn't yet
+ * support ACLs at all on illumos), faked success is the only palatable
+ * course of action for now. Atonement will follow.
+ *
+ * See also: https://bugzilla.redhat.com/show_bug.cgi?id=1322167
+ */
+ err = ENOTSUP;
+ if (crgetuid(cr) == 0) {
+ char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ if (vnodetopath(NULL, vp, path, MAXPATHLEN, cr) == 0 &&
+ strncmp(path, "/run/systemd/", 13) == 0) {
+ /* Saccharin-sweet fake success */
+ err = 0;
+ }
+ kmem_free(path, MAXPATHLEN);
+ }
+ acl_free(acl);
+
+ return (err);
+}
+
+/* ARGSUSED */
+int
+lx_acl_getxattr(vnode_t *vp, enum lx_acl_type atype, void *data, size_t slen,
+ ssize_t *solen)
+{
+ const boolean_t is_dir = (vp->v_type == VDIR);
+ vsecattr_t vsattr;
+ int err;
+
+ if (vp->v_type == VLNK) {
+ return (ENOTSUP);
+ } else if (atype == LX_ACL_DEFAULT && !is_dir) {
+ return (ENODATA);
+ }
+
+ bzero(&vsattr, sizeof (vsattr));
+ vsattr.vsa_mask = VSA_ACECNT;
+ if ((err = VOP_GETSECATTR(vp, &vsattr, 0, CRED(), NULL)) != 0) {
+ err = (err == ENOENT) ? ENODATA : err;
+ return (err);
+ }
+
+ if (vsattr.vsa_aclentp != NULL)
+ kmem_free(vsattr.vsa_aclentp, vsattr.vsa_aclentsz);
+
+ return (ENODATA);
+}
+
+/* ARGSUSED */
+int
+lx_acl_removexattr(vnode_t *vp, enum lx_acl_type atype)
+{
+ return (ENODATA);
+}
+
+/* ARGSUSED */
+int
+lx_acl_listxattr(vnode_t *vp, uio_t *uio)
+{
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_audit.c b/usr/src/uts/common/brand/lx/os/lx_audit.c
new file mode 100644
index 0000000000..6e522e6d8d
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_audit.c
@@ -0,0 +1,1604 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+/*
+ * The Linux auditing system provides a fairly complex rule-based syntax
+ * for configuring what actions are to be audited. The user-level details
+ * are generally described in the Linux audit.rules(7), auditctl(8), and
+ * auditd(8) man pages. The user/kernel netlink API does not seem to be
+ * documented. The Linux kernel source and the user-level auditd source must
+ * be used to understand the interface we have to emulate. The relevant Linux
+ * source files are:
+ * include/uapi/linux/audit.h
+ * include/linux/audit.h
+ * kernel/audit.c
+ *
+ * The lx_netlink module implements the API used for getting or changing the
+ * audit configuration. For rule-oriented operations (list, append, delete),
+ * an lx_audit_rule_t structure (or sequence when listing) is passed in/out of
+ * the kernel. The netlink code calls into the lx_audit_append_rule or
+ * lx_audit_delete_rule functions here to perform the relevant operation.
+ * Within the lx_audit_rule_t structure, each member has the following
+ * meaning:
+ * lxar_flag: corresponds to user-level list (e.g. "exit" for syscall return)
+ * lxar_action: user-level action (e.g. "always")
+ * lxar_fld_cnt: number of fields specified in lxar_fields, lxar_values, and
+ * lxar_flg_flag arrays
+ * lxar_mask: syscall number bitmask the rule applies to (bit position in
+ * the array corresponds to the syscall number)
+ * laxr_fields: array of fields in the rule (i.e. each -F on user-level rule).
+ * A numeric code (e.g. LX_RF_AUDIT_ARCH) is assigned to each
+ * possible field.
+ * lxar_values: array of numeric field values (e.g. the internal b64 value on
+ * the -F AUDIT_ARCH=b64 rule)
+ * lxar_fld_flag: array of field operators (e.g. the '=' operator on the
+ * -F AUDIT_ARCH=b64 rule)
+ * lxar_buflen: length of the buffer data immediately following
+ * lxar_buf: A variable amount of additional field string data. Non-numeric
+ * field values are passed here. For example, the string associated
+ * with the '-F key=...' or -F path=...' rules. For string values,
+ * the corresponding lxar_values entry is the length of the string.
+ * The strings in lxar_buf are not C strings because they are not
+ * NULL terminated. The character data is pulled out of lxar_buf
+ * in chunks specified by the value and the pointer into the buf
+ * is advanced accordingly.
+ *
+ * There are two primary kinds of actions which we are currently interested in
+ * auditing;
+ * 1) system call return
+ * this corresponds to user-level "exit" rule actions
+ * 2) file system related actions
+ * this corresponds to user-level file system watch rules (-w)
+ *
+ * Only system call return is currently implemented, and only a very limited
+ * subset of all of the possible rule selection behavior.
+ *
+ * The Linux audit rule syntax defines that all selection criteria within a
+ * rule is ANDed together before an audit record is created. However, multiple
+ * rules can be defined for a specific syscall. For example, this user-level
+ * syntax defines two different rules for the "open" syscall:
+ * -a always,exit -F arch=b64 -S open -F auid>=1000 -F key=user-open
+ * -a always,exit -F arch=b64 -S open -F auid=0 -F key=priv-open
+ * The first rule would cause an audit record to be created when an "open"
+ * syscall returns and the syscall was performed by a process with a
+ * loginuid >= 1000. The key added to that audit record would be "user-open".
+ * The second rule would create an audit record if the loginuid was 0 and the
+ * record's key would be "priv-open".
+ *
+ * When auditing is enabled for a syscall return, we have to look at multiple
+ * rules and create an audit record for each rule that matches the selection
+ * criteria.
+ *
+ * Although the current implementation is limited, the overall structure is
+ * designed to be enhanced as more auditing support is added over time.
+ *
+ * By default, auditing is not enabled for a zone and no internal audit data
+ * exists. When the first netlink audit msg is received, the zone's audit state
+ * (lx_audit_state_t) is allocated (via lx_audit_init) and attached to the
+ * zone's lx brand-specific data (lxzd_audit_state). Once allocated, the audit
+ * data will persist until the zone halts.
+ *
+ * Audit records are enqueued onto the lxast_ev_queue and a worker thread
+ * (lx_audit_worker) is responsible for dequeueing the audit records and
+ * sending them up to the user-level auditd.
+ *
+ * Audit rules are stored in the lxast_rules list. This is an internal list
+ * consisting of elements of type lx_audit_rule_ent_t. Each element contains
+ * the input rule (lxare_rule) along with some additional data parsed out of
+ * the rule when it is appended (currently only the arch and key).
+ *
+ * When auditing is enabled for a syscall, the appropriate entry in the
+ * lxast_sys64_rulep (or lxast_sys32_rulep) array will point to the first
+ * rule that is applicable to the syscall. When that syscall returns, rule
+ * matching proceeds from that rule to the end of the rule list.
+ *
+ * New rules are always appended at the end of the list and Linux expects that
+ * rules are matched in order.
+ *
+ * If the rule list ever gets large enough that a linear search, anchored off
+ * the syscall pointer, becomes a performance bottleneck, then we'll have to
+ * explore alternate implementations. However, use of auditing is not that
+ * common to begin with, and most syscalls are typically not audited, so as
+ * long as the number of rules is in the order of tens, then the current
+ * implementation should be fine.
+ *
+ * When a rule is deleted, all associated syscall entries (lxast_sys64_rulep or
+ * lxast_sys32_rulep) are cleared, then the rule list is searched to see if
+ * there are any remaining rules which are applicable to the syscall(s). If so,
+ * pointers are reestablished in the relevant lxast_sys64_rulep (or 32) array.
+ */
+
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/ddi.h>
+#include <sys/zone.h>
+#include <sys/strsubr.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sunddi.h>
+#include <sys/strsun.h>
+#include <sys/tihdr.h>
+#include <sys/sockio.h>
+#include <sys/brand.h>
+#include <sys/debug.h>
+#include <sys/ucred.h>
+#include <sys/session.h>
+#include <sys/lx_types.h>
+#include <sys/lx_audit.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_socket.h>
+#include <sys/bitmap.h>
+#include <sockcommon.h>
+
+#define LX_AUDIT_FEATURE_VERSION 1
+
+/*
+ * Audit status mask values (lxas_mask in structure defined below)
+ * See Linux include/uapi/linux/audit.h
+ */
+#define LX_AUDIT_STATUS_ENABLED 0x001
+#define LX_AUDIT_STATUS_FAILURE 0x002
+#define LX_AUDIT_STATUS_PID 0x004
+#define LX_AUDIT_STATUS_RATE_LIMIT 0x008
+#define LX_AUDIT_STATUS_BACKLOG_LIMIT 0x010
+#define LX_AUDIT_STATUS_BACKLOG_WAIT_TIME 0x020
+#define LX_AUDIT_STATUS_LOST 0x040
+
+/*
+ * Audit features
+ * See Linux include/uapi/linux/audit.h
+ */
+#define LX_AUDIT_F_BACKLOG_LIMIT 0x001
+#define LX_AUDIT_F_BACKLOG_WAIT_TIME 0x002
+#define LX_AUDIT_F_EXECUTABLE_PATH 0x004
+#define LX_AUDIT_F_EXCLUDE_EXTEND 0x008
+#define LX_AUDIT_F_SESSIONID_FILTER 0x010
+#define LX_AUDIT_F_LOST_RESET 0x020
+#define LX_AUDIT_F_FILTER_FS 0x040
+
+#define LX_AUDIT_FEATURE_ALL (LX_AUDIT_F_BACKLOG_LIMIT | \
+ LX_AUDIT_F_BACKLOG_WAIT_TIME | LX_AUDIT_F_EXECUTABLE_PATH | \
+ LX_AUDIT_F_EXCLUDE_EXTEND | LX_AUDIT_F_SESSIONID_FILTER | \
+ LX_AUDIT_F_LOST_RESET | LX_AUDIT_F_FILTER_FS)
+
+
+/* Audit events */
+#define LX_AUDIT_SYSCALL 1300 /* syscall */
+#define LX_AUDIT_PATH 1302 /* file path */
+#define LX_AUDIT_CONFIG_CHANGE 1305 /* configuration change */
+#define LX_AUDIT_CWD 1307 /* current working directory */
+#define LX_AUDIT_EXECVE 1309 /* exec args */
+#define LX_AUDIT_EOE 1320 /* end of multi-record event */
+
+#define LX_AUDIT_BITMASK_SIZE 64
+#define LX_AUDIT_MAX_KEY_LEN 256
+
+/* Audit rule filter type */
+#define LX_AUDIT_FILTER_USER 0 /* user generated msgs */
+#define LX_AUDIT_FILTER_TASK 1 /* task creation */
+#define LX_AUDIT_FILTER_ENTRY 2 /* syscall entry - obsolete */
+#define LX_AUDIT_FILTER_WATCH 3 /* fs watch */
+#define LX_AUDIT_FILTER_EXIT 4 /* syscall return */
+#define LX_AUDIT_FILTER_TYPE 5 /* audit log start */
+#define LX_AUDIT_FILTER_FS 6 /* audit inode child */
+
+/* Audit rule action type */
+#define LX_AUDIT_ACT_NEVER 0
+#define LX_AUDIT_ACT_POSSIBLE 1
+#define LX_AUDIT_ACT_ALWAYS 2 /* the common case */
+
+#define LX_AUDIT_RULE_MAX_FIELDS 64
+
+/* Linux defaults */
+#define LX_AUDIT_DEF_BACKLOG_LIMIT 64
+#define LX_AUDIT_DEF_WAIT_TIME (60 * HZ_TO_LX_USERHZ(hz))
+
+/*
+ * Audit rule field types
+ * Linux defines a lot of Rule Field values in include/uapi/linux/audit.h.
+ * We currently only handle a few.
+ */
+#define LX_RF_AUDIT_LOGINUID 9 /* e.g. auid>=1000 */
+#define LX_RF_AUDIT_ARCH 11 /* e.g. -F arch=b64 */
+#define LX_RF_AUDIT_WATCH 105 /* user-level -w rule */
+#define LX_RF_AUDIT_PERM 106 /* user-level -p option */
+#define LX_RF_AUDIT_FILTERKEY 210 /* user-level -k key option */
+
+/*
+ * Audit rule field operators
+ * Linux defines the operator values in include/uapi/linux/audit.h.
+ * These 4 bits are combined in various ways for additional operators.
+ */
+#define LX_OF_AUDIT_BM 0x08000000 /* bit mask (&) */
+#define LX_OF_AUDIT_LT 0x10000000
+#define LX_OF_AUDIT_GT 0x20000000
+#define LX_OF_AUDIT_EQ 0x40000000
+#define LX_OF_AUDIT_NE (LX_OF_AUDIT_LT | LX_OF_AUDIT_GT)
+#define LX_OF_AUDIT_BT (LX_OF_AUDIT_BM | LX_OF_AUDIT_EQ) /* bit test (&=) */
+#define LX_OF_AUDIT_LE (LX_OF_AUDIT_LT | LX_OF_AUDIT_EQ)
+#define LX_OF_AUDIT_GE (LX_OF_AUDIT_GT | LX_OF_AUDIT_EQ)
+#define LX_OF_AUDIT_ALL (LX_OF_AUDIT_EQ | LX_OF_AUDIT_NE | LX_OF_AUDIT_BM)
+
+/*
+ * Audit rule arch specification
+ * See Linux EM_X86_64 and EM_386 defs.
+ * -F arch=b64 looks like: 0xc000003e
+ * -F arch=b32 looks like: 0x40000003
+ * If no arch is specified (possible with '-S syslog', '-S all', or '-w <file>')
+ * the rule applies to both architectures and LX_RF_AUDIT_ARCH is not passed.
+ */
+#define LX_AUDIT_ARCH64 0xc000003e
+#define LX_AUDIT_ARCH32 0x40000003
+
+/*
+ * See Linux include/uapi/linux/audit.h, AUDIT_MESSAGE_TEXT_MAX is 8560.
+ * The auditd src has MAX_AUDIT_MESSAGE_LENGTH as 8970.
+ * Until necessary, we'll limit ourselves to a smaller length.
+ */
+#define LX_AUDIT_MESSAGE_TEXT_MAX 1024
+
+typedef struct lx_audit_features {
+ uint32_t lxaf_version;
+ uint32_t lxaf_mask;
+ uint32_t lxaf_features;
+ uint32_t lxaf_lock;
+} lx_audit_features_t;
+
+typedef struct lx_audit_status {
+ uint32_t lxas_mask;
+ uint32_t lxas_enabled;
+ uint32_t lxas_failure;
+ uint32_t lxas_pid;
+ uint32_t lxas_rate_limit;
+ uint32_t lxas_backlog_limit;
+ uint32_t lxas_lost;
+ uint32_t lxas_backlog;
+ /* LINTED: E_ANONYMOUS_UNION_DECL */
+ union {
+ uint32_t lxas_version;
+ uint32_t lxas_feature_bitmap;
+ };
+ uint32_t lxas_backlog_wait_time;
+} lx_audit_status_t;
+
+typedef struct lx_audit_rule {
+ uint32_t lxar_flag;
+ uint32_t lxar_action;
+ uint32_t lxar_fld_cnt;
+ uint32_t lxar_mask[LX_AUDIT_BITMASK_SIZE];
+ uint32_t lxar_fields[LX_AUDIT_RULE_MAX_FIELDS];
+ uint32_t lxar_values[LX_AUDIT_RULE_MAX_FIELDS];
+ uint32_t lxar_fld_flag[LX_AUDIT_RULE_MAX_FIELDS];
+ uint32_t lxar_buflen;
+ /* LINTED: E_ZERO_OR_NEGATIVE_SUBSCRIPT */
+ char lxar_buf[0];
+} lx_audit_rule_t;
+
+/*
+ * Internal structure for an audit rule.
+ * Each rule is on the zone's top-level list of all rules (lxast_rules).
+ * This structure also holds the parsed character string fields from the
+ * original input rule (lxar_buf) so that we don't need to re-parse that
+ * data on every match.
+ */
+typedef struct lx_audit_rule_ent {
+ list_node_t lxare_link;
+ lx_audit_rule_t lxare_rule;
+ char *lxare_buf;
+ boolean_t lxare_is32bit;
+ boolean_t lxare_is64bit;
+ char *lxare_key;
+} lx_audit_rule_ent_t;
+
+typedef enum lx_audit_fail {
+ LXAE_SILENT,
+ LXAE_PRINT, /* default */
+ LXAE_PANIC /* reboot the zone */
+} lx_audit_fail_t;
+
+typedef struct lx_audit_record {
+ list_node_t lxar_link;
+ uint32_t lxar_type;
+ char *lxar_msg;
+} lx_audit_record_t;
+
+/*
+ * Per-zone audit state
+ * Lazy allocated when first needed.
+ *
+ * lxast_rate_limit
+ * Currently unused, but can be get/set. Linux default is 0.
+ * lxast_backlog_limit
+ * The maximum number of outstanding audit events allowed (the Linux kernel
+ * default is 64). If the limit is reached, lxast_failure determines what
+ * to do.
+ * lxast_backlog_wait_time
+ * Currently unused, but can be get/set. Linux default is 60HZ.
+ */
+typedef struct lx_audit_state {
+ lx_audit_fail_t lxast_failure; /* failure behavior */
+ uint32_t lxast_rate_limit;
+ uint32_t lxast_backlog_limit;
+ uint32_t lxast_backlog_wait_time;
+ lx_audit_rule_ent_t *lxast_sys32_rulep[LX_NSYSCALLS];
+ lx_audit_rule_ent_t *lxast_sys64_rulep[LX_NSYSCALLS];
+ kcondvar_t lxast_worker_cv;
+ kmutex_t lxast_lock; /* protects members below */
+ pid_t lxast_pid; /* auditd pid */
+ uint64_t lxast_seq; /* event sequence num */
+ uint32_t lxast_backlog; /* num of queued events */
+ uint32_t lxast_lost; /* num of lost events */
+ void *lxast_sock; /* auditd lx_netlink_sock_t */
+ boolean_t lxast_exit; /* taskq worker should quit */
+ boolean_t lxast_panicing; /* audit forcing reboot? */
+ kthread_t *lxast_worker;
+ list_t lxast_ev_queue; /* audit record queue */
+ list_t lxast_rules; /* the list of rules */
+} lx_audit_state_t;
+
+/*
+ * Function pointer to netlink function used by audit worker threads to send
+ * audit messages up to the user-level auditd.
+ */
+static int (*lx_audit_emit_msg)(void *, uint_t, const char *, uint_t);
+static kmutex_t lx_audit_em_lock; /* protects emit_msg above */
+
+/* From uts/common/brand/lx/syscall/lx_socket.c */
+extern long lx_socket(int, int, int);
+/* From uts/common/syscall/close.c */
+extern int close(int);
+
+static int
+lx_audit_emit_syscall_event(uint_t mtype, void *lxsock, const char *msg)
+{
+ int err;
+
+ err = lx_audit_emit_msg(lxsock, mtype, msg, LX_AUDIT_MESSAGE_TEXT_MAX);
+ if (err != 0)
+ return (err);
+ err = lx_audit_emit_msg(lxsock, 0, NULL, 0);
+ return (err);
+}
+
+/*
+ * Worker thread for audit record output up to user-level auditd.
+ */
+static void
+lx_audit_worker(void *a)
+{
+ lx_audit_state_t *asp = (lx_audit_state_t *)a;
+ lx_audit_record_t *rp;
+ int err;
+
+ VERIFY(asp != NULL);
+
+ mutex_enter(&asp->lxast_lock);
+
+ while (!asp->lxast_exit) {
+
+ if (asp->lxast_backlog == 0 || asp->lxast_sock == NULL ||
+ asp->lxast_pid == 0) {
+ cv_wait(&asp->lxast_worker_cv, &asp->lxast_lock);
+ continue;
+ }
+
+ rp = list_remove_head(&asp->lxast_ev_queue);
+ asp->lxast_backlog--;
+
+ err = lx_audit_emit_syscall_event(rp->lxar_type,
+ asp->lxast_sock, rp->lxar_msg);
+ if (err != ENOMEM && err != ENOSPC) {
+ kmem_free(rp->lxar_msg, LX_AUDIT_MESSAGE_TEXT_MAX);
+ kmem_free(rp, sizeof (lx_audit_record_t));
+ } else {
+ /*
+ * Put it back on the list, drop the mutex so that
+ * any other audit-related action could occur (such as
+ * socket deletion), then wait briefly before retry.
+ */
+ list_insert_head(&asp->lxast_ev_queue, rp);
+ asp->lxast_backlog++;
+ mutex_exit(&asp->lxast_lock);
+ /* wait 1/10th second and try again */
+ delay(drv_usectohz(100000));
+ mutex_enter(&asp->lxast_lock);
+ }
+ }
+
+ /* Leave state ready for new worker when auditing restarted */
+ asp->lxast_exit = B_FALSE;
+ mutex_exit(&asp->lxast_lock);
+
+ thread_exit();
+}
+
+static void
+lx_audit_set_worker(uint32_t pid, void *lxsock,
+ void (*cb)(void *, boolean_t))
+{
+ lx_audit_state_t *asp = ztolxzd(curzone)->lxzd_audit_state;
+
+ ASSERT(asp != NULL);
+ ASSERT(MUTEX_HELD(&asp->lxast_lock));
+
+ /* First, stop any existing worker thread */
+ while (asp->lxast_sock != NULL) {
+ mutex_exit(&asp->lxast_lock);
+ lx_audit_stop_worker(NULL, cb);
+ mutex_enter(&asp->lxast_lock);
+ /* unlikely we loop, but handle racing setters */
+ }
+
+ VERIFY(asp->lxast_pid == 0);
+ VERIFY(asp->lxast_sock == NULL);
+ VERIFY(asp->lxast_exit == B_FALSE);
+ VERIFY(asp->lxast_worker == NULL);
+ if (pid != 0) {
+ /* Start a worker with the new socket */
+ asp->lxast_sock = lxsock;
+ cb(asp->lxast_sock, B_TRUE);
+ asp->lxast_pid = pid;
+ asp->lxast_worker = thread_create(NULL, 0, lx_audit_worker,
+ asp, 0, curzone->zone_zsched, TS_RUN, minclsyspri);
+ }
+}
+
+static boolean_t
+lx_audit_match_val(uint32_t op, uint32_t ruleval, uint32_t curval)
+{
+ switch (op) {
+ case LX_OF_AUDIT_LT:
+ return (curval < ruleval);
+ case LX_OF_AUDIT_GT:
+ return (curval > ruleval);
+ case LX_OF_AUDIT_EQ:
+ return (curval == ruleval);
+ case LX_OF_AUDIT_NE:
+ return (curval != ruleval);
+ case LX_OF_AUDIT_LE:
+ return (curval <= ruleval);
+ case LX_OF_AUDIT_GE:
+ return (curval >= ruleval);
+ case LX_OF_AUDIT_BM: /* bit mask - any bit is set? */
+ return ((curval & ruleval) != 0);
+ case LX_OF_AUDIT_BT: /* bit test - all bits must be set */
+ return ((curval & ruleval) == ruleval);
+ default:
+ break;
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Per the Linux audit.rules(7) man page, a rule with an auid of -1 means the
+ * process does not have a loginuid. We'll use the absence of a session on the
+ * process to mimic this behavior.
+ */
+static uint32_t
+lx_audit_get_auid()
+{
+ sess_t *s;
+ uint32_t v;
+
+ /*
+ * A process with no session has:
+ * s_dev == 0xffffffffffffffff
+ * s_vp == NULL
+ * s_cred == NULL
+ */
+ s = curproc->p_sessp;
+ if (s != NULL && s->s_vp != NULL) {
+ v = crgetsuid(CRED());
+ } else {
+ v = UINT32_MAX; /* emulate auid of -1 */
+ }
+
+ return (v);
+}
+
+/*
+ * Determine if the rule matches.
+ * Currently, we're actually just checking LX_RF_AUDIT_LOGINUID (-F auid)
+ * fields, but as we add support for additional field matching, this function
+ * should be enhanced.
+ */
+static boolean_t
+lx_audit_syscall_rule_match(lx_audit_rule_ent_t *erp)
+{
+ uint32_t i, v;
+ lx_audit_rule_t *rp = &erp->lxare_rule;
+
+ for (i = 0; i < rp->lxar_fld_cnt; i++) {
+ uint32_t ftype, fval, fop;
+
+ ftype = rp->lxar_fields[i];
+ if (ftype != LX_RF_AUDIT_LOGINUID)
+ continue;
+
+ fop = rp->lxar_fld_flag[i];
+ fval = rp->lxar_values[i];
+ v = lx_audit_get_auid();
+
+ if (!lx_audit_match_val(fop, fval, v))
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+static int
+lx_audit_write(file_t *fp, const char *msg)
+{
+ int fflag;
+ ssize_t count;
+ size_t nwrite = 0;
+ struct uio auio;
+ struct iovec aiov;
+
+ count = strlen(msg);
+ fflag = fp->f_flag;
+
+ aiov.iov_base = (void *) msg;
+ aiov.iov_len = count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ return (lx_write_common(fp, &auio, &nwrite, B_FALSE));
+}
+
+/*
+ * We first try to send the msg out to the zone's logging service, then
+ * fallback to the zone's console, although in practice, that is unlikely to
+ * be useful to most users.
+ */
+static void
+lx_audit_log_msg(const char *msg)
+{
+ int fd;
+ struct sockaddr_un addr;
+ struct sonode *so;
+ uint_t alen;
+ uint_t sizediff = (sizeof (addr) - sizeof (addr.sun_path));
+ file_t *fp;
+ int err;
+ vnode_t *vp;
+
+ ttolwp(curthread)->lwp_errno = 0;
+ fd = lx_socket(LX_AF_UNIX, LX_SOCK_DGRAM, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ goto trycons;
+
+ bzero((char *)&addr, sizeof (addr));
+ addr.sun_family = AF_UNIX;
+ (void) strncpy(addr.sun_path, "/dev/log", sizeof (addr.sun_path) - 1);
+ alen = strlen(addr.sun_path) + 1 + sizediff;
+
+ /*
+ * We can't use lx_connect here since that expects to be called from
+ * user-land, so we do the (streamlined) connect ourselves.
+ */
+ if ((so = getsonode(fd, &err, &fp)) == NULL) {
+ (void) close(fd);
+ goto trycons;
+ }
+
+ err = socket_connect(so, (struct sockaddr *)&addr, alen, fp->f_flag,
+ _SOCONNECT_XPG4_2, CRED());
+
+ if (err == 0)
+ err = lx_audit_write(fp, msg);
+
+ releasef(fd); /* release getsonode hold */
+ (void) close(fd);
+
+ if (err == 0)
+ return;
+
+trycons:
+ /* "open" the console device */
+ if (lookupnameatcred("/dev/console", UIO_SYSSPACE, FOLLOW, NULLVPP,
+ &vp, NULL, CRED()) != 0)
+ return;
+
+ if (falloc(vp, FWRITE, &fp, &fd) != 0) {
+ VN_RELE(vp);
+ return;
+ }
+ mutex_exit(&fp->f_tlock);
+ setf(fd, fp);
+
+ /* nothing left to do if console write fails */
+ (void) lx_audit_write(fp, msg);
+ close(fd);
+}
+
+static void
+lx_audit_fail(lx_audit_state_t *asp, const char *msg)
+{
+ ASSERT(MUTEX_HELD(&asp->lxast_lock));
+
+ if (asp->lxast_failure == LXAE_PRINT ||
+ asp->lxast_failure == LXAE_PANIC) {
+ /*
+ * Linux can ratelimit the amount of log spam here, so we'll
+ * do something similar, especially since this could be called
+ * on many syscall returns if the audit daemon is down or
+ * not consuming audit records for some other reason.
+ */
+ if (asp->lxast_lost % 100 == 0)
+ lx_audit_log_msg(msg);
+ if (asp->lxast_failure == LXAE_PANIC &&
+ !asp->lxast_panicing) {
+ /*
+ * Reboot the zone so that no audit records are lost.
+ * We delay a second to give the zone's logger a chance
+ * to handle the log message. We have to drop the lock
+ * here in case the zone's logger itself is making
+ * syscalls which would be audited, although that
+ * wouldn't be the ideal configuration.
+ */
+ asp->lxast_panicing = B_TRUE;
+ mutex_exit(&asp->lxast_lock);
+ lx_audit_log_msg("audit: panic");
+ delay(drv_usectohz(1000000));
+ zone_kadmin(A_SHUTDOWN, AD_BOOT, NULL, kcred);
+ mutex_enter(&asp->lxast_lock);
+ }
+ }
+ asp->lxast_lost++;
+}
+
+/*
+ * This formats the input string into a format that matches Linux. The input
+ * strings are small right now (<= PSARGSZ) so for simpicity we're using
+ * a temporary buffer of adequate size.
+ */
+static void
+lx_audit_fmt_str(char *dst, char *str, uint_t dlen)
+{
+ char *sp, tmp[100];
+
+ (void) strlcpy(tmp, str, sizeof (tmp));
+ if ((sp = strchr(tmp, ' ')) != NULL)
+ *sp = '\0';
+
+ if ((sp = strchr(tmp, '"')) == NULL) {
+ (void) snprintf(dst, dlen, "\"%s\"", tmp);
+ } else {
+ char *p, *dp;
+ uint_t olen = 0;
+
+ ASSERT(dlen > 2);
+ dlen -= 2; /* leave room for terminating nul */
+ dp = dst;
+ for (p = str; *p != '\0' && olen < dlen; p++) {
+ (void) sprintf(dp, "%02x", *p);
+ dp += 2;
+ olen += 2;
+ }
+ *dp = '\0';
+ }
+}
+
+/*
+ * Format and enqueue a syscall audit record.
+ */
+static void
+lx_audit_syscall_fmt_rcd(int sysnum, uint32_t arch, long ret,
+ lx_audit_state_t *asp, lx_audit_rule_ent_t *erp, uint64_t seq,
+ timestruc_t *tsp)
+{
+ klwp_t *lwp;
+ proc_t *p;
+ uint32_t items, sessid;
+ lx_lwp_data_t *lwpd;
+ lx_audit_record_t *rp;
+ cred_t *cr = CRED();
+ minor_t minor;
+ char key[LX_AUDIT_MAX_KEY_LEN + 6]; /* for key="%s" formatting */
+ char exe[PSARGSZ * 2 + 8], comm[MAXCOMLEN * 2 + 8];
+
+ ASSERT(MUTEX_HELD(&asp->lxast_lock));
+
+ if (asp->lxast_backlog >= asp->lxast_backlog_limit) {
+ lx_audit_fail(asp, "audit: backlog limit exceeded");
+ return;
+ }
+
+ if (arch == LX_AUDIT_ARCH32) {
+ items = MIN(4, lx_sysent32[sysnum].sy_narg);
+ } else {
+ ASSERT3U(arch, ==, LX_AUDIT_ARCH64);
+ items = MIN(4, lx_sysent64[sysnum].sy_narg);
+ }
+
+ lwp = ttolwp(curthread);
+ lwpd = lwptolxlwp(lwp);
+ p = curproc;
+
+ /*
+ * For the key, if no key has been set on the rule, Linux formats the
+ * string "(null)" (with no quotes - i.e. key=(null)).
+ */
+ if (erp->lxare_key != NULL) {
+ (void) snprintf(key, sizeof (key), "key=\"%s\"",
+ erp->lxare_key);
+ } else {
+ (void) snprintf(key, sizeof (key), "key=(null)");
+ }
+
+ rp = kmem_alloc(sizeof (lx_audit_record_t), KM_NOSLEEP);
+ if (rp == NULL) {
+ lx_audit_fail(asp, "audit: no kernel memory");
+ return;
+ }
+ rp->lxar_msg = kmem_zalloc(LX_AUDIT_MESSAGE_TEXT_MAX, KM_NOSLEEP);
+ if (rp->lxar_msg == NULL) {
+ kmem_free(rp, sizeof (lx_audit_record_t));
+ lx_audit_fail(asp, "audit: no kernel memory");
+ return;
+ }
+ rp->lxar_type = LX_AUDIT_SYSCALL;
+
+ mutex_enter(&p->p_splock);
+ sessid = p->p_sessp->s_sid;
+ minor = getminor(p->p_sessp->s_dev);
+ mutex_exit(&p->p_splock);
+
+ mutex_enter(&p->p_lock);
+ lx_audit_fmt_str(exe, p->p_user.u_psargs, sizeof (exe));
+ lx_audit_fmt_str(comm, p->p_user.u_comm, sizeof (comm));
+ mutex_exit(&p->p_lock);
+
+ /*
+ * See Linux audit_log_exit() for how a syscall exit record is
+ * formatted.
+ *
+ * For "arch" value, see Linux AUDIT_ARCH_IA64, AUDIT_ARCH_I386,
+ * __AUDIT_ARCH_64BIT and __AUDIT_ARCH_LE definitions.
+ *
+ * For fsuid/fsgid, see lx_setfsuid/lx_setfsgid for how we handle that.
+ */
+ (void) snprintf(rp->lxar_msg, LX_AUDIT_MESSAGE_TEXT_MAX,
+ "audit(%lu.%03lu:%lu): arch=%x syscall=%u "
+ "success=%s exit=%ld a0=%lu a1=%lu a2=%lu a3=%lu items=%u "
+ "ppid=%u pid=%u auid=%u uid=%u gid=%u euid=%u suid=%u "
+ "fsuid=%u egid=%u sgid=%u fsgid=%u tty=pts%u ses=%u "
+ "comm=%s exe=%s %s",
+ (uint64_t)tsp->tv_sec, /* zone's timestamp */
+ (uint64_t)tsp->tv_nsec / 1000000,
+ seq, /* serial number */
+ arch, /* arch */
+ sysnum, /* syscall */
+ (lwp->lwp_errno == 0 ? "yes" : "no"), /* success */
+ ret, /* exit */
+ lwpd->br_syscall_args[0], /* a0 */
+ lwpd->br_syscall_args[1], /* a1 */
+ lwpd->br_syscall_args[2], /* a2 */
+ lwpd->br_syscall_args[3], /* a3 */
+ items, /* items */
+ lx_lwp_ppid(lwp, NULL, NULL), /* ppid */
+ (lwpd->br_pid == curzone->zone_proc_initpid ? 1 : lwpd->br_pid),
+ lx_audit_get_auid(), /* auid */
+ crgetruid(cr), /* uid */
+ crgetrgid(cr), /* gid */
+ crgetuid(cr), /* euid */
+ crgetsuid(cr), /* saved uid */
+ crgetuid(cr), /* fsuid */
+ crgetgid(cr), /* egid */
+ crgetsgid(cr), /* saved gid */
+ crgetgid(cr), /* fsgid */
+ minor, /* tty */
+ sessid, /* ses */
+ comm, /* comm */
+ exe, /* exe */
+ key); /* key="VAL" */
+
+ list_insert_tail(&asp->lxast_ev_queue, rp);
+ if (asp->lxast_backlog == 0)
+ cv_signal(&asp->lxast_worker_cv);
+ asp->lxast_backlog++;
+}
+
+/*
+ * Get the next rule in the list that is generally applicable to the given
+ * syscall.
+ */
+static lx_audit_rule_ent_t *
+lx_audit_next_applicable_rule(int sysnum, uint32_t arch, lx_audit_state_t *asp,
+ lx_audit_rule_ent_t *erp)
+{
+ ASSERT(MUTEX_HELD(&asp->lxast_lock));
+
+ for (erp = list_next(&asp->lxast_rules, erp);
+ erp != NULL;
+ erp = list_next(&asp->lxast_rules, erp)) {
+ lx_audit_rule_t *r = &erp->lxare_rule;
+
+ /* Determine if the rule in the list has the same ARCH. */
+ if (arch == LX_AUDIT_ARCH32 && !erp->lxare_is32bit)
+ continue;
+ if (arch == LX_AUDIT_ARCH64 && !erp->lxare_is64bit)
+ continue;
+
+ /* Determine if this rule applies to the relevant syscall. */
+ if (BT_TEST32(r->lxar_mask, sysnum))
+ return (erp);
+ }
+
+ return (NULL);
+}
+
+void
+lx_audit_syscall_exit(int sysnum, long ret)
+{
+ lx_zone_data_t *lxzd = ztolxzd(curzone);
+ lx_audit_state_t *asp;
+ uint64_t seq;
+ lx_audit_rule_ent_t *erp;
+ timestruc_t ts;
+ uint32_t arch;
+
+ if (lxzd->lxzd_audit_enabled == LXAE_DISABLED)
+ return;
+
+ if (sysnum >= LX_NSYSCALLS)
+ return;
+
+ asp = lxzd->lxzd_audit_state;
+ ASSERT(asp != NULL);
+
+ if (get_udatamodel() == DATAMODEL_ILP32) {
+ arch = LX_AUDIT_ARCH32;
+ } else {
+ ASSERT(get_udatamodel() == DATAMODEL_LP64);
+ arch = LX_AUDIT_ARCH64;
+ }
+
+ /*
+ * Fast top-level check to see if we're auditing this syscall.
+ * We don't take the mutex for this since there is no need.
+ */
+ if (arch == LX_AUDIT_ARCH32) {
+ if (asp->lxast_sys32_rulep[sysnum] == NULL)
+ return;
+ } else {
+ if (asp->lxast_sys64_rulep[sysnum] == NULL)
+ return;
+ }
+
+ mutex_enter(&asp->lxast_lock);
+ if (arch == LX_AUDIT_ARCH32) {
+ erp = asp->lxast_sys32_rulep[sysnum];
+ } else {
+ erp = asp->lxast_sys64_rulep[sysnum];
+ }
+
+ if (erp == NULL) {
+ /* Hit a race and the syscall is no longer being audited */
+ mutex_exit(&asp->lxast_lock);
+ return;
+ }
+
+ /*
+ * All of the records in the set (i.e. same serial number) have
+ * the same timestamp.
+ */
+ seq = asp->lxast_seq++;
+ gethrestime(&ts);
+ ts.tv_sec -= curzone->zone_boot_time;
+
+ /*
+ * We have to determine if the first rule associated with the syscall,
+ * or any subsequent applicable rules, match.
+ *
+ * The first rule associated with the syscall may (or may not) match,
+ * but there can be additional rules which might also match. The first
+ * possible rule is always the one that enables the syscall auditing,
+ * but we also have to iterate to the end of the list to see if any
+ * other rules are applicable to this syscall.
+ */
+ for (; erp != NULL;
+ erp = lx_audit_next_applicable_rule(sysnum, arch, asp, erp)) {
+ if (!lx_audit_syscall_rule_match(erp))
+ continue;
+
+ lx_audit_syscall_fmt_rcd(sysnum, arch, ret, asp, erp, seq, &ts);
+ }
+
+ /*
+ * TODO: Currently we only output a single SYSCALL record.
+ * Real Linux emits a set of audit records for a syscall exit event
+ * (e.g. for an unlink syscall):
+ * type=SYSCALL
+ * type=CWD
+ * type=PATH - one for the parent dir
+ * type=PATH - one for the actual file unlinked
+ * type=PROCTITLE - (this one seems worthless)
+ * followed by an AUDIT_EOE message (which seems to be ignored).
+ *
+ * For syscalls that don't change files in the file system (e.g. ioctl)
+ * there are no PATH records.
+ */
+ mutex_exit(&asp->lxast_lock);
+}
+
+/*
+ * Determine which syscalls this rule applies to and setup a fast pointer for
+ * the syscall to enable it's rule match.
+ *
+ * We have to look at each bit and translate the external syscall bits into the
+ * internal syscall number.
+ */
+static void
+lx_enable_syscall_rule(lx_audit_state_t *asp, lx_audit_rule_t *rulep,
+ lx_audit_rule_ent_t *rp)
+{
+ uint_t sysnum;
+
+ ASSERT(MUTEX_HELD(&asp->lxast_lock));
+
+ for (sysnum = 0; sysnum < LX_NSYSCALLS; sysnum++) {
+ if (BT_TEST32(rulep->lxar_mask, sysnum)) {
+ if (rp->lxare_is32bit) {
+ if (asp->lxast_sys32_rulep[sysnum] == NULL)
+ asp->lxast_sys32_rulep[sysnum] = rp;
+ }
+ if (rp->lxare_is64bit) {
+ if (asp->lxast_sys64_rulep[sysnum] == NULL)
+ asp->lxast_sys64_rulep[sysnum] = rp;
+ }
+ }
+ }
+}
+
+int
+lx_audit_append_rule(void *r, uint_t datalen)
+{
+ lx_audit_rule_t *rulep = (lx_audit_rule_t *)r;
+ char *datap;
+ uint_t i;
+ lx_audit_rule_ent_t *rp;
+ lx_audit_state_t *asp;
+ boolean_t is_32bit = B_TRUE, is_64bit = B_TRUE, sys_found = B_FALSE;
+ char *tdp;
+ char key[LX_AUDIT_MAX_KEY_LEN + 1];
+ uint32_t tlen;
+
+ if (ztolxzd(curproc->p_zone)->lxzd_audit_enabled == LXAE_LOCKED)
+ return (EPERM);
+
+ if (datalen < sizeof (lx_audit_rule_t))
+ return (EINVAL);
+ datalen -= sizeof (lx_audit_rule_t);
+
+ if (rulep->lxar_fld_cnt > LX_AUDIT_RULE_MAX_FIELDS)
+ return (EINVAL);
+
+ if (rulep->lxar_buflen > datalen)
+ return (EINVAL);
+
+ datap = rulep->lxar_buf;
+
+ /*
+ * First check the rule to determine if we support the flag, actions,
+ * and all of the fields specified (since currently, our rule support
+ * is incomplete).
+ *
+ * NOTE: We currently only handle syscall exit rules.
+ */
+ if (rulep->lxar_flag != LX_AUDIT_FILTER_EXIT ||
+ rulep->lxar_action != LX_AUDIT_ACT_ALWAYS)
+ return (ENOTSUP);
+ if (rulep->lxar_fld_cnt > LX_AUDIT_RULE_MAX_FIELDS)
+ return (EINVAL);
+ tdp = datap;
+ tlen = rulep->lxar_buflen;
+ key[0] = '\0';
+ for (i = 0; i < rulep->lxar_fld_cnt; i++) {
+ uint32_t ftype, fval, fop;
+
+ fop = rulep->lxar_fld_flag[i];
+ ftype = rulep->lxar_fields[i];
+ fval = rulep->lxar_values[i];
+ DTRACE_PROBE3(lx__audit__field, uint32_t, fop,
+ uint32_t, ftype, uint32_t, fval);
+
+ if (ftype == LX_RF_AUDIT_ARCH) {
+ if (fop != LX_OF_AUDIT_EQ)
+ return (ENOTSUP);
+ if (!is_32bit || !is_64bit)
+ return (EINVAL);
+ if (fval == LX_AUDIT_ARCH64) {
+ is_32bit = B_FALSE;
+ } else if (fval == LX_AUDIT_ARCH32) {
+ is_64bit = B_FALSE;
+ } else {
+ return (ENOTSUP);
+ }
+ } else if (ftype == LX_RF_AUDIT_LOGINUID) {
+ if ((fop & LX_OF_AUDIT_ALL) == 0)
+ return (ENOTSUP);
+ } else if (ftype == LX_RF_AUDIT_FILTERKEY) {
+ if (fop != LX_OF_AUDIT_EQ)
+ return (ENOTSUP);
+ if (tlen < fval || fval > LX_AUDIT_MAX_KEY_LEN)
+ return (EINVAL);
+ if (key[0] != '\0')
+ return (EINVAL);
+ /* while we're here, save the parsed key */
+ bcopy(tdp, key, fval);
+ key[fval] = '\0';
+ tdp += fval;
+ tlen -= fval;
+ } else {
+ /*
+ * TODO: expand the support for additional Linux field
+ * options.
+ */
+ return (ENOTSUP);
+ }
+ }
+ for (i = 0; i < LX_NSYSCALLS; i++) {
+ if (BT_TEST32(rulep->lxar_mask, i)) {
+ /* At least one syscall enabled in this mask entry */
+ sys_found = B_TRUE;
+ break;
+ }
+ }
+ if (!sys_found)
+ return (ENOTSUP);
+
+ asp = ztolxzd(curzone)->lxzd_audit_state;
+ ASSERT(asp != NULL);
+
+ /*
+ * We have confirmed that we can handle the rule specified.
+ * Before taking the lock, allocate and setup the internal rule struct.
+ */
+ rp = kmem_alloc(sizeof (lx_audit_rule_ent_t), KM_SLEEP);
+ bcopy(rulep, &rp->lxare_rule, sizeof (lx_audit_rule_t));
+ rp->lxare_buf = kmem_alloc(rulep->lxar_buflen, KM_SLEEP);
+ bcopy(datap, rp->lxare_buf, rulep->lxar_buflen);
+ rp->lxare_is32bit = is_32bit;
+ rp->lxare_is64bit = is_64bit;
+ if (key[0] == '\0') {
+ rp->lxare_key = NULL;
+ } else {
+ int slen = strlen(key);
+ rp->lxare_key = kmem_alloc(slen + 1, KM_SLEEP);
+ (void) strlcpy(rp->lxare_key, key, slen + 1);
+ }
+
+ mutex_enter(&asp->lxast_lock);
+ /* Save the rule on our top-level list. */
+ list_insert_tail(&asp->lxast_rules, rp);
+ /* Enable tracing on the relevant syscalls. */
+ lx_enable_syscall_rule(asp, rulep, rp);
+ mutex_exit(&asp->lxast_lock);
+
+ return (0);
+}
+
+int
+lx_audit_delete_rule(void *r, uint_t datalen)
+{
+ lx_audit_rule_t *rulep = (lx_audit_rule_t *)r;
+ char *datap;
+ uint_t sysnum;
+ lx_audit_state_t *asp;
+ lx_audit_rule_ent_t *erp;
+
+ if (ztolxzd(curproc->p_zone)->lxzd_audit_enabled == LXAE_LOCKED)
+ return (EPERM);
+
+ if (datalen < sizeof (lx_audit_rule_t))
+ return (EINVAL);
+ datalen -= sizeof (lx_audit_rule_t);
+
+ if (rulep->lxar_fld_cnt > LX_AUDIT_RULE_MAX_FIELDS)
+ return (EINVAL);
+
+ if (rulep->lxar_buflen > datalen)
+ return (EINVAL);
+
+ datap = rulep->lxar_buf;
+
+ asp = ztolxzd(curzone)->lxzd_audit_state;
+ ASSERT(asp != NULL);
+
+ mutex_enter(&asp->lxast_lock);
+
+ /* Find the matching rule from the rule list */
+ for (erp = list_head(&asp->lxast_rules);
+ erp != NULL;
+ erp = list_next(&asp->lxast_rules, erp)) {
+ lx_audit_rule_t *r;
+ uint_t i;
+ boolean_t mtch;
+
+ r = &erp->lxare_rule;
+ if (rulep->lxar_flag != r->lxar_flag)
+ continue;
+ if (rulep->lxar_action != r->lxar_action)
+ continue;
+ if (rulep->lxar_fld_cnt != r->lxar_fld_cnt)
+ continue;
+ for (i = 0, mtch = B_TRUE; i < LX_AUDIT_BITMASK_SIZE; i++) {
+ if (rulep->lxar_mask[i] != r->lxar_mask[i]) {
+ mtch = B_FALSE;
+ break;
+ }
+ }
+ if (!mtch)
+ continue;
+
+ for (i = 0, mtch = B_TRUE; i < rulep->lxar_fld_cnt; i++) {
+ if (rulep->lxar_fields[i] != r->lxar_fields[i] ||
+ rulep->lxar_values[i] != r->lxar_values[i] ||
+ rulep->lxar_fld_flag[i] != r->lxar_fld_flag[i]) {
+ mtch = B_FALSE;
+ break;
+ }
+ }
+ if (!mtch)
+ continue;
+ if (rulep->lxar_buflen != r->lxar_buflen)
+ continue;
+ if (bcmp(datap, erp->lxare_buf, r->lxar_buflen) == 0)
+ break;
+ }
+
+ /* There is no matching rule */
+ if (erp == NULL) {
+ mutex_exit(&asp->lxast_lock);
+ return (ENOENT);
+ }
+
+ /*
+ * Disable each relevant syscall enabling.
+ */
+ for (sysnum = 0; sysnum < LX_NSYSCALLS; sysnum++) {
+ if (BT_TEST32(rulep->lxar_mask, sysnum)) {
+ /*
+ * If this was the first rule on the list for the
+ * given syscall (likely, since usually only one rule
+ * per syscall) then either disable tracing for that
+ * syscall, or point to the next applicable rule in the
+ * list.
+ */
+ if (erp->lxare_is32bit) {
+ if (asp->lxast_sys32_rulep[sysnum] == erp) {
+ asp->lxast_sys32_rulep[sysnum] =
+ lx_audit_next_applicable_rule(
+ sysnum, LX_AUDIT_ARCH32, asp, erp);
+ }
+ }
+ if (erp->lxare_is64bit) {
+ if (asp->lxast_sys64_rulep[sysnum] == erp) {
+ asp->lxast_sys64_rulep[sysnum] =
+ lx_audit_next_applicable_rule(
+ sysnum, LX_AUDIT_ARCH64, asp, erp);
+ }
+ }
+ }
+ }
+
+ /* Remove the rule from the top-level list */
+ list_remove(&asp->lxast_rules, erp);
+
+ kmem_free(erp->lxare_buf, erp->lxare_rule.lxar_buflen);
+ if (erp->lxare_key != NULL)
+ kmem_free(erp->lxare_key, strlen(erp->lxare_key) + 1);
+ kmem_free(erp, sizeof (lx_audit_rule_ent_t));
+
+ mutex_exit(&asp->lxast_lock);
+ return (0);
+}
+
+void
+lx_audit_emit_user_msg(uint_t mtype, uint_t len, char *datap)
+{
+ lx_zone_data_t *lxzd = ztolxzd(curzone);
+ lx_audit_state_t *asp;
+ lx_audit_record_t *rp;
+ timestruc_t ts;
+ uint_t sessid;
+ proc_t *p = curproc;
+ lx_lwp_data_t *lwpd = lwptolxlwp(ttolwp(curthread));
+ uint_t prelen, alen;
+ char msg[LX_AUDIT_MESSAGE_TEXT_MAX];
+
+ /*
+ * For user messages, auditing may not actually be initialized. If not,
+ * just return.
+ */
+ if (lxzd->lxzd_audit_enabled == LXAE_DISABLED ||
+ lxzd->lxzd_audit_state == NULL)
+ return;
+
+ if (len >= sizeof (msg))
+ len = sizeof (msg) - 1;
+
+ mutex_enter(&p->p_splock);
+ sessid = p->p_sessp->s_sid;
+ mutex_exit(&p->p_splock);
+
+ asp = lxzd->lxzd_audit_state;
+ ASSERT(asp != NULL);
+
+ mutex_enter(&asp->lxast_lock);
+
+ if (asp->lxast_backlog >= asp->lxast_backlog_limit) {
+ lx_audit_fail(asp, "audit: backlog limit exceeded");
+ mutex_exit(&asp->lxast_lock);
+ return;
+ }
+
+ rp = kmem_alloc(sizeof (lx_audit_record_t), KM_NOSLEEP);
+ if (rp == NULL) {
+ lx_audit_fail(asp, "audit: no kernel memory");
+ mutex_exit(&asp->lxast_lock);
+ return;
+ }
+ rp->lxar_msg = kmem_zalloc(LX_AUDIT_MESSAGE_TEXT_MAX, KM_NOSLEEP);
+ if (rp->lxar_msg == NULL) {
+ lx_audit_fail(asp, "audit: no kernel memory");
+ mutex_exit(&asp->lxast_lock);
+ kmem_free(rp, sizeof (lx_audit_record_t));
+ return;
+ }
+ rp->lxar_type = mtype;
+ bcopy(datap, msg, len);
+ msg[len] = '\0';
+
+ gethrestime(&ts);
+ ts.tv_sec -= curzone->zone_boot_time;
+
+ (void) snprintf(rp->lxar_msg, LX_AUDIT_MESSAGE_TEXT_MAX,
+ "audit(%lu.%03lu:%lu): pid=%u uid=%u auid=%u ses=%u msg=\'",
+ (uint64_t)ts.tv_sec, /* zone's timestamp */
+ (uint64_t)ts.tv_nsec / 1000000,
+ asp->lxast_seq++, /* serial number */
+ (lwpd->br_pid == curzone->zone_proc_initpid ? 1 : lwpd->br_pid),
+ crgetruid(CRED()), /* uid */
+ lx_audit_get_auid(), /* auid */
+ sessid); /* ses */
+
+ prelen = strlen(rp->lxar_msg);
+ alen = LX_AUDIT_MESSAGE_TEXT_MAX - prelen - 2;
+ (void) strlcat(rp->lxar_msg + prelen, msg, alen);
+ (void) strlcat(rp->lxar_msg, "\'", LX_AUDIT_MESSAGE_TEXT_MAX);
+
+ list_insert_tail(&asp->lxast_ev_queue, rp);
+ if (asp->lxast_backlog == 0)
+ cv_signal(&asp->lxast_worker_cv);
+ asp->lxast_backlog++;
+ mutex_exit(&asp->lxast_lock);
+}
+
+void
+lx_audit_list_rules(void *reply,
+ void (*cb)(void *, void *, uint_t, void *, uint_t))
+{
+ lx_audit_state_t *asp;
+ lx_audit_rule_ent_t *rp;
+
+ asp = ztolxzd(curzone)->lxzd_audit_state;
+ ASSERT(asp != NULL);
+
+ /*
+ * Output the rule list
+ */
+ mutex_enter(&asp->lxast_lock);
+ for (rp = list_head(&asp->lxast_rules); rp != NULL;
+ rp = list_next(&asp->lxast_rules, rp)) {
+ cb(reply, &rp->lxare_rule, sizeof (lx_audit_rule_t),
+ rp->lxare_buf, rp->lxare_rule.lxar_buflen);
+ }
+ mutex_exit(&asp->lxast_lock);
+}
+
+void
+lx_audit_get_feature(void *reply, void (*cb)(void *, void *, uint_t))
+{
+ lx_audit_features_t af;
+
+ af.lxaf_version = LX_AUDIT_FEATURE_VERSION;
+ af.lxaf_mask = 0xffffffff;
+ af.lxaf_features = 0;
+ af.lxaf_lock = 0;
+
+ cb(reply, &af, sizeof (af));
+}
+
+void
+lx_audit_get(void *reply, void (*cb)(void *, void *, uint_t))
+{
+ lx_audit_status_t status;
+ lx_zone_data_t *lxzd;
+ lx_audit_state_t *asp;
+
+ lxzd = ztolxzd(curproc->p_zone);
+ asp = lxzd->lxzd_audit_state;
+ ASSERT(asp != NULL);
+
+ bzero(&status, sizeof (status));
+
+ mutex_enter(&asp->lxast_lock);
+ status.lxas_enabled = lxzd->lxzd_audit_enabled;
+ status.lxas_failure = asp->lxast_failure;
+ status.lxas_pid = asp->lxast_pid;
+ status.lxas_rate_limit = asp->lxast_rate_limit;
+ status.lxas_backlog_limit = asp->lxast_backlog_limit;
+ status.lxas_lost = asp->lxast_lost;
+ status.lxas_backlog = asp->lxast_backlog;
+ status.lxas_backlog_wait_time = asp->lxast_backlog_wait_time;
+ status.lxas_feature_bitmap = LX_AUDIT_FEATURE_ALL;
+ mutex_exit(&asp->lxast_lock);
+
+ cb(reply, &status, sizeof (status));
+}
+
+int
+lx_audit_set(void *lxsock, void *s, uint_t datalen,
+ void (*cb)(void *, boolean_t))
+{
+ lx_audit_status_t *statusp = (lx_audit_status_t *)s;
+ lx_zone_data_t *lxzd;
+ lx_audit_state_t *asp;
+
+ /*
+ * Unfortunately, some user-level code does not send down a full
+ * lx_audit_status_t structure in the message (e.g. this occurs on
+ * CentOS7). Only the structure up to, but not including, the embedded
+ * union is being sent in. This appears to be a result of the user-level
+ * code being built for older versions of the kernel. To handle this,
+ * we have to subtract the last 8 bytes from the size in order to
+ * accomodate this code. We'll revalidate with the full size if
+ * LX_AUDIT_STATUS_BACKLOG_WAIT_TIME were to be set in the mask.
+ */
+ if (datalen < sizeof (lx_audit_status_t) - 8)
+ return (EINVAL);
+
+ lxzd = ztolxzd(curproc->p_zone);
+ asp = lxzd->lxzd_audit_state;
+ ASSERT(asp != NULL);
+
+ /* Once the config is locked, we only allow changing the auditd pid */
+ mutex_enter(&asp->lxast_lock);
+ if (lxzd->lxzd_audit_enabled == LXAE_LOCKED &&
+ (statusp->lxas_mask & ~LX_AUDIT_STATUS_PID)) {
+ mutex_exit(&asp->lxast_lock);
+ return (EPERM);
+ }
+
+ if (statusp->lxas_mask & LX_AUDIT_STATUS_FAILURE) {
+ switch (statusp->lxas_failure) {
+ case LXAE_SILENT:
+ case LXAE_PRINT:
+ case LXAE_PANIC:
+ asp->lxast_failure = statusp->lxas_failure;
+ break;
+ default:
+ mutex_exit(&asp->lxast_lock);
+ return (EINVAL);
+ }
+ }
+ if (statusp->lxas_mask & LX_AUDIT_STATUS_PID) {
+ /*
+ * The process that sets the pid is the daemon, so this is the
+ * socket we'll write audit records out to.
+ */
+ lx_audit_set_worker(statusp->lxas_pid, lxsock, cb);
+ }
+ if (statusp->lxas_mask & LX_AUDIT_STATUS_RATE_LIMIT) {
+ asp->lxast_rate_limit = statusp->lxas_rate_limit;
+ }
+ if (statusp->lxas_mask & LX_AUDIT_STATUS_BACKLOG_LIMIT) {
+ asp->lxast_backlog_limit = statusp->lxas_backlog_limit;
+ }
+ if (statusp->lxas_mask & LX_AUDIT_STATUS_BACKLOG_WAIT_TIME) {
+ /*
+ * See the comment above. We have to revalidate the full struct
+ * size since we previously only validated for a shorter struct.
+ */
+ if (datalen < sizeof (lx_audit_status_t)) {
+ mutex_exit(&asp->lxast_lock);
+ return (EINVAL);
+ }
+ asp->lxast_backlog_wait_time = statusp->lxas_backlog_wait_time;
+ }
+ if (statusp->lxas_mask & LX_AUDIT_STATUS_LOST) {
+ asp->lxast_lost = statusp->lxas_lost;
+ }
+
+ if (statusp->lxas_mask & LX_AUDIT_STATUS_ENABLED) {
+ switch (statusp->lxas_enabled) {
+ case 0:
+ lxzd->lxzd_audit_enabled = LXAE_DISABLED;
+ break;
+ case 1:
+ lxzd->lxzd_audit_enabled = LXAE_ENABLED;
+ break;
+ case 2:
+ lxzd->lxzd_audit_enabled = LXAE_LOCKED;
+ break;
+ default:
+ mutex_exit(&asp->lxast_lock);
+ return (EINVAL);
+ }
+ }
+ mutex_exit(&asp->lxast_lock);
+
+ return (0);
+}
+
+void
+lx_audit_stop_worker(void *s, void (*cb)(void *, boolean_t))
+{
+ lx_audit_state_t *asp = ztolxzd(curzone)->lxzd_audit_state;
+ kt_did_t tid = 0;
+
+ ASSERT(asp != NULL);
+ mutex_enter(&asp->lxast_lock);
+ if (s == NULL) {
+ s = asp->lxast_sock;
+ } else {
+ VERIFY(s == asp->lxast_sock);
+ }
+ asp->lxast_sock = NULL;
+ asp->lxast_pid = 0;
+ if (asp->lxast_worker != NULL) {
+ tid = asp->lxast_worker->t_did;
+ asp->lxast_worker = NULL;
+ asp->lxast_exit = B_TRUE;
+ cv_signal(&asp->lxast_worker_cv);
+ }
+ if (s != NULL)
+ cb(s, B_FALSE);
+ mutex_exit(&asp->lxast_lock);
+
+ if (tid != 0)
+ thread_join(tid);
+}
+
+/*
+ * Called when audit netlink message received, in order to perform lazy
+ * allocation of audit state for the zone. We also perform the one-time step to
+ * cache the netlink callback used by the audit worker thread to send messages
+ * up to the auditd.
+ */
+void
+lx_audit_init(int (*cb)(void *, uint_t, const char *, uint_t))
+{
+ lx_zone_data_t *lxzd = ztolxzd(curzone);
+ lx_audit_state_t *asp;
+
+ mutex_enter(&lxzd->lxzd_lock);
+
+ if (lxzd->lxzd_audit_state != NULL) {
+ mutex_exit(&lxzd->lxzd_lock);
+ return;
+ }
+
+ asp = kmem_zalloc(sizeof (lx_audit_state_t), KM_SLEEP);
+
+ mutex_init(&asp->lxast_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&asp->lxast_worker_cv, NULL, CV_DEFAULT, NULL);
+ list_create(&asp->lxast_ev_queue, sizeof (lx_audit_record_t),
+ offsetof(lx_audit_record_t, lxar_link));
+ list_create(&asp->lxast_rules, sizeof (lx_audit_rule_ent_t),
+ offsetof(lx_audit_rule_ent_t, lxare_link));
+ asp->lxast_failure = LXAE_PRINT;
+ asp->lxast_backlog_limit = LX_AUDIT_DEF_BACKLOG_LIMIT;
+ asp->lxast_backlog_wait_time = LX_AUDIT_DEF_WAIT_TIME;
+
+ lxzd->lxzd_audit_state = asp;
+
+ mutex_exit(&lxzd->lxzd_lock);
+
+ mutex_enter(&lx_audit_em_lock);
+ if (lx_audit_emit_msg == NULL)
+ lx_audit_emit_msg = cb;
+ mutex_exit(&lx_audit_em_lock);
+}
+
+/*
+ * Called when netlink module is unloading so that we can clear the cached
+ * netlink callback used by the audit worker thread to send messages up to the
+ * auditd.
+ */
+void
+lx_audit_cleanup(void)
+{
+ mutex_enter(&lx_audit_em_lock);
+ lx_audit_emit_msg = NULL;
+ mutex_exit(&lx_audit_em_lock);
+}
+
+/*
+ * Called when the zone is being destroyed, not when auditing is being disabled.
+ * Note that zsched has already exited and any lxast_worker thread has exited.
+ */
+void
+lx_audit_fini(zone_t *zone)
+{
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+ lx_audit_state_t *asp;
+ lx_audit_record_t *rp;
+ lx_audit_rule_ent_t *erp;
+
+ ASSERT(MUTEX_HELD(&lxzd->lxzd_lock));
+
+ if ((asp = lxzd->lxzd_audit_state) == NULL)
+ return;
+
+ mutex_enter(&asp->lxast_lock);
+
+ VERIFY(asp->lxast_worker == NULL);
+
+ rp = list_remove_head(&asp->lxast_ev_queue);
+ while (rp != NULL) {
+ kmem_free(rp->lxar_msg, LX_AUDIT_MESSAGE_TEXT_MAX);
+ kmem_free(rp, sizeof (lx_audit_record_t));
+ rp = list_remove_head(&asp->lxast_ev_queue);
+ }
+
+ list_destroy(&asp->lxast_ev_queue);
+ asp->lxast_backlog = 0;
+ asp->lxast_pid = 0;
+
+ erp = list_remove_head(&asp->lxast_rules);
+ while (erp != NULL) {
+ kmem_free(erp->lxare_buf, erp->lxare_rule.lxar_buflen);
+ if (erp->lxare_key != NULL)
+ kmem_free(erp->lxare_key, strlen(erp->lxare_key) + 1);
+ kmem_free(erp, sizeof (lx_audit_rule_ent_t));
+ erp = list_remove_head(&asp->lxast_rules);
+ }
+ list_destroy(&asp->lxast_rules);
+
+ mutex_exit(&asp->lxast_lock);
+
+ cv_destroy(&asp->lxast_worker_cv);
+ mutex_destroy(&asp->lxast_lock);
+ lxzd->lxzd_audit_state = NULL;
+ kmem_free(asp, sizeof (lx_audit_state_t));
+}
+
+/*
+ * Audit initialization/cleanup when lx brand module is loaded and
+ * unloaded.
+ */
+void
+lx_audit_ld()
+{
+ mutex_init(&lx_audit_em_lock, NULL, MUTEX_DEFAULT, NULL);
+}
+
+void
+lx_audit_unld()
+{
+ mutex_destroy(&lx_audit_em_lock);
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_brand.c b/usr/src/uts/common/brand/lx/os/lx_brand.c
new file mode 100644
index 0000000000..31bb86cce1
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_brand.c
@@ -0,0 +1,2728 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2020 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+/*
+ * The LX Brand: emulation of a Linux operating environment within a zone.
+ *
+ * OVERVIEW
+ *
+ * The LX brand enables a full Linux userland -- including a C library,
+ * init(1) framework, and some set of applications -- to run unmodified
+ * within an illumos zone. Unlike illumos, where applications are expected
+ * to link against and consume functions exported from libraries, the
+ * supported Linux binary compatibility boundary is the system call
+ * interface. By accurately emulating the behaviour of Linux system calls,
+ * Linux software can be executed in this environment as if it were running
+ * on a native Linux system.
+ *
+ * EMULATING LINUX SYSTEM CALLS
+ *
+ * Linux system calls are made in 32-bit processes via the "int 0x80"
+ * instruction; in 64-bit processes the "syscall" instruction is used, as it
+ * is with native illumos processes. In both cases, arguments to system
+ * calls are generally passed in registers and the usermode stack is not
+ * interpreted or modified by the Linux kernel.
+ *
+ * When the emulated Linux process makes a system call, it traps into the
+ * illumos kernel. The in-kernel brand module contains various emulation
+ * routines, and can fully service some emulated system calls; e.g. read(2)
+ * and write(2). Other system calls require assistance from the illumos
+ * libc, bouncing back out to the brand library ("lx_brand.so.1") for
+ * emulation.
+ *
+ * The brand mechanism allows for the provision of an alternative trap
+ * handler for the various system call mechanisms. Traditionally this was
+ * used to immediately revector execution to the usermode emulation library,
+ * which was responsible for handling all system calls. In the interests of
+ * more accurate emulation and increased performance, much of the regular
+ * illumos system call path is now invoked. Only the argument processing and
+ * handler dispatch are replaced by the brand, via the per-LWP
+ * "lwp_brand_syscall" interposition function pointer.
+ *
+ * THE NATIVE AND BRAND STACKS
+ *
+ * Some runtime environments (e.g. the Go language) allocate very small
+ * thread stacks, preferring to grow or split the stack as necessary. The
+ * Linux kernel generally does not use the usermode stack when servicing
+ * system calls, so this is not a problem. In order for our emulation to
+ * have the same zero stack impact, we must execute usermode emulation
+ * routines on an _alternate_ stack. This is similar, in principle, to the
+ * use of sigaltstack(3C) to run signal handlers off the main thread stack.
+ *
+ * To this end, the brand library allocates and installs an alternate stack
+ * (called the "native" stack) for each LWP. The in-kernel brand code uses
+ * this stack for usermode emulation calls and interposed signal delivery,
+ * while the emulated Linux process sees only the data on the main thread
+ * stack, known as the "brand" stack. The stack mode is tracked in the
+ * per-LWP brand-private data, using the LX_STACK_MODE_* enum.
+ *
+ * The stack mode doubles as a system call "mode bit". When in the
+ * LX_STACK_MODE_BRAND mode, system calls are processed as emulated Linux
+ * system calls. In other modes, system calls are assumed to be native
+ * illumos system calls as made during brand library initialisation and
+ * usermode emulation.
+ *
+ * USERMODE EMULATION
+ *
+ * When a Linux system call cannot be emulated within the kernel, we preserve
+ * the register state of the Linux process and revector the LWP to the brand
+ * library usermode emulation handler: the "lx_emulate()" function in
+ * "lx_brand.so.1". This revectoring is modelled on the delivery of signals,
+ * and is performed in "lx_emulate_user()".
+ *
+ * First, the emulated process state is written out to the usermode stack of
+ * the process as a "ucontext_t" object. Arguments to the emulation routine
+ * are passed on the stack or in registers, depending on the ABI. When the
+ * usermode emulation is complete, the result is passed back to the kernel
+ * (via the "B_EMULATION_DONE" brandsys subcommand) with the saved context
+ * for restoration.
+ *
+ * SIGNAL DELIVERY, SETCONTEXT AND GETCONTEXT
+ *
+ * When servicing emulated system calls in the usermode brand library, or
+ * during signal delivery, various state is preserved by the kernel so that
+ * the running LWP may be revectored to a handling routine. The context
+ * allows the kernel to restart the program at the point of interruption,
+ * either at the return of the signal handler, via setcontext(3C); or after
+ * the usermode emulation request has been serviced, via B_EMULATION_DONE.
+ *
+ * In illumos native processes, the saved context (a "ucontext_t" object)
+ * includes the state of registers and the current signal mask at the point
+ * of interruption. The context also includes a link to the most recently
+ * saved context, forming a chain to be unwound as requests complete. The LX
+ * brand requires additional book-keeping to describe the machine state: in
+ * particular, the current stack mode and the occupied extent of the native
+ * stack.
+ *
+ * The brand code is able to interpose on the context save and restore
+ * operations in the kernel -- see "lx_savecontext()" and
+ * "lx_restorecontext()" -- to enable getcontext(3C) and setcontext(3C) to
+ * function correctly in the face of a dual stack LWP. The brand also
+ * interposes on the signal delivery mechanism -- see "lx_sendsig()" and
+ * "lx_sendsig_stack()" -- to allow all signals to be delivered to the brand
+ * library interposer on the native stack, regardless of the interrupted
+ * execution mode. Linux sigaltstack(2) emulation is performed entirely by
+ * the usermode brand library during signal handler interposition.
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/errno.h>
+#include <sys/thread.h>
+#include <sys/systm.h>
+#include <sys/syscall.h>
+#include <sys/proc.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+#include <sys/model.h>
+#include <sys/exec.h>
+#include <sys/lx_impl.h>
+#include <sys/machbrand.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_futex.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/lx_userhz.h>
+#include <sys/param.h>
+#include <sys/termios.h>
+#include <sys/sunddi.h>
+#include <sys/ddi.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+#include <sys/auxv.h>
+#include <sys/priv.h>
+#include <sys/regset.h>
+#include <sys/privregs.h>
+#include <sys/archsystm.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/sdt.h>
+#include <sys/x86_archext.h>
+#include <sys/controlregs.h>
+#include <sys/core.h>
+#include <sys/stack.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <lx_signum.h>
+#include <util/sscanf.h>
+#include <sys/lx_brand.h>
+#include <sys/zfs_ioctl.h>
+#include <inet/tcp_impl.h>
+#include <inet/udp_impl.h>
+
+int lx_debug = 0;
+uint_t lx_hz_scale = 0;
+
+void lx_init_brand_data(zone_t *, kmutex_t *);
+void lx_free_brand_data(zone_t *);
+void lx_setbrand(proc_t *);
+int lx_getattr(zone_t *, int, void *, size_t *);
+int lx_setattr(zone_t *, int, void *, size_t);
+int lx_brandsys(int, int64_t *, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+void lx_set_kern_version(zone_t *, char *);
+void lx_copy_procdata(proc_t *, proc_t *);
+
+extern int getsetcontext(int, void *);
+extern int waitsys(idtype_t, id_t, siginfo_t *, int);
+#if defined(_SYSCALL32_IMPL)
+extern int getsetcontext32(int, void *);
+extern int waitsys32(idtype_t, id_t, siginfo_t *, int);
+#endif
+
+extern int zvol_name2minor(const char *, minor_t *);
+extern int zvol_create_minor(const char *);
+
+extern void lx_proc_exit(proc_t *);
+extern int lx_sched_affinity(int, uintptr_t, int, uintptr_t, int64_t *);
+
+extern void lx_io_clear(lx_proc_data_t *);
+extern void lx_io_cleanup(proc_t *);
+
+extern void lx_ioctl_init();
+extern void lx_ioctl_fini();
+extern void lx_socket_init();
+extern void lx_socket_fini();
+
+extern int lx_start_nfs_lockd();
+extern void lx_upcall_statd();
+
+lx_systrace_f *lx_systrace_entry_ptr;
+lx_systrace_f *lx_systrace_return_ptr;
+
+static int lx_systrace_enabled;
+
+/*
+ * cgroup file system maintenance functions which are set when cgroups loads.
+ */
+void (*lx_cgrp_initlwp)(vfs_t *, uint_t, id_t, pid_t);
+void (*lx_cgrp_freelwp)(vfs_t *, uint_t, id_t, pid_t);
+
+/*
+ * While this is effectively mmu.hole_start - PAGESIZE, we don't particularly
+ * want an MMU dependency here (and should there be a microprocessor without
+ * a hole, we don't want to start allocating from the top of the VA range).
+ */
+#define LX_MAXSTACK64 0x7ffffff00000
+
+uint64_t lx_maxstack64 = LX_MAXSTACK64;
+
+static int lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
+ struct intpdata *idata, int level, size_t *execsz, int setid,
+ caddr_t exec_file, struct cred *cred, int *brand_action);
+
+static boolean_t lx_native_exec(uint8_t, const char **);
+static uint32_t lx_map32limit(proc_t *);
+
+static void lx_savecontext(ucontext_t *);
+static void lx_restorecontext(ucontext_t *);
+static caddr_t lx_sendsig_stack(int);
+static void lx_sendsig(int);
+#if defined(_SYSCALL32_IMPL)
+static void lx_savecontext32(ucontext32_t *);
+#endif
+static int lx_setid_clear(vattr_t *, cred_t *);
+#if defined(_LP64)
+static int lx_pagefault(proc_t *, klwp_t *, caddr_t, enum fault_type,
+ enum seg_rw);
+#endif
+static void lx_clearbrand(proc_t *, boolean_t);
+
+typedef struct lx_zfs_ds {
+ list_node_t ds_link;
+ char ds_name[MAXPATHLEN];
+ uint64_t ds_cookie;
+} lx_zfs_ds_t;
+
+/* lx brand */
+struct brand_ops lx_brops = {
+ lx_init_brand_data, /* b_init_brand_data */
+ lx_free_brand_data, /* b_free_brand_data */
+ lx_brandsys, /* b_brandsys */
+ lx_setbrand, /* b_setbrand */
+ lx_getattr, /* b_getattr */
+ lx_setattr, /* b_setattr */
+ lx_copy_procdata, /* b_copy_procdata */
+ lx_proc_exit, /* b_proc_exit */
+ lx_exec, /* b_exec */
+ lx_setrval, /* b_lwp_setrval */
+ lx_lwpdata_alloc, /* b_lwpdata_alloc */
+ lx_lwpdata_free, /* b_lwpdata_free */
+ lx_initlwp, /* b_initlwp */
+ lx_initlwp_post, /* b_initlwp_post */
+ lx_forklwp, /* b_forklwp */
+ lx_freelwp, /* b_freelwp */
+ lx_exitlwp, /* b_lwpexit */
+ lx_elfexec, /* b_elfexec */
+ NULL, /* b_sigset_native_to_brand */
+ NULL, /* b_sigset_brand_to_native */
+ lx_sigfd_translate, /* b_sigfd_translate */
+ NSIG, /* b_nsig */
+ lx_exit_with_sig, /* b_exit_with_sig */
+ lx_wait_filter, /* b_wait_filter */
+ lx_native_exec, /* b_native_exec */
+ lx_map32limit, /* b_map32limit */
+ lx_stop_notify, /* b_stop_notify */
+ lx_waitid_helper, /* b_waitid_helper */
+ lx_sigcld_repost, /* b_sigcld_repost */
+ lx_ptrace_issig_stop, /* b_issig_stop */
+ lx_ptrace_sig_ignorable, /* b_sig_ignorable */
+ lx_savecontext, /* b_savecontext */
+#if defined(_SYSCALL32_IMPL)
+ lx_savecontext32, /* b_savecontext32 */
+#endif
+ lx_restorecontext, /* b_restorecontext */
+ lx_sendsig_stack, /* b_sendsig_stack */
+ lx_sendsig, /* b_sendsig */
+ lx_setid_clear, /* b_setid_clear */
+#if defined(_LP64)
+ lx_pagefault, /* b_pagefault */
+#else
+ NULL,
+#endif
+ B_FALSE, /* b_intp_parse_arg */
+ lx_clearbrand, /* b_clearbrand */
+ lx_upcall_statd, /* b_rpc_statd */
+ lx_acct_out /* b_acct_out */
+};
+
+struct brand_mach_ops lx_mops = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ lx_fixsegreg,
+ lx_fsbase
+};
+
+struct brand lx_brand = {
+ BRAND_VER_1,
+ "lx",
+ &lx_brops,
+ &lx_mops,
+ sizeof (struct lx_proc_data)
+};
+
+static struct modlbrand modlbrand = {
+ &mod_brandops, "lx brand", &lx_brand
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlbrand, NULL
+};
+
+void
+lx_proc_exit(proc_t *p)
+{
+ lx_proc_data_t *lxpd;
+ proc_t *cp;
+
+ lx_clone_grp_exit(p, B_FALSE);
+ /* Cleanup any outstanding aio contexts */
+ lx_io_cleanup(p);
+
+ mutex_enter(&p->p_lock);
+ VERIFY((lxpd = ptolxproc(p)) != NULL);
+ VERIFY(lxpd->l_ptrace == 0);
+ if ((lxpd->l_flags & LX_PROC_CHILD_DEATHSIG) == 0) {
+ mutex_exit(&p->p_lock);
+ return;
+ }
+ mutex_exit(&p->p_lock);
+
+ /* Check for children which desire notification of parental death. */
+ mutex_enter(&pidlock);
+ for (cp = p->p_child; cp != NULL; cp = cp->p_sibling) {
+ mutex_enter(&cp->p_lock);
+ if ((lxpd = ptolxproc(cp)) == NULL) {
+ mutex_exit(&cp->p_lock);
+ continue;
+ }
+ if (lxpd->l_parent_deathsig != 0) {
+ sigtoproc(cp, NULL, lxpd->l_parent_deathsig);
+ }
+ mutex_exit(&cp->p_lock);
+ }
+ mutex_exit(&pidlock);
+}
+
+void
+lx_setbrand(proc_t *p)
+{
+ /* Send SIGCHLD to parent by default when child exits */
+ ptolxproc(p)->l_signal = stol_signo[SIGCHLD];
+}
+
+/* ARGSUSED */
+int
+lx_setattr(zone_t *zone, int attr, void *ubuf, size_t ubufsz)
+{
+ lx_zone_data_t *lxzd = (lx_zone_data_t *)zone->zone_brand_data;
+
+ switch (attr) {
+ case LX_ATTR_KERN_RELEASE: {
+ char buf[LX_KERN_RELEASE_MAX];
+ bzero(buf, LX_KERN_RELEASE_MAX);
+ if (ubufsz >= LX_KERN_RELEASE_MAX) {
+ return (ERANGE);
+ }
+ if (copyin(ubuf, buf, ubufsz) != 0) {
+ return (EFAULT);
+ }
+ mutex_enter(&lxzd->lxzd_lock);
+ (void) strlcpy(lxzd->lxzd_kernel_release, buf,
+ LX_KERN_RELEASE_MAX);
+ mutex_exit(&lxzd->lxzd_lock);
+ return (0);
+ }
+ case LX_ATTR_KERN_VERSION: {
+ char buf[LX_KERN_VERSION_MAX];
+ bzero(buf, LX_KERN_VERSION_MAX);
+ if (ubufsz >= LX_KERN_VERSION_MAX) {
+ return (ERANGE);
+ }
+ if (copyin(ubuf, buf, ubufsz) != 0) {
+ return (EFAULT);
+ }
+ mutex_enter(&lxzd->lxzd_lock);
+ (void) strlcpy(lxzd->lxzd_kernel_version, buf,
+ LX_KERN_VERSION_MAX);
+ mutex_exit(&lxzd->lxzd_lock);
+ return (0);
+ }
+ case LX_ATTR_TTY_GID: {
+ gid_t gid;
+ if (ubufsz != sizeof (gid)) {
+ return (ERANGE);
+ }
+ if (copyin(ubuf, &gid, ubufsz) != 0) {
+ return (EFAULT);
+ }
+ mutex_enter(&lxzd->lxzd_lock);
+ lxzd->lxzd_ttygrp = gid;
+ mutex_exit(&lxzd->lxzd_lock);
+ return (0);
+ }
+ default:
+ return (EINVAL);
+ }
+}
+
+/* ARGSUSED */
+int
+lx_getattr(zone_t *zone, int attr, void *ubuf, size_t *ubufsz)
+{
+ lx_zone_data_t *lxzd = (lx_zone_data_t *)zone->zone_brand_data;
+ int len;
+
+ switch (attr) {
+ case LX_ATTR_KERN_RELEASE: {
+ char buf[LX_KERN_RELEASE_MAX];
+
+ mutex_enter(&lxzd->lxzd_lock);
+ len = strnlen(lxzd->lxzd_kernel_release, LX_KERN_RELEASE_MAX);
+ len++;
+ if (*ubufsz < len) {
+ mutex_exit(&lxzd->lxzd_lock);
+ return (ERANGE);
+ }
+ bzero(buf, sizeof (buf));
+ (void) strncpy(buf, lxzd->lxzd_kernel_release, sizeof (buf));
+ mutex_exit(&lxzd->lxzd_lock);
+ if (copyout(buf, ubuf, len) != 0) {
+ return (EFAULT);
+ }
+ *ubufsz = len;
+ return (0);
+ }
+ case LX_ATTR_KERN_VERSION: {
+ char buf[LX_KERN_VERSION_MAX];
+
+ mutex_enter(&lxzd->lxzd_lock);
+ len = strnlen(lxzd->lxzd_kernel_version, LX_KERN_VERSION_MAX);
+ len++;
+ if (*ubufsz < len) {
+ mutex_exit(&lxzd->lxzd_lock);
+ return (ERANGE);
+ }
+ bzero(buf, sizeof (buf));
+ (void) strncpy(buf, lxzd->lxzd_kernel_version, sizeof (buf));
+ mutex_exit(&lxzd->lxzd_lock);
+ if (copyout(buf, ubuf, len) != 0) {
+ return (EFAULT);
+ }
+ *ubufsz = len;
+ return (0);
+ }
+ default:
+ return (EINVAL);
+ }
+}
+
+uint32_t
+lx_map32limit(proc_t *p)
+{
+ /*
+ * To be bug-for-bug compatible with Linux, we have MAP_32BIT only
+ * allow mappings in the first 31 bits. This was a nuance in the
+ * original Linux implementation circa 2002, and applications have
+ * come to depend on its behavior.
+ *
+ * This is only relevant for 64-bit processes.
+ */
+ if (p->p_model == DATAMODEL_LP64)
+ return ((uint32_t)1 << 31);
+
+ return ((uint32_t)USERLIMIT32);
+}
+
+void
+lx_brand_systrace_enable(void)
+{
+ VERIFY(!lx_systrace_enabled);
+
+ lx_systrace_enabled = 1;
+}
+
+void
+lx_brand_systrace_disable(void)
+{
+ VERIFY(lx_systrace_enabled);
+
+ lx_systrace_enabled = 0;
+}
+
+void
+lx_lwp_set_native_stack_current(lx_lwp_data_t *lwpd, uintptr_t new_sp)
+{
+ VERIFY(lwpd->br_ntv_stack != 0);
+
+ /*
+ * The "brand-lx-set-ntv-stack-current" probe has arguments:
+ * arg0: stack pointer before change
+ * arg1: stack pointer after change
+ * arg2: current stack base
+ */
+ DTRACE_PROBE3(brand__lx__set__ntv__stack__current,
+ uintptr_t, lwpd->br_ntv_stack_current,
+ uintptr_t, new_sp,
+ uintptr_t, lwpd->br_ntv_stack);
+
+ lwpd->br_ntv_stack_current = new_sp;
+}
+
+#if defined(_LP64)
+static int
+lx_pagefault(proc_t *p, klwp_t *lwp, caddr_t addr, enum fault_type type,
+ enum seg_rw rw)
+{
+ int syscall_num;
+
+ /*
+ * We only want to handle a very specific set of circumstances.
+ * Namely: this is a 64-bit LX-branded process attempting to execute an
+ * address in a page for which it does not have a valid mapping. If
+ * this is not the case, we bail out as fast as possible.
+ */
+ VERIFY(PROC_IS_BRANDED(p));
+ if (type != F_INVAL || rw != S_EXEC || lwp_getdatamodel(lwp) !=
+ DATAMODEL_NATIVE) {
+ return (-1);
+ }
+
+ if (!lx_vsyscall_iscall(lwp, (uintptr_t)addr, &syscall_num)) {
+ return (-1);
+ }
+
+ /*
+ * This is a valid vsyscall address. We service the system call and
+ * return 0 to signal that the pagefault has been handled completely.
+ */
+ lx_vsyscall_enter(p, lwp, syscall_num);
+ return (0);
+}
+#endif
+
+static void
+lx_clearbrand(proc_t *p, boolean_t lwps_ok)
+{
+ lx_clone_grp_exit(p, lwps_ok);
+}
+
+/*
+ * This hook runs prior to sendsig() processing and allows us to nominate
+ * an alternative stack pointer for delivery of the signal handling frame.
+ * Critically, this routine should _not_ modify any LWP state as the
+ * savecontext() does not run until after this hook.
+ */
+/* ARGSUSED */
+static caddr_t
+lx_sendsig_stack(int sig)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ /*
+ * We want to take signal delivery on the native stack, but only if
+ * one has been allocated and installed for this LWP.
+ */
+ if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+ /*
+ * The program is not running on the native stack. Return
+ * the native stack pointer from our brand-private data so
+ * that we may switch to it for signal handling.
+ */
+ return ((caddr_t)lwpd->br_ntv_stack_current);
+ } else {
+ struct regs *rp = lwptoregs(lwp);
+
+ /*
+ * Either the program is already running on the native stack,
+ * or one has not yet been allocated for this LWP. Use the
+ * current stack pointer value.
+ */
+ return ((caddr_t)rp->r_sp);
+ }
+}
+
+/*
+ * This hook runs after sendsig() processing and allows us to update the
+ * per-LWP mode flags for system calls and stacks. The pre-signal
+ * context has already been saved and delivered to the user at this point.
+ */
+/* ARGSUSED */
+static void
+lx_sendsig(int sig)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ struct regs *rp = lwptoregs(lwp);
+
+ switch (lwpd->br_stack_mode) {
+ case LX_STACK_MODE_BRAND:
+ case LX_STACK_MODE_NATIVE:
+ /*
+ * In lx_sendsig_stack(), we nominated a stack pointer from the
+ * native stack. Update the stack mode, and the current in-use
+ * extent of the native stack, accordingly:
+ */
+ lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+ lx_lwp_set_native_stack_current(lwpd, rp->r_sp);
+
+ /*
+ * Fix up segment registers, etc.
+ */
+ lx_switch_to_native(lwp);
+ break;
+
+ default:
+ /*
+ * Otherwise, the brand library has not yet installed the
+ * alternate stack for this LWP. Signals will be handled on
+ * the regular stack thread.
+ */
+ return;
+ }
+}
+
+/*
+ * This hook runs prior to the context restoration, allowing us to take action
+ * or modify the context before it is loaded.
+ */
+static void
+lx_restorecontext(ucontext_t *ucp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ uintptr_t flags = (uintptr_t)ucp->uc_brand_data[0];
+ caddr_t sp = ucp->uc_brand_data[1];
+
+ if (lwpd->br_stack_mode == LX_STACK_MODE_PREINIT) {
+ /*
+ * Since we're here with stack_mode as LX_STACK_MODE_PREINIT,
+ * that can only mean we took a signal really early in this
+ * thread's lifetime, before we had a chance to setup a native
+ * stack and start running the thread's code. Since we're still
+ * handling everything on the single stack, we can't do any of
+ * the usual work below. Note: this means we cannot look at
+ * "flags" since the uc_brand_data may not have been properly
+ * set, depending on where we were when we took the signal.
+ */
+ return;
+ }
+
+ /*
+ * We have a saved native stack pointer value that we must restore
+ * into the per-LWP data.
+ */
+ if (flags & LX_UC_RESTORE_NATIVE_SP) {
+ lx_lwp_set_native_stack_current(lwpd, (uintptr_t)sp);
+ }
+
+ /*
+ * We do not wish to restore the value of uc_link in this context,
+ * so replace it with the value currently in the LWP.
+ */
+ if (flags & LX_UC_IGNORE_LINK) {
+ ucp->uc_link = (ucontext_t *)lwp->lwp_oldcontext;
+ }
+
+ /*
+ * Set or restore the stack mode. Usually this restores the mode, but
+ * the lx_runexe code flow also uses this to set the mode from
+ * LX_STACK_MODE_INIT to LX_UC_STACK_BRAND.
+ */
+ if (flags & LX_UC_STACK_NATIVE) {
+ lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+ } else if (flags & LX_UC_STACK_BRAND) {
+ lwpd->br_stack_mode = LX_STACK_MODE_BRAND;
+ }
+
+#if defined(__amd64)
+ /*
+ * Override the fs/gsbase in the context with the value provided
+ * through the Linux arch_prctl(2) system call.
+ */
+ if (flags & LX_UC_STACK_BRAND) {
+ if (lwpd->br_lx_fsbase != 0) {
+ ucp->uc_mcontext.gregs[REG_FSBASE] = lwpd->br_lx_fsbase;
+ }
+ if (lwpd->br_lx_gsbase != 0) {
+ ucp->uc_mcontext.gregs[REG_GSBASE] = lwpd->br_lx_gsbase;
+ }
+ }
+#endif
+}
+
+static void
+lx_savecontext(ucontext_t *ucp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ uintptr_t flags = 0;
+
+ /*
+ * The ucontext_t affords us three private pointer-sized members in
+ * "uc_brand_data". We pack a variety of flags into the first element,
+ * and an optional stack pointer in the second element. The flags
+ * determine which stack pointer (native or brand), if any, is stored
+ * in the second element. The third element may contain the system
+ * call number; this is analogous to the "orig_[er]ax" member of a
+ * Linux "user_regs_struct".
+ */
+
+ if (lwpd->br_stack_mode != LX_STACK_MODE_INIT &&
+ lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+ /*
+ * Record the value of the native stack pointer to restore
+ * when returning to this branded context:
+ */
+ flags |= LX_UC_RESTORE_NATIVE_SP;
+ ucp->uc_brand_data[1] = (void *)lwpd->br_ntv_stack_current;
+ }
+
+ /*
+ * Save the stack mode:
+ */
+ if (lwpd->br_stack_mode == LX_STACK_MODE_NATIVE) {
+ flags |= LX_UC_STACK_NATIVE;
+ } else if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+ flags |= LX_UC_STACK_BRAND;
+ }
+
+ /*
+ * If we might need to restart this system call, save that information
+ * in the context:
+ */
+ if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+ ucp->uc_brand_data[2] =
+ (void *)(uintptr_t)lwpd->br_syscall_num;
+ if (lwpd->br_syscall_restart) {
+ flags |= LX_UC_RESTART_SYSCALL;
+ }
+ } else {
+ ucp->uc_brand_data[2] = NULL;
+ }
+
+ ucp->uc_brand_data[0] = (void *)flags;
+}
+
+#if defined(_SYSCALL32_IMPL)
+static void
+lx_savecontext32(ucontext32_t *ucp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ unsigned int flags = 0;
+
+ /*
+ * The ucontext_t affords us three private pointer-sized members in
+ * "uc_brand_data". We pack a variety of flags into the first element,
+ * and an optional stack pointer in the second element. The flags
+ * determine which stack pointer (native or brand), if any, is stored
+ * in the second element. The third element may contain the system
+ * call number; this is analogous to the "orig_[er]ax" member of a
+ * Linux "user_regs_struct".
+ */
+
+ if (lwpd->br_stack_mode != LX_STACK_MODE_INIT &&
+ lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+ /*
+ * Record the value of the native stack pointer to restore
+ * when returning to this branded context:
+ */
+ flags |= LX_UC_RESTORE_NATIVE_SP;
+ ucp->uc_brand_data[1] = (caddr32_t)lwpd->br_ntv_stack_current;
+ }
+
+ /*
+ * Save the stack mode:
+ */
+ if (lwpd->br_stack_mode == LX_STACK_MODE_NATIVE) {
+ flags |= LX_UC_STACK_NATIVE;
+ } else if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+ flags |= LX_UC_STACK_BRAND;
+ }
+
+ /*
+ * If we might need to restart this system call, save that information
+ * in the context:
+ */
+ if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+ ucp->uc_brand_data[2] = (caddr32_t)lwpd->br_syscall_num;
+ if (lwpd->br_syscall_restart) {
+ flags |= LX_UC_RESTART_SYSCALL;
+ }
+ } else {
+ ucp->uc_brand_data[2] = (caddr32_t)(uintptr_t)NULL;
+ }
+
+ ucp->uc_brand_data[0] = flags;
+}
+#endif
+
+static int
+lx_zfs_ioctl(ldi_handle_t lh, int cmd, zfs_cmd_t *zc, size_t *dst_alloc_size)
+{
+ uint64_t cookie;
+ size_t dstsize;
+ int rc, unused;
+
+ cookie = zc->zc_cookie;
+
+ dstsize = (dst_alloc_size == NULL ? 0 : 8192);
+
+again:
+ if (dst_alloc_size != NULL) {
+ zc->zc_nvlist_dst = (uint64_t)(intptr_t)kmem_alloc(dstsize,
+ KM_SLEEP);
+ zc->zc_nvlist_dst_size = dstsize;
+ }
+
+ rc = ldi_ioctl(lh, cmd, (intptr_t)zc, FKIOCTL, kcred, &unused);
+ if (rc == ENOMEM && dst_alloc_size != NULL) {
+ /*
+ * Our nvlist_dst buffer was too small, retry with a bigger
+ * buffer. ZFS will tell us the exact needed size.
+ */
+ size_t newsize = zc->zc_nvlist_dst_size;
+ ASSERT(newsize > dstsize);
+
+ kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, dstsize);
+ dstsize = newsize;
+ zc->zc_cookie = cookie;
+
+ goto again;
+ }
+
+ if (dst_alloc_size != NULL) {
+ *dst_alloc_size = dstsize;
+ }
+
+ return (rc);
+}
+
+static int
+lx_zone_zfs_open(ldi_handle_t *lh, dev_t *zfs_dev)
+{
+ ldi_ident_t li;
+
+ if (ldi_ident_from_mod(&modlinkage, &li) != 0) {
+ return (-1);
+ }
+ if (ldi_open_by_name("/dev/zfs", FREAD|FWRITE, kcred, lh, li) != 0) {
+ ldi_ident_release(li);
+ return (-1);
+ }
+ ldi_ident_release(li);
+ if (ldi_get_dev(*lh, zfs_dev) != 0) {
+ (void) ldi_close(*lh, FREAD|FWRITE, kcred);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * We only get the relevant properties for zvols. This is because we're
+ * essentially iterating all of the ZFS datasets/zvols on the entire system
+ * when we boot the zone and there is a significant performance penalty if we
+ * have to retrieve all of the properties for everything. Especially since we
+ * don't care about any of them except the zvols actually in our delegated
+ * datasets.
+ *
+ * Note that the two properties we care about, volsize & volblocksize, are
+ * mandatory for zvols and should always be present. Also, note that the
+ * blocksize property value cannot change after the zvol has been created.
+ */
+static void
+lx_zvol_props(ldi_handle_t lh, zfs_cmd_t *zc, uint64_t *vsz, uint64_t *bsz)
+{
+ int rc;
+ size_t size;
+ nvlist_t *nv = NULL, *nv2;
+
+ rc = lx_zfs_ioctl(lh, ZFS_IOC_OBJSET_STATS, zc, &size);
+ if (rc != 0)
+ return;
+
+ rc = nvlist_unpack((char *)(uintptr_t)zc->zc_nvlist_dst,
+ zc->zc_nvlist_dst_size, &nv, 0);
+ ASSERT(rc == 0);
+
+ kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
+ zc->zc_nvlist_dst = (uintptr_t)NULL;
+ zc->zc_nvlist_dst_size = 0;
+
+ if ((rc = nvlist_lookup_nvlist(nv, "volsize", &nv2)) == 0) {
+ uint64_t val;
+
+ rc = nvlist_lookup_uint64(nv2, ZPROP_VALUE, &val);
+ if (rc == 0) {
+ *vsz = val;
+ }
+ }
+
+ if ((rc = nvlist_lookup_nvlist(nv, "volblocksize", &nv2)) == 0) {
+ uint64_t val;
+
+ rc = nvlist_lookup_uint64(nv2, ZPROP_VALUE, &val);
+ if (rc == 0) {
+ *bsz = val;
+ }
+ }
+
+ nvlist_free(nv);
+}
+
+/*
+ * Unlike ZFS proper, which does dynamic zvols, we currently only generate the
+ * zone's "disk" list once at zone boot time and use that consistently in all
+ * of the various subsystems (devfs, sysfs, procfs). This allows us to avoid
+ * re-iterating the datasets every time one of those subsystems accesses a
+ * "disk" and allows us to keep the view consistent across all subsystems, but
+ * it does mean a reboot is required to see new "disks". This is somewhat
+ * mitigated by its similarity to actual disk drives on a real system.
+ */
+static void
+lx_zone_get_zvols(zone_t *zone, ldi_handle_t lh, minor_t *emul_minor)
+{
+ lx_zone_data_t *lxzd;
+ list_t *zvol_lst, ds_lst;
+ int rc;
+ unsigned int devnum = 0;
+ size_t size;
+ zfs_cmd_t *zc;
+ nvpair_t *elem = NULL;
+ nvlist_t *pnv = NULL;
+
+ lxzd = ztolxzd(zone);
+ ASSERT(lxzd != NULL);
+ zvol_lst = lxzd->lxzd_vdisks;
+
+ zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
+ if (lx_zfs_ioctl(lh, ZFS_IOC_POOL_CONFIGS, zc, &size) != 0) {
+ goto out;
+ }
+ ASSERT(zc->zc_cookie > 0);
+
+ rc = nvlist_unpack((char *)(uintptr_t)zc->zc_nvlist_dst,
+ zc->zc_nvlist_dst_size, &pnv, 0);
+ kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
+ if (rc != 0)
+ goto out;
+
+ /*
+ * We use a dataset list to process all of the datasets in the pool
+ * without doing recursion so that we don't risk blowing the kernel
+ * stack.
+ */
+ list_create(&ds_lst, sizeof (lx_zfs_ds_t),
+ offsetof(lx_zfs_ds_t, ds_link));
+
+ while ((elem = nvlist_next_nvpair(pnv, elem)) != NULL) {
+ lx_zfs_ds_t *ds;
+
+ ds = kmem_zalloc(sizeof (lx_zfs_ds_t), KM_SLEEP);
+ (void) strcpy(ds->ds_name, nvpair_name(elem));
+ list_insert_head(&ds_lst, ds);
+
+ while (ds != NULL) {
+ int w; /* dummy variable */
+
+ bzero(zc, sizeof (zfs_cmd_t));
+ zc->zc_cookie = ds->ds_cookie;
+ (void) strcpy(zc->zc_name, ds->ds_name);
+
+ rc = lx_zfs_ioctl(lh, ZFS_IOC_DATASET_LIST_NEXT,
+ zc, NULL);
+ /* Update the cookie before doing anything else. */
+ ds->ds_cookie = zc->zc_cookie;
+
+ if (rc != 0) {
+ list_remove(&ds_lst, ds);
+ kmem_free(ds, sizeof (lx_zfs_ds_t));
+ ds = list_tail(&ds_lst);
+ continue;
+ }
+
+ /* Reserved internal names, skip over these. */
+ if (strchr(zc->zc_name, '$') != NULL ||
+ strchr(zc->zc_name, '%') != NULL)
+ continue;
+
+ if (!zone_dataset_visible_inzone(zone, zc->zc_name, &w))
+ continue;
+
+ if (zc->zc_objset_stats.dds_type == DMU_OST_ZVOL) {
+ lx_virt_disk_t *vd;
+ minor_t m = 0;
+ char *znm = zc->zc_name;
+
+ /* Create a virtual disk entry for the zvol */
+ vd = kmem_zalloc(sizeof (lx_virt_disk_t),
+ KM_SLEEP);
+ vd->lxvd_type = LXVD_ZVOL;
+ (void) snprintf(vd->lxvd_name,
+ sizeof (vd->lxvd_name),
+ "zvol%u", devnum++);
+ (void) strlcpy(vd->lxvd_real_name,
+ zc->zc_name,
+ sizeof (vd->lxvd_real_name));
+
+ /* Record emulated and real dev_t values */
+ vd->lxvd_emul_dev = makedevice(LX_MAJOR_DISK,
+ (*emul_minor)++);
+ if (zvol_name2minor(znm, &m) != 0) {
+ (void) zvol_create_minor(znm);
+ VERIFY(zvol_name2minor(znm, &m) == 0);
+ }
+ if (m != 0) {
+ vd->lxvd_real_dev = makedevice(
+ getmajor(lxzd->lxzd_zfs_dev), m);
+ }
+
+ /* Query volume size properties */
+ lx_zvol_props(lh, zc, &vd->lxvd_volsize,
+ &vd->lxvd_blksize);
+
+ list_insert_tail(zvol_lst, vd);
+ } else {
+ lx_zfs_ds_t *nds;
+
+ /* Create a new ds_t for the child. */
+ nds = kmem_zalloc(sizeof (lx_zfs_ds_t),
+ KM_SLEEP);
+ (void) strcpy(nds->ds_name, zc->zc_name);
+ list_insert_after(&ds_lst, ds, nds);
+
+ /* Depth-first, so do the one just created. */
+ ds = nds;
+ }
+ }
+
+ ASSERT(list_is_empty(&ds_lst));
+ }
+
+ list_destroy(&ds_lst);
+
+out:
+ nvlist_free(pnv);
+ kmem_free(zc, sizeof (zfs_cmd_t));
+}
+
+static void
+lx_zone_get_zfsds(zone_t *zone, minor_t *emul_minor)
+{
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+ vfs_t *vfsp = zone->zone_rootvp->v_vfsp;
+
+ /*
+ * Only the root will be mounted at zone init time.
+ * Finding means of discovering other datasets mounted in the zone
+ * would be a good enhancement later.
+ */
+ if (getmajor(vfsp->vfs_dev) == getmajor(lxzd->lxzd_zfs_dev)) {
+ lx_virt_disk_t *vd;
+
+ vd = kmem_zalloc(sizeof (lx_virt_disk_t), KM_SLEEP);
+ vd->lxvd_type = LXVD_ZFS_DS;
+ vd->lxvd_real_dev = vfsp->vfs_dev;
+ vd->lxvd_emul_dev = makedevice(LX_MAJOR_DISK, (*emul_minor)++);
+ (void) snprintf(vd->lxvd_name, sizeof (vd->lxvd_name),
+ "zfsds%u", 0);
+ (void) strlcpy(vd->lxvd_real_name,
+ refstr_value(vfsp->vfs_resource),
+ sizeof (vd->lxvd_real_name));
+
+ list_insert_tail(lxzd->lxzd_vdisks, vd);
+ }
+}
+
+/* Cleanup virtual disk list */
+static void
+lx_zone_cleanup_vdisks(lx_zone_data_t *lxzd)
+{
+ lx_virt_disk_t *vd;
+
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+ vd = (list_remove_head(lxzd->lxzd_vdisks));
+ while (vd != NULL) {
+ kmem_free(vd, sizeof (lx_virt_disk_t));
+ vd = list_remove_head(lxzd->lxzd_vdisks);
+ }
+
+ list_destroy(lxzd->lxzd_vdisks);
+ kmem_free(lxzd->lxzd_vdisks, sizeof (list_t));
+ lxzd->lxzd_vdisks = NULL;
+}
+
+/*
+ * By default illumos restricts access to ULP_DEF_EPRIV_PORT1 and
+ * ULP_DEF_EPRIV_PORT2 for TCP and UDP, even though these ports are outside of
+ * the privileged port range. Linux does not do this, so we need to remove
+ * these defaults.
+ *
+ * See also: mod_set_extra_privports
+ */
+static void
+lx_fix_ns_eports(netstack_t *ns)
+{
+ tcp_stack_t *tcps;
+ udp_stack_t *udps;
+ in_port_t *ports;
+ uint_t i, nports;
+ kmutex_t *lock;
+
+ tcps = ns->netstack_tcp;
+ ports = tcps->tcps_g_epriv_ports;
+ nports = tcps->tcps_g_num_epriv_ports;
+ lock = &tcps->tcps_epriv_port_lock;
+ mutex_enter(lock);
+ for (i = 0; i < nports; i++)
+ ports[i] = 0;
+ mutex_exit(lock);
+
+ udps = ns->netstack_udp;
+ ports = udps->us_epriv_ports;
+ nports = udps->us_num_epriv_ports;
+ lock = &udps->us_epriv_port_lock;
+ mutex_enter(lock);
+ for (i = 0; i < nports; i++)
+ ports[i] = 0;
+ mutex_exit(lock);
+}
+
+/*
+ * The default limit for TCP buffer sizing on illumos is smaller than its
+ * counterparts on Linux. Adjust it to meet minimum expectations.
+ */
+static void
+lx_fix_ns_buffers(netstack_t *ns)
+{
+ mod_prop_info_t *pinfo;
+ ulong_t target, parsed;
+ char buf[16];
+
+ /*
+ * Prior to kernel 3.4, Linux defaulted to a max of 4MB for both the
+ * tcp_rmem and tcp_wmem tunables. Kernels since then increase the
+ * tcp_rmem default max to 6MB. Since illumos lacks separate tunables
+ * to cap sizing for read and write buffers, the higher value is
+ * selected for compatibility.
+ */
+ if (lx_kern_release_cmp(curzone, "3.4.0") < 0) {
+ target = 4*1024*1024;
+ } else {
+ target = 6*1024*1024;
+ }
+
+ pinfo = mod_prop_lookup(ns->netstack_tcp->tcps_propinfo_tbl,
+ "max_buf", MOD_PROTO_TCP);
+ if (pinfo == NULL ||
+ pinfo->mpi_getf(ns, pinfo, NULL, buf, sizeof (buf), 0) != 0 ||
+ ddi_strtoul(buf, NULL, 10, &parsed) != 0 ||
+ parsed >= target) {
+ return;
+ }
+
+ (void) snprintf(buf, sizeof (buf), "%lu", target);
+ (void) pinfo->mpi_setf(ns, CRED(), pinfo, NULL, buf, 0);
+}
+
+static void
+lx_bootup_hooks()
+{
+ netstack_t *ns;
+
+ ns = netstack_get_current();
+ if (ns == NULL)
+ return;
+
+ lx_fix_ns_eports(ns);
+ lx_fix_ns_buffers(ns);
+
+ netstack_rele(ns);
+}
+
+void
+lx_init_brand_data(zone_t *zone, kmutex_t *zsl)
+{
+ lx_zone_data_t *data;
+ ldi_handle_t lh;
+
+ ASSERT(MUTEX_HELD(zsl));
+ ASSERT(zone->zone_brand == &lx_brand);
+ ASSERT(zone->zone_brand_data == NULL);
+
+ data = (lx_zone_data_t *)kmem_zalloc(sizeof (lx_zone_data_t), KM_SLEEP);
+ mutex_init(&data->lxzd_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /* No need to hold mutex now since zone_brand_data is not set yet. */
+
+ /*
+ * Set the default lxzd_kernel_version to 2.4.
+ * This can be changed by a call to setattr() during zone boot.
+ */
+ (void) strlcpy(data->lxzd_kernel_release, "2.4.21",
+ LX_KERN_RELEASE_MAX);
+ (void) strlcpy(data->lxzd_kernel_version, "BrandZ virtual linux",
+ LX_KERN_VERSION_MAX);
+ data->lxzd_pipe_max_sz = lx_pipe_max_default;
+
+ zone->zone_brand_data = data;
+
+ /*
+ * In Linux, if the init(1) process terminates the system panics.
+ * The zone must reboot to simulate this behaviour.
+ */
+ zone->zone_reboot_on_init_exit = B_TRUE;
+
+ /*
+ * We cannot hold the zone_status_lock while performing zfs operations
+ * so we drop the lock, get the zfs devs as the last step in this
+ * function, then reaquire the lock. Don't add any code after this
+ * which requires that the zone_status_lock was continuously held.
+ */
+ mutex_exit(zsl);
+
+ data->lxzd_vdisks = kmem_alloc(sizeof (list_t), KM_SLEEP);
+ list_create(data->lxzd_vdisks, sizeof (lx_virt_disk_t),
+ offsetof(lx_virt_disk_t, lxvd_link));
+
+ if (lx_zone_zfs_open(&lh, &data->lxzd_zfs_dev) == 0) {
+ minor_t emul_minor = 1;
+
+ lx_zone_get_zfsds(zone, &emul_minor);
+ lx_zone_get_zvols(zone, lh, &emul_minor);
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ } else {
+ /* Avoid matching any devices */
+ data->lxzd_zfs_dev = makedevice(-1, 0);
+ }
+ mutex_enter(zsl);
+}
+
+void
+lx_free_brand_data(zone_t *zone)
+{
+ lx_zone_data_t *data = ztolxzd(zone);
+ ASSERT(data != NULL);
+ mutex_enter(&data->lxzd_lock);
+ lx_audit_fini(zone);
+ if (data->lxzd_ioctl_sock != NULL) {
+ /*
+ * Since zone_kcred has been cleaned up already, close the
+ * socket using the global kcred.
+ */
+ (void) ksocket_close(data->lxzd_ioctl_sock, kcred);
+ data->lxzd_ioctl_sock = NULL;
+ }
+ ASSERT(data->lxzd_cgroup == NULL);
+
+ lx_zone_cleanup_vdisks(data);
+
+ mutex_exit(&data->lxzd_lock);
+ zone->zone_brand_data = NULL;
+ mutex_destroy(&data->lxzd_lock);
+ kmem_free(data, sizeof (*data));
+}
+
+void
+lx_unsupported(char *dmsg)
+{
+ lx_proc_data_t *pd = ttolxproc(curthread);
+
+ DTRACE_PROBE1(brand__lx__unsupported, char *, dmsg);
+
+ if (pd != NULL && (pd->l_flags & LX_PROC_STRICT_MODE) != 0) {
+ /*
+ * If this process was run with strict mode enabled
+ * (via LX_STRICT in the environment), we mark this
+ * LWP as having triggered an unsupported behaviour.
+ * This flag will be checked at an appropriate point
+ * by lx_check_strict_failure().
+ */
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+
+ lwpd->br_strict_failure = B_TRUE;
+ }
+}
+
+void
+lx_check_strict_failure(lx_lwp_data_t *lwpd)
+{
+ proc_t *p;
+
+ if (!lwpd->br_strict_failure) {
+ return;
+ }
+
+ lwpd->br_strict_failure = B_FALSE;
+
+ /*
+ * If this process is operating in strict mode (via LX_STRICT in
+ * the environment), and has triggered a call to
+ * lx_unsupported(), we drop SIGSYS on it as we return.
+ */
+ p = curproc;
+ mutex_enter(&p->p_lock);
+ sigtoproc(p, curthread, SIGSYS);
+ mutex_exit(&p->p_lock);
+}
+
+void
+lx_trace_sysenter(int syscall_num, uintptr_t *args)
+{
+ if (lx_systrace_enabled) {
+ VERIFY(lx_systrace_entry_ptr != NULL);
+
+ (*lx_systrace_entry_ptr)(syscall_num, args[0], args[1],
+ args[2], args[3], args[4], args[5]);
+ }
+}
+
+void
+lx_trace_sysreturn(int syscall_num, long ret)
+{
+ if (lx_systrace_enabled) {
+ VERIFY(lx_systrace_return_ptr != NULL);
+
+ (*lx_systrace_return_ptr)(syscall_num, ret, ret, 0, 0, 0, 0);
+ }
+}
+
+/*
+ * Get the addresses of the user-space system call handler and attach it to
+ * the proc structure. Returning 0 indicates success; the value returned
+ * by the system call is the value stored in rval. Returning a non-zero
+ * value indicates a failure; the value returned is used to set errno, -1
+ * is returned from the syscall and the contents of rval are ignored. To
+ * set errno and have the syscall return a value other than -1 we can
+ * manually set errno and rval and return 0.
+ */
+int
+lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
+ uintptr_t arg3, uintptr_t arg4)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = ttoproc(t);
+ lx_proc_data_t *pd;
+ struct termios *termios;
+ uint_t termios_len;
+ int error;
+ int code;
+ int sig;
+ lx_brand_registration_t reg;
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ /*
+ * There is one operation that is suppored for non-branded
+ * process. B_EXEC_BRAND. This is the equilivant of an
+ * exec call, but the new process that is created will be
+ * a branded process.
+ */
+ if (cmd == B_EXEC_BRAND) {
+ VERIFY(p->p_zone != NULL);
+ VERIFY(p->p_zone->zone_brand == &lx_brand);
+ return (exec_common(
+ (char *)arg1, (const char **)arg2, (const char **)arg3,
+ EBA_BRAND));
+ }
+
+ /* For all other operations this must be a branded process. */
+ if (p->p_brand == NULL)
+ return (ENOSYS);
+
+ /*
+ * Certain native applications may wish to start the lx_lockd process.
+ * Every other process that's not branded should be denied.
+ */
+ if (p->p_brand != &lx_brand && cmd != B_START_NFS_LOCKD)
+ return (ENOSYS);
+
+ if (cmd != B_START_NFS_LOCKD)
+ VERIFY(p->p_brand_data != NULL);
+
+ switch (cmd) {
+ case B_REGISTER:
+ if (lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+ lx_print("stack mode was not PREINIT during "
+ "REGISTER\n");
+ return (EINVAL);
+ }
+
+ if (p->p_model == DATAMODEL_NATIVE) {
+ if (copyin((void *)arg1, &reg, sizeof (reg)) != 0) {
+ lx_print("Failed to copyin brand registration "
+ "at 0x%p\n", (void *)arg1);
+ return (EFAULT);
+ }
+ }
+#ifdef _LP64
+ else {
+ /* 32-bit userland on 64-bit kernel */
+ lx_brand_registration32_t reg32;
+
+ if (copyin((void *)arg1, &reg32, sizeof (reg32)) != 0) {
+ lx_print("Failed to copyin brand registration "
+ "at 0x%p\n", (void *)arg1);
+ return (EFAULT);
+ }
+
+ reg.lxbr_version = (uint_t)reg32.lxbr_version;
+ reg.lxbr_handler =
+ (void *)(uintptr_t)reg32.lxbr_handler;
+ reg.lxbr_flags = reg32.lxbr_flags;
+ }
+#endif
+
+ if (reg.lxbr_version != LX_VERSION_1) {
+ lx_print("Invalid brand library version (%u)\n",
+ reg.lxbr_version);
+ return (EINVAL);
+ }
+
+ if ((reg.lxbr_flags & ~LX_PROC_ALL) != 0) {
+ lx_print("Invalid brand flags (%u)\n",
+ reg.lxbr_flags);
+ return (EINVAL);
+ }
+
+ lx_print("Assigning brand 0x%p and handler 0x%p to proc 0x%p\n",
+ (void *)&lx_brand, (void *)reg.lxbr_handler, (void *)p);
+ pd = p->p_brand_data;
+ pd->l_handler = (uintptr_t)reg.lxbr_handler;
+ pd->l_flags = reg.lxbr_flags & LX_PROC_ALL;
+
+ /*
+ * There are certain setup tasks which cannot be performed
+ * during the lx_init_brand_data hook due to the calling
+ * context from zoneadmd (in the GZ). This work is instead
+ * delayed until the init process starts inside the zone.
+ */
+ if (p->p_pid == p->p_zone->zone_proc_initpid) {
+ lx_bootup_hooks();
+ }
+
+ return (0);
+
+ case B_TTYMODES:
+ /* This is necessary for emulating TCGETS ioctls. */
+ if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(),
+ DDI_PROP_NOTPROM, "ttymodes", (uchar_t **)&termios,
+ &termios_len) != DDI_SUCCESS)
+ return (EIO);
+
+ ASSERT(termios_len == sizeof (*termios));
+
+ if (copyout(&termios, (void *)arg1, sizeof (termios)) != 0) {
+ ddi_prop_free(termios);
+ return (EFAULT);
+ }
+
+ ddi_prop_free(termios);
+ return (0);
+
+ case B_ELFDATA: {
+ mutex_enter(&p->p_lock);
+ pd = curproc->p_brand_data;
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ lx_elf_data_t led;
+
+ bcopy(&pd->l_elf_data, &led, sizeof (led));
+ mutex_exit(&p->p_lock);
+
+ if (copyout(&led, (void *)arg1,
+ sizeof (lx_elf_data_t)) != 0) {
+ return (EFAULT);
+ }
+ }
+#if defined(_LP64)
+ else {
+ /* 32-bit userland on 64-bit kernel */
+ lx_elf_data32_t led32;
+
+ led32.ed_phdr = (int)pd->l_elf_data.ed_phdr;
+ led32.ed_phent = (int)pd->l_elf_data.ed_phent;
+ led32.ed_phnum = (int)pd->l_elf_data.ed_phnum;
+ led32.ed_entry = (int)pd->l_elf_data.ed_entry;
+ led32.ed_base = (int)pd->l_elf_data.ed_base;
+ led32.ed_ldentry = (int)pd->l_elf_data.ed_ldentry;
+ mutex_exit(&p->p_lock);
+
+ if (copyout(&led32, (void *)arg1,
+ sizeof (led32)) != 0) {
+ return (EFAULT);
+ }
+ }
+#endif
+ return (0);
+ }
+
+ case B_EXEC_NATIVE:
+ return (exec_common((char *)arg1, (const char **)arg2,
+ (const char **)arg3, EBA_NATIVE));
+
+ /*
+ * The B_TRUSS_POINT subcommand is used so that we can make a no-op
+ * syscall for debugging purposes (dtracing) from within the user-level
+ * emulation.
+ */
+ case B_TRUSS_POINT:
+ return (0);
+
+ case B_LPID_TO_SPAIR: {
+ /*
+ * Given a Linux pid as arg1, return the Solaris pid in arg2 and
+ * the Solaris LWP in arg3. We also translate pid 1 (which is
+ * hardcoded in many applications) to the zone's init process.
+ */
+ pid_t s_pid;
+ id_t s_tid;
+
+ if ((pid_t)arg1 == 1) {
+ s_pid = p->p_zone->zone_proc_initpid;
+ /* handle the dead/missing init(1M) case */
+ if (s_pid == -1)
+ s_pid = 1;
+ s_tid = 1;
+ } else if (lx_lpid_to_spair((pid_t)arg1, &s_pid, &s_tid) < 0) {
+ return (ESRCH);
+ }
+
+ if (copyout(&s_pid, (void *)arg2, sizeof (s_pid)) != 0 ||
+ copyout(&s_tid, (void *)arg3, sizeof (s_tid)) != 0) {
+ return (EFAULT);
+ }
+
+ return (0);
+ }
+
+ case B_PTRACE_STOP_FOR_OPT:
+ return (lx_ptrace_stop_for_option((int)arg1, arg2 == 0 ?
+ B_FALSE : B_TRUE, (ulong_t)arg3, arg4));
+
+ case B_PTRACE_CLONE_BEGIN:
+ /*
+ * Leverage ptrace brand call to create a clone group for this
+ * proc if necessary.
+ */
+ lx_clone_grp_create((uint_t)arg3);
+
+ return (lx_ptrace_set_clone_inherit((int)arg1, arg2 == 0 ?
+ B_FALSE : B_TRUE));
+
+ case B_PTRACE_SIG_RETURN: {
+ /*
+ * Our ptrace emulation must emit PR_SYSEXIT for rt_sigreturn.
+ * Since that syscall does not pass through the normal
+ * emulation, which would call lx_syscall_return, the event is
+ * emitted manually. A successful result of the syscall is
+ * assumed since there is little to be done in the face of
+ * failure.
+ */
+ struct regs *rp = lwptoregs(lwp);
+
+ rp->r_r0 = 0;
+ (void) lx_ptrace_stop(LX_PR_SYSEXIT);
+ return (0);
+ }
+
+ case B_UNSUPPORTED: {
+ char dmsg[256];
+
+ if (copyin((void *)arg1, &dmsg, sizeof (dmsg)) != 0) {
+ lx_print("Failed to copyin unsupported msg "
+ "at 0x%p\n", (void *)arg1);
+ return (EFAULT);
+ }
+ dmsg[255] = '\0';
+ lx_unsupported(dmsg);
+
+ lx_check_strict_failure(lwpd);
+
+ return (0);
+ }
+
+ case B_STORE_ARGS: {
+ /*
+ * B_STORE_ARGS subcommand
+ * arg1 = address of struct to be copied in
+ * arg2 = size of the struct being copied in
+ * arg3-arg6 ignored
+ * rval = the amount of data copied.
+ */
+ void *buf;
+
+ /* only have upper limit because arg2 is unsigned */
+ if (arg2 > LX_BR_ARGS_SIZE_MAX) {
+ return (EINVAL);
+ }
+
+ buf = kmem_alloc(arg2, KM_SLEEP);
+ if (copyin((void *)arg1, buf, arg2) != 0) {
+ lx_print("Failed to copyin scall arg at 0x%p\n",
+ (void *) arg1);
+ kmem_free(buf, arg2);
+ /*
+ * Purposely not setting br_scall_args to NULL
+ * to preserve data for debugging.
+ */
+ return (EFAULT);
+ }
+
+ if (lwpd->br_scall_args != NULL) {
+ ASSERT(lwpd->br_args_size > 0);
+ kmem_free(lwpd->br_scall_args,
+ lwpd->br_args_size);
+ }
+
+ lwpd->br_scall_args = buf;
+ lwpd->br_args_size = arg2;
+ *rval = arg2;
+ return (0);
+ }
+
+ case B_HELPER_CLONE:
+ return (lx_helper_clone(rval, arg1, (void *)arg2, (void *)arg3,
+ (void *)arg4));
+
+ case B_HELPER_SETGROUPS:
+ return (lx_helper_setgroups(arg1, (gid_t *)arg2));
+
+ case B_HELPER_SIGQUEUE:
+ return (lx_helper_rt_sigqueueinfo(arg1, arg2,
+ (siginfo_t *)arg3));
+
+ case B_HELPER_TGSIGQUEUE:
+ return (lx_helper_rt_tgsigqueueinfo(arg1, arg2, arg3,
+ (siginfo_t *)arg4));
+
+ case B_GETPID:
+ /*
+ * The usermode clone(2) code needs to be able to call
+ * lx_getpid() from native code:
+ */
+ *rval = lx_getpid();
+ return (0);
+
+ case B_SET_NATIVE_STACK:
+ /*
+ * B_SET_NATIVE_STACK subcommand
+ * arg1 = the base of the stack to use for emulation
+ */
+ if (lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+ lx_print("B_SET_NATIVE_STACK when stack was already "
+ "set to %p\n", (void *)arg1);
+ return (EEXIST);
+ }
+
+ /*
+ * We move from the PREINIT state, where we have no brand
+ * emulation stack, to the INIT state. Here, we are still
+ * running on what will become the BRAND stack, but are running
+ * emulation (i.e. native) code. Once the initialisation
+ * process for this thread has finished, we will jump to
+ * brand-specific code, while moving to the BRAND mode.
+ *
+ * When a new LWP is created, lx_initlwp() will clear the
+ * stack data. If that LWP is actually being duplicated
+ * into a child process by fork(2), lx_forklwp() will copy
+ * it so that the cloned thread will keep using the same
+ * alternate stack.
+ */
+ lwpd->br_ntv_stack = arg1;
+ lwpd->br_stack_mode = LX_STACK_MODE_INIT;
+ lx_lwp_set_native_stack_current(lwpd, arg1);
+
+ return (0);
+
+ case B_GET_CURRENT_CONTEXT:
+ /*
+ * B_GET_CURRENT_CONTEXT subcommand:
+ * arg1 = address for pointer to current ucontext_t
+ */
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ caddr32_t addr = (caddr32_t)lwp->lwp_oldcontext;
+
+ error = copyout(&addr, (void *)arg1, sizeof (addr));
+ } else
+#endif
+ {
+ error = copyout(&lwp->lwp_oldcontext, (void *)arg1,
+ sizeof (lwp->lwp_oldcontext));
+ }
+
+ return (error != 0 ? EFAULT : 0);
+
+ case B_JUMP_TO_LINUX:
+ /*
+ * B_JUMP_TO_LINUX subcommand:
+ * arg1 = ucontext_t pointer for jump state
+ */
+
+ if (arg1 == (uintptr_t)NULL)
+ return (EINVAL);
+
+ switch (lwpd->br_stack_mode) {
+ case LX_STACK_MODE_NATIVE: {
+ struct regs *rp = lwptoregs(lwp);
+
+ /*
+ * We are on the NATIVE stack, so we must preserve
+ * the extent of that stack. The pointer will be
+ * reset by a future setcontext().
+ */
+ lx_lwp_set_native_stack_current(lwpd,
+ (uintptr_t)rp->r_sp);
+ break;
+ }
+
+ case LX_STACK_MODE_INIT:
+ /*
+ * The LWP is transitioning to Linux code for the first
+ * time.
+ */
+ break;
+
+ case LX_STACK_MODE_PREINIT:
+ /*
+ * This LWP has not installed an alternate stack for
+ * usermode emulation handling.
+ */
+ return (ENOENT);
+
+ case LX_STACK_MODE_BRAND:
+ /*
+ * The LWP should not be on the BRAND stack.
+ */
+ exit(CLD_KILLED, SIGSYS);
+ return (0);
+ }
+
+ /*
+ * Transfer control to Linux:
+ */
+ return (lx_runexe(lwp, (void *)arg1));
+
+ case B_EMULATION_DONE:
+ /*
+ * B_EMULATION_DONE subcommand:
+ * arg1 = ucontext_t * to restore
+ * arg2 = system call number
+ * arg3 = return code
+ * arg4 = if operation failed, the errno value
+ */
+
+ /*
+ * The first part of this operation is a setcontext() to
+ * restore the register state to the copy we preserved
+ * before vectoring to the usermode emulation routine.
+ * If that fails, we return (hopefully) to the emulation
+ * routine and it will handle the error.
+ */
+#if (_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ error = getsetcontext32(SETCONTEXT, (void *)arg1);
+ } else
+#endif
+ {
+ error = getsetcontext(SETCONTEXT, (void *)arg1);
+ }
+
+ if (error != 0) {
+ return (error);
+ }
+
+ /*
+ * The saved Linux context has been restored. We handle the
+ * return value or errno with code common to the in-kernel
+ * system call emulation.
+ */
+ if ((error = (int)arg4) != 0) {
+ /*
+ * lx_syscall_return() looks at the errno in the LWP,
+ * so set it here:
+ */
+ (void) set_errno(error);
+ }
+ lx_syscall_return(ttolwp(curthread), (int)arg2, (long)arg3);
+
+ return (0);
+
+ case B_EXIT_AS_SIG:
+ code = CLD_KILLED;
+ sig = (int)arg1;
+ proc_is_exiting(p);
+ if (exitlwps(1) != 0) {
+ mutex_enter(&p->p_lock);
+ lwp_exit();
+ }
+ ttolwp(curthread)->lwp_cursig = sig;
+ if (sig == SIGSEGV) {
+ if (core(sig, 0) == 0)
+ code = CLD_DUMPED;
+ }
+ exit(code, sig);
+ /* NOTREACHED */
+ break;
+
+ case B_OVERRIDE_KERN_VER: {
+ void *urel = (void *)arg1;
+ void *uver = (void *)arg2;
+ size_t len;
+
+ pd = ptolxproc(p);
+ if (urel != NULL) {
+ if (copyinstr(urel, pd->l_uname_release,
+ LX_KERN_RELEASE_MAX, &len) != 0) {
+ return (EFAULT);
+ }
+ pd->l_uname_release[LX_KERN_RELEASE_MAX - 1] = '\0';
+ }
+ if (uver != NULL) {
+ if (copyinstr(uver, pd->l_uname_version,
+ LX_KERN_VERSION_MAX, &len) != 0) {
+ return (EFAULT);
+ }
+ pd->l_uname_version[LX_KERN_VERSION_MAX - 1] = '\0';
+ }
+
+ return (0);
+ }
+
+ case B_GET_PERSONALITY: {
+ unsigned int result;
+
+ mutex_enter(&p->p_lock);
+ pd = ptolxproc(p);
+ result = pd->l_personality;
+ mutex_exit(&p->p_lock);
+ return (result);
+ }
+
+ case B_START_NFS_LOCKD:
+ (void) lx_start_nfs_lockd();
+ return (0);
+
+ case B_BLOCK_ALL_SIGS: {
+ uint_t result = 0;
+
+ mutex_enter(&p->p_lock);
+ pd = ptolxproc(p);
+ /*
+ * This is used to block handling of all signals during vfork()
+ * or clone(LX_CLONE_VFORK) emulation to match Linux semantics
+ * and prevent the parent's signal handlers being called before
+ * they are properly reset.
+ */
+ if (pd->l_block_all_signals != 0) {
+ result = set_errno(EAGAIN);
+ } else {
+ pd->l_block_all_signals = 1;
+ }
+ mutex_exit(&p->p_lock);
+ return (result);
+ }
+
+ case B_UNBLOCK_ALL_SIGS: {
+ uint_t result = 0;
+
+ mutex_enter(&p->p_lock);
+ pd = ptolxproc(p);
+ if (pd->l_block_all_signals == 0) {
+ result = set_errno(EINVAL);
+ } else {
+ pd->l_block_all_signals = 0;
+ }
+ mutex_exit(&p->p_lock);
+ return (result);
+ }
+
+ case B_ALL_SIGS_BLOCKED: {
+ uint_t result;
+
+ mutex_enter(&p->p_lock);
+ pd = ptolxproc(p);
+ result = pd->l_block_all_signals;
+ mutex_exit(&p->p_lock);
+ return (result);
+ }
+ }
+
+ return (EINVAL);
+}
+
+/*
+ * Compare linux kernel version to the one set for the zone.
+ * Returns greater than 0 if zone version is higher, less than 0 if the zone
+ * version is lower, and 0 if the versions are equal.
+ */
+int
+lx_kern_release_cmp(zone_t *zone, const char *vers)
+{
+ int zvers[3] = {0, 0, 0};
+ int cvers[3] = {0, 0, 0};
+ int i;
+ lx_zone_data_t *lxzd = (lx_zone_data_t *)zone->zone_brand_data;
+
+ VERIFY(zone->zone_brand == &lx_brand);
+
+ mutex_enter(&lxzd->lxzd_lock);
+ (void) sscanf(lxzd->lxzd_kernel_release, "%d.%d.%d", &zvers[0],
+ &zvers[1], &zvers[2]);
+ mutex_exit(&lxzd->lxzd_lock);
+ (void) sscanf(vers, "%d.%d.%d", &cvers[0], &cvers[1], &cvers[2]);
+
+ for (i = 0; i < 3; i++) {
+ if (zvers[i] > cvers[i]) {
+ return (1);
+ } else if (zvers[i] < cvers[i]) {
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Linux unconditionally removes the setuid and setgid bits when changing
+ * file ownership. This brand hook overrides the illumos native behaviour,
+ * which is based on the PRIV_FILE_SETID privilege.
+ */
+/* ARGSUSED */
+static int
+lx_setid_clear(vattr_t *vap, cred_t *cr)
+{
+ if (S_ISDIR(vap->va_mode)) {
+ return (0);
+ }
+
+ if (vap->va_mode & S_ISUID) {
+ vap->va_mask |= AT_MODE;
+ vap->va_mode &= ~S_ISUID;
+ }
+ if ((vap->va_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+ vap->va_mask |= AT_MODE;
+ vap->va_mode &= ~S_ISGID;
+ }
+
+ return (0);
+}
+
+/*
+ * Copy the per-process brand data from a parent proc to a child.
+ */
+void
+lx_copy_procdata(proc_t *cp, proc_t *pp)
+{
+ lx_proc_data_t *cpd, *ppd;
+
+ /*
+ * Since b_copy_procdata is called during getproc(), while the child
+ * process is still being initialized, acquiring cp->p_lock should not
+ * be required.
+ */
+ VERIFY(cp->p_brand == &lx_brand);
+ VERIFY((cpd = cp->p_brand_data) != NULL);
+
+ mutex_enter(&pp->p_lock);
+ VERIFY(pp->p_brand == &lx_brand);
+ VERIFY((ppd = pp->p_brand_data) != NULL);
+
+ bcopy(ppd, cpd, sizeof (lx_proc_data_t));
+ mutex_exit(&pp->p_lock);
+
+ /* Clear any aio contexts from child */
+ lx_io_clear(cpd);
+
+ /*
+ * The l_ptrace count is normally manipulated only while under holding
+ * p_lock. Since this is a freshly created process, it's safe to zero
+ * out. If it is to be inherited, the attach will occur later.
+ */
+ cpd->l_ptrace = 0;
+
+ cpd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_cur = LX_RLIM64_INFINITY;
+ cpd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_max = LX_RLIM64_INFINITY;
+
+ cpd->l_fake_limits[LX_RLFAKE_NICE].rlim_cur = 20;
+ cpd->l_fake_limits[LX_RLFAKE_NICE].rlim_max = 20;
+
+ cpd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_cur = LX_RLIM64_INFINITY;
+ cpd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_max = LX_RLIM64_INFINITY;
+
+ cpd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_cur = LX_RLIM64_INFINITY;
+ cpd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_max = LX_RLIM64_INFINITY;
+
+ bzero(cpd->l_clone_grps, sizeof (cpd->l_clone_grps));
+}
+
+#if defined(_LP64)
+static void
+Ehdr32to64(Elf32_Ehdr *src, Ehdr *dst)
+{
+ bcopy(src->e_ident, dst->e_ident, sizeof (src->e_ident));
+ dst->e_type = src->e_type;
+ dst->e_machine = src->e_machine;
+ dst->e_version = src->e_version;
+ dst->e_entry = src->e_entry;
+ dst->e_phoff = src->e_phoff;
+ dst->e_shoff = src->e_shoff;
+ dst->e_flags = src->e_flags;
+ dst->e_ehsize = src->e_ehsize;
+ dst->e_phentsize = src->e_phentsize;
+ dst->e_phnum = src->e_phnum;
+ dst->e_shentsize = src->e_shentsize;
+ dst->e_shnum = src->e_shnum;
+ dst->e_shstrndx = src->e_shstrndx;
+}
+#endif /* _LP64 */
+
+static void
+restoreexecenv(struct execenv *ep, stack_t *sp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+
+ setexecenv(ep);
+ lwp->lwp_sigaltstack.ss_sp = sp->ss_sp;
+ lwp->lwp_sigaltstack.ss_size = sp->ss_size;
+ lwp->lwp_sigaltstack.ss_flags = sp->ss_flags;
+}
+
+static uintptr_t
+lx_map_vdso(struct uarg *args, struct cred *cred)
+{
+ int err;
+ char *fpath = LX_VDSO_PATH;
+ vnode_t *vp;
+ vattr_t attr;
+ caddr_t addr;
+
+#if defined(_LP64)
+ if (args->to_model != DATAMODEL_NATIVE) {
+ fpath = LX_VDSO_PATH32;
+ }
+#endif
+
+ /*
+ * The comm page should have been mapped in already.
+ */
+ if (args->commpage == (uintptr_t)NULL) {
+ return ((uintptr_t)NULL);
+ }
+
+ /*
+ * Ensure the VDSO library is present and appropriately sized.
+ * This lookup is started at the zone root to avoid complications for
+ * processes which have chrooted. For the specified lookup root to be
+ * used, the leading slash must be dropped from the path.
+ */
+ ASSERT(fpath[0] == '/');
+ fpath++;
+ if (lookupnameat(fpath, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp,
+ curzone->zone_rootvp) != 0) {
+ return ((uintptr_t)NULL);
+ }
+
+ /*
+ * The VDSO requires data exposed via the comm page in order to
+ * function properly. The VDSO is always mapped in at a fixed known
+ * offset from the comm page, providing an easy means to locate it.
+ */
+ addr = (caddr_t)(args->commpage - LX_VDSO_SIZE);
+ attr.va_mask = AT_SIZE;
+ if (VOP_GETATTR(vp, &attr, 0, cred, NULL) != 0 ||
+ attr.va_size > LX_VDSO_SIZE) {
+ VN_RELE(vp);
+ return ((uintptr_t)NULL);
+ }
+
+ err = execmap(vp, addr, attr.va_size, 0, 0,
+ PROT_USER|PROT_READ|PROT_EXEC, 1, 0);
+ VN_RELE(vp);
+ if (err != 0) {
+ return ((uintptr_t)NULL);
+ }
+ return ((uintptr_t)addr);
+}
+
+/*
+ * Exec routine called by elfexec() to load either 32-bit or 64-bit Linux
+ * binaries.
+ */
+/* ARGSUSED4 */
+static int
+lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
+ struct intpdata *idata, int level, size_t *execsz, int setid,
+ caddr_t exec_file, struct cred *cred, int *brand_action)
+{
+ int error;
+ vnode_t *nvp;
+ Ehdr ehdr;
+ Addr uphdr_vaddr;
+ intptr_t voffset;
+ char *interp = NULL;
+ uintptr_t ldaddr = (uintptr_t)NULL;
+ proc_t *p = ttoproc(curthread);
+ klwp_t *lwp = ttolwp(curthread);
+ lx_proc_data_t *lxpd = ptolxproc(p);
+ struct execenv env, origenv;
+ stack_t orig_sigaltstack;
+ struct user *up = PTOU(ttoproc(curthread));
+ lx_elf_data_t edp;
+ char *lib_path = LX_LIB_PATH;
+ boolean_t execstk = B_TRUE;
+ unsigned int personality;
+
+ ASSERT(p->p_brand == &lx_brand);
+ ASSERT(lxpd != NULL);
+
+ /*
+ * Start with a separate struct for ELF data instead of inheriting
+ * values from the currently running binary. This ensures that fields
+ * such as ed_base are cleared if the new binary does not utilize an
+ * interpreter.
+ */
+ bzero(&edp, sizeof (edp));
+
+#if defined(_LP64)
+ if (args->to_model != DATAMODEL_NATIVE) {
+ lib_path = LX_LIB_PATH32;
+ }
+#endif
+
+ /*
+ * Set the brandname and library name for the new process so that
+ * elfexec() puts them onto the stack.
+ */
+ args->brandname = LX_BRANDNAME;
+ args->emulator = lib_path;
+
+#if defined(_LP64)
+ /*
+ * To conform with the way Linux lays out the address space, we clamp
+ * the stack to be the top of the lower region of the x86-64 canonical
+ * form address space -- which has the side-effect of laying out the
+ * entire address space in that lower region. Note that this only
+ * matters on 64-bit processes (this value will always be greater than
+ * the size of a 32-bit address space) and doesn't actually affect
+ * USERLIMIT: if a Linux-branded processes wishes to map something
+ * into the top half of the address space, it can do so -- but with
+ * the user stack starting at the top of the bottom region, those high
+ * virtual addresses won't be used unless explicitly directed.
+ */
+ args->maxstack = lx_maxstack64;
+#endif
+
+ /*
+ * Search the binary for a PT_GNU_STACK header. The PF_X bit contained
+ * within is used to dictate protection defaults for the stack, among
+ * other things.
+ */
+ if (args->to_model == DATAMODEL_NATIVE) {
+ Ehdr ehdr;
+ Phdr *phdrp;
+ caddr_t phdrbase = NULL;
+ size_t phdrsize = 0;
+ uint_t nphdrs, hsize;
+
+ if ((error = elfreadhdr(vp, cred, &ehdr, &nphdrs, &phdrbase,
+ &phdrsize)) != 0) {
+ return (error);
+ }
+
+ hsize = ehdr.e_phentsize;
+ /* LINTED: alignment */
+ phdrp = (Phdr *)phdrbase;
+ for (uint_t i = nphdrs; i > 0; i--) {
+ switch (phdrp->p_type) {
+ case PT_GNU_STACK:
+ if ((phdrp->p_flags & PF_X) == 0) {
+ execstk = B_FALSE;
+ }
+ break;
+ }
+ /* LINTED: alignment */
+ phdrp = (Phdr *)((caddr_t)phdrp + hsize);
+ }
+ kmem_free(phdrbase, phdrsize);
+ }
+#if defined(_LP64)
+ else {
+ Elf32_Ehdr ehdr;
+ Elf32_Phdr *phdrp;
+ caddr_t phdrbase = NULL;
+ size_t phdrsize = 0;
+ uint_t nphdrs, hsize;
+
+ if ((error = elf32readhdr(vp, cred, &ehdr, &nphdrs, &phdrbase,
+ &phdrsize)) != 0) {
+ return (error);
+ }
+
+ hsize = ehdr.e_phentsize;
+ /* LINTED: alignment */
+ phdrp = (Elf32_Phdr *)phdrbase;
+ for (uint_t i = nphdrs; i > 0; i--) {
+ switch (phdrp->p_type) {
+ case PT_GNU_STACK:
+ if ((phdrp->p_flags & PF_X) == 0) {
+ execstk = B_FALSE;
+ }
+ break;
+ }
+ /* LINTED: alignment */
+ phdrp = (Elf32_Phdr *)((caddr_t)phdrp + hsize);
+ }
+ kmem_free(phdrbase, phdrsize);
+ }
+#endif
+
+ /*
+ * Revert the base personality while maintaining any existing flags.
+ */
+ personality = LX_PER_LINUX | (lxpd->l_personality & ~LX_PER_MASK);
+
+ /*
+ * Linux defaults to an executable stack unless the aformentioned
+ * PT_GNU_STACK entry in the elf header dictates otherwise. Enabling
+ * the READ_IMPLIES_EXEC personality flag is also implied in this case.
+ */
+ if (execstk) {
+ args->stk_prot |= PROT_EXEC;
+ args->stk_prot_override = B_TRUE;
+ personality |= LX_PER_READ_IMPLIES_EXEC;
+ }
+
+ /*
+ * We will first exec the brand library, then map in the linux
+ * executable and the linux linker.
+ */
+ if ((error = lookupname(lib_path, UIO_SYSSPACE, FOLLOW, NULLVPP,
+ &nvp))) {
+ uprintf("%s: not found.", lib_path);
+ return (error);
+ }
+
+ /*
+ * We will eventually set the p_exec member to be the vnode for the new
+ * executable when we call setexecenv(). However, if we get an error
+ * before that call we need to restore the execenv to its original
+ * values so that when we return to the caller fop_close() works
+ * properly while cleaning up from the failed exec(). Restoring the
+ * original value will also properly decrement the 2nd VN_RELE that we
+ * took on the brand library.
+ */
+ origenv.ex_bssbase = p->p_bssbase;
+ origenv.ex_brkbase = p->p_brkbase;
+ origenv.ex_brksize = p->p_brksize;
+ origenv.ex_vp = p->p_exec;
+ orig_sigaltstack.ss_sp = lwp->lwp_sigaltstack.ss_sp;
+ orig_sigaltstack.ss_size = lwp->lwp_sigaltstack.ss_size;
+ orig_sigaltstack.ss_flags = lwp->lwp_sigaltstack.ss_flags;
+
+ if (args->to_model == DATAMODEL_NATIVE) {
+ error = elfexec(nvp, uap, args, idata, INTP_MAXDEPTH + 1,
+ execsz, setid, exec_file, cred, brand_action);
+ }
+#if defined(_LP64)
+ else {
+ error = elf32exec(nvp, uap, args, idata, INTP_MAXDEPTH + 1,
+ execsz, setid, exec_file, cred, brand_action);
+ }
+#endif
+ VN_RELE(nvp);
+ if (error != 0) {
+ restoreexecenv(&origenv, &orig_sigaltstack);
+ return (error);
+ }
+
+ /*
+ * exec-ed in the brand library above.
+ * The u_auxv vectors are now setup by elfexec to point to the
+ * brand emulation library and its linker.
+ */
+
+ /*
+ * After execing the brand library (which should have implicitly mapped
+ * in the comm page), map the VDSO into the approprate place in the AS.
+ */
+ lxpd->l_vdso = lx_map_vdso(args, cred);
+
+ bzero(&env, sizeof (env));
+
+ /*
+ * map in the the Linux executable
+ */
+ if (args->to_model == DATAMODEL_NATIVE) {
+ error = mapexec_brand(vp, args, &ehdr, &uphdr_vaddr,
+ &voffset, exec_file, &interp, &env.ex_bssbase,
+ &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
+ }
+#if defined(_LP64)
+ else {
+ Elf32_Ehdr ehdr32;
+ Elf32_Addr uphdr_vaddr32;
+
+ error = mapexec32_brand(vp, args, &ehdr32, &uphdr_vaddr32,
+ &voffset, exec_file, &interp, &env.ex_bssbase,
+ &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
+
+ Ehdr32to64(&ehdr32, &ehdr);
+
+ if (uphdr_vaddr32 == (Elf32_Addr)-1)
+ uphdr_vaddr = (Addr)-1;
+ else
+ uphdr_vaddr = uphdr_vaddr32;
+ }
+#endif
+ if (error != 0) {
+ restoreexecenv(&origenv, &orig_sigaltstack);
+
+ if (interp != NULL)
+ kmem_free(interp, MAXPATHLEN);
+
+ return (error);
+ }
+
+ /*
+ * Save off the important properties of the lx executable. The brand
+ * library will ask us for this data later, when it is ready to set
+ * things up for the lx executable.
+ */
+ edp.ed_phdr = (uphdr_vaddr == -1) ? voffset + ehdr.e_phoff :
+ voffset + uphdr_vaddr;
+ edp.ed_entry = voffset + ehdr.e_entry;
+ edp.ed_phent = ehdr.e_phentsize;
+ edp.ed_phnum = ehdr.e_phnum;
+
+ if (interp != NULL) {
+ if (ehdr.e_type == ET_DYN) {
+ /*
+ * This is a shared object executable, so we need to
+ * pick a reasonable place to put the heap. Just don't
+ * use the first page.
+ */
+ env.ex_brkbase = (caddr_t)PAGESIZE;
+ env.ex_bssbase = (caddr_t)PAGESIZE;
+ }
+
+ /*
+ * If the program needs an interpreter (most do), map it in and
+ * store relevant information about it in the aux vector, where
+ * the brand library can find it.
+ */
+ if ((error = lookupname(interp, UIO_SYSSPACE, FOLLOW,
+ NULLVPP, &nvp))) {
+ uprintf("%s: not found.", interp);
+ restoreexecenv(&origenv, &orig_sigaltstack);
+ kmem_free(interp, MAXPATHLEN);
+ return (error);
+ }
+
+ kmem_free(interp, MAXPATHLEN);
+ interp = NULL;
+
+ /*
+ * map in the Linux linker
+ */
+ if (args->to_model == DATAMODEL_NATIVE) {
+ error = mapexec_brand(nvp, args, &ehdr,
+ &uphdr_vaddr, &voffset, exec_file, NULL, NULL,
+ NULL, NULL, NULL, &ldaddr);
+ }
+#if defined(_LP64)
+ else {
+ Elf32_Ehdr ehdr32;
+ Elf32_Addr uphdr_vaddr32;
+
+ error = mapexec32_brand(nvp, args, &ehdr32,
+ &uphdr_vaddr32, &voffset, exec_file, NULL, NULL,
+ NULL, NULL, NULL, &ldaddr);
+
+ Ehdr32to64(&ehdr32, &ehdr);
+
+ if (uphdr_vaddr32 == (Elf32_Addr)-1)
+ uphdr_vaddr = (Addr)-1;
+ else
+ uphdr_vaddr = uphdr_vaddr32;
+ }
+#endif
+
+ VN_RELE(nvp);
+ if (error != 0) {
+ restoreexecenv(&origenv, &orig_sigaltstack);
+ return (error);
+ }
+
+ /*
+ * Now that we know the base address of the brand's linker,
+ * we also save this for later use by the brand library.
+ */
+ edp.ed_base = voffset;
+ edp.ed_ldentry = voffset + ehdr.e_entry;
+ } else {
+ /*
+ * This program has no interpreter. The lx brand library will
+ * jump to the address in the AT_SUN_BRAND_LDENTRY aux vector,
+ * so in this case, put the entry point of the main executable
+ * there.
+ */
+ if (ehdr.e_type == ET_EXEC) {
+ /*
+ * An executable with no interpreter, this must be a
+ * statically linked executable, which means we loaded
+ * it at the address specified in the elf header, in
+ * which case the e_entry field of the elf header is an
+ * absolute address.
+ */
+ edp.ed_ldentry = ehdr.e_entry;
+ edp.ed_entry = ehdr.e_entry;
+ } else {
+ /*
+ * A shared object with no interpreter, we use the
+ * calculated address from above.
+ */
+ edp.ed_ldentry = edp.ed_entry;
+
+ /*
+ * In all situations except an ET_DYN elf object with no
+ * interpreter, we want to leave the brk and base
+ * values set by mapexec_brand alone. Normally when
+ * running ET_DYN objects on Solaris (most likely
+ * /lib/ld.so.1) the kernel sets brk and base to 0 since
+ * it doesn't know where to put the heap, and later the
+ * linker will call brk() to initialize the heap in:
+ * usr/src/cmd/sgs/rtld/common/setup.c:setup()
+ * after it has determined where to put it. (This
+ * decision is made after the linker loads and inspects
+ * elf properties of the target executable being run.)
+ *
+ * The Linux linker does not do this, though, and
+ * doesn't understand the semantics that we give the
+ * native linker (namely, that the first non-zero arg
+ * call to brk() will *set* the brkbase but leave
+ * brksize at 0 -- Linux binaries instead expect that
+ * this would extend the brk from 0 upto that arg).
+ *
+ * So we should never leave here with ex_brkbase == 0
+ * or else we will get segfaults as Linux binaries
+ * misinterpret what we return from brk().
+ *
+ * It's probably not great, but we'll just set brkbase
+ * to PAGESIZE. If there's something down there in the
+ * way then libc/malloc should fall back to mmap() when
+ * we fail to extend the brk for them.
+ */
+ if (ehdr.e_type == ET_DYN) {
+ env.ex_bssbase = (caddr_t)PAGESIZE;
+ env.ex_brkbase = (caddr_t)PAGESIZE;
+ env.ex_brksize = 0;
+ }
+ }
+ }
+
+ /*
+ * Never leave this code with a 0 brkbase, or else we expose the
+ * native linker semantics of initial brk() to Linux binaries which
+ * will misinterpret them.
+ */
+ ASSERT3U(env.ex_brkbase, !=, (caddr_t)0);
+
+ env.ex_vp = vp;
+ setexecenv(&env);
+
+ /*
+ * We try to keep /proc's view of the aux vector consistent with
+ * what's on the process stack. See the comment on the lx_times
+ * syscall for an explanation of the hardcoded LX_USERHZ.
+ */
+ if (args->to_model == DATAMODEL_NATIVE) {
+ auxv_t phdr_auxv[4] = {
+ { AT_SUN_BRAND_LX_PHDR, 0 },
+ { AT_SUN_BRAND_LX_INTERP, 0 },
+ { AT_SUN_BRAND_LX_CLKTCK, 0 },
+ { AT_SUN_BRAND_LX_SYSINFO_EHDR, 0 }
+ };
+ phdr_auxv[0].a_un.a_val = edp.ed_phdr;
+ phdr_auxv[1].a_un.a_val = ldaddr;
+ phdr_auxv[2].a_un.a_val = LX_USERHZ;
+ phdr_auxv[3].a_un.a_val = lxpd->l_vdso;
+
+ if (copyout(&phdr_auxv, args->auxp_brand,
+ sizeof (phdr_auxv)) == -1)
+ return (EFAULT);
+ }
+#if defined(_LP64)
+ else {
+ auxv32_t phdr_auxv32[4] = {
+ { AT_SUN_BRAND_LX_PHDR, 0 },
+ { AT_SUN_BRAND_LX_INTERP, 0 },
+ { AT_SUN_BRAND_LX_CLKTCK, 0 },
+ { AT_SUN_BRAND_LX_SYSINFO_EHDR, 0 }
+ };
+ phdr_auxv32[0].a_un.a_val = edp.ed_phdr;
+ phdr_auxv32[1].a_un.a_val = ldaddr;
+ phdr_auxv32[2].a_un.a_val = hz;
+ phdr_auxv32[3].a_un.a_val = lxpd->l_vdso;
+
+ if (copyout(&phdr_auxv32, args->auxp_brand,
+ sizeof (phdr_auxv32)) == -1)
+ return (EFAULT);
+ }
+#endif
+
+ /*
+ * /proc uses the AT_ENTRY aux vector entry to deduce
+ * the location of the executable in the address space. The user
+ * structure contains a copy of the aux vector that needs to have those
+ * entries patched with the values of the real lx executable (they
+ * currently contain the values from the lx brand library that was
+ * elfexec'd, above).
+ *
+ * For live processes, AT_BASE is used to locate the linker segment,
+ * which /proc and friends will later use to find Solaris symbols
+ * (such as rtld_db_preinit). However, for core files, /proc uses
+ * AT_ENTRY to find the right segment to label as the executable.
+ * So we set AT_ENTRY to be the entry point of the linux executable,
+ * but leave AT_BASE to be the address of the Solaris linker.
+ */
+ for (uint_t i = 0; i < __KERN_NAUXV_IMPL; i++) {
+ switch (up->u_auxv[i].a_type) {
+ case AT_ENTRY:
+ up->u_auxv[i].a_un.a_val = edp.ed_entry;
+ break;
+
+ case AT_SUN_BRAND_LX_PHDR:
+ up->u_auxv[i].a_un.a_val = edp.ed_phdr;
+ break;
+
+ case AT_SUN_BRAND_LX_INTERP:
+ up->u_auxv[i].a_un.a_val = ldaddr;
+ break;
+
+ case AT_SUN_BRAND_LX_CLKTCK:
+ up->u_auxv[i].a_un.a_val = hz;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Record the brand ELF data and new personality now that the exec has
+ * proceeded successfully.
+ */
+ bcopy(&edp, &lxpd->l_elf_data, sizeof (edp));
+ lxpd->l_personality = personality;
+
+ return (0);
+}
+
+boolean_t
+lx_native_exec(uint8_t osabi, const char **interp)
+{
+ if (osabi != ELFOSABI_SOLARIS)
+ return (B_FALSE);
+
+ /*
+ * If the process root matches the zone root, prepend /native to the
+ * interpreter path for native executables. Absolute precision from
+ * VN_CMP is not necessary since any change of process root is likely
+ * to make native binaries inaccessible via /native.
+ *
+ * Processes which chroot directly into /native will be able to
+ * function as expected with no need for the prefix.
+ */
+ mutex_enter(&curproc->p_lock);
+ if (VN_CMP(curproc->p_user.u_rdir, curproc->p_zone->zone_rootvp)) {
+ *interp = "/native";
+ }
+ mutex_exit(&curproc->p_lock);
+
+ return (B_TRUE);
+}
+
+static void
+lx_syscall_init(void)
+{
+ int i;
+
+ /*
+ * Count up the 32-bit Linux system calls. Note that lx_sysent32
+ * has (LX_NSYSCALLS + 1) entries.
+ */
+ for (i = 0; i <= LX_NSYSCALLS && lx_sysent32[i].sy_name != NULL; i++)
+ continue;
+ lx_nsysent32 = i;
+
+#if defined(_LP64)
+ /*
+ * Count up the 64-bit Linux system calls. Note that lx_sysent64
+ * has (LX_NSYSCALLS + 1) entries.
+ */
+ for (i = 0; i <= LX_NSYSCALLS && lx_sysent64[i].sy_name != NULL; i++)
+ continue;
+ lx_nsysent64 = i;
+#endif
+}
+
+int
+_init(void)
+{
+ int err = 0;
+
+ /* Initialize USER_HZ scaling factor */
+ ASSERT(hz >= LX_USERHZ);
+ lx_hz_scale = hz / LX_USERHZ;
+
+ lx_syscall_init();
+ lx_pid_init();
+ lx_ioctl_init();
+ lx_futex_init();
+ lx_ptrace_init();
+ lx_socket_init();
+ lx_audit_ld();
+
+ err = mod_install(&modlinkage);
+ if (err != 0) {
+ cmn_err(CE_WARN, "Couldn't install lx brand module");
+
+ /*
+ * This looks drastic, but it should never happen. These
+ * two data structures should be completely free-able until
+ * they are used by Linux processes. Since the brand
+ * wasn't loaded there should be no Linux processes, and
+ * thus no way for these data structures to be modified.
+ */
+ lx_pid_fini();
+ lx_ioctl_fini();
+ if (lx_futex_fini())
+ panic("lx brand module cannot be loaded or unloaded.");
+ }
+ return (err);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int err;
+ int futex_done = 0;
+
+ /*
+ * If there are any zones using this brand, we can't allow it to be
+ * unloaded.
+ */
+ if (brand_zone_count(&lx_brand))
+ return (EBUSY);
+
+ lx_ptrace_fini();
+ lx_pid_fini();
+ lx_ioctl_fini();
+ lx_socket_fini();
+ lx_audit_unld();
+
+ if ((err = lx_futex_fini()) != 0) {
+ goto done;
+ }
+ futex_done = 1;
+
+ err = mod_remove(&modlinkage);
+
+done:
+ if (err) {
+ /*
+ * If we can't unload the module, then we have to get it
+ * back into a sane state.
+ */
+ lx_ptrace_init();
+ lx_pid_init();
+ lx_ioctl_init();
+ lx_socket_init();
+
+ if (futex_done) {
+ lx_futex_init();
+ }
+ }
+
+ return (err);
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_lockd.c b/usr/src/uts/common/brand/lx/os/lx_lockd.c
new file mode 100644
index 0000000000..37b744b0e8
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_lockd.c
@@ -0,0 +1,351 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+/*
+ * lx_start_nfs_lockd() starts an NFS lockd (lx_lockd) process inside the zone.
+ * This uses the same technique as used in our lx cgroupfs to launch a release
+ * agent process. This is called implicitly when an NFS mount syscall occurs
+ * within the zone. See the user-level lx_lockd source for the "big theory"
+ * comment behind this.
+ *
+ * lx_upcall_statd() is a brand hook that interposes on the rpc.statd RPC
+ * handling so that we can interface to a Linux rpc.statd that must run
+ * when NFSv3 locking is in use. The rpc.statd handles server or client reboots
+ * and interacts with the lockd to reclaim locks after the server reboots. The
+ * rcp.statd also informs the server when we reboot, so the server can release
+ * the locks we held.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/errno.h>
+#include <sys/cred.h>
+#include <sys/systm.h>
+#include <sys/policy.h>
+#include <sys/vmparam.h>
+#include <sys/contract_impl.h>
+#include <sys/pool.h>
+#include <sys/stack.h>
+#include <sys/var.h>
+#include <sys/rt.h>
+#include <sys/fx.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/pathname.h>
+#include <rpcsvc/nlm_prot.h>
+#include <rpcsvc/sm_inter.h>
+#include <klm/nlm_impl.h>
+
+#define LX_LOCKD_PATH "/native/usr/lib/brand/lx/lx_lockd"
+
+/* Linux lockd RPC called by statd when it detects an NFS server reboot */
+#define LX_NLMPROC_NSM_NOTIFY 16
+
+/* From uts/common/klm/nlm_impl.c */
+extern void nlm_netbuf_to_netobj(struct netbuf *, int *, netobj *);
+extern void nlm_nsm_clnt_init(CLIENT *, struct nlm_nsm *);
+
+/*
+ * Check if the current lockd is still running.
+ */
+static boolean_t
+lx_lockd_alive(pid_t lockd_pid)
+{
+ boolean_t ret = B_FALSE;
+ proc_t *p;
+ vnode_t *vp;
+ char path[MAXPATHLEN];
+
+ mutex_enter(&pidlock);
+ p = prfind(lockd_pid);
+ if (p == NULL) {
+ mutex_exit(&pidlock);
+ return (B_FALSE);
+ }
+
+ mutex_enter(&p->p_lock);
+ if (p->p_stat == SZOMB || (p->p_flag & SEXITING) != 0) {
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+ return (B_FALSE);
+ }
+ vp = p->p_exec;
+ VN_HOLD(vp);
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+
+ if (vnodetopath(NULL, vp, path, sizeof (path), CRED()) == 0 &&
+ strcmp(path, LX_LOCKD_PATH) == 0) {
+ ret = B_TRUE;
+ }
+
+ VN_RELE(vp);
+ return (ret);
+}
+
+static void
+lx_run_lockd(void *a)
+{
+ proc_t *p = curproc;
+ zone_t *z = curzone;
+ struct core_globals *cg;
+ lx_zone_data_t *lxzd = ztolxzd(z);
+ int res;
+
+ ASSERT(!INGLOBALZONE(p));
+ VERIFY(lxzd != NULL);
+
+ /* The following block is derived from start_init_common */
+ ASSERT_STACK_ALIGNED();
+
+ p->p_cstime = p->p_stime = p->p_cutime = p->p_utime = 0;
+ p->p_usrstack = (caddr_t)USRSTACK32;
+ p->p_model = DATAMODEL_ILP32;
+ p->p_stkprot = PROT_ZFOD & ~PROT_EXEC;
+ p->p_datprot = PROT_ZFOD & ~PROT_EXEC;
+ p->p_stk_ctl = INT32_MAX;
+
+ p->p_as = as_alloc();
+ p->p_as->a_proc = p;
+ p->p_as->a_userlimit = (caddr_t)USERLIMIT32;
+ (void) hat_setup(p->p_as->a_hat, HAT_INIT);
+
+ VERIFY((cg = zone_getspecific(core_zone_key, z)) != NULL);
+
+ corectl_path_hold(cg->core_default_path);
+ corectl_content_hold(cg->core_default_content);
+
+ p->p_corefile = cg->core_default_path;
+ p->p_content = cg->core_default_content;
+
+ init_mstate(curthread, LMS_SYSTEM);
+ res = exec_init(LX_LOCKD_PATH, NULL);
+
+ /* End of code derived from start_init_common */
+
+ /* The following is derived from zone_start_init - see comments there */
+ if (res != 0 || zone_status_get(global_zone) >= ZONE_IS_SHUTTING_DOWN) {
+ if (proc_exit(CLD_EXITED, res) != 0) {
+ mutex_enter(&p->p_lock);
+ ASSERT(p->p_flag & SEXITLWPS);
+ lwp_exit();
+ }
+ } else {
+ id_t cid = curthread->t_cid;
+
+ mutex_enter(&class_lock);
+ ASSERT(cid < loaded_classes);
+ if (strcmp(sclass[cid].cl_name, "FX") == 0 &&
+ z->zone_fixed_hipri) {
+ pcparms_t pcparms;
+
+ pcparms.pc_cid = cid;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_upri = FXMAXUPRI;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_uprilim =
+ FXMAXUPRI;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_cflags =
+ FX_DOUPRILIM | FX_DOUPRI;
+
+ mutex_enter(&pidlock);
+ mutex_enter(&p->p_lock);
+ (void) parmsset(&pcparms, curthread);
+ mutex_exit(&p->p_lock);
+ mutex_exit(&pidlock);
+ } else if (strcmp(sclass[cid].cl_name, "RT") == 0) {
+ curthread->t_pri = RTGPPRIO0;
+ }
+ mutex_exit(&class_lock);
+
+ /*
+ * Set our pid as the lockd pid in the zone data, or exit
+ * if another process raced and already did so.
+ */
+ mutex_enter(&lxzd->lxzd_lock);
+ if (lxzd->lxzd_lockd_pid != 0) {
+ /* another mount raced and created a new lockd */
+ mutex_exit(&lxzd->lxzd_lock);
+ if (proc_exit(CLD_EXITED, 0) != 0) {
+ mutex_enter(&p->p_lock);
+ ASSERT(p->p_flag & SEXITLWPS);
+ lwp_exit();
+ }
+ return;
+ }
+ lxzd->lxzd_lockd_pid = p->p_pid;
+ mutex_exit(&lxzd->lxzd_lock);
+
+ /* cause the process to return to userland. */
+ lwp_rtt();
+ }
+}
+
+/*
+ * Launch the user-level, native, lx_lockd process.
+ */
+int
+lx_start_nfs_lockd()
+{
+ id_t cid;
+ proc_t *p = ttoproc(curthread);
+ zone_t *z = p->p_zone;
+ lx_zone_data_t *lxzd = ztolxzd(z);
+
+ ASSERT(!INGLOBALZONE(p));
+ ASSERT(lxzd != NULL);
+
+ /*
+ * This should only be called by the mount emulation, which must have
+ * 'root' privileges in order to have performed a mount, but
+ * double-check.
+ */
+ if (crgetuid(CRED()) != 0)
+ return (EPERM);
+
+ mutex_enter(&lxzd->lxzd_lock);
+ if (lxzd->lxzd_lockd_pid != 0) {
+ /* verify lockd is still alive */
+ pid_t lockd_pid;
+
+ lockd_pid = lxzd->lxzd_lockd_pid;
+ mutex_exit(&lxzd->lxzd_lock);
+
+ if (lx_lockd_alive(lockd_pid))
+ return (EEXIST);
+
+ mutex_enter(&lxzd->lxzd_lock);
+ if (lxzd->lxzd_lockd_pid != lockd_pid) {
+ /* another mount raced and created a new lockd */
+ mutex_exit(&lxzd->lxzd_lock);
+ return (EEXIST);
+ }
+
+ /* old lockd is dead, launch a new one */
+ lxzd->lxzd_lockd_pid = 0;
+ }
+ mutex_exit(&lxzd->lxzd_lock);
+
+ if (z->zone_defaultcid > 0) {
+ cid = z->zone_defaultcid;
+ } else {
+ pool_lock();
+ cid = pool_get_class(z->zone_pool);
+ pool_unlock();
+ }
+ if (cid == -1)
+ cid = defaultcid;
+
+ /*
+ * There's nothing to do here if creating the proc fails, but we
+ * return the result to make it obvious while DTracing.
+ */
+ return (newproc(lx_run_lockd, NULL, cid, minclsyspri - 1, NULL, -1));
+}
+
+void
+lx_upcall_statd(int op, struct nlm_globals *g, struct nlm_host *host)
+{
+ struct nlm_nsm *nsm;
+ struct mon args;
+ struct mon_id *mip = &args.mon_id;
+ int family;
+ netobj obj;
+ enum clnt_stat stat;
+
+ /*
+ * For Linux rpc.statd monitor registration, the Linux NSMPROC_MON and
+ * NSMPROC_UNMON RPC upcalls correspond almost directly to the native
+ * SM_MON and SM_UNMON RPC upcalls. The key differences with the native
+ * registration is that in our nlm_host_monitor function we make two
+ * RPC calls:
+ * - the first RPC (nsmaddrproc1_reg_1) uses our private 'nsm_addr'
+ * RPC protocol to register the lockd RPC information that statd
+ * should call when it detects that the remote server rebooted
+ * - the second RPC (sm_mon_1) tells statd the information about the
+ * remote server to be monitored
+ * For Linux, there is only a single RPC from the kernel to the local
+ * statd. This RPC is equivalent to our sm_mon_1 code, but it uses the
+ * Linux-private NLMPROC_NSM_NOTIFY lockd procedure in the 'my_proc'
+ * RPC parameter. This corresponds to our private 'nsm_addr' code, and
+ * tells statd which lockd RPC to call when it detects a server reboot.
+ *
+ * Because our sm_mon_1 RPC is so similar to the Linux RPC, we can use
+ * that directly and simply set the expected value in the 'my_proc'
+ * argument.
+ *
+ * Within the kernel lockd RPC handling, the nlm_prog_3_dtable dispatch
+ * table has an entry for each lockd RPC function. Thus, this table also
+ * contains an entry for the Linux NLMPROC_NSM_NOTIFY procedure. That
+ * procedure number is unused by the native lockd code, so there is no
+ * conflict with dispatching that procedure. The implementation of the
+ * procedure corresponds to the native, private NLM_SM_NOTIFY1
+ * procedure which is called by the native rpc.statd.
+ *
+ * The Linux RPC call to "unmonitor" a host expects the same arguments
+ * as we pass to monitor, so that is also handled here by this same
+ * brand hook.
+ */
+
+ /*
+ * If the NLM was set up to be "v4 only" (i.e. no RPC call handlers
+ * to localhost at configure time), the semaphore is uninitialized,
+ * and will indefinitely hang. FURTHERMORE if just the semaphore
+ * was initialized, we'd still panic with a NULL nsm->ns_handle.
+ */
+ if (g->nlm_v4_only) {
+ stat = RPC_SYSTEMERROR;
+ goto bail;
+ }
+
+ nlm_netbuf_to_netobj(&host->nh_addr, &family, &obj);
+ nsm = &g->nlm_nsm;
+
+ bzero(&args, sizeof (args));
+
+ mip->mon_name = host->nh_name;
+ mip->my_id.my_name = uts_nodename();
+ mip->my_id.my_prog = NLM_PROG;
+ mip->my_id.my_vers = NLM_SM;
+ mip->my_id.my_proc = LX_NLMPROC_NSM_NOTIFY;
+ if (op == SM_MON) {
+ bcopy(&host->nh_sysid, args.priv, sizeof (uint16_t));
+ }
+
+ sema_p(&nsm->ns_sem);
+ nlm_nsm_clnt_init(nsm->ns_handle, nsm);
+ if (op == SM_MON) {
+ struct sm_stat_res mres;
+
+ bzero(&mres, sizeof (mres));
+ stat = sm_mon_1(&args, &mres, nsm->ns_handle);
+ } else {
+ struct sm_stat ures;
+
+ ASSERT(op == SM_UNMON);
+ bzero(&ures, sizeof (ures));
+ stat = sm_unmon_1(mip, &ures, nsm->ns_handle);
+ }
+ sema_v(&nsm->ns_sem);
+
+bail:
+ if (stat != RPC_SUCCESS) {
+ NLM_WARN("Failed to contact local statd, stat=%d", stat);
+ if (op == SM_MON) {
+ mutex_enter(&g->lock);
+ host->nh_flags &= ~NLM_NH_MONITORED;
+ mutex_exit(&g->lock);
+ }
+ }
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_misc.c b/usr/src/uts/common/brand/lx/os/lx_misc.c
new file mode 100644
index 0000000000..a97e025200
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_misc.c
@@ -0,0 +1,1136 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2022 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/archsystm.h>
+#include <sys/privregs.h>
+#include <sys/exec.h>
+#include <sys/lwp.h>
+#include <sys/sem.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_siginfo.h>
+#include <sys/lx_futex.h>
+#include <lx_errno.h>
+#include <sys/lx_userhz.h>
+#include <sys/cmn_err.h>
+#include <sys/siginfo.h>
+#include <sys/contract/process_impl.h>
+#include <sys/x86_archext.h>
+#include <sys/sdt.h>
+#include <lx_signum.h>
+#include <lx_syscall.h>
+#include <sys/proc.h>
+#include <sys/procfs.h>
+#include <net/if.h>
+#include <inet/ip6.h>
+#include <sys/sunddi.h>
+#include <sys/dlpi.h>
+#include <sys/sysmacros.h>
+
+/* Linux specific functions and definitions */
+static void lx_save(void *);
+static void lx_restore(void *);
+
+/* Context op template. */
+static const struct ctxop_template lx_ctxop_template = {
+ .ct_rev = CTXOP_TPL_REV,
+ .ct_save = lx_save,
+ .ct_restore = lx_restore,
+ .ct_exit = lx_save,
+};
+
+/*
+ * Set the return code for the forked child, always zero
+ */
+/*ARGSUSED*/
+void
+lx_setrval(klwp_t *lwp, int v1, int v2)
+{
+ lwptoregs(lwp)->r_r0 = 0;
+}
+
+/*
+ * Reset process state on exec(2)
+ */
+void
+lx_exec()
+{
+ klwp_t *lwp = ttolwp(curthread);
+ struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+ proc_t *p = ttoproc(curthread);
+ lx_proc_data_t *pd = ptolxproc(p);
+ struct regs *rp = lwptoregs(lwp);
+
+ /* b_exec is called without p_lock held */
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ /*
+ * Any l_handler handlers set as a result of B_REGISTER are now
+ * invalid; clear them.
+ */
+ pd->l_handler = (uintptr_t)NULL;
+
+ /*
+ * If this was a multi-threaded Linux process and this lwp wasn't the
+ * main lwp, then we need to make its Illumos and Linux PIDs match.
+ */
+ if (curthread->t_tid != 1) {
+ lx_pid_reassign(curthread);
+ }
+
+ /*
+ * Inform ptrace(2) that we are processing an execve(2) call so that if
+ * we are traced we can post either the PTRACE_EVENT_EXEC event or the
+ * legacy SIGTRAP.
+ */
+ (void) lx_ptrace_stop_for_option(LX_PTRACE_O_TRACEEXEC, B_FALSE, 0, 0);
+
+ /* clear the fs/gsbase values until the app. can reinitialize them */
+ lwpd->br_lx_fsbase = (uintptr_t)NULL;
+ lwpd->br_ntv_fsbase = (uintptr_t)NULL;
+ lwpd->br_lx_gsbase = (uintptr_t)NULL;
+ lwpd->br_ntv_gsbase = (uintptr_t)NULL;
+
+ /*
+ * Clear the native stack flags. This will be reinitialised by
+ * lx_init() in the new process image.
+ */
+ lwpd->br_stack_mode = LX_STACK_MODE_PREINIT;
+ lwpd->br_ntv_stack = 0;
+ lwpd->br_ntv_stack_current = 0;
+
+ ctxop_install(lwptot(lwp), &lx_ctxop_template, lwp);
+
+ /*
+ * clear out the tls array
+ */
+ bzero(lwpd->br_tls, sizeof (lwpd->br_tls));
+
+ /*
+ * reset the tls entries in the gdt
+ */
+ kpreempt_disable();
+ lx_restore(lwp);
+ kpreempt_enable();
+
+ /*
+ * The exec syscall doesn't return (so we don't call lx_syscall_return)
+ * but for our ptrace emulation we need to do this so that a tracer
+ * does not get out of sync. We know that by the time this lx_exec
+ * function is called that the exec has succeeded.
+ */
+ rp->r_r0 = 0;
+ (void) lx_ptrace_stop(LX_PR_SYSEXIT);
+}
+
+static void
+lx_cleanlwp(klwp_t *lwp, proc_t *p)
+{
+ struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+ void *rb_list = NULL;
+
+ VERIFY(lwpd != NULL);
+
+ mutex_enter(&p->p_lock);
+ if ((lwpd->br_ptrace_flags & LX_PTF_EXITING) == 0) {
+ lx_ptrace_exit(p, lwp);
+ }
+
+ /*
+ * While we have p_lock, clear the TP_KTHREAD flag. This is needed
+ * to prevent races within lx procfs. It's fine for prchoose() to pick
+ * this thread now since it is exiting and no longer blocked in the
+ * kernel.
+ */
+ lwptot(lwp)->t_proc_flag &= ~TP_KTHREAD;
+
+ /*
+ * While we have p_lock, safely grab any robust_list references and
+ * clear the lwp field.
+ */
+ sprlock_proc(p);
+ rb_list = lwpd->br_robust_list;
+ lwpd->br_robust_list = NULL;
+ sprunlock(p);
+
+ if (rb_list != NULL) {
+ lx_futex_robust_exit((uintptr_t)rb_list, lwpd->br_pid);
+ }
+
+ /*
+ * We need to run our context exit operation (lx_save) here to ensure
+ * we don't leave any garbage around. This is necessary to handle the
+ * following calling sequence:
+ * exit -> proc_exit -> lx_freelwp -> removectx
+ * That is, when our branded process exits, proc_exit will call our
+ * lx_freelwp brand hook which does call this function (lx_cleanlwp),
+ * but lx_freelwp also removes our context exit operation. The context
+ * exit functions are run by exitctx, which is called by either
+ * lwp_exit or thread_exit. The thread_exit function is called at the
+ * end of proc_exit when we'll swtch() to another thread, but by then
+ * our context exit function has been removed.
+ *
+ * It's ok if this function happens to be called more than once (for
+ * example, if we exec a native binary).
+ */
+ kpreempt_disable();
+ lx_save(lwp);
+ kpreempt_enable();
+}
+
+void
+lx_exitlwp(klwp_t *lwp)
+{
+ struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+ proc_t *p = lwptoproc(lwp);
+ kthread_t *t;
+ sigqueue_t *sqp = NULL;
+ pid_t ppid;
+ id_t ptid;
+
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ if (lwpd == NULL) {
+ /* second time thru' */
+ return;
+ }
+
+ lx_cleanlwp(lwp, p);
+
+ if (lwpd->br_clear_ctidp != NULL) {
+ (void) suword32(lwpd->br_clear_ctidp, 0);
+ (void) lx_futex((uintptr_t)lwpd->br_clear_ctidp, FUTEX_WAKE, 1,
+ (uintptr_t)NULL, (uintptr_t)NULL, 0);
+ lwpd->br_clear_ctidp = NULL;
+ }
+
+ if (lwpd->br_signal != 0) {
+ /*
+ * The first thread in a process doesn't cause a signal to
+ * be sent when it exits. It was created by a fork(), not
+ * a clone(), so the parent should get signalled when the
+ * process exits.
+ */
+ if (lwpd->br_ptid == -1)
+ goto free;
+
+ sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+ /*
+ * If br_ppid is 0, it means this is a CLONE_PARENT thread,
+ * so the signal goes to the parent process - not to a
+ * specific thread in this process.
+ */
+ p = lwptoproc(lwp);
+ if (lwpd->br_ppid == 0) {
+ mutex_enter(&p->p_lock);
+ ppid = p->p_ppid;
+ t = NULL;
+ } else {
+ /*
+ * If we have been reparented to init or if our
+ * parent thread is gone, then nobody gets
+ * signaled.
+ */
+ if ((lx_lwp_ppid(lwp, &ppid, &ptid) == 1) ||
+ (ptid == -1))
+ goto free;
+
+ mutex_enter(&pidlock);
+ if ((p = prfind(ppid)) == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ goto free;
+ }
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+
+ if ((t = idtot(p, ptid)) == NULL) {
+ mutex_exit(&p->p_lock);
+ goto free;
+ }
+ }
+
+ sqp->sq_info.si_signo = lwpd->br_signal;
+ sqp->sq_info.si_code = lwpd->br_exitwhy;
+ sqp->sq_info.si_status = lwpd->br_exitwhat;
+ sqp->sq_info.si_pid = lwpd->br_pid;
+ sqp->sq_info.si_uid = crgetruid(CRED());
+ sigaddqa(p, t, sqp);
+ mutex_exit(&p->p_lock);
+ sqp = NULL;
+ }
+
+free:
+ if (lwpd->br_scall_args != NULL) {
+ ASSERT(lwpd->br_args_size > 0);
+ kmem_free(lwpd->br_scall_args, lwpd->br_args_size);
+ }
+ if (sqp)
+ kmem_free(sqp, sizeof (sigqueue_t));
+}
+
+void
+lx_freelwp(klwp_t *lwp)
+{
+ struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+ proc_t *p = lwptoproc(lwp);
+ lx_zone_data_t *lxzdata;
+ vfs_t *cgrp;
+
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ if (lwpd == NULL) {
+ /*
+ * There is one case where an LX branded process will possess
+ * LWPs which lack their own brand data. During the course of
+ * executing native binary, the process will be preemptively
+ * branded to allow hooks such as b_native_exec to function.
+ * If that process possesses multiple LWPS, they will _not_ be
+ * branded since they will exit if the exec succeeds. It's
+ * during this LWP exit that lx_freelwp would be called on an
+ * unbranded LWP. When that is the case, it is acceptable to
+ * bypass the hook.
+ */
+ return;
+ }
+
+ /* cgroup integration */
+ lxzdata = ztolxzd(p->p_zone);
+ mutex_enter(&lxzdata->lxzd_lock);
+ cgrp = lxzdata->lxzd_cgroup;
+ if (cgrp != NULL) {
+ VFS_HOLD(cgrp);
+ mutex_exit(&lxzdata->lxzd_lock);
+ ASSERT(lx_cgrp_freelwp != NULL);
+ (*lx_cgrp_freelwp)(cgrp, lwpd->br_cgroupid, lwptot(lwp)->t_tid,
+ lwpd->br_pid);
+ VFS_RELE(cgrp);
+ } else {
+ mutex_exit(&lxzdata->lxzd_lock);
+ }
+
+ /*
+ * It is possible for the lx_freelwp hook to be called without a prior
+ * call to lx_exitlwp being made. This happens as part of lwp
+ * de-branding when a native binary is executed from a branded process.
+ *
+ * To cover all cases, lx_cleanlwp is called from lx_exitlwp as well
+ * here in lx_freelwp. When the second call is redundant, the
+ * resources will already be freed and no work will be needed.
+ */
+ lx_cleanlwp(lwp, p);
+
+ /*
+ * Remove our system call interposer.
+ */
+ lwp->lwp_brand_syscall = NULL;
+
+ /*
+ * If this process is being de-branded during an exec(),
+ * the LX ctxops may have already been removed, so the result
+ * from ctxop_remove is irrelevant.
+ */
+ (void) ctxop_remove(lwptot(lwp), &lx_ctxop_template, lwp);
+ if (lwpd->br_pid != 0) {
+ lx_pid_rele(lwptoproc(lwp)->p_pid, lwptot(lwp)->t_tid);
+ }
+
+ /*
+ * Discard the affinity mask.
+ */
+ VERIFY(lwpd->br_affinitymask != NULL);
+ cpuset_free(lwpd->br_affinitymask);
+ lwpd->br_affinitymask = NULL;
+
+ /*
+ * Ensure that lx_ptrace_exit() has been called to detach
+ * ptrace(2) tracers and tracees.
+ */
+ VERIFY(lwpd->br_ptrace_tracer == NULL);
+ VERIFY(lwpd->br_ptrace_accord == NULL);
+
+ lwp->lwp_brand = NULL;
+ kmem_free(lwpd, sizeof (struct lx_lwp_data));
+}
+
+void *
+lx_lwpdata_alloc(proc_t *p)
+{
+ lx_lwp_data_t *lwpd;
+ struct lx_pid *lpidp;
+ cpuset_t *affmask;
+ pid_t newpid = 0;
+ struct pid *pidp = NULL;
+
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ /*
+ * LWPs beyond the first will require a pid to be allocated to emulate
+ * Linux's goofy thread model. While this allocation may be
+ * unnecessary when a single-lwp process undergoes branding, it cannot
+ * be performed during b_initlwp due to p_lock being held.
+ */
+ if (p->p_lwpcnt > 0) {
+ if ((newpid = pid_allocate(p, 0, 0)) < 0) {
+ return (NULL);
+ }
+ pidp = pid_find(newpid);
+ }
+
+ lwpd = kmem_zalloc(sizeof (struct lx_lwp_data), KM_SLEEP);
+ lpidp = kmem_zalloc(sizeof (struct lx_pid), KM_SLEEP);
+ affmask = cpuset_alloc(KM_SLEEP);
+
+ lpidp->lxp_lpid = newpid;
+ lpidp->lxp_pidp = pidp;
+ lwpd->br_lpid = lpidp;
+ lwpd->br_affinitymask = affmask;
+
+ return (lwpd);
+}
+
+/*
+ * Free lwp brand data if an error occurred during lwp_create.
+ * Otherwise, lx_freelwp will be used to free the resources after they're
+ * associated with the lwp via lx_initlwp.
+ */
+void
+lx_lwpdata_free(void *lwpbd)
+{
+ lx_lwp_data_t *lwpd = (lx_lwp_data_t *)lwpbd;
+ VERIFY(lwpd != NULL);
+ VERIFY(lwpd->br_lpid != NULL);
+ VERIFY(lwpd->br_affinitymask != NULL);
+
+ cpuset_free(lwpd->br_affinitymask);
+ if (lwpd->br_lpid->lxp_pidp != NULL) {
+ (void) pid_rele(lwpd->br_lpid->lxp_pidp);
+ }
+ kmem_free(lwpd->br_lpid, sizeof (*lwpd->br_lpid));
+ kmem_free(lwpd, sizeof (*lwpd));
+}
+
+void
+lx_initlwp(klwp_t *lwp, void *lwpbd)
+{
+ lx_lwp_data_t *lwpd = (lx_lwp_data_t *)lwpbd;
+ lx_lwp_data_t *plwpd = ttolxlwp(curthread);
+ kthread_t *tp = lwptot(lwp);
+ proc_t *p = lwptoproc(lwp);
+ lx_zone_data_t *lxzdata;
+ vfs_t *cgrp;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+ VERIFY(lwp->lwp_brand == NULL);
+
+ lwpd->br_exitwhy = CLD_EXITED;
+ lwpd->br_lwp = lwp;
+ lwpd->br_clear_ctidp = NULL;
+ lwpd->br_set_ctidp = NULL;
+ lwpd->br_signal = 0;
+ lwpd->br_stack_mode = LX_STACK_MODE_PREINIT;
+ cpuset_all(lwpd->br_affinitymask);
+
+ /*
+ * The first thread in a process has ppid set to the parent
+ * process's pid, and ptid set to -1. Subsequent threads in the
+ * process have their ppid set to the pid of the thread that
+ * created them, and their ptid to that thread's tid.
+ */
+ if (tp->t_next == tp) {
+ lwpd->br_ppid = tp->t_procp->p_ppid;
+ lwpd->br_ptid = -1;
+ } else if (plwpd != NULL) {
+ bcopy(plwpd->br_tls, lwpd->br_tls, sizeof (lwpd->br_tls));
+ lwpd->br_ppid = plwpd->br_pid;
+ lwpd->br_ptid = curthread->t_tid;
+ /* The child inherits the fs/gsbase values from the parent */
+ lwpd->br_lx_fsbase = plwpd->br_lx_fsbase;
+ lwpd->br_ntv_fsbase = plwpd->br_ntv_fsbase;
+ lwpd->br_lx_gsbase = plwpd->br_lx_gsbase;
+ lwpd->br_ntv_gsbase = plwpd->br_ntv_gsbase;
+ } else {
+ /*
+ * Oddball case: the parent thread isn't a Linux process.
+ */
+ lwpd->br_ppid = 0;
+ lwpd->br_ptid = -1;
+ }
+ lwp->lwp_brand = lwpd;
+
+ /*
+ * When during lx_lwpdata_alloc, we must decide whether or not to
+ * allocate a new pid to associate with the lwp. Since p_lock is not
+ * held at that point, the only time we can guarantee a new pid isn't
+ * needed is when p_lwpcnt == 0. This is because other lwps won't be
+ * present to race with us with regards to pid allocation.
+ *
+ * This means that in all other cases (where p_lwpcnt > 0), we expect
+ * that lx_lwpdata_alloc will allocate a pid for us to use here, even
+ * if it is uneeded. If this process is undergoing an exec, for
+ * example, the single existing lwp will not need a new pid when it is
+ * rebranded. In that case, lx_pid_assign will free the uneeded pid.
+ */
+ VERIFY(lwpd->br_lpid->lxp_pidp != NULL || p->p_lwpcnt == 0);
+
+ lx_pid_assign(tp, lwpd->br_lpid);
+ lwpd->br_tgid = lwpd->br_pid;
+ /*
+ * Having performed the lx pid assignement, the lpid reference is no
+ * longer needed. The underlying data will be freed during lx_freelwp.
+ */
+ lwpd->br_lpid = NULL;
+
+ ctxop_install(lwptot(lwp), &lx_ctxop_template, lwp);
+
+ /*
+ * Install branded system call hooks for this LWP:
+ */
+ lwp->lwp_brand_syscall = lx_syscall_enter;
+
+ /*
+ * The new LWP inherits the parent LWP cgroup ID.
+ */
+ if (plwpd != NULL) {
+ lwpd->br_cgroupid = plwpd->br_cgroupid;
+ }
+ /*
+ * The new LWP inherits the parent LWP emulated scheduling info.
+ */
+ if (plwpd != NULL) {
+ lwpd->br_schd_class = plwpd->br_schd_class;
+ lwpd->br_schd_pri = plwpd->br_schd_pri;
+ lwpd->br_schd_flags = plwpd->br_schd_flags;
+ lwpd->br_schd_runtime = plwpd->br_schd_runtime;
+ lwpd->br_schd_deadline = plwpd->br_schd_deadline;
+ lwpd->br_schd_period = plwpd->br_schd_period;
+ }
+ lxzdata = ztolxzd(p->p_zone);
+ mutex_enter(&lxzdata->lxzd_lock);
+ cgrp = lxzdata->lxzd_cgroup;
+ if (cgrp != NULL) {
+ VFS_HOLD(cgrp);
+ mutex_exit(&lxzdata->lxzd_lock);
+ ASSERT(lx_cgrp_initlwp != NULL);
+ (*lx_cgrp_initlwp)(cgrp, lwpd->br_cgroupid, lwptot(lwp)->t_tid,
+ lwpd->br_pid);
+ VFS_RELE(cgrp);
+ } else {
+ mutex_exit(&lxzdata->lxzd_lock);
+ }
+}
+
+void
+lx_initlwp_post(klwp_t *lwp)
+{
+ lx_lwp_data_t *plwpd = ttolxlwp(curthread);
+ /*
+ * If the parent LWP has a ptrace(2) tracer, the new LWP may
+ * need to inherit that same tracer.
+ */
+ if (plwpd != NULL) {
+ lx_ptrace_inherit_tracer(plwpd, lwptolxlwp(lwp));
+ }
+}
+
+/*
+ * There is no need to have any locking for either the source or
+ * destination struct lx_lwp_data structs. This is always run in the
+ * thread context of the source thread, and the destination thread is
+ * always newly created and not referred to from anywhere else.
+ */
+void
+lx_forklwp(klwp_t *srclwp, klwp_t *dstlwp)
+{
+ struct lx_lwp_data *src = srclwp->lwp_brand;
+ struct lx_lwp_data *dst = dstlwp->lwp_brand;
+
+ dst->br_ppid = src->br_pid;
+ dst->br_ptid = lwptot(srclwp)->t_tid;
+ bcopy(src->br_tls, dst->br_tls, sizeof (dst->br_tls));
+
+ switch (src->br_stack_mode) {
+ case LX_STACK_MODE_BRAND:
+ case LX_STACK_MODE_NATIVE:
+ /*
+ * The parent LWP has an alternate stack installed.
+ * The child LWP should have the same stack base and extent.
+ */
+ dst->br_stack_mode = src->br_stack_mode;
+ dst->br_ntv_stack = src->br_ntv_stack;
+ dst->br_ntv_stack_current = src->br_ntv_stack_current;
+ break;
+
+ default:
+ /*
+ * Otherwise, clear the stack data for this LWP.
+ */
+ dst->br_stack_mode = LX_STACK_MODE_PREINIT;
+ dst->br_ntv_stack = 0;
+ dst->br_ntv_stack_current = 0;
+ }
+
+ /*
+ * copy only these flags
+ */
+ dst->br_lwp_flags = src->br_lwp_flags & BR_CPU_BOUND;
+ dst->br_scall_args = NULL;
+ lx_affinity_forklwp(srclwp, dstlwp);
+
+ /*
+ * Flag so child doesn't ptrace-stop on syscall exit.
+ */
+ dst->br_ptrace_flags |= LX_PTF_NOSTOP;
+
+ if (src->br_clone_grp_flags != 0) {
+ lx_clone_grp_enter(src->br_clone_grp_flags, lwptoproc(srclwp),
+ lwptoproc(dstlwp));
+ /* clone group no longer pending on this thread */
+ src->br_clone_grp_flags = 0;
+ }
+}
+
+/*
+ * When switching a Linux process off the CPU, clear its GDT entries.
+ */
+/* ARGSUSED */
+static void
+lx_save(void *arg)
+{
+ int i;
+
+#if defined(__amd64)
+ reset_sregs();
+#endif
+ for (i = 0; i < LX_TLSNUM; i++)
+ gdt_update_usegd(GDT_TLSMIN + i, &null_udesc);
+}
+
+/*
+ * When switching a Linux process on the CPU, set its GDT entries.
+ *
+ * For 64-bit code we don't have to worry about explicitly setting the
+ * %fsbase via wrmsr(MSR_AMD_FSBASE) here. Instead, that should happen
+ * automatically in update_sregs if we are executing in user-land. If this
+ * is the case then pcb_rupdate should be set.
+ */
+static void
+lx_restore(void *arg)
+{
+ klwp_t *t = (klwp_t *)arg;
+ struct lx_lwp_data *lwpd = lwptolxlwp(t);
+ user_desc_t *tls;
+ int i;
+
+ ASSERT(lwpd);
+
+ tls = lwpd->br_tls;
+ for (i = 0; i < LX_TLSNUM; i++)
+ gdt_update_usegd(GDT_TLSMIN + i, &tls[i]);
+}
+
+void
+lx_set_gdt(int entry, user_desc_t *descrp)
+{
+
+ gdt_update_usegd(entry, descrp);
+}
+
+void
+lx_clear_gdt(int entry)
+{
+ gdt_update_usegd(entry, &null_udesc);
+}
+
+longlong_t
+lx_nosys()
+{
+ return (set_errno(ENOSYS));
+}
+
+/*
+ * Brand-specific routine to check if given non-Solaris standard segment
+ * register values should be modified to other values.
+ */
+/*ARGSUSED*/
+greg_t
+lx_fixsegreg(greg_t sr, model_t datamodel)
+{
+ uint16_t idx = SELTOIDX(sr);
+
+ ASSERT(sr == (sr & 0xffff));
+
+ /*
+ * If the segment selector is a valid TLS selector, just return it.
+ */
+ if (!SELISLDT(sr) && idx >= GDT_TLSMIN && idx <= GDT_TLSMAX)
+ return (sr | SEL_UPL);
+
+ /*
+ * Force the SR into the LDT in ring 3 for 32-bit processes.
+ *
+ * 64-bit processes get the null GDT selector since they are not
+ * allowed to have a private LDT.
+ */
+#if defined(__amd64)
+ return (datamodel == DATAMODEL_ILP32 ? (sr | SEL_TI_LDT | SEL_UPL) : 0);
+#elif defined(__i386)
+ datamodel = datamodel; /* datamodel currently unused for 32-bit */
+ return (sr | SEL_TI_LDT | SEL_UPL);
+#endif /* __amd64 */
+}
+
+/*
+ * Brand-specific function to convert the fsbase as pulled from the register
+ * into a native fsbase suitable for locating the ulwp_t from the kernel.
+ */
+uintptr_t
+lx_fsbase(klwp_t *lwp, uintptr_t fsbase)
+{
+ lx_lwp_data_t *lwpd = lwp->lwp_brand;
+
+ if (lwpd->br_stack_mode != LX_STACK_MODE_BRAND ||
+ lwpd->br_ntv_fsbase == (uintptr_t)NULL) {
+ return (fsbase);
+ }
+
+ return (lwpd->br_ntv_fsbase);
+}
+
+/*
+ * These two functions simulate winfo and post_sigcld for the lx brand. The
+ * difference is delivering a designated signal as opposed to always SIGCLD.
+ */
+static void
+lx_winfo(proc_t *pp, k_siginfo_t *ip, struct lx_proc_data *dat)
+{
+ ASSERT(MUTEX_HELD(&pidlock));
+ bzero(ip, sizeof (k_siginfo_t));
+ ip->si_signo = ltos_signo[dat->l_signal];
+ ip->si_code = pp->p_wcode;
+ ip->si_pid = pp->p_pid;
+ ip->si_ctid = PRCTID(pp);
+ ip->si_zoneid = pp->p_zone->zone_id;
+ ip->si_status = pp->p_wdata;
+ /*
+ * These siginfo values are converted to USER_HZ in the user-land
+ * brand signal code.
+ */
+ ip->si_stime = pp->p_stime;
+ ip->si_utime = pp->p_utime;
+}
+
+static void
+lx_post_exit_sig(proc_t *cp, sigqueue_t *sqp, struct lx_proc_data *dat)
+{
+ proc_t *pp = cp->p_parent;
+
+ ASSERT(MUTEX_HELD(&pidlock));
+ mutex_enter(&pp->p_lock);
+ /*
+ * Since Linux doesn't queue SIGCHLD, or any other non RT
+ * signals, we just blindly deliver whatever signal we can.
+ */
+ ASSERT(sqp != NULL);
+ lx_winfo(cp, &sqp->sq_info, dat);
+ sigaddqa(pp, NULL, sqp);
+ sqp = NULL;
+ mutex_exit(&pp->p_lock);
+}
+
+
+/*
+ * Brand specific code for exiting and sending a signal to the parent, as
+ * opposed to sigcld().
+ */
+void
+lx_exit_with_sig(proc_t *cp, sigqueue_t *sqp)
+{
+ proc_t *pp = cp->p_parent;
+ lx_proc_data_t *lx_brand_data = ptolxproc(cp);
+ ASSERT(MUTEX_HELD(&pidlock));
+
+ switch (cp->p_wcode) {
+ case CLD_EXITED:
+ case CLD_DUMPED:
+ case CLD_KILLED:
+ ASSERT(cp->p_stat == SZOMB);
+ /*
+ * The broadcast on p_srwchan_cv is a kludge to
+ * wakeup a possible thread in uadmin(A_SHUTDOWN).
+ */
+ cv_broadcast(&cp->p_srwchan_cv);
+
+ /*
+ * Add to newstate list of the parent
+ */
+ add_ns(pp, cp);
+
+ cv_broadcast(&pp->p_cv);
+ if ((pp->p_flag & SNOWAIT) ||
+ PTOU(pp)->u_signal[SIGCLD - 1] == SIG_IGN) {
+ if (!(cp->p_pidflag & CLDWAITPID))
+ freeproc(cp);
+ } else if (!(cp->p_pidflag & CLDNOSIGCHLD) &&
+ lx_brand_data->l_signal != 0) {
+ lx_post_exit_sig(cp, sqp, lx_brand_data);
+ sqp = NULL;
+ }
+ break;
+
+ case CLD_STOPPED:
+ case CLD_CONTINUED:
+ case CLD_TRAPPED:
+ panic("Should not be called in this case");
+ }
+
+ if (sqp)
+ siginfofree(sqp);
+}
+
+/*
+ * Filters based on arguments that have been passed in by a separate syscall
+ * using the B_STORE_ARGS mechanism. if the __WALL flag is set, no filter is
+ * applied, otherwise we look at the difference between a clone and non-clone
+ * process.
+ * The definition of a clone process in Linux is a thread that does not deliver
+ * SIGCHLD to its parent. The option __WCLONE indicates to wait only on clone
+ * processes. Without that option, a process should only wait on normal
+ * children. The following table shows the cases.
+ *
+ * default __WCLONE
+ * no SIGCHLD - X
+ * SIGCHLD X -
+ *
+ * This is an XOR of __WCLONE being set, and SIGCHLD being the signal sent on
+ * process exit.
+ *
+ * More information on wait in lx brands can be found at
+ * usr/src/lib/brand/lx/lx_brand/common/wait.c.
+ */
+/* ARGSUSED */
+boolean_t
+lx_wait_filter(proc_t *pp, proc_t *cp)
+{
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+ int flags = lwpd->br_waitid_flags;
+ boolean_t ret;
+
+ if (!lwpd->br_waitid_emulate) {
+ return (B_TRUE);
+ }
+
+ mutex_enter(&cp->p_lock);
+ if (flags & LX_WALL) {
+ ret = B_TRUE;
+ } else {
+ lx_proc_data_t *pd = ptolxproc(cp);
+ boolean_t is_sigchld = B_TRUE;
+ boolean_t match_wclone = B_FALSE;
+
+ /*
+ * When calling clone, an alternate signal can be chosen to
+ * deliver to the parent when the child exits.
+ */
+ if (pd != NULL && pd->l_signal != stol_signo[SIGCHLD]) {
+ is_sigchld = B_FALSE;
+ }
+ if ((flags & LX_WCLONE) != 0) {
+ match_wclone = B_TRUE;
+ }
+
+ ret = (match_wclone ^ is_sigchld) ? B_TRUE : B_FALSE;
+ }
+ mutex_exit(&cp->p_lock);
+
+ return (ret);
+}
+
+void
+lx_ifname_convert(char *ifname, lx_if_action_t act)
+{
+ if (act == LX_IF_TONATIVE) {
+ if (strncmp(ifname, "lo", IFNAMSIZ) == 0)
+ (void) strlcpy(ifname, "lo0", IFNAMSIZ);
+ } else {
+ if (strncmp(ifname, "lo0", IFNAMSIZ) == 0)
+ (void) strlcpy(ifname, "lo", IFNAMSIZ);
+ }
+}
+
+void
+lx_ifflags_convert(uint64_t *flags, lx_if_action_t act)
+{
+ uint64_t buf;
+
+ buf = *flags & (IFF_UP | IFF_BROADCAST | IFF_DEBUG |
+ IFF_LOOPBACK | IFF_POINTOPOINT | IFF_NOTRAILERS |
+ IFF_RUNNING | IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI);
+
+ /* Linux has different shift for multicast flag */
+ if (act == LX_IF_TONATIVE) {
+ if (*flags & 0x1000)
+ buf |= IFF_MULTICAST;
+ } else {
+ if (*flags & IFF_MULTICAST)
+ buf |= 0x1000;
+ }
+ *flags = buf;
+}
+
+/*
+ * Convert an IPv6 address into the numbers used by /proc/net/if_inet6
+ */
+unsigned int
+lx_ipv6_scope_convert(const in6_addr_t *addr)
+{
+ if (IN6_IS_ADDR_V4COMPAT(addr)) {
+ return (LX_IPV6_ADDR_COMPATv4);
+ } else if (IN6_ARE_ADDR_EQUAL(addr, &ipv6_loopback)) {
+ return (LX_IPV6_ADDR_LOOPBACK);
+ } else if (IN6_IS_ADDR_LINKLOCAL(addr)) {
+ return (LX_IPV6_ADDR_LINKLOCAL);
+ } else if (IN6_IS_ADDR_SITELOCAL(addr)) {
+ return (LX_IPV6_ADDR_SITELOCAL);
+ } else {
+ return (0x0000U);
+ }
+}
+
+
+void
+lx_stol_hwaddr(const struct sockaddr_dl *src, struct sockaddr *dst, int *size)
+{
+ int copy_size = MIN(src->sdl_alen, sizeof (dst->sa_data));
+
+ switch (src->sdl_type) {
+ case DL_ETHER:
+ dst->sa_family = LX_ARPHRD_ETHER;
+ break;
+ case DL_LOOP:
+ dst->sa_family = LX_ARPHRD_LOOPBACK;
+ break;
+ default:
+ dst->sa_family = LX_ARPHRD_VOID;
+ }
+
+ bcopy(LLADDR(src), dst->sa_data, copy_size);
+ *size = copy_size;
+}
+
+/*
+ * Brand hook to convert native kernel siginfo signal number, errno, code, pid
+ * and si_status to Linux values. Similar to the stol_ksiginfo function but
+ * this one converts in-place, converts the pid, and does not copyout.
+ */
+void
+lx_sigfd_translate(k_siginfo_t *infop)
+{
+ zone_t *zone = curproc->p_zone;
+
+ infop->si_signo = lx_stol_signo(infop->si_signo, LX_SIGKILL);
+ infop->si_status = lx_stol_status(infop->si_status, LX_SIGKILL);
+ infop->si_code = lx_stol_sigcode(infop->si_code);
+ infop->si_errno = lx_errno(infop->si_errno, EINVAL);
+
+ /* Map zsched and zone init to pid 1 */
+ if (infop->si_pid == zone->zone_proc_initpid ||
+ infop->si_pid == zone->zone_zsched->p_pid) {
+ infop->si_pid = 1;
+ }
+}
+
+int
+stol_ksiginfo_copyout(k_siginfo_t *sip, void *ulxsip)
+{
+ lx_siginfo_t lsi;
+
+ bzero(&lsi, sizeof (lsi));
+ lsi.lsi_signo = lx_stol_signo(sip->si_signo, SIGCLD);
+ lsi.lsi_code = lx_stol_sigcode(sip->si_code);
+ lsi.lsi_errno = lx_errno(sip->si_errno, EINVAL);
+
+ switch (lsi.lsi_signo) {
+ case LX_SIGPOLL:
+ lsi.lsi_band = sip->si_band;
+ lsi.lsi_fd = sip->si_fd;
+ break;
+
+ case LX_SIGCHLD:
+ lsi.lsi_pid = sip->si_pid;
+ if (sip->si_code <= 0 || sip->si_code == CLD_EXITED) {
+ lsi.lsi_status = sip->si_status;
+ } else {
+ lsi.lsi_status = lx_stol_status(sip->si_status,
+ SIGKILL);
+ }
+ lsi.lsi_utime = HZ_TO_LX_USERHZ(sip->si_utime);
+ lsi.lsi_stime = HZ_TO_LX_USERHZ(sip->si_stime);
+ break;
+
+ case LX_SIGILL:
+ case LX_SIGBUS:
+ case LX_SIGFPE:
+ case LX_SIGSEGV:
+ lsi.lsi_addr = sip->si_addr;
+ break;
+
+ default:
+ lsi.lsi_pid = sip->si_pid;
+ lsi.lsi_uid = LX_UID32_TO_UID16(sip->si_uid);
+ }
+
+ if (copyout(&lsi, ulxsip, sizeof (lsi)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+
+#if defined(_SYSCALL32_IMPL)
+int
+stol_ksiginfo32_copyout(k_siginfo_t *sip, void *ulxsip)
+{
+ lx_siginfo32_t lsi;
+
+ bzero(&lsi, sizeof (lsi));
+ lsi.lsi_signo = lx_stol_signo(sip->si_signo, SIGCLD);
+ lsi.lsi_code = lx_stol_sigcode(sip->si_code);
+ lsi.lsi_errno = lx_errno(sip->si_errno, EINVAL);
+
+ switch (lsi.lsi_signo) {
+ case LX_SIGPOLL:
+ lsi.lsi_band = sip->si_band;
+ lsi.lsi_fd = sip->si_fd;
+ break;
+
+ case LX_SIGCHLD:
+ lsi.lsi_pid = sip->si_pid;
+ if (sip->si_code <= 0 || sip->si_code == CLD_EXITED) {
+ lsi.lsi_status = sip->si_status;
+ } else {
+ lsi.lsi_status = lx_stol_status(sip->si_status,
+ SIGKILL);
+ }
+ lsi.lsi_utime = HZ_TO_LX_USERHZ(sip->si_utime);
+ lsi.lsi_stime = HZ_TO_LX_USERHZ(sip->si_stime);
+ break;
+
+ case LX_SIGILL:
+ case LX_SIGBUS:
+ case LX_SIGFPE:
+ case LX_SIGSEGV:
+ lsi.lsi_addr = (caddr32_t)(uintptr_t)sip->si_addr;
+ break;
+
+ default:
+ lsi.lsi_pid = sip->si_pid;
+ lsi.lsi_uid = LX_UID32_TO_UID16(sip->si_uid);
+ }
+
+ if (copyout(&lsi, ulxsip, sizeof (lsi)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+#endif
+
+/* Given an LX LWP, determine where user register state is stored. */
+lx_regs_location_t
+lx_regs_location(lx_lwp_data_t *lwpd, void **ucp, boolean_t for_write)
+{
+ switch (lwpd->br_stack_mode) {
+ case LX_STACK_MODE_BRAND:
+ /*
+ * The LWP was stopped with the brand stack and register state
+ * loaded, e.g. during a syscall emulated within the kernel.
+ */
+ return (LX_REG_LOC_LWP);
+
+ case LX_STACK_MODE_PREINIT:
+ if (for_write) {
+ /* setting registers not allowed in this state */
+ break;
+ }
+ if (lwpd->br_ptrace_whatstop == LX_PR_SIGNALLED ||
+ lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) {
+ /* The LWP was stopped by tracing on exec. */
+ return (LX_REG_LOC_LWP);
+ }
+ break;
+
+ case LX_STACK_MODE_NATIVE:
+ if (for_write) {
+ /* setting registers not allowed in this state */
+ break;
+ }
+ if (lwpd->br_ptrace_whystop == PR_BRAND) {
+ /* Called while ptrace-event-stopped by lx_exec. */
+ if (lwpd->br_ptrace_whatstop == LX_PR_EVENT) {
+ return (LX_REG_LOC_LWP);
+ }
+
+ /* Called while ptrace-event-stopped after clone. */
+ if (lwpd->br_ptrace_whatstop == LX_PR_SIGNALLED &&
+ lwpd->br_ptrace_stopsig == LX_SIGSTOP &&
+ (lwpd->br_ptrace_flags & LX_PTF_STOPPED)) {
+ return (LX_REG_LOC_LWP);
+ }
+
+ /*
+ * Called to obtain syscall exit for other cases
+ * (e.g. pseudo return from rt_sigreturn).
+ */
+ if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT &&
+ (lwpd->br_ptrace_flags & LX_PTF_STOPPED)) {
+ return (LX_REG_LOC_LWP);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (lwpd->br_ptrace_stopucp != (uintptr_t)NULL) {
+ /*
+ * The LWP was stopped in the usermode emulation library
+ * but a ucontext_t for the preserved brand stack and
+ * register state was provided. Return the register state
+ * from that ucontext_t.
+ */
+ VERIFY(ucp != NULL);
+ *ucp = (void *)lwpd->br_ptrace_stopucp;
+ return (LX_REG_LOC_UCP);
+ }
+
+ return (LX_REG_LOC_UNAVAIL);
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_pid.c b/usr/src/uts/common/brand/lx/os/lx_pid.c
new file mode 100644
index 0000000000..fd82a84a22
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_pid.c
@@ -0,0 +1,498 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/bitmap.h>
+#include <sys/var.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/brand.h>
+#include <sys/zone.h>
+#include <sys/lx_brand.h>
+
+#define LINUX_PROC_FACTOR 8 /* factor down the hash table by this */
+static int hash_len = 4; /* desired average hash chain length */
+static int hash_size; /* no of buckets in the hash table */
+
+static struct lx_pid **stol_pid_hash;
+static struct lx_pid **ltos_pid_hash;
+
+#define LTOS_HASH(pid) ((pid) & (hash_size - 1))
+#define STOL_HASH(pid, tid) (((pid) + (tid)) & (hash_size - 1))
+
+static kmutex_t hash_lock;
+
+static void
+lx_pid_insert_hash(struct lx_pid *lpidp)
+{
+ int shash = STOL_HASH(lpidp->lxp_spid, lpidp->lxp_stid);
+ int lhash = LTOS_HASH(lpidp->lxp_lpid);
+
+ ASSERT(MUTEX_HELD(&hash_lock));
+
+ lpidp->lxp_stol_next = stol_pid_hash[shash];
+ stol_pid_hash[shash] = lpidp;
+
+ lpidp->lxp_ltos_next = ltos_pid_hash[lhash];
+ ltos_pid_hash[lhash] = lpidp;
+}
+
+static struct lx_pid *
+lx_pid_remove_hash(pid_t pid, id_t tid)
+{
+ struct lx_pid **hpp;
+ struct lx_pid *lpidp = NULL;
+
+ ASSERT(MUTEX_HELD(&hash_lock));
+
+ hpp = &stol_pid_hash[STOL_HASH(pid, tid)];
+ while (*hpp) {
+ if ((*hpp)->lxp_spid == pid && (*hpp)->lxp_stid == tid) {
+ lpidp = *hpp;
+ *hpp = (*hpp)->lxp_stol_next;
+ break;
+ }
+ hpp = &(*hpp)->lxp_stol_next;
+ }
+
+ /*
+ * when called during error recovery the pid may already
+ * be released
+ */
+ if (lpidp == NULL)
+ return (NULL);
+
+ hpp = &ltos_pid_hash[LTOS_HASH(lpidp->lxp_lpid)];
+ while (*hpp) {
+ if (*hpp == lpidp) {
+ *hpp = lpidp->lxp_ltos_next;
+ break;
+ }
+ hpp = &(*hpp)->lxp_ltos_next;
+ }
+
+ return (lpidp);
+}
+
+/*
+ * given a solaris pid/tid pair, create a linux pid
+ */
+void
+lx_pid_assign(kthread_t *t, struct lx_pid *lpidp)
+{
+ proc_t *p = ttoproc(t);
+ lx_lwp_data_t *lwpd = ttolxlwp(t);
+ pid_t spid = p->p_pid;
+ id_t stid = t->t_tid;
+
+ /*
+ * When lx_initlwp is called from lx_setbrand, p_lwpcnt will already be
+ * equal to 1. Since lx_initlwp is being called against an lwp that
+ * already exists, an additional pid allocation is not necessary.
+ *
+ * We check for this by testing br_ppid == 0.
+ */
+ if (p->p_lwpcnt > 0 && lwpd->br_ppid != 0) {
+ /*
+ * Assign allocated pid to any thread other than the first.
+ * The lpid and pidp fields should be populated.
+ */
+ VERIFY(lpidp->lxp_pidp != NULL);
+ VERIFY(lpidp->lxp_lpid != 0);
+ } else {
+ /*
+ * There are cases where a pid is speculatively allocated but
+ * is not needed. We are obligated to free it here.
+ */
+ if (lpidp->lxp_pidp != NULL) {
+ (void) pid_rele(lpidp->lxp_pidp);
+ }
+ lpidp->lxp_pidp = NULL;
+ lpidp->lxp_lpid = spid;
+ }
+
+ lpidp->lxp_spid = spid;
+ lpidp->lxp_stid = stid;
+ lpidp->lxp_start = t->t_start;
+ lpidp->lxp_procp = p;
+
+ /*
+ * Now place the pid into the Linux-SunOS and SunOS-Linux conversion
+ * hash tables.
+ */
+ mutex_enter(&hash_lock);
+ lx_pid_insert_hash(lpidp);
+ mutex_exit(&hash_lock);
+
+ lwpd->br_pid = lpidp->lxp_lpid;
+}
+
+/*
+ * If we are exec()ing the process, this thread's tid is about to be reset
+ * to 1. Make sure the Linux PID bookkeeping reflects that change.
+ */
+void
+lx_pid_reassign(kthread_t *t)
+{
+ proc_t *p = ttoproc(t);
+ struct pid *old_pidp;
+ struct lx_pid *lpidp;
+
+ ASSERT(p->p_lwpcnt == 1);
+
+ mutex_enter(&hash_lock);
+
+ /*
+ * Clean up all the traces of this thread's 'fake' Linux PID.
+ */
+ lpidp = lx_pid_remove_hash(p->p_pid, t->t_tid);
+ ASSERT(lpidp != NULL);
+ old_pidp = lpidp->lxp_pidp;
+ lpidp->lxp_pidp = NULL;
+
+ /*
+ * Now register this thread as (pid, 1).
+ */
+ lpidp->lxp_lpid = p->p_pid;
+ lpidp->lxp_spid = p->p_pid;
+ lpidp->lxp_stid = 1;
+ lx_pid_insert_hash(lpidp);
+
+ mutex_exit(&hash_lock);
+
+ if (old_pidp)
+ (void) pid_rele(old_pidp);
+}
+
+/*
+ * release a solaris pid/tid pair
+ */
+void
+lx_pid_rele(pid_t pid, id_t tid)
+{
+ struct lx_pid *lpidp;
+
+ mutex_enter(&hash_lock);
+ lpidp = lx_pid_remove_hash(pid, tid);
+ mutex_exit(&hash_lock);
+
+ if (lpidp) {
+ if (lpidp->lxp_pidp)
+ (void) pid_rele(lpidp->lxp_pidp);
+
+ kmem_free(lpidp, sizeof (*lpidp));
+ }
+}
+
+/*
+ * given a linux pid, return the solaris pid/tid pair
+ */
+int
+lx_lpid_to_spair(pid_t lpid, pid_t *spid, id_t *stid)
+{
+ struct lx_pid *hp;
+
+ if (lpid == 1) {
+ pid_t initpid;
+
+ /*
+ * We are trying to look up the Linux init process for the
+ * current zone, which we pretend has pid 1.
+ */
+ if ((initpid = curzone->zone_proc_initpid) == -1) {
+ /*
+ * We could not find the init process for this zone.
+ */
+ return (-1);
+ }
+
+ if (spid != NULL)
+ *spid = initpid;
+ if (stid != NULL)
+ *stid = 1;
+
+ return (0);
+ }
+
+ mutex_enter(&hash_lock);
+ for (hp = ltos_pid_hash[LTOS_HASH(lpid)]; hp != NULL;
+ hp = hp->lxp_ltos_next) {
+ if (hp->lxp_lpid == lpid) {
+ if (spid)
+ *spid = hp->lxp_spid;
+ if (stid)
+ *stid = hp->lxp_stid;
+ break;
+ }
+ }
+ mutex_exit(&hash_lock);
+ if (hp != NULL)
+ return (0);
+
+ /*
+ * We didn't find this pid in our translation table.
+ * But this still could be the pid of a native process
+ * running in the current zone so check for that here.
+ *
+ * Note that prfind() only searches for processes in the current zone.
+ */
+ mutex_enter(&pidlock);
+ if (prfind(lpid) != NULL) {
+ mutex_exit(&pidlock);
+ if (spid)
+ *spid = lpid;
+ if (stid)
+ *stid = 0;
+ return (0);
+ }
+ mutex_exit(&pidlock);
+
+ return (-1);
+}
+
+/*
+ * Given a Linux pid, locate the proc_t and optionally acquire P_PR_LOCK.
+ * Returns 0 on success with p_lock held for the proc_t in question.
+ */
+int
+lx_lpid_lock(pid_t lpid, zone_t *zone, lx_pid_flag_t flag, proc_t **pp,
+ kthread_t **tp)
+{
+ proc_t *p;
+ kthread_t *t;
+ id_t tid = 0;
+
+ ASSERT(MUTEX_NOT_HELD(&pidlock));
+ ASSERT(pp != NULL);
+ ASSERT(zone != NULL && zone->zone_brand == &lx_brand);
+
+retry:
+ p = NULL;
+ if (lpid == 1) {
+ pid_t initpid;
+
+ /*
+ * Look up the init process for the zone.
+ */
+ if ((initpid = zone->zone_proc_initpid) <= 0) {
+ return (-1);
+ }
+ mutex_enter(&pidlock);
+ p = prfind_zone(initpid, zone->zone_id);
+ tid = 0;
+ } else {
+ struct lx_pid *hp;
+
+ mutex_enter(&pidlock);
+ mutex_enter(&hash_lock);
+ for (hp = ltos_pid_hash[LTOS_HASH(lpid)]; hp != NULL;
+ hp = hp->lxp_ltos_next) {
+ if (hp->lxp_lpid == lpid) {
+ tid = hp->lxp_stid;
+ p = hp->lxp_procp;
+ break;
+ }
+ }
+ mutex_exit(&hash_lock);
+ /*
+ * If the pid wasn't listed in the ltos hash, it may correspond
+ * to an native process in the zone.
+ */
+ if (p == NULL) {
+ p = prfind_zone(lpid, zone->zone_id);
+ tid = 0;
+ }
+ }
+
+ if (p == NULL) {
+ mutex_exit(&pidlock);
+ return (-1);
+ }
+
+ /*
+ * Bail on processes belonging to the system, those which are not yet
+ * complete and zombies (unless explicitly allowed via the flags).
+ */
+ if (p->p_stat == SIDL || (p->p_flag & SSYS) != 0 ||
+ (p->p_stat == SZOMB && (flag & LXP_ZOMBOK) == 0)) {
+ mutex_exit(&pidlock);
+ return (-1);
+ }
+ mutex_enter(&p->p_lock);
+ mutex_exit(&pidlock);
+
+ if (flag & LXP_PRLOCK) {
+ /*
+ * It would be convenient to call sprtrylock_proc() for this
+ * task. Unfortunately, its behavior of filtering zombies is
+ * excessive for some lx_proc use cases. Instead, when the
+ * provided flags do not indicate that zombies are allowed,
+ * exiting processes are filtered out (as would be performed by
+ * sprtrylock_proc).
+ */
+ if ((p->p_flag & (SEXITING|SEXITLWPS)) != 0 &&
+ (flag & LXP_ZOMBOK) == 0) {
+ mutex_exit(&p->p_lock);
+ return (-1);
+ }
+ if (p->p_proc_flag & P_PR_LOCK) {
+ sprwaitlock_proc(p);
+ goto retry;
+ } else {
+ p->p_proc_flag |= P_PR_LOCK;
+ }
+ }
+
+ if (tid == 0) {
+ t = p->p_tlist;
+ } else {
+ lwpdir_t *ld;
+
+ ld = lwp_hash_lookup(p, tid);
+ if (ld == NULL) {
+ if (flag & LXP_PRLOCK) {
+ sprunprlock(p);
+ }
+ mutex_exit(&p->p_lock);
+ return (-1);
+ }
+ t = ld->ld_entry->le_thread;
+ }
+ *pp = p;
+ if (tp != NULL) {
+ *tp = t;
+ }
+ return (0);
+}
+
+
+/*
+ * Given an lwp, return the Linux pid of its parent. If the caller
+ * wants them, we return the SunOS (pid, tid) as well.
+ */
+pid_t
+lx_lwp_ppid(klwp_t *lwp, pid_t *ppidp, id_t *ptidp)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ proc_t *p = lwptoproc(lwp);
+ const pid_t zoneinit = p->p_zone->zone_proc_initpid;
+ const pid_t ppid = p->p_ppid;
+
+ /*
+ * Report a ppid of 1 for processes which are children to either init
+ * or a process outside the zone.
+ */
+ if (ppid == zoneinit || (p->p_flag & SZONETOP) != 0) {
+ goto ppid_is_zinit;
+ }
+
+ /*
+ * Our native concept of a 'parent pid' matches Linux in two cases:
+ *
+ * - TGID and PID are equal: This is either the first thread in the
+ * process or one created with CLONE_THREAD.
+ *
+ * - The brand lwp value for PPID is 0: This is either the child of a
+ * differently-branded process or was created with the CLONE_PARENT.
+ */
+ if (p->p_pid == lwpd->br_tgid || lwpd->br_ppid == 0) {
+ if (ppidp != NULL)
+ *ppidp = ppid;
+ if (ptidp != NULL)
+ *ptidp = -1;
+ return (ppid);
+ }
+
+ /*
+ * In all other cases, we are looking for the parent of this specific
+ * thread, which in Linux refers to the thread that clone(2)d it. We
+ * stashed that thread's PID away when this thread was created.
+ */
+ mutex_enter(&hash_lock);
+ for (struct lx_pid *hp = ltos_pid_hash[LTOS_HASH(lwpd->br_ppid)];
+ hp != NULL; hp = hp->lxp_ltos_next) {
+ if (lwpd->br_ppid == hp->lxp_lpid) {
+ /*
+ * The PID matches, but there are a couple cases when
+ * the translation is not suitable:
+ *
+ * - The cached start time is too young, indicating
+ * that the thread exited and the PID was reused by
+ * another process.
+ * - The parent is zoneinit
+ *
+ * In both cases, a result of ppid=1 is yielded.
+ */
+ if (hp->lxp_start > lwptot(lwp)->t_start ||
+ lwpd->br_ppid == zoneinit) {
+ break;
+ }
+
+ /* Good match, yield the result */
+ if (ppidp != NULL)
+ *ppidp = hp->lxp_spid;
+ if (ptidp != NULL)
+ *ptidp = hp->lxp_stid;
+ mutex_exit(&hash_lock);
+ return (lwpd->br_ppid);
+ }
+ }
+ mutex_exit(&hash_lock);
+ /*
+ * If no match is found in the Linux->SunOS translation hash, fall back
+ * to assuming the zone init process as the parent.
+ */
+
+ppid_is_zinit:
+ if (ppidp != NULL)
+ *ppidp = 1;
+ if (ptidp != NULL)
+ *ptidp = -1;
+ return (1);
+}
+
+void
+lx_pid_init(void)
+{
+ hash_size = 1 << highbit(v.v_proc / (hash_len * LINUX_PROC_FACTOR));
+
+ stol_pid_hash = kmem_zalloc(sizeof (struct lx_pid *) * hash_size,
+ KM_SLEEP);
+ ltos_pid_hash = kmem_zalloc(sizeof (struct lx_pid *) * hash_size,
+ KM_SLEEP);
+
+ mutex_init(&hash_lock, NULL, MUTEX_DEFAULT, NULL);
+}
+
+void
+lx_pid_fini(void)
+{
+ kmem_free(stol_pid_hash, sizeof (struct lx_pid *) * hash_size);
+ kmem_free(ltos_pid_hash, sizeof (struct lx_pid *) * hash_size);
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_ptrace.c b/usr/src/uts/common/brand/lx/os/lx_ptrace.c
new file mode 100644
index 0000000000..07757ab0aa
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_ptrace.c
@@ -0,0 +1,2710 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+/*
+ * Emulation of the Linux ptrace(2) interface.
+ *
+ * OVERVIEW
+ *
+ * The Linux process model is somewhat different from the illumos native
+ * model. One critical difference is that each Linux thread has a unique
+ * identifier in the pid namespace. The lx brand assigns a pid to each LWP
+ * within the emulated process, giving the pid of the process itself to the
+ * first LWP.
+ *
+ * The Linux ptrace(2) interface allows for any LWP in a branded process to
+ * exert control over any other LWP within the same zone. Control is exerted
+ * by the use of the ptrace(2) system call itself, which accepts a number of
+ * request codes. Feedback on traced events is primarily received by the
+ * tracer through SIGCLD and the emulated waitpid(2) and waitid(2) system
+ * calls. Many of the possible ptrace(2) requests will only succeed if the
+ * target LWP is in a "ptrace-stop" condition.
+ *
+ * HISTORY
+ *
+ * The brand support for ptrace(2) was originally built on top of the rich
+ * support for debugging and tracing provided through the illumos /proc
+ * interfaces, mounted at /native/proc within the zone. The native legacy
+ * ptrace(3C) functionality was used as a starting point, but was generally
+ * insufficient for complete and precise emulation. The extant legacy
+ * interface, and indeed our native SIGCLD and waitid(2) facilities, are
+ * focused on _process_ level concerns -- the Linux interface has been
+ * extended to be aware of LWPs as well.
+ *
+ * In order to allow us to focus on providing more complete and accurate
+ * emulation without extensive and undesirable changes to the native
+ * facilities, this second generation ptrace(2) emulation is mostly separate
+ * from any other tracing or debugging framework in the system.
+ *
+ * ATTACHING TRACERS TO TRACEES
+ *
+ * There are several ways that a child LWP may becomed traced by a tracer.
+ * To determine which attach method caused a tracee to become attached, one
+ * may inspect the "br_ptrace_attach" member of the LWP-specific brand data
+ * with the debugger.
+ *
+ * The first attach methods to consider are the attaching ptrace(2) requests:
+ *
+ * PTRACE_TRACEME
+ *
+ * If an LWP makes a PTRACE_TRACEME call, it will be attached as a tracee
+ * to its parent LWP (br_ppid). Using PTRACE_TRACEME does _not_ cause the
+ * tracee to be held in a stop condition. It is common practice for
+ * consumers to raise(SIGSTOP) immediately afterward.
+ *
+ * PTRACE_ATTACH
+ *
+ * An LWP may attempt to trace any other LWP in this, or another, process.
+ * We currently allow any attach where the process containing the tracer
+ * LWP has permission to write to /proc for the process containing the
+ * intended tracer. This action also sends a SIGSTOP to the newly attached
+ * tracee.
+ *
+ * The second class of attach methods are the clone(2)/fork(2) inheritance
+ * options that may be set on a tracee with PTRACE_SETOPTIONS:
+ *
+ * PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK and PTRACE_O_TRACECLONE
+ *
+ * If these options have been set on a tracee, then a fork(2), vfork(2) or
+ * clone(2) respectively will cause the newly created LWP to be traced by
+ * the same tracer. The same set of ptrace(2) options will also be set on
+ * the new child.
+ *
+ * The third class of attach method is the PTRACE_CLONE flag to clone(2).
+ * This flag induces the same inheritance as PTRACE_O_TRACECLONE, but is
+ * passed by the tracee as an argument to clone(2).
+ *
+ * DETACHING TRACEES
+ *
+ * Tracees can be detached by the tracer with the PTRACE_DETACH request.
+ * This request is only valid when the tracee is in a ptrace(2) stop
+ * condition, and is itself a restarting action.
+ *
+ * If the tracer exits without detaching all of its tracees, then all of the
+ * tracees are automatically detached and restarted. If a tracee was in
+ * "signal-delivery-stop" at the time the tracer exited, the signal will be
+ * released to the child unless it is a SIGSTOP. We drop this instance of
+ * SIGSTOP in order to prevent the child from becoming stopped by job
+ * control.
+ *
+ * ACCORD ALLOCATION AND MANAGEMENT
+ *
+ * The "lx_ptrace_accord_t" object tracks the agreement between a tracer LWP
+ * and zero or more tracee LWPs. It is explicitly illegal for a tracee to
+ * trace its tracer, and we block this in PTRACE_ATTACH/PTRACE_TRACEME.
+ *
+ * An LWP starts out without an accord. If a child of that LWP calls
+ * ptrace(2) with the PTRACE_TRACEME subcommand, or if the LWP itself uses
+ * PTRACE_ATTACH, an accord will be allocated and stored on that LWP. The
+ * accord structure is not released from that LWP until it arrives in
+ * lx_exitlwp(), as called by lwp_exit(). A new accord will not be
+ * allocated, even if one does not exist, once an LWP arrives in lx_exitlwp()
+ * and sets the LX_PTF_EXITING flag. An LWP will have at most one accord
+ * structure throughout its entire lifecycle; once it has one, it has the
+ * same one until death.
+ *
+ * The accord is reference counted (lxpa_refcnt), starting at a count of one
+ * at creation to represent the link from the tracer LWP to its accord. The
+ * accord is not freed until the reference count falls to zero.
+ *
+ * To make mutual exclusion between a detaching tracer and various notifying
+ * tracees simpler, the tracer will hold "pidlock" while it clears the
+ * accord members that point back to the tracer LWP and CV.
+ *
+ * SIGNALS AND JOB CONTROL
+ *
+ * Various actions, either directly ptrace(2) related or commonly associated
+ * with tracing, cause process- or thread-directed SIGSTOP signals to be sent
+ * to tracees (a "signal-delivery-stop"). These signals, and indeed any signal
+ * other than SIGKILL, can be suppressed by the tracer when using a restarting
+ * request (including PTRACE_DETACH) on a child. The signal may also be
+ * substituted for a different signal.
+ *
+ * If a SIGSTOP (or other stopping signal) is not suppressed by the tracer,
+ * it will induce the regular illumos native job control stop of the entire
+ * traced process. This is at least passingly similar to the Linux "group
+ * stop" ptrace(2) condition.
+ *
+ * SYSTEM CALL TRACING
+ *
+ * The ptrace(2) interface enables the tracer to hold the tracee on entry and
+ * exit from system calls. When a stopped tracee is restarted through the
+ * PTRACE_SYSCALL request, the LX_PTF_SYSCALL flag is set until the next
+ * system call boundary. Whether this is a "syscall-entry-stop" or
+ * "syscall-exit-stop", the tracee is held and the tracer is notified via
+ * SIGCLD/waitpid(2) in the usual way. The flag LX_PTF_SYSCALL flag is
+ * cleared after each stop; for ongoing system call tracing the tracee must
+ * be continuously restarted with PTRACE_SYSCALL.
+ *
+ * SPECIAL CASES FOR STOP EVENTS
+ *
+ * The strace command is one of the primary consumers of ptrace. In order for
+ * strace to properly understand what is actually happening when it receives a
+ * signal associated with a stop event, these signals must match Linux behavior
+ * exactly or the strace consumer will get out of sync and report incorrect
+ * state. There are a couple of special cases we have to handle to provide
+ * proper interaction of the syscall-entry-stop, syscall-exit-stop, and
+ * signal-delivery-stop events:
+ * 1) The child process of a clone/fork does not emit a syscall-exit-stop event.
+ * 2) A signal that arrives between syscall-enter-stop & syscall-exit-stop must
+ * not immediately emit signal-delivery-stop. This event must be emitted
+ * after the syscall is interrupted and syscall-exit-stop has been emitted.
+ *
+ * EVENT STOPS
+ *
+ * Various events (particularly FORK, VFORK, CLONE, EXEC and EXIT) are
+ * enabled by the tracer through PTRACE_SETOPTIONS. Once enabled, the tracee
+ * will be stopped at the nominated points of interest and the tracer
+ * notified. The tracer may request additional information about the event,
+ * such as the pid of new LWPs and processes, via PTRACE_GETEVENTMSG.
+ *
+ * LOCK ORDERING RULES
+ *
+ * It is not safe, in general, to hold p_lock for two different processes at
+ * the same time. This constraint is the primary reason for the existence
+ * (and complexity) of the ptrace(2) accord mechanism.
+ *
+ * In order to facilitate looking up accords by the "pid" of a tracer LWP,
+ * p_lock for the tracer process may be held while entering the accord mutex
+ * (lxpa_lock). This mutex protects the accord flags and reference count.
+ * The reference count is manipulated through lx_ptrace_accord_hold() and
+ * lx_ptrace_accord_rele().
+ *
+ * DO NOT interact with the accord mutex (lxpa_lock) directly. The
+ * lx_ptrace_accord_enter() and lx_ptrace_accord_exit() functions do various
+ * book-keeping and lock ordering enforcement and MUST be used.
+ *
+ * It is NOT legal to take ANY p_lock while holding the accord mutex
+ * (lxpa_lock). If the lxpa_tracees_lock is to be held concurrently with
+ * lxpa_lock, lxpa_lock MUST be taken first and dropped before taking p_lock
+ * of any processes from the tracee list.
+ *
+ * It is NOT legal to take a tracee p_lock and then attempt to enter the
+ * accord mutex (or tracee list mutex) of its tracer. When running as the
+ * tracee LWP, the tracee's hold will prevent the accord from being freed.
+ * Use of the LX_PTF_STOPPING or LX_PTF_CLONING flag in the LWP-specific brand
+ * data prevents an exiting tracer from altering the tracee until the tracee
+ * has come to an orderly stop, without requiring the tracee to hold its own
+ * p_lock the entire time it is stopping.
+ *
+ * It is not safe, in general, to enter "pidlock" while holding the p_lock of
+ * any process. It is similarly illegal to hold any accord locks (lxpa_lock
+ * or lxpa_sublock) while attempting to enter "pidlock". As "pidlock" is a
+ * global mutex, it should be held for the shortest possible time.
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/ksynch.h>
+#include <sys/sysmacros.h>
+#include <sys/procfs.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/wait.h>
+#include <sys/prsystm.h>
+#include <sys/note.h>
+
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_misc.h>
+#include <lx_syscall.h>
+#include <lx_signum.h>
+
+
+typedef enum lx_ptrace_cont_flags_t {
+ LX_PTC_NONE = 0x00,
+ LX_PTC_SYSCALL = 0x01,
+ LX_PTC_SINGLESTEP = 0x02
+} lx_ptrace_cont_flags_t;
+
+
+extern int lx_user_regs_copyin(lx_lwp_data_t *, void *);
+extern int lx_user_regs_copyout(lx_lwp_data_t *, void *);
+extern int lx_ptrace_peekuser(lx_lwp_data_t *, uintptr_t, void *);
+extern int lx_ptrace_pokeuser(lx_lwp_data_t *, uintptr_t, void *);
+extern int lx_user_fpregs_copyin(lx_lwp_data_t *, void *);
+extern int lx_user_fpregs_copyout(lx_lwp_data_t *, void *);
+extern int lx_user_fpxregs_copyin(lx_lwp_data_t *, void *);
+extern int lx_user_fpxregs_copyout(lx_lwp_data_t *, void *);
+
+/*
+ * Macros for checking the state of an LWP via "br_ptrace_flags":
+ */
+#define LX_PTRACE_BUSY \
+ (LX_PTF_EXITING | LX_PTF_STOPPING | LX_PTF_CLONING)
+
+#define VISIBLE(a) (((a)->br_ptrace_flags & LX_PTF_EXITING) == 0)
+#define TRACEE_BUSY(a) (((a)->br_ptrace_flags & LX_PTRACE_BUSY) != 0)
+
+#define ACCORD_HELD(a) MUTEX_HELD(&(a)->lxpa_lock)
+
+#define LX_PID_TO_INIT(x) ((x) == curproc->p_zone->zone_proc_initpid ? \
+ 1 : (x))
+#define LX_INIT_TO_PID(x) ((x) == 1 ? \
+ curproc->p_zone->zone_proc_initpid : (x))
+
+static kcondvar_t lx_ptrace_busy_cv;
+static kmem_cache_t *lx_ptrace_accord_cache;
+
+/*
+ * Enter the accord mutex.
+ */
+static void
+lx_ptrace_accord_enter(lx_ptrace_accord_t *accord)
+{
+ VERIFY(MUTEX_NOT_HELD(&accord->lxpa_tracees_lock));
+
+ mutex_enter(&accord->lxpa_lock);
+}
+
+/*
+ * Exit the accord mutex. If the reference count has dropped to zero,
+ * free the accord.
+ */
+static void
+lx_ptrace_accord_exit(lx_ptrace_accord_t *accord)
+{
+ VERIFY(ACCORD_HELD(accord));
+
+ if (accord->lxpa_refcnt > 0) {
+ mutex_exit(&accord->lxpa_lock);
+ return;
+ }
+
+ /*
+ * When the reference count drops to zero we must free the accord.
+ */
+ VERIFY(accord->lxpa_tracer == NULL);
+ VERIFY(MUTEX_NOT_HELD(&accord->lxpa_tracees_lock));
+ VERIFY(list_is_empty(&accord->lxpa_tracees));
+ VERIFY(accord->lxpa_flags & LX_ACC_TOMBSTONE);
+
+ mutex_destroy(&accord->lxpa_lock);
+ mutex_destroy(&accord->lxpa_tracees_lock);
+
+ kmem_cache_free(lx_ptrace_accord_cache, accord);
+}
+
+/*
+ * Drop our reference to this accord. If this drops the reference count
+ * to zero, the next lx_ptrace_accord_exit() will free the accord.
+ */
+static void
+lx_ptrace_accord_rele(lx_ptrace_accord_t *accord)
+{
+ VERIFY(ACCORD_HELD(accord));
+
+ VERIFY(accord->lxpa_refcnt > 0);
+ accord->lxpa_refcnt--;
+}
+
+/*
+ * Place an additional hold on an accord.
+ */
+static void
+lx_ptrace_accord_hold(lx_ptrace_accord_t *accord)
+{
+ VERIFY(ACCORD_HELD(accord));
+
+ accord->lxpa_refcnt++;
+}
+
+/*
+ * Fetch the accord for this LWP. If one has not yet been created, and the
+ * process is not exiting, allocate it now. Must be called with p_lock held
+ * for the process containing the target LWP.
+ *
+ * If successful, we return holding the accord lock (lxpa_lock).
+ */
+static int
+lx_ptrace_accord_get_locked(klwp_t *lwp, lx_ptrace_accord_t **accordp,
+ boolean_t allocate_one)
+{
+ lx_ptrace_accord_t *lxpa;
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ proc_t *p = lwptoproc(lwp);
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * If this LWP does not have an accord, we wish to allocate
+ * and install one.
+ */
+ if ((lxpa = lwpd->br_ptrace_accord) == NULL) {
+ if (!allocate_one || !VISIBLE(lwpd)) {
+ /*
+ * Either we do not wish to allocate an accord, or this
+ * LWP has already begun exiting from a ptrace
+ * perspective.
+ */
+ *accordp = NULL;
+ return (ESRCH);
+ }
+
+ lxpa = kmem_cache_alloc(lx_ptrace_accord_cache, KM_SLEEP);
+ bzero(lxpa, sizeof (*lxpa));
+
+ /*
+ * The initial reference count is 1 because we are referencing
+ * it in from the soon-to-be tracer LWP.
+ */
+ lxpa->lxpa_refcnt = 1;
+ mutex_init(&lxpa->lxpa_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&lxpa->lxpa_tracees_lock, NULL, MUTEX_DEFAULT, NULL);
+ list_create(&lxpa->lxpa_tracees, sizeof (lx_lwp_data_t),
+ offsetof(lx_lwp_data_t, br_ptrace_linkage));
+ lxpa->lxpa_cvp = &p->p_cv;
+
+ lxpa->lxpa_tracer = lwpd;
+ lwpd->br_ptrace_accord = lxpa;
+ }
+
+ /*
+ * Lock the accord before returning it to the caller.
+ */
+ lx_ptrace_accord_enter(lxpa);
+
+ /*
+ * There should be at least one active reference to this accord,
+ * otherwise it should have been freed.
+ */
+ VERIFY(lxpa->lxpa_refcnt > 0);
+
+ *accordp = lxpa;
+ return (0);
+}
+
+/*
+ * Accords belong to the tracer LWP. Get the accord for this tracer or return
+ * an error if it was not possible. To prevent deadlocks, the caller MUST NOT
+ * hold p_lock on its own or any other process.
+ *
+ * If successful, we return holding the accord lock (lxpa_lock).
+ */
+static int
+lx_ptrace_accord_get_by_pid(pid_t lxpid, lx_ptrace_accord_t **accordp)
+{
+ int ret = ESRCH;
+ proc_t *aproc;
+ kthread_t *athr;
+ klwp_t *alwp;
+ lx_lwp_data_t *alwpd;
+
+ VERIFY(MUTEX_NOT_HELD(&curproc->p_lock));
+
+ /*
+ * Locate the process containing the tracer LWP based on its Linux pid
+ * and lock it.
+ */
+ if (lx_lpid_lock(lxpid, curzone, LXP_PRLOCK, &aproc, &athr) != 0) {
+ return (ESRCH);
+ }
+
+ /*
+ * Locate the tracer LWP itself and ensure that it is visible to
+ * ptrace(2).
+ */
+ if ((alwp = ttolwp(athr)) == NULL ||
+ (alwpd = lwptolxlwp(alwp)) == NULL ||
+ !VISIBLE(alwpd)) {
+ sprunlock(aproc);
+ return (ESRCH);
+ }
+
+ /*
+ * We should not fetch our own accord this way.
+ */
+ if (athr == curthread) {
+ sprunlock(aproc);
+ return (EPERM);
+ }
+
+ /*
+ * Fetch (or allocate) the accord owned by this tracer LWP:
+ */
+ ret = lx_ptrace_accord_get_locked(alwp, accordp, B_TRUE);
+
+ /*
+ * Unlock the process and return.
+ */
+ sprunlock(aproc);
+ return (ret);
+}
+
+/*
+ * Get (or allocate) the ptrace(2) accord for the current LWP, acting as a
+ * tracer. The caller MUST NOT currently hold p_lock on the process containing
+ * this LWP.
+ *
+ * If successful, we return holding the accord lock (lxpa_lock).
+ */
+static int
+lx_ptrace_accord_get(lx_ptrace_accord_t **accordp, boolean_t allocate_one)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = lwptoproc(lwp);
+ int ret;
+
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ /*
+ * Lock the tracer (this LWP).
+ */
+ mutex_enter(&p->p_lock);
+
+ /*
+ * Fetch (or allocate) the accord for this LWP:
+ */
+ ret = lx_ptrace_accord_get_locked(lwp, accordp, allocate_one);
+
+ mutex_exit(&p->p_lock);
+
+ return (ret);
+}
+
+/*
+ * Restart an LWP if it is in "ptrace-stop". This function may induce sleep,
+ * so the caller MUST NOT hold any mutexes other than p_lock for the process
+ * containing the LWP.
+ */
+static void
+lx_ptrace_restart_lwp(klwp_t *lwp)
+{
+ kthread_t *rt = lwptot(lwp);
+ proc_t *rproc = lwptoproc(lwp);
+ lx_lwp_data_t *rlwpd = lwptolxlwp(lwp);
+
+ VERIFY(rt != curthread);
+ VERIFY(MUTEX_HELD(&rproc->p_lock));
+
+ /*
+ * Exclude potential meddling from procfs.
+ */
+ prbarrier(rproc);
+
+ /*
+ * Check that the LWP is still in "ptrace-stop" and, if so, restart it.
+ */
+ thread_lock(rt);
+ if (BSTOPPED(rt) && rt->t_whystop == PR_BRAND) {
+ rt->t_schedflag |= TS_BSTART;
+ setrun_locked(rt);
+
+ /*
+ * Clear stop reason.
+ */
+ rlwpd->br_ptrace_whystop = 0;
+ rlwpd->br_ptrace_whatstop = 0;
+ rlwpd->br_ptrace_flags &= ~(LX_PTF_CLDPEND | LX_PTF_WAITPEND);
+ }
+ thread_unlock(rt);
+}
+
+static void
+lx_ptrace_winfo(lx_lwp_data_t *remote, k_siginfo_t *ip, boolean_t waitflag,
+ pid_t *event_ppid, pid_t *event_pid)
+{
+ int signo;
+
+ /*
+ * Populate our k_siginfo_t with data about this "ptrace-stop"
+ * condition:
+ */
+ bzero(ip, sizeof (*ip));
+ ip->si_signo = SIGCLD;
+ ip->si_pid = LX_PID_TO_INIT(remote->br_pid);
+ ip->si_code = CLD_TRAPPED;
+
+ switch (remote->br_ptrace_whatstop) {
+ case LX_PR_SYSENTRY:
+ case LX_PR_SYSEXIT:
+ ip->si_status = SIGTRAP;
+ if (remote->br_ptrace_options & LX_PTRACE_O_TRACESYSGOOD) {
+ ip->si_status |= 0x80;
+ }
+ break;
+
+ case LX_PR_SIGNALLED:
+ signo = remote->br_ptrace_stopsig;
+ if (signo < 1 || signo >= LX_NSIG) {
+ /*
+ * If this signal number is not valid, pretend it
+ * was a SIGTRAP.
+ */
+ ip->si_status = SIGTRAP;
+ } else {
+ ip->si_status = ltos_signo[signo];
+ }
+ break;
+
+ case LX_PR_EVENT:
+ ip->si_status = SIGTRAP | remote->br_ptrace_event;
+ /*
+ * Record the Linux pid of both this LWP and the create
+ * event we are dispatching. We will use this information
+ * to unblock any subsequent ptrace(2) events that depend
+ * on this one.
+ */
+ if (event_ppid != NULL)
+ *event_ppid = remote->br_pid;
+ if (event_pid != NULL)
+ *event_pid = (pid_t)remote->br_ptrace_eventmsg;
+ break;
+
+ default:
+ cmn_err(CE_PANIC, "unxpected stop subreason: %d",
+ remote->br_ptrace_whatstop);
+ }
+
+ /*
+ * If WNOWAIT was specified, do not mark the event as posted
+ * so that it may be re-fetched on another call to waitid().
+ */
+ if (waitflag)
+ remote->br_ptrace_flags &= ~(LX_PTF_CLDPEND | LX_PTF_WAITPEND);
+}
+
+/*
+ * Receive notification from stop() of a PR_BRAND stop.
+ */
+void
+lx_stop_notify(proc_t *p, klwp_t *lwp, ushort_t why, ushort_t what)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ lx_ptrace_accord_t *accord;
+ klwp_t *plwp = NULL;
+ proc_t *pp = NULL;
+ lx_lwp_data_t *parent;
+ boolean_t cldpend = B_TRUE;
+ boolean_t cldpost = B_FALSE;
+ sigqueue_t *sqp = NULL;
+
+ /*
+ * We currently only care about LX-specific stop reasons.
+ */
+ if (why != PR_BRAND)
+ return;
+
+ switch (what) {
+ case LX_PR_SYSENTRY:
+ case LX_PR_SYSEXIT:
+ case LX_PR_SIGNALLED:
+ case LX_PR_EVENT:
+ break;
+ default:
+ cmn_err(CE_PANIC, "unexpected subreason for PR_BRAND"
+ " stop: %d", (int)what);
+ }
+
+ /*
+ * We should be holding the lock on our containing process. The
+ * STOPPING flag should have been set by lx_ptrace_stop() for all
+ * PR_BRAND stops.
+ */
+ VERIFY(MUTEX_HELD(&p->p_lock));
+ VERIFY(lwpd->br_ptrace_flags & LX_PTF_STOPPING);
+ VERIFY((accord = lwpd->br_ptrace_tracer) != NULL);
+
+ /*
+ * We must drop our process lock to take "pidlock". The
+ * LX_PTF_STOPPING flag protects us from an exiting or detaching tracer.
+ */
+ mutex_exit(&p->p_lock);
+
+ /*
+ * Allocate before we enter any mutexes.
+ */
+ sqp = kmem_zalloc(sizeof (*sqp), KM_SLEEP);
+
+ /*
+ * We take pidlock now, which excludes all callers of waitid() and
+ * prevents an exiting tracer from clearing critical accord members.
+ */
+ mutex_enter(&pidlock);
+ mutex_enter(&p->p_lock);
+
+ /*
+ * Get the ptrace(2) "parent" process, to which we may send
+ * a SIGCLD signal later.
+ */
+ if ((parent = accord->lxpa_tracer) != NULL &&
+ (plwp = parent->br_lwp) != NULL) {
+ pp = lwptoproc(plwp);
+ }
+
+ /*
+ * Our tracer should not have been modified in our absence; the
+ * LX_PTF_STOPPING flag prevents it.
+ */
+ VERIFY(lwpd->br_ptrace_tracer == accord);
+
+ /*
+ * Stash data for this stop condition in the LWP data while we hold
+ * both pidlock and our p_lock.
+ */
+ lwpd->br_ptrace_whystop = why;
+ lwpd->br_ptrace_whatstop = what;
+ lwpd->br_ptrace_flags |= LX_PTF_WAITPEND;
+
+ /*
+ * If this event does not depend on an event from the parent LWP,
+ * populate the siginfo_t for the event pending on this tracee LWP.
+ */
+ if (!(lwpd->br_ptrace_flags & LX_PTF_PARENT_WAIT) && pp != NULL) {
+ cldpost = B_TRUE;
+ lx_ptrace_winfo(lwpd, &sqp->sq_info, B_FALSE, NULL, NULL);
+ }
+
+ /*
+ * Drop our p_lock so that we may lock the tracer.
+ */
+ mutex_exit(&p->p_lock);
+ if (cldpost && pp != NULL) {
+ /*
+ * Post the SIGCLD to the tracer.
+ */
+ mutex_enter(&pp->p_lock);
+ if (!sigismember(&pp->p_sig, SIGCLD)) {
+ sigaddqa(pp, plwp->lwp_thread, sqp);
+ cldpend = B_FALSE;
+ sqp = NULL;
+ }
+ mutex_exit(&pp->p_lock);
+ }
+
+ /*
+ * We re-take our process lock now. The lock will be held until
+ * the thread is actually marked stopped, so we will not race with
+ * lx_ptrace_lock_if_stopped() or lx_waitid_helper().
+ */
+ mutex_enter(&p->p_lock);
+
+ /*
+ * We clear the STOPPING flag; stop() continues to hold our p_lock
+ * until our thread stop state is visible.
+ */
+ lwpd->br_ptrace_flags &= ~LX_PTF_STOPPING;
+ lwpd->br_ptrace_flags |= LX_PTF_STOPPED;
+ if (cldpend) {
+ /*
+ * We sent the SIGCLD for this new wait condition already.
+ */
+ lwpd->br_ptrace_flags |= LX_PTF_CLDPEND;
+ }
+
+ /*
+ * If lx_ptrace_exit_tracer(), or a detach operation, is trying to
+ * detach our tracer, it will be sleeping on this CV until
+ * LX_PTF_STOPPING is clear. Wake it now.
+ */
+ cv_broadcast(&lx_ptrace_busy_cv);
+
+ /*
+ * While still holding pidlock, we attempt to wake our tracer from a
+ * potential waitid() slumber.
+ */
+ if (accord->lxpa_cvp != NULL) {
+ cv_broadcast(accord->lxpa_cvp);
+ }
+
+ /*
+ * We release pidlock and return as we were called: with our p_lock
+ * held.
+ */
+ mutex_exit(&pidlock);
+
+ if (sqp != NULL) {
+ kmem_free(sqp, sizeof (*sqp));
+ }
+}
+
+/*
+ * For any restarting action (e.g. PTRACE_CONT, PTRACE_SYSCALL or
+ * PTRACE_DETACH) to be allowed, the tracee LWP must be in "ptrace-stop". This
+ * check must ONLY be run on tracees of the current LWP. If the check is
+ * successful, we return with the tracee p_lock held.
+ *
+ * In the case of PTRACE_DETACH, we can return with the tracee locked even if
+ * it is not in "ptrace-stop". This can happen for various reasons, such as if
+ * the remote process is already job-stopped in the kernel. We must still be
+ * able to detach from this process. We return ENOENT in this case.
+ */
+static int
+lx_ptrace_lock_if_stopped(lx_ptrace_accord_t *accord, lx_lwp_data_t *remote,
+ boolean_t detaching)
+{
+ klwp_t *rlwp = remote->br_lwp;
+ proc_t *rproc = lwptoproc(rlwp);
+ kthread_t *rt = lwptot(rlwp);
+
+ /*
+ * We must never check that we, ourselves, are stopped. We must also
+ * have the accord tracee list locked while we lock our tracees.
+ */
+ VERIFY(curthread != rt);
+ VERIFY(MUTEX_HELD(&accord->lxpa_tracees_lock));
+ VERIFY(accord->lxpa_tracer == ttolxlwp(curthread));
+
+ /*
+ * Lock the process containing the tracee LWP.
+ */
+ mutex_enter(&rproc->p_lock);
+ if (!VISIBLE(remote)) {
+ /*
+ * The tracee LWP is currently detaching itself as it exits.
+ * It is no longer visible to ptrace(2).
+ */
+ mutex_exit(&rproc->p_lock);
+ return (ESRCH);
+ }
+
+ /*
+ * We must only check whether tracees of the current LWP are stopped.
+ * We check this condition after confirming visibility as an exiting
+ * tracee may no longer be completely consistent.
+ */
+ VERIFY(remote->br_ptrace_tracer == accord);
+
+ if (!(remote->br_ptrace_flags & LX_PTF_STOPPED)) {
+ if (detaching) {
+ /*
+ * The tracee is not in "ptrace-stop", but we still
+ * return with the locked process. This is indicated
+ * by ENOENT.
+ */
+ return (ENOENT);
+ }
+
+ /*
+ * The tracee is not in "ptrace-stop", so we release the
+ * process.
+ */
+ mutex_exit(&rproc->p_lock);
+ return (ESRCH);
+ }
+
+ /*
+ * The tracee is stopped. We return holding its process lock so that
+ * the caller may manipulate it.
+ */
+ return (0);
+}
+
+static int
+lx_ptrace_setoptions(lx_lwp_data_t *remote, uintptr_t options)
+{
+ /*
+ * Check for valid options.
+ */
+ if ((options & ~LX_PTRACE_O_ALL) != 0) {
+ return (EINVAL);
+ }
+
+ /*
+ * Set ptrace options on the target LWP.
+ */
+ remote->br_ptrace_options = (lx_ptrace_options_t)options;
+
+ return (0);
+}
+
+static int
+lx_ptrace_geteventmsg(lx_lwp_data_t *remote, void *umsgp)
+{
+ int error;
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ uint32_t tmp = remote->br_ptrace_eventmsg;
+
+ error = copyout(&tmp, umsgp, sizeof (uint32_t));
+ } else
+#endif
+ {
+ error = copyout(&remote->br_ptrace_eventmsg, umsgp,
+ sizeof (ulong_t));
+ }
+
+ return (error);
+}
+
+static int
+lx_ptrace_getsiginfo(lx_lwp_data_t *remote, void *usiginfo)
+{
+ klwp_t *lwp = remote->br_lwp;
+ int lx_sig;
+
+ lx_sig = lx_stol_signo(lwp->lwp_cursig, 0);
+ if (lx_sig < 1 || lwp->lwp_curinfo == NULL) {
+ return (EINVAL);
+ }
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ if (stol_ksiginfo32_copyout(&lwp->lwp_curinfo->sq_info,
+ usiginfo) != 0) {
+ return (EFAULT);
+ }
+ } else
+#endif
+ {
+ if (stol_ksiginfo_copyout(&lwp->lwp_curinfo->sq_info,
+ usiginfo) != 0) {
+ return (EFAULT);
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Implements the PTRACE_CONT subcommand of the Linux ptrace(2) interface.
+ */
+static int
+lx_ptrace_cont(lx_lwp_data_t *remote, lx_ptrace_cont_flags_t flags, int signo)
+{
+ klwp_t *lwp = remote->br_lwp;
+
+ if (flags & LX_PTC_SINGLESTEP) {
+ /*
+ * We do not currently support single-stepping.
+ */
+ lx_unsupported("PTRACE_SINGLESTEP not currently implemented");
+ return (EINVAL);
+ }
+
+ /*
+ * The tracer may choose to suppress the delivery of a signal, or
+ * select an alternative signal for delivery. If this is an
+ * appropriate ptrace(2) "signal-delivery-stop", br_ptrace_stopsig
+ * will be used as the new signal number.
+ *
+ * As with so many other aspects of the Linux ptrace(2) interface, this
+ * may fail silently if the state machine is not aligned correctly.
+ */
+ remote->br_ptrace_stopsig = signo;
+ remote->br_ptrace_donesig = 0;
+
+ /*
+ * Handle the syscall-stop flag if this is a PTRACE_SYSCALL restart:
+ */
+ if (flags & LX_PTC_SYSCALL) {
+ remote->br_ptrace_flags |= LX_PTF_SYSCALL;
+ } else {
+ remote->br_ptrace_flags &= ~LX_PTF_SYSCALL;
+ }
+
+ lx_ptrace_restart_lwp(lwp);
+
+ return (0);
+}
+
+/*
+ * Implements the PTRACE_DETACH subcommand of the Linux ptrace(2) interface.
+ *
+ * The LWP identified by the Linux pid "lx_pid" will, if it as a tracee of the
+ * current LWP, be detached and (optionally) set runnable.
+ */
+static void
+lx_ptrace_detach(lx_ptrace_accord_t *accord, lx_lwp_data_t *remote, int signo,
+ boolean_t restart)
+{
+ klwp_t *rlwp = remote->br_lwp;
+
+ /*
+ * The tracee LWP may have been in "ptrace-stop" (restart is true if
+ * that was the case). We now hold the tracee's p_lock.
+ * Detach the LWP from the accord and set it running.
+ */
+ VERIFY(!TRACEE_BUSY(remote));
+ VERIFY(MUTEX_HELD(&accord->lxpa_tracees_lock));
+ remote->br_ptrace_flags &= ~(LX_PTF_SYSCALL | LX_PTF_INHERIT);
+ VERIFY(list_link_active(&remote->br_ptrace_linkage));
+ list_remove(&accord->lxpa_tracees, remote);
+
+ remote->br_ptrace_attach = LX_PTA_NONE;
+ remote->br_ptrace_tracer = NULL;
+ remote->br_ptrace_flags = 0;
+
+ /*
+ * Decrement traced-lwp count for the process.
+ */
+ ASSERT(MUTEX_HELD(&rlwp->lwp_procp->p_lock));
+ VERIFY(ptolxproc(rlwp->lwp_procp)->l_ptrace-- >= 1);
+
+ /*
+ * The tracer may, as described in lx_ptrace_cont(), choose to suppress
+ * or modify the delivered signal.
+ */
+ remote->br_ptrace_stopsig = signo;
+ remote->br_ptrace_donesig = 0;
+
+ if (restart) {
+ lx_ptrace_restart_lwp(rlwp);
+ }
+}
+
+/*
+ * This routine implements the PTRACE_ATTACH operation of the Linux ptrace(2)
+ * interface.
+ *
+ * This LWP is requesting to be attached as a tracer to another LWP -- the
+ * tracee. If a ptrace accord to track the list of tracees has not yet been
+ * allocated, one will be allocated and attached to this LWP now.
+ *
+ * The "br_ptrace_tracer" on the tracee LWP is set to this accord, and the
+ * tracee LWP is then added to the "lxpa_tracees" list in the accord. We drop
+ * locks between these two phases; the only consumer of trace events from this
+ * accord is this LWP, which obviously cannot be running waitpid(2) at the same
+ * time as this call to ptrace(2).
+ */
+static int
+lx_ptrace_attach(pid_t lx_pid)
+{
+ int error = ESRCH;
+ /*
+ * Our (Tracer) LWP:
+ */
+ lx_ptrace_accord_t *accord;
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+ /*
+ * Remote (Tracee) LWP:
+ */
+ proc_t *rproc;
+ kthread_t *rthr;
+ klwp_t *rlwp;
+ lx_lwp_data_t *rlwpd;
+
+ if (lwpd->br_pid == lx_pid) {
+ /*
+ * We cannot trace ourselves.
+ */
+ return (EPERM);
+ }
+
+ /*
+ * Ensure that we have an accord and obtain a lock on it. This
+ * routine should not fail because the LWP cannot make ptrace(2) system
+ * calls after it has begun exiting.
+ */
+ VERIFY0(lwpd->br_ptrace_flags & LX_PTF_EXITING);
+ VERIFY(lx_ptrace_accord_get(&accord, B_TRUE) == 0);
+
+ /*
+ * Place speculative hold in case the attach is successful.
+ */
+ lx_ptrace_accord_hold(accord);
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * Locate the process containing the tracee LWP based on its Linux pid
+ * and lock it.
+ */
+ if (lx_lpid_lock(lx_pid, curzone, LXP_PRLOCK, &rproc, &rthr) != 0) {
+ /*
+ * We could not find the target process.
+ */
+ goto errout;
+ }
+
+ /*
+ * Locate the tracee LWP.
+ */
+ if ((rlwp = ttolwp(rthr)) == NULL ||
+ (rlwpd = lwptolxlwp(rlwp)) == NULL ||
+ !VISIBLE(rlwpd)) {
+ /*
+ * The LWP could not be found, was not branded, or is not
+ * visible to ptrace(2) at this time.
+ */
+ goto unlock_errout;
+ }
+
+ /*
+ * We now hold the lock on the tracee. Attempt to install ourselves
+ * as the tracer.
+ */
+ if (curproc != rproc && priv_proc_cred_perm(curproc->p_cred, rproc,
+ NULL, VWRITE) != 0) {
+ /*
+ * This process does not have permission to trace the remote
+ * process.
+ */
+ error = EPERM;
+ } else if (rlwpd->br_ptrace_tracer != NULL) {
+ /*
+ * This LWP is already being traced.
+ */
+ VERIFY(list_link_active(&rlwpd->br_ptrace_linkage));
+ VERIFY(rlwpd->br_ptrace_attach != LX_PTA_NONE);
+ error = EPERM;
+ } else {
+ lx_proc_data_t *rprocd = ptolxproc(rproc);
+
+ /*
+ * Bond the tracee to the accord.
+ */
+ VERIFY0(rlwpd->br_ptrace_flags & LX_PTF_EXITING);
+ VERIFY(rlwpd->br_ptrace_attach == LX_PTA_NONE);
+ rlwpd->br_ptrace_attach = LX_PTA_ATTACH;
+ rlwpd->br_ptrace_tracer = accord;
+
+ /* Don't emit ptrace syscall-stop-exit event on kernel exit. */
+ rlwpd->br_ptrace_flags |= LX_PTF_NOSTOP;
+
+ /*
+ * We had no tracer, and are thus not in the tracees list.
+ * It is safe to take the tracee list lock while we insert
+ * ourselves.
+ */
+ mutex_enter(&accord->lxpa_tracees_lock);
+ VERIFY(!list_link_active(&rlwpd->br_ptrace_linkage));
+ list_insert_tail(&accord->lxpa_tracees, rlwpd);
+ /*
+ * Bump traced-lwp count for the remote process.
+ */
+ rprocd->l_ptrace++;
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Send a thread-directed SIGSTOP.
+ */
+ sigtoproc(rproc, rthr, SIGSTOP);
+
+
+ error = 0;
+ }
+
+unlock_errout:
+ /*
+ * Unlock the process containing the tracee LWP and the accord.
+ */
+ sprunlock(rproc);
+
+errout:
+ if (error != 0) {
+ /*
+ * The attach was not successful. Remove our speculative
+ * hold.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+ }
+
+ return (error);
+}
+
+int
+lx_ptrace_set_clone_inherit(int option, boolean_t inherit_flag)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = lwptoproc(lwp);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ switch (option) {
+ case LX_PTRACE_O_TRACEFORK:
+ case LX_PTRACE_O_TRACEVFORK:
+ case LX_PTRACE_O_TRACECLONE:
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ mutex_enter(&p->p_lock);
+
+ lwpd->br_ptrace_clone_option = option;
+
+ if (inherit_flag) {
+ lwpd->br_ptrace_flags |= LX_PTF_INHERIT;
+ } else {
+ lwpd->br_ptrace_flags &= ~LX_PTF_INHERIT;
+ }
+
+ mutex_exit(&p->p_lock);
+ return (0);
+}
+
+/*
+ * If the parent LWP is being traced, we want to attach ourselves to the
+ * same accord.
+ */
+void
+lx_ptrace_inherit_tracer(lx_lwp_data_t *src, lx_lwp_data_t *dst)
+{
+ proc_t *srcp = lwptoproc(src->br_lwp);
+ proc_t *dstp = lwptoproc(dst->br_lwp);
+ lx_ptrace_accord_t *accord;
+ boolean_t is_fork = B_FALSE;
+
+ VERIFY(MUTEX_HELD(&dstp->p_lock));
+ if (srcp != dstp) {
+ /*
+ * In the case of being called via forklwp, some lock shuffling
+ * is required. The destination p_lock must be dropped to
+ * avoid deadlocks when locking the source and manipulating
+ * ptrace accord resources.
+ */
+ is_fork = B_TRUE;
+ sprlock_proc(dstp);
+ mutex_exit(&dstp->p_lock);
+ mutex_enter(&srcp->p_lock);
+ }
+
+ if ((accord = src->br_ptrace_tracer) == NULL) {
+ /*
+ * The source LWP does not have a tracer to inherit.
+ */
+ goto out;
+ }
+
+ /*
+ * There are two conditions to check when determining if the new
+ * child should inherit the same tracer (and tracing options) as its
+ * parent. Either condition is sufficient to trigger inheritance.
+ */
+ dst->br_ptrace_attach = LX_PTA_NONE;
+ if ((src->br_ptrace_options & src->br_ptrace_clone_option) != 0) {
+ /*
+ * Condition 1:
+ * The clone(2), fork(2) and vfork(2) emulated system calls
+ * populate "br_ptrace_clone_option" with the specific
+ * ptrace(2) SETOPTIONS option that applies to this
+ * operation. If the relevant option has been enabled by the
+ * tracer then we inherit.
+ */
+ dst->br_ptrace_attach |= LX_PTA_INHERIT_OPTIONS;
+
+ } else if ((src->br_ptrace_flags & LX_PTF_INHERIT) != 0) {
+ /*
+ * Condition 2:
+ * If the caller opted in to inheritance with the
+ * PTRACE_CLONE flag to clone(2), the LX_PTF_INHERIT flag
+ * will be set and we inherit.
+ */
+ dst->br_ptrace_attach |= LX_PTA_INHERIT_CLONE;
+ }
+
+ /*
+ * These values only apply for the duration of a single clone(2), et
+ * al, system call.
+ */
+ src->br_ptrace_flags &= ~LX_PTF_INHERIT;
+ src->br_ptrace_clone_option = 0;
+
+ if (dst->br_ptrace_attach == LX_PTA_NONE) {
+ /*
+ * No condition triggered inheritance.
+ */
+ goto out;
+ }
+
+ /*
+ * Set the LX_PTF_CLONING flag to prevent us from being detached
+ * while our p_lock is dropped.
+ */
+ src->br_ptrace_flags |= LX_PTF_CLONING;
+ mutex_exit(&srcp->p_lock);
+
+ /*
+ * Hold the accord for the new LWP.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_hold(accord);
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * Install the tracer and copy the current PTRACE_SETOPTIONS options.
+ */
+ dst->br_ptrace_tracer = accord;
+ dst->br_ptrace_options = src->br_ptrace_options;
+
+ /*
+ * This flag prevents waitid() from seeing events for the new child
+ * until the parent is able to post the relevant ptrace event to
+ * the tracer.
+ */
+ dst->br_ptrace_flags |= LX_PTF_PARENT_WAIT;
+
+ mutex_enter(&accord->lxpa_tracees_lock);
+ VERIFY(list_link_active(&src->br_ptrace_linkage));
+ VERIFY(!list_link_active(&dst->br_ptrace_linkage));
+ list_insert_tail(&accord->lxpa_tracees, dst);
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Relock our process and clear our busy flag.
+ */
+ mutex_enter(&srcp->p_lock);
+ src->br_ptrace_flags &= ~LX_PTF_CLONING;
+
+ /*
+ * Bump traced-lwp count for the process.
+ */
+ ptolxproc(dstp)->l_ptrace++;
+
+ /*
+ * If lx_ptrace_exit_tracer(), or a detach operation, is trying to
+ * detach our tracer, it will be sleeping on this CV until
+ * LX_PTF_CLONING is clear. Wake it now.
+ */
+ cv_broadcast(&lx_ptrace_busy_cv);
+
+out:
+ if (is_fork) {
+ mutex_exit(&srcp->p_lock);
+ mutex_enter(&dstp->p_lock);
+ sprunprlock(dstp);
+ }
+}
+
+static int
+lx_ptrace_traceme(void)
+{
+ int error;
+ boolean_t did_attach = B_FALSE;
+ /*
+ * Our (Tracee) LWP:
+ */
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = lwptoproc(lwp);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ /*
+ * Remote (Tracer) LWP:
+ */
+ lx_ptrace_accord_t *accord;
+
+ /*
+ * We are intending to be the tracee. Fetch (or allocate) the accord
+ * for our parent LWP.
+ */
+ if ((error = lx_ptrace_accord_get_by_pid(lx_lwp_ppid(lwp, NULL,
+ NULL), &accord)) != 0) {
+ /*
+ * Could not determine the Linux pid of the parent LWP, or
+ * could not get the accord for that LWP.
+ */
+ return (error);
+ }
+
+ /*
+ * We now hold the accord lock.
+ */
+ if (accord->lxpa_flags & LX_ACC_TOMBSTONE) {
+ /*
+ * The accord is marked for death; give up now.
+ */
+ lx_ptrace_accord_exit(accord);
+ return (ESRCH);
+ }
+
+ /*
+ * Bump the reference count so that the accord is not freed. We need
+ * to drop the accord lock before we take our own p_lock.
+ */
+ lx_ptrace_accord_hold(accord);
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * We now lock _our_ process and determine if we can install our parent
+ * as our tracer.
+ */
+ mutex_enter(&p->p_lock);
+ if (lwpd->br_ptrace_tracer != NULL) {
+ /*
+ * This LWP is already being traced.
+ */
+ VERIFY(lwpd->br_ptrace_attach != LX_PTA_NONE);
+ error = EPERM;
+ } else {
+ /*
+ * Bond ourselves to the accord. We already bumped the accord
+ * reference count.
+ */
+ VERIFY(lwpd->br_ptrace_attach == LX_PTA_NONE);
+ lwpd->br_ptrace_attach = LX_PTA_TRACEME;
+ lwpd->br_ptrace_tracer = accord;
+ did_attach = B_TRUE;
+ error = 0;
+
+ /*
+ * Speculatively bump l_ptrace now before dropping p_lock.
+ * It will be reverted if the tracee attachment fails.
+ */
+ ptolxproc(p)->l_ptrace++;
+ }
+ mutex_exit(&p->p_lock);
+
+ /*
+ * Lock the accord tracee list and add this LWP. Once we are in the
+ * tracee list, it is the responsibility of the tracer to detach us.
+ */
+ if (error == 0) {
+ lx_ptrace_accord_enter(accord);
+ mutex_enter(&accord->lxpa_tracees_lock);
+
+ if (!(accord->lxpa_flags & LX_ACC_TOMBSTONE)) {
+ /*
+ * Put ourselves in the tracee list for this accord.
+ */
+ VERIFY(!list_link_active(&lwpd->br_ptrace_linkage));
+ list_insert_tail(&accord->lxpa_tracees, lwpd);
+ mutex_exit(&accord->lxpa_tracees_lock);
+ lx_ptrace_accord_exit(accord);
+
+ return (0);
+ }
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * The accord has been marked for death. We must
+ * untrace ourselves.
+ */
+ error = ESRCH;
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * Undo speculative increment of ptracer count.
+ */
+ mutex_enter(&p->p_lock);
+ ptolxproc(p)->l_ptrace--;
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Our optimism was unjustified: We were unable to attach. We need to
+ * lock the process containing this LWP again in order to remove the
+ * tracer.
+ */
+ VERIFY(error != 0);
+ mutex_enter(&p->p_lock);
+ if (did_attach) {
+ /*
+ * Verify that things were as we left them:
+ */
+ VERIFY(!list_link_active(&lwpd->br_ptrace_linkage));
+ VERIFY(lwpd->br_ptrace_tracer == accord);
+
+ lwpd->br_ptrace_attach = LX_PTA_NONE;
+ lwpd->br_ptrace_tracer = NULL;
+ }
+ mutex_exit(&p->p_lock);
+
+ /*
+ * Remove our speculative hold on the accord, possibly causing it to be
+ * freed in the process.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+
+ return (error);
+}
+
+static boolean_t
+lx_ptrace_stop_common(proc_t *p, lx_lwp_data_t *lwpd, ushort_t what)
+{
+ boolean_t reset_nostop = B_FALSE;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * Mark this LWP as stopping and call stop() to enter "ptrace-stop".
+ */
+ VERIFY0(lwpd->br_ptrace_flags & LX_PTF_STOPPING);
+ lwpd->br_ptrace_flags |= LX_PTF_STOPPING;
+
+ if (lwpd->br_lwp->lwp_nostop == 1 &&
+ lwpd->br_ptrace_event == LX_PTRACE_EVENT_EXEC) {
+ /* We need to clear this to get the signal delivered. */
+ lwpd->br_lwp->lwp_nostop = 0;
+ reset_nostop = B_TRUE;
+ }
+
+ stop(PR_BRAND, what);
+
+ if (reset_nostop) {
+ VERIFY(lwpd->br_lwp->lwp_nostop == 0);
+ lwpd->br_lwp->lwp_nostop = 1;
+ }
+
+ /*
+ * We are back from "ptrace-stop" with our process lock held.
+ */
+ lwpd->br_ptrace_flags &= ~(LX_PTF_STOPPING | LX_PTF_STOPPED |
+ LX_PTF_CLDPEND);
+ lwpd->br_ptrace_stopucp = (uintptr_t)NULL;
+ cv_broadcast(&lx_ptrace_busy_cv);
+ mutex_exit(&p->p_lock);
+
+ return (B_TRUE);
+}
+
+int
+lx_ptrace_stop_for_option(int option, boolean_t child, ulong_t msg,
+ uintptr_t ucp)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = lwptoproc(lwp);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ mutex_enter(&p->p_lock);
+ if (lwpd->br_ptrace_tracer == NULL) {
+ mutex_exit(&p->p_lock);
+ return (ESRCH);
+ }
+
+ if (!child) {
+ /*
+ * Only the first event posted by a new process is to be held
+ * until the matching parent event is dispatched, and only if
+ * it is a "child" event. This is not a child event, so we
+ * clear the wait flag.
+ */
+ lwpd->br_ptrace_flags &= ~LX_PTF_PARENT_WAIT;
+
+ } else if (option == LX_PTRACE_O_TRACEVFORK) {
+ /*
+ * For a child, we have to handle vfork as a special case. In
+ * lx_ptrace_inherit_tracer() we set LX_PTF_PARENT_WAIT to
+ * force events to be delayed until the parent posts its event.
+ * This flag is cleared in lx_waitid_helper() to enforce a
+ * "happens after" relationship. However, this obviously cannot
+ * work for the vfork case. Thus, we clear our flag now so that
+ * we can deliver the signal in lx_stop_notify(), if necessary.
+ */
+ lwpd->br_ptrace_flags &= ~LX_PTF_PARENT_WAIT;
+ }
+
+ if (!(lwpd->br_ptrace_options & option)) {
+ if (option == LX_PTRACE_O_TRACEEXEC) {
+ /*
+ * Without PTRACE_O_TRACEEXEC, the Linux kernel will
+ * send SIGTRAP to the process.
+ */
+ sigtoproc(p, t, SIGTRAP);
+ mutex_exit(&p->p_lock);
+ return (0);
+ }
+
+ /*
+ * The flag for this trace event is not enabled, so we will not
+ * stop.
+ */
+ mutex_exit(&p->p_lock);
+ return (ESRCH);
+ }
+
+ if (child) {
+ switch (option) {
+ case LX_PTRACE_O_TRACECLONE:
+ case LX_PTRACE_O_TRACEFORK:
+ case LX_PTRACE_O_TRACEVFORK:
+ /*
+ * Send the child LWP a directed SIGSTOP.
+ */
+ sigtoproc(p, t, SIGSTOP);
+ mutex_exit(&p->p_lock);
+ return (0);
+ default:
+ goto nostop;
+ }
+ }
+
+ lwpd->br_ptrace_eventmsg = msg;
+
+ switch (option) {
+ case LX_PTRACE_O_TRACECLONE:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_CLONE;
+ break;
+ case LX_PTRACE_O_TRACEEXEC:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_EXEC;
+ lwpd->br_ptrace_eventmsg = 0;
+ break;
+ case LX_PTRACE_O_TRACEEXIT:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_EXIT;
+ break;
+ case LX_PTRACE_O_TRACEFORK:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_FORK;
+ break;
+ case LX_PTRACE_O_TRACEVFORK:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_VFORK;
+ break;
+ case LX_PTRACE_O_TRACEVFORKDONE:
+ lwpd->br_ptrace_event = LX_PTRACE_EVENT_VFORK_DONE;
+ lwpd->br_ptrace_eventmsg = 0;
+ break;
+ default:
+ goto nostop;
+ }
+
+ /*
+ * Userland may have passed in a ucontext_t pointer for
+ * PTRACE_GETREGS/PTRACE_SETREGS usage while stopped.
+ */
+ lwpd->br_ptrace_stopucp = ucp;
+
+ /*
+ * p_lock for the process containing the tracee will be dropped by
+ * lx_ptrace_stop_common().
+ */
+ return (lx_ptrace_stop_common(p, lwpd, LX_PR_EVENT) ? 0 : ESRCH);
+
+nostop:
+ lwpd->br_ptrace_event = 0;
+ lwpd->br_ptrace_eventmsg = 0;
+ mutex_exit(&p->p_lock);
+ return (ESRCH);
+}
+
+boolean_t
+lx_ptrace_stop(ushort_t what)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = lwptoproc(lwp);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ VERIFY(what == LX_PR_SYSENTRY || what == LX_PR_SYSEXIT ||
+ what == LX_PR_SIGNALLED);
+
+ /*
+ * If we do not have an accord, bail out early.
+ */
+ if (lwpd->br_ptrace_tracer == NULL)
+ return (B_FALSE);
+
+ /*
+ * Lock this process and re-check the condition.
+ */
+ mutex_enter(&p->p_lock);
+
+ /*
+ * The child after a fork/clone doesn't emit syscall-exit-stop event.
+ */
+ if (what == LX_PR_SYSEXIT && (lwpd->br_ptrace_flags & LX_PTF_NOSTOP)) {
+ lwpd->br_ptrace_flags &= ~LX_PTF_NOSTOP;
+ mutex_exit(&p->p_lock);
+ return (B_FALSE);
+ }
+
+ if (lwpd->br_ptrace_tracer == NULL) {
+ VERIFY0(lwpd->br_ptrace_flags & LX_PTF_SYSCALL);
+ mutex_exit(&p->p_lock);
+ return (B_FALSE);
+ }
+
+ if (what == LX_PR_SYSENTRY || what == LX_PR_SYSEXIT) {
+ if (what == LX_PR_SYSENTRY) {
+ lwpd->br_ptrace_flags |= LX_PTF_INSYSCALL;
+ } else {
+ lwpd->br_ptrace_flags &= ~LX_PTF_INSYSCALL;
+ }
+
+ /*
+ * This is a syscall-entry-stop or syscall-exit-stop point.
+ */
+ if (!(lwpd->br_ptrace_flags & LX_PTF_SYSCALL)) {
+ /*
+ * A system call stop has not been requested.
+ */
+ mutex_exit(&p->p_lock);
+ return (B_FALSE);
+ }
+
+ /*
+ * The PTRACE_SYSCALL restart command applies only to the next
+ * system call entry or exit. The tracer must restart us with
+ * PTRACE_SYSCALL while we are in ptrace-stop for us to fire
+ * again at the next system call boundary.
+ */
+ lwpd->br_ptrace_flags &= ~LX_PTF_SYSCALL;
+ }
+
+ /*
+ * p_lock for the process containing the tracee will be dropped by
+ * lx_ptrace_stop_common().
+ */
+ return (lx_ptrace_stop_common(p, lwpd, what));
+}
+
+/*
+ * In addition to performing the ptrace sig_stop handling, this function is
+ * also used to block signal from being delivered.
+ *
+ * Return 0 if issig_forreal() should continue on, -1 if issig_forreal should
+ * recheck after we've made changes, or 1 if issig_forreal should stop checking
+ * signals.
+ */
+int
+lx_ptrace_issig_stop(proc_t *p, klwp_t *lwp)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ int lx_sig;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ if (ptolxproc(p)->l_block_all_signals != 0)
+ return (1);
+
+ /*
+ * In very rare circumstances, a process which is almost completely
+ * through proc_exit() may incur issig checks in the current thread via
+ * clean-up actions. The process will still be branded, but the thread
+ * will have already been stripped of any LX-specific data on its way
+ * to the grave. Bail early if the brand data is missing.
+ */
+ if (lwpd == NULL) {
+ return (0);
+ }
+
+ /*
+ * If we do not have an accord, bail out now. Additionally, if there
+ * is no valid signal then we have no reason to stop.
+ */
+ if (lwpd->br_ptrace_tracer == NULL || lwp->lwp_cursig == SIGKILL ||
+ (lwp->lwp_cursig == 0 || lwp->lwp_cursig > NSIG) ||
+ (lx_sig = stol_signo[lwp->lwp_cursig]) < 1) {
+ if (lwp->lwp_cursig == 0) {
+ /*
+ * If this lwp has no current signal, it means that any
+ * signal ignorance enabled by br_ptrace_donesig has
+ * already taken place (the signal was consumed).
+ * By clearing donesig, we declare desire to ignore no
+ * signals for accurate ptracing.
+ */
+ lwpd->br_ptrace_donesig = 0;
+ }
+ return (0);
+ }
+
+ /*
+ * We can't deliver the signal-delivery-stop condition while we're
+ * between the syscall-enter-stop and syscall-exit-stop conditions.
+ * We must first let the signal interrupt the in-progress syscall, let
+ * it emit syscall-exit-stop with the interrupted result, then we'll
+ * come back here to emit signal-delivery-stop.
+ */
+ if (lwpd->br_ptrace_flags & LX_PTF_INSYSCALL) {
+ return (0);
+ }
+
+ /*
+ * We stash the signal on the LWP where our waitid_helper will find it
+ * and enter the ptrace "signal-delivery-stop" condition.
+ */
+ lwpd->br_ptrace_stopsig = lx_sig;
+ lwpd->br_ptrace_donesig = 0;
+ (void) lx_ptrace_stop_common(p, lwpd, LX_PR_SIGNALLED);
+ mutex_enter(&p->p_lock);
+
+ /*
+ * When we return, the signal may have been altered or suppressed.
+ */
+ if (lwpd->br_ptrace_stopsig != lx_sig) {
+ int native_sig;
+ lx_sig = lwpd->br_ptrace_stopsig;
+
+ if (lx_sig >= LX_NSIG) {
+ lx_sig = 0;
+ }
+
+ /*
+ * Translate signal from Linux signal number back to
+ * an illumos native signal.
+ */
+ if (lx_sig >= LX_NSIG || lx_sig < 0 || (native_sig =
+ ltos_signo[lx_sig]) < 1) {
+ /*
+ * The signal is not deliverable.
+ */
+ lwp->lwp_cursig = 0;
+ lwp->lwp_extsig = 0;
+ if (lwp->lwp_curinfo) {
+ siginfofree(lwp->lwp_curinfo);
+ lwp->lwp_curinfo = NULL;
+ }
+ } else {
+ /*
+ * Alter the currently dispatching signal.
+ */
+ if (native_sig == SIGKILL) {
+ /*
+ * We mark ourselves the victim and request
+ * a restart of signal processing.
+ */
+ p->p_flag |= SKILLED;
+ p->p_flag &= ~SEXTKILLED;
+ return (-1);
+ }
+ lwp->lwp_cursig = native_sig;
+ lwp->lwp_extsig = 0;
+ if (lwp->lwp_curinfo != NULL) {
+ lwp->lwp_curinfo->sq_info.si_signo = native_sig;
+ }
+ }
+ }
+
+ lwpd->br_ptrace_donesig = lwp->lwp_cursig;
+ lwpd->br_ptrace_stopsig = 0;
+ return (0);
+}
+
+boolean_t
+lx_ptrace_sig_ignorable(proc_t *p, klwp_t *lwp, int sig)
+{
+ lx_proc_data_t *lxpd = ptolxproc(p);
+
+ /*
+ * Ignored signals and ptrace:
+ *
+ * When a process is being ptraced by another, special care is needed
+ * while handling signals. Since the tracer is interested in all
+ * signals sent to the tracee, an effort must be made to initially
+ * bypass signal ignorance logic. This allows the signal to be placed
+ * in the tracee's sigqueue to be inspected and potentially altered by
+ * the tracer.
+ *
+ * A critical detail in this procedure is how a signal is handled after
+ * tracer has completed processing for the event. If the signal would
+ * have been ignored, were it not for the initial ptrace override, then
+ * lx_ptrace_sig_ignorable must report B_TRUE when the tracee is
+ * restarted and resumes signal processing. This is done by recording
+ * the most recent tracee signal consumed by ptrace.
+ */
+
+ if (lxpd->l_ptrace != 0 && lx_stol_signo(sig, 0) != 0) {
+ /*
+ * This process is being ptraced. Bypass signal ignorance for
+ * anything that maps to a valid Linux signal...
+ */
+ if (lwp != NULL && lwptolxlwp(lwp)->br_ptrace_donesig == sig) {
+ /*
+ * ...Unless it is a signal which has already been
+ * processed by the tracer.
+ */
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+ }
+ return (B_TRUE);
+}
+
+static void
+lx_ptrace_exit_tracer(proc_t *p, lx_lwp_data_t *lwpd,
+ lx_ptrace_accord_t *accord)
+{
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ lx_ptrace_accord_enter(accord);
+ /*
+ * Mark this accord for death. This means no new tracees can be
+ * attached to this accord.
+ */
+ VERIFY0(accord->lxpa_flags & LX_ACC_TOMBSTONE);
+ accord->lxpa_flags |= LX_ACC_TOMBSTONE;
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * Walk the list of tracees, detaching them and setting them runnable
+ * if they are stopped.
+ */
+ for (;;) {
+ klwp_t *rlwp;
+ proc_t *rproc;
+ lx_lwp_data_t *remote;
+ kmutex_t *rmp;
+
+ mutex_enter(&accord->lxpa_tracees_lock);
+ if (list_is_empty(&accord->lxpa_tracees)) {
+ mutex_exit(&accord->lxpa_tracees_lock);
+ break;
+ }
+
+ /*
+ * Fetch the first tracee LWP in the list and lock the process
+ * which contains it.
+ */
+ remote = list_head(&accord->lxpa_tracees);
+ rlwp = remote->br_lwp;
+ rproc = lwptoproc(rlwp);
+ /*
+ * The p_lock mutex persists beyond the life of the process
+ * itself. We save the address, here, to prevent the need to
+ * dereference the proc_t after awaking from sleep.
+ */
+ rmp = &rproc->p_lock;
+ mutex_enter(rmp);
+
+ if (TRACEE_BUSY(remote)) {
+ /*
+ * This LWP is currently detaching itself on exit, or
+ * mid-way through stop(). We must wait for this
+ * action to be completed. While we wait on the CV, we
+ * must drop the accord tracee list lock.
+ */
+ mutex_exit(&accord->lxpa_tracees_lock);
+ cv_wait(&lx_ptrace_busy_cv, rmp);
+
+ /*
+ * While we were waiting, some state may have changed.
+ * Restart the walk to be sure we don't miss anything.
+ */
+ mutex_exit(rmp);
+ continue;
+ }
+
+ /*
+ * We now hold p_lock on the process. Remove the tracee from
+ * the list.
+ */
+ VERIFY(list_link_active(&remote->br_ptrace_linkage));
+ list_remove(&accord->lxpa_tracees, remote);
+
+ /*
+ * Unlink the accord and clear our trace flags.
+ */
+ remote->br_ptrace_attach = LX_PTA_NONE;
+ remote->br_ptrace_tracer = NULL;
+ remote->br_ptrace_flags = 0;
+
+ /*
+ * Let go of the list lock before we restart the LWP. We must
+ * not hold any locks other than the process p_lock when
+ * we call lx_ptrace_restart_lwp() as it will thread_lock
+ * the tracee.
+ */
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Decrement traced-lwp count for the remote process.
+ */
+ VERIFY(ptolxproc(rproc)->l_ptrace-- >= 1);
+
+ /*
+ * Ensure that the LWP is not stopped on our account.
+ */
+ lx_ptrace_restart_lwp(rlwp);
+
+ /*
+ * Unlock the former tracee.
+ */
+ mutex_exit(rmp);
+
+ /*
+ * Drop the hold this tracee had on the accord.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+ }
+
+ mutex_enter(&p->p_lock);
+ lwpd->br_ptrace_accord = NULL;
+ mutex_exit(&p->p_lock);
+
+ /*
+ * Clean up and release our hold on the accord If we completely
+ * detached all tracee LWPs, this will free the accord. Otherwise, it
+ * will be freed when they complete their cleanup.
+ *
+ * We hold "pidlock" while clearing these members for easy exclusion of
+ * waitid(), etc.
+ */
+ mutex_enter(&pidlock);
+ lx_ptrace_accord_enter(accord);
+ accord->lxpa_cvp = NULL;
+ accord->lxpa_tracer = NULL;
+ mutex_exit(&pidlock);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+}
+
+static void
+lx_ptrace_exit_tracee(proc_t *p, lx_lwp_data_t *lwpd)
+{
+ lx_ptrace_accord_t *accord;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * Be careful in the face of detaching and attaching tracers.
+ * lwpd->br_ptrace_tracer is modified only when p->p_lock is held. Lock
+ * ordering says that accord->lxpa_tracees_lock must be taken prior to
+ * p->p_lock, so we have to get a reference to the accord and hold it
+ * across dropping p->p_lock.
+ *
+ * In the face of a tracer going away and a new one coming in, we may
+ * take a lap.
+ */
+again:
+ if ((accord = lwpd->br_ptrace_tracer) == NULL) {
+ return;
+ }
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_hold(accord);
+ lx_ptrace_accord_exit(accord);
+ mutex_exit(&p->p_lock);
+
+ /*
+ * We are the tracee LWP. Lock the accord tracee list and then our
+ * containing process.
+ */
+ mutex_enter(&accord->lxpa_tracees_lock);
+ mutex_enter(&p->p_lock);
+
+ /*
+ * Be sure that the accord currently associated with the lwp is the one
+ * for which we are holding lxpa_tracees_lock.
+ */
+ if (lwpd->br_ptrace_tracer != accord) {
+ mutex_exit(&p->p_lock);
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+
+ mutex_enter(&p->p_lock);
+
+ goto again;
+ }
+
+ /*
+ * Remove our reference to the accord. We will release our hold
+ * later.
+ */
+ lwpd->br_ptrace_attach = LX_PTA_NONE;
+ lwpd->br_ptrace_tracer = NULL;
+
+ /*
+ * Remove this LWP from the accord tracee list:
+ */
+ VERIFY(list_link_active(&lwpd->br_ptrace_linkage));
+ list_remove(&accord->lxpa_tracees, lwpd);
+
+ /*
+ * Wake up any tracers waiting for us to detach from the accord.
+ */
+ cv_broadcast(&lx_ptrace_busy_cv);
+
+ /*
+ * Decrement traced-lwp count for the process.
+ */
+ VERIFY(ptolxproc(p)->l_ptrace-- >= 1);
+
+ mutex_exit(&p->p_lock);
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Grab "pidlock" and wake the tracer if it is blocked in waitid().
+ */
+ mutex_enter(&pidlock);
+ if (accord->lxpa_cvp != NULL) {
+ cv_broadcast(accord->lxpa_cvp);
+ }
+ mutex_exit(&pidlock);
+
+ /*
+ * Release the holds on the accord. One is the hold taken earlier in
+ * this function and the other is lwpd's hold.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+
+ mutex_enter(&p->p_lock);
+}
+
+/*
+ * This routine is called from lx_exitlwp() when an LWP is ready to exit. If
+ * this LWP is being traced, it will be detached from the tracer's accord. The
+ * routine will also detach any LWPs being traced by this LWP.
+ */
+void
+lx_ptrace_exit(proc_t *p, klwp_t *lwp)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ lx_ptrace_accord_t *accord;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * Mark our LWP as exiting from a ptrace perspective. This will
+ * prevent a new accord from being allocated if one does not exist
+ * already, and will make us invisible to PTRACE_ATTACH/PTRACE_TRACEME.
+ */
+ VERIFY0(lwpd->br_ptrace_flags & LX_PTF_EXITING);
+ lwpd->br_ptrace_flags |= LX_PTF_EXITING;
+
+ if (lwpd->br_ptrace_tracer != NULL) {
+ /*
+ * We are traced by another LWP and must detach ourselves.
+ */
+ lx_ptrace_exit_tracee(p, lwpd);
+ VERIFY(MUTEX_HELD(&p->p_lock));
+ }
+
+ if ((accord = lwpd->br_ptrace_accord) != NULL) {
+ /*
+ * We have been tracing other LWPs, and must detach from
+ * them and clean up our accord.
+ */
+ mutex_exit(&p->p_lock);
+ lx_ptrace_exit_tracer(p, lwpd, accord);
+ mutex_enter(&p->p_lock);
+ }
+}
+
+/*
+ * Called when a SIGCLD signal is dispatched so that we may enqueue another.
+ * Return 0 if we enqueued a signal, or -1 if not.
+ */
+int
+lx_sigcld_repost(proc_t *pp, sigqueue_t *sqp)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ lx_ptrace_accord_t *accord;
+ lx_lwp_data_t *remote;
+ klwp_t *rlwp;
+ proc_t *rproc;
+ boolean_t found = B_FALSE;
+
+ VERIFY(MUTEX_HELD(&pidlock));
+ VERIFY(MUTEX_NOT_HELD(&pp->p_lock));
+ VERIFY(lwptoproc(lwp) == pp);
+
+ mutex_enter(&pp->p_lock);
+ if ((accord = lwpd->br_ptrace_accord) == NULL) {
+ /*
+ * This LWP is not a tracer LWP, so there will be no
+ * SIGCLD.
+ */
+ mutex_exit(&pp->p_lock);
+ return (-1);
+ }
+ mutex_exit(&pp->p_lock);
+
+ mutex_enter(&accord->lxpa_tracees_lock);
+ for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+ remote = list_next(&accord->lxpa_tracees, remote)) {
+ rlwp = remote->br_lwp;
+ rproc = lwptoproc(rlwp);
+
+ /*
+ * Check if this LWP is in "ptrace-stop". If in the correct
+ * stop condition, lock the process containing the tracee LWP.
+ */
+ if (lx_ptrace_lock_if_stopped(accord, remote, B_FALSE) != 0) {
+ continue;
+ }
+
+ if (remote->br_ptrace_flags & LX_PTF_PARENT_WAIT) {
+ /*
+ * This event depends on waitid() clearing out the
+ * event of another LWP. Skip it for now.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ if (!(remote->br_ptrace_flags & LX_PTF_CLDPEND)) {
+ /*
+ * No SIGCLD is required for this LWP.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ if (!(remote->br_ptrace_flags & LX_PTF_WAITPEND) ||
+ remote->br_ptrace_whystop == 0 ||
+ remote->br_ptrace_whatstop == 0) {
+ /*
+ * No (new) stop reason to post for this LWP.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ /*
+ * We found a process of interest. Leave the process
+ * containing the tracee LWP locked and break out of the loop.
+ */
+ found = B_TRUE;
+ break;
+ }
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ if (!found) {
+ return (-1);
+ }
+
+ /*
+ * Generate siginfo for this tracee LWP.
+ */
+ lx_ptrace_winfo(remote, &sqp->sq_info, B_FALSE, NULL, NULL);
+ remote->br_ptrace_flags &= ~LX_PTF_CLDPEND;
+ mutex_exit(&rproc->p_lock);
+
+ mutex_enter(&pp->p_lock);
+ if (sigismember(&pp->p_sig, SIGCLD)) {
+ mutex_exit(&pp->p_lock);
+
+ mutex_enter(&rproc->p_lock);
+ remote->br_ptrace_flags |= LX_PTF_CLDPEND;
+ mutex_exit(&rproc->p_lock);
+
+ return (-1);
+ }
+ sigaddqa(pp, curthread, sqp);
+ mutex_exit(&pp->p_lock);
+
+ return (0);
+}
+
+/*
+ * Consume the next available ptrace(2) event queued against the accord for
+ * this LWP. The event will be emitted as if through waitid(), and converted
+ * by lx_waitpid() and friends before the return to usermode.
+ */
+int
+lx_waitid_helper(idtype_t idtype, id_t id, k_siginfo_t *ip, int options,
+ boolean_t *brand_wants_wait, int *rval)
+{
+ lx_ptrace_accord_t *accord;
+ klwp_t *lwp = ttolwp(curthread);
+ proc_t *p = lwptoproc(lwp);
+ lx_lwp_data_t *local = lwptolxlwp(lwp);
+ lx_lwp_data_t *remote;
+ boolean_t found = B_FALSE;
+ klwp_t *rlwp = NULL;
+ proc_t *rproc = NULL;
+ pid_t event_pid = 0, event_ppid = 0;
+ boolean_t waitflag = !(options & WNOWAIT);
+ boolean_t target_found = B_FALSE;
+
+ VERIFY(MUTEX_HELD(&pidlock));
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+ /*
+ * By default, we do not expect waitid() to block on our account.
+ */
+ *brand_wants_wait = B_FALSE;
+
+ if (!local->br_waitid_emulate) {
+ /*
+ * This waitid() call is not expecting emulated results.
+ */
+ return (-1);
+ }
+
+ switch (idtype) {
+ case P_ALL:
+ case P_PID:
+ case P_PGID:
+ break;
+ default:
+ /*
+ * This idtype has no power here.
+ */
+ return (-1);
+ }
+
+ if (lx_ptrace_accord_get(&accord, B_FALSE) != 0) {
+ /*
+ * This LWP does not have an accord; it cannot be tracing.
+ */
+ return (-1);
+ }
+
+ /*
+ * We do not need an additional hold on the accord as it belongs to
+ * the running, tracer, LWP.
+ */
+ lx_ptrace_accord_exit(accord);
+
+ mutex_enter(&accord->lxpa_tracees_lock);
+ if (list_is_empty(&accord->lxpa_tracees)) {
+ /*
+ * Though it has an accord, there are currently no tracees in
+ * the list for this LWP.
+ */
+ mutex_exit(&accord->lxpa_tracees_lock);
+ return (-1);
+ }
+
+ /*
+ * Walk the list of tracees and determine if any of them have events to
+ * report.
+ */
+ for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+ remote = list_next(&accord->lxpa_tracees, remote)) {
+ rlwp = remote->br_lwp;
+ rproc = lwptoproc(rlwp);
+
+ /*
+ * We check to see if this LWP matches an id we are waiting for.
+ */
+ switch (idtype) {
+ case P_ALL:
+ break;
+ case P_PID:
+ if (remote->br_pid != id)
+ continue;
+ break;
+ case P_PGID:
+ if (rproc->p_pgrp != id)
+ continue;
+ break;
+ default:
+ cmn_err(CE_PANIC, "unexpected idtype: %d", idtype);
+ }
+
+ /* This tracee matches provided idtype and id */
+ target_found = B_TRUE;
+
+ /*
+ * Check if this LWP is in "ptrace-stop". If in the correct
+ * stop condition, lock the process containing the tracee LWP.
+ */
+ if (lx_ptrace_lock_if_stopped(accord, remote, B_FALSE) != 0) {
+ continue;
+ }
+
+ if (remote->br_ptrace_flags & LX_PTF_PARENT_WAIT) {
+ /*
+ * This event depends on waitid() clearing out the
+ * event of another LWP. Skip it for now.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ if (!(remote->br_ptrace_flags & LX_PTF_WAITPEND) ||
+ remote->br_ptrace_whystop == 0 ||
+ remote->br_ptrace_whatstop == 0) {
+ /*
+ * No (new) stop reason to post for this LWP.
+ */
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ /*
+ * We found a process of interest. Leave the process
+ * containing the tracee LWP locked and break out of the loop.
+ */
+ found = B_TRUE;
+ break;
+ }
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ if (!found) {
+ /*
+ * There were no events of interest, but we have tracees.
+ * If any of the tracees matched the spcified criteria, signal
+ * to waitid() that it should block if the provided flags allow
+ * for it.
+ */
+ if (target_found) {
+ *brand_wants_wait = B_TRUE;
+ }
+
+ return (-1);
+ }
+
+ /*
+ * Populate the signal information.
+ */
+ lx_ptrace_winfo(remote, ip, waitflag, &event_ppid, &event_pid);
+
+ /*
+ * Unlock the tracee.
+ */
+ mutex_exit(&rproc->p_lock);
+
+ if (event_pid != 0 && event_ppid != 0) {
+ /*
+ * We need to do another pass around the tracee list and
+ * unblock any events that have a "happens after" relationship
+ * with this event.
+ */
+ mutex_enter(&accord->lxpa_tracees_lock);
+ for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+ remote = list_next(&accord->lxpa_tracees, remote)) {
+ rlwp = remote->br_lwp;
+ rproc = lwptoproc(rlwp);
+
+ mutex_enter(&rproc->p_lock);
+
+ if (remote->br_pid != event_pid ||
+ remote->br_ppid != event_ppid) {
+ mutex_exit(&rproc->p_lock);
+ continue;
+ }
+
+ remote->br_ptrace_flags &= ~LX_PTF_PARENT_WAIT;
+
+ mutex_exit(&rproc->p_lock);
+ }
+ mutex_exit(&accord->lxpa_tracees_lock);
+ }
+
+ /*
+ * If we are consuming this wait state, we remove the SIGCLD from
+ * the queue and post another.
+ */
+ if (waitflag) {
+ mutex_exit(&pidlock);
+ sigcld_delete(ip);
+ sigcld_repost();
+ mutex_enter(&pidlock);
+ }
+
+ *rval = 0;
+ return (0);
+}
+
+static int
+lx_ptrace_peek(lx_lwp_data_t *lwpd, uintptr_t addr, void *data)
+{
+ proc_t *p = lwptoproc(lwpd->br_lwp);
+ long buf;
+ int error = 0, size = sizeof (buf);
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ size = sizeof (uint32_t);
+ }
+#endif
+ if ((addr & (size - 1)) != 0) {
+ /* unaligned access */
+ return (EINVAL);
+ }
+
+ mutex_exit(&p->p_lock);
+ error = uread(p, &buf, size, addr);
+ mutex_enter(&p->p_lock);
+
+ if (error != 0) {
+ return (EIO);
+ }
+ if (copyout(&buf, data, size) != 0) {
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+static int
+lx_ptrace_poke(lx_lwp_data_t *lwpd, uintptr_t addr, uintptr_t data)
+{
+ proc_t *p = lwptoproc(lwpd->br_lwp);
+ int error = 0, size = sizeof (data);
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ size = sizeof (uint32_t);
+ }
+#endif
+ if ((addr & (size - 1)) != 0) {
+ /* unaligned access */
+ return (EINVAL);
+ }
+
+ mutex_exit(&p->p_lock);
+ error = uwrite(p, &data, size, addr);
+ mutex_enter(&p->p_lock);
+
+ if (error != 0) {
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+lx_ptrace_kill(lx_lwp_data_t *lwpd)
+{
+ sigtoproc(lwptoproc(lwpd->br_lwp), NULL, SIGKILL);
+
+ return (0);
+}
+
+static int
+lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data)
+{
+ lx_lwp_data_t *local = ttolxlwp(curthread);
+ lx_ptrace_accord_t *accord;
+ lx_lwp_data_t *remote;
+ klwp_t *rlwp;
+ proc_t *rproc;
+ int error;
+ boolean_t found = B_FALSE, restart = B_TRUE;
+
+ /*
+ * PTRACE_TRACEME and PTRACE_ATTACH operations induce the tracing of
+ * one LWP by another. The target LWP must not be traced already.
+ */
+ switch (ptrace_op) {
+ case LX_PTRACE_TRACEME:
+ return (lx_ptrace_traceme());
+
+ case LX_PTRACE_ATTACH:
+ return (lx_ptrace_attach(lxpid));
+ }
+
+ /*
+ * Ensure that we have an accord and obtain a lock on it. This routine
+ * should not fail because the LWP cannot make ptrace(2) system calls
+ * after it has begun exiting.
+ */
+ VERIFY0(local->br_ptrace_flags & LX_PTF_EXITING);
+ VERIFY(lx_ptrace_accord_get(&accord, B_TRUE) == 0);
+
+ /*
+ * The accord belongs to this (the tracer) LWP, and we have a hold on
+ * it. We drop the lock so that we can take other locks.
+ */
+ lx_ptrace_accord_exit(accord);
+
+ /*
+ * Does the tracee list contain the pid in question?
+ */
+retry:
+ mutex_enter(&accord->lxpa_tracees_lock);
+ for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+ remote = list_next(&accord->lxpa_tracees, remote)) {
+ if (remote->br_pid == lxpid) {
+ found = B_TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ /*
+ * The requested pid does not appear in the tracee list.
+ */
+ mutex_exit(&accord->lxpa_tracees_lock);
+ return (ESRCH);
+ }
+
+ if (ptrace_op == LX_PTRACE_DETACH) {
+ /*
+ * We're detaching, make sure in-syscall flag is off so that
+ * signal will stop the process directly.
+ */
+ remote->br_ptrace_flags &= ~LX_PTF_INSYSCALL;
+ }
+
+ /*
+ * Attempt to lock the target LWP.
+ */
+ if ((error = lx_ptrace_lock_if_stopped(accord, remote,
+ (ptrace_op == LX_PTRACE_DETACH))) != 0) {
+ /*
+ * The LWP was not in "ptrace-stop". For detach, ENOENT
+ * indicates that the LWP was not in "ptrace-stop", but is
+ * still locked.
+ */
+ if (ptrace_op == LX_PTRACE_DETACH && error == ENOENT) {
+ /*
+ * We're detaching, but the process was not in
+ * ptrace_stop, so we don't want to try to restart it.
+ */
+ restart = B_FALSE;
+ } else {
+ mutex_exit(&accord->lxpa_tracees_lock);
+ return (error);
+ }
+ }
+
+ /*
+ * The target LWP is in "ptrace-stop". We have the containing process
+ * locked.
+ */
+ rlwp = remote->br_lwp;
+ rproc = lwptoproc(rlwp);
+
+ if (ptrace_op == LX_PTRACE_DETACH) {
+ if (TRACEE_BUSY(remote)) {
+ kmutex_t *rmp;
+
+ /*
+ * There is a tricky race condition we have to watch
+ * out for here (for example, if a tracee is in the
+ * kernel in the middle of a syscall). When the tracee
+ * is leaving the kernel, it will set LX_PTF_STOPPING.
+ * In lx_stop_notify() the tracee has to drop its
+ * p_lock, take pidlock, then reacquire p_lock, before
+ * it will clear LX_PTF_STOPPING and set LX_PTF_STOPPED.
+ * During that window, if this tracer is trying to
+ * detach, we have to make sure the tracee is restarted.
+ * We handle this case in the same way we handle
+ * the tracer exiting in lx_ptrace_exit_tracer().
+ */
+ rmp = &rproc->p_lock;
+ mutex_exit(&accord->lxpa_tracees_lock);
+ (void) cv_wait_sig(&lx_ptrace_busy_cv, rmp);
+
+ /*
+ * While we were waiting, state will have changed, so
+ * retry.
+ */
+ mutex_exit(rmp);
+ goto retry;
+ }
+
+ lx_ptrace_detach(accord, remote, (int)data, restart);
+ /*
+ * Drop the lock on both the tracee process and the tracee list.
+ */
+ mutex_exit(&rproc->p_lock);
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Release a hold from the accord.
+ */
+ lx_ptrace_accord_enter(accord);
+ lx_ptrace_accord_rele(accord);
+ lx_ptrace_accord_exit(accord);
+
+ return (0);
+ }
+
+ /*
+ * The tracees lock is not needed for any of the other operations.
+ * Drop it so further actions can avoid deadlock.
+ */
+ mutex_exit(&accord->lxpa_tracees_lock);
+
+ /*
+ * Process the ptrace(2) request:
+ */
+ switch (ptrace_op) {
+ case LX_PTRACE_CONT:
+ error = lx_ptrace_cont(remote, LX_PTC_NONE, (int)data);
+ break;
+
+ case LX_PTRACE_SYSCALL:
+ error = lx_ptrace_cont(remote, LX_PTC_SYSCALL, (int)data);
+ break;
+
+ case LX_PTRACE_SINGLESTEP:
+ error = lx_ptrace_cont(remote, LX_PTC_SINGLESTEP, (int)data);
+ break;
+
+ case LX_PTRACE_SETOPTIONS:
+ error = lx_ptrace_setoptions(remote, data);
+ break;
+
+ case LX_PTRACE_GETEVENTMSG:
+ error = lx_ptrace_geteventmsg(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_GETREGS:
+ error = lx_user_regs_copyout(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_SETREGS:
+ error = lx_user_regs_copyin(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_GETSIGINFO:
+ error = lx_ptrace_getsiginfo(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_PEEKTEXT:
+ case LX_PTRACE_PEEKDATA:
+ error = lx_ptrace_peek(remote, addr, (void *)data);
+ break;
+
+ case LX_PTRACE_POKETEXT:
+ case LX_PTRACE_POKEDATA:
+ error = lx_ptrace_poke(remote, addr, data);
+ break;
+
+ case LX_PTRACE_PEEKUSER:
+ error = lx_ptrace_peekuser(remote, addr, (void *)data);
+ break;
+
+ case LX_PTRACE_POKEUSER:
+ error = lx_ptrace_pokeuser(remote, addr, (void *)data);
+ break;
+
+ case LX_PTRACE_GETFPREGS:
+ error = lx_user_fpregs_copyout(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_SETFPREGS:
+ error = lx_user_fpregs_copyin(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_GETFPXREGS:
+ error = lx_user_fpxregs_copyout(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_SETFPXREGS:
+ error = lx_user_fpxregs_copyin(remote, (void *)data);
+ break;
+
+ case LX_PTRACE_KILL:
+ error = lx_ptrace_kill(remote);
+ break;
+
+ default:
+ error = EINVAL;
+ }
+
+ /*
+ * Drop the lock on both the tracee process and the tracee list.
+ */
+ mutex_exit(&rproc->p_lock);
+
+ return (error);
+}
+
+int
+lx_ptrace(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data)
+{
+ int error;
+
+ error = lx_ptrace_kernel(ptrace_op, LX_INIT_TO_PID(lxpid), addr, data);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+void
+lx_ptrace_init(void)
+{
+ cv_init(&lx_ptrace_busy_cv, NULL, CV_DEFAULT, NULL);
+
+ lx_ptrace_accord_cache = kmem_cache_create("lx_ptrace_accord",
+ sizeof (lx_ptrace_accord_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
+}
+
+void
+lx_ptrace_fini(void)
+{
+ cv_destroy(&lx_ptrace_busy_cv);
+
+ kmem_cache_destroy(lx_ptrace_accord_cache);
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_signal.c b/usr/src/uts/common/brand/lx/os/lx_signal.c
new file mode 100644
index 0000000000..53e0cecc14
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_signal.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 2015 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/sunddi.h>
+#include <lx_signum.h>
+
+void
+lx_ltos_sigset(lx_sigset_t *lsigp, k_sigset_t *ssigp)
+{
+ int lx_sig, sig;
+
+ sigemptyset(ssigp);
+ for (lx_sig = 1; lx_sig <= LX_NSIG; lx_sig++) {
+ if (lx_sigismember(lsigp, lx_sig) &&
+ ((sig = ltos_signo[lx_sig]) > 0))
+ sigaddset(ssigp, sig);
+ }
+
+ /* Emulate sigutok() restrictions */
+ ssigp->__sigbits[0] &= (FILLSET0 & ~CANTMASK0);
+ ssigp->__sigbits[1] &= (FILLSET1 & ~CANTMASK1);
+ ssigp->__sigbits[2] &= (FILLSET2 & ~CANTMASK2);
+}
+
+void
+lx_stol_sigset(k_sigset_t *ssigp, lx_sigset_t *lsigp)
+{
+ int sig, lx_sig;
+
+ bzero(lsigp, sizeof (lx_sigset_t));
+ for (sig = 1; sig < NSIG; sig++) {
+ if (sigismember(ssigp, sig) &&
+ ((lx_sig = stol_signo[sig]) > 0))
+ lx_sigaddset(lsigp, lx_sig);
+ }
+}
diff --git a/usr/src/uts/common/brand/lx/os/lx_syscall.c b/usr/src/uts/common/brand/lx/os/lx_syscall.c
new file mode 100644
index 0000000000..f349cfec1f
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/os/lx_syscall.c
@@ -0,0 +1,1229 @@
+/*
+ * 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 2019 Joyent, Inc.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#include <sys/kmem.h>
+#include <sys/errno.h>
+#include <sys/thread.h>
+#include <sys/systm.h>
+#include <sys/syscall.h>
+#include <sys/proc.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+#include <sys/model.h>
+#include <sys/privregs.h>
+#include <sys/brand.h>
+#include <sys/machbrand.h>
+#include <sys/sdt.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_misc.h>
+#include <lx_errno.h>
+
+
+/*
+ * Flags for sysent entries:
+ */
+#define LX_SYS_NOSYS_REASON 0x07
+#define LX_SYS_EBPARG6 0x08
+
+/*
+ * Flags that denote the specific reason we do not have a particular system
+ * call. These reasons are only valid if the function is NULL.
+ */
+#define NOSYS_USERMODE 0
+#define NOSYS_NULL 1
+#define NOSYS_NONE 2
+#define NOSYS_NO_EQUIV 3
+#define NOSYS_KERNEL 4
+#define NOSYS_UNDOC 5
+#define NOSYS_OBSOLETE 6
+#define NOSYS_MAX NOSYS_OBSOLETE
+
+#if NOSYS_MAX > LX_SYS_NOSYS_REASON
+#error NOSYS reason codes must fit in LX_SYS_NOSYS_REASON
+#endif
+
+/*
+ * Strings describing the reason we do not emulate a particular system call
+ * in the kernel.
+ */
+static char *nosys_reasons[] = {
+ NULL, /* NOSYS_USERMODE means this call is emulated in usermode */
+ "Not done yet",
+ "No such Linux system call",
+ "No equivalent illumos functionality",
+ "Reads/modifies Linux kernel state",
+ "Undocumented and/or rarely used system call",
+ "Unsupported, obsolete system call"
+};
+
+
+#if defined(_LP64)
+/*
+ * System call handler table and entry count for Linux x86_64 (amd64):
+ */
+lx_sysent_t lx_sysent64[LX_NSYSCALLS + 1];
+int lx_nsysent64;
+#endif
+/*
+ * System call handler table and entry count for Linux x86 (i386):
+ */
+lx_sysent_t lx_sysent32[LX_NSYSCALLS + 1];
+int lx_nsysent32;
+
+#if defined(_LP64)
+struct lx_vsyscall
+{
+ uintptr_t lv_addr;
+ uintptr_t lv_scnum;
+} lx_vsyscalls[] = {
+ { LX_VSYS_gettimeofday, LX_SYS_gettimeofday },
+ { LX_VSYS_time, LX_SYS_time },
+ { LX_VSYS_getcpu, LX_SYS_getcpu },
+ { (uintptr_t)NULL, (uintptr_t)NULL }
+};
+#endif
+
+#if defined(__amd64)
+static int
+lx_emulate_args(klwp_t *lwp, const lx_sysent_t *s, uintptr_t *args)
+{
+ struct regs *rp = lwptoregs(lwp);
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ /*
+ * Note: Syscall argument passing is different from function
+ * call argument passing on amd64. For function calls, the
+ * fourth arg is passed via %rcx, but for system calls the 4th
+ * arg is passed via %r10. This is because in amd64, the
+ * syscall instruction puts the lower 32 bits of %rflags in
+ * %r11 and puts the %rip value to %rcx.
+ *
+ * Appendix A of the amd64 ABI (Linux conventions) states that
+ * syscalls are limited to 6 args and no arg is passed on the
+ * stack.
+ */
+ args[0] = rp->r_rdi;
+ args[1] = rp->r_rsi;
+ args[2] = rp->r_rdx;
+ args[3] = rp->r_r10;
+ args[4] = rp->r_r8;
+ args[5] = rp->r_r9;
+ } else {
+ /*
+ * If the system call takes 6 args, then libc has stashed them
+ * in memory at the address contained in %ebx. Except for some
+ * syscalls which store the 6th argument in %ebp.
+ */
+ if (s->sy_narg == 6 && !(s->sy_flags & LX_SYS_EBPARG6)) {
+ uint32_t args32[6];
+
+ if (copyin((void *)rp->r_rbx, &args32,
+ sizeof (args32)) != 0) {
+ /*
+ * Clear the argument vector so that the
+ * trace probe does not expose kernel
+ * memory.
+ */
+ bzero(args, 6 * sizeof (uintptr_t));
+ return (set_errno(EFAULT));
+ }
+
+ args[0] = args32[0];
+ args[1] = args32[1];
+ args[2] = args32[2];
+ args[3] = args32[3];
+ args[4] = args32[4];
+ args[5] = args32[5];
+ } else {
+ args[0] = rp->r_rbx;
+ args[1] = rp->r_rcx;
+ args[2] = rp->r_rdx;
+ args[3] = rp->r_rsi;
+ args[4] = rp->r_rdi;
+ args[5] = rp->r_rbp;
+ }
+ }
+
+ return (0);
+}
+
+#else /* !__amd64 */
+
+static int
+lx_emulate_args(klwp_t *lwp, const lx_sysent_t *s, uintptr_t *args)
+{
+ struct regs *rp = lwptoregs(lwp);
+
+ /*
+ * If the system call takes 6 args, then libc has stashed them
+ * in memory at the address contained in %ebx. Except for some
+ * syscalls which store the 6th argument in %ebp.
+ */
+ if (s->sy_narg == 6 && !(s->sy_flags & LX_SYS_EBPARG6)) {
+ if (copyin((void *)rp->r_ebx, args, 6 * sizeof (uintptr_t)) !=
+ 0) {
+ /*
+ * Clear the argument vector so that the trace probe
+ * does not expose kernel memory.
+ */
+ bzero(args, 6 * sizeof (uintptr_t));
+ return (set_errno(EFAULT));
+ }
+ } else {
+ args[0] = rp->r_ebx;
+ args[1] = rp->r_ecx;
+ args[2] = rp->r_edx;
+ args[3] = rp->r_esi;
+ args[4] = rp->r_edi;
+ args[5] = rp->r_ebp;
+ }
+
+ return (0);
+}
+#endif
+
+void
+lx_syscall_return(klwp_t *lwp, int syscall_num, long ret)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ struct regs *rp = lwptoregs(lwp);
+ int error = lwp->lwp_errno;
+
+ if (error != EINTR) {
+ /*
+ * If this system call was not interrupted, clear the system
+ * call restart flag before lx_setcontext() can pass it to
+ * usermode.
+ */
+ lwpd->br_syscall_restart = B_FALSE;
+ }
+
+ if (error != 0) {
+ /*
+ * Convert from illumos to Linux errno:
+ */
+ ret = -lx_errno(error, EINVAL);
+ }
+
+ /*
+ * 32-bit Linux system calls return via %eax; 64-bit calls return via
+ * %rax.
+ */
+ rp->r_r0 = ret;
+
+ /*
+ * Hold for the ptrace(2) "syscall-exit-stop" condition if required by
+ * PTRACE_SYSCALL. Note that the register state may be modified by
+ * tracer.
+ */
+ (void) lx_ptrace_stop(LX_PR_SYSEXIT);
+
+ /*
+ * Emit audit record, if necessary.
+ */
+ lx_audit_syscall_exit(syscall_num, ret);
+
+ /*
+ * Fire the DTrace "lx-syscall:::return" probe:
+ */
+ lx_trace_sysreturn(syscall_num, ret);
+
+ /*
+ * Clear errno for next time. We do not clear "br_syscall_restart" or
+ * "br_syscall_num" as they are potentially used by "lx_savecontext()"
+ * in the signal delivery path.
+ */
+ lwp->lwp_errno = 0;
+
+ lx_check_strict_failure(lwpd);
+
+ /*
+ * We want complete control of the registers on return from this
+ * emulated Linux system call:
+ */
+ lwp->lwp_eosys = JUSTRETURN;
+}
+
+static void
+lx_syscall_unsup_msg(lx_sysent_t *s, int syscall_num, int unsup_reason)
+{
+ char buf[100];
+
+ if (s == NULL) {
+ (void) snprintf(buf, sizeof (buf), "NOSYS (%d): out of bounds",
+ syscall_num);
+ } else {
+ VERIFY(unsup_reason < (sizeof (nosys_reasons) /
+ sizeof (*nosys_reasons)));
+
+ if (s->sy_name == NULL) {
+ (void) snprintf(buf, sizeof (buf), "NOSYS (%d): %s",
+ syscall_num, nosys_reasons[unsup_reason]);
+ } else {
+ (void) snprintf(buf, sizeof (buf), "NOSYS (%s): %s",
+ s->sy_name, nosys_reasons[unsup_reason]);
+ }
+ }
+
+ lx_unsupported(buf);
+}
+
+/*
+ * This function is used to override the processing of arguments and
+ * invocation of a handler for emulated system calls, installed on each
+ * branded LWP as "lwp_brand_syscall". If this system call should use the
+ * native path, we return 1. If we handled this system call (and have made
+ * arrangements with respect to post-return usermode register state) we
+ * return 0.
+ */
+int
+lx_syscall_enter(void)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ struct regs *rp = lwptoregs(lwp);
+ int syscall_num;
+ int error;
+ long ret = 0;
+ lx_sysent_t *s;
+ uintptr_t args[6];
+ unsigned int unsup_reason;
+
+ /*
+ * If we got here, we should have an LWP-specific brand data
+ * structure.
+ */
+ VERIFY(lwpd != NULL);
+
+ if (lwpd->br_stack_mode != LX_STACK_MODE_BRAND) {
+ /*
+ * The lwp is not in in BRAND execution mode, so we return
+ * to the regular native system call path.
+ */
+ DTRACE_PROBE(brand__lx__syscall__hook__skip);
+ return (1);
+ }
+
+ /*
+ * Clear the restartable system call flag. This flag will be set
+ * on in the system call handler if the call is a candidate for
+ * a restart. It will be saved by lx_setcontext() in the event
+ * that we take a signal, and used in the signal handling path
+ * to restart the system call iff SA_RESTART was set for this
+ * signal. Save the system call number so that we can store it
+ * in the saved context if required.
+ */
+ lwpd->br_syscall_restart = B_FALSE;
+ lwpd->br_syscall_num = (int)rp->r_r0;
+
+ /*
+ * Hold for the ptrace(2) "syscall-entry-stop" condition if traced by
+ * PTRACE_SYSCALL. The system call number and arguments may be
+ * modified by the tracer.
+ */
+ (void) lx_ptrace_stop(LX_PR_SYSENTRY);
+
+ /*
+ * Check that the system call number is within the bounds we expect.
+ */
+ syscall_num = lwpd->br_syscall_num;
+ if (syscall_num < 0 || syscall_num > LX_MAX_SYSCALL(lwp)) {
+ lx_syscall_unsup_msg(NULL, syscall_num, 0);
+
+ (void) set_errno(ENOTSUP);
+ lx_syscall_return(lwp, syscall_num, -1);
+ return (0);
+ }
+
+#if defined(_LP64)
+ if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) {
+ s = &lx_sysent64[syscall_num];
+ } else
+#endif
+ {
+ s = &lx_sysent32[syscall_num];
+ }
+
+ /*
+ * Process the arguments for this system call and fire the DTrace
+ * "lx-syscall:::entry" probe:
+ */
+ error = lx_emulate_args(lwp, s, args);
+ lx_trace_sysenter(syscall_num, args);
+ lwpd->br_syscall_args[0] = args[0];
+ lwpd->br_syscall_args[1] = args[1];
+ lwpd->br_syscall_args[2] = args[2];
+ lwpd->br_syscall_args[3] = args[3];
+ if (error != 0) {
+ /*
+ * Could not read and process the arguments. Return the error
+ * to the process.
+ */
+ (void) set_errno(error);
+ lx_syscall_return(lwp, syscall_num, -1);
+ return (0);
+ }
+
+ if (s->sy_callc != NULL) {
+ /*
+ * Call the in-kernel handler for this Linux system call:
+ */
+ lwpd->br_eosys = NORMALRETURN;
+ ret = s->sy_callc(args[0], args[1], args[2], args[3], args[4],
+ args[5]);
+ if (lwpd->br_eosys == NORMALRETURN) {
+ lx_syscall_return(lwp, syscall_num, ret);
+ }
+ return (0);
+ }
+
+ /*
+ * There is no in-kernel handler.
+ */
+ switch (unsup_reason = (s->sy_flags & LX_SYS_NOSYS_REASON)) {
+ case NOSYS_USERMODE:
+ /*
+ * Pass to the usermode emulation routine.
+ */
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_emulate_user32(lwp, syscall_num, args);
+ } else
+#endif
+ {
+ lx_emulate_user(lwp, syscall_num, args);
+ }
+ return (0);
+
+ default:
+ /*
+ * We are not emulating this system call at all.
+ */
+ lx_syscall_unsup_msg(s, syscall_num, unsup_reason);
+
+ (void) set_errno(ENOTSUP);
+ lx_syscall_return(lwp, syscall_num, -1);
+ return (0);
+ }
+}
+
+#if defined(_LP64)
+/*
+ * Emulate vsyscall support.
+ *
+ * Linux magically maps a single page into the address space of each process,
+ * allowing them to make 'vsyscalls'. Originally designed to counteract the
+ * perceived overhead of regular system calls, vsyscalls were implemented as
+ * code residing in userspace which could be called directly. The userspace
+ * implementations of these vsyscalls which have now been replaced by
+ * instructions which vector into the normal syscall path.
+ *
+ * Implementing vsyscalls on Illumos is complicated by the fact that the
+ * required static address region resides inside the kernel address space.
+ * Rather than mapping a user-accessible page into the KAS, a different
+ * approach is taken. The vsyscall gate is emulated by interposing on
+ * pagefaults in trap(). An attempt to execute a known vsyscall address will
+ * result in emulating the appropriate system call rather than inducing a
+ * SIGSEGV.
+ */
+void
+lx_vsyscall_enter(proc_t *p, klwp_t *lwp, int scnum)
+{
+ struct regs *rp = lwptoregs(lwp);
+ uintptr_t raddr;
+
+ /*
+ * Fetch the return address from the process stack.
+ */
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+ if (copyin((void *)rp->r_rsp, &raddr, sizeof (raddr)) != 0) {
+#if DEBUG
+ printf("lx_vsyscall_call: bad brand stack at vsyscall "
+ "cmd=%s, pid=%d, sp=0x%p\n", PTOU(p)->u_comm,
+ p->p_pid, (void *)rp->r_rsp);
+#endif
+
+ /*
+ * The process jumped to the vsyscall address without a
+ * correctly configured stack. Terminate the process.
+ */
+ exit(CLD_KILLED, SIGSEGV);
+ return;
+ }
+
+ DTRACE_PROBE1(brand__lx__vsyscall, int, scnum);
+
+ /* Simulate vectoring into the syscall */
+ rp->r_rax = scnum;
+ rp->r_rip = raddr;
+ rp->r_rsp += sizeof (uintptr_t);
+
+ (void) lx_syscall_enter();
+}
+
+boolean_t
+lx_vsyscall_iscall(klwp_t *lwp, uintptr_t addr, int *scnum)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ int i;
+
+ if (lwpd->br_stack_mode != LX_STACK_MODE_BRAND) {
+ /*
+ * We only handle vsyscalls when running Linux code.
+ */
+ return (B_FALSE);
+ }
+
+ if (addr < LX_VSYSCALL_ADDR ||
+ addr >= (LX_VSYSCALL_ADDR + LX_VSYSCALL_SIZE)) {
+ /*
+ * Ignore faults outside the vsyscall page.
+ */
+ return (B_FALSE);
+ }
+
+ for (i = 0; lx_vsyscalls[i].lv_addr != (uintptr_t)NULL; i++) {
+ if (addr == lx_vsyscalls[i].lv_addr) {
+ /*
+ * This is a valid vsyscall address.
+ */
+ *scnum = lx_vsyscalls[i].lv_scnum;
+ return (B_TRUE);
+ }
+ }
+
+ lx_unsupported("bad vsyscall access");
+ return (B_FALSE);
+}
+#endif
+
+/*
+ * Linux defines system call numbers for 32-bit x86 in the file:
+ * arch/x86/syscalls/syscall_32.tbl
+ */
+lx_sysent_t lx_sysent32[] = {
+ {"nosys", NULL, NOSYS_NONE, 0}, /* 0 */
+ {"exit", NULL, 0, 1}, /* 1 */
+ {"fork", NULL, 0, 0}, /* 2 */
+ {"read", lx_read, 0, 3}, /* 3 */
+ {"write", lx_write, 0, 3}, /* 4 */
+ {"open", lx_open, 0, 3}, /* 5 */
+ {"close", lx_close, 0, 1}, /* 6 */
+ {"waitpid", lx_waitpid, 0, 3}, /* 7 */
+ {"creat", lx_creat, 0, 2}, /* 8 */
+ {"link", lx_link, 0, 2}, /* 9 */
+ {"unlink", lx_unlink, 0, 1}, /* 10 */
+ {"execve", NULL, 0, 3}, /* 11 */
+ {"chdir", lx_chdir, 0, 1}, /* 12 */
+ {"time", lx_time, 0, 1}, /* 13 */
+ {"mknod", NULL, 0, 3}, /* 14 */
+ {"chmod", lx_chmod, 0, 2}, /* 15 */
+ {"lchown16", lx_lchown16, 0, 3}, /* 16 */
+ {"break", NULL, NOSYS_OBSOLETE, 0}, /* 17 */
+ {"stat", NULL, NOSYS_OBSOLETE, 0}, /* 18 */
+ {"lseek", lx_lseek32, 0, 3}, /* 19 */
+ {"getpid", lx_getpid, 0, 0}, /* 20 */
+ {"mount", lx_mount, 0, 5}, /* 21 */
+ {"umount", lx_umount, 0, 1}, /* 22 */
+ {"setuid16", lx_setuid16, 0, 1}, /* 23 */
+ {"getuid16", lx_getuid16, 0, 0}, /* 24 */
+ {"stime", lx_stime, 0, 1}, /* 25 */
+ {"ptrace", lx_ptrace, 0, 4}, /* 26 */
+ {"alarm", lx_alarm, 0, 1}, /* 27 */
+ {"fstat", NULL, NOSYS_OBSOLETE, 0}, /* 28 */
+ {"pause", lx_pause, 0, 0}, /* 29 */
+ {"utime", NULL, 0, 2}, /* 30 */
+ {"stty", NULL, NOSYS_OBSOLETE, 0}, /* 31 */
+ {"gtty", NULL, NOSYS_OBSOLETE, 0}, /* 32 */
+ {"access", lx_access, 0, 2}, /* 33 */
+ {"nice", lx_nice, 0, 1}, /* 34 */
+ {"ftime", NULL, NOSYS_OBSOLETE, 0}, /* 35 */
+ {"sync", lx_sync, 0, 0}, /* 36 */
+ {"kill", lx_kill, 0, 2}, /* 37 */
+ {"rename", lx_rename, 0, 2}, /* 38 */
+ {"mkdir", lx_mkdir, 0, 2}, /* 39 */
+ {"rmdir", NULL, 0, 1}, /* 40 */
+ {"dup", lx_dup, 0, 1}, /* 41 */
+ {"pipe", lx_pipe, 0, 1}, /* 42 */
+ {"times", lx_times, 0, 1}, /* 43 */
+ {"prof", NULL, NOSYS_OBSOLETE, 0}, /* 44 */
+ {"brk", lx_brk, 0, 1}, /* 45 */
+ {"setgid16", lx_setgid16, 0, 1}, /* 46 */
+ {"getgid16", lx_getgid16, 0, 0}, /* 47 */
+ {"signal", NULL, 0, 2}, /* 48 */
+ {"geteuid16", lx_geteuid16, 0, 0}, /* 49 */
+ {"getegid16", lx_getegid16, 0, 0}, /* 50 */
+ {"acct", lx_acct, 0, 1}, /* 51 */
+ {"umount2", lx_umount2, 0, 2}, /* 52 */
+ {"lock", NULL, NOSYS_OBSOLETE, 0}, /* 53 */
+ {"ioctl", lx_ioctl, 0, 3}, /* 54 */
+ {"fcntl", lx_fcntl, 0, 3}, /* 55 */
+ {"mpx", NULL, NOSYS_OBSOLETE, 0}, /* 56 */
+ {"setpgid", lx_setpgid, 0, 2}, /* 57 */
+ {"ulimit", NULL, NOSYS_OBSOLETE, 0}, /* 58 */
+ {"olduname", NULL, NOSYS_OBSOLETE, 0}, /* 59 */
+ {"umask", lx_umask, 0, 1}, /* 60 */
+ {"chroot", lx_chroot, 0, 1}, /* 61 */
+ {"ustat", NULL, NOSYS_OBSOLETE, 2}, /* 62 */
+ {"dup2", lx_dup2, 0, 2}, /* 63 */
+ {"getppid", lx_getppid, 0, 0}, /* 64 */
+ {"getpgrp", lx_getpgrp, 0, 0}, /* 65 */
+ {"setsid", lx_setsid, 0, 0}, /* 66 */
+ {"sigaction", NULL, 0, 3}, /* 67 */
+ {"sgetmask", NULL, NOSYS_OBSOLETE, 0}, /* 68 */
+ {"ssetmask", NULL, NOSYS_OBSOLETE, 0}, /* 69 */
+ {"setreuid16", lx_setreuid16, 0, 2}, /* 70 */
+ {"setregid16", lx_setregid16, 0, 2}, /* 71 */
+ {"sigsuspend", NULL, 0, 1}, /* 72 */
+ {"sigpending", NULL, 0, 1}, /* 73 */
+ {"sethostname", lx_sethostname, 0, 2}, /* 74 */
+ {"setrlimit", lx_setrlimit, 0, 2}, /* 75 */
+ {"getrlimit", lx_oldgetrlimit, 0, 2}, /* 76 */
+ {"getrusage", lx_getrusage, 0, 2}, /* 77 */
+ {"gettimeofday", lx_gettimeofday, 0, 2}, /* 78 */
+ {"settimeofday", NULL, 0, 2}, /* 79 */
+ {"getgroups16", NULL, 0, 2}, /* 80 */
+ {"setgroups16", NULL, 0, 2}, /* 81 */
+ {"select", NULL, NOSYS_OBSOLETE, 0}, /* 82 */
+ {"symlink", lx_symlink, 0, 2}, /* 83 */
+ {"oldlstat", NULL, NOSYS_OBSOLETE, 0}, /* 84 */
+ {"readlink", lx_readlink, 0, 3}, /* 85 */
+ {"uselib", NULL, NOSYS_KERNEL, 0}, /* 86 */
+ {"swapon", lx_swapon, 0, 2}, /* 87 */
+ {"reboot", lx_reboot, 0, 4}, /* 88 */
+ {"readdir", NULL, 0, 3}, /* 89 */
+ {"mmap", lx_mmap, 0, 6}, /* 90 */
+ {"munmap", lx_munmap, 0, 2}, /* 91 */
+ {"truncate", NULL, 0, 2}, /* 92 */
+ {"ftruncate", NULL, 0, 2}, /* 93 */
+ {"fchmod", lx_fchmod, 0, 2}, /* 94 */
+ {"fchown16", lx_fchown16, 0, 3}, /* 95 */
+ {"getpriority", lx_getpriority, 0, 2}, /* 96 */
+ {"setpriority", lx_setpriority, 0, 3}, /* 97 */
+ {"profil", NULL, NOSYS_NO_EQUIV, 0}, /* 98 */
+ {"statfs", NULL, 0, 2}, /* 99 */
+ {"fstatfs", NULL, 0, 2}, /* 100 */
+ {"ioperm", NULL, NOSYS_NO_EQUIV, 0}, /* 101 */
+ {"socketcall", lx_socketcall, 0, 2}, /* 102 */
+ {"syslog", lx_syslog, 0, 3}, /* 103 */
+ {"setitimer", NULL, 0, 3}, /* 104 */
+ {"getitimer", lx_getitimer, 0, 2}, /* 105 */
+ {"stat", lx_stat32, 0, 2}, /* 106 */
+ {"lstat", lx_lstat32, 0, 2}, /* 107 */
+ {"fstat", lx_fstat32, 0, 2}, /* 108 */
+ {"uname", NULL, NOSYS_OBSOLETE, 0}, /* 109 */
+ {"oldiopl", NULL, NOSYS_NO_EQUIV, 0}, /* 110 */
+ {"vhangup", lx_vhangup, 0, 0}, /* 111 */
+ {"idle", NULL, NOSYS_NO_EQUIV, 0}, /* 112 */
+ {"vm86old", NULL, NOSYS_OBSOLETE, 0}, /* 113 */
+ {"wait4", lx_wait4, 0, 4}, /* 114 */
+ {"swapoff", lx_swapoff, 0, 1}, /* 115 */
+ {"sysinfo", lx_sysinfo32, 0, 1}, /* 116 */
+ {"ipc", NULL, 0, 5}, /* 117 */
+ {"fsync", NULL, 0, 1}, /* 118 */
+ {"sigreturn", NULL, 0, 1}, /* 119 */
+ {"clone", NULL, 0, 5}, /* 120 */
+ {"setdomainname", lx_setdomainname, 0, 2}, /* 121 */
+ {"uname", lx_uname, 0, 1}, /* 122 */
+ {"modify_ldt", lx_modify_ldt, 0, 3}, /* 123 */
+ {"adjtimex", NULL, 0, 1}, /* 124 */
+ {"mprotect", lx_mprotect, 0, 3}, /* 125 */
+ {"sigprocmask", NULL, 0, 3}, /* 126 */
+ {"create_module", NULL, NOSYS_KERNEL, 0}, /* 127 */
+ {"init_module", NULL, NOSYS_KERNEL, 0}, /* 128 */
+ {"delete_module", NULL, NOSYS_KERNEL, 0}, /* 129 */
+ {"get_kernel_syms", NULL, NOSYS_KERNEL, 0}, /* 130 */
+ {"quotactl", NULL, NOSYS_KERNEL, 0}, /* 131 */
+ {"getpgid", lx_getpgid, 0, 1}, /* 132 */
+ {"fchdir", lx_fchdir, 0, 1}, /* 133 */
+ {"bdflush", NULL, NOSYS_KERNEL, 0}, /* 134 */
+ {"sysfs", NULL, 0, 3}, /* 135 */
+ {"personality", lx_personality, 0, 1}, /* 136 */
+ {"afs_syscall", NULL, NOSYS_KERNEL, 0}, /* 137 */
+ {"setfsuid16", lx_setfsuid16, 0, 1}, /* 138 */
+ {"setfsgid16", lx_setfsgid16, 0, 1}, /* 139 */
+ {"llseek", lx_llseek, 0, 5}, /* 140 */
+ {"getdents", lx_getdents_32, 0, 3}, /* 141 */
+ {"select", lx_select, 0, 5}, /* 142 */
+ {"flock", lx_flock, 0, 2}, /* 143 */
+ {"msync", lx_msync, 0, 3}, /* 144 */
+ {"readv", lx_readv, 0, 3}, /* 145 */
+ {"writev", lx_writev, 0, 3}, /* 146 */
+ {"getsid", lx_getsid, 0, 1}, /* 147 */
+ {"fdatasync", NULL, 0, 1}, /* 148 */
+ {"sysctl", NULL, 0, 1}, /* 149 */
+ {"mlock", lx_mlock, 0, 2}, /* 150 */
+ {"munlock", lx_munlock, 0, 2}, /* 151 */
+ {"mlockall", lx_mlockall, 0, 1}, /* 152 */
+ {"munlockall", lx_munlockall, 0, 0}, /* 153 */
+ {"sched_setparam", lx_sched_setparam, 0, 2}, /* 154 */
+ {"sched_getparam", lx_sched_getparam, 0, 2}, /* 155 */
+ {"sched_setscheduler", lx_sched_setscheduler, 0, 3}, /* 156 */
+ {"sched_getscheduler", lx_sched_getscheduler, 0, 1}, /* 157 */
+ {"sched_yield", lx_sched_yield, 0, 0}, /* 158 */
+ {"sched_get_priority_max", lx_sched_get_priority_max, 0, 1}, /* 159 */
+ {"sched_get_priority_min", lx_sched_get_priority_min, 0, 1}, /* 160 */
+ {"sched_rr_get_interval", lx_sched_rr_get_interval, 0, 2}, /* 161 */
+ {"nanosleep", lx_nanosleep, 0, 2}, /* 162 */
+ {"mremap", lx_mremap, 0, 5}, /* 163 */
+ {"setresuid16", lx_setresuid16, 0, 3}, /* 164 */
+ {"getresuid16", lx_getresuid16, 0, 3}, /* 165 */
+ {"vm86", NULL, NOSYS_NO_EQUIV, 0}, /* 166 */
+ {"query_module", NULL, 0, 5}, /* 167 */
+ {"poll", lx_poll, 0, 3}, /* 168 */
+ {"nfsservctl", NULL, NOSYS_KERNEL, 0}, /* 169 */
+ {"setresgid16", lx_setresgid16, 0, 3}, /* 170 */
+ {"getresgid16", lx_getresgid16, 0, 3}, /* 171 */
+ {"prctl", lx_prctl, 0, 5}, /* 172 */
+ {"rt_sigreturn", NULL, 0, 0}, /* 173 */
+ {"rt_sigaction", NULL, 0, 4}, /* 174 */
+ {"rt_sigprocmask", NULL, 0, 4}, /* 175 */
+ {"rt_sigpending", NULL, 0, 2}, /* 176 */
+ {"rt_sigtimedwait", NULL, 0, 4}, /* 177 */
+ {"rt_sigqueueinfo", NULL, 0, 3}, /* 178 */
+ {"rt_sigsuspend", NULL, 0, 2}, /* 179 */
+ {"pread64", lx_pread32, 0, 5}, /* 180 */
+ {"pwrite64", lx_pwrite32, 0, 5}, /* 181 */
+ {"chown16", lx_chown16, 0, 3}, /* 182 */
+ {"getcwd", lx_getcwd, 0, 2}, /* 183 */
+ {"capget", NULL, 0, 2}, /* 184 */
+ {"capset", NULL, 0, 2}, /* 185 */
+ {"sigaltstack", NULL, 0, 2}, /* 186 */
+ {"sendfile", NULL, 0, 4}, /* 187 */
+ {"getpmsg", NULL, NOSYS_OBSOLETE, 0}, /* 188 */
+ {"putpmsg", NULL, NOSYS_OBSOLETE, 0}, /* 189 */
+ {"vfork", NULL, 0, 0}, /* 190 */
+ {"getrlimit", lx_getrlimit, 0, 2}, /* 191 */
+ {"mmap2", lx_mmap2, LX_SYS_EBPARG6, 6}, /* 192 */
+ {"truncate64", NULL, 0, 3}, /* 193 */
+ {"ftruncate64", NULL, 0, 3}, /* 194 */
+ {"stat64", lx_stat64, 0, 2}, /* 195 */
+ {"lstat64", lx_lstat64, 0, 2}, /* 196 */
+ {"fstat64", lx_fstat64, 0, 2}, /* 197 */
+ {"lchown", lx_lchown, 0, 3}, /* 198 */
+ {"getuid", lx_getuid, 0, 0}, /* 199 */
+ {"getgid", lx_getgid, 0, 0}, /* 200 */
+ {"geteuid", lx_geteuid, 0, 0}, /* 201 */
+ {"getegid", lx_getegid, 0, 0}, /* 202 */
+ {"setreuid", lx_setreuid, 0, 0}, /* 203 */
+ {"setregid", lx_setregid, 0, 0}, /* 204 */
+ {"getgroups", NULL, 0, 2}, /* 205 */
+ {"setgroups", NULL, 0, 2}, /* 206 */
+ {"fchown", lx_fchown, 0, 3}, /* 207 */
+ {"setresuid", lx_setresuid, 0, 3}, /* 208 */
+ {"getresuid", lx_getresuid, 0, 3}, /* 209 */
+ {"setresgid", lx_setresgid, 0, 3}, /* 210 */
+ {"getresgid", lx_getresgid, 0, 3}, /* 211 */
+ {"chown", lx_chown, 0, 3}, /* 212 */
+ {"setuid", lx_setuid, 0, 1}, /* 213 */
+ {"setgid", lx_setgid, 0, 1}, /* 214 */
+ {"setfsuid", lx_setfsuid, 0, 1}, /* 215 */
+ {"setfsgid", lx_setfsgid, 0, 1}, /* 216 */
+ {"pivot_root", NULL, NOSYS_KERNEL, 0}, /* 217 */
+ {"mincore", lx_mincore, 0, 3}, /* 218 */
+ {"madvise", lx_madvise, 0, 3}, /* 219 */
+ {"getdents64", lx_getdents64, 0, 3}, /* 220 */
+ {"fcntl64", lx_fcntl64, 0, 3}, /* 221 */
+ {"tux", NULL, NOSYS_NO_EQUIV, 0}, /* 222 */
+ {"security", NULL, NOSYS_NO_EQUIV, 0}, /* 223 */
+ {"gettid", lx_gettid, 0, 0}, /* 224 */
+ {"readahead", NULL, NOSYS_NO_EQUIV, 0}, /* 225 */
+ {"setxattr", lx_setxattr, 0, 5}, /* 226 */
+ {"lsetxattr", lx_lsetxattr, 0, 5}, /* 227 */
+ {"fsetxattr", lx_fsetxattr, 0, 5}, /* 228 */
+ {"getxattr", lx_getxattr, 0, 4}, /* 229 */
+ {"lgetxattr", lx_lgetxattr, 0, 4}, /* 230 */
+ {"fgetxattr", lx_fgetxattr, 0, 4}, /* 231 */
+ {"listxattr", lx_listxattr, 0, 3}, /* 232 */
+ {"llistxattr", lx_llistxattr, 0, 3}, /* 233 */
+ {"flistxattr", lx_flistxattr, 0, 3}, /* 234 */
+ {"removexattr", lx_removexattr, 0, 2}, /* 235 */
+ {"lremovexattr", lx_lremovexattr, 0, 2}, /* 236 */
+ {"fremovexattr", lx_fremovexattr, 0, 2}, /* 237 */
+ {"tkill", lx_tkill, 0, 2}, /* 238 */
+ {"sendfile64", NULL, 0, 4}, /* 239 */
+ {"futex", lx_futex, LX_SYS_EBPARG6, 6}, /* 240 */
+ {"sched_setaffinity", lx_sched_setaffinity, 0, 3}, /* 241 */
+ {"sched_getaffinity", lx_sched_getaffinity, 0, 3}, /* 242 */
+ {"set_thread_area", lx_set_thread_area, 0, 1}, /* 243 */
+ {"get_thread_area", lx_get_thread_area, 0, 1}, /* 244 */
+ {"io_setup", lx_io_setup, 0, 2}, /* 245 */
+ {"io_destroy", lx_io_destroy, 0, 1}, /* 246 */
+ {"io_getevents", lx_io_getevents, 0, 5}, /* 247 */
+ {"io_submit", lx_io_submit, 0, 3}, /* 248 */
+ {"io_cancel", lx_io_cancel, 0, 3}, /* 249 */
+ {"fadvise64", lx_fadvise64_32, 0, 5}, /* 250 */
+ {"nosys", NULL, 0, 0}, /* 251 */
+ {"group_exit", NULL, 0, 1}, /* 252 */
+ {"lookup_dcookie", NULL, NOSYS_NO_EQUIV, 0}, /* 253 */
+ {"epoll_create", lx_epoll_create, 0, 1}, /* 254 */
+ {"epoll_ctl", lx_epoll_ctl, 0, 4}, /* 255 */
+ {"epoll_wait", lx_epoll_wait, 0, 4}, /* 256 */
+ {"remap_file_pages", NULL, NOSYS_NO_EQUIV, 0}, /* 257 */
+ {"set_tid_address", lx_set_tid_address, 0, 1}, /* 258 */
+ {"timer_create", lx_timer_create, 0, 3}, /* 259 */
+ {"timer_settime", NULL, 0, 4}, /* 260 */
+ {"timer_gettime", NULL, 0, 2}, /* 261 */
+ {"timer_getoverrun", NULL, 0, 1}, /* 262 */
+ {"timer_delete", NULL, 0, 1}, /* 263 */
+ {"clock_settime", lx_clock_settime, 0, 2}, /* 264 */
+ {"clock_gettime", lx_clock_gettime, 0, 2}, /* 265 */
+ {"clock_getres", lx_clock_getres, 0, 2}, /* 266 */
+ {"clock_nanosleep", NULL, 0, 4}, /* 267 */
+ {"statfs64", NULL, 0, 2}, /* 268 */
+ {"fstatfs64", NULL, 0, 2}, /* 269 */
+ {"tgkill", lx_tgkill, 0, 3}, /* 270 */
+
+/*
+ * The following system calls only exist in kernel 2.6 and greater:
+ */
+ {"utimes", NULL, 0, 2}, /* 271 */
+ {"fadvise64_64", lx_fadvise64_64, LX_SYS_EBPARG6, 6}, /* 272 */
+ {"vserver", NULL, NOSYS_NULL, 0}, /* 273 */
+ {"mbind", NULL, NOSYS_NULL, 0}, /* 274 */
+ {"get_mempolicy", NULL, NOSYS_NULL, 0}, /* 275 */
+ {"set_mempolicy", NULL, NOSYS_NULL, 0}, /* 276 */
+ {"mq_open", NULL, NOSYS_NULL, 0}, /* 277 */
+ {"mq_unlink", NULL, NOSYS_NULL, 0}, /* 278 */
+ {"mq_timedsend", NULL, NOSYS_NULL, 0}, /* 279 */
+ {"mq_timedreceive", NULL, NOSYS_NULL, 0}, /* 280 */
+ {"mq_notify", NULL, NOSYS_NULL, 0}, /* 281 */
+ {"mq_getsetattr", NULL, NOSYS_NULL, 0}, /* 282 */
+ {"kexec_load", NULL, NOSYS_NULL, 0}, /* 283 */
+ {"waitid", lx_waitid, 0, 4}, /* 284 */
+ {"sys_setaltroot", NULL, NOSYS_NULL, 0}, /* 285 */
+ {"add_key", NULL, NOSYS_NULL, 0}, /* 286 */
+ {"request_key", NULL, NOSYS_NULL, 0}, /* 287 */
+ {"keyctl", NULL, NOSYS_NULL, 0}, /* 288 */
+ {"ioprio_set", lx_ioprio_set, 0, 3}, /* 289 */
+ {"ioprio_get", lx_ioprio_get, 0, 2}, /* 290 */
+ {"inotify_init", NULL, 0, 0}, /* 291 */
+ {"inotify_add_watch", NULL, 0, 3}, /* 292 */
+ {"inotify_rm_watch", NULL, 0, 2}, /* 293 */
+ {"migrate_pages", NULL, NOSYS_NULL, 0}, /* 294 */
+ {"openat", lx_openat, 0, 4}, /* 295 */
+ {"mkdirat", lx_mkdirat, 0, 3}, /* 296 */
+ {"mknodat", NULL, 0, 4}, /* 297 */
+ {"fchownat", lx_fchownat, 0, 5}, /* 298 */
+ {"futimesat", NULL, 0, 3}, /* 299 */
+ {"fstatat64", lx_fstatat64, 0, 4}, /* 300 */
+ {"unlinkat", lx_unlinkat, 0, 3}, /* 301 */
+ {"renameat", lx_renameat, 0, 4}, /* 302 */
+ {"linkat", lx_linkat, 0, 5}, /* 303 */
+ {"symlinkat", lx_symlinkat, 0, 3}, /* 304 */
+ {"readlinkat", lx_readlinkat, 0, 4}, /* 305 */
+ {"fchmodat", lx_fchmodat, 0, 3}, /* 306 */
+ {"faccessat", lx_faccessat, 0, 4}, /* 307 */
+ {"pselect6", lx_pselect, LX_SYS_EBPARG6, 6}, /* 308 */
+ {"ppoll", lx_ppoll, 0, 5}, /* 309 */
+ {"unshare", lx_unshare, 0, 1}, /* 310 */
+ {"set_robust_list", lx_set_robust_list, 0, 2}, /* 311 */
+ {"get_robust_list", lx_get_robust_list, 0, 3}, /* 312 */
+ {"splice", lx_splice, LX_SYS_EBPARG6, 6}, /* 313 */
+ {"sync_file_range", lx_sync_file_range, 0, 4}, /* 314 */
+ {"tee", NULL, NOSYS_NULL, 0}, /* 315 */
+ {"vmsplice", NULL, NOSYS_NULL, 0}, /* 316 */
+ {"move_pages", NULL, NOSYS_NULL, 0}, /* 317 */
+ {"getcpu", lx_getcpu, 0, 3}, /* 318 */
+ {"epoll_pwait", lx_epoll_pwait, 0, 5}, /* 319 */
+ {"utimensat", NULL, 0, 4}, /* 320 */
+ {"signalfd", NULL, 0, 3}, /* 321 */
+ {"timerfd_create", NULL, 0, 2}, /* 322 */
+ {"eventfd", lx_eventfd, 0, 1}, /* 323 */
+ {"fallocate", lx_fallocate32, LX_SYS_EBPARG6, 6}, /* 324 */
+ {"timerfd_settime", NULL, 0, 4}, /* 325 */
+ {"timerfd_gettime", NULL, 0, 2}, /* 326 */
+ {"signalfd4", NULL, 0, 4}, /* 327 */
+ {"eventfd2", lx_eventfd2, 0, 2}, /* 328 */
+ {"epoll_create1", lx_epoll_create1, 0, 1}, /* 329 */
+ {"dup3", lx_dup3, 0, 3}, /* 330 */
+ {"pipe2", lx_pipe2, 0, 2}, /* 331 */
+ {"inotify_init1", NULL, 0, 1}, /* 332 */
+ {"preadv", lx_preadv32, 0, 5}, /* 333 */
+ {"pwritev", lx_pwritev32, 0, 5}, /* 334 */
+ {"rt_tgsigqueueinfo", NULL, 0, 4}, /* 335 */
+ {"perf_event_open", NULL, NOSYS_NULL, 0}, /* 336 */
+ {"recvmmsg", lx_recvmmsg, 0, 5}, /* 337 */
+ {"fanotify_init", NULL, NOSYS_NULL, 0}, /* 338 */
+ {"fanotify_mark", NULL, NOSYS_NULL, 0}, /* 339 */
+ {"prlimit64", lx_prlimit64, 0, 4}, /* 340 */
+ {"name_to_handle_at", NULL, NOSYS_NULL, 0}, /* 341 */
+ {"open_by_handle_at", NULL, NOSYS_NULL, 0}, /* 342 */
+ {"clock_adjtime", NULL, NOSYS_NULL, 0}, /* 343 */
+ {"syncfs", lx_syncfs, 0, 1}, /* 344 */
+ {"sendmmsg", lx_sendmmsg, 0, 4}, /* 345 */
+ {"setns", NULL, NOSYS_NULL, 0}, /* 346 */
+ {"process_vm_readv", NULL, NOSYS_NULL, 0}, /* 347 */
+ {"process_vm_writev", NULL, NOSYS_NULL, 0}, /* 348 */
+ {"kcmp", NULL, NOSYS_NULL, 0}, /* 349 */
+ {"finit_module", NULL, NOSYS_NULL, 0}, /* 350 */
+ {"sched_setattr", lx_sched_setattr, 0, 3}, /* 351 */
+ {"sched_getattr", lx_sched_getattr, 0, 4}, /* 352 */
+ {"renameat2", NULL, NOSYS_NULL, 0}, /* 353 */
+ {"seccomp", NULL, NOSYS_NULL, 0}, /* 354 */
+ {"getrandom", lx_getrandom, 0, 3}, /* 355 */
+ {"memfd_create", NULL, NOSYS_NULL, 0}, /* 356 */
+ {"bpf", NULL, NOSYS_NULL, 0}, /* 357 */
+ {"execveat", NULL, NOSYS_NULL, 0}, /* 358 */
+};
+
+#if defined(_LP64)
+/*
+ * Linux defines system call numbers for 64-bit x86 in the file:
+ * arch/x86/syscalls/syscall_64.tbl
+ */
+lx_sysent_t lx_sysent64[] = {
+ {"read", lx_read, 0, 3}, /* 0 */
+ {"write", lx_write, 0, 3}, /* 1 */
+ {"open", lx_open, 0, 3}, /* 2 */
+ {"close", lx_close, 0, 1}, /* 3 */
+ {"stat", lx_stat64, 0, 2}, /* 4 */
+ {"fstat", lx_fstat64, 0, 2}, /* 5 */
+ {"lstat", lx_lstat64, 0, 2}, /* 6 */
+ {"poll", lx_poll, 0, 3}, /* 7 */
+ {"lseek", lx_lseek64, 0, 3}, /* 8 */
+ {"mmap", lx_mmap, 0, 6}, /* 9 */
+ {"mprotect", lx_mprotect, 0, 3}, /* 10 */
+ {"munmap", lx_munmap, 0, 2}, /* 11 */
+ {"brk", lx_brk, 0, 1}, /* 12 */
+ {"rt_sigaction", NULL, 0, 4}, /* 13 */
+ {"rt_sigprocmask", NULL, 0, 4}, /* 14 */
+ {"rt_sigreturn", NULL, 0, 0}, /* 15 */
+ {"ioctl", lx_ioctl, 0, 3}, /* 16 */
+ {"pread64", lx_pread, 0, 4}, /* 17 */
+ {"pwrite64", lx_pwrite, 0, 4}, /* 18 */
+ {"readv", lx_readv, 0, 3}, /* 19 */
+ {"writev", lx_writev, 0, 3}, /* 20 */
+ {"access", lx_access, 0, 2}, /* 21 */
+ {"pipe", lx_pipe, 0, 1}, /* 22 */
+ {"select", lx_select, 0, 5}, /* 23 */
+ {"sched_yield", lx_sched_yield, 0, 0}, /* 24 */
+ {"mremap", lx_mremap, 0, 5}, /* 25 */
+ {"msync", lx_msync, 0, 3}, /* 26 */
+ {"mincore", lx_mincore, 0, 3}, /* 27 */
+ {"madvise", lx_madvise, 0, 3}, /* 28 */
+ {"shmget", NULL, 0, 3}, /* 29 */
+ {"shmat", NULL, 0, 4}, /* 30 */
+ {"shmctl", NULL, 0, 3}, /* 31 */
+ {"dup", lx_dup, 0, 1}, /* 32 */
+ {"dup2", lx_dup2, 0, 2}, /* 33 */
+ {"pause", lx_pause, 0, 0}, /* 34 */
+ {"nanosleep", lx_nanosleep, 0, 2}, /* 35 */
+ {"getitimer", lx_getitimer, 0, 2}, /* 36 */
+ {"alarm", lx_alarm, 0, 1}, /* 37 */
+ {"setitimer", NULL, 0, 3}, /* 38 */
+ {"getpid", lx_getpid, 0, 0}, /* 39 */
+ {"sendfile", NULL, 0, 4}, /* 40 */
+ {"socket", lx_socket, 0, 3}, /* 41 */
+ {"connect", lx_connect, 0, 3}, /* 42 */
+ {"accept", lx_accept, 0, 3}, /* 43 */
+ {"sendto", lx_sendto, 0, 6}, /* 44 */
+ {"recvfrom", lx_recvfrom, 0, 6}, /* 45 */
+ {"sendmsg", lx_sendmsg, 0, 3}, /* 46 */
+ {"recvmsg", lx_recvmsg, 0, 3}, /* 47 */
+ {"shutdown", lx_shutdown, 0, 2}, /* 48 */
+ {"bind", lx_bind, 0, 3}, /* 49 */
+ {"listen", lx_listen, 0, 2}, /* 50 */
+ {"getsockname", lx_getsockname, 0, 3}, /* 51 */
+ {"getpeername", lx_getpeername, 0, 3}, /* 52 */
+ {"socketpair", lx_socketpair, 0, 4}, /* 53 */
+ {"setsockopt", lx_setsockopt, 0, 5}, /* 54 */
+ {"getsockopt", lx_getsockopt, 0, 5}, /* 55 */
+ {"clone", NULL, 0, 5}, /* 56 */
+ {"fork", NULL, 0, 0}, /* 57 */
+ {"vfork", NULL, 0, 0}, /* 58 */
+ {"execve", NULL, 0, 3}, /* 59 */
+ {"exit", NULL, 0, 1}, /* 60 */
+ {"wait4", lx_wait4, 0, 4}, /* 61 */
+ {"kill", lx_kill, 0, 2}, /* 62 */
+ {"uname", lx_uname, 0, 1}, /* 63 */
+ {"semget", NULL, 0, 3}, /* 64 */
+ {"semop", NULL, 0, 3}, /* 65 */
+ {"semctl", NULL, 0, 4}, /* 66 */
+ {"shmdt", NULL, 0, 1}, /* 67 */
+ {"msgget", NULL, 0, 2}, /* 68 */
+ {"msgsnd", NULL, 0, 4}, /* 69 */
+ {"msgrcv", NULL, 0, 5}, /* 70 */
+ {"msgctl", NULL, 0, 3}, /* 71 */
+ {"fcntl", lx_fcntl64, 0, 3}, /* 72 */
+ {"flock", lx_flock, 0, 2}, /* 73 */
+ {"fsync", NULL, 0, 1}, /* 74 */
+ {"fdatasync", NULL, 0, 1}, /* 75 */
+ {"truncate", NULL, 0, 2}, /* 76 */
+ {"ftruncate", NULL, 0, 2}, /* 77 */
+ {"getdents", lx_getdents_64, 0, 3}, /* 78 */
+ {"getcwd", lx_getcwd, 0, 2}, /* 79 */
+ {"chdir", lx_chdir, 0, 1}, /* 80 */
+ {"fchdir", lx_fchdir, 0, 1}, /* 81 */
+ {"rename", lx_rename, 0, 2}, /* 82 */
+ {"mkdir", lx_mkdir, 0, 2}, /* 83 */
+ {"rmdir", NULL, 0, 1}, /* 84 */
+ {"creat", lx_creat, 0, 2}, /* 85 */
+ {"link", lx_link, 0, 2}, /* 86 */
+ {"unlink", lx_unlink, 0, 1}, /* 87 */
+ {"symlink", lx_symlink, 0, 2}, /* 88 */
+ {"readlink", lx_readlink, 0, 3}, /* 89 */
+ {"chmod", lx_chmod, 0, 2}, /* 90 */
+ {"fchmod", lx_fchmod, 0, 2}, /* 91 */
+ {"chown", lx_chown, 0, 3}, /* 92 */
+ {"fchown", lx_fchown, 0, 3}, /* 93 */
+ {"lchown", lx_lchown, 0, 3}, /* 94 */
+ {"umask", lx_umask, 0, 1}, /* 95 */
+ {"gettimeofday", lx_gettimeofday, 0, 2}, /* 96 */
+ {"getrlimit", lx_getrlimit, 0, 2}, /* 97 */
+ {"getrusage", lx_getrusage, 0, 2}, /* 98 */
+ {"sysinfo", lx_sysinfo64, 0, 1}, /* 99 */
+ {"times", lx_times, 0, 1}, /* 100 */
+ {"ptrace", lx_ptrace, 0, 4}, /* 101 */
+ {"getuid", lx_getuid, 0, 0}, /* 102 */
+ {"syslog", lx_syslog, 0, 3}, /* 103 */
+ {"getgid", lx_getgid, 0, 0}, /* 104 */
+ {"setuid", lx_setuid, 0, 1}, /* 105 */
+ {"setgid", lx_setgid, 0, 1}, /* 106 */
+ {"geteuid", lx_geteuid, 0, 0}, /* 107 */
+ {"getegid", lx_getegid, 0, 0}, /* 108 */
+ {"setpgid", lx_setpgid, 0, 2}, /* 109 */
+ {"getppid", lx_getppid, 0, 0}, /* 110 */
+ {"getpgrp", lx_getpgrp, 0, 0}, /* 111 */
+ {"setsid", lx_setsid, 0, 0}, /* 112 */
+ {"setreuid", lx_setreuid, 0, 0}, /* 113 */
+ {"setregid", lx_setregid, 0, 0}, /* 114 */
+ {"getgroups", NULL, 0, 2}, /* 115 */
+ {"setgroups", NULL, 0, 2}, /* 116 */
+ {"setresuid", lx_setresuid, 0, 3}, /* 117 */
+ {"getresuid", lx_getresuid, 0, 3}, /* 118 */
+ {"setresgid", lx_setresgid, 0, 3}, /* 119 */
+ {"getresgid", lx_getresgid, 0, 3}, /* 120 */
+ {"getpgid", lx_getpgid, 0, 1}, /* 121 */
+ {"setfsuid", lx_setfsuid, 0, 1}, /* 122 */
+ {"setfsgid", lx_setfsgid, 0, 1}, /* 123 */
+ {"getsid", lx_getsid, 0, 1}, /* 124 */
+ {"capget", NULL, 0, 2}, /* 125 */
+ {"capset", NULL, 0, 2}, /* 126 */
+ {"rt_sigpending", NULL, 0, 2}, /* 127 */
+ {"rt_sigtimedwait", NULL, 0, 4}, /* 128 */
+ {"rt_sigqueueinfo", NULL, 0, 3}, /* 129 */
+ {"rt_sigsuspend", NULL, 0, 2}, /* 130 */
+ {"sigaltstack", NULL, 0, 2}, /* 131 */
+ {"utime", NULL, 0, 2}, /* 132 */
+ {"mknod", NULL, 0, 3}, /* 133 */
+ {"uselib", NULL, NOSYS_KERNEL, 0}, /* 134 */
+ {"personality", lx_personality, 0, 1}, /* 135 */
+ {"ustat", NULL, NOSYS_OBSOLETE, 2}, /* 136 */
+ {"statfs", NULL, 0, 2}, /* 137 */
+ {"fstatfs", NULL, 0, 2}, /* 138 */
+ {"sysfs", NULL, 0, 3}, /* 139 */
+ {"getpriority", lx_getpriority, 0, 2}, /* 140 */
+ {"setpriority", lx_setpriority, 0, 3}, /* 141 */
+ {"sched_setparam", lx_sched_setparam, 0, 2}, /* 142 */
+ {"sched_getparam", lx_sched_getparam, 0, 2}, /* 143 */
+ {"sched_setscheduler", lx_sched_setscheduler, 0, 3}, /* 144 */
+ {"sched_getscheduler", lx_sched_getscheduler, 0, 1}, /* 145 */
+ {"sched_get_priority_max", lx_sched_get_priority_max, 0, 1}, /* 146 */
+ {"sched_get_priority_min", lx_sched_get_priority_min, 0, 1}, /* 147 */
+ {"sched_rr_get_interval", lx_sched_rr_get_interval, 0, 2}, /* 148 */
+ {"mlock", lx_mlock, 0, 2}, /* 149 */
+ {"munlock", lx_munlock, 0, 2}, /* 150 */
+ {"mlockall", lx_mlockall, 0, 1}, /* 151 */
+ {"munlockall", lx_munlockall, 0, 0}, /* 152 */
+ {"vhangup", lx_vhangup, 0, 0}, /* 153 */
+ {"modify_ldt", lx_modify_ldt, 0, 3}, /* 154 */
+ {"pivot_root", NULL, NOSYS_KERNEL, 0}, /* 155 */
+ {"sysctl", NULL, 0, 1}, /* 156 */
+ {"prctl", lx_prctl, 0, 5}, /* 157 */
+ {"arch_prctl", lx_arch_prctl, 0, 2}, /* 158 */
+ {"adjtimex", NULL, 0, 1}, /* 159 */
+ {"setrlimit", lx_setrlimit, 0, 2}, /* 160 */
+ {"chroot", lx_chroot, 0, 1}, /* 161 */
+ {"sync", lx_sync, 0, 0}, /* 162 */
+ {"acct", lx_acct, 0, 1}, /* 163 */
+ {"settimeofday", NULL, 0, 2}, /* 164 */
+ {"mount", lx_mount, 0, 5}, /* 165 */
+ {"umount2", lx_umount2, 0, 2}, /* 166 */
+ {"swapon", lx_swapon, 0, 2}, /* 167 */
+ {"swapoff", lx_swapoff, 0, 1}, /* 168 */
+ {"reboot", lx_reboot, 0, 4}, /* 169 */
+ {"sethostname", lx_sethostname, 0, 2}, /* 170 */
+ {"setdomainname", lx_setdomainname, 0, 2}, /* 171 */
+ {"iopl", NULL, NOSYS_NO_EQUIV, 0}, /* 172 */
+ {"ioperm", NULL, NOSYS_NO_EQUIV, 0}, /* 173 */
+ {"create_module", NULL, NOSYS_KERNEL, 0}, /* 174 */
+ {"init_module", NULL, NOSYS_KERNEL, 0}, /* 175 */
+ {"delete_module", NULL, NOSYS_KERNEL, 0}, /* 176 */
+ {"get_kernel_syms", NULL, NOSYS_KERNEL, 0}, /* 177 */
+ {"query_module", NULL, 0, 5}, /* 178 */
+ {"quotactl", NULL, NOSYS_KERNEL, 0}, /* 179 */
+ {"nfsservctl", NULL, NOSYS_KERNEL, 0}, /* 180 */
+ {"getpmsg", NULL, NOSYS_OBSOLETE, 0}, /* 181 */
+ {"putpmsg", NULL, NOSYS_OBSOLETE, 0}, /* 182 */
+ {"afs_syscall", NULL, NOSYS_KERNEL, 0}, /* 183 */
+ {"tux", NULL, NOSYS_NO_EQUIV, 0}, /* 184 */
+ {"security", NULL, NOSYS_NO_EQUIV, 0}, /* 185 */
+ {"gettid", lx_gettid, 0, 0}, /* 186 */
+ {"readahead", NULL, NOSYS_NO_EQUIV, 0}, /* 187 */
+ {"setxattr", lx_setxattr, 0, 5}, /* 188 */
+ {"lsetxattr", lx_lsetxattr, 0, 5}, /* 189 */
+ {"fsetxattr", lx_fsetxattr, 0, 5}, /* 190 */
+ {"getxattr", lx_getxattr, 0, 4}, /* 191 */
+ {"lgetxattr", lx_lgetxattr, 0, 4}, /* 192 */
+ {"fgetxattr", lx_fgetxattr, 0, 4}, /* 193 */
+ {"listxattr", lx_listxattr, 0, 3}, /* 194 */
+ {"llistxattr", lx_llistxattr, 0, 3}, /* 195 */
+ {"flistxattr", lx_flistxattr, 0, 3}, /* 196 */
+ {"removexattr", lx_removexattr, 0, 2}, /* 197 */
+ {"lremovexattr", lx_lremovexattr, 0, 2}, /* 198 */
+ {"fremovexattr", lx_fremovexattr, 0, 2}, /* 199 */
+ {"tkill", lx_tkill, 0, 2}, /* 200 */
+ {"time", lx_time, 0, 1}, /* 201 */
+ {"futex", lx_futex, 0, 6}, /* 202 */
+ {"sched_setaffinity", lx_sched_setaffinity, 0, 3}, /* 203 */
+ {"sched_getaffinity", lx_sched_getaffinity, 0, 3}, /* 204 */
+ {"set_thread_area", lx_set_thread_area, 0, 1}, /* 205 */
+ {"io_setup", lx_io_setup, 0, 2}, /* 206 */
+ {"io_destroy", lx_io_destroy, 0, 1}, /* 207 */
+ {"io_getevents", lx_io_getevents, 0, 5}, /* 208 */
+ {"io_submit", lx_io_submit, 0, 3}, /* 209 */
+ {"io_cancel", lx_io_cancel, 0, 3}, /* 210 */
+ {"get_thread_area", lx_get_thread_area, 0, 1}, /* 211 */
+ {"lookup_dcookie", NULL, NOSYS_NO_EQUIV, 0}, /* 212 */
+ {"epoll_create", lx_epoll_create, 0, 1}, /* 213 */
+ {"epoll_ctl_old", NULL, NOSYS_NULL, 0}, /* 214 */
+ {"epoll_wait_old", NULL, NOSYS_NULL, 0}, /* 215 */
+ {"remap_file_pages", NULL, NOSYS_NO_EQUIV, 0}, /* 216 */
+ {"getdents64", lx_getdents64, 0, 3}, /* 217 */
+ {"set_tid_address", lx_set_tid_address, 0, 1}, /* 218 */
+ {"restart_syscall", NULL, NOSYS_NULL, 0}, /* 219 */
+ {"semtimedop", NULL, 0, 4}, /* 220 */
+ {"fadvise64", lx_fadvise64, 0, 4}, /* 221 */
+ {"timer_create", lx_timer_create, 0, 3}, /* 222 */
+ {"timer_settime", NULL, 0, 4}, /* 223 */
+ {"timer_gettime", NULL, 0, 2}, /* 224 */
+ {"timer_getoverrun", NULL, 0, 1}, /* 225 */
+ {"timer_delete", NULL, 0, 1}, /* 226 */
+ {"clock_settime", lx_clock_settime, 0, 2}, /* 227 */
+ {"clock_gettime", lx_clock_gettime, 0, 2}, /* 228 */
+ {"clock_getres", lx_clock_getres, 0, 2}, /* 229 */
+ {"clock_nanosleep", NULL, 0, 4}, /* 230 */
+ {"exit_group", NULL, 0, 1}, /* 231 */
+ {"epoll_wait", lx_epoll_wait, 0, 4}, /* 232 */
+ {"epoll_ctl", lx_epoll_ctl, 0, 4}, /* 233 */
+ {"tgkill", lx_tgkill, 0, 3}, /* 234 */
+ {"utimes", NULL, 0, 2}, /* 235 */
+ {"vserver", NULL, NOSYS_NULL, 0}, /* 236 */
+ {"mbind", NULL, NOSYS_NULL, 0}, /* 237 */
+ {"set_mempolicy", NULL, NOSYS_NULL, 0}, /* 238 */
+ {"get_mempolicy", NULL, NOSYS_NULL, 0}, /* 239 */
+ {"mq_open", NULL, NOSYS_NULL, 0}, /* 240 */
+ {"mq_unlink", NULL, NOSYS_NULL, 0}, /* 241 */
+ {"mq_timedsend", NULL, NOSYS_NULL, 0}, /* 242 */
+ {"mq_timedreceive", NULL, NOSYS_NULL, 0}, /* 243 */
+ {"mq_notify", NULL, NOSYS_NULL, 0}, /* 244 */
+ {"mq_getsetattr", NULL, NOSYS_NULL, 0}, /* 245 */
+ {"kexec_load", NULL, NOSYS_NULL, 0}, /* 246 */
+ {"waitid", lx_waitid, 0, 4}, /* 247 */
+ {"add_key", NULL, NOSYS_NULL, 0}, /* 248 */
+ {"request_key", NULL, NOSYS_NULL, 0}, /* 249 */
+ {"keyctl", NULL, NOSYS_NULL, 0}, /* 250 */
+ {"ioprio_set", lx_ioprio_set, 0, 3}, /* 251 */
+ {"ioprio_get", lx_ioprio_get, 0, 2}, /* 252 */
+ {"inotify_init", NULL, 0, 0}, /* 253 */
+ {"inotify_add_watch", NULL, 0, 3}, /* 254 */
+ {"inotify_rm_watch", NULL, 0, 2}, /* 255 */
+ {"migrate_pages", NULL, NOSYS_NULL, 0}, /* 256 */
+ {"openat", lx_openat, 0, 4}, /* 257 */
+ {"mkdirat", lx_mkdirat, 0, 3}, /* 258 */
+ {"mknodat", NULL, 0, 4}, /* 259 */
+ {"fchownat", lx_fchownat, 0, 5}, /* 260 */
+ {"futimesat", NULL, 0, 3}, /* 261 */
+ {"fstatat64", lx_fstatat64, 0, 4}, /* 262 */
+ {"unlinkat", lx_unlinkat, 0, 3}, /* 263 */
+ {"renameat", lx_renameat, 0, 4}, /* 264 */
+ {"linkat", lx_linkat, 0, 5}, /* 265 */
+ {"symlinkat", lx_symlinkat, 0, 3}, /* 266 */
+ {"readlinkat", lx_readlinkat, 0, 4}, /* 267 */
+ {"fchmodat", lx_fchmodat, 0, 3}, /* 268 */
+ {"faccessat", lx_faccessat, 0, 4}, /* 269 */
+ {"pselect6", lx_pselect, 0, 6}, /* 270 */
+ {"ppoll", lx_ppoll, 0, 5}, /* 271 */
+ {"unshare", lx_unshare, 0, 1}, /* 272 */
+ {"set_robust_list", lx_set_robust_list, 0, 2}, /* 273 */
+ {"get_robust_list", lx_get_robust_list, 0, 3}, /* 274 */
+ {"splice", lx_splice, 0, 6}, /* 275 */
+ {"tee", NULL, NOSYS_NULL, 0}, /* 276 */
+ {"sync_file_range", lx_sync_file_range, 0, 4}, /* 277 */
+ {"vmsplice", NULL, NOSYS_NULL, 0}, /* 278 */
+ {"move_pages", NULL, NOSYS_NULL, 0}, /* 279 */
+ {"utimensat", NULL, 0, 4}, /* 280 */
+ {"epoll_pwait", lx_epoll_pwait, 0, 5}, /* 281 */
+ {"signalfd", NULL, 0, 3}, /* 282 */
+ {"timerfd_create", NULL, 0, 2}, /* 283 */
+ {"eventfd", lx_eventfd, 0, 1}, /* 284 */
+ {"fallocate", lx_fallocate, 0, 4}, /* 285 */
+ {"timerfd_settime", NULL, 0, 4}, /* 286 */
+ {"timerfd_gettime", NULL, 0, 2}, /* 287 */
+ {"accept4", lx_accept4, 0, 4}, /* 288 */
+ {"signalfd4", NULL, 0, 4}, /* 289 */
+ {"eventfd2", lx_eventfd2, 0, 2}, /* 290 */
+ {"epoll_create1", lx_epoll_create1, 0, 1}, /* 291 */
+ {"dup3", lx_dup3, 0, 3}, /* 292 */
+ {"pipe2", lx_pipe2, 0, 2}, /* 293 */
+ {"inotify_init1", NULL, 0, 1}, /* 294 */
+ {"preadv", lx_preadv, 0, 4}, /* 295 */
+ {"pwritev", lx_pwritev, 0, 4}, /* 296 */
+ {"rt_tgsigqueueinfo", NULL, 0, 4}, /* 297 */
+ {"perf_event_open", NULL, NOSYS_NULL, 0}, /* 298 */
+ {"recvmmsg", lx_recvmmsg, 0, 5}, /* 299 */
+ {"fanotify_init", NULL, NOSYS_NULL, 0}, /* 300 */
+ {"fanotify_mark", NULL, NOSYS_NULL, 0}, /* 301 */
+ {"prlimit64", lx_prlimit64, 0, 4}, /* 302 */
+ {"name_to_handle_at", NULL, NOSYS_NULL, 0}, /* 303 */
+ {"open_by_handle_at", NULL, NOSYS_NULL, 0}, /* 304 */
+ {"clock_adjtime", NULL, NOSYS_NULL, 0}, /* 305 */
+ {"syncfs", lx_syncfs, 0, 1}, /* 306 */
+ {"sendmmsg", lx_sendmmsg, 0, 4}, /* 307 */
+ {"setns", NULL, NOSYS_NULL, 0}, /* 309 */
+ {"getcpu", lx_getcpu, 0, 3}, /* 309 */
+ {"process_vm_readv", NULL, NOSYS_NULL, 0}, /* 310 */
+ {"process_vm_writev", NULL, NOSYS_NULL, 0}, /* 311 */
+ {"kcmp", NULL, NOSYS_NULL, 0}, /* 312 */
+ {"finit_module", NULL, NOSYS_NULL, 0}, /* 313 */
+ {"sched_setattr", lx_sched_setattr, 0, 3}, /* 314 */
+ {"sched_getattr", lx_sched_getattr, 0, 4}, /* 315 */
+ {"renameat2", NULL, NOSYS_NULL, 0}, /* 316 */
+ {"seccomp", NULL, NOSYS_NULL, 0}, /* 317 */
+ {"getrandom", lx_getrandom, 0, 3}, /* 318 */
+ {"memfd_create", NULL, NOSYS_NULL, 0}, /* 319 */
+ {"kexec_file_load", NULL, NOSYS_NULL, 0}, /* 320 */
+ {"bpf", NULL, NOSYS_NULL, 0}, /* 321 */
+ {"execveat", NULL, NOSYS_NULL, 0}, /* 322 */
+
+ /* XXX TBD gap then x32 syscalls from 512 - 544 */
+};
+#endif
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_proc.h b/usr/src/uts/common/brand/lx/procfs/lx_proc.h
new file mode 100644
index 0000000000..f4d18fffc1
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/procfs/lx_proc.h
@@ -0,0 +1,394 @@
+/*
+ * 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 2019 Joyent, Inc.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#ifndef _LX_PROC_H
+#define _LX_PROC_H
+
+#ifdef _LXPROC_NATIVE_H
+#error Attempted to include branded lx_proc.h after native lxproc.h
+#endif
+
+#define _LXPROC_BRANDED_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * lxproc.h: declarations, data structures and macros for lxprocfs
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/debug.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/user.h>
+#include <sys/t_lock.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/dnlc.h>
+#include <sys/atomic.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/nvpair.h>
+#include <vm/as.h>
+#include <vm/anon.h>
+
+/*
+ * Convert a vnode into an lxpr_mnt_t
+ */
+#define VTOLXPM(vp) ((lxpr_mnt_t *)(vp)->v_vfsp->vfs_data)
+
+/*
+ * convert a vnode into an lxpr_node
+ */
+#define VTOLXP(vp) ((lxpr_node_t *)(vp)->v_data)
+
+/*
+ * convert a lxprnode into a vnode
+ */
+#define LXPTOV(lxpnp) ((lxpnp)->lxpr_vnode)
+
+/*
+ * convert a lxpr_node into zone for fs
+ */
+#define LXPTOZ(lxpnp) \
+ (((lxpr_mnt_t *)(lxpnp)->lxpr_vnode->v_vfsp->vfs_data)->lxprm_zone)
+
+#define LXPNSIZ 256 /* max size of lx /proc file name entries */
+
+/*
+ * Pretend that a directory entry takes 16 bytes
+ */
+#define LXPR_SDSIZE 16
+
+/*
+ * Node/file types for lx /proc files
+ * (directories and files contained therein).
+ */
+typedef enum lxpr_nodetype {
+ LXPR_INVALID, /* nodes start at 1 */
+ LXPR_PROCDIR, /* /proc */
+ LXPR_PIDDIR, /* /proc/<pid> */
+ LXPR_PID_AUXV, /* /proc/<pid>/auxv */
+ LXPR_PID_CGROUP, /* /proc/<pid>/cgroup */
+ LXPR_PID_CMDLINE, /* /proc/<pid>/cmdline */
+ LXPR_PID_COMM, /* /proc/<pid>/comm */
+ LXPR_PID_CPU, /* /proc/<pid>/cpu */
+ LXPR_PID_CURDIR, /* /proc/<pid>/cwd */
+ LXPR_PID_ENV, /* /proc/<pid>/environ */
+ LXPR_PID_EXE, /* /proc/<pid>/exe */
+ LXPR_PID_GIDMAP, /* /proc/<pid>/gid_map */
+ LXPR_PID_LIMITS, /* /proc/<pid>/limits */
+ LXPR_PID_LOGINUID, /* /proc/<pid>/loginuid */
+ LXPR_PID_MAPS, /* /proc/<pid>/maps */
+ LXPR_PID_MEM, /* /proc/<pid>/mem */
+ LXPR_PID_MOUNTINFO, /* /proc/<pid>/mountinfo */
+ LXPR_PID_MOUNTS, /* /proc/<pid>/mounts */
+ LXPR_PID_OOM_SCR_ADJ, /* /proc/<pid>/oom_score_adj */
+ LXPR_PID_PERSONALITY, /* /proc/<pid>/personality */
+ LXPR_PID_ROOTDIR, /* /proc/<pid>/root */
+ LXPR_PID_STAT, /* /proc/<pid>/stat */
+ LXPR_PID_STATM, /* /proc/<pid>/statm */
+ LXPR_PID_STATUS, /* /proc/<pid>/status */
+ LXPR_PID_TASKDIR, /* /proc/<pid>/task */
+ LXPR_PID_TASK_IDDIR, /* /proc/<pid>/task/<tid> */
+ LXPR_PID_FDDIR, /* /proc/<pid>/fd */
+ LXPR_PID_FD_FD, /* /proc/<pid>/fd/nn */
+ LXPR_PID_FDINFODIR, /* /proc/<pid>/fdinfo */
+ LXPR_PID_FDINFO_FD, /* /proc/<pid>/fdinfo/nn */
+ LXPR_PID_UIDMAP, /* /proc/<pid>/uid_map */
+ LXPR_PID_TID_AUXV, /* /proc/<pid>/task/<tid>/auxv */
+ LXPR_PID_TID_CGROUP, /* /proc/<pid>/task/<tid>/cgroup */
+ LXPR_PID_TID_CMDLINE, /* /proc/<pid>/task/<tid>/cmdline */
+ LXPR_PID_TID_COMM, /* /proc/<pid>/task/<tid>/comm */
+ LXPR_PID_TID_CPU, /* /proc/<pid>/task/<tid>/cpu */
+ LXPR_PID_TID_CURDIR, /* /proc/<pid>/task/<tid>/cwd */
+ LXPR_PID_TID_ENV, /* /proc/<pid>/task/<tid>/environ */
+ LXPR_PID_TID_EXE, /* /proc/<pid>/task/<tid>/exe */
+ LXPR_PID_TID_GIDMAP, /* /proc/<pid>/task/<tid>/gid_map */
+ LXPR_PID_TID_LIMITS, /* /proc/<pid>/task/<tid>/limits */
+ LXPR_PID_TID_LOGINUID, /* /proc/<pid>/task/<tid>/loginuid */
+ LXPR_PID_TID_MAPS, /* /proc/<pid>/task/<tid>/maps */
+ LXPR_PID_TID_MEM, /* /proc/<pid>/task/<tid>/mem */
+ LXPR_PID_TID_MOUNTINFO, /* /proc/<pid>/task/<tid>/mountinfo */
+ LXPR_PID_TID_OOM_SCR_ADJ, /* /proc/<pid>/task/<tid>/oom_score_adj */
+ LXPR_PID_TID_PERSONALITY, /* /proc/<pid>/task/<tid>/personality */
+ LXPR_PID_TID_ROOTDIR, /* /proc/<pid>/task/<tid>/root */
+ LXPR_PID_TID_STAT, /* /proc/<pid>/task/<tid>/stat */
+ LXPR_PID_TID_STATM, /* /proc/<pid>/task/<tid>/statm */
+ LXPR_PID_TID_STATUS, /* /proc/<pid>/task/<tid>/status */
+ LXPR_PID_TID_FDDIR, /* /proc/<pid>/task/<tid>/fd */
+ LXPR_PID_TID_FD_FD, /* /proc/<pid>/task/<tid>/fd/nn */
+ LXPR_PID_TID_FDINFODIR, /* /proc/<pid>/task/<tid>/fdinfo */
+ LXPR_PID_TID_FDINFO_FD, /* /proc/<pid>/task/<tid>/fdinfo/nn */
+ LXPR_PID_TID_UIDMAP, /* /proc/<pid>/task/<tid>/uid_map */
+ LXPR_CGROUPS, /* /proc/cgroups */
+ LXPR_CMDLINE, /* /proc/cmdline */
+ LXPR_CPUINFO, /* /proc/cpuinfo */
+ LXPR_DEVICES, /* /proc/devices */
+ LXPR_DISKSTATS, /* /proc/diskstats */
+ LXPR_DMA, /* /proc/dma */
+ LXPR_FILESYSTEMS, /* /proc/filesystems */
+ LXPR_INTERRUPTS, /* /proc/interrupts */
+ LXPR_IOPORTS, /* /proc/ioports */
+ LXPR_KCORE, /* /proc/kcore */
+ LXPR_KMSG, /* /proc/kmsg */
+ LXPR_LOADAVG, /* /proc/loadavg */
+ LXPR_MEMINFO, /* /proc/meminfo */
+ LXPR_MODULES, /* /proc/modules */
+ LXPR_MOUNTS, /* /proc/mounts */
+ LXPR_NETDIR, /* /proc/net */
+ LXPR_NET_ARP, /* /proc/net/arp */
+ LXPR_NET_DEV, /* /proc/net/dev */
+ LXPR_NET_DEV_MCAST, /* /proc/net/dev_mcast */
+ LXPR_NET_IF_INET6, /* /proc/net/if_inet6 */
+ LXPR_NET_IGMP, /* /proc/net/igmp */
+ LXPR_NET_IP_MR_CACHE, /* /proc/net/ip_mr_cache */
+ LXPR_NET_IP_MR_VIF, /* /proc/net/ip_mr_vif */
+ LXPR_NET_IPV6_ROUTE, /* /proc/net/ipv6_route */
+ LXPR_NET_MCFILTER, /* /proc/net/mcfilter */
+ LXPR_NET_NETSTAT, /* /proc/net/netstat */
+ LXPR_NET_RAW, /* /proc/net/raw */
+ LXPR_NET_ROUTE, /* /proc/net/route */
+ LXPR_NET_RPC, /* /proc/net/rpc */
+ LXPR_NET_RT_CACHE, /* /proc/net/rt_cache */
+ LXPR_NET_SOCKSTAT, /* /proc/net/sockstat */
+ LXPR_NET_SNMP, /* /proc/net/snmp */
+ LXPR_NET_STAT, /* /proc/net/stat */
+ LXPR_NET_TCP, /* /proc/net/tcp */
+ LXPR_NET_TCP6, /* /proc/net/tcp6 */
+ LXPR_NET_UDP, /* /proc/net/udp */
+ LXPR_NET_UDP6, /* /proc/net/udp6 */
+ LXPR_NET_UNIX, /* /proc/net/unix */
+ LXPR_PARTITIONS, /* /proc/partitions */
+ LXPR_SELF, /* /proc/self */
+ LXPR_STAT, /* /proc/stat */
+ LXPR_SWAPS, /* /proc/swaps */
+ LXPR_SYSDIR, /* /proc/sys/ */
+ LXPR_SYS_FSDIR, /* /proc/sys/fs/ */
+ LXPR_SYS_FS_AIO_MAX_NR, /* /proc/sys/fs/aio-max-nr */
+ LXPR_SYS_FS_AIO_NR, /* /proc/sys/fs/aio-nr */
+ LXPR_SYS_FS_FILEMAX, /* /proc/sys/fs/file-max */
+ LXPR_SYS_FS_FILENR, /* /proc/sys/fs/file-nr */
+ LXPR_SYS_FS_INOTIFYDIR, /* /proc/sys/fs/inotify */
+ LXPR_SYS_FS_INOTIFY_MAX_QUEUED_EVENTS, /* inotify/max_queued_events */
+ LXPR_SYS_FS_INOTIFY_MAX_USER_INSTANCES, /* inotify/max_user_instances */
+ LXPR_SYS_FS_INOTIFY_MAX_USER_WATCHES, /* inotify/max_user_watches */
+ LXPR_SYS_FS_PIPE_MAX, /* /proc/sys/fs/pipe-max-size */
+ LXPR_SYS_KERNELDIR, /* /proc/sys/kernel/ */
+ LXPR_SYS_KERNEL_CAPLCAP, /* /proc/sys/kernel/cap_last_cap */
+ LXPR_SYS_KERNEL_COREPATT, /* /proc/sys/kernel/core_pattern */
+ LXPR_SYS_KERNEL_HOSTNAME, /* /proc/sys/kernel/hostname */
+ LXPR_SYS_KERNEL_MSGMAX, /* /proc/sys/kernel/msgmax */
+ LXPR_SYS_KERNEL_MSGMNB, /* /proc/sys/kernel/msgmnb */
+ LXPR_SYS_KERNEL_MSGMNI, /* /proc/sys/kernel/msgmni */
+ LXPR_SYS_KERNEL_NGROUPS_MAX, /* /proc/sys/kernel/ngroups_max */
+ LXPR_SYS_KERNEL_OSREL, /* /proc/sys/kernel/osrelease */
+ LXPR_SYS_KERNEL_PID_MAX, /* /proc/sys/kernel/pid_max */
+ LXPR_SYS_KERNEL_RANDDIR, /* /proc/sys/kernel/random */
+ LXPR_SYS_KERNEL_RAND_BOOTID, /* /proc/sys/kernel/random/boot_id */
+ LXPR_SYS_KERNEL_RAND_ENTAVL, /* /proc/sys/kernel/random/entropy_avail */
+ LXPR_SYS_KERNEL_RAND_UUID, /* /proc/sys/kernel/random/uuid */
+ LXPR_SYS_KERNEL_SEM, /* /proc/sys/kernel/sem */
+ LXPR_SYS_KERNEL_SHMALL, /* /proc/sys/kernel/shmall */
+ LXPR_SYS_KERNEL_SHMMAX, /* /proc/sys/kernel/shmmax */
+ LXPR_SYS_KERNEL_SHMMNI, /* /proc/sys/kernel/shmmni */
+ LXPR_SYS_KERNEL_THREADS_MAX, /* /proc/sys/kernel/threads-max */
+ LXPR_SYS_NETDIR, /* /proc/sys/net */
+ LXPR_SYS_NET_COREDIR, /* /proc/sys/net/core */
+ LXPR_SYS_NET_CORE_SOMAXCON, /* /proc/sys/net/core/somaxconn */
+ LXPR_SYS_NET_IPV4DIR, /* /proc/sys/net/ipv4 */
+ LXPR_SYS_NET_IPV4_ICMP_EIB, /* .../icmp_echo_ignore_broadcasts */
+ LXPR_SYS_NET_IPV4_IP_FORWARD, /* .../net/ipv4/ip_forward */
+ LXPR_SYS_NET_IPV4_IP_LPORT_RANGE, /* .../net/ipv4/ip_local_port_range */
+ /* .../tcp_allowed_congestion_control */
+ LXPR_SYS_NET_IPV4_TCP_CC_ALLOW,
+ /* .../tcp_available_congestion_control */
+ LXPR_SYS_NET_IPV4_TCP_CC_AVAIL,
+ /* .../tcp_congestion_control */
+ LXPR_SYS_NET_IPV4_TCP_CC_CURR,
+ LXPR_SYS_NET_IPV4_TCP_FIN_TO, /* /proc/sys/net/ipv4/tcp_fin_timeout */
+ LXPR_SYS_NET_IPV4_TCP_KA_INT, /* .../net/ipv4/tcp_keepalive_intvl */
+ LXPR_SYS_NET_IPV4_TCP_KA_TIM, /* .../net/ipv4/tcp_keepalive_time */
+ LXPR_SYS_NET_IPV4_TCP_MAX_SYN_BL, /* .../net/ipv4/tcp_max_syn_backlog */
+ LXPR_SYS_NET_IPV4_TCP_RETRY2, /* /proc/sys/net/ipv4/tcp_retries2 */
+ LXPR_SYS_NET_IPV4_TCP_RMEM, /* /proc/sys/net/ipv4/tcp_rmem */
+ LXPR_SYS_NET_IPV4_TCP_SACK, /* /proc/sys/net/ipv4/tcp_sack */
+ LXPR_SYS_NET_IPV4_TCP_WINSCALE, /* .../net/ipv4/tcp_window_scaling */
+ LXPR_SYS_NET_IPV4_TCP_WMEM, /* /proc/sys/net/ipv4/tcp_wmem */
+ LXPR_SYS_VMDIR, /* /proc/sys/vm */
+ LXPR_SYS_VM_DIRTY_BG_BYTES, /* .../vm/dirty_background_bytes */
+ LXPR_SYS_VM_DIRTY_BG_RATIO, /* .../vm/dirty_background_ratio */
+ LXPR_SYS_VM_DIRTY_BYTES, /* /proc/sys/vm/dirty_bytes */
+ LXPR_SYS_VM_DIRTY_EXP_CS, /* .../vm/dirty_expire_centisecs */
+ LXPR_SYS_VM_DIRTY_RATIO, /* /proc/sys/vm/dirty_ratio */
+ LXPR_SYS_VM_DIRTYTIME_EXP_SEC, /* .../vm/dirtytime_expire_seconds */
+ LXPR_SYS_VM_DIRTY_WB_CS, /* .../vm/dirty_writeback_centisecs */
+ LXPR_SYS_VM_MAX_MAP_CNT, /* /proc/sys/vm/max_map_count */
+ LXPR_SYS_VM_MINFR_KB, /* /proc/sys/vm/min_free_kbytes */
+ LXPR_SYS_VM_NHUGEP, /* /proc/sys/vm/nr_hugepages */
+ LXPR_SYS_VM_OVERCOMMIT_MEM, /* /proc/sys/vm/overcommit_memory */
+ LXPR_SYS_VM_SWAPPINESS, /* /proc/sys/vm/swappiness */
+ LXPR_UPTIME, /* /proc/uptime */
+ LXPR_VERSION, /* /proc/version */
+ LXPR_VMSTAT, /* /proc/vmstat */
+ LXPR_NFILES /* number of lx /proc file types */
+} lxpr_nodetype_t;
+
+
+/*
+ * Number of fds allowed for in the inode number calculation
+ * per process (if a process has more fds then inode numbers
+ * may be duplicated)
+ */
+#define LXPR_FD_PERPROC 2000
+
+/*
+ * Linux sector size for /proc/diskstats
+ */
+#define LXPR_SECTOR_SIZE 512
+
+/*
+ * external dirent characteristics
+ */
+typedef struct {
+ lxpr_nodetype_t d_type;
+ char *d_name;
+} lxpr_dirent_t;
+
+/*
+ * This is the lxprocfs private data object
+ * which is attached to v_data in the vnode structure
+ */
+typedef struct lxpr_node {
+ lxpr_nodetype_t lxpr_type; /* type of this node */
+ vnode_t *lxpr_vnode; /* vnode for the node */
+ vnode_t *lxpr_parent; /* parent directory */
+ vnode_t *lxpr_realvp; /* real vnode, file in dirs */
+ timestruc_t lxpr_time; /* creation etc time for file */
+ mode_t lxpr_mode; /* file mode bits */
+ uid_t lxpr_uid; /* file owner */
+ gid_t lxpr_gid; /* file group owner */
+ pid_t lxpr_pid; /* pid of proc referred to */
+ uint_t lxpr_desc; /* addl. descriptor (fd or tid) */
+ ino_t lxpr_ino; /* node id */
+} lxpr_node_t;
+
+struct zone; /* forward declaration */
+
+/*
+ * This is the lxprocfs private data object
+ * which is attached to vfs_data in the vfs structure
+ */
+typedef struct lxpr_mnt {
+ lxpr_node_t *lxprm_node; /* node at root of proc mount */
+ struct zone *lxprm_zone; /* zone for this mount */
+ ldi_ident_t lxprm_li; /* ident for ldi */
+} lxpr_mnt_t;
+
+extern vnodeops_t *lxpr_vnodeops;
+extern int nproc_highbit; /* highbit(v.v_nproc) */
+
+typedef struct mounta mounta_t;
+
+extern void lxpr_initnodecache();
+extern void lxpr_fininodecache();
+extern void lxpr_initrootnode(lxpr_node_t **, vfs_t *);
+extern ino_t lxpr_inode(lxpr_nodetype_t, pid_t, int);
+extern ino_t lxpr_parentinode(lxpr_node_t *);
+extern boolean_t lxpr_is_writable(lxpr_nodetype_t);
+extern lxpr_node_t *lxpr_getnode(vnode_t *, lxpr_nodetype_t, proc_t *, int);
+extern void lxpr_freenode(lxpr_node_t *);
+extern vnode_t *lxpr_lookup_fdnode(vnode_t *, const char *);
+extern int lxpr_readlink_fdnode(lxpr_node_t *, char *, size_t);
+extern vnode_t *lxpr_lookup_fdinfonode(vnode_t *, const char *);
+extern int lxpr_open_flags_convert(offset_t, uint32_t);
+
+typedef struct lxpr_uiobuf {
+ uio_t *uiop;
+ char *buffer;
+ uint32_t buffsize;
+ char *pos;
+ size_t beg;
+ int error;
+} lxpr_uiobuf_t;
+
+extern lxpr_uiobuf_t *lxpr_uiobuf_new(uio_t *);
+extern void lxpr_uiobuf_free(lxpr_uiobuf_t *);
+extern int lxpr_uiobuf_flush(lxpr_uiobuf_t *);
+extern void lxpr_uiobuf_seek(lxpr_uiobuf_t *, offset_t);
+extern boolean_t lxpr_uiobuf_nonblock(lxpr_uiobuf_t *);
+extern void lxpr_uiobuf_write(lxpr_uiobuf_t *, const char *, size_t);
+extern void lxpr_uiobuf_printf(lxpr_uiobuf_t *, const char *, ...);
+extern void lxpr_uiobuf_seterr(lxpr_uiobuf_t *, int);
+
+extern int lxpr_core_path_l2s(const char *, char *, size_t);
+extern int lxpr_core_path_s2l(const char *, char *, size_t);
+
+typedef enum lxpr_zombok {
+ NO_ZOMB = 0,
+ ZOMB_OK
+} zombok_t;
+
+extern proc_t *lxpr_lock(lxpr_node_t *, zombok_t);
+extern proc_t *lxpr_lock_pid(lxpr_node_t *, pid_t, zombok_t, kthread_t **);
+extern void lxpr_unlock(proc_t *);
+extern netstack_t *lxpr_netstack(lxpr_node_t *);
+extern void lxpr_fixpid(zone_t *, proc_t *, pid_t *, pid_t *);
+extern file_t *lxpr_getf(proc_t *, uint_t, short *);
+extern void lxpr_releasef(proc_t *, uint_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef islower
+#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
+#endif
+#ifndef toupper
+#define toupper(x) (islower(x) ? (x) - 'a' + 'A' : (x))
+#endif
+
+#endif /* _LX_PROC_H */
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prsubr.c b/usr/src/uts/common/brand/lx/procfs/lx_prsubr.c
new file mode 100644
index 0000000000..967d594913
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/procfs/lx_prsubr.c
@@ -0,0 +1,1065 @@
+/*
+ * 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 2019 Joyent, Inc.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+/*
+ * lxprsubr.c: Various functions for the /lxproc vnodeops.
+ */
+
+#include <sys/varargs.h>
+
+#include <sys/cpuvar.h>
+#include <sys/mman.h>
+#include <sys/vmsystm.h>
+#include <sys/prsystm.h>
+#include <sys/brand.h>
+#include <sys/fcntl.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+
+#include "lx_proc.h"
+
+#define LXPRCACHE_NAME "lxbpr_cache"
+
+static int lxpr_node_constructor(void *, void *, int);
+static void lxpr_node_destructor(void *, void *);
+
+static kmem_cache_t *lxpr_node_cache;
+
+int lx_pr_bufsize = 4000;
+
+struct lxpr_zfs_ds {
+ list_node_t ds_link;
+ char ds_name[MAXPATHLEN];
+ uint64_t ds_cookie;
+};
+
+struct lxpr_uiobuf *
+lxpr_uiobuf_new(uio_t *uiop)
+{
+ /* Allocate memory for both lxpr_uiobuf and output buffer */
+ int bufsize = lx_pr_bufsize;
+ struct lxpr_uiobuf *uiobuf =
+ kmem_alloc(sizeof (struct lxpr_uiobuf) + bufsize, KM_SLEEP);
+
+ uiobuf->uiop = uiop;
+ uiobuf->buffer = (char *)&uiobuf[1];
+ uiobuf->buffsize = bufsize;
+ uiobuf->pos = uiobuf->buffer;
+ uiobuf->beg = 0;
+ uiobuf->error = 0;
+
+ return (uiobuf);
+}
+
+void
+lxpr_uiobuf_free(struct lxpr_uiobuf *uiobuf)
+{
+ ASSERT(uiobuf != NULL);
+ ASSERT(uiobuf->pos == uiobuf->buffer);
+
+ kmem_free(uiobuf, sizeof (struct lxpr_uiobuf) + uiobuf->buffsize);
+}
+
+void
+lxpr_uiobuf_seek(struct lxpr_uiobuf *uiobuf, offset_t offset)
+{
+ uiobuf->uiop->uio_offset = (off_t)offset;
+}
+
+boolean_t
+lxpr_uiobuf_nonblock(struct lxpr_uiobuf *uiobuf)
+{
+ if ((uiobuf->uiop->uio_fmode & FNONBLOCK) != 0)
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
+void
+lxpr_uiobuf_seterr(struct lxpr_uiobuf *uiobuf, int err)
+{
+ ASSERT(uiobuf->error == 0);
+
+ uiobuf->error = err;
+}
+
+int
+lxpr_uiobuf_flush(struct lxpr_uiobuf *uiobuf)
+{
+ off_t off = uiobuf->uiop->uio_offset;
+ caddr_t uaddr = uiobuf->buffer;
+ size_t beg = uiobuf->beg;
+ size_t size = (uintptr_t)uiobuf->pos - (uintptr_t)uaddr;
+
+ if (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ ASSERT(off >= beg);
+
+ if (beg + size > off && off >= 0)
+ uiobuf->error =
+ uiomove(uaddr + (off - beg), size - (off - beg),
+ UIO_READ, uiobuf->uiop);
+
+ uiobuf->beg += size;
+ }
+
+ uiobuf->pos = uaddr;
+
+ return (uiobuf->error);
+}
+
+void
+lxpr_uiobuf_write(struct lxpr_uiobuf *uiobuf, const char *buf, size_t size)
+{
+ /* While we can still carry on */
+ while (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ uintptr_t remain = (uintptr_t)uiobuf->buffsize -
+ ((uintptr_t)uiobuf->pos - (uintptr_t)uiobuf->buffer);
+
+ /* Enough space in buffer? */
+ if (remain >= size) {
+ bcopy(buf, uiobuf->pos, size);
+ uiobuf->pos += size;
+ return;
+ }
+
+ /* Not enough space, so copy all we can and try again */
+ bcopy(buf, uiobuf->pos, remain);
+ uiobuf->pos += remain;
+ (void) lxpr_uiobuf_flush(uiobuf);
+ buf += remain;
+ size -= remain;
+ }
+}
+
+#define TYPBUFFSIZE 256
+
+void
+lxpr_uiobuf_printf(struct lxpr_uiobuf *uiobuf, const char *fmt, ...)
+{
+ va_list args;
+ char buff[TYPBUFFSIZE];
+ int len;
+ char *buffer;
+
+ /* Can we still do any output */
+ if (uiobuf->error != 0 || uiobuf->uiop->uio_resid == 0)
+ return;
+
+ va_start(args, fmt);
+
+ /* Try using stack allocated buffer */
+ len = vsnprintf(buff, TYPBUFFSIZE, fmt, args);
+ if (len < TYPBUFFSIZE) {
+ va_end(args);
+ lxpr_uiobuf_write(uiobuf, buff, len);
+ return;
+ }
+
+ /* Not enough space in pre-allocated buffer */
+ buffer = kmem_alloc(len + 1, KM_SLEEP);
+
+ /*
+ * We know we allocated the correct amount of space
+ * so no check on the return value
+ */
+ (void) vsnprintf(buffer, len+1, fmt, args);
+ lxpr_uiobuf_write(uiobuf, buffer, len);
+ va_end(args);
+ kmem_free(buffer, len+1);
+}
+
+/*
+ * Lookup process, potentially constrained by pid associated with lxpr_node and
+ * return with p_lock and P_PR_LOCK held.
+ */
+proc_t *
+lxpr_lock_pid(lxpr_node_t *lxpnp, pid_t pid, zombok_t zombie_ok,
+ kthread_t **tp)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ proc_t *p;
+ kthread_t *t;
+ lx_pid_flag_t flags = LXP_PRLOCK;
+
+ ASSERT(!MUTEX_HELD(&pidlock));
+
+ /* Consider zsched to be invisible to LX */
+ if (pid == zone->zone_zsched->p_pid) {
+ return (NULL);
+ }
+ if (zombie_ok == ZOMB_OK) {
+ flags |= LXP_ZOMBOK;
+ }
+
+retry:
+ if (lx_lpid_lock(pid, zone, flags, &p, &t) != 0) {
+ return (NULL);
+ }
+
+ /*
+ * Make sure that thread lookups (where non-main LX threads are
+ * assigned a pid not equal to the encompassing parent) match the pid
+ * of the encompasing directory. This must be performed carefully for
+ * the Linux pid 1 as it will not equal the native pid despite the
+ * process matching.
+ *
+ * This is necessary to constrain paths such as /proc/<pid>/task/<tid>.
+ */
+ if (lxpnp->lxpr_pid != 0 && lxpnp->lxpr_pid != pid &&
+ !(pid == 1 && lxpnp->lxpr_pid == zone->zone_proc_initpid)) {
+ klwp_t *lwp;
+ lx_lwp_data_t *lwpd;
+
+ /*
+ * Only LWPs of branded processes will be accessible this way.
+ * The threads of native processes lack pid assignments which
+ * LX uses to emulate Linux's weird thread/process model.
+ */
+ if ((lwp = ttolwp(t)) == NULL ||
+ (lwpd = lwptolxlwp(lwp)) == NULL ||
+ lwpd->br_pid != pid) {
+ sprunlock(p);
+ return (NULL);
+ }
+ }
+
+ if (zombie_ok == NO_ZOMB &&
+ ((p->p_flag & SEXITING) || p->p_stat == SZOMB)) {
+ sprunlock(p);
+ return (NULL);
+ }
+
+ /*
+ * Accessing a process which is undergoing exec(2) is somewhat risky.
+ * In particular, the p_exec field is updated outside p_lock. To avoid
+ * this mess, access is denied when P_PR_EXEC set unless the caller
+ * happens to be the process itself. This allows actions such as
+ * re-exec()-ing /proc/<pid>/exe to make forward progress.
+ *
+ * All other callers must block until the flag is cleared.
+ */
+ if ((p->p_proc_flag & P_PR_EXEC) != 0) {
+ if (p != curproc) {
+ kmutex_t *mp;
+
+ /*
+ * Drop PR_LOCK and wait for the exec() to ping the CV
+ * once it has completed. Afterward, the pid is looked
+ * up again in case the process exited for some reason.
+ */
+ mp = &p->p_lock;
+ sprunprlock(p);
+ cv_wait(&pr_pid_cv[p->p_slot], mp);
+ mutex_exit(mp);
+ goto retry;
+ }
+ }
+
+ if (tp != NULL) {
+ *tp = t;
+ }
+ return (p);
+}
+
+netstack_t *
+lxpr_netstack(lxpr_node_t *lxpnp)
+{
+ return (netstack_hold_if_active(LXPTOZ(lxpnp)->zone_netstack));
+}
+
+/*
+ * Lookup process from pid associated with lxpr_node and return with p_lock and
+ * P_PR_LOCK held.
+ */
+proc_t *
+lxpr_lock(lxpr_node_t *lxpnp, zombok_t zombie_ok)
+{
+ return (lxpr_lock_pid(lxpnp, lxpnp->lxpr_pid, zombie_ok, NULL));
+}
+
+void
+lxpr_fixpid(zone_t *zone, proc_t *p, pid_t *pidp, pid_t *ppidp)
+{
+ pid_t pid = p->p_pid;
+ pid_t ppid = p->p_ppid;
+
+ ASSERT(p != NULL);
+ ASSERT(pidp != NULL);
+ ASSERT(zone->zone_brand == &lx_brand);
+ ASSERT(pid != zone->zone_zsched->p_pid);
+
+ if (pid == zone->zone_proc_initpid) {
+ pid = 1;
+ ppid = 0; /* parent pid for init is 0 */
+ } else {
+ if (ppid == zone->zone_proc_initpid) {
+ /*
+ * Convert ppid to the Linux default of 1 if our parent
+ * is the zone's init process
+ */
+ ppid = 1;
+ } else if (ppid == zone->zone_zsched->p_pid ||
+ (p->p_flag & SZONETOP) != 0) {
+ /*
+ * Additionally, if the process has no valid parent
+ * inside the zone (or its parent is zsched), lie and
+ * claim init as the parent.
+ */
+ ppid = 1;
+ }
+ }
+
+ *pidp = pid;
+ if (ppidp != NULL) {
+ *ppidp = ppid;
+ }
+}
+
+/*
+ * lxpr_unlock()
+ *
+ * Unlock locked process
+ */
+void
+lxpr_unlock(proc_t *p)
+{
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(!MUTEX_HELD(&pidlock));
+
+ cv_signal(&pr_pid_cv[p->p_slot]);
+ p->p_proc_flag &= ~P_PR_LOCK;
+ mutex_exit(&p->p_lock);
+}
+
+file_t *
+lxpr_getf(proc_t *p, uint_t fd, short *flag)
+{
+ uf_entry_t *ufp;
+ uf_info_t *fip;
+ file_t *fp;
+
+ ASSERT(MUTEX_HELD(&p->p_lock) && (p->p_proc_flag & P_PR_LOCK));
+
+ fip = P_FINFO(p);
+
+ if (fd >= fip->fi_nfiles)
+ return (NULL);
+
+ /*
+ * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+ * going away while we dereference into fi_list.
+ */
+ mutex_exit(&p->p_lock);
+ mutex_enter(&fip->fi_lock);
+ UF_ENTER(ufp, fip, fd);
+ if ((fp = ufp->uf_file) != NULL && fp->f_count > 0) {
+ if (flag != NULL)
+ *flag = ufp->uf_flag;
+ ufp->uf_refcnt++;
+ } else {
+ fp = NULL;
+ }
+ UF_EXIT(ufp);
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&p->p_lock);
+
+ return (fp);
+}
+
+void
+lxpr_releasef(proc_t *p, uint_t fd)
+{
+ uf_entry_t *ufp;
+ uf_info_t *fip;
+
+ ASSERT(MUTEX_HELD(&p->p_lock) && (p->p_proc_flag & P_PR_LOCK));
+
+ fip = P_FINFO(p);
+
+ mutex_exit(&p->p_lock);
+ mutex_enter(&fip->fi_lock);
+ UF_ENTER(ufp, fip, fd);
+ ASSERT3U(ufp->uf_refcnt, >, 0);
+ ufp->uf_refcnt--;
+ UF_EXIT(ufp);
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&p->p_lock);
+}
+
+
+void
+lxpr_initnodecache()
+{
+ lxpr_node_cache = kmem_cache_create(LXPRCACHE_NAME,
+ sizeof (lxpr_node_t), 0,
+ lxpr_node_constructor, lxpr_node_destructor, NULL, NULL, NULL, 0);
+}
+
+void
+lxpr_fininodecache()
+{
+ kmem_cache_destroy(lxpr_node_cache);
+}
+
+static int
+lxpr_node_constructor(void *buf, void *un, int kmflags)
+{
+ lxpr_node_t *lxpnp = buf;
+ vnode_t *vp;
+
+ vp = lxpnp->lxpr_vnode = vn_alloc(kmflags);
+ if (vp == NULL)
+ return (-1);
+
+ (void) vn_setops(vp, lxpr_vnodeops);
+ vp->v_data = lxpnp;
+
+ return (0);
+}
+
+static void
+lxpr_node_destructor(void *buf, void *un)
+{
+ lxpr_node_t *lxpnp = buf;
+
+ vn_free(LXPTOV(lxpnp));
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them
+ * to give the inode number for an lxproc node
+ */
+ino_t
+lxpr_inode(lxpr_nodetype_t type, pid_t pid, int desc)
+{
+ switch (type) {
+ case LXPR_PIDDIR:
+ return (maxpid + pid + 1);
+ case LXPR_PID_TASK_IDDIR:
+ return (maxpid + (desc * 10));
+ case LXPR_PROCDIR:
+ return (maxpid + 2);
+ case LXPR_PID_FD_FD:
+ return (maxpid + 2 +
+ (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) +
+ LXPR_NFILES + desc);
+ default:
+ return (maxpid + 2 +
+ (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) +
+ type);
+ }
+}
+
+/*
+ * Return inode number of parent (directory)
+ */
+ino_t
+lxpr_parentinode(lxpr_node_t *lxpnp)
+{
+ /*
+ * If the input node is the root then the parent inode
+ * is the mounted on inode so just return our inode number
+ */
+ if (lxpnp->lxpr_type != LXPR_PROCDIR)
+ return (VTOLXP(lxpnp->lxpr_parent)->lxpr_ino);
+ else
+ return (lxpnp->lxpr_ino);
+}
+
+/*
+ * Allocate a new lxproc node
+ *
+ * This also allocates the vnode associated with it
+ */
+lxpr_node_t *
+lxpr_getnode(vnode_t *dp, lxpr_nodetype_t type, proc_t *p, int desc)
+{
+ lxpr_node_t *lxpnp;
+ vnode_t *vp;
+ user_t *up;
+ timestruc_t now;
+
+ /*
+ * Allocate a new node. It is deallocated in vop_inactive
+ */
+ lxpnp = kmem_cache_alloc(lxpr_node_cache, KM_SLEEP);
+
+ /*
+ * Set defaults (may be overridden below)
+ */
+ gethrestime(&now);
+ lxpnp->lxpr_type = type;
+ lxpnp->lxpr_realvp = NULL;
+ lxpnp->lxpr_parent = dp;
+ lxpnp->lxpr_desc = desc;
+ VN_HOLD(dp);
+ if (p != NULL) {
+ lxpr_node_t *dlxpnp = VTOLXP(dp);
+
+ lxpnp->lxpr_pid = p->p_pid;
+ /* Propagate the tid whenever possible. */
+ if (desc == 0 && dlxpnp->lxpr_desc != 0) {
+ lxpnp->lxpr_desc = dlxpnp->lxpr_desc;
+ }
+ lxpnp->lxpr_time = PTOU(p)->u_start;
+ lxpnp->lxpr_uid = crgetruid(p->p_cred);
+ lxpnp->lxpr_gid = crgetrgid(p->p_cred);
+ lxpnp->lxpr_ino = lxpr_inode(type, p->p_pid, desc);
+ } else {
+ /* Pretend files without a proc belong to sched */
+ lxpnp->lxpr_pid = 0;
+ lxpnp->lxpr_time = now;
+ lxpnp->lxpr_uid = lxpnp->lxpr_gid = 0;
+ lxpnp->lxpr_ino = lxpr_inode(type, 0, 0);
+ }
+
+ /* initialize the vnode data */
+ vp = lxpnp->lxpr_vnode;
+ vn_reinit(vp);
+ vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
+ vp->v_vfsp = dp->v_vfsp;
+
+ /*
+ * Do node specific stuff
+ */
+ if (lxpr_is_writable(type)) {
+ /* These two have different modes; handled later. */
+ if (type != LXPR_PID_FD_FD && type != LXPR_PID_TID_FD_FD) {
+ vp->v_type = VREG;
+ lxpnp->lxpr_mode = 0644;
+ return (lxpnp);
+ }
+ }
+
+ switch (type) {
+ case LXPR_PROCDIR:
+ vp->v_flag |= VROOT;
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by everyone */
+ break;
+
+ case LXPR_PID_CURDIR:
+ ASSERT(p != NULL);
+
+ /*
+ * Zombie check. p_stat is officially protected by pidlock,
+ * but we can't grab pidlock here because we already hold
+ * p_lock. Luckily if we look at the process exit code
+ * we see that p_stat only transisions from SRUN to SZOMB
+ * while p_lock is held. Aside from this, the only other
+ * p_stat transition that we need to be aware about is
+ * SIDL to SRUN, but that's not a problem since lxpr_lock()
+ * ignores nodes in the SIDL state so we'll never get a node
+ * that isn't already in the SRUN state.
+ */
+ if (p->p_stat == SZOMB || (p->p_flag & SEXITING) != 0) {
+ lxpnp->lxpr_realvp = NULL;
+ } else {
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ up = PTOU(p);
+ lxpnp->lxpr_realvp = up->u_cdir;
+ ASSERT(lxpnp->lxpr_realvp != NULL);
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_ROOTDIR:
+ ASSERT(p != NULL);
+ /* Zombie check. see locking comment above */
+ if (p->p_stat == SZOMB || (p->p_flag & SEXITING) != 0) {
+ lxpnp->lxpr_realvp = NULL;
+ } else {
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ up = PTOU(p);
+ lxpnp->lxpr_realvp =
+ up->u_rdir != NULL ? up->u_rdir : rootdir;
+ ASSERT(lxpnp->lxpr_realvp != NULL);
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_EXE:
+ ASSERT(p != NULL);
+ lxpnp->lxpr_realvp = p->p_exec;
+ if (lxpnp->lxpr_realvp != NULL) {
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777;
+ break;
+
+ case LXPR_SELF:
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_TASKDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by everyone */
+ break;
+
+ case LXPR_PID_TASK_IDDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by everyone */
+ break;
+
+ case LXPR_PID_FD_FD:
+ case LXPR_PID_TID_FD_FD:
+ ASSERT(p != NULL);
+ /* lxpr_realvp is set after we return */
+ lxpnp->lxpr_mode = 0700; /* read-write-exe owner only */
+ vp->v_type = VLNK;
+ break;
+
+ case LXPR_PID_FDINFO_FD:
+ case LXPR_PID_TID_FDINFO_FD:
+ ASSERT(p != NULL);
+ lxpnp->lxpr_mode = 0400; /* read by owner only */
+ vp->v_type = VREG;
+ break;
+
+ case LXPR_PID_FDDIR:
+ case LXPR_PID_TID_FDDIR:
+ case LXPR_PID_FDINFODIR:
+ case LXPR_PID_TID_FDINFODIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0500; /* read-search by owner only */
+ break;
+
+ case LXPR_PIDDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0511;
+ break;
+
+ case LXPR_NETDIR:
+ case LXPR_SYSDIR:
+ case LXPR_SYS_FSDIR:
+ case LXPR_SYS_FS_INOTIFYDIR:
+ case LXPR_SYS_KERNELDIR:
+ case LXPR_SYS_KERNEL_RANDDIR:
+ case LXPR_SYS_NETDIR:
+ case LXPR_SYS_NET_COREDIR:
+ case LXPR_SYS_NET_IPV4DIR:
+ case LXPR_SYS_VMDIR:
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by all */
+ break;
+
+ case LXPR_PID_AUXV:
+ case LXPR_PID_PERSONALITY:
+ case LXPR_PID_ENV:
+ case LXPR_PID_MEM:
+ ASSERT(p != NULL);
+ /*FALLTHRU*/
+ case LXPR_KCORE:
+ vp->v_type = VREG;
+ lxpnp->lxpr_mode = 0400; /* read-only by owner only */
+ break;
+
+ default:
+ vp->v_type = VREG;
+ lxpnp->lxpr_mode = 0444; /* read-only by all */
+ break;
+ }
+
+ return (lxpnp);
+}
+
+
+/*
+ * Free the storage obtained from lxpr_getnode().
+ */
+void
+lxpr_freenode(lxpr_node_t *lxpnp)
+{
+ ASSERT(lxpnp != NULL);
+ ASSERT(LXPTOV(lxpnp) != NULL);
+
+ /*
+ * delete any association with realvp
+ */
+ if (lxpnp->lxpr_realvp != NULL)
+ VN_RELE(lxpnp->lxpr_realvp);
+
+ /*
+ * delete any association with parent vp
+ */
+ if (lxpnp->lxpr_parent != NULL)
+ VN_RELE(lxpnp->lxpr_parent);
+
+ /*
+ * Release the lxprnode.
+ */
+ kmem_cache_free(lxpr_node_cache, lxpnp);
+}
+
+static int
+lxpr_parse_fdnode_num(const char *name)
+{
+ char *endptr = NULL;
+ long num;
+ int fd;
+
+ if (ddi_strtol(name, &endptr, 10, &num) != 0) {
+ return (-1);
+ } else if (name[0] < '0' || name[0] > '9' || *endptr != '\0') {
+ /*
+ * ddi_strtol allows leading spaces and trailing garbage
+ * We do not tolerate such foolishness.
+ */
+ return (-1);
+ } else if ((fd = (int)num) < 0) {
+ return (-1);
+ }
+ return (fd);
+}
+
+/*
+ * Attempt to locate vnode for /proc/<pid>/fd/<#>.
+ */
+vnode_t *
+lxpr_lookup_fdnode(vnode_t *dvp, const char *name)
+{
+ lxpr_node_t *lxdp = VTOLXP(dvp);
+ lxpr_node_t *lxfp;
+ int fd;
+ proc_t *p;
+ vnode_t *vp = NULL;
+ file_t *fp;
+
+ ASSERT(lxdp->lxpr_type == LXPR_PID_FDDIR ||
+ lxdp->lxpr_type == LXPR_PID_TID_FDDIR);
+
+ if ((fd = lxpr_parse_fdnode_num(name)) == -1)
+ return (NULL);
+
+ /* Lock the owner process */
+ if ((p = lxpr_lock(lxdp, NO_ZOMB)) == NULL)
+ return (NULL);
+
+ /* Not applicable to processes which are system-owned. */
+ if (p->p_as == &kas) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ lxfp = lxpr_getnode(dvp, LXPR_PID_FD_FD, p, fd);
+
+ if ((fp = lxpr_getf(p, fd, NULL)) != NULL) {
+ vp = fp->f_vnode;
+ VN_HOLD(vp);
+ lxpr_releasef(p, fd);
+ }
+
+ if (vp == NULL) {
+ lxpr_unlock(p);
+ lxpr_freenode(lxfp);
+ return (NULL);
+ }
+
+ /*
+ * Fill in the lxpr_node so future references will be able to
+ * find the underlying vnode. The vnode is held on the realvp.
+ */
+ lxfp->lxpr_realvp = vp;
+ vp = LXPTOV(lxfp);
+
+ /*
+ * For certain entries (sockets, pipes, etc), Linux expects a
+ * bogus-named symlink. If that's the case, report the type as
+ * VNON to bypass link-following elsewhere in the vfs system.
+ *
+ * See lxpr_readlink for more details.
+ */
+ if (lxpr_readlink_fdnode(lxfp, NULL, 0) == 0)
+ vp->v_type = VNON;
+
+ lxpr_unlock(p);
+ ASSERT(vp != NULL);
+ return (vp);
+}
+
+/*
+ * Attempt to create Linux-proc-style fake symlinks contents for supported
+ * /proc/<pid>/fd/<#> entries.
+ */
+int
+lxpr_readlink_fdnode(lxpr_node_t *lxpnp, char *bp, size_t len)
+{
+ const char *format;
+ vnode_t *rvp = lxpnp->lxpr_realvp;
+ vattr_t attr;
+
+ switch (rvp->v_type) {
+ case VSOCK:
+ format = "socket:[%lu]";
+ break;
+ case VFIFO:
+ format = "pipe:[%lu]";
+ break;
+ default:
+ return (-1);
+ }
+
+ /* Fetch the inode of the underlying vnode */
+ if (VOP_GETATTR(rvp, &attr, 0, CRED(), NULL) != 0)
+ return (-1);
+
+ if (bp != NULL)
+ (void) snprintf(bp, len, format, (ino_t)attr.va_nodeid);
+ return (0);
+}
+
+/*
+ * Attempt to locate vnode for /proc/<pid>/fdinfo/<#>.
+ */
+vnode_t *
+lxpr_lookup_fdinfonode(vnode_t *dvp, const char *name)
+{
+ lxpr_node_t *lxdp = VTOLXP(dvp);
+ lxpr_node_t *lxfp;
+ proc_t *p;
+ int fd;
+
+ ASSERT(lxdp->lxpr_type == LXPR_PID_FDINFODIR);
+
+ if ((fd = lxpr_parse_fdnode_num(name)) == -1)
+ return (NULL);
+
+ /* Lock the owner process */
+ if ((p = lxpr_lock(lxdp, NO_ZOMB)) == NULL)
+ return (NULL);
+
+ /* Not applicable to processes which are system-owned. */
+ if (p->p_as == &kas) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ lxfp = lxpr_getnode(dvp, LXPR_PID_FDINFO_FD, p, fd);
+
+ lxpr_unlock(p);
+ ASSERT(LXPTOV(lxfp) != NULL);
+ return (LXPTOV(lxfp));
+}
+
+/*
+ * Translate native file flags to Linux open flags.
+ */
+int
+lxpr_open_flags_convert(offset_t uf_flag, uint32_t f_flag)
+{
+ int flags = 0;
+
+ switch (f_flag & (FREAD | FWRITE)) {
+ case FREAD:
+ flags = LX_O_RDONLY;
+ break;
+ case FWRITE:
+ flags = LX_O_WRONLY;
+ break;
+ case FREAD | FWRITE:
+ flags = LX_O_RDWR;
+ break;
+ }
+
+ if (f_flag & FNDELAY)
+ flags |= LX_O_NDELAY;
+ if (f_flag & FAPPEND)
+ flags |= LX_O_APPEND;
+ if (f_flag & FSYNC)
+ flags |= LX_O_SYNC;
+ if (f_flag & FNONBLOCK)
+ flags |= LX_O_NONBLOCK;
+
+ if (f_flag & FCREAT)
+ flags |= LX_O_CREAT;
+ if (f_flag & FTRUNC)
+ flags |= LX_O_TRUNC;
+ if (f_flag & FEXCL)
+ flags |= LX_O_EXCL;
+ if (f_flag & FASYNC)
+ flags |= LX_O_ASYNC;
+ if (f_flag & FOFFMAX)
+ flags |= LX_O_LARGEFILE;
+ if (f_flag & FNOCTTY)
+ flags |= LX_O_NOCTTY;
+ if (f_flag & FNOFOLLOW)
+ flags |= LX_O_NOFOLLOW;
+
+ if (f_flag & FDIRECT)
+ flags |= LX_O_DIRECT;
+ if (f_flag & __FLXPATH)
+ flags |= LX_O_PATH;
+
+ if (uf_flag & FD_CLOEXEC)
+ flags |= LX_O_CLOEXEC;
+
+ return (flags);
+}
+
+/*
+ * Translate a Linux core_pattern path to a native illumos one, by replacing
+ * the appropriate % escape sequences.
+ *
+ * Any % escape sequences that are not recognised are double-escaped so that
+ * they will be inserted literally into the path (to mimic Linux).
+ */
+int
+lxpr_core_path_l2s(const char *inp, char *outp, size_t outsz)
+{
+ int i = 0, j = 0;
+ char x;
+
+ while (j < outsz - 1) {
+ x = inp[i++];
+ if (x == '\0')
+ break;
+ if (x != '%') {
+ outp[j++] = x;
+ continue;
+ }
+
+ x = inp[i++];
+ if (x == '\0')
+ break;
+
+ /* Make sure we have enough space in the output buffer. */
+ if (j + 2 >= outsz - 1)
+ return (EINVAL);
+
+ switch (x) {
+ case 'E':
+ if (j + 4 >= outsz - 1)
+ return (EINVAL);
+ outp[j++] = '%';
+ outp[j++] = 'd';
+ outp[j++] = '%';
+ outp[j++] = 'f';
+ break;
+ case 'e':
+ outp[j++] = '%';
+ outp[j++] = 'f';
+ break;
+ case 'p':
+ case 'g':
+ case 'u':
+ case 't':
+ case '%':
+ outp[j++] = '%';
+ outp[j++] = x;
+ break;
+ case 'h':
+ outp[j++] = '%';
+ outp[j++] = 'n';
+ break;
+ default:
+ /* No translation, make it literal. */
+ if (j + 3 >= outsz - 1)
+ return (EINVAL);
+ outp[j++] = '%';
+ outp[j++] = '%';
+ outp[j++] = x;
+ break;
+ }
+ }
+
+ outp[j] = '\0';
+ return (0);
+}
+
+/*
+ * Translate an illumos core pattern path back to Linux format.
+ */
+int
+lxpr_core_path_s2l(const char *inp, char *outp, size_t outsz)
+{
+ int i = 0, j = 0;
+ char x;
+
+ while (j < outsz - 1) {
+ x = inp[i++];
+ if (x == '\0')
+ break;
+ if (x != '%') {
+ outp[j++] = x;
+ continue;
+ }
+
+ x = inp[i++];
+ if (x == '\0')
+ break;
+
+ /* Make sure we have enough space in the output buffer. */
+ if (j + 2 >= outsz - 1)
+ return (EINVAL);
+
+ switch (x) {
+ case 'd':
+ /* No Linux equivalent unless it's %d%f. */
+ if (inp[i] == '%' && inp[i + 1] == 'f') {
+ i += 2;
+ outp[j++] = '%';
+ outp[j++] = 'E';
+ }
+ break;
+ case 'f':
+ outp[j++] = '%';
+ outp[j++] = 'e';
+ break;
+ case 'p':
+ case 'P':
+ case 'g':
+ case 'u':
+ case 't':
+ case '%':
+ outp[j++] = '%';
+ outp[j++] = (x == 'P' ? 'p' : x);
+ break;
+ case 'n':
+ outp[j++] = '%';
+ outp[j++] = 'h';
+ break;
+ default:
+ /* No translation. */
+ break;
+ }
+ }
+
+ outp[j] = '\0';
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prvfsops.c b/usr/src/uts/common/brand/lx/procfs/lx_prvfsops.c
new file mode 100644
index 0000000000..b4dc5091c2
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/procfs/lx_prvfsops.c
@@ -0,0 +1,377 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * lxprvfsops.c: vfs operations for /lxprocfs.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/mode.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#include <sys/mount.h>
+#include <sys/bitmap.h>
+#include <sys/kmem.h>
+#include <sys/policy.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+
+#include "lx_proc.h"
+
+/* Module level parameters */
+static int lxprocfstype;
+static dev_t lxprocdev;
+static kmutex_t lxpr_mount_lock;
+
+int nproc_highbit; /* highbit(v.v_nproc) */
+
+static int lxpr_mount(vfs_t *, vnode_t *, mounta_t *, cred_t *);
+static int lxpr_unmount(vfs_t *, int, cred_t *);
+static int lxpr_root(vfs_t *, vnode_t **);
+static int lxpr_statvfs(vfs_t *, statvfs64_t *);
+static int lxpr_init(int, char *);
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "lx_proc",
+ lxpr_init,
+ VSW_ZMOUNT,
+ NULL
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+ &mod_fsops, "lx brand procfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlfs, NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int retval;
+
+ /*
+ * attempt to unload the module
+ */
+ if ((retval = mod_remove(&modlinkage)) != 0)
+ goto done;
+
+ /*
+ * destroy lxpr_node cache
+ */
+ lxpr_fininodecache();
+
+ /*
+ * clean out the vfsops and vnodeops
+ */
+ (void) vfs_freevfsops_by_type(lxprocfstype);
+ vn_freevnodeops(lxpr_vnodeops);
+
+ mutex_destroy(&lxpr_mount_lock);
+done:
+ return (retval);
+}
+
+static int
+lxpr_init(int fstype, char *name)
+{
+ static const fs_operation_def_t lxpr_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = lxpr_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = lxpr_unmount },
+ VFSNAME_ROOT, { .vfs_root = lxpr_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = lxpr_statvfs },
+ NULL, NULL
+ };
+ extern const fs_operation_def_t lxpr_vnodeops_template[];
+ int error;
+ major_t dev;
+
+ nproc_highbit = highbit(v.v_proc);
+ lxprocfstype = fstype;
+ ASSERT(lxprocfstype != 0);
+
+ mutex_init(&lxpr_mount_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /*
+ * Associate VFS ops vector with this fstype.
+ */
+ error = vfs_setfsops(fstype, lxpr_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "lxpr_init: bad vfs ops template");
+ return (error);
+ }
+
+ /*
+ * Set up vnode ops vector too.
+ */
+ error = vn_make_ops(name, lxpr_vnodeops_template, &lxpr_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "lxpr_init: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * Assign a unique "device" number (used by stat(2)).
+ */
+ if ((dev = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "lxpr_init: can't get unique device number");
+ dev = 0;
+ }
+
+ /*
+ * Make the pseudo device
+ */
+ lxprocdev = makedevice(dev, 0);
+
+ /*
+ * Initialise cache for lxpr_nodes
+ */
+ lxpr_initnodecache();
+
+ return (0);
+}
+
+static int
+lxpr_mount(vfs_t *vfsp, vnode_t *mvp, mounta_t *uap, cred_t *cr)
+{
+ lxpr_mnt_t *lxpr_mnt;
+ zone_t *zone = curproc->p_zone;
+ ldi_ident_t li;
+ int err;
+
+ /*
+ * must be root to mount
+ */
+ if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+ return (EPERM);
+
+ /*
+ * mount point must be a directory
+ */
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /*
+ * Mounting lx_proc is not allowed outside an LX zone.
+ */
+ if (zone->zone_brand != &lx_brand) {
+ return (ENOTSUP);
+ }
+
+ /*
+ * Having the resource be anything but "lxproc" doesn't make sense
+ */
+ vfs_setresource(vfsp, "lxproc", 0);
+
+ lxpr_mnt = kmem_alloc(sizeof (*lxpr_mnt), KM_SLEEP);
+
+ if ((err = ldi_ident_from_mod(&modlinkage, &li)) != 0) {
+ kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
+ return (err);
+ }
+ lxpr_mnt->lxprm_li = li;
+
+ mutex_enter(&lxpr_mount_lock);
+
+ /*
+ * Ensure we don't allow overlaying mounts
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ mutex_exit(&lxpr_mount_lock);
+ kmem_free(lxpr_mnt, sizeof ((*lxpr_mnt)));
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /*
+ * Hold a zone reference for access to the lxzd structure.
+ */
+ zone_hold(lxpr_mnt->lxprm_zone = zone);
+
+ /*
+ * Allocate the first vnode and arbitrarily set the parent vnode to the
+ * mounted over directory
+ */
+ lxpr_mnt->lxprm_node = lxpr_getnode(mvp, LXPR_PROCDIR, NULL, 0);
+
+ /* Correctly set the fs for the root node */
+ lxpr_mnt->lxprm_node->lxpr_vnode->v_vfsp = vfsp;
+
+ vfs_make_fsid(&vfsp->vfs_fsid, lxprocdev, lxprocfstype);
+ vfsp->vfs_bsize = DEV_BSIZE;
+ vfsp->vfs_fstype = lxprocfstype;
+ vfsp->vfs_data = (caddr_t)lxpr_mnt;
+ vfsp->vfs_dev = lxprocdev;
+
+ mutex_exit(&lxpr_mount_lock);
+
+ return (0);
+}
+
+static int
+lxpr_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+ lxpr_mnt_t *lxpr_mnt = (lxpr_mnt_t *)vfsp->vfs_data;
+ vnode_t *vp;
+ int count;
+
+ ASSERT(lxpr_mnt != NULL);
+ vp = LXPTOV(lxpr_mnt->lxprm_node);
+
+ mutex_enter(&lxpr_mount_lock);
+
+ /*
+ * must be root to unmount
+ */
+ if (secpolicy_fs_unmount(cr, vfsp) != 0) {
+ mutex_exit(&lxpr_mount_lock);
+ return (EPERM);
+ }
+
+ /*
+ * forced unmount is not supported by this file system
+ */
+ if (flag & MS_FORCE) {
+ mutex_exit(&lxpr_mount_lock);
+ return (ENOTSUP);
+ }
+
+ /*
+ * Ensure that no vnodes are in use on this mount point.
+ */
+ mutex_enter(&vp->v_lock);
+ count = vp->v_count;
+ mutex_exit(&vp->v_lock);
+ if (count > 1) {
+ mutex_exit(&lxpr_mount_lock);
+ return (EBUSY);
+ }
+
+
+ /*
+ * purge the dnlc cache for vnode entries
+ * associated with this file system
+ */
+ count = dnlc_purge_vfsp(vfsp, 0);
+
+ /*
+ * free up the lxprnode
+ */
+ lxpr_freenode(lxpr_mnt->lxprm_node);
+ zone_rele(lxpr_mnt->lxprm_zone);
+
+ ldi_ident_release(lxpr_mnt->lxprm_li);
+
+ kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
+
+ mutex_exit(&lxpr_mount_lock);
+
+ return (0);
+}
+
+static int
+lxpr_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ lxpr_node_t *lxpnp = ((lxpr_mnt_t *)vfsp->vfs_data)->lxprm_node;
+ vnode_t *vp = LXPTOV(lxpnp);
+
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+lxpr_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+ int n;
+ dev32_t d32;
+ extern uint_t nproc;
+
+ n = v.v_proc - nproc;
+
+ bzero((caddr_t)sp, sizeof (*sp));
+ sp->f_bsize = DEV_BSIZE;
+ sp->f_frsize = DEV_BSIZE;
+ sp->f_blocks = (fsblkcnt64_t)0;
+ sp->f_bfree = (fsblkcnt64_t)0;
+ sp->f_bavail = (fsblkcnt64_t)0;
+ sp->f_files = (fsfilcnt64_t)v.v_proc + 2;
+ sp->f_ffree = (fsfilcnt64_t)n;
+ sp->f_favail = (fsfilcnt64_t)n;
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sp->f_fsid = d32;
+ /* It is guaranteed that vsw_name will fit in f_basetype */
+ (void) strcpy(sp->f_basetype, vfssw[lxprocfstype].vsw_name);
+ sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sp->f_namemax = 64; /* quite arbitrary */
+ bzero(sp->f_fstr, sizeof (sp->f_fstr));
+
+ /* We know f_fstr is 32 chars */
+ (void) strcpy(sp->f_fstr, "/proc");
+ (void) strcpy(&sp->f_fstr[6], "/proc");
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c
new file mode 100644
index 0000000000..6d4d2357eb
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c
@@ -0,0 +1,8553 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+/*
+ * lx_proc -- a Linux-compatible /proc for the LX brand
+ */
+
+#include <sys/cpupart.h>
+#include <sys/cpuvar.h>
+#include <sys/queue.h>
+#include <sys/session.h>
+#include <sys/vmparam.h>
+#include <sys/mman.h>
+#include <vm/rm.h>
+#include <vm/seg_vn.h>
+#include <sys/sdt.h>
+#include <lx_signum.h>
+#include <sys/strlog.h>
+#include <sys/stropts.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_brand.h>
+#include <lx_auxv.h>
+#include <sys/x86_archext.h>
+#include <sys/archsystm.h>
+#include <sys/fp.h>
+#include <sys/pool_pset.h>
+#include <sys/pset.h>
+#include <sys/zone.h>
+#include <sys/fcntl.h>
+#include <sys/pghw.h>
+#include <sys/vfs_opreg.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <sys/rctl.h>
+#include <sys/kstat.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_types.h>
+#include <sys/lx_userhz.h>
+#include <sys/brand.h>
+#include <sys/cred_impl.h>
+#include <sys/tihdr.h>
+#include <sys/corectl.h>
+#include <sys/rctl_impl.h>
+#include <inet/cc.h>
+#include <inet/ip.h>
+#include <inet/ip_ire.h>
+#include <inet/ip6.h>
+#include <inet/ip_if.h>
+#include <inet/tcp.h>
+#include <inet/tcp_impl.h>
+#include <inet/udp_impl.h>
+#include <inet/ipclassifier.h>
+#include <sys/socketvar.h>
+#include <fs/sockfs/socktpi.h>
+#include <sys/random.h>
+#include <sys/procfs.h>
+
+/* Dependent on procfs */
+extern kthread_t *prchoose(proc_t *);
+extern int prreadcmdline(proc_t *, char *, size_t, size_t *);
+extern int prreadenvv(proc_t *, char *, size_t, size_t *);
+extern int prreadbuf(proc_t *, uintptr_t, uint8_t *, size_t, size_t *);
+
+#include "lx_proc.h"
+
+extern pgcnt_t swapfs_minfree;
+
+/*
+ * Pointer to the vnode ops vector for this fs.
+ * This is instantiated in lxprinit() in lxpr_vfsops.c
+ */
+vnodeops_t *lxpr_vnodeops;
+
+static int lxpr_open(vnode_t **, int, cred_t *, caller_context_t *);
+static int lxpr_close(vnode_t *, int, int, offset_t, cred_t *,
+ caller_context_t *);
+static int lxpr_create(struct vnode *, char *, struct vattr *, enum vcexcl,
+ int, struct vnode **, struct cred *, int, caller_context_t *, vsecattr_t *);
+static int lxpr_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxpr_write(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxpr_space(vnode_t *, int, flock64_t *, int, offset_t, cred_t *,
+ caller_context_t *);
+static int lxpr_setattr(vnode_t *, vattr_t *, int, cred_t *,
+ caller_context_t *);
+static int lxpr_getattr(vnode_t *, vattr_t *, int, cred_t *,
+ caller_context_t *);
+static int lxpr_access(vnode_t *, int, int, cred_t *, caller_context_t *);
+static int lxpr_lookup(vnode_t *, char *, vnode_t **,
+ pathname_t *, int, vnode_t *, cred_t *, caller_context_t *, int *,
+ pathname_t *);
+static int lxpr_readdir(vnode_t *, uio_t *, cred_t *, int *,
+ caller_context_t *, int);
+static int lxpr_readlink(vnode_t *, uio_t *, cred_t *, caller_context_t *);
+static int lxpr_cmp(vnode_t *, vnode_t *, caller_context_t *);
+static int lxpr_realvp(vnode_t *, vnode_t **, caller_context_t *);
+static int lxpr_poll(vnode_t *, short, int, short *, pollhead_t **,
+ caller_context_t *);
+static int lxpr_sync(void);
+static void lxpr_inactive(vnode_t *, cred_t *, caller_context_t *);
+
+static int lxpr_doaccess(lxpr_node_t *, boolean_t, int, int, cred_t *,
+ caller_context_t *);
+
+static vnode_t *lxpr_lookup_procdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_piddir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_not_a_dir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_fddir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_fdinfodir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_netdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sysdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_fsdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_fs_inotifydir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_kerneldir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_kdir_randdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_netdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_net_coredir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_net_ipv4dir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_vmdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_taskdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_task_tid_dir(vnode_t *, char *);
+
+static int lxpr_readdir_procdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_piddir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_not_a_dir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_fddir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_fdinfodir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_netdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sysdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_fsdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_fs_inotifydir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_kerneldir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_kdir_randdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_netdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_net_coredir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_net_ipv4dir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_vmdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_taskdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_task_tid_dir(lxpr_node_t *, uio_t *, int *);
+
+static void lxpr_read_invalid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_empty(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cgroups(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cmdline(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cpuinfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_devices(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_diskstats(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_isdir(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_fd(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_fdinfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_filesystems(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_kmsg(lxpr_node_t *, lxpr_uiobuf_t *, ldi_handle_t);
+static void lxpr_read_loadavg(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_meminfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_mounts(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_partitions(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_swaps(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_uptime(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_version(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_vmstat(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_pid_auxv(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_cgroup(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_cmdline(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_env(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_id_map(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_limits(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_loginuid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_maps(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_mountinfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_oom_scr_adj(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_personality(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_statm(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_pid_tid_comm(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_tid_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_tid_status(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_net_arp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_dev(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_dev_mcast(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_if_inet6(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_igmp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ip_mr_cache(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ip_mr_vif(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ipv6_route(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_mcfilter(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_netstat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_raw(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_route(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_rpc(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_rt_cache(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_sockstat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_snmp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_tcp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_tcp6(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_udp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_udp6(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_unix(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_aiomax(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_aionr(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_filemax(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_filenr(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_inotify_max_queued_events(lxpr_node_t *,
+ lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_inotify_max_user_instances(lxpr_node_t *,
+ lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_inotify_max_user_watches(lxpr_node_t *,
+ lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_pipe_max(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_caplcap(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_corepatt(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_hostname(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_msgmax(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_msgmnb(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_msgmni(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_ngroups_max(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_osrel(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_pid_max(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_rand_bootid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_rand_entavl(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_rand_uuid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_sem(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_shmall(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_shmmax(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_shmmni(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_threads_max(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_core_somaxc(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_icmp_eib(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_ip_forward(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_ip_lport_range(lxpr_node_t *,
+ lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_cc_allow(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_cc_avail(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_cc_curr(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_fin_to(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_ka_int(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_max_syn_bl(lxpr_node_t *,
+ lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_retry2(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_rwmem(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_sack(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_winscale(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_dirty(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_max_map_cnt(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_minfr_kb(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_nhpages(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_overcommit_mem(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_swappiness(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static int lxpr_write_pid_tid_comm(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+static int lxpr_write_pid_loginuid(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+static int lxpr_write_sys_fs_pipe_max(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+static int lxpr_write_sys_net_core_somaxc(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+static int lxpr_write_sys_net_ipv4_icmp_eib(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_ip_lport_range(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_cc_curr(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_fin_to(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_ka_int(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_max_syn_bl(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_retry2(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_rwmem(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_sack(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_winscale(lxpr_node_t *, uio_t *,
+ cred_t *, caller_context_t *);
+static int lxpr_write_sys_kernel_corepatt(lxpr_node_t *, uio_t *, cred_t *,
+ caller_context_t *);
+
+/*
+ * Simple conversion
+ */
+#define btok(x) ((x) >> 10) /* bytes to kbytes */
+#define ptok(x) ((x) << (PAGESHIFT - 10)) /* pages to kbytes */
+
+#define ttolxlwp(t) ((struct lx_lwp_data *)ttolwpbrand(t))
+
+extern rctl_hndl_t rc_process_semmsl;
+extern rctl_hndl_t rc_process_semopm;
+extern rctl_hndl_t rc_zone_semmni;
+extern rctl_hndl_t rc_process_msgmnb;
+
+extern rctl_hndl_t rc_zone_msgmni;
+extern rctl_hndl_t rc_zone_shmmax;
+extern rctl_hndl_t rc_zone_shmmni;
+
+/* From uts/common/crypto/io/swrand.c */
+extern swrand_stats_t swrand_stats;
+
+#define ONEGB 1073741824ULL
+#define FOURGB 4294967295ULL
+
+/*
+ * The maximum length of the concatenation of env vector strings we
+ * will return to the user via the branded procfs.
+ */
+int lxpr_maxenvvlen = 4096;
+
+/*
+ * The lx /proc vnode operations vector
+ */
+const fs_operation_def_t lxpr_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = lxpr_open },
+ VOPNAME_CLOSE, { .vop_close = lxpr_close },
+ VOPNAME_READ, { .vop_read = lxpr_read },
+ VOPNAME_WRITE, { .vop_read = lxpr_write },
+ VOPNAME_GETATTR, { .vop_getattr = lxpr_getattr },
+ VOPNAME_ACCESS, { .vop_access = lxpr_access },
+ VOPNAME_LOOKUP, { .vop_lookup = lxpr_lookup },
+ VOPNAME_CREATE, { .vop_create = lxpr_create },
+ VOPNAME_READDIR, { .vop_readdir = lxpr_readdir },
+ VOPNAME_READLINK, { .vop_readlink = lxpr_readlink },
+ VOPNAME_SPACE, { .vop_space = lxpr_space },
+ VOPNAME_SETATTR, { .vop_setattr = lxpr_setattr },
+ VOPNAME_FSYNC, { .error = lxpr_sync },
+ VOPNAME_SEEK, { .error = lxpr_sync },
+ VOPNAME_INACTIVE, { .vop_inactive = lxpr_inactive },
+ VOPNAME_CMP, { .vop_cmp = lxpr_cmp },
+ VOPNAME_REALVP, { .vop_realvp = lxpr_realvp },
+ VOPNAME_POLL, { .vop_poll = lxpr_poll },
+ NULL, NULL
+};
+
+
+/*
+ * file contents of an lx /proc directory.
+ */
+static lxpr_dirent_t lx_procdir[] = {
+ { LXPR_CGROUPS, "cgroups" },
+ { LXPR_CMDLINE, "cmdline" },
+ { LXPR_CPUINFO, "cpuinfo" },
+ { LXPR_DEVICES, "devices" },
+ { LXPR_DISKSTATS, "diskstats" },
+ { LXPR_DMA, "dma" },
+ { LXPR_FILESYSTEMS, "filesystems" },
+ { LXPR_INTERRUPTS, "interrupts" },
+ { LXPR_IOPORTS, "ioports" },
+ { LXPR_KCORE, "kcore" },
+ { LXPR_KMSG, "kmsg" },
+ { LXPR_LOADAVG, "loadavg" },
+ { LXPR_MEMINFO, "meminfo" },
+ { LXPR_MODULES, "modules" },
+ { LXPR_MOUNTS, "mounts" },
+ { LXPR_NETDIR, "net" },
+ { LXPR_PARTITIONS, "partitions" },
+ { LXPR_SELF, "self" },
+ { LXPR_STAT, "stat" },
+ { LXPR_SWAPS, "swaps" },
+ { LXPR_SYSDIR, "sys" },
+ { LXPR_UPTIME, "uptime" },
+ { LXPR_VERSION, "version" },
+ { LXPR_VMSTAT, "vmstat" }
+};
+
+#define PROCDIRFILES (sizeof (lx_procdir) / sizeof (lx_procdir[0]))
+
+/*
+ * Contents of an lx /proc/<pid> directory.
+ */
+static lxpr_dirent_t piddir[] = {
+ { LXPR_PID_AUXV, "auxv" },
+ { LXPR_PID_CGROUP, "cgroup" },
+ { LXPR_PID_CMDLINE, "cmdline" },
+ { LXPR_PID_COMM, "comm" },
+ { LXPR_PID_CPU, "cpu" },
+ { LXPR_PID_CURDIR, "cwd" },
+ { LXPR_PID_ENV, "environ" },
+ { LXPR_PID_EXE, "exe" },
+ { LXPR_PID_GIDMAP, "gid_map" },
+ { LXPR_PID_LIMITS, "limits" },
+ { LXPR_PID_LOGINUID, "loginuid" },
+ { LXPR_PID_MAPS, "maps" },
+ { LXPR_PID_MEM, "mem" },
+ { LXPR_PID_MOUNTINFO, "mountinfo" },
+ { LXPR_PID_MOUNTS, "mounts" },
+ { LXPR_PID_OOM_SCR_ADJ, "oom_score_adj" },
+ { LXPR_PID_PERSONALITY, "personality" },
+ { LXPR_PID_ROOTDIR, "root" },
+ { LXPR_PID_STAT, "stat" },
+ { LXPR_PID_STATM, "statm" },
+ { LXPR_PID_STATUS, "status" },
+ { LXPR_PID_TASKDIR, "task" },
+ { LXPR_PID_FDDIR, "fd" },
+ { LXPR_PID_FDINFODIR, "fdinfo" },
+ { LXPR_PID_UIDMAP, "uid_map" }
+};
+
+#define PIDDIRFILES (sizeof (piddir) / sizeof (piddir[0]))
+
+/*
+ * Contents of an lx /proc/<pid>/task/<tid> directory.
+ */
+static lxpr_dirent_t tiddir[] = {
+ { LXPR_PID_TID_AUXV, "auxv" },
+ { LXPR_PID_CGROUP, "cgroup" },
+ { LXPR_PID_CMDLINE, "cmdline" },
+ { LXPR_PID_TID_COMM, "comm" },
+ { LXPR_PID_CPU, "cpu" },
+ { LXPR_PID_CURDIR, "cwd" },
+ { LXPR_PID_ENV, "environ" },
+ { LXPR_PID_EXE, "exe" },
+ { LXPR_PID_GIDMAP, "gid_map" },
+ { LXPR_PID_LIMITS, "limits" },
+ { LXPR_PID_LOGINUID, "loginuid" },
+ { LXPR_PID_MAPS, "maps" },
+ { LXPR_PID_MEM, "mem" },
+ { LXPR_PID_MOUNTINFO, "mountinfo" },
+ { LXPR_PID_TID_OOM_SCR_ADJ, "oom_score_adj" },
+ { LXPR_PID_PERSONALITY, "personality" },
+ { LXPR_PID_ROOTDIR, "root" },
+ { LXPR_PID_TID_STAT, "stat" },
+ { LXPR_PID_STATM, "statm" },
+ { LXPR_PID_TID_STATUS, "status" },
+ { LXPR_PID_FDDIR, "fd" },
+ { LXPR_PID_FDINFODIR, "fdinfo" },
+ { LXPR_PID_UIDMAP, "uid_map" }
+};
+
+#define TIDDIRFILES (sizeof (tiddir) / sizeof (tiddir[0]))
+
+#define LX_RLIM_INFINITY 0xFFFFFFFFFFFFFFFF
+
+#define RCTL_INFINITE(x) \
+ ((x.rcv_flagaction & RCTL_LOCAL_MAXIMAL) && \
+ (x.rcv_flagaction & RCTL_GLOBAL_INFINITE))
+
+typedef struct lxpr_rlimtab {
+ char *rlim_name; /* limit name */
+ char *rlim_unit; /* limit unit */
+ char *rlim_rctl; /* rctl source */
+} lxpr_rlimtab_t;
+
+#define RLIM_MAXFD "Max open files"
+
+static lxpr_rlimtab_t lxpr_rlimtab[] = {
+ { "Max cpu time", "seconds", "process.max-cpu-time" },
+ { "Max file size", "bytes", "process.max-file-size" },
+ { "Max data size", "bytes", "process.max-data-size" },
+ { "Max stack size", "bytes", "process.max-stack-size" },
+ { "Max core file size", "bytes", "process.max-core-size" },
+ { "Max resident set", "bytes", "zone.max-physical-memory" },
+ { "Max processes", "processes", "zone.max-lwps" },
+ { RLIM_MAXFD, "files", "process.max-file-descriptor" },
+ { "Max locked memory", "bytes", "zone.max-locked-memory" },
+ { "Max address space", "bytes", "process.max-address-space" },
+ { "Max file locks", "locks", NULL },
+ { "Max pending signals", "signals",
+ "process.max-sigqueue-size" },
+ { "Max msgqueue size", "bytes", "process.max-msg-messages" }
+};
+
+#define LX_RLIM_TAB_LEN (sizeof (lxpr_rlimtab) / sizeof (lxpr_rlimtab[0]))
+
+
+/*
+ * contents of lx /proc/net directory
+ */
+static lxpr_dirent_t netdir[] = {
+ { LXPR_NET_ARP, "arp" },
+ { LXPR_NET_DEV, "dev" },
+ { LXPR_NET_DEV_MCAST, "dev_mcast" },
+ { LXPR_NET_IF_INET6, "if_inet6" },
+ { LXPR_NET_IGMP, "igmp" },
+ { LXPR_NET_IP_MR_CACHE, "ip_mr_cache" },
+ { LXPR_NET_IP_MR_VIF, "ip_mr_vif" },
+ { LXPR_NET_IPV6_ROUTE, "ipv6_route" },
+ { LXPR_NET_MCFILTER, "mcfilter" },
+ { LXPR_NET_NETSTAT, "netstat" },
+ { LXPR_NET_RAW, "raw" },
+ { LXPR_NET_ROUTE, "route" },
+ { LXPR_NET_RPC, "rpc" },
+ { LXPR_NET_RT_CACHE, "rt_cache" },
+ { LXPR_NET_SOCKSTAT, "sockstat" },
+ { LXPR_NET_SNMP, "snmp" },
+ { LXPR_NET_STAT, "stat" },
+ { LXPR_NET_TCP, "tcp" },
+ { LXPR_NET_TCP6, "tcp6" },
+ { LXPR_NET_UDP, "udp" },
+ { LXPR_NET_UDP6, "udp6" },
+ { LXPR_NET_UNIX, "unix" }
+};
+
+#define NETDIRFILES (sizeof (netdir) / sizeof (netdir[0]))
+
+/*
+ * contents of /proc/sys directory
+ */
+static lxpr_dirent_t sysdir[] = {
+ { LXPR_SYS_FSDIR, "fs" },
+ { LXPR_SYS_KERNELDIR, "kernel" },
+ { LXPR_SYS_NETDIR, "net" },
+ { LXPR_SYS_VMDIR, "vm" },
+};
+
+#define SYSDIRFILES (sizeof (sysdir) / sizeof (sysdir[0]))
+
+/*
+ * contents of /proc/sys/fs directory
+ */
+static lxpr_dirent_t sys_fsdir[] = {
+ { LXPR_SYS_FS_AIO_MAX_NR, "aio-max-nr" },
+ { LXPR_SYS_FS_AIO_NR, "aio-nr" },
+ { LXPR_SYS_FS_FILEMAX, "file-max" },
+ { LXPR_SYS_FS_FILENR, "file-nr" },
+ { LXPR_SYS_FS_INOTIFYDIR, "inotify" },
+ { LXPR_SYS_FS_PIPE_MAX, "pipe-max-size" },
+};
+
+#define SYS_FSDIRFILES (sizeof (sys_fsdir) / sizeof (sys_fsdir[0]))
+
+/*
+ * contents of /proc/sys/fs/inotify directory
+ */
+static lxpr_dirent_t sys_fs_inotifydir[] = {
+ { LXPR_SYS_FS_INOTIFY_MAX_QUEUED_EVENTS, "max_queued_events" },
+ { LXPR_SYS_FS_INOTIFY_MAX_USER_INSTANCES, "max_user_instances" },
+ { LXPR_SYS_FS_INOTIFY_MAX_USER_WATCHES, "max_user_watches" },
+};
+
+#define SYS_FS_INOTIFYDIRFILES \
+ (sizeof (sys_fs_inotifydir) / sizeof (sys_fs_inotifydir[0]))
+
+/*
+ * contents of /proc/sys/kernel directory
+ */
+static lxpr_dirent_t sys_kerneldir[] = {
+ { LXPR_SYS_KERNEL_CAPLCAP, "cap_last_cap" },
+ { LXPR_SYS_KERNEL_COREPATT, "core_pattern" },
+ { LXPR_SYS_KERNEL_HOSTNAME, "hostname" },
+ { LXPR_SYS_KERNEL_MSGMAX, "msgmax" },
+ { LXPR_SYS_KERNEL_MSGMNB, "msgmnb" },
+ { LXPR_SYS_KERNEL_MSGMNI, "msgmni" },
+ { LXPR_SYS_KERNEL_NGROUPS_MAX, "ngroups_max" },
+ { LXPR_SYS_KERNEL_OSREL, "osrelease" },
+ { LXPR_SYS_KERNEL_PID_MAX, "pid_max" },
+ { LXPR_SYS_KERNEL_RANDDIR, "random" },
+ { LXPR_SYS_KERNEL_SEM, "sem" },
+ { LXPR_SYS_KERNEL_SHMALL, "shmall" },
+ { LXPR_SYS_KERNEL_SHMMAX, "shmmax" },
+ { LXPR_SYS_KERNEL_SHMMNI, "shmmni" },
+ { LXPR_SYS_KERNEL_THREADS_MAX, "threads-max" },
+};
+
+#define SYS_KERNELDIRFILES (sizeof (sys_kerneldir) / sizeof (sys_kerneldir[0]))
+
+/*
+ * contents of /proc/sys/kernel/random directory
+ */
+static lxpr_dirent_t sys_randdir[] = {
+ { LXPR_SYS_KERNEL_RAND_BOOTID, "boot_id" },
+ { LXPR_SYS_KERNEL_RAND_ENTAVL, "entropy_avail" },
+ { LXPR_SYS_KERNEL_RAND_UUID, "uuid" },
+};
+
+#define SYS_RANDDIRFILES (sizeof (sys_randdir) / sizeof (sys_randdir[0]))
+
+/*
+ * contents of /proc/sys/net directory
+ */
+static lxpr_dirent_t sys_netdir[] = {
+ { LXPR_SYS_NET_COREDIR, "core" },
+ { LXPR_SYS_NET_IPV4DIR, "ipv4" },
+};
+
+#define SYS_NETDIRFILES (sizeof (sys_netdir) / sizeof (sys_netdir[0]))
+
+/*
+ * contents of /proc/sys/net/core directory
+ */
+static lxpr_dirent_t sys_net_coredir[] = {
+ { LXPR_SYS_NET_CORE_SOMAXCON, "somaxconn" },
+};
+
+#define SYS_NET_COREDIRFILES \
+ (sizeof (sys_net_coredir) / sizeof (sys_net_coredir[0]))
+
+/*
+ * contents of /proc/sys/net/ipv4 directory
+ * See the Linux ip(7) & tcp(7) man pages for descriptions and the illumos
+ * ip(7p) & tcp(7p) man pages for the native descriptions.
+ */
+static lxpr_dirent_t sys_net_ipv4dir[] = {
+ { LXPR_SYS_NET_IPV4_ICMP_EIB, "icmp_echo_ignore_broadcasts" },
+ { LXPR_SYS_NET_IPV4_IP_FORWARD, "ip_forward" },
+ { LXPR_SYS_NET_IPV4_IP_LPORT_RANGE, "ip_local_port_range" },
+ { LXPR_SYS_NET_IPV4_TCP_CC_ALLOW, "tcp_allowed_congestion_control" },
+ { LXPR_SYS_NET_IPV4_TCP_CC_AVAIL, "tcp_available_congestion_control" },
+ { LXPR_SYS_NET_IPV4_TCP_CC_CURR, "tcp_congestion_control" },
+ { LXPR_SYS_NET_IPV4_TCP_FIN_TO, "tcp_fin_timeout" },
+ { LXPR_SYS_NET_IPV4_TCP_KA_INT, "tcp_keepalive_intvl" },
+ { LXPR_SYS_NET_IPV4_TCP_KA_TIM, "tcp_keepalive_time" },
+ { LXPR_SYS_NET_IPV4_TCP_MAX_SYN_BL, "tcp_max_syn_backlog" },
+ { LXPR_SYS_NET_IPV4_TCP_RETRY2, "tcp_retries2" },
+ { LXPR_SYS_NET_IPV4_TCP_RMEM, "tcp_rmem" },
+ { LXPR_SYS_NET_IPV4_TCP_SACK, "tcp_sack" },
+ { LXPR_SYS_NET_IPV4_TCP_WINSCALE, "tcp_window_scaling" },
+ { LXPR_SYS_NET_IPV4_TCP_WMEM, "tcp_wmem" },
+};
+
+#define SYS_NET_IPV4DIRFILES \
+ (sizeof (sys_net_ipv4dir) / sizeof (sys_net_ipv4dir[0]))
+
+/*
+ * contents of /proc/sys/vm directory
+ */
+static lxpr_dirent_t sys_vmdir[] = {
+ { LXPR_SYS_VM_DIRTY_BG_BYTES, "dirty_background_bytes" },
+ { LXPR_SYS_VM_DIRTY_BG_RATIO, "dirty_background_ratio" },
+ { LXPR_SYS_VM_DIRTY_BYTES, "dirty_bytes" },
+ { LXPR_SYS_VM_DIRTY_EXP_CS, "dirty_expire_centisecs" },
+ { LXPR_SYS_VM_DIRTY_RATIO, "dirty_ratio" },
+ { LXPR_SYS_VM_DIRTYTIME_EXP_SEC, "dirtytime_expire_seconds" },
+ { LXPR_SYS_VM_DIRTY_WB_CS, "dirty_writeback_centisecs" },
+ { LXPR_SYS_VM_MAX_MAP_CNT, "max_map_count" },
+ { LXPR_SYS_VM_MINFR_KB, "min_free_kbytes" },
+ { LXPR_SYS_VM_NHUGEP, "nr_hugepages" },
+ { LXPR_SYS_VM_OVERCOMMIT_MEM, "overcommit_memory" },
+ { LXPR_SYS_VM_SWAPPINESS, "swappiness" },
+};
+
+#define SYS_VMDIRFILES (sizeof (sys_vmdir) / sizeof (sys_vmdir[0]))
+
+/*
+ * Table for standard writable files. Non-standard writable files not in this
+ * table can be handled explicitly as special cases.
+ * This table drives lxpr_is_writable, lxpr_write, and lxpr_create.
+ * Note that the entries LXPR_PID_FD_FD and LXPR_PID_TID_FD_FD exist in the
+ * table both to verify writability and to satisfy opening with O_CREATE.
+ */
+typedef struct wftab {
+ lxpr_nodetype_t wft_type; /* file entry type */
+ int (*wft_wrf)(lxpr_node_t *, struct uio *, cred_t *,
+ caller_context_t *); /* write function */
+} wftab_t;
+
+static wftab_t wr_tab[] = {
+ {LXPR_PID_COMM, lxpr_write_pid_tid_comm},
+ {LXPR_PID_FD_FD, NULL},
+ {LXPR_PID_LOGINUID, lxpr_write_pid_loginuid},
+ {LXPR_PID_OOM_SCR_ADJ, NULL},
+ {LXPR_PID_TID_COMM, lxpr_write_pid_tid_comm},
+ {LXPR_PID_TID_FD_FD, NULL},
+ {LXPR_PID_TID_OOM_SCR_ADJ, NULL},
+ {LXPR_SYS_FS_FILEMAX, NULL},
+ {LXPR_SYS_KERNEL_COREPATT, lxpr_write_sys_kernel_corepatt},
+ {LXPR_SYS_KERNEL_SHMALL, NULL},
+ {LXPR_SYS_KERNEL_SHMMAX, NULL},
+ {LXPR_SYS_FS_PIPE_MAX, lxpr_write_sys_fs_pipe_max},
+ {LXPR_SYS_NET_CORE_SOMAXCON, lxpr_write_sys_net_core_somaxc},
+ {LXPR_SYS_NET_IPV4_ICMP_EIB, lxpr_write_sys_net_ipv4_icmp_eib},
+ {LXPR_SYS_NET_IPV4_IP_FORWARD, NULL},
+ {LXPR_SYS_NET_IPV4_IP_LPORT_RANGE,
+ lxpr_write_sys_net_ipv4_ip_lport_range},
+ {LXPR_SYS_NET_IPV4_TCP_CC_ALLOW, NULL},
+ {LXPR_SYS_NET_IPV4_TCP_CC_AVAIL, NULL},
+ {LXPR_SYS_NET_IPV4_TCP_CC_CURR, lxpr_write_sys_net_ipv4_tcp_cc_curr},
+ {LXPR_SYS_NET_IPV4_TCP_FIN_TO, lxpr_write_sys_net_ipv4_tcp_fin_to},
+ {LXPR_SYS_NET_IPV4_TCP_KA_INT, lxpr_write_sys_net_ipv4_tcp_ka_int},
+ {LXPR_SYS_NET_IPV4_TCP_KA_TIM, lxpr_write_sys_net_ipv4_tcp_ka_tim},
+ {LXPR_SYS_NET_IPV4_TCP_MAX_SYN_BL,
+ lxpr_write_sys_net_ipv4_tcp_max_syn_bl},
+ {LXPR_SYS_NET_IPV4_TCP_RETRY2, lxpr_write_sys_net_ipv4_tcp_retry2},
+ {LXPR_SYS_NET_IPV4_TCP_RMEM, lxpr_write_sys_net_ipv4_tcp_rwmem},
+ {LXPR_SYS_NET_IPV4_TCP_SACK, lxpr_write_sys_net_ipv4_tcp_sack},
+ {LXPR_SYS_NET_IPV4_TCP_WINSCALE, lxpr_write_sys_net_ipv4_tcp_winscale},
+ {LXPR_SYS_NET_IPV4_TCP_WMEM, lxpr_write_sys_net_ipv4_tcp_rwmem},
+ {LXPR_SYS_VM_DIRTY_BG_BYTES, NULL},
+ {LXPR_SYS_VM_DIRTY_BG_RATIO, NULL},
+ {LXPR_SYS_VM_DIRTY_BYTES, NULL},
+ {LXPR_SYS_VM_DIRTY_EXP_CS, NULL},
+ {LXPR_SYS_VM_DIRTY_RATIO, NULL},
+ {LXPR_SYS_VM_DIRTYTIME_EXP_SEC, NULL},
+ {LXPR_SYS_VM_DIRTY_WB_CS, NULL},
+ {LXPR_SYS_VM_OVERCOMMIT_MEM, NULL},
+ {LXPR_SYS_VM_SWAPPINESS, NULL},
+ {LXPR_INVALID, NULL}
+};
+
+/*
+ * Centralized test for the standard writable proc files. Other non-standard
+ * writable files might be handled separately.
+ */
+boolean_t
+lxpr_is_writable(lxpr_nodetype_t type)
+{
+ int i;
+
+ for (i = 0; wr_tab[i].wft_type != LXPR_INVALID; i++) {
+ if (wr_tab[i].wft_type == type)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+/*
+ * lxpr_open(): Vnode operation for VOP_OPEN()
+ */
+static int
+lxpr_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ vnode_t *vp = *vpp;
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ vnode_t *rvp;
+ int error = 0;
+
+ /* Restrict writes to certain files */
+ if ((flag & FWRITE) && !lxpr_is_writable(type)) {
+ return (EPERM);
+ }
+
+ /*
+ * If we are opening an underlying file only allow regular files,
+ * fifos or sockets; reject the open for anything else.
+ * Just do it if we are opening the current or root directory.
+ */
+ if (lxpnp->lxpr_realvp != NULL) {
+ rvp = lxpnp->lxpr_realvp;
+
+ if (type == LXPR_PID_FD_FD && rvp->v_type != VREG &&
+ rvp->v_type != VFIFO && rvp->v_type != VSOCK) {
+ error = EACCES;
+ } else {
+ if (type == LXPR_PID_FD_FD && rvp->v_type == VFIFO) {
+ /*
+ * This flag lets the fifo open know that
+ * we're using proc/fd to open a fd which we
+ * already have open. Otherwise, the fifo might
+ * reject an open if the other end has closed.
+ */
+ flag |= FKLYR;
+ }
+ /*
+ * Need to hold rvp since VOP_OPEN() may release it.
+ */
+ VN_HOLD(rvp);
+ error = VOP_OPEN(&rvp, flag, cr, ct);
+ if (error) {
+ VN_RELE(rvp);
+ } else {
+ *vpp = rvp;
+ VN_RELE(vp);
+ }
+ }
+ }
+
+ return (error);
+}
+
+
+/*
+ * lxpr_close(): Vnode operation for VOP_CLOSE()
+ */
+static int
+lxpr_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ct)
+{
+#ifdef DEBUG
+ lxpr_node_t *lxpr = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpr->lxpr_type;
+
+ /*
+ * we should never get here because the close is done on the realvp
+ * for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR &&
+ type != LXPR_PID_EXE);
+#endif /* DEBUG */
+
+ return (0);
+}
+
+static void (*lxpr_read_function[])() = {
+ NULL, /* invalid */
+ lxpr_read_isdir, /* /proc */
+ lxpr_read_isdir, /* /proc/<pid> */
+ lxpr_read_pid_auxv, /* /proc/<pid>/auxv */
+ lxpr_read_pid_cgroup, /* /proc/<pid>/cgroup */
+ lxpr_read_pid_cmdline, /* /proc/<pid>/cmdline */
+ lxpr_read_pid_tid_comm, /* /proc/<pid>/comm */
+ lxpr_read_empty, /* /proc/<pid>/cpu */
+ lxpr_read_invalid, /* /proc/<pid>/cwd */
+ lxpr_read_pid_env, /* /proc/<pid>/environ */
+ lxpr_read_invalid, /* /proc/<pid>/exe */
+ lxpr_read_pid_id_map, /* /proc/<pid>/gid_map */
+ lxpr_read_pid_limits, /* /proc/<pid>/limits */
+ lxpr_read_pid_loginuid, /* /proc/<pid>/loginuid */
+ lxpr_read_pid_maps, /* /proc/<pid>/maps */
+ lxpr_read_empty, /* /proc/<pid>/mem */
+ lxpr_read_pid_mountinfo, /* /proc/<pid>/mountinfo */
+ lxpr_read_mounts, /* /proc/<pid>/mounts */
+ lxpr_read_pid_oom_scr_adj, /* /proc/<pid>/oom_score_adj */
+ lxpr_read_pid_personality, /* /proc/<pid>/personality */
+ lxpr_read_invalid, /* /proc/<pid>/root */
+ lxpr_read_pid_tid_stat, /* /proc/<pid>/stat */
+ lxpr_read_pid_statm, /* /proc/<pid>/statm */
+ lxpr_read_pid_tid_status, /* /proc/<pid>/status */
+ lxpr_read_isdir, /* /proc/<pid>/task */
+ lxpr_read_isdir, /* /proc/<pid>/task/nn */
+ lxpr_read_isdir, /* /proc/<pid>/fd */
+ lxpr_read_fd, /* /proc/<pid>/fd/nn */
+ lxpr_read_isdir, /* /proc/<pid>/fdinfo */
+ lxpr_read_fdinfo, /* /proc/<pid>/fdinfo/nn */
+ lxpr_read_pid_id_map, /* /proc/<pid>/uid_map */
+ lxpr_read_pid_auxv, /* /proc/<pid>/task/<tid>/auxv */
+ lxpr_read_pid_cgroup, /* /proc/<pid>/task/<tid>/cgroup */
+ lxpr_read_pid_cmdline, /* /proc/<pid>/task/<tid>/cmdline */
+ lxpr_read_pid_tid_comm, /* /proc/<pid>/task/<tid>/comm */
+ lxpr_read_empty, /* /proc/<pid>/task/<tid>/cpu */
+ lxpr_read_invalid, /* /proc/<pid>/task/<tid>/cwd */
+ lxpr_read_pid_env, /* /proc/<pid>/task/<tid>/environ */
+ lxpr_read_invalid, /* /proc/<pid>/task/<tid>/exe */
+ lxpr_read_pid_id_map, /* /proc/<pid>/task/<tid>/gid_map */
+ lxpr_read_pid_limits, /* /proc/<pid>/task/<tid>/limits */
+ lxpr_read_pid_loginuid, /* /proc/<pid>/task/<tid>/loginuid */
+ lxpr_read_pid_maps, /* /proc/<pid>/task/<tid>/maps */
+ lxpr_read_empty, /* /proc/<pid>/task/<tid>/mem */
+ lxpr_read_pid_mountinfo, /* /proc/<pid>/task/<tid>/mountinfo */
+ lxpr_read_pid_oom_scr_adj, /* /proc/<pid>/task/<tid>/oom_scr_adj */
+ lxpr_read_pid_personality, /* /proc/<pid>/task/<tid>/personality */
+ lxpr_read_invalid, /* /proc/<pid>/task/<tid>/root */
+ lxpr_read_pid_tid_stat, /* /proc/<pid>/task/<tid>/stat */
+ lxpr_read_pid_statm, /* /proc/<pid>/task/<tid>/statm */
+ lxpr_read_pid_tid_status, /* /proc/<pid>/task/<tid>/status */
+ lxpr_read_isdir, /* /proc/<pid>/task/<tid>/fd */
+ lxpr_read_fd, /* /proc/<pid>/task/<tid>/fd/nn */
+ lxpr_read_isdir, /* /proc/<pid>/task/<tid>/fdinfo */
+ lxpr_read_fdinfo, /* /proc/<pid>/task/<tid>/fdinfo/nn */
+ lxpr_read_pid_id_map, /* /proc/<pid>/task/<tid>/uid_map */
+ lxpr_read_cgroups, /* /proc/cgroups */
+ lxpr_read_cmdline, /* /proc/cmdline */
+ lxpr_read_cpuinfo, /* /proc/cpuinfo */
+ lxpr_read_devices, /* /proc/devices */
+ lxpr_read_diskstats, /* /proc/diskstats */
+ lxpr_read_empty, /* /proc/dma */
+ lxpr_read_filesystems, /* /proc/filesystems */
+ lxpr_read_empty, /* /proc/interrupts */
+ lxpr_read_empty, /* /proc/ioports */
+ lxpr_read_empty, /* /proc/kcore */
+ lxpr_read_invalid, /* /proc/kmsg -- see lxpr_read() */
+ lxpr_read_loadavg, /* /proc/loadavg */
+ lxpr_read_meminfo, /* /proc/meminfo */
+ lxpr_read_empty, /* /proc/modules */
+ lxpr_read_mounts, /* /proc/mounts */
+ lxpr_read_isdir, /* /proc/net */
+ lxpr_read_net_arp, /* /proc/net/arp */
+ lxpr_read_net_dev, /* /proc/net/dev */
+ lxpr_read_net_dev_mcast, /* /proc/net/dev_mcast */
+ lxpr_read_net_if_inet6, /* /proc/net/if_inet6 */
+ lxpr_read_net_igmp, /* /proc/net/igmp */
+ lxpr_read_net_ip_mr_cache, /* /proc/net/ip_mr_cache */
+ lxpr_read_net_ip_mr_vif, /* /proc/net/ip_mr_vif */
+ lxpr_read_net_ipv6_route, /* /proc/net/ipv6_route */
+ lxpr_read_net_mcfilter, /* /proc/net/mcfilter */
+ lxpr_read_net_netstat, /* /proc/net/netstat */
+ lxpr_read_net_raw, /* /proc/net/raw */
+ lxpr_read_net_route, /* /proc/net/route */
+ lxpr_read_net_rpc, /* /proc/net/rpc */
+ lxpr_read_net_rt_cache, /* /proc/net/rt_cache */
+ lxpr_read_net_sockstat, /* /proc/net/sockstat */
+ lxpr_read_net_snmp, /* /proc/net/snmp */
+ lxpr_read_net_stat, /* /proc/net/stat */
+ lxpr_read_net_tcp, /* /proc/net/tcp */
+ lxpr_read_net_tcp6, /* /proc/net/tcp6 */
+ lxpr_read_net_udp, /* /proc/net/udp */
+ lxpr_read_net_udp6, /* /proc/net/udp6 */
+ lxpr_read_net_unix, /* /proc/net/unix */
+ lxpr_read_partitions, /* /proc/partitions */
+ lxpr_read_invalid, /* /proc/self */
+ lxpr_read_stat, /* /proc/stat */
+ lxpr_read_swaps, /* /proc/swaps */
+ lxpr_read_invalid, /* /proc/sys */
+ lxpr_read_invalid, /* /proc/sys/fs */
+ lxpr_read_sys_fs_aiomax, /* /proc/sys/fs/aio-max-nr */
+ lxpr_read_sys_fs_aionr, /* /proc/sys/fs/aio-nr */
+ lxpr_read_sys_fs_filemax, /* /proc/sys/fs/file-max */
+ lxpr_read_sys_fs_filenr, /* /proc/sys/fs/file-nr */
+ lxpr_read_invalid, /* /proc/sys/fs/inotify */
+ lxpr_read_sys_fs_inotify_max_queued_events, /* max_queued_events */
+ lxpr_read_sys_fs_inotify_max_user_instances, /* max_user_instances */
+ lxpr_read_sys_fs_inotify_max_user_watches, /* max_user_watches */
+ lxpr_read_sys_fs_pipe_max, /* /proc/sys/fs/pipe-max-size */
+ lxpr_read_invalid, /* /proc/sys/kernel */
+ lxpr_read_sys_kernel_caplcap, /* /proc/sys/kernel/cap_last_cap */
+ lxpr_read_sys_kernel_corepatt, /* /proc/sys/kernel/core_pattern */
+ lxpr_read_sys_kernel_hostname, /* /proc/sys/kernel/hostname */
+ lxpr_read_sys_kernel_msgmax, /* /proc/sys/kernel/msgmax */
+ lxpr_read_sys_kernel_msgmnb, /* /proc/sys/kernel/msgmnb */
+ lxpr_read_sys_kernel_msgmni, /* /proc/sys/kernel/msgmni */
+ lxpr_read_sys_kernel_ngroups_max, /* /proc/sys/kernel/ngroups_max */
+ lxpr_read_sys_kernel_osrel, /* /proc/sys/kernel/osrelease */
+ lxpr_read_sys_kernel_pid_max, /* /proc/sys/kernel/pid_max */
+ lxpr_read_invalid, /* /proc/sys/kernel/random */
+ lxpr_read_sys_kernel_rand_bootid, /* /proc/sys/kernel/random/boot_id */
+ lxpr_read_sys_kernel_rand_entavl, /* .../kernel/random/entropy_avail */
+ lxpr_read_sys_kernel_rand_uuid, /* .../kernel/random/uuid */
+ lxpr_read_sys_kernel_sem, /* /proc/sys/kernel/sem */
+ lxpr_read_sys_kernel_shmall, /* /proc/sys/kernel/shmall */
+ lxpr_read_sys_kernel_shmmax, /* /proc/sys/kernel/shmmax */
+ lxpr_read_sys_kernel_shmmni, /* /proc/sys/kernel/shmmni */
+ lxpr_read_sys_kernel_threads_max, /* /proc/sys/kernel/threads-max */
+ lxpr_read_invalid, /* /proc/sys/net */
+ lxpr_read_invalid, /* /proc/sys/net/core */
+ lxpr_read_sys_net_core_somaxc, /* /proc/sys/net/core/somaxconn */
+ lxpr_read_invalid, /* /proc/sys/net/ipv4 */
+ lxpr_read_sys_net_ipv4_icmp_eib, /* .../icmp_echo_ignore_broadcasts */
+ lxpr_read_sys_net_ipv4_ip_forward, /* .../ipv4/ip_forward */
+ lxpr_read_sys_net_ipv4_ip_lport_range, /* ../ipv4/ip_local_port_range */
+ /* .../tcp_allowed_congestion_control */
+ lxpr_read_sys_net_ipv4_tcp_cc_allow,
+ /* .../tcp_available_congestion_control */
+ lxpr_read_sys_net_ipv4_tcp_cc_avail,
+ /* .../tcp_congestion_control */
+ lxpr_read_sys_net_ipv4_tcp_cc_curr,
+ lxpr_read_sys_net_ipv4_tcp_fin_to, /* .../ipv4/tcp_fin_timeout */
+ lxpr_read_sys_net_ipv4_tcp_ka_int, /* .../ipv4/tcp_keepalive_intvl */
+ lxpr_read_sys_net_ipv4_tcp_ka_tim, /* .../ipv4/tcp_keepalive_time */
+ lxpr_read_sys_net_ipv4_tcp_max_syn_bl, /* ../ipv4/tcp_max_syn_backlog */
+ lxpr_read_sys_net_ipv4_tcp_retry2, /* .../ipv4/tcp_retries2 */
+ lxpr_read_sys_net_ipv4_tcp_rwmem, /* .../ipv4/tcp_rmem */
+ lxpr_read_sys_net_ipv4_tcp_sack, /* .../ipv4/tcp_sack */
+ lxpr_read_sys_net_ipv4_tcp_winscale, /* .../ipv4/tcp_window_scaling */
+ lxpr_read_sys_net_ipv4_tcp_rwmem, /* .../ipv4/tcp_wmem */
+ lxpr_read_invalid, /* /proc/sys/vm */
+ lxpr_read_sys_vm_dirty, /* .../vm/dirty_background_bytes */
+ lxpr_read_sys_vm_dirty, /* .../vm/dirty_background_ratio */
+ lxpr_read_sys_vm_dirty, /* .../vm/dirty_bytes */
+ lxpr_read_sys_vm_dirty, /* .../vm/dirty_expire_centisecs */
+ lxpr_read_sys_vm_dirty, /* .../vm/dirty_ratio */
+ lxpr_read_sys_vm_dirty, /* .../vm/dirtytime_expire_seconds */
+ lxpr_read_sys_vm_dirty, /* .../vm/dirty_writeback_centisecs */
+ lxpr_read_sys_vm_max_map_cnt, /* /proc/sys/vm/max_map_count */
+ lxpr_read_sys_vm_minfr_kb, /* /proc/sys/vm/min_free_kbytes */
+ lxpr_read_sys_vm_nhpages, /* /proc/sys/vm/nr_hugepages */
+ lxpr_read_sys_vm_overcommit_mem, /* /proc/sys/vm/overcommit_memory */
+ lxpr_read_sys_vm_swappiness, /* /proc/sys/vm/swappiness */
+ lxpr_read_uptime, /* /proc/uptime */
+ lxpr_read_version, /* /proc/version */
+ lxpr_read_vmstat, /* /proc/vmstat */
+};
+
+CTASSERT(ARRAY_SIZE(lxpr_read_function) == LXPR_NFILES);
+
+/*
+ * Array of lookup functions, indexed by lx /proc file type.
+ */
+static vnode_t *(*lxpr_lookup_function[])() = {
+ NULL, /* invalid */
+ lxpr_lookup_procdir, /* /proc */
+ lxpr_lookup_piddir, /* /proc/<pid> */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/auxv */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cgroup */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cmdline */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/comm */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cpu */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cwd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/environ */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/exe */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/gid_map */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/limits */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/loginuid */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/maps */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/mem */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/mountinfo */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/mounts */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/oom_score_adj */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/personality */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/root */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/stat */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/statm */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/status */
+ lxpr_lookup_taskdir, /* /proc/<pid>/task */
+ lxpr_lookup_task_tid_dir, /* /proc/<pid>/task/nn */
+ lxpr_lookup_fddir, /* /proc/<pid>/fd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/fd/nn */
+ lxpr_lookup_fdinfodir, /* /proc/<pid>/fdinfo */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/fdinfo/nn */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/uid_map */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/auxv */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/cgroup */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/cmdline */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/comm */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/cpu */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/cwd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/environ */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/exe */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/gid_map */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/limits */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/loginuid */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/maps */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/mem */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/mountinfo */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/oom_scr_adj */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/personality */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/root */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/stat */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/statm */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/status */
+ lxpr_lookup_fddir, /* /proc/<pid>/task/<tid>/fd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/fd/nn */
+ lxpr_lookup_fdinfodir, /* /proc/<pid>/task/<tid>/fdinfo */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/fdinfo/nn */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/task/<tid>/uid_map */
+ lxpr_lookup_not_a_dir, /* /proc/cgroups */
+ lxpr_lookup_not_a_dir, /* /proc/cmdline */
+ lxpr_lookup_not_a_dir, /* /proc/cpuinfo */
+ lxpr_lookup_not_a_dir, /* /proc/devices */
+ lxpr_lookup_not_a_dir, /* /proc/diskstats */
+ lxpr_lookup_not_a_dir, /* /proc/dma */
+ lxpr_lookup_not_a_dir, /* /proc/filesystems */
+ lxpr_lookup_not_a_dir, /* /proc/interrupts */
+ lxpr_lookup_not_a_dir, /* /proc/ioports */
+ lxpr_lookup_not_a_dir, /* /proc/kcore */
+ lxpr_lookup_not_a_dir, /* /proc/kmsg */
+ lxpr_lookup_not_a_dir, /* /proc/loadavg */
+ lxpr_lookup_not_a_dir, /* /proc/meminfo */
+ lxpr_lookup_not_a_dir, /* /proc/modules */
+ lxpr_lookup_not_a_dir, /* /proc/mounts */
+ lxpr_lookup_netdir, /* /proc/net */
+ lxpr_lookup_not_a_dir, /* /proc/net/arp */
+ lxpr_lookup_not_a_dir, /* /proc/net/dev */
+ lxpr_lookup_not_a_dir, /* /proc/net/dev_mcast */
+ lxpr_lookup_not_a_dir, /* /proc/net/if_inet6 */
+ lxpr_lookup_not_a_dir, /* /proc/net/igmp */
+ lxpr_lookup_not_a_dir, /* /proc/net/ip_mr_cache */
+ lxpr_lookup_not_a_dir, /* /proc/net/ip_mr_vif */
+ lxpr_lookup_not_a_dir, /* /proc/net/ipv6_route */
+ lxpr_lookup_not_a_dir, /* /proc/net/mcfilter */
+ lxpr_lookup_not_a_dir, /* /proc/net/netstat */
+ lxpr_lookup_not_a_dir, /* /proc/net/raw */
+ lxpr_lookup_not_a_dir, /* /proc/net/route */
+ lxpr_lookup_not_a_dir, /* /proc/net/rpc */
+ lxpr_lookup_not_a_dir, /* /proc/net/rt_cache */
+ lxpr_lookup_not_a_dir, /* /proc/net/sockstat */
+ lxpr_lookup_not_a_dir, /* /proc/net/snmp */
+ lxpr_lookup_not_a_dir, /* /proc/net/stat */
+ lxpr_lookup_not_a_dir, /* /proc/net/tcp */
+ lxpr_lookup_not_a_dir, /* /proc/net/tcp6 */
+ lxpr_lookup_not_a_dir, /* /proc/net/udp */
+ lxpr_lookup_not_a_dir, /* /proc/net/udp6 */
+ lxpr_lookup_not_a_dir, /* /proc/net/unix */
+ lxpr_lookup_not_a_dir, /* /proc/partitions */
+ lxpr_lookup_not_a_dir, /* /proc/self */
+ lxpr_lookup_not_a_dir, /* /proc/stat */
+ lxpr_lookup_not_a_dir, /* /proc/swaps */
+ lxpr_lookup_sysdir, /* /proc/sys */
+ lxpr_lookup_sys_fsdir, /* /proc/sys/fs */
+ lxpr_lookup_not_a_dir, /* /proc/sys/fs/aio-max-nr */
+ lxpr_lookup_not_a_dir, /* /proc/sys/fs/aio-nr */
+ lxpr_lookup_not_a_dir, /* /proc/sys/fs/file-max */
+ lxpr_lookup_not_a_dir, /* /proc/sys/fs/file-nr */
+ lxpr_lookup_sys_fs_inotifydir, /* /proc/sys/fs/inotify */
+ lxpr_lookup_not_a_dir, /* .../inotify/max_queued_events */
+ lxpr_lookup_not_a_dir, /* .../inotify/max_user_instances */
+ lxpr_lookup_not_a_dir, /* .../inotify/max_user_watches */
+ lxpr_lookup_not_a_dir, /* /proc/sys/fs/pipe-max-size */
+ lxpr_lookup_sys_kerneldir, /* /proc/sys/kernel */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/cap_last_cap */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/core_pattern */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/hostname */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/msgmax */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/msgmnb */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/msgmni */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/ngroups_max */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/osrelease */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/pid_max */
+ lxpr_lookup_sys_kdir_randdir, /* /proc/sys/kernel/random */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/random/boot_id */
+ lxpr_lookup_not_a_dir, /* .../kernel/random/entropy_avail */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/random/uuid */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/sem */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/shmall */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/shmmax */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/shmmni */
+ lxpr_lookup_not_a_dir, /* /proc/sys/kernel/threads-max */
+ lxpr_lookup_sys_netdir, /* /proc/sys/net */
+ lxpr_lookup_sys_net_coredir, /* /proc/sys/net/core */
+ lxpr_lookup_not_a_dir, /* /proc/sys/net/core/somaxconn */
+ lxpr_lookup_sys_net_ipv4dir, /* /proc/sys/net/ipv4 */
+ lxpr_lookup_not_a_dir, /* .../icmp_echo_ignore_broadcasts */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/ip_forward */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/ip_local_port_range */
+ /* .../tcp_allowed_congestion_control */
+ lxpr_lookup_not_a_dir,
+ /* .../tcp_available_congestion_control */
+ lxpr_lookup_not_a_dir,
+ /* .../tcp_congestion_control */
+ lxpr_lookup_not_a_dir,
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_fin_timeout */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_keepalive_intvl */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_keepalive_time */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_max_syn_backlog */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_retries2 */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_rmem */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_sack */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_window_scaling */
+ lxpr_lookup_not_a_dir, /* .../net/ipv4/tcp_wmem */
+ lxpr_lookup_sys_vmdir, /* /proc/sys/vm */
+ lxpr_lookup_not_a_dir, /* .../vm/dirty_background_bytes */
+ lxpr_lookup_not_a_dir, /* .../vm/dirty_background_ratio */
+ lxpr_lookup_not_a_dir, /* .../vm/dirty_bytes */
+ lxpr_lookup_not_a_dir, /* .../vm/dirty_expire_centisecs */
+ lxpr_lookup_not_a_dir, /* .../vm/dirty_ratio */
+ lxpr_lookup_not_a_dir, /* .../vm/dirtytime_expire_seconds */
+ lxpr_lookup_not_a_dir, /* .../vm/dirty_writeback_centisecs */
+ lxpr_lookup_not_a_dir, /* /proc/sys/vm/max_map_count */
+ lxpr_lookup_not_a_dir, /* /proc/sys/vm/min_free_kbytes */
+ lxpr_lookup_not_a_dir, /* /proc/sys/vm/nr_hugepages */
+ lxpr_lookup_not_a_dir, /* /proc/sys/vm/overcommit_memory */
+ lxpr_lookup_not_a_dir, /* /proc/sys/vm/swappiness */
+ lxpr_lookup_not_a_dir, /* /proc/uptime */
+ lxpr_lookup_not_a_dir, /* /proc/version */
+ lxpr_lookup_not_a_dir, /* /proc/vmstat */
+};
+
+CTASSERT(ARRAY_SIZE(lxpr_lookup_function) == LXPR_NFILES);
+
+/*
+ * Array of readdir functions, indexed by /proc file type.
+ */
+static int (*lxpr_readdir_function[])() = {
+ NULL, /* invalid */
+ lxpr_readdir_procdir, /* /proc */
+ lxpr_readdir_piddir, /* /proc/<pid> */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/auxv */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cgroup */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cmdline */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/comm */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cpu */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cwd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/environ */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/exe */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/gid_map */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/limits */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/loginuid */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/maps */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/mem */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/mountinfo */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/mounts */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/oom_score_adj */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/personality */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/root */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/stat */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/statm */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/status */
+ lxpr_readdir_taskdir, /* /proc/<pid>/task */
+ lxpr_readdir_task_tid_dir, /* /proc/<pid>/task/nn */
+ lxpr_readdir_fddir, /* /proc/<pid>/fd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/fd/nn */
+ lxpr_readdir_fdinfodir, /* /proc/<pid>/fdinfo */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/fdinfo/nn */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/uid_map */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/auxv */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/cgroup */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/cmdline */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/comm */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/cpu */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/cwd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/environ */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/exe */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/gid_map */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/limits */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/loginuid */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/maps */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/mem */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/mountinfo */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid/oom_scr_adj */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid/personality */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/root */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/stat */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/statm */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/status */
+ lxpr_readdir_fddir, /* /proc/<pid>/task/<tid>/fd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/fd/nn */
+ lxpr_readdir_fdinfodir, /* /proc/<pid>/task/<tid>/fdinfo */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/fdinfo/nn */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/task/<tid>/uid_map */
+ lxpr_readdir_not_a_dir, /* /proc/cgroups */
+ lxpr_readdir_not_a_dir, /* /proc/cmdline */
+ lxpr_readdir_not_a_dir, /* /proc/cpuinfo */
+ lxpr_readdir_not_a_dir, /* /proc/devices */
+ lxpr_readdir_not_a_dir, /* /proc/diskstats */
+ lxpr_readdir_not_a_dir, /* /proc/dma */
+ lxpr_readdir_not_a_dir, /* /proc/filesystems */
+ lxpr_readdir_not_a_dir, /* /proc/interrupts */
+ lxpr_readdir_not_a_dir, /* /proc/ioports */
+ lxpr_readdir_not_a_dir, /* /proc/kcore */
+ lxpr_readdir_not_a_dir, /* /proc/kmsg */
+ lxpr_readdir_not_a_dir, /* /proc/loadavg */
+ lxpr_readdir_not_a_dir, /* /proc/meminfo */
+ lxpr_readdir_not_a_dir, /* /proc/modules */
+ lxpr_readdir_not_a_dir, /* /proc/mounts */
+ lxpr_readdir_netdir, /* /proc/net */
+ lxpr_readdir_not_a_dir, /* /proc/net/arp */
+ lxpr_readdir_not_a_dir, /* /proc/net/dev */
+ lxpr_readdir_not_a_dir, /* /proc/net/dev_mcast */
+ lxpr_readdir_not_a_dir, /* /proc/net/if_inet6 */
+ lxpr_readdir_not_a_dir, /* /proc/net/igmp */
+ lxpr_readdir_not_a_dir, /* /proc/net/ip_mr_cache */
+ lxpr_readdir_not_a_dir, /* /proc/net/ip_mr_vif */
+ lxpr_readdir_not_a_dir, /* /proc/net/ipv6_route */
+ lxpr_readdir_not_a_dir, /* /proc/net/mcfilter */
+ lxpr_readdir_not_a_dir, /* /proc/net/netstat */
+ lxpr_readdir_not_a_dir, /* /proc/net/raw */
+ lxpr_readdir_not_a_dir, /* /proc/net/route */
+ lxpr_readdir_not_a_dir, /* /proc/net/rpc */
+ lxpr_readdir_not_a_dir, /* /proc/net/rt_cache */
+ lxpr_readdir_not_a_dir, /* /proc/net/sockstat */
+ lxpr_readdir_not_a_dir, /* /proc/net/snmp */
+ lxpr_readdir_not_a_dir, /* /proc/net/stat */
+ lxpr_readdir_not_a_dir, /* /proc/net/tcp */
+ lxpr_readdir_not_a_dir, /* /proc/net/tcp6 */
+ lxpr_readdir_not_a_dir, /* /proc/net/udp */
+ lxpr_readdir_not_a_dir, /* /proc/net/udp6 */
+ lxpr_readdir_not_a_dir, /* /proc/net/unix */
+ lxpr_readdir_not_a_dir, /* /proc/partitions */
+ lxpr_readdir_not_a_dir, /* /proc/self */
+ lxpr_readdir_not_a_dir, /* /proc/stat */
+ lxpr_readdir_not_a_dir, /* /proc/swaps */
+ lxpr_readdir_sysdir, /* /proc/sys */
+ lxpr_readdir_sys_fsdir, /* /proc/sys/fs */
+ lxpr_readdir_not_a_dir, /* /proc/sys/fs/aio-max-nr */
+ lxpr_readdir_not_a_dir, /* /proc/sys/fs/aio-nr */
+ lxpr_readdir_not_a_dir, /* /proc/sys/fs/file-max */
+ lxpr_readdir_not_a_dir, /* /proc/sys/fs/file-nr */
+ lxpr_readdir_sys_fs_inotifydir, /* /proc/sys/fs/inotify */
+ lxpr_readdir_not_a_dir, /* .../inotify/max_queued_events */
+ lxpr_readdir_not_a_dir, /* .../inotify/max_user_instances */
+ lxpr_readdir_not_a_dir, /* .../inotify/max_user_watches */
+ lxpr_readdir_not_a_dir, /* /proc/sys/fs/pipe-max-size */
+ lxpr_readdir_sys_kerneldir, /* /proc/sys/kernel */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/cap_last_cap */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/core_pattern */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/hostname */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/msgmax */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/msgmnb */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/msgmni */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/ngroups_max */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/osrelease */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/pid_max */
+ lxpr_readdir_sys_kdir_randdir, /* /proc/sys/kernel/random */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/random/boot_id */
+ lxpr_readdir_not_a_dir, /* .../kernel/random/entropy_avail */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/random/uuid */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/sem */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/shmall */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/shmmax */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/shmmni */
+ lxpr_readdir_not_a_dir, /* /proc/sys/kernel/threads-max */
+ lxpr_readdir_sys_netdir, /* /proc/sys/net */
+ lxpr_readdir_sys_net_coredir, /* /proc/sys/net/core */
+ lxpr_readdir_not_a_dir, /* /proc/sys/net/core/somaxconn */
+ lxpr_readdir_sys_net_ipv4dir, /* /proc/sys/net/ipv4 */
+ lxpr_readdir_not_a_dir, /* .../icmp_echo_ignore_broadcasts */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/ip_forward */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/ip_local_port_range */
+ /* .../tcp_allowed_congestion_control */
+ lxpr_readdir_not_a_dir,
+ /* .../tcp_available_congestion_control */
+ lxpr_readdir_not_a_dir,
+ /* .../tcp_congestion_control */
+ lxpr_readdir_not_a_dir,
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_fin_timeout */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_keepalive_intvl */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_keepalive_time */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_max_syn_backlog */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_retries2 */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_rmem */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_sack */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_window_scaling */
+ lxpr_readdir_not_a_dir, /* .../net/ipv4/tcp_wmem */
+ lxpr_readdir_sys_vmdir, /* /proc/sys/vm */
+ lxpr_readdir_not_a_dir, /* .../vm/dirty_background_bytes */
+ lxpr_readdir_not_a_dir, /* .../vm/dirty_background_ratio */
+ lxpr_readdir_not_a_dir, /* .../vm/dirty_bytes */
+ lxpr_readdir_not_a_dir, /* .../vm/dirty_expire_centisecs */
+ lxpr_readdir_not_a_dir, /* .../vm/dirty_ratio */
+ lxpr_readdir_not_a_dir, /* .../vm/dirtytime_expire_seconds */
+ lxpr_readdir_not_a_dir, /* .../vm/dirty_writeback_centisecs */
+ lxpr_readdir_not_a_dir, /* /proc/sys/vm/max_map_count */
+ lxpr_readdir_not_a_dir, /* /proc/sys/vm/min_free_kbytes */
+ lxpr_readdir_not_a_dir, /* /proc/sys/vm/nr_hugepages */
+ lxpr_readdir_not_a_dir, /* /proc/sys/vm/overcommit_memory */
+ lxpr_readdir_not_a_dir, /* /proc/sys/vm/swappiness */
+ lxpr_readdir_not_a_dir, /* /proc/uptime */
+ lxpr_readdir_not_a_dir, /* /proc/version */
+ lxpr_readdir_not_a_dir, /* /proc/vmstat */
+};
+
+CTASSERT(ARRAY_SIZE(lxpr_readdir_function) == LXPR_NFILES);
+
+/*
+ * lxpr_read(): Vnode operation for VOP_READ()
+ *
+ * As the format of all the files that can be read in the lx procfs is human
+ * readable and not binary structures there do not have to be different
+ * read variants depending on whether the reading process model is 32 or 64 bits
+ * (at least in general, and certainly the difference is unlikely to be enough
+ * to justify have different routines for 32 and 64 bit reads
+ */
+static int
+lxpr_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ lxpr_uiobuf_t *uiobuf = lxpr_uiobuf_new(uiop);
+ int error;
+
+ ASSERT(type < LXPR_NFILES);
+
+ if (type == LXPR_KMSG) {
+ ldi_ident_t li = VTOLXPM(vp)->lxprm_li;
+ ldi_handle_t ldih;
+ struct strioctl str;
+ int rv;
+
+ /*
+ * Open the zone's console device using the layered driver
+ * interface.
+ */
+ if ((error =
+ ldi_open_by_name("/dev/log", FREAD, cr, &ldih, li)) != 0)
+ return (error);
+
+ /*
+ * Send an ioctl to the underlying console device, letting it
+ * know we're interested in getting console messages.
+ */
+ str.ic_cmd = I_CONSLOG;
+ str.ic_timout = 0;
+ str.ic_len = 0;
+ str.ic_dp = NULL;
+ if ((error = ldi_ioctl(ldih, I_STR,
+ (intptr_t)&str, FKIOCTL, cr, &rv)) != 0)
+ return (error);
+
+ lxpr_read_kmsg(lxpnp, uiobuf, ldih);
+
+ if ((error = ldi_close(ldih, FREAD, cr)) != 0)
+ return (error);
+ } else {
+ lxpr_read_function[type](lxpnp, uiobuf);
+ }
+
+ error = lxpr_uiobuf_flush(uiobuf);
+ lxpr_uiobuf_free(uiobuf);
+
+ return (error);
+}
+
+/*
+ * lxpr_read_invalid(), lxpr_read_isdir(), lxpr_read_empty()
+ *
+ * Various special case reads:
+ * - trying to read a directory
+ * - invalid file (used to mean a file that should be implemented,
+ * but isn't yet)
+ * - empty file
+ * - wait to be able to read a file that will never have anything to read
+ */
+static void
+lxpr_read_isdir(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_seterr(uiobuf, EISDIR);
+}
+
+static void
+lxpr_read_invalid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+}
+
+static void
+lxpr_read_empty(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/*
+ * lxpr_read_pid_auxv(): read process aux vector
+ */
+static void
+lxpr_read_pid_auxv(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ lx_proc_data_t *pd;
+ lx_elf_data_t *edp = NULL;
+ int i, cnt;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_AUXV ||
+ lxpnp->lxpr_type == LXPR_PID_TID_AUXV);
+
+ p = lxpr_lock(lxpnp, NO_ZOMB);
+
+ if (p == NULL) {
+ return;
+ }
+ if ((pd = ptolxproc(p)) == NULL) {
+ /* Emit a single AT_NULL record for non-branded processes */
+ auxv_t buf;
+
+ bzero(&buf, sizeof (buf));
+ lxpr_unlock(p);
+ lxpr_uiobuf_write(uiobuf, (char *)&buf, sizeof (buf));
+ return;
+ } else {
+ edp = &pd->l_elf_data;
+ }
+
+ if (p->p_model == DATAMODEL_NATIVE) {
+ auxv_t buf[__KERN_NAUXV_IMPL];
+
+ /*
+ * Because a_type is only of size int (not long), the buffer
+ * contents must be zeroed first to ensure cleanliness.
+ */
+ bzero(buf, sizeof (buf));
+ for (i = 0, cnt = 0; i < __KERN_NAUXV_IMPL; i++) {
+ if (lx_auxv_stol(&p->p_user.u_auxv[i],
+ &buf[cnt], edp) == 0) {
+ cnt++;
+ }
+ if (p->p_user.u_auxv[i].a_type == AT_NULL) {
+ break;
+ }
+ }
+ lxpr_unlock(p);
+ lxpr_uiobuf_write(uiobuf, (char *)buf, cnt * sizeof (buf[0]));
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ auxv32_t buf[__KERN_NAUXV_IMPL];
+
+ for (i = 0, cnt = 0; i < __KERN_NAUXV_IMPL; i++) {
+ auxv_t temp;
+
+ if (lx_auxv_stol(&p->p_user.u_auxv[i],
+ &temp, edp) == 0) {
+ buf[cnt].a_type = (int)temp.a_type;
+ buf[cnt].a_un.a_val = (int)temp.a_un.a_val;
+ cnt++;
+ }
+ if (p->p_user.u_auxv[i].a_type == AT_NULL) {
+ break;
+ }
+ }
+ lxpr_unlock(p);
+ lxpr_uiobuf_write(uiobuf, (char *)buf, cnt * sizeof (buf[0]));
+ }
+#endif /* defined(_SYSCALL32_IMPL) */
+}
+
+/*
+ * lxpr_read_pid_cgroup(): read cgroups for process
+ */
+static void
+lxpr_read_pid_cgroup(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_CGROUP ||
+ lxpnp->lxpr_type == LXPR_PID_TID_CGROUP);
+
+ p = lxpr_lock(lxpnp, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+ lxpr_unlock(p);
+
+ /* basic stub, 3rd field will need to be populated */
+ lxpr_uiobuf_printf(uiobuf, "1:name=systemd:/\n");
+}
+
+/*
+ * lxpr_read_pid_cmdline(): read argument vector from process
+ */
+static void
+lxpr_read_pid_cmdline(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ char *buf;
+ size_t asz = PRMAXARGVLEN, sz;
+ int r;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_CMDLINE ||
+ lxpnp->lxpr_type == LXPR_PID_TID_CMDLINE);
+
+ buf = kmem_alloc(asz, KM_SLEEP);
+
+ p = lxpr_lock(lxpnp, NO_ZOMB);
+ if (p == NULL) {
+ kmem_free(buf, asz);
+ return;
+ }
+
+ r = prreadcmdline(p, buf, asz, &sz);
+
+ lxpr_unlock(p);
+
+ if (r != 0) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ } else {
+ lxpr_uiobuf_write(uiobuf, buf, sz);
+ }
+
+ kmem_free(buf, asz);
+}
+
+/*
+ * lxpr_read_pid_tid_comm(): read command name from thread
+ */
+static void
+lxpr_read_pid_tid_comm(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ kthread_t *t;
+ pid_t tid;
+ char buf[LX_PR_SET_NAME_NAMELEN], *pnm;
+
+ VERIFY(lxpnp->lxpr_type == LXPR_PID_COMM ||
+ lxpnp->lxpr_type == LXPR_PID_TID_COMM);
+
+ tid = (lxpnp->lxpr_desc == 0) ? lxpnp->lxpr_pid : lxpnp->lxpr_desc;
+ p = lxpr_lock_pid(lxpnp, tid, ZOMB_OK, &t);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+ if (t == NULL) {
+ lxpr_unlock(p);
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * If a thread name has not been set, use the process command name.
+ * This also covers the /proc/{pid}/comm case.
+ */
+ if (t->t_name == NULL) {
+ pnm = p->p_user.u_comm;
+ } else {
+ pnm = t->t_name;
+ }
+
+ /* Truncate with NUL if the name is longer than the Linux size. */
+ (void) strlcpy(buf, pnm, sizeof (buf));
+
+ lxpr_unlock(p);
+ lxpr_uiobuf_printf(uiobuf, "%s\n", buf);
+}
+
+static int
+lxpr_write_pid_tid_comm(lxpr_node_t *lxpnp, struct uio *uio, struct cred *cr,
+ caller_context_t *ct)
+{
+ int error;
+ size_t olen;
+ char *buf;
+ proc_t *p;
+ kthread_t *t;
+ pid_t tid;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_COMM ||
+ lxpnp->lxpr_type == LXPR_PID_TID_COMM);
+
+ /*
+ * Only a thread in the process can update one of the thread names. Not
+ * even a process with root privileges. Linux returns EINVAL (not EPERM)
+ * for this case.
+ */
+ if (lxpnp->lxpr_pid != curproc->p_pid)
+ return (EINVAL);
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > LX_PR_SET_NAME_NAMELEN - 1)
+ olen = LX_PR_SET_NAME_NAMELEN - 1;
+
+ buf = kmem_zalloc(THREAD_NAME_MAX, KM_SLEEP);
+
+ error = uiomove(buf, olen, UIO_WRITE, uio);
+ if (error != 0) {
+ kmem_free(buf, THREAD_NAME_MAX);
+ return (error);
+ }
+ buf[LX_PR_SET_NAME_NAMELEN - 1] = '\0';
+
+ tid = (lxpnp->lxpr_desc == 0) ? lxpnp->lxpr_pid : lxpnp->lxpr_desc;
+ p = lxpr_lock_pid(lxpnp, tid, NO_ZOMB, &t);
+ if (p == NULL) {
+ kmem_free(buf, THREAD_NAME_MAX);
+ return (ENXIO);
+ }
+ if (t == NULL) {
+ lxpr_unlock(p);
+ kmem_free(buf, THREAD_NAME_MAX);
+ return (ENXIO);
+ }
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * See comments for thread_setname() and prctl(LX_PR_SET_NAME) handling.
+ */
+ if (t->t_name == NULL) {
+ t->t_name = buf;
+ } else {
+ (void) strlcpy(t->t_name, buf, THREAD_NAME_MAX);
+ kmem_free(buf, THREAD_NAME_MAX);
+ }
+
+ if (t->t_tid == 1) {
+ (void) strncpy(p->p_user.u_comm, t->t_name, MAXCOMLEN + 1);
+ (void) strncpy(p->p_user.u_psargs, t->t_name, PSARGSZ);
+ }
+
+ lxpr_unlock(p);
+ return (0);
+}
+
+/*
+ * lxpr_read_pid_env(): read env vector from process
+ */
+static void
+lxpr_read_pid_env(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ char *buf;
+ size_t asz = lxpr_maxenvvlen, sz;
+ int r;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_ENV);
+
+ buf = kmem_alloc(asz, KM_SLEEP);
+
+ p = lxpr_lock(lxpnp, NO_ZOMB);
+ if (p == NULL) {
+ kmem_free(buf, asz);
+ return;
+ }
+
+ r = prreadenvv(p, buf, asz, &sz);
+ lxpr_unlock(p);
+
+ if (r != 0) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ } else {
+ lxpr_uiobuf_write(uiobuf, buf, sz);
+ }
+ kmem_free(buf, asz);
+}
+
+/*
+ * lxpr_read_pid_limits(): ulimit file
+ */
+static void
+lxpr_read_pid_limits(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ rctl_qty_t cur[LX_RLIM_TAB_LEN], max[LX_RLIM_TAB_LEN];
+ int i;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_LIMITS ||
+ lxpnp->lxpr_type == LXPR_PID_TID_LIMITS);
+
+ p = lxpr_lock(lxpnp, NO_ZOMB);
+ if (p == NULL) {
+ return;
+ }
+
+ for (i = 0; i < LX_RLIM_TAB_LEN; i++) {
+ char *kname = lxpr_rlimtab[i].rlim_rctl;
+ rctl_val_t nval, *oval = NULL;
+ rctl_hndl_t hndl;
+
+ /* default to unlimited for resources without an analog */
+ cur[i] = RLIM_INFINITY;
+ max[i] = RLIM_INFINITY;
+ if (kname == NULL || (hndl = rctl_hndl_lookup(kname)) == -1) {
+ continue;
+ }
+ while (rctl_local_get(hndl, oval, &nval, p) == 0) {
+ oval = &nval;
+ switch (nval.rcv_privilege) {
+ case RCPRIV_BASIC:
+ if (!RCTL_INFINITE(nval))
+ cur[i] = nval.rcv_value;
+ break;
+ case RCPRIV_PRIVILEGED:
+ if (!RCTL_INFINITE(nval))
+ max[i] = nval.rcv_value;
+ break;
+ }
+ }
+ /*
+ * If "Max open files" is still set to RLIM_INFINITY, make it
+ * match the max value so that we do not output "unlimited".
+ */
+ if (strcmp(lxpr_rlimtab[i].rlim_name, RLIM_MAXFD) == 0 &&
+ cur[i] == RLIM_INFINITY) {
+ cur[i] = max[i];
+ }
+
+ }
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf, "%-25s %-20s %-20s %-10s\n",
+ "Limit", "Soft Limit", "Hard Limit", "Units");
+ for (i = 0; i < LX_RLIM_TAB_LEN; i++) {
+ lxpr_uiobuf_printf(uiobuf, "%-25s", lxpr_rlimtab[i].rlim_name);
+ if (cur[i] == RLIM_INFINITY || cur[i] == LX_RLIM_INFINITY) {
+ lxpr_uiobuf_printf(uiobuf, " %-20s", "unlimited");
+ } else {
+ lxpr_uiobuf_printf(uiobuf, " %-20lu", cur[i]);
+ }
+ if (max[i] == RLIM_INFINITY || max[i] == LX_RLIM_INFINITY) {
+ lxpr_uiobuf_printf(uiobuf, " %-20s", "unlimited");
+ } else {
+ lxpr_uiobuf_printf(uiobuf, " %-20lu", max[i]);
+ }
+ lxpr_uiobuf_printf(uiobuf, " %-10s\n",
+ lxpr_rlimtab[i].rlim_unit);
+ }
+}
+/*
+ * lxpr_read_pid_id_map(): gid_map and uid_map file
+ */
+static void
+lxpr_read_pid_id_map(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_GIDMAP ||
+ lxpnp->lxpr_type == LXPR_PID_UIDMAP);
+
+ lxpr_uiobuf_printf(uiobuf, "%10u %10u %10u\n", 0, 0, MAXUID);
+}
+
+/*
+ * lxpr_read_pid_loginuid(): loginuid file
+ */
+static void
+lxpr_read_pid_loginuid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ lx_proc_data_t *pd;
+ uid_t lu = 0;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_LOGINUID ||
+ lxpnp->lxpr_type == LXPR_PID_TID_LOGINUID);
+
+ p = lxpr_lock(lxpnp, NO_ZOMB);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ if ((pd = ptolxproc(p)) != NULL) {
+ lu = pd->l_loginuid;
+ }
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf, "%d", lu);
+}
+
+/*
+ * lxpr_read_pid_maps(): memory map file
+ */
+static void
+lxpr_read_pid_maps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ lx_proc_data_t *lxpd;
+ struct as *as;
+ struct seg *seg;
+ char *buf;
+ int buflen = MAXPATHLEN;
+ struct print_data {
+ uintptr_t saddr;
+ uintptr_t eaddr;
+ int type;
+ char prot[5];
+ uintptr_t offset;
+ vnode_t *vp;
+ char *name_override;
+ struct print_data *next;
+ } *print_head = NULL;
+ struct print_data **print_tail = &print_head;
+ struct print_data *pbuf;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_MAPS ||
+ lxpnp->lxpr_type == LXPR_PID_TID_MAPS);
+
+ p = lxpr_lock(lxpnp, NO_ZOMB);
+ if (p == NULL) {
+ return;
+ }
+
+ as = p->p_as;
+ lxpd = ptolxproc(p);
+
+ if (as == &kas) {
+ lxpr_unlock(p);
+ return;
+ }
+
+ mutex_exit(&p->p_lock);
+
+ /* Iterate over all segments in the address space */
+ AS_LOCK_ENTER(as, RW_READER);
+ for (seg = AS_SEGFIRST(as); seg != NULL; seg = AS_SEGNEXT(as, seg)) {
+ vnode_t *vp;
+ uint_t protbits;
+
+ if ((seg->s_flags & S_HOLE) != 0) {
+ continue;
+ }
+
+ pbuf = kmem_alloc(sizeof (*pbuf), KM_SLEEP);
+
+ pbuf->saddr = (uintptr_t)seg->s_base;
+ pbuf->eaddr = pbuf->saddr + seg->s_size;
+ pbuf->type = SEGOP_GETTYPE(seg, seg->s_base);
+
+ /*
+ * Cheat and only use the protection bits of the first page
+ * in the segment
+ */
+ (void) strncpy(pbuf->prot, "----", sizeof (pbuf->prot));
+ (void) SEGOP_GETPROT(seg, seg->s_base, 0, &protbits);
+
+ if (protbits & PROT_READ) pbuf->prot[0] = 'r';
+ if (protbits & PROT_WRITE) pbuf->prot[1] = 'w';
+ if (protbits & PROT_EXEC) pbuf->prot[2] = 'x';
+ if (pbuf->type & MAP_SHARED) pbuf->prot[3] = 's';
+ else if (pbuf->type & MAP_PRIVATE) pbuf->prot[3] = 'p';
+
+ if (seg->s_ops == &segvn_ops &&
+ SEGOP_GETVP(seg, seg->s_base, &vp) == 0 &&
+ vp != NULL && vp->v_type == VREG) {
+ VN_HOLD(vp);
+ pbuf->vp = vp;
+ } else {
+ pbuf->vp = NULL;
+ }
+
+ pbuf->offset = SEGOP_GETOFFSET(seg, (caddr_t)pbuf->saddr);
+
+ pbuf->name_override = NULL;
+ if (lxpd != NULL) {
+ if (pbuf->saddr == lxpd->l_vdso) {
+ pbuf->name_override = "[vdso]";
+ } else if (pbuf->saddr == p->p_user.u_commpagep) {
+ pbuf->name_override = "[vvar]";
+ }
+ }
+
+ pbuf->next = NULL;
+ *print_tail = pbuf;
+ print_tail = &pbuf->next;
+ }
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+
+ buf = kmem_alloc(buflen, KM_SLEEP);
+
+ /* print the data we've extracted */
+ pbuf = print_head;
+ while (pbuf != NULL) {
+ struct print_data *pbuf_next;
+ vattr_t vattr;
+
+ int maj = 0;
+ int min = 0;
+ ino_t inode = 0;
+
+ *buf = '\0';
+ if (pbuf->name_override != NULL) {
+ (void) strncpy(buf, pbuf->name_override, buflen);
+ } else if (pbuf->vp != NULL) {
+ vattr.va_mask = AT_FSID | AT_NODEID;
+ if (VOP_GETATTR(pbuf->vp, &vattr, 0, CRED(),
+ NULL) == 0) {
+ maj = getmajor(vattr.va_fsid);
+ min = getminor(vattr.va_fsid);
+ inode = vattr.va_nodeid;
+ }
+ (void) vnodetopath(NULL, pbuf->vp, buf, buflen, CRED());
+ VN_RELE(pbuf->vp);
+ }
+
+ if (p->p_model == DATAMODEL_LP64) {
+ lxpr_uiobuf_printf(uiobuf,
+ "%08llx-%08llx %s %08llx %02x:%02x %llu%s%s\n",
+ pbuf->saddr, pbuf->eaddr, pbuf->prot, pbuf->offset,
+ maj, min, inode, *buf != '\0' ? " " : "", buf);
+ } else {
+ lxpr_uiobuf_printf(uiobuf,
+ "%08x-%08x %s %08x %02x:%02x %llu%s%s\n",
+ (uint32_t)pbuf->saddr, (uint32_t)pbuf->eaddr,
+ pbuf->prot, (uint32_t)pbuf->offset, maj, min,
+ inode, *buf != '\0' ? " " : "", buf);
+ }
+
+ pbuf_next = pbuf->next;
+ kmem_free(pbuf, sizeof (*pbuf));
+ pbuf = pbuf_next;
+ }
+
+ kmem_free(buf, buflen);
+}
+
+/*
+ * Make mount entry look more like Linux. Non-zero return to skip it.
+ */
+static int
+lxpr_clean_mntent(char **mntpt, char **fstype, char **resource)
+{
+ if (strcmp(*mntpt, "/var/ld") == 0 ||
+ strcmp(*fstype, "objfs") == 0 ||
+ strcmp(*fstype, "mntfs") == 0 ||
+ strcmp(*fstype, "ctfs") == 0 ||
+ strncmp(*mntpt, "/native/", 8) == 0) {
+ return (1);
+ }
+
+ if (strcmp(*fstype, "tmpfs") == 0) {
+ *resource = "tmpfs";
+ } else if (strcmp(*fstype, "lx_proc") == 0) {
+ *resource = *fstype = "proc";
+ } else if (strcmp(*fstype, "lx_sysfs") == 0) {
+ *resource = *fstype = "sysfs";
+ } else if (strcmp(*fstype, "lx_devfs") == 0) {
+ *resource = *fstype = "devtmpfs";
+ } else if (strcmp(*fstype, "lx_cgroup") == 0) {
+ *resource = *fstype = "cgroup";
+ } else if (strcmp(*fstype, "lxautofs") == 0) {
+ *fstype = "autofs";
+ }
+
+ return (0);
+}
+
+
+typedef struct lxpr_mount_entry {
+ list_node_t lme_link;
+ uint_t lme_id;
+ uint_t lme_parent_id;
+ refstr_t *lme_mntpt;
+ refstr_t *lme_resource;
+ uint_t lme_mntopts_len;
+ char *lme_mntopts;
+ uint_t lme_flag;
+ int lme_fstype;
+ dev_t lme_dev;
+ boolean_t lme_force;
+} lxpr_mount_entry_t;
+
+static int lxpr_zfs_fstype = -1;
+
+#define LXPR_ROOT_MOUNT_ID 15
+#define LXPR_MNT_OPT_CHUNK 128
+
+/* List of native, non-Linux mount options we should omit. */
+static const char *lx_invalid_mnt_opts[] = {
+ "xattr",
+ NULL
+};
+
+/* First see if we should omit this option */
+static boolean_t
+lxpr_skip_mntopt(const char *s)
+{
+ uint_t i;
+
+ for (i = 0; lx_invalid_mnt_opts[i] != NULL; i++) {
+ if (strcmp(s, lx_invalid_mnt_opts[i]) == 0)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+static void
+lxpr_append_mntopt(lxpr_mount_entry_t *lme, char *s)
+{
+ while (strlcat(lme->lme_mntopts, s, lme->lme_mntopts_len) >=
+ lme->lme_mntopts_len) {
+ /* expand option string */
+ uint_t tlen = lme->lme_mntopts_len + LXPR_MNT_OPT_CHUNK;
+ char *t = kmem_alloc(tlen, KM_SLEEP);
+
+ (void) strlcpy(t, lme->lme_mntopts, tlen);
+ kmem_free(lme->lme_mntopts, lme->lme_mntopts_len);
+ lme->lme_mntopts_len = tlen;
+ lme->lme_mntopts = t;
+ }
+}
+
+/*
+ * Perform the somewhat complicated work of getting the mount options string
+ * for the mount.
+ */
+static void
+lxpr_get_mntopts(vfs_t *vfsp, lxpr_mount_entry_t *lme)
+{
+ uint_t i;
+ mntopt_t *mop;
+ boolean_t have_nosuid = B_FALSE, have_nodev = B_FALSE;
+
+ lme->lme_mntopts_len = LXPR_MNT_OPT_CHUNK;
+ lme->lme_mntopts = kmem_alloc(lme->lme_mntopts_len, KM_SLEEP);
+ lme->lme_mntopts[0] = '\0';
+
+ /* Always show rw/ro option */
+ lxpr_append_mntopt(lme,
+ (lme->lme_flag & VFS_RDONLY) == 0 ? "rw" : "ro");
+
+ for (i = 0; i < vfsp->vfs_mntopts.mo_count; i++) {
+ mop = &vfsp->vfs_mntopts.mo_list[i];
+ if ((mop->mo_flags & MO_NODISPLAY) || !(mop->mo_flags & MO_SET))
+ continue;
+
+ if (strcmp(mop->mo_name, "ro") == 0 ||
+ strcmp(mop->mo_name, "rw") == 0)
+ continue;
+
+ if (strcmp(mop->mo_name, "nosuid") == 0)
+ have_nosuid = B_TRUE;
+ /* sigh, either option string is used */
+ if (strcmp(mop->mo_name, "nodev") == 0 ||
+ strcmp(mop->mo_name, "nodevices") == 0)
+ have_nodev = B_TRUE;
+
+ if (!lxpr_skip_mntopt(mop->mo_name)) {
+ lxpr_append_mntopt(lme, ",");
+ lxpr_append_mntopt(lme, mop->mo_name);
+ if (mop->mo_arg != NULL) {
+ lxpr_append_mntopt(lme, "=");
+ lxpr_append_mntopt(lme, mop->mo_arg);
+ }
+ }
+ }
+
+ /*
+ * Sometimes nosuid is an explicit string, other times it's a flag.
+ * The same is true for nodevices.
+ */
+ if (!have_nosuid && (lme->lme_flag & VFS_NOSETUID)) {
+ lxpr_append_mntopt(lme, ",nosuid");
+ }
+ if (!have_nodev && (lme->lme_flag & VFS_NODEVICES)) {
+ lxpr_append_mntopt(lme, ",nodevices");
+ }
+}
+
+static list_t *
+lxpr_enumerate_mounts(zone_t *zone)
+{
+ vfs_t *vfsp, *rvfsp, *vfslist;
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+ list_t *result;
+ lxpr_mount_entry_t *lme;
+ lx_virt_disk_t *vd;
+ uint_t root_id, mount_id;
+ char tmppath[MAXPATHLEN];
+
+ result = kmem_alloc(sizeof (list_t), KM_SLEEP);
+ list_create(result, sizeof (lxpr_mount_entry_t),
+ offsetof(lxpr_mount_entry_t, lme_link));
+ /* use an arbitrary start value for the root mount_id */
+ root_id = 15;
+ mount_id = root_id + 1;
+
+ ASSERT(zone != global_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ vfs_list_read_lock();
+ vfsp = vfslist = zone->zone_vfslist;
+
+ /*
+ * If the zone has a root entry, it will be the first in the list.
+ * Conjure one up if needed.
+ */
+ if (vfslist == NULL || strcmp(refstr_value(vfsp->vfs_mntpt),
+ zone->zone_rootpath) != 0) {
+ rvfsp = zone->zone_rootvp->v_vfsp;
+ } else {
+ rvfsp = vfslist;
+ vfsp = vfslist->vfs_zone_next;
+ }
+
+ lme = kmem_alloc(sizeof (lxpr_mount_entry_t), KM_SLEEP);
+ lme->lme_id = root_id;
+ lme->lme_parent_id = 0;
+ lme->lme_mntpt = refstr_alloc(zone->zone_rootpath);
+ lme->lme_flag = rvfsp->vfs_flag;
+ lme->lme_fstype = rvfsp->vfs_fstype;
+ lme->lme_force = B_TRUE;
+ lxpr_get_mntopts(rvfsp, lme);
+
+ lme->lme_resource = NULL;
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ if (vd->lxvd_type == LXVD_ZFS_DS &&
+ vd->lxvd_real_dev == rvfsp->vfs_dev) {
+ (void) snprintf(tmppath, sizeof (tmppath),
+ "%sdev/%s", zone->zone_rootpath, vd->lxvd_name);
+ lme->lme_resource = refstr_alloc(tmppath);
+ lme->lme_dev = vd->lxvd_emul_dev;
+ break;
+ }
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+ if (lme->lme_resource == NULL) {
+ lme->lme_resource = refstr_alloc(zone->zone_rootpath);
+ lme->lme_dev = rvfsp->vfs_dev;
+ }
+ list_insert_head(result, lme);
+
+ do {
+ if (vfsp == NULL) {
+ break;
+ }
+ /* Skip mounts we shouldn't show */
+ if ((vfsp->vfs_flag & VFS_NOMNTTAB) != 0) {
+ vfsp = vfsp->vfs_zone_next;
+ continue;
+ }
+
+ lme = kmem_alloc(sizeof (lxpr_mount_entry_t), KM_SLEEP);
+ lme->lme_id = mount_id++;
+ lme->lme_parent_id = root_id;
+ lme->lme_mntpt = vfsp->vfs_mntpt;
+ refstr_hold(vfsp->vfs_mntpt);
+ lme->lme_flag = vfsp->vfs_flag;
+ lme->lme_fstype = vfsp->vfs_fstype;
+ lme->lme_force = B_FALSE;
+ lxpr_get_mntopts(vfsp, lme);
+
+ lme->lme_resource = NULL;
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ if (vd->lxvd_type == LXVD_ZFS_DS &&
+ vd->lxvd_real_dev == vfsp->vfs_dev) {
+ char vdev[MAXPATHLEN];
+
+ (void) snprintf(vdev, sizeof (vdev),
+ "%sdev/%s",
+ zone->zone_rootpath, vd->lxvd_name);
+ lme->lme_resource = refstr_alloc(vdev);
+ lme->lme_dev = vd->lxvd_emul_dev;
+ break;
+ }
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+ if (lme->lme_resource == NULL) {
+ lme->lme_resource = vfsp->vfs_resource;
+ refstr_hold(vfsp->vfs_resource);
+ lme->lme_dev = vfsp->vfs_dev;
+ }
+ list_insert_tail(result, lme);
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+ /* Add a single dummy entry for /native/usr */
+ lme = kmem_alloc(sizeof (lxpr_mount_entry_t), KM_SLEEP);
+ lme->lme_id = mount_id++;
+ lme->lme_parent_id = root_id;
+ lme->lme_flag = VFS_RDONLY;
+ lme->lme_dev = makedevice(0, 1);
+ (void) snprintf(tmppath, sizeof (tmppath),
+ "%snative/usr", zone->zone_rootpath);
+ lme->lme_mntpt = refstr_alloc(tmppath);
+ lme->lme_resource = lme->lme_mntpt;
+ lme->lme_mntopts_len = 3;
+ lme->lme_mntopts = kmem_alloc(lme->lme_mntopts_len, KM_SLEEP);
+ (void) strlcpy(lme->lme_mntopts, "ro", lme->lme_mntopts_len);
+ refstr_hold(lme->lme_mntpt);
+ if (lxpr_zfs_fstype == -1) {
+ vfssw_t *zfssw = vfs_getvfssw("zfs");
+ VERIFY(zfssw != NULL);
+ lxpr_zfs_fstype = ((uintptr_t)zfssw - (uintptr_t)vfssw) /
+ sizeof (vfssw[0]);
+ VERIFY(&vfssw[lxpr_zfs_fstype] == zfssw);
+ }
+ lme->lme_fstype = lxpr_zfs_fstype;
+ lme->lme_force = B_TRUE;
+ list_insert_tail(result, lme);
+
+ return (result);
+}
+
+static uint_t
+lxpr_get_mountid(zone_t *zone, vfs_t *match_vfsp)
+{
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+ vfs_t *vfsp, *vfslist;
+ uint_t mount_id;
+
+ if (match_vfsp == NULL)
+ return (0);
+
+ /* Mount IDs start at 15 for the root, see lxpr_enumerate_mounts() */
+ mount_id = 15;
+
+ ASSERT(zone != global_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ if (zone->zone_rootvp->v_vfsp == match_vfsp)
+ return (mount_id);
+
+ vfs_list_read_lock();
+
+ vfsp = vfslist = zone->zone_vfslist;
+
+ do {
+ if (vfsp == zone->zone_rootvp->v_vfsp)
+ continue;
+
+ if (vfsp == NULL)
+ break;
+
+ /* Skip mounts we shouldn't show */
+ if ((vfsp->vfs_flag & VFS_NOMNTTAB) != 0) {
+ vfsp = vfsp->vfs_zone_next;
+ continue;
+ }
+
+ mount_id++;
+
+ if (vfsp == match_vfsp) {
+ vfs_list_unlock();
+ return (mount_id);
+ }
+
+ vfsp = vfsp->vfs_zone_next;
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+ return (0);
+}
+
+/*
+ * lxpr_read_pid_mountinfo(): information about process mount points.
+ */
+static void
+lxpr_read_pid_mountinfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ list_t *mounts;
+ lxpr_mount_entry_t *lme;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_MOUNTINFO ||
+ lxpnp->lxpr_type == LXPR_PID_TID_MOUNTINFO);
+
+ mounts = lxpr_enumerate_mounts(zone);
+
+ /*
+ * now we can run through what we've extracted without holding
+ * vfs_list_read_lock()
+ */
+ lme = (lxpr_mount_entry_t *)list_remove_head(mounts);
+ while (lme != NULL) {
+ char *resource, *mntpt, *fstype, *rwflag;
+ vnode_t *vp;
+ int error;
+
+ mntpt = (char *)refstr_value(lme->lme_mntpt);
+ resource = (char *)refstr_value(lme->lme_resource);
+
+ if (mntpt == NULL || mntpt[0] == '\0') {
+ goto nextp;
+ }
+ mntpt = ZONE_PATH_TRANSLATE(mntpt, zone);
+ error = lookupname(mntpt, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ goto nextp;
+ } else if ((vp->v_flag & VROOT) == 0 && !lme->lme_force) {
+ VN_RELE(vp);
+ goto nextp;
+ }
+ VN_RELE(vp);
+
+ if (resource != NULL && resource[0] != '\0') {
+ if (resource[0] == '/') {
+ resource = ZONE_PATH_VISIBLE(resource, zone) ?
+ ZONE_PATH_TRANSLATE(resource, zone) : mntpt;
+ }
+ } else {
+ resource = "none";
+ }
+
+ /* Make things look more like Linux. */
+ fstype = vfssw[lme->lme_fstype].vsw_name;
+ if (lxpr_clean_mntent(&mntpt, &fstype, &resource) != 0 &&
+ !lme->lme_force) {
+ goto nextp;
+ }
+ rwflag = ((lme->lme_flag & VFS_RDONLY) == 0) ? "rw" : "ro";
+
+ /*
+ * XXX parent ID is not tracked correctly here. Currently we
+ * always assume the parent ID is the root ID.
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "%d %d %d:%d / %s %s - %s %s %s\n",
+ lme->lme_id, lme->lme_parent_id,
+ getmajor(lme->lme_dev), getminor(lme->lme_dev),
+ mntpt, rwflag, fstype, resource, lme->lme_mntopts);
+
+nextp:
+ refstr_rele(lme->lme_mntpt);
+ refstr_rele(lme->lme_resource);
+ kmem_free(lme->lme_mntopts, lme->lme_mntopts_len);
+ kmem_free(lme, sizeof (lxpr_mount_entry_t));
+ lme = (lxpr_mount_entry_t *)list_remove_head(mounts);
+ }
+
+ list_destroy(mounts);
+ kmem_free(mounts, sizeof (list_t));
+}
+
+/*
+ * lxpr_read_pid_oom_scr_adj(): read oom_score_adj for process
+ */
+static void
+lxpr_read_pid_oom_scr_adj(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_OOM_SCR_ADJ ||
+ lxpnp->lxpr_type == LXPR_PID_TID_OOM_SCR_ADJ);
+
+ p = lxpr_lock(lxpnp, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+ lxpr_unlock(p);
+
+ /* always 0 */
+ lxpr_uiobuf_printf(uiobuf, "0\n");
+}
+
+/*
+ * lxpr_read_pid_personality(): read personality for process
+ */
+static void
+lxpr_read_pid_personality(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ lx_proc_data_t *lxpd;
+ unsigned int personality;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_PERSONALITY);
+
+ p = lxpr_lock(lxpnp, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+ if ((lxpd = ptolxproc(p)) != NULL) {
+ personality = lxpd->l_personality;
+ } else {
+ /* Report native processes as having the SunOS personality */
+ personality = LX_PER_SUNOS;
+ }
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf, "%08x\n", personality);
+}
+
+/*
+ * lxpr_read_pid_statm(): memory status file
+ */
+static void
+lxpr_read_pid_statm(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ struct as *as;
+ size_t vsize, rss;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STATM ||
+ lxpnp->lxpr_type == LXPR_PID_TID_STATM);
+
+ p = lxpr_lock(lxpnp, ZOMB_OK);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ as = p->p_as;
+ mutex_exit(&p->p_lock);
+ if (as != &kas) {
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = btopr(as->a_resvsize);
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+ } else {
+ vsize = 0;
+ rss = 0;
+ }
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%lu %lu %lu %lu %lu %lu %lu\n",
+ vsize, rss, 0l, rss, 0l, 0l, 0l);
+}
+
+/*
+ * Determine number of LWPs visible in the process. In particular we want to
+ * ignore aio in-kernel threads.
+ */
+static uint_t
+lxpr_count_tasks(proc_t *p)
+{
+ uint_t cnt = 0;
+ kthread_t *t;
+
+ if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+ (p->p_as == &kas)) {
+ return (0);
+ }
+
+ if (p->p_brand != &lx_brand || (t = p->p_tlist) == NULL) {
+ cnt = p->p_lwpcnt;
+ } else {
+ do {
+ lx_lwp_data_t *lwpd = ttolxlwp(t);
+ /* Don't count aio kernel worker threads */
+ if ((t->t_proc_flag & TP_KTHREAD) != 0 &&
+ lwpd != NULL &&
+ (lwpd->br_lwp_flags & BR_AIO_LWP) == 0) {
+ cnt++;
+ }
+
+ t = t->t_forw;
+ } while (t != p->p_tlist);
+ }
+
+ return (cnt);
+}
+
+/*
+ * pid/tid common code to read status file
+ */
+static void
+lxpr_read_status_common(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf,
+ uint_t lookup_id)
+{
+ proc_t *p;
+ kthread_t *t;
+ user_t *up;
+ cred_t *cr;
+ const gid_t *groups;
+ struct as *as;
+ char *status;
+ pid_t pid, ppid;
+ pid_t tid = (lookup_id == 0) ? lxpnp->lxpr_pid : lookup_id;
+ k_sigset_t current, ignore, handle;
+ int i, lx_sig, lwpcnt, ngroups;
+ char buf_comm[MAXCOMLEN + 1];
+ rlim64_t fdlim;
+ size_t vsize = 0, nlocked = 0, rss = 0, stksize = 0;
+ boolean_t printsz = B_FALSE;
+
+
+ p = lxpr_lock_pid(lxpnp, tid, ZOMB_OK, &t);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ /* Translate the pid (e.g. initpid to 1) */
+ lxpr_fixpid(LXPTOZ(lxpnp), p, &pid, &ppid);
+
+ if (t != NULL) {
+ thread_lock(t);
+ switch (t->t_state) {
+ case TS_SLEEP:
+ status = "S (sleeping)";
+ break;
+ case TS_RUN:
+ case TS_ONPROC:
+ status = "R (running)";
+ break;
+ case TS_ZOMB:
+ status = "Z (zombie)";
+ break;
+ case TS_STOPPED:
+ status = "T (stopped)";
+ break;
+ default:
+ status = "! (unknown)";
+ break;
+ }
+ thread_unlock(t);
+ } else {
+ if (lookup_id != 0) {
+ /* we can't find this specific thread */
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ lxpr_unlock(p);
+ return;
+ }
+
+ /*
+ * there is a hole in the exit code, where a proc can have
+ * no threads but it is yet to be flagged SZOMB. We will
+ * assume we are about to become a zombie
+ */
+ status = "Z (zombie)";
+ }
+
+ up = PTOU(p);
+ mutex_enter(&p->p_crlock);
+ crhold(cr = p->p_cred);
+ mutex_exit(&p->p_crlock);
+
+ (void) strlcpy(buf_comm, up->u_comm, sizeof (buf_comm));
+ fdlim = p->p_fno_ctl;
+ lwpcnt = lxpr_count_tasks(p);
+
+ /*
+ * Gather memory information
+ */
+ as = p->p_as;
+ if ((p->p_stat != SZOMB) && !(p->p_flag & (SSYS | SEXITING)) &&
+ (as != &kas)) {
+ mutex_exit(&p->p_lock);
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = as->a_resvsize;
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+
+ nlocked = p->p_locked_mem;
+ stksize = p->p_stksize;
+ printsz = B_TRUE;
+ }
+
+ /*
+ * Gather signal information
+ */
+ sigemptyset(&current);
+ sigemptyset(&ignore);
+ sigemptyset(&handle);
+ for (i = 1; i < NSIG; i++) {
+ lx_sig = stol_signo[i];
+
+ if ((lx_sig > 0) && (lx_sig <= LX_NSIG)) {
+ if (sigismember(&p->p_sig, i))
+ sigaddset(&current, lx_sig);
+
+ if (up->u_signal[i - 1] == SIG_IGN)
+ sigaddset(&ignore, lx_sig);
+ else if (up->u_signal[i - 1] != SIG_DFL)
+ sigaddset(&handle, lx_sig);
+ }
+ }
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "Name:\t%s\n"
+ "State:\t%s\n"
+ "Tgid:\t%d\n"
+ "Pid:\t%d\n"
+ "PPid:\t%d\n"
+ "TracerPid:\t%d\n"
+ "Uid:\t%u\t%u\t%u\t%u\n"
+ "Gid:\t%u\t%u\t%u\t%u\n"
+ "FDSize:\t%d\n"
+ "Groups:\t",
+ buf_comm,
+ status,
+ pid, /* thread group id - same as pid */
+ (lookup_id == 0) ? pid : lxpnp->lxpr_desc,
+ ppid,
+ 0,
+ crgetruid(cr), crgetuid(cr), crgetsuid(cr), crgetuid(cr),
+ crgetrgid(cr), crgetgid(cr), crgetsgid(cr), crgetgid(cr),
+ fdlim);
+ ngroups = crgetngroups(cr);
+ groups = crgetgroups(cr);
+ for (i = 0; i < ngroups; i++) {
+ lxpr_uiobuf_printf(uiobuf,
+ "%u ",
+ groups[i]);
+ }
+ crfree(cr);
+ if (printsz) {
+ lxpr_uiobuf_printf(uiobuf,
+ "\n"
+ "VmSize:\t%8lu kB\n"
+ "VmLck:\t%8lu kB\n"
+ "VmRSS:\t%8lu kB\n"
+ "VmData:\t%8lu kB\n"
+ "VmStk:\t%8lu kB\n"
+ "VmExe:\t%8lu kB\n"
+ "VmLib:\t%8lu kB",
+ btok(vsize),
+ btok(nlocked),
+ ptok(rss),
+ 0l,
+ btok(stksize),
+ ptok(rss),
+ 0l);
+ }
+ lxpr_uiobuf_printf(uiobuf, "\nThreads:\t%u\n", lwpcnt);
+ lxpr_uiobuf_printf(uiobuf,
+ "SigPnd:\t%08x%08x\n"
+ "SigBlk:\t%08x%08x\n"
+ "SigIgn:\t%08x%08x\n"
+ "SigCgt:\t%08x%08x\n",
+ current.__sigbits[1], current.__sigbits[0],
+ 0, 0, /* signals blocked on per thread basis */
+ ignore.__sigbits[1], ignore.__sigbits[0],
+ handle.__sigbits[1], handle.__sigbits[0]);
+ /* Report only the full bounding set for now */
+ lxpr_uiobuf_printf(uiobuf,
+ "CapInh:\t%016x\n"
+ "CapPrm:\t%016x\n"
+ "CapEff:\t%016x\n"
+ "CapBnd:\t%016llx\n",
+ 0, 0, 0, 0x1fffffffffLL);
+}
+
+/*
+ * lxpr_read_pid_tid_status(): status file
+ */
+static void
+lxpr_read_pid_tid_status(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STATUS ||
+ lxpnp->lxpr_type == LXPR_PID_TID_STATUS);
+
+ lxpr_read_status_common(lxpnp, uiobuf, lxpnp->lxpr_desc);
+}
+
+/*
+ * Same logic as the lx devfs lxd_pts_devt_translator.
+ */
+static dev_t
+lxpr_xlate_pts_dev(dev_t dev)
+{
+ minor_t min = getminor(dev);
+ int lx_maj, lx_min;
+
+ lx_maj = LX_PTS_MAJOR_MIN + (min / LX_MAXMIN);
+ lx_min = min % LX_MAXMIN;
+
+ return (LX_MAKEDEVICE(lx_maj, lx_min));
+}
+
+/*
+ * pid/tid common code to read stat file
+ */
+static void
+lxpr_read_pid_tid_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ kthread_t *t;
+ struct as *as;
+ zone_t *zone;
+ char stat;
+ pid_t pid, ppid, pgpid, spid, tid;
+ gid_t psgid;
+ dev_t psdev;
+ size_t rss, vsize;
+ int nice, pri, lwpcnt;
+ caddr_t wchan, stackbase;
+ processorid_t cpu;
+ clock_t utime, stime, cutime, cstime, ticks, boottime;
+ char buf_comm[MAXCOMLEN + 1];
+ rlim64_t vmem_ctl;
+ int exit_signal = -1;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STAT ||
+ lxpnp->lxpr_type == LXPR_PID_TID_STAT);
+
+ zone = LXPTOZ(lxpnp);
+ tid = (lxpnp->lxpr_desc == 0) ? lxpnp->lxpr_pid : lxpnp->lxpr_desc;
+ p = lxpr_lock_pid(lxpnp, tid, ZOMB_OK, &t);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ /* Set Linux defaults if we're the zone's init process */
+ pid = p->p_pid;
+ lxpr_fixpid(zone, p, &pid, &ppid);
+ if (pid == 1) {
+ /* init process */
+ pgpid = 0;
+ psgid = (gid_t)-1;
+ spid = 0;
+ psdev = 0;
+ } else {
+ pgpid = p->p_pgrp;
+ mutex_enter(&p->p_splock);
+ mutex_enter(&p->p_sessp->s_lock);
+ spid = p->p_sessp->s_sid;
+ psdev = lxpr_xlate_pts_dev(p->p_sessp->s_dev);
+ if (p->p_sessp->s_cred)
+ psgid = crgetgid(p->p_sessp->s_cred);
+ else
+ psgid = crgetgid(p->p_cred);
+
+ mutex_exit(&p->p_sessp->s_lock);
+ mutex_exit(&p->p_splock);
+ }
+
+ if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+ (p->p_as == &kas)) {
+ stackbase = 0;
+ } else {
+ /* from prgetstackbase() */
+ stackbase = p->p_usrstack - p->p_stksize;
+ }
+
+ utime = stime = 0;
+ if (t != NULL) {
+ klwp_t *lwp = ttolwp(t);
+ hrtime_t utm = 0, stm = 0;
+
+ /*
+ * For field 38 (the exit signal), some apps explicitly use
+ * this field in a check to distinguish processes from threads,
+ * and assume only processes have a valid signal in this field!
+ */
+ if (t->t_tid == 1) {
+ lx_proc_data_t *lxpd = ptolxproc(p);
+
+ if (lxpd != NULL) {
+ exit_signal = lxpd->l_signal;
+ } else {
+ exit_signal = SIGCHLD;
+ }
+ }
+
+ thread_lock(t);
+ switch (t->t_state) {
+ case TS_SLEEP:
+ stat = 'S';
+ break;
+ case TS_RUN:
+ case TS_ONPROC:
+ stat = 'R';
+ break;
+ case TS_ZOMB:
+ stat = 'Z';
+ break;
+ case TS_STOPPED:
+ stat = 'T';
+ break;
+ default:
+ stat = '!';
+ break;
+ }
+
+ if (CL_DONICE(t, NULL, 0, &nice) != 0)
+ nice = 0;
+
+ pri = t->t_pri;
+ wchan = t->t_wchan;
+ cpu = t->t_cpu->cpu_id;
+
+ if (lwp != NULL) {
+ struct mstate *ms = &lwp->lwp_mstate;
+
+ utm = ms->ms_acct[LMS_USER];
+ stm = ms->ms_acct[LMS_SYSTEM];
+
+ /* convert unscaled high-res time to nanoseconds */
+ scalehrtime(&utm);
+ scalehrtime(&stm);
+ }
+
+ thread_unlock(t);
+
+ /* Linux /proc expects these values in ticks */
+ utime = (clock_t)NSEC_TO_TICK(utm);
+ stime = (clock_t)NSEC_TO_TICK(stm);
+ } else {
+ /* Only zombies have no threads */
+ stat = 'Z';
+ nice = 0;
+ pri = 0;
+ wchan = 0;
+ cpu = 0;
+ }
+ as = p->p_as;
+ mutex_exit(&p->p_lock);
+ if (as != &kas) {
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = as->a_resvsize;
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+ } else {
+ vsize = 0;
+ rss = 0;
+ }
+ mutex_enter(&p->p_lock);
+
+ if (tid == p->p_pid) {
+ /* process */
+ utime = p->p_utime;
+ stime = p->p_stime;
+ } else {
+ /* tid: utime & stime for the thread set in block above */
+ /* EMPTY */
+ }
+ cutime = p->p_cutime;
+ cstime = p->p_cstime;
+ lwpcnt = lxpr_count_tasks(p);
+ vmem_ctl = p->p_vmem_ctl;
+ (void) strlcpy(buf_comm, p->p_user.u_comm, sizeof (buf_comm));
+ ticks = p->p_user.u_ticks; /* lbolt at process start */
+ /* adjust ticks to account for zone boot time */
+ boottime = zone->zone_zsched->p_user.u_ticks;
+ ticks -= boottime;
+ lxpr_unlock(p);
+
+ /* Adjust hz for relevant fields */
+ utime = HZ_TO_LX_USERHZ(utime);
+ stime = HZ_TO_LX_USERHZ(stime);
+ cutime = HZ_TO_LX_USERHZ(cutime);
+ cstime = HZ_TO_LX_USERHZ(cstime);
+ ticks = HZ_TO_LX_USERHZ(ticks);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%d " /* 1 */
+ "(%s) %c %d %d %d %d %d " /* 2-8 */
+ "%lu %lu %lu %lu %lu " /* 9-13 */
+ "%lu %lu %ld %ld " /* 14-17 */
+ "%d %d %d " /* 18-20 */
+ "%lu " /* 21 */
+ "%lu " /* 22 */
+ "%lu %ld %llu " /* 23-25 */
+ "%lu %lu %llu " /* 26-28 */
+ "%lu %lu " /* 29-30 */
+ "%lu %lu %lu %lu " /* 31-34 */
+ "%lu " /* 35 */
+ "%lu %lu " /* 36-37 */
+ "%d " /* 38 */
+ "%d" /* 39 */
+ "\n",
+ tid, /* 1 */
+ buf_comm, stat, ppid, pgpid, spid, psdev, psgid, /* 2-8 */
+ 0l, 0l, 0l, 0l, 0l, /* flags, minflt, cminflt, majflt, cmajflt */
+ utime, stime, cutime, cstime, /* 14-17 */
+ pri, nice, lwpcnt, /* 18-20 */
+ 0l, /* itrealvalue (time before next SIGALRM) 21 */
+ ticks, /* 22 */
+ vsize, rss, vmem_ctl, /* 23-25 */
+ 0l, 0l, stackbase, /* startcode, endcode, startstack 26-28 */
+ 0l, 0l, /* kstkesp, kstkeip 29-30 */
+ 0l, 0l, 0l, 0l, /* signal, blocked, sigignore, sigcatch 31-34 */
+ wchan, /* 35 */
+ 0l, 0l, /* nswap,cnswap 36-37 */
+ exit_signal, /* exit_signal 38 */
+ cpu /* 39 */);
+}
+
+static void
+lxpr_read_net_arp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+struct lxpr_ifstat {
+ uint64_t rx_bytes;
+ uint64_t rx_packets;
+ uint64_t rx_errors;
+ uint64_t rx_drop;
+ uint64_t tx_bytes;
+ uint64_t tx_packets;
+ uint64_t tx_errors;
+ uint64_t tx_drop;
+ uint64_t collisions;
+ uint64_t rx_multicast;
+};
+
+static void *
+lxpr_kstat_read(kstat_t *kn, boolean_t byname, size_t *size, int *num,
+ zoneid_t zoneid)
+{
+ kstat_t *kp;
+ int i, nrec = 0;
+ size_t bufsize;
+ void *buf = NULL;
+
+ if (byname == B_TRUE) {
+ kp = kstat_hold_byname(kn->ks_module, kn->ks_instance,
+ kn->ks_name, zoneid);
+ } else {
+ kp = kstat_hold_bykid(kn->ks_kid, zoneid);
+ }
+ if (kp == NULL) {
+ return (NULL);
+ }
+ if (kp->ks_flags & KSTAT_FLAG_INVALID) {
+ kstat_rele(kp);
+ return (NULL);
+ }
+
+ bufsize = kp->ks_data_size + 1;
+ kstat_rele(kp);
+
+ /*
+ * The kstat in question is released so that kmem_alloc(KM_SLEEP) is
+ * performed without it held. After the alloc, the kstat is reacquired
+ * and its size is checked again. If the buffer is no longer large
+ * enough, the alloc and check are repeated up to three times.
+ */
+ for (i = 0; i < 2; i++) {
+ buf = kmem_alloc(bufsize, KM_SLEEP);
+
+ /* Check if bufsize still appropriate */
+ if (byname == B_TRUE) {
+ kp = kstat_hold_byname(kn->ks_module, kn->ks_instance,
+ kn->ks_name, zoneid);
+ } else {
+ kp = kstat_hold_bykid(kn->ks_kid, zoneid);
+ }
+ if (kp == NULL || kp->ks_flags & KSTAT_FLAG_INVALID) {
+ if (kp != NULL) {
+ kstat_rele(kp);
+ }
+ kmem_free(buf, bufsize);
+ return (NULL);
+ }
+ KSTAT_ENTER(kp);
+ (void) KSTAT_UPDATE(kp, KSTAT_READ);
+ if (bufsize < kp->ks_data_size) {
+ kmem_free(buf, bufsize);
+ buf = NULL;
+ bufsize = kp->ks_data_size + 1;
+ KSTAT_EXIT(kp);
+ kstat_rele(kp);
+ continue;
+ } else {
+ if (KSTAT_SNAPSHOT(kp, buf, KSTAT_READ) != 0) {
+ kmem_free(buf, bufsize);
+ buf = NULL;
+ }
+ nrec = kp->ks_ndata;
+ KSTAT_EXIT(kp);
+ kstat_rele(kp);
+ break;
+ }
+ }
+
+ if (buf != NULL) {
+ *size = bufsize;
+ *num = nrec;
+ }
+ return (buf);
+}
+
+static int
+lxpr_kstat_ifstat(kstat_t *kn, struct lxpr_ifstat *ifs, zoneid_t zoneid)
+{
+ kstat_named_t *kp;
+ int i, num;
+ size_t size;
+
+ /*
+ * Search by name instead of by kid since there's a small window to
+ * race against kstats being added/removed.
+ */
+ bzero(ifs, sizeof (*ifs));
+ kp = (kstat_named_t *)lxpr_kstat_read(kn, B_TRUE, &size, &num, zoneid);
+ if (kp == NULL)
+ return (-1);
+ for (i = 0; i < num; i++) {
+ if (strncmp(kp[i].name, "rbytes64", KSTAT_STRLEN) == 0)
+ ifs->rx_bytes = kp[i].value.ui64;
+ else if (strncmp(kp[i].name, "ipackets64", KSTAT_STRLEN) == 0)
+ ifs->rx_packets = kp[i].value.ui64;
+ else if (strncmp(kp[i].name, "ierrors", KSTAT_STRLEN) == 0)
+ ifs->rx_errors = kp[i].value.ui32;
+ else if (strncmp(kp[i].name, "norcvbuf", KSTAT_STRLEN) == 0)
+ ifs->rx_drop = kp[i].value.ui32;
+ else if (strncmp(kp[i].name, "multircv", KSTAT_STRLEN) == 0)
+ ifs->rx_multicast = kp[i].value.ui32;
+ else if (strncmp(kp[i].name, "obytes64", KSTAT_STRLEN) == 0)
+ ifs->tx_bytes = kp[i].value.ui64;
+ else if (strncmp(kp[i].name, "opackets64", KSTAT_STRLEN) == 0)
+ ifs->tx_packets = kp[i].value.ui64;
+ else if (strncmp(kp[i].name, "oerrors", KSTAT_STRLEN) == 0)
+ ifs->tx_errors = kp[i].value.ui32;
+ else if (strncmp(kp[i].name, "noxmtbuf", KSTAT_STRLEN) == 0)
+ ifs->tx_drop = kp[i].value.ui32;
+ else if (strncmp(kp[i].name, "collisions", KSTAT_STRLEN) == 0)
+ ifs->collisions = kp[i].value.ui32;
+ }
+ kmem_free(kp, size);
+ return (0);
+}
+
+static void
+lxpr_read_net_dev(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ kstat_t *ksr;
+ kstat_t ks0;
+ int i, nidx;
+ size_t sidx;
+ struct lxpr_ifstat ifs;
+ zoneid_t zoneid = LXPTOZ(lxpnp)->zone_id;
+
+ lxpr_uiobuf_printf(uiobuf, "Inter-| Receive "
+ " | Transmit\n");
+ lxpr_uiobuf_printf(uiobuf, " face |bytes packets errs drop fifo"
+ " frame compressed multicast|bytes packets errs drop fifo"
+ " colls carrier compressed\n");
+
+ ks0.ks_kid = 0;
+ ksr = (kstat_t *)lxpr_kstat_read(&ks0, B_FALSE, &sidx, &nidx, zoneid);
+ if (ksr == NULL)
+ return;
+
+ for (i = 1; i < nidx; i++) {
+ if (strncmp(ksr[i].ks_module, "link", KSTAT_STRLEN) == 0 ||
+ strncmp(ksr[i].ks_module, "lo", KSTAT_STRLEN) == 0) {
+ if (lxpr_kstat_ifstat(&ksr[i], &ifs, zoneid) != 0)
+ continue;
+
+ /* Overwriting the name is ok in the local snapshot */
+ lx_ifname_convert(ksr[i].ks_name, LX_IF_FROMNATIVE);
+ lxpr_uiobuf_printf(uiobuf, "%6s: %7llu %7llu %4lu "
+ "%4lu %4u %5u %10u %9lu %8llu %7llu %4lu %4lu %4u "
+ "%5lu %7u %10u\n",
+ ksr[i].ks_name,
+ ifs.rx_bytes, ifs.rx_packets,
+ ifs.rx_errors, ifs.rx_drop,
+ 0, 0, 0, ifs.rx_multicast,
+ ifs.tx_bytes, ifs.tx_packets,
+ ifs.tx_errors, ifs.tx_drop,
+ 0, ifs.collisions, 0, 0);
+ }
+ }
+
+ kmem_free(ksr, sidx);
+}
+
+static void
+lxpr_read_net_dev_mcast(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_inet6_out(const in6_addr_t *addr, char buf[33])
+{
+ const uint8_t *ip = addr->s6_addr;
+ char digits[] = "0123456789abcdef";
+ int i;
+ for (i = 0; i < 16; i++) {
+ buf[2 * i] = digits[ip[i] >> 4];
+ buf[2 * i + 1] = digits[ip[i] & 0xf];
+ }
+ buf[32] = '\0';
+}
+
+static void
+lxpr_read_net_if_inet6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ ill_t *ill;
+ ipif_t *ipif;
+ ill_walk_context_t ctx;
+ char ifname[LIFNAMSIZ], ip6out[33];
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL)
+ return;
+ ipst = ns->netstack_ip;
+
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+ ill = ILL_START_WALK_V6(&ctx, ipst);
+
+ for (; ill != NULL; ill = ill_next(&ctx, ill)) {
+ for (ipif = ill->ill_ipif; ipif != NULL;
+ ipif = ipif->ipif_next) {
+ uint_t index = ill->ill_phyint->phyint_ifindex;
+ int plen = ip_mask_to_plen_v6(&ipif->ipif_v6net_mask);
+ unsigned int scope = lx_ipv6_scope_convert(
+ &ipif->ipif_v6lcl_addr);
+ /* Always report PERMANENT flag */
+ int flag = 0x80;
+
+ (void) snprintf(ifname, LIFNAMSIZ, "%s", ill->ill_name);
+ lx_ifname_convert(ifname, LX_IF_FROMNATIVE);
+ lxpr_inet6_out(&ipif->ipif_v6lcl_addr, ip6out);
+
+ lxpr_uiobuf_printf(uiobuf, "%32s %02x %02x %02x %02x"
+ " %8s\n", ip6out, index, plen, scope, flag, ifname);
+ }
+ }
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+}
+
+static void
+lxpr_read_net_igmp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_read_net_ip_mr_cache(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_read_net_ip_mr_vif(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_format_route_ipv6(ire_t *ire, lxpr_uiobuf_t *uiobuf)
+{
+ uint32_t flags;
+ char name[IFNAMSIZ];
+ char ipv6addr[33];
+
+ lxpr_inet6_out(&ire->ire_addr_v6, ipv6addr);
+ lxpr_uiobuf_printf(uiobuf, "%s %02x ", ipv6addr,
+ ip_mask_to_plen_v6(&ire->ire_mask_v6));
+
+ /* punt on this for now */
+ lxpr_uiobuf_printf(uiobuf, "%s %02x ",
+ "00000000000000000000000000000000", 0);
+
+ lxpr_inet6_out(&ire->ire_gateway_addr_v6, ipv6addr);
+ lxpr_uiobuf_printf(uiobuf, "%s", ipv6addr);
+
+ flags = ire->ire_flags &
+ (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DYNAMIC|RTF_MODIFIED);
+ /* Linux's RTF_LOCAL equivalent */
+ if (ire->ire_metrics.iulp_local)
+ flags |= 0x80000000;
+
+ if (ire->ire_ill != NULL) {
+ ill_get_name(ire->ire_ill, name, sizeof (name));
+ lx_ifname_convert(name, LX_IF_FROMNATIVE);
+ } else {
+ name[0] = '\0';
+ }
+
+ lxpr_uiobuf_printf(uiobuf, " %08x %08x %08x %08x %8s\n",
+ 0, /* metric */
+ ire->ire_refcnt,
+ 0,
+ flags,
+ name);
+}
+
+static void
+lxpr_read_net_ipv6_route(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ ip_stack_t *ipst;
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL)
+ return;
+ ipst = ns->netstack_ip;
+
+ /*
+ * LX branded zones are expected to have exclusive IP stack, hence
+ * using ALL_ZONES as the zoneid filter.
+ */
+ ire_walk_v6(&lxpr_format_route_ipv6, uiobuf, ALL_ZONES, ipst);
+
+ netstack_rele(ns);
+}
+
+static void
+lxpr_read_net_mcfilter(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_read_net_netstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_read_net_raw(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+#define LXPR_SKIP_ROUTE(type) \
+ (((IRE_IF_CLONE | IRE_BROADCAST | IRE_MULTICAST | \
+ IRE_NOROUTE | IRE_LOOPBACK | IRE_LOCAL) & type) != 0)
+
+static void
+lxpr_format_route_ipv4(ire_t *ire, lxpr_uiobuf_t *uiobuf)
+{
+ uint32_t flags;
+ char name[IFNAMSIZ];
+ ill_t *ill;
+ ire_t *nire;
+ ipif_t *ipif;
+ ipaddr_t gateway;
+
+ if (LXPR_SKIP_ROUTE(ire->ire_type) || ire->ire_testhidden != 0)
+ return;
+
+ /* These route flags have direct Linux equivalents */
+ flags = ire->ire_flags &
+ (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DYNAMIC|RTF_MODIFIED);
+
+ /*
+ * Search for a suitable IRE for naming purposes.
+ * On Linux, the default route is typically associated with the
+ * interface used to access gateway. The default IRE on illumos
+ * typically lacks an ill reference but its parent might have one.
+ */
+ nire = ire;
+ do {
+ ill = nire->ire_ill;
+ nire = nire->ire_dep_parent;
+ } while (ill == NULL && nire != NULL);
+ if (ill != NULL) {
+ ill_get_name(ill, name, sizeof (name));
+ lx_ifname_convert(name, LX_IF_FROMNATIVE);
+ } else {
+ name[0] = '*';
+ name[1] = '\0';
+ }
+
+ /*
+ * Linux suppresses the gateway address for directly connected
+ * interface networks. To emulate this behavior, we walk all addresses
+ * of a given route interface. If one matches the gateway, it is
+ * displayed as NULL.
+ */
+ gateway = ire->ire_gateway_addr;
+ if ((ill = ire->ire_ill) != NULL) {
+ for (ipif = ill->ill_ipif; ipif != NULL;
+ ipif = ipif->ipif_next) {
+ if (ipif->ipif_lcl_addr == gateway) {
+ gateway = 0;
+ break;
+ }
+ }
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "%s\t%08X\t%08X\t%04X\t%d\t%u\t"
+ "%d\t%08X\t%d\t%u\t%u\n",
+ name,
+ ire->ire_addr,
+ gateway,
+ flags, 0, 0,
+ 0, /* priority */
+ ire->ire_mask,
+ 0, 0, /* mss, window */
+ ire->ire_metrics.iulp_rtt);
+}
+
+static void
+lxpr_read_net_route(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ ip_stack_t *ipst;
+
+ lxpr_uiobuf_printf(uiobuf, "Iface\tDestination\tGateway \tFlags\t"
+ "RefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\n");
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL)
+ return;
+ ipst = ns->netstack_ip;
+
+ /*
+ * LX branded zones are expected to have exclusive IP stack, hence
+ * using ALL_ZONES as the zoneid filter.
+ */
+ ire_walk_v4(&lxpr_format_route_ipv4, uiobuf, ALL_ZONES, ipst);
+
+ netstack_rele(ns);
+}
+
+static void
+lxpr_read_net_rpc(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_read_net_rt_cache(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_read_net_sockstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+typedef struct lxpr_snmp_table {
+ const char *lst_proto;
+ const char **lst_fields;
+} lxpr_snmp_table_t;
+
+static const char *lxpr_snmp_ip_fields[] = {
+ "forwarding", "defaultTTL", "inReceives", "inHdrErrors",
+ "inAddrErrors", "forwDatagrams", "inUnknownProtos", "inDiscards",
+ "inDelivers", "outRequests", "outDiscards", "outNoRoutes",
+ "reasmTimeout", "reasmReqds", "reasmOKs", "reasmFails", "fragOKs",
+ "fragFails", "fragCreates",
+ NULL
+};
+
+static const char *lxpr_snmp_icmp_fields[] = {
+ "inMsgs", "inErrors", "inCsumErrors", "inDestUnreachs", "inTimeExcds",
+ "inParmProbs", "inSrcQuenchs", "inRedirects", "inEchos", "inEchoReps",
+ "inTimestamps", "inTimestampReps", "inAddrMasks", "inAddrMaskReps",
+ "outMsgs", "outErrors", "outDestUnreachs", "outTimeExcds",
+ "outParmProbs", "outSrcQuenchs", "outRedirects", "outEchos",
+ "outEchoReps", "outTimestamps", "outTimestampReps", "outAddrMasks",
+ "outAddrMaskReps",
+ NULL
+};
+
+static const char *lxpr_snmp_tcp_fields[] = {
+ "rtoAlgorithm", "rtoMin", "rtoMax", "maxConn", "activeOpens",
+ "passiveOpens", "attemptFails", "estabResets", "currEstab", "inSegs",
+ "outSegs", "retransSegs", "inErrs", "outRsts", "inCsumErrors",
+ NULL
+};
+
+static const char *lxpr_snmp_udp_fields[] = {
+ "inDatagrams", "noPorts", "inErrors", "outDatagrams", "rcvbufErrors",
+ "sndbufErrors", "inCsumErrors",
+ NULL
+};
+
+static lxpr_snmp_table_t lxpr_snmp_ip = { "ip", lxpr_snmp_ip_fields };
+static lxpr_snmp_table_t lxpr_snmp_icmp = { "icmp", lxpr_snmp_icmp_fields };
+static lxpr_snmp_table_t lxpr_snmp_tcp = { "tcp", lxpr_snmp_tcp_fields };
+static lxpr_snmp_table_t lxpr_snmp_udp = { "udp", lxpr_snmp_udp_fields };
+
+static lxpr_snmp_table_t *lxpr_net_snmptab[] = {
+ &lxpr_snmp_ip,
+ &lxpr_snmp_icmp,
+ &lxpr_snmp_tcp,
+ &lxpr_snmp_udp,
+ NULL
+};
+
+static void
+lxpr_kstat_print_tab(lxpr_uiobuf_t *uiobuf, lxpr_snmp_table_t *table,
+ kstat_t *kn, zoneid_t zoneid)
+{
+ kstat_named_t *klist;
+ char upname[KSTAT_STRLEN], upfield[KSTAT_STRLEN];
+ int i, j, num;
+ size_t size;
+
+ klist = (kstat_named_t *)lxpr_kstat_read(kn, B_TRUE, &size, &num,
+ zoneid);
+ if (klist == NULL)
+ return;
+
+ /* Print the header line, fields capitalized */
+ (void) strncpy(upname, table->lst_proto, KSTAT_STRLEN);
+ upname[0] = toupper(upname[0]);
+ lxpr_uiobuf_printf(uiobuf, "%s:", upname);
+ for (i = 0; table->lst_fields[i] != NULL; i++) {
+ (void) strncpy(upfield, table->lst_fields[i], KSTAT_STRLEN);
+ upfield[0] = toupper(upfield[0]);
+ lxpr_uiobuf_printf(uiobuf, " %s", upfield);
+ }
+ lxpr_uiobuf_printf(uiobuf, "\n%s:", upname);
+
+ /* Then loop back through to print the value line. */
+ for (i = 0; table->lst_fields[i] != NULL; i++) {
+ kstat_named_t *kpoint = NULL;
+ for (j = 0; j < num; j++) {
+ if (strncmp(klist[j].name, table->lst_fields[i],
+ KSTAT_STRLEN) == 0) {
+ kpoint = &klist[j];
+ break;
+ }
+ }
+ if (kpoint == NULL) {
+ /* Output 0 for unknown fields */
+ lxpr_uiobuf_printf(uiobuf, " 0");
+ } else {
+ switch (kpoint->data_type) {
+ case KSTAT_DATA_INT32:
+ lxpr_uiobuf_printf(uiobuf, " %d",
+ kpoint->value.i32);
+ break;
+ case KSTAT_DATA_UINT32:
+ lxpr_uiobuf_printf(uiobuf, " %u",
+ kpoint->value.ui32);
+ break;
+ case KSTAT_DATA_INT64:
+ lxpr_uiobuf_printf(uiobuf, " %ld",
+ kpoint->value.l);
+ break;
+ case KSTAT_DATA_UINT64:
+ lxpr_uiobuf_printf(uiobuf, " %lu",
+ kpoint->value.ul);
+ break;
+ }
+ }
+ }
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ kmem_free(klist, size);
+}
+
+static void
+lxpr_read_net_snmp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ kstat_t *ksr;
+ kstat_t ks0;
+ lxpr_snmp_table_t **table = lxpr_net_snmptab;
+ int i, t, nidx;
+ size_t sidx;
+ zoneid_t zoneid = LXPTOZ(lxpnp)->zone_id;
+
+ ks0.ks_kid = 0;
+ ksr = (kstat_t *)lxpr_kstat_read(&ks0, B_FALSE, &sidx, &nidx, zoneid);
+ if (ksr == NULL)
+ return;
+
+ for (t = 0; table[t] != NULL; t++) {
+ for (i = 0; i < nidx; i++) {
+ if (strncmp(ksr[i].ks_class, "mib2", KSTAT_STRLEN) != 0)
+ continue;
+ if (strncmp(ksr[i].ks_name, table[t]->lst_proto,
+ KSTAT_STRLEN) == 0) {
+ lxpr_kstat_print_tab(uiobuf, table[t], &ksr[i],
+ zoneid);
+ break;
+ }
+ }
+ }
+ kmem_free(ksr, sidx);
+}
+
+static void
+lxpr_read_net_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static int
+lxpr_convert_tcp_state(int st)
+{
+ /*
+ * Derived from the enum located in the Linux kernel sources:
+ * include/net/tcp_states.h
+ */
+ switch (st) {
+ case TCPS_ESTABLISHED:
+ return (1);
+ case TCPS_SYN_SENT:
+ return (2);
+ case TCPS_SYN_RCVD:
+ return (3);
+ case TCPS_FIN_WAIT_1:
+ return (4);
+ case TCPS_FIN_WAIT_2:
+ return (5);
+ case TCPS_TIME_WAIT:
+ return (6);
+ case TCPS_CLOSED:
+ return (7);
+ case TCPS_CLOSE_WAIT:
+ return (8);
+ case TCPS_LAST_ACK:
+ return (9);
+ case TCPS_LISTEN:
+ return (10);
+ case TCPS_CLOSING:
+ return (11);
+ default:
+ /* No translation for TCPS_IDLE, TCPS_BOUND or anything else */
+ return (0);
+ }
+}
+
+static void
+lxpr_format_tcp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf, ushort_t ipver)
+{
+ int i, sl = 0;
+ connf_t *connfp;
+ conn_t *connp;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ int sonode_shift;
+
+ ASSERT(ipver == IPV4_VERSION || ipver == IPV6_VERSION);
+ if (ipver == IPV4_VERSION) {
+ lxpr_uiobuf_printf(uiobuf, " sl local_address rem_address "
+ "st tx_queue rx_queue tr tm->when retrnsmt uid timeout "
+ "inode\n");
+ } else {
+ lxpr_uiobuf_printf(uiobuf, " sl "
+ "local_address "
+ "remote_address "
+ "st tx_queue rx_queue tr tm->when retrnsmt "
+ "uid timeout inode\n");
+ }
+ /*
+ * Due to differences between the Linux and illumos TCP
+ * implementations, some data will be omitted from the output here.
+ *
+ * Valid fields:
+ * - local_address
+ * - remote_address
+ * - st
+ * - tx_queue
+ * - rx_queue
+ * - uid
+ * - inode
+ *
+ * Omitted/invalid fields
+ * - tr
+ * - tm->when
+ * - retrnsmt
+ * - timeout
+ */
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL)
+ return;
+ ipst = ns->netstack_ip;
+
+ sonode_shift = highbit(sizeof (sonode_t));
+
+ for (i = 0; i < CONN_G_HASH_SIZE; i++) {
+ connfp = &ipst->ips_ipcl_globalhash_fanout[i];
+ connp = NULL;
+ while ((connp =
+ ipcl_get_next_conn(connfp, connp, IPCL_TCPCONN)) != NULL) {
+ tcp_t *tcp;
+ ino_t inode;
+ sonode_t *so = (sonode_t *)connp->conn_upper_handle;
+ if (connp->conn_ipversion != ipver)
+ continue;
+ tcp = connp->conn_tcp;
+ if (ipver == IPV4_VERSION) {
+ lxpr_uiobuf_printf(uiobuf,
+ "%4d: %08X:%04X %08X:%04X ",
+ ++sl,
+ connp->conn_laddr_v4,
+ ntohs(connp->conn_lport),
+ connp->conn_faddr_v4,
+ ntohs(connp->conn_fport));
+ } else {
+ lxpr_uiobuf_printf(uiobuf, "%4d: "
+ "%08X%08X%08X%08X:%04X "
+ "%08X%08X%08X%08X:%04X ",
+ ++sl,
+ connp->conn_laddr_v6.s6_addr32[0],
+ connp->conn_laddr_v6.s6_addr32[1],
+ connp->conn_laddr_v6.s6_addr32[2],
+ connp->conn_laddr_v6.s6_addr32[3],
+ ntohs(connp->conn_lport),
+ connp->conn_faddr_v6.s6_addr32[0],
+ connp->conn_faddr_v6.s6_addr32[1],
+ connp->conn_faddr_v6.s6_addr32[2],
+ connp->conn_faddr_v6.s6_addr32[3],
+ ntohs(connp->conn_fport));
+ }
+
+ /*
+ * We cannot use VOP_GETATTR here to fetch the
+ * simulated inode for the socket via the
+ * so->so_vnode. This is because there is a (very
+ * tight) race for when the v_vfsp is set on the
+ * sonode's vnode. However, all we really want here is
+ * the inode number, which we can compute using the
+ * same algorithm as socket_vop_getattr.
+ */
+ inode = ((ino_t)so >> sonode_shift) & 0xFFFF;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%02X %08X:%08X %02X:%08X %08X "
+ "%5u %8d %lu %d %p %u %u %u %u %d\n",
+ lxpr_convert_tcp_state(tcp->tcp_state),
+ tcp->tcp_rcv_cnt, tcp->tcp_unsent, /* rx/tx queue */
+ 0, 0, /* tr, when */
+ 0, /* per-connection rexmits aren't tracked today */
+ connp->conn_cred->cr_uid,
+ 0, /* timeout */
+ /* inode + more */
+ inode, 0, NULL, 0, 0, 0, 0, 0);
+ }
+ }
+ netstack_rele(ns);
+}
+
+static void
+lxpr_read_net_tcp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_format_tcp(lxpnp, uiobuf, IPV4_VERSION);
+}
+
+static void
+lxpr_read_net_tcp6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_format_tcp(lxpnp, uiobuf, IPV6_VERSION);
+}
+
+static void
+lxpr_format_udp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf, ushort_t ipver)
+{
+ int i, sl = 0;
+ connf_t *connfp;
+ conn_t *connp;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ int sonode_shift;
+
+ ASSERT(ipver == IPV4_VERSION || ipver == IPV6_VERSION);
+ if (ipver == IPV4_VERSION) {
+ lxpr_uiobuf_printf(uiobuf, " sl local_address rem_address"
+ " st tx_queue rx_queue tr tm->when retrnsmt uid"
+ " timeout inode ref pointer drops\n");
+ } else {
+ lxpr_uiobuf_printf(uiobuf, " sl "
+ "local_address "
+ "remote_address "
+ "st tx_queue rx_queue tr tm->when retrnsmt "
+ "uid timeout inode ref pointer drops\n");
+ }
+ /*
+ * Due to differences between the Linux and illumos UDP
+ * implementations, some data will be omitted from the output here.
+ *
+ * Valid fields:
+ * - local_address
+ * - remote_address
+ * - st: limited
+ * - uid
+ *
+ * Omitted/invalid fields
+ * - tx_queue
+ * - rx_queue
+ * - tr
+ * - tm->when
+ * - retrnsmt
+ * - timeout
+ * - inode
+ */
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL)
+ return;
+ ipst = ns->netstack_ip;
+
+ sonode_shift = highbit(sizeof (sonode_t));
+
+ for (i = 0; i < CONN_G_HASH_SIZE; i++) {
+ connfp = &ipst->ips_ipcl_globalhash_fanout[i];
+ connp = NULL;
+ while ((connp =
+ ipcl_get_next_conn(connfp, connp, IPCL_UDPCONN)) != NULL) {
+ udp_t *udp;
+ ino_t inode;
+ int state = 0;
+ sonode_t *so = (sonode_t *)connp->conn_upper_handle;
+ if (connp->conn_ipversion != ipver)
+ continue;
+ udp = connp->conn_udp;
+ if (ipver == IPV4_VERSION) {
+ lxpr_uiobuf_printf(uiobuf,
+ "%4d: %08X:%04X %08X:%04X ",
+ ++sl,
+ connp->conn_laddr_v4,
+ ntohs(connp->conn_lport),
+ connp->conn_faddr_v4,
+ ntohs(connp->conn_fport));
+ } else {
+ lxpr_uiobuf_printf(uiobuf, "%4d: "
+ "%08X%08X%08X%08X:%04X "
+ "%08X%08X%08X%08X:%04X ",
+ ++sl,
+ connp->conn_laddr_v6.s6_addr32[0],
+ connp->conn_laddr_v6.s6_addr32[1],
+ connp->conn_laddr_v6.s6_addr32[2],
+ connp->conn_laddr_v6.s6_addr32[3],
+ ntohs(connp->conn_lport),
+ connp->conn_faddr_v6.s6_addr32[0],
+ connp->conn_faddr_v6.s6_addr32[1],
+ connp->conn_faddr_v6.s6_addr32[2],
+ connp->conn_faddr_v6.s6_addr32[3],
+ ntohs(connp->conn_fport));
+ }
+
+ switch (udp->udp_state) {
+ case TS_UNBND:
+ case TS_IDLE:
+ state = 7;
+ break;
+ case TS_DATA_XFER:
+ state = 1;
+ break;
+ }
+
+ /*
+ * We cannot use VOP_GETATTR here to fetch the
+ * simulated inode for the socket via the
+ * so->so_vnode. This is because there is a (very
+ * tight) race for when the v_vfsp is set on the
+ * sonode's vnode. However, all we really want here is
+ * the inode number, which we can compute using the
+ * same algorithm as socket_vop_getattr.
+ */
+ inode = ((ino_t)so >> sonode_shift) & 0xFFFF;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%02X %08X:%08X %02X:%08X %08X "
+ "%5u %8d %lu %d %p %d\n",
+ state,
+ 0, 0, /* rx/tx queue */
+ 0, 0, /* tr, when */
+ 0, /* retrans */
+ connp->conn_cred->cr_uid,
+ 0, /* timeout */
+ /* inode, ref, pointer, drops */
+ inode, 0, NULL, 0);
+ }
+ }
+ netstack_rele(ns);
+}
+
+static void
+lxpr_read_net_udp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_format_udp(lxpnp, uiobuf, IPV4_VERSION);
+}
+
+static void
+lxpr_read_net_udp6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_format_udp(lxpnp, uiobuf, IPV6_VERSION);
+}
+
+static void
+lxpr_read_net_unix(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ sonode_t *so;
+ zoneid_t zoneid = LXPTOZ(lxpnp)->zone_id;
+
+ lxpr_uiobuf_printf(uiobuf, "Num RefCount Protocol Flags Type "
+ "St Inode Path\n");
+
+ mutex_enter(&socklist.sl_lock);
+ for (so = socklist.sl_list; so != NULL;
+ so = _SOTOTPI(so)->sti_next_so) {
+ vnode_t *vp = so->so_vnode;
+ vattr_t attr;
+ sotpi_info_t *sti;
+ const char *name = NULL;
+ int status = 0;
+ int type = 0;
+ int flags = 0;
+
+ /* Only process active sonodes in this zone */
+ if (so->so_count == 0 || so->so_zoneid != zoneid)
+ continue;
+
+ /*
+ * Grab the inode, if possible.
+ * This must be done before entering so_lock.
+ */
+ if (vp == NULL ||
+ VOP_GETATTR(vp, &attr, 0, CRED(), NULL) != 0)
+ attr.va_nodeid = 0;
+
+ mutex_enter(&so->so_lock);
+ sti = _SOTOTPI(so);
+
+ if (sti->sti_laddr_sa != NULL &&
+ sti->sti_laddr_len > 0) {
+ name = sti->sti_laddr_sa->sa_data;
+ } else if (sti->sti_faddr_sa != NULL &&
+ sti->sti_faddr_len > 0) {
+ name = sti->sti_faddr_sa->sa_data;
+ }
+
+ /*
+ * Derived from enum values in Linux kernel source:
+ * include/uapi/linux/net.h
+ */
+ if ((so->so_state & SS_ISDISCONNECTING) != 0) {
+ status = 4;
+ } else if ((so->so_state & SS_ISCONNECTING) != 0) {
+ status = 2;
+ } else if ((so->so_state & SS_ISCONNECTED) != 0) {
+ status = 3;
+ } else {
+ status = 1;
+ /* Add ACC flag for stream-type server sockets */
+ if (so->so_type != SOCK_DGRAM &&
+ sti->sti_laddr_sa != NULL)
+ flags |= 0x10000;
+ }
+
+ /* Convert to Linux type */
+ switch (so->so_type) {
+ case SOCK_DGRAM:
+ type = 2;
+ break;
+ case SOCK_SEQPACKET:
+ type = 5;
+ break;
+ default:
+ type = 1;
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "%p: %08X %08X %08X %04X %02X %5llu",
+ so,
+ so->so_count,
+ 0, /* proto, always 0 */
+ flags,
+ type,
+ status,
+ (ino_t)attr.va_nodeid);
+
+ /*
+ * Due to shortcomings in the abstract socket emulation, they
+ * cannot be properly represented here (as @<path>).
+ *
+ * This will be the case until they are better implemented.
+ */
+ if (name != NULL)
+ lxpr_uiobuf_printf(uiobuf, " %s\n", name);
+ else
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ mutex_exit(&so->so_lock);
+ }
+ mutex_exit(&socklist.sl_lock);
+}
+
+/*
+ * lxpr_read_kmsg(): read the contents of the kernel message queue. We
+ * translate this into the reception of console messages for this zone; each
+ * read copies out a single zone console message, or blocks until the next one
+ * is produced, unless we're open non-blocking, in which case we return after
+ * 1ms.
+ */
+
+#define LX_KMSG_PRI "<0>"
+
+static void
+lxpr_read_kmsg(lxpr_node_t *lxpnp, struct lxpr_uiobuf *uiobuf, ldi_handle_t lh)
+{
+ mblk_t *mp;
+ timestruc_t to;
+ timestruc_t *tp = NULL;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_KMSG);
+
+ if (lxpr_uiobuf_nonblock(uiobuf)) {
+ to.tv_sec = 0;
+ to.tv_nsec = 1000000; /* 1msec */
+ tp = &to;
+ }
+
+ if (ldi_getmsg(lh, &mp, tp) == 0) {
+ /*
+ * lx procfs doesn't like successive reads to the same file
+ * descriptor unless we do an explicit rewind each time.
+ */
+ lxpr_uiobuf_seek(uiobuf, 0);
+
+ lxpr_uiobuf_printf(uiobuf, "%s%s", LX_KMSG_PRI,
+ mp->b_cont->b_rptr);
+
+ freemsg(mp);
+ }
+}
+
+/*
+ * lxpr_read_loadavg(): read the contents of the "loadavg" file. We do just
+ * enough for uptime and other simple lxproc readers to work
+ */
+extern int nthread;
+
+static void
+lxpr_read_loadavg(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ulong_t avenrun1;
+ ulong_t avenrun5;
+ ulong_t avenrun15;
+ ulong_t avenrun1_cs;
+ ulong_t avenrun5_cs;
+ ulong_t avenrun15_cs;
+ int loadavg[3];
+ int *loadbuf;
+ cpupart_t *cp;
+ zone_t *zone = LXPTOZ(lxpnp);
+
+ uint_t nrunnable = 0;
+ rctl_qty_t nlwps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_LOADAVG);
+
+ mutex_enter(&cpu_lock);
+
+ /*
+ * Need to add up values over all CPU partitions. If pools are active,
+ * only report the values of the zone's partition, which by definition
+ * includes the current CPU.
+ */
+ if (pool_pset_enabled()) {
+ psetid_t psetid = zone_pset_get(LXPTOZ(lxpnp));
+
+ ASSERT(LXPTOZ(lxpnp) != &zone0);
+ cp = CPU->cpu_part;
+
+ nrunnable = cp->cp_nrunning + cp->cp_nrunnable;
+ (void) cpupart_get_loadavg(psetid, &loadavg[0], 3);
+ loadbuf = &loadavg[0];
+ } else {
+ cp = cp_list_head;
+ do {
+ nrunnable += cp->cp_nrunning + cp->cp_nrunnable;
+ } while ((cp = cp->cp_next) != cp_list_head);
+
+ loadbuf = zone == global_zone ?
+ &avenrun[0] : zone->zone_avenrun;
+ }
+
+ /*
+ * If we're in the non-global zone, we'll report the total number of
+ * LWPs in the zone for the "nproc" parameter of /proc/loadavg,
+ * otherwise will just use nthread (which will include kernel threads,
+ * but should be good enough for lxproc).
+ */
+ nlwps = zone == global_zone ? nthread : zone->zone_nlwps;
+
+ mutex_exit(&cpu_lock);
+
+ avenrun1 = loadbuf[0] >> FSHIFT;
+ avenrun1_cs = ((loadbuf[0] & (FSCALE-1)) * 100) >> FSHIFT;
+ avenrun5 = loadbuf[1] >> FSHIFT;
+ avenrun5_cs = ((loadbuf[1] & (FSCALE-1)) * 100) >> FSHIFT;
+ avenrun15 = loadbuf[2] >> FSHIFT;
+ avenrun15_cs = ((loadbuf[2] & (FSCALE-1)) * 100) >> FSHIFT;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%ld.%02d %ld.%02d %ld.%02d %d/%d %d\n",
+ avenrun1, avenrun1_cs,
+ avenrun5, avenrun5_cs,
+ avenrun15, avenrun15_cs,
+ nrunnable, nlwps, 0);
+}
+
+/*
+ * lxpr_read_meminfo(): read the contents of the "meminfo" file.
+ */
+static void
+lxpr_read_meminfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+ ulong_t total_mem, free_mem, total_swap;
+ boolean_t swap_disabled;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_MEMINFO);
+ ASSERT(zone->zone_brand == &lx_brand);
+ ASSERT(lxzd != NULL);
+ swap_disabled = lxzd->lxzd_swap_disabled;
+
+ zone_get_physmem_data(zone->zone_id, (pgcnt_t *)&total_mem,
+ (pgcnt_t *)&free_mem);
+ total_mem = ptob(total_mem);
+ free_mem = ptob(free_mem);
+
+ if (swap_disabled) {
+ total_swap = 0;
+ } else {
+ if (zone->zone_max_swap_ctl == UINT64_MAX) {
+ total_swap = ptob(k_anoninfo.ani_max);
+ } else {
+ mutex_enter(&zone->zone_mem_lock);
+ total_swap = zone->zone_max_swap_ctl;
+ mutex_exit(&zone->zone_mem_lock);
+ }
+ }
+
+ /*
+ * SwapFree
+ * On illumos we reserve swap up front, whereas on Linux they just
+ * wing it and kill a random process if they run out of backing store
+ * for virtual memory. Our swap reservation doesn't translate to that
+ * model, so just inform the caller that no swap is being used.
+ *
+ * MemAvailable
+ * MemAvailable entry is available since Linux Kernel +3.14, is an
+ * estimate of how much memory is available for starting new
+ * applications, without swapping. In lxbrand we will always return the
+ * available free memory as an estimate of this value.
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "MemTotal: %8lu kB\n"
+ "MemFree: %8lu kB\n"
+ "MemAvailable: %8lu kB\n"
+ "MemShared: %8u kB\n"
+ "Buffers: %8u kB\n"
+ "Cached: %8u kB\n"
+ "SwapCached: %8u kB\n"
+ "Active: %8u kB\n"
+ "Inactive: %8u kB\n"
+ "HighTotal: %8u kB\n"
+ "HighFree: %8u kB\n"
+ "LowTotal: %8u kB\n"
+ "LowFree: %8u kB\n"
+ "SwapTotal: %8lu kB\n"
+ "SwapFree: %8lu kB\n",
+ btok(total_mem), /* MemTotal */
+ btok(free_mem), /* MemFree */
+ btok(free_mem), /* MemAvailable */
+ 0, /* MemShared */
+ 0, /* Buffers */
+ 0, /* Cached */
+ 0, /* SwapCached */
+ 0, /* Active */
+ 0, /* Inactive */
+ 0, /* HighTotal */
+ 0, /* HighFree */
+ btok(total_mem), /* LowTotal */
+ btok(free_mem), /* LowFree */
+ btok(total_swap), /* SwapTotal */
+ btok(total_swap)); /* SwapFree */
+}
+
+/*
+ * lxpr_read_mounts():
+ *
+ * Note: we currently also use this for /proc/{pid}/mounts since we don't
+ * yet support mount namespaces.
+ */
+static void
+lxpr_read_mounts(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ list_t *mounts;
+ lxpr_mount_entry_t *lme;
+
+ mounts = lxpr_enumerate_mounts(zone);
+
+ /*
+ * now we can run through what we've extracted without holding
+ * vfs_list_read_lock()
+ */
+ lme = list_remove_head(mounts);
+ while (lme != NULL) {
+ char *resource, *mntpt, *fstype;
+ vnode_t *vp;
+ int error;
+
+ mntpt = (char *)refstr_value(lme->lme_mntpt);
+ resource = (char *)refstr_value(lme->lme_resource);
+
+ if (mntpt == NULL || mntpt[0] == '\0') {
+ goto nextp;
+ }
+ mntpt = ZONE_PATH_TRANSLATE(mntpt, zone);
+ error = lookupname(mntpt, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ goto nextp;
+ } else if ((vp->v_flag & VROOT) == 0 && !lme->lme_force) {
+ VN_RELE(vp);
+ goto nextp;
+ }
+ VN_RELE(vp);
+
+ if (resource != NULL && resource[0] != '\0') {
+ if (resource[0] == '/') {
+ resource = ZONE_PATH_VISIBLE(resource, zone) ?
+ ZONE_PATH_TRANSLATE(resource, zone) : mntpt;
+ }
+ } else {
+ resource = "none";
+ }
+
+ /* Make things look more like Linux. */
+ fstype = vfssw[lme->lme_fstype].vsw_name;
+ if (lxpr_clean_mntent(&mntpt, &fstype, &resource) != 0 &&
+ !lme->lme_force) {
+ goto nextp;
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "%s %s %s %s 0 0\n",
+ resource, mntpt, fstype, lme->lme_mntopts);
+
+nextp:
+ refstr_rele(lme->lme_mntpt);
+ refstr_rele(lme->lme_resource);
+ kmem_free(lme->lme_mntopts, lme->lme_mntopts_len);
+ kmem_free(lme, sizeof (lxpr_mount_entry_t));
+ lme = list_remove_head(mounts);
+ }
+
+ list_destroy(mounts);
+ kmem_free(mounts, sizeof (list_t));
+}
+
+/*
+ * lxpr_read_partitions():
+ *
+ * Over the years, /proc/partitions has been made considerably smaller -- to
+ * the point that it really is only major number, minor number, number of
+ * blocks (which we report as 0), and partition name.
+ *
+ * We support this because some things want to see it to make sense of
+ * /proc/diskstats, and also because "fdisk -l" and a few other things look
+ * here to find all disks on the system.
+ */
+static void
+lxpr_read_partitions(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lx_zone_data_t *lxzd;
+ lx_virt_disk_t *vd;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PARTITIONS);
+
+ lxpr_uiobuf_printf(uiobuf, "major minor #blocks name\n\n");
+
+ lxzd = ztolxzd(LXPTOZ(lxpnp));
+ if (lxzd == NULL)
+ return;
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ lxpr_uiobuf_printf(uiobuf, "%4d %7d %10d %s\n",
+ getmajor(vd->lxvd_emul_dev), getminor(vd->lxvd_emul_dev),
+ 0, vd->lxvd_name);
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+}
+
+/*
+ * There aren't many actual devices inside a zone but we want to provide the
+ * major numbers for the pseudo devices that do exist, including our pts/ptm
+ * device, as well as the zvol virtual disk device. We simply hardcode the
+ * emulated major numbers that are used elsewhere in the code and that match
+ * the expected Linux major numbers. See lx devfs where some of the major
+ * numbers have no defined constants.
+ */
+static void
+lxpr_read_devices(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_DEVICES);
+
+ lxpr_uiobuf_printf(uiobuf, "Character devices:\n");
+ lxpr_uiobuf_printf(uiobuf, "%3d /dev/tty\n", LX_TTY_MAJOR);
+ lxpr_uiobuf_printf(uiobuf, "%3d /dev/console\n", LX_TTY_MAJOR);
+ lxpr_uiobuf_printf(uiobuf, "%3d /dev/ptmx\n", LX_TTY_MAJOR);
+ lxpr_uiobuf_printf(uiobuf, "%3d ptm\n", LX_PTM_MAJOR);
+ lxpr_uiobuf_printf(uiobuf, "%3d pts\n", LX_PTS_MAJOR_MIN);
+
+ lxpr_uiobuf_printf(uiobuf, "\nBlock devices:\n");
+ lxpr_uiobuf_printf(uiobuf, "%3d zvol\n", LX_MAJOR_DISK);
+}
+
+/*
+ * lxpr_read_diskstats():
+ *
+ * See the block comment above the per-device output-generating line for the
+ * details of the format.
+ */
+static void
+lxpr_read_diskstats(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ lx_zone_data_t *lxzd;
+ kstat_t kn;
+ int num;
+ zone_vfs_kstat_t *kip;
+ size_t size;
+ lx_virt_disk_t *vd;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_DISKSTATS);
+
+ lxzd = ztolxzd(zone);
+ if (lxzd == NULL)
+ return;
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ /*
+ * Use the zone_vfs kstat, which is a superset of a kstat_io_t, since
+ * it tracks IO at the zone level.
+ */
+ (void) strlcpy(kn.ks_module, "zone_vfs", sizeof (kn.ks_module));
+ (void) strlcpy(kn.ks_name, zone->zone_name, sizeof (kn.ks_name));
+ kn.ks_instance = zone->zone_id;
+
+ kip = (zone_vfs_kstat_t *)lxpr_kstat_read(&kn, B_TRUE, &size, &num,
+ zone->zone_id);
+ if (kip == NULL)
+ return;
+
+ if (size < sizeof (kstat_io_t)) {
+ kmem_free(kip, size);
+ return;
+ }
+
+ /*
+ * Because the zone vfs stats are tracked at the zone level we use
+ * the same kstat for the zone's virtual disk (the zpool) and any
+ * zvols that might also visible within the zone.
+ */
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ /*
+ * /proc/diskstats is defined to have one line of output for
+ * each block device, with each line containing the following
+ * 14 fields:
+ *
+ * 1 - major number
+ * 2 - minor mumber
+ * 3 - device name
+ * 4 - reads completed successfully
+ * 5 - reads merged
+ * 6 - sectors read
+ * 7 - time spent reading (ms)
+ * 8 - writes completed
+ * 9 - writes merged
+ * 10 - sectors written
+ * 11 - time spent writing (ms)
+ * 12 - I/Os currently in progress
+ * 13 - time spent doing I/Os (ms)
+ * 14 - weighted time spent doing I/Os (ms)
+ *
+ * One small hiccup: we don't actually keep track of time
+ * spent reading vs. time spent writing -- we keep track of
+ * time waiting vs. time actually performing I/O. While we
+ * could divide the total time by the I/O mix (making the
+ * obviously wrong assumption that I/O operations all take the
+ * same amount of time), this has the undesirable side-effect
+ * of moving backwards. Instead, we report the total time
+ * (read + write) for all three stats (read, write, total).
+ * This is also a lie of sorts, but it should be more
+ * immediately clear to the user that reads and writes are
+ * each being double-counted as the other.
+ *
+ * Since certain consumers interpret the major/minor numbers to
+ * infer device names, some translation is required to avoid
+ * output which results in totally unexpected results.
+ */
+
+ lxpr_uiobuf_printf(uiobuf, "%4d %7d %s ",
+ getmajor(vd->lxvd_emul_dev),
+ getminor(vd->lxvd_emul_dev),
+ vd->lxvd_name);
+
+ if (vd->lxvd_type == LXVD_ZFS_DS) {
+ /*
+ * Use the zone-wide vfs stats for any zfs datasets
+ * represented via virtual devices.
+ */
+#define KV(N) kip->zv_ ## N.value.ui64
+#define NS_PER_MS (uint64_t)(NANOSEC / MILLISEC)
+ lxpr_uiobuf_printf(uiobuf,
+ "%llu %llu %llu %llu "
+ "%llu %llu %llu %llu "
+ "%llu %llu %llu\n",
+ (uint64_t)KV(reads), 0LL,
+ KV(nread) / (uint64_t)LXPR_SECTOR_SIZE,
+ (KV(rtime) + KV(wtime)) / NS_PER_MS,
+ (uint64_t)KV(writes), 0LL,
+ KV(nwritten) / (uint64_t)LXPR_SECTOR_SIZE,
+ (KV(rtime) + KV(wtime)) / NS_PER_MS,
+ (uint64_t)(KV(rcnt) + KV(wcnt)),
+ (KV(rtime) + KV(wtime)) / NS_PER_MS,
+ (KV(rlentime) + KV(wlentime)) / NS_PER_MS);
+#undef KV
+#undef NS_PER_MS
+ } else {
+ /*
+ * Report nearly-zeroed statistics for other devices.
+ *
+ * Since iostat will ignore devices which report no
+ * succesful reads or writes, a single read of one
+ * sector, taking 1ms, is reported.
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "1 0 1 1 0 0 0 0 0 0 0\n");
+ }
+
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+
+ kmem_free(kip, size);
+}
+
+/*
+ * lxpr_read_version(): read the contents of the "version" file.
+ */
+static void
+lxpr_read_version(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lx_zone_data_t *lxzd = ztolxzd(LXPTOZ(lxpnp));
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ char release[LX_KERN_RELEASE_MAX];
+ char version[LX_KERN_VERSION_MAX];
+
+ mutex_enter(&lxzd->lxzd_lock);
+ (void) strlcpy(release, lxzd->lxzd_kernel_release, sizeof (release));
+ (void) strlcpy(version, lxzd->lxzd_kernel_version, sizeof (version));
+ mutex_exit(&lxzd->lxzd_lock);
+
+ /* Use per-process overrides, if specified */
+ if (lxpd != NULL && lxpd->l_uname_release[0] != '\0') {
+ (void) strlcpy(release, lxpd->l_uname_release,
+ sizeof (release));
+ }
+ if (lxpd != NULL && lxpd->l_uname_version[0] != '\0') {
+ (void) strlcpy(version, lxpd->l_uname_version,
+ sizeof (version));
+ }
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%s version %s (%s version %d.%d.%d) %s\n",
+ LX_UNAME_SYSNAME, release,
+#if defined(__GNUC__)
+ "gcc", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__,
+#else
+ "cc", 1, 0, 0,
+#endif
+ version);
+}
+
+static void
+lxpr_read_vmstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+
+ ulong_t pgpgin_cum = 0;
+ ulong_t pgpgout_cum = 0;
+ ulong_t pgswapout_cum = 0;
+ ulong_t pgswapin_cum = 0;
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+ /* Calculate cumulative stats */
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ /* Only count CPUs which are present and active. */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ pgpgin_cum += CPU_STATS(cp, vm.pgpgin);
+ pgpgout_cum += CPU_STATS(cp, vm.pgpgout);
+ pgswapin_cum += CPU_STATS(cp, vm.pgswapin);
+ pgswapout_cum += CPU_STATS(cp, vm.pgswapout);
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+ mutex_exit(&cpu_lock);
+
+ /*
+ * Needless to say, the metrics presented by vmstat are very specific
+ * to the internals of the Linux kernel. There is little per-zone
+ * information which can be translated in a meaningful way to fit the
+ * expected fields. For the time being, the output is kept sparse.
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "pgpgin %lu\n"
+ "pgpgout %lu\n"
+ "pswpin %lu\n"
+ "pswpout %lu\n",
+ pgpgin_cum,
+ pgpgout_cum,
+ pgswapin_cum,
+ pgswapout_cum);
+}
+
+/*
+ * lxpr_read_stat(): read the contents of the "stat" file.
+ *
+ */
+static void
+lxpr_read_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ ulong_t idle_cum = 0;
+ ulong_t sys_cum = 0;
+ ulong_t user_cum = 0;
+ ulong_t irq_cum = 0;
+ ulong_t cpu_nrunnable_cum = 0;
+ ulong_t w_io_cum = 0;
+
+ ulong_t pgpgin_cum = 0;
+ ulong_t pgpgout_cum = 0;
+ ulong_t pgswapout_cum = 0;
+ ulong_t pgswapin_cum = 0;
+ ulong_t intr_cum = 0;
+ ulong_t pswitch_cum = 0;
+ ulong_t forks_cum = 0;
+ hrtime_t msnsecs[NCMSTATES];
+ /* is the emulated release > 2.4 */
+ boolean_t newer_than24 = lx_kern_release_cmp(LXPTOZ(lxpnp), "2.4") > 0;
+ zone_t *zone = LXPTOZ(lxpnp);
+ const char *fmtstr0, *fmtstr1;
+ /* temporary variable since scalehrtime modifies data in place */
+ hrtime_t tmptime;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_STAT);
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ /* Calculate cumulative stats */
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ int i;
+
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ get_cpu_mstate(cp, msnsecs);
+
+ idle_cum += NSEC_TO_TICK(msnsecs[CMS_IDLE]);
+ sys_cum += NSEC_TO_TICK(msnsecs[CMS_SYSTEM]);
+ user_cum += NSEC_TO_TICK(msnsecs[CMS_USER]);
+
+ pgpgin_cum += CPU_STATS(cp, vm.pgpgin);
+ pgpgout_cum += CPU_STATS(cp, vm.pgpgout);
+ pgswapin_cum += CPU_STATS(cp, vm.pgswapin);
+ pgswapout_cum += CPU_STATS(cp, vm.pgswapout);
+
+
+ if (newer_than24) {
+ cpu_nrunnable_cum += cp->cpu_disp->disp_nrunnable;
+ w_io_cum += CPU_STATS(cp, sys.iowait);
+ for (i = 0; i < NCMSTATES; i++) {
+ tmptime = cp->cpu_intracct[i];
+ scalehrtime(&tmptime);
+ irq_cum += NSEC_TO_TICK(tmptime);
+ }
+ }
+
+ for (i = 0; i < PIL_MAX; i++)
+ intr_cum += CPU_STATS(cp, sys.intr[i]);
+
+ pswitch_cum += CPU_STATS(cp, sys.pswitch);
+ forks_cum += CPU_STATS(cp, sys.sysfork);
+ forks_cum += CPU_STATS(cp, sys.sysvfork);
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ if (lx_kern_release_cmp(zone, "2.6.33") >= 0) {
+ fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0 0 0 0\n";
+ fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0 0 0 0\n";
+ } else if (lx_kern_release_cmp(zone, "2.6.24") >= 0) {
+ fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0 0 0\n";
+ fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0 0 0\n";
+ } else if (lx_kern_release_cmp(zone, "2.6.11") >= 0) {
+ fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0 0\n";
+ fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0 0\n";
+ } else if (lx_kern_release_cmp(zone, "2.5.41") >= 0) {
+ fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0\n";
+ fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0\n";
+ } else {
+ /* Note: we pass an unused param to these fmt strings */
+ fmtstr0 = "cpu %lu 0 %lu %lu\n";
+ fmtstr1 = "cpu%d %lu 0 %lu %lu\n";
+ }
+
+ /* Adjust hz */
+ user_cum = HZ_TO_LX_USERHZ(user_cum);
+ sys_cum = HZ_TO_LX_USERHZ(sys_cum);
+ idle_cum = HZ_TO_LX_USERHZ(idle_cum);
+ irq_cum = HZ_TO_LX_USERHZ(irq_cum);
+
+ lxpr_uiobuf_printf(uiobuf, fmtstr0,
+ user_cum, sys_cum, idle_cum, irq_cum);
+
+ /* Do per processor stats */
+ do {
+ int i;
+
+ ulong_t idle_ticks;
+ ulong_t sys_ticks;
+ ulong_t user_ticks;
+ ulong_t irq_ticks = 0;
+
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ get_cpu_mstate(cp, msnsecs);
+
+ idle_ticks = HZ_TO_LX_USERHZ(NSEC_TO_TICK(msnsecs[CMS_IDLE]));
+ sys_ticks = HZ_TO_LX_USERHZ(NSEC_TO_TICK(msnsecs[CMS_SYSTEM]));
+ user_ticks = HZ_TO_LX_USERHZ(NSEC_TO_TICK(msnsecs[CMS_USER]));
+
+ for (i = 0; i < NCMSTATES; i++) {
+ tmptime = cp->cpu_intracct[i];
+ scalehrtime(&tmptime);
+ irq_ticks += NSEC_TO_TICK(tmptime);
+ }
+ irq_ticks = HZ_TO_LX_USERHZ(irq_ticks);
+
+ lxpr_uiobuf_printf(uiobuf, fmtstr1, HZ_TO_LX_USERHZ(cp->cpu_id),
+ user_ticks, sys_ticks, idle_ticks, irq_ticks);
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+
+ if (newer_than24) {
+ lxpr_uiobuf_printf(uiobuf,
+ "page %lu %lu\n"
+ "swap %lu %lu\n"
+ "intr %lu\n"
+ "ctxt %lu\n"
+ "btime %lu\n"
+ "processes %lu\n"
+ "procs_running %lu\n"
+ "procs_blocked %lu\n",
+ pgpgin_cum, pgpgout_cum,
+ pgswapin_cum, pgswapout_cum,
+ intr_cum,
+ pswitch_cum,
+ zone->zone_boot_time,
+ forks_cum,
+ cpu_nrunnable_cum,
+ w_io_cum);
+ } else {
+ lxpr_uiobuf_printf(uiobuf,
+ "page %lu %lu\n"
+ "swap %lu %lu\n"
+ "intr %lu\n"
+ "ctxt %lu\n"
+ "btime %lu\n"
+ "processes %lu\n",
+ pgpgin_cum, pgpgout_cum,
+ pgswapin_cum, pgswapout_cum,
+ intr_cum,
+ pswitch_cum,
+ zone->zone_boot_time,
+ forks_cum);
+ }
+}
+
+/*
+ * lxpr_read_swaps():
+ *
+ * We don't support swap files or partitions, but some programs like to look
+ * here just to check we have some swap on the system, so we lie and show
+ * our entire swap cap as one swap partition. See lxpr_read_meminfo for an
+ * explanation on why we report 0 used swap.
+ *
+ * The zone's lxzd_swap_disabled boolean controls whether or not we pretend
+ * swap space is configured.
+ *
+ * It is important to use formatting identical to the Linux implementation
+ * so that consumers do not break. See swap_show() in mm/swapfile.c.
+ */
+static void
+lxpr_read_swaps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ boolean_t swap_enabled;
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+
+ ASSERT(zone->zone_brand == &lx_brand);
+ ASSERT(lxzd != NULL);
+ swap_enabled = !lxzd->lxzd_swap_disabled;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n");
+
+ if (swap_enabled) {
+ uint64_t totswap, usedswap;
+
+ if (zone->zone_max_swap_ctl == UINT64_MAX) {
+ totswap = (k_anoninfo.ani_max * PAGESIZE) >> 10;
+ } else {
+ mutex_enter(&zone->zone_mem_lock);
+ /* Uses units of 1 kb (2^10). */
+ totswap = zone->zone_max_swap_ctl >> 10;
+ mutex_exit(&zone->zone_mem_lock);
+ }
+ usedswap = 0;
+
+ lxpr_uiobuf_printf(uiobuf, "%-40s%s\t%llu\t%llu\t%d\n",
+ "/dev/swap", "partition", totswap, usedswap, -1);
+ }
+}
+
+static void
+lxpr_read_sys_fs_aiomax(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_AIO_MAX_NR);
+ lxpr_uiobuf_printf(uiobuf, "%llu\n", LX_AIO_MAX_NR);
+}
+
+static void
+lxpr_read_sys_fs_aionr(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+ uint64_t curr;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_AIO_NR);
+ ASSERT(zone->zone_brand == &lx_brand);
+ ASSERT(lxzd != NULL);
+
+ mutex_enter(&lxzd->lxzd_lock);
+ curr = (uint64_t)(lxzd->lxzd_aio_nr);
+ mutex_exit(&lxzd->lxzd_lock);
+ lxpr_uiobuf_printf(uiobuf, "%llu\n", curr);
+}
+
+/*
+ * lxpr_read_sys_fs_filemax():
+ *
+ * The zone's total number of open files is not fixed or tunable, but we can
+ * provide a number by taking:
+ * (zone's proc limit) * (process.max-file-descriptor rctl privileged limit).
+ * The privileged rctl limit is the same as rlim_fd_max.
+ */
+static void
+lxpr_read_sys_fs_filemax(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ uint64_t max_fh, proc_lim;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_FILEMAX);
+ proc_lim = (uint64_t)(zone->zone_nprocs_ctl == INT_MAX ?
+ maxpid : zone->zone_nprocs_ctl);
+ max_fh = proc_lim * (uint64_t)rlim_fd_max;
+ lxpr_uiobuf_printf(uiobuf, "%llu\n", max_fh);
+}
+
+/*
+ * lxpr_read_sys_fs_filenr():
+ *
+ * Contains 3 numbers: current number of allocated file handles (open files),
+ * number of free file handles, and max. number of file handles (same value as
+ * we use in lxpr_read_sys_fs_filemax). Note that since Linux 2.6 the "free"
+ * value is always 0, so we just do the same here. We don't keep track of the
+ * number of files in use within a zone, so we approximate that value by
+ * looking at the current "fi_nfiles" value for each process in the zone.
+ */
+static void
+lxpr_read_sys_fs_filenr(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ uint64_t max_fh, proc_lim, curr_files = 0;
+ int i;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_FILENR);
+ proc_lim = (uint64_t)(zone->zone_nprocs_ctl == INT_MAX ?
+ maxpid : zone->zone_nprocs_ctl);
+ max_fh = proc_lim * (uint64_t)rlim_fd_max;
+
+ for (i = 1; i < v.v_proc; i++) {
+ uint_t nfiles;
+ proc_t *p;
+ uf_info_t *fip;
+
+ mutex_enter(&pidlock);
+
+ if ((p = pid_entry(i)) == NULL || p->p_stat == SIDL ||
+ p->p_pid == 0 || p->p_zone != zone ||
+ p == zone->zone_zsched ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+ mutex_exit(&pidlock);
+ continue;
+ }
+
+ fip = P_FINFO(p);
+ mutex_enter(&fip->fi_lock);
+ nfiles = fip->fi_nfiles;
+ mutex_exit(&fip->fi_lock);
+
+ mutex_exit(&pidlock);
+
+ curr_files += nfiles;
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "%llu\t0\t%llu\n", curr_files, max_fh);
+}
+
+/*
+ * inotify tunables exported via /proc.
+ */
+extern int inotify_maxevents;
+extern int inotify_maxinstances;
+extern int inotify_maxwatches;
+
+static void
+lxpr_read_sys_fs_inotify_max_queued_events(lxpr_node_t *lxpnp,
+ lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFY_MAX_QUEUED_EVENTS);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", inotify_maxevents);
+}
+
+static void
+lxpr_read_sys_fs_inotify_max_user_instances(lxpr_node_t *lxpnp,
+ lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFY_MAX_USER_INSTANCES);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", inotify_maxinstances);
+}
+
+static void
+lxpr_read_sys_fs_inotify_max_user_watches(lxpr_node_t *lxpnp,
+ lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFY_MAX_USER_WATCHES);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", inotify_maxwatches);
+}
+
+static void
+lxpr_read_sys_fs_pipe_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lx_zone_data_t *lxzd = ztolxzd(LXPTOZ(lxpnp));
+ uint_t pipe_max;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_PIPE_MAX);
+ ASSERT(lxzd != NULL);
+
+ mutex_enter(&lxzd->lxzd_lock);
+ pipe_max = lxzd->lxzd_pipe_max_sz;
+ mutex_exit(&lxzd->lxzd_lock);
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", pipe_max);
+}
+
+static void
+lxpr_read_sys_kernel_caplcap(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_CAPLCAP);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", LX_CAP_MAX_VALID);
+}
+
+static void
+lxpr_read_sys_kernel_corepatt(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ struct core_globals *cg;
+ refstr_t *rp;
+ corectl_path_t *ccp;
+ char tr[MAXPATHLEN];
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_COREPATT);
+
+ cg = zone_getspecific(core_zone_key, zone);
+ ASSERT(cg != NULL);
+
+ /* If core dumps are disabled, return an empty string. */
+ if ((cg->core_options & CC_PROCESS_PATH) == 0) {
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ return;
+ }
+
+ ccp = cg->core_default_path;
+ mutex_enter(&ccp->ccp_mtx);
+ if ((rp = ccp->ccp_path) != NULL)
+ refstr_hold(rp);
+ mutex_exit(&ccp->ccp_mtx);
+
+ if (rp == NULL) {
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ return;
+ }
+
+ bzero(tr, sizeof (tr));
+ if (lxpr_core_path_s2l(refstr_value(rp), tr, sizeof (tr)) != 0) {
+ refstr_rele(rp);
+ lxpr_uiobuf_printf(uiobuf, "\n");
+ return;
+ }
+
+ refstr_rele(rp);
+ lxpr_uiobuf_printf(uiobuf, "%s\n", tr);
+}
+
+static void
+lxpr_read_sys_kernel_hostname(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_HOSTNAME);
+ lxpr_uiobuf_printf(uiobuf, "%s\n", uts_nodename());
+}
+
+static void
+lxpr_read_sys_kernel_msgmax(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ /*
+ * We don't have an rctl for this. See our definition for LX_MSGMAX
+ * in the user-level emulation library. Once that code moves into
+ * the kernel, we can use a common definition. This matches the
+ * value on Linux.
+ */
+ uint_t val = 8192;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_MSGMAX);
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", val);
+}
+
+static void
+lxpr_read_sys_kernel_msgmnb(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ rctl_qty_t val;
+ proc_t *pp = curproc;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_MSGMNB);
+
+ mutex_enter(&pp->p_lock);
+ val = rctl_enforced_value(rc_process_msgmnb, pp->p_rctls, pp);
+ mutex_exit(&pp->p_lock);
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)val);
+}
+
+static void
+lxpr_read_sys_kernel_msgmni(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ rctl_qty_t val;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_MSGMNI);
+
+ mutex_enter(&curproc->p_lock);
+ val = rctl_enforced_value(rc_zone_msgmni,
+ LXPTOZ(lxpnp)->zone_rctls, curproc);
+ mutex_exit(&curproc->p_lock);
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)val);
+}
+
+static void
+lxpr_read_sys_kernel_ngroups_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_NGROUPS_MAX);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", ngroups_max);
+}
+
+static void
+lxpr_read_sys_kernel_osrel(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+ char version[LX_KERN_VERSION_MAX];
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_OSREL);
+ ASSERT(zone->zone_brand == &lx_brand);
+ ASSERT(lxzd != NULL);
+
+ mutex_enter(&lxzd->lxzd_lock);
+ (void) strlcpy(version, lxzd->lxzd_kernel_version, sizeof (version));
+ mutex_exit(&lxzd->lxzd_lock);
+ lxpr_uiobuf_printf(uiobuf, "%s\n", version);
+}
+
+static void
+lxpr_read_sys_kernel_pid_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_PID_MAX);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", maxpid);
+}
+
+static void
+lxpr_gen_uuid(char *uuid, size_t size)
+{
+ uint8_t r[16];
+ if (random_get_bytes(r, sizeof (r)) != 0) {
+ (void) random_get_pseudo_bytes(r, sizeof (r));
+ }
+ /* Set UUID version to 4 (random) */
+ r[6] = 0x40 | (r[6] & 0x0f);
+ /* Set UUID variant to 1 */
+ r[8] = 0x80 | (r[8] & 0x3f);
+
+ (void) snprintf(uuid, size,
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x"
+ "-%02x%02x%02x%02x%02x%02x",
+ r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8],
+ r[9], r[10], r[11], r[12], r[13], r[14], r[15]);
+}
+
+static void
+lxpr_read_sys_kernel_rand_bootid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ /*
+ * This file isn't documented on the Linux proc(5) man page but
+ * according to the blog of the author of systemd/journald (the
+ * consumer), he says:
+ * boot_id: A random ID that is regenerated on each boot. As such it
+ * can be used to identify the local machine's current boot. It's
+ * universally available on any recent Linux kernel. It's a good and
+ * safe choice if you need to identify a specific boot on a specific
+ * booted kernel.
+ *
+ * On Linux the format appears to resemble a uuid so stick with that.
+ */
+ zone_t *zone = LXPTOZ(lxpnp);
+ lx_zone_data_t *lxzd = ztolxzd(zone);
+ char bootid[UUID_PRINTABLE_STRING_LENGTH];
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_RAND_BOOTID);
+ ASSERT(zone->zone_brand == &lx_brand);
+ ASSERT(lxzd != NULL);
+
+ mutex_enter(&lxzd->lxzd_lock);
+ if (lxzd->lxzd_bootid[0] == '\0') {
+ lxpr_gen_uuid(lxzd->lxzd_bootid, sizeof (lxzd->lxzd_bootid));
+ }
+ (void) strlcpy(bootid, lxzd->lxzd_bootid, sizeof (bootid));
+ mutex_exit(&lxzd->lxzd_lock);
+
+ lxpr_uiobuf_printf(uiobuf, "%s\n", bootid);
+}
+
+/*
+ * The amount of entropy available (in bits).
+ */
+static void
+lxpr_read_sys_kernel_rand_entavl(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_RAND_ENTAVL);
+ ASSERT(LXPTOZ(lxpnp)->zone_brand == &lx_brand);
+
+ lxpr_uiobuf_printf(uiobuf, "%d\n", swrand_stats.ss_entEst);
+}
+
+static void
+lxpr_read_sys_kernel_rand_uuid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ /*
+ * Each read from this read-only file should return a new
+ * random 128-bit UUID string in the standard UUID format.
+ */
+ zone_t *zone = LXPTOZ(lxpnp);
+ char uuid[UUID_PRINTABLE_STRING_LENGTH];
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_RAND_UUID);
+ ASSERT(zone->zone_brand == &lx_brand);
+
+ lxpr_gen_uuid(uuid, sizeof (uuid));
+
+ lxpr_uiobuf_printf(uiobuf, "%s\n", uuid);
+}
+
+static void
+lxpr_read_sys_kernel_sem(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *pp = curproc;
+ zone_t *zone = LXPTOZ(lxpnp);
+ rctl_qty_t vmsl, vopm, vmni, vmns;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SEM);
+
+ mutex_enter(&pp->p_lock);
+ vmsl = rctl_enforced_value(rc_process_semmsl, pp->p_rctls, pp);
+ vopm = rctl_enforced_value(rc_process_semopm, pp->p_rctls, pp);
+ vmni = rctl_enforced_value(rc_zone_semmni, zone->zone_rctls, pp);
+ mutex_exit(&pp->p_lock);
+ vmns = vmsl * vmni;
+ if (vmns < vmsl || vmns < vmni) {
+ vmns = ULLONG_MAX;
+ }
+ /*
+ * Format: semmsl semmns semopm semmni
+ * - semmsl: Limit semaphores in a sempahore set.
+ * - semmns: Limit semaphores in all semaphore sets
+ * - semopm: Limit operations in a single semop call
+ * - semmni: Limit number of semaphore sets
+ */
+ lxpr_uiobuf_printf(uiobuf, "%llu\t%llu\t%llu\t%llu\n",
+ vmsl, vmns, vopm, vmni);
+}
+
+static void
+lxpr_read_sys_kernel_shmall(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ rctl_qty_t val;
+ zone_t *zone = LXPTOZ(lxpnp);
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SHMALL);
+
+ mutex_enter(&curproc->p_lock);
+ val = rctl_enforced_value(rc_zone_shmmax, zone->zone_rctls, curproc);
+ mutex_exit(&curproc->p_lock);
+
+ /* value is in pages */
+ lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)btop(val));
+}
+
+static void
+lxpr_read_sys_kernel_shmmax(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ rctl_qty_t val;
+ zone_t *zone = LXPTOZ(lxpnp);
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SHMMAX);
+
+ mutex_enter(&curproc->p_lock);
+ val = rctl_enforced_value(rc_zone_shmmax, zone->zone_rctls, curproc);
+ mutex_exit(&curproc->p_lock);
+
+ if (val > FOURGB)
+ val = FOURGB;
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)val);
+}
+
+static void
+lxpr_read_sys_kernel_shmmni(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ rctl_qty_t val;
+ zone_t *zone = LXPTOZ(lxpnp);
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SHMMNI);
+
+ mutex_enter(&curproc->p_lock);
+ val = rctl_enforced_value(rc_zone_shmmni, zone->zone_rctls, curproc);
+ mutex_exit(&curproc->p_lock);
+
+ if (val > FOURGB)
+ val = FOURGB;
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)val);
+}
+
+static void
+lxpr_read_sys_kernel_threads_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_THREADS_MAX);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", LXPTOZ(lxpnp)->zone_nlwps_ctl);
+}
+
+static void
+lxpr_read_sys_net_core_somaxc(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_CORE_SOMAXCON);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_printf(uiobuf, "%d\n", SOMAXCONN);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n", tcps->tcps_conn_req_max_q);
+ netstack_rele(ns);
+}
+
+/*
+ * icmp_echo_ignore_broadcasts
+ * integer; 0 or 1
+ *
+ * illumos: ndd /dev/ip ip_respond_to_echo_broadcast
+ * From the tunable guide: Control whether IPv4 responds to broadcast ICMPv4
+ * echo request. default: 1 (enabled)
+ * Not in ip(7p) man page.
+ *
+ * Note that the Linux setting is the inverse of the illumos value.
+ */
+static void
+lxpr_read_sys_net_ipv4_icmp_eib(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ ip_stack_t *ipst;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_ICMP_EIB);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ ipst = ns->netstack_ip;
+ lxpr_uiobuf_printf(uiobuf, "%d\n", !ipst->ips_ip_g_resp_to_echo_bcast);
+ netstack_rele(ns);
+}
+
+/*
+ * ip_forward
+ * integer; default: 0
+ *
+ * illumos: ndd /dev/ip ip_forwarding
+ * default: 0 (disabled)
+ * Forwarding is described in the ip(7p) man page. We do not support forwarding
+ * in lx at this time, thus we do not support Linux-ABI methods for
+ * enabling/disabling forwarding, and this is always 0.
+ */
+static void
+lxpr_read_sys_net_ipv4_ip_forward(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_IP_FORWARD);
+ lxpr_uiobuf_printf(uiobuf, "0\n");
+}
+
+/*
+ * ip_local_port_range
+ *
+ * The low & high port number range.
+ * integers; default: 32768 61000
+ *
+ * illumos: tcp_smallest_anon_port & tcp_largest_anon_port
+ * Not in tcp(7p) man page.
+ */
+static void
+lxpr_read_sys_net_ipv4_ip_lport_range(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_IP_LPORT_RANGE);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\t%d\n",
+ tcps->tcps_smallest_anon_port, tcps->tcps_largest_anon_port);
+ netstack_rele(ns);
+}
+
+static void
+lxpr_read_sys_net_ipv4_tcp_cc_allow(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ /* For now the set of allowed algos is the same as those available. */
+ return (lxpr_read_sys_net_ipv4_tcp_cc_avail(lxpnp, uiobuf));
+}
+
+static int
+lxpr_uiobuf_printf_ccname(void *cd, struct cc_algo *algo)
+{
+ lxpr_uiobuf_t *uiobuf = cd;
+ lxpr_uiobuf_printf(uiobuf, "%s", algo->name);
+ lxpr_uiobuf_printf(uiobuf,
+ STAILQ_NEXT(algo, entries) != NULL ? " " : "\n");
+ return (0);
+}
+
+static void
+lxpr_read_sys_net_ipv4_tcp_cc_avail(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ (void) cc_walk_algos(lxpr_uiobuf_printf_ccname, uiobuf);
+}
+
+static void
+lxpr_read_sys_net_ipv4_tcp_cc_curr(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_CC_CURR);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%s\n",
+ tcps->tcps_default_cc_algo->name);
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_fin_timeout
+ *
+ * This specifies how many seconds to wait for a final FIN packet before the
+ * socket is forcibly closed. This is strictly a violation of the TCP
+ * specification, but required to prevent denial-of-service attacks.
+ * integer; default: 60;
+ *
+ * illumos: tcp_fin_wait_2_flush_interval
+ * Not in tcp(7p) man page but see comment in uts/common/inet/tcp/tcp_input.c
+ * in the tcp_input_data() function on the use of tcp_fin_wait_2_flush_interval.
+ * The value is in milliseconds.
+ */
+static void
+lxpr_read_sys_net_ipv4_tcp_fin_to(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_FIN_TO);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n",
+ tcps->tcps_fin_wait_2_flush_interval / 1000);
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_keepalive_intvl
+ *
+ * The number of seconds between TCP keep-alive probes. default: 75
+ * Linux retries tcp_keepalive_probes (9) times before timing out.
+ *
+ * illumos:
+ * We have tcp_ka_rinterval but there is no corresponding tcps_* tunable for
+ * this. The closest is tcps_keepalive_abort_interval which specifies the
+ * time threshold for aborting a TCP connection in milliseconds. Linux retries
+ * 9 times (giving a total of 11.25 minutes) so we emulate this by dividing out
+ * tcps_keepalive_abort_interval by 9.
+ */
+static void
+lxpr_read_sys_net_ipv4_tcp_ka_int(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_INT);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n",
+ (tcps->tcps_keepalive_abort_interval / 1000) / 9);
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_keepalive_time
+ *
+ * The number of seconds a connection needs to be idle before TCP begins
+ * sending out keep-alive probes. The default value is 7200 seconds (2 hours).
+ *
+ * illumos: tcp_keepalive_interval
+ * The interval for sending out the first probe in milliseconds. The default is
+ * two hours.
+ */
+static void
+lxpr_read_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_TIM);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n",
+ (tcps->tcps_keepalive_interval / 1000));
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_max_syn_backlog
+ *
+ * The number of half-open connections that can be kept by the backlog queue.
+ * See the Linux tcp(7) man page.
+ *
+ * illumos: tcp_conn_req_max_q0
+ */
+static void
+lxpr_read_sys_net_ipv4_tcp_max_syn_bl(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_MAX_SYN_BL);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n", tcps->tcps_conn_req_max_q0);
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_retries2
+ *
+ * Controls number of TCP retries for data packets. Often tuned down for HA
+ * configurations. RFC 1122 recommends at least 100 seconds for the timeout,
+ * which, for Linux, corresponds to a value of ~8. Oracle suggests a value of
+ * 3 for a RAC configuration, as do various HA tuning guides.
+ * integer; Ubuntu 16.04 default: 15
+ *
+ * illumos: There are 4 ndd parameters that are related to this:
+ * tcp_rexmit_interval_initial: 1000
+ * tcp_rexmit_interval_min: 400
+ * tcp_rexmit_interval_max: 60000
+ * tcp_rexmit_interval_extra: 0
+ * Not in tcp(7p) man page.
+ *
+ * From the tunables guide:
+ * tcp_rexmit_interval_initial is the initial retransmission timeout (RTO) for
+ * a TCP connection in milliseconds (ms).
+ * The interval_min value is the minimum RTO in ms.
+ * The interval_max value is the maximum RTO in ms.
+ * The extra value is an extra time (in ms) to add in to the RTO.
+ */
+static void
+lxpr_read_sys_net_ipv4_tcp_retry2(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+ uint_t i, retry, rx_min, rx_max;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_RETRY2);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ rx_min = tcps->tcps_rexmit_interval_min;
+ rx_max = tcps->tcps_rexmit_interval_max;
+ netstack_rele(ns);
+
+ for (i = rx_min, retry = 0; i < rx_max; retry++) {
+ i *= 2;
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", retry);
+}
+
+/*
+ * tcp_rmem and tcp_wmem
+ *
+ * Display the minimum, default, and maximum TCP receive/transmit window sizes,
+ * in bytes. See the Linux tcp(7) man page.
+ *
+ * In illumos this roughly corresponds to: tcp_recv_hiwat or tcp_xmit_hiwat,
+ * and tcp_max_buf.
+ * tcp_recv_hiwat is the default TCP receive window size
+ * tcp_xmit_hiwat is the default TCP send window size
+ * tcp_max_buf is the maximum TCP send and receive buffer size
+ */
+static void
+lxpr_read_sys_net_ipv4_tcp_rwmem(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+ uint_t min;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_RMEM ||
+ lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_WMEM);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+
+ /* Linux defaults to a page */
+ min = MIN((lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_RMEM ?
+ tcps->tcps_recv_hiwat : tcps->tcps_xmit_hiwat), PAGESIZE);
+
+ lxpr_uiobuf_printf(uiobuf, "%d\t%d\t%d\n",
+ min,
+ (lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_RMEM ?
+ tcps->tcps_recv_hiwat : tcps->tcps_xmit_hiwat),
+ tcps->tcps_max_buf);
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_sack
+ *
+ * Enable RFC 2018 TCP Selective Acknowledgements. Boolean, default: enabled
+ *
+ * illumos: tcp_sack_permitted
+ * tcp_sack_permitted 0 == disabled, 1 == no initiate but accept,
+ * 2 == initiate and accept. default is 2.
+ */
+static void
+lxpr_read_sys_net_ipv4_tcp_sack(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_SACK);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n",
+ (tcps->tcps_sack_permitted == 0 ? 0 : 1));
+ netstack_rele(ns);
+}
+
+/*
+ * tcp_window_scaling
+ *
+ * RFC 1323 TCP window scaling. This feature allows the use of a large window
+ * (> 64K) on a TCP connection. Boolean; default: enabled
+ *
+ * illumos: tcp_wscale_always
+ * tcp_wscale_always is set to 1, the window scale option will always be
+ * set when connecting to a remote system. If tcp_wscale_always is 0, the
+ * window scale option will be set only if the user has requested a send or
+ * receive window larger than 64K. The default value of is 1.
+ */
+static void
+lxpr_read_sys_net_ipv4_tcp_winscale(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_WINSCALE);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENXIO);
+ return;
+ }
+
+ tcps = ns->netstack_tcp;
+ lxpr_uiobuf_printf(uiobuf, "%d\n", tcps->tcps_wscale_always);
+ netstack_rele(ns);
+}
+
+/*
+ * The /proc/sys/vm/dirty* files are (poorly) documented in the Linux
+ * source file Documentation/sysctl/vm.txt. These are various VM tunables
+ * that we'll never support, but that a few misguided apps want to inspect and
+ * modify. We simply hardcode some default values and we'll lie about write
+ * success to these files.
+ */
+static void
+lxpr_read_sys_vm_dirty(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ uint_t val;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_DIRTY_BG_BYTES ||
+ lxpnp->lxpr_type == LXPR_SYS_VM_DIRTY_BG_RATIO ||
+ lxpnp->lxpr_type == LXPR_SYS_VM_DIRTY_BYTES ||
+ lxpnp->lxpr_type == LXPR_SYS_VM_DIRTY_EXP_CS ||
+ lxpnp->lxpr_type == LXPR_SYS_VM_DIRTY_RATIO ||
+ lxpnp->lxpr_type == LXPR_SYS_VM_DIRTYTIME_EXP_SEC ||
+ lxpnp->lxpr_type == LXPR_SYS_VM_DIRTY_WB_CS);
+
+ switch (lxpnp->lxpr_type) {
+ case LXPR_SYS_VM_DIRTY_BG_RATIO:
+ val = 10;
+ break;
+ case LXPR_SYS_VM_DIRTY_EXP_CS:
+ val = 3000;
+ break;
+ case LXPR_SYS_VM_DIRTY_RATIO:
+ val = 20;
+ break;
+ case LXPR_SYS_VM_DIRTYTIME_EXP_SEC:
+ val = 43200;
+ break;
+ case LXPR_SYS_VM_DIRTY_WB_CS:
+ val = 500;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "%u\n", val);
+}
+
+static void
+lxpr_read_sys_vm_max_map_cnt(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_MAX_MAP_CNT);
+ /* We don't limit mappings, just say we have a large limit. */
+ lxpr_uiobuf_printf(uiobuf, "%d\n", 16777215);
+}
+
+static void
+lxpr_read_sys_vm_minfr_kb(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_MINFR_KB);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+static void
+lxpr_read_sys_vm_nhpages(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_NHUGEP);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+static void
+lxpr_read_sys_vm_overcommit_mem(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_OVERCOMMIT_MEM);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+static void
+lxpr_read_sys_vm_swappiness(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_SWAPPINESS);
+ lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+/*
+ * lxpr_read_uptime(): read the contents of the "uptime" file.
+ *
+ * format is: "%.2lf, %.2lf",uptime_secs, idle_secs
+ * Use fixed point arithmetic to get 2 decimal places
+ */
+static void
+lxpr_read_uptime(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ ulong_t idle_cum = 0;
+ ulong_t cpu_count = 0;
+ ulong_t idle_s;
+ ulong_t idle_cs;
+ ulong_t up_s;
+ ulong_t up_cs;
+ hrtime_t birthtime;
+ hrtime_t centi_sec = 10000000; /* 10^7 */
+
+ ASSERT(lxpnp->lxpr_type == LXPR_UPTIME);
+
+ /* Calculate cumulative stats */
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ idle_cum += CPU_STATS(cp, sys.cpu_ticks_idle);
+ idle_cum += CPU_STATS(cp, sys.cpu_ticks_wait);
+ cpu_count += 1;
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+ mutex_exit(&cpu_lock);
+
+ /* Getting the Zone zsched process startup time */
+ birthtime = LXPTOZ(lxpnp)->zone_zsched->p_mstart;
+ up_cs = (gethrtime() - birthtime) / centi_sec;
+ up_s = up_cs / 100;
+ up_cs %= 100;
+
+ ASSERT(cpu_count > 0);
+ idle_cum /= cpu_count;
+ idle_s = idle_cum / hz;
+ idle_cs = idle_cum % hz;
+ idle_cs *= 100;
+ idle_cs /= hz;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%ld.%02d %ld.%02d\n", up_s, up_cs, idle_s, idle_cs);
+}
+
+/*
+ * Report a list of each cgroup subsystem supported by our emulated cgroup fs.
+ * This needs to exist for systemd to run but for now we don't report any
+ * cgroup subsystems as being installed. The commented example below shows
+ * how to print a subsystem entry.
+ */
+static void
+lxpr_read_cgroups(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\t%s\t%s\n",
+ "#subsys_name", "hierarchy", "num_cgroups", "enabled");
+
+ /*
+ * lxpr_uiobuf_printf(uiobuf, "%s\t%s\t%s\t%s\n",
+ * "cpu,cpuacct", "2", "1", "1");
+ */
+}
+
+/*
+ * Report the zone boot arguments.
+ */
+static void
+lxpr_read_cmdline(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ lxpr_uiobuf_printf(uiobuf, "%s\n", zone->zone_bootargs);
+}
+
+
+typedef enum {
+ LXCS_ALWAYS = 0,
+ LXCS_CPUID1_ECX,
+ LXCS_CPUID1_EDX,
+ LXCS_CPUID7_EBX,
+ LXCS_CPUID7_ECX,
+ LXCS_CPUID7_EDX,
+ LXCS_CPUIDD1_EAX,
+ LXCS_CPUIDX1_ECX,
+ LXCS_CPUIDX1_EDX,
+ LXCS_REG_MAX
+} lx_cpuinfo_source_t;
+
+typedef struct {
+ lx_cpuinfo_source_t lxcm_source;
+ uint32_t lxcm_flag;
+ const char *lxcm_name;
+} lx_cpuinfo_mapping_t;
+
+/*
+ * This listing is derived from the X86_FEATURE flags data in the Linux kernel.
+ * Some entries are missing detectino routines. They remain in the list,
+ * although commented out, to preserve proper order should they be fixed later.
+ */
+lx_cpuinfo_mapping_t lx_cpuinfo_mappings[] = {
+ /* CPUID EDX: */
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_FPU, "fpu" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_VME, "vme" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_DE, "de" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PSE, "pse" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_TSC, "tsc" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_MSR, "msr" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PAE, "pae" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_MCE, "mce" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_CX8, "cx8" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_APIC, "apic" },
+ /* reserved */
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_SEP, "sep" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_MTRR, "mtrr" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PGE, "pge" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_MCA, "mca" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_CMOV, "cmov" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PAT, "pat" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PSE36, "pse36" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PSN, "pn" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_CLFSH, "clflush" },
+ /* reserved */
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_DS, "dts" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_ACPI, "acpi" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_MMX, "mmx" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_FXSR, "fxsr" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_SSE, "sse" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_SSE2, "sse2" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_SS, "ss" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_HTT, "ht" },
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_TM, "tm" },
+ /* reserved */
+ { LXCS_CPUID1_EDX, CPUID_INTC_EDX_PBE, "pbe" },
+
+ /* AMD-defined CPU features, CPUID level 0x80000001, word 1 */
+#if defined(__amd64)
+ { LXCS_ALWAYS, 1, "syscall" },
+#endif
+ /* Present in the Linux listing but not in recent AMD docs: "mp" */
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_NX, "nx" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_MMXamd, "mmxext" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_FFXSR, "fxsr_opt" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_1GPG, "pdpe1gb" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_TSCP, "rdtscp" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_LM, "lm" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_3DNowx, "3dnowext" },
+ { LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_3DNow, "3dnow" },
+
+ /* CPUID ECX: */
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSE3, "pni" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_PCLMULQDQ, "pclmulqdq" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_DTES64, "dtes64" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_MON, "monitor" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_DSCPL, "ds_cpl" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_VMX, "vmx" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_SMX, "smx" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_EST, "est" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_TM2, "tm2" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSSE3, "ssse3" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_CID, "cid" },
+ { LXCS_CPUID1_ECX, 0x00000800, "sdbg" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_FMA, "fma" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_CX16, "cx16" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_ETPRD, "xtpr" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_PDCM, "pdcm" },
+ /* reserved */
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_PCID, "pcid" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_DCA, "dca" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSE4_1, "sse4_1" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSE4_2, "sse4_2" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_X2APIC, "x2apic" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_MOVBE, "movbe" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_POPCNT, "popcnt" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_TSCDL, "tsc_deadline_timer" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_AES, "aes" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_XSAVE, "xsave" },
+ /* osxsave */
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_AVX, "avx" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_F16C, "f16c" },
+ { LXCS_CPUID1_ECX, CPUID_INTC_ECX_RDRAND, "rdrand" },
+ /* not used */
+
+ /*
+ * Other features, Linux-defined mapping
+ * This range is used for feature bits which conflict or are synthesized
+ * Skipped:
+ * "recovery",
+ * "longrun",
+ * "lrti",
+ * "cxmmx",
+ * "k6_mtrr",
+ * "cyrix_arr",
+ * "centaur_mcr",
+ * "constant_tsc",
+ * "up",
+ * "arch_perfmon",
+ * "pebs",
+ * "bts",
+ * "rep_good",
+ * "nopl",
+ * "xtopology",
+ * "tsc_reliable",
+ * "nonstop_tsc",
+ * "extd_apicid",
+ * "amd_dcm",
+ * "aperfmperf",
+ * "eagerfpu",
+ * "nonstop_tsc_s3",
+ *
+ * "hypervisor",
+ * "rng",
+ * "rng_en",
+ * "ace",
+ * "ace_en",
+ * "ace2",
+ * "ace2_en",
+ * "phe",
+ * "phe_en",
+ * "pmm",
+ * "pmm_en",
+ */
+
+ /*
+ * More extended AMD flags: CPUID level 0x80000001, ecx, word 6
+ */
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_AHF64, "lahf_lm" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_CMP_LGCY, "cmp_legacy" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SVM, "svm" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_EAS, "extapic" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_CR8D, "cr8_legacy" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_LZCNT, "abm" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SSE4A, "sse4a" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_MAS, "misalignsse" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_3DNP, "3dnowprefetch" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_OSVW, "osvw" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_IBS, "ibs" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_XOP, "xop" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SKINIT, "skinit" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_WDT, "wdt" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_LWP, "lwp" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_FMA4, "fma4" },
+ { LXCS_CPUIDX1_ECX, 0x00020000, "tce" },
+
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_NIDMSR, "nodeid_msr" },
+
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_TBM, "tbm" },
+ { LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_TOPOEXT, "topoext" },
+ { LXCS_CPUIDX1_ECX, 0x00800000, "perfctr_core" },
+ { LXCS_CPUIDX1_ECX, 0x01000000, "perfctr_nb" },
+ { LXCS_CPUIDX1_ECX, 0x02000000, "bpext" },
+ { LXCS_CPUIDX1_ECX, 0x04000000, "perfctr_l2" },
+ { LXCS_CPUIDX1_ECX, 0x08000000, "mwaitx" },
+
+ /*
+ * Aux flags and virt bits.
+ * Skipped:
+ * "cpb",
+ * "epb",
+ * "hw_pstate",
+ * "proc_feedback",
+ * "intel_pt",
+ * "tpr_shadow",
+ * "vnmi",
+ * "flexpriority",
+ * "ept",
+ * "vpid",
+ * "vmmcall",
+ */
+
+ /*
+ * Intel-defined CPU features, CPUID level 0x00000007:0 (ebx), word 9
+ */
+ { LXCS_CPUID7_EBX, 0x00000001, "fsgsbase" },
+ { LXCS_CPUID7_EBX, 0x00000002, "tsc_adjust" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_BMI1, "bmi1" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_HLE, "hle" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX2, "avx2" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_SMEP, "smep" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_BMI2, "bmi2" },
+ { LXCS_CPUID7_EBX, 0x00000200, "erms" },
+ { LXCS_CPUID7_EBX, 0x00000400, "invpcid" },
+ { LXCS_CPUID7_EBX, 0x00000800, "rtm" },
+ { LXCS_CPUID7_EBX, 0x00001000, "cqm" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_MPX, "mpx" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX512F, "avx512f" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX512DQ, "avx512dq" },
+
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_RDSEED, "rdseed" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_ADX, "adx" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_SMAP, "smap" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX512IFMA, "avx512ifma" },
+
+ { LXCS_CPUID7_EBX, 0x00400000, "pcommit" },
+ { LXCS_CPUID7_EBX, 0x00800000, "clflushopt" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_CLWB, "clwb" },
+
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX512PF, "avx512pf" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX512ER, "avx512er" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX512CD, "avx512cd" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_SHA, "sha_ni" },
+
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX512BW, "avx512bw" },
+ { LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX512VL, "avx512vl" },
+
+ /*
+ * Intel-defined CPU features, CPUID level 0x00000007:0 (ecx)
+ */
+ { LXCS_CPUID7_ECX, CPUID_INTC_ECX_7_0_AVX512VBMI, "avx512vbmi" },
+ { LXCS_CPUID7_ECX, CPUID_INTC_ECX_7_0_AVX512VPOPCDQ,
+ "avx512_vpopcntdq" },
+
+ /*
+ * Intel-defined CPU features, CPUID level 0x00000007:0 (edx)
+ */
+ { LXCS_CPUID7_EDX, CPUID_INTC_EDX_7_0_AVX5124NNIW, "avx512_4nniw" },
+ { LXCS_CPUID7_EDX, CPUID_INTC_EDX_7_0_AVX5124FMAPS, "avx512_4fmaps" },
+
+ /*
+ * Extended state features, CPUID level 0x0000000d:1 (eax)
+ */
+ { LXCS_CPUIDD1_EAX, CPUID_INTC_EAX_D_1_XSAVEOPT, "xsaveopt" },
+ { LXCS_CPUIDD1_EAX, CPUID_INTC_EAX_D_1_XSAVEC, "xsavec" },
+ { LXCS_CPUIDD1_EAX, 0x00000004, "xgetbv1" },
+ { LXCS_CPUIDD1_EAX, CPUID_INTC_EAX_D_1_XSAVES, "xsaves" },
+
+ /*
+ * Skipped:
+ * "cqm_llc",
+ * "cqm_occup_llc",
+ * "clzero",
+ */
+
+ /*
+ * Thermal and Power Management Leaf, CPUID level 0x00000006 (eax)
+ * Skipped:
+ * "dtherm",
+ * "ida",
+ * "arat",
+ * "pln",
+ * "pts",
+ * "hwp",
+ * "hwp_notify",
+ * "hwp_act_window",
+ * "hwp_epp",
+ * "hwp_pkg_req",
+ */
+
+ /*
+ * AMD SVM Feature Identification, CPUID level 0x8000000a (edx)
+ * Skipped:
+ * "npt",
+ * "lbrv",
+ * "svm_lock",
+ * "nrip_save",
+ * "tsc_scale",
+ * "vmcb_clean",
+ * "flushbyasid",
+ * "decodeassists",
+ * "pausefilter",
+ * "pfthreshold",
+ */
+};
+
+#define LX_CPUINFO_MAPPING_MAX \
+ (sizeof (lx_cpuinfo_mappings) / sizeof (lx_cpuinfo_mappings[0]))
+
+static void
+lxpr_read_cpuinfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ int i;
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ char brandstr[CPU_IDSTRLEN];
+
+ ASSERT(lxpnp->lxpr_type == LXPR_CPUINFO);
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ struct cpuid_regs cpr;
+ uint32_t maxeax, xmaxeax, cpuid_res[LXCS_REG_MAX] = { 0 };
+
+ cpr.cp_eax = 0;
+ maxeax = cpuid_insn(cp, &cpr);
+ cpr.cp_eax = 0x80000000;
+ xmaxeax = cpuid_insn(cp, &cpr);
+
+ cpuid_res[LXCS_ALWAYS] = 1;
+ if (maxeax >= 1) {
+ cpr.cp_eax = 1;
+ (void) cpuid_insn(cp, &cpr);
+ cpuid_res[LXCS_CPUID1_ECX] = cpr.cp_ecx;
+ cpuid_res[LXCS_CPUID1_EDX] = cpr.cp_edx;
+ }
+ if (maxeax >= 7) {
+ cpr.cp_eax = 7;
+ (void) cpuid_insn(cp, &cpr);
+ cpuid_res[LXCS_CPUID7_EBX] = cpr.cp_ebx;
+ cpuid_res[LXCS_CPUID7_ECX] = cpr.cp_ecx;
+ cpuid_res[LXCS_CPUID7_EDX] = cpr.cp_edx;
+ }
+ if (maxeax >= 0xd) {
+ cpr.cp_eax = 0xd;
+ cpr.cp_ecx = 1;
+ (void) cpuid_insn(cp, &cpr);
+ cpuid_res[LXCS_CPUIDD1_EAX] = cpr.cp_eax;
+ }
+ if (xmaxeax >= 0x80000001) {
+ cpr.cp_eax = 0x80000001;
+ (void) cpuid_insn(cp, &cpr);
+ cpuid_res[LXCS_CPUIDX1_ECX] = cpr.cp_ecx;
+ cpuid_res[LXCS_CPUIDX1_EDX] = cpr.cp_edx;
+ }
+
+ (void) cpuid_getbrandstr(cp, brandstr, CPU_IDSTRLEN);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "processor\t: %d\n"
+ "vendor_id\t: %s\n"
+ "cpu family\t: %d\n"
+ "model\t\t: %d\n"
+ "model name\t: %s\n"
+ "stepping\t: %d\n"
+ "cpu MHz\t\t: %u.%03u\n",
+ cp->cpu_id, cpuid_getvendorstr(cp), cpuid_getfamily(cp),
+ cpuid_getmodel(cp), brandstr, cpuid_getstep(cp),
+ (uint32_t)(cpu_freq_hz / 1000000),
+ ((uint32_t)(cpu_freq_hz / 1000)) % 1000);
+
+ lxpr_uiobuf_printf(uiobuf, "cache size\t: %u KB\n",
+ getl2cacheinfo(cp, NULL, NULL, NULL) / 1024);
+
+ if (is_x86_feature(x86_featureset, X86FSET_HTT)) {
+ /*
+ * 'siblings' is used for HT-style threads
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "physical id\t: %lu\n"
+ "siblings\t: %u\n",
+ pg_plat_hw_instance_id(cp, PGHW_CHIP),
+ cpuid_get_ncpu_per_chip(cp));
+ }
+
+ /*
+ * Since we're relatively picky about running on older hardware,
+ * we can be somewhat cavalier about the answers to these ones.
+ *
+ * In fact, given the hardware we support, we just say:
+ *
+ * fdiv_bug : no (if we're on a 64-bit kernel)
+ * hlt_bug : no
+ * f00f_bug : no
+ * coma_bug : no
+ * wp : yes (write protect in supervsr mode)
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "fdiv_bug\t: %s\n"
+ "hlt_bug \t: no\n"
+ "f00f_bug\t: no\n"
+ "coma_bug\t: no\n"
+ "fpu\t\t: %s\n"
+ "fpu_exception\t: %s\n"
+ "cpuid level\t: %d\n"
+ "flags\t\t:",
+#if defined(__i386)
+ fpu_pentium_fdivbug ? "yes" : "no",
+#else
+ "no",
+#endif /* __i386 */
+ fpu_exists ? "yes" : "no", fpu_exists ? "yes" : "no",
+ maxeax);
+
+ /* Print CPUID feature flags */
+ for (i = 0; i < LX_CPUINFO_MAPPING_MAX; i++) {
+ lx_cpuinfo_mapping_t *lxm = &lx_cpuinfo_mappings[i];
+
+ ASSERT(lxm->lxcm_source < LXCS_REG_MAX);
+ if (cpuid_res[lxm->lxcm_source] & lxm->lxcm_flag) {
+ lxpr_uiobuf_printf(uiobuf, " %s",
+ lxm->lxcm_name);
+ }
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "\n\n");
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+}
+
+static void
+lxpr_read_fd(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_FD_FD);
+ lxpr_uiobuf_seterr(uiobuf, EFAULT);
+}
+
+static void
+lxpr_read_fdinfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ proc_t *p;
+ file_t *fp;
+ vnode_t *vp;
+ offset_t off;
+ short uf_flag;
+ int fd;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_FDINFO_FD);
+
+ p = lxpr_lock(lxpnp, NO_ZOMB);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ if ((p->p_flag & SSYS) || p->p_as == &kas) {
+ lxpr_uiobuf_seterr(uiobuf, EFAULT);
+ lxpr_unlock(p);
+ return;
+ }
+
+ fd = lxpnp->lxpr_desc;
+
+ fp = lxpr_getf(p, fd, &uf_flag);
+ if (fp == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, ENOENT);
+ lxpr_unlock(p);
+ return;
+ }
+ vp = fp->f_vnode;
+
+ /*
+ * Check that the offset value in the underlying file_t is plausible
+ * and reset to 0 if not.
+ */
+ if (fp->f_offset == -1) {
+ off = 0;
+ } else {
+ off = fp->f_offset;
+ if (VOP_SEEK(vp, 0, &off, NULL) != 0)
+ off = 0;
+ }
+
+ lxpr_uiobuf_printf(uiobuf, "pos:\t%ld\n", off);
+ lxpr_uiobuf_printf(uiobuf, "flags:\t0%o\n",
+ lxpr_open_flags_convert(uf_flag,
+ fp->f_flag2 << 16 | fp->f_flag));
+ lxpr_uiobuf_printf(uiobuf, "mnt_id:\t%u\n",
+ lxpr_get_mountid(zone, vp->v_vfsp));
+
+ /* Could show additional fields based on vp->v_type */
+
+ lxpr_releasef(p, fd);
+ lxpr_unlock(p);
+}
+
+/*
+ * Report a list of file systems loaded in the kernel. We only report the ones
+ * which we support and which may be checked by various components to see if
+ * they are loaded.
+ */
+static void
+lxpr_read_filesystems(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "autofs");
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "cgroup");
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "nfs");
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "proc");
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "sysfs");
+ lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "tmpfs");
+}
+
+/*
+ * Calculate the number of links in the task dir. Some code (e.g. chromium)
+ * depends on this value being accurate.
+ */
+static uint_t
+lxpr_count_taskdir(lxpr_node_t *lxpnp)
+{
+ proc_t *p;
+ uint_t cnt;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_TASKDIR);
+
+ p = lxpr_lock(lxpnp, ZOMB_OK);
+ if (p == NULL)
+ return (0);
+
+ cnt = lxpr_count_tasks(p);
+
+ lxpr_unlock(p);
+
+ /* Add the fixed entries ("." & "..") */
+ cnt += 2;
+ return (cnt);
+}
+
+/*
+ * lxpr_getattr(): Vnode operation for VOP_GETATTR()
+ */
+static int
+lxpr_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ register lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ extern uint_t nproc;
+ int error;
+
+ /*
+ * Return attributes of underlying vnode if ATTR_REAL
+ *
+ * but keep fd files with the symlink permissions
+ */
+ if (lxpnp->lxpr_realvp != NULL && (flags & ATTR_REAL)) {
+ vnode_t *rvp = lxpnp->lxpr_realvp;
+
+ /*
+ * withold attribute information to owner or root
+ */
+ if ((error = VOP_ACCESS(rvp, 0, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * now its attributes
+ */
+ if ((error = VOP_GETATTR(rvp, vap, flags, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * if it's a file in lx /proc/pid/fd/xx then set its
+ * mode and keep it looking like a symlink, fifo or socket
+ */
+ if (type == LXPR_PID_FD_FD) {
+ vap->va_mode = lxpnp->lxpr_mode;
+ vap->va_type = lxpnp->lxpr_realvp->v_type;
+ vap->va_size = 0;
+ vap->va_nlink = 1;
+ }
+ return (0);
+ }
+
+ /* Default attributes, that may be overridden below */
+ bzero(vap, sizeof (*vap));
+ vap->va_atime = vap->va_mtime = vap->va_ctime = lxpnp->lxpr_time;
+ vap->va_nlink = 1;
+ vap->va_type = vp->v_type;
+ vap->va_mode = lxpnp->lxpr_mode;
+ vap->va_fsid = vp->v_vfsp->vfs_dev;
+ vap->va_blksize = DEV_BSIZE;
+ vap->va_uid = lxpnp->lxpr_uid;
+ vap->va_gid = lxpnp->lxpr_gid;
+ vap->va_nodeid = lxpnp->lxpr_ino;
+
+ switch (type) {
+ case LXPR_PROCDIR:
+ vap->va_nlink = nproc + 2 + PROCDIRFILES;
+ vap->va_size = (nproc + 2 + PROCDIRFILES) * LXPR_SDSIZE;
+ break;
+ case LXPR_PIDDIR:
+ vap->va_nlink = PIDDIRFILES;
+ vap->va_size = PIDDIRFILES * LXPR_SDSIZE;
+ break;
+ case LXPR_PID_TASKDIR:
+ vap->va_nlink = lxpr_count_taskdir(lxpnp);
+ vap->va_size = vap->va_nlink * LXPR_SDSIZE;
+ break;
+ case LXPR_PID_TASK_IDDIR:
+ vap->va_nlink = TIDDIRFILES;
+ vap->va_size = TIDDIRFILES * LXPR_SDSIZE;
+ break;
+ case LXPR_SELF:
+ vap->va_uid = crgetruid(curproc->p_cred);
+ vap->va_gid = crgetrgid(curproc->p_cred);
+ break;
+ case LXPR_PID_FD_FD:
+ case LXPR_PID_TID_FD_FD:
+ /*
+ * Restore VLNK type for lstat-type activity.
+ * See lxpr_readlink for more details.
+ */
+ if ((flags & FOLLOW) == 0)
+ vap->va_type = VLNK;
+ case LXPR_PID_FDINFO_FD:
+ case LXPR_PID_TID_FDINFO_FD:
+ /* Linux leaves the file size for these as 0 */
+ break;
+ default:
+ break;
+ }
+
+ vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
+ return (0);
+}
+
+/*
+ * lxpr_access(): Vnode operation for VOP_ACCESS()
+ */
+static int
+lxpr_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
+{
+ return (lxpr_doaccess(VTOLXP(vp), B_FALSE, mode, flags, cr, ct));
+}
+
+/*
+ * This makes up the bulk of the logic for lxpr_access. An extra parameter
+ * ('shallow') is present to differentiate checks that must pass muster against
+ * an underlying resource (lxpr_realvp) and those that are only concerned with
+ * permission to the process.
+ */
+static int
+lxpr_doaccess(lxpr_node_t *lxpnp, boolean_t shallow, int mode, int flags,
+ cred_t *cr, caller_context_t *ct)
+{
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ boolean_t allow_pid_access = B_FALSE;
+ int shift = 0;
+ proc_t *tp;
+
+ /*
+ * lx /proc is primarily a read only file system
+ * We handle LXPR_SYSDIR as a special case. At least 'systemd' expects
+ * access() to report /proc/sys is writable, but we can't do that in
+ * lxpr_is_writable since it breaks other code paths that check if they
+ * can write there.
+ */
+ if ((mode & VWRITE) && !lxpr_is_writable(type)) {
+ if (type != LXPR_SYSDIR)
+ return (EROFS);
+ }
+
+ if (type == LXPR_PIDDIR) {
+ return (0);
+ }
+ if (lxpnp->lxpr_pid != 0) {
+ if ((tp = lxpr_lock(lxpnp, ZOMB_OK)) == NULL) {
+ return (ENOENT);
+ }
+ if (tp == curproc || secpolicy_proc_access(cr) == 0 ||
+ priv_proc_cred_perm(cr, tp, NULL, mode) == 0) {
+ allow_pid_access = B_TRUE;
+ }
+ lxpr_unlock(tp);
+ switch (type) {
+ case LXPR_PID_CGROUP:
+ case LXPR_PID_CMDLINE:
+ case LXPR_PID_COMM:
+ case LXPR_PID_LIMITS:
+ case LXPR_PID_LOGINUID:
+ case LXPR_PID_MOUNTINFO:
+ case LXPR_PID_MOUNTS:
+ case LXPR_PID_OOM_SCR_ADJ:
+ case LXPR_PID_STAT:
+ case LXPR_PID_STATM:
+ case LXPR_PID_STATUS:
+ case LXPR_PID_TASKDIR:
+ case LXPR_PID_TASK_IDDIR:
+ case LXPR_PID_TID_CGROUP:
+ case LXPR_PID_TID_CMDLINE:
+ case LXPR_PID_TID_COMM:
+ case LXPR_PID_TID_LIMITS:
+ case LXPR_PID_TID_LOGINUID:
+ case LXPR_PID_TID_MOUNTINFO:
+ case LXPR_PID_TID_OOM_SCR_ADJ:
+ case LXPR_PID_TID_STAT:
+ case LXPR_PID_TID_STATM:
+ case LXPR_PID_TID_STATUS:
+ /*
+ * These entries are accessible to any process on the
+ * system which wishes to query them.
+ */
+ break;
+ default:
+ /*
+ * All other entries under the pid/tid hierarchy
+ * require proper authorization to be accessed.
+ */
+ if (!allow_pid_access) {
+ return (EACCES);
+ }
+ break;
+ }
+ }
+
+ /*
+ * If this entry has an underlying vnode, rely upon its access checks.
+ * Skip this if a shallow check has been requested.
+ */
+ if (lxpnp->lxpr_realvp != NULL && !shallow) {
+ return (VOP_ACCESS(lxpnp->lxpr_realvp, mode, flags, cr, ct));
+ }
+
+ /*
+ * Allow access to those (root) possessing the correct privilege or
+ * already authorized against a pid-specific resource.
+ */
+ if (allow_pid_access || secpolicy_proc_access(cr) == 0) {
+ return (0);
+ }
+
+ /*
+ * Access check is based on only one of owner, group, public. If not
+ * owner, then check group. If not a member of the group, then check
+ * public access.
+ */
+ if (crgetuid(cr) != lxpnp->lxpr_uid) {
+ shift += 3;
+ if (!groupmember((uid_t)lxpnp->lxpr_gid, cr))
+ shift += 3;
+ }
+
+ mode &= ~(lxpnp->lxpr_mode << shift);
+
+ if (mode == 0)
+ return (0);
+
+ return (EACCES);
+}
+
+static vnode_t *
+lxpr_lookup_not_a_dir(vnode_t *dp, char *comp)
+{
+ return (NULL);
+}
+
+/*
+ * lxpr_lookup(): Vnode operation for VOP_LOOKUP()
+ */
+static int
+lxpr_lookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ lxpr_node_t *lxpnp = VTOLXP(dp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ int error;
+
+ ASSERT(dp->v_type == VDIR);
+ ASSERT(type < LXPR_NFILES);
+
+ /*
+ * we should never get here because the lookup
+ * is done on the realvp for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR);
+
+ /*
+ * restrict lookup permission to owner or root
+ */
+ if ((error = lxpr_access(dp, VEXEC, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * Just return the parent vnode if that's where we are trying to go.
+ */
+ if (strcmp(comp, "..") == 0) {
+ VN_HOLD(lxpnp->lxpr_parent);
+ *vpp = lxpnp->lxpr_parent;
+ return (0);
+ }
+
+ /*
+ * Special handling for directory searches. Note: null component name
+ * denotes that the current directory is being searched.
+ */
+ if ((dp->v_type == VDIR) && (*comp == '\0' || strcmp(comp, ".") == 0)) {
+ VN_HOLD(dp);
+ *vpp = dp;
+ return (0);
+ }
+
+ *vpp = (lxpr_lookup_function[type](dp, comp));
+ return ((*vpp == NULL) ? ENOENT : 0);
+}
+
+/*
+ * Do a sequential search on the given directory table
+ */
+static vnode_t *
+lxpr_lookup_common(vnode_t *dp, char *comp, proc_t *p,
+ lxpr_dirent_t *dirtab, int dirtablen)
+{
+ lxpr_node_t *lxpnp;
+ int count;
+
+ for (count = 0; count < dirtablen; count++) {
+ if (strcmp(dirtab[count].d_name, comp) == 0) {
+ lxpnp = lxpr_getnode(dp, dirtab[count].d_type, p, 0);
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+ return (dp);
+ }
+ }
+ return (NULL);
+}
+
+static vnode_t *
+lxpr_lookup_piddir(vnode_t *dp, char *comp)
+{
+ proc_t *p;
+
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PIDDIR);
+
+ p = lxpr_lock(VTOLXP(dp), ZOMB_OK);
+ if (p == NULL)
+ return (NULL);
+
+ dp = lxpr_lookup_common(dp, comp, p, piddir, PIDDIRFILES);
+
+ lxpr_unlock(p);
+
+ return (dp);
+}
+
+/*
+ * Lookup one of the process's task ID's.
+ */
+static vnode_t *
+lxpr_lookup_taskdir(vnode_t *dp, char *comp)
+{
+ lxpr_node_t *dlxpnp = VTOLXP(dp);
+ lxpr_node_t *lxpnp;
+ proc_t *p;
+ uint_t tid;
+ int c;
+ kthread_t *t;
+
+ ASSERT(dlxpnp->lxpr_type == LXPR_PID_TASKDIR);
+
+ /*
+ * convert the string rendition of the filename to a thread ID
+ */
+ tid = 0;
+ while ((c = *comp++) != '\0') {
+ int otid;
+ if (c < '0' || c > '9')
+ return (NULL);
+
+ otid = tid;
+ tid = 10 * tid + c - '0';
+ /* integer overflow */
+ if (tid / 10 != otid)
+ return (NULL);
+ }
+
+ /*
+ * get the proc to work with and lock it
+ */
+ p = lxpr_lock_pid(dlxpnp, tid, NO_ZOMB, &t);
+ if (p == NULL)
+ return (NULL);
+
+ /*
+ * Bail if this is a system process.
+ */
+ if (p->p_as == &kas) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ if (p->p_brand != &lx_brand) {
+ /*
+ * Only the main thread is visible for non-branded processes.
+ */
+ t = p->p_tlist;
+ if (tid != p->p_pid || t == NULL) {
+ t = NULL;
+ }
+ } else if (t != NULL) {
+ /*
+ * Disallow any access to aio in-kernel worker threads.
+ * To prevent a potential race while looking at the lwp data
+ * for an exiting thread, we clear the TP_KTHREAD bit in
+ * lx_cleanlwp() while the p_lock is held.
+ */
+ if ((t->t_proc_flag & TP_KTHREAD) != 0) {
+ lx_lwp_data_t *lwpd;
+
+ VERIFY((lwpd = ttolxlwp(t)) != NULL);
+ if ((lwpd->br_lwp_flags & BR_AIO_LWP) != 0) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+ }
+ }
+
+ if (t == NULL) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ /*
+ * Allocate and fill in a new lx /proc taskid node.
+ * Instead of the last arg being a fd, it is a tid.
+ */
+ lxpnp = lxpr_getnode(dp, LXPR_PID_TASK_IDDIR, p, tid);
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+ lxpr_unlock(p);
+ return (dp);
+}
+
+/*
+ * Lookup one of the process's task ID's.
+ */
+static vnode_t *
+lxpr_lookup_task_tid_dir(vnode_t *dp, char *comp)
+{
+ lxpr_node_t *dlxpnp = VTOLXP(dp);
+ lxpr_node_t *lxpnp;
+ proc_t *p;
+ kthread_t *t;
+ int i;
+
+ ASSERT(dlxpnp->lxpr_type == LXPR_PID_TASK_IDDIR);
+
+ /*
+ * get the proc to work with and lock it
+ */
+ p = lxpr_lock_pid(dlxpnp, dlxpnp->lxpr_desc, NO_ZOMB, &t);
+ if (p == NULL)
+ return (NULL);
+
+ /*
+ * Bail if this is a system process.
+ */
+ if (p->p_as == &kas) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ /*
+ * allocate and fill in the new lx /proc taskid dir node
+ */
+ for (i = 0; i < TIDDIRFILES; i++) {
+ if (strcmp(tiddir[i].d_name, comp) == 0) {
+ lxpnp = lxpr_getnode(dp, tiddir[i].d_type, p,
+ dlxpnp->lxpr_desc);
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+ lxpr_unlock(p);
+ return (dp);
+ }
+ }
+
+ lxpr_unlock(p);
+ return (NULL);
+}
+
+/*
+ * Lookup one of the process's open files.
+ */
+static vnode_t *
+lxpr_lookup_fddir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PID_FDDIR ||
+ VTOLXP(dp)->lxpr_type == LXPR_PID_TID_FDDIR);
+
+ return (lxpr_lookup_fdnode(dp, comp));
+}
+
+static vnode_t *
+lxpr_lookup_fdinfodir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PID_FDINFODIR ||
+ VTOLXP(dp)->lxpr_type == LXPR_PID_TID_FDINFODIR);
+
+ return (lxpr_lookup_fdinfonode(dp, comp));
+}
+
+static vnode_t *
+lxpr_lookup_netdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_NETDIR);
+
+ dp = lxpr_lookup_common(dp, comp, NULL, netdir, NETDIRFILES);
+
+ return (dp);
+}
+
+static vnode_t *
+lxpr_lookup_procdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PROCDIR);
+
+ /*
+ * We know all the names of files & dirs in our file system structure
+ * except those that are pid names. These change as pids are created/
+ * deleted etc., so we just look for a number as the first char to see
+ * if we are we doing pid lookups.
+ *
+ * Don't need to check for "self" as it is implemented as a symlink
+ */
+ if (*comp >= '0' && *comp <= '9') {
+ pid_t pid = 0;
+ lxpr_node_t *lxpnp = NULL;
+ vnode_t *vp;
+ proc_t *p;
+ kthread_t *t;
+ int c;
+
+ while ((c = *comp++) != '\0')
+ pid = 10 * pid + c - '0';
+
+ /*
+ * Can't continue if the process is still loading or it doesn't
+ * really exist yet (or maybe it just died!)
+ */
+ p = lxpr_lock_pid(VTOLXP(dp), pid, ZOMB_OK, &t);
+ if (p == NULL)
+ return (NULL);
+
+ if (secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ /*
+ * Allocate and populate a new LX /proc node.
+ *
+ * Directory entries for non-main threads can be looked up as
+ * /proc/<tid> despite the fact that they do not appear in the
+ * readdir output. Record the lookup pid (tid) so that later
+ * operations can be aware of this context.
+ */
+ lxpnp = lxpr_getnode(dp, LXPR_PIDDIR, p, pid);
+
+ lxpr_unlock(p);
+ vp = LXPTOV(lxpnp);
+ ASSERT(vp != NULL);
+
+ return (vp);
+ }
+
+ /* Lookup fixed names */
+ return (lxpr_lookup_common(dp, comp, NULL, lx_procdir, PROCDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sysdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYSDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sysdir, SYSDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_kerneldir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_KERNELDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_kerneldir,
+ SYS_KERNELDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_kdir_randdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_KERNEL_RANDDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_randdir,
+ SYS_RANDDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_netdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_NETDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_netdir,
+ SYS_NETDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_net_coredir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_NET_COREDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_net_coredir,
+ SYS_NET_COREDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_net_ipv4dir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_NET_IPV4DIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_net_ipv4dir,
+ SYS_NET_IPV4DIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_vmdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_VMDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_vmdir,
+ SYS_VMDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_fsdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_FSDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_fsdir,
+ SYS_FSDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_fs_inotifydir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_FS_INOTIFYDIR);
+ return (lxpr_lookup_common(dp, comp, NULL, sys_fs_inotifydir,
+ SYS_FS_INOTIFYDIRFILES));
+}
+
+/*
+ * lxpr_readdir(): Vnode operation for VOP_READDIR()
+ */
+static int
+lxpr_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ lxpr_node_t *lxpnp = VTOLXP(dp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ ssize_t uresid;
+ off_t uoffset;
+ int error;
+
+ ASSERT(dp->v_type == VDIR);
+ ASSERT(type < LXPR_NFILES);
+
+ /*
+ * we should never get here because the readdir
+ * is done on the realvp for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR);
+
+ /*
+ * restrict readdir permission to owner or root
+ */
+ if ((error = lxpr_access(dp, VREAD, 0, cr, ct)) != 0)
+ return (error);
+
+ uoffset = uiop->uio_offset;
+ uresid = uiop->uio_resid;
+
+ /* can't do negative reads */
+ if (uoffset < 0 || uresid <= 0)
+ return (EINVAL);
+
+ /* can't read directory entries that don't exist! */
+ if (uoffset % LXPR_SDSIZE)
+ return (ENOENT);
+
+ return (lxpr_readdir_function[type](lxpnp, uiop, eofp));
+}
+
+static int
+lxpr_readdir_not_a_dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ return (ENOTDIR);
+}
+
+/*
+ * This has the common logic for returning directory entries
+ */
+static int
+lxpr_readdir_common(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp,
+ lxpr_dirent_t *dirtab, int dirtablen)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+
+ oresid = uiop->uio_resid;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Satisfy user request
+ */
+ while ((uresid = uiop->uio_resid) > 0) {
+ int dirindex;
+ off_t uoffset;
+ int reclen;
+ int error;
+
+ uoffset = uiop->uio_offset;
+ dirindex = (uoffset / LXPR_SDSIZE) - 2;
+
+ if (uoffset == 0) {
+
+ dirent->d_ino = lxpnp->lxpr_ino;
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '\0';
+ reclen = DIRENT64_RECLEN(1);
+
+ } else if (uoffset == LXPR_SDSIZE) {
+
+ dirent->d_ino = lxpr_parentinode(lxpnp);
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '.';
+ dirent->d_name[2] = '\0';
+ reclen = DIRENT64_RECLEN(2);
+
+ } else if (dirindex >= 0 && dirindex < dirtablen) {
+ int slen = strlen(dirtab[dirindex].d_name);
+
+ dirent->d_ino = lxpr_inode(dirtab[dirindex].d_type,
+ lxpnp->lxpr_pid, 0);
+
+ VERIFY(slen < LXPNSIZ);
+ (void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+ reclen = DIRENT64_RECLEN(slen);
+
+ } else {
+ /* Run out of table entries */
+ if (eofp) {
+ *eofp = 1;
+ }
+ return (0);
+ }
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ /*
+ * if the size of the data to transfer is greater
+ * that that requested then we can't do it this transfer.
+ */
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid) {
+ return (EINVAL);
+ }
+ break;
+ }
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+ * by the same amount. But we want uiop->uio_offset to change
+ * in increments of LXPR_SDSIZE, which is different from the
+ * number of bytes being returned to the user. So we set
+ * uiop->uio_offset separately, ignoring what uiomove() does.
+ */
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ return (error);
+
+ uiop->uio_offset = uoffset + LXPR_SDSIZE;
+ }
+
+ /* Have run out of space, but could have just done last table entry */
+ if (eofp) {
+ *eofp =
+ (uiop->uio_offset >= ((dirtablen+2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+ return (0);
+}
+
+
+static int
+lxpr_readdir_procdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+ off_t uoffset;
+ zone_t *zone;
+ int error;
+ int ceof;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PROCDIR);
+
+ oresid = uiop->uio_resid;
+ zone = LXPTOZ(lxpnp);
+
+ /*
+ * We return directory entries in the order: "." and ".." then the
+ * unique lxproc files, then the directories corresponding to the
+ * running processes. We have defined this as the ordering because
+ * it allows us to more easily keep track of where we are betwen calls
+ * to getdents(). If the number of processes changes between calls
+ * then we can't lose track of where we are in the lxproc files.
+ */
+
+ /* Do the fixed entries */
+ error = lxpr_readdir_common(lxpnp, uiop, &ceof, lx_procdir,
+ PROCDIRFILES);
+
+ /* Finished if we got an error or if we couldn't do all the table */
+ if (error != 0 || ceof == 0)
+ return (error);
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /* Do the process entries */
+ while ((uresid = uiop->uio_resid) > 0) {
+ proc_t *p;
+ pid_t pid, raw_pid;
+ int len;
+ int reclen;
+ int i;
+
+ uoffset = uiop->uio_offset;
+
+ /*
+ * Stop when entire proc table has been examined.
+ */
+ i = (uoffset / LXPR_SDSIZE) - 2 - PROCDIRFILES;
+ if (i < 0 || i >= v.v_proc) {
+ /* Run out of table entries */
+ if (eofp) {
+ *eofp = 1;
+ }
+ return (0);
+ }
+ mutex_enter(&pidlock);
+
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, a PID of 0, the
+ * zsched process for the zone, and anything the security
+ * policy doesn't allow us to look at.
+ */
+ if ((p = pid_entry(i)) == NULL || p->p_stat == SIDL ||
+ p->p_pid == 0 || p->p_zone != zone ||
+ p == zone->zone_zsched ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+ mutex_exit(&pidlock);
+ goto next;
+ }
+
+ /* Translate the pid (e.g. initpid to 1) */
+ lxpr_fixpid(LXPTOZ(lxpnp), p, &pid, NULL);
+ raw_pid = p->p_pid;
+
+ ASSERT(p->p_stat != 0);
+
+ mutex_exit(&pidlock);
+
+ dirent->d_ino = lxpr_inode(LXPR_PIDDIR, raw_pid, 0);
+ len = snprintf(dirent->d_name, LXPNSIZ, "%d", pid);
+ ASSERT(len < LXPNSIZ);
+ reclen = DIRENT64_RECLEN(len);
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ /*
+ * if the size of the data to transfer is greater
+ * that that requested then we can't do it this transfer.
+ */
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid)
+ return (EINVAL);
+ break;
+ }
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+ * by the same amount. But we want uiop->uio_offset to change
+ * in increments of LXPR_SDSIZE, which is different from the
+ * number of bytes being returned to the user. So we set
+ * uiop->uio_offset separately, in the increment of this for
+ * the loop, ignoring what uiomove() does.
+ */
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ return (error);
+next:
+ uiop->uio_offset = uoffset + LXPR_SDSIZE;
+ }
+
+ if (eofp != NULL) {
+ *eofp = (uiop->uio_offset >=
+ ((v.v_proc + PROCDIRFILES + 2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+
+ return (0);
+}
+
+static int
+lxpr_readdir_piddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ proc_t *p;
+ int err;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PIDDIR);
+
+ /* can't read its contents if it died */
+ if ((p = lxpr_lock(lxpnp, ZOMB_OK)) == NULL) {
+ return (ENOENT);
+ }
+ err = lxpr_readdir_common(lxpnp, uiop, eofp, piddir, PIDDIRFILES);
+ lxpr_unlock(p);
+ return (err);
+}
+
+static int
+lxpr_readdir_netdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_NETDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, netdir, NETDIRFILES));
+}
+
+static int
+lxpr_readdir_taskdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+ off_t uoffset;
+ int error, ceof, tiddirsize, tasknum;
+ proc_t *p;
+ kthread_t *t;
+ boolean_t branded;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_TASKDIR);
+
+ oresid = uiop->uio_resid;
+
+ p = lxpr_lock(lxpnp, ZOMB_OK);
+ if (p == NULL) {
+ return (ENOENT);
+ }
+
+ /*
+ * Just emit static entries for system processes and zombies.
+ */
+ if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+ (p->p_as == &kas)) {
+ lxpr_unlock(p);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, 0, 0));
+ }
+
+ /*
+ * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+ * going away while we iterate over its threads.
+ */
+ tiddirsize = p->p_lwpcnt;
+ branded = (p->p_brand == &lx_brand);
+ mutex_exit(&p->p_lock);
+
+ /* Do the fixed entries (in this case just "." & "..") */
+ error = lxpr_readdir_common(lxpnp, uiop, &ceof, 0, 0);
+
+ /* Finished if we got an error or if we couldn't do all the table */
+ if (error != 0 || ceof == 0)
+ goto out;
+
+ if ((t = p->p_tlist) == NULL) {
+ if (eofp != NULL)
+ *eofp = 1;
+ goto out;
+ }
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Loop until user's request is satisfied or until all thread's have
+ * been returned.
+ */
+ for (tasknum = 0; (uresid = uiop->uio_resid) > 0; tasknum++) {
+ int i, reclen, len;
+ uint_t emul_tid;
+ lx_lwp_data_t *lwpd;
+
+ uoffset = uiop->uio_offset;
+
+ /*
+ * Stop at the end of the thread list
+ */
+ i = (uoffset / LXPR_SDSIZE) - 2;
+ if (i < 0 || i >= tiddirsize) {
+ if (eofp) {
+ *eofp = 1;
+ }
+ goto out;
+ }
+
+ if (i != tasknum)
+ goto next;
+
+ if (!branded) {
+ /*
+ * Emulating the goofy linux task model is impossible
+ * to do for native processes. We can compromise by
+ * presenting only the main thread to the consumer.
+ */
+ emul_tid = p->p_pid;
+ } else {
+ if ((lwpd = ttolxlwp(t)) == NULL) {
+ goto next;
+ }
+ /* Don't show aio kernel worker threads */
+ if ((t->t_proc_flag & TP_KTHREAD) != 0 &&
+ (lwpd->br_lwp_flags & BR_AIO_LWP) != 0) {
+ goto next;
+ }
+ emul_tid = lwpd->br_pid;
+ /*
+ * Convert pid to Linux default of 1 if we're the
+ * zone's init.
+ */
+ if (emul_tid == LXPTOZ(lxpnp)->zone_proc_initpid)
+ emul_tid = 1;
+ }
+
+ dirent->d_ino = lxpr_inode(LXPR_PID_TASK_IDDIR, p->p_pid,
+ emul_tid);
+ len = snprintf(dirent->d_name, LXPNSIZ, "%d", emul_tid);
+ ASSERT(len < LXPNSIZ);
+ reclen = DIRENT64_RECLEN(len);
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid)
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+ * by the same amount. But we want uiop->uio_offset to change
+ * in increments of LXPR_SDSIZE, which is different from the
+ * number of bytes being returned to the user. So we set
+ * uiop->uio_offset separately, in the increment of this for
+ * the loop, ignoring what uiomove() does.
+ */
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ goto out;
+
+next:
+ uiop->uio_offset = uoffset + LXPR_SDSIZE;
+
+ if ((t = t->t_forw) == p->p_tlist || !branded) {
+ if (eofp != NULL)
+ *eofp = 1;
+ goto out;
+ }
+ }
+
+ if (eofp != NULL)
+ *eofp = 0;
+
+out:
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ return (error);
+}
+
+static int
+lxpr_readdir_task_tid_dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ proc_t *p;
+ kthread_t *t;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_TASK_IDDIR);
+
+ /* Confirm that process and thread are still present */
+ p = lxpr_lock_pid(lxpnp, lxpnp->lxpr_desc, NO_ZOMB, &t);
+ if (p == NULL) {
+ return (ENOENT);
+ }
+ lxpr_unlock(p);
+
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, tiddir, TIDDIRFILES));
+}
+
+static int
+lxpr_readdir_fdlist(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp,
+ lxpr_nodetype_t inodetype)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+ off_t uoffset;
+ int error, ceof, fddirsize;
+ proc_t *p;
+ uf_info_t *fip;
+
+ ASSERT(
+ (inodetype == LXPR_PID_FD_FD && (
+ lxpnp->lxpr_type == LXPR_PID_FDDIR ||
+ lxpnp->lxpr_type == LXPR_PID_TID_FDDIR)) ||
+ (inodetype == LXPR_PID_FDINFO_FD && (
+ lxpnp->lxpr_type == LXPR_PID_FDINFODIR ||
+ lxpnp->lxpr_type == LXPR_PID_TID_FDINFODIR)));
+
+ oresid = uiop->uio_resid;
+
+ p = lxpr_lock(lxpnp, ZOMB_OK);
+ if (p == NULL)
+ return (ENOENT);
+
+ /*
+ * For exiting/exited processes or those belonging to the system, only
+ * emit the fixed entries.
+ */
+ if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+ (p->p_as == &kas)) {
+ lxpr_unlock(p);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, 0, 0));
+ }
+
+ /*
+ * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+ * going away while we iterate over its fi_list.
+ */
+ mutex_exit(&p->p_lock);
+
+ /* Get open file info */
+ fip = (&(p)->p_user.u_finfo);
+ mutex_enter(&fip->fi_lock);
+ fddirsize = fip->fi_nfiles;
+
+ /* Do the fixed entries (in this case just "." & "..") */
+ error = lxpr_readdir_common(lxpnp, uiop, &ceof, 0, 0);
+
+ /* Finished if we got an error or if we couldn't do all the table */
+ if (error != 0 || ceof == 0)
+ goto out;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Loop until user's request is satisfied or until
+ * all file descriptors have been examined.
+ */
+ for (; (uresid = uiop->uio_resid) > 0;
+ uiop->uio_offset = uoffset + LXPR_SDSIZE) {
+ int reclen;
+ int fd;
+ int len;
+
+ uoffset = uiop->uio_offset;
+
+ /*
+ * Stop at the end of the fd list
+ */
+ fd = (uoffset / LXPR_SDSIZE) - 2;
+ if (fd < 0 || fd >= fddirsize) {
+ if (eofp) {
+ *eofp = 1;
+ }
+ goto out;
+ }
+
+ if (fip->fi_list[fd].uf_file == NULL)
+ continue;
+
+ dirent->d_ino = lxpr_inode(inodetype, p->p_pid, fd);
+ len = snprintf(dirent->d_name, LXPNSIZ, "%d", fd);
+ ASSERT(len < LXPNSIZ);
+ reclen = DIRENT64_RECLEN(len);
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid)
+ error = EINVAL;
+ goto out;
+ }
+
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ goto out;
+ }
+
+ if (eofp != NULL) {
+ *eofp =
+ (uiop->uio_offset >= ((fddirsize+2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+
+out:
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ return (error);
+}
+
+static int
+lxpr_readdir_fddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ return (lxpr_readdir_fdlist(lxpnp, uiop, eofp, LXPR_PID_FD_FD));
+}
+
+static int
+lxpr_readdir_fdinfodir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ return (lxpr_readdir_fdlist(lxpnp, uiop, eofp, LXPR_PID_FDINFO_FD));
+}
+
+static int
+lxpr_readdir_sysdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYSDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sysdir, SYSDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_fsdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FSDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_fsdir,
+ SYS_FSDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_fs_inotifydir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFYDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_fs_inotifydir,
+ SYS_FS_INOTIFYDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_kerneldir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNELDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_kerneldir,
+ SYS_KERNELDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_kdir_randdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_RANDDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_randdir,
+ SYS_RANDDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_netdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NETDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_netdir,
+ SYS_NETDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_net_coredir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_COREDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_net_coredir,
+ SYS_NET_COREDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_net_ipv4dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4DIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_net_ipv4dir,
+ SYS_NET_IPV4DIRFILES));
+}
+
+static int
+lxpr_readdir_sys_vmdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_VMDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_vmdir,
+ SYS_VMDIRFILES));
+}
+
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+
+/*
+ * Obtain a numeric value from the null-terminated input string.
+ * We don't have strtok in the kernel, so tokenize this ourselves and
+ * validate the input.
+ */
+static int
+lxpr_tokenize_num(char *str, long *pv, char **ep)
+{
+ char *pstart, *pc, c, *endptr;
+ long v;
+
+ for (pc = str; isspace(*pc); pc++)
+ ;
+
+ for (pstart = pc; isdigit(*pc); pc++)
+ ;
+ if (pc == pstart || (!isspace(*pc) && *pc != '\0'))
+ return (EINVAL);
+ c = *pc;
+ *pc = '\0';
+
+ if (ddi_strtol(pstart, &endptr, 10, &v) != 0) {
+ *pc = c;
+ return (EINVAL);
+ }
+ if (*endptr != '\0') {
+ *pc = c;
+ return (EINVAL);
+ }
+
+ if (pv != NULL)
+ *pv = v;
+ if (ep != NULL)
+ *ep = ++pc;
+
+ return (0);
+}
+
+static int
+lxpr_write_tcp_property(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct, char *prop,
+ int (*xlate)(char *, int))
+{
+ int error;
+ int res = 0;
+ size_t olen;
+ char val[16]; /* big enough for a uint numeric string */
+ netstack_t *ns;
+ mod_prop_info_t *ptbl = NULL;
+ mod_prop_info_t *pinfo = NULL;
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (val) - 1)
+ return (EINVAL);
+
+ bzero(val, sizeof (val));
+ error = uiomove(val, olen, UIO_WRITE, uio);
+ if (error != 0)
+ return (error);
+
+ if (val[olen - 1] == '\n')
+ val[olen - 1] = '\0';
+
+ if (val[0] == '\0') /* no input */
+ return (EINVAL);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL)
+ return (EINVAL);
+
+ if (xlate != NULL && xlate(val, sizeof (val)) != 0) {
+ netstack_rele(ns);
+ return (EINVAL);
+ }
+
+ ptbl = ns->netstack_tcp->tcps_propinfo_tbl;
+ pinfo = mod_prop_lookup(ptbl, prop, MOD_PROTO_TCP);
+ if (pinfo == NULL || pinfo->mpi_setf(ns, cr, pinfo, NULL, val, 0) != 0)
+ res = EINVAL;
+
+ netstack_rele(ns);
+ return (res);
+}
+
+static int
+lxpr_write_sys_net_core_somaxc(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_CORE_SOMAXCON);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "_conn_req_max_q", NULL));
+}
+
+static int
+lxpr_xlate_sec2ms(char *val, int size)
+{
+ long sec;
+ char *ep;
+
+ if (lxpr_tokenize_num(val, &sec, &ep) != 0)
+ return (EINVAL);
+ if (*ep != '\0')
+ return (EINVAL);
+ if (snprintf(val, size, "%ld", sec * 1000) >= size)
+ return (EINVAL);
+ return (0);
+}
+
+static int
+lxpr_xlate_ka_intvl(char *val, int size)
+{
+ long sec;
+ char *ep;
+
+ if (lxpr_tokenize_num(val, &sec, &ep) != 0)
+ return (EINVAL);
+ if (*ep != '\0')
+ return (EINVAL);
+ if (snprintf(val, size, "%ld", sec * 1000 * 9) >= size)
+ return (EINVAL);
+ return (0);
+}
+
+/*
+ * Approximately translate the input count value into a reasonable
+ * _rexmit_interval_max timeout.
+ */
+static int
+lxpr_xlate_retry2(char *val, int size)
+{
+ long cnt;
+ char *ep;
+ uint_t i, rx_max;
+
+ if (lxpr_tokenize_num(val, &cnt, &ep) != 0)
+ return (EINVAL);
+ if (*ep != '\0')
+ return (EINVAL);
+
+ /*
+ * The _rexmit_interval_max is limited to 2 hours, so a count of 15
+ * or more will exceed that due to exponential backoff.
+ */
+ if (cnt > 15)
+ cnt = 15;
+
+ rx_max = 400; /* Start with default _rexmit_interval_min in ms */
+ for (i = 0; i < cnt; i++)
+ rx_max *= 2;
+
+ /*
+ * The _rexmit_interval_max is limited to 2 hours, so if we went over
+ * the limit, just use 2 hours (in ms).
+ */
+ if (rx_max > (7200 * 1000))
+ rx_max = 7200 * 1000;
+
+ if (snprintf(val, size, "%u", rx_max) >= size)
+ return (EINVAL);
+ return (0);
+}
+
+static int
+lxpr_xlate_sack(char *val, int size)
+{
+ long flag;
+ char *ep;
+
+ if (lxpr_tokenize_num(val, &flag, &ep) != 0)
+ return (EINVAL);
+ if (*ep != '\0')
+ return (EINVAL);
+ if (flag != 0 && flag != 1)
+ return (EINVAL);
+ /* see comment on lxpr_read_sys_net_ipv4_tcp_sack */
+ if (snprintf(val, size, "%d", (flag == 0 ? 0 : 2)) >= size)
+ return (EINVAL);
+ return (0);
+}
+
+/*
+ * We're updating a property on the ip stack so we can't reuse
+ * lxpr_write_tcp_property.
+ */
+static int
+lxpr_write_sys_net_ipv4_icmp_eib(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ int error;
+ size_t olen;
+ char val[16]; /* big enough for a uint numeric string */
+ long flag;
+ char *ep;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_ICMP_EIB);
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (val) - 1)
+ return (EINVAL);
+
+ bzero(val, sizeof (val));
+ error = uiomove(val, olen, UIO_WRITE, uio);
+ if (error != 0)
+ return (error);
+
+ if (val[olen - 1] == '\n')
+ val[olen - 1] = '\0';
+
+ if (val[0] == '\0') /* no input */
+ return (EINVAL);
+
+ if (lxpr_tokenize_num(val, &flag, &ep) != 0)
+ return (EINVAL);
+
+ if (*ep != '\0' || (flag != 0 && flag != 1))
+ return (EINVAL);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL)
+ return (EINVAL);
+
+ ipst = ns->netstack_ip;
+ ipst->ips_ip_g_resp_to_echo_bcast = !flag;
+
+ netstack_rele(ns);
+ return (0);
+}
+
+/*
+ * We expect two port numbers on a line as input for the range, and we have to
+ * set two properties on the netstack_tcp, so we can't reuse
+ * lxpr_write_tcp_property.
+ */
+static int
+lxpr_write_sys_net_ipv4_ip_lport_range(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ int res;
+ size_t olen;
+ char vals[32]; /* big enough for a line w/ 2 16-bit numeric strings */
+ char *ep;
+ long low, high;
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+ mod_prop_info_t *ptbl = NULL;
+ mod_prop_info_t *pinfo = NULL;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_IP_LPORT_RANGE);
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (vals) - 1)
+ return (EINVAL);
+
+ bzero(vals, sizeof (vals));
+ res = uiomove(vals, olen, UIO_WRITE, uio);
+ if (res != 0)
+ return (res);
+
+ if (lxpr_tokenize_num(vals, &low, &ep) != 0)
+ return (EINVAL);
+
+ if (lxpr_tokenize_num(ep, &high, &ep) != 0)
+ return (EINVAL);
+
+ if (*ep != '\0') {
+ /* make sure no other tokens on the line */
+ *ep++ = '\0';
+ for (; isspace(*ep); ep++)
+ ;
+ if (*ep != '\0')
+ return (EINVAL);
+ }
+
+ if (low > high || high > 65535)
+ return (EINVAL);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL)
+ return (EINVAL);
+
+ tcps = ns->netstack_tcp;
+ if (low < tcps->tcps_smallest_nonpriv_port) {
+ netstack_rele(ns);
+ return (EINVAL);
+ }
+
+ ptbl = ns->netstack_tcp->tcps_propinfo_tbl;
+
+ (void) snprintf(vals, sizeof (vals), "%ld", low);
+ pinfo = mod_prop_lookup(ptbl, "smallest_anon_port", MOD_PROTO_TCP);
+ if (pinfo == NULL || pinfo->mpi_setf(ns, cr, pinfo, NULL, vals, 0) != 0)
+ res = EINVAL;
+
+ (void) snprintf(vals, sizeof (vals), "%ld", high);
+ pinfo = mod_prop_lookup(ptbl, "largest_anon_port", MOD_PROTO_TCP);
+ if (pinfo == NULL || pinfo->mpi_setf(ns, cr, pinfo, NULL, vals, 0) != 0)
+ res = EINVAL;
+
+ netstack_rele(ns);
+ return (res);
+}
+
+/*
+ * We expect three numbers on a line as input for the range, and we have to
+ * set two properties on the netstack_tcp, so we can't reuse
+ * lxpr_write_tcp_property.
+ *
+ * See the Linux tcp(7) man page.
+ */
+static int
+lxpr_write_sys_net_ipv4_tcp_rwmem(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ int res;
+ size_t olen;
+ char vals[80]; /* big enough for a line w/ 3 numeric strings */
+ char *ep;
+ long min, def, max, min_limit;
+ netstack_t *ns;
+ tcp_stack_t *tcps;
+ mod_prop_info_t *ptbl;
+ mod_prop_info_t *pinfo;
+ char *attr;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_RMEM ||
+ lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_WMEM);
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (vals) - 1)
+ return (EINVAL);
+
+ bzero(vals, sizeof (vals));
+ res = uiomove(vals, olen, UIO_WRITE, uio);
+ if (res != 0)
+ return (res);
+
+ if (lxpr_tokenize_num(vals, &min, &ep) != 0)
+ return (EINVAL);
+
+ if (lxpr_tokenize_num(ep, &def, &ep) != 0)
+ return (EINVAL);
+
+ if (lxpr_tokenize_num(ep, &max, &ep) != 0)
+ return (EINVAL);
+
+ if (*ep != '\0') {
+ /* make sure no other tokens on the line */
+ *ep++ = '\0';
+ for (; isspace(*ep); ep++)
+ ;
+ if (*ep != '\0')
+ return (EINVAL);
+ }
+
+ /*
+ * Ensure the numbers are valid, low to high.
+ * Valid ranges from the tunable's guide.
+ */
+ min_limit = (lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_RMEM ?
+ 2048 : 4096);
+ if (min > def || def > max || min < min_limit ||
+ def > ONEGB || max < 8192)
+ return (EINVAL);
+
+ ns = lxpr_netstack(lxpnp);
+ if (ns == NULL)
+ return (EINVAL);
+
+ tcps = ns->netstack_tcp;
+
+ /* recv_hiwat and xmit_hiwat are aliased to recv_buf and send_buf. */
+ attr = (lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_RMEM ?
+ "recv_buf" : "send_buf");
+
+ (void) snprintf(vals, sizeof (vals), "%ld", def);
+ ptbl = ns->netstack_tcp->tcps_propinfo_tbl;
+ pinfo = mod_prop_lookup(ptbl, attr, MOD_PROTO_TCP);
+ if (pinfo == NULL ||
+ pinfo->mpi_setf(ns, cr, pinfo, NULL, vals, 0) != 0)
+ res = EINVAL;
+
+ /*
+ * Don't reduce max for one side (recv or xmit) since that impacts the
+ * other.
+ */
+ if (res == 0 && max > tcps->tcps_max_buf) {
+ (void) snprintf(vals, sizeof (vals), "%ld", max);
+ pinfo = mod_prop_lookup(ptbl, "max_buf", MOD_PROTO_TCP);
+ if (pinfo == NULL ||
+ pinfo->mpi_setf(ns, cr, pinfo, NULL, vals, 0) != 0)
+ res = EINVAL;
+ }
+
+ netstack_rele(ns);
+ return (res);
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_cc_curr(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_CC_CURR);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "congestion_control", NULL));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_fin_to(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_FIN_TO);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "_fin_wait_2_flush_interval", lxpr_xlate_sec2ms));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_ka_int(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_INT);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "_keepalive_abort_interval", lxpr_xlate_ka_intvl));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_TIM);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "_keepalive_interval", lxpr_xlate_sec2ms));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_max_syn_bl(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_MAX_SYN_BL);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "_conn_req_max_q0", NULL));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_retry2(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_RETRY2);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+ "_rexmit_interval_max", lxpr_xlate_retry2));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_sack(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_SACK);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct, "sack",
+ lxpr_xlate_sack));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_winscale(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_WINSCALE);
+ return (lxpr_write_tcp_property(lxpnp, uio, cr, ct, "_wscale_always",
+ NULL));
+}
+
+static int
+lxpr_write_sys_fs_pipe_max(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ int error;
+ size_t olen;
+ char val[16]; /* big enough for a uint numeric string */
+ char *ep;
+ long u;
+ size_t size;
+ lx_zone_data_t *lxzd = ztolxzd(LXPTOZ(lxpnp));
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_PIPE_MAX);
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (val) - 1)
+ return (EINVAL);
+
+ bzero(val, sizeof (val));
+ error = uiomove(val, olen, UIO_WRITE, uio);
+ if (error != 0)
+ return (error);
+
+ if (lxpr_tokenize_num(val, &u, &ep) != 0)
+ return (EINVAL);
+ if (*ep != '\0')
+ return (EINVAL);
+
+ /*
+ * Bound to PAGESIZE <= input <= lx_pipe_max_limit, then round to the
+ * nearest page. Linux is a little more picky, rounding to the nearest
+ * power-of-two pages. Such strengthened behavior can be added later
+ * if needed.
+ */
+ size = (size_t)u;
+ size = P2ROUNDUP(MIN(MAX(PAGESIZE, size), lx_pipe_max_limit), PAGESIZE);
+
+ ASSERT(size <= lx_pipe_max_limit);
+
+ mutex_enter(&lxzd->lxzd_lock);
+ lxzd->lxzd_pipe_max_sz = size;
+ mutex_exit(&lxzd->lxzd_lock);
+
+ return (0);
+}
+
+static int
+lxpr_write_sys_kernel_corepatt(lxpr_node_t *lxpnp, struct uio *uio,
+ struct cred *cr, caller_context_t *ct)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ struct core_globals *cg;
+ refstr_t *rp, *nrp;
+ corectl_path_t *ccp;
+ char val[MAXPATHLEN];
+ char valtr[MAXPATHLEN];
+ size_t olen;
+ int error;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_COREPATT);
+
+ cg = zone_getspecific(core_zone_key, zone);
+ ASSERT(cg != NULL);
+
+ if (secpolicy_coreadm(cr) != 0)
+ return (EPERM);
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (val) - 1)
+ return (EINVAL);
+
+ bzero(val, sizeof (val));
+ error = uiomove(val, olen, UIO_WRITE, uio);
+ if (error != 0)
+ return (error);
+
+ if (val[olen - 1] == '\n')
+ val[olen - 1] = '\0';
+
+ if (val[0] == '|')
+ return (EINVAL);
+
+ if ((error = lxpr_core_path_l2s(val, valtr, sizeof (valtr))) != 0)
+ return (error);
+
+ nrp = refstr_alloc(valtr);
+
+ ccp = cg->core_default_path;
+ mutex_enter(&ccp->ccp_mtx);
+ rp = ccp->ccp_path;
+ refstr_hold((ccp->ccp_path = nrp));
+ cg->core_options |= CC_PROCESS_PATH;
+ mutex_exit(&ccp->ccp_mtx);
+
+ if (rp != NULL)
+ refstr_rele(rp);
+
+ return (0);
+}
+
+static int
+lxpr_write_pid_loginuid(lxpr_node_t *lxpnp, struct uio *uio, struct cred *cr,
+ caller_context_t *ct)
+{
+ int error;
+ size_t olen;
+ char val[16]; /* big enough for a uint numeric string */
+ char *ep;
+ long u;
+ proc_t *p;
+ lx_proc_data_t *pd;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_LOGINUID);
+
+ if (uio->uio_loffset != 0)
+ return (EINVAL);
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ olen = uio->uio_resid;
+ if (olen > sizeof (val) - 1)
+ return (EINVAL);
+
+ bzero(val, sizeof (val));
+ error = uiomove(val, olen, UIO_WRITE, uio);
+ if (error != 0)
+ return (error);
+
+ if (lxpr_tokenize_num(val, &u, &ep) != 0)
+ return (EINVAL);
+ if (*ep != '\0')
+ return (EINVAL);
+
+ if ((p = lxpr_lock(lxpnp, NO_ZOMB)) == NULL)
+ return (ENXIO);
+
+ if ((pd = ptolxproc(p)) != NULL) {
+ pd->l_loginuid = (uid_t)u;
+ }
+ lxpr_unlock(p);
+
+ return (0);
+}
+
+static int
+lxpr_readlink_exe(lxpr_node_t *lxpnp, char *buf, size_t size, cred_t *cr)
+{
+ size_t dlen = DIRENT64_RECLEN(MAXPATHLEN);
+ dirent64_t *dp;
+ vnode_t *dirvp;
+ int error = ENOENT;
+ char *dbuf;
+ proc_t *p;
+ size_t len;
+
+ p = lxpr_lock(lxpnp, NO_ZOMB);
+
+ if (p == NULL)
+ return (error);
+
+ dirvp = p->p_execdir;
+ if (dirvp == NULL) {
+ lxpr_unlock(p);
+ return (error);
+ }
+
+ VN_HOLD(dirvp);
+ lxpr_unlock(p);
+
+ /* Look up the parent directory path */
+ if ((error = vnodetopath(NULL, dirvp, buf, size, cr)) != 0) {
+ VN_RELE(dirvp);
+ return (error);
+ }
+
+ len = strlen(buf);
+
+ dbuf = kmem_alloc(dlen, KM_SLEEP);
+
+ /*
+ * Walk the parent directory to find the vnode for p->p_exec, in order
+ * to derive its path.
+ */
+ if ((error = dirfindvp(NULL, dirvp, lxpnp->lxpr_realvp,
+ cr, dbuf, dlen, &dp)) == 0 &&
+ strlen(dp->d_name) + len + 1 < size) {
+ buf[len] = '/';
+ (void) strcpy(buf + len + 1, dp->d_name);
+ } else {
+ error = ENOENT;
+ }
+ VN_RELE(dirvp);
+ kmem_free(dbuf, dlen);
+ return (error);
+}
+
+/*
+ * lxpr_readlink(): Vnode operation for VOP_READLINK()
+ */
+static int
+lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
+{
+ char *bp;
+ size_t buflen, klen;
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ vnode_t *rvp = lxpnp->lxpr_realvp;
+ pid_t pid;
+ int error = 0;
+
+ /*
+ * Linux does something very "clever" for /proc/<pid>/fd/<num> entries.
+ * Open FDs are represented as symlinks, the link contents
+ * corresponding to the open resource. For plain files or devices,
+ * this isn't absurd since one can dereference the symlink to query
+ * the underlying resource. For sockets or pipes, it becomes ugly in a
+ * hurry. To maintain this human-readable output, those FD symlinks
+ * point to bogus targets such as "socket:[<inodenum>]". This requires
+ * circumventing vfs since the stat/lstat behavior on those FD entries
+ * will be unusual. (A stat must retrieve information about the open
+ * socket or pipe. It cannot fail because the link contents point to
+ * an absent file.)
+ *
+ * To accomplish this, lxpr_getnode returns an vnode typed VNON for FD
+ * entries. This bypasses code paths which would normally
+ * short-circuit on symlinks and allows us to emulate the vfs behavior
+ * expected by /proc consumers.
+ */
+ if (vp->v_type != VLNK && lxpnp->lxpr_type != LXPR_PID_FD_FD)
+ return (EINVAL);
+
+ buflen = klen = MAXPATHLEN + 1;
+ bp = kmem_alloc(klen, KM_SLEEP);
+
+ /* Try to produce a symlink name for anything that has a realvp */
+ if (rvp != NULL) {
+ error = lxpr_doaccess(lxpnp, B_TRUE, VREAD, 0, cr, ct);
+ if (error != 0)
+ goto out;
+
+ error = vnodetopath(NULL, rvp, bp, buflen, cr);
+
+ /*
+ * Special handling for /proc/<pid>/exe where the vnode path is
+ * not cached.
+ */
+ if (error != 0 && lxpnp->lxpr_type == LXPR_PID_EXE)
+ error = lxpr_readlink_exe(lxpnp, bp, buflen, cr);
+
+ if (error != 0) {
+ /*
+ * Special handling possible for /proc/<pid>/fd/<num>
+ * Generate <type>:[<inode>] links, if allowed.
+ */
+ if (lxpnp->lxpr_type != LXPR_PID_FD_FD ||
+ lxpr_readlink_fdnode(lxpnp, bp, buflen) != 0) {
+ goto out;
+ }
+ }
+ } else {
+ switch (lxpnp->lxpr_type) {
+ case LXPR_SELF:
+ /* Translate the pid (e.g. initpid to 1) */
+ lxpr_fixpid(LXPTOZ(lxpnp), curproc, &pid, NULL);
+
+ /*
+ * Don't need to check result as every possible int
+ * will fit within MAXPATHLEN bytes.
+ */
+ (void) snprintf(bp, buflen, "%d", pid);
+ break;
+ case LXPR_PID_CURDIR:
+ case LXPR_PID_ROOTDIR:
+ case LXPR_PID_EXE:
+ error = EACCES;
+ goto out;
+ default:
+ /*
+ * Need to return error so that nothing thinks
+ * that the symlink is empty and hence "."
+ */
+ error = EINVAL;
+ goto out;
+ }
+ }
+
+ /* copy the link data to user space */
+ error = uiomove(bp, strlen(bp), UIO_READ, uiop);
+
+out:
+ kmem_free(bp, klen);
+ return (error);
+}
+
+
+/*
+ * lxpr_inactive(): Vnode operation for VOP_INACTIVE()
+ * Vnode is no longer referenced, deallocate the file
+ * and all its resources.
+ */
+static void
+lxpr_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+ lxpr_freenode(VTOLXP(vp));
+}
+
+/*
+ * lxpr_sync(): Vnode operation for VOP_SYNC()
+ */
+static int
+lxpr_sync()
+{
+ /*
+ * Nothing to sync but this function must never fail
+ */
+ return (0);
+}
+
+/*
+ * lxpr_cmp(): Vnode operation for VOP_CMP()
+ */
+static int
+lxpr_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+ vnode_t *rvp;
+
+ while (vn_matchops(vp1, lxpr_vnodeops) &&
+ (rvp = VTOLXP(vp1)->lxpr_realvp) != NULL) {
+ vp1 = rvp;
+ }
+
+ while (vn_matchops(vp2, lxpr_vnodeops) &&
+ (rvp = VTOLXP(vp2)->lxpr_realvp) != NULL) {
+ vp2 = rvp;
+ }
+
+ if (vn_matchops(vp1, lxpr_vnodeops) || vn_matchops(vp2, lxpr_vnodeops))
+ return (vp1 == vp2);
+ return (VOP_CMP(vp1, vp2, ct));
+}
+
+/*
+ * lxpr_realvp(): Vnode operation for VOP_REALVP()
+ */
+static int
+lxpr_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
+{
+ vnode_t *rvp;
+
+ if ((rvp = VTOLXP(vp)->lxpr_realvp) != NULL) {
+ vp = rvp;
+ if (VOP_REALVP(vp, &rvp, ct) == 0)
+ vp = rvp;
+ }
+
+ *vpp = vp;
+ return (0);
+}
+
+/* Pollhead for fake POLLET support below */
+static struct pollhead lxpr_pollhead;
+
+static int
+lxpr_poll(vnode_t *vp, short ev, int anyyet, short *reventsp,
+ pollhead_t **phpp, caller_context_t *ct)
+{
+ *reventsp = 0;
+ if (ev & POLLIN)
+ *reventsp |= POLLIN;
+ if (ev & POLLRDNORM)
+ *reventsp |= POLLRDNORM;
+ if (ev & POLLRDBAND)
+ *reventsp |= POLLRDBAND;
+ if (ev & POLLOUT)
+ *reventsp |= POLLOUT;
+ if (ev & POLLWRBAND)
+ *reventsp |= POLLWRBAND;
+
+ /*
+ * Newer versions of systemd will monitor /proc/self/mountinfo with
+ * edge-triggered epoll (via libmount). If adding said resource to an
+ * epoll descriptor fails, as would be the expectation for a call to
+ * fs_poll when POLLET is present, then systemd will abort and the zone
+ * will fail to properly boot. Until proper pollwakeup() support is
+ * wired into lx_proc, valid POLLET support must be faked.
+ *
+ * While the only known (at this time) lx_proc resource where POLLET
+ * support is mandatory is LXPR_PID_MOUNTINFO, we cast a wide net to
+ * avoid other unexpected trouble. Normal devpoll caching (emitting a
+ * pollhead when (*reventsp == 0 && !anyyet)) is not enabled.
+ */
+ if ((ev & POLLET) != 0) {
+ *phpp = &lxpr_pollhead;
+ }
+ return (0);
+}
+
+static int
+lxpr_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ int i;
+
+ for (i = 0; wr_tab[i].wft_type != LXPR_INVALID; i++) {
+ if (wr_tab[i].wft_type == type) {
+ if (wr_tab[i].wft_wrf != NULL) {
+ return (wr_tab[i].wft_wrf(lxpnp, uiop, cr, ct));
+ }
+ break;
+ }
+ }
+
+ /* pretend we wrote the whole thing */
+ uiop->uio_offset += uiop->uio_resid;
+ uiop->uio_resid = 0;
+ return (0);
+}
+
+/* Needed for writable files which are first "truncated" */
+static int
+lxpr_space(vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset,
+ cred_t *cred, caller_context_t *ct)
+{
+ int error;
+
+ if (cmd != F_FREESP)
+ return (EINVAL);
+ if ((error = lxpr_access(vp, VWRITE, 0, cred, ct)) != 0)
+ return (error);
+
+ return (0);
+}
+
+/*
+ * Needed for writable files which are first "truncated". We only support
+ * truncation.
+ */
+static int
+lxpr_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ int error;
+
+ if (vap->va_mask != AT_SIZE)
+ return (EINVAL);
+ if ((error = lxpr_access(vp, VWRITE, 0, cr, ct)) != 0)
+ return (error);
+
+ return (0);
+}
+
+/*
+ * We need to allow open with O_CREAT for the writable files.
+ */
+static int
+lxpr_create(vnode_t *dvp, char *nm, vattr_t *vap, enum vcexcl exclusive,
+ int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
+ vsecattr_t *vsecp)
+{
+ lxpr_node_t *lxpnp = VTOLXP(dvp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ vnode_t *vp = NULL;
+ int error;
+
+ ASSERT(type < LXPR_NFILES);
+
+ /*
+ * restrict create permission to owner or root
+ */
+ if ((error = lxpr_access(dvp, VEXEC, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ if (*nm == '\0')
+ return (EPERM);
+
+ if (dvp->v_type != VDIR)
+ return (EPERM);
+
+ if (exclusive == EXCL)
+ return (EEXIST);
+
+ /*
+ * No writable files in top-level proc dir. We check this to avoid
+ * getting a non-proc node via "..".
+ */
+ if (type != LXPR_PROCDIR &&
+ lxpr_lookup(dvp, nm, &vp, NULL, 0, NULL, cr, ct, NULL, NULL) == 0) {
+ lxpr_nodetype_t ftype = VTOLXP(vp)->lxpr_type;
+ if (!lxpr_is_writable(ftype)) {
+ VN_RELE(vp);
+ vp = NULL;
+ }
+ }
+
+ if (vp != NULL) {
+ ASSERT(vp->v_type != VDIR);
+
+ /* confirm permissions against existing file */
+ if ((error = lxpr_access(vp, mode, 0, cr, ct)) != 0) {
+ VN_RELE(vp);
+ return (error);
+ }
+
+ *vpp = vp;
+ return (0);
+ }
+
+ /*
+ * Linux proc does not allow creation of addition, non-subsystem
+ * specific files inside the hierarchy. ENOENT is tossed when such
+ * actions are attempted.
+ */
+ return (ENOENT);
+}
diff --git a/usr/src/uts/common/brand/lx/sys/lx_acl.h b/usr/src/uts/common/brand/lx/sys/lx_acl.h
new file mode 100644
index 0000000000..1e5ab26407
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_acl.h
@@ -0,0 +1,45 @@
+/*
+ * 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) 2017 Joyent, Inc.
+ */
+
+#ifndef _LX_ACL_H
+#define _LX_ACL_H
+
+#include <sys/vnode.h>
+#include <sys/uio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Both fall under the 'system.' namespace */
+#define LX_XATTR_POSIX_ACL_ACCESS "posix_acl_access"
+#define LX_XATTR_POSIX_ACL_DEFAULT "posix_acl_default"
+
+enum lx_acl_type {
+ LX_ACL_ACCESS,
+ LX_ACL_DEFAULT
+};
+
+extern int lx_acl_setxattr(vnode_t *, enum lx_acl_type, void *, size_t);
+extern int lx_acl_getxattr(vnode_t *, enum lx_acl_type, void *, size_t,
+ ssize_t *);
+extern int lx_acl_removexattr(vnode_t *, enum lx_acl_type);
+extern int lx_acl_listxattr(vnode_t *, uio_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_ACL_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_audit.h b/usr/src/uts/common/brand/lx/sys/lx_audit.h
new file mode 100644
index 0000000000..76686dd9ec
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_audit.h
@@ -0,0 +1,38 @@
+/*
+ * 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 2018 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _LX_AUDIT_H
+#define _LX_AUDIT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void lx_audit_init(int (*)(void *, uint_t, const char *, uint_t));
+extern void lx_audit_cleanup(void);
+extern void lx_audit_stop_worker(void *, void (*)(void *, boolean_t));
+extern int lx_audit_append_rule(void *, uint_t);
+extern int lx_audit_delete_rule(void *, uint_t);
+extern void lx_audit_list_rules(void *,
+ void (*)(void *, void *, uint_t, void *, uint_t));
+extern void lx_audit_get_feature(void *, void (*)(void *, void *, uint_t));
+extern void lx_audit_get(void *, void (*)(void *, void *, uint_t));
+extern int lx_audit_set(void *, void *, uint_t, void (*cb)(void *, boolean_t));
+extern void lx_audit_emit_user_msg(uint_t, uint_t, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_AUDIT_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_autofs.h b/usr/src/uts/common/brand/lx/sys/lx_autofs.h
new file mode 100644
index 0000000000..17b19895f4
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_autofs.h
@@ -0,0 +1,511 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _LX_AUTOFS_H
+#define _LX_AUTOFS_H
+
+/*
+ * The lxautofs filesystem and driver exist to emulate the Linux autofs
+ * filesystem and /dev/autofs device (this code emulates both). The
+ * purpose is to provide support for the Linux "automount" automounter.
+ *
+ * The device ioctls map fairly closely to the filesystem ioctls. The device
+ * ioctls have superseded the filesystem ioctls and the automounter will
+ * use the device ioctls if the device exists.
+ *
+ * The device ioctls are used by the automounter to perform recovery
+ * in cases where the automounter is restarted while mounts are present. It
+ * also allows for better management operations when a filesystem is mounted
+ * on top of an autofs mountpoint, as in the case of an NFS direct mount on
+ * top of an autofs mount.
+ *
+ *
+ * +++ Linux automounter background.
+ *
+ * Linux has two automounters: "amd" (not used in any popular, modern distro)
+ * and "automount".
+ *
+ * "automount" is the normal Linux automounter. It utilizes a kernel
+ * filesystem (autofs) and device (/dev/autofs) to provide its functionality.
+ * Basically, it mounts the autofs filesystem at any automounter controlled
+ * mountpoint. This filesystem then intercepts and redirects lookup operations
+ * to the userland automounter process via a pipe. The pipe to the automounter
+ * is established via a mount option when the autofs filesystem is mounted or
+ * via the setpipefd ioctl if the automounter restarts. When the automounter
+ * receives a request via this pipe, it does lookups (or unmounts) to whatever
+ * backing store it's configured to use, does mkdir operations on the autofs
+ * filesystem, mounts remote NFS filesystems on any directories it manages or
+ * just created, and signals the autofs device via an ioctl to let it know
+ * that the lookup (or expire) can continue. Other management operations (such
+ * as querying expiration for unmounting) are performed using the autofs device.
+ *
+ *
+ * +++ Linux autofs documentation.
+ *
+ * Within the Linux src tree, see the file:
+ * Documentation/filesystems/autofs4-mount-control.txt
+ * This documents some of the autofs behavior and the device driver ioctls.
+ *
+ * The following URL (https://lwn.net/Articles/606960/) documents autofs in
+ * general. This patch was targeted for Documentation/filesystems/autofs4.txt,
+ * but seems to have never integrated into the Linux src tree.
+ *
+ *
+ * +++ Linux autofs (and automount daemon) notes
+ *
+ * Since we're mimicking the behavior of the Linux autofs filesystem and
+ * device, we document some of the observed behavior here.
+ *
+ * There are multiple versions of the autofs filesystem kernel API protocol
+ * and modern implementations of the user-land automount daemon would depend
+ * on v5, although the filesystem API has been superseded by the driver ioctl
+ * API, which is roughly similar.
+ *
+ * We'll describe the filesystem ioctls first, since support for those was
+ * implemented first. The device ioctls roughly correspond to the filesystem
+ * ioctls and were implemented last, but the automounter will use those
+ * ioctls, instead of the filesystem ioctls, when the device is present.
+ *
+ * Our original autofs implementation was developed in the mid-2000s around the
+ * v2 protocol, but that is currently obsolete. Our current implementation is
+ * based around the v5 protocol API. There was no autofs device support at that
+ * time.
+ *
+ * The autoumounter supports 3 different, mutually exclusive, mount options for
+ * each mountpoint:
+ * - indirect (this was all you got with the v2 support)
+ * - direct
+ * - offset
+ *
+ * An 'indirect' mountpoint is managed with dynamic mounts below that
+ * mountpoint. For example, if '/home' were an indirect autofs mount, then
+ * accessing a username under /home would traverse the 'lookup' code described
+ * below, cause a local subdirectory to be created, and a mount, usually NFS,
+ * onto that username subdirectory.
+ *
+ * A 'direct' mountpoint is an autofs mountpoint which will trigger the
+ * mounting of another filesystem overtop that mountpoint when accessed.
+ *
+ * An 'offset' mountpoint behaves like a 'direct' mountpoint but it is
+ * created dynamically by the automounter underneath an 'indirect' mountpoint.
+ * For example, if '/net' were an indirect autosfs mountpoint and the host
+ * 'jurassic' exported two NFS filesystems; '/var/crash' and '/var/core', then
+ * accessing '/net/jurassic' would trigger the automounter to create two
+ * subdirectories; '/net/jurassic/var/crash' and '/net/jurassic/var/core'. The
+ * automounter would then mount an autofs offset mount onto each one of these
+ * directories. Accessing either of those directories would then trigger
+ * automounter to perform another mount on top, as is done with a 'direct'
+ * mount.
+ *
+ * General behavior
+ *
+ * A) Autofs allows root owned, non-automounter processes to create
+ * directories in the autofs filesystem. The autofs filesystem treats the
+ * automounter's process group as special, but it doesn't prevent root
+ * processes outside of the automounter's process group from creating new
+ * directories in the autofs filesystem.
+ *
+ * B) Autofs doesn't allow creation of any non-directory entries in the
+ * autofs filesystem. No entity can create files (e.g. /bin/touch or
+ * VOP_CREATE/VOP_SYMLINK/etc.) The only entries that can exist within
+ * the autofs filesystem are directories.
+ *
+ * C) Autofs only intercepts vop lookup operations. Notably, it does _not_
+ * intercept and re-direct vop readdir operations. This means that the
+ * observed behavior of the Linux automounter can be considerably different
+ * from that of the illumos automounter. Specifically, on illumos if an autofs
+ * mountpoint is mounted _without_ the -nobrowse option then if a user does
+ * an ls operation (which translates into a vop readdir operation) then the
+ * automounter will intercept that operation and list all the possible
+ * directories and mountpoints without actually mounting any filesystems.
+ * Essentially, all automounter managed mountpoints on Linux will behave
+ * like "-nobrowse" mountpoints on illumos. Here's an example to illustrate
+ * this. If /ws was mounted on illumos without the -nobrowse option and an
+ * auto_ws yp map was setup as the backing store for this mountpoint, then an
+ * "ls /ws" would list all the keys in the map as valid directories, but an
+ * "ls /ws" on Linux would list an emptry directory.
+ *
+ * D) NFS mounts are performed by the automount process. When the automount
+ * process gets a redirected lookup request, it determines _all_ the
+ * possible remote mountpoints for that request, creates directory paths
+ * via mkdir, and mounts the remote filesystems on the newly created paths.
+ * This is described in the offset mount example above. Once the automounter
+ * completed the mounts it would signal the autofs filesystem (via an ioctl)
+ * that the lookup could continue.
+ *
+ * E.1) Autofs only redirects vop lookup operations for path entries that
+ * don't already exist in the autofs filesystem. So for the example above,
+ * an initial (after the start of the automounter) "ls /net/jurassic" would
+ * result in a request to the automounter. A subsequest "ls /net/jurassic"
+ * would not result in a request to the automounter. Even if
+ * /net/jurassic/var/crash and /net/jurassic/var/core were manually unmounted
+ * after the initial "ls /net/jurassic", a subsequest "ls /net/jurassic"
+ * would not result in a new request to the automounter.
+ *
+ * E.2) Autofs lookup requests that are sent to the automounter only include
+ * the root directory path component. So for example, after starting up
+ * the automounter if a user were to do a "ls /net/jurassic/var/crash", the
+ * initial lookup request actually sent to the automounter would just be for
+ * "jurassic" (the same request as if the user had done "ls /net/jurassic").
+ * After the initial mounting of the two offset mounts onto crash and core the
+ * lookup would continue and a final lookup request would be sent to the
+ * automounter for "crash" (but this would be on a different vfs from the
+ * /net vfs).
+ *
+ * E.3) The two statements above aren't entirely entirely true. The Linux
+ * autofs filesystem will also redirect lookup operations for leaf
+ * directories that don't have a filesystem mounted on them. Using the
+ * example above, if a user did a "ls /net/jurassic", then manually
+ * unmounted /net/jurassic/var/crash, and then did an "ls
+ * /net/jurassic/var/crash", this would result in a request for
+ * "jurassic/var/crash" being sent to the automounter. The strange thing
+ * (a Linux bug perhaps) is that the automounter won't do anything with this
+ * request and the lookup will fail.
+ *
+ * F) The autofs filesystem communication protocol (what ioctls it supports
+ * and what data it passes to the automount process) is versioned. The
+ * userland automount daemon (as of version v5.0.7) expects v5 of the protocol
+ * (by running the AUTOFS_IOC_PROTOSUBVER ioctl), and exits if that is not
+ * supported. For v2-v5 the structure passed through the pipe always begins
+ * with a common header followed by different fields depending on the packet
+ * type. In addition the different versions support additional ioctls.
+ *
+ * v2 - basic lookup request
+ * v3 - adds expiring (umounting)
+ * v4 - adds expire multi
+ * v5 - adds missing indirect, expire indirect, missing direct & expire direct.
+ * Defines a new protocol structure layout.
+ * The v5 'missing indirect' and 'missing direct' ioctls are analogous to
+ * the v2 'missing' ioctl. These ioctls are used to initiate a mount via
+ * a lookup. The 'expire' ioctls are used by the automounter to query if
+ * it is possible to unmount the filesystem. 'direct' and 'indirect'
+ * refer to the mount option type that the automounter performed and
+ * correlate to an automounter direct or indirect map mointpoint.
+ *
+ * G) The automounter periodically issues an 'expire' ioctl to autofs to
+ * obtain the name of a mountpoint which the automounter can unmount.
+ * Unmounting is dicussed in more detail below.
+ *
+ * H) The device ioctls roughly correspond to the filesystem ioctls, but
+ * instead of being tied to an auotfs mountpoint vnode, they can be called any
+ * time. The argument structure uses either a path or an autofs pipe file
+ * descriptor to indicate what is being operated on.
+ *
+ * +++ lxautofs notes
+ *
+ * 1) In general, the lxautofs filesystem tries to mimic the behavior of the
+ * Linux autofs filesystem with the following exceptions:
+ *
+ * 1.1) We don't bother to implement the E.3 functionality listed above
+ * since it doesn't appear to be of any use.
+ *
+ * 1.2) We only fully implement v2 and v5 of the autofs protocol.
+ *
+ * 2) In general, the approach taken for lxautofs is to keep it as simple
+ * as possible and to minimize it's memory usage. To do this all information
+ * about the contents of the lxautofs filesystem are mirrored in the
+ * underlying filesystem that lxautofs is mounted on and most vop operations
+ * are simply passed onto this underlying filesystem. This means we don't
+ * have to implement most of the complex operations that a full filesystem
+ * normally has to implement. It also means that most of our filesystem state
+ * (wrt the contents of the filesystem) doesn't actually have to be stored
+ * in memory, we can simply go to the underlying filesystem to get it when
+ * it's requested. For the purposes of discussion, we'll call the underlying
+ * filesystem the "backing store."
+ *
+ * The backing store is actually a directory called ".lxautofs" which is created
+ * in the directory where the lxautofs filesystem is mounted. When the
+ * lxautofs filesystem is unmounted this backing store directory is deleted.
+ * If this directory exists at mount time (perhaps the system crashed while a
+ * previous lxautofs instance was mounted at the same location) it will be
+ * deleted. There are a few implications of using a backing store worth
+ * mentioning.
+ *
+ * 2.1) lxautofs can't be mounted on a read only filesystem. If this
+ * proves to be a problem we can probably move the location of the
+ * backing store.
+ *
+ * 2.2) If the backing store filesystem runs out of space then the
+ * automounter process won't be able to create more directories and mount
+ * new filesystems. Of course, strange failures usually happen when
+ * filesystems run out of space.
+ *
+ * 3) Why aren't we using gfs? gfs has two different usage models.
+ *
+ * 3.1) I'm my own filesystem but i'm using gfs to help with managing
+ * readdir operations.
+ *
+ * 3.2) I'm a gfs filesystem and gfs is managing all my vnodes
+ *
+ * We're not using the 3.1 interfaces because we don't implement readdir
+ * ourselves. We pass all readdir operations onto the backing store
+ * filesystem and utilize its readdir implementation.
+ *
+ * We're not using the 3.2 interfaces because they are really designed for
+ * in memory filesystems where all of the filesystem state is stored in
+ * memory. They don't lend themselves to filesystems where part of the
+ * state is in memory and part of the state is on disk.
+ *
+ * For more information on gfs take a look at the block comments in the
+ * top of gfs.c
+ *
+ * 4) Unmounting
+ *
+ * The automounter has a timeout associated with each mount. It informs autofs
+ * of this timeout using the LX_AUTOFS_DEV_IOC_TIMEOUT_CMD ioctl after autofs
+ * has been mounted on the mountpoint.
+ *
+ * After the automounter has mounted something associated with the mountpoint
+ * then periodically (<timeout>/4 seconds) the automounter will issue the
+ * LX_AUTOFS_DEV_IOC_EXPIRE_CMD ioctl on the autofs mount. autofs is expected
+ * to respond with an underlying mountpoint entry which is a candidate for
+ * unmounting. The automounter will attempt to unmount the filesystem
+ * (which may fail if it is busy, since this is obviously racy) and then
+ * acknowledge the expire ioctl. The successful acknowledgement is independent
+ * of the success of unmounting the underlying filesystem.
+ *
+ * Unmount handling varies based on which type of mount the autofs was mounted
+ * with (indirect, direct or offset).
+ *
+ * To support 'indirect' mount expiration, the autofs vfs keeps track of the
+ * filesystems mounted immediately under the autofs mountpoint (in
+ * lav_mnt_list) after a lookup has completed successfully. Upon receipt of the
+ * LX_AUTOFS_IOC_DEV_EXPIRE_CMD ioctl, autofs removes the first element from
+ * the list, attempts to check if it is busy and if not, returns that mountpoint
+ * over the fifo (if busy the entry is added to the end of the list). When the
+ * ioctl is acknowledged, if the mountpoint still exists, that means the unmount
+ * failed and the entry is added at the back of the list. If there are no
+ * elements or the first one is busy, EAGAIN is returned for the 'expire' ioctl
+ * and the autoumounter will check again in <timeout>/4 seconds.
+ *
+ * For example, if /home is an autofs indirect mount, then there are typically
+ * many different {username}-specific NFS mounts under that /home autofs mount.
+ * autofs uses the lav_mnt_list to respond to 'expire' ioctls in a round-robin
+ * fashion so that the automounter can unmount user file systems that aren't in
+ * use.
+ *
+ * Expiring 'direct' mounts is similar, but since there is only a single mount,
+ * the lav_mnt_list only will have at most one entry if there is a filesystem
+ * mounted overtop of the autofs mount.
+ *
+ * Expiring 'offset' mounts is more complicated because there are at least
+ * two different autofs VFSs involved (the top-level and one for each offset
+ * mount underneath). The actual offset mount is handled exactly like a 'direct'
+ * mount. The top-level is an indirect mount and is handled in a similar way
+ * as described above for indirect mounts, but special handling is needed for
+ * each offset mount below.
+ *
+ * This can be explained using the same 'jurassic' example described earlier
+ * (/net is an autofs 'indirect' mount and the host 'jurassic' has two exported
+ * file systems; /var/crash and /var/core). If the user accesses
+ * /net/jurassic/var/crash then the automounter would setup the system so that
+ * the following mounts exist:
+ * - /net (the original autofs indirect mount which triggers everything)
+ * - /net/jurassic/var/crash (autofs offset mount)
+ * - /net/jurassic/var/crash (NFS mount on top of the autofs offset mount)
+ * - /net/jurassic/var/core (autofs offset mount)
+ *
+ * For expiration the automounter will issue the LX_AUTOFS_IOC_EXPIRE_MULTI
+ * ioctl on each autofs vfs for which something is mounted, so we would receive
+ * an expire ioctl on /net and another on /net/jusrassic/var/crash. The vfs for
+ * /net will be tracking "jurassic", but we detect it is busy and won't do
+ * anything at first. The vfs for "crash" will work like a direct mount and
+ * acknowledge the expire ioctl to the automounter once that filesystem times
+ * out and is no longer busy. The automounter will then unmount the "crash"
+ * NFS mount.
+ *
+ * Once the "crash" NFS mount has been unmounted by the automounter, we're left
+ * with the two autofs offset mounts under jurassic. The automounter will not
+ * try to unmount either of those, so we have to do that. Once we get another
+ * expire ioctl on /net and check "jurassic", we'll see there are only autofs
+ * mounts under /net/jurassic. We umount those using the lx_autofs_umount_offset
+ * function and respond to the automounter expire ioctl with "jurassic", in the
+ * same way as we would for any other indirect mount.
+ *
+ * 5) Recovery
+ *
+ * If the automounter is restarted for any reason, it needs to cope with
+ * pre-existing autofs mounts, as well as other automount-initiated mounts (e.g.
+ * a direct mount on top of an autofs mountpoint). The automounter uses the
+ * /proc/mounts file to correlate mounts to the managed mountpoints. It then
+ * uses the /dev/autofs device to openmount each of the autofs devices and
+ * reinitialize them using the various dev ioctls (timeout, requester, etc.).
+ *
+ * In general, the autoumounter will closemount the mountpoint once it's done,
+ * but it doesn't in the case of an offset mountpoint with nothing mounted
+ * on top. In this case the automounter expects autofs to expire that mountpoint
+ * before it will closemount (so things can subsequently cleanup). We handle
+ * this special case in the expire code path.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Note that the name of the actual file system is lxautofs, not lx_autofs, but
+ * the code uses lx_autofs to prefix the various names. This is because file
+ * system names are limited to 8 characters.
+ */
+#define LX_AUTOFS_NAME "lxautofs"
+
+#define LX_AUTOFS_MINORNAME "autofs"
+
+/*
+ * Mount options supported.
+ */
+#define LX_MNTOPT_FD "fd"
+#define LX_MNTOPT_PGRP "pgrp"
+#define LX_MNTOPT_MINPROTO "minproto"
+#define LX_MNTOPT_MAXPROTO "maxproto"
+#define LX_MNTOPT_INDIRECT "indirect"
+#define LX_MNTOPT_DIRECT "direct"
+#define LX_MNTOPT_OFFSET "offset"
+
+/*
+ * Version/subversion of the Linux kernel automount protocol we support.
+ *
+ * We fully support v2 and v5. We'll return ENOTSUP for all of the ioctls we
+ * don't yet handle.
+ */
+#define LX_AUTOFS_PROTO_VERS5 5
+#define LX_AUTOFS_PROTO_SUBVERSION 2
+#define LX_AUTOFS_PROTO_VERS2 2
+
+/* packet types */
+typedef enum laph_ptype {
+ LX_AUTOFS_PTYPE_MISSING, /* 0 */
+ LX_AUTOFS_PTYPE_EXPIRE, /* 1 */
+ LX_AUTOFS_PTYPE_EXPIRE_MULTI, /* 2 */
+ LX_AUTOFS_PTYPE_MISSING_INDIR, /* 3 */
+ LX_AUTOFS_PTYPE_EXPIRE_INDIR, /* 4 */
+ LX_AUTOFS_PTYPE_MISSING_DIRECT, /* 5 */
+ LX_AUTOFS_PTYPE_EXPIRE_DIRECT /* 6 */
+} laph_ptype_t;
+
+/*
+ * Common header for all versions of the protocol.
+ */
+typedef struct lx_autofs_pkt_hdr {
+ int laph_protover; /* protocol version number */
+ laph_ptype_t laph_type;
+ int laph_id; /* every pkt must have a unique id */
+} lx_autofs_pkt_hdr_t;
+
+/*
+ * Command structure sent to automount process from lxautofs via a pipe.
+ * This structure is the same for v2-v4 of the automount protocol
+ * (the communication pipe is established at mount time).
+ */
+typedef struct lx_autofs_v2_pkt {
+ lx_autofs_pkt_hdr_t lap_hdr;
+ int lap_name_len; /* don't include newline or NULL */
+ char lap_name[256]; /* path component to lookup */
+} lx_autofs_v2_pkt_t;
+
+/* v4 multi-expire */
+typedef struct lx_autofs_v4_exp_pkt {
+ lx_autofs_pkt_hdr_t lape_hdr;
+ int lape_len;
+ char lape_name[MAXNAMELEN];
+} lx_autofs_v4_exp_pkt_t;
+
+/* v5 */
+typedef struct lx_autofs_v5_pkt {
+ lx_autofs_pkt_hdr_t lap_hdr;
+ uint32_t lap_dev;
+ uint64_t lap_ino;
+ uint32_t lap_uid;
+ uint32_t lap_gid;
+ uint32_t lap_pid;
+ uint32_t lap_tgid;
+ uint32_t lap_name_len;
+ char lap_name[256];
+} lx_autofs_v5_pkt_t;
+
+union lx_autofs_pkt {
+ lx_autofs_v2_pkt_t lap_v2;
+ lx_autofs_v5_pkt_t lap_v5;
+};
+
+#define lap_protover lap_v2.lap_hdr.laph_protover
+#define lap_type lap_v2.lap_hdr.laph_type
+#define lap_id lap_v2.lap_hdr.laph_id
+
+/*
+ * Ioctls fully supported (v2 protocol).
+ */
+#define LX_AUTOFS_IOC_READY 0x00009360 /* arg: int */
+#define LX_AUTOFS_IOC_FAIL 0x00009361 /* arg: int */
+#define LX_AUTOFS_IOC_CATATONIC 0x00009362 /* arg: <none> */
+
+/*
+ * Ioctls supported (v3/v4 protocol).
+ */
+#define LX_AUTOFS_IOC_PROTOVER 0x80049363 /* arg: int */
+#define LX_AUTOFS_IOC_SETTIMEOUT 0xc0089364 /* arg: ulong_t */
+
+/*
+ * Ioctls not supported (v3/v4 protocol).
+ */
+ /* arg: lx_autofs_v3_exp_pkt_t * */
+#define LX_AUTOFS_IOC_EXPIRE 0x81109365
+
+/*
+ * Ioctls supported (v5 protocol).
+ */
+#define LX_AUTOFS_IOC_PROTOSUBVER 0x80049367 /* arg: int */
+#define LX_AUTOFS_IOC_ASKUMOUNT 0x80049370 /* arg: int */
+#define LX_AUTOFS_IOC_EXPIRE_MULTI 0x40049366 /* arg: int */
+#define LX_AUTOFS_IOC_EXPIRE_INDIRECT LX_AUTOFS_IOC_EXPIRE_MULTI
+#define LX_AUTOFS_IOC_EXPIRE_DIRECT LX_AUTOFS_IOC_EXPIRE_MULTI
+
+/*
+ * autofs device ioctls
+ */
+#define LX_AUTOFS_DEV_IOC_VERSION_CMD 0xc0189371
+#define LX_AUTOFS_DEV_IOC_PROTOVER_CMD 0xc0189372
+#define LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD 0xc0189373
+#define LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD 0xc0189374
+#define LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD 0xc0189375
+#define LX_AUTOFS_DEV_IOC_READY_CMD 0xc0189376
+#define LX_AUTOFS_DEV_IOC_FAIL_CMD 0xc0189377
+#define LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD 0xc0189378
+#define LX_AUTOFS_DEV_IOC_CATATONIC_CMD 0xc0189379
+#define LX_AUTOFS_DEV_IOC_TIMEOUT_CMD 0xc018937a
+#define LX_AUTOFS_DEV_IOC_REQUESTER_CMD 0xc018937b
+#define LX_AUTOFS_DEV_IOC_EXPIRE_CMD 0xc018937c
+#define LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD 0xc018937d
+#define LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD 0xc018937e
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_AUTOFS_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h b/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h
new file mode 100644
index 0000000000..39ea96d1fe
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _LX_AUTOFS_IMPL_H
+#define _LX_AUTOFS_IMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/file.h>
+#include <sys/id_space.h>
+#include <sys/modhash.h>
+#include <sys/vnode.h>
+
+#include <sys/lx_autofs.h>
+
+/*
+ * Space key.
+ * Used to persist data across lx_autofs filesystem module unloads.
+ */
+#define LX_AUTOFS_SPACE_KEY_UDEV LX_AUTOFS_NAME "_udev"
+
+/*
+ * Name of the backing store directory.
+ */
+#define LX_AUTOFS_BS_DIR "." LX_AUTOFS_NAME
+
+#define LX_AUTOFS_VFS_ID_HASH_SIZE 15
+#define LX_AUTOFS_VFS_PATH_HASH_SIZE 15
+#define LX_AUTOFS_VFS_VN_HASH_SIZE 15
+
+enum lx_autofs_mnttype { LXAMT_NONE, LXAMT_INDIR, LXAMT_DIRECT, LXAMT_OFFSET };
+
+typedef struct lx_autofs_mntent {
+ list_node_t lxafme_lst;
+ uint64_t lxafme_ts; /* time stamp */
+ uint_t lxafme_len;
+ char *lxafme_path;
+} lx_autofs_mntent_t;
+
+/*
+ * VFS data object.
+ */
+typedef struct lx_autofs_vfs {
+ /* Info about the underlying filesystem and backing store. */
+ vnode_t *lav_mvp;
+ char *lav_bs_name;
+ vnode_t *lav_bs_vp;
+
+ /* Info about the automounter process managing this filesystem. */
+ int lav_fd;
+ pid_t lav_pgrp;
+ file_t *lav_fifo_wr;
+ file_t *lav_fifo_rd;
+
+ /* The mount's dev and ino values for v5 protocol msg */
+ uint64_t lav_dev;
+ u_longlong_t lav_ino;
+
+ /* options from the mount */
+ enum lx_autofs_mnttype lav_mnttype;
+ int lav_min_proto;
+
+ /*
+ * ioctl-set timeout value. The automounter will perform an expire
+ * ioctl every timeout/4 seconds. We use this to expire a mount once
+ * it is inactive for the full timeout.
+ */
+ ulong_t lav_timeout;
+
+ /* ioctl-set catatonic value (prevents future mounts). */
+ boolean_t lav_catatonic;
+
+ /* Mount initiator's uid/gid for recovery handling. */
+ uid_t lav_uid;
+ gid_t lav_gid;
+
+ /* Each automount requests needs a unique id. */
+ id_space_t *lav_ids;
+
+ /* All remaining structure members are protected by lav_lock. */
+ kmutex_t lav_lock;
+ /* openmount counter */
+ int lav_openmnt_cnt;
+
+
+ /* Hashes to keep track of outstanding automounter requests. */
+ mod_hash_t *lav_path_hash;
+ mod_hash_t *lav_id_hash;
+
+ /* We need to keep track of all our vnodes. */
+ vnode_t *lav_root;
+ mod_hash_t *lav_vn_hash;
+
+ /* list of current mounts */
+ list_t lav_mnt_list;
+} lx_autofs_vfs_t;
+
+enum lx_autofs_callres { LXACR_NONE, LXACR_READY, LXACR_FAIL };
+
+/*
+ * Structure to keep track of automounter requests sent to user-land.
+ */
+typedef struct lx_autofs_automnt_req {
+ /* Packet that gets sent to the automounter. */
+ union lx_autofs_pkt laar_pkt;
+ int laar_pkt_size;
+
+ /* Reference count. Always updated atomically. */
+ uint_t laar_ref;
+
+ /*
+ * Fields to keep track and sync threads waiting on a lookup.
+ * Fields are protected by lalr_lock.
+ */
+ kmutex_t laar_lock;
+ kcondvar_t laar_cv;
+ int laar_complete;
+
+ enum lx_autofs_callres laar_result;
+} lx_autofs_automnt_req_t;
+
+/*
+ * Generic stack structure.
+ */
+typedef struct stack_elem {
+ list_node_t se_list;
+ caddr_t se_ptr1;
+ caddr_t se_ptr2;
+ caddr_t se_ptr3;
+} stack_elem_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_AUTOFS_IMPL_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_brand.h b/usr/src/uts/common/brand/lx/sys/lx_brand.h
new file mode 100644
index 0000000000..35b1bddb03
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_brand.h
@@ -0,0 +1,772 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#ifndef _LX_BRAND_H
+#define _LX_BRAND_H
+
+#ifndef _ASM
+#include <sys/types.h>
+#include <sys/cpuvar.h>
+#include <sys/zone.h>
+#include <sys/ksocket.h>
+#include <sys/vfs.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/cpuvar.h>
+#include <sys/lx_futex.h>
+#include <sys/lx_userhz.h>
+#include <sys/uuid.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LX_BRANDNAME "lx"
+
+/*
+ * Brand uname info
+ */
+#define LX_UNAME_SYSNAME "Linux"
+#define LX_UNAME_RELEASE_2_6 "2.6.18"
+#define LX_UNAME_RELEASE_2_4 "2.4.21"
+#define LX_UNAME_VERSION "BrandZ virtual linux"
+#define LX_UNAME_MACHINE32 "i686"
+#define LX_UNAME_MACHINE64 "x86_64"
+
+#define LX_LIB_PATH32 "/native/usr/lib/lx_brand.so.1"
+#define LX_LIB_PATH64 "/native/usr/lib/amd64/lx_brand.so.1"
+
+#define LX_VDSO_PATH32 "/native/usr/lib/brand/lx/lx_vdso.so.1"
+#define LX_VDSO_PATH64 "/native/usr/lib/brand/lx/amd64/lx_vdso.so.1"
+
+#if defined(_LP64)
+#define LX_LIB_PATH LX_LIB_PATH64
+#define LX_UNAME_MACHINE LX_UNAME_MACHINE64
+#define LX_VDSO_PATH LX_VDSO_PATH64
+#else
+#define LX_LIB_PATH LX_LIB_PATH32
+#define LX_UNAME_MACHINE LX_UNAME_MACHINE32
+#define LX_VDSO_PATH LX_VDSO_PATH32
+#endif
+
+/*
+ * This must be large enough for both the 32-bit table and 64-bit table.
+ */
+#define LX_NSYSCALLS 358
+
+/* Highest capability we know about */
+#define LX_CAP_MAX_VALID 36
+
+/* sched attr flag values */
+#define LX_SCHED_FLAG_RESET_ON_FORK 0x1
+/*
+ * brand(2) subcommands
+ *
+ * Everything >= 128 is a brand-specific subcommand.
+ * > 192 is reserved for in-kernel emulated system calls.
+ */
+#define B_LPID_TO_SPAIR 128
+#define B_GET_CURRENT_CONTEXT 129
+#define B_EMULATION_DONE 130
+/* Some native programs use B_START_NFS_LOCKD, so don't change this. */
+#define B_START_NFS_LOCKD 131
+#define B_BLOCK_ALL_SIGS 132
+#define B_UNBLOCK_ALL_SIGS 133
+#define B_PTRACE_CLONE_BEGIN 134
+#define B_PTRACE_STOP_FOR_OPT 135
+#define B_UNSUPPORTED 136
+#define B_STORE_ARGS 137
+#define B_GETPID 138
+#define B_JUMP_TO_LINUX 139
+#define B_ALL_SIGS_BLOCKED 140
+#define B_EXIT_AS_SIG 141
+/* formerly B_HELPER_WAITID 142 */
+#define B_HELPER_CLONE 143
+#define B_HELPER_SETGROUPS 144
+#define B_HELPER_SIGQUEUE 145
+#define B_HELPER_TGSIGQUEUE 146
+#define B_SET_NATIVE_STACK 147
+/* formerly B_SIGEV_THREAD_ID 148 */
+#define B_OVERRIDE_KERN_VER 149
+#define B_PTRACE_SIG_RETURN 150
+#define B_GET_PERSONALITY 151
+
+#ifndef _ASM
+/*
+ * Support for Linux PTRACE_SETOPTIONS handling.
+ */
+typedef enum lx_ptrace_options {
+ LX_PTRACE_O_TRACESYSGOOD = 0x0001,
+ LX_PTRACE_O_TRACEFORK = 0x0002,
+ LX_PTRACE_O_TRACEVFORK = 0x0004,
+ LX_PTRACE_O_TRACECLONE = 0x0008,
+ LX_PTRACE_O_TRACEEXEC = 0x0010,
+ LX_PTRACE_O_TRACEVFORKDONE = 0x0020,
+ LX_PTRACE_O_TRACEEXIT = 0x0040,
+ LX_PTRACE_O_TRACESECCOMP = 0x0080
+} lx_ptrace_options_t;
+
+#define LX_PTRACE_O_ALL \
+ (LX_PTRACE_O_TRACESYSGOOD | LX_PTRACE_O_TRACEFORK | \
+ LX_PTRACE_O_TRACEVFORK | LX_PTRACE_O_TRACECLONE | \
+ LX_PTRACE_O_TRACEEXEC | LX_PTRACE_O_TRACEVFORKDONE | \
+ LX_PTRACE_O_TRACEEXIT | LX_PTRACE_O_TRACESECCOMP)
+#endif /* !_ASM */
+
+/* siginfo si_status for traced events */
+#define LX_PTRACE_EVENT_FORK 0x100
+#define LX_PTRACE_EVENT_VFORK 0x200
+#define LX_PTRACE_EVENT_CLONE 0x300
+#define LX_PTRACE_EVENT_EXEC 0x400
+#define LX_PTRACE_EVENT_VFORK_DONE 0x500
+#define LX_PTRACE_EVENT_EXIT 0x600
+#define LX_PTRACE_EVENT_SECCOMP 0x700
+
+/*
+ * Brand-private values for the "pr_what" member of lwpstatus, for use with the
+ * PR_BRAND stop reason. These reasons are validated in lx_stop_notify();
+ * update it if you add new reasons here.
+ */
+#define LX_PR_SYSENTRY 1
+#define LX_PR_SYSEXIT 2
+#define LX_PR_SIGNALLED 3
+#define LX_PR_EVENT 4
+
+
+#define LX_VERSION_1 1
+#define LX_VERSION LX_VERSION_1
+
+#define LX_ATTR_KERN_RELEASE ZONE_ATTR_BRAND_ATTRS
+#define LX_ATTR_KERN_VERSION (ZONE_ATTR_BRAND_ATTRS + 1)
+#define LX_ATTR_TTY_GID (ZONE_ATTR_BRAND_ATTRS + 2)
+
+/*
+ * Aux vector containing phdr of Linux executable and ehdr of interpreter
+ * (if any), both of which are used by lx_librtld_db to ascertain r_debug.
+ * We repurpose the 3rd brand-specific aux vector slot for the Linux
+ * AT_SYSINFO_EHDR entry (we modify the a_type in the brand library).
+ */
+#define AT_SUN_BRAND_LX_PHDR AT_SUN_BRAND_AUX1
+#define AT_SUN_BRAND_LX_INTERP AT_SUN_BRAND_AUX2
+#define AT_SUN_BRAND_LX_CLKTCK AT_SUN_BRAND_AUX3
+#define AT_SUN_BRAND_LX_SYSINFO_EHDR AT_SUN_BRAND_AUX4
+
+/* Aux vectors containing real/effective user/group IDs */
+#define AT_LX_UID 11
+#define AT_LX_EUID 12
+#define AT_LX_GID 13
+#define AT_LX_EGID 14
+/* Aux vector containing hz value */
+#define AT_CLKTCK 17
+/* Aux vector containing secure boolean */
+#define AT_SECURE 23
+/* Aux vector containing vDSO addr */
+#define AT_SYSINFO_EHDR 33
+
+/*
+ * Usermode emulation routines are run on an alternate stack allocated by
+ * the brand library. Every LWP in a process will incur this overhead beyond
+ * the regular thread stack:
+ */
+#define LX_NATIVE_STACK_PAGE_COUNT 64
+
+/*
+ * When returning in a new child process created with vfork(2) (or CLONE_VFORK)
+ * we discard some of the native stack to prevent corruption of the parent
+ * emulation state.
+ */
+#define LX_NATIVE_STACK_VFORK_GAP 0x3000
+
+#ifndef _ASM
+
+extern struct brand lx_brand;
+
+typedef struct lx_brand_registration {
+ uint_t lxbr_version; /* version number */
+ void *lxbr_handler; /* base address of handler */
+ uint32_t lxbr_flags; /* LX_PROC_* registration flags */
+} lx_brand_registration_t;
+
+typedef struct lx_brand_registration32 {
+ uint_t lxbr_version; /* version number */
+ uint32_t lxbr_handler; /* base address of handler */
+ uint32_t lxbr_flags; /* LX_PROC_* registration flags */
+} lx_brand_registration32_t;
+
+#endif /* _ASM */
+
+/*
+ * GDT usage
+ */
+#define GDT_TLSMIN (GDT_BRANDMIN)
+#define GDT_TLSMAX (GDT_TLSMIN + 2)
+#define LX_TLSNUM (GDT_TLSMAX - GDT_TLSMIN)
+
+#ifndef _ASM
+
+/*
+ * Stores information needed by the lx linker to launch the main
+ * lx executable.
+ */
+typedef struct lx_elf_data64 {
+ uintptr_t ed_phdr;
+ uintptr_t ed_phent;
+ uintptr_t ed_phnum;
+ uintptr_t ed_entry;
+ uintptr_t ed_base;
+ uintptr_t ed_ldentry;
+} lx_elf_data64_t;
+
+typedef struct lx_elf_data32 {
+ uint32_t ed_phdr;
+ uint32_t ed_phent;
+ uint32_t ed_phnum;
+ uint32_t ed_entry;
+ uint32_t ed_base;
+ uint32_t ed_ldentry;
+} lx_elf_data32_t;
+
+#if defined(_LP64)
+typedef lx_elf_data64_t lx_elf_data_t;
+#else
+typedef lx_elf_data32_t lx_elf_data_t;
+#endif
+
+typedef enum lx_proc_flags {
+ /* flags configurable via brandsys() and members of LX_PROC_ALL */
+ LX_PROC_INSTALL_MODE = 0x01,
+ LX_PROC_STRICT_MODE = 0x02,
+ /* internal flags */
+ LX_PROC_CHILD_DEATHSIG = 0x04,
+ LX_PROC_NO_DUMP = 0x08 /* for lx_prctl LX_PR_[GS]ET_DUMPABLE */
+} lx_proc_flags_t;
+
+#define LX_PROC_ALL (LX_PROC_INSTALL_MODE | LX_PROC_STRICT_MODE)
+
+/* Maximum length for fields of LX uname */
+#define LX_SYS_UTS_LN 65
+
+/* Max. length of kernel release string */
+#define LX_KERN_RELEASE_MAX LX_SYS_UTS_LN
+#define LX_KERN_VERSION_MAX LX_SYS_UTS_LN
+
+#ifdef _KERNEL
+
+/*
+ * Entry points for cgroup integration.
+ */
+extern void (*lx_cgrp_initlwp)(vfs_t *, uint_t, id_t, pid_t);
+extern void (*lx_cgrp_freelwp)(vfs_t *, uint_t, id_t, pid_t);
+
+#define LX_RLFAKE_LOCKS 0
+#define LX_RLFAKE_NICE 1
+#define LX_RLFAKE_RTPRIO 2
+#define LX_RLFAKE_RTTIME 3
+
+#define LX_RLFAKE_NLIMITS 4
+
+#define LX_RLIM64_INFINITY (~0ULL)
+
+typedef struct {
+ uint64_t rlim_cur;
+ uint64_t rlim_max;
+} lx_rlimit64_t;
+
+typedef struct {
+ list_node_t lx_clgrpm_link;
+ proc_t *lx_clgrpm_pp;
+} lx_clone_grp_member_t;
+
+typedef struct {
+ kmutex_t lx_clgrp_lock; /* protects cnt & member list */
+ uint_t lx_clgrp_cnt;
+ list_t lx_clgrp_members;
+} lx_clone_grp_t;
+
+/* Entries in the l_clone_grps clone-group array */
+#define LX_CLGRP_FS 0
+#define LX_CLGRP_MAX 1
+
+/* See explanation in lx_mem.c about lx_mremap */
+#define LX_REMAP_ANONCACHE_NENTRIES 4
+typedef struct lx_segmap {
+ uintptr_t lxsm_vaddr; /* virtual address of mapping */
+ size_t lxsm_size; /* size of mapping in bytes */
+ uint64_t lxsm_lru; /* LRU field for cache */
+ uint_t lxsm_flags; /* protection and attribute flags */
+} lx_segmap_t;
+
+typedef struct lx_proc_data {
+ uintptr_t l_handler; /* address of user-space handler */
+ pid_t l_ppid; /* pid of originating parent proc */
+ uid_t l_loginuid; /* /proc/{pid}/loginuid */
+ int64_t l_ptrace; /* count of process lwps observed by ptrace */
+ lx_elf_data_t l_elf_data; /* ELF data for linux executable */
+ /* signal to deliver to parent when this thread group dies */
+ int l_signal;
+ /* native signal to deliver to process when parent dies */
+ int l_parent_deathsig;
+ lx_proc_flags_t l_flags;
+
+ kmutex_t l_clone_grp_lock; /* protects the following member */
+ lx_clone_grp_t *l_clone_grps[LX_CLGRP_MAX];
+
+ lx_rlimit64_t l_fake_limits[LX_RLFAKE_NLIMITS];
+
+ kmutex_t l_io_ctx_lock; /* protects the following members */
+ uintptr_t l_io_ctxpage;
+ kcondvar_t l_io_destroy_cv;
+ uint_t l_io_ctx_cnt;
+ struct lx_io_ctx **l_io_ctxs;
+
+ /* Override zone-wide settings for uname release and version */
+ char l_uname_release[LX_KERN_RELEASE_MAX];
+ char l_uname_version[LX_KERN_VERSION_MAX];
+
+ /* Linux process personality */
+ unsigned int l_personality;
+
+ /* VDSO location */
+ uintptr_t l_vdso;
+
+ /* mremap anon cache */
+ kmutex_t l_remap_anoncache_lock;
+ uint64_t l_remap_anoncache_generation;
+ lx_segmap_t l_remap_anoncache[LX_REMAP_ANONCACHE_NENTRIES];
+
+ /* Block all signals to all threads; used during vfork */
+ uint_t l_block_all_signals;
+} lx_proc_data_t;
+
+#endif /* _KERNEL */
+
+/*
+ * Linux process personality(2) flags stored in l_personality
+ */
+#define LX_PER_UNAME26 0x0020000
+#define LX_PER_ADDR_NO_RANDOMIZE 0x0040000
+#define LX_PER_FDPIC_FUNCPTRS 0x0080000
+#define LX_PER_MMAP_PAGE_ZERO 0x0100000
+#define LX_PER_ADDR_COMPAT_LAYOUT 0x0200000
+#define LX_PER_READ_IMPLIES_EXEC 0x0400000
+#define LX_PER_ADDR_LIMIT_32BIT 0x0800000
+#define LX_PER_SHORT_INODE 0x1000000
+#define LX_PER_WHOLE_SECONDS 0x2000000
+#define LX_PER_STICKY_TIMEOUTS 0x4000000
+#define LX_PER_ADDR_LIMIT_3GB 0x8000000
+
+#define LX_PER_LINUX 0x00
+#define LX_PER_SUNOS (0x06 | LX_PER_STICKY_TIMEOUTS)
+#define LX_PER_MASK 0xff
+
+/* max. number of aio control blocks (see lx_io_setup) allowed across zone */
+#define LX_AIO_MAX_NR 65536
+
+/*
+ * A data type big enough to bitmap all Linux possible cpus.
+ * The bitmap size is defined as 1024 cpus in the Linux 2.4 and 2.6 man pages
+ * for sched_getaffinity() and sched_getaffinity().
+ */
+#define LX_NCPU (1024)
+#define LX_AFF_ULONGS (LX_NCPU / (8 * sizeof (ulong_t)))
+typedef ulong_t lx_affmask_t[LX_AFF_ULONGS];
+
+/*
+ * Flag values for uc_brand_data[0] in the ucontext_t:
+ */
+#define LX_UC_STACK_NATIVE 0x00001
+#define LX_UC_STACK_BRAND 0x00002
+#define LX_UC_RESTORE_NATIVE_SP 0x00010
+#define LX_UC_FRAME_IS_SYSCALL 0x00100
+#define LX_UC_RESTART_SYSCALL 0x01000
+#define LX_UC_IGNORE_LINK 0x10000
+
+#ifdef _KERNEL
+
+typedef struct lx_lwp_data lx_lwp_data_t;
+
+/*
+ * Flag values for "lxpa_flags" on a ptrace(2) accord.
+ */
+typedef enum lx_accord_flags {
+ LX_ACC_TOMBSTONE = 0x01
+} lx_accord_flags_t;
+
+/*
+ * Flags values for "br_ptrace_flags" in the LWP-specific data.
+ */
+typedef enum lx_ptrace_flags {
+ LX_PTF_SYSCALL = 0x01, /* handling syscall or a trap */
+ LX_PTF_EXITING = 0x02,
+ LX_PTF_STOPPING = 0x04,
+ LX_PTF_INHERIT = 0x08,
+ LX_PTF_STOPPED = 0x10,
+ LX_PTF_PARENT_WAIT = 0x20,
+ LX_PTF_CLDPEND = 0x40,
+ LX_PTF_CLONING = 0x80,
+ LX_PTF_WAITPEND = 0x100,
+ LX_PTF_NOSTOP = 0x200, /* disable syscall stop event */
+ LX_PTF_INSYSCALL = 0x400 /* between syscall enter & exit */
+} lx_ptrace_flags_t;
+
+/*
+ * A ptrace(2) accord represents the relationship between a tracer LWP and the
+ * set of LWPs that it is tracing: the tracees. This data structure belongs
+ * primarily to the tracer, but is reference counted so that it may be freed by
+ * whoever references it last.
+ */
+typedef struct lx_ptrace_accord {
+ kmutex_t lxpa_lock;
+ uint_t lxpa_refcnt;
+ lx_accord_flags_t lxpa_flags;
+
+ /*
+ * The tracer must hold "pidlock" while clearing these fields for
+ * exclusion of waitid(), etc.
+ */
+ lx_lwp_data_t *lxpa_tracer;
+ kcondvar_t *lxpa_cvp;
+
+ /*
+ * The "lxpa_tracees_lock" mutex protects the tracee list.
+ */
+ kmutex_t lxpa_tracees_lock;
+ list_t lxpa_tracees;
+} lx_ptrace_accord_t;
+
+/*
+ * These values are stored in the per-LWP data for a tracee when it is attached
+ * to a tracer. They record the method that was used to attach.
+ */
+typedef enum lx_ptrace_attach {
+ LX_PTA_NONE = 0x00, /* not attached */
+ LX_PTA_ATTACH = 0x01, /* due to tracer using PTRACE_ATTACH */
+ LX_PTA_TRACEME = 0x02, /* due to child using PTRACE_TRACEME */
+ LX_PTA_INHERIT_CLONE = 0x04, /* due to PTRACE_CLONE clone(2) flag */
+ LX_PTA_INHERIT_OPTIONS = 0x08 /* due to PTRACE_SETOPTIONS options */
+} lx_ptrace_attach_t;
+
+typedef enum lx_stack_mode {
+ LX_STACK_MODE_PREINIT = 0,
+ LX_STACK_MODE_INIT,
+ LX_STACK_MODE_NATIVE,
+ LX_STACK_MODE_BRAND
+} lx_stack_mode_t;
+
+struct lx_pid {
+ pid_t lxp_spid; /* the SunOS pid and ... */
+ id_t lxp_stid; /* ... tid pair */
+ pid_t lxp_lpid; /* the corresponding linux pid */
+ time_t lxp_start; /* birthday of this pid */
+ struct pid *lxp_pidp; /* allocated pid struct */
+ proc_t *lxp_procp; /* proc_t corresponding to lxp_spid */
+ struct lx_pid *lxp_stol_next; /* link in stol hash table */
+ struct lx_pid *lxp_ltos_next; /* link in ltos hash table */
+};
+
+/*
+ * lx-specific data in the klwp_t
+ */
+struct lx_lwp_data {
+ uint_t br_lwp_flags; /* misc. flags */
+ klwp_t *br_lwp; /* back pointer to container lwp */
+ int br_signal; /* signal to send to parent when */
+ /* clone()'ed child terminates */
+ int br_exitwhy; /* reason for thread (process) exit */
+ int br_exitwhat; /* exit code / killing signal */
+ cpuset_t *br_affinitymask; /* bitmask of CPU sched affinities */
+ struct user_desc br_tls[LX_TLSNUM];
+ /* descriptors used by libc for TLS */
+ ulong_t br_lx_fsbase; /* lx fsbase for 64-bit thread ptr */
+ ulong_t br_ntv_fsbase; /* native fsbase 64-bit thread ptr */
+ ulong_t br_lx_gsbase; /* lx user-land gsbase */
+ ulong_t br_ntv_gsbase; /* native user-land gsbase */
+ pid_t br_pid; /* converted pid for this thread */
+ pid_t br_tgid; /* thread group ID for this thread */
+ pid_t br_ppid; /* parent pid for this thread */
+ id_t br_ptid; /* parent tid for this thread */
+ void *br_clear_ctidp; /* clone thread id ptr */
+ void *br_set_ctidp; /* clone thread id ptr */
+ void *br_robust_list; /* robust lock list, if any */
+
+ /* first 4 syscall args - used for auditing */
+ uintptr_t br_syscall_args[4];
+
+ /*
+ * The following struct is used by some system calls to pass extra
+ * flags into the kernel without impinging on the namespace for
+ * illumos.
+ */
+ void *br_scall_args;
+ int br_args_size; /* size in bytes of br_scall_args */
+
+ boolean_t br_waitid_emulate;
+ int br_waitid_flags;
+
+ lx_ptrace_flags_t br_ptrace_flags; /* ptrace flags for this LWP */
+ lx_ptrace_options_t br_ptrace_options; /* PTRACE_SETOPTIONS options */
+ lx_ptrace_options_t br_ptrace_clone_option; /* current clone(2) type */
+
+ lx_ptrace_attach_t br_ptrace_attach; /* how did we get attached */
+ lx_ptrace_accord_t *br_ptrace_accord; /* accord for this tracer LWP */
+ lx_ptrace_accord_t *br_ptrace_tracer; /* accord tracing this LWP */
+ list_node_t br_ptrace_linkage; /* linkage for lxpa_tracees list */
+
+ ushort_t br_ptrace_whystop; /* stop reason, 0 for no stop */
+ ushort_t br_ptrace_whatstop; /* stop sub-reason */
+
+ int32_t br_ptrace_stopsig; /* stop signal, 0 for no signal */
+ /*
+ * Track the last (native) signal number processed by a ptrace.
+ * This allows the tracee to properly handle ignored signals after
+ * the tracer has been notified and the tracee restarted.
+ */
+ int32_t br_ptrace_donesig;
+ uintptr_t br_ptrace_stopucp; /* usermode ucontext_t pointer */
+
+ uint_t br_ptrace_event;
+ ulong_t br_ptrace_eventmsg;
+
+ int br_syscall_num; /* current system call number */
+ boolean_t br_syscall_restart; /* should restart on EINTR */
+
+ /*
+ * Store the LX_STACK_MODE for this LWP, and the current extent of the
+ * native (emulation) stack. This is similar, in principle, to the
+ * sigaltstack mechanism for signal handling. We also use this mode
+ * flag to determine how to process system calls from this LWP.
+ */
+ lx_stack_mode_t br_stack_mode;
+ uintptr_t br_ntv_stack;
+ uintptr_t br_ntv_stack_current;
+
+ /*
+ * If strict mode is enabled (via LX_STRICT in the environment), any
+ * call to lx_unsupported() will set this boolean to B_TRUE. This will
+ * cause us to drop SIGSYS on the LWP as it attempts to return to
+ * usermode.
+ */
+ boolean_t br_strict_failure;
+
+ /*
+ * Some syscalls emulated in-kernel still call back out to the
+ * userspace emulation for certain functions. When that is the case,
+ * the syscall_return logic must be bypassed at the end of the
+ * in-kernel syscall code. The NORMALRETURN and JUSTRETURN constants
+ * are used to choose the behavior.
+ */
+ char br_eosys;
+
+ /*
+ * Hold a pre-allocated lx_pid structure to be used during lx_initlwp.
+ */
+ struct lx_pid *br_lpid;
+
+ /*
+ * ID of the cgroup this thread belongs to.
+ */
+ uint_t br_cgroupid;
+
+ /*
+ * When the zone is running under FSS (which is the common case) then
+ * we cannot change scheduling class, so we emulate that. By default
+ * Linux uses LX_SCHED_OTHER (which is 0) and that only supports a
+ * priority of 0, so no special initialization is needed.
+ */
+ int br_schd_class; /* emulated scheduling class */
+ int br_schd_pri; /* emulated scheduling priority */
+ uint64_t br_schd_flags; /* emulated [sg]et_attr flags */
+ uint64_t br_schd_runtime; /* emulated DEADLINE */
+ uint64_t br_schd_deadline; /* emulated DEADLINE */
+ uint64_t br_schd_period; /* emulated DEADLINE */
+
+ fwaiter_t br_fwaiter; /* futex upon which we're waiting */
+ uint_t br_clone_grp_flags; /* pending clone group */
+};
+
+/*
+ * Upper limit on br_args_size, low because this value can persist until
+ * overridden with another value, and the size is given from userland.
+ */
+#define LX_BR_ARGS_SIZE_MAX (1024)
+
+typedef enum lx_audit_enbl {
+ LXAE_DISABLED,
+ LXAE_ENABLED,
+ LXAE_LOCKED
+} lx_audit_enbl_t;
+
+/*
+ * brand specific data
+ *
+ * We currently only support a single cgroup mount in an lx zone so we only have
+ * one ptr (lxzd_cgroup) but this could be changed to a list if cgroups is ever
+ * enhanced to support different mounts with different subsystem controllers.
+ */
+typedef struct lx_zone_data {
+ kmutex_t lxzd_lock; /* protects all members */
+ char lxzd_kernel_release[LX_KERN_RELEASE_MAX];
+ char lxzd_kernel_version[LX_KERN_VERSION_MAX];
+ ksocket_t lxzd_ioctl_sock;
+ char lxzd_bootid[UUID_PRINTABLE_STRING_LENGTH]; /* procfs boot_id */
+ gid_t lxzd_ttygrp; /* tty gid for pty chown */
+ vfs_t *lxzd_cgroup; /* cgroup for this zone */
+ pid_t lxzd_lockd_pid; /* pid of NFS lockd */
+ list_t *lxzd_vdisks; /* virtual disks (zvols) */
+ dev_t lxzd_zfs_dev; /* major num for zfs */
+ uint_t lxzd_aio_nr; /* see lx_aio.c */
+ uint_t lxzd_pipe_max_sz; /* pipe-max-size sysctl val */
+ boolean_t lxzd_swap_disabled; /* no fake swap in zone? */
+ lx_audit_enbl_t lxzd_audit_enabled; /* auditing? */
+ struct lx_audit_state *lxzd_audit_state; /* zone's audit state */
+} lx_zone_data_t;
+
+/* LWP br_lwp_flags values */
+#define BR_CPU_BOUND 0x0001
+#define BR_AIO_LWP 0x0002 /* aio kernel worker thread */
+
+#define ttolxlwp(t) ((struct lx_lwp_data *)ttolwpbrand(t))
+#define lwptolxlwp(l) ((struct lx_lwp_data *)lwptolwpbrand(l))
+#define ttolxproc(t) \
+ (((t)->t_procp->p_brand == &lx_brand) ? \
+ (struct lx_proc_data *)(t)->t_procp->p_brand_data : NULL)
+#define ptolxproc(p) \
+ (((p)->p_brand == &lx_brand) ? \
+ (struct lx_proc_data *)(p)->p_brand_data : NULL)
+#define ztolxzd(z) \
+ (((z)->zone_brand == &lx_brand) ? \
+ (lx_zone_data_t *)(z)->zone_brand_data : NULL)
+
+/* Macro for converting to system call arguments. */
+#define LX_ARGS(scall) ((struct lx_##scall##_args *)\
+ (ttolxlwp(curthread)->br_scall_args))
+
+typedef enum lx_virt_disk_type {
+ LXVD_NONE,
+ LXVD_ZFS_DS,
+ LXVD_ZVOL
+} lx_virt_disk_type_t;
+
+typedef struct lx_virt_disk {
+ list_node_t lxvd_link;
+ char lxvd_name[MAXNAMELEN];
+ lx_virt_disk_type_t lxvd_type;
+ dev_t lxvd_emul_dev;
+ dev_t lxvd_real_dev;
+ uint64_t lxvd_volsize;
+ uint64_t lxvd_blksize;
+ char lxvd_real_name[MAXPATHLEN];
+} lx_virt_disk_t;
+
+/*
+ * Determine the upper bound on the system call number:
+ */
+#if defined(_LP64)
+#define LX_MAX_SYSCALL(lwp) \
+ ((lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) ? \
+ lx_nsysent64 : lx_nsysent32)
+#else
+#define LX_MAX_SYSCALL(lwp) lx_nsysent32
+#endif
+
+extern int lx_kern_release_cmp(zone_t *, const char *);
+
+extern void lx_lwp_set_native_stack_current(lx_lwp_data_t *, uintptr_t);
+extern void lx_divert(klwp_t *, uintptr_t);
+extern int lx_runexe(klwp_t *, void *);
+extern void lx_switch_to_native(klwp_t *);
+
+extern int lx_syscall_enter(void);
+extern void lx_syscall_return(klwp_t *, int, long);
+
+extern void lx_trace_sysenter(int, uintptr_t *);
+extern void lx_trace_sysreturn(int, long);
+
+extern void lx_emulate_user(klwp_t *, int, uintptr_t *);
+
+extern void lx_audit_ld();
+extern void lx_audit_unld();
+extern void lx_audit_fini(zone_t *);
+extern void lx_audit_syscall_exit(int, long);
+
+#if defined(_SYSCALL32_IMPL)
+extern void lx_emulate_user32(klwp_t *, int, uintptr_t *);
+#endif
+
+extern int lx_debug;
+#define lx_print if (lx_debug) printf
+
+/*
+ * Flags for lx_lpid_lock()
+ */
+typedef enum {
+ LXP_PRLOCK = 0x1, /* acquire PR_LOCK as part of locking */
+ LXP_ZOMBOK = 0x2 /* allow locking of zombies */
+} lx_pid_flag_t;
+
+extern void lx_pid_assign(kthread_t *, struct lx_pid *);
+extern void lx_pid_reassign(kthread_t *);
+extern void lx_pid_rele(pid_t, id_t);
+extern pid_t lx_lpid_to_spair(pid_t, pid_t *, id_t *);
+extern int lx_lpid_lock(pid_t, zone_t *, lx_pid_flag_t, proc_t **,
+ kthread_t **);
+extern pid_t lx_lwp_ppid(klwp_t *, pid_t *, id_t *);
+extern void lx_pid_init(void);
+extern void lx_pid_fini(void);
+extern void lx_acct_out(vnode_t *, int);
+
+extern uint_t lx_pipe_max_limit;
+extern uint_t lx_pipe_max_default;
+
+/*
+ * In-Kernel Linux System Call Description.
+ */
+typedef struct lx_sysent {
+ char *sy_name;
+ long (*sy_callc)();
+ char sy_flags;
+ char sy_narg;
+} lx_sysent_t;
+
+#if defined(_LP64)
+extern lx_sysent_t lx_sysent64[LX_NSYSCALLS + 1];
+extern int lx_nsysent64;
+#endif
+extern lx_sysent_t lx_sysent32[LX_NSYSCALLS + 1];
+extern int lx_nsysent32;
+
+#endif /* _KERNEL */
+#endif /* _ASM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_BRAND_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_fcntl.h b/usr/src/uts/common/brand/lx/sys/lx_fcntl.h
new file mode 100644
index 0000000000..f82c6b867d
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_fcntl.h
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYS_LX_FCNTL_H
+#define _SYS_LX_FCNTL_H
+
+#include <sys/vnode.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Lx open/fcntl flags
+ */
+#define LX_O_RDONLY 00
+#define LX_O_WRONLY 01
+#define LX_O_RDWR 02
+#define LX_O_ACCMODE (LX_O_RDONLY | LX_O_WRONLY | LX_O_RDWR)
+#define LX_O_CREAT 0100
+#define LX_O_EXCL 0200
+#define LX_O_NOCTTY 0400
+#define LX_O_TRUNC 01000
+#define LX_O_APPEND 02000
+#define LX_O_NONBLOCK 04000
+#define LX_O_NDELAY LX_O_NONBLOCK
+#define LX_O_SYNC 010000
+#define LX_O_FSYNC LX_O_SYNC
+#define LX_O_ASYNC 020000
+#define LX_O_DIRECT 040000
+#define LX_O_LARGEFILE 0100000
+#define LX_O_DIRECTORY 0200000
+#define LX_O_NOFOLLOW 0400000
+#define LX_O_CLOEXEC 02000000
+#define LX_O_PATH 010000000
+
+#define LX_F_DUPFD 0
+#define LX_F_GETFD 1
+#define LX_F_SETFD 2
+#define LX_F_GETFL 3
+#define LX_F_SETFL 4
+#define LX_F_GETLK 5
+#define LX_F_SETLK 6
+#define LX_F_SETLKW 7
+#define LX_F_SETOWN 8
+#define LX_F_GETOWN 9
+#define LX_F_SETSIG 10
+#define LX_F_GETSIG 11
+
+#define LX_F_GETLK64 12
+#define LX_F_SETLK64 13
+#define LX_F_SETLKW64 14
+
+#define LX_F_SETLEASE 1024
+#define LX_F_GETLEASE 1025
+#define LX_F_NOTIFY 1026
+#define LX_F_CANCELLK 1029
+#define LX_F_DUPFD_CLOEXEC 1030
+#define LX_F_SETPIPE_SZ 1031
+#define LX_F_GETPIPE_SZ 1032
+
+#define LX_F_RDLCK 0
+#define LX_F_WRLCK 1
+#define LX_F_UNLCK 2
+
+/* Test for emulated O_PATH setting in file_t flags */
+#define LX_IS_O_PATH(f) (((f)->f_flag & (FREAD|FWRITE)) == 0)
+
+extern int lx_vp_at(int, char *, vnode_t **, int);
+
+/*
+ * Lx flock codes.
+ */
+#define LX_NAME_MAX 255
+#define LX_LOCK_SH 1 /* shared */
+#define LX_LOCK_EX 2 /* exclusive */
+#define LX_LOCK_NB 4 /* non-blocking */
+#define LX_LOCK_UN 8 /* unlock */
+
+/*
+ * On Linux the constants AT_REMOVEDIR and AT_EACCESS have the same value.
+ * AT_REMOVEDIR is used only by unlinkat and AT_EACCESS is used only by
+ * faccessat.
+ */
+#define LX_AT_FDCWD (-100)
+#define LX_AT_SYMLINK_NOFOLLOW 0x100
+#define LX_AT_REMOVEDIR 0x200
+#define LX_AT_EACCESS 0x200
+#define LX_AT_SYMLINK_FOLLOW 0x400
+#define LX_AT_NO_AUTOMOUNT 0x800
+#define LX_AT_EMPTY_PATH 0x1000
+
+typedef struct lx_flock {
+ short l_type;
+ short l_whence;
+ long l_start;
+ long l_len;
+ int l_pid;
+} lx_flock_t;
+
+typedef struct lx_flock64 {
+ short l_type;
+ short l_whence;
+ long long l_start;
+ long long l_len;
+ int l_pid;
+} lx_flock64_t;
+
+#if defined(_KERNEL)
+
+/*
+ * 64-bit kernel view of 32-bit usermode structs.
+ */
+#pragma pack(4)
+typedef struct lx_flock32 {
+ int16_t l_type;
+ int16_t l_whence;
+ int32_t l_start;
+ int32_t l_len;
+ int32_t l_pid;
+} lx_flock32_t;
+
+typedef struct lx_flock64_32 {
+ int16_t l_type;
+ int16_t l_whence;
+ int64_t l_start;
+ int64_t l_len;
+ int32_t l_pid;
+} lx_flock64_32_t;
+#pragma pack()
+
+#endif /* _KERNEL && _SYSCALL32_IMPL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_FCNTL_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_futex.h b/usr/src/uts/common/brand/lx/sys/lx_futex.h
new file mode 100644
index 0000000000..7eba389218
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_futex.h
@@ -0,0 +1,143 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2017, Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_FUTEX_H
+#define _SYS_LX_FUTEX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FUTEX_WAIT 0
+#define FUTEX_WAKE 1
+#define FUTEX_FD 2
+#define FUTEX_REQUEUE 3
+#define FUTEX_CMP_REQUEUE 4
+#define FUTEX_WAKE_OP 5
+#define FUTEX_LOCK_PI 6
+#define FUTEX_UNLOCK_PI 7
+#define FUTEX_TRYLOCK_PI 8
+#define FUTEX_WAIT_BITSET 9
+#define FUTEX_WAKE_BITSET 10
+#define FUTEX_WAIT_REQUEUE_PI 11
+#define FUTEX_CMP_REQUEUE_PI 12
+#define FUTEX_MAX_CMD FUTEX_CMP_REQUEUE_PI
+
+/*
+ * Flags that can be OR'd into a futex operation.
+ */
+#define FUTEX_CMD_MASK 0x007f
+#define FUTEX_PRIVATE_FLAG 0x0080
+#define FUTEX_CLOCK_REALTIME 0x0100
+
+#define FUTEX_BITSET_MATCH_ANY 0xffffffff
+/*
+ * FUTEX_WAKE_OP operations
+ */
+#define FUTEX_OP_SET 0 /* *(int *)UADDR2 = OPARG; */
+#define FUTEX_OP_ADD 1 /* *(int *)UADDR2 += OPARG; */
+#define FUTEX_OP_OR 2 /* *(int *)UADDR2 |= OPARG; */
+#define FUTEX_OP_ANDN 3 /* *(int *)UADDR2 &= ~OPARG; */
+#define FUTEX_OP_XOR 4 /* *(int *)UADDR2 ^= OPARG; */
+
+/*
+ * FUTEX_WAKE_OP comparison operations
+ */
+#define FUTEX_OP_CMP_EQ 0 /* if (oldval == CMPARG) wake */
+#define FUTEX_OP_CMP_NE 1 /* if (oldval != CMPARG) wake */
+#define FUTEX_OP_CMP_LT 2 /* if (oldval < CMPARG) wake */
+#define FUTEX_OP_CMP_LE 3 /* if (oldval <= CMPARG) wake */
+#define FUTEX_OP_CMP_GT 4 /* if (oldval > CMPARG) wake */
+#define FUTEX_OP_CMP_GE 5 /* if (oldval >= CMPARG) wake */
+
+/*
+ * The encoding of the FUTEX_WAKE_OP operation in 32 bits:
+ *
+ * +--+-- - --+-- - --+-- - --+-- - --+
+ * |S |OP |CMP |OPARG |CMPARG |
+ * +--+-- - --+-- - --+-- - --+-- - --+
+ * |31|30 - 28|27 - 24|23 - 12|11 - 0|
+ *
+ * The S bit denotes that the OPARG should be (1 << OPARG) instead of OPARG.
+ * (Yes, this whole thing is entirely absurd -- see the block comment in
+ * lx_futex.c for an explanation of this nonsense.) Macros to extract the
+ * various components from the operation, given the above encoding:
+ */
+#define FUTEX_OP_OP(x) (((x) >> 28) & 7)
+#define FUTEX_OP_CMP(x) (((x) >> 24) & 15)
+#define FUTEX_OP_OPARG(x) (((x) >> 31) ? (1 << (((x) << 8) >> 20)) : \
+ ((((x) << 8) >> 20)))
+#define FUTEX_OP_CMPARG(x) (((x) << 20) >> 20)
+
+#ifdef _KERNEL
+
+/*
+ * This structure is used to track all the threads currently waiting on a
+ * futex. There is one fwaiter_t for each blocked thread. We store all
+ * fwaiter_t's in a hash structure, indexed by the memid_t of the integer
+ * containing the futex's value.
+ *
+ * At the moment, all fwaiter_t's for a single futex are simply dumped into
+ * the hash bucket. If futex contention ever becomes a hot path, we can
+ * chain a single futex's waiters together.
+ */
+typedef struct fwaiter {
+ memid_t fw_memid; /* memid of the user-space futex */
+ kcondvar_t fw_cv; /* cond var */
+ struct fwaiter *fw_next; /* hash queue */
+ struct fwaiter *fw_prev; /* hash queue */
+ uint32_t fw_bits; /* bits waiting on */
+ pid_t fw_tid; /* for PI futexes; the waiter's tid */
+ int fw_opri; /* for PI futexes; original pri. */
+ boolean_t fw_pri_up; /* for PI futexes; pri. increased */
+ volatile int fw_woken;
+} fwaiter_t;
+
+#define FUTEX_WAITERS 0x80000000
+#define FUTEX_OWNER_DIED 0x40000000
+#define FUTEX_TID_MASK 0x3fffffff
+
+#define FUTEX_ROBUST_LOCK_PI 1
+#define FUTEX_ROBUST_LIST_LIMIT 2048
+
+extern long lx_futex(uintptr_t addr, int cmd, int val, uintptr_t lx_timeout,
+ uintptr_t addr2, int val2);
+extern void lx_futex_init(void);
+extern int lx_futex_fini(void);
+extern long lx_set_robust_list(void *listp, size_t len);
+extern long lx_get_robust_list(pid_t pid, void **listp, size_t *lenp);
+extern void lx_futex_robust_exit(uintptr_t addr, uint32_t tid);
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_FUTEX_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_impl.h b/usr/src/uts/common/brand/lx/sys/lx_impl.h
new file mode 100644
index 0000000000..03b9d43038
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_impl.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _LX_IMPL_H
+#define _LX_IMPL_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (lx_systrace_f)(ulong_t, ulong_t, ulong_t, ulong_t, ulong_t,
+ ulong_t, ulong_t);
+
+
+extern lx_systrace_f *lx_systrace_entry_ptr;
+extern lx_systrace_f *lx_systrace_return_ptr;
+
+extern void lx_brand_systrace_enable(void);
+extern void lx_brand_systrace_disable(void);
+
+extern void lx_unsupported(char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_IMPL_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_ldt.h b/usr/src/uts/common/brand/lx/sys/lx_ldt.h
new file mode 100644
index 0000000000..08d4d78efb
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_ldt.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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright 2018 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_LINUX_LDT_H
+#define _SYS_LINUX_LDT_H
+
+#include <sys/segments.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ldt_info {
+ uint_t entry_number;
+ uint_t base_addr;
+ uint_t limit;
+ uint_t seg_32bit:1,
+ contents:2,
+ read_exec_only:1,
+ limit_in_pages:1,
+ seg_not_present:1,
+ useable:1;
+};
+
+#define LDT_INFO_EMPTY(info) \
+ ((info)->base_addr == 0 && (info)->limit == 0 && \
+ (info)->contents == 0 && (info)->read_exec_only == 1 && \
+ (info)->seg_32bit == 0 && (info)->limit_in_pages == 0 && \
+ (info)->seg_not_present == 1 && (info)->useable == 0)
+
+#if defined(__amd64)
+#define SETMODE(desc) (desc)->usd_long = SDP_SHORT;
+#else
+#define SETMODE(desc)
+#endif
+
+#define LDT_INFO_TO_DESC(info, desc) { \
+ USEGD_SETBASE(desc, (info)->base_addr); \
+ USEGD_SETLIMIT(desc, (info)->limit); \
+ (desc)->usd_type = ((info)->contents << 2) | \
+ ((info)->read_exec_only ^ 1) << 1 | SDT_S | SDT_A; \
+ (desc)->usd_dpl = SEL_UPL; \
+ (desc)->usd_p = (info)->seg_not_present ^ 1; \
+ (desc)->usd_def32 = (info)->seg_32bit; \
+ (desc)->usd_gran = (info)->limit_in_pages; \
+ (desc)->usd_avl = (info)->useable; \
+ SETMODE(desc); \
+}
+
+#define DESC_TO_LDT_INFO(desc, info) { \
+ bzero((info), sizeof (*(info))); \
+ (info)->base_addr = USEGD_GETBASE(desc); \
+ (info)->limit = USEGD_GETLIMIT(desc); \
+ (info)->seg_not_present = (desc)->usd_p ^ 1; \
+ (info)->contents = ((desc)->usd_type >> 2) & 3; \
+ (info)->read_exec_only = (((desc)->usd_type >> 1) & 1) ^ 1; \
+ (info)->seg_32bit = (desc)->usd_def32; \
+ (info)->limit_in_pages = (desc)->usd_gran; \
+ (info)->useable = (desc)->usd_avl; \
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LINUX_LDT_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_misc.h b/usr/src/uts/common/brand/lx/sys/lx_misc.h
new file mode 100644
index 0000000000..e81a1597f3
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_misc.h
@@ -0,0 +1,135 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#ifndef _SYS__LX_MISC_H
+#define _SYS__LX_MISC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <sys/siginfo.h>
+#include <sys/lx_brand.h>
+
+#ifdef _KERNEL
+
+extern void lx_setrval(klwp_t *, int, int);
+extern void lx_exec();
+extern void lx_exitlwp(klwp_t *);
+extern void lx_freelwp(klwp_t *);
+extern void *lx_lwpdata_alloc(proc_t *);
+extern void lx_lwpdata_free(void *);
+extern void lx_initlwp(klwp_t *, void *);
+extern void lx_initlwp_post(klwp_t *);
+extern void lx_forklwp(klwp_t *, klwp_t *);
+
+extern void lx_affinity_forklwp(klwp_t *, klwp_t *);
+
+extern void lx_set_gdt(int, user_desc_t *);
+extern void lx_clear_gdt(int);
+
+extern longlong_t lx_nosys();
+
+extern void lx_clone_grp_create(uint_t);
+extern void lx_clone_grp_enter(uint_t, proc_t *, proc_t *);
+extern void lx_clone_grp_exit(proc_t *, boolean_t);
+extern boolean_t lx_clone_grp_member(lx_proc_data_t *, uint_t);
+extern int lx_clone_grp_walk(lx_proc_data_t *, uint_t,
+ int (*)(proc_t *, void *), void *);
+
+extern greg_t lx_fixsegreg(greg_t, model_t);
+extern uintptr_t lx_fsbase(klwp_t *, uintptr_t);
+extern void lx_exit_with_sig(proc_t *, sigqueue_t *);
+extern boolean_t lx_wait_filter(proc_t *, proc_t *);
+extern void lx_sigfd_translate(k_siginfo_t *);
+extern int stol_ksiginfo_copyout(k_siginfo_t *, void *);
+
+extern int ltos_at_flag(int, int, boolean_t);
+#if defined(_SYSCALL32_IMPL)
+extern int stol_ksiginfo32_copyout(k_siginfo_t *, void *);
+#endif
+
+typedef enum lx_regs_location {
+ LX_REG_LOC_UNAVAIL,
+ LX_REG_LOC_LWP,
+ LX_REG_LOC_UCP
+} lx_regs_location_t;
+
+extern lx_regs_location_t lx_regs_location(lx_lwp_data_t *, void **, boolean_t);
+
+
+typedef enum lx_if_action {
+ LX_IF_FROMNATIVE,
+ LX_IF_TONATIVE
+} lx_if_action_t;
+
+/* Linux ARP protocol hardware identifiers */
+#define LX_ARPHRD_ETHER 1 /* Ethernet */
+#define LX_ARPHRD_LOOPBACK 772 /* Loopback */
+#define LX_ARPHRD_VOID 0xffff /* Unknown */
+
+/* IPv6 address scope values used in /proc/net/if_inet6 */
+#define LX_IPV6_ADDR_LOOPBACK 0x0010U
+#define LX_IPV6_ADDR_LINKLOCAL 0x0020U
+#define LX_IPV6_ADDR_SITELOCAL 0x0040U
+#define LX_IPV6_ADDR_COMPATv4 0x0080U
+
+/* Maximum length of a thread name, including the NUL terminator */
+#define LX_PR_SET_NAME_NAMELEN 16
+
+extern void lx_ifname_convert(char *, lx_if_action_t);
+extern void lx_ifflags_convert(uint64_t *, lx_if_action_t);
+extern unsigned int lx_ipv6_scope_convert(const in6_addr_t *);
+extern void lx_stol_hwaddr(const struct sockaddr_dl *, struct sockaddr *,
+ int *);
+
+extern boolean_t lx_ptrace_stop(ushort_t);
+extern void lx_stop_notify(proc_t *, klwp_t *, ushort_t, ushort_t);
+extern void lx_ptrace_init(void);
+extern void lx_ptrace_fini(void);
+extern int lx_waitid_helper(idtype_t, id_t, k_siginfo_t *, int, boolean_t *,
+ int *);
+extern void lx_ptrace_exit(proc_t *, klwp_t *);
+extern void lx_ptrace_inherit_tracer(lx_lwp_data_t *, lx_lwp_data_t *);
+extern int lx_ptrace_stop_for_option(int, boolean_t, ulong_t, uintptr_t);
+extern int lx_ptrace_set_clone_inherit(int, boolean_t);
+extern int lx_sigcld_repost(proc_t *, sigqueue_t *);
+extern int lx_ptrace_issig_stop(proc_t *, klwp_t *);
+extern boolean_t lx_ptrace_sig_ignorable(proc_t *, klwp_t *, int);
+
+extern int lx_helper_clone(int64_t *, int, void *, void *, void *);
+extern int lx_helper_setgroups(int, gid_t *);
+extern int lx_helper_rt_sigqueueinfo(pid_t, int, siginfo_t *);
+extern int lx_helper_rt_tgsigqueueinfo(pid_t, pid_t, int, siginfo_t *);
+
+extern boolean_t lx_vsyscall_iscall(klwp_t *, uintptr_t, int *);
+extern void lx_vsyscall_enter(proc_t *, klwp_t *, int);
+
+extern void lx_check_strict_failure(lx_lwp_data_t *);
+
+extern boolean_t lx_is_eventfd(file_t *);
+
+extern int lx_read_common(file_t *, uio_t *, size_t *, boolean_t);
+extern int lx_write_common(file_t *, uio_t *, size_t *, boolean_t);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS__LX_MISC_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_ptm.h b/usr/src/uts/common/brand/lx/sys/lx_ptm.h
new file mode 100644
index 0000000000..74bbc939a3
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_ptm.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYS_PTM_LINUX_H
+#define _SYS_PTM_LINUX_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LX_PTM_DRV "lx_ptm"
+#define LX_PTM_MINOR_NODE "lx_ptmajor"
+
+#define LX_PTM_DEV_TO_PTS(dev) (getminor(dev) - 1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_PTM_LINUX_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_siginfo.h b/usr/src/uts/common/brand/lx/sys/lx_siginfo.h
new file mode 100644
index 0000000000..9f606b614f
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_siginfo.h
@@ -0,0 +1,190 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _LX_SIGINFO_H
+#define _LX_SIGINFO_H
+
+#include <sys/lx_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * lx_siginfo_t lsi_code values
+ *
+ * LX_SI_ASYNCNL: Sent by asynch name lookup completion
+ * LX_SI_DETHREAD: Sent by execve() killing subsidiary threads
+ * LX_SI_SIGIO: Sent by queued SIGIO
+ * LX_SI_ASYNCIO: Sent by asynchronous I/O completion
+ * LX_SI_MESGQ: Sent by real time message queue state change
+ * LX_SI_TIMER: Sent by timer expiration
+ * LX_SI_QUEUE: Sent by sigqueue
+ * LX_SI_USER: Sent by kill, sigsend, raise, etc.
+ * LX_SI_KERNEL: Sent by kernel
+ * LX_SI_CODE_NOT_EXIST: Error code. When translating from Linux to
+ * illumos errors, if there is no translation available, this value
+ * should be used. This value should have no meaning as an si_code in
+ * illumos or Linux.
+ *
+ * At present, LX_SI_ASYNCNL, LX_SI_DETHREAD, and LX_SI_SIGIO are unused by
+ * BrandZ.
+ */
+#define LX_SI_CODE_NOT_EXIST (-61)
+#define LX_SI_ASYNCNL (-60)
+#define LX_SI_DETHREAD (-7)
+#define LX_SI_TKILL (-6)
+#define LX_SI_SIGIO (-5)
+#define LX_SI_ASYNCIO (-4)
+#define LX_SI_MESGQ (-3)
+#define LX_SI_TIMER (-2)
+#define LX_SI_QUEUE (-1)
+#define LX_SI_USER (0)
+#define LX_SI_KERNEL (0x80)
+
+#define LX_SI_MAX_SIZE 128
+#define LX_SI_PAD_SIZE_32 ((LX_SI_MAX_SIZE / sizeof (int)) - 3)
+#define LX_SI_PAD_SIZE_64 ((LX_SI_MAX_SIZE / sizeof (int)) - 4)
+
+#if defined(_LP64)
+/*
+ * Because of the odd number (3) of ints before the union, we need to account
+ * for the smaller padding needed on x64 due to the union being offset to an 8
+ * byte boundary.
+ */
+#define LX_SI_PAD_SIZE LX_SI_PAD_SIZE_64
+#else
+#define LX_SI_PAD_SIZE LX_SI_PAD_SIZE_32
+#endif
+
+typedef struct lx_siginfo {
+ int lsi_signo;
+ int lsi_errno;
+ int lsi_code;
+ union {
+ int _pad[LX_SI_PAD_SIZE];
+
+ struct {
+ pid_t _pid;
+ lx_uid16_t _uid;
+ } _kill;
+
+ struct {
+ uint_t _timer1;
+ uint_t _timer2;
+ } _timer;
+
+ struct {
+ pid_t _pid;
+ lx_uid16_t _uid;
+ union sigval _sigval;
+ } _rt;
+
+ struct {
+ pid_t _pid;
+ lx_uid16_t _uid;
+ int _status;
+ clock_t _utime;
+ clock_t _stime;
+ } _sigchld;
+
+ struct {
+ void *_addr;
+ } _sigfault;
+
+ struct {
+ int _band;
+ int _fd;
+ } _sigpoll;
+ } _sifields;
+} lx_siginfo_t;
+
+#if defined(_KERNEL) && defined(_SYSCALL32_IMPL)
+/*
+ * 64-bit kernel view of the 32-bit "lx_siginfo_t" object.
+ */
+#pragma pack(4)
+typedef struct lx_siginfo32 {
+ int lsi_signo;
+ int lsi_errno;
+ int lsi_code;
+ union {
+ int _pad[LX_SI_PAD_SIZE_32];
+
+ struct {
+ pid32_t _pid;
+ lx_uid16_t _uid;
+ } _kill;
+
+ struct {
+ uint_t _timer1;
+ uint_t _timer2;
+ } _timer;
+
+ struct {
+ pid32_t _pid;
+ lx_uid16_t _uid;
+ union sigval32 _sigval;
+ } _rt;
+
+ struct {
+ pid32_t _pid;
+ lx_uid16_t _uid;
+ int _status;
+ clock32_t _utime;
+ clock32_t _stime;
+ } _sigchld;
+
+ struct {
+ caddr32_t _addr;
+ } _sigfault;
+
+ struct {
+ int _band;
+ int _fd;
+ } _sigpoll;
+ } _sifields;
+} lx_siginfo32_t;
+#pragma pack()
+#endif /* defined(_KERNEL) && defined(_SYSCALL32_IMPL) */
+
+#define lsi_pid _sifields._kill._pid
+#define lsi_uid _sifields._kill._uid
+#define lsi_status _sifields._sigchld._status
+#define lsi_utime _sifields._sigchld._utime
+#define lsi_stime _sifields._sigchld._stime
+#define lsi_value _sifields._rt._sigval
+#define lsi_int _sifields._rt._sigval.sivalx_int
+#define lsi_ptr _sifields._rt._sigval.sivalx_ptr
+#define lsi_addr _sifields._sigfault._addr
+#define lsi_band _sifields._sigpoll._band
+#define lsi_fd _sifields._sigpoll._fd
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_SIGINFO_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_signal.h b/usr/src/uts/common/brand/lx/sys/lx_signal.h
new file mode 100644
index 0000000000..552c36238b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_signal.h
@@ -0,0 +1,32 @@
+/*
+ * 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 _LX_SIGNAL_H
+#define _LX_SIGNAL_H
+
+#include <lx_signum.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void lx_ltos_sigset(lx_sigset_t *, k_sigset_t *);
+extern void lx_stol_sigset(k_sigset_t *, lx_sigset_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_SIGNAL_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_socket.h b/usr/src/uts/common/brand/lx/sys/lx_socket.h
new file mode 100644
index 0000000000..99489e4d13
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_socket.h
@@ -0,0 +1,444 @@
+/*
+ * 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 2016 Joyent, Inc.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#ifndef _SYS_LX_SOCKET_H
+#define _SYS_LX_SOCKET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Linux address family definitions
+ * Some of these are not supported
+ */
+#define LX_AF_UNSPEC 0 /* Unspecified */
+#define LX_AF_UNIX 1 /* local file/pipe name */
+#define LX_AF_INET 2 /* IP protocol family */
+#define LX_AF_AX25 3 /* Amateur Radio AX.25 */
+#define LX_AF_IPX 4 /* Novell Internet Protocol */
+#define LX_AF_APPLETALK 5 /* Appletalk */
+#define LX_AF_NETROM 6 /* Amateur radio */
+#define LX_AF_BRIDGE 7 /* Multiprotocol bridge */
+#define LX_AF_ATMPVC 8 /* ATM PVCs */
+#define LX_AF_X25 9 /* X.25 */
+#define LX_AF_INET6 10 /* IPV 6 */
+#define LX_AF_ROSE 11 /* Amateur Radio X.25 */
+#define LX_AF_DECNET 12 /* DECnet */
+#define LX_AF_NETBEUI 13 /* 802.2LLC */
+#define LX_AF_SECURITY 14 /* Security callback */
+#define LX_AF_KEY 15 /* key management */
+#define LX_AF_ROUTE 16 /* Alias to emulate 4.4BSD */
+#define LX_AF_NETLINK LX_AF_ROUTE
+#define LX_AF_PACKET 17 /* Packet family */
+#define LX_AF_ASH 18 /* Ash ? */
+#define LX_AF_ECONET 19 /* Acorn Econet */
+#define LX_AF_ATMSVC 20 /* ATM SVCs */
+#define LX_AF_SNA 22 /* Linux SNA */
+#define LX_AF_IRDA 23 /* IRDA sockets */
+#define LX_AF_PPPOX 24 /* PPPoX sockets */
+#define LX_AF_WANPIPE 25 /* Wanpipe API sockets */
+#define LX_AF_LLC 26
+/* gap in Linux defines for 27 and 28 */
+#define LX_AF_CAN 29
+#define LX_AF_TIPC 30
+#define LX_AF_BLUETOOTH 31 /* Bluetooth sockets */
+#define LX_AF_IUCV 32
+#define LX_AF_RXRPC 33
+
+/* limit of AF mappings */
+#define LX_AF_MAX LX_AF_RXRPC
+
+#define AF_NOTSUPPORTED -1
+#define AF_INVAL -2
+
+/*
+ * Options for use with [gs]etsockopt at the SOL_SOCKET level.
+ */
+#define LX_SOL_SOCKET 1
+
+#define LX_SCM_RIGHTS 1
+#define LX_SCM_CRED 2
+
+#define LX_SO_DEBUG 1
+#define LX_SO_REUSEADDR 2
+#define LX_SO_TYPE 3
+#define LX_SO_ERROR 4
+#define LX_SO_DONTROUTE 5
+#define LX_SO_BROADCAST 6
+#define LX_SO_SNDBUF 7
+#define LX_SO_RCVBUF 8
+#define LX_SO_KEEPALIVE 9
+#define LX_SO_OOBINLINE 10
+#define LX_SO_NO_CHECK 11
+#define LX_SO_PRIORITY 12
+#define LX_SO_LINGER 13
+#define LX_SO_BSDCOMPAT 14
+#define LX_SO_REUSEPORT 15
+/*
+ * For Linux see unix(7) man page SO_PASSCRED description. For Illumos see
+ * socket.h(3HEAD) man page SO_RECVUCRED description.
+ */
+#define LX_SO_PASSCRED 16
+#define LX_SO_PEERCRED 17
+#define LX_SO_RCVLOWAT 18
+#define LX_SO_SNDLOWAT 19
+#define LX_SO_RCVTIMEO 20
+#define LX_SO_SNDTIMEO 21
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define LX_SO_SECURITY_AUTHENTICATION 22
+#define LX_SO_SECURITY_ENCRYPTION_TRANSPORT 23
+#define LX_SO_SECURITY_ENCRYPTION_NETWORK 24
+#define LX_SO_BINDTODEVICE 25
+/* Socket filtering */
+#define LX_SO_ATTACH_FILTER 26
+#define LX_SO_DETACH_FILTER 27
+#define LX_SO_PEERNAME 28
+#define LX_SO_TIMESTAMP 29
+#define LX_SCM_TIMESTAMP LX_SO_TIMESTAMP
+#define LX_SO_ACCEPTCONN 30
+
+#define LX_SO_PEERSEC 31
+#define LX_SO_SNDBUFFORCE 32
+#define LX_SO_RCVBUFFORCE 33
+#define LX_SO_PASSSEC 34
+#define LX_SO_TIMESTAMPNS 35
+#define LX_SCM_TIMESTAMPNS LX_SO_TIMESTAMPNS
+#define LX_SO_MARK 36
+#define LX_SO_TIMESTAMPING 37
+#define LX_SCM_TIMESTAMPING LX_SO_TIMESTAMPING
+#define LX_SO_PROTOCOL 38
+#define LX_SO_DOMAIN 39
+#define LX_SO_RXQ_OVFL 40
+#define LX_SO_WIFI_STATUS 41
+#define LX_SCM_WIFI_STATUS LX_SO_WIFI_STATUS
+#define LX_SO_PEEK_OFF 42
+#define LX_SO_NOFCS 43
+#define LX_SO_LOCK_FILTER 44
+#define LX_SO_SELECT_ERR_QUEUE 45
+#define LX_SO_BUSY_POLL 46
+#define LX_SO_MAX_PACING_RATE 47
+#define LX_SO_BPF_EXTENSIONS 48
+
+/*
+ * Options for use with [gs]etsockopt at the RAW level.
+ * IPPROTO_RAW
+ */
+#define LX_ICMP_FILTER 1
+
+/*
+ * Options for use with [gs]etsockopt at the PACKET level.
+ * SOL_PACKET
+ */
+#define LX_SOL_PACKET 263
+
+#define LX_PACKET_ADD_MEMBERSHIP 1
+#define LX_PACKET_DROP_MEMBERSHIP 2
+#define LX_PACKET_RECV_OUTPUT 3
+#define LX_PACKET_RX_RING 5
+#define LX_PACKET_STATISTICS 6
+
+/*
+ * Options for use with [gs]etsockopt at the NETLINK level.
+ * SOL_NETLINK
+ */
+#define LX_SOL_NETLINK 270
+
+/*
+ * Linux socket type definitions
+ */
+#define LX_SOCK_STREAM 1 /* Connection-based byte streams */
+#define LX_SOCK_DGRAM 2 /* Connectionless, datagram */
+#define LX_SOCK_RAW 3 /* Raw protocol interface */
+#define LX_SOCK_RDM 4 /* Reliably-delivered message */
+#define LX_SOCK_SEQPACKET 5 /* Sequenced packet stream */
+#define LX_SOCK_PACKET 10 /* Linux specific */
+#define LX_SOCK_MAX 11
+
+/*
+ * The Linux socket type can be or-ed with other flags (e.g. SOCK_CLOEXEC).
+ */
+#define LX_SOCK_TYPE_MASK 0xf
+
+/*
+ * Linux flags for socket, socketpair and accept4. These are or-ed into the
+ * socket type value. In the Linux net.h header these come from fcntl.h (note
+ * that they are in octal in the Linux header).
+ */
+#define LX_SOCK_CLOEXEC 0x80000
+#define LX_SOCK_NONBLOCK 0x800
+
+#define SOCK_NOTSUPPORTED -1
+#define SOCK_INVAL -2
+
+/*
+ * PF_PACKET protocol definitions.
+ */
+#define LX_ETH_P_802_3 0x0001
+#define LX_ETH_P_ALL 0x0003
+#define LX_ETH_P_802_2 0x0004
+#define LX_ETH_P_IP 0x0800
+#define LX_ETH_P_ARP 0x0806
+#define LX_ETH_P_IPV6 0x86DD
+
+/*
+ * IP Protocol levels. Some of these match the Illumos IPPROTO_* values.
+ */
+#define LX_IPPROTO_IP 0
+#define LX_IPPROTO_ICMP 1
+#define LX_IPPROTO_IGMP 2
+#define LX_IPPROTO_TCP 6
+#define LX_IPPROTO_UDP 17
+#define LX_IPPROTO_IPV6 41
+#define LX_IPPROTO_ICMPV6 58
+#define LX_IPPROTO_RAW 255
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ * IPPROTO_IP
+ */
+#define LX_IP_TOS 1
+#define LX_IP_TTL 2
+#define LX_IP_HDRINCL 3
+#define LX_IP_OPTIONS 4
+#define LX_IP_ROUTER_ALERT 5
+#define LX_IP_RECVOPTS 6
+#define LX_IP_RETOPTS 7
+#define LX_IP_PKTINFO 8
+#define LX_IP_PKTOPTIONS 9
+#define LX_IP_MTU_DISCOVER 10
+#define LX_IP_RECVERR 11
+#define LX_IP_RECVTTL 12
+#define LX_IP_RECVTOS 13
+#define LX_IP_MTU 14
+#define LX_IP_FREEBIND 15
+#define LX_IP_IPSEC_POLICY 16
+#define LX_IP_XFRM_POLICY 17
+#define LX_IP_PASSSEC 18
+#define LX_IP_TRANSPARENT 19
+#define LX_IP_ORIGDSTADDR 20
+#define LX_IP_MINTTL 21
+#define LX_IP_NODEFRAG 22
+/* Linux apparently leaves a gap here */
+#define LX_IP_MULTICAST_IF 32
+#define LX_IP_MULTICAST_TTL 33
+#define LX_IP_MULTICAST_LOOP 34
+#define LX_IP_ADD_MEMBERSHIP 35
+#define LX_IP_DROP_MEMBERSHIP 36
+#define LX_IP_UNBLOCK_SOURC 37
+#define LX_IP_BLOCK_SOURCE 38
+#define LX_IP_ADD_SOURCE_MEMBERSHIP 39
+#define LX_IP_DROP_SOURCE_MEMBERSHIP 40
+#define LX_IP_MSFILTER 41
+#define LX_MCAST_JOIN_GROUP 42
+#define LX_MCAST_BLOCK_SOURCE 43
+#define LX_MCAST_UNBLOCK_SOURCE 44
+#define LX_MCAST_LEAVE_GROUP 45
+#define LX_MCAST_JOIN_SOURCE_GROUP 46
+#define LX_MCAST_LEAVE_SOURCE_GROUP 47
+#define LX_MCAST_MSFILTER 48
+#define LX_IP_MULTICAST_ALL 49
+#define LX_IP_UNICAST_IF 50
+
+/*
+ * LX_IP_MTU_DISCOVER values
+ */
+#define LX_IP_PMTUDISC_DONT 0
+#define LX_IP_PMTUDISC_WANT 1
+#define LX_IP_PMTUDISC_DO 2
+#define LX_IP_PMTUDISC_PROBE 3
+#define LX_IP_PMTUDISC_INTERFACE 4
+#define LX_IP_PMTUDISC_OMIT 5
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ * IPPROTO_IPV6
+ */
+
+#define LX_IPV6_ADDRFORM 1
+#define LX_IPV6_2292PKTINFO 2
+#define LX_IPV6_2292HOPOPTS 3
+#define LX_IPV6_2292DSTOPTS 4
+#define LX_IPV6_2292RTHDR 5
+#define LX_IPV6_2292PKTOPTIONS 6
+#define LX_IPV6_CHECKSUM 7
+#define LX_IPV6_2292HOPLIMIT 8
+#define LX_IPV6_NEXTHOP 9
+#define LX_IPV6_AUTHHDR 10
+#define LX_IPV6_UNICAST_HOPS 16
+#define LX_IPV6_MULTICAST_IF 17
+#define LX_IPV6_MULTICAST_HOPS 18
+#define LX_IPV6_MULTICAST_LOOP 19
+#define LX_IPV6_JOIN_GROUP 20
+#define LX_IPV6_LEAVE_GROUP 21
+#define LX_IPV6_ROUTER_ALERT 22
+#define LX_IPV6_MTU_DISCOVER 23
+#define LX_IPV6_MTU 24
+#define LX_IPV6_RECVERR 25
+#define LX_IPV6_V6ONLY 26
+#define LX_IPV6_JOIN_ANYCAST 27
+#define LX_IPV6_LEAVE_ANYCAST 28
+#define LX_IPV6_IPSEC_POLICY 34
+#define LX_IPV6_XFRM_POLICY 35
+
+#define LX_IPV6_RECVPKTINFO 49
+#define LX_IPV6_PKTINFO 50
+#define LX_IPV6_RECVHOPLIMIT 51
+#define LX_IPV6_HOPLIMIT 52
+#define LX_IPV6_RECVHOPOPTS 53
+#define LX_IPV6_HOPOPTS 54
+#define LX_IPV6_RTHDRDSTOPTS 55
+#define LX_IPV6_RECVRTHDR 56
+#define LX_IPV6_RTHDR 57
+#define LX_IPV6_RECVDSTOPTS 58
+#define LX_IPV6_DSTOPTS 59
+#define LX_IPV6_RECVTCLASS 66
+#define LX_IPV6_TCLASS 67
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ * IPPROTO_ICMPV6
+ */
+
+#define LX_ICMP6_FILTER 1
+
+/*
+ * Options for use with [gs]etsockopt at the TCP level.
+ * IPPROTO_TCP
+ */
+#define LX_TCP_NODELAY 1 /* Don't delay send to coalesce packets */
+#define LX_TCP_MAXSEG 2 /* Set maximum segment size */
+#define LX_TCP_CORK 3 /* Control sending of partial frames */
+#define LX_TCP_KEEPIDLE 4 /* Start keeplives after this period */
+#define LX_TCP_KEEPINTVL 5 /* Interval between keepalives */
+#define LX_TCP_KEEPCNT 6 /* Number of keepalives before death */
+#define LX_TCP_SYNCNT 7 /* Number of SYN retransmits */
+#define LX_TCP_LINGER2 8 /* Life time of orphaned FIN-WAIT-2 state */
+#define LX_TCP_DEFER_ACCEPT 9 /* Wake up listener only when data arrive */
+#define LX_TCP_WINDOW_CLAMP 10 /* Bound advertised window */
+#define LX_TCP_INFO 11 /* Information about this connection. */
+#define LX_TCP_QUICKACK 12 /* Bock/reenable quick ACKs. */
+#define LX_TCP_CONGESTION 13 /* Congestion control algorithm */
+#define LX_TCP_MD5SIG 14 /* TCP MD5 Signature (RFC2385) */
+#define LX_TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts on thin streams */
+#define LX_TCP_THIN_DUPACK 17 /* Fast retrans. after 1 dupack */
+#define LX_TCP_USER_TIMEOUT 18 /* How long for loss retry before timeout */
+#define LX_TCP_REPAIR 19 /* TCP socket under repair */
+#define LX_TCP_REPAIR_QUEUE 20
+#define LX_TCP_QUEUE_SEQ 21
+#define LX_TCP_REPAIR_OPTIONS 22
+#define LX_TCP_FASTOPEN 23 /* Enable FastOpen on listeners */
+#define LX_TCP_TIMESTAMP 24
+#define LX_TCP_NOTSENT_LOWAT 25 /* limit number of unsent bytes */
+
+/*
+ * Options for use with [gs]etsockopt at the IGMP level.
+ * IPPROTO_IGMP
+ */
+#define LX_IGMP_MINLEN 8
+#define LX_IGMP_MAX_HOST_REPORT_DELAY 10
+#define LX_IGMP_HOST_MEMBERSHIP_QUERY 0x11
+#define LX_IGMP_HOST_MEMBERSHIP_REPORT 0x12
+#define LX_IGMP_DVMRP 0x13
+#define LX_IGMP_PIM 0x14
+#define LX_IGMP_TRACE 0x15
+#define LX_IGMP_HOST_NEW_MEMBERSHIP_REPORT 0x16
+#define LX_IGMP_HOST_LEAVE_MESSAGE 0x17
+#define LX_IGMP_MTRACE_RESP 0x1e
+#define LX_IGMP_MTRACE 0x1f
+
+/*
+ * Linux socket flags for use with recv(2)/send(2)/recvmsg(2)/sendmsg(2)
+ */
+#define LX_MSG_OOB 0x1
+#define LX_MSG_PEEK 0x2
+#define LX_MSG_DONTROUTE 0x4
+#define LX_MSG_CTRUNC 0x8
+#define LX_MSG_PROXY 0x10
+#define LX_MSG_TRUNC 0x20
+#define LX_MSG_DONTWAIT 0x40
+#define LX_MSG_EOR 0x80
+#define LX_MSG_WAITALL 0x100
+#define LX_MSG_FIN 0x200
+#define LX_MSG_SYN 0x400
+#define LX_MSG_CONFIRM 0x800
+#define LX_MSG_RST 0x1000
+#define LX_MSG_ERRQUEUE 0x2000
+#define LX_MSG_NOSIGNAL 0x4000
+#define LX_MSG_MORE 0x8000
+#define LX_MSG_WAITFORONE 0x10000
+#define LX_MSG_FASTOPEN 0x20000000
+#define LX_MSG_CMSG_CLOEXEC 0x40000000
+
+typedef struct lx_msghdr {
+ void *msg_name; /* optional address */
+ socklen_t msg_namelen; /* size of address */
+ struct iovec *msg_iov; /* scatter/gather array */
+ size_t msg_iovlen; /* # elements in msg_iov */
+ void *msg_control; /* ancillary data */
+ size_t msg_controllen; /* ancillary data buffer len */
+ int msg_flags; /* flags on received message */
+} lx_msghdr_t;
+
+typedef struct lx_mmsghdr {
+ lx_msghdr_t msg_hdr; /* message header */
+ unsigned int msg_len; /* no. of bytes transmitted */
+} lx_mmsghdr_t;
+
+#if defined(_LP64)
+
+typedef struct lx_msghdr32 {
+ caddr32_t msg_name; /* optional address */
+ uint32_t msg_namelen; /* size of address */
+ caddr32_t msg_iov; /* scatter/gather array */
+ int32_t msg_iovlen; /* # elements in msg_iov */
+ caddr32_t msg_control; /* ancillary data */
+ uint32_t msg_controllen; /* ancillary data buffer len */
+ int32_t msg_flags; /* flags on received message */
+} lx_msghdr32_t;
+
+typedef struct lx_mmsghdr32 {
+ lx_msghdr32_t msg_hdr; /* message header */
+ unsigned int msg_len; /* no. of bytes transmitted */
+} lx_mmsghdr32_t;
+
+#endif
+
+typedef struct lx_sockaddr_in6 {
+ sa_family_t sin6_family;
+ in_port_t sin6_port;
+ uint32_t sin6_flowinfo;
+ struct in6_addr sin6_addr;
+ uint32_t sin6_scope_id; /* Depends on scope of sin6_addr */
+ /* one 32-bit field shorter than illumos */
+} lx_sockaddr_in6_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_SOCKET_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
new file mode 100644
index 0000000000..78fbf6e0a8
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
@@ -0,0 +1,341 @@
+/*
+ * 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.
+ * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#ifndef _SYS_LINUX_SYSCALLS_H
+#define _SYS_LINUX_SYSCALLS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+
+extern long lx_accept();
+extern long lx_accept4();
+extern long lx_access();
+extern long lx_acct();
+extern long lx_alarm();
+extern long lx_arch_prctl();
+extern long lx_bind();
+extern long lx_brk();
+extern long lx_chdir();
+extern long lx_chmod();
+extern long lx_chown();
+extern long lx_chown16();
+extern long lx_chroot();
+extern long lx_clock_getres();
+extern long lx_clock_gettime();
+extern long lx_clock_settime();
+extern long lx_close();
+extern long lx_connect();
+extern long lx_creat();
+extern long lx_dup();
+extern long lx_dup2();
+extern long lx_dup3();
+extern long lx_epoll_create();
+extern long lx_epoll_create1();
+extern long lx_epoll_ctl();
+extern long lx_epoll_pwait();
+extern long lx_epoll_wait();
+extern long lx_eventfd();
+extern long lx_eventfd2();
+extern long lx_faccessat();
+extern long lx_fadvise64();
+extern long lx_fadvise64_32();
+extern long lx_fadvise64_64();
+extern long lx_fallocate();
+extern long lx_fallocate32();
+extern long lx_fchdir();
+extern long lx_fchmod();
+extern long lx_fchmodat();
+extern long lx_fchown();
+extern long lx_fchown16();
+extern long lx_fchownat();
+extern long lx_fcntl();
+extern long lx_fcntl64();
+extern long lx_fgetxattr();
+extern long lx_flistxattr();
+extern long lx_flock();
+extern long lx_fremovexattr();
+extern long lx_fsetxattr();
+extern long lx_fstat32();
+extern long lx_fstat64();
+extern long lx_fstatat64();
+extern long lx_futex();
+extern long lx_get_robust_list();
+extern long lx_get_thread_area();
+extern long lx_getcpu();
+extern long lx_getcwd();
+extern long lx_getdents_32();
+extern long lx_getdents_64();
+extern long lx_getdents64();
+extern long lx_getegid();
+extern long lx_getegid16();
+extern long lx_geteuid();
+extern long lx_geteuid16();
+extern long lx_getgid();
+extern long lx_getgid16();
+extern long lx_getitimer();
+extern long lx_getpeername();
+extern long lx_getpgid();
+extern long lx_getpgrp();
+extern long lx_getsockname();
+extern long lx_getpid();
+extern long lx_getppid();
+extern long lx_getpriority();
+extern long lx_getrandom();
+extern long lx_getresgid();
+extern long lx_getresgid16();
+extern long lx_getresuid();
+extern long lx_getresuid16();
+extern long lx_getrlimit();
+extern long lx_getrusage();
+extern long lx_getsid();
+extern long lx_getsockopt();
+extern long lx_gettid();
+extern long lx_gettimeofday();
+extern long lx_getuid();
+extern long lx_getuid16();
+extern long lx_getxattr();
+extern long lx_io_cancel();
+extern long lx_io_destroy();
+extern long lx_io_getevents();
+extern long lx_io_setup();
+extern long lx_io_submit();
+extern long lx_ioctl();
+extern long lx_ioprio_get();
+extern long lx_ioprio_set();
+extern long lx_kill();
+extern long lx_lchown();
+extern long lx_lchown16();
+extern long lx_lgetxattr();
+extern long lx_link();
+extern long lx_linkat();
+extern long lx_listen();
+extern long lx_llistxattr();
+extern long lx_llseek();
+extern long lx_lremovexattr();
+extern long lx_lseek32();
+extern long lx_lseek64();
+extern long lx_lsetxattr();
+extern long lx_lstat32();
+extern long lx_lstat64();
+extern long lx_listxattr();
+extern long lx_madvise();
+extern long lx_mincore();
+extern long lx_mkdir();
+extern long lx_mkdirat();
+extern long lx_mlock();
+extern long lx_mlockall();
+extern long lx_mmap();
+extern long lx_mmap2();
+extern long lx_mremap();
+extern long lx_mprotect();
+extern long lx_modify_ldt();
+extern long lx_mount();
+extern long lx_msync();
+extern long lx_munlock();
+extern long lx_munlockall();
+extern long lx_munmap();
+extern long lx_nanosleep();
+extern long lx_nice();
+extern long lx_oldgetrlimit();
+extern long lx_open();
+extern long lx_openat();
+extern long lx_pause();
+extern long lx_personality();
+extern long lx_pipe();
+extern long lx_pipe2();
+extern long lx_poll();
+extern long lx_ppoll();
+extern long lx_pread();
+extern long lx_pread32();
+extern long lx_preadv();
+extern long lx_preadv32();
+extern long lx_prctl();
+extern long lx_prlimit64();
+extern long lx_pselect();
+extern long lx_ptrace();
+extern long lx_pwrite();
+extern long lx_pwrite32();
+extern long lx_pwritev();
+extern long lx_pwritev32();
+extern long lx_read();
+extern long lx_readlink();
+extern long lx_readlinkat();
+extern long lx_readv();
+extern long lx_reboot();
+extern long lx_recv();
+extern long lx_recvmsg();
+extern long lx_recvmmsg();
+extern long lx_recvfrom();
+extern long lx_rename();
+extern long lx_renameat();
+extern long lx_sched_getaffinity();
+extern long lx_sched_getparam();
+extern long lx_sched_getscheduler();
+extern long lx_sched_getattr();
+extern long lx_sched_get_priority_max();
+extern long lx_sched_get_priority_min();
+extern long lx_sched_rr_get_interval();
+extern long lx_sched_setaffinity();
+extern long lx_sched_setattr();
+extern long lx_sched_setparam();
+extern long lx_sched_setscheduler();
+extern long lx_sched_yield();
+extern long lx_select();
+extern long lx_send();
+extern long lx_sendmsg();
+extern long lx_sendmmsg();
+extern long lx_sendto();
+extern long lx_set_robust_list();
+extern long lx_set_thread_area();
+extern long lx_set_tid_address();
+extern long lx_setdomainname();
+extern long lx_setfsuid();
+extern long lx_setfsuid16();
+extern long lx_setfsgid();
+extern long lx_setfsgid16();
+extern long lx_setgid();
+extern long lx_setgid16();
+extern long lx_sethostname();
+extern long lx_setpgid();
+extern long lx_setpriority();
+extern long lx_setregid();
+extern long lx_setregid16();
+extern long lx_setresgid();
+extern long lx_setresgid16();
+extern long lx_setresuid();
+extern long lx_setresuid16();
+extern long lx_setreuid();
+extern long lx_setreuid16();
+extern long lx_setrlimit();
+extern long lx_setsid();
+extern long lx_setuid();
+extern long lx_setuid16();
+extern long lx_setxattr();
+extern long lx_setsockopt();
+extern long lx_symlink();
+extern long lx_symlinkat();
+extern long lx_shutdown();
+extern long lx_socket();
+extern long lx_socketcall();
+extern long lx_socketpair();
+extern long lx_splice();
+extern long lx_stat32();
+extern long lx_stat64();
+extern long lx_stime();
+extern long lx_swapoff();
+extern long lx_swapon();
+extern long lx_sync();
+extern long lx_sync_file_range();
+extern long lx_syncfs();
+extern long lx_sysinfo32();
+extern long lx_sysinfo64();
+extern long lx_syslog();
+extern long lx_removexattr();
+extern long lx_tgkill();
+extern long lx_time();
+extern long lx_times();
+extern long lx_timer_create();
+extern long lx_tkill();
+extern long lx_umask();
+extern long lx_umount();
+extern long lx_umount2();
+extern long lx_uname();
+extern long lx_unlink();
+extern long lx_unlinkat();
+extern long lx_unshare();
+extern long lx_vhangup();
+extern long lx_wait4();
+extern long lx_waitid();
+extern long lx_waitpid();
+extern long lx_write();
+extern long lx_writev();
+
+#if defined(_LP64)
+/*
+ * Linux vsyscall addresses:
+ */
+#define LX_VSYS_gettimeofday (uintptr_t)0xffffffffff600000
+#define LX_VSYS_time (uintptr_t)0xffffffffff600400
+#define LX_VSYS_getcpu (uintptr_t)0xffffffffff600800
+
+#define LX_VSYSCALL_ADDR (uintptr_t)0xffffffffff600000
+#define LX_VSYSCALL_SIZE (uintptr_t)0x1000
+#endif
+
+#endif /* _KERNEL */
+
+/*
+ * System call numbers for revectoring:
+ */
+
+#if defined(__amd64)
+#define LX_SYS_close 3
+#define LX_SYS_gettimeofday 96
+#define LX_SYS_mount 165
+#define LX_SYS_time 201
+#define LX_SYS_io_setup 206
+#define LX_SYS_clock_gettime 228
+#define LX_SYS_getcpu 309
+
+#define LX_SYS32_close 6
+#define LX_SYS32_gettimeofday 78
+#define LX_SYS32_time 13
+#define LX_SYS32_mount 21
+#define LX_SYS32_clock_gettime 265
+#define LX_SYS32_io_setup 245
+#define LX_SYS32_getcpu 318
+#elif defined(__i386)
+#define LX_SYS_close 6
+#define LX_SYS_mount 21
+#define LX_SYS_gettimeofday 78
+#define LX_SYS_time 13
+#define LX_SYS_clock_gettime 265
+#define LX_SYS_io_setup 245
+#define LX_SYS_getcpu 318
+#else
+#error "Architecture not supported"
+#endif /* defined(__amd64) */
+
+/*
+ * The current code in the VDSO operates under the expectation that it will be
+ * mapped at a fixed offset from the comm page. This simplifies the act of
+ * locating said page without any other reference. The VDSO must fit within
+ * this offset, matching the same value as COMM_PAGE_ALIGN.
+ * See: uts/i86pc/sys/comm_page.h
+ */
+#define LX_VDSO_SIZE 0x4000
+#define LX_VDSO_ADDR_MASK ~(LX_VDSO_SIZE - 1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LINUX_SYSCALLS_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_types.h b/usr/src/uts/common/brand/lx/sys/lx_types.h
new file mode 100644
index 0000000000..90363c8939
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_types.h
@@ -0,0 +1,144 @@
+/*
+ * 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.
+ * Copyright 2016 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_LX_TYPES_H
+#define _SYS_LX_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _KERNEL
+
+#define SHRT_MIN (-32768) /* min value of a "short int" */
+#define SHRT_MAX 32767 /* max value of a "short int" */
+#define USHRT_MAX 65535 /* max of "unsigned short int" */
+#define INT_MIN (-2147483647-1) /* min value of an "int" */
+#define INT_MAX 2147483647 /* max value of an "int" */
+#define UINT_MAX 4294967295U /* max value of an "unsigned int" */
+
+#ifndef LLONG_MAX
+#define LLONG_MAX 9223372036854775807LL
+#endif
+
+#if defined(_LP64)
+#define LONG_MAX 9223372036854775807L
+#define ULONG_MAX 18446744073709551615UL
+#else
+#define LONG_MAX 2147483647L /* max value of a 32-bit "long int" */
+#define ULONG_MAX 4294967295UL /* max value of a 32-bit "ulong int" */
+#endif
+
+#endif /* !_KERNEL */
+
+
+typedef uint64_t lx_dev_t;
+typedef uint16_t lx_dev16_t;
+typedef uint32_t lx_ino_t;
+typedef uint64_t lx_ino64_t;
+typedef uint32_t lx_uid_t;
+typedef uint16_t lx_uid16_t;
+typedef uint32_t lx_gid_t;
+typedef uint16_t lx_gid16_t;
+typedef uint32_t lx_off_t;
+typedef uint64_t lx_off64_t;
+typedef uint32_t lx_blksize_t;
+typedef uint32_t lx_blkcnt_t;
+typedef uint64_t lx_blkcnt64_t;
+typedef uint32_t lx_mode_t;
+typedef uint16_t lx_mode16_t;
+
+/*
+ * Linux mangles major/minor numbers into dev_t differently than SunOS.
+ */
+#ifdef _LP64
+#define LX_MAKEDEVICE(maj, min) \
+ (((min) & 0xff) | (((maj) & 0xfff) << 8) | \
+ ((uint64_t)((min) & ~0xff) << 12) | ((uint64_t)((maj) & ~0xfff) << 32))
+
+#define LX_GETMAJOR(lx_dev) ((((lx_dev) >> 8) & 0xfff) | \
+ ((((uint64_t)(lx_dev)) >> 32) & ~0xfff))
+
+#else
+#define LX_MAKEDEVICE(maj, min) \
+ (((min) & 0xff) | (((maj) & 0xfff) << 8) | (((min) & ~0xff) << 12))
+
+#define LX_GETMAJOR(lx_dev) (((lx_dev) >> 8) & 0xfff)
+#endif
+
+#define LX_GETMINOR(lx_dev) (((lx_dev) & 0xff) | (((lx_dev) >> 12) & ~0xff))
+/* Linux supports 20 bits for the minor, and 12 bits for the major number */
+#define LX_MAXMIN 0xfffff
+#define LX_MAXMAJ 0xfff
+
+/*
+ * Certain Linux tools care deeply about major/minor number mapping.
+ * Map virtual disks (zfs datasets, zvols, etc) into a safe reserved range.
+ */
+#define LX_MAJOR_DISK 203
+
+/* LX ptm driver major/minor number */
+#define LX_PTM_MAJOR 5
+#define LX_PTM_MINOR 2
+
+/* LX pts driver major number range */
+#define LX_PTS_MAJOR_MIN 136
+#define LX_PTS_MAJOR_MAX 143
+
+/* LX tty/cons driver major number */
+#define LX_TTY_MAJOR 5
+
+#define LX_UID16_TO_UID32(uid16) \
+ (((uid16) == (lx_uid16_t)-1) ? ((lx_uid_t)-1) : (lx_uid_t)(uid16))
+
+#define LX_GID16_TO_GID32(gid16) \
+ (((gid16) == (lx_gid16_t)-1) ? ((lx_gid_t)-1) : (lx_gid_t)(gid16))
+
+/* Overflow values default to NFS nobody. */
+
+#define UID16_OVERFLOW ((lx_uid16_t)65534)
+#define GID16_OVERFLOW ((lx_gid16_t)65534)
+
+/*
+ * All IDs with high word non-zero are converted to default overflow values to
+ * avoid inadvertent truncation to zero (root) (!).
+ */
+#define LX_UID32_TO_UID16(uid32) \
+ ((((uid32) & 0xffff0000) == 0) ? ((lx_uid16_t)(uid32)) : \
+ (((uid32) == ((lx_uid_t)-1)) ? ((lx_uid16_t)-1) : UID16_OVERFLOW))
+
+#define LX_GID32_TO_GID16(gid32) \
+ ((((gid32) & 0xffff0000) == 0) ? ((lx_gid16_t)(gid32)) : \
+ (((gid32) == ((lx_gid_t)-1)) ? ((lx_gid16_t)-1) : GID16_OVERFLOW))
+
+#define LX_32TO64(lo, hi) \
+ ((uint64_t)((uint64_t)(lo) | ((uint64_t)(hi) << 32)))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_LX_TYPES_H */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_userhz.h b/usr/src/uts/common/brand/lx/sys/lx_userhz.h
new file mode 100644
index 0000000000..ebbda28698
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sys/lx_userhz.h
@@ -0,0 +1,64 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+#ifndef _LX_USERHZ_H
+#define _LX_USERHZ_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Within the kernel, Linux implements an internal hz that they refer to as a
+ * "jiffy". Linux can be built with different hz, but on modern kernels
+ * it is frequently 250. However, Linux has a separate concept for the hz
+ * that is visible outside the kernel. This is called "USER_HZ" and is the
+ * value returned by 'sysconf(_SC_CLK_TCK)'. This is almost universally set to
+ * 100hz. Some (lazy) applications just hardcode 100hz instead of checking.
+ * To accommodate these broken applications, we always work with a USER_HZ of
+ * 100 and scale accordingly. See the Linux time(7) man page for a more
+ * detailed discussion of their behavior. See the comment in our
+ * uts/common/conf/param.c for a discussion of valid native hz values.
+ *
+ * There are a few interfaces which expose a clock_t to user-land and which
+ * need to be considered for USER_HZ adjustment.
+ * 1) The times(2) syscall. This is handled correctly.
+ * 2) The waitid(2) syscall passes a siginfo_t which contains si_stime and
+ * si_utime. Testing waitid(2) on various Linux distributions shows that the
+ * these fields are garbage. This aligns with the Linux waitid(2) man page,
+ * which describes the subset of the siginfo_t structure that is populated.
+ * Neither si_stime or si_utime are listed.
+ * 3) A sigaction(2) handler can pass a siginfo_t. This is only documented to
+ * occur when the sa_flags is SA_SIGINFO. The si_stime and si_utime are
+ * documented to only be populated when the signal is SIGCHLD. However,
+ * testing on Linux seems to show that these fields are not consistent
+ * with the corresponding times(2) data for the process, even for the
+ * SIGCHLD sigaction handler case.
+ * 4) Some fields in /proc/stat and /proc/pid/stat. See the Linux proc man
+ * page for references to sysconf(_SC_CLK_TCK).
+ *
+ * Although the siginfo_t si_stime and si_utime data for cases #2 and #3 is not
+ * consistent on Linux, we populate these fields correctly to be on the safe
+ * side.
+ */
+extern uint_t lx_hz_scale;
+#define LX_USERHZ 100
+#define HZ_TO_LX_USERHZ(x) ((x) / lx_hz_scale)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_USERHZ_H */
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_access.c b/usr/src/uts/common/brand/lx/syscall/lx_access.c
new file mode 100644
index 0000000000..8cf836cd7a
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_access.c
@@ -0,0 +1,223 @@
+/*
+ * 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) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved
+ *
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ *
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/isa_defs.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/cred_impl.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/pathname.h>
+#include <sys/vnode.h>
+#include <sys/uio.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/file.h>
+#include <fs/fs_subr.h>
+#include <c2/audit.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+
+/*
+ * Determine accessibility of file.
+ */
+
+#define E_OK 010 /* use effective ids */
+#define R_OK 004
+#define W_OK 002
+#define X_OK 001
+
+/*
+ * Convert Linux LX_AT_* flags to SunOS AT_* flags but skip verifying allowed
+ * flags have been passed. This also allows EACCESS/REMOVEDIR to be translated
+ * correctly since on linux they have the same value.
+ *
+ * Some code can actually pass in other bits in the flag. We may have to simply
+ * ignore these, as indicated by the enforce parameter.
+ */
+int
+ltos_at_flag(int lflag, int allow, boolean_t enforce)
+{
+ int sflag = 0;
+
+ if ((lflag & LX_AT_EACCESS) && (allow & AT_EACCESS)) {
+ lflag &= ~LX_AT_EACCESS;
+ sflag |= AT_EACCESS;
+ }
+
+ if ((lflag & LX_AT_REMOVEDIR) && (allow & AT_REMOVEDIR)) {
+ lflag &= ~LX_AT_REMOVEDIR;
+ sflag |= AT_REMOVEDIR;
+ }
+
+ if ((lflag & LX_AT_SYMLINK_NOFOLLOW) && (allow & AT_SYMLINK_NOFOLLOW)) {
+ lflag &= ~LX_AT_SYMLINK_NOFOLLOW;
+ sflag |= AT_SYMLINK_NOFOLLOW;
+ }
+
+ /* right now SunOS doesn't have a _FOLLOW flag, so use a fake one */
+ if ((lflag & LX_AT_SYMLINK_FOLLOW) && (allow & LX_AT_SYMLINK_FOLLOW)) {
+ lflag &= ~LX_AT_SYMLINK_FOLLOW;
+ sflag |= LX_AT_SYMLINK_FOLLOW;
+ }
+
+ /* If lflag is not zero than some flags did not hit the above code. */
+ if (enforce && lflag)
+ return (-1);
+
+ return (sflag);
+}
+
+/*
+ * For illumos, access() does this:
+ * If the process has appropriate privileges, an implementation may indicate
+ * success for X_OK even if none of the execute file permission bits are set.
+ *
+ * But for Linux, access() does this:
+ * If the calling process is privileged (i.e., its real UID is zero), then
+ * an X_OK check is successful for a regular file if execute permission is
+ * enabled for any of the file owner, group, or other.
+ *
+ * Linux used to behave more like illumos on older kernels:
+ * In kernel 2.4 (and earlier) there is some strangeness in the handling
+ * of X_OK tests for superuser. If all categories of execute permission
+ * are disabled for a nondirectory file, then the only access() test that
+ * returns -1 is when mode is specified as just X_OK; if R_OK or W_OK is
+ * also specified in mode, then access() returns 0 for such files.
+ *
+ * So we need to handle the case where a privileged process is checking for
+ * X_OK but none of the execute bits are set on the file. We'll keep the old
+ * 2.4 behavior for 2.4 emulation but use the new behavior for any other
+ * kernel rev.
+ */
+static int
+lx_common_access(char *fname, int fmode, vnode_t *startvp)
+{
+ vnode_t *vp;
+ cred_t *tmpcr;
+ int error;
+ int mode;
+ cred_t *cr;
+ int estale_retry = 0;
+
+ if (fmode & ~(E_OK|R_OK|W_OK|X_OK))
+ return (EINVAL);
+
+ mode = ((fmode & (R_OK|W_OK|X_OK)) << 6);
+
+ cr = CRED();
+
+ /* OK to use effective uid/gid, i.e., no need to crdup(CRED())? */
+ if ((fmode & E_OK) != 0 ||
+ (cr->cr_uid == cr->cr_ruid && cr->cr_gid == cr->cr_rgid)) {
+ tmpcr = cr;
+ crhold(tmpcr);
+ } else {
+ tmpcr = crdup(cr);
+ tmpcr->cr_uid = cr->cr_ruid;
+ tmpcr->cr_gid = cr->cr_rgid;
+ tmpcr->cr_ruid = cr->cr_uid;
+ tmpcr->cr_rgid = cr->cr_gid;
+ }
+
+lookup:
+ if ((error = lookupnameatcred(fname, UIO_USERSPACE, FOLLOW, NULLVPP,
+ &vp, startvp, tmpcr)) != 0) {
+ if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
+ goto lookup;
+ crfree(tmpcr);
+ return (error);
+ }
+
+ if (mode != 0) {
+ error = VOP_ACCESS(vp, mode, 0, tmpcr, NULL);
+ if (error != 0) {
+ if ((error == ESTALE) &&
+ fs_need_estale_retry(estale_retry++)) {
+ VN_RELE(vp);
+ goto lookup;
+ }
+
+ } else if ((fmode & X_OK) != 0 && cr->cr_ruid == 0 &&
+ lx_kern_release_cmp(curproc->p_zone, "2.4.0") > 0) {
+ /* check for incorrect execute success */
+ vattr_t va;
+
+ va.va_mask = AT_MODE;
+ if ((error = VOP_GETATTR(vp, &va, 0, cr, NULL)) == 0) {
+ mode_t m = VTTOIF(va.va_type) | va.va_mode;
+
+ if ((m & S_IFMT) == S_IFREG &&
+ !(m & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+ /* no execute bits set in the mode */
+ error = EACCES;
+ }
+ }
+ }
+ }
+
+ crfree(tmpcr);
+ VN_RELE(vp);
+ return (error);
+}
+
+int
+lx_faccessat(int atfd, char *fname, int fmode, int flag)
+{
+ vnode_t *startvp;
+ int error;
+
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ if ((flag = ltos_at_flag(flag, AT_EACCESS, B_FALSE)) < 0)
+ return (set_errno(EINVAL));
+
+ if (fname == NULL)
+ return (set_errno(EFAULT));
+ if ((error = fgetstartvp(atfd, fname, &startvp)) != 0)
+ return (set_errno(error));
+ if (AU_AUDITING() && startvp != NULL)
+ audit_setfsat_path(1);
+
+ /* Do not allow E_OK unless AT_EACCESS flag is set */
+ if ((flag & AT_EACCESS) == 0)
+ fmode &= ~E_OK;
+
+ error = lx_common_access(fname, fmode, startvp);
+ if (startvp != NULL)
+ VN_RELE(startvp);
+ if (error)
+ return (set_errno(error));
+ return (0);
+}
+
+int
+lx_access(char *fname, int fmode)
+{
+ return (lx_faccessat(LX_AT_FDCWD, fname, fmode, 0));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_aio.c b/usr/src/uts/common/brand/lx/syscall/lx_aio.c
new file mode 100644
index 0000000000..6748245db8
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_aio.c
@@ -0,0 +1,1345 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+/*
+ * Linux aio syscall support.
+ *
+ * The Linux story around the io_* syscalls is very confusing. The io_* syscalls
+ * are not exposed via glibc and in fact, glibc seems to implement its own aio
+ * without using the io_* syscalls at all. However, there is the libaio library
+ * which uses the io_* syscalls, although its implementation of the io_*
+ * functions (with the same names!) is different from the syscalls themselves,
+ * and it uses different definitions for some of the structures involved.
+ *
+ * These syscalls are documented to use an aio_context_t for the context
+ * parameter. On Linux this is a ulong_t. The contexts live in the kernel
+ * address space and are looked up using the aio_context_t parameter. However,
+ * the Linux libaio library, which is a consumer of the io_* syscalls, abuses
+ * the context by assuming it can be used as a pointer into memory that is
+ * mapped into the process. To accomodate this abomination we map a page of
+ * anonymous memory and expose the context to user-land as a pointer offset
+ * into that page. The page itself is never used by our code and our internal
+ * context ID is simply an integer we calculate based on the page pointer
+ * offset.
+ *
+ * Most applications never use aio, so we don't want an implementation that
+ * adds overhead to every process, but on the other hand, when an application is
+ * using aio, it is for performance reasons and we want to be as efficient as
+ * possible. In particular, we don't want to dynamically allocate resources
+ * in the paths that enqueue I/O. Instead, we pre-allocate the resources
+ * we may need when the application performs the io_setup call and keep the
+ * io_submit and io_getevents calls streamlined.
+ *
+ * The general approach here is inspired by the native aio support provided by
+ * libc in user-land. We have worker threads that pick up pending work from
+ * the context "lxioctx_pending" list and synchronously issue the operation in
+ * the control block. When the operation completes, the thread places the
+ * control block into the context "lxioctx_done" list for later consumption by
+ * io_getevents. The thread will then attempt to service another pending
+ * operation or wait for more work to arrive.
+ *
+ * The control blocks on the pending or done lists are referenced by an
+ * lx_io_elem_t struct. This simply holds a pointer to the user-land control
+ * block and the result of the operation. These elements are pre-allocated at
+ * io_setup time and stored on the context "lxioctx_free" list.
+ *
+ * io_submit pulls elements off of the free list, places them on the pending
+ * list and kicks a worker thread to run. io_getevents pulls elements off of
+ * the done list, sets up an event to return, and places the elements back
+ * onto the free list.
+ *
+ * The worker threads are pre-allocated at io_setup time. These are LWP's
+ * that are part of the process, but never leave the kernel. The number of
+ * LWP's is allocated based on the nr_events argument to io_setup. Because
+ * this argument can theoretically be large (up to LX_AIO_MAX_NR), we want to
+ * pre-allocate enough threads to get good I/O concurrency, but not overdo it.
+ * For a small nr_events (<= lx_aio_base_workers) we pre-allocate as many
+ * threads as nr_events so that all of the the I/O can run in parallel. Once
+ * we exceed lx_aio_base_workers, we scale up the number of threads by 2, until
+ * we hit the maximum at lx_aio_max_workers. See the code in io_setup for more
+ * information.
+ *
+ * Because the worker threads never leave the kernel, they are marked with the
+ * TP_KTHREAD bit so that /proc operations essentially ignore them. We also tag
+ * the brand lwp flags with the BR_AIO_LWP bit so that these threads never
+ * appear in the lx /proc. Aside from servicing aio submissions, the worker
+ * threads don't participate in most application-initiated operations. Forking
+ * is a special case for the workers. The Linux fork(2) and vfork(2) behavior
+ * always forks only a single thread; the caller. However, during cfork() the
+ * system attempts to quiesce all threads by calling holdlwps(). The workers
+ * check for SHOLDFORK and SHOLDFORK1 in their loops and suspend themselves ala
+ * holdlwp() if the process forks.
+ *
+ * It is hard to make any generalized statements about how the aio syscalls
+ * are used in production. MySQL is one of the more popular consumers of aio
+ * and in the default configuration it will create 10 contexts with a capacity
+ * of 256 I/Os (io_setup nr_events) and 1 context with a capacity of 100 I/Os.
+ * Another application we've seen will create 8 contexts, each with a capacity
+ * of 128 I/Os. In practice 1-7 was the typical number of in-flight I/Os.
+ *
+ * The default configuration for MySQL uses 4 read and 4 write threads. Each
+ * thread has an associated context. MySQL also allocates 3 additional contexts,
+ * so in the default configuration it will only use 11, but the number of
+ * read and write threads can be tuned up to a maximum of 64. We can expand
+ * a process's number of contexts up to a maximum of LX_IOCTX_CNT_MAX, which
+ * is significantly more than we've ever seen in use.
+ *
+ * According to www.kernel.org/doc/Documentation/sysctl/fs.txt, the
+ * /proc/sys/fs entries for aio are:
+ * - aio-nr: The total of all nr_events values specified on the io_setup
+ * call for every active context.
+ * - aio-max-nr: The upper limit for aio-nr
+ * aio-nr is tracked as a zone-wide value. We keep aio-max-nr limited to
+ * LX_AIO_MAX_NR, which matches Linux and provides plenty of headroom for the
+ * zone.
+ */
+
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+#include <sys/brand.h>
+#include <sys/sysmacros.h>
+#include <sys/sdt.h>
+#include <sys/procfs.h>
+#include <sys/eventfd.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_misc.h>
+#include <lx_errno.h>
+
+/* These constants match Linux */
+#define LX_IOCB_FLAG_RESFD 0x0001
+#define LX_IOCB_CMD_PREAD 0
+#define LX_IOCB_CMD_PWRITE 1
+#define LX_IOCB_CMD_FSYNC 2
+#define LX_IOCB_CMD_FDSYNC 3
+#define LX_IOCB_CMD_PREADX 4
+#define LX_IOCB_CMD_POLL 5
+#define LX_IOCB_CMD_NOOP 6
+#define LX_IOCB_CMD_PREADV 7
+#define LX_IOCB_CMD_PWRITEV 8
+
+#define LX_KIOCB_KEY 0
+
+/*
+ * Base and max. number of contexts/process. Note that we currently map one
+ * page to manage the user-level context ID, so that code must be adjusted if
+ * LX_IOCTX_CNT_MAX is ever enlarged. Currently, this is the limit for the
+ * number of 64-bit pointers in one 4k page.
+ */
+#define LX_IOCTX_CNT_BASE 16
+#define LX_IOCTX_CNT_MAX 512
+
+/*
+ * Max number of control block pointers, or lx_io_event_t's, to allocate on the
+ * stack in io_submit or io_getevents.
+ */
+#define MAX_ALLOC_ON_STACK 128
+#define alloca(x) __builtin_alloca(x)
+extern void *__builtin_alloca(size_t);
+
+/* The context is an offset within the ctxpage we mapped */
+#define CTXID_TO_PTR(L, I) ((L)->l_io_ctxpage + ((I) * sizeof (uintptr_t)))
+#define PTR_TO_CTXID(L, P) ((int)((uintptr_t)(P) - (L)->l_io_ctxpage) / \
+ sizeof (uintptr_t))
+
+typedef ulong_t lx_aio_context_t;
+
+uint_t lx_aio_base_workers = 16; /* num threads/context before scaling */
+uint_t lx_aio_max_workers = 32; /* upper limit on threads/context */
+
+/*
+ * Internal representation of an aio context.
+ */
+typedef struct lx_io_ctx {
+ boolean_t lxioctx_shutdown; /* context is being destroyed */
+ uint_t lxioctx_maxn; /* nr_events from io_setup */
+ uint_t lxioctx_in_use; /* reference counter */
+ kmutex_t lxioctx_f_lock; /* free list lock */
+ uint_t lxioctx_free_cnt; /* num. elements in free list */
+ list_t lxioctx_free; /* free list */
+ kmutex_t lxioctx_p_lock; /* pending list lock */
+ kcondvar_t lxioctx_pending_cv; /* pending list cv */
+ list_t lxioctx_pending; /* pending list */
+ kmutex_t lxioctx_d_lock; /* done list lock */
+ kcondvar_t lxioctx_done_cv; /* done list cv */
+ uint_t lxioctx_done_cnt; /* num. elements in done list */
+ list_t lxioctx_done; /* done list */
+} lx_io_ctx_t;
+
+/*
+ * Linux binary definition of an I/O event.
+ */
+typedef struct lx_io_event {
+ uint64_t lxioe_data; /* data payload */
+ uint64_t lxioe_object; /* object of origin */
+ int64_t lxioe_res; /* result code */
+ int64_t lxioe_res2; /* "secondary" result (WTF?) */
+} lx_io_event_t;
+
+/*
+ * Linux binary definition of an I/O control block.
+ */
+typedef struct lx_iocb {
+ uint64_t lxiocb_data; /* data payload */
+ uint32_t lxiocb_key; /* must be LX_KIOCB_KEY (!) */
+ uint32_t lxiocb_reserved1;
+ uint16_t lxiocb_op; /* operation */
+ int16_t lxiocb_reqprio; /* request priority */
+ uint32_t lxiocb_fd; /* file descriptor */
+ uint64_t lxiocb_buf; /* data buffer */
+ uint64_t lxiocb_nbytes; /* number of bytes */
+ int64_t lxiocb_offset; /* offset in file */
+ uint64_t lxiocb_reserved2;
+ uint32_t lxiocb_flags; /* LX_IOCB_FLAG_* flags */
+ uint32_t lxiocb_resfd; /* eventfd fd, if any */
+} lx_iocb_t;
+
+typedef struct lx_io_elem {
+ list_node_t lxioelem_link;
+ uint16_t lxioelem_op; /* operation */
+ uint16_t lxioelem_flags; /* bits from lxiocb_flags */
+ int lxioelem_fd; /* file descriptor */
+ file_t *lxioelem_fp; /* getf() file pointer */
+ int lxioelem_resfd; /* RESFD file descriptor */
+ file_t *lxioelem_resfp; /* RESFD getf() file pointer */
+ void *lxioelem_buf; /* data buffer */
+ uint64_t lxioelem_nbytes; /* number of bytes */
+ int64_t lxioelem_offset; /* offset in file */
+ uint64_t lxioelem_data;
+ ssize_t lxioelem_res;
+ void *lxioelem_cbp; /* ptr to iocb in userspace */
+} lx_io_elem_t;
+
+/* From lx_rw.c */
+extern ssize_t lx_pread_fp(file_t *, void *, size_t, off64_t);
+extern ssize_t lx_pwrite_fp(file_t *, void *, size_t, off64_t);
+
+/* From common/syscall/rw.c */
+extern int fdsync(int, int);
+/* From common/os/grow.c */
+extern caddr_t smmap64(caddr_t, size_t, int, int, int, off_t);
+
+/*
+ * Given an aio_context ID, return our internal context pointer with an
+ * additional ref. count, or NULL if cp not found.
+ */
+static lx_io_ctx_t *
+lx_io_cp_hold(lx_aio_context_t cid)
+{
+ int id;
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ lx_io_ctx_t *cp;
+
+ mutex_enter(&lxpd->l_io_ctx_lock);
+
+ if (lxpd->l_io_ctxs == NULL) {
+ ASSERT(lxpd->l_io_ctx_cnt == 0);
+ ASSERT(lxpd->l_io_ctxpage == (uintptr_t)NULL);
+ goto bad;
+ }
+
+ id = PTR_TO_CTXID(lxpd, cid);
+ if (id < 0 || id >= lxpd->l_io_ctx_cnt)
+ goto bad;
+
+ if ((cp = lxpd->l_io_ctxs[id]) == NULL)
+ goto bad;
+
+ if (cp->lxioctx_shutdown)
+ goto bad;
+
+ atomic_inc_32(&cp->lxioctx_in_use);
+ mutex_exit(&lxpd->l_io_ctx_lock);
+ return (cp);
+
+bad:
+ mutex_exit(&lxpd->l_io_ctx_lock);
+ return (NULL);
+}
+
+/*
+ * Release a hold on the context and clean up the context if it was the last
+ * hold.
+ */
+static void
+lx_io_cp_rele(lx_io_ctx_t *cp)
+{
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ lx_zone_data_t *lxzd;
+ int i;
+ lx_io_elem_t *ep;
+
+ mutex_enter(&lxpd->l_io_ctx_lock);
+ ASSERT(cp->lxioctx_in_use >= 1);
+ if (cp->lxioctx_in_use > 1) {
+ atomic_dec_32(&cp->lxioctx_in_use);
+ /* wake all threads waiting on context rele */
+ cv_broadcast(&lxpd->l_io_destroy_cv);
+ mutex_exit(&lxpd->l_io_ctx_lock);
+ return;
+ }
+
+ /*
+ * We hold the last ref.
+ */
+ for (i = 0; i < lxpd->l_io_ctx_cnt; i++) {
+ if (lxpd->l_io_ctxs[i] == cp) {
+ lxpd->l_io_ctxs[i] = NULL;
+ break;
+ }
+ }
+ ASSERT(i < lxpd->l_io_ctx_cnt);
+ /* wake all threads waiting on context destruction */
+ cv_broadcast(&lxpd->l_io_destroy_cv);
+ ASSERT(cp->lxioctx_shutdown == B_TRUE);
+
+ mutex_exit(&lxpd->l_io_ctx_lock);
+
+ /* can now decrement the zone's overall aio counter */
+ lxzd = ztolxzd(curproc->p_zone);
+ mutex_enter(&lxzd->lxzd_lock);
+ VERIFY(cp->lxioctx_maxn <= lxzd->lxzd_aio_nr);
+ lxzd->lxzd_aio_nr -= cp->lxioctx_maxn;
+ mutex_exit(&lxzd->lxzd_lock);
+
+ /*
+ * We have the only pointer to the context now. Free all
+ * elements from all three queues and the context itself.
+ */
+ while ((ep = list_remove_head(&cp->lxioctx_free)) != NULL) {
+ kmem_free(ep, sizeof (lx_io_elem_t));
+ }
+
+ /*
+ * During io_submit() we use getf() to get/validate the file pointer
+ * for the file descriptor in each control block. We do not releasef()
+ * the fd, but instead pass along the fd and file pointer to the worker
+ * threads. In order to manage this hand-off we use clear_active_fd()
+ * in the syscall path and then in our thread which takes over the file
+ * descriptor, we use a combination of set_active_fd() and releasef().
+ * Because our thread that is taking ownership of the fd has not called
+ * getf(), we first call set_active_fd(-1) to reserve a slot in the
+ * active fd array for ourselves.
+ */
+ set_active_fd(-1);
+ while ((ep = list_remove_head(&cp->lxioctx_pending)) != NULL) {
+ set_active_fd(ep->lxioelem_fd);
+ releasef(ep->lxioelem_fd);
+
+ if (ep->lxioelem_flags & LX_IOCB_FLAG_RESFD) {
+ set_active_fd(ep->lxioelem_resfd);
+ releasef(ep->lxioelem_resfd);
+ }
+
+ kmem_free(ep, sizeof (lx_io_elem_t));
+ }
+
+ while ((ep = list_remove_head(&cp->lxioctx_done)) != NULL) {
+ kmem_free(ep, sizeof (lx_io_elem_t));
+ }
+
+ ASSERT(list_is_empty(&cp->lxioctx_free));
+ list_destroy(&cp->lxioctx_free);
+ ASSERT(list_is_empty(&cp->lxioctx_pending));
+ list_destroy(&cp->lxioctx_pending);
+ ASSERT(list_is_empty(&cp->lxioctx_done));
+ list_destroy(&cp->lxioctx_done);
+
+ kmem_free(cp, sizeof (lx_io_ctx_t));
+}
+
+/*
+ * Called by a worker thread to perform the operation specified in the control
+ * block.
+ *
+ * Linux returns a negative errno in the event "lxioelem_res" field as the
+ * result of a failed operation. We do the same.
+ */
+static void
+lx_io_do_op(lx_io_elem_t *ep)
+{
+ int err;
+ int64_t res = 0;
+
+ set_active_fd(ep->lxioelem_fd);
+
+ ttolwp(curthread)->lwp_errno = 0;
+ switch (ep->lxioelem_op) {
+ case LX_IOCB_CMD_FSYNC:
+ case LX_IOCB_CMD_FDSYNC:
+ /*
+ * Note that Linux always returns EINVAL for these two
+ * operations. This is apparently because nothing in Linux
+ * defines the 'aio_fsync' function. Thus, it is unlikely any
+ * application will actually submit these.
+ *
+ * This is basically fdsync(), but we already have the fp.
+ */
+ err = VOP_FSYNC(ep->lxioelem_fp->f_vnode,
+ (ep->lxioelem_op == LX_IOCB_CMD_FSYNC) ? FSYNC : FDSYNC,
+ ep->lxioelem_fp->f_cred, NULL);
+ if (err != 0) {
+ (void) set_errno(err);
+ }
+
+ break;
+
+ case LX_IOCB_CMD_PREAD:
+ res = lx_pread_fp(ep->lxioelem_fp, ep->lxioelem_buf,
+ ep->lxioelem_nbytes, ep->lxioelem_offset);
+ break;
+
+ case LX_IOCB_CMD_PWRITE:
+ res = lx_pwrite_fp(ep->lxioelem_fp, ep->lxioelem_buf,
+ ep->lxioelem_nbytes, ep->lxioelem_offset);
+ break;
+
+ default:
+ /* We validated the op at io_submit syscall time */
+ VERIFY(0);
+ break;
+ }
+ if (ttolwp(curthread)->lwp_errno != 0)
+ res = -lx_errno(ttolwp(curthread)->lwp_errno, EINVAL);
+
+ ep->lxioelem_res = res;
+
+ releasef(ep->lxioelem_fd);
+ ep->lxioelem_fd = 0;
+ ep->lxioelem_fp = NULL;
+}
+
+/*
+ * The operation has either completed or been cancelled. Finalize the handling
+ * and move the operation onto the "done" queue.
+ */
+static void
+lx_io_finish_op(lx_io_ctx_t *cp, lx_io_elem_t *ep, boolean_t do_event)
+{
+ boolean_t do_resfd;
+ int resfd = 0;
+ file_t *resfp = NULL;
+
+ if (ep->lxioelem_flags & LX_IOCB_FLAG_RESFD) {
+ do_resfd = B_TRUE;
+ resfd = ep->lxioelem_resfd;
+ resfp = ep->lxioelem_resfp;
+ } else {
+ do_resfd = B_FALSE;
+ }
+
+ ep->lxioelem_flags = 0;
+ ep->lxioelem_resfd = 0;
+ ep->lxioelem_resfp = NULL;
+
+ mutex_enter(&cp->lxioctx_d_lock);
+ list_insert_tail(&cp->lxioctx_done, ep);
+ cp->lxioctx_done_cnt++;
+ cv_signal(&cp->lxioctx_done_cv);
+ mutex_exit(&cp->lxioctx_d_lock);
+
+ /* Update the eventfd if necessary */
+ if (do_resfd) {
+ vnode_t *vp = resfp->f_vnode;
+ uint64_t val = 1;
+
+ set_active_fd(resfd);
+
+ if (do_event) {
+ /*
+ * Eventfd notifications from AIO are special in that
+ * they are not expected to block. This interface allows
+ * the eventfd value to reach (but not cross) the
+ * overflow value.
+ */
+ (void) VOP_IOCTL(vp, EVENTFDIOC_POST, (intptr_t)&val,
+ FKIOCTL, resfp->f_cred, NULL, NULL);
+ }
+
+ releasef(resfd);
+ }
+}
+
+/*
+ * First check if this worker needs to quit due to shutdown or exit. Return
+ * true in this case.
+ *
+ * Then check if our process is forking. In this case it expects all LWPs to be
+ * stopped first. For the worker threads, a stop equivalent to holdlwp() is
+ * necessary before the fork can proceed.
+ *
+ * It is common to check p_flag outside of p_lock (see issig) and we want to
+ * avoid making p_lock any hotter since this is called in the worker main loops.
+ */
+static boolean_t
+lx_io_worker_chk_status(lx_io_ctx_t *cp, boolean_t locked)
+{
+ if (cp->lxioctx_shutdown)
+ return (B_TRUE);
+
+ if (curproc->p_flag & (SEXITLWPS | SKILLED)) {
+ cp->lxioctx_shutdown = B_TRUE;
+ return (B_TRUE);
+ }
+
+ if (curproc->p_flag & (SHOLDFORK | SHOLDFORK1)) {
+ if (locked)
+ mutex_exit(&cp->lxioctx_p_lock);
+
+ mutex_enter(&curproc->p_lock);
+ stop(PR_SUSPENDED, SUSPEND_NORMAL);
+ mutex_exit(&curproc->p_lock);
+
+ if (locked)
+ mutex_enter(&cp->lxioctx_p_lock);
+
+ if (cp->lxioctx_shutdown)
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Worker thread - pull work off the pending queue, perform the operation and
+ * place the result on the done queue. Do this as long as work is pending, then
+ * wait for more.
+ */
+static void
+lx_io_worker(void *a)
+{
+ lx_io_ctx_t *cp = (lx_io_ctx_t *)a;
+ lx_io_elem_t *ep;
+
+ set_active_fd(-1); /* See comment in lx_io_cp_rele */
+
+ while (!cp->lxioctx_shutdown) {
+ mutex_enter(&cp->lxioctx_p_lock);
+ if (list_is_empty(&cp->lxioctx_pending)) {
+ /*
+ * This must be cv_wait_sig, as opposed to cv_wait, so
+ * that pokelwps works correctly on these threads.
+ *
+ * The worker threads have all of their signals held,
+ * so a cv_wait_sig return of 0 here only occurs while
+ * we're shutting down.
+ */
+ if (cv_wait_sig(&cp->lxioctx_pending_cv,
+ &cp->lxioctx_p_lock) == 0)
+ cp->lxioctx_shutdown = B_TRUE;
+ }
+
+ if (lx_io_worker_chk_status(cp, B_TRUE)) {
+ mutex_exit(&cp->lxioctx_p_lock);
+ break;
+ }
+
+ ep = list_remove_head(&cp->lxioctx_pending);
+ mutex_exit(&cp->lxioctx_p_lock);
+
+ while (ep != NULL) {
+ lx_io_do_op(ep);
+
+ lx_io_finish_op(cp, ep, B_TRUE);
+
+ if (lx_io_worker_chk_status(cp, B_FALSE))
+ break;
+
+ mutex_enter(&cp->lxioctx_p_lock);
+ ep = list_remove_head(&cp->lxioctx_pending);
+ mutex_exit(&cp->lxioctx_p_lock);
+ }
+ }
+
+ lx_io_cp_rele(cp);
+
+ ASSERT(curthread->t_lwp != NULL);
+ mutex_enter(&curproc->p_lock);
+ lwp_exit();
+}
+
+/*
+ * LTP passes -1 for nr_events but we're limited by LX_AIO_MAX_NR anyway.
+ */
+long
+lx_io_setup(uint_t nr_events, void *ctxp)
+{
+ int i, slot;
+ proc_t *p = curproc;
+ lx_proc_data_t *lxpd = ptolxproc(p);
+ lx_zone_data_t *lxzd = ztolxzd(p->p_zone);
+ lx_io_ctx_t *cp;
+ lx_io_elem_t *ep;
+ uintptr_t cid;
+ uint_t nworkers;
+ k_sigset_t hold_set;
+
+#ifdef _SYSCALL32_IMPL
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ uintptr32_t cid32;
+
+ if (copyin(ctxp, &cid32, sizeof (cid32)) != 0)
+ return (set_errno(EFAULT));
+ cid = (uintptr_t)cid32;
+ } else
+#endif
+ if (copyin(ctxp, &cid, sizeof (cid)) != 0)
+ return (set_errno(EFAULT));
+
+ /* The cid in user-land must be NULL to start */
+ if (cid != (uintptr_t)NULL || nr_events > LX_AIO_MAX_NR)
+ return (set_errno(EINVAL));
+
+ mutex_enter(&lxzd->lxzd_lock);
+ if ((nr_events + lxzd->lxzd_aio_nr) > LX_AIO_MAX_NR) {
+ mutex_exit(&lxzd->lxzd_lock);
+ return (set_errno(EAGAIN));
+ }
+ lxzd->lxzd_aio_nr += nr_events;
+ mutex_exit(&lxzd->lxzd_lock);
+
+ /* Find a free slot */
+ mutex_enter(&lxpd->l_io_ctx_lock);
+ if (lxpd->l_io_ctxs == NULL) {
+ /*
+ * First use of aio, allocate a context array and a page
+ * in our address space to use for context ID handling.
+ */
+ uintptr_t ctxpage;
+
+ ASSERT(lxpd->l_io_ctx_cnt == 0);
+ ASSERT(lxpd->l_io_ctxpage == (uintptr_t)NULL);
+
+ ttolwp(curthread)->lwp_errno = 0;
+ ctxpage = (uintptr_t)smmap64(0, PAGESIZE, PROT_READ,
+ MAP_SHARED | MAP_ANON, -1, 0);
+ if (ttolwp(curthread)->lwp_errno != 0) {
+ mutex_exit(&lxpd->l_io_ctx_lock);
+ return (set_errno(ENOMEM));
+ }
+
+ lxpd->l_io_ctxpage = ctxpage;
+ lxpd->l_io_ctx_cnt = LX_IOCTX_CNT_BASE;
+ lxpd->l_io_ctxs = kmem_zalloc(lxpd->l_io_ctx_cnt *
+ sizeof (lx_io_ctx_t *), KM_SLEEP);
+ slot = 0;
+ } else {
+ ASSERT(lxpd->l_io_ctx_cnt > 0);
+ for (slot = 0; slot < lxpd->l_io_ctx_cnt; slot++) {
+ if (lxpd->l_io_ctxs[slot] == NULL)
+ break;
+ }
+
+ if (slot == lxpd->l_io_ctx_cnt) {
+ /* Double our context array up to the max. */
+ const uint_t new_cnt = lxpd->l_io_ctx_cnt * 2;
+ const uint_t old_size = lxpd->l_io_ctx_cnt *
+ sizeof (lx_io_ctx_t *);
+ const uint_t new_size = new_cnt *
+ sizeof (lx_io_ctx_t *);
+ struct lx_io_ctx **old_array = lxpd->l_io_ctxs;
+
+ if (new_cnt > LX_IOCTX_CNT_MAX) {
+ mutex_exit(&lxpd->l_io_ctx_lock);
+ mutex_enter(&lxzd->lxzd_lock);
+ lxzd->lxzd_aio_nr -= nr_events;
+ mutex_exit(&lxzd->lxzd_lock);
+ return (set_errno(ENOMEM));
+ }
+
+ /* See big theory comment explaining context ID. */
+ VERIFY(PAGESIZE >= new_size);
+ lxpd->l_io_ctxs = kmem_zalloc(new_size, KM_SLEEP);
+
+ bcopy(old_array, lxpd->l_io_ctxs, old_size);
+ kmem_free(old_array, old_size);
+ lxpd->l_io_ctx_cnt = new_cnt;
+
+ /* note: 'slot' is now valid in the new array */
+ }
+ }
+
+ cp = kmem_zalloc(sizeof (lx_io_ctx_t), KM_SLEEP);
+ list_create(&cp->lxioctx_free, sizeof (lx_io_elem_t),
+ offsetof(lx_io_elem_t, lxioelem_link));
+ list_create(&cp->lxioctx_pending, sizeof (lx_io_elem_t),
+ offsetof(lx_io_elem_t, lxioelem_link));
+ list_create(&cp->lxioctx_done, sizeof (lx_io_elem_t),
+ offsetof(lx_io_elem_t, lxioelem_link));
+ mutex_init(&cp->lxioctx_f_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&cp->lxioctx_p_lock, NULL, MUTEX_DEFAULT, NULL);
+ mutex_init(&cp->lxioctx_d_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&cp->lxioctx_pending_cv, NULL, CV_DEFAULT, NULL);
+ cv_init(&cp->lxioctx_done_cv, NULL, CV_DEFAULT, NULL);
+
+ /* Add a hold on this context until we're done setting up */
+ cp->lxioctx_in_use = 1;
+ lxpd->l_io_ctxs[slot] = cp;
+
+ cid = CTXID_TO_PTR(lxpd, slot);
+
+ mutex_exit(&lxpd->l_io_ctx_lock);
+
+ /*
+ * Finish setting up the context.
+ *
+ * The context is in the l_io_ctxs array now, so it is potentially
+ * visible to other threads. However, we have a hold so it cannot be
+ * destroyed, and both lxioctx_free_cnt and lxioctx_maxn are still 0,
+ * so nothing can be submitted to this context yet either.
+ */
+
+ /* Setup the free list of internal control block elements */
+ for (i = 0; i < nr_events; i++) {
+ ep = kmem_zalloc(sizeof (lx_io_elem_t), KM_SLEEP);
+ list_insert_head(&cp->lxioctx_free, ep);
+ }
+
+ /*
+ * Pre-allocate the worker threads at setup time.
+ *
+ * Based on how much concurrent input we may be given, we want enough
+ * worker threads to get good parallelism but we also want to taper off
+ * and cap at our upper limit. Our zone's ZFS I/O limit may also come
+ * into play when we're pumping lots of I/O in parallel.
+ *
+ * Note: a possible enhancement here would be to also limit the number
+ * of worker threads based on the zone's cpu-cap. That is, if the
+ * cap is low, we might not want too many worker threads.
+ */
+ if (nr_events <= lx_aio_base_workers) {
+ nworkers = nr_events;
+ } else {
+ /* scale up until hit max */
+ nworkers = (nr_events / 2) + (lx_aio_base_workers / 2);
+ if (nworkers > lx_aio_max_workers)
+ nworkers = lx_aio_max_workers;
+ }
+
+ sigfillset(&hold_set);
+ for (i = 0; i < nworkers; i++) {
+ klwp_t *l;
+ kthread_t *t;
+
+ /*
+ * Note that this lwp will not "stop at sys_rtt" as described
+ * on lwp_create. This lwp will run entirely in the kernel as
+ * a worker thread serving aio requests.
+ */
+ l = lwp_create(lx_io_worker, (void *)cp, 0, p, TS_STOPPED,
+ minclsyspri - 1, &hold_set, curthread->t_cid, 0);
+ if (l == NULL) {
+ if (i == 0) {
+ /*
+ * Uh-oh - we can't create a single worker.
+ * Release our hold which will cleanup.
+ */
+ cp->lxioctx_shutdown = B_TRUE;
+ mutex_enter(&lxpd->l_io_ctx_lock);
+ cp->lxioctx_maxn = nr_events;
+ mutex_exit(&lxpd->l_io_ctx_lock);
+ lx_io_cp_rele(cp);
+ return (set_errno(ENOMEM));
+ } else {
+ /*
+ * No new lwp but we already have at least 1
+ * worker so don't fail entire syscall.
+ */
+ break;
+ }
+ }
+
+ atomic_inc_32(&cp->lxioctx_in_use);
+
+ /*
+ * Mark it as an in-kernel thread, an lx AIO worker LWP, and
+ * set it running.
+ */
+ t = lwptot(l);
+ mutex_enter(&curproc->p_lock);
+ t->t_proc_flag = (t->t_proc_flag & ~TP_HOLDLWP) | TP_KTHREAD;
+ lwptolxlwp(l)->br_lwp_flags |= BR_AIO_LWP;
+ lwp_create_done(t);
+ mutex_exit(&curproc->p_lock);
+ }
+
+ /*
+ * io_submit can occur once lxioctx_free_cnt and lxioctx_maxn are
+ * non-zero.
+ */
+ mutex_enter(&lxpd->l_io_ctx_lock);
+ cp->lxioctx_maxn = cp->lxioctx_free_cnt = nr_events;
+ mutex_exit(&lxpd->l_io_ctx_lock);
+ /* Release our hold, worker thread refs keep ctx alive. */
+ lx_io_cp_rele(cp);
+
+#ifdef _SYSCALL32_IMPL
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ uintptr32_t cid32 = (uintptr32_t)cid;
+
+ if (copyout(&cid32, ctxp, sizeof (cid32)) != 0) {
+ /* Since we did a copyin above, this shouldn't fail */
+ (void) lx_io_destroy(cid);
+ return (set_errno(EFAULT));
+ }
+ } else
+#endif
+ if (copyout(&cid, ctxp, sizeof (cid)) != 0) {
+ /* Since we did a copyin above, this shouldn't fail */
+ (void) lx_io_destroy(cid);
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+
+long
+lx_io_submit(lx_aio_context_t cid, const long nr, uintptr_t **bpp)
+{
+ uint_t i = 0;
+ int err = 0;
+ const size_t sz = nr * sizeof (uintptr_t);
+ lx_io_ctx_t *cp;
+ lx_io_elem_t *ep;
+ lx_iocb_t **iocbpp;
+
+ if ((cp = lx_io_cp_hold(cid)) == NULL)
+ return (set_errno(EINVAL));
+
+ if (nr == 0) {
+ lx_io_cp_rele(cp);
+ return (0);
+ }
+
+ if (nr < 0 || nr > cp->lxioctx_maxn) {
+ lx_io_cp_rele(cp);
+ return (set_errno(EINVAL));
+ }
+
+ if (nr > MAX_ALLOC_ON_STACK) {
+ iocbpp = (lx_iocb_t **)kmem_alloc(sz, KM_NOSLEEP);
+ if (iocbpp == NULL) {
+ lx_io_cp_rele(cp);
+ return (set_errno(EAGAIN));
+ }
+ } else {
+ iocbpp = (lx_iocb_t **)alloca(sz);
+ }
+
+#ifdef _SYSCALL32_IMPL
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ uintptr32_t *iocbpp32;
+
+ if (copyin(bpp, iocbpp, nr * sizeof (uintptr32_t)) != 0) {
+ lx_io_cp_rele(cp);
+ err = EFAULT;
+ goto out;
+ }
+
+ /*
+ * Zero-extend the 32-bit pointers to proper size. This is
+ * performed "in reverse" so it can be done in-place, rather
+ * than with an additional translation copy.
+ */
+ iocbpp32 = (uintptr32_t *)iocbpp;
+ i = nr;
+ do {
+ i--;
+ iocbpp[i] = (lx_iocb_t *)(uintptr_t)iocbpp32[i];
+ } while (i != 0);
+ } else
+#endif
+ if (copyin(bpp, iocbpp, nr * sizeof (uintptr_t)) != 0) {
+ lx_io_cp_rele(cp);
+ err = EFAULT;
+ goto out;
+ }
+
+ /* We need to return an error if not able to process any of them */
+ mutex_enter(&cp->lxioctx_f_lock);
+ if (cp->lxioctx_free_cnt == 0) {
+ mutex_exit(&cp->lxioctx_f_lock);
+ lx_io_cp_rele(cp);
+ err = EAGAIN;
+ goto out;
+ }
+ mutex_exit(&cp->lxioctx_f_lock);
+
+ for (i = 0; i < nr; i++) {
+ lx_iocb_t cb;
+ file_t *fp, *resfp = NULL;
+
+ if (cp->lxioctx_shutdown)
+ break;
+
+ if (copyin(iocbpp[i], &cb, sizeof (lx_iocb_t)) != 0) {
+ err = EFAULT;
+ break;
+ }
+
+ /* There is only one valid flag */
+ if (cb.lxiocb_flags & ~LX_IOCB_FLAG_RESFD) {
+ err = EINVAL;
+ break;
+ }
+
+ switch (cb.lxiocb_op) {
+ case LX_IOCB_CMD_FSYNC:
+ case LX_IOCB_CMD_FDSYNC:
+ case LX_IOCB_CMD_PREAD:
+ case LX_IOCB_CMD_PWRITE:
+ break;
+
+ /*
+ * We don't support asynchronous preadv and pwritev (an
+ * asynchronous scatter/gather being a somewhat odd
+ * notion to begin with); we return EINVAL for that
+ * case, which the caller should be able to deal with.
+ * We also return EINVAL for LX_IOCB_CMD_NOOP or any
+ * unrecognized opcode.
+ */
+ default:
+ err = EINVAL;
+ break;
+ }
+ if (err != 0)
+ break;
+
+ /* Validate fd */
+ if ((fp = getf(cb.lxiocb_fd)) == NULL) {
+ err = EBADF;
+ break;
+ }
+
+ if (cb.lxiocb_op == LX_IOCB_CMD_PREAD &&
+ (fp->f_flag & FREAD) == 0) {
+ err = EBADF;
+ releasef(cb.lxiocb_fd);
+ break;
+ } else if (cb.lxiocb_op == LX_IOCB_CMD_PWRITE &&
+ (fp->f_flag & FWRITE) == 0) {
+ err = EBADF;
+ releasef(cb.lxiocb_fd);
+ break;
+ }
+
+ /*
+ * A character device is a bit complicated. Linux seems to
+ * accept these on some devices (e.g. /dev/zero) but not
+ * others (e.g. /proc/self/fd/0). This might be related to
+ * the device being seek-able, but a simple seek-set to the
+ * current offset will succeed for us on a pty. For now we
+ * handle this by rejecting the device if it is a stream.
+ *
+ * If it is a pipe (VFIFO) or directory (VDIR), we error here
+ * as does Linux. If it is a socket (VSOCK), it's ok here but
+ * we will post ESPIPE when processing the I/O CB, as does
+ * Linux. We also error on our other types: VDOOR, VPROC,
+ * VPORT, VBAD.
+ */
+ if (fp->f_vnode->v_type == VCHR) {
+ if (fp->f_vnode->v_stream != NULL) {
+ err = EINVAL;
+ releasef(cb.lxiocb_fd);
+ break;
+ }
+ } else if (fp->f_vnode->v_type != VREG &&
+ fp->f_vnode->v_type != VBLK &&
+ fp->f_vnode->v_type != VSOCK) {
+ err = EINVAL;
+ releasef(cb.lxiocb_fd);
+ break;
+ }
+
+ if (cb.lxiocb_flags & LX_IOCB_FLAG_RESFD) {
+ if ((resfp = getf(cb.lxiocb_resfd)) == NULL ||
+ !lx_is_eventfd(resfp)) {
+ err = EINVAL;
+ releasef(cb.lxiocb_fd);
+ if (resfp != NULL)
+ releasef(cb.lxiocb_resfd);
+ break;
+ }
+ }
+
+ mutex_enter(&cp->lxioctx_f_lock);
+ if (cp->lxioctx_free_cnt == 0) {
+ mutex_exit(&cp->lxioctx_f_lock);
+ releasef(cb.lxiocb_fd);
+ if (cb.lxiocb_flags & LX_IOCB_FLAG_RESFD) {
+ releasef(cb.lxiocb_resfd);
+ }
+ if (i == 0) {
+ /*
+ * Another thread used all of the free entries
+ * after the check preceding this loop. Since
+ * we did nothing, we must return an error.
+ */
+ err = EAGAIN;
+ }
+ break;
+ }
+ ep = list_remove_head(&cp->lxioctx_free);
+ cp->lxioctx_free_cnt--;
+ ASSERT(ep != NULL);
+ mutex_exit(&cp->lxioctx_f_lock);
+
+ ep->lxioelem_op = cb.lxiocb_op;
+ ep->lxioelem_fd = cb.lxiocb_fd;
+ ep->lxioelem_fp = fp;
+ ep->lxioelem_buf = (void *)(uintptr_t)cb.lxiocb_buf;
+ ep->lxioelem_nbytes = cb.lxiocb_nbytes;
+ ep->lxioelem_offset = cb.lxiocb_offset;
+ ep->lxioelem_data = cb.lxiocb_data;
+ ep->lxioelem_cbp = iocbpp[i];
+
+ /* Hang on to the fp but setup to hand it off to a worker */
+ clear_active_fd(cb.lxiocb_fd);
+
+ if (cb.lxiocb_flags & LX_IOCB_FLAG_RESFD) {
+ ep->lxioelem_flags = LX_IOCB_FLAG_RESFD;
+ ep->lxioelem_resfd = cb.lxiocb_resfd;
+ ep->lxioelem_resfp = resfp;
+ clear_active_fd(cb.lxiocb_resfd);
+ }
+
+ mutex_enter(&cp->lxioctx_p_lock);
+ list_insert_tail(&cp->lxioctx_pending, ep);
+ cv_signal(&cp->lxioctx_pending_cv);
+ mutex_exit(&cp->lxioctx_p_lock);
+ }
+
+ lx_io_cp_rele(cp);
+
+out:
+ if (nr > MAX_ALLOC_ON_STACK) {
+ kmem_free(iocbpp, sz);
+ }
+ if (i == 0 && err != 0)
+ return (set_errno(err));
+
+ return (i);
+}
+
+long
+lx_io_getevents(lx_aio_context_t cid, long min_nr, const long nr,
+ lx_io_event_t *events, timespec_t *timeoutp)
+{
+ int i;
+ lx_io_ctx_t *cp;
+ const size_t sz = nr * sizeof (lx_io_event_t);
+ timespec_t timeout, *tp;
+ lx_io_event_t *out;
+
+ if ((cp = lx_io_cp_hold(cid)) == NULL)
+ return (set_errno(EINVAL));
+
+ if (min_nr < 0 || min_nr > cp->lxioctx_maxn ||
+ nr < 0 || nr > cp->lxioctx_maxn) {
+ lx_io_cp_rele(cp);
+ return (set_errno(EINVAL));
+ }
+
+ if (nr == 0) {
+ lx_io_cp_rele(cp);
+ return (0);
+ }
+
+ if (events == NULL) {
+ lx_io_cp_rele(cp);
+ return (set_errno(EFAULT));
+ }
+
+ if (timeoutp == NULL) {
+ tp = NULL;
+ } else {
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(timeoutp, &timeout, sizeof (timestruc_t))) {
+ lx_io_cp_rele(cp);
+ return (EFAULT);
+ }
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ timestruc32_t timeout32;
+ if (copyin(timeoutp, &timeout32,
+ sizeof (timestruc32_t))) {
+ lx_io_cp_rele(cp);
+ return (EFAULT);
+ }
+ timeout.tv_sec = (time_t)timeout32.tv_sec;
+ timeout.tv_nsec = timeout32.tv_nsec;
+ }
+#endif
+
+ if (itimerspecfix(&timeout)) {
+ lx_io_cp_rele(cp);
+ return (EINVAL);
+ }
+
+ tp = &timeout;
+ if (timeout.tv_sec == 0 && timeout.tv_nsec == 0) {
+ /*
+ * A timeout of 0:0 is like a poll; we return however
+ * many events are ready, irrespective of the passed
+ * min_nr.
+ */
+ min_nr = 0;
+ } else {
+ timestruc_t now;
+
+ /*
+ * We're given a relative time; add it to the current
+ * time to derive an absolute time.
+ */
+ gethrestime(&now);
+ timespecadd(tp, &now);
+ }
+ }
+
+ out = kmem_zalloc(sz, KM_SLEEP);
+
+ /*
+ * A min_nr of 0 is like a poll even if given a NULL timeout; we return
+ * however many events are ready.
+ */
+ if (min_nr > 0) {
+ mutex_enter(&cp->lxioctx_d_lock);
+ while (!cp->lxioctx_shutdown && cp->lxioctx_done_cnt < min_nr) {
+ int r;
+
+ r = cv_waituntil_sig(&cp->lxioctx_done_cv,
+ &cp->lxioctx_d_lock, tp, timechanged);
+ if (r < 0) {
+ /* timeout */
+ mutex_exit(&cp->lxioctx_d_lock);
+ lx_io_cp_rele(cp);
+ kmem_free(out, sz);
+ return (0);
+ } else if (r == 0) {
+ /* interrupted */
+ mutex_exit(&cp->lxioctx_d_lock);
+ lx_io_cp_rele(cp);
+ kmem_free(out, sz);
+ return (set_errno(EINTR));
+ }
+
+ /*
+ * Signalled that something was queued up. Check if
+ * there are now enough or if we have to wait for more.
+ */
+ }
+ ASSERT(cp->lxioctx_done_cnt >= min_nr || cp->lxioctx_shutdown);
+ mutex_exit(&cp->lxioctx_d_lock);
+ }
+
+ /*
+ * For each done control block, move it into the Linux event we return.
+ * As we're doing this, we also moving it from the done list to the
+ * free list.
+ */
+ for (i = 0; i < nr && !cp->lxioctx_shutdown; i++) {
+ lx_io_event_t *lxe;
+ lx_io_elem_t *ep;
+
+ lxe = &out[i];
+
+ mutex_enter(&cp->lxioctx_d_lock);
+ if (cp->lxioctx_done_cnt == 0) {
+ mutex_exit(&cp->lxioctx_d_lock);
+ break;
+ }
+
+ ep = list_remove_head(&cp->lxioctx_done);
+ cp->lxioctx_done_cnt--;
+ mutex_exit(&cp->lxioctx_d_lock);
+
+ lxe->lxioe_data = ep->lxioelem_data;
+ lxe->lxioe_object = (uint64_t)(uintptr_t)ep->lxioelem_cbp;
+ lxe->lxioe_res = ep->lxioelem_res;
+ lxe->lxioe_res2 = 0;
+
+ /* Put it back on the free list */
+ ep->lxioelem_cbp = NULL;
+ ep->lxioelem_data = 0;
+ ep->lxioelem_res = 0;
+ mutex_enter(&cp->lxioctx_f_lock);
+ list_insert_head(&cp->lxioctx_free, ep);
+ cp->lxioctx_free_cnt++;
+ mutex_exit(&cp->lxioctx_f_lock);
+ }
+
+ lx_io_cp_rele(cp);
+
+ /*
+ * Note: Linux seems to push the events back into the queue if the
+ * copyout fails. Since this error is due to an application bug, it
+ * seems unlikely we need to worry about it, but we can revisit this
+ * if it is ever seen to be an issue.
+ */
+ if (i > 0 && copyout(out, events, i * sizeof (lx_io_event_t)) != 0) {
+ kmem_free(out, sz);
+ return (set_errno(EFAULT));
+ }
+
+ kmem_free(out, sz);
+ return (i);
+}
+
+/*
+ * Linux never returns 0 from io_cancel. A successful cancellation will return
+ * EINPROGRESS and the result for the cancelled operation will be available via
+ * a normal io_getevents call. The third parameter (the "result") to this
+ * syscall is unused. Note that currently the Linux man pages are incorrect
+ * about this behavior. Also note that in Linux, only the USB driver currently
+ * support aio cancellation, so callers will almost always get EINVAL when they
+ * attempt to cancel an IO on Linux.
+ */
+/*ARGSUSED*/
+long
+lx_io_cancel(lx_aio_context_t cid, lx_iocb_t *iocbp, lx_io_event_t *result)
+{
+ lx_io_ctx_t *cp;
+ lx_io_elem_t *ep;
+ uint32_t buf;
+
+ /*
+ * The Linux io_cancel copies in a field from the iocb in order to
+ * locate the matching kernel-internal structure. To appease the LTP
+ * test case which exercises this, a similar copy is performed here.
+ */
+ if (copyin(iocbp, &buf, sizeof (buf)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ if ((cp = lx_io_cp_hold(cid)) == NULL)
+ return (set_errno(EINVAL));
+
+ /* Try to pull the CB off the pending list */
+ mutex_enter(&cp->lxioctx_p_lock);
+ ep = list_head(&cp->lxioctx_pending);
+ while (ep != NULL) {
+ if (ep->lxioelem_cbp == iocbp) {
+ list_remove(&cp->lxioctx_pending, ep);
+ break;
+ }
+ ep = list_next(&cp->lxioctx_pending, ep);
+ }
+ mutex_exit(&cp->lxioctx_p_lock);
+
+ if (ep == NULL) {
+ lx_io_cp_rele(cp);
+ return (set_errno(EAGAIN));
+ }
+
+ set_active_fd(-1); /* See comment in lx_io_cp_rele */
+ set_active_fd(ep->lxioelem_fd);
+ releasef(ep->lxioelem_fd);
+ ep->lxioelem_fd = 0;
+ ep->lxioelem_fp = NULL;
+ ep->lxioelem_res = -lx_errno(EINTR, EINTR);
+
+ lx_io_finish_op(cp, ep, B_FALSE);
+ lx_io_cp_rele(cp);
+
+ return (set_errno(EINPROGRESS));
+}
+
+long
+lx_io_destroy(lx_aio_context_t cid)
+{
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ lx_io_ctx_t *cp;
+ int cnt = 0;
+
+ if ((cp = lx_io_cp_hold(cid)) == NULL)
+ return (set_errno(EINVAL));
+
+ mutex_enter(&lxpd->l_io_ctx_lock);
+ cp->lxioctx_shutdown = B_TRUE;
+
+ /*
+ * Wait for the worker threads and any blocked io_getevents threads to
+ * exit. We have a hold and our rele will cleanup after all other holds
+ * are released.
+ */
+ ASSERT(cp->lxioctx_in_use >= 1);
+ while (cp->lxioctx_in_use > 1) {
+ DTRACE_PROBE2(lx__io__destroy, lx_io_ctx_t *, cp, int, cnt);
+ cv_broadcast(&cp->lxioctx_pending_cv);
+ cv_broadcast(&cp->lxioctx_done_cv);
+
+ /*
+ * Each worker has a hold. We want to let those threads finish
+ * up and exit.
+ */
+ cv_wait(&lxpd->l_io_destroy_cv, &lxpd->l_io_ctx_lock);
+ cnt++;
+ }
+
+ mutex_exit(&lxpd->l_io_ctx_lock);
+ lx_io_cp_rele(cp);
+ return (0);
+}
+
+/*
+ * Called at proc fork to clear contexts from child. We don't bother to unmap
+ * l_io_ctxpage since the vast majority of processes will immediately exec and
+ * cause an unmapping. If the child does not exec, there will simply be a
+ * single shared page in its address space, so no additional anonymous memory
+ * is consumed.
+ */
+void
+lx_io_clear(lx_proc_data_t *cpd)
+{
+ cpd->l_io_ctxs = NULL;
+ cpd->l_io_ctx_cnt = 0;
+ cpd->l_io_ctxpage = (uintptr_t)NULL;
+}
+
+/*
+ * Called via lx_proc_exit to cleanup any existing io context array. All
+ * worker threads should have already exited by this point, so all contexts
+ * should already be deleted.
+ */
+void
+lx_io_cleanup(proc_t *p)
+{
+ lx_proc_data_t *lxpd;
+ int i;
+
+ mutex_enter(&p->p_lock);
+ VERIFY((lxpd = ptolxproc(p)) != NULL);
+ mutex_exit(&p->p_lock);
+
+ mutex_enter(&lxpd->l_io_ctx_lock);
+ if (lxpd->l_io_ctxs == NULL) {
+ ASSERT(lxpd->l_io_ctx_cnt == 0);
+ mutex_exit(&lxpd->l_io_ctx_lock);
+ return;
+ }
+
+ ASSERT(lxpd->l_io_ctx_cnt > 0);
+ for (i = 0; i < lxpd->l_io_ctx_cnt; i++) {
+ ASSERT(lxpd->l_io_ctxs[i] == NULL);
+ }
+
+ kmem_free(lxpd->l_io_ctxs, lxpd->l_io_ctx_cnt * sizeof (lx_io_ctx_t *));
+ lxpd->l_io_ctxs = NULL;
+ lxpd->l_io_ctx_cnt = 0;
+ mutex_exit(&lxpd->l_io_ctx_lock);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_brk.c b/usr/src/uts/common/brand/lx/syscall/lx_brk.c
new file mode 100644
index 0000000000..d46e442759
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_brk.c
@@ -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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/errno.h>
+
+/* From usr/src/uts/common/os/grow.c */
+extern intptr_t brk(caddr_t);
+
+long
+lx_brk(caddr_t nva)
+{
+ if (nva != 0) {
+ (void) brk(nva);
+
+ /*
+ * Despite claims to the contrary in the man page, when Linux
+ * brk(2) fails, errno is left unchanged.
+ */
+ ttolwp(curthread)->lwp_errno = 0;
+ }
+
+ /*
+ * When ASLR was integrated, our internal brk(2) was updated to emit
+ * the current brk when arg0 == 0. Using the function yields an
+ * equivalent result to manually calculating the brk, but also
+ * serializes with changes to the process AS.
+ */
+ return ((long)brk((caddr_t)0));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_chmod.c b/usr/src/uts/common/brand/lx/syscall/lx_chmod.c
new file mode 100644
index 0000000000..7783b97cb0
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_chmod.c
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/thread.h>
+#include <sys/klwp.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+
+long
+lx_vn_chmod(vnode_t *vp, int mode)
+{
+ vattr_t vattr;
+
+ vattr.va_mode = mode & MODEMASK;
+ vattr.va_mask = AT_MODE;
+
+ if (vn_is_readonly(vp)) {
+ return (EROFS);
+ }
+ return (VOP_SETATTR(vp, &vattr, 0, CRED(), NULL));
+}
+
+static long
+lx_fchmodat_wrapper(int fd, char *path, int mode)
+{
+ long error;
+ vnode_t *vp;
+
+ if ((error = lx_vp_at(fd, path, &vp, 0)) != 0) {
+ lx_proc_data_t *pd = ttolxproc(curthread);
+
+ /*
+ * If the process is in "install mode", return success
+ * if the operation failed due to an absent file.
+ */
+ if (error == ENOENT &&
+ (pd->l_flags & LX_PROC_INSTALL_MODE)) {
+ return (0);
+ }
+ return (set_errno(error));
+ }
+
+ error = lx_vn_chmod(vp, mode);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fchmodat(int fd, char *path, int mode)
+{
+ return (lx_fchmodat_wrapper(fd, path, mode));
+}
+
+long
+lx_fchmod(int fd, int mode)
+{
+ file_t *fp;
+ vnode_t *vp;
+ long error;
+
+ /*
+ * In order to do proper O_PATH handling, lx_fchmod cannot leverage
+ * lx_fchmodat with a NULL path since the desired behavior differs.
+ */
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ if (LX_IS_O_PATH(fp)) {
+ releasef(fd);
+ return (set_errno(EBADF));
+ }
+ vp = fp->f_vnode;
+ VN_HOLD(vp);
+ releasef(fd);
+
+ error = lx_vn_chmod(vp, mode);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_chmod(char *path, int mode)
+{
+ return (lx_fchmodat_wrapper(LX_AT_FDCWD, path, mode));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_chown.c b/usr/src/uts/common/brand/lx/syscall/lx_chown.c
new file mode 100644
index 0000000000..830fba0a73
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_chown.c
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/zone.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_types.h>
+
+long
+lx_vn_chown(vnode_t *vp, uid_t uid, gid_t gid)
+{
+ vattr_t vattr;
+ zone_t *zone = crgetzone(CRED());
+
+ if ((uid != (uid_t)-1 && !VALID_UID(uid, zone)) ||
+ (gid != (gid_t)-1 && !VALID_GID(gid, zone))) {
+ return (EINVAL);
+ }
+ vattr.va_uid = uid;
+ vattr.va_gid = gid;
+ vattr.va_mask = 0;
+ if (vattr.va_uid != -1)
+ vattr.va_mask |= AT_UID;
+ if (vattr.va_gid != -1)
+ vattr.va_mask |= AT_GID;
+
+ if (vn_is_readonly(vp)) {
+ return (EROFS);
+ }
+ return (VOP_SETATTR(vp, &vattr, 0, CRED(), NULL));
+}
+
+long
+lx_fchownat_wrapper(int fd, char *path, uid_t uid, gid_t gid, int native_flag)
+{
+ long error;
+ vnode_t *vp;
+
+ if ((error = lx_vp_at(fd, path, &vp, native_flag)) != 0) {
+ lx_proc_data_t *pd = ttolxproc(curthread);
+
+ /*
+ * If the process is in "install mode", return success
+ * if the operation failed due to an absent file.
+ */
+ if (error == ENOENT &&
+ (pd->l_flags & LX_PROC_INSTALL_MODE)) {
+ return (0);
+ }
+ return (set_errno(error));
+ }
+
+ error = lx_vn_chown(vp, uid, gid);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fchown_wrapper(int fd, uid_t uid, gid_t gid)
+{
+ file_t *fp;
+ vnode_t *vp;
+ long error;
+
+ /*
+ * In order to do proper O_PATH handling, lx_fchown cannot leverage
+ * lx_fchownat with a NULL path since the desired behavior differs.
+ */
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ if (LX_IS_O_PATH(fp)) {
+ releasef(fd);
+ return (set_errno(EBADF));
+ }
+ vp = fp->f_vnode;
+ VN_HOLD(vp);
+ releasef(fd);
+
+ error = lx_vn_chown(vp, uid, gid);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fchownat(int fd, char *path, uid_t uid, gid_t gid, int flag)
+{
+ int native_flag = 0;
+
+ if (flag & LX_AT_EMPTY_PATH) {
+ char c;
+
+ /*
+ * According to fchownat(2), when AT_EMPTY_PATH is set: "if
+ * path is an empty string, operate on the file referred to by
+ * fd". We pass NULL in place of the empty string, which
+ * causes fchownat() to operate on the fd we passed without an
+ * additional lookup.
+ */
+ if (copyin(path, &c, sizeof (c)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ if (c == '\0') {
+ path = NULL;
+ }
+
+ flag &= ~LX_AT_EMPTY_PATH;
+ }
+ if (flag & LX_AT_SYMLINK_NOFOLLOW) {
+ flag &= ~LX_AT_SYMLINK_NOFOLLOW;
+ native_flag |= AT_SYMLINK_NOFOLLOW;
+ }
+ if (flag != 0) {
+ return (set_errno(EINVAL));
+ }
+
+ return (lx_fchownat_wrapper(fd, path, uid, gid, native_flag));
+}
+
+long
+lx_fchown(int fd, uid_t uid, gid_t gid)
+{
+ return (lx_fchown_wrapper(fd, uid, gid));
+}
+
+long
+lx_lchown(char *path, uid_t uid, gid_t gid)
+{
+ return (lx_fchownat_wrapper(AT_FDCWD, path, uid, gid,
+ AT_SYMLINK_NOFOLLOW));
+}
+
+long
+lx_chown(char *path, uid_t uid, gid_t gid)
+{
+ return (lx_fchownat_wrapper(AT_FDCWD, path, uid, gid, 0));
+}
+
+long
+lx_fchown16(int fd, lx_uid16_t uid, lx_gid16_t gid)
+{
+ return (lx_fchown_wrapper(fd, LX_UID16_TO_UID32(uid),
+ LX_GID16_TO_GID32(gid)));
+}
+
+long
+lx_lchown16(char *path, uid_t uid, gid_t gid)
+{
+ return (lx_fchownat_wrapper(AT_FDCWD, path, LX_UID16_TO_UID32(uid),
+ LX_GID16_TO_GID32(gid), AT_SYMLINK_NOFOLLOW));
+}
+
+long
+lx_chown16(char *path, lx_uid16_t uid, lx_gid16_t gid)
+{
+ return (lx_fchownat_wrapper(AT_FDCWD, path, LX_UID16_TO_UID32(uid),
+ LX_GID16_TO_GID32(gid), 0));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_clone.c b/usr/src/uts/common/brand/lx/syscall/lx_clone.c
new file mode 100644
index 0000000000..4e00e90b1a
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_clone.c
@@ -0,0 +1,513 @@
+/*
+ * 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.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * [This comment omits the 'LX_' prefix on the clone flag names.]
+ *
+ * The vast majority of clone calls result in the creation of a new process or
+ * a new thread. Both of these map easily from Linux to our native code. For
+ * these calls, the user-level brand library uses a brand call to hook into the
+ * lx_helper_clone function for the required in-kernel support.
+ *
+ * A fork will typically provide these clone flags:
+ * CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID
+ *
+ * A new thread will use our SHARED_AS macro which has the flags:
+ * CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_THREAD | CLONE_VM
+ *
+ * In rare cases an application will attempt to use a subset of the SHARED_AS
+ * flags in order to implement some sharing between two processes without using
+ * a true thread. Because we do not have native support for this concept, the
+ * lx brand implements the notion of a 'clone-group'. This is a set of
+ * processes which share a subset of the allowed SHARED_AS flags. The lx brand
+ * syscalls implement the appropriate sharing for each flag. A clone-group is
+ * only instantiated in the rare case that a subset of the SHARED_AS flags are
+ * used with clone.
+ *
+ * The following set of flags could theoretically be supported, although most
+ * are not implemented at this time. The user-level brand library will validate
+ * that a supported subset of the flags are being used, or error if not. We
+ * also re-validate in the kernel.
+ *
+ * CLONE_FILES: share the file descriptor table
+ * CLONE_FS: share the filesystem information (root of the filesystem, the
+ * CWD, and the umask)
+ * CLONE_SIGHAND: share the table of signal handlers
+ * CLONE_THREAD: share the thread group
+ * CLONE_VM: share the address space
+ *
+ * At this time, only those flags defined in CLONE_GRP_SUBSET (CLONE_FS) are
+ * implemented.
+ *
+ * When a clone-group is in use, the lx_proc_data_t`l_clone_grps array will
+ * hold groups of processes sharing the attributes relevant to the clone flag.
+ * Each supported flag can have an associated group list in the array.
+ *
+ * On the first clone, a new lx_clone_grp_t struct will be created. This struct
+ * holds a pointer to each process in the group. A reference to that group is
+ * held in the appropriate slot in l_clone_grps. The struct is created for
+ * the parent process by lx_clone_grp_create() and then the child process will
+ * associate itself with the group(s) using lx_clone_grp_enter().
+ *
+ * Each syscall acting upon attributes relevant to a clone-group must include
+ * logic to do so properly. The syscalls will use lx_clone_grp_member() to
+ * determine if clone-group handling is required, and use lx_clone_grp_walk()
+ * to walk the list of processes in the group and apply the provided callback
+ * to each process.
+ *
+ * The following example illustrates how a common clone group would be used,
+ * as processes clone with the same set of CLONE_* flags.
+ * A clones B with CLONE_FS
+ * B clones C with CLONE_FS
+ * When A clones B, a new clone group is created and saved in the LX_CLGRP_FS
+ * slot in the l_clone_grps array on both A and B. When B clones, since a group
+ * already exists, C is added to the group and the group is saved in the
+ * LX_CLGRP_FS slot on C.
+ *
+ * The following example illustrates how two common clone groups would be used,
+ * as processes clone with the same set of CLONE_* flags.
+ * A clones B with CLONE_FS|CLONE_THREAD
+ * A new clone group is created and saved in the LX_CLGRP_FS slot in the
+ * l_clone_grps array on both A and B. A second clone group is created and
+ * saved in the LX_CLGRP_THREAD slot on both A and B (note that LX_CLGRP_THREAD
+ * is not implemented at this time).
+ *
+ * The following example illustrates how different clone groups would be used,
+ * as processes clone with different sets of CLONE_* flags.
+ * A clones B with CLONE_FS
+ * B clones C with CLONE_THREAD
+ * C clones D with CLONE_FS
+ * In this example, only A&B and C&D should share their FS information. B&C
+ * have to be in two clone groups. When A clones, a new clone group is created
+ * and saved in the LX_CLGRP_FS slot in the l_clone_grps array on both A and B.
+ * When B clones, a new clone group is created and saved in the LX_CLGRP_THREAD
+ * slot on both B and C (note that LX_CLGRP_THREAD is not implemented at this
+ * time). When C clones, a new clone group is created and saved in the
+ * LX_CLGRP_FS slot on both C and D.
+ *
+ * When a process exits, it removes itself from any groups to which it belongs.
+ * When the last process exits a group, it is cleaned up.
+ *
+ * If clone-groups were commonly used, this implementation would be inefficient
+ * and unwieldy, but since they are so rare a straightforward list-based
+ * approach is adequate.
+ *
+ * During group creation, the l_clone_grp_lock is first taken to ensure only
+ * one group is created, otherwise, only the group's lx_clgrp_lock protects the
+ * list.
+ *
+ * Note: Despite the locking, there is still a subtle race that can occur in
+ * this code. This occurs if a process has two threads and one of them is about
+ * to execute a clone-group aware syscall (e.g. chdir), while the other thread
+ * is forking to create a new clone-group. In theory the child process could be
+ * created, but not yet in the group. The syscall in the first thread could
+ * thus miss the new process. For example, the first thread might chdir the
+ * parent, but since the child process was alrady created, but not yet in the
+ * clone-group, it would not be chdir-ed.
+ */
+
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_ldt.h>
+#include <sys/lx_misc.h>
+#include <lx_signum.h>
+#include <lx_syscall.h>
+#include <sys/x86_archext.h>
+#include <sys/controlregs.h>
+
+/*
+ * We currently only support a single clone-group (CLONE_FS) but the design
+ * allows for future expansion by expanding the lx_proc_data+t`l_clone_grps
+ * array.
+ */
+static int
+lx_clone_flag2grp(uint_t flag)
+{
+ if (flag & LX_CLONE_FS)
+ return (LX_CLGRP_FS);
+
+ return (-1);
+}
+
+/*
+ * Note: this function has the side effect of clearing the flags.
+ */
+static int
+lx_clone_flags_iter(uint_t *fp)
+{
+ if (*fp & LX_CLONE_FS) {
+ *fp &= ~LX_CLONE_FS;
+ return (LX_CLGRP_FS);
+ }
+
+ return (-1);
+}
+
+/*
+ * Setup the current process in the proper clone-group(s) and record the
+ * clone-group flags on the lwp so that we can join the child process to the
+ * group during lx_forklwp().
+ */
+void
+lx_clone_grp_create(uint_t flags)
+{
+ int offset;
+ lx_proc_data_t *plproc = ttolxproc(curthread);
+ lx_lwp_data_t *ldp = (lx_lwp_data_t *)ttolwp(curthread)->lwp_brand;
+ lx_clone_grp_t **cgps;
+ lx_clone_grp_t *cgp;
+ lx_clone_grp_member_t *mp;
+
+ if (!LX_IS_CLONE_GRP(flags))
+ return;
+
+ ldp->br_clone_grp_flags = flags & LX_CLONE_GRP_SUBSET;
+
+ cgps = plproc->l_clone_grps;
+ /*
+ * We take the top-level mutex during create to ensure we only create
+ * one group per flag.
+ */
+ mutex_enter(&plproc->l_clone_grp_lock);
+ while ((offset = lx_clone_flags_iter(&flags)) != -1) {
+ cgp = cgps[offset];
+
+ /*
+ * If we already havae a clone-group list for this flag then
+ * nothing to do.
+ */
+ if (cgp != NULL)
+ continue;
+
+ /*
+ * Create a new clone-group. If it ever becomes an issue, we
+ * could preallocate this memory before taking
+ * l_clone_grp_lock.
+ */
+ cgp = kmem_alloc(sizeof (lx_clone_grp_t), KM_SLEEP);
+ mutex_init(&cgp->lx_clgrp_lock, NULL, MUTEX_DEFAULT, NULL);
+ cgp->lx_clgrp_cnt = 1;
+ list_create(&cgp->lx_clgrp_members,
+ sizeof (lx_clone_grp_member_t),
+ offsetof(lx_clone_grp_member_t, lx_clgrpm_link));
+
+ mp = kmem_zalloc(sizeof (lx_clone_grp_member_t), KM_SLEEP);
+ mp->lx_clgrpm_pp = curproc;
+ list_insert_tail(&cgp->lx_clgrp_members, mp);
+
+ /* Attach group to our proc */
+ plproc->l_clone_grps[offset] = cgp;
+ }
+ mutex_exit(&plproc->l_clone_grp_lock);
+}
+
+/*
+ * Add the child process to the proper parent clone-group(s).
+ *
+ * Called from lx_forklwp, thus there is no need to have any locking for the
+ * destination proc. This is always run in the thread context of the source
+ * thread, and the destination thread is always newly created and not referred
+ * to from anywhere else. The source process should have already created the
+ * clone group(s) that we need to place the child into via lx_clone_grp_create.
+ */
+void
+lx_clone_grp_enter(uint_t flags, proc_t *srcp, proc_t *dstp)
+{
+ int offset;
+ lx_proc_data_t *plproc = ptolxproc(srcp);
+ lx_proc_data_t *clproc = ptolxproc(dstp);
+ lx_clone_grp_t **cgps;
+ lx_clone_grp_t *cgp;
+ lx_clone_grp_member_t *mp;
+
+ cgps = plproc->l_clone_grps;
+ while ((offset = lx_clone_flags_iter(&flags)) != -1) {
+ cgp = cgps[offset];
+
+ /*
+ * Parent should already have a clone-group list for this flag.
+ * The child joins that group.
+ */
+ VERIFY(cgp != NULL);
+
+ mp = kmem_zalloc(sizeof (lx_clone_grp_member_t), KM_SLEEP);
+ mp->lx_clgrpm_pp = dstp;
+
+ mutex_enter(&cgp->lx_clgrp_lock);
+ list_insert_tail(&cgp->lx_clgrp_members, mp);
+ cgp->lx_clgrp_cnt++;
+ clproc->l_clone_grps[offset] = cgp;
+ mutex_exit(&cgp->lx_clgrp_lock);
+ }
+}
+
+/*
+ * The process is exiting or we're exec-ing a native app. In the unlikely event
+ * it is in a clone-group, remove it from the group and perform any necessary
+ * cleanup. Normally we're called from lx_proc_exit(), so we know we're the
+ * last lwp in the process, but we can also be called from lx_clearbrand() when
+ * exec-ing a native application. In this case we know the lwp(s) are stopped
+ * (It is possible to have multiple lwps if we branded the process but the
+ * exec failed. Those lwps were just branded as part of the exec, and will
+ * be de-branded).
+ */
+void
+lx_clone_grp_exit(proc_t *p, boolean_t lwps_ok)
+{
+ int i;
+ lx_proc_data_t *plproc = ptolxproc(p);
+ lx_clone_grp_t **cgps;
+
+ ASSERT(!MUTEX_HELD(&p->p_lock));
+ ASSERT(plproc != NULL);
+
+ if (!lwps_ok)
+ VERIFY(p->p_lwpcnt <= 1);
+
+ cgps = plproc->l_clone_grps;
+ for (i = 0; i < LX_CLGRP_MAX; i++) {
+ lx_clone_grp_t *cgp;
+ lx_clone_grp_member_t *mp;
+ boolean_t found;
+
+ cgp = cgps[i];
+ if (cgp == NULL)
+ continue;
+
+ /*
+ * The rare case when this process belongs to a clone-group.
+ */
+
+ mutex_enter(&cgp->lx_clgrp_lock);
+
+ /* First remove ourselves from the group. */
+ found = B_FALSE;
+ mp = list_head(&cgp->lx_clgrp_members);
+ while (mp != NULL) {
+ if (mp->lx_clgrpm_pp == p) {
+ found = B_TRUE;
+ list_remove(&cgp->lx_clgrp_members, mp);
+ kmem_free(mp, sizeof (lx_clone_grp_member_t));
+ ASSERT(cgp->lx_clgrp_cnt > 0);
+ cgp->lx_clgrp_cnt--;
+ plproc->l_clone_grps[i] = NULL;
+ break;
+ }
+ mp = list_next(&cgp->lx_clgrp_members, mp);
+ }
+ VERIFY(found);
+
+ if (cgp->lx_clgrp_cnt > 0) {
+ mutex_exit(&cgp->lx_clgrp_lock);
+ continue;
+ }
+
+ /*
+ * cgp->lx_clgrp_cnt == 0
+ *
+ * We're the sole remaining member; finish cleanup now.
+ */
+ ASSERT(plproc->l_clone_grps[i] == NULL);
+ mutex_exit(&cgp->lx_clgrp_lock);
+
+ /* Delete the group since there are no more references to it. */
+ VERIFY(list_is_empty(&cgp->lx_clgrp_members));
+
+ list_destroy(&cgp->lx_clgrp_members);
+ mutex_destroy(&cgp->lx_clgrp_lock);
+ kmem_free(cgp, sizeof (lx_clone_grp_t));
+ }
+}
+
+/*
+ * Return true in the rare case that the process is a member of a clone group
+ * with the specific flag set. Clone groups are only added to the array
+ * atomically until this process exits, so we don't need to take
+ * l_clone_grp_lock.
+ */
+boolean_t
+lx_clone_grp_member(lx_proc_data_t *dp, uint_t flag)
+{
+ int offset;
+
+ if ((offset = lx_clone_flag2grp(flag)) == -1)
+ return (B_FALSE);
+
+ if (dp->l_clone_grps[offset] != NULL) {
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Walk all of the processes in the clone-group list and apply the callback
+ * to each. Because we're holding the group list lock (lx_clgrp_lock) none of
+ * the processes can exit, but that is the only locking guarantee made by this
+ * function itself.
+ */
+int
+lx_clone_grp_walk(lx_proc_data_t *dp, uint_t flag, int (*cb)(proc_t *, void *),
+ void *arg)
+{
+ int offset;
+ lx_clone_grp_t *cgp;
+ lx_clone_grp_member_t *mp;
+ int res, rv = 0;
+
+
+ ASSERT(dp != NULL);
+ /* We should not be called unless we belong to a group */
+ VERIFY((offset = lx_clone_flag2grp(flag)) != -1);
+ VERIFY(dp->l_clone_grps[offset] != NULL);
+
+ cgp = dp->l_clone_grps[offset];
+ mutex_enter(&cgp->lx_clgrp_lock);
+
+ mp = list_head(&cgp->lx_clgrp_members);
+ while (mp != NULL) {
+ res = cb(mp->lx_clgrpm_pp, arg);
+ /* return the first error we see, but try all procs */
+ if (res != 0 && rv == 0)
+ rv = res;
+ mp = list_next(&cgp->lx_clgrp_members, mp);
+ }
+
+ mutex_exit(&cgp->lx_clgrp_lock);
+
+ return (rv);
+}
+
+
+/*
+ * Our lwp has already been created at this point, so this routine is
+ * responsible for setting up all the state needed to track this as a
+ * linux cloned thread.
+ */
+/* ARGSUSED */
+int
+lx_helper_clone(int64_t *rval, int flags, void *ptidp, void *tls, void *ctidp)
+{
+ struct lx_lwp_data *lwpd = ttolxlwp(curthread);
+ struct lx_proc_data *lproc = ttolxproc(curthread);
+ struct ldt_info info;
+ struct user_desc descr;
+ int tls_index;
+ int entry = -1;
+ int signo;
+
+ signo = flags & LX_CSIGNAL;
+ if (signo < 0 || signo > LX_NSIG)
+ return (set_errno(EINVAL));
+
+ if (!(flags & LX_CLONE_THREAD)) {
+ lproc->l_signal = signo;
+ } else {
+ if (flags & LX_CLONE_SETTLS) {
+ if (get_udatamodel() == DATAMODEL_ILP32) {
+ if (copyin((caddr_t)tls, &info, sizeof (info)))
+ return (set_errno(EFAULT));
+
+ if (LDT_INFO_EMPTY(&info))
+ return (set_errno(EINVAL));
+
+ entry = info.entry_number;
+ if (entry < GDT_TLSMIN || entry > GDT_TLSMAX)
+ return (set_errno(EINVAL));
+
+ tls_index = entry - GDT_TLSMIN;
+
+ /*
+ * Convert the user-space structure into a real
+ * x86 descriptor and copy it into this LWP's
+ * TLS array. We also load it into the GDT.
+ */
+ LDT_INFO_TO_DESC(&info, &descr);
+ bcopy(&descr, &lwpd->br_tls[tls_index],
+ sizeof (descr));
+ lx_set_gdt(entry, &lwpd->br_tls[tls_index]);
+ } else {
+ /*
+ * Set the Linux %fsbase for this LWP. We will
+ * restore it the next time we return to Linux
+ * via setcontext()/lx_restorecontext().
+ */
+ lwpd->br_lx_fsbase = (uintptr_t)tls;
+ }
+ }
+
+ lwpd->br_clear_ctidp =
+ (flags & LX_CLONE_CHILD_CLEARTID) ? ctidp : NULL;
+
+ if (signo && ! (flags & LX_CLONE_DETACH))
+ lwpd->br_signal = signo;
+ else
+ lwpd->br_signal = 0;
+
+ if (flags & LX_CLONE_THREAD)
+ lwpd->br_tgid = curthread->t_procp->p_pid;
+
+ if (flags & LX_CLONE_PARENT)
+ lwpd->br_ppid = 0;
+
+ if ((flags & LX_CLONE_CHILD_SETTID) && (ctidp != NULL) &&
+ (suword32(ctidp, lwpd->br_pid) != 0)) {
+ if (entry >= 0)
+ lx_clear_gdt(entry);
+ return (set_errno(EFAULT));
+ }
+ if ((flags & LX_CLONE_PARENT_SETTID) && (ptidp != NULL) &&
+ (suword32(ptidp, lwpd->br_pid) != 0)) {
+ if (entry >= 0)
+ lx_clear_gdt(entry);
+ return (set_errno(EFAULT));
+ }
+ }
+
+ *rval = lwpd->br_pid;
+ return (0);
+}
+
+long
+lx_set_tid_address(int *tidp)
+{
+ struct lx_lwp_data *lwpd = ttolxlwp(curthread);
+ long rv;
+
+ lwpd->br_clear_ctidp = tidp;
+
+ if (curproc->p_pid == curproc->p_zone->zone_proc_initpid) {
+ rv = 1;
+ } else {
+ rv = lwpd->br_pid;
+ }
+
+ return (rv);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_close.c b/usr/src/uts/common/brand/lx/syscall/lx_close.c
new file mode 100644
index 0000000000..5d1a1605c1
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_close.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 2017 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/brand.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_syscalls.h>
+
+
+extern int close(int);
+
+long
+lx_close(int fdes)
+{
+ return (close(fdes));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_cpu.c b/usr/src/uts/common/brand/lx/syscall/lx_cpu.c
new file mode 100644
index 0000000000..b0a92394dc
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_cpu.c
@@ -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 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/cpuvar.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_impl.h>
+
+/*
+ * We support neither the second argument (NUMA node), nor the third (obsolete
+ * pre-2.6.24 caching functionality which was ultimately broken).
+ */
+/* ARGSUSED1 */
+long
+lx_getcpu(unsigned int *cpu, uintptr_t p2, uintptr_t p3)
+{
+ unsigned int curcpu = curthread->t_cpu->cpu_id;
+
+ if (copyout(&curcpu, cpu, sizeof (curcpu)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_dup.c b/usr/src/uts/common/brand/lx/syscall/lx_dup.c
new file mode 100644
index 0000000000..d0f513753c
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_dup.c
@@ -0,0 +1,53 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+
+/* From usr/src/uts/common/syscall/fcntl.c */
+extern int fcntl(int, int, intptr_t);
+
+long
+lx_dup(int fd)
+{
+ return (fcntl(fd, F_DUPFD, 0));
+}
+
+long
+lx_dup2(int oldfd, int newfd)
+{
+ return (fcntl(oldfd, F_DUP2FD, newfd));
+}
+
+long
+lx_dup3(int oldfd, int newfd, int flags)
+{
+ int rc;
+
+ /* The only valid flag is O_CLOEXEC. */
+ if (flags & ~LX_O_CLOEXEC)
+ return (set_errno(EINVAL));
+
+ /* Only DUP2FD_CLOEXEC returns EINVAL on the same fd's */
+ if (oldfd == newfd)
+ return (set_errno(EINVAL));
+
+ rc = fcntl(oldfd, (flags == 0) ? F_DUP2FD : F_DUP2FD_CLOEXEC, newfd);
+ return (rc);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_epoll.c b/usr/src/uts/common/brand/lx/syscall/lx_epoll.c
new file mode 100644
index 0000000000..47688dad6a
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_epoll.c
@@ -0,0 +1,303 @@
+/*
+ * 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 2017 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/epoll.h>
+#include <sys/devpoll.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/vnode.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/lx_signal.h>
+
+static major_t devpoll_major = 0;
+
+static boolean_t
+lx_epoll_isvalid(file_t *fp)
+{
+ vnode_t *vp = fp->f_vnode;
+
+ if (vp->v_type == VCHR && getmajor(vp->v_rdev) == devpoll_major)
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
+long
+lx_epoll_create1(int flags)
+{
+ int err, fd, rv;
+ int fmode = FREAD | FWRITE;
+ boolean_t cloexec = B_FALSE;
+ vnode_t *vp = NULL;
+ file_t *fp = NULL;
+
+ if (flags & EPOLL_CLOEXEC) {
+ cloexec = B_TRUE;
+ flags &= ~EPOLL_CLOEXEC;
+ }
+ if (flags != 0) {
+ /* No other flags accepted at this time */
+ return (set_errno(EINVAL));
+ }
+
+ if (falloc((vnode_t *)NULL, fmode, &fp, &fd) != 0) {
+ err = EMFILE;
+ goto error;
+ }
+ if (ldi_vp_from_name("/devices/pseudo/poll@0:poll", &vp) != 0) {
+ err = ENOENT;
+ goto error;
+ }
+ if ((err = VOP_OPEN(&vp, fmode | FKLYR, CRED(), NULL)) != 0) {
+ goto error;
+ }
+ err = VOP_IOCTL(vp, DP_EPOLLCOMPAT, 0, fmode, CRED(), &rv, NULL);
+ if (err != 0) {
+ (void) VOP_CLOSE(vp, fmode, 0, 0, CRED(), NULL);
+ goto error;
+ }
+
+ devpoll_major = getmajor(vp->v_rdev);
+
+ fp->f_vnode = vp;
+ mutex_exit(&fp->f_tlock);
+ setf(fd, fp);
+ if (cloexec) {
+ f_setfd(fd, FD_CLOEXEC);
+ }
+ return (fd);
+
+error:
+ if (fp != NULL) {
+ setf(fd, NULL);
+ unfalloc(fp);
+ }
+ if (vp != NULL) {
+ VN_RELE(vp);
+ }
+ return (set_errno(err));
+}
+
+long
+lx_epoll_create(int size)
+{
+ if (size <= 0) {
+ return (set_errno(EINVAL));
+ }
+
+ return (lx_epoll_create1(0));
+}
+
+
+/* Match values from libc implementation */
+#define EPOLLIGNORED (EPOLLMSG | EPOLLWAKEUP)
+#define EPOLLSWIZZLED \
+ (EPOLLRDHUP | EPOLLONESHOT | EPOLLET | EPOLLWRBAND | EPOLLWRNORM)
+#define EPOLL_TIMEOUT_CLAMP(t) (((t) < -1) ? -1 : (t))
+
+long
+lx_epoll_ctl(int fd, int op, int pfd, void *event)
+{
+ epoll_event_t epevent;
+ dvpoll_epollfd_t dpevent[2];
+ file_t *fp;
+ iovec_t aiov;
+ uio_t auio;
+ uint32_t events, ev = 0;
+ int error = 0, i = 0;
+
+ dpevent[i].dpep_pollfd.fd = pfd;
+ switch (op) {
+ case EPOLL_CTL_DEL:
+ dpevent[i].dpep_pollfd.events = POLLREMOVE;
+ break;
+
+ case EPOLL_CTL_MOD:
+ /*
+ * In the modify case, we pass down two events: one to
+ * remove the event and another to add it back.
+ */
+ dpevent[i++].dpep_pollfd.events = POLLREMOVE;
+ dpevent[i].dpep_pollfd.fd = pfd;
+ /* FALLTHROUGH */
+
+ case EPOLL_CTL_ADD:
+ if (copyin(event, &epevent, sizeof (epevent)) != 0)
+ return (set_errno(EFAULT));
+
+ /*
+ * Mask off the events that we ignore, and then swizzle the
+ * events for which our values differ from their epoll(7)
+ * equivalents.
+ */
+ events = epevent.events;
+ ev = events & ~(EPOLLIGNORED | EPOLLSWIZZLED);
+
+ if (events & EPOLLRDHUP)
+ ev |= POLLRDHUP;
+ if (events & EPOLLET)
+ ev |= POLLET;
+ if (events & EPOLLONESHOT)
+ ev |= POLLONESHOT;
+ if (events & EPOLLWRNORM)
+ ev |= POLLWRNORM;
+ if (events & EPOLLWRBAND)
+ ev |= POLLWRBAND;
+
+ dpevent[i].dpep_data = epevent.data.u64;
+ dpevent[i].dpep_pollfd.events = ev;
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ } else if (!lx_epoll_isvalid(fp)) {
+ releasef(fd);
+ return (set_errno(EINVAL));
+ }
+
+ aiov.iov_base = (void *)dpevent;
+ aiov.iov_len = sizeof (dvpoll_epollfd_t) * (i + 1);
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = aiov.iov_len;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_loffset = 0;
+ auio.uio_fmode = fp->f_flag;
+
+ error = VOP_WRITE(fp->f_vnode, &auio, 1, fp->f_cred, NULL);
+
+ releasef(fd);
+
+ switch (error) {
+ case 0:
+ return (0);
+
+ case EBADF:
+ case EEXIST:
+ case EINVAL:
+ case ENOENT:
+ case ENOMEM:
+ case ENOSPC:
+ case EPERM:
+ /*
+ * Legal errors should pass straight through.
+ */
+ return (set_errno(error));
+
+ case ELOOP:
+ /*
+ * In the case of descriptor loops, /dev/poll emits a more
+ * descriptive error than Linux epoll consumers would expect.
+ */
+ return (set_errno(EINVAL));
+
+ default:
+ /*
+ * While devpoll itself should not emit unexpected errors, it
+ * is possible that a VOP_POLL handler might. There is little
+ * choice but to map these unexpected errors to something which
+ * is valid for epoll_ctl.
+ */
+ return (set_errno(ENOMEM));
+ }
+}
+
+long
+lx_epoll_wait(int fd, void *events, int maxevents, int timeout)
+{
+ struct dvpoll arg;
+ file_t *fp;
+ int rv = 0, error, flag;
+
+ if (maxevents <= 0) {
+ return (set_errno(EINVAL));
+ }
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ } else if (!lx_epoll_isvalid(fp)) {
+ releasef(fd);
+ return (set_errno(EINVAL));
+ }
+
+ arg.dp_nfds = maxevents;
+ arg.dp_timeout = EPOLL_TIMEOUT_CLAMP(timeout);
+ arg.dp_fds = (pollfd_t *)events;
+ flag = fp->f_flag | DATAMODEL_NATIVE | FKIOCTL;
+ error = VOP_IOCTL(fp->f_vnode, DP_POLL, (uintptr_t)&arg, flag,
+ fp->f_cred, &rv, NULL);
+
+ releasef(fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (rv);
+}
+
+long
+lx_epoll_pwait(int fd, void *events, int maxevents, int timeout, void *sigmask)
+{
+ struct dvpoll arg;
+ file_t *fp;
+ int rv = 0, error, flag;
+ k_sigset_t ksig;
+
+ if (maxevents <= 0) {
+ return (set_errno(EINVAL));
+ }
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ } else if (!lx_epoll_isvalid(fp)) {
+ releasef(fd);
+ return (set_errno(EINVAL));
+ }
+ if (sigmask != NULL) {
+ lx_sigset_t lsig;
+
+ if (copyin(sigmask, &lsig, sizeof (lsig)) != 0) {
+ releasef(fd);
+ return (set_errno(EFAULT));
+ }
+ lx_ltos_sigset(&lsig, &ksig);
+ arg.dp_setp = (sigset_t *)&ksig;
+ } else {
+ arg.dp_setp = NULL;
+ }
+
+ arg.dp_nfds = maxevents;
+ arg.dp_timeout = EPOLL_TIMEOUT_CLAMP(timeout);
+ arg.dp_fds = (pollfd_t *)events;
+ flag = fp->f_flag | DATAMODEL_NATIVE | FKIOCTL;
+ error = VOP_IOCTL(fp->f_vnode, DP_PPOLL, (uintptr_t)&arg, flag,
+ fp->f_cred, &rv, NULL);
+
+ releasef(fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (rv);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_eventfd.c b/usr/src/uts/common/brand/lx/syscall/lx_eventfd.c
new file mode 100644
index 0000000000..21205aa18a
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_eventfd.c
@@ -0,0 +1,126 @@
+/*
+ * 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 2017 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/vnode.h>
+#include <sys/eventfd.h>
+
+static major_t eventfd_major = 0;
+
+/* io_submit uses this to validate control block eventfd descriptors */
+boolean_t
+lx_is_eventfd(file_t *fp)
+{
+ vnode_t *vp = fp->f_vnode;
+
+ if (vp->v_type == VCHR && getmajor(vp->v_rdev) == eventfd_major)
+ return (B_TRUE);
+ return (B_FALSE);
+}
+
+long
+lx_eventfd2(uint_t initval, int flags)
+{
+ int err, fd;
+ int fmode = FREAD | FWRITE;
+ vnode_t *vp = NULL;
+ file_t *fp = NULL;
+
+ if (flags & ~(EFD_NONBLOCK | EFD_CLOEXEC | EFD_SEMAPHORE))
+ return (set_errno(EINVAL));
+
+ if (flags & EFD_NONBLOCK)
+ fmode |= FNONBLOCK;
+
+ if (falloc((vnode_t *)NULL, fmode, &fp, &fd) != 0)
+ return (set_errno(EMFILE));
+
+ if (ldi_vp_from_name("/dev/eventfd", &vp) != 0) {
+ /*
+ * If /dev/eventfd is not available then it is less jarring to
+ * Linux programs to tell them that the system call is not
+ * supported instead of reporting an error (ENOENT) they are
+ * not expecting.
+ */
+ err = ENOTSUP;
+ goto error;
+ }
+ if ((err = VOP_OPEN(&vp, fmode | FKLYR, CRED(), NULL)) != 0) {
+ VN_RELE(vp);
+ vp = NULL;
+ goto error;
+ }
+
+ if (flags & EFD_SEMAPHORE) {
+ int rv;
+
+ if ((err = VOP_IOCTL(vp, EVENTFDIOC_SEMAPHORE, 0, fmode, CRED(),
+ &rv, NULL)) != 0)
+ goto error;
+ }
+
+ if (initval != 0) {
+ uint64_t val = initval;
+ struct uio auio;
+ struct iovec aiov;
+
+ /* write initial value */
+ aiov.iov_base = (caddr_t)&val;
+ aiov.iov_len = sizeof (val);
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = 0;
+ auio.uio_offset = 0;
+ auio.uio_resid = sizeof (val);
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_fmode = FWRITE;
+
+ if ((err = VOP_WRITE(vp, &auio, FWRITE, CRED(), NULL)) != 0)
+ goto error;
+ }
+
+ eventfd_major = getmajor(vp->v_rdev);
+
+ fp->f_vnode = vp;
+ mutex_exit(&fp->f_tlock);
+ setf(fd, fp);
+ if (flags & EFD_CLOEXEC) {
+ f_setfd(fd, FD_CLOEXEC);
+ }
+ return (fd);
+
+error:
+ if (fp != NULL) {
+ setf(fd, NULL);
+ unfalloc(fp);
+ }
+ if (vp != NULL) {
+ (void) VOP_CLOSE(vp, fmode, 0, 0, CRED(), NULL);
+ VN_RELE(vp);
+ }
+ return (set_errno(err));
+}
+
+long
+lx_eventfd(uint_t val)
+{
+ return (lx_eventfd2(val, 0));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_fadvise.c b/usr/src/uts/common/brand/lx/syscall/lx_fadvise.c
new file mode 100644
index 0000000000..61f9b936f2
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_fadvise.c
@@ -0,0 +1,103 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/lx_misc.h>
+
+/*
+ * Based on illumos posix_fadvise which does nothing. The only difference is
+ * that on Linux an fd refering to a pipe or FIFO returns EINVAL. The Linux
+ * POSIX_FADV_* values are the same as the illumos values. See how the 32-bit
+ * glibc calls fadvise64; the offeset is a 64-bit value, but the length is not.
+ * fadvise64_64 passes both the offset and length as 64-bit values. The 64-bit
+ * fadvise64 caller always passes 64-bit values for the offset and length.
+ */
+
+/*
+ * This is the fadvise64 function used by 64-bit callers, and by 32-bit callers
+ * after they have adjusted their arguments.
+ */
+/* ARGSUSED */
+int
+lx_fadvise64(int fd, off64_t offset, off64_t len, int advice)
+{
+ file_t *fp;
+ boolean_t is_fifo;
+
+ switch (advice) {
+ case POSIX_FADV_NORMAL:
+ case POSIX_FADV_RANDOM:
+ case POSIX_FADV_SEQUENTIAL:
+ case POSIX_FADV_WILLNEED:
+ case POSIX_FADV_DONTNEED:
+ case POSIX_FADV_NOREUSE:
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ if (len < 0)
+ return (set_errno(EINVAL));
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+ is_fifo = (fp->f_vnode->v_type == VFIFO);
+ releasef(fd);
+
+ if (is_fifo)
+ return (set_errno(ESPIPE));
+
+ return (0);
+}
+
+/*
+ * This is the fadvise64 function used by 32-bit callers. Linux passes the
+ * 64-bit offset by concatenating consecutive arguments. We must perform the
+ * same conversion here.
+ */
+long
+lx_fadvise64_32(int fd, uint32_t off_lo, uint32_t off_hi, int32_t len,
+ int advice)
+{
+ off64_t offset;
+
+ offset = off_hi;
+ offset = offset << 32;
+ offset |= off_lo;
+
+ return (lx_fadvise64(fd, offset, (off64_t)len, advice));
+}
+
+/*
+ * This function is only used by 32-bit callers. Linux passes the 64-bit offset
+ * and length by concatenating consecutive arguments. We must perform the same
+ * conversion here.
+ */
+long
+lx_fadvise64_64(int fd, uint32_t off_lo, uint32_t off_hi, uint32_t len_lo,
+ uint32_t len_hi, int advice)
+{
+ off64_t offset;
+ off64_t len;
+
+ offset = off_hi;
+ offset = offset << 32;
+ offset |= off_lo;
+ len = len_hi;
+ len = len << 32;
+ len |= len_lo;
+
+ return (lx_fadvise64(fd, offset, len, advice));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_fallocate.c b/usr/src/uts/common/brand/lx/syscall/lx_fallocate.c
new file mode 100644
index 0000000000..338e4399fe
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_fallocate.c
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+#include <sys/systm.h>
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/nbmlock.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+#include <sys/sdt.h>
+
+extern int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
+
+#define LX_FALLOC_FL_KEEP_SIZE 0x01
+#define LX_FALLOC_FL_PUNCH_HOLE 0x02
+#define LX_FALLOC_FL_NO_HIDE_STALE 0x04
+#define LX_FALLOC_FL_COLLAPSE_RANGE 0x08
+#define LX_FALLOC_FL_ZERO_RANGE 0x10
+
+#define LX_FALLOC_VALID (LX_FALLOC_FL_KEEP_SIZE | LX_FALLOC_FL_PUNCH_HOLE | \
+ LX_FALLOC_FL_NO_HIDE_STALE | LX_FALLOC_FL_COLLAPSE_RANGE | \
+ LX_FALLOC_FL_ZERO_RANGE)
+
+#define LX_FALLOC_UNSUPP (LX_FALLOC_FL_NO_HIDE_STALE | \
+ LX_FALLOC_FL_COLLAPSE_RANGE)
+
+long
+lx_fallocate(int fd, int mode, off_t offset, off_t len)
+{
+ int error = 0;
+ file_t *fp;
+ vnode_t *vp;
+ int64_t tot;
+ struct flock64 bf;
+ vattr_t vattr;
+ u_offset_t f_offset;
+ boolean_t in_crit = B_FALSE;
+
+ /*
+ * Error checking is in a specific order to make LTP happy.
+ */
+
+ tot = offset + len;
+ if (tot > (LLONG_MAX / (int64_t)1024))
+ return (set_errno(EFBIG));
+
+ if (mode & LX_FALLOC_UNSUPP)
+ return (set_errno(EOPNOTSUPP));
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ if ((fp->f_flag & FWRITE) == 0) {
+ error = EBADF;
+ goto done;
+ }
+
+ vp = fp->f_vnode;
+ if (vp->v_type != VREG) {
+ error = EINVAL;
+ goto done;
+ }
+
+ if (offset < 0 || len <= 0) {
+ error = EINVAL;
+ goto done;
+ }
+
+ if (tot < 0LL) {
+ error = EFBIG;
+ goto done;
+ }
+
+ if ((mode & ~LX_FALLOC_VALID) != 0) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /*
+ * If this is the only flag then we don't actually do any work.
+ */
+ if (mode == LX_FALLOC_FL_KEEP_SIZE)
+ goto done;
+
+ bzero(&bf, sizeof (bf));
+
+ vattr.va_mask = AT_SIZE;
+ if ((error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) != 0)
+ goto done;
+
+ if (mode == 0) {
+ /* Nothing to do if not extending the file */
+ if (vattr.va_size >= tot)
+ goto done;
+
+ /* Extend the file. */
+ bf.l_start = (off64_t)tot;
+ bf.l_len = (off64_t)0;
+
+ } else if (mode & LX_FALLOC_FL_PUNCH_HOLE) {
+ /*
+ * Deallocate space in the file.
+ */
+ if ((mode & LX_FALLOC_FL_KEEP_SIZE) == 0) {
+ /* this flag is required with punch hole */
+ error = EINVAL;
+ goto done;
+ }
+
+ if (mode &
+ ~(LX_FALLOC_FL_PUNCH_HOLE | LX_FALLOC_FL_KEEP_SIZE)) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /* Make sure we don't extend since keep_size is set. */
+ if (vattr.va_size < tot) {
+ if (offset > vattr.va_size)
+ goto done;
+ len = (off_t)vattr.va_size - offset;
+ }
+
+ bf.l_start = (off64_t)offset;
+ bf.l_len = (off64_t)len;
+
+ } else if (mode & LX_FALLOC_FL_ZERO_RANGE) {
+ /*
+ * Zero out the space in the file.
+ */
+ if (mode &
+ ~(LX_FALLOC_FL_ZERO_RANGE | LX_FALLOC_FL_KEEP_SIZE)) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /* Make sure we don't extend when keep_size is set. */
+ if (mode & LX_FALLOC_FL_KEEP_SIZE && vattr.va_size < tot) {
+ if (offset > vattr.va_size)
+ goto done;
+ len = vattr.va_size - offset;
+ }
+
+ bf.l_start = (off64_t)offset;
+ bf.l_len = (off64_t)len;
+ } else {
+ /* We should have already handled all flags */
+ VERIFY(0);
+ }
+
+ /*
+ * Check for locks in the range.
+ */
+ f_offset = fp->f_offset;
+ error = flock_check(vp, &bf, f_offset, MAXOFF_T);
+ if (error != 0)
+ goto done;
+
+ /*
+ * Check for conflicting non-blocking mandatory locks.
+ * We need to get the size again under nbl_start_crit.
+ */
+ if (nbl_need_check(vp)) {
+ u_offset_t begin;
+ ssize_t length;
+
+ nbl_start_crit(vp, RW_READER);
+ in_crit = B_TRUE;
+ vattr.va_mask = AT_SIZE;
+ if ((error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) != 0)
+ goto done;
+
+ /*
+ * Make sure we don't extend when keep_size is set.
+ */
+ if (mode & LX_FALLOC_FL_KEEP_SIZE && vattr.va_size < tot) {
+ ASSERT(mode & (LX_FALLOC_FL_PUNCH_HOLE |
+ LX_FALLOC_FL_ZERO_RANGE));
+
+ /*
+ * If the size grew we can short-circuit the rest of
+ * the work, otherwise adjust bf for the vop_space
+ * call.
+ */
+ if (offset >= vattr.va_size)
+ goto done;
+ len = vattr.va_size - offset;
+ bf.l_len = (off64_t)len;
+ }
+
+ if (offset > vattr.va_size) {
+ begin = vattr.va_size;
+ length = offset - vattr.va_size;
+ } else {
+ begin = offset;
+ length = vattr.va_size - offset;
+ }
+
+ if (nbl_conflict(vp, NBL_WRITE, begin, length, 0, NULL)) {
+ error = EACCES;
+ goto done;
+ }
+ }
+
+ error = VOP_SPACE(vp, F_FREESP, &bf, 0, f_offset, fp->f_cred, NULL);
+
+done:
+ if (in_crit)
+ nbl_end_crit(vp);
+
+ releasef(fd);
+ if (error != 0)
+ return (set_errno(error));
+
+ return (0);
+}
+
+long
+lx_fallocate32(int fd, int mode, uint32_t offl, uint32_t offh, uint32_t lenl,
+ uint32_t lenh)
+{
+ int64_t offset = 0, len = 0;
+
+ /*
+ * From 32-bit callers, Linux passes the 64-bit offset and len by
+ * concatenating consecutive arguments. We must perform the same
+ * conversion here.
+ */
+ offset = offh;
+ offset = offset << 32;
+ offset |= offl;
+ len = lenh;
+ len = len << 32;
+ len |= lenl;
+
+ return (lx_fallocate(fd, mode, (off_t)offset, (off_t)len));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_fcntl.c b/usr/src/uts/common/brand/lx/syscall/lx_fcntl.c
new file mode 100644
index 0000000000..a5406c0a4f
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_fcntl.c
@@ -0,0 +1,701 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/cmn_err.h>
+#include <sys/pathname.h>
+#include <sys/policy.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_socket.h>
+#include <sys/brand.h>
+#include <sys/fs/fifonode.h>
+#include <sys/strsubr.h>
+#include <sys/stream.h>
+#include <sys/flock.h>
+
+extern int fcntl(int, int, intptr_t);
+extern int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
+extern int lx_pipe_setsz(stdata_t *, uint_t, boolean_t);
+
+
+int
+lx_vp_at(int fd, char *upath, vnode_t **vpp, int flag)
+{
+ vnode_t *startvp;
+ int error;
+
+ if (fd == LX_AT_FDCWD) {
+ fd = AT_FDCWD;
+ }
+
+ if ((error = fgetstartvp(fd, upath, &startvp)) != 0) {
+ return (error);
+ }
+
+ if (upath != NULL) {
+ uio_seg_t seg = UIO_USERSPACE;
+
+ error = lookupnameat(upath, seg,
+ (flag == AT_SYMLINK_NOFOLLOW) ? NO_FOLLOW : FOLLOW,
+ NULLVPP, vpp, startvp);
+ if (startvp != NULL) {
+ VN_RELE(startvp);
+ }
+ return (error);
+ } else {
+ /* VN_HOLD was established in fgetstartvp */
+ *vpp = startvp;
+ VERIFY(*vpp);
+ return (0);
+ }
+}
+
+#define LTOS_FLOCK(l, s) \
+{ \
+ s->l_type = ltos_type(l->l_type); \
+ s->l_whence = l->l_whence; \
+ s->l_start = l->l_start; \
+ s->l_len = l->l_len; \
+ s->l_sysid = 0; /* not defined in linux */ \
+ s->l_pid = (pid_t)l->l_pid; \
+}
+
+#define STOL_FLOCK(s, l) \
+{ \
+ l->l_type = stol_type(s->l_type); \
+ l->l_whence = s->l_whence; \
+ l->l_start = s->l_start; \
+ l->l_len = s->l_len; \
+ l->l_pid = (int)s->l_pid; \
+}
+
+static short
+ltos_type(short l_type)
+{
+ switch (l_type) {
+ case LX_F_RDLCK:
+ return (F_RDLCK);
+ case LX_F_WRLCK:
+ return (F_WRLCK);
+ case LX_F_UNLCK:
+ return (F_UNLCK);
+ default:
+ return (-1);
+ }
+}
+
+static short
+stol_type(short l_type)
+{
+ switch (l_type) {
+ case F_RDLCK:
+ return (LX_F_RDLCK);
+ case F_WRLCK:
+ return (LX_F_WRLCK);
+ case F_UNLCK:
+ return (LX_F_UNLCK);
+ default:
+ /* can't ever happen */
+ return (0);
+ }
+}
+
+static void
+ltos_flock(struct lx_flock *l, struct flock64 *s)
+{
+ LTOS_FLOCK(l, s)
+}
+
+static void
+stol_flock(struct flock64 *s, struct lx_flock *l)
+{
+ STOL_FLOCK(s, l)
+}
+
+static void
+ltos_flock64(struct lx_flock64_32 *l, struct flock64 *s)
+{
+ LTOS_FLOCK(l, s)
+}
+
+static void
+stol_flock64(struct flock64 *s, struct lx_flock64_32 *l)
+{
+ STOL_FLOCK(s, l)
+}
+
+static int
+lx_fcntl_getfl(int fd)
+{
+ int retval;
+ int rc;
+
+ retval = fcntl(fd, F_GETFL, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+
+ if ((retval & O_ACCMODE) == O_RDONLY)
+ rc = LX_O_RDONLY;
+ else if ((retval & O_ACCMODE) == O_WRONLY)
+ rc = LX_O_WRONLY;
+ else
+ rc = LX_O_RDWR;
+ /* O_NDELAY != O_NONBLOCK, so we need to check for both */
+ if (retval & O_NDELAY)
+ rc |= LX_O_NDELAY;
+ if (retval & O_NONBLOCK)
+ rc |= LX_O_NONBLOCK;
+ if (retval & O_APPEND)
+ rc |= LX_O_APPEND;
+ if (retval & O_SYNC)
+ rc |= LX_O_SYNC;
+ if (retval & O_LARGEFILE)
+ rc |= LX_O_LARGEFILE;
+ if (retval & FASYNC)
+ rc |= LX_O_ASYNC;
+
+ return (rc);
+}
+
+#define LX_SETFL_MASK (O_NONBLOCK | O_APPEND | O_SYNC | FASYNC);
+
+static int
+lx_fcntl_setfl(int fd, ulong_t arg)
+{
+ int flags;
+
+ /*
+ * When performing fcntl(F_SETFL), only certain flags are
+ * allowed to be manipulated. A mask is used to preserve
+ * other flags, such as those which are specified during
+ * open(2). The mask on Linux excludes O_LARGEFILE from
+ * being manipulated, whereas illumos expects the flag to
+ * be set. In order to properly preserve the O_LARGEFILE
+ * (FOFFMAX) state, we must first query for it via
+ * fcntl(F_GETFL) so that the value can be carried
+ * through.
+ */
+ flags = fcntl(fd, F_GETFL, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+
+ flags &= ~LX_SETFL_MASK;
+
+ /* LX_O_NDELAY == LX_O_NONBLOCK, so we only check for one */
+ if (arg & LX_O_NDELAY)
+ flags |= O_NONBLOCK;
+ if (arg & LX_O_APPEND)
+ flags |= O_APPEND;
+ if (arg & LX_O_SYNC)
+ flags |= O_SYNC;
+ if (arg & LX_O_ASYNC)
+ flags |= FASYNC;
+
+ return (fcntl(fd, F_SETFL, flags));
+}
+
+
+static int
+lx_fcntl_pipesz(int fd, int cmd, ulong_t arg)
+{
+ file_t *fp;
+ vnode_t *vp;
+ stdata_t *str;
+ int err = 0, res = 0;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ vp = fp->f_vnode;
+ if (vp->v_type != VFIFO || vp->v_op != fifo_vnodeops) {
+ err = EBADF;
+ goto out;
+ }
+ VERIFY((str = vp->v_stream) != NULL);
+
+ if (cmd == LX_F_SETPIPE_SZ) {
+ err = lx_pipe_setsz(str, (uint_t)arg, B_FALSE);
+ } else if (cmd == LX_F_GETPIPE_SZ) {
+ size_t val;
+
+ err = strqget(RD(str->sd_wrq), QHIWAT, 0, &val);
+ res = val;
+ } else {
+ /* NOTREACHED */
+ ASSERT(0);
+ }
+
+out:
+ releasef(fd);
+ if (err != 0) {
+ return (set_errno(err));
+ }
+ return (res);
+}
+
+static int
+lx_fcntl_common(int fd, int cmd, ulong_t arg)
+{
+ int rc = 0;
+ pid_t pid;
+ int error;
+ int rv;
+ int32_t flag;
+ file_t *fp;
+
+ /*
+ * We depend on the call to fcntl to set the errno if necessary.
+ */
+ ttolwp(curthread)->lwp_errno = 0;
+
+ switch (cmd) {
+ case LX_F_SETSIG:
+ case LX_F_GETSIG:
+ case LX_F_SETLEASE:
+ case LX_F_GETLEASE:
+ case LX_F_NOTIFY:
+ case LX_F_CANCELLK:
+ {
+ char buf[80];
+
+ (void) snprintf(buf, sizeof (buf),
+ "unsupported fcntl command: %d", cmd);
+ lx_unsupported(buf);
+ }
+ return (set_errno(ENOTSUP));
+
+ case LX_F_DUPFD:
+ rc = fcntl(fd, F_DUPFD, arg);
+ break;
+
+ case LX_F_DUPFD_CLOEXEC:
+ rc = fcntl(fd, F_DUPFD_CLOEXEC, arg);
+ break;
+
+ case LX_F_GETFD:
+ rc = fcntl(fd, F_GETFD, 0);
+ break;
+
+ case LX_F_SETFD:
+ rc = fcntl(fd, F_SETFD, arg);
+ break;
+
+ case LX_F_GETFL:
+ rc = lx_fcntl_getfl(fd);
+ break;
+
+ case LX_F_SETFL:
+ rc = lx_fcntl_setfl(fd, arg);
+ break;
+
+ case LX_F_SETOWN:
+ pid = (pid_t)arg;
+ if (pid == 1) {
+ /* Setown for the init process uses the real pid. */
+ pid = curzone->zone_proc_initpid;
+ }
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ rv = 0;
+
+ flag = fp->f_flag | get_udatamodel() | FKIOCTL;
+ error = VOP_IOCTL(fp->f_vnode, FIOSETOWN, (intptr_t)&pid,
+ flag, CRED(), &rv, NULL);
+ releasef(fd);
+ if (error != 0) {
+ /*
+ * On illumos F_SETOWN is only defined for sockets, but
+ * some apps hardcode to do this fcntl on other devices
+ * (e.g. /dev/tty) to setup signal handling. If the
+ * app is only setting itself to be the signal
+ * handler, we pretend to succeed.
+ */
+ if (error != EINVAL ||
+ curthread->t_procp->p_pid != pid) {
+ return (set_errno(error));
+ }
+ }
+
+ rc = 0;
+ break;
+
+ case LX_F_GETOWN:
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ rv = 0;
+
+ flag = fp->f_flag | get_udatamodel() | FKIOCTL;
+ error = VOP_IOCTL(fp->f_vnode, FIOGETOWN, (intptr_t)&pid,
+ flag, CRED(), &rv, NULL);
+ releasef(fd);
+ if (error != 0)
+ return (set_errno(error));
+
+ if (pid == curzone->zone_proc_initpid) {
+ /* Getown for the init process returns 1. */
+ pid = 1;
+ }
+
+ rc = pid;
+ break;
+
+ case LX_F_SETPIPE_SZ:
+ case LX_F_GETPIPE_SZ:
+ rc = lx_fcntl_pipesz(fd, cmd, arg);
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ return (rc);
+}
+
+static int
+lx_fcntl_lock_cmd_to_s(int lx_cmd)
+{
+ switch (lx_cmd) {
+ case LX_F_GETLK:
+ return (F_GETLK);
+ case LX_F_SETLK:
+ return (F_SETLK);
+ case LX_F_SETLKW:
+ return (F_SETLKW);
+ case LX_F_GETLK64:
+ return (F_GETLK64);
+ case LX_F_SETLK64:
+ return (F_SETLK64);
+ case LX_F_SETLKW64:
+ return (F_SETLKW64);
+ default:
+ VERIFY(0);
+ /*NOTREACHED*/
+ return (0);
+ }
+}
+
+/*
+ * This is a pain but we can't re-use the fcntl code for locking since it does
+ * its own copyin/copyout for the flock struct. Since we have to convert the
+ * struct we have to do our own copyin/out. Thus we replicate the fcntl code for
+ * these 3 cmds. Luckily it's not much.
+ */
+static int
+lx_fcntl_lock(int fd, int lx_cmd, void *arg)
+{
+ int cmd;
+ int error = 0;
+ file_t *fp;
+ vnode_t *vp;
+ int flag;
+ offset_t maxoffset;
+ u_offset_t offset;
+ model_t datamodel;
+ lx_flock_t lxflk;
+ lx_flock64_32_t lxflk64;
+ struct flock64 bf;
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ maxoffset = MAXOFF_T;
+ datamodel = DATAMODEL_NATIVE;
+#if defined(_SYSCALL32_IMPL)
+ if ((datamodel = get_udatamodel()) == DATAMODEL_ILP32)
+ maxoffset = MAXOFF32_T;
+#endif
+ vp = fp->f_vnode;
+ flag = fp->f_flag;
+ offset = fp->f_offset;
+
+ cmd = lx_fcntl_lock_cmd_to_s(lx_cmd);
+
+ switch (cmd) {
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+ if (datamodel == DATAMODEL_NATIVE) {
+ if (copyin(arg, &lxflk, sizeof (lx_flock_t)) != 0) {
+ error = EFAULT;
+ break;
+ }
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ lx_flock32_t lxflk32;
+
+ if (copyin(arg, &lxflk32, sizeof (lxflk32)) != 0) {
+ error = EFAULT;
+ break;
+ }
+
+ lxflk.l_type = lxflk32.l_type;
+ lxflk.l_whence = lxflk32.l_whence;
+ lxflk.l_start = (off64_t)lxflk32.l_start;
+ lxflk.l_len = (off64_t)lxflk32.l_len;
+ lxflk.l_pid = lxflk32.l_pid;
+ }
+#endif /* _SYSCALL32_IMPL */
+
+ ltos_flock(&lxflk, &bf);
+
+ if ((error = flock_check(vp, &bf, offset, maxoffset)) != 0)
+ break;
+
+ if ((error = VOP_FRLOCK(vp, cmd, &bf, flag, offset, NULL,
+ fp->f_cred, NULL)) != 0) {
+ if (cmd == F_SETLKW && error == EINTR) {
+ ttolxlwp(curthread)->br_syscall_restart =
+ B_TRUE;
+ }
+ break;
+ }
+
+ if (cmd != F_GETLK)
+ break;
+
+ /*
+ * The command is GETLK, return result.
+ */
+ stol_flock(&bf, &lxflk);
+
+ /*
+ * If no lock is found, only the type field is changed.
+ */
+ if (lxflk.l_type == LX_F_UNLCK) {
+ /* l_type always first entry, always a short */
+ if (copyout(&lxflk.l_type, &((lx_flock_t *)arg)->l_type,
+ sizeof (lxflk.l_type)))
+ error = EFAULT;
+ break;
+ }
+
+ if (bf.l_start > maxoffset || bf.l_len > maxoffset) {
+ error = EOVERFLOW;
+ break;
+ }
+
+ if (datamodel == DATAMODEL_NATIVE) {
+ if (copyout(&lxflk, arg, sizeof (lxflk)) != 0) {
+ error = EFAULT;
+ break;
+ }
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ lx_flock32_t lxflk32;
+
+ if (bf.l_start > MAXOFF32_T || bf.l_len > MAXOFF32_T) {
+ error = EOVERFLOW;
+ break;
+ }
+
+ lxflk32.l_type = lxflk.l_type;
+ lxflk32.l_whence = lxflk.l_whence;
+ lxflk32.l_start = lxflk.l_start;
+ lxflk32.l_len = lxflk.l_len;
+ lxflk32.l_pid = lxflk.l_pid;
+
+ if (copyout(&lxflk32, arg, sizeof (lxflk32)) != 0) {
+ error = EFAULT;
+ break;
+ }
+ }
+#endif /* _SYSCALL32_IMPL */
+ break;
+
+ case F_GETLK64:
+ case F_SETLK64:
+ case F_SETLKW64:
+ /*
+ * Large File support is only used for ILP32 apps.
+ */
+ if (datamodel != DATAMODEL_ILP32) {
+ error = EINVAL;
+ break;
+ }
+
+ if (cmd == F_GETLK64)
+ cmd = F_GETLK;
+ else if (cmd == F_SETLK64)
+ cmd = F_SETLK;
+ else if (cmd == F_SETLKW64)
+ cmd = F_SETLKW;
+
+ if (copyin(arg, &lxflk64, sizeof (lxflk64)) != 0) {
+ error = EFAULT;
+ break;
+ }
+
+ ltos_flock64(&lxflk64, &bf);
+
+ if ((error = flock_check(vp, &bf, offset, MAXOFFSET_T)) != 0)
+ break;
+
+ if ((error = VOP_FRLOCK(vp, cmd, &bf, flag, offset, NULL,
+ fp->f_cred, NULL)) != 0)
+ break;
+
+ if (cmd != F_GETLK)
+ break;
+
+ /*
+ * The command is GETLK, return result.
+ */
+ stol_flock64(&bf, &lxflk64);
+
+ /*
+ * If no lock is found, only the type field is changed.
+ */
+ if (lxflk64.l_type == LX_F_UNLCK) {
+ /* l_type always first entry, always a short */
+ if (copyout(&lxflk64.l_type,
+ &((lx_flock64_t *)arg)->l_type,
+ sizeof (lxflk64.l_type)))
+ error = EFAULT;
+ break;
+ }
+
+ if (bf.l_start > maxoffset || bf.l_len > maxoffset) {
+ error = EOVERFLOW;
+ break;
+ }
+
+ if (copyout(&lxflk64, arg, sizeof (lxflk64)) != 0) {
+ error = EFAULT;
+ break;
+ }
+ break;
+ }
+
+ releasef(fd);
+ if (error)
+ return (set_errno(error));
+
+ return (0);
+}
+
+long
+lx_fcntl(int fd, int cmd, intptr_t arg)
+{
+ switch (cmd) {
+ case LX_F_GETLK64:
+ case LX_F_SETLK64:
+ case LX_F_SETLKW64:
+ /* The 64-bit fcntl commands must go through fcntl64(). */
+ return (set_errno(EINVAL));
+
+ case LX_F_GETLK:
+ case LX_F_SETLK:
+ case LX_F_SETLKW:
+ return (lx_fcntl_lock(fd, cmd, (void *)arg));
+
+ default:
+ return (lx_fcntl_common(fd, cmd, arg));
+ }
+}
+
+long
+lx_fcntl64(int fd, int cmd, intptr_t arg)
+{
+ switch (cmd) {
+ case LX_F_GETLK:
+ case LX_F_SETLK:
+ case LX_F_SETLKW:
+ case LX_F_GETLK64:
+ case LX_F_SETLKW64:
+ case LX_F_SETLK64:
+ return (lx_fcntl_lock(fd, cmd, (void *)arg));
+
+ default:
+ return (lx_fcntl_common(fd, cmd, (ulong_t)arg));
+ }
+}
+
+/*
+ * Apply or remove an advisory lock on the entire file. F_FLOCK and F_FLOCKW
+ * are OFD-style locks. For more information, see the comment on ofdlock().
+ */
+long
+lx_flock(int fd, int op)
+{
+ int cmd;
+ int error;
+ flock64_t bf;
+ file_t *fp;
+
+ if (op & LX_LOCK_NB) {
+ cmd = F_FLOCK;
+ op &= ~LX_LOCK_NB;
+ } else {
+ cmd = F_FLOCKW;
+ }
+
+ switch (op) {
+ case LX_LOCK_UN:
+ bf.l_type = F_UNLCK;
+ break;
+ case LX_LOCK_SH:
+ bf.l_type = F_RDLCK;
+ break;
+ case LX_LOCK_EX:
+ bf.l_type = F_WRLCK;
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ bf.l_whence = 0;
+ bf.l_start = 0;
+ bf.l_len = 0;
+ bf.l_sysid = 0;
+ bf.l_pid = 0;
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ /*
+ * See the locking comment in fcntl.c. In summary, the *_frlock
+ * functions in the various file systems basically do some validation,
+ * then funnel everything through the fs_frlock function. For OFD-style
+ * locks, fs_frlock will do nothing. Once control returns here, we call
+ * the ofdlock function to do the actual locking.
+ */
+ error = VOP_FRLOCK(fp->f_vnode, cmd, &bf, fp->f_flag, fp->f_offset,
+ NULL, fp->f_cred, NULL);
+ if (error != 0) {
+ releasef(fd);
+ return (set_errno(error));
+ }
+ error = ofdlock(fp, cmd, &bf, fp->f_flag, fp->f_offset);
+ if (error != 0) {
+ if (cmd == F_FLOCKW && error == EINTR)
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ (void) set_errno(error);
+ }
+ releasef(fd);
+ return (error);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_futex.c b/usr/src/uts/common/brand/lx/syscall/lx_futex.c
new file mode 100644
index 0000000000..b6244223f9
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_futex.c
@@ -0,0 +1,1728 @@
+/*
+ * 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.
+ */
+
+/*
+ * Copyright 2020 Joyent, Inc.
+ * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <vm/as.h>
+#include <vm/seg.h>
+#include <vm/seg_vn.h>
+#include <vm/page.h>
+#include <sys/priv.h>
+#include <sys/mman.h>
+#include <sys/timer.h>
+#include <sys/condvar.h>
+#include <sys/inttypes.h>
+#include <sys/cmn_err.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_futex.h>
+#include <sys/lx_impl.h>
+#include <sys/sdt.h>
+
+/*
+ * Futexes are a Linux-specific implementation of inter-process mutexes.
+ * They are designed to use shared memory for simple, uncontested
+ * operations, and rely on the kernel to resolve any contention issues.
+ *
+ * Most of the information in this section comes from the paper "Futexes
+ * Are Tricky", by Ulrich Drepper. This paper is currently available at:
+ * http://people.redhat.com/~drepper/futex.pdf.
+ *
+ * A futex itself a 4-byte integer, which must be 4-byte aligned. The
+ * value of this integer is expected to be modified using user-level atomic
+ * operations. For the original, simple futexes, the futex(4) design itself did
+ * not impose any semantic constraints on the value stored in the futex; it is
+ * up to the application to define its own protocol. For the newer,
+ * priority-inheritance (PI) futexes, the value is 0 or the TID of the holder,
+ * as defined in futex(2).
+ *
+ * When the application decides that kernel intervention is required, it
+ * will use the futex(2) system call. Originally there were 5 different
+ * operations that could be performed on a futex, using this system call, but
+ * that has subsequently been extended. Since this interface has evolved over
+ * time, there are several different prototypes available to the user.
+ * Fortunately, there is only a single kernel-level interface:
+ *
+ * long sys_futex(void *futex1, int cmd, int val1,
+ * struct timespec *timeout, void *futex2, int val2)
+ *
+ * The kernel-level operations that may be performed on a simple futex are:
+ *
+ * FUTEX_WAIT
+ *
+ * Atomically verify that futex1 contains the value val1. If it
+ * doesn't, return EWOULDBLOCK. If it does contain the expected
+ * value, the thread will sleep until somebody performs a FUTEX_WAKE
+ * on the futex. The caller may also specify a timeout, indicating
+ * the maximum time the thread should sleep. If the timer expires,
+ * the call returns ETIMEDOUT. If the thread is awoken with a signal,
+ * the call returns EINTR. Otherwise, the call returns 0.
+ *
+ * FUTEX_WAKE
+ *
+ * Wake up val1 processes that are waiting on futex1. The call
+ * returns the number of blocked threads that were woken up.
+ *
+ * FUTEX_WAIT_BITSET/FUTEX_WAKE_BITSET
+ *
+ * Similar to FUTEX_WAIT/FUTEX_WAKE, but each takes an additional argument
+ * denoting a bit vector, with wakers will only waking waiters that match
+ * in one or more bits. These semantics are dubious enough, but the
+ * interface has an inconsistency that is glaring even by the
+ * embarrassingly low standards that Linux sets for itself: the timeout
+ * argument to FUTEX_WAIT_BITSET is absolute, not relative as it is for
+ * FUTEX_WAIT. And as if that weren't enough unnecessary complexity,
+ * the caller may specify this absolute timeout to be against either
+ * CLOCK_MONOTONIC or CLOCK_REALTIME -- but only for FUTEX_WAIT_BITSET,
+ * of course!
+ *
+ * FUTEX_WAKE_OP
+ *
+ * The implementation of a conditional variable in terms of futexes
+ * actually uses two futexes: one to assure sequential access and one to
+ * represent the condition variable. This implementation gives rise to a
+ * particular performance problem whereby a thread is awoken on the futex
+ * that represents the condition variable only to have to (potentially)
+ * immediately wait on the futex that protects the condition variable.
+ * (Do not confuse the futex that serves to protect the condition variable
+ * with the pthread_mutex_t associated with pthread_cond_t -- which
+ * represents a third futex.) To (over)solve this problem, FUTEX_WAKE_OP
+ * was invented, which performs an atomic compare-and-exchange on a
+ * second address in a specified fashion (that is, with a specified
+ * operation). Here are the possible operations (OPARG is defined
+ * to be 12 bit value embedded in the operation):
+ *
+ * - FUTEX_OP_SET: Sets the value at the second address to OPARG
+ * - FUTEX_OP_ADD: Adds the value to OPARG
+ * - FUTEX_OP_OR: OR's the value with OPARG
+ * - FUTEX_OP_ANDN: Performs a negated AND of the value with OPARG
+ * - FUTEX_OP_XOR: XOR's the value with OPARG
+ *
+ * After this compare-and-exchange on the second address, a FUTEX_WAKE is
+ * performed on the first address and -- if the compare-and-exchange
+ * matches a specified result based on a specified comparison operation --
+ * a FUTEX_WAKE is performed on the second address. Here are the possible
+ * comparison operations:
+ *
+ * - FUTEX_OP_CMP_EQ: If old value is CMPARG, wake
+ * - FUTEX_OP_CMP_NE: If old value is not equal to CMPARG, wake
+ * - FUTEX_OP_CMP_LT: If old value is less than CMPARG, wake
+ * - FUTEX_OP_CMP_LE: If old value is less than or equal to CMPARG, wake
+ * - FUTEX_OP_CMP_GT: If old value is greater than CMPARG, wake
+ * - FUTEX_OP_CMP_GE: If old value is greater than or equal to CMPARG, wake
+ *
+ * As a practical matter, the only way that this is used (or, some might
+ * argue, is usable) is by the implementation of pthread_cond_signal(),
+ * which uses FUTEX_WAKE_OP to -- in a single system call -- unlock the
+ * futex that protects the condition variable and wake the futex that
+ * represents the condition variable. The second wake-up is conditional
+ * because the futex that protects the condition variable (rather than the
+ * one that represents it) may or may not have waiters. Given that this
+ * is the use case, FUTEX_WAKE_OP is falsely generic: despite allowing for
+ * five different kinds of operations and six different kinds of
+ * comparision operations, in practice only one is used. (Namely, setting
+ * to 0 and waking if the old value is greater than 1 -- which denotes
+ * that waiters are present and the wakeup should be performed.) Moreover,
+ * because FUTEX_WAKE_OP does not (and cannot) optimize anything in the
+ * case that the pthread_mutex_t associated with the pthread_cond_t is
+ * held at the time of a pthread_cond_signal(), this entire mechanism is
+ * essentially for naught in this case. As one can imagine (and can
+ * verify on just about any source base that uses pthread_cond_signal()),
+ * it is overwhelmingly the common case that the lock associated with the
+ * pthread_cond_t is held at the time of pthread_cond_signal(), assuring
+ * that the problem that all of this complexity was designed to solve
+ * isn't, in fact, solved because the signalled thread simply wakes up
+ * only to block again on the held mutex. Cue a slow clap!
+ *
+ * FUTEX_CMP_REQUEUE
+ *
+ * If the value stored in futex1 matches that passed in in val2, wake
+ * up val1 processes that are waiting on futex1. Otherwise, return
+ * EAGAIN.
+ *
+ * If there are more than val1 threads waiting on the futex, remove
+ * the remaining threads from this futex, and requeue them on futex2.
+ * The caller can limit the number of threads being requeued by
+ * encoding an integral numerical value in the position usually used
+ * for the timeout pointer.
+ *
+ * The call returns the number of blocked threads that were woken up
+ * or requeued.
+ *
+ * FUTEX_REQUEUE
+ *
+ * Identical to FUTEX_CMP_REQUEUE except that it does not use val2.
+ * This command has been declared broken and obsolete, but we still
+ * need to support it.
+ *
+ * FUTEX_FD
+ *
+ * Return a file descriptor, which can be used to refer to the futex.
+ * This operation was broken by design, and was blessedly removed in
+ * Linux 2.6.26 ("because it was inherently racy"); it should go without
+ * saying that we don't support this operation.
+ *
+ * The kernel-level operations that may be performed on a PI futex are:
+ *
+ * FUTEX_LOCK_PI
+ *
+ * Called after a user-land attempt to acquire the lock using an atomic
+ * instruction failed because the futex had a nonzero value (the current
+ * holder's TID). Once enqueued, the thread sleeps until FUTEX_UNLOCK_PI
+ * is called on the futex, or the timeout expires. The timeout argument to
+ * FUTEX_LOCK_PI is absolute, unlike FUTEX_WAIT, and cannot be modified
+ * as with FUTEX_WAIT_BITSET!
+ *
+ * FUTEX_TRYLOCK_PI
+ *
+ * Similar to FUTEX_LOCK_PI but can be used for error recovery as
+ * described in futex(2).
+ *
+ * FUTEX_UNLOCK_PI
+ *
+ * Called when user-land cannot atomically release the lock because
+ * there are waiting threads. This will wake the highest priority waiting
+ * thread.
+ *
+ * FUTEX_CMP_REQUEUE_PI
+ *
+ * Not implemented at this time.
+ *
+ * FUTEX_WAIT_REQUEUE_PI
+ *
+ * Not implemented at this time.
+ *
+ * Priority Inheritance
+ *
+ * Our general approach to priority inheritance recognizes the fact that the
+ * application is almost certainly not a real-time process running on dedicated
+ * hardware. The zone is most likely running in a multi-tenant environment under
+ * FSS, in spite of whatever scheduling class the Linux application thinks it is
+ * using. Thus, we make our best effort to handle priority inheritance. When a
+ * thread must block on a PI futex, it may increase the scheduling priority of
+ * the futex holder to match the blocking thread. The futex holder's original
+ * priority will be restored when it unlocks the futex.
+ *
+ * This approach does not always handle transitive priority inheritance. For
+ * example, three threads at Low, Medium and High priority:
+ * L holds futex X
+ * M holds futex Y and became enqueued on X (M bumped L's priority to M)
+ * H enqueues on Y and bumps priority of M to H, but never bumps L's priority
+ * (which is currently M) up to H
+ * In reality this scenario is both uncommon and likely still executes
+ * reasonably well under a multi-tenant, FSS scenario. Also note that if H
+ * enqueued on Y before M enqueues on X, then L will have its priority raised
+ * to H when M enqueues on X.
+ *
+ * PI Futex Cleanup
+ *
+ * Futex cleanup can occur when a thread exits unexpectedly while holding one
+ * or more futexes. Normally this done via a "robust" futex and cleanup of a
+ * robust PI futex works in the same way as a non-PI robust futex (see
+ * lx_futex_robust_exit). On Linux, in the case of a non-robust PI futex,
+ * cleanup can still occur because the futex is associated with a real-time
+ * mutex inside the kernel (see the futex(2) man page for more details). For lx
+ * we are not using anything similar. When a thread exits, lx_futex_robust_exit
+ * will be called, but we would have to iterate every hash bucket, and every
+ * futex in the chain, to look for futexes held by the exiting thread. This
+ * would be very expensive and would occur whether or not the thread held any
+ * futexes. Thus, at this time we don't set the FUTEX_OWNER_DIED bit on
+ * non-robust PI futexes held by a thread when it exits while holding futexes.
+ * In practice this does not seem to be a serious limitation since user-level
+ * code generally appears to use robust futexes, but this may need to be
+ * revisited if it is observed to be an issue.
+ */
+
+/*
+ * The structure of the robust_list, as set with the set_robust_list() system
+ * call. See lx_futex_robust_exit(), below, for details.
+ */
+typedef struct futex_robust_list {
+ uintptr_t frl_head; /* list of robust locks held */
+ uint64_t frl_offset; /* offset of lock word within a lock */
+ uintptr_t frl_pending; /* pending operation */
+} futex_robust_list_t;
+
+#if defined(_SYSCALL32_IMPL)
+
+#pragma pack(4)
+typedef struct futex_robust_list32 {
+ uint32_t frl_head; /* list of robust locks held */
+ uint32_t frl_offset; /* offset of lock word within a lock */
+ uint32_t frl_pending; /* pending operation */
+} futex_robust_list32_t;
+#pragma pack()
+
+#endif
+
+#define MEMID_COPY(s, d) \
+ { (d)->val[0] = (s)->val[0]; (d)->val[1] = (s)->val[1]; }
+#define MEMID_EQUAL(s, d) \
+ ((d)->val[0] == (s)->val[0] && (d)->val[1] == (s)->val[1])
+
+/*
+ * Because collisions on this hash table can be a source of negative
+ * scalability, we make it pretty large: 4,096 entries -- 64K. If this
+ * size is found to be insufficient, the size should be made dynamic.
+ * (Making it dynamic will be delicate because the per-chain locking will
+ * necessitate memory retiring or similar; see the 2008 ACM Queue article
+ * "Real-world concurrency" for details on this technique.)
+ */
+#define HASH_SHIFT_SZ 12
+#define HASH_SIZE (1 << HASH_SHIFT_SZ)
+#define HASH_FUNC(id) \
+ ((((uintptr_t)((id)->val[1]) >> 3) + \
+ ((uintptr_t)((id)->val[1]) >> (3 + HASH_SHIFT_SZ)) + \
+ ((uintptr_t)((id)->val[1]) >> (3 + 2 * HASH_SHIFT_SZ)) + \
+ ((uintptr_t)((id)->val[0]) >> 3) + \
+ ((uintptr_t)((id)->val[0]) >> (3 + HASH_SHIFT_SZ)) + \
+ ((uintptr_t)((id)->val[0]) >> (3 + 2 * HASH_SHIFT_SZ))) & \
+ (HASH_SIZE - 1))
+
+/*
+ * A small, invalid value we can compare against to find the highest scheduling
+ * priority.
+ */
+#define BELOW_MINPRI INT_MIN
+
+/*
+ * Arbitrary limit on the number of CAS failures allowed in tight looping
+ * contexts before a back-off retry occurs.
+ */
+#define CAS_LOOP_LIMIT 100
+
+/*
+ * We place the per-chain lock next to the pointer to the chain itself.
+ * When compared to an array of orthogonal locks, this reduces false sharing
+ * (though adjacent entries can still be falsely shared -- just not as many),
+ * while having the additional bonus of increasing locality.
+ */
+typedef struct futex_hash {
+ kmutex_t fh_lock;
+ fwaiter_t *fh_waiters;
+} futex_hash_t;
+
+static futex_hash_t futex_hash[HASH_SIZE];
+
+static void
+futex_hashin(fwaiter_t *fwp)
+{
+ int index;
+
+ index = HASH_FUNC(&fwp->fw_memid);
+ ASSERT(MUTEX_HELD(&futex_hash[index].fh_lock));
+
+ fwp->fw_prev = NULL;
+ fwp->fw_next = futex_hash[index].fh_waiters;
+ if (fwp->fw_next)
+ fwp->fw_next->fw_prev = fwp;
+ futex_hash[index].fh_waiters = fwp;
+}
+
+static void
+futex_hashout(fwaiter_t *fwp)
+{
+ int index;
+
+ index = HASH_FUNC(&fwp->fw_memid);
+ ASSERT(MUTEX_HELD(&futex_hash[index].fh_lock));
+
+ if (fwp->fw_prev)
+ fwp->fw_prev->fw_next = fwp->fw_next;
+ if (fwp->fw_next)
+ fwp->fw_next->fw_prev = fwp->fw_prev;
+ if (futex_hash[index].fh_waiters == fwp)
+ futex_hash[index].fh_waiters = fwp->fw_next;
+
+ fwp->fw_prev = NULL;
+ fwp->fw_next = NULL;
+}
+
+/*
+ * Go to sleep until somebody does a WAKE operation on this futex, we get a
+ * signal, or the timeout expires.
+ */
+static int
+futex_wait(memid_t *memid, caddr_t addr,
+ int val, timespec_t *timeout, uint32_t bits, boolean_t hrtime)
+{
+ kthread_t *t = curthread;
+ lx_lwp_data_t *lwpd = ttolxlwp(t);
+ fwaiter_t *fwp = &lwpd->br_fwaiter;
+ int err, ret;
+ int32_t curval;
+ int index;
+
+ /*
+ * The LMS_USER_LOCK micro state becomes valid if we sleep; otherwise
+ * our time will accrue against LMS_SYSTEM. Use of this micro state
+ * is modelled on lwp_mutex_timedlock(), a native analogue of
+ * futex_wait().
+ */
+ (void) new_mstate(t, LMS_USER_LOCK);
+
+ fwp->fw_woken = 0;
+ fwp->fw_bits = bits;
+ fwp->fw_tid = 0;
+
+ MEMID_COPY(memid, &fwp->fw_memid);
+ cv_init(&fwp->fw_cv, NULL, CV_DEFAULT, NULL);
+
+ index = HASH_FUNC(&fwp->fw_memid);
+ mutex_enter(&futex_hash[index].fh_lock);
+
+ if (fuword32(addr, (uint32_t *)&curval)) {
+ err = set_errno(EFAULT);
+ goto out;
+ }
+ if (curval != val) {
+ err = set_errno(EWOULDBLOCK);
+ goto out;
+ }
+
+ /*
+ * We can't have hrtime and a timeout of 0. See below about
+ * CLOCK_REALTIME.
+ * On Linux this is is an invalid state anyway, so we'll short cut
+ * this early to avoid a panic from passing a null pointer to ts2hrt().
+ */
+ if (hrtime && timeout == NULL) {
+ err = set_errno(EINVAL);
+ goto out;
+ }
+
+ futex_hashin(fwp);
+
+ err = 0;
+ while ((fwp->fw_woken == 0) && (err == 0)) {
+ /*
+ * If hrtime is set, we interpret timeout to be absolute and
+ * CLOCK_MONOTONIC-based; otherwise we treat it as absolute
+ * and CLOCK_REALTIME-based. (Strictly speaking -- or at least
+ * in as much as the term "strictly" means anything in the
+ * semantic shambles that is Linux -- FUTEX_WAIT defines its
+ * timeout to be CLOCK_MONOTONIC-based but limited by system
+ * clock interval; we treat these semantics as effectively
+ * CLOCK_REALTIME.)
+ */
+ if (hrtime) {
+ ret = cv_timedwait_sig_hrtime(&fwp->fw_cv,
+ &futex_hash[index].fh_lock, ts2hrt(timeout));
+ } else {
+ ret = cv_waituntil_sig(&fwp->fw_cv,
+ &futex_hash[index].fh_lock, timeout, timechanged);
+ }
+
+ if (ret < 0) {
+ err = set_errno(ETIMEDOUT);
+ } else if (ret == 0) {
+ /*
+ * According to signal(7), a futex(2) call with the
+ * FUTEX_WAIT operation is restartable.
+ */
+ ttolxlwp(t)->br_syscall_restart = B_TRUE;
+ err = set_errno(EINTR);
+ }
+ }
+
+ /*
+ * The futex is normally hashed out in wakeup. If we timed out or
+ * got a signal, we need to hash it out here instead.
+ */
+ if (fwp->fw_woken == 0)
+ futex_hashout(fwp);
+
+out:
+ mutex_exit(&futex_hash[index].fh_lock);
+
+ return (err);
+}
+
+/*
+ * Wake up to wake_threads threads that are blocked on the futex at memid.
+ */
+static int
+futex_wake(memid_t *memid, int wake_threads, uint32_t mask)
+{
+ fwaiter_t *fwp, *next;
+ int index;
+ int ret = 0;
+
+ index = HASH_FUNC(memid);
+
+ mutex_enter(&futex_hash[index].fh_lock);
+
+ for (fwp = futex_hash[index].fh_waiters;
+ fwp != NULL && ret < wake_threads; fwp = next) {
+ next = fwp->fw_next;
+ if (MEMID_EQUAL(&fwp->fw_memid, memid)) {
+ if (fwp->fw_tid != 0) {
+ /*
+ * A PI waiter. It is invalid to mix PI and
+ * non-PI usage on the same futex.
+ */
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (set_errno(EINVAL));
+ }
+
+ if ((fwp->fw_bits & mask)) {
+ futex_hashout(fwp);
+ fwp->fw_woken = 1;
+ cv_signal(&fwp->fw_cv);
+ ret++;
+ }
+ }
+ }
+
+ mutex_exit(&futex_hash[index].fh_lock);
+
+ return (ret);
+}
+
+static int
+futex_wake_op_execute(int32_t *addr, int32_t val3)
+{
+ int32_t op = FUTEX_OP_OP(val3);
+ int32_t cmp = FUTEX_OP_CMP(val3);
+ int32_t cmparg = FUTEX_OP_CMPARG(val3);
+ int32_t oparg, oldval, newval;
+ label_t ljb;
+ int rval;
+ uint_t loops = 0;
+
+ if ((uintptr_t)addr >= KERNELBASE)
+ return (-EFAULT);
+
+ if (on_fault(&ljb))
+ return (-EFAULT);
+
+ oparg = FUTEX_OP_OPARG(val3);
+
+ do {
+ /*
+ * Bail out (for a later retry) if the CAS operation repeatedly
+ * fails to set the new value.
+ */
+ if (loops++ > CAS_LOOP_LIMIT) {
+ no_fault();
+ return (-EAGAIN);
+ }
+
+ oldval = *addr;
+ newval = oparg;
+
+ switch (op) {
+ case FUTEX_OP_SET:
+ break;
+
+ case FUTEX_OP_ADD:
+ newval += oparg;
+ break;
+
+ case FUTEX_OP_OR:
+ newval |= oparg;
+ break;
+
+ case FUTEX_OP_ANDN:
+ newval &= ~oparg;
+ break;
+
+ case FUTEX_OP_XOR:
+ newval ^= oparg;
+ break;
+
+ default:
+ no_fault();
+ return (-EINVAL);
+ }
+ } while (atomic_cas_32((uint32_t *)addr, oldval, newval) != oldval);
+
+ no_fault();
+
+ switch (cmp) {
+ case FUTEX_OP_CMP_EQ:
+ rval = (oldval == cmparg);
+ break;
+
+ case FUTEX_OP_CMP_NE:
+ rval = (oldval != cmparg);
+ break;
+
+ case FUTEX_OP_CMP_LT:
+ rval = (oldval < cmparg);
+ break;
+
+ case FUTEX_OP_CMP_LE:
+ rval = (oldval <= cmparg);
+ break;
+
+ case FUTEX_OP_CMP_GT:
+ rval = (oldval > cmparg);
+ break;
+
+ case FUTEX_OP_CMP_GE:
+ rval = (oldval >= cmparg);
+ break;
+
+ default:
+ return (-EINVAL);
+ }
+
+ return (rval);
+}
+
+static int
+futex_wake_op(memid_t *memid, caddr_t addr2, memid_t *memid2,
+ int wake_threads, int wake_threads2, int val3)
+{
+ kmutex_t *l1, *l2;
+ int ret = 0, ret2 = 0, wake;
+ fwaiter_t *fwp, *next;
+ int index1, index2;
+
+ index1 = HASH_FUNC(memid);
+ index2 = HASH_FUNC(memid2);
+
+ if (index1 == index2) {
+ l1 = &futex_hash[index1].fh_lock;
+ l2 = NULL;
+ } else if (index1 < index2) {
+ l1 = &futex_hash[index1].fh_lock;
+ l2 = &futex_hash[index2].fh_lock;
+ } else {
+ l1 = &futex_hash[index2].fh_lock;
+ l2 = &futex_hash[index1].fh_lock;
+ }
+
+retry:
+ mutex_enter(l1);
+ if (l2 != NULL)
+ mutex_enter(l2);
+
+ /* LINTED: alignment */
+ if ((wake = futex_wake_op_execute((int32_t *)addr2, val3)) < 0) {
+ /*
+ * If the futex op fails on a looping CAS attempt, drop the
+ * involved mutexes to allow others to run, and try again.
+ */
+ if (wake == -EAGAIN) {
+ if (l2 != NULL)
+ mutex_exit(l2);
+ mutex_exit(l1);
+ goto retry;
+ }
+
+ (void) set_errno(-wake); /* convert back to positive errno */
+ ret = -1;
+ goto out;
+ }
+
+ for (fwp = futex_hash[index1].fh_waiters; fwp != NULL; fwp = next) {
+ next = fwp->fw_next;
+ if (!MEMID_EQUAL(&fwp->fw_memid, memid))
+ continue;
+
+ if (fwp->fw_tid != 0) {
+ /*
+ * A PI waiter. It is invalid to mix PI and non-PI
+ * usage on the same futex.
+ */
+ (void) set_errno(EINVAL);
+ ret = -1;
+ goto out;
+ }
+
+ futex_hashout(fwp);
+ fwp->fw_woken = 1;
+ cv_signal(&fwp->fw_cv);
+ if (++ret >= wake_threads) {
+ break;
+ }
+ }
+
+ if (!wake)
+ goto out;
+
+ for (fwp = futex_hash[index2].fh_waiters; fwp != NULL; fwp = next) {
+ next = fwp->fw_next;
+ if (!MEMID_EQUAL(&fwp->fw_memid, memid2))
+ continue;
+
+ if (fwp->fw_tid != 0) {
+ /*
+ * A PI waiter. It is invalid to mix PI and non-PI
+ * usage on the same futex.
+ */
+ (void) set_errno(EINVAL);
+ ret = -1;
+ goto out;
+ }
+
+ futex_hashout(fwp);
+ fwp->fw_woken = 1;
+ cv_signal(&fwp->fw_cv);
+ if (++ret2 >= wake_threads2) {
+ break;
+ }
+ }
+
+ ret += ret2;
+out:
+ if (l2 != NULL)
+ mutex_exit(l2);
+ mutex_exit(l1);
+
+ return (ret);
+}
+
+/*
+ * Wake up to wake_threads waiting on the futex at memid. If there are
+ * more than that many threads waiting, requeue the remaining threads on
+ * the futex at requeue_memid.
+ */
+static int
+futex_requeue(memid_t *memid, memid_t *requeue_memid, int wake_threads,
+ ulong_t requeue_threads, caddr_t addr, int *cmpval)
+{
+ fwaiter_t *fwp, *next;
+ int index1, index2;
+ int ret = 0;
+ int32_t curval;
+ kmutex_t *l1, *l2;
+
+ /*
+ * To ensure that we don't miss a wakeup if the value of cmpval
+ * changes, we need to grab locks on both the original and new hash
+ * buckets. To avoid deadlock, we always grab the lower-indexed
+ * lock first.
+ */
+ index1 = HASH_FUNC(memid);
+ index2 = HASH_FUNC(requeue_memid);
+
+ if (index1 == index2) {
+ l1 = &futex_hash[index1].fh_lock;
+ l2 = NULL;
+ } else if (index1 < index2) {
+ l1 = &futex_hash[index1].fh_lock;
+ l2 = &futex_hash[index2].fh_lock;
+ } else {
+ l1 = &futex_hash[index2].fh_lock;
+ l2 = &futex_hash[index1].fh_lock;
+ }
+
+ mutex_enter(l1);
+ if (l2 != NULL)
+ mutex_enter(l2);
+
+ if (cmpval != NULL) {
+ if (fuword32(addr, (uint32_t *)&curval)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (curval != *cmpval) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ }
+
+ for (fwp = futex_hash[index1].fh_waiters; fwp != NULL; fwp = next) {
+ next = fwp->fw_next;
+ if (!MEMID_EQUAL(&fwp->fw_memid, memid))
+ continue;
+
+ futex_hashout(fwp);
+ if (ret++ < wake_threads) {
+ fwp->fw_woken = 1;
+ cv_signal(&fwp->fw_cv);
+ } else {
+ MEMID_COPY(requeue_memid, &fwp->fw_memid);
+ futex_hashin(fwp);
+
+ if ((ret - wake_threads) >= requeue_threads)
+ break;
+ }
+ }
+
+out:
+ if (l2 != NULL)
+ mutex_exit(l2);
+ mutex_exit(l1);
+
+ if (ret < 0)
+ return (set_errno(-ret));
+ return (ret);
+}
+
+/*
+ * Copy in the timeout provided by the application and convert it to an
+ * absolute timeout. Sadly, this is complicated by the different timeout
+ * semantics of FUTEX_WAIT vs. FUTEX_WAIT_BITSET vs. FUTEX_LOCK_PI. (Yes, you
+ * read that correctly; all three of these have different timeout semantics;
+ * see the block comment at the top of the file for commentary on this
+ * inanity.) This function doesn't attempt to clean up all of these
+ * differences, however; we will only copy the timer value in, perform some
+ * basic sanity checking, and (if it's an operation operating on a relative
+ * time, which is to say FUTEX_WAIT) adjust it to be absolute. All other
+ * nuances (namely, the resolution and clock of the timeout) are left up to
+ * the caller.
+ */
+static int
+get_timeout(void *lx_timeout, timestruc_t *timeout, int cmd)
+{
+ timestruc_t now;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(lx_timeout, timeout, sizeof (timestruc_t)))
+ return (EFAULT);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ timestruc32_t timeout32;
+ if (copyin(lx_timeout, &timeout32, sizeof (timestruc32_t)))
+ return (EFAULT);
+ timeout->tv_sec = (time_t)timeout32.tv_sec;
+ timeout->tv_nsec = timeout32.tv_nsec;
+ }
+#endif
+ if (itimerspecfix(timeout))
+ return (EINVAL);
+
+ if (cmd == FUTEX_WAIT) {
+ /*
+ * We've been given a relative time; add it to the current
+ * time to derive an absolute time.
+ */
+ gethrestime(&now);
+ timespecadd(timeout, &now);
+ }
+
+ return (0);
+}
+
+/*
+ * Attempt to take the futex. If currently held, enqueue (sleep) on the futex
+ * until a thread performs futex_unlock_pi, we get a signal, or the timeout
+ * expires. If 'is_trylock' is true and the futex is currently held, return
+ * EAGAIN immediately.
+ */
+static int
+futex_lock_pi(memid_t *memid, uint32_t *addr, timespec_t *timeout,
+ boolean_t is_trylock)
+{
+ kthread_t *t = curthread;
+ lx_lwp_data_t *lwpd = ttolxlwp(t);
+ fwaiter_t *fwp = &lwpd->br_fwaiter;
+ fwaiter_t *f_fwp;
+ int fpri, mypri;
+ int err;
+ int index;
+ /* volatile to silence gcc clobber warning for longjmp */
+ volatile pid_t mytid;
+ pid_t ftid; /* current futex holder tid */
+ proc_t *fproc = NULL; /* current futex holder proc */
+ kthread_t *fthrd; /* current futex holder thread */
+ volatile uint32_t oldval;
+ volatile uint_t loops = 0;
+
+ if ((uintptr_t)addr >= KERNELBASE)
+ return (set_errno(EFAULT));
+
+ mytid = (lwpd->br_pid == curzone->zone_proc_initpid ? 1 : lwpd->br_pid);
+
+ /*
+ * Have to take mutex first to prevent the following race with unlock:
+ * a) T1 sees a tid in the futex and atomically sets FUTEX_WAITERS.
+ * b) T2 calls unlock, sees there are waiters, but since nothing is in
+ * the queue yet, it simply returns with the futex now containing 0.
+ * c) T1 proceeds to enqueue itself.
+ * At this point nothing will ever wake T1.
+ */
+ index = HASH_FUNC(memid);
+retry:
+ mutex_enter(&futex_hash[index].fh_lock);
+
+ /* It would be very unusual to actually loop here. */
+ oldval = 0;
+ /* CONSTCOND */
+ while (1) {
+ uint32_t curval;
+ label_t ljb;
+
+ /*
+ * Make a round trip through the lock if too many CAS failures
+ * occur, indicative of userspace tomfoolery.
+ */
+ if (loops++ > CAS_LOOP_LIMIT) {
+ mutex_exit(&futex_hash[index].fh_lock);
+ goto retry;
+ }
+
+ if (on_fault(&ljb)) {
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (set_errno(EFAULT));
+ }
+
+ /*
+ * We optimistically try to set our tid on the off chance that
+ * the futex was released after we initiated the syscall. That
+ * may work but it is the unlikely path and is usually just our
+ * way of getting the current value. This also handles the
+ * retry in the case when the futex only has the high bits set.
+ */
+ curval = atomic_cas_32(addr, oldval, mytid);
+ if (oldval == curval) {
+ no_fault();
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (0);
+ }
+
+ oldval = curval;
+ ftid = oldval & FUTEX_TID_MASK;
+ /* high bits were only ones set, so we retry to set our tid */
+ if (ftid == 0) {
+ no_fault();
+ continue;
+ }
+
+ if (ftid == mytid) {
+ no_fault();
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (set_errno(EDEADLK));
+ }
+
+ /* The futex is currently held by another thread. */
+ if (is_trylock) {
+ no_fault();
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (set_errno(EAGAIN));
+ }
+
+ curval = atomic_cas_32(addr, oldval, oldval | FUTEX_WAITERS);
+ no_fault();
+ if (curval == oldval) {
+ /*
+ * We set the WAITERS bit so now we can enqueue our
+ * thread on the mutex. This is the typical path.
+ */
+ oldval |= FUTEX_WAITERS;
+ break;
+ }
+
+ /*
+ * The rare case when a change snuck into the window between
+ * first getting the futex value and updating it; retry.
+ */
+ oldval = 0;
+ }
+
+ /*
+ * Determine if the current futex holder's priority needs to inherit
+ * our priority (only if it should be increased).
+ *
+ * If a non-branded proc is sharing this futex(!?) then we don't
+ * interact with it. This seems like it would only occur maliciously.
+ * That proc will never be able to call futex(2) to unlock the futex.
+ * We just return ESRCH for this invalid case.
+ *
+ * Otherwise, get the holder's priority and if necessary, bump it up to
+ * our level.
+ */
+ mutex_enter(&curproc->p_lock);
+ (void) CL_DOPRIO(curthread, kcred, 0, &mypri);
+ mutex_exit(&curproc->p_lock);
+
+ if (lx_lpid_lock(ftid, curzone, 0, &fproc, &fthrd) != 0) {
+ label_t ljb;
+
+ if (on_fault(&ljb) == 0) {
+ (void) atomic_cas_32(addr, oldval,
+ oldval | FUTEX_OWNER_DIED);
+ }
+ no_fault();
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (set_errno(ESRCH));
+ }
+ if (!PROC_IS_BRANDED(fproc)) {
+ mutex_exit(&fproc->p_lock);
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (set_errno(ESRCH));
+ }
+
+ ASSERT(MUTEX_HELD(&fproc->p_lock));
+ (void) CL_DOPRIO(fthrd, kcred, 0, &fpri);
+
+ f_fwp = &lwptolxlwp(ttolwp(fthrd))->br_fwaiter;
+ if (mypri > fpri) {
+ /* Save holder's current pri if not already bumped up */
+ if (!f_fwp->fw_pri_up)
+ f_fwp->fw_opri = fpri;
+ f_fwp->fw_pri_up = B_TRUE;
+ DTRACE_PROBE2(futex__lck__pri, int, mypri, int, fpri);
+ CL_DOPRIO(fthrd, kcred, mypri - fpri, &fpri);
+ }
+
+ /*
+ * If we haven't already been bumped by some other thread then
+ * record our pri at time of enqueue.
+ */
+ if (!fwp->fw_pri_up) {
+ fwp->fw_opri = mypri;
+ }
+ mutex_exit(&fproc->p_lock);
+
+ /*
+ * Enqueue our thread on the mutex. This is similar to futex_wait().
+ * See futex_wait() for LMS_USER_LOCK state description.
+ */
+ (void) new_mstate(t, LMS_USER_LOCK);
+
+ fwp->fw_woken = 0;
+ fwp->fw_bits = 0;
+ fwp->fw_tid = mytid;
+ MEMID_COPY(memid, &fwp->fw_memid);
+ cv_init(&fwp->fw_cv, NULL, CV_DEFAULT, NULL);
+
+ futex_hashin(fwp);
+
+ err = 0;
+ while (fwp->fw_woken == 0 && err == 0) {
+ int ret;
+
+ ret = cv_waituntil_sig(&fwp->fw_cv, &futex_hash[index].fh_lock,
+ timeout, timechanged);
+ if (ret < 0) {
+ err = set_errno(ETIMEDOUT);
+ } else if (ret == 0) {
+ /* EINTR is not valid for futex_lock_pi */
+ err = set_errno(EAGAIN);
+ }
+ }
+
+ /*
+ * The futex is normally hashed out in futex_unlock_pi. If we timed out
+ * or got a signal, we need to hash it out here instead.
+ */
+ if (fwp->fw_woken == 0)
+ futex_hashout(fwp);
+
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (err);
+}
+
+/*
+ * This must be a separate function to prevent compiler complaints about
+ * clobbering variables via longjmp (on_fault). When setting the new owner we
+ * must preserve the current WAITERS and OWNER_DIED bits.
+ */
+static int
+futex_unlock_pi_waiter(fwaiter_t *fnd_fwp, uint32_t *addr, uint32_t curval)
+{
+ label_t ljb;
+ pid_t tid;
+
+ if (on_fault(&ljb)) {
+ return (EFAULT);
+ }
+
+ /* No waiter on this futex; again, not normal, but not an error. */
+ if (fnd_fwp == NULL) {
+ int res = 0;
+ if (atomic_cas_32(addr, curval,
+ 0 | (curval & FUTEX_OWNER_DIED)) != curval)
+ res = EINVAL;
+ no_fault();
+ return (res);
+ }
+
+ tid = fnd_fwp->fw_tid | (curval & (FUTEX_WAITERS | FUTEX_OWNER_DIED));
+ if (atomic_cas_32(addr, curval, tid) != curval) {
+ /*
+ * The value was changed behind our back, return an error and
+ * don't dequeue the waiter.
+ */
+ no_fault();
+ return (EINVAL);
+ }
+
+ no_fault();
+
+ futex_hashout(fnd_fwp);
+ fnd_fwp->fw_woken = 1;
+ cv_signal(&fnd_fwp->fw_cv);
+
+ return (0);
+}
+
+/*
+ * Paired with futex_lock_pi; wake up highest priority thread that is blocked
+ * on the futex at memid. A non-zero 'clean_tid' argument is used for a PI
+ * futex during robust or trylock cleanup when the calling thread may not own
+ * the futex. During cleanup we check that the futex contains the expected
+ * tid to avoid cleanup races.
+ */
+static int
+futex_unlock_pi(memid_t *memid, uint32_t *addr, pid_t clean_tid)
+{
+ kthread_t *t = curthread;
+ lx_lwp_data_t *lwpd = ttolxlwp(t);
+ fwaiter_t *fwp, *fnd_fwp;
+ uint32_t curval;
+ pid_t mytid;
+ pid_t holder_tid;
+ int index;
+ int hipri;
+ int res;
+
+ if ((uintptr_t)addr >= KERNELBASE)
+ return (EFAULT);
+
+ mytid = (lwpd->br_pid == curzone->zone_proc_initpid ? 1 : lwpd->br_pid);
+
+ /* See comment in futex_lock_pi for why we take the mutex first. */
+ index = HASH_FUNC(memid);
+ mutex_enter(&futex_hash[index].fh_lock);
+
+ if (fuword32(addr, &curval)) {
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (EFAULT);
+ }
+
+ holder_tid = curval & FUTEX_TID_MASK;
+ if (clean_tid == 0) {
+ /* Not cleaning up so we must hold the futex */
+ if (holder_tid != mytid) {
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (EPERM);
+ }
+ } else {
+ /*
+ * We're doing cleanup but we want to check if another thread
+ * already did the cleanup due to a race before we took the
+ * futex_hash.fh_lock.
+ *
+ * There are two posible cases here:
+ * 1) During robust cleanup we already cleared the dead tid
+ * from the futex and set the FUTEX_OWNER_DIED bit.
+ * 2) During trylock cleanup we want to be sure the tid we
+ * saw in the futex before we took the futex_hash lock
+ * is still there and that we did not race with another
+ * trylock also doing cleanup.
+ */
+ DTRACE_PROBE2(futex__unl__clean, int, curval, int, clean_tid);
+ if ((curval & FUTEX_OWNER_DIED) != 0) {
+ if (holder_tid != 0) {
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (0);
+ }
+ } else if (holder_tid != clean_tid) {
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (0);
+ }
+ }
+
+ /*
+ * If necessary, restore our old priority. Since we only ever bump up
+ * the priority, our incr should be negative, but we allow for the
+ * case where the priority was lowered in some other way while we held
+ * the futex. Also, we only reset our priority on a true unlock, not
+ * when cleaning up, as indicated by clean_tid.
+ */
+ if (clean_tid == 0) {
+ fwp = &lwpd->br_fwaiter;
+ if (fwp->fw_pri_up) {
+ int curpri;
+ int incr;
+
+ mutex_enter(&curproc->p_lock);
+ CL_DOPRIO(curthread, kcred, 0, &curpri);
+ DTRACE_PROBE2(futex__unl__pri, int, fwp->fw_opri,
+ int, curpri);
+ incr = fwp->fw_opri - curpri;
+ if (incr < 0) {
+ CL_DOPRIO(curthread, kcred, incr, &curpri);
+ }
+ mutex_exit(&curproc->p_lock);
+ fwp->fw_pri_up = B_FALSE;
+ }
+ }
+
+ /*
+ * Normally an application wouldn't make the syscall if the WAITERS
+ * bit is not set, but we also come through here on robust and trylock
+ * cleanup. Preserve the OWNER_DIED bit even though there are no
+ * waiters and we're just clearing the tid.
+ */
+ if ((curval & FUTEX_WAITERS) == 0) {
+ res = 0;
+ label_t fjb;
+
+ if (on_fault(&fjb)) {
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (EFAULT);
+ }
+ if (atomic_cas_32(addr, curval,
+ 0 | (curval & FUTEX_OWNER_DIED)) != curval) {
+ res = EINVAL;
+ }
+
+ no_fault();
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (res);
+ }
+
+ /* Find the highest priority waiter. */
+ hipri = BELOW_MINPRI;
+ fnd_fwp = NULL;
+ for (fwp = futex_hash[index].fh_waiters; fwp != NULL;
+ fwp = fwp->fw_next) {
+ if (MEMID_EQUAL(&fwp->fw_memid, memid)) {
+ if (fwp->fw_tid == 0) {
+ /*
+ * A non-PI waiter. It is invalid to mix PI and
+ * non-PI usage on the same futex.
+ */
+ no_fault();
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (EINVAL);
+ }
+ /*
+ * Because futex_hashin inserts at the head of the list
+ * we want to find the oldest entry with the highest
+ * priority (hence >=).
+ */
+ if (fwp->fw_opri >= hipri) {
+ fnd_fwp = fwp;
+ hipri = fwp->fw_opri;
+ }
+ }
+ }
+
+ res = futex_unlock_pi_waiter(fnd_fwp, addr, curval);
+ mutex_exit(&futex_hash[index].fh_lock);
+ return (res);
+}
+
+/*
+ * Handle the case where the futex holder is gone and try to recover. Trylock
+ * will never enqueue on the futex and must return EAGAIN if it is held by
+ * a live process.
+ */
+static int
+futex_trylock_pi(memid_t *memid, uint32_t *addr)
+{
+ uint32_t curval;
+ pid_t ftid; /* current futex holder tid */
+ proc_t *fproc = NULL; /* current futex holder proc */
+ kthread_t *fthrd; /* current futex holder thread */
+
+ if ((uintptr_t)addr >= KERNELBASE)
+ return (set_errno(EFAULT));
+
+ if (fuword32(addr, &curval))
+ return (set_errno(EFAULT));
+
+ /* The futex is free, use the normal flow. */
+ if (curval == 0)
+ return (futex_lock_pi(memid, addr, NULL, B_TRUE));
+
+ /* Determine if the current futex holder is still alive. */
+ ftid = curval & FUTEX_TID_MASK;
+ if (lx_lpid_lock(ftid, curzone, 0, &fproc, &fthrd) == 0) {
+ mutex_exit(&fproc->p_lock);
+ } else {
+ /*
+ * The current holder is gone. Unlock then take the lock.
+ * Ignore any error that may result from two threads racing to
+ * cleanup.
+ */
+ (void) futex_unlock_pi(memid, addr, ftid);
+ }
+ return (futex_lock_pi(memid, addr, NULL, B_TRUE));
+}
+
+long
+lx_futex(uintptr_t addr, int op, int val, uintptr_t lx_timeout,
+ uintptr_t addr2, int val3)
+{
+ struct as *as = curproc->p_as;
+ memid_t memid, memid2;
+ timestruc_t timeout;
+ timestruc_t *tptr = NULL;
+ int val2 = 0;
+ int rval = 0;
+ int cmd = op & FUTEX_CMD_MASK;
+ int private = op & FUTEX_PRIVATE_FLAG;
+ char dmsg[32];
+
+ /* must be aligned on int boundary */
+ if (addr & 0x3)
+ return (set_errno(EINVAL));
+
+ /* Sanity check the futex command */
+ if (cmd < 0 || cmd > FUTEX_MAX_CMD)
+ return (set_errno(EINVAL));
+
+ if (cmd == FUTEX_FD) {
+ /*
+ * FUTEX_FD was sentenced to death for grievous crimes of
+ * semantics against humanity; it has been ripped out of Linux
+ * and will never be supported by us.
+ */
+ (void) snprintf(dmsg, sizeof (dmsg), "futex 0x%x", cmd);
+ lx_unsupported(dmsg);
+ return (set_errno(ENOSYS));
+ }
+
+ switch (cmd) {
+ case FUTEX_WAIT_REQUEUE_PI:
+ case FUTEX_CMP_REQUEUE_PI:
+ /*
+ * These are operations that we don't currently support, but
+ * may well need to in the future. For now, callers need to
+ * deal with these being missing -- but if and as that changes,
+ * they may well need to be implemented.
+ */
+ (void) snprintf(dmsg, sizeof (dmsg), "futex 0x%x", cmd);
+ lx_unsupported(dmsg);
+ return (set_errno(ENOSYS));
+ }
+
+ if ((op & FUTEX_CLOCK_REALTIME) && cmd != FUTEX_WAIT_BITSET) {
+ /*
+ * Linux only allows FUTEX_CLOCK_REALTIME to be set on the
+ * FUTEX_WAIT_BITSET and FUTEX_WAIT_REQUEUE_PI commands.
+ */
+ return (set_errno(ENOSYS));
+ }
+
+ /* Copy in the timeout structure from userspace. */
+ if ((cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_BITSET ||
+ cmd == FUTEX_LOCK_PI) && lx_timeout != (uintptr_t)NULL) {
+ rval = get_timeout((timespec_t *)lx_timeout, &timeout, cmd);
+
+ if (rval != 0)
+ return (set_errno(rval));
+ tptr = &timeout;
+ }
+
+ switch (cmd) {
+ case FUTEX_REQUEUE:
+ case FUTEX_CMP_REQUEUE:
+ case FUTEX_WAKE_OP:
+ /*
+ * lx_timeout is nominally a pointer to a userspace address.
+ * For several commands, however, it actually contains
+ * an additional integer parameter. This is horrible, and
+ * the people who did this to us should be sorry.
+ */
+ val2 = (int)lx_timeout;
+ }
+
+ /*
+ * Translate the process-specific, user-space futex virtual
+ * address(es) to a universal memid. If the private bit is set, we
+ * can just use our as plus the virtual address, saving quite a bit
+ * of effort.
+ */
+ if (private) {
+ memid.val[0] = (uintptr_t)as;
+ memid.val[1] = (uintptr_t)addr;
+ } else {
+ rval = as_getmemid(as, (void *)addr, &memid);
+ if (rval != 0)
+ return (set_errno(rval));
+ }
+
+ if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
+ cmd == FUTEX_WAKE_OP) {
+ if (addr2 & 0x3)
+ return (set_errno(EINVAL));
+
+ if (private) {
+ memid2.val[0] = (uintptr_t)as;
+ memid2.val[1] = (uintptr_t)addr2;
+ } else {
+ rval = as_getmemid(as, (void *)addr2, &memid2);
+ if (rval)
+ return (set_errno(rval));
+ }
+ }
+
+ switch (cmd) {
+ case FUTEX_WAIT:
+ rval = futex_wait(&memid, (void *)addr, val,
+ tptr, FUTEX_BITSET_MATCH_ANY, B_FALSE);
+ break;
+
+ case FUTEX_WAIT_BITSET:
+ rval = futex_wait(&memid, (void *)addr, val, tptr, val3,
+ (tptr == NULL || (op & FUTEX_CLOCK_REALTIME) != 0) ?
+ B_FALSE : B_TRUE);
+ break;
+
+ case FUTEX_WAKE:
+ rval = futex_wake(&memid, val, FUTEX_BITSET_MATCH_ANY);
+ break;
+
+ case FUTEX_WAKE_BITSET:
+ rval = futex_wake(&memid, val, val3);
+ break;
+
+ case FUTEX_WAKE_OP:
+ rval = futex_wake_op(&memid, (void *)addr2, &memid2,
+ val, val2, val3);
+ break;
+
+ case FUTEX_CMP_REQUEUE:
+ rval = futex_requeue(&memid, &memid2, val,
+ val2, (void *)addr2, &val3);
+
+ break;
+
+ case FUTEX_REQUEUE:
+ /*
+ * Per Linux futex(2), FUTEX_REQUEUE is the same as
+ * FUTEX_CMP_REQUEUE, except val3 is ignored. futex_requeue()
+ * will elide the val3 check if cmpval (the last argument) is
+ * NULL.
+ */
+ rval = futex_requeue(&memid, &memid2, val,
+ val2, (void *)addr2, NULL);
+
+ break;
+
+ case FUTEX_LOCK_PI:
+ rval = futex_lock_pi(&memid, (uint32_t *)addr, tptr, B_FALSE);
+ break;
+
+ case FUTEX_TRYLOCK_PI:
+ rval = futex_trylock_pi(&memid, (uint32_t *)addr);
+ break;
+
+ case FUTEX_UNLOCK_PI:
+ rval = futex_unlock_pi(&memid, (uint32_t *)addr, 0);
+ if (rval != 0)
+ (void) set_errno(rval);
+ break;
+ }
+
+ return (rval);
+}
+
+/*
+ * Wake the next waiter if the thread holding the futex has exited without
+ * releasing the futex.
+ */
+static void
+futex_robust_wake(memid_t *memid, uint32_t tid)
+{
+ fwaiter_t *fwp;
+ int index;
+
+ index = HASH_FUNC(memid);
+
+ mutex_enter(&futex_hash[index].fh_lock);
+
+ for (fwp = futex_hash[index].fh_waiters; fwp != NULL;
+ fwp = fwp->fw_next) {
+ if (MEMID_EQUAL(&fwp->fw_memid, memid))
+ break;
+ }
+
+ if (fwp != NULL) {
+ if (fwp->fw_tid != 0) {
+ /*
+ * This is a PI futex and there is a waiter; unlock the
+ * futex in cleanup mode. Ignore errors, which are very
+ * unlikely, but could happen if the futex was in an
+ * unexpected state due to some other cleanup, such as
+ * might happen with a concurrent trylock call.
+ */
+ mutex_exit(&futex_hash[index].fh_lock);
+ (void) futex_unlock_pi(memid,
+ (uint32_t *)(uintptr_t)memid->val[1], tid);
+ return;
+ }
+
+ /* non-PI futex, just wake it */
+ futex_hashout(fwp);
+ fwp->fw_woken = 1;
+ cv_signal(&fwp->fw_cv);
+ }
+
+ mutex_exit(&futex_hash[index].fh_lock);
+}
+
+/*
+ * Does the dirty work of actually dropping a held robust lock in the event
+ * of the untimely death of the owner; see lx_futex_robust_exit(), below.
+ */
+static void
+lx_futex_robust_drop(uintptr_t addr, uint32_t tid)
+{
+ memid_t memid;
+ uint32_t oldval, newval;
+
+ VERIFY(addr + sizeof (uint32_t) < KERNELBASE);
+
+ do {
+ fuword32_noerr((void *)addr, &oldval);
+
+ if ((oldval & FUTEX_TID_MASK) != tid)
+ return;
+
+ newval = (oldval & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
+ } while (atomic_cas_32((uint32_t *)addr, oldval, newval) != oldval);
+
+ /*
+ * We have now denoted that this lock's owner is dead; we need to
+ * wake any waiters.
+ */
+ if (as_getmemid(curproc->p_as, (void *)addr, &memid) != 0)
+ return;
+
+ futex_robust_wake(&memid, tid);
+}
+
+/*
+ * Called when a thread is exiting. The role of the kernel is very clearly
+ * spelled out in the Linux design document entitled robust-futex-ABI.txt:
+ * we must (carefully!) iterate over the list of held locks pointed to by
+ * the robust list head; for each lock, we'll check to see if the calling
+ * (exiting) thread is the owner, and if so, denote that the lock is dead
+ * and wake any waiters. (The "pending" field of the head points to a lock
+ * that is in transition; it should be dropped if held.) If there are any
+ * errors through here at all (including memory operations), we abort the
+ * entire operation.
+ */
+void
+lx_futex_robust_exit(uintptr_t addr, uint32_t tid)
+{
+ futex_robust_list_t list;
+ uintptr_t entry, next;
+ model_t model = get_udatamodel();
+ int length = 0;
+ label_t ljb;
+
+ if (on_fault(&ljb))
+ return;
+
+ if (addr + sizeof (futex_robust_list_t) >= KERNELBASE)
+ goto out;
+
+ if (model == DATAMODEL_NATIVE) {
+ copyin_noerr((void *)addr, &list, sizeof (list));
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ futex_robust_list32_t list32;
+
+ copyin_noerr((void *)addr, &list32, sizeof (list32));
+ list.frl_head = list32.frl_head;
+ list.frl_offset = list32.frl_offset;
+ list.frl_pending = list32.frl_pending;
+ }
+#endif
+
+ /*
+ * Strip off the PI bit, if any.
+ */
+ entry = list.frl_head & ~FUTEX_ROBUST_LOCK_PI;
+
+ while (entry != addr && length++ < FUTEX_ROBUST_LIST_LIMIT) {
+ if (entry + list.frl_offset + sizeof (uint32_t) >= KERNELBASE)
+ goto out;
+
+ if (model == DATAMODEL_NATIVE) {
+ fulword_noerr((void *)entry, &next);
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ uint32_t next32;
+ fuword32_noerr((void *)entry, &next32);
+ next = next32;
+ }
+#endif
+
+ /*
+ * Drop the robust mutex -- but only if our pending lock didn't
+ * somehow sneak on there.
+ */
+ if (entry != list.frl_pending)
+ lx_futex_robust_drop(entry + list.frl_offset, tid);
+
+ entry = next & ~FUTEX_LOCK_PI;
+ }
+
+ /*
+ * Finally, drop the pending lock if there is one.
+ */
+ if (list.frl_pending != (uint32_t)(uintptr_t)NULL && list.frl_pending +
+ list.frl_offset + sizeof (uint32_t) < KERNELBASE)
+ lx_futex_robust_drop(list.frl_pending + list.frl_offset, tid);
+
+out:
+ no_fault();
+}
+
+long
+lx_set_robust_list(void *listp, size_t len)
+{
+ proc_t *p = curproc;
+ klwp_t *lwp = ttolwp(curthread);
+ struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (len != sizeof (futex_robust_list_t))
+ return (set_errno(EINVAL));
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ if (len != sizeof (futex_robust_list32_t))
+ return (set_errno(EINVAL));
+ }
+#endif
+
+ /*
+ * To assure that we are serialized with respect to any racing call
+ * to lx_get_robust_list(), we lock ourselves to set the value. (Note
+ * that sprunlock() drops p_lock.)
+ */
+ mutex_enter(&p->p_lock);
+ sprlock_proc(p);
+ lwpd->br_robust_list = listp;
+ sprunlock(p);
+
+ return (0);
+}
+
+long
+lx_get_robust_list(pid_t pid, void **listp, size_t *lenp)
+{
+ model_t model = get_udatamodel();
+ proc_t *rproc;
+ kthread_t *rthr;
+ klwp_t *rlwp;
+ lx_lwp_data_t *rlwpd;
+ void *list;
+ int err = 0;
+
+ if (pid == 0) {
+ /*
+ * A pid of 0 denotes the current thread; we lock the current
+ * process even though it isn't strictly necessary (we can't
+ * race with set_robust_list() because a thread may only set
+ * its robust list on itself).
+ */
+ rproc = curproc;
+ rlwpd = lwptolxlwp(ttolwp(curthread));
+ mutex_enter(&curproc->p_lock);
+ sprlock_proc(rproc);
+ } else {
+ if (lx_lpid_lock(pid, curzone, LXP_PRLOCK, &rproc,
+ &rthr) != 0) {
+ return (set_errno(ESRCH));
+ }
+
+ if (rproc->p_model != model ||
+ (rlwp = ttolwp(rthr)) == NULL ||
+ (rlwpd = lwptolxlwp(rlwp)) == NULL) {
+ /*
+ * The target process does not match our data model, or
+ * we couldn't find the LWP, or the target process is
+ * not branded.
+ */
+ err = ESRCH;
+ goto out;
+ }
+ }
+
+ if (curproc != rproc &&
+ priv_proc_cred_perm(curproc->p_cred, rproc, NULL, VREAD) != 0) {
+ /*
+ * We don't have the permission to examine the target.
+ */
+ err = EPERM;
+ goto out;
+ }
+
+ list = rlwpd->br_robust_list;
+
+out:
+ sprunlock(rproc);
+
+ if (err != 0)
+ return (set_errno(err));
+
+ if (model == DATAMODEL_NATIVE) {
+ if (sulword(listp, (uintptr_t)list) != 0)
+ return (set_errno(EFAULT));
+
+ if (sulword(lenp, sizeof (futex_robust_list_t)) != 0)
+ return (set_errno(EFAULT));
+ }
+#if defined(_SYSCALL32_IMPL)
+ else {
+ if (suword32(listp, (uint32_t)(uintptr_t)list) != 0)
+ return (set_errno(EFAULT));
+
+ if (suword32(lenp, sizeof (futex_robust_list32_t)) != 0)
+ return (set_errno(EFAULT));
+ }
+#endif
+
+ return (0);
+}
+
+void
+lx_futex_init(void)
+{
+ int i;
+
+ for (i = 0; i < HASH_SIZE; i++)
+ mutex_init(&futex_hash[i].fh_lock, NULL, MUTEX_DEFAULT, NULL);
+}
+
+int
+lx_futex_fini(void)
+{
+ int i, err;
+
+ err = 0;
+ for (i = 0; (err == 0) && (i < HASH_SIZE); i++) {
+ mutex_enter(&futex_hash[i].fh_lock);
+ if (futex_hash[i].fh_waiters != NULL)
+ err = EBUSY;
+ mutex_exit(&futex_hash[i].fh_lock);
+ }
+ return (err);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_getcwd.c b/usr/src/uts/common/brand/lx/syscall/lx_getcwd.c
new file mode 100644
index 0000000000..275a781fa0
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_getcwd.c
@@ -0,0 +1,52 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+
+/*
+ * getcwd() - Linux syscall semantics are slightly different; we need to return
+ * the length of the pathname copied (+ 1 for the terminating NULL byte.)
+ */
+long
+lx_getcwd(char *buf, int size)
+{
+ int len;
+ int error;
+ vnode_t *vp;
+ char path[MAXPATHLEN + 1];
+
+ mutex_enter(&curproc->p_lock);
+ vp = PTOU(curproc)->u_cdir;
+ VN_HOLD(vp);
+ mutex_exit(&curproc->p_lock);
+ if ((error = vnodetopath(NULL, vp, path, sizeof (path), CRED())) != 0) {
+ VN_RELE(vp);
+ return (set_errno(error));
+ }
+ VN_RELE(vp);
+
+ len = strlen(path) + 1;
+ if (len > size)
+ return (set_errno(ERANGE));
+
+ if (copyout(path, buf, len) != 0)
+ return (set_errno(EFAULT));
+
+ return (len);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_getdents.c b/usr/src/uts/common/brand/lx/syscall/lx_getdents.c
new file mode 100644
index 0000000000..5bde892aea
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_getdents.c
@@ -0,0 +1,416 @@
+/*
+ * 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 2017 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/filio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/inttypes.h>
+#include <sys/vnode.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/sunddi.h>
+
+#include <sys/lx_types.h>
+#include <sys/lx_misc.h>
+
+#define LX_NAMEMAX 256
+
+#define LX_GETDENTS_MAX_BUFSZ 65536
+
+/*
+ * See the comment in our lx_sysfs VFS code for a detailed explanation around
+ * the handling of 'd_type' here.
+ */
+#define LX_DT_UNKNOWN 0
+#define LX_DT_FIFO 1
+#define LX_DT_CHR 2
+#define LX_DT_DIR 4
+#define LX_DT_BLK 6
+#define LX_DT_REG 8
+#define LX_DT_LNK 10
+#define LX_DT_SOCK 12
+
+/*
+ * Set by lx_sysfs when it loads. lx_sysfs depends on the lx_brand module,
+ * so our module has to load first and define the variables that lx_sysfs will
+ * set when it loads.
+ */
+int lx_sysfs_vfs_type;
+int (*lx_sysfs_vtype)(ino_t);
+
+/*
+ * Because the Linux dirent has an extra field (d_type), it's possible that
+ * each entry will be 8 bytes larger (and aligned to 8 bytes) due to padding.
+ * To prevent overrun during translation, the illumos-native buffer is sized
+ * pessimistically.
+ */
+#define LTOS_GETDENTS_BUFSZ(bufsz, datasz) \
+ (((bufsz) / (((datasz) + 15) & ~7)) * sizeof (struct dirent))
+
+/*
+ * Linux d_type offset is at (d_reclen - 1). See the Linux getdents(2) man page.
+ * This macro assumes d_reclen is already set correctly.
+ */
+#define LX_DTYPE(l) *(((char *)l) + (l->d_reclen - 1))
+
+/*
+ * Record must be long enough to house d_name string, null terminator and
+ * d_type field. It's then padded to nearest 8-byte boundary
+ */
+#define LX_RECLEN(l, t) \
+ ((offsetof(t, d_name) + 2 + (l) + 7) & ~7)
+
+/*
+ * Bytes after d_name string until d_reclen should be zeroed.
+ * Includes zero-terminating d_name
+ */
+#define LX_ZEROLEN(l, t) \
+ (LX_RECLEN(l, t) - \
+ ((offsetof(t, d_name) + (l))))
+
+/* The output format of getdents differs if the caller is 32 or 64 bit. */
+struct lx_dirent_32 {
+ uint32_t d_ino;
+ int32_t d_off;
+ ushort_t d_reclen;
+ char d_name[1];
+ uchar_t d_type;
+};
+
+struct lx_dirent_64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ ushort_t d_reclen;
+ char d_name[1];
+ uchar_t d_type;
+};
+
+static long
+lx_getdents_common(int fd, caddr_t uptr, size_t count,
+ unsigned int lx_size, int (*outcb)(caddr_t, caddr_t, int, boolean_t))
+{
+ vnode_t *vp;
+ boolean_t is_sysfs = B_FALSE;
+ file_t *fp;
+ struct uio auio;
+ struct iovec aiov;
+ int error, at_eof;
+ int sbufsz, lbufsz, bufsz;
+ void *lbuf, *sbuf;
+ size_t outb = 0;
+
+ if (count < lx_size) {
+ return (set_errno(EINVAL));
+ }
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ vp = fp->f_vnode;
+ if (vp->v_type != VDIR) {
+ releasef(fd);
+ return (set_errno(ENOTDIR));
+ }
+ if (!(fp->f_flag & FREAD)) {
+ releasef(fd);
+ return (set_errno(EBADF));
+ }
+
+ if (vp->v_vfsp->vfs_fstype == lx_sysfs_vfs_type) {
+ is_sysfs = B_TRUE;
+ }
+
+ if (count > LX_GETDENTS_MAX_BUFSZ) {
+ /*
+ * If the target buffer passed to us is huge, keep the
+ * translation buffers moderate in size. Iteration will be
+ * used to fill the request.
+ */
+ lbufsz = LX_GETDENTS_MAX_BUFSZ;
+ sbufsz = LTOS_GETDENTS_BUFSZ(LX_GETDENTS_MAX_BUFSZ, lx_size);
+ } else if (count < (lx_size + MAXPATHLEN)) {
+ /*
+ * If the target buffer is tiny, allocate a Linux-format buffer
+ * big enough to hold at least one max-length row while keeping
+ * the illumos-format buffer pesimistic in size.
+ *
+ * Assuming the buffer is truely tiny, it's likely that the
+ * result will not fit and an EINVAL will be tossed.
+ */
+ lbufsz = (lx_size + MAXPATHLEN);
+ sbufsz = MAX((LTOS_GETDENTS_BUFSZ(count, lx_size)),
+ sizeof (struct dirent));
+ } else {
+ lbufsz = count;
+ sbufsz = LTOS_GETDENTS_BUFSZ(count, lx_size);
+ }
+ bufsz = sbufsz;
+ lbuf = kmem_alloc(lbufsz, KM_SLEEP);
+ sbuf = kmem_alloc(sbufsz, KM_SLEEP);
+
+ aiov.iov_base = sbuf;
+ aiov.iov_len = sbufsz;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_resid = sbufsz;
+ auio.uio_fmode = 0;
+ auio.uio_extflg = UIO_COPY_CACHED;
+
+ /*
+ * Since we use a conservative buffer allocation for the differing
+ * struct sizing and Linux places fewer limits on getdents buffers in
+ * general, there's a chance we'll undershoot on the record count.
+ * When this happens, we can simply repeat the READDIR operation until
+ * the available records are exhausted or we've filled the user buffer.
+ */
+ do {
+ int res;
+
+ (void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
+ error = VOP_READDIR(vp, &auio, fp->f_cred, &at_eof, NULL, 0);
+ VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
+ if (error != 0 || auio.uio_resid == sbufsz) {
+ break;
+ }
+ res = outcb(sbuf, lbuf, bufsz - auio.uio_resid, is_sysfs);
+ VERIFY(res <= lbufsz);
+ if (res == 0) {
+ /* no records to copyout from this batch */
+ break;
+ } else if (res > count) {
+ /*
+ * For very small buffer sizes, it's possible that a
+ * single record is too large due to a long filename.
+ */
+ error = EINVAL;
+ break;
+ }
+
+ VERIFY(outb + res <= count);
+ if (copyout(lbuf, (void *)(uptr + outb), res) != 0) {
+ error = EFAULT;
+ break;
+ }
+ outb += res;
+
+ /*
+ * We undershot the request buffer.
+ * Reset for another READDIR, taking care not to overshoot.
+ */
+ bufsz = MIN(sbufsz, LTOS_GETDENTS_BUFSZ(count - outb, lx_size));
+ auio.uio_resid = bufsz;
+ aiov.iov_len = bufsz;
+ aiov.iov_base = sbuf;
+
+ /*
+ * Continued progress is allowed only if EOF has not been
+ * reached and there is enough remaining buffer space to hold
+ * an entry with a max-length filename.
+ */
+ } while (at_eof == 0 && (count - outb) >= (lx_size + MAXPATHLEN));
+
+ kmem_free(lbuf, lbufsz);
+ kmem_free(sbuf, sbufsz);
+
+ if (error) {
+ releasef(fd);
+ return (set_errno(error));
+ }
+
+ fp->f_offset = auio.uio_loffset;
+ releasef(fd);
+ return (outb);
+}
+
+static int
+lx_get_sysfs_dtype(ino_t ino)
+{
+ vtype_t vt;
+
+ vt = lx_sysfs_vtype(ino);
+
+ switch (vt) {
+ case VREG: return (LX_DT_REG);
+ case VDIR: return (LX_DT_DIR);
+ case VBLK: return (LX_DT_BLK);
+ case VCHR: return (LX_DT_CHR);
+ case VLNK: return (LX_DT_LNK);
+ case VFIFO: return (LX_DT_FIFO);
+ case VSOCK: return (LX_DT_SOCK);
+ default: return (LX_DT_UNKNOWN);
+ }
+}
+
+static int
+lx_getdents_format32(caddr_t sbuf, caddr_t lbuf, int len, boolean_t is_sysfs)
+{
+ struct dirent *sd;
+ struct lx_dirent_32 *ld;
+ int namelen;
+ int size = 0;
+
+ while (len > 0) {
+ /* LINTED: alignment */
+ sd = (struct dirent *)sbuf;
+ /* LINTED: alignment */
+ ld = (struct lx_dirent_32 *)lbuf;
+ namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1);
+
+ ld->d_ino = sd->d_ino;
+ ld->d_off = sd->d_off;
+ (void) strncpy(ld->d_name, sd->d_name, namelen);
+ ld->d_name[namelen] = 0;
+ ld->d_reclen = (ushort_t)LX_RECLEN(namelen,
+ struct lx_dirent_32);
+ /* Zero out any alignment padding and d_type */
+ bzero(ld->d_name + namelen,
+ LX_ZEROLEN(namelen, struct lx_dirent_32));
+
+ if (is_sysfs) {
+ LX_DTYPE(ld) = lx_get_sysfs_dtype(ld->d_ino);
+ }
+
+ len -= sd->d_reclen;
+ size += ld->d_reclen;
+ sbuf += sd->d_reclen;
+ lbuf += ld->d_reclen;
+ }
+ return (size);
+}
+
+static int
+lx_getdents_format64(caddr_t sbuf, caddr_t lbuf, int len, boolean_t is_sysfs)
+{
+ struct dirent *sd;
+ struct lx_dirent_64 *ld;
+ int namelen;
+ int size = 0;
+
+ while (len > 0) {
+ /* LINTED: alignment */
+ sd = (struct dirent *)sbuf;
+ /* LINTED: alignment */
+ ld = (struct lx_dirent_64 *)lbuf;
+ namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1);
+
+ ld->d_ino = sd->d_ino;
+ ld->d_off = sd->d_off;
+ (void) strncpy(ld->d_name, sd->d_name, namelen);
+ ld->d_name[namelen] = 0;
+ ld->d_reclen = (ushort_t)LX_RECLEN(namelen,
+ struct lx_dirent_64);
+ /* Zero out any alignment padding and d_type */
+ bzero(ld->d_name + namelen,
+ LX_ZEROLEN(namelen, struct lx_dirent_64));
+
+ if (is_sysfs) {
+ LX_DTYPE(ld) = lx_get_sysfs_dtype(ld->d_ino);
+ }
+
+ len -= sd->d_reclen;
+ size += ld->d_reclen;
+ sbuf += sd->d_reclen;
+ lbuf += ld->d_reclen;
+ }
+ return (size);
+}
+
+long
+lx_getdents_32(int fd, caddr_t buf, size_t count)
+{
+ return (lx_getdents_common(fd, buf, count,
+ sizeof (struct lx_dirent_32), lx_getdents_format32));
+}
+
+long
+lx_getdents_64(int fd, caddr_t buf, size_t count)
+{
+ return (lx_getdents_common(fd, buf, count,
+ sizeof (struct lx_dirent_64), lx_getdents_format64));
+}
+
+struct lx_dirent64 {
+ uint64_t d_ino;
+ int64_t d_off;
+ ushort_t d_reclen;
+ uchar_t d_type;
+ char d_name[1];
+};
+
+#define LX_RECLEN64(namelen) \
+ ((offsetof(struct lx_dirent64, d_name) + 1 + (namelen) + 7) & ~7)
+
+#define LX_ZEROLEN64(namelen) \
+ (LX_RECLEN64(namelen) - \
+ ((offsetof(struct lx_dirent64, d_name) + (namelen))))
+
+static int
+lx_getdents64_format(caddr_t sbuf, caddr_t lbuf, int len, boolean_t is_sysfs)
+{
+ struct dirent *sd;
+ struct lx_dirent64 *ld;
+ int namelen;
+ int size = 0;
+
+ while (len > 0) {
+ /* LINTED: alignment */
+ sd = (struct dirent *)sbuf;
+ /* LINTED: alignment */
+ ld = (struct lx_dirent64 *)lbuf;
+ namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1);
+
+ ld->d_ino = sd->d_ino;
+ ld->d_off = sd->d_off;
+ ld->d_type = LX_DT_UNKNOWN;
+ (void) strncpy(ld->d_name, sd->d_name, namelen);
+ ld->d_name[namelen] = 0;
+ ld->d_reclen = (ushort_t)LX_RECLEN64(namelen);
+ /* Zero out any alignment padding */
+ bzero(ld->d_name + namelen, LX_ZEROLEN64(namelen));
+
+ if (is_sysfs) {
+ ld->d_type = lx_get_sysfs_dtype(ld->d_ino);
+ }
+
+ len -= sd->d_reclen;
+ size += ld->d_reclen;
+ sbuf += sd->d_reclen;
+ lbuf += ld->d_reclen;
+ }
+ return (size);
+}
+
+
+long
+lx_getdents64(int fd, caddr_t buf, size_t count)
+{
+ return (lx_getdents_common(fd, buf, count,
+ sizeof (struct lx_dirent64), lx_getdents64_format));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_getpid.c b/usr/src/uts/common/brand/lx/syscall/lx_getpid.c
new file mode 100644
index 0000000000..0ebd93304e
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_getpid.c
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright 2017 Joyent, Inc.
+ */
+
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/cpuvar.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+/*
+ * return the pid
+ */
+long
+lx_getpid(void)
+{
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+ long rv;
+
+ if (curproc->p_pid == curproc->p_zone->zone_proc_initpid) {
+ rv = 1;
+ } else {
+ VERIFY(lwpd != NULL);
+
+ rv = lwpd->br_tgid;
+ }
+
+ return (rv);
+}
+
+/*
+ * return the parent pid
+ */
+long
+lx_getppid(void)
+{
+ return (lx_lwp_ppid(ttolwp(curthread), NULL, NULL));
+}
+
+/*
+ * return the thread id
+ */
+long
+lx_gettid(void)
+{
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+
+ return (lwpd->br_pid == curzone->zone_proc_initpid ? 1 : lwpd->br_pid);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_getrandom.c b/usr/src/uts/common/brand/lx/syscall/lx_getrandom.c
new file mode 100644
index 0000000000..acc4073483
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_getrandom.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 2015 Joyent, Inc.
+ */
+
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+/*
+ * From "uts/common/syscall/getrandom.c":
+ */
+extern int getrandom(void *, size_t, int);
+
+long
+lx_getrandom(void *bufp, size_t buflen, int flags)
+{
+ /*
+ * According to signal(7), calls to getrandom(2) are restartable.
+ */
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+
+ return (getrandom(bufp, buflen, flags));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_id.c b/usr/src/uts/common/brand/lx/syscall/lx_id.c
new file mode 100644
index 0000000000..67f0fc9e5e
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_id.c
@@ -0,0 +1,509 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/zone.h>
+#include <sys/cred.h>
+#include <sys/cred_impl.h>
+#include <sys/policy.h>
+#include <sys/lx_types.h>
+
+#define LX_NGROUPS_MAX 32
+
+/* From usr/src/uts/common/syscall/gid.c & uid.c */
+extern int setgid(gid_t);
+extern int setregid(gid_t, gid_t);
+extern int setreuid(uid_t, uid_t);
+extern int setuid(uid_t);
+
+/* From usr/src/uts/common/syscall/groups.c */
+extern int setgroups(int, gid_t *);
+
+long
+lx_getegid(void)
+{
+ return (crgetgid(CRED()));
+}
+
+long
+lx_getegid16(void)
+{
+ return ((int)LX_GID32_TO_GID16(crgetgid(CRED())));
+}
+
+long
+lx_geteuid(void)
+{
+ return (crgetuid(CRED()));
+}
+
+long
+lx_geteuid16(void)
+{
+ return ((int)LX_UID32_TO_UID16(crgetuid(CRED())));
+}
+
+long
+lx_getgid(void)
+{
+ return (crgetrgid(CRED()));
+}
+
+long
+lx_getgid16(void)
+{
+ return ((int)LX_GID32_TO_GID16(crgetrgid(CRED())));
+}
+
+long
+lx_getuid(void)
+{
+ return (crgetruid(CRED()));
+}
+
+long
+lx_getuid16(void)
+{
+ return ((int)LX_UID32_TO_UID16(crgetruid(CRED())));
+}
+
+long
+lx_setgid(gid_t gid)
+{
+ return (setgid(gid));
+}
+
+long
+lx_setgid16(lx_gid16_t gid)
+{
+ return (setgid(LX_GID16_TO_GID32(gid)));
+}
+
+long
+lx_setregid(gid_t rgid, gid_t egid)
+{
+ return (setregid(rgid, egid));
+}
+
+long
+lx_setregid16(lx_gid16_t rgid, lx_gid16_t egid)
+{
+ return (setregid(LX_UID16_TO_UID32(rgid), LX_UID16_TO_UID32(egid)));
+}
+
+long
+lx_setreuid(uid_t ruid, uid_t euid)
+{
+ return (setreuid(ruid, euid));
+}
+
+long
+lx_setreuid16(lx_uid16_t ruid, lx_uid16_t euid)
+{
+ return (setreuid(LX_UID16_TO_UID32(ruid), LX_UID16_TO_UID32(euid)));
+}
+
+long
+lx_setuid(uid_t uid)
+{
+ return (setuid(uid));
+}
+
+long
+lx_setuid16(lx_uid16_t uid)
+{
+ return (setuid(LX_UID16_TO_UID32(uid)));
+}
+
+/*
+ * This function is based on setreuid in common/syscall/uid.c and exists
+ * because illumos does not have a way to explicitly set the saved uid (suid)
+ * from any other system call.
+ */
+long
+lx_setresuid(lx_uid_t ruid, lx_uid_t euid, lx_uid_t suid)
+{
+ proc_t *p;
+ int error = 0;
+ int do_nocd = 0;
+ int uidchge = 0;
+ uid_t oldruid = ruid;
+ cred_t *cr, *newcr;
+ zoneid_t zoneid = getzoneid();
+
+ if ((ruid != -1 && (ruid > MAXUID)) ||
+ (euid != -1 && (euid > MAXUID)) ||
+ (suid != -1 && (suid > MAXUID))) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /*
+ * Need to pre-allocate the new cred structure before grabbing
+ * the p_crlock mutex.
+ */
+ newcr = cralloc();
+
+ p = ttoproc(curthread);
+
+retry:
+ mutex_enter(&p->p_crlock);
+ cr = p->p_cred;
+
+ if (ruid != -1 &&
+ ruid != cr->cr_ruid && ruid != cr->cr_uid &&
+ ruid != cr->cr_suid && secpolicy_allow_setid(cr, ruid, B_FALSE)) {
+ error = EPERM;
+ } else if (euid != -1 &&
+ euid != cr->cr_ruid && euid != cr->cr_uid &&
+ euid != cr->cr_suid && secpolicy_allow_setid(cr, euid, B_FALSE)) {
+ error = EPERM;
+ } else if (suid != -1 &&
+ suid != cr->cr_ruid && suid != cr->cr_uid &&
+ suid != cr->cr_suid && secpolicy_allow_setid(cr, suid, B_FALSE)) {
+ error = EPERM;
+ } else {
+ if (!uidchge && ruid != -1 && cr->cr_ruid != ruid) {
+ /*
+ * The ruid of the process is going to change. In order
+ * to avoid a race condition involving the
+ * process count associated with the newly given ruid,
+ * we increment the count before assigning the
+ * credential to the process.
+ * To do that, we'll have to take pidlock, so we first
+ * release p_crlock.
+ */
+ mutex_exit(&p->p_crlock);
+ uidchge = 1;
+ mutex_enter(&pidlock);
+ upcount_inc(ruid, zoneid);
+ mutex_exit(&pidlock);
+ /*
+ * As we released p_crlock we can't rely on the cr
+ * we read. So retry the whole thing.
+ */
+ goto retry;
+ }
+ crhold(cr);
+ crcopy_to(cr, newcr);
+ p->p_cred = newcr;
+
+ if (euid != -1)
+ newcr->cr_uid = euid;
+ if (suid != -1)
+ newcr->cr_suid = suid;
+ if (ruid != -1) {
+ oldruid = newcr->cr_ruid;
+ newcr->cr_ruid = ruid;
+ ASSERT(ruid != oldruid ? uidchge : 1);
+ }
+
+ /*
+ * A process that gives up its privilege
+ * must be marked to produce no core dump.
+ */
+ if ((cr->cr_uid != newcr->cr_uid ||
+ cr->cr_ruid != newcr->cr_ruid ||
+ cr->cr_suid != newcr->cr_suid))
+ do_nocd = 1;
+
+ crfree(cr);
+ }
+ mutex_exit(&p->p_crlock);
+
+ /*
+ * We decrement the number of processes associated with the oldruid
+ * to match the increment above, even if the ruid of the process
+ * did not change or an error occurred (oldruid == uid).
+ */
+ if (uidchge) {
+ ASSERT(oldruid != -1 && ruid != -1);
+ mutex_enter(&pidlock);
+ upcount_dec(oldruid, zoneid);
+ mutex_exit(&pidlock);
+ }
+
+ if (error == 0) {
+ if (do_nocd) {
+ mutex_enter(&p->p_lock);
+ p->p_flag |= SNOCD;
+ mutex_exit(&p->p_lock);
+ }
+ crset(p, newcr); /* broadcast to process threads */
+ goto done;
+ }
+ crfree(newcr);
+done:
+ if (error)
+ return (set_errno(error));
+ else
+ return (0);
+}
+
+long
+lx_setresuid16(lx_uid16_t ruid16, lx_uid16_t euid16, lx_uid16_t suid16)
+{
+ long rval;
+
+ rval = lx_setresuid(
+ LX_UID16_TO_UID32(ruid16),
+ LX_UID16_TO_UID32(euid16),
+ LX_UID16_TO_UID32(suid16));
+
+ return (rval);
+}
+
+/*
+ * This function is based on setregid in common/syscall/gid.c
+ */
+long
+lx_setresgid(lx_gid_t rgid, lx_gid_t egid, lx_gid_t sgid)
+{
+ proc_t *p;
+ int error = 0;
+ int do_nocd = 0;
+ cred_t *cr, *newcr;
+
+ if ((rgid != -1 && (rgid > MAXUID)) ||
+ (egid != -1 && (egid > MAXUID)) ||
+ (sgid != -1 && (sgid > MAXUID))) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /*
+ * Need to pre-allocate the new cred structure before grabbing
+ * the p_crlock mutex.
+ */
+ newcr = cralloc();
+
+ p = ttoproc(curthread);
+ mutex_enter(&p->p_crlock);
+ cr = p->p_cred;
+
+ if (rgid != -1 &&
+ rgid != cr->cr_rgid && rgid != cr->cr_gid &&
+ rgid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) {
+ error = EPERM;
+ } else if (egid != -1 &&
+ egid != cr->cr_rgid && egid != cr->cr_gid &&
+ egid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) {
+ error = EPERM;
+ } else if (sgid != -1 &&
+ sgid != cr->cr_rgid && sgid != cr->cr_gid &&
+ sgid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) {
+ error = EPERM;
+ } else {
+ crhold(cr);
+ crcopy_to(cr, newcr);
+ p->p_cred = newcr;
+
+ if (egid != -1)
+ newcr->cr_gid = egid;
+ if (sgid != -1)
+ newcr->cr_sgid = sgid;
+ if (rgid != -1)
+ newcr->cr_rgid = rgid;
+
+ /*
+ * A process that gives up its privilege
+ * must be marked to produce no core dump.
+ */
+ if ((cr->cr_gid != newcr->cr_gid ||
+ cr->cr_rgid != newcr->cr_rgid ||
+ cr->cr_sgid != newcr->cr_sgid))
+ do_nocd = 1;
+
+ crfree(cr);
+ }
+ mutex_exit(&p->p_crlock);
+
+ if (error == 0) {
+ if (do_nocd) {
+ mutex_enter(&p->p_lock);
+ p->p_flag |= SNOCD;
+ mutex_exit(&p->p_lock);
+ }
+ crset(p, newcr); /* broadcast to process threads */
+ goto done;
+ }
+ crfree(newcr);
+done:
+ if (error)
+ return (set_errno(error));
+ else
+ return (0);
+}
+
+long
+lx_setresgid16(lx_gid16_t rgid16, lx_gid16_t egid16, lx_gid16_t sgid16)
+{
+ long rval;
+
+ rval = lx_setresgid(
+ LX_GID16_TO_GID32(rgid16),
+ LX_GID16_TO_GID32(egid16),
+ LX_GID16_TO_GID32(sgid16));
+
+ return (rval);
+}
+
+/*
+ * Linux defines NGROUPS_MAX to be 32, but on illumos it is only 16. We employ
+ * the terrible hack below so that tests may proceed, if only on DEBUG kernels.
+ */
+int
+lx_helper_setgroups(int ngroups, gid_t *grouplist)
+{
+#ifdef DEBUG
+ if (ngroups > ngroups_max && ngroups <= LX_NGROUPS_MAX)
+ ngroups = ngroups_max;
+#endif /* DEBUG */
+
+ return (setgroups(ngroups, grouplist));
+}
+
+long
+lx_getresuid(lx_uid_t *ruid, lx_uid_t *euid, lx_uid_t *suid)
+{
+ lx_uid_t lx_ruid, lx_euid, lx_suid;
+ cred_t *cr = CRED();
+
+ lx_ruid = (lx_uid_t)crgetruid(cr);
+ lx_euid = (lx_uid_t)crgetuid(cr);
+ lx_suid = (lx_uid_t)crgetsuid(cr);
+
+ if (copyout(&lx_ruid, (void *)ruid, sizeof (lx_uid_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_euid, (void *)euid, sizeof (lx_uid_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_suid, (void *)suid, sizeof (lx_uid_t)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+long
+lx_getresuid16(lx_uid16_t *ruid16, lx_uid16_t *euid16, lx_uid16_t *suid16)
+{
+ lx_uid16_t lx_ruid16, lx_euid16, lx_suid16;
+ cred_t *cr = CRED();
+
+ lx_ruid16 = LX_UID32_TO_UID16((lx_uid_t)crgetruid(cr));
+ lx_euid16 = LX_UID32_TO_UID16((lx_uid_t)crgetuid(cr));
+ lx_suid16 = LX_UID32_TO_UID16((lx_uid_t)crgetsuid(cr));
+
+ if (copyout(&lx_ruid16, (void *)ruid16, sizeof (lx_uid16_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_euid16, (void *)euid16, sizeof (lx_uid16_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_suid16, (void *)suid16, sizeof (lx_uid16_t)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+long
+lx_getresgid(lx_gid_t *rgid, lx_gid_t *egid, lx_gid_t *sgid)
+{
+ lx_gid_t lx_rgid, lx_egid, lx_sgid;
+ cred_t *cr = CRED();
+
+ lx_rgid = (lx_gid_t)crgetrgid(cr);
+ lx_egid = (lx_gid_t)crgetgid(cr);
+ lx_sgid = (lx_gid_t)crgetsgid(cr);
+
+ if (copyout(&lx_rgid, (void *)rgid, sizeof (lx_gid_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_egid, (void *)egid, sizeof (lx_gid_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_sgid, (void *)sgid, sizeof (lx_gid_t)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+long
+lx_getresgid16(lx_gid16_t *rgid16, lx_gid16_t *egid16, lx_gid16_t *sgid16)
+{
+ lx_gid16_t lx_rgid16, lx_egid16, lx_sgid16;
+ cred_t *cr = CRED();
+
+ lx_rgid16 = LX_GID32_TO_GID16((lx_gid_t)crgetrgid(cr));
+ lx_egid16 = LX_GID32_TO_GID16((lx_gid_t)crgetgid(cr));
+ lx_sgid16 = LX_GID32_TO_GID16((lx_gid_t)crgetsgid(cr));
+
+ if (copyout(&lx_rgid16, (void *)rgid16, sizeof (lx_gid16_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_egid16, (void *)egid16, sizeof (lx_gid16_t)) != 0)
+ return (set_errno(EFAULT));
+ if (copyout(&lx_sgid16, (void *)sgid16, sizeof (lx_gid16_t)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/*
+ * The lx brand cannot support the setfs[ug]id16/setfs[ug]id calls as that
+ * would require significant rework of the illumos privilege mechanisms, so
+ * instead return the current effective [ug]id.
+ *
+ * In Linux, fsids track effective IDs, so returning the effective IDs works
+ * as a substitute; returning the current value also denotes failure of the
+ * call if the caller had specified something different. We don't need to
+ * worry about setting error codes because the Linux calls don't set any.
+ */
+/*ARGSUSED*/
+long
+lx_setfsuid16(uid_t fsuid16)
+{
+ return ((int)LX_UID32_TO_UID16(crgetuid(CRED())));
+}
+
+/*ARGSUSED*/
+long
+lx_setfsgid16(gid_t fsgid16)
+{
+ return ((int)LX_GID32_TO_GID16(crgetgid(CRED())));
+}
+
+/*ARGSUSED*/
+long
+lx_setfsuid(uid_t fsuid)
+{
+ return (crgetuid(CRED()));
+}
+
+/*ARGSUSED*/
+long
+lx_setfsgid(gid_t fsgid)
+{
+ return (crgetgid(CRED()));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c b/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c
new file mode 100644
index 0000000000..f745a90e41
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c
@@ -0,0 +1,1901 @@
+/*
+ * 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 2019 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/file.h>
+#include <sys/filio.h>
+#include <sys/vnode.h>
+#include <sys/fcntl.h>
+#include <sys/termio.h>
+#include <sys/termios.h>
+#include <sys/ptyvar.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/sockio.h>
+#include <sys/stropts.h>
+#include <sys/ptms.h>
+#include <sys/cred.h>
+#include <sys/cred_impl.h>
+#include <sys/sysmacros.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_ptm.h>
+#include <sys/brand.h>
+#include <sys/sunddi.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/session.h>
+#include <sys/kmem.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <net/if_arp.h>
+#include <sys/ioccom.h>
+#include <sys/dtrace.h>
+#include <sys/ethernet.h>
+#include <sys/dlpi.h>
+#include <sys/lx_autofs.h>
+#include <sys/netstack.h>
+#include <inet/ip.h>
+#include <inet/ip_if.h>
+#include <sys/dkio.h>
+#include <sys/sdt.h>
+
+/*
+ * Linux ioctl types
+ */
+#define LX_IOC_TYPE_HD 0x03
+#define LX_IOC_TYPE_BLK 0x12
+#define LX_IOC_TYPE_FD 0x54
+#define LX_IOC_TYPE_DTRACE 0x68
+#define LX_IOC_TYPE_SOCK 0x89
+#define LX_IOC_TYPE_AUTOFS 0x93
+
+/*
+ * Supported ioctls
+ */
+#define LX_HDIO_GETGEO 0x0301
+#define LX_BLKGETSIZE 0x1260
+#define LX_BLKSSZGET 0x1268
+#define LX_BLKGETSIZE64 0x80081272
+#define LX_TCGETS 0x5401
+#define LX_TCSETS 0x5402
+#define LX_TCSETSW 0x5403
+#define LX_TCSETSF 0x5404
+#define LX_TCGETA 0x5405
+#define LX_TCSETA 0x5406
+#define LX_TCSETAW 0x5407
+#define LX_TCSETAF 0x5408
+#define LX_TCSBRK 0x5409
+#define LX_TCXONC 0x540a
+#define LX_TCFLSH 0x540b
+#define LX_TIOCEXCL 0x540c
+#define LX_TIOCNXCL 0x540d
+#define LX_TIOCSCTTY 0x540e
+#define LX_TIOCGPGRP 0x540f
+#define LX_TIOCSPGRP 0x5410
+#define LX_TIOCOUTQ 0x5411
+#define LX_TIOCSTI 0x5412
+#define LX_TIOCGWINSZ 0x5413
+#define LX_TIOCSWINSZ 0x5414
+#define LX_TIOCMGET 0x5415
+#define LX_TIOCMBIS 0x5416
+#define LX_TIOCMBIC 0x5417
+#define LX_TIOCMSET 0x5418
+#define LX_TIOCGSOFTCAR 0x5419
+#define LX_TIOCSSOFTCAR 0x541a
+#define LX_FIONREAD 0x541b
+#define LX_TIOCPKT 0x5420
+#define LX_FIONBIO 0x5421
+#define LX_TIOCNOTTY 0x5422
+#define LX_TIOCSETD 0x5423
+#define LX_TIOCGETD 0x5424
+#define LX_TCSBRKP 0x5425
+#define LX_TIOCGSID 0x5429
+#define LX_TIOCGPTN 0x80045430
+#define LX_TIOCSPTLCK 0x40045431
+#define LX_FIONCLEX 0x5450
+#define LX_FIOCLEX 0x5451
+#define LX_FIOASYNC 0x5452
+#define LX_FIOSETOWN 0x8901
+#define LX_SIOCSPGRP 0x8902
+#define LX_FIOGETOWN 0x8903
+#define LX_SIOCGPGRP 0x8904
+#define LX_SIOCATMARK 0x8905
+#define LX_SIOCGSTAMP 0x8906
+#define LX_SIOCADDRT 0x890b
+#define LX_SIOCDELRT 0x890c
+#define LX_SIOCRTMSG 0x890d
+#define LX_SIOCGIFNAME 0x8910
+#define LX_SIOCSIFLINK 0x8911
+#define LX_SIOCGIFCONF 0x8912
+#define LX_SIOCGIFFLAGS 0x8913
+#define LX_SIOCSIFFLAGS 0x8914
+#define LX_SIOCGIFADDR 0x8915
+#define LX_SIOCSIFADDR 0x8916
+#define LX_SIOCGIFDSTADDR 0x8917
+#define LX_SIOCSIFDSTADDR 0x8918
+#define LX_SIOCGIFBRDADDR 0x8919
+#define LX_SIOCSIFBRDADDR 0x891a
+#define LX_SIOCGIFNETMASK 0x891b
+#define LX_SIOCSIFNETMASK 0x891c
+#define LX_SIOCGIFMETRIC 0x891d
+#define LX_SIOCSIFMETRIC 0x891e
+#define LX_SIOCGIFMEM 0x891f
+#define LX_SIOCSIFMEM 0x8920
+#define LX_SIOCGIFMTU 0x8921
+#define LX_SIOCSIFMTU 0x8922
+#define LX_SIOCSIFNAME 0x8923
+#define LX_SIOCSIFHWADDR 0x8924
+#define LX_SIOCGIFENCAP 0x8925
+#define LX_SIOCSIFENCAP 0x8926
+#define LX_SIOCGIFHWADDR 0x8927
+#define LX_SIOCGIFSLAVE 0x8929
+#define LX_SIOCSIFSLAVE 0x8930
+#define LX_SIOCADDMULTI 0x8931
+#define LX_SIOCDELMULTI 0x8932
+#define LX_SIOCGIFINDEX 0x8933
+#define LX_SIOCSIFPFLAGS 0x8934
+#define LX_SIOCGIFPFLAGS 0x8935
+#define LX_SIOCDIFADDR 0x8936
+#define LX_SIOCSIFHWBROADCAST 0x8937
+#define LX_SIOCGIFCOUNT 0x8938
+#define LX_SIOCGIFBR 0x8940
+#define LX_SIOCSIFBR 0x8941
+#define LX_SIOCGIFTXQLEN 0x8942
+#define LX_SIOCSIFTXQLEN 0x8943
+#define LX_SIOCETHTOOL 0x8946
+#define LX_SIOCGMIIPHY 0x8947
+#define LX_SIOCGMIIREG 0x8948
+#define LX_SIOCSMIIREG 0x8949
+#define LX_SIOCWANDEV 0x894a
+#define LX_SIOCOUTQNSD 0x894b
+#define LX_SIOCDARP 0x8953
+#define LX_SIOCGARP 0x8954
+#define LX_SIOCSARP 0x8955
+#define LX_SIOCDRARP 0x8960
+#define LX_SIOCGRARP 0x8961
+#define LX_SIOCSRARP 0x8962
+#define LX_SIOCGIFMAP 0x8970
+#define LX_SIOCSIFMAP 0x8971
+#define LX_SIOCADDDLCI 0x8980
+#define LX_SIOCDELDLCI 0x8981
+#define LX_SIOCGIFVLAN 0x8982
+#define LX_SIOCSIFVLAN 0x8983
+#define LX_SIOCBONDENSLAVE 0x8990
+#define LX_SIOCBONDRELEASE 0x8991
+#define LX_SIOCBONDSETHWADDR 0x8992
+#define LX_SIOCBONDSLAVEINFOQUERY 0x8993
+#define LX_SIOCBONDINFOQUERY 0x8994
+#define LX_SIOCBONDCHANGEACTIVE 0x8995
+#define LX_SIOCBRADDBR 0x89a0
+#define LX_SIOCBRDELBR 0x89a1
+#define LX_SIOCBRADDIF 0x89a2
+#define LX_SIOCBRDELIF 0x89a3
+#define LX_SIOCSHWTSTAMP 0x89b0
+#define LX_SIOCGHWTSTAMP 0x89b1
+#define LX_SIOCDEVPRIVATE 0x89f0
+#define LX_SIOCPROTOPRIVATE 0x89e0
+
+#define FLUSER(fp) fp->f_flag | get_udatamodel()
+#define FLFAKE(fp) fp->f_flag | FKIOCTL
+
+/*
+ * LX_NCC must be different from LX_NCCS since while the termio and termios
+ * structures may look similar they are fundamentally different sizes and
+ * have different members.
+ */
+#define LX_NCC 8
+#define LX_NCCS 19
+
+struct lx_termio {
+ unsigned short c_iflag; /* input mode flags */
+ unsigned short c_oflag; /* output mode flags */
+ unsigned short c_cflag; /* control mode flags */
+ unsigned short c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[LX_NCC]; /* control characters */
+};
+
+struct lx_termios {
+ uint32_t c_iflag; /* input mode flags */
+ uint32_t c_oflag; /* output mode flags */
+ uint32_t c_cflag; /* control mode flags */
+ uint32_t c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[LX_NCCS]; /* control characters */
+};
+
+/*
+ * c_cc characters which are valid for lx_termio and lx_termios
+ */
+#define LX_VINTR 0
+#define LX_VQUIT 1
+#define LX_VERASE 2
+#define LX_VKILL 3
+#define LX_VEOF 4
+#define LX_VTIME 5
+#define LX_VMIN 6
+#define LX_VSWTC 7
+
+/*
+ * c_cc characters which are valid for lx_termios
+ */
+#define LX_VSTART 8
+#define LX_VSTOP 9
+#define LX_VSUSP 10
+#define LX_VEOL 11
+#define LX_VREPRINT 12
+#define LX_VDISCARD 13
+#define LX_VWERASE 14
+#define LX_VLNEXT 15
+#define LX_VEOL2 16
+
+/*
+ * Defaults needed for SunOS to Linux format conversion.
+ * See INIT_C_CC in linux-stable/include/asm-generic/termios.h
+ */
+#define LX_DEF_VTIME 0
+#define LX_DEF_VMIN 1
+#define LX_DEF_VEOF '\004'
+#define LX_DEF_VEOL 0
+
+/* VSD key for lx_cc information */
+static uint_t lx_ioctl_vsd = 0;
+
+
+/* Terminal helpers */
+
+static void
+l2s_termios(struct lx_termios *l_tios, struct termios *s_tios)
+{
+ ASSERT((l_tios != NULL) && (s_tios != NULL));
+
+ bzero(s_tios, sizeof (*s_tios));
+
+ s_tios->c_iflag = l_tios->c_iflag;
+ s_tios->c_oflag = l_tios->c_oflag;
+ s_tios->c_cflag = l_tios->c_cflag;
+ s_tios->c_lflag = l_tios->c_lflag;
+
+ if (s_tios->c_lflag & ICANON) {
+ s_tios->c_cc[VEOF] = l_tios->c_cc[LX_VEOF];
+ s_tios->c_cc[VEOL] = l_tios->c_cc[LX_VEOL];
+ } else {
+ s_tios->c_cc[VMIN] = l_tios->c_cc[LX_VMIN];
+ s_tios->c_cc[VTIME] = l_tios->c_cc[LX_VTIME];
+ }
+
+ s_tios->c_cc[VEOL2] = l_tios->c_cc[LX_VEOL2];
+ s_tios->c_cc[VERASE] = l_tios->c_cc[LX_VERASE];
+ s_tios->c_cc[VKILL] = l_tios->c_cc[LX_VKILL];
+ s_tios->c_cc[VREPRINT] = l_tios->c_cc[LX_VREPRINT];
+ s_tios->c_cc[VLNEXT] = l_tios->c_cc[LX_VLNEXT];
+ s_tios->c_cc[VWERASE] = l_tios->c_cc[LX_VWERASE];
+ s_tios->c_cc[VINTR] = l_tios->c_cc[LX_VINTR];
+ s_tios->c_cc[VQUIT] = l_tios->c_cc[LX_VQUIT];
+ s_tios->c_cc[VSWTCH] = l_tios->c_cc[LX_VSWTC];
+ s_tios->c_cc[VSTART] = l_tios->c_cc[LX_VSTART];
+ s_tios->c_cc[VSTOP] = l_tios->c_cc[LX_VSTOP];
+ s_tios->c_cc[VSUSP] = l_tios->c_cc[LX_VSUSP];
+ s_tios->c_cc[VDISCARD] = l_tios->c_cc[LX_VDISCARD];
+}
+
+static void
+l2s_termio(struct lx_termio *l_tio, struct termio *s_tio)
+{
+ ASSERT((l_tio != NULL) && (s_tio != NULL));
+
+ bzero(s_tio, sizeof (*s_tio));
+
+ s_tio->c_iflag = l_tio->c_iflag;
+ s_tio->c_oflag = l_tio->c_oflag;
+ s_tio->c_cflag = l_tio->c_cflag;
+ s_tio->c_lflag = l_tio->c_lflag;
+
+ if (s_tio->c_lflag & ICANON) {
+ s_tio->c_cc[VEOF] = l_tio->c_cc[LX_VEOF];
+ } else {
+ s_tio->c_cc[VMIN] = l_tio->c_cc[LX_VMIN];
+ s_tio->c_cc[VTIME] = l_tio->c_cc[LX_VTIME];
+ }
+
+ s_tio->c_cc[VINTR] = l_tio->c_cc[LX_VINTR];
+ s_tio->c_cc[VQUIT] = l_tio->c_cc[LX_VQUIT];
+ s_tio->c_cc[VERASE] = l_tio->c_cc[LX_VERASE];
+ s_tio->c_cc[VKILL] = l_tio->c_cc[LX_VKILL];
+ s_tio->c_cc[VSWTCH] = l_tio->c_cc[LX_VSWTC];
+}
+
+static void
+termios2lx_cc(struct lx_termios *l_tios, struct lx_cc *lio)
+{
+ ASSERT((l_tios != NULL) && (lio != NULL));
+
+ bzero(lio, sizeof (*lio));
+
+ lio->veof = l_tios->c_cc[LX_VEOF];
+ lio->veol = l_tios->c_cc[LX_VEOL];
+ lio->vmin = l_tios->c_cc[LX_VMIN];
+ lio->vtime = l_tios->c_cc[LX_VTIME];
+}
+
+static void
+termio2lx_cc(struct lx_termio *l_tio, struct lx_cc *lio)
+{
+ ASSERT((l_tio != NULL) && (lio != NULL));
+
+ bzero(lio, sizeof (*lio));
+
+ lio->veof = l_tio->c_cc[LX_VEOF];
+ lio->veol = 0;
+ lio->vmin = l_tio->c_cc[LX_VMIN];
+ lio->vtime = l_tio->c_cc[LX_VTIME];
+}
+
+static void
+s2l_termios(struct termios *s_tios, struct lx_termios *l_tios)
+{
+ ASSERT((s_tios != NULL) && (l_tios != NULL));
+
+ bzero(l_tios, sizeof (*l_tios));
+
+ l_tios->c_iflag = s_tios->c_iflag;
+ l_tios->c_oflag = s_tios->c_oflag;
+ l_tios->c_cflag = s_tios->c_cflag;
+ l_tios->c_lflag = s_tios->c_lflag;
+
+ /*
+ * Since use of the VMIN/VTIME and VEOF/VEOL control characters is
+ * mutually exclusive (determined by ICANON), SunOS aliases them in the
+ * c_cc field in termio/termios. Linux does not perform this aliasing,
+ * so it expects that the default values are present regardless of
+ * ICANON status.
+ *
+ * These defaults can be overridden later by any values stored via the
+ * lx_cc mechanism.
+ */
+ if (s_tios->c_lflag & ICANON) {
+ l_tios->c_cc[LX_VEOF] = s_tios->c_cc[VEOF];
+ l_tios->c_cc[LX_VEOL] = s_tios->c_cc[VEOL];
+ l_tios->c_cc[LX_VTIME] = LX_DEF_VTIME;
+ l_tios->c_cc[LX_VMIN] = LX_DEF_VMIN;
+
+ } else {
+ l_tios->c_cc[LX_VMIN] = s_tios->c_cc[VMIN];
+ l_tios->c_cc[LX_VTIME] = s_tios->c_cc[VTIME];
+ l_tios->c_cc[LX_VEOF] = LX_DEF_VEOF;
+ l_tios->c_cc[LX_VEOL] = LX_DEF_VEOL;
+ }
+
+ l_tios->c_cc[LX_VEOL2] = s_tios->c_cc[VEOL2];
+ l_tios->c_cc[LX_VERASE] = s_tios->c_cc[VERASE];
+ l_tios->c_cc[LX_VKILL] = s_tios->c_cc[VKILL];
+ l_tios->c_cc[LX_VREPRINT] = s_tios->c_cc[VREPRINT];
+ l_tios->c_cc[LX_VLNEXT] = s_tios->c_cc[VLNEXT];
+ l_tios->c_cc[LX_VWERASE] = s_tios->c_cc[VWERASE];
+ l_tios->c_cc[LX_VINTR] = s_tios->c_cc[VINTR];
+ l_tios->c_cc[LX_VQUIT] = s_tios->c_cc[VQUIT];
+ l_tios->c_cc[LX_VSWTC] = s_tios->c_cc[VSWTCH];
+ l_tios->c_cc[LX_VSTART] = s_tios->c_cc[VSTART];
+ l_tios->c_cc[LX_VSTOP] = s_tios->c_cc[VSTOP];
+ l_tios->c_cc[LX_VSUSP] = s_tios->c_cc[VSUSP];
+ l_tios->c_cc[LX_VDISCARD] = s_tios->c_cc[VDISCARD];
+}
+
+static void
+s2l_termio(struct termio *s_tio, struct lx_termio *l_tio)
+{
+ ASSERT((s_tio != NULL) && (l_tio != NULL));
+
+ bzero(l_tio, sizeof (*l_tio));
+
+ l_tio->c_iflag = s_tio->c_iflag;
+ l_tio->c_oflag = s_tio->c_oflag;
+ l_tio->c_cflag = s_tio->c_cflag;
+ l_tio->c_lflag = s_tio->c_lflag;
+
+ if (s_tio->c_lflag & ICANON) {
+ l_tio->c_cc[LX_VEOF] = s_tio->c_cc[VEOF];
+ l_tio->c_cc[LX_VTIME] = LX_DEF_VTIME;
+ l_tio->c_cc[LX_VMIN] = LX_DEF_VMIN;
+ } else {
+ l_tio->c_cc[LX_VMIN] = s_tio->c_cc[VMIN];
+ l_tio->c_cc[LX_VTIME] = s_tio->c_cc[VTIME];
+ l_tio->c_cc[LX_VEOF] = LX_DEF_VEOF;
+ }
+
+ l_tio->c_cc[LX_VINTR] = s_tio->c_cc[VINTR];
+ l_tio->c_cc[LX_VQUIT] = s_tio->c_cc[VQUIT];
+ l_tio->c_cc[LX_VERASE] = s_tio->c_cc[VERASE];
+ l_tio->c_cc[LX_VKILL] = s_tio->c_cc[VKILL];
+ l_tio->c_cc[LX_VSWTC] = s_tio->c_cc[VSWTCH];
+}
+
+static void
+set_lx_cc(vnode_t *vp, struct lx_cc *lio)
+{
+ struct lx_cc *cur;
+ /*
+ * Linux expects that the termio/termios control characters are
+ * preserved more strictly than illumos supports. In order to preserve
+ * the illusion that the characters are maintained, they are stored as
+ * vnode-specific data.
+ */
+ mutex_enter(&vp->v_vsd_lock);
+ cur = (struct lx_cc *)vsd_get(vp, lx_ioctl_vsd);
+ if (cur == NULL) {
+ cur = kmem_alloc(sizeof (struct lx_cc), KM_SLEEP);
+ bcopy(lio, cur, sizeof (struct lx_cc));
+ (void) vsd_set(vp, lx_ioctl_vsd, cur);
+ } else {
+ bcopy(lio, cur, sizeof (struct lx_cc));
+ }
+ mutex_exit(&vp->v_vsd_lock);
+}
+
+static int
+get_lx_cc(vnode_t *vp, struct lx_cc *lio)
+{
+ struct lx_cc *cur;
+ int rv = 1;
+ mutex_enter(&vp->v_vsd_lock);
+ cur = (struct lx_cc *)vsd_get(vp, lx_ioctl_vsd);
+ if (cur != NULL) {
+ bcopy(cur, lio, sizeof (*lio));
+ rv = 0;
+ }
+ mutex_exit(&vp->v_vsd_lock);
+ return (rv);
+}
+
+/* Socket helpers */
+
+typedef struct lx_ifreq32 {
+ char ifr_name[IFNAMSIZ];
+ union {
+ struct sockaddr ifru_addr;
+ } ifr_ifrn;
+} lx_ifreq32_t;
+
+typedef struct lx_ifreq64 {
+ char ifr_name[IFNAMSIZ];
+ union {
+ struct sockaddr ifru_addr;
+ /* pad this out to the Linux size */
+ uint64_t ifmap[3];
+ } ifr_ifrn;
+} lx_ifreq64_t;
+
+typedef struct lx_ifconf32 {
+ int32_t if_len;
+ caddr32_t if_buf;
+} lx_ifconf32_t;
+
+typedef struct lx_ifconf64 {
+ int32_t if_len;
+ caddr_t if_buf;
+} lx_ifconf64_t;
+
+
+/* Generic translators */
+
+/* ARGSUSED */
+static int
+ict_pass(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ int error = 0;
+ int rv;
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+ NULL);
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_fionbio(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ vnode_t *vp;
+ int32_t iflag, flags;
+ int error;
+
+ if (copyin((caddr_t)arg, &iflag, sizeof (iflag)))
+ return (set_errno(EFAULT));
+
+ mutex_enter(&fp->f_tlock);
+ vp = fp->f_vnode;
+ flags = fp->f_flag;
+ /* Linux sets NONBLOCK instead of FIONBIO */
+ if (iflag)
+ flags |= FNONBLOCK;
+ else
+ flags &= ~FNONBLOCK;
+ /* push the flag down */
+ error = VOP_SETFL(vp, fp->f_flag, flags, fp->f_cred, NULL);
+ fp->f_flag = flags;
+ mutex_exit(&fp->f_tlock);
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_fionread(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ vnode_t *vp;
+ struct vattr vattr;
+ int error = 0;
+ int rv;
+ /*
+ * offset is int32_t because that is what FIONREAD is defined in terms
+ * of. We cap at INT_MAX as in other cases for this ioctl.
+ */
+ int32_t offset;
+
+ vp = fp->f_vnode;
+
+ if (vp->v_type == VREG || vp->v_type == VDIR) {
+ vattr.va_mask = AT_SIZE;
+ error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL);
+ if (error != 0)
+ return (set_errno(error));
+ offset = MIN(vattr.va_size - fp->f_offset, INT_MAX);
+ if (copyout(&offset, (caddr_t)arg, sizeof (offset)))
+ return (set_errno(EFAULT));
+ } else {
+ error = VOP_IOCTL(vp, FIONREAD, arg, FLUSER(fp), fp->f_cred,
+ &rv, NULL);
+ if (error)
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+/*
+ * hard disk-related translators
+ *
+ * Note that the normal disk ioctls only work for VCHR devices. See spec_ioctl
+ * which will return ENOTTY for a VBLK device. However, fdisk, etc. expect to
+ * work with block devices.
+ *
+ * We expect a zvol to be the primary block device we're interacting with and
+ * we use the zone's lxzd_vdisks list to handle zvols specifically.
+ */
+
+typedef struct lx_hd_geom {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+} lx_hd_geom_t;
+
+/*
+ * Return the volsize and blksize for the correct virtual "disk" for the zone.
+ * Only these two values are returned in 'vdp' within this code.
+ *
+ * A virtual "disk" can be a zvol visible within the zone, but most zones are
+ * not configured with a delegated dataset necessary to make zvols visible.
+ *
+ * To make various applications happy, lx also pretends that our root filesystem
+ * (normally within the zone's dataset) lives on a virtual disk. We have a
+ * /dev/zfsds0 symlink which points at /dev/zfs. This appears in various places
+ * to give the illusion of root's disk. For example, see:
+ * /proc/partitions
+ * /sys/block/zfsds0
+ * /sys/devices/zfs/zfsds0
+ * If an application issues the various LX_HDIO_GETGEO, LX_BLKGETSIZE*, or
+ * LX_BLKSSZGET ioctls on /dev/zfs (that is, minor number 0), we want to return
+ * something sane. In this case, we return the total size (which is normally
+ * limited by a quota) of the dataset that the zone root lives on.
+ */
+static boolean_t
+lx_lookup_zdsk_info(lx_zone_data_t *lxzd, dev_t dev, lx_virt_disk_t *vdp)
+{
+ lx_virt_disk_t *vd;
+
+ /* Handle /dev/zfs */
+ if (getminor(dev) == 0) {
+ struct statvfs64 sv;
+
+ if (VFS_STATVFS(curzone->zone_rootvp->v_vfsp, &sv) == 0) {
+ vdp->lxvd_volsize = sv.f_blocks * sv.f_frsize;
+ vdp->lxvd_blksize = sv.f_frsize;
+ } else {
+ vdp->lxvd_volsize = 0;
+ /* always set to prevent potential divide-by-zero */
+ vdp->lxvd_blksize = 512;
+ }
+
+ return (B_TRUE);
+ }
+
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ if (vd->lxvd_type == LXVD_ZVOL && vd->lxvd_real_dev == dev) {
+ bzero(vdp, sizeof (*vdp));
+ vdp->lxvd_volsize = vd->lxvd_volsize;
+ vdp->lxvd_blksize = vd->lxvd_blksize;
+ return (B_TRUE);
+ }
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * See zvol_ioctl() which always fails for DKIOCGGEOM. The geometry for a
+ * zvol (or really any modern disk) is made up, so we do that here as well.
+ */
+/* ARGSUSED */
+static int
+ict_hdgetgeo(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ lx_hd_geom_t lx_geom;
+ lx_zone_data_t *lxzd;
+
+ if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+ return (set_errno(EINVAL));
+
+ lxzd = ztolxzd(curproc->p_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+ lx_virt_disk_t vd;
+
+ if (!lx_lookup_zdsk_info(lxzd, fp->f_vnode->v_rdev, &vd) ||
+ vd.lxvd_volsize == 0 || vd.lxvd_blksize == 0) {
+ /* should only happen if new zvol */
+ bzero(&lx_geom, sizeof (lx_geom));
+ } else {
+ const diskaddr_t blks =
+ MAX(1, vd.lxvd_volsize / vd.lxvd_blksize);
+
+ /*
+ * Attempt to conjure up a Cylinder-Head-Sector
+ * geometry for the given virtual disk size.
+ */
+ if (blks <= (63*16*65535)) {
+ /*
+ * Use traditional BIOS-style geometry for
+ * adequately small disks.
+ */
+ lx_geom.sectors = 63;
+ lx_geom.heads = 16;
+ lx_geom.cylinders = MAX(1, (blks / (63 * 16)));
+ } else if (blks <= (64*32*65535)) {
+ /* 1MB per cylinder for 512-byte sectors */
+ lx_geom.sectors = 64;
+ lx_geom.heads = 32;
+ lx_geom.cylinders = (blks / (64 * 32));
+ } else {
+ /*
+ * Max out the geometry sizing for large disks.
+ * This may not be adequate for truely huge
+ * volumes (maxing out at a little under 2TB
+ * for those with a 512-byte blocksize), but it
+ * is the best we can do with the given struct.
+ */
+ lx_geom.sectors = 255;
+ lx_geom.heads = 255;
+ lx_geom.cylinders = MIN(65535,
+ (blks / (255*255)));
+ }
+ lx_geom.start = 0;
+ }
+ } else {
+ int res, rv;
+ struct dk_geom geom;
+
+ res = VOP_IOCTL(fp->f_vnode, DKIOCGGEOM, (intptr_t)&geom,
+ fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+ if (res > 0)
+ return (set_errno(res));
+
+ lx_geom.heads = geom.dkg_nhead;
+ lx_geom.sectors = geom.dkg_nsect;
+ lx_geom.cylinders = geom.dkg_ncyl;
+ lx_geom.start = 0;
+ }
+
+ if (copyout(&lx_geom, (caddr_t)arg, sizeof (lx_geom)))
+ return (set_errno(EFAULT));
+ return (0);
+}
+
+/*
+ * Per the Linux sd(4) man page, get the number of sectors. The linux/fs.h
+ * header says its 512 byte blocks.
+ */
+/* ARGSUSED */
+static int
+ict_blkgetsize(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ diskaddr_t tot;
+ lx_zone_data_t *lxzd;
+
+ if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+ return (set_errno(EINVAL));
+
+ lxzd = ztolxzd(curproc->p_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+ lx_virt_disk_t vd;
+
+ if (!lx_lookup_zdsk_info(lxzd, fp->f_vnode->v_rdev, &vd)) {
+ /* should only happen if new zvol */
+ tot = 0;
+ } else {
+ tot = vd.lxvd_volsize / 512;
+ }
+ } else {
+ int res, rv;
+ struct dk_minfo minfo;
+
+ res = VOP_IOCTL(fp->f_vnode, DKIOCGMEDIAINFO, (intptr_t)&minfo,
+ fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+ if (res > 0)
+ return (set_errno(res));
+
+ tot = minfo.dki_capacity;
+ if (minfo.dki_lbsize > 512) {
+ uint_t bsize = minfo.dki_lbsize / 512;
+
+ tot *= bsize;
+ }
+ }
+
+ if (copyout(&tot, (caddr_t)arg, sizeof (long)))
+ return (set_errno(EFAULT));
+ return (0);
+}
+
+/*
+ * Get the sector size (i.e. the logical block size).
+ */
+/* ARGSUSED */
+static int
+ict_blkgetssize(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ uint_t bsize;
+ lx_zone_data_t *lxzd;
+
+ if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+ return (set_errno(EINVAL));
+
+ lxzd = ztolxzd(curproc->p_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+ lx_virt_disk_t vd;
+
+ if (!lx_lookup_zdsk_info(lxzd, fp->f_vnode->v_rdev, &vd)) {
+ /* should only happen if new zvol */
+ bsize = 0;
+ } else {
+ bsize = (uint_t)vd.lxvd_blksize;
+ }
+ } else {
+ int res, rv;
+ struct dk_minfo minfo;
+
+ res = VOP_IOCTL(fp->f_vnode, DKIOCGMEDIAINFO, (intptr_t)&minfo,
+ fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+ if (res > 0)
+ return (set_errno(res));
+
+ bsize = (uint_t)minfo.dki_lbsize;
+ }
+
+ if (copyout(&bsize, (caddr_t)arg, sizeof (bsize)))
+ return (set_errno(EFAULT));
+ return (0);
+}
+
+/*
+ * Get the size. The linux/fs.h header says its in bytes.
+ */
+/* ARGSUSED */
+static int
+ict_blkgetsize64(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ uint64_t tot;
+ lx_zone_data_t *lxzd;
+
+ if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+ return (set_errno(EINVAL));
+
+ lxzd = ztolxzd(curproc->p_zone);
+ ASSERT(lxzd != NULL);
+ ASSERT(lxzd->lxzd_vdisks != NULL);
+
+ if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+ lx_virt_disk_t vd;
+
+ if (!lx_lookup_zdsk_info(lxzd, fp->f_vnode->v_rdev, &vd)) {
+ /* should only happen if new zvol */
+ tot = 0;
+ } else {
+ tot = vd.lxvd_volsize;
+ }
+ } else {
+ int res, rv;
+ struct dk_minfo minfo;
+
+ res = VOP_IOCTL(fp->f_vnode, DKIOCGMEDIAINFO, (intptr_t)&minfo,
+ fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+ if (res > 0)
+ return (set_errno(res));
+
+ tot = minfo.dki_capacity * minfo.dki_lbsize;
+ }
+
+ if (copyout(&tot, (caddr_t)arg, sizeof (uint64_t)))
+ return (set_errno(EFAULT));
+ return (0);
+}
+
+/* ARGSUSED */
+/* Terminal-related translators */
+
+/* ARGSUSED */
+static int
+ict_tcsets(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct lx_termios l_tios;
+ struct termios s_tios;
+ struct lx_cc lio;
+ int error, rv;
+
+ ASSERT(cmd == TCSETS || cmd == TCSETSW || cmd == TCSETSF);
+
+ if (copyin((struct lx_termios *)arg, &l_tios, sizeof (l_tios)) != 0)
+ return (set_errno(EFAULT));
+ termios2lx_cc(&l_tios, &lio);
+ l2s_termios(&l_tios, &s_tios);
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tios,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ if (error)
+ return (set_errno(error));
+ /* preserve lx_cc */
+ set_lx_cc(fp->f_vnode, &lio);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcseta(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct lx_termio l_tio;
+ struct termio s_tio;
+ struct lx_cc lio;
+ int error, rv;
+
+ ASSERT(cmd == TCSETA || cmd == TCSETAW || cmd == TCSETAF);
+
+ if (copyin((struct lx_termio *)arg, &l_tio, sizeof (l_tio)) != 0)
+ return (set_errno(EFAULT));
+ l2s_termio(&l_tio, &s_tio);
+ termio2lx_cc(&l_tio, &lio);
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tio,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ if (error)
+ return (set_errno(error));
+ /* preserve lx_cc */
+ set_lx_cc(fp->f_vnode, &lio);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcgets_ptm(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct lx_termios l_tios;
+ struct termios s_tios, *s_tiosd;
+ uint_t s_tiosl;
+
+ /* get termios defaults */
+ if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(),
+ DDI_PROP_NOTPROM, "ttymodes", (uchar_t **)&s_tiosd,
+ &s_tiosl) != DDI_SUCCESS)
+ return (EIO);
+ ASSERT(s_tiosl == sizeof (*s_tiosd));
+ bcopy(s_tiosd, &s_tios, sizeof (s_tios));
+ ddi_prop_free(s_tiosd);
+
+ /* Now munge the data to how Linux wants it. */
+ s2l_termios(&s_tios, &l_tios);
+ if (copyout(&l_tios, (struct lx_termios *)arg, sizeof (l_tios)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcgets_native(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct lx_termios l_tios;
+ struct termios s_tios;
+ struct lx_cc lio;
+ int error, rv;
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tios,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ /*
+ * systemd calls isatty() on the standard input for a process in order
+ * to determine if it should call chown() upon it. It expects to
+ * receive ENOTTY when the input is not a TTY, but the native illumos
+ * ioctl() call returns ENXIO. Without the following translation,
+ * systemd services fail with 'Failed to change ownership of terminal'
+ */
+ if (error)
+ return (set_errno(error == ENXIO ? ENOTTY : error));
+
+ /* Now munge the data to how Linux wants it. */
+ s2l_termios(&s_tios, &l_tios);
+
+ /* return preserved lx_cc */
+ if (get_lx_cc(fp->f_vnode, &lio) == 0) {
+ l_tios.c_cc[LX_VEOF] = lio.veof;
+ l_tios.c_cc[LX_VEOL] = lio.veol;
+ l_tios.c_cc[LX_VMIN] = lio.vmin;
+ l_tios.c_cc[LX_VTIME] = lio.vtime;
+ }
+
+ if (copyout(&l_tios, (struct lx_termios *)arg, sizeof (l_tios)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcgets(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ if (getmajor(fp->f_vnode->v_rdev) == ddi_name_to_major(LX_PTM_DRV))
+ return (ict_tcgets_ptm(fp, cmd, arg, lxcmd));
+ else
+ return (ict_tcgets_native(fp, cmd, arg, lxcmd));
+}
+
+/* ARGSUSED */
+static int
+ict_tcgeta(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct lx_termio l_tio;
+ struct termio s_tio;
+ struct lx_cc lio;
+ int error, rv;
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tio,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ if (error)
+ return (set_errno(error));
+
+ s2l_termio(&s_tio, &l_tio);
+ /* return preserved lx_cc */
+ if (get_lx_cc(fp->f_vnode, &lio) == 0) {
+ l_tio.c_cc[LX_VEOF] = lio.veof;
+ l_tio.c_cc[LX_VMIN] = lio.vmin;
+ l_tio.c_cc[LX_VTIME] = lio.vtime;
+ }
+
+ if (copyout(&l_tio, (struct lx_termios *)arg, sizeof (l_tio)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocspgrp(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ pid_t lpid, spid, tid;
+ int error, rv;
+
+ /* Converting to the illumos pid is necessary */
+ if (copyin((pid_t *)arg, &lpid, sizeof (lpid)) < 0)
+ return (set_errno(EFAULT));
+ if (lx_lpid_to_spair(lpid, &spid, &tid) < 0)
+ return (set_errno(EPERM));
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&spid,
+ fp->f_flag |FKIOCTL, fp->f_cred, &rv, NULL);
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcsbrkp(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ int rv, error;
+ /* use null duration to emulate TCSBRKP */
+ int dur = 0;
+ error = VOP_IOCTL(fp->f_vnode, TCSBRK, (intptr_t)&dur,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocgpgrp(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ pid_t spgrp;
+ int error, rv;
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&spgrp, FLFAKE(fp),
+ fp->f_cred, &rv, NULL);
+ if (error == 0) {
+ if (spgrp == curproc->p_zone->zone_proc_initpid) {
+ spgrp = 1;
+ }
+ if (copyout(&spgrp, (caddr_t)arg, sizeof (spgrp))) {
+ return (set_errno(EFAULT));
+ }
+ }
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_sptlock(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct strioctl istr;
+ int error, rv;
+
+ istr.ic_cmd = UNLKPT;
+ istr.ic_len = 0;
+ istr.ic_timout = 0;
+ istr.ic_dp = NULL;
+ error = VOP_IOCTL(fp->f_vnode, I_STR, (intptr_t)&istr,
+ fp->f_flag |FKIOCTL, fp->f_cred, &rv, NULL);
+ /*
+ * The success/fail return values are different between Linux
+ * and illumos. Linux expects 0 or -1. Illumos can return
+ * positive number on success.
+ */
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_gptn(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct strioctl istr;
+ cred_t *cr;
+ pt_own_t pto;
+ int error, rv;
+ int ptyno;
+ lx_zone_data_t *lxzd = ztolxzd(curproc->p_zone);
+
+ /* This operation is only valid for the lx_ptm device. */
+ if (getmajor(fp->f_vnode->v_rdev) != ddi_name_to_major(LX_PTM_DRV))
+ return (set_errno(ENOTTY));
+
+ cr = CRED();
+ pto.pto_ruid = cr->cr_uid;
+ /*
+ * Both Linux and our native code (see grantpt() in native libc)
+ * prefer assigning the "tty" gid to the new pty. On Linux this is
+ * done by udev. Since we're in the kernel we cannot lookup the gid, so
+ * we rely on the lx_support program to initialize the value in the
+ * zone data at boot time.
+ */
+ if (lxzd->lxzd_ttygrp == 0) {
+ pto.pto_rgid = cr->cr_gid;
+ } else {
+ pto.pto_rgid = lxzd->lxzd_ttygrp;
+ }
+
+ istr.ic_cmd = OWNERPT;
+ istr.ic_len = sizeof (pto);
+ istr.ic_timout = 0;
+ istr.ic_dp = (char *)&pto;
+ error = VOP_IOCTL(fp->f_vnode, I_STR, (intptr_t)&istr,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+
+ if (error)
+ return (set_errno((error == ENOTTY) ? error: EACCES));
+
+ ptyno = getminor(fp->f_vnode->v_rdev) - 1;
+ if (copyout(&ptyno, (caddr_t)arg, sizeof (ptyno)))
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocgwinsz(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ int error, rv;
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+ NULL);
+
+ /*
+ * A few Linux libc's (e.g. musl) have chosen to implement isatty()
+ * using the TIOCGWINSZ ioctl. Some apps also do the same thing
+ * directly. On Linux that ioctl will return a size of 0x0 for dumb
+ * terminals but on illumos see the handling for TIOCGWINSZ in ptem's
+ * ptioc(). We fail if the winsize is all zeros. To emulate the Linux
+ * behavior use the native ioctl check that we do for isatty and return
+ * a size of 0x0 if that succeeds.
+ */
+ if (error == EINVAL) {
+ int err;
+ struct termio s_tio;
+
+ err = VOP_IOCTL(fp->f_vnode, TCGETA, (intptr_t)&s_tio,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+
+ if (err == 0) {
+ struct winsize w;
+
+ bzero(&w, sizeof (w));
+ if (copyout(&w, (struct winsize *)arg, sizeof (w)) != 0)
+ return (set_errno(EFAULT));
+ return (0);
+ }
+ }
+
+ if (error != 0)
+ return (set_errno(error));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocsctty(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ pid_t ttysid, mysid;
+ int error, rv;
+ proc_t *p = curproc;
+
+ /* getsid */
+ mutex_enter(&p->p_splock);
+ mysid = p->p_sessp->s_sid;
+ mutex_exit(&p->p_splock);
+
+ /*
+ * Report success if we already control the tty.
+ * If no one controls it, TIOCSCTTY will change that later.
+ */
+ error = VOP_IOCTL(fp->f_vnode, TIOCGSID, (intptr_t)&ttysid,
+ FLFAKE(fp), fp->f_cred, &rv, NULL);
+ if (error == 0 && ttysid == mysid)
+ return (0);
+
+ /*
+ * Need to make sure we're a session leader, otherwise the
+ * TIOCSCTTY ioctl will fail.
+ */
+ mutex_enter(&pidlock);
+ if (p->p_sessp->s_sidp != p->p_pidp && !pgmembers(p->p_pid)) {
+ mutex_exit(&pidlock);
+ sess_create();
+ } else {
+ mutex_exit(&pidlock);
+ }
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, 0, FLUSER(fp),
+ fp->f_cred, &rv, NULL);
+ return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* Socket-related translators */
+
+/* ARGSUSED */
+static int
+ict_siocatmark(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ vnode_t *vp = fp->f_vnode;
+ int error, rv;
+ /*
+ * Linux expects a SIOCATMARK of a UDP socket to return ENOTTY, while
+ * Illumos allows it. Linux prior to 2.6.39 returned EINVAL for this.
+ */
+ if (vp->v_type != VSOCK || VTOSO(vp)->so_type != SOCK_STREAM)
+ return (set_errno(ENOTTY));
+
+ error = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+ NULL);
+ if (error)
+ return (set_errno(error));
+
+ return (0);
+}
+
+static int
+ict_if_ioctl(vnode_t *vn, int cmd, intptr_t arg, int flags, cred_t *cred)
+{
+ int error, rv;
+ lx_zone_data_t *lxzd = ztolxzd(curproc->p_zone);
+ ksocket_t ks;
+
+ ASSERT(lxzd != NULL);
+
+ /*
+ * For ioctls of this type, we are strict about address family
+ * whereas Linux is lenient. This strictness can be avoided by using
+ * an internal AF_INET ksocket, which we use if the family is anything
+ * but AF_PACKET.
+ */
+ if (vn->v_type == VSOCK && VTOSO(vn)->so_family == AF_PACKET)
+ return (VOP_IOCTL(vn, cmd, arg, flags, cred, &rv, NULL));
+
+ mutex_enter(&lxzd->lxzd_lock);
+ ks = lxzd->lxzd_ioctl_sock;
+ if (ks == NULL) {
+ /*
+ * Linux is not at all picky about address family when it comes
+ * to supporting interface-related ioctls. To mimic this
+ * behavior, we'll attempt those ioctls against a ksocket
+ * configured for that purpose.
+ */
+ (void) ksocket_socket(&lxzd->lxzd_ioctl_sock, AF_INET,
+ SOCK_DGRAM, 0, 0, curproc->p_zone->zone_kcred);
+ ks = lxzd->lxzd_ioctl_sock;
+ }
+ mutex_exit(&lxzd->lxzd_lock);
+
+ if (ks != NULL) {
+ error = ksocket_ioctl(ks, cmd, arg, &rv, cred);
+ } else {
+ error = VOP_IOCTL(vn, cmd, arg, flags, cred, &rv, NULL);
+ }
+
+ return (error);
+}
+
+static int
+ict_sioghwaddr(file_t *fp, struct lifreq *lreq)
+{
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)&lreq->lifr_addr;
+ struct sockaddr hwaddr;
+ int error, size;
+
+ error = ict_if_ioctl(fp->f_vnode, SIOCGLIFHWADDR, (intptr_t)lreq,
+ FLFAKE(fp), fp->f_cred);
+
+ if (error == EADDRNOTAVAIL &&
+ strncmp(lreq->lifr_name, "lo", 2) == 0) {
+ /* Emulate success on suspected loopbacks */
+ sdl->sdl_type = DL_LOOP;
+ sdl->sdl_alen = ETHERADDRL;
+ bzero(LLADDR(sdl), sdl->sdl_alen);
+ error = 0;
+ }
+
+ if (error == 0) {
+ bzero(&hwaddr, sizeof (hwaddr));
+ lx_stol_hwaddr(sdl, &hwaddr, &size);
+ bcopy(&hwaddr, &lreq->lifr_addr,
+ size + sizeof (sdl->sdl_family));
+ }
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifname(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct ifreq req;
+ int len;
+ char name[LIFNAMSIZ];
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ phyint_t *phyi;
+
+ if (fp->f_vnode->v_type != VSOCK) {
+ return (set_errno(EINVAL));
+ }
+
+ len = (curproc->p_model == DATAMODEL_LP64) ? sizeof (lx_ifreq64_t) :
+ sizeof (lx_ifreq32_t);
+ if (copyin((struct ifreq *)arg, &req, len) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ /*
+ * Since Linux calls this ioctl on all sorts of sockets, perform the
+ * interface name lookup manually.
+ */
+ if ((ns = netstack_get_current()) == NULL) {
+ return (set_errno(EINVAL));
+ }
+ ipst = ns->netstack_ip;
+
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+ phyi = avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
+ (void *) &req.ifr_index, NULL);
+ if (phyi != NULL) {
+ (void) strncpy(name, phyi->phyint_name, LIFNAMSIZ);
+ lx_ifname_convert(name, LX_IF_FROMNATIVE);
+ } else {
+ name[0] = '\0';
+ }
+
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+
+ if (strlen(name) != 0) {
+ /* Truncate for ifreq and copyout */
+ (void) strncpy(req.ifr_name, name, IFNAMSIZ);
+ if (copyout(&req, (struct ifreq *)arg, len) != 0) {
+ return (set_errno(EFAULT));
+ }
+ return (0);
+ }
+
+ return (set_errno(EINVAL));
+}
+
+/* ARGSUSED */
+static int
+ict_siolifreq(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ struct ifreq req;
+ struct lifreq lreq;
+ int error, len;
+
+ /* Convert from Linux ifreq to illumos lifreq */
+ if (curproc->p_model == DATAMODEL_LP64)
+ len = sizeof (lx_ifreq64_t);
+ else
+ len = sizeof (lx_ifreq32_t);
+ if (copyin((struct ifreq *)arg, &req, len) != 0)
+ return (set_errno(EFAULT));
+ bzero(&lreq, sizeof (lreq));
+ (void) strncpy(lreq.lifr_name, req.ifr_name, IFNAMSIZ);
+ bcopy(&req.ifr_ifru, &lreq.lifr_lifru, len - IFNAMSIZ);
+ lx_ifname_convert(lreq.lifr_name, LX_IF_TONATIVE);
+
+ switch (cmd) {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ case SIOCGIFMETRIC:
+ case SIOCSIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCSIFMTU:
+ /*
+ * Convert cmd from SIO*IF* to SIO*LIF*.
+ * This is needed since Linux allows ifreq operations on ipv6
+ * sockets where illumos does not.
+ */
+ cmd = ((cmd & IOC_INOUT) |
+ _IOW('i', ((cmd & 0xff) + 100), struct lifreq));
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+ FLFAKE(fp), fp->f_cred);
+ break;
+ case SIOCGIFINDEX:
+ cmd = SIOCGLIFINDEX;
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+ FLFAKE(fp), fp->f_cred);
+ break;
+ case SIOCGIFFLAGS:
+ cmd = SIOCGLIFFLAGS;
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+ FLFAKE(fp), fp->f_cred);
+ if (error == 0)
+ lx_ifflags_convert(&lreq.lifr_flags, LX_IF_FROMNATIVE);
+ break;
+ case SIOCSIFFLAGS:
+ cmd = SIOCSLIFFLAGS;
+ lx_ifflags_convert(&lreq.lifr_flags, LX_IF_TONATIVE);
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+ FLFAKE(fp), fp->f_cred);
+ break;
+ case SIOCGIFHWADDR:
+ error = ict_sioghwaddr(fp, &lreq);
+ break;
+ case LX_SIOCGIFTXQLEN:
+ /*
+ * Illumos lacks the notion of txqlen. Confirm the provided
+ * interface is valid with SIOCGLIFINDEX and return a fake
+ * txqlen of 1. Loopback devices will report txqlen of 0.
+ */
+ if (strncmp(lreq.lifr_name, "lo", 2) == 0) {
+ lreq.lifr_index = 0;
+ error = 0;
+ break;
+ }
+ cmd = SIOCGLIFINDEX;
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+ FLFAKE(fp), fp->f_cred);
+ if (error == 0) {
+ /* lifr_index aliases to the qlen field */
+ lreq.lifr_index = 1;
+ }
+ break;
+ case LX_SIOCSIFHWADDR:
+ /*
+ * We're not going to support SIOCSIFHWADDR, but we need to be
+ * able to check the result of the copyin first to see if the
+ * command should have returned EFAULT.
+ */
+ default:
+ error = EINVAL;
+ }
+
+ if (error != 0)
+ return (set_errno(error));
+
+ /* Convert back to a Linux ifreq */
+ lx_ifname_convert(lreq.lifr_name, LX_IF_FROMNATIVE);
+ bzero(&req, sizeof (req));
+ (void) strncpy(req.ifr_name, lreq.lifr_name, IFNAMSIZ);
+ bcopy(&lreq.lifr_lifru, &req.ifr_ifru, len - IFNAMSIZ);
+
+ if (copyout(&req, (struct lifreq *)arg, len) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifconf32(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ lx_ifconf32_t conf;
+ lx_ifreq32_t *oreq;
+ struct ifconf sconf;
+ int ifcount, error, i;
+ size_t native_len, lx_len;
+
+ if (copyin((lx_ifconf32_t *)arg, &conf, sizeof (conf)) != 0)
+ return (set_errno(EFAULT));
+
+ /*
+ * First, figure out how many interfaces exist so that kmem allocations
+ * are no larger than needed.
+ */
+ error = ict_if_ioctl(fp->f_vnode, SIOCGIFNUM, (intptr_t)&ifcount,
+ FLFAKE(fp), fp->f_cred);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ /* They want to know how many interfaces there are. */
+ if (conf.if_len <= 0 || conf.if_buf == (uint32_t)(uintptr_t)NULL) {
+ conf.if_len = ifcount * sizeof (lx_ifreq32_t);
+
+ if (copyout(&conf, (lx_ifconf32_t *)arg, sizeof (conf)) != 0)
+ return (set_errno(EFAULT));
+ return (0);
+ }
+
+ ifcount = MIN(ifcount, conf.if_len / sizeof (lx_ifreq32_t));
+
+ /* Get interface configuration list. */
+ native_len = ifcount * sizeof (struct ifreq);
+ sconf.ifc_len = native_len;
+ sconf.ifc_req = (struct ifreq *)kmem_alloc(sconf.ifc_len, KM_SLEEP);
+
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&sconf, FLFAKE(fp),
+ fp->f_cred);
+ if (error != 0) {
+ kmem_free(sconf.ifc_req, native_len);
+ return (set_errno(error));
+ }
+ /* Recalculate in case a nic was removed between ict_if_ioctl calls. */
+ ifcount = sconf.ifc_len / sizeof (struct ifreq);
+
+ /* Convert data to Linux format & rename interfaces */
+ lx_len = ifcount * sizeof (lx_ifreq32_t);
+ oreq = (lx_ifreq32_t *)kmem_alloc(lx_len, KM_SLEEP);
+ for (i = 0; i < ifcount; i++) {
+ /*
+ * struct ifreq and lx_ifreq32_t are the same size, unlike the
+ * 64-bit version of this function.
+ */
+ bcopy(&sconf.ifc_req[i], oreq + i, sizeof (lx_ifreq32_t));
+ lx_ifname_convert(oreq[i].ifr_name, LX_IF_FROMNATIVE);
+ }
+ conf.if_len = lx_len;
+ kmem_free(sconf.ifc_req, native_len);
+
+ error = 0;
+ if (copyout(oreq, (caddr_t)(uintptr_t)conf.if_buf, conf.if_len) != 0 ||
+ copyout(&conf, (lx_ifconf32_t *)arg, sizeof (conf)) != 0)
+ error = set_errno(EFAULT);
+
+ kmem_free(oreq, lx_len);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifconf64(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ lx_ifconf64_t conf;
+ lx_ifreq64_t *oreq;
+ struct ifconf sconf;
+ int ifcount, error, i;
+ size_t native_len, lx_len;
+
+ if (copyin((lx_ifconf64_t *)arg, &conf, sizeof (conf)) != 0)
+ return (set_errno(EFAULT));
+
+ /*
+ * First, figure out how many interfaces exist so that kmem allocations
+ * are no larger than needed.
+ */
+ error = ict_if_ioctl(fp->f_vnode, SIOCGIFNUM, (intptr_t)&ifcount,
+ FLFAKE(fp), fp->f_cred);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ /* They want to know how many interfaces there are. */
+ if (conf.if_len <= 0 || conf.if_buf == NULL) {
+ conf.if_len = ifcount * sizeof (lx_ifreq64_t);
+
+ if (copyout(&conf, (lx_ifconf64_t *)arg, sizeof (conf)) != 0)
+ return (set_errno(EFAULT));
+ return (0);
+ }
+
+ ifcount = MIN(ifcount, conf.if_len / sizeof (lx_ifreq64_t));
+
+ /* Get interface configuration list. */
+ native_len = ifcount * sizeof (struct ifreq);
+ sconf.ifc_len = native_len;
+ sconf.ifc_req = (struct ifreq *)kmem_alloc(sconf.ifc_len, KM_SLEEP);
+
+ error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&sconf, FLFAKE(fp),
+ fp->f_cred);
+ if (error != 0) {
+ kmem_free(sconf.ifc_req, native_len);
+ return (set_errno(error));
+ }
+ /* Recalculate in case a nic was removed between ict_if_ioctl calls. */
+ ifcount = sconf.ifc_len / sizeof (struct ifreq);
+
+ /* Convert data to Linux format & rename interfaces */
+ lx_len = ifcount * sizeof (lx_ifreq64_t);
+ oreq = (lx_ifreq64_t *)kmem_zalloc(lx_len, KM_SLEEP);
+ for (i = 0; i < ifcount; i++) {
+ /*
+ * struct ifreq and lx_ifreq64_t start with common elements.
+ * Anything after that is padding, which is zeroed with
+ * kmem_zalloc above.
+ */
+ bcopy(&sconf.ifc_req[i], oreq + i, sizeof (oreq->ifr_name) +
+ sizeof (oreq->ifr_ifrn.ifru_addr));
+ lx_ifname_convert(oreq[i].ifr_name, LX_IF_FROMNATIVE);
+ }
+ conf.if_len = lx_len;
+ kmem_free(sconf.ifc_req, native_len);
+
+ error = 0;
+ if (copyout(oreq, (caddr_t)(uintptr_t)conf.if_buf, conf.if_len) != 0 ||
+ copyout(&conf, (lx_ifconf64_t *)arg, sizeof (conf)) != 0)
+ error = set_errno(EFAULT);
+
+ kmem_free(oreq, lx_len);
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifconf(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ if (curproc->p_model == DATAMODEL_LP64)
+ return (ict_siocgifconf64(fp, cmd, arg, lxcmd));
+ else
+ return (ict_siocgifconf32(fp, cmd, arg, lxcmd));
+}
+
+/*
+ * Unfortunately some of the autofs ioctls want to return a positive integer
+ * result which does not indicate an error. To minimize disruption in the
+ * rest of the code, we'll treat a positive return as an errno and a negative
+ * return as the non-error return (which we then negate).
+ */
+/* ARGSUSED */
+static int
+ict_autofs(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+ int res = 0;
+ int rv;
+
+ res = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+ NULL);
+ if (res > 0)
+ return (set_errno(res));
+ if (res == 0)
+ return (0);
+ return (-res);
+}
+
+/* Structure used to define an ioctl translator. */
+typedef struct lx_ioc_cmd_translator {
+ int lict_lxcmd;
+ int lict_cmd;
+ int (*lict_func)(file_t *fp, int cmd, intptr_t arg, int lxcmd);
+} lx_ioc_cmd_translator_t;
+
+#define LX_IOC_CMD_TRANSLATOR_PASS(ioc_cmd_sym) \
+ { (int)LX_##ioc_cmd_sym, (int)ioc_cmd_sym, ict_pass },
+
+#define LX_IOC_CMD_TRANSLATOR_FILTER(ioc_cmd_sym, ioct_handler) \
+ { (int)LX_##ioc_cmd_sym, (int)ioc_cmd_sym, ioct_handler },
+
+#define LX_IOC_CMD_TRANSLATOR_CUSTOM(ioc_cmd_sym, ioct_handler) \
+ { (int)ioc_cmd_sym, (int)ioc_cmd_sym, ioct_handler },
+
+#define LX_IOC_CMD_TRANSLATOR_PTHRU(ioc_cmd_sym) \
+ { (int)ioc_cmd_sym, (int)ioc_cmd_sym, ict_pass },
+
+#define LX_IOC_CMD_TRANSLATOR_END \
+ {0, 0, NULL}
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_fd[] = {
+ LX_IOC_CMD_TRANSLATOR_FILTER(FIONBIO, ict_fionbio)
+ LX_IOC_CMD_TRANSLATOR_FILTER(FIONREAD, ict_fionread)
+ LX_IOC_CMD_TRANSLATOR_PASS(FIOASYNC)
+
+ /* streams related */
+ LX_IOC_CMD_TRANSLATOR_PASS(TCXONC)
+ LX_IOC_CMD_TRANSLATOR_PASS(TCFLSH)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCEXCL)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCNXCL)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCSTI)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCSWINSZ)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCMBIS)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCMBIC)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCMSET)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCSETD)
+ LX_IOC_CMD_TRANSLATOR_PASS(TCSBRK)
+
+ /* terminal related */
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCGETD)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCGSID)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCNOTTY)
+ LX_IOC_CMD_TRANSLATOR_PASS(TIOCPKT)
+
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETS, ict_tcsets)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETSW, ict_tcsets)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETSF, ict_tcsets)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETA, ict_tcseta)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETAW, ict_tcseta)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCSETAF, ict_tcseta)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCGETS, ict_tcgets)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TCGETA, ict_tcgeta)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TIOCGWINSZ, ict_tiocgwinsz)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_TCSBRKP, ict_tcsbrkp)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TIOCSPGRP, ict_tiocspgrp)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TIOCGPGRP, ict_tiocgpgrp)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCSPTLCK, ict_sptlock)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCGPTN, ict_gptn)
+ LX_IOC_CMD_TRANSLATOR_FILTER(TIOCSCTTY, ict_tiocsctty)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_socket[] = {
+ LX_IOC_CMD_TRANSLATOR_PASS(FIOGETOWN)
+
+ LX_IOC_CMD_TRANSLATOR_PASS(SIOCSPGRP)
+ LX_IOC_CMD_TRANSLATOR_PASS(SIOCGPGRP)
+ LX_IOC_CMD_TRANSLATOR_PASS(SIOCGSTAMP)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCATMARK, ict_siocatmark)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFFLAGS, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFFLAGS, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFDSTADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFDSTADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFBRDADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFBRDADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFNETMASK, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFNETMASK, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFMETRIC, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFMETRIC, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFMTU, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFMTU, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFHWADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCSIFHWADDR, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFINDEX, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCGIFTXQLEN, ict_siolifreq)
+ LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFCONF, ict_siocgifconf)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCGIFNAME, ict_siocgifname)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_dtrace[] = {
+ LX_IOC_CMD_TRANSLATOR_PTHRU(DTRACEHIOC_ADD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(DTRACEHIOC_REMOVE)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(DTRACEHIOC_ADDDOF)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_autofs[] = {
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_READY)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_FAIL)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_CATATONIC)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_PROTOVER)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_SETTIMEOUT)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_EXPIRE)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_EXPIRE_MULTI)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_PROTOSUBVER)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_ASKUMOUNT)
+
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_VERSION_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_PROTOVER_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_READY_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_FAIL_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_CATATONIC_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_TIMEOUT_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_REQUESTER_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_EXPIRE_CMD)
+ LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD,
+ ict_autofs)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_hd[] = {
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_HDIO_GETGEO, ict_hdgetgeo)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_blk[] = {
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_BLKGETSIZE, ict_blkgetsize)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_BLKSSZGET, ict_blkgetssize)
+ LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_BLKGETSIZE64, ict_blkgetsize64)
+
+ LX_IOC_CMD_TRANSLATOR_END
+};
+
+/*
+ * Linux only restarts ioctls for "slow" devices. This includes terminals,
+ * pipes, and sockets. If additional "slow" devices are discovered in the
+ * future, they can be added here as well.
+ */
+static boolean_t
+lx_ioctl_is_slow_dev(file_t *fp)
+{
+ int rv;
+ struct termio s_tio;
+ vtype_t vt = fp->f_vnode->v_type;
+
+ if (vt == VFIFO || vt == VSOCK)
+ return (B_TRUE);
+
+ /* Check if it's a terminal using the isatty() approach. */
+ if (vt == VCHR && VOP_IOCTL(fp->f_vnode, TCGETA, (intptr_t)&s_tio,
+ FLFAKE(fp), fp->f_cred, &rv, NULL) == 0)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+static void
+lx_ioctl_vsd_free(void *data)
+{
+ kmem_free(data, sizeof (struct lx_cc));
+}
+
+void
+lx_ioctl_init()
+{
+ vsd_create(&lx_ioctl_vsd, lx_ioctl_vsd_free);
+}
+
+void
+lx_ioctl_fini()
+{
+ vsd_destroy(&lx_ioctl_vsd);
+}
+
+long
+lx_ioctl(int fdes, int cmd, intptr_t arg)
+{
+ file_t *fp;
+ int res = 0, error = ENOTTY;
+ lx_ioc_cmd_translator_t *ict = NULL;
+
+ if (cmd == LX_FIOCLEX || cmd == LX_FIONCLEX) {
+ res = f_setfd_error(fdes, (cmd == LX_FIOCLEX) ? FD_CLOEXEC : 0);
+ return ((res != 0) ? set_errno(res) : 0);
+ }
+
+ if ((fp = getf(fdes)) == NULL)
+ return (set_errno(EBADF));
+
+ switch ((cmd & 0xff00) >> 8) {
+ case LX_IOC_TYPE_FD:
+ ict = lx_ioc_xlate_fd;
+ break;
+
+ case LX_IOC_TYPE_DTRACE:
+ ict = lx_ioc_xlate_dtrace;
+ break;
+
+ case LX_IOC_TYPE_SOCK:
+ ict = lx_ioc_xlate_socket;
+ error = EOPNOTSUPP;
+ break;
+
+ case LX_IOC_TYPE_AUTOFS:
+ ict = lx_ioc_xlate_autofs;
+ break;
+
+ case LX_IOC_TYPE_BLK:
+ ict = lx_ioc_xlate_blk;
+ break;
+
+ case LX_IOC_TYPE_HD:
+ ict = lx_ioc_xlate_hd;
+ break;
+
+ default:
+ releasef(fdes);
+ return (set_errno(ENOTTY));
+ }
+
+ /*
+ * Today, none of the ioctls supported by the emulation possess
+ * overlapping cmd values. Because of that, no type interrogation of
+ * the fd is done before executing specific ioctl emulation. It's
+ * assumed that the vnode-specific logic called by the emulation
+ * function will reject ioctl commands not supported by the fd.
+ */
+ VERIFY(ict != NULL);
+ while (ict->lict_func != NULL) {
+ if (ict->lict_lxcmd == cmd)
+ break;
+ ict++;
+ }
+ if (ict->lict_func == NULL) {
+ releasef(fdes);
+ return (set_errno(error));
+ }
+
+ res = ict->lict_func(fp, ict->lict_cmd, arg, ict->lict_lxcmd);
+
+ if (ttolwp(curthread)->lwp_errno == EINTR && lx_ioctl_is_slow_dev(fp))
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+
+ releasef(fdes);
+ return (res);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_ioprio.c b/usr/src/uts/common/brand/lx/syscall/lx_ioprio.c
new file mode 100644
index 0000000000..13397e199e
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_ioprio.c
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/lx_brand.h>
+
+/* 'which' values. */
+#define LX_IOPRIO_WHO_PROCESS 1
+#define LX_IOPRIO_WHO_PGRP 2
+#define LX_IOPRIO_WHO_USER 3
+
+/*
+ * The possible values for the class. We report best effort (BE) as the class
+ * in use.
+ */
+#define LX_IOPRIO_CLASS_RT 1
+#define LX_IOPRIO_CLASS_BE 2
+#define LX_IOPRIO_CLASS_IDLE 3
+
+/* Macro to determine the class from the input mask */
+#define LX_IOPRIO_PRIO_CLASS(m) ((m) >> 13)
+
+/* ARGSUSED */
+long
+lx_ioprio_get(int which, int who)
+{
+ if (which < LX_IOPRIO_WHO_PROCESS || which > LX_IOPRIO_WHO_USER)
+ return (set_errno(EINVAL));
+
+ return (LX_IOPRIO_CLASS_BE);
+}
+
+/*
+ * We allow setting any valid class, even though it's ignored.
+ * We ignore the 'who' parameter which means that we're not searching for
+ * the specified target in order to return a specific errno in the case that
+ * the target does not exist.
+ */
+/* ARGSUSED */
+long
+lx_ioprio_set(int which, int who, int mask)
+{
+ int class;
+
+ if (which < LX_IOPRIO_WHO_PROCESS || which > LX_IOPRIO_WHO_USER)
+ return (set_errno(EINVAL));
+
+ class = LX_IOPRIO_PRIO_CLASS(mask);
+ if (class < LX_IOPRIO_CLASS_RT || class > LX_IOPRIO_CLASS_IDLE)
+ return (set_errno(EINVAL));
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_kill.c b/usr/src/uts/common/brand/lx/syscall/lx_kill.c
new file mode 100644
index 0000000000..6fefbde705
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_kill.c
@@ -0,0 +1,408 @@
+/*
+ * 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.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/thread.h>
+#include <sys/signal.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <lx_signum.h>
+#include <sys/contract/process_impl.h>
+
+extern int kill(pid_t, int);
+
+/*
+ * Check if it is legal to send this signal to the init process. Linux
+ * kill(2) semantics dictate that no _unhandled_ signal may be sent to pid
+ * 1.
+ */
+static int
+lx_init_sig_check(int sig, pid_t pid)
+{
+ proc_t *p;
+ int rv = 0;
+
+ mutex_enter(&pidlock);
+ if ((p = prfind(pid)) == NULL || p->p_stat == SIDL) {
+ rv = ESRCH;
+ } else if (sig != 0) {
+ if (sigismember(&cantmask, sig)) {
+ rv = EPERM;
+ } else {
+ mutex_enter(&p->p_lock);
+ if (PTOU(p)->u_signal[sig-1] == SIG_DFL ||
+ PTOU(p)->u_signal[sig-1] == SIG_IGN) {
+ rv = EPERM;
+ }
+ mutex_exit(&p->p_lock);
+ }
+ }
+ mutex_exit(&pidlock);
+
+ return (rv);
+}
+
+static long
+lx_thrkill(pid_t tgid, pid_t pid, int lx_sig, boolean_t tgkill)
+{
+ kthread_t *t;
+ proc_t *pp, *cp = curproc;
+ sigqueue_t *sqp;
+ int sig, rv;
+
+ /*
+ * Unlike kill(2), Linux tkill(2) doesn't allow signals to
+ * be sent to process IDs <= 0 as it doesn't overlay any special
+ * semantics on the pid.
+ */
+ if ((pid <= 0) || ((lx_sig < 0) || (lx_sig > LX_NSIG)) ||
+ ((sig = ltos_signo[lx_sig]) < 0))
+ return (set_errno(EINVAL));
+
+ /*
+ * If the Linux pid is 1, translate the pid to the actual init
+ * pid for the zone. Note that Linux dictates that no unhandled
+ * signals may be sent to init, so check for that, too.
+ *
+ * Otherwise, extract the tid and real pid from the Linux pid.
+ */
+ if (pid == 1) {
+ pid_t initpid;
+
+ initpid = cp->p_zone->zone_proc_initpid;
+ if ((rv = lx_init_sig_check(sig, initpid)) != 0) {
+ return (set_errno(rv));
+ }
+ }
+ sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+ /*
+ * Find the process for the passed pid...
+ */
+ if (lx_lpid_lock(pid, curzone, 0, &pp, &t) != 0) {
+ rv = set_errno(ESRCH);
+ goto free_and_exit;
+ }
+
+ /*
+ * Make sure the thread group matches the thread.
+ */
+ if (tgkill) {
+ if ((pid == 1 && tgid != 1) ||
+ (pid != 1 && tgid != pp->p_pid)) {
+ mutex_exit(&pp->p_lock);
+ rv = set_errno(ESRCH);
+ goto free_and_exit;
+ }
+ }
+
+ /*
+ * Deny permission to send the signal if either of the following
+ * is true:
+ *
+ * + The signal is SIGCONT and the target pid is not in the same
+ * session as the sender
+ *
+ * + prochasprocperm() shows the user lacks sufficient permission
+ * to send the signal to the target pid
+ */
+ if (((sig == SIGCONT) && (pp->p_sessp != cp->p_sessp)) ||
+ (!prochasprocperm(pp, cp, CRED()))) {
+ mutex_exit(&pp->p_lock);
+ rv = set_errno(EPERM);
+ goto free_and_exit;
+ }
+
+ /* a signal of 0 means just check for the existence of the thread */
+ if (lx_sig == 0) {
+ mutex_exit(&pp->p_lock);
+ rv = 0;
+ goto free_and_exit;
+ }
+
+ sqp->sq_info.si_signo = sig;
+ sqp->sq_info.si_code = SI_LWP;
+ sqp->sq_info.si_pid = cp->p_pid;
+ sqp->sq_info.si_zoneid = getzoneid();
+ sqp->sq_info.si_uid = crgetruid(CRED());
+ sigaddqa(pp, t, sqp);
+
+ mutex_exit(&pp->p_lock);
+
+ return (0);
+
+free_and_exit:
+ kmem_free(sqp, sizeof (sigqueue_t));
+ return (rv);
+}
+
+long
+lx_tgkill(pid_t tgid, pid_t pid, int lx_sig)
+{
+ return (lx_thrkill(tgid, pid, lx_sig, B_TRUE));
+}
+
+long
+lx_tkill(pid_t pid, int lx_sig)
+{
+ return (lx_thrkill(0, pid, lx_sig, B_FALSE));
+}
+
+long
+lx_kill(pid_t lx_pid, int lx_sig)
+{
+ pid_t s_pid, initpid;
+ sigsend_t v;
+ zone_t *zone = curzone;
+ struct proc *p;
+ int err, sig, nfound;
+
+ if ((lx_sig < 0) || (lx_sig > LX_NSIG) ||
+ ((sig = ltos_signo[lx_sig]) < 0))
+ return (set_errno(EINVAL));
+
+ initpid = zone->zone_proc_initpid;
+ if (lx_pid == 0 || lx_pid == -1) {
+ s_pid = 0;
+ } else if (lx_pid > 0) {
+ /*
+ * Translations for individual processes (including pid 1) is
+ * all handled by lx_lpid_to_spair.
+ */
+ if (lx_lpid_to_spair(lx_pid, &s_pid, NULL) != 0) {
+ /*
+ * If we didn't find this pid that means it doesn't
+ * exist in this zone.
+ */
+ return (set_errno(ESRCH));
+ }
+ } else {
+ ASSERT(lx_pid < 0);
+ if (lx_lpid_to_spair(-lx_pid, &s_pid, NULL) != 0) {
+ /*
+ * If we didn't find this pid it means that the
+ * process group leader doesn't exist in this zone.
+ * In this case assuming that the Linux pid is
+ * the same as the Solaris pid will get us the
+ * correct behavior.
+ */
+ s_pid = -lx_pid;
+ }
+ }
+
+ /*
+ * Check that it is legal for this signal to be sent to init
+ */
+ if (s_pid == initpid && (err = lx_init_sig_check(sig, s_pid)) != 0)
+ return (set_errno(err));
+
+ /*
+ * For individual processes, kill() semantics are the same between
+ * Solaris and Linux.
+ */
+ if (lx_pid >= 0)
+ return (kill(s_pid, sig));
+
+ /*
+ * In Solaris, sending a signal to -pid means "send a signal to
+ * everyone in process group pid." In Linux it means "send a
+ * signal to everyone in the group other than init." Sending a
+ * signal to -1 means "send a signal to every process except init
+ * and myself."
+ */
+
+ bzero(&v, sizeof (v));
+ v.sig = sig;
+ v.checkperm = 1;
+ v.sicode = SI_USER;
+ err = 0;
+
+ mutex_enter(&pidlock);
+
+ p = (lx_pid == -1) ? practive : pgfind(s_pid);
+ nfound = 0;
+ while (err == 0 && p != NULL) {
+ if ((p->p_zone == zone) && (p->p_stat != SIDL) &&
+ (p->p_pid != initpid) && (lx_pid < -1 || p != curproc)) {
+ nfound++;
+ err = sigsendproc(p, &v);
+ }
+
+ p = (lx_pid == -1) ? p->p_next : p->p_pglink;
+ }
+ mutex_exit(&pidlock);
+
+ /*
+ * If we found no processes, we'll return ESRCH -- but unlike our
+ * native kill(2), we do not return EPERM if processes are found but
+ * we did not have permission to send any of them a signal.
+ */
+ if (nfound == 0)
+ err = ESRCH;
+
+ return (err ? set_errno(err) : 0);
+}
+
+/*
+ * This handles the unusual case where the user sends a non-queueable signal
+ * through rt_sigqueueinfo. Signals sent with codes that indicate they are
+ * queuable are sent through the sigqueue syscall via the user level function
+ * lx_rt_sigqueueinfo().
+ */
+int
+lx_helper_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *uinfo)
+{
+ proc_t *target_proc;
+ pid_t s_pid;
+ zone_t *zone = curproc->p_zone;
+ sigsend_t send;
+ int err;
+ siginfo_t kinfo;
+
+ if (copyin(uinfo, &kinfo, sizeof (siginfo_t)) != 0)
+ return (set_errno(EFAULT));
+ /* Unlike in lx_kill, this process id must be exact, no negatives. */
+ if (tgid == 0)
+ return (set_errno(ESRCH));
+ if (tgid < 0)
+ return (set_errno(EINVAL));
+ /*
+ * Translate init directly, otherwise use the convenient utility
+ * function to translate. Since we're sending to the whole group, we
+ * only need the solaris pid, and not the lwp id.
+ */
+ if (tgid == 1) {
+ s_pid = zone->zone_proc_initpid;
+ } else {
+ if (lx_lpid_to_spair(tgid, &s_pid, NULL) != 0) {
+ /*
+ * If we didn't find this pid that means it doesn't
+ * exist in this zone.
+ */
+ return (set_errno(ESRCH));
+ }
+ }
+ /*
+ * We shouldn't have queuable signals here, those are sent elsewhere by
+ * the usermode handler for this emulated call.
+ */
+ if (!SI_CANQUEUE(kinfo.si_code)) {
+ return (set_errno(EINVAL));
+ }
+ /* Since our signal shouldn't queue, we just call sigsendproc(). */
+ bzero(&send, sizeof (send));
+ send.sig = sig;
+ send.checkperm = 1;
+ send.sicode = kinfo.si_code;
+ send.value = kinfo.si_value;
+
+ mutex_enter(&pidlock);
+ target_proc = prfind(s_pid);
+ err = 0;
+ if (target_proc != NULL) {
+ err = sigsendproc(target_proc, &send);
+ if (err == 0 && send.perm == 0)
+ err = EPERM;
+ } else {
+ err = ESRCH;
+ }
+ mutex_exit(&pidlock);
+
+ return (err ? set_errno(err) : 0);
+}
+
+/*
+ * Unlike the above function, this handles all system calls to rt_tgsigqueue
+ * regardless of si_code.
+ */
+int
+lx_helper_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *uinfo)
+{
+ int err;
+ proc_t *p = NULL;
+ kthread_t *t;
+ sigqueue_t *sqp;
+ siginfo_t kinfo;
+
+ if (copyin(uinfo, &kinfo, sizeof (siginfo_t)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+
+ if (lx_lpid_lock(tid, curzone, 0, &p, &t) != 0) {
+ err = ESRCH;
+ goto errout;
+ }
+
+ /*
+ * For group leaders, the SunOS pid == Linux pid, so the SunOS leader
+ * pid should be the same as the tgid. Because the tgid comes in via
+ * the syscall, we need to check for an invalid value.
+ */
+ if (p->p_pid != tgid) {
+ err = EINVAL;
+ goto errout;
+ }
+
+ /*
+ * In order to match the Linux behavior of emitting ESRCH errors before
+ * confirming that the signal is valid, this check _must_ be performed
+ * after the target process/thread is located.
+ */
+ if (sig < 0 || sig >= NSIG) {
+ err = EINVAL;
+ goto errout;
+ }
+
+ /*
+ * To merely check for the existence of a thread, the caller will pass
+ * a signal value of 0.
+ */
+ if (sig != 0) {
+ ASSERT(sqp != NULL);
+
+ sqp->sq_info.si_signo = sig;
+ sqp->sq_info.si_code = kinfo.si_code;
+ sqp->sq_info.si_pid = p->p_pid;
+ sqp->sq_info.si_ctid = PRCTID(p);
+ sqp->sq_info.si_zoneid = getzoneid();
+ sqp->sq_info.si_uid = crgetruid(CRED());
+ sigaddqa(p, t, sqp);
+ }
+ mutex_exit(&p->p_lock);
+ return (0);
+
+errout:
+ if (p != NULL) {
+ mutex_exit(&p->p_lock);
+ }
+ kmem_free(sqp, sizeof (sigqueue_t));
+ return (set_errno(err));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_link.c b/usr/src/uts/common/brand/lx/syscall/lx_link.c
new file mode 100644
index 0000000000..f2e268771e
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_link.c
@@ -0,0 +1,200 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/systm.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+
+#define LX_LINK_ALLOWED (LX_AT_SYMLINK_FOLLOW | LX_AT_EMPTY_PATH)
+
+/* From "uts/common/syscall/stat.c" */
+extern int cstatat_getvp(int, char *, int, vnode_t **, cred_t **);
+/* From uts/common/syscall/unlink.c */
+extern int unlinkat(int, char *, int);
+/* From uts/common/syscall/symlink.c */
+extern int symlinkat(char *, int, char *);
+/* From uts/common/syscall/readlink.c */
+extern ssize_t readlinkat(int, char *, char *, size_t);
+
+static long
+lx_link_common(int ffd, char *from, int tfd, char *to, int flags)
+{
+ int error;
+ vnode_t *fsvp = NULL, *tsvp = NULL;
+ enum symfollow follow = NO_FOLLOW;
+
+ if ((flags & ~LX_LINK_ALLOWED) != 0) {
+ return (set_errno(EINVAL));
+ }
+ if ((flags & LX_AT_EMPTY_PATH) == 0) {
+ char c;
+
+ /*
+ * Check that both 'from' and 'to' names are non-empty if
+ * AT_EMPTY_PATH is not set.
+ */
+ if (copyin(from, &c, sizeof (c)) != 0) {
+ return (set_errno(EFAULT));
+ } else if (c == '\0') {
+ return (set_errno(ENOENT));
+ }
+ if (copyin(to, &c, sizeof (c)) != 0) {
+ return (set_errno(EFAULT));
+ } else if (c == '\0') {
+ return (set_errno(ENOENT));
+ }
+
+ /*
+ * XXX: When our support for LX capabilities improves, ENOENT
+ * should be thrown when a process lacking CAP_DAC_READ_SEARCH
+ * attempts to use the AT_EMPTY_PATH flag.
+ */
+ }
+ if ((flags & LX_AT_SYMLINK_FOLLOW) != 0) {
+ follow = FOLLOW;
+ }
+
+ if ((error = fgetstartvp(ffd, from, &fsvp)) != 0) {
+ goto out;
+ }
+ if ((error = fgetstartvp(tfd, to, &tsvp)) != 0) {
+ goto out;
+ }
+ error = vn_linkat(fsvp, from, follow, tsvp, to, UIO_USERSPACE);
+
+out:
+ if (fsvp != NULL) {
+ VN_RELE(fsvp);
+ }
+ if (tsvp != NULL) {
+ VN_RELE(tsvp);
+ }
+ if (error) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_link(char *from, char *to)
+{
+ return (lx_link_common(AT_FDCWD, from, AT_FDCWD, to, 0));
+}
+
+long
+lx_linkat(int ffd, char *from, int tfd, char *to, int flags)
+{
+ ffd = (ffd == LX_AT_FDCWD) ? AT_FDCWD : ffd;
+ tfd = (tfd == LX_AT_FDCWD) ? AT_FDCWD : tfd;
+
+ return (lx_link_common(ffd, from, tfd, to, flags));
+}
+
+static boolean_t
+lx_isdir(int atfd, char *path)
+{
+ cred_t *cr = NULL;
+ vnode_t *vp = NULL;
+ boolean_t is_dir;
+
+ if (cstatat_getvp(atfd, path, NO_FOLLOW, &vp, &cr) != 0)
+ return (B_FALSE);
+
+ crfree(cr);
+ is_dir = (vp->v_type == VDIR);
+ VN_RELE(vp);
+
+ return (is_dir);
+}
+
+long
+lx_unlink(char *path)
+{
+ int err;
+
+ if ((err = unlinkat(AT_FDCWD, path, 0)) == EPERM) {
+ /* On Linux, an unlink of a dir returns EISDIR, not EPERM. */
+ if (lx_isdir(AT_FDCWD, path))
+ return (set_errno(EISDIR));
+ }
+
+ return (err);
+}
+
+long
+lx_unlinkat(int atfd, char *path, int flag)
+{
+ int err;
+
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ if ((flag = ltos_at_flag(flag, AT_REMOVEDIR, B_TRUE)) < 0)
+ return (set_errno(EINVAL));
+
+ err = unlinkat(atfd, path, flag);
+ if (err == EPERM && !(flag & AT_REMOVEDIR)) {
+ /* On Linux, an unlink of a dir returns EISDIR, not EPERM. */
+ if (lx_isdir(atfd, path))
+ return (set_errno(EISDIR));
+ }
+ if (err == EEXIST && (flag & AT_REMOVEDIR)) {
+ /* On Linux, an unlink of a non-empty dir returns ENOTEMPTY, not EEXIST. */
+ if (lx_isdir(atfd, path))
+ return (set_errno(ENOTEMPTY));
+ }
+
+ return (err);
+}
+
+long
+lx_symlink(char *name1, char *name2)
+{
+ return (symlinkat(name1, AT_FDCWD, name2));
+}
+
+long
+lx_symlinkat(char *name1, int atfd, char *name2)
+{
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ return (symlinkat(name1, atfd, name2));
+}
+
+long
+lx_readlink(char *path, char *buf, size_t bufsize)
+{
+ if (bufsize <= 0)
+ return (set_errno(EINVAL));
+
+ return (readlinkat(AT_FDCWD, path, buf, bufsize));
+}
+
+long
+lx_readlinkat(int atfd, char *path, char *buf, size_t bufsize)
+{
+ if (bufsize <= 0)
+ return (set_errno(EINVAL));
+
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ return (readlinkat(atfd, path, buf, bufsize));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_lseek.c b/usr/src/uts/common/brand/lx/syscall/lx_lseek.c
new file mode 100644
index 0000000000..3ac32a2faf
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_lseek.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 2017 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+
+
+#if defined(_SYSCALL32_IMPL) || defined(_ILP32)
+
+/* from uts/common/syscalls/lseek.c */
+extern offset_t llseek32(int32_t, uint32_t, uint32_t, int);
+extern off32_t lseek32(int32_t, off32_t, int32_t);
+
+long
+lx_llseek(int fd, uint32_t off_high, uint32_t off_low, void *out, int whence)
+{
+ offset_t res;
+
+ ASSERT(get_udatamodel() == DATAMODEL_ILP32);
+ res = llseek32(fd, off_low, off_high, whence);
+ if (ttolwp(curthread)->lwp_errno == 0) {
+ if (copyout(&res, out, sizeof (offset_t)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ }
+ return (ttolwp(curthread)->lwp_errno);
+}
+
+
+long
+lx_lseek32(int fd, off32_t offset, int whence)
+{
+ offset_t res;
+ const uint32_t hival = (offset < 0) ? (uint32_t)-1 : 0;
+
+ /*
+ * When returning EOVERFLOW for an offset which is outside the bounds
+ * of an off32_t, Linux will still perform the actual seek before
+ * yielding EOVERFLOW.
+ *
+ * In order to emulate that behavior, an llseek bound to the 64-bit
+ * boundary is used. The overflow can then be reported after the
+ * successful seek.
+ */
+ ASSERT(get_udatamodel() == DATAMODEL_ILP32);
+ res = llseek32(fd, (uint32_t)offset, hival, whence);
+ if (ttolwp(curthread)->lwp_errno == 0 && res > MAXOFF32_T) {
+ return (set_errno(EOVERFLOW));
+ }
+ return (res);
+
+}
+#endif /* defined(_SYSCALL32_IMPL) || defined(_ILP32) */
+
+#if defined(_LP64)
+
+/* from uts/common/syscalls/lseek.c */
+extern off_t lseek64(int, off_t, int);
+
+long
+lx_lseek64(int fd, off_t offset, int whence)
+{
+ ASSERT(get_udatamodel() == DATAMODEL_LP64);
+ return (lseek64(fd, offset, whence));
+}
+
+#endif /* defined(_LP64) */
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_mem.c b/usr/src/uts/common/brand/lx/syscall/lx_mem.c
new file mode 100644
index 0000000000..15351444c8
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_mem.c
@@ -0,0 +1,1118 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <sys/debug.h>
+#include <sys/sysmacros.h>
+#include <sys/policy.h>
+#include <sys/lx_brand.h>
+#include <sys/fcntl.h>
+#include <sys/pathname.h>
+#include <vm/seg_vn.h>
+#include <vm/seg_spt.h>
+#include <sys/shm_impl.h>
+#include <vm/as.h>
+
+/* From uts/common/os/grow.c */
+extern int mprotect(caddr_t, size_t, int);
+extern caddr_t smmap64(caddr_t, size_t, int, int, int, off_t);
+extern int munmap(caddr_t, size_t);
+/* From uts/common/syscall/close.c */
+extern int close(int);
+/* From uts/common/fs/proc/prsubr.c */
+extern uint_t pr_getprot(struct seg *, int, void **, caddr_t *, caddr_t *,
+ caddr_t);
+/* From uts/common/vm/seg_spt.c */
+extern struct seg_ops segspt_shmops;
+/* From uts/common/syscall/memcntl.c */
+extern int memcntl(caddr_t, size_t, int, caddr_t, int, int);
+/* From uts/common/os/grow.c */
+extern int smmap_common(caddr_t *, size_t, int, int, struct file *, offset_t);
+
+/*
+ * After Linux 2.6.8, an unprivileged process can lock memory up to its
+ * RLIMIT_MEMLOCK resource limit.
+ *
+ * Within memcntl() it assumes we have PRIV_PROC_LOCK_MEMORY, or the check in
+ * secpolicy_lock_memory() will fail when we attempt to lock memory. Thus,
+ * to support the Linux semantics, we bypass memcntl() and perform the locking
+ * operations directly.
+ */
+
+#define LX_MADV_NORMAL 0
+#define LX_MADV_RANDOM 1
+#define LX_MADV_SEQUENTIAL 2
+#define LX_MADV_WILLNEED 3
+#define LX_MADV_DONTNEED 4
+#define LX_MADV_FREE 8
+#define LX_MADV_REMOVE 9
+#define LX_MADV_DONTFORK 10
+#define LX_MADV_DOFORK 11
+#define LX_MADV_MERGEABLE 12
+#define LX_MADV_UNMERGEABLE 13
+#define LX_MADV_HUGEPAGE 14
+#define LX_MADV_NOHUGEPAGE 15
+#define LX_MADV_DONTDUMP 16
+#define LX_MADV_DODUMP 17
+
+#define LX_VALID_MSYNC (MS_ASYNC|MS_INVALIDATE|MS_SYNC)
+
+#define LX_PROT_GROWSDOWN 0x01000000
+#define LX_PROT_GROWSUP 0x02000000
+
+/* Internal segment map flags */
+#define LX_SM_READ 0x01
+#define LX_SM_WRITE 0x02
+#define LX_SM_EXEC 0x04
+#define LX_SM_SHM 0x08
+#define LX_SM_ANON 0x10
+#define LX_SM_SHARED 0x20
+#define LX_SM_NORESERVE 0x40
+
+/* For convenience */
+#define LX_PROT_GROWMASK (LX_PROT_GROWSUP|LX_PROT_GROWSDOWN)
+
+/* From lx_rlimit.c */
+extern void lx_get_rctl(char *, struct rlimit64 *);
+
+static int
+lx_mlock_common(int op, uintptr_t addr, size_t len)
+{
+ int err;
+ struct as *as = curproc->p_as;
+ const uintptr_t align_addr = addr & (uintptr_t)PAGEMASK;
+ const size_t align_len = P2ROUNDUP(len + (addr & PAGEOFFSET), PAGESIZE);
+
+ if (len == 0) {
+ /* Linux short-circuits to success on zero length */
+ return (0);
+ } else if ((align_addr + align_len) <= align_addr) {
+ /* Catch overflow (including when aligning len) */
+ return (set_errno(EINVAL));
+ }
+
+ err = as_ctl(as, (caddr_t)align_addr, align_len, op, 0, 0, NULL, 0);
+ if (err == EAGAIN)
+ err = ENOMEM;
+ return (err == 0 ? 0 : set_errno(err));
+}
+
+int
+lx_mlock(uintptr_t addr, size_t len)
+{
+ int err;
+
+ /*
+ * If the the caller is not privileged and either the limit is 0, or
+ * the kernel version is earlier than 2.6.9, then fail with EPERM. See
+ * LTP mlock2.c.
+ */
+ if ((err = secpolicy_lock_memory(CRED())) != 0) {
+ struct rlimit64 rlim64;
+
+ lx_get_rctl("process.max-locked-memory", &rlim64);
+ if (rlim64.rlim_cur == 0 ||
+ lx_kern_release_cmp(curzone, "2.6.9") < 0)
+ return (set_errno(err));
+ }
+
+ return (lx_mlock_common(MC_LOCK, addr, len));
+}
+
+int
+lx_munlock(uintptr_t addr, size_t len)
+{
+ return (lx_mlock_common(MC_UNLOCK, addr, len));
+}
+
+int
+lx_mlockall(int flags)
+{
+ int err;
+ struct as *as = curproc->p_as;
+
+ /*
+ * If the the caller is not privileged and either the limit is 0, or
+ * the kernel version is earlier than 2.6.9, then fail with EPERM. See
+ * LTP mlockall2.c.
+ */
+ if ((err = secpolicy_lock_memory(CRED())) != 0) {
+ struct rlimit64 rlim64;
+
+ lx_get_rctl("process.max-locked-memory", &rlim64);
+ if (rlim64.rlim_cur == 0 ||
+ lx_kern_release_cmp(curzone, "2.6.9") < 0)
+ return (set_errno(err));
+ }
+
+ if ((flags & ~(MCL_FUTURE | MCL_CURRENT)) || flags == 0)
+ return (set_errno(EINVAL));
+
+ err = as_ctl(as, 0, 0, MC_LOCKAS, 0, (uintptr_t)flags, NULL, 0);
+ if (err == EAGAIN)
+ err = ENOMEM;
+ return (err == 0 ? 0 : set_errno(err));
+}
+
+int
+lx_munlockall(void)
+{
+ int err;
+ struct as *as = curproc->p_as;
+
+ if (lx_kern_release_cmp(curzone, "2.6.9") < 0) {
+ if ((err = secpolicy_lock_memory(CRED())) != 0)
+ return (set_errno(err));
+ }
+
+ err = as_ctl(as, 0, 0, MC_UNLOCKAS, 0, 0, NULL, 0);
+ return (err == 0 ? 0 : set_errno(err));
+}
+
+int
+lx_msync(uintptr_t addr, size_t len, int flags)
+{
+ const size_t align_len = P2ROUNDUP(len, PAGESIZE);
+
+ if ((addr & PAGEOFFSET) != 0 ||
+ (flags & ~LX_VALID_MSYNC) != 0) {
+ return (set_errno(EINVAL));
+ } else if (len == 0) {
+ /* Linux short-circuits to success on zero length */
+ return (0);
+ } else if ((addr + align_len) < addr) {
+ /* Catch overflow (including when aligning len) */
+ return (set_errno(ENOMEM));
+ }
+
+ return (memcntl((caddr_t)addr, align_len, MC_SYNC,
+ (caddr_t)(uintptr_t)flags, 0, 0));
+}
+
+int
+lx_madvise(uintptr_t addr, size_t len, int advice)
+{
+ int err;
+ const size_t align_len = P2ROUNDUP(len, PAGESIZE);
+
+ switch (advice) {
+ case LX_MADV_REMOVE:
+ /* approximately similar */
+ advice = MADV_FREE;
+ break;
+
+ case LX_MADV_DONTNEED:
+ /*
+ * On Linux, MADV_DONTNEED implies an immediate purge of the
+ * specified region. This is spuriously different from
+ * (nearly) every other Unix, having apparently been done to
+ * mimic the semantics on Digital Unix (!). This is bad enough
+ * (MADV_FREE both has better semantics and results in better
+ * performance), but it gets worse: Linux applications (and
+ * notably, jemalloc) have managed to depend on the busted
+ * semantics of MADV_DONTNEED on Linux. We implement these
+ * semantics via MADV_PURGE -- and we translate our advice
+ * accordingly.
+ */
+ advice = MADV_PURGE;
+ break;
+
+ case LX_MADV_FREE:
+ advice = MADV_FREE;
+ break;
+
+ case LX_MADV_NORMAL:
+ case LX_MADV_RANDOM:
+ case LX_MADV_SEQUENTIAL:
+ case LX_MADV_WILLNEED:
+ /* These map directly to the illumos values */
+ break;
+
+ case LX_MADV_DONTFORK:
+ case LX_MADV_DOFORK:
+ case LX_MADV_HUGEPAGE:
+ case LX_MADV_NOHUGEPAGE:
+ case LX_MADV_DONTDUMP:
+ case LX_MADV_DODUMP:
+ /* harmless to pretend these work */
+ return (0);
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ if ((addr & PAGEOFFSET) != 0) {
+ return (set_errno(EINVAL));
+ } else if (len == 0) {
+ /* Linux short-circuits to success on zero length */
+ return (0);
+ } else if ((addr + align_len) <= addr) {
+ /*
+ * Catch overflow (including when aligning len). Unlike
+ * similar syscalls, this is an EINVAL failure for madvise(2).
+ */
+ return (set_errno(EINVAL));
+ }
+
+ err = memcntl((caddr_t)addr, align_len, MC_ADVISE,
+ (caddr_t)(intptr_t)advice, 0, 0);
+ if (err == EBUSY) {
+ if (advice != MADV_PURGE) {
+ return (set_errno(EINVAL));
+ }
+ /*
+ * If we received an EBUSY from a MADV_PURGE, we will now try
+ * again with a MADV_DONTNEED: there are conditions (namely,
+ * with locked mappings that haven't yet been faulted in) where
+ * MADV_PURGE will fail but MADV_DONTNEED will succeed. If
+ * this succeeds, we'll call the operation a success; if not,
+ * we'll kick back EINVAL.
+ */
+ advice = MADV_DONTNEED;
+ err = memcntl((caddr_t)addr, align_len, MC_ADVISE,
+ (caddr_t)(intptr_t)advice, 0, 0);
+ if (err != 0) {
+ return (set_errno(EINVAL));
+ }
+ /* Clear the old errno since success was eventually achieved. */
+ ttolwp(curthread)->lwp_errno = 0;
+ }
+ return (err);
+}
+
+int
+lx_mprotect(uintptr_t addr, size_t len, int prot)
+{
+ const size_t align_len = P2ROUNDUP(len, PAGESIZE);
+
+ /*
+ * The flags for native mprotect(2) are essentially the same as those
+ * on Linux, with the exception of PROT_GROWSUP/PROT_GROWSDOWN, for
+ * which there is no native analog. Those flags are presently ignored,
+ * unless they are both present, which represents an invalid argument.
+ */
+ if ((prot & LX_PROT_GROWMASK) == LX_PROT_GROWMASK) {
+ return (set_errno(EINVAL));
+ }
+ prot &= ~(LX_PROT_GROWMASK);
+
+ if ((addr & PAGEOFFSET) != 0) {
+ return (set_errno(EINVAL));
+ } else if (len == 0) {
+ /* Linux short-circuits to success on zero length */
+ return (0);
+ } else if ((addr + align_len) <= addr) {
+ /* Catch overflow (including when aligning len) */
+ return (set_errno(ENOMEM));
+ }
+
+ return (mprotect((void *)addr, align_len, prot));
+}
+
+/*
+ * There are two forms of mmap, mmap() and mmap2(). The only difference is that
+ * the final argument to mmap2() specifies the number of pages, not bytes. Also,
+ * mmap2 is 32-bit only.
+ *
+ * Linux has a number of additional flags, but they are all deprecated. We also
+ * ignore the MAP_GROWSDOWN flag, which has no equivalent on Solaris.
+ *
+ * The Linux mmap() returns ENOMEM in some cases where illumos returns
+ * EOVERFLOW, so we translate the errno as necessary.
+ */
+
+#define LX_MAP_ANONYMOUS 0x00020
+#define LX_MAP_LOCKED 0x02000
+#define LX_MAP_NORESERVE 0x04000
+#define LX_MAP_32BIT 0x00040
+
+#define ONE_GB 0x40000000
+
+static void lx_remap_anoncache_invalidate(uintptr_t, size_t);
+
+static int
+lx_ltos_mmap_flags(int flags)
+{
+ int new_flags;
+
+ new_flags = flags & (MAP_TYPE | MAP_FIXED);
+
+ if (flags & LX_MAP_ANONYMOUS)
+ new_flags |= MAP_ANONYMOUS;
+ if (flags & LX_MAP_NORESERVE)
+ new_flags |= MAP_NORESERVE;
+
+#if defined(_LP64)
+ if (flags & LX_MAP_32BIT)
+ new_flags |= MAP_32BIT;
+#endif
+
+ return (new_flags);
+}
+
+static void *
+lx_mmap_common(void *addr, size_t len, int prot, int flags, int fd, off64_t off)
+{
+ caddr_t ret;
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+
+ /*
+ * Under Linux, the file descriptor is ignored when mapping zfod
+ * anonymous memory, On illumos, we want the fd set to -1 for the
+ * same functionality.
+ */
+ if (flags & LX_MAP_ANONYMOUS)
+ fd = -1;
+
+ /*
+ * We refuse, as a matter of principle, to overcommit memory.
+ * Unfortunately, several bits of important and popular software expect
+ * to be able to pre-allocate large amounts of virtual memory but then
+ * probably never use it. One particularly bad example of this
+ * practice is golang. Another is the JVM.
+ *
+ * In the interest of running software, unsafe or not, we fudge
+ * something vaguely similar to overcommit by permanently enabling
+ * MAP_NORESERVE unless MAP_LOCKED was requested:
+ */
+ if (!(flags & LX_MAP_LOCKED)) {
+ flags |= LX_MAP_NORESERVE;
+ }
+
+ /*
+ * This is totally insane. The NOTES section in the linux mmap(2) man
+ * page claims that on some architectures, read protection may
+ * automatically include exec protection. It has been observed on a
+ * native linux system that the /proc/<pid>/maps file does indeed
+ * show that segments mmap'd from userland (such as libraries mapped in
+ * by the dynamic linker) all have exec the permission set, even for
+ * data segments.
+ *
+ * This insanity is tempered by the fact that the behavior is disabled
+ * for ELF binaries bearing a PT_GNU_STACK header which lacks PF_X
+ * (which most do). Such a header will clear the READ_IMPLIES_EXEC
+ * flag from the process personality.
+ */
+ if (prot & PROT_READ) {
+ if ((lxpd->l_personality & LX_PER_READ_IMPLIES_EXEC) != 0) {
+ prot |= PROT_EXEC;
+ }
+ }
+
+ ret = smmap64(addr, len, prot, lx_ltos_mmap_flags(flags), fd, off);
+ if (ttolwp(curthread)->lwp_errno != 0) {
+ if (ttolwp(curthread)->lwp_errno == EOVERFLOW)
+ (void) set_errno(ENOMEM);
+ return ((void *)-1);
+ }
+
+ if (flags & LX_MAP_LOCKED) {
+ (void) lx_mlock_common(MC_LOCK, (uintptr_t)ret, len);
+ /* clear any errno from mlock */
+ ttolwp(curthread)->lwp_errno = 0;
+ }
+
+ /*
+ * We have a new mapping; invalidate any cached anonymous regions that
+ * overlap(ped) with it.
+ */
+ mutex_enter(&lxpd->l_remap_anoncache_lock);
+ lx_remap_anoncache_invalidate((uintptr_t)ret, len);
+ mutex_exit(&lxpd->l_remap_anoncache_lock);
+
+ return (ret);
+}
+
+long
+lx_mmap(void *addr, size_t len, int prot, int flags, int fd, off64_t off)
+{
+ return ((ssize_t)lx_mmap_common(addr, len, prot, flags, fd, off));
+}
+
+long
+lx_mmap2(void *addr, size_t len, int prot, int flags,
+ int fd, off_t off)
+{
+ return ((ssize_t)lx_mmap_common(addr, len, prot, flags, fd,
+ (off64_t)off * PAGESIZE));
+}
+
+long
+lx_munmap(void *addr, size_t len)
+{
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+
+ /*
+ * Invalidate any cached anonymous regions that overlap(ped) with it.
+ */
+ mutex_enter(&lxpd->l_remap_anoncache_lock);
+ lx_remap_anoncache_invalidate((uintptr_t)addr, len);
+ mutex_exit(&lxpd->l_remap_anoncache_lock);
+
+ return (munmap(addr, len));
+}
+
+#define LX_MREMAP_MAYMOVE 1 /* mapping can be moved */
+#define LX_MREMAP_FIXED 2 /* address is fixed */
+
+/*
+ * Unfortunately, the Linux mremap() manpage contains a statement that is, at
+ * best, grossly oversimplified: that mremap() "can be used to implement a
+ * very efficient realloc(3)." To the degree this is true at all, it is only
+ * true narrowly (namely, when large buffers are being expanded but can't be
+ * expanded in place due to virtual address space restrictions) -- but
+ * apparently, someone took this very literally, because variants of glibc
+ * appear to simply implement realloc() in terms of mremap(). This is
+ * unfortunate because absent intelligent usage, it forces realloc() to have
+ * an unncessary interaction with the VM system for small expansions -- and if
+ * realloc() is itself abused (e.g., if a consumer repeatedly expands and
+ * contracts the same memory buffer), the net result can be less efficient
+ * than a much more naive realloc() implementation. And if native Linux is
+ * suboptimal in this case, we are deeply pathological, having not
+ * historically supported mremap() for anonymous mappings at all. To make
+ * this at least palatable, we not only support remap for anonymous mappings
+ * (see lx_remap_anon(), below), we also cache the metadata associated with
+ * these anonymous remappings to reduce the need to search our address space.
+ * We implement the anonymous metadata cache with l_remap_anoncache, an LRU
+ * cache of lx_segmap_t's that correspond to anonymous segments that have been
+ * resized (only anonymous mappings that have been remapped are cached). The
+ * cache is part of the process's lx-brand-specifc data.
+ */
+
+/*
+ * Search our address space (as) mappings to find the specified mapping. This
+ * is derived from the procfs prgetmap() code. We implement the "reserved"
+ * behavior on the segment so as to accommodate the case where an mmap()'d and
+ * then ftruncate()'d file is being mremap()'d: we use the size of the
+ * mapping (which we need to validate old_size).
+ *
+ * Return 0 if mapping is found, errno if there is a problem or if mapping
+ * not found. If the mapping is found, we populate the mp parameter, vpp and
+ * offp with the results.
+ */
+static int
+lx_get_mapping(uintptr_t find_addr, size_t find_size, lx_segmap_t *mp,
+ vnode_t **vpp, offset_t *offp)
+{
+ struct as *as = curproc->p_as;
+ struct seg *seg;
+ uint_t prot;
+ caddr_t saddr, eaddr, naddr;
+
+ /* pr_getprot asserts that the as is held as a writer */
+ AS_LOCK_ENTER(as, RW_WRITER);
+
+ seg = as_segat(as, (caddr_t)find_addr);
+ if (seg == NULL || (seg->s_flags & S_HOLE) != 0) {
+ AS_LOCK_EXIT(as);
+ return (EFAULT);
+ }
+
+ /*
+ * We're interested in the reserved space, so we use the size of the
+ * segment itself.
+ */
+ eaddr = seg->s_base + seg->s_size;
+ for (saddr = seg->s_base; saddr < eaddr; saddr = naddr) {
+ uintptr_t vaddr;
+ size_t size;
+ struct vnode *vp;
+ void *tmp = NULL;
+
+ prot = pr_getprot(seg, 1, &tmp, &saddr, &naddr, eaddr);
+ if (saddr == naddr)
+ continue;
+
+ vaddr = (uintptr_t)saddr;
+ size = (uintptr_t)naddr - (uintptr_t)saddr;
+
+ if (vaddr == find_addr && find_size < size &&
+ (find_size & PAGEOFFSET) != 0) {
+ /*
+ * We found a mapping but the size being requested is
+ * less than the mapping and not a multiple of our page
+ * size. If it is an anonymous mapping, that likely
+ * means the application did the initial mmap with this
+ * odd size. We'll round up to the next page boundary
+ * in this case.
+ */
+ if (seg->s_ops == &segspt_shmops ||
+ (seg->s_ops == &segvn_ops &&
+ (SEGOP_GETVP(seg, saddr, &vp) != 0 ||
+ vp == NULL))) {
+ /*
+ * It's anonymous, round up the size.
+ */
+ find_size = ptob(btopr(find_size));
+ }
+ }
+
+ /* Check if mapping matches our arguments */
+ if (vaddr == find_addr && size == find_size) {
+ struct vattr vattr;
+
+ mp->lxsm_vaddr = vaddr;
+ mp->lxsm_size = size;
+ mp->lxsm_flags = 0;
+
+ *offp = SEGOP_GETOFFSET(seg, saddr);
+
+ if (prot & PROT_READ)
+ mp->lxsm_flags |= LX_SM_READ;
+ if (prot & PROT_WRITE)
+ mp->lxsm_flags |= LX_SM_WRITE;
+ if (prot & PROT_EXEC)
+ mp->lxsm_flags |= LX_SM_EXEC;
+ if (SEGOP_GETTYPE(seg, saddr) & MAP_SHARED)
+ mp->lxsm_flags |= LX_SM_SHARED;
+ if (SEGOP_GETTYPE(seg, saddr) & MAP_NORESERVE)
+ mp->lxsm_flags |= LX_SM_NORESERVE;
+ if (seg->s_ops == &segspt_shmops ||
+ (seg->s_ops == &segvn_ops &&
+ (SEGOP_GETVP(seg, saddr, &vp) != 0 ||
+ vp == NULL)))
+ mp->lxsm_flags |= LX_SM_ANON;
+
+ if (seg->s_ops == &segspt_shmops) {
+ mp->lxsm_flags |= LX_SM_SHM;
+ } else if ((mp->lxsm_flags & LX_SM_SHARED) &&
+ curproc->p_segacct && shmgetid(curproc,
+ seg->s_base) != SHMID_NONE) {
+ mp->lxsm_flags |= LX_SM_SHM;
+ }
+
+ vattr.va_mask = AT_FSID | AT_NODEID;
+ if (seg->s_ops == &segvn_ops &&
+ SEGOP_GETVP(seg, saddr, &vp) == 0 &&
+ vp != NULL && vp->v_type == VREG &&
+ VOP_GETATTR(vp, &vattr, 0, CRED(),
+ NULL) == 0) {
+ VN_HOLD(vp);
+ *vpp = vp;
+ } else {
+ *vpp = NULL;
+ }
+
+ AS_LOCK_EXIT(as);
+ return (0);
+ }
+
+ if (vaddr <= find_addr &&
+ find_addr + find_size < vaddr + size) {
+ /*
+ * We have a mismatch, but our specified range is a
+ * subset of the actual segment; this is EINVAL.
+ */
+ AS_LOCK_EXIT(as);
+ DTRACE_PROBE2(lx__mremap__badsubset, caddr_t,
+ vaddr, size_t, size);
+ return (EINVAL);
+ }
+ }
+
+ AS_LOCK_EXIT(as);
+ return (EFAULT);
+}
+
+static void
+lx_remap_anoncache_invalidate(uintptr_t addr, size_t size)
+{
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ uint_t i;
+
+ ASSERT(MUTEX_HELD(&lxpd->l_remap_anoncache_lock));
+
+ if (lxpd->l_remap_anoncache_generation == 0)
+ return;
+
+ for (i = 0; i < LX_REMAP_ANONCACHE_NENTRIES; i++) {
+ lx_segmap_t *map = &lxpd->l_remap_anoncache[i];
+
+ /*
+ * If the ranges overlap at all, we zap it.
+ */
+ if (addr < map->lxsm_vaddr + map->lxsm_size &&
+ map->lxsm_vaddr < addr + size) {
+ bzero(map, sizeof (lx_segmap_t));
+ }
+ }
+}
+
+static void
+lx_remap_anoncache_load(lx_segmap_t *map, size_t size)
+{
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ uint64_t oldest = UINT64_MAX;
+ lx_segmap_t *evict = NULL;
+ uint_t i;
+
+ ASSERT(MUTEX_HELD(&lxpd->l_remap_anoncache_lock));
+
+ for (i = 0; i < LX_REMAP_ANONCACHE_NENTRIES; i++) {
+ lx_segmap_t *cp = &lxpd->l_remap_anoncache[i];
+
+ if (cp->lxsm_vaddr == map->lxsm_vaddr) {
+ /*
+ * We're already in the cache -- we just need to update
+ * our LRU field and size to reflect the hit.
+ */
+ cp->lxsm_lru = lxpd->l_remap_anoncache_generation++;
+ cp->lxsm_size = size;
+ return;
+ }
+
+ if (cp->lxsm_vaddr == 0) {
+ evict = cp;
+ break;
+ }
+
+ if (cp->lxsm_lru < oldest) {
+ oldest = cp->lxsm_lru;
+ evict = cp;
+ }
+ }
+
+ /* Update the entry we're evicting */
+ ASSERT(evict != NULL);
+ evict->lxsm_vaddr = map->lxsm_vaddr;
+ evict->lxsm_size = size;
+ evict->lxsm_flags = map->lxsm_flags;
+ evict->lxsm_lru = lxpd->l_remap_anoncache_generation++;
+}
+
+static int lx_u2u_copy(void *, void *, size_t);
+
+/*
+ * As part of lx_remap() (see below) and to accommodate heavy realloc() use
+ * cases (see the discussion of the l_remap_anoncache, above), we allow
+ * anonymous segments to be "remapped" in that we are willing to truncate them
+ * or append to them (as much as that's allowed by virtual address space
+ * usage). If we fall out of these cases, we take the more expensive option
+ * of actually copying the data to a new segment -- but we locate the address
+ * in a portion of the address space that should give us plenty of VA space to
+ * expand.
+ *
+ * We return the address of the mapping or set errno if there is a problem.
+ */
+static long
+lx_remap_anon(lx_segmap_t *mapin, size_t new_size, uint_t flags,
+ uintptr_t new_addr)
+{
+ lx_segmap_t m;
+ int mflags = MAP_ANON;
+ int prot = 0;
+ void *addr, *hint = NULL;
+
+ ASSERT(MUTEX_HELD(&ptolxproc(curproc)->l_remap_anoncache_lock));
+
+ /*
+ * Make a copy of the input lx_segmap_t argument since it might be
+ * a reference into the anon cache, and we're manipulating cache
+ * entries during this function.
+ */
+ m = *mapin;
+
+ /*
+ * If our new size is less than our old size and we're either not
+ * being ordered to move it or the address we're being ordered to
+ * move it to is our current address, we can just act as Procrustes
+ * and chop off anything larger than the new size.
+ */
+ if (new_size < m.lxsm_size && (!(flags & LX_MREMAP_FIXED) ||
+ new_addr == m.lxsm_vaddr)) {
+ if (munmap((void *)(m.lxsm_vaddr + new_size),
+ m.lxsm_size - new_size) != 0) {
+ return (set_errno(EINVAL));
+ }
+
+ lx_remap_anoncache_load(&m, new_size);
+ return (m.lxsm_vaddr);
+ }
+
+ if (m.lxsm_flags & LX_SM_SHM)
+ return (set_errno(EINVAL));
+
+ if (m.lxsm_flags & LX_SM_WRITE)
+ prot |= PROT_WRITE;
+
+ if (m.lxsm_flags & LX_SM_READ)
+ prot |= PROT_READ;
+
+ if (m.lxsm_flags & LX_SM_EXEC)
+ prot |= PROT_EXEC;
+
+ mflags |= (m.lxsm_flags & LX_SM_SHARED) ? MAP_SHARED : MAP_PRIVATE;
+
+ if (m.lxsm_flags & LX_SM_NORESERVE)
+ mflags |= MAP_NORESERVE;
+
+ /*
+ * If we're not being told where to move it, let's try to expand our
+ * mapping in place by adding a fixed mapping after it.
+ */
+ if (!(flags & LX_MREMAP_FIXED)) {
+ void *tmp_addr = (void *)(m.lxsm_vaddr + m.lxsm_size);
+
+ ASSERT(new_size > m.lxsm_size);
+ addr = smmap64(tmp_addr, new_size - m.lxsm_size, prot,
+ mflags, -1, 0);
+ if (ttolwp(curthread)->lwp_errno != 0) {
+ /* There is no place to mmap some extra anon */
+ return (set_errno(EINVAL));
+ }
+
+ if (addr == tmp_addr) {
+ /* The expansion worked */
+ lx_remap_anoncache_load(&m, new_size);
+ return (m.lxsm_vaddr);
+ }
+
+ /*
+ * Our advisory address was not followed -- which, as a
+ * practical matter, means that the range conflicted with an
+ * extant mapping. Unmap wherever our attempted expansion
+ * landed, and drop into the relocation case.
+ */
+ (void) munmap(addr, new_size - m.lxsm_size);
+ }
+
+ lx_remap_anoncache_invalidate(m.lxsm_vaddr, m.lxsm_size);
+
+ /*
+ * If we're here, we actually need to move this mapping -- so if we
+ * can't move it, we're done.
+ */
+ if (!(flags & LX_MREMAP_MAYMOVE))
+ return (set_errno(ENOMEM));
+
+ /*
+ * If this is a shared private mapping, we can't remap it.
+ */
+ if (m.lxsm_flags & LX_SM_SHARED)
+ return (set_errno(EINVAL));
+
+ if (flags & LX_MREMAP_FIXED) {
+ mflags |= MAP_FIXED;
+ hint = (void *)new_addr;
+ } else {
+ /*
+ * Search our address space for a gap to remap into. To give
+ * ourselves plenty of room for further mremap() expansion,
+ * we'll multiply our new size by 16 and look for a gap at
+ * least that big. Historically we looked for an empty gap
+ * around the 2GB region, so we start our search for the lowest
+ * gap in that vicinity.
+ */
+ caddr_t base;
+ size_t upper;
+
+ base = (caddr_t)ONE_GB;
+ upper = (uintptr_t)USERLIMIT - (uintptr_t)base;
+
+ if (as_gap(curproc->p_as, (new_size << 4UL), &base, &upper,
+ AH_LO, NULL) != -1)
+ hint = base;
+ }
+
+ addr = smmap64(hint, new_size, prot, mflags, -1, 0);
+ if (ttolwp(curthread)->lwp_errno != 0) {
+ return (ttolwp(curthread)->lwp_errno);
+ }
+
+ if (lx_u2u_copy((void *)m.lxsm_vaddr, addr, m.lxsm_size) != 0) {
+ /* We couldn't complete the relocation, backout & fail */
+ (void) munmap(addr, new_size);
+ return (set_errno(ENOMEM));
+ }
+
+ (void) munmap((void *)m.lxsm_vaddr, m.lxsm_size);
+
+ /*
+ * Add the relocated mapping to the cache.
+ */
+ m.lxsm_vaddr = (uintptr_t)addr;
+ lx_remap_anoncache_load(&m, new_size);
+
+ return ((long)addr);
+}
+
+/*
+ * We don't have a native mremap() (nor do we particularly want one), so
+ * we emulate it strictly in lx. The idea is simple: we just want to
+ * mmap() the underlying object with the new size and rip down the old mapping.
+ * However, this is slightly complicated because we don't actually have the
+ * file descriptor that corresponds to the resized mapping. So to get a file
+ * descriptor, we may have to search our address space for the mapping and use
+ * the associated vnode to create a file descriptor. Assuming that this
+ * succeeds, we then mmap() it and rip down the original mapping. There are
+ * clearly many reasons why this might fail; absent a more apt errno (e.g.,
+ * ENOMEM in some cases), we return EINVAL to denote these cases.
+ */
+long
+lx_mremap(uintptr_t old_addr, size_t old_size, size_t new_size, int flags,
+ uintptr_t new_addr)
+{
+ int prot = 0, oflags, mflags = 0, i, res;
+ lx_segmap_t map, *mp;
+ int rval = 0;
+ lx_proc_data_t *lxpd;
+ offset_t off;
+ struct vnode *vp = NULL;
+ file_t *fp;
+ caddr_t naddr;
+
+ if (flags & LX_MREMAP_FIXED) {
+ /* MREMAP_FIXED requires MREMAP_MAYMOVE */
+ if ((flags & LX_MREMAP_MAYMOVE) == 0)
+ return (set_errno(EINVAL));
+
+ if (new_addr & PAGEOFFSET)
+ return (set_errno(EINVAL));
+
+ mflags |= MAP_FIXED;
+ } else {
+ if (new_size == old_size)
+ return (old_addr);
+
+ /* new_addr is optional and only valid when LX_MREMAP_FIXED. */
+ new_addr = (uintptr_t)NULL;
+ }
+
+ if (old_addr & PAGEOFFSET)
+ return (set_errno(EINVAL));
+
+ if (new_size == 0)
+ return (set_errno(EINVAL));
+
+ /*
+ * First consult the anoncache; if we find the segment there, we'll
+ * drop straight into lx_remap_anon() and save ourself the pain of
+ * searching our address space.
+ */
+ lxpd = ptolxproc(curproc);
+ mutex_enter(&lxpd->l_remap_anoncache_lock);
+
+ for (i = 0; i < LX_REMAP_ANONCACHE_NENTRIES; i++) {
+ long rv;
+
+ mp = &lxpd->l_remap_anoncache[i];
+
+ if (mp->lxsm_vaddr != old_addr)
+ continue;
+
+ if (mp->lxsm_size != old_size)
+ continue;
+
+ /*
+ * lx_remap_anon will either:
+ * a) expand/contract in place, returning old_addr
+ * b) relocate & expand the mapping, returning a new address
+ * c) there will be an error of some sort and errno will be set
+ */
+ rv = lx_remap_anon(mp, new_size, flags, new_addr);
+ mutex_exit(&lxpd->l_remap_anoncache_lock);
+ return (rv);
+ }
+
+ mutex_exit(&lxpd->l_remap_anoncache_lock);
+
+ /*
+ * Search our address space to find the specified mapping.
+ */
+ if ((res = lx_get_mapping(old_addr, old_size, &map, &vp, &off)) > 0)
+ return (set_errno(res));
+
+ /*
+ * We found the mapping.
+ */
+ mp = &map;
+ DTRACE_PROBE1(lx__mremap__seg, lx_segmap_t *, mp);
+
+ if (mp->lxsm_flags & LX_SM_SHM) {
+ /*
+ * If this is either ISM or System V shared memory, we're not
+ * going to remap it.
+ */
+ rval = set_errno(EINVAL);
+ goto out;
+ }
+
+ if (mp->lxsm_flags & LX_SM_ANON) {
+ /*
+ * This is an anonymous mapping -- which is the one case in
+ * which we perform something that approaches a true remap.
+ */
+ long rv;
+
+ if (vp != NULL)
+ VN_RELE(vp);
+ mutex_enter(&lxpd->l_remap_anoncache_lock);
+ rv = lx_remap_anon(mp, new_size, flags, new_addr);
+ mutex_exit(&lxpd->l_remap_anoncache_lock);
+ return (rv);
+ }
+
+ /* The rest of the code is for a 'named' mapping */
+
+ if (!(flags & LX_MREMAP_MAYMOVE)) {
+ /*
+ * If we're not allowed to move this mapping, we're going to
+ * act as if we can't expand it.
+ */
+ rval = set_errno(ENOMEM);
+ goto out;
+ }
+
+ if (!(mp->lxsm_flags & LX_SM_SHARED)) {
+ /*
+ * If this is a private mapping, we're not going to remap it.
+ */
+ rval = set_errno(EINVAL);
+ goto out;
+ }
+
+ oflags = (mp->lxsm_flags & LX_SM_WRITE) ? (FWRITE | FREAD) : FREAD;
+ if (vp == NULL) {
+ /*
+ * If vp is NULL, the path might not exist. We're going to kick
+ * it back with EINVAL.
+ */
+ rval = set_errno(EINVAL);
+ goto out;
+ }
+
+ /* falloc cannot fail with a NULL fdp. */
+ VERIFY0(falloc(vp, oflags, &fp, NULL));
+ mutex_exit(&fp->f_tlock);
+
+ if (mp->lxsm_flags & LX_SM_WRITE)
+ prot |= PROT_WRITE;
+
+ if (mp->lxsm_flags & LX_SM_READ)
+ prot |= PROT_READ;
+
+ if (mp->lxsm_flags & LX_SM_EXEC)
+ prot |= PROT_EXEC;
+
+ mflags |= MAP_SHARED;
+
+ /*
+ * We're using smmap_common to pass the fp directly, instead of
+ * initializing a temporary file descriptor for smmap64(), so as to
+ * prevent any inadvertent use of that temporary fd within the
+ * application.
+ */
+ naddr = (caddr_t)new_addr;
+ rval = smmap_common(&naddr, new_size, prot, mflags, fp, off);
+
+ mutex_enter(&fp->f_tlock);
+ unfalloc(fp);
+
+ if (rval != 0) {
+ rval = set_errno(ENOMEM);
+ goto out;
+ }
+
+ /*
+ * Our mapping succeeded; we're now going to rip down the old mapping.
+ */
+ (void) munmap((void *)old_addr, old_size);
+
+out:
+ if (vp != NULL)
+ VN_RELE(vp);
+
+ if (rval == 0)
+ return ((long)naddr);
+ return ((long)rval);
+}
+
+#pragma GCC diagnostic ignored "-Wclobbered"
+/*
+ * During mremap we had to relocate the initial anonymous mapping to a new
+ * location (a new anonymous mapping). Copy the user-level data from the first
+ * mapping to the second mapping.
+ *
+ * We have to lock both sides to ensure there is no fault. We do this in 16MB
+ * chunks at a time and we do not concern ourselves with the zone's
+ * max-locked-memory rctl.
+ *
+ * Keep this function at the end since we're disabling the compiler's "clobber"
+ * check due to the on_fault call.
+ */
+static int
+lx_u2u_copy(void *src, void *dst, size_t len)
+{
+ size_t mlen;
+ caddr_t sp, dp;
+ int err;
+ page_t **ppa_src, **ppa_dst;
+ label_t ljb;
+ struct as *p_as = curproc->p_as;
+
+ /* Both sides should be page aligned since they're from smmap64 */
+ ASSERT(((uintptr_t)src & PAGEOFFSET) == 0);
+ ASSERT(((uintptr_t)dst & PAGEOFFSET) == 0);
+ /* Both came from mmap, so they should be valid user pointers */
+ ASSERT((uintptr_t)src < USERLIMIT && (uintptr_t)dst < USERLIMIT);
+
+ sp = src;
+ dp = dst;
+
+ do {
+ mlen = MIN(len, 16 * 1024 * 1024);
+
+ err = as_pagelock(p_as, &ppa_src, sp, mlen, S_READ);
+ if (err != 0) {
+ return (err);
+ }
+ err = as_pagelock(p_as, &ppa_dst, dp, mlen, S_WRITE);
+ if (err != 0) {
+ as_pageunlock(p_as, ppa_src, sp, mlen, S_READ);
+ return (err);
+ }
+
+ DTRACE_PROBE3(lx__mremap__copy, void *, sp, void *, dp,
+ size_t, mlen);
+
+ /* on_fault calls smap_disable */
+ if (on_fault(&ljb)) {
+ /*
+ * Given that the pages are locked and smap is disabled,
+ * we really should never get here. If we somehow do
+ * get here, the copy fails just as if we could not
+ * lock the pages to begin with.
+ */
+ as_pageunlock(p_as, ppa_dst, dp, mlen, S_WRITE);
+ as_pageunlock(p_as, ppa_src, sp, mlen, S_READ);
+ return (EFAULT);
+ }
+ ucopy(sp, dp, mlen);
+ no_fault(); /* calls smap_enable */
+
+ as_pageunlock(p_as, ppa_dst, dp, mlen, S_WRITE);
+ as_pageunlock(p_as, ppa_src, sp, mlen, S_READ);
+
+ len -= mlen;
+ sp += mlen;
+ dp += mlen;
+ } while (len > 0);
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_miscsys.c b/usr/src/uts/common/brand/lx/syscall/lx_miscsys.c
new file mode 100644
index 0000000000..25f06e134b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_miscsys.c
@@ -0,0 +1,495 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <sys/systeminfo.h>
+#include <sys/fcntl.h>
+#include <sys/resource.h>
+#include <sys/uadmin.h>
+#include <sys/lx_misc.h>
+#include <lx_syscall.h>
+
+#define LINUX_REBOOT_MAGIC1 0xfee1dead
+#define LINUX_REBOOT_MAGIC2 672274793
+#define LINUX_REBOOT_MAGIC2A 85072278
+#define LINUX_REBOOT_MAGIC2B 369367448
+#define LINUX_REBOOT_MAGIC2C 537993216
+
+#define LINUX_REBOOT_CMD_RESTART 0x1234567
+#define LINUX_REBOOT_CMD_HALT 0xcdef0123
+#define LINUX_REBOOT_CMD_CAD_ON 0x89abcdef
+#define LINUX_REBOOT_CMD_CAD_OFF 0
+#define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedc
+#define LINUX_REBOOT_CMD_RESTART2 0xa1b2c3d4
+#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2
+#define LINUX_REBOOT_CMD_KEXEC 0x45584543
+
+#define LX_RUSAGE_SELF 0
+#define LX_RUSAGE_CHILDREN (-1)
+#define LX_RUSAGE_BOTH (-2)
+#define LX_RUSAGE_THREAD 1
+
+#define LX_SWAP_PRIOMASK 0x7fff
+#define LX_SWAP_PREFER 0x8000
+#define LX_SWAP_DISCARD 0x10000
+#define LX_SWAP_DISCARD_ONCE 0x20000
+#define LX_SWAP_DISCARD_PAGES 0x40000
+
+#define LX_SWAP_ALL (LX_SWAP_DISCARD_PAGES | \
+ LX_SWAP_DISCARD_ONCE | \
+ LX_SWAP_DISCARD | \
+ LX_SWAP_PREFER | LX_SWAP_PRIOMASK)
+
+/* From uts/common/fs/vfs.c */
+extern void vfs_sync(int);
+/* From uts/common/os/grow.c */
+extern int mincore(caddr_t, size_t, char *);
+extern int munmap(caddr_t, size_t);
+/* From uts/common/os/session.c */
+extern int vhangup();
+/* From uts/common/syscall/alarm.c */
+extern int alarm(int);
+/* From uts/common/syscall/chdir.c */
+extern int chdir(char *);
+extern int chroot(char *);
+extern int fchdir(int);
+/* From uts/common/syscall/nice.c */
+extern int nice(int);
+/* From uts/common/syscall/open.c */
+extern int open(char *, int, int);
+/* From uts/common/syscall/pause.c */
+extern int pause();
+/* From uts/common/syscall/rusagesys.c */
+extern int rusagesys(int, void *, void *, void *, void *);
+/* From uts/common/syscall/systeminfo.c */
+extern long systeminfo(int, char *, long);
+/* From uts/common/syscall/timers.c */
+extern int getitimer(uint_t, struct itimerval *);
+/* From uts/common/syscall/time.c */
+extern int stime(time_t);
+/* From uts/common/syscall/uadmin.c */
+extern int uadmin(int, int, uintptr_t);
+/* From uts/common/syscall/chdir.c */
+extern int chdir_proc(proc_t *, vnode_t *, boolean_t, boolean_t);
+/* From uts/common/fs/lookup.c */
+extern int lookupname(char *, enum uio_seg, int, vnode_t **, vnode_t **);
+/* From uts/common/fs/fs_subr.c */
+extern int fs_need_estale_retry(int);
+/* From uts/common/os/acct.c */
+extern int sysacct(char *);
+
+/* The callback arguments when handling a FS clone group. */
+typedef struct {
+ vnode_t *lcfa_vp;
+ boolean_t lcfa_type;
+ boolean_t lcfa_traverse;
+} lx_clone_fs_arg_t;
+
+long
+lx_alarm(int seconds)
+{
+ return (alarm(seconds));
+}
+
+static int
+lx_clone_fs_cb(proc_t *pp, void *arg)
+{
+ lx_clone_fs_arg_t *ap = (lx_clone_fs_arg_t *)arg;
+ int err;
+
+ /*
+ * Either:
+ * A) The initial lookupname() from lx_clone_fs_do_group() will have
+ * added a hold on the vnode to ensure its existence throughout the
+ * walk.
+ * B) We added a hold in fchdir.
+ * We need to add another hold for each process in the group.
+ */
+ VN_HOLD(ap->lcfa_vp);
+ if ((err = chdir_proc(pp, ap->lcfa_vp, ap->lcfa_type,
+ ap->lcfa_traverse)) != 0) {
+ /* if we failed, chdir_proc already did a rele on vp */
+ return (err);
+ }
+
+ return (0);
+}
+
+/*
+ * Check to see if the process is in a CLONE_FS clone group. Return false
+ * if not (the normal case), otherwise perform the setup, do the group walk
+ * and return true.
+ */
+static boolean_t
+lx_clone_fs_do_group(char *path, boolean_t is_chroot, int *errp)
+{
+ lx_proc_data_t *lproc = ttolxproc(curthread);
+ vnode_t *vp;
+ lx_clone_fs_arg_t arg;
+ int err;
+ int estale_retry = 0;
+
+ if (!lx_clone_grp_member(lproc, LX_CLONE_FS))
+ return (B_FALSE);
+
+ /* Handle the rare case of being in a CLONE_FS clone group */
+
+retry:
+ err = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+ if (err != 0) {
+ if (err == ESTALE && fs_need_estale_retry(estale_retry++))
+ goto retry;
+ *errp = err;
+ return (B_TRUE);
+ }
+
+ arg.lcfa_vp = vp;
+ arg.lcfa_type = is_chroot;
+ arg.lcfa_traverse = B_TRUE;
+
+ /*
+ * We use the VN_HOLD from the lookup to guarantee vp exists for the
+ * entire walk.
+ */
+ err = lx_clone_grp_walk(lproc, LX_CLONE_FS, lx_clone_fs_cb,
+ (void *)&arg);
+ VN_RELE(vp);
+ *errp = err;
+ return (B_TRUE);
+}
+
+long
+lx_chdir(char *path)
+{
+ int err;
+
+ /* Handle the rare case of being in a CLONE_FS clone group */
+ if (lx_clone_fs_do_group(path, B_FALSE, &err))
+ return ((err != 0) ? set_errno(err) : 0);
+
+ return (chdir(path));
+}
+
+long
+lx_chroot(char *path)
+{
+ int err;
+
+ /* Handle the rare case of being in a CLONE_FS clone group */
+ if (lx_clone_fs_do_group(path, B_TRUE, &err))
+ return ((err != 0) ? set_errno(err) : 0);
+
+ return (chroot(path));
+}
+
+long
+lx_creat(char *path, mode_t mode)
+{
+ return (open(path, O_WRONLY | O_CREAT | O_TRUNC, mode));
+}
+
+long
+lx_fchdir(int fd)
+{
+ lx_proc_data_t *lproc = ttolxproc(curthread);
+
+ if (lx_clone_grp_member(lproc, LX_CLONE_FS)) {
+ /* Handle the rare case of being in a CLONE_FS clone group */
+ file_t *fp;
+ vnode_t *vp;
+ lx_clone_fs_arg_t arg;
+ int err;
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+ vp = fp->f_vnode;
+ VN_HOLD(vp);
+ releasef(fd);
+
+ arg.lcfa_vp = vp;
+ arg.lcfa_type = B_FALSE;
+ arg.lcfa_traverse = B_FALSE;
+
+ /*
+ * We use the VN_HOLD above to guarantee vp exists for the
+ * entire walk.
+ */
+ err = lx_clone_grp_walk(lproc, LX_CLONE_FS, lx_clone_fs_cb,
+ (void *)&arg);
+ VN_RELE(vp);
+ if (err)
+ return (set_errno(err));
+ return (0);
+ }
+
+ return (fchdir(fd));
+}
+
+long
+lx_getitimer(int which, struct itimerval *value)
+{
+ return (getitimer(which, value));
+}
+
+/* Linux and illumos have the same rusage structures. */
+long
+lx_getrusage(int who, struct rusage *rup)
+{
+ int code;
+
+ switch (who) {
+ case LX_RUSAGE_SELF:
+ code = _RUSAGESYS_GETRUSAGE;
+ break;
+ case LX_RUSAGE_CHILDREN:
+ code = _RUSAGESYS_GETRUSAGE_CHLD;
+ break;
+ case LX_RUSAGE_THREAD:
+ code = _RUSAGESYS_GETRUSAGE_LWP;
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ return (rusagesys(code, rup, NULL, NULL, NULL));
+}
+
+long
+lx_mincore(caddr_t addr, size_t len, char *vec)
+{
+ int r;
+
+ r = mincore(addr, len, vec);
+ if (r == EINVAL) {
+ /*
+ * LTP mincore01 expects mincore with a huge len to fail with
+ * ENOMEM on a modern kernel, although on Linux 2.6.11 and
+ * earlier, it will return EINVAL.
+ */
+ if (lx_kern_release_cmp(curzone, "2.6.11") > 0 && (long)len < 0)
+ return (set_errno(ENOMEM));
+ }
+ return (r);
+}
+
+long
+lx_nice(int incr)
+{
+ return (nice(incr));
+}
+
+long
+lx_pause(void)
+{
+ return (pause());
+}
+
+/*ARGSUSED*/
+long
+lx_reboot(int magic1, int magic2, uint_t flag, uintptr_t p4)
+{
+ if (magic1 != LINUX_REBOOT_MAGIC1)
+ return (set_errno(EINVAL));
+
+ switch (magic2) {
+ case LINUX_REBOOT_MAGIC2:
+ case LINUX_REBOOT_MAGIC2A:
+ case LINUX_REBOOT_MAGIC2B:
+ case LINUX_REBOOT_MAGIC2C:
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Once we have better Linux capabilities(7) support we should check
+ * CAP_SYS_BOOT instead.
+ */
+ if (crgetuid(CRED()) != 0)
+ return (set_errno(EPERM));
+
+ switch (flag) {
+ case LINUX_REBOOT_CMD_CAD_ON:
+ case LINUX_REBOOT_CMD_CAD_OFF:
+ /* ignored */
+ return (0);
+
+ case LINUX_REBOOT_CMD_POWER_OFF:
+ case LINUX_REBOOT_CMD_HALT:
+ return (uadmin(A_SHUTDOWN, AD_HALT, (uintptr_t)NULL));
+
+ case LINUX_REBOOT_CMD_RESTART:
+ case LINUX_REBOOT_CMD_RESTART2:
+ /* RESTART2 may need more work */
+ return (uadmin(A_SHUTDOWN, AD_BOOT, (uintptr_t)NULL));
+
+ default:
+ return (set_errno(EINVAL));
+ }
+}
+
+long
+lx_setdomainname(char *name, long len)
+{
+ if (len < 0 || len >= LX_SYS_UTS_LN)
+ return (set_errno(EINVAL));
+
+ ttolwp(curthread)->lwp_errno = 0;
+ (void) systeminfo(SI_SET_SRPC_DOMAIN, name, len);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+ return (0);
+}
+
+long
+lx_sethostname(char *name, size_t len)
+{
+ ttolwp(curthread)->lwp_errno = 0;
+ (void) systeminfo(SI_SET_HOSTNAME, name, len);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+ return (0);
+}
+
+long
+lx_stime(time_t *tp)
+{
+ time_t time;
+
+ if (copyin(tp, &time, sizeof (time)) != 0)
+ return (set_errno(EFAULT));
+
+ return (stime(time));
+}
+
+long
+lx_sync(void)
+{
+ vfs_sync(0);
+ return (0);
+}
+
+/*
+ * For syslog, since there is no Linux kernel and nothing to log, we simply
+ * emulate a kernel buffer (LOG_BUF_LEN) of 0 bytes and only handle errors for
+ * bad input. All actions except 3 and 10 require CAP_SYS_ADMIN or CAP_SYSLOG
+ * so without full capabilities support, for now we just perform an euid check.
+ */
+long
+lx_syslog(int type, char *bufp, int len)
+{
+ if (type < 0 || type > 10)
+ return (set_errno(EINVAL));
+
+ if (type != 3 && type != 10 && crgetuid(CRED()) != 0)
+ return (set_errno(EPERM));
+
+ if (type >= 2 && type <= 4 && (bufp == NULL || len < 0))
+ return (set_errno(EINVAL));
+
+ if (type == 8 && (len < 1 || len > 8))
+ return (set_errno(EINVAL));
+
+ return (0);
+}
+
+long
+lx_vhangup(void)
+{
+ if (crgetuid(CRED()) != 0)
+ return (set_errno(EPERM));
+
+ /*
+ * The native vhangup code does nothing except check for the sys_config
+ * privilege. Eventually we'll first want to check our emulation for the
+ * Linux CAP_SYS_TTY_CONFIG capability, but currently, since we've
+ * already checked that our process is root, just succeed.
+ */
+ return (0);
+}
+
+long
+lx_acct(char *p)
+{
+ return (sysacct(p));
+}
+
+/*
+ * Support for Linux namespaces is not yet implemented. Normally we would
+ * simply return ENOSYS for this. However, "systemd" uses mount namespaces to
+ * provide the PrivateTmp feature for some services. Use of this feature is
+ * becoming common and these services will fail to run without namespace
+ * support. "systemd" has a fallback to allow these types of services to run if
+ * it sees either EACCES or EPERM when it tries to setup the namespace. Until
+ * we have namespace support, we return EPERM to workaround this issue.
+ */
+/*ARGSUSED*/
+long
+lx_unshare(int flags)
+{
+ return (set_errno(EPERM));
+}
+
+/*
+ * The whole idea of "swap space" within a zone is a complete fabrication.
+ * However, some apps expect to be able to see swap space data in the /proc
+ * files, while other apps actually don't want there to be any swap space
+ * configured. We use the swapon/off syscalls to allow this visibility to be
+ * controlled from within the zone iself. Note that the "swapon" CLI tends to
+ * do a lot of additional validation which will fail within a zone.
+ *
+ * Once we have better Linux capabilities(7) support we should check
+ * CAP_SYS_ADMIN instead of uid == 0.
+ */
+long
+lx_swapoff(char *path)
+{
+ char buf[MAXPATHLEN];
+ size_t len;
+ lx_zone_data_t *lxzd;
+
+ /* Simple validaton of the argument */
+ if (copyinstr(path, buf, sizeof (buf), &len) != 0)
+ return (set_errno(EFAULT));
+ if (crgetuid(CRED()) != 0)
+ return (set_errno(EPERM));
+
+ lxzd = ztolxzd(curzone);
+ ASSERT(lxzd != NULL);
+
+ lxzd->lxzd_swap_disabled = B_TRUE;
+ return (0);
+}
+
+long
+lx_swapon(char *path, int flags)
+{
+ char buf[MAXPATHLEN];
+ size_t len;
+ lx_zone_data_t *lxzd;
+
+ /* Simple validaton of the arguments */
+ if (copyinstr(path, buf, sizeof (buf), &len) != 0)
+ return (set_errno(EFAULT));
+ if (flags & ~LX_SWAP_ALL)
+ return (set_errno(EINVAL));
+ if (crgetuid(CRED()) != 0)
+ return (set_errno(EPERM));
+
+ lxzd = ztolxzd(curzone);
+ ASSERT(lxzd != NULL);
+
+ lxzd->lxzd_swap_disabled = B_FALSE;
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_mkdir.c b/usr/src/uts/common/brand/lx/syscall/lx_mkdir.c
new file mode 100644
index 0000000000..2f29f56d5f
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_mkdir.c
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/lx_fcntl.h>
+
+/*
+ * From "uts/common/syscall/mkdir.c":
+ */
+extern int mkdirat(int, char *, int);
+
+long
+lx_mkdirat(int fd, char *dname, int dmode)
+{
+ if (fd == LX_AT_FDCWD) {
+ fd = AT_FDCWD;
+ }
+
+ return (mkdirat(fd, dname, dmode));
+}
+
+long
+lx_mkdir(char *dname, int dmode)
+{
+ return (mkdirat(AT_FDCWD, dname, dmode));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_modify_ldt.c b/usr/src/uts/common/brand/lx/syscall/lx_modify_ldt.c
new file mode 100644
index 0000000000..aa6e12a7d8
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_modify_ldt.c
@@ -0,0 +1,121 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/segments.h>
+#include <sys/archsystm.h>
+#include <sys/proc.h>
+#include <sys/sysi86.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_ldt.h>
+
+/*
+ * Read the ldt_info structure in from the Linux app, convert it to an ssd
+ * structure, and then call setdscr() to do all the heavy lifting.
+ */
+static int
+write_ldt(void *data, ulong_t count)
+{
+ user_desc_t usd;
+ struct ssd ssd;
+ struct ldt_info ldt_inf;
+ proc_t *pp = curthread->t_procp;
+ int err;
+
+ if (count != sizeof (ldt_inf))
+ return (set_errno(EINVAL));
+
+ if (copyin(data, &ldt_inf, sizeof (ldt_inf)))
+ return (set_errno(EFAULT));
+
+ if (ldt_inf.entry_number >= MAXNLDT)
+ return (set_errno(EINVAL));
+
+ LDT_INFO_TO_DESC(&ldt_inf, &usd);
+ usd_to_ssd(&usd, &ssd, SEL_LDT(ldt_inf.entry_number));
+
+ /*
+ * Get everyone into a safe state before changing the LDT.
+ */
+ if (!holdlwps(SHOLDFORK1))
+ return (set_errno(EINTR));
+
+ err = setdscr(&ssd);
+
+ /*
+ * Release the hounds!
+ */
+ mutex_enter(&pp->p_lock);
+ continuelwps(pp);
+ mutex_exit(&pp->p_lock);
+
+ return (err ? set_errno(err) : 0);
+}
+
+static int
+read_ldt(void *uptr, ulong_t count)
+{
+ proc_t *pp = curproc;
+ int bytes;
+
+ if (pp->p_ldt == NULL)
+ return (0);
+
+ bytes = (pp->p_ldtlimit + 1) * sizeof (user_desc_t);
+ if (bytes > count)
+ bytes = count;
+
+ if (copyout(pp->p_ldt, uptr, bytes))
+ return (set_errno(EFAULT));
+
+ return (bytes);
+}
+
+long
+lx_modify_ldt(int op, void *data, ulong_t count)
+{
+ int rval;
+
+ switch (op) {
+ case 0:
+ rval = read_ldt(data, count);
+ break;
+
+ case 1:
+ rval = write_ldt(data, count);
+ break;
+
+ default:
+ rval = set_errno(ENOSYS);
+ break;
+ }
+
+ return (rval);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_mount.c b/usr/src/uts/common/brand/lx/syscall/lx_mount.c
new file mode 100644
index 0000000000..fff6c81339
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_mount.c
@@ -0,0 +1,744 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/ctype.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+#include <sys/types.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_autofs.h>
+
+#define tolower(x) (((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' + 'a' : (x))
+
+/*
+ * mount(2) is significantly different between Linux and illumos. One of the
+ * main differences is between the set of flags. Some flags on Linux can be
+ * translated to an illumos equivalent, some are converted to a
+ * filesystem-specific option, while others have no equivalent whatsoever.
+ *
+ * Another big difference is that mounting NFS is fully handled in the kernel on
+ * Linux whereas on illumos a lot of preliminary work is done by the NFS mount
+ * command before calling mount(2). As a simplification, we forward NFS
+ * mount calls back out to the user-level library which does the same kind of
+ * preliminary processing that is done by the native user-level NFS mount code.
+ */
+#define LX_MS_MGC_VAL 0xC0ED0000
+#define LX_MS_RDONLY 0x00000001
+#define LX_MS_NOSUID 0x00000002
+#define LX_MS_NODEV 0x00000004
+#define LX_MS_NOEXEC 0x00000008
+#define LX_MS_SYNCHRONOUS 0x00000010
+#define LX_MS_REMOUNT 0x00000020
+#define LX_MS_MANDLOCK 0x00000040
+#define LX_MS_NOATIME 0x00000400
+#define LX_MS_NODIRATIME 0x00000800
+#define LX_MS_BIND 0x00001000
+#define LX_MS_MOVE 0x00002000
+#define LX_MS_REC 0x00004000
+#define LX_MS_SILENT 0x00008000
+#define LX_MS_POSIXACL 0x00010000
+#define LX_MS_UNBINDABLE 0x00020000
+#define LX_MS_PRIVATE 0x00040000
+#define LX_MS_SLAVE 0x00080000
+#define LX_MS_SHARED 0x00100000
+#define LX_MS_RELATIME 0x00200000
+#define LX_MS_KERNMOUNT 0x00400000
+#define LX_MS_I_VERSION 0x00800000
+#define LX_MS_STRICTATIME 0x01000000
+#define LX_MS_LAZYTIME 0x02000000
+
+/* Linux kernel-internal flags - ignored if passed in */
+#define LX_MS_NOSEC 0x10000000
+#define LX_MS_BORN 0x20000000
+#define LX_MS_ACTIVE 0x40000000
+#define LX_MS_NOUSER 0x80000000
+
+#define LX_MS_SUPPORTED (LX_MS_MGC_VAL | \
+ LX_MS_RDONLY | LX_MS_NOSUID | \
+ LX_MS_NODEV | LX_MS_NOEXEC | \
+ LX_MS_REMOUNT | LX_MS_NOATIME | \
+ LX_MS_BIND | LX_MS_SILENT | \
+ LX_MS_STRICTATIME | LX_MS_NOSEC | \
+ LX_MS_BORN | LX_MS_ACTIVE | LX_MS_NOUSER)
+
+/*
+ * support definitions
+ */
+typedef enum mount_opt_type {
+ MOUNT_OPT_INVALID = 0,
+ MOUNT_OPT_NORMAL = 1, /* option value: none */
+ MOUNT_OPT_UINT = 2, /* option value: unsigned int */
+ MOUNT_OPT_PASSTHRU = 3 /* option value: validated downstream */
+} mount_opt_type_t;
+
+typedef struct mount_opt {
+ char *mo_name;
+ mount_opt_type_t mo_type;
+} mount_opt_t;
+
+/* From uts/common/syscall/umount.c */
+extern int umount2(char *, int);
+
+/* From lx_chown.c */
+extern long lx_vn_chown(vnode_t *, uid_t, gid_t);
+
+/*
+ * Globals
+ */
+static mount_opt_t lofs_options[] = {
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_proc_options[] = {
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_sysfs_options[] = {
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_tmpfs_options[] = {
+ { "size", MOUNT_OPT_PASSTHRU },
+ { "mode", MOUNT_OPT_UINT },
+ { "uid", MOUNT_OPT_UINT },
+ { "gid", MOUNT_OPT_UINT },
+ { "nr_inodes", MOUNT_OPT_PASSTHRU },
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_autofs_options[] = {
+ { LX_MNTOPT_FD, MOUNT_OPT_UINT },
+ { LX_MNTOPT_PGRP, MOUNT_OPT_UINT },
+ { LX_MNTOPT_MINPROTO, MOUNT_OPT_UINT },
+ { LX_MNTOPT_MAXPROTO, MOUNT_OPT_UINT },
+ { LX_MNTOPT_INDIRECT, MOUNT_OPT_NORMAL },
+ { LX_MNTOPT_DIRECT, MOUNT_OPT_NORMAL },
+ { LX_MNTOPT_OFFSET, MOUNT_OPT_NORMAL },
+ { NULL, MOUNT_OPT_INVALID }
+};
+
+static const char *lx_common_mnt_opts[] = {
+ "exec",
+ "noexec",
+ "devices",
+ "nodevices",
+ "dev",
+ "nodev",
+ "suid",
+ "nosuid",
+ NULL
+};
+
+/*
+ * Check the mount options.
+ *
+ * On illumos all mount option verification is done by the user-level mount
+ * command. Invalid options are simply ignored by domount(). Thus, we check
+ * here for invalid/unsupported options.
+ */
+static int
+lx_mnt_opt_verify(char *opts, mount_opt_t *mop)
+{
+ int opts_len = strlen(opts);
+ char *opt, *tp;
+ int opt_len, i;
+ boolean_t last = B_FALSE;
+
+ ASSERT((opts != NULL) && (mop != NULL));
+
+ /* If no options were specified, nothing to do. */
+ if (opts_len == 0)
+ return (0);
+
+ /* If no options are allowed, fail. */
+ if (mop[0].mo_name == NULL)
+ return (ENOTSUP);
+
+ /* Don't accept leading or trailing ','. */
+ if ((opts[0] == ',') || (opts[opts_len] == ','))
+ return (EINVAL);
+
+ /* Don't accept sequential ','. */
+ for (i = 1; i < opts_len; i++) {
+ if ((opts[i - 1] == ',') && (opts[i] == ','))
+ return (EINVAL);
+ }
+
+ /*
+ * Verify each prop one at a time. There is no strtok in the kernel but
+ * it's easy to tokenize the entry ourselves.
+ */
+ opt = opts;
+ for (tp = opt; *tp != ',' && *tp != '\0'; tp++)
+ ;
+ if (*tp == ',') {
+ *tp = '\0';
+ } else {
+ last = B_TRUE;
+ }
+ for (;;) {
+ opt_len = strlen(opt);
+
+ /* Check common options we support on all filesystems */
+ for (i = 0; lx_common_mnt_opts[i] != NULL; i++) {
+ if (strcmp(opt, lx_common_mnt_opts[i]) == 0)
+ goto next_opt;
+ }
+
+ /* Check for matching option/value pair. */
+ for (i = 0; mop[i].mo_name != NULL; i++) {
+ char *ovalue;
+ int ovalue_len, mo_len;
+
+ /* If the options is too short don't bother comparing */
+ mo_len = strlen(mop[i].mo_name);
+ if (opt_len < mo_len) {
+ /* Keep trying to find a match. */
+ continue;
+ }
+
+ /* Compare the option to an allowed option. */
+ if (strncmp(mop[i].mo_name, opt, mo_len) != 0) {
+ /* Keep trying to find a match. */
+ continue;
+ }
+
+ if (mop[i].mo_type == MOUNT_OPT_NORMAL) {
+ /* The option doesn't take a value. */
+ if (opt_len == mo_len) {
+ /* This option is ok. */
+ break;
+ } else {
+ /* Keep trying to find a match. */
+ continue;
+ }
+ }
+
+ /* This options takes a value. */
+ if ((opt_len == mo_len) || (opt[mo_len] != '=')) {
+ /* Keep trying to find a match. */
+ continue;
+ }
+
+ /* We have an option match. Verify option value. */
+ ovalue = &opt[mo_len] + 1;
+ ovalue_len = strlen(ovalue);
+
+ /* Value can't be zero length string. */
+ if (ovalue_len == 0) {
+ goto bad;
+ }
+
+ if (mop[i].mo_type == MOUNT_OPT_UINT) {
+ int j;
+ /* Verify that value is an unsigned int. */
+ for (j = 0; j < ovalue_len; j++) {
+ if (!ISDIGIT(ovalue[j])) {
+ goto bad;
+ }
+ }
+ } else if (mop[i].mo_type == MOUNT_OPT_PASSTHRU) {
+ /* Filesystem will do its own validation. */
+ break;
+ } else {
+ /* Unknown option type specified. */
+ goto bad;
+ }
+
+ /* The option is ok. */
+ break;
+ }
+
+ /* If there were no matches this is an unsupported option. */
+ if (mop[i].mo_name == NULL) {
+ goto bad;
+ }
+
+next_opt:
+ /*
+ * This option is ok, either we're done or move on to the next
+ * option.
+ */
+ if (last)
+ break;
+
+ *tp = ',';
+ opt = tp + 1;
+ for (tp = opt; *tp != ',' && *tp != '\0'; tp++)
+ ;
+ if (*tp == ',') {
+ *tp = '\0';
+ } else {
+ last = B_TRUE;
+ }
+ };
+
+ /* We verified all the options. */
+ return (0);
+
+bad:
+ if (!last) {
+ *tp = ',';
+ }
+ return (EINVAL);
+}
+
+/*
+ * Remove an option from the string and save it in the provided buffer.
+ * The option string should have already been verified as valid.
+ * Return 0 if not present, -1 if error, and 1 if present and fine.
+ */
+static int
+lx_mnt_opt_rm(char *opts, char *rmopt, char *retstr, int retlen)
+{
+ int opts_len = strlen(opts);
+ char *optstart, *optend;
+ int optlen;
+
+ ASSERT((opts != NULL) && (rmopt != NULL));
+
+ if (retstr != NULL)
+ retstr[0] = '\0';
+
+ /* If no options were specified, there's no problem. */
+ if (opts_len == 0)
+ return (0);
+
+ if ((optstart = strstr(opts, rmopt)) == NULL)
+ return (0);
+
+ for (optend = optstart; *optend != ',' && *optend != '\0'; optend++)
+ ;
+
+ optlen = optend - optstart;
+ if (retstr != NULL) {
+ if (optlen >= retlen)
+ return (-1);
+ (void) strncpy(retstr, optstart, optlen);
+ retstr[optlen] = '\0';
+ }
+
+ if (*optend == ',')
+ optend++;
+
+ optlen = strlen(optend) + 1;
+ bcopy(optend, optstart, optlen);
+
+ if (*optstart == '\0' && optstart != opts) {
+ /* removed last opt and it had a preceeding opt, remove comma */
+ *(optstart - 1) = '\0';
+ }
+
+ return (1);
+}
+
+static int
+lx_mnt_opt_val(char *opt, int *valp)
+{
+ char *op, *ep;
+ long lval;
+
+ if ((op = strchr(opt, '=')) == NULL)
+ return (-1);
+
+ op++;
+ if (!ISDIGIT(*op))
+ return (-1);
+
+ if (ddi_strtoul(op, &ep, 10, (ulong_t *)&lval) != 0 || lval > INT_MAX) {
+ return (-1);
+ }
+
+ if (*ep != '\0')
+ return (-1);
+
+ *valp = (int)lval;
+ return (0);
+}
+
+static int
+lx_mnt_add_opt(char *option, char *buf, size_t buf_size)
+{
+ char *fmt_str = NULL;
+ size_t len;
+
+ ASSERT((option != NULL) && (strlen(option) > 0));
+ ASSERT((buf != NULL) && (buf_size > 0));
+
+ if (buf[0] == '\0') {
+ fmt_str = "%s";
+ } else {
+ fmt_str = ",%s";
+ }
+
+ len = strlen(buf);
+ VERIFY(len <= buf_size);
+ buf_size -= len;
+ buf += len;
+
+ if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1))
+ return (EOVERFLOW);
+ return (0);
+}
+
+static int
+lx_mnt_copyin_arg(const char *from, char *to, size_t len)
+{
+ size_t slen;
+ int rv;
+
+ rv = copyinstr(from, to, len, &slen);
+ if (rv == ENAMETOOLONG || slen == len)
+ return (ENAMETOOLONG);
+ if (rv != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+long
+lx_mount(const char *sourcep, const char *targetp, const char *fstypep,
+ uint_t flags, const void *datap)
+{
+ char fstype[16];
+ char *source, *target, *options;
+ size_t sourcel, targetl, optionsl;
+ int sflags, rv;
+ struct mounta ma, *map = &ma;
+ vfs_t *vfsp;
+ vnode_t *vp = NULL;
+ int uid = -1;
+ int gid = -1;
+
+ if ((rv = lx_mnt_copyin_arg(fstypep, fstype, sizeof (fstype))) != 0) {
+ if (rv == ENAMETOOLONG)
+ return (set_errno(ENODEV));
+ return (set_errno(rv));
+ }
+
+ /*
+ * Vector back out to userland emulation for NFS.
+ */
+ if (strcmp(fstype, "nfs") == 0 || strcmp(fstype, "nfs4") == 0) {
+ uintptr_t uargs[5] = {(uintptr_t)sourcep, (uintptr_t)targetp,
+ (uintptr_t)fstypep, (uintptr_t)flags, (uintptr_t)datap};
+
+ /* The userspace emulation will do the lx_syscall_return() */
+ ttolxlwp(curthread)->br_eosys = JUSTRETURN;
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_emulate_user32(ttolwp(curthread), LX_SYS32_mount,
+ uargs);
+ } else
+#endif
+ {
+ lx_emulate_user(ttolwp(curthread), LX_SYS_mount, uargs);
+ }
+ return (0);
+ }
+
+ /* Make sure we support the requested mount flags. */
+ if ((flags & ~LX_MS_SUPPORTED) != 0)
+ return (set_errno(ENOTSUP));
+
+ sourcel = targetl = MAXPATHLEN;
+ optionsl = MAX_MNTOPT_STR;
+ source = kmem_alloc(sourcel, KM_SLEEP);
+ target = kmem_alloc(targetl, KM_SLEEP);
+ options = kmem_alloc(optionsl, KM_SLEEP);
+
+ sflags = MS_SYSSPACE | MS_OPTIONSTR;
+ options[0] = '\0';
+
+ /* Copy in parameters that are always present. */
+ if ((rv = lx_mnt_copyin_arg(sourcep, source, sourcel)) != 0)
+ goto out;
+
+ if ((rv = lx_mnt_copyin_arg(targetp, target, targetl)) != 0)
+ goto out;
+
+ /*
+ * While SunOS is picky about mount(2) target paths being absolute,
+ * Linux is not so strict. In order to facilitate this looser
+ * requirement we must lookup the full path.
+ */
+ if (target[0] != '/') {
+ if ((rv = lookupname(target, UIO_SYSSPACE, FOLLOW,
+ NULLVPP, &vp)) != 0) {
+ goto out;
+ }
+
+ rv = vnodetopath(NULL, vp, target, targetl, CRED());
+ VN_RELE(vp);
+ if (rv != 0)
+ goto out;
+ }
+
+ /*
+ * Following commits* in September 2020, systemd mounts most
+ * filesystems via an open file descriptor in order to avoid
+ * following mount point symlinks.
+ * It does this by opening the mount point with O_NOFOLLOW|O_PATH
+ * and then performing the mount on /proc/self/fd/<fd>.
+ * In illumos, this results in a mount attempt on the lx_proc vnode
+ * instead of the intended mount point, which fails since /proc files
+ * are generally marked as unmountable (and we wouldn't want this
+ * anyway).
+ * As a workaround, the /proc vnode's link target is retrieved and
+ * used for mount. This lookup causes procfs to return the path of
+ * the vnode's realvp which is the path of the underlying directory.
+ * Additonally, since systemd is holding an open file descriptor for
+ * the mount point, overlay mounts also need to be allowed.
+ *
+ * * https://github.com/systemd/systemd/commit/28126409b20bca9aa6f
+ * * https://github.com/systemd/systemd/commit/21935150a0c42b91a32
+ */
+ if (strncmp(target, "/proc/self/fd/", 14) == 0 &&
+ lookupname(target, UIO_SYSSPACE, NO_FOLLOW, NULLVPP, &vp) == 0) {
+ struct iovec iov = {0};
+ struct uio uio = {0};
+
+ iov.iov_base = target;
+ iov.iov_len = targetl;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = targetl;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_llimit = MAXOFFSET_T;
+
+ rv = VOP_READLINK(vp, &uio, CRED(), NULL);
+ VN_RELE(vp);
+ if (rv != 0)
+ goto out;
+ target[targetl - uio.uio_resid] = '\0';
+ sflags |= MS_OVERLAY;
+ }
+
+ /* Copy in Linux mount options. */
+ if (datap != NULL &&
+ (rv = lx_mnt_copyin_arg(datap, options, optionsl)) != 0) {
+ goto out;
+ }
+
+ /* Do filesystem specific mount work. */
+ if (flags & LX_MS_BIND) {
+ /* If MS_BIND is set, we turn this into a lofs mount. */
+ (void) strcpy(fstype, "lofs");
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lofs_options)) != 0)
+ goto out;
+ } else if (strcmp(fstype, "tmpfs") == 0) {
+ char idstr[64];
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_tmpfs_options)) != 0)
+ goto out;
+
+ /*
+ * Linux defaults to mode=1777 for tmpfs mounts.
+ */
+ if (strstr(options, "mode=") == NULL) {
+ if (options[0] != '\0')
+ (void) strlcat(options, ",", optionsl);
+ (void) strlcat(options, "mode=1777", optionsl);
+ }
+
+ /*
+ * Linux supports "nr_inodes=<val>" for tmpfs. There is no
+ * analogue in illumos - drop the option.
+ */
+ (void) lx_mnt_opt_rm(options, "nr_inodes=", NULL, 0);
+
+ switch (lx_mnt_opt_rm(options, "uid=", idstr, sizeof (idstr))) {
+ case 0:
+ uid = -1;
+ break;
+ case 1:
+ if (lx_mnt_opt_val(idstr, &uid) < 0) {
+ rv = EINVAL;
+ goto out;
+ }
+ break;
+ default:
+ rv = E2BIG;
+ goto out;
+ }
+ switch (lx_mnt_opt_rm(options, "gid=", idstr, sizeof (idstr))) {
+ case 0:
+ gid = -1;
+ break;
+ case 1:
+ if (lx_mnt_opt_val(idstr, &gid) < 0) {
+ rv = EINVAL;
+ goto out;
+ }
+ break;
+ default:
+ rv = E2BIG;
+ goto out;
+ }
+
+ /*
+ * Linux seems to always allow overlay mounts. We allow this
+ * everywhere except under /dev where it interferes with device
+ * emulation.
+ */
+ if (strcmp(target, "/dev") != 0 &&
+ strncmp(target, "/dev/", 5) != 0)
+ sflags |= MS_OVERLAY;
+ } else if (strcmp(fstype, "proc") == 0) {
+ /* Translate proc mount requests to lx_proc requests. */
+ (void) strcpy(fstype, "lx_proc");
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_proc_options)) != 0)
+ goto out;
+ } else if (strcmp(fstype, "sysfs") == 0) {
+ /* Translate sysfs mount requests to lx_sysfs requests. */
+ (void) strcpy(fstype, "lx_sysfs");
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_sysfs_options)) != 0)
+ goto out;
+ } else if (strcmp(fstype, "cgroup") == 0) {
+ /* Translate cgroup mount requests to lx_cgroup requests. */
+ (void) strcpy(fstype, "lx_cgroup");
+
+ /*
+ * Currently don't verify Linux mount options since we can
+ * have a subsystem string provided.
+ */
+ } else if (strcmp(fstype, "autofs") == 0) {
+ /* Translate autofs mount requests to lxautofs requests. */
+ (void) strcpy(fstype, LX_AUTOFS_NAME);
+
+ /* Verify Linux mount options. */
+ if ((rv = lx_mnt_opt_verify(options, lx_autofs_options)) != 0)
+ goto out;
+
+ /* Linux seems to always allow overlay mounts */
+ sflags |= MS_OVERLAY;
+ } else {
+ return (set_errno(ENODEV));
+ }
+
+ /* Convert some Linux flags to illumos flags. */
+ if (flags & LX_MS_RDONLY)
+ sflags |= MS_RDONLY;
+ if (flags & LX_MS_NOSUID)
+ sflags |= MS_NOSUID;
+ if (flags & LX_MS_REMOUNT)
+ sflags |= MS_REMOUNT;
+
+ /*
+ * Convert some Linux flags to illumos option strings.
+ */
+ if (flags & LX_MS_STRICTATIME) {
+ /*
+ * The "strictatime" mount option ensures that none of the
+ * weaker atime-related mode options are in effect.
+ */
+ flags &= ~(LX_MS_RELATIME | LX_MS_NOATIME);
+ }
+ if ((flags & LX_MS_NODEV) &&
+ (rv = lx_mnt_add_opt("nodev", options, optionsl)) != 0) {
+ goto out;
+ }
+ if ((flags & LX_MS_NOEXEC) &&
+ (rv = lx_mnt_add_opt("noexec", options, optionsl)) != 0) {
+ goto out;
+ }
+ if ((flags & LX_MS_NOATIME) &&
+ (rv = lx_mnt_add_opt("noatime", options, optionsl)) != 0) {
+ goto out;
+ }
+
+ if ((rv = lookupname(target, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp)) != 0)
+ goto out;
+
+ /* If mounting proc over itself, just return ok */
+ if (strcmp(fstype, "lx_proc") == 0 && strcmp("lx_proc",
+ vfssw[vp->v_vfsp->vfs_fstype].vsw_name) == 0) {
+ VN_RELE(vp);
+ rv = 0;
+ goto out;
+ }
+
+ map->spec = source;
+ map->dir = target;
+ map->flags = sflags;
+ map->fstype = fstype;
+ map->dataptr = NULL;
+ map->datalen = 0;
+ map->optptr = options;
+ map->optlen = optionsl;
+
+ rv = domount(NULL, map, vp, CRED(), &vfsp);
+ VN_RELE(vp);
+ if (rv != 0)
+ goto out;
+
+ VFS_RELE(vfsp);
+ if (strcmp(fstype, "tmpfs") == 0 && (uid != -1 || gid != -1)) {
+ /* Handle tmpfs uid/gid mount options. */
+ if (lookupname(target, UIO_SYSSPACE, FOLLOW, NULLVPP,
+ &vp) == 0) {
+ (void) lx_vn_chown(vp, (uid_t)uid, (gid_t)gid);
+ VN_RELE(vp);
+ }
+ }
+
+out:
+ kmem_free(source, sourcel);
+ kmem_free(target, targetl);
+ kmem_free(options, optionsl);
+
+ return (rv == 0 ? rv : set_errno(rv));
+}
+
+/*
+ * umount() is identical to illumos, though implemented on top of umount2().
+ */
+long
+lx_umount(char *path)
+{
+ return (umount2(path, 0));
+}
+
+/*
+ * The Linux umount2() system call is identical to illumos but has a different
+ * value for MNT_FORCE (the logical equivalent to MS_FORCE).
+ */
+#define LX_MNT_FORCE 0x1
+
+long
+lx_umount2(char *path, int flg)
+{
+ int flags = 0;
+
+ if (flg & ~LX_MNT_FORCE)
+ return (set_errno(EINVAL));
+
+ if (flg & LX_MNT_FORCE)
+ flags |= MS_FORCE;
+
+ return (umount2(path, flags));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_open.c b/usr/src/uts/common/brand/lx/syscall/lx_open.c
new file mode 100644
index 0000000000..5d5e4397ce
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_open.c
@@ -0,0 +1,224 @@
+/*
+ * 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 2018 Joyent, Inc.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/inttypes.h>
+#include <sys/mutex.h>
+
+#include <sys/lx_types.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+#include <sys/brand.h>
+
+extern int fcntl(int, int, intptr_t);
+extern int openat(int, char *, int, int);
+extern int open(char *, int, int);
+extern int close(int);
+extern int cioctl(file_t *, int, intptr_t, int *);
+extern int lookupnameat(char *, enum uio_seg, int, vnode_t **, vnode_t **,
+ vnode_t *);
+
+
+static int
+ltos_open_flags(int input)
+{
+ int flags;
+
+ if (input & LX_O_PATH)
+ input &= (LX_O_DIRECTORY | LX_O_NOFOLLOW | LX_O_CLOEXEC);
+
+ /*
+ * The illumos O_ACCMODE also includes O_SEARCH|O_EXEC
+ * so this has the effect of stripping those here.
+ */
+ flags = (input & LX_O_ACCMODE);
+
+ if (input & LX_O_CREAT)
+ flags |= O_CREAT;
+ if (input & LX_O_EXCL)
+ flags |= O_EXCL;
+ if (input & LX_O_NOCTTY)
+ flags |= O_NOCTTY;
+ if (input & LX_O_TRUNC)
+ flags |= O_TRUNC;
+ if (input & LX_O_APPEND)
+ flags |= O_APPEND;
+ if (input & LX_O_NONBLOCK)
+ flags |= O_NONBLOCK;
+ if (input & LX_O_SYNC)
+ flags |= O_SYNC;
+ if (input & LX_O_LARGEFILE)
+ flags |= O_LARGEFILE;
+ if (input & LX_O_NOFOLLOW)
+ flags |= O_NOFOLLOW;
+ if (input & LX_O_CLOEXEC)
+ flags |= O_CLOEXEC;
+ if (input & LX_O_DIRECTORY)
+ flags |= O_DIRECTORY;
+
+ /*
+ * Linux uses the LX_O_DIRECT flag to do raw, synchronous I/O to the
+ * device backing the fd in question. illumos has O_DIRECT but
+ * we additionally need O_RSYNC|O_SYNC to simulate the Linux
+ * semantics as far as possible.
+ *
+ * The LX_O_DIRECT flag also requires that the transfer size and
+ * alignment of I/O buffers be a multiple of the logical block size for
+ * the underlying file system, but frankly there isn't an easy way to
+ * support that functionality without doing something like adding an
+ * fcntl(2) flag to denote LX_O_DIRECT mode.
+ *
+ * Since LX_O_DIRECT is merely a performance advisory, we'll just
+ * emulate what we can and trust that the only applications expecting
+ * an error when performing I/O from a misaligned buffer or when
+ * passing a transfer size is not a multiple of the underlying file
+ * system block size will be test suites.
+ */
+ if (input & LX_O_DIRECT)
+ flags |= (O_RSYNC|O_SYNC|O_DIRECT);
+
+ return (flags);
+}
+
+#define LX_POSTPROCESS_OPTS (LX_O_ASYNC | LX_O_PATH)
+
+static int
+lx_open_postprocess(int fd, int fmode)
+{
+ file_t *fp;
+ int error = 0;
+
+ if ((fmode & LX_POSTPROCESS_OPTS) == 0) {
+ /* Skip out early, if possible */
+ return (0);
+ }
+
+ if ((fp = getf(fd)) == NULL) {
+ /*
+ * It is possible that this fd was closed by the time we
+ * arrived here if some one is hammering away with close().
+ */
+ return (EIO);
+ }
+
+ if (fmode & LX_O_ASYNC && error == 0) {
+ if ((error = VOP_SETFL(fp->f_vnode, fp->f_flag, FASYNC,
+ fp->f_cred, NULL)) == 0) {
+ mutex_enter(&fp->f_tlock);
+ fp->f_flag |= FASYNC;
+ mutex_exit(&fp->f_tlock);
+ }
+ }
+
+ if (fmode & LX_O_PATH && error == 0) {
+ /*
+ * While the O_PATH flag has no direct analog in SunOS, it is
+ * emulated by removing both FREAD and FWRITE from f_flag.
+ * This causes read(2) and write(2) result in EBADF and can be
+ * checked for in other syscalls to trigger the correct behavior
+ * there.
+ */
+ mutex_enter(&fp->f_tlock);
+ fp->f_flag &= ~(FREAD|FWRITE);
+ mutex_exit(&fp->f_tlock);
+ }
+
+ releasef(fd);
+ if (error != 0) {
+ (void) closeandsetf(fd, NULL);
+ }
+ return (error);
+}
+
+long
+lx_openat(int atfd, char *path, int fmode, int cmode)
+{
+ int flags, fd, error;
+ mode_t mode = 0;
+
+ if (atfd == LX_AT_FDCWD)
+ atfd = AT_FDCWD;
+
+ flags = ltos_open_flags(fmode);
+
+ if ((fmode & (LX_O_NOFOLLOW|LX_O_PATH|__FLXPATH)) ==
+ (LX_O_NOFOLLOW|LX_O_PATH|__FLXPATH)) {
+ flags |= __FLXPATH;
+ }
+
+ if (flags & O_CREAT)
+ mode = (mode_t)cmode;
+
+ ttolwp(curthread)->lwp_errno = 0;
+ fd = openat(atfd, path, flags, mode);
+ if (ttolwp(curthread)->lwp_errno != 0) {
+ if ((fmode & (LX_O_NOFOLLOW|LX_O_PATH|__FLXPATH)) ==
+ (LX_O_NOFOLLOW|LX_O_PATH) &&
+ ttolwp(curthread)->lwp_errno == ELOOP) {
+ /*
+ * On Linux, if O_NOFOLLOW and O_PATH are set together
+ * and the target is a symbolic link, then openat
+ * should return a file descriptor referring to the
+ * symbolic link.
+ *
+ * This file descriptor can be used with fchownat(2),
+ * fstatat(2), linkat(2), and readlinkat(2) alongside
+ * an empty pathname.
+ *
+ * illumos has a private interface flag that causes
+ * openat() to return a file descriptor attached to
+ * the symlink's vnode. This, in conjunction with the
+ * other adjustments made in lx_open_postprocess()
+ * for O_PATH, is enough to satisfy systemd and
+ * other parts of Linux.
+ */
+ return (lx_openat(atfd, path, fmode|__FLXPATH, cmode));
+ }
+
+ if (ttolwp(curthread)->lwp_errno == EINTR)
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+
+ return (ttolwp(curthread)->lwp_errno);
+ }
+
+ if ((error = lx_open_postprocess(fd, fmode)) != 0) {
+ return (set_errno(error));
+ }
+ return (fd);
+}
+
+long
+lx_open(char *path, int fmode, int cmode)
+{
+ return (lx_openat(LX_AT_FDCWD, path, fmode, cmode));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_personality.c b/usr/src/uts/common/brand/lx/syscall/lx_personality.c
new file mode 100644
index 0000000000..e7aa945b50
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_personality.c
@@ -0,0 +1,112 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/brand.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+
+
+/*
+ * These flags are for what Linux calls "bug emulation".
+ * (Descriptions from the personality(2) Linux man page.)
+ *
+ * Flags which are currently actionable in LX:
+ * - READ_IMPLIES_EXEC (since Linux 2.6.8)
+ * With this flag set, PROT_READ implies PROT_EXEC for mmap(2).
+ *
+ * Flags which are current accepted but ignored:
+ * - UNAME26 (since Linux 3.1)
+ * Have uname(2) report a 2.6.40+ version number rather than a 3.x version
+ * number. Added as a stopgap measure to support broken applications that
+ * could not handle the kernel version- numbering switch from 2.6.x to 3.x.
+ *
+ * - ADDR_NO_RANDOMIZE (since Linux 2.6.12)
+ * With this flag set, disable address-space-layout randomization.
+ *
+ * - FDPIC_FUNCPTRS (since Linux 2.6.11)
+ * User-space function pointers to signal handlers point (on certain
+ * architectures) to descriptors.
+ *
+ * - MMAP_PAGE_ZERO (since Linux 2.4.0)
+ * Map page 0 as read-only (to support binaries that depend on this SVr4
+ * behavior).
+ *
+ * - ADDR_COMPAT_LAYOUT (since Linux 2.6.9)
+ * With this flag set, provide legacy virtual address space layout.
+ *
+ * - ADDR_LIMIT_32BIT (since Linux 2.2)
+ * Limit the address space to 32 bits.
+ *
+ * - SHORT_INODE (since Linux 2.4.0)
+ * No effects(?).
+ *
+ * - WHOLE_SECONDS (since Linux 1.2.0)
+ * No effects(?).
+ *
+ * - STICKY_TIMEOUTS (since Linux 1.2.0)
+ * With this flag set, select(2), pselect(2), and ppoll(2) do not modify the
+ * returned timeout argument when interrupted by a signal handler.
+ *
+ * - ADDR_LIMIT_3GB (since Linux 2.4.0)
+ * With this flag set, use 0xc0000000 as the offset at which to search a
+ * virtual memory chunk on mmap(2); otherwise use 0xffffe000.
+ */
+
+#define LX_PER_GET 0xffffffff
+
+long
+lx_personality(unsigned int arg)
+{
+ lx_proc_data_t *lxpd = ptolxproc(curproc);
+ unsigned int result = 0;
+
+ mutex_enter(&curproc->p_lock);
+ result = lxpd->l_personality;
+
+ if (arg == LX_PER_GET) {
+ mutex_exit(&curproc->p_lock);
+ return (result);
+ }
+
+ /*
+ * Prevent changes to the personality if the process is undergoing an
+ * exec. This will allow elfexec and friends to manipulate the
+ * personality without hinderance.
+ */
+ if ((curproc->p_flag & P_PR_EXEC) != 0) {
+ mutex_exit(&curproc->p_lock);
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Keep tabs when a non-Linux personality is set. This is silently
+ * allowed to succeed, even though the emulation required is almost
+ * certainly missing.
+ */
+ if ((arg & LX_PER_MASK) != LX_PER_LINUX) {
+ char buf[64];
+
+ (void) snprintf(buf, sizeof (buf), "invalid personality: %02X",
+ arg & LX_PER_MASK);
+ lx_unsupported(buf);
+ }
+
+ lxpd->l_personality = arg;
+ mutex_exit(&curproc->p_lock);
+ return (result);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_pgrp.c b/usr/src/uts/common/brand/lx/syscall/lx_pgrp.c
new file mode 100644
index 0000000000..2acd9d431e
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_pgrp.c
@@ -0,0 +1,189 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/lx_misc.h>
+
+#define LX_INIT_PGID 1
+#define LX_INIT_SID 1
+
+/* From uts/common/syscall/pgrpsys.c */
+extern int setpgrp(int, int, int);
+
+long
+lx_getpgrp(void)
+{
+ int pg;
+
+ /* getpgrp() */
+ pg = setpgrp(0, 0, 0);
+
+ /*
+ * If the pgrp is that of the init process, return the value Linux
+ * expects.
+ */
+ if (pg == curzone->zone_proc_initpid)
+ return (LX_INIT_PGID);
+
+ return (pg);
+}
+
+long
+lx_getpgid(int pid)
+{
+ pid_t spid;
+ int tid;
+ int pg;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ /*
+ * If the supplied pid matches that of the init process, return the pgid
+ * Linux expects.
+ */
+ if (pid == curzone->zone_proc_initpid)
+ return (LX_INIT_PGID);
+
+ if (pid == 0) {
+ spid = curproc->p_pid;
+ } else if (lx_lpid_to_spair(pid, &spid, &tid) < 0) {
+ return (set_errno(ESRCH));
+ }
+
+ /* getpgid() */
+ ttolwp(curthread)->lwp_errno = 0;
+ pg = setpgrp(4, spid, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+
+ /*
+ * If the pgid is that of the init process, return the value Linux
+ * expects.
+ */
+ if (pg == curzone->zone_proc_initpid)
+ return (LX_INIT_PGID);
+
+ return (pg);
+}
+
+long
+lx_setpgid(pid_t pid, pid_t pgid)
+{
+ pid_t spid, spgid;
+ int tid;
+ int pg;
+ int ret;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ if (pgid < 0)
+ return (set_errno(EINVAL));
+
+ if (pid == 0) {
+ spid = curproc->p_pid;
+ } else if (lx_lpid_to_spair(pid, &spid, &tid) < 0) {
+ return (set_errno(ESRCH));
+ }
+
+ if (pgid == 0) {
+ spgid = spid;
+ } else if (lx_lpid_to_spair(pgid, &spgid, &tid) < 0) {
+ return (set_errno(ESRCH));
+ }
+
+ /* setpgid() */
+ ret = setpgrp(5, spid, spgid);
+
+ if (ret == EPERM) {
+ /*
+ * On Linux, when calling setpgid with a desired pgid that is
+ * equal to the current pgid of the process, no error is
+ * emitted. This differs slightly from illumos which would
+ * return EPERM. To emulate the Linux behavior, we check
+ * specifically for matching pgids.
+ */
+
+ /* getpgid() */
+ ttolwp(curthread)->lwp_errno = 0;
+ pg = setpgrp(4, spid, 0);
+ if (ttolwp(curthread)->lwp_errno == 0 && spgid == pg)
+ return (0);
+ return (set_errno(EPERM));
+ }
+
+ return (ret);
+}
+
+long
+lx_getsid(int pid)
+{
+ pid_t spid;
+ int tid;
+ int sid;
+
+ if (pid < 0)
+ return (set_errno(ESRCH));
+
+ /*
+ * If the supplied pid matches that of the init process, return the sid
+ * Linux expects.
+ */
+ if (pid == curzone->zone_proc_initpid)
+ return (LX_INIT_SID);
+
+ if (pid == 0) {
+ spid = curproc->p_pid;
+ } else if (lx_lpid_to_spair(pid, &spid, &tid) < 0) {
+ return (set_errno(ESRCH));
+ }
+
+ /* getsid() */
+ ttolwp(curthread)->lwp_errno = 0;
+ sid = setpgrp(2, spid, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+
+
+ /*
+ * If the sid is that of the init process, return the value Linux
+ * expects.
+ */
+ if (sid == curzone->zone_proc_initpid)
+ return (LX_INIT_SID);
+
+ return (sid);
+}
+
+long
+lx_setsid(void)
+{
+ int sid;
+
+ /* setsid() */
+ ttolwp(curthread)->lwp_errno = 0;
+ sid = setpgrp(3, 0, 0);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ return (ttolwp(curthread)->lwp_errno);
+
+ /*
+ * If the sid is that of the init process, return the value Linux
+ * expects.
+ */
+ if (sid == curzone->zone_proc_initpid)
+ return (LX_INIT_SID);
+
+ return (sid);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_pipe.c b/usr/src/uts/common/brand/lx/syscall/lx_pipe.c
new file mode 100644
index 0000000000..96959e40df
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_pipe.c
@@ -0,0 +1,309 @@
+/*
+ * 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) 1984, 1986, 1987, 1988, 1989 AT&T. All Rights Reserved.
+ *
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2017 Joyent, Inc.
+ */
+
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/cpuvar.h>
+#include <sys/cred.h>
+#include <sys/user.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/stream.h>
+#include <sys/strsubr.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <sys/fs/fifonode.h>
+#include <sys/fcntl.h>
+#include <sys/policy.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+#include <sys/sysmacros.h>
+
+#define LX_DEFAULT_PIPE_SIZE 65536
+
+/*
+ * Our default value for fs.pipe-size-max mirrors Linux. The enforced maximum
+ * is meant to provide some sort of upper bound on pipe buffer sizing. Its
+ * value was chosen somewhat arbitrarily.
+ */
+uint_t lx_pipe_max_default = 1048576;
+uint_t lx_pipe_max_limit = 8388608;
+
+int
+lx_pipe_setsz(stdata_t *str, uint_t size, boolean_t is_init)
+{
+ int err;
+ stdata_t *mate;
+ lx_zone_data_t *lxzd = ztolxzd(curzone);
+ uint_t max_size = lxzd->lxzd_pipe_max_sz;
+ fifonode_t *fnp1, *fnp2;
+
+ size = P2ROUNDUP(size, PAGESIZE);
+ if (size == 0) {
+ return (EINVAL);
+ } else if (size > max_size && secpolicy_resource(CRED()) != 0) {
+ if (!is_init) {
+ return (EPERM);
+ }
+ /*
+ * If the size limit is breached during initial pipe setup,
+ * simply clamp it to the maximum. On Linux kernels prior to
+ * 4.9, this clamping would not occur and it would be possible
+ * to open a pipe with the default buffer size even if it
+ * exceeded the sysctl limit. Rather than trigger behavior
+ * here based on the configured kernel version, it is applied
+ * to all callers.
+ */
+ size = max_size;
+ ASSERT(max_size <= lx_pipe_max_limit);
+ } else if (size > lx_pipe_max_limit) {
+ /*
+ * Unlike Linux, we do maintain a global hard cap on pipe
+ * buffer limits.
+ */
+ return (EPERM);
+ }
+
+ if (!STRMATED(str)) {
+ err = strqset(RD(str->sd_wrq), QHIWAT, 0, (intptr_t)size);
+ if (err == 0) {
+ fnp1 = VTOF(str->sd_vnode);
+ mutex_enter(&fnp1->fn_lock->flk_lock);
+ fnp1->fn_hiwat = size;
+ mutex_exit(&fnp1->fn_lock->flk_lock);
+ }
+ return (err);
+ }
+
+ /*
+ * Ensure consistent order so the set operation is always attempted on
+ * the "higher" stream first.
+ */
+ if (str > str->sd_mate) {
+ VERIFY((mate = str->sd_mate) != NULL);
+ } else {
+ mate = str;
+ VERIFY((str = mate->sd_mate) != NULL);
+ }
+
+ /*
+ * While it is unfortunate that an error could occur for the latter
+ * half of the stream pair, there is little to be done about it aside
+ * from reporting the failure.
+ */
+ if ((err = strqset(RD(str->sd_wrq), QHIWAT, 0, (intptr_t)size)) == 0) {
+ err = strqset(RD(mate->sd_wrq), QHIWAT, 0, (intptr_t)size);
+ }
+
+ if (err == 0) {
+ fnp1 = VTOF(str->sd_vnode);
+ fnp2 = VTOF(str->sd_mate->sd_vnode);
+
+ /*
+ * See fnode_constructor. Both sides should have the same
+ * lock. We expect our callers to ensure that the vnodes
+ * are VFIFO and have v_op == fifovnops.
+ */
+ ASSERT(str->sd_vnode->v_type == VFIFO);
+ ASSERT(str->sd_mate->sd_vnode->v_type == VFIFO);
+ ASSERT(fnp1->fn_lock == fnp2->fn_lock);
+
+ mutex_enter(&fnp1->fn_lock->flk_lock);
+
+ fnp1->fn_hiwat = size;
+ fnp2->fn_hiwat = size;
+
+ mutex_exit(&fnp1->fn_lock->flk_lock);
+ }
+
+ return (err);
+}
+
+/*
+ * Based on native pipe(2) system call, except that the pipe is half-duplex.
+ */
+static int
+lx_hd_pipe(intptr_t arg, int flags)
+{
+ vnode_t *vp1, *vp2;
+ struct file *fp1, *fp2;
+ int error = 0;
+ int flag1, flag2, iflags;
+ int fd1, fd2;
+ stdata_t *str;
+
+ /*
+ * Validate allowed flags.
+ */
+ if ((flags & ~(FCLOEXEC|FNONBLOCK)) != 0) {
+ return (set_errno(EINVAL));
+ }
+ /*
+ * Allocate and initialize two vnodes.
+ */
+ makepipe(&vp1, &vp2);
+
+ /*
+ * Allocate and initialize two file table entries and two
+ * file pointers. The first file pointer is open for read and the
+ * second is open for write.
+ */
+ if ((error = falloc(vp1, FREAD, &fp1, &fd1)) != 0) {
+ VN_RELE(vp1);
+ VN_RELE(vp2);
+ return (set_errno(error));
+ }
+
+ if ((error = falloc(vp2, FWRITE, &fp2, &fd2)) != 0)
+ goto out2;
+
+ /*
+ * Create two stream heads and attach to each vnode.
+ */
+ if ((error = fifo_stropen(&vp1, FREAD, fp1->f_cred, 0, 0)) != 0)
+ goto out;
+
+ if ((error = fifo_stropen(&vp2, FWRITE, fp2->f_cred, 0, 0)) != 0) {
+ (void) VOP_CLOSE(vp1, FREAD, 1, (offset_t)0,
+ fp1->f_cred, NULL);
+ goto out;
+ }
+
+ strmate(vp1, vp2);
+
+ VTOF(vp1)->fn_ino = VTOF(vp2)->fn_ino = fifogetid();
+
+ /*
+ * Attempt to set pipe buffer sizes to expected value.
+ */
+ VERIFY((str = vp1->v_stream) != NULL);
+ (void) lx_pipe_setsz(str, LX_DEFAULT_PIPE_SIZE, B_TRUE);
+
+ /*
+ * Set the O_NONBLOCK flag if requested.
+ */
+ if (flags & FNONBLOCK) {
+ flag1 = fp1->f_flag;
+ flag2 = fp2->f_flag;
+ iflags = flags & FNONBLOCK;
+
+ if ((error = VOP_SETFL(vp1, flag1, iflags, fp1->f_cred,
+ NULL)) != 0) {
+ goto out_vop_close;
+ }
+ fp1->f_flag |= iflags;
+
+ if ((error = VOP_SETFL(vp2, flag2, iflags, fp2->f_cred,
+ NULL)) != 0) {
+ goto out_vop_close;
+ }
+ fp2->f_flag |= iflags;
+ }
+
+ /*
+ * Return the file descriptors to the user. They now
+ * point to two different vnodes which have different
+ * stream heads.
+ */
+ if (copyout(&fd1, &((int *)arg)[0], sizeof (int)) ||
+ copyout(&fd2, &((int *)arg)[1], sizeof (int))) {
+ error = EFAULT;
+ goto out_vop_close;
+ }
+
+ /*
+ * Now fill in the entries that falloc reserved
+ */
+ mutex_exit(&fp1->f_tlock);
+ mutex_exit(&fp2->f_tlock);
+ setf(fd1, fp1);
+ setf(fd2, fp2);
+
+ /*
+ * Optionally set the FCLOEXEC flag
+ */
+ if ((flags & FCLOEXEC) != 0) {
+ f_setfd(fd1, FD_CLOEXEC);
+ f_setfd(fd2, FD_CLOEXEC);
+ }
+
+ return (0);
+out_vop_close:
+ (void) VOP_CLOSE(vp1, FREAD, 1, (offset_t)0, fp1->f_cred, NULL);
+ (void) VOP_CLOSE(vp2, FWRITE, 1, (offset_t)0, fp2->f_cred, NULL);
+out:
+ setf(fd2, NULL);
+ unfalloc(fp2);
+out2:
+ setf(fd1, NULL);
+ unfalloc(fp1);
+ VN_RELE(vp1);
+ VN_RELE(vp2);
+ return (set_errno(error));
+}
+
+/*
+ * pipe(2) system call.
+ */
+long
+lx_pipe(intptr_t arg)
+{
+ return (lx_hd_pipe(arg, 0));
+}
+
+/*
+ * pipe2(2) system call.
+ */
+long
+lx_pipe2(intptr_t arg, int lxflags)
+{
+ int flags = 0;
+
+ /*
+ * Validate allowed flags.
+ */
+ if ((lxflags & ~(LX_O_NONBLOCK | LX_O_CLOEXEC)) != 0) {
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Convert from Linux flags to illumos flags.
+ */
+ if (lxflags & LX_O_NONBLOCK) {
+ flags |= FNONBLOCK;
+ }
+ if (lxflags & LX_O_CLOEXEC) {
+ flags |= FCLOEXEC;
+ }
+
+ return (lx_hd_pipe(arg, flags));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_poll.c b/usr/src/uts/common/brand/lx/syscall/lx_poll.c
new file mode 100644
index 0000000000..e54130aff1
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_poll.c
@@ -0,0 +1,786 @@
+/*
+ * 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) 2017, Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/sunddi.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/poll_impl.h>
+#include <sys/schedctl.h>
+#include <sys/lx_signal.h>
+
+/*
+ * Max number of FDs that can be given to poll() or select() before we return
+ * EINVAL (the Linux man page documents this value as {OPEN_MAX}, and defaults
+ * it to this value).
+ */
+int lx_poll_max_fds = 1048576;
+
+/* From uts/common/syscall/poll.c */
+extern int poll_copyin(pollstate_t *, pollfd_t *, nfds_t);
+extern int poll_common(pollstate_t *, pollfd_t *, nfds_t, timespec_t *, int *);
+
+/*
+ * These events are identical between Linux and SunOS
+ */
+#define LX_POLLIN 0x001
+#define LX_POLLPRI 0x002
+#define LX_POLLOUT 0x004
+#define LX_POLLERR 0x008
+#define LX_POLLHUP 0x010
+#define LX_POLLNVAL 0x020
+#define LX_POLLRDNORM 0x040
+#define LX_POLLRDBAND 0x080
+
+#define LX_POLL_COMMON_EVENTS (LX_POLLIN | LX_POLLPRI | LX_POLLOUT | \
+ LX_POLLERR | LX_POLLHUP | LX_POLLNVAL | LX_POLLRDNORM | LX_POLLRDBAND)
+
+/*
+ * These events differ between Linux and SunOS
+ */
+#define LX_POLLWRNORM 0x0100
+#define LX_POLLWRBAND 0x0200
+#define LX_POLLRDHUP 0x2000
+
+
+#define LX_POLL_SUPPORTED_EVENTS \
+ (LX_POLL_COMMON_EVENTS | LX_POLLWRNORM | LX_POLLWRBAND | LX_POLLRDHUP)
+
+
+static int
+lx_poll_copyin(pollstate_t *ps, pollfd_t *fds, nfds_t nfds, short *oldevt)
+{
+ int i, error = 0;
+ pollfd_t *pollfdp;
+
+ if ((error = poll_copyin(ps, fds, nfds)) != 0) {
+ return (error);
+ }
+ pollfdp = ps->ps_pollfd;
+
+ /* Convert the Linux events bitmask into SunOS equivalent. */
+ for (i = 0; i < nfds; i++) {
+ short lx_events = pollfdp[i].events;
+ short events;
+
+ /*
+ * If the caller is polling for an unsupported event, we
+ * have to bail out.
+ */
+ if (lx_events & ~LX_POLL_SUPPORTED_EVENTS) {
+ return (ENOTSUP);
+ }
+
+ events = lx_events & LX_POLL_COMMON_EVENTS;
+ if (lx_events & LX_POLLWRNORM)
+ events |= POLLWRNORM;
+ if (lx_events & LX_POLLWRBAND)
+ events |= POLLWRBAND;
+ if (lx_events & LX_POLLRDHUP)
+ events |= POLLRDHUP;
+ pollfdp[i].events = events;
+ oldevt[i] = lx_events;
+ }
+ return (0);
+}
+
+static int
+lx_poll_copyout(pollfd_t *pollfdp, pollfd_t *fds, nfds_t nfds, short *oldevt)
+{
+ int i;
+
+ /*
+ * Convert SunOS revents bitmask into Linux equivalent and restore
+ * cached events field which was swizzled by lx_poll_copyin.
+ */
+ for (i = 0; i < nfds; i++) {
+ short revents = pollfdp[i].revents;
+ short lx_revents = revents & LX_POLL_COMMON_EVENTS;
+ short orig_events = oldevt[i];
+
+ if (revents & POLLWRBAND)
+ lx_revents |= LX_POLLWRBAND;
+ if (revents & POLLRDHUP)
+ lx_revents |= LX_POLLRDHUP;
+ /*
+ * Because POLLOUT and POLLWRNORM are native defined as the
+ * same value, care must be taken when translating them to
+ * Linux where they differ.
+ */
+ if (revents & POLLOUT) {
+ if ((orig_events & LX_POLLOUT) == 0)
+ lx_revents &= ~LX_POLLOUT;
+ if (orig_events & LX_POLLWRNORM)
+ lx_revents |= LX_POLLWRNORM;
+ }
+
+ pollfdp[i].revents = lx_revents;
+ pollfdp[i].events = orig_events;
+ }
+
+ if (copyout(pollfdp, fds, sizeof (pollfd_t) * nfds) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static long
+lx_poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = ttoproc(t);
+ pollstate_t *ps = NULL;
+ pollfd_t *pollfdp = NULL;
+ short *oldevt = NULL;
+ int error = 0, fdcnt = 0;
+
+ /*
+ * Reset our signal mask, if requested.
+ */
+ if (ksetp != NULL) {
+ mutex_enter(&p->p_lock);
+ schedctl_finish_sigblock(t);
+ lwp->lwp_sigoldmask = t->t_hold;
+ t->t_hold = *ksetp;
+ t->t_flag |= T_TOMASK;
+ /*
+ * Call cv_reltimedwait_sig() just to check for signals.
+ * We will return immediately with either 0 or -1.
+ */
+ if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
+ TR_CLOCK_TICK)) {
+ mutex_exit(&p->p_lock);
+ error = EINTR;
+ goto pollout;
+ }
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Initialize pollstate and copy in pollfd data if present.
+ */
+ if (nfds != 0) {
+ /*
+ * Cap the number of FDs they can give us so we don't go
+ * allocating a huge chunk of memory. Note that this is *not*
+ * the RLIMIT_NOFILE rctl.
+ */
+ if (nfds > lx_poll_max_fds) {
+ error = EINVAL;
+ goto pollout;
+ }
+
+ /*
+ * Need to allocate memory for pollstate before anything
+ * because the mutex and cv are created in this space
+ */
+ ps = pollstate_create();
+ if (ps->ps_pcache == NULL)
+ ps->ps_pcache = pcache_alloc();
+
+ /*
+ * Certain event types which are distinct on Linux are aliased
+ * against each other on illumos. In order properly translate
+ * back into the Linux format, the original events of interest
+ * are stored in 'oldevt' for use during lx_poll_copyout.
+ */
+ oldevt = kmem_alloc(nfds * sizeof (short), KM_SLEEP);
+ if ((error = lx_poll_copyin(ps, fds, nfds, oldevt)) != 0)
+ goto pollout;
+ pollfdp = ps->ps_pollfd;
+
+ /*
+ * The Linux poll(2) implicitly polls for POLLERR and POLLHUP
+ * in addition to any other events specified for the file
+ * descriptors in question. It does not modify pollfd_t`events
+ * to reflect that fact when performing a later copyout.
+ */
+ ps->ps_implicit_ev = POLLERR | POLLHUP;
+ }
+
+ /*
+ * Perform the actual poll.
+ */
+ error = poll_common(ps, fds, nfds, tsp, &fdcnt);
+
+ /*
+ * Clear implicit event interest, if needed.
+ */
+ if (ps != NULL) {
+ ps->ps_implicit_ev = 0;
+ }
+
+
+pollout:
+ /*
+ * If we changed the signal mask but we received no signal then restore
+ * the signal mask. Otherwise psig() will deal with the signal mask.
+ */
+ if (ksetp != NULL) {
+ mutex_enter(&p->p_lock);
+ if (lwp->lwp_cursig == 0) {
+ t->t_hold = lwp->lwp_sigoldmask;
+ t->t_flag &= ~T_TOMASK;
+ }
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Copy out the events and return the fdcnt to the user.
+ */
+ if (nfds != 0 && error == 0) {
+ error = lx_poll_copyout(pollfdp, fds, nfds, oldevt);
+ }
+ if (oldevt != NULL) {
+ kmem_free(oldevt, nfds * sizeof (short));
+ }
+ if (error) {
+ return (set_errno(error));
+ }
+ return (fdcnt);
+}
+
+long
+lx_poll(pollfd_t *fds, nfds_t nfds, int timeout)
+{
+ timespec_t ts, *tsp = NULL;
+
+ if (timeout >= 0) {
+ ts.tv_sec = timeout / MILLISEC;
+ ts.tv_nsec = (timeout % MILLISEC) * MICROSEC;
+ tsp = &ts;
+ }
+
+ return (lx_poll_common(fds, nfds, tsp, NULL));
+}
+
+long
+lx_ppoll(pollfd_t *fds, nfds_t nfds, timespec_t *timeoutp, lx_sigset_t *setp)
+{
+ timespec_t ts, *tsp = NULL;
+ k_sigset_t kset, *ksetp = NULL;
+
+ /*
+ * Copy in timeout and sigmask.
+ */
+ if (timeoutp != NULL) {
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(timeoutp, &ts, sizeof (ts)))
+ return (set_errno(EFAULT));
+ } else {
+ timespec32_t ts32;
+
+ if (copyin(timeoutp, &ts32, sizeof (ts32)))
+ return (set_errno(EFAULT));
+ TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
+ }
+
+ if (itimerspecfix(&ts))
+ return (set_errno(EINVAL));
+ tsp = &ts;
+ }
+ if (setp != NULL) {
+ lx_sigset_t lset;
+
+ if (copyin(setp, &lset, sizeof (lset)))
+ return (set_errno(EFAULT));
+ lx_ltos_sigset(&lset, &kset);
+ ksetp = &kset;
+ }
+
+ return (lx_poll_common(fds, nfds, tsp, ksetp));
+}
+
+typedef struct lx_select_buf_s {
+ long *lsb_rfds;
+ long *lsb_wfds;
+ long *lsb_efds;
+ unsigned int lsb_size;
+} lx_select_buf_t;
+
+/*
+ * Size (in bytes) of buffer appropriate for fd_set copyin/copyout.
+ * Linux uses buffers of 'long' to accomplish this.
+ */
+#define LX_FD_SET_BYTES (sizeof (long))
+#define LX_FD_SET_BITS (8 * LX_FD_SET_BYTES)
+#define LX_FD_SET_SIZE(nfds) \
+ ((((nfds) + (LX_FD_SET_BITS - 1)) / LX_FD_SET_BITS) * LX_FD_SET_BYTES)
+
+static int
+lx_select_copyin(pollstate_t *ps, lx_select_buf_t *sbuf, int nfds,
+ long *rfds, long *wfds, long *efds)
+{
+ int n;
+ long *in, *out, *ex;
+ long absent = 0;
+ pollfd_t *pfd;
+ nfds_t old_nfds;
+
+ /*
+ * Just like pollsys and lx_poll, attempt to reuse ps_pollfd if it is
+ * appropriately sized. See poll_copyin for more detail.
+ */
+ old_nfds = ps->ps_nfds;
+ if (nfds != old_nfds) {
+ kmem_free(ps->ps_pollfd, old_nfds * sizeof (pollfd_t));
+ pfd = kmem_alloc(nfds * sizeof (pollfd_t), KM_SLEEP);
+ ps->ps_pollfd = pfd;
+ ps->ps_nfds = nfds;
+ } else {
+ pfd = ps->ps_pollfd;
+ }
+
+ if (rfds != NULL) {
+ if (copyin(rfds, sbuf->lsb_rfds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+ if (wfds != NULL) {
+ if (copyin(wfds, sbuf->lsb_wfds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+ if (efds != NULL) {
+ if (copyin(efds, sbuf->lsb_efds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+
+ /*
+ * For each fd, if any bits are set convert them into the appropriate
+ * pollfd struct. (Derived from libc's select logic)
+ */
+ in = (rfds != NULL) ? sbuf->lsb_rfds : &absent;
+ out = (wfds != NULL) ? sbuf->lsb_wfds : &absent;
+ ex = (efds != NULL) ? sbuf->lsb_efds : &absent;
+ for (n = 0; n < nfds; n += LX_FD_SET_BITS) {
+ unsigned long b, m, j;
+
+ b = (unsigned long)(*in | *out | *ex);
+ m = 1;
+ for (j = 0; j < LX_FD_SET_BITS; j++) {
+ int fd = n + j;
+
+ if (fd >= nfds)
+ return (0);
+ pfd->events = 0;
+ if (b & 1) {
+ pfd->fd = fd;
+ if (*in & m)
+ pfd->events |= POLLRDNORM;
+ if (*out & m)
+ pfd->events |= POLLWRNORM;
+ if (*ex & m)
+ pfd->events |= POLLRDBAND;
+ } else {
+ pfd->fd = -1;
+ }
+ pfd++;
+ b >>= 1;
+ m <<= 1;
+ }
+
+ if (rfds != NULL)
+ in++;
+ if (wfds != NULL)
+ out++;
+ if (efds != NULL)
+ ex++;
+ }
+ return (0);
+}
+
+static int
+lx_select_copyout(pollfd_t *pollfdp, lx_select_buf_t *sbuf, int nfds,
+ long *rfds, long *wfds, long *efds, int *fdcnt)
+{
+ int n;
+ pollfd_t *pfd;
+ long rv = 0;
+
+ /*
+ * If poll did not find any fds of interest, we can just zero out the
+ * fd_set fields for copyout.
+ */
+ if (*fdcnt == 0) {
+ if (rfds != NULL) {
+ bzero(sbuf->lsb_rfds, sbuf->lsb_size);
+ }
+ if (wfds != NULL) {
+ bzero(sbuf->lsb_wfds, sbuf->lsb_size);
+ }
+ if (efds != NULL) {
+ bzero(sbuf->lsb_efds, sbuf->lsb_size);
+ }
+ goto copyout;
+ }
+
+ /*
+ * For each fd, if any bits are set convert them into the appropriate
+ * pollfd struct. (Derived from libc's select logic)
+ */
+ pfd = pollfdp;
+ for (n = 0; n < nfds; n += LX_FD_SET_BITS) {
+ unsigned long m, j;
+ long in = 0, out = 0, ex = 0;
+
+ m = 1;
+ for (j = 0; j < LX_FD_SET_BITS; j++) {
+ if ((n + j) >= nfds)
+ break;
+ if (pfd->revents != 0) {
+ if (pfd->revents & POLLNVAL) {
+ return (EBADF);
+ }
+ if (pfd->revents & POLLRDNORM) {
+ in |= m;
+ rv++;
+ }
+ if (pfd->revents & POLLWRNORM) {
+ out |= m;
+ rv++;
+ }
+ if (pfd->revents & POLLRDBAND) {
+ ex |= m;
+ rv++;
+ }
+ /*
+ * Only set this bit on return if we asked
+ * about input conditions.
+ */
+ if ((pfd->revents & (POLLHUP|POLLERR)) &&
+ (pfd->events & POLLRDNORM)) {
+ if ((in & m) == 0) {
+ /* wasn't already set */
+ rv++;
+ }
+ in |= m;
+ }
+ /*
+ * Only set this bit on return if we asked
+ * about output conditions.
+ */
+ if ((pfd->revents & (POLLHUP|POLLERR)) &&
+ (pfd->events & POLLWRNORM)) {
+ if ((out & m) == 0) {
+ /* wasn't already set */
+ rv++;
+ }
+ out |= m;
+ }
+ /*
+ * Only set this bit on return if we asked
+ * about output conditions.
+ */
+ if ((pfd->revents & (POLLHUP|POLLERR)) &&
+ (pfd->events & POLLRDBAND)) {
+ if ((ex & m) == 0) {
+ /* wasn't already set */
+ rv++;
+ }
+ ex |= m;
+ }
+ }
+ m <<= 1;
+ pfd++;
+ }
+ if (rfds != NULL)
+ sbuf->lsb_rfds[n / LX_FD_SET_BITS] = in;
+ if (wfds != NULL)
+ sbuf->lsb_wfds[n / LX_FD_SET_BITS] = out;
+ if (efds != NULL)
+ sbuf->lsb_efds[n / LX_FD_SET_BITS] = ex;
+ }
+
+copyout:
+ if (rfds != NULL) {
+ if (copyout(sbuf->lsb_rfds, rfds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+ if (wfds != NULL) {
+ if (copyout(sbuf->lsb_wfds, wfds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+ if (efds != NULL) {
+ if (copyout(sbuf->lsb_efds, efds, sbuf->lsb_size) != 0) {
+ return (EFAULT);
+ }
+ }
+ *fdcnt = rv;
+ return (0);
+}
+
+
+static long
+lx_select_common(int nfds, long *rfds, long *wfds, long *efds,
+ timespec_t *tsp, k_sigset_t *ksetp)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = ttoproc(t);
+ pollstate_t *ps = NULL;
+ pollfd_t *pollfdp = NULL, *fake_fds = NULL;
+ lx_select_buf_t sbuf = {0};
+ int error = 0, fdcnt = 0;
+
+ if (nfds < 0) {
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Reset our signal mask, if requested.
+ */
+ if (ksetp != NULL) {
+ mutex_enter(&p->p_lock);
+ schedctl_finish_sigblock(t);
+ lwp->lwp_sigoldmask = t->t_hold;
+ t->t_hold = *ksetp;
+ t->t_flag |= T_TOMASK;
+ /*
+ * Call cv_reltimedwait_sig() just to check for signals.
+ * We will return immediately with either 0 or -1.
+ */
+ if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
+ TR_CLOCK_TICK)) {
+ mutex_exit(&p->p_lock);
+ error = EINTR;
+ goto out;
+ }
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Because poll caching uses the userspace pollfd_t pointer to verify
+ * cache reuse validity, a simulated value must be supplied when
+ * emulating Linux select(2). The first non-NULL pointer from
+ * rfds/wfds/efds is used for this purpose.
+ */
+ if (rfds != NULL) {
+ fake_fds = (pollfd_t *)rfds;
+ } else if (wfds != NULL) {
+ fake_fds = (pollfd_t *)wfds;
+ } else if (efds != NULL) {
+ fake_fds = (pollfd_t *)efds;
+ } else {
+ /*
+ * A non-zero nfds was supplied but all three fd_set pointers
+ * were null. Fall back to doing a simple timeout.
+ */
+ nfds = 0;
+ }
+
+ /*
+ * Initialize pollstate and copy in pollfd data if present.
+ */
+ if (nfds != 0) {
+ /*
+ * Cap the number of FDs they can give us so we don't go
+ * allocating a huge chunk of memory. Note that this is *not*
+ * the RLIMIT_NOFILE rctl.
+ */
+ if (nfds > lx_poll_max_fds) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /*
+ * Need to allocate memory for pollstate before anything
+ * because the mutex and cv are created in this space
+ */
+ ps = pollstate_create();
+ if (ps->ps_pcache == NULL)
+ ps->ps_pcache = pcache_alloc();
+
+ sbuf.lsb_size = LX_FD_SET_SIZE(nfds);
+ if (rfds != NULL)
+ sbuf.lsb_rfds = kmem_alloc(sbuf.lsb_size, KM_SLEEP);
+ if (wfds != NULL)
+ sbuf.lsb_wfds = kmem_alloc(sbuf.lsb_size, KM_SLEEP);
+ if (efds != NULL)
+ sbuf.lsb_efds = kmem_alloc(sbuf.lsb_size, KM_SLEEP);
+
+ error = lx_select_copyin(ps, &sbuf, nfds, rfds, wfds, efds);
+ if (error != 0) {
+ goto out;
+ }
+
+ pollfdp = ps->ps_pollfd;
+ }
+
+ /*
+ * Perform the actual poll.
+ */
+ error = poll_common(ps, fake_fds, (nfds_t)nfds, tsp, &fdcnt);
+
+out:
+ /*
+ * If we changed the signal mask but we received no signal then restore
+ * the signal mask. Otherwise psig() will deal with the signal mask.
+ */
+ if (ksetp != NULL) {
+ mutex_enter(&p->p_lock);
+ if (lwp->lwp_cursig == 0) {
+ t->t_hold = lwp->lwp_sigoldmask;
+ t->t_flag &= ~T_TOMASK;
+ }
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Copy out the events and return the fdcnt to the user.
+ */
+ if (error == 0 && nfds != 0) {
+ error = lx_select_copyout(pollfdp, &sbuf, nfds, rfds, wfds,
+ efds, &fdcnt);
+ }
+ if (sbuf.lsb_size != 0) {
+ if (sbuf.lsb_rfds != NULL)
+ kmem_free(sbuf.lsb_rfds, sbuf.lsb_size);
+ if (sbuf.lsb_wfds != NULL)
+ kmem_free(sbuf.lsb_wfds, sbuf.lsb_size);
+ if (sbuf.lsb_efds != NULL)
+ kmem_free(sbuf.lsb_efds, sbuf.lsb_size);
+ }
+ if (error) {
+ return (set_errno(error));
+ }
+ return (fdcnt);
+}
+
+long
+lx_select(int nfds, long *rfds, long *wfds, long *efds,
+ struct timeval *timeoutp)
+{
+ timespec_t ts, *tsp = NULL;
+
+ if (timeoutp != NULL) {
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ struct timeval tv;
+
+ if (copyin(timeoutp, &tv, sizeof (tv)))
+ return (set_errno(EFAULT));
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * (NANOSEC / MICROSEC);
+ } else {
+ struct timeval32 tv32;
+
+ if (copyin(timeoutp, &tv32, sizeof (tv32)))
+ return (set_errno(EFAULT));
+ ts.tv_sec = tv32.tv_sec;
+ ts.tv_nsec = tv32.tv_usec * (NANOSEC / MICROSEC);
+ }
+
+ if (itimerspecfix(&ts))
+ return (set_errno(EINVAL));
+ tsp = &ts;
+ }
+
+ return (lx_select_common(nfds, rfds, wfds, efds, tsp, NULL));
+}
+
+
+typedef struct {
+ uintptr_t lpsa_addr;
+ unsigned long lpsa_len;
+} lx_pselect_sig_arg_t;
+
+#if defined(_LP64)
+typedef struct {
+ caddr32_t lpsa_addr;
+ uint32_t lpsa_len;
+} lx_pselect_sig_arg32_t;
+#endif /* defined(_LP64) */
+
+long
+lx_pselect(int nfds, long *rfds, long *wfds, long *efds,
+ timespec_t *timeoutp, void *setp)
+{
+ timespec_t ts, *tsp = NULL;
+ k_sigset_t kset, *ksetp = NULL;
+
+ /*
+ * Copy in timeout and sigmask.
+ */
+ if (timeoutp != NULL) {
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(timeoutp, &ts, sizeof (ts)))
+ return (set_errno(EFAULT));
+ } else {
+ timespec32_t ts32;
+
+ if (copyin(timeoutp, &ts32, sizeof (ts32)))
+ return (set_errno(EFAULT));
+ TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
+ }
+
+ if (itimerspecfix(&ts))
+ return (set_errno(EINVAL));
+ tsp = &ts;
+ }
+ if (setp != NULL) {
+ lx_sigset_t lset, *sigaddr = NULL;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ lx_pselect_sig_arg_t lpsa;
+
+ if (copyin(setp, &lpsa, sizeof (lpsa)) != 0)
+ return (set_errno(EFAULT));
+ /*
+ * Linux forces a size to be passed only so it can
+ * check that it's the size of a sigset_t.
+ */
+ if (lpsa.lpsa_len != sizeof (lx_sigset_t))
+ return (set_errno(EINVAL));
+
+ sigaddr = (lx_sigset_t *)lpsa.lpsa_addr;
+ }
+#if defined(_LP64)
+ else {
+ lx_pselect_sig_arg32_t lpsa32;
+
+ if (copyin(setp, &lpsa32, sizeof (lpsa32)) != 0)
+ return (set_errno(EFAULT));
+ /*
+ * Linux forces a size to be passed only so it can
+ * check that it's the size of a sigset_t.
+ */
+ if (lpsa32.lpsa_len != sizeof (lx_sigset_t))
+ return (set_errno(EINVAL));
+
+ sigaddr = (lx_sigset_t *)(uint64_t)lpsa32.lpsa_addr;
+ }
+#endif /* defined(_LP64) */
+
+ /* This is where we check if the sigset is *really* NULL. */
+ if (sigaddr != NULL) {
+ if (copyin(sigaddr, &lset, sizeof (lset)) != 0)
+ return (set_errno(EFAULT));
+
+ lx_ltos_sigset(&lset, &kset);
+ ksetp = &kset;
+ }
+ }
+
+ return (lx_select_common(nfds, rfds, wfds, efds, tsp, ksetp));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_prctl.c b/usr/src/uts/common/brand/lx/syscall/lx_prctl.c
new file mode 100644
index 0000000000..8b3c267653
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_prctl.c
@@ -0,0 +1,351 @@
+/*
+ * 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) 2018, Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/priv.h>
+#include <sys/brand.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_misc.h>
+#include <lx_signum.h>
+
+#define LX_PR_SET_PDEATHSIG 1
+#define LX_PR_GET_PDEATHSIG 2
+#define LX_PR_GET_DUMPABLE 3
+#define LX_PR_SET_DUMPABLE 4
+#define LX_PR_GET_UNALIGN 5
+#define LX_PR_SET_UNALIGN 6
+#define LX_PR_GET_KEEPCAPS 7
+#define LX_PR_SET_KEEPCAPS 8
+#define LX_PR_GET_FPEMU 9
+#define LX_PR_SET_FPEMU 10
+#define LX_PR_GET_FPEXC 11
+#define LX_PR_SET_FPEXC 12
+#define LX_PR_GET_TIMING 13
+#define LX_PR_SET_TIMING 14
+#define LX_PR_SET_NAME 15
+#define LX_PR_GET_NAME 16
+#define LX_PR_GET_ENDIAN 19
+#define LX_PR_SET_ENDIAN 20
+#define LX_PR_GET_SECCOMP 21
+#define LX_PR_SET_SECCOMP 22
+#define LX_PR_CAPBSET_READ 23
+#define LX_PR_CAPBSET_DROP 24
+#define LX_PR_GET_TSC 25
+#define LX_PR_SET_TSC 26
+#define LX_PR_GET_SECUREBITS 27
+#define LX_PR_SET_SECUREBITS 28
+#define LX_PR_SET_TIMERSLACK 29
+#define LX_PR_GET_TIMERSLACK 30
+#define LX_PR_TASK_PERF_EVENTS_DISABLE 31
+#define LX_PR_TASK_PERF_EVENTS_ENABLE 32
+#define LX_PR_MCE_KILL 33
+#define LX_PR_MCE_KILL_GET 34
+#define LX_PR_SET_MM 35
+#define LX_PR_SET_CHILD_SUBREAPER 36
+#define LX_PR_GET_CHILD_SUBREAPER 37
+#define LX_PR_SET_NO_NEW_PRIVS 38
+#define LX_PR_GET_NO_NEW_PRIVS 39
+#define LX_PR_GET_TID_ADDRESS 40
+#define LX_PR_SET_THP_DISABLE 41
+#define LX_PR_GET_THP_DISABLE 42
+
+#define SECCOMP_MODE_FILTER 2
+
+long
+lx_prctl(int opt, uintptr_t arg2, uintptr_t arg3)
+{
+ long err;
+ char ebuf[64];
+
+ switch (opt) {
+ case LX_PR_GET_DUMPABLE: {
+ /* Only track in brand data - could hook into SNOCD later */
+ lx_proc_data_t *lxpd;
+ int val;
+
+ mutex_enter(&curproc->p_lock);
+ VERIFY((lxpd = ptolxproc(curproc)) != NULL);
+ val = lxpd->l_flags & LX_PROC_NO_DUMP;
+ mutex_exit(&curproc->p_lock);
+
+ return (val == 0);
+ }
+
+ case LX_PR_SET_DUMPABLE: {
+ lx_proc_data_t *lxpd;
+
+ if (arg2 != 0 && arg2 != 1) {
+ return (set_errno(EINVAL));
+ }
+
+ mutex_enter(&curproc->p_lock);
+ VERIFY((lxpd = ptolxproc(curproc)) != NULL);
+ if (arg2 == 0) {
+ lxpd->l_flags |= LX_PROC_NO_DUMP;
+ } else {
+ lxpd->l_flags &= ~LX_PROC_NO_DUMP;
+ }
+ mutex_exit(&curproc->p_lock);
+
+ return (0);
+ }
+
+ case LX_PR_GET_SECUREBITS: {
+ /* Our bits are always 0 */
+ return (0);
+ }
+
+ case LX_PR_SET_SECUREBITS: {
+ /* Ignore setting any bits from arg2 */
+ return (0);
+ }
+
+ case LX_PR_SET_KEEPCAPS: {
+ /*
+ * The closest illumos analog to SET_KEEPCAPS is the PRIV_AWARE
+ * flag. There are probably some cases where it's not exactly
+ * the same, but this will do for a first try.
+ */
+ if (arg2 == 0) {
+ err = setpflags(PRIV_AWARE_RESET, 1, NULL);
+ } else {
+ err = setpflags(PRIV_AWARE, 1, NULL);
+ }
+
+ if (err != 0) {
+ return (set_errno(err));
+ }
+ return (0);
+ }
+
+ case LX_PR_GET_NAME: {
+ /*
+ * We allow longer thread names than Linux for compatibility
+ * with other OSes (Solaris, NetBSD) that also allow larger
+ * names. We just truncate (with NUL termination) if
+ * the name is longer.
+ */
+ char name[LX_PR_SET_NAME_NAMELEN] = { 0 };
+ kthread_t *t = curthread;
+
+ mutex_enter(&ttoproc(t)->p_lock);
+ if (t->t_name != NULL) {
+ (void) strlcpy(name, t->t_name, sizeof (name));
+ }
+ mutex_exit(&ttoproc(t)->p_lock);
+
+ /*
+ * FWIW, the prctl(2) manpage says that the user-supplied
+ * buffer should be at least 16 (LX_PR_SET_NAME_NAMELEN) bytes
+ * long.
+ */
+ if (copyout(name, (void *)arg2, LX_PR_SET_NAME_NAMELEN) != 0) {
+ return (set_errno(EFAULT));
+ }
+ return (0);
+ }
+
+ case LX_PR_SET_NAME: {
+ char name[LX_PR_SET_NAME_NAMELEN] = { 0 };
+ kthread_t *t = curthread;
+ proc_t *p = ttoproc(t);
+ int ret;
+
+ ret = copyinstr((const char *)arg2, name, sizeof (name), NULL);
+ /*
+ * prctl(2) explicitly states that over length strings are
+ * silently truncated
+ */
+ if (ret != 0 && ret != ENAMETOOLONG) {
+ return (set_errno(EFAULT));
+ }
+ name[LX_PR_SET_NAME_NAMELEN - 1] = '\0';
+
+ if ((ret = thread_setname(t, name)) != 0) {
+ return (set_errno(ret));
+ }
+
+ /*
+ * In Linux, PR_SET_NAME sets the name of the thread, not the
+ * process. Due to the historical quirks of Linux's asinine
+ * thread model, this name is effectively the name of the
+ * process (as visible via ps(1)) if the thread is the first of
+ * its task group. The first thread is therefore special, and
+ * to best mimic Linux semantics we set the thread name, and if
+ * we are setting LWP 1, we also update the name of the process.
+ */
+ if (t->t_tid != 1) {
+ return (0);
+ }
+
+ /*
+ * We are currently choosing to not allow an empty thread
+ * name to clear p->p_user.u_comm and p->p_user.u_psargs.
+ * This is a slight divergence from linux behavior (which
+ * allows this) so that we can preserve the original command.
+ */
+ if (strlen(name) == 0) {
+ return (0);
+ }
+
+ /*
+ * We explicitly use t->t_name here instead of name in case
+ * a thread has come in between the above thread_setname()
+ * call and the setting of u_comm/u_psargs below. On Linux,
+ * one can also change the name of a thread (either itself or
+ * another thread in the same process) via writing to /proc, so
+ * while racy, this is no worse than what might happen on
+ * Linux.
+ */
+ mutex_enter(&p->p_lock);
+ (void) strncpy(p->p_user.u_comm, t->t_name, MAXCOMLEN + 1);
+ (void) strncpy(p->p_user.u_psargs, t->t_name, PSARGSZ);
+ mutex_exit(&p->p_lock);
+ return (0);
+ }
+
+ case LX_PR_GET_PDEATHSIG: {
+ int sig;
+ lx_proc_data_t *lxpd;
+
+ mutex_enter(&curproc->p_lock);
+ VERIFY((lxpd = ptolxproc(curproc)) != NULL);
+ sig = lxpd->l_parent_deathsig;
+ mutex_exit(&curproc->p_lock);
+
+ return (sig);
+ }
+
+ case LX_PR_SET_PDEATHSIG: {
+ int sig = lx_ltos_signo((int)arg2, 0);
+ proc_t *pp = NULL;
+ lx_proc_data_t *lxpd;
+
+ if (sig == 0 && arg2 != 0) {
+ return (set_errno(EINVAL));
+ }
+
+ mutex_enter(&pidlock);
+ /* Set signal on our self */
+ mutex_enter(&curproc->p_lock);
+ VERIFY((lxpd = ptolxproc(curproc)) != NULL);
+ lxpd->l_parent_deathsig = sig;
+ pp = curproc->p_parent;
+ mutex_exit(&curproc->p_lock);
+
+ /* Configure parent to potentially signal children on death */
+ mutex_enter(&pp->p_lock);
+ if (PROC_IS_BRANDED(pp)) {
+ VERIFY((lxpd = ptolxproc(pp)) != NULL);
+ /*
+ * Mark the parent as having children which wish to be
+ * signaled on death of parent.
+ */
+ lxpd->l_flags |= LX_PROC_CHILD_DEATHSIG;
+ } else {
+ /*
+ * If the parent is not a branded process, the needed
+ * hooks to facilitate this mechanism will not fire
+ * when it dies. We lie about success in this case.
+ */
+ /* EMPTY */
+ }
+ mutex_exit(&pp->p_lock);
+ mutex_exit(&pidlock);
+ return (0);
+ }
+
+ case LX_PR_CAPBSET_DROP: {
+ /*
+ * On recent versions of Linux the login svc drops capabilities
+ * and if that fails the svc dies and is restarted by systemd.
+ * For now we pretend dropping capabilities succeeded.
+ */
+ return (0);
+ }
+
+ case LX_PR_SET_NO_NEW_PRIVS: {
+ /*
+ * On recent versions of Linux more services are starting to set
+ * NoNewPrivs=yes in their systemd unit file. Since we currently
+ * just return success for LX_PR_CAPBSET_DROP there is currently
+ * no need to map this to the illumos privileges.
+ */
+ return (0);
+ }
+
+ case LX_PR_GET_NO_NEW_PRIVS: {
+ /*
+ * Some Linux applications (such as Elasticsearch) use
+ * PR_SET_NO_NEW_PRIVS to enable the NoNewPrivs flag, and then
+ * query the flag status, and fail unless the query indicates
+ * the flag is enabled. Since we return success in
+ * LX_PR_SET_NO_NEW_PRIVS, we assume here that the application
+ * has intended to enable the flag, so we return 1 indicating
+ * that the flag is enabled.
+ */
+ return (1);
+ }
+
+ case LX_PR_SET_SECCOMP: {
+ if (arg2 == SECCOMP_MODE_FILTER) {
+ if (arg3 == (uintptr_t)NULL) {
+ /*
+ * prctl(2) says PR_SET_SECCOMP should indicate
+ * EFAULT if arg3 is an invalid address.
+ */
+ return (set_errno(EFAULT));
+ }
+
+ /*
+ * Some Linux applications install seccomp BPF rules.
+ * For example, Elasticsearch installs rules that
+ * prevent fork/exec. Since Illumos doesn't have an
+ * analogous system call rule engine, for now we just
+ * pretend and lie that the rule installation
+ * succeeded.
+ */
+ /* So we can track who needs this */
+ DTRACE_PROBE(lx__SECCOMP);
+ return (0);
+ }
+ break;
+ }
+
+ case LX_PR_GET_SECCOMP: {
+ /*
+ * Some Linux applications (such as Elasticsearch) use
+ * PR_SET_SECCOMP to install seccomp BPF rules, and then query
+ * the status, and fail unless the query indicates its rules
+ * are installed. Since we return success in LX_PR_SET_SECCOMP,
+ * we assume here that the application has intended to install
+ * its rules, so we return SECCOMP_MODE_FILTER indicating that
+ * its rules are installed.
+ */
+ return (SECCOMP_MODE_FILTER);
+ }
+
+ default:
+ break;
+ }
+
+ (void) snprintf(ebuf, 64, "prctl option %d", opt);
+ lx_unsupported(ebuf);
+ return (set_errno(EINVAL));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_priority.c b/usr/src/uts/common/brand/lx/syscall/lx_priority.c
new file mode 100644
index 0000000000..44c60b66bf
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_priority.c
@@ -0,0 +1,192 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/procset.h>
+#include <sys/resource.h>
+#include <sys/priocntl.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+/* From uts/common/disp/priocntl.c */
+extern int donice(procset_t *, pcnice_t *);
+
+/*
+ * The Linux syscall returns priorities in the range (highest) 40-1 (lowest)
+ * and then glibc adjusts these to the range -20 - 19.
+ */
+long
+lx_getpriority(int which, id_t who)
+{
+ int rval;
+ idtype_t idtype;
+ id_t id, lid;
+ pcnice_t pcnice;
+ procset_t procset;
+
+ switch (which) {
+ case PRIO_PROCESS:
+ idtype = P_PID;
+ if (who > 0 && lx_lpid_to_spair(who, &who, &lid) < 0)
+ return (set_errno(ESRCH));
+ break;
+ case PRIO_PGRP:
+ idtype = P_PGID;
+ break;
+ case PRIO_USER:
+ idtype = P_UID;
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ /* Linux fails with a different errno on a negative id */
+ if (who < 0)
+ return (set_errno(ESRCH));
+
+ id = (who == 0 ? P_MYID : who);
+
+ pcnice.pc_val = 0;
+ pcnice.pc_op = PC_GETNICE;
+
+ setprocset(&procset, POP_AND, idtype, id, P_ALL, 0);
+
+ rval = donice(&procset, &pcnice);
+ if (rval != 0) {
+ if (which == PRIO_PROCESS &&
+ (who == curproc->p_pid || who == 0) &&
+ strcmp(sclass[curthread->t_cid].cl_name, "RT") == 0) {
+ /*
+ * donice() will always return EINVAL if we're in the
+ * RT class. The zone won't be able to put itself or any
+ * of its processes into RT, but if we put the whole
+ * zone into RT via the scheduling-class property, then
+ * getpriority would always fail. This breaks pam and
+ * prevents any login. Just pretend to be the highest
+ * priority.
+ */
+ return (40);
+ }
+
+ /*
+ * Linux does not return EINVAL for invalid 'who' values, it
+ * returns ESRCH instead. We already validated 'which' above.
+ */
+ if (rval == EINVAL)
+ rval = ESRCH;
+ return (set_errno(rval));
+ }
+
+ /*
+ * The return value of the getpriority syscall is biased by 20 to avoid
+ * returning negative values when successful (-20 internally is our
+ * highest priority and 19 is our lowest).
+ */
+ return (20 - pcnice.pc_val);
+}
+
+/*
+ * Return EPERM if the current process is not allowed to operate on the target
+ * process (which is part of the procset for setpriority).
+ */
+/* ARGSUSED */
+static int
+lx_chk_pripriv(proc_t *pp, char *dummy)
+{
+ ASSERT(MUTEX_HELD(&pidlock));
+ mutex_enter(&pp->p_lock);
+ if (!prochasprocperm(pp, curproc, CRED())) {
+ mutex_exit(&pp->p_lock);
+ return (EPERM);
+ }
+ mutex_exit(&pp->p_lock);
+ return (0);
+}
+
+long
+lx_setpriority(int which, id_t who, int prio)
+{
+ int rval;
+ idtype_t idtype;
+ id_t id, lid;
+ pcnice_t pcnice;
+ procset_t procset;
+
+ switch (which) {
+ case PRIO_PROCESS:
+ idtype = P_PID;
+ if (who > 0 && lx_lpid_to_spair(who, &who, &lid) < 0)
+ return (set_errno(ESRCH));
+ break;
+ case PRIO_PGRP:
+ idtype = P_PGID;
+ break;
+ case PRIO_USER:
+ idtype = P_UID;
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ /* Linux fails with a different errno on a negative id */
+ if (who < 0)
+ return (set_errno(ESRCH));
+
+ id = (who == 0 ? P_MYID : who);
+
+ if (prio > NZERO - 1) {
+ prio = NZERO - 1;
+ } else if (prio < -NZERO) {
+ prio = -NZERO;
+ }
+
+ pcnice.pc_val = prio;
+ pcnice.pc_op = PC_SETNICE;
+
+ setprocset(&procset, POP_AND, idtype, id, P_ALL, 0);
+
+ rval = donice(&procset, &pcnice);
+ if (rval != 0) {
+ /*
+ * Once we fully support Linux capabilities, we should update
+ * the following check to look at the CAP_SYS_NICE capability.
+ */
+ if (rval == EPERM && crgetuid(CRED()) != 0) {
+ /*
+ * donice() returns EPERM under two conditions:
+ * 1) if either the real or eff. uid don't match
+ * 2) we lack the privileges to raise the priority
+ *
+ * However, setpriority() must return a different errno
+ * based on the following:
+ * EPERM - real or eff. uid did not match
+ * EACCES - trying to increase priority
+ *
+ * We use lx_chk_pripriv to determine which case we hit.
+ *
+ * Note that the native setpriority(3C) code has the
+ * same race on re-checking.
+ */
+ if (dotoprocs(&procset, lx_chk_pripriv, NULL) != EPERM)
+ rval = EACCES;
+ }
+
+ return (set_errno(rval));
+ }
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_rename.c b/usr/src/uts/common/brand/lx/syscall/lx_rename.c
new file mode 100644
index 0000000000..2fad627771
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_rename.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 2016 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/lx_fcntl.h>
+
+/* From uts/common/syscall/rename.c */
+extern int rename(char *, char *);
+extern int renameat(int, char *, int, char *);
+
+long
+lx_rename(char *p1, char *p2)
+{
+ return (rename(p1, p2));
+}
+
+long
+lx_renameat(int atfd1, char *p1, int atfd2, char *p2)
+{
+ if (atfd1 == LX_AT_FDCWD)
+ atfd1 = AT_FDCWD;
+
+ if (atfd2 == LX_AT_FDCWD)
+ atfd2 = AT_FDCWD;
+
+ return (renameat(atfd1, p1, atfd2, p2));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_rlimit.c b/usr/src/uts/common/brand/lx/syscall/lx_rlimit.c
new file mode 100644
index 0000000000..eadc588824
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_rlimit.c
@@ -0,0 +1,597 @@
+/*
+ * 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 2017 Joyent, Inc.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/zone.h>
+#include <sys/cpuvar.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+#include <sys/sysmacros.h>
+#include <sys/var.h>
+
+#define LX_RLIMIT_CPU 0
+#define LX_RLIMIT_FSIZE 1
+#define LX_RLIMIT_DATA 2
+#define LX_RLIMIT_STACK 3
+#define LX_RLIMIT_CORE 4
+#define LX_RLIMIT_RSS 5
+#define LX_RLIMIT_NPROC 6
+#define LX_RLIMIT_NOFILE 7
+#define LX_RLIMIT_MEMLOCK 8
+#define LX_RLIMIT_AS 9
+#define LX_RLIMIT_LOCKS 10 /* NA limit on locks, early 2.4 only */
+#define LX_RLIMIT_SIGPENDING 11
+#define LX_RLIMIT_MSGQUEUE 12
+#define LX_RLIMIT_NICE 13 /* NA ceiling for nice */
+#define LX_RLIMIT_RTPRIO 14 /* NA ceiling on the RT priority */
+#define LX_RLIMIT_RTTIME 15 /* NA cpu limit for RT proc. */
+
+#define LX_RLIMIT_NLIMITS 16
+
+#define RCTL_INFINITE(x) \
+ ((x->rcv_flagaction & RCTL_LOCAL_MAXIMAL) && \
+ (x->rcv_flagaction & RCTL_GLOBAL_INFINITE))
+
+typedef struct {
+ ulong_t rlim_cur;
+ ulong_t rlim_max;
+} lx_rlimit_t;
+
+typedef struct {
+ uint32_t rlim_cur;
+ uint32_t rlim_max;
+} lx_rlimit32_t;
+
+/*
+ * Linux supports many of the same resources that we do, but on illumos these
+ * are rctls. Instead of using rlimit, we use rctls for all of the limits.
+ * This table is used to translate Linux rlimit keys into the illumos legacy
+ * rlimit. We then primarily use the rctl/rlimit compatability code to
+ * manage these.
+ */
+static int l_to_r[LX_RLIMIT_NLIMITS] = {
+ RLIMIT_CPU, /* 0 CPU */
+ RLIMIT_FSIZE, /* 1 FSIZE */
+ RLIMIT_DATA, /* 2 DATA */
+ RLIMIT_STACK, /* 3 STACK */
+ RLIMIT_CORE, /* 4 CORE */
+ -1, /* 5 RSS */
+ -1, /* 6 NPROC */
+ RLIMIT_NOFILE, /* 7 NOFILE */
+ -1, /* 8 MEMLOCK */
+ RLIMIT_AS, /* 9 AS */
+ -1, /* 10 LOCKS */
+ -1, /* 11 SIGPENDING */
+ -1, /* 12 MSGQUEUE */
+ -1, /* 13 NICE */
+ -1, /* 14 RTPRIO */
+ -1 /* 15 RTTIME */
+};
+
+/*
+ * Magic value Linux uses to indicate infinity
+ */
+#define LX_RLIM_INFINITY_N ULONG_MAX
+
+void
+lx_get_rctl(char *nm, struct rlimit64 *rlp64)
+{
+ rctl_hndl_t hndl;
+ rctl_val_t *oval, *nval;
+
+ rlp64->rlim_cur = RLIM_INFINITY;
+ rlp64->rlim_max = RLIM_INFINITY;
+
+ nval = kmem_alloc(sizeof (rctl_val_t), KM_SLEEP);
+ mutex_enter(&curproc->p_lock);
+
+ hndl = rctl_hndl_lookup(nm);
+ oval = NULL;
+ while ((hndl != -1) && rctl_local_get(hndl, oval, nval, curproc) == 0) {
+ oval = nval;
+ switch (nval->rcv_privilege) {
+ case RCPRIV_BASIC:
+ if (!RCTL_INFINITE(nval))
+ rlp64->rlim_cur = nval->rcv_value;
+ break;
+ case RCPRIV_PRIVILEGED:
+ if (!RCTL_INFINITE(nval))
+ rlp64->rlim_max = nval->rcv_value;
+ break;
+ }
+ }
+
+ mutex_exit(&curproc->p_lock);
+ kmem_free(nval, sizeof (rctl_val_t));
+
+ if (rlp64->rlim_cur == RLIM_INFINITY &&
+ rlp64->rlim_max != RLIM_INFINITY)
+ rlp64->rlim_cur = rlp64->rlim_max;
+}
+
+static int
+lx_getrlimit_common(int lx_resource, uint64_t *rlim_curp, uint64_t *rlim_maxp)
+{
+ lx_proc_data_t *pd = ptolxproc(curproc);
+ int resource;
+ int64_t cur = -1;
+ boolean_t cur_inf = B_FALSE;
+ int64_t max = -1;
+ boolean_t max_inf = B_FALSE;
+ struct rlimit64 rlim64;
+
+ if (lx_resource < 0 || lx_resource >= LX_RLIMIT_NLIMITS)
+ return (EINVAL);
+
+ switch (lx_resource) {
+ case LX_RLIMIT_LOCKS:
+ rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_cur;
+ rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_max;
+ break;
+
+ case LX_RLIMIT_NICE:
+ rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_NICE].rlim_cur;
+ rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_NICE].rlim_max;
+ break;
+
+ case LX_RLIMIT_RTPRIO:
+ rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_cur;
+ rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_max;
+ break;
+
+ case LX_RLIMIT_RTTIME:
+ rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_cur;
+ rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_max;
+ break;
+
+ case LX_RLIMIT_RSS:
+ /* zone.max-physical-memory */
+ zone_get_physmem_data(curzone->zone_id,
+ (pgcnt_t *)&rlim64.rlim_cur,
+ (pgcnt_t *)&rlim64.rlim_max); /* max is dummy variable */
+ rlim64.rlim_cur = rlim64.rlim_max = ptob(rlim64.rlim_cur);
+
+ break;
+
+ case LX_RLIMIT_NPROC:
+ /*
+ * This is a limit on the number of processes for a
+ * real user ID (not enforced for privileged processes).
+ *
+ * This is analogous to v.v_maxup but is further capped
+ * by zone.max-processes
+ */
+ rlim64.rlim_cur = rlim64.rlim_max =
+ MIN(v.v_maxup, curzone->zone_nprocs_ctl);
+ break;
+
+ case LX_RLIMIT_MEMLOCK:
+ lx_get_rctl("process.max-locked-memory", &rlim64);
+
+ /* If unlimited, use zone.max-locked-memory */
+ if (rlim64.rlim_max == RLIM64_INFINITY)
+ rlim64.rlim_max = curzone->zone_locked_mem_ctl;
+ if (rlim64.rlim_cur == RLIM64_INFINITY)
+ rlim64.rlim_cur = curzone->zone_locked_mem_ctl;
+ break;
+
+ case LX_RLIMIT_SIGPENDING:
+ lx_get_rctl("process.max-sigqueue-size", &rlim64);
+ break;
+
+ case LX_RLIMIT_MSGQUEUE:
+ lx_get_rctl("process.max-msg-messages", &rlim64);
+ break;
+
+ default:
+ resource = l_to_r[lx_resource];
+
+ mutex_enter(&curproc->p_lock);
+ (void) rctl_rlimit_get(rctlproc_legacy[resource], curproc,
+ &rlim64);
+ mutex_exit(&curproc->p_lock);
+ break;
+ }
+
+
+ if (rlim64.rlim_cur == RLIM64_INFINITY) {
+ cur = LX_RLIM_INFINITY_N;
+ } else {
+ cur = rlim64.rlim_cur;
+ }
+ if (rlim64.rlim_max == RLIM64_INFINITY) {
+ max = LX_RLIM_INFINITY_N;
+ } else {
+ max = rlim64.rlim_max;
+ }
+
+ if (lx_resource == LX_RLIMIT_STACK && cur > INT_MAX) {
+ /*
+ * Stunningly, Linux has somehow managed to confuse the concept
+ * of a "limit" with that of a "default" -- and the value of
+ * RLIMIT_STACK is used by NPTL as the _default_ stack size if
+ * it isn't specified. (!!) Even for a system that prides
+ * itself on slapdash castles of junk, this is an amazingly
+ * willful act of incompetence -- and one that is gleefully
+ * confessed in the pthread_create() man page: "if the
+ * RLIMIT_STACK soft resource limit at the time the program
+ * started has any value other than 'unlimited', then it
+ * determines the default stack size of new threads." A
+ * typical stack limit for us is 32TB; if it needs to be said,
+ * setting the default stack size to be 32TB doesn't work so
+ * well! Of course, glibc dropping a deuce in its pants
+ * becomes our problem -- so to prevent smelly accidents we
+ * tell Linux that any stack limit over the old (32-bit) values
+ * for infinity are just infinitely large.
+ */
+ cur_inf = B_TRUE;
+ max_inf = B_TRUE;
+ }
+
+ if (cur_inf) {
+ *rlim_curp = LX_RLIM64_INFINITY;
+ } else {
+ *rlim_curp = cur;
+ }
+
+ if (max_inf) {
+ *rlim_maxp = LX_RLIM64_INFINITY;
+ } else {
+ *rlim_maxp = max;
+ }
+
+ return (0);
+}
+
+/*
+ * This is the 'new' getrlimit, variously called getrlimit or ugetrlimit
+ * in Linux headers and code. The only difference between this and the old
+ * getrlimit (variously called getrlimit or old_getrlimit) is the value of
+ * RLIM_INFINITY, which is smaller for the older version. Modern code will
+ * use this version by default.
+ */
+long
+lx_getrlimit(int resource, lx_rlimit_t *rlp)
+{
+ int rv;
+ lx_rlimit_t rl;
+ uint64_t rlim_cur, rlim_max;
+
+ rv = lx_getrlimit_common(resource, &rlim_cur, &rlim_max);
+ if (rv != 0)
+ return (set_errno(rv));
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (rlim_cur == LX_RLIM64_INFINITY)
+ rl.rlim_cur = LX_RLIM_INFINITY_N;
+ else if (rlim_cur > LX_RLIM_INFINITY_N)
+ rl.rlim_cur = LX_RLIM_INFINITY_N;
+ else
+ rl.rlim_cur = (ulong_t)rlim_cur;
+
+ if (rlim_max == LX_RLIM64_INFINITY)
+ rl.rlim_max = LX_RLIM_INFINITY_N;
+ else if (rlim_max > LX_RLIM_INFINITY_N)
+ rl.rlim_max = LX_RLIM_INFINITY_N;
+ else
+ rl.rlim_max = (ulong_t)rlim_max;
+
+ if (copyout(&rl, rlp, sizeof (rl)) != 0)
+ return (set_errno(EFAULT));
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ lx_rlimit32_t rl32;
+
+ if (rlim_cur > UINT_MAX)
+ rl.rlim_cur = UINT_MAX;
+ else
+ rl.rlim_cur = (ulong_t)rlim_cur;
+
+ if (rlim_max > UINT_MAX)
+ rl.rlim_max = UINT_MAX;
+ else
+ rl.rlim_max = (ulong_t)rlim_max;
+
+ rl32.rlim_cur = rl.rlim_cur;
+ rl32.rlim_max = rl.rlim_max;
+
+ if (copyout(&rl32, rlp, sizeof (rl32)) != 0)
+ return (set_errno(EFAULT));
+ }
+#endif
+
+ return (0);
+}
+
+/*
+ * This is the 'old' getrlimit, variously called getrlimit or old_getrlimit
+ * in Linux headers and code. The only difference between this and the new
+ * getrlimit (variously called getrlimit or ugetrlimit) is the value of
+ * RLIM_INFINITY, which is smaller for the older version.
+ *
+ * This is only used for 32-bit code.
+ */
+long
+lx_oldgetrlimit(int resource, lx_rlimit_t *rlp)
+{
+ int rv;
+ lx_rlimit32_t rl32;
+ uint64_t rlim_cur, rlim_max;
+
+ rv = lx_getrlimit_common(resource, &rlim_cur, &rlim_max);
+ if (rv != 0)
+ return (set_errno(rv));
+
+ if (rlim_cur > INT_MAX)
+ rl32.rlim_cur = INT_MAX;
+ else
+ rl32.rlim_cur = (ulong_t)rlim_cur;
+
+ if (rlim_max > INT_MAX)
+ rl32.rlim_max = INT_MAX;
+ else
+ rl32.rlim_max = (ulong_t)rlim_cur;
+
+ if (copyout(&rl32, rlp, sizeof (rl32)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+static int
+lx_set_rctl(char *nm, struct rlimit64 *rlp64)
+{
+ int err;
+ rctl_hndl_t hndl;
+ rctl_alloc_gp_t *gp;
+
+ gp = rctl_rlimit_set_prealloc(1);
+
+ mutex_enter(&curproc->p_lock);
+
+ hndl = rctl_hndl_lookup(nm);
+
+ /*
+ * We're not supposed to do this but since we want all our rctls to
+ * behave like rlimits, we take advantage of this function to set up
+ * this way.
+ */
+ err = rctl_rlimit_set(hndl, curproc, rlp64, gp, RCTL_LOCAL_DENY, 0,
+ CRED());
+
+ mutex_exit(&curproc->p_lock);
+
+ rctl_prealloc_destroy(gp);
+
+ return (err);
+}
+
+static int
+lx_setrlimit_common(int lx_resource, uint64_t rlim_cur, uint64_t rlim_max)
+{
+ lx_proc_data_t *pd = ptolxproc(curproc);
+ int err;
+ int resource;
+ rctl_alloc_gp_t *gp;
+ struct rlimit64 rl64;
+
+ if (lx_resource < 0 || lx_resource >= LX_RLIMIT_NLIMITS)
+ return (EINVAL);
+
+ switch (lx_resource) {
+ case LX_RLIMIT_LOCKS:
+ pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_cur = rlim_cur;
+ pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_max = rlim_max;
+ break;
+
+ case LX_RLIMIT_NICE:
+ pd->l_fake_limits[LX_RLFAKE_NICE].rlim_cur = rlim_cur;
+ pd->l_fake_limits[LX_RLFAKE_NICE].rlim_max = rlim_max;
+ break;
+
+ case LX_RLIMIT_RTPRIO:
+ pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_cur = rlim_cur;
+ pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_max = rlim_max;
+ break;
+
+ case LX_RLIMIT_RTTIME:
+ pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_cur = rlim_cur;
+ pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_max = rlim_max;
+ break;
+
+ case LX_RLIMIT_RSS:
+ /*
+ * zone.max-physical-memory
+ * Since we're emulating the value via a zone rctl, we can't
+ * set that from within the zone. Lie and say we set the value.
+ */
+ break;
+
+ case LX_RLIMIT_NPROC:
+ /*
+ * zone.max-processes
+ * Since we're emulating the value via a zone rctl, we can't
+ * set that from within the zone. Lie and say we set the value.
+ */
+ break;
+
+ case LX_RLIMIT_MEMLOCK:
+ /*
+ * We allow setting to unlimited (LX_RLIM_INFINITY_N). The zone
+ * limit will always apply.
+ */
+ rl64.rlim_cur = rlim_cur;
+ rl64.rlim_max = rlim_max;
+ err = lx_set_rctl("process.max-locked-memory", &rl64);
+ if (err != 0)
+ return (set_errno(err));
+ break;
+
+ case LX_RLIMIT_SIGPENDING:
+ /*
+ * On Ubuntu at least, the login and sshd processes expect to
+ * set this limit to 16k and login will fail if this fails. On
+ * illumos we have a system limit of 8k and normally the
+ * privileged limit is 512. We simply pretend this works to
+ * allow login to work.
+ */
+ if (rlim_max > 8192)
+ return (0);
+
+ rl64.rlim_cur = rlim_cur;
+ rl64.rlim_max = rlim_max;
+ if ((err = lx_set_rctl("process.max-sigqueue-size", &rl64))
+ != 0)
+ return (set_errno(err));
+ break;
+
+ case LX_RLIMIT_MSGQUEUE:
+ rl64.rlim_cur = rlim_cur;
+ rl64.rlim_max = rlim_max;
+ if ((err = lx_set_rctl("process.max-msg-messages", &rl64)) != 0)
+ return (set_errno(err));
+ break;
+
+ default:
+ resource = l_to_r[lx_resource];
+
+ /*
+ * Linux limits the max number of open files to 1m and there is
+ * a test for this.
+ */
+ if (lx_resource == LX_RLIMIT_NOFILE && rlim_max > (1024 * 1024))
+ return (EPERM);
+
+ rl64.rlim_cur = rlim_cur;
+ rl64.rlim_max = rlim_max;
+ gp = rctl_rlimit_set_prealloc(1);
+
+ mutex_enter(&curproc->p_lock);
+ err = rctl_rlimit_set(rctlproc_legacy[resource], curproc,
+ &rl64, gp, rctlproc_flags[resource],
+ rctlproc_signals[resource], CRED());
+ mutex_exit(&curproc->p_lock);
+
+ rctl_prealloc_destroy(gp);
+ if (err != 0)
+ return (set_errno(err));
+ break;
+ }
+
+ return (0);
+}
+
+long
+lx_setrlimit(int resource, lx_rlimit_t *rlp)
+{
+ int rv;
+ lx_rlimit_t rl;
+ uint64_t rlim_cur, rlim_max;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(rlp, &rl, sizeof (rl)) != 0)
+ return (set_errno(EFAULT));
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ lx_rlimit32_t rl32;
+
+ if (copyin(rlp, &rl32, sizeof (rl32)) != 0)
+ return (set_errno(EFAULT));
+
+ rl.rlim_cur = rl32.rlim_cur;
+ rl.rlim_max = rl32.rlim_max;
+ }
+#endif
+
+ if ((rl.rlim_max != LX_RLIM_INFINITY_N &&
+ rl.rlim_cur == LX_RLIM_INFINITY_N) ||
+ rl.rlim_cur > rl.rlim_max)
+ return (set_errno(EINVAL));
+
+ if (rl.rlim_cur == LX_RLIM_INFINITY_N)
+ rlim_cur = LX_RLIM64_INFINITY;
+ else
+ rlim_cur = rl.rlim_cur;
+
+ if (rl.rlim_max == LX_RLIM_INFINITY_N)
+ rlim_max = LX_RLIM64_INFINITY;
+ else
+ rlim_max = rl.rlim_max;
+
+ rv = lx_setrlimit_common(resource, rlim_cur, rlim_max);
+ if (rv != 0)
+ return (set_errno(rv));
+ return (0);
+}
+
+/*
+ * From the man page:
+ * The Linux-specific prlimit() system call combines and extends the
+ * functionality of setrlimit() and getrlimit(). It can be used to both set
+ * and get the resource limits of an arbitrary process.
+ *
+ * If pid is 0, then the call applies to the calling process.
+ */
+long
+lx_prlimit64(pid_t pid, int resource, lx_rlimit64_t *nrlp, lx_rlimit64_t *orlp)
+{
+ int rv;
+ lx_rlimit64_t nrl, orl;
+
+ if (pid != 0) {
+ /* XXX TBD if needed */
+ char buf[80];
+
+ (void) snprintf(buf, sizeof (buf),
+ "setting prlimit %d for another process\n", resource);
+ lx_unsupported(buf);
+ return (ENOTSUP);
+ }
+
+ if (orlp != NULL) {
+ /* we first get the current limits */
+ rv = lx_getrlimit_common(resource, &orl.rlim_cur,
+ &orl.rlim_max);
+ if (rv != 0)
+ return (set_errno(rv));
+ }
+
+ if (nrlp != NULL) {
+ if (copyin(nrlp, &nrl, sizeof (nrl)) != 0)
+ return (set_errno(EFAULT));
+
+ if ((nrl.rlim_max != LX_RLIM64_INFINITY &&
+ nrl.rlim_cur == LX_RLIM64_INFINITY) ||
+ nrl.rlim_cur > nrl.rlim_max)
+ return (set_errno(EINVAL));
+
+ rv = lx_setrlimit_common(resource, nrl.rlim_cur, nrl.rlim_max);
+ if (rv != 0)
+ return (set_errno(rv));
+ }
+
+ if (orlp != NULL) {
+ /* now return the original limits, if necessary */
+ if (copyout(&orl, orlp, sizeof (orl)) != 0)
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_rw.c b/usr/src/uts/common/brand/lx/syscall/lx_rw.c
new file mode 100644
index 0000000000..34aafcaf5d
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_rw.c
@@ -0,0 +1,956 @@
+/*
+ * 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 2017 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/nbmlock.h>
+#include <sys/limits.h>
+
+/* uts/common/syscall/rw.c */
+extern size_t copyout_max_cached;
+
+
+/* Common routines */
+
+static int
+lx_iovec_copyin(void *uiovp, int iovcnt, iovec_t *kiovp, ssize_t *count)
+{
+#ifdef _SYSCALL32_IMPL
+ /*
+ * 32-bit callers need to have their iovec expanded, while ensuring
+ * that they can't move more than 2Gbytes of data in a single call.
+ */
+ if (get_udatamodel() == DATAMODEL_ILP32) {
+ struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+ int aiov32len = 0;
+ ssize32_t total32 = 0;
+ int i;
+
+ if (iovcnt > IOV_MAX_STACK) {
+ aiov32len = iovcnt * sizeof (iovec32_t);
+ aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+ }
+
+ if (copyin(uiovp, aiov32, iovcnt * sizeof (iovec32_t))) {
+ if (aiov32len != 0) {
+ kmem_free(aiov32, aiov32len);
+ }
+ return (EFAULT);
+ }
+
+ for (i = 0; i < iovcnt; i++) {
+ ssize32_t iovlen32 = aiov32[i].iov_len;
+ total32 += iovlen32;
+ if (iovlen32 < 0 || total32 < 0) {
+ if (aiov32len != 0) {
+ kmem_free(aiov32, aiov32len);
+ }
+ return (EINVAL);
+ }
+ kiovp[i].iov_len = iovlen32;
+ kiovp[i].iov_base =
+ (caddr_t)(uintptr_t)aiov32[i].iov_base;
+ /* Linux does a basic sanity test on the address */
+ if ((uintptr_t)kiovp[i].iov_base >= USERLIMIT32) {
+ if (aiov32len != 0) {
+ kmem_free(aiov32, aiov32len);
+ }
+ return (EFAULT);
+ }
+ }
+ *count = total32;
+
+ if (aiov32len != 0)
+ kmem_free(aiov32, aiov32len);
+ } else
+#endif
+ {
+ ssize_t total = 0;
+ int i;
+
+ if (copyin(uiovp, kiovp, iovcnt * sizeof (iovec_t)))
+ return (EFAULT);
+ for (i = 0; i < iovcnt; i++) {
+ ssize_t iovlen = kiovp[i].iov_len;
+ total += iovlen;
+ if (iovlen < 0 || total < 0) {
+ return (EINVAL);
+ }
+ /* Linux does a basic sanity test on the address */
+ if ((uintptr_t)kiovp[i].iov_base >= USERLIMIT) {
+ return (EFAULT);
+ }
+ }
+ *count = total;
+ }
+ return (0);
+}
+
+int
+lx_read_common(file_t *fp, uio_t *uiop, size_t *nread, boolean_t positioned)
+{
+ vnode_t *vp = fp->f_vnode;
+ int error = 0, rwflag = 0, ioflag;
+ ssize_t count = uiop->uio_resid;
+ size_t rcount = 0;
+ struct cpu *cp;
+ boolean_t in_crit = B_FALSE;
+
+ if (fp->f_vnode->v_type == VDIR) {
+ return (EISDIR);
+ }
+ if (positioned &&
+ (fp->f_vnode->v_type == VFIFO || fp->f_vnode->v_type == VSOCK)) {
+ return (ESPIPE);
+ }
+
+ /*
+ * We have to enter the critical region before calling VOP_RWLOCK
+ * to avoid a deadlock with ufs.
+ */
+ if (nbl_need_check(vp)) {
+ int svmand;
+
+ nbl_start_crit(vp, RW_READER);
+ in_crit = B_TRUE;
+ error = nbl_svmand(vp, fp->f_cred, &svmand);
+ if (error != 0)
+ goto out;
+ if (nbl_conflict(vp, NBL_READ, uiop->uio_offset, count, svmand,
+ NULL) != 0) {
+ error = EACCES;
+ goto out;
+ }
+ }
+
+ (void) VOP_RWLOCK(vp, rwflag, NULL);
+ /*
+ * For non-positioned reads, recheck offset/count validity inside
+ * VOP_WRLOCK to prevent filesize from changing during validation.
+ */
+ if (!positioned) {
+ u_offset_t uoffset = (u_offset_t)(ulong_t)fp->f_offset;
+
+ if ((vp->v_type == VREG) && (uoffset >= OFFSET_MAX(fp))) {
+ struct vattr va;
+
+ va.va_mask = AT_SIZE;
+ error = VOP_GETATTR(vp, &va, 0, fp->f_cred, NULL);
+ VOP_RWUNLOCK(vp, rwflag, NULL);
+ if (error != 0)
+ goto out;
+ /* We have to return EOF if fileoff is >= file size. */
+ if (uoffset >= va.va_size)
+ goto out;
+ /*
+ * File is greater than or equal to maxoff and
+ * therefore we return EOVERFLOW.
+ */
+ error = EOVERFLOW;
+ goto out;
+ }
+ if ((vp->v_type == VREG) &&
+ (uoffset + count > OFFSET_MAX(fp))) {
+ count = (ssize_t)(OFFSET_MAX(fp) - uoffset);
+ uiop->uio_resid = count;
+ }
+ uiop->uio_offset = uoffset;
+ }
+ ioflag = uiop->uio_fmode & (FAPPEND|FSYNC|FDSYNC|FRSYNC);
+ /* If read sync is not asked for, filter sync flags */
+ if ((ioflag & FRSYNC) == 0)
+ ioflag &= ~(FSYNC|FDSYNC);
+ error = VOP_READ(vp, uiop, ioflag, fp->f_cred, NULL);
+ rcount = count - uiop->uio_resid;
+ CPU_STATS_ENTER_K();
+ cp = CPU;
+ CPU_STATS_ADDQ(cp, sys, sysread, 1);
+ CPU_STATS_ADDQ(cp, sys, readch, (ulong_t)rcount);
+ CPU_STATS_EXIT_K();
+ ttolwp(curthread)->lwp_ru.ioch += (ulong_t)rcount;
+ /* Store offset for non-positioned reads */
+ if (!positioned) {
+ if (vp->v_type == VFIFO) {
+ /* Backward compatibility */
+ fp->f_offset = rcount;
+ } else if (((fp->f_flag & FAPPEND) == 0) ||
+ (vp->v_type != VREG) || (count != 0)) {
+ /* POSIX */
+ fp->f_offset = uiop->uio_loffset;
+ }
+ }
+ VOP_RWUNLOCK(vp, rwflag, NULL);
+
+out:
+ if (in_crit)
+ nbl_end_crit(vp);
+ *nread = rcount;
+ return (error);
+}
+
+int
+lx_write_common(file_t *fp, uio_t *uiop, size_t *nwrite, boolean_t positioned)
+{
+ vnode_t *vp = fp->f_vnode;
+ int error = 0, rwflag = 1, ioflag;
+ ssize_t count = uiop->uio_resid;
+ size_t wcount = 0;
+ struct cpu *cp;
+ boolean_t in_crit = B_FALSE;
+
+ if (positioned &&
+ (fp->f_vnode->v_type == VFIFO || fp->f_vnode->v_type == VSOCK)) {
+ return (ESPIPE);
+ }
+
+ /*
+ * We have to enter the critical region before calling VOP_RWLOCK
+ * to avoid a deadlock with ufs.
+ */
+ if (nbl_need_check(vp)) {
+ int svmand;
+
+ nbl_start_crit(vp, RW_READER);
+ in_crit = B_TRUE;
+ error = nbl_svmand(vp, fp->f_cred, &svmand);
+ if (error != 0)
+ goto out;
+ if (nbl_conflict(vp, NBL_WRITE, uiop->uio_loffset, count,
+ svmand, NULL) != 0) {
+ error = EACCES;
+ goto out;
+ }
+ }
+
+ (void) VOP_RWLOCK(vp, rwflag, NULL);
+
+ if (!positioned) {
+ /*
+ * For non-positioned writes, the value of fp->f_offset is
+ * re-queried while inside VOP_RWLOCK. This ensures that other
+ * writes which alter the filesize will be taken into account.
+ */
+ uiop->uio_loffset = fp->f_offset;
+ ioflag = uiop->uio_fmode & (FAPPEND|FSYNC|FDSYNC|FRSYNC);
+ } else {
+ /*
+ * In a senseless departure from POSIX, positioned write calls
+ * on Linux do _not_ ignore the O_APPEND flag.
+ */
+ ioflag = uiop->uio_fmode & (FAPPEND|FSYNC|FDSYNC|FRSYNC);
+ }
+ if (vp->v_type == VREG) {
+ u_offset_t fileoff = (u_offset_t)(ulong_t)uiop->uio_loffset;
+
+ if (fileoff >= curproc->p_fsz_ctl) {
+ VOP_RWUNLOCK(vp, rwflag, NULL);
+ mutex_enter(&curproc->p_lock);
+ (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
+ curproc->p_rctls, curproc, RCA_UNSAFE_SIGINFO);
+ mutex_exit(&curproc->p_lock);
+ error = EFBIG;
+ goto out;
+ }
+ if (fileoff >= OFFSET_MAX(fp)) {
+ VOP_RWUNLOCK(vp, rwflag, NULL);
+ error = EFBIG;
+ goto out;
+ }
+ if (fileoff + count > OFFSET_MAX(fp)) {
+ count = (ssize_t)(OFFSET_MAX(fp) - fileoff);
+ uiop->uio_resid = count;
+ }
+ }
+
+ error = VOP_WRITE(vp, uiop, ioflag, fp->f_cred, NULL);
+ wcount = count - uiop->uio_resid;
+ CPU_STATS_ENTER_K();
+ cp = CPU;
+ CPU_STATS_ADDQ(cp, sys, syswrite, 1);
+ CPU_STATS_ADDQ(cp, sys, writech, (ulong_t)wcount);
+ CPU_STATS_EXIT_K();
+ ttolwp(curthread)->lwp_ru.ioch += (ulong_t)wcount;
+
+ /* Store offset for non-positioned writes */
+ if (!positioned) {
+ if (vp->v_type == VFIFO) {
+ /* Backward compatibility */
+ fp->f_offset = wcount;
+ } else if (((fp->f_flag & FAPPEND) == 0) ||
+ (vp->v_type != VREG) || (count != 0)) {
+ /* POSIX */
+ fp->f_offset = uiop->uio_loffset;
+ }
+ }
+ VOP_RWUNLOCK(vp, rwflag, NULL);
+
+out:
+ if (in_crit)
+ nbl_end_crit(vp);
+ *nwrite = wcount;
+ return (error);
+}
+
+/*
+ * The Linux routines for reading and writing data from file descriptors behave
+ * differently from their SunOS counterparts in a few key ways:
+ *
+ * - Passing an iovcnt of 0 to the vectored functions results in an error on
+ * SunOS, but on Linux it yields return value of 0.
+ *
+ * - If any data is successfully read or written, Linux will return a success.
+ * This is unlike SunOS which would return an error code for the entire
+ * operation in cases where vectors had gone unprocessed.
+ *
+ * - Breaking from POSIX, Linux positioned writes (pwrite/pwritev) on Linux
+ * will obey the O_APPEND flag if it is set on the descriptor.
+ */
+
+ssize_t
+lx_read(int fdes, void *cbuf, size_t ccount)
+{
+ struct uio auio;
+ struct iovec aiov;
+ file_t *fp;
+ ssize_t count = (ssize_t)ccount;
+ size_t nread = 0;
+ int fflag, error = 0;
+
+ if (count < 0)
+ return (set_errno(EINVAL));
+ if ((fp = getf(fdes)) == NULL)
+ return (set_errno(EBADF));
+ if (((fflag = fp->f_flag) & FREAD) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG && count == 0) {
+ goto out;
+ }
+
+ aiov.iov_base = cbuf;
+ aiov.iov_len = count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = MAXOFFSET_T;
+ auio.uio_fmode = fflag;
+ if (count <= copyout_max_cached)
+ auio.uio_extflg = UIO_COPY_CACHED;
+ else
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_read_common(fp, &auio, &nread, B_FALSE);
+
+ if (error == EINTR) {
+ if (nread != 0) {
+ error = 0;
+ } else {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (error != 0)
+ return (set_errno(error));
+ return ((ssize_t)nread);
+}
+
+ssize_t
+lx_write(int fdes, void *cbuf, size_t ccount)
+{
+ struct uio auio;
+ struct iovec aiov;
+ file_t *fp;
+ ssize_t count = (ssize_t)ccount;
+ size_t nwrite = 0;
+ int fflag, error = 0;
+
+ if (count < 0)
+ return (set_errno(EINVAL));
+ if ((fp = getf(fdes)) == NULL)
+ return (set_errno(EBADF));
+ if (((fflag = fp->f_flag) & FWRITE) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG && count == 0) {
+ goto out;
+ }
+
+ aiov.iov_base = cbuf;
+ aiov.iov_len = count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_write_common(fp, &auio, &nwrite, B_FALSE);
+
+ if (error == EINTR) {
+ if (nwrite != 0) {
+ error = 0;
+ } else {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (error != 0)
+ return (set_errno(error));
+ return (nwrite);
+}
+
+ssize_t
+lx_readv(int fdes, struct iovec *iovp, int iovcnt)
+{
+ struct uio auio;
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
+ file_t *fp;
+ ssize_t count;
+ size_t nread = 0;
+ int fflag, error = 0;
+
+ if (iovcnt < 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EINVAL));
+ } else if (iovcnt == 0) {
+ return (0);
+ }
+
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+ if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(error));
+ }
+
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(EBADF));
+ }
+ if (((fflag = fp->f_flag) & FREAD) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG && count == 0) {
+ goto out;
+ }
+
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = MAXOFFSET_T;
+ auio.uio_fmode = fflag;
+ if (count <= copyout_max_cached)
+ auio.uio_extflg = UIO_COPY_CACHED;
+ else
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_read_common(fp, &auio, &nread, B_FALSE);
+
+ if (error != 0) {
+ if (nread != 0) {
+ error = 0;
+ } else if (error == EINTR) {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (nread);
+}
+
+ssize_t
+lx_writev(int fdes, struct iovec *iovp, int iovcnt)
+{
+ struct uio auio;
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
+ file_t *fp;
+ ssize_t count;
+ size_t nwrite = 0;
+ int fflag, error = 0;
+
+ if (iovcnt < 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EINVAL));
+ } else if (iovcnt == 0) {
+ return (0);
+ }
+
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+ if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(error));
+ }
+
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(EBADF));
+ }
+ if (((fflag = fp->f_flag) & FWRITE) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG && count == 0) {
+ goto out;
+ }
+
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_loffset = fp->f_offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_write_common(fp, &auio, &nwrite, B_FALSE);
+
+ if (error != 0) {
+ if (nwrite != 0) {
+ error = 0;
+ } else if (error == EINTR) {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (nwrite);
+}
+
+ssize_t
+lx_pread_fp(file_t *fp, void *cbuf, size_t ccount, off64_t offset)
+{
+ struct uio auio;
+ struct iovec aiov;
+ ssize_t count = (ssize_t)ccount;
+ size_t nread = 0;
+ int fflag, error = 0;
+
+ if (count < 0)
+ return (set_errno(EINVAL));
+ if (((fflag = fp->f_flag) & FREAD) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG) {
+ u_offset_t fileoff = (u_offset_t)offset;
+
+ if (count == 0)
+ goto out;
+ /*
+ * Return EINVAL if an invalid offset comes to pread.
+ * Negative offset from user will cause this error.
+ */
+ if (fileoff > MAXOFFSET_T) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Limit offset such that we don't read or write
+ * a file beyond the maximum offset representable in
+ * an off_t structure.
+ */
+ if (fileoff + count > MAXOFFSET_T)
+ count = (ssize_t)((offset_t)MAXOFFSET_T - fileoff);
+ }
+
+ aiov.iov_base = cbuf;
+ aiov.iov_len = count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = MAXOFFSET_T;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_CACHED;
+
+ error = lx_read_common(fp, &auio, &nread, B_TRUE);
+
+ if (error == EINTR) {
+ if (nread != 0) {
+ error = 0;
+ } else {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ if (error) {
+ return (set_errno(error));
+ }
+ return ((ssize_t)nread);
+
+}
+
+ssize_t
+lx_pread(int fdes, void *cbuf, size_t ccount, off64_t offset)
+{
+ file_t *fp;
+ size_t nread;
+
+ if ((fp = getf(fdes)) == NULL)
+ return (set_errno(EBADF));
+
+ nread = lx_pread_fp(fp, cbuf, ccount, offset);
+ releasef(fdes);
+ return (nread);
+}
+
+ssize_t
+lx_pwrite_fp(file_t *fp, void *cbuf, size_t ccount, off64_t offset)
+{
+ struct uio auio;
+ struct iovec aiov;
+ ssize_t count = (ssize_t)ccount;
+ size_t nwrite = 0;
+ int fflag, error = 0;
+
+ if (count < 0)
+ return (set_errno(EINVAL));
+ if (((fflag = fp->f_flag) & (FWRITE)) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG) {
+ u_offset_t fileoff = (u_offset_t)offset;
+
+ if (count == 0)
+ goto out;
+ /*
+ * return EINVAL for offsets that cannot be
+ * represented in an off_t.
+ */
+ if (fileoff > MAXOFFSET_T) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Take appropriate action if we are trying to write above the
+ * resource limit.
+ */
+ if (fileoff >= curproc->p_fsz_ctl) {
+ mutex_enter(&curproc->p_lock);
+ (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
+ curproc->p_rctls, curproc, RCA_UNSAFE_SIGINFO);
+ mutex_exit(&curproc->p_lock);
+
+ error = EFBIG;
+ goto out;
+ }
+ /*
+ * Don't allow pwrite to cause file sizes to exceed maxoffset.
+ */
+ if (fileoff == MAXOFFSET_T) {
+ error = EFBIG;
+ goto out;
+ }
+ if (fileoff + count > MAXOFFSET_T)
+ count = (ssize_t)((u_offset_t)MAXOFFSET_T - fileoff);
+ }
+
+ aiov.iov_base = cbuf;
+ aiov.iov_len = count;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_CACHED;
+
+ error = lx_write_common(fp, &auio, &nwrite, B_TRUE);
+
+ if (error == EINTR) {
+ if (nwrite != 0) {
+ error = 0;
+ } else {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ if (error) {
+ return (set_errno(error));
+ }
+ return (nwrite);
+}
+
+ssize_t
+lx_pwrite(int fdes, void *cbuf, size_t ccount, off64_t offset)
+{
+ file_t *fp;
+ size_t nwrite;
+
+ if ((fp = getf(fdes)) == NULL)
+ return (set_errno(EBADF));
+
+ nwrite = lx_pwrite_fp(fp, cbuf, ccount, offset);
+ releasef(fdes);
+ return (nwrite);
+}
+
+ssize_t
+lx_pread32(int fdes, void *cbuf, size_t ccount, uint32_t off_lo,
+ uint32_t off_hi)
+{
+ return (lx_pread(fdes, cbuf, ccount, LX_32TO64(off_lo, off_hi)));
+}
+
+ssize_t
+lx_pwrite32(int fdes, void *cbuf, size_t ccount, uint32_t off_lo,
+ uint32_t off_hi)
+{
+ return (lx_pwrite(fdes, cbuf, ccount, LX_32TO64(off_lo, off_hi)));
+}
+
+ssize_t
+lx_preadv(int fdes, void *iovp, int iovcnt, off64_t offset)
+{
+ struct uio auio;
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
+ file_t *fp;
+ ssize_t count;
+ size_t nread = 0;
+ int fflag, error = 0;
+
+ if (iovcnt < 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EINVAL));
+ } else if (iovcnt == 0) {
+ return (0);
+ }
+
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+ if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(error));
+ }
+
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(EBADF));
+ }
+ if (((fflag = fp->f_flag) & FREAD) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG) {
+ u_offset_t fileoff = (u_offset_t)offset;
+
+ if (count == 0)
+ goto out;
+ /*
+ * Return EINVAL if an invalid offset comes to pread.
+ * Negative offset from user will cause this error.
+ */
+ if (fileoff > MAXOFFSET_T) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Limit offset such that we don't read or write a file beyond
+ * the maximum offset representable in an off_t structure.
+ */
+ if (fileoff + count > MAXOFFSET_T)
+ count = (ssize_t)((offset_t)MAXOFFSET_T - fileoff);
+ }
+
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_loffset = offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = MAXOFFSET_T;
+ auio.uio_fmode = fflag;
+ if (count <= copyout_max_cached)
+ auio.uio_extflg = UIO_COPY_CACHED;
+ else
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_read_common(fp, &auio, &nread, B_TRUE);
+
+ if (error != 0) {
+ if (nread != 0) {
+ error = 0;
+ } else if (error == EINTR) {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (nread);
+}
+
+ssize_t
+lx_pwritev(int fdes, void *iovp, int iovcnt, off64_t offset)
+{
+ struct uio auio;
+ struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+ int aiovlen = 0;
+ file_t *fp;
+ ssize_t count;
+ size_t nwrite = 0;
+ int fflag, error = 0;
+
+ if (iovcnt < 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EINVAL));
+ } else if (iovcnt == 0) {
+ return (0);
+ }
+
+ if (iovcnt > IOV_MAX_STACK) {
+ aiovlen = iovcnt * sizeof (iovec_t);
+ aiov = kmem_alloc(aiovlen, KM_SLEEP);
+ }
+ if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(error));
+ }
+
+ if ((fp = getf(fdes)) == NULL) {
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ return (set_errno(EBADF));
+ }
+ if (((fflag = fp->f_flag) & FWRITE) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp->f_vnode->v_type == VREG) {
+ u_offset_t fileoff = (u_offset_t)offset;
+
+ if (count == 0)
+ goto out;
+ /*
+ * Return EINVAL if an invalid offset comes to pread.
+ * Negative offset from user will cause this error.
+ */
+ if (fileoff > MAXOFFSET_T) {
+ error = EINVAL;
+ goto out;
+ }
+ /*
+ * Take appropriate action if we are trying to write above the
+ * resource limit.
+ */
+ if (fileoff >= curproc->p_fsz_ctl) {
+ mutex_enter(&curproc->p_lock);
+ (void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
+ curproc->p_rctls, curproc, RCA_UNSAFE_SIGINFO);
+ mutex_exit(&curproc->p_lock);
+
+ error = EFBIG;
+ goto out;
+ }
+ /*
+ * Don't allow pwritev to cause file sizes to exceed maxoffset.
+ */
+ if (fileoff == MAXOFFSET_T) {
+ error = EFBIG;
+ goto out;
+ }
+ /*
+ * Limit offset such that we don't read or write a file beyond
+ * the maximum offset representable in an off_t structure.
+ */
+ if (fileoff + count > MAXOFFSET_T)
+ count = (ssize_t)((u_offset_t)MAXOFFSET_T - fileoff);
+ }
+
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_loffset = offset;
+ auio.uio_resid = count;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_llimit = curproc->p_fsz_ctl;
+ auio.uio_fmode = fflag;
+ auio.uio_extflg = UIO_COPY_DEFAULT;
+
+ error = lx_write_common(fp, &auio, &nwrite, B_TRUE);
+
+ if (error != 0) {
+ if (nwrite != 0) {
+ error = 0;
+ } else if (error == EINTR) {
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+ }
+out:
+ releasef(fdes);
+ if (aiovlen != 0)
+ kmem_free(aiov, aiovlen);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (nwrite);
+}
+
+ssize_t
+lx_preadv32(int fdes, void *iovp, int iovcnt, uint32_t off_lo, uint32_t off_hi)
+{
+ return (lx_preadv(fdes, iovp, iovcnt, LX_32TO64(off_lo, off_hi)));
+}
+
+ssize_t
+lx_pwritev32(int fdes, void *iovp, int iovcnt, uint32_t off_lo,
+ uint32_t off_hi)
+{
+ return (lx_pwritev(fdes, iovp, iovcnt, LX_32TO64(off_lo, off_hi)));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_sched.c b/usr/src/uts/common/brand/lx/syscall/lx_sched.c
new file mode 100644
index 0000000000..6d4904a5fe
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_sched.c
@@ -0,0 +1,1161 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * Emulation for scheduling related syscalls.
+ *
+ * Under a typical zone configuration the zones will always be running under
+ * FSS so that no single zone can monopolize the system. Zones do not have the
+ * privilege to leave FSS (for the obvious reason that this would violate the
+ * global zone resource management policies). Thus, for the sched_* syscalls
+ * we typically will never be able to emulate those using our other native
+ * scheduling classes. Under this common case we simply track the scheduler
+ * settings on the lwp's lx brand structure and we also try to adjust the
+ * lwp priority within the valid range to approximate the intended effect.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/cpu.h>
+#include <sys/rtpriocntl.h>
+#include <sys/tspriocntl.h>
+#include <sys/processor.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/sysmacros.h>
+#include <sys/policy.h>
+#include <sys/procset.h>
+#include <sys/priocntl.h>
+
+typedef int l_pid_t;
+
+extern int yield();
+extern long priocntl_common(int, procset_t *, int, caddr_t, caddr_t, uio_seg_t);
+
+static int lx_sched_setprocset(procset_t *, l_pid_t);
+static long lx_do_priocntlsys(int, procset_t *, void *);
+
+#define BITS_PER_BYTE 8
+
+/*
+ * Linux scheduler policies.
+ */
+#define LX_SCHED_OTHER 0
+#define LX_SCHED_FIFO 1
+#define LX_SCHED_RR 2
+#define LX_SCHED_BATCH 3
+#define LX_SCHED_IDLE 5
+#define LX_SCHED_DEADLINE 6
+
+/*
+ * Linux scheduler priority ranges.
+ */
+#define LX_SCHED_PRIORITY_MIN_OTHER 0
+#define LX_SCHED_PRIORITY_MAX_OTHER 0
+#define LX_SCHED_PRIORITY_MIN_RRFIFO 1
+#define LX_SCHED_PRIORITY_MAX_RRFIFO 99
+
+#define MAXPRI 60 /* See FSS_MAXUPRI */
+
+/*
+ * When emulating scheduling priorities (e.g. under FSS) we'll do the best we
+ * can by adjusting the thread's priority within our range.
+ */
+static int lx_emul_pri_map[] = {
+ 0, /* LX_SCHED_OTHER */
+ MAXPRI, /* LX_SCHED_FIFO */
+ MAXPRI - 1, /* LX_SCHED_RR */
+ -MAXPRI + 1, /* LX_SCHED_BATCH */
+ 0, /* UNUSED */
+ -MAXPRI, /* LX_SCHED_IDLE */
+ MAXPRI /* LX_SCHED_DEADLINE */
+};
+
+/*
+ * Determine if we should emulate the sched_* syscalls. A zone is almost always
+ * going to be running under FSS in any kind of production configuration, and
+ * FSS is currently the only class which zone processes won't have the privilege
+ * to leave. Instead of checking for FSS explicitly, we generalize our check
+ * using CL_CANEXIT.
+ */
+#define EMUL_SCHED() (CL_CANEXIT(curthread, CRED()) != 0)
+
+struct lx_sched_param {
+ int lx_sched_prio;
+};
+
+typedef struct lx_sched_attr {
+ uint32_t lx_size;
+
+ uint32_t lx_sched_policy;
+ uint64_t lx_sched_flags;
+
+ /* For LX_SCHED_OTHER or LX_SCHED_BATCH */
+ int lx_sched_nice;
+
+ /* For LX_SCHED_FIFO or LX_SCHED_RR */
+ uint32_t lx_sched_priority;
+
+ /* For LX_SCHED_DEADLINE */
+ uint64_t lx_sched_runtime;
+ uint64_t lx_sched_deadline;
+ uint64_t lx_sched_period;
+} lx_sched_attr_t;
+
+long
+lx_sched_yield(void)
+{
+ yield();
+
+ return (0);
+}
+
+static void
+ltos_cpuset(lx_affmask_t *lmask, cpuset_t *smask)
+{
+ /* NOTE: fix this code if NCPU is ever made > LX_NCPU */
+
+ cpuset_zero(smask);
+ for (int i = 0; i < NCPU; i++) {
+ if (BT_TEST(*lmask, i)) {
+ cpuset_add(smask, i);
+ }
+ }
+}
+
+static void
+stol_cpuset(cpuset_t *smask, lx_affmask_t *lmask)
+{
+ /* NOTE: fix this code if NCPU is ever made > LX_NCPU */
+
+ bzero(lmask, sizeof (*lmask));
+ for (int i = 0; i < NCPU; i++) {
+ if (cpu_in_set(smask, i)) {
+ BT_SET(*lmask, i);
+ }
+ }
+}
+
+/*
+ * Find and lock a process for lx_sched_* operations.
+ * Sets 'pp' and 'tp' on success, with P_PR_LOCK set and p_lock held.
+ * The target process must be branded.
+ */
+static int
+lx_sched_pidlock(l_pid_t pid, proc_t **pp, kthread_t **tp, boolean_t is_write)
+{
+ proc_t *p;
+ kthread_t *t = NULL;
+ int err = 0;
+
+ if (pid < 0) {
+ return (EINVAL);
+ }
+ if (pid == 0) {
+ p = curproc;
+ ASSERT(PROC_IS_BRANDED(p));
+ mutex_enter(&p->p_lock);
+ sprlock_proc(p);
+
+ *tp = curthread;
+ *pp = p;
+ return (0);
+ }
+
+ if (lx_lpid_lock((pid_t)pid, curzone, LXP_PRLOCK, &p, &t) != 0) {
+ return (ESRCH);
+ }
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ if (!(PROC_IS_BRANDED(p))) {
+ sprunlock(p);
+ return (EPERM);
+ }
+
+ if (is_write) {
+ cred_t *cr = CRED();
+
+ /*
+ * To perform a sched_* operation on a thread outside of the
+ * current process, either the euid/egid of the target must
+ * match, or the calling process must hold CAP_SYS_NICE.
+ * (PRIV_PROC_PRIOUP maps to CAP_SYS_NICE)
+ */
+ err = 0;
+ if (secpolicy_raisepriority(cr) != 0) {
+ err = 0;
+ mutex_exit(&p->p_lock);
+ mutex_enter(&p->p_crlock);
+ if (crgetuid(cr) != crgetuid(p->p_cred) ||
+ crgetgid(cr) != crgetgid(p->p_cred)) {
+ err = EPERM;
+ }
+ mutex_exit(&p->p_crlock);
+ mutex_enter(&p->p_lock);
+ if (err != 0) {
+ sprunlock(p);
+ return (err);
+ }
+ }
+ }
+ *pp = p;
+ *tp = t;
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ return (0);
+}
+
+long
+lx_sched_getaffinity(l_pid_t pid, unsigned int len, void *maskp)
+{
+ proc_t *p;
+ kthread_t *tp = NULL;
+ lx_lwp_data_t *lwpd;
+ int err;
+ unsigned int pmin, pmax, compare_size;
+ lx_affmask_t lmask;
+ cpuset_t *smask;
+
+ /*
+ * The length boundary requirement is to match Linux's behavior.
+ */
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ compare_size = sizeof (uint32_t);
+ break;
+ default:
+ compare_size = sizeof (ulong_t);
+ break;
+ }
+ if ((len & (compare_size - 1)) != 0) {
+ return (set_errno(EINVAL));
+ }
+
+ smask = cpuset_alloc(KM_SLEEP);
+ if ((err = lx_sched_pidlock(pid, &p, &tp, B_FALSE)) != 0) {
+ cpuset_free(smask);
+ return (set_errno(err));
+ }
+
+ mutex_exit(&p->p_lock);
+ mutex_enter(&cpu_lock);
+ mutex_enter(&p->p_lock);
+ /*
+ * Grab the existing affinity mask and constrain it by the current set
+ * of active CPUs (which may have changed since it was assigned.
+ */
+ lwpd = ttolxlwp(tp);
+ cpuset_or(smask, lwpd->br_affinitymask);
+ cpuset_and(smask, &cpu_active_set);
+ sprunlock(p);
+ mutex_exit(&cpu_lock);
+
+ cpuset_bounds(smask, &pmin, &pmax);
+ stol_cpuset(smask, &lmask);
+ cpuset_free(smask);
+
+ /*
+ * It is out of convenience that this check is performed so late. If
+ * the need arises, it could be altered to be done earlier in order to
+ * match Linux error ordering.
+ */
+ if (pmax >= (len * BITS_PER_BYTE)) {
+ return (set_errno(EINVAL));
+ }
+
+ len = MIN(len, sizeof (lx_affmask_t));
+ if (copyout(&lmask, maskp, len) != 0) {
+ return (set_errno(EFAULT));
+ }
+ return (len);
+}
+
+long
+lx_sched_setaffinity(l_pid_t pid, unsigned int len, void *maskp)
+{
+ proc_t *p;
+ kthread_t *tp = NULL;
+ lx_lwp_data_t *lwpd;
+ int err;
+ unsigned int pmin, pmax;
+ lx_affmask_t lmask;
+ cpuset_t *smask;
+
+ if (pid < 0) {
+ return (set_errno(EINVAL));
+ }
+
+ if (len < sizeof (lmask)) {
+ bzero(&lmask, sizeof (lmask));
+ } else if (len > sizeof (lmask)) {
+ len = sizeof (lmask);
+ }
+ if (copyin(maskp, &lmask, len) != 0) {
+ return (set_errno(EFAULT));
+ }
+ smask = cpuset_alloc(KM_SLEEP);
+ ltos_cpuset(&lmask, smask);
+ if ((err = lx_sched_pidlock(pid, &p, &tp, B_TRUE)) != 0) {
+ cpuset_free(smask);
+ return (set_errno(err));
+ }
+
+ /*
+ * Constrain the mask to currently active CPUs.
+ */
+ mutex_exit(&p->p_lock);
+ mutex_enter(&cpu_lock);
+ mutex_enter(&p->p_lock);
+ lwpd = ttolxlwp(tp);
+
+ cpuset_and(smask, &cpu_active_set);
+ if (cpuset_isnull(smask)) {
+ err = EINVAL;
+ goto out;
+ }
+ if (cpuset_isequal(lwpd->br_affinitymask, smask)) {
+ err = 0;
+ goto out;
+ }
+
+ /*
+ * If one (and only one) CPU is selected in the affinity mask, bind the
+ * thread to that CPU.
+ */
+ cpuset_bounds(smask, &pmin, &pmax);
+ VERIFY(pmin != CPUSET_NOTINSET);
+ if (pmin == pmax) {
+ processorid_t obind;
+
+ (void) cpu_bind_thread(tp, pmin, &obind, &err);
+ if (err != 0) {
+ goto out;
+ }
+ } else {
+ /*
+ * If the thread transitions away from a single-CPU mask, it
+ * should be unbound from that processor.
+ */
+ cpuset_bounds(lwpd->br_affinitymask, &pmin, &pmax);
+ if (pmin == pmax) {
+ processorid_t obind;
+ (void) cpu_bind_thread(tp, PBIND_NONE, &obind, &err);
+ }
+ }
+ cpuset_zero(lwpd->br_affinitymask);
+ cpuset_or(lwpd->br_affinitymask, smask);
+ err = 0;
+
+out:
+ mutex_exit(&cpu_lock);
+ sprunlock(p);
+ cpuset_free(smask);
+ if (err != 0) {
+ return (set_errno(err));
+ }
+ return (0);
+}
+
+void
+lx_affinity_forklwp(klwp_t *srclwp, klwp_t *dstlwp)
+{
+ proc_t *pp = lwptoproc(srclwp);
+ lx_lwp_data_t *slwpd = lwptolxlwp(srclwp);
+ lx_lwp_data_t *dlwpd = lwptolxlwp(dstlwp);
+
+ /*
+ * Copy over the affinity mask. This could be enhanced in the future
+ * to perform single-CPU binding like sched_setaffinity.
+ */
+ mutex_enter(&pp->p_lock);
+ cpuset_zero(dlwpd->br_affinitymask);
+ cpuset_or(dlwpd->br_affinitymask, slwpd->br_affinitymask);
+ mutex_exit(&pp->p_lock);
+}
+
+long
+lx_sched_setscheduler(l_pid_t pid, int policy, struct lx_sched_param *param)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ procset_t procset;
+ procset_t procset_cid;
+ pcparms_t pcparm;
+ pcinfo_t pcinfo;
+ struct lx_sched_param sched_param;
+ tsparms_t *tsp;
+ int prio, maxupri;
+ int rv;
+
+ if (pid < 0 || param == NULL)
+ return (set_errno(EINVAL));
+
+ if (copyin(param, &sched_param, sizeof (sched_param)))
+ return (set_errno(EFAULT));
+
+ prio = sched_param.lx_sched_prio;
+
+ if (EMUL_SCHED()) {
+ proc_t *p;
+ kthread_t *tp = NULL;
+ int incr;
+ lx_lwp_data_t *lwpd;
+
+ switch (policy) {
+ case LX_SCHED_OTHER:
+ case LX_SCHED_BATCH:
+ case LX_SCHED_IDLE:
+ case LX_SCHED_DEADLINE:
+ if (prio != LX_SCHED_PRIORITY_MIN_OTHER)
+ return (set_errno(EINVAL));
+ break;
+ case LX_SCHED_FIFO:
+ case LX_SCHED_RR:
+ if (crgetuid(CRED()) != 0)
+ return (set_errno(EPERM));
+ if (prio < LX_SCHED_PRIORITY_MIN_RRFIFO ||
+ prio > LX_SCHED_PRIORITY_MAX_RRFIFO)
+ return (set_errno(EINVAL));
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ /* Find and operate on the target lwp. */
+ if ((rv = lx_sched_pidlock(pid, &p, &tp, B_TRUE)) != 0)
+ return (set_errno(rv));
+
+ lwpd = lwptolxlwp(ttolwp(tp));
+ if (lwpd->br_schd_class == LX_SCHED_IDLE &&
+ policy != LX_SCHED_IDLE && crgetuid(CRED()) != 0) {
+
+ sprunlock(p);
+ return (set_errno(EPERM));
+ }
+
+ lwpd->br_schd_class = policy;
+ lwpd->br_schd_pri = prio;
+
+ ASSERT(policy <= LX_SCHED_DEADLINE);
+ incr = lx_emul_pri_map[policy];
+
+ CL_DOPRIO(tp, CRED(), incr, &rv);
+
+ sprunlock(p);
+ return (0);
+ }
+
+ if ((rv = lx_sched_setprocset(&procset, pid)))
+ return (rv);
+
+ /* get the class id */
+ pcparm.pc_cid = PC_CLNULL;
+ (void) lx_do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /* get the current policy */
+ bzero(&pcinfo, sizeof (pcinfo));
+ pcinfo.pc_cid = pcparm.pc_cid;
+ (void) lx_do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (policy < 0) {
+ if (strcmp(pcinfo.pc_clname, "TS") == 0) {
+ policy = LX_SCHED_OTHER;
+ } else if (strcmp(pcinfo.pc_clname, "RT") == 0) {
+ policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
+ RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
+ } else {
+ return (set_errno(EINVAL));
+ }
+ }
+
+ bzero(&pcinfo, sizeof (pcinfo));
+ bzero(&pcparm, sizeof (pcparm));
+ setprocset(&procset_cid, POP_AND, P_PID, 0, P_ALL, 0);
+ switch (policy) {
+ case LX_SCHED_FIFO:
+ case LX_SCHED_RR:
+ (void) strcpy(pcinfo.pc_clname, "RT");
+ (void) lx_do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (prio < 0 ||
+ prio > ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri)
+ return (set_errno(EINVAL));
+ pcparm.pc_cid = pcinfo.pc_cid;
+ ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio;
+ ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs =
+ policy == LX_SCHED_RR ? RT_TQDEF : RT_TQINF;
+ break;
+
+ case LX_SCHED_OTHER:
+ (void) strcpy(pcinfo.pc_clname, "TS");
+ (void) lx_do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri;
+ if (prio > maxupri || prio < -maxupri)
+ return (set_errno(EINVAL));
+
+ pcparm.pc_cid = pcinfo.pc_cid;
+ tsp = (tsparms_t *)pcparm.pc_clparms;
+ tsp->ts_upri = prio;
+ tsp->ts_uprilim = TS_NOCHANGE;
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * finally set scheduling policy and parameters
+ */
+ (void) lx_do_priocntlsys(PC_SETPARMS, &procset, &pcparm);
+
+ return (0);
+}
+
+long
+lx_sched_getscheduler(l_pid_t pid)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ procset_t procset;
+ pcparms_t pcparm;
+ pcinfo_t pcinfo;
+ int policy;
+ int rv;
+
+ if (pid < 0)
+ return (set_errno(EINVAL));
+
+ if (EMUL_SCHED()) {
+ proc_t *p;
+ kthread_t *tp = NULL;
+
+ /* Find and operate on the target lwp. */
+ if ((rv = lx_sched_pidlock(pid, &p, &tp, B_FALSE)) != 0)
+ return (set_errno(rv));
+
+ policy = lwptolxlwp(ttolwp(tp))->br_schd_class;
+ sprunlock(p);
+
+ return (policy);
+ }
+
+ if ((rv = lx_sched_setprocset(&procset, pid)))
+ return (rv);
+
+ /*
+ * get the class id
+ */
+ pcparm.pc_cid = PC_CLNULL;
+ (void) lx_do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * get the class info and identify the equivalent linux policy
+ */
+ bzero(&pcinfo, sizeof (pcinfo));
+ pcinfo.pc_cid = pcparm.pc_cid;
+ (void) lx_do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (strcmp(pcinfo.pc_clname, "TS") == 0) {
+ policy = LX_SCHED_OTHER;
+ } else if (strcmp(pcinfo.pc_clname, "RT") == 0) {
+ policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
+ RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
+ } else {
+ policy = set_errno(EINVAL);
+ }
+
+ return (policy);
+}
+
+long
+lx_sched_setparam(l_pid_t pid, struct lx_sched_param *param)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ procset_t procset;
+ procset_t procset_cid;
+ pcparms_t pcparm;
+ pcinfo_t pcinfo;
+ struct lx_sched_param sched_param;
+ tsparms_t *tsp;
+ int policy;
+ int prio, maxupri;
+ int rv;
+
+ if (pid < 0 || param == NULL)
+ return (set_errno(EINVAL));
+
+ if (copyin(param, &sched_param, sizeof (sched_param)))
+ return (set_errno(EFAULT));
+
+ prio = sched_param.lx_sched_prio;
+
+ if (EMUL_SCHED()) {
+ proc_t *p;
+ kthread_t *tp = NULL;
+ int incr;
+
+ /* Find and operate on the target lwp. */
+ if ((rv = lx_sched_pidlock(pid, &p, &tp, B_TRUE)) != 0)
+ return (set_errno(rv));
+
+ policy = lwptolxlwp(ttolwp(tp))->br_schd_class;
+ switch (policy) {
+ case LX_SCHED_OTHER:
+ case LX_SCHED_BATCH:
+ case LX_SCHED_IDLE:
+ case LX_SCHED_DEADLINE:
+ if (prio != LX_SCHED_PRIORITY_MIN_OTHER) {
+ sprunlock(p);
+ return (set_errno(EINVAL));
+ }
+ break;
+ case LX_SCHED_FIFO:
+ case LX_SCHED_RR:
+ if (crgetuid(CRED()) != 0) {
+ sprunlock(p);
+ return (set_errno(EPERM));
+ }
+ if (prio < LX_SCHED_PRIORITY_MIN_RRFIFO ||
+ prio > LX_SCHED_PRIORITY_MAX_RRFIFO) {
+ sprunlock(p);
+ return (set_errno(EINVAL));
+ }
+ break;
+ default:
+ /* this shouldn't happen */
+ ASSERT(0);
+ sprunlock(p);
+ return (set_errno(EINVAL));
+ }
+
+ lwptolxlwp(ttolwp(tp))->br_schd_pri = prio;
+
+ ASSERT(policy <= LX_SCHED_DEADLINE);
+ incr = lx_emul_pri_map[policy];
+
+ CL_DOPRIO(tp, CRED(), incr, &rv);
+ sprunlock(p);
+ return (0);
+ }
+
+ if ((rv = lx_sched_setprocset(&procset, pid)))
+ return (rv);
+
+ /*
+ * get the class id
+ */
+ pcparm.pc_cid = PC_CLNULL;
+ (void) lx_do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * get the current policy
+ */
+ bzero(&pcinfo, sizeof (pcinfo));
+ pcinfo.pc_cid = pcparm.pc_cid;
+ (void) lx_do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (strcmp(pcinfo.pc_clname, "TS") == 0)
+ policy = LX_SCHED_OTHER;
+ else if (strcmp(pcinfo.pc_clname, "RT") == 0)
+ policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
+ RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
+ else
+ return (set_errno(EINVAL));
+
+ bzero(&pcinfo, sizeof (pcinfo));
+ bzero(&pcparm, sizeof (pcparm));
+ setprocset(&procset_cid, POP_AND, P_PID, 0, P_ALL, 0);
+ switch (policy) {
+ case LX_SCHED_FIFO:
+ case LX_SCHED_RR:
+ (void) strcpy(pcinfo.pc_clname, "RT");
+ (void) lx_do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ if (prio < 0 ||
+ prio > ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri)
+ return (set_errno(EINVAL));
+ pcparm.pc_cid = pcinfo.pc_cid;
+ ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio;
+ ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs =
+ policy == LX_SCHED_RR ? RT_TQDEF : RT_TQINF;
+ break;
+
+ case LX_SCHED_OTHER:
+ (void) strcpy(pcinfo.pc_clname, "TS");
+ (void) lx_do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri;
+ if (prio > maxupri || prio < -maxupri)
+ return (set_errno(EINVAL));
+
+ pcparm.pc_cid = pcinfo.pc_cid;
+ tsp = (tsparms_t *)pcparm.pc_clparms;
+ tsp->ts_upri = prio;
+ tsp->ts_uprilim = TS_NOCHANGE;
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * finally set scheduling policy and parameters
+ */
+ (void) lx_do_priocntlsys(PC_SETPARMS, &procset, &pcparm);
+
+ return (0);
+}
+
+long
+lx_sched_getparam(l_pid_t pid, struct lx_sched_param *param)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ struct lx_sched_param local_param;
+ procset_t procset;
+ pcparms_t pcparm;
+ pcinfo_t pcinfo;
+ tsinfo_t *tsi;
+ int prio, scale;
+ int rv;
+
+ if (pid < 0 || param == NULL)
+ return (set_errno(EINVAL));
+
+ if (EMUL_SCHED()) {
+ proc_t *p;
+ kthread_t *tp = NULL;
+
+ /* Find and operate on the target lwp. */
+ if ((rv = lx_sched_pidlock(pid, &p, &tp, B_FALSE)) != 0)
+ return (set_errno(rv));
+
+ local_param.lx_sched_prio = lwptolxlwp(ttolwp(tp))->br_schd_pri;
+ sprunlock(p);
+ if (copyout(&local_param, param, sizeof (local_param)))
+ return (set_errno(EFAULT));
+
+ return (0);
+ }
+
+ if ((rv = lx_sched_setprocset(&procset, pid)))
+ return (rv);
+
+ /*
+ * get the class id
+ */
+ pcparm.pc_cid = PC_CLNULL;
+ (void) lx_do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * get the class info and identify the equivalent linux policy
+ */
+ bzero(&pcinfo, sizeof (pcinfo));
+ pcinfo.pc_cid = pcparm.pc_cid;
+ (void) lx_do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ bzero(&local_param, sizeof (local_param));
+ if (strcmp(pcinfo.pc_clname, "TS") == 0) {
+ /*
+ * I don't know if we need to do this, coz it can't be
+ * changed from zero anyway.....
+ */
+ tsi = (tsinfo_t *)pcinfo.pc_clinfo;
+ prio = ((tsparms_t *)pcparm.pc_clparms)->ts_upri;
+ scale = tsi->ts_maxupri;
+ if (scale == 0)
+ local_param.lx_sched_prio = 0;
+ else
+ local_param.lx_sched_prio = -(prio * 20) / scale;
+ } else if (strcmp(pcinfo.pc_clname, "RT") == 0) {
+ local_param.lx_sched_prio =
+ ((rtparms_t *)pcparm.pc_clparms)->rt_pri;
+ } else {
+ rv = set_errno(EINVAL);
+ }
+
+ if (rv == 0)
+ if (copyout(&local_param, param, sizeof (local_param)))
+ return (set_errno(EFAULT));
+
+ return (rv);
+}
+
+long
+lx_sched_rr_get_interval(l_pid_t pid, struct timespec *ival)
+{
+ klwp_t *lwp = ttolwp(curthread);
+ struct timespec interval;
+ procset_t procset;
+ pcparms_t pcparm;
+ pcinfo_t pcinfo;
+ int rv;
+
+ if (pid < 0)
+ return (set_errno(EINVAL));
+
+ if (EMUL_SCHED()) {
+ int policy;
+ proc_t *p;
+ kthread_t *tp = NULL;
+
+ /* Find and operate on the target lwp. */
+ if ((rv = lx_sched_pidlock(pid, &p, &tp, B_FALSE)) != 0)
+ return (set_errno(rv));
+
+ policy = lwptolxlwp(ttolwp(tp))->br_schd_class;
+ sprunlock(p);
+
+ interval.tv_sec = 0;
+ if (policy == LX_SCHED_RR) {
+ /* Use a made-up value similar to Linux */
+ interval.tv_nsec = 100000000;
+ } else {
+ interval.tv_nsec = 0;
+ }
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ timespec32_t t32;
+
+ /*
+ * A timespec may overflow for 32-bit but EOVERFLOW
+ * is not documented as an acceptable error for
+ * sched_rr_get_interval. Such an occurance would be
+ * exceptionally weird for the RR interval.
+ */
+ TIMESPEC_TO_TIMESPEC32(&t32, &interval);
+
+ if (copyout(&t32, ival, sizeof (t32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ }
+ else
+#endif
+ {
+ if (copyout(&interval, ival, sizeof (interval)))
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+ }
+
+ if ((rv = lx_sched_setprocset(&procset, pid)))
+ return (rv);
+
+ /*
+ * get the class id
+ */
+ pcparm.pc_cid = PC_CLNULL;
+ (void) lx_do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * get the class info and identify the equivalent linux policy
+ */
+ bzero(&pcinfo, sizeof (pcinfo));
+ pcinfo.pc_cid = pcparm.pc_cid;
+ (void) lx_do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * get the class info and identify the equivalent linux policy
+ */
+ setprocset(&procset, POP_AND, P_PID, 0, P_ALL, 0);
+ bzero(&pcinfo, sizeof (pcinfo));
+ (void) strcpy(pcinfo.pc_clname, "RT");
+ (void) lx_do_priocntlsys(PC_GETCID, &procset, &pcinfo);
+ if (lwp->lwp_errno)
+ return (lwp->lwp_errno);
+
+ /*
+ * Contrary to what the man page says, you don't have to be in RR to
+ * get this interval.
+ */
+ if (((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs != RT_TQINF) {
+ interval.tv_sec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqsecs;
+ interval.tv_nsec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs;
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ timespec32_t t32;
+
+ /*
+ * Like above, the 32-bit EOVERFLOW check is not
+ * appropriate here.
+ */
+ TIMESPEC_TO_TIMESPEC32(&t32, &interval);
+
+ if (copyout(&t32, ival, sizeof (t32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ }
+ else
+#endif
+ {
+ if (copyout(&interval, ival, sizeof (interval)))
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+ }
+
+ return (set_errno(EINVAL));
+}
+
+long
+lx_sched_get_priority_min(uintptr_t policy)
+{
+ /*
+ * Linux scheduling priorities are not alterable, so there is no
+ * illumos translation necessary.
+ */
+ switch (policy) {
+ case LX_SCHED_FIFO:
+ case LX_SCHED_RR:
+ return (LX_SCHED_PRIORITY_MIN_RRFIFO);
+ case LX_SCHED_OTHER:
+ case LX_SCHED_BATCH:
+ case LX_SCHED_IDLE:
+ case LX_SCHED_DEADLINE:
+ return (LX_SCHED_PRIORITY_MIN_OTHER);
+ default:
+ break;
+ }
+ return (set_errno(EINVAL));
+}
+
+long
+lx_sched_get_priority_max(uintptr_t policy)
+{
+ /*
+ * Linux scheduling priorities are not alterable, so there is no
+ * illumos translation necessary.
+ */
+ switch (policy) {
+ case LX_SCHED_FIFO:
+ case LX_SCHED_RR:
+ return (LX_SCHED_PRIORITY_MAX_RRFIFO);
+ case LX_SCHED_OTHER:
+ case LX_SCHED_BATCH:
+ case LX_SCHED_IDLE:
+ case LX_SCHED_DEADLINE:
+ return (LX_SCHED_PRIORITY_MAX_OTHER);
+ default:
+ break;
+ }
+ return (set_errno(EINVAL));
+}
+
+long
+lx_sched_setattr(l_pid_t pid, lx_sched_attr_t *attr, uint32_t flags)
+{
+ int rv;
+ uint32_t lx_size;
+ lx_sched_attr_t local_attr;
+ uint64_t flg;
+
+ if (pid < 0 || attr == NULL || flags != 0)
+ return (set_errno(EINVAL));
+
+ if (copyin(attr, &lx_size, sizeof (lx_size)))
+ return (set_errno(EFAULT));
+
+ if (lx_size > sizeof (local_attr))
+ return (set_errno(E2BIG));
+
+ bzero(&local_attr, sizeof (local_attr));
+ if (copyin(attr, &local_attr, lx_size))
+ return (set_errno(EFAULT));
+
+ flg = local_attr.lx_sched_flags;
+ if ((flg & ~LX_SCHED_FLAG_RESET_ON_FORK) != 0)
+ return (set_errno(EINVAL));
+
+ if (EMUL_SCHED()) {
+ int policy;
+ proc_t *p;
+ kthread_t *tp = NULL;
+ int incr;
+ lx_lwp_data_t *lwpd;
+
+ /* Find and operate on the target lwp. */
+ if ((rv = lx_sched_pidlock(pid, &p, &tp, B_TRUE)) != 0)
+ return (set_errno(rv));
+
+ policy = local_attr.lx_sched_policy;
+
+ switch (policy) {
+ case LX_SCHED_OTHER:
+ case LX_SCHED_BATCH:
+ case LX_SCHED_IDLE:
+ break;
+ case LX_SCHED_FIFO:
+ case LX_SCHED_RR:
+ if (crgetuid(CRED()) != 0) {
+ sprunlock(p);
+ return (set_errno(EPERM));
+ }
+ if (local_attr.lx_sched_priority <
+ LX_SCHED_PRIORITY_MIN_RRFIFO ||
+ local_attr.lx_sched_priority >
+ LX_SCHED_PRIORITY_MAX_RRFIFO) {
+ sprunlock(p);
+ return (set_errno(EINVAL));
+ }
+ break;
+
+ case LX_SCHED_DEADLINE:
+ if (crgetuid(CRED()) != 0) {
+ sprunlock(p);
+ return (set_errno(EPERM));
+ }
+ break;
+ default:
+ sprunlock(p);
+ return (set_errno(EINVAL));
+ }
+
+ lwpd = lwptolxlwp(ttolwp(tp));
+ lwpd->br_schd_class = policy;
+ lwpd->br_schd_flags = flg;
+ lwpd->br_schd_pri = local_attr.lx_sched_priority;
+
+ lwpd->br_schd_runtime = local_attr.lx_sched_runtime;
+ lwpd->br_schd_deadline = local_attr.lx_sched_deadline;
+ lwpd->br_schd_period = local_attr.lx_sched_period;
+
+ ASSERT(policy <= LX_SCHED_DEADLINE);
+ incr = lx_emul_pri_map[policy];
+
+ CL_DOPRIO(tp, CRED(), incr, &rv);
+ sprunlock(p);
+ return (0);
+ }
+
+ /* Currently not supported under other classes */
+ return (set_errno(ENOSYS));
+}
+
+long
+lx_sched_getattr(l_pid_t pid, lx_sched_attr_t *attr, uint32_t size,
+ uint32_t flags)
+{
+ lx_sched_attr_t local_attr;
+ int rv;
+
+ if (pid < 0 || attr == NULL || flags != 0 || size < sizeof (local_attr))
+ return (set_errno(EINVAL));
+
+ bzero(&local_attr, sizeof (local_attr));
+ if (EMUL_SCHED()) {
+ proc_t *p;
+ kthread_t *tp = NULL;
+ lx_lwp_data_t *lwpd;
+
+ /* Find and operate on the target lwp. */
+ if ((rv = lx_sched_pidlock(pid, &p, &tp, B_FALSE)) != 0)
+ return (set_errno(rv));
+
+ lwpd = lwptolxlwp(ttolwp(tp));
+ local_attr.lx_sched_policy = lwpd->br_schd_class;
+ local_attr.lx_sched_priority = lwpd->br_schd_pri;
+ local_attr.lx_sched_flags = lwpd->br_schd_flags;
+
+ local_attr.lx_sched_runtime = lwpd->br_schd_runtime;
+ local_attr.lx_sched_deadline = lwpd->br_schd_deadline;
+ local_attr.lx_sched_period = lwpd->br_schd_period;
+
+ sprunlock(p);
+
+ local_attr.lx_size = sizeof (lx_sched_attr_t);
+
+ if (copyout(&local_attr, attr, sizeof (local_attr)))
+ return (set_errno(EFAULT));
+
+ return (0);
+ }
+
+ /* Currently not supported under other classes */
+ return (set_errno(ENOSYS));
+}
+
+static int
+lx_sched_setprocset(procset_t *procset, l_pid_t pid)
+{
+ id_t lid, rid;
+ idtype_t lidtype, ridtype;
+
+ /*
+ * define the target lwp
+ */
+ if (pid == 0)
+ pid = curproc->p_pid;
+
+ if (lx_lpid_to_spair(pid, &pid, &lid) < 0)
+ return (set_errno(ESRCH));
+ rid = 0;
+ ridtype = P_ALL;
+ lidtype = P_LWPID;
+
+ setprocset(procset, POP_AND, lidtype, lid, ridtype, rid);
+
+ return (0);
+}
+
+static long
+lx_do_priocntlsys(int cmd, procset_t *procset, void *arg)
+{
+ return (priocntl_common(PC_VERSION, procset, cmd, (caddr_t)arg, 0,
+ UIO_SYSSPACE));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_socket.c b/usr/src/uts/common/brand/lx/syscall/lx_socket.c
new file mode 100644
index 0000000000..a433020f90
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_socket.c
@@ -0,0 +1,4832 @@
+/*
+ * 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 2020 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2022 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/cmn_err.h>
+#include <sys/sockio.h>
+#include <sys/thread.h>
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kmem.h>
+#include <sys/un.h>
+#include <sys/sunddi.h>
+#include <sys/cred.h>
+#include <sys/ucred.h>
+#include <sys/model.h>
+#include <sys/brand.h>
+#include <sys/vmsystm.h>
+#include <sys/limits.h>
+#include <sys/fcntl.h>
+#include <sys/sysmacros.h>
+#include <netpacket/packet.h>
+#include <sockcommon.h>
+#include <socktpi_impl.h>
+#include <netinet/udp.h>
+#include <sys/sdt.h>
+#include <netinet/tcp.h>
+#include <netinet/igmp.h>
+#include <netinet/icmp6.h>
+#include <inet/cc.h>
+#include <inet/tcp_impl.h>
+#include <lx_errno.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_socket.h>
+#include <sys/lx_types.h>
+#include <sys/lx_impl.h>
+
+/* From uts/common/fs/sockfs/socksyscalls.c */
+extern int listen(int, int, int);
+extern int shutdown(int, int, int);
+
+typedef struct lx_ucred {
+ pid_t lxu_pid;
+ lx_uid_t lxu_uid;
+ lx_gid_t lxu_gid;
+} lx_ucred_t;
+
+typedef struct lx_socket_aux_data
+{
+ kmutex_t lxsad_lock;
+ enum lxsad_status_t {
+ LXSS_NONE = 0,
+ LXSS_CONNECTING,
+ LXSS_CONNECTED
+ } lxsad_status;
+ uint_t lxsad_flags;
+} lx_socket_aux_data_t;
+
+#define LX_SS_MAXSIZE 128
+
+typedef struct lx_sockaddr_storage {
+ unsigned short lxss_family;
+ char lxdata[LX_SS_MAXSIZE - sizeof (unsigned short)];
+} lx_sockaddr_storage_t;
+
+typedef struct lx_group_req {
+ uint32_t lxgr_interface;
+#ifdef _LP64
+ /* On 64-bit linux kernels, gr_interface is padded by 4 bytes. */
+ uint32_t _lxgr_pad;
+#endif
+ lx_sockaddr_storage_t lxgr_group;
+} lx_group_req_t;
+
+#if defined(_SYSCALL32_IMPL)
+
+typedef struct lx_group_req32 {
+ uint32_t lxgr_interface;
+ lx_sockaddr_storage_t lxgr_group;
+} lx_group_req32_t;
+
+#endif /* defined(_SYSCALL32_IMPL) */
+
+/* lxsad_flags */
+#define LXSAD_FL_STRCRED 0x1
+#define LXSAD_FL_EMULSEQPKT 0x2
+/* These two work together to implement Linux SO_REUSEADDR semantics. */
+#define LXSAD_FL_EMULRUADDR 0x4
+#define LXSAD_FL_EMULRUPORT 0x8
+
+static lx_socket_aux_data_t *lx_sad_acquire(vnode_t *);
+
+/* VSD key for lx-specific socket information */
+static uint_t lx_socket_vsd = 0;
+
+/* Convenience enum to enforce translation direction */
+typedef enum lx_xlate_dir {
+ SUNOS_TO_LX,
+ LX_TO_SUNOS
+} lx_xlate_dir_t;
+
+/* enum for getpeername/getsockname handling */
+typedef enum lx_getname_type {
+ LX_GETPEERNAME,
+ LX_GETSOCKNAME
+} lx_getname_type_t;
+
+/*
+ * What follows are a series of tables we use to translate Linux constants
+ * into equivalent Illumos constants and back again. I wish this were
+ * cleaner, more programmatic, and generally nicer. Sadly, life is messy,
+ * and Unix networking even more so.
+ */
+static const int ltos_family[LX_AF_MAX + 1] = {
+ AF_UNSPEC, /* LX_AF_UNSPEC */
+ AF_UNIX, /* LX_AF_UNIX */
+ AF_INET, /* LX_AF_INET */
+ AF_NOTSUPPORTED, /* LX_AF_AX25 */
+ AF_NOTSUPPORTED, /* LX_AF_IPX */
+ AF_NOTSUPPORTED, /* LX_AF_APPLETALK */
+ AF_NOTSUPPORTED, /* LX_AF_NETROM */
+ AF_NOTSUPPORTED, /* LX_AF_BRIDGE */
+ AF_NOTSUPPORTED, /* LX_AF_ATMPVC */
+ AF_NOTSUPPORTED, /* LX_AF_X25 */
+ AF_INET6, /* LX_AF_INET6 */
+ AF_NOTSUPPORTED, /* LX_AF_ROSE */
+ AF_NOTSUPPORTED, /* LX_AF_DECNET */
+ AF_NOTSUPPORTED, /* LX_AF_NETBEUI */
+ AF_NOTSUPPORTED, /* LX_AF_SECURITY */
+ AF_NOTSUPPORTED, /* LX_AF_KEY */
+ AF_LX_NETLINK, /* LX_AF_NETLINK */
+ AF_PACKET, /* LX_AF_PACKET */
+ AF_NOTSUPPORTED, /* LX_AF_ASH */
+ AF_NOTSUPPORTED, /* LX_AF_ECONET */
+ AF_NOTSUPPORTED, /* LX_AF_ATMSVC */
+ AF_NOTSUPPORTED, /* LX_AF_RDS */
+ AF_NOTSUPPORTED, /* LX_AF_SNA */
+ AF_NOTSUPPORTED, /* LX_AF_IRDA */
+ AF_NOTSUPPORTED, /* LX_AF_PPOX */
+ AF_NOTSUPPORTED, /* LX_AF_WANPIPE */
+ AF_NOTSUPPORTED, /* LX_AF_LLC */
+ AF_NOTSUPPORTED, /* NONE */
+ AF_NOTSUPPORTED, /* NONE */
+ AF_NOTSUPPORTED, /* LX_AF_CAN */
+ AF_NOTSUPPORTED, /* LX_AF_TIPC */
+ AF_NOTSUPPORTED, /* LX_AF_BLUETOOTH */
+ AF_NOTSUPPORTED, /* LX_AF_IUCV */
+ AF_NOTSUPPORTED /* LX_AF_RXRPC */
+ /* LX_AF_ISDN */
+ /* LX_AF_PHONET */
+ /* LX_AF_IEEE802154 */
+ /* LX_AF_CAIF */
+ /* LX_AF_ALG */
+ /* LX_AF_NFC */
+ /* LX_AF_VSOCK */
+};
+
+static const int stol_family[LX_AF_MAX + 1] = {
+ AF_UNSPEC, /* AF_UNSPEC */
+ AF_UNIX, /* AF_UNIX */
+ AF_INET, /* AF_INET */
+ AF_NOTSUPPORTED, /* AF_IMPLINK */
+ AF_NOTSUPPORTED, /* AF_PUP */
+ AF_NOTSUPPORTED, /* AF_CHAOS */
+ AF_NOTSUPPORTED, /* AF_NS */
+ AF_NOTSUPPORTED, /* AF_NBS */
+ AF_NOTSUPPORTED, /* AF_ECMA */
+ AF_NOTSUPPORTED, /* AF_DATAKIT */
+ AF_NOTSUPPORTED, /* AF_CCITT */
+ AF_NOTSUPPORTED, /* AF_SNA */
+ AF_NOTSUPPORTED, /* AF_DECNET */
+ AF_NOTSUPPORTED, /* AF_DLI */
+ AF_NOTSUPPORTED, /* AF_LAT */
+ AF_NOTSUPPORTED, /* AF_HYLINK */
+ AF_NOTSUPPORTED, /* AF_APPLETALK */
+ AF_NOTSUPPORTED, /* AF_NIT */
+ AF_NOTSUPPORTED, /* AF_802 */
+ AF_NOTSUPPORTED, /* AF_OSI */
+ AF_NOTSUPPORTED, /* AF_X25 */
+ AF_NOTSUPPORTED, /* AF_OSINET */
+ AF_NOTSUPPORTED, /* AF_GOSIP */
+ AF_NOTSUPPORTED, /* AF_IPX */
+ AF_NOTSUPPORTED, /* AF_ROUTE */
+ AF_NOTSUPPORTED, /* AF_LINK */
+ LX_AF_INET6, /* AF_INET6 */
+ AF_NOTSUPPORTED, /* AF_KEY */
+ AF_NOTSUPPORTED, /* AF_NCA */
+ AF_NOTSUPPORTED, /* AF_POLICY */
+ AF_NOTSUPPORTED, /* AF_INET_OFFLOAD */
+ AF_NOTSUPPORTED, /* AF_TRILL */
+ LX_AF_PACKET, /* AF_PACKET */
+ LX_AF_NETLINK /* AF_LX_NETLINK */
+};
+
+#define LTOS_FAMILY(d) ((d) <= LX_AF_MAX ? ltos_family[(d)] : AF_INVAL)
+#define STOL_FAMILY(d) ((d) <= LX_AF_MAX ? stol_family[(d)] : AF_INVAL)
+
+
+static const int ltos_socktype[LX_SOCK_PACKET + 1] = {
+ SOCK_NOTSUPPORTED, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW,
+ SOCK_RDM, SOCK_SEQPACKET, SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED,
+ SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED
+};
+
+static const int stol_socktype[SOCK_SEQPACKET + 1] = {
+ SOCK_NOTSUPPORTED, LX_SOCK_DGRAM, LX_SOCK_STREAM, SOCK_NOTSUPPORTED,
+ LX_SOCK_RAW, LX_SOCK_RDM, LX_SOCK_SEQPACKET
+};
+
+#define LTOS_SOCKTYPE(t) \
+ ((t) <= LX_SOCK_PACKET ? ltos_socktype[(t)] : SOCK_INVAL)
+#define STOL_SOCKTYPE(t) \
+ ((t) <= SOCK_SEQPACKET ? stol_socktype[(t)] : SOCK_INVAL)
+
+
+/*
+ * This string is used to prefix all abstract namespace Unix sockets, ie all
+ * abstract namespace sockets are converted to regular sockets in the /tmp
+ * directory with .ABSK_ prefixed to their names.
+ */
+#define ABST_PRFX "/tmp/.ABSK_"
+#define ABST_PRFX_LEN (sizeof (ABST_PRFX) - 1)
+
+#define DATAFILT "datafilt"
+
+typedef enum {
+ lxa_none,
+ lxa_abstract,
+ lxa_devlog
+} lx_addr_type_t;
+
+static int
+ltos_pkt_proto(int protocol)
+{
+ switch (ntohs(protocol)) {
+ case LX_ETH_P_802_2:
+ return (ETH_P_802_2);
+ case LX_ETH_P_IP:
+ return (ETH_P_IP);
+ case LX_ETH_P_ARP:
+ return (ETH_P_ARP);
+ case LX_ETH_P_IPV6:
+ return (ETH_P_IPV6);
+ case LX_ETH_P_ALL:
+ case LX_ETH_P_802_3:
+ return (ETH_P_ALL);
+ default:
+ return (-1);
+ }
+}
+
+
+typedef struct lx_flag_map {
+ enum {
+ LXFM_MAP,
+ LXFM_IGNORE,
+ LXFM_UNSUP
+ } lxfm_action;
+ int lxfm_sunos_flag;
+ int lxfm_linux_flag;
+ char *lxfm_name;
+} lx_flag_map_t;
+
+static lx_flag_map_t lx_flag_map_tbl[] = {
+ { LXFM_MAP, MSG_OOB, LX_MSG_OOB, NULL },
+ { LXFM_MAP, MSG_PEEK, LX_MSG_PEEK, NULL },
+ { LXFM_MAP, MSG_DONTROUTE, LX_MSG_DONTROUTE, NULL },
+ { LXFM_MAP, MSG_CTRUNC, LX_MSG_CTRUNC, NULL },
+ { LXFM_MAP, MSG_TRUNC, LX_MSG_TRUNC, NULL },
+ { LXFM_MAP, MSG_DONTWAIT, LX_MSG_DONTWAIT, NULL },
+ { LXFM_MAP, MSG_EOR, LX_MSG_EOR, NULL },
+ { LXFM_MAP, MSG_WAITALL, LX_MSG_WAITALL, NULL },
+ /* MSG_CONFIRM is safe to ignore */
+ { LXFM_IGNORE, 0, LX_MSG_CONFIRM, NULL },
+ /*
+ * The NOSIGNAL and CMSG_CLOEXEC flags are handled by the emulation
+ * outside of the flag-conversion routine.
+ */
+ { LXFM_IGNORE, 0, LX_MSG_NOSIGNAL, NULL },
+ { LXFM_IGNORE, 0, LX_MSG_CMSG_CLOEXEC, NULL },
+ { LXFM_UNSUP, LX_MSG_PROXY, 0, "MSG_PROXY" },
+ { LXFM_UNSUP, LX_MSG_FIN, 0, "MSG_FIN" },
+ { LXFM_UNSUP, LX_MSG_SYN, 0, "MSG_SYN" },
+ { LXFM_UNSUP, LX_MSG_RST, 0, "MSG_RST" },
+ { LXFM_UNSUP, LX_MSG_ERRQUEUE, 0, "MSG_ERRQUEUE" },
+ { LXFM_UNSUP, LX_MSG_MORE, 0, "MSG_MORE" },
+ { LXFM_UNSUP, LX_MSG_WAITFORONE, 0, "MSG_WAITFORONE" },
+ { LXFM_UNSUP, LX_MSG_FASTOPEN, 0, "MSG_FASTOPEN" },
+};
+
+#define LX_FLAG_MAP_MAX \
+ (sizeof (lx_flag_map_tbl) / sizeof (lx_flag_map_tbl[0]))
+
+#define LX_UNSUP_BUFSZ 64
+
+static int
+lx_xlate_sock_flags(int inflags, lx_xlate_dir_t dir)
+{
+ int i, outflags = 0;
+ char buf[LX_UNSUP_BUFSZ];
+
+ VERIFY(dir == SUNOS_TO_LX || dir == LX_TO_SUNOS);
+
+ for (i = 0; i < LX_FLAG_MAP_MAX; i++) {
+ lx_flag_map_t *map = &lx_flag_map_tbl[i];
+ int match, out;
+
+ if (dir == SUNOS_TO_LX) {
+ match = inflags & map->lxfm_sunos_flag;
+ out = map->lxfm_linux_flag;
+ } else {
+ match = inflags & map->lxfm_linux_flag;
+ out = map->lxfm_sunos_flag;
+ }
+ switch (map->lxfm_action) {
+ case LXFM_MAP:
+ if (match != 0) {
+ inflags &= ~(match);
+ outflags |= out;
+ }
+ break;
+ case LXFM_IGNORE:
+ if (match != 0) {
+ inflags &= ~(match);
+ }
+ break;
+ case LXFM_UNSUP:
+ if (match != 0) {
+ (void) snprintf(buf, LX_UNSUP_BUFSZ,
+ "unsupported sock flag %s", map->lxfm_name);
+ lx_unsupported(buf);
+ }
+ }
+ }
+ if (inflags != 0) {
+ (void) snprintf(buf, LX_UNSUP_BUFSZ,
+ "unsupported sock flags 0x%08x", inflags);
+ lx_unsupported(buf);
+ }
+
+ return (outflags);
+}
+
+typedef enum lx_sun_type {
+ LX_SUN_NORMAL,
+ LX_SUN_ABSTRACT,
+} lx_sun_type_t;
+
+static void
+ltos_sockaddr_ux(const struct sockaddr *inaddr, const socklen_t inlen,
+ struct sockaddr **outaddr, socklen_t *outlen, lx_sun_type_t *sun_type)
+{
+ struct sockaddr_un buf;
+ /* Calculate size of (sun_family + any padding) in sockaddr */
+ int sizediff = (sizeof (buf) - sizeof (buf.sun_path));
+ int len = inlen - sizediff;
+
+ VERIFY(len > 0);
+ VERIFY(len <= sizeof (buf.sun_path));
+ bzero(&buf, sizeof (buf));
+
+ if (inaddr->sa_data[0] == '\0') {
+ /*
+ * Linux supports abstract Unix sockets, which are simply
+ * sockets that do not exist on the file system. These sockets
+ * are denoted by beginning the path with a NULL character. To
+ * support these, we strip out the leading NULL character and
+ * change the path to point to a real place in /tmp directory,
+ * by prepending ABST_PRFX and replacing all illegal characters
+ * with * '_'.
+ *
+ * Since these sockets are supposed to exist outside the
+ * filesystem, they must be cleaned up after use. This removal
+ * is performed during bind().
+ */
+ int idx, odx;
+
+ /* Add our abstract prefix */
+ (void) strcpy(buf.sun_path, ABST_PRFX);
+ for (idx = 1, odx = ABST_PRFX_LEN;
+ idx < len && odx < sizeof (buf.sun_path);
+ idx++, odx++) {
+ char c = inaddr->sa_data[idx];
+ if (c == '\0' || c == '/') {
+ buf.sun_path[odx] = '_';
+ } else {
+ buf.sun_path[odx] = c;
+ }
+ }
+
+ /*
+ * Since abstract socket addresses might not be NUL terminated,
+ * we must explicitly NUL terminate the translated path.
+ * Care is taken not to overflow the buffer.
+ */
+ if (odx == sizeof (buf.sun_path)) {
+ buf.sun_path[odx - 1] = '\0';
+ } else {
+ buf.sun_path[odx] = '\0';
+ }
+
+ if (sun_type != NULL) {
+ *sun_type = LX_SUN_ABSTRACT;
+ }
+ } else {
+ /* Copy the address directly, minding termination */
+ (void) strncpy(buf.sun_path, inaddr->sa_data, len);
+ len = strnlen(buf.sun_path, len);
+ if (len == sizeof (buf.sun_path)) {
+ buf.sun_path[len - 1] = '\0';
+ } else {
+ VERIFY(len < sizeof (buf.sun_path));
+ buf.sun_path[len] = '\0';
+ }
+
+ if (sun_type != NULL) {
+ *sun_type = LX_SUN_NORMAL;
+ }
+ }
+ buf.sun_family = AF_UNIX;
+ *outlen = strlen(buf.sun_path) + 1 + sizediff;
+ VERIFY(*outlen <= sizeof (struct sockaddr_un));
+
+ *outaddr = kmem_alloc(*outlen, KM_SLEEP);
+ bcopy(&buf, *outaddr, *outlen);
+}
+
+/*
+ * Copy in a Linux-native socket address from userspace and convert it into
+ * illumos format. When successful, it will allocate an appropriately sized
+ * struct to be freed by the caller.
+ */
+static long
+ltos_sockaddr_copyin(const struct sockaddr *inaddr, const socklen_t inlen,
+ struct sockaddr **outaddr, socklen_t *outlen, lx_sun_type_t *sun_type)
+{
+ sa_family_t family;
+ struct sockaddr *laddr;
+ struct sockaddr_ll *sal;
+ int proto, error = 0;
+
+ VERIFY(inaddr != NULL);
+
+ if (inlen < sizeof (sa_family_t) ||
+ inlen > sizeof (struct sockaddr_storage)) {
+ return (EINVAL);
+ }
+ laddr = kmem_alloc(inlen, KM_SLEEP);
+ if (copyin(inaddr, laddr, inlen) != 0) {
+ kmem_free(laddr, inlen);
+ return (EFAULT);
+ }
+
+ family = LTOS_FAMILY(laddr->sa_family);
+ switch (family) {
+ case (sa_family_t)AF_NOTSUPPORTED:
+ error = EPROTONOSUPPORT;
+ break;
+
+ case (sa_family_t)AF_INVAL:
+ error = EAFNOSUPPORT;
+ break;
+
+ case AF_UNIX:
+ if (inlen < sizeof (sa_family_t) + 2 ||
+ inlen > sizeof (struct sockaddr_un)) {
+ error = EINVAL;
+ break;
+ }
+ ltos_sockaddr_ux(laddr, inlen, outaddr, outlen,
+ sun_type);
+
+ /* AF_UNIX bypasses the standard copy logic */
+ kmem_free(laddr, inlen);
+ return (0);
+
+ case AF_PACKET:
+ if (inlen < sizeof (struct sockaddr_ll)) {
+ error = EINVAL;
+ break;
+ }
+ *outlen = sizeof (struct sockaddr_ll);
+
+ /* sll_protocol must be translated */
+ /* LINTED: alignment */
+ sal = (struct sockaddr_ll *)laddr;
+ proto = ltos_pkt_proto(sal->sll_protocol);
+ if (proto < 0) {
+ error = EINVAL;
+ }
+ sal->sll_protocol = proto;
+ break;
+
+ case AF_INET:
+ if (inlen < sizeof (struct sockaddr)) {
+ error = EINVAL;
+ break;
+ }
+ *outlen = sizeof (struct sockaddr);
+ break;
+
+ case AF_INET6:
+ /*
+ * The illumos sockaddr_in6 has one more 32-bit field
+ * than the Linux version. We simply zero that field
+ * via kmem_zalloc.
+ */
+ if (inlen < sizeof (lx_sockaddr_in6_t)) {
+ error = EINVAL;
+ break;
+ }
+ *outlen = sizeof (struct sockaddr_in6);
+ *outaddr = (struct sockaddr *)kmem_zalloc(*outlen,
+ KM_SLEEP);
+ bcopy(laddr, *outaddr, sizeof (lx_sockaddr_in6_t));
+ (*outaddr)->sa_family = AF_INET6;
+ /* AF_INET6 bypasses the standard copy logic */
+ kmem_free(laddr, inlen);
+ return (0);
+
+ default:
+ *outlen = inlen;
+ }
+
+ if (error == 0) {
+ /*
+ * For most address families, just copying into a sockaddr of
+ * the correct size and updating sa_family is adequate.
+ */
+ VERIFY(inlen >= *outlen);
+
+ *outaddr = (struct sockaddr *)kmem_zalloc(*outlen, KM_SLEEP);
+ bcopy(laddr, *outaddr, *outlen);
+ (*outaddr)->sa_family = family;
+ }
+ kmem_free(laddr, inlen);
+ return (error);
+}
+
+/*
+ * Convert an illumos-native socket address into Linux format and copy it out
+ * to userspace.
+ */
+static long
+stol_sockaddr_copyout(struct sockaddr *inaddr, socklen_t inlen,
+ struct sockaddr *outaddr, void *outlenp, socklen_t orig)
+{
+ socklen_t size = inlen;
+ struct sockaddr_storage buf;
+ struct sockaddr *bufaddr;
+
+ /*
+ * Either we were passed a valid sockaddr (with length) or the length
+ * is set to 0.
+ */
+ VERIFY(inaddr != NULL || inlen == 0);
+
+ if (inlen == 0) {
+ goto finish;
+ }
+
+
+ switch (inaddr->sa_family) {
+ case AF_INET:
+ if (inlen != sizeof (struct sockaddr)) {
+ return (EINVAL);
+ }
+ break;
+
+ case AF_INET6:
+ if (inlen != sizeof (struct sockaddr_in6)) {
+ return (EINVAL);
+ }
+ /*
+ * The linux sockaddr_in6 is shorter than illumos.
+ * Truncate the extra field on the way out.
+ */
+ size = (sizeof (lx_sockaddr_in6_t));
+ inlen = (sizeof (lx_sockaddr_in6_t));
+ break;
+
+ case AF_UNIX:
+ if (inlen > sizeof (struct sockaddr_un)) {
+ return (EINVAL);
+ }
+
+ /*
+ * On Linux an empty AF_UNIX address is returned as NULL, which
+ * means setting the returned length to only encompass the
+ * address family part of the buffer. However, some code also
+ * references the address portion of the buffer and uses it,
+ * even though the returned length has been shortened. Thus, we
+ * clear the buffer to ensure that the address portion is NULL.
+ */
+ if (inaddr->sa_data[0] == '\0') {
+ bzero(&buf, sizeof (buf));
+ inlen = sizeof (inaddr->sa_family);
+ }
+ break;
+
+ case (sa_family_t)AF_NOTSUPPORTED:
+ return (EPROTONOSUPPORT);
+
+ case (sa_family_t)AF_INVAL:
+ return (EAFNOSUPPORT);
+
+ default:
+ break;
+ }
+
+ /*
+ * The input should be smaller than sockaddr_storage, the largest
+ * sockaddr we support.
+ */
+ VERIFY(inlen <= sizeof (buf));
+
+ bufaddr = (struct sockaddr *)&buf;
+ bcopy(inaddr, bufaddr, inlen);
+ bufaddr->sa_family = STOL_FAMILY(bufaddr->sa_family);
+
+ /*
+ * It is possible that userspace passed us a smaller buffer than we
+ * hope to output. When this is the case, we will truncate our output
+ * to the max size of their buffer but report the true size of the
+ * sockaddr when outputting the outlen value.
+ */
+ size = (orig < size) ? orig : size;
+
+ if (copyout(bufaddr, outaddr, size) != 0) {
+ return (EFAULT);
+ }
+
+finish:
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ int32_t len32 = (int32_t)inlen;
+ if (copyout(&len32, outlenp, sizeof (len32)) != 0) {
+ return (EFAULT);
+ }
+ } else
+#endif /* defined(_LP64) */
+ {
+ if (copyout(&inlen, outlenp, sizeof (inlen)) != 0) {
+ return (EFAULT);
+ }
+ }
+
+ return (0);
+}
+
+typedef struct lx_cmsg_xlate {
+ int lcx_sunos_level;
+ int lcx_sunos_type;
+ int (*lcx_stol_conv)(struct cmsghdr *, struct cmsghdr *);
+ int lcx_linux_level;
+ int lcx_linux_type;
+ int (*lcx_ltos_conv)(struct cmsghdr *, struct cmsghdr *);
+} lx_cmsg_xlate_t;
+
+static int cmsg_conv_generic(struct cmsghdr *, struct cmsghdr *);
+static int stol_conv_ucred(struct cmsghdr *, struct cmsghdr *);
+static int ltos_conv_ucred(struct cmsghdr *, struct cmsghdr *);
+static int stol_conv_recvttl(struct cmsghdr *, struct cmsghdr *);
+
+/*
+ * Table describing SunOS <-> Linux cmsg translation mappings.
+ * Certain types (IP_RECVTTL) are only converted in one direction and are
+ * indicated by one of the translation functions being set to NULL.
+ */
+static lx_cmsg_xlate_t lx_cmsg_xlate_tbl[] = {
+ { SOL_SOCKET, SCM_RIGHTS, cmsg_conv_generic,
+ LX_SOL_SOCKET, LX_SCM_RIGHTS, cmsg_conv_generic },
+ { SOL_SOCKET, SCM_UCRED, stol_conv_ucred,
+ LX_SOL_SOCKET, LX_SCM_CRED, ltos_conv_ucred },
+ { SOL_SOCKET, SCM_TIMESTAMP, cmsg_conv_generic,
+ LX_SOL_SOCKET, LX_SCM_TIMESTAMP, cmsg_conv_generic },
+ { IPPROTO_IP, IP_PKTINFO, cmsg_conv_generic,
+ LX_IPPROTO_IP, LX_IP_PKTINFO, cmsg_conv_generic },
+ { IPPROTO_IP, IP_RECVTTL, stol_conv_recvttl,
+ LX_IPPROTO_IP, LX_IP_TTL, NULL },
+ { IPPROTO_IP, IP_RECVTOS, cmsg_conv_generic,
+ LX_IPPROTO_IP, LX_IP_TOS, cmsg_conv_generic },
+ { IPPROTO_IP, IP_TTL, cmsg_conv_generic,
+ LX_IPPROTO_IP, LX_IP_TTL, cmsg_conv_generic },
+ { IPPROTO_IPV6, IPV6_HOPLIMIT, cmsg_conv_generic,
+ LX_IPPROTO_IPV6, LX_IPV6_HOPLIMIT, cmsg_conv_generic },
+ { IPPROTO_IPV6, IPV6_PKTINFO, cmsg_conv_generic,
+ LX_IPPROTO_IPV6, LX_IPV6_PKTINFO, cmsg_conv_generic },
+ { IPPROTO_IPV6, IPV6_TCLASS, cmsg_conv_generic,
+ LX_IPPROTO_IPV6, LX_IPV6_TCLASS, cmsg_conv_generic }
+};
+
+#define LX_MAX_CMSG_XLATE \
+ (sizeof (lx_cmsg_xlate_tbl) / sizeof (lx_cmsg_xlate_tbl[0]))
+
+#if defined(_LP64)
+
+typedef struct {
+ int64_t cmsg_len;
+ int32_t cmsg_level;
+ int32_t cmsg_type;
+} lx_cmsghdr64_t;
+
+/* The alignment/padding for 64bit Linux cmsghdr is not the same. */
+#define LX_CMSG64_ALIGNMENT 8
+#define ISALIGNED_LX_CMSG64(addr) \
+ (((uintptr_t)(addr) & (LX_CMSG64_ALIGNMENT - 1)) == 0)
+#define ROUNDUP_LX_CMSG64_LEN(len) \
+ (((len) + LX_CMSG64_ALIGNMENT - 1) & ~(LX_CMSG64_ALIGNMENT - 1))
+
+#define LX_CMSG64_IS_ALIGNED(m) \
+ (((uintptr_t)(m) & (_CMSG_DATA_ALIGNMENT - 1)) == 0)
+#define LX_CMSG64_DATA(c) ((unsigned char *)(((lx_cmsghdr64_t *)(c)) + 1))
+/*
+ * LX_CMSG64_VALID is closely derived from CMSG_VALID with one particularly
+ * important addition. Since cmsg_len is 64bit, (cmsg + cmsg_len) is checked
+ * against the start address as well. This prevents bogus inputs from wrapping
+ * around the address space.
+ */
+#define LX_CMSG64_VALID(cmsg, start, end) \
+ (ISALIGNED_LX_CMSG64(cmsg) && \
+ ((uintptr_t)(cmsg) >= (uintptr_t)(start)) && \
+ ((uintptr_t)(cmsg) < (uintptr_t)(end)) && \
+ ((cmsg)->cmsg_len >= sizeof (lx_cmsghdr64_t)) && \
+ ((uintptr_t)(cmsg) + (cmsg)->cmsg_len <= (uintptr_t)(end)) && \
+ ((uintptr_t)(cmsg) + (cmsg)->cmsg_len >= (uintptr_t)(start)))
+#define LX_CMSG64_NEXT(cmsg) \
+ (lx_cmsghdr64_t *)((uintptr_t)(cmsg) + \
+ ROUNDUP_LX_CMSG64_LEN((cmsg)->cmsg_len))
+#define LX_CMSG64_DIFF sizeof (uint32_t)
+
+#endif /* defined(_LP64) */
+
+/*
+ * convert ucred_s to lx_ucred.
+ */
+static int
+stol_conv_ucred(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+ /*
+ * Format the data correctly in the omsg buffer.
+ */
+ if (omsg != NULL) {
+ struct ucred_s *scred;
+ prcred_t *cr;
+ lx_ucred_t lcred;
+
+ scred = (struct ucred_s *)CMSG_CONTENT(inmsg);
+ lcred.lxu_pid = scred->uc_pid;
+ /* LINTED: alignment */
+ cr = UCCRED(scred);
+ if (cr != NULL) {
+ lcred.lxu_uid = cr->pr_euid;
+ lcred.lxu_gid = cr->pr_egid;
+ } else {
+ lcred.lxu_uid = lcred.lxu_gid = 0;
+ }
+
+ bcopy(&lcred, CMSG_CONTENT(omsg), sizeof (lx_ucred_t));
+ }
+
+ return (sizeof (struct cmsghdr) + sizeof (lx_ucred_t));
+}
+
+static int
+ltos_conv_ucred(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+ if (omsg != NULL) {
+ struct ucred_s *uc;
+ prcred_t *pc;
+ lx_ucred_t *lcred;
+
+ uc = (struct ucred_s *)CMSG_CONTENT(omsg);
+ /* LINTED: alignment */
+ pc = (prcred_t *)((char *)uc + sizeof (struct ucred_s));
+
+ uc->uc_credoff = sizeof (struct ucred_s);
+
+ lcred = (lx_ucred_t *)CMSG_CONTENT(inmsg);
+
+ uc->uc_pid = lcred->lxu_pid;
+ pc->pr_euid = lcred->lxu_uid;
+ pc->pr_egid = lcred->lxu_gid;
+ }
+
+ return (sizeof (struct cmsghdr) + sizeof (struct ucred_s) +
+ sizeof (prcred_t));
+
+}
+
+static int
+stol_conv_recvttl(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+ /*
+ * SunOS communicates the TTL of incoming packets via IP_RECVTTL using
+ * a uint8_t value instead of IP_TTL using an int. This conversion is
+ * only needed in the one direction since Linux does not handle
+ * IP_RECVTTL in the sendmsg path.
+ */
+ if (omsg != NULL) {
+ uint8_t *inttl = (uint8_t *)CMSG_CONTENT(inmsg);
+ int *ottl = (int *)CMSG_CONTENT(omsg);
+
+ *ottl = (int)*inttl;
+ }
+
+ return (sizeof (struct cmsghdr) + sizeof (int));
+}
+
+static int
+cmsg_conv_generic(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+ if (omsg != NULL) {
+ size_t data_len;
+
+ data_len = inmsg->cmsg_len - sizeof (struct cmsghdr);
+ bcopy(CMSG_CONTENT(inmsg), CMSG_CONTENT(omsg), data_len);
+ }
+
+ return (inmsg->cmsg_len);
+}
+
+static int
+lx_xlate_cmsg(struct cmsghdr *inmsg, struct cmsghdr *omsg, lx_xlate_dir_t dir)
+{
+ int i;
+ int len;
+
+ VERIFY(dir == SUNOS_TO_LX || dir == LX_TO_SUNOS);
+
+ for (i = 0; i < LX_MAX_CMSG_XLATE; i++) {
+ lx_cmsg_xlate_t *xlate = &lx_cmsg_xlate_tbl[i];
+ if (dir == LX_TO_SUNOS &&
+ inmsg->cmsg_level == xlate->lcx_linux_level &&
+ inmsg->cmsg_type == xlate->lcx_linux_type &&
+ xlate->lcx_ltos_conv != NULL) {
+ len = xlate->lcx_ltos_conv(inmsg, omsg);
+ if (omsg != NULL) {
+ omsg->cmsg_len = len;
+ omsg->cmsg_level = xlate->lcx_sunos_level;
+ omsg->cmsg_type = xlate->lcx_sunos_type;
+ }
+ return (len);
+ } else if (dir == SUNOS_TO_LX &&
+ inmsg->cmsg_level == xlate->lcx_sunos_level &&
+ inmsg->cmsg_type == xlate->lcx_sunos_type &&
+ xlate->lcx_stol_conv != NULL) {
+ len = xlate->lcx_stol_conv(inmsg, omsg);
+ if (omsg != NULL) {
+ omsg->cmsg_len = len;
+ omsg->cmsg_level = xlate->lcx_linux_level;
+ omsg->cmsg_type = xlate->lcx_linux_type;
+ }
+ return (len);
+ }
+ }
+ /*
+ * The Linux man page for sendmsg does not define a specific error for
+ * unsupported cmsgs. While it is meant to indicated bad values for
+ * passed flags, EOPNOTSUPP appears to be the next closest choice.
+ */
+ return (-EOPNOTSUPP);
+}
+
+static long
+ltos_cmsgs_copyin(void *addr, socklen_t inlen, void **outmsg,
+ socklen_t *outlenp)
+{
+ void *inbuf, *obuf;
+ struct cmsghdr *inmsg, *omsg;
+ int slen = 0;
+
+ if (inlen < sizeof (struct cmsghdr) || inlen > SO_MAXARGSIZE) {
+ return (EINVAL);
+ }
+
+#if defined(_LP64)
+ if (get_udatamodel() == DATAMODEL_NATIVE &&
+ inlen < sizeof (lx_cmsghdr64_t)) {
+ /* The size requirements are more strict for 64bit. */
+ return (EINVAL);
+ }
+#endif /* defined(_LP64) */
+
+ inbuf = kmem_alloc(inlen, KM_SLEEP);
+ if (copyin(addr, inbuf, inlen) != 0) {
+ kmem_free(inbuf, inlen);
+ return (EFAULT);
+ }
+
+#if defined(_LP64)
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ /*
+ * Linux cmsg headers are longer than illumos under x86_64.
+ * Convert to regular cmsgs first.
+ */
+ lx_cmsghdr64_t *lmsg;
+ struct cmsghdr *smsg;
+ void *newbuf;
+ int len = 0;
+
+ /* Inventory the new cmsg size */
+ for (lmsg = (lx_cmsghdr64_t *)inbuf;
+ LX_CMSG64_VALID(lmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+ lmsg = LX_CMSG64_NEXT(lmsg)) {
+ len += ROUNDUP_cmsglen(lmsg->cmsg_len - LX_CMSG64_DIFF);
+ }
+
+ VERIFY(len < inlen);
+ if (len == 0) {
+ /* Input was bogus, so we can give up early. */
+ kmem_free(inbuf, inlen);
+ *outmsg = NULL;
+ *outlenp = 0;
+ return (EINVAL);
+ }
+
+ newbuf = kmem_alloc(len, KM_SLEEP);
+
+ for (lmsg = (lx_cmsghdr64_t *)inbuf,
+ smsg = (struct cmsghdr *)newbuf;
+ LX_CMSG64_VALID(lmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+ lmsg = LX_CMSG64_NEXT(lmsg), smsg = CMSG_NEXT(smsg)) {
+ smsg->cmsg_level = lmsg->cmsg_level;
+ smsg->cmsg_type = lmsg->cmsg_type;
+ smsg->cmsg_len = lmsg->cmsg_len - LX_CMSG64_DIFF;
+
+ /* The above length measurement should ensure this */
+ ASSERT(CMSG_VALID(smsg, newbuf,
+ (uintptr_t)newbuf + len));
+
+ bcopy(LX_CMSG64_DATA(lmsg), CMSG_CONTENT(smsg),
+ smsg->cmsg_len - sizeof (*smsg));
+ }
+
+ kmem_free(inbuf, inlen);
+ inbuf = newbuf;
+ inlen = len;
+ }
+#endif /* defined(_LP64) */
+
+ /*
+ * Now determine how much space we need for the conversion.
+ */
+ for (inmsg = (struct cmsghdr *)inbuf;
+ CMSG_VALID(inmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+ inmsg = CMSG_NEXT(inmsg)) {
+ int sz;
+
+ if ((sz = lx_xlate_cmsg(inmsg, NULL, LX_TO_SUNOS)) < 0) {
+ /* unsupported msg */
+ kmem_free(inbuf, inlen);
+ return (-sz);
+ }
+
+ slen += ROUNDUP_cmsglen(sz);
+ }
+
+ obuf = kmem_zalloc(slen, KM_SLEEP);
+
+ /*
+ * Now do the conversion.
+ */
+ for (inmsg = (struct cmsghdr *)inbuf, omsg = (struct cmsghdr *)obuf;
+ CMSG_VALID(inmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+ inmsg = CMSG_NEXT(inmsg), omsg = CMSG_NEXT(omsg)) {
+ VERIFY(lx_xlate_cmsg(inmsg, omsg, LX_TO_SUNOS) >= 0);
+ }
+
+ kmem_free(inbuf, inlen);
+ *outmsg = obuf;
+ *outlenp = slen;
+ return (0);
+}
+
+static long
+stol_cmsgs_copyout(void *input, socklen_t inlen, void *addr,
+ void *outlenp, socklen_t orig_outlen)
+{
+ void *obuf;
+ struct cmsghdr *inmsg, *omsg;
+ int error = 0;
+ socklen_t lx_len = 0;
+#if defined(_LP64)
+ model_t model = get_udatamodel();
+#endif
+
+ if (inlen == 0) {
+ /* Simply output the zero controllen */
+ goto finish;
+ }
+
+ VERIFY(inlen >= sizeof (struct cmsghdr));
+
+ /*
+ * First determine how much space we need for the conversion and
+ * make sure the caller has provided at least that much space to return
+ * results.
+ */
+ for (inmsg = (struct cmsghdr *)input;
+ CMSG_VALID(inmsg, input, (uintptr_t)input + inlen) != 0;
+ inmsg = CMSG_NEXT(inmsg)) {
+ int sz;
+
+ if ((sz = lx_xlate_cmsg(inmsg, NULL, SUNOS_TO_LX)) < 0) {
+ /* unsupported msg */
+ return (-sz);
+ }
+
+#if defined(_LP64)
+ if (model == DATAMODEL_NATIVE) {
+ /*
+ * The converted 64-bit cmsgs require an additional 4
+ * bytes of header space and must be aligned to 8 bytes
+ * (instead of the typical 4 for x86)
+ */
+ sz = ROUNDUP_LX_CMSG64_LEN(sz + LX_CMSG64_DIFF);
+ } else
+#endif /* defined(_LP64) */
+ {
+ /*
+ * The converted 32-bit cmsgs do not require additional
+ * header space or padding for Linux conversion.
+ */
+ sz = ROUNDUP_cmsglen(sz);
+ }
+
+ /*
+ * Unlike SunOS, Linux requires that the last cmsg be
+ * adequately padded for alignment.
+ */
+ lx_len += sz;
+ }
+
+ if (lx_len > orig_outlen || addr == NULL) {
+ /* This will be interpreted by the caller */
+ error = EMSGSIZE;
+ lx_len = 0;
+ goto finish;
+ }
+
+ /*
+ * Since cmsgs are often padded to an aligned size, kmem_zalloc is
+ * necessary to prevent leaking the contents of uninitialized memory.
+ */
+ obuf = kmem_zalloc(lx_len, KM_SLEEP);
+
+ /*
+ * Convert the msgs.
+ */
+ for (inmsg = (struct cmsghdr *)input, omsg = (struct cmsghdr *)obuf;
+ CMSG_VALID(inmsg, input, (uintptr_t)input + inlen) != 0;
+ inmsg = CMSG_NEXT(inmsg), omsg = CMSG_NEXT(omsg)) {
+ VERIFY(lx_xlate_cmsg(inmsg, omsg, SUNOS_TO_LX) >= 0);
+ }
+
+#if defined(_LP64)
+ if (model == DATAMODEL_NATIVE) {
+ /* Linux cmsg headers are longer than illumos under x86_64. */
+ struct cmsghdr *smsg;
+ lx_cmsghdr64_t *lmsg;
+ void *newbuf;
+
+ /*
+ * Once again, kmem_zalloc is needed to avoid leaking the
+ * contents of uninialized memory
+ */
+ newbuf = kmem_zalloc(lx_len, KM_SLEEP);
+ for (smsg = (struct cmsghdr *)obuf,
+ lmsg = (lx_cmsghdr64_t *)newbuf;
+ CMSG_VALID(smsg, obuf, (uintptr_t)obuf + inlen) != 0;
+ smsg = CMSG_NEXT(smsg), lmsg = LX_CMSG64_NEXT(lmsg)) {
+ lmsg->cmsg_level = smsg->cmsg_level;
+ lmsg->cmsg_type = smsg->cmsg_type;
+ lmsg->cmsg_len = smsg->cmsg_len + LX_CMSG64_DIFF;
+
+ ASSERT(LX_CMSG64_VALID(lmsg, newbuf,
+ (uintptr_t)newbuf + lx_len) != 0);
+
+ bcopy(CMSG_CONTENT(smsg), LX_CMSG64_DATA(lmsg),
+ smsg->cmsg_len - sizeof (*smsg));
+ }
+
+ kmem_free(obuf, lx_len);
+ obuf = newbuf;
+ }
+#endif /* defined(_LP64) */
+
+ if (copyout(obuf, addr, lx_len) != 0) {
+ kmem_free(obuf, lx_len);
+ return (EFAULT);
+ }
+ kmem_free(obuf, lx_len);
+
+finish:
+ if (outlenp != NULL) {
+#if defined(_LP64)
+ if (model != DATAMODEL_NATIVE) {
+ int32_t len32 = (int32_t)lx_len;
+ if (copyout(&len32, outlenp, sizeof (len32)) != 0) {
+ return (EFAULT);
+ }
+ } else
+#endif /* defined(_LP64) */
+ {
+ if (copyout(&lx_len, outlenp, sizeof (lx_len)) != 0) {
+ return (EFAULT);
+ }
+ }
+ }
+ return (error);
+}
+
+static void
+lx_cmsg_set_cloexec(void *input, socklen_t inlen)
+{
+ struct cmsghdr *inmsg;
+
+ if (inlen == 0) {
+ return;
+ }
+
+ for (inmsg = (struct cmsghdr *)input;
+ CMSG_VALID(inmsg, input, (uintptr_t)input + inlen) != 0;
+ inmsg = CMSG_NEXT(inmsg)) {
+ if (inmsg->cmsg_level == SOL_SOCKET &&
+ inmsg->cmsg_type == SCM_RIGHTS) {
+ int *fds = (int *)CMSG_CONTENT(inmsg);
+ int i, num = (int)CMSG_CONTENTLEN(inmsg) / sizeof (int);
+
+ for (i = 0; i < num; i++) {
+ char flags;
+ file_t *fp;
+
+ fp = getf(fds[i]);
+ if (fp == NULL) {
+ /*
+ * It is possible that a received fd
+ * will already have been closed if a
+ * thread in the local process is
+ * indiscriminately issuing close(2)
+ * calls while the message is being
+ * received. If that is the case, no
+ * further processing of the fd is
+ * needed. It will still be passed
+ * up in the cmsg even though the
+ * caller chose to close it already.
+ */
+ continue;
+ }
+
+ flags = f_getfd(fds[i]);
+ flags |= FD_CLOEXEC;
+ f_setfd(fds[i], flags);
+ releasef(fds[i]);
+ }
+ }
+ }
+}
+
+static int
+lx_cmsg_try_ucred(sonode_t *so, struct nmsghdr *msg, socklen_t origlen)
+{
+ lx_socket_aux_data_t *sad;
+ struct cmsghdr *cmsg = NULL;
+ int msgsize;
+ cred_t *cred;
+
+ if (origlen == 0) {
+ return (0);
+ }
+ sad = lx_sad_acquire(SOTOV(so));
+ if ((sad->lxsad_flags & LXSAD_FL_STRCRED) == 0) {
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+ }
+ mutex_exit(&sad->lxsad_lock);
+
+ mutex_enter(&so->so_lock);
+ if (so->so_peercred == NULL) {
+ mutex_exit(&so->so_lock);
+ return (0);
+ }
+ crhold(cred = so->so_peercred);
+ mutex_exit(&so->so_lock);
+
+ msgsize = ucredminsize(cred) + sizeof (struct cmsghdr);
+ if (msg->msg_control == NULL) {
+ msg->msg_controllen = msgsize;
+ msg->msg_control = cmsg = kmem_zalloc(msgsize, KM_SLEEP);
+ } else {
+ /*
+ * The so_recvmsg operation may have allocated a msg_control
+ * buffer which precisely fits all returned cmsgs. We must
+ * manually verify the length of that cmsg data and reallocate
+ * the buffer if it lacks the necessary space.
+ */
+ uintptr_t start = (uintptr_t)msg->msg_control;
+ uintptr_t end = start + msg->msg_controllen;
+
+ ASSERT(msg->msg_controllen > 0);
+ cmsg = (struct cmsghdr *)msg->msg_control;
+ while (CMSG_VALID(cmsg, start, end) != 0) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_UCRED) {
+ /*
+ * If some later code change results in a ucred
+ * being attached anyways, there is no need for
+ * us to do it manually
+ */
+ crfree(cred);
+ return (0);
+ }
+ cmsg = CMSG_NEXT(cmsg);
+ }
+ if (((uintptr_t)cmsg + msgsize) > end) {
+ socklen_t offset = (uintptr_t)cmsg - start;
+ socklen_t newsize = offset + msgsize;
+ void *newbuf;
+
+ if (newsize < msg->msg_controllen) {
+ /* size overflow, bail */
+ crfree(cred);
+ return (-1);
+ }
+ newbuf = kmem_alloc(newsize, KM_SLEEP);
+ bcopy(msg->msg_control, newbuf, msg->msg_controllen);
+ kmem_free(msg->msg_control, msg->msg_controllen);
+
+ msg->msg_control = newbuf;
+ msg->msg_controllen = newsize;
+ cmsg = (struct cmsghdr *)((uintptr_t)newbuf + offset);
+ }
+ }
+
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_UCRED;
+ cmsg->cmsg_len = msgsize;
+ (void) cred2ucred(cred, so->so_cpid, CMSG_CONTENT(cmsg), CRED());
+ crfree(cred);
+ return (0);
+}
+
+static lx_socket_aux_data_t *
+lx_sad_acquire(vnode_t *vp)
+{
+ lx_socket_aux_data_t *cur, *created;
+
+ mutex_enter(&vp->v_vsd_lock);
+ cur = (lx_socket_aux_data_t *)vsd_get(vp, lx_socket_vsd);
+ if (cur == NULL) {
+ /* perform our allocation carefully */
+ mutex_exit(&vp->v_vsd_lock);
+
+ created = (lx_socket_aux_data_t *)kmem_zalloc(
+ sizeof (*created), KM_SLEEP);
+
+ mutex_enter(&vp->v_vsd_lock);
+ cur = (lx_socket_aux_data_t *)vsd_get(vp, lx_socket_vsd);
+ if (cur == NULL) {
+ mutex_init(&created->lxsad_lock, NULL, MUTEX_DEFAULT,
+ NULL);
+ (void) vsd_set(vp, lx_socket_vsd, created);
+ cur = created;
+ } else {
+ kmem_free(created, sizeof (*created));
+ }
+ }
+ mutex_exit(&vp->v_vsd_lock);
+ mutex_enter(&cur->lxsad_lock);
+ return (cur);
+}
+
+static int
+lx_convert_pkt_proto(int protocol)
+{
+ switch (ntohs(protocol)) {
+ case LX_ETH_P_802_2:
+ return (ETH_P_802_2);
+ case LX_ETH_P_IP:
+ return (ETH_P_IP);
+ case LX_ETH_P_ARP:
+ return (ETH_P_ARP);
+ case LX_ETH_P_IPV6:
+ return (ETH_P_IPV6);
+ case LX_ETH_P_ALL:
+ case LX_ETH_P_802_3:
+ return (ETH_P_ALL);
+ default:
+ return (-1);
+ }
+}
+
+static int
+lx_convert_sock_args(int in_dom, int in_type, int in_proto, int *out_dom,
+ int *out_type, int *out_options, int *out_proto)
+{
+ int domain, type, options;
+
+ if (in_dom < 0 || in_type < 0 || in_proto < 0)
+ return (EINVAL);
+
+ domain = LTOS_FAMILY(in_dom);
+ if (domain == AF_NOTSUPPORTED || domain == AF_UNSPEC)
+ return (EAFNOSUPPORT);
+ if (domain == AF_INVAL)
+ return (EINVAL);
+
+ type = LTOS_SOCKTYPE(in_type & LX_SOCK_TYPE_MASK);
+ if (type == SOCK_INVAL)
+ return (EINVAL);
+ /*
+ * Linux does not allow the app to specify IP Protocol for raw sockets.
+ * SunOS does, so bail out here.
+ */
+ if (type == SOCK_NOTSUPPORTED ||
+ (domain == AF_INET && type == SOCK_RAW && in_proto == IPPROTO_IP)) {
+ if (lx_kern_release_cmp(curzone, "2.6.15") < 0) {
+ /*
+ * Use error appropriate for kernel version.
+ * See lx_socket_create for more detail.
+ */
+ return (ESOCKTNOSUPPORT);
+ }
+ return (EPROTONOSUPPORT);
+ }
+
+ options = 0;
+ in_type &= ~(LX_SOCK_TYPE_MASK);
+ if (in_type & LX_SOCK_NONBLOCK) {
+ in_type ^= LX_SOCK_NONBLOCK;
+ options |= SOCK_NONBLOCK;
+ }
+ if (in_type & LX_SOCK_CLOEXEC) {
+ in_type ^= LX_SOCK_CLOEXEC;
+ options |= SOCK_CLOEXEC;
+ }
+ if (in_type != 0) {
+ return (EINVAL);
+ }
+
+ /* Protocol definitions for PF_PACKET differ between Linux and SunOS */
+ if (domain == PF_PACKET &&
+ (in_proto = lx_convert_pkt_proto(in_proto)) < 0)
+ return (EINVAL);
+
+ *out_dom = domain;
+ *out_type = type;
+ *out_options = options;
+ *out_proto = in_proto;
+ return (0);
+}
+
+/*
+ * For restartable socket syscall handling, the relevant syscalls are only
+ * restarted when a timeout is not set on the socket.
+ */
+static void
+lx_sock_syscall_restart(sonode_t *so, boolean_t recv)
+{
+ if (recv) {
+ if (so->so_rcvtimeo != 0)
+ return;
+ } else {
+ if (so->so_sndtimeo != 0)
+ return;
+ }
+
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+}
+
+static int
+lx_socket_create(int domain, int type, int protocol, int options, file_t **fpp,
+ int *fdp)
+{
+ sonode_t *so;
+ vnode_t *vp;
+ file_t *fp;
+ int err, fd;
+
+ /*
+ * EACCES is returned in Linux when the user isn't allowed to use a
+ * "ping socket". EACCES is also used by the iputils-ping userland
+ * application to determine if fallback to SOCK_RAW is necessary.
+ *
+ * This can be removed if we ever implement SOCK_DGRAM + IPPROTO_ICMP.
+ */
+ if ((domain == AF_INET && type == SOCK_DGRAM && protocol ==
+ IPPROTO_ICMP) || (domain == AF_INET6 && type == SOCK_DGRAM &&
+ protocol == IPPROTO_ICMPV6))
+ return (EACCES);
+
+ /* logic cloned from so_socket */
+ so = socket_create(domain, type, protocol, NULL, NULL, SOCKET_SLEEP,
+ SOV_DEFAULT, CRED(), &err);
+
+ if (so == NULL) {
+ switch (err) {
+ case EPROTOTYPE:
+ case EPROTONOSUPPORT:
+ if (lx_kern_release_cmp(curzone, "2.6.15") < 0) {
+ /*
+ * Linux changed its socket error behavior in
+ * versions 2.6.15 and later. See git commit
+ * 86c8f9d158f68538a971a47206a46a22c7479bac in
+ * the Linux repository.
+ *
+ * LTP presently checks for version 2.6.16.
+ */
+ return (ESOCKTNOSUPPORT);
+ }
+ return (EPROTONOSUPPORT);
+ default:
+ return (err);
+ }
+ }
+
+ /* Allocate a file descriptor for the socket */
+ vp = SOTOV(so);
+ if ((err = falloc(vp, FWRITE|FREAD, &fp, &fd)) != 0) {
+ (void) socket_close(so, 0, CRED());
+ socket_destroy(so);
+ return (err);
+ }
+
+ /*
+ * Linux programs do not tolerate errors appearing from asynchronous
+ * events (such as ICMP messages arriving). Setting SM_DEFERERR will
+ * prevent checking/delivery of such errors.
+ */
+ so->so_mode |= SM_DEFERERR;
+
+ /* Now fill in the entries that falloc reserved */
+ if (options & SOCK_NONBLOCK) {
+ so->so_state |= SS_NONBLOCK;
+ fp->f_flag |= FNONBLOCK;
+ }
+ mutex_exit(&fp->f_tlock);
+ *fpp = fp;
+ *fdp = fd;
+ return (0);
+}
+
+static void
+lx_socket_destroy(file_t *fp, int fd)
+{
+ sonode_t *so = VTOSO(fp->f_vnode);
+
+ setf(fd, NULL);
+
+ mutex_enter(&fp->f_tlock);
+ unfalloc(fp);
+
+ (void) socket_close(so, 0, CRED());
+ socket_destroy(so);
+}
+
+long
+lx_socket(int domain, int type, int protocol)
+{
+ int error, options, fd = -1;
+ file_t *fp = NULL;
+
+ if ((error = lx_convert_sock_args(domain, type, protocol, &domain,
+ &type, &options, &protocol)) != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_socket_create(domain, type, protocol, options, &fp, &fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ setf(fd, fp);
+ if ((options & SOCK_CLOEXEC) != 0) {
+ f_setfd(fd, FD_CLOEXEC);
+ }
+ return (fd);
+}
+
+long
+lx_bind(long sock, uintptr_t name, socklen_t namelen)
+{
+ struct sonode *so;
+ struct sockaddr *addr = NULL;
+ socklen_t len = 0;
+ file_t *fp;
+ int error;
+ lx_sun_type_t sun_type;
+ boolean_t not_sock = B_FALSE;
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ return (set_errno(error));
+ }
+
+ if (namelen != 0) {
+ error = ltos_sockaddr_copyin((struct sockaddr *)name, namelen,
+ &addr, &len, &sun_type);
+ if (error != 0) {
+ releasef(sock);
+ return (set_errno(error));
+ }
+ }
+
+ if (addr != NULL && addr->sa_family == AF_UNIX) {
+ vnode_t *vp;
+
+ error = so_ux_lookup(so, (struct sockaddr_un *)addr, B_TRUE,
+ &vp);
+ if (error == 0) {
+ /* A valid socket exists and is open at this address. */
+ VN_RELE(vp);
+ } else {
+ /* Keep track of paths which are not valid sockets. */
+ if (error == ENOTSOCK) {
+ not_sock = B_TRUE;
+ }
+
+ /*
+ * When binding to an abstract namespace address or
+ * /dev/log, implicit clean-up must occur if there is
+ * not a valid socket at the specififed address. See
+ * ltos_sockaddr_copyin for details about why these
+ * socket types act differently.
+ */
+ if (sun_type == LX_SUN_ABSTRACT) {
+ (void) vn_removeat(NULL, addr->sa_data,
+ UIO_SYSSPACE, RMFILE);
+ }
+ }
+ }
+
+ error = socket_bind(so, addr, len, _SOBIND_XPG4_2, CRED());
+
+ /*
+ * Linux returns EADDRINUSE for attempts to bind to Unix domain
+ * sockets that aren't sockets.
+ */
+ if (error == EINVAL && addr != NULL && addr->sa_family == AF_UNIX &&
+ not_sock == B_TRUE) {
+ error = EADDRINUSE;
+ }
+
+ releasef(sock);
+
+ if (addr != NULL) {
+ kmem_free(addr, len);
+ }
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_connect(long sock, uintptr_t name, socklen_t namelen)
+{
+ struct sonode *so;
+ struct sockaddr *addr = NULL;
+ lx_socket_aux_data_t *sad = NULL;
+ socklen_t len = 0;
+ file_t *fp;
+ int error;
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ return (set_errno(error));
+ }
+
+ /*
+ * Ensure the name is sized appropriately before we alloc memory and
+ * copy it in from userspace. We need at least the address family to
+ * make later sizing decisions.
+ */
+ if (namelen != 0) {
+ error = ltos_sockaddr_copyin((struct sockaddr *)name, namelen,
+ &addr, &len, NULL);
+ if (error != 0) {
+ releasef(sock);
+ return (set_errno(error));
+ }
+ }
+
+ error = socket_connect(so, addr, len, fp->f_flag,
+ _SOCONNECT_XPG4_2, CRED());
+
+ if (error == EINTR)
+ lx_sock_syscall_restart(so, B_FALSE);
+
+ /*
+ * Linux connect(2) behavior is rather strange when using the
+ * O_NONBLOCK flag. The first call will return EINPROGRESS, as
+ * expected. Provided that is successful, a second call to connect
+ * will return 0 instead of EISCONN. Subsequent connect calls will
+ * return EISCONN.
+ */
+ if ((fp->f_flag & FNONBLOCK) != 0 && error != 0) {
+ sad = lx_sad_acquire(SOTOV(so));
+ if (error == EISCONN &&
+ sad->lxsad_status == LXSS_CONNECTING) {
+ /* Report the one success */
+ sad->lxsad_status = LXSS_CONNECTED;
+ error = 0;
+ } else if (error == EINPROGRESS) {
+ sad->lxsad_status = LXSS_CONNECTING;
+ }
+ mutex_exit(&sad->lxsad_lock);
+ }
+
+ /*
+ * When connecting to a UDP socket, configure it so that future
+ * sendto/sendmsg operations are allowed to specify a destination
+ * address. See the Posix spec. for sendto(2). Linux allows this while
+ * illumos would return EISCONN if the option is not set.
+ */
+ if (error == 0 && so->so_protocol == IPPROTO_UDP &&
+ (so->so_family == AF_INET || so->so_family == AF_INET6)) {
+ int val = 1;
+
+ DTRACE_PROBE(lx__connect__udp);
+ (void) socket_setsockopt(so, IPPROTO_UDP, UDP_SND_TO_CONNECTED,
+ &val, sizeof (val), CRED());
+ }
+
+ releasef(sock);
+
+ if (addr != NULL) {
+ kmem_free(addr, len);
+ }
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+/*
+ * Custom version of socket_recvmsg for error-handling overrides.
+ */
+static int
+lx_socket_recvmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
+ cred_t *cr)
+{
+ int error;
+ ssize_t orig_resid = uiop->uio_resid;
+
+ /*
+ * Do not bypass the cache when reading data, as the application
+ * is likely to access the data shortly.
+ */
+ uiop->uio_extflg |= UIO_COPY_CACHED;
+
+ error = SOP_RECVMSG(so, msg, uiop, cr);
+
+ switch (error) {
+ case EINTR:
+ /* EAGAIN is EWOULDBLOCK */
+ case EWOULDBLOCK:
+ /* We did a partial read */
+ if (uiop->uio_resid != orig_resid)
+ error = 0;
+ break;
+ case ENOTCONN:
+ /*
+ * The rules are different for non-blocking sockets which are
+ * still in the process of making a connection
+ */
+ if ((msg->msg_flags & MSG_DONTWAIT) != 0 ||
+ (uiop->uio_fmode & (FNONBLOCK|FNDELAY)) != 0) {
+ error = EAGAIN;
+ }
+ break;
+ default:
+ break;
+ }
+ return (error);
+}
+
+static long
+lx_recv_common(int sock, struct nmsghdr *msg, xuio_t *xuiop, int flags,
+ void *namelenp, void *controllenp, void *flagsp)
+{
+ struct sonode *so;
+ file_t *fp;
+ void *name;
+ socklen_t namelen;
+ void *control;
+ socklen_t controllen;
+ ssize_t len;
+ int error;
+ boolean_t fd_cloexec;
+ boolean_t is_peek_trunc;
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ return (set_errno(error));
+ }
+
+ fd_cloexec = ((flags & LX_MSG_CMSG_CLOEXEC) != 0);
+ flags = lx_xlate_sock_flags(flags, LX_TO_SUNOS);
+ is_peek_trunc = (flags & (MSG_PEEK|MSG_TRUNC)) == (MSG_PEEK|MSG_TRUNC);
+ len = xuiop->xu_uio.uio_resid;
+ xuiop->xu_uio.uio_fmode = fp->f_flag;
+ xuiop->xu_uio.uio_extflg = UIO_COPY_CACHED;
+
+ /*
+ * Linux accepts MSG_TRUNC as an input flag, unlike SunOS and many
+ * other UNIX distributions. When combined with MSG_PEEK, it causes
+ * recvmsg to return the size of the waiting message, regardless of
+ * buffer size. This behavior is commonly used with a 0-length buffer
+ * to interrogate the size of a queued message prior to allocating a
+ * buffer for it.
+ *
+ * In order to support this functionality, a custom XUIO type is used
+ * to communicate the total message size out from the depths of sockfs.
+ */
+ if (is_peek_trunc) {
+ xuiop->xu_uio.uio_extflg |= UIO_XUIO;
+ xuiop->xu_type = UIOTYPE_PEEKSIZE;
+ xuiop->xu_ext.xu_ps.xu_ps_set = B_FALSE;
+ xuiop->xu_ext.xu_ps.xu_ps_size = 0;
+ }
+
+ name = msg->msg_name;
+ namelen = msg->msg_namelen;
+ control = msg->msg_control;
+ controllen = msg->msg_controllen;
+
+ /*
+ * socket_recvmsg will allocate these if needed.
+ * NULL them out to prevent any confusion.
+ */
+ msg->msg_name = NULL;
+ msg->msg_control = NULL;
+
+ msg->msg_flags = flags & (MSG_OOB | MSG_PEEK | MSG_WAITALL |
+ MSG_DONTWAIT);
+ /* Default to XPG4.2 operation */
+ msg->msg_flags |= MSG_XPG4_2;
+
+ error = lx_socket_recvmsg(so, msg, (struct uio *)xuiop, CRED());
+ if (error) {
+ if (error == EINTR)
+ lx_sock_syscall_restart(so, B_TRUE);
+ releasef(sock);
+ return (set_errno(error));
+ }
+ lwp_stat_update(LWP_STAT_MSGRCV, 1);
+ releasef(sock);
+
+ if (namelen != 0) {
+ error = stol_sockaddr_copyout(msg->msg_name, msg->msg_namelen,
+ name, namelenp, namelen);
+
+ if (msg->msg_namelen != 0) {
+ kmem_free(msg->msg_name, (size_t)msg->msg_namelen);
+ msg->msg_namelen = 0;
+ }
+
+ /*
+ * Errors during copyout of the name are not a concern to Linux
+ * callers at this point in the syscall
+ */
+ if (error != 0 && error != EFAULT) {
+ goto err;
+ }
+ }
+
+ if (controllen != 0) {
+ if (fd_cloexec) {
+ /*
+ * If CLOEXEC needs to set on file descriptors passed
+ * via SCM_RIGHTS, do so before formatting the cmsgs
+ * for Linux.
+ */
+ lx_cmsg_set_cloexec(msg->msg_control,
+ msg->msg_controllen);
+ }
+ if (so->so_family == AF_UNIX &&
+ (so->so_mode & SM_CONNREQUIRED) != 0) {
+ /*
+ * It may be necessary to append a SCM_UCRED cmsg to
+ * the controls if SO_PASSCRED is set on a
+ * connection-oriented AF_UNIX socket.
+ *
+ * See lx_setsockopt_socket for more details.
+ */
+ if (lx_cmsg_try_ucred(so, msg, controllen) != 0) {
+ msg->msg_flags |= MSG_CTRUNC;
+ }
+ }
+
+ error = stol_cmsgs_copyout(msg->msg_control,
+ msg->msg_controllen, control, controllenp, controllen);
+
+ if (error != 0) {
+ /*
+ * If there was an error during cmsg translation or
+ * copyout, we need to clean up any FDs that are being
+ * passed back via SCM_RIGHTS. This prevents us from
+ * leaking those open files.
+ */
+ so_closefds(msg->msg_control, msg->msg_controllen, 0,
+ 0);
+
+ /*
+ * An error during cmsg_copyout means we had
+ * _something_ to process.
+ */
+ VERIFY(msg->msg_controllen != 0);
+
+ kmem_free(msg->msg_control,
+ (size_t)msg->msg_controllen);
+ msg->msg_controllen = 0;
+
+ if (error == EMSGSIZE) {
+ /* Communicate that messages were truncated */
+ msg->msg_flags |= MSG_CTRUNC;
+ error = 0;
+ } else {
+ goto err;
+ }
+ } else if (msg->msg_controllen != 0) {
+ kmem_free(msg->msg_control,
+ (size_t)msg->msg_controllen);
+ msg->msg_controllen = 0;
+ }
+ }
+
+ if (flagsp != NULL) {
+ int flags;
+
+ /* Clear internal flag. */
+ flags = msg->msg_flags & ~MSG_XPG4_2;
+ flags = lx_xlate_sock_flags(flags, SUNOS_TO_LX);
+
+ if (copyout(&flags, flagsp, sizeof (flags) != 0)) {
+ error = EFAULT;
+ goto err;
+ }
+ }
+
+ /*
+ * If both MSG_PEEK|MSG_TRUNC were set on the input flags and the
+ * socket layer was able to calculate the total message size for us,
+ * return that instead of the copied size.
+ */
+ if (is_peek_trunc && xuiop->xu_ext.xu_ps.xu_ps_set == B_TRUE) {
+ return (xuiop->xu_ext.xu_ps.xu_ps_size);
+ }
+
+ return (len - xuiop->xu_uio.uio_resid);
+
+err:
+ if (msg->msg_controllen != 0) {
+ /* Prevent FD leakage (see above) */
+ so_closefds(msg->msg_control, msg->msg_controllen, 0, 0);
+ kmem_free(msg->msg_control, (size_t)msg->msg_controllen);
+ }
+ if (msg->msg_namelen != 0) {
+ kmem_free(msg->msg_name, (size_t)msg->msg_namelen);
+ }
+ return (set_errno(error));
+}
+
+long
+lx_recv(int sock, void *buffer, size_t len, int flags)
+{
+ struct nmsghdr smsg;
+ xuio_t xuio;
+ struct iovec uiov;
+
+ if ((ssize_t)len < 0) {
+ /*
+ * The input len is unsigned, so limit it to SSIZE_MAX since
+ * the return value is signed.
+ */
+ return (set_errno(EINVAL));
+ }
+
+ uiov.iov_base = buffer;
+ uiov.iov_len = len;
+ xuio.xu_uio.uio_loffset = 0;
+ xuio.xu_uio.uio_iov = &uiov;
+ xuio.xu_uio.uio_iovcnt = 1;
+ xuio.xu_uio.uio_resid = len;
+ xuio.xu_uio.uio_segflg = UIO_USERSPACE;
+ xuio.xu_uio.uio_limit = 0;
+
+ smsg.msg_namelen = 0;
+ smsg.msg_controllen = 0;
+ smsg.msg_flags = 0;
+ return (lx_recv_common(sock, &smsg, &xuio, flags, NULL, NULL, NULL));
+}
+
+long
+lx_recvfrom(int sock, void *buffer, size_t len, int flags,
+ struct sockaddr *srcaddr, socklen_t *addrlenp)
+{
+ struct nmsghdr smsg;
+ xuio_t xuio;
+ struct iovec uiov;
+
+ if ((ssize_t)len < 0) {
+ /* Keep len reasonably limited (see lx_recv) */
+ return (set_errno(EINVAL));
+ }
+
+ uiov.iov_base = buffer;
+ uiov.iov_len = len;
+ xuio.xu_uio.uio_loffset = 0;
+ xuio.xu_uio.uio_iov = &uiov;
+ xuio.xu_uio.uio_iovcnt = 1;
+ xuio.xu_uio.uio_resid = len;
+ xuio.xu_uio.uio_segflg = UIO_USERSPACE;
+ xuio.xu_uio.uio_limit = 0;
+
+ smsg.msg_name = (char *)srcaddr;
+ if (addrlenp != NULL && srcaddr != NULL) {
+ /*
+ * Despite addrlenp being defined as a socklen_t *, Linux
+ * treats it internally as an int *. Certain LTP tests depend
+ * upon this behavior, so we must emulate it as well.
+ */
+ int namelen;
+
+ if (copyin(addrlenp, &namelen, sizeof (namelen)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ if (namelen < 0) {
+ return (set_errno(EINVAL));
+ }
+ smsg.msg_namelen = namelen;
+ } else {
+ smsg.msg_namelen = 0;
+ }
+ smsg.msg_controllen = 0;
+ smsg.msg_flags = 0;
+
+ return (lx_recv_common(sock, &smsg, &xuio, flags, addrlenp, NULL,
+ NULL));
+}
+
+long
+lx_recvmsg(int sock, void *msg, int flags)
+{
+ struct nmsghdr smsg;
+ xuio_t xuio;
+ struct iovec luiov[IOV_MAX_STACK], *uiov;
+ int i, iovcnt, iovsize;
+ long res;
+ ssize_t len = 0;
+ void *namelenp, *controllenp, *flagsp;
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_msghdr32_t lmsg32;
+ if (copyin(msg, &lmsg32, sizeof (lmsg32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ smsg.msg_name = (void *)(uintptr_t)lmsg32.msg_name;
+ smsg.msg_namelen = lmsg32.msg_namelen;
+ smsg.msg_iov = (struct iovec *)(uintptr_t)lmsg32.msg_iov;
+ smsg.msg_iovlen = lmsg32.msg_iovlen;
+ smsg.msg_control = (void *)(uintptr_t)lmsg32.msg_control;
+ smsg.msg_controllen = lmsg32.msg_controllen;
+ smsg.msg_flags = lmsg32.msg_flags;
+
+ namelenp = &((lx_msghdr32_t *)msg)->msg_namelen;
+ controllenp = &((lx_msghdr32_t *)msg)->msg_controllen;
+ flagsp = &((lx_msghdr32_t *)msg)->msg_flags;
+ } else
+#endif /* defined(_LP64) */
+ {
+ lx_msghdr_t lmsg;
+ if (copyin(msg, &lmsg, sizeof (lmsg)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ smsg.msg_name = lmsg.msg_name;
+ smsg.msg_namelen = lmsg.msg_namelen;
+ smsg.msg_iov = lmsg.msg_iov;
+ smsg.msg_iovlen = lmsg.msg_iovlen;
+ smsg.msg_control = lmsg.msg_control;
+ smsg.msg_controllen = lmsg.msg_controllen;
+ smsg.msg_flags = lmsg.msg_flags;
+
+ namelenp = &((lx_msghdr_t *)msg)->msg_namelen;
+ controllenp = &((lx_msghdr_t *)msg)->msg_controllen;
+ flagsp = &((lx_msghdr_t *)msg)->msg_flags;
+ }
+
+ iovcnt = smsg.msg_iovlen;
+ if (iovcnt < 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EMSGSIZE));
+ }
+ if (iovcnt > IOV_MAX_STACK) {
+ iovsize = iovcnt * sizeof (struct iovec);
+ uiov = kmem_alloc(iovsize, KM_SLEEP);
+ } else if (iovcnt > 0) {
+ iovsize = 0;
+ uiov = luiov;
+ } else {
+ iovsize = 0;
+ uiov = NULL;
+ goto noiov;
+ }
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ /* convert from 32bit iovec structs */
+ struct iovec32 luiov32[IOV_MAX_STACK], *uiov32;
+ ssize_t iov32size;
+ ssize32_t count32;
+
+ iov32size = iovcnt * sizeof (struct iovec32);
+ if (iovsize != 0) {
+ uiov32 = kmem_alloc(iov32size, KM_SLEEP);
+ } else {
+ uiov32 = luiov32;
+ }
+
+ if (copyin((struct iovec32 *)smsg.msg_iov, uiov32, iov32size)) {
+ if (iovsize != 0) {
+ kmem_free(uiov32, iov32size);
+ kmem_free(uiov, iovsize);
+ }
+
+ return (set_errno(EFAULT));
+ }
+
+ count32 = 0;
+ for (i = 0; i < iovcnt; i++) {
+ ssize32_t iovlen32;
+
+ iovlen32 = uiov32[i].iov_len;
+ count32 += iovlen32;
+ if (iovlen32 < 0 || count32 < 0) {
+ if (iovsize != 0) {
+ kmem_free(uiov32, iov32size);
+ kmem_free(uiov, iovsize);
+ }
+
+ return (set_errno(EINVAL));
+ }
+
+ uiov[i].iov_len = iovlen32;
+ uiov[i].iov_base =
+ (caddr_t)(uintptr_t)uiov32[i].iov_base;
+ }
+ len = count32;
+
+ if (iovsize != 0) {
+ kmem_free(uiov32, iov32size);
+ }
+ } else
+#endif /* defined(_LP64) */
+ {
+ if (copyin(smsg.msg_iov, uiov,
+ iovcnt * sizeof (struct iovec)) != 0) {
+ if (iovsize != 0) {
+ kmem_free(uiov, iovsize);
+ }
+ return (set_errno(EFAULT));
+ }
+
+ len = 0;
+ for (i = 0; i < iovcnt; i++) {
+ ssize_t iovlen = uiov[i].iov_len;
+ len += iovlen;
+ if (iovlen < 0 || len < 0) {
+ if (iovsize != 0) {
+ kmem_free(uiov, iovsize);
+ }
+ return (set_errno(EINVAL));
+ }
+ }
+ }
+
+noiov:
+ /* Since the iovec is passed via the uio, NULL it out in the msg */
+ smsg.msg_iov = NULL;
+
+ xuio.xu_uio.uio_loffset = 0;
+ xuio.xu_uio.uio_iov = uiov;
+ xuio.xu_uio.uio_iovcnt = iovcnt;
+ xuio.xu_uio.uio_resid = len;
+ xuio.xu_uio.uio_segflg = UIO_USERSPACE;
+ xuio.xu_uio.uio_limit = 0;
+
+ res = lx_recv_common(sock, &smsg, &xuio, flags, namelenp, controllenp,
+ flagsp);
+
+ if (iovsize != 0) {
+ kmem_free(uiov, iovsize);
+ }
+
+ return (res);
+}
+
+long
+lx_recvmmsg(int sock, void *msg, uint_t vlen, int flags, timespec_t *timeoutp)
+{
+ hrtime_t deadline = 0;
+ uint_t rcvd = 0;
+ long ret = 0;
+ boolean_t waitforone;
+
+ waitforone = ((flags & LX_MSG_WAITFORONE) != 0);
+ flags &= ~LX_MSG_WAITFORONE;
+
+ /*
+ * We want to limit the work that a thread calling recvmmsg() can
+ * perform in the kernel so that it cannot accrue too high a priority.
+ * Artificially capping vlen means that the thread will return to
+ * userspace after processing at most IOV_MAX messages, giving the
+ * system a chance to reset the thread priority.
+ *
+ * Linux does not cap vlen here and recvmmsg() is expected to return
+ * once vlen messages have been received, a timeout occurs, or if an
+ * error is encountered; the artificial cap adds another case.
+ *
+ * It is possible that returning "early" in this emulation will
+ * cause problems with some applications however a properly written
+ * recvmmsg() consumer should consume only the received datagrams
+ * and try again if it wants more. This may need revisiting in the
+ * future.
+ */
+ if (vlen > IOV_MAX)
+ vlen = IOV_MAX;
+
+ if (timeoutp != NULL) {
+ timespec_t timeout;
+ uhrtime_t utime = (uhrtime_t)gethrtime();
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(timeoutp, &timeout, sizeof (timestruc_t)))
+ return (set_errno(EFAULT));
+ } else {
+ timestruc32_t timeout32;
+ if (copyin(timeoutp, &timeout32,
+ sizeof (timestruc32_t)))
+ return (set_errno(EFAULT));
+ timeout.tv_sec = (time_t)timeout32.tv_sec;
+ timeout.tv_nsec = timeout32.tv_nsec;
+ }
+
+ if (itimerspecfix(&timeout))
+ return (set_errno(EINVAL));
+
+ /*
+ * Make sure that deadline will not overflow. itimerspecfix()
+ * has already checked for negative values and too big a value
+ * in tv_nsec
+ */
+ if (timeout.tv_sec >= HRTIME_MAX / NANOSEC)
+ return (set_errno(EINVAL));
+
+ utime += timeout.tv_sec * NANOSEC;
+ utime += timeout.tv_nsec;
+
+ if (utime > HRTIME_MAX)
+ return (set_errno(EINVAL));
+
+ deadline = (hrtime_t)utime;
+ }
+
+ for (rcvd = 0; rcvd < vlen; rcvd++) {
+ uint_t *ptr;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ lx_mmsghdr_t *hdr = (lx_mmsghdr_t *)msg;
+ hdr += rcvd;
+ ret = lx_recvmsg(sock, (lx_msghdr_t *)hdr, flags);
+ ptr = &hdr->msg_len;
+ } else {
+ lx_mmsghdr32_t *hdr = (lx_mmsghdr32_t *)msg;
+ hdr += rcvd;
+ ret = lx_recvmsg(sock, (lx_msghdr32_t *)hdr, flags);
+ ptr = &hdr->msg_len;
+ }
+ if (ttolwp(curthread)->lwp_errno != 0)
+ break;
+ copyout(&ret, ptr, sizeof (*ptr));
+ /*
+ * If MSG_WAITFORONE is set, set MSG_DONTWAIT after the
+ * first packet has been received.
+ */
+ if (waitforone) {
+ flags |= LX_MSG_DONTWAIT;
+ waitforone = B_FALSE;
+ }
+ /*
+ * The Linux man page documents the timeout option as
+ * only being checked after each datagram is received.
+ * The man page does not document ETIMEDOUT as a return
+ * code so we do not set an errno.
+ */
+ if (deadline > 0 && gethrtime() >= deadline)
+ break;
+ }
+
+ if (rcvd > 0) {
+ /*
+ * Any error code is deliberately discarded if any message
+ * was successfully received.
+ */
+ ttolwp(curthread)->lwp_errno = 0;
+ return (rcvd);
+ }
+
+ return (ret);
+}
+
+/*
+ * Custom version of socket_sendmsg for error-handling overrides.
+ */
+static int
+lx_socket_sendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
+ cred_t *cr, boolean_t nosig)
+{
+ int error = 0;
+ ssize_t orig_resid = uiop->uio_resid;
+
+ /*
+ * Do not bypass the cache if we are doing a local (AF_UNIX) write.
+ */
+ if (so->so_family == AF_UNIX) {
+ uiop->uio_extflg |= UIO_COPY_CACHED;
+ } else {
+ uiop->uio_extflg &= ~UIO_COPY_CACHED;
+ }
+
+ error = SOP_SENDMSG(so, msg, uiop, cr);
+
+ switch (error) {
+ case EINTR:
+ case ENOMEM:
+ /* EAGAIN is EWOULDBLOCK */
+ case EWOULDBLOCK:
+ /* We did a partial send */
+ if (uiop->uio_resid != orig_resid) {
+ error = 0;
+ }
+ break;
+
+ case ENOTCONN:
+ /*
+ * The rules are different for non-blocking sockets which are
+ * still in the process of making a connection
+ */
+ if ((msg->msg_flags & MSG_DONTWAIT) != 0 ||
+ (uiop->uio_fmode & (FNONBLOCK|FNDELAY)) != 0) {
+ error = EAGAIN;
+ break;
+ }
+
+ /* Appease LTP and match behavior detailed in the man page */
+ error = EPIPE;
+ /* FALLTHROUGH */
+ case EPIPE:
+ if (nosig == B_FALSE) {
+ tsignal(curthread, SIGPIPE);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return (error);
+}
+
+static long
+lx_send_common(int sock, struct nmsghdr *msg, struct uio *uiop, int flags)
+{
+ struct sonode *so;
+ file_t *fp;
+ struct sockaddr *name = NULL;
+ socklen_t namelen;
+ void *control = NULL;
+ socklen_t controllen;
+ ssize_t len = 0;
+ int error;
+ boolean_t nosig;
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ return (set_errno(error));
+ }
+
+ uiop->uio_fmode = fp->f_flag;
+
+ /* Allocate and copyin name and control */
+ if (msg->msg_name != NULL && msg->msg_namelen != 0) {
+ ASSERT(MUTEX_NOT_HELD(&so->so_lock));
+
+ error = ltos_sockaddr_copyin((struct sockaddr *)msg->msg_name,
+ msg->msg_namelen, &name, &namelen, NULL);
+ if (error != 0) {
+ goto done;
+ }
+ /* copyin_name null terminates addresses for AF_UNIX */
+ msg->msg_namelen = namelen;
+ msg->msg_name = name;
+ } else {
+ msg->msg_name = name = NULL;
+ msg->msg_namelen = namelen = 0;
+ }
+
+ if (msg->msg_control != NULL && msg->msg_controllen != 0) {
+ /*
+ * Verify that the length is not excessive to prevent
+ * an application from consuming all of kernel memory.
+ */
+ if (msg->msg_controllen > SO_MAXARGSIZE) {
+ error = EINVAL;
+ goto done;
+ }
+ if ((error = ltos_cmsgs_copyin(msg->msg_control,
+ msg->msg_controllen, &control, &controllen)) != 0) {
+ goto done;
+ }
+ msg->msg_control = control;
+ msg->msg_controllen = controllen;
+ } else {
+ msg->msg_control = control = NULL;
+ msg->msg_controllen = controllen = 0;
+ }
+
+ len = uiop->uio_resid;
+ msg->msg_flags = lx_xlate_sock_flags(flags, LX_TO_SUNOS);
+ /* Default to XPG4.2 operation */
+ msg->msg_flags |= MSG_XPG4_2;
+ nosig = ((flags & LX_MSG_NOSIGNAL) != 0);
+
+ error = lx_socket_sendmsg(so, msg, uiop, CRED(), nosig);
+ if (error == EINTR)
+ lx_sock_syscall_restart(so, B_FALSE);
+done:
+ if (control != NULL) {
+ kmem_free(control, controllen);
+ }
+ if (name != NULL) {
+ kmem_free(name, namelen);
+ }
+ if (error != 0) {
+ releasef(sock);
+ return (set_errno(error));
+ }
+ lwp_stat_update(LWP_STAT_MSGSND, 1);
+ releasef(sock);
+ return (len - uiop->uio_resid);
+}
+
+/*
+ * For both send and sendto Linux evaluates errors in a different order than
+ * we do internally. Specifically it will check the buffer address before
+ * checking if the socket is connected. This can lead to a different errno on
+ * us vs. Linux (seen with LTP) but we don't bother to emulate this.
+ */
+long
+lx_send(int sock, void *buffer, size_t len, int flags)
+{
+ struct nmsghdr smsg;
+ struct uio auio;
+ struct iovec aiov[1];
+
+ if ((ssize_t)len < 0) {
+ /* Keep len reasonably limited (see lx_recv) */
+ return (set_errno(EINVAL));
+ }
+
+ aiov[0].iov_base = buffer;
+ aiov[0].iov_len = len;
+ auio.uio_loffset = 0;
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = len;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_limit = 0;
+
+ smsg.msg_name = NULL;
+ smsg.msg_control = NULL;
+ return (lx_send_common(sock, &smsg, &auio, flags));
+}
+
+long
+lx_sendto(int sock, void *buffer, size_t len, int flags,
+ struct sockaddr *dstaddr, socklen_t addrlen)
+{
+ struct nmsghdr smsg;
+ struct uio auio;
+ struct iovec aiov[1];
+
+ if ((ssize_t)len < 0) {
+ /* Keep len reasonably limited (see lx_recv) */
+ return (set_errno(EINVAL));
+ }
+
+ aiov[0].iov_base = buffer;
+ aiov[0].iov_len = len;
+ auio.uio_loffset = 0;
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = len;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_limit = 0;
+
+ smsg.msg_name = (char *)dstaddr;
+ smsg.msg_namelen = addrlen;
+ smsg.msg_control = NULL;
+ return (lx_send_common(sock, &smsg, &auio, flags));
+}
+
+long
+lx_sendmsg(int sock, void *msg, int flags)
+{
+ struct nmsghdr smsg;
+ struct uio auio;
+ struct iovec buf[IOV_MAX_STACK], *aiov;
+ int i, iovcnt, iovsize;
+ long res;
+ ssize_t len = 0;
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_msghdr32_t lmsg32;
+ if (copyin(msg, &lmsg32, sizeof (lmsg32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ smsg.msg_name = (void *)(uintptr_t)lmsg32.msg_name;
+ smsg.msg_namelen = lmsg32.msg_namelen;
+ smsg.msg_iov = (struct iovec *)(uintptr_t)lmsg32.msg_iov;
+ smsg.msg_iovlen = lmsg32.msg_iovlen;
+ smsg.msg_control = (void *)(uintptr_t)lmsg32.msg_control;
+ smsg.msg_controllen = lmsg32.msg_controllen;
+ smsg.msg_flags = lmsg32.msg_flags;
+ } else
+#endif /* defined(_LP64) */
+ {
+ lx_msghdr_t lmsg;
+ if (copyin(msg, &lmsg, sizeof (lmsg)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ smsg.msg_name = lmsg.msg_name;
+ smsg.msg_namelen = lmsg.msg_namelen;
+ smsg.msg_iov = lmsg.msg_iov;
+ smsg.msg_iovlen = lmsg.msg_iovlen;
+ smsg.msg_control = lmsg.msg_control;
+ smsg.msg_controllen = lmsg.msg_controllen;
+ smsg.msg_flags = lmsg.msg_flags;
+ }
+
+ iovcnt = smsg.msg_iovlen;
+ if (iovcnt <= 0 || iovcnt > IOV_MAX) {
+ return (set_errno(EMSGSIZE));
+ }
+ if (iovcnt > IOV_MAX_STACK) {
+ iovsize = iovcnt * sizeof (struct iovec);
+ aiov = kmem_alloc(iovsize, KM_SLEEP);
+ } else {
+ iovsize = 0;
+ aiov = buf;
+ }
+
+#if defined(_LP64)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ /* convert from 32bit iovec structs */
+ struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+ ssize_t iov32size;
+ ssize32_t count32;
+
+ iov32size = iovcnt * sizeof (struct iovec32);
+ if (iovsize != 0) {
+ aiov32 = kmem_alloc(iov32size, KM_SLEEP);
+ }
+
+ if (copyin((struct iovec32 *)smsg.msg_iov, aiov32, iov32size)) {
+ if (iovsize != 0) {
+ kmem_free(aiov32, iov32size);
+ kmem_free(aiov, iovsize);
+ }
+
+ return (set_errno(EFAULT));
+ }
+
+ count32 = 0;
+ for (i = 0; i < iovcnt; i++) {
+ ssize32_t iovlen32;
+
+ iovlen32 = aiov32[i].iov_len;
+ count32 += iovlen32;
+ if (iovlen32 < 0 || count32 < 0) {
+ if (iovsize != 0) {
+ kmem_free(aiov32, iov32size);
+ kmem_free(aiov, iovsize);
+ }
+
+ return (set_errno(EINVAL));
+ }
+
+ aiov[i].iov_len = iovlen32;
+ aiov[i].iov_base =
+ (caddr_t)(uintptr_t)aiov32[i].iov_base;
+ }
+ len = count32;
+
+ if (iovsize != 0) {
+ kmem_free(aiov32, iov32size);
+ }
+ } else
+#endif /* defined(_LP64) */
+ {
+ if (copyin(smsg.msg_iov, aiov,
+ iovcnt * sizeof (struct iovec)) != 0) {
+ if (iovsize != 0) {
+ kmem_free(aiov, iovsize);
+ }
+ return (set_errno(EFAULT));
+ }
+
+ len = 0;
+ for (i = 0; i < iovcnt; i++) {
+ ssize_t iovlen = aiov[i].iov_len;
+
+ len += iovlen;
+ if (iovlen < 0 || len < 0) {
+ if (iovsize != 0) {
+ kmem_free(aiov, iovsize);
+ }
+ return (set_errno(EINVAL));
+ }
+ }
+ }
+ /* Since the iovec is passed via the uio, NULL it out in the msg */
+ smsg.msg_iov = NULL;
+
+ auio.uio_loffset = 0;
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_resid = len;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_limit = 0;
+
+ res = lx_send_common(sock, &smsg, &auio, flags);
+
+ if (iovsize != 0) {
+ kmem_free(aiov, iovsize);
+ }
+
+ return (res);
+}
+
+long
+lx_sendmmsg(int sock, void *msg, uint_t vlen, int flags)
+{
+ long ret = 0;
+ uint_t sent = 0;
+
+ /*
+ * Linux caps vlen to UIO_MAXIOV (1024).
+ */
+ if (vlen > IOV_MAX)
+ vlen = IOV_MAX;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ lx_mmsghdr_t *hdr = msg;
+
+ for (sent = 0; sent < vlen; sent++, hdr++) {
+ ret = lx_sendmsg(sock, (lx_msghdr_t *)hdr, flags);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ break;
+ copyout(&ret, &hdr->msg_len, sizeof (hdr->msg_len));
+ }
+ } else {
+ lx_mmsghdr32_t *hdr = msg;
+
+ for (sent = 0; sent < vlen; sent++, hdr++) {
+ ret = lx_sendmsg(sock, (lx_msghdr32_t *)hdr, flags);
+ if (ttolwp(curthread)->lwp_errno != 0)
+ break;
+ copyout(&ret, &hdr->msg_len, sizeof (hdr->msg_len));
+ }
+ }
+
+ if (sent > 0) {
+ /*
+ * Any error code is deliberately discarded if any message
+ * was successfully sent.
+ */
+ ttolwp(curthread)->lwp_errno = 0;
+ return (sent);
+ }
+
+ return (ret);
+}
+
+/*
+ * Linux socket option type definitions
+ *
+ * The protocol `levels` are well defined (see in.h) The option values are
+ * not so well defined. Linux often uses different values vs. Illumos
+ * although they mean the same thing. For example, IP_TOS in Linux is
+ * defined as value 1 but in Illumos it is defined as value 3. This table
+ * maps all the Protocol levels to their options and maps them between
+ * Linux and Illumos and vice versa. Hence the reason for the complexity.
+ *
+ * For a certain subset of sockopts, Linux will implicitly truncate optval
+ * input, so long as optlen meets a minimum size. Because illumos is strict
+ * about optlen, we must cap optlen for those options.
+ */
+
+typedef struct lx_sockopt_map {
+ const int lsm_opt; /* Illumos-native equivalent */
+ const int lsm_lcap; /* Cap optlen to this size. (Ignored if 0) */
+} lx_sockopt_map_t;
+
+typedef struct lx_proto_opts {
+ const lx_sockopt_map_t *lpo_entries; /* Linux->SunOS map entries */
+ unsigned int lpo_max; /* max entries in table */
+} lx_proto_opts_t;
+
+#define OPTNOTSUP -1 /* we don't support it */
+
+#define PROTO_SOCKOPTS(opts) \
+ { (opts), sizeof ((opts)) / sizeof ((opts)[0]) }
+
+/* Shorten name so the columns can line up */
+#define IP_MREQ_SZ sizeof (struct ip_mreq)
+
+static const lx_sockopt_map_t ltos_ip_sockopts[LX_IP_UNICAST_IF + 1] = {
+ { OPTNOTSUP, 0 },
+ { IP_TOS, sizeof (int) }, /* IP_TOS */
+ { IP_TTL, sizeof (int) }, /* IP_TTL */
+ { IP_HDRINCL, sizeof (int) }, /* IP_HDRINCL */
+ { IP_OPTIONS, 0 }, /* IP_OPTIONS */
+ { OPTNOTSUP, 0 }, /* IP_ROUTER_ALERT */
+ { IP_RECVOPTS, sizeof (int) }, /* IP_RECVOPTS */
+ { IP_RETOPTS, sizeof (int) }, /* IP_RETOPTS */
+ { IP_PKTINFO, sizeof (int) }, /* IP_PKTINFO */
+ { OPTNOTSUP, 0 }, /* IP_PKTOPTIONS */
+ { OPTNOTSUP, 0 }, /* IP_MTUDISCOVER */
+ { OPTNOTSUP, 0 }, /* IP_RECVERR */
+ { IP_RECVTTL, sizeof (int) }, /* IP_RECVTTL */
+ { IP_RECVTOS, sizeof (int) }, /* IP_RECVTOS */
+ { OPTNOTSUP, 0 }, /* IP_MTU */
+ { OPTNOTSUP, 0 }, /* IP_FREEBIND */
+ { OPTNOTSUP, 0 }, /* IP_IPSEC_POLICY */
+ { OPTNOTSUP, 0 }, /* IP_XFRM_POLICY */
+ { OPTNOTSUP, 0 }, /* IP_PASSSEC */
+ { OPTNOTSUP, 0 }, /* IP_TRANSPARENT */
+ { OPTNOTSUP, 0 }, /* IP_ORIGDSTADDR */
+ { OPTNOTSUP, 0 }, /* IP_MINTTL */
+ { OPTNOTSUP, 0 }, /* IP_NODEFRAG */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IP_MULTICAST_IF, sizeof (int) }, /* IP_MULTICAST_IF */
+ { IP_MULTICAST_TTL, sizeof (int) }, /* IP_MULTICAST_TTL */
+ { IP_MULTICAST_LOOP, sizeof (int) }, /* IP_MULTICAST_LOOP */
+ { IP_ADD_MEMBERSHIP, IP_MREQ_SZ }, /* IP_ADD_MEMBERSHIP */
+ { IP_DROP_MEMBERSHIP, IP_MREQ_SZ }, /* IP_DROP_MEMBERSHIP */
+ { IP_UNBLOCK_SOURCE, 0 }, /* IP_UNBLOCK_SOURCE */
+ { IP_BLOCK_SOURCE, 0 }, /* IP_BLOCK_SOURCE */
+ { IP_ADD_SOURCE_MEMBERSHIP, 0 }, /* IP_ADD_SOURCE_MEMBERSHIP */
+ { OPTNOTSUP, 0 }, /* IP_DROP_SOURCE_MEMBERSHIP */
+ { OPTNOTSUP, 0 }, /* IP_MSFILTER */
+ { MCAST_JOIN_GROUP, 0 }, /* MCAST_JOIN_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_BLOCK_SOURCE */
+ { OPTNOTSUP, 0 }, /* MCAST_UNBLOCK_SOURCE */
+ { MCAST_LEAVE_GROUP, 0 }, /* MCAST_LEAVE_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_JOIN_SOURCE_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_LEAVE_SOURCE_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_MSFILTER */
+ { OPTNOTSUP, 0 }, /* IP_MULTICAST_ALL */
+ { OPTNOTSUP, 0 } /* IP_UNICAST_IF */
+};
+
+/* Shorten name so the columns can line up */
+#define IP6_MREQ_SZ sizeof (struct ipv6_mreq)
+
+static const lx_sockopt_map_t ltos_ipv6_sockopts[LX_IPV6_TCLASS + 1] = {
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 }, /* IPV6_ADDRFORM */
+ { OPTNOTSUP, 0 }, /* IPV6_2292PKTINFO */
+ { OPTNOTSUP, 0 }, /* IPV6_2292HOPOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_2292DSTOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_2292RTHDR */
+ { OPTNOTSUP, 0 }, /* IPV6_2292PKTOPTIONS */
+ { IPV6_CHECKSUM, sizeof (int) }, /* IPV6_CHECKSUM */
+ { OPTNOTSUP, 0 }, /* IPV6_2292HOPLIMIT */
+ { OPTNOTSUP, 0 }, /* IPV6_NEXTHOP */
+ { OPTNOTSUP, 0 }, /* IPV6_AUTHHDR */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IPV6_UNICAST_HOPS, sizeof (int) }, /* IPV6_UNICAST_HOPS */
+ { IPV6_MULTICAST_IF, sizeof (int) }, /* IPV6_MULTICAST_IF */
+ { IPV6_MULTICAST_HOPS, sizeof (int) }, /* IPV6_MULTICAST_HOPS */
+ { IPV6_MULTICAST_LOOP, sizeof (int) }, /* IPV6_MULTICAST_LOOP */
+ { IPV6_ADD_MEMBERSHIP, IP6_MREQ_SZ }, /* IPV6_JOIN_GROUP */
+ { IPV6_DROP_MEMBERSHIP, IP6_MREQ_SZ }, /* IPV6_LEAVE_GROUP */
+ { OPTNOTSUP, 0 }, /* IPV6_ROUTER_ALERT */
+ { OPTNOTSUP, 0 }, /* IPV6_MTU_DISCOVER */
+ { OPTNOTSUP, 0 }, /* IPV6_MTU */
+ { OPTNOTSUP, 0 }, /* IPV6_RECVERR */
+ { IPV6_V6ONLY, sizeof (int) }, /* IPV6_V6ONLY */
+ { OPTNOTSUP, 0 }, /* IPV6_JOIN_ANYCAST */
+ { OPTNOTSUP, 0 }, /* IPV6_LEAVE_ANYCAST */
+ { OPTNOTSUP, 0 }, /* IPV6_IPSEC_POLICY */
+ { OPTNOTSUP, 0 }, /* IPV6_XFRM_POLICY */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { MCAST_JOIN_GROUP, 0 }, /* MCAST_JOIN_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_BLOCK_SOURCE */
+ { OPTNOTSUP, 0 }, /* MCAST_UNBLOCK_SOURCE */
+ { MCAST_LEAVE_GROUP, 0 }, /* MCAST_LEAVE_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_JOIN_SOURCE_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_LEAVE_SOURCE_GROUP */
+ { OPTNOTSUP, 0 }, /* MCAST_MSFILTER */
+ { IPV6_RECVPKTINFO, sizeof (int) }, /* IPV6_RECVPKTINFO */
+ { IPV6_PKTINFO, 0 }, /* IPV6_PKTINFO */
+ { IPV6_RECVHOPLIMIT, sizeof (int) }, /* IPV6_RECVHOPLIMIT */
+ { IPV6_HOPLIMIT, 0 }, /* IPV6_HOPLIMIT */
+ { OPTNOTSUP, 0 }, /* IPV6_RECVHOPOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_HOPOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_RTHDRDSTOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_RECVRTHDR */
+ { OPTNOTSUP, 0 }, /* IPV6_RTHDR */
+ { OPTNOTSUP, 0 }, /* IPV6_RECVDSTOPTS */
+ { OPTNOTSUP, 0 }, /* IPV6_DSTOPTS */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IPV6_RECVTCLASS, sizeof (int) }, /* IPV6_RECVTCLASS */
+ { IPV6_TCLASS, sizeof (int) } /* IPV6_TCLASS */
+};
+
+static const lx_sockopt_map_t ltos_icmpv6_sockopts[LX_ICMP6_FILTER + 1] = {
+ { OPTNOTSUP, 0 },
+ { ICMP6_FILTER, 0 } /* ICMP6_FILTER */
+};
+
+/*
+ * Options marked as "in code" in their comment are handled in the
+ * lx_setsockopt_tcp() and lx_getsockopt_tcp() functions.
+ *
+ * For the Linux TCP_SYNCNT option (the number of SYN retransmits) we emulate
+ * that by interpreting the two connection interval settings:
+ * TCP_CONN_NOTIFY_THRESHOLD
+ * tcp_first_ctimer_threshold = tcps->tcps_ip_notify_cinterval
+ * TCP_CONN_ABORT_THRESHOLD
+ * tcp_second_ctimer_threshold = tcps->tcps_ip_abort_cinterval
+ * The system (re)transmits a SYN and performs a doubling backoff from the
+ * first timer until it passes the second timer. We determine the SYN count
+ * from these two values. Normally it will be 5. Also see the TCPS_SYN_SENT
+ * case in tcp_timer(); a tcp_second_ctimer_threshold value of 0 means to
+ * retransmit SYN indefinitely.
+ *
+ * For the Linux TCP_USER_TIMEOUT option we use our TCP_ABORT_THRESHOLD since
+ * this seems to be the closest match. This value is the
+ * tcp_second_timer_threshold, which gets initialized to the
+ * tcp_ip_abort_interval value. The tunable guide describes this as:
+ * For a given TCP connection, if TCP has been retransmitting for
+ * tcp_ip_abort_interval period of time and it has not received any
+ * acknowledgment from the other endpoint during this period, TCP closes
+ * this connection.
+ * The value is in milliseconds, which matches TCP_USER_TIMEOUT.
+ */
+static const lx_sockopt_map_t ltos_tcp_sockopts[LX_TCP_NOTSENT_LOWAT + 1] = {
+ { OPTNOTSUP, 0 },
+ { TCP_NODELAY, sizeof (int) }, /* TCP_NODELAY */
+ { TCP_MAXSEG, sizeof (int) }, /* TCP_MAXSEG - in code */
+ { TCP_CORK, sizeof (int) }, /* TCP_CORK */
+ { TCP_KEEPIDLE, sizeof (int) }, /* TCP_KEEPIDLE */
+ { TCP_KEEPINTVL, sizeof (int) }, /* TCP_KEEPINTVL */
+ { TCP_KEEPCNT, sizeof (int) }, /* TCP_KEEPCNT */
+ { OPTNOTSUP, 0 }, /* TCP_SYNCNT - in code */
+ { TCP_LINGER2, sizeof (int) }, /* TCP_LINGER2 */
+ { OPTNOTSUP, 0 }, /* TCP_DEFER_ACCEPT - in code */
+ { OPTNOTSUP, 0 }, /* TCP_WINDOW_CLAMP - in code */
+ { OPTNOTSUP, 0 }, /* TCP_INFO */
+ { TCP_QUICKACK, sizeof (int) }, /* TCP_QUICKACK */
+ { TCP_CONGESTION, CC_ALGO_NAME_MAX }, /* TCP_CONGESTION */
+ { OPTNOTSUP, 0 }, /* TCP_MD5SIG */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 }, /* TCP_THIN_LINEAR_TIMEOUTS */
+ { OPTNOTSUP, 0 }, /* TCP_THIN_DUPACK */
+ { TCP_ABORT_THRESHOLD, sizeof (int) }, /* TCP_USER_TIMEOUT */
+ { OPTNOTSUP, 0 }, /* TCP_REPAIR */
+ { OPTNOTSUP, 0 }, /* TCP_REPAIR_QUEUE */
+ { OPTNOTSUP, 0 }, /* TCP_QUEUE_SEQ */
+ { OPTNOTSUP, 0 }, /* TCP_REPAIR_OPTIONS */
+ { OPTNOTSUP, 0 }, /* TCP_FASTOPEN */
+ { OPTNOTSUP, 0 }, /* TCP_TIMESTAMP */
+ { OPTNOTSUP, 0 } /* TCP_NOTSENT_LOWAT */
+};
+
+static const lx_sockopt_map_t ltos_igmp_sockopts[IGMP_MTRACE + 1] = {
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IGMP_MINLEN, 0 }, /* IGMP_MINLEN */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IGMP_MEMBERSHIP_QUERY, 0 }, /* IGMP_HOST_MEMBERSHIP_QUERY */
+ { IGMP_V1_MEMBERSHIP_REPORT, 0 }, /* IGMP_HOST_MEMBERSHIP_REPORT */
+ { IGMP_DVMRP, 0 }, /* IGMP_DVMRP */
+ { IGMP_PIM, 0 }, /* IGMP_PIM */
+ { OPTNOTSUP, 0 }, /* IGMP_TRACE */
+ { IGMP_V2_MEMBERSHIP_REPORT, 0 }, /* IGMPV2_HOST_MEMBERSHIP_REPORT */
+ { IGMP_V2_LEAVE_GROUP, 0 }, /* IGMP_HOST_LEAVE_MESSAGE */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 },
+ { IGMP_MTRACE_RESP, 0 }, /* IGMP_MTRACE_RESP */
+ { IGMP_MTRACE, 0 } /* IGMP_MTRACE */
+};
+
+static const lx_sockopt_map_t ltos_socket_sockopts[LX_SO_BPF_EXTENSIONS + 1] = {
+ { OPTNOTSUP, 0 },
+ { SO_DEBUG, sizeof (int) }, /* SO_DEBUG */
+ { SO_REUSEADDR, sizeof (int) }, /* SO_REUSEADDR */
+ { SO_TYPE, 0 }, /* SO_TYPE */
+ { SO_ERROR, 0 }, /* SO_ERROR */
+ { SO_DONTROUTE, sizeof (int) }, /* SO_DONTROUTE */
+ { SO_BROADCAST, sizeof (int) }, /* SO_BROADCAST */
+ { SO_SNDBUF, sizeof (int) }, /* SO_SNDBUF */
+ { SO_RCVBUF, sizeof (int) }, /* SO_RCVBUF */
+ { SO_KEEPALIVE, sizeof (int) }, /* SO_KEEPALIVE */
+ { SO_OOBINLINE, sizeof (int) }, /* SO_OOBINLINE */
+ { OPTNOTSUP, 0 }, /* SO_NO_CHECK */
+ { OPTNOTSUP, 0 }, /* SO_PRIORITY */
+ { SO_LINGER, 0 }, /* SO_LINGER */
+ { OPTNOTSUP, 0 }, /* SO_BSDCOMPAT */
+ { SO_REUSEPORT, sizeof (int) }, /* SO_REUSEPORT */
+ { SO_RECVUCRED, sizeof (int) }, /* SO_PASSCRED */
+ { OPTNOTSUP, 0 }, /* SO_PEERCRED */
+ { SO_RCVLOWAT, sizeof (int) }, /* SO_RCVLOWAT */
+ { SO_SNDLOWAT, sizeof (int) }, /* SO_SNDLOWAT */
+ { SO_RCVTIMEO, 0 }, /* SO_RCVTIMEO */
+ { SO_SNDTIMEO, 0 }, /* SO_SNDTIMEO */
+ { OPTNOTSUP, 0 }, /* SO_SECURITY_AUTHENTICATION */
+ { OPTNOTSUP, 0 }, /* SO_SECURITY_ENCRYPTION_TRANSPORT */
+ { OPTNOTSUP, 0 }, /* SO_SECURITY_ENCRYPTION_NETWORK */
+ { OPTNOTSUP, 0 }, /* SO_BINDTODEVICE */
+ { SO_ATTACH_FILTER, 0 }, /* SO_ATTACH_FILTER */
+ { SO_DETACH_FILTER, 0 }, /* SO_DETACH_FILTER */
+ { OPTNOTSUP, 0 }, /* SO_PEERNAME */
+ { SO_TIMESTAMP, sizeof (int) }, /* SO_TIMESTAMP */
+ { SO_ACCEPTCONN, 0 }, /* SO_ACCEPTCONN */
+ { OPTNOTSUP, 0 }, /* SO_PEERSEC */
+ { SO_SNDBUF, sizeof (int) }, /* SO_SNDBUFFORCE */
+ { SO_RCVBUF, sizeof (int) }, /* SO_RCVBUFFORCE */
+ { OPTNOTSUP, 0 }, /* SO_PASSSEC */
+ { OPTNOTSUP, 0 }, /* SO_TIMESTAMPNS */
+ { OPTNOTSUP, 0 }, /* SO_MARK */
+ { OPTNOTSUP, 0 }, /* SO_TIMESTAMPING */
+ { SO_PROTOTYPE, 0 }, /* SO_PROTOCOL */
+ { SO_DOMAIN, 0 }, /* SO_DOMAIN */
+ { OPTNOTSUP, 0 }, /* SO_RXQ_OVFL */
+ { OPTNOTSUP, 0 }, /* SO_WIFI_STATUS */
+ { OPTNOTSUP, 0 }, /* SO_PEEK_OFF */
+ { OPTNOTSUP, 0 }, /* SO_NOFCS */
+ { OPTNOTSUP, 0 }, /* SO_LOCK_FILTER */
+ { OPTNOTSUP, 0 }, /* SO_SELECT_ERR_QUEUE */
+ { OPTNOTSUP, 0 }, /* SO_BUSY_POLL */
+ { OPTNOTSUP, 0 }, /* SO_MAX_PACING_RATE */
+ { OPTNOTSUP, 0 } /* SO_BPF_EXTENSIONS */
+};
+
+static const lx_sockopt_map_t ltos_raw_sockopts[LX_ICMP_FILTER + 1] = {
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 } /* ICMP_FILTER */
+};
+
+static const lx_sockopt_map_t ltos_packet_sockopts[LX_PACKET_STATISTICS + 1] = {
+ { OPTNOTSUP, 0 },
+ { PACKET_ADD_MEMBERSHIP, 0 }, /* PACKET_ADD_MEMBERSHIP */
+ { PACKET_DROP_MEMBERSHIP, 0 }, /* PACKET_DROP_MEMBERSHIP */
+ { OPTNOTSUP, 0 }, /* PACKET_RECV_OUTPUT */
+ { OPTNOTSUP, 0 },
+ { OPTNOTSUP, 0 }, /* PACKET_RX_RING */
+ { PACKET_STATISTICS, 0 } /* PACKET_STATISTICS */
+};
+
+/* Needed for SO_ATTACH_FILTER */
+struct lx_bpf_program {
+ unsigned short bf_len;
+ caddr_t bf_insns;
+};
+
+/* Invert filter fields as Linux expects */
+#define LX_ICMP6_FILTER_INVERT(filterp) ( \
+ ((filterp)->__icmp6_filt[0] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[1] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[2] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[3] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[4] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[5] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[6] ^= 0xFFFFFFFFU), \
+ ((filterp)->__icmp6_filt[7] ^= 0xFFFFFFFFU))
+
+static boolean_t
+lx_sockopt_lookup(lx_proto_opts_t tbl, int *optname, socklen_t *optlen)
+{
+ const lx_sockopt_map_t *entry;
+
+ if (*optname > tbl.lpo_max) {
+ return (B_FALSE);
+ }
+ entry = &tbl.lpo_entries[*optname];
+ if (entry->lsm_opt == OPTNOTSUP) {
+ return (B_FALSE);
+ }
+ *optname = entry->lsm_opt;
+ /* Truncate the optlen if needed/allowed */
+ if (entry->lsm_lcap != 0 && *optlen > entry->lsm_lcap) {
+ *optlen = entry->lsm_lcap;
+ }
+ return (B_TRUE);
+}
+
+static int
+lx_mcast_common(sonode_t *so, int level, int optname, void *optval,
+ socklen_t optlen)
+{
+ int error;
+ struct group_req gr;
+ lx_sockaddr_storage_t *lxss;
+
+ ASSERT(optname == LX_MCAST_JOIN_GROUP ||
+ optname == LX_MCAST_LEAVE_GROUP);
+
+ /*
+ * For MCAST_JOIN_GROUP and MCAST_LEAVE_GROUP, Linux uses a
+ * gr_group that has a different size from the native gr_group.
+ * We need to translate to the native gr_group taking special
+ * care to do the right thing when dealing with a 32-bit program
+ * making a call into a 64-bit kernel.
+ */
+
+ bzero(&gr, sizeof (gr));
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ if (optlen != sizeof (lx_group_req32_t)) {
+ return (EINVAL);
+ }
+
+ lx_group_req32_t *lxgr = optval;
+
+ /* use the 32-bit type */
+ gr.gr_interface = lxgr->lxgr_interface;
+ lxss = &lxgr->lxgr_group;
+ } else
+#endif /* defined(_SYSCALL32_IMPL) */
+ {
+ if (optlen != sizeof (lx_group_req_t)) {
+ return (EINVAL);
+ }
+
+ lx_group_req_t *lxgr = optval;
+
+ gr.gr_interface = lxgr->lxgr_interface;
+ lxss = &lxgr->lxgr_group;
+ }
+
+ bcopy(lxss, &gr.gr_group, sizeof (*lxss));
+ gr.gr_group.ss_family = LTOS_FAMILY(lxss->lxss_family);
+
+ optlen = sizeof (gr);
+ optname = (optname == LX_MCAST_JOIN_GROUP) ?
+ MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
+
+ error = socket_setsockopt(so, level, optname, &gr,
+ optlen, CRED());
+ return (error);
+}
+
+
+/*
+ * NOTE: For now, the following mess applies to TCP (i.e. AF_INET{,6} +
+ * SOCK_STREAM) only, until we enable SO_REUSEPORT for other socket/protocol
+ * types as well. The lx_so_needs_reusehandling() macro indicates what
+ * socket(s) apply to the following mess.
+ */
+#define lx_so_needs_reusehandling(so) ((so)->so_type == SOCK_STREAM && \
+ ((so)->so_family == AF_INET || (so)->so_family == AF_INET6))
+
+/*
+ * So in Linux, the SO_REUSEADDR includes, essentially, SO_REUSEPORT as part
+ * of its functionality. Experiments on CentOS 7 with a 3.10-ish kernel show
+ * that querying on SO_REUSEPORT show it's "off" if SO_REUSEADDR gets set.
+ * This means we can't count on directly querying the native socket state. We
+ * munge things here in LX-land to essentially turn on both REUSEADDR and
+ * REUSEPORT in native conn_t state for LX processes that set SO_REUSEADDR.
+ *
+ * We also keep track if the wily Linux app sends BOTH REUSEADDR and REUSEPORT
+ * down. We can return that both are on, or if it uses just REUSEADDR, we
+ * don't return yes for a check of REUSEPORT. This means our conn_t state may
+ * be different than what an LX process will see. "REUSEPORT" for LX may be
+ * off, but internally it will be on.
+ *
+ * BEGIN CSTYLED
+ * State table for internal conn_reuse{addr,port}:
+ *
+ * LX ADDR,PORT Int. ADDR,PORT New ADDR New LX New Int. LXchg? Intchg?
+ * ============ ============== ======== ====== ======== ====== =======
+ *
+ * off,off off,off off off,off off,off NO NO
+ *
+ * off,off off,off on on,off on,on YES YES(2)
+ *
+ * off,on off,on off off,on off,on NO NO
+ *
+ * off,on off,on on on,on on,on YES YES
+ *
+ * on,off on,on off off,off off,off YES YES(2)
+ *
+ * on,off on,on on on,off on,on NO NO
+ *
+ * on,on on,on off off,on off,on YES YES
+ *
+ * on,on on,on on on,on on,on NO NO
+ *
+ *
+ * LX ADDR,PORT Int. ADDR,PORT New PORT New LX New Int. LXchg? Intchg?
+ * ============ ============== ======== ====== ======== ====== =======
+ *
+ * off,off off,off off off,off off,off NO NO
+ *
+ * off,off off,off on off,on off,on YES YES
+ *
+ * off,on off,on off off,off off,off YES YES
+ *
+ * off,on off,on on off,on off,on NO NO
+ *
+ * on,off on,on off on,off on,on NO NO
+ *
+ * on,off on,on on on,on on,on YES NO
+ *
+ * on,on on,on off on,off on,on YES NO
+ *
+ * on,on on,on on on,on on,on NO NO
+ *
+ * END CSTYLED
+ *
+ * For setting these options, we need to obey the state table above.
+ * For getting REUSEADDR, the native stack handles it already.
+ * For getting REUSEPORT, we'll have to track the auxiliary data's flags.
+ */
+static int
+lx_set_reuse_handler(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ lx_socket_aux_data_t *sad;
+ boolean_t enable;
+ int error;
+
+ if (optlen != sizeof (int))
+ return (EINVAL);
+ enable = (*((int *)optval) != 0);
+
+ ASSERT(optname == LX_SO_REUSEADDR || optname == LX_SO_REUSEPORT);
+ sad = lx_sad_acquire(SOTOV(so));
+
+ /*
+ * lx_sad_acquire() holds its mutex for us. This protects us
+ * against racing option-setters on the same socket.
+ */
+ if (optname == LX_SO_REUSEADDR) {
+ /* Check if already set to what we want! */
+ if (enable ==
+ ((sad->lxsad_flags & LXSAD_FL_EMULRUADDR) != 0)) {
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+ }
+
+ /*
+ * At this point, we know we need to change SO_REUSEADDR,
+ * Linux-style. We know these are supported options too,
+ * so we don't bother with any lookup.
+ */
+ error = socket_setsockopt(so, SOL_SOCKET, SO_REUSEADDR,
+ optval, optlen, CRED());
+ if (error != 0) {
+ mutex_exit(&sad->lxsad_lock);
+ return (error);
+ }
+ if (enable)
+ sad->lxsad_flags |= LXSAD_FL_EMULRUADDR;
+ else
+ sad->lxsad_flags &= ~LXSAD_FL_EMULRUADDR;
+
+ /*
+ * At THIS point, we need to figure out if we ALSO need to
+ * toggle the native-side SO_REUSEPORT state because Linux's
+ * SO_REUSEADDR ALSO include the moral equivalent of
+ * SO_REUSEPORT. There may be further subtleties, but for now
+ * assume a Linux app that uses SO_REUSEADDR wants that
+ * SO_REUSEPORT functionality thrown in for free.
+ *
+ * Check for SO_REUSEPORT already enabled first.
+ */
+ if ((sad->lxsad_flags & LXSAD_FL_EMULRUPORT) != 0) {
+ /* Someone turned on REUSEPORT first, we're good. */
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+ }
+
+ /*
+ * Fall through to REUSEPORT setting, it'll know it's a
+ * supplement based on (optname == SO_REUSEADDR).
+ */
+ } else if (enable ==
+ ((sad->lxsad_flags & LXSAD_FL_EMULRUPORT) != 0)) {
+ /*
+ * If we reach here, we're setting REUSEPORT to what it's
+ * already set.
+ */
+ ASSERT3U(optname, ==, LX_SO_REUSEPORT);
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+ }
+
+ if (optname == LX_SO_REUSEPORT &&
+ ((sad->lxsad_flags & LXSAD_FL_EMULRUADDR) != 0)) {
+ /*
+ * Corner case: REUSEPORT change *but* REUSEADDR is still
+ * enabled. We must not alter conn_t/native state here, as
+ * REUSEADDR *needs* REUSEPORT enabled on conn_t/native state.
+ * If we want to enable REUSEPORT, the setsockopt would be a
+ * NOP. If want to disable it, we MUST NOT turn off native
+ * REUSEPORT lest we break Linux-like behavior, and instead
+ * merely turn off the LXSAD_FL_EMULRUPORT flag.
+ */
+ error = 0;
+ } else {
+ /*
+ * At this point, we need to change REUSEPORT. We may be
+ * doing it for an actual REUSEPORT change, OR for Linux
+ * REUSEADDR semantics. As earlier, we know the option map
+ * lookup is superfluous.
+ */
+ error = socket_setsockopt(so, SOL_SOCKET, SO_REUSEPORT, optval,
+ optlen, CRED());
+ }
+
+ if (error != 0 && optname == LX_SO_REUSEADDR) {
+ int addr_error, revert_to_optval;
+
+ ASSERT0(sad->lxsad_flags & LXSAD_FL_EMULRUPORT);
+ /*
+ * We need more cleanup if the REUSEPORT change fails during
+ * an actual REUSEADDR set.
+ */
+ if (enable) {
+ sad->lxsad_flags &= ~LXSAD_FL_EMULRUADDR;
+ revert_to_optval = 0;
+ } else {
+ sad->lxsad_flags |= LXSAD_FL_EMULRUADDR;
+ revert_to_optval = 1;
+ }
+
+ /* Just hardwire it, we're in trouble! */
+ addr_error = socket_setsockopt(so, SOL_SOCKET, SO_REUSEADDR,
+ &revert_to_optval, optlen, CRED());
+ if (addr_error != 0) {
+ /*
+ * Well this sucks, we really shot ourselves in the
+ * foot. We should somehow signal a catastrophic
+ * error. For now, just return the one we had earlier.
+ */
+ DTRACE_PROBE1(lx__reuse__seconderr, int, addr_error);
+ mutex_exit(&sad->lxsad_lock);
+ return (error);
+ }
+ /*
+ * Else we managed successfully to clean up and can fall
+ * through the normal error path.
+ */
+ } else if (error == 0 && optname == LX_SO_REUSEPORT) {
+ /* We successfully changed REUSEPORT explicitly. */
+ if (enable)
+ sad->lxsad_flags |= LXSAD_FL_EMULRUPORT;
+ else
+ sad->lxsad_flags &= ~LXSAD_FL_EMULRUPORT;
+ }
+ /* Else it's an error for an explicit REUSEPORT, just return. */
+
+ mutex_exit(&sad->lxsad_lock);
+ return (error);
+}
+
+static int
+lx_setsockopt_ip(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ int *intval = (int *)optval;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ip_sockopts);
+
+ switch (optname) {
+ case LX_IP_RECVERR:
+ /*
+ * Ping sets this option to receive errors on raw sockets.
+ * Currently we just ignore it to make ping happy. From the
+ * Linux ip.7 man page:
+ *
+ * For raw sockets, IP_RECVERR enables passing of all
+ * received ICMP errors to the application.
+ *
+ * Programs known to depend upon this:
+ * - ping
+ * - traceroute
+ * - mount.nfs
+ */
+ return (0);
+
+ case LX_IP_MTU_DISCOVER: {
+ int val;
+
+ /*
+ * We translate Linux's IP_MTU_DISCOVER into our IP_DONTFRAG,
+ * allowing this be a byte or an integer and observing the
+ * inverted sense of the two relative to one another (and
+ * translating accordingly).
+ */
+ if (optlen < sizeof (int)) {
+ val = *((uint8_t *)optval);
+ } else {
+ val = *((int *)optval);
+ }
+
+ switch (val) {
+ case LX_IP_PMTUDISC_DONT:
+ val = 1;
+ break;
+
+ case LX_IP_PMTUDISC_DO:
+ case LX_IP_PMTUDISC_WANT:
+ val = 0;
+ break;
+
+ default:
+ return (EOPNOTSUPP);
+ }
+
+ error = socket_setsockopt(so, IPPROTO_IP, IP_DONTFRAG,
+ &val, sizeof (val), CRED());
+ return (error);
+ }
+
+ case LX_IP_MULTICAST_TTL:
+ case LX_IP_MULTICAST_LOOP:
+ /*
+ * For IP_MULTICAST_TTL and IP_MULTICAST_LOOP, Linux defines
+ * the option value to be an integer while we define it to be
+ * an unsigned character. To prevent the kernel from spitting
+ * back an error on an illegal length, verify that the option
+ * value is less than UCHAR_MAX before truncating optlen.
+ */
+ if (optlen <= 0 || optlen > sizeof (int) ||
+ *intval > UINT8_MAX) {
+ return (EINVAL);
+ }
+ optlen = sizeof (uchar_t);
+ break;
+
+ case LX_MCAST_JOIN_GROUP:
+ case LX_MCAST_LEAVE_GROUP:
+ error = lx_mcast_common(so, IPPROTO_IP, optname, optval,
+ optlen);
+ return (error);
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, IPPROTO_IP, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_ipv6(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ipv6_sockopts);
+
+ switch (optname) {
+ case LX_IPV6_MTU:
+ /*
+ * There isn't a good translation for IPV6_MTU and certain apps
+ * such as bind9 will bail if it cannot be set.
+ * We just lie about the success for now.
+ */
+ return (0);
+ case LX_MCAST_JOIN_GROUP:
+ case LX_MCAST_LEAVE_GROUP:
+ error = lx_mcast_common(so, IPPROTO_IPV6, optname, optval,
+ optlen);
+ return (error);
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+ error = socket_setsockopt(so, IPPROTO_IPV6, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_icmpv6(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_icmpv6_sockopts);
+
+ if (optname == LX_ICMP6_FILTER && optval != NULL) {
+ /*
+ * Surprise! The input to ICMP6_FILTER on Linux is inverted
+ * when compared to illumos.
+ */
+ if (optlen != sizeof (icmp6_filter_t)) {
+ return (EINVAL);
+ }
+ LX_ICMP6_FILTER_INVERT((icmp6_filter_t *)optval);
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+ error = socket_setsockopt(so, IPPROTO_ICMPV6, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_tcp(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_tcp_sockopts);
+ cred_t *cr = CRED();
+ uint32_t rto_max, abrt_thresh;
+ boolean_t abrt_changed = B_FALSE, rto_max_changed = B_FALSE;
+
+ if (optname == LX_TCP_WINDOW_CLAMP) {
+ /* It appears safe to lie and say we did this. */
+ return (0);
+ }
+
+ if (optname == LX_TCP_MAXSEG) {
+ /*
+ * We can get, but not set, TCP_MAXSEG. However, it appears
+ * safe to lie and say we did this. A future extension might
+ * be to allow setting this before a connection is established.
+ */
+ return (0);
+ }
+
+ if (optname == LX_TCP_SYNCNT) {
+ int intval;
+ uint64_t syn_last_backoff;
+ uint_t syn_cnt, syn_backoff, len;
+
+ /*
+ * See the comment above the ltos_tcp_sockopts table for an
+ * explanation of the TCP_SYNCNT emulation.
+ */
+ if (optlen != sizeof (int)) {
+ return (EINVAL);
+ }
+ intval = *(int *)optval;
+ if (intval > 255) {
+ return (EINVAL);
+ }
+
+ len = sizeof (syn_backoff);
+ error = socket_getsockopt(so, IPPROTO_TCP,
+ TCP_CONN_NOTIFY_THRESHOLD, &syn_backoff, &len, 0, cr);
+ if (error != 0)
+ return (error);
+
+ syn_last_backoff = syn_backoff;
+ for (syn_cnt = 0; syn_cnt < intval; syn_cnt++) {
+ syn_last_backoff *= 2;
+ /*
+ * Since the tcps_ip_abort_cinterval is milliseconds and
+ * stored as a uint_t, it's basically impossible to get
+ * up to the Linux limit of 255 SYN retries due to the
+ * doubling on the backoff.
+ */
+ if (syn_last_backoff > UINT_MAX) {
+ return (EINVAL);
+ }
+ }
+
+ syn_backoff = (uint_t)syn_last_backoff;
+ error = socket_setsockopt(so, IPPROTO_TCP,
+ TCP_CONN_ABORT_THRESHOLD, &syn_backoff, len, cr);
+ return (error);
+ }
+
+ if (optname == LX_TCP_DEFER_ACCEPT) {
+ int *intval;
+ char *dfp;
+
+ /*
+ * Emulate TCP_DEFER_ACCEPT using the datafilt(7M) socket
+ * filter but we can't emulate the timeout aspect so treat any
+ * non-zero value as enabling and zero as disabling.
+ */
+ if (optlen != sizeof (int)) {
+ return (EINVAL);
+ }
+ intval = (int *)optval;
+
+ /*
+ * socket_setsockopt asserts that the optval is aligned, so
+ * we use kmem_alloc to ensure this.
+ */
+ dfp = (char *)kmem_alloc(sizeof (DATAFILT), KM_SLEEP);
+ (void) strcpy(dfp, DATAFILT);
+
+ if (*intval > 0) {
+ error = socket_setsockopt(so, SOL_FILTER, FIL_ATTACH,
+ dfp, 9, cr);
+ if (error == EEXIST) {
+ error = 0;
+ }
+ } else {
+ error = socket_setsockopt(so, SOL_FILTER, FIL_DETACH,
+ dfp, 9, cr);
+ if (error == ENXIO) {
+ error = 0;
+ }
+ }
+ kmem_free(dfp, sizeof (DATAFILT));
+ return (error);
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ if (optname == TCP_KEEPINTVL) {
+ /*
+ * When setting TCP_KEEPINTVL there is an unfortunate set of
+ * dependencies. TCP_KEEPINTVL must be <= TCP_RTO_MAX and
+ * TCP_RTO_MAX must be <= TCP_ABORT_THRESHOLD. Thus, we may
+ * have to increase one or both of these in order to increase
+ * TCP_KEEPINTVL. Note that TCP_KEEPINTVL is passed in seconds
+ * but TCP_RTO_MAX and TCP_ABORT_THRESHOLD are in milliseconds.
+ * Also note that we currently make no attempt to handle
+ * concurrent application threads simultaneously changing
+ * TCP_KEEPINTVL, since that is unlikely. We could revisit
+ * locking if it ever becomes an issue.
+ */
+ uint32_t new_val = *(uint_t *)optval * 1000;
+ uint32_t len;
+
+ /*
+ * Linux limits this to 32k, so we do too. However, anything
+ * over 2 hours (7200000 ms) will fail anyway due to the
+ * system-wide default (see "_rexmit_interval_max" in
+ * tcp_tunables.c). Our 2 hour default seems reasonable as a
+ * practical limit for now.
+ */
+ if (*(uint_t *)optval > SHRT_MAX)
+ return (EINVAL);
+
+ len = sizeof (rto_max);
+ if ((error = socket_getsockopt(so, IPPROTO_TCP, TCP_RTO_MAX,
+ &rto_max, &len, 0, cr)) != 0)
+ return (error);
+ len = sizeof (abrt_thresh);
+ if ((error = socket_getsockopt(so, IPPROTO_TCP,
+ TCP_ABORT_THRESHOLD, &abrt_thresh, &len, 0, cr)) != 0)
+ return (error);
+
+ if (new_val > abrt_thresh) {
+ error = socket_setsockopt(so, IPPROTO_TCP,
+ TCP_ABORT_THRESHOLD, &new_val, sizeof (new_val),
+ cr);
+ if (error != 0)
+ goto fail;
+ abrt_changed = B_TRUE;
+ }
+ if (new_val > rto_max) {
+ error = socket_setsockopt(so, IPPROTO_TCP,
+ TCP_RTO_MAX, &new_val, sizeof (new_val), cr);
+ if (error != 0)
+ goto fail;
+ rto_max_changed = B_TRUE;
+ }
+ }
+
+ error = socket_setsockopt(so, IPPROTO_TCP, optname, optval, optlen, cr);
+
+fail:
+ if (error != 0 && optname == TCP_KEEPINTVL) {
+ /*
+ * If changing TCP_KEEPINTVL failed then we may need to
+ * restore the previous values for TCP_ABORT_THRESHOLD and
+ * TCP_RTO_MAX.
+ */
+ if (rto_max_changed) {
+ (void) socket_setsockopt(so, IPPROTO_TCP,
+ TCP_RTO_MAX, &rto_max,
+ sizeof (rto_max), cr);
+ }
+ if (abrt_changed) {
+ (void) socket_setsockopt(so, IPPROTO_TCP,
+ TCP_ABORT_THRESHOLD, &abrt_thresh,
+ sizeof (abrt_thresh), cr);
+ }
+ }
+
+ return (error);
+}
+
+static int
+lx_setsockopt_socket(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_socket_sockopts);
+ struct lx_bpf_program *lbp;
+ int *intval;
+ struct bpf_program bp;
+
+ switch (optname) {
+ case LX_SO_BSDCOMPAT:
+ /* Linux ignores this option. */
+ return (0);
+
+ case LX_SO_TIMESTAMP:
+ /*
+ * SO_TIMESTAMP is not supported on AF_UNIX sockets but we have
+ * some of those which apps use for logging, etc., so pretend
+ * this worked.
+ */
+ if (so->so_family == AF_UNIX) {
+ return (0);
+ }
+ break;
+
+ case LX_SO_ATTACH_FILTER:
+ /*
+ * Convert bpf program struct
+ */
+ if (optlen != sizeof (struct lx_bpf_program)) {
+ return (EINVAL);
+ }
+ lbp = (struct lx_bpf_program *)optval;
+ bp.bf_len = lbp->bf_len;
+ /* LINTED: alignment */
+ bp.bf_insns = (struct bpf_insn *)lbp->bf_insns;
+ optval = &bp;
+ break;
+
+ case LX_SO_PASSSEC:
+ /*
+ * SO_PASSSEC is very similar to SO_PASSCRED (emulated by
+ * SO_RECVUCRED) in that it requests that cmsgs containing
+ * identity information be attached to recieved messages.
+ * Instead of ucred information, security-module-specific
+ * information such as selinux label is expected
+ *
+ * Since LX does not at all support selinux today, the
+ * option is silently accepted.
+ */
+ return (0);
+
+ case LX_SO_REUSEADDR:
+ case LX_SO_REUSEPORT:
+ if (lx_so_needs_reusehandling(so)) {
+ /*
+ * See lx_set_reuse_handler's comments for the oddness
+ * of REUSE* in some cases.
+ */
+ return (lx_set_reuse_handler(so, optname, optval,
+ optlen));
+ }
+ break;
+
+ case LX_SO_PASSCRED:
+ /*
+ * In many cases, the Linux SO_PASSCRED is mapped to the SunOS
+ * SO_RECVUCRED to enable the passing of peer credential
+ * information via received cmsgs. One exception is for
+ * connection-oriented AF_UNIX sockets which do not yet support
+ * that option. Instead, we track the setting internally and,
+ * when there is appropriate cmsg space, emulate the credential
+ * passing by querying the STREAMS ioctl.
+ *
+ * Note: this approach is broken for the case when a process
+ * sets up a Unix-domain socket with SO_PASSCRED, then forks
+ * one or more children, and expects to use the cmsg cred to
+ * accurately know which child pid sent the message (currently
+ * a pid is recorded when the socket is connected, not for each
+ * msg sent). getpeerucred(3c) suffers from the same problem.
+ * We have a workaround in lx_socketpair (use DGRAM if
+ * SEQPACKET), but the general case requires enhancing our
+ * streams support to allow passing credential cmsgs on a
+ * connection-oriented Unix socket.
+ */
+ if (so->so_family == AF_UNIX &&
+ (so->so_mode & SM_CONNREQUIRED) != 0) {
+ lx_socket_aux_data_t *sad;
+
+ if (optlen != sizeof (int)) {
+ return (EINVAL);
+ }
+ intval = (int *)optval;
+ sad = lx_sad_acquire(SOTOV(so));
+ if (*intval == 0) {
+ sad->lxsad_flags &= ~LXSAD_FL_STRCRED;
+ } else {
+ sad->lxsad_flags |= LXSAD_FL_STRCRED;
+ }
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+ }
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, SOL_SOCKET, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_raw(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_raw_sockopts);
+
+ switch (optname) {
+ case LX_ICMP_FILTER:
+ /*
+ * This option is currently ignored to appease ping.
+ */
+ return (0);
+
+ case LX_IPV6_CHECKSUM:
+ /*
+ * Ping6 tries to set the IPV6_CHECKSUM offset in a way that
+ * illumos won't allow. Quietly ignore this to prevent it from
+ * complaining.
+ */
+ return (0);
+
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, IPPROTO_TCP, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_packet(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_packet_sockopts);
+ struct packet_mreq *mr;
+
+ switch (optname) {
+ case LX_PACKET_ADD_MEMBERSHIP:
+ case LX_PACKET_DROP_MEMBERSHIP:
+ /* Convert Linux mr_type to illumos */
+ if (optlen != sizeof (struct packet_mreq)) {
+ return (EINVAL);
+ }
+ mr = (struct packet_mreq *)optval;
+ if (--mr->mr_type > PACKET_MR_ALLMULTI)
+ return (EINVAL);
+ optval = mr;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, SOL_PACKET, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_setsockopt_igmp(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+ int error;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_igmp_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_setsockopt(so, IPPROTO_IGMP, optname, optval, optlen,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_ip(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ip_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_IP, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_ipv6(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ipv6_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_IPV6, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_icmpv6(sonode_t *so, int optname, void *optval,
+ socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_icmpv6_sockopts);
+
+ if (optname == LX_ICMP6_FILTER) {
+ error = socket_getsockopt(so, IPPROTO_ICMPV6, ICMP6_FILTER,
+ optval, optlen, 0, CRED());
+
+ /*
+ * ICMP6_FILTER is inverted on Linux. Make it so before copying
+ * back to caller's buffer.
+ */
+ if (error == 0) {
+ LX_ICMP6_FILTER_INVERT((icmp6_filter_t *)optval);
+ }
+ return (error);
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_ICMPV6, optname, optval, optlen,
+ 0, CRED());
+ return (error);
+}
+
+/*
+ * When attempting to get socket options on AF_UNIX sockets we need to be a bit
+ * careful with the returned errno values. It turns out different OSes return
+ * different errno values here:
+ * - illumos: ENOPROTOOPT
+ * - Linux: EOPNOTSUPP
+ * - FreeBSD: EINVAL
+ * Therefore we remap ENOPROTOOPT to EOPNOTSUPP when a userland program attempts
+ * to get one of the various TCP_XXX options under this condition.
+ */
+static int
+lx_getsockopt_tcp(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+ int error = 0;
+ cred_t *cr = CRED();
+ int *intval = (int *)optval;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_tcp_sockopts);
+
+ switch (optname) {
+ case LX_TCP_WINDOW_CLAMP:
+ /*
+ * We do not support these options but some apps rely on them.
+ * Rather than return an error we just return 0. This isn't
+ * exactly a lie, since the options really aren't set, but it's
+ * not the whole truth either. Fortunately, we aren't under
+ * oath.
+ */
+ if (*optlen < sizeof (int)) {
+ return (EINVAL);
+ }
+ if (so->so_family == AF_UNIX) {
+ return (EOPNOTSUPP);
+ } else {
+ *intval = 0;
+ }
+ *optlen = sizeof (int);
+ return (error);
+
+ case LX_TCP_SYNCNT:
+ /*
+ * See the comment above the ltos_tcp_sockopts table for an
+ * explanation of the TCP_SYNCNT emulation.
+ */
+ if (*optlen < sizeof (int)) {
+ error = EINVAL;
+ } else {
+ uint_t syn_cnt, syn_backoff, syn_abortconn, len;
+
+ len = sizeof (syn_backoff);
+ error = socket_getsockopt(so, IPPROTO_TCP,
+ TCP_CONN_NOTIFY_THRESHOLD, &syn_backoff, &len, 0,
+ cr);
+ if (error != 0)
+ goto out;
+ error = socket_getsockopt(so, IPPROTO_TCP,
+ TCP_CONN_ABORT_THRESHOLD, &syn_abortconn, &len, 0,
+ cr);
+ if (error != 0)
+ goto out;
+
+ syn_cnt = 0;
+ while (syn_backoff < syn_abortconn) {
+ syn_cnt++;
+ syn_backoff *= 2;
+ }
+ if (syn_cnt > 255) /* clamp to Linux limit */
+ syn_cnt = 255;
+
+ *intval = syn_cnt;
+ *optlen = sizeof (int);
+ }
+
+ goto out;
+
+ case LX_TCP_DEFER_ACCEPT:
+ /*
+ * We do support TCP_DEFER_ACCEPT using the datafilt(7M) socket
+ * filter but we don't emulate the timeout aspect so treat the
+ * existence as 1 and absence as 0.
+ */
+ if (*optlen < sizeof (int)) {
+ error = EINVAL;
+ } else {
+ struct fil_info fi[10];
+ int i;
+ socklen_t len = sizeof (fi);
+
+ if ((error = socket_getsockopt(so, SOL_FILTER,
+ FIL_LIST, fi, &len, 0, cr)) != 0) {
+ *optlen = sizeof (int);
+ goto out;
+ }
+
+ *intval = 0;
+ len = len / sizeof (struct fil_info);
+ for (i = 0; i < len; i++) {
+ if (fi[i].fi_flags == FILF_PROG &&
+ strcmp(fi[i].fi_name, "datafilt") == 0) {
+ *intval = 1;
+ break;
+ }
+ }
+ }
+ *optlen = sizeof (int);
+ goto out;
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ if (optname <= sockopts_tbl.lpo_max &&
+ so->so_family == AF_UNIX) {
+ return (EOPNOTSUPP);
+ }
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_TCP, optname, optval, optlen, 0,
+ cr);
+
+out:
+ if (error == ENOPROTOOPT && so->so_family == AF_UNIX) {
+ return (EOPNOTSUPP);
+ }
+ return (error);
+}
+
+static int
+lx_getsockopt_socket(sonode_t *so, int optname, void *optval,
+ socklen_t *optlen)
+{
+ int error = 0;
+ int *intval = (int *)optval;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_socket_sockopts);
+ lx_socket_aux_data_t *sad;
+
+ switch (optname) {
+ case LX_SO_PROTOCOL:
+ /*
+ * We need to special-case netlink and AF_UNIX too.
+ */
+ if (so->so_family != AF_LX_NETLINK && so->so_family != AF_UNIX)
+ break; /* Common-case it. */
+ if (*optlen < sizeof (int)) {
+ error = EINVAL;
+ } else {
+ *intval = so->so_protocol;
+ }
+ *optlen = sizeof (int);
+ return (error);
+
+ case LX_SO_TYPE:
+ /*
+ * Special handling for connectionless AF_UNIX sockets.
+ * See lx_socketpair for more details.
+ */
+ if (so->so_family == AF_UNIX &&
+ (so->so_mode & SM_CONNREQUIRED) == 0) {
+ if (*optlen < sizeof (int))
+ return (EINVAL);
+ sad = lx_sad_acquire(SOTOV(so));
+ if ((sad->lxsad_flags & LXSAD_FL_EMULSEQPKT) != 0) {
+ *intval = LX_SOCK_SEQPACKET;
+ *optlen = sizeof (int);
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+ }
+ mutex_exit(&sad->lxsad_lock);
+ }
+ break;
+
+ case LX_SO_PASSSEC:
+ /*
+ * Communicate value of 0 since selinux-related functionality
+ * is not supported.
+ */
+ if (*optlen < sizeof (int)) {
+ error = EINVAL;
+ } else {
+ *intval = 0;
+ }
+ *optlen = sizeof (int);
+ return (error);
+
+ case LX_SO_PASSCRED:
+ /*
+ * Special handling for connection-oriented AF_UNIX sockets.
+ * See lx_setsockopt_socket for more details.
+ */
+ if (so->so_family == AF_UNIX &&
+ (so->so_mode & SM_CONNREQUIRED) != 0) {
+ if (*optlen < sizeof (int)) {
+ return (EINVAL);
+ }
+ sad = lx_sad_acquire(SOTOV(so));
+ *intval = ((sad->lxsad_flags & LXSAD_FL_STRCRED) == 0 ?
+ 0 : 1);
+ *optlen = sizeof (int);
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+ }
+ break;
+
+ case LX_SO_REUSEPORT:
+ /*
+ * See lx_so_needs_reusehandling() and lx_set_reuse_handler()
+ * for the sordid details.
+ */
+ if (!lx_so_needs_reusehandling(so))
+ break;
+
+ if (*optlen < sizeof (int))
+ return (EINVAL);
+ sad = lx_sad_acquire(SOTOV(so));
+ *optlen = sizeof (int);
+ *intval =
+ (sad->lxsad_flags & LXSAD_FL_EMULRUPORT) == 0 ? 0 : 1;
+ mutex_exit(&sad->lxsad_lock);
+ return (0);
+
+ case LX_SO_PEERCRED:
+ if (*optlen < sizeof (struct lx_ucred)) {
+ error = EINVAL;
+ } else {
+ struct lx_ucred *lcred = (struct lx_ucred *)optval;
+
+ mutex_enter(&so->so_lock);
+ if ((so->so_mode & SM_CONNREQUIRED) == 0) {
+ error = ENOTSUP;
+ } else if (so->so_peercred == NULL) {
+ error = EINVAL;
+ } else {
+ lcred->lxu_uid = crgetuid(so->so_peercred);
+ lcred->lxu_gid = crgetgid(so->so_peercred);
+ lcred->lxu_pid = so->so_cpid;
+ }
+ mutex_exit(&so->so_lock);
+ }
+ *optlen = sizeof (struct lx_ucred);
+ return (error);
+
+ default:
+ break;
+ }
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, SOL_SOCKET, optname, optval, optlen, 0,
+ CRED());
+
+ if (error == 0) {
+ switch (optname) {
+ case SO_TYPE:
+ /* translate our type back to Linux */
+ *intval = STOL_SOCKTYPE(*intval);
+ break;
+
+ case SO_ERROR:
+ *intval = lx_errno(*intval, EINVAL);
+ break;
+ default:
+ break;
+ }
+ }
+ return (error);
+}
+
+static int
+lx_getsockopt_raw(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_raw_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_RAW, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_packet(sonode_t *so, int optname, void *optval,
+ socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_packet_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, SOL_PACKET, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+static int
+lx_getsockopt_igmp(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+ int error = 0;
+ lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_igmp_sockopts);
+
+ if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+ return (ENOPROTOOPT);
+ }
+
+ error = socket_getsockopt(so, IPPROTO_IGMP, optname, optval, optlen, 0,
+ CRED());
+ return (error);
+}
+
+long
+lx_setsockopt(int sock, int level, int optname, void *optval, socklen_t optlen)
+{
+ struct sonode *so;
+ file_t *fp;
+ int buflen = 0;
+ intptr_t stkbuf[2];
+ void *optbuf = stkbuf;
+ int error = 0;
+
+ if (optlen != 0) {
+ if (optlen > SO_MAXARGSIZE) {
+ return (set_errno(EINVAL));
+ }
+ if (optlen > sizeof (stkbuf)) {
+ buflen = optlen;
+ optbuf = kmem_alloc(optlen, KM_SLEEP);
+ } else {
+ /*
+ * Zero the on-stack buffer to avoid poisoning smaller
+ * optvals with stack garbage.
+ */
+ stkbuf[0] = 0;
+ stkbuf[1] = 0;
+ }
+ if (copyin(optval, optbuf, optlen) != 0) {
+ if (buflen != 0) {
+ kmem_free(optbuf, buflen);
+ }
+ return (set_errno(EFAULT));
+ }
+ } else {
+ optbuf = NULL;
+ }
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ if (buflen != 0) {
+ kmem_free(optbuf, buflen);
+ }
+ return (set_errno(error));
+ }
+
+ switch (level) {
+ case LX_IPPROTO_IP:
+ error = lx_setsockopt_ip(so, optname, optbuf, optlen);
+ break;
+ case LX_IPPROTO_IPV6:
+ error = lx_setsockopt_ipv6(so, optname, optbuf, optlen);
+ break;
+ case LX_IPPROTO_ICMPV6:
+ error = lx_setsockopt_icmpv6(so, optname, optbuf, optlen);
+ break;
+ case LX_IPPROTO_TCP:
+ error = lx_setsockopt_tcp(so, optname, optbuf, optlen);
+ break;
+ case LX_SOL_SOCKET:
+ error = lx_setsockopt_socket(so, optname, optbuf, optlen);
+ break;
+ case LX_IPPROTO_RAW:
+ error = lx_setsockopt_raw(so, optname, optbuf, optlen);
+ break;
+ case LX_SOL_PACKET:
+ error = lx_setsockopt_packet(so, optname, optbuf, optlen);
+ break;
+ case LX_IPPROTO_IGMP:
+ error = lx_setsockopt_igmp(so, optname, optbuf, optlen);
+ break;
+ case LX_SOL_NETLINK:
+ /*
+ * Since our netlink implmentation is modeled after Linux,
+ * sockopts can be passed directly through.
+ */
+ error = socket_setsockopt(so, LX_SOL_NETLINK, optname, optval,
+ optlen, CRED());
+ break;
+ default:
+ error = ENOPROTOOPT;
+ break;
+ }
+
+ if (error == ENOPROTOOPT) {
+ char buf[LX_UNSUP_BUFSZ];
+
+ (void) snprintf(buf, LX_UNSUP_BUFSZ, "setsockopt(%d, %d)",
+ level, optname);
+ lx_unsupported(buf);
+ }
+ if (buflen != 0) {
+ kmem_free(optbuf, buflen);
+ }
+ releasef(sock);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_getsockopt(int sock, int level, int optname, void *optval,
+ socklen_t *optlenp)
+{
+ struct sonode *so;
+ file_t *fp;
+ int error = 0, buflen = 0;
+ socklen_t optlen;
+ intptr_t stkbuf[2];
+ void *optbuf = stkbuf;
+
+ if (copyin(optlenp, &optlen, sizeof (optlen)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ if (optlen != 0) {
+ if (optlen > SO_MAXARGSIZE) {
+ return (set_errno(EINVAL));
+ }
+ if (optlen > sizeof (stkbuf)) {
+ buflen = optlen;
+ optbuf = kmem_zalloc(optlen, KM_SLEEP);
+ } else {
+ /* zero the on-stack buffer, just in case */
+ stkbuf[0] = 0;
+ stkbuf[1] = 0;
+ }
+ } else {
+ optbuf = NULL;
+ }
+ if ((so = getsonode(sock, &error, &fp)) == NULL) {
+ if (buflen != 0) {
+ kmem_free(optbuf, buflen);
+ }
+ return (set_errno(error));
+ }
+
+ switch (level) {
+ case LX_IPPROTO_IP:
+ error = lx_getsockopt_ip(so, optname, optbuf, &optlen);
+ break;
+ case LX_IPPROTO_IPV6:
+ error = lx_getsockopt_ipv6(so, optname, optbuf, &optlen);
+ break;
+ case LX_IPPROTO_ICMPV6:
+ error = lx_getsockopt_icmpv6(so, optname, optbuf, &optlen);
+ break;
+ case LX_IPPROTO_TCP:
+ error = lx_getsockopt_tcp(so, optname, optbuf, &optlen);
+ break;
+ case LX_SOL_SOCKET:
+ error = lx_getsockopt_socket(so, optname, optbuf, &optlen);
+ break;
+ case LX_IPPROTO_RAW:
+ error = lx_getsockopt_raw(so, optname, optbuf, &optlen);
+ break;
+ case LX_SOL_PACKET:
+ error = lx_getsockopt_packet(so, optname, optbuf, &optlen);
+ break;
+ case LX_IPPROTO_IGMP:
+ error = lx_getsockopt_igmp(so, optname, optbuf, &optlen);
+ break;
+ case LX_SOL_NETLINK:
+ /*
+ * Since our netlink implmentation is modeled after Linux,
+ * sockopts can be passed directly through.
+ */
+ error = socket_getsockopt(so, LX_SOL_NETLINK, optname, optval,
+ &optlen, 0, CRED());
+ break;
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ if (error == ENOPROTOOPT) {
+ char buf[LX_UNSUP_BUFSZ];
+
+ (void) snprintf(buf, LX_UNSUP_BUFSZ, "getsockopt(%d, %d)",
+ level, optname);
+ lx_unsupported(buf);
+ }
+ if (copyout(&optlen, optlenp, sizeof (optlen)) != 0) {
+ error = EFAULT;
+ }
+ if (error == 0 && optlen > 0) {
+ VERIFY(optlen <= sizeof (stkbuf) || optlen <= buflen);
+ if (copyout(optbuf, optval, optlen) != 0) {
+ error = EFAULT;
+ }
+ }
+ if (buflen != 0) {
+ kmem_free(optbuf, buflen);
+ }
+ releasef(sock);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_getname_common(lx_getname_type_t type, int sockfd, void *np, int *nlp)
+{
+ struct sockaddr_storage buf;
+ struct sockaddr *name = (struct sockaddr *)&buf;
+ socklen_t namelen, namelen_orig;
+ int err, tmp;
+ struct sonode *so;
+
+ /* We need to validate the name address up front to pass LTP. */
+ if (copyin(np, &tmp, sizeof (tmp)) != 0)
+ return (set_errno(EFAULT));
+
+ if (copyin(nlp, &namelen, sizeof (socklen_t)) != 0)
+ return (set_errno(EFAULT));
+ namelen_orig = namelen;
+
+ /* LTP can pass -1 */
+ if ((int)namelen < 0)
+ return (set_errno(EINVAL));
+
+ if ((so = getsonode(sockfd, &err, NULL)) == NULL)
+ return (set_errno(err));
+
+ bzero(&buf, sizeof (buf));
+ namelen = sizeof (struct sockaddr_storage);
+ if (type == LX_GETPEERNAME) {
+ err = socket_getpeername(so, name, &namelen, B_FALSE, CRED());
+ } else {
+ err = socket_getsockname(so, name, &namelen, CRED());
+ }
+
+ if (err == 0) {
+ ASSERT(namelen <= so->so_max_addr_len);
+ err = stol_sockaddr_copyout(name, namelen,
+ (struct sockaddr *)np, (socklen_t *)nlp, namelen_orig);
+ }
+
+ releasef(sockfd);
+ return (err != 0 ? set_errno(err) : 0);
+}
+
+long
+lx_getpeername(int sockfd, void *np, int *nlp)
+{
+ return (lx_getname_common(LX_GETPEERNAME, sockfd, np, nlp));
+}
+
+long
+lx_getsockname(int sockfd, void *np, int *nlp)
+{
+ return (lx_getname_common(LX_GETSOCKNAME, sockfd, np, nlp));
+}
+
+static int
+lx_accept_common(int sock, struct sockaddr *name, socklen_t *nlp, int flags)
+{
+ struct sonode *so;
+ file_t *fp;
+ int error;
+ socklen_t namelen;
+ struct sonode *nso;
+ struct vnode *nvp;
+ struct file *nfp;
+ int nfd;
+ int arg;
+
+ if (flags & ~(LX_SOCK_CLOEXEC | LX_SOCK_NONBLOCK)) {
+ return (set_errno(EINVAL));
+ }
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL)
+ return (set_errno(error));
+
+ if (name != NULL) {
+ /*
+ * The Linux man page says that -1 is returned and errno is set
+ * to EFAULT if the "name" address is bad, but it is silent on
+ * what to set errno to if the "namelen" address is bad.
+ * LTP expects EINVAL.
+ *
+ * Note that we must first check the name pointer, as the Linux
+ * docs state nothing is copied out if the "name" pointer is
+ * NULL. If it is NULL, we don't care about the namelen
+ * pointer's value or about dereferencing it.
+ */
+ if (copyin(nlp, &namelen, sizeof (namelen))) {
+ releasef(sock);
+ return (set_errno(EINVAL));
+ }
+ if (namelen == 0) {
+ name = NULL;
+ }
+ } else {
+ namelen = 0;
+ }
+
+ /*
+ * Allocate the user fd before socket_accept() in order to
+ * catch EMFILE errors before calling socket_accept().
+ */
+ if ((error = falloc(NULL, FWRITE|FREAD, &nfp, &nfd)) != 0) {
+ eprintsoline(so, EMFILE);
+ releasef(sock);
+ return (set_errno(error));
+ }
+ if ((error = socket_accept(so, fp->f_flag, CRED(), &nso)) != 0) {
+ if (error == EINTR)
+ lx_sock_syscall_restart(so, B_TRUE);
+ setf(nfd, NULL);
+ unfalloc(nfp);
+ releasef(sock);
+ return (set_errno(error));
+ }
+
+ nvp = SOTOV(nso);
+
+ if (namelen != 0) {
+ socklen_t addrlen = sizeof (struct sockaddr_storage);
+ struct sockaddr_storage buf;
+ struct sockaddr *addrp = (struct sockaddr *)&buf;
+
+ if ((error = socket_getpeername(nso, addrp, &addrlen, B_TRUE,
+ CRED())) == 0) {
+ error = stol_sockaddr_copyout(addrp, addrlen,
+ name, nlp, namelen);
+ /*
+ * Logic might dictate that we should check if we can
+ * write to the namelen pointer earlier so we don't
+ * accept a pending connection only to fail the call
+ * because we can't write the namelen value back out.
+ * However, testing shows Linux does indeed fail the
+ * call after accepting the connection so we must
+ * behave in a compatible manner.
+ */
+ } else {
+ ASSERT(error == EINVAL || error == ENOTCONN);
+ error = ECONNABORTED;
+ }
+ }
+
+ if (error != 0) {
+ setf(nfd, NULL);
+ unfalloc(nfp);
+ (void) socket_close(nso, 0, CRED());
+ socket_destroy(nso);
+ releasef(sock);
+ return (set_errno(error));
+ }
+
+ /* Fill in the entries that falloc reserved */
+ nfp->f_vnode = nvp;
+ mutex_exit(&nfp->f_tlock);
+ setf(nfd, nfp);
+
+ /* Act on LX_SOCK_CLOEXEC from flags */
+ if (flags & LX_SOCK_CLOEXEC) {
+ f_setfd(nfd, FD_CLOEXEC);
+ }
+
+ /*
+ * In Linux, accept()ed sockets do not inherit anything set by fcntl(),
+ * so either explicitly set the flags or filter those out.
+ *
+ * The VOP_SETFL code is a simplification of the F_SETFL code in
+ * fcntl(). Ignore any errors from VOP_SETFL.
+ */
+ arg = 0;
+ if (flags & LX_SOCK_NONBLOCK)
+ arg |= FNONBLOCK;
+
+ error = VOP_SETFL(nvp, nfp->f_flag, arg, nfp->f_cred, NULL);
+ if (error != 0) {
+ eprintsoline(so, error);
+ error = 0;
+ } else {
+ mutex_enter(&nfp->f_tlock);
+ nfp->f_flag &= ~FMASK | (FREAD|FWRITE);
+ nfp->f_flag |= arg;
+ mutex_exit(&nfp->f_tlock);
+ }
+
+ releasef(sock);
+ return (nfd);
+}
+
+long
+lx_accept(int sockfd, void *np, int *nlp)
+{
+ return (lx_accept_common(sockfd, (struct sockaddr *)np,
+ (socklen_t *)nlp, 0));
+}
+
+long
+lx_accept4(int sockfd, void *np, int *nlp, int flags)
+{
+ return (lx_accept_common(sockfd, (struct sockaddr *)np,
+ (socklen_t *)nlp, flags));
+}
+
+long
+lx_listen(int sockfd, int backlog)
+{
+ return (listen(sockfd, backlog, 0));
+}
+
+long
+lx_shutdown(int sockfd, int how)
+{
+ return (shutdown(sockfd, how, 0));
+}
+
+/*
+ * Connect two sockets together for a socketpair. This is derived from
+ * so_socketpair, but forgoes the task of dealing with file descriptors.
+ */
+static int
+lx_socketpair_connect(file_t *fp1, file_t *fp2)
+{
+ sonode_t *so1, *so2;
+ sotpi_info_t *sti1, *sti2;
+ struct sockaddr_ux name;
+ int error;
+
+ so1 = VTOSO(fp1->f_vnode);
+ so2 = VTOSO(fp2->f_vnode);
+ sti1 = SOTOTPI(so1);
+ sti2 = SOTOTPI(so2);
+
+ VERIFY(so1->so_ops == &sotpi_sonodeops &&
+ so2->so_ops == &sotpi_sonodeops);
+
+ if (so1->so_type == SOCK_DGRAM) {
+ /*
+ * Bind both sockets and connect them with each other.
+ */
+ error = socket_bind(so1, NULL, 0, _SOBIND_UNSPEC, CRED());
+ if (error) {
+ return (error);
+ }
+ error = socket_bind(so2, NULL, 0, _SOBIND_UNSPEC, CRED());
+ if (error) {
+ return (error);
+ }
+ name.sou_family = AF_UNIX;
+ name.sou_addr = sti2->sti_ux_laddr;
+ error = socket_connect(so1, (struct sockaddr *)&name,
+ (socklen_t)sizeof (name), 0, _SOCONNECT_NOXLATE, CRED());
+ if (error) {
+ return (error);
+ }
+ name.sou_addr = sti1->sti_ux_laddr;
+ error = socket_connect(so2, (struct sockaddr *)&name,
+ (socklen_t)sizeof (name), 0, _SOCONNECT_NOXLATE, CRED());
+ return (error);
+ } else {
+ sonode_t *nso;
+
+ /*
+ * Bind both sockets, with 'so1' being a listener. Connect
+ * 'so2' to 'so1', doing so as nonblocking to avoid waiting for
+ * soaccept to complete. Accept the connection on 'so1',
+ * replacing the socket/vnode in 'fp1' with the new connection.
+ *
+ * We could simply call socket_listen() here (which would do the
+ * binding automatically) if the code didn't rely on passing
+ * _SOBIND_NOXLATE to the TPI implementation of socket_bind().
+ */
+ error = socket_bind(so1, NULL, 0, _SOBIND_UNSPEC|
+ _SOBIND_NOXLATE|_SOBIND_LISTEN|_SOBIND_SOCKETPAIR, CRED());
+ if (error) {
+ return (error);
+ }
+ error = socket_bind(so2, NULL, 0, _SOBIND_UNSPEC, CRED());
+ if (error) {
+ return (error);
+ }
+
+ name.sou_family = AF_UNIX;
+ name.sou_addr = sti1->sti_ux_laddr;
+ error = socket_connect(so2,
+ (struct sockaddr *)&name,
+ (socklen_t)sizeof (name),
+ FNONBLOCK, _SOCONNECT_NOXLATE, CRED());
+ if (error != 0 && error != EINPROGRESS) {
+ return (error);
+ }
+
+ error = socket_accept(so1, 0, CRED(), &nso);
+ if (error) {
+ return (error);
+ }
+
+ /* wait for so2 being SS_CONNECTED */
+ mutex_enter(&so2->so_lock);
+ error = sowaitconnected(so2, 0, 0);
+ mutex_exit(&so2->so_lock);
+ if (error != 0) {
+ (void) socket_close(nso, 0, CRED());
+ socket_destroy(nso);
+ return (error);
+ }
+
+ (void) socket_close(so1, 0, CRED());
+ socket_destroy(so1);
+ fp1->f_vnode = SOTOV(nso);
+ }
+ return (0);
+}
+
+long
+lx_socketpair(int domain, int type, int protocol, int *sv)
+{
+ int err, options, fds[2];
+ file_t *fps[2];
+ boolean_t emul_seqp = B_FALSE;
+
+ /*
+ * For the special case of SOCK_SEQPACKET for AF_UNIX, we want to treat
+ * this as a SOCK_DGRAM. The semantics are similar, but our native code
+ * will not pass cmsg creds over a connection-oriented socket, unlike a
+ * connectionless one. Some Linux code depends on this for Unix-domain
+ * sockets. In particular, a sockopt of SO_PASSCRED, which we map into
+ * our native SO_RECVUCRED, must work across fork so that the correct
+ * pid of the sender is available in the cmsg. See the comment in
+ * lx_setsockopt_socket().
+ */
+ if (domain == LX_AF_UNIX && type == LX_SOCK_SEQPACKET) {
+ type = LX_SOCK_DGRAM;
+ emul_seqp = B_TRUE;
+ }
+
+ if ((err = lx_convert_sock_args(domain, type, protocol, &domain, &type,
+ &options, &protocol)) != 0) {
+ return (set_errno(err));
+ }
+
+ if ((err = lx_socket_create(domain, type, protocol, options, &fps[0],
+ &fds[0])) != 0) {
+ return (set_errno(err));
+ }
+
+ /*
+ * While it seems silly to check the family after socket creation, this
+ * is done to appease LTP when it tries some outlandish combinations of
+ * domain/type/protocol. The socket_create function is relied upon to
+ * emit the expected errors.
+ */
+ if (VTOSO(fps[0]->f_vnode)->so_family != AF_UNIX) {
+ lx_socket_destroy(fps[0], fds[0]);
+ return (set_errno(EOPNOTSUPP));
+ }
+
+ if ((err = lx_socket_create(domain, type, protocol, options, &fps[1],
+ &fds[1])) != 0) {
+ lx_socket_destroy(fps[0], fds[0]);
+ return (set_errno(err));
+ }
+
+ err = lx_socketpair_connect(fps[0], fps[1]);
+ if (err != 0) {
+ lx_socket_destroy(fps[0], fds[0]);
+ lx_socket_destroy(fps[1], fds[1]);
+ return (set_errno(err));
+ }
+
+ if (emul_seqp) {
+ int i;
+ for (i = 0; i < 2; i++) {
+ sonode_t *so = VTOSO(fps[i]->f_vnode);
+ lx_socket_aux_data_t *sad = lx_sad_acquire(SOTOV(so));
+ sad->lxsad_flags |= LXSAD_FL_EMULSEQPKT;
+ mutex_exit(&sad->lxsad_lock);
+ }
+ }
+
+ setf(fds[0], fps[0]);
+ setf(fds[1], fps[1]);
+
+ if ((options & SOCK_CLOEXEC) != 0) {
+ f_setfd(fds[0], FD_CLOEXEC);
+ f_setfd(fds[1], FD_CLOEXEC);
+ }
+ if (copyout(fds, sv, sizeof (fds)) != 0) {
+ (void) closeandsetf(fds[0], NULL);
+ (void) closeandsetf(fds[1], NULL);
+ return (set_errno(EFAULT));
+ }
+ return (0);
+}
+
+
+#if defined(_SYSCALL32_IMPL)
+
+#define LX_SYS_SOCKETCALL 102
+#define LX_SOCKETCALL_MAX 20
+
+typedef long (*lx_sockfn_t)();
+
+static struct {
+ lx_sockfn_t s_fn; /* Function implementing the subcommand */
+ int s_nargs; /* Number of arguments the function takes */
+} lx_socketcall_fns[] = {
+ lx_socket, 3, /* socket */
+ lx_bind, 3, /* bind */
+ lx_connect, 3, /* connect */
+ lx_listen, 2, /* listen */
+ lx_accept, 3, /* accept */
+ lx_getsockname, 3, /* getsockname */
+ lx_getpeername, 3, /* getpeername */
+ lx_socketpair, 4, /* socketpair */
+ lx_send, 4, /* send */
+ lx_recv, 4, /* recv */
+ lx_sendto, 6, /* sendto */
+ lx_recvfrom, 6, /* recvfrom */
+ lx_shutdown, 2, /* shutdown */
+ lx_setsockopt, 5, /* setsockopt */
+ lx_getsockopt, 5, /* getsockopt */
+ lx_sendmsg, 3, /* sendmsg */
+ lx_recvmsg, 3, /* recvmsg */
+ lx_accept4, 4, /* accept4 */
+ lx_recvmmsg, 5, /* recvmmsg */
+ lx_sendmmsg, 4 /* sendmmsg */
+};
+
+long
+lx_socketcall(long p1, uint32_t *p2)
+{
+ int subcmd, i;
+ unsigned long args[6] = { 0, 0, 0, 0, 0, 0 };
+
+ /* incoming subcmds are 1-indexed */
+ subcmd = (int)p1 - 1;
+
+ if (subcmd < 0 || subcmd >= LX_SOCKETCALL_MAX ||
+ lx_socketcall_fns[subcmd].s_fn == NULL) {
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * Copy the arguments to the subcommand in from the app's address
+ * space, returning EFAULT if we get a bogus pointer.
+ */
+ for (i = 0; i < lx_socketcall_fns[subcmd].s_nargs; i++) {
+ uint32_t arg;
+
+ if (copyin(&p2[i], &arg, sizeof (uint32_t)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ args[i] = (unsigned long)arg;
+ }
+
+ return ((lx_socketcall_fns[subcmd].s_fn)(args[0], args[1], args[2],
+ args[3], args[4], args[5]));
+}
+
+#endif /* defined(_SYSCALL32_IMPL) */
+
+static void
+lx_socket_vsd_free(void *data)
+{
+ lx_socket_aux_data_t *entry;
+
+ entry = (lx_socket_aux_data_t *)data;
+ mutex_destroy(&entry->lxsad_lock);
+ kmem_free(entry, sizeof (*entry));
+}
+
+void
+lx_socket_init()
+{
+ vsd_create(&lx_socket_vsd, lx_socket_vsd_free);
+}
+
+void
+lx_socket_fini()
+{
+ vsd_destroy(&lx_socket_vsd);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_splice.c b/usr/src/uts/common/brand/lx/syscall/lx_splice.c
new file mode 100644
index 0000000000..64db538413
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_splice.c
@@ -0,0 +1,491 @@
+/*
+ * 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 2017, Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/sunddi.h>
+#include <sys/fs/fifonode.h>
+#include <sys/strsun.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_signal.h>
+
+/* Splice flags */
+#define LX_SPLICE_F_MOVE 0x01
+#define LX_SPLICE_F_NONBLOCK 0x02
+#define LX_SPLICE_F_MORE 0x04
+#define LX_SPLICE_F_GIFT 0x08
+
+/*
+ * Use a max buffer size of 32k. This is a good compromise between doing I/O in
+ * large chunks, the limit on how much data we can write into an lx pipe by
+ * default (LX_DEFAULT_PIPE_SIZE), and how much kernel memory we'll allocate.
+ */
+#define LX_SPL_BUF_SIZE (32 * 1024)
+
+/*
+ * We only want to read as much from the input fd as we can write into the
+ * output fd, up to our buffer size. Figure out what that quantity is.
+ * Note that len will continuously decrease to 0 which triggers the typical
+ * end of the splice loop.
+ */
+static size_t
+lx_spl_wr_sz(file_t *fp_out, u_offset_t fileoff, size_t bsz, size_t len,
+ boolean_t first)
+{
+ size_t sz;
+
+ sz = MIN(bsz, len);
+ if (fp_out->f_vnode->v_type == VFIFO) {
+ /*
+ * If no readers on pipe, or if it would go over high water
+ * mark then return 0. Note that the first write into a
+ * pipe is expected to block if we're over the high water mark.
+ */
+ fifonode_t *fn_dest = VTOF(fp_out->f_vnode)->fn_dest;
+ fifolock_t *fn_lock = fn_dest->fn_lock;
+
+ mutex_enter(&fn_lock->flk_lock);
+ if (fn_dest->fn_rcnt == 0) {
+ sz = 0;
+ } else if (!first &&
+ (sz + fn_dest->fn_count) > fn_dest->fn_hiwat) {
+ sz = 0;
+ }
+ mutex_exit(&fn_lock->flk_lock);
+ } else if (fp_out->f_vnode->v_type == VREG) {
+ if (fileoff >= curproc->p_fsz_ctl ||
+ fileoff >= OFFSET_MAX(fp_out)) {
+ sz = 0;
+ } else {
+ sz = MIN(sz, (size_t)curproc->p_fsz_ctl - fileoff);
+ sz = MIN(sz, (size_t)OFFSET_MAX(fp_out) - fileoff);
+ }
+ }
+
+ /*
+ * if (fp_out->f_vnode->v_type == VSOCK)
+ *
+ * There is no good way to determine if a socket is "full". A write for
+ * the different protocol implementations can return EWOULDBLOCK under
+ * different conditions, none of which we can easily check for in
+ * advance.
+ */
+
+ return (sz);
+}
+
+/*
+ * The splice read function handles "reading" from a pipe and passes everything
+ * else along to our normal VOP_READ code path.
+ *
+ * When we have a pipe as our input, we don't want to consume the data out
+ * of the pipe until the write has succeeded. This aligns more closely with
+ * the Linux behavior when a write error occurs. Thus, when a pipe is the input
+ * and we got some data, we return with the fifo flagged as FIFORDBLOCK. This
+ * ensures that the data we're writing cannot be consumed by another thread
+ * until we consume it ourself.
+ *
+ * The pipe "read" code here is derived from the fifo I_PEEK code.
+ */
+static int
+lx_spl_read(file_t *fp, uio_t *uiop, size_t *nread, boolean_t pipe_in,
+ boolean_t rd_pos)
+{
+ fifonode_t *fnp;
+ fifolock_t *fn_lock;
+ int count;
+ mblk_t *bp;
+
+ if (!pipe_in)
+ return (lx_read_common(fp, uiop, nread, rd_pos));
+
+ ASSERT(fp->f_vnode->v_type == VFIFO);
+ fnp = VTOF(fp->f_vnode);
+ fn_lock = fnp->fn_lock;
+ *nread = 0;
+
+ mutex_enter(&fn_lock->flk_lock);
+
+ /*
+ * If the pipe has been switched to socket mode then this implies an
+ * internal programmatic error. Likewise, if it was switched to
+ * socket mode because we dropped the lock to set the stayfast flag.
+ */
+ if ((fnp->fn_flag & FIFOFAST) == 0 || !fifo_stayfast_enter(fnp)) {
+ mutex_exit(&fn_lock->flk_lock);
+ return (EBADF);
+ }
+
+ while (fnp->fn_count == 0 || (fnp->fn_flag & FIFORDBLOCK) != 0) {
+ fifonode_t *fn_dest = fnp->fn_dest;
+
+ /* No writer, EOF */
+ if (fn_dest->fn_wcnt == 0 || fn_dest->fn_rcnt == 0) {
+ fifo_stayfast_exit(fnp);
+ mutex_exit(&fn_lock->flk_lock);
+ return (0);
+ }
+
+ /* If non-blocking, return EAGAIN otherwise 0. */
+ if (uiop->uio_fmode & (FNDELAY|FNONBLOCK)) {
+ fifo_stayfast_exit(fnp);
+ mutex_exit(&fn_lock->flk_lock);
+ if (uiop->uio_fmode & FNONBLOCK)
+ return (EAGAIN);
+ return (0);
+ }
+
+ /* Wait for data */
+ fnp->fn_flag |= FIFOWANTR;
+ if (!cv_wait_sig_swap(&fnp->fn_wait_cv, &fn_lock->flk_lock)) {
+ fifo_stayfast_exit(fnp);
+ mutex_exit(&fn_lock->flk_lock);
+ return (EINTR);
+ }
+ }
+
+ VERIFY((fnp->fn_flag & FIFORDBLOCK) == 0);
+ VERIFY((fnp->fn_flag & FIFOSTAYFAST) != 0);
+
+ /* Get up to our read size or whatever is currently available. */
+ count = MIN(uiop->uio_resid, fnp->fn_count);
+ ASSERT(count > 0);
+ *nread = count;
+ bp = fnp->fn_mp;
+ while (count > 0) {
+ uint_t cnt = MIN(uiop->uio_resid, MBLKL(bp));
+
+ /*
+ * We have the input pipe locked and we know there is data
+ * available to consume. We're doing a UIO_SYSSPACE move into
+ * an internal buffer that we allocated in lx_splice() so
+ * this should never fail.
+ */
+ VERIFY(uiomove((char *)bp->b_rptr, cnt, UIO_READ, uiop) == 0);
+ count -= cnt;
+ bp = bp->b_cont;
+ }
+
+ fnp->fn_flag |= FIFORDBLOCK;
+
+ mutex_exit(&fn_lock->flk_lock);
+ return (0);
+}
+
+/*
+ * We've already "read" the data out of the pipe without actually consuming it.
+ * Here we update the pipe to consume the data and discard it. This is derived
+ * from the fifo_read code, except that we already know the amount of data
+ * in the pipe to consume and we don't have to actually move any data.
+ */
+static void
+lx_spl_consume(file_t *fp, uint_t count)
+{
+ fifonode_t *fnp, *fn_dest;
+ fifolock_t *fn_lock;
+
+ ASSERT(fp->f_vnode->v_type == VFIFO);
+
+ fnp = VTOF(fp->f_vnode);
+ fn_lock = fnp->fn_lock;
+
+ mutex_enter(&fn_lock->flk_lock);
+ VERIFY(fnp->fn_count >= count);
+
+ while (count > 0) {
+ int bpsize = MBLKL(fnp->fn_mp);
+ int decr_size = MIN(bpsize, count);
+
+ fnp->fn_count -= decr_size;
+ if (bpsize <= decr_size) {
+ mblk_t *bp = fnp->fn_mp;
+ fnp->fn_mp = fnp->fn_mp->b_cont;
+ freeb(bp);
+ } else {
+ fnp->fn_mp->b_rptr += decr_size;
+ }
+
+ count -= decr_size;
+ }
+
+ fnp->fn_flag &= ~FIFORDBLOCK;
+ fifo_stayfast_exit(fnp);
+
+ fifo_wakereader(fnp, fn_lock);
+
+ /*
+ * Wake up any blocked writers, processes sleeping on POLLWRNORM, or
+ * processes waiting for SIGPOLL.
+ */
+ fn_dest = fnp->fn_dest;
+ if (fn_dest->fn_flag & (FIFOWANTW | FIFOHIWATW) &&
+ fnp->fn_count < fn_dest->fn_hiwat) {
+ fifo_wakewriter(fn_dest, fn_lock);
+ }
+
+ /* Update vnode update access time */
+ fnp->fn_atime = fnp->fn_dest->fn_atime = gethrestime_sec();
+
+ mutex_exit(&fn_lock->flk_lock);
+}
+
+/*
+ * Transfer data from the input file descriptor to the output file descriptor
+ * without leaving the kernel. For Linux this is limited by it's kernel
+ * implementation which forces at least one of the file descriptors to be a
+ * pipe. Our implementation is likely quite different from the Linux
+ * one, which appears to play some VM tricks with shared pages from the pipe
+ * code. Instead, our implementation uses our normal VOP_READ/VOP_WRITE
+ * operations to internally move the data while using a single uio buffer. We
+ * implement the additional Linux behavior around the various checks and
+ * limitations.
+ *
+ * One key point on the read side is how we handle an input pipe. We don't
+ * want to consume the data out of the pipe until the write has succeeded.
+ * This aligns more closely with the Linux behavior when a write error occurs.
+ * The lx_spl_read() and lx_spl_consume() functions are used to handle this
+ * case.
+ */
+long
+lx_splice(int fd_in, off_t *off_in, int fd_out, off_t *off_out, size_t len,
+ uint_t flags)
+{
+ int error = 0;
+ file_t *fp_in = NULL, *fp_out = NULL;
+ boolean_t found_pipe = B_FALSE, rd_pos = B_FALSE, wr_pos = B_FALSE;
+ boolean_t first = B_TRUE, pipe_in = B_FALSE;
+ iovec_t iov;
+ uio_t uio;
+ void *buf = NULL;
+ off_t r_off = 0, w_off = 0;
+ ushort_t r_flag, w_flag;
+ size_t bsize = 0, wr_sz, nread, nwrite, total = 0;
+
+ /*
+ * Start by validating the inputs.
+ *
+ * Linux doesn't bother to check for valid flags, so neither do we.
+ * Also, aside from SPLICE_F_NONBLOCK, we ignore the rest of the
+ * flags since they're just hints to the Linux kernel implementation
+ * and have no effect on the proper functioning of the syscall.
+ */
+
+ if (len == 0)
+ return (0);
+
+ if ((fp_in = getf(fd_in)) == NULL) {
+ error = EBADF;
+ goto done;
+ }
+ switch (fp_in->f_vnode->v_type) {
+ case VFIFO:
+ /* A fifo that is not in fast mode does not count as a pipe */
+ if (((VTOF(fp_in->f_vnode))->fn_flag & FIFOFAST) != 0) {
+ found_pipe = B_TRUE;
+ pipe_in = B_TRUE;
+ }
+ /*FALLTHROUGH*/
+ case VSOCK:
+ if (off_in != NULL) {
+ error = ESPIPE;
+ goto done;
+ }
+ break;
+ case VREG:
+ case VBLK:
+ case VCHR:
+ case VPROC:
+ if (off_in != NULL) {
+ if (copyin(off_in, &r_off, sizeof (r_off)) != 0) {
+ error = EFAULT;
+ goto done;
+ }
+ rd_pos = B_TRUE;
+ }
+ break;
+ default:
+ error = EBADF;
+ goto done;
+ }
+ r_flag = fp_in->f_flag;
+ if ((r_flag & FREAD) == 0) {
+ error = EBADF;
+ goto done;
+ }
+
+ if ((fp_out = getf(fd_out)) == NULL) {
+ error = EBADF;
+ goto done;
+ }
+ switch (fp_out->f_vnode->v_type) {
+ case VFIFO:
+ found_pipe = B_TRUE;
+ /* Splicing to ourself returns EINVAL on Linux */
+ if (pipe_in) {
+ fifonode_t *fnp = VTOF(fp_in->f_vnode);
+ if (VTOF(fp_out->f_vnode) == fnp->fn_dest) {
+ error = EINVAL;
+ goto done;
+ }
+ }
+ /*FALLTHROUGH*/
+ case VSOCK:
+ if (off_out != NULL) {
+ error = ESPIPE;
+ goto done;
+ }
+ break;
+ case VREG:
+ case VBLK:
+ case VCHR:
+ case VPROC:
+ if (off_out != NULL) {
+ if (copyin(off_out, &w_off, sizeof (w_off)) != 0) {
+ error = EFAULT;
+ goto done;
+ }
+ wr_pos = B_TRUE;
+ }
+ break;
+ default:
+ error = EBADF;
+ goto done;
+ }
+ w_flag = fp_out->f_flag;
+ if ((w_flag & FWRITE) == 0) {
+ error = EBADF;
+ goto done;
+ }
+ /* Appending is invalid for output fd in splice */
+ if ((w_flag & FAPPEND) != 0) {
+ error = EINVAL;
+ goto done;
+ }
+
+ if (!found_pipe) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /*
+ * Check for non-blocking pipe operations. If no data in the input
+ * pipe, return EAGAIN. If the output pipe is full, return EAGAIN.
+ */
+ if (flags & LX_SPLICE_F_NONBLOCK) {
+ fifonode_t *fn_dest;
+
+ if (fp_in->f_vnode->v_type == VFIFO) {
+ fn_dest = VTOF(fp_in->f_vnode)->fn_dest;
+ if (fn_dest->fn_count == 0) {
+ error = EAGAIN;
+ goto done;
+ }
+ }
+ if (fp_out->f_vnode->v_type == VFIFO) {
+ fn_dest = VTOF(fp_out->f_vnode)->fn_dest;
+ fifolock_t *fn_lock = fn_dest->fn_lock;
+ mutex_enter(&fn_lock->flk_lock);
+ if (fn_dest->fn_count >= fn_dest->fn_hiwat) {
+ mutex_exit(&fn_lock->flk_lock);
+ error = EAGAIN;
+ goto done;
+ }
+ mutex_exit(&fn_lock->flk_lock);
+ }
+ }
+
+ bsize = MIN(LX_SPL_BUF_SIZE, len);
+
+ buf = kmem_alloc(bsize, KM_SLEEP);
+ bzero(&uio, sizeof (uio));
+ uio.uio_iovcnt = 1;
+ uio.uio_iov = &iov;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_llimit = curproc->p_fsz_ctl;
+
+ /*
+ * Loop reading data from fd_in and writing to fd_out. This is
+ * controlled by how much of the requested data we can actually write,
+ * particularly when the destination is a pipe. This matches the Linux
+ * behavior, which may terminate earlier than the full 'len' if the
+ * pipe fills up. However, we need to block when writing into a full
+ * pipe on the first iteration of the loop. We already checked above
+ * for a full output pipe when non-blocking.
+ */
+ while ((wr_sz = lx_spl_wr_sz(fp_out, w_off, bsize, len, first)) > 0) {
+ first = B_FALSE;
+
+ /* (re)setup for a read */
+ uio.uio_resid = iov.iov_len = wr_sz; /* only rd. max writable */
+ iov.iov_base = buf;
+ uio.uio_offset = r_off;
+ uio.uio_extflg = UIO_COPY_CACHED;
+ uio.uio_fmode = r_flag;
+ error = lx_spl_read(fp_in, &uio, &nread, pipe_in, rd_pos);
+ if (error != 0 || nread == 0)
+ break;
+ r_off = uio.uio_offset;
+
+ /* Setup and perform a write from the same buffer */
+ uio.uio_resid = iov.iov_len = nread;
+ iov.iov_base = buf;
+ uio.uio_offset = w_off;
+ uio.uio_extflg = UIO_COPY_DEFAULT;
+ uio.uio_fmode = w_flag;
+ error = lx_write_common(fp_out, &uio, &nwrite, wr_pos);
+ if (error != 0) {
+ if (pipe_in) {
+ /* Need to unblock reading from the fifo. */
+ fifonode_t *fnp = VTOF(fp_in->f_vnode);
+
+ mutex_enter(&fnp->fn_lock->flk_lock);
+ fnp->fn_flag &= ~FIFORDBLOCK;
+ fifo_stayfast_exit(fnp);
+ fifo_wakereader(fnp, fnp->fn_lock);
+ mutex_exit(&fnp->fn_lock->flk_lock);
+ }
+ break;
+ }
+ w_off = uio.uio_offset;
+
+ /*
+ * If input is a pipe, then we can consume the amount of data
+ * out of the pipe that we successfully wrote.
+ */
+ if (pipe_in)
+ lx_spl_consume(fp_in, nwrite);
+
+ total += nwrite;
+ len -= nwrite;
+ }
+
+done:
+ if (buf != NULL)
+ kmem_free(buf, bsize);
+ if (fp_in != NULL)
+ releasef(fd_in);
+ if (fp_out != NULL)
+ releasef(fd_out);
+ if (error != 0)
+ return (set_errno(error));
+
+ return (total);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_stat.c b/usr/src/uts/common/brand/lx/syscall/lx_stat.c
new file mode 100644
index 0000000000..0f5460816b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_stat.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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/model.h>
+#include <sys/mode.h>
+#include <sys/stat.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_types.h>
+#include <sys/lx_impl.h>
+#include <sys/brand.h>
+#include <sys/ddi.h>
+
+/* From "uts/common/syscall/stat.c" */
+extern int cstatat_getvp(int, char *, int, vnode_t **, cred_t **);
+
+typedef struct lx_timespec32 {
+ int32_t ts_sec;
+ int32_t ts_nsec;
+} lx_timespec32_t;
+
+typedef struct lx_timespec64 {
+ int64_t ts_sec;
+ int64_t ts_nsec;
+}lx_timespec64_t;
+
+struct lx_stat32 {
+ uint16_t st_dev;
+ uint16_t st_pad1;
+ uint32_t st_ino;
+ uint16_t st_mode;
+ uint16_t st_nlink;
+ uint16_t st_uid;
+ uint16_t st_gid;
+ uint16_t st_rdev;
+ uint16_t st_pad2;
+ uint32_t st_size;
+ uint32_t st_blksize;
+ uint32_t st_blocks;
+ lx_timespec32_t st_atime;
+ lx_timespec32_t st_mtime;
+ lx_timespec32_t st_ctime;
+ uint32_t st_pad3;
+ uint32_t st_pad4;
+};
+
+#pragma pack(4)
+struct lx_stat64_32 {
+ uint64_t st_dev;
+ uint32_t st_pad1;
+ uint32_t st_small_ino;
+ uint32_t st_mode;
+ uint32_t st_nlink;
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint64_t st_rdev;
+ uint32_t st_pad2;
+ uint64_t st_size;
+ uint32_t st_blksize;
+ uint64_t st_blocks;
+ lx_timespec32_t st_atime;
+ lx_timespec32_t st_mtime;
+ lx_timespec32_t st_ctime;
+ uint64_t st_ino;
+};
+#pragma pack()
+
+#if defined(_LP64)
+struct lx_stat64_64 {
+ uint64_t st_dev;
+ uint64_t st_ino;
+ uint64_t st_nlink; /* yes, the order really is */
+ uint32_t st_mode; /* different for these two */
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint32_t st_pad0;
+ uint64_t st_rdev;
+ int64_t st_size;
+ int64_t st_blksize;
+ int64_t st_blocks;
+ lx_timespec64_t st_atime;
+ lx_timespec64_t st_mtime;
+ lx_timespec64_t st_ctime;
+ int64_t st_unused[3];
+};
+#endif /* defined(_LP64) */
+
+typedef enum lx_stat_fmt {
+ LXF_STAT32,
+ LXF_STAT64_32,
+ LXF_STAT64_64
+} lx_stat_fmt_t;
+
+static void
+lx_stat_xlate_dev(vattr_t *vattr)
+{
+ lx_zone_data_t *lxzd = ztolxzd(curproc->p_zone);
+ dev_t dev;
+ lx_virt_disk_t *vd;
+ boolean_t is_dev;
+
+ if (S_ISCHR(vattr->va_mode) || S_ISBLK(vattr->va_mode)) {
+ dev = vattr->va_rdev;
+ is_dev = B_TRUE;
+ } else {
+ dev = vattr->va_fsid;
+ is_dev = B_FALSE;
+ }
+
+ /*
+ * See if this is the /dev/zfs device. If it is, the device number has
+ * already been converted to Linux format in the lx devfs so we have
+ * to check for that and not a native major/minor style.
+ */
+ if (S_ISCHR(vattr->va_mode) &&
+ LX_GETMAJOR(dev) == getmajor(lxzd->lxzd_zfs_dev) &&
+ LX_GETMINOR(dev) == 0) {
+ /*
+ * We use the /dev/zfs device as a placeholder for our in-zone
+ * fabricated /dev/zfsds0 device that we're pretending / is
+ * mounted on. lx_zone_get_zfsds has pre-allocated this
+ * entry in the emulated device list. Reset dev so we can
+ * properly match in the following loop.
+ */
+ dev = curproc->p_zone->zone_rootvp->v_vfsp->vfs_dev;
+ }
+
+ /* Substitute emulated major/minor on zvols or mounted datasets. */
+ vd = list_head(lxzd->lxzd_vdisks);
+ while (vd != NULL) {
+ if (vd->lxvd_real_dev == dev) {
+ dev = vd->lxvd_emul_dev;
+ /*
+ * We only update rdev for matching zfds/zvol devices
+ * so that the other devices are unchanged.
+ */
+ if (is_dev) {
+ vattr->va_rdev = LX_MAKEDEVICE(getmajor(dev),
+ getminor(dev));
+ }
+ break;
+ }
+ vd = list_next(lxzd->lxzd_vdisks, vd);
+ }
+
+ /* Mangle st_dev into expected format */
+ vattr->va_fsid = LX_MAKEDEVICE(getmajor(dev), getminor(dev));
+}
+
+static long
+lx_stat_common(vnode_t *vp, cred_t *cr, void *outp, lx_stat_fmt_t fmt,
+ int follow)
+{
+ vattr_t vattr;
+ mode_t mode;
+ int error, flags;
+
+ /*
+ * When symlink following is desired, the ATTR_REAL flag is necessary
+ * to circumvent some of the weird behavior present in filesystems like
+ * lx_proc.
+ */
+ flags = (follow == FOLLOW) ? ATTR_REAL : 0;
+
+ vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE;
+ if ((error = VOP_GETATTR(vp, &vattr, flags, cr, NULL)) != 0) {
+ return (error);
+ }
+
+ mode = VTTOIF(vattr.va_type) | vattr.va_mode;
+ if ((mode & S_IFMT) == S_IFBLK) {
+ /* Linux seems to report a 0 st_size for all block devices */
+ vattr.va_size = 0;
+ }
+ if (vattr.va_rdev == NODEV) {
+ /* Linux leaves st_rdev zeroed when it is absent */
+ vattr.va_rdev = 0;
+ }
+
+ lx_stat_xlate_dev(&vattr);
+
+ if (fmt == LXF_STAT32) {
+ struct lx_stat32 sb;
+
+ if (vattr.va_fsid > USHRT_MAX || vattr.va_rdev > USHRT_MAX ||
+ vattr.va_nlink > USHRT_MAX || vattr.va_size > INT_MAX) {
+ return (EOVERFLOW);
+ }
+
+ bzero(&sb, sizeof (sb));
+ sb.st_dev = vattr.va_fsid;
+ sb.st_ino = vattr.va_nodeid;
+ sb.st_mode = mode;
+ sb.st_nlink = vattr.va_nlink;
+ sb.st_uid = LX_UID32_TO_UID16(vattr.va_uid);
+ sb.st_gid = LX_GID32_TO_GID16(vattr.va_gid);
+ sb.st_rdev = vattr.va_rdev;
+ sb.st_size = vattr.va_size;
+ sb.st_blksize = vattr.va_blksize;
+ sb.st_blocks = vattr.va_nblocks;
+ sb.st_atime.ts_sec = vattr.va_atime.tv_sec;
+ sb.st_atime.ts_nsec = vattr.va_atime.tv_nsec;
+ sb.st_mtime.ts_sec = vattr.va_mtime.tv_sec;
+ sb.st_mtime.ts_nsec = vattr.va_mtime.tv_nsec;
+ sb.st_ctime.ts_sec = vattr.va_ctime.tv_sec;
+ sb.st_ctime.ts_nsec = vattr.va_ctime.tv_nsec;
+ if (copyout(&sb, outp, sizeof (sb)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ } else if (fmt == LXF_STAT64_32) {
+ struct lx_stat64_32 sb;
+
+ bzero(&sb, sizeof (sb));
+ sb.st_dev = vattr.va_fsid;
+ sb.st_ino = vattr.va_nodeid;
+ sb.st_small_ino = (vattr.va_nodeid & UINT_MAX);
+ sb.st_mode = mode;
+ sb.st_nlink = vattr.va_nlink;
+ sb.st_uid = vattr.va_uid;
+ sb.st_gid = vattr.va_gid;
+ sb.st_rdev = vattr.va_rdev;
+ sb.st_size = vattr.va_size;
+ sb.st_blksize = vattr.va_blksize;
+ sb.st_blocks = vattr.va_nblocks;
+ sb.st_atime.ts_sec = vattr.va_atime.tv_sec;
+ sb.st_atime.ts_nsec = vattr.va_atime.tv_nsec;
+ sb.st_mtime.ts_sec = vattr.va_mtime.tv_sec;
+ sb.st_mtime.ts_nsec = vattr.va_mtime.tv_nsec;
+ sb.st_ctime.ts_sec = vattr.va_ctime.tv_sec;
+ sb.st_ctime.ts_nsec = vattr.va_ctime.tv_nsec;
+ if (copyout(&sb, outp, sizeof (sb)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ } else if (fmt == LXF_STAT64_64) {
+#if defined(_LP64)
+ struct lx_stat64_64 sb;
+
+ bzero(&sb, sizeof (sb));
+ sb.st_dev = vattr.va_fsid;
+ sb.st_ino = vattr.va_nodeid;
+ sb.st_mode = mode;
+ sb.st_nlink = vattr.va_nlink;
+ sb.st_uid = vattr.va_uid;
+ sb.st_gid = vattr.va_gid;
+ sb.st_rdev = vattr.va_rdev;
+ sb.st_size = vattr.va_size;
+ sb.st_blksize = vattr.va_blksize;
+ sb.st_blocks = vattr.va_nblocks;
+ sb.st_atime.ts_sec = vattr.va_atime.tv_sec;
+ sb.st_atime.ts_nsec = vattr.va_atime.tv_nsec;
+ sb.st_mtime.ts_sec = vattr.va_mtime.tv_sec;
+ sb.st_mtime.ts_nsec = vattr.va_mtime.tv_nsec;
+ sb.st_ctime.ts_sec = vattr.va_ctime.tv_sec;
+ sb.st_ctime.ts_nsec = vattr.va_ctime.tv_nsec;
+ if (copyout(&sb, outp, sizeof (sb)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+#else
+ /* Invalid output format on 32-bit */
+ VERIFY(0);
+#endif
+ }
+
+ /* Invalid output format */
+ VERIFY(0);
+ return (0);
+}
+
+long
+lx_stat32(char *name, void *outp)
+{
+ vnode_t *vp = NULL;
+ cred_t *cr = NULL;
+ int error;
+
+ if ((error = cstatat_getvp(AT_FDCWD, name, FOLLOW, &vp, &cr)) != 0) {
+ return (set_errno(error));
+ }
+ error = lx_stat_common(vp, cr, outp, LXF_STAT32, FOLLOW);
+ VN_RELE(vp);
+ crfree(cr);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fstat32(int fd, void *outp)
+{
+ file_t *fp;
+ int error;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ error = lx_stat_common(fp->f_vnode, fp->f_cred, outp, LXF_STAT32,
+ FOLLOW);
+ releasef(fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_lstat32(char *name, void *outp)
+{
+ vnode_t *vp = NULL;
+ cred_t *cr = NULL;
+ int error;
+
+ if ((error = cstatat_getvp(AT_FDCWD, name, NO_FOLLOW, &vp, &cr)) != 0) {
+ return (set_errno(error));
+ }
+ error = lx_stat_common(vp, cr, outp, LXF_STAT32, NO_FOLLOW);
+ VN_RELE(vp);
+ crfree(cr);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_stat64(char *name, void *outp)
+{
+ vnode_t *vp = NULL;
+ cred_t *cr = NULL;
+ model_t model = get_udatamodel();
+ int error;
+
+ if ((error = cstatat_getvp(AT_FDCWD, name, FOLLOW, &vp, &cr)) != 0) {
+ return (set_errno(error));
+ }
+ error = lx_stat_common(vp, cr, outp,
+ (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32, FOLLOW);
+ VN_RELE(vp);
+ crfree(cr);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fstat64(int fd, void *outp)
+{
+ file_t *fp;
+ model_t model = get_udatamodel();
+ int error;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ error = lx_stat_common(fp->f_vnode, fp->f_cred, outp,
+ (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32, FOLLOW);
+ releasef(fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+#define LX_FSTATAT_ALLOWED (LX_AT_SYMLINK_NOFOLLOW | LX_AT_EMPTY_PATH | \
+ LX_AT_NO_AUTOMOUNT)
+
+long
+lx_fstatat64(int fd, char *name, void *outp, int flag)
+{
+ vnode_t *vp = NULL;
+ cred_t *cr = NULL;
+ model_t model = get_udatamodel();
+ int follow = FOLLOW;
+ int error;
+ char c;
+
+ if (fd == LX_AT_FDCWD) {
+ fd = AT_FDCWD;
+ }
+ if ((flag & ~LX_FSTATAT_ALLOWED) != 0) {
+ return (set_errno(EINVAL));
+ }
+ if ((flag & LX_AT_SYMLINK_NOFOLLOW) != 0) {
+ follow = NO_FOLLOW;
+ }
+
+ if ((flag & LX_AT_NO_AUTOMOUNT) != 0)
+ follow |= __FLXNOAUTO;
+
+ if (copyin(name, &c, sizeof (c)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ if (c == '\0') {
+ if ((flag & LX_AT_EMPTY_PATH) == 0) {
+ return (set_errno(ENOENT));
+ }
+
+ /*
+ * When AT_EMPTY_PATH is set and and empty string has been
+ * passed for the name parameter, direct the lookup against the
+ * vnode for that fd.
+ */
+ if (fd == AT_FDCWD) {
+ mutex_enter(&curproc->p_lock);
+ vp = PTOU(curproc)->u_cdir;
+ VN_HOLD(vp);
+ mutex_exit(&curproc->p_lock);
+ cr = CRED();
+ crhold(cr);
+ } else {
+ file_t *fp;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+ vp = fp->f_vnode;
+ VN_HOLD(vp);
+ cr = fp->f_cred;
+ crhold(cr);
+ releasef(fd);
+ }
+ } else {
+ if ((error = cstatat_getvp(fd, name, follow, &vp, &cr)) != 0) {
+ return (set_errno(error));
+ }
+ }
+
+ error = lx_stat_common(vp, cr, outp,
+ (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32, follow);
+ VN_RELE(vp);
+ crfree(cr);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_lstat64(char *name, void *outp)
+{
+ vnode_t *vp = NULL;
+ cred_t *cr = NULL;
+ model_t model = get_udatamodel();
+ int error;
+
+ if ((error = cstatat_getvp(AT_FDCWD, name, NO_FOLLOW, &vp, &cr)) != 0) {
+ return (set_errno(error));
+ }
+ error = lx_stat_common(vp, cr, outp,
+ (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32,
+ NO_FOLLOW);
+ VN_RELE(vp);
+ crfree(cr);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_sync.c b/usr/src/uts/common/brand/lx/syscall/lx_sync.c
new file mode 100644
index 0000000000..614afca0b0
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_sync.c
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+
+long
+lx_syncfs(int fd)
+{
+ file_t *fp;
+ vfs_t *vfsp;
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ vfsp = fp->f_vnode->v_vfsp;
+ releasef(fd);
+
+ (void) (vfsp->vfs_op->vfs_sync)(vfsp, 0, CRED());
+
+ return (0);
+}
+
+#define LX_SYNC_FILE_RANGE_WAIT_BEFORE 0x1
+#define LX_SYNC_FILE_RANGE_WRITE 0x2
+#define LX_SYNC_FILE_RANGE_WAIT_AFTER 0x4
+
+#define LX_SYNC_FILE_RANGE_VALID (LX_SYNC_FILE_RANGE_WAIT_BEFORE | \
+ LX_SYNC_FILE_RANGE_WRITE | LX_SYNC_FILE_RANGE_WAIT_AFTER)
+
+
+long
+lx_sync_file_range(int fd, off_t offset, off_t nbytes, int flags)
+{
+ file_t *fp;
+ int error, sflags = 0;
+
+ if ((flags & ~LX_SYNC_FILE_RANGE_VALID) != 0)
+ return (set_errno(EINVAL));
+ if (offset < 0 || nbytes < 0)
+ return (set_errno(EINVAL));
+
+ if ((fp = getf(fd)) == NULL)
+ return (set_errno(EBADF));
+
+ /*
+ * Since sync_file_range is implemented in terms of VOP_PUTPAGE, both
+ * SYNC_FILE_RANGE_WAIT flags are treated as forcing synchronous
+ * operation. While this differs from the Linux behavior where
+ * BEFORE/AFTER are distinct, it achieves an adequate level of safety
+ * since the requested data is synced out at the end of the call.
+ */
+ if ((flags & (LX_SYNC_FILE_RANGE_WAIT_BEFORE |
+ LX_SYNC_FILE_RANGE_WAIT_AFTER)) == 0) {
+ sflags |= B_ASYNC;
+ }
+
+ error = VOP_PUTPAGE(fp->f_vnode, offset, nbytes, sflags, CRED(), NULL);
+ if (error == ENOSYS) {
+ error = ESPIPE;
+ }
+
+ releasef(fd);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_sysinfo.c b/usr/src/uts/common/brand/lx/syscall/lx_sysinfo.c
new file mode 100644
index 0000000000..052ad322a7
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_sysinfo.c
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ * Copyright 2017 Joyent, Inc.
+ */
+
+#include <vm/anon.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/zone.h>
+#include <sys/time.h>
+
+typedef struct lx_sysinfo {
+ int64_t si_uptime; /* Seconds since boot */
+ uint64_t si_loads[3]; /* 1, 5, and 15 minute avg runq length */
+ uint64_t si_totalram; /* Total memory size */
+ uint64_t si_freeram; /* Available memory */
+ uint64_t si_sharedram; /* Shared memory */
+ uint64_t si_bufferram; /* Buffer memory */
+ uint64_t si_totalswap; /* Total swap space */
+ uint64_t si_freeswap; /* Avail swap space */
+ uint16_t si_procs; /* Process count */
+ uint16_t si_pad; /* Padding */
+ uint64_t si_totalhigh; /* High memory size */
+ uint64_t si_freehigh; /* Avail high memory */
+ uint32_t si_mem_unit; /* Unit size of memory fields */
+} lx_sysinfo_t;
+
+#if defined(_SYSCALL32_IMPL)
+/*
+ * 64-bit kernel view of the 32-bit usermode struct.
+ */
+#pragma pack(4)
+typedef struct lx_sysinfo32 {
+ int32_t si_uptime; /* Seconds since boot */
+ uint32_t si_loads[3]; /* 1, 5, and 15 minute avg runq length */
+ uint32_t si_totalram; /* Total memory size */
+ uint32_t si_freeram; /* Available memory */
+ uint32_t si_sharedram; /* Shared memory */
+ uint32_t si_bufferram; /* Buffer memory */
+ uint32_t si_totalswap; /* Total swap space */
+ uint32_t si_freeswap; /* Avail swap space */
+ uint16_t si_procs; /* Process count */
+ uint16_t si_pad; /* Padding */
+ uint32_t si_totalhigh; /* High memory size */
+ uint32_t si_freehigh; /* Avail high memory */
+ uint32_t si_mem_unit; /* Unit size of memory fields */
+ char __si_pad[8];
+} lx_sysinfo32_t;
+#pragma pack()
+#endif
+
+extern pgcnt_t swapfs_minfree;
+
+static void
+lx_sysinfo_common(lx_sysinfo_t *si)
+{
+ zone_t *zone = curzone;
+ pgcnt_t zphysmem, zfreemem;
+ ulong_t ztotswap, zfreeswap;
+
+ si->si_uptime = gethrestime_sec() - zone->zone_boot_time;
+
+ si->si_loads[0] = zone->zone_hp_avenrun[0];
+ si->si_loads[1] = zone->zone_hp_avenrun[1];
+ si->si_loads[2] = zone->zone_hp_avenrun[2];
+
+ /*
+ * In linux each thread looks like a process, so we conflate the
+ * two in this stat as well.
+ */
+ si->si_procs = (int32_t)zone->zone_nlwps;
+
+ zone_get_physmem_data(zone->zone_id, &zphysmem, &zfreemem);
+
+ if (zone->zone_max_swap_ctl == UINT64_MAX) {
+ ztotswap = k_anoninfo.ani_max;
+ zfreeswap = k_anoninfo.ani_free;
+ } else {
+ /*
+ * See the comment in swapctl for a description of how free is
+ * calculated within a zone.
+ */
+ rctl_qty_t used;
+ spgcnt_t avail;
+ uint64_t max;
+
+ avail = MAX((spgcnt_t)(availrmem - swapfs_minfree), 0);
+ max = k_anoninfo.ani_max + k_anoninfo.ani_mem_resv + avail;
+
+ mutex_enter(&zone->zone_mem_lock);
+ ztotswap = btop(zone->zone_max_swap_ctl);
+ used = btop(zone->zone_max_swap);
+ mutex_exit(&zone->zone_mem_lock);
+
+ zfreeswap = MIN(ztotswap, max) - used;
+ }
+
+ /*
+ * If the maximum memory stat is less than 1^20 pages (i.e. 4GB),
+ * then we report the result in bytes. Otherwise we use pages.
+ * Once we start supporting >1TB systems/zones, we'll need a third
+ * option.
+ */
+ if (MAX(zphysmem, ztotswap) < 1024 * 1024) {
+ si->si_totalram = ptob(zphysmem);
+ si->si_freeram = ptob(zfreemem);
+ si->si_totalswap = ptob(ztotswap);
+ si->si_freeswap = ptob(zfreeswap);
+ si->si_mem_unit = 1;
+ } else {
+ si->si_totalram = zphysmem;
+ si->si_freeram = zfreemem;
+ si->si_totalswap = ztotswap;
+ si->si_freeswap = zfreeswap;
+ si->si_mem_unit = PAGESIZE;
+ }
+ si->si_bufferram = 0;
+ si->si_sharedram = 0;
+
+ /*
+ * These two stats refer to high physical memory. If an
+ * application running in a Linux zone cares about this, then
+ * either it or we are broken.
+ */
+ si->si_totalhigh = 0;
+ si->si_freehigh = 0;
+}
+
+long
+lx_sysinfo64(caddr_t sip)
+{
+ lx_sysinfo_t si;
+
+ bzero(&si, sizeof (si));
+ lx_sysinfo_common(&si);
+
+ if (copyout(&si, sip, sizeof (si)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+
+#if defined(_SYSCALL32_IMPL)
+long
+lx_sysinfo32(caddr_t sip)
+{
+ lx_sysinfo_t si;
+ lx_sysinfo32_t si32;
+ int i;
+
+ lx_sysinfo_common(&si);
+
+ /*
+ * Convert the lx_sysinfo_t into the legacy 32-bit view:
+ */
+ bzero(&si32, sizeof (si32));
+ si32.si_uptime = si.si_uptime;
+
+ for (i = 0; i < 3; i++) {
+ if ((si.si_loads[i]) > 0x7fffffff)
+ si32.si_loads[i] = 0x7fffffff;
+ else
+ si32.si_loads[i] = si.si_loads[i];
+ }
+
+ si32.si_procs = si.si_procs;
+ si32.si_totalram = si.si_totalram;
+ si32.si_freeram = si.si_freeram;
+ si32.si_totalswap = si.si_totalswap;
+ si32.si_freeswap = si.si_freeswap;
+ si32.si_mem_unit = si.si_mem_unit;
+
+ si32.si_bufferram = si.si_bufferram;
+ si32.si_sharedram = si.si_sharedram;
+
+ si32.si_totalhigh = si.si_totalhigh;
+ si32.si_freehigh = si.si_freehigh;
+
+ if (copyout(&si32, sip, sizeof (si32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+#endif
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_thread_area.c b/usr/src/uts/common/brand/lx/syscall/lx_thread_area.c
new file mode 100644
index 0000000000..a84c17e139
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_thread_area.c
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ * Copyright 2018 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/cpuvar.h>
+#include <sys/archsystm.h>
+#include <sys/proc.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_ldt.h>
+#include <sys/lx_misc.h>
+#include <sys/x86_archext.h>
+#include <sys/controlregs.h>
+#include <lx_syscall.h>
+
+/* ARGSUSED */
+long
+lx_arch_prctl(int code, ulong_t addr)
+{
+#if defined(__amd64)
+ klwp_t *lwp = ttolwp(curthread);
+ lx_lwp_data_t *llwp = lwptolxlwp(lwp);
+ pcb_t *pcb = &lwp->lwp_pcb;
+
+ switch (code) {
+ case LX_ARCH_GET_FS:
+ if (copyout(&llwp->br_lx_fsbase, (void *)addr,
+ sizeof (llwp->br_lx_fsbase)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ break;
+
+ case LX_ARCH_SET_FS:
+ llwp->br_lx_fsbase = addr;
+
+ kpreempt_disable();
+ if (pcb->pcb_fsbase != llwp->br_lx_fsbase) {
+ pcb->pcb_fsbase = llwp->br_lx_fsbase;
+
+ /*
+ * Ensure we go out via update_sregs.
+ */
+ PCB_SET_UPDATE_SEGS(pcb);
+ }
+ kpreempt_enable();
+ break;
+
+ case LX_ARCH_GET_GS:
+ if (copyout(&llwp->br_lx_gsbase, (void *)addr,
+ sizeof (llwp->br_lx_gsbase)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ break;
+
+ case LX_ARCH_SET_GS:
+ llwp->br_lx_gsbase = addr;
+
+ kpreempt_disable();
+ if (pcb->pcb_gsbase != llwp->br_lx_gsbase) {
+ pcb->pcb_gsbase = llwp->br_lx_gsbase;
+
+ /*
+ * Ensure we go out via update_sregs.
+ */
+ PCB_SET_UPDATE_SEGS(pcb);
+ }
+ kpreempt_enable();
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+#endif
+
+ return (0);
+}
+
+long
+lx_get_thread_area(struct ldt_info *inf)
+{
+ struct lx_lwp_data *jlwp = ttolxlwp(curthread);
+ struct ldt_info ldt_inf;
+ user_desc_t *dscrp;
+ int entry;
+
+ if (fuword32(&inf->entry_number, (uint32_t *)&entry))
+ return (set_errno(EFAULT));
+
+ if (entry < GDT_TLSMIN || entry > GDT_TLSMAX)
+ return (set_errno(EINVAL));
+
+ dscrp = jlwp->br_tls + entry - GDT_TLSMIN;
+
+ /*
+ * convert the solaris ldt to the linux format expected by the
+ * caller
+ */
+ DESC_TO_LDT_INFO(dscrp, &ldt_inf);
+ ldt_inf.entry_number = entry;
+
+ if (copyout(&ldt_inf, inf, sizeof (struct ldt_info)))
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+long
+lx_set_thread_area(struct ldt_info *inf)
+{
+ struct lx_lwp_data *jlwp = ttolxlwp(curthread);
+ struct ldt_info ldt_inf;
+ user_desc_t *dscrp;
+ int entry;
+ int i;
+
+ if (copyin(inf, &ldt_inf, sizeof (ldt_inf)))
+ return (set_errno(EFAULT));
+
+ entry = ldt_inf.entry_number;
+ if (entry == -1) {
+ /*
+ * Find an empty entry in the tls for this thread.
+ * The casts assume each user_desc_t entry is 8 bytes.
+ */
+ for (i = 0, dscrp = jlwp->br_tls; i < LX_TLSNUM; i++, dscrp++) {
+ if (((uint_t *)dscrp)[0] == 0 &&
+ ((uint_t *)dscrp)[1] == 0)
+ break;
+ }
+
+ if (i < LX_TLSNUM) {
+ /*
+ * found one
+ */
+ entry = i + GDT_TLSMIN;
+ if (suword32(&inf->entry_number, entry))
+ return (set_errno(EFAULT));
+ } else {
+ return (set_errno(ESRCH));
+ }
+ }
+
+ if (entry < GDT_TLSMIN || entry > GDT_TLSMAX)
+ return (set_errno(EINVAL));
+
+ /*
+ * convert the linux ldt info to standard intel descriptor
+ */
+ dscrp = jlwp->br_tls + entry - GDT_TLSMIN;
+
+ if (LDT_INFO_EMPTY(&ldt_inf)) {
+ ((uint_t *)dscrp)[0] = 0;
+ ((uint_t *)dscrp)[1] = 0;
+ } else {
+ LDT_INFO_TO_DESC(&ldt_inf, dscrp);
+ }
+
+ /*
+ * update the gdt with the new descriptor
+ */
+ kpreempt_disable();
+
+ for (i = 0, dscrp = jlwp->br_tls; i < LX_TLSNUM; i++, dscrp++)
+ lx_set_gdt(GDT_TLSMIN + i, dscrp);
+
+ kpreempt_enable();
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_time.c b/usr/src/uts/common/brand/lx/syscall/lx_time.c
new file mode 100644
index 0000000000..b9bc8e5ab4
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_time.c
@@ -0,0 +1,72 @@
+/*
+ * 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 2017, Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/times.h>
+#include <sys/msacct.h>
+#include <sys/lx_userhz.h>
+
+/* See the comment on LX_USERHZ for more details. */
+#define LX_NSEC_PER_USERHZ (NANOSEC / LX_USERHZ)
+#define NSEC_TO_LX_USERHZ(nsec) ((nsec) / LX_NSEC_PER_USERHZ)
+
+/*
+ * Our times(2) implementation is based on the native times(2), but with
+ * the necessary scaling to adjust to USER_HZ. Also, Linux avoids writing
+ * to a NULL tp, whereas our native code returns EFAULT.
+ */
+long
+lx_times(struct tms *tp)
+{
+ proc_t *p = curproc;
+ struct tms p_time;
+ clock_t ret_lbolt;
+
+ mutex_enter(&p->p_lock);
+ p_time.tms_utime =
+ (clock_t)NSEC_TO_LX_USERHZ(mstate_aggr_state(p, LMS_USER));
+ p_time.tms_stime =
+ (clock_t)NSEC_TO_LX_USERHZ(mstate_aggr_state(p, LMS_SYSTEM));
+ p_time.tms_cutime = HZ_TO_LX_USERHZ(p->p_cutime);
+ p_time.tms_cstime = HZ_TO_LX_USERHZ(p->p_cstime);
+ mutex_exit(&p->p_lock);
+
+#ifdef _SYSCALL32_IMPL
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ struct tms32 t32;
+
+ t32.tms_utime = p_time.tms_utime;
+ t32.tms_stime = p_time.tms_stime;
+ t32.tms_cutime = p_time.tms_cutime;
+ t32.tms_cstime = p_time.tms_cstime;
+
+ if (tp != NULL && copyout(&t32, tp, sizeof (t32)) != 0)
+ return (set_errno(EFAULT));
+
+ ret_lbolt = ddi_get_lbolt();
+ return ((clock32_t)HZ_TO_LX_USERHZ(ret_lbolt));
+ } else
+#endif /* _SYSCALL32_IMPL */
+ {
+ if (tp != NULL && copyout(&p_time, tp, sizeof (p_time)) != 0)
+ return (set_errno(EFAULT));
+
+ ret_lbolt = ddi_get_lbolt();
+ return (HZ_TO_LX_USERHZ(ret_lbolt));
+ }
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_timer.c b/usr/src/uts/common/brand/lx/syscall/lx_timer.c
new file mode 100644
index 0000000000..279bdbddc7
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_timer.c
@@ -0,0 +1,637 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * The illumos kernel provides two clock backends: CLOCK_REALTIME, the
+ * adjustable system wall clock; and CLOCK_HIGHRES, the monotonically
+ * increasing time source that is not subject to drift or adjustment. By
+ * contrast, the Linux kernel is furnished with an overabundance of narrowly
+ * differentiated clock types.
+ *
+ * Fortunately, most of the commonly used Linux clock types are either similar
+ * enough to the native clock backends that they can be directly mapped, or
+ * represent queries to the per-process and per-LWP microstate counters.
+ *
+ * CLOCK_BOOTTIME is identical to CLOCK_MONOTONIC, except that it takes into
+ * account time that the system is suspended. Since that is uninteresting to
+ * us, we treat it the same.
+ */
+
+#include <sys/time.h>
+#include <sys/systm.h>
+#include <sys/cmn_err.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+#include <lx_signum.h>
+
+/*
+ * From "uts/common/os/timer.c":
+ */
+extern int clock_settime(clockid_t, timespec_t *);
+extern int clock_gettime(clockid_t, timespec_t *);
+extern int clock_getres(clockid_t, timespec_t *);
+extern int nanosleep(timespec_t *, timespec_t *);
+
+
+static int lx_emul_clock_getres(clockid_t, timespec_t *);
+static int lx_emul_clock_gettime(clockid_t, timespec_t *);
+static int lx_emul_clock_settime(clockid_t, timespec_t *);
+
+typedef struct lx_clock_backend {
+ clockid_t lclk_ntv_id;
+ int (*lclk_clock_getres)(clockid_t, timespec_t *);
+ int (*lclk_clock_gettime)(clockid_t, timespec_t *);
+ int (*lclk_clock_settime)(clockid_t, timespec_t *);
+} lx_clock_backend_t;
+
+/*
+ * NOTE: The Linux man pages state this structure is obsolete and is
+ * unsupported, so it is declared here for sizing purposes only.
+ */
+struct lx_timezone {
+ int tz_minuteswest; /* minutes W of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+
+/*
+ * Use the native clock_* system call implementation, but with a translated
+ * clock identifier:
+ */
+#define NATIVE(ntv_id) \
+ { ntv_id, clock_getres, clock_gettime, clock_settime }
+
+/*
+ * This backend is not supported, so we provide an emulation handler:
+ */
+#define EMUL(ntv_id) \
+ { ntv_id, lx_emul_clock_getres, lx_emul_clock_gettime, \
+ lx_emul_clock_settime }
+
+static lx_clock_backend_t lx_clock_backends[] = {
+ NATIVE(CLOCK_REALTIME), /* LX_CLOCK_REALTIME */
+ NATIVE(CLOCK_HIGHRES), /* LX_CLOCK_MONOTONIC */
+ EMUL(CLOCK_PROCESS_CPUTIME_ID), /* LX_CLOCK_PROCESS_CPUTIME_ID */
+ EMUL(CLOCK_THREAD_CPUTIME_ID), /* LX_CLOCK_THREAD_CPUTIME_ID */
+ NATIVE(CLOCK_HIGHRES), /* LX_CLOCK_MONOTONIC_RAW */
+ NATIVE(CLOCK_REALTIME), /* LX_CLOCK_REALTIME_COARSE */
+ NATIVE(CLOCK_HIGHRES), /* LX_CLOCK_MONOTONIC_COARSE */
+ NATIVE(CLOCK_HIGHRES) /* LX_CLOCK_BOOTTIME */
+};
+
+#define LX_CLOCK_MAX \
+ (sizeof (lx_clock_backends) / sizeof (lx_clock_backends[0]))
+#define LX_CLOCK_BACKEND(clk) (((clk) < LX_CLOCK_MAX && (clk) >= 0) ? \
+ &lx_clock_backends[(clk)] : NULL)
+
+/*
+ * Linux defines the size of the sigevent structure to be 64 bytes. In order
+ * to meet that definition, the trailing union includes a member which pads it
+ * out to the desired length for the given architecture.
+ */
+#define LX_SIGEV_PAD_SIZE ((64 - \
+ (sizeof (int) * 2 + sizeof (union sigval))) / sizeof (int))
+
+typedef struct {
+ union sigval lx_sigev_value;
+ int lx_sigev_signo;
+ int lx_sigev_notify;
+ union {
+ int lx_pad[LX_SIGEV_PAD_SIZE];
+ int lx_tid;
+ struct {
+ void (*lx_notify_function)(union sigval);
+ void *lx_notify_attribute;
+ } lx_sigev_thread;
+ } lx_sigev_un;
+} lx_sigevent_t;
+
+
+#ifdef _SYSCALL32_IMPL
+
+#define LX_SIGEV32_PAD_SIZE ((64 - \
+ (sizeof (int) * 2 + sizeof (union sigval32))) / sizeof (int))
+
+typedef struct {
+ union sigval32 lx_sigev_value;
+ int lx_sigev_signo;
+ int lx_sigev_notify;
+ union {
+ int lx_pad[LX_SIGEV32_PAD_SIZE];
+ int lx_tid;
+ struct {
+ caddr32_t lx_notify_function;
+ caddr32_t lx_notify_attribute;
+ } lx_sigev_thread;
+ } lx_sigev_un;
+} lx_sigevent32_t;
+
+#endif /* _SYSCALL32_IMPL */
+
+#define LX_SIGEV_SIGNAL 0
+#define LX_SIGEV_NONE 1
+#define LX_SIGEV_THREAD 2
+#define LX_SIGEV_THREAD_ID 4
+
+/*
+ * Access private SIGEV_THREAD_ID callback state in itimer_t
+ */
+#define LX_SIGEV_THREAD_ID_LPID(it) ((it)->it_cb_data[0])
+#define LX_SIGEV_THREAD_ID_TID(it) ((it)->it_cb_data[1])
+
+
+/* ARGSUSED */
+static int
+lx_emul_clock_settime(clockid_t clock, timespec_t *tp)
+{
+ return (set_errno(EINVAL));
+}
+
+static int
+lx_emul_clock_gettime(clockid_t clock, timespec_t *tp)
+{
+ timespec_t t;
+
+ switch (clock) {
+ case CLOCK_PROCESS_CPUTIME_ID: {
+ proc_t *p = ttoproc(curthread);
+ hrtime_t snsecs, unsecs;
+
+ /*
+ * Based on getrusage() in "rusagesys.c":
+ */
+ mutex_enter(&p->p_lock);
+ unsecs = mstate_aggr_state(p, LMS_USER);
+ snsecs = mstate_aggr_state(p, LMS_SYSTEM);
+ mutex_exit(&p->p_lock);
+
+ hrt2ts(unsecs + snsecs, &t);
+ break;
+ }
+
+ case CLOCK_THREAD_CPUTIME_ID: {
+ klwp_t *lwp = ttolwp(curthread);
+ struct mstate *ms = &lwp->lwp_mstate;
+ hrtime_t snsecs, unsecs;
+
+ /*
+ * Based on getrusage_lwp() in "rusagesys.c":
+ */
+ unsecs = ms->ms_acct[LMS_USER];
+ snsecs = ms->ms_acct[LMS_SYSTEM] + ms->ms_acct[LMS_TRAP];
+
+ scalehrtime(&unsecs);
+ scalehrtime(&snsecs);
+
+ hrt2ts(unsecs + snsecs, &t);
+ break;
+ }
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ timespec32_t t32;
+
+ if (TIMESPEC_OVERFLOW(&t)) {
+ return (set_errno(EOVERFLOW));
+ }
+ TIMESPEC_TO_TIMESPEC32(&t32, &t);
+
+ if (copyout(&t32, tp, sizeof (t32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+ }
+#endif
+
+ if (copyout(&t, tp, sizeof (t)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+
+static int
+lx_emul_clock_getres(clockid_t clock, timespec_t *tp)
+{
+ timespec_t t;
+
+ if (tp == NULL) {
+ return (0);
+ }
+
+ switch (clock) {
+ case CLOCK_PROCESS_CPUTIME_ID:
+ case CLOCK_THREAD_CPUTIME_ID:
+ /*
+ * These clock backends return microstate accounting values for
+ * the LWP or the entire process. The Linux kernel claims they
+ * have nanosecond resolution; so will we.
+ */
+ t.tv_sec = 0;
+ t.tv_nsec = 1;
+ break;
+
+ default:
+ return (set_errno(EINVAL));
+ }
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ timespec32_t t32;
+
+ if (TIMESPEC_OVERFLOW(&t)) {
+ return (set_errno(EOVERFLOW));
+ }
+ TIMESPEC_TO_TIMESPEC32(&t32, &t);
+
+ if (copyout(&t32, tp, sizeof (t32)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+ }
+#endif
+
+ if (copyout(&t, tp, sizeof (t)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
+
+static void
+lx_clock_unsupported(int clock)
+{
+ char buf[100];
+
+ (void) snprintf(buf, sizeof (buf), "unsupported clock: %d", clock);
+ lx_unsupported(buf);
+}
+
+long
+lx_clock_settime(int clock, timespec_t *tp)
+{
+ lx_clock_backend_t *backend;
+
+ if ((backend = LX_CLOCK_BACKEND(clock)) == NULL) {
+ lx_clock_unsupported(clock);
+ return (set_errno(EINVAL));
+ }
+
+ return (backend->lclk_clock_settime(backend->lclk_ntv_id, tp));
+}
+
+long
+lx_clock_gettime(int clock, timespec_t *tp)
+{
+ lx_clock_backend_t *backend;
+
+ if ((backend = LX_CLOCK_BACKEND(clock)) == NULL) {
+ lx_clock_unsupported(clock);
+ return (set_errno(EINVAL));
+ }
+
+ return (backend->lclk_clock_gettime(backend->lclk_ntv_id, tp));
+}
+
+long
+lx_clock_getres(int clock, timespec_t *tp)
+{
+ lx_clock_backend_t *backend;
+
+ if ((backend = LX_CLOCK_BACKEND(clock)) == NULL) {
+ lx_clock_unsupported(clock);
+ return (set_errno(EINVAL));
+ }
+
+ /*
+ * It is important this check is performed after the clock
+ * check. Both glibc and musl, in their clock_getcpuclockid(),
+ * use clock_getres() with a NULL tp to validate a clock
+ * value. Performing the tp check before the clock check could
+ * indicate a valid clock to libc when it shouldn't.
+ */
+ if (tp == NULL) {
+ return (0);
+ }
+
+ return (backend->lclk_clock_getres(backend->lclk_ntv_id, tp));
+}
+
+static int
+lx_ltos_sigev(lx_sigevent_t *lev, struct sigevent *sev)
+{
+ bzero(sev, sizeof (*sev));
+
+ switch (lev->lx_sigev_notify) {
+ case LX_SIGEV_NONE:
+ sev->sigev_notify = SIGEV_NONE;
+ break;
+
+ case LX_SIGEV_SIGNAL:
+ case LX_SIGEV_THREAD_ID:
+ sev->sigev_notify = SIGEV_SIGNAL;
+ break;
+
+ case LX_SIGEV_THREAD:
+ /*
+ * Just as in illumos, SIGEV_THREAD handling is performed in
+ * userspace with the help of SIGEV_SIGNAL/SIGEV_THREAD_ID.
+ *
+ * It's not expected to make an appearance in the syscall.
+ */
+ default:
+ return (EINVAL);
+ }
+
+ sev->sigev_signo = lx_ltos_signo(lev->lx_sigev_signo, 0);
+ sev->sigev_value = lev->lx_sigev_value;
+
+ /* Ensure SIGEV_SIGNAL has a valid signo to work with. */
+ if (sev->sigev_notify == SIGEV_SIGNAL && sev->sigev_signo == 0) {
+ return (EINVAL);
+ }
+ return (0);
+}
+
+static int
+lx_sigev_copyin(lx_sigevent_t *userp, lx_sigevent_t *levp)
+{
+#ifdef _SYSCALL32_IMPL
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ lx_sigevent32_t lev32;
+
+ if (copyin(userp, &lev32, sizeof (lev32)) != 0) {
+ return (EFAULT);
+ }
+ levp->lx_sigev_value.sival_int = lev32.lx_sigev_value.sival_int;
+ levp->lx_sigev_signo = lev32.lx_sigev_signo;
+ levp->lx_sigev_notify = lev32.lx_sigev_notify;
+ levp->lx_sigev_un.lx_tid = lev32.lx_sigev_un.lx_tid;
+ } else
+#endif /* _SYSCALL32_IMPL */
+ {
+ if (copyin(userp, levp, sizeof (lx_sigevent_t)) != 0) {
+ return (EFAULT);
+ }
+ }
+ return (0);
+}
+
+static void
+lx_sigev_thread_fire(itimer_t *it)
+{
+ proc_t *p = it->it_proc;
+ pid_t lpid = (pid_t)LX_SIGEV_THREAD_ID_LPID(it);
+ id_t tid = (id_t)LX_SIGEV_THREAD_ID_TID(it);
+ lwpdir_t *ld;
+
+ ASSERT(MUTEX_HELD(&it->it_mutex));
+ ASSERT(it->it_pending == 0);
+ ASSERT(it->it_flags & IT_SIGNAL);
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ ld = lwp_hash_lookup(p, tid);
+ if (ld != NULL) {
+ lx_lwp_data_t *lwpd;
+ kthread_t *t;
+
+ t = ld->ld_entry->le_thread;
+ lwpd = ttolxlwp(t);
+ if (lwpd != NULL && lwpd->br_pid == lpid) {
+ /*
+ * A thread matching the LX pid is still present in the
+ * process. Send a targeted signal as requested.
+ */
+ it->it_pending = 1;
+ mutex_exit(&it->it_mutex);
+ sigaddqa(p, t, it->it_sigq);
+ return;
+ }
+ }
+
+ mutex_exit(&it->it_mutex);
+}
+
+long
+lx_timer_create(int clock, lx_sigevent_t *sevp, timer_t *tidp)
+{
+ int error;
+ lx_sigevent_t lev;
+ struct sigevent sev;
+ clock_backend_t *backend = NULL;
+ proc_t *p = curproc;
+ itimer_t *itp;
+ timer_t tid;
+
+ if (clock == -2) {
+ /*
+ * A change was made to the old userspace timer emulation to
+ * handle this specific clock ID for MapR. It was wrongly
+ * mapped to CLOCK_REALTIME rather than CLOCK_THREAD_CPUTIME_ID
+ * which it maps to. Until the CLOCK_*_CPUTIME_ID timers can
+ * be emulated, the admittedly incorrect mapping will remain.
+ */
+ backend = clock_get_backend(CLOCK_REALTIME);
+ } else {
+ lx_clock_backend_t *lback = LX_CLOCK_BACKEND(clock);
+
+ if (lback != NULL) {
+ backend = clock_get_backend(lback->lclk_ntv_id);
+ }
+ }
+ if (backend == NULL) {
+ return (set_errno(EINVAL));
+ }
+
+ /* We have to convert the Linux sigevent layout to the illumos layout */
+ if (sevp != NULL) {
+ if ((error = lx_sigev_copyin(sevp, &lev)) != 0) {
+ return (set_errno(error));
+ }
+ if ((error = lx_ltos_sigev(&lev, &sev)) != 0) {
+ return (set_errno(error));
+ }
+ } else {
+ bzero(&sev, sizeof (sev));
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_signo = SIGALRM;
+ }
+
+ if ((error = timer_setup(backend, &sev, NULL, &itp, &tid)) != 0) {
+ return (set_errno(error));
+ }
+
+ /*
+ * The SIGEV_THREAD_ID notification method in Linux allows the caller
+ * to target a specific thread to receive the signal. The IT_CALLBACK
+ * timer functionality is used to fulfill this need. After translating
+ * the LX pid to a SunOS thread ID (ensuring it exists in the current
+ * process), those IDs are attached to the timer along with the custom
+ * lx_sigev_thread_fire callback. This targets the signal notification
+ * properly when the timer fires.
+ */
+ if (lev.lx_sigev_notify == LX_SIGEV_THREAD_ID) {
+ pid_t lpid, spid;
+ id_t stid;
+
+ lpid = (pid_t)lev.lx_sigev_un.lx_tid;
+ if (lx_lpid_to_spair(lpid, &spid, &stid) != 0 ||
+ spid != curproc->p_pid) {
+ error = EINVAL;
+ goto err;
+ }
+
+ itp->it_flags |= IT_CALLBACK;
+ itp->it_cb_func = lx_sigev_thread_fire;
+ LX_SIGEV_THREAD_ID_LPID(itp) = lpid;
+ LX_SIGEV_THREAD_ID_TID(itp) = stid;
+ }
+
+ /*
+ * When the sigevent is not specified, its sigev_value field is
+ * expected to be populated with the timer ID.
+ */
+ if (sevp == NULL) {
+ itp->it_sigq->sq_info.si_value.sival_int = tid;
+ }
+
+ if (copyout(&tid, tidp, sizeof (timer_t)) != 0) {
+ error = EFAULT;
+ goto err;
+ }
+
+ timer_release(p, itp);
+ return (0);
+
+err:
+ timer_delete_grabbed(p, tid, itp);
+ return (set_errno(error));
+}
+
+long
+lx_gettimeofday(struct timeval *tvp, struct lx_timezone *tzp)
+{
+ struct lx_timezone tz;
+
+ bzero(&tz, sizeof (tz));
+
+ /*
+ * We want to be similar to libc which just does a fasttrap to
+ * gethrestime and simply converts that result. We follow how uniqtime
+ * does the conversion but we can't use that code since it does some
+ * extra work which can cause the result to bounce around based on which
+ * CPU we run on.
+ */
+ if (tvp != NULL) {
+ struct timeval tv;
+ timestruc_t ts;
+ int usec, nsec;
+
+ gethrestime(&ts);
+ nsec = ts.tv_nsec;
+ usec = nsec + (nsec >> 2);
+ usec = nsec + (usec >> 1);
+ usec = nsec + (usec >> 2);
+ usec = nsec + (usec >> 4);
+ usec = nsec - (usec >> 3);
+ usec = nsec + (usec >> 2);
+ usec = nsec + (usec >> 3);
+ usec = nsec + (usec >> 4);
+ usec = nsec + (usec >> 1);
+ usec = nsec + (usec >> 6);
+ usec = usec >> 10;
+
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = usec;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyout(&tv, tvp, sizeof (tv)) != 0)
+ return (set_errno(EFAULT));
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ struct timeval32 tv32;
+
+ if (TIMEVAL_OVERFLOW(&tv))
+ return (set_errno(EOVERFLOW));
+ TIMEVAL_TO_TIMEVAL32(&tv32, &tv);
+
+ if (copyout(&tv32, tvp, sizeof (tv32)))
+ return (set_errno(EFAULT));
+ }
+#endif
+ }
+
+ /*
+ * The Linux man page states use of the second parameter is obsolete,
+ * but gettimeofday(2) should still return EFAULT if it is set
+ * to a bad non-NULL pointer (sigh...)
+ */
+ if (tzp != NULL && copyout(&tz, tzp, sizeof (tz)) != 0)
+ return (set_errno(EFAULT));
+
+ return (0);
+}
+
+/*
+ * On Linux a bad buffer will set errno to EFAULT, and on Illumos the failure
+ * mode is documented as "undefined."
+ */
+long
+lx_time(time_t *tp)
+{
+ timestruc_t ts;
+ struct timeval tv;
+
+ gethrestime(&ts);
+ tv.tv_sec = ts.tv_sec;
+ tv.tv_usec = 0;
+
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (tp != NULL &&
+ copyout(&tv.tv_sec, tp, sizeof (tv.tv_sec)) != 0)
+ return (set_errno(EFAULT));
+
+ return (tv.tv_sec);
+ }
+#ifdef _SYSCALL32_IMPL
+ else {
+ struct timeval32 tv32;
+
+ if (TIMEVAL_OVERFLOW(&tv))
+ return (set_errno(EOVERFLOW));
+ TIMEVAL_TO_TIMEVAL32(&tv32, &tv);
+
+ if (tp != NULL &&
+ copyout(&tv32.tv_sec, tp, sizeof (tv32.tv_sec)))
+ return (set_errno(EFAULT));
+
+ return (tv32.tv_sec);
+ }
+#endif /* _SYSCALL32_IMPL */
+ /* NOTREACHED */
+}
+
+long
+lx_nanosleep(timespec_t *rqtp, timespec_t *rmtp)
+{
+ return (nanosleep(rqtp, rmtp));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_umask.c b/usr/src/uts/common/brand/lx/syscall/lx_umask.c
new file mode 100644
index 0000000000..cb5e4ed232
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_umask.c
@@ -0,0 +1,52 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/lx_misc.h>
+#include <lx_syscall.h>
+
+/* From usr/src/uts/common/syscall/umask.c */
+extern int umask(int);
+
+/*
+ * Just do what umask() does, but for the given process.
+ */
+static int
+lx_clone_umask_cb(proc_t *pp, void *arg)
+{
+ mode_t cmask = (mode_t)(intptr_t)arg;
+ mode_t orig;
+
+ orig = PTOU(pp)->u_cmask;
+ PTOU(pp)->u_cmask = (mode_t)(cmask & PERMMASK);
+ return ((int)orig);
+}
+
+long
+lx_umask(mode_t cmask)
+{
+ lx_proc_data_t *lproc = ttolxproc(curthread);
+
+ /* Handle the rare case of being in a CLONE_FS clone group */
+ if (lx_clone_grp_member(lproc, LX_CLONE_FS)) {
+ int omask;
+
+ omask = lx_clone_grp_walk(lproc, LX_CLONE_FS, lx_clone_umask_cb,
+ (void *)(intptr_t)cmask);
+ return (omask);
+ }
+
+ return (umask(cmask));
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_uname.c b/usr/src/uts/common/brand/lx/syscall/lx_uname.c
new file mode 100644
index 0000000000..2d18408eaa
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_uname.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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+
+struct lx_utsname {
+ char lxu_sysname[LX_SYS_UTS_LN];
+ char lxu_nodename[LX_SYS_UTS_LN];
+ char lxu_release[LX_SYS_UTS_LN];
+ char lxu_version[LX_SYS_UTS_LN];
+ char lxu_machine[LX_SYS_UTS_LN];
+ char lxu_domainname[LX_SYS_UTS_LN];
+};
+
+long
+lx_uname(void *uptr)
+{
+ proc_t *p = curproc;
+ lx_proc_data_t *lxpd = ptolxproc(p);
+ lx_zone_data_t *lxzd = ztolxzd(p->p_zone);
+ struct lx_utsname un;
+
+ bzero(&un, sizeof (un));
+
+ (void) strlcpy(un.lxu_sysname, LX_UNAME_SYSNAME, LX_SYS_UTS_LN);
+ (void) strlcpy(un.lxu_nodename, p->p_zone->zone_nodename,
+ LX_SYS_UTS_LN);
+
+ mutex_enter(&lxzd->lxzd_lock);
+
+ if (lxpd->l_uname_release[0] != '\0') {
+ (void) strlcpy(un.lxu_release, lxpd->l_uname_release,
+ LX_SYS_UTS_LN);
+ } else {
+ (void) strlcpy(un.lxu_release, lxzd->lxzd_kernel_release,
+ LX_SYS_UTS_LN);
+ }
+ if (lxpd->l_uname_version[0] != '\0') {
+ (void) strlcpy(un.lxu_version, lxpd->l_uname_version,
+ LX_SYS_UTS_LN);
+ } else {
+ (void) strlcpy(un.lxu_version, lxzd->lxzd_kernel_version,
+ LX_SYS_UTS_LN);
+ }
+
+ mutex_exit(&lxzd->lxzd_lock);
+
+ if (get_udatamodel() == DATAMODEL_LP64) {
+ (void) strlcpy(un.lxu_machine, LX_UNAME_MACHINE64,
+ LX_SYS_UTS_LN);
+ } else {
+ (void) strlcpy(un.lxu_machine, LX_UNAME_MACHINE32,
+ LX_SYS_UTS_LN);
+ }
+ (void) strlcpy(un.lxu_domainname, p->p_zone->zone_domain,
+ LX_SYS_UTS_LN);
+
+ if (copyout(&un, uptr, sizeof (un)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_wait.c b/usr/src/uts/common/brand/lx/syscall/lx_wait.c
new file mode 100644
index 0000000000..3a5ba69b93
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_wait.c
@@ -0,0 +1,377 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
+ * wait() family of functions.
+ *
+ * The first minor difference between the Linux and Solaris family of wait()
+ * calls is that the values for WNOHANG and WUNTRACED are different. Thankfully,
+ * the exit status values are identical between the two implementations.
+ *
+ * Things get very different and very complicated when we introduce the Linux
+ * threading model. Under linux, both threads and child processes are
+ * represented as processes. However, the behavior of wait() with respect to
+ * each child varies according to the flags given to clone()
+ *
+ * SIGCHLD The SIGCHLD signal should be sent on termination
+ * CLONE_THREAD The child shares the same thread group as the parent
+ * CLONE_DETACHED The parent receives no notification when the child exits
+ *
+ * The following flags control the Linux behavior w.r.t. the above attributes:
+ *
+ * __WALL Wait on all children, regardless of type
+ * __WCLONE Wait only on non-SIGCHLD children
+ * __WNOTHREAD Don't wait on children of other threads in this group
+ *
+ * The following chart shows whether wait() returns when the child exits:
+ *
+ * default __WCLONE __WALL
+ * no SIGCHLD - X X
+ * SIGCHLD X - X
+ *
+ * The following chart shows whether wait() returns when the grandchild exits:
+ *
+ * default __WNOTHREAD
+ * no CLONE_THREAD - -
+ * CLONE_THREAD X -
+ *
+ * The CLONE_DETACHED flag is universal - when the child exits, no state is
+ * stored and wait() has no effect.
+ *
+ * XXX Support the above combination of options, or some reasonable subset that
+ * covers at least fork() and pthread_create().
+ */
+
+#include <sys/wait.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/lx_misc.h>
+#include <lx_signum.h>
+#include <lx_errno.h>
+#include <lx_syscall.h>
+
+/*
+ * From "uts/common/os/exit.c" and "uts/common/syscall/rusagesys.c":
+ */
+extern int waitid(idtype_t, id_t, k_siginfo_t *, int);
+extern int rusagesys(int, void *, void *, void *, void *);
+
+/*
+ * Convert between Linux options and Solaris options, returning -1 if any
+ * invalid flags are found.
+ */
+#define LX_WNOHANG 0x00000001
+#define LX_WUNTRACED 0x00000002
+#define LX_WSTOPPED LX_WUNTRACED
+#define LX_WEXITED 0x00000004
+#define LX_WCONTINUED 0x00000008
+#define LX_WNOWAIT 0x01000000
+
+#define LX_WNOTHREAD 0x20000000
+#define LX_WALL 0x40000000
+#define LX_WCLONE 0x80000000
+
+#define LX_P_ALL 0x0
+#define LX_P_PID 0x1
+#define LX_P_GID 0x2
+
+/*
+ * Split the passed waitpid/waitid options into two separate variables:
+ * those for the native illumos waitid(2), and the extra Linux-specific
+ * options we will handle in our brand-specific code.
+ */
+static int
+ltos_options(uintptr_t options, int *native_options, int *extra_options)
+{
+ int newoptions = 0;
+
+ if (((options) & ~(LX_WNOHANG | LX_WUNTRACED | LX_WEXITED |
+ LX_WCONTINUED | LX_WNOWAIT | LX_WNOTHREAD | LX_WALL |
+ LX_WCLONE)) != 0) {
+ return (-1);
+ }
+
+ *extra_options = options & (LX_WNOTHREAD | LX_WALL | LX_WCLONE);
+
+ if (options & LX_WNOHANG)
+ newoptions |= WNOHANG;
+ if (options & LX_WUNTRACED)
+ newoptions |= WUNTRACED;
+ if (options & LX_WEXITED)
+ newoptions |= WEXITED;
+ if (options & LX_WCONTINUED)
+ newoptions |= WCONTINUED;
+ if (options & LX_WNOWAIT)
+ newoptions |= WNOWAIT;
+
+ /*
+ * The trapped option is implicit on Linux.
+ */
+ newoptions |= WTRAPPED;
+
+ *native_options = newoptions;
+ return (0);
+}
+
+static int
+lx_wstat(int code, int status)
+{
+ int stat = 0;
+
+ switch (code) {
+ case CLD_EXITED:
+ stat = status << 8;
+ break;
+ case CLD_DUMPED:
+ stat = lx_stol_signo(status, SIGKILL) | WCOREFLG;
+ break;
+ case CLD_KILLED:
+ stat = lx_stol_signo(status, SIGKILL);
+ break;
+ case CLD_TRAPPED:
+ case CLD_STOPPED:
+ stat = (lx_stol_status(status, SIGKILL) << 8) | WSTOPFLG;
+ break;
+ case CLD_CONTINUED:
+ stat = WCONTFLG;
+ break;
+ }
+
+ return (stat);
+}
+
+static int
+lx_call_waitid(idtype_t idtype, id_t id, k_siginfo_t *sip, int native_options,
+ int extra_options)
+{
+ lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+ int error;
+
+ /*
+ * Our brand-specific waitid helper only understands a subset of
+ * the possible idtypes. Ensure we keep to that subset here:
+ */
+ if (idtype != P_ALL && idtype != P_PID && idtype != P_PGID) {
+ return (EINVAL);
+ }
+
+ /*
+ * Enable the return of emulated ptrace(2) stop conditions
+ * through lx_waitid_helper, and stash the Linux-specific
+ * extra waitid() flags.
+ */
+ lwpd->br_waitid_emulate = B_TRUE;
+ lwpd->br_waitid_flags = extra_options;
+
+ if ((error = waitid(idtype, id, sip, native_options)) == EINTR) {
+ /*
+ * According to signal(7), the wait4(2), waitid(2), and
+ * waitpid(2) system calls are restartable.
+ */
+ ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+ }
+
+ lwpd->br_waitid_emulate = B_FALSE;
+ lwpd->br_waitid_flags = 0;
+
+ return (error);
+}
+
+long
+lx_wait4(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+ k_siginfo_t info = { 0 };
+ idtype_t idtype;
+ id_t id;
+ int status = 0;
+ pid_t pid = (pid_t)p1;
+ int error;
+ int native_options, extra_options;
+ int *statusp = (int *)p2;
+ void *rup = (void *)p4;
+
+ if (ltos_options(p3, &native_options, &extra_options) == -1) {
+ return (set_errno(EINVAL));
+ }
+
+ if (pid > maxpid) {
+ return (set_errno(ECHILD));
+ }
+
+ /*
+ * While not listed as a valid return code, Linux's wait4(2) does,
+ * in fact, get an EFAULT if either the status pointer or rusage
+ * pointer is invalid. Since a failed waitpid should leave child
+ * process in a state where a future wait4(2) will succeed, we
+ * check them by copying out the values their buffers originally
+ * contained. (We need to do this as a failed system call should
+ * never affect the contents of a passed buffer.)
+ *
+ * This will fail if the buffers in question are write-only.
+ */
+ if (statusp != NULL) {
+ if (copyin(statusp, &status, sizeof (status)) != 0 ||
+ copyout(&status, statusp, sizeof (status)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ }
+
+ /*
+ * Do the same check for the "struct rusage" pointer, which differs
+ * in size for 32- and 64-bit processes.
+ */
+ if (rup != NULL) {
+ struct rusage ru;
+ void *krup = &ru;
+ size_t rusz = sizeof (ru);
+#if defined(_SYSCALL32_IMPL)
+ struct rusage32 ru32;
+
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ krup = &ru32;
+ rusz = sizeof (ru32);
+ }
+#endif
+
+ if (copyin(rup, krup, rusz) != 0 ||
+ copyout(krup, rup, rusz) != 0) {
+ return (set_errno(EFAULT));
+ }
+ }
+
+ if (pid < -1) {
+ idtype = P_PGID;
+ id = -pid;
+ } else if (pid == -1) {
+ idtype = P_ALL;
+ id = 0;
+ } else if (pid == 0) {
+ idtype = P_PGID;
+ mutex_enter(&pidlock);
+ id = curproc->p_pgrp;
+ mutex_exit(&pidlock);
+ } else {
+ idtype = P_PID;
+ id = pid;
+ }
+
+ native_options |= (WEXITED | WTRAPPED);
+
+ if ((error = lx_call_waitid(idtype, id, &info, native_options,
+ extra_options)) != 0) {
+ return (set_errno(error));
+ }
+
+ /*
+ * If the WNOHANG flag was specified and no child was found return 0.
+ */
+ if ((native_options & WNOHANG) && info.si_pid == 0) {
+ return (0);
+ }
+
+ status = lx_wstat(info.si_code, info.si_status);
+
+ /*
+ * Unfortunately if this attempt to copy out either the status or the
+ * rusage fails, the process will be in an inconsistent state as
+ * subsequent calls to wait for the same child will fail where they
+ * should succeed on a Linux system. This, however, is rather
+ * unlikely since we tested the validity of both above.
+ */
+ if (statusp != NULL) {
+ if (copyout(&status, statusp, sizeof (status)) != 0) {
+ return (set_errno(EFAULT));
+ }
+ }
+
+ if (rup != NULL) {
+ if ((error = rusagesys(_RUSAGESYS_GETRUSAGE_CHLD, rup, NULL,
+ NULL, NULL)) != 0) {
+ return (set_errno(error));
+ }
+ }
+
+ return (info.si_pid);
+}
+
+long
+lx_waitpid(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+ return (lx_wait4(p1, p2, p3, (uintptr_t)NULL));
+}
+
+long
+lx_waitid(uintptr_t idtype, uintptr_t id, uintptr_t infop, uintptr_t opt)
+{
+ int error;
+ int native_options, extra_options;
+ k_siginfo_t info = { 0 };
+
+ if (ltos_options(opt, &native_options, &extra_options) == -1) {
+ return (set_errno(EINVAL));
+ }
+
+ if (((opt) & (LX_WEXITED | LX_WSTOPPED | LX_WCONTINUED)) == 0) {
+ return (set_errno(EINVAL));
+ }
+
+ switch (idtype) {
+ case LX_P_ALL:
+ idtype = P_ALL;
+ break;
+ case LX_P_PID:
+ idtype = P_PID;
+ break;
+ case LX_P_GID:
+ idtype = P_PGID;
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+
+ if ((error = lx_call_waitid(idtype, id, &info, native_options,
+ extra_options)) != 0) {
+ return (set_errno(error));
+ }
+
+ /*
+ * If the WNOHANG flag was specified and no child was found return 0.
+ */
+ if ((native_options & WNOHANG) && info.si_pid == 0) {
+ return (0);
+ }
+
+#if defined(_SYSCALL32_IMPL)
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ return (stol_ksiginfo32_copyout(&info, (void *)infop));
+ } else
+#endif
+ {
+ return (stol_ksiginfo_copyout(&info, (void *)infop));
+ }
+}
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_xattr.c b/usr/src/uts/common/brand/lx/syscall/lx_xattr.c
new file mode 100644
index 0000000000..19bf9a4ebb
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/syscall/lx_xattr.c
@@ -0,0 +1,519 @@
+/*
+ * 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) 2017 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+#include <sys/lx_acl.h>
+
+
+#define LX_XATTR_NAME_MAX 255
+#define LX_XATTR_SIZE_MAX 65536
+#define LX_XATTR_LIST_MAX 65536
+
+#define LX_XATTR_FLAG_CREATE 0x1
+#define LX_XATTR_FLAG_REPLACE 0x2
+#define LX_XATTR_FLAGS_VALID (LX_XATTR_FLAG_CREATE | LX_XATTR_FLAG_REPLACE)
+
+enum lx_xattr_ns {
+ LX_XATTR_NS_SECURITY,
+ LX_XATTR_NS_SYSTEM,
+ LX_XATTR_NS_TRUSTED,
+ LX_XATTR_NS_USER,
+ LX_XATTR_NS_INVALID /* Catch-all for invalid namespaces */
+};
+
+/* Present under the 'security.' namespace */
+#define LX_XATTR_CAPABILITY "capability"
+
+typedef struct lx_xattr_ns_list {
+ const char *lxnl_name;
+ unsigned lxnl_len;
+ enum lx_xattr_ns lxnl_ns;
+} lx_xattr_ns_list_t;
+
+static lx_xattr_ns_list_t lx_xattr_namespaces[] = {
+ { "user.", 5, LX_XATTR_NS_USER },
+ { "system.", 7, LX_XATTR_NS_SYSTEM },
+ { "trusted.", 8, LX_XATTR_NS_TRUSTED },
+ { "security.", 9, LX_XATTR_NS_SECURITY },
+ { NULL, 0, LX_XATTR_NS_INVALID }
+};
+
+static int
+lx_xattr_parse(const char *name, size_t nlen, const char **key)
+{
+ lx_xattr_ns_list_t *lxn = lx_xattr_namespaces;
+
+ for (; lxn->lxnl_name != NULL; lxn++) {
+ if (nlen < lxn->lxnl_len) {
+ continue;
+ }
+ if (strncmp(lxn->lxnl_name, name, lxn->lxnl_len) == 0) {
+ *key = name + (lxn->lxnl_len);
+ return (lxn->lxnl_ns);
+ }
+ }
+
+ *key = name;
+ return (LX_XATTR_NS_INVALID);
+}
+
+/*
+ * *xattr() family of functions.
+ *
+ * These are largely unimplemented. In most cases we return EOPNOTSUPP, rather
+ * than using NOSYS_NO_EQUIV to avoid unwanted stderr output from ls(1).
+ *
+ * Note that CRED() is used instead of f_cred in the f*xattr functions. This
+ * is intentional as Linux does not have the same notion of per-fd credentials.
+ */
+
+/* ARGSUSED */
+static int
+lx_setxattr_common(vnode_t *vp, char *name, void *value, size_t sz, int flags)
+{
+ int error, type;
+ char name_buf[LX_XATTR_NAME_MAX + 1];
+ const char *key;
+ size_t name_len;
+ void *buf = NULL;
+
+ if ((flags & ~LX_XATTR_FLAGS_VALID) != 0) {
+ return (EINVAL);
+ }
+ error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
+ if (error == ENAMETOOLONG || name_len == sizeof (name_buf)) {
+ return (ERANGE);
+ } else if (error != 0) {
+ return (EFAULT);
+ }
+
+ type = lx_xattr_parse(name_buf, name_len, &key);
+
+ if (sz != 0) {
+ if (sz > LX_XATTR_SIZE_MAX) {
+ return (E2BIG);
+ }
+ buf = kmem_alloc(sz, KM_SLEEP);
+ if (copyin(value, buf, sz) != 0) {
+ kmem_free(buf, sz);
+ return (EFAULT);
+ }
+ }
+
+ error = EOPNOTSUPP;
+ switch (type) {
+ case LX_XATTR_NS_SECURITY:
+ /*
+ * In order to keep package management software happy, despite
+ * lacking support for file-based Linux capabilities via
+ * xattrs, we fake success when root attempts a setxattr on
+ * that attribute.
+ */
+ if (crgetuid(CRED()) == 0 &&
+ strcmp(key, LX_XATTR_CAPABILITY) == 0) {
+ error = 0;
+ }
+ break;
+ case LX_XATTR_NS_SYSTEM:
+ if (strcmp(key, LX_XATTR_POSIX_ACL_ACCESS) == 0) {
+ error = lx_acl_setxattr(vp, LX_ACL_ACCESS, buf, sz);
+ } else if (strcmp(key, LX_XATTR_POSIX_ACL_DEFAULT) == 0) {
+ error = lx_acl_setxattr(vp, LX_ACL_DEFAULT, buf, sz);
+ }
+ default:
+ break;
+ }
+
+ if (buf != NULL) {
+ kmem_free(buf, sz);
+ }
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+lx_getxattr_common(vnode_t *vp, char *name, char *value, size_t sz,
+ ssize_t *osz)
+{
+ int error, type;
+ char name_buf[LX_XATTR_NAME_MAX + 1];
+ const char *key;
+ size_t name_len;
+ void *buf = NULL;
+
+ error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
+ if (error == ENAMETOOLONG || name_len == sizeof (name_buf)) {
+ return (ERANGE);
+ } else if (error != 0) {
+ return (EFAULT);
+ }
+ if (sz != 0) {
+ if (sz > LX_XATTR_SIZE_MAX) {
+ sz = LX_XATTR_SIZE_MAX;
+ }
+ buf = kmem_alloc(sz, KM_SLEEP);
+ }
+
+ type = lx_xattr_parse(name_buf, name_len, &key);
+
+ error = EOPNOTSUPP;
+ switch (type) {
+ case LX_XATTR_NS_SYSTEM:
+ if (strcmp(key, LX_XATTR_POSIX_ACL_ACCESS) == 0) {
+ error = lx_acl_getxattr(vp, LX_ACL_ACCESS, buf, sz,
+ osz);
+ } else if (strcmp(key, LX_XATTR_POSIX_ACL_DEFAULT) == 0) {
+ error = lx_acl_getxattr(vp, LX_ACL_DEFAULT, buf, sz,
+ osz);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (error == 0 && buf != NULL) {
+ VERIFY(*osz <= sz);
+
+ if (copyout(buf, value, *osz) != 0) {
+ error = EFAULT;
+ }
+ }
+ if (buf != NULL) {
+ kmem_free(buf, sz);
+ }
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+lx_listxattr_common(vnode_t *vp, void *value, size_t size, ssize_t *osize)
+{
+ struct uio auio;
+ struct iovec aiov;
+ int err = 0;
+
+ aiov.iov_base = value;
+ aiov.iov_len = size;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_loffset = 0;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_resid = size;
+ auio.uio_fmode = 0;
+ auio.uio_extflg = UIO_COPY_CACHED;
+
+ /*
+ * Call into all the listxattr routines (which may be no-ops) which are
+ * currently implemented.
+ */
+ err = lx_acl_listxattr(vp, &auio);
+
+ if (err == 0) {
+ *osize = size - auio.uio_resid;
+ }
+
+ return (err);
+}
+
+/* ARGSUSED */
+static int
+lx_removexattr_common(vnode_t *vp, char *name)
+{
+ int error, type;
+ char name_buf[LX_XATTR_NAME_MAX + 1];
+ const char *key;
+ size_t name_len;
+
+ error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
+ if (error == ENAMETOOLONG || name_len == sizeof (name_buf)) {
+ return (ERANGE);
+ } else if (error != 0) {
+ return (EFAULT);
+ }
+
+
+ type = lx_xattr_parse(name_buf, name_len, &key);
+
+ error = EOPNOTSUPP;
+ switch (type) {
+ case LX_XATTR_NS_SYSTEM:
+ if (strcmp(key, LX_XATTR_POSIX_ACL_ACCESS) == 0) {
+ error = lx_acl_removexattr(vp, LX_ACL_ACCESS);
+ } else if (strcmp(key, LX_XATTR_POSIX_ACL_DEFAULT) == 0) {
+ error = lx_acl_removexattr(vp, LX_ACL_DEFAULT);
+ }
+ default:
+ break;
+ }
+
+ return (EOPNOTSUPP);
+}
+
+
+long
+lx_setxattr(char *path, char *name, void *value, size_t size, int flags)
+{
+ int error;
+ vnode_t *vp = NULL;
+
+ error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_setxattr_common(vp, name, value, size, flags);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_lsetxattr(char *path, char *name, void *value, size_t size, int flags)
+{
+ int error;
+ vnode_t *vp = NULL;
+
+ error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_setxattr_common(vp, name, value, size, flags);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+long
+lx_fsetxattr(int fd, char *name, void *value, size_t size, int flags)
+{
+ int error;
+ file_t *fp;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+
+ error = lx_setxattr_common(fp->f_vnode, name, value, size, flags);
+ releasef(fd);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+ssize_t
+lx_getxattr(char *path, char *name, void *value, size_t size)
+{
+ int error;
+ vnode_t *vp = NULL;
+ ssize_t osize;
+
+ error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_getxattr_common(vp, name, value, size, &osize);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+ssize_t
+lx_lgetxattr(char *path, char *name, void *value, size_t size)
+{
+
+ int error;
+ vnode_t *vp = NULL;
+ ssize_t osize;
+
+ error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_getxattr_common(vp, name, value, size, &osize);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+ssize_t
+lx_fgetxattr(int fd, char *name, void *value, size_t size)
+{
+ int error;
+ file_t *fp;
+ ssize_t osize;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+
+ /*
+ * When a file is opened with O_PATH we clear read/write and fgetxattr
+ * is expected to return EBADF.
+ */
+ if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
+ releasef(fd);
+ return (set_errno(EBADF));
+ }
+
+ error = lx_getxattr_common(fp->f_vnode, name, value, size, &osize);
+ releasef(fd);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+ssize_t
+lx_listxattr(char *path, char *list, size_t size)
+{
+ int error;
+ vnode_t *vp = NULL;
+ ssize_t osize;
+
+ error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_listxattr_common(vp, list, size, &osize);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+ssize_t
+lx_llistxattr(char *path, char *list, size_t size)
+{
+ int error;
+ vnode_t *vp = NULL;
+ ssize_t osize;
+
+ error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_listxattr_common(vp, list, size, &osize);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+ssize_t
+lx_flistxattr(int fd, char *list, size_t size)
+{
+ int error;
+ file_t *fp;
+ ssize_t osize;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+
+ error = lx_listxattr_common(fp->f_vnode, list, size, &osize);
+ releasef(fd);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (osize);
+}
+
+int
+lx_removexattr(char *path, char *name)
+{
+ int error;
+ vnode_t *vp = NULL;
+
+ error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_removexattr_common(vp, name);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+int
+lx_lremovexattr(char *path, char *name)
+{
+ int error;
+ vnode_t *vp = NULL;
+
+ error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+ if (error != 0) {
+ return (set_errno(error));
+ }
+
+ error = lx_removexattr_common(vp, name);
+ VN_RELE(vp);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
+
+int
+lx_fremovexattr(int fd, char *name)
+{
+ int error;
+ file_t *fp;
+
+ if ((fp = getf(fd)) == NULL) {
+ return (set_errno(EBADF));
+ }
+
+ error = lx_removexattr_common(fp->f_vnode, name);
+ releasef(fd);
+
+ if (error != 0) {
+ return (set_errno(error));
+ }
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/sysfs/lx_sysfs.h b/usr/src/uts/common/brand/lx/sysfs/lx_sysfs.h
new file mode 100644
index 0000000000..f34ed31dcb
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sysfs/lx_sysfs.h
@@ -0,0 +1,198 @@
+/*
+ * 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 2017 Joyent, Inc.
+ */
+
+#ifndef _LXSYSFS_H
+#define _LXSYSFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * lx_sysfs.h: declarations, data structures and macros for lx_sysfs
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/debug.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/user.h>
+#include <sys/t_lock.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/dnlc.h>
+#include <sys/atomic.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <vm/as.h>
+#include <vm/anon.h>
+#include <sys/netstack.h>
+#include <inet/ip.h>
+#include <inet/ip_if.h>
+
+/*
+ * Convert a vnode into an lxsys_mnt_t
+ */
+#define VTOLXSM(vp) ((lxsys_mnt_t *)(vp)->v_vfsp->vfs_data)
+
+/*
+ * convert a vnode into an lxsys_node
+ */
+#define VTOLXS(vp) ((lxsys_node_t *)(vp)->v_data)
+
+/*
+ * convert a lxsys_node into a vnode
+ */
+#define LXSTOV(lxsnp) ((lxsnp)->lxsys_vnode)
+
+/*
+ * convert a lxsys_node into zone for fs
+ */
+#define LXSTOZ(lxsnp) \
+ (((lxsys_mnt_t *)(lxsnp)->lxsys_vnode->v_vfsp->vfs_data)->lxsysm_zone)
+
+#define LXSNSIZ 256 /* max size of lx /sys file name entries */
+
+/*
+ * Pretend that a directory entry takes 16 bytes
+ */
+#define LXSYS_SDSIZE 16
+
+/* Root sysfs lxsys_instance */
+#define LXSYS_INST_ROOT 0
+
+/*
+ * Node/file types for lx /sys files
+ * (directories and files contained therein).
+ */
+typedef enum lxsys_nodetype {
+ LXSYS_NONE, /* None-type to keep inodes non-zero */
+ LXSYS_STATIC, /* Statically defined entries */
+ LXSYS_CLASS_NET, /* /sys/class/net/<iface> */
+ LXSYS_DEV_NET, /* /sys/devices/virtual/net/<iface> */
+ LXSYS_BLOCK, /* /sys/block/<dev> */
+ LXSYS_DEV_ZFS, /* /sys/devices/zfs/<dev> */
+ LXSYS_DEV_SYS_CPU, /* /sys/devices/system/cpu/<cpu> */
+ LXSYS_DEV_SYS_CPUINFO, /* /sys/devices/system/cpu/cpuN/<info> */
+ LXSYS_DEV_SYS_NODE, /* /sys/devices/system/node/node0/<info> */
+ LXSYS_MAXTYPE, /* type limit */
+} lxsys_nodetype_t;
+
+/*
+ * external dirent characteristics
+ */
+typedef struct {
+ unsigned int d_idnum;
+ char *d_name;
+} lxsys_dirent_t;
+
+typedef struct {
+ unsigned int dl_instance;
+ lxsys_dirent_t *dl_list;
+ int dl_length;
+} lxsys_dirlookup_t;
+
+/*
+ * This is the lx sysfs private data object
+ * which is attached to v_data in the vnode structure
+ */
+struct lxsys_node;
+typedef struct lxsys_node lxsys_node_t;
+struct lxsys_node {
+ lxsys_nodetype_t lxsys_type; /* type ID of node */
+ unsigned int lxsys_instance; /* instance ID node */
+ unsigned int lxsys_endpoint; /* endpoint ID node */
+ vnode_t *lxsys_vnode; /* vnode for the node */
+ vnode_t *lxsys_parentvp; /* parent directory */
+ lxsys_node_t *lxsys_next; /* next list entry */
+ timestruc_t lxsys_time; /* creation time */
+ mode_t lxsys_mode; /* file mode bits */
+ uid_t lxsys_uid; /* file owner */
+ gid_t lxsys_gid; /* file group owner */
+ ino_t lxsys_ino; /* node id */
+};
+
+/*
+ * This is the lxsysfs private data object
+ * which is attached to vfs_data in the vfs structure
+ */
+typedef struct lxsys_mnt {
+ kmutex_t lxsysm_lock; /* protects fields */
+ lxsys_node_t *lxsysm_node; /* node at root of sys mount */
+ zone_t *lxsysm_zone; /* zone for this mount */
+} lxsys_mnt_t;
+
+extern vnodeops_t *lxsys_vnodeops;
+
+typedef struct mounta mounta_t;
+
+extern void lxsys_initnodecache();
+extern void lxsys_fininodecache();
+extern ino_t lxsys_inode(lxsys_nodetype_t, unsigned int, unsigned int);
+extern ino_t lxsys_parentinode(lxsys_node_t *);
+extern lxsys_node_t *lxsys_getnode(vnode_t *, lxsys_nodetype_t, unsigned int,
+ unsigned int);
+extern lxsys_node_t *lxsys_getnode_static(vnode_t *, unsigned int);
+extern void lxsys_freenode(lxsys_node_t *);
+
+extern netstack_t *lxsys_netstack(lxsys_node_t *);
+extern ill_t *lxsys_find_ill(ip_stack_t *, uint_t);
+
+extern int lxsys_ino_get_type(ino_t);
+
+typedef struct lxpr_uiobuf {
+ uio_t *uiop;
+ char *buffer;
+ uint32_t bufsize;
+ char *pos;
+ size_t beg;
+ int error;
+} lxsys_uiobuf_t;
+
+extern lxsys_uiobuf_t *lxsys_uiobuf_new(uio_t *);
+extern void lxsys_uiobuf_free(lxsys_uiobuf_t *);
+extern void lxsys_uiobuf_seterr(lxsys_uiobuf_t *, int);
+extern int lxsys_uiobuf_flush(lxsys_uiobuf_t *);
+extern void lxsys_uiobuf_write(lxsys_uiobuf_t *, const char *, size_t);
+extern void lxsys_uiobuf_printf(lxsys_uiobuf_t *uiobuf, const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef islower
+#define islower(x) (((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
+#endif
+#ifndef toupper
+#define toupper(x) (islower(x) ? (x) - 'a' + 'A' : (x))
+#endif
+
+#endif /* _LXSYSFS_H */
diff --git a/usr/src/uts/common/brand/lx/sysfs/lx_syssubr.c b/usr/src/uts/common/brand/lx/sysfs/lx_syssubr.c
new file mode 100644
index 0000000000..69234ddbaa
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sysfs/lx_syssubr.c
@@ -0,0 +1,443 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+/*
+ * lx_syssubr.c: Various functions for the /sys vnodeops.
+ */
+
+#include <sys/varargs.h>
+
+#include <sys/cpuvar.h>
+#include <sys/mman.h>
+#include <sys/vmsystm.h>
+#include <sys/prsystm.h>
+
+#include "lx_sysfs.h"
+
+#define LXSYSCACHE_NAME "lxsys_cache"
+
+static int lxsys_node_constructor(void *, void *, int);
+static void lxsys_node_destructor(void *, void *);
+
+static kmem_cache_t *lxsys_node_cache;
+
+void
+lxsys_initnodecache()
+{
+ lxsys_node_cache = kmem_cache_create(LXSYSCACHE_NAME,
+ sizeof (lxsys_node_t), 0,
+ lxsys_node_constructor, lxsys_node_destructor, NULL, NULL, NULL, 0);
+}
+
+void
+lxsys_fininodecache()
+{
+ kmem_cache_destroy(lxsys_node_cache);
+}
+
+/* ARGSUSED */
+static int
+lxsys_node_constructor(void *buf, void *un, int kmflags)
+{
+ lxsys_node_t *lxsnp = buf;
+ vnode_t *vp;
+
+ vp = lxsnp->lxsys_vnode = vn_alloc(kmflags);
+ if (vp == NULL)
+ return (-1);
+
+ (void) vn_setops(vp, lxsys_vnodeops);
+ vp->v_data = lxsnp;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+lxsys_node_destructor(void *buf, void *un)
+{
+ lxsys_node_t *lxsnp = buf;
+
+ vn_free(LXSTOV(lxsnp));
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them
+ * to give the inode number for an lxsys node
+ */
+ino_t
+lxsys_inode(lxsys_nodetype_t type, unsigned int instance,
+ unsigned int endpoint)
+{
+ /*
+ * Sysfs Inode format:
+ * 0000AABBBBCC
+ *
+ * AA - TYPE
+ * BBBB - INSTANCE
+ * CC - ENDPOINT
+ */
+ ASSERT(instance <= 0xffff);
+ ASSERT(endpoint <= 0xff);
+
+ return ((ino_t)(type << 24)|(instance << 8)|endpoint);
+}
+
+/*
+ * Return inode number of parent (directory)
+ */
+ino_t
+lxsys_parentinode(lxsys_node_t *lxsnp)
+{
+ /*
+ * If the input node is the root then the parent inode
+ * is the mounted on inode so just return our inode number
+ */
+ if (lxsnp->lxsys_type == LXSYS_STATIC &&
+ lxsnp->lxsys_instance == LXSYS_INST_ROOT) {
+ return (lxsnp->lxsys_ino);
+ } else {
+ return (VTOLXS(lxsnp->lxsys_parentvp)->lxsys_ino);
+ }
+}
+
+/*
+ * Allocate a new lxsys node
+ *
+ * This also allocates the vnode associated with it
+ */
+lxsys_node_t *
+lxsys_getnode(vnode_t *dp, lxsys_nodetype_t type, unsigned int instance,
+ unsigned int endpoint)
+{
+ lxsys_node_t *lxsnp;
+ vnode_t *vp;
+ timestruc_t now;
+
+ /*
+ * Allocate a new node. It is deallocated in vop_innactive
+ */
+ lxsnp = kmem_cache_alloc(lxsys_node_cache, KM_SLEEP);
+
+ /*
+ * Set defaults (may be overridden below)
+ */
+ gethrestime(&now);
+ lxsnp->lxsys_type = type;
+ lxsnp->lxsys_instance = instance;
+ lxsnp->lxsys_endpoint = endpoint;
+ lxsnp->lxsys_next = NULL;
+ lxsnp->lxsys_parentvp = dp;
+ VN_HOLD(dp);
+
+ lxsnp->lxsys_time = now;
+ lxsnp->lxsys_uid = lxsnp->lxsys_gid = 0;
+ lxsnp->lxsys_ino = lxsys_inode(type, instance, endpoint);
+
+ /* initialize the vnode data */
+ vp = lxsnp->lxsys_vnode;
+ vn_reinit(vp);
+ vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
+ vp->v_vfsp = dp->v_vfsp;
+
+ /*
+ * Default to a directory with open permissions.
+ * Specific components will override this
+ */
+ if (type == LXSYS_STATIC && instance == LXSYS_INST_ROOT) {
+ vp->v_flag |= VROOT;
+ }
+ vp->v_type = VDIR;
+ lxsnp->lxsys_mode = 0555;
+
+ return (lxsnp);
+}
+
+lxsys_node_t *
+lxsys_getnode_static(vnode_t *dp, unsigned int instance)
+{
+ lxsys_mnt_t *lxsm = VTOLXSM(dp);
+ lxsys_node_t *lnp, *tail = NULL;
+
+ mutex_enter(&lxsm->lxsysm_lock);
+ for (lnp = lxsm->lxsysm_node; lnp != NULL; lnp = lnp->lxsys_next) {
+ if (lnp->lxsys_instance == instance) {
+ VERIFY(lnp->lxsys_parentvp == dp);
+
+ VN_HOLD(lnp->lxsys_vnode);
+ mutex_exit(&lxsm->lxsysm_lock);
+ return (lnp);
+ } else if (lnp->lxsys_next == NULL) {
+ /* Found no match by the end of the list */
+ tail = lnp;
+ break;
+ }
+ }
+
+ tail->lxsys_next = lxsys_getnode(dp, LXSYS_STATIC, instance, 0);
+ lnp = tail->lxsys_next;
+ /* Allow mounts on static entries */
+ LXSTOV(lnp)->v_flag &= (~VNOMOUNT);
+ mutex_exit(&lxsm->lxsysm_lock);
+ return (lnp);
+}
+
+/* Clean up persistence for static lxsys_node */
+int
+lxsys_freenode_static(lxsys_node_t *lnp)
+{
+ lxsys_node_t *plnp;
+ vnode_t *vp = LXSTOV(lnp);
+ lxsys_mnt_t *lxsm = VTOLXSM(vp);
+
+ if (lnp->lxsys_instance == LXSYS_INST_ROOT) {
+ /*
+ * The root vnode does not need special cleanup since it
+ * anchors the list and is freed by lxsys_unmount.
+ */
+ return (0);
+ }
+
+ mutex_enter(&lxsm->lxsysm_lock);
+
+ /*
+ * It is possible that a different process acquired a fresh reference
+ * to this vnode via lookup while we were waiting on the lxsysm_lock.
+ * To avoid freeing the vnode out from under them, we will double-check
+ * v_count and bail from the fop_inactive if it was grabbed.
+ */
+ mutex_enter(&vp->v_lock);
+ if (vp->v_count != 1) {
+ VERIFY(vp->v_count > 0);
+
+ /* Release our hold before bailing out of lxsys_inactive */
+ vp->v_count--;
+
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&lxsm->lxsysm_lock);
+ return (-1);
+ }
+ mutex_exit(&vp->v_lock);
+
+ /* search for the record pointing to lnp */
+ plnp = lxsm->lxsysm_node;
+ while (plnp != NULL && plnp->lxsys_next != lnp) {
+ plnp = plnp->lxsys_next;
+ }
+ /* entry should always be found */
+ VERIFY(plnp != NULL);
+ plnp->lxsys_next = lnp->lxsys_next;
+
+ mutex_exit(&lxsm->lxsysm_lock);
+ return (0);
+}
+
+/*
+ * Free the storage obtained from lxsys_getnode().
+ */
+void
+lxsys_freenode(lxsys_node_t *lxsnp)
+{
+ vnode_t *vp = LXSTOV(lxsnp);
+
+ VERIFY(vp != NULL);
+
+ if (lxsnp->lxsys_type == LXSYS_STATIC) {
+ if (lxsys_freenode_static(lxsnp) != 0) {
+ return;
+ }
+ }
+
+ /*
+ * delete any association with parent vp
+ */
+ if (lxsnp->lxsys_parentvp != NULL)
+ VN_RELE(lxsnp->lxsys_parentvp);
+
+ /*
+ * Release the lxsysnode.
+ */
+ kmem_cache_free(lxsys_node_cache, lxsnp);
+}
+
+/*
+ * Get the netstack associated with this lxsys mount
+ */
+netstack_t *
+lxsys_netstack(lxsys_node_t *lnp)
+{
+ zone_t *zone = VTOLXSM(LXSTOV(lnp))->lxsysm_zone;
+
+ return (netstack_hold_if_active(zone->zone_netstack));
+}
+
+ill_t *
+lxsys_find_ill(ip_stack_t *ipst, uint_t ifindex)
+{
+ ill_t *ill;
+ phyint_t *phyi;
+
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+ phyi = avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
+ (void *) &ifindex, NULL);
+ if (phyi != NULL) {
+ /*
+ * Since interface information presented via /sys is not
+ * specific to IPv4 or IPv6, an ill reference from either
+ * protocol will be adequate. Check both, starting with IPv4
+ * for a valid reference to use.
+ */
+ for (ill = phyi->phyint_illv4; ill != phyi->phyint_illv6;
+ ill = phyi->phyint_illv6) {
+ if (ill != NULL) {
+ mutex_enter(&ill->ill_lock);
+ if (!ILL_IS_CONDEMNED(ill)) {
+ ill_refhold_locked(ill);
+ mutex_exit(&ill->ill_lock);
+ rw_exit(&ipst->ips_ill_g_lock);
+ return (ill);
+ }
+ mutex_exit(&ill->ill_lock);
+ }
+ }
+ }
+ rw_exit(&ipst->ips_ill_g_lock);
+ return (NULL);
+}
+
+
+#define LXSYSUIOBUFSZ 4096
+
+lxsys_uiobuf_t *
+lxsys_uiobuf_new(uio_t *uiop)
+{
+ /* Allocate memory for both lxsys_uiobuf and output buffer */
+ int bufsize = LXSYSUIOBUFSZ;
+ lxsys_uiobuf_t *uiobuf =
+ kmem_alloc(sizeof (lxsys_uiobuf_t) + bufsize, KM_SLEEP);
+
+ uiobuf->uiop = uiop;
+ uiobuf->buffer = (char *)&uiobuf[1];
+ uiobuf->bufsize = bufsize;
+ uiobuf->pos = uiobuf->buffer;
+ uiobuf->beg = 0;
+ uiobuf->error = 0;
+
+ return (uiobuf);
+}
+
+void
+lxsys_uiobuf_free(lxsys_uiobuf_t *uiobuf)
+{
+ ASSERT(uiobuf != NULL);
+ ASSERT(uiobuf->pos == uiobuf->buffer);
+
+ kmem_free(uiobuf, sizeof (lxsys_uiobuf_t) + uiobuf->bufsize);
+}
+
+void
+lxsys_uiobuf_seterr(lxsys_uiobuf_t *uiobuf, int err)
+{
+ ASSERT(uiobuf->error == 0);
+
+ uiobuf->error = err;
+}
+
+int
+lxsys_uiobuf_flush(lxsys_uiobuf_t *uiobuf)
+{
+ off_t off = uiobuf->uiop->uio_offset;
+ caddr_t uaddr = uiobuf->buffer;
+ size_t beg = uiobuf->beg;
+ size_t size = (uintptr_t)uiobuf->pos - (uintptr_t)uaddr;
+
+ if (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ ASSERT(off >= beg);
+
+ if (beg + size > off && off >= 0)
+ uiobuf->error =
+ uiomove(uaddr + (off - beg), size - (off - beg),
+ UIO_READ, uiobuf->uiop);
+
+ uiobuf->beg += size;
+ }
+
+ uiobuf->pos = uaddr;
+
+ return (uiobuf->error);
+}
+
+void
+lxsys_uiobuf_write(lxsys_uiobuf_t *uiobuf, const char *buf, size_t size)
+{
+ /* While we can still carry on */
+ while (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ uintptr_t remain = (uintptr_t)uiobuf->bufsize -
+ ((uintptr_t)uiobuf->pos - (uintptr_t)uiobuf->buffer);
+
+ /* Enough space in buffer? */
+ if (remain >= size) {
+ bcopy(buf, uiobuf->pos, size);
+ uiobuf->pos += size;
+ return;
+ }
+
+ /* Not enough space, so copy all we can and try again */
+ bcopy(buf, uiobuf->pos, remain);
+ uiobuf->pos += remain;
+ (void) lxsys_uiobuf_flush(uiobuf);
+ buf += remain;
+ size -= remain;
+ }
+}
+
+#define TYPBUFFSIZE 256
+
+void
+lxsys_uiobuf_printf(lxsys_uiobuf_t *uiobuf, const char *fmt, ...)
+{
+ va_list args;
+ char buff[TYPBUFFSIZE];
+ int len;
+ char *buffer;
+
+ /* Can we still do any output */
+ if (uiobuf->error != 0 || uiobuf->uiop->uio_resid == 0)
+ return;
+
+ va_start(args, fmt);
+
+ /* Try using stack allocated buffer */
+ len = vsnprintf(buff, TYPBUFFSIZE, fmt, args);
+ if (len < TYPBUFFSIZE) {
+ va_end(args);
+ lxsys_uiobuf_write(uiobuf, buff, len);
+ return;
+ }
+
+ /* Not enough space in pre-allocated buffer */
+ buffer = kmem_alloc(len + 1, KM_SLEEP);
+
+ /*
+ * We know we allocated the correct amount of space
+ * so no check on the return value
+ */
+ (void) vsnprintf(buffer, len+1, fmt, args);
+ lxsys_uiobuf_write(uiobuf, buffer, len);
+ va_end(args);
+ kmem_free(buffer, len+1);
+}
diff --git a/usr/src/uts/common/brand/lx/sysfs/lx_sysvfsops.c b/usr/src/uts/common/brand/lx/sysfs/lx_sysvfsops.c
new file mode 100644
index 0000000000..fddc1e0234
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sysfs/lx_sysvfsops.c
@@ -0,0 +1,365 @@
+/*
+ * 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 2017 Joyent, Inc.
+ */
+
+/*
+ * lxsysvfsops.c: vfs operations for lx sysfs.
+ *
+ * sysfs has a close relationship with the lx getdents(2) syscall. This is
+ * necessary so that the getdents code can populate the 'd_type' entries
+ * during a sysfs readdir operation. The glibc code which accesses sysfs
+ * (specifically the 'cpu' subtree) expects dirents to have the d_type field
+ * populated. One problematic consumer is java, which becomes unstable if it
+ * gets the incorrect data from glibc. When sysfs loads, it populates the
+ * lx_sysfs_vfs_type and lx_sysfs_vtype variables defined in lx_getdents.c.
+ * The getdents code can then call into sysfs to determine the d_type for any
+ * given inode directory entry.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/mode.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#include <sys/mount.h>
+#include <sys/bitmap.h>
+#include <sys/kmem.h>
+#include <sys/policy.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/lx_impl.h>
+
+#include "lx_sysfs.h"
+
+/* Module level parameters */
+static int lxsysfstype;
+static dev_t lxsysdev;
+static kmutex_t lxsys_mount_lock;
+
+extern int lx_sysfs_vfs_type;
+extern int (*lx_sysfs_vtype)(ino_t);
+
+static int lxsys_mount(vfs_t *, vnode_t *, mounta_t *, cred_t *);
+static int lxsys_unmount(vfs_t *, int, cred_t *);
+static int lxsys_root(vfs_t *, vnode_t **);
+static int lxsys_statvfs(vfs_t *, statvfs64_t *);
+static int lxsys_init(int, char *);
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "lx_sysfs",
+ lxsys_init,
+ VSW_ZMOUNT,
+ NULL
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+ &mod_fsops, "lx brand sysfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlfs, NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int retval;
+
+ /*
+ * attempt to unload the module
+ */
+ if ((retval = mod_remove(&modlinkage)) != 0)
+ goto done;
+
+ lx_sysfs_vfs_type = 0;
+ lx_sysfs_vtype = NULL;
+
+ /*
+ * destroy lxsys_node cache
+ */
+ lxsys_fininodecache();
+
+ /*
+ * clean out the vfsops and vnodeops
+ */
+ (void) vfs_freevfsops_by_type(lxsysfstype);
+ vn_freevnodeops(lxsys_vnodeops);
+
+ mutex_destroy(&lxsys_mount_lock);
+done:
+ return (retval);
+}
+
+static int
+lxsys_init(int fstype, char *name)
+{
+ static const fs_operation_def_t lxsys_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = lxsys_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = lxsys_unmount },
+ VFSNAME_ROOT, { .vfs_root = lxsys_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = lxsys_statvfs },
+ NULL, NULL
+ };
+ extern const fs_operation_def_t lxsys_vnodeops_template[];
+ int error;
+ major_t dev;
+
+ lx_sysfs_vtype = lxsys_ino_get_type;
+ lx_sysfs_vfs_type = lxsysfstype = fstype;
+ ASSERT(lxsysfstype != 0);
+
+ mutex_init(&lxsys_mount_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /*
+ * Associate VFS ops vector with this fstype.
+ */
+ error = vfs_setfsops(fstype, lxsys_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "lxsys_init: bad vfs ops template");
+ return (error);
+ }
+
+ /*
+ * Set up vnode ops vector too.
+ */
+ error = vn_make_ops(name, lxsys_vnodeops_template, &lxsys_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "lxsys_init: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * Assign a unique "device" number (used by stat(2)).
+ */
+ if ((dev = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "lxsys_init: can't get unique device number");
+ dev = 0;
+ }
+
+ /*
+ * Make the pseudo device
+ */
+ lxsysdev = makedevice(dev, 0);
+
+ /*
+ * Initialise cache for lxsys_nodes
+ */
+ lxsys_initnodecache();
+
+ return (0);
+}
+
+static int
+lxsys_mount(vfs_t *vfsp, vnode_t *mvp, mounta_t *uap, cred_t *cr)
+{
+ lxsys_mnt_t *lxsys_mnt;
+ zone_t *zone = curproc->p_zone;
+
+ /*
+ * must be root to mount
+ */
+ if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+ return (EPERM);
+
+ /*
+ * mount point must be a directory
+ */
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ if (zone == global_zone) {
+ zone_t *mntzone;
+
+ mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
+ zone_rele(mntzone);
+ if (zone != mntzone)
+ return (EBUSY);
+ }
+
+ /*
+ * Having the resource be anything but "lxsys" doesn't make sense
+ */
+ vfs_setresource(vfsp, "lxsys", 0);
+
+ lxsys_mnt = kmem_alloc(sizeof (*lxsys_mnt), KM_SLEEP);
+
+ mutex_enter(&lxsys_mount_lock);
+
+ /*
+ * Ensure we don't allow overlaying mounts
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ mutex_exit(&lxsys_mount_lock);
+ kmem_free(lxsys_mnt, sizeof ((*lxsys_mnt)));
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+
+ mutex_init(&lxsys_mnt->lxsysm_lock, NULL, MUTEX_DEFAULT, NULL);
+ zone_hold(lxsys_mnt->lxsysm_zone = zone);
+
+ /* Arbitrarily set the parent vnode to the mounted over directory */
+ lxsys_mnt->lxsysm_node = lxsys_getnode(mvp, LXSYS_STATIC,
+ LXSYS_INST_ROOT, 0);
+ lxsys_mnt->lxsysm_node->lxsys_next = NULL;
+
+ /* Correctly set the fs for the root node */
+ lxsys_mnt->lxsysm_node->lxsys_vnode->v_vfsp = vfsp;
+
+ vfs_make_fsid(&vfsp->vfs_fsid, lxsysdev, lxsysfstype);
+ vfsp->vfs_bsize = DEV_BSIZE;
+ vfsp->vfs_fstype = lxsysfstype;
+ vfsp->vfs_data = (caddr_t)lxsys_mnt;
+ vfsp->vfs_dev = lxsysdev;
+
+ mutex_exit(&lxsys_mount_lock);
+
+ return (0);
+}
+
+static int
+lxsys_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+ lxsys_mnt_t *lxsys_mnt = (lxsys_mnt_t *)vfsp->vfs_data;
+ lxsys_node_t *lnp;
+ vnode_t *vp;
+ int count;
+
+ VERIFY(lxsys_mnt != NULL);
+
+ mutex_enter(&lxsys_mount_lock);
+
+ /* must be root to unmount */
+ if (secpolicy_fs_unmount(cr, vfsp) != 0) {
+ mutex_exit(&lxsys_mount_lock);
+ return (EPERM);
+ }
+
+ /* forced unmount is not supported by this fs */
+ if (flag & MS_FORCE) {
+ mutex_exit(&lxsys_mount_lock);
+ return (ENOTSUP);
+ }
+
+ /* Ensure that no vnodes are in use on this mount point. */
+ lnp = lxsys_mnt->lxsysm_node;
+ vp = LXSTOV(lnp);
+ mutex_enter(&vp->v_lock);
+ count = vp->v_count;
+ mutex_exit(&vp->v_lock);
+ if (count > 1) {
+ mutex_exit(&lxsys_mount_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * If there are no references to the root vnode the list of persistent
+ * static vnodes should be empty
+ */
+ VERIFY(lnp->lxsys_next == NULL);
+
+ (void) dnlc_purge_vfsp(vfsp, 0);
+
+ lxsys_mnt->lxsysm_node = NULL;
+ lxsys_freenode(lnp);
+ zone_rele(lxsys_mnt->lxsysm_zone);
+ vfsp->vfs_data = NULL;
+ kmem_free(lxsys_mnt, sizeof (*lxsys_mnt));
+
+ mutex_exit(&lxsys_mount_lock);
+
+ return (0);
+}
+
+static int
+lxsys_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ lxsys_mnt_t *lxsm = (lxsys_mnt_t *)vfsp->vfs_data;
+ vnode_t *vp;
+
+ VERIFY(lxsm != NULL);
+ VERIFY(lxsm->lxsysm_node != NULL);
+
+ vp = LXSTOV(lxsm->lxsysm_node);
+ VN_HOLD(vp);
+ *vpp = vp;
+
+ return (0);
+}
+
+static int
+lxsys_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+ dev32_t d32;
+
+ bzero((caddr_t)sp, sizeof (*sp));
+ sp->f_bsize = DEV_BSIZE;
+ sp->f_frsize = DEV_BSIZE;
+ sp->f_blocks = (fsblkcnt64_t)0;
+ sp->f_bfree = (fsblkcnt64_t)0;
+ sp->f_bavail = (fsblkcnt64_t)0;
+ sp->f_files = (fsfilcnt64_t)3;
+ sp->f_ffree = (fsfilcnt64_t)0; /* none */
+ sp->f_favail = (fsfilcnt64_t)0; /* none */
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sp->f_fsid = d32;
+ /* It is guaranteed that vsw_name will fit in f_basetype */
+ (void) strcpy(sp->f_basetype, vfssw[lxsysfstype].vsw_name);
+ sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sp->f_namemax = 64; /* quite arbitrary */
+ bzero(sp->f_fstr, sizeof (sp->f_fstr));
+
+ /* We know f_fstr is 32 chars */
+ (void) strcpy(sp->f_fstr, "/sys");
+ (void) strcpy(&sp->f_fstr[6], "/sys");
+
+ return (0);
+}
diff --git a/usr/src/uts/common/brand/lx/sysfs/lx_sysvnops.c b/usr/src/uts/common/brand/lx/sysfs/lx_sysvnops.c
new file mode 100644
index 0000000000..10c99baa7b
--- /dev/null
+++ b/usr/src/uts/common/brand/lx/sysfs/lx_sysvnops.c
@@ -0,0 +1,2165 @@
+/*
+ * 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 2017 Joyent, Inc.
+ */
+
+/*
+ * lx_sysfs -- a Linux-compatible /sys for the LX brand
+ */
+
+#include <vm/seg_vn.h>
+#include <sys/sdt.h>
+#include <sys/strlog.h>
+#include <sys/stropts.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_brand.h>
+#include <sys/x86_archext.h>
+#include <sys/archsystm.h>
+#include <sys/fp.h>
+#include <sys/pool_pset.h>
+#include <sys/pset.h>
+#include <sys/zone.h>
+#include <sys/pghw.h>
+#include <sys/vfs_opreg.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <sys/lx_misc.h>
+#include <sys/brand.h>
+#include <sys/cred_impl.h>
+#include <sys/tihdr.h>
+#include <sys/sunddi.h>
+#include <sys/vnode.h>
+#include <sys/netstack.h>
+#include <sys/ethernet.h>
+#include <inet/ip_arp.h>
+
+#include "lx_sysfs.h"
+
+/*
+ * Pointer to the vnode ops vector for this fs.
+ * This is instantiated in lxsys_init() in lx_sysvfsops.c
+ */
+vnodeops_t *lxsys_vnodeops;
+
+static int lxsys_open(vnode_t **, int, cred_t *, caller_context_t *);
+static int lxsys_close(vnode_t *, int, int, offset_t, cred_t *,
+ caller_context_t *);
+static int lxsys_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxsys_getattr(vnode_t *, vattr_t *, int, cred_t *,
+ caller_context_t *);
+static int lxsys_access(vnode_t *, int, int, cred_t *, caller_context_t *);
+static int lxsys_lookup(vnode_t *, char *, vnode_t **,
+ pathname_t *, int, vnode_t *, cred_t *, caller_context_t *, int *,
+ pathname_t *);
+static int lxsys_readdir(vnode_t *, uio_t *, cred_t *, int *,
+ caller_context_t *, int);
+static int lxsys_readlink(vnode_t *, uio_t *, cred_t *, caller_context_t *);
+static int lxsys_cmp(vnode_t *, vnode_t *, caller_context_t *);
+static int lxsys_sync(void);
+static void lxsys_inactive(vnode_t *, cred_t *, caller_context_t *);
+
+static vnode_t *lxsys_lookup_static(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_class_netdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_virtual_netdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_blockdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_zfsdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_syscpu(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_syscpuinfo(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_sysnode(lxsys_node_t *, char *);
+
+static int lxsys_read_static(lxsys_node_t *, lxsys_uiobuf_t *);
+static int lxsys_read_devices_virtual_net(lxsys_node_t *, lxsys_uiobuf_t *);
+static int lxsys_read_devices_zfs_block(lxsys_node_t *, lxsys_uiobuf_t *);
+static int lxsys_read_devices_syscpu(lxsys_node_t *, lxsys_uiobuf_t *);
+static int lxsys_read_devices_sysnode(lxsys_node_t *, lxsys_uiobuf_t *);
+
+static int lxsys_readdir_devices_syscpu(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_syscpuinfo(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_sysnode(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_static(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_class_netdir(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_virtual_netdir(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_blockdir(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_zfsdir(lxsys_node_t *, uio_t *, int *);
+
+static int lxsys_readlink_class_net(lxsys_node_t *, char *, size_t);
+static int lxsys_readlink_block(lxsys_node_t *, char *, size_t);
+
+/*
+ * The lx /sys vnode operations vector
+ */
+const fs_operation_def_t lxsys_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = lxsys_open },
+ VOPNAME_CLOSE, { .vop_close = lxsys_close },
+ VOPNAME_READ, { .vop_read = lxsys_read },
+ VOPNAME_GETATTR, { .vop_getattr = lxsys_getattr },
+ VOPNAME_ACCESS, { .vop_access = lxsys_access },
+ VOPNAME_LOOKUP, { .vop_lookup = lxsys_lookup },
+ VOPNAME_READDIR, { .vop_readdir = lxsys_readdir },
+ VOPNAME_READLINK, { .vop_readlink = lxsys_readlink },
+ VOPNAME_FSYNC, { .error = lxsys_sync },
+ VOPNAME_SEEK, { .error = lxsys_sync },
+ VOPNAME_INACTIVE, { .vop_inactive = lxsys_inactive },
+ VOPNAME_CMP, { .vop_cmp = lxsys_cmp },
+ NULL, NULL
+};
+
+typedef enum lxsys_cpu_state {
+ LXSYS_CPU_ON, /* online */
+ LXSYS_CPU_OFF, /* offline */
+ LXSYS_CPU_ANY, /* don't care */
+} lxsys_cpu_state_t;
+
+static void lxsys_format_cpu(char *, int, lxsys_cpu_state_t);
+
+/*
+ * Sysfs Inode format:
+ * 0000AABBBBCC
+ *
+ * AA - TYPE
+ * BBBB - INSTANCE
+ * CC - ENDPOINT
+ *
+ * Where TYPE is one of:
+ * 1 - SYS_STATIC
+ * 2 - SYS_CLASS_NET
+ * 3 - SYS_DEV_NET
+ * 4 - SYS_BLOCK
+ * 5 - SYS_DEV_ZFS
+ * 6 - SYS_DEV_SYS_CPU
+ * 7 - SYS_DEV_SYS_CPUINFO
+ * 8 - SYS_DEV_SYS_NODE
+ *
+ * Static entries will have assigned INSTANCE identifiers:
+ * - 0x00: /sys
+ * - 0x01: /sys/class
+ * - 0x02: /sys/devices
+ * - 0x03: /sys/fs
+ * - 0x04: /sys/class/net
+ * - 0x05: /sys/devices/virtual
+ * - 0x06: /sys/devices/system
+ * - 0x07: /sys/fs/cgroup
+ * - 0x08: /sys/devices/virtual/net
+ * - 0x09: /sys/block
+ * - 0x0a: /sys/devices/zfs
+ * - 0x0b: /sys/devices/system/cpu
+ * - 0x0c: /sys/devices/system/node
+ * - 0x0d: /sys/bus
+ *
+ * Dynamic /sys/class/net/<interface> symlinks will use an INSTANCE derived
+ * from the corresonding ifindex.
+ *
+ * Dynamic /sys/devices/virtual/net/<interface>/<entries> directories will use
+ * an INSTANCE derived from the ifindex and statically assigned ENDPOINT IDs
+ * for the contained entries.
+ *
+ * Dynamic /sys/block/<dev> symlinks will use an INSTANCE derived from the
+ * device major and instance from records listed in kstat or zvols.
+ *
+ * Dynamic /sys/devices/zfs/<dev> directories will use an INSTANCE derived from
+ * the emulated minor number.
+ *
+ * Semi-static/Dynamic /sys/devices/system/cpu contains the fixed 'kernel_max',
+ * 'offline', 'online', 'possible', and 'present' files, and a dynamic set of
+ * cpuN subdirectories. All of these are dynamic nodes.
+ *
+ * Static /sys/devices/system/node/node0 currently only contains a
+ * static cpulist file, but will likely need future dynamic entries for cpuN
+ * symlinks, and perhaps other static files. By only providing 'node0' we
+ * pretend that there is only a single NUMA node available to a zone (trying to
+ * be NUMA-aware inside a zone is generally not going to work anyway).
+ * If dynamic entries are added under node0, it must be converted to the
+ * semi-static/dynamic approach as used under /sys/devices/system/cpu.
+ *
+ * The dyn_ino_type table must be updated whenever a new static instance is
+ * defined.
+ */
+
+#define LXSYS_INST_CLASSDIR 0x1
+#define LXSYS_INST_DEVICESDIR 0x2
+#define LXSYS_INST_FSDIR 0x3
+#define LXSYS_INST_CLASS_NETDIR 0x4
+#define LXSYS_INST_DEVICES_VIRTUALDIR 0x5
+#define LXSYS_INST_DEVICES_SYSTEMDIR 0x6
+#define LXSYS_INST_FS_CGROUPDIR 0x7
+#define LXSYS_INST_DEVICES_VIRTUAL_NETDIR 0x8
+#define LXSYS_INST_BLOCKDIR 0x9
+#define LXSYS_INST_DEVICES_ZFSDIR 0xa
+#define LXSYS_INST_DEVICES_SYSCPU 0xb
+#define LXSYS_INST_DEVICES_SYSNODE 0xc
+#define LXSYS_INST_BUSDIR 0xd
+#define LXSYS_INST_MAX LXSYS_INST_BUSDIR /* limit */
+
+/*
+ * These are of dynamic type (LXSYS_DEV_SYS_CPU), but essentially fixed
+ * instances. Under /sys/devices/system/cpu we have: kernel_max, offline,
+ * online, possible and present. We also have a dynamic set of cpuN subdirs.
+ * The cpuN subdirs are actually of type LXSYS_DEV_SYS_CPUINFO, so we can use
+ * the following instance IDs for the fixed files.
+ */
+#define LXSYS_INST_DEV_SYSCPU_KMAX 0x1
+#define LXSYS_INST_DEV_SYSCPU_OFFLINE 0x2
+#define LXSYS_INST_DEV_SYSCPU_ONLINE 0x3
+#define LXSYS_INST_DEV_SYSCPU_POSSIBLE 0x4
+#define LXSYS_INST_DEV_SYSCPU_PRESENT 0x5
+
+/*
+ * This array is used for directory inode correction in lxsys_readdir_common
+ * when a directory's static-type entry is actually a dynamic-type.
+ */
+static int dyn_ino_type [] = {
+ 0, /* invalid */
+ 0, /* LXSYS_INST_CLASSDIR */
+ 0, /* LXSYS_INST_DEVICESDIR */
+ 0, /* LXSYS_INST_FSDIR */
+ LXSYS_CLASS_NET, /* LXSYS_INST_CLASS_NETDIR */
+ 0, /* LXSYS_INST_DEVICES_VIRTUALDIR */
+ 0, /* LXSYS_INST_DEVICES_SYSTEMDIR */
+ 0, /* LXSYS_INST_FS_CGROUPDIR */
+ LXSYS_DEV_NET, /* LXSYS_INST_DEV_VIRTUAL_NETDIR */
+ LXSYS_BLOCK, /* LXSYS_INST_BLOCKDIR */
+ LXSYS_DEV_ZFS, /* LXSYS_INST_DEVICES_ZFSDIR */
+ LXSYS_DEV_SYS_CPU, /* LXSYS_INST_DEVICES_SYSCPU */
+ LXSYS_DEV_SYS_NODE, /* LXSYS_INST_DEV_SYSNODE */
+ 0, /* LXSYS_INST_BUSDIR */
+};
+#define DYN_INO_LEN \
+ (sizeof (dyn_ino_type) / sizeof ((dyn_ino_type)[0]))
+
+/*
+ * file contents of an lx /sys directory.
+ */
+static lxsys_dirent_t dirlist_root[] = {
+ { LXSYS_INST_BLOCKDIR, "block" },
+ { LXSYS_INST_BUSDIR, "bus" },
+ { LXSYS_INST_CLASSDIR, "class" },
+ { LXSYS_INST_DEVICESDIR, "devices" },
+ { LXSYS_INST_FSDIR, "fs" }
+};
+static lxsys_dirent_t dirlist_class[] = {
+ { LXSYS_INST_CLASS_NETDIR, "net" }
+};
+static lxsys_dirent_t dirlist_fs[] = {
+ { LXSYS_INST_FS_CGROUPDIR, "cgroup" }
+};
+static lxsys_dirent_t dirlist_devices[] = {
+ { LXSYS_INST_DEVICES_SYSTEMDIR, "system" },
+ { LXSYS_INST_DEVICES_VIRTUALDIR, "virtual" },
+ { LXSYS_INST_DEVICES_ZFSDIR, "zfs" }
+};
+static lxsys_dirent_t dirlist_devices_virtual[] = {
+ { LXSYS_INST_DEVICES_VIRTUAL_NETDIR, "net" }
+};
+
+static lxsys_dirent_t dirlist_devices_system[] = {
+ { LXSYS_INST_DEVICES_SYSCPU, "cpu" },
+ { LXSYS_INST_DEVICES_SYSNODE, "node" }
+};
+
+#define LXSYS_ENDP_NET_ADDRESS 1
+#define LXSYS_ENDP_NET_ADDRLEN 2
+#define LXSYS_ENDP_NET_FLAGS 3
+#define LXSYS_ENDP_NET_IFINDEX 4
+#define LXSYS_ENDP_NET_MTU 5
+#define LXSYS_ENDP_NET_TXQLEN 6
+#define LXSYS_ENDP_NET_TYPE 7
+
+#define LXSYS_ENDP_BLOCK_DEVICE 1
+
+#define LXSYS_ENDP_NODE_CPULIST 1
+#define LXSYS_ENDP_NODE_CPUMAP 2
+
+static lxsys_dirent_t dirlist_devices_virtual_net[] = {
+ { LXSYS_ENDP_NET_ADDRESS, "address" },
+ { LXSYS_ENDP_NET_ADDRLEN, "addr_len" },
+ { LXSYS_ENDP_NET_FLAGS, "flags" },
+ { LXSYS_ENDP_NET_IFINDEX, "ifindex" },
+ { LXSYS_ENDP_NET_MTU, "mtu" },
+ { LXSYS_ENDP_NET_TXQLEN, "tx_queue_len" },
+ { LXSYS_ENDP_NET_TYPE, "type" }
+};
+
+static lxsys_dirent_t dirlist_devices_zfs_block[] = {
+ { LXSYS_ENDP_BLOCK_DEVICE, "device" }
+};
+
+static lxsys_dirent_t dirlist_devices_sysnode[] = {
+ { LXSYS_ENDP_NODE_CPULIST, "cpulist" },
+ { LXSYS_ENDP_NODE_CPUMAP, "cpumap" }
+};
+
+#define SYSDIRLISTSZ(l) (sizeof (l) / sizeof ((l)[0]))
+
+#define SYSDLENT(i, l) { i, l, SYSDIRLISTSZ(l) }
+static lxsys_dirlookup_t lxsys_dirlookup[] = {
+ SYSDLENT(LXSYS_INST_ROOT, dirlist_root),
+ SYSDLENT(LXSYS_INST_CLASSDIR, dirlist_class),
+ SYSDLENT(LXSYS_INST_FSDIR, dirlist_fs),
+ { LXSYS_INST_FS_CGROUPDIR, NULL, 0 },
+ SYSDLENT(LXSYS_INST_DEVICESDIR, dirlist_devices),
+ SYSDLENT(LXSYS_INST_DEVICES_SYSTEMDIR, dirlist_devices_system),
+ SYSDLENT(LXSYS_INST_DEVICES_VIRTUALDIR, dirlist_devices_virtual),
+ SYSDLENT(LXSYS_INST_DEVICES_SYSNODE, dirlist_devices_sysnode),
+ { LXSYS_INST_BUSDIR, NULL, 0 },
+};
+
+
+/*
+ * Array of lookup functions, indexed by lx /sys file type.
+ */
+static vnode_t *(*lxsys_lookup_function[LXSYS_MAXTYPE])() = {
+ NULL, /* LXSYS_NONE */
+ lxsys_lookup_static, /* LXSYS_STATIC */
+ lxsys_lookup_class_netdir, /* LXSYS_CLASS_NET */
+ lxsys_lookup_devices_virtual_netdir, /* LXSYS_DEV_NET */
+ lxsys_lookup_blockdir, /* LXSYS_BLOCK */
+ lxsys_lookup_devices_zfsdir, /* LXSYS_DEV_ZFS */
+ lxsys_lookup_devices_syscpu, /* LXSYS_DEV_SYS_CPU */
+ lxsys_lookup_devices_syscpuinfo, /* LXSYS_DEV_SYS_CPUINFO */
+ lxsys_lookup_devices_sysnode, /* LXSYS_DEV_SYS_NODE */
+};
+
+/*
+ * Array of readdir functions, indexed by /sys file type.
+ */
+static int (*lxsys_readdir_function[LXSYS_MAXTYPE])() = {
+ NULL, /* LXSYS_NONE */
+ lxsys_readdir_static, /* LXSYS_STATIC */
+ lxsys_readdir_class_netdir, /* LXSYS_CLASS_NET */
+ lxsys_readdir_devices_virtual_netdir, /* LXSYS_DEV_NET */
+ lxsys_readdir_blockdir, /* LXSYS_BLOCK */
+ lxsys_readdir_devices_zfsdir, /* LXSYS_DEV_ZFS */
+ lxsys_readdir_devices_syscpu, /* LXSYS_DEV_SYS_CPU */
+ lxsys_readdir_devices_syscpuinfo, /* LXSYS_DEV_SYS_CPUINFO */
+ lxsys_readdir_devices_sysnode, /* LXSYS_DEV_SYS_NODE */
+};
+
+/*
+ * Array of read functions, indexed by /sys file type.
+ */
+static int (*lxsys_read_function[LXSYS_MAXTYPE])() = {
+ NULL, /* LXSYS_NONE */
+ lxsys_read_static, /* LXSYS_STATIC */
+ NULL, /* LXSYS_CLASS_NET */
+ lxsys_read_devices_virtual_net, /* LXSYS_DEV_NET */
+ NULL, /* LXSYS_BLOCK */
+ lxsys_read_devices_zfs_block, /* LXSYS_DEV_ZFS */
+ lxsys_read_devices_syscpu, /* LXSYS_DEV_SYS_CPU */
+ NULL, /* LXSYS_DEV_SYS_CPUINFO */
+ lxsys_read_devices_sysnode, /* LXSYS_DEV_SYS_NODE */
+};
+
+/*
+ * Array of readlink functions, indexed by /sys file type.
+ */
+static int (*lxsys_readlink_function[LXSYS_MAXTYPE])() = {
+ NULL, /* LXSYS_NONE */
+ NULL, /* LXSYS_STATIC */
+ lxsys_readlink_class_net, /* LXSYS_CLASS_NET */
+ NULL, /* LXSYS_DEV_NET */
+ lxsys_readlink_block, /* LXSYS_BLOCK */
+ NULL, /* LXSYS_DEV_ZFS */
+ NULL, /* LXSYS_DEV_SYS_CPU */
+ NULL, /* LXSYS_DEV_SYS_CPUINFO */
+ NULL, /* LXSYS_DEV_SYS_NODE */
+};
+
+/*
+ * Given one of our inodes, return the vnode type.
+ *
+ * lxsys_getnode will always set the vnode type to VDIR. It expects the
+ * caller (normally the lookup functions) to fix the type. Those same rules are
+ * encoded here for our inode-to-type translation.
+ */
+int
+lxsys_ino_get_type(ino_t ino)
+{
+ lxsys_nodetype_t type;
+ unsigned int instance;
+ unsigned int endpoint;
+
+ type = (ino & 0xff000000) >> 24;
+ instance = (ino & 0xffff00) >> 8;
+ endpoint = (ino & 0xff);
+
+ if (instance > LXSYS_INST_MAX)
+ return (VNON);
+
+ /* Validate non-static node types */
+ if (type != LXSYS_STATIC &&
+ (type <= LXSYS_STATIC || type >= LXSYS_MAXTYPE)) {
+ return (VNON);
+ }
+
+ if (type != LXSYS_STATIC) {
+ /* Non-static node types */
+ switch (type) {
+ case LXSYS_CLASS_NET:
+ if (instance != 0) {
+ return (VLNK);
+ }
+ break;
+ case LXSYS_DEV_NET:
+ /*
+ * /sys/devices/virtual/net usually has the eth0 and
+ * lo directories. Each network device directory is an
+ * instances with a 0 endpoint. The files within
+ * that directory have a non-0 endpoint.
+ */
+ if (endpoint != 0) {
+ return (VREG);
+ }
+ break;
+ case LXSYS_BLOCK:
+ if (instance != 0) {
+ return (VLNK);
+ }
+ break;
+ case LXSYS_DEV_ZFS:
+ /*
+ * /sys/devices/zfs usually has the zfsds0 directory
+ * instance with a 0 endpoint. The device file within
+ * that directory has a non-0 endpoint.
+ */
+ if (endpoint != 0) {
+ return (VREG);
+ }
+ break;
+ case LXSYS_DEV_SYS_CPU:
+ if (instance != 0) {
+ return (VREG);
+ }
+ break;
+ case LXSYS_DEV_SYS_CPUINFO:
+ /*
+ * There is an instance of /sys/devices/system/cpu/cpuN
+ * for each CPU. These have an instance per CPU and
+ * currently the endpoint is 0 since there is nothing
+ * underneath the cpuN subdirectories. Future
+ * regular file entries are likely to be added there.
+ */
+ if (endpoint != 0) {
+ return (VREG);
+ }
+ break;
+ case LXSYS_DEV_SYS_NODE:
+ /*
+ * /sys/devices/system/node has the node0 directory
+ * instance with a 0 endpoint. The cpulist file within
+ * that directory has a non-0 endpoint.
+ */
+ if (endpoint != 0) {
+ return (VREG);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return (VDIR);
+}
+
+/*
+ * lxsys_open(): Vnode operation for VOP_OPEN()
+ */
+/* ARGSUSED */
+static int
+lxsys_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ /*
+ * We only allow reading in this file system
+ */
+ if (flag & FWRITE)
+ return (EROFS);
+
+ return (0);
+}
+
+
+/*
+ * lxsys_close(): Vnode operation for VOP_CLOSE()
+ */
+/* ARGSUSED */
+static int
+lxsys_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ct)
+{
+ return (0);
+}
+
+
+/*
+ * lxsys_read(): Vnode operation for VOP_READ()
+ * All we currently have in this fs are directories.
+ */
+/* ARGSUSED */
+static int
+lxsys_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxsys_node_t *lnp = VTOLXS(vp);
+ lxsys_nodetype_t type = lnp->lxsys_type;
+ int (*rlfunc)();
+ int error;
+ lxsys_uiobuf_t *luio;
+
+ VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+ if (vp->v_type == VDIR) {
+ return (EISDIR);
+ }
+
+ rlfunc = lxsys_read_function[type];
+ if (rlfunc != NULL) {
+ luio = lxsys_uiobuf_new(uiop);
+ if ((error = rlfunc(lnp, luio)) == 0) {
+ error = lxsys_uiobuf_flush(luio);
+ }
+ lxsys_uiobuf_free(luio);
+ } else {
+ error = EIO;
+ }
+
+ return (error);
+}
+
+/*
+ * lxsys_getattr(): Vnode operation for VOP_GETATTR()
+ */
+/* ARGSUSED */
+static int
+lxsys_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ register lxsys_node_t *lxsnp = VTOLXS(vp);
+
+ /* Default attributes, that may be overridden below */
+ bzero(vap, sizeof (*vap));
+ vap->va_atime = vap->va_mtime = vap->va_ctime = lxsnp->lxsys_time;
+ vap->va_nlink = 1;
+ vap->va_type = vp->v_type;
+ vap->va_mode = lxsnp->lxsys_mode;
+ vap->va_fsid = vp->v_vfsp->vfs_dev;
+ vap->va_blksize = DEV_BSIZE;
+ vap->va_uid = lxsnp->lxsys_uid;
+ vap->va_gid = lxsnp->lxsys_gid;
+ vap->va_nodeid = lxsnp->lxsys_ino;
+
+ vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
+ return (0);
+}
+
+/*
+ * lxsys_access(): Vnode operation for VOP_ACCESS()
+ */
+/* ARGSUSED */
+static int
+lxsys_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
+{
+ lxsys_node_t *lxsnp = VTOLXS(vp);
+ int shift = 0;
+
+ /*
+ * Although our lx sysfs is basically a read only file system, Linux
+ * expects it to be writable so we can't just error if (mode & VWRITE).
+ */
+
+ /* If user is root allow access regardless of permission bits */
+ if (secpolicy_proc_access(cr) == 0)
+ return (0);
+
+ /*
+ * Access check is based on only one of owner, group, public. If not
+ * owner, then check group. If not a member of the group, then check
+ * public access.
+ */
+ if (crgetuid(cr) != lxsnp->lxsys_uid) {
+ shift += 3;
+ if (!groupmember((uid_t)lxsnp->lxsys_gid, cr))
+ shift += 3;
+ }
+
+ mode &= ~(lxsnp->lxsys_mode << shift);
+
+ if (mode == 0)
+ return (0);
+
+ return (EACCES);
+}
+
+/*
+ * lxsys_lookup(): Vnode operation for VOP_LOOKUP()
+ */
+/* ARGSUSED */
+static int
+lxsys_lookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ lxsys_node_t *lxsnp = VTOLXS(dp);
+ lxsys_nodetype_t type = lxsnp->lxsys_type;
+ int error;
+
+ VERIFY(dp->v_type == VDIR);
+ VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+ /*
+ * restrict lookup permission to owner or root
+ */
+ if ((error = lxsys_access(dp, VEXEC, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * Just return the parent vnode if that's where we are trying to go.
+ */
+ if (strcmp(comp, "..") == 0) {
+ VN_HOLD(lxsnp->lxsys_parentvp);
+ *vpp = lxsnp->lxsys_parentvp;
+ return (0);
+ }
+
+ /*
+ * Special handling for directory searches. Note: null component name
+ * denotes that the current directory is being searched.
+ */
+ if ((dp->v_type == VDIR) && (*comp == '\0' || strcmp(comp, ".") == 0)) {
+ VN_HOLD(dp);
+ *vpp = dp;
+ return (0);
+ }
+
+ *vpp = (lxsys_lookup_function[type](lxsnp, comp));
+ return ((*vpp == NULL) ? ENOENT : 0);
+}
+
+static lxsys_node_t *
+lxsys_lookup_disk(lxsys_node_t *ldp, char *comp, lxsys_nodetype_t type)
+{
+ lxsys_node_t *lnp = NULL;
+ lx_zone_data_t *lxzdata;
+ lx_virt_disk_t *vd;
+
+ lxzdata = ztolxzd(curproc->p_zone);
+ if (lxzdata == NULL)
+ return (NULL);
+ ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+ vd = list_head(lxzdata->lxzd_vdisks);
+ while (vd != NULL) {
+ int inst = getminor(vd->lxvd_emul_dev) & 0xffff;
+
+ if (strcmp(vd->lxvd_name, comp) == 0 && inst != 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, type, inst, 0);
+ break;
+ }
+
+ vd = list_next(lxzdata->lxzd_vdisks, vd);
+ }
+
+ return (lnp);
+}
+
+static vnode_t *
+lxsys_lookup_static(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_dirent_t *dirent = NULL;
+ int i, len = 0;
+
+ for (i = 0; i < SYSDIRLISTSZ(lxsys_dirlookup); i++) {
+ if (ldp->lxsys_instance == lxsys_dirlookup[i].dl_instance) {
+ dirent = lxsys_dirlookup[i].dl_list;
+ len = lxsys_dirlookup[i].dl_length;
+ break;
+ }
+ }
+ if (dirent == NULL) {
+ return (NULL);
+ }
+
+ for (i = 0; i < len; i++) {
+ if (strncmp(comp, dirent[i].d_name, MAXPATHLEN) == 0) {
+ lxsys_nodetype_t node_type = ldp->lxsys_type;
+ unsigned int node_instance = 0;
+ lxsys_node_t *lnp;
+
+ switch (dirent[i].d_idnum) {
+ case LXSYS_INST_BLOCKDIR:
+ node_type = LXSYS_BLOCK;
+ break;
+ case LXSYS_INST_CLASS_NETDIR:
+ node_type = LXSYS_CLASS_NET;
+ break;
+ case LXSYS_INST_DEVICES_VIRTUAL_NETDIR:
+ node_type = LXSYS_DEV_NET;
+ break;
+ case LXSYS_INST_DEVICES_ZFSDIR:
+ node_type = LXSYS_DEV_ZFS;
+ break;
+ case LXSYS_INST_DEVICES_SYSCPU:
+ node_type = LXSYS_DEV_SYS_CPU;
+ break;
+ case LXSYS_INST_DEVICES_SYSNODE:
+ node_type = LXSYS_DEV_SYS_NODE;
+ break;
+ default:
+ /* Another static node */
+ node_instance = dirent[i].d_idnum;
+ }
+ if (node_type == LXSYS_STATIC) {
+ lnp = lxsys_getnode_static(ldp->lxsys_vnode,
+ node_instance);
+ } else {
+ lnp = lxsys_getnode(ldp->lxsys_vnode,
+ node_type, node_instance, 0);
+ }
+ return (lnp->lxsys_vnode);
+ }
+ }
+ return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_class_netdir(lxsys_node_t *ldp, char *comp)
+{
+ vnode_t *result = NULL;
+ lxsys_node_t *lnp;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ avl_tree_t *phytree;
+ phyint_t *phyi;
+ char ifname[LIFNAMSIZ];
+
+ if (ldp->lxsys_type != LXSYS_CLASS_NET ||
+ ldp->lxsys_instance != 0) {
+ /* Lookups only allowed at directory level */
+ return (NULL);
+ }
+
+ (void) strncpy(ifname, comp, LIFNAMSIZ);
+ lx_ifname_convert(ifname, LX_IF_TONATIVE);
+
+ if ((ns = lxsys_netstack(ldp)) == NULL) {
+ return (NULL);
+ }
+ ipst = ns->netstack_ip;
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+
+ phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_name;
+ phyi = avl_find(phytree, ifname, NULL);
+ if (phyi != NULL) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, ldp->lxsys_type,
+ phyi->phyint_ifindex, 0);
+ result = lnp->lxsys_vnode;
+ result->v_type = VLNK;
+ }
+
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+
+ return (result);
+}
+
+static vnode_t *
+lxsys_lookup_devices_virtual_netdir(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_node_t *lnp;
+
+ if (ldp->lxsys_instance == 0) {
+ /* top-level interface listing */
+ vnode_t *result = NULL;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ avl_tree_t *phytree;
+ phyint_t *phyi;
+ char ifname[LIFNAMSIZ];
+
+ (void) strncpy(ifname, comp, LIFNAMSIZ);
+ lx_ifname_convert(ifname, LX_IF_TONATIVE);
+
+ if ((ns = lxsys_netstack(ldp)) == NULL) {
+ return (NULL);
+ }
+ ipst = ns->netstack_ip;
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+
+ phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_name;
+ phyi = avl_find(phytree, ifname, NULL);
+ if (phyi != NULL) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, ldp->lxsys_type,
+ phyi->phyint_ifindex, 0);
+ result = lnp->lxsys_vnode;
+ }
+
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+
+ return (result);
+ } else if (ldp->lxsys_endpoint == 0) {
+ /* interface-level sub-item listing */
+ int i, size;
+ lxsys_dirent_t *dirent;
+
+ size = SYSDIRLISTSZ(dirlist_devices_virtual_net);
+ for (i = 0; i < size; i++) {
+ dirent = &dirlist_devices_virtual_net[i];
+ if (strncmp(comp, dirent->d_name, LXSNSIZ) == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode,
+ ldp->lxsys_type, ldp->lxsys_instance,
+ dirent->d_idnum);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ return (lnp->lxsys_vnode);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_blockdir(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_node_t *lnp;
+
+ if (ldp->lxsys_instance == 0) {
+ /* top-level dev listing */
+ lnp = lxsys_lookup_disk(ldp, comp, LXSYS_BLOCK);
+
+ if (lnp != NULL) {
+ lnp->lxsys_vnode->v_type = VLNK;
+ return (lnp->lxsys_vnode);
+ }
+ }
+
+ return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_devices_zfsdir(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_node_t *lnp;
+
+ if (ldp->lxsys_instance == 0) {
+ /* top-level dev listing */
+ lnp = lxsys_lookup_disk(ldp, comp, LXSYS_DEV_ZFS);
+
+ if (lnp != NULL) {
+ return (lnp->lxsys_vnode);
+ }
+ } else if (ldp->lxsys_endpoint == 0) {
+ /* disk-level sub-item listing */
+ int i, size;
+ lxsys_dirent_t *dirent;
+
+ /*
+ * All of these entries currently look like regular files
+ * but on a real Linux system some will be subdirs. This should
+ * be fixed when we populate the directory for real.
+ */
+ size = SYSDIRLISTSZ(dirlist_devices_zfs_block);
+ for (i = 0; i < size; i++) {
+ dirent = &dirlist_devices_zfs_block[i];
+ if (strncmp(comp, dirent->d_name, LXSNSIZ) == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode,
+ ldp->lxsys_type, ldp->lxsys_instance,
+ dirent->d_idnum);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ return (lnp->lxsys_vnode);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_devices_syscpu(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_node_t *lnp = NULL;
+
+ if (ldp->lxsys_instance == 0) {
+ /* top-level cpu listing */
+
+ /* If fixed entry */
+ if (strcmp(comp, "kernel_max") == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, LXSYS_DEV_SYS_CPU,
+ LXSYS_INST_DEV_SYSCPU_KMAX, 0);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ } else if (strcmp(comp, "offline") == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, LXSYS_DEV_SYS_CPU,
+ LXSYS_INST_DEV_SYSCPU_OFFLINE, 0);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ } else if (strcmp(comp, "online") == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, LXSYS_DEV_SYS_CPU,
+ LXSYS_INST_DEV_SYSCPU_ONLINE, 0);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ } else if (strcmp(comp, "possible") == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, LXSYS_DEV_SYS_CPU,
+ LXSYS_INST_DEV_SYSCPU_POSSIBLE, 0);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ } else if (strcmp(comp, "present") == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, LXSYS_DEV_SYS_CPU,
+ LXSYS_INST_DEV_SYSCPU_PRESENT, 0);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ } else {
+ /* Else dynamic cpuN entry */
+ cpuset_t *avail; /* all installed CPUs */
+ uint_t i, avlo, avhi;
+
+ avail = cpuset_alloc(KM_SLEEP);
+ cpuset_all(avail);
+
+ /* Take a snapshot of the available set */
+ mutex_enter(&cpu_lock);
+ cpuset_and(avail, &cpu_available);
+ mutex_exit(&cpu_lock);
+
+ cpuset_bounds(avail, &avlo, &avhi);
+
+ for (i = avlo; i <= avhi; i++) {
+ char cpunm[16];
+
+ if (!cpu_in_set(avail, i))
+ continue;
+
+ (void) snprintf(cpunm, sizeof (cpunm), "cpu%u",
+ i);
+
+ if (strcmp(comp, cpunm) == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode,
+ LXSYS_DEV_SYS_CPUINFO, i + 1, 0);
+ break;
+ }
+ }
+ cpuset_free(avail);
+ }
+
+ if (lnp != NULL) {
+ return (lnp->lxsys_vnode);
+ }
+ } else if (ldp->lxsys_endpoint == 0) {
+ /* cpu-level sub-item listing, currently empty */
+ /* EMPTY */
+ }
+
+ return (NULL);
+}
+
+/* ARGSUSED */
+static vnode_t *
+lxsys_lookup_devices_syscpuinfo(lxsys_node_t *ldp, char *comp)
+{
+ return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_devices_sysnode(lxsys_node_t *ldp, char *comp)
+{
+ lxsys_node_t *lnp = NULL;
+
+ if (ldp->lxsys_instance == 0) {
+ /*
+ * The system is presently represented as a single node,
+ * regardless of any NUMA topology which exists.
+ * The instances are offset by 1 to account for the top level
+ * directory occupying instance 0.
+ */
+ if (strcmp(comp, "node0") == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode, ldp->lxsys_type,
+ 1, 0);
+ return (lnp->lxsys_vnode);
+ }
+ } else {
+ /* interface-level sub-item listing */
+ int i, size;
+ lxsys_dirent_t *dirent;
+
+ size = SYSDIRLISTSZ(dirlist_devices_sysnode);
+ for (i = 0; i < size; i++) {
+ dirent = &dirlist_devices_sysnode[i];
+ if (strncmp(comp, dirent->d_name, LXSNSIZ) == 0) {
+ lnp = lxsys_getnode(ldp->lxsys_vnode,
+ ldp->lxsys_type, ldp->lxsys_instance,
+ dirent->d_idnum);
+ lnp->lxsys_vnode->v_type = VREG;
+ lnp->lxsys_mode = 0444;
+ return (lnp->lxsys_vnode);
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+static int
+lxsys_read_devices_virtual_net(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+ netstack_t *ns;
+ ill_t *ill;
+ uint_t ifindex = lnp->lxsys_instance;
+ uint8_t *addr;
+ uint64_t flags;
+ int error = 0;
+
+ if (ifindex == 0 || lnp->lxsys_endpoint == 0) {
+ return (EISDIR);
+ }
+
+ if ((ns = lxsys_netstack(lnp)) == NULL) {
+ return (EIO);
+ }
+
+ ill = lxsys_find_ill(ns->netstack_ip, ifindex);
+ if (ill == NULL) {
+ netstack_rele(ns);
+ return (EIO);
+ }
+
+ switch (lnp->lxsys_endpoint) {
+ case LXSYS_ENDP_NET_ADDRESS:
+ if (ill->ill_phys_addr_length != ETHERADDRL) {
+ lxsys_uiobuf_printf(luio, "00:00:00:00:00:00\n");
+ break;
+ }
+ addr = ill->ill_phys_addr;
+ lxsys_uiobuf_printf(luio,
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ break;
+ case LXSYS_ENDP_NET_ADDRLEN:
+ lxsys_uiobuf_printf(luio, "%u\n",
+ IS_LOOPBACK(ill) ? ETHERADDRL : ill->ill_phys_addr_length);
+ break;
+ case LXSYS_ENDP_NET_FLAGS:
+ flags = (ill->ill_flags | ill->ill_ipif->ipif_flags |
+ ill->ill_phyint->phyint_flags) & 0xffff;
+ lx_ifflags_convert(&flags, LX_IF_FROMNATIVE);
+ lxsys_uiobuf_printf(luio, "0x%x\n", flags);
+ break;
+ case LXSYS_ENDP_NET_IFINDEX:
+ lxsys_uiobuf_printf(luio, "%u\n", ifindex);
+ break;
+ case LXSYS_ENDP_NET_MTU:
+ lxsys_uiobuf_printf(luio, "%u\n", ill->ill_mtu);
+ break;
+ case LXSYS_ENDP_NET_TXQLEN:
+ /* perpetuate the txqlen lie */
+ if (IS_LOOPBACK(ill)) {
+ lxsys_uiobuf_printf(luio, "0\n");
+ } else {
+ lxsys_uiobuf_printf(luio, "1\n");
+ }
+ break;
+ case LXSYS_ENDP_NET_TYPE:
+ lxsys_uiobuf_printf(luio, "%u\n",
+ IS_LOOPBACK(ill) ? LX_ARPHRD_LOOPBACK :
+ arp_hw_type(ill->ill_mactype));
+ break;
+ default:
+ error = EIO;
+ }
+
+ ill_refrele(ill);
+ netstack_rele(ns);
+ return (error);
+}
+
+/* ARGSUSED1 */
+static int
+lxsys_read_devices_zfs_block(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+ uint_t dskindex = lnp->lxsys_instance;
+
+ if (dskindex == 0 || lnp->lxsys_endpoint == 0) {
+ return (EISDIR);
+ }
+
+ return (EIO);
+}
+
+/*
+ * In the Linux src tree, see ABI/stable/sysfs-devices-node.
+ *
+ * For the 'cpumap' file, each CPU is treated as a bit, then those are
+ * accumulated and printed as a hex digit, with CPU0 as the rightmost bit.
+ * Each set of 8 digits (i.e. 32 CPUs) is then delimited with a comma.
+ * Since we are emulating a single NUMA group, all of our CPUs will be listed
+ * in this file. For example, a 48 CPU system would look like:
+ * 00000000,00000000,00000000,00000000,00000000,00000000,0000ffff,ffffffff
+ * It comes out this way because 'kernel_max' is NCPU, which is currently
+ * defined to be 256.
+ */
+static int
+lxsys_read_devices_sysnode(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+ if (lnp->lxsys_instance == 1) {
+ char outbuf[256];
+
+ if (lnp->lxsys_endpoint == LXSYS_ENDP_NODE_CPULIST) {
+ /* Show the range of CPUs */
+ lxsys_format_cpu(outbuf, sizeof (outbuf),
+ LXSYS_CPU_ANY);
+ } else if (lnp->lxsys_endpoint == LXSYS_ENDP_NODE_CPUMAP) {
+ int i;
+ uint_t j, ndigits;
+ cpuset_t *avail; /* all installed CPUs */
+
+ avail = cpuset_alloc(KM_SLEEP);
+ cpuset_all(avail);
+
+ /* Take a snapshot of the available set */
+ mutex_enter(&cpu_lock);
+ cpuset_and(avail, &cpu_available);
+ mutex_exit(&cpu_lock);
+
+ outbuf[0] = '\0';
+ ndigits = 0;
+ for (i = NCPU - 1; i >= 0; i -= 4) {
+ char buf[8];
+ int cnt = 3;
+ uint_t digit = 0;
+
+ for (j = i; cnt >= 0; j--, cnt--) {
+ if (cpu_in_set(avail, j))
+ digit |= 1 << cnt;
+ }
+ (void) snprintf(buf, sizeof (buf), "%x", digit);
+ if (ndigits == 8) {
+ (void) strlcat(outbuf, ",",
+ sizeof (outbuf));
+ ndigits = 0;
+ }
+ (void) strlcat(outbuf, buf, sizeof (outbuf));
+ ndigits++;
+ }
+
+ cpuset_free(avail);
+ } else {
+ return (EISDIR);
+ }
+
+ lxsys_uiobuf_printf(luio, "%s\n", outbuf);
+ return (0);
+ }
+ return (EISDIR);
+}
+
+static void
+lxsys_format_range(char *buf, int blen, boolean_t *first, uint_t start,
+ uint_t cnt)
+{
+ char tmp[256];
+ char *delim;
+
+ if (cnt == 0)
+ return;
+
+ if (*first) {
+ *first = B_FALSE;
+ delim = "";
+ } else {
+ delim = ",";
+ }
+ if (cnt > 1) {
+ (void) snprintf(tmp, sizeof (tmp), "%s%u-%u", delim, start,
+ start + cnt - 1);
+ } else {
+ (void) snprintf(tmp, sizeof (tmp), "%s%u", delim, start);
+ }
+ (void) strlcat(buf, tmp, blen);
+}
+
+/*
+ * Format a string of which CPUs are online, offline, or don't care (depending
+ * on chk_state), and which would be formatted like this:
+ * 0-31
+ * or
+ * 0-12,14,20-31
+ */
+static void
+lxsys_format_cpu(char *buf, int blen, lxsys_cpu_state_t chk_state)
+{
+ uint_t start, cnt, avlo, avhi;
+ boolean_t first = B_TRUE;
+ cpuset_t *active; /* CPUs online */
+ cpuset_t *avail; /* all installed CPUs */
+
+ active = cpuset_alloc(KM_SLEEP);
+ avail = cpuset_alloc(KM_SLEEP);
+ cpuset_all(active);
+ cpuset_all(avail);
+
+ /* Take a snapshot of the available and active sets */
+ mutex_enter(&cpu_lock);
+ cpuset_and(avail, &cpu_available);
+ cpuset_and(active, &cpu_active_set);
+ mutex_exit(&cpu_lock);
+
+ cpuset_bounds(avail, &avlo, &avhi);
+
+ buf[0] = '\0';
+ if (chk_state == LXSYS_CPU_ANY) {
+ start = avlo;
+ cnt = avhi + 1;
+ } else {
+ uint_t i;
+ boolean_t incl_cpu = B_TRUE;
+
+ start = 0;
+ cnt = 0;
+ for (i = avlo; i <= avhi; i++) {
+ if (chk_state == LXSYS_CPU_ON) {
+ if (!cpu_in_set(active, i))
+ incl_cpu = B_FALSE;
+ } else {
+ if (cpu_in_set(active, i))
+ incl_cpu = B_FALSE;
+ }
+
+ if (incl_cpu && cpu_in_set(avail, i)) {
+ cnt++;
+ } else {
+ /*
+ * Note: this may print nothing if our 'cnt'
+ * is 0, but we advance 'start' properly so we
+ * handle the next range of elements we're
+ * looking for.
+ */
+ lxsys_format_range(buf, blen, &first, start,
+ cnt);
+ start += cnt + 1;
+ cnt = 0;
+ incl_cpu = B_TRUE;
+ }
+ }
+ }
+
+ cpuset_free(avail);
+ cpuset_free(active);
+
+ lxsys_format_range(buf, blen, &first, start, cnt);
+}
+
+static int
+lxsys_read_devices_syscpu(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+ uint_t inst = lnp->lxsys_instance;
+ char outbuf[256];
+
+ /*
+ * For 'kernel_max', 'offline', 'online', 'possible', and 'present',
+ * see the Documentaion/cputopology.txt file in the Linux src tree.
+ */
+ if (inst == LXSYS_INST_DEV_SYSCPU_KMAX) {
+ lxsys_uiobuf_printf(luio, "%d\n", NCPU - 1);
+ return (0);
+ }
+
+ if (inst == LXSYS_INST_DEV_SYSCPU_OFFLINE) {
+ lxsys_format_cpu(outbuf, sizeof (outbuf), LXSYS_CPU_OFF);
+ lxsys_uiobuf_printf(luio, "%s\n", outbuf);
+ return (0);
+ }
+
+ if (inst == LXSYS_INST_DEV_SYSCPU_ONLINE) {
+ lxsys_format_cpu(outbuf, sizeof (outbuf), LXSYS_CPU_ON);
+ lxsys_uiobuf_printf(luio, "%s\n", outbuf);
+ return (0);
+ }
+
+ if (inst == LXSYS_INST_DEV_SYSCPU_POSSIBLE ||
+ inst == LXSYS_INST_DEV_SYSCPU_PRESENT) {
+ lxsys_format_cpu(outbuf, sizeof (outbuf), LXSYS_CPU_ANY);
+ lxsys_uiobuf_printf(luio, "%s\n", outbuf);
+ return (0);
+ }
+
+ /* All other nodes are directories */
+ return (EISDIR);
+}
+
+/* ARGSUSED */
+static int
+lxsys_read_static(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+ /* All static nodes are directories */
+ return (EISDIR);
+}
+
+/*
+ * lxsys_readdir(): Vnode operation for VOP_READDIR()
+ */
+/* ARGSUSED */
+static int
+lxsys_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ lxsys_node_t *lxsnp = VTOLXS(dp);
+ lxsys_nodetype_t type = lxsnp->lxsys_type;
+ ssize_t uresid;
+ off_t uoffset;
+ int error, leof;
+
+ ASSERT(dp->v_type == VDIR);
+ VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+ /*
+ * restrict readdir permission to owner or root
+ */
+ if ((error = lxsys_access(dp, VREAD, 0, cr, ct)) != 0)
+ return (error);
+
+ uoffset = uiop->uio_offset;
+ uresid = uiop->uio_resid;
+
+ /* can't do negative reads */
+ if (uoffset < 0 || uresid <= 0)
+ return (EINVAL);
+
+ /* can't read directory entries that don't exist! */
+ if (uoffset % LXSYS_SDSIZE)
+ return (ENOENT);
+
+ /* Free lower functions from having to check eofp == NULL */
+ if (eofp == NULL) {
+ eofp = &leof;
+ }
+
+ return (lxsys_readdir_function[lxsnp->lxsys_type](lxsnp, uiop, eofp));
+}
+
+static int
+lxsys_dirent_out(dirent64_t *d, ushort_t n, struct uio *uio)
+{
+ int error;
+ off_t offset = uio->uio_offset;
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset by the
+ * same amount. But we want uiop->uio_offset to change in increments
+ * of LXSYS_SDSIZE, which is different from the number of bytes being
+ * returned to the user. To accomplish this, we set uiop->uio_offset
+ * separately on success, overriding what uiomove() does.
+ */
+ d->d_off = (off64_t)(offset + LXSYS_SDSIZE);
+ d->d_reclen = n;
+ if ((error = uiomove(d, n, UIO_READ, uio)) != 0) {
+ return (error);
+ }
+ uio->uio_offset = offset + LXSYS_SDSIZE;
+ return (0);
+}
+
+/*
+ * This has the common logic for returning directory entries
+ */
+static int
+lxsys_readdir_common(lxsys_node_t *lxsnp, uio_t *uiop, int *eofp,
+ lxsys_dirent_t *dirtab, int dirtablen)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+
+ oresid = uiop->uio_resid;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /* Satisfy user request */
+ while ((uresid = uiop->uio_resid) > 0) {
+ int dirindex;
+ off_t uoffset;
+ int reclen;
+ int error;
+
+ uoffset = uiop->uio_offset;
+ dirindex = (uoffset / LXSYS_SDSIZE) - 2;
+
+ if (uoffset == 0) {
+
+ dirent->d_ino = lxsnp->lxsys_ino;
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '\0';
+ reclen = DIRENT64_RECLEN(1);
+
+ } else if (uoffset == LXSYS_SDSIZE) {
+
+ dirent->d_ino = lxsys_parentinode(lxsnp);
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '.';
+ dirent->d_name[2] = '\0';
+ reclen = DIRENT64_RECLEN(2);
+
+ } else if (dirindex >= 0 && dirindex < dirtablen) {
+
+ int slen = strlen(dirtab[dirindex].d_name);
+ int idnum, ino_type = 0;
+
+ idnum = dirtab[dirindex].d_idnum;
+ if (idnum > 0 && idnum < DYN_INO_LEN)
+ ino_type = dyn_ino_type[idnum];
+
+ if (ino_type != 0) {
+ /*
+ * Correct the inode for static directories
+ * which contain non-static lxsys_nodetype_t's.
+ */
+ dirent->d_ino = lxsys_inode(ino_type, 0, 0);
+ DTRACE_PROBE3(lxsys__fix__inode,
+ char *, dirtab[dirindex].d_name,
+ int, ino_type, int, dirent->d_ino);
+ } else {
+ dirent->d_ino = lxsys_inode(LXSYS_STATIC,
+ idnum, 0);
+ }
+
+ (void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+ reclen = DIRENT64_RECLEN(slen);
+
+ } else {
+ /* Run out of table entries */
+ *eofp = 1;
+ return (0);
+ }
+
+ /*
+ * If the size of the data to transfer is greater than the
+ * user-provided buffer, we cannot continue.
+ */
+ if (reclen > uresid) {
+ /* Error if no entries have been returned yet. */
+ if (uresid == oresid) {
+ return (EINVAL);
+ }
+ break;
+ }
+
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ return (error);
+ }
+ }
+
+ /* Have run out of space, but could have just done last table entry */
+ *eofp = (uiop->uio_offset >= ((dirtablen+2) * LXSYS_SDSIZE)) ? 1 : 0;
+ return (0);
+}
+
+static int
+lxsys_readdir_subdir(lxsys_node_t *lxsnp, uio_t *uiop, int *eofp,
+ lxsys_dirent_t *dirtab, int dirtablen)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+
+ VERIFY(dirtab != NULL || dirtablen == 0);
+
+ oresid = uiop->uio_resid;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /* Satisfy user request */
+ while ((uresid = uiop->uio_resid) > 0) {
+ int dirindex;
+ off_t uoffset;
+ int reclen;
+ int error;
+
+ uoffset = uiop->uio_offset;
+ dirindex = (uoffset / LXSYS_SDSIZE) - 2;
+
+ if (uoffset == 0) {
+
+ dirent->d_ino = lxsnp->lxsys_ino;
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '\0';
+ reclen = DIRENT64_RECLEN(1);
+
+ } else if (uoffset == LXSYS_SDSIZE) {
+
+ dirent->d_ino = lxsys_parentinode(lxsnp);
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '.';
+ dirent->d_name[2] = '\0';
+ reclen = DIRENT64_RECLEN(2);
+
+ } else if (dirindex >= 0 && dirindex < dirtablen) {
+
+ int slen = strlen(dirtab[dirindex].d_name);
+
+ dirent->d_ino = lxsys_inode(lxsnp->lxsys_type,
+ lxsnp->lxsys_instance, dirtab[dirindex].d_idnum);
+ (void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+ reclen = DIRENT64_RECLEN(slen);
+
+ } else {
+ /* Run out of table entries */
+ *eofp = 1;
+ return (0);
+ }
+
+ /*
+ * If the size of the data to transfer is greater than the
+ * user-provided buffer, we cannot continue.
+ */
+ if (reclen > uresid) {
+ /* Error if no entries have been returned yet. */
+ if (uresid == oresid) {
+ return (EINVAL);
+ }
+ break;
+ }
+
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ return (error);
+ }
+ }
+
+ /* Have run out of space, but could have just done last table entry */
+ *eofp = (uiop->uio_offset >= ((dirtablen+2) * LXSYS_SDSIZE)) ? 1 : 0;
+ return (0);
+}
+
+static int
+lxsys_readdir_ifaces(lxsys_node_t *ldp, struct uio *uiop, int *eofp,
+ lxsys_nodetype_t type)
+{
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid, uresid;
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ avl_tree_t *phytree;
+ phyint_t *phyi;
+ int error, i;
+
+
+ /* Emit "." and ".." entries */
+ oresid = uiop->uio_resid;
+ error = lxsys_readdir_common(ldp, uiop, eofp, NULL, 0);
+ if (error != 0 || *eofp == 0) {
+ return (error);
+ }
+
+ if ((ns = lxsys_netstack(ldp)) == NULL) {
+ *eofp = 1;
+ return (0);
+ }
+ ipst = ns->netstack_ip;
+
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+ phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_index;
+ phyi = avl_first(phytree);
+ if (phyi == NULL) {
+ *eofp = 1;
+ }
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Skip records we have already passed with the offset.
+ * This accounts for the two "." and ".." records already seen.
+ */
+ for (i = (uiop->uio_offset/LXSYS_SDSIZE) - 2; i > 0; i--) {
+ if ((phyi = avl_walk(phytree, phyi, AVL_AFTER)) == NULL) {
+ *eofp = 1;
+ break;
+ }
+ }
+
+ while ((uresid = uiop->uio_resid) > 0 && phyi != NULL) {
+ uint_t ifindex;
+ int reclen;
+
+ ifindex = phyi->phyint_ifindex;
+ (void) strncpy(dirent->d_name, phyi->phyint_name, LIFNAMSIZ);
+ lx_ifname_convert(dirent->d_name, LX_IF_FROMNATIVE);
+ dirent->d_ino = lxsys_inode(type, ifindex, 0);
+ reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+ if (reclen > uresid) {
+ if (uresid == oresid) {
+ /* Not enough space for one record */
+ error = EINVAL;
+ }
+ break;
+ }
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ break;
+ }
+
+ if ((phyi = avl_walk(phytree, phyi, AVL_AFTER)) == NULL) {
+ *eofp = 1;
+ break;
+ }
+ }
+
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+ return (error);
+}
+
+static int
+lxsys_readdir_disks(lxsys_node_t *ldp, struct uio *uiop, int *eofp,
+ lxsys_nodetype_t type)
+{
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid, uresid;
+ int skip, error;
+ int reclen;
+ uint_t instance;
+ lx_zone_data_t *lxzdata;
+ lx_virt_disk_t *vd;
+
+ /* Emit "." and ".." entries */
+ oresid = uiop->uio_resid;
+ error = lxsys_readdir_common(ldp, uiop, eofp, NULL, 0);
+ if (error != 0 || *eofp == 0) {
+ return (error);
+ }
+
+ skip = (uiop->uio_offset/LXSYS_SDSIZE) - 2;
+
+ lxzdata = ztolxzd(curproc->p_zone);
+ if (lxzdata == NULL)
+ return (EINVAL);
+ ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+ vd = list_head(lxzdata->lxzd_vdisks);
+ while (vd != NULL) {
+ if (skip > 0) {
+ skip--;
+ goto next;
+ }
+
+ if (strnlen(vd->lxvd_name, sizeof (vd->lxvd_name)) > LXSNSIZ)
+ goto next;
+
+ (void) strncpy(dirent->d_name, vd->lxvd_name, LXSNSIZ);
+
+ instance = getminor(vd->lxvd_emul_dev) & 0xffff;
+ if (instance == 0)
+ goto next;
+
+ dirent->d_ino = lxsys_inode(type, instance, 0);
+ reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+ uresid = uiop->uio_resid;
+ if (reclen > uresid) {
+ if (uresid == oresid) {
+ /* Not enough space for one record */
+ error = EINVAL;
+ }
+ break;
+ }
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ break;
+ }
+
+next:
+ vd = list_next(lxzdata->lxzd_vdisks, vd);
+ }
+
+ /* Indicate EOF if we reached the end of the virtual disks. */
+ if (vd == NULL) {
+ *eofp = 1;
+ }
+
+ return (error);
+}
+
+
+static int
+lxsys_readdir_static(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ lxsys_dirent_t *dirent = NULL;
+ int i, len = 0;
+ boolean_t found = B_FALSE;
+
+ for (i = 0; i < SYSDIRLISTSZ(lxsys_dirlookup); i++) {
+ if (lnp->lxsys_instance == lxsys_dirlookup[i].dl_instance) {
+ dirent = lxsys_dirlookup[i].dl_list;
+ len = lxsys_dirlookup[i].dl_length;
+ found = B_TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ return (ENOTDIR);
+ }
+
+ return (lxsys_readdir_common(lnp, uiop, eofp, dirent, len));
+}
+
+static int
+lxsys_readdir_class_netdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ if (lnp->lxsys_type != LXSYS_CLASS_NET ||
+ lnp->lxsys_instance != 0) {
+ /*
+ * Since /sys/class/net contains only symlinks, readdir
+ * operations should not be performed anywhere except the top
+ * level (instance == 0).
+ */
+ return (ENOTDIR);
+ }
+
+ return (lxsys_readdir_ifaces(lnp, uiop, eofp, LXSYS_CLASS_NET));
+}
+
+static int
+lxsys_readdir_devices_virtual_netdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ int error;
+
+ if (lnp->lxsys_instance == 0) {
+ /* top-level interface listing */
+ error = lxsys_readdir_ifaces(lnp, uiop, eofp,
+ LXSYS_DEV_NET);
+ } else if (lnp->lxsys_endpoint == 0) {
+ /* interface-level sub-item listing */
+ error = lxsys_readdir_subdir(lnp, uiop, eofp,
+ dirlist_devices_virtual_net,
+ SYSDIRLISTSZ(dirlist_devices_virtual_net));
+ } else {
+ /* there shouldn't be subdirs below this */
+ error = ENOTDIR;
+ }
+
+ return (error);
+}
+
+static int
+lxsys_readdir_blockdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ if (lnp->lxsys_type != LXSYS_BLOCK ||
+ lnp->lxsys_instance != 0) {
+ /*
+ * Since /sys/block contains only symlinks, readdir operations
+ * should not be performed anywhere except the top level
+ * (instance == 0).
+ */
+ return (ENOTDIR);
+ }
+
+ return (lxsys_readdir_disks(lnp, uiop, eofp, LXSYS_BLOCK));
+}
+
+static int
+lxsys_readdir_devices_zfsdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ int error;
+
+ if (lnp->lxsys_instance == 0) {
+ /* top-level dev listing */
+ error = lxsys_readdir_disks(lnp, uiop, eofp,
+ LXSYS_DEV_ZFS);
+ } else if (lnp->lxsys_endpoint == 0) {
+ /* disk-level sub-item listing */
+ error = lxsys_readdir_subdir(lnp, uiop, eofp,
+ dirlist_devices_zfs_block,
+ SYSDIRLISTSZ(dirlist_devices_zfs_block));
+ } else {
+ /*
+ * Currently there shouldn't be subdirs below this but
+ * on a real Linux system some will be subdirs. This should
+ * be fixed when we populate the directory for real.
+ */
+ error = ENOTDIR;
+ }
+
+ return (error);
+}
+
+/* Handle fixed entries within the cpu directory. */
+static int
+lxsys_do_sub_cpu(struct uio *uiop, ssize_t oresid, dirent64_t *dirent,
+ char *nm, int inst, int *errp)
+{
+ int reclen;
+ ssize_t uresid;
+
+ (void) strncpy(dirent->d_name, nm, LXSNSIZ);
+
+ dirent->d_ino = lxsys_inode(LXSYS_DEV_SYS_CPU, inst, 0);
+ reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+ uresid = uiop->uio_resid;
+ if (reclen > uresid) {
+ if (uresid == oresid) {
+ /* Not enough space for one record */
+ *errp = EINVAL;
+ }
+ return (-1);
+ }
+ if ((*errp = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+lxsys_readdir_cpu(lxsys_node_t *ldp, struct uio *uiop, int *eofp)
+{
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid, uresid;
+ int skip, error;
+ int reclen;
+ cpuset_t *avail;
+ uint_t i, avlo, avhi;
+
+ /* Emit "." and ".." entries */
+ oresid = uiop->uio_resid;
+ error = lxsys_readdir_common(ldp, uiop, eofp, NULL, 0);
+ if (error != 0 || *eofp == 0) {
+ return (error);
+ }
+
+ skip = (uiop->uio_offset/LXSYS_SDSIZE) - 2;
+
+ /* Fixed entries */
+ if (skip > 0) {
+ skip--;
+ } else {
+ if (lxsys_do_sub_cpu(uiop, oresid, dirent, "kernel_max",
+ LXSYS_INST_DEV_SYSCPU_KMAX, &error) != 0)
+ goto done;
+
+ if (lxsys_do_sub_cpu(uiop, oresid, dirent, "offline",
+ LXSYS_INST_DEV_SYSCPU_OFFLINE, &error) != 0)
+ goto done;
+
+ if (lxsys_do_sub_cpu(uiop, oresid, dirent, "online",
+ LXSYS_INST_DEV_SYSCPU_ONLINE, &error) != 0)
+ goto done;
+
+ if (lxsys_do_sub_cpu(uiop, oresid, dirent, "possible",
+ LXSYS_INST_DEV_SYSCPU_POSSIBLE, &error) != 0)
+ goto done;
+
+ if (lxsys_do_sub_cpu(uiop, oresid, dirent, "present",
+ LXSYS_INST_DEV_SYSCPU_PRESENT, &error) != 0)
+ goto done;
+ }
+
+ avail = cpuset_alloc(KM_SLEEP);
+ cpuset_all(avail);
+
+ /* Take a snapshot of the available set */
+ mutex_enter(&cpu_lock);
+ cpuset_and(avail, &cpu_available);
+ mutex_exit(&cpu_lock);
+
+ cpuset_bounds(avail, &avlo, &avhi);
+
+ /* Output dynamic CPU info */
+ for (i = avlo; i <= avhi; i++) {
+ char cpunm[16];
+
+ if (skip > 0) {
+ skip--;
+ continue;
+ }
+
+ if (!cpu_in_set(avail, i))
+ continue;
+
+ (void) snprintf(cpunm, sizeof (cpunm), "cpu%u", i);
+ (void) strncpy(dirent->d_name, cpunm, LXSNSIZ);
+
+ dirent->d_ino = lxsys_inode(LXSYS_DEV_SYS_CPUINFO, i + 1, 0);
+ reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+ uresid = uiop->uio_resid;
+ if (reclen > uresid) {
+ if (uresid == oresid) {
+ /* Not enough space for one record */
+ error = EINVAL;
+ }
+ break;
+ }
+ if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+ break;
+ }
+ }
+ cpuset_free(avail);
+
+ /* Indicate EOF if we reached the end of the CPU list. */
+ if (i == avhi) {
+ *eofp = 1;
+ }
+
+done:
+ return (error);
+}
+
+static int
+lxsys_readdir_devices_syscpu(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ int error;
+
+ if (lnp->lxsys_instance == 0) {
+ /* top-level cpu listing */
+ error = lxsys_readdir_cpu(lnp, uiop, eofp);
+ } else if (lnp->lxsys_endpoint == 0) {
+ /* cpu-level sub-item listing */
+ error = lxsys_readdir_subdir(lnp, uiop, eofp, NULL, 0);
+ } else {
+ /*
+ * Currently there shouldn't be subdirs below this but
+ * on a real Linux system some will be subdirs. This should
+ * be fixed when we populate the directory for real.
+ */
+ error = ENOTDIR;
+ }
+
+ return (error);
+}
+
+static int
+lxsys_readdir_devices_syscpuinfo(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ int error;
+
+ if (lnp->lxsys_type != LXSYS_DEV_SYS_CPUINFO) {
+ /*
+ * Since /sys/devices/system/cpu/cpuN is empty, readdir
+ * operations should not be performed anywhere except the top
+ * level.
+ */
+ return (ENOTDIR);
+ }
+
+ /*
+ * Emit "." and ".." entries
+ * All cpuN directories are currently empty.
+ */
+ error = lxsys_readdir_common(lnp, uiop, eofp, NULL, 0);
+ if (error != 0 || *eofp == 0) {
+ return (error);
+ }
+
+ /* Indicate EOF */
+ *eofp = 1;
+
+ return (error);
+}
+
+static int
+lxsys_readdir_devices_sysnode(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+ int error;
+
+ if (lnp->lxsys_instance == 0) {
+ /* top-level node listing */
+ longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid, uresid;
+ int reclen, skip;
+
+ /* Emit "." and ".." entries */
+ oresid = uiop->uio_resid;
+ error = lxsys_readdir_common(lnp, uiop, eofp, NULL, 0);
+ if (error != 0 || *eofp == 0) {
+ return (error);
+ }
+ skip = (uiop->uio_offset/LXSYS_SDSIZE) - 2;
+
+ /* Fixed entries */
+ if (skip > 0) {
+ skip--;
+ } else {
+ (void) strncpy(dirent->d_name, "node0", LXSNSIZ);
+
+ dirent->d_ino = lxsys_inode(LXSYS_DEV_SYS_NODE,
+ 1, 0);
+ reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+ uresid = uiop->uio_resid;
+ if (reclen > uresid) {
+ if (uresid == oresid) {
+ /* Not enough space for one record */
+ return (EINVAL);
+ }
+ return (0);
+ }
+ error = lxsys_dirent_out(dirent, reclen, uiop);
+ }
+ /* Indicate EOF */
+ if (error == 0) {
+ *eofp = 1;
+ }
+ } else if (lnp->lxsys_endpoint == 0) {
+ /* node-level sub-item listing */
+ error = lxsys_readdir_subdir(lnp, uiop, eofp,
+ dirlist_devices_sysnode,
+ SYSDIRLISTSZ(dirlist_devices_sysnode));
+ } else {
+ /* there shouldn't be subdirs below this */
+ error = ENOTDIR;
+ }
+
+ return (error);
+}
+
+/*
+ * lxsys_readlink(): Vnode operation for VOP_READLINK()
+ */
+/* ARGSUSED */
+static int
+lxsys_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
+{
+ char buf[MAXPATHLEN + 1];
+ lxsys_node_t *lnp = VTOLXS(vp);
+ lxsys_nodetype_t type = lnp->lxsys_type;
+ int (*rlfunc)();
+ int error;
+
+ VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+ if (vp->v_type != VLNK) {
+ return (EINVAL);
+ }
+
+ rlfunc = lxsys_readlink_function[lnp->lxsys_type];
+ if (rlfunc != NULL) {
+ if ((error = rlfunc(lnp, buf, sizeof (buf))) == 0) {
+ error = uiomove(buf, strlen(buf), UIO_READ, uiop);
+ }
+ } else {
+ error = EINVAL;
+ }
+
+ return (error);
+}
+
+
+static int
+lxsys_readlink_class_net(lxsys_node_t *lnp, char *buf, size_t len)
+{
+ netstack_t *ns;
+ ip_stack_t *ipst;
+ avl_tree_t *phytree;
+ phyint_t *phyi;
+ uint_t ifindex;
+ char ifname[LIFNAMSIZ];
+ int error = EINVAL;
+
+ if ((ifindex = lnp->lxsys_instance) == 0) {
+ return (error);
+ }
+
+ if ((ns = lxsys_netstack(lnp)) == NULL) {
+ return (error);
+ }
+ ipst = ns->netstack_ip;
+ rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+
+ phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_index;
+ phyi = avl_find(phytree, &ifindex, NULL);
+ if (phyi != NULL) {
+ (void) strncpy(ifname, phyi->phyint_name, LIFNAMSIZ);
+ lx_ifname_convert(ifname, LX_IF_FROMNATIVE);
+ (void) snprintf(buf, len, "/sys/devices/virtual/net/%s",
+ ifname);
+ error = 0;
+ }
+
+ rw_exit(&ipst->ips_ill_g_lock);
+ netstack_rele(ns);
+ return (error);
+}
+
+static int
+lxsys_readlink_block(lxsys_node_t *lnp, char *buf, size_t len)
+{
+ int inst, error = EINVAL;
+ lx_zone_data_t *lxzdata;
+ lx_virt_disk_t *vd;
+
+ if ((inst = lnp->lxsys_instance) == 0) {
+ return (error);
+ }
+
+ lxzdata = ztolxzd(curproc->p_zone);
+ if (lxzdata == NULL)
+ return (error);
+ ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+ vd = list_head(lxzdata->lxzd_vdisks);
+ while (vd != NULL) {
+ int vinst = getminor(vd->lxvd_emul_dev) & 0xffff;
+
+ if (vinst == inst) {
+ (void) snprintf(buf, len,
+ "../devices/zfs/%s", vd->lxvd_name);
+ error = 0;
+ break;
+ }
+ vd = list_next(lxzdata->lxzd_vdisks, vd);
+ }
+
+ return (error);
+}
+
+/*
+ * lxsys_inactive(): Vnode operation for VOP_INACTIVE()
+ * Vnode is no longer referenced, deallocate the file
+ * and all its resources.
+ */
+/* ARGSUSED */
+static void
+lxsys_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+ lxsys_freenode(VTOLXS(vp));
+}
+
+/*
+ * lxsys_sync(): Vnode operation for VOP_SYNC()
+ */
+static int
+lxsys_sync()
+{
+ /*
+ * Nothing to sync but this function must never fail
+ */
+ return (0);
+}
+
+/*
+ * lxsys_cmp(): Vnode operation for VOP_CMP()
+ */
+static int
+lxsys_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+ if (vn_matchops(vp1, lxsys_vnodeops) ||
+ vn_matchops(vp2, lxsys_vnodeops))
+ return (vp1 == vp2);
+ return (VOP_CMP(vp1, vp2, ct));
+}
diff --git a/usr/src/uts/common/brand/sn1/sn1_brand.c b/usr/src/uts/common/brand/sn1/sn1_brand.c
index 02d293fcb2..ebdabce2b5 100644
--- a/usr/src/uts/common/brand/sn1/sn1_brand.c
+++ b/usr/src/uts/common/brand/sn1/sn1_brand.c
@@ -43,43 +43,69 @@
char *sn1_emulation_table = NULL;
-void sn1_init_brand_data(zone_t *);
+void sn1_init_brand_data(zone_t *, kmutex_t *);
void sn1_free_brand_data(zone_t *);
void sn1_setbrand(proc_t *);
int sn1_getattr(zone_t *, int, void *, size_t *);
int sn1_setattr(zone_t *, int, void *, size_t);
int sn1_brandsys(int, int64_t *, uintptr_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t);
void sn1_copy_procdata(proc_t *, proc_t *);
-void sn1_proc_exit(struct proc *, klwp_t *);
+void sn1_proc_exit(struct proc *);
void sn1_exec();
-int sn1_initlwp(klwp_t *);
+void sn1_initlwp(klwp_t *, void *);
void sn1_forklwp(klwp_t *, klwp_t *);
void sn1_freelwp(klwp_t *);
void sn1_lwpexit(klwp_t *);
int sn1_elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
- size_t *, int, caddr_t, cred_t *, int);
+ size_t *, int, caddr_t, cred_t *, int *);
/* sn1 brand */
struct brand_ops sn1_brops = {
- sn1_init_brand_data,
- sn1_free_brand_data,
- sn1_brandsys,
- sn1_setbrand,
- sn1_getattr,
- sn1_setattr,
- sn1_copy_procdata,
- sn1_proc_exit,
- sn1_exec,
- lwp_setrval,
- sn1_initlwp,
- sn1_forklwp,
- sn1_freelwp,
- sn1_lwpexit,
- sn1_elfexec,
- NULL,
- NULL,
- NSIG,
+ sn1_init_brand_data, /* b_init_brand_data */
+ sn1_free_brand_data, /* b_free_brand_data */
+ sn1_brandsys, /* b_brandsys */
+ sn1_setbrand, /* b_setbrand */
+ sn1_getattr, /* b_getattr */
+ sn1_setattr, /* b_setattr */
+ sn1_copy_procdata, /* b_copy_procdata */
+ sn1_proc_exit, /* b_proc_exit */
+ sn1_exec, /* b_exec */
+ lwp_setrval, /* b_lwp_setrval */
+ NULL, /* b_lwpdata_alloc */
+ NULL, /* b_lwpdata_free */
+ sn1_initlwp, /* b_initlwp */
+ NULL, /* b_initlwp_post */
+ sn1_forklwp, /* b_forklwp */
+ sn1_freelwp, /* b_freelwp */
+ sn1_lwpexit, /* b_lwpexit */
+ sn1_elfexec, /* b_elfexec */
+ NULL, /* b_sigset_native_to_brand */
+ NULL, /* b_sigset_brand_to_native */
+ NULL, /* b_sigfd_translate */
+ NSIG, /* b_nsig */
+ NULL, /* b_exit_with_sig */
+ NULL, /* b_wait_filter */
+ NULL, /* b_native_exec */
+ NULL, /* b_map32limit */
+ NULL, /* b_stop_notify */
+ NULL, /* b_waitid_helper */
+ NULL, /* b_sigcld_repost */
+ NULL, /* b_issig_stop */
+ NULL, /* b_sig_ignorable */
+ NULL, /* b_savecontext */
+#if defined(_SYSCALL32_IMPL)
+ NULL, /* b_savecontext32 */
+#endif
+ NULL, /* b_restorecontext */
+ NULL, /* b_sendsig_stack */
+ NULL, /* b_sendsig */
+ NULL, /* b_setid_clear */
+ NULL, /* b_pagefault */
+ B_TRUE, /* b_intp_parse_arg */
+ NULL, /* b_clearbrand */
+ NULL, /* b_rpc_statd */
+ NULL /* b_acct_out */
};
#ifdef sparc
@@ -95,9 +121,12 @@ struct brand_mach_ops sn1_mops = {
struct brand_mach_ops sn1_mops = {
sn1_brand_sysenter_callback,
+ NULL,
sn1_brand_int91_callback,
sn1_brand_syscall_callback,
- sn1_brand_syscall32_callback
+ sn1_brand_syscall32_callback,
+ NULL,
+ NULL
};
#else /* ! __amd64 */
@@ -105,7 +134,10 @@ struct brand_mach_ops sn1_mops = {
struct brand_mach_ops sn1_mops = {
sn1_brand_sysenter_callback,
NULL,
+ NULL,
sn1_brand_syscall_callback,
+ NULL,
+ NULL,
NULL
};
#endif /* __amd64 */
@@ -116,7 +148,8 @@ struct brand sn1_brand = {
BRAND_VER_1,
"sn1",
&sn1_brops,
- &sn1_mops
+ &sn1_mops,
+ sizeof (brand_proc_data_t),
};
static struct modlbrand modlbrand = {
@@ -149,10 +182,10 @@ sn1_setattr(zone_t *zone, int attr, void *buf, size_t bufsize)
return (EINVAL);
}
-/*ARGSUSED*/
+/* ARGSUSED5 */
int
sn1_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
- uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6)
+ uintptr_t arg3, uintptr_t arg4)
{
int res;
@@ -172,9 +205,9 @@ sn1_copy_procdata(proc_t *child, proc_t *parent)
}
void
-sn1_proc_exit(struct proc *p, klwp_t *l)
+sn1_proc_exit(struct proc *p)
{
- brand_solaris_proc_exit(p, l, &sn1_brand);
+ brand_solaris_proc_exit(p, &sn1_brand);
}
void
@@ -183,10 +216,10 @@ sn1_exec()
brand_solaris_exec(&sn1_brand);
}
-int
-sn1_initlwp(klwp_t *l)
+void
+sn1_initlwp(klwp_t *l, void *bd)
{
- return (brand_solaris_initlwp(l, &sn1_brand));
+ brand_solaris_initlwp(l, &sn1_brand);
}
void
@@ -215,18 +248,18 @@ sn1_free_brand_data(zone_t *zone)
/*ARGSUSED*/
void
-sn1_init_brand_data(zone_t *zone)
+sn1_init_brand_data(zone_t *zone, kmutex_t *zsl)
{
}
int
sn1_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
int level, size_t *execsz, int setid, caddr_t exec_file, cred_t *cred,
- int brand_action)
+ int *brand_action)
{
return (brand_solaris_elfexec(vp, uap, args, idatap, level, execsz,
setid, exec_file, cred, brand_action, &sn1_brand, SN1_BRANDNAME,
- SN1_LIB, SN1_LIB32, SN1_LINKER, SN1_LINKER32));
+ SN1_LIB, SN1_LIB32));
}
int
diff --git a/usr/src/uts/common/brand/sn1/sn1_brand.h b/usr/src/uts/common/brand/sn1/sn1_brand.h
index b487745e21..fef9dc128b 100644
--- a/usr/src/uts/common/brand/sn1/sn1_brand.h
+++ b/usr/src/uts/common/brand/sn1/sn1_brand.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
#ifndef _SN1_BRAND_H
@@ -37,20 +38,14 @@ extern "C" {
#define SN1_VERSION SN1_VERSION_1
#define SN1_LIB_NAME "sn1_brand.so.1"
-#define SN1_LINKER_NAME "ld.so.1"
#define SN1_LIB32 BRAND_NATIVE_DIR "usr/lib/" SN1_LIB_NAME
-#define SN1_LINKER32 "/lib/" SN1_LINKER_NAME
-
#define SN1_LIB64 BRAND_NATIVE_DIR "usr/lib/64/" SN1_LIB_NAME
-#define SN1_LINKER64 "/lib/64/" SN1_LINKER_NAME
#if defined(_LP64)
#define SN1_LIB SN1_LIB64
-#define SN1_LINKER SN1_LINKER64
#else /* !_LP64 */
#define SN1_LIB SN1_LIB32
-#define SN1_LINKER SN1_LINKER32
#endif /* !_LP64 */
#if defined(_KERNEL)
diff --git a/usr/src/uts/common/brand/solaris10/s10_brand.c b/usr/src/uts/common/brand/solaris10/s10_brand.c
index b3ea043cdb..4de7cbcc05 100644
--- a/usr/src/uts/common/brand/solaris10/s10_brand.c
+++ b/usr/src/uts/common/brand/solaris10/s10_brand.c
@@ -46,45 +46,71 @@
char *s10_emulation_table = NULL;
-void s10_init_brand_data(zone_t *);
+void s10_init_brand_data(zone_t *, kmutex_t *);
void s10_free_brand_data(zone_t *);
void s10_setbrand(proc_t *);
int s10_getattr(zone_t *, int, void *, size_t *);
int s10_setattr(zone_t *, int, void *, size_t);
int s10_brandsys(int, int64_t *, uintptr_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t);
void s10_copy_procdata(proc_t *, proc_t *);
-void s10_proc_exit(struct proc *, klwp_t *);
+void s10_proc_exit(struct proc *);
void s10_exec();
-int s10_initlwp(klwp_t *);
+void s10_initlwp(klwp_t *, void *);
void s10_forklwp(klwp_t *, klwp_t *);
void s10_freelwp(klwp_t *);
void s10_lwpexit(klwp_t *);
int s10_elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
- size_t *, int, caddr_t, cred_t *, int);
+ size_t *, int, caddr_t, cred_t *, int *);
void s10_sigset_native_to_s10(sigset_t *);
void s10_sigset_s10_to_native(sigset_t *);
/* s10 brand */
struct brand_ops s10_brops = {
- s10_init_brand_data,
- s10_free_brand_data,
- s10_brandsys,
- s10_setbrand,
- s10_getattr,
- s10_setattr,
- s10_copy_procdata,
- s10_proc_exit,
- s10_exec,
- lwp_setrval,
- s10_initlwp,
- s10_forklwp,
- s10_freelwp,
- s10_lwpexit,
- s10_elfexec,
- s10_sigset_native_to_s10,
- s10_sigset_s10_to_native,
- S10_NSIG,
+ s10_init_brand_data, /* b_init_brand_data */
+ s10_free_brand_data, /* b_free_brand_data */
+ s10_brandsys, /* b_brandsys */
+ s10_setbrand, /* b_setbrand */
+ s10_getattr, /* b_getattr */
+ s10_setattr, /* b_setattr */
+ s10_copy_procdata, /* b_copy_procdata */
+ s10_proc_exit, /* b_proc_exit */
+ s10_exec, /* b_exec */
+ lwp_setrval, /* b_lwp_setrval */
+ NULL, /* b_lwpdata_alloc */
+ NULL, /* b_lwpdata_free */
+ s10_initlwp, /* b_initlwp */
+ NULL, /* b_initlwp_post */
+ s10_forklwp, /* b_forklwp */
+ s10_freelwp, /* b_freelwp */
+ s10_lwpexit, /* b_lwpexit */
+ s10_elfexec, /* b_elfexec */
+ s10_sigset_native_to_s10, /* b_sigset_native_to_brand */
+ s10_sigset_s10_to_native, /* b_sigset_brand_to_native */
+ NULL, /* b_sigfd_translate */
+ S10_NSIG, /* b_nsig */
+ NULL, /* b_exit_with_sig */
+ NULL, /* b_wait_filter */
+ NULL, /* b_native_exec */
+ NULL, /* b_map32limit */
+ NULL, /* b_stop_notify */
+ NULL, /* b_waitid_helper */
+ NULL, /* b_sigcld_repost */
+ NULL, /* b_issig_stop */
+ NULL, /* b_sig_ignorable */
+ NULL, /* b_savecontext */
+#if defined(_SYSCALL32_IMPL)
+ NULL, /* b_savecontext32 */
+#endif
+ NULL, /* b_restorecontext */
+ NULL, /* b_sendsig_stack */
+ NULL, /* b_sendsig */
+ NULL, /* b_setid_clear */
+ NULL, /* b_pagefault */
+ B_TRUE, /* b_intp_parse_arg */
+ NULL, /* b_clearbrand */
+ NULL, /* b_rpc_statd */
+ NULL /* b_acct_out */
};
#ifdef sparc
@@ -100,9 +126,12 @@ struct brand_mach_ops s10_mops = {
struct brand_mach_ops s10_mops = {
s10_brand_sysenter_callback,
+ NULL,
s10_brand_int91_callback,
s10_brand_syscall_callback,
- s10_brand_syscall32_callback
+ s10_brand_syscall32_callback,
+ NULL,
+ NULL
};
#else /* ! __amd64 */
@@ -110,7 +139,10 @@ struct brand_mach_ops s10_mops = {
struct brand_mach_ops s10_mops = {
s10_brand_sysenter_callback,
NULL,
+ NULL,
s10_brand_syscall_callback,
+ NULL,
+ NULL,
NULL
};
#endif /* __amd64 */
@@ -121,7 +153,8 @@ struct brand s10_brand = {
BRAND_VER_1,
"solaris10",
&s10_brops,
- &s10_mops
+ &s10_mops,
+ sizeof (brand_proc_data_t),
};
static struct modlbrand modlbrand = {
@@ -250,10 +283,10 @@ s10_native(void *cmd, void *args)
return (0);
}
-/*ARGSUSED*/
+/* ARGSUSED5 */
int
s10_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
- uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6)
+ uintptr_t arg3, uintptr_t arg4)
{
proc_t *p = curproc;
int res;
@@ -327,9 +360,9 @@ s10_copy_procdata(proc_t *child, proc_t *parent)
}
void
-s10_proc_exit(struct proc *p, klwp_t *l)
+s10_proc_exit(struct proc *p)
{
- brand_solaris_proc_exit(p, l, &s10_brand);
+ brand_solaris_proc_exit(p, &s10_brand);
}
void
@@ -338,10 +371,10 @@ s10_exec()
brand_solaris_exec(&s10_brand);
}
-int
-s10_initlwp(klwp_t *l)
+void
+s10_initlwp(klwp_t *l, void *bd)
{
- return (brand_solaris_initlwp(l, &s10_brand));
+ brand_solaris_initlwp(l, &s10_brand);
}
void
@@ -381,7 +414,7 @@ s10_free_brand_data(zone_t *zone)
}
void
-s10_init_brand_data(zone_t *zone)
+s10_init_brand_data(zone_t *zone, kmutex_t *zsl)
{
ASSERT(zone->zone_brand == &s10_brand);
ASSERT(zone->zone_brand_data == NULL);
@@ -391,11 +424,11 @@ s10_init_brand_data(zone_t *zone)
int
s10_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
int level, size_t *execsz, int setid, caddr_t exec_file, cred_t *cred,
- int brand_action)
+ int *brand_action)
{
return (brand_solaris_elfexec(vp, uap, args, idatap, level, execsz,
setid, exec_file, cred, brand_action, &s10_brand, S10_BRANDNAME,
- S10_LIB, S10_LIB32, S10_LINKER, S10_LINKER32));
+ S10_LIB, S10_LIB32));
}
void
diff --git a/usr/src/uts/common/brand/solaris10/s10_brand.h b/usr/src/uts/common/brand/solaris10/s10_brand.h
index 11f9853f48..ffef485e12 100644
--- a/usr/src/uts/common/brand/solaris10/s10_brand.h
+++ b/usr/src/uts/common/brand/solaris10/s10_brand.h
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
#ifndef _S10_BRAND_H
@@ -42,17 +43,12 @@ extern "C" {
#define S10_LINKER_NAME "ld.so.1"
#define S10_LIB32 BRAND_NATIVE_DIR "usr/lib/" S10_LIB_NAME
-#define S10_LINKER32 "/lib/" S10_LINKER_NAME
-
#define S10_LIB64 BRAND_NATIVE_DIR "usr/lib/64/" S10_LIB_NAME
-#define S10_LINKER64 "/lib/64/" S10_LINKER_NAME
#if defined(_LP64)
#define S10_LIB S10_LIB64
-#define S10_LINKER S10_LINKER64
#else /* !_LP64 */
#define S10_LIB S10_LIB32
-#define S10_LINKER S10_LINKER32
#endif /* !_LP64 */
/*
diff --git a/usr/src/uts/common/conf/param.c b/usr/src/uts/common/conf/param.c
index a4266ba267..93481c378e 100644
--- a/usr/src/uts/common/conf/param.c
+++ b/usr/src/uts/common/conf/param.c
@@ -22,6 +22,7 @@
/*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2017, Joyent, Inc.
* Copyright 2012 Milan Jurik. All rights reserved.
* Copyright 2022 Garrett D'Amore <garrett@damore.org>
*/
@@ -538,8 +539,8 @@ char *isa_list = architecture;
static pgcnt_t original_physmem = 0;
#define MIN_DEFAULT_MAXUSERS 8u
-#define MAX_DEFAULT_MAXUSERS 2048u
-#define MAX_MAXUSERS 4096u
+#define MAX_DEFAULT_MAXUSERS 10000u
+#define MAX_MAXUSERS 20000u
void
param_preset(void)
@@ -551,7 +552,7 @@ void
param_calc(int platform_max_nprocs)
{
/*
- * Default to about one "user" per megabyte, taking into
+ * Default to about one "user" per 8MB, taking into
* account both physical and virtual constraints.
* Note: 2^20 is a meg; shifting right by (20 - PAGESHIFT)
* converts pages to megs without integer overflow.
@@ -565,8 +566,9 @@ param_calc(int platform_max_nprocs)
if (maxusers == 0) {
pgcnt_t physmegs = physmem >> (20 - PAGESHIFT);
pgcnt_t virtmegs = vmem_size(heap_arena, VMEM_FREE) >> 20;
- maxusers = MIN(MAX(MIN(physmegs, virtmegs),
- MIN_DEFAULT_MAXUSERS), MAX_DEFAULT_MAXUSERS);
+ maxusers = MIN(physmegs, virtmegs) >> 3; /* divide by 8 */
+ maxusers = MAX(maxusers, MIN_DEFAULT_MAXUSERS);
+ maxusers = MIN(maxusers, MAX_DEFAULT_MAXUSERS);
}
if (maxusers > MAX_MAXUSERS) {
maxusers = MAX_MAXUSERS;
@@ -583,15 +585,26 @@ param_calc(int platform_max_nprocs)
/*
* We need to dynamically change any variables now so that
- * the setting of maxusers and pidmax propagate to the other
+ * the setting of maxusers and maxpid propagate to the other
* variables that are dependent on them.
*/
if (reserved_procs == 0)
reserved_procs = 5;
- if (pidmax < reserved_procs || pidmax > MAX_MAXPID)
+ if (pidmax < reserved_procs || pidmax > MAX_MAXPID) {
maxpid = MAX_MAXPID;
- else
+ } else {
+ /*
+ * If pidmax has not been explicity set in /etc/system, then
+ * increase it to the maximum on larger machines. We choose a
+ * 128GB memory size as the threshold to increase pidmax.
+ */
+ if (pidmax == DEFAULT_MAXPID) {
+ if (physmem > (btop(128ULL * 0x40000000ULL))) {
+ pidmax = MAX_MAXPID;
+ }
+ }
maxpid = pidmax;
+ }
/*
* This allows platform-dependent code to constrain the maximum
diff --git a/usr/src/uts/common/contract/process.c b/usr/src/uts/common/contract/process.c
index 3f25547b50..32e5707cb2 100644
--- a/usr/src/uts/common/contract/process.c
+++ b/usr/src/uts/common/contract/process.c
@@ -952,6 +952,18 @@ contract_process_exit(cont_process_t *ctp, proc_t *p, int exitstatus)
(void) cte_publish_all(ct, event, nvl, NULL);
mutex_enter(&ct->ct_lock);
}
+
+ /*
+ * CT_PR_EV_EXIT is not part of the CT_PR_ALLFATAL definition since
+ * we never allow including this in the fatal set via a user-land
+ * application, but we do allow CT_PR_EV_EXIT in the contract's fatal
+ * set for a process setup for zone init. See zone_start_init().
+ */
+ if (EVFATALP(ctp, CT_PR_EV_EXIT)) {
+ ASSERT(MUTEX_HELD(&ct->ct_lock));
+ contract_process_kill(ct, p, B_TRUE);
+ }
+
if (empty) {
/*
* Send EMPTY message.
@@ -1054,6 +1066,17 @@ contract_process_fork(ctmpl_process_t *rtmpl, proc_t *cp, proc_t *pp,
event->cte_type = CT_PR_EV_FORK;
(void) cte_publish_all(ct, event, nvl, NULL);
}
+
+ /*
+ * Because the CT_PR_KEEP_EXEC flag is meant to be used by applications
+ * which are not contract aware, we can assume that these applications
+ * will never explicitly abandon the child's new contract. Thus, we
+ * abandon it now.
+ */
+ if (ctp->conp_params & CT_PR_KEEP_EXEC) {
+ (void) contract_abandon(ct, pp, 1);
+ }
+
return (ctp);
}
diff --git a/usr/src/uts/common/crypto/api/kcf_random.c b/usr/src/uts/common/crypto/api/kcf_random.c
index b99a834503..80bbb64505 100644
--- a/usr/src/uts/common/crypto/api/kcf_random.c
+++ b/usr/src/uts/common/crypto/api/kcf_random.c
@@ -70,6 +70,7 @@
#include <sys/cpuvar.h>
#include <sys/taskq.h>
#include <rng/fips_random.h>
+#include <sys/strlog.h>
#define RNDPOOLSIZE 1024 /* Pool size in bytes */
#define MINEXTRACTBYTES 20
diff --git a/usr/src/uts/common/crypto/core/kcf_sched.c b/usr/src/uts/common/crypto/core/kcf_sched.c
index 9e079a079e..ec9df915c5 100644
--- a/usr/src/uts/common/crypto/core/kcf_sched.c
+++ b/usr/src/uts/common/crypto/core/kcf_sched.c
@@ -1027,9 +1027,9 @@ kcfpool_svc(void *arg)
case 0:
case -1:
/*
- * Woke up with no work to do. Check
- * if this thread should exit. We keep
- * at least kcf_minthreads.
+ * Woke up with no work to do. Check if we
+ * should lwp_exit() (which won't return). We
+ * keep at least kcf_minthreads.
*/
if (kcfpool->kp_threads > kcf_minthreads) {
KCF_ATOMIC_DECR(kcfpool->kp_threads);
diff --git a/usr/src/uts/common/disp/cmt.c b/usr/src/uts/common/disp/cmt.c
index fd734bd229..30a69a23f0 100644
--- a/usr/src/uts/common/disp/cmt.c
+++ b/usr/src/uts/common/disp/cmt.c
@@ -201,13 +201,15 @@ pg_cmt_cpu_startup(cpu_t *cp)
/*
* Return non-zero if thread can migrate between "from" and "to"
- * without a performance penalty
+ * without a performance penalty. This is true only if we share a core on
+ * virtually any CPU; sharing the last-level cache is insufficient to make
+ * migration possible without penalty.
*/
int
pg_cmt_can_migrate(cpu_t *from, cpu_t *to)
{
- if (from->cpu_physid->cpu_cacheid ==
- to->cpu_physid->cpu_cacheid)
+ if (from->cpu_physid->cpu_coreid ==
+ to->cpu_physid->cpu_coreid)
return (1);
return (0);
}
diff --git a/usr/src/uts/common/disp/cpucaps.c b/usr/src/uts/common/disp/cpucaps.c
index 46f53faab6..2a4365ff73 100644
--- a/usr/src/uts/common/disp/cpucaps.c
+++ b/usr/src/uts/common/disp/cpucaps.c
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2013 Joyent, Inc. All rights reserved.
*/
#include <sys/disp.h>
@@ -74,6 +75,32 @@
* Putting threads on wait queues in random places while running in the
* kernel might lead to all kinds of locking problems.
*
+ * Bursting
+ * ========
+ *
+ * CPU bursting occurs when the CPU usage is over the baseline but under the
+ * cap. The baseline CPU (zone.cpu-baseline) is set in a multi-tenant
+ * environment so that we know how much CPU is allocated for a tenant under
+ * normal utilization. We can then track how much time a zone is spending
+ * over the "normal" CPU utilization expected for that zone using the
+ * "above_base_sec" kstat. This kstat is cumulative.
+ *
+ * If the zone has a burst limit (zone.cpu-burst-time) then the zone can
+ * burst for that period of time (in seconds) before the effective cap is
+ * lowered to the baseline. Once the effective cap is lowered, the zone
+ * will run at the baseline for the burst limit before the effective cap is
+ * raised again to the full value. This will allow the zone to burst again.
+ * We can watch this behavior using the kstats. The "effective" kstat shows
+ * which cap is being used, the baseline value or the burst value. The
+ * "burst_limit_sec" shows the value of the zone.cpu-burst-time rctl and the
+ * "bursting_sec" kstat shows how many seconds the zone has currently been
+ * bursting. When the CPU load is continuously greater than the baseline,
+ * bursting_sec will increase, up to the burst_limit_sec value, then the
+ * effective kstat will drop to the baseline and the bursting_sec value will
+ * decrease until it hits 0, at which time the effective kstat will return to
+ * the full burst value and the bursting_sec value will begin to increase
+ * again.
+ *
* Accounting
* ==========
*
@@ -203,18 +230,28 @@ static void caps_update();
*/
struct cap_kstat {
kstat_named_t cap_value;
+ kstat_named_t cap_baseline;
+ kstat_named_t cap_effective;
+ kstat_named_t cap_burst_limit;
+ kstat_named_t cap_bursting;
kstat_named_t cap_usage;
kstat_named_t cap_nwait;
kstat_named_t cap_below;
kstat_named_t cap_above;
+ kstat_named_t cap_above_base;
kstat_named_t cap_maxusage;
kstat_named_t cap_zonename;
} cap_kstat = {
{ "value", KSTAT_DATA_UINT64 },
+ { "baseline", KSTAT_DATA_UINT64 },
+ { "effective", KSTAT_DATA_UINT64 },
+ { "burst_limit_sec", KSTAT_DATA_UINT64 },
+ { "bursting_sec", KSTAT_DATA_UINT64 },
{ "usage", KSTAT_DATA_UINT64 },
{ "nwait", KSTAT_DATA_UINT64 },
{ "below_sec", KSTAT_DATA_UINT64 },
{ "above_sec", KSTAT_DATA_UINT64 },
+ { "above_base_sec", KSTAT_DATA_UINT64 },
{ "maxusage", KSTAT_DATA_UINT64 },
{ "zonename", KSTAT_DATA_STRING },
};
@@ -311,7 +348,7 @@ cap_enable(list_t *l, cpucap_t *cap, hrtime_t value)
cap->cap_below = cap->cap_above = 0;
cap->cap_maxusage = 0;
cap->cap_usage = 0;
- cap->cap_value = value;
+ cap->cap_value = cap->cap_chk_value = value;
waitq_unblock(&cap->cap_waitq);
if (CPUCAPS_OFF()) {
cpucaps_enabled = B_TRUE;
@@ -340,19 +377,21 @@ cap_disable(list_t *l, cpucap_t *cap)
ASSERT(CAP_ENABLED(cap));
waitq_block(&cap->cap_waitq);
+
+ /* do this first to avoid race with cap_kstat_update */
+ if (cap->cap_kstat != NULL) {
+ kstat_delete(cap->cap_kstat);
+ cap->cap_kstat = NULL;
+ }
+
list_remove(l, cap);
if (list_is_empty(&capped_projects) && list_is_empty(&capped_zones)) {
cpucaps_enabled = B_FALSE;
cpucaps_clock_callout = NULL;
}
- cap->cap_value = 0;
+ cap->cap_value = cap->cap_chk_value = 0;
cap->cap_project = NULL;
cap->cap_zone = NULL;
- if (cap->cap_kstat != NULL) {
- kstat_delete(cap->cap_kstat);
- cap->cap_kstat = NULL;
- }
-
}
/*
@@ -487,6 +526,8 @@ cap_walk(list_t *l, void (*cb)(cpucap_t *, int64_t))
* The waitq_isempty check is performed without the waitq lock. If a new thread
* is placed on the waitq right after the check, it will be picked up during the
* next invocation of cap_poke_waitq().
+ *
+ * Called once per tick for zones.
*/
/* ARGSUSED */
static void
@@ -494,15 +535,92 @@ cap_poke_waitq(cpucap_t *cap, int64_t gen)
{
ASSERT(MUTEX_HELD(&caps_lock));
- if (cap->cap_usage >= cap->cap_value) {
+ if (cap->cap_base != 0) {
+ /*
+ * Because of the way usage is calculated and decayed, its
+ * possible for the zone to be slightly over its cap, but we
+ * don't want to count that after we have reduced the effective
+ * cap to the baseline. That way the zone will be able to
+ * burst again after the burst_limit has expired.
+ */
+ if (cap->cap_usage > cap->cap_base &&
+ cap->cap_chk_value == cap->cap_value) {
+ cap->cap_above_base++;
+
+ /*
+ * If bursting is limited and we've been bursting
+ * longer than we're supposed to, then set the
+ * effective cap to the baseline.
+ */
+ if (cap->cap_burst_limit != 0) {
+ cap->cap_bursting++;
+ if (cap->cap_bursting >= cap->cap_burst_limit)
+ cap->cap_chk_value = cap->cap_base;
+ }
+ } else if (cap->cap_bursting > 0) {
+ /*
+ * We're not bursting now, but we were, decay the
+ * bursting timer.
+ */
+ cap->cap_bursting--;
+ /*
+ * Reset the effective cap once we decay to 0 so we
+ * can burst again.
+ */
+ if (cap->cap_bursting == 0 &&
+ cap->cap_chk_value != cap->cap_value)
+ cap->cap_chk_value = cap->cap_value;
+ }
+ }
+
+ if (cap->cap_usage >= cap->cap_chk_value) {
cap->cap_above++;
} else {
waitq_t *wq = &cap->cap_waitq;
cap->cap_below++;
- if (!waitq_isempty(wq))
- waitq_runone(wq);
+ if (!waitq_isempty(wq)) {
+ int i, ndequeue, p;
+
+ /*
+ * Since this function is only called once per tick,
+ * we can hit a situation where we have artificially
+ * limited the project/zone below its cap. This would
+ * happen if we have multiple threads queued up but
+ * only dequeued one thread/tick. To avoid this we
+ * dequeue multiple threads, calculated based on the
+ * usage percentage of the cap. It is possible that we
+ * could dequeue too many threads and some of them
+ * might be put back on the wait queue quickly, but
+ * since we know that threads are on the wait queue
+ * because we're capping, we know that there is unused
+ * CPU cycles anyway, so this extra work would not
+ * hurt. Also, the ndequeue number is only an upper
+ * bound and we might dequeue less, depending on how
+ * many threads are actually in the wait queue. The
+ * ndequeue values are empirically derived and could be
+ * adjusted or calculated in another way if necessary.
+ */
+ p = (int)((100 * cap->cap_usage) / cap->cap_chk_value);
+ if (p >= 98)
+ ndequeue = 10;
+ else if (p >= 95)
+ ndequeue = 20;
+ else if (p >= 90)
+ ndequeue = 40;
+ else if (p >= 85)
+ ndequeue = 80;
+ else
+ ndequeue = 160;
+
+ for (i = 0; i < ndequeue; i++) {
+ waitq_runone(wq);
+ if (waitq_isempty(wq))
+ break;
+ }
+ DTRACE_PROBE2(cpucaps__pokeq, int, p, int, i);
+ }
}
}
@@ -629,14 +747,14 @@ cap_project_zone_modify_walker(kproject_t *kpj, void *arg)
* Remove all projects in this zone without caps
* from the capped_projects list.
*/
- if (project_cap->cap_value == MAX_USAGE) {
+ if (project_cap->cap_chk_value == MAX_USAGE) {
cap_project_disable(kpj);
}
} else if (CAP_DISABLED(project_cap)) {
/*
* Add the project to capped_projects list.
*/
- ASSERT(project_cap->cap_value == 0);
+ ASSERT(project_cap->cap_chk_value == 0);
cap_project_enable(kpj, MAX_USAGE);
}
mutex_exit(&caps_lock);
@@ -746,7 +864,7 @@ cpucaps_zone_set(zone_t *zone, rctl_qty_t cap_val)
/*
* No state transitions, just change the value
*/
- cap->cap_value = value;
+ cap->cap_value = cap->cap_chk_value = value;
}
ASSERT(MUTEX_HELD(&caps_lock));
@@ -757,6 +875,108 @@ cpucaps_zone_set(zone_t *zone, rctl_qty_t cap_val)
}
/*
+ * Set zone's base cpu value to base_val
+ */
+int
+cpucaps_zone_set_base(zone_t *zone, rctl_qty_t base_val)
+{
+ cpucap_t *cap = NULL;
+ hrtime_t value;
+
+ ASSERT(base_val <= MAXCAP);
+ if (base_val > MAXCAP)
+ base_val = MAXCAP;
+
+ if (CPUCAPS_OFF() || !ZONE_IS_CAPPED(zone))
+ return (0);
+
+ if (zone->zone_cpucap == NULL)
+ cap = cap_alloc();
+
+ mutex_enter(&caps_lock);
+
+ if (cpucaps_busy) {
+ mutex_exit(&caps_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * Double-check whether zone->zone_cpucap is NULL, now with caps_lock
+ * held. If it is still NULL, assign a newly allocated cpucap to it.
+ */
+ if (zone->zone_cpucap == NULL) {
+ zone->zone_cpucap = cap;
+ } else if (cap != NULL) {
+ cap_free(cap);
+ }
+
+ cap = zone->zone_cpucap;
+
+ value = base_val * cap_tick_cost;
+ if (value < 0 || value > cap->cap_value)
+ value = 0;
+
+ cap->cap_base = value;
+
+ mutex_exit(&caps_lock);
+
+ return (0);
+}
+
+/*
+ * Set zone's maximum burst time in seconds. A burst time of 0 means that
+ * the zone can run over its baseline indefinitely.
+ */
+int
+cpucaps_zone_set_burst_time(zone_t *zone, rctl_qty_t base_val)
+{
+ cpucap_t *cap = NULL;
+ hrtime_t value;
+
+ ASSERT(base_val <= INT_MAX);
+ /* Treat the default as 0 - no limit */
+ if (base_val == INT_MAX)
+ base_val = 0;
+ if (base_val > INT_MAX)
+ base_val = INT_MAX;
+
+ if (CPUCAPS_OFF() || !ZONE_IS_CAPPED(zone))
+ return (0);
+
+ if (zone->zone_cpucap == NULL)
+ cap = cap_alloc();
+
+ mutex_enter(&caps_lock);
+
+ if (cpucaps_busy) {
+ mutex_exit(&caps_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * Double-check whether zone->zone_cpucap is NULL, now with caps_lock
+ * held. If it is still NULL, assign a newly allocated cpucap to it.
+ */
+ if (zone->zone_cpucap == NULL) {
+ zone->zone_cpucap = cap;
+ } else if (cap != NULL) {
+ cap_free(cap);
+ }
+
+ cap = zone->zone_cpucap;
+
+ value = SEC_TO_TICK(base_val);
+ if (value < 0)
+ value = 0;
+
+ cap->cap_burst_limit = value;
+
+ mutex_exit(&caps_lock);
+
+ return (0);
+}
+
+/*
* The project is going away so disable its cap.
*/
void
@@ -902,7 +1122,7 @@ cpucaps_project_set(kproject_t *kpj, rctl_qty_t cap_val)
if (CAP_DISABLED(cap))
cap_project_enable(kpj, value);
else
- cap->cap_value = value;
+ cap->cap_value = cap->cap_chk_value = value;
} else if (CAP_ENABLED(cap)) {
/*
* User requested to drop a cap on the project. If it is part of
@@ -910,7 +1130,7 @@ cpucaps_project_set(kproject_t *kpj, rctl_qty_t cap_val)
* otherwise disable the cap.
*/
if (ZONE_IS_CAPPED(kpj->kpj_zone)) {
- cap->cap_value = MAX_USAGE;
+ cap->cap_value = cap->cap_chk_value = MAX_USAGE;
} else {
cap_project_disable(kpj);
}
@@ -948,6 +1168,26 @@ cpucaps_zone_get(zone_t *zone)
}
/*
+ * Get current zone baseline.
+ */
+rctl_qty_t
+cpucaps_zone_get_base(zone_t *zone)
+{
+ return (zone->zone_cpucap != NULL ?
+ (rctl_qty_t)(zone->zone_cpucap->cap_base / cap_tick_cost) : 0);
+}
+
+/*
+ * Get current zone maximum burst time.
+ */
+rctl_qty_t
+cpucaps_zone_get_burst_time(zone_t *zone)
+{
+ return (zone->zone_cpucap != NULL ?
+ (rctl_qty_t)(TICK_TO_SEC(zone->zone_cpucap->cap_burst_limit)) : 0);
+}
+
+/*
* Charge project of thread t the time thread t spent on CPU since previously
* adjusted.
*
@@ -1045,7 +1285,7 @@ cpucaps_charge(kthread_id_t t, caps_sc_t *csc, cpucaps_charge_t charge_type)
project_cap = kpj->kpj_cpucap;
- if (project_cap->cap_usage >= project_cap->cap_value) {
+ if (project_cap->cap_usage >= project_cap->cap_chk_value) {
t->t_schedflag |= TS_PROJWAITQ;
rc = B_TRUE;
} else if (t->t_schedflag & TS_PROJWAITQ) {
@@ -1059,7 +1299,7 @@ cpucaps_charge(kthread_id_t t, caps_sc_t *csc, cpucaps_charge_t charge_type)
} else {
cpucap_t *zone_cap = zone->zone_cpucap;
- if (zone_cap->cap_usage >= zone_cap->cap_value) {
+ if (zone_cap->cap_usage >= zone_cap->cap_chk_value) {
t->t_schedflag |= TS_ZONEWAITQ;
rc = B_TRUE;
} else if (t->t_schedflag & TS_ZONEWAITQ) {
@@ -1119,6 +1359,7 @@ cpucaps_enforce(kthread_t *t)
/*
* Convert internal cap statistics into values exported by cap kstat.
+ * Note that the kstat is held throughout this function but caps_lock is not.
*/
static int
cap_kstat_update(kstat_t *ksp, int rw)
@@ -1133,6 +1374,12 @@ cap_kstat_update(kstat_t *ksp, int rw)
capsp->cap_value.value.ui64 =
ROUND_SCALE(cap->cap_value, cap_tick_cost);
+ capsp->cap_baseline.value.ui64 =
+ ROUND_SCALE(cap->cap_base, cap_tick_cost);
+ capsp->cap_effective.value.ui64 =
+ ROUND_SCALE(cap->cap_chk_value, cap_tick_cost);
+ capsp->cap_burst_limit.value.ui64 =
+ ROUND_SCALE(cap->cap_burst_limit, tick_sec);
capsp->cap_usage.value.ui64 =
ROUND_SCALE(cap->cap_usage, cap_tick_cost);
capsp->cap_maxusage.value.ui64 =
@@ -1140,6 +1387,10 @@ cap_kstat_update(kstat_t *ksp, int rw)
capsp->cap_nwait.value.ui64 = cap->cap_waitq.wq_count;
capsp->cap_below.value.ui64 = ROUND_SCALE(cap->cap_below, tick_sec);
capsp->cap_above.value.ui64 = ROUND_SCALE(cap->cap_above, tick_sec);
+ capsp->cap_above_base.value.ui64 =
+ ROUND_SCALE(cap->cap_above_base, tick_sec);
+ capsp->cap_bursting.value.ui64 =
+ ROUND_SCALE(cap->cap_bursting, tick_sec);
kstat_named_setstr(&capsp->cap_zonename, zonename);
return (0);
diff --git a/usr/src/uts/common/disp/disp.c b/usr/src/uts/common/disp/disp.c
index a9d5f969dc..f0e4aaecab 100644
--- a/usr/src/uts/common/disp/disp.c
+++ b/usr/src/uts/common/disp/disp.c
@@ -2256,7 +2256,7 @@ disp_getbest(disp_t *dp)
* placed earlier.
*/
if (tcp == NULL ||
- pri >= minclsyspri ||
+ (pri >= minclsyspri && tp->t_procp == &p0) ||
tp->t_cpu != tcp)
break;
diff --git a/usr/src/uts/common/disp/fx.c b/usr/src/uts/common/disp/fx.c
index 7fc81e7815..191075e032 100644
--- a/usr/src/uts/common/disp/fx.c
+++ b/usr/src/uts/common/disp/fx.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -70,16 +70,6 @@ static struct modlinkage modlinkage = {
};
-/*
- * control flags (kparms->fx_cflags).
- */
-#define FX_DOUPRILIM 0x01 /* change user priority limit */
-#define FX_DOUPRI 0x02 /* change user priority */
-#define FX_DOTQ 0x04 /* change FX time quantum */
-
-
-#define FXMAXUPRI 60 /* maximum user priority setting */
-
#define FX_MAX_UNPRIV_PRI 0 /* maximum unpriviledge priority */
/*
diff --git a/usr/src/uts/common/disp/priocntl.c b/usr/src/uts/common/disp/priocntl.c
index 5412df83f5..60e870ba28 100644
--- a/usr/src/uts/common/disp/priocntl.c
+++ b/usr/src/uts/common/disp/priocntl.c
@@ -114,7 +114,7 @@ copyin_vaparms32(caddr_t arg, pc_vaparms_t *vap, uio_seg_t seg)
#endif
-static int donice(procset_t *, pcnice_t *);
+int donice(procset_t *, pcnice_t *);
static int doprio(procset_t *, pcprio_t *);
static int proccmp(proc_t *, struct pcmpargs *);
static int setparms(proc_t *, struct stprmargs *);
@@ -991,7 +991,7 @@ setprocnice(proc_t *pp, pcnice_t *pcnice)
/*
* Update the nice value of the specified LWP or set of processes.
*/
-static int
+int
donice(procset_t *procset, pcnice_t *pcnice)
{
int err_proc = 0;
diff --git a/usr/src/uts/common/disp/rt.c b/usr/src/uts/common/disp/rt.c
index f87f8c56ce..115e42ccb8 100644
--- a/usr/src/uts/common/disp/rt.c
+++ b/usr/src/uts/common/disp/rt.c
@@ -22,7 +22,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2013 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -103,13 +103,6 @@ _info(struct modinfo *modinfop)
pri_t rt_maxpri = RTMAXPRI; /* maximum real-time priority */
rtdpent_t *rt_dptbl; /* real-time dispatcher parameter table */
-/*
- * control flags (kparms->rt_cflags).
- */
-#define RT_DOPRI 0x01 /* change priority */
-#define RT_DOTQ 0x02 /* change RT time quantum */
-#define RT_DOSIG 0x04 /* change RT time quantum signal */
-
static int rt_admin(caddr_t, cred_t *);
static int rt_enterclass(kthread_t *, id_t, void *, cred_t *, void *);
static int rt_fork(kthread_t *, kthread_t *, void *);
diff --git a/usr/src/uts/common/disp/rt_dptbl.c b/usr/src/uts/common/disp/rt_dptbl.c
index 1012b5aef2..a5c8836518 100644
--- a/usr/src/uts/common/disp/rt_dptbl.c
+++ b/usr/src/uts/common/disp/rt_dptbl.c
@@ -68,8 +68,6 @@ _info(struct modinfo *modinfop)
return (mod_info(&modlinkage, modinfop));
}
-#define RTGPPRIO0 100 /* Global priority for RT priority 0 */
-
rtdpent_t config_rt_dptbl[] = {
/* prilevel Time quantum */
diff --git a/usr/src/uts/common/disp/thread.c b/usr/src/uts/common/disp/thread.c
index 764942d4df..b2b28ec06f 100644
--- a/usr/src/uts/common/disp/thread.c
+++ b/usr/src/uts/common/disp/thread.c
@@ -77,6 +77,10 @@
#include <sys/ctype.h>
#include <sys/smt.h>
+#ifndef STACK_GROWTH_DOWN
+#error Stacks do not grow downward; 3b2 zombie attack detected!
+#endif
+
struct kmem_cache *thread_cache; /* cache of free threads */
struct kmem_cache *lwp_cache; /* cache of free lwps */
struct kmem_cache *turnstile_cache; /* cache of free turnstiles */
@@ -374,7 +378,7 @@ thread_create(
if (stksize <= sizeof (kthread_t) + PTR24_ALIGN)
cmn_err(CE_PANIC, "thread_create: proposed stack size"
" too small to hold thread.");
-#ifdef STACK_GROWTH_DOWN
+
stksize -= SA(sizeof (kthread_t) + PTR24_ALIGN - 1);
stksize &= -PTR24_ALIGN; /* make thread aligned */
t = (kthread_t *)(stk + stksize);
@@ -383,13 +387,6 @@ thread_create(
audit_thread_create(t);
t->t_stk = stk + stksize;
t->t_stkbase = stk;
-#else /* stack grows to larger addresses */
- stksize -= SA(sizeof (kthread_t));
- t = (kthread_t *)(stk);
- bzero(t, sizeof (kthread_t));
- t->t_stk = stk + sizeof (kthread_t);
- t->t_stkbase = stk + stksize + sizeof (kthread_t);
-#endif /* STACK_GROWTH_DOWN */
t->t_flag |= T_TALLOCSTK;
t->t_swap = stk;
} else {
@@ -402,13 +399,8 @@ thread_create(
* Initialize t_stk to the kernel stack pointer to use
* upon entry to the kernel
*/
-#ifdef STACK_GROWTH_DOWN
t->t_stk = stk + stksize;
t->t_stkbase = stk;
-#else
- t->t_stk = stk; /* 3b2-like */
- t->t_stkbase = stk + stksize;
-#endif /* STACK_GROWTH_DOWN */
}
if (kmem_stackinfo != 0) {
@@ -584,6 +576,9 @@ thread_exit(void)
if ((t->t_proc_flag & TP_ZTHREAD) != 0)
cmn_err(CE_PANIC, "thread_exit: zthread_exit() not called");
+ if ((t->t_flag & T_SPLITSTK) != 0)
+ cmn_err(CE_PANIC, "thread_exit: called when stack is split");
+
tsd_exit(); /* Clean up this thread's TSD */
kcpc_passivate(); /* clean up performance counter state */
@@ -2050,6 +2045,103 @@ thread_change_pri(kthread_t *t, pri_t disp_pri, int front)
return (on_rq);
}
+
+/*
+ * There are occasions in the kernel when we need much more stack than we
+ * allocate by default, but we do not wish to have that work done
+ * asynchronously by another thread. To accommodate these scenarios, we allow
+ * for a split stack (also known as a "segmented stack") whereby a new stack
+ * is dynamically allocated and the current thread jumps onto it for purposes
+ * of executing the specified function. After the specified function returns,
+ * the stack is deallocated and control is returned to the caller. This
+ * functionality is implemented by thread_splitstack(), below; there are a few
+ * constraints on its use:
+ *
+ * - The caller must be in a context where it is safe to block for memory.
+ * - The caller cannot be in a t_onfault context
+ * - The called function must not call thread_exit() while on the split stack
+ *
+ * The code will explicitly panic if these constraints are violated. Notably,
+ * however, thread_splitstack() _can_ be called on a split stack -- there
+ * is no limit to the level that split stacks can nest.
+ *
+ * When the stack is split, it is constructed such that stack backtraces
+ * from kernel debuggers continue to function -- though note that DTrace's
+ * stack() action and stackdepth function will only show the stack up to and
+ * including thread_splitstack_run(); DTrace explicitly bounds itself to
+ * pointers that exist within the current declared stack as a safety
+ * mechanism.
+ */
+void
+thread_splitstack(void (*func)(void *), void *arg, size_t stksize)
+{
+ kthread_t *t = curthread;
+ caddr_t ostk, ostkbase, stk;
+ ushort_t otflag;
+
+ if (t->t_onfault != NULL)
+ panic("thread_splitstack: called with non-NULL t_onfault");
+
+ ostk = t->t_stk;
+ ostkbase = t->t_stkbase;
+ otflag = t->t_flag;
+
+ stksize = roundup(stksize, PAGESIZE);
+
+ if (stksize < default_stksize)
+ stksize = default_stksize;
+
+ if (stksize == default_stksize) {
+ stk = (caddr_t)segkp_cache_get(segkp_thread);
+ } else {
+ stksize = roundup(stksize, PAGESIZE);
+ stk = (caddr_t)segkp_get(segkp, stksize,
+ (KPD_HASREDZONE | KPD_NO_ANON | KPD_LOCKED));
+ }
+
+ /*
+ * We're going to lock ourselves before we set T_SPLITSTK to assure
+ * that we're not swapped out in the meantime. (Note that we don't
+ * bother to set t_swap, as we're not going to be swapped out.)
+ */
+ thread_lock(t);
+
+ if (!(otflag & T_SPLITSTK))
+ t->t_flag |= T_SPLITSTK;
+
+ t->t_stk = stk + stksize;
+ t->t_stkbase = stk;
+
+ thread_unlock(t);
+
+ /*
+ * Now actually run on the new (split) stack...
+ */
+ thread_splitstack_run(t->t_stk, func, arg);
+
+ /*
+ * We're back onto our own stack; lock ourselves and restore our
+ * pre-split state.
+ */
+ thread_lock(t);
+
+ t->t_stk = ostk;
+ t->t_stkbase = ostkbase;
+
+ if (!(otflag & T_SPLITSTK))
+ t->t_flag &= ~T_SPLITSTK;
+
+ thread_unlock(t);
+
+ /*
+ * Now that we are entirely back on our own stack, call back into
+ * the platform layer to perform any platform-specific cleanup.
+ */
+ thread_splitstack_cleanup();
+
+ segkp_release(segkp, stk);
+}
+
/*
* Tunable kmem_stackinfo is set, fill the kernel thread stack with a
* specific pattern.
diff --git a/usr/src/uts/common/dtrace/dtrace.c b/usr/src/uts/common/dtrace/dtrace.c
index 054cb43c9b..5ecd546d01 100644
--- a/usr/src/uts/common/dtrace/dtrace.c
+++ b/usr/src/uts/common/dtrace/dtrace.c
@@ -7793,7 +7793,7 @@ dtrace_cred2priv(cred_t *cr, uint32_t *privp, uid_t *uidp, zoneid_t *zoneidp)
priv = DTRACE_PRIV_ALL;
} else {
*uidp = crgetuid(cr);
- *zoneidp = crgetzoneid(cr);
+ *zoneidp = crgetzonedid(cr);
priv = 0;
if (PRIV_POLICY_ONLY(cr, PRIV_DTRACE_KERNEL, B_FALSE))
@@ -8289,7 +8289,7 @@ dtrace_register(const char *name, const dtrace_pattr_t *pap, uint32_t priv,
provider->dtpv_priv.dtpp_flags = priv;
if (cr != NULL) {
provider->dtpv_priv.dtpp_uid = crgetuid(cr);
- provider->dtpv_priv.dtpp_zoneid = crgetzoneid(cr);
+ provider->dtpv_priv.dtpp_zoneid = crgetzonedid(cr);
}
provider->dtpv_pops = *pops;
@@ -8893,6 +8893,7 @@ dtrace_probe_enable(const dtrace_probedesc_t *desc, dtrace_enabling_t *enab)
uint32_t priv;
uid_t uid;
zoneid_t zoneid;
+ dtrace_state_t *state = enab->dten_vstate->dtvs_state;
ASSERT(MUTEX_HELD(&dtrace_lock));
dtrace_ecb_create_cache = NULL;
@@ -8907,8 +8908,22 @@ dtrace_probe_enable(const dtrace_probedesc_t *desc, dtrace_enabling_t *enab)
}
dtrace_probekey(desc, &pkey);
- dtrace_cred2priv(enab->dten_vstate->dtvs_state->dts_cred.dcr_cred,
- &priv, &uid, &zoneid);
+ dtrace_cred2priv(state->dts_cred.dcr_cred, &priv, &uid, &zoneid);
+
+ if ((priv & DTRACE_PRIV_ZONEOWNER) &&
+ state->dts_options[DTRACEOPT_ZONE] != DTRACEOPT_UNSET) {
+ /*
+ * If we have the privilege of instrumenting all zones but we
+ * have been told to instrument but one, we will spoof this up
+ * depriving ourselves of DTRACE_PRIV_ZONEOWNER for purposes
+ * of dtrace_match(). (Note that DTRACEOPT_ZONE is not for
+ * security but rather for performance: it allows the global
+ * zone to instrument USDT probes in a local zone without
+ * requiring all zones to be instrumented.)
+ */
+ priv &= ~DTRACE_PRIV_ZONEOWNER;
+ zoneid = state->dts_options[DTRACEOPT_ZONE];
+ }
return (dtrace_match(&pkey, priv, uid, zoneid, dtrace_ecb_create_enable,
enab));
diff --git a/usr/src/uts/common/dtrace/sdt_subr.c b/usr/src/uts/common/dtrace/sdt_subr.c
index 5112b1dc37..2db9d5447e 100644
--- a/usr/src/uts/common/dtrace/sdt_subr.c
+++ b/usr/src/uts/common/dtrace/sdt_subr.c
@@ -114,6 +114,10 @@ static dtrace_pattr_t smb_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
};
+/*
+ * When adding a new provider you must add it before sdt as sdt is a catch all
+ * for remaining probes.
+ */
sdt_provider_t sdt_providers[] = {
{ "vtrace", "__vtrace_", &vtrace_attr },
{ "sysinfo", "__cpu_sysinfo_", &info_attr, DTRACE_PRIV_USER },
@@ -136,6 +140,7 @@ sdt_provider_t sdt_providers[] = {
{ "fc", "__fc_", &fc_attr },
{ "srp", "__srp_", &fc_attr },
{ "sysevent", "__sysevent_", &stab_attr },
+ { "vnd", "__vnd_", &stab_attr },
{ "sdt", NULL, &sdt_attr },
{ NULL }
};
@@ -1473,6 +1478,34 @@ sdt_argdesc_t sdt_args[] = {
{ "fc", "abts-receive", 2, 2, "fct_i_remote_port_t *",
"fc_port_info_t *" },
+ { "vnd", "flow-blocked", 0, 0, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "flow-blocked", 1, 1, "uint64_t", "uint64_t" },
+ { "vnd", "flow-blocked", 2, 2, "uintptr_t", "uintptr_t" },
+ { "vnd", "flow-resumed", 0, 0, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "flow-resumed", 1, 1, "uint64_t", "uint64_t" },
+ { "vnd", "flow-resumed", 2, 2, "uintptr_t", "uintptr_t" },
+ { "vnd", "drop-in", 0, 0, "mblk_t *", "pktinfo_t *" },
+ { "vnd", "drop-in", 1, 1, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "drop-in", 2, 2, "mblk_t *", "etherinfo_t *" },
+ { "vnd", "drop-in", 3, 3, "const char *", "const char *" },
+ { "vnd", "drop-out", 0, 0, "mblk_t *", "pktinfo_t *" },
+ { "vnd", "drop-out", 1, 1, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "drop-out", 2, 2, "mblk_t *", "etherinfo_t *" },
+ { "vnd", "drop-out", 3, 3, "const char *", "const char *" },
+ { "vnd", "drop-ctl", 0, 0, "mblk_t *", "pktinfo_t *" },
+ { "vnd", "drop-ctl", 1, 1, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "drop-ctl", 2, 2, "mblk_t *", "etherinfo_t *" },
+ { "vnd", "drop-ctl", 3, 3, "const char *", "const char *" },
+ { "vnd", "send", 0, 0, "mblk_t *", "pktinfo_t *" },
+ { "vnd", "send", 1, 1, "void *", "csinfo_t *" },
+ { "vnd", "send", 2, 2, "void *", "ipinfo_t *" },
+ { "vnd", "send", 3, 3, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "send", 4, 4, "mblk_t *", "etherinfo_t *" },
+ { "vnd", "recv", 0, 0, "mblk_t *", "pktinfo_t *" },
+ { "vnd", "recv", 1, 1, "void *", "csinfo_t *" },
+ { "vnd", "recv", 2, 2, "void *", "ipinfo_t *" },
+ { "vnd", "recv", 3, 3, "vnd_str_t *", "ifinfo_t *" },
+ { "vnd", "recv", 4, 4, "mblk_t *", "etherinfo_t *" },
{ NULL }
};
diff --git a/usr/src/uts/common/exec/elf/elf.c b/usr/src/uts/common/exec/elf/elf.c
index 73d302aaa5..53bbd078ba 100644
--- a/usr/src/uts/common/exec/elf/elf.c
+++ b/usr/src/uts/common/exec/elf/elf.c
@@ -26,7 +26,7 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
- * Copyright 2019, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2022 Oxide Computer Company
*/
@@ -94,7 +94,6 @@ static int mapelfexec(vnode_t *, Ehdr *, uint_t, caddr_t, Phdr **, Phdr **,
Phdr **, Phdr **, Phdr *, caddr_t *, caddr_t *, intptr_t *, uintptr_t *,
size_t, size_t *, size_t *);
-
#ifdef _ELF32_COMPAT
/* Link against the non-compat instances when compiling the 32-bit version. */
extern size_t elf_datasz_max;
@@ -181,12 +180,16 @@ elf_ctx_resize_scratch(elf_core_ctx_t *ctx, size_t sz)
#endif /* _ELF32_COMPAT */
/*
- * Map in the executable pointed to by vp. Returns 0 on success.
+ * Map in the executable pointed to by vp. Returns 0 on success. Note that
+ * this function currently has the maximum number of arguments allowed by
+ * modstubs on x86 (MAXNARG)! Do _not_ add to this function signature without
+ * adding to MAXNARG. (Better yet, do not add to this monster of a function
+ * signature!)
*/
int
mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
- intptr_t *voffset, caddr_t exec_file, int *interp, caddr_t *bssbase,
- caddr_t *brkbase, size_t *brksize, uintptr_t *lddatap)
+ intptr_t *voffset, caddr_t exec_file, char **interpp, caddr_t *bssbase,
+ caddr_t *brkbase, size_t *brksize, uintptr_t *lddatap, uintptr_t *minaddrp)
{
size_t len, phdrsize;
struct vattr vat;
@@ -197,12 +200,16 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
Phdr *junk = NULL;
Phdr *dynphdr = NULL;
Phdr *dtrphdr = NULL;
+ char *interp = NULL;
uintptr_t lddata, minaddr;
size_t execsz;
if (lddatap != NULL)
*lddatap = 0;
+ if (minaddrp != NULL)
+ *minaddrp = (uintptr_t)NULL;
+
if (error = execpermissions(vp, &vat, args)) {
uprintf("%s: Cannot execute %s\n", exec_file, args->pathname);
return (error);
@@ -234,24 +241,85 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
return (error);
}
+ if (minaddrp != NULL)
+ *minaddrp = minaddr;
+
/*
- * Inform our caller if the executable needs an interpreter.
+ * If the executable requires an interpreter, determine its name.
*/
- *interp = (dynphdr == NULL) ? 0 : 1;
+ if (dynphdr != NULL) {
+ ssize_t resid;
+
+ if (dynphdr->p_filesz > MAXPATHLEN || dynphdr->p_filesz == 0) {
+ uprintf("%s: Invalid interpreter\n", exec_file);
+ kmem_free(phdrbase, phdrsize);
+ return (ENOEXEC);
+ }
+
+ interp = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ if ((error = vn_rdwr(UIO_READ, vp, interp,
+ (ssize_t)dynphdr->p_filesz,
+ (offset_t)dynphdr->p_offset, UIO_SYSSPACE, 0,
+ (rlim64_t)0, CRED(), &resid)) != 0 || resid != 0 ||
+ interp[dynphdr->p_filesz - 1] != '\0') {
+ uprintf("%s: Cannot obtain interpreter pathname\n",
+ exec_file);
+ kmem_free(interp, MAXPATHLEN);
+ kmem_free(phdrbase, phdrsize);
+ return (error != 0 ? error : ENOEXEC);
+ }
+ }
/*
* If this is a statically linked executable, voffset should indicate
* the address of the executable itself (it normally holds the address
* of the interpreter).
*/
- if (ehdr->e_type == ET_EXEC && *interp == 0)
+ if (ehdr->e_type == ET_EXEC && interp == NULL)
*voffset = minaddr;
+ /*
+ * If the caller has asked for the interpreter name, return it (it's
+ * up to the caller to free it); if the caller hasn't asked for it,
+ * free it ourselves.
+ */
+ if (interpp != NULL) {
+ *interpp = interp;
+ } else if (interp != NULL) {
+ kmem_free(interp, MAXPATHLEN);
+ }
+
if (uphdr != NULL) {
*uphdr_vaddr = uphdr->p_vaddr;
if (uphdr->p_flags == 0)
kmem_free(uphdr, sizeof (Phdr));
+ } else if (ehdr->e_type == ET_DYN) {
+ /*
+ * If we don't have a uphdr, we'll apply the logic found
+ * in mapelfexec() and use the p_vaddr of the first PT_LOAD
+ * section as the base address of the object.
+ */
+ const Phdr *phdr = (Phdr *)phdrbase;
+ const uint_t hsize = ehdr->e_phentsize;
+ uint_t i;
+
+ for (i = nphdrs; i > 0; i--) {
+ if (phdr->p_type == PT_LOAD) {
+ *uphdr_vaddr = (uintptr_t)phdr->p_vaddr +
+ ehdr->e_phoff;
+ break;
+ }
+
+ phdr = (Phdr *)((caddr_t)phdr + hsize);
+ }
+
+ /*
+ * If we don't have a PT_LOAD segment, we should have returned
+ * ENOEXEC when elfsize() returned 0, above.
+ */
+ VERIFY(i > 0);
} else {
*uphdr_vaddr = (Addr)-1;
}
@@ -263,13 +331,13 @@ mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
int
elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
int level, size_t *execsz, int setid, caddr_t exec_file, cred_t *cred,
- int brand_action)
+ int *brand_action)
{
caddr_t phdrbase = NULL;
caddr_t bssbase = 0;
caddr_t brkbase = 0;
size_t brksize = 0;
- size_t dlnsize;
+ size_t dlnsize, nsize = 0;
aux_entry_t *aux;
int error;
ssize_t resid;
@@ -349,7 +417,9 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
*execsz = btopr(SINCR) + btopr(SSIZE) + btopr(NCARGS32-1);
} else {
args->to_model = DATAMODEL_LP64;
- args->stk_prot &= ~PROT_EXEC;
+ if (!args->stk_prot_override) {
+ args->stk_prot &= ~PROT_EXEC;
+ }
#if defined(__x86)
args->dat_prot &= ~PROT_EXEC;
#endif
@@ -361,11 +431,25 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
#endif /* _LP64 */
/*
- * We delay invoking the brand callback until we've figured out
- * what kind of elf binary we're trying to run, 32-bit or 64-bit.
- * We do this because now the brand library can just check
- * args->to_model to see if the target is 32-bit or 64-bit without
- * having do duplicate all the code above.
+ * We delay invoking the brand callback until we've figured out what
+ * kind of elf binary we're trying to run, 32-bit or 64-bit. We do this
+ * because now the brand library can just check args->to_model to see if
+ * the target is 32-bit or 64-bit without having do duplicate all the
+ * code above.
+ *
+ * We also give the brand a chance to indicate that based on the ELF
+ * OSABI of the target binary it should become unbranded and optionally
+ * indicate that it should be treated as existing in a specific prefix.
+ *
+ * Note that if a brand opts to go down this route it does not actually
+ * end up being debranded. In other words, future programs that exec
+ * will still be considered for branding unless this escape hatch is
+ * used. Consider the case of lx brand for example. If a user runs
+ * /native/usr/sbin/dtrace -c /bin/ls, the isaexec and normal executable
+ * of DTrace that's in /native will take this escape hatch and be run
+ * and interpreted using the normal system call table; however, the
+ * execution of a non-illumos binary in the form of /bin/ls will still
+ * be branded and be subject to all of the normal actions of the brand.
*
* The level checks associated with brand handling below are used to
* prevent a loop since the brand elfexec function typically comes back
@@ -373,8 +457,20 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
* handling in the #! interpreter code will increment the level before
* calling gexec to run the final elfexec interpreter.
*/
+ if ((level <= INTP_MAXDEPTH) && (*brand_action != EBA_NATIVE) &&
+ (PROC_IS_BRANDED(p)) && (BROP(p)->b_native_exec != NULL)) {
+ if (BROP(p)->b_native_exec(ehdrp->e_ident[EI_OSABI],
+ &args->brand_nroot) == B_TRUE) {
+ ASSERT(ehdrp->e_ident[EI_OSABI]);
+ *brand_action = EBA_NATIVE;
+ /* Add one for the trailing '/' in the path */
+ if (args->brand_nroot != NULL)
+ nsize = strlen(args->brand_nroot) + 1;
+ }
+ }
+
if ((level <= INTP_MAXDEPTH) &&
- (brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
+ (*brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
error = BROP(p)->b_elfexec(vp, uap, args,
idatap, level + 1, execsz, setid, exec_file, cred,
brand_action);
@@ -448,6 +544,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
* AT_BASE
* AT_FLAGS
* AT_PAGESZ
+ * AT_RANDOM (added in stk_copyout)
* AT_SUN_AUXFLAGS
* AT_SUN_HWCAP
* AT_SUN_HWCAP2
@@ -456,7 +553,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
* AT_SUN_EXECNAME (added in stk_copyout)
* AT_NULL
*
- * total == 10
+ * total == 11
*/
if (hasintp && hasu) {
/*
@@ -471,7 +568,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
*
* total = 5
*/
- args->auxsize = (10 + 5) * sizeof (aux_entry_t);
+ args->auxsize = (11 + 5) * sizeof (aux_entry_t);
} else if (hasintp) {
/*
* Has PT_INTERP but no PT_PHDR
@@ -481,9 +578,9 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
*
* total = 2
*/
- args->auxsize = (10 + 2) * sizeof (aux_entry_t);
+ args->auxsize = (11 + 2) * sizeof (aux_entry_t);
} else {
- args->auxsize = 10 * sizeof (aux_entry_t);
+ args->auxsize = 11 * sizeof (aux_entry_t);
}
} else {
args->auxsize = 0;
@@ -497,6 +594,15 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
args->auxsize += sizeof (aux_entry_t);
/*
+ * If this is a native binary that's been given a modified interpreter
+ * root, inform it that the native system exists at that root.
+ */
+ if (args->brand_nroot != NULL) {
+ args->auxsize += sizeof (aux_entry_t);
+ }
+
+
+ /*
* On supported kernels (x86_64) make room in the auxv for the
* AT_SUN_COMMPAGE entry. This will go unpopulated on i86xpv systems
* which do not provide such functionality.
@@ -508,13 +614,24 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
args->auxsize += 3 * sizeof (aux_entry_t);
#endif /* defined(__amd64) */
- if ((brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
+ /*
+ * If we have user credentials, we'll supply the following entries:
+ * AT_SUN_UID
+ * AT_SUN_RUID
+ * AT_SUN_GID
+ * AT_SUN_RGID
+ */
+ if (cred != NULL) {
+ args->auxsize += 4 * sizeof (aux_entry_t);
+ }
+
+ if ((*brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
branded = 1;
/*
- * We will be adding 4 entries to the aux vectors. One for
- * the the brandname and 3 for the brand specific aux vectors.
+ * We will be adding 5 entries to the aux vectors. One for
+ * the brandname and 4 for the brand specific aux vectors.
*/
- args->auxsize += 4 * sizeof (aux_entry_t);
+ args->auxsize += 5 * sizeof (aux_entry_t);
}
/* If the binary has an explicit ASLR flag, it must be honoured */
@@ -595,7 +712,8 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
aux = bigwad->elfargs;
/*
* Move args to the user's stack.
- * This can fill in the AT_SUN_PLATFORM and AT_SUN_EXECNAME aux entries.
+ * This can fill in the AT_SUN_PLATFORM, AT_SUN_EXECNAME and AT_RANDOM
+ * aux entries.
*/
if ((error = exec_args(uap, args, idatap, (void **)&aux)) != 0) {
if (error == -1) {
@@ -645,7 +763,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
char *p;
struct vnode *nvp;
- dlnsize = intphdr->p_filesz;
+ dlnsize = intphdr->p_filesz + nsize;
/*
* Make sure none of the component pieces of dlnsize result in
@@ -656,10 +774,15 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
goto bad;
}
+ if (nsize != 0) {
+ bcopy(args->brand_nroot, dlnp, nsize - 1);
+ dlnp[nsize - 1] = '/';
+ }
+
/*
* Read in "interpreter" pathname.
*/
- if ((error = vn_rdwr(UIO_READ, vp, dlnp,
+ if ((error = vn_rdwr(UIO_READ, vp, dlnp + nsize,
(ssize_t)intphdr->p_filesz, (offset_t)intphdr->p_offset,
UIO_SYSSPACE, 0, (rlim64_t)0, CRED(), &resid)) != 0) {
uprintf("%s: Cannot obtain interpreter pathname\n",
@@ -842,8 +965,8 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
#endif /* defined(__amd64) */
/*
- * Note: AT_SUN_PLATFORM and AT_SUN_EXECNAME were filled in via
- * exec_args()
+ * Note: AT_SUN_PLATFORM, AT_SUN_EXECNAME and AT_RANDOM were
+ * filled in via exec_args()
*/
ADDAUX(aux, AT_BASE, voffset)
ADDAUX(aux, AT_FLAGS, at_flags)
@@ -871,7 +994,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
* malicious user within the zone from crafting a wrapper to
* run native suid commands with unsecure libraries interposed.
*/
- if ((brand_action == EBA_NATIVE) && (PROC_IS_BRANDED(p) &&
+ if ((*brand_action == EBA_NATIVE) && (PROC_IS_BRANDED(p) &&
(setid &= ~EXECSETID_SETID) != 0))
auxf &= ~AF_SUN_SETUGID;
@@ -886,6 +1009,17 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
ADDAUX(aux, AT_SUN_AUXFLAGS, auxf);
/*
+ * Record information about the real and effective user and
+ * group IDs.
+ */
+ if (cred != NULL) {
+ ADDAUX(aux, AT_SUN_UID, crgetuid(cred));
+ ADDAUX(aux, AT_SUN_RUID, crgetruid(cred));
+ ADDAUX(aux, AT_SUN_GID, crgetgid(cred));
+ ADDAUX(aux, AT_SUN_RGID, crgetrgid(cred));
+ }
+
+ /*
* Hardware capability flag word (performance hints)
* Used for choosing faster library routines.
* (Potentially different between 32-bit and 64-bit ABIs)
@@ -912,6 +1046,7 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
ADDAUX(aux, AT_SUN_BRAND_AUX1, 0)
ADDAUX(aux, AT_SUN_BRAND_AUX2, 0)
ADDAUX(aux, AT_SUN_BRAND_AUX3, 0)
+ ADDAUX(aux, AT_SUN_BRAND_AUX4, 0)
}
/*
@@ -1119,10 +1254,10 @@ getelfhead(vnode_t *vp, cred_t *credp, Ehdr *ehdr, uint_t *nshdrs,
* We got here by the first two bytes in ident,
* now read the entire ELF header.
*/
- if ((error = vn_rdwr(UIO_READ, vp, (caddr_t)ehdr,
- sizeof (Ehdr), (offset_t)0, UIO_SYSSPACE, 0,
- (rlim64_t)0, credp, &resid)) != 0)
+ if ((error = vn_rdwr(UIO_READ, vp, (caddr_t)ehdr, sizeof (Ehdr),
+ (offset_t)0, UIO_SYSSPACE, 0, (rlim64_t)0, credp, &resid)) != 0) {
return (error);
+ }
/*
* Since a separate version is compiled for handling 32-bit and
@@ -1131,8 +1266,9 @@ getelfhead(vnode_t *vp, cred_t *credp, Ehdr *ehdr, uint_t *nshdrs,
*/
if (resid != 0 ||
ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
- ehdr->e_ident[EI_MAG3] != ELFMAG3)
+ ehdr->e_ident[EI_MAG3] != ELFMAG3) {
return (ENOEXEC);
+ }
if ((ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) ||
#if defined(_ILP32) || defined(_ELF32_COMPAT)
@@ -1141,8 +1277,9 @@ getelfhead(vnode_t *vp, cred_t *credp, Ehdr *ehdr, uint_t *nshdrs,
ehdr->e_ident[EI_CLASS] != ELFCLASS64 ||
#endif
!elfheadcheck(ehdr->e_ident[EI_DATA], ehdr->e_machine,
- ehdr->e_flags))
+ ehdr->e_flags)) {
return (EINVAL);
+ }
*nshdrs = ehdr->e_shnum;
*shstrndx = ehdr->e_shstrndx;
@@ -1162,9 +1299,8 @@ getelfhead(vnode_t *vp, cred_t *credp, Ehdr *ehdr, uint_t *nshdrs,
if ((error = vn_rdwr(UIO_READ, vp, (caddr_t)&shdr,
sizeof (shdr), (offset_t)ehdr->e_shoff, UIO_SYSSPACE, 0,
- (rlim64_t)0, credp, NULL)) != 0) {
+ (rlim64_t)0, credp, NULL)) != 0)
return (error);
- }
if (*nshdrs == 0)
*nshdrs = shdr.sh_size;
@@ -1335,7 +1471,7 @@ mapelfexec(
size_t *brksize)
{
Phdr *phdr;
- int error, page, prot;
+ int error, page, prot, lastprot = 0;
caddr_t addr = NULL;
caddr_t minaddr = (caddr_t)UINTPTR_MAX;
uint_t i;
@@ -1343,9 +1479,11 @@ mapelfexec(
boolean_t ptload = B_FALSE;
off_t offset;
const uint_t hsize = ehdr->e_phentsize;
+ uintptr_t lastaddr = 0;
extern int use_brk_lpg;
if (ehdr->e_type == ET_DYN) {
+ caddr_t vaddr;
secflagset_t flags = 0;
/*
* Obtain the virtual address of a hole in the
@@ -1357,23 +1495,65 @@ mapelfexec(
map_addr(&addr, len, (offset_t)0, 1, flags);
if (addr == NULL)
return (ENOMEM);
- *voffset = (intptr_t)addr;
/*
- * Calculate the minimum vaddr so it can be subtracted out.
- * According to the ELF specification, since PT_LOAD sections
- * must be sorted by increasing p_vaddr values, this is
- * guaranteed to be the first PT_LOAD section.
+ * Despite the fact that mmapobj(2) refuses to load them, we
+ * need to support executing ET_DYN objects that have a
+ * non-NULL p_vaddr. When found in the wild, these objects
+ * are likely to be due to an old (and largely obviated) Linux
+ * facility, prelink(8), that rewrites shared objects to
+ * prefer specific (disjoint) virtual address ranges. (Yes,
+ * this is putatively for performance -- and yes, it has
+ * limited applicability, many edge conditions and grisly
+ * failure modes; even for Linux, it's insane.) As ELF
+ * mandates that the PT_LOAD segments be in p_vaddr order, we
+ * find the lowest p_vaddr by finding the first PT_LOAD
+ * segment.
*/
phdr = (Phdr *)phdrbase;
for (i = nphdrs; i > 0; i--) {
if (phdr->p_type == PT_LOAD) {
- *voffset -= (uintptr_t)phdr->p_vaddr;
+ addr = (caddr_t)(uintptr_t)phdr->p_vaddr;
break;
}
phdr = (Phdr *)((caddr_t)phdr + hsize);
}
+ /*
+ * We have a non-zero p_vaddr in the first PT_LOAD segment --
+ * presumably because we're directly executing a prelink(8)'d
+ * ld-linux.so. While we could correctly execute such an
+ * object without locating it at its desired p_vaddr (it is,
+ * after all, still relocatable), our inner antiquarian
+ * derives a perverse pleasure in accommodating the steampunk
+ * prelink(8) contraption -- goggles on!
+ */
+ if ((vaddr = addr) != NULL) {
+ if (as_gap(curproc->p_as, len, &addr, &len,
+ AH_LO, NULL) == -1 || addr != vaddr) {
+ addr = NULL;
+ }
+ }
+
+ if (addr == NULL) {
+ /*
+ * We either have a NULL p_vaddr (the common case, by
+ * many orders of magnitude) or we have a non-NULL
+ * p_vaddr and we were unable to obtain the specified
+ * VA range (presumably because it's an illegal
+ * address). Either way, obtain an address in which
+ * to map the interpreter.
+ */
+ map_addr(&addr, len, (offset_t)0, 1, 0);
+ if (addr == NULL)
+ return (ENOMEM);
+ }
+
+ /*
+ * Our voffset is the difference between where we landed and
+ * where we wanted to be.
+ */
+ *voffset = (uintptr_t)addr - (uintptr_t)vaddr;
} else {
*voffset = 0;
}
@@ -1437,6 +1617,41 @@ mapelfexec(
if (addr < minaddr)
minaddr = addr;
+ /*
+ * Segments need not correspond to page boundaries:
+ * they are permitted to share a page. If two PT_LOAD
+ * segments share the same page, and the permissions
+ * of the segments differ, the behavior is historically
+ * that the permissions of the latter segment are used
+ * for the page that the two segments share. This is
+ * also historically a non-issue: binaries generated
+ * by most anything will make sure that two PT_LOAD
+ * segments with differing permissions don't actually
+ * share any pages. However, there exist some crazy
+ * things out there (including at least an obscure
+ * Portuguese teaching language called G-Portugol) that
+ * actually do the wrong thing and expect it to work:
+ * they have a segment with execute permission share
+ * a page with a subsequent segment that does not
+ * have execute permissions and expect the resulting
+ * shared page to in fact be executable. To accommodate
+ * such broken link editors, we take advantage of a
+ * latitude explicitly granted to the loader: it is
+ * permitted to make _any_ PT_LOAD segment executable
+ * (provided that it is readable or writable). If we
+ * see that we're sharing a page and that the previous
+ * page was executable, we will add execute permissions
+ * to our segment.
+ */
+ if (btop(lastaddr) == btop((uintptr_t)addr) &&
+ (phdr->p_flags & (PF_R | PF_W)) &&
+ (lastprot & PROT_EXEC)) {
+ prot |= PROT_EXEC;
+ }
+
+ lastaddr = (uintptr_t)addr + phdr->p_filesz;
+ lastprot = prot;
+
zfodsz = (size_t)phdr->p_memsz - phdr->p_filesz;
offset = phdr->p_offset;
@@ -1521,8 +1736,22 @@ mapelfexec(
break;
case PT_INTERP:
- if (ptload)
- goto bad;
+ /*
+ * The ELF specification is unequivocal about the
+ * PT_INTERP program header with respect to any PT_LOAD
+ * program header: "If it is present, it must precede
+ * any loadable segment entry." Linux, however, makes
+ * no attempt to enforce this -- which has allowed some
+ * binary editing tools to get away with generating
+ * invalid ELF binaries in the respect that PT_INTERP
+ * occurs after the first PT_LOAD program header. This
+ * is unfortunate (and of course, disappointing) but
+ * it's no worse than that: there is no reason that we
+ * can't process the PT_INTERP entry (if present) after
+ * one or more PT_LOAD entries. We therefore
+ * deliberately do not check ptload here and always
+ * store dyphdr to be the PT_INTERP program header.
+ */
*intphdr = phdr;
break;
@@ -1629,6 +1858,7 @@ elfnote(vnode_t *vp, offset_t *offsetp, int type, int descsz, void *desc,
return (0);
}
+
/*
* Copy the section data from one vnode to the section of another vnode.
*/
@@ -1676,28 +1906,38 @@ elf_copy_scn(elf_core_ctx_t *ctx, const Shdr *src, vnode_t *src_vp, Shdr *dst)
}
/*
+ * The design of this check is intentional.
+ * In particular, we want to capture any sections that begin with '.debug_' for
+ * a few reasons:
+ *
+ * 1) Various revisions to the DWARF spec end up changing the set of section
+ * headers that exist. This ensures that we don't need to change the kernel
+ * to get a new version.
+ *
+ * 2) Other software uses .debug_ sections for things which aren't DWARF. This
+ * allows them to be captured as well.
+ */
+#define IS_DEBUGSECTION(name) (strncmp(name, ".debug_", strlen(".debug_")) == 0)
+
+/*
* Walk sections for a given ELF object, counting (or copying) those of
* interest (CTF, symtab, strtab, .debug_*).
*/
-static int
+static uint_t
elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr,
- Shdr *v, uint_t idx, uint_t remain, shstrtab_t *shstrtab, uint_t *countp)
+ Shdr *v, uint_t idx, uint_t remain, shstrtab_t *shstrtab, int *errp)
{
Ehdr ehdr;
const core_content_t content = ctx->ecc_content;
cred_t *credp = ctx->ecc_credp;
Shdr *ctf = NULL, *symtab = NULL, *strtab = NULL;
uintptr_t off = 0;
- uint_t nshdrs, shstrndx, nphdrs, count = 0;
+ uint_t nshdrs, shstrndx, nphdrs, ndebug, count = 0;
u_offset_t *doffp = &ctx->ecc_doffset;
boolean_t ctf_link = B_FALSE;
caddr_t shbase;
size_t shsize, shstrsize;
char *shstrbase;
- int error = 0;
- const boolean_t justcounting = v == NULL;
-
- *countp = 0;
if ((content &
(CC_CONTENT_CTF | CC_CONTENT_SYMTAB | CC_CONTENT_DEBUG)) == 0) {
@@ -1712,6 +1952,7 @@ elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr,
/* Starting at index 1 skips SHT_NULL which is expected at index 0 */
off = ehdr.e_shentsize;
+ ndebug = 0;
for (uint_t i = 1; i < nshdrs; i++, off += ehdr.e_shentsize) {
Shdr *shdr, *symchk = NULL, *strchk;
const char *name;
@@ -1739,51 +1980,8 @@ elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr,
strcmp(name, shstrtab_data[STR_SYMTAB]) == 0) {
symchk = shdr;
} else if ((content & CC_CONTENT_DEBUG) != 0 &&
- strncmp(name, ".debug_", strlen(".debug_")) == 0) {
- /*
- * The design of the above check is intentional. In
- * particular, we want to capture any sections that
- * begin with '.debug_' for a few reasons:
- *
- * 1) Various revisions to the DWARF spec end up
- * changing the set of section headers that exist. This
- * ensures that we don't need to change the kernel to
- * get a new version.
- *
- * 2) Other software uses .debug_ sections for things
- * which aren't DWARF. This allows them to be captured
- * as well.
- */
- count++;
-
- if (!justcounting) {
- if (count > remain) {
- error = ENOMEM;
- goto done;
- }
-
- elf_ctx_resize_scratch(ctx, shdr->sh_size);
-
- if (!shstrtab_ndx(shstrtab,
- name, &v[idx].sh_name)) {
- error = ENOMEM;
- goto done;
- }
-
- v[idx].sh_addr = (Addr)(uintptr_t)saddr;
- v[idx].sh_type = shdr->sh_type;
- v[idx].sh_addralign = shdr->sh_addralign;
- *doffp = roundup(*doffp, v[idx].sh_addralign);
- v[idx].sh_offset = *doffp;
- v[idx].sh_size = shdr->sh_size;
- v[idx].sh_link = 0;
- v[idx].sh_entsize = shdr->sh_entsize;
- v[idx].sh_info = shdr->sh_info;
-
- elf_copy_scn(ctx, shdr, mvp, &v[idx]);
- idx++;
- }
-
+ IS_DEBUGSECTION(name)) {
+ ndebug++;
continue;
} else {
continue;
@@ -1815,24 +2013,19 @@ elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr,
count += 1;
if (symtab != NULL)
count += 2;
-
- if (count > remain) {
- count = remain;
- if (!justcounting)
- error = ENOMEM;
+ count += ndebug;
+ if (v == NULL || count == 0 || count > remain) {
+ count = MIN(count, remain);
goto done;
}
- if (justcounting)
- goto done;
-
/* output CTF section */
if (ctf != NULL) {
elf_ctx_resize_scratch(ctx, ctf->sh_size);
if (!shstrtab_ndx(shstrtab,
shstrtab_data[STR_CTF], &v[idx].sh_name)) {
- error = ENOMEM;
+ *errp = ENOMEM;
goto done;
}
v[idx].sh_addr = (Addr)(uintptr_t)saddr;
@@ -1875,12 +2068,12 @@ elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr,
if (!shstrtab_ndx(shstrtab,
shstrtab_data[symtab_type], &symtab_name)) {
- error = ENOMEM;
+ *errp = ENOMEM;
goto done;
}
if (!shstrtab_ndx(shstrtab,
shstrtab_data[strtab_type], &strtab_name)) {
- error = ENOMEM;
+ *errp = ENOMEM;
goto done;
}
@@ -1915,14 +2108,52 @@ elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr,
idx++;
}
+ if (ndebug == 0)
+ goto done;
+
+ /* output DEBUG sections */
+ off = 0;
+ for (uint_t i = 1; i < nshdrs; i++, off += ehdr.e_shentsize) {
+ const char *name;
+ Shdr *shdr;
+
+ shdr = (Shdr *)(shbase + off);
+ if (shdr->sh_name >= shstrsize || shdr->sh_type == SHT_NULL)
+ continue;
+
+ name = shstrbase + shdr->sh_name;
+
+ if (!IS_DEBUGSECTION(name))
+ continue;
+
+ elf_ctx_resize_scratch(ctx, shdr->sh_size);
+
+ if (!shstrtab_ndx(shstrtab, name, &v[idx].sh_name)) {
+ *errp = ENOMEM;
+ goto done;
+ }
+
+ v[idx].sh_addr = (Addr)(uintptr_t)saddr;
+ v[idx].sh_type = shdr->sh_type;
+ v[idx].sh_addralign = shdr->sh_addralign;
+ *doffp = roundup(*doffp, v[idx].sh_addralign);
+ v[idx].sh_offset = *doffp;
+ v[idx].sh_size = shdr->sh_size;
+ v[idx].sh_link = 0;
+ v[idx].sh_entsize = shdr->sh_entsize;
+ v[idx].sh_info = shdr->sh_info;
+
+ elf_copy_scn(ctx, shdr, mvp, &v[idx]);
+ idx++;
+
+ if (--ndebug == 0)
+ break;
+ }
+
done:
kmem_free(shstrbase, shstrsize);
kmem_free(shbase, shsize);
-
- if (error == 0)
- *countp = count;
-
- return (error);
+ return (count);
}
/*
@@ -1979,8 +2210,9 @@ elf_process_scns(elf_core_ctx_t *ctx, Shdr *v, uint_t nv, uint_t *nshdrsp)
if (seg->s_ops != &segvn_ops ||
SEGOP_GETVP(seg, seg->s_base, &mvp) != 0 ||
mvp == lastvp || mvp == NULL || mvp->v_type != VREG ||
- (segsize = pr_getsegsize(seg, 1)) == 0)
+ (segsize = pr_getsegsize(seg, 1)) == 0) {
continue;
+ }
eaddr = saddr + segsize;
prot = pr_getprot(seg, 1, &tmp, &saddr, &naddr, eaddr);
@@ -1993,8 +2225,8 @@ elf_process_scns(elf_core_ctx_t *ctx, Shdr *v, uint_t nv, uint_t *nshdrsp)
if ((prot & (PROT_WRITE | PROT_EXEC)) != PROT_EXEC)
continue;
- error = elf_process_obj_scns(ctx, mvp, saddr, v, idx, remain,
- &shstrtab, &count);
+ count = elf_process_obj_scns(ctx, mvp, saddr, v, idx, remain,
+ &shstrtab, &error);
if (error != 0)
goto done;
@@ -2106,8 +2338,9 @@ top:
* we overflow the 16 bits allotted to the program header count in
* the ELF header, we'll need that program header at index zero.
*/
- if (nshdrs == 0 && nphdrs >= PN_XNUM)
+ if (nshdrs == 0 && nphdrs >= PN_XNUM) {
nshdrs = 1;
+ }
/*
* Allocate a buffer which is sized adequately to hold the ehdr, phdrs
@@ -2556,7 +2789,7 @@ static struct modlexec modlexec = {
extern int elf32exec(vnode_t *vp, execa_t *uap, uarg_t *args,
intpdata_t *idatap, int level, size_t *execsz,
int setid, caddr_t exec_file, cred_t *cred,
- int brand_action);
+ int *brand_action);
extern int elf32core(vnode_t *vp, proc_t *p, cred_t *credp,
rlim64_t rlimit, int sig, core_content_t content);
diff --git a/usr/src/uts/common/exec/elf/elf_notes.c b/usr/src/uts/common/exec/elf/elf_notes.c
index 78305cc076..0a0d405eba 100644
--- a/usr/src/uts/common/exec/elf/elf_notes.c
+++ b/usr/src/uts/common/exec/elf/elf_notes.c
@@ -347,11 +347,13 @@ write_elfnotes(proc_t *p, int sig, vnode_t *vp, offset_t offset,
/* open file table */
+ mutex_enter(&p->p_lock);
vroot = PTOU(p)->u_rdir;
if (vroot == NULL)
vroot = rootdir;
VN_HOLD(vroot);
+ mutex_exit(&p->p_lock);
fip = P_FINFO(p);
diff --git a/usr/src/uts/common/exec/intp/intp.c b/usr/src/uts/common/exec/intp/intp.c
index 935bad0a8c..95583a57ce 100644
--- a/usr/src/uts/common/exec/intp/intp.c
+++ b/usr/src/uts/common/exec/intp/intp.c
@@ -48,6 +48,7 @@
#include <sys/kmem.h>
#include <sys/note.h>
#include <sys/sdt.h>
+#include <sys/brand.h>
/*
* This is the loadable module wrapper.
@@ -55,7 +56,7 @@
#include <sys/modctl.h>
extern int intpexec(struct vnode *, struct execa *, struct uarg *,
- struct intpdata *, int, size_t *, int, caddr_t, struct cred *, int);
+ struct intpdata *, int, size_t *, int, caddr_t, struct cred *, int *);
static struct execsw esw = {
intpmagicstr,
@@ -127,13 +128,20 @@ getintphead(struct vnode *vp, struct intpdata *idatap)
*cp = '\0';
/*
- * Locate the beginning and end of the interpreter name.
- * In addition to the name, one additional argument may
- * optionally be included here, to be prepended to the
- * arguments provided on the command line. Thus, for
- * example, you can say
+ * Locate the beginning and end of the interpreter name. Historically,
+ * for illumos and its predecessors, in addition to the name, one
+ * additional argument may optionally be included here, to be prepended
+ * to the arguments provided on the command line. Thus, for example,
+ * you can say
*
- * #! /usr/bin/awk -f
+ * #! /usr/bin/awk -f
+ *
+ * However, handling of interpreter arguments varies across operating
+ * systems and other systems allow more than one argument. In
+ * particular, Linux allows more than one and delivers all arguments
+ * as a single string (argv[1] is "-arg1 -arg2 ..."). We support this
+ * style of argument handling as a brand-specific option (setting
+ * b_intp_parse_arg to B_FALSE).
*/
for (cp = &linep[2]; *cp == ' '; cp++)
;
@@ -152,9 +160,12 @@ getintphead(struct vnode *vp, struct intpdata *idatap)
idatap->intp_arg[0] = NULL;
else {
idatap->intp_arg[0] = cp;
- while (*cp && *cp != ' ')
- cp++;
- *cp = '\0';
+ if (!PROC_IS_BRANDED(curproc) ||
+ BROP(curproc)->b_intp_parse_arg) {
+ while (*cp && *cp != ' ')
+ cp++;
+ *cp = '\0';
+ }
}
}
return (0);
@@ -189,9 +200,8 @@ intpexec(
int setid,
caddr_t exec_file,
struct cred *cred,
- int brand_action)
+ int *brand_action)
{
- _NOTE(ARGUNUSED(brand_action))
vnode_t *nvp;
int error = 0;
struct intpdata idata;
@@ -282,7 +292,7 @@ intpexec(
}
error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred,
- EBA_NONE);
+ brand_action);
if (!error) {
/*
diff --git a/usr/src/uts/common/exec/java/java.c b/usr/src/uts/common/exec/java/java.c
index 9b38dec5a0..7e9029fa55 100644
--- a/usr/src/uts/common/exec/java/java.c
+++ b/usr/src/uts/common/exec/java/java.c
@@ -86,10 +86,10 @@ char *jexec_arg = "-jar";
static int
javaexec(vnode_t *vp, struct execa *uap, struct uarg *args,
struct intpdata *idatap, int level, size_t *execsz, int setid,
- caddr_t execfile, cred_t *cred, int brand_action)
+ caddr_t execfile, cred_t *cred, int *brand_action)
{
struct intpdata idata;
- int error;
+ int error, eba;
ssize_t resid;
vnode_t *nvp;
off_t xoff, xoff_end;
@@ -161,8 +161,9 @@ javaexec(vnode_t *vp, struct execa *uap, struct uarg *args,
args->pathname = resolvepn.pn_path;
/* don't free resolvepn until we are done with args */
pn_free(&lookpn);
- error = gexec(&nvp, uap, args, &idata, level + 1, execsz, execfile,
- cred, EBA_NONE);
+ eba = EBA_NONE;
+ error = gexec(&nvp, uap, args, &idata, level + 1, execsz,
+ execfile, cred, &eba);
if (!error) {
/*
diff --git a/usr/src/uts/common/exec/shbin/shbin.c b/usr/src/uts/common/exec/shbin/shbin.c
index 25a88b05c1..c1f69ed4b1 100644
--- a/usr/src/uts/common/exec/shbin/shbin.c
+++ b/usr/src/uts/common/exec/shbin/shbin.c
@@ -59,7 +59,7 @@ shbinexec(
int setid,
caddr_t exec_file,
struct cred *cred,
- int brand_action);
+ int *brand_action);
#define SHBIN_CNTL(x) ((x)&037)
#define SHBINMAGIC_LEN 4
@@ -161,11 +161,11 @@ shbinexec(
int setid,
caddr_t exec_file,
struct cred *cred,
- int brand_action)
+ int *brand_action)
{
_NOTE(ARGUNUSED(brand_action))
vnode_t *nvp;
- int error = 0;
+ int error = 0, eba;
struct intpdata idata;
struct pathname intppn;
struct pathname resolvepn;
@@ -245,8 +245,9 @@ shbinexec(
args->fname = devfd;
}
+ eba = EBA_NONE;
error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred,
- EBA_NONE);
+ &eba);
if (!error) {
/*
diff --git a/usr/src/uts/common/fs/dev/sdev_netops.c b/usr/src/uts/common/fs/dev/sdev_netops.c
index a426eeaf10..ce08e3697b 100644
--- a/usr/src/uts/common/fs/dev/sdev_netops.c
+++ b/usr/src/uts/common/fs/dev/sdev_netops.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright (c) 2018, Joyent, Inc. All rights reserved.
*/
/*
@@ -41,8 +42,102 @@
#include <sys/zone.h>
#include <sys/dls.h>
+static const char *devnet_zpath = "/dev/net/zone/";
struct vnodeops *devnet_vnodeops;
+static zoneid_t
+devnet_nodetozone(sdev_node_t *dv)
+{
+ char *zname = NULL, *dup;
+ zone_t *zone;
+ int duplen;
+ zoneid_t zid;
+
+ /*
+ * If in a non-global zone, always return it's zid no matter what the
+ * node is.
+ */
+ zid = getzoneid();
+ if (zid != GLOBAL_ZONEID)
+ return (zid);
+
+ /*
+ * If it doesn't have /dev/net/zone/ then it can't be a specific zone
+ * we're targetting.
+ */
+ if (strncmp(devnet_zpath, dv->sdev_path, strlen(devnet_zpath)) != 0)
+ return (GLOBAL_ZONEID);
+
+ if (dv->sdev_vnode->v_type == VDIR) {
+ zone = zone_find_by_name(dv->sdev_name);
+ } else {
+ /* Non directories have the form /dev/net/zone/%z/%s */
+ dup = strdup(dv->sdev_path);
+ duplen = strlen(dup);
+ zname = strrchr(dup, '/');
+ *zname = '\0';
+ zname--;
+ zname = strrchr(dup, '/');
+ zname++;
+ zone = zone_find_by_name(zname);
+ kmem_free(dup, duplen + 1);
+ }
+ if (zone == NULL)
+ return (GLOBAL_ZONEID);
+ zid = zone->zone_id;
+ zone_rele(zone);
+ return (zid);
+}
+
+static int
+devnet_mkdir(struct sdev_node *ddv, char *name)
+{
+ sdev_node_t *dv;
+ struct vattr va;
+ int ret;
+
+ ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+ dv = sdev_cache_lookup(ddv, name);
+ if (dv != NULL) {
+ SDEV_SIMPLE_RELE(dv);
+ return (EEXIST);
+ }
+
+ va = *sdev_getdefault_attr(VDIR);
+ gethrestime(&va.va_atime);
+ va.va_mtime = va.va_atime;
+ va.va_ctime = va.va_atime;
+
+ ret = sdev_mknode(ddv, name, &dv, &va, NULL, NULL, kcred, SDEV_READY);
+ if (ret != 0)
+ return (ret);
+ SDEV_SIMPLE_RELE(dv);
+ return (0);
+}
+
+/*
+ * We basically need to walk down the directory path to determine what we should
+ * do. At the top level of /dev/net, only the directory /dev/net/zone is valid,
+ * and it is always valid. Following on that, /dev/net/zone/%zonename is valid
+ * if and only if we can look up that zone name. If it's not, or it's some other
+ * name, then it's SDEV_VTOR_INVALID.
+ */
+static int
+devnet_dirvalidate(struct sdev_node *dv)
+{
+ zone_t *zonep;
+ char *path = "/dev/net/zone";
+
+ if (strcmp(path, dv->sdev_path) == 0)
+ return (SDEV_VTOR_VALID);
+
+ zonep = zone_find_by_name(dv->sdev_name);
+ if (zonep == NULL)
+ return (SDEV_VTOR_INVALID);
+ zone_rele(zonep);
+ return (SDEV_VTOR_VALID);
+}
+
/*
* Check if a net sdev_node is still valid - i.e. it represents a current
* network link.
@@ -60,11 +155,20 @@ devnet_validate(struct sdev_node *dv)
ASSERT(dv->sdev_state == SDEV_READY);
- if (dls_mgmt_get_linkid(dv->sdev_name, &linkid) != 0)
+ if (dv->sdev_vnode->v_type == VDIR)
+ return (devnet_dirvalidate(dv));
+
+ if (strncmp(devnet_zpath, dv->sdev_path, strlen(devnet_zpath)) == 0) {
+ ASSERT(SDEV_IS_GLOBAL(dv));
+ zoneid = devnet_nodetozone(dv);
+ } else {
+ zoneid = getzoneid();
+ }
+
+ if (dls_mgmt_get_linkid_in_zone(dv->sdev_name, &linkid, zoneid) != 0)
return (SDEV_VTOR_INVALID);
- if (SDEV_IS_GLOBAL(dv))
+ if (zoneid == GLOBAL_ZONEID)
return (SDEV_VTOR_VALID);
- zoneid = getzoneid();
return (zone_check_datalink(&zoneid, linkid) == 0 ?
SDEV_VTOR_VALID : SDEV_VTOR_INVALID);
}
@@ -74,13 +178,14 @@ devnet_validate(struct sdev_node *dv)
* a net entry when the node is not found in the cache.
*/
static int
-devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp)
+devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp,
+ zoneid_t zid)
{
timestruc_t now;
dev_t dev;
int error;
- if ((error = dls_devnet_open(nm, ddhp, &dev)) != 0) {
+ if ((error = dls_devnet_open_in_zone(nm, ddhp, &dev, zid)) != 0) {
sdcmn_err12(("devnet_create_rvp: not a valid vanity name "
"network node: %s\n", nm));
return (error);
@@ -116,6 +221,7 @@ devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
struct sdev_node *ddv = VTOSDEV(dvp);
struct sdev_node *dv = NULL;
dls_dl_handle_t ddh = NULL;
+ zone_t *zone;
struct vattr vattr;
int nmlen;
int error = ENOENT;
@@ -123,6 +229,9 @@ devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
if (SDEVTOV(ddv)->v_type != VDIR)
return (ENOTDIR);
+ if (!SDEV_IS_GLOBAL(ddv) && crgetzoneid(cred) == GLOBAL_ZONEID)
+ return (EPERM);
+
/*
* Empty name or ., return node itself.
*/
@@ -145,6 +254,12 @@ devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
rw_enter(&ddv->sdev_contents, RW_WRITER);
/*
+ * ZOMBIED parent does not allow new node creation, bail out early.
+ */
+ if (ddv->sdev_state == SDEV_ZOMBIE)
+ goto failed;
+
+ /*
* directory cache lookup:
*/
if ((dv = sdev_cache_lookup(ddv, nm)) != NULL) {
@@ -153,13 +268,42 @@ devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
goto found;
}
+ if (SDEV_IS_GLOBAL(ddv)) {
+ /*
+ * Check for /dev/net/zone
+ */
+ if (strcmp("zone", nm) == 0 && strcmp("/dev/net",
+ ddv->sdev_path) == 0) {
+ (void) devnet_mkdir(ddv, nm);
+ dv = sdev_cache_lookup(ddv, nm);
+ ASSERT(dv != NULL);
+ goto found;
+ }
+
+ /*
+ * Check for /dev/net/zone/%z. We can't use devnet_zpath due to
+ * its trailing slash.
+ */
+ if (strcmp("/dev/net/zone", ddv->sdev_path) == 0) {
+ zone = zone_find_by_name(nm);
+ if (zone == NULL)
+ goto failed;
+ (void) devnet_mkdir(ddv, nm);
+ zone_rele(zone);
+ dv = sdev_cache_lookup(ddv, nm);
+ ASSERT(dv != NULL);
+ goto found;
+ }
+ } else if (strcmp("/dev/net", ddv->sdev_path) != 0) {
+ goto failed;
+ }
+
/*
- * ZOMBIED parent does not allow new node creation, bail out early.
+ * We didn't find what we were looking for. What that is depends a lot
+ * on what directory we're in.
*/
- if (ddv->sdev_state == SDEV_ZOMBIE)
- goto failed;
- error = devnet_create_rvp(nm, &vattr, &ddh);
+ error = devnet_create_rvp(nm, &vattr, &ddh, devnet_nodetozone(ddv));
if (error != 0)
goto failed;
@@ -219,7 +363,7 @@ devnet_filldir_datalink(datalink_id_t linkid, void *arg)
if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL)
goto found;
- if (devnet_create_rvp(link, &vattr, &ddh) != 0)
+ if (devnet_create_rvp(link, &vattr, &ddh, devnet_nodetozone(arg)) != 0)
return (0);
ASSERT(ddh != NULL);
@@ -244,16 +388,77 @@ found:
return (0);
}
+/*
+ * Fill in all the entries for the current zone.
+ */
static void
-devnet_filldir(struct sdev_node *ddv)
+devnet_fillzone(struct sdev_node *ddv, zoneid_t zid)
{
- sdev_node_t *dv, *next;
datalink_id_t linkid;
+ ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+ if (zid == GLOBAL_ZONEID) {
+ ASSERT(SDEV_IS_GLOBAL(ddv));
+ linkid = DATALINK_INVALID_LINKID;
+ do {
+ linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL,
+ DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE);
+ if (linkid != DATALINK_INVALID_LINKID)
+ (void) devnet_filldir_datalink(linkid, ddv);
+ } while (linkid != DATALINK_INVALID_LINKID);
+ } else {
+ (void) zone_datalink_walk(zid, devnet_filldir_datalink, ddv);
+ }
+}
+
+/*
+ * Callback for zone_walk when filling up /dev/net/zone/...
+ */
+static int
+devnet_fillzdir_cb(zone_t *zonep, void *arg)
+{
+ sdev_node_t *ddv = arg;
+
+ ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
+ (void) devnet_mkdir(ddv, zonep->zone_name);
+ return (0);
+}
+
+/*
+ * Fill in a directory that isn't the top level /dev/net.
+ */
+static void
+devnet_fillzdir(struct sdev_node *ddv)
+{
+ zone_t *zonep;
+ char *path = "/dev/net/zone";
+
+ if (strcmp(path, ddv->sdev_path) == 0) {
+ (void) zone_walk(devnet_fillzdir_cb, ddv);
+ return;
+ }
+
+ zonep = zone_find_by_name(ddv->sdev_name);
+ if (zonep == NULL)
+ return;
+ devnet_fillzone(ddv, zonep->zone_id);
+ zone_rele(zonep);
+}
+
+static void
+devnet_filldir(struct sdev_node *ddv)
+{
+ int ret;
+ sdev_node_t *dv, *next;
+
ASSERT(RW_READ_HELD(&ddv->sdev_contents));
if (rw_tryupgrade(&ddv->sdev_contents) == 0) {
rw_exit(&ddv->sdev_contents);
rw_enter(&ddv->sdev_contents, RW_WRITER);
+ if (ddv->sdev_state == SDEV_ZOMBIE) {
+ rw_exit(&ddv->sdev_contents);
+ return;
+ }
}
for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
@@ -276,31 +481,38 @@ devnet_filldir(struct sdev_node *ddv)
if (SDEVTOV(dv)->v_count > 0)
continue;
+
SDEV_HOLD(dv);
+
+ /*
+ * Clean out everything underneath before we remove ourselves.
+ */
+ if (SDEVTOV(dv)->v_type == VDIR) {
+ ret = sdev_cleandir(dv, NULL, 0);
+ ASSERT(ret == 0);
+ }
/* remove the cache node */
(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
SDEV_CACHE_DELETE);
SDEV_RELE(dv);
}
+ if (strcmp(ddv->sdev_path, "/dev/net") != 0) {
+ devnet_fillzdir(ddv);
+ goto done;
+ }
+
if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild())
goto done;
if (SDEV_IS_GLOBAL(ddv)) {
- linkid = DATALINK_INVALID_LINKID;
- do {
- linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL,
- DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE);
- if (linkid != DATALINK_INVALID_LINKID)
- (void) devnet_filldir_datalink(linkid, ddv);
- } while (linkid != DATALINK_INVALID_LINKID);
+ devnet_fillzone(ddv, GLOBAL_ZONEID);
+ (void) devnet_mkdir(ddv, "zone");
} else {
- (void) zone_datalink_walk(getzoneid(),
- devnet_filldir_datalink, ddv);
+ devnet_fillzone(ddv, getzoneid());
}
ddv->sdev_flags &= ~SDEV_BUILD;
-
done:
rw_downgrade(&ddv->sdev_contents);
}
@@ -319,6 +531,9 @@ devnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
ASSERT(sdvp);
+ if (crgetzoneid(cred) == GLOBAL_ZONEID && !SDEV_IS_GLOBAL(sdvp))
+ return (EPERM);
+
if (uiop->uio_offset == 0)
devnet_filldir(sdvp);
diff --git a/usr/src/uts/common/fs/dev/sdev_vnops.c b/usr/src/uts/common/fs/dev/sdev_vnops.c
index 8fe926f6fb..5a00242482 100644
--- a/usr/src/uts/common/fs/dev/sdev_vnops.c
+++ b/usr/src/uts/common/fs/dev/sdev_vnops.c
@@ -894,6 +894,9 @@ sdev_remove(struct vnode *dvp, char *nm, struct cred *cred,
}
}
+ if (error == 0)
+ i_ddi_di_cache_invalidate();
+
return (error);
}
@@ -1218,6 +1221,7 @@ sdev_symlink(struct vnode *dvp, char *lnm, struct vattr *tva,
sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
if (SDEV_IS_GLOBAL(parent))
atomic_inc_ulong(&parent->sdev_gdir_gen);
+ i_ddi_di_cache_invalidate();
/* wake up other threads blocked on looking up this node */
mutex_enter(&self->sdev_lookup_lock);
@@ -1290,6 +1294,7 @@ sdev_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp,
sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
if (SDEV_IS_GLOBAL(parent))
atomic_inc_ulong(&parent->sdev_gdir_gen);
+ i_ddi_di_cache_invalidate();
/* wake up other threads blocked on looking up this node */
mutex_enter(&self->sdev_lookup_lock);
@@ -1405,6 +1410,9 @@ sdev_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred,
}
+ if (error == 0)
+ i_ddi_di_cache_invalidate();
+
return (error);
}
diff --git a/usr/src/uts/common/fs/dev/sdev_zvolops.c b/usr/src/uts/common/fs/dev/sdev_zvolops.c
index 8f22ef32f0..e236eb3f72 100644
--- a/usr/src/uts/common/fs/dev/sdev_zvolops.c
+++ b/usr/src/uts/common/fs/dev/sdev_zvolops.c
@@ -472,8 +472,10 @@ devzvol_create_pool_dirs(struct vnode *dvp)
ASSERT(dvp->v_count > 0);
rc = VOP_LOOKUP(dvp, nvpair_name(elem), &vp, NULL, 0,
NULL, kcred, NULL, 0, NULL);
- /* should either work, or not be visible from a zone */
- ASSERT(rc == 0 || rc == ENOENT);
+ /*
+ * should either work or we should get an error if this should
+ * not be visible from the zone, or disallowed in the zone
+ */
if (rc == 0)
VN_RELE(vp);
pools++;
diff --git a/usr/src/uts/common/fs/fem.c b/usr/src/uts/common/fs/fem.c
index 9f7f284842..769316bb4c 100644
--- a/usr/src/uts/common/fs/fem.c
+++ b/usr/src/uts/common/fs/fem.c
@@ -23,6 +23,10 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
#include <sys/types.h>
#include <sys/atomic.h>
#include <sys/kmem.h>
@@ -33,11 +37,12 @@
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
-
#include <sys/fem.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/vfs_opreg.h>
+#include <sys/stack.h>
+#include <sys/archsystm.h>
#define NNODES_DEFAULT 8 /* Default number of nodes in a fem_list */
/*
@@ -291,6 +296,536 @@ _op_find(femarg_t *ap, void **fp, int offs0, int offs1)
}
#endif
+/*
+ * File event monitoring handoffs
+ *
+ * File event monitoring relies on being able to inject stack frames between
+ * vnode consumers and the underlying file systems. This becomes problematic
+ * when there exist many monitors, as kernel stack depth is finite. The model
+ * very much encodes this injected frame: the flow of control deliberately
+ * lies with the monitor, not with the monitoring system. While we could
+ * conceivably address this by allowing each subsystem to install at most
+ * one monitor per vnode (and impose on subsystems that they handle any
+ * of their own consumer multiplexing internally), this in fact exports a
+ * substantial amount of run-time complexity to deal with an uncommon case
+ * (and, it must be said, assumes a small number of consuming subsystems).
+ * To allow our abstraction to remain clean, we instead check our remaining
+ * stack in every vnext_*() call; if the amount of stack remaining is lower
+ * than a threshold (fem_stack_needed), we call thread_splitstack() to carry
+ * on the execution of the monitors and the underlying vnode operation on a
+ * split stack. Because we can only pass a single argument to our split stack
+ * function, we must marshal our arguments, the mechanics of which are somewhat
+ * ornate in terms of the code: to marshal in a type-safe manner, we define a
+ * baton that is a union of payload structures for each kind of operation,
+ * loading the per-operation payload explicitly and calling into common handoff
+ * code that itself calls thread_splitstack(). The function passed to
+ * thread_splitstack() is a per-entry point function that continues monitor
+ * processing given the specified (marshalled) arguments. While this method
+ * is a little verbose to implement, it has the advantage of being relatively
+ * robust (that is, broadly type-safe) while imposing minimal burden on each
+ * vnext_*() entry point.
+ *
+ * In terms of the implementation:
+ *
+ * - The FEM_BATON_n macros define the per-entry point baton structures
+ * - The fem_baton_payload_t contains the union of these structures
+ * - The FEM_VNEXTn_DECL macros declare the post-handoff entry point
+ * - The FEM_VNEXTn macros constitute the per-handoff entry point
+ *
+ * Note that we don't use variadic macros -- we define a variant of these
+ * macros for each of our relevant argument counts. This may seem overly
+ * explicit, but it is deliberate: the object here is to minimize the
+ * future maintenance burden by minimizing the likelihood of introduced
+ * error -- not to minimize the number of characters in this source file.
+ */
+
+#ifndef STACK_GROWTH_DOWN
+#error Downward stack growth assumed.
+#endif
+
+int fem_stack_toodeep;
+uintptr_t fem_stack_needed = 8 * 1024;
+size_t fem_handoff_stacksize = 128 * 1024;
+
+#define FEM_TOODEEP() (STACK_BIAS + (uintptr_t)getfp() - \
+ (uintptr_t)curthread->t_stkbase < fem_stack_needed)
+
+#define FEM_BATON_1(what, t0, l0) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ } fb_##what
+
+#define FEM_BATON_2(what, t0, l0, t1, l1) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ } fb_##what
+
+#define FEM_BATON_3(what, t0, l0, t1, l1, t2, l2) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ } fb_##what
+
+#define FEM_BATON_4(what, t0, l0, t1, l1, t2, l2, t3, l3) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ t3 fb_##what##_##l3; \
+ } fb_##what
+
+#define FEM_BATON_5(what, t0, l0, t1, l1, t2, l2, t3, l3, t4, l4) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ t3 fb_##what##_##l3; \
+ t4 fb_##what##_##l4; \
+ } fb_##what
+
+#define FEM_BATON_6(what, t0, l0, t1, l1, t2, l2, t3, l3, t4, l4, t5, l5) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ t3 fb_##what##_##l3; \
+ t4 fb_##what##_##l4; \
+ t5 fb_##what##_##l5; \
+ } fb_##what
+
+#define FEM_BATON_8(what, t0, l0, t1, l1, t2, l2, t3, l3, t4, l4, t5, l5, \
+ t6, l6, t7, l7) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ t3 fb_##what##_##l3; \
+ t4 fb_##what##_##l4; \
+ t5 fb_##what##_##l5; \
+ t6 fb_##what##_##l6; \
+ t7 fb_##what##_##l7; \
+ } fb_##what
+
+#define FEM_BATON_9(what, t0, l0, t1, l1, t2, l2, t3, l3, t4, l4, t5, l5, \
+ t6, l6, t7, l7, t8, l8) \
+ struct { \
+ void *fb_##what##_arg0; \
+ caller_context_t *fb_##what##_ct; \
+ t0 fb_##what##_##l0; \
+ t1 fb_##what##_##l1; \
+ t2 fb_##what##_##l2; \
+ t3 fb_##what##_##l3; \
+ t4 fb_##what##_##l4; \
+ t5 fb_##what##_##l5; \
+ t6 fb_##what##_##l6; \
+ t7 fb_##what##_##l7; \
+ t8 fb_##what##_##l8; \
+ } fb_##what
+
+typedef union {
+ FEM_BATON_2(open, int, mode, cred_t *, cr);
+ FEM_BATON_4(close, int, flag, int, count,
+ offset_t, offset, cred_t *, cr);
+ FEM_BATON_3(read, uio_t *, uiop, int, ioflag, cred_t *, cr);
+ FEM_BATON_3(write, uio_t *, uiop, int, ioflag, cred_t *, cr);
+ FEM_BATON_5(ioctl, int, cmd, intptr_t, arg,
+ int, flag, cred_t *, cr, int *, rvalp);
+ FEM_BATON_3(setfl, int, oflags, int, nflags, cred_t *, cr);
+ FEM_BATON_3(getattr, vattr_t *, vap, int, flags, cred_t *, cr);
+ FEM_BATON_3(setattr, vattr_t *, vap, int, flags, cred_t *, cr);
+ FEM_BATON_3(access, int, mode, int, flags, cred_t *, cr);
+ FEM_BATON_8(lookup, char *, nm, vnode_t **, vpp,
+ pathname_t *, pnp, int, flags, vnode_t *, rdir,
+ cred_t *, cr, int *, direntflags, pathname_t *, realpnp);
+ FEM_BATON_8(create, char *, name, vattr_t *, vap,
+ vcexcl_t, excl, int, mode, vnode_t **, vpp,
+ cred_t *, cr, int, flag, vsecattr_t *, vsecp);
+ FEM_BATON_3(remove, char *, nm, cred_t *, cr, int, flags);
+ FEM_BATON_4(link, vnode_t *, svp, char *, tnm,
+ cred_t *, cr, int, flags);
+ FEM_BATON_5(rename, char *, snm, vnode_t *, tdvp,
+ char *, tnm, cred_t *, cr, int, flags);
+ FEM_BATON_6(mkdir, char *, dirname, vattr_t *, vap,
+ vnode_t **, vpp, cred_t *, cr, int, flags,
+ vsecattr_t *, vsecp);
+ FEM_BATON_4(rmdir, char *, nm, vnode_t *, cdir,
+ cred_t *, cr, int, flags);
+ FEM_BATON_4(readdir, uio_t *, uiop, cred_t *, cr,
+ int *, eofp, int, flags);
+ FEM_BATON_5(symlink, char *, linkname, vattr_t *, vap,
+ char *, target, cred_t *, cr, int, flags);
+ FEM_BATON_2(readlink, uio_t *, uiop, cred_t *, cr);
+ FEM_BATON_2(fsync, int, syncflag, cred_t *, cr);
+ FEM_BATON_1(inactive, cred_t *, cr);
+ FEM_BATON_1(fid, fid_t *, fidp);
+ FEM_BATON_1(rwlock, int, write_lock);
+ FEM_BATON_1(rwunlock, int, write_lock);
+ FEM_BATON_2(seek, offset_t, ooff, offset_t *, noffp);
+ FEM_BATON_1(cmp, vnode_t *, vp2);
+ FEM_BATON_6(frlock, int, cmd, struct flock64 *, bfp,
+ int, flag, offset_t, offset, struct flk_callback *, flk_cbp,
+ cred_t *, cr);
+ FEM_BATON_5(space, int, cmd, struct flock64 *, bfp,
+ int, flag, offset_t, offset, cred_t *, cr);
+ FEM_BATON_1(realvp, vnode_t **, vpp);
+ FEM_BATON_9(getpage, offset_t, off, size_t, len,
+ uint_t *, protp, struct page **, plarr, size_t, plsz,
+ struct seg *, seg, caddr_t, addr, enum seg_rw, rw,
+ cred_t *, cr);
+ FEM_BATON_4(putpage, offset_t, off, size_t, len,
+ int, flags, cred_t *, cr);
+ FEM_BATON_8(map, offset_t, off, struct as *, as,
+ caddr_t *, addrp, size_t, len, uchar_t, prot,
+ uchar_t, maxprot, uint_t, flags, cred_t *, cr);
+ FEM_BATON_8(addmap, offset_t, off, struct as *, as,
+ caddr_t, addr, size_t, len, uchar_t, prot,
+ uchar_t, maxprot, uint_t, flags, cred_t *, cr);
+ FEM_BATON_8(delmap, offset_t, off, struct as *, as,
+ caddr_t, addr, size_t, len, uint_t, prot,
+ uint_t, maxprot, uint_t, flags, cred_t *, cr);
+ FEM_BATON_4(poll, short, events, int, anyyet,
+ short *, reventsp, struct pollhead **, phpp);
+ FEM_BATON_3(dump, caddr_t, addr, offset_t, lbdn, offset_t, dblks);
+ FEM_BATON_3(pathconf, int, cmd, ulong_t *, valp, cred_t *, cr);
+ FEM_BATON_5(pageio, struct page *, pp, u_offset_t, io_off,
+ size_t, io_len, int, flags, cred_t *, cr);
+ FEM_BATON_2(dumpctl, int, action, offset_t *, blkp);
+ FEM_BATON_4(dispose, struct page *, pp, int, flag,
+ int, dn, cred_t *, cr);
+ FEM_BATON_3(setsecattr, vsecattr_t *, vsap, int, flag, cred_t *, cr);
+ FEM_BATON_3(getsecattr, vsecattr_t *, vsap, int, flag, cred_t *, cr);
+ FEM_BATON_4(shrlock, int, cmd, struct shrlock *, shr,
+ int, flag, cred_t *, cr);
+ FEM_BATON_3(vnevent, vnevent_t, vnevent, vnode_t *, dvp, char *, cname);
+ FEM_BATON_3(reqzcbuf, enum uio_rw, ioflag,
+ xuio_t *, xuiop, cred_t *, cr);
+ FEM_BATON_2(retzcbuf, xuio_t *, xuiop, cred_t *, cr);
+} fem_baton_payload_t;
+
+typedef struct {
+ fem_baton_payload_t fb_payload;
+ int (*fb_func)();
+ void (*fb_handoff)();
+ int fb_rval;
+} fem_baton_t;
+
+static int
+fem_handoff(fem_baton_t *bp)
+{
+ fem_stack_toodeep++;
+ thread_splitstack(bp->fb_handoff, bp, fem_handoff_stacksize);
+
+ return (bp->fb_rval);
+}
+
+#define FEM_VNEXT3_DECL(what, a0, a1, a2) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2); \
+}
+
+#define FEM_VNEXT4_DECL(what, a0, a1, a2, a3) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3); \
+}
+
+#define FEM_VNEXT5_DECL(what, a0, a1, a2, a3, a4) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4); \
+}
+
+#define FEM_VNEXT6_DECL(what, a0, a1, a2, a3, a4, a5) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4, \
+ bp->fb_payload.fb_##what.fb_##what##_##a5); \
+}
+
+#define FEM_VNEXT7_DECL(what, a0, a1, a2, a3, a4, a5, a6) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4, \
+ bp->fb_payload.fb_##what.fb_##what##_##a5, \
+ bp->fb_payload.fb_##what.fb_##what##_##a6); \
+}
+
+#define FEM_VNEXT8_DECL(what, a0, a1, a2, a3, a4, a5, a6, a7) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4, \
+ bp->fb_payload.fb_##what.fb_##what##_##a5, \
+ bp->fb_payload.fb_##what.fb_##what##_##a6, \
+ bp->fb_payload.fb_##what.fb_##what##_##a7); \
+}
+
+#define FEM_VNEXT10_DECL(what, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4, \
+ bp->fb_payload.fb_##what.fb_##what##_##a5, \
+ bp->fb_payload.fb_##what.fb_##what##_##a6, \
+ bp->fb_payload.fb_##what.fb_##what##_##a7, \
+ bp->fb_payload.fb_##what.fb_##what##_##a8, \
+ bp->fb_payload.fb_##what.fb_##what##_##a9); \
+}
+
+#define FEM_VNEXT11_DECL(what, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \
+void \
+fem_handoff_##what(fem_baton_t *bp) \
+{ \
+ bp->fb_rval = bp->fb_func( \
+ bp->fb_payload.fb_##what.fb_##what##_##a0, \
+ bp->fb_payload.fb_##what.fb_##what##_##a1, \
+ bp->fb_payload.fb_##what.fb_##what##_##a2, \
+ bp->fb_payload.fb_##what.fb_##what##_##a3, \
+ bp->fb_payload.fb_##what.fb_##what##_##a4, \
+ bp->fb_payload.fb_##what.fb_##what##_##a5, \
+ bp->fb_payload.fb_##what.fb_##what##_##a6, \
+ bp->fb_payload.fb_##what.fb_##what##_##a7, \
+ bp->fb_payload.fb_##what.fb_##what##_##a8, \
+ bp->fb_payload.fb_##what.fb_##what##_##a9, \
+ bp->fb_payload.fb_##what.fb_##what##_##a10); \
+}
+
+#define FEM_VNEXT3(what, func, a0, a1, a2) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2))
+
+#define FEM_VNEXT4(what, func, a0, a1, a2, a3) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3))
+
+#define FEM_VNEXT5(what, func, a0, a1, a2, a3, a4) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4))
+
+#define FEM_VNEXT6(what, func, a0, a1, a2, a3, a4, a5) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_payload.fb_##what.fb_##what##_##a5 = a5; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4, a5))
+
+#define FEM_VNEXT7(what, func, a0, a1, a2, a3, a4, a5, a6) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_payload.fb_##what.fb_##what##_##a5 = a5; \
+ baton->fb_payload.fb_##what.fb_##what##_##a6 = a6; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4, a5, a6))
+
+#define FEM_VNEXT8(what, func, a0, a1, a2, a3, a4, a5, a6, a7) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_payload.fb_##what.fb_##what##_##a5 = a5; \
+ baton->fb_payload.fb_##what.fb_##what##_##a6 = a6; \
+ baton->fb_payload.fb_##what.fb_##what##_##a7 = a7; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4, a5, a6, a7))
+
+#define FEM_VNEXT10(what, func, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_payload.fb_##what.fb_##what##_##a5 = a5; \
+ baton->fb_payload.fb_##what.fb_##what##_##a6 = a6; \
+ baton->fb_payload.fb_##what.fb_##what##_##a7 = a7; \
+ baton->fb_payload.fb_##what.fb_##what##_##a8 = a8; \
+ baton->fb_payload.fb_##what.fb_##what##_##a9 = a9; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9))
+
+#define FEM_VNEXT11(what, func, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \
+ if (FEM_TOODEEP()) { \
+ fem_baton_t *baton; \
+ int rval; \
+ \
+ baton = kmem_alloc(sizeof (fem_baton_t), KM_SLEEP); \
+ baton->fb_payload.fb_##what.fb_##what##_##a0 = a0; \
+ baton->fb_payload.fb_##what.fb_##what##_##a1 = a1; \
+ baton->fb_payload.fb_##what.fb_##what##_##a2 = a2; \
+ baton->fb_payload.fb_##what.fb_##what##_##a3 = a3; \
+ baton->fb_payload.fb_##what.fb_##what##_##a4 = a4; \
+ baton->fb_payload.fb_##what.fb_##what##_##a5 = a5; \
+ baton->fb_payload.fb_##what.fb_##what##_##a6 = a6; \
+ baton->fb_payload.fb_##what.fb_##what##_##a7 = a7; \
+ baton->fb_payload.fb_##what.fb_##what##_##a8 = a8; \
+ baton->fb_payload.fb_##what.fb_##what##_##a9 = a9; \
+ baton->fb_payload.fb_##what.fb_##what##_##a10 = a10; \
+ baton->fb_handoff = fem_handoff_##what; \
+ baton->fb_func = func; \
+ \
+ rval = fem_handoff(baton); \
+ kmem_free(baton, sizeof (fem_baton_t)); \
+ \
+ return (rval); \
+ } \
+ return (func(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10))
+
static fem_t *
fem_alloc()
{
@@ -2040,10 +2575,60 @@ static struct fs_operation_def fshead_vfs_spec[] = {
* 5. Return by invoking the base operation with the base object.
*
* for each classification, there needs to be at least one "next" operation
- * for each "head"operation.
- *
+ * for each "head" operation. Note that we also use the FEM_VNEXTn_DECL macros
+ * to define the function to run when the stack is split; see the discussion
+ * on "File event monitoring handoffs", above.
*/
+FEM_VNEXT4_DECL(open, arg0, mode, cr, ct)
+FEM_VNEXT6_DECL(close, arg0, flag, count, offset, cr, ct)
+FEM_VNEXT5_DECL(read, arg0, uiop, ioflag, cr, ct)
+FEM_VNEXT5_DECL(write, arg0, uiop, ioflag, cr, ct)
+FEM_VNEXT7_DECL(ioctl, arg0, cmd, arg, flag, cr, rvalp, ct)
+FEM_VNEXT5_DECL(setfl, arg0, oflags, nflags, cr, ct)
+FEM_VNEXT5_DECL(getattr, arg0, vap, flags, cr, ct)
+FEM_VNEXT5_DECL(setattr, arg0, vap, flags, cr, ct)
+FEM_VNEXT5_DECL(access, arg0, mode, flags, cr, ct)
+FEM_VNEXT10_DECL(lookup, arg0, nm, vpp, pnp, flags, rdir,
+ cr, ct, direntflags, realpnp)
+FEM_VNEXT10_DECL(create, arg0, name, vap, excl, mode, vpp, cr, flag, ct, vsecp)
+FEM_VNEXT5_DECL(remove, arg0, nm, cr, ct, flags)
+FEM_VNEXT6_DECL(link, arg0, svp, tnm, cr, ct, flags)
+FEM_VNEXT7_DECL(rename, arg0, snm, tdvp, tnm, cr, ct, flags)
+FEM_VNEXT8_DECL(mkdir, arg0, dirname, vap, vpp, cr, ct, flags, vsecp)
+FEM_VNEXT6_DECL(rmdir, arg0, nm, cdir, cr, ct, flags)
+FEM_VNEXT6_DECL(readdir, arg0, uiop, cr, eofp, ct, flags)
+FEM_VNEXT7_DECL(symlink, arg0, linkname, vap, target, cr, ct, flags)
+FEM_VNEXT4_DECL(readlink, arg0, uiop, cr, ct)
+FEM_VNEXT4_DECL(fsync, arg0, syncflag, cr, ct)
+FEM_VNEXT3_DECL(fid, arg0, fidp, ct)
+FEM_VNEXT3_DECL(rwlock, arg0, write_lock, ct)
+FEM_VNEXT4_DECL(seek, arg0, ooff, noffp, ct)
+FEM_VNEXT3_DECL(cmp, arg0, vp2, ct)
+FEM_VNEXT8_DECL(frlock, arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct)
+FEM_VNEXT7_DECL(space, arg0, cmd, bfp, flag, offset, cr, ct)
+FEM_VNEXT3_DECL(realvp, arg0, vpp, ct)
+FEM_VNEXT11_DECL(getpage, arg0, off, len, protp, plarr, plsz,
+ seg, addr, rw, cr, ct)
+FEM_VNEXT6_DECL(putpage, arg0, off, len, flags, cr, ct)
+FEM_VNEXT10_DECL(map, arg0, off, as, addrp, len, prot, maxprot,
+ flags, cr, ct)
+FEM_VNEXT10_DECL(addmap, arg0, off, as, addr, len, prot, maxprot,
+ flags, cr, ct)
+FEM_VNEXT10_DECL(delmap, arg0, off, as, addr, len, prot, maxprot,
+ flags, cr, ct)
+FEM_VNEXT6_DECL(poll, arg0, events, anyyet, reventsp, phpp, ct)
+FEM_VNEXT5_DECL(dump, arg0, addr, lbdn, dblks, ct)
+FEM_VNEXT5_DECL(pathconf, arg0, cmd, valp, cr, ct)
+FEM_VNEXT7_DECL(pageio, arg0, pp, io_off, io_len, flags, cr, ct)
+FEM_VNEXT4_DECL(dumpctl, arg0, action, blkp, ct)
+FEM_VNEXT5_DECL(setsecattr, arg0, vsap, flag, cr, ct)
+FEM_VNEXT5_DECL(getsecattr, arg0, vsap, flag, cr, ct)
+FEM_VNEXT6_DECL(shrlock, arg0, cmd, shr, flag, cr, ct)
+FEM_VNEXT5_DECL(vnevent, arg0, vnevent, dvp, cname, ct)
+FEM_VNEXT5_DECL(reqzcbuf, arg0, ioflag, xuiop, cr, ct)
+FEM_VNEXT4_DECL(retzcbuf, arg0, xuiop, cr, ct)
+
int
vnext_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
{
@@ -2055,7 +2640,7 @@ vnext_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_open, femop_open);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, mode, cr, ct));
+ FEM_VNEXT4(open, func, arg0, mode, cr, ct);
}
int
@@ -2070,7 +2655,7 @@ vnext_close(femarg_t *vf, int flag, int count, offset_t offset, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_close, femop_close);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, flag, count, offset, cr, ct));
+ FEM_VNEXT6(close, func, arg0, flag, count, offset, cr, ct);
}
int
@@ -2085,7 +2670,7 @@ vnext_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_read, femop_read);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, uiop, ioflag, cr, ct));
+ FEM_VNEXT5(read, func, arg0, uiop, ioflag, cr, ct);
}
int
@@ -2100,7 +2685,7 @@ vnext_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_write, femop_write);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, uiop, ioflag, cr, ct));
+ FEM_VNEXT5(write, func, arg0, uiop, ioflag, cr, ct);
}
int
@@ -2115,7 +2700,7 @@ vnext_ioctl(femarg_t *vf, int cmd, intptr_t arg, int flag, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_ioctl, femop_ioctl);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, cmd, arg, flag, cr, rvalp, ct));
+ FEM_VNEXT7(ioctl, func, arg0, cmd, arg, flag, cr, rvalp, ct);
}
int
@@ -2130,7 +2715,7 @@ vnext_setfl(femarg_t *vf, int oflags, int nflags, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_setfl, femop_setfl);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, oflags, nflags, cr, ct));
+ FEM_VNEXT5(setfl, func, arg0, oflags, nflags, cr, ct);
}
int
@@ -2145,7 +2730,7 @@ vnext_getattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_getattr, femop_getattr);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vap, flags, cr, ct));
+ FEM_VNEXT5(getattr, func, arg0, vap, flags, cr, ct);
}
int
@@ -2160,7 +2745,7 @@ vnext_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_setattr, femop_setattr);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vap, flags, cr, ct));
+ FEM_VNEXT5(setattr, func, arg0, vap, flags, cr, ct);
}
int
@@ -2175,7 +2760,7 @@ vnext_access(femarg_t *vf, int mode, int flags, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_access, femop_access);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, mode, flags, cr, ct));
+ FEM_VNEXT5(access, func, arg0, mode, flags, cr, ct);
}
int
@@ -2191,8 +2776,8 @@ vnext_lookup(femarg_t *vf, char *nm, vnode_t **vpp, pathname_t *pnp,
vsop_find(vf, &func, int, &arg0, vop_lookup, femop_lookup);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, nm, vpp, pnp, flags, rdir, cr, ct,
- direntflags, realpnp));
+ FEM_VNEXT10(lookup, func, arg0, nm, vpp, pnp, flags, rdir, cr, ct,
+ direntflags, realpnp);
}
int
@@ -2208,7 +2793,8 @@ vnext_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
vsop_find(vf, &func, int, &arg0, vop_create, femop_create);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, name, vap, excl, mode, vpp, cr, flag, ct, vsecp));
+ FEM_VNEXT10(create, func, arg0, name, vap, excl,
+ mode, vpp, cr, flag, ct, vsecp);
}
int
@@ -2223,7 +2809,7 @@ vnext_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
vsop_find(vf, &func, int, &arg0, vop_remove, femop_remove);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, nm, cr, ct, flags));
+ FEM_VNEXT5(remove, func, arg0, nm, cr, ct, flags);
}
int
@@ -2238,7 +2824,7 @@ vnext_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_link, femop_link);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, svp, tnm, cr, ct, flags));
+ FEM_VNEXT6(link, func, arg0, svp, tnm, cr, ct, flags);
}
int
@@ -2253,7 +2839,7 @@ vnext_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_rename, femop_rename);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, snm, tdvp, tnm, cr, ct, flags));
+ FEM_VNEXT7(rename, func, arg0, snm, tdvp, tnm, cr, ct, flags);
}
int
@@ -2268,7 +2854,7 @@ vnext_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp,
vsop_find(vf, &func, int, &arg0, vop_mkdir, femop_mkdir);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, dirname, vap, vpp, cr, ct, flags, vsecp));
+ FEM_VNEXT8(mkdir, func, arg0, dirname, vap, vpp, cr, ct, flags, vsecp);
}
int
@@ -2283,7 +2869,7 @@ vnext_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_rmdir, femop_rmdir);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, nm, cdir, cr, ct, flags));
+ FEM_VNEXT6(rmdir, func, arg0, nm, cdir, cr, ct, flags);
}
int
@@ -2298,7 +2884,7 @@ vnext_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
vsop_find(vf, &func, int, &arg0, vop_readdir, femop_readdir);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, uiop, cr, eofp, ct, flags));
+ FEM_VNEXT6(readdir, func, arg0, uiop, cr, eofp, ct, flags);
}
int
@@ -2313,7 +2899,7 @@ vnext_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target,
vsop_find(vf, &func, int, &arg0, vop_symlink, femop_symlink);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, linkname, vap, target, cr, ct, flags));
+ FEM_VNEXT7(symlink, func, arg0, linkname, vap, target, cr, ct, flags);
}
int
@@ -2327,7 +2913,7 @@ vnext_readlink(femarg_t *vf, uio_t *uiop, cred_t *cr, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_readlink, femop_readlink);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, uiop, cr, ct));
+ FEM_VNEXT4(readlink, func, arg0, uiop, cr, ct);
}
int
@@ -2341,7 +2927,7 @@ vnext_fsync(femarg_t *vf, int syncflag, cred_t *cr, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_fsync, femop_fsync);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, syncflag, cr, ct));
+ FEM_VNEXT4(fsync, func, arg0, syncflag, cr, ct);
}
void
@@ -2369,7 +2955,7 @@ vnext_fid(femarg_t *vf, fid_t *fidp, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_fid, femop_fid);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, fidp, ct));
+ FEM_VNEXT3(fid, func, arg0, fidp, ct);
}
int
@@ -2383,7 +2969,7 @@ vnext_rwlock(femarg_t *vf, int write_lock, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_rwlock, femop_rwlock);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, write_lock, ct));
+ FEM_VNEXT3(rwlock, func, arg0, write_lock, ct);
}
void
@@ -2411,7 +2997,7 @@ vnext_seek(femarg_t *vf, offset_t ooff, offset_t *noffp, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_seek, femop_seek);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, ooff, noffp, ct));
+ FEM_VNEXT4(seek, func, arg0, ooff, noffp, ct);
}
int
@@ -2425,7 +3011,7 @@ vnext_cmp(femarg_t *vf, vnode_t *vp2, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_cmp, femop_cmp);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vp2, ct));
+ FEM_VNEXT3(cmp, func, arg0, vp2, ct);
}
int
@@ -2441,7 +3027,7 @@ vnext_frlock(femarg_t *vf, int cmd, struct flock64 *bfp, int flag,
vsop_find(vf, &func, int, &arg0, vop_frlock, femop_frlock);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct));
+ FEM_VNEXT8(frlock, func, arg0, cmd, bfp, flag, offset, flk_cbp, cr, ct);
}
int
@@ -2456,7 +3042,7 @@ vnext_space(femarg_t *vf, int cmd, struct flock64 *bfp, int flag,
vsop_find(vf, &func, int, &arg0, vop_space, femop_space);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, cmd, bfp, flag, offset, cr, ct));
+ FEM_VNEXT7(space, func, arg0, cmd, bfp, flag, offset, cr, ct);
}
int
@@ -2470,7 +3056,7 @@ vnext_realvp(femarg_t *vf, vnode_t **vpp, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_realvp, femop_realvp);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vpp, ct));
+ FEM_VNEXT3(realvp, func, arg0, vpp, ct);
}
int
@@ -2486,8 +3072,8 @@ vnext_getpage(femarg_t *vf, offset_t off, size_t len, uint_t *protp,
vsop_find(vf, &func, int, &arg0, vop_getpage, femop_getpage);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, off, len, protp, plarr, plsz, seg, addr, rw,
- cr, ct));
+ FEM_VNEXT11(getpage, func, arg0, off, len, protp,
+ plarr, plsz, seg, addr, rw, cr, ct);
}
int
@@ -2502,7 +3088,7 @@ vnext_putpage(femarg_t *vf, offset_t off, size_t len, int flags,
vsop_find(vf, &func, int, &arg0, vop_putpage, femop_putpage);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, off, len, flags, cr, ct));
+ FEM_VNEXT6(putpage, func, arg0, off, len, flags, cr, ct);
}
int
@@ -2518,8 +3104,8 @@ vnext_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp,
vsop_find(vf, &func, int, &arg0, vop_map, femop_map);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, off, as, addrp, len, prot, maxprot, flags,
- cr, ct));
+ FEM_VNEXT10(map, func, arg0, off, as, addrp, len, prot, maxprot, flags,
+ cr, ct);
}
int
@@ -2535,8 +3121,8 @@ vnext_addmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr,
vsop_find(vf, &func, int, &arg0, vop_addmap, femop_addmap);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags,
- cr, ct));
+ FEM_VNEXT10(addmap, func, arg0, off, as, addr, len, prot, maxprot,
+ flags, cr, ct);
}
int
@@ -2552,8 +3138,8 @@ vnext_delmap(femarg_t *vf, offset_t off, struct as *as, caddr_t addr,
vsop_find(vf, &func, int, &arg0, vop_delmap, femop_delmap);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, off, as, addr, len, prot, maxprot, flags,
- cr, ct));
+ FEM_VNEXT10(delmap, func, arg0, off, as, addr, len, prot, maxprot,
+ flags, cr, ct);
}
int
@@ -2568,7 +3154,7 @@ vnext_poll(femarg_t *vf, short events, int anyyet, short *reventsp,
vsop_find(vf, &func, int, &arg0, vop_poll, femop_poll);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, events, anyyet, reventsp, phpp, ct));
+ FEM_VNEXT6(poll, func, arg0, events, anyyet, reventsp, phpp, ct);
}
int
@@ -2583,7 +3169,7 @@ vnext_dump(femarg_t *vf, caddr_t addr, offset_t lbdn, offset_t dblks,
vsop_find(vf, &func, int, &arg0, vop_dump, femop_dump);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, addr, lbdn, dblks, ct));
+ FEM_VNEXT5(dump, func, arg0, addr, lbdn, dblks, ct);
}
int
@@ -2598,7 +3184,7 @@ vnext_pathconf(femarg_t *vf, int cmd, ulong_t *valp, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_pathconf, femop_pathconf);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, cmd, valp, cr, ct));
+ FEM_VNEXT5(pathconf, func, arg0, cmd, valp, cr, ct);
}
int
@@ -2613,7 +3199,7 @@ vnext_pageio(femarg_t *vf, struct page *pp, u_offset_t io_off,
vsop_find(vf, &func, int, &arg0, vop_pageio, femop_pageio);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, pp, io_off, io_len, flags, cr, ct));
+ FEM_VNEXT7(pageio, func, arg0, pp, io_off, io_len, flags, cr, ct);
}
int
@@ -2627,7 +3213,7 @@ vnext_dumpctl(femarg_t *vf, int action, offset_t *blkp, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_dumpctl, femop_dumpctl);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, action, blkp, ct));
+ FEM_VNEXT4(dumpctl, func, arg0, action, blkp, ct);
}
void
@@ -2657,7 +3243,7 @@ vnext_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_setsecattr, femop_setsecattr);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vsap, flag, cr, ct));
+ FEM_VNEXT5(setsecattr, func, arg0, vsap, flag, cr, ct);
}
int
@@ -2672,7 +3258,7 @@ vnext_getsecattr(femarg_t *vf, vsecattr_t *vsap, int flag, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_getsecattr, femop_getsecattr);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vsap, flag, cr, ct));
+ FEM_VNEXT5(getsecattr, func, arg0, vsap, flag, cr, ct);
}
int
@@ -2687,7 +3273,7 @@ vnext_shrlock(femarg_t *vf, int cmd, struct shrlock *shr, int flag,
vsop_find(vf, &func, int, &arg0, vop_shrlock, femop_shrlock);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, cmd, shr, flag, cr, ct));
+ FEM_VNEXT6(shrlock, func, arg0, cmd, shr, flag, cr, ct);
}
int
@@ -2702,7 +3288,7 @@ vnext_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *cname,
vsop_find(vf, &func, int, &arg0, vop_vnevent, femop_vnevent);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, vnevent, dvp, cname, ct));
+ FEM_VNEXT5(vnevent, func, arg0, vnevent, dvp, cname, ct);
}
int
@@ -2717,7 +3303,7 @@ vnext_reqzcbuf(femarg_t *vf, enum uio_rw ioflag, xuio_t *xuiop, cred_t *cr,
vsop_find(vf, &func, int, &arg0, vop_reqzcbuf, femop_reqzcbuf);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, ioflag, xuiop, cr, ct));
+ FEM_VNEXT5(reqzcbuf, func, arg0, ioflag, xuiop, cr, ct);
}
int
@@ -2731,7 +3317,7 @@ vnext_retzcbuf(femarg_t *vf, xuio_t *xuiop, cred_t *cr, caller_context_t *ct)
vsop_find(vf, &func, int, &arg0, vop_retzcbuf, femop_retzcbuf);
ASSERT(func != NULL);
ASSERT(arg0 != NULL);
- return ((*func)(arg0, xuiop, cr, ct));
+ FEM_VNEXT4(retzcbuf, func, arg0, xuiop, cr, ct);
}
int
diff --git a/usr/src/uts/common/fs/fifofs/fifosubr.c b/usr/src/uts/common/fs/fifofs/fifosubr.c
index 6e56000ffe..a908f91267 100644
--- a/usr/src/uts/common/fs/fifofs/fifosubr.c
+++ b/usr/src/uts/common/fs/fifofs/fifosubr.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2017 Joyent, Inc.
*/
/*
@@ -61,7 +62,6 @@
#if FIFODEBUG
int Fifo_fastmode = 1; /* pipes/fifos will be opened in fast mode */
int Fifo_verbose = 0; /* msg when switching out of fast mode */
-int Fifohiwat = FIFOHIWAT; /* Modifiable FIFO high water mark */
#endif
/*
@@ -196,6 +196,7 @@ fnode_constructor(void *buf, void *cdrarg, int kmflags)
fnp->fn_dest = fnp;
fnp->fn_mp = NULL;
fnp->fn_count = 0;
+ fnp->fn_hiwat = FIFOHIWAT;
fnp->fn_rsynccnt = 0;
fnp->fn_wsynccnt = 0;
fnp->fn_wwaitcnt = 0;
@@ -388,11 +389,7 @@ fifoinit(int fstype, char *name)
pipe_constructor, pipe_destructor, NULL,
(void *)(sizeof (fifodata_t)), NULL, 0);
-#if FIFODEBUG
- if (Fifohiwat < FIFOHIWAT)
- Fifohiwat = FIFOHIWAT;
-#endif /* FIFODEBUG */
- fifo_strdata.qi_minfo->mi_hiwat = Fifohiwat;
+ fifo_strdata.qi_minfo->mi_hiwat = FIFOHIWAT;
return (0);
}
@@ -614,9 +611,12 @@ fifo_stropen(vnode_t **vpp, int flag, cred_t *crp, int dotwist, int lockheld)
/*
* The other end of the pipe is almost closed so
* reject any other open on this end of the pipe
- * This only happens with a pipe mounted under namefs
+ * This normally only happens with a pipe mounted under namefs, but
+ * we can also see an open via proc/fd, which should still succeed.
+ * To indicate the proc/fd case the FKLYR flag is passed.
*/
- if ((fnp->fn_flag & (FIFOCLOSE|ISPIPE)) == (FIFOCLOSE|ISPIPE)) {
+ if ((fnp->fn_flag & (FIFOCLOSE|ISPIPE)) == (FIFOCLOSE|ISPIPE) &&
+ (flag & FKLYR) == 0) {
fifo_cleanup(oldvp, flag);
cv_broadcast(&fnp->fn_wait_cv);
if (!lockheld)
@@ -1161,7 +1161,8 @@ fifo_wakewriter(fifonode_t *fn_dest, fifolock_t *fn_lock)
int fn_dflag = fn_dest->fn_flag;
ASSERT(MUTEX_HELD(&fn_lock->flk_lock));
- ASSERT(fn_dest->fn_dest->fn_count < Fifohiwat);
+ ASSERT(fn_dest->fn_dest->fn_count < fn_dest->fn_dest->fn_hiwat);
+
if ((fn_dflag & FIFOWANTW)) {
cv_broadcast(&fn_dest->fn_wait_cv);
}
diff --git a/usr/src/uts/common/fs/fifofs/fifovnops.c b/usr/src/uts/common/fs/fifofs/fifovnops.c
index c1b4652633..ceec9bd012 100644
--- a/usr/src/uts/common/fs/fifofs/fifovnops.c
+++ b/usr/src/uts/common/fs/fifofs/fifovnops.c
@@ -28,7 +28,7 @@
*/
/*
- * Copyright 2015, Joyent, Inc.
+ * Copyright 2017, Joyent, Inc.
* Copyright (c) 2017 by Delphix. All rights reserved.
*/
@@ -104,10 +104,6 @@ static int fifo_setsecattr(struct vnode *, vsecattr_t *, int, struct cred *,
static int fifo_getsecattr(struct vnode *, vsecattr_t *, int, struct cred *,
caller_context_t *);
-/* functions local to this file */
-static boolean_t fifo_stayfast_enter(fifonode_t *);
-static void fifo_stayfast_exit(fifonode_t *);
-
/*
* Define the data structures external to this file.
*/
@@ -645,7 +641,7 @@ fifo_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *crp,
* (3) write-only FIFO with no data
* (4) no data and FNDELAY flag is set.
* Otherwise return
- * EAGAIN if FNONBLOCK is set and no data to read
+ * EAGAIN if FNONBLOCK is set and no data to read or FIFORDBLOCK is set
* EINTR if signal received while waiting for data
*
* While there is no data to read....
@@ -681,7 +677,7 @@ fifo_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *crp,
* Check for data on our input queue
*/
- while (fnp->fn_count == 0) {
+ while (fnp->fn_count == 0 || (fnp->fn_flag & FIFORDBLOCK) != 0) {
/*
* No data on first attempt and no writer, then EOF
*/
@@ -731,6 +727,7 @@ fifo_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *crp,
}
ASSERT(fnp->fn_mp != NULL);
+ VERIFY((fnp->fn_flag & FIFORDBLOCK) == 0);
/* For pipes copy should not bypass cache */
uiop->uio_extflg |= UIO_COPY_CACHED;
@@ -772,6 +769,18 @@ fifo_read(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *crp,
&fn_lock->flk_lock))
goto trywake;
+ /*
+ * If another thread snuck in and started to
+ * consume data using read-blocking out of
+ * the pipe while we were blocked in the
+ * cv_wait, then since we have already consumed
+ * some of the data out of the pipe we need
+ * to return with a short read.
+ */
+ if ((fnp->fn_flag & FIFORDBLOCK) != 0) {
+ goto trywake;
+ }
+
if (!(fnp->fn_flag & FIFOFAST))
goto stream_mode;
}
@@ -787,11 +796,11 @@ trywake:
/*
* wake up any blocked writers, processes
* sleeping on POLLWRNORM, or processes waiting for SIGPOLL
- * Note: checking for fn_count < Fifohiwat emulates
+ * Note: checking for fn_count < fn_hiwat emulates
* STREAMS functionality when low water mark is 0
*/
if (fn_dest->fn_flag & (FIFOWANTW | FIFOHIWATW) &&
- fnp->fn_count < Fifohiwat) {
+ fnp->fn_count < fn_dest->fn_hiwat) {
fifo_wakewriter(fn_dest, fn_lock);
}
goto done;
@@ -904,7 +913,7 @@ fifo_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *crp,
/*
* check to make sure we are not over high water mark
*/
- while (fn_dest->fn_count >= Fifohiwat) {
+ while (fn_dest->fn_count >= fn_dest->fn_hiwat) {
/*
* Indicate that we have gone over high
* water mark
@@ -962,7 +971,7 @@ fifo_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *crp,
* then we must break the message up into PIPE_BUF
* chunks to stay compliant with STREAMS
*/
- if (uiop->uio_resid + fn_dest->fn_count > Fifohiwat)
+ if (uiop->uio_resid + fn_dest->fn_count > fn_dest->fn_hiwat)
size = MIN(uiop->uio_resid, PIPE_BUF);
else
size = uiop->uio_resid;
@@ -1213,7 +1222,8 @@ fifo_fastioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr,
if (arg != 0) {
goto turn_fastoff;
}
- *rvalp = (fnp->fn_dest->fn_count < Fifohiwat) ? 1 : 0;
+ *rvalp = (fnp->fn_dest->fn_count < fnp->fn_dest->fn_hiwat) ?
+ 1 : 0;
mutex_exit(&fn_lock->flk_lock);
return (0);
@@ -1827,7 +1837,7 @@ fifo_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
retevents = POLLHUP;
} else if (events & (POLLWRNORM | POLLWRBAND)) {
if (events & POLLWRNORM) {
- if (fn_dest->fn_count < Fifohiwat)
+ if (fn_dest->fn_count < fn_dest->fn_hiwat)
retevents = POLLWRNORM;
else
fnp->fn_flag |= FIFOHIWATW;
@@ -1996,7 +2006,7 @@ fifo_getsecattr(struct vnode *vp, vsecattr_t *vsap, int flag, struct cred *crp,
* the lock.
* If the fifo switches into stream mode while we are waiting, return failure.
*/
-static boolean_t
+boolean_t
fifo_stayfast_enter(fifonode_t *fnp)
{
ASSERT(MUTEX_HELD(&fnp->fn_lock->flk_lock));
@@ -2018,7 +2028,7 @@ fifo_stayfast_enter(fifonode_t *fnp)
* - threads wanting to turn into stream mode waiting in fifo_fastoff(),
* - other writers threads waiting in fifo_stayfast_enter().
*/
-static void
+void
fifo_stayfast_exit(fifonode_t *fnp)
{
fifonode_t *fn_dest = fnp->fn_dest;
diff --git a/usr/src/uts/common/fs/hyprlofs/hyprlofs_dir.c b/usr/src/uts/common/fs/hyprlofs/hyprlofs_dir.c
new file mode 100644
index 0000000000..cc03f41c8d
--- /dev/null
+++ b/usr/src/uts/common/fs/hyprlofs/hyprlofs_dir.c
@@ -0,0 +1,640 @@
+/*
+ * 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.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/policy.h>
+#include <sys/fs/hyprlofs_info.h>
+
+static int hldir_make_hlnode(hlnode_t *, hlfsmount_t *, vattr_t *, enum de_op,
+ vnode_t *, hlnode_t **, cred_t *);
+static int hldiraddentry(hlnode_t *, hlnode_t *, char *);
+
+
+#define HL_HASH_SIZE 8192 /* must be power of 2 */
+#define HL_MUTEX_SIZE 64
+
+static hldirent_t *hl_hashtable[HL_HASH_SIZE];
+static kmutex_t hl_hashmutex[HL_MUTEX_SIZE];
+
+#define HL_HASH_INDEX(a) ((a) & (HL_HASH_SIZE-1))
+#define HL_MUTEX_INDEX(a) ((a) & (HL_MUTEX_SIZE-1))
+
+#define HYPRLOFS_HASH(tp, name, hash) \
+ { \
+ char Xc, *Xcp; \
+ hash = (uint_t)(uintptr_t)(tp) >> 8; \
+ for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++) \
+ hash = (hash << 4) + hash + (uint_t)Xc; \
+ }
+
+void
+hyprlofs_hash_init(void)
+{
+ int ix;
+
+ for (ix = 0; ix < HL_MUTEX_SIZE; ix++)
+ mutex_init(&hl_hashmutex[ix], NULL, MUTEX_DEFAULT, NULL);
+}
+
+static void
+hyprlofs_hash_in(hldirent_t *h)
+{
+ uint_t hash;
+ hldirent_t **prevpp;
+ kmutex_t *hmtx;
+
+ HYPRLOFS_HASH(h->hld_parent, h->hld_name, hash);
+ h->hld_hash = hash;
+ prevpp = &hl_hashtable[HL_HASH_INDEX(hash)];
+ hmtx = &hl_hashmutex[HL_MUTEX_INDEX(hash)];
+ mutex_enter(hmtx);
+ h->hld_link = *prevpp;
+ *prevpp = h;
+ mutex_exit(hmtx);
+}
+
+/* Remove hldirent *h from the hash list. */
+static void
+hyprlofs_hash_out(hldirent_t *h)
+{
+ uint_t hash;
+ hldirent_t **prevpp;
+ kmutex_t *hmtx;
+
+ hash = h->hld_hash;
+ prevpp = &hl_hashtable[HL_HASH_INDEX(hash)];
+ hmtx = &hl_hashmutex[HL_MUTEX_INDEX(hash)];
+ mutex_enter(hmtx);
+ while (*prevpp != h)
+ prevpp = &(*prevpp)->hld_link;
+ *prevpp = h->hld_link;
+ mutex_exit(hmtx);
+}
+
+static hldirent_t *
+hyprlofs_hash_lookup(char *name, hlnode_t *parent, uint_t hold,
+ hlnode_t **found)
+{
+ hldirent_t *l;
+ uint_t hash;
+ kmutex_t *hmtx;
+ hlnode_t *hnp;
+
+ HYPRLOFS_HASH(parent, name, hash);
+ hmtx = &hl_hashmutex[HL_MUTEX_INDEX(hash)];
+ mutex_enter(hmtx);
+ l = hl_hashtable[HL_HASH_INDEX(hash)];
+ while (l) {
+ if (l->hld_hash == hash && l->hld_parent == parent &&
+ strcmp(l->hld_name, name) == 0) {
+ /*
+ * Ensure that the hlnode that we put a hold on is the
+ * same one that we pass back. Thus the temp. var
+ * hnp is necessary.
+ */
+ hnp = l->hld_hlnode;
+ if (hold) {
+ ASSERT(hnp);
+ hlnode_hold(hnp);
+ }
+ if (found)
+ *found = hnp;
+ mutex_exit(hmtx);
+ return (l);
+ } else {
+ l = l->hld_link;
+ }
+ }
+ mutex_exit(hmtx);
+ return (NULL);
+}
+
+/*
+ * Search directory 'parent' for entry 'name'.
+ *
+ * The calling thread can't hold the write version of the rwlock for the
+ * directory being searched
+ *
+ * On success *foundtp points to the found hlnode with its vnode held.
+ */
+int
+hyprlofs_dirlookup(hlnode_t *parent, char *name, hlnode_t **foundtp, cred_t *cr)
+{
+ int error;
+
+ *foundtp = NULL;
+ if (parent->hln_type != VDIR)
+ return (ENOTDIR);
+
+ if ((error = hyprlofs_taccess(parent, VEXEC, cr)))
+ return (error);
+
+ if (*name == '\0') {
+ hlnode_hold(parent);
+ *foundtp = parent;
+ return (0);
+ }
+
+ /*
+ * Search the directory for the matching name. We need the lock
+ * protecting the hln_dir list so that it doesn't change out from
+ * underneath us. hyprlofs_hash_lookup() will pass back the hlnode
+ * with a hold on it.
+ */
+ if (hyprlofs_hash_lookup(name, parent, 1, foundtp) != NULL) {
+ ASSERT(*foundtp);
+ return (0);
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Enter a directory entry (either a file or subdir, depending on op) for
+ * 'name' and 'hp' into directory 'dir'
+ */
+int
+hyprlofs_direnter(
+ hlfsmount_t *hm,
+ hlnode_t *dir, /* target directory to make entry in */
+ char *name, /* name of entry */
+ enum de_op op, /* entry operation */
+ vnode_t *realvp, /* real vnode */
+ vattr_t *va,
+ hlnode_t **hpp, /* return hlnode */
+ cred_t *cr)
+{
+ hldirent_t *hdp;
+ hlnode_t *found = NULL;
+ hlnode_t *hp;
+ int error = 0;
+ char *s;
+
+ /* hln_rwlock is held to serialize direnter and dirdeletes */
+ ASSERT(RW_WRITE_HELD(&dir->hln_rwlock));
+ ASSERT(dir->hln_type == VDIR);
+
+ /* Don't allow '/' characters in pathname component */
+ for (s = name; *s; s++)
+ if (*s == '/')
+ return (EACCES);
+
+ if (name[0] == '\0')
+ panic("hyprlofs_direnter: NULL name");
+
+ /*
+ * This might be a "dangling detached directory". It could have been
+ * removed, but a reference to it kept in u_cwd. Don't bother searching
+ * it, and with any luck the user will get tired of dealing with us and
+ * cd to some absolute pathway. This is in ufs, too.
+ */
+ if (dir->hln_nlink == 0) {
+ return (ENOENT);
+ }
+
+ /* Search for the entry. Return "found" if it exists. */
+ hdp = hyprlofs_hash_lookup(name, dir, 1, &found);
+
+ if (hdp) {
+ ASSERT(found);
+ switch (op) {
+ case DE_CREATE:
+ case DE_MKDIR:
+ if (hpp) {
+ *hpp = found;
+ error = EEXIST;
+ } else {
+ hlnode_rele(found);
+ }
+ break;
+ }
+ } else {
+
+ /*
+ * The entry does not exist. Check write perms in dir to see if
+ * entry can be created.
+ */
+ if ((error = hyprlofs_taccess(dir, VWRITE, cr)))
+ return (error);
+
+ /* Make new hlnode and directory entry as required. */
+ if ((error = hldir_make_hlnode(dir, hm, va, op, realvp, &hp,
+ cr)))
+ return (error);
+
+ if ((error = hldiraddentry(dir, hp, name))) {
+ /* Unmake the inode we just made. */
+ rw_enter(&hp->hln_rwlock, RW_WRITER);
+ if ((hp->hln_type) == VDIR) {
+ ASSERT(hdp == NULL);
+ /* cleanup allocs made by hyprlofs_dirinit() */
+ hyprlofs_dirtrunc(hp);
+ }
+ mutex_enter(&hp->hln_tlock);
+ hp->hln_nlink = 0;
+ mutex_exit(&hp->hln_tlock);
+ gethrestime(&hp->hln_ctime);
+ rw_exit(&hp->hln_rwlock);
+ hlnode_rele(hp);
+ hp = NULL;
+ } else if (hpp) {
+ *hpp = hp;
+ } else {
+ hlnode_rele(hp);
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Delete entry hp of name "nm" from dir. Free dir entry space and decrement
+ * link count on hlnode(s).
+ */
+int
+hyprlofs_dirdelete(hlnode_t *dir, hlnode_t *hp, char *nm, enum dr_op op,
+ cred_t *cr)
+{
+ hldirent_t *hpdp;
+ int error;
+ size_t namelen;
+ hlnode_t *hnp;
+ timestruc_t now;
+
+ ASSERT(RW_WRITE_HELD(&dir->hln_rwlock));
+ ASSERT(RW_WRITE_HELD(&hp->hln_rwlock));
+ ASSERT(dir->hln_type == VDIR);
+
+ if (nm[0] == '\0')
+ panic("hyprlofs_dirdelete: NULL name for %p", (void *)hp);
+
+ /* return error if removing . or .. */
+ if (nm[0] == '.') {
+ if (nm[1] == '\0')
+ return (EINVAL);
+ if (nm[1] == '.' && nm[2] == '\0')
+ return (EEXIST); /* thus in ufs */
+ }
+
+ if ((error = hyprlofs_taccess(dir, VEXEC|VWRITE, cr)) != 0)
+ return (error);
+
+ if (dir->hln_dir == NULL)
+ return (ENOENT);
+
+ hpdp = hyprlofs_hash_lookup(nm, dir, 0, &hnp);
+ if (hpdp == NULL) {
+ /*
+ * If it is gone, some other thread got here first!
+ * Return error ENOENT.
+ */
+ return (ENOENT);
+ }
+
+ /*
+ * If the hlnode in the hldirent changed (shouldn't happen since we
+ * don't support rename) then original is gone, so return that status
+ * (same as UFS).
+ */
+ if (hp != hnp)
+ return (ENOENT);
+
+ hyprlofs_hash_out(hpdp);
+
+ /* Take hpdp out of the directory list. */
+ ASSERT(hpdp->hld_next != hpdp);
+ ASSERT(hpdp->hld_prev != hpdp);
+ if (hpdp->hld_prev) {
+ hpdp->hld_prev->hld_next = hpdp->hld_next;
+ }
+ if (hpdp->hld_next) {
+ hpdp->hld_next->hld_prev = hpdp->hld_prev;
+ }
+
+ /*
+ * If the roving slot pointer happens to match hpdp, point it at the
+ * previous dirent.
+ */
+ if (dir->hln_dir->hld_prev == hpdp) {
+ dir->hln_dir->hld_prev = hpdp->hld_prev;
+ }
+ ASSERT(hpdp->hld_next != hpdp);
+ ASSERT(hpdp->hld_prev != hpdp);
+
+ /* hpdp points to the correct directory entry */
+ namelen = strlen(hpdp->hld_name) + 1;
+
+ kmem_free(hpdp, sizeof (hldirent_t) + namelen);
+ dir->hln_size -= (sizeof (hldirent_t) + namelen);
+ dir->hln_dirents--;
+
+ gethrestime(&now);
+ dir->hln_mtime = now;
+ dir->hln_ctime = now;
+ hp->hln_ctime = now;
+
+ ASSERT(hp->hln_nlink > 0);
+ DECR_COUNT(&hp->hln_nlink, &hp->hln_tlock);
+ if (op == DR_RMDIR && hp->hln_type == VDIR) {
+ hyprlofs_dirtrunc(hp);
+ ASSERT(hp->hln_nlink == 0);
+ }
+ return (0);
+}
+
+/*
+ * hyprlofs_dirinit initializes a dir with '.' and '..' entries without
+ * checking perms and locking
+ */
+void
+hyprlofs_dirinit(
+ hlnode_t *parent, /* parent of directory to initialize */
+ hlnode_t *dir) /* the new directory */
+{
+ hldirent_t *dot, *dotdot;
+ timestruc_t now;
+
+ ASSERT(RW_WRITE_HELD(&parent->hln_rwlock));
+ ASSERT(dir->hln_type == VDIR);
+
+ dot = kmem_zalloc(sizeof (hldirent_t) + 2, KM_SLEEP);
+ dotdot = kmem_zalloc(sizeof (hldirent_t) + 3, KM_SLEEP);
+
+ /* Initialize the entries */
+ dot->hld_hlnode = dir;
+ dot->hld_offset = 0;
+ dot->hld_name = (char *)dot + sizeof (hldirent_t);
+ dot->hld_name[0] = '.';
+ dot->hld_parent = dir;
+ hyprlofs_hash_in(dot);
+
+ dotdot->hld_hlnode = parent;
+ dotdot->hld_offset = 1;
+ dotdot->hld_name = (char *)dotdot + sizeof (hldirent_t);
+ dotdot->hld_name[0] = '.';
+ dotdot->hld_name[1] = '.';
+ dotdot->hld_parent = dir;
+ hyprlofs_hash_in(dotdot);
+
+ /* Initialize directory entry list. */
+ dot->hld_next = dotdot;
+ dot->hld_prev = dotdot;
+ dotdot->hld_next = NULL;
+ dotdot->hld_prev = dot;
+
+ gethrestime(&now);
+ dir->hln_mtime = now;
+ dir->hln_ctime = now;
+
+ /*
+ * Since hyprlofs_dirinit is called with both dir and parent being the
+ * same for the root vnode, we need to increment this before we set
+ * hln_nlink = 2 below.
+ */
+ INCR_COUNT(&parent->hln_nlink, &parent->hln_tlock);
+ parent->hln_ctime = now;
+
+ dir->hln_dir = dot;
+ dir->hln_size = 2 * sizeof (hldirent_t) + 5; /* dot and dotdot */
+ dir->hln_dirents = 2;
+ dir->hln_nlink = 2;
+}
+
+
+/*
+ * hyprlofs_dirtrunc removes all dir entries under this dir.
+ */
+void
+hyprlofs_dirtrunc(hlnode_t *dir)
+{
+ hldirent_t *hdp;
+ hlnode_t *tp;
+ size_t namelen;
+ timestruc_t now;
+
+ ASSERT(RW_WRITE_HELD(&dir->hln_rwlock));
+ ASSERT(dir->hln_type == VDIR);
+
+ if (dir->hln_looped)
+ return;
+
+ for (hdp = dir->hln_dir; hdp; hdp = dir->hln_dir) {
+ ASSERT(hdp->hld_next != hdp);
+ ASSERT(hdp->hld_prev != hdp);
+ ASSERT(hdp->hld_hlnode);
+
+ dir->hln_dir = hdp->hld_next;
+ namelen = strlen(hdp->hld_name) + 1;
+
+ /*
+ * Adjust the link counts to account for this dir entry removal.
+ */
+ tp = hdp->hld_hlnode;
+
+ ASSERT(tp->hln_nlink > 0);
+ DECR_COUNT(&tp->hln_nlink, &tp->hln_tlock);
+
+ hyprlofs_hash_out(hdp);
+
+ kmem_free(hdp, sizeof (hldirent_t) + namelen);
+ dir->hln_size -= (sizeof (hldirent_t) + namelen);
+ dir->hln_dirents--;
+ }
+
+ gethrestime(&now);
+ dir->hln_mtime = now;
+ dir->hln_ctime = now;
+
+ ASSERT(dir->hln_dir == NULL);
+ ASSERT(dir->hln_size == 0);
+ ASSERT(dir->hln_dirents == 0);
+}
+
+static int
+hldiraddentry(
+ hlnode_t *dir, /* target directory to make entry in */
+ hlnode_t *hp, /* new hlnode */
+ char *name)
+{
+ hldirent_t *hdp, *hpdp;
+ size_t namelen, alloc_size;
+ timestruc_t now;
+
+ /*
+ * Make sure the parent dir wasn't removed from underneath the caller.
+ */
+ if (dir->hln_dir == NULL)
+ return (ENOENT);
+
+ /* Check that everything is on the same FS. */
+ if (hp->hln_vnode->v_vfsp != dir->hln_vnode->v_vfsp)
+ return (EXDEV);
+
+ /* Alloc and init dir entry */
+ namelen = strlen(name) + 1;
+ alloc_size = namelen + sizeof (hldirent_t);
+ hdp = kmem_zalloc(alloc_size, KM_NOSLEEP_LAZY);
+ if (hdp == NULL)
+ return (ENOSPC);
+
+ dir->hln_size += alloc_size;
+ dir->hln_dirents++;
+ hdp->hld_hlnode = hp;
+ hdp->hld_parent = dir;
+
+ /* The dir entry and its name were allocated sequentially. */
+ hdp->hld_name = (char *)hdp + sizeof (hldirent_t);
+ (void) strcpy(hdp->hld_name, name);
+
+ hyprlofs_hash_in(hdp);
+
+ /*
+ * Some utilities expect the size of a directory to remain fairly
+ * static. For example, a routine which unlinks files between calls to
+ * readdir(); the size of the dir changes from underneath it and so the
+ * real dir offset in bytes is invalid. To circumvent this problem, we
+ * initialize a dir entry with a phony offset, and use this offset to
+ * determine end of file in hyprlofs_readdir.
+ */
+ hpdp = dir->hln_dir->hld_prev;
+ /*
+ * Install at first empty "slot" in directory list.
+ */
+ while (hpdp->hld_next != NULL && (hpdp->hld_next->hld_offset -
+ hpdp->hld_offset) <= 1) {
+ ASSERT(hpdp->hld_next != hpdp);
+ ASSERT(hpdp->hld_prev != hpdp);
+ ASSERT(hpdp->hld_next->hld_offset > hpdp->hld_offset);
+ hpdp = hpdp->hld_next;
+ }
+ hdp->hld_offset = hpdp->hld_offset + 1;
+
+ /*
+ * If we're at the end of the dirent list and the offset (which is
+ * necessarily the largest offset in this dir) is more than twice the
+ * number of dirents, that means the dir is 50% holes. At this point
+ * we reset the slot pointer back to the beginning of the dir so we
+ * start using the holes. The idea is that if there are N dirents,
+ * there must also be N holes, so we can satisfy the next N creates by
+ * walking at most 2N entries; thus the average cost of a create is
+ * constant. Note that we use the first dirent's hld_prev as the roving
+ * slot pointer. This saves a word in every dirent.
+ */
+ if (hpdp->hld_next == NULL && hpdp->hld_offset > 2 * dir->hln_dirents)
+ dir->hln_dir->hld_prev = dir->hln_dir->hld_next;
+ else
+ dir->hln_dir->hld_prev = hdp;
+
+ ASSERT(hpdp->hld_next != hpdp);
+ ASSERT(hpdp->hld_prev != hpdp);
+
+ hdp->hld_next = hpdp->hld_next;
+ if (hdp->hld_next) {
+ hdp->hld_next->hld_prev = hdp;
+ }
+ hdp->hld_prev = hpdp;
+ hpdp->hld_next = hdp;
+
+ ASSERT(hdp->hld_next != hdp);
+ ASSERT(hdp->hld_prev != hdp);
+ ASSERT(hpdp->hld_next != hpdp);
+ ASSERT(hpdp->hld_prev != hpdp);
+
+ gethrestime(&now);
+ dir->hln_mtime = now;
+ dir->hln_ctime = now;
+
+ return (0);
+}
+
+static int
+hldir_make_hlnode(hlnode_t *dir, hlfsmount_t *hm, vattr_t *va, enum de_op op,
+ vnode_t *realvp, hlnode_t **newnode, cred_t *cr)
+{
+ hlnode_t *hp;
+ enum vtype type;
+
+ ASSERT(va != NULL);
+ ASSERT(op == DE_CREATE || op == DE_MKDIR);
+ if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) ||
+ ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
+ return (EOVERFLOW);
+ type = va->va_type;
+ hp = kmem_zalloc(sizeof (hlnode_t), KM_SLEEP);
+ hyprlofs_node_init(hm, hp, va, cr);
+
+ hp->hln_vnode->v_rdev = hp->hln_rdev = NODEV;
+ hp->hln_vnode->v_type = type;
+ hp->hln_uid = crgetuid(cr);
+
+ /*
+ * To determine the gid of the created file:
+ * If the directory's set-gid bit is set, set the gid to the gid
+ * of the parent dir, otherwise, use the process's gid.
+ */
+ if (dir->hln_mode & VSGID)
+ hp->hln_gid = dir->hln_gid;
+ else
+ hp->hln_gid = crgetgid(cr);
+
+ /*
+ * If we're creating a dir and the parent dir has the set-GID bit set,
+ * set it on the new dir. Otherwise, if the user is neither privileged
+ * nor a member of the file's new group, clear the file's set-GID bit.
+ */
+ if (dir->hln_mode & VSGID && type == VDIR)
+ hp->hln_mode |= VSGID;
+ else {
+ if ((hp->hln_mode & VSGID) &&
+ secpolicy_vnode_setids_setgids(cr, hp->hln_gid) != 0)
+ hp->hln_mode &= ~VSGID;
+ }
+
+ if (va->va_mask & AT_ATIME)
+ hp->hln_atime = va->va_atime;
+ if (va->va_mask & AT_MTIME)
+ hp->hln_mtime = va->va_mtime;
+
+ if (op == DE_MKDIR) {
+ hyprlofs_dirinit(dir, hp);
+ hp->hln_looped = 0;
+ } else {
+ hp->hln_realvp = realvp;
+ hp->hln_size = va->va_size;
+ hp->hln_looped = 1;
+ }
+
+ *newnode = hp;
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/hyprlofs/hyprlofs_subr.c b/usr/src/uts/common/fs/hyprlofs/hyprlofs_subr.c
new file mode 100644
index 0000000000..1d857309f3
--- /dev/null
+++ b/usr/src/uts/common/fs/hyprlofs/hyprlofs_subr.c
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/t_lock.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/debug.h>
+#include <sys/time.h>
+#include <sys/cmn_err.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/vfs.h>
+#include <sys/cred.h>
+#include <sys/kmem.h>
+#include <sys/atomic.h>
+#include <sys/policy.h>
+#include <sys/fs/hyprlofs_info.h>
+
+#define MODESHIFT 3
+
+/* Initialize a hlnode and add it to file list under mount point. */
+void
+hyprlofs_node_init(hlfsmount_t *hm, hlnode_t *h, vattr_t *vap, cred_t *cr)
+{
+ vnode_t *vp;
+ timestruc_t now;
+
+ ASSERT(vap != NULL);
+
+ rw_init(&h->hln_rwlock, NULL, RW_DEFAULT, NULL);
+ mutex_init(&h->hln_tlock, NULL, MUTEX_DEFAULT, NULL);
+ h->hln_mode = MAKEIMODE(vap->va_type, vap->va_mode);
+ h->hln_mask = 0;
+ h->hln_type = vap->va_type;
+ h->hln_nodeid = (ino64_t)(uint32_t)((uintptr_t)h >> 3);
+ h->hln_nlink = 1;
+ h->hln_size = 0;
+
+ if (cr == NULL) {
+ h->hln_uid = vap->va_uid;
+ h->hln_gid = vap->va_gid;
+ } else {
+ h->hln_uid = crgetuid(cr);
+ h->hln_gid = crgetgid(cr);
+ }
+
+ h->hln_fsid = hm->hlm_dev;
+ h->hln_rdev = vap->va_rdev;
+ h->hln_blksize = PAGESIZE;
+ h->hln_nblocks = 0;
+ gethrestime(&now);
+ h->hln_atime = now;
+ h->hln_mtime = now;
+ h->hln_ctime = now;
+ h->hln_seq = 0;
+ h->hln_dir = NULL;
+
+ h->hln_vnode = vn_alloc(KM_SLEEP);
+ vp = HLNTOV(h);
+ vn_setops(vp, hyprlofs_vnodeops);
+ vp->v_vfsp = hm->hlm_vfsp;
+ vp->v_type = vap->va_type;
+ vp->v_rdev = vap->va_rdev;
+ vp->v_data = (caddr_t)h;
+ mutex_enter(&hm->hlm_contents);
+ /*
+ * Increment the pseudo generation number for this hlnode. Since
+ * hlnodes are allocated and freed, there really is no particular
+ * generation number for a new hlnode. Just fake it by using a
+ * counter in each file system.
+ */
+ h->hln_gen = hm->hlm_gen++;
+
+ /*
+ * Add new hlnode to end of linked list of hlnodes for this hyprlofs
+ * Root dir is handled specially in hyprlofs_mount.
+ */
+ if (hm->hlm_rootnode != (hlnode_t *)NULL) {
+ h->hln_forw = NULL;
+ h->hln_back = hm->hlm_rootnode->hln_back;
+ h->hln_back->hln_forw = hm->hlm_rootnode->hln_back = h;
+ }
+ mutex_exit(&hm->hlm_contents);
+ vn_exists(vp);
+}
+
+int
+hyprlofs_taccess(void *vtp, int mode, cred_t *cr)
+{
+ hlnode_t *hp = vtp;
+ int shift = 0;
+
+ /* Check access based on owner, group and public perms in hlnode. */
+ if (crgetuid(cr) != hp->hln_uid) {
+ shift += MODESHIFT;
+ if (groupmember(hp->hln_gid, cr) == 0)
+ shift += MODESHIFT;
+ }
+
+ return (secpolicy_vnode_access2(cr, HLNTOV(hp), hp->hln_uid,
+ hp->hln_mode << shift, mode));
+}
diff --git a/usr/src/uts/common/fs/hyprlofs/hyprlofs_vfsops.c b/usr/src/uts/common/fs/hyprlofs/hyprlofs_vfsops.c
new file mode 100644
index 0000000000..bf80da6dbe
--- /dev/null
+++ b/usr/src/uts/common/fs/hyprlofs/hyprlofs_vfsops.c
@@ -0,0 +1,613 @@
+/*
+ * 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.
+ */
+
+/*
+ * Hyperlofs is a hybrid file system combining features of the tmpfs(7FS) and
+ * lofs(7FS) file systems. It is modeled on code from both of these file
+ * systems.
+ *
+ * The purpose is to create a high performance name space for files on which
+ * applications will compute. Given a large number of data files with various
+ * owners, we want to construct a view onto those files such that only a subset
+ * is visible to the applications and such that the view can be changed very
+ * quickly as compute progresses. Entries in the name space are not mounts and
+ * thus do not appear in the mnttab. Entries in the name space are allowed to
+ * refer to files on different backing file systems. Intermediate directories
+ * in the name space exist only in-memory, ala tmpfs. There are no leaf nodes
+ * in the name space except for entries that refer to backing files ala lofs.
+ *
+ * The name space is managed via ioctls issued on the mounted file system and
+ * is mostly read-only for the compute applications. That is, applications
+ * cannot create new files in the name space. If a file is unlinked by an
+ * application, that only removes the file from the name space, the backing
+ * file remains in place. It is possible for applications to write-through to
+ * the backing files if the file system is mounted read-write.
+ *
+ * The name space is managed via the HYPRLOFS_ADD_ENTRIES, HYPRLOFS_RM_ENTRIES,
+ * and HYPRLOFS_RM_ALL ioctls on the top-level mount.
+ *
+ * The HYPRLOFS_ADD_ENTRIES ioctl specifies path(s) to the backing file(s) and
+ * the name(s) for the file(s) in the name space. The name(s) may be path(s)
+ * which will be relative to the root of the mount and thus cannot begin with
+ * a /. If the name is a path, it does not have to correspond to any backing
+ * path. The intermediate directories will only exist in the name space. The
+ * entry(ies) will be added to the name space.
+ *
+ * The HYPRLOFS_RM_ENTRIES ioctl specifies the name(s) of the file(s) in the
+ * name space which should be removed. The name(s) may be path(s) which will
+ * be relative to the root of the mount and thus cannot begin with a /. The
+ * named entry(ies) will be removed.
+ *
+ * The HYPRLOFS_RM_ALL ioctl will remove all mappings from the name space.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/time.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/statvfs.h>
+#include <sys/mount.h>
+#include <sys/debug.h>
+#include <sys/systm.h>
+#include <sys/mntent.h>
+#include <fs/fs_subr.h>
+#include <vm/page.h>
+#include <vm/anon.h>
+#include <sys/model.h>
+#include <sys/policy.h>
+
+#include <sys/fs/swapnode.h>
+#include <sys/fs/hyprlofs_info.h>
+
+static int hyprlofsfstype;
+
+/*
+ * hyprlofs vfs operations.
+ */
+static int hyprlofsinit(int, char *);
+static int hyprlofs_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
+static int hyprlofs_unmount(vfs_t *, int, cred_t *);
+static int hyprlofs_root(vfs_t *, vnode_t **);
+static int hyprlofs_statvfs(vfs_t *, struct statvfs64 *);
+static int hyprlofs_vget(vfs_t *, vnode_t **, struct fid *);
+
+/*
+ * Loadable module wrapper
+ */
+#include <sys/modctl.h>
+
+static mntopts_t hyprlofs_mntopts;
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "hyprlofs",
+ hyprlofsinit,
+ VSW_HASPROTO|VSW_CANREMOUNT|VSW_STATS|VSW_ZMOUNT,
+ &hyprlofs_mntopts
+};
+
+static mntopts_t hyprlofs_mntopts = {
+ 0, NULL
+};
+
+/*
+ * Module linkage information
+ */
+static struct modlfs modlfs = {
+ &mod_fsops, "filesystem for hyprlofs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modlfs, NULL
+};
+
+int
+_init()
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_fini()
+{
+ int error;
+
+ error = mod_remove(&modlinkage);
+ if (error)
+ return (error);
+ /*
+ * Tear down the operations vectors
+ */
+ (void) vfs_freevfsops_by_type(hyprlofsfstype);
+ vn_freevnodeops(hyprlofs_vnodeops);
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * The following are patchable variables limiting the amount of system
+ * resources hyprlofs can use.
+ *
+ * hyprlofs_maxkmem limits the amount of kernel kmem_alloc memory hyprlofs can
+ * use for it's data structures (e.g. hlnodes, directory entries). It is set
+ * as a percentage of physical memory which is determined when hyprlofs is
+ * first used in the system.
+ *
+ * hyprlofs_minfree is the minimum amount of swap space that hyprlofs leaves for
+ * the rest of the system. If the amount of free swap space in the system
+ * (i.e. anoninfo.ani_free) drops below hyprlofs_minfree, hyprlofs anon
+ * allocations will fail.
+ */
+size_t hyprlofs_maxkmem = 0;
+size_t hyprlofs_minfree = 0;
+size_t hyprlofs_kmemspace; /* bytes of kernel heap used by all hyprlofs */
+
+static major_t hyprlofs_major;
+static minor_t hyprlofs_minor;
+static kmutex_t hyprlofs_minor_lock;
+
+/*
+ * initialize global hyprlofs locks and hashes when loading hyprlofs module
+ */
+static int
+hyprlofsinit(int fstype, char *name)
+{
+ static const fs_operation_def_t hl_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = hyprlofs_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = hyprlofs_unmount },
+ VFSNAME_ROOT, { .vfs_root = hyprlofs_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = hyprlofs_statvfs },
+ VFSNAME_VGET, { .vfs_vget = hyprlofs_vget },
+ NULL, NULL
+ };
+ int error;
+ extern void hyprlofs_hash_init();
+
+ hyprlofs_hash_init();
+ hyprlofsfstype = fstype;
+ ASSERT(hyprlofsfstype != 0);
+
+ error = vfs_setfsops(fstype, hl_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "hyprlofsinit: bad vfs ops template");
+ return (error);
+ }
+
+ error = vn_make_ops(name, hyprlofs_vnodeops_template,
+ &hyprlofs_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "hyprlofsinit: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * hyprlofs_minfree is an absolute limit of swap space which still
+ * allows other processes to execute. Set it if its not patched.
+ */
+ if (hyprlofs_minfree == 0)
+ hyprlofs_minfree = btopr(HYPRLOFSMINFREE);
+
+ if ((hyprlofs_major = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN,
+ "hyprlofsinit: Can't get unique device number.");
+ hyprlofs_major = 0;
+ }
+ mutex_init(&hyprlofs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
+ return (0);
+}
+
+static int
+hyprlofs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+ hlfsmount_t *hm = NULL;
+ hlnode_t *hp;
+ struct pathname dpn;
+ int error;
+ vattr_t rattr;
+ int got_attrs;
+
+ if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
+ return (error);
+ if (secpolicy_hyprlofs_control(cr) != 0)
+ return (EPERM);
+
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ if (uap->flags & MS_REMOUNT)
+ return (EBUSY);
+
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /* Having the resource be anything but "swap" doesn't make sense. */
+ vfs_setresource(vfsp, "swap", 0);
+
+ if ((error = pn_get(uap->dir,
+ (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE,
+ &dpn)) != 0)
+ goto out;
+
+ if ((hm = kmem_zalloc(sizeof (hlfsmount_t), KM_NOSLEEP_LAZY)) == NULL) {
+ pn_free(&dpn);
+ error = ENOMEM;
+ goto out;
+ }
+
+ /* Get an available minor device number for this mount */
+ mutex_enter(&hyprlofs_minor_lock);
+ do {
+ hyprlofs_minor = (hyprlofs_minor + 1) & L_MAXMIN32;
+ hm->hlm_dev = makedevice(hyprlofs_major, hyprlofs_minor);
+ } while (vfs_devismounted(hm->hlm_dev));
+ mutex_exit(&hyprlofs_minor_lock);
+
+ /*
+ * Set but don't bother entering the mutex since hlfsmount is not on
+ * the mount list yet.
+ */
+ mutex_init(&hm->hlm_contents, NULL, MUTEX_DEFAULT, NULL);
+
+ hm->hlm_vfsp = vfsp;
+
+ vfsp->vfs_data = (caddr_t)hm;
+ vfsp->vfs_fstype = hyprlofsfstype;
+ vfsp->vfs_dev = hm->hlm_dev;
+ vfsp->vfs_bsize = PAGESIZE;
+ vfsp->vfs_flag |= VFS_NOTRUNC;
+ vfs_make_fsid(&vfsp->vfs_fsid, hm->hlm_dev, hyprlofsfstype);
+ hm->hlm_mntpath = kmem_zalloc(dpn.pn_pathlen + 1, KM_SLEEP);
+ (void) strcpy(hm->hlm_mntpath, dpn.pn_path);
+
+ /* allocate and initialize root hlnode structure */
+ bzero(&rattr, sizeof (vattr_t));
+ rattr.va_mode = (mode_t)(S_IFDIR | 0777);
+ rattr.va_type = VDIR;
+ rattr.va_rdev = 0;
+ hp = kmem_zalloc(sizeof (hlnode_t), KM_SLEEP);
+ hyprlofs_node_init(hm, hp, &rattr, cr);
+
+ /* Get the mode, uid, and gid from the underlying mount point. */
+ rattr.va_mask = AT_MODE|AT_UID|AT_GID;
+ got_attrs = VOP_GETATTR(mvp, &rattr, 0, cr, NULL);
+
+ rw_enter(&hp->hln_rwlock, RW_WRITER);
+ HLNTOV(hp)->v_flag |= VROOT;
+
+ /*
+ * If the getattr succeeded, use its results, otherwise allow the
+ * previously set defaults to prevail.
+ */
+ if (got_attrs == 0) {
+ hp->hln_mode = rattr.va_mode;
+ hp->hln_uid = rattr.va_uid;
+ hp->hln_gid = rattr.va_gid;
+ }
+
+ /*
+ * Initialize linked list of hlnodes so that the back pointer of the
+ * root hlnode always points to the last one on the list and the
+ * forward pointer of the last node is null
+ */
+ hp->hln_back = hp;
+ hp->hln_forw = NULL;
+ hp->hln_nlink = 0;
+ hm->hlm_rootnode = hp;
+
+ hyprlofs_dirinit(hp, hp);
+
+ rw_exit(&hp->hln_rwlock);
+
+ pn_free(&dpn);
+ error = 0;
+
+out:
+ return (error);
+}
+
+static int
+hyprlofs_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+ hlfsmount_t *hm = (hlfsmount_t *)VFSTOHLM(vfsp);
+ hlnode_t *hnp, *cancel;
+ vnode_t *vp;
+ int error;
+
+ if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
+ return (error);
+ if (secpolicy_hyprlofs_control(cr) != 0)
+ return (EPERM);
+
+ /*
+ * forced unmount is not supported by this file system
+ * and thus, ENOTSUP, is being returned.
+ */
+ if (flag & MS_FORCE)
+ return (ENOTSUP);
+
+ mutex_enter(&hm->hlm_contents);
+
+ /*
+ * If there are no open files, only the root node should have a ref cnt.
+ * With hlm_contents held, nothing can be added or removed. There may
+ * be some dirty pages. To prevent fsflush from disrupting the unmount,
+ * put a hold on each node while scanning. If we find a previously
+ * referenced node, undo the holds we have placed and fail EBUSY.
+ */
+ hnp = hm->hlm_rootnode;
+ if (HLNTOV(hnp)->v_count > 1) {
+ mutex_exit(&hm->hlm_contents);
+ return (EBUSY);
+ }
+
+ for (hnp = hnp->hln_forw; hnp; hnp = hnp->hln_forw) {
+ if ((vp = HLNTOV(hnp))->v_count > 0) {
+ cancel = hm->hlm_rootnode->hln_forw;
+ while (cancel != hnp) {
+ vp = HLNTOV(cancel);
+ ASSERT(vp->v_count > 0);
+ VN_RELE(vp);
+ cancel = cancel->hln_forw;
+ }
+ mutex_exit(&hm->hlm_contents);
+ return (EBUSY);
+ }
+ VN_HOLD(vp);
+ }
+
+ /* We can drop the mutex now because no one can find this mount */
+ mutex_exit(&hm->hlm_contents);
+
+ /*
+ * Free all alloc'd memory associated with this FS. To do this, we go
+ * through the file list twice, once to remove all the dir entries, and
+ * then to remove all the files.
+ */
+
+ /* Remove all directory entries */
+ for (hnp = hm->hlm_rootnode; hnp; hnp = hnp->hln_forw) {
+ rw_enter(&hnp->hln_rwlock, RW_WRITER);
+ if (hnp->hln_type == VDIR)
+ hyprlofs_dirtrunc(hnp);
+ rw_exit(&hnp->hln_rwlock);
+ }
+
+ ASSERT(hm->hlm_rootnode);
+
+ /*
+ * All links are gone, v_count is keeping nodes in place. VN_RELE
+ * should make the node disappear, unless somebody is holding pages
+ * against it. Wait and retry until it disappears.
+ *
+ * We re-acquire the lock to prevent others who have a HOLD on a hlnode
+ * from blowing it away (in hyprlofs_inactive) while we're trying to
+ * get to it here. Once we have a HOLD on it we know it'll stick around.
+ */
+ mutex_enter(&hm->hlm_contents);
+
+ /* Remove all the files (except the rootnode) backwards. */
+ while ((hnp = hm->hlm_rootnode->hln_back) != hm->hlm_rootnode) {
+ mutex_exit(&hm->hlm_contents);
+ /* Note we handled the link count in pass 2 above. */
+ vp = HLNTOV(hnp);
+ VN_RELE(vp);
+ mutex_enter(&hm->hlm_contents);
+ /*
+ * It's still there after the RELE. Someone else like pageout
+ * has a hold on it so wait a bit and then try again.
+ */
+ if (hnp == hm->hlm_rootnode->hln_back) {
+ VN_HOLD(vp);
+ mutex_exit(&hm->hlm_contents);
+ delay(hz / 4);
+ mutex_enter(&hm->hlm_contents);
+ }
+ }
+ mutex_exit(&hm->hlm_contents);
+
+ VN_RELE(HLNTOV(hm->hlm_rootnode));
+
+ ASSERT(hm->hlm_mntpath);
+
+ kmem_free(hm->hlm_mntpath, strlen(hm->hlm_mntpath) + 1);
+
+ mutex_destroy(&hm->hlm_contents);
+ kmem_free(hm, sizeof (hlfsmount_t));
+
+ return (0);
+}
+
+/* Return root hlnode for given vnode */
+static int
+hyprlofs_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ hlfsmount_t *hm = (hlfsmount_t *)VFSTOHLM(vfsp);
+ hlnode_t *hp = hm->hlm_rootnode;
+ vnode_t *vp;
+
+ ASSERT(hp);
+
+ vp = HLNTOV(hp);
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+hyprlofs_statvfs(vfs_t *vfsp, struct statvfs64 *sbp)
+{
+ hlfsmount_t *hm = (hlfsmount_t *)VFSTOHLM(vfsp);
+ ulong_t blocks;
+ dev32_t d32;
+ zoneid_t eff_zid;
+ struct zone *zp;
+
+ /*
+ * The FS may have been mounted by the GZ on behalf of the NGZ. In
+ * that case, the hlfsmount zone_id will be the global zone. We want
+ * to show the swap cap inside the zone in this case, even though the
+ * FS was mounted by the GZ.
+ */
+ if (curproc->p_zone->zone_id != GLOBAL_ZONEUNIQID)
+ zp = curproc->p_zone;
+ else
+ zp = hm->hlm_vfsp->vfs_zone;
+
+ if (zp == NULL)
+ eff_zid = GLOBAL_ZONEUNIQID;
+ else
+ eff_zid = zp->zone_id;
+
+ sbp->f_bsize = PAGESIZE;
+ sbp->f_frsize = PAGESIZE;
+
+ /*
+ * Find the amount of available physical and memory swap
+ */
+ mutex_enter(&anoninfo_lock);
+ ASSERT(k_anoninfo.ani_max >= k_anoninfo.ani_phys_resv);
+ blocks = (ulong_t)CURRENT_TOTAL_AVAILABLE_SWAP;
+ mutex_exit(&anoninfo_lock);
+
+ if (blocks > hyprlofs_minfree)
+ sbp->f_bfree = blocks - hyprlofs_minfree;
+ else
+ sbp->f_bfree = 0;
+
+ sbp->f_bavail = sbp->f_bfree;
+
+ /*
+ * Total number of blocks is what's available plus what's been used
+ */
+ sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree);
+
+ if (eff_zid != GLOBAL_ZONEUNIQID &&
+ zp->zone_max_swap_ctl != UINT64_MAX) {
+ /*
+ * If the fs is used by a NGZ with a swap cap, then report the
+ * capped size.
+ */
+ rctl_qty_t cap, used;
+ pgcnt_t pgcap, pgused;
+
+ mutex_enter(&zp->zone_mem_lock);
+ cap = zp->zone_max_swap_ctl;
+ used = zp->zone_max_swap;
+ mutex_exit(&zp->zone_mem_lock);
+
+ pgcap = btop(cap);
+ pgused = btop(used);
+
+ sbp->f_bfree = MIN(pgcap - pgused, sbp->f_bfree);
+ sbp->f_bavail = sbp->f_bfree;
+ sbp->f_blocks = MIN(pgcap, sbp->f_blocks);
+ }
+
+ /*
+ * This is fairly inaccurate since it doesn't take into account the
+ * names stored in the directory entries.
+ */
+ sbp->f_ffree = sbp->f_files = ptob(availrmem) /
+ (sizeof (hlnode_t) + sizeof (hldirent_t));
+
+ sbp->f_favail = (fsfilcnt64_t)(sbp->f_ffree);
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sbp->f_fsid = d32;
+ (void) strcpy(sbp->f_basetype, vfssw[hyprlofsfstype].vsw_name);
+ (void) strncpy(sbp->f_fstr, hm->hlm_mntpath, sizeof (sbp->f_fstr));
+ /*
+ * ensure null termination
+ */
+ sbp->f_fstr[sizeof (sbp->f_fstr) - 1] = '\0';
+ sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sbp->f_namemax = MAXNAMELEN - 1;
+ return (0);
+}
+
+static int
+hyprlofs_vget(vfs_t *vfsp, vnode_t **vpp, struct fid *fidp)
+{
+ hlfid_t *hfid;
+ hlfsmount_t *hm = (hlfsmount_t *)VFSTOHLM(vfsp);
+ hlnode_t *hp = NULL;
+
+ hfid = (hlfid_t *)fidp;
+ *vpp = NULL;
+
+ mutex_enter(&hm->hlm_contents);
+ for (hp = hm->hlm_rootnode; hp; hp = hp->hln_forw) {
+ mutex_enter(&hp->hln_tlock);
+ if (hp->hln_nodeid == hfid->hlfid_ino) {
+ /*
+ * If the gen numbers don't match we know the file
+ * won't be found since only one hlnode can have this
+ * number at a time.
+ */
+ if (hp->hln_gen != hfid->hlfid_gen ||
+ hp->hln_nlink == 0) {
+ mutex_exit(&hp->hln_tlock);
+ mutex_exit(&hm->hlm_contents);
+ return (0);
+ }
+ *vpp = (vnode_t *)HLNTOV(hp);
+
+ VN_HOLD(*vpp);
+
+ if ((hp->hln_mode & S_ISVTX) &&
+ !(hp->hln_mode & (S_IXUSR | S_IFDIR))) {
+ mutex_enter(&(*vpp)->v_lock);
+ (*vpp)->v_flag |= VISSWAP;
+ mutex_exit(&(*vpp)->v_lock);
+ }
+ mutex_exit(&hp->hln_tlock);
+ mutex_exit(&hm->hlm_contents);
+ return (0);
+ }
+ mutex_exit(&hp->hln_tlock);
+ }
+ mutex_exit(&hm->hlm_contents);
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/hyprlofs/hyprlofs_vnops.c b/usr/src/uts/common/fs/hyprlofs/hyprlofs_vnops.c
new file mode 100644
index 0000000000..52dba31761
--- /dev/null
+++ b/usr/src/uts/common/fs/hyprlofs/hyprlofs_vnops.c
@@ -0,0 +1,1450 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/t_lock.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/kmem.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/dirent.h>
+#include <sys/pathname.h>
+#include <sys/fs/hyprlofs.h>
+#include <sys/fs/hyprlofs_info.h>
+#include <sys/mman.h>
+#include <vm/pvn.h>
+#include <sys/cmn_err.h>
+#include <sys/buf.h>
+#include <sys/policy.h>
+#include <fs/fs_subr.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+static int hyprlofs_add_entry(vnode_t *, char *, char *, cred_t *,
+ caller_context_t *);
+static int hyprlofs_rm_entry(vnode_t *, char *, cred_t *, caller_context_t *,
+ int);
+static int hyprlofs_rm_all(vnode_t *, cred_t *, caller_context_t *, int);
+static int hyprlofs_remove(vnode_t *, char *, cred_t *, caller_context_t *,
+ int);
+static int hyprlofs_get_all(vnode_t *, intptr_t, cred_t *, caller_context_t *,
+ int);
+
+/*
+ * This is a somewhat arbitrary upper limit on the number of entries we can
+ * pass in on a single add/rm ioctl call. This is only used to validate that
+ * the input list looks sane.
+ */
+#define MAX_IOCTL_PARAMS 100000
+
+static int
+hyprlofs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ vnode_t *rvp;
+ int error;
+
+ rvp = REALVP(*vpp);
+
+ if (VTOHLN(*vpp)->hln_looped == 0)
+ return (0);
+
+ /*
+ * looped back, pass through to real vnode. Need to hold new reference
+ * to vp since VOP_OPEN() may decide to release it.
+ */
+ VN_HOLD(rvp);
+ error = VOP_OPEN(&rvp, flag, cr, ct);
+ ASSERT(rvp->v_count > 1);
+ VN_RELE(rvp);
+
+ return (error);
+}
+
+static int
+hyprlofs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ct)
+{
+ if (VTOHLN(vp)->hln_looped == 0) {
+ cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
+ cleanshares(vp, ttoproc(curthread)->p_pid);
+ return (0);
+ }
+
+ return (VOP_CLOSE(REALVP(vp), flag, count, offset, cr, ct));
+}
+
+static int
+hyprlofs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+ return (VOP_READ(REALVP(vp), uiop, ioflag, cr, ct));
+}
+
+static int
+hyprlofs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ /* We don't support writing to non-regular files */
+ if (vp->v_type != VREG)
+ return (EINVAL);
+
+ if (vn_is_readonly(vp))
+ return (EROFS);
+
+ return (VOP_WRITE(REALVP(vp), uiop, ioflag, cr, ct));
+}
+
+/* ARGSUSED */
+static int
+hyprlofs_ioctl(vnode_t *vp, int cmd, intptr_t data, int flag,
+ cred_t *cr, int *rvalp, caller_context_t *ct)
+{
+ uint_t len, cnt;
+ int i, error;
+ model_t model;
+ char path[MAXPATHLEN];
+ char nm[MAXPATHLEN];
+
+ /* We only support the hyprlofs ioctls on the root vnode */
+ if (!(vp->v_flag & VROOT))
+ return (ENOTTY);
+
+ /*
+ * Check if managing hyprlofs is allowed.
+ */
+ if (secpolicy_hyprlofs_control(cr) != 0)
+ return (EPERM);
+
+ if (cmd == HYPRLOFS_ADD_ENTRIES || cmd == HYPRLOFS_RM_ENTRIES) {
+ model = get_udatamodel();
+
+ if (model == DATAMODEL_NATIVE) {
+ hyprlofs_entries_t ebuf;
+ hyprlofs_entry_t *e;
+
+ if (copyin((void *)data, &ebuf, sizeof (ebuf)))
+ return (EFAULT);
+ cnt = ebuf.hle_len;
+ if (cnt > MAX_IOCTL_PARAMS)
+ return (EINVAL);
+ len = sizeof (hyprlofs_entry_t) * cnt;
+
+ e = kmem_alloc(len, KM_SLEEP);
+ if (copyin((void *)(ebuf.hle_entries), e, len)) {
+ kmem_free(e, len);
+ return (EFAULT);
+ }
+
+ for (i = 0; i < cnt; i++) {
+ if (e[i].hle_nlen == 0 ||
+ e[i].hle_nlen >= sizeof (nm)) {
+ kmem_free(e, len);
+ return (EINVAL);
+ }
+
+ if (copyin(e[i].hle_name, nm, e[i].hle_nlen)
+ != 0) {
+ kmem_free(e, len);
+ return (EFAULT);
+ }
+ nm[e[i].hle_nlen] = '\0';
+
+ if (cmd == HYPRLOFS_ADD_ENTRIES) {
+ if (e[i].hle_plen == 0 ||
+ e[i].hle_plen >= sizeof (path)) {
+ kmem_free(e, len);
+ return (EINVAL);
+ }
+
+ if (copyin(e[i].hle_path, path,
+ e[i].hle_plen) != 0) {
+ kmem_free(e, len);
+ return (EFAULT);
+ }
+ path[e[i].hle_plen] = '\0';
+
+ if ((error = hyprlofs_add_entry(vp,
+ path, nm, cr, ct)) != 0) {
+ kmem_free(e, len);
+ return (error);
+ }
+ } else {
+ if ((error = hyprlofs_rm_entry(vp, nm,
+ cr, ct, flag)) != 0) {
+ kmem_free(e, len);
+ return (error);
+ }
+ }
+ }
+
+ kmem_free(e, len);
+ return (0);
+
+ } else {
+ hyprlofs_entries32_t ebuf32;
+ hyprlofs_entry32_t *e32;
+
+ if (copyin((void *)data, &ebuf32, sizeof (ebuf32)))
+ return (EFAULT);
+
+ cnt = ebuf32.hle_len;
+ if (cnt > MAX_IOCTL_PARAMS)
+ return (EINVAL);
+ len = sizeof (hyprlofs_entry32_t) * cnt;
+
+ e32 = kmem_alloc(len, KM_SLEEP);
+ if (copyin((void *)(unsigned long)(ebuf32.hle_entries),
+ e32, len)) {
+ kmem_free(e32, len);
+ return (EFAULT);
+ }
+
+ for (i = 0; i < cnt; i++) {
+ if (e32[i].hle_nlen == 0 ||
+ e32[i].hle_nlen >= sizeof (nm)) {
+ kmem_free(e32, len);
+ return (EINVAL);
+ }
+
+ if (copyin((void *)(unsigned long)
+ e32[i].hle_name, nm,
+ e32[i].hle_nlen) != 0) {
+ kmem_free(e32, len);
+ return (EFAULT);
+ }
+ nm[e32[i].hle_nlen] = '\0';
+
+ if (cmd == HYPRLOFS_ADD_ENTRIES) {
+ if (e32[i].hle_plen == 0 ||
+ e32[i].hle_plen >= sizeof (path)) {
+ kmem_free(e32, len);
+ return (EINVAL);
+ }
+
+ if (copyin((void *)(unsigned long)
+ e32[i].hle_path, path,
+ e32[i].hle_plen) != 0) {
+ kmem_free(e32, len);
+ return (EFAULT);
+ }
+ path[e32[i].hle_plen] = '\0';
+
+ if ((error = hyprlofs_add_entry(vp,
+ path, nm, cr, ct)) != 0) {
+ kmem_free(e32, len);
+ return (error);
+ }
+ } else {
+ if ((error = hyprlofs_rm_entry(vp, nm,
+ cr, ct, flag)) != 0) {
+ kmem_free(e32, len);
+ return (error);
+ }
+ }
+ }
+
+ kmem_free(e32, len);
+ return (0);
+ }
+ }
+
+ if (cmd == HYPRLOFS_RM_ALL) {
+ return (hyprlofs_rm_all(vp, cr, ct, flag));
+ }
+
+ if (cmd == HYPRLOFS_GET_ENTRIES) {
+ return (hyprlofs_get_all(vp, data, cr, ct, flag));
+ }
+
+ return (ENOTTY);
+}
+
+static int
+hyprlofs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ hlnode_t *tp = (hlnode_t *)VTOHLN(vp);
+ vattr_t tmp_va;
+
+ if (tp->hln_looped == 1) {
+ int error;
+
+ if ((error = VOP_GETATTR(REALVP(vp), &tmp_va, flags, cr,
+ ct)) != 0)
+ return (error);
+ }
+
+ mutex_enter(&tp->hln_tlock);
+ vap->va_type = vp->v_type;
+ vap->va_mode = tp->hln_mode & MODEMASK;
+ vap->va_uid = tp->hln_uid;
+ vap->va_gid = tp->hln_gid;
+ vap->va_fsid = tp->hln_fsid;
+ vap->va_nodeid = (ino64_t)tp->hln_nodeid;
+ vap->va_nlink = tp->hln_nlink;
+ vap->va_size = (u_offset_t)tp->hln_size;
+ vap->va_atime = tp->hln_atime;
+ vap->va_mtime = tp->hln_mtime;
+ vap->va_ctime = tp->hln_ctime;
+ vap->va_blksize = PAGESIZE;
+ vap->va_rdev = tp->hln_rdev;
+ vap->va_seq = tp->hln_seq;
+
+ if (tp->hln_looped == 1) {
+ vap->va_nblocks = tmp_va.va_nblocks;
+ } else {
+ vap->va_nblocks =
+ (fsblkcnt64_t)btodb(ptob(btopr(vap->va_size)));
+ }
+ mutex_exit(&tp->hln_tlock);
+ return (0);
+}
+
+/*ARGSUSED4*/
+static int
+hyprlofs_setattr(vnode_t *vp, vattr_t *vap, int flags,
+ cred_t *cr, caller_context_t *ct)
+{
+ hlnode_t *tp = (hlnode_t *)VTOHLN(vp);
+ int error = 0;
+ vattr_t *get;
+ long mask;
+
+ /*
+ * Cannot set these attributes
+ */
+ if ((vap->va_mask & AT_NOSET) || (vap->va_mask & AT_XVATTR))
+ return (EINVAL);
+
+ mutex_enter(&tp->hln_tlock);
+
+ get = &tp->hln_attr;
+ /*
+ * Change file access modes. Must be owner or have sufficient
+ * privileges.
+ */
+ error = secpolicy_vnode_setattr(cr, vp, vap, get, flags,
+ hyprlofs_taccess, tp);
+
+ if (error)
+ goto out;
+
+ mask = vap->va_mask;
+
+ if (mask & AT_MODE) {
+ get->va_mode &= S_IFMT;
+ get->va_mode |= vap->va_mode & ~S_IFMT;
+ }
+
+ if (mask & AT_UID)
+ get->va_uid = vap->va_uid;
+ if (mask & AT_GID)
+ get->va_gid = vap->va_gid;
+ if (mask & AT_ATIME)
+ get->va_atime = vap->va_atime;
+ if (mask & AT_MTIME)
+ get->va_mtime = vap->va_mtime;
+
+ if (mask & (AT_UID | AT_GID | AT_MODE | AT_MTIME))
+ gethrestime(&tp->hln_ctime);
+
+out:
+ mutex_exit(&tp->hln_tlock);
+ return (error);
+}
+
+static int
+hyprlofs_access(vnode_t *vp, int mode, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ hlnode_t *tp = (hlnode_t *)VTOHLN(vp);
+ int error;
+
+ if (mode & VWRITE) {
+ if (vp->v_type == VREG && vn_is_readonly(vp))
+ return (EROFS);
+ }
+ if (VTOHLN(vp)->hln_looped == 1)
+ return (VOP_ACCESS(REALVP(vp), mode, flags, cr, ct));
+
+ mutex_enter(&tp->hln_tlock);
+ error = hyprlofs_taccess(tp, mode, cr);
+ mutex_exit(&tp->hln_tlock);
+ return (error);
+}
+
+/* ARGSUSED3 */
+static int
+hyprlofs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ hlnode_t *tp = (hlnode_t *)VTOHLN(dvp);
+ hlnode_t *ntp = NULL;
+ int error;
+
+ if (VTOHLN(dvp)->hln_looped == 1)
+ return (VOP_LOOKUP(REALVP(dvp), nm, vpp, pnp, flags, rdir,
+ cr, ct, direntflags, realpnp));
+
+ if (flags & LOOKUP_XATTR)
+ return (EINVAL);
+
+ /* Null component name is a synonym for directory being searched. */
+ if (*nm == '\0') {
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
+ ASSERT(tp);
+
+ if ((error = hyprlofs_dirlookup(tp, nm, &ntp, cr)) == 0) {
+ ASSERT(ntp);
+ *vpp = HLNTOV(ntp);
+ }
+ return (error);
+}
+
+/*
+ * Create the loopback from the hyprlofs vnode to the real vnode.
+ */
+static int
+hyprlofs_loopback(vnode_t *dvp, vnode_t *rvp, char *nm, vattr_t *vap,
+ int mode, cred_t *cr, caller_context_t *ct)
+{
+ hlnode_t *parent;
+ hlfsmount_t *tm;
+ int error;
+ hlnode_t *oldtp;
+ vnode_t *vp;
+
+ parent = (hlnode_t *)VTOHLN(dvp);
+ tm = (hlfsmount_t *)VTOHLM(dvp);
+ error = 0;
+ oldtp = NULL;
+
+ if (vap->va_type == VREG && (vap->va_mode & VSVTX)) {
+ /* we don't support the sticky bit */
+ vap->va_mode &= ~VSVTX;
+ } else if (vap->va_type == VNON) {
+ return (EINVAL);
+ }
+
+ /* Null component name is a synonym for directory being searched. */
+ if (*nm == '\0') {
+ VN_HOLD(dvp);
+ oldtp = parent;
+ } else {
+ error = hyprlofs_dirlookup(parent, nm, &oldtp, cr);
+ }
+
+ if (error == 0) { /* name found */
+ ASSERT(oldtp);
+
+ rw_enter(&oldtp->hln_rwlock, RW_WRITER);
+
+ /*
+ * if create/read-only an existing directory, allow it
+ */
+ if ((oldtp->hln_type == VDIR) && (mode & VWRITE))
+ error = EISDIR;
+ else {
+ error = hyprlofs_taccess(oldtp, mode, cr);
+ }
+
+ if (error) {
+ rw_exit(&oldtp->hln_rwlock);
+ hlnode_rele(oldtp);
+ return (error);
+ }
+
+ vp = HLNTOV(oldtp);
+ rw_exit(&oldtp->hln_rwlock);
+
+ if (vp->v_type == VREG) {
+ hlnode_rele(oldtp);
+ return (EEXIST);
+ }
+
+ vnevent_create(vp, ct);
+ return (0);
+ }
+
+ if (error != ENOENT)
+ return (error);
+
+ rw_enter(&parent->hln_rwlock, RW_WRITER);
+ error = hyprlofs_direnter(tm, parent, nm, DE_CREATE, rvp, vap, NULL,
+ cr);
+ rw_exit(&parent->hln_rwlock);
+
+ return (error);
+}
+
+/*
+ * Create an in-memory directory based on the add-entry ioctl name.
+ * If the dir exists, return EEXIST but still also return node in vpp.
+ */
+static int
+hyprlofs_mkdir(vnode_t *dvp, char *nm, vattr_t *va, vnode_t **vpp, cred_t *cr)
+{
+ hlnode_t *parent = (hlnode_t *)VTOHLN(dvp);
+ hlnode_t *self = NULL;
+ hlfsmount_t *tm = (hlfsmount_t *)VTOHLM(dvp);
+ int error;
+
+ /*
+ * Might be dangling directory. Catch it here, because a ENOENT return
+ * from hyprlofs_dirlookup() is a valid return.
+ */
+ if (parent->hln_nlink == 0)
+ return (ENOENT);
+
+ error = hyprlofs_dirlookup(parent, nm, &self, cr);
+ if (error == 0) {
+ ASSERT(self);
+ hlnode_rele(self);
+ /* We can't loop in under a looped in directory */
+ if (self->hln_looped)
+ return (EACCES);
+ *vpp = HLNTOV(self);
+ return (EEXIST);
+ }
+ if (error != ENOENT)
+ return (error);
+
+ rw_enter(&parent->hln_rwlock, RW_WRITER);
+ error = hyprlofs_direnter(tm, parent, nm, DE_MKDIR, (vnode_t *)NULL,
+ va, &self, cr);
+ rw_exit(&parent->hln_rwlock);
+
+ if (error == 0 || error == EEXIST) {
+ hlnode_rele(self);
+ *vpp = HLNTOV(self);
+ }
+
+ return (error);
+}
+
+/*
+ * Loop in a file or directory into the namespace.
+ */
+static int
+hyprlofs_add_entry(vnode_t *vp, char *fspath, char *fsname,
+ cred_t *cr, caller_context_t *ct)
+{
+ int error;
+ char *p, *pnm;
+ vnode_t *realvp, *dvp;
+ vattr_t va;
+
+ /*
+ * Get vnode for the real file/dir. We'll have a hold on realvp which
+ * we won't vn_rele until hyprlofs_inactive.
+ */
+ if ((error = lookupname(fspath, UIO_SYSSPACE, FOLLOW, NULLVPP,
+ &realvp)) != 0)
+ return (error);
+
+ /* no devices allowed */
+ if (IS_DEVVP(realvp)) {
+ VN_RELE(realvp);
+ return (ENODEV);
+ }
+
+ /*
+ * realvp may be an AUTOFS node, in which case we perform a VOP_ACCESS
+ * to trigger the mount of the intended filesystem. This causes a
+ * loopback mount of the intended filesystem instead of the AUTOFS
+ * filesystem.
+ */
+ if ((error = VOP_ACCESS(realvp, 0, 0, cr, NULL)) != 0) {
+ VN_RELE(realvp);
+ return (error);
+ }
+
+ /*
+ * We're interested in the top most filesystem. This is specially
+ * important when fspath is a trigger AUTOFS node, since we're really
+ * interested in mounting the filesystem AUTOFS mounted as result of
+ * the VOP_ACCESS() call not the AUTOFS node itself.
+ */
+ if (vn_mountedvfs(realvp) != NULL) {
+ if ((error = traverse(&realvp)) != 0) {
+ VN_RELE(realvp);
+ return (error);
+ }
+ }
+
+ va.va_type = VNON;
+ /*
+ * If the target name is a path, make sure we have all of the
+ * intermediate directories, creating them if necessary.
+ */
+ dvp = vp;
+ pnm = p = fsname;
+
+ /* path cannot be absolute */
+ if (*p == '/') {
+ VN_RELE(realvp);
+ return (EINVAL);
+ }
+
+ for (p = strchr(pnm, '/'); p != NULL; p = strchr(pnm, '/')) {
+ if (va.va_type == VNON)
+ /* use the top-level dir as the template va for mkdir */
+ if ((error = VOP_GETATTR(vp, &va, 0, cr, NULL)) != 0) {
+ VN_RELE(realvp);
+ return (error);
+ }
+
+ *p = '\0';
+
+ /* Path component cannot be empty or relative */
+ if (pnm[0] == '\0' ||
+ (pnm[0] == '.' && pnm[1] == '.' && pnm[2] == '\0')) {
+ VN_RELE(realvp);
+ return (EINVAL);
+ }
+
+ if ((error = hyprlofs_mkdir(dvp, pnm, &va, &dvp, cr)) != 0 &&
+ error != EEXIST) {
+ VN_RELE(realvp);
+ return (error);
+ }
+
+ *p = '/';
+ pnm = p + 1;
+ }
+
+ /* The file name is required */
+ if (pnm[0] == '\0') {
+ VN_RELE(realvp);
+ return (EINVAL);
+ }
+
+ /* Now use the real file's va as the template va */
+ if ((error = VOP_GETATTR(realvp, &va, 0, cr, NULL)) != 0) {
+ VN_RELE(realvp);
+ return (error);
+ }
+
+ /* Make the vnode */
+ error = hyprlofs_loopback(dvp, realvp, pnm, &va, va.va_mode, cr, ct);
+ if (error != 0)
+ VN_RELE(realvp);
+ return (error);
+}
+
+/*
+ * Remove a looped in file from the namespace.
+ */
+static int
+hyprlofs_rm_entry(vnode_t *dvp, char *fsname, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ int error;
+ char *p, *pnm;
+ hlnode_t *parent;
+ hlnode_t *fndtp;
+
+ pnm = p = fsname;
+
+ /* path cannot be absolute */
+ if (*p == '/')
+ return (EINVAL);
+
+ /*
+ * If the target name is a path, get the containing dir and simple
+ * file name.
+ */
+ parent = (hlnode_t *)VTOHLN(dvp);
+ for (p = strchr(pnm, '/'); p != NULL; p = strchr(pnm, '/')) {
+ *p = '\0';
+
+ /* Path component cannot be empty or relative */
+ if (pnm[0] == '\0' ||
+ (pnm[0] == '.' && pnm[1] == '.' && pnm[2] == '\0'))
+ return (EINVAL);
+
+ if ((error = hyprlofs_dirlookup(parent, pnm, &fndtp, cr)) != 0)
+ return (error);
+
+ dvp = HLNTOV(fndtp);
+ parent = fndtp;
+ pnm = p + 1;
+ }
+
+ /* The file name is required */
+ if (pnm[0] == '\0')
+ return (EINVAL);
+
+ /* Remove the entry from the parent dir */
+ return (hyprlofs_remove(dvp, pnm, cr, ct, flags));
+}
+
+/*
+ * Remove all looped in files from the namespace.
+ */
+static int
+hyprlofs_rm_all(vnode_t *dvp, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ int error = 0;
+ hlnode_t *hp = (hlnode_t *)VTOHLN(dvp);
+ hldirent_t *hdp;
+
+ hlnode_hold(hp);
+
+ /*
+ * There's a window here where someone could have removed
+ * all the entries in the directory after we put a hold on the
+ * vnode but before we grabbed the rwlock. Just return.
+ */
+ if (hp->hln_dir == NULL) {
+ if (hp->hln_nlink) {
+ panic("empty directory 0x%p", (void *)hp);
+ /*NOTREACHED*/
+ }
+ goto done;
+ }
+
+ hdp = hp->hln_dir;
+ while (hdp) {
+ hlnode_t *fndhp;
+
+ if (strcmp(hdp->hld_name, ".") == 0 ||
+ strcmp(hdp->hld_name, "..") == 0) {
+ hdp = hdp->hld_next;
+ continue;
+ }
+
+ /* This holds the fndhp vnode */
+ error = hyprlofs_dirlookup(hp, hdp->hld_name, &fndhp, cr);
+ if (error != 0)
+ goto done;
+ hlnode_rele(fndhp);
+
+ if (fndhp->hln_looped == 0) {
+ /* recursively remove contents of this subdir */
+ if (fndhp->hln_type == VDIR) {
+ vnode_t *tvp = HLNTOV(fndhp);
+
+ error = hyprlofs_rm_all(tvp, cr, ct, flags);
+ if (error != 0)
+ goto done;
+ }
+ }
+
+ /* remove the entry */
+ error = hyprlofs_remove(dvp, hdp->hld_name, cr, ct, flags);
+ if (error != 0)
+ goto done;
+
+ hdp = hp->hln_dir;
+ }
+
+done:
+ hlnode_rele(hp);
+ return (error);
+}
+
+/*
+ * Get a list of all looped in files in the namespace.
+ */
+static int
+hyprlofs_get_all_entries(vnode_t *dvp, hyprlofs_curr_entry_t *hcp,
+ char *prefix, uint_t *pcnt, uint_t n_max,
+ cred_t *cr, caller_context_t *ct, int flags)
+{
+ int error = 0;
+ int too_big = 0;
+ uint_t cnt;
+ uint_t len;
+ hlnode_t *hp = (hlnode_t *)VTOHLN(dvp);
+ hldirent_t *hdp;
+ char *path;
+
+ cnt = *pcnt;
+ path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+ hlnode_hold(hp);
+
+ /*
+ * There's a window here where someone could have removed
+ * all the entries in the directory after we put a hold on the
+ * vnode but before we grabbed the rwlock. Just return.
+ */
+ if (hp->hln_dir == NULL) {
+ if (hp->hln_nlink) {
+ panic("empty directory 0x%p", (void *)hp);
+ /*NOTREACHED*/
+ }
+ goto done;
+ }
+
+ hdp = hp->hln_dir;
+ while (hdp) {
+ hlnode_t *fndhp;
+ vnode_t *tvp;
+
+ if (strcmp(hdp->hld_name, ".") == 0 ||
+ strcmp(hdp->hld_name, "..") == 0) {
+ hdp = hdp->hld_next;
+ continue;
+ }
+
+ /* This holds the fndhp vnode */
+ error = hyprlofs_dirlookup(hp, hdp->hld_name, &fndhp, cr);
+ if (error != 0)
+ goto done;
+ hlnode_rele(fndhp);
+
+ if (fndhp->hln_looped == 0) {
+ /* recursively get contents of this subdir */
+ VERIFY(fndhp->hln_type == VDIR);
+ tvp = HLNTOV(fndhp);
+
+ if (*prefix == '\0')
+ (void) strlcpy(path, hdp->hld_name, MAXPATHLEN);
+ else
+ (void) snprintf(path, MAXPATHLEN, "%s/%s",
+ prefix, hdp->hld_name);
+
+ error = hyprlofs_get_all_entries(tvp, hcp, path,
+ &cnt, n_max, cr, ct, flags);
+
+ if (error == E2BIG) {
+ too_big = 1;
+ error = 0;
+ }
+ if (error != 0)
+ goto done;
+ } else {
+ if (cnt < n_max) {
+ char *p;
+
+ if (*prefix == '\0')
+ (void) strlcpy(path, hdp->hld_name,
+ MAXPATHLEN);
+ else
+ (void) snprintf(path, MAXPATHLEN,
+ "%s/%s", prefix, hdp->hld_name);
+
+ len = strlen(path);
+ ASSERT(len <= MAXPATHLEN);
+ if (copyout(path, (void *)(hcp[cnt].hce_name),
+ len)) {
+ error = EFAULT;
+ goto done;
+ }
+
+ tvp = REALVP(HLNTOV(fndhp));
+ if (tvp->v_path == vn_vpath_empty) {
+ p = "<unknown>";
+ } else {
+ p = tvp->v_path;
+ }
+ len = strlen(p);
+ ASSERT(len <= MAXPATHLEN);
+ if (copyout(p, (void *)(hcp[cnt].hce_path),
+ len)) {
+ error = EFAULT;
+ goto done;
+ }
+ }
+
+ cnt++;
+ if (cnt > n_max)
+ too_big = 1;
+ }
+
+ hdp = hdp->hld_next;
+ }
+
+done:
+ hlnode_rele(hp);
+ kmem_free(path, MAXPATHLEN);
+
+ *pcnt = cnt;
+ if (error == 0 && too_big == 1)
+ error = E2BIG;
+
+ return (error);
+}
+
+/*
+ * Return a list of all looped in files in the namespace.
+ */
+static int
+hyprlofs_get_all(vnode_t *dvp, intptr_t data, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ uint_t limit, cnt;
+ int error;
+ model_t model;
+ hyprlofs_curr_entry_t *e;
+
+ model = get_udatamodel();
+
+ if (model == DATAMODEL_NATIVE) {
+ hyprlofs_curr_entries_t ebuf;
+
+ if (copyin((void *)data, &ebuf, sizeof (ebuf)))
+ return (EFAULT);
+ limit = ebuf.hce_cnt;
+ e = ebuf.hce_entries;
+ if (limit > MAX_IOCTL_PARAMS)
+ return (EINVAL);
+
+ } else {
+ hyprlofs_curr_entries32_t ebuf32;
+
+ if (copyin((void *)data, &ebuf32, sizeof (ebuf32)))
+ return (EFAULT);
+
+ limit = ebuf32.hce_cnt;
+ e = (hyprlofs_curr_entry_t *)(unsigned long)
+ (ebuf32.hce_entries);
+ if (limit > MAX_IOCTL_PARAMS)
+ return (EINVAL);
+ }
+
+ cnt = 0;
+ error = hyprlofs_get_all_entries(dvp, e, "", &cnt, limit, cr, ct,
+ flags);
+
+ if (error == 0 || error == E2BIG) {
+ if (model == DATAMODEL_NATIVE) {
+ hyprlofs_curr_entries_t ebuf;
+
+ ebuf.hce_cnt = cnt;
+ if (copyout(&ebuf, (void *)data, sizeof (ebuf)))
+ return (EFAULT);
+
+ } else {
+ hyprlofs_curr_entries32_t ebuf32;
+
+ ebuf32.hce_cnt = cnt;
+ if (copyout(&ebuf32, (void *)data, sizeof (ebuf32)))
+ return (EFAULT);
+ }
+ }
+
+ return (error);
+}
+
+/* ARGSUSED3 */
+static int
+hyprlofs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ hlnode_t *parent = (hlnode_t *)VTOHLN(dvp);
+ int error;
+ hlnode_t *hp = NULL;
+
+ /* This holds the hp vnode */
+ error = hyprlofs_dirlookup(parent, nm, &hp, cr);
+ if (error)
+ return (error);
+
+ ASSERT(hp);
+ rw_enter(&parent->hln_rwlock, RW_WRITER);
+ rw_enter(&hp->hln_rwlock, RW_WRITER);
+
+ error = hyprlofs_dirdelete(parent, hp, nm, DR_REMOVE, cr);
+
+ rw_exit(&hp->hln_rwlock);
+ rw_exit(&parent->hln_rwlock);
+ vnevent_remove(HLNTOV(hp), dvp, nm, ct);
+
+ /*
+ * We've now dropped the dir link so by rele-ing our vnode we should
+ * clean up in hyprlofs_inactive.
+ */
+ hlnode_rele(hp);
+
+ return (error);
+}
+
+/* ARGSUSED4 */
+static int
+hyprlofs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
+ caller_context_t *ct, int flags)
+{
+ hlnode_t *parent = (hlnode_t *)VTOHLN(dvp);
+ hlnode_t *self = NULL;
+ vnode_t *vp;
+ int error = 0;
+
+ /* Return error if removing . or .. */
+ if (strcmp(nm, ".") == 0)
+ return (EINVAL);
+ if (strcmp(nm, "..") == 0)
+ return (EEXIST); /* Should be ENOTEMPTY */
+ error = hyprlofs_dirlookup(parent, nm, &self, cr);
+ if (error)
+ return (error);
+
+ rw_enter(&parent->hln_rwlock, RW_WRITER);
+ rw_enter(&self->hln_rwlock, RW_WRITER);
+
+ vp = HLNTOV(self);
+ if (vp == dvp || vp == cdir) {
+ error = EINVAL;
+ goto done1;
+ }
+ if (self->hln_type != VDIR) {
+ error = ENOTDIR;
+ goto done1;
+ }
+
+ /*
+ * When a dir is looped in, we only remove the in-memory dir, not the
+ * backing dir.
+ */
+ if (self->hln_looped == 0) {
+ mutex_enter(&self->hln_tlock);
+ if (self->hln_nlink > 2) {
+ mutex_exit(&self->hln_tlock);
+ error = EEXIST;
+ goto done1;
+ }
+ mutex_exit(&self->hln_tlock);
+
+ if (vn_vfswlock(vp)) {
+ error = EBUSY;
+ goto done1;
+ }
+ if (vn_mountedvfs(vp) != NULL) {
+ error = EBUSY;
+ goto done;
+ }
+
+ /*
+ * Check for an empty directory, i.e. only includes entries for
+ * "." and ".."
+ */
+ if (self->hln_dirents > 2) {
+ error = EEXIST; /* SIGH should be ENOTEMPTY */
+ /*
+ * Update atime because checking hln_dirents is
+ * equivalent to reading the directory
+ */
+ gethrestime(&self->hln_atime);
+ goto done;
+ }
+
+ error = hyprlofs_dirdelete(parent, self, nm, DR_RMDIR, cr);
+ } else {
+ error = hyprlofs_dirdelete(parent, self, nm, DR_REMOVE, cr);
+ }
+
+done:
+ if (self->hln_looped == 0)
+ vn_vfsunlock(vp);
+done1:
+ rw_exit(&self->hln_rwlock);
+ rw_exit(&parent->hln_rwlock);
+ vnevent_rmdir(HLNTOV(self), dvp, nm, ct);
+
+ /*
+ * We've now dropped the dir link so by rele-ing our vnode we should
+ * clean up in hyprlofs_inactive.
+ */
+ hlnode_rele(self);
+
+ return (error);
+}
+
+static int
+hyprlofs_readdir(vnode_t *vp, struct uio *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ hlnode_t *hp = (hlnode_t *)VTOHLN(vp);
+ hldirent_t *hdp;
+ int error = 0;
+ size_t namelen;
+ struct dirent64 *dp;
+ ulong_t offset;
+ ulong_t total_bytes_wanted;
+ ulong_t outcount = 0;
+ ulong_t bufsize;
+ size_t reclen;
+ caddr_t outbuf;
+
+ if (VTOHLN(vp)->hln_looped == 1)
+ return (VOP_READDIR(REALVP(vp), uiop, cr, eofp, ct, flags));
+
+ if (uiop->uio_loffset >= MAXOFF_T) {
+ if (eofp)
+ *eofp = 1;
+ return (0);
+ }
+ /* assuming syscall has already called hln_rwlock */
+ ASSERT(RW_READ_HELD(&hp->hln_rwlock));
+
+ if (uiop->uio_iovcnt != 1)
+ return (EINVAL);
+
+ if (vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ /*
+ * There's a window here where someone could have removed
+ * all the entries in the directory after we put a hold on the
+ * vnode but before we grabbed the rwlock. Just return.
+ */
+ if (hp->hln_dir == NULL) {
+ if (hp->hln_nlink) {
+ panic("empty directory 0x%p", (void *)hp);
+ /*NOTREACHED*/
+ }
+ return (0);
+ }
+
+ /* Get space for multiple dir entries */
+ total_bytes_wanted = uiop->uio_iov->iov_len;
+ bufsize = total_bytes_wanted + sizeof (struct dirent64);
+ outbuf = kmem_alloc(bufsize, KM_SLEEP);
+
+ dp = (struct dirent64 *)((uintptr_t)outbuf);
+
+ offset = 0;
+ hdp = hp->hln_dir;
+ while (hdp) {
+ namelen = strlen(hdp->hld_name); /* no +1 needed */
+ offset = hdp->hld_offset;
+ if (offset >= uiop->uio_offset) {
+ reclen = DIRENT64_RECLEN(namelen);
+ if (outcount + reclen > total_bytes_wanted) {
+ if (!outcount)
+ /* Buffer too small for any entries. */
+ error = EINVAL;
+ break;
+ }
+ ASSERT(hdp->hld_hlnode != NULL);
+
+ /* zero out uninitialized bytes */
+ (void) strncpy(dp->d_name, hdp->hld_name,
+ DIRENT64_NAMELEN(reclen));
+ dp->d_reclen = (ushort_t)reclen;
+ dp->d_ino = (ino64_t)hdp->hld_hlnode->hln_nodeid;
+ dp->d_off = (offset_t)hdp->hld_offset + 1;
+ dp = (struct dirent64 *)
+ ((uintptr_t)dp + dp->d_reclen);
+ outcount += reclen;
+ ASSERT(outcount <= bufsize);
+ }
+ hdp = hdp->hld_next;
+ }
+
+ if (!error)
+ error = uiomove(outbuf, outcount, UIO_READ, uiop);
+
+ if (!error) {
+ /*
+ * If we reached the end of the list our offset should now be
+ * just past the end.
+ */
+ if (!hdp) {
+ offset += 1;
+ if (eofp)
+ *eofp = 1;
+ } else if (eofp)
+ *eofp = 0;
+ uiop->uio_offset = offset;
+ }
+ gethrestime(&hp->hln_atime);
+ kmem_free(outbuf, bufsize);
+ return (error);
+}
+
+static int
+hyprlofs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct)
+{
+ if (VTOHLN(vp)->hln_looped == 1)
+ return (VOP_FSYNC(REALVP(vp), syncflag, cr, ct));
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+hyprlofs_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+ hlnode_t *hp = (hlnode_t *)VTOHLN(vp);
+ hlfsmount_t *hm = (hlfsmount_t *)VFSTOHLM(vp->v_vfsp);
+
+ rw_enter(&hp->hln_rwlock, RW_WRITER);
+
+ mutex_enter(&hp->hln_tlock);
+ mutex_enter(&vp->v_lock);
+ ASSERT(vp->v_count >= 1);
+
+ /*
+ * If we don't have the last hold or the link count is non-zero,
+ * there's nothing to do except drop our hold.
+ */
+ if (vp->v_count > 1 || hp->hln_nlink != 0) {
+ vp->v_count--;
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&hp->hln_tlock);
+ rw_exit(&hp->hln_rwlock);
+ return;
+ }
+
+ mutex_exit(&vp->v_lock);
+ mutex_exit(&hp->hln_tlock);
+
+ /* release hold on the real vnode now */
+ if (hp->hln_looped == 1 && hp->hln_realvp != NULL)
+ VN_RELE(hp->hln_realvp);
+
+ /* Here's our chance to send invalid event while we're between locks */
+ vn_invalid(HLNTOV(hp));
+
+ mutex_enter(&hm->hlm_contents);
+ if (hp->hln_forw == NULL)
+ hm->hlm_rootnode->hln_back = hp->hln_back;
+ else
+ hp->hln_forw->hln_back = hp->hln_back;
+ hp->hln_back->hln_forw = hp->hln_forw;
+ mutex_exit(&hm->hlm_contents);
+ rw_exit(&hp->hln_rwlock);
+ rw_destroy(&hp->hln_rwlock);
+ mutex_destroy(&hp->hln_tlock);
+ vn_free(HLNTOV(hp));
+ kmem_free(hp, sizeof (hlnode_t));
+}
+
+static int
+hyprlofs_fid(vnode_t *vp, struct fid *fidp, caller_context_t *ct)
+{
+ hlnode_t *hp = (hlnode_t *)VTOHLN(vp);
+ hlfid_t *hfid;
+
+ if (VTOHLN(vp)->hln_looped == 1)
+ return (VOP_FID(REALVP(vp), fidp, ct));
+
+ if (fidp->fid_len < (sizeof (hlfid_t) - sizeof (ushort_t))) {
+ fidp->fid_len = sizeof (hlfid_t) - sizeof (ushort_t);
+ return (ENOSPC);
+ }
+
+ hfid = (hlfid_t *)fidp;
+ bzero(hfid, sizeof (hlfid_t));
+ hfid->hlfid_len = (int)sizeof (hlfid_t) - sizeof (ushort_t);
+
+ hfid->hlfid_ino = hp->hln_nodeid;
+ hfid->hlfid_gen = hp->hln_gen;
+
+ return (0);
+}
+
+static int
+hyprlofs_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *protp,
+ page_t *pl[], size_t plsz, struct seg *seg, caddr_t addr, enum seg_rw rw,
+ cred_t *cr, caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_GETPAGE(REALVP(vp), off, len, protp, pl, plsz, seg, addr,
+ rw, cr, ct));
+}
+
+int
+hyprlofs_putpage(vnode_t *vp, offset_t off, size_t len, int flags,
+ cred_t *cr, caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_PUTPAGE(REALVP(vp), off, len, flags, cr, ct));
+}
+
+static int
+hyprlofs_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp,
+ size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_MAP(REALVP(vp), off, as, addrp, len, prot, maxprot, flags,
+ cr, ct));
+}
+
+static int
+hyprlofs_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+ size_t len, uchar_t prot, uchar_t maxprot, uint_t flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_ADDMAP(REALVP(vp), off, as, addr, len, prot, maxprot,
+ flags, cr, ct));
+}
+
+static int
+hyprlofs_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr,
+ size_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_DELMAP(REALVP(vp), off, as, addr, len, prot, maxprot,
+ flags, cr, ct));
+}
+
+static int
+hyprlofs_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
+ offset_t offset, cred_t *cr, caller_context_t *ct)
+{
+ /* return EACCES to be consistent with mmap */
+ if (VTOHLN(vp)->hln_looped != 1)
+ return (EACCES);
+ return (VOP_SPACE(REALVP(vp), cmd, bfp, flag, offset, cr, ct));
+}
+
+static int
+hyprlofs_seek(vnode_t *vp, offset_t ooff, offset_t *noffp,
+ caller_context_t *ct)
+{
+ if (VTOHLN(vp)->hln_looped == 0)
+ return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
+
+ return (VOP_SEEK(REALVP(vp), ooff, noffp, ct));
+}
+
+static int
+hyprlofs_rwlock(vnode_t *vp, int write_lock, caller_context_t *ct)
+{
+ hlnode_t *hp = VTOHLN(vp);
+
+ if (hp->hln_looped == 1)
+ return (VOP_RWLOCK(REALVP(vp), write_lock, ct));
+
+ if (write_lock) {
+ rw_enter(&hp->hln_rwlock, RW_WRITER);
+ } else {
+ rw_enter(&hp->hln_rwlock, RW_READER);
+ }
+ return (write_lock);
+}
+
+static void
+hyprlofs_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct)
+{
+ hlnode_t *hp = VTOHLN(vp);
+
+ if (hp->hln_looped == 1) {
+ VOP_RWUNLOCK(REALVP(vp), write_lock, ct);
+ return;
+ }
+
+ rw_exit(&hp->hln_rwlock);
+}
+
+static int
+hyprlofs_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
+ caller_context_t *ct)
+{
+ int error;
+
+ if (VTOHLN(vp)->hln_looped == 1)
+ return (VOP_PATHCONF(REALVP(vp), cmd, valp, cr, ct));
+
+ switch (cmd) {
+ case _PC_XATTR_ENABLED:
+ case _PC_XATTR_EXISTS:
+ case _PC_SATTR_ENABLED:
+ case _PC_SATTR_EXISTS:
+ error = EINVAL;
+ break;
+ case _PC_TIMESTAMP_RESOLUTION:
+ /* nanosecond timestamp resolution */
+ *valp = 1L;
+ error = 0;
+ break;
+ default:
+ error = fs_pathconf(vp, cmd, valp, cr, ct);
+ }
+ return (error);
+}
+
+
+struct vnodeops *hyprlofs_vnodeops;
+
+const fs_operation_def_t hyprlofs_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = hyprlofs_open },
+ VOPNAME_CLOSE, { .vop_close = hyprlofs_close },
+ VOPNAME_READ, { .vop_read = hyprlofs_read },
+ VOPNAME_WRITE, { .vop_write = hyprlofs_write },
+ VOPNAME_IOCTL, { .vop_ioctl = hyprlofs_ioctl },
+ VOPNAME_GETATTR, { .vop_getattr = hyprlofs_getattr },
+ VOPNAME_SETATTR, { .vop_setattr = hyprlofs_setattr },
+ VOPNAME_ACCESS, { .vop_access = hyprlofs_access },
+ VOPNAME_LOOKUP, { .vop_lookup = hyprlofs_lookup },
+ VOPNAME_CREATE, { .error = fs_error },
+ VOPNAME_REMOVE, { .vop_remove = hyprlofs_remove },
+ VOPNAME_LINK, { .error = fs_error },
+ VOPNAME_RENAME, { .error = fs_error },
+ VOPNAME_MKDIR, { .error = fs_error },
+ VOPNAME_RMDIR, { .vop_rmdir = hyprlofs_rmdir },
+ VOPNAME_READDIR, { .vop_readdir = hyprlofs_readdir },
+ VOPNAME_SYMLINK, { .error = fs_error },
+ VOPNAME_READLINK, { .error = fs_error },
+ VOPNAME_FSYNC, { .vop_fsync = hyprlofs_fsync },
+ VOPNAME_INACTIVE, { .vop_inactive = hyprlofs_inactive },
+ VOPNAME_FID, { .vop_fid = hyprlofs_fid },
+ VOPNAME_RWLOCK, { .vop_rwlock = hyprlofs_rwlock },
+ VOPNAME_RWUNLOCK, { .vop_rwunlock = hyprlofs_rwunlock },
+ VOPNAME_SEEK, { .vop_seek = hyprlofs_seek },
+ VOPNAME_SPACE, { .vop_space = hyprlofs_space },
+ VOPNAME_GETPAGE, { .vop_getpage = hyprlofs_getpage },
+ VOPNAME_PUTPAGE, { .vop_putpage = hyprlofs_putpage },
+ VOPNAME_MAP, { .vop_map = hyprlofs_map },
+ VOPNAME_ADDMAP, { .vop_addmap = hyprlofs_addmap },
+ VOPNAME_DELMAP, { .vop_delmap = hyprlofs_delmap },
+ VOPNAME_PATHCONF, { .vop_pathconf = hyprlofs_pathconf },
+ VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support },
+ NULL, NULL
+};
diff --git a/usr/src/uts/common/fs/lookup.c b/usr/src/uts/common/fs/lookup.c
index 2e48a21150..71e2aeb48b 100644
--- a/usr/src/uts/common/fs/lookup.c
+++ b/usr/src/uts/common/fs/lookup.c
@@ -58,6 +58,7 @@
#include <sys/zone.h>
#include <sys/dnlc.h>
#include <sys/fs/snode.h>
+#include <sys/brand.h>
/* Controls whether paths are stored with vnodes. */
int vfs_vnode_path = 1;
@@ -244,6 +245,9 @@ lookuppnvp(
pp = &presrvd;
}
+ if (flags & __FLXNOAUTO)
+ lookup_flags |= __FLXNOAUTO;
+
if (auditing)
audit_anchorpath(pnp, vp == rootvp);
diff --git a/usr/src/uts/common/fs/lxproc/lxpr_subr.c b/usr/src/uts/common/fs/lxproc/lxpr_subr.c
new file mode 100644
index 0000000000..e19281fc15
--- /dev/null
+++ b/usr/src/uts/common/fs/lxproc/lxpr_subr.c
@@ -0,0 +1,524 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <sys/varargs.h>
+#include <sys/cpuvar.h>
+#include <sys/mman.h>
+#include <sys/vmsystm.h>
+#include <sys/prsystm.h>
+
+#include "lxproc.h"
+
+#define LXPRCACHE_NAME "lxpr_cache"
+
+static int lxpr_node_constructor(void *, void *, int);
+static void lxpr_node_destructor(void *, void *);
+
+static kmem_cache_t *lxpr_node_cache;
+
+struct lxpr_uiobuf {
+ uio_t *uiop;
+ char *buffer;
+ uint32_t buffsize;
+ char *pos;
+ size_t beg;
+ int error;
+};
+
+int lxpr_bufsize = 4000;
+
+struct lxpr_uiobuf *
+lxpr_uiobuf_new(uio_t *uiop)
+{
+ /* Allocate memory for both lxpr_uiobuf and output buffer */
+ int bufsize = lxpr_bufsize;
+ struct lxpr_uiobuf *uiobuf =
+ kmem_alloc(sizeof (struct lxpr_uiobuf) + bufsize, KM_SLEEP);
+
+ uiobuf->uiop = uiop;
+ uiobuf->buffer = (char *)&uiobuf[1];
+ uiobuf->buffsize = bufsize;
+ uiobuf->pos = uiobuf->buffer;
+ uiobuf->beg = 0;
+ uiobuf->error = 0;
+
+ return (uiobuf);
+}
+
+void
+lxpr_uiobuf_free(struct lxpr_uiobuf *uiobuf)
+{
+ ASSERT(uiobuf != NULL);
+ ASSERT(uiobuf->pos == uiobuf->buffer);
+
+ kmem_free(uiobuf, sizeof (struct lxpr_uiobuf) + uiobuf->buffsize);
+}
+
+void
+lxpr_uiobuf_seek(struct lxpr_uiobuf *uiobuf, offset_t offset)
+{
+ uiobuf->uiop->uio_offset = (off_t)offset;
+}
+
+void
+lxpr_uiobuf_seterr(struct lxpr_uiobuf *uiobuf, int err)
+{
+ ASSERT(uiobuf->error == 0);
+
+ uiobuf->error = err;
+}
+
+int
+lxpr_uiobuf_flush(struct lxpr_uiobuf *uiobuf)
+{
+ off_t off = uiobuf->uiop->uio_offset;
+ caddr_t uaddr = uiobuf->buffer;
+ size_t beg = uiobuf->beg;
+ size_t size = (uintptr_t)uiobuf->pos - (uintptr_t)uaddr;
+
+ if (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ ASSERT(off >= beg);
+
+ if (beg + size > off && off >= 0)
+ uiobuf->error =
+ uiomove(uaddr + (off - beg), size - (off - beg),
+ UIO_READ, uiobuf->uiop);
+
+ uiobuf->beg += size;
+ }
+
+ uiobuf->pos = uaddr;
+
+ return (uiobuf->error);
+}
+
+void
+lxpr_uiobuf_write(struct lxpr_uiobuf *uiobuf, const char *buf, size_t size)
+{
+ /* While we can still carry on */
+ while (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+ uintptr_t remain = (uintptr_t)uiobuf->buffsize -
+ ((uintptr_t)uiobuf->pos - (uintptr_t)uiobuf->buffer);
+
+ /* Enough space in buffer? */
+ if (remain >= size) {
+ bcopy(buf, uiobuf->pos, size);
+ uiobuf->pos += size;
+ return;
+ }
+
+ /* Not enough space, so copy all we can and try again */
+ bcopy(buf, uiobuf->pos, remain);
+ uiobuf->pos += remain;
+ (void) lxpr_uiobuf_flush(uiobuf);
+ buf += remain;
+ size -= remain;
+ }
+}
+
+#define TYPBUFFSIZE 256
+
+void
+lxpr_uiobuf_printf(struct lxpr_uiobuf *uiobuf, const char *fmt, ...)
+{
+ va_list args;
+ char buff[TYPBUFFSIZE];
+ int len;
+ char *buffer;
+
+ /* Can we still do any output */
+ if (uiobuf->error != 0 || uiobuf->uiop->uio_resid == 0)
+ return;
+
+ va_start(args, fmt);
+
+ /* Try using stack allocated buffer */
+ len = vsnprintf(buff, TYPBUFFSIZE, fmt, args);
+ if (len < TYPBUFFSIZE) {
+ va_end(args);
+ lxpr_uiobuf_write(uiobuf, buff, len);
+ return;
+ }
+
+ /* Not enough space in pre-allocated buffer */
+ buffer = kmem_alloc(len + 1, KM_SLEEP);
+
+ /*
+ * We know we allocated the correct amount of space
+ * so no check on the return value
+ */
+ (void) vsnprintf(buffer, len+1, fmt, args);
+ lxpr_uiobuf_write(uiobuf, buffer, len);
+ va_end(args);
+ kmem_free(buffer, len+1);
+}
+
+/*
+ * lxpr_lock():
+ *
+ * Lookup process from pid and return with p_plock and P_PR_LOCK held.
+ */
+proc_t *
+lxpr_lock(pid_t pid)
+{
+ proc_t *p;
+ kmutex_t *mp;
+
+ ASSERT(!MUTEX_HELD(&pidlock));
+
+ for (;;) {
+ mutex_enter(&pidlock);
+
+ /*
+ * If the pid is 1, we really want the zone's init process
+ */
+ p = prfind((pid == 1) ?
+ curproc->p_zone->zone_proc_initpid : pid);
+
+ if (p == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ return (NULL);
+ }
+
+ /*
+ * p_lock is persistent, but p itself is not -- it could
+ * vanish during cv_wait(). Load p->p_lock now so we can
+ * drop it after cv_wait() without referencing p.
+ */
+ mp = &p->p_lock;
+ mutex_enter(mp);
+
+ mutex_exit(&pidlock);
+
+ if (p->p_flag & SEXITING) {
+ /*
+ * This process is exiting -- let it go.
+ */
+ mutex_exit(mp);
+ return (NULL);
+ }
+
+ if (!(p->p_proc_flag & P_PR_LOCK))
+ break;
+
+ cv_wait(&pr_pid_cv[p->p_slot], mp);
+ mutex_exit(mp);
+ }
+
+ p->p_proc_flag |= P_PR_LOCK;
+ return (p);
+}
+
+/*
+ * lxpr_unlock()
+ *
+ * Unlock locked process
+ */
+void
+lxpr_unlock(proc_t *p)
+{
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(!MUTEX_HELD(&pidlock));
+
+ cv_signal(&pr_pid_cv[p->p_slot]);
+ p->p_proc_flag &= ~P_PR_LOCK;
+ mutex_exit(&p->p_lock);
+}
+
+void
+lxpr_initnodecache()
+{
+ lxpr_node_cache = kmem_cache_create(LXPRCACHE_NAME,
+ sizeof (lxpr_node_t), 0,
+ lxpr_node_constructor, lxpr_node_destructor, NULL, NULL, NULL, 0);
+}
+
+void
+lxpr_fininodecache()
+{
+ kmem_cache_destroy(lxpr_node_cache);
+}
+
+/* ARGSUSED */
+static int
+lxpr_node_constructor(void *buf, void *un, int kmflags)
+{
+ lxpr_node_t *lxpnp = buf;
+ vnode_t *vp;
+
+ vp = lxpnp->lxpr_vnode = vn_alloc(kmflags);
+ if (vp == NULL)
+ return (-1);
+
+ (void) vn_setops(vp, lxpr_vnodeops);
+ vp->v_data = lxpnp;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_node_destructor(void *buf, void *un)
+{
+ lxpr_node_t *lxpnp = buf;
+
+ vn_free(LXPTOV(lxpnp));
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them
+ * to give the inode number for an lxproc node
+ */
+ino_t
+lxpr_inode(lxpr_nodetype_t type, pid_t pid, int fd)
+{
+ if (pid == 1)
+ pid = curproc->p_zone->zone_proc_initpid;
+
+ switch (type) {
+ case LXPR_PIDDIR:
+ return (pid + 1);
+ case LXPR_PROCDIR:
+ return (maxpid + 2);
+ case LXPR_PID_FD_FD:
+ return (maxpid + 2 +
+ (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) +
+ LXPR_NFILES + fd);
+ default:
+ return (maxpid + 2 +
+ (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) +
+ type);
+ }
+}
+
+/*
+ * Return inode number of parent (directory)
+ */
+ino_t
+lxpr_parentinode(lxpr_node_t *lxpnp)
+{
+ /*
+ * If the input node is the root then the parent inode
+ * is the mounted on inode so just return our inode number
+ */
+ if (lxpnp->lxpr_type != LXPR_PROCDIR)
+ return (VTOLXP(lxpnp->lxpr_parent)->lxpr_ino);
+ else
+ return (lxpnp->lxpr_ino);
+}
+
+/*
+ * Allocate a new lxproc node
+ *
+ * This also allocates the vnode associated with it
+ */
+lxpr_node_t *
+lxpr_getnode(vnode_t *dp, lxpr_nodetype_t type, proc_t *p, int fd)
+{
+ lxpr_node_t *lxpnp;
+ vnode_t *vp;
+ user_t *up;
+ timestruc_t now;
+
+ /*
+ * Allocate a new node. It is deallocated in vop_innactive
+ */
+ lxpnp = kmem_cache_alloc(lxpr_node_cache, KM_SLEEP);
+
+ /*
+ * Set defaults (may be overridden below)
+ */
+ gethrestime(&now);
+ lxpnp->lxpr_type = type;
+ lxpnp->lxpr_realvp = NULL;
+ lxpnp->lxpr_parent = dp;
+ VN_HOLD(dp);
+ if (p != NULL) {
+ lxpnp->lxpr_pid = ((p->p_pid ==
+ curproc->p_zone->zone_proc_initpid) ? 1 : p->p_pid);
+
+ lxpnp->lxpr_time = PTOU(p)->u_start;
+ lxpnp->lxpr_uid = crgetruid(p->p_cred);
+ lxpnp->lxpr_gid = crgetrgid(p->p_cred);
+ lxpnp->lxpr_ino = lxpr_inode(type, p->p_pid, fd);
+ } else {
+ /* Pretend files without a proc belong to sched */
+ lxpnp->lxpr_pid = 0;
+ lxpnp->lxpr_time = now;
+ lxpnp->lxpr_uid = lxpnp->lxpr_gid = 0;
+ lxpnp->lxpr_ino = lxpr_inode(type, 0, 0);
+ }
+
+ /* initialize the vnode data */
+ vp = lxpnp->lxpr_vnode;
+ vn_reinit(vp);
+ vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
+ vp->v_vfsp = dp->v_vfsp;
+
+ /*
+ * Do node specific stuff
+ */
+ switch (type) {
+ case LXPR_PROCDIR:
+ vp->v_flag |= VROOT;
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by everyone */
+ break;
+
+ case LXPR_PID_CURDIR:
+ ASSERT(p != NULL);
+
+ /*
+ * Zombie check. p_stat is officially protected by pidlock,
+ * but we can't grab pidlock here because we already hold
+ * p_lock. Luckily if we look at the process exit code
+ * we see that p_stat only transisions from SRUN to SZOMB
+ * while p_lock is held. Aside from this, the only other
+ * p_stat transition that we need to be aware about is
+ * SIDL to SRUN, but that's not a problem since lxpr_lock()
+ * ignores nodes in the SIDL state so we'll never get a node
+ * that isn't already in the SRUN state.
+ */
+ if (p->p_stat == SZOMB || (p->p_flag & SEXITING) != 0) {
+ lxpnp->lxpr_realvp = NULL;
+ } else {
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ up = PTOU(p);
+ lxpnp->lxpr_realvp = up->u_cdir;
+ ASSERT(lxpnp->lxpr_realvp != NULL);
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_ROOTDIR:
+ ASSERT(p != NULL);
+ /* Zombie check. see locking comment above */
+ if (p->p_stat == SZOMB || (p->p_flag & SEXITING) != 0) {
+ lxpnp->lxpr_realvp = NULL;
+ } else {
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ up = PTOU(p);
+ lxpnp->lxpr_realvp =
+ up->u_rdir != NULL ? up->u_rdir : rootdir;
+ ASSERT(lxpnp->lxpr_realvp != NULL);
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_EXE:
+ ASSERT(p != NULL);
+ lxpnp->lxpr_realvp = p->p_exec;
+ if (lxpnp->lxpr_realvp != NULL) {
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777;
+ break;
+
+ case LXPR_SELF:
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0777; /* anyone does anything ! */
+ break;
+
+ case LXPR_PID_FD_FD:
+ ASSERT(p != NULL);
+ /* lxpr_realvp is set after we return */
+ vp->v_type = VLNK;
+ lxpnp->lxpr_mode = 0700; /* read-write-exe owner only */
+ break;
+
+ case LXPR_PID_FDDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0500; /* read-search by owner only */
+ break;
+
+ case LXPR_PIDDIR:
+ ASSERT(p != NULL);
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0511;
+ break;
+
+ case LXPR_NETDIR:
+ vp->v_type = VDIR;
+ lxpnp->lxpr_mode = 0555; /* read-search by all */
+ break;
+
+ case LXPR_PID_ENV:
+ case LXPR_PID_MEM:
+ ASSERT(p != NULL);
+ /*FALLTHRU*/
+ case LXPR_KCORE:
+ vp->v_type = VREG;
+ lxpnp->lxpr_mode = 0400; /* read-only by owner only */
+ break;
+
+ default:
+ vp->v_type = VREG;
+ lxpnp->lxpr_mode = 0444; /* read-only by all */
+ break;
+ }
+
+ return (lxpnp);
+}
+
+
+/*
+ * Free the storage obtained from lxpr_getnode().
+ */
+void
+lxpr_freenode(lxpr_node_t *lxpnp)
+{
+ ASSERT(lxpnp != NULL);
+ ASSERT(LXPTOV(lxpnp) != NULL);
+
+ /*
+ * delete any association with realvp
+ */
+ if (lxpnp->lxpr_realvp != NULL)
+ VN_RELE(lxpnp->lxpr_realvp);
+
+ /*
+ * delete any association with parent vp
+ */
+ if (lxpnp->lxpr_parent != NULL)
+ VN_RELE(lxpnp->lxpr_parent);
+
+ /*
+ * Release the lxprnode.
+ */
+ kmem_cache_free(lxpr_node_cache, lxpnp);
+}
diff --git a/usr/src/uts/common/fs/lxproc/lxpr_vfsops.c b/usr/src/uts/common/fs/lxproc/lxpr_vfsops.c
new file mode 100644
index 0000000000..1bb7bd3823
--- /dev/null
+++ b/usr/src/uts/common/fs/lxproc/lxpr_vfsops.c
@@ -0,0 +1,367 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/mode.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#include <sys/mount.h>
+#include <sys/bitmap.h>
+#include <sys/kmem.h>
+#include <sys/policy.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+
+#include "lxproc.h"
+
+/* Module level parameters */
+static int lxprocfstype;
+static dev_t lxprocdev;
+static kmutex_t lxpr_mount_lock;
+
+int nproc_highbit; /* highbit(v.v_nproc) */
+
+static int lxpr_mount(vfs_t *, vnode_t *, mounta_t *, cred_t *);
+static int lxpr_unmount(vfs_t *, int, cred_t *);
+static int lxpr_root(vfs_t *, vnode_t **);
+static int lxpr_statvfs(vfs_t *, statvfs64_t *);
+static int lxpr_init(int, char *);
+
+static vfsdef_t vfw = {
+ VFSDEF_VERSION,
+ "lxproc",
+ lxpr_init,
+ VSW_ZMOUNT,
+ NULL
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+ &mod_fsops, "generic linux procfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, (void *)&modlfs, NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int retval;
+
+ /*
+ * attempt to unload the module
+ */
+ if ((retval = mod_remove(&modlinkage)) != 0)
+ goto done;
+
+ /*
+ * destroy lxpr_node cache
+ */
+ lxpr_fininodecache();
+
+ /*
+ * clean out the vfsops and vnodeops
+ */
+ (void) vfs_freevfsops_by_type(lxprocfstype);
+ vn_freevnodeops(lxpr_vnodeops);
+
+ mutex_destroy(&lxpr_mount_lock);
+done:
+ return (retval);
+}
+
+static int
+lxpr_init(int fstype, char *name)
+{
+ static const fs_operation_def_t lxpr_vfsops_template[] = {
+ VFSNAME_MOUNT, { .vfs_mount = lxpr_mount },
+ VFSNAME_UNMOUNT, { .vfs_unmount = lxpr_unmount },
+ VFSNAME_ROOT, { .vfs_root = lxpr_root },
+ VFSNAME_STATVFS, { .vfs_statvfs = lxpr_statvfs },
+ NULL, NULL
+ };
+ extern const fs_operation_def_t lxpr_vnodeops_template[];
+ int error;
+ major_t dev;
+
+ nproc_highbit = highbit(v.v_proc);
+ lxprocfstype = fstype;
+ ASSERT(lxprocfstype != 0);
+
+ mutex_init(&lxpr_mount_lock, NULL, MUTEX_DEFAULT, NULL);
+
+ /*
+ * Associate VFS ops vector with this fstype.
+ */
+ error = vfs_setfsops(fstype, lxpr_vfsops_template, NULL);
+ if (error != 0) {
+ cmn_err(CE_WARN, "lxpr_init: bad vfs ops template");
+ return (error);
+ }
+
+ /*
+ * Set up vnode ops vector too.
+ */
+ error = vn_make_ops(name, lxpr_vnodeops_template, &lxpr_vnodeops);
+ if (error != 0) {
+ (void) vfs_freevfsops_by_type(fstype);
+ cmn_err(CE_WARN, "lxpr_init: bad vnode ops template");
+ return (error);
+ }
+
+ /*
+ * Assign a unique "device" number (used by stat(2)).
+ */
+ if ((dev = getudev()) == (major_t)-1) {
+ cmn_err(CE_WARN, "lxpr_init: can't get unique device number");
+ dev = 0;
+ }
+
+ /*
+ * Make the pseudo device
+ */
+ lxprocdev = makedevice(dev, 0);
+
+ /*
+ * Initialize cache for lxpr_nodes
+ */
+ lxpr_initnodecache();
+
+ return (0);
+}
+
+static int
+lxpr_mount(vfs_t *vfsp, vnode_t *mvp, mounta_t *uap, cred_t *cr)
+{
+ lxpr_mnt_t *lxpr_mnt;
+ zone_t *zone = curproc->p_zone;
+ ldi_ident_t li;
+ int err;
+
+ /*
+ * must be root to mount
+ */
+ if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+ return (EPERM);
+
+ /*
+ * mount point must be a directory
+ */
+ if (mvp->v_type != VDIR)
+ return (ENOTDIR);
+
+ if (zone == global_zone) {
+ zone_t *mntzone;
+
+ mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
+ zone_rele(mntzone);
+ if (zone != mntzone)
+ return (EBUSY);
+ }
+
+ /*
+ * Having the resource be anything but "lxproc" doesn't make sense
+ */
+ vfs_setresource(vfsp, "lxproc", 0);
+
+ lxpr_mnt = kmem_alloc(sizeof (*lxpr_mnt), KM_SLEEP);
+
+ if ((err = ldi_ident_from_mod(&modlinkage, &li)) != 0) {
+ kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
+ return (err);
+ }
+
+ lxpr_mnt->lxprm_li = li;
+
+ mutex_enter(&lxpr_mount_lock);
+
+ /*
+ * Ensure we don't allow overlaying mounts
+ */
+ mutex_enter(&mvp->v_lock);
+ if ((uap->flags & MS_OVERLAY) == 0 &&
+ (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+ mutex_exit(&mvp->v_lock);
+ mutex_exit(&lxpr_mount_lock);
+ kmem_free(lxpr_mnt, sizeof ((*lxpr_mnt)));
+ return (EBUSY);
+ }
+ mutex_exit(&mvp->v_lock);
+
+ /*
+ * allocate the first vnode
+ */
+ zone_hold(lxpr_mnt->lxprm_zone = zone);
+
+ /* Arbitrarily set the parent vnode to the mounted over directory */
+ lxpr_mnt->lxprm_node = lxpr_getnode(mvp, LXPR_PROCDIR, NULL, 0);
+
+ /* Correctly set the fs for the root node */
+ lxpr_mnt->lxprm_node->lxpr_vnode->v_vfsp = vfsp;
+
+ vfs_make_fsid(&vfsp->vfs_fsid, lxprocdev, lxprocfstype);
+ vfsp->vfs_bsize = DEV_BSIZE;
+ vfsp->vfs_fstype = lxprocfstype;
+ vfsp->vfs_data = (caddr_t)lxpr_mnt;
+ vfsp->vfs_dev = lxprocdev;
+
+ mutex_exit(&lxpr_mount_lock);
+
+ return (0);
+}
+
+static int
+lxpr_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+ lxpr_mnt_t *lxpr_mnt = (lxpr_mnt_t *)vfsp->vfs_data;
+ vnode_t *vp;
+ int count;
+
+ ASSERT(lxpr_mnt != NULL);
+ vp = LXPTOV(lxpr_mnt->lxprm_node);
+
+ mutex_enter(&lxpr_mount_lock);
+
+ /*
+ * must be root to unmount
+ */
+ if (secpolicy_fs_unmount(cr, vfsp) != 0) {
+ mutex_exit(&lxpr_mount_lock);
+ return (EPERM);
+ }
+
+ /*
+ * forced unmount is not supported by this file system
+ */
+ if (flag & MS_FORCE) {
+ mutex_exit(&lxpr_mount_lock);
+ return (ENOTSUP);
+ }
+
+ /*
+ * Ensure that no vnodes are in use on this mount point.
+ */
+ mutex_enter(&vp->v_lock);
+ count = vp->v_count;
+ mutex_exit(&vp->v_lock);
+ if (count > 1) {
+ mutex_exit(&lxpr_mount_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * purge the dnlc cache for vnode entries
+ * associated with this file system
+ */
+ count = dnlc_purge_vfsp(vfsp, 0);
+
+ /*
+ * free up the lxprnode
+ */
+ lxpr_freenode(lxpr_mnt->lxprm_node);
+ zone_rele(lxpr_mnt->lxprm_zone);
+ kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
+
+ mutex_exit(&lxpr_mount_lock);
+
+ return (0);
+}
+
+static int
+lxpr_root(vfs_t *vfsp, vnode_t **vpp)
+{
+ lxpr_node_t *lxpnp = ((lxpr_mnt_t *)vfsp->vfs_data)->lxprm_node;
+ vnode_t *vp = LXPTOV(lxpnp);
+
+ VN_HOLD(vp);
+ *vpp = vp;
+ return (0);
+}
+
+static int
+lxpr_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+ int n;
+ dev32_t d32;
+ extern uint_t nproc;
+
+ n = v.v_proc - nproc;
+
+ bzero((caddr_t)sp, sizeof (*sp));
+ sp->f_bsize = DEV_BSIZE;
+ sp->f_frsize = DEV_BSIZE;
+ sp->f_blocks = (fsblkcnt64_t)0;
+ sp->f_bfree = (fsblkcnt64_t)0;
+ sp->f_bavail = (fsblkcnt64_t)0;
+ sp->f_files = (fsfilcnt64_t)v.v_proc + 2;
+ sp->f_ffree = (fsfilcnt64_t)n;
+ sp->f_favail = (fsfilcnt64_t)n;
+ (void) cmpldev(&d32, vfsp->vfs_dev);
+ sp->f_fsid = d32;
+ /* It is guaranteed that vsw_name will fit in f_basetype */
+ (void) strcpy(sp->f_basetype, vfssw[lxprocfstype].vsw_name);
+ sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+ sp->f_namemax = 64; /* quite arbitrary */
+
+ (void) strcpy(sp->f_fstr, "lxproc");
+
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/lxproc/lxpr_vnops.c b/usr/src/uts/common/fs/lxproc/lxpr_vnops.c
new file mode 100644
index 0000000000..60b3d52f09
--- /dev/null
+++ b/usr/src/uts/common/fs/lxproc/lxpr_vnops.c
@@ -0,0 +1,3105 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2017 Joyent, Inc.
+ */
+
+/*
+ * lxproc -- a loosely Linux-compatible /proc
+ *
+ * We have -- confusingly -- two implementations of Linux /proc. One is to
+ * support the LX brand with a Linux /proc entirely compatible with the Linux
+ * world view; the other -- this one -- is to support native (but Linux-borne)
+ * programs that wish to view the native system via the Linux /proc model. So
+ * the aspiration here is to provide something that sufficiently approximates
+ * the Linux /proc implementation for purposes of offering some compatibility
+ * for simple Linux /proc readers (e.g., ps/top/htop). However, it is not
+ * intended to exactly mimic Linux semantics; when choosing between offering
+ * compatibility and telling the truth, we emphatically pick the truth. A
+ * particular glaring example of this is the Linux notion of "tasks" (that is,
+ * threads), which -- due to historical misadventures on Linux -- allocate their
+ * identifiers from the process identifier space. (That is, each thread has in
+ * effect a pid.) Some Linux /proc readers have come to depend on this
+ * attribute, and become confused when threads appear with proper identifiers,
+ * so we simply opt for the pre-2.6 behavior, and do not present the tasks
+ * directory at all. Similarly, when choosing between offering compatibility
+ * and remaining consistent with our broader security model, we (obviously)
+ * choose security over compatibility. In short, this is meant to be a best
+ * effort -- no more -- and as such, it should not be unified with the much
+ * more complete Linux /proc implementation found in the LX brand.
+ */
+
+#include <sys/cpupart.h>
+#include <sys/cpuvar.h>
+#include <sys/session.h>
+#include <sys/vmparam.h>
+#include <sys/mman.h>
+#include <vm/rm.h>
+#include <vm/seg_vn.h>
+#include <sys/sdt.h>
+#include <sys/strlog.h>
+#include <sys/stropts.h>
+#include <sys/cmn_err.h>
+#include <sys/x86_archext.h>
+#include <sys/archsystm.h>
+#include <sys/fp.h>
+#include <sys/pool_pset.h>
+#include <sys/pset.h>
+#include <sys/zone.h>
+#include <sys/pghw.h>
+#include <sys/vfs_opreg.h>
+
+/* Dependent on procfs */
+extern kthread_t *prchoose(proc_t *);
+
+#include "lxproc.h"
+
+extern pgcnt_t swapfs_minfree;
+extern time_t boot_time;
+
+/*
+ * Pointer to the vnode ops vector for this fs.
+ * This is instantiated in lxprinit() in lxpr_vfsops.c
+ */
+vnodeops_t *lxpr_vnodeops;
+
+static int lxpr_open(vnode_t **, int, cred_t *, caller_context_t *);
+static int lxpr_close(vnode_t *, int, int, offset_t, cred_t *,
+ caller_context_t *);
+static int lxpr_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxpr_getattr(vnode_t *, vattr_t *, int, cred_t *,
+ caller_context_t *);
+static int lxpr_access(vnode_t *, int, int, cred_t *, caller_context_t *);
+static int lxpr_lookup(vnode_t *, char *, vnode_t **,
+ pathname_t *, int, vnode_t *, cred_t *, caller_context_t *, int *,
+ pathname_t *);
+static int lxpr_readdir(vnode_t *, uio_t *, cred_t *, int *,
+ caller_context_t *, int);
+static int lxpr_readlink(vnode_t *, uio_t *, cred_t *, caller_context_t *);
+static int lxpr_cmp(vnode_t *, vnode_t *, caller_context_t *);
+static int lxpr_realvp(vnode_t *, vnode_t **, caller_context_t *);
+static int lxpr_sync(void);
+static void lxpr_inactive(vnode_t *, cred_t *, caller_context_t *);
+
+static vnode_t *lxpr_lookup_procdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_piddir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_not_a_dir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_fddir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_netdir(vnode_t *, char *);
+
+static int lxpr_readdir_procdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_piddir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_not_a_dir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_fddir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_netdir(lxpr_node_t *, uio_t *, int *);
+
+static void lxpr_read_invalid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_empty(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cpuinfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_isdir(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_fd(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_kmsg(lxpr_node_t *, lxpr_uiobuf_t *, ldi_handle_t);
+static void lxpr_read_loadavg(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_meminfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_mounts(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_partitions(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_uptime(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_version(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_pid_cmdline(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_maps(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_statm(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_status(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_net_arp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_dev(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_dev_mcast(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_igmp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ip_mr_cache(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ip_mr_vif(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_mcfilter(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_netstat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_raw(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_route(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_rpc(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_rt_cache(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_sockstat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_snmp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_tcp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_udp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_unix(lxpr_node_t *, lxpr_uiobuf_t *);
+
+/*
+ * Simple conversion
+ */
+#define btok(x) ((x) >> 10) /* bytes to kbytes */
+#define ptok(x) ((x) << (PAGESHIFT - 10)) /* pages to kbytes */
+
+/*
+ * The lxproc vnode operations vector
+ */
+const fs_operation_def_t lxpr_vnodeops_template[] = {
+ VOPNAME_OPEN, { .vop_open = lxpr_open },
+ VOPNAME_CLOSE, { .vop_close = lxpr_close },
+ VOPNAME_READ, { .vop_read = lxpr_read },
+ VOPNAME_GETATTR, { .vop_getattr = lxpr_getattr },
+ VOPNAME_ACCESS, { .vop_access = lxpr_access },
+ VOPNAME_LOOKUP, { .vop_lookup = lxpr_lookup },
+ VOPNAME_READDIR, { .vop_readdir = lxpr_readdir },
+ VOPNAME_READLINK, { .vop_readlink = lxpr_readlink },
+ VOPNAME_FSYNC, { .error = lxpr_sync },
+ VOPNAME_SEEK, { .error = lxpr_sync },
+ VOPNAME_INACTIVE, { .vop_inactive = lxpr_inactive },
+ VOPNAME_CMP, { .vop_cmp = lxpr_cmp },
+ VOPNAME_REALVP, { .vop_realvp = lxpr_realvp },
+ NULL, NULL
+};
+
+/*
+ * file contents of an lxproc directory.
+ */
+static lxpr_dirent_t lxpr_dir[] = {
+ { LXPR_CMDLINE, "cmdline" },
+ { LXPR_CPUINFO, "cpuinfo" },
+ { LXPR_DEVICES, "devices" },
+ { LXPR_DMA, "dma" },
+ { LXPR_FILESYSTEMS, "filesystems" },
+ { LXPR_INTERRUPTS, "interrupts" },
+ { LXPR_IOPORTS, "ioports" },
+ { LXPR_KCORE, "kcore" },
+ { LXPR_KMSG, "kmsg" },
+ { LXPR_LOADAVG, "loadavg" },
+ { LXPR_MEMINFO, "meminfo" },
+ { LXPR_MOUNTS, "mounts" },
+ { LXPR_NETDIR, "net" },
+ { LXPR_PARTITIONS, "partitions" },
+ { LXPR_SELF, "self" },
+ { LXPR_STAT, "stat" },
+ { LXPR_UPTIME, "uptime" },
+ { LXPR_VERSION, "version" }
+};
+
+#define PROCDIRFILES (sizeof (lxpr_dir) / sizeof (lxpr_dir[0]))
+
+/*
+ * Contents of an /lxproc/<pid> directory.
+ */
+static lxpr_dirent_t piddir[] = {
+ { LXPR_PID_CMDLINE, "cmdline" },
+ { LXPR_PID_CPU, "cpu" },
+ { LXPR_PID_CURDIR, "cwd" },
+ { LXPR_PID_ENV, "environ" },
+ { LXPR_PID_EXE, "exe" },
+ { LXPR_PID_MAPS, "maps" },
+ { LXPR_PID_MEM, "mem" },
+ { LXPR_PID_ROOTDIR, "root" },
+ { LXPR_PID_STAT, "stat" },
+ { LXPR_PID_STATM, "statm" },
+ { LXPR_PID_STATUS, "status" },
+ { LXPR_PID_FDDIR, "fd" }
+};
+
+#define PIDDIRFILES (sizeof (piddir) / sizeof (piddir[0]))
+
+/*
+ * contents of /lxproc/net directory
+ */
+static lxpr_dirent_t netdir[] = {
+ { LXPR_NET_ARP, "arp" },
+ { LXPR_NET_DEV, "dev" },
+ { LXPR_NET_DEV_MCAST, "dev_mcast" },
+ { LXPR_NET_IGMP, "igmp" },
+ { LXPR_NET_IP_MR_CACHE, "ip_mr_cache" },
+ { LXPR_NET_IP_MR_VIF, "ip_mr_vif" },
+ { LXPR_NET_MCFILTER, "mcfilter" },
+ { LXPR_NET_NETSTAT, "netstat" },
+ { LXPR_NET_RAW, "raw" },
+ { LXPR_NET_ROUTE, "route" },
+ { LXPR_NET_RPC, "rpc" },
+ { LXPR_NET_RT_CACHE, "rt_cache" },
+ { LXPR_NET_SOCKSTAT, "sockstat" },
+ { LXPR_NET_SNMP, "snmp" },
+ { LXPR_NET_STAT, "stat" },
+ { LXPR_NET_TCP, "tcp" },
+ { LXPR_NET_UDP, "udp" },
+ { LXPR_NET_UNIX, "unix" }
+};
+
+#define NETDIRFILES (sizeof (netdir) / sizeof (netdir[0]))
+
+/*
+ * These are the major signal number differences between Linux and native:
+ *
+ * ====================================
+ * | Number | Linux | Native |
+ * | ====== | ========= | ========== |
+ * | 7 | SIGBUS | SIGEMT |
+ * | 10 | SIGUSR1 | SIGBUS |
+ * | 12 | SIGUSR2 | SIGSYS |
+ * | 16 | SIGSTKFLT | SIGUSR1 |
+ * | 17 | SIGCHLD | SIGUSR2 |
+ * | 18 | SIGCONT | SIGCHLD |
+ * | 19 | SIGSTOP | SIGPWR |
+ * | 20 | SIGTSTP | SIGWINCH |
+ * | 21 | SIGTTIN | SIGURG |
+ * | 22 | SIGTTOU | SIGPOLL |
+ * | 23 | SIGURG | SIGSTOP |
+ * | 24 | SIGXCPU | SIGTSTP |
+ * | 25 | SIGXFSZ | SIGCONT |
+ * | 26 | SIGVTALARM | SIGTTIN |
+ * | 27 | SIGPROF | SIGTTOU |
+ * | 28 | SIGWINCH | SIGVTALARM |
+ * | 29 | SIGPOLL | SIGPROF |
+ * | 30 | SIGPWR | SIGXCPU |
+ * | 31 | SIGSYS | SIGXFSZ |
+ * ====================================
+ *
+ * Not every Linux signal maps to a native signal, nor does every native
+ * signal map to a Linux counterpart. However, when signals do map, the
+ * mapping is unique.
+ */
+static int
+lxpr_sigmap[NSIG] = {
+ 0,
+ LX_SIGHUP,
+ LX_SIGINT,
+ LX_SIGQUIT,
+ LX_SIGILL,
+ LX_SIGTRAP,
+ LX_SIGABRT,
+ LX_SIGSTKFLT,
+ LX_SIGFPE,
+ LX_SIGKILL,
+ LX_SIGBUS,
+ LX_SIGSEGV,
+ LX_SIGSYS,
+ LX_SIGPIPE,
+ LX_SIGALRM,
+ LX_SIGTERM,
+ LX_SIGUSR1,
+ LX_SIGUSR2,
+ LX_SIGCHLD,
+ LX_SIGPWR,
+ LX_SIGWINCH,
+ LX_SIGURG,
+ LX_SIGPOLL,
+ LX_SIGSTOP,
+ LX_SIGTSTP,
+ LX_SIGCONT,
+ LX_SIGTTIN,
+ LX_SIGTTOU,
+ LX_SIGVTALRM,
+ LX_SIGPROF,
+ LX_SIGXCPU,
+ LX_SIGXFSZ,
+ -1, /* 32: illumos SIGWAITING */
+ -1, /* 33: illumos SIGLWP */
+ -1, /* 34: illumos SIGFREEZE */
+ -1, /* 35: illumos SIGTHAW */
+ -1, /* 36: illumos SIGCANCEL */
+ -1, /* 37: illumos SIGLOST */
+ -1, /* 38: illumos SIGXRES */
+ -1, /* 39: illumos SIGJVM1 */
+ -1, /* 40: illumos SIGJVM2 */
+ -1, /* 41: illumos SIGINFO */
+ LX_SIGRTMIN, /* 42: illumos _SIGRTMIN */
+ LX_SIGRTMIN + 1,
+ LX_SIGRTMIN + 2,
+ LX_SIGRTMIN + 3,
+ LX_SIGRTMIN + 4,
+ LX_SIGRTMIN + 5,
+ LX_SIGRTMIN + 6,
+ LX_SIGRTMIN + 7,
+ LX_SIGRTMIN + 8,
+ LX_SIGRTMIN + 9,
+ LX_SIGRTMIN + 10,
+ LX_SIGRTMIN + 11,
+ LX_SIGRTMIN + 12,
+ LX_SIGRTMIN + 13,
+ LX_SIGRTMIN + 14,
+ LX_SIGRTMIN + 15,
+ LX_SIGRTMIN + 16,
+ LX_SIGRTMIN + 17,
+ LX_SIGRTMIN + 18,
+ LX_SIGRTMIN + 19,
+ LX_SIGRTMIN + 20,
+ LX_SIGRTMIN + 21,
+ LX_SIGRTMIN + 22,
+ LX_SIGRTMIN + 23,
+ LX_SIGRTMIN + 24,
+ LX_SIGRTMIN + 25,
+ LX_SIGRTMIN + 26,
+ LX_SIGRTMIN + 27,
+ LX_SIGRTMIN + 28,
+ LX_SIGRTMIN + 29,
+ LX_SIGRTMIN + 30,
+ LX_SIGRTMAX
+};
+
+/*
+ * lxpr_open(): Vnode operation for VOP_OPEN()
+ */
+static int
+lxpr_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+ vnode_t *vp = *vpp;
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ vnode_t *rvp;
+ int error = 0;
+
+ /*
+ * We only allow reading in this file systrem
+ */
+ if (flag & FWRITE)
+ return (EROFS);
+
+ /*
+ * If we are opening an underlying file only allow regular files
+ * reject the open for anything but a regular file.
+ * Just do it if we are opening the current or root directory.
+ */
+ if (lxpnp->lxpr_realvp != NULL) {
+ rvp = lxpnp->lxpr_realvp;
+
+ if (type == LXPR_PID_FD_FD && rvp->v_type != VREG)
+ error = EACCES;
+ else {
+ /*
+ * Need to hold rvp since VOP_OPEN() may release it.
+ */
+ VN_HOLD(rvp);
+ error = VOP_OPEN(&rvp, flag, cr, ct);
+ if (error) {
+ VN_RELE(rvp);
+ } else {
+ *vpp = rvp;
+ VN_RELE(vp);
+ }
+ }
+ }
+
+ return (error);
+}
+
+
+/*
+ * lxpr_close(): Vnode operation for VOP_CLOSE()
+ */
+/* ARGSUSED */
+static int
+lxpr_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxpr_node_t *lxpr = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpr->lxpr_type;
+
+ /*
+ * we should never get here because the close is done on the realvp
+ * for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR &&
+ type != LXPR_PID_EXE);
+
+ return (0);
+}
+
+static void (*lxpr_read_function[LXPR_NFILES])() = {
+ lxpr_read_isdir, /* /proc */
+ lxpr_read_isdir, /* /proc/<pid> */
+ lxpr_read_pid_cmdline, /* /proc/<pid>/cmdline */
+ lxpr_read_empty, /* /proc/<pid>/cpu */
+ lxpr_read_invalid, /* /proc/<pid>/cwd */
+ lxpr_read_empty, /* /proc/<pid>/environ */
+ lxpr_read_invalid, /* /proc/<pid>/exe */
+ lxpr_read_pid_maps, /* /proc/<pid>/maps */
+ lxpr_read_empty, /* /proc/<pid>/mem */
+ lxpr_read_invalid, /* /proc/<pid>/root */
+ lxpr_read_pid_stat, /* /proc/<pid>/stat */
+ lxpr_read_pid_statm, /* /proc/<pid>/statm */
+ lxpr_read_pid_status, /* /proc/<pid>/status */
+ lxpr_read_isdir, /* /proc/<pid>/fd */
+ lxpr_read_fd, /* /proc/<pid>/fd/nn */
+ lxpr_read_empty, /* /proc/cmdline */
+ lxpr_read_cpuinfo, /* /proc/cpuinfo */
+ lxpr_read_empty, /* /proc/devices */
+ lxpr_read_empty, /* /proc/dma */
+ lxpr_read_empty, /* /proc/filesystems */
+ lxpr_read_empty, /* /proc/interrupts */
+ lxpr_read_empty, /* /proc/ioports */
+ lxpr_read_empty, /* /proc/kcore */
+ lxpr_read_invalid, /* /proc/kmsg -- see lxpr_read() */
+ lxpr_read_loadavg, /* /proc/loadavg */
+ lxpr_read_meminfo, /* /proc/meminfo */
+ lxpr_read_mounts, /* /proc/mounts */
+ lxpr_read_isdir, /* /proc/net */
+ lxpr_read_net_arp, /* /proc/net/arp */
+ lxpr_read_net_dev, /* /proc/net/dev */
+ lxpr_read_net_dev_mcast, /* /proc/net/dev_mcast */
+ lxpr_read_net_igmp, /* /proc/net/igmp */
+ lxpr_read_net_ip_mr_cache, /* /proc/net/ip_mr_cache */
+ lxpr_read_net_ip_mr_vif, /* /proc/net/ip_mr_vif */
+ lxpr_read_net_mcfilter, /* /proc/net/mcfilter */
+ lxpr_read_net_netstat, /* /proc/net/netstat */
+ lxpr_read_net_raw, /* /proc/net/raw */
+ lxpr_read_net_route, /* /proc/net/route */
+ lxpr_read_net_rpc, /* /proc/net/rpc */
+ lxpr_read_net_rt_cache, /* /proc/net/rt_cache */
+ lxpr_read_net_sockstat, /* /proc/net/sockstat */
+ lxpr_read_net_snmp, /* /proc/net/snmp */
+ lxpr_read_net_stat, /* /proc/net/stat */
+ lxpr_read_net_tcp, /* /proc/net/tcp */
+ lxpr_read_net_udp, /* /proc/net/udp */
+ lxpr_read_net_unix, /* /proc/net/unix */
+ lxpr_read_partitions, /* /proc/partitions */
+ lxpr_read_invalid, /* /proc/self */
+ lxpr_read_stat, /* /proc/stat */
+ lxpr_read_uptime, /* /proc/uptime */
+ lxpr_read_version, /* /proc/version */
+};
+
+/*
+ * Array of lookup functions, indexed by /lxproc file type.
+ */
+static vnode_t *(*lxpr_lookup_function[LXPR_NFILES])() = {
+ lxpr_lookup_procdir, /* /proc */
+ lxpr_lookup_piddir, /* /proc/<pid> */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cmdline */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cpu */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/cwd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/environ */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/exe */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/maps */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/mem */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/root */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/stat */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/statm */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/status */
+ lxpr_lookup_fddir, /* /proc/<pid>/fd */
+ lxpr_lookup_not_a_dir, /* /proc/<pid>/fd/nn */
+ lxpr_lookup_not_a_dir, /* /proc/cmdline */
+ lxpr_lookup_not_a_dir, /* /proc/cpuinfo */
+ lxpr_lookup_not_a_dir, /* /proc/devices */
+ lxpr_lookup_not_a_dir, /* /proc/dma */
+ lxpr_lookup_not_a_dir, /* /proc/filesystems */
+ lxpr_lookup_not_a_dir, /* /proc/interrupts */
+ lxpr_lookup_not_a_dir, /* /proc/ioports */
+ lxpr_lookup_not_a_dir, /* /proc/kcore */
+ lxpr_lookup_not_a_dir, /* /proc/kmsg */
+ lxpr_lookup_not_a_dir, /* /proc/loadavg */
+ lxpr_lookup_not_a_dir, /* /proc/meminfo */
+ lxpr_lookup_not_a_dir, /* /proc/mounts */
+ lxpr_lookup_netdir, /* /proc/net */
+ lxpr_lookup_not_a_dir, /* /proc/net/arp */
+ lxpr_lookup_not_a_dir, /* /proc/net/dev */
+ lxpr_lookup_not_a_dir, /* /proc/net/dev_mcast */
+ lxpr_lookup_not_a_dir, /* /proc/net/igmp */
+ lxpr_lookup_not_a_dir, /* /proc/net/ip_mr_cache */
+ lxpr_lookup_not_a_dir, /* /proc/net/ip_mr_vif */
+ lxpr_lookup_not_a_dir, /* /proc/net/mcfilter */
+ lxpr_lookup_not_a_dir, /* /proc/net/netstat */
+ lxpr_lookup_not_a_dir, /* /proc/net/raw */
+ lxpr_lookup_not_a_dir, /* /proc/net/route */
+ lxpr_lookup_not_a_dir, /* /proc/net/rpc */
+ lxpr_lookup_not_a_dir, /* /proc/net/rt_cache */
+ lxpr_lookup_not_a_dir, /* /proc/net/sockstat */
+ lxpr_lookup_not_a_dir, /* /proc/net/snmp */
+ lxpr_lookup_not_a_dir, /* /proc/net/stat */
+ lxpr_lookup_not_a_dir, /* /proc/net/tcp */
+ lxpr_lookup_not_a_dir, /* /proc/net/udp */
+ lxpr_lookup_not_a_dir, /* /proc/net/unix */
+ lxpr_lookup_not_a_dir, /* /proc/partitions */
+ lxpr_lookup_not_a_dir, /* /proc/self */
+ lxpr_lookup_not_a_dir, /* /proc/stat */
+ lxpr_lookup_not_a_dir, /* /proc/uptime */
+ lxpr_lookup_not_a_dir, /* /proc/version */
+};
+
+/*
+ * Array of readdir functions, indexed by /proc file type.
+ */
+static int (*lxpr_readdir_function[LXPR_NFILES])() = {
+ lxpr_readdir_procdir, /* /proc */
+ lxpr_readdir_piddir, /* /proc/<pid> */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cmdline */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cpu */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/cwd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/environ */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/exe */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/maps */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/mem */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/root */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/stat */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/statm */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/status */
+ lxpr_readdir_fddir, /* /proc/<pid>/fd */
+ lxpr_readdir_not_a_dir, /* /proc/<pid>/fd/nn */
+ lxpr_readdir_not_a_dir, /* /proc/cmdline */
+ lxpr_readdir_not_a_dir, /* /proc/cpuinfo */
+ lxpr_readdir_not_a_dir, /* /proc/devices */
+ lxpr_readdir_not_a_dir, /* /proc/dma */
+ lxpr_readdir_not_a_dir, /* /proc/filesystems */
+ lxpr_readdir_not_a_dir, /* /proc/interrupts */
+ lxpr_readdir_not_a_dir, /* /proc/ioports */
+ lxpr_readdir_not_a_dir, /* /proc/kcore */
+ lxpr_readdir_not_a_dir, /* /proc/kmsg */
+ lxpr_readdir_not_a_dir, /* /proc/loadavg */
+ lxpr_readdir_not_a_dir, /* /proc/meminfo */
+ lxpr_readdir_not_a_dir, /* /proc/mounts */
+ lxpr_readdir_netdir, /* /proc/net */
+ lxpr_readdir_not_a_dir, /* /proc/net/arp */
+ lxpr_readdir_not_a_dir, /* /proc/net/dev */
+ lxpr_readdir_not_a_dir, /* /proc/net/dev_mcast */
+ lxpr_readdir_not_a_dir, /* /proc/net/igmp */
+ lxpr_readdir_not_a_dir, /* /proc/net/ip_mr_cache */
+ lxpr_readdir_not_a_dir, /* /proc/net/ip_mr_vif */
+ lxpr_readdir_not_a_dir, /* /proc/net/mcfilter */
+ lxpr_readdir_not_a_dir, /* /proc/net/netstat */
+ lxpr_readdir_not_a_dir, /* /proc/net/raw */
+ lxpr_readdir_not_a_dir, /* /proc/net/route */
+ lxpr_readdir_not_a_dir, /* /proc/net/rpc */
+ lxpr_readdir_not_a_dir, /* /proc/net/rt_cache */
+ lxpr_readdir_not_a_dir, /* /proc/net/sockstat */
+ lxpr_readdir_not_a_dir, /* /proc/net/snmp */
+ lxpr_readdir_not_a_dir, /* /proc/net/stat */
+ lxpr_readdir_not_a_dir, /* /proc/net/tcp */
+ lxpr_readdir_not_a_dir, /* /proc/net/udp */
+ lxpr_readdir_not_a_dir, /* /proc/net/unix */
+ lxpr_readdir_not_a_dir, /* /proc/partitions */
+ lxpr_readdir_not_a_dir, /* /proc/self */
+ lxpr_readdir_not_a_dir, /* /proc/stat */
+ lxpr_readdir_not_a_dir, /* /proc/uptime */
+ lxpr_readdir_not_a_dir, /* /proc/version */
+};
+
+
+/*
+ * lxpr_read(): Vnode operation for VOP_READ()
+ *
+ * As the format of all the files that can be read in lxproc is human readable
+ * and not binary structures there do not have to be different read variants
+ * depending on whether the reading process model is 32- or 64-bit.
+ */
+/* ARGSUSED */
+static int
+lxpr_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+ caller_context_t *ct)
+{
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ lxpr_uiobuf_t *uiobuf = lxpr_uiobuf_new(uiop);
+ int error;
+
+ ASSERT(type < LXPR_NFILES);
+
+ if (type == LXPR_KMSG) {
+ ldi_ident_t li = VTOLXPM(vp)->lxprm_li;
+ ldi_handle_t ldih;
+ struct strioctl str;
+ int rv;
+
+ /*
+ * Open the zone's console device using the layered driver
+ * interface.
+ */
+ if ((error =
+ ldi_open_by_name("/dev/log", FREAD, cr, &ldih, li)) != 0)
+ return (error);
+
+ /*
+ * Send an ioctl to the underlying console device, letting it
+ * know we're interested in getting console messages.
+ */
+ str.ic_cmd = I_CONSLOG;
+ str.ic_timout = 0;
+ str.ic_len = 0;
+ str.ic_dp = NULL;
+ if ((error = ldi_ioctl(ldih, I_STR,
+ (intptr_t)&str, FKIOCTL, cr, &rv)) != 0)
+ return (error);
+
+ lxpr_read_kmsg(lxpnp, uiobuf, ldih);
+
+ if ((error = ldi_close(ldih, FREAD, cr)) != 0)
+ return (error);
+ } else {
+ lxpr_read_function[type](lxpnp, uiobuf);
+ }
+
+ error = lxpr_uiobuf_flush(uiobuf);
+ lxpr_uiobuf_free(uiobuf);
+
+ return (error);
+}
+
+/*
+ * lxpr_read_invalid(), lxpr_read_isdir(), lxpr_read_empty()
+ *
+ * Various special case reads:
+ * - trying to read a directory
+ * - invalid file (used to mean a file that should be implemented,
+ * but isn't yet)
+ * - empty file
+ * - wait to be able to read a file that will never have anything to read
+ */
+/* ARGSUSED */
+static void
+lxpr_read_isdir(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_seterr(uiobuf, EISDIR);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_invalid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_empty(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/*
+ * lxpr_read_pid_cmdline():
+ *
+ * This is not precisely compatible with Linux: the Linux cmdline returns argv
+ * with the correct separation using \0 between the arguments, but we cannot do
+ * that without copying the real argv from the correct process context. This
+ * is too difficult to attempt so we pretend that the entire cmdline is just
+ * argv[0]. This is good enough for ps and htop to display correctly, but might
+ * cause some other things not to work correctly.
+ */
+static void
+lxpr_read_pid_cmdline(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ char *buf;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_CMDLINE);
+
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ buf = PTOU(p)->u_argv != 0 ? PTOU(p)->u_psargs : PTOU(p)->u_comm;
+
+ lxpr_uiobuf_write(uiobuf, buf, strlen(buf) + 1);
+ lxpr_unlock(p);
+}
+
+/*
+ * lxpr_read_pid_maps(): memory map file
+ */
+static void
+lxpr_read_pid_maps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ struct as *as;
+ struct seg *seg;
+ char *buf;
+ int buflen = MAXPATHLEN;
+ struct print_data {
+ caddr_t saddr;
+ caddr_t eaddr;
+ int type;
+ char prot[5];
+ uint32_t offset;
+ vnode_t *vp;
+ struct print_data *next;
+ } *print_head = NULL;
+ struct print_data **print_tail = &print_head;
+ struct print_data *pbuf;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_MAPS);
+
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ as = p->p_as;
+
+ if (as == &kas) {
+ lxpr_unlock(p);
+ return;
+ }
+
+ mutex_exit(&p->p_lock);
+
+ /* Iterate over all segments in the address space */
+ AS_LOCK_ENTER(as, RW_READER);
+ for (seg = AS_SEGFIRST(as); seg != NULL; seg = AS_SEGNEXT(as, seg)) {
+ vnode_t *vp;
+ uint_t protbits;
+
+ if ((seg->s_flags & S_HOLE) != 0) {
+ continue;
+ }
+
+ pbuf = kmem_alloc(sizeof (*pbuf), KM_SLEEP);
+
+ pbuf->saddr = seg->s_base;
+ pbuf->eaddr = seg->s_base+seg->s_size;
+ pbuf->type = SEGOP_GETTYPE(seg, seg->s_base);
+
+ /*
+ * Cheat and only use the protection bits of the first page
+ * in the segment
+ */
+ (void) strncpy(pbuf->prot, "----", sizeof (pbuf->prot));
+ (void) SEGOP_GETPROT(seg, seg->s_base, 0, &protbits);
+
+ if (protbits & PROT_READ) pbuf->prot[0] = 'r';
+ if (protbits & PROT_WRITE) pbuf->prot[1] = 'w';
+ if (protbits & PROT_EXEC) pbuf->prot[2] = 'x';
+ if (pbuf->type & MAP_SHARED) pbuf->prot[3] = 's';
+ else if (pbuf->type & MAP_PRIVATE) pbuf->prot[3] = 'p';
+
+ if (seg->s_ops == &segvn_ops &&
+ SEGOP_GETVP(seg, seg->s_base, &vp) == 0 &&
+ vp != NULL && vp->v_type == VREG) {
+ VN_HOLD(vp);
+ pbuf->vp = vp;
+ } else {
+ pbuf->vp = NULL;
+ }
+
+ pbuf->offset = (uint32_t)SEGOP_GETOFFSET(seg, pbuf->saddr);
+
+ pbuf->next = NULL;
+ *print_tail = pbuf;
+ print_tail = &pbuf->next;
+ }
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+
+ buf = kmem_alloc(buflen, KM_SLEEP);
+
+ /* print the data we've extracted */
+ pbuf = print_head;
+ while (pbuf != NULL) {
+ struct print_data *pbuf_next;
+ vattr_t vattr;
+
+ int maj = 0;
+ int min = 0;
+ u_longlong_t inode = 0;
+
+ *buf = '\0';
+ if (pbuf->vp != NULL) {
+ vattr.va_mask = AT_FSID | AT_NODEID;
+ if (VOP_GETATTR(pbuf->vp, &vattr, 0, CRED(),
+ NULL) == 0) {
+ maj = getmajor(vattr.va_fsid);
+ min = getminor(vattr.va_fsid);
+ inode = vattr.va_nodeid;
+ }
+ (void) vnodetopath(NULL, pbuf->vp, buf, buflen, CRED());
+ VN_RELE(pbuf->vp);
+ }
+
+ if (*buf != '\0') {
+ lxpr_uiobuf_printf(uiobuf,
+ "%08x-%08x %s %08x %02d:%03d %lld %s\n",
+ pbuf->saddr, pbuf->eaddr, pbuf->prot, pbuf->offset,
+ maj, min, inode, buf);
+ } else {
+ lxpr_uiobuf_printf(uiobuf,
+ "%08x-%08x %s %08x %02d:%03d %lld\n",
+ pbuf->saddr, pbuf->eaddr, pbuf->prot, pbuf->offset,
+ maj, min, inode);
+ }
+
+ pbuf_next = pbuf->next;
+ kmem_free(pbuf, sizeof (*pbuf));
+ pbuf = pbuf_next;
+ }
+
+ kmem_free(buf, buflen);
+}
+
+/*
+ * lxpr_read_pid_statm(): memory status file
+ */
+static void
+lxpr_read_pid_statm(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ struct as *as;
+ size_t vsize;
+ size_t rss;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STATM);
+
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ as = p->p_as;
+
+ mutex_exit(&p->p_lock);
+
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = btopr(as->a_resvsize);
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%lu %lu %lu %lu %lu %lu %lu\n",
+ vsize, rss, 0l, rss, 0l, 0l, 0l);
+}
+
+/*
+ * lxpr_read_pid_status(): status file
+ */
+static void
+lxpr_read_pid_status(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ kthread_t *t;
+ user_t *up;
+ cred_t *cr;
+ const gid_t *groups;
+ int ngroups;
+ struct as *as;
+ char *status;
+ pid_t pid, ppid;
+ size_t vsize;
+ size_t rss;
+ k_sigset_t current, ignore, handle;
+ int i, lx_sig;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STATUS);
+
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ pid = p->p_pid;
+
+ /*
+ * Convert pid to the Linux default of 1 if we're the zone's init
+ * process
+ */
+ if (pid == curproc->p_zone->zone_proc_initpid) {
+ pid = 1;
+ ppid = 0; /* parent pid for init is 0 */
+ } else {
+ /*
+ * Make sure not to reference parent PIDs that reside outside
+ * the zone
+ */
+ ppid = ((p->p_flag & SZONETOP)
+ ? curproc->p_zone->zone_zsched->p_pid : p->p_ppid);
+
+ /*
+ * Convert ppid to the Linux default of 1 if our parent is the
+ * zone's init process
+ */
+ if (ppid == curproc->p_zone->zone_proc_initpid)
+ ppid = 1;
+ }
+
+ t = prchoose(p);
+ if (t != NULL) {
+ switch (t->t_state) {
+ case TS_SLEEP:
+ status = "S (sleeping)";
+ break;
+ case TS_RUN:
+ case TS_ONPROC:
+ status = "R (running)";
+ break;
+ case TS_ZOMB:
+ status = "Z (zombie)";
+ break;
+ case TS_STOPPED:
+ status = "T (stopped)";
+ break;
+ default:
+ status = "! (unknown)";
+ break;
+ }
+ thread_unlock(t);
+ } else {
+ /*
+ * there is a hole in the exit code, where a proc can have
+ * no threads but it is yet to be flagged SZOMB. We will
+ * assume we are about to become a zombie
+ */
+ status = "Z (zombie)";
+ }
+
+ up = PTOU(p);
+ mutex_enter(&p->p_crlock);
+ crhold(cr = p->p_cred);
+ mutex_exit(&p->p_crlock);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "Name:\t%s\n"
+ "State:\t%s\n"
+ "Tgid:\t%d\n"
+ "Pid:\t%d\n"
+ "PPid:\t%d\n"
+ "TracerPid:\t%d\n"
+ "Uid:\t%u\t%u\t%u\t%u\n"
+ "Gid:\t%u\t%u\t%u\t%u\n"
+ "FDSize:\t%d\n"
+ "Groups:\t",
+ up->u_comm,
+ status,
+ pid, /* thread group id - same as pid */
+ pid,
+ ppid,
+ 0,
+ crgetruid(cr), crgetuid(cr), crgetsuid(cr), crgetuid(cr),
+ crgetrgid(cr), crgetgid(cr), crgetsgid(cr), crgetgid(cr),
+ p->p_fno_ctl);
+
+ ngroups = crgetngroups(cr);
+ groups = crgetgroups(cr);
+ for (i = 0; i < ngroups; i++) {
+ lxpr_uiobuf_printf(uiobuf,
+ "%u ",
+ groups[i]);
+ }
+ crfree(cr);
+
+ as = p->p_as;
+ if ((p->p_stat != SZOMB) && !(p->p_flag & (SSYS | SEXITING)) &&
+ (as != &kas)) {
+ mutex_exit(&p->p_lock);
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = as->a_resvsize;
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "\n"
+ "VmSize:\t%8lu kB\n"
+ "VmLck:\t%8lu kB\n"
+ "VmRSS:\t%8lu kB\n"
+ "VmData:\t%8lu kB\n"
+ "VmStk:\t%8lu kB\n"
+ "VmExe:\t%8lu kB\n"
+ "VmLib:\t%8lu kB",
+ btok(vsize),
+ 0l,
+ ptok(rss),
+ 0l,
+ btok(p->p_stksize),
+ ptok(rss),
+ 0l);
+ }
+
+ sigemptyset(&current);
+ sigemptyset(&ignore);
+ sigemptyset(&handle);
+
+ for (i = 1; i < NSIG; i++) {
+ lx_sig = lxpr_sigmap[i];
+
+ if ((lx_sig > 0) && (lx_sig <= LX_NSIG)) {
+ if (sigismember(&p->p_sig, i))
+ sigaddset(&current, lx_sig);
+
+ if (up->u_signal[i - 1] == SIG_IGN)
+ sigaddset(&ignore, lx_sig);
+ else if (up->u_signal[i - 1] != SIG_DFL)
+ sigaddset(&handle, lx_sig);
+ }
+ }
+
+ lxpr_uiobuf_printf(uiobuf,
+ "\n"
+ "SigPnd:\t%08x%08x\n"
+ "SigBlk:\t%08x%08x\n"
+ "SigIgn:\t%08x%08x\n"
+ "SigCgt:\t%08x%08x\n"
+ "CapInh:\t%016x\n"
+ "CapPrm:\t%016x\n"
+ "CapEff:\t%016x\n",
+ current.__sigbits[1], current.__sigbits[0],
+ 0, 0, /* signals blocked on per thread basis */
+ ignore.__sigbits[1], ignore.__sigbits[0],
+ handle.__sigbits[1], handle.__sigbits[0],
+ /* Can't do anything with linux capabilities */
+ 0,
+ 0,
+ 0);
+
+ lxpr_unlock(p);
+}
+
+
+/*
+ * lxpr_read_pid_stat(): pid stat file
+ */
+static void
+lxpr_read_pid_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ proc_t *p;
+ kthread_t *t;
+ struct as *as;
+ char stat;
+ pid_t pid, ppid, pgpid, spid;
+ gid_t psgid;
+ dev_t psdev;
+ size_t rss, vsize;
+ int nice, pri;
+ caddr_t wchan;
+ processorid_t cpu;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_STAT);
+
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL) {
+ lxpr_uiobuf_seterr(uiobuf, EINVAL);
+ return;
+ }
+
+ pid = p->p_pid;
+
+ /*
+ * Set Linux defaults if we're the zone's init process
+ */
+ if (pid == curproc->p_zone->zone_proc_initpid) {
+ pid = 1; /* PID for init */
+ ppid = 0; /* parent PID for init is 0 */
+ pgpid = 0; /* process group for init is 0 */
+ psgid = (gid_t)-1; /* credential GID for init is -1 */
+ spid = 0; /* session id for init is 0 */
+ psdev = 0; /* session device for init is 0 */
+ } else {
+ /*
+ * Make sure not to reference parent PIDs that reside outside
+ * the zone
+ */
+ ppid = ((p->p_flag & SZONETOP) ?
+ curproc->p_zone->zone_zsched->p_pid : p->p_ppid);
+
+ /*
+ * Convert ppid to the Linux default of 1 if our parent is the
+ * zone's init process
+ */
+ if (ppid == curproc->p_zone->zone_proc_initpid)
+ ppid = 1;
+
+ pgpid = p->p_pgrp;
+
+ mutex_enter(&p->p_splock);
+ mutex_enter(&p->p_sessp->s_lock);
+ spid = p->p_sessp->s_sid;
+ psdev = p->p_sessp->s_dev;
+ if (p->p_sessp->s_cred)
+ psgid = crgetgid(p->p_sessp->s_cred);
+ else
+ psgid = crgetgid(p->p_cred);
+
+ mutex_exit(&p->p_sessp->s_lock);
+ mutex_exit(&p->p_splock);
+ }
+
+ t = prchoose(p);
+ if (t != NULL) {
+ switch (t->t_state) {
+ case TS_SLEEP:
+ stat = 'S'; break;
+ case TS_RUN:
+ case TS_ONPROC:
+ stat = 'R'; break;
+ case TS_ZOMB:
+ stat = 'Z'; break;
+ case TS_STOPPED:
+ stat = 'T'; break;
+ default:
+ stat = '!'; break;
+ }
+
+ if (CL_DONICE(t, NULL, 0, &nice) != 0)
+ nice = 0;
+
+ pri = t->t_pri;
+ wchan = t->t_wchan;
+ cpu = t->t_cpu->cpu_id;
+ thread_unlock(t);
+ } else {
+ /* Only zombies have no threads */
+ stat = 'Z';
+ nice = 0;
+ pri = 0;
+ wchan = 0;
+ cpu = 0;
+ }
+ as = p->p_as;
+ mutex_exit(&p->p_lock);
+ AS_LOCK_ENTER(as, RW_READER);
+ vsize = as->a_resvsize;
+ rss = rm_asrss(as);
+ AS_LOCK_EXIT(as);
+ mutex_enter(&p->p_lock);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%d (%s) %c %d %d %d %d %d "
+ "%lu %lu %lu %lu %lu "
+ "%lu %lu %ld %ld "
+ "%d %d %d "
+ "%lu "
+ "%lu "
+ "%lu %ld %llu "
+ "%lu %lu %u "
+ "%lu %lu "
+ "%lu %lu %lu %lu "
+ "%lu "
+ "%lu %lu "
+ "%d "
+ "%d"
+ "\n",
+ pid, PTOU(p)->u_comm, stat, ppid, pgpid, spid, psdev, psgid,
+ 0l, 0l, 0l, 0l, 0l, /* flags, minflt, cminflt, majflt, cmajflt */
+ p->p_utime, p->p_stime, p->p_cutime, p->p_cstime,
+ pri, nice, p->p_lwpcnt,
+ 0l, /* itrealvalue (time before next SIGALRM) */
+ PTOU(p)->u_ticks,
+ vsize, rss, p->p_vmem_ctl,
+ 0l, 0l, USRSTACK, /* startcode, endcode, startstack */
+ 0l, 0l, /* kstkesp, kstkeip */
+ 0l, 0l, 0l, 0l, /* signal, blocked, sigignore, sigcatch */
+ wchan,
+ 0l, 0l, /* nswap, cnswap */
+ 0, /* exit_signal */
+ cpu);
+
+ lxpr_unlock(p);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_arp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_dev(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_printf(uiobuf, "Inter-| Receive "
+ " | Transmit\n");
+ lxpr_uiobuf_printf(uiobuf, " face |bytes packets errs drop fifo"
+ " frame compressed multicast|bytes packets errs drop fifo"
+ " colls carrier compressed\n");
+
+ /*
+ * Data about each interface should go here, but that shouldn't be added
+ * unless there is an lxproc reader that actually makes use of it (and
+ * doesn't need anything else that we refuse to provide)...
+ */
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_dev_mcast(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_igmp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ip_mr_cache(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ip_mr_vif(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_mcfilter(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_netstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_raw(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_route(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_rpc(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_rt_cache(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_sockstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_snmp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_tcp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_udp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_unix(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/*
+ * lxpr_read_kmsg(): read the contents of the kernel message queue. We
+ * translate this into the reception of console messages for this zone; each
+ * read copies out a single zone console message, or blocks until the next one
+ * is produced.
+ */
+
+#define LX_KMSG_PRI "<0>"
+
+static void
+lxpr_read_kmsg(lxpr_node_t *lxpnp, struct lxpr_uiobuf *uiobuf, ldi_handle_t lh)
+{
+ mblk_t *mp;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_KMSG);
+
+ if (ldi_getmsg(lh, &mp, NULL) == 0) {
+ /*
+ * lxproc doesn't like successive reads to the same file
+ * descriptor unless we do an explicit rewind each time.
+ */
+ lxpr_uiobuf_seek(uiobuf, 0);
+
+ lxpr_uiobuf_printf(uiobuf, "%s%s", LX_KMSG_PRI,
+ mp->b_cont->b_rptr);
+
+ freemsg(mp);
+ }
+}
+
+/*
+ * lxpr_read_loadavg(): read the contents of the "loadavg" file. We do just
+ * enough for uptime and other simple lxproc readers to work
+ */
+extern int nthread;
+
+static void
+lxpr_read_loadavg(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ulong_t avenrun1;
+ ulong_t avenrun5;
+ ulong_t avenrun15;
+ ulong_t avenrun1_cs;
+ ulong_t avenrun5_cs;
+ ulong_t avenrun15_cs;
+ int loadavg[3];
+ int *loadbuf;
+ cpupart_t *cp;
+ zone_t *zone = LXPTOZ(lxpnp);
+
+ uint_t nrunnable = 0;
+ rctl_qty_t nlwps;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_LOADAVG);
+
+ mutex_enter(&cpu_lock);
+
+ /*
+ * Need to add up values over all CPU partitions. If pools are active,
+ * only report the values of the zone's partition, which by definition
+ * includes the current CPU.
+ */
+ if (pool_pset_enabled()) {
+ psetid_t psetid = zone_pset_get(curproc->p_zone);
+
+ ASSERT(curproc->p_zone != &zone0);
+ cp = CPU->cpu_part;
+
+ nrunnable = cp->cp_nrunning + cp->cp_nrunnable;
+ (void) cpupart_get_loadavg(psetid, &loadavg[0], 3);
+ loadbuf = &loadavg[0];
+ } else {
+ cp = cp_list_head;
+ do {
+ nrunnable += cp->cp_nrunning + cp->cp_nrunnable;
+ } while ((cp = cp->cp_next) != cp_list_head);
+
+ loadbuf = zone == global_zone ?
+ &avenrun[0] : zone->zone_avenrun;
+ }
+
+ /*
+ * If we're in the non-global zone, we'll report the total number of
+ * LWPs in the zone for the "nproc" parameter of /proc/loadavg,
+ * otherwise will just use nthread (which will include kernel threads,
+ * but should be good enough for lxproc).
+ */
+ nlwps = zone == global_zone ? nthread : zone->zone_nlwps;
+
+ mutex_exit(&cpu_lock);
+
+ avenrun1 = loadbuf[0] >> FSHIFT;
+ avenrun1_cs = ((loadbuf[0] & (FSCALE-1)) * 100) >> FSHIFT;
+ avenrun5 = loadbuf[1] >> FSHIFT;
+ avenrun5_cs = ((loadbuf[1] & (FSCALE-1)) * 100) >> FSHIFT;
+ avenrun15 = loadbuf[2] >> FSHIFT;
+ avenrun15_cs = ((loadbuf[2] & (FSCALE-1)) * 100) >> FSHIFT;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%ld.%02d %ld.%02d %ld.%02d %d/%d %d\n",
+ avenrun1, avenrun1_cs,
+ avenrun5, avenrun5_cs,
+ avenrun15, avenrun15_cs,
+ nrunnable, nlwps, 0);
+}
+
+/*
+ * lxpr_read_meminfo(): read the contents of the "meminfo" file.
+ */
+static void
+lxpr_read_meminfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ zone_t *zone = LXPTOZ(lxpnp);
+ int global = zone == global_zone;
+ ulong_t total_mem, free_mem, total_swap, used_swap;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_MEMINFO);
+
+ zone_get_physmem_data(zone->zone_id, (pgcnt_t *)&total_mem,
+ (pgcnt_t *)&free_mem);
+ total_mem = ptob(total_mem);
+ free_mem = ptob(free_mem);
+
+ if (global || zone->zone_max_swap_ctl == UINT64_MAX) {
+ total_swap = ptob(k_anoninfo.ani_max);
+ used_swap = ptob(k_anoninfo.ani_phys_resv);
+ } else {
+ mutex_enter(&zone->zone_mem_lock);
+ total_swap = zone->zone_max_swap_ctl;
+ used_swap = zone->zone_max_swap;
+ mutex_exit(&zone->zone_mem_lock);
+ }
+
+ lxpr_uiobuf_printf(uiobuf,
+ " total: used: free: shared: buffers: cached:\n"
+ "Mem: %8lu %8lu %8lu %8u %8u %8u\n"
+ "Swap: %8lu %8lu %8lu\n"
+ "MemTotal: %8lu kB\n"
+ "MemFree: %8lu kB\n"
+ "MemAvailable: %8lu kB\n"
+ "MemShared: %8u kB\n"
+ "Buffers: %8u kB\n"
+ "Cached: %8u kB\n"
+ "SwapCached: %8u kB\n"
+ "Active: %8u kB\n"
+ "Inactive: %8u kB\n"
+ "HighTotal: %8u kB\n"
+ "HighFree: %8u kB\n"
+ "LowTotal: %8u kB\n"
+ "LowFree: %8u kB\n"
+ "SwapTotal: %8lu kB\n"
+ "SwapFree: %8lu kB\n",
+ total_mem, total_mem - free_mem, free_mem, 0, 0, 0,
+ total_swap, used_swap, total_swap - used_swap,
+ btok(total_mem), /* MemTotal */
+ btok(free_mem), /* MemFree */
+ btok(free_mem), /* MemAvailable */
+ 0, /* MemShared */
+ 0, /* Buffers */
+ 0, /* Cached */
+ 0, /* SwapCached */
+ 0, /* Active */
+ 0, /* Inactive */
+ 0, /* HighTotal */
+ 0, /* HighFree */
+ btok(total_mem), /* LowTotal */
+ btok(free_mem), /* LowFree */
+ btok(total_swap), /* SwapTotal */
+ btok(total_swap - used_swap)); /* SwapFree */
+}
+
+/*
+ * lxpr_read_mounts():
+ */
+/* ARGSUSED */
+static void
+lxpr_read_mounts(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ struct vfs *vfsp;
+ struct vfs *vfslist;
+ zone_t *zone = LXPTOZ(lxpnp);
+ struct print_data {
+ refstr_t *vfs_mntpt;
+ refstr_t *vfs_resource;
+ uint_t vfs_flag;
+ int vfs_fstype;
+ struct print_data *next;
+ } *print_head = NULL;
+ struct print_data **print_tail = &print_head;
+ struct print_data *printp;
+
+ vfs_list_read_lock();
+
+ if (zone == global_zone) {
+ vfsp = vfslist = rootvfs;
+ } else {
+ vfsp = vfslist = zone->zone_vfslist;
+ /*
+ * If the zone has a root entry, it will be the first in
+ * the list. If it doesn't, we conjure one up.
+ */
+ if (vfslist == NULL || strcmp(refstr_value(vfsp->vfs_mntpt),
+ zone->zone_rootpath) != 0) {
+ struct vfs *tvfsp;
+ /*
+ * The root of the zone is not a mount point. The vfs
+ * we want to report is that of the zone's root vnode.
+ */
+ tvfsp = zone->zone_rootvp->v_vfsp;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "/ / %s %s 0 0\n",
+ vfssw[tvfsp->vfs_fstype].vsw_name,
+ tvfsp->vfs_flag & VFS_RDONLY ? "ro" : "rw");
+
+ }
+ if (vfslist == NULL) {
+ vfs_list_unlock();
+ return;
+ }
+ }
+
+ /*
+ * Later on we have to do a lookupname, which can end up causing
+ * another vfs_list_read_lock() to be called. Which can lead to a
+ * deadlock. To avoid this, we extract the data we need into a local
+ * list, then we can run this list without holding vfs_list_read_lock()
+ * We keep the list in the same order as the vfs_list
+ */
+ do {
+ /* Skip mounts we shouldn't show */
+ if (vfsp->vfs_flag & VFS_NOMNTTAB) {
+ goto nextfs;
+ }
+
+ printp = kmem_alloc(sizeof (*printp), KM_SLEEP);
+ refstr_hold(vfsp->vfs_mntpt);
+ printp->vfs_mntpt = vfsp->vfs_mntpt;
+ refstr_hold(vfsp->vfs_resource);
+ printp->vfs_resource = vfsp->vfs_resource;
+ printp->vfs_flag = vfsp->vfs_flag;
+ printp->vfs_fstype = vfsp->vfs_fstype;
+ printp->next = NULL;
+
+ *print_tail = printp;
+ print_tail = &printp->next;
+
+nextfs:
+ vfsp = (zone == global_zone) ?
+ vfsp->vfs_next : vfsp->vfs_zone_next;
+
+ } while (vfsp != vfslist);
+
+ vfs_list_unlock();
+
+ /*
+ * now we can run through what we've extracted without holding
+ * vfs_list_read_lock()
+ */
+ printp = print_head;
+ while (printp != NULL) {
+ struct print_data *printp_next;
+ const char *resource;
+ char *mntpt;
+ struct vnode *vp;
+ int error;
+
+ mntpt = (char *)refstr_value(printp->vfs_mntpt);
+ resource = refstr_value(printp->vfs_resource);
+
+ if (mntpt != NULL && mntpt[0] != '\0')
+ mntpt = ZONE_PATH_TRANSLATE(mntpt, zone);
+ else
+ mntpt = "-";
+
+ error = lookupname(mntpt, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
+
+ if (error != 0)
+ goto nextp;
+
+ if (!(vp->v_flag & VROOT)) {
+ VN_RELE(vp);
+ goto nextp;
+ }
+ VN_RELE(vp);
+
+ if (resource != NULL && resource[0] != '\0') {
+ if (resource[0] == '/') {
+ resource = ZONE_PATH_VISIBLE(resource, zone) ?
+ ZONE_PATH_TRANSLATE(resource, zone) :
+ mntpt;
+ }
+ } else {
+ resource = "-";
+ }
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%s %s %s %s 0 0\n",
+ resource, mntpt, vfssw[printp->vfs_fstype].vsw_name,
+ printp->vfs_flag & VFS_RDONLY ? "ro" : "rw");
+
+nextp:
+ printp_next = printp->next;
+ refstr_rele(printp->vfs_mntpt);
+ refstr_rele(printp->vfs_resource);
+ kmem_free(printp, sizeof (*printp));
+ printp = printp_next;
+
+ }
+}
+
+/*
+ * lxpr_read_partitions():
+ *
+ * We don't support partitions in a local zone because it requires access to
+ * physical devices. But we need to fake up enough of the file to show that we
+ * have no partitions.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_partitions(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_printf(uiobuf,
+ "major minor #blocks name rio rmerge rsect ruse "
+ "wio wmerge wsect wuse running use aveq\n\n");
+}
+
+/*
+ * lxpr_read_version(): read the contents of the "version" file. Note that
+ * we don't lie here -- we don't pretend that we're Linux. If lxproc is to
+ * be used in a Linux-branded zone, there will need to be a mount option to
+ * indicate that Linux should be more fully mimicked.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_version(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ lxpr_uiobuf_printf(uiobuf,
+ "%s version %s (%s version %d.%d.%d) "
+ "#%s SMP %s\n",
+ utsname.sysname, utsname.release,
+#if defined(__GNUC__)
+ "gcc",
+ __GNUC__,
+ __GNUC_MINOR__,
+ __GNUC_PATCHLEVEL__,
+#else
+ "Sun C",
+ __SUNPRO_C / 0x100,
+ (__SUNPRO_C & 0xff) / 0x10,
+ __SUNPRO_C & 0xf,
+#endif
+ utsname.version,
+ "00:00:00 00/00/00");
+}
+
+/*
+ * lxpr_read_stat(): read the contents of the "stat" file.
+ *
+ */
+/* ARGSUSED */
+static void
+lxpr_read_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ ulong_t idle_cum = 0;
+ ulong_t sys_cum = 0;
+ ulong_t user_cum = 0;
+ ulong_t irq_cum = 0;
+ ulong_t cpu_nrunnable_cum = 0;
+ ulong_t w_io_cum = 0;
+
+ ulong_t pgpgin_cum = 0;
+ ulong_t pgpgout_cum = 0;
+ ulong_t pgswapout_cum = 0;
+ ulong_t pgswapin_cum = 0;
+ ulong_t intr_cum = 0;
+ ulong_t pswitch_cum = 0;
+ ulong_t forks_cum = 0;
+ hrtime_t msnsecs[NCMSTATES];
+
+ /* temporary variable since scalehrtime modifies data in place */
+ hrtime_t tmptime;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_STAT);
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ /* Calculate cumulative stats */
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ int i;
+
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ get_cpu_mstate(cp, msnsecs);
+
+ idle_cum += NSEC_TO_TICK(msnsecs[CMS_IDLE]);
+ sys_cum += NSEC_TO_TICK(msnsecs[CMS_SYSTEM]);
+ user_cum += NSEC_TO_TICK(msnsecs[CMS_USER]);
+
+ pgpgin_cum += CPU_STATS(cp, vm.pgpgin);
+ pgpgout_cum += CPU_STATS(cp, vm.pgpgout);
+ pgswapin_cum += CPU_STATS(cp, vm.pgswapin);
+ pgswapout_cum += CPU_STATS(cp, vm.pgswapout);
+
+ cpu_nrunnable_cum += cp->cpu_disp->disp_nrunnable;
+ w_io_cum += CPU_STATS(cp, sys.iowait);
+ for (i = 0; i < NCMSTATES; i++) {
+ tmptime = cp->cpu_intracct[i];
+ scalehrtime(&tmptime);
+ irq_cum += NSEC_TO_TICK(tmptime);
+ }
+
+ for (i = 0; i < PIL_MAX; i++)
+ intr_cum += CPU_STATS(cp, sys.intr[i]);
+
+ pswitch_cum += CPU_STATS(cp, sys.pswitch);
+ forks_cum += CPU_STATS(cp, sys.sysfork);
+ forks_cum += CPU_STATS(cp, sys.sysvfork);
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ lxpr_uiobuf_printf(uiobuf, "cpu %lu %lu %lu %lu %lu %lu %lu\n",
+ user_cum, 0L, sys_cum, idle_cum, 0L, irq_cum, 0L);
+
+ /* Do per processor stats */
+ do {
+ int i;
+
+ ulong_t idle_ticks;
+ ulong_t sys_ticks;
+ ulong_t user_ticks;
+ ulong_t irq_ticks = 0;
+
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ get_cpu_mstate(cp, msnsecs);
+
+ idle_ticks = NSEC_TO_TICK(msnsecs[CMS_IDLE]);
+ sys_ticks = NSEC_TO_TICK(msnsecs[CMS_SYSTEM]);
+ user_ticks = NSEC_TO_TICK(msnsecs[CMS_USER]);
+
+ for (i = 0; i < NCMSTATES; i++) {
+ tmptime = cp->cpu_intracct[i];
+ scalehrtime(&tmptime);
+ irq_ticks += NSEC_TO_TICK(tmptime);
+ }
+
+ lxpr_uiobuf_printf(uiobuf,
+ "cpu%d %lu %lu %lu %lu %lu %lu %lu\n",
+ cp->cpu_id, user_ticks, 0L, sys_ticks, idle_ticks,
+ 0L, irq_ticks, 0L);
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "page %lu %lu\n"
+ "swap %lu %lu\n"
+ "intr %lu\n"
+ "ctxt %lu\n"
+ "btime %lu\n"
+ "processes %lu\n"
+ "procs_running %lu\n"
+ "procs_blocked %lu\n",
+ pgpgin_cum, pgpgout_cum,
+ pgswapin_cum, pgswapout_cum,
+ intr_cum,
+ pswitch_cum,
+ boot_time,
+ forks_cum,
+ cpu_nrunnable_cum,
+ w_io_cum);
+}
+
+/*
+ * lxpr_read_uptime(): read the contents of the "uptime" file.
+ *
+ * format is: "%.2lf, %.2lf",uptime_secs, idle_secs
+ * Use fixed point arithmetic to get 2 decimal places
+ */
+/* ARGSUSED */
+static void
+lxpr_read_uptime(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ ulong_t idle_cum = 0;
+ ulong_t cpu_count = 0;
+ ulong_t idle_s;
+ ulong_t idle_cs;
+ ulong_t up_s;
+ ulong_t up_cs;
+ hrtime_t birthtime;
+ hrtime_t centi_sec = 10000000; /* 10^7 */
+
+ ASSERT(lxpnp->lxpr_type == LXPR_UPTIME);
+
+ /* Calculate cumulative stats */
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ /*
+ * Don't count CPUs that aren't even in the system
+ * or aren't up yet.
+ */
+ if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+ continue;
+ }
+
+ idle_cum += CPU_STATS(cp, sys.cpu_ticks_idle);
+ idle_cum += CPU_STATS(cp, sys.cpu_ticks_wait);
+ cpu_count += 1;
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+ mutex_exit(&cpu_lock);
+
+ /* Getting the Zone zsched process startup time */
+ birthtime = LXPTOZ(lxpnp)->zone_zsched->p_mstart;
+ up_cs = (gethrtime() - birthtime) / centi_sec;
+ up_s = up_cs / 100;
+ up_cs %= 100;
+
+ ASSERT(cpu_count > 0);
+ idle_cum /= cpu_count;
+ idle_s = idle_cum / hz;
+ idle_cs = idle_cum % hz;
+ idle_cs *= 100;
+ idle_cs /= hz;
+
+ lxpr_uiobuf_printf(uiobuf,
+ "%ld.%02d %ld.%02d\n", up_s, up_cs, idle_s, idle_cs);
+}
+
+static const char *amd_x_edx[] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, "syscall",
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, "mp",
+ "nx", NULL, "mmxext", NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, "lm", "3dnowext", "3dnow"
+};
+
+static const char *amd_x_ecx[] = {
+ "lahf_lm", NULL, "svm", NULL,
+ "altmovcr8"
+};
+
+static const char *tm_x_edx[] = {
+ "recovery", "longrun", NULL, "lrti"
+};
+
+/*
+ * Intel calls no-execute "xd" in its docs, but Linux still reports it as "nx."
+ */
+static const char *intc_x_edx[] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, "syscall",
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ "nx", NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, "lm", NULL, NULL
+};
+
+static const char *intc_edx[] = {
+ "fpu", "vme", "de", "pse",
+ "tsc", "msr", "pae", "mce",
+ "cx8", "apic", NULL, "sep",
+ "mtrr", "pge", "mca", "cmov",
+ "pat", "pse36", "pn", "clflush",
+ NULL, "dts", "acpi", "mmx",
+ "fxsr", "sse", "sse2", "ss",
+ "ht", "tm", "ia64", "pbe"
+};
+
+/*
+ * "sse3" on linux is called "pni" (Prescott New Instructions).
+ */
+static const char *intc_ecx[] = {
+ "pni", NULL, NULL, "monitor",
+ "ds_cpl", NULL, NULL, "est",
+ "tm2", NULL, "cid", NULL,
+ NULL, "cx16", "xtpr"
+};
+
+static void
+lxpr_read_cpuinfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ int i;
+ uint32_t bits;
+ cpu_t *cp, *cpstart;
+ int pools_enabled;
+ const char **fp;
+ char brandstr[CPU_IDSTRLEN];
+ struct cpuid_regs cpr;
+ int maxeax;
+ int std_ecx, std_edx, ext_ecx, ext_edx;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_CPUINFO);
+
+ mutex_enter(&cpu_lock);
+ pools_enabled = pool_pset_enabled();
+
+ cp = cpstart = CPU->cpu_part->cp_cpulist;
+ do {
+ /*
+ * This returns the maximum eax value for standard cpuid
+ * functions in eax.
+ */
+ cpr.cp_eax = 0;
+ (void) cpuid_insn(cp, &cpr);
+ maxeax = cpr.cp_eax;
+
+ /*
+ * Get standard x86 feature flags.
+ */
+ cpr.cp_eax = 1;
+ (void) cpuid_insn(cp, &cpr);
+ std_ecx = cpr.cp_ecx;
+ std_edx = cpr.cp_edx;
+
+ /*
+ * Now get extended feature flags.
+ */
+ cpr.cp_eax = 0x80000001;
+ (void) cpuid_insn(cp, &cpr);
+ ext_ecx = cpr.cp_ecx;
+ ext_edx = cpr.cp_edx;
+
+ (void) cpuid_getbrandstr(cp, brandstr, CPU_IDSTRLEN);
+
+ lxpr_uiobuf_printf(uiobuf,
+ "processor\t: %d\n"
+ "vendor_id\t: %s\n"
+ "cpu family\t: %d\n"
+ "model\t\t: %d\n"
+ "model name\t: %s\n"
+ "stepping\t: %d\n"
+ "cpu MHz\t\t: %u.%03u\n",
+ cp->cpu_id, cpuid_getvendorstr(cp), cpuid_getfamily(cp),
+ cpuid_getmodel(cp), brandstr, cpuid_getstep(cp),
+ (uint32_t)(cpu_freq_hz / 1000000),
+ ((uint32_t)(cpu_freq_hz / 1000)) % 1000);
+
+ lxpr_uiobuf_printf(uiobuf, "cache size\t: %u KB\n",
+ getl2cacheinfo(cp, NULL, NULL, NULL) / 1024);
+
+ if (is_x86_feature(x86_featureset, X86FSET_HTT)) {
+ /*
+ * 'siblings' is used for HT-style threads
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "physical id\t: %lu\n"
+ "siblings\t: %u\n",
+ pg_plat_hw_instance_id(cp, PGHW_CHIP),
+ cpuid_get_ncpu_per_chip(cp));
+ }
+
+ /*
+ * Since we're relatively picky about running on older hardware,
+ * we can be somewhat cavalier about the answers to these ones.
+ *
+ * In fact, given the hardware we support, we just say:
+ *
+ * fdiv_bug : no (if we're on a 64-bit kernel)
+ * hlt_bug : no
+ * f00f_bug : no
+ * coma_bug : no
+ * wp : yes (write protect in supervsr mode)
+ */
+ lxpr_uiobuf_printf(uiobuf,
+ "fdiv_bug\t: %s\n"
+ "hlt_bug \t: no\n"
+ "f00f_bug\t: no\n"
+ "coma_bug\t: no\n"
+ "fpu\t\t: %s\n"
+ "fpu_exception\t: %s\n"
+ "cpuid level\t: %d\n"
+ "flags\t\t:",
+#if defined(__i386)
+ fpu_pentium_fdivbug ? "yes" : "no",
+#else
+ "no",
+#endif /* __i386 */
+ fpu_exists ? "yes" : "no", fpu_exists ? "yes" : "no",
+ maxeax);
+
+ for (bits = std_edx, fp = intc_edx, i = 0;
+ i < sizeof (intc_edx) / sizeof (intc_edx[0]); fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+
+ /*
+ * name additional features where appropriate
+ */
+ switch (x86_vendor) {
+ case X86_VENDOR_Intel:
+ for (bits = ext_edx, fp = intc_x_edx, i = 0;
+ i < sizeof (intc_x_edx) / sizeof (intc_x_edx[0]);
+ fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+ break;
+
+ case X86_VENDOR_AMD:
+ for (bits = ext_edx, fp = amd_x_edx, i = 0;
+ i < sizeof (amd_x_edx) / sizeof (amd_x_edx[0]);
+ fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+
+ for (bits = ext_ecx, fp = amd_x_ecx, i = 0;
+ i < sizeof (amd_x_ecx) / sizeof (amd_x_ecx[0]);
+ fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+ break;
+
+ case X86_VENDOR_TM:
+ for (bits = ext_edx, fp = tm_x_edx, i = 0;
+ i < sizeof (tm_x_edx) / sizeof (tm_x_edx[0]);
+ fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+ break;
+ default:
+ break;
+ }
+
+ for (bits = std_ecx, fp = intc_ecx, i = 0;
+ i < sizeof (intc_ecx) / sizeof (intc_ecx[0]); fp++, i++)
+ if ((bits & (1 << i)) != 0 && *fp)
+ lxpr_uiobuf_printf(uiobuf, " %s", *fp);
+
+ lxpr_uiobuf_printf(uiobuf, "\n\n");
+
+ if (pools_enabled)
+ cp = cp->cpu_next_part;
+ else
+ cp = cp->cpu_next;
+ } while (cp != cpstart);
+
+ mutex_exit(&cpu_lock);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_fd(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_FD_FD);
+ lxpr_uiobuf_seterr(uiobuf, EFAULT);
+}
+
+/*
+ * lxpr_getattr(): Vnode operation for VOP_GETATTR()
+ */
+static int
+lxpr_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ register lxpr_node_t *lxpnp = VTOLXP(vp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ extern uint_t nproc;
+ int error;
+
+ /*
+ * Return attributes of underlying vnode if ATTR_REAL
+ *
+ * but keep fd files with the symlink permissions
+ */
+ if (lxpnp->lxpr_realvp != NULL && (flags & ATTR_REAL)) {
+ vnode_t *rvp = lxpnp->lxpr_realvp;
+
+ /*
+ * withold attribute information to owner or root
+ */
+ if ((error = VOP_ACCESS(rvp, 0, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * now its attributes
+ */
+ if ((error = VOP_GETATTR(rvp, vap, flags, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * if it's a file in lx /proc/pid/fd/xx then set its
+ * mode and keep it looking like a symlink
+ */
+ if (type == LXPR_PID_FD_FD) {
+ vap->va_mode = lxpnp->lxpr_mode;
+ vap->va_type = vp->v_type;
+ vap->va_size = 0;
+ vap->va_nlink = 1;
+ }
+ return (0);
+ }
+
+ /* Default attributes, that may be overridden below */
+ bzero(vap, sizeof (*vap));
+ vap->va_atime = vap->va_mtime = vap->va_ctime = lxpnp->lxpr_time;
+ vap->va_nlink = 1;
+ vap->va_type = vp->v_type;
+ vap->va_mode = lxpnp->lxpr_mode;
+ vap->va_fsid = vp->v_vfsp->vfs_dev;
+ vap->va_blksize = DEV_BSIZE;
+ vap->va_uid = lxpnp->lxpr_uid;
+ vap->va_gid = lxpnp->lxpr_gid;
+ vap->va_nodeid = lxpnp->lxpr_ino;
+
+ switch (type) {
+ case LXPR_PROCDIR:
+ vap->va_nlink = nproc + 2 + PROCDIRFILES;
+ vap->va_size = (nproc + 2 + PROCDIRFILES) * LXPR_SDSIZE;
+ break;
+ case LXPR_PIDDIR:
+ vap->va_nlink = PIDDIRFILES;
+ vap->va_size = PIDDIRFILES * LXPR_SDSIZE;
+ break;
+ case LXPR_SELF:
+ vap->va_uid = crgetruid(curproc->p_cred);
+ vap->va_gid = crgetrgid(curproc->p_cred);
+ break;
+ default:
+ break;
+ }
+
+ vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
+ return (0);
+}
+
+/*
+ * lxpr_access(): Vnode operation for VOP_ACCESS()
+ */
+static int
+lxpr_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
+{
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ int shift = 0;
+ proc_t *tp;
+
+ /* lx /proc is a read only file system */
+ if (mode & VWRITE)
+ return (EROFS);
+
+ /*
+ * If this is a restricted file, check access permissions.
+ */
+ switch (lxpnp->lxpr_type) {
+ case LXPR_PIDDIR:
+ return (0);
+ case LXPR_PID_CURDIR:
+ case LXPR_PID_ENV:
+ case LXPR_PID_EXE:
+ case LXPR_PID_MAPS:
+ case LXPR_PID_MEM:
+ case LXPR_PID_ROOTDIR:
+ case LXPR_PID_FDDIR:
+ case LXPR_PID_FD_FD:
+ if ((tp = lxpr_lock(lxpnp->lxpr_pid)) == NULL)
+ return (ENOENT);
+ if (tp != curproc && secpolicy_proc_access(cr) != 0 &&
+ priv_proc_cred_perm(cr, tp, NULL, mode) != 0) {
+ lxpr_unlock(tp);
+ return (EACCES);
+ }
+ lxpr_unlock(tp);
+ default:
+ break;
+ }
+
+ if (lxpnp->lxpr_realvp != NULL) {
+ /*
+ * For these we use the underlying vnode's accessibility.
+ */
+ return (VOP_ACCESS(lxpnp->lxpr_realvp, mode, flags, cr, ct));
+ }
+
+ /* If user is root allow access regardless of permission bits */
+ if (secpolicy_proc_access(cr) == 0)
+ return (0);
+
+ /*
+ * Access check is based on only one of owner, group, public. If not
+ * owner, then check group. If not a member of the group, then check
+ * public access.
+ */
+ if (crgetuid(cr) != lxpnp->lxpr_uid) {
+ shift += 3;
+ if (!groupmember((uid_t)lxpnp->lxpr_gid, cr))
+ shift += 3;
+ }
+
+ mode &= ~(lxpnp->lxpr_mode << shift);
+
+ if (mode == 0)
+ return (0);
+
+ return (EACCES);
+}
+
+/* ARGSUSED */
+static vnode_t *
+lxpr_lookup_not_a_dir(vnode_t *dp, char *comp)
+{
+ return (NULL);
+}
+
+/*
+ * lxpr_lookup(): Vnode operation for VOP_LOOKUP()
+ */
+/* ARGSUSED */
+static int
+lxpr_lookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp,
+ int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+ int *direntflags, pathname_t *realpnp)
+{
+ lxpr_node_t *lxpnp = VTOLXP(dp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ int error;
+
+ ASSERT(dp->v_type == VDIR);
+ ASSERT(type < LXPR_NFILES);
+
+ /*
+ * we should never get here because the lookup
+ * is done on the realvp for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR);
+
+ /*
+ * restrict lookup permission to owner or root
+ */
+ if ((error = lxpr_access(dp, VEXEC, 0, cr, ct)) != 0) {
+ return (error);
+ }
+
+ /*
+ * Just return the parent vnode if that's where we are trying to go.
+ */
+ if (strcmp(comp, "..") == 0) {
+ VN_HOLD(lxpnp->lxpr_parent);
+ *vpp = lxpnp->lxpr_parent;
+ return (0);
+ }
+
+ /*
+ * Special handling for directory searches. Note: null component name
+ * denotes that the current directory is being searched.
+ */
+ if ((dp->v_type == VDIR) && (*comp == '\0' || strcmp(comp, ".") == 0)) {
+ VN_HOLD(dp);
+ *vpp = dp;
+ return (0);
+ }
+
+ *vpp = (lxpr_lookup_function[type](dp, comp));
+ return ((*vpp == NULL) ? ENOENT : 0);
+}
+
+/*
+ * Do a sequential search on the given directory table
+ */
+static vnode_t *
+lxpr_lookup_common(vnode_t *dp, char *comp, proc_t *p,
+ lxpr_dirent_t *dirtab, int dirtablen)
+{
+ lxpr_node_t *lxpnp;
+ int count;
+
+ for (count = 0; count < dirtablen; count++) {
+ if (strcmp(dirtab[count].d_name, comp) == 0) {
+ lxpnp = lxpr_getnode(dp, dirtab[count].d_type, p, 0);
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+ return (dp);
+ }
+ }
+ return (NULL);
+}
+
+static vnode_t *
+lxpr_lookup_piddir(vnode_t *dp, char *comp)
+{
+ proc_t *p;
+
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PIDDIR);
+
+ p = lxpr_lock(VTOLXP(dp)->lxpr_pid);
+ if (p == NULL)
+ return (NULL);
+
+ dp = lxpr_lookup_common(dp, comp, p, piddir, PIDDIRFILES);
+
+ lxpr_unlock(p);
+
+ return (dp);
+}
+
+/*
+ * Lookup one of the process's open files.
+ */
+static vnode_t *
+lxpr_lookup_fddir(vnode_t *dp, char *comp)
+{
+ lxpr_node_t *dlxpnp = VTOLXP(dp);
+ lxpr_node_t *lxpnp;
+ vnode_t *vp = NULL;
+ proc_t *p;
+ file_t *fp;
+ uint_t fd;
+ int c;
+ uf_entry_t *ufp;
+ uf_info_t *fip;
+
+ ASSERT(dlxpnp->lxpr_type == LXPR_PID_FDDIR);
+
+ /*
+ * convert the string rendition of the filename
+ * to a file descriptor
+ */
+ fd = 0;
+ while ((c = *comp++) != '\0') {
+ int ofd;
+ if (c < '0' || c > '9')
+ return (NULL);
+
+ ofd = fd;
+ fd = 10*fd + c - '0';
+ /* integer overflow */
+ if (fd / 10 != ofd)
+ return (NULL);
+ }
+
+ /*
+ * get the proc to work with and lock it
+ */
+ p = lxpr_lock(dlxpnp->lxpr_pid);
+ if ((p == NULL))
+ return (NULL);
+
+ /*
+ * If the process is a zombie or system process
+ * it can't have any open files.
+ */
+ if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+ (p->p_as == &kas)) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ /*
+ * get us a fresh node/vnode
+ */
+ lxpnp = lxpr_getnode(dp, LXPR_PID_FD_FD, p, fd);
+
+ /*
+ * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+ * going away while we dereference into fi_list.
+ */
+ mutex_exit(&p->p_lock);
+
+ /*
+ * get open file info
+ */
+ fip = (&(p)->p_user.u_finfo);
+ mutex_enter(&fip->fi_lock);
+
+ if (fd < fip->fi_nfiles) {
+ UF_ENTER(ufp, fip, fd);
+ /*
+ * ensure the fd is still kosher.
+ * it may have gone between the readdir and
+ * the lookup
+ */
+ if (fip->fi_list[fd].uf_file == NULL) {
+ mutex_exit(&fip->fi_lock);
+ UF_EXIT(ufp);
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ lxpr_freenode(lxpnp);
+ return (NULL);
+ }
+
+ if ((fp = ufp->uf_file) != NULL)
+ vp = fp->f_vnode;
+ UF_EXIT(ufp);
+ }
+ mutex_exit(&fip->fi_lock);
+
+ if (vp == NULL) {
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ lxpr_freenode(lxpnp);
+ return (NULL);
+ } else {
+ /*
+ * Fill in the lxpr_node so future references will be able to
+ * find the underlying vnode. The vnode is held on the realvp.
+ */
+ lxpnp->lxpr_realvp = vp;
+ VN_HOLD(lxpnp->lxpr_realvp);
+ }
+
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+
+ return (dp);
+}
+
+static vnode_t *
+lxpr_lookup_netdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_NETDIR);
+
+ dp = lxpr_lookup_common(dp, comp, NULL, netdir, NETDIRFILES);
+
+ return (dp);
+}
+
+static vnode_t *
+lxpr_lookup_procdir(vnode_t *dp, char *comp)
+{
+ ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PROCDIR);
+
+ /*
+ * We know all the names of files & dirs in our file system structure
+ * except those that are pid names. These change as pids are created/
+ * deleted etc., so we just look for a number as the first char to see
+ * if we are we doing pid lookups.
+ *
+ * Don't need to check for "self" as it is implemented as a symlink
+ */
+ if (*comp >= '0' && *comp <= '9') {
+ pid_t pid = 0;
+ lxpr_node_t *lxpnp = NULL;
+ proc_t *p;
+ int c;
+
+ while ((c = *comp++) != '\0')
+ pid = 10 * pid + c - '0';
+
+ /*
+ * Can't continue if the process is still loading or it doesn't
+ * really exist yet (or maybe it just died!)
+ */
+ p = lxpr_lock(pid);
+ if (p == NULL)
+ return (NULL);
+
+ if (secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+ lxpr_unlock(p);
+ return (NULL);
+ }
+
+ /*
+ * allocate and fill in a new lxpr node
+ */
+ lxpnp = lxpr_getnode(dp, LXPR_PIDDIR, p, 0);
+
+ lxpr_unlock(p);
+
+ dp = LXPTOV(lxpnp);
+ ASSERT(dp != NULL);
+
+ return (dp);
+ }
+
+ /* Lookup fixed names */
+ return (lxpr_lookup_common(dp, comp, NULL, lxpr_dir, PROCDIRFILES));
+}
+
+/*
+ * lxpr_readdir(): Vnode operation for VOP_READDIR()
+ */
+/* ARGSUSED */
+static int
+lxpr_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ lxpr_node_t *lxpnp = VTOLXP(dp);
+ lxpr_nodetype_t type = lxpnp->lxpr_type;
+ ssize_t uresid;
+ off_t uoffset;
+ int error;
+
+ ASSERT(dp->v_type == VDIR);
+ ASSERT(type < LXPR_NFILES);
+
+ /*
+ * we should never get here because the readdir
+ * is done on the realvp for these nodes
+ */
+ ASSERT(type != LXPR_PID_FD_FD &&
+ type != LXPR_PID_CURDIR &&
+ type != LXPR_PID_ROOTDIR);
+
+ /*
+ * restrict readdir permission to owner or root
+ */
+ if ((error = lxpr_access(dp, VREAD, 0, cr, ct)) != 0)
+ return (error);
+
+ uoffset = uiop->uio_offset;
+ uresid = uiop->uio_resid;
+
+ /* can't do negative reads */
+ if (uoffset < 0 || uresid <= 0)
+ return (EINVAL);
+
+ /* can't read directory entries that don't exist! */
+ if (uoffset % LXPR_SDSIZE)
+ return (ENOENT);
+
+ return (lxpr_readdir_function[lxpnp->lxpr_type](lxpnp, uiop, eofp));
+}
+
+/* ARGSUSED */
+static int
+lxpr_readdir_not_a_dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ return (ENOTDIR);
+}
+
+/*
+ * This has the common logic for returning directory entries
+ */
+static int
+lxpr_readdir_common(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp,
+ lxpr_dirent_t *dirtab, int dirtablen)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+
+ oresid = uiop->uio_resid;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Satisfy user request
+ */
+ while ((uresid = uiop->uio_resid) > 0) {
+ int dirindex;
+ off_t uoffset;
+ int reclen;
+ int error;
+
+ uoffset = uiop->uio_offset;
+ dirindex = (uoffset / LXPR_SDSIZE) - 2;
+
+ if (uoffset == 0) {
+
+ dirent->d_ino = lxpnp->lxpr_ino;
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '\0';
+ reclen = DIRENT64_RECLEN(1);
+
+ } else if (uoffset == LXPR_SDSIZE) {
+
+ dirent->d_ino = lxpr_parentinode(lxpnp);
+ dirent->d_name[0] = '.';
+ dirent->d_name[1] = '.';
+ dirent->d_name[2] = '\0';
+ reclen = DIRENT64_RECLEN(2);
+
+ } else if (dirindex >= 0 && dirindex < dirtablen) {
+ int slen = strlen(dirtab[dirindex].d_name);
+
+ dirent->d_ino = lxpr_inode(dirtab[dirindex].d_type,
+ lxpnp->lxpr_pid, 0);
+
+ VERIFY(slen < LXPNSIZ);
+ (void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+ reclen = DIRENT64_RECLEN(slen);
+
+ } else {
+ /* Run out of table entries */
+ if (eofp) {
+ *eofp = 1;
+ }
+ return (0);
+ }
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ /*
+ * if the size of the data to transfer is greater
+ * that that requested then we can't do it this transfer.
+ */
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid) {
+ return (EINVAL);
+ }
+ break;
+ }
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+ * by the same amount. But we want uiop->uio_offset to change
+ * in increments of LXPR_SDSIZE, which is different from the
+ * number of bytes being returned to the user. So we set
+ * uiop->uio_offset separately, ignoring what uiomove() does.
+ */
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ return (error);
+
+ uiop->uio_offset = uoffset + LXPR_SDSIZE;
+ }
+
+ /* Have run out of space, but could have just done last table entry */
+ if (eofp) {
+ *eofp =
+ (uiop->uio_offset >= ((dirtablen+2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+ return (0);
+}
+
+
+static int
+lxpr_readdir_procdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+ off_t uoffset;
+ zoneid_t zoneid;
+ pid_t pid;
+ int error;
+ int ceof;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PROCDIR);
+
+ oresid = uiop->uio_resid;
+ zoneid = LXPTOZ(lxpnp)->zone_id;
+
+ /*
+ * We return directory entries in the order: "." and ".." then the
+ * unique lxproc files, then the directories corresponding to the
+ * running processes. We have defined this as the ordering because
+ * it allows us to more easily keep track of where we are betwen calls
+ * to getdents(). If the number of processes changes between calls
+ * then we can't lose track of where we are in the lxproc files.
+ */
+
+ /* Do the fixed entries */
+ error = lxpr_readdir_common(lxpnp, uiop, &ceof, lxpr_dir,
+ PROCDIRFILES);
+
+ /* Finished if we got an error or if we couldn't do all the table */
+ if (error != 0 || ceof == 0)
+ return (error);
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /* Do the process entries */
+ while ((uresid = uiop->uio_resid) > 0) {
+ proc_t *p;
+ int len;
+ int reclen;
+ int i;
+
+ uoffset = uiop->uio_offset;
+
+ /*
+ * Stop when entire proc table has been examined.
+ */
+ i = (uoffset / LXPR_SDSIZE) - 2 - PROCDIRFILES;
+ if (i < 0 || i >= v.v_proc) {
+ /* Run out of table entries */
+ if (eofp) {
+ *eofp = 1;
+ }
+ return (0);
+ }
+ mutex_enter(&pidlock);
+
+ /*
+ * Skip indices for which there is no pid_entry, PIDs for
+ * which there is no corresponding process, a PID of 0,
+ * and anything the security policy doesn't allow
+ * us to look at.
+ */
+ if ((p = pid_entry(i)) == NULL || p->p_stat == SIDL ||
+ p->p_pid == 0 ||
+ secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+ mutex_exit(&pidlock);
+ goto next;
+ }
+ mutex_exit(&pidlock);
+
+ /*
+ * Convert pid to the Linux default of 1 if we're the zone's
+ * init process, otherwise use the value from the proc
+ * structure
+ */
+ pid = ((p->p_pid != curproc->p_zone->zone_proc_initpid) ?
+ p->p_pid : 1);
+
+ /*
+ * If this /proc was mounted in the global zone, view
+ * all procs; otherwise, only view zone member procs.
+ */
+ if (zoneid != GLOBAL_ZONEID && p->p_zone->zone_id != zoneid) {
+ goto next;
+ }
+
+ ASSERT(p->p_stat != 0);
+
+ dirent->d_ino = lxpr_inode(LXPR_PIDDIR, pid, 0);
+ len = snprintf(dirent->d_name, LXPNSIZ, "%d", pid);
+ ASSERT(len < LXPNSIZ);
+ reclen = DIRENT64_RECLEN(len);
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ /*
+ * if the size of the data to transfer is greater
+ * that that requested then we can't do it this transfer.
+ */
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid)
+ return (EINVAL);
+ break;
+ }
+
+ /*
+ * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+ * by the same amount. But we want uiop->uio_offset to change
+ * in increments of LXPR_SDSIZE, which is different from the
+ * number of bytes being returned to the user. So we set
+ * uiop->uio_offset separately, in the increment of this for
+ * the loop, ignoring what uiomove() does.
+ */
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ return (error);
+next:
+ uiop->uio_offset = uoffset + LXPR_SDSIZE;
+ }
+
+ if (eofp != NULL) {
+ *eofp = (uiop->uio_offset >=
+ ((v.v_proc + PROCDIRFILES + 2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+
+ return (0);
+}
+
+static int
+lxpr_readdir_piddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ proc_t *p;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PIDDIR);
+
+ /* can't read its contents if it died */
+ mutex_enter(&pidlock);
+
+ p = prfind((lxpnp->lxpr_pid == 1) ?
+ curproc->p_zone->zone_proc_initpid : lxpnp->lxpr_pid);
+
+ if (p == NULL || p->p_stat == SIDL) {
+ mutex_exit(&pidlock);
+ return (ENOENT);
+ }
+ mutex_exit(&pidlock);
+
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, piddir, PIDDIRFILES));
+}
+
+static int
+lxpr_readdir_netdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ ASSERT(lxpnp->lxpr_type == LXPR_NETDIR);
+ return (lxpr_readdir_common(lxpnp, uiop, eofp, netdir, NETDIRFILES));
+}
+
+static int
+lxpr_readdir_fddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+ /* bp holds one dirent64 structure */
+ longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+ dirent64_t *dirent = (dirent64_t *)bp;
+ ssize_t oresid; /* save a copy for testing later */
+ ssize_t uresid;
+ off_t uoffset;
+ int error;
+ int ceof;
+ proc_t *p;
+ int fddirsize = -1;
+ uf_info_t *fip;
+
+ ASSERT(lxpnp->lxpr_type == LXPR_PID_FDDIR);
+
+ oresid = uiop->uio_resid;
+
+ /* can't read its contents if it died */
+ p = lxpr_lock(lxpnp->lxpr_pid);
+ if (p == NULL)
+ return (ENOENT);
+
+ if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+ (p->p_as == &kas))
+ fddirsize = 0;
+
+ /*
+ * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+ * going away while we iterate over its fi_list.
+ */
+ mutex_exit(&p->p_lock);
+
+ /* Get open file info */
+ fip = (&(p)->p_user.u_finfo);
+ mutex_enter(&fip->fi_lock);
+
+ if (fddirsize == -1)
+ fddirsize = fip->fi_nfiles;
+
+ /* Do the fixed entries (in this case just "." & "..") */
+ error = lxpr_readdir_common(lxpnp, uiop, &ceof, 0, 0);
+
+ /* Finished if we got an error or if we couldn't do all the table */
+ if (error != 0 || ceof == 0)
+ goto out;
+
+ /* clear out the dirent buffer */
+ bzero(bp, sizeof (bp));
+
+ /*
+ * Loop until user's request is satisfied or until
+ * all file descriptors have been examined.
+ */
+ for (; (uresid = uiop->uio_resid) > 0;
+ uiop->uio_offset = uoffset + LXPR_SDSIZE) {
+ int reclen;
+ int fd;
+ int len;
+
+ uoffset = uiop->uio_offset;
+
+ /*
+ * Stop at the end of the fd list
+ */
+ fd = (uoffset / LXPR_SDSIZE) - 2;
+ if (fd < 0 || fd >= fddirsize) {
+ if (eofp) {
+ *eofp = 1;
+ }
+ goto out;
+ }
+
+ if (fip->fi_list[fd].uf_file == NULL)
+ continue;
+
+ dirent->d_ino = lxpr_inode(LXPR_PID_FD_FD, lxpnp->lxpr_pid, fd);
+ len = snprintf(dirent->d_name, LXPNSIZ, "%d", fd);
+ ASSERT(len < LXPNSIZ);
+ reclen = DIRENT64_RECLEN(len);
+
+ dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+ dirent->d_reclen = (ushort_t)reclen;
+
+ if (reclen > uresid) {
+ /*
+ * Error if no entries have been returned yet.
+ */
+ if (uresid == oresid)
+ error = EINVAL;
+ goto out;
+ }
+
+ if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+ uiop)) != 0)
+ goto out;
+ }
+
+ if (eofp != NULL) {
+ *eofp =
+ (uiop->uio_offset >= ((fddirsize+2) * LXPR_SDSIZE)) ? 1 : 0;
+ }
+
+out:
+ mutex_exit(&fip->fi_lock);
+ mutex_enter(&p->p_lock);
+ lxpr_unlock(p);
+ return (error);
+}
+
+
+/*
+ * lxpr_readlink(): Vnode operation for VOP_READLINK()
+ */
+/* ARGSUSED */
+static int
+lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
+{
+ char bp[MAXPATHLEN + 1];
+ size_t buflen = sizeof (bp);
+ lxpr_node_t *lxpnp = VTOLXP(vp);
+ vnode_t *rvp = lxpnp->lxpr_realvp;
+ pid_t pid;
+ int error = 0;
+
+ /* must be a symbolic link file */
+ if (vp->v_type != VLNK)
+ return (EINVAL);
+
+ /* Try to produce a symlink name for anything that has a realvp */
+ if (rvp != NULL) {
+ if ((error = lxpr_access(vp, VREAD, 0, CRED(), ct)) != 0)
+ return (error);
+ if ((error = vnodetopath(NULL, rvp, bp, buflen, CRED())) != 0)
+ return (error);
+ } else {
+ switch (lxpnp->lxpr_type) {
+ case LXPR_SELF:
+ /*
+ * Convert pid to the Linux default of 1 if we're the
+ * zone's init process
+ */
+ pid = ((curproc->p_pid !=
+ curproc->p_zone->zone_proc_initpid)
+ ? curproc->p_pid : 1);
+
+ /*
+ * Don't need to check result as every possible int
+ * will fit within MAXPATHLEN bytes.
+ */
+ (void) snprintf(bp, buflen, "%d", pid);
+ break;
+ case LXPR_PID_CURDIR:
+ case LXPR_PID_ROOTDIR:
+ case LXPR_PID_EXE:
+ return (EACCES);
+ default:
+ /*
+ * Need to return error so that nothing thinks
+ * that the symlink is empty and hence "."
+ */
+ return (EINVAL);
+ }
+ }
+
+ /* copy the link data to user space */
+ return (uiomove(bp, strlen(bp), UIO_READ, uiop));
+}
+
+/*
+ * lxpr_inactive(): Vnode operation for VOP_INACTIVE()
+ * Vnode is no longer referenced, deallocate the file
+ * and all its resources.
+ */
+/* ARGSUSED */
+static void
+lxpr_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+ lxpr_freenode(VTOLXP(vp));
+}
+
+/*
+ * lxpr_sync(): Vnode operation for VOP_SYNC()
+ */
+static int
+lxpr_sync()
+{
+ /*
+ * Nothing to sync but this function must never fail
+ */
+ return (0);
+}
+
+/*
+ * lxpr_cmp(): Vnode operation for VOP_CMP()
+ */
+static int
+lxpr_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+ vnode_t *rvp;
+
+ while (vn_matchops(vp1, lxpr_vnodeops) &&
+ (rvp = VTOLXP(vp1)->lxpr_realvp) != NULL) {
+ vp1 = rvp;
+ }
+
+ while (vn_matchops(vp2, lxpr_vnodeops) &&
+ (rvp = VTOLXP(vp2)->lxpr_realvp) != NULL) {
+ vp2 = rvp;
+ }
+
+ if (vn_matchops(vp1, lxpr_vnodeops) || vn_matchops(vp2, lxpr_vnodeops))
+ return (vp1 == vp2);
+
+ return (VOP_CMP(vp1, vp2, ct));
+}
+
+/*
+ * lxpr_realvp(): Vnode operation for VOP_REALVP()
+ */
+static int
+lxpr_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
+{
+ vnode_t *rvp;
+
+ if ((rvp = VTOLXP(vp)->lxpr_realvp) != NULL) {
+ vp = rvp;
+ if (VOP_REALVP(vp, &rvp, ct) == 0)
+ vp = rvp;
+ }
+
+ *vpp = vp;
+ return (0);
+}
diff --git a/usr/src/uts/common/fs/lxproc/lxproc.h b/usr/src/uts/common/fs/lxproc/lxproc.h
new file mode 100644
index 0000000000..eadb2ccd27
--- /dev/null
+++ b/usr/src/uts/common/fs/lxproc/lxproc.h
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+#ifndef _LXPROC_H
+#define _LXPROC_H
+
+#ifdef _LXPROC_BRANDED_H
+#error Attempted to include native lxproc.h after branded lx_proc.h
+#endif
+
+#define _LXPROC_NATIVE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * lxproc.h: declarations, data structures and macros for lxprocfs
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/debug.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/user.h>
+#include <sys/t_lock.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/dnlc.h>
+#include <sys/atomic.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <vm/as.h>
+#include <vm/anon.h>
+
+#define LX_SIGHUP 1
+#define LX_SIGINT 2
+#define LX_SIGQUIT 3
+#define LX_SIGILL 4
+#define LX_SIGTRAP 5
+#define LX_SIGABRT 6
+#define LX_SIGIOT 6
+#define LX_SIGBUS 7
+#define LX_SIGFPE 8
+#define LX_SIGKILL 9
+#define LX_SIGUSR1 10
+#define LX_SIGSEGV 11
+#define LX_SIGUSR2 12
+#define LX_SIGPIPE 13
+#define LX_SIGALRM 14
+#define LX_SIGTERM 15
+#define LX_SIGSTKFLT 16
+#define LX_SIGCHLD 17
+#define LX_SIGCONT 18
+#define LX_SIGSTOP 19
+#define LX_SIGTSTP 20
+#define LX_SIGTTIN 21
+#define LX_SIGTTOU 22
+#define LX_SIGURG 23
+#define LX_SIGXCPU 24
+#define LX_SIGXFSZ 25
+#define LX_SIGVTALRM 26
+#define LX_SIGPROF 27
+#define LX_SIGWINCH 28
+#define LX_SIGIO 29
+#define LX_SIGPOLL LX_SIGIO
+#define LX_SIGPWR 30
+#define LX_SIGSYS 31
+#define LX_SIGUNUSED 31
+
+#define LX_NSIG 64 /* Linux _NSIG */
+
+#define LX_SIGRTMIN 32
+#define LX_SIGRTMAX LX_NSIG
+
+/*
+ * Convert a vnode into an lxpr_mnt_t
+ */
+#define VTOLXPM(vp) ((lxpr_mnt_t *)(vp)->v_vfsp->vfs_data)
+
+/*
+ * convert a vnode into an lxpr_node
+ */
+#define VTOLXP(vp) ((lxpr_node_t *)(vp)->v_data)
+
+/*
+ * convert a lxprnode into a vnode
+ */
+#define LXPTOV(lxpnp) ((lxpnp)->lxpr_vnode)
+
+/*
+ * convert a lxpr_node into zone for fs
+ */
+#define LXPTOZ(lxpnp) \
+ (((lxpr_mnt_t *)(lxpnp)->lxpr_vnode->v_vfsp->vfs_data)->lxprm_zone)
+
+#define LXPNSIZ 256 /* max size of lx /proc file name entries */
+
+/*
+ * Pretend that a directory entry takes 16 bytes
+ */
+#define LXPR_SDSIZE 16
+
+/*
+ * Node/file types for lx /proc files
+ * (directories and files contained therein).
+ */
+typedef enum lxpr_nodetype {
+ LXPR_PROCDIR, /* /proc */
+ LXPR_PIDDIR, /* /proc/<pid> */
+ LXPR_PID_CMDLINE, /* /proc/<pid>/cmdline */
+ LXPR_PID_CPU, /* /proc/<pid>/cpu */
+ LXPR_PID_CURDIR, /* /proc/<pid>/cwd */
+ LXPR_PID_ENV, /* /proc/<pid>/environ */
+ LXPR_PID_EXE, /* /proc/<pid>/exe */
+ LXPR_PID_MAPS, /* /proc/<pid>/maps */
+ LXPR_PID_MEM, /* /proc/<pid>/mem */
+ LXPR_PID_ROOTDIR, /* /proc/<pid>/root */
+ LXPR_PID_STAT, /* /proc/<pid>/stat */
+ LXPR_PID_STATM, /* /proc/<pid>/statm */
+ LXPR_PID_STATUS, /* /proc/<pid>/status */
+ LXPR_PID_FDDIR, /* /proc/<pid>/fd */
+ LXPR_PID_FD_FD, /* /proc/<pid>/fd/nn */
+ LXPR_CMDLINE, /* /proc/cmdline */
+ LXPR_CPUINFO, /* /proc/cpuinfo */
+ LXPR_DEVICES, /* /proc/devices */
+ LXPR_DMA, /* /proc/dma */
+ LXPR_FILESYSTEMS, /* /proc/filesystems */
+ LXPR_INTERRUPTS, /* /proc/interrupts */
+ LXPR_IOPORTS, /* /proc/ioports */
+ LXPR_KCORE, /* /proc/kcore */
+ LXPR_KMSG, /* /proc/kmsg */
+ LXPR_LOADAVG, /* /proc/loadavg */
+ LXPR_MEMINFO, /* /proc/meminfo */
+ LXPR_MOUNTS, /* /proc/mounts */
+ LXPR_NETDIR, /* /proc/net */
+ LXPR_NET_ARP, /* /proc/net/arp */
+ LXPR_NET_DEV, /* /proc/net/dev */
+ LXPR_NET_DEV_MCAST, /* /proc/net/dev_mcast */
+ LXPR_NET_IGMP, /* /proc/net/igmp */
+ LXPR_NET_IP_MR_CACHE, /* /proc/net/ip_mr_cache */
+ LXPR_NET_IP_MR_VIF, /* /proc/net/ip_mr_vif */
+ LXPR_NET_MCFILTER, /* /proc/net/mcfilter */
+ LXPR_NET_NETSTAT, /* /proc/net/netstat */
+ LXPR_NET_RAW, /* /proc/net/raw */
+ LXPR_NET_ROUTE, /* /proc/net/route */
+ LXPR_NET_RPC, /* /proc/net/rpc */
+ LXPR_NET_RT_CACHE, /* /proc/net/rt_cache */
+ LXPR_NET_SOCKSTAT, /* /proc/net/sockstat */
+ LXPR_NET_SNMP, /* /proc/net/snmp */
+ LXPR_NET_STAT, /* /proc/net/stat */
+ LXPR_NET_TCP, /* /proc/net/tcp */
+ LXPR_NET_UDP, /* /proc/net/udp */
+ LXPR_NET_UNIX, /* /proc/net/unix */
+ LXPR_PARTITIONS, /* /proc/partitions */
+ LXPR_SELF, /* /proc/self */
+ LXPR_STAT, /* /proc/stat */
+ LXPR_UPTIME, /* /proc/uptime */
+ LXPR_VERSION, /* /proc/version */
+ LXPR_NFILES /* number of lx /proc file types */
+} lxpr_nodetype_t;
+
+/*
+ * Number of fds allowed for in the inode number calculation
+ * per process (if a process has more fds then inode numbers
+ * may be duplicated)
+ */
+#define LXPR_FD_PERPROC 2000
+
+/*
+ * external dirent characteristics
+ */
+#define LXPRMAXNAMELEN 14
+typedef struct {
+ lxpr_nodetype_t d_type;
+ char d_name[LXPRMAXNAMELEN];
+} lxpr_dirent_t;
+
+/*
+ * This is the lxprocfs private data object
+ * which is attached to v_data in the vnode structure
+ */
+typedef struct lxpr_node {
+ lxpr_nodetype_t lxpr_type; /* type of this node */
+ vnode_t *lxpr_vnode; /* vnode for the node */
+ vnode_t *lxpr_parent; /* parent directory */
+ vnode_t *lxpr_realvp; /* real vnode, file in dirs */
+ timestruc_t lxpr_time; /* creation etc time for file */
+ mode_t lxpr_mode; /* file mode bits */
+ uid_t lxpr_uid; /* file owner */
+ gid_t lxpr_gid; /* file group owner */
+ pid_t lxpr_pid; /* pid of proc referred to */
+ ino_t lxpr_ino; /* node id */
+} lxpr_node_t;
+
+struct zone; /* forward declaration */
+
+/*
+ * This is the lxprocfs private data object
+ * which is attached to vfs_data in the vfs structure
+ */
+typedef struct lxpr_mnt {
+ lxpr_node_t *lxprm_node; /* node at root of proc mount */
+ struct zone *lxprm_zone; /* zone for this mount */
+ ldi_ident_t lxprm_li; /* ident for ldi */
+} lxpr_mnt_t;
+
+extern vnodeops_t *lxpr_vnodeops;
+extern int nproc_highbit; /* highbit(v.v_nproc) */
+
+typedef struct mounta mounta_t;
+
+extern void lxpr_initnodecache();
+extern void lxpr_fininodecache();
+extern void lxpr_initrootnode(lxpr_node_t **, vfs_t *);
+extern ino_t lxpr_inode(lxpr_nodetype_t, pid_t, int);
+extern ino_t lxpr_parentinode(lxpr_node_t *);
+extern lxpr_node_t *lxpr_getnode(vnode_t *, lxpr_nodetype_t, proc_t *, int);
+extern void lxpr_freenode(lxpr_node_t *);
+
+typedef struct lxpr_uiobuf lxpr_uiobuf_t;
+extern lxpr_uiobuf_t *lxpr_uiobuf_new(uio_t *);
+extern void lxpr_uiobuf_free(lxpr_uiobuf_t *);
+extern int lxpr_uiobuf_flush(lxpr_uiobuf_t *);
+extern void lxpr_uiobuf_seek(lxpr_uiobuf_t *, offset_t);
+extern void lxpr_uiobuf_write(lxpr_uiobuf_t *, const char *, size_t);
+extern void lxpr_uiobuf_printf(lxpr_uiobuf_t *, const char *, ...);
+extern void lxpr_uiobuf_seterr(lxpr_uiobuf_t *, int);
+
+proc_t *lxpr_lock(pid_t);
+void lxpr_unlock(proc_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LXPROC_H */
diff --git a/usr/src/uts/common/fs/nfs/nfs3_vfsops.c b/usr/src/uts/common/fs/nfs/nfs3_vfsops.c
index d6a88a97c3..f6c6b62925 100644
--- a/usr/src/uts/common/fs/nfs/nfs3_vfsops.c
+++ b/usr/src/uts/common/fs/nfs/nfs3_vfsops.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, Joyent, Inc. All rights reserved.
*/
/*
diff --git a/usr/src/uts/common/fs/nfs/nfs3_vnops.c b/usr/src/uts/common/fs/nfs/nfs3_vnops.c
index 74d47dd93d..279cc60877 100644
--- a/usr/src/uts/common/fs/nfs/nfs3_vnops.c
+++ b/usr/src/uts/common/fs/nfs/nfs3_vnops.c
@@ -29,7 +29,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2022 Oxide Computer Company
*/
@@ -3354,10 +3354,9 @@ nfs3rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
if (nvp)
vnevent_rename_dest(nvp, ndvp, nnm, ct);
- if (odvp != ndvp)
- vnevent_rename_dest_dir(ndvp, ct);
ASSERT(ovp != NULL);
vnevent_rename_src(ovp, odvp, onm, ct);
+ vnevent_rename_dest_dir(ndvp, ovp, nnm, ct);
}
if (nvp) {
@@ -5537,8 +5536,13 @@ nfs3_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
va.va_size = bfp->l_start;
error = nfs3setattr(vp, &va, 0, cr);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
} else
error = EINVAL;
}
diff --git a/usr/src/uts/common/fs/nfs/nfs4_vfsops.c b/usr/src/uts/common/fs/nfs/nfs4_vfsops.c
index f0320aaee0..25088aafcb 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_vfsops.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_vfsops.c
@@ -22,6 +22,7 @@
/*
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, Joyent, Inc. All rights reserved.
*/
/*
diff --git a/usr/src/uts/common/fs/nfs/nfs4_vnops.c b/usr/src/uts/common/fs/nfs/nfs4_vnops.c
index 2a501bc898..b57ad066e4 100644
--- a/usr/src/uts/common/fs/nfs/nfs4_vnops.c
+++ b/usr/src/uts/common/fs/nfs/nfs4_vnops.c
@@ -38,7 +38,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright 2022 Oxide Computer Company
*/
@@ -3757,8 +3757,13 @@ nfs4_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
*/
error = nfs4setattr(vp, vap, flags, cr, NULL);
- if (error == 0 && (vap->va_mask & AT_SIZE) && vap->va_size == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0 && (vap->va_mask & AT_SIZE)) {
+ if (vap->va_size == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
return (error);
}
@@ -8074,8 +8079,9 @@ link_call:
* vnode if it already existed.
*/
if (error == 0) {
- vnode_t *tvp;
+ vnode_t *tvp, *tovp;
rnode4_t *trp;
+
/*
* Notify the vnode. Each links is represented by
* a different vnode, in nfsv4.
@@ -8088,23 +8094,20 @@ link_call:
vnevent_rename_dest(tvp, ndvp, nnm, ct);
}
- /*
- * if the source and destination directory are not the
- * same notify the destination directory.
- */
- if (VTOR4(odvp) != VTOR4(ndvp)) {
- trp = VTOR4(ndvp);
- tvp = ndvp;
- if (IS_SHADOW(ndvp, trp))
- tvp = RTOV4(trp);
- vnevent_rename_dest_dir(tvp, ct);
- }
-
trp = VTOR4(ovp);
- tvp = ovp;
+ tovp = ovp;
if (IS_SHADOW(ovp, trp))
+ tovp = RTOV4(trp);
+
+ vnevent_rename_src(tovp, odvp, onm, ct);
+
+ trp = VTOR4(ndvp);
+ tvp = ndvp;
+
+ if (IS_SHADOW(ndvp, trp))
tvp = RTOV4(trp);
- vnevent_rename_src(tvp, odvp, onm, ct);
+
+ vnevent_rename_dest_dir(tvp, tovp, nnm, ct);
}
if (nvp) {
@@ -11021,8 +11024,13 @@ nfs4_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
va.va_size = bfp->l_start;
error = nfs4setattr(vp, &va, 0, cr, NULL);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
} else
error = EINVAL;
}
diff --git a/usr/src/uts/common/fs/nfs/nfs_sys.c b/usr/src/uts/common/fs/nfs/nfs_sys.c
index 434c9a2a3e..8048d13ca3 100644
--- a/usr/src/uts/common/fs/nfs/nfs_sys.c
+++ b/usr/src/uts/common/fs/nfs/nfs_sys.c
@@ -30,6 +30,7 @@
*/
/*
+ * Copyright 2017 Joyent, Inc.
* Copyright 2018 Nexenta Systems, Inc.
*/
@@ -242,7 +243,7 @@ nfssys(enum nfssys_op opcode, void *arg)
lsa.n_fmly = STRUCT_FGET(ulsa, n_fmly);
lsa.n_proto = STRUCT_FGET(ulsa, n_proto);
lsa.n_rdev = expldev(STRUCT_FGET(ulsa, n_rdev));
- lsa.debug = STRUCT_FGET(ulsa, debug);
+ lsa.n_v4_only = STRUCT_FGET(ulsa, n_v4_only);
lsa.timout = STRUCT_FGET(ulsa, timout);
lsa.grace = STRUCT_FGET(ulsa, grace);
lsa.retransmittimeout = STRUCT_FGET(ulsa,
diff --git a/usr/src/uts/common/fs/nfs/nfs_vfsops.c b/usr/src/uts/common/fs/nfs/nfs_vfsops.c
index c9cc306f95..5041ebb6fe 100644
--- a/usr/src/uts/common/fs/nfs/nfs_vfsops.c
+++ b/usr/src/uts/common/fs/nfs/nfs_vfsops.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, Joyent, Inc. All rights reserved.
*
* Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
* All rights reserved.
diff --git a/usr/src/uts/common/fs/nfs/nfs_vnops.c b/usr/src/uts/common/fs/nfs/nfs_vnops.c
index 1a1082bcb8..ee3bac484f 100644
--- a/usr/src/uts/common/fs/nfs/nfs_vnops.c
+++ b/usr/src/uts/common/fs/nfs/nfs_vnops.c
@@ -26,7 +26,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
@@ -1174,8 +1174,13 @@ nfs_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
error = nfssetattr(vp, vap, flags, cr);
- if (error == 0 && (mask & AT_SIZE) && vap->va_size == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0 && (mask & AT_SIZE)) {
+ if (vap->va_size == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
return (error);
}
@@ -2688,11 +2693,9 @@ nfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr,
if (nvp)
vnevent_rename_dest(nvp, ndvp, nnm, ct);
- if (odvp != ndvp)
- vnevent_rename_dest_dir(ndvp, ct);
-
ASSERT(ovp != NULL);
vnevent_rename_src(ovp, odvp, onm, ct);
+ vnevent_rename_dest_dir(ndvp, ovp, nnm, ct);
}
if (nvp) {
@@ -4620,8 +4623,13 @@ nfs_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag,
va.va_size = bfp->l_start;
error = nfssetattr(vp, &va, 0, cr);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
} else
error = EINVAL;
}
diff --git a/usr/src/uts/common/fs/pcfs/pc_dir.c b/usr/src/uts/common/fs/pcfs/pc_dir.c
index 976715e346..275330a0ae 100644
--- a/usr/src/uts/common/fs/pcfs/pc_dir.c
+++ b/usr/src/uts/common/fs/pcfs/pc_dir.c
@@ -22,7 +22,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/param.h>
@@ -826,8 +826,7 @@ top:
if (error == 0) {
vnevent_rename_src(PCTOV(pcp), PCTOV(dp), snm, ctp);
- if (dp != tdp)
- vnevent_rename_dest_dir(PCTOV(tdp), ctp);
+ vnevent_rename_dest_dir(PCTOV(tdp), PCTOV(pcp), tnm, ctp);
}
done:
diff --git a/usr/src/uts/common/fs/pcfs/pc_vnops.c b/usr/src/uts/common/fs/pcfs/pc_vnops.c
index 013a6d3352..1965444071 100644
--- a/usr/src/uts/common/fs/pcfs/pc_vnops.c
+++ b/usr/src/uts/common/fs/pcfs/pc_vnops.c
@@ -782,8 +782,11 @@ pcfs_setattr(
if (error)
goto out;
- if (vap->va_size == 0)
+ if (vap->va_size == 0) {
vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
}
/*
* Change file modified times.
diff --git a/usr/src/uts/common/fs/portfs/port_fop.c b/usr/src/uts/common/fs/portfs/port_fop.c
index c9c417fda8..e11d5c8be4 100644
--- a/usr/src/uts/common/fs/portfs/port_fop.c
+++ b/usr/src/uts/common/fs/portfs/port_fop.c
@@ -24,7 +24,7 @@
*/
/*
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
* Copyright 2022 Oxide Computer Company
*/
@@ -540,14 +540,14 @@ port_fop_trimpfplist(vnode_t *vp)
port_pcache_remove_fop(pfcp, pfp);
mutex_exit(&pfcp->pfc_lock);
if (tdvp != NULL)
- VN_RELE(tdvp);
+ VN_PHANTOM_RELE(tdvp);
}
}
}
/*
* This routine returns 1, if the vnode can be rele'ed by the caller.
- * The caller has to VN_RELE the vnode with out holding any
+ * The caller has to VN_PHANTOM_RELE the vnode with out holding any
* locks.
*/
int
@@ -617,7 +617,7 @@ port_fop_femuninstall(vnode_t *vp)
* able to remove it from the port's queue).
*
* vpp and dvpp will point to the vnode and directory vnode which the caller
- * is required to VN_RELE without holding any locks.
+ * is required to VN_PHANTOM_RELE without holding any locks.
*/
int
port_remove_fop(portfop_t *pfp, portfop_cache_t *pfcp, int cleanup,
@@ -727,8 +727,8 @@ port_cache_lookup_fop(portfop_cache_t *pfcp, pid_t pid, uintptr_t obj)
/*
* Given the file name, get the vnode and also the directory vnode
- * On return, the vnodes are held (VN_HOLD). The caller has to VN_RELE
- * the vnode(s).
+ * On return, the vnodes are held with phantom holds (VN_PHANTOM_HOLD). The
+ * caller has to VN_PHANTOM_RELE the vnode(s).
*/
int
port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp, char **cname,
@@ -778,6 +778,17 @@ port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp, char **cname,
}
}
+ /* Trade VN_HOLD()s from lookuppn with VN_PHANTOM_HOLD()s */
+ if (dvp != NULL && *dvp != NULL) {
+ VN_PHANTOM_HOLD(*dvp);
+ VN_RELE(*dvp);
+ }
+
+ if (vp != NULL && *vp != NULL) {
+ VN_PHANTOM_HOLD(*vp);
+ VN_RELE(*vp);
+ }
+
pn_free(&pn);
return (error);
}
@@ -1177,7 +1188,7 @@ port_pfp_setup(portfop_t **pfpp, port_t *pp, vnode_t *vp, portfop_cache_t *pfcp,
* Hold a reference to the vnode since
* we successfully installed the hooks.
*/
- VN_HOLD(vp);
+ VN_PHANTOM_HOLD(vp);
} else {
(void) fem_uninstall(vp, femp, vp);
pvp->pvp_femp = NULL;
@@ -1210,7 +1221,7 @@ port_pfp_setup(portfop_t **pfpp, port_t *pp, vnode_t *vp, portfop_cache_t *pfcp,
* Hold the directory vnode since we have a reference now.
*/
if (dvp != NULL)
- VN_HOLD(dvp);
+ VN_PHANTOM_HOLD(dvp);
*pfpp = pfp;
return (0);
}
@@ -1225,9 +1236,9 @@ port_resolve_vp(vnode_t *vp)
*/
if (vfs_mntdummyvp && mntfstype != 0 &&
vp->v_vfsp->vfs_fstype == mntfstype) {
- VN_RELE(vp);
+ VN_PHANTOM_RELE(vp);
vp = vfs_mntdummyvp;
- VN_HOLD(vfs_mntdummyvp);
+ VN_PHANTOM_HOLD(vfs_mntdummyvp);
}
/*
@@ -1235,8 +1246,8 @@ port_resolve_vp(vnode_t *vp)
* hardlinks.
*/
if ((VOP_REALVP(vp, &rvp, NULL) == 0) && vp != rvp) {
- VN_HOLD(rvp);
- VN_RELE(vp);
+ VN_PHANTOM_HOLD(rvp);
+ VN_PHANTOM_RELE(vp);
vp = rvp;
}
return (vp);
@@ -1248,10 +1259,10 @@ port_resolve_vp(vnode_t *vp)
* The association is identified by the object pointer and the pid.
* The events argument contains the events to be monitored for.
*
- * The vnode will have a VN_HOLD once the fem hooks are installed.
+ * The vnode will have a VN_PHANTOM_HOLD once the fem hooks are installed.
*
- * Every reference(pfp) to the directory vnode will have a VN_HOLD to ensure
- * that the directory vnode pointer does not change.
+ * Every reference(pfp) to the directory vnode will have a VN_PHANTOM_HOLD to
+ * ensure that the directory vnode pointer does not change.
*/
int
port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
@@ -1331,7 +1342,7 @@ port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
*/
if (dvp != NULL && dvp->v_vfsp != vp->v_vfsp &&
!(orig->v_type == VPROC && vp != NULL && vp->v_type != VPROC)) {
- VN_RELE(dvp);
+ VN_PHANTOM_RELE(dvp);
dvp = NULL;
}
@@ -1351,8 +1362,8 @@ port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
/*
- * If it is not the same vnode, just discard it. VN_RELE needs to be
- * called with no locks held, therefore save vnode pointers and
+ * If it is not the same vnode, just discard it. VN_PHANTOM_RELE needs
+ * to be called with no locks held, therefore save vnode pointers and
* vn_rele them later.
*/
if (pfp != NULL && (pfp->pfop_vp != vp || pfp->pfop_dvp != dvp)) {
@@ -1405,7 +1416,7 @@ port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
* This vnode pointer is just used
* for comparison, so rele it
*/
- VN_RELE(tvp);
+ VN_PHANTOM_RELE(tvp);
}
}
@@ -1438,8 +1449,8 @@ port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
* active and it is not being removed from
* the vnode list. This is checked in
* port_remove_fop with the vnode lock held.
- * The vnode returned is VN_RELE'ed after dropping
- * the locks.
+ * The vnode returned is VN_PHANTOM_RELE'ed after
+ * dropping the locks.
*/
tdvp = tvp = NULL;
if (port_remove_fop(pfp, pfcp, 0, NULL, &tvp, &tdvp)) {
@@ -1452,9 +1463,9 @@ port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
}
mutex_exit(&pfcp->pfc_lock);
if (tvp != NULL)
- VN_RELE(tvp);
+ VN_PHANTOM_RELE(tvp);
if (tdvp != NULL)
- VN_RELE(tdvp);
+ VN_PHANTOM_RELE(tdvp);
goto errout;
}
} else {
@@ -1521,14 +1532,14 @@ errout:
* Release the hold acquired due to the lookup operation.
*/
if (vp != NULL)
- VN_RELE(vp);
+ VN_PHANTOM_RELE(vp);
if (dvp != NULL)
- VN_RELE(dvp);
+ VN_PHANTOM_RELE(dvp);
if (oldvp != NULL)
- VN_RELE(oldvp);
+ VN_PHANTOM_RELE(oldvp);
if (olddvp != NULL)
- VN_RELE(olddvp);
+ VN_PHANTOM_RELE(olddvp);
/*
* copied file name not used, free it.
@@ -1589,9 +1600,9 @@ port_dissociate_fop(port_t *pp, uintptr_t object)
(void) port_remove_fop(pfp, pfcp, 1, &active, &tvp, &tdvp);
mutex_exit(&pfcp->pfc_lock);
if (tvp != NULL)
- VN_RELE(tvp);
+ VN_PHANTOM_RELE(tvp);
if (tdvp != NULL)
- VN_RELE(tdvp);
+ VN_PHANTOM_RELE(tdvp);
return (active ? 0 : ENOENT);
}
@@ -1629,7 +1640,7 @@ port_close_fop(void *arg, int port, pid_t pid, int lastclose)
* be possible as the port is being closed.
*
* The common case is that the port is not shared and all the entries
- * are of this pid and have to be freed. Since VN_RELE has to be
+ * are of this pid and have to be freed. Since VN_PHANTOM_RELE has to be
* called outside the lock, we do it in batches.
*/
hashtbl = (portfop_t **)pfcp->pfc_hash;
@@ -1656,14 +1667,14 @@ port_close_fop(void *arg, int port, pid_t pid, int lastclose)
if (pfp == NULL)
index++;
/*
- * Now call VN_RELE if we have collected enough vnodes or
- * we have reached the end of the hash table.
+ * Now call VN_PHANTOM_RELE if we have collected enough vnodes
+ * or we have reached the end of the hash table.
*/
if (i >= (PORTFOP_NVP - 1) ||
(i > 0 && index == PORTFOP_HASHSIZE)) {
mutex_exit(&pfcp->pfc_lock);
while (i > 0) {
- VN_RELE(vpl[--i]);
+ VN_PHANTOM_RELE(vpl[--i]);
vpl[i] = NULL;
}
mutex_enter(&pfcp->pfc_lock);
@@ -1771,7 +1782,7 @@ port_fop_excep(list_t *tlist, int op)
port_pcache_remove_fop(pfcp, pfp);
mutex_exit(&pfcp->pfc_lock);
if (tdvp != NULL)
- VN_RELE(tdvp);
+ VN_PHANTOM_RELE(tdvp);
}
}
@@ -1935,7 +1946,7 @@ port_fop_sendevent(vnode_t *vp, int events, vnode_t *dvp, char *cname)
* that may be attempting to remove an object from the vnode's.
*/
if (port_fop_femuninstall(vp))
- VN_RELE(vp);
+ VN_PHANTOM_RELE(vp);
/*
* Send exception events and discard the watch entries.
@@ -2070,7 +2081,7 @@ port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr)
* unmount is in process.
*/
port_fop_sendevent(pvp->pvp_vp, UNMOUNTED, NULL, NULL);
- VN_RELE(pvp->pvp_vp);
+ VN_PHANTOM_RELE(pvp->pvp_vp);
}
error = vfsnext_unmount(vf, flag, cr);
diff --git a/usr/src/uts/common/fs/proc/prargv.c b/usr/src/uts/common/fs/proc/prargv.c
new file mode 100644
index 0000000000..60d098d125
--- /dev/null
+++ b/usr/src/uts/common/fs/proc/prargv.c
@@ -0,0 +1,530 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/sunddi.h>
+#include <sys/proc.h>
+#include <sys/procfs.h>
+#include <sys/sysmacros.h>
+#include <vm/as.h>
+
+/*
+ * Safely read a contiguous region of memory from 'addr' in the address space
+ * of a particular process into the supplied kernel buffer (*buf, sz).
+ * Partially mapped regions will result in a partial read terminating at the
+ * first hole in the address space. The number of bytes actually read is
+ * returned to the caller via 'rdsz'.
+ */
+int
+prreadbuf(proc_t *p, uintptr_t ustart, char *buf, size_t sz, size_t *rdsz)
+{
+ int error = 0;
+ size_t rem = sz;
+ off_t pos = 0;
+
+ if (rdsz != NULL)
+ *rdsz = 0;
+
+ while (rem != 0) {
+ uintptr_t addr = ustart + pos;
+ size_t len = MIN(rem, PAGESIZE - (addr & PAGEOFFSET));
+
+ if ((error = uread(p, buf + pos, len, addr)) != 0) {
+ if (error == ENXIO) {
+ /*
+ * ENXIO from uread() indicates that the page
+ * does not exist. This will simply be a
+ * partial read.
+ */
+ error = 0;
+ }
+ break;
+ }
+
+ rem -= len;
+ pos += len;
+ }
+
+ if (rdsz != NULL)
+ *rdsz = pos;
+
+ return (error);
+}
+
+
+/*
+ * Effectively a truncating version of copyinstr().
+ *
+ * The resulting string is guaranteed to be truncated to fit within the buffer
+ * (hence sz == 0 is not supported). The returned size includes the truncating
+ * NUL.
+ */
+int
+prreadstr(proc_t *p, uintptr_t ustart, char *buf, size_t bufsz, size_t *rdsz)
+{
+ size_t slen;
+ int err;
+
+ VERIFY(bufsz != 0);
+
+ if ((err = prreadbuf(p, ustart, buf, bufsz, &slen)) != 0)
+ return (err);
+
+ slen = strnlen(buf, slen);
+
+ if (slen == bufsz)
+ slen--;
+
+ buf[slen++] = '\0';
+
+ if (rdsz != NULL)
+ *rdsz = slen;
+ return (0);
+}
+
+/*
+ * /proc/pid/cmdline: Linux-compatible '\0'-separated process argv.
+ *
+ * Unlike /proc/pid/argv, this looks at the exec()-time argv string area, rather
+ * than starting from the argv[] array. Thus changes to the array are not
+ * noticed, but direct modifications of the string are visible here. Since it's
+ * common for applications to expect it, we implement the Linux semantics here.
+ *
+ * There is special handling if the process has modified its argv: if the last
+ * byte of the argv string area is no longer NUL, then we presume that it has
+ * done setproctitle() or similar, and we should copy it as a single string from
+ * the start, even though it overflows into the env string area. Note that we
+ * can't use copyinstr() as that returns ENAMETOOLONG rather than truncating as
+ * we need.
+ *
+ * Otherwise, we provide the argv string area in toto.
+ */
+int
+prreadcmdline(proc_t *p, char *buf, size_t bufsz, size_t *slen)
+{
+ user_t *up = &p->p_user;
+ uint8_t term;
+ int err = 0;
+
+ VERIFY(bufsz == PRMAXARGVLEN);
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ if ((p->p_flag & SSYS) || p->p_as == &kas || up->u_argvstrsize == 0) {
+ bcopy(up->u_psargs, buf, MIN(bufsz, sizeof (up->u_psargs)));
+ buf[bufsz - 1] = '\0';
+ *slen = strlen(buf) + 1;
+ return (0);
+ }
+
+ VERIFY(up->u_argvstrs != (uintptr_t)NULL);
+
+ mutex_exit(&p->p_lock);
+
+ if (uread(p, &term, sizeof (term),
+ up->u_argvstrs + up->u_argvstrsize - 1) != 0) {
+ err = EFAULT;
+ goto out;
+ }
+
+ if (term != '\0') {
+ err = prreadstr(p, up->u_argvstrs, buf, bufsz, slen);
+ } else {
+ size_t size = MIN(bufsz, up->u_argvstrsize);
+ err = prreadbuf(p, up->u_argvstrs, buf, size, slen);
+ }
+
+out:
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ return (err);
+}
+
+
+/*
+ * Attempt to read the argument vector (argv) from this process. The caller
+ * must hold the p_lock mutex, and have marked the process P_PR_LOCK (e.g. via
+ * prlock or lx_prlock).
+ *
+ * The caller must provide a buffer (buf, buflen). We will concatenate each
+ * argument string (including the NUL terminator) into this buffer. The number
+ * of characters written to this buffer (including the final NUL terminator)
+ * will be stored in 'slen'.
+ */
+int
+prreadargv(proc_t *p, char *buf, size_t bufsz, size_t *slen)
+{
+ int error;
+ user_t *up;
+ struct as *as;
+ size_t pos = 0;
+ caddr_t *argv = NULL;
+ size_t argvsz = 0;
+ int i;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+
+ up = PTOU(p);
+ as = p->p_as;
+
+ if ((p->p_flag & SSYS) || as == &kas || up->u_argv == (uintptr_t)NULL) {
+ /*
+ * Return the regular psargs string to the caller.
+ */
+ bcopy(up->u_psargs, buf, MIN(bufsz, sizeof (up->u_psargs)));
+ buf[bufsz - 1] = '\0';
+ *slen = strlen(buf) + 1;
+
+ return (0);
+ }
+
+ /*
+ * Allocate space to store argv array.
+ */
+ argvsz = up->u_argc * (p->p_model == DATAMODEL_ILP32 ?
+ sizeof (caddr32_t) : sizeof (caddr_t));
+ argv = kmem_alloc(argvsz, KM_SLEEP);
+
+ /*
+ * Extract the argv array from the target process. Drop p_lock
+ * while we do I/O to avoid deadlock with the clock thread.
+ */
+ mutex_exit(&p->p_lock);
+ if ((error = prreadbuf(p, up->u_argv, (char *)argv,
+ argvsz, NULL)) != 0) {
+ kmem_free(argv, argvsz);
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ return (-1);
+ }
+
+ /*
+ * Read each argument string from the pointers in the argv array.
+ */
+ pos = 0;
+ for (i = 0; i < up->u_argc; i++) {
+ size_t rdsz, trysz;
+ uintptr_t arg;
+ off_t j;
+ boolean_t found_nul;
+ boolean_t do_retry = B_TRUE;
+
+#ifdef _SYSCALL32_IMPL
+ if (p->p_model == DATAMODEL_ILP32) {
+ arg = (uintptr_t)((caddr32_t *)argv)[i];
+ } else {
+ arg = (uintptr_t)argv[i];
+ }
+#else
+ arg = (uintptr_t)argv[i];
+#endif
+
+ /*
+ * Stop trying to read arguments if we reach a NULL
+ * pointer in the vector.
+ */
+ if (arg == (uintptr_t)NULL)
+ break;
+
+ /*
+ * Stop reading if we have read the maximum length
+ * we can return to the user.
+ */
+ if (pos >= bufsz)
+ break;
+
+ /*
+ * Initially we try a short read, on the assumption that
+ * most individual argument strings are less than 80
+ * characters long.
+ */
+ if ((trysz = MIN(80, bufsz - pos - 1)) < 80) {
+ /*
+ * We don't have room in the target buffer for even
+ * an entire short read, so there is no need to retry
+ * with a longer read.
+ */
+ do_retry = B_FALSE;
+ }
+
+retry:
+ /*
+ * Read string data for this argument. Leave room
+ * in the buffer for a final NUL terminator.
+ */
+ if ((error = prreadbuf(p, arg, (char *)&buf[pos], trysz,
+ &rdsz)) != 0) {
+ /*
+ * There was a problem reading this string
+ * from the process. Give up.
+ */
+ break;
+ }
+
+ /*
+ * Find the NUL terminator.
+ */
+ found_nul = B_FALSE;
+ for (j = 0; j < rdsz; j++) {
+ if (buf[pos + j] == '\0') {
+ found_nul = B_TRUE;
+ break;
+ }
+ }
+
+ if (!found_nul && do_retry) {
+ /*
+ * We did not find a NUL terminator, but this
+ * was a first pass short read. Try once more
+ * with feeling.
+ */
+ trysz = bufsz - pos - 1;
+ do_retry = B_FALSE;
+ goto retry;
+ }
+
+ /*
+ * Commit the string we read to the buffer.
+ */
+ pos += j + 1;
+ if (!found_nul && pos < bufsz) {
+ /*
+ * A NUL terminator was not found; add one.
+ */
+ buf[pos++] = '\0';
+ }
+ }
+
+ /*
+ * Ensure the entire string is NUL-terminated.
+ */
+ buf[bufsz - 1] = '\0';
+
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ kmem_free(argv, argvsz);
+
+ /*
+ * If the operation was a success, return the copied string length
+ * to the caller.
+ */
+ *slen = (error == 0) ? pos : 0;
+
+ return (error);
+}
+
+/*
+ * Similar to prreadargv except reads the env vector. This is slightly more
+ * complex because there is no count for the env vector that corresponds to
+ * u_argc.
+ */
+int
+prreadenvv(proc_t *p, char *buf, size_t bufsz, size_t *slen)
+{
+ int error;
+ user_t *up;
+ struct as *as;
+ size_t pos = 0;
+ caddr_t *envp = NULL;
+ uintptr_t tmpp = (uintptr_t)NULL;
+ size_t envpsz = 0, rdsz = 0;
+ int i;
+ int cnt, bound;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+
+ up = PTOU(p);
+ as = p->p_as;
+
+ if ((p->p_flag & SSYS) || as == &kas || up->u_envp == (uintptr_t)NULL) {
+ /*
+ * Return empty string.
+ */
+ buf[0] = '\0';
+ *slen = 1;
+
+ return (0);
+ }
+
+ /*
+ * Drop p_lock while we do I/O to avoid deadlock with the clock thread.
+ */
+ mutex_exit(&p->p_lock);
+
+ /*
+ * We first have to count how many env entries we have. This is
+ * somewhat painful. We extract the env entries from the target process
+ * one entry at a time. Stop trying to read env entries if we reach a
+ * NULL pointer in the vector or hit our upper bound (which we take
+ * as the bufsz/4) to ensure we don't run off.
+ */
+ rdsz = (p->p_model == DATAMODEL_ILP32 ?
+ sizeof (caddr32_t) : sizeof (caddr_t));
+ bound = (int)(bufsz / 4);
+ for (cnt = 0, tmpp = up->u_envp; cnt < bound; cnt++, tmpp += rdsz) {
+ caddr_t tmp = NULL;
+
+ if ((error = prreadbuf(p, tmpp, (char *)&tmp, rdsz,
+ NULL)) != 0) {
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ return (-1);
+ }
+
+ if (tmp == NULL)
+ break;
+ }
+ if (cnt == 0) {
+ /* Return empty string. */
+ buf[0] = '\0';
+ *slen = 1;
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ return (0);
+ }
+
+ /*
+ * Allocate space to store env array.
+ */
+ envpsz = cnt * (p->p_model == DATAMODEL_ILP32 ?
+ sizeof (caddr32_t) : sizeof (caddr_t));
+ envp = kmem_alloc(envpsz, KM_SLEEP);
+
+ /*
+ * Extract the env array from the target process.
+ */
+ if ((error = prreadbuf(p, up->u_envp, (char *)envp, envpsz,
+ NULL)) != 0) {
+ kmem_free(envp, envpsz);
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ return (-1);
+ }
+
+ /*
+ * Read each env string from the pointers in the env array.
+ */
+ pos = 0;
+ for (i = 0; i < cnt; i++) {
+ size_t rdsz, trysz;
+ uintptr_t ev;
+ off_t j;
+ boolean_t found_nul;
+ boolean_t do_retry = B_TRUE;
+
+#ifdef _SYSCALL32_IMPL
+ if (p->p_model == DATAMODEL_ILP32) {
+ ev = (uintptr_t)((caddr32_t *)envp)[i];
+ } else {
+ ev = (uintptr_t)envp[i];
+ }
+#else
+ ev = (uintptr_t)envp[i];
+#endif
+
+ /*
+ * Stop trying to read env entries if we reach a NULL
+ * pointer in the vector.
+ */
+ if (ev == (uintptr_t)NULL)
+ break;
+
+ /*
+ * Stop reading if we have read the maximum length
+ * we can return to the user.
+ */
+ if (pos >= bufsz)
+ break;
+
+ /*
+ * Initially we try a short read, on the assumption that
+ * most individual env strings are less than 80
+ * characters long.
+ */
+ if ((trysz = MIN(80, bufsz - pos - 1)) < 80) {
+ /*
+ * We don't have room in the target buffer for even
+ * an entire short read, so there is no need to retry
+ * with a longer read.
+ */
+ do_retry = B_FALSE;
+ }
+
+retry:
+ /*
+ * Read string data for this env var. Leave room
+ * in the buffer for a final NUL terminator.
+ */
+ if ((error = prreadbuf(p, ev, (char *)&buf[pos], trysz,
+ &rdsz)) != 0) {
+ /*
+ * There was a problem reading this string
+ * from the process. Give up.
+ */
+ break;
+ }
+
+ /*
+ * Find the NUL terminator.
+ */
+ found_nul = B_FALSE;
+ for (j = 0; j < rdsz; j++) {
+ if (buf[pos + j] == '\0') {
+ found_nul = B_TRUE;
+ break;
+ }
+ }
+
+ if (!found_nul && do_retry) {
+ /*
+ * We did not find a NUL terminator, but this
+ * was a first pass short read. Try once more
+ * with feeling.
+ */
+ trysz = bufsz - pos - 1;
+ do_retry = B_FALSE;
+ goto retry;
+ }
+
+ /*
+ * Commit the string we read to the buffer.
+ */
+ pos += j + 1;
+ if (!found_nul && pos < bufsz) {
+ /*
+ * A NUL terminator was not found; add one.
+ */
+ buf[pos++] = '\0';
+ }
+ }
+
+ /*
+ * Ensure the entire string is NUL-terminated.
+ */
+ buf[bufsz - 1] = '\0';
+
+ mutex_enter(&p->p_lock);
+ VERIFY(p->p_proc_flag & P_PR_LOCK);
+ kmem_free(envp, envpsz);
+
+ /*
+ * If the operation was a success, return the copied string length
+ * to the caller.
+ */
+ *slen = (error == 0) ? pos : 0;
+
+ return (error);
+}
diff --git a/usr/src/uts/common/fs/proc/prcontrol.c b/usr/src/uts/common/fs/proc/prcontrol.c
index 6b151a6369..07dcb1e7db 100644
--- a/usr/src/uts/common/fs/proc/prcontrol.c
+++ b/usr/src/uts/common/fs/proc/prcontrol.c
@@ -25,7 +25,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
*/
#include <sys/types.h>
@@ -1481,7 +1481,7 @@ pr_setsig(prnode_t *pnp, siginfo_t *sip)
} else if (t->t_state == TS_STOPPED && sig == SIGKILL) {
/* If SIGKILL, set stopped lwp running */
p->p_stopsig = 0;
- t->t_schedflag |= TS_XSTART | TS_PSTART;
+ t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
t->t_dtrace_stop = 0;
setrun_locked(t);
}
@@ -2276,9 +2276,17 @@ pr_szoneid(proc_t *p, zoneid_t zoneid, cred_t *cr)
return (EPERM);
if (zoneid != GLOBAL_ZONEID && zoneid != p->p_zone->zone_id)
return (EINVAL);
- if ((zptr = zone_find_by_id(zoneid)) == NULL)
- return (EINVAL);
+ /*
+ * We cannot hold p_lock when we call zone_find_by_id since that can
+ * lead to a deadlock. zone_find_by_id() takes zonehash_lock.
+ * zone_enter() can hold the zonehash_lock and needs p_lock when it
+ * calls task_join.
+ */
mutex_exit(&p->p_lock);
+ if ((zptr = zone_find_by_id(zoneid)) == NULL) {
+ mutex_enter(&p->p_lock);
+ return (EINVAL);
+ }
mutex_enter(&p->p_crlock);
oldcred = p->p_cred;
crhold(oldcred);
diff --git a/usr/src/uts/common/fs/proc/prdata.h b/usr/src/uts/common/fs/proc/prdata.h
index a661478c50..6d8ac2e6ed 100644
--- a/usr/src/uts/common/fs/proc/prdata.h
+++ b/usr/src/uts/common/fs/proc/prdata.h
@@ -27,7 +27,7 @@
/* All Rights Reserved */
/*
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
*/
@@ -124,6 +124,8 @@ typedef enum prnodetype {
#if defined(__i386) || defined(__amd64)
PR_LDT, /* /proc/<pid>/ldt */
#endif
+ PR_ARGV, /* /proc/<pid>/argv */
+ PR_CMDLINE, /* /proc/<pid>/cmdline */
PR_USAGE, /* /proc/<pid>/usage */
PR_LUSAGE, /* /proc/<pid>/lusage */
PR_PAGEDATA, /* /proc/<pid>/pagedata */
@@ -355,6 +357,9 @@ extern file_t *pr_getf(proc_t *, uint_t, short *);
extern void pr_releasef(file_t *);
extern void pr_setfault(proc_t *, fltset_t *);
extern int prusrio(proc_t *, enum uio_rw, struct uio *, int);
+extern int prreadargv(proc_t *, char *, size_t, size_t *);
+extern int prreadcmdline(proc_t *, char *, size_t, size_t *);
+extern int prreadenvv(proc_t *, char *, size_t, size_t *);
extern int prwritectl(vnode_t *, struct uio *, cred_t *);
extern int prlock(prnode_t *, int);
extern void prunmark(proc_t *);
diff --git a/usr/src/uts/common/fs/proc/prsubr.c b/usr/src/uts/common/fs/proc/prsubr.c
index 5591ffd89b..be41826b54 100644
--- a/usr/src/uts/common/fs/proc/prsubr.c
+++ b/usr/src/uts/common/fs/proc/prsubr.c
@@ -222,6 +222,7 @@ prchoose(proc_t *p)
case PR_SYSEXIT:
case PR_SIGNALLED:
case PR_FAULTED:
+ case PR_BRAND:
/*
* Make an lwp calling exit() be the
* last lwp seen in the process.
@@ -555,6 +556,12 @@ prexecend(void)
pcp->prc_tslot = tslot;
}
}
+
+ /*
+ * There may be threads waiting for the flag change blocked behind the
+ * pr_pid_cv as well.
+ */
+ cv_signal(&pr_pid_cv[p->p_slot]);
}
/*
diff --git a/usr/src/uts/common/fs/proc/prvnops.c b/usr/src/uts/common/fs/proc/prvnops.c
index e535b1f647..39f8e6f01e 100644
--- a/usr/src/uts/common/fs/proc/prvnops.c
+++ b/usr/src/uts/common/fs/proc/prvnops.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
* Copyright (c) 2017 by Delphix. All rights reserved.
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
* Copyright 2022 MNX Cloud, Inc.
@@ -171,8 +171,12 @@ static prdirent_t piddir[] = {
"contracts" },
{ PR_SECFLAGS, 28 * sizeof (prdirent_t), sizeof (prdirent_t),
"secflags" },
+ { PR_ARGV, 29 * sizeof (prdirent_t), sizeof (prdirent_t),
+ "argv" },
+ { PR_CMDLINE, 30 * sizeof (prdirent_t), sizeof (prdirent_t),
+ "cmdline" },
#if defined(__x86)
- { PR_LDT, 29 * sizeof (prdirent_t), sizeof (prdirent_t),
+ { PR_LDT, 31 * sizeof (prdirent_t), sizeof (prdirent_t),
"ldt" },
#endif
};
@@ -593,6 +597,7 @@ static int pr_read_inval(), pr_read_as(), pr_read_status(),
#if defined(__x86)
pr_read_ldt(),
#endif
+ pr_read_argv(), pr_read_cmdline(),
pr_read_usage(), pr_read_lusage(), pr_read_pagedata(),
pr_read_watch(), pr_read_lwpstatus(), pr_read_lwpsinfo(),
pr_read_lwpusage(), pr_read_lwpname(),
@@ -623,6 +628,8 @@ static int (*pr_read_function[PR_NFILES])() = {
#if defined(__x86)
pr_read_ldt, /* /proc/<pid>/ldt */
#endif
+ pr_read_argv, /* /proc/<pid>/argv */
+ pr_read_cmdline, /* /proc/<pid>/cmdline */
pr_read_usage, /* /proc/<pid>/usage */
pr_read_lusage, /* /proc/<pid>/lusage */
pr_read_pagedata, /* /proc/<pid>/pagedata */
@@ -689,6 +696,76 @@ pr_uioread(void *base, long count, uio_t *uiop)
}
static int
+pr_read_cmdline(prnode_t *pnp, uio_t *uiop)
+{
+ char *args;
+ int error;
+ size_t asz = PRMAXARGVLEN, sz;
+
+ /*
+ * Allocate a scratch buffer for collection of the process arguments.
+ */
+ args = kmem_alloc(asz, KM_SLEEP);
+
+ ASSERT(pnp->pr_type == PR_CMDLINE);
+
+ if ((error = prlock(pnp, ZNO)) != 0) {
+ kmem_free(args, asz);
+ return (error);
+ }
+
+ if ((error = prreadcmdline(pnp->pr_common->prc_proc, args, asz,
+ &sz)) != 0) {
+ prunlock(pnp);
+ kmem_free(args, asz);
+ return (error);
+ }
+
+ prunlock(pnp);
+
+ error = pr_uioread(args, sz, uiop);
+
+ kmem_free(args, asz);
+
+ return (error);
+}
+
+static int
+pr_read_argv(prnode_t *pnp, uio_t *uiop)
+{
+ char *args;
+ int error;
+ size_t asz = PRMAXARGVLEN, sz;
+
+ /*
+ * Allocate a scratch buffer for collection of the process arguments.
+ */
+ args = kmem_alloc(asz, KM_SLEEP);
+
+ ASSERT(pnp->pr_type == PR_ARGV);
+
+ if ((error = prlock(pnp, ZNO)) != 0) {
+ kmem_free(args, asz);
+ return (error);
+ }
+
+ if ((error = prreadargv(pnp->pr_common->prc_proc, args, asz,
+ &sz)) != 0) {
+ prunlock(pnp);
+ kmem_free(args, asz);
+ return (error);
+ }
+
+ prunlock(pnp);
+
+ error = pr_uioread(args, sz, uiop);
+
+ kmem_free(args, asz);
+
+ return (error);
+}
+
+static int
pr_read_as(prnode_t *pnp, uio_t *uiop)
{
int error;
@@ -1913,6 +1990,8 @@ static int (*pr_read_function_32[PR_NFILES])() = {
#if defined(__x86)
pr_read_ldt, /* /proc/<pid>/ldt */
#endif
+ pr_read_argv, /* /proc/<pid>/argv */
+ pr_read_cmdline, /* /proc/<pid>/cmdline */
pr_read_usage_32, /* /proc/<pid>/usage */
pr_read_lusage_32, /* /proc/<pid>/lusage */
pr_read_pagedata_32, /* /proc/<pid>/pagedata */
@@ -2841,6 +2920,103 @@ prread(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct)
#endif
}
+/*
+ * We make pr_write_psinfo_fname() somewhat simpler by asserting at compile
+ * time that PRFNSZ has the same definition as MAXCOMLEN.
+ */
+#if PRFNSZ != MAXCOMLEN
+#error PRFNSZ/MAXCOMLEN mismatch
+#endif
+
+static int
+pr_write_psinfo_fname(prnode_t *pnp, uio_t *uiop)
+{
+ char fname[PRFNSZ];
+ int offset = offsetof(psinfo_t, pr_fname), error;
+
+#ifdef _SYSCALL32_IMPL
+ if (curproc->p_model != DATAMODEL_LP64)
+ offset = offsetof(psinfo32_t, pr_fname);
+#endif
+
+ /*
+ * If this isn't a write to pr_fname (or if the size doesn't match
+ * PRFNSZ) return.
+ */
+ if (uiop->uio_offset != offset || uiop->uio_resid != PRFNSZ)
+ return (0);
+
+ if ((error = uiomove(fname, PRFNSZ, UIO_WRITE, uiop)) != 0)
+ return (error);
+
+ fname[PRFNSZ - 1] = '\0';
+
+ if ((error = prlock(pnp, ZNO)) != 0)
+ return (error);
+
+ bcopy(fname, pnp->pr_common->prc_proc->p_user.u_comm, PRFNSZ);
+
+ prunlock(pnp);
+
+ return (0);
+}
+
+/*
+ * We make pr_write_psinfo_psargs() somewhat simpler by asserting at compile
+ * time that PRARGSZ has the same definition as PSARGSZ.
+ */
+#if PRARGSZ != PSARGSZ
+#error PRARGSZ/PSARGSZ mismatch
+#endif
+
+static int
+pr_write_psinfo_psargs(prnode_t *pnp, uio_t *uiop)
+{
+ char psargs[PRARGSZ];
+ int offset = offsetof(psinfo_t, pr_psargs), error;
+
+#ifdef _SYSCALL32_IMPL
+ if (curproc->p_model != DATAMODEL_LP64)
+ offset = offsetof(psinfo32_t, pr_psargs);
+#endif
+
+ /*
+ * If this isn't a write to pr_psargs (or if the size doesn't match
+ * PRARGSZ) return.
+ */
+ if (uiop->uio_offset != offset || uiop->uio_resid != PRARGSZ)
+ return (0);
+
+ if ((error = uiomove(psargs, PRARGSZ, UIO_WRITE, uiop)) != 0)
+ return (error);
+
+ psargs[PRARGSZ - 1] = '\0';
+
+ if ((error = prlock(pnp, ZNO)) != 0)
+ return (error);
+
+ bcopy(psargs, pnp->pr_common->prc_proc->p_user.u_psargs, PRARGSZ);
+
+ prunlock(pnp);
+
+ return (0);
+}
+
+int
+pr_write_psinfo(prnode_t *pnp, uio_t *uiop)
+{
+ int error;
+
+ if ((error = pr_write_psinfo_fname(pnp, uiop)) != 0)
+ return (error);
+
+ if ((error = pr_write_psinfo_psargs(pnp, uiop)) != 0)
+ return (error);
+
+ return (0);
+}
+
+
/* Note we intentionally don't handle partial writes/updates. */
static int
pr_write_lwpname(prnode_t *pnp, uio_t *uiop)
@@ -2967,6 +3143,9 @@ prwrite(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct)
uiop->uio_resid = resid;
return (error);
+ case PR_PSINFO:
+ return (pr_write_psinfo(pnp, uiop));
+
case PR_LWPNAME:
return (pr_write_lwpname(pnp, uiop));
@@ -3296,6 +3475,13 @@ prgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
case PR_AUXV:
vap->va_size = __KERN_NAUXV_IMPL * PR_OBJSIZE(auxv32_t, auxv_t);
break;
+ case PR_ARGV:
+ if ((p->p_flag & SSYS) || p->p_as == &kas) {
+ vap->va_size = PSARGSZ;
+ } else {
+ vap->va_size = PRMAXARGVLEN;
+ }
+ break;
#if defined(__x86)
case PR_LDT:
mutex_exit(&p->p_lock);
@@ -3418,6 +3604,7 @@ prgetattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
#endif
case PR_CTL:
case PR_LWPCTL:
+ case PR_CMDLINE:
default:
vap->va_size = 0;
break;
@@ -3472,6 +3659,8 @@ praccess(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
case PR_USAGE:
case PR_LUSAGE:
case PR_LWPUSAGE:
+ case PR_ARGV:
+ case PR_CMDLINE:
p = pr_p_lock(pnp);
mutex_exit(&pr_pidlock);
if (p == NULL)
@@ -3557,6 +3746,8 @@ static vnode_t *(*pr_lookup_function[PR_NFILES])() = {
#if defined(__x86)
pr_lookup_notdir, /* /proc/<pid>/ldt */
#endif
+ pr_lookup_notdir, /* /proc/<pid>/argv */
+ pr_lookup_notdir, /* /proc/<pid>/cmdline */
pr_lookup_notdir, /* /proc/<pid>/usage */
pr_lookup_notdir, /* /proc/<pid>/lusage */
pr_lookup_notdir, /* /proc/<pid>/pagedata */
@@ -4887,16 +5078,18 @@ prgetnode(vnode_t *dp, prnodetype_t type)
pnp->pr_mode = 0600; /* read-write by owner only */
break;
+ case PR_PSINFO:
case PR_LWPNAME:
pnp->pr_mode = 0644; /* readable by all + owner can write */
break;
- case PR_PSINFO:
case PR_LPSINFO:
case PR_LWPSINFO:
case PR_USAGE:
case PR_LUSAGE:
case PR_LWPUSAGE:
+ case PR_ARGV:
+ case PR_CMDLINE:
pnp->pr_mode = 0444; /* read-only by all */
break;
@@ -5004,6 +5197,8 @@ static int (*pr_readdir_function[PR_NFILES])() = {
#if defined(__x86)
pr_readdir_notdir, /* /proc/<pid>/ldt */
#endif
+ pr_readdir_notdir, /* /proc/<pid>/argv */
+ pr_readdir_notdir, /* /proc/<pid>/cmdline */
pr_readdir_notdir, /* /proc/<pid>/usage */
pr_readdir_notdir, /* /proc/<pid>/lusage */
pr_readdir_notdir, /* /proc/<pid>/pagedata */
@@ -5157,6 +5352,8 @@ pr_readdir_piddir(prnode_t *pnp, uio_t *uiop, int *eofp)
case PR_PROCDIR:
case PR_PSINFO:
case PR_USAGE:
+ case PR_ARGV:
+ case PR_CMDLINE:
break;
default:
continue;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_kshare.c b/usr/src/uts/common/fs/smbsrv/smb_kshare.c
index 01d382fed7..056619d90b 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_kshare.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_kshare.c
@@ -351,6 +351,7 @@ smb_kshare_g_fini(void)
kmem_cache_destroy(smb_kshare_cache_share);
}
+
/*
* A list of shares in nvlist format can be sent down
* from userspace thourgh the IOCTL interface. The nvlist
diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c
index 7f56792f7d..af12a0c30b 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_server.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_server.c
@@ -897,6 +897,22 @@ smb_server_enum(smb_ioc_svcenum_t *ioc)
smb_svcenum_t *svcenum = &ioc->svcenum;
smb_server_t *sv;
int rc;
+ uint32_t buflen_adjusted;
+
+ /*
+ * Reality check that the buffer-length insize the enum doesn't
+ * overrun the ioctl's total length.
+ *
+ * NOTE: Assume se_buf is at the end of smb_svcenum_t.
+ */
+ buflen_adjusted = svcenum->se_buflen +
+ offsetof(smb_svcenum_t, se_buf) + sizeof (ioc->hdr);
+ if (buflen_adjusted < svcenum->se_buflen || /* Overflow check 1, */
+ buflen_adjusted < offsetof(smb_svcenum_t, se_buf) || /* check 2, */
+ buflen_adjusted < sizeof (ioc->hdr) || /* check 3. */
+ buflen_adjusted > ioc->hdr.len) {
+ return (EINVAL);
+ }
/*
* Reality check that the buffer-length insize the enum doesn't
diff --git a/usr/src/uts/common/fs/sockfs/sockcommon_sops.c b/usr/src/uts/common/fs/sockfs/sockcommon_sops.c
index b1f74b993b..768a001d72 100644
--- a/usr/src/uts/common/fs/sockfs/sockcommon_sops.c
+++ b/usr/src/uts/common/fs/sockfs/sockcommon_sops.c
@@ -129,7 +129,7 @@ so_bind(struct sonode *so, struct sockaddr *name, socklen_t namelen,
{
int error;
- SO_BLOCK_FALLBACK(so, SOP_BIND(so, name, namelen, flags, cr));
+ SO_BLOCK_FALLBACK_SAFE(so, SOP_BIND(so, name, namelen, flags, cr));
ASSERT(flags == _SOBIND_XPG4_2 || flags == _SOBIND_SOCKBSD);
@@ -279,7 +279,7 @@ so_connect(struct sonode *so, struct sockaddr *name,
* This can happen if a non blocking operation caused an error.
*/
- if (so->so_error != 0) {
+ if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
mutex_enter(&so->so_lock);
error = sogeterr(so, B_TRUE);
mutex_exit(&so->so_lock);
@@ -378,7 +378,7 @@ so_sendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
break;
}
- if (so->so_error != 0) {
+ if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
mutex_enter(&so->so_lock);
error = sogeterr(so, B_TRUE);
mutex_exit(&so->so_lock);
@@ -487,7 +487,7 @@ so_sendmblk_impl(struct sonode *so, struct nmsghdr *msg, int fflag,
error = EPIPE;
break;
}
- if (so->so_error != 0) {
+ if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
mutex_enter(&so->so_lock);
error = sogeterr(so, B_TRUE);
mutex_exit(&so->so_lock);
@@ -622,7 +622,7 @@ so_getsockname(struct sonode *so, struct sockaddr *addr,
{
int error;
- SO_BLOCK_FALLBACK(so, SOP_GETSOCKNAME(so, addr, addrlen, cr));
+ SO_BLOCK_FALLBACK_SAFE(so, SOP_GETSOCKNAME(so, addr, addrlen, cr));
if (so->so_filter_active == 0 ||
(error = sof_filter_getsockname(so, addr, addrlen, cr)) < 0)
@@ -671,7 +671,7 @@ so_getsockopt(struct sonode *so, int level, int option_name,
if (level == SOL_FILTER)
return (sof_getsockopt(so, option_name, optval, optlenp, cr));
- SO_BLOCK_FALLBACK(so,
+ SO_BLOCK_FALLBACK_SAFE(so,
SOP_GETSOCKOPT(so, level, option_name, optval, optlenp, flags, cr));
if ((so->so_filter_active == 0 ||
@@ -760,7 +760,7 @@ so_setsockopt(struct sonode *so, int level, int option_name,
if (level == SOL_FILTER)
return (sof_setsockopt(so, option_name, optval, optlen, cr));
- SO_BLOCK_FALLBACK(so,
+ SO_BLOCK_FALLBACK_SAFE(so,
SOP_SETSOCKOPT(so, level, option_name, optval, optlen, cr));
/* X/Open requires this check */
@@ -845,7 +845,7 @@ so_ioctl(struct sonode *so, int cmd, intptr_t arg, int mode,
* If there is a pending error, return error
* This can happen if a non blocking operation caused an error.
*/
- if (so->so_error != 0) {
+ if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
mutex_enter(&so->so_lock);
error = sogeterr(so, B_TRUE);
mutex_exit(&so->so_lock);
diff --git a/usr/src/uts/common/fs/sockfs/sockcommon_subr.c b/usr/src/uts/common/fs/sockfs/sockcommon_subr.c
index ab9c479af3..df159a122c 100644
--- a/usr/src/uts/common/fs/sockfs/sockcommon_subr.c
+++ b/usr/src/uts/common/fs/sockfs/sockcommon_subr.c
@@ -671,10 +671,15 @@ so_dequeue_msg(struct sonode *so, mblk_t **mctlp, struct uio *uiop,
int more = 0;
int error;
ssize_t oobmark;
+ ssize_t copied = 0;
sodirect_t *sodp = so->so_direct;
+ xuio_t *xuio = NULL;
partial_read = B_FALSE;
*mctlp = NULL;
+ if ((uiop->uio_extflg & UIO_XUIO) != 0) {
+ xuio = (xuio_t *)uiop;
+ }
again:
mutex_enter(&so->so_lock);
again1:
@@ -785,8 +790,6 @@ again1:
* enabled socket, uio_resid can be 0.
*/
if (uiop->uio_resid >= 0) {
- ssize_t copied = 0;
-
if (sodp != NULL && (DB_FLAGS(mp) & DBLK_UIOA)) {
mutex_enter(&so->so_lock);
ASSERT(uiop == (uio_t *)&sodp->sod_uioa);
@@ -844,6 +847,18 @@ again1:
}
if (mp != NULL) { /* more data blocks in msg */
more |= MOREDATA;
+
+ /*
+ * If requested, tally up remaining data along with the
+ * amount already copied.
+ */
+ if (xuio != NULL &&
+ xuio->xu_type == UIOTYPE_PEEKSIZE) {
+ xuio->xu_ext.xu_ps.xu_ps_set = B_TRUE;
+ xuio->xu_ext.xu_ps.xu_ps_size =
+ copied + msgdsize(mp);
+ }
+
if ((flags & (MSG_PEEK|MSG_TRUNC))) {
if (flags & MSG_PEEK) {
freemsg(mp);
diff --git a/usr/src/uts/common/fs/sockfs/sockfilter.c b/usr/src/uts/common/fs/sockfs/sockfilter.c
index 1fa4efe59f..62a079f419 100644
--- a/usr/src/uts/common/fs/sockfs/sockfilter.c
+++ b/usr/src/uts/common/fs/sockfs/sockfilter.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#include <sys/systm.h>
@@ -246,6 +247,18 @@ sof_setsockopt_impl(struct sonode *so, int option_name,
/* Module loaded OK, so there must be an ops vector */
ASSERT(ent->sofe_mod != NULL);
+
+ /*
+ * Check again to confirm ATTACH is ok. See if the the module
+ * is not SOF_ATT_SAFE after an unsafe operation has taken
+ * place.
+ */
+ if ((ent->sofe_mod->sofm_flags & SOF_ATT_SAFE) == 0 &&
+ so->so_state & SS_FILOP_UNSF) {
+ sof_instance_destroy(inst);
+ return (EINVAL);
+ }
+
inst->sofi_ops = &ent->sofe_mod->sofm_ops;
SOF_STAT_ADD(inst, tot_active_attach, 1);
@@ -1445,7 +1458,13 @@ sof_filter_ioctl(struct sonode *so, int cmd, intptr_t arg, int mode,
* sof_register(version, name, ops, flags)
*
* Register a socket filter identified by name `name' and which should use
- * the ops vector `ops' for event notification. `flags' should be set to 0.
+ * the ops vector `ops' for event notification. `flags' should be set to 0
+ * by default for "unsafe" modules or SOF_ATT_SAFE for "safe" modules. An
+ * unsafe filter is one that cannot be attached after any socket operation has
+ * occured. This is the legacy default. A "safe" filter can be attached even
+ * after some basic initial socket operations have taken place. This set is
+ * currently bind, getsockname, getsockopt and setsockopt. The order in which
+ * a "safe" filter can be attached is more relaxed, and thus more flexible.
* On success 0 is returned, otherwise an errno is returned.
*/
int
@@ -1453,14 +1472,13 @@ sof_register(int version, const char *name, const sof_ops_t *ops, int flags)
{
sof_module_t *mod;
- _NOTE(ARGUNUSED(flags));
-
if (version != SOF_VERSION)
return (EINVAL);
mod = kmem_zalloc(sizeof (sof_module_t), KM_SLEEP);
mod->sofm_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
(void) strcpy(mod->sofm_name, name);
+ mod->sofm_flags = flags;
mod->sofm_ops = *ops;
mutex_enter(&sof_module_lock);
diff --git a/usr/src/uts/common/fs/sockfs/sockfilter_impl.h b/usr/src/uts/common/fs/sockfs/sockfilter_impl.h
index e9a09bad88..e63831e172 100644
--- a/usr/src/uts/common/fs/sockfs/sockfilter_impl.h
+++ b/usr/src/uts/common/fs/sockfs/sockfilter_impl.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SOCKFS_SOCKFILTER_H
@@ -51,6 +52,7 @@ typedef struct sof_kstat sof_kstat_t;
struct sof_module {
char *sofm_name;
+ int sofm_flags;
sof_ops_t sofm_ops;
uint_t sofm_refcnt;
list_node_t sofm_node;
diff --git a/usr/src/uts/common/fs/sockfs/socksubr.c b/usr/src/uts/common/fs/sockfs/socksubr.c
index 3262150f79..739d439851 100644
--- a/usr/src/uts/common/fs/sockfs/socksubr.c
+++ b/usr/src/uts/common/fs/sockfs/socksubr.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
* Copyright 2015, Joyent, Inc. All rights reserved.
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
@@ -420,8 +421,10 @@ sogetoff(mblk_t *mp, t_uscalar_t offset,
*
* The underlying filesystem VSOCK vnode has a v_stream pointer that
* references the actual stream head (hence indirectly the actual sonode).
+ *
+ * This function is non-static so it can be used by brand emulation.
*/
-static int
+int
so_ux_lookup(struct sonode *so, struct sockaddr_un *soun, int checkaccess,
vnode_t **vpp)
{
diff --git a/usr/src/uts/common/fs/sockfs/socktpi_impl.h b/usr/src/uts/common/fs/sockfs/socktpi_impl.h
index 6a515be122..24acb81a0a 100644
--- a/usr/src/uts/common/fs/sockfs/socktpi_impl.h
+++ b/usr/src/uts/common/fs/sockfs/socktpi_impl.h
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SOCKFS_SOCKTPI_IMPL_H
@@ -56,6 +57,8 @@ extern int sogetrderr(vnode_t *, int, int *);
extern int sogetwrerr(vnode_t *, int, int *);
extern int so_addr_verify(struct sonode *, const struct sockaddr *,
socklen_t);
+extern int so_ux_lookup(struct sonode *, struct sockaddr_un *, int,
+ vnode_t **);
extern int so_ux_addr_xlate(struct sonode *, struct sockaddr *,
socklen_t, int, void **, socklen_t *);
extern void so_unix_close(struct sonode *);
diff --git a/usr/src/uts/common/fs/swapfs/swap_subr.c b/usr/src/uts/common/fs/swapfs/swap_subr.c
index 74c4302da9..a4d983665b 100644
--- a/usr/src/uts/common/fs/swapfs/swap_subr.c
+++ b/usr/src/uts/common/fs/swapfs/swap_subr.c
@@ -110,9 +110,11 @@ swapfs_recalc(pgcnt_t pgs)
* memory that can be used as swap space should do so by
* setting swapfs_desfree at boot time, not swapfs_minfree.
* However, swapfs_minfree is tunable by install as a
- * workaround for bugid 1147463.
+ * workaround for bugid 1147463. Note swapfs_minfree is set
+ * to 1/8th of memory, but clamped at the limit of 256 MB.
*/
- new_swapfs_minfree = MAX(btopr(2 * 1024 * 1024), pgs >> 3);
+ new_swapfs_minfree = MIN(MAX(btopr(2 * 1024 * 1024), pgs >> 3),
+ btopr(256 * 1024 * 1024));
}
/*
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_dir.c b/usr/src/uts/common/fs/tmpfs/tmp_dir.c
index 06ef8dd7fd..b28ced7111 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_dir.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_dir.c
@@ -21,10 +21,9 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
@@ -445,20 +444,7 @@ tdirenter(
/*
* Unmake the inode we just made.
*/
- rw_enter(&tp->tn_rwlock, RW_WRITER);
- if ((tp->tn_type) == VDIR) {
- ASSERT(tdp == NULL);
- /*
- * cleanup allocs made by tdirinit()
- */
- tdirtrunc(tp);
- }
- mutex_enter(&tp->tn_tlock);
- tp->tn_nlink = 0;
- mutex_exit(&tp->tn_tlock);
- gethrestime(&tp->tn_ctime);
- rw_exit(&tp->tn_rwlock);
- tmpnode_rele(tp);
+ tmpnode_cleanup(tp);
tp = NULL;
}
} else if (tpp) {
@@ -493,6 +479,7 @@ tdirdelete(
enum dr_op op,
struct cred *cred)
{
+ struct tmount *tm;
struct tdirent *tpdp;
int error;
size_t namelen;
@@ -578,7 +565,8 @@ tdirdelete(
*/
namelen = strlen(tpdp->td_name) + 1;
- tmp_memfree(tpdp, sizeof (struct tdirent) + namelen);
+ tm = TNTOTM(dir);
+ tmp_kmem_free(tm, tpdp, sizeof (struct tdirent) + namelen);
dir->tn_size -= (sizeof (struct tdirent) + namelen);
dir->tn_dirents--;
@@ -600,19 +588,27 @@ tdirdelete(
* tdirinit is used internally to initialize a directory (dir)
* with '.' and '..' entries without checking permissions and locking
*/
-void
+int
tdirinit(
struct tmpnode *parent, /* parent of directory to initialize */
struct tmpnode *dir) /* the new directory */
{
+ struct tmount *tm;
struct tdirent *dot, *dotdot;
timestruc_t now;
ASSERT(RW_WRITE_HELD(&parent->tn_rwlock));
ASSERT(dir->tn_type == VDIR);
- dot = tmp_memalloc(sizeof (struct tdirent) + 2, TMP_MUSTHAVE);
- dotdot = tmp_memalloc(sizeof (struct tdirent) + 3, TMP_MUSTHAVE);
+ tm = TNTOTM(parent);
+ dot = tmp_kmem_zalloc(tm, sizeof (struct tdirent) + 2, KM_SLEEP);
+ if (dot == NULL)
+ return (ENOSPC);
+ dotdot = tmp_kmem_zalloc(tm, sizeof (struct tdirent) + 3, KM_SLEEP);
+ if (dotdot == NULL) {
+ tmp_kmem_free(tm, dot, sizeof (struct tdirent) + 2);
+ return (ENOSPC);
+ }
/*
* Initialize the entries
@@ -663,6 +659,8 @@ tdirinit(
dir->tn_size = 2 * sizeof (struct tdirent) + 5; /* dot and dotdot */
dir->tn_dirents = 2;
dir->tn_nlink = 2;
+
+ return (0);
}
@@ -674,6 +672,7 @@ tdirtrunc(struct tmpnode *dir)
{
struct tdirent *tdp;
struct tmpnode *tp;
+ struct tmount *tm;
size_t namelen;
timestruc_t now;
int isvattrdir, isdotdot, skip_decr;
@@ -681,6 +680,8 @@ tdirtrunc(struct tmpnode *dir)
ASSERT(RW_WRITE_HELD(&dir->tn_rwlock));
ASSERT(dir->tn_type == VDIR);
+ tm = TNTOTM(dir);
+
isvattrdir = (dir->tn_vnode->v_flag & V_XATTRDIR) ? 1 : 0;
for (tdp = dir->tn_dir; tdp; tdp = dir->tn_dir) {
ASSERT(tdp->td_next != tdp);
@@ -712,7 +713,7 @@ tdirtrunc(struct tmpnode *dir)
tmpfs_hash_out(tdp);
- tmp_memfree(tdp, sizeof (struct tdirent) + namelen);
+ tmp_kmem_free(tm, tdp, sizeof (struct tdirent) + namelen);
dir->tn_size -= (sizeof (struct tdirent) + namelen);
dir->tn_dirents--;
}
@@ -965,6 +966,7 @@ tdiraddentry(
enum de_op op,
struct tmpnode *fromtp)
{
+ struct tmount *tm;
struct tdirent *tdp, *tpdp;
size_t namelen, alloc_size;
timestruc_t now;
@@ -985,9 +987,10 @@ tdiraddentry(
/*
* Allocate and initialize directory entry
*/
+ tm = TNTOTM(dir);
namelen = strlen(name) + 1;
alloc_size = namelen + sizeof (struct tdirent);
- tdp = tmp_memalloc(alloc_size, 0);
+ tdp = tmp_kmem_zalloc(tm, alloc_size, KM_NOSLEEP_LAZY);
if (tdp == NULL)
return (ENOSPC);
@@ -1087,7 +1090,10 @@ tdirmaketnode(
((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
return (EOVERFLOW);
type = va->va_type;
- tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE);
+ tp = tmp_kmem_zalloc(tm, sizeof (struct tmpnode), KM_SLEEP);
+ if (tp == NULL) {
+ return (ENOSPC);
+ }
tmpnode_init(tm, tp, va, cred);
/* setup normal file/dir's extended attribute directory */
@@ -1149,8 +1155,13 @@ tdirmaketnode(
if (va->va_mask & AT_MTIME)
tp->tn_mtime = va->va_mtime;
- if (op == DE_MKDIR)
- tdirinit(dir, tp);
+ if (op == DE_MKDIR) {
+ int ret;
+ if ((ret = tdirinit(dir, tp)) != 0) {
+ tmpnode_cleanup(tp);
+ return (ret);
+ }
+ }
*newnode = tp;
return (0);
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_subr.c b/usr/src/uts/common/fs/tmpfs/tmp_subr.c
index 8723631555..0c48c03a75 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_subr.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_subr.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -43,6 +43,7 @@
#include <sys/fs/tmpnode.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
+#include <vm/anon.h>
#define KILOBYTE 1024
#define MEGABYTE (1024 * KILOBYTE)
@@ -54,6 +55,80 @@
extern pgcnt_t swapfs_minfree;
+void *
+tmp_kmem_zalloc(struct tmount *tm, size_t size, int flag)
+{
+ void *buf;
+ zone_t *zone;
+ size_t pages;
+
+ mutex_enter(&tm->tm_contents);
+ zone = tm->tm_vfsp->vfs_zone;
+ if (tm->tm_anonmem + size > tm->tm_anonmax ||
+ tm->tm_anonmem + size < tm->tm_anonmem ||
+ size + ptob(tmpfs_minfree) <= size ||
+ !anon_checkspace(size + ptob(tmpfs_minfree), zone)) {
+ mutex_exit(&tm->tm_contents);
+ return (NULL);
+ }
+
+ /*
+ * Only make anonymous memory reservations when a page boundary is
+ * crossed. This is necessary since the anon_resv functions rounds up
+ * to PAGESIZE internally.
+ */
+ pages = btopr(tm->tm_allocmem + size);
+ pages -= btopr(tm->tm_allocmem);
+ if (pages > 0 && anon_try_resv_zone(ptob(pages), zone) == 0) {
+ mutex_exit(&tm->tm_contents);
+ return (NULL);
+ }
+
+ tm->tm_allocmem += size;
+ tm->tm_anonmem += size;
+ mutex_exit(&tm->tm_contents);
+
+ buf = kmem_zalloc(size, flag);
+ if (buf == NULL) {
+ mutex_enter(&tm->tm_contents);
+ ASSERT(tm->tm_anonmem > tm->tm_anonmem - size);
+ tm->tm_anonmem -= size;
+ if (pages > 0) {
+ /*
+ * Re-chasing the zone pointer is necessary since a
+ * forced umount could have been performed while the
+ * tm_contents lock was dropped during allocation.
+ */
+ anon_unresv_zone(ptob(pages), tm->tm_vfsp->vfs_zone);
+ }
+ mutex_exit(&tm->tm_contents);
+ }
+
+ return (buf);
+}
+
+void
+tmp_kmem_free(struct tmount *tm, void *buf, size_t size)
+{
+ size_t pages;
+
+ kmem_free(buf, size);
+ mutex_enter(&tm->tm_contents);
+ ASSERT(tm->tm_anonmem > tm->tm_anonmem - size);
+ tm->tm_anonmem -= size;
+ pages = btopr(tm->tm_allocmem);
+ tm->tm_allocmem -= size;
+ pages -= btopr(tm->tm_allocmem);
+ /*
+ * Like the tmp_kmem_zalloc case, only unreserve anonymous memory when
+ * a page boundary has been crossed.
+ */
+ if (pages > 0) {
+ anon_unresv_zone(size, tm->tm_vfsp->vfs_zone);
+ }
+ mutex_exit(&tm->tm_contents);
+}
+
int
tmp_taccess(void *vtp, int mode, struct cred *cred)
{
@@ -99,42 +174,8 @@ tmp_sticky_remove_access(struct tmpnode *dir, struct tmpnode *entry,
}
/*
- * Allocate zeroed memory if tmpfs_maxkmem has not been exceeded
- * or the 'musthave' flag is set. 'musthave' allocations should
- * always be subordinate to normal allocations so that tmpfs_maxkmem
- * can't be exceeded by more than a few KB. Example: when creating
- * a new directory, the tmpnode is a normal allocation; if that
- * succeeds, the dirents for "." and ".." are 'musthave' allocations.
- */
-void *
-tmp_memalloc(size_t size, int musthave)
-{
- static time_t last_warning;
- time_t now;
-
- if (atomic_add_long_nv(&tmp_kmemspace, size) < tmpfs_maxkmem ||
- musthave)
- return (kmem_zalloc(size, KM_SLEEP));
-
- atomic_add_long(&tmp_kmemspace, -size);
- now = gethrestime_sec();
- if (last_warning != now) {
- last_warning = now;
- cmn_err(CE_WARN, "tmp_memalloc: tmpfs over memory limit");
- }
- return (NULL);
-}
-
-void
-tmp_memfree(void *cp, size_t size)
-{
- kmem_free(cp, size);
- atomic_add_long(&tmp_kmemspace, -size);
-}
-
-/*
- * Convert a string containing a number (number of bytes) to a pgcnt_t,
- * containing the corresponding number of pages. On 32-bit kernels, the
+ * Convert a string containing a number (number of bytes) to a size_t,
+ * containing the corresponding number of bytes. On 32-bit kernels, the
* maximum value encoded in 'str' is PAGESIZE * ULONG_MAX, while the value
* returned in 'maxpg' is at most ULONG_MAX.
*
@@ -152,7 +193,7 @@ tmp_memfree(void *cp, size_t size)
* error.
*/
int
-tmp_convnum(char *str, pgcnt_t *maxpg)
+tmp_convnum(char *str, size_t *maxbytes)
{
u_longlong_t num = 0;
#ifdef _LP64
@@ -160,6 +201,7 @@ tmp_convnum(char *str, pgcnt_t *maxpg)
#else
u_longlong_t max_bytes = PAGESIZE * (uint64_t)ULONG_MAX;
#endif
+ size_t pages;
char *c;
const struct convchar {
char *cc_char;
@@ -250,13 +292,21 @@ valid_char:
done:
/*
- * Since btopr() rounds up to page granularity, this round-up can
- * cause an overflow only if 'num' is between (max_bytes - PAGESIZE)
- * and (max_bytes). In this case the resulting number is zero, which
- * is what we check for below.
+ * We've been given a size in bytes; however, we want to make sure that
+ * we have at least one page worth no matter what. Therefore we use
+ * btopr to round up. However, this may cause an overflow only if 'num'
+ * is between (max_bytes - PAGESIZE) and (max_bytes). In this case the
+ * resulting number is zero, which is what we check for below. Note, we
+ * require at least one page, so if pages is zero, well, it wasn't going
+ * to work anyways.
*/
- if ((*maxpg = (pgcnt_t)btopr(num)) == 0 && num != 0)
+ pages = btopr(num);
+ if (pages == 0) {
return (EINVAL);
+ }
+
+ *maxbytes = ptob(pages);
+
return (0);
}
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_tnode.c b/usr/src/uts/common/fs/tmpfs/tmp_tnode.c
index 51e57b2611..13ea356924 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_tnode.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_tnode.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -64,21 +65,35 @@ tmp_resv(
int pagecreate) /* call anon_resv if set */
{
pgcnt_t pages = btopr(delta);
+ size_t pbytes = ptob(pages);
zone_t *zone;
ASSERT(RW_WRITE_HELD(&tp->tn_rwlock));
ASSERT(tp->tn_type == VREG);
+
/*
- * pagecreate is set only if we actually need to call anon_resv
- * to reserve an additional page of anonymous memory.
- * Since anon_resv always reserves a page at a time,
- * it should only get called when we know we're growing the
- * file into a new page or filling a hole.
+ * pagecreate is set only if we actually need to call anon_resv to
+ * reserve an additional page of anonymous memory. Since anon_resv
+ * always reserves a page at a time, it should only get called when we
+ * know we're growing the file into a new page or filling a hole. This
+ * is why we transform delta into a number of pages. However, because we
+ * track bytes and not pages, we convert that back to a number of bytes
+ * that we allocate against.
*
- * Deny if trying to reserve more than tmpfs can allocate
+ * Deny if trying to reserve more than tmpfs can allocate, the
+ * allocation causes an overflow, or the delta round up overflowed.
+ * Note, that btopr rounds up, so we need to catch the unsigned
+ * overflow. Note, rounding up when we are within a page of SIZE_MAX is
+ * done by adding a page, overflowing, which will then be rounded back
+ * to zero. Hence the following check.
*/
+ if (pages == 0 && delta != 0)
+ return (1);
+
zone = tm->tm_vfsp->vfs_zone;
- if (pagecreate && ((tm->tm_anonmem + pages > tm->tm_anonmax) ||
+ if (pagecreate && ((tm->tm_anonmem + pbytes > tm->tm_anonmax) ||
+ (tm->tm_anonmem + pbytes < tm->tm_anonmem) ||
+ (ptob(pages + tmpfs_minfree) <= pbytes) ||
(!anon_checkspace(ptob(pages + tmpfs_minfree), zone)) ||
(anon_try_resv_zone(delta, zone) == 0))) {
return (1);
@@ -89,7 +104,7 @@ tmp_resv(
*/
if (pagecreate) {
mutex_enter(&tm->tm_contents);
- tm->tm_anonmem += pages;
+ tm->tm_anonmem += pbytes;
mutex_exit(&tm->tm_contents);
TRACE_2(TR_FAC_VM, TR_ANON_TMPFS, "anon tmpfs:%p %lu",
@@ -110,13 +125,27 @@ tmp_unresv(
struct tmpnode *tp,
size_t delta)
{
+ size_t pages, pbytes;
+
ASSERT(RW_WRITE_HELD(&tp->tn_rwlock));
ASSERT(tp->tn_type == VREG);
+ /*
+ * If this is true, we have a grevious overflow bug and some size
+ * accounting has been messed with as having an amount to truncate at
+ * this size would imply that all of memory was used for this file. No
+ * matter how small the kernel, it will always need at least one page.
+ */
+ pages = btopr(delta);
+ if (pages == 0 && delta != 0)
+ panic("tmpfs unsigned overflow detected");
+ pbytes = ptob(pages);
+
anon_unresv_zone(delta, tm->tm_vfsp->vfs_zone);
mutex_enter(&tm->tm_contents);
- tm->tm_anonmem -= btopr(delta);
+ ASSERT(tm->tm_anonmem > tm->tm_anonmem - pbytes);
+ tm->tm_anonmem -= pbytes;
mutex_exit(&tm->tm_contents);
TRACE_2(TR_FAC_VM, TR_ANON_TMPFS, "anon tmpfs:%p %lu", tp, delta);
@@ -154,6 +183,26 @@ tmpnode_growmap(struct tmpnode *tp, ulong_t newsize)
}
/*
+ * This is used to clean up a tmpnode that hasn't made it out the door. In other
+ * words, we allocated it and did a tmpnode_init; however, before it could get
+ * fully inserted into a directory, bad things happened and it failed.
+ */
+void
+tmpnode_cleanup(struct tmpnode *tp)
+{
+ rw_enter(&tp->tn_rwlock, RW_WRITER);
+ if ((tp->tn_type) == VDIR) {
+ tdirtrunc(tp);
+ }
+ mutex_enter(&tp->tn_tlock);
+ tp->tn_nlink = 0;
+ mutex_exit(&tp->tn_tlock);
+ gethrestime(&tp->tn_ctime);
+ rw_exit(&tp->tn_rwlock);
+ tmpnode_rele(tp);
+}
+
+/*
* Initialize a tmpnode and add it to file list under mount point.
*/
void
@@ -232,7 +281,6 @@ tmpnode_trunc(
{
size_t oldsize = tp->tn_size;
size_t delta;
- struct vnode *vp = TNTOV(tp);
timestruc_t now;
int error = 0;
@@ -316,7 +364,7 @@ tmpnode_trunc(
/* Delete anon array for tmpnode */
ASSERT(tp->tn_nblocks == 0);
ASSERT(anon_get_ptr(tp->tn_anon, 0) == NULL);
- ASSERT(!vn_has_cached_data(vp));
+ ASSERT(!vn_has_cached_data(TNTOV(tp)));
anon_release(tp->tn_anon, tp->tn_asize);
tp->tn_anon = NULL;
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c b/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c
index a7cf62cb99..24310fefe5 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -56,6 +56,15 @@
static int tmpfsfstype;
/*
+ * tmpfs_mountcount is used to prevent module unloads while there is still
+ * state from a former mount hanging around. With forced umount support, the
+ * filesystem module must not be allowed to go away before the last
+ * VFS_FREEVFS() call has been made. Since this is just an atomic counter,
+ * there's no need for locking.
+ */
+static uint32_t tmpfs_mountcount;
+
+/*
* tmpfs vfs operations.
*/
static int tmpfsinit(int, char *);
@@ -65,6 +74,7 @@ static int tmp_unmount(struct vfs *, int, struct cred *);
static int tmp_root(struct vfs *, struct vnode **);
static int tmp_statvfs(struct vfs *, struct statvfs64 *);
static int tmp_vget(struct vfs *, struct vnode **, struct fid *);
+static void tmp_freevfs(vfs_t *vfsp);
/*
* Loadable module wrapper
@@ -123,6 +133,14 @@ _fini()
{
int error;
+ /*
+ * If a forceably unmounted instance is still hanging around, we cannot
+ * allow the module to be unloaded because that would cause panics once
+ * the VFS framework decides it's time to call into VFS_FREEVFS().
+ */
+ if (tmpfs_mountcount)
+ return (EBUSY);
+
error = mod_remove(&modlinkage);
if (error)
return (error);
@@ -141,14 +159,6 @@ _info(struct modinfo *modinfop)
}
/*
- * The following are patchable variables limiting the amount of system
- * resources tmpfs can use.
- *
- * tmpfs_maxkmem limits the amount of kernel kmem_alloc memory
- * tmpfs can use for it's data structures (e.g. tmpnodes, directory entries)
- * It is not determined by setting a hard limit but rather as a percentage of
- * physical memory which is determined when tmpfs is first used in the system.
- *
* tmpfs_minfree is the minimum amount of swap space that tmpfs leaves for
* the rest of the system. In other words, if the amount of free swap space
* in the system (i.e. anoninfo.ani_free) drops below tmpfs_minfree, tmpfs
@@ -157,9 +167,7 @@ _info(struct modinfo *modinfop)
* There is also a per mount limit on the amount of swap space
* (tmount.tm_anonmax) settable via a mount option.
*/
-size_t tmpfs_maxkmem = 0;
size_t tmpfs_minfree = 0;
-size_t tmp_kmemspace; /* bytes of kernel heap used by all tmpfs */
static major_t tmpfs_major;
static minor_t tmpfs_minor;
@@ -178,6 +186,7 @@ tmpfsinit(int fstype, char *name)
VFSNAME_ROOT, { .vfs_root = tmp_root },
VFSNAME_STATVFS, { .vfs_statvfs = tmp_statvfs },
VFSNAME_VGET, { .vfs_vget = tmp_vget },
+ VFSNAME_FREEVFS, { .vfs_freevfs = tmp_freevfs },
NULL, NULL
};
int error;
@@ -212,18 +221,12 @@ tmpfsinit(int fstype, char *name)
tmpfs_minfree = btopr(TMPMINFREE);
}
- /*
- * The maximum amount of space tmpfs can allocate is
- * TMPMAXPROCKMEM percent of kernel memory
- */
- if (tmpfs_maxkmem == 0)
- tmpfs_maxkmem = MAX(PAGESIZE, kmem_maxavail() / TMPMAXFRACKMEM);
-
if ((tmpfs_major = getudev()) == (major_t)-1) {
cmn_err(CE_WARN, "tmpfsinit: Can't get unique device number.");
tmpfs_major = 0;
}
mutex_init(&tmpfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
+ tmpfs_mountcount = 0;
return (0);
}
@@ -234,7 +237,7 @@ tmp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
struct tmpnode *tp;
struct pathname dpn;
int error;
- pgcnt_t anonmax;
+ size_t anonmax;
struct vattr rattr;
int got_attrs;
boolean_t mode_arg = B_FALSE;
@@ -278,7 +281,18 @@ tmp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
if ((error = tmp_convnum(argstr, &anonmax)) != 0)
goto out;
} else {
- anonmax = ULONG_MAX;
+ anonmax = SIZE_MAX;
+ }
+
+ /*
+ * The "mode" mount argument allows the operator to override the
+ * permissions of the root of the tmpfs mount.
+ */
+ if (vfs_optionisset(vfsp, "mode", &argstr)) {
+ if ((error = tmp_convmode(argstr, &root_mode)) != 0) {
+ goto out;
+ }
+ mode_arg = B_TRUE;
}
/*
@@ -311,7 +325,8 @@ tmp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
goto out;
}
- if ((tm = tmp_memalloc(sizeof (struct tmount), 0)) == NULL) {
+ if ((tm = kmem_zalloc(sizeof (struct tmount), KM_NOSLEEP_LAZY)) ==
+ NULL) {
pn_free(&dpn);
error = ENOMEM;
goto out;
@@ -343,17 +358,37 @@ tmp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
vfsp->vfs_bsize = PAGESIZE;
vfsp->vfs_flag |= VFS_NOTRUNC;
vfs_make_fsid(&vfsp->vfs_fsid, tm->tm_dev, tmpfsfstype);
- tm->tm_mntpath = tmp_memalloc(dpn.pn_pathlen + 1, TMP_MUSTHAVE);
+ tm->tm_mntpath = kmem_zalloc(dpn.pn_pathlen + 1, KM_SLEEP);
(void) strcpy(tm->tm_mntpath, dpn.pn_path);
/*
+ * Preemptively set vfs_zone before any of the tmp_kmem_* functions are
+ * called. That field is not populated until after a successful
+ * VFS_MOUNT when domount() sets vfsp metadata via vfs_add(). An
+ * accurate value is required for proper swap usage accounting.
+ */
+ ASSERT0(uap->flags & MS_REMOUNT);
+ ASSERT(vfsp->vfs_zone == NULL);
+ vfsp->vfs_zone = curproc->p_zone;
+
+ /*
* allocate and initialize root tmpnode structure
*/
bzero(&rattr, sizeof (struct vattr));
rattr.va_mode = (mode_t)(S_IFDIR | root_mode);
rattr.va_type = VDIR;
rattr.va_rdev = 0;
- tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE);
+ tp = tmp_kmem_zalloc(tm, sizeof (struct tmpnode), KM_SLEEP);
+ if (tp == NULL) {
+ kmem_free(tm->tm_mntpath, strlen(tm->tm_mntpath) + 1);
+ mutex_destroy(&tm->tm_contents);
+ mutex_destroy(&tm->tm_renamelck);
+ kmem_free(tm, sizeof (struct tmount));
+
+ pn_free(&dpn);
+ error = ENOMEM;
+ goto out;
+ }
tmpnode_init(tm, tp, &rattr, cr);
/*
@@ -392,12 +427,34 @@ tmp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
tp->tn_nlink = 0;
tm->tm_rootnode = tp;
- tdirinit(tp, tp);
+ if (tdirinit(tp, tp) != 0) {
+ /*
+ * While we would normally let our VOP_INACTIVE function take
+ * care of cleaning up here, we're in a bit of a delicate
+ * situation, so we do so manually. While it's tempting to try
+ * and rely upon tmpfs_freevfs() and others, it's probably safer
+ * for the time to do this manually at the cost of duplication.
+ */
+ vn_invalid(TNTOV(tp));
+ rw_destroy(&tp->tn_rwlock);
+ mutex_destroy(&tp->tn_tlock);
+ vn_free(TNTOV(tp));
+ tmp_kmem_free(tm, tp, sizeof (struct tmpnode));
+
+ kmem_free(tm->tm_mntpath, strlen(tm->tm_mntpath) + 1);
+ mutex_destroy(&tm->tm_contents);
+ mutex_destroy(&tm->tm_renamelck);
+ kmem_free(tm, sizeof (struct tmount));
+ pn_free(&dpn);
+ error = ENOMEM;
+ goto out;
+ }
rw_exit(&tp->tn_rwlock);
pn_free(&dpn);
error = 0;
+ atomic_inc_32(&tmpfs_mountcount);
out:
if (error == 0)
@@ -413,36 +470,107 @@ tmp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
struct tmpnode *tnp, *cancel;
struct vnode *vp;
int error;
+ uint_t cnt;
+ int i;
if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
return (error);
- /*
- * forced unmount is not supported by this file system
- * and thus, ENOTSUP, is being returned.
- */
- if (flag & MS_FORCE)
- return (ENOTSUP);
-
mutex_enter(&tm->tm_contents);
/*
- * If there are no open files, only the root node should have
- * a reference count.
+ * In the normal unmount case (non-forced unmount), if there are no
+ * open files, only the root node should have a reference count.
+ *
* With tm_contents held, nothing can be added or removed.
* There may be some dirty pages. To prevent fsflush from
* disrupting the unmount, put a hold on each node while scanning.
* If we find a previously referenced node, undo the holds we have
* placed and fail EBUSY.
+ *
+ * However, in the case of a forced umount, things are a bit different.
+ * An additional VFS_HOLD is added for each outstanding VN_HOLD to
+ * ensure that the file system is not cleaned up (tmp_freevfs) until
+ * the last vfs hold is dropped. This happens in tmp_inactive as the
+ * vnodes are released. Also, we can't add an additional VN_HOLD in
+ * this case since that would prevent tmp_inactive from ever being
+ * called. Finally, we do need to drop the zone ref now (zone_rele_ref)
+ * so that the zone is not blocked waiting for the final file system
+ * cleanup.
*/
tnp = tm->tm_rootnode;
- if (TNTOV(tnp)->v_count > 1) {
+
+ vp = TNTOV(tnp);
+ mutex_enter(&vp->v_lock);
+ cnt = vp->v_count;
+ if (flag & MS_FORCE) {
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+ /* Extra hold which we rele below when we drop the zone ref */
+ VFS_HOLD(vfsp);
+
+ for (i = 1; i < cnt; i++)
+ VFS_HOLD(vfsp);
+
+ /* drop the mutex now because no one can find this mount */
+ mutex_exit(&tm->tm_contents);
+ } else if (cnt > 1) {
+ mutex_exit(&vp->v_lock);
mutex_exit(&tm->tm_contents);
return (EBUSY);
}
+ mutex_exit(&vp->v_lock);
+ /*
+ * Check for open files. An open file causes everything to unwind
+ * unless this is a forced umount.
+ */
for (tnp = tnp->tn_forw; tnp; tnp = tnp->tn_forw) {
- if ((vp = TNTOV(tnp))->v_count > 0) {
+ vp = TNTOV(tnp);
+ mutex_enter(&vp->v_lock);
+ cnt = vp->v_count;
+ if (flag & MS_FORCE) {
+ for (i = 0; i < cnt; i++)
+ VFS_HOLD(vfsp);
+
+ /*
+ * In the case of a forced umount don't add an
+ * additional VN_HOLD on the already held vnodes, like
+ * we do in the non-forced unmount case. If the
+ * cnt > 0, then the vnode already has at least one
+ * hold and we need tmp_inactive to get called when the
+ * last pre-existing hold on the node is released so
+ * that we can VFS_RELE the VFS holds we just added.
+ */
+ if (cnt == 0) {
+ /* directly add VN_HOLD since have the lock */
+ vp->v_count++;
+ }
+
+ mutex_exit(&vp->v_lock);
+
+ /*
+ * If the tmpnode has any pages associated with it
+ * (i.e. if it's a normal file with non-zero size), the
+ * tmpnode could still be discovered by pageout or
+ * fsflush via the page vnode pointers. To prevent this
+ * from interfering with the tmp_freevfs, truncate the
+ * tmpnode now.
+ */
+ if (tnp->tn_size != 0 && tnp->tn_type == VREG) {
+ rw_enter(&tnp->tn_rwlock, RW_WRITER);
+ rw_enter(&tnp->tn_contents, RW_WRITER);
+
+ (void) tmpnode_trunc(tm, tnp, 0);
+
+ rw_exit(&tnp->tn_contents);
+ rw_exit(&tnp->tn_rwlock);
+
+ ASSERT(tnp->tn_size == 0);
+ ASSERT(tnp->tn_nblocks == 0);
+ }
+ } else if (cnt > 0) {
+ /* An open file; unwind the holds we've been adding. */
+ mutex_exit(&vp->v_lock);
cancel = tm->tm_rootnode->tn_forw;
while (cancel != tnp) {
vp = TNTOV(cancel);
@@ -452,14 +580,50 @@ tmp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
}
mutex_exit(&tm->tm_contents);
return (EBUSY);
+ } else {
+ /* directly add a VN_HOLD since we have the lock */
+ vp->v_count++;
+ mutex_exit(&vp->v_lock);
}
- VN_HOLD(vp);
}
- /*
- * We can drop the mutex now because no one can find this mount
- */
- mutex_exit(&tm->tm_contents);
+ if (flag & MS_FORCE) {
+ /*
+ * Drop the zone ref now since we don't know how long it will
+ * be until the final vfs_rele is called by tmp_inactive.
+ */
+ if (vfsp->vfs_zone) {
+ zone_rele_ref(&vfsp->vfs_implp->vi_zone_ref,
+ ZONE_REF_VFS);
+ vfsp->vfs_zone = 0;
+ }
+ /* We can now drop the extra hold we added above. */
+ VFS_RELE(vfsp);
+ } else {
+ /*
+ * For the non-forced case, we can drop the mutex now because
+ * no one can find this mount anymore
+ */
+ vfsp->vfs_flag |= VFS_UNMOUNTED;
+ mutex_exit(&tm->tm_contents);
+ }
+
+ return (0);
+}
+
+/*
+ * Implementation of VFS_FREEVFS() to support forced umounts. This is called by
+ * the vfs framework after umount and the last VFS_RELE, to trigger the release
+ * of any resources still associated with the given vfs_t. We only add
+ * additional VFS_HOLDs during the forced umount case, so this is normally
+ * called immediately after tmp_umount.
+ */
+void
+tmp_freevfs(vfs_t *vfsp)
+{
+ struct tmount *tm = (struct tmount *)VFSTOTM(vfsp);
+ struct tmpnode *tnp;
+ struct vnode *vp;
/*
* Free all kmemalloc'd and anonalloc'd memory associated with
@@ -469,6 +633,16 @@ tmp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
* tmpnode_free which assumes that the directory entry has been
* removed before the file.
*/
+
+ /*
+ * Now that we are tearing ourselves down we need to remove the
+ * UNMOUNTED flag. If we don't, we'll later hit a VN_RELE when we remove
+ * files from the system causing us to have a negative value. Doing this
+ * seems a bit better than trying to set a flag on the tmount that says
+ * we're tearing down.
+ */
+ vfsp->vfs_flag &= ~VFS_UNMOUNTED;
+
/*
* Remove all directory entries
*/
@@ -535,15 +709,16 @@ tmp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
ASSERT(tm->tm_mntpath);
- tmp_memfree(tm->tm_mntpath, strlen(tm->tm_mntpath) + 1);
+ kmem_free(tm->tm_mntpath, strlen(tm->tm_mntpath) + 1);
ASSERT(tm->tm_anonmem == 0);
mutex_destroy(&tm->tm_contents);
mutex_destroy(&tm->tm_renamelck);
- tmp_memfree(tm, sizeof (struct tmount));
+ kmem_free(tm, sizeof (struct tmount));
- return (0);
+ /* Allow _fini() to succeed now */
+ atomic_dec_32(&tmpfs_mountcount);
}
/*
@@ -605,18 +780,19 @@ tmp_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
* If tm_anonmax for this mount is less than the available swap space
* (minus the amount tmpfs can't use), use that instead
*/
- if (blocks > tmpfs_minfree)
+ if (blocks > tmpfs_minfree && tm->tm_anonmax > tm->tm_anonmem) {
sbp->f_bfree = MIN(blocks - tmpfs_minfree,
- tm->tm_anonmax - tm->tm_anonmem);
- else
+ btop(tm->tm_anonmax) - btopr(tm->tm_anonmem));
+ } else {
sbp->f_bfree = 0;
+ }
sbp->f_bavail = sbp->f_bfree;
/*
* Total number of blocks is what's available plus what's been used
*/
- sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree + tm->tm_anonmem);
+ sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree + btopr(tm->tm_anonmem));
if (eff_zid != GLOBAL_ZONEUNIQID &&
zp->zone_max_swap_ctl != UINT64_MAX) {
@@ -646,13 +822,7 @@ tmp_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
* available to tmpfs. This is fairly inaccurate since it doesn't
* take into account the names stored in the directory entries.
*/
- if (tmpfs_maxkmem > tmp_kmemspace)
- sbp->f_ffree = (tmpfs_maxkmem - tmp_kmemspace) /
- (sizeof (struct tmpnode) + sizeof (struct tdirent));
- else
- sbp->f_ffree = 0;
-
- sbp->f_files = tmpfs_maxkmem /
+ sbp->f_ffree = sbp->f_files = ptob(availrmem) /
(sizeof (struct tmpnode) + sizeof (struct tdirent));
sbp->f_favail = (fsfilcnt64_t)(sbp->f_ffree);
(void) cmpldev(&d32, vfsp->vfs_dev);
diff --git a/usr/src/uts/common/fs/tmpfs/tmp_vnops.c b/usr/src/uts/common/fs/tmpfs/tmp_vnops.c
index a09f206d88..cbe19aefea 100644
--- a/usr/src/uts/common/fs/tmpfs/tmp_vnops.c
+++ b/usr/src/uts/common/fs/tmpfs/tmp_vnops.c
@@ -25,7 +25,7 @@
*/
/*
- * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2016 RackTop Systems.
* Copyright (c) 2017 by Delphix. All rights reserved.
@@ -586,6 +586,10 @@ tmp_read(struct vnode *vp, struct uio *uiop, int ioflag, cred_t *cred,
struct tmount *tm = (struct tmount *)VTOTM(vp);
int error;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
/*
* We don't currently support reading non-regular files
*/
@@ -615,6 +619,10 @@ tmp_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cred,
struct tmount *tm = (struct tmount *)VTOTM(vp);
int error;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
/*
* We don't currently support writing to non-regular files
*/
@@ -788,8 +796,13 @@ tmp_setattr(
rw_exit(&tp->tn_contents);
rw_exit(&tp->tn_rwlock);
- if (error == 0 && vap->va_size == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (vap->va_size == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
goto out1;
}
@@ -835,6 +848,9 @@ tmp_lookup(
struct tmpnode *ntp = NULL;
int error;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (dvp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
/* allow cd into @ dir */
if (flags & LOOKUP_XATTR) {
@@ -853,6 +869,8 @@ tmp_lookup(
rw_enter(&tp->tn_rwlock, RW_WRITER);
if (tp->tn_xattrdp == NULL) {
+ int err;
+
if (!(flags & CREATE_XATTR_DIR)) {
rw_exit(&tp->tn_rwlock);
return (ENOENT);
@@ -873,9 +891,13 @@ tmp_lookup(
return (error);
}
- xdp = tmp_memalloc(sizeof (struct tmpnode),
- TMP_MUSTHAVE);
tm = VTOTM(dvp);
+ xdp = tmp_kmem_zalloc(tm, sizeof (struct tmpnode),
+ KM_SLEEP);
+ if (xdp == NULL) {
+ rw_exit(&tp->tn_rwlock);
+ return (ENOSPC);
+ }
tmpnode_init(tm, xdp, &tp->tn_attr, NULL);
/*
* Fix-up fields unique to attribute directories.
@@ -893,7 +915,16 @@ tmp_lookup(
}
xdp->tn_vnode->v_type = VDIR;
xdp->tn_vnode->v_flag |= V_XATTRDIR;
- tdirinit(tp, xdp);
+ if ((err = tdirinit(tp, xdp)) != 0) {
+ rw_exit(&tp->tn_rwlock);
+ /*
+ * This never got properly initialized so we can
+ * just clean it up.
+ */
+ xdp->tn_vnode->v_flag &= V_XATTRDIR;
+ tmpnode_cleanup(tp);
+ return (err);
+ }
tp->tn_xattrdp = xdp;
} else {
VN_HOLD(tp->tn_xattrdp->tn_vnode);
@@ -1302,10 +1333,8 @@ tmp_rename(
vnevent_rename_src(TNTOV(fromtp), odvp, onm, ct);
/*
* vnevent_rename_dest is called in tdirenter().
- * Notify the target dir if not same as source dir.
*/
- if (ndvp != odvp)
- vnevent_rename_dest_dir(ndvp, ct);
+ vnevent_rename_dest_dir(ndvp, TNTOV(fromtp), nnm, ct);
}
done:
@@ -1474,6 +1503,10 @@ tmp_readdir(
int reclen;
caddr_t outbuf;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
if (uiop->uio_loffset >= MAXOFF_T) {
if (eofp)
*eofp = 1;
@@ -1607,12 +1640,12 @@ tmp_symlink(
rw_exit(&parent->tn_rwlock);
if (error) {
- if (self)
+ if (self != NULL)
tmpnode_rele(self);
return (error);
}
len = strlen(tnm) + 1;
- cp = tmp_memalloc(len, 0);
+ cp = tmp_kmem_zalloc(tm, len, KM_NOSLEEP_LAZY);
if (cp == NULL) {
tmpnode_rele(self);
return (ENOSPC);
@@ -1677,10 +1710,27 @@ top:
* there's little to do -- just drop our hold.
*/
if (vp->v_count > 1 || tp->tn_nlink != 0) {
- VN_RELE_LOCKED(vp);
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED) {
+ /*
+ * Since the file system was forcibly unmounted, we can
+ * have a case (v_count == 1, tn_nlink != 0) where this
+ * file was open so we didn't add an extra hold on the
+ * file in tmp_unmount. We are counting on the
+ * interaction of the hold made in tmp_unmount and
+ * rele-ed in tmp_vfsfree so we need to be sure we
+ * don't decrement in this case.
+ */
+ if (vp->v_count > 1)
+ VN_RELE_LOCKED(vp);
+ } else {
+ VN_RELE_LOCKED(vp);
+ }
mutex_exit(&vp->v_lock);
mutex_exit(&tp->tn_tlock);
rw_exit(&tp->tn_rwlock);
+ /* If the filesystem was umounted by force, rele the vfs ref */
+ if (tm->tm_vfsp->vfs_flag & VFS_UNMOUNTED)
+ VFS_RELE(tm->tm_vfsp);
return;
}
@@ -1705,7 +1755,7 @@ top:
goto top;
}
if (tp->tn_type == VLNK)
- tmp_memfree(tp->tn_symlink, tp->tn_size + 1);
+ tmp_kmem_free(tm, tp->tn_symlink, tp->tn_size + 1);
}
/*
@@ -1739,7 +1789,11 @@ top:
rw_destroy(&tp->tn_rwlock);
mutex_destroy(&tp->tn_tlock);
vn_free(TNTOV(tp));
- tmp_memfree(tp, sizeof (struct tmpnode));
+ tmp_kmem_free(tm, tp, sizeof (struct tmpnode));
+
+ /* If the filesystem was umounted by force, rele the vfs ref */
+ if (tm->tm_vfsp->vfs_flag & VFS_UNMOUNTED)
+ VFS_RELE(tm->tm_vfsp);
}
/* ARGSUSED2 */
@@ -1861,6 +1915,10 @@ tmp_getapage(
struct vnode *pvp;
u_offset_t poff;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
if (protp != NULL)
*protp = PROT_ALL;
again:
@@ -2082,6 +2140,10 @@ tmp_putapage(
u_offset_t offset;
u_offset_t tmpoff;
+ /* If the filesystem was umounted by force, return immediately. */
+ if (vp->v_vfsp->vfs_flag & VFS_UNMOUNTED)
+ return (EIO);
+
ASSERT(PAGE_LOCKED(pp));
/* Kluster in tmp_klustsize chunks */
@@ -2342,8 +2404,13 @@ tmp_space(
return (EFBIG);
error = tmp_freesp(vp, bfp, flag);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
}
return (error);
}
diff --git a/usr/src/uts/common/fs/udfs/udf_dir.c b/usr/src/uts/common/fs/udfs/udf_dir.c
index c1e2c74a87..def046a0bf 100644
--- a/usr/src/uts/common/fs/udfs/udf_dir.c
+++ b/usr/src/uts/common/fs/udfs/udf_dir.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
*/
#include <sys/types.h>
@@ -562,9 +563,8 @@ out:
namep, ctp);
}
- if (sdp != tdp) {
- vnevent_rename_dest_dir(ITOV(tdp), ctp);
- }
+ vnevent_rename_dest_dir(ITOV(tdp), ITOV(tip),
+ namep, ctp);
}
/*
diff --git a/usr/src/uts/common/fs/udfs/udf_vnops.c b/usr/src/uts/common/fs/udfs/udf_vnops.c
index 054056c63a..51ce9b28af 100644
--- a/usr/src/uts/common/fs/udfs/udf_vnops.c
+++ b/usr/src/uts/common/fs/udfs/udf_vnops.c
@@ -569,8 +569,11 @@ udf_setattr(
goto update_inode;
}
- if (vap->va_size == 0)
+ if (vap->va_size == 0) {
vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
}
/*
* Change file access or modified times.
@@ -1649,8 +1652,13 @@ udf_space(
} else if ((error = convoff(vp, bfp, 0, offset)) == 0) {
error = ud_freesp(vp, bfp, flag, cr);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
}
return (error);
diff --git a/usr/src/uts/common/fs/ufs/ufs_vnops.c b/usr/src/uts/common/fs/ufs/ufs_vnops.c
index 2be623f755..8aa961e340 100644
--- a/usr/src/uts/common/fs/ufs/ufs_vnops.c
+++ b/usr/src/uts/common/fs/ufs/ufs_vnops.c
@@ -2084,8 +2084,13 @@ again:
goto update_inode;
}
- if (error == 0 && vap->va_size)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (vap->va_size) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
}
if (ulp) {
@@ -3610,12 +3615,7 @@ retry_firstlock:
if (error == 0) {
vnevent_rename_src(ITOV(sip), sdvp, snm, ct);
- /*
- * Notify the target directory of the rename event
- * if source and target directories are not the same.
- */
- if (sdvp != tdvp)
- vnevent_rename_dest_dir(tdvp, ct);
+ vnevent_rename_dest_dir(tdvp, ITOV(sip), tnm, ct);
}
errout:
@@ -4350,8 +4350,13 @@ ufs_space(struct vnode *vp, int cmd, struct flock64 *bfp, int flag,
return (error);
error = ufs_freesp(vp, bfp, flag, cr);
- if (error == 0 && bfp->l_start == 0)
- vnevent_truncate(vp, ct);
+ if (error == 0) {
+ if (bfp->l_start == 0) {
+ vnevent_truncate(vp, ct);
+ } else {
+ vnevent_resize(vp, ct);
+ }
+ }
} else if (cmd == F_ALLOCSP) {
error = ufs_lockfs_begin(ufsvfsp, &ulp,
ULOCKFS_FALLOCATE_MASK);
diff --git a/usr/src/uts/common/fs/vfs.c b/usr/src/uts/common/fs/vfs.c
index 3cd2feebef..460d15bcbd 100644
--- a/usr/src/uts/common/fs/vfs.c
+++ b/usr/src/uts/common/fs/vfs.c
@@ -857,9 +857,11 @@ vfs_mountroot(void)
for (p = practive; p != NULL; p = p->p_next) {
ASSERT(p == &p0 || p->p_parent == &p0);
+ mutex_enter(&p->p_lock);
PTOU(p)->u_cdir = rootdir;
VN_HOLD(PTOU(p)->u_cdir);
PTOU(p)->u_rdir = NULL;
+ mutex_exit(&p->p_lock);
}
mutex_exit(&pidlock);
@@ -3883,6 +3885,8 @@ vfs_to_modname(const char *vfstype)
vfstype = "fdfs";
} else if (strncmp(vfstype, "nfs", 3) == 0) {
vfstype = "nfs";
+ } else if (strcmp(vfstype, "lxproc") == 0) {
+ vfstype = "lxprocfs";
}
return (vfstype);
diff --git a/usr/src/uts/common/fs/vnode.c b/usr/src/uts/common/fs/vnode.c
index 4e73f7f6e6..953ee80471 100644
--- a/usr/src/uts/common/fs/vnode.c
+++ b/usr/src/uts/common/fs/vnode.c
@@ -25,6 +25,7 @@
* Copyright 2022 Spencer Evans-Cole.
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -209,6 +210,11 @@ static void (**vsd_destructor)(void *);
cr = crgetmapped(cr); \
}
+#define VOP_LATENCY_10MS 10000000
+#define VOP_LATENCY_100MS 100000000
+#define VOP_LATENCY_1S 1000000000
+#define VOP_LATENCY_10S 10000000000
+
/*
* Convert stat(2) formats to vnode types and vice versa. (Knows about
* numerical order of S_IFMT and vnode types.)
@@ -849,6 +855,36 @@ vn_rele(vnode_t *vp)
mutex_exit(&vp->v_lock);
}
+void
+vn_phantom_rele(vnode_t *vp)
+{
+ mutex_enter(&vp->v_lock);
+ VERIFY3U(vp->v_count, >=, vp->v_phantom_count);
+ vp->v_phantom_count--;
+ DTRACE_PROBE1(vn__phantom_rele, vnode_t *, vp);
+ if (vp->v_count == 1) {
+ ASSERT0(vp->v_phantom_count);
+ mutex_exit(&vp->v_lock);
+ VOP_INACTIVE(vp, CRED(), NULL);
+ return;
+ }
+ VERIFY(vp->v_count > 0);
+ VN_RELE_LOCKED(vp);
+ mutex_exit(&vp->v_lock);
+}
+
+/*
+ * Return the number of non-phantom holds. Things such as portfs will use
+ * phantom holds to prevent it from blocking filesystems from mounting over
+ * watched directories.
+ */
+uint_t
+vn_count(vnode_t *vp)
+{
+ ASSERT(MUTEX_HELD(&vp->v_lock));
+ return (vp->v_count - vp->v_phantom_count);
+}
+
/*
* Release a vnode referenced by the DNLC. Multiple DNLC references are treated
* as a single reference, so v_count is not decremented until the last DNLC hold
@@ -1130,7 +1166,20 @@ top:
* Do remaining checks for FNOFOLLOW and FNOLINKS.
*/
if ((filemode & FNOFOLLOW) && vp->v_type == VLNK) {
- error = ELOOP;
+ /*
+ * The __FLXPATH flag is a private interface for use by the lx
+ * brand in order to emulate open(O_NOFOLLOW|O_PATH) which,
+ * when a symbolic link is encountered, returns a file
+ * descriptor which references it.
+ * See uts/common/brand/lx/syscall/lx_open.c
+ *
+ * When this flag is set, VOP_OPEN() is not called (for a
+ * symlink, most filesystems will return ENOSYS anyway)
+ * and the link's vnode is returned to be linked to the
+ * file descriptor.
+ */
+ if ((filemode & __FLXPATH) == 0)
+ error = ELOOP;
goto out;
}
if (filemode & FNOLINKS) {
@@ -2441,6 +2490,7 @@ vn_reinit(vnode_t *vp)
{
vp->v_count = 1;
vp->v_count_dnlc = 0;
+ vp->v_phantom_count = 0;
vp->v_vfsp = NULL;
vp->v_stream = NULL;
vp->v_vfsmountedhere = NULL;
@@ -2497,6 +2547,7 @@ vn_free(vnode_t *vp)
*/
ASSERT((vp->v_count == 0) || (vp->v_count == 1));
ASSERT(vp->v_count_dnlc == 0);
+ ASSERT0(vp->v_phantom_count);
VERIFY(vp->v_path != NULL);
if (vp->v_path != vn_vpath_empty) {
kmem_free(vp->v_path, strlen(vp->v_path) + 1);
@@ -2587,6 +2638,7 @@ vnevent_rename_src(vnode_t *vp, vnode_t *dvp, char *name, caller_context_t *ct)
if (vp == NULL || vp->v_femhead == NULL) {
return;
}
+ (void) VOP_VNEVENT(dvp, VE_RENAME_SRC_DIR, vp, name, ct);
(void) VOP_VNEVENT(vp, VE_RENAME_SRC, dvp, name, ct);
}
@@ -2601,12 +2653,13 @@ vnevent_rename_dest(vnode_t *vp, vnode_t *dvp, char *name,
}
void
-vnevent_rename_dest_dir(vnode_t *vp, caller_context_t *ct)
+vnevent_rename_dest_dir(vnode_t *vp, vnode_t *nvp, char *name,
+ caller_context_t *ct)
{
if (vp == NULL || vp->v_femhead == NULL) {
return;
}
- (void) VOP_VNEVENT(vp, VE_RENAME_DEST_DIR, NULL, NULL, ct);
+ (void) VOP_VNEVENT(vp, VE_RENAME_DEST_DIR, nvp, name, ct);
}
void
@@ -2693,6 +2746,15 @@ vnevent_truncate(vnode_t *vp, caller_context_t *ct)
(void) VOP_VNEVENT(vp, VE_TRUNCATE, NULL, NULL, ct);
}
+void
+vnevent_resize(vnode_t *vp, caller_context_t *ct)
+{
+ if (vp == NULL || vp->v_femhead == NULL) {
+ return;
+ }
+ (void) VOP_VNEVENT(vp, VE_RESIZE, NULL, NULL, ct);
+}
+
/*
* Vnode accessors.
*/
@@ -3468,14 +3530,58 @@ fop_read(
cred_t *cr,
caller_context_t *ct)
{
- int err;
ssize_t resid_start = uiop->uio_resid;
+ zone_t *zonep = curzone;
+ zone_vfs_kstat_t *zvp = zonep->zone_vfs_stats;
+
+ hrtime_t start = 0, lat;
+ ssize_t len;
+ int err;
+
+ if ((vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VBLK) &&
+ vp->v_vfsp != NULL && (vp->v_vfsp->vfs_flag & VFS_STATS)) {
+ start = gethrtime();
+
+ mutex_enter(&zonep->zone_vfs_lock);
+ kstat_runq_enter(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+ }
VOPXID_MAP_CR(vp, cr);
err = (*(vp)->v_op->vop_read)(vp, uiop, ioflag, cr, ct);
- VOPSTATS_UPDATE_IO(vp, read,
- read_bytes, (resid_start - uiop->uio_resid));
+ len = resid_start - uiop->uio_resid;
+
+ VOPSTATS_UPDATE_IO(vp, read, read_bytes, len);
+
+ if (start != 0) {
+ mutex_enter(&zonep->zone_vfs_lock);
+ zonep->zone_vfs_rwstats.reads++;
+ zonep->zone_vfs_rwstats.nread += len;
+ kstat_runq_exit(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+
+ lat = gethrtime() - start;
+
+ if (lat >= VOP_LATENCY_10MS) {
+ if (lat < VOP_LATENCY_100MS)
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ else if (lat < VOP_LATENCY_1S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_10S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ } else {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_10s_ops.value.ui64);
+ }
+ }
+ }
+
return (err);
}
@@ -3487,14 +3593,63 @@ fop_write(
cred_t *cr,
caller_context_t *ct)
{
- int err;
ssize_t resid_start = uiop->uio_resid;
+ zone_t *zonep = curzone;
+ zone_vfs_kstat_t *zvp = zonep->zone_vfs_stats;
+
+ hrtime_t start = 0, lat;
+ ssize_t len;
+ int err;
+
+ /*
+ * For the purposes of VFS kstat consumers, the "waitq" calculation is
+ * repurposed as the active queue for VFS write operations. There's no
+ * actual wait queue for VFS operations.
+ */
+ if ((vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VBLK) &&
+ vp->v_vfsp != NULL && (vp->v_vfsp->vfs_flag & VFS_STATS)) {
+ start = gethrtime();
+
+ mutex_enter(&zonep->zone_vfs_lock);
+ kstat_waitq_enter(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+ }
VOPXID_MAP_CR(vp, cr);
err = (*(vp)->v_op->vop_write)(vp, uiop, ioflag, cr, ct);
- VOPSTATS_UPDATE_IO(vp, write,
- write_bytes, (resid_start - uiop->uio_resid));
+ len = resid_start - uiop->uio_resid;
+
+ VOPSTATS_UPDATE_IO(vp, write, write_bytes, len);
+
+ if (start != 0) {
+ mutex_enter(&zonep->zone_vfs_lock);
+ zonep->zone_vfs_rwstats.writes++;
+ zonep->zone_vfs_rwstats.nwritten += len;
+ kstat_waitq_exit(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+
+ lat = gethrtime() - start;
+
+ if (lat >= VOP_LATENCY_10MS) {
+ if (lat < VOP_LATENCY_100MS)
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ else if (lat < VOP_LATENCY_1S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_10S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ } else {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_10s_ops.value.ui64);
+ }
+ }
+ }
+
return (err);
}
diff --git a/usr/src/uts/common/fs/zfs/abd.c b/usr/src/uts/common/fs/zfs/abd.c
index b2004f3d42..b841a8f38e 100644
--- a/usr/src/uts/common/fs/zfs/abd.c
+++ b/usr/src/uts/common/fs/zfs/abd.c
@@ -171,7 +171,10 @@ int zfs_abd_scatter_min_size = 512 * 3;
* it at runtime would cause ABD iteration to work incorrectly for ABDs which
* were allocated with the old size, so a safeguard has been put in place which
* will cause the machine to panic if you change it and try to access the data
- * within a scattered ABD.
+ * within a scattered ABD. Note that tuning this value to be smaller than the
+ * page size can induce heavy fragmentation in the slab layer, which may itself
+ * result in more memory waste than is saved by the smaller chunk size -- and
+ * will induces more computational work in the slab layer. Tune with caution!
*/
size_t zfs_abd_chunk_size = 4096;
diff --git a/usr/src/uts/common/fs/zfs/arc.c b/usr/src/uts/common/fs/zfs/arc.c
index bf8b77f268..12b5872cdc 100644
--- a/usr/src/uts/common/fs/zfs/arc.c
+++ b/usr/src/uts/common/fs/zfs/arc.c
@@ -284,6 +284,7 @@
#include <sys/vdev.h>
#include <sys/vdev_impl.h>
#include <sys/dsl_pool.h>
+#include <sys/zfs_zone.h>
#include <sys/zio_checksum.h>
#include <sys/multilist.h>
#include <sys/abd.h>
@@ -349,7 +350,7 @@ int arc_grow_retry = 60;
int arc_kmem_cache_reap_retry_ms = 1000;
/* shift of arc_c for calculating overflow limit in arc_get_data_impl */
-int zfs_arc_overflow_shift = 8;
+int zfs_arc_overflow_shift = 3;
/* shift of arc_c for calculating both min and max arc_p */
int arc_p_min_shift = 4;
@@ -6112,6 +6113,14 @@ top:
if (hash_lock != NULL)
mutex_exit(hash_lock);
+ /*
+ * At this point, this read I/O has already missed in the ARC
+ * and will be going through to the disk. The I/O throttle
+ * should delay this I/O if this zone is using more than its I/O
+ * priority allows.
+ */
+ zfs_zone_io_throttle(ZFS_ZONE_IOP_READ);
+
if (*arc_flags & ARC_FLAG_WAIT)
return (zio_wait(rzio));
@@ -7168,6 +7177,10 @@ arc_init(void)
if (arc_c_min < arc_meta_limit / 2 && zfs_arc_min == 0)
arc_c_min = arc_meta_limit / 2;
+ /* On larger-memory machines, we clamp the minimum at 1GB */
+ if (zfs_arc_min == 0)
+ arc_c_min = MIN(arc_c_min, (1 << 30));
+
if (zfs_arc_meta_min > 0) {
arc_meta_min = zfs_arc_meta_min;
} else {
diff --git a/usr/src/uts/common/fs/zfs/dbuf.c b/usr/src/uts/common/fs/zfs/dbuf.c
index f610268bf4..38c4a83cb1 100644
--- a/usr/src/uts/common/fs/zfs/dbuf.c
+++ b/usr/src/uts/common/fs/zfs/dbuf.c
@@ -1125,8 +1125,17 @@ dbuf_read_impl(dmu_buf_impl_t *db, zio_t *zio, uint32_t flags,
arc_space_consume(max_bonuslen, ARC_SPACE_BONUS);
if (bonuslen < max_bonuslen)
bzero(db->db.db_data, max_bonuslen);
- if (bonuslen)
- bcopy(DN_BONUS(dn->dn_phys), db->db.db_data, bonuslen);
+ if (bonuslen) {
+ /*
+ * Absent byzantine on-disk corruption, we fully expect
+ * our bonuslen to be no more than max_bonuslen --
+ * but we nonetheless explicitly clamp it on the bcopy()
+ * to prevent any on-disk corruption from becoming
+ * rampant in-kernel corruption.
+ */
+ bcopy(DN_BONUS(dn->dn_phys), db->db.db_data,
+ MIN(bonuslen, max_bonuslen));
+ }
DB_DNODE_EXIT(db);
db->db_state = DB_CACHED;
mutex_exit(&db->db_mtx);
diff --git a/usr/src/uts/common/fs/zfs/dmu_send.c b/usr/src/uts/common/fs/zfs/dmu_send.c
index b7135df3fa..d91a48e2ca 100644
--- a/usr/src/uts/common/fs/zfs/dmu_send.c
+++ b/usr/src/uts/common/fs/zfs/dmu_send.c
@@ -22,7 +22,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2011, 2015 by Delphix. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
* Copyright 2014 HybridCluster. All rights reserved.
* Copyright 2016 RackTop Systems.
* Copyright (c) 2014 Integros [integros.com]
diff --git a/usr/src/uts/common/fs/zfs/dmu_tx.c b/usr/src/uts/common/fs/zfs/dmu_tx.c
index 53d5765bcb..6cb39d61a5 100644
--- a/usr/src/uts/common/fs/zfs/dmu_tx.c
+++ b/usr/src/uts/common/fs/zfs/dmu_tx.c
@@ -39,11 +39,11 @@
#include <sys/sa_impl.h>
#include <sys/zfs_context.h>
#include <sys/varargs.h>
+#include <sys/zfs_zone.h>
typedef void (*dmu_tx_hold_func_t)(dmu_tx_t *tx, struct dnode *dn,
uint64_t arg1, uint64_t arg2);
-
dmu_tx_t *
dmu_tx_create_dd(dsl_dir_t *dd)
{
@@ -213,6 +213,8 @@ dmu_tx_count_write(dmu_tx_hold_t *txh, uint64_t off, uint64_t len)
if (len == 0)
return;
+ zfs_zone_io_throttle(ZFS_ZONE_IOP_LOGICAL_WRITE);
+
(void) zfs_refcount_add_many(&txh->txh_space_towrite, len, FTAG);
if (zfs_refcount_count(&txh->txh_space_towrite) > 2 * DMU_MAX_ACCESS)
diff --git a/usr/src/uts/common/fs/zfs/dsl_dir.c b/usr/src/uts/common/fs/zfs/dsl_dir.c
index 02cad5f98e..c3d24abb3d 100644
--- a/usr/src/uts/common/fs/zfs/dsl_dir.c
+++ b/usr/src/uts/common/fs/zfs/dsl_dir.c
@@ -43,6 +43,7 @@
#include <sys/zio.h>
#include <sys/arc.h>
#include <sys/sunddi.h>
+#include <sys/zfs_zone.h>
#include <sys/zfeature.h>
#include <sys/policy.h>
#include <sys/zfs_znode.h>
@@ -1413,7 +1414,7 @@ dsl_dir_tempreserve_space(dsl_dir_t *dd, uint64_t lsize, uint64_t asize,
* locks are held.
*/
txg_delay(dd->dd_pool, tx->tx_txg,
- MSEC2NSEC(10), MSEC2NSEC(10));
+ zfs_zone_txg_delay(), MSEC2NSEC(10));
err = SET_ERROR(ERESTART);
}
}
diff --git a/usr/src/uts/common/fs/zfs/dsl_pool.c b/usr/src/uts/common/fs/zfs/dsl_pool.c
index bc6f9aff77..d3901c6f79 100644
--- a/usr/src/uts/common/fs/zfs/dsl_pool.c
+++ b/usr/src/uts/common/fs/zfs/dsl_pool.c
@@ -44,6 +44,7 @@
#include <sys/zfs_znode.h>
#include <sys/spa_impl.h>
#include <sys/dsl_deadlist.h>
+#include <sys/zfs_zone.h>
#include <sys/vdev_impl.h>
#include <sys/metaslab_impl.h>
#include <sys/bptree.h>
@@ -905,7 +906,7 @@ dsl_pool_undirty_space(dsl_pool_t *dp, int64_t space, uint64_t txg)
}
ASSERT3U(dp->dp_dirty_pertxg[txg & TXG_MASK], >=, space);
dp->dp_dirty_pertxg[txg & TXG_MASK] -= space;
- ASSERT3U(dp->dp_dirty_total, >=, space);
+ VERIFY3U(dp->dp_dirty_total, >=, space);
dsl_pool_dirty_delta(dp, -space);
mutex_exit(&dp->dp_lock);
}
diff --git a/usr/src/uts/common/fs/zfs/metaslab.c b/usr/src/uts/common/fs/zfs/metaslab.c
index 68733f47c1..4828824b10 100644
--- a/usr/src/uts/common/fs/zfs/metaslab.c
+++ b/usr/src/uts/common/fs/zfs/metaslab.c
@@ -23,6 +23,7 @@
* Copyright (c) 2011, 2018 by Delphix. All rights reserved.
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
+ * Copyright 2019 Joyent, Inc.
* Copyright (c) 2017, Intel Corporation.
*/
@@ -71,6 +72,11 @@ int zfs_metaslab_sm_blksz_with_log = (1 << 17);
int zfs_condense_pct = 200;
/*
+ * Never condense any space map. This is for debugging/recovery only.
+ */
+int zfs_condense_never = 0;
+
+/*
* Condensing a metaslab is not guaranteed to actually reduce the amount of
* space used on disk. In particular, a space map uses data in increments of
* MAX(1 << ashift, space_map_blksize), so a metaslab might use the
@@ -863,6 +869,7 @@ metaslab_group_activate(metaslab_group_t *mg)
{
metaslab_class_t *mc = mg->mg_class;
metaslab_group_t *mgprev, *mgnext;
+ char kstat_name[KSTAT_STRLEN];
ASSERT3U(spa_config_held(mc->mc_spa, SCL_ALLOC, RW_WRITER), !=, 0);
@@ -887,6 +894,33 @@ metaslab_group_activate(metaslab_group_t *mg)
mgprev->mg_next = mg;
mgnext->mg_prev = mg;
}
+
+ /* Create a kstat to monitor the loading and unloading of metaslabs. */
+ (void) snprintf(kstat_name, sizeof (kstat_name), "%llx",
+ (unsigned long long) mg->mg_vd->vdev_guid);
+
+ mutex_init(&mg->mg_kstat_lock, NULL, MUTEX_DEFAULT, NULL);
+ if ((mg->mg_kstat = kstat_create("zfs_metaslab_group", 0,
+ kstat_name, "misc", KSTAT_TYPE_NAMED,
+ sizeof (metaslab_group_kstat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL)) != NULL) {
+
+ metaslab_group_kstat_t *mg_kstat = kmem_zalloc(
+ sizeof (metaslab_group_kstat_t), KM_SLEEP);
+ kstat_named_init(&mg_kstat->mg_loads, "loads",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&mg_kstat->mg_unloads, "unloads",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&mg_kstat->mg_spa_name, "spa_name",
+ KSTAT_DATA_STRING);
+ kstat_named_setstr(&mg_kstat->mg_spa_name,
+ mg->mg_vd->vdev_spa->spa_name);
+
+ mg->mg_kstat->ks_data = mg_kstat;
+ mg->mg_kstat->ks_lock = &mg->mg_kstat_lock;
+ kstat_install(mg->mg_kstat);
+ }
+
mc->mc_rotor = mg;
}
@@ -963,6 +997,14 @@ metaslab_group_passivate(metaslab_group_t *mg)
mg->mg_prev = NULL;
mg->mg_next = NULL;
+
+ if (mg->mg_kstat != NULL) {
+ metaslab_group_kstat_t *data = mg->mg_kstat->ks_data;
+
+ kstat_delete(mg->mg_kstat);
+ kmem_free(data, sizeof (metaslab_group_kstat_t));
+ }
+ mutex_destroy(&mg->mg_kstat_lock);
}
boolean_t
@@ -2400,6 +2442,7 @@ metaslab_load_impl(metaslab_t *msp)
int
metaslab_load(metaslab_t *msp)
{
+ kstat_t *ksp;
ASSERT(MUTEX_HELD(&msp->ms_lock));
/*
@@ -2412,6 +2455,12 @@ metaslab_load(metaslab_t *msp)
VERIFY(!msp->ms_loading);
ASSERT(!msp->ms_condensing);
+ ksp = msp->ms_group->mg_kstat;
+ if (ksp != NULL) {
+ metaslab_group_kstat_t *mg_ksp = ksp->ks_data;
+ atomic_inc_64(&mg_ksp->mg_loads.value.ui64);
+ }
+
/*
* We set the loading flag BEFORE potentially dropping the lock to
* wait for an ongoing flush (see ms_flushing below). This way other
@@ -4290,12 +4339,11 @@ metaslab_sync_done(metaslab_t *msp, uint64_t txg)
/*
* If the metaslab is loaded and we've not tried to load or allocate
- * from it in 'metaslab_unload_delay' txgs, then unload it.
+ * from it in 'metaslab_unload_delay' txgs, then we normally unload it.
*/
if (msp->ms_loaded &&
msp->ms_disabled == 0 &&
msp->ms_selected_txg + metaslab_unload_delay < txg) {
-
for (int t = 1; t < TXG_CONCURRENT_STATES; t++) {
VERIFY0(range_tree_space(
msp->ms_allocating[(txg + t) & TXG_MASK]));
@@ -4539,8 +4587,6 @@ metaslab_block_alloc(metaslab_t *msp, uint64_t size, uint64_t txg)
range_tree_add(msp->ms_allocating[txg & TXG_MASK], start, size);
msp->ms_allocating_total += size;
- /* Track the last successful allocation */
- msp->ms_alloc_txg = txg;
metaslab_verify_space(msp, txg);
}
diff --git a/usr/src/uts/common/fs/zfs/spa.c b/usr/src/uts/common/fs/zfs/spa.c
index d6e230fbb4..db3317e4cd 100644
--- a/usr/src/uts/common/fs/zfs/spa.c
+++ b/usr/src/uts/common/fs/zfs/spa.c
@@ -1961,6 +1961,12 @@ spa_check_for_missing_logs(spa_t *spa)
if (idx > 0) {
spa_load_failed(spa, "some log devices are missing");
vdev_dbgmsg_print_tree(rvd, 2);
+
+ /* Save the timestamp of the last completed txg. */
+ VERIFY(nvlist_add_uint64(spa->spa_load_info,
+ ZPOOL_CONFIG_LOAD_TIME,
+ spa->spa_last_ubsync_txg_ts) == 0);
+
return (SET_ERROR(ENXIO));
}
} else {
@@ -1969,10 +1975,21 @@ spa_check_for_missing_logs(spa_t *spa)
if (tvd->vdev_islog &&
tvd->vdev_state == VDEV_STATE_CANT_OPEN) {
+ nvlist_t *rewind_info = fnvlist_alloc();
+
spa_set_log_state(spa, SPA_LOG_CLEAR);
spa_load_note(spa, "some log devices are "
"missing, ZIL is dropped.");
vdev_dbgmsg_print_tree(rvd, 2);
+
+ VERIFY(nvlist_add_uint64(rewind_info,
+ ZPOOL_CONFIG_LOAD_TIME,
+ spa->spa_uberblock.ub_timestamp) == 0);
+
+ VERIFY(nvlist_add_nvlist(spa->spa_load_info,
+ ZPOOL_CONFIG_REWIND_INFO,
+ rewind_info) == 0);
+
break;
}
}
diff --git a/usr/src/uts/common/fs/zfs/sys/metaslab_impl.h b/usr/src/uts/common/fs/zfs/sys/metaslab_impl.h
index bec7bdef2e..6adc8fa14e 100644
--- a/usr/src/uts/common/fs/zfs/sys/metaslab_impl.h
+++ b/usr/src/uts/common/fs/zfs/sys/metaslab_impl.h
@@ -283,8 +283,17 @@ struct metaslab_group {
boolean_t mg_disabled_updating;
kmutex_t mg_ms_disabled_lock;
kcondvar_t mg_ms_disabled_cv;
+
+ kstat_t *mg_kstat;
+ kmutex_t mg_kstat_lock;
};
+typedef struct metaslab_group_kstat {
+ kstat_named_t mg_loads;
+ kstat_named_t mg_unloads;
+ kstat_named_t mg_spa_name;
+} metaslab_group_kstat_t;
+
/*
* This value defines the number of elements in the ms_lbas array. The value
* of 64 was chosen as it covers all power of 2 buckets up to UINT64_MAX.
@@ -491,7 +500,6 @@ struct metaslab {
hrtime_t ms_unload_time; /* time last unloaded */
hrtime_t ms_selected_time; /* time last allocated from */
- uint64_t ms_alloc_txg; /* last successful alloc (debug only) */
uint64_t ms_max_size; /* maximum allocatable size */
/*
diff --git a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h
index d542368e7c..d760127ed9 100644
--- a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h
+++ b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h
@@ -151,6 +151,7 @@ struct vdev_queue {
avl_tree_t vq_write_offset_tree;
avl_tree_t vq_trim_offset_tree;
uint64_t vq_last_offset;
+ zoneid_t vq_last_zone_id;
hrtime_t vq_io_complete_ts; /* time last i/o completed */
kmutex_t vq_lock;
};
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_zone.h b/usr/src/uts/common/fs/zfs/sys/zfs_zone.h
new file mode 100644
index 0000000000..f1431b3f55
--- /dev/null
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_zone.h
@@ -0,0 +1,63 @@
+/*
+ * 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 2015, Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_FS_ZFS_ZONE_H
+#define _SYS_FS_ZFS_ZONE_H
+
+#ifdef _KERNEL
+#include <sys/isa_defs.h>
+#include <sys/types32.h>
+#include <sys/vdev_impl.h>
+#include <sys/zio.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ ZFS_ZONE_IOP_READ = 0,
+ ZFS_ZONE_IOP_WRITE,
+ ZFS_ZONE_IOP_LOGICAL_WRITE,
+} zfs_zone_iop_type_t;
+
+extern void zfs_zone_io_throttle(zfs_zone_iop_type_t);
+
+extern void zfs_zone_zio_init(zio_t *);
+extern void zfs_zone_zio_start(zio_t *);
+extern void zfs_zone_zio_done(zio_t *);
+extern void zfs_zone_zio_dequeue(zio_t *);
+extern void zfs_zone_zio_enqueue(zio_t *);
+extern void zfs_zone_report_txg_sync(void *);
+extern hrtime_t zfs_zone_txg_delay();
+#ifdef _KERNEL
+extern zio_t *zfs_zone_schedule(vdev_queue_t *, zio_priority_t, avl_index_t,
+ avl_tree_t *);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_FS_ZFS_ZONE_H */
diff --git a/usr/src/uts/common/fs/zfs/sys/zio.h b/usr/src/uts/common/fs/zfs/sys/zio.h
index d03106b942..7592614d6d 100644
--- a/usr/src/uts/common/fs/zfs/sys/zio.h
+++ b/usr/src/uts/common/fs/zfs/sys/zio.h
@@ -394,8 +394,14 @@ typedef int zio_pipe_stage_t(zio_t *zio);
* the reexecute flags are protected by io_lock, modifiable by children,
* and always propagated -- even when ZIO_FLAG_DONT_PROPAGATE is set.
*/
-#define ZIO_REEXECUTE_NOW 0x01
-#define ZIO_REEXECUTE_SUSPEND 0x02
+#define ZIO_REEXECUTE_NOW 0x01
+#define ZIO_REEXECUTE_SUSPEND 0x02
+#define ZIO_REEXECUTE_NO_SUSPEND 0x04
+
+#define ZIO_SHOULD_REEXECUTE(x) \
+ ((x)->io_reexecute & ZIO_REEXECUTE_NOW || \
+ ((x)->io_reexecute & ZIO_REEXECUTE_SUSPEND && \
+ (((x)->io_reexecute & ZIO_REEXECUTE_NO_SUSPEND) == 0)))
/*
* The io_trim flags are used to specify the type of TRIM to perform. They
@@ -465,6 +471,7 @@ struct zio {
hrtime_t io_timestamp;
hrtime_t io_queued_timestamp;
hrtime_t io_target_timestamp;
+ hrtime_t io_dispatched; /* time I/O was dispatched to disk */
hrtime_t io_delta; /* vdev queue service delta */
hrtime_t io_delay; /* Device access time (disk or */
/* file). */
@@ -500,6 +507,7 @@ struct zio {
zio_cksum_report_t *io_cksum_report;
uint64_t io_ena;
+ zoneid_t io_zoneid; /* zone which originated this I/O */
/* Taskq dispatching state */
taskq_ent_t io_tqent;
};
diff --git a/usr/src/uts/common/fs/zfs/txg.c b/usr/src/uts/common/fs/zfs/txg.c
index a8670dcaa8..a99e581737 100644
--- a/usr/src/uts/common/fs/zfs/txg.c
+++ b/usr/src/uts/common/fs/zfs/txg.c
@@ -32,6 +32,7 @@
#include <sys/dsl_scan.h>
#include <sys/zil.h>
#include <sys/callb.h>
+#include <sys/zfs_zone.h>
/*
* ZFS Transaction Groups
@@ -535,6 +536,8 @@ txg_sync_thread(void *arg)
txg, tx->tx_quiesce_txg_waiting, tx->tx_sync_txg_waiting);
mutex_exit(&tx->tx_sync_lock);
+ zfs_zone_report_txg_sync(dp);
+
start = ddi_get_lbolt();
spa_sync(spa, txg);
delta = ddi_get_lbolt() - start;
diff --git a/usr/src/uts/common/fs/zfs/vdev_disk.c b/usr/src/uts/common/fs/zfs/vdev_disk.c
index cd5e80d769..228529d9fe 100644
--- a/usr/src/uts/common/fs/zfs/vdev_disk.c
+++ b/usr/src/uts/common/fs/zfs/vdev_disk.c
@@ -28,6 +28,7 @@
*/
#include <sys/zfs_context.h>
+#include <sys/zfs_zone.h>
#include <sys/spa_impl.h>
#include <sys/refcount.h>
#include <sys/vdev_impl.h>
@@ -165,6 +166,8 @@ vdev_disk_off_finalize(ldi_handle_t lh __unused, ldi_ev_cookie_t ecookie,
int ldi_result, void *arg, void *ev_data __unused)
{
vdev_t *vd = (vdev_t *)arg;
+ vdev_disk_t *dvd = vd->vdev_tsd;
+ vdev_disk_ldi_cb_t *lcb;
/*
* Ignore events other than offline.
@@ -764,6 +767,7 @@ static void
vdev_disk_close(vdev_t *vd)
{
vdev_disk_t *dvd = vd->vdev_tsd;
+ vdev_disk_ldi_cb_t *lcb;
if (vd->vdev_reopening || dvd == NULL)
return;
@@ -1028,6 +1032,8 @@ vdev_disk_io_start(zio_t *zio)
bp->b_bufsize = zio->io_size;
bp->b_iodone = vdev_disk_io_intr;
+ zfs_zone_zio_start(zio);
+
/*
* In general we would expect ldi_strategy() to return non-zero only
* because of programming errors, but we've also seen this fail shortly
@@ -1044,6 +1050,8 @@ vdev_disk_io_done(zio_t *zio)
{
vdev_t *vd = zio->io_vd;
+ zfs_zone_zio_done(zio);
+
/*
* If the device returned EIO, then attempt a DKIOCSTATE ioctl to see if
* the device has been removed. If this is the case, then we trigger an
diff --git a/usr/src/uts/common/fs/zfs/vdev_queue.c b/usr/src/uts/common/fs/zfs/vdev_queue.c
index 4c6515c43d..b40126cac0 100644
--- a/usr/src/uts/common/fs/zfs/vdev_queue.c
+++ b/usr/src/uts/common/fs/zfs/vdev_queue.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -35,6 +36,7 @@
#include <sys/zio.h>
#include <sys/avl.h>
#include <sys/dsl_pool.h>
+#include <sys/zfs_zone.h>
#include <sys/metaslab_impl.h>
#include <sys/abd.h>
@@ -145,7 +147,7 @@ uint32_t zfs_vdev_sync_write_min_active = 10;
uint32_t zfs_vdev_sync_write_max_active = 10;
uint32_t zfs_vdev_async_read_min_active = 1;
uint32_t zfs_vdev_async_read_max_active = 3;
-uint32_t zfs_vdev_async_write_min_active = 1;
+uint32_t zfs_vdev_async_write_min_active = 3;
uint32_t zfs_vdev_async_write_max_active = 10;
uint32_t zfs_vdev_scrub_min_active = 1;
uint32_t zfs_vdev_scrub_max_active = 2;
@@ -274,6 +276,8 @@ vdev_queue_init(vdev_t *vd)
vdev_queue_offset_compare, sizeof (zio_t),
offsetof(struct zio, io_offset_node));
+ vq->vq_last_zone_id = 0;
+
for (zio_priority_t p = 0; p < ZIO_PRIORITY_NUM_QUEUEABLE; p++) {
int (*compfn) (const void *, const void *);
@@ -318,6 +322,7 @@ vdev_queue_io_add(vdev_queue_t *vq, zio_t *zio)
spa_t *spa = zio->io_spa;
ASSERT3U(zio->io_priority, <, ZIO_PRIORITY_NUM_QUEUEABLE);
+ zfs_zone_zio_enqueue(zio);
avl_add(vdev_queue_class_tree(vq, zio->io_priority), zio);
avl_add(vdev_queue_type_tree(vq, zio->io_type), zio);
@@ -334,6 +339,7 @@ vdev_queue_io_remove(vdev_queue_t *vq, zio_t *zio)
spa_t *spa = zio->io_spa;
ASSERT3U(zio->io_priority, <, ZIO_PRIORITY_NUM_QUEUEABLE);
+ zfs_zone_zio_dequeue(zio);
avl_remove(vdev_queue_class_tree(vq, zio->io_priority), zio);
avl_remove(vdev_queue_type_tree(vq, zio->io_type), zio);
@@ -732,7 +738,11 @@ again:
search.io_timestamp = 0;
search.io_offset = vq->vq_last_offset - 1;
VERIFY3P(avl_find(tree, &search, &idx), ==, NULL);
+#ifdef _KERNEL
+ zio = zfs_zone_schedule(vq, p, idx, tree);
+#else
zio = avl_nearest(tree, idx, AVL_AFTER);
+#endif
if (zio == NULL)
zio = avl_first(tree);
ASSERT3U(zio->io_priority, ==, p);
@@ -890,9 +900,11 @@ vdev_queue_change_io_priority(zio_t *zio, zio_priority_t priority)
spa_t *spa = zio->io_spa;
zio_priority_t oldpri = zio->io_priority;
+ zfs_zone_zio_dequeue(zio);
avl_remove(vdev_queue_class_tree(vq, zio->io_priority), zio);
zio->io_priority = priority;
avl_add(vdev_queue_class_tree(vq, zio->io_priority), zio);
+ zfs_zone_zio_enqueue(zio);
mutex_enter(&spa->spa_iokstat_lock);
ASSERT3U(spa->spa_queue_stats[oldpri].spa_queued, >, 0);
diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
index f479ea9f30..b74baf46ea 100644
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
@@ -696,9 +696,10 @@ zfs_secpolicy_setprop(const char *dsname, zfs_prop_t prop, nvpair_t *propval,
* Check permissions for special properties.
*/
switch (prop) {
+ case ZFS_PROP_DEDUP:
case ZFS_PROP_ZONED:
/*
- * Disallow setting of 'zoned' from within a local zone.
+ * Disallow setting these properties from within a local zone.
*/
if (!INGLOBALZONE(curproc))
return (SET_ERROR(EPERM));
@@ -1022,6 +1023,9 @@ zfs_secpolicy_recv(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
{
int error;
+ if (secpolicy_fs_import(cr) != 0)
+ return (set_errno(EPERM));
+
if ((error = zfs_secpolicy_write_perms(zc->zc_name,
ZFS_DELEG_PERM_RECEIVE, cr)) != 0)
return (error);
@@ -2162,7 +2166,8 @@ zfs_ioc_vdev_setfru(zfs_cmd_t *zc)
}
static int
-zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os)
+zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os,
+ boolean_t cachedpropsonly)
{
int error = 0;
nvlist_t *nv;
@@ -2180,7 +2185,8 @@ zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os)
* XXX reading with out owning
*/
if (!zc->zc_objset_stats.dds_inconsistent &&
- dmu_objset_type(os) == DMU_OST_ZVOL) {
+ dmu_objset_type(os) == DMU_OST_ZVOL &&
+ !cachedpropsonly) {
error = zvol_get_stats(os, nv);
if (error == EIO)
return (error);
@@ -2207,11 +2213,24 @@ static int
zfs_ioc_objset_stats(zfs_cmd_t *zc)
{
objset_t *os;
+ nvlist_t *nvl = NULL;
+ boolean_t cachedpropsonly = B_FALSE;
int error;
+ if (zc->zc_nvlist_src != (uintptr_t)NULL &&
+ (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ zc->zc_iflags, &nvl) != 0))
+ return (error);
+
+ if (nvl != NULL) {
+ (void) nvlist_lookup_boolean_value(nvl, "cachedpropsonly",
+ &cachedpropsonly);
+ nvlist_free(nvl);
+ }
+
error = dmu_objset_hold(zc->zc_name, FTAG, &os);
if (error == 0) {
- error = zfs_ioc_objset_stats_impl(zc, os);
+ error = zfs_ioc_objset_stats_impl(zc, os, cachedpropsonly);
dmu_objset_rele(os, FTAG);
}
@@ -2406,8 +2425,21 @@ static int
zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
{
objset_t *os;
+ nvlist_t *nvl = NULL;
+ boolean_t cachedpropsonly = B_FALSE;
int error;
+ if (zc->zc_nvlist_src != (uintptr_t)NULL &&
+ (error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
+ zc->zc_iflags, &nvl) != 0))
+ return (error);
+
+ if (nvl != NULL) {
+ (void) nvlist_lookup_boolean_value(nvl, "cachedpropsonly",
+ &cachedpropsonly);
+ nvlist_free(nvl);
+ }
+
error = dmu_objset_hold(zc->zc_name, FTAG, &os);
if (error != 0) {
return (error == ENOENT ? ESRCH : error);
@@ -2437,8 +2469,10 @@ zfs_ioc_snapshot_list_next(zfs_cmd_t *zc)
objset_t *ossnap;
error = dmu_objset_from_ds(ds, &ossnap);
- if (error == 0)
- error = zfs_ioc_objset_stats_impl(zc, ossnap);
+ if (error == 0) {
+ error = zfs_ioc_objset_stats_impl(zc,
+ ossnap, cachedpropsonly);
+ }
dsl_dataset_rele(ds, FTAG);
}
} else if (error == ENOENT) {
@@ -3148,6 +3182,7 @@ zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
uint64_t sense = ZFS_PROP_UNDEFINED;
uint64_t norm = ZFS_PROP_UNDEFINED;
uint64_t u8 = ZFS_PROP_UNDEFINED;
+ int error;
ASSERT(zplprops != NULL);
@@ -3194,8 +3229,9 @@ zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
VERIFY(nvlist_add_uint64(zplprops,
zfs_prop_to_name(ZFS_PROP_VERSION), zplver) == 0);
- if (norm == ZFS_PROP_UNDEFINED)
- VERIFY(zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm) == 0);
+ if (norm == ZFS_PROP_UNDEFINED &&
+ (error = zfs_get_zplprop(os, ZFS_PROP_NORMALIZE, &norm)) != 0)
+ return (error);
VERIFY(nvlist_add_uint64(zplprops,
zfs_prop_to_name(ZFS_PROP_NORMALIZE), norm) == 0);
@@ -3204,13 +3240,15 @@ zfs_fill_zplprops_impl(objset_t *os, uint64_t zplver,
*/
if (norm)
u8 = 1;
- if (u8 == ZFS_PROP_UNDEFINED)
- VERIFY(zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8) == 0);
+ if (u8 == ZFS_PROP_UNDEFINED &&
+ (error = zfs_get_zplprop(os, ZFS_PROP_UTF8ONLY, &u8)) != 0)
+ return (error);
VERIFY(nvlist_add_uint64(zplprops,
zfs_prop_to_name(ZFS_PROP_UTF8ONLY), u8) == 0);
- if (sense == ZFS_PROP_UNDEFINED)
- VERIFY(zfs_get_zplprop(os, ZFS_PROP_CASE, &sense) == 0);
+ if (sense == ZFS_PROP_UNDEFINED &&
+ (error = zfs_get_zplprop(os, ZFS_PROP_CASE, &sense)) != 0)
+ return (error);
VERIFY(nvlist_add_uint64(zplprops,
zfs_prop_to_name(ZFS_PROP_CASE), sense) == 0);
@@ -6591,7 +6629,8 @@ error:
static zfs_ioc_vec_t zfs_ioc_vec[ZFS_IOC_LAST - ZFS_IOC_FIRST];
static void
-zfs_ioctl_register_legacy(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func,
+zfs_ioctl_register_legacy(const char *name, zfs_ioc_t ioc,
+ zfs_ioc_legacy_func_t *func,
zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck,
boolean_t log_history, zfs_ioc_poolcheck_t pool_check)
{
@@ -6602,6 +6641,7 @@ zfs_ioctl_register_legacy(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func,
ASSERT3P(vec->zvec_legacy_func, ==, NULL);
ASSERT3P(vec->zvec_func, ==, NULL);
+ vec->zvec_name = name;
vec->zvec_legacy_func = func;
vec->zvec_secpolicy = secpolicy;
vec->zvec_namecheck = namecheck;
@@ -6645,7 +6685,7 @@ zfs_ioctl_register_pool(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func,
zfs_secpolicy_func_t *secpolicy, boolean_t log_history,
zfs_ioc_poolcheck_t pool_check)
{
- zfs_ioctl_register_legacy(ioc, func, secpolicy,
+ zfs_ioctl_register_legacy(NULL, ioc, func, secpolicy,
POOL_NAME, log_history, pool_check);
}
@@ -6653,14 +6693,15 @@ static void
zfs_ioctl_register_dataset_nolog(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func,
zfs_secpolicy_func_t *secpolicy, zfs_ioc_poolcheck_t pool_check)
{
- zfs_ioctl_register_legacy(ioc, func, secpolicy,
+ zfs_ioctl_register_legacy(NULL, ioc, func, secpolicy,
DATASET_NAME, B_FALSE, pool_check);
}
static void
-zfs_ioctl_register_pool_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func)
+zfs_ioctl_register_pool_modify(const char *name, zfs_ioc_t ioc,
+ zfs_ioc_legacy_func_t *func)
{
- zfs_ioctl_register_legacy(ioc, func, zfs_secpolicy_config,
+ zfs_ioctl_register_legacy(name, ioc, func, zfs_secpolicy_config,
POOL_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY);
}
@@ -6668,7 +6709,7 @@ static void
zfs_ioctl_register_pool_meta(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func,
zfs_secpolicy_func_t *secpolicy)
{
- zfs_ioctl_register_legacy(ioc, func, secpolicy,
+ zfs_ioctl_register_legacy(NULL, ioc, func, secpolicy,
NO_NAME, B_FALSE, POOL_CHECK_NONE);
}
@@ -6676,7 +6717,7 @@ static void
zfs_ioctl_register_dataset_read_secpolicy(zfs_ioc_t ioc,
zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy)
{
- zfs_ioctl_register_legacy(ioc, func, secpolicy,
+ zfs_ioctl_register_legacy(NULL, ioc, func, secpolicy,
DATASET_NAME, B_FALSE, POOL_CHECK_SUSPENDED);
}
@@ -6688,10 +6729,10 @@ zfs_ioctl_register_dataset_read(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func)
}
static void
-zfs_ioctl_register_dataset_modify(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func,
- zfs_secpolicy_func_t *secpolicy)
+zfs_ioctl_register_dataset_modify(const char *name, zfs_ioc_t ioc,
+ zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy)
{
- zfs_ioctl_register_legacy(ioc, func, secpolicy,
+ zfs_ioctl_register_legacy(name, ioc, func, secpolicy,
DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY);
}
@@ -6838,34 +6879,35 @@ zfs_ioctl_init(void)
/* IOCTLS that use the legacy function signature */
- zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze,
- zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_READONLY);
+ zfs_ioctl_register_legacy("pool_freeze", ZFS_IOC_POOL_FREEZE,
+ zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE,
+ POOL_CHECK_READONLY);
zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create,
zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE);
- zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SCAN,
+ zfs_ioctl_register_pool_modify("pool_scan", ZFS_IOC_POOL_SCAN,
zfs_ioc_pool_scan);
- zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_UPGRADE,
+ zfs_ioctl_register_pool_modify("pool_upgrade", ZFS_IOC_POOL_UPGRADE,
zfs_ioc_pool_upgrade);
- zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ADD,
+ zfs_ioctl_register_pool_modify("vdev_add", ZFS_IOC_VDEV_ADD,
zfs_ioc_vdev_add);
- zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_REMOVE,
+ zfs_ioctl_register_pool_modify("vdev_remove", ZFS_IOC_VDEV_REMOVE,
zfs_ioc_vdev_remove);
- zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SET_STATE,
+ zfs_ioctl_register_pool_modify("vdev_set_state", ZFS_IOC_VDEV_SET_STATE,
zfs_ioc_vdev_set_state);
- zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_ATTACH,
+ zfs_ioctl_register_pool_modify("vdev_attach", ZFS_IOC_VDEV_ATTACH,
zfs_ioc_vdev_attach);
- zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_DETACH,
+ zfs_ioctl_register_pool_modify("vdev_detach", ZFS_IOC_VDEV_DETACH,
zfs_ioc_vdev_detach);
- zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETPATH,
+ zfs_ioctl_register_pool_modify("vdev_setpath", ZFS_IOC_VDEV_SETPATH,
zfs_ioc_vdev_setpath);
- zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SETFRU,
+ zfs_ioctl_register_pool_modify("vdev_setfru", ZFS_IOC_VDEV_SETFRU,
zfs_ioc_vdev_setfru);
- zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_SET_PROPS,
+ zfs_ioctl_register_pool_modify("pool_set_props", ZFS_IOC_POOL_SET_PROPS,
zfs_ioc_pool_set_props);
- zfs_ioctl_register_pool_modify(ZFS_IOC_VDEV_SPLIT,
+ zfs_ioctl_register_pool_modify("vdev_split", ZFS_IOC_VDEV_SPLIT,
zfs_ioc_vdev_split);
- zfs_ioctl_register_pool_modify(ZFS_IOC_POOL_REGUID,
+ zfs_ioctl_register_pool_modify("pool_reguid", ZFS_IOC_POOL_REGUID,
zfs_ioc_pool_reguid);
zfs_ioctl_register_pool_meta(ZFS_IOC_POOL_CONFIGS,
@@ -6943,20 +6985,20 @@ zfs_ioctl_init(void)
zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_SEND,
zfs_ioc_send, zfs_secpolicy_send);
- zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_PROP, zfs_ioc_set_prop,
- zfs_secpolicy_none);
- zfs_ioctl_register_dataset_modify(ZFS_IOC_DESTROY, zfs_ioc_destroy,
- zfs_secpolicy_destroy);
- zfs_ioctl_register_dataset_modify(ZFS_IOC_RENAME, zfs_ioc_rename,
- zfs_secpolicy_rename);
- zfs_ioctl_register_dataset_modify(ZFS_IOC_RECV, zfs_ioc_recv,
+ zfs_ioctl_register_dataset_modify("set_prop", ZFS_IOC_SET_PROP,
+ zfs_ioc_set_prop, zfs_secpolicy_none);
+ zfs_ioctl_register_dataset_modify("destroy", ZFS_IOC_DESTROY,
+ zfs_ioc_destroy, zfs_secpolicy_destroy);
+ zfs_ioctl_register_dataset_modify("rename", ZFS_IOC_RENAME,
+ zfs_ioc_rename, zfs_secpolicy_rename);
+ zfs_ioctl_register_dataset_modify("recv", ZFS_IOC_RECV, zfs_ioc_recv,
zfs_secpolicy_recv);
- zfs_ioctl_register_dataset_modify(ZFS_IOC_PROMOTE, zfs_ioc_promote,
- zfs_secpolicy_promote);
- zfs_ioctl_register_dataset_modify(ZFS_IOC_INHERIT_PROP,
+ zfs_ioctl_register_dataset_modify("promote", ZFS_IOC_PROMOTE,
+ zfs_ioc_promote, zfs_secpolicy_promote);
+ zfs_ioctl_register_dataset_modify("inherit_prop", ZFS_IOC_INHERIT_PROP,
zfs_ioc_inherit_prop, zfs_secpolicy_inherit_prop);
- zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_FSACL, zfs_ioc_set_fsacl,
- zfs_secpolicy_set_fsacl);
+ zfs_ioctl_register_dataset_modify("set_fsacl", ZFS_IOC_SET_FSACL,
+ zfs_ioc_set_fsacl, zfs_secpolicy_set_fsacl);
zfs_ioctl_register_dataset_nolog(ZFS_IOC_SHARE, zfs_ioc_share,
zfs_secpolicy_share, POOL_CHECK_NONE);
@@ -7333,7 +7375,32 @@ zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp)
nvlist_free(outnvl);
} else {
+ spa_t *spa;
+ uint64_t orig_cookie = zc->zc_cookie;
+
error = vec->zvec_legacy_func(zc);
+
+ if (error == 0 && vec->zvec_allow_log &&
+ vec->zvec_name != NULL &&
+ spa_open(zc->zc_name, &spa, FTAG) == 0) {
+ nvlist_t *lognv = NULL;
+ char *msg;
+ uint_t len = strlen(vec->zvec_name) +
+ strlen(zc->zc_name) + 128;
+
+ msg = kmem_alloc(len, KM_SLEEP);
+
+ lognv = fnvlist_alloc();
+ (void) snprintf(msg, len,
+ "%s pool: %s cookie: %lu guid: %lx", vec->zvec_name,
+ zc->zc_name, orig_cookie, zc->zc_guid);
+ fnvlist_add_string(lognv, ZPOOL_HIST_IOCTL, msg);
+
+ (void) spa_history_log_nvl(spa, lognv);
+ spa_close(spa, FTAG);
+ fnvlist_free(lognv);
+ kmem_free(msg, len);
+ }
}
out:
diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
index 288dc93e3c..95a2be6239 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
@@ -23,7 +23,7 @@
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
* Copyright 2020 Joshua M. Clulow <josh@sysmgr.org>
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
* Copyright 2022 Oxide Computer Company
@@ -1917,7 +1917,7 @@ zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
mutex_enter(&mvp->v_lock);
if ((uap->flags & MS_REMOUNT) == 0 &&
(uap->flags & MS_OVERLAY) == 0 &&
- (mvp->v_count != 1 || (mvp->v_flag & VROOT))) {
+ (vn_count(mvp) != 1 || (mvp->v_flag & VROOT))) {
mutex_exit(&mvp->v_lock);
return (SET_ERROR(EBUSY));
}
@@ -2342,6 +2342,17 @@ zfs_umount(vfs_t *vfsp, int fflag, cred_t *cr)
if (zfsvfs->z_ctldir != NULL)
zfsctl_destroy(zfsvfs);
+ /*
+ * If we're doing a forced unmount on a dataset which still has
+ * references and is in a zone, then we need to cleanup the zone
+ * reference at this point or else the zone will never be able to
+ * shutdown.
+ */
+ if ((fflag & MS_FORCE) && vfsp->vfs_count > 1 && vfsp->vfs_zone) {
+ zone_rele_ref(&vfsp->vfs_implp->vi_zone_ref, ZONE_REF_VFS);
+ vfsp->vfs_zone = NULL;
+ }
+
return (0);
}
diff --git a/usr/src/uts/common/fs/zfs/zfs_vnops.c b/usr/src/uts/common/fs/zfs/zfs_vnops.c
index 1ee01c9146..dd58b4a549 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vnops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c
@@ -847,6 +847,17 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
limit = MAXOFFSET_T;
+ /*
+ * Pre-fault the pages to ensure slow (eg NFS) pages
+ * don't hold up txg.
+ * Skip this if uio contains loaned arc_buf.
+ */
+ if ((uio->uio_extflg == UIO_XUIO) &&
+ (((xuio_t *)uio)->xu_type == UIOTYPE_ZEROCOPY))
+ xuio = (xuio_t *)uio;
+ else
+ uio_prefaultpages(n, uio);
+
ZFS_ENTER(zfsvfs);
ZFS_VERIFY_ZP(zp);
@@ -901,17 +912,6 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
}
/*
- * Pre-fault the pages to ensure slow (eg NFS) pages
- * don't hold up txg.
- * Skip this if uio contains loaned arc_buf.
- */
- if ((uio->uio_extflg == UIO_XUIO) &&
- (((xuio_t *)uio)->xu_type == UIOTYPE_ZEROCOPY))
- xuio = (xuio_t *)uio;
- else
- uio_prefaultpages(MIN(n, max_blksz), uio);
-
- /*
* If in append mode, set the io offset pointer to eof.
*/
locked_range_t *lr;
@@ -1147,9 +1147,6 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
break;
ASSERT(tx_bytes == nbytes);
n -= nbytes;
-
- if (!xuio && n > 0)
- uio_prefaultpages(MIN(n, max_blksz), uio);
}
rangelock_exit(lr);
@@ -3164,8 +3161,11 @@ top:
return (err);
}
- if (vap->va_size == 0)
+ if (vap->va_size == 0) {
vnevent_truncate(ZTOV(zp), ct);
+ } else {
+ vnevent_resize(ZTOV(zp), ct);
+ }
}
if (mask & (AT_ATIME|AT_MTIME) ||
@@ -4173,9 +4173,7 @@ top:
if (error == 0) {
vnevent_rename_src(ZTOV(szp), sdvp, snm, ct);
- /* notify the target dir if it is not the same as source dir */
- if (tdvp != sdvp)
- vnevent_rename_dest_dir(tdvp, ct);
+ vnevent_rename_dest_dir(tdvp, ZTOV(szp), tnm, ct);
}
out:
if (zl != NULL)
@@ -5265,8 +5263,13 @@ zfs_space(vnode_t *vp, int cmd, flock64_t *bfp, int flag,
error = zfs_freesp(zp, off, len, flag, TRUE);
- if (error == 0 && off == 0 && len == 0)
- vnevent_truncate(ZTOV(zp), ct);
+ if (error == 0 && len == 0) {
+ if (off == 0) {
+ vnevent_truncate(ZTOV(zp), ct);
+ } else {
+ vnevent_resize(ZTOV(zp), ct);
+ }
+ }
ZFS_EXIT(zfsvfs);
return (error);
diff --git a/usr/src/uts/common/fs/zfs/zfs_zone.c b/usr/src/uts/common/fs/zfs/zfs_zone.c
new file mode 100644
index 0000000000..f151595095
--- /dev/null
+++ b/usr/src/uts/common/fs/zfs/zfs_zone.c
@@ -0,0 +1,1419 @@
+/*
+ * 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 2018, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * The ZFS/Zone I/O throttle and scheduler attempts to ensure fair access to
+ * ZFS I/O resources for each zone.
+ *
+ * I/O contention can be major pain point on a multi-tenant system. A single
+ * zone can issue a stream of I/O operations, usually synchronous writes, which
+ * disrupt I/O performance for all other zones. This problem is further
+ * exacerbated by ZFS, which buffers all asynchronous writes in a single TXG,
+ * a set of blocks which are atomically synced to disk. The process of
+ * syncing a TXG can occupy all of a device's I/O bandwidth, thereby starving
+ * out any pending read operations.
+ *
+ * There are two facets to this capability; the throttle and the scheduler.
+ *
+ * Throttle
+ *
+ * The requirements on the throttle are:
+ *
+ * 1) Ensure consistent and predictable I/O latency across all zones.
+ * 2) Sequential and random workloads have very different characteristics,
+ * so it is a non-starter to track IOPS or throughput.
+ * 3) A zone should be able to use the full disk bandwidth if no other zone
+ * is actively using the disk.
+ *
+ * The throttle has two components: one to track and account for each zone's
+ * I/O requests, and another to throttle each zone's operations when it
+ * exceeds its fair share of disk I/O. When the throttle detects that a zone is
+ * consuming more than is appropriate, each read or write system call is
+ * delayed by up to 100 microseconds, which we've found is sufficient to allow
+ * other zones to interleave I/O requests during those delays.
+ *
+ * Note: The throttle will delay each logical I/O (as opposed to the physical
+ * I/O which will likely be issued asynchronously), so it may be easier to
+ * think of the I/O throttle delaying each read/write syscall instead of the
+ * actual I/O operation. For each zone, the throttle tracks an ongoing average
+ * of read and write operations performed to determine the overall I/O
+ * utilization for each zone.
+ *
+ * The throttle calculates a I/O utilization metric for each zone using the
+ * following formula:
+ *
+ * (# of read syscalls) x (Average read latency) +
+ * (# of write syscalls) x (Average write latency)
+ *
+ * Once each zone has its utilization metric, the I/O throttle will compare I/O
+ * utilization across all zones, and if a zone has a higher-than-average I/O
+ * utilization, system calls from that zone are throttled. That is, if one
+ * zone has a much higher utilization, that zone's delay is increased by 5
+ * microseconds, up to a maximum of 100 microseconds. Conversely, if a zone is
+ * already throttled and has a lower utilization than average, its delay will
+ * be lowered by 5 microseconds.
+ *
+ * The throttle calculation is driven by IO activity, but since IO does not
+ * happen at fixed intervals, timestamps are used to track when the last update
+ * was made and to drive recalculation.
+ *
+ * The throttle recalculates each zone's I/O usage and throttle delay (if any)
+ * on the zfs_zone_adjust_time interval. Overall I/O latency is maintained as
+ * a decayed average which is updated on the zfs_zone_sys_avg_cycle interval.
+ *
+ * Scheduler
+ *
+ * The I/O scheduler manages the vdev queues – the queues of pending I/Os to
+ * issue to the disks. It only makes scheduling decisions for the two
+ * synchronous I/O queues (read & write).
+ *
+ * The scheduler maintains how many I/Os in the queue are from each zone, and
+ * if one zone has a disproportionately large number of I/Os in the queue, the
+ * scheduler will allow certain I/Os from the underutilized zones to be "bumped"
+ * and pulled from the middle of the queue. This bump allows zones with a small
+ * number of I/Os (so small they may not even be taken into account by the
+ * throttle) to complete quickly instead of waiting behind dozens of I/Os from
+ * other zones.
+ */
+
+#include <sys/spa.h>
+#include <sys/vdev_impl.h>
+#include <sys/zfs_zone.h>
+
+#ifndef _KERNEL
+
+/*
+ * Stubs for when compiling for user-land.
+ */
+
+void
+zfs_zone_io_throttle(zfs_zone_iop_type_t type)
+{
+}
+
+void
+zfs_zone_zio_init(zio_t *zp)
+{
+}
+
+void
+zfs_zone_zio_start(zio_t *zp)
+{
+}
+
+void
+zfs_zone_zio_done(zio_t *zp)
+{
+}
+
+void
+zfs_zone_zio_dequeue(zio_t *zp)
+{
+}
+
+void
+zfs_zone_zio_enqueue(zio_t *zp)
+{
+}
+
+/*ARGSUSED*/
+void
+zfs_zone_report_txg_sync(void *dp)
+{
+}
+
+hrtime_t
+zfs_zone_txg_delay()
+{
+ return (MSEC2NSEC(10));
+}
+
+#else
+
+/*
+ * The real code.
+ */
+
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/atomic.h>
+#include <sys/zio.h>
+#include <sys/zone.h>
+#include <sys/avl.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+
+/*
+ * The zone throttle delays read and write operations from certain zones based
+ * on each zone's IO utilitzation. Once a cycle (defined by zfs_zone_cycle_time
+ * below), the delays for each zone are recalculated based on the utilization
+ * over the previous window.
+ */
+boolean_t zfs_zone_delay_enable = B_TRUE; /* enable IO throttle */
+uint8_t zfs_zone_delay_step = 5; /* usec amnt to change delay */
+uint8_t zfs_zone_delay_ceiling = 100; /* usec delay max */
+
+boolean_t zfs_zone_priority_enable = B_TRUE; /* enable IO priority */
+
+/*
+ * For certain workloads, one zone may be issuing primarily sequential I/O and
+ * another primarily random I/O. The sequential I/O will complete much more
+ * quickly than the random I/O, driving the average system latency for those
+ * operations way down. As a result, the random I/O may be throttled back, even
+ * though the sequential I/O should be throttled to allow the random I/O more
+ * access to the disk.
+ *
+ * This tunable limits the discrepancy between the read and write system
+ * latency. If one becomes excessively high, this tunable prevents the I/O
+ * throttler from exacerbating the imbalance.
+ */
+uint_t zfs_zone_rw_lat_limit = 10;
+
+/*
+ * The I/O throttle will only start delaying zones when it detects disk
+ * utilization has reached a certain level. This tunable controls the
+ * threshold at which the throttle will start delaying zones. When the number
+ * of vdevs is small, the calculation should correspond closely with the %b
+ * column from iostat -- but as the number of vdevs becomes large, it will
+ * correlate less and less to any single device (therefore making it a poor
+ * approximation for the actual I/O utilization on such systems). We
+ * therefore use our derived utilization conservatively: we know that low
+ * derived utilization does indeed correlate to low I/O use -- but that a high
+ * rate of derived utilization does not necesarily alone denote saturation;
+ * where we see a high rate of utilization, we also look for laggard I/Os to
+ * attempt to detect saturation.
+ */
+uint_t zfs_zone_util_threshold = 80;
+uint_t zfs_zone_underutil_threshold = 60;
+
+/*
+ * There are three important tunables here: zfs_zone_laggard_threshold denotes
+ * the threshold at which an I/O is considered to be of notably high latency;
+ * zfs_zone_laggard_recent denotes the number of microseconds before the
+ * current time after which the last laggard is considered to be sufficiently
+ * recent to merit increasing the throttle; zfs_zone_laggard_ancient denotes
+ * the microseconds before the current time before which the last laggard is
+ * considered to be sufficiently old to merit decreasing the throttle. The
+ * most important tunable of these three is the zfs_zone_laggard_threshold: in
+ * modeling data from a large public cloud, this tunable was found to have a
+ * much greater effect on the throttle than the two time-based thresholds.
+ * This must be set high enough to not result in spurious throttling, but not
+ * so high as to allow pathological I/O to persist in the system.
+ */
+uint_t zfs_zone_laggard_threshold = 50000; /* 50 ms */
+uint_t zfs_zone_laggard_recent = 1000000; /* 1000 ms */
+uint_t zfs_zone_laggard_ancient = 5000000; /* 5000 ms */
+
+/*
+ * Throughout this subsystem, our timestamps are in microseconds. Our system
+ * average cycle is one second or 1 million microseconds. Our zone counter
+ * update cycle is two seconds or 2 million microseconds. We use a longer
+ * duration for that cycle because some ops can see a little over two seconds of
+ * latency when they are being starved by another zone.
+ */
+uint_t zfs_zone_sys_avg_cycle = 1000000; /* 1 s */
+uint_t zfs_zone_cycle_time = 2000000; /* 2 s */
+
+/*
+ * How often the I/O throttle will reevaluate each zone's utilization, in
+ * microseconds. Default is 1/4 sec.
+ */
+uint_t zfs_zone_adjust_time = 250000; /* 250 ms */
+
+typedef struct {
+ hrtime_t cycle_start;
+ hrtime_t cycle_lat;
+ hrtime_t sys_avg_lat;
+ uint_t cycle_cnt;
+} sys_lat_cycle_t;
+
+typedef struct {
+ hrtime_t zi_now;
+ uint_t zi_avgrlat;
+ uint_t zi_avgwlat;
+ uint64_t zi_totpri;
+ uint64_t zi_totutil;
+ int zi_active;
+ uint_t zi_diskutil;
+ boolean_t zi_underutil;
+ boolean_t zi_overutil;
+} zoneio_stats_t;
+
+static sys_lat_cycle_t rd_lat;
+static sys_lat_cycle_t wr_lat;
+
+/*
+ * Some basic disk stats to determine disk utilization. The utilization info
+ * for all disks on the system is aggregated into these values.
+ *
+ * Overall disk utilization for the current cycle is calculated as:
+ *
+ * ((zfs_disk_rtime - zfs_disk_last_rtime) * 100)
+ * ----------------------------------------------
+ * ((now - zfs_zone_last_checked) * 1000);
+ */
+kmutex_t zfs_disk_lock; /* protects the following: */
+uint_t zfs_disk_rcnt; /* Number of outstanding IOs */
+hrtime_t zfs_disk_rtime = 0; /* cummulative sum of time performing IO */
+hrtime_t zfs_disk_rlastupdate = 0; /* time last IO dispatched */
+
+hrtime_t zfs_disk_last_rtime = 0; /* prev. cycle's zfs_disk_rtime val */
+/* time that we last updated per-zone throttle info */
+kmutex_t zfs_last_check_lock; /* protects zfs_zone_last_checked */
+hrtime_t zfs_zone_last_checked = 0;
+hrtime_t zfs_disk_last_laggard = 0;
+
+/*
+ * Data used to keep track of how often txg sync is running.
+ */
+extern int zfs_txg_timeout;
+static uint_t txg_last_check;
+static uint_t txg_cnt;
+static uint_t txg_sync_rate;
+
+boolean_t zfs_zone_schedule_enable = B_TRUE; /* enable IO sched. */
+/*
+ * Threshold for when zio scheduling should kick in.
+ *
+ * This threshold is based on the zfs_vdev_sync_read_max_active value for the
+ * number of I/Os that can be pending on a device. If there are more than the
+ * max_active ops already queued up, beyond those already issued to the vdev,
+ * then use zone-based scheduling to get the next synchronous zio.
+ */
+uint32_t zfs_zone_schedule_thresh = 10;
+
+/*
+ * On each pass of the scheduler we increment the zone's weight (up to this
+ * maximum). The weight is used by the scheduler to prevent starvation so
+ * that zones which haven't been able to do any IO over many iterations
+ * will max out thier weight to this value.
+ */
+#define SCHED_WEIGHT_MAX 20
+
+/*
+ * Tunables for delay throttling when TXG sync is occurring.
+ *
+ * If the zone is performing a write and we're doing above normal TXG syncing,
+ * then throttle for longer than normal. The zone's wait time is multiplied
+ * by the scale (zfs_zone_txg_throttle_scale).
+ */
+int zfs_zone_txg_throttle_scale = 2;
+hrtime_t zfs_zone_txg_delay_nsec = MSEC2NSEC(20);
+
+typedef struct {
+ int zq_qdepth;
+ zio_priority_t zq_queue;
+ int zq_priority;
+ int zq_wt;
+ zoneid_t zq_zoneid;
+} zone_q_bump_t;
+
+/*
+ * This uses gethrtime() but returns a value in usecs.
+ */
+#define GET_USEC_TIME (gethrtime() / 1000)
+#define NANO_TO_MICRO(x) (x / (NANOSEC / MICROSEC))
+
+/*
+ * Keep track of the zone's ZFS IOPs.
+ *
+ * See the comment on the zfs_zone_io_throttle function for which/how IOPs are
+ * accounted for.
+ *
+ * If the number of ops is >1 then we can just use that value. However,
+ * if the number of ops is <2 then we might have a zone which is trying to do
+ * IO but is not able to get any ops through the system. We don't want to lose
+ * track of this zone so we factor in its decayed count into the current count.
+ *
+ * Each cycle (zfs_zone_sys_avg_cycle) we want to update the decayed count.
+ * However, since this calculation is driven by IO activity and since IO does
+ * not happen at fixed intervals, we use a timestamp to see when the last update
+ * was made. If it was more than one cycle ago, then we need to decay the
+ * historical count by the proper number of additional cycles in which no IO was
+ * performed.
+ *
+ * Return a time delta indicating how far into the current cycle we are or 0
+ * if the last IO was more than a cycle ago.
+ */
+static hrtime_t
+compute_historical_zone_cnt(hrtime_t unow, sys_zio_cntr_t *cp)
+{
+ hrtime_t delta;
+ int gen_cnt;
+
+ /*
+ * Check if its time to recompute a new zone count.
+ * If we're still collecting data for the current cycle, return false.
+ */
+ delta = unow - cp->cycle_start;
+ if (delta < zfs_zone_cycle_time)
+ return (delta);
+
+ /* A previous cycle is past, compute the new zone count. */
+
+ /*
+ * Figure out how many generations we have to decay the historical
+ * count, since multiple cycles may have elapsed since our last IO.
+ * We depend on int rounding here.
+ */
+ gen_cnt = (int)(delta / zfs_zone_cycle_time);
+
+ /* If more than 5 cycles since last the IO, reset count. */
+ if (gen_cnt > 5) {
+ cp->zone_avg_cnt = 0;
+ } else {
+ /* Update the count. */
+ int i;
+
+ /*
+ * If the zone did more than 1 IO, just use its current count
+ * as the historical value, otherwise decay the historical
+ * count and factor that into the new historical count. We
+ * pick a threshold > 1 so that we don't lose track of IO due
+ * to int rounding.
+ */
+ if (cp->cycle_cnt > 1)
+ cp->zone_avg_cnt = cp->cycle_cnt;
+ else
+ cp->zone_avg_cnt = cp->cycle_cnt +
+ (cp->zone_avg_cnt / 2);
+
+ /*
+ * If more than one generation has elapsed since the last
+ * update, decay the values further.
+ */
+ for (i = 1; i < gen_cnt; i++)
+ cp->zone_avg_cnt = cp->zone_avg_cnt / 2;
+ }
+
+ /* A new cycle begins. */
+ cp->cycle_start = unow;
+ cp->cycle_cnt = 0;
+
+ return (0);
+}
+
+/*
+ * Add IO op data to the zone.
+ */
+static void
+add_zone_iop(zone_persist_t *zpd, hrtime_t unow, zfs_zone_iop_type_t op)
+{
+ zone_zfs_io_t *iop;
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ iop = zpd->zpers_zfsp;
+ if (iop == NULL) {
+ mutex_exit(&zpd->zpers_zfs_lock);
+ return;
+ }
+
+ switch (op) {
+ case ZFS_ZONE_IOP_READ:
+ (void) compute_historical_zone_cnt(unow, &iop->zpers_rd_ops);
+ iop->zpers_rd_ops.cycle_cnt++;
+ break;
+ case ZFS_ZONE_IOP_WRITE:
+ (void) compute_historical_zone_cnt(unow, &iop->zpers_wr_ops);
+ iop->zpers_wr_ops.cycle_cnt++;
+ break;
+ case ZFS_ZONE_IOP_LOGICAL_WRITE:
+ (void) compute_historical_zone_cnt(unow, &iop->zpers_lwr_ops);
+ iop->zpers_lwr_ops.cycle_cnt++;
+ break;
+ }
+ mutex_exit(&zpd->zpers_zfs_lock);
+}
+
+/*
+ * Use a decaying average to keep track of the overall system latency.
+ *
+ * We want to have the recent activity heavily weighted, but if the
+ * activity decreases or stops, then the average should quickly decay
+ * down to the new value.
+ *
+ * Each cycle (zfs_zone_sys_avg_cycle) we want to update the decayed average.
+ * However, since this calculation is driven by IO activity and since IO does
+ * not happen at fixed intervals, we use a timestamp to see when the last
+ * update was made. If it was more than one cycle ago, then we need to decay
+ * the average by the proper number of additional cycles in which no IO was
+ * performed.
+ *
+ * Return true if we actually computed a new system average.
+ * If we're still within an active cycle there is nothing to do, return false.
+ */
+static boolean_t
+compute_new_sys_avg(hrtime_t unow, sys_lat_cycle_t *cp)
+{
+ hrtime_t delta;
+ int gen_cnt;
+
+ /*
+ * Check if its time to recompute a new average.
+ * If we're still collecting data for the current cycle, return false.
+ */
+ delta = unow - cp->cycle_start;
+ if (delta < zfs_zone_sys_avg_cycle)
+ return (B_FALSE);
+
+ /* A previous cycle is past, compute a new system average. */
+
+ /*
+ * Figure out how many generations we have to decay, since multiple
+ * cycles may have elapsed since our last IO.
+ * We count on int rounding here.
+ */
+ gen_cnt = (int)(delta / zfs_zone_sys_avg_cycle);
+
+ /* If more than 5 cycles since last the IO, reset average. */
+ if (gen_cnt > 5) {
+ cp->sys_avg_lat = 0;
+ } else {
+ /* Update the average. */
+ int i;
+
+ cp->sys_avg_lat =
+ (cp->sys_avg_lat + cp->cycle_lat) / (1 + cp->cycle_cnt);
+
+ /*
+ * If more than one generation has elapsed since the last
+ * update, decay the values further.
+ */
+ for (i = 1; i < gen_cnt; i++)
+ cp->sys_avg_lat = cp->sys_avg_lat / 2;
+ }
+
+ /* A new cycle begins. */
+ cp->cycle_start = unow;
+ cp->cycle_cnt = 0;
+ cp->cycle_lat = 0;
+
+ return (B_TRUE);
+}
+
+static void
+add_sys_iop(hrtime_t unow, int op, int lat)
+{
+ switch (op) {
+ case ZFS_ZONE_IOP_READ:
+ (void) compute_new_sys_avg(unow, &rd_lat);
+ atomic_inc_uint(&rd_lat.cycle_cnt);
+ atomic_add_64((uint64_t *)&rd_lat.cycle_lat, (int64_t)lat);
+ break;
+ case ZFS_ZONE_IOP_WRITE:
+ (void) compute_new_sys_avg(unow, &wr_lat);
+ atomic_inc_uint(&wr_lat.cycle_cnt);
+ atomic_add_64((uint64_t *)&wr_lat.cycle_lat, (int64_t)lat);
+ break;
+ }
+}
+
+/*
+ * Get the zone IO counts.
+ */
+static uint_t
+calc_zone_cnt(hrtime_t unow, sys_zio_cntr_t *cp)
+{
+ hrtime_t delta;
+ uint_t cnt;
+
+ if ((delta = compute_historical_zone_cnt(unow, cp)) == 0) {
+ /*
+ * No activity in the current cycle, we already have the
+ * historical data so we'll use that.
+ */
+ cnt = cp->zone_avg_cnt;
+ } else {
+ /*
+ * If we're less than half way through the cycle then use
+ * the current count plus half the historical count, otherwise
+ * just use the current count.
+ */
+ if (delta < (zfs_zone_cycle_time / 2))
+ cnt = cp->cycle_cnt + (cp->zone_avg_cnt / 2);
+ else
+ cnt = cp->cycle_cnt;
+ }
+
+ return (cnt);
+}
+
+/*
+ * Get the average read/write latency in usecs for the system.
+ */
+static uint_t
+calc_avg_lat(hrtime_t unow, sys_lat_cycle_t *cp)
+{
+ if (compute_new_sys_avg(unow, cp)) {
+ /*
+ * No activity in the current cycle, we already have the
+ * historical data so we'll use that.
+ */
+ return (cp->sys_avg_lat);
+ } else {
+ /*
+ * We're within a cycle; weight the current activity higher
+ * compared to the historical data and use that.
+ */
+ DTRACE_PROBE3(zfs__zone__calc__wt__avg,
+ uintptr_t, cp->sys_avg_lat,
+ uintptr_t, cp->cycle_lat,
+ uintptr_t, cp->cycle_cnt);
+
+ return ((cp->sys_avg_lat + (cp->cycle_lat * 8)) /
+ (1 + (cp->cycle_cnt * 8)));
+ }
+}
+
+/*
+ * Account for the current IOP on the zone and for the system as a whole.
+ * The latency parameter is in usecs.
+ */
+static void
+add_iop(zone_persist_t *zpd, hrtime_t unow, zfs_zone_iop_type_t op,
+ hrtime_t lat)
+{
+ /* Add op to zone */
+ add_zone_iop(zpd, unow, op);
+
+ /* Track system latency */
+ if (op != ZFS_ZONE_IOP_LOGICAL_WRITE)
+ add_sys_iop(unow, op, lat);
+}
+
+/*
+ * Calculate and return the total number of read ops, write ops and logical
+ * write ops for the given zone. If the zone has issued operations of any type
+ * return a non-zero value, otherwise return 0.
+ */
+static int
+get_zone_io_cnt(hrtime_t unow, zone_zfs_io_t *zpd, uint_t *rops, uint_t *wops,
+ uint_t *lwops)
+{
+ ASSERT3P(zpd, !=, NULL);
+
+ *rops = calc_zone_cnt(unow, &zpd->zpers_rd_ops);
+ *wops = calc_zone_cnt(unow, &zpd->zpers_wr_ops);
+ *lwops = calc_zone_cnt(unow, &zpd->zpers_lwr_ops);
+
+ DTRACE_PROBE4(zfs__zone__io__cnt, uintptr_t, zpd,
+ uintptr_t, *rops, uintptr_t, *wops, uintptr_t, *lwops);
+
+ return (*rops | *wops | *lwops);
+}
+
+/*
+ * Get the average read/write latency in usecs for the system.
+ */
+static void
+get_sys_avg_lat(hrtime_t unow, uint_t *rlat, uint_t *wlat)
+{
+ *rlat = calc_avg_lat(unow, &rd_lat);
+ *wlat = calc_avg_lat(unow, &wr_lat);
+
+ /*
+ * In an attempt to improve the accuracy of the throttling algorithm,
+ * assume that IO operations can't have zero latency. Instead, assume
+ * a reasonable lower bound for each operation type. If the actual
+ * observed latencies are non-zero, use those latency values instead.
+ */
+ if (*rlat == 0)
+ *rlat = 1000;
+ if (*wlat == 0)
+ *wlat = 1000;
+
+ DTRACE_PROBE2(zfs__zone__sys__avg__lat, uintptr_t, *rlat,
+ uintptr_t, *wlat);
+}
+
+/*
+ * Find disk utilization for each zone and average utilization for all active
+ * zones.
+ */
+static int
+zfs_zone_wait_adjust_calculate_cb(zone_t *zonep, void *arg)
+{
+ zoneio_stats_t *sp = arg;
+ uint_t rops, wops, lwops;
+ zone_persist_t *zpd = &zone_pdata[zonep->zone_id];
+ zone_zfs_io_t *iop = zpd->zpers_zfsp;
+
+ ASSERT3P(iop, !=, NULL);
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ if (zonep->zone_id == GLOBAL_ZONEID ||
+ get_zone_io_cnt(sp->zi_now, iop, &rops, &wops, &lwops) == 0) {
+ mutex_exit(&zpd->zpers_zfs_lock);
+ return (0);
+ }
+
+ iop->zpers_io_util = (rops * sp->zi_avgrlat) + (wops * sp->zi_avgwlat) +
+ (lwops * sp->zi_avgwlat);
+ sp->zi_totutil += iop->zpers_io_util;
+
+ if (iop->zpers_io_util > 0) {
+ sp->zi_active++;
+ sp->zi_totpri += iop->zpers_zfs_io_pri;
+ }
+
+ /*
+ * sdt:::zfs-zone-utilization
+ *
+ * arg0: zone ID
+ * arg1: read operations observed during time window
+ * arg2: physical write operations observed during time window
+ * arg3: logical write ops observed during time window
+ * arg4: calculated utilization given read and write ops
+ * arg5: I/O priority assigned to this zone
+ */
+ DTRACE_PROBE6(zfs__zone__utilization, uint_t, zonep->zone_id,
+ uint_t, rops, uint_t, wops, uint_t, lwops,
+ uint64_t, iop->zpers_io_util, uint16_t, iop->zpers_zfs_io_pri);
+
+ mutex_exit(&zpd->zpers_zfs_lock);
+
+ return (0);
+}
+
+static void
+zfs_zone_delay_inc(zone_zfs_io_t *zpd)
+{
+ ASSERT3P(zpd, !=, NULL);
+
+ if (zpd->zpers_io_delay < zfs_zone_delay_ceiling)
+ zpd->zpers_io_delay += zfs_zone_delay_step;
+}
+
+static void
+zfs_zone_delay_dec(zone_zfs_io_t *zpd)
+{
+ ASSERT3P(zpd, !=, NULL);
+
+ if (zpd->zpers_io_delay > 0)
+ zpd->zpers_io_delay -= zfs_zone_delay_step;
+}
+
+/*
+ * For all zones "far enough" away from the average utilization, increase that
+ * zones delay. Otherwise, reduce its delay.
+ */
+static int
+zfs_zone_wait_adjust_delay_cb(zone_t *zonep, void *arg)
+{
+ zone_persist_t *zpd = &zone_pdata[zonep->zone_id];
+ zone_zfs_io_t *iop = zpd->zpers_zfsp;
+ zoneio_stats_t *sp = arg;
+ uint8_t delay;
+ uint_t fairutil = 0;
+
+ ASSERT3P(iop, !=, NULL);
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ delay = iop->zpers_io_delay;
+ iop->zpers_io_util_above_avg = 0;
+
+ /*
+ * Given the calculated total utilitzation for all zones, calculate the
+ * fair share of I/O for this zone.
+ */
+ if (zfs_zone_priority_enable && sp->zi_totpri > 0) {
+ fairutil = (sp->zi_totutil * iop->zpers_zfs_io_pri) /
+ sp->zi_totpri;
+ } else if (sp->zi_active > 0) {
+ fairutil = sp->zi_totutil / sp->zi_active;
+ }
+
+ /*
+ * Adjust each IO's delay. If the overall delay becomes too high, avoid
+ * increasing beyond the ceiling value.
+ */
+ if (iop->zpers_io_util > fairutil && sp->zi_overutil) {
+ iop->zpers_io_util_above_avg = 1;
+
+ if (sp->zi_active > 1)
+ zfs_zone_delay_inc(iop);
+ } else if (iop->zpers_io_util < fairutil || sp->zi_underutil ||
+ sp->zi_active <= 1) {
+ zfs_zone_delay_dec(iop);
+ }
+
+ /*
+ * sdt:::zfs-zone-throttle
+ *
+ * arg0: zone ID
+ * arg1: old delay for this zone
+ * arg2: new delay for this zone
+ * arg3: calculated fair I/O utilization
+ * arg4: actual I/O utilization
+ */
+ DTRACE_PROBE5(zfs__zone__throttle, uintptr_t, zonep->zone_id,
+ uintptr_t, delay, uintptr_t, iop->zpers_io_delay,
+ uintptr_t, fairutil, uintptr_t, iop->zpers_io_util);
+
+ mutex_exit(&zpd->zpers_zfs_lock);
+
+ return (0);
+}
+
+/*
+ * Examine the utilization between different zones, and adjust the delay for
+ * each zone appropriately.
+ */
+static void
+zfs_zone_wait_adjust(hrtime_t unow, hrtime_t last_checked)
+{
+ zoneio_stats_t stats;
+ hrtime_t laggard_udelta = 0;
+
+ (void) bzero(&stats, sizeof (stats));
+
+ stats.zi_now = unow;
+ get_sys_avg_lat(unow, &stats.zi_avgrlat, &stats.zi_avgwlat);
+
+ if (stats.zi_avgrlat > stats.zi_avgwlat * zfs_zone_rw_lat_limit)
+ stats.zi_avgrlat = stats.zi_avgwlat * zfs_zone_rw_lat_limit;
+ else if (stats.zi_avgrlat * zfs_zone_rw_lat_limit < stats.zi_avgwlat)
+ stats.zi_avgwlat = stats.zi_avgrlat * zfs_zone_rw_lat_limit;
+
+ if (zone_walk(zfs_zone_wait_adjust_calculate_cb, &stats) != 0)
+ return;
+
+ /*
+ * Calculate disk utilization for the most recent period.
+ */
+ if (zfs_disk_last_rtime == 0 || unow - last_checked <= 0) {
+ stats.zi_diskutil = 0;
+ } else {
+ stats.zi_diskutil =
+ ((zfs_disk_rtime - zfs_disk_last_rtime) * 100) /
+ ((unow - last_checked) * 1000);
+ }
+ zfs_disk_last_rtime = zfs_disk_rtime;
+
+ if (unow > zfs_disk_last_laggard)
+ laggard_udelta = unow - zfs_disk_last_laggard;
+
+ /*
+ * To minimize porpoising, we have three separate states for our
+ * assessment of I/O performance: overutilized, underutilized, and
+ * neither overutilized nor underutilized. We will increment the
+ * throttle if a zone is using more than its fair share _and_ I/O
+ * is overutilized; we will decrement the throttle if a zone is using
+ * less than its fair share _or_ I/O is underutilized.
+ */
+ stats.zi_underutil = stats.zi_diskutil < zfs_zone_underutil_threshold ||
+ laggard_udelta > zfs_zone_laggard_ancient;
+
+ stats.zi_overutil = stats.zi_diskutil > zfs_zone_util_threshold &&
+ laggard_udelta < zfs_zone_laggard_recent;
+
+ /*
+ * sdt:::zfs-zone-stats
+ *
+ * Statistics observed over the last period:
+ *
+ * arg0: average system read latency
+ * arg1: average system write latency
+ * arg2: number of active zones
+ * arg3: total I/O 'utilization' for all zones
+ * arg4: total I/O priority of all active zones
+ * arg5: calculated disk utilization
+ */
+ DTRACE_PROBE6(zfs__zone__stats, uintptr_t, stats.zi_avgrlat,
+ uintptr_t, stats.zi_avgwlat, uintptr_t, stats.zi_active,
+ uintptr_t, stats.zi_totutil, uintptr_t, stats.zi_totpri,
+ uintptr_t, stats.zi_diskutil);
+
+ (void) zone_walk(zfs_zone_wait_adjust_delay_cb, &stats);
+}
+
+/*
+ * Callback used to calculate a zone's IO schedule priority.
+ *
+ * We scan the zones looking for ones with ops in the queue. Out of those,
+ * we pick the one that calculates to the highest schedule priority.
+ */
+static int
+get_sched_pri_cb(zone_t *zonep, void *arg)
+{
+ int pri;
+ uint_t cnt;
+ zone_q_bump_t *qbp = arg;
+ zio_priority_t p = qbp->zq_queue;
+ zone_persist_t *zpd = &zone_pdata[zonep->zone_id];
+ zone_zfs_io_t *iop;
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ iop = zpd->zpers_zfsp;
+ if (iop == NULL) {
+ mutex_exit(&zpd->zpers_zfs_lock);
+ return (0);
+ }
+
+ cnt = iop->zpers_zfs_queued[p];
+ if (cnt == 0) {
+ iop->zpers_zfs_weight = 0;
+ mutex_exit(&zpd->zpers_zfs_lock);
+ return (0);
+ }
+
+ /*
+ * On each pass, increment the zone's weight. We use this as input
+ * to the calculation to prevent starvation. The value is reset
+ * each time we issue an IO for this zone so zones which haven't
+ * done any IO over several iterations will see their weight max
+ * out.
+ */
+ if (iop->zpers_zfs_weight < SCHED_WEIGHT_MAX)
+ iop->zpers_zfs_weight++;
+
+ /*
+ * This zone's IO priority is the inverse of the number of IOs
+ * the zone has enqueued * zone's configured priority * weight.
+ * The queue depth has already been scaled by 10 to avoid problems
+ * with int rounding.
+ *
+ * This means that zones with fewer IOs in the queue will get
+ * preference unless other zone's assigned priority pulls them
+ * ahead. The weight is factored in to help ensure that zones
+ * which haven't done IO in a while aren't getting starved.
+ */
+ pri = (qbp->zq_qdepth / cnt) *
+ iop->zpers_zfs_io_pri * iop->zpers_zfs_weight;
+
+ /*
+ * If this zone has a higher priority than what we found so far,
+ * it becomes the new leading contender.
+ */
+ if (pri > qbp->zq_priority) {
+ qbp->zq_zoneid = zonep->zone_id;
+ qbp->zq_priority = pri;
+ qbp->zq_wt = iop->zpers_zfs_weight;
+ }
+ mutex_exit(&zpd->zpers_zfs_lock);
+ return (0);
+}
+
+/*
+ * See if we need to bump a zone's zio to the head of the queue. This is only
+ * done on the two synchronous I/O queues (see the block comment on the
+ * zfs_zone_schedule function). We get the correct vdev_queue_class_t and
+ * queue depth from our caller.
+ *
+ * For single-threaded synchronous processes a zone cannot get more than
+ * 1 op into the queue at a time unless the zone is running multiple processes
+ * in parallel. This can cause an imbalance in performance if there are zones
+ * with many parallel processes (and ops in the queue) vs. other zones which
+ * are doing simple single-threaded processes, such as interactive tasks in the
+ * shell. These zones can get backed up behind a deep queue and their IO
+ * performance will appear to be very poor as a result. This can make the
+ * zone work badly for interactive behavior.
+ *
+ * The scheduling algorithm kicks in once we start to get a deeper queue.
+ * Once that occurs, we look at all of the zones to see which one calculates
+ * to the highest priority. We bump that zone's first zio to the head of the
+ * queue.
+ *
+ * We use a counter on the zone so that we can quickly find how many ops each
+ * zone has in the queue without having to search the entire queue itself.
+ * This scales better since the number of zones is expected to be on the
+ * order of 10-100 whereas the queue depth can be in the range of 50-2000.
+ * In addition, since the zio's in the queue only have the zoneid, we would
+ * have to look up the zone for each zio enqueued and that means the overhead
+ * for scanning the queue each time would be much higher.
+ *
+ * In all cases, we fall back to simply pulling the next op off the queue
+ * if something should go wrong.
+ */
+static zio_t *
+get_next_zio(vdev_queue_class_t *vqc, int qdepth, zio_priority_t p,
+ avl_tree_t *tree)
+{
+ zone_q_bump_t qbump;
+ zio_t *zp = NULL, *zphead;
+ int cnt = 0;
+
+ /* To avoid problems with int rounding, scale the queue depth by 10 */
+ qbump.zq_qdepth = qdepth * 10;
+ qbump.zq_priority = 0;
+ qbump.zq_zoneid = 0;
+ qbump.zq_queue = p;
+ (void) zone_walk(get_sched_pri_cb, &qbump);
+
+ zphead = avl_first(tree);
+
+ /* Check if the scheduler didn't pick a zone for some reason!? */
+ if (qbump.zq_zoneid != 0) {
+ for (zp = avl_first(tree); zp != NULL;
+ zp = avl_walk(tree, zp, AVL_AFTER)) {
+ if (zp->io_zoneid == qbump.zq_zoneid)
+ break;
+ cnt++;
+ }
+ }
+
+ if (zp == NULL) {
+ zp = zphead;
+ } else if (zp != zphead) {
+ /*
+ * Only fire the probe if we actually picked a different zio
+ * than the one already at the head of the queue.
+ */
+ DTRACE_PROBE4(zfs__zone__sched__bump, uint_t, zp->io_zoneid,
+ uint_t, cnt, int, qbump.zq_priority, int, qbump.zq_wt);
+ }
+
+ return (zp);
+}
+
+/*
+ * Add our zone ID to the zio so we can keep track of which zones are doing
+ * what, even when the current thread processing the zio is not associated
+ * with the zone (e.g. the kernel taskq which pushes out TX groups).
+ */
+void
+zfs_zone_zio_init(zio_t *zp)
+{
+ zone_t *zonep = curzone;
+
+ zp->io_zoneid = zonep->zone_id;
+}
+
+/*
+ * Track and throttle IO operations per zone. Called from:
+ * - dmu_tx_count_write for (logical) write ops (both dataset and zvol writes
+ * go through this path)
+ * - arc_read for read ops that miss the ARC (both dataset and zvol)
+ * For each operation, increment that zone's counter based on the type of
+ * operation, then delay the operation, if necessary.
+ *
+ * There are three basic ways that we can see write ops:
+ * 1) An application does write syscalls. Those ops go into a TXG which
+ * we'll count here. Sometime later a kernel taskq thread (we'll see the
+ * vdev IO as zone 0) will perform some number of physical writes to commit
+ * the TXG to disk. Those writes are not associated with the zone which
+ * made the write syscalls and the number of operations is not correlated
+ * between the taskq and the zone. We only see logical writes in this
+ * function, we see the physcial writes in the zfs_zone_zio_start and
+ * zfs_zone_zio_done functions.
+ * 2) An application opens a file with O_SYNC. Each write will result in
+ * an operation which we'll see here plus a low-level vdev write from
+ * that zone.
+ * 3) An application does write syscalls followed by an fsync(). We'll
+ * count the writes going into a TXG here. We'll also see some number
+ * (usually much smaller, maybe only 1) of low-level vdev writes from this
+ * zone when the fsync is performed, plus some other low-level vdev writes
+ * from the taskq in zone 0 (are these metadata writes?).
+ *
+ * 4) In addition to the above, there are misc. system-level writes, such as
+ * writing out dirty pages to swap, or sync(2) calls, which will be handled
+ * by the global zone and which we count but don't generally worry about.
+ *
+ * Because of the above, we can see writes twice; first because this function
+ * is always called by a zone thread for logical writes, but then we also will
+ * count the physical writes that are performed at a low level via
+ * zfs_zone_zio_start. Without this, it can look like a non-global zone never
+ * writes (case 1). Depending on when the TXG is synced, the counts may be in
+ * the same sample bucket or in a different one.
+ *
+ * Tracking read operations is simpler due to their synchronous semantics. The
+ * zfs_read function -- called as a result of a read(2) syscall -- will always
+ * retrieve the data to be read through arc_read and we only come into this
+ * function when we have an arc miss.
+ */
+void
+zfs_zone_io_throttle(zfs_zone_iop_type_t type)
+{
+ zoneid_t zid = curzone->zone_id;
+ zone_persist_t *zpd = &zone_pdata[zid];
+ zone_zfs_io_t *iop;
+ hrtime_t unow;
+ uint16_t wait;
+
+ unow = GET_USEC_TIME;
+
+ /*
+ * Only bump the counter for logical writes here. The counters for
+ * tracking physical IO operations are handled in zfs_zone_zio_done.
+ */
+ if (type == ZFS_ZONE_IOP_LOGICAL_WRITE) {
+ add_iop(zpd, unow, type, 0);
+ }
+
+ if (!zfs_zone_delay_enable)
+ return;
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ iop = zpd->zpers_zfsp;
+ if (iop == NULL) {
+ mutex_exit(&zpd->zpers_zfs_lock);
+ return;
+ }
+
+ /*
+ * If the zone's I/O priority is set to zero, don't throttle that zone's
+ * operations at all.
+ */
+ if (iop->zpers_zfs_io_pri == 0) {
+ mutex_exit(&zpd->zpers_zfs_lock);
+ return;
+ }
+
+ /* Handle periodically updating the per-zone I/O parameters */
+ if ((unow - zfs_zone_last_checked) > zfs_zone_adjust_time) {
+ hrtime_t last_checked;
+ boolean_t do_update = B_FALSE;
+
+ /* Recheck under mutex */
+ mutex_enter(&zfs_last_check_lock);
+ last_checked = zfs_zone_last_checked;
+ if ((unow - last_checked) > zfs_zone_adjust_time) {
+ zfs_zone_last_checked = unow;
+ do_update = B_TRUE;
+ }
+ mutex_exit(&zfs_last_check_lock);
+
+ if (do_update) {
+ mutex_exit(&zpd->zpers_zfs_lock);
+
+ zfs_zone_wait_adjust(unow, last_checked);
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ iop = zpd->zpers_zfsp;
+ if (iop == NULL) {
+ mutex_exit(&zpd->zpers_zfs_lock);
+ return;
+ }
+ }
+ }
+
+ wait = iop->zpers_io_delay;
+ mutex_exit(&zpd->zpers_zfs_lock);
+
+ if (wait > 0) {
+ /*
+ * If this is a write and we're doing above normal TXG
+ * syncing, then throttle for longer than normal.
+ */
+ if (type == ZFS_ZONE_IOP_LOGICAL_WRITE &&
+ (txg_cnt > 1 || txg_sync_rate > 1))
+ wait *= zfs_zone_txg_throttle_scale;
+
+ /*
+ * sdt:::zfs-zone-wait
+ *
+ * arg0: zone ID
+ * arg1: type of IO operation
+ * arg2: time to delay (in us)
+ */
+ DTRACE_PROBE3(zfs__zone__wait, uintptr_t, zid,
+ uintptr_t, type, uintptr_t, wait);
+
+ drv_usecwait(wait);
+
+ if (curzone->zone_vfs_stats != NULL) {
+ atomic_inc_64(&curzone->zone_vfs_stats->
+ zv_delay_cnt.value.ui64);
+ atomic_add_64(&curzone->zone_vfs_stats->
+ zv_delay_time.value.ui64, wait);
+ }
+ }
+}
+
+/*
+ * XXX Ignore the pool pointer parameter for now.
+ *
+ * Keep track to see if the TXG sync rate is running above the expected rate.
+ * If so, this implies that we are filling TXG's at a high rate due to a heavy
+ * write workload. We use this as input into the zone throttle.
+ *
+ * This function is called every 5 seconds (zfs_txg_timeout) under a normal
+ * write load. In this case, the sync rate is going to be 1. When there
+ * is a heavy write load, TXG's fill up fast and the sync thread will write
+ * the TXG more frequently (perhaps once a second). In this case the rate
+ * will be > 1. The sync rate is a lagging indicator since it can be up
+ * to 5 seconds old. We use the txg_cnt to keep track of the rate in the
+ * current 5 second interval and txg_sync_rate to keep track of the previous
+ * 5 second interval. In that way we don't have a period (1 or more seconds)
+ * where the txg_cnt == 0 and we cut back on throttling even though the rate
+ * is still high.
+ */
+/*ARGSUSED*/
+void
+zfs_zone_report_txg_sync(void *dp)
+{
+ uint_t now;
+
+ txg_cnt++;
+ now = (uint_t)(gethrtime() / NANOSEC);
+ if ((now - txg_last_check) >= zfs_txg_timeout) {
+ txg_sync_rate = txg_cnt / 2;
+ txg_cnt = 0;
+ txg_last_check = now;
+ }
+}
+
+hrtime_t
+zfs_zone_txg_delay()
+{
+ zone_persist_t *zpd = &zone_pdata[curzone->zone_id];
+ zone_zfs_io_t *iop;
+ uint8_t above;
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ iop = zpd->zpers_zfsp;
+ if (iop == NULL) {
+ mutex_exit(&zpd->zpers_zfs_lock);
+ return (0);
+ }
+
+ above = iop->zpers_io_util_above_avg;
+ mutex_exit(&zpd->zpers_zfs_lock);
+
+ if (above) {
+ return (zfs_zone_txg_delay_nsec);
+ }
+
+ return (MSEC2NSEC(10));
+}
+
+/*
+ * Called from vdev_disk_io_start when an IO hits the end of the zio pipeline
+ * and is issued.
+ * Keep track of start time for latency calculation in zfs_zone_zio_done.
+ */
+void
+zfs_zone_zio_start(zio_t *zp)
+{
+ zone_persist_t *zpd = &zone_pdata[zp->io_zoneid];
+ zone_zfs_io_t *iop;
+
+ /*
+ * I/Os of type ZIO_TYPE_IOCTL are used to flush the disk cache, not for
+ * an actual I/O operation. Ignore those operations as they relate to
+ * throttling and scheduling.
+ */
+ if (zp->io_type == ZIO_TYPE_IOCTL)
+ return;
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ iop = zpd->zpers_zfsp;
+ if (iop != NULL) {
+ if (zp->io_type == ZIO_TYPE_READ)
+ kstat_runq_enter(&iop->zpers_zfs_rwstats);
+ iop->zpers_zfs_weight = 0;
+ }
+ mutex_exit(&zpd->zpers_zfs_lock);
+
+ mutex_enter(&zfs_disk_lock);
+ zp->io_dispatched = gethrtime();
+
+ if (zfs_disk_rcnt++ != 0)
+ zfs_disk_rtime += (zp->io_dispatched - zfs_disk_rlastupdate);
+ zfs_disk_rlastupdate = zp->io_dispatched;
+ mutex_exit(&zfs_disk_lock);
+}
+
+/*
+ * Called from vdev_disk_io_done when an IO completes.
+ * Increment our counter for zone ops.
+ * Calculate the IO latency avg. for this zone.
+ */
+void
+zfs_zone_zio_done(zio_t *zp)
+{
+ zone_persist_t *zpd;
+ zone_zfs_io_t *iop;
+ hrtime_t now, unow, udelta;
+
+ if (zp->io_type == ZIO_TYPE_IOCTL)
+ return;
+
+ if (zp->io_dispatched == 0)
+ return;
+
+ zpd = &zone_pdata[zp->io_zoneid];
+
+ now = gethrtime();
+ unow = NANO_TO_MICRO(now);
+ udelta = unow - NANO_TO_MICRO(zp->io_dispatched);
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ iop = zpd->zpers_zfsp;
+ if (iop != NULL) {
+ /*
+ * To calculate the wsvc_t average, keep a cumulative sum of
+ * all the wait time before each I/O was dispatched. Since most
+ * writes are asynchronous, only track the wait time for
+ * read I/Os.
+ */
+ if (zp->io_type == ZIO_TYPE_READ) {
+ iop->zpers_zfs_rwstats.reads++;
+ iop->zpers_zfs_rwstats.nread += zp->io_size;
+ iop->zpers_zfs_rd_waittime +=
+ zp->io_dispatched - zp->io_timestamp;
+ kstat_runq_exit(&iop->zpers_zfs_rwstats);
+ } else {
+ iop->zpers_zfs_rwstats.writes++;
+ iop->zpers_zfs_rwstats.nwritten += zp->io_size;
+ }
+ }
+ mutex_exit(&zpd->zpers_zfs_lock);
+
+ mutex_enter(&zfs_disk_lock);
+ zfs_disk_rcnt--;
+ zfs_disk_rtime += (now - zfs_disk_rlastupdate);
+ zfs_disk_rlastupdate = now;
+
+ if (udelta > zfs_zone_laggard_threshold)
+ zfs_disk_last_laggard = unow;
+
+ mutex_exit(&zfs_disk_lock);
+
+ if (zfs_zone_delay_enable) {
+ add_iop(zpd, unow, zp->io_type == ZIO_TYPE_READ ?
+ ZFS_ZONE_IOP_READ : ZFS_ZONE_IOP_WRITE, udelta);
+ }
+
+ /*
+ * sdt:::zfs-zone-latency
+ *
+ * arg0: zone ID
+ * arg1: type of I/O operation
+ * arg2: I/O latency (in us)
+ */
+ DTRACE_PROBE3(zfs__zone__latency, uintptr_t, zp->io_zoneid,
+ uintptr_t, zp->io_type, uintptr_t, udelta);
+}
+
+void
+zfs_zone_zio_dequeue(zio_t *zp)
+{
+ zio_priority_t p;
+ zone_persist_t *zpd = &zone_pdata[zp->io_zoneid];
+ zone_zfs_io_t *iop;
+
+ p = zp->io_priority;
+ if (p != ZIO_PRIORITY_SYNC_READ && p != ZIO_PRIORITY_SYNC_WRITE)
+ return;
+
+ /* We depend on p being defined as either 0 or 1 */
+ ASSERT(p < 2);
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ iop = zpd->zpers_zfsp;
+ if (iop != NULL) {
+ ASSERT(iop->zpers_zfs_queued[p] > 0);
+ if (iop->zpers_zfs_queued[p] == 0) {
+ cmn_err(CE_WARN, "zfs_zone_zio_dequeue: count==0");
+ } else {
+ iop->zpers_zfs_queued[p]--;
+ }
+ }
+ mutex_exit(&zpd->zpers_zfs_lock);
+}
+
+void
+zfs_zone_zio_enqueue(zio_t *zp)
+{
+ zio_priority_t p;
+ zone_persist_t *zpd = &zone_pdata[zp->io_zoneid];
+ zone_zfs_io_t *iop;
+
+ p = zp->io_priority;
+ if (p != ZIO_PRIORITY_SYNC_READ && p != ZIO_PRIORITY_SYNC_WRITE)
+ return;
+
+ /* We depend on p being defined as either 0 or 1 */
+ ASSERT(p < 2);
+
+ mutex_enter(&zpd->zpers_zfs_lock);
+ iop = zpd->zpers_zfsp;
+ if (iop != NULL) {
+ iop->zpers_zfs_queued[p]++;
+ }
+ mutex_exit(&zpd->zpers_zfs_lock);
+}
+
+/*
+ * Called from vdev_queue_io_to_issue. That function is where zio's are listed
+ * in FIFO order on one of the sync queues, then pulled off (by
+ * vdev_queue_io_remove) and issued. We potentially do zone-based scheduling
+ * here to find a zone's zio deeper in the sync queue and issue that instead
+ * of simply doing FIFO.
+ *
+ * We only do zone-based zio scheduling for the two synchronous I/O queues
+ * (read & write). These queues are normally serviced in FIFO order but we
+ * may decide to move a zone's zio to the head of the line. A typical I/O
+ * load will be mostly synchronous reads and some asynchronous writes (which
+ * are scheduled differently due to transaction groups). There will also be
+ * some synchronous writes for those apps which want to ensure their data is on
+ * disk. We want to make sure that a zone with a single-threaded app (e.g. the
+ * shell) that is doing synchronous I/O (typically reads) isn't penalized by
+ * other zones which are doing lots of synchronous I/O because they have many
+ * running threads.
+ *
+ * The vq->vq_lock mutex is held when we're executing this function so we
+ * can safely access the "last zone" variable on the queue.
+ */
+zio_t *
+zfs_zone_schedule(vdev_queue_t *vq, zio_priority_t p, avl_index_t idx,
+ avl_tree_t *tree)
+{
+ vdev_queue_class_t *vqc = &vq->vq_class[p];
+ uint_t cnt;
+ zoneid_t last_zone;
+ zio_t *zio;
+
+ ASSERT(MUTEX_HELD(&vq->vq_lock));
+
+ /* Don't change the order on the LBA ordered queues. */
+ if (p != ZIO_PRIORITY_SYNC_READ && p != ZIO_PRIORITY_SYNC_WRITE)
+ return (avl_nearest(tree, idx, AVL_AFTER));
+
+ /* We depend on p being defined as either 0 or 1 */
+ ASSERT(p < 2);
+
+ cnt = avl_numnodes(tree);
+ last_zone = vq->vq_last_zone_id;
+
+ /*
+ * If there are only a few zios in the queue then just issue the head.
+ * If there are more than a few zios already queued up, then use
+ * scheduling to get the next zio.
+ */
+ if (!zfs_zone_schedule_enable || cnt < zfs_zone_schedule_thresh)
+ zio = avl_nearest(tree, idx, AVL_AFTER);
+ else
+ zio = get_next_zio(vqc, cnt, p, tree);
+
+ vq->vq_last_zone_id = zio->io_zoneid;
+
+ /*
+ * Probe with 4 args; the number of IOs in the queue, the zone that
+ * was last scheduled off this queue, the zone that was associated
+ * with the next IO that is scheduled, and which queue (priority).
+ */
+ DTRACE_PROBE4(zfs__zone__sched, uint_t, cnt, uint_t, last_zone,
+ uint_t, zio->io_zoneid, uint_t, p);
+
+ return (zio);
+}
+
+#endif
diff --git a/usr/src/uts/common/fs/zfs/zil.c b/usr/src/uts/common/fs/zfs/zil.c
index 450ccb94e5..7a15838338 100644
--- a/usr/src/uts/common/fs/zfs/zil.c
+++ b/usr/src/uts/common/fs/zfs/zil.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Joyent, Inc. All rights reserved.
* Copyright (c) 2011, 2018 by Delphix. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
*/
@@ -106,8 +107,23 @@ boolean_t zil_nocacheflush = B_FALSE;
* Limit SLOG write size per commit executed with synchronous priority.
* Any writes above that will be executed with lower (asynchronous) priority
* to limit potential SLOG device abuse by single active ZIL writer.
+ *
+ * The default upstream value for zil_slog_bulk is:
+ * uint64_t zil_slog_bulk = 768 * 1024;
+ * For SmartOS, we default to using a high value to essentially disable this
+ * behavior.
+ *
+ * Because the default value of this tunable forces some zil_commit writes down
+ * to io_priority ZIO_PRIORITY_ASYNC_WRITE, those zio's would be in the same
+ * zio pipeline queue as all of the async spa_sync zio's. This can lead to
+ * serious latency problems for the user-level application code because it is
+ * blocked on completion of the zil_commit. We see this when a spa_sync zio is
+ * running slow (e.g. when metaslab loading takes a long time in the
+ * zio_dva_allocate pipeline stage), thus delaying all zio's backed up in the
+ * ZIO_PRIORITY_ASYNC_WRITE queue. For SmartOS, we choose to keep all
+ * zil_commmit zio's at ZIO_PRIORITY_SYNC_WRITE.
*/
-uint64_t zil_slog_bulk = 768 * 1024;
+uint64_t zil_slog_bulk = 0x100000000ULL;
static kmem_cache_t *zil_lwb_cache;
static kmem_cache_t *zil_zcw_cache;
@@ -3079,13 +3095,20 @@ zil_close(zilog_t *zilog)
txg = MAX(zilog->zl_dirty_max_txg, lwb->lwb_max_txg);
mutex_exit(&zilog->zl_lock);
- /*
- * We need to use txg_wait_synced() to wait long enough for the
- * ZIL to be clean, and to wait for all pending lwbs to be
- * written out.
- */
- if (txg != 0)
+ if (zilog_is_dirty(zilog)) {
+ /*
+ * If we're dirty, always wait for the current transaction --
+ * our lwb_max_txg may be in the past.
+ */
+ txg_wait_synced(zilog->zl_dmu_pool, 0);
+ } else if (txg != 0) {
+ /*
+ * We need to use txg_wait_synced() to wait long enough for the
+ * ZIL to be clean, and to wait for all pending lwbs to be
+ * written out.
+ */
txg_wait_synced(zilog->zl_dmu_pool, txg);
+ }
if (zilog_is_dirty(zilog))
zfs_dbgmsg("zil (%p) is dirty, txg %llu", zilog, txg);
diff --git a/usr/src/uts/common/fs/zfs/zio.c b/usr/src/uts/common/fs/zfs/zio.c
index f8a98f73f3..b32dffd79c 100644
--- a/usr/src/uts/common/fs/zfs/zio.c
+++ b/usr/src/uts/common/fs/zfs/zio.c
@@ -22,6 +22,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2018 by Delphix. All rights reserved.
* Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
* Copyright (c) 2017, Intel Corporation.
* Copyright 2020 Joyent, Inc.
@@ -43,6 +44,7 @@
#include <sys/ddt.h>
#include <sys/blkptr.h>
#include <sys/zfeature.h>
+#include <sys/zfs_zone.h>
#include <sys/time.h>
#include <sys/dsl_scan.h>
#include <sys/metaslab_impl.h>
@@ -765,6 +767,7 @@ zio_create(zio_t *pio, spa_t *spa, uint64_t txg, const blkptr_t *bp,
zio->io_bookmark = *zb;
if (pio != NULL) {
+ zio->io_zoneid = pio->io_zoneid;
if (zio->io_metaslab_class == NULL)
zio->io_metaslab_class = pio->io_metaslab_class;
if (zio->io_logical == NULL)
@@ -772,6 +775,8 @@ zio_create(zio_t *pio, spa_t *spa, uint64_t txg, const blkptr_t *bp,
if (zio->io_child_type == ZIO_CHILD_GANG)
zio->io_gang_leader = pio->io_gang_leader;
zio_add_child(pio, zio);
+ } else {
+ zfs_zone_zio_init(zio);
}
return (zio);
@@ -4317,6 +4322,24 @@ zio_done(zio_t *zio)
}
}
+ /*
+ * When we have an error on a slog vdev, we must ensure that the
+ * zio is not suspended. Suspending the zio will cause dataset deletion
+ * or an attempt to remove the slog to hang. In both cases, the code
+ * might be trying to clean up the zil blocks on the slog, but because
+ * the slog is dead, the suspended zio causes this to hang indefinitely.
+ * The system properly switches over to using zils on regular storage
+ * when the slog dies.
+ *
+ * This is a reasonable point in the stack to detect that the vdev is
+ * a slog. The 'no_suspend' flag will propagate up to the logical zio
+ * via zio_notify_parent.
+ */
+ if (zio->io_error && vd != NULL && vd->vdev_islog &&
+ !vdev_accessible(vd, zio)) {
+ zio->io_reexecute |= ZIO_REEXECUTE_NO_SUSPEND;
+ }
+
if (zio->io_error && zio == lio) {
/*
* Determine whether zio should be reexecuted. This will
@@ -4361,7 +4384,7 @@ zio_done(zio_t *zio)
*/
zio_inherit_child_errors(zio, ZIO_CHILD_LOGICAL);
- if ((zio->io_error || zio->io_reexecute) &&
+ if ((zio->io_error || ZIO_SHOULD_REEXECUTE(zio)) &&
IO_IS_ALLOCATING(zio) && zio->io_gang_leader == zio &&
!(zio->io_flags & (ZIO_FLAG_IO_REWRITE | ZIO_FLAG_NOPWRITE)))
zio_dva_unallocate(zio, zio->io_gang_tree, bp);
@@ -4375,7 +4398,7 @@ zio_done(zio_t *zio)
(zio->io_reexecute & ZIO_REEXECUTE_SUSPEND))
zio->io_reexecute = 0;
- if (zio->io_reexecute) {
+ if (ZIO_SHOULD_REEXECUTE(zio)) {
/*
* This is a logical I/O that wants to reexecute.
*
@@ -4446,7 +4469,7 @@ zio_done(zio_t *zio)
}
ASSERT(zio->io_child_count == 0);
- ASSERT(zio->io_reexecute == 0);
+ ASSERT(!ZIO_SHOULD_REEXECUTE(zio));
ASSERT(zio->io_error == 0 || (zio->io_flags & ZIO_FLAG_CANFAIL));
/*
diff --git a/usr/src/uts/common/fs/zfs/zvol.c b/usr/src/uts/common/fs/zfs/zvol.c
index 2bb311d28d..3d2a42aa46 100644
--- a/usr/src/uts/common/fs/zfs/zvol.c
+++ b/usr/src/uts/common/fs/zfs/zvol.c
@@ -82,6 +82,7 @@
#include <sys/zvol.h>
#include <sys/dumphdr.h>
#include <sys/zil_impl.h>
+#include <sys/sdt.h>
#include <sys/dbuf.h>
#include <sys/dmu_tx.h>
#include <sys/zfeature.h>
@@ -140,6 +141,11 @@ typedef struct zvol_state {
#define ZVOL_EXCL 0x4
#define ZVOL_WCE 0x8
+#define VOP_LATENCY_10MS 10000000
+#define VOP_LATENCY_100MS 100000000
+#define VOP_LATENCY_1S 1000000000
+#define VOP_LATENCY_10S 10000000000
+
/*
* zvol maximum transfer in one DMU tx.
*/
@@ -1342,6 +1348,9 @@ zvol_read(dev_t dev, uio_t *uio, cred_t *cr)
zvol_state_t *zv;
uint64_t volsize;
int error = 0;
+ zone_t *zonep = curzone;
+ uint64_t tot_bytes;
+ hrtime_t start, lat;
zv = zfsdev_get_soft_state(minor, ZSST_ZVOL);
if (zv == NULL)
@@ -1360,6 +1369,14 @@ zvol_read(dev_t dev, uio_t *uio, cred_t *cr)
smt_begin_unsafe();
+ DTRACE_PROBE3(zvol__uio__start, dev_t, dev, uio_t *, uio, int, 0);
+
+ mutex_enter(&zonep->zone_vfs_lock);
+ kstat_runq_enter(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+ start = gethrtime();
+ tot_bytes = 0;
+
locked_range_t *lr = rangelock_enter(&zv->zv_rangelock,
uio->uio_loffset, uio->uio_resid, RL_READER);
while (uio->uio_resid > 0 && uio->uio_loffset < volsize) {
@@ -1369,6 +1386,7 @@ zvol_read(dev_t dev, uio_t *uio, cred_t *cr)
if (bytes > volsize - uio->uio_loffset)
bytes = volsize - uio->uio_loffset;
+ tot_bytes += bytes;
error = dmu_read_uio(zv->zv_objset, ZVOL_OBJ, uio, bytes);
if (error) {
/* convert checksum errors into IO errors */
@@ -1379,6 +1397,38 @@ zvol_read(dev_t dev, uio_t *uio, cred_t *cr)
}
rangelock_exit(lr);
+ mutex_enter(&zonep->zone_vfs_lock);
+ zonep->zone_vfs_rwstats.reads++;
+ zonep->zone_vfs_rwstats.nread += tot_bytes;
+ kstat_runq_exit(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+
+ lat = gethrtime() - start;
+
+ if (lat >= VOP_LATENCY_10MS) {
+ zone_vfs_kstat_t *zvp;
+
+ zvp = zonep->zone_vfs_stats;
+ if (lat < VOP_LATENCY_100MS) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_1S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_10S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ } else {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_10s_ops.value.ui64);
+ }
+ }
+
+ DTRACE_PROBE4(zvol__uio__done, dev_t, dev, uio_t *, uio, int, 0, int,
+ error);
+
smt_end_unsafe();
return (error);
@@ -1393,6 +1443,9 @@ zvol_write(dev_t dev, uio_t *uio, cred_t *cr)
uint64_t volsize;
int error = 0;
boolean_t sync;
+ zone_t *zonep = curzone;
+ uint64_t tot_bytes;
+ hrtime_t start, lat;
zv = zfsdev_get_soft_state(minor, ZSST_ZVOL);
if (zv == NULL)
@@ -1411,6 +1464,19 @@ zvol_write(dev_t dev, uio_t *uio, cred_t *cr)
smt_begin_unsafe();
+ DTRACE_PROBE3(zvol__uio__start, dev_t, dev, uio_t *, uio, int, 1);
+
+ /*
+ * For the purposes of VFS kstat consumers, the "waitq" calculation is
+ * repurposed as the active queue for zvol write operations. There's no
+ * actual wait queue for zvol operations.
+ */
+ mutex_enter(&zonep->zone_vfs_lock);
+ kstat_waitq_enter(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+ start = gethrtime();
+ tot_bytes = 0;
+
sync = !(zv->zv_flags & ZVOL_WCE) ||
(zv->zv_objset->os_sync == ZFS_SYNC_ALWAYS);
@@ -1424,6 +1490,7 @@ zvol_write(dev_t dev, uio_t *uio, cred_t *cr)
if (bytes > volsize - off) /* don't write past the end */
bytes = volsize - off;
+ tot_bytes += bytes;
dmu_tx_hold_write_by_dnode(tx, zv->zv_dn, off, bytes);
error = dmu_tx_assign(tx, TXG_WAIT);
if (error) {
@@ -1443,8 +1510,40 @@ zvol_write(dev_t dev, uio_t *uio, cred_t *cr)
if (sync)
zil_commit(zv->zv_zilog, ZVOL_OBJ);
+ DTRACE_PROBE4(zvol__uio__done, dev_t, dev, uio_t *, uio, int, 1, int,
+ error);
+
smt_end_unsafe();
+ mutex_enter(&zonep->zone_vfs_lock);
+ zonep->zone_vfs_rwstats.writes++;
+ zonep->zone_vfs_rwstats.nwritten += tot_bytes;
+ kstat_waitq_exit(&zonep->zone_vfs_rwstats);
+ mutex_exit(&zonep->zone_vfs_lock);
+
+ lat = gethrtime() - start;
+
+ if (lat >= VOP_LATENCY_10MS) {
+ zone_vfs_kstat_t *zvp;
+
+ zvp = zonep->zone_vfs_stats;
+ if (lat < VOP_LATENCY_100MS) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_1S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ } else if (lat < VOP_LATENCY_10S) {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ } else {
+ atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+ atomic_inc_64(&zvp->zv_10s_ops.value.ui64);
+ }
+ }
+
return (error);
}
diff --git a/usr/src/uts/common/inet/bpf.h b/usr/src/uts/common/inet/bpf.h
new file mode 100644
index 0000000000..e3eac799e5
--- /dev/null
+++ b/usr/src/uts/common/inet/bpf.h
@@ -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 2016 Joyent, Inc.
+ */
+
+#ifndef _INET_BPF_H
+#define _INET_BPF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef _KERNEL
+
+#include <sys/types.h>
+
+/*
+ * Clone bpf_insn definition so that consumers don't need net/bpf.h to reason
+ * about struct sizing.
+ */
+typedef struct ip_bpf_insn {
+ uint16_t code;
+ uint8_t jt;
+ uint8_t jf;
+ uint32_t k;
+} ip_bpf_insn_t;
+
+extern uint32_t ip_bpf_filter(ip_bpf_insn_t *, uchar_t *, uint_t, uint_t);
+extern boolean_t ip_bpf_validate(ip_bpf_insn_t *, uint_t);
+
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _INET_BPF_H */
diff --git a/usr/src/uts/common/io/bpf/bpf_filter.c b/usr/src/uts/common/inet/bpf_filter.c
index db5b224a5e..5a9ba38da6 100644
--- a/usr/src/uts/common/io/bpf/bpf_filter.c
+++ b/usr/src/uts/common/inet/bpf_filter.c
@@ -38,6 +38,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/param.h>
@@ -45,11 +46,12 @@
#include <sys/stream.h>
#include <sys/byteorder.h>
#include <sys/sdt.h>
+#include <inet/bpf.h>
+#include <net/bpf.h>
#define EXTRACT_SHORT(p) BE_IN16(p)
#define EXTRACT_LONG(p) BE_IN32(p)
-#ifdef _KERNEL
#define M_LEN(_m) ((_m)->b_wptr - (_m)->b_rptr)
#define mtod(_a, _t) ((_t)((_a)->b_rptr))
#define MINDEX(len, m, k) \
@@ -123,11 +125,7 @@ m_xhalf(mblk_t *m, uint32_t k, int *err)
*err = 0;
return ((cp[0] << 8) | mtod(m0, uchar_t *)[0]);
}
-#else /* _KERNEL */
-#include <stdlib.h>
-#endif /* !_KERNEL */
-#include <net/bpf.h>
/*
* Execute the filter program starting at pc on the packet p
@@ -137,8 +135,8 @@ m_xhalf(mblk_t *m, uint32_t k, int *err)
* packet is only in one mblk_t.
* When buflen is 0, p is an mblk_t pointer.
*/
-uint_t
-bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
+uint32_t
+ip_bpf_filter(ip_bpf_insn_t *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
{
uint32_t A, X, k;
uint32_t mem[BPF_MEMWORDS];
@@ -147,7 +145,7 @@ bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
/*
* No filter means accept all.
*/
- return ((uint_t)-1);
+ return ((uint32_t)-1);
A = 0;
X = 0;
--pc;
@@ -165,10 +163,10 @@ bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
abort();
#endif
case BPF_RET|BPF_K:
- return ((uint_t)pc->k);
+ return (pc->k);
case BPF_RET|BPF_A:
- return ((uint_t)A);
+ return (A);
case BPF_LD|BPF_W|BPF_ABS:
k = pc->k;
@@ -456,7 +454,6 @@ bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
/* NOTREACHED */
}
-#ifdef _KERNEL
/*
* Return true if the 'fcode' is a valid filter program.
* The constraints are that each jump be forward and to a valid
@@ -468,14 +465,14 @@ bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
* The kernel needs to be able to verify an application's filter code.
* Otherwise, a bogus program could easily crash the system.
*/
-int
-bpf_validate(struct bpf_insn *f, int len)
+boolean_t
+ip_bpf_validate(ip_bpf_insn_t *f, uint_t len)
{
uint_t i, from;
- struct bpf_insn *p;
+ ip_bpf_insn_t *p;
if (len < 1 || len > BPF_MAXINSNS)
- return (0);
+ return (B_FALSE);
for (i = 0; i < len; ++i) {
p = &f[i];
@@ -489,7 +486,7 @@ bpf_validate(struct bpf_insn *f, int len)
switch (BPF_MODE(p->code)) {
case BPF_MEM:
if (p->k >= BPF_MEMWORDS)
- return (0);
+ return (B_FALSE);
break;
case BPF_ABS:
case BPF_IND:
@@ -498,13 +495,13 @@ bpf_validate(struct bpf_insn *f, int len)
case BPF_LEN:
break;
default:
- return (0);
+ return (B_FALSE);
}
break;
case BPF_ST:
case BPF_STX:
if (p->k >= BPF_MEMWORDS)
- return (0);
+ return (B_FALSE);
break;
case BPF_ALU:
switch (BPF_OP(p->code)) {
@@ -522,10 +519,10 @@ bpf_validate(struct bpf_insn *f, int len)
* Check for constant division by 0.
*/
if (BPF_RVAL(p->code) == BPF_K && p->k == 0)
- return (0);
+ return (B_FALSE);
break;
default:
- return (0);
+ return (B_FALSE);
}
break;
case BPF_JMP:
@@ -549,17 +546,17 @@ bpf_validate(struct bpf_insn *f, int len)
switch (BPF_OP(p->code)) {
case BPF_JA:
if (from + p->k < from || from + p->k >= len)
- return (0);
+ return (B_FALSE);
break;
case BPF_JEQ:
case BPF_JGT:
case BPF_JGE:
case BPF_JSET:
if (from + p->jt >= len || from + p->jf >= len)
- return (0);
+ return (B_FALSE);
break;
default:
- return (0);
+ return (B_FALSE);
}
break;
case BPF_RET:
@@ -567,10 +564,9 @@ bpf_validate(struct bpf_insn *f, int len)
case BPF_MISC:
break;
default:
- return (0);
+ return (B_FALSE);
}
}
return (BPF_CLASS(f[len - 1].code) == BPF_RET);
}
-#endif
diff --git a/usr/src/uts/common/inet/ip.h b/usr/src/uts/common/inet/ip.h
index c081c44a04..ebf2574363 100644
--- a/usr/src/uts/common/inet/ip.h
+++ b/usr/src/uts/common/inet/ip.h
@@ -1416,6 +1416,7 @@ typedef union ill_g_head_u {
#define ILL_CAPAB_DLD 0x20 /* DLD capabilities */
#define ILL_CAPAB_DLD_POLL 0x40 /* Polling */
#define ILL_CAPAB_DLD_DIRECT 0x80 /* Direct function call */
+#define ILL_CAPAB_DLD_IPCHECK 0x100 /* Check if IPs are permitted */
/*
* Per-ill Hardware Checksumming capbilities.
@@ -1772,6 +1773,10 @@ typedef struct ill_s {
* Used to save errors that occur during plumbing
*/
uint_t ill_ifname_pending_err;
+ /*
+ * Used to save errors that occur during binding
+ */
+ uint_t ill_dl_bind_err;
avl_node_t ill_avl_byppa; /* avl node based on ppa */
uint_t ill_mcast_nces; /* Number of NCEs that are multicast. */
list_t ill_nce; /* pointer to nce_s list */
@@ -1938,6 +1943,7 @@ typedef struct ill_s {
* ill_nd_lla_len ipsq + down ill only when ill is up
* ill_phys_addr_pend ipsq + down ill only when ill is up
* ill_ifname_pending_err ipsq ipsq
+ * ill_dl_bind_err ipsq ipsq
* ill_avl_byppa ipsq, ill_g_lock write once
*
* ill_fastpath_list ill_lock ill_lock
@@ -3580,6 +3586,8 @@ typedef void (*ip_flow_enable_t)(void *, ip_mac_tx_cookie_t);
typedef void *(*ip_dld_callb_t)(void *,
ip_flow_enable_t, void *);
typedef boolean_t (*ip_dld_fctl_t)(void *, ip_mac_tx_cookie_t);
+typedef boolean_t (*ip_mac_ipcheck_t)(void *, boolean_t,
+ in6_addr_t *);
typedef int (*ip_capab_func_t)(void *, uint_t,
void *, uint_t);
@@ -3632,6 +3640,12 @@ typedef struct ill_dld_direct_s { /* DLD provided driver Tx */
void *idd_tx_fctl_dh; /* mac_client_handle */
} ill_dld_direct_t;
+/* IP - DLD direct function call to check if an IP is allowed */
+typedef struct ill_dld_ipcheck_s {
+ ip_mac_ipcheck_t idi_allowed_df;
+ void *idi_allowed_dh;
+} ill_dld_ipcheck_t;
+
/* IP - DLD polling capability */
typedef struct ill_dld_poll_s {
ill_rx_ring_t idp_ring_tbl[ILL_MAX_RINGS];
@@ -3643,6 +3657,7 @@ struct ill_dld_capab_s {
void *idc_capab_dh; /* dld_str_t *dsp */
ill_dld_direct_t idc_direct;
ill_dld_poll_t idc_poll;
+ ill_dld_ipcheck_t idc_ipcheck;
};
/*
diff --git a/usr/src/uts/common/inet/ip/conn_opt.c b/usr/src/uts/common/inet/ip/conn_opt.c
index 7aac9b655a..eeec56b162 100644
--- a/usr/src/uts/common/inet/ip/conn_opt.c
+++ b/usr/src/uts/common/inet/ip/conn_opt.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
*/
/* Copyright (c) 1990 Mentat Inc. */
@@ -644,6 +645,9 @@ conn_opt_get(conn_opt_arg_t *coa, t_scalar_t level, t_scalar_t name,
case SO_REUSEADDR:
*i1 = connp->conn_reuseaddr ? SO_REUSEADDR : 0;
break; /* goto sizeof (int) option return */
+ case SO_REUSEPORT:
+ *i1 = connp->conn_reuseport;
+ break; /* goto sizeof (int) option return */
case SO_TYPE:
*i1 = connp->conn_so_type;
break; /* goto sizeof (int) option return */
@@ -1214,8 +1218,24 @@ conn_opt_set_ip(conn_opt_arg_t *coa, t_scalar_t name, uint_t inlen,
ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
int error;
- if (connp->conn_family != AF_INET)
+ if (connp->conn_family == AF_INET6 &&
+ connp->conn_ipversion == IPV4_VERSION) {
+ /*
+ * Allow certain IPv4 options to be set on an AF_INET6 socket
+ * if the connection is still IPv4.
+ */
+ switch (name) {
+ case IP_TOS:
+ case T_IP_TOS:
+ case IP_TTL:
+ case IP_DONTFRAG:
+ break;
+ default:
+ return (EINVAL);
+ }
+ } else if (connp->conn_family != AF_INET) {
return (EINVAL);
+ }
ifindex = UINT_MAX;
switch (name) {
diff --git a/usr/src/uts/common/inet/ip/icmp.c b/usr/src/uts/common/inet/ip/icmp.c
index 57ee0c5585..46c791298a 100644
--- a/usr/src/uts/common/inet/ip/icmp.c
+++ b/usr/src/uts/common/inet/ip/icmp.c
@@ -81,6 +81,7 @@
#include <sys/tsol/tnet.h>
#include <inet/rawip_impl.h>
+#include <net/bpf.h>
#include <sys/disp.h>
@@ -1018,6 +1019,12 @@ icmp_close_free(conn_t *connp)
icmp->icmp_filter = NULL;
}
+ if (icmp->icmp_bpf_len != 0) {
+ kmem_free(icmp->icmp_bpf_prog, icmp->icmp_bpf_len);
+ icmp->icmp_bpf_len = 0;
+ icmp->icmp_bpf_prog = NULL;
+ }
+
/*
* Clear any fields which the kmem_cache constructor clears.
* Only icmp_connp needs to be preserved.
@@ -1971,6 +1978,104 @@ icmp_tpi_opt_get(queue_t *q, int level, int name, uchar_t *ptr)
return (err);
}
+static int
+icmp_attach_filter(icmp_t *icmp, uint_t inlen, const uchar_t *invalp)
+{
+ struct bpf_program prog;
+ ip_bpf_insn_t *insns = NULL;
+ unsigned int size;
+
+#ifdef _LP64
+ if (get_udatamodel() != DATAMODEL_NATIVE) {
+ struct bpf_program32 *prog32;
+
+ if (inlen != sizeof (struct bpf_program32)) {
+ return (EINVAL);
+ }
+ prog32 = (struct bpf_program32 *)invalp;
+ prog.bf_len = prog32->bf_len;
+ prog.bf_insns = (void *)(uint64_t)prog32->bf_insns;
+ } else
+#endif
+ if (inlen == sizeof (struct bpf_program)) {
+ bcopy(invalp, &prog, sizeof (prog));
+ } else {
+ return (EINVAL);
+ }
+
+ if (prog.bf_len > BPF_MAXINSNS || prog.bf_len == 0) {
+ return (EINVAL);
+ }
+ size = prog.bf_len * sizeof (struct bpf_insn);
+ insns = kmem_alloc(size, KM_SLEEP);
+ if (copyin(prog.bf_insns, insns, size) != 0) {
+ kmem_free(insns, size);
+ return (EFAULT);
+ }
+ if (!ip_bpf_validate(insns, prog.bf_len)) {
+ kmem_free(insns, size);
+ return (EINVAL);
+ }
+
+ rw_enter(&icmp->icmp_bpf_lock, RW_WRITER);
+ if (icmp->icmp_bpf_len != 0) {
+ ASSERT(icmp->icmp_bpf_prog != NULL);
+
+ kmem_free(icmp->icmp_bpf_prog, icmp->icmp_bpf_len);
+ }
+ icmp->icmp_bpf_len = size;
+ icmp->icmp_bpf_prog = insns;
+ rw_exit(&icmp->icmp_bpf_lock);
+ return (0);
+}
+
+static int
+icmp_detach_filter(icmp_t *icmp)
+{
+ int error;
+
+ rw_enter(&icmp->icmp_bpf_lock, RW_WRITER);
+ if (icmp->icmp_bpf_len == 0) {
+ ASSERT(icmp->icmp_bpf_prog == NULL);
+ error = ENOENT;
+ } else {
+ kmem_free(icmp->icmp_bpf_prog,
+ icmp->icmp_bpf_len);
+ icmp->icmp_bpf_len = 0;
+ icmp->icmp_bpf_prog = NULL;
+ error = 0;
+ }
+ rw_exit(&icmp->icmp_bpf_lock);
+ return (error);
+}
+
+static boolean_t
+icmp_eval_filter(icmp_t *icmp, mblk_t *mp, ip_recv_attr_t *ira)
+{
+ boolean_t res;
+ uchar_t *buf = mp->b_rptr;
+ uint_t wirelen, len = MBLKL(mp);
+
+ rw_enter(&icmp->icmp_bpf_lock, RW_READER);
+ if (icmp->icmp_bpf_len == 0) {
+ rw_exit(&icmp->icmp_bpf_lock);
+ return (B_FALSE);
+ }
+ if (ira->ira_flags & IRAF_IS_IPV4) {
+ ipha_t *ipha = (ipha_t *)buf;
+
+ wirelen = ntohs(ipha->ipha_length);
+ } else {
+ ip6_t *ip6h = (ip6_t *)buf;
+
+ wirelen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN;
+ }
+ res = !ip_bpf_filter(icmp->icmp_bpf_prog, buf, wirelen, len);
+ rw_exit(&icmp->icmp_bpf_lock);
+
+ return (res);
+}
+
/*
* This routine sets socket options.
*/
@@ -2060,6 +2165,10 @@ icmp_do_opt_set(conn_opt_arg_t *coa, int level, int name,
return (ENOBUFS);
}
break;
+ case SO_ATTACH_FILTER:
+ return (icmp_attach_filter(icmp, inlen, invalp));
+ case SO_DETACH_FILTER:
+ return (icmp_detach_filter(icmp));
}
break;
@@ -2605,6 +2714,14 @@ icmp_input(void *arg1, mblk_t *mp, void *arg2, ip_recv_attr_t *ira)
/* Initialize regardless of IP version */
ipps.ipp_fields = 0;
+ /* Apply socket filter, if needed */
+ if (icmp->icmp_bpf_len != 0) {
+ if (icmp_eval_filter(icmp, mp, ira)) {
+ freemsg(mp);
+ return;
+ }
+ }
+
if (ira->ira_flags & IRAF_IS_IPV4) {
ASSERT(IPH_HDR_VERSION(rptr) == IPV4_VERSION);
ASSERT(MBLKL(mp) >= sizeof (ipha_t));
diff --git a/usr/src/uts/common/inet/ip/icmp_opt_data.c b/usr/src/uts/common/inet/ip/icmp_opt_data.c
index ff0310de0c..d65d3164d3 100644
--- a/usr/src/uts/common/inet/ip/icmp_opt_data.c
+++ b/usr/src/uts/common/inet/ip/icmp_opt_data.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -41,6 +42,7 @@
#include <netinet/ip_mroute.h>
#include <inet/optcom.h>
#include <inet/rawip_impl.h>
+#include <net/bpf.h>
/*
* Table of all known options handled on a ICMP protocol stack.
@@ -86,6 +88,10 @@ opdes_t icmp_opt_arr[] = {
0 },
{ SO_DOMAIN, SOL_SOCKET, OA_R, OA_R, OP_NP, 0, sizeof (int), 0 },
+{ SO_ATTACH_FILTER, SOL_SOCKET, OA_W, OA_W, OP_NP, 0,
+ sizeof (struct bpf_program), 0 },
+{ SO_DETACH_FILTER, SOL_SOCKET, OA_W, OA_W, OP_NP, 0, 0, 0 },
+
{ IP_OPTIONS, IPPROTO_IP, OA_RW, OA_RW, OP_NP,
(OP_VARLEN|OP_NODEFAULT),
IP_MAX_OPT_LENGTH + IP_ADDR_LEN, -1 /* not initialized */ },
diff --git a/usr/src/uts/common/inet/ip/ip.c b/usr/src/uts/common/inet/ip/ip.c
index 6063fa01d2..704f152bb9 100644
--- a/usr/src/uts/common/inet/ip/ip.c
+++ b/usr/src/uts/common/inet/ip/ip.c
@@ -8235,7 +8235,6 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
conn_t *connp = NULL;
t_uscalar_t paddrreq;
mblk_t *mp_hw;
- boolean_t success;
boolean_t ioctl_aborted = B_FALSE;
boolean_t log = B_TRUE;
@@ -8335,7 +8334,8 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
ill->ill_state_flags &= ~ILL_DOWN_IN_PROGRESS;
mutex_exit(&ill->ill_lock);
/*
- * Something went wrong with the bind. We presumably
+ * Something went wrong with the bind. If this was the
+ * result of a DL_NOTE_REPLUMB, then we presumably
* have an IOCTL hanging out waiting for completion.
* Find it, take down the interface that was coming
* up, and complete the IOCTL with the error noted.
@@ -8352,6 +8352,15 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
(void) ipif_down(ipif, NULL, NULL);
/* error is set below the switch */
+ } else {
+ /*
+ * There's no pending IOCTL, so the bind was
+ * most likely started by ill_dl_up(). We save
+ * the error and let it take care of responding
+ * to the IOCTL.
+ */
+ ill->ill_dl_bind_err = dlea->dl_unix_errno ?
+ dlea->dl_unix_errno : ENXIO;
}
break;
case DL_ENABMULTI_REQ:
@@ -8475,55 +8484,7 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
DTRACE_PROBE1(ip__rput__dlpi__bind__ack, ill_t *, ill);
ill_nic_event_dispatch(ill, 0, NE_UP, NULL, 0);
- /*
- * Now bring up the resolver; when that is complete, we'll
- * create IREs. Note that we intentionally mirror what
- * ipif_up() would have done, because we got here by way of
- * ill_dl_up(), which stopped ipif_up()'s processing.
- */
- if (ill->ill_isv6) {
- /*
- * v6 interfaces.
- * Unlike ARP which has to do another bind
- * and attach, once we get here we are
- * done with NDP
- */
- (void) ipif_resolver_up(ipif, Res_act_initial);
- if ((err = ipif_ndp_up(ipif, B_TRUE)) == 0)
- err = ipif_up_done_v6(ipif);
- } else if (ill->ill_net_type == IRE_IF_RESOLVER) {
- /*
- * ARP and other v4 external resolvers.
- * Leave the pending mblk intact so that
- * the ioctl completes in ip_rput().
- */
- if (connp != NULL)
- mutex_enter(&connp->conn_lock);
- mutex_enter(&ill->ill_lock);
- success = ipsq_pending_mp_add(connp, ipif, q, mp1, 0);
- mutex_exit(&ill->ill_lock);
- if (connp != NULL)
- mutex_exit(&connp->conn_lock);
- if (success) {
- err = ipif_resolver_up(ipif, Res_act_initial);
- if (err == EINPROGRESS) {
- freemsg(mp);
- return;
- }
- mp1 = ipsq_pending_mp_get(ipsq, &connp);
- } else {
- /* The conn has started closing */
- err = EINTR;
- }
- } else {
- /*
- * This one is complete. Reply to pending ioctl.
- */
- (void) ipif_resolver_up(ipif, Res_act_initial);
- err = ipif_up_done(ipif);
- }
-
- if ((err == 0) && (ill->ill_up_ipifs)) {
+ if (ill->ill_up_ipifs) {
err = ill_up_ipifs(ill, q, mp1);
if (err == EINPROGRESS) {
freemsg(mp);
@@ -8531,25 +8492,6 @@ ip_rput_dlpi_writer(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *dummy_arg)
}
}
- /*
- * If we have a moved ipif to bring up, and everything has
- * succeeded to this point, bring it up on the IPMP ill.
- * Otherwise, leave it down -- the admin can try to bring it
- * up by hand if need be.
- */
- if (ill->ill_move_ipif != NULL) {
- if (err != 0) {
- ill->ill_move_ipif = NULL;
- } else {
- ipif = ill->ill_move_ipif;
- ill->ill_move_ipif = NULL;
- err = ipif_up(ipif, q, mp1);
- if (err == EINPROGRESS) {
- freemsg(mp);
- return;
- }
- }
- }
break;
case DL_NOTIFY_IND: {
@@ -12621,6 +12563,7 @@ ip_process_ioctl(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *arg)
struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
ip_ioctl_cmd_t *ipip = arg;
ip_extract_func_t *extract_funcp;
+ ill_t *ill;
cmd_info_t ci;
int err;
boolean_t entered_ipsq = B_FALSE;
@@ -12742,6 +12685,13 @@ ip_process_ioctl(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *arg)
ipsq_current_start(ipsq, ci.ci_ipif, ipip->ipi_cmd);
/*
+ * We need to cache the ill_t that we're going to use as the argument
+ * to the ipif-ioctl DTrace probe (below) because the ci_ipif can be
+ * blown away by calling ipi_func.
+ */
+ ill = ci.ci_ipif == NULL ? NULL : ci.ci_ipif->ipif_ill;
+
+ /*
* A return value of EINPROGRESS means the ioctl is
* either queued and waiting for some reason or has
* already completed.
@@ -12749,9 +12699,7 @@ ip_process_ioctl(ipsq_t *ipsq, queue_t *q, mblk_t *mp, void *arg)
err = (*ipip->ipi_func)(ci.ci_ipif, ci.ci_sin, q, mp, ipip, ci.ci_lifr);
DTRACE_PROBE4(ipif__ioctl, char *, "ip_process_ioctl finish WR",
- int, ipip->ipi_cmd,
- ill_t *, ci.ci_ipif == NULL ? NULL : ci.ci_ipif->ipif_ill,
- ipif_t *, ci.ci_ipif);
+ int, ipip->ipi_cmd, ill_t *, ill, ipif_t *, ci.ci_ipif);
ip_ioctl_finish(q, mp, err, IPI2MODE(ipip), ipsq);
if (entered_ipsq)
diff --git a/usr/src/uts/common/inet/ip/ip_if.c b/usr/src/uts/common/inet/ip/ip_if.c
index cc67299a1b..2307837eb8 100644
--- a/usr/src/uts/common/inet/ip/ip_if.c
+++ b/usr/src/uts/common/inet/ip/ip_if.c
@@ -22,7 +22,7 @@
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1990 Mentat Inc.
* Copyright (c) 2013 by Delphix. All rights reserved.
- * Copyright (c) 2016, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
* Copyright (c) 2014, OmniTI Computer Consulting, Inc. All rights reserved.
*/
@@ -174,7 +174,7 @@ static ipif_t *ipif_lookup_on_name_async(char *name, size_t namelen,
static int ill_alloc_ppa(ill_if_t *, ill_t *);
static void ill_delete_interface_type(ill_if_t *);
-static int ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q);
+static int ill_dl_up(ill_t *ill, ipif_t *ipif);
static void ill_dl_down(ill_t *ill);
static void ill_down(ill_t *ill);
static void ill_down_ipifs(ill_t *, boolean_t);
@@ -1380,6 +1380,36 @@ ill_capability_probe(ill_t *ill)
ill->ill_dlpi_capab_state = IDCS_PROBE_SENT;
}
+static boolean_t
+ill_capability_wait(ill_t *ill)
+{
+ /*
+ * I'm in this ill's squeue, aka a writer. The ILL_CONDEMNED flag can
+ * only be set by someone who is the writer. Since we
+ * drop-and-reacquire the squeue in this loop, we need to check for
+ * ILL_CONDEMNED, which if set means nothing can signal our capability
+ * condition variable.
+ */
+ ASSERT(IAM_WRITER_ILL(ill));
+
+ while (ill->ill_capab_pending_cnt != 0 &&
+ (ill->ill_state_flags & ILL_CONDEMNED) == 0) {
+ /* This may enable blocked callers of ill_capability_done(). */
+ ipsq_exit(ill->ill_phyint->phyint_ipsq);
+ /* Pause a bit (1msec) before we re-enter the squeue. */
+ delay(drv_usectohz(1000000));
+
+ /*
+ * If ipsq_enter() fails, someone set ILL_CONDEMNED
+ * while we dropped the squeue. Indicate such to the caller.
+ */
+ if (!ipsq_enter(ill, B_FALSE, CUR_OP))
+ return (B_FALSE);
+ }
+
+ return ((ill->ill_state_flags & ILL_CONDEMNED) == 0);
+}
+
void
ill_capability_reset(ill_t *ill, boolean_t reneg)
{
@@ -1390,6 +1420,8 @@ ill_capability_reset(ill_t *ill, boolean_t reneg)
ill->ill_dlpi_capab_state = reneg ? IDCS_RENEG : IDCS_RESET_SENT;
+ ASSERT(ill->ill_capab_reset_mp != NULL);
+
ill_capability_send(ill, ill->ill_capab_reset_mp);
ill->ill_capab_reset_mp = NULL;
/*
@@ -2109,6 +2141,49 @@ ill_capability_lso_enable(ill_t *ill)
}
}
+/*
+ * Check whether or not mac will prevent us from sending with a given IP
+ * address. This requires having the IPCHECK capability, which we should
+ * always be able to successfully negotiate, but if it's somehow missing
+ * then we just permit the caller to use the address, since mac does the
+ * actual enforcement and ip is just performing a courtesy check to help
+ * prevent users from unwittingly setting and attempting to use blocked
+ * addresses.
+ */
+static boolean_t
+ill_ipcheck_addr(ill_t *ill, in6_addr_t *v6addr)
+{
+ if ((ill->ill_capabilities & ILL_CAPAB_DLD_IPCHECK) == 0)
+ return (B_TRUE);
+
+ ill_dld_ipcheck_t *idi = &ill->ill_dld_capab->idc_ipcheck;
+ ip_mac_ipcheck_t ipcheck = idi->idi_allowed_df;
+ return (ipcheck(idi->idi_allowed_dh, ill->ill_isv6, v6addr));
+}
+
+static void
+ill_capability_ipcheck_enable(ill_t *ill)
+{
+ ill_dld_capab_t *idc = ill->ill_dld_capab;
+ ill_dld_ipcheck_t *idi = &idc->idc_ipcheck;
+ dld_capab_ipcheck_t spoof;
+ int rc;
+
+ ASSERT(IAM_WRITER_ILL(ill));
+
+ bzero(&spoof, sizeof (spoof));
+ if ((rc = idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_IPCHECK,
+ &spoof, DLD_ENABLE)) == 0) {
+ idi->idi_allowed_df = (ip_mac_ipcheck_t)spoof.ipc_allowed_df;
+ idi->idi_allowed_dh = spoof.ipc_allowed_dh;
+ ill->ill_capabilities |= ILL_CAPAB_DLD_IPCHECK;
+ } else {
+ cmn_err(CE_WARN, "warning: could not enable IPCHECK "
+ "capability, rc = %d\n", rc);
+ DTRACE_PROBE2(ipcheck__off, (ill_t *), ill, (int), rc);
+ }
+}
+
static void
ill_capability_dld_enable(ill_t *ill)
{
@@ -2121,6 +2196,8 @@ ill_capability_dld_enable(ill_t *ill)
ill_capability_direct_enable(ill);
ill_capability_poll_enable(ill);
}
+
+ ill_capability_ipcheck_enable(ill);
ill_capability_lso_enable(ill);
ill->ill_capabilities |= ILL_CAPAB_DLD;
ill_mac_perim_exit(ill, mph);
@@ -2186,6 +2263,15 @@ ill_capability_dld_disable(ill_t *ill)
NULL, DLD_DISABLE);
}
+ if ((ill->ill_capabilities & ILL_CAPAB_DLD_IPCHECK) != 0) {
+ ASSERT(ill->ill_dld_capab->idc_ipcheck.idi_allowed_df != NULL);
+ ASSERT(ill->ill_dld_capab->idc_ipcheck.idi_allowed_dh != NULL);
+
+ ill->ill_capabilities &= ~ILL_CAPAB_DLD_IPCHECK;
+ (void) idc->idc_capab_df(idc->idc_capab_dh, DLD_CAPAB_IPCHECK,
+ NULL, DLD_DISABLE);
+ }
+
ill->ill_capabilities &= ~ILL_CAPAB_DLD;
ill_mac_perim_exit(ill, mph);
}
@@ -9676,7 +9762,6 @@ ip_sioctl_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
in6_addr_t v6addr;
boolean_t need_up = B_FALSE;
ill_t *ill;
- int i;
ip1dbg(("ip_sioctl_addr(%s:%u %p)\n",
ipif->ipif_ill->ill_name, ipif->ipif_id, (void *)ipif));
@@ -9751,20 +9836,9 @@ ip_sioctl_addr(ipif_t *ipif, sin_t *sin, queue_t *q, mblk_t *mp,
IN6_IPADDR_TO_V4MAPPED(addr, &v6addr);
}
- /*
- * verify that the address being configured is permitted by the
- * ill_allowed_ips[] for the interface.
- */
- if (ill->ill_allowed_ips_cnt > 0) {
- for (i = 0; i < ill->ill_allowed_ips_cnt; i++) {
- if (IN6_ARE_ADDR_EQUAL(&ill->ill_allowed_ips[i],
- &v6addr))
- break;
- }
- if (i == ill->ill_allowed_ips_cnt) {
- pr_addr_dbg("!allowed addr %s\n", AF_INET6, &v6addr);
- return (EPERM);
- }
+ /* verify that the address being configured is permitted by mac */
+ if (!ill_ipcheck_addr(ill, &v6addr)) {
+ return (EPERM);
}
/*
* Even if there is no change we redo things just to rerun
@@ -12704,6 +12778,12 @@ ill_dl_down(ill_t *ill)
}
ill->ill_unbind_mp = NULL;
+
+ mutex_enter(&ill->ill_lock);
+ ill->ill_dl_up = 0;
+ ill_nic_event_dispatch(ill, 0, NE_DOWN, NULL, 0);
+ mutex_exit(&ill->ill_lock);
+
if (mp != NULL) {
ip1dbg(("ill_dl_down: %s (%u) for %s\n",
dl_primstr(*(int *)mp->b_rptr), *(int *)mp->b_rptr,
@@ -12726,11 +12806,13 @@ ill_dl_down(ill_t *ill)
ill_capability_dld_disable(ill);
ill_capability_reset(ill, B_FALSE);
ill_dlpi_send(ill, mp);
+
+ /*
+ * Wait for the capability reset to finish.
+ * In this case, it doesn't matter WHY or HOW it finished.
+ */
+ (void) ill_capability_wait(ill);
}
- mutex_enter(&ill->ill_lock);
- ill->ill_dl_up = 0;
- ill_nic_event_dispatch(ill, 0, NE_DOWN, NULL, 0);
- mutex_exit(&ill->ill_lock);
}
void
@@ -12852,6 +12934,7 @@ void
ill_capability_done(ill_t *ill)
{
ASSERT(ill->ill_capab_pending_cnt != 0);
+ ASSERT(IAM_WRITER_ILL(ill));
ill_dlpi_done(ill, DL_CAPABILITY_REQ);
@@ -14480,7 +14563,14 @@ ipif_up(ipif_t *ipif, queue_t *q, mblk_t *mp)
* address/netmask etc cause a down/up dance, but
* does not cause an unbind (DL_UNBIND) with the driver
*/
- return (ill_dl_up(ill, ipif, mp, q));
+ if ((err = ill_dl_up(ill, ipif)) != 0) {
+ return (err);
+ }
+ }
+
+ /* Reject bringing up interfaces with unusable IP addresses */
+ if (!ill_ipcheck_addr(ill, &ipif->ipif_v6lcl_addr)) {
+ return (EPERM);
}
/*
@@ -14593,24 +14683,22 @@ ill_delete_ires(ill_t *ill)
/*
* Perform a bind for the physical device.
- * When the routine returns EINPROGRESS then mp has been consumed and
- * the ioctl will be acked from ip_rput_dlpi.
- * Allocate an unbind message and save it until ipif_down.
+ *
+ * When the routine returns successfully then dlpi has been bound and
+ * capabilities negotiated. An unbind message will have been allocated
+ * for later use in ipif_down.
*/
static int
-ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q)
+ill_dl_up(ill_t *ill, ipif_t *ipif)
{
mblk_t *bind_mp = NULL;
mblk_t *unbind_mp = NULL;
- conn_t *connp;
- boolean_t success;
int err;
DTRACE_PROBE2(ill__downup, char *, "ill_dl_up", ill_t *, ill);
ip1dbg(("ill_dl_up(%s)\n", ill->ill_name));
ASSERT(IAM_WRITER_ILL(ill));
- ASSERT(mp != NULL);
/*
* Make sure we have an IRE_MULTICAST in case we immediately
@@ -14645,19 +14733,6 @@ ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q)
if (unbind_mp == NULL)
goto bad;
}
- /*
- * Record state needed to complete this operation when the
- * DL_BIND_ACK shows up. Also remember the pre-allocated mblks.
- */
- connp = CONN_Q(q) ? Q_TO_CONN(q) : NULL;
- ASSERT(connp != NULL || !CONN_Q(q));
- GRAB_CONN_LOCK(q);
- mutex_enter(&ipif->ipif_ill->ill_lock);
- success = ipsq_pending_mp_add(connp, ipif, q, mp, 0);
- mutex_exit(&ipif->ipif_ill->ill_lock);
- RELEASE_CONN_LOCK(q);
- if (!success)
- goto bad;
/*
* Save the unbind message for ill_dl_down(); it will be consumed when
@@ -14669,6 +14744,18 @@ ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q)
ill_dlpi_send(ill, bind_mp);
/* Send down link-layer capabilities probe if not already done. */
ill_capability_probe(ill);
+ /*
+ * Wait for DLPI to be bound and the capability probe to finish.
+ * The call drops-and-reacquires the squeue. If it couldn't because
+ * ILL_CONDEMNED got set, bail.
+ */
+ if (!ill_capability_wait(ill))
+ return (ENXIO);
+
+ /* DLPI failed to bind. Return the saved error */
+ if (!ill->ill_dl_up) {
+ return (ill->ill_dl_bind_err);
+ }
/*
* Sysid used to rely on the fact that netboots set domainname
@@ -14686,11 +14773,7 @@ ill_dl_up(ill_t *ill, ipif_t *ipif, mblk_t *mp, queue_t *q)
cmn_err(CE_WARN, "no cached dhcp response");
}
- /*
- * This operation will complete in ip_rput_dlpi with either
- * a DL_BIND_ACK or DL_ERROR_ACK.
- */
- return (EINPROGRESS);
+ return (0);
bad:
ip1dbg(("ill_dl_up(%s) FAILED\n", ill->ill_name));
diff --git a/usr/src/uts/common/inet/ip/ip_squeue.c b/usr/src/uts/common/inet/ip/ip_squeue.c
index 13e961333c..b6565d9c1f 100644
--- a/usr/src/uts/common/inet/ip/ip_squeue.c
+++ b/usr/src/uts/common/inet/ip/ip_squeue.c
@@ -153,7 +153,7 @@ ip_squeue_create(pri_t pri)
{
squeue_t *sqp;
- sqp = squeue_create(pri);
+ sqp = squeue_create(pri, B_TRUE);
ASSERT(sqp != NULL);
if (ip_squeue_create_callback != NULL)
ip_squeue_create_callback(sqp);
diff --git a/usr/src/uts/common/inet/ip/ipclassifier.c b/usr/src/uts/common/inet/ip/ipclassifier.c
index 34832d56e5..d47997a4aa 100644
--- a/usr/src/uts/common/inet/ip/ipclassifier.c
+++ b/usr/src/uts/common/inet/ip/ipclassifier.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
* Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
* Copyright 2022 Joyent, Inc.
*/
@@ -871,67 +872,91 @@ ipcl_hash_remove_locked(conn_t *connp, connf_t *connfp)
mutex_exit(&(connfp)->connf_lock); \
}
-#define IPCL_HASH_INSERT_BOUND(connfp, connp) { \
- conn_t *pconnp = NULL, *nconnp; \
- IPCL_HASH_REMOVE((connp)); \
- mutex_enter(&(connfp)->connf_lock); \
- nconnp = (connfp)->connf_head; \
- while (nconnp != NULL && \
- !_IPCL_V4_MATCH_ANY(nconnp->conn_laddr_v6)) { \
- pconnp = nconnp; \
- nconnp = nconnp->conn_next; \
- } \
- if (pconnp != NULL) { \
- pconnp->conn_next = (connp); \
- (connp)->conn_prev = pconnp; \
- } else { \
- (connfp)->connf_head = (connp); \
- } \
- if (nconnp != NULL) { \
- (connp)->conn_next = nconnp; \
- nconnp->conn_prev = (connp); \
- } \
- (connp)->conn_fanout = (connfp); \
- (connp)->conn_flags = ((connp)->conn_flags & ~IPCL_REMOVED) | \
- IPCL_BOUND; \
- CONN_INC_REF(connp); \
- mutex_exit(&(connfp)->connf_lock); \
-}
+/*
+ * When inserting bound or wildcard entries into the hash, ordering rules are
+ * used to facilitate timely and correct lookups. The order is as follows:
+ * 1. Entries bound to a specific address
+ * 2. Entries bound to INADDR_ANY
+ * 3. Entries bound to ADDR_UNSPECIFIED
+ * Entries in a category which share conn_lport (such as those using
+ * SO_REUSEPORT) will be ordered such that the newest inserted is first.
+ */
-#define IPCL_HASH_INSERT_WILDCARD(connfp, connp) { \
- conn_t **list, *prev, *next; \
- boolean_t isv4mapped = \
- IN6_IS_ADDR_V4MAPPED(&(connp)->conn_laddr_v6); \
- IPCL_HASH_REMOVE((connp)); \
- mutex_enter(&(connfp)->connf_lock); \
- list = &(connfp)->connf_head; \
- prev = NULL; \
- while ((next = *list) != NULL) { \
- if (isv4mapped && \
- IN6_IS_ADDR_UNSPECIFIED(&next->conn_laddr_v6) && \
- connp->conn_zoneid == next->conn_zoneid) { \
- (connp)->conn_next = next; \
- if (prev != NULL) \
- prev = next->conn_prev; \
- next->conn_prev = (connp); \
- break; \
- } \
- list = &next->conn_next; \
- prev = next; \
- } \
- (connp)->conn_prev = prev; \
- *list = (connp); \
- (connp)->conn_fanout = (connfp); \
- (connp)->conn_flags = ((connp)->conn_flags & ~IPCL_REMOVED) | \
- IPCL_BOUND; \
- CONN_INC_REF((connp)); \
- mutex_exit(&(connfp)->connf_lock); \
+void
+ipcl_hash_insert_bound(connf_t *connfp, conn_t *connp)
+{
+ conn_t *pconnp, *nconnp;
+
+ IPCL_HASH_REMOVE(connp);
+ mutex_enter(&connfp->connf_lock);
+ nconnp = connfp->connf_head;
+ pconnp = NULL;
+ while (nconnp != NULL) {
+ /*
+ * Walk though entries associated with the fanout until one is
+ * found which fulfills any of these conditions:
+ * 1. Listen address of ADDR_ANY/ADDR_UNSPECIFIED
+ * 2. Listen port the same as connp
+ */
+ if (_IPCL_V4_MATCH_ANY(nconnp->conn_laddr_v6) ||
+ connp->conn_lport == nconnp->conn_lport)
+ break;
+ pconnp = nconnp;
+ nconnp = nconnp->conn_next;
+ }
+ if (pconnp != NULL) {
+ pconnp->conn_next = connp;
+ connp->conn_prev = pconnp;
+ } else {
+ connfp->connf_head = connp;
+ }
+ if (nconnp != NULL) {
+ connp->conn_next = nconnp;
+ nconnp->conn_prev = connp;
+ }
+ connp->conn_fanout = connfp;
+ connp->conn_flags = (connp->conn_flags & ~IPCL_REMOVED) | IPCL_BOUND;
+ CONN_INC_REF(connp);
+ mutex_exit(&connfp->connf_lock);
}
void
ipcl_hash_insert_wildcard(connf_t *connfp, conn_t *connp)
{
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ conn_t **list, *prev, *next;
+ conn_t *pconnp = NULL, *nconnp;
+ boolean_t isv4mapped = IN6_IS_ADDR_V4MAPPED(&connp->conn_laddr_v6);
+
+ IPCL_HASH_REMOVE(connp);
+ mutex_enter(&connfp->connf_lock);
+ nconnp = connfp->connf_head;
+ pconnp = NULL;
+ while (nconnp != NULL) {
+ if (IN6_IS_ADDR_V4MAPPED_ANY(&nconnp->conn_laddr_v6) &&
+ isv4mapped && connp->conn_lport == nconnp->conn_lport)
+ break;
+ if (IN6_IS_ADDR_UNSPECIFIED(&nconnp->conn_laddr_v6) &&
+ (isv4mapped ||
+ connp->conn_lport == nconnp->conn_lport))
+ break;
+
+ pconnp = nconnp;
+ nconnp = nconnp->conn_next;
+ }
+ if (pconnp != NULL) {
+ pconnp->conn_next = connp;
+ connp->conn_prev = pconnp;
+ } else {
+ connfp->connf_head = connp;
+ }
+ if (nconnp != NULL) {
+ connp->conn_next = nconnp;
+ nconnp->conn_prev = connp;
+ }
+ connp->conn_fanout = connfp;
+ connp->conn_flags = (connp->conn_flags & ~IPCL_REMOVED) | IPCL_BOUND;
+ CONN_INC_REF(connp);
+ mutex_exit(&connfp->connf_lock);
}
/*
@@ -1037,9 +1062,9 @@ ipcl_sctp_hash_insert(conn_t *connp, in_port_t lport)
IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_faddr_v6)) {
if (IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6) ||
IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_laddr_v6)) {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
} else {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
}
} else {
IPCL_HASH_INSERT_CONNECTED(connfp, connp);
@@ -1208,9 +1233,9 @@ ipcl_bind_insert_v4(conn_t *connp)
if (connp->conn_faddr_v4 != INADDR_ANY) {
IPCL_HASH_INSERT_CONNECTED(connfp, connp);
} else if (connp->conn_laddr_v4 != INADDR_ANY) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
if (protocol == IPPROTO_RSVP)
ill_set_inputfn_all(ipst);
@@ -1222,9 +1247,9 @@ ipcl_bind_insert_v4(conn_t *connp)
connfp = &ipst->ips_ipcl_bind_fanout[
IPCL_BIND_HASH(lport, ipst)];
if (connp->conn_laddr_v4 != INADDR_ANY) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
if (cl_inet_listen != NULL) {
ASSERT(connp->conn_ipversion == IPV4_VERSION);
@@ -1274,9 +1299,9 @@ ipcl_bind_insert_v6(conn_t *connp)
if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6)) {
IPCL_HASH_INSERT_CONNECTED(connfp, connp);
} else if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6)) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
break;
@@ -1286,9 +1311,9 @@ ipcl_bind_insert_v6(conn_t *connp)
connfp = &ipst->ips_ipcl_bind_fanout[
IPCL_BIND_HASH(lport, ipst)];
if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6)) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
if (cl_inet_listen != NULL) {
sa_family_t addr_family;
@@ -1419,9 +1444,9 @@ ipcl_conn_insert_v4(conn_t *connp)
if (connp->conn_faddr_v4 != INADDR_ANY) {
IPCL_HASH_INSERT_CONNECTED(connfp, connp);
} else if (connp->conn_laddr_v4 != INADDR_ANY) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
break;
}
@@ -1507,9 +1532,9 @@ ipcl_conn_insert_v6(conn_t *connp)
if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6)) {
IPCL_HASH_INSERT_CONNECTED(connfp, connp);
} else if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6)) {
- IPCL_HASH_INSERT_BOUND(connfp, connp);
+ ipcl_hash_insert_bound(connfp, connp);
} else {
- IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+ ipcl_hash_insert_wildcard(connfp, connp);
}
break;
}
@@ -2095,6 +2120,7 @@ rawip_conn_constructor(void *buf, void *cdrarg, int kmflags)
connp->conn_flags = IPCL_RAWIPCONN;
connp->conn_proto = IPPROTO_ICMP;
icmp->icmp_connp = connp;
+ rw_init(&icmp->icmp_bpf_lock, NULL, RW_DEFAULT, NULL);
rw_init(&connp->conn_ilg_lock, NULL, RW_DEFAULT, NULL);
connp->conn_ixa = kmem_zalloc(sizeof (ip_xmit_attr_t), kmflags);
if (connp->conn_ixa == NULL)
@@ -2119,6 +2145,7 @@ rawip_conn_destructor(void *buf, void *cdrarg)
mutex_destroy(&connp->conn_lock);
cv_destroy(&connp->conn_cv);
rw_destroy(&connp->conn_ilg_lock);
+ rw_destroy(&icmp->icmp_bpf_lock);
/* Can be NULL if constructor failed */
if (connp->conn_ixa != NULL) {
diff --git a/usr/src/uts/common/inet/ipclassifier.h b/usr/src/uts/common/inet/ipclassifier.h
index 89968826b3..70cff374a4 100644
--- a/usr/src/uts/common/inet/ipclassifier.h
+++ b/usr/src/uts/common/inet/ipclassifier.h
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
/*
@@ -299,7 +300,8 @@ struct conn_s {
conn_ipv6_recvpathmtu : 1, /* IPV6_RECVPATHMTU */
conn_mcbc_bind : 1, /* Bound to multi/broadcast */
- conn_pad_to_bit_31 : 12;
+ conn_reuseport : 1, /* SO_REUSEPORT state */
+ conn_pad_to_bit_31 : 11;
boolean_t conn_blocked; /* conn is flow-controlled */
diff --git a/usr/src/uts/common/inet/ipd/ipd.c b/usr/src/uts/common/inet/ipd/ipd.c
index 104603d840..22f2d79d24 100644
--- a/usr/src/uts/common/inet/ipd/ipd.c
+++ b/usr/src/uts/common/inet/ipd/ipd.c
@@ -9,7 +9,7 @@
* http://www.illumos.org/license/CDDL.
*/
/*
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2018, Joyent, Inc. All rights reserved.
*/
/*
@@ -222,7 +222,7 @@ typedef struct ipd_netstack {
net_handle_t ipdn_v6hdl; /* IPv4 net handle */
int ipdn_hooked; /* are hooks registered */
hook_t *ipdn_v4in; /* IPv4 traffic in hook */
- hook_t *ipdn_v4out; /* IPv4 traffice out hook */
+ hook_t *ipdn_v4out; /* IPv4 traffic out hook */
hook_t *ipdn_v6in; /* IPv6 traffic in hook */
hook_t *ipdn_v6out; /* IPv6 traffic out hook */
int ipdn_enabled; /* which perturbs are on */
@@ -613,7 +613,7 @@ ipd_toggle_delay(ipd_netstack_t *ins, uint32_t delay)
/*
* If ipd_check_hooks_failed, that must mean that we failed to set up
* the hooks, so we are going to effectively zero out and fail the
- * request to enable corruption.
+ * request to enable packet delays.
*/
if (rval != 0)
ins->ipdn_delay = 0;
diff --git a/usr/src/uts/common/inet/ipf/cfw.c b/usr/src/uts/common/inet/ipf/cfw.c
new file mode 100644
index 0000000000..941aeac328
--- /dev/null
+++ b/usr/src/uts/common/inet/ipf/cfw.c
@@ -0,0 +1,659 @@
+/*
+ * 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 2019, Joyent, Inc.
+ */
+
+/* IPF oddness for compilation in userland for IPF tests. */
+#if defined(KERNEL) || defined(_KERNEL)
+#undef KERNEL
+#undef _KERNEL
+#define KERNEL 1
+#define _KERNEL 1
+#endif
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#include "netinet/ip_compat.h"
+#ifdef USE_INET6
+#include <netinet/icmp6.h>
+#endif
+#include <netinet/tcpip.h>
+#include "netinet/ip_fil.h"
+#include "netinet/ip_nat.h"
+#include "netinet/ip_frag.h"
+#include "netinet/ip_state.h"
+#include "netinet/ip_proxy.h"
+#include "netinet/ip_auth.h"
+#include "netinet/ipf_stack.h"
+#ifdef IPFILTER_SCAN
+#include "netinet/ip_scan.h"
+#endif
+#ifdef IPFILTER_SYNC
+#include "netinet/ip_sync.h"
+#endif
+#include "netinet/ip_pool.h"
+#include "netinet/ip_htable.h"
+#ifdef IPFILTER_COMPILED
+#include "netinet/ip_rules.h"
+#endif
+#if defined(_KERNEL)
+#include <sys/sunddi.h>
+#endif
+
+#include "netinet/ipf_cfw.h"
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/cred.h>
+#include <sys/ddi.h>
+
+/*
+ * cfw == Cloud Firewall ==> routines for a global-zone data collector about
+ * ipf events for SmartOS. The only ones that CFW cares about are ones
+ * enforced by global-zone-controlled rulesets.
+ *
+ * The variable below is tied into the GZ-only ipf device /dev/ipfev, that
+ * flips this on when there is an open instance. This feature will also
+ * consume an fr_flag to have per-rule granularity.
+ */
+boolean_t ipf_cfwlog_enabled;
+
+/*
+ * Because ipf's test tools in $SRC/cmd insert all of these files, we need to
+ * stub out what we can vs. drag in even more headers and who knows what else.
+ */
+#ifdef _KERNEL
+
+/*
+ * CFW event ring buffer. Remember, this is for ALL ZONES because only a
+ * global-zone event-reader will be consuming these. In other words, it's
+ * not something to instantiate per-netstack.
+ *
+ * We may want to get more sophisticated and performant (e.g. per-processor),
+ * but for now keep the ring buffer simple and stupid.
+ * Must be a power of 2, to be bitmaskable, and must be countable by a uint_t
+ *
+ * Resizeable, see ipf_cfw_ring_resize() below.
+ */
+#define IPF_CFW_DEFAULT_RING_BUFS 1024
+#define IPF_CFW_MIN_RING_BUFS 8
+#define IPF_CFW_MAX_RING_BUFS (1U << 31U)
+
+/* Assume C's init-to-zero is sufficient for these types... */
+static kmutex_t cfw_ringlock;
+static kcondvar_t cfw_ringcv;
+
+static cfwev_t *cfw_ring; /* NULL by default. */
+static uint32_t cfw_ringsize; /* 0 by default, number of array elements. */
+static uint32_t cfw_ringmask; /* 0 by default. */
+
+/* If these are equal, we're either empty or full. */
+static uint_t cfw_ringstart, cfw_ringend;
+static boolean_t cfw_ringfull; /* Tell the difference here! */
+/* Bean-counters. */
+static uint64_t cfw_evreports;
+static uint64_t cfw_evdrops;
+
+/*
+ * Place an event in the CFW event ring buffer.
+ *
+ * For now, be simple and drop the oldest event if we overflow. We may wish to
+ * selectively drop older events based on type in the future.
+ */
+static void
+ipf_cfwev_report(cfwev_t *event)
+{
+ mutex_enter(&cfw_ringlock);
+ cfw_ring[cfw_ringend] = *event;
+ cfw_ringend++;
+ cfw_ringend &= cfw_ringmask;
+ if (cfw_ringfull) {
+ cfw_ringstart++;
+ cfw_ringstart &= cfw_ringmask;
+ ASSERT3U(cfw_ringstart, ==, cfw_ringend);
+ DTRACE_PROBE(ipf__cfw__evdrop);
+ cfw_evdrops++;
+ } else {
+ cfw_ringfull = (cfw_ringend == cfw_ringstart);
+ }
+ cfw_evreports++;
+ cv_broadcast(&cfw_ringcv);
+ mutex_exit(&cfw_ringlock);
+}
+
+/*
+ * Provide access to multiple CFW events that can allow copying straight from
+ * the ring buffer up to userland. Requires a callback (which could call
+ * uiomove() directly, OR to a local still-in-kernel buffer) that must do the
+ * data copying-out.
+ *
+ * Callback function is of the form:
+ *
+ * uint_t cfw_many_cb(cfwev_t *evptr, int num_avail, void *cbarg);
+ *
+ * The function must return how many events got consumed, which MUST be <= the
+ * number available. The function must ALSO UNDERSTAND that cfw_ringlock is
+ * held and must not be released during this time. The function may be called
+ * more than once, if the available buffers wrap-around OR "block" is set and
+ * we don't have enough buffers. If any callback returns 0, exit the function
+ * with however many were consumed.
+ *
+ * This function, like the callback, returns the number of events *CONSUMED*.
+ *
+ * . . .
+ *
+ * Tunables for ipf_cfwev_consume_many().
+ *
+ * If you wish to attempt to coalesce reads (to reduce the likelihood of one
+ * event at a time during high load) change the number of tries below to
+ * something not 0. Early experiments set this to 10.
+ *
+ * The wait between tries is in usecs in cfw_timeout_wait. The pessimal
+ * case for this is a timeout_wait-spaced trickle of one event at a time.
+ */
+uint_t cfw_timeout_tries = 0;
+uint_t cfw_timeout_wait = 10000; /* 10ms wait. */
+
+typedef struct uio_error_s {
+ struct uio *ue_uio;
+ int ue_error;
+} uio_error_t;
+
+static uint_t
+ipf_cfwev_consume_many(uint_t num_requested, boolean_t block,
+ cfwmanycb_t cfw_many_cb, void *cbarg)
+{
+ uint_t consumed = 0, cb_consumed, contig_size;
+ uint_t timeout_tries = cfw_timeout_tries;
+ boolean_t eintr = B_FALSE;
+
+ mutex_enter(&cfw_ringlock);
+
+ while (num_requested > 0) {
+ clock_t delta;
+
+ /* Silly reality checks */
+ ASSERT3U(cfw_ringstart, <, cfw_ringsize);
+ ASSERT3U(cfw_ringend, <, cfw_ringsize);
+
+ if (cfw_ringstart > cfw_ringend || cfw_ringfull) {
+ /* We have from ringstart to the buffer's end. */
+ contig_size = cfw_ringsize - cfw_ringstart;
+ } else if (cfw_ringstart < cfw_ringend) {
+ /* We have no potential wrapping at this time. */
+ contig_size = cfw_ringend - cfw_ringstart;
+ } else if (block && cv_wait_sig(&cfw_ringcv, &cfw_ringlock)) {
+ /* Maybe something to consume now, try again. */
+ continue;
+ } else {
+ /* Nothing (more) to consume, return! */
+ eintr = (block && consumed == 0);
+ break;
+ }
+
+ /* Less asked-for than what we needed. */
+ if (num_requested < contig_size)
+ contig_size = num_requested;
+
+ cb_consumed =
+ cfw_many_cb(&(cfw_ring[cfw_ringstart]), contig_size, cbarg);
+ ASSERT3U(cb_consumed, <=, contig_size);
+
+ cfw_ringstart += cb_consumed;
+ ASSERT3U(cfw_ringstart, <=, cfw_ringmask + 1);
+ cfw_ringstart &= cfw_ringmask; /* In case of wraparound. */
+ consumed += cb_consumed;
+ cfw_ringfull = (cfw_ringfull && cb_consumed == 0);
+ if (cb_consumed < contig_size) {
+ /*
+ * Callback returned less than given.
+ * This is likely a uio error, but we have
+ * something. Get out of here.
+ */
+ break;
+ }
+ ASSERT3U(cb_consumed, ==, contig_size);
+ num_requested -= contig_size;
+
+ if (num_requested == 0) {
+ /* All done! */
+ break;
+ }
+
+ if (cfw_ringstart != cfw_ringend) {
+ /*
+ * We wrapped around the end of the buffer, and
+ * we have more available to fill our request.
+ */
+ ASSERT0(cfw_ringstart);
+ ASSERT(!cfw_ringfull);
+ continue;
+ }
+
+ /*
+ * We obtained some of the events we requested, but not all.
+ * Since we have nothing to consume, wait *a little* longer.
+ */
+ if (timeout_tries == 0)
+ break; /* Don't bother... */
+ delta = drv_usectohz(cfw_timeout_wait);
+ timeout_tries--;
+
+ switch (cv_reltimedwait_sig(&cfw_ringcv, &cfw_ringlock, delta,
+ TR_CLOCK_TICK)) {
+ case 0:
+ /*
+ * Received signal! Return what we have OR if we have
+ * nothing, EINTR.
+ */
+ DTRACE_PROBE1(ipf__cfw__timedsignal, int, consumed);
+ eintr = (consumed == 0);
+ num_requested = 0;
+ break;
+ case -1:
+ /* Time reached! Bail with what we got. */
+ DTRACE_PROBE(ipf__cfw__timedexpired);
+ num_requested = 0;
+ break;
+ default:
+ /* Aha! We've got more! */
+ DTRACE_PROBE(ipf__cfw__moredata);
+ break;
+ }
+ }
+
+ mutex_exit(&cfw_ringlock);
+ if (eintr)
+ ((uio_error_t *)cbarg)->ue_error = EINTR;
+ return (consumed);
+}
+
+/*
+ * SmartOS likes using the zone's debug id. Make sure we squirrel that away in
+ * the ipf netstack instance if it's not there.
+ */
+static inline zoneid_t
+ifs_to_did(ipf_stack_t *ifs)
+{
+ if (ifs->ifs_zone_did == 0) {
+ zone_t *zone;
+
+ /*
+ * We can't get the zone_did at initialization time because
+ * most zone data isn't readily available then, cement the did
+ * in place now.
+ */
+ VERIFY3U(ifs->ifs_zone, !=, GLOBAL_ZONEID);
+ zone = zone_find_by_id(ifs->ifs_zone);
+ if (zone != NULL) {
+ ifs->ifs_zone_did = zone->zone_did;
+ zone_rele(zone);
+ }
+ /* Else we are either in shutdown or something weirder. */
+ }
+ return (ifs->ifs_zone_did);
+}
+
+/*
+ * ipf_block_cfwlog()
+ *
+ * Called by fr_check(). Record drop events for the global-zone data
+ * collector. Use rest-of-ipf-style names for the parameters.
+ */
+void
+ipf_block_cfwlog(frentry_t *fr, fr_info_t *fin, ipf_stack_t *ifs)
+{
+ cfwev_t event = {0};
+
+ /*
+ * We need a rule.
+ * Capture failure by using dtrace on this function's entry.
+ * 'ipf_block_cfwlog:entry /arg0 == NULL/ { printf("GOTCHA!\n"); }'
+ */
+ if (fr == NULL)
+ return;
+
+ event.cfwev_type = CFWEV_BLOCK;
+ event.cfwev_length = sizeof (event);
+ /*
+ * IPF code elsewhere does the cheesy single-flag check, even though
+ * there are two flags in a rule (one for in, one for out).
+ */
+ event.cfwev_direction = (fr->fr_flags & FR_INQUE) ?
+ CFWDIR_IN : CFWDIR_OUT;
+
+ event.cfwev_protocol = fin->fin_p;
+ /*
+ * NOTE: fin_*port is in host/native order, and ICMP info is here too.
+ */
+ event.cfwev_sport = htons(fin->fin_sport);
+ event.cfwev_dport = htons(fin->fin_dport);
+
+ switch (fin->fin_v) {
+ case IPV4_VERSION:
+ IN6_INADDR_TO_V4MAPPED(&fin->fin_src, &event.cfwev_saddr);
+ IN6_INADDR_TO_V4MAPPED(&fin->fin_dst, &event.cfwev_daddr);
+ break;
+ case IPV6_VERSION:
+ event.cfwev_saddr = fin->fin_src6.in6;
+ event.cfwev_daddr = fin->fin_dst6.in6;
+ break;
+ default:
+ /* We should never reach here, but mark it if we do. */
+ DTRACE_PROBE1(ipf__cfw__frinfo__badipversion, frinfo_t *, fin);
+ return;
+ }
+
+ /*
+ * uniqtime() is what ipf's GETKTIME() uses.
+ * If cfwev_tstamp needs to be sourced from elsewhere, fix that here.
+ */
+ uniqtime(&event.cfwev_tstamp);
+ event.cfwev_zonedid = ifs_to_did(ifs);
+ event.cfwev_ruleid = fin->fin_rule;
+ memcpy(event.cfwev_ruleuuid, fr->fr_uuid, sizeof (uuid_t));
+
+ ipf_cfwev_report(&event);
+}
+
+/*
+ * ipf_log_cfwlog()
+ *
+ * Twin of ipstate_log(), but records state events for the global-zone data
+ * collector.
+ */
+void
+ipf_log_cfwlog(struct ipstate *is, uint_t type, ipf_stack_t *ifs)
+{
+ cfwev_t event = {0};
+
+ switch (type) {
+ case ISL_NEW:
+ case ISL_CLONE:
+ event.cfwev_type = CFWEV_BEGIN;
+ break;
+ case ISL_EXPIRE:
+ case ISL_FLUSH:
+ case ISL_REMOVE:
+ case ISL_KILLED:
+ case ISL_ORPHAN:
+ /*
+ * We don't care about session disappearances in CFW logging
+ * for now. (Possible future: CFWEV_END)
+ */
+ return;
+ default:
+ event.cfwev_type = CFWEV_BLOCK;
+ break;
+ }
+
+ /*
+ * IPF code elsewhere does the cheesy single-flag check, even though
+ * there are two flags in a rule (one for in, one for out). Follow
+ * suit here.
+ */
+ event.cfwev_length = sizeof (event);
+ ASSERT(is->is_rule != NULL);
+ event.cfwev_direction = (is->is_rule->fr_flags & FR_INQUE) ?
+ CFWDIR_IN : CFWDIR_OUT;
+ event.cfwev_protocol = is->is_p;
+ switch (is->is_p) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ /* NOTE: is_*port is in network order. */
+ event.cfwev_sport = is->is_sport;
+ event.cfwev_dport = is->is_dport;
+ break;
+ case IPPROTO_ICMP:
+ case IPPROTO_ICMPV6:
+ /* Scribble the ICMP type in sport... */
+ event.cfwev_sport = is->is_icmp.ici_type;
+ break;
+ /* Other protocols leave the event's port fields empty. */
+ }
+
+ switch(is->is_v) {
+ case IPV4_VERSION:
+ IN6_INADDR_TO_V4MAPPED(&is->is_src.in4, &event.cfwev_saddr);
+ IN6_INADDR_TO_V4MAPPED(&is->is_dst.in4, &event.cfwev_daddr);
+ break;
+ case IPV6_VERSION:
+ event.cfwev_saddr = is->is_src.in6;
+ event.cfwev_daddr = is->is_dst.in6;
+ break;
+ default:
+ /* Can't parse addresses if we don't know the version. Drop. */
+ DTRACE_PROBE1(ipf__cfw__ipstate__badipversion,
+ struct ipstate *, is);
+ return;
+ }
+
+ /*
+ * uniqtime() is what ipf's GETKTIME() uses.
+ * If cfwev_tstamp needs to be sourced from elsewhere, fix that here.
+ */
+ uniqtime(&event.cfwev_tstamp);
+ event.cfwev_zonedid = ifs_to_did(ifs);
+ event.cfwev_ruleid = is->is_rulen;
+ memcpy(event.cfwev_ruleuuid, is->is_uuid, sizeof (uuid_t));
+
+ ipf_cfwev_report(&event);
+}
+
+/*
+ * Callback routine we use for ipf_cfwev_consume_many().
+ * Returning 0 means error indication.
+ */
+static uint_t
+cfwlog_read_manycb(cfwev_t *evptr, uint_t num_avail, void *cbarg)
+{
+ uio_error_t *ue = (uio_error_t *)cbarg;
+
+ ASSERT(MUTEX_HELD(&cfw_ringlock));
+
+ if (ue->ue_error != 0)
+ return (0);
+
+ ue->ue_error = uiomove((caddr_t)evptr, num_avail * sizeof (*evptr),
+ UIO_READ, ue->ue_uio);
+ if (ue->ue_error != 0)
+ return (0);
+
+ return (num_avail);
+}
+
+/*
+ * Resize the CFW event ring buffer.
+ *
+ * The caller must ensure the new size is a power of 2 between
+ * IPF_CFW_{MIN,MAX}_RING_BUFS (inclusive) or the special values
+ * IPF_CFW_RING_ALLOCATE (first-time creation) or IPF_CFW_RING_DESTROY
+ * (netstack-unload destruction).
+ *
+ * Everything in the current ring will be destroyed (and reported as a drop)
+ * upon resize.
+ */
+int
+ipf_cfw_ring_resize(uint32_t newsize)
+{
+ ASSERT(MUTEX_HELD(&cfw_ringlock) || newsize == IPF_CFW_RING_ALLOCATE ||
+ newsize == IPF_CFW_RING_DESTROY);
+
+ if (newsize == IPF_CFW_RING_ALLOCATE) {
+ if (cfw_ring != NULL)
+ return (EBUSY);
+ newsize = IPF_CFW_DEFAULT_RING_BUFS;
+ /* Fall through to allocating a new ring buffer. */
+ } else {
+ /* We may be called during error cleanup, so be liberal here. */
+ if ((cfw_ring == NULL && newsize == IPF_CFW_RING_DESTROY) ||
+ newsize == cfw_ringsize) {
+ return (0);
+ }
+ kmem_free(cfw_ring, cfw_ringsize * sizeof (cfwev_t));
+ cfw_ring = NULL;
+ if (cfw_ringfull) {
+ cfw_evdrops += cfw_ringsize;
+ } else if (cfw_ringstart > cfw_ringend) {
+ cfw_evdrops += cfw_ringend +
+ (cfw_ringsize - cfw_ringstart);
+ } else {
+ cfw_evdrops += cfw_ringend - cfw_ringstart;
+ }
+ cfw_ringsize = cfw_ringmask = cfw_ringstart = cfw_ringend = 0;
+ cfw_ringfull = B_FALSE;
+
+ if (newsize == IPF_CFW_RING_DESTROY)
+ return (0);
+ /*
+ * Keep the reports & drops around because if we're just
+ * resizing, we need to know what we lost.
+ */
+ }
+
+ ASSERT(ISP2(newsize));
+ cfw_ring = kmem_alloc(newsize * sizeof (cfwev_t), KM_SLEEP);
+ /* KM_SLEEP means we always succeed. */
+ cfw_ringsize = newsize;
+ cfw_ringmask = cfw_ringsize - 1;
+
+ return (0);
+}
+
+/*
+ * ioctl handler for /dev/ipfev. Only supports SIOCIPFCFWCFG (get data
+ * collector statistics and configuration), and SIOCIPFCFWNEWSZ (resize the
+ * event ring buffer).
+ */
+/* ARGSUSED */
+int
+ipf_cfwlog_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *cp,
+ int *rp)
+{
+ ipfcfwcfg_t cfginfo;
+ int error;
+
+ if (cmd != SIOCIPFCFWCFG && cmd != SIOCIPFCFWNEWSZ)
+ return (EIO);
+
+ if (crgetzoneid(cp) != GLOBAL_ZONEID)
+ return (EACCES);
+
+ error = COPYIN((caddr_t)data, (caddr_t)&cfginfo, sizeof (cfginfo));
+ if (error != 0)
+ return (EFAULT);
+
+ cfginfo.ipfcfwc_maxevsize = sizeof (cfwev_t);
+ mutex_enter(&cfw_ringlock);
+ cfginfo.ipfcfwc_evreports = cfw_evreports;
+ if (cmd == SIOCIPFCFWNEWSZ) {
+ uint32_t newsize = cfginfo.ipfcfwc_evringsize;
+
+ /* Do ioctl parameter checking here, then call the resizer. */
+ if (newsize < IPF_CFW_MIN_RING_BUFS ||
+ newsize > IPF_CFW_MAX_RING_BUFS || !ISP2(newsize)) {
+ error = EINVAL;
+ } else {
+ error = ipf_cfw_ring_resize(cfginfo.ipfcfwc_evringsize);
+ }
+ } else {
+ error = 0;
+ }
+ /* Both cfw_evdrops and cfw_ringsize are affected by resize. */
+ cfginfo.ipfcfwc_evdrops = cfw_evdrops;
+ cfginfo.ipfcfwc_evringsize = cfw_ringsize;
+ mutex_exit(&cfw_ringlock);
+
+ if (error != 0)
+ return (error);
+
+ error = COPYOUT((caddr_t)&cfginfo, (caddr_t)data, sizeof (cfginfo));
+ if (error != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+/*
+ * Send events up via /dev/ipfev reads. Will return only complete events.
+ */
+/* ARGSUSED */
+int
+ipf_cfwlog_read(dev_t dev, struct uio *uio, cred_t *cp)
+{
+ uint_t requested, consumed;
+ uio_error_t ue = {uio, 0};
+ boolean_t block;
+
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_resid < sizeof (cfwev_t))
+ return (EINVAL);
+
+ block = ((uio->uio_fmode & (FNDELAY | FNONBLOCK)) == 0);
+ requested = uio->uio_resid / sizeof (cfwev_t);
+
+ /*
+ * As stated earlier, ipf_cfwev_consume_many() takes a callback.
+ * The callback may be called multiple times before we return.
+ * The callback will execute uiomove().
+ */
+ consumed = ipf_cfwev_consume_many(requested, block, cfwlog_read_manycb,
+ &ue);
+ ASSERT3U(consumed, <=, requested);
+ if (!block && consumed == 0 && ue.ue_error == 0) {
+ /* No data available. */
+ ue.ue_error = EWOULDBLOCK;
+ } else if (ue.ue_error != 0 && ue.ue_error != EINTR) {
+ /*
+ * We had a problem that wasn't simply a
+ * case of cv_wait_sig() receiving a signal.
+ */
+ DTRACE_PROBE1(ipf__cfw__uiodiscard, int, consumed);
+ mutex_enter(&cfw_ringlock);
+ cfw_evdrops += consumed;
+ mutex_exit(&cfw_ringlock);
+ }
+ return (ue.ue_error);
+}
+
+#else /* _KERNEL */
+
+/* Blank stubs to satisfy userland's test compilations. */
+
+int
+ipf_cfw_ring_resize(uint32_t a)
+{
+ return (0);
+}
+
+void
+ipf_log_cfwlog(struct ipstate *a, uint_t b, ipf_stack_t *c)
+{
+}
+
+void
+ipf_block_cfwlog(frentry_t *a, fr_info_t *b, ipf_stack_t *c)
+{
+}
+
+#endif /* _KERNEL */
diff --git a/usr/src/uts/common/inet/ipf/fil.c b/usr/src/uts/common/inet/ipf/fil.c
index 78980be106..48fa6e7325 100644
--- a/usr/src/uts/common/inet/ipf/fil.c
+++ b/usr/src/uts/common/inet/ipf/fil.c
@@ -5,7 +5,7 @@
*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#if defined(KERNEL) || defined(_KERNEL)
@@ -2588,6 +2588,9 @@ ipf_stack_t *ifs;
}
#endif
+ if (IFS_CFWLOG(ifs, fr) && FR_ISBLOCK(pass))
+ ipf_block_cfwlog(fr, fin, ifs);
+
/*
* The FI_STATE flag is cleared here so that calling fr_checkstate
* will work when called from inside of fr_fastroute. Although
diff --git a/usr/src/uts/common/inet/ipf/ip_fil_solaris.c b/usr/src/uts/common/inet/ipf/ip_fil_solaris.c
index c9d5f03e13..0d34e0fce3 100644
--- a/usr/src/uts/common/inet/ipf/ip_fil_solaris.c
+++ b/usr/src/uts/common/inet/ipf/ip_fil_solaris.c
@@ -5,7 +5,7 @@
*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
#if !defined(lint)
@@ -85,6 +85,14 @@ static int ipf_hook6_loop_out __P((hook_event_token_t, hook_data_t,
static int ipf_hook6_loop_in __P((hook_event_token_t, hook_data_t,
void *));
static int ipf_hook6 __P((hook_data_t, int, int, void *));
+static int ipf_hookvndl3v4_in __P((hook_event_token_t, hook_data_t,
+ void *));
+static int ipf_hookvndl3v6_in __P((hook_event_token_t, hook_data_t,
+ void *));
+static int ipf_hookvndl3v4_out __P((hook_event_token_t, hook_data_t,
+ void *));
+static int ipf_hookvndl3v6_out __P((hook_event_token_t, hook_data_t,
+ void *));
static int ipf_hookviona_in __P((hook_event_token_t, hook_data_t, void *));
static int ipf_hookviona_out __P((hook_event_token_t, hook_data_t,
@@ -116,7 +124,7 @@ u_long *ip_forwarding = NULL;
#endif
vmem_t *ipf_minor; /* minor number arena */
-void *ipf_state; /* DDI state */
+void *ipf_state; /* DDI state */
/*
* GZ-controlled and per-zone stacks:
@@ -141,28 +149,38 @@ void *ipf_state; /* DDI state */
*/
/* IPv4 hook names */
-char *hook4_nicevents = "ipfilter_hook4_nicevents";
-char *hook4_nicevents_gz = "ipfilter_hook4_nicevents_gz";
-char *hook4_in = "ipfilter_hook4_in";
-char *hook4_in_gz = "ipfilter_hook4_in_gz";
-char *hook4_out = "ipfilter_hook4_out";
-char *hook4_out_gz = "ipfilter_hook4_out_gz";
-char *hook4_loop_in = "ipfilter_hook4_loop_in";
-char *hook4_loop_in_gz = "ipfilter_hook4_loop_in_gz";
-char *hook4_loop_out = "ipfilter_hook4_loop_out";
-char *hook4_loop_out_gz = "ipfilter_hook4_loop_out_gz";
+char *hook4_nicevents = "ipfilter_hook4_nicevents";
+char *hook4_nicevents_gz = "ipfilter_hook4_nicevents_gz";
+char *hook4_in = "ipfilter_hook4_in";
+char *hook4_in_gz = "ipfilter_hook4_in_gz";
+char *hook4_out = "ipfilter_hook4_out";
+char *hook4_out_gz = "ipfilter_hook4_out_gz";
+char *hook4_loop_in = "ipfilter_hook4_loop_in";
+char *hook4_loop_in_gz = "ipfilter_hook4_loop_in_gz";
+char *hook4_loop_out = "ipfilter_hook4_loop_out";
+char *hook4_loop_out_gz = "ipfilter_hook4_loop_out_gz";
/* IPv6 hook names */
-char *hook6_nicevents = "ipfilter_hook6_nicevents";
-char *hook6_nicevents_gz = "ipfilter_hook6_nicevents_gz";
-char *hook6_in = "ipfilter_hook6_in";
-char *hook6_in_gz = "ipfilter_hook6_in_gz";
-char *hook6_out = "ipfilter_hook6_out";
-char *hook6_out_gz = "ipfilter_hook6_out_gz";
-char *hook6_loop_in = "ipfilter_hook6_loop_in";
-char *hook6_loop_in_gz = "ipfilter_hook6_loop_in_gz";
-char *hook6_loop_out = "ipfilter_hook6_loop_out";
-char *hook6_loop_out_gz = "ipfilter_hook6_loop_out_gz";
+char *hook6_nicevents = "ipfilter_hook6_nicevents";
+char *hook6_nicevents_gz = "ipfilter_hook6_nicevents_gz";
+char *hook6_in = "ipfilter_hook6_in";
+char *hook6_in_gz = "ipfilter_hook6_in_gz";
+char *hook6_out = "ipfilter_hook6_out";
+char *hook6_out_gz = "ipfilter_hook6_out_gz";
+char *hook6_loop_in = "ipfilter_hook6_loop_in";
+char *hook6_loop_in_gz = "ipfilter_hook6_loop_in_gz";
+char *hook6_loop_out = "ipfilter_hook6_loop_out";
+char *hook6_loop_out_gz = "ipfilter_hook6_loop_out_gz";
+
+/* vnd IPv4/v6 hook names */
+char *hook4_vnd_in = "ipfilter_hookvndl3v4_in";
+char *hook4_vnd_in_gz = "ipfilter_hookvndl3v4_in_gz";
+char *hook6_vnd_in = "ipfilter_hookvndl3v6_in";
+char *hook6_vnd_in_gz = "ipfilter_hookvndl3v6_in_gz";
+char *hook4_vnd_out = "ipfilter_hookvndl3v4_out";
+char *hook4_vnd_out_gz = "ipfilter_hookvndl3v4_out_gz";
+char *hook6_vnd_out = "ipfilter_hookvndl3v6_out";
+char *hook6_vnd_out_gz = "ipfilter_hookvndl3v6_out_gz";
/* viona hook names */
char *hook_viona_in = "ipfilter_hookviona_in";
@@ -170,6 +188,39 @@ char *hook_viona_in_gz = "ipfilter_hookviona_in_gz";
char *hook_viona_out = "ipfilter_hookviona_out";
char *hook_viona_out_gz = "ipfilter_hookviona_out_gz";
+/*
+ * For VIONA. The net_{instance,protocol}_notify_register() functions only
+ * deal with per-callback-function granularity. We need two wrapper functions
+ * for GZ-controlled and per-zone instances.
+ */
+static int
+ipf_hook_instance_notify_gz(hook_notify_cmd_t command, void *arg,
+ const char *netid, const char *dummy, const char *instance)
+{
+ return (ipf_hook_instance_notify(command, arg, netid, dummy, instance));
+}
+
+static int
+ipf_hook_instance_notify_ngz(hook_notify_cmd_t command, void *arg,
+ const char *netid, const char *dummy, const char *instance)
+{
+ return (ipf_hook_instance_notify(command, arg, netid, dummy, instance));
+}
+
+static int
+ipf_hook_protocol_notify_gz(hook_notify_cmd_t command, void *arg,
+ const char *name, const char *dummy, const char *he_name)
+{
+ return (ipf_hook_protocol_notify(command, arg, name, dummy, he_name));
+}
+
+static int
+ipf_hook_protocol_notify_ngz(hook_notify_cmd_t command, void *arg,
+ const char *name, const char *dummy, const char *he_name)
+{
+ return (ipf_hook_protocol_notify(command, arg, name, dummy, he_name));
+}
+
/* ------------------------------------------------------------------------ */
/* Function: ipldetach */
/* Returns: int - 0 == success, else error. */
@@ -267,10 +318,36 @@ ipf_stack_t *ifs;
}
/*
+ * Remove VND hooks
+ */
+ if (ifs->ifs_ipf_vndl3v4 != NULL) {
+ UNDO_HOOK(ifs_ipf_vndl3v4, ifs_hookvndl3v4_physical_in,
+ NH_PHYSICAL_IN, ifs_ipfhookvndl3v4_in);
+ UNDO_HOOK(ifs_ipf_vndl3v4, ifs_hookvndl3v4_physical_out,
+ NH_PHYSICAL_OUT, ifs_ipfhookvndl3v4_out);
+
+ if (net_protocol_release(ifs->ifs_ipf_vndl3v4) != 0)
+ goto detach_failed;
+ ifs->ifs_ipf_vndl3v4 = NULL;
+ }
+
+ if (ifs->ifs_ipf_vndl3v6 != NULL) {
+ UNDO_HOOK(ifs_ipf_vndl3v6, ifs_hookvndl3v6_physical_in,
+ NH_PHYSICAL_IN, ifs_ipfhookvndl3v6_in);
+ UNDO_HOOK(ifs_ipf_vndl3v6, ifs_hookvndl3v6_physical_out,
+ NH_PHYSICAL_OUT, ifs_ipfhookvndl3v6_out);
+
+ if (net_protocol_release(ifs->ifs_ipf_vndl3v6) != 0)
+ goto detach_failed;
+ ifs->ifs_ipf_vndl3v6 = NULL;
+ }
+
+ /*
* Remove notification of viona hooks
*/
net_instance_notify_unregister(ifs->ifs_netid,
- ipf_hook_instance_notify);
+ ifs->ifs_gz_controlled ? ipf_hook_instance_notify_gz :
+ ipf_hook_instance_notify_ngz);
#undef UNDO_HOOK
@@ -278,6 +355,10 @@ ipf_stack_t *ifs;
* Normally, viona will unregister itself before ipldetach() is called,
* so these will be no-ops, but out of caution, we try to make sure
* we've removed any of our references.
+ *
+ * For now, the _gz and _ngz versions are both wrappers to what's
+ * below. Just call it directly, but if that changes fix here as
+ * well.
*/
(void) ipf_hook_protocol_notify(HN_UNREGISTER, ifs, Hn_VIONA, NULL,
NH_PHYSICAL_IN);
@@ -295,6 +376,10 @@ ipf_stack_t *ifs;
* traced, we pass the same value the nethook framework would
* pass, even though the callback does not currently use the
* value.
+ *
+ * For now, the _gz and _ngz versions are both wrappers to
+ * what's below. Just call it directly, but if that changes
+ * fix here as well.
*/
(void) ipf_hook_instance_notify(HN_UNREGISTER, ifs, netidstr,
NULL, Hn_VIONA);
@@ -495,6 +580,49 @@ ipf_stack_t *ifs;
}
/*
+ * Add VND INET hooks
+ */
+ ifs->ifs_ipf_vndl3v4 = net_protocol_lookup(id, NHF_VND_INET);
+ if (ifs->ifs_ipf_vndl3v4 == NULL)
+ goto hookup_failed;
+
+ HOOK_INIT_GZ_BEFORE(ifs->ifs_ipfhookvndl3v4_in, ipf_hookvndl3v4_in,
+ hook4_vnd_in, hook4_vnd_in_gz, ifs);
+ HOOK_INIT_GZ_AFTER(ifs->ifs_ipfhookvndl3v4_out, ipf_hookvndl3v4_out,
+ hook4_vnd_out, hook4_vnd_out_gz, ifs);
+ ifs->ifs_hookvndl3v4_physical_in = (net_hook_register(ifs->ifs_ipf_vndl3v4,
+ NH_PHYSICAL_IN, ifs->ifs_ipfhookvndl3v4_in) == 0);
+ if (!ifs->ifs_hookvndl3v4_physical_in)
+ goto hookup_failed;
+
+ ifs->ifs_hookvndl3v4_physical_out = (net_hook_register(ifs->ifs_ipf_vndl3v4,
+ NH_PHYSICAL_OUT, ifs->ifs_ipfhookvndl3v4_out) == 0);
+ if (!ifs->ifs_hookvndl3v4_physical_out)
+ goto hookup_failed;
+
+
+ /*
+ * VND INET6 hooks
+ */
+ ifs->ifs_ipf_vndl3v6 = net_protocol_lookup(id, NHF_VND_INET6);
+ if (ifs->ifs_ipf_vndl3v6 == NULL)
+ goto hookup_failed;
+
+ HOOK_INIT_GZ_BEFORE(ifs->ifs_ipfhookvndl3v6_in, ipf_hookvndl3v6_in,
+ hook6_vnd_in, hook6_vnd_in_gz, ifs);
+ HOOK_INIT_GZ_AFTER(ifs->ifs_ipfhookvndl3v6_out, ipf_hookvndl3v6_out,
+ hook6_vnd_out, hook6_vnd_out_gz, ifs);
+ ifs->ifs_hookvndl3v6_physical_in = (net_hook_register(ifs->ifs_ipf_vndl3v6,
+ NH_PHYSICAL_IN, ifs->ifs_ipfhookvndl3v6_in) == 0);
+ if (!ifs->ifs_hookvndl3v6_physical_in)
+ goto hookup_failed;
+
+ ifs->ifs_hookvndl3v6_physical_out = (net_hook_register(ifs->ifs_ipf_vndl3v6,
+ NH_PHYSICAL_OUT, ifs->ifs_ipfhookvndl3v6_out) == 0);
+ if (!ifs->ifs_hookvndl3v6_physical_out)
+ goto hookup_failed;
+
+ /*
* VIONA INET hooks. While the nethook framework allows us to register
* hooks for events that haven't been registered yet, we instead
* register and unregister our hooks in response to notifications
@@ -504,9 +632,15 @@ ipf_stack_t *ifs;
* is unloaded, the viona module cannot later re-register them if it
* gets reloaded. As the ip, vnd, and ipf modules are rarely unloaded
* even on DEBUG kernels, they do not experience this issue.
+ *
+ * Today, the per-zone ones don't matter for a BHYVE-branded zone, BUT
+ * the ipf_hook_protocol_notify() function is GZ vs. per-zone aware.
+ * Employ two different versions of ipf_hook_instance_notify(), one for
+ * the GZ-controlled, and one for the per-zone one.
*/
- if (net_instance_notify_register(id, ipf_hook_instance_notify,
- ifs) != 0)
+ if (net_instance_notify_register(id, ifs->ifs_gz_controlled ?
+ ipf_hook_instance_notify_gz : ipf_hook_instance_notify_ngz, ifs) !=
+ 0)
goto hookup_failed;
/*
@@ -688,6 +822,7 @@ ipf_hook_instance_notify(hook_notify_cmd_t command, void *arg,
{
ipf_stack_t *ifs = arg;
int ret = 0;
+ const boolean_t gz = ifs->ifs_gz_controlled;
/* We currently only care about viona hooks */
if (strcmp(instance, Hn_VIONA) != 0)
@@ -705,14 +840,16 @@ ipf_hook_instance_notify(hook_notify_cmd_t command, void *arg,
return (EPROTONOSUPPORT);
ret = net_protocol_notify_register(ifs->ifs_ipf_viona,
- ipf_hook_protocol_notify, ifs);
+ gz ? ipf_hook_protocol_notify_gz :
+ ipf_hook_protocol_notify_ngz, ifs);
VERIFY(ret == 0 || ret == ESHUTDOWN);
break;
case HN_UNREGISTER:
if (ifs->ifs_ipf_viona == NULL)
break;
VERIFY0(net_protocol_notify_unregister(ifs->ifs_ipf_viona,
- ipf_hook_protocol_notify));
+ gz ? ipf_hook_protocol_notify_gz :
+ ipf_hook_protocol_notify_ngz));
VERIFY0(net_protocol_release(ifs->ifs_ipf_viona));
ifs->ifs_ipf_viona = NULL;
break;
@@ -821,6 +958,9 @@ int *rp;
return ENXIO;
unit = isp->ipfs_minor;
+ if (unit == IPL_LOGEV)
+ return (ipf_cfwlog_ioctl(dev, cmd, data, mode, cp, rp));
+
zid = crgetzoneid(cp);
if (cmd == SIOCIPFZONESET) {
if (zid == GLOBAL_ZONEID)
@@ -1129,14 +1269,14 @@ ipf_stack_t *ifs;
{
net_handle_t nif;
- if (v == 4)
- nif = ifs->ifs_ipf_ipv4;
- else if (v == 6)
- nif = ifs->ifs_ipf_ipv6;
- else
- return 0;
-
- return (net_phylookup(nif, name));
+ if (v == 4)
+ nif = ifs->ifs_ipf_ipv4;
+ else if (v == 6)
+ nif = ifs->ifs_ipf_ipv6;
+ else
+ return 0;
+
+ return (net_phylookup(nif, name));
}
/*
@@ -1161,11 +1301,35 @@ cred_t *cred;
if (IPL_LOGMAX < min)
return ENXIO;
+ /* Special-case ipfev: global-zone-open only. */
+ if (min == IPL_LOGEV) {
+ if (crgetzoneid(cred) != GLOBAL_ZONEID)
+ return (ENXIO);
+ /*
+ * Else enable the CFW logging of events.
+ * NOTE: For now, we only allow one open at a time.
+ * Use atomic_cas to confirm/deny. And also for now,
+ * assume sizeof (boolean_t) == sizeof (uint_t).
+ *
+ * Per the *_{refrele,REFRELE}() in other parts of inet,
+ * ensure all loads/stores complete before calling cas.
+ * membar_exit() does this.
+ */
+ membar_exit();
+ if (atomic_cas_uint(&ipf_cfwlog_enabled, 0, 1) != 0)
+ return (EBUSY);
+ }
+
minor = (minor_t)(uintptr_t)vmem_alloc(ipf_minor, 1,
VM_BESTFIT | VM_SLEEP);
if (ddi_soft_state_zalloc(ipf_state, minor) != 0) {
vmem_free(ipf_minor, (void *)(uintptr_t)minor, 1);
+ if (min == IPL_LOGEV) {
+ /* See above... */
+ membar_exit();
+ VERIFY(atomic_cas_uint(&ipf_cfwlog_enabled, 1, 0) == 1);
+ }
return ENXIO;
}
@@ -1187,6 +1351,7 @@ int flags, otype;
cred_t *cred;
{
minor_t min = getminor(dev);
+ ipf_devstate_t *isp;
#ifdef IPFDEBUG
cmn_err(CE_CONT, "iplclose(%x,%x,%x,%x)\n", dev, flags, otype, cred);
@@ -1195,6 +1360,15 @@ cred_t *cred;
if (IPL_LOGMAX < min)
return ENXIO;
+ isp = ddi_get_soft_state(ipf_state, min);
+ if (isp != NULL && isp->ipfs_minor == IPL_LOGEV) {
+ /*
+ * Disable CFW logging. See iplopen() for details.
+ */
+ membar_exit();
+ VERIFY(atomic_cas_uint(&ipf_cfwlog_enabled, 1, 0) == 1);
+ }
+
ddi_soft_state_free(ipf_state, min);
vmem_free(ipf_minor, (void *)(uintptr_t)min, 1);
@@ -1225,6 +1399,8 @@ cred_t *cp;
return ENXIO;
unit = isp->ipfs_minor;
+ if (unit == IPL_LOGEV)
+ return (ipf_cfwlog_read(dev, uio, cp));
/*
* ipf_find_stack returns with a read lock on ifs_ipf_global
@@ -1277,6 +1453,9 @@ cred_t *cp;
return ENXIO;
unit = isp->ipfs_minor;
+ if (unit == IPL_LOGEV)
+ return (EIO); /* ipfev doesn't support write yet. */
+
/*
* ipf_find_stack returns with a read lock on ifs_ipf_global
*/
@@ -2068,8 +2247,11 @@ frdest_t *fdp;
return (-1);
}
- /* Check the src here, fin_ifp is the src interface. */
- if (!(fin->fin_flx & FI_GENERATED) &&
+ /*
+ * If we're forwarding (vs. injecting), check the src here, fin_ifp is
+ * the src interface.
+ */
+ if (fdp != NULL && !(fin->fin_flx & FI_GENERATED) &&
!fr_forwarding_enabled((phy_if_t)fin->fin_ifp, net_data_p)) {
return (-1);
}
@@ -2138,8 +2320,8 @@ frdest_t *fdp;
inj->ni_physical = net_routeto(net_data_p, sinp, NULL);
}
- /* we're checking the destination here */
- if (!(fin->fin_flx & FI_GENERATED) &&
+ /* If we're forwarding (vs. injecting), check the destinatation here. */
+ if (fdp != NULL && !(fin->fin_flx & FI_GENERATED) &&
!fr_forwarding_enabled(inj->ni_physical, net_data_p)) {
goto bad_fastroute;
}
@@ -2355,6 +2537,42 @@ int ipf_hook_ether(hook_event_token_t token, hook_data_t info, void *arg,
}
/* ------------------------------------------------------------------------ */
+/* Function: ipf_hookvndl3_in */
+/* Returns: int - 0 == packet ok, else problem, free packet if not done */
+/* Parameters: event(I) - pointer to event */
+/* info(I) - pointer to hook information for firewalling */
+/* */
+/* The vnd hooks are private hooks to ON. They represents a layer 2 */
+/* datapath generally used to implement virtual machines. The driver sends */
+/* along L3 packets of either type IP or IPv6. The ethertype to distinguish */
+/* them is in the upper 16 bits while the remaining bits are the */
+/* traditional packet hook flags. */
+/* */
+/* They end up calling the appropriate traditional ip hooks. */
+/* ------------------------------------------------------------------------ */
+/*ARGSUSED*/
+int ipf_hookvndl3v4_in(hook_event_token_t token, hook_data_t info, void *arg)
+{
+ return ipf_hook4_in(token, info, arg);
+}
+
+int ipf_hookvndl3v6_in(hook_event_token_t token, hook_data_t info, void *arg)
+{
+ return ipf_hook6_in(token, info, arg);
+}
+
+/*ARGSUSED*/
+int ipf_hookvndl3v4_out(hook_event_token_t token, hook_data_t info, void *arg)
+{
+ return ipf_hook4_out(token, info, arg);
+}
+
+int ipf_hookvndl3v6_out(hook_event_token_t token, hook_data_t info, void *arg)
+{
+ return ipf_hook6_out(token, info, arg);
+}
+
+/* ------------------------------------------------------------------------ */
/* Function: ipf_hookviona_{in,out} */
/* Returns: int - 0 == packet ok, else problem, free packet if not done */
/* Parameters: event(I) - pointer to event */
@@ -3120,16 +3338,16 @@ fr_info_t *fin;
/* both IP versions. The details are going to be explained here. */
/* */
/* The packet looks as follows: */
-/* xxx | IP hdr | IP payload ... | */
-/* ^ ^ ^ ^ */
-/* | | | | */
+/* xxx | IP hdr | IP payload ... | */
+/* ^ ^ ^ ^ */
+/* | | | | */
/* | | | fin_m->b_wptr = fin->fin_dp + fin->fin_dlen */
/* | | | */
/* | | `- fin_m->fin_dp (in case of IPv4 points to L4 header) */
/* | | */
/* | `- fin_m->b_rptr + fin_ipoff (fin_ipoff is most likely 0 in case */
/* | of loopback) */
-/* | */
+/* | */
/* `- fin_m->b_rptr - points to L2 header in case of physical NIC */
/* */
/* All relevant IP headers are pulled up into the first mblk. It happened */
diff --git a/usr/src/uts/common/inet/ipf/ip_log.c b/usr/src/uts/common/inet/ipf/ip_log.c
index 584ee42d9a..b70e320def 100644
--- a/usr/src/uts/common/inet/ipf/ip_log.c
+++ b/usr/src/uts/common/inet/ipf/ip_log.c
@@ -8,7 +8,7 @@
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#include <sys/param.h>
@@ -373,9 +373,11 @@ u_int flags;
if (fin->fin_fr != NULL) {
ipfl.fl_loglevel = fin->fin_fr->fr_loglevel;
ipfl.fl_logtag = fin->fin_fr->fr_logtag;
+ bcopy(fin->fin_fr->fr_uuid, ipfl.fl_uuid, sizeof (uuid_t));
} else {
ipfl.fl_loglevel = 0xffff;
ipfl.fl_logtag = FR_NOLOGTAG;
+ bzero(ipfl.fl_uuid, sizeof (uuid_t));
}
if (fin->fin_nattag != NULL)
bcopy(fin->fin_nattag, (void *)&ipfl.fl_nattag,
diff --git a/usr/src/uts/common/inet/ipf/ip_state.c b/usr/src/uts/common/inet/ipf/ip_state.c
index 184f8775b6..a45bcbfdaf 100644
--- a/usr/src/uts/common/inet/ipf/ip_state.c
+++ b/usr/src/uts/common/inet/ipf/ip_state.c
@@ -5,7 +5,7 @@
*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#if defined(KERNEL) || defined(_KERNEL)
@@ -108,6 +108,7 @@ struct file;
# include <sys/systm.h>
# endif
#endif
+#include <sys/uuid.h>
/* END OF INCLUDES */
@@ -1445,6 +1446,7 @@ u_int flags;
is->is_sti.tqe_flags |= TQE_RULEBASED;
}
is->is_tag = fr->fr_logtag;
+ memcpy(is->is_uuid, fr->fr_uuid, sizeof (uuid_t));
is->is_ifp[(out << 1) + 1] = fr->fr_ifas[1];
is->is_ifp[(1 - out) << 1] = fr->fr_ifas[2];
@@ -1524,6 +1526,9 @@ u_int flags;
if (ifs->ifs_ipstate_logging)
ipstate_log(is, ISL_NEW, ifs);
+ if (IFS_CFWLOG(ifs, is->is_rule))
+ ipf_log_cfwlog(is, ISL_NEW, ifs);
+
RWLOCK_EXIT(&ifs->ifs_ipf_state);
fin->fin_rev = IP6_NEQ(&is->is_dst, &fin->fin_daddr);
fin->fin_flx |= FI_STATE;
@@ -2314,6 +2319,8 @@ u_32_t cmask;
is->is_flags &= ~(SI_W_SPORT|SI_W_DPORT);
if ((flags & SI_CLONED) && ifs->ifs_ipstate_logging)
ipstate_log(is, ISL_CLONE, ifs);
+ if ((flags & SI_CLONED) && IFS_CFWLOG(ifs, is->is_rule))
+ ipf_log_cfwlog(is, ISL_CLONE, ifs);
}
ret = -1;
@@ -3397,6 +3404,15 @@ ipf_stack_t *ifs;
if (ifs->ifs_ipstate_logging != 0 && why != 0)
ipstate_log(is, why, ifs);
+ /*
+ * For now, ipf_log_cfwlog() copes with all "why" values. Strictly
+ * speaking, though, they all map to one event (CFWEV_END), which for
+ * now is not supported, hence no code calling ipf_log_cfwlog() like
+ * below:
+ *
+ * if (why != 0 && IFS_CFWLOG(ifs, is->is_rule))
+ * ipf_log_cfwlog(is, why, ifs);
+ */
if (is->is_rule != NULL) {
is->is_rule->fr_statecnt--;
@@ -3931,7 +3947,6 @@ int flags;
return rval;
}
-
/* ------------------------------------------------------------------------ */
/* Function: ipstate_log */
/* Returns: Nil */
diff --git a/usr/src/uts/common/inet/ipf/ipf.conf b/usr/src/uts/common/inet/ipf/ipf.conf
index 6b36f9fdbf..f49e024a72 100644
--- a/usr/src/uts/common/inet/ipf/ipf.conf
+++ b/usr/src/uts/common/inet/ipf/ipf.conf
@@ -1,3 +1,8 @@
#
#
name="ipf" parent="pseudo" instance=0;
+
+# Increase the state table limits. fr_statemax should be ~70% of fr_statesize,
+# and both should be prime numbers
+fr_statesize=151007;
+fr_statemax=113279;
diff --git a/usr/src/uts/common/inet/ipf/netinet/Makefile b/usr/src/uts/common/inet/ipf/netinet/Makefile
index cca3b48ac4..88f91e633f 100644
--- a/usr/src/uts/common/inet/ipf/netinet/Makefile
+++ b/usr/src/uts/common/inet/ipf/netinet/Makefile
@@ -1,16 +1,15 @@
#
-#ident "%Z%%M% %I% %E% SMI"
-#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2019 Joyent, Inc.
#
# uts/common/inet/ipf/netinet/Makefile
#
# include global definitions
include ../../../../../Makefile.master
-HDRS= ipl.h ip_compat.h ip_fil.h ip_nat.h ip_proxy.h ip_state.h \
- ip_frag.h ip_auth.h ip_lookup.h ip_pool.h ip_htable.h ipf_stack.h
+HDRS= ipl.h ip_compat.h ip_fil.h ip_nat.h ip_proxy.h ip_state.h ip_frag.h \
+ ip_auth.h ip_lookup.h ip_pool.h ip_htable.h ipf_stack.h ipf_cfw.h
ROOTDIRS= $(ROOT)/usr/include/netinet
diff --git a/usr/src/uts/common/inet/ipf/netinet/ip_fil.h b/usr/src/uts/common/inet/ipf/netinet/ip_fil.h
index 4c3c5683b5..bb5ce7bd6c 100644
--- a/usr/src/uts/common/inet/ipf/netinet/ip_fil.h
+++ b/usr/src/uts/common/inet/ipf/netinet/ip_fil.h
@@ -8,7 +8,7 @@
*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2019, Joyent, Inc.
*/
#ifndef __IP_FIL_H__
@@ -16,6 +16,7 @@
#include "netinet/ip_compat.h"
#include <sys/zone.h>
+#include <sys/uuid.h>
#ifdef SOLARIS
#undef SOLARIS
@@ -115,6 +116,8 @@
#define SIOCDELFR SIOCRMAFR
#define SIOCINSFR SIOCINAFR
# define SIOCIPFZONESET _IOWR('r', 97, struct ipfzoneobj)
+# define SIOCIPFCFWCFG _IOR('r', 98, struct ipfcfwcfg)
+# define SIOCIPFCFWNEWSZ _IOWR('r', 99, struct ipfcfwcfg)
/*
* What type of table is getting flushed?
@@ -600,6 +603,7 @@ typedef struct frentry {
u_32_t fr_flags; /* per-rule flags && options (see below) */
u_32_t fr_logtag; /* user defined log tag # */
u_32_t fr_collect; /* collection number */
+ uuid_t fr_uuid; /* user defined uuid */
u_int fr_arg; /* misc. numeric arg for rule */
u_int fr_loglevel; /* syslog log facility + priority */
u_int fr_age[2]; /* non-TCP timeouts */
@@ -728,6 +732,7 @@ typedef struct frentry {
#define FR_NEWISN 0x400000 /* new ISN for outgoing TCP */
#define FR_NOICMPERR 0x800000 /* do not match ICMP errors in state */
#define FR_STATESYNC 0x1000000 /* synchronize state to slave */
+#define FR_CFWLOG 0x2000000 /* Global CFW logging enabled */
#define FR_NOMATCH 0x8000000 /* no match occured */
/* 0x10000000 FF_LOGPASS */
/* 0x20000000 FF_LOGBLOCK */
@@ -883,6 +888,7 @@ typedef struct ipflog {
u_32_t fl_lflags;
u_32_t fl_logtag;
ipftag_t fl_nattag;
+ uuid_t fl_uuid;
u_short fl_plen; /* extra data after hlen */
u_short fl_loglevel; /* syslog log level */
char fl_group[FR_GROUPLEN];
@@ -931,6 +937,7 @@ typedef struct ipflog {
#define IPSYNC_NAME "/dev/ipsync"
#define IPSCAN_NAME "/dev/ipscan"
#define IPLOOKUP_NAME "/dev/iplookup"
+#define IPFEV_NAME "/dev/ipfev"
#define IPL_LOGIPF 0 /* Minor device #'s for accessing logs */
#define IPL_LOGNAT 1
@@ -939,8 +946,9 @@ typedef struct ipflog {
#define IPL_LOGSYNC 4
#define IPL_LOGSCAN 5
#define IPL_LOGLOOKUP 6
-#define IPL_LOGCOUNT 7
-#define IPL_LOGMAX 7
+#define IPL_LOGEV 7
+#define IPL_LOGCOUNT 8
+#define IPL_LOGMAX 8
#define IPL_LOGSIZE (IPL_LOGMAX + 1)
#define IPL_LOGALL -1
#define IPL_LOGNONE -2
@@ -1181,6 +1189,21 @@ typedef struct ipfzoneobj {
char ipfz_zonename[ZONENAME_MAX]; /* zone to act on */
} ipfzoneobj_t;
+/* ioctl to grab CFW logging parameters */
+typedef struct ipfcfwcfg {
+ /* CFG => Max event size, NEWSZ => ignored in, like CFG out. */
+ uint32_t ipfcfwc_maxevsize;
+ /*
+ * CFG => Current ring size,
+ * NEWSZ => New ring size, must be 2^N for 3 <= N <= 31.
+ */
+ uint32_t ipfcfwc_evringsize;
+ /* CFG => Number of event reports, NEWSZ => ignored in, like CFG out. */
+ uint64_t ipfcfwc_evreports;
+ /* CFG => Number of event drops, NEWSZ => ignored in, like CFG out. */
+ uint64_t ipfcfwc_evdrops;
+} ipfcfwcfg_t;
+
#if defined(_KERNEL)
/* Set ipfs_zoneid to this if no zone has been set: */
#define IPFS_ZONE_UNSET -2
@@ -1560,6 +1583,23 @@ extern int ipllog __P((int, fr_info_t *, void **, size_t *, int *, int,
ipf_stack_t *));
extern void fr_logunload __P((ipf_stack_t *));
+/* SmartOS single-FD global-zone state accumulator (see cfw.c) */
+extern boolean_t ipf_cfwlog_enabled;
+struct ipstate; /* Ugggh. */
+extern void ipf_log_cfwlog __P((struct ipstate *, uint_t, ipf_stack_t *));
+extern void ipf_block_cfwlog __P((frentry_t *, fr_info_t *, ipf_stack_t *));
+#define IFS_CFWLOG(ifs, fr) ((ifs)->ifs_gz_controlled && ipf_cfwlog_enabled &&\
+ fr != NULL && ((fr)->fr_flags & FR_CFWLOG))
+struct cfwev_s; /* See ipf_cfw.h */
+extern boolean_t ipf_cfwev_consume __P((struct cfwev_s *, boolean_t));
+/* See cfw.c's ipf_cfwev_consume_many() for details. */
+typedef uint_t (*cfwmanycb_t) __P((struct cfwev_s *, uint_t, void *));
+extern int ipf_cfwlog_read __P((dev_t, struct uio *, struct cred *));
+extern int ipf_cfwlog_ioctl __P((dev_t, int, intptr_t, int, cred_t *, int *));
+#define IPF_CFW_RING_ALLOCATE 0
+#define IPF_CFW_RING_DESTROY 1
+extern int ipf_cfw_ring_resize(uint32_t);
+
extern frentry_t *fr_acctpkt __P((fr_info_t *, u_32_t *));
extern int fr_copytolog __P((int, char *, int));
extern u_short fr_cksum __P((mb_t *, ip_t *, int, void *));
diff --git a/usr/src/uts/common/inet/ipf/netinet/ip_state.h b/usr/src/uts/common/inet/ipf/netinet/ip_state.h
index 4c605c1b89..ef315d5ef1 100644
--- a/usr/src/uts/common/inet/ipf/netinet/ip_state.h
+++ b/usr/src/uts/common/inet/ipf/netinet/ip_state.h
@@ -8,11 +8,14 @@
*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef __IP_STATE_H__
#define __IP_STATE_H__
+#include <sys/uuid.h>
+
#if defined(__STDC__) || defined(__GNUC__) || defined(_AIX51)
# define SIOCDELST _IOW('r', 61, struct ipfobj)
#else
@@ -66,6 +69,7 @@ typedef struct ipstate {
/* in both directions */
u_32_t is_optmsk[2]; /* " " mask */
/* in both directions */
+ uuid_t is_uuid;
u_short is_sec; /* security options set */
u_short is_secmsk; /* " " mask */
u_short is_auth; /* authentication options set */
diff --git a/usr/src/uts/common/inet/ipf/netinet/ipf_cfw.h b/usr/src/uts/common/inet/ipf/netinet/ipf_cfw.h
new file mode 100644
index 0000000000..1972d2b3f7
--- /dev/null
+++ b/usr/src/uts/common/inet/ipf/netinet/ipf_cfw.h
@@ -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 2019 Joyent, Inc.
+ */
+
+#ifndef __IPF_CFW_H__
+#define __IPF_CFW_H__
+
+#include <sys/types.h>
+#include <inet/ip6.h>
+#include <sys/uuid.h>
+
+/* Because ipf compiles this kernel file in userland testing... */
+#ifndef ASSERT3U
+#define ASSERT3U(a, b, c) ASSERT(a ## b ## c);
+#endif /* ASSERT3U */
+
+/*
+ * CFW Event, which is emitted to a global-zone listener. The global-zone
+ * listener solves the one-fd-per-zone problem of using each zone's ipmon.
+ *
+ * These must be 64-bit aligned because they form an array in-kernel. There
+ * might be reserved fields to ensure that alignment.
+ */
+#define CFWEV_BLOCK 1
+#define CFWEV_BEGIN 2
+#define CFWEV_END 3
+#define CFWDIR_IN 1
+#define CFWDIR_OUT 2
+
+typedef struct cfwev_s {
+ uint16_t cfwev_type; /* BEGIN, END, BLOCK */
+ uint16_t cfwev_length; /* in bytes, so capped to 65535 bytes */
+ zoneid_t cfwev_zonedid; /* Pullable from ipf_stack_t. */
+
+ uint32_t cfwev_ruleid; /* Pullable from fr_info_t. */
+ uint16_t cfwev_sport; /* Source port (network order) */
+ uint16_t cfwev_dport; /* Dest. port (network order) */
+
+ uint8_t cfwev_protocol; /* IPPROTO_* */
+ /* "direction" informs if src/dst are local/remote or remote/local. */
+ uint8_t cfwev_direction;
+ uint8_t cfwev_reserved[6]; /* Ensures 64-bit alignment. */
+
+ in6_addr_t cfwev_saddr; /* IPv4 addresses are V4MAPPED. */
+ in6_addr_t cfwev_daddr;
+
+ /*
+ * Because of 'struct timeval' being different between 32-bit and
+ * 64-bit ABIs, this interface is only usable by 64-bit binaries.
+ */
+ struct timeval cfwev_tstamp;
+
+ uuid_t cfwev_ruleuuid; /* Pullable from fr_info_t. */
+} cfwev_t;
+
+
+
+#endif /* __IPF_CFW_H__ */
diff --git a/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h b/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h
index 0ceea1e921..0b2a8d826f 100644
--- a/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h
+++ b/usr/src/uts/common/inet/ipf/netinet/ipf_stack.h
@@ -6,7 +6,7 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright 2018 Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef __IPF_STACK_H__
@@ -46,6 +46,7 @@ struct ipf_stack {
struct ipf_stack *ifs_gz_cont_ifs;
netid_t ifs_netid;
zoneid_t ifs_zone;
+ zoneid_t ifs_zone_did;
boolean_t ifs_gz_controlled;
/* ipf module */
@@ -126,6 +127,11 @@ struct ipf_stack {
hook_t *ifs_ipfhook6_loop_out;
hook_t *ifs_ipfhook6_nicevents;
+ hook_t *ifs_ipfhookvndl3v4_in;
+ hook_t *ifs_ipfhookvndl3v6_in;
+ hook_t *ifs_ipfhookvndl3v4_out;
+ hook_t *ifs_ipfhookvndl3v6_out;
+
hook_t *ifs_ipfhookviona_in;
hook_t *ifs_ipfhookviona_out;
@@ -140,12 +146,18 @@ struct ipf_stack {
boolean_t ifs_hook6_nic_events;
boolean_t ifs_hook6_loopback_in;
boolean_t ifs_hook6_loopback_out;
+ boolean_t ifs_hookvndl3v4_physical_in;
+ boolean_t ifs_hookvndl3v6_physical_in;
+ boolean_t ifs_hookvndl3v4_physical_out;
+ boolean_t ifs_hookvndl3v6_physical_out;
boolean_t ifs_hookviona_physical_in;
boolean_t ifs_hookviona_physical_out;
int ifs_ipf_loopback;
net_handle_t ifs_ipf_ipv4;
net_handle_t ifs_ipf_ipv6;
+ net_handle_t ifs_ipf_vndl3v4;
+ net_handle_t ifs_ipf_vndl3v6;
net_handle_t ifs_ipf_viona;
/* ip_auth.c */
@@ -305,6 +317,7 @@ struct ipf_stack {
char *ifs_addmask_key;
char *ifs_rn_zeros;
char *ifs_rn_ones;
+
#ifdef KERNEL
/* kstats for inbound and outbound */
kstat_t *ifs_kstatp[2];
diff --git a/usr/src/uts/common/inet/ipf/solaris.c b/usr/src/uts/common/inet/ipf/solaris.c
index c541f4dddc..5ccbfa3188 100644
--- a/usr/src/uts/common/inet/ipf/solaris.c
+++ b/usr/src/uts/common/inet/ipf/solaris.c
@@ -6,7 +6,7 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -116,7 +116,7 @@ static void ipf_stack_shutdown __P((const netid_t, void *));
static int ipf_property_g_update __P((dev_info_t *));
static char *ipf_devfiles[] = { IPL_NAME, IPNAT_NAME, IPSTATE_NAME,
IPAUTH_NAME, IPSYNC_NAME, IPSCAN_NAME,
- IPLOOKUP_NAME, NULL };
+ IPLOOKUP_NAME, IPFEV_NAME, NULL };
extern void *ipf_state; /* DDI state */
extern vmem_t *ipf_minor; /* minor number arena */
@@ -625,7 +625,6 @@ ipf_stack_shutdown(const netid_t id, void *arg)
/*
* Destroy things for ipf for one stack.
*/
-/* ARGSUSED */
static void
ipf_stack_destroy_one(const netid_t id, ipf_stack_t *ifs)
{
@@ -742,6 +741,9 @@ ddi_attach_cmd_t cmd;
ipf_dev_info = dip;
+ if (ipf_cfw_ring_resize(IPF_CFW_RING_ALLOCATE) != 0)
+ goto attach_failed;
+
ipfncb = net_instance_alloc(NETINFO_VERSION);
if (ipfncb == NULL)
goto attach_failed;
@@ -769,6 +771,7 @@ ddi_attach_cmd_t cmd;
}
attach_failed:
+ (void) ipf_cfw_ring_resize(IPF_CFW_RING_DESTROY);
ddi_remove_minor_node(dip, NULL);
ddi_prop_remove_all(dip);
ddi_soft_state_fini(&ipf_state);
@@ -796,6 +799,7 @@ ddi_detach_cmd_t cmd;
* framework guarantees we are not active with this devinfo
* node in any other entry points at this time.
*/
+ (void) ipf_cfw_ring_resize(IPF_CFW_RING_DESTROY);
ddi_prop_remove_all(dip);
i = ddi_get_instance(dip);
ddi_remove_minor_node(dip, NULL);
diff --git a/usr/src/uts/common/inet/mib2.h b/usr/src/uts/common/inet/mib2.h
index 5a168523ee..85ca5ebdec 100644
--- a/usr/src/uts/common/inet/mib2.h
+++ b/usr/src/uts/common/inet/mib2.h
@@ -23,6 +23,7 @@
/*
* Copyright (c) 1990 Mentat Inc.
* Copyright (c) 2015, 2016 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -1400,6 +1401,8 @@ typedef struct tcpConnEntryInfo_s {
/* round-trip time smoothed average (us) */
Gauge ce_rtt_sa;
/* current rto (retransmit timeout) */
+ Gauge ce_rtt_sd;
+ /* current rto (retransmit timeout) */
Gauge ce_rto;
/* round-trip time count */
Gauge ce_rtt_cnt;
diff --git a/usr/src/uts/common/inet/rawip_impl.h b/usr/src/uts/common/inet/rawip_impl.h
index 6fb72d1d08..ddb482db78 100644
--- a/usr/src/uts/common/inet/rawip_impl.h
+++ b/usr/src/uts/common/inet/rawip_impl.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1990 Mentat Inc. */
@@ -43,6 +44,7 @@ extern "C" {
#include <inet/ip.h>
#include <inet/optcom.h>
#include <inet/tunables.h>
+#include <inet/bpf.h>
/*
* ICMP stack instances
@@ -84,6 +86,10 @@ typedef struct icmp_s {
mblk_t *icmp_fallback_queue_head;
mblk_t *icmp_fallback_queue_tail;
struct sockaddr_storage icmp_delayed_addr;
+
+ krwlock_t icmp_bpf_lock; /* protects icmp_bpf */
+ ip_bpf_insn_t *icmp_bpf_prog; /* SO_ATTACH_FILTER bpf */
+ uint_t icmp_bpf_len;
} icmp_t;
/*
diff --git a/usr/src/uts/common/inet/sockmods/datafilt.c b/usr/src/uts/common/inet/sockmods/datafilt.c
new file mode 100644
index 0000000000..6e1171de46
--- /dev/null
+++ b/usr/src/uts/common/inet/sockmods/datafilt.c
@@ -0,0 +1,116 @@
+/*
+ * 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) 2012, OmniTI Computer Consulting, Inc. All rights reserved.
+ */
+
+/*
+ * This file implements a socketfilter used to deter TCP connections.
+ * To defer a connection means to delay the return of accept(3SOCKET)
+ * until at least one byte is ready to be read(2). This filter may be
+ * applied automatically or programmatically through the use of
+ * soconfig(1M) and setsockopt(3SOCKET).
+ */
+
+#include <sys/kmem.h>
+#include <sys/systm.h>
+#include <sys/stropts.h>
+#include <sys/strsun.h>
+#include <sys/socketvar.h>
+#include <sys/sockfilter.h>
+#include <sys/note.h>
+#include <sys/taskq.h>
+
+#define DATAFILT_MODULE "datafilt"
+
+static struct modlmisc dataf_modlmisc = {
+ &mod_miscops,
+ "Kernel data-ready socket filter"
+};
+
+static struct modlinkage dataf_modlinkage = {
+ MODREV_1,
+ &dataf_modlmisc,
+ NULL
+};
+
+static sof_rval_t
+dataf_attach_passive_cb(sof_handle_t handle, sof_handle_t ph,
+ void *parg, struct sockaddr *laddr, socklen_t laddrlen,
+ struct sockaddr *faddr, socklen_t faddrlen, void **cookiep)
+{
+ _NOTE(ARGUNUSED(handle, ph, parg, laddr, laddrlen, faddr, faddrlen,
+ cookiep));
+ return (SOF_RVAL_DEFER);
+}
+
+static void
+dataf_detach_cb(sof_handle_t handle, void *cookie, cred_t *cr)
+{
+ _NOTE(ARGUNUSED(handle, cookie, cr));
+}
+
+static mblk_t *
+dataf_data_in_cb(sof_handle_t handle, void *cookie, mblk_t *mp, int flags,
+ size_t *lenp)
+{
+ _NOTE(ARGUNUSED(cookie, flags, lenp));
+
+ if (mp != NULL && MBLKL(mp) > 0) {
+ sof_newconn_ready(handle);
+ sof_bypass(handle);
+ }
+
+ return (mp);
+}
+
+static sof_ops_t dataf_ops = {
+ .sofop_attach_passive = dataf_attach_passive_cb,
+ .sofop_detach = dataf_detach_cb,
+ .sofop_data_in = dataf_data_in_cb
+};
+
+int
+_init(void)
+{
+ int err;
+
+ /*
+ * This module is safe to attach even after some preliminary socket
+ * setup calls have taken place. See the comment for SOF_ATT_SAFE.
+ */
+ err = sof_register(SOF_VERSION, DATAFILT_MODULE, &dataf_ops,
+ SOF_ATT_SAFE);
+ if (err != 0)
+ return (err);
+ if ((err = mod_install(&dataf_modlinkage)) != 0)
+ (void) sof_unregister(DATAFILT_MODULE);
+
+ return (err);
+}
+
+int
+_fini(void)
+{
+ int err;
+
+ if ((err = sof_unregister(DATAFILT_MODULE)) != 0)
+ return (err);
+
+ return (mod_remove(&dataf_modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&dataf_modlinkage, modinfop));
+}
diff --git a/usr/src/uts/common/inet/sockmods/sockmod_pfp.c b/usr/src/uts/common/inet/sockmods/sockmod_pfp.c
index 586d7f06f8..76191e93b8 100644
--- a/usr/src/uts/common/inet/sockmods/sockmod_pfp.c
+++ b/usr/src/uts/common/inet/sockmods/sockmod_pfp.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/types.h>
@@ -51,6 +51,7 @@
#include <sys/mac_client.h>
#include <sys/mac_provider.h>
#include <sys/mac_client_priv.h>
+#include <inet/bpf.h>
#include <netpacket/packet.h>
@@ -448,7 +449,7 @@ pfp_packet(void *arg, mac_resource_handle_t mrh, mblk_t *mp, boolean_t flag)
buffer = (uchar_t *)mp;
}
rw_enter(&ps->ps_bpflock, RW_READER);
- if (bpf_filter(ps->ps_bpf.bf_insns, buffer,
+ if (ip_bpf_filter((ip_bpf_insn_t *)ps->ps_bpf.bf_insns, buffer,
hdr.mhi_pktsize, buflen) == 0) {
rw_exit(&ps->ps_bpflock);
ps->ps_stats.tp_drops++;
@@ -1336,7 +1337,7 @@ pfp_setsocket_sockopt(sock_lower_handle_t handle, int option_name,
const void *optval, socklen_t optlen)
{
struct bpf_program prog;
- struct bpf_insn *fcode;
+ ip_bpf_insn_t *fcode;
struct pfpsock *ps;
struct sock_proto_props sopp;
int error = 0;
@@ -1370,10 +1371,10 @@ pfp_setsocket_sockopt(sock_lower_handle_t handle, int option_name,
return (EFAULT);
}
- if (bpf_validate(fcode, (int)prog.bf_len)) {
+ if (ip_bpf_validate(fcode, prog.bf_len)) {
rw_enter(&ps->ps_bpflock, RW_WRITER);
pfp_release_bpf(ps);
- ps->ps_bpf.bf_insns = fcode;
+ ps->ps_bpf.bf_insns = (struct bpf_insn *)fcode;
ps->ps_bpf.bf_len = size;
rw_exit(&ps->ps_bpflock);
diff --git a/usr/src/uts/common/inet/squeue.c b/usr/src/uts/common/inet/squeue.c
index 9fa40eccb6..e65af832eb 100644
--- a/usr/src/uts/common/inet/squeue.c
+++ b/usr/src/uts/common/inet/squeue.c
@@ -61,6 +61,10 @@
* connection are processed on that squeue. The connection ("conn") to
* squeue mapping is stored in "conn_t" member "conn_sqp".
*
+ * If the squeue is not related to TCP/IP, then the value of sqp->sq_isip is
+ * false and it will not have an associated conn_t, which means many aspects of
+ * the system, such as polling and swtiching squeues will not be used.
+ *
* Since the processing of the connection cuts across multiple layers
* but still allows packets for different connnection to be processed on
* other CPU/squeues, squeues are also termed as "Vertical Perimeter" or
@@ -241,7 +245,7 @@ squeue_init(void)
}
squeue_t *
-squeue_create(pri_t pri)
+squeue_create(pri_t pri, boolean_t isip)
{
squeue_t *sqp = kmem_cache_alloc(squeue_cache, KM_SLEEP);
@@ -256,11 +260,36 @@ squeue_create(pri_t pri)
sqp->sq_enter = squeue_enter;
sqp->sq_drain = squeue_drain;
+ sqp->sq_isip = isip;
return (sqp);
}
/*
+ * We need to kill the threads and then clean up. We should VERIFY that
+ * polling is disabled so we don't have to worry about disassociating from
+ * MAC/IP/etc.
+ */
+void
+squeue_destroy(squeue_t *sqp)
+{
+ kt_did_t worker, poll;
+ mutex_enter(&sqp->sq_lock);
+ VERIFY(!(sqp->sq_state & (SQS_POLL_THR_QUIESCED |
+ SQS_POLL_QUIESCE_DONE | SQS_PAUSE | SQS_EXIT)));
+ worker = sqp->sq_worker->t_did;
+ poll = sqp->sq_poll_thr->t_did;
+ sqp->sq_state |= SQS_EXIT;
+ cv_signal(&sqp->sq_poll_cv);
+ cv_signal(&sqp->sq_worker_cv);
+ mutex_exit(&sqp->sq_lock);
+
+ thread_join(poll);
+ thread_join(worker);
+ kmem_cache_free(squeue_cache, sqp);
+}
+
+/*
* Bind squeue worker thread to the specified CPU, given by CPU id.
* If the CPU id value is -1, bind the worker thread to the value
* specified in sq_bind field. If a thread is already bound to a
@@ -380,18 +409,21 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
* Handle squeue switching. More details in the
* block comment at the top of the file
*/
- if (connp->conn_sqp == sqp) {
+ if (sqp->sq_isip == B_FALSE || connp->conn_sqp == sqp) {
SQUEUE_DBG_SET(sqp, mp, proc, connp,
tag);
- connp->conn_on_sqp = B_TRUE;
+ if (sqp->sq_isip == B_TRUE)
+ connp->conn_on_sqp = B_TRUE;
DTRACE_PROBE3(squeue__proc__start, squeue_t *,
sqp, mblk_t *, mp, conn_t *, connp);
(*proc)(connp, mp, sqp, ira);
DTRACE_PROBE2(squeue__proc__end, squeue_t *,
sqp, conn_t *, connp);
- connp->conn_on_sqp = B_FALSE;
+ if (sqp->sq_isip == B_TRUE) {
+ connp->conn_on_sqp = B_FALSE;
+ CONN_DEC_REF(connp);
+ }
SQUEUE_DBG_CLEAR(sqp);
- CONN_DEC_REF(connp);
} else {
SQUEUE_ENTER_ONE(connp->conn_sqp, mp, proc,
connp, ira, SQ_FILL, SQTAG_SQUEUE_CHANGE);
@@ -407,7 +439,7 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
* still be best to process a single queued
* item if it matches the active connection.
*/
- if (sqp->sq_first != NULL) {
+ if (sqp->sq_first != NULL && sqp->sq_isip) {
squeue_try_drain_one(sqp, connp);
}
@@ -423,7 +455,7 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
return;
}
} else {
- if (ira != NULL) {
+ if (sqp->sq_isip == B_TRUE && ira != NULL) {
mblk_t *attrmp;
ASSERT(cnt == 1);
@@ -496,7 +528,8 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
if (!(sqp->sq_state & SQS_REENTER) &&
(process_flag != SQ_FILL) && (sqp->sq_first == NULL) &&
(sqp->sq_run == curthread) && (cnt == 1) &&
- (connp->conn_on_sqp == B_FALSE)) {
+ (sqp->sq_isip == B_FALSE ||
+ connp->conn_on_sqp == B_FALSE)) {
sqp->sq_state |= SQS_REENTER;
mutex_exit(&sqp->sq_lock);
@@ -511,15 +544,21 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
* Handle squeue switching. More details in the
* block comment at the top of the file
*/
- if (connp->conn_sqp == sqp) {
- connp->conn_on_sqp = B_TRUE;
+ if (sqp->sq_isip == B_FALSE || connp->conn_sqp == sqp) {
+ SQUEUE_DBG_SET(sqp, mp, proc, connp,
+ tag);
+ if (sqp->sq_isip == B_TRUE)
+ connp->conn_on_sqp = B_TRUE;
DTRACE_PROBE3(squeue__proc__start, squeue_t *,
sqp, mblk_t *, mp, conn_t *, connp);
(*proc)(connp, mp, sqp, ira);
DTRACE_PROBE2(squeue__proc__end, squeue_t *,
sqp, conn_t *, connp);
- connp->conn_on_sqp = B_FALSE;
- CONN_DEC_REF(connp);
+ if (sqp->sq_isip == B_TRUE) {
+ connp->conn_on_sqp = B_FALSE;
+ CONN_DEC_REF(connp);
+ }
+ SQUEUE_DBG_CLEAR(sqp);
} else {
SQUEUE_ENTER_ONE(connp->conn_sqp, mp, proc,
connp, ira, SQ_FILL, SQTAG_SQUEUE_CHANGE);
@@ -540,7 +579,7 @@ squeue_enter(squeue_t *sqp, mblk_t *mp, mblk_t *tail, uint32_t cnt,
#ifdef DEBUG
mp->b_tag = tag;
#endif
- if (ira != NULL) {
+ if (sqp->sq_isip && ira != NULL) {
mblk_t *attrmp;
ASSERT(cnt == 1);
@@ -658,7 +697,7 @@ again:
mp->b_prev = NULL;
/* Is there an ip_recv_attr_t to handle? */
- if (ip_recv_attr_is_mblk(mp)) {
+ if (sqp->sq_isip == B_TRUE && ip_recv_attr_is_mblk(mp)) {
mblk_t *attrmp = mp;
ASSERT(attrmp->b_cont != NULL);
@@ -683,20 +722,25 @@ again:
/*
- * Handle squeue switching. More details in the
- * block comment at the top of the file
+ * Handle squeue switching. More details in the block comment at
+ * the top of the file. non-IP squeues cannot switch, as there
+ * is no conn_t.
*/
- if (connp->conn_sqp == sqp) {
+ if (sqp->sq_isip == B_FALSE || connp->conn_sqp == sqp) {
SQUEUE_DBG_SET(sqp, mp, proc, connp,
mp->b_tag);
- connp->conn_on_sqp = B_TRUE;
+ if (sqp->sq_isip == B_TRUE)
+ connp->conn_on_sqp = B_TRUE;
DTRACE_PROBE3(squeue__proc__start, squeue_t *,
sqp, mblk_t *, mp, conn_t *, connp);
(*proc)(connp, mp, sqp, ira);
DTRACE_PROBE2(squeue__proc__end, squeue_t *,
sqp, conn_t *, connp);
- connp->conn_on_sqp = B_FALSE;
- CONN_DEC_REF(connp);
+ if (sqp->sq_isip == B_TRUE) {
+ connp->conn_on_sqp = B_FALSE;
+ CONN_DEC_REF(connp);
+ }
+ SQUEUE_DBG_CLEAR(sqp);
} else {
SQUEUE_ENTER_ONE(connp->conn_sqp, mp, proc, connp, ira,
SQ_FILL, SQTAG_SQUEUE_CHANGE);
@@ -925,6 +969,11 @@ squeue_polling_thread(squeue_t *sqp)
cv_wait(async, lock);
CALLB_CPR_SAFE_END(&cprinfo, lock);
+ if (sqp->sq_state & SQS_EXIT) {
+ mutex_exit(lock);
+ thread_exit();
+ }
+
ctl_state = sqp->sq_state & (SQS_POLL_THR_CONTROL |
SQS_POLL_THR_QUIESCED);
if (ctl_state != 0) {
@@ -950,6 +999,9 @@ squeue_polling_thread(squeue_t *sqp)
(SQS_PROC|SQS_POLLING|SQS_GET_PKTS)) ==
(SQS_PROC|SQS_POLLING|SQS_GET_PKTS));
+ /* Only IP related squeues should reach this point */
+ VERIFY(sqp->sq_isip == B_TRUE);
+
poll_again:
sq_rx_ring = sqp->sq_rx_ring;
sq_get_pkts = sq_rx_ring->rr_rx;
@@ -1079,6 +1131,7 @@ squeue_worker_thr_control(squeue_t *sqp)
ill_rx_ring_t *rx_ring;
ASSERT(MUTEX_HELD(&sqp->sq_lock));
+ VERIFY(sqp->sq_isip == B_TRUE);
if (sqp->sq_state & SQS_POLL_RESTART) {
/* Restart implies a previous quiesce. */
@@ -1190,6 +1243,11 @@ squeue_worker(squeue_t *sqp)
for (;;) {
for (;;) {
+ if (sqp->sq_state & SQS_EXIT) {
+ mutex_exit(lock);
+ thread_exit();
+ }
+
/*
* If the poll thread has handed control to us
* we need to break out of the wait.
@@ -1286,6 +1344,7 @@ squeue_synch_enter(conn_t *connp, mblk_t *use_mp)
again:
sqp = connp->conn_sqp;
+ VERIFY(sqp->sq_isip == B_TRUE);
mutex_enter(&sqp->sq_lock);
if (sqp->sq_first == NULL && !(sqp->sq_state & SQS_PROC)) {
@@ -1374,6 +1433,7 @@ squeue_try_drain_one(squeue_t *sqp, conn_t *compare_conn)
ASSERT(MUTEX_HELD(&sqp->sq_lock));
ASSERT((sqp->sq_state & SQS_PROC) == 0);
ASSERT(sqp->sq_run == NULL);
+ ASSERT(sqp->sq_isip);
VERIFY(mp != NULL);
/*
@@ -1440,6 +1500,9 @@ squeue_try_drain_one(squeue_t *sqp, conn_t *compare_conn)
CONN_DEC_REF(connp);
SQUEUE_DBG_CLEAR(sqp);
+ if (ira != NULL)
+ ira_cleanup(ira, B_TRUE);
+
done:
mutex_enter(&sqp->sq_lock);
sqp->sq_state &= ~(SQS_PROC);
@@ -1451,6 +1514,7 @@ squeue_synch_exit(conn_t *connp, int flag)
{
squeue_t *sqp = connp->conn_sqp;
+ VERIFY(sqp->sq_isip == B_TRUE);
ASSERT(flag == SQ_NODRAIN || flag == SQ_PROCESS);
mutex_enter(&sqp->sq_lock);
diff --git a/usr/src/uts/common/inet/tcp.h b/usr/src/uts/common/inet/tcp.h
index 775c5abe6b..3ed2b7174a 100644
--- a/usr/src/uts/common/inet/tcp.h
+++ b/usr/src/uts/common/inet/tcp.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
* Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014, 2017 by Delphix. All rights reserved.
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
@@ -137,6 +137,7 @@ typedef struct tcphdra_s {
struct conn_s;
struct tcp_listen_cnt_s;
+struct tcp_rg_s;
/*
* Control structure for each open TCP stream,
@@ -407,6 +408,13 @@ typedef struct tcp_s {
struct tcp_s *tcp_bind_hash_port; /* tcp_t's bound to the same lport */
struct tcp_s **tcp_ptpbhn;
+ /*
+ * Group of tcp_t entries bound to the same adress and port via
+ * SO_REUSEPORT. The pointer itself is protected by tf_lock in the
+ * containing tcps_bind_fanout slot.
+ */
+ struct tcp_rg_s *tcp_rg_bind;
+
uint_t tcp_maxpsz_multiplier;
uint32_t tcp_lso_max; /* maximum LSO payload */
diff --git a/usr/src/uts/common/inet/tcp/tcp.c b/usr/src/uts/common/inet/tcp/tcp.c
index 9348ea3d0f..427a6df274 100644
--- a/usr/src/uts/common/inet/tcp/tcp.c
+++ b/usr/src/uts/common/inet/tcp/tcp.c
@@ -961,8 +961,7 @@ void
tcp_stop_lingering(tcp_t *tcp)
{
clock_t delta = 0;
- tcp_stack_t *tcps = tcp->tcp_tcps;
- conn_t *connp = tcp->tcp_connp;
+ conn_t *connp = tcp->tcp_connp;
tcp->tcp_linger_tid = 0;
if (tcp->tcp_state > TCPS_LISTEN) {
@@ -990,7 +989,7 @@ tcp_stop_lingering(tcp_t *tcp)
if (tcp->tcp_state == TCPS_TIME_WAIT) {
tcp_time_wait_append(tcp);
- TCP_DBGSTAT(tcps, tcp_detach_time_wait);
+ TCP_DBGSTAT(tcp->tcp_tcps, tcp_detach_time_wait);
goto finish;
}
@@ -1429,6 +1428,21 @@ tcp_free(tcp_t *tcp)
tcp->tcp_cc_algo->cb_destroy(&tcp->tcp_ccv);
/*
+ * Destroy any association with SO_REUSEPORT group.
+ */
+ if (tcp->tcp_rg_bind != NULL) {
+ /*
+ * This is only necessary for connections which enabled
+ * SO_REUSEPORT but were never bound. Such connections should
+ * be the one and only member of the tcp_rg_tp to which they
+ * have been associated.
+ */
+ VERIFY(tcp_rg_remove(tcp->tcp_rg_bind, tcp));
+ tcp_rg_destroy(tcp->tcp_rg_bind);
+ tcp->tcp_rg_bind = NULL;
+ }
+
+ /*
* If this is a non-STREAM socket still holding on to an upper
* handle, release it. As a result of fallback we might also see
* STREAMS based conns with upper handles, in which case there is
@@ -2477,8 +2491,10 @@ tcp_init_values(tcp_t *tcp, tcp_t *parent)
* Path MTU might have changed by either increase or decrease, so need to
* adjust the MSS based on the value of ixa_pmtu. No need to handle tiny
* or negative MSS, since tcp_mss_set() will do it.
+ *
+ * Returns B_TRUE when the connection PMTU changes, otherwise B_FALSE.
*/
-void
+boolean_t
tcp_update_pmtu(tcp_t *tcp, boolean_t decrease_only)
{
uint32_t pmtu;
@@ -2488,10 +2504,10 @@ tcp_update_pmtu(tcp_t *tcp, boolean_t decrease_only)
iaflags_t ixaflags;
if (tcp->tcp_tcps->tcps_ignore_path_mtu)
- return;
+ return (B_FALSE);
if (tcp->tcp_state < TCPS_ESTABLISHED)
- return;
+ return (B_FALSE);
/*
* Always call ip_get_pmtu() to make sure that IP has updated
@@ -2511,13 +2527,13 @@ tcp_update_pmtu(tcp_t *tcp, boolean_t decrease_only)
* Nothing to change, so just return.
*/
if (mss == tcp->tcp_mss)
- return;
+ return (B_FALSE);
/*
* Currently, for ICMP errors, only PMTU decrease is handled.
*/
if (mss > tcp->tcp_mss && decrease_only)
- return;
+ return (B_FALSE);
DTRACE_PROBE2(tcp_update_pmtu, int32_t, tcp->tcp_mss, uint32_t, mss);
@@ -2552,6 +2568,7 @@ tcp_update_pmtu(tcp_t *tcp, boolean_t decrease_only)
tcp->tcp_ipha->ipha_fragment_offset_and_flags = 0;
}
ixa->ixa_flags = ixaflags;
+ return (B_TRUE);
}
int
@@ -3424,7 +3441,7 @@ tcp_notify(void *arg, ip_xmit_attr_t *ixa, ixa_notify_type_t ntype,
tcp_update_lso(tcp, connp->conn_ixa);
break;
case IXAN_PMTU:
- tcp_update_pmtu(tcp, B_FALSE);
+ (void) tcp_update_pmtu(tcp, B_FALSE);
break;
case IXAN_ZCOPY:
tcp_update_zcopy(tcp);
@@ -3755,7 +3772,6 @@ tcp_stack_init(netstackid_t stackid, netstack_t *ns)
{
tcp_stack_t *tcps;
int i;
- int error = 0;
major_t major;
size_t arrsz;
@@ -3819,8 +3835,7 @@ tcp_stack_init(netstackid_t stackid, netstack_t *ns)
tcps->tcps_mibkp = tcp_kstat_init(stackid);
major = mod_name_to_major(INET_NAME);
- error = ldi_ident_from_major(major, &tcps->tcps_ldi_ident);
- ASSERT(error == 0);
+ VERIFY0(ldi_ident_from_major(major, &tcps->tcps_ldi_ident));
tcps->tcps_ixa_cleanup_mp = allocb_wait(0, BPRI_MED, STR_NOSIG, NULL);
ASSERT(tcps->tcps_ixa_cleanup_mp != NULL);
cv_init(&tcps->tcps_ixa_cleanup_ready_cv, NULL, CV_DEFAULT, NULL);
diff --git a/usr/src/uts/common/inet/tcp/tcp_bind.c b/usr/src/uts/common/inet/tcp/tcp_bind.c
index 86242fc944..5c2e1e1932 100644
--- a/usr/src/uts/common/inet/tcp/tcp_bind.c
+++ b/usr/src/uts/common/inet/tcp/tcp_bind.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
* Copyright (c) 2016 by Delphix. All rights reserved.
*/
@@ -56,6 +57,7 @@ static uint32_t tcp_random_anon_port = 1;
static int tcp_bind_select_lport(tcp_t *, in_port_t *, boolean_t,
cred_t *cr);
static in_port_t tcp_get_next_priv_port(const tcp_t *);
+static int tcp_rg_insert(tcp_rg_t *, struct tcp_s *);
/*
* Hash list insertion routine for tcp_t structures. Each hash bucket
@@ -173,6 +175,16 @@ tcp_bind_hash_remove(tcp_t *tcp)
ASSERT(lockp != NULL);
mutex_enter(lockp);
+
+ /* destroy any association with SO_REUSEPORT group */
+ if (tcp->tcp_rg_bind != NULL) {
+ if (tcp_rg_remove(tcp->tcp_rg_bind, tcp)) {
+ /* Last one out turns off the lights */
+ tcp_rg_destroy(tcp->tcp_rg_bind);
+ }
+ tcp->tcp_rg_bind = NULL;
+ }
+
if (tcp->tcp_ptpbhn) {
tcpnext = tcp->tcp_bind_hash_port;
if (tcpnext != NULL) {
@@ -638,13 +650,12 @@ tcp_bind_check(conn_t *connp, struct sockaddr *sa, socklen_t len, cred_t *cr,
}
/*
- * If the "bind_to_req_port_only" parameter is set, if the requested port
- * number is available, return it, If not return 0
+ * If the "bind_to_req_port_only" parameter is set and the requested port
+ * number is available, return it (else return 0).
*
- * If "bind_to_req_port_only" parameter is not set and
- * If the requested port number is available, return it. If not, return
- * the first anonymous port we happen across. If no anonymous ports are
- * available, return 0. addr is the requested local address, if any.
+ * If "bind_to_req_port_only" parameter is not set and the requested port
+ * number is available, return it. If not, return the first anonymous port we
+ * happen across. If no anonymous ports are available, return 0.
*
* In either case, when succeeding update the tcp_t to record the port number
* and insert it in the bind hash table.
@@ -664,6 +675,7 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
int loopmax;
conn_t *connp = tcp->tcp_connp;
tcp_stack_t *tcps = tcp->tcp_tcps;
+ boolean_t reuseport = connp->conn_reuseport;
/*
* Lookup for free addresses is done in a loop and "loopmax"
@@ -700,6 +712,7 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
tf_t *tbf;
tcp_t *ltcp;
conn_t *lconnp;
+ boolean_t attempt_reuse = B_FALSE;
lport = htons(port);
@@ -726,6 +739,7 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
for (; ltcp != NULL; ltcp = ltcp->tcp_bind_hash_port) {
boolean_t not_socket;
boolean_t exclbind;
+ boolean_t addrmatch;
lconnp = ltcp->tcp_connp;
@@ -831,22 +845,35 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
&lconnp->conn_faddr_v6)))
continue;
+ addrmatch = IN6_ARE_ADDR_EQUAL(laddr,
+ &lconnp->conn_bound_addr_v6);
+
+ if (addrmatch && reuseport && bind_to_req_port_only &&
+ (ltcp->tcp_state == TCPS_BOUND ||
+ ltcp->tcp_state == TCPS_LISTEN)) {
+ /*
+ * This entry is bound to the exact same
+ * address and port. If SO_REUSEPORT is set on
+ * the calling socket, attempt to reuse this
+ * binding if it too had SO_REUSEPORT enabled
+ * when it was bound.
+ */
+ attempt_reuse = (ltcp->tcp_rg_bind != NULL);
+ break;
+ }
+
if (!reuseaddr) {
/*
- * No socket option SO_REUSEADDR.
- * If existing port is bound to
- * a non-wildcard IP address
- * and the requesting stream is
- * bound to a distinct
- * different IP addresses
- * (non-wildcard, also), keep
- * going.
+ * No socket option SO_REUSEADDR. If an
+ * existing port is bound to a non-wildcard IP
+ * address and the requesting stream is bound
+ * to a distinct different IP address
+ * (non-wildcard, also), keep going.
*/
if (!V6_OR_V4_INADDR_ANY(*laddr) &&
!V6_OR_V4_INADDR_ANY(
lconnp->conn_bound_addr_v6) &&
- !IN6_ARE_ADDR_EQUAL(laddr,
- &lconnp->conn_bound_addr_v6))
+ !addrmatch)
continue;
if (ltcp->tcp_state >= TCPS_BOUND) {
/*
@@ -861,27 +888,49 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
* socket option SO_REUSEADDR is set on the
* binding tcp_t.
*
- * If two streams are bound to
- * same IP address or both addr
- * and bound source are wildcards
- * (INADDR_ANY), we want to stop
- * searching.
- * We have found a match of IP source
- * address and source port, which is
- * refused regardless of the
- * SO_REUSEADDR setting, so we break.
+ * If two streams are bound to the same IP
+ * address or both addr and bound source are
+ * wildcards (INADDR_ANY), we want to stop
+ * searching. We have found a match of IP
+ * source address and source port, which is
+ * refused regardless of the SO_REUSEADDR
+ * setting, so we break.
*/
- if (IN6_ARE_ADDR_EQUAL(laddr,
- &lconnp->conn_bound_addr_v6) &&
+ if (addrmatch &&
(ltcp->tcp_state == TCPS_LISTEN ||
ltcp->tcp_state == TCPS_BOUND))
break;
}
}
- if (ltcp != NULL) {
+ if (ltcp != NULL && !attempt_reuse) {
/* The port number is busy */
mutex_exit(&tbf->tf_lock);
} else {
+ if (attempt_reuse) {
+ int err;
+ struct tcp_rg_s *rg;
+
+ ASSERT(ltcp != NULL);
+ ASSERT(ltcp->tcp_rg_bind != NULL);
+ ASSERT(tcp->tcp_rg_bind != NULL);
+ ASSERT(ltcp->tcp_rg_bind != tcp->tcp_rg_bind);
+
+ err = tcp_rg_insert(ltcp->tcp_rg_bind, tcp);
+ if (err != 0) {
+ mutex_exit(&tbf->tf_lock);
+ return (0);
+ }
+ /*
+ * Now that the newly-binding socket has joined
+ * the existing reuseport group on ltcp, it
+ * should clean up its own (empty) group.
+ */
+ rg = tcp->tcp_rg_bind;
+ tcp->tcp_rg_bind = ltcp->tcp_rg_bind;
+ VERIFY(tcp_rg_remove(rg, tcp));
+ tcp_rg_destroy(rg);
+ }
+
/*
* This port is ours. Insert in fanout and mark as
* bound to prevent others from getting the port
@@ -946,3 +995,124 @@ tcp_bindi(tcp_t *tcp, in_port_t port, const in6_addr_t *laddr,
} while (++count < loopmax);
return (0);
}
+
+/* Max number of members in TCP SO_REUSEPORT group */
+#define TCP_RG_SIZE_MAX 64
+/* Step size when expanding members array */
+#define TCP_RG_SIZE_STEP 2
+
+
+tcp_rg_t *
+tcp_rg_init(tcp_t *tcp)
+{
+ tcp_rg_t *rg;
+ rg = kmem_alloc(sizeof (tcp_rg_t), KM_NOSLEEP_LAZY);
+ if (rg == NULL)
+ return (NULL);
+ rg->tcprg_members = kmem_zalloc(2 * sizeof (tcp_t *), KM_NOSLEEP_LAZY);
+ if (rg->tcprg_members == NULL) {
+ kmem_free(rg, sizeof (tcp_rg_t));
+ return (NULL);
+ }
+
+ mutex_init(&rg->tcprg_lock, NULL, MUTEX_DEFAULT, NULL);
+ rg->tcprg_size = 2;
+ rg->tcprg_count = 1;
+ rg->tcprg_active = 1;
+ rg->tcprg_members[0] = tcp;
+ return (rg);
+}
+
+void
+tcp_rg_destroy(tcp_rg_t *rg)
+{
+ mutex_enter(&rg->tcprg_lock);
+ ASSERT(rg->tcprg_count == 0);
+ ASSERT(rg->tcprg_active == 0);
+ kmem_free(rg->tcprg_members, rg->tcprg_size * sizeof (tcp_t *));
+ mutex_destroy(&rg->tcprg_lock);
+ kmem_free(rg, sizeof (struct tcp_rg_s));
+}
+
+static int
+tcp_rg_insert(tcp_rg_t *rg, tcp_t *tcp)
+{
+ mutex_enter(&rg->tcprg_lock);
+
+ VERIFY(rg->tcprg_size > 0);
+ VERIFY(rg->tcprg_count <= rg->tcprg_size);
+ if (rg->tcprg_count != 0) {
+ cred_t *oldcred = rg->tcprg_members[0]->tcp_connp->conn_cred;
+ cred_t *newcred = tcp->tcp_connp->conn_cred;
+
+ if (crgetuid(oldcred) != crgetuid(newcred) ||
+ crgetzoneid(oldcred) != crgetzoneid(newcred)) {
+ mutex_exit(&rg->tcprg_lock);
+ return (EPERM);
+ }
+ }
+
+ if (rg->tcprg_count == rg->tcprg_size) {
+ unsigned int oldalloc = rg->tcprg_size * sizeof (tcp_t *);
+ unsigned int newsize = rg->tcprg_size + TCP_RG_SIZE_STEP;
+ tcp_t **newmembers;
+
+ if (newsize > TCP_RG_SIZE_MAX) {
+ mutex_exit(&rg->tcprg_lock);
+ return (EINVAL);
+ }
+ newmembers = kmem_zalloc(newsize * sizeof (tcp_t *),
+ KM_NOSLEEP_LAZY);
+ if (newmembers == NULL) {
+ mutex_exit(&rg->tcprg_lock);
+ return (ENOMEM);
+ }
+ bcopy(rg->tcprg_members, newmembers, oldalloc);
+ kmem_free(rg->tcprg_members, oldalloc);
+ rg->tcprg_members = newmembers;
+ rg->tcprg_size = newsize;
+ }
+
+ rg->tcprg_members[rg->tcprg_count] = tcp;
+ rg->tcprg_count++;
+ rg->tcprg_active++;
+
+ mutex_exit(&rg->tcprg_lock);
+ return (0);
+}
+
+boolean_t
+tcp_rg_remove(tcp_rg_t *rg, tcp_t *tcp)
+{
+ int i;
+ boolean_t is_empty;
+
+ mutex_enter(&rg->tcprg_lock);
+ for (i = 0; i < rg->tcprg_count; i++) {
+ if (rg->tcprg_members[i] == tcp)
+ break;
+ }
+ /* The item should be present */
+ ASSERT(i < rg->tcprg_count);
+ /* Move the last member into this position */
+ rg->tcprg_count--;
+ rg->tcprg_members[i] = rg->tcprg_members[rg->tcprg_count];
+ rg->tcprg_members[rg->tcprg_count] = NULL;
+ if (tcp->tcp_connp->conn_reuseport != 0)
+ rg->tcprg_active--;
+ is_empty = (rg->tcprg_count == 0);
+ mutex_exit(&rg->tcprg_lock);
+ return (is_empty);
+}
+
+void
+tcp_rg_setactive(tcp_rg_t *rg, boolean_t is_active)
+{
+ mutex_enter(&rg->tcprg_lock);
+ if (is_active) {
+ rg->tcprg_active++;
+ } else {
+ rg->tcprg_active--;
+ }
+ mutex_exit(&rg->tcprg_lock);
+}
diff --git a/usr/src/uts/common/inet/tcp/tcp_input.c b/usr/src/uts/common/inet/tcp/tcp_input.c
index dd264528fc..22b0019a6a 100644
--- a/usr/src/uts/common/inet/tcp/tcp_input.c
+++ b/usr/src/uts/common/inet/tcp/tcp_input.c
@@ -5715,10 +5715,12 @@ noticmpv4:
switch (icmph->icmph_code) {
case ICMP_FRAGMENTATION_NEEDED:
/*
- * Update Path MTU, then try to send something out.
+ * Attempt to update path MTU and, if the MSS of the
+ * connection is altered, retransmit outstanding data.
*/
- tcp_update_pmtu(tcp, B_TRUE);
- tcp_rexmit_after_error(tcp);
+ if (tcp_update_pmtu(tcp, B_TRUE)) {
+ tcp_rexmit_after_error(tcp);
+ }
break;
case ICMP_PORT_UNREACHABLE:
case ICMP_PROTOCOL_UNREACHABLE:
@@ -5761,7 +5763,7 @@ noticmpv4:
break;
}
break;
- case ICMP_SOURCE_QUENCH: {
+ case ICMP_SOURCE_QUENCH:
/*
* use a global boolean to control
* whether TCP should respond to ICMP_SOURCE_QUENCH.
@@ -5786,7 +5788,6 @@ noticmpv4:
}
break;
}
- }
freemsg(mp);
}
@@ -5839,10 +5840,12 @@ noticmpv6:
switch (icmp6->icmp6_type) {
case ICMP6_PACKET_TOO_BIG:
/*
- * Update Path MTU, then try to send something out.
+ * Attempt to update path MTU and, if the MSS of the connection
+ * is altered, retransmit outstanding data.
*/
- tcp_update_pmtu(tcp, B_TRUE);
- tcp_rexmit_after_error(tcp);
+ if (tcp_update_pmtu(tcp, B_TRUE)) {
+ tcp_rexmit_after_error(tcp);
+ }
break;
case ICMP6_DST_UNREACH:
switch (icmp6->icmp6_code) {
diff --git a/usr/src/uts/common/inet/tcp/tcp_opt_data.c b/usr/src/uts/common/inet/tcp/tcp_opt_data.c
index 8687b52d53..15e49ae070 100644
--- a/usr/src/uts/common/inet/tcp/tcp_opt_data.c
+++ b/usr/src/uts/common/inet/tcp/tcp_opt_data.c
@@ -67,7 +67,8 @@ opdes_t tcp_opt_arr[] = {
{ SO_USELOOPBACK, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0
},
{ SO_BROADCAST, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
-{ SO_REUSEADDR, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
+{ SO_REUSEADDR, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
+{ SO_REUSEPORT, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
{ SO_OOBINLINE, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
{ SO_TYPE, SOL_SOCKET, OA_R, OA_R, OP_NP, 0, sizeof (int), 0 },
{ SO_SNDBUF, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
@@ -505,6 +506,104 @@ tcp_opt_get(conn_t *connp, int level, int name, uchar_t *ptr)
}
/*
+ * Set a TCP connection's participation in SO_REUSEPORT. This operation is
+ * performed under the protection of the squeue via tcp_setsockopt.
+ * The manipulation of tcp_rg_bind, as part of this operation, is subject to
+ * these constraints:
+ * 1. Prior to bind(), tcp_rg_bind can be set/cleared in tcp_set_reuseport
+ * under the protection of the squeue.
+ * 2. Once the connection has been bound, the tcp_rg_bind pointer must not be
+ * altered until such time as tcp_free() cleans up the connection.
+ * 3. A connection undergoing bind, which matches to a connection participating
+ * in port-reuse, will switch its tcp_rg_bind pointer when it joins the
+ * group of an existing connection in tcp_bindi().
+ */
+static int
+tcp_set_reuseport(conn_t *connp, boolean_t do_enable)
+{
+ tcp_t *tcp = connp->conn_tcp;
+ struct tcp_rg_s *rg;
+
+ if (!IPCL_IS_NONSTR(connp)) {
+ if (do_enable) {
+ /*
+ * SO_REUSEPORT cannot be enabled on sockets which have
+ * fallen back to the STREAMS API.
+ */
+ return (EINVAL);
+ } else {
+ /*
+ * A connection with SO_REUSEPORT enabled should be
+ * prevented from falling back to STREAMS mode via
+ * logic in tcp_fallback. It is legal, however, for
+ * fallen-back connections to affirm the disabled state
+ * of SO_REUSEPORT.
+ */
+ ASSERT(connp->conn_reuseport == 0);
+ return (0);
+ }
+ }
+ if (tcp->tcp_state <= TCPS_CLOSED) {
+ return (EINVAL);
+ }
+ if (connp->conn_reuseport == 0 && do_enable) {
+ /* disabled -> enabled */
+ if (tcp->tcp_rg_bind != NULL) {
+ tcp_rg_setactive(tcp->tcp_rg_bind, do_enable);
+ } else {
+ /*
+ * Connection state is not a concern when initially
+ * populating tcp_rg_bind. Setting it to non-NULL on a
+ * bound or listening connection would only mean that
+ * new reused-port binds become a possibility.
+ */
+ if ((rg = tcp_rg_init(tcp)) == NULL) {
+ return (ENOMEM);
+ }
+ tcp->tcp_rg_bind = rg;
+ }
+ connp->conn_reuseport = 1;
+ } else if (connp->conn_reuseport != 0 && !do_enable) {
+ /* enabled -> disabled */
+ ASSERT(tcp->tcp_rg_bind != NULL);
+ if (tcp->tcp_state == TCPS_IDLE) {
+ /*
+ * If the connection has not been bound yet, discard
+ * the reuse group state. Since disabling SO_REUSEPORT
+ * on a bound socket will _not_ prevent others from
+ * reusing the port, the presence of tcp_rg_bind is
+ * used to determine reuse availability, not
+ * conn_reuseport.
+ *
+ * This allows proper behavior for examples such as:
+ *
+ * setsockopt(fd1, ... SO_REUSEPORT, &on_val...);
+ * bind(fd1, &myaddr, ...);
+ * setsockopt(fd1, ... SO_REUSEPORT, &off_val...);
+ *
+ * setsockopt(fd2, ... SO_REUSEPORT, &on_val...);
+ * bind(fd2, &myaddr, ...); // <- SHOULD SUCCEED
+ *
+ */
+ rg = tcp->tcp_rg_bind;
+ tcp->tcp_rg_bind = NULL;
+ VERIFY(tcp_rg_remove(rg, tcp));
+ tcp_rg_destroy(rg);
+ } else {
+ /*
+ * If a connection has been bound, it's no longer safe
+ * to manipulate tcp_rg_bind until connection clean-up
+ * during tcp_free. Just mark the member status of the
+ * connection as inactive.
+ */
+ tcp_rg_setactive(tcp->tcp_rg_bind, do_enable);
+ }
+ connp->conn_reuseport = 0;
+ }
+ return (0);
+}
+
+/*
* We declare as 'int' rather than 'void' to satisfy pfi_t arg requirements.
* Parameters are assumed to be verified by the caller.
*/
@@ -674,6 +773,11 @@ tcp_opt_set(conn_t *connp, uint_t optset_context, int level, int name,
}
*outlenp = inlen;
return (0);
+ case SO_REUSEPORT:
+ if (!checkonly) {
+ return (tcp_set_reuseport(connp, *i1 != 0));
+ }
+ return (0);
}
break;
case IPPROTO_TCP:
@@ -1031,10 +1135,6 @@ tcp_opt_set(conn_t *connp, uint_t optset_context, int level, int name,
}
break;
case IPPROTO_IP:
- if (connp->conn_family != AF_INET) {
- *outlenp = 0;
- return (EINVAL);
- }
switch (name) {
case IP_SEC_OPT:
/*
diff --git a/usr/src/uts/common/inet/tcp/tcp_socket.c b/usr/src/uts/common/inet/tcp/tcp_socket.c
index 9b6c0daac3..32422be675 100644
--- a/usr/src/uts/common/inet/tcp/tcp_socket.c
+++ b/usr/src/uts/common/inet/tcp/tcp_socket.c
@@ -1029,6 +1029,16 @@ tcp_fallback(sock_lower_handle_t proto_handle, queue_t *q,
}
/*
+ * Do not allow fallback on connections making use of SO_REUSEPORT.
+ */
+ if (tcp->tcp_rg_bind != NULL) {
+ freeb(stropt_mp);
+ freeb(ordrel_mp);
+ squeue_synch_exit(connp, SQ_NODRAIN);
+ return (EINVAL);
+ }
+
+ /*
* Both endpoints must be of the same type (either STREAMS or
* non-STREAMS) for fusion to be enabled. So if we are fused,
* we have to unfuse.
diff --git a/usr/src/uts/common/inet/tcp/tcp_stats.c b/usr/src/uts/common/inet/tcp/tcp_stats.c
index e29c76a696..226467e167 100644
--- a/usr/src/uts/common/inet/tcp/tcp_stats.c
+++ b/usr/src/uts/common/inet/tcp/tcp_stats.c
@@ -21,8 +21,8 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent Inc. All rights reserved.
* Copyright (c) 2015, 2016 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
*/
@@ -131,9 +131,14 @@ tcp_set_conninfo(tcp_t *tcp, struct tcpConnEntryInfo_s *tcei, boolean_t ispriv)
tcei->ce_rto = tcp->tcp_rto;
tcei->ce_mss = tcp->tcp_mss;
tcei->ce_state = tcp->tcp_state;
- tcei->ce_rtt_sa = NSEC2USEC(tcp->tcp_rtt_sa >> 3);
tcei->ce_rtt_sum = NSEC2USEC(tcp->tcp_rtt_sum);
tcei->ce_rtt_cnt = tcp->tcp_rtt_cnt;
+
+ /* tcp_rtt_sa is stored as 8 times the average RTT */
+ tcei->ce_rtt_sa = NSEC2USEC(tcp->tcp_rtt_sa >> 3);
+
+ /* tcp_rtt_sd is stored as 4 times the average RTTVAR */
+ tcei->ce_rtt_sd = NSEC2USEC(tcp->tcp_rtt_sd >> 2);
}
/*
diff --git a/usr/src/uts/common/inet/tcp/tcp_timers.c b/usr/src/uts/common/inet/tcp/tcp_timers.c
index 5793a7fd27..7d9b449392 100644
--- a/usr/src/uts/common/inet/tcp/tcp_timers.c
+++ b/usr/src/uts/common/inet/tcp/tcp_timers.c
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright 2011 Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
* Copyright (c) 2014, 2017 by Delphix. All rights reserved.
*/
diff --git a/usr/src/uts/common/inet/tcp_impl.h b/usr/src/uts/common/inet/tcp_impl.h
index 5669592cff..61af05f749 100644
--- a/usr/src/uts/common/inet/tcp_impl.h
+++ b/usr/src/uts/common/inet/tcp_impl.h
@@ -61,9 +61,9 @@ extern sock_downcalls_t sock_tcp_downcalls;
* by setting it to 0.
*/
#define TCP_XMIT_LOWATER 4096
-#define TCP_XMIT_HIWATER 49152
+#define TCP_XMIT_HIWATER 128000
#define TCP_RECV_LOWATER 2048
-#define TCP_RECV_HIWATER 128000
+#define TCP_RECV_HIWATER 1048576
/*
* Bind hash list size and has function. It has to be a power of 2 for
@@ -395,6 +395,22 @@ typedef struct tcp_listen_cnt_s {
uint32_t tlc_drop;
} tcp_listen_cnt_t;
+/*
+ * Track tcp_t entities bound to the same port/address tuple via SO_REUSEPORT.
+ * - tcprg_lock: Protects the other fields
+ * - tcprg_size: Allocated size (in entries) of tcprg_members array
+ * - tcprg_count: Count of occupied tcprg_members slots
+ * - tcprg_active: Count of members which still have SO_REUSEPORT set
+ * - tcprg_members: Connections associated with address/port group
+ */
+typedef struct tcp_rg_s {
+ kmutex_t tcprg_lock;
+ unsigned int tcprg_size;
+ unsigned int tcprg_count;
+ unsigned int tcprg_active;
+ tcp_t **tcprg_members;
+} tcp_rg_t;
+
#define TCP_TLC_REPORT_INTERVAL (30 * MINUTES)
#define TCP_DECR_LISTEN_CNT(tcp) \
@@ -678,7 +694,7 @@ extern int tcp_rwnd_set(tcp_t *, uint32_t);
extern int tcp_set_destination(tcp_t *);
extern void tcp_set_ws_value(tcp_t *);
extern void tcp_stop_lingering(tcp_t *);
-extern void tcp_update_pmtu(tcp_t *, boolean_t);
+extern boolean_t tcp_update_pmtu(tcp_t *, boolean_t);
extern mblk_t *tcp_zcopy_backoff(tcp_t *, mblk_t *, boolean_t);
extern boolean_t tcp_zcopy_check(tcp_t *);
extern void tcp_zcopy_notify(tcp_t *);
@@ -695,6 +711,10 @@ extern in_port_t tcp_bindi(tcp_t *, in_port_t, const in6_addr_t *,
int, boolean_t, boolean_t, boolean_t);
extern in_port_t tcp_update_next_port(in_port_t, const tcp_t *,
boolean_t);
+extern tcp_rg_t *tcp_rg_init(tcp_t *);
+extern boolean_t tcp_rg_remove(tcp_rg_t *, tcp_t *);
+extern void tcp_rg_destroy(tcp_rg_t *);
+extern void tcp_rg_setactive(tcp_rg_t *, boolean_t);
/*
* Fusion related functions in tcp_fusion.c.
diff --git a/usr/src/uts/common/inet/udp/udp.c b/usr/src/uts/common/inet/udp/udp.c
index 5d42a69fa2..4e208465f2 100644
--- a/usr/src/uts/common/inet/udp/udp.c
+++ b/usr/src/uts/common/inet/udp/udp.c
@@ -1671,6 +1671,11 @@ udp_opt_get(conn_t *connp, t_scalar_t level, t_scalar_t name,
*i1 = udp->udp_vxlanhash;
mutex_exit(&connp->conn_lock);
return (sizeof (int));
+ case UDP_SND_TO_CONNECTED:
+ mutex_enter(&connp->conn_lock);
+ *i1 = udp->udp_snd_to_conn ? 1 : 0;
+ mutex_exit(&connp->conn_lock);
+ return (sizeof (int));
}
}
mutex_enter(&connp->conn_lock);
@@ -1826,6 +1831,11 @@ udp_do_opt_set(conn_opt_arg_t *coa, int level, int name,
}
/* Fully handled this option. */
return (0);
+ case UDP_SND_TO_CONNECTED:
+ mutex_enter(&connp->conn_lock);
+ udp->udp_snd_to_conn = onoff;
+ mutex_exit(&connp->conn_lock);
+ return (0);
}
break;
}
@@ -6096,10 +6106,18 @@ udp_send(sock_lower_handle_t proto_handle, mblk_t *mp, struct nmsghdr *msg,
else
return (error);
}
- if (udp->udp_state == TS_DATA_XFER) {
+
+ /*
+ * Check if we're allowed to send to a connection on which we've
+ * already called 'connect'. The posix spec. allows both behaviors but
+ * historically we've returned an error if already connected. The
+ * client can allow this via a sockopt.
+ */
+ if (udp->udp_state == TS_DATA_XFER && !udp->udp_snd_to_conn) {
UDPS_BUMP_MIB(us, udpOutErrors);
return (EISCONN);
}
+
error = proto_verify_ip_addr(connp->conn_family,
(struct sockaddr *)msg->msg_name, msg->msg_namelen);
if (error != 0) {
diff --git a/usr/src/uts/common/inet/udp/udp_opt_data.c b/usr/src/uts/common/inet/udp/udp_opt_data.c
index c8e7d79e47..9c05b8c876 100644
--- a/usr/src/uts/common/inet/udp/udp_opt_data.c
+++ b/usr/src/uts/common/inet/udp/udp_opt_data.c
@@ -294,7 +294,9 @@ opdes_t udp_opt_arr[] = {
},
{ UDP_NAT_T_ENDPOINT, IPPROTO_UDP, OA_RW, OA_RW, OP_PRIVPORT, 0, sizeof (int),
0 },
-{ UDP_SRCPORT_HASH, IPPROTO_UDP, OA_R, OA_RW, OP_CONFIG, 0, sizeof (int), 0 }
+{ UDP_SRCPORT_HASH, IPPROTO_UDP, OA_R, OA_RW, OP_CONFIG, 0, sizeof (int), 0 },
+{ UDP_SND_TO_CONNECTED, IPPROTO_UDP, OA_R, OA_RW, OP_CONFIG, 0, sizeof (int),
+ 0 }
};
/*
diff --git a/usr/src/uts/common/inet/udp_impl.h b/usr/src/uts/common/inet/udp_impl.h
index 0fc597ccf3..ef11973707 100644
--- a/usr/src/uts/common/inet/udp_impl.h
+++ b/usr/src/uts/common/inet/udp_impl.h
@@ -179,12 +179,12 @@ typedef struct udp_s {
udp_issocket : 1, /* socket mode; sockfs is on top */
udp_nat_t_endpoint : 1, /* UDP_NAT_T_ENDPOINT option */
udp_rcvhdr : 1, /* UDP_RCVHDR option */
-
udp_vxlanhash: 1, /* UDP_SRCPORT_HASH option */
/* Because there's only VXLAN, cheat */
/* and only use a single bit */
+ udp_snd_to_conn: 1, /* UDP_SND_TO_CONNECTED option */
- udp_pad_to_bit_31 : 28;
+ udp_pad_to_bit_31 : 27;
/* Following 2 fields protected by the uf_lock */
struct udp_s *udp_bind_hash; /* Bind hash chain */
diff --git a/usr/src/uts/common/io/bpf/bpf_wrap.c b/usr/src/uts/common/io/bpf/bpf_wrap.c
new file mode 100644
index 0000000000..6cbde58a20
--- /dev/null
+++ b/usr/src/uts/common/io/bpf/bpf_wrap.c
@@ -0,0 +1,35 @@
+/*
+ * 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 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <net/bpf.h>
+#include <inet/bpf.h>
+
+/*
+ * With BPF filter validation and evaluation moved into the 'ip' module, these
+ * wrapper functions are provided to expose the original interface.
+ */
+
+uint_t
+bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
+{
+ return ((uint_t)ip_bpf_filter((ip_bpf_insn_t *)pc, p, wirelen, buflen));
+}
+
+int
+bpf_validate(struct bpf_insn *f, int len)
+{
+ return ((int)ip_bpf_validate((ip_bpf_insn_t *)f, (uint_t)len));
+}
diff --git a/usr/src/uts/common/io/dld/dld_drv.c b/usr/src/uts/common/io/dld/dld_drv.c
index dbcd9caea8..676fca1249 100644
--- a/usr/src/uts/common/io/dld/dld_drv.c
+++ b/usr/src/uts/common/io/dld/dld_drv.c
@@ -348,8 +348,8 @@ drv_ioc_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
if ((err = dls_devnet_hold_tmp(diap->dia_linkid, &dlh)) != 0)
return (err);
- if ((err = mac_perim_enter_by_macname(
- dls_devnet_mac(dlh), &mph)) != 0) {
+ if ((err = mac_perim_enter_by_macname(dls_devnet_mac(dlh),
+ &mph)) != 0) {
dls_devnet_rele_tmp(dlh);
return (err);
}
@@ -361,7 +361,6 @@ drv_ioc_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
}
mac_sdu_get(dlp->dl_mh, NULL, &diap->dia_max_sdu);
-
dls_link_rele(dlp);
mac_perim_exit(mph);
dls_devnet_rele_tmp(dlh);
@@ -703,7 +702,8 @@ drv_ioc_prop_common(dld_ioc_macprop_t *prop, intptr_t arg, boolean_t set,
err = EACCES;
goto done;
}
- err = dls_devnet_setzid(dlh, dzp->diz_zid);
+ err = dls_devnet_setzid(dlh, dzp->diz_zid,
+ dzp->diz_transient);
} else {
kprop->pr_perm_flags = MAC_PROP_PERM_RW;
(*(zoneid_t *)kprop->pr_val) = dls_devnet_getzid(dlh);
@@ -877,7 +877,7 @@ drv_ioc_rename(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
return (err);
if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
- dir->dir_link)) != 0)
+ dir->dir_link, dir->dir_zoneinit)) != 0)
return (err);
if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
@@ -1332,10 +1332,13 @@ drv_ioc_gettran(void *karg, intptr_t arg, int mode, cred_t *cred,
dls_link_t *dlp = NULL;
dld_ioc_gettran_t *dgt = karg;
- if ((ret = mac_perim_enter_by_linkid(dgt->dgt_linkid, &mph)) != 0)
+ if ((ret = dls_devnet_hold_tmp(dgt->dgt_linkid, &dlh)) != 0)
goto done;
- if ((ret = dls_devnet_hold_link(dgt->dgt_linkid, &dlh, &dlp)) != 0)
+ if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
+ goto done;
+
+ if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
goto done;
/*
@@ -1354,13 +1357,14 @@ drv_ioc_gettran(void *karg, intptr_t arg, int mode, cred_t *cred,
}
done:
- if (dlh != NULL && dlp != NULL) {
- dls_devnet_rele_link(dlh, dlp);
- }
+ if (dlp != NULL)
+ dls_link_rele(dlp);
- if (mph != NULL) {
+ if (mph != NULL)
mac_perim_exit(mph);
- }
+
+ if (dlh != NULL)
+ dls_devnet_rele_tmp(dlh);
return (ret);
}
@@ -1384,10 +1388,13 @@ drv_ioc_readtran(void *karg, intptr_t arg, int mode, cred_t *cred,
if (dti->dti_nbytes != 256 || dti->dti_off != 0)
return (EINVAL);
- if ((ret = mac_perim_enter_by_linkid(dti->dti_linkid, &mph)) != 0)
+ if ((ret = dls_devnet_hold_tmp(dti->dti_linkid, &dlh)) != 0)
goto done;
- if ((ret = dls_devnet_hold_link(dti->dti_linkid, &dlh, &dlp)) != 0)
+ if ((ret = mac_perim_enter_by_macname(dls_devnet_mac(dlh), &mph)) != 0)
+ goto done;
+
+ if ((ret = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
goto done;
/*
@@ -1407,13 +1414,14 @@ drv_ioc_readtran(void *karg, intptr_t arg, int mode, cred_t *cred,
}
done:
- if (dlh != NULL && dlp != NULL) {
- dls_devnet_rele_link(dlh, dlp);
- }
+ if (dlp != NULL)
+ dls_link_rele(dlp);
- if (mph != NULL) {
+ if (mph != NULL)
mac_perim_exit(mph);
- }
+
+ if (dlh != NULL)
+ dls_devnet_rele_tmp(dlh);
return (ret);
}
@@ -1510,7 +1518,6 @@ done:
return (ret);
}
-
/*
* Note that ioctls that modify links have a NULL di_priv_func(), as
* privileges can only be checked after we know the class of the link being
diff --git a/usr/src/uts/common/io/dld/dld_proto.c b/usr/src/uts/common/io/dld/dld_proto.c
index c60392f853..596147f4e9 100644
--- a/usr/src/uts/common/io/dld/dld_proto.c
+++ b/usr/src/uts/common/io/dld/dld_proto.c
@@ -42,7 +42,7 @@ static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
- proto_notify_req, proto_passive_req;
+ proto_notify_req, proto_passive_req, proto_exclusive_req;
static void proto_capability_advertise(dld_str_t *, mblk_t *);
static int dld_capab_poll_disable(dld_str_t *, dld_capab_poll_t *);
@@ -122,6 +122,9 @@ dld_proto(dld_str_t *dsp, mblk_t *mp)
case DL_PASSIVE_REQ:
proto_passive_req(dsp, mp);
break;
+ case DL_EXCLUSIVE_REQ:
+ proto_exclusive_req(dsp, mp);
+ break;
default:
proto_req(dsp, mp);
break;
@@ -610,6 +613,10 @@ proto_promiscon_req(dld_str_t *dsp, mblk_t *mp)
new_flags |= DLS_PROMISC_RX_ONLY;
break;
+ case DL_PROMISC_FIXUPS:
+ new_flags |= DLS_PROMISC_FIXUPS;
+ break;
+
default:
dl_err = DL_NOTSUPPORTED;
goto failed2;
@@ -705,6 +712,14 @@ proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp)
new_flags &= ~DLS_PROMISC_RX_ONLY;
break;
+ case DL_PROMISC_FIXUPS:
+ if (!(dsp->ds_promisc & DLS_PROMISC_FIXUPS)) {
+ dl_err = DL_NOTENAB;
+ goto failed2;
+ }
+ new_flags &= ~DLS_PROMISC_FIXUPS;
+ break;
+
default:
dl_err = DL_NOTSUPPORTED;
goto failed2;
@@ -1305,7 +1320,8 @@ proto_passive_req(dld_str_t *dsp, mblk_t *mp)
* If we've already become active by issuing an active primitive,
* then it's too late to try to become passive.
*/
- if (dsp->ds_passivestate == DLD_ACTIVE) {
+ if (dsp->ds_passivestate == DLD_ACTIVE ||
+ dsp->ds_passivestate == DLD_EXCLUSIVE) {
dl_err = DL_OUTSTATE;
goto failed;
}
@@ -1359,12 +1375,20 @@ dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags)
ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
+ if (dsp->ds_sap == ETHERTYPE_IPV6)
+ return (ENOTSUP);
+
switch (flags) {
case DLD_ENABLE:
dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf,
direct->di_rx_ch);
- direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put;
+ if (direct->di_flags & DI_DIRECT_RAW) {
+ direct->di_tx_df =
+ (uintptr_t)str_mdata_raw_fastpath_put;
+ } else {
+ direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put;
+ }
direct->di_tx_dh = dsp;
direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify;
direct->di_tx_cb_dh = dsp->ds_mch;
@@ -1463,6 +1487,9 @@ dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags)
ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
+ if (dsp->ds_sap == ETHERTYPE_IPV6)
+ return (ENOTSUP);
+
switch (flags) {
case DLD_ENABLE:
return (dld_capab_poll_enable(dsp, poll));
@@ -1473,12 +1500,34 @@ dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags)
}
static int
+dld_capab_ipcheck(dld_str_t *dsp, void *data, uint_t flags)
+{
+ dld_capab_ipcheck_t *ipc = data;
+
+ ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
+
+ switch (flags) {
+ case DLD_ENABLE:
+ ipc->ipc_allowed_df = (uintptr_t)mac_protect_check_addr;
+ ipc->ipc_allowed_dh = dsp->ds_mch;
+ return (0);
+ case DLD_DISABLE:
+ return (0);
+ }
+
+ return (ENOTSUP);
+}
+
+static int
dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags)
{
dld_capab_lso_t *lso = data;
ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
+ if (dsp->ds_sap == ETHERTYPE_IPV6)
+ return (ENOTSUP);
+
switch (flags) {
case DLD_ENABLE: {
mac_capab_lso_t mac_lso;
@@ -1534,8 +1583,9 @@ dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags)
* completes. So we limit the check to DLD_ENABLE case.
*/
if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) &&
- (!(dsp->ds_sap == ETHERTYPE_IP || dsp->ds_sap == ETHERTYPE_IPV6) ||
- !check_mod_above(dsp->ds_rq, "ip"))) {
+ (((dsp->ds_sap != ETHERTYPE_IP && dsp->ds_sap != ETHERTYPE_IPV6) ||
+ !check_mod_above(dsp->ds_rq, "ip")) &&
+ !check_mod_above(dsp->ds_rq, "vnd"))) {
return (ENOTSUP);
}
@@ -1564,6 +1614,10 @@ dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags)
err = dld_capab_lso(dsp, data, flags);
break;
+ case DLD_CAPAB_IPCHECK:
+ err = dld_capab_ipcheck(dsp, data, flags);
+ break;
+
default:
err = ENOTSUP;
break;
@@ -1625,10 +1679,15 @@ proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
}
/*
- * Direct capability negotiation interface between IP and DLD
+ * Direct capability negotiation interface between IP/VND and DLD. Note
+ * that for vnd we only allow the case where the media type is the
+ * native media type so we know that there are no transformations that
+ * would have to happen to the mac header that it receives.
*/
- if ((dsp->ds_sap == ETHERTYPE_IP || dsp->ds_sap == ETHERTYPE_IPV6) &&
- check_mod_above(dsp->ds_rq, "ip")) {
+ if (((dsp->ds_sap == ETHERTYPE_IP || dsp->ds_sap == ETHERTYPE_IPV6) &&
+ check_mod_above(dsp->ds_rq, "ip")) ||
+ (check_mod_above(dsp->ds_rq, "vnd") &&
+ dsp->ds_mip->mi_media == dsp->ds_mip->mi_nativemedia)) {
dld_capable = B_TRUE;
subsize += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_dld_t);
@@ -1747,3 +1806,36 @@ dld_capabilities_disable(dld_str_t *dsp)
if (dsp->ds_polling)
(void) dld_capab_poll_disable(dsp, NULL);
}
+
+static void
+proto_exclusive_req(dld_str_t *dsp, mblk_t *mp)
+{
+ int ret = 0;
+ t_uscalar_t dl_err;
+ mac_perim_handle_t mph;
+
+ if (dsp->ds_passivestate != DLD_UNINITIALIZED) {
+ dl_err = DL_OUTSTATE;
+ goto failed;
+ }
+
+ if (MBLKL(mp) < DL_EXCLUSIVE_REQ_SIZE) {
+ dl_err = DL_BADPRIM;
+ goto failed;
+ }
+
+ mac_perim_enter_by_mh(dsp->ds_mh, &mph);
+ ret = dls_exclusive_set(dsp, B_TRUE);
+ mac_perim_exit(mph);
+
+ if (ret != 0) {
+ dl_err = DL_SYSERR;
+ goto failed;
+ }
+
+ dsp->ds_passivestate = DLD_EXCLUSIVE;
+ dlokack(dsp->ds_wq, mp, DL_EXCLUSIVE_REQ);
+ return;
+failed:
+ dlerrorack(dsp->ds_wq, mp, DL_EXCLUSIVE_REQ, dl_err, (t_uscalar_t)ret);
+}
diff --git a/usr/src/uts/common/io/dld/dld_str.c b/usr/src/uts/common/io/dld/dld_str.c
index 9f89165455..e9e98441b5 100644
--- a/usr/src/uts/common/io/dld/dld_str.c
+++ b/usr/src/uts/common/io/dld/dld_str.c
@@ -23,6 +23,10 @@
*/
/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
* Data-Link Driver
*/
@@ -857,6 +861,77 @@ i_dld_ether_header_update_tag(mblk_t *mp, uint_t pri, uint16_t vid,
return (mp);
}
+static boolean_t
+i_dld_raw_ether_check(dld_str_t *dsp, mac_header_info_t *mhip, mblk_t **mpp)
+{
+ mblk_t *mp = *mpp;
+ mblk_t *newmp;
+ uint_t pri, vid, dvid;
+
+ dvid = mac_client_vid(dsp->ds_mch);
+
+ /*
+ * Discard the packet if this is a VLAN stream but the VID in
+ * the packet is not correct.
+ */
+ vid = VLAN_ID(mhip->mhi_tci);
+ if ((dvid != VLAN_ID_NONE) && (vid != VLAN_ID_NONE))
+ return (B_FALSE);
+
+ /*
+ * Discard the packet if this packet is a tagged packet
+ * but both pri and VID are 0.
+ */
+ pri = VLAN_PRI(mhip->mhi_tci);
+ if (mhip->mhi_istagged && !mhip->mhi_ispvid && pri == 0 &&
+ vid == VLAN_ID_NONE)
+ return (B_FALSE);
+
+ /*
+ * Update the priority bits to the per-stream priority if
+ * priority is not set in the packet. Update the VID for
+ * packets on a VLAN stream.
+ */
+ pri = (pri == 0) ? dsp->ds_pri : 0;
+ if ((pri != 0) || (dvid != VLAN_ID_NONE)) {
+ if ((newmp = i_dld_ether_header_update_tag(mp, pri,
+ dvid, dsp->ds_dlp->dl_tagmode)) == NULL) {
+ return (B_FALSE);
+ }
+ *mpp = newmp;
+ }
+
+ return (B_TRUE);
+}
+
+mac_tx_cookie_t
+str_mdata_raw_fastpath_put(dld_str_t *dsp, mblk_t *mp, uintptr_t f_hint,
+ uint16_t flag)
+{
+ boolean_t is_ethernet = (dsp->ds_mip->mi_media == DL_ETHER);
+ mac_header_info_t mhi;
+ mac_tx_cookie_t cookie;
+
+ if (mac_vlan_header_info(dsp->ds_mh, mp, &mhi) != 0)
+ goto discard;
+
+ if (is_ethernet) {
+ if (i_dld_raw_ether_check(dsp, &mhi, &mp) == B_FALSE)
+ goto discard;
+ }
+
+ if ((cookie = DLD_TX(dsp, mp, f_hint, flag)) != (mac_tx_cookie_t)NULL) {
+ DLD_SETQFULL(dsp);
+ }
+ return (cookie);
+discard:
+ /* TODO: bump kstat? */
+ freemsg(mp);
+ return ((mac_tx_cookie_t)NULL);
+}
+
+
+
/*
* M_DATA put (IP fast-path mode)
*/
@@ -905,7 +980,6 @@ str_mdata_raw_put(dld_str_t *dsp, mblk_t *mp)
mblk_t *bp, *newmp;
size_t size;
mac_header_info_t mhi;
- uint_t pri, vid, dvid;
uint_t max_sdu;
/*
@@ -951,38 +1025,8 @@ str_mdata_raw_put(dld_str_t *dsp, mblk_t *mp)
goto discard;
if (is_ethernet) {
- dvid = mac_client_vid(dsp->ds_mch);
-
- /*
- * Discard the packet if this is a VLAN stream but the VID in
- * the packet is not correct.
- */
- vid = VLAN_ID(mhi.mhi_tci);
- if ((dvid != VLAN_ID_NONE) && (vid != VLAN_ID_NONE))
- goto discard;
-
- /*
- * Discard the packet if this packet is a tagged packet
- * but both pri and VID are 0.
- */
- pri = VLAN_PRI(mhi.mhi_tci);
- if (mhi.mhi_istagged && !mhi.mhi_ispvid && pri == 0 &&
- vid == VLAN_ID_NONE)
+ if (i_dld_raw_ether_check(dsp, &mhi, &mp) == B_FALSE)
goto discard;
-
- /*
- * Update the priority bits to the per-stream priority if
- * priority is not set in the packet. Update the VID for
- * packets on a VLAN stream.
- */
- pri = (pri == 0) ? dsp->ds_pri : 0;
- if ((pri != 0) || (dvid != VLAN_ID_NONE)) {
- if ((newmp = i_dld_ether_header_update_tag(mp, pri,
- dvid, dsp->ds_dlp->dl_tagmode)) == NULL) {
- goto discard;
- }
- mp = newmp;
- }
}
if (DLD_TX(dsp, mp, 0, 0) != 0) {
diff --git a/usr/src/uts/common/io/dls/dls.c b/usr/src/uts/common/io/dls/dls.c
index b26637203f..3fa65ef35d 100644
--- a/usr/src/uts/common/io/dls/dls.c
+++ b/usr/src/uts/common/io/dls/dls.c
@@ -250,7 +250,7 @@ dls_promisc(dld_str_t *dsp, uint32_t new_flags)
{
int err = 0;
uint32_t old_flags = dsp->ds_promisc;
- const uint32_t option_flags = DLS_PROMISC_RX_ONLY;
+ const uint32_t option_flags = DLS_PROMISC_RX_ONLY | DLS_PROMISC_FIXUPS;
uint32_t old_type = old_flags & ~option_flags;
uint32_t new_type = new_flags & ~option_flags;
mac_client_promisc_type_t mptype = MAC_CLIENT_PROMISC_ALL;
@@ -274,6 +274,8 @@ dls_promisc(dld_str_t *dsp, uint32_t new_flags)
*/
if (new_flags & DLS_PROMISC_RX_ONLY)
mac_flags |= MAC_PROMISC_FLAGS_NO_TX_LOOP;
+ if (new_flags & DLS_PROMISC_FIXUPS)
+ mac_flags |= MAC_PROMISC_FLAGS_DO_FIXUPS;
if (new_type == DLS_PROMISC_SAP)
mac_flags |= MAC_PROMISC_FLAGS_NO_PHYS;
@@ -643,6 +645,22 @@ boolean_t
dls_accept_promisc(dld_str_t *dsp, mac_header_info_t *mhip, dls_rx_t *ds_rx,
void **ds_rx_arg, boolean_t loopback)
{
+ if (dsp->ds_promisc == 0) {
+ /*
+ * If there are active walkers of the mi_promisc_list when
+ * promiscuousness is disabled, ds_promisc will be cleared,
+ * but the DLS will remain on the mi_promisc_list until the
+ * walk is completed. If we do not recognize this case here,
+ * we won't properly execute the ds_promisc case in the common
+ * accept routine -- and we will potentially accept a packet
+ * that has originated with this DLS (which in turn can
+ * induce recursion and death by stack overflow). If
+ * ds_promisc is zero, we know that we are in this window --
+ * and we refuse to accept the packet.
+ */
+ return (B_FALSE);
+ }
+
return (dls_accept_common(dsp, mhip, ds_rx, ds_rx_arg, B_TRUE,
loopback));
}
@@ -673,7 +691,10 @@ dls_mac_active_set(dls_link_t *dlp)
* Set the function to start receiving packets.
*/
mac_rx_set(dlp->dl_mch, i_dls_link_rx, dlp);
+ } else if (dlp->dl_exclusive == B_TRUE) {
+ return (EBUSY);
}
+
dlp->dl_nactive++;
return (0);
}
@@ -699,7 +720,11 @@ dls_active_set(dld_str_t *dsp)
if (dsp->ds_passivestate == DLD_PASSIVE)
return (0);
- /* If we're already active, then there's nothing more to do. */
+ if (dsp->ds_dlp->dl_exclusive == B_TRUE &&
+ dsp->ds_passivestate != DLD_EXCLUSIVE)
+ return (EBUSY);
+
+ /* If we're already active, we need to check the link's exclusivity */
if ((dsp->ds_nactive == 0) &&
((err = dls_mac_active_set(dsp->ds_dlp)) != 0)) {
/* except for ENXIO all other errors are mapped to EBUSY */
@@ -708,7 +733,8 @@ dls_active_set(dld_str_t *dsp)
return (err);
}
- dsp->ds_passivestate = DLD_ACTIVE;
+ dsp->ds_passivestate = dsp->ds_dlp->dl_exclusive == B_TRUE ?
+ DLD_EXCLUSIVE : DLD_ACTIVE;
dsp->ds_nactive++;
return (0);
}
@@ -739,7 +765,32 @@ dls_active_clear(dld_str_t *dsp, boolean_t all)
if (dsp->ds_nactive != 0)
return;
- ASSERT(dsp->ds_passivestate == DLD_ACTIVE);
+ ASSERT(dsp->ds_passivestate == DLD_ACTIVE ||
+ dsp->ds_passivestate == DLD_EXCLUSIVE);
dls_mac_active_clear(dsp->ds_dlp);
+ /*
+ * We verify below to ensure that no other part of DLS has mucked with
+ * our exclusive state.
+ */
+ if (dsp->ds_passivestate == DLD_EXCLUSIVE)
+ VERIFY(dls_exclusive_set(dsp, B_FALSE) == 0);
dsp->ds_passivestate = DLD_UNINITIALIZED;
}
+
+int
+dls_exclusive_set(dld_str_t *dsp, boolean_t enable)
+{
+ ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
+
+ if (enable == B_FALSE) {
+ dsp->ds_dlp->dl_exclusive = B_FALSE;
+ return (0);
+ }
+
+ if (dsp->ds_dlp->dl_nactive != 0)
+ return (EBUSY);
+
+ dsp->ds_dlp->dl_exclusive = B_TRUE;
+
+ return (0);
+}
diff --git a/usr/src/uts/common/io/dls/dls_link.c b/usr/src/uts/common/io/dls/dls_link.c
index 4099d0b801..eee3569b10 100644
--- a/usr/src/uts/common/io/dls/dls_link.c
+++ b/usr/src/uts/common/io/dls/dls_link.c
@@ -686,6 +686,7 @@ i_dls_link_destroy(dls_link_t *dlp)
dlp->dl_mnh = NULL;
dlp->dl_unknowns = 0;
dlp->dl_nonip_cnt = 0;
+ dlp->dl_exclusive = B_FALSE;
kmem_cache_free(i_dls_link_cachep, dlp);
}
diff --git a/usr/src/uts/common/io/dls/dls_mgmt.c b/usr/src/uts/common/io/dls/dls_mgmt.c
index 49e867a19e..90b65ab36a 100644
--- a/usr/src/uts/common/io/dls/dls_mgmt.c
+++ b/usr/src/uts/common/io/dls/dls_mgmt.c
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2019 Joyent, Inc.
*/
/*
* Copyright (c) 2016 by Delphix. All rights reserved.
@@ -85,6 +86,14 @@ static door_handle_t dls_mgmt_dh = NULL;
/* dls_devnet_t dd_flags */
#define DD_CONDEMNED 0x1
#define DD_IMPLICIT_IPTUN 0x2 /* Implicitly-created ip*.*tun* tunnel */
+#define DD_INITIALIZING 0x4
+
+/*
+ * If the link is marked as initializing or condemned then it should
+ * not be visible outside of the DLS framework.
+ */
+#define DD_NOT_VISIBLE(flags) ( \
+ (flags & (DD_CONDEMNED | DD_INITIALIZING)) != 0)
/*
* This structure is used to keep the <linkid, macname> mapping.
@@ -108,13 +117,14 @@ typedef struct dls_devnet_s {
zoneid_t dd_zid; /* current zone */
boolean_t dd_prop_loaded;
taskqid_t dd_prop_taskid;
+ boolean_t dd_transient; /* link goes away when zone does */
} dls_devnet_t;
static int i_dls_devnet_create_iptun(const char *, const char *,
datalink_id_t *);
static int i_dls_devnet_destroy_iptun(datalink_id_t);
-static int i_dls_devnet_setzid(dls_devnet_t *, zoneid_t, boolean_t);
-static int dls_devnet_unset(const char *, datalink_id_t *, boolean_t);
+static int i_dls_devnet_setzid(dls_devnet_t *, zoneid_t, boolean_t, boolean_t);
+static int dls_devnet_unset(mac_handle_t, datalink_id_t *, boolean_t);
/*ARGSUSED*/
static int
@@ -134,9 +144,9 @@ i_dls_devnet_destructor(void *buf, void *arg)
{
dls_devnet_t *ddp = buf;
- ASSERT(ddp->dd_ksp == NULL);
- ASSERT(ddp->dd_ref == 0);
- ASSERT(ddp->dd_tref == 0);
+ VERIFY(ddp->dd_ksp == NULL);
+ VERIFY(ddp->dd_ref == 0);
+ VERIFY(ddp->dd_tref == 0);
mutex_destroy(&ddp->dd_mutex);
cv_destroy(&ddp->dd_cv);
}
@@ -148,7 +158,12 @@ dls_zone_remove(datalink_id_t linkid, void *arg)
dls_devnet_t *ddp;
if (dls_devnet_hold_tmp(linkid, &ddp) == 0) {
- (void) dls_devnet_setzid(ddp, GLOBAL_ZONEID);
+ /*
+ * Don't bother moving transient links back to the global zone
+ * since we will simply delete them in dls_devnet_unset.
+ */
+ if (!ddp->dd_transient)
+ (void) dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE);
dls_devnet_rele_tmp(ddp);
}
return (0);
@@ -529,6 +544,7 @@ dls_mgmt_get_linkid(const char *link, datalink_id_t *linkid)
getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID;
(void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN);
+ getlinkid.ld_zoneid = getzoneid();
if ((err = i_dls_mgmt_upcall(&getlinkid, sizeof (getlinkid), &retval,
sizeof (retval))) == 0) {
@@ -537,6 +553,27 @@ dls_mgmt_get_linkid(const char *link, datalink_id_t *linkid)
return (err);
}
+int
+dls_mgmt_get_linkid_in_zone(const char *link, datalink_id_t *linkid,
+ zoneid_t zid)
+{
+ dlmgmt_door_getlinkid_t getlinkid;
+ dlmgmt_getlinkid_retval_t retval;
+ int err;
+
+ ASSERT(getzoneid() == GLOBAL_ZONEID || zid == getzoneid());
+ getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID;
+ (void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN);
+ getlinkid.ld_zoneid = zid;
+
+ if ((err = i_dls_mgmt_upcall(&getlinkid, sizeof (getlinkid), &retval,
+ sizeof (retval))) == 0) {
+ *linkid = retval.lr_linkid;
+ }
+ return (err);
+}
+
+
datalink_id_t
dls_mgmt_get_next(datalink_id_t linkid, datalink_class_t class,
datalink_media_t dmedia, uint32_t flags)
@@ -736,13 +773,24 @@ dls_devnet_stat_update(kstat_t *ksp, int rw)
* Create the "link" kstats.
*/
static void
-dls_devnet_stat_create(dls_devnet_t *ddp, zoneid_t zoneid)
+dls_devnet_stat_create(dls_devnet_t *ddp, zoneid_t zoneid, zoneid_t newzoneid)
{
kstat_t *ksp;
+ char *nm;
+ char kname[MAXLINKNAMELEN];
+
+ if (zoneid != newzoneid) {
+ ASSERT(zoneid == GLOBAL_ZONEID);
+ (void) snprintf(kname, sizeof (kname), "z%d_%s", newzoneid,
+ ddp->dd_linkname);
+ nm = kname;
+ } else {
+ nm = ddp->dd_linkname;
+ }
- if (dls_stat_create("link", 0, ddp->dd_linkname, zoneid,
+ if (dls_stat_create("link", 0, nm, zoneid,
dls_devnet_stat_update, (void *)(uintptr_t)ddp->dd_linkid,
- &ksp) == 0) {
+ &ksp, newzoneid) == 0) {
ASSERT(ksp != NULL);
if (zoneid == ddp->dd_owner_zid) {
ASSERT(ddp->dd_ksp == NULL);
@@ -762,12 +810,12 @@ dls_devnet_stat_destroy(dls_devnet_t *ddp, zoneid_t zoneid)
{
if (zoneid == ddp->dd_owner_zid) {
if (ddp->dd_ksp != NULL) {
- kstat_delete(ddp->dd_ksp);
+ dls_stat_delete(ddp->dd_ksp);
ddp->dd_ksp = NULL;
}
} else {
if (ddp->dd_zone_ksp != NULL) {
- kstat_delete(ddp->dd_zone_ksp);
+ dls_stat_delete(ddp->dd_zone_ksp);
ddp->dd_zone_ksp = NULL;
}
}
@@ -778,24 +826,38 @@ dls_devnet_stat_destroy(dls_devnet_t *ddp, zoneid_t zoneid)
* and create the new set using the new name.
*/
static void
-dls_devnet_stat_rename(dls_devnet_t *ddp)
+dls_devnet_stat_rename(dls_devnet_t *ddp, boolean_t zoneinit)
{
if (ddp->dd_ksp != NULL) {
- kstat_delete(ddp->dd_ksp);
+ dls_stat_delete(ddp->dd_ksp);
ddp->dd_ksp = NULL;
}
- /* We can't rename a link while it's assigned to a non-global zone. */
+ if (zoneinit && ddp->dd_zone_ksp != NULL) {
+ dls_stat_delete(ddp->dd_zone_ksp);
+ ddp->dd_zone_ksp = NULL;
+ }
+ /*
+ * We can't rename a link while it's assigned to a non-global zone
+ * unless we're first initializing the zone while readying it.
+ */
ASSERT(ddp->dd_zone_ksp == NULL);
- dls_devnet_stat_create(ddp, ddp->dd_owner_zid);
+ dls_devnet_stat_create(ddp, ddp->dd_owner_zid,
+ (zoneinit ? ddp->dd_zid : ddp->dd_owner_zid));
+ if (zoneinit)
+ dls_devnet_stat_create(ddp, ddp->dd_zid, ddp->dd_zid);
}
/*
- * Associate a linkid with a given link (identified by macname)
+ * Associate the linkid with the link identified by macname. If this
+ * is called on behalf of a physical link then linkid may be
+ * DATALINK_INVALID_LINKID. Otherwise, if called on behalf of a
+ * virtual link, linkid must have a value.
*/
static int
-dls_devnet_set(const char *macname, datalink_id_t linkid, zoneid_t zoneid,
+dls_devnet_set(mac_handle_t mh, datalink_id_t linkid, zoneid_t zoneid,
dls_devnet_t **ddpp)
{
+ const char *macname = mac_name(mh);
dls_devnet_t *ddp = NULL;
datalink_class_t class;
int err;
@@ -828,17 +890,41 @@ dls_devnet_set(const char *macname, datalink_id_t linkid, zoneid_t zoneid,
}
/*
- * This might be a physical link that has already
- * been created, but which does not have a linkid
- * because dlmgmtd was not running when it was created.
+ * If we arrive here we know we are attempting to set
+ * the linkid on a physical link. A virtual link
+ * should never arrive here because it should never
+ * call this function without a linkid. Virtual links
+ * are created through dlgmtmd and thus we know
+ * dlmgmtd is alive to assign it a linkid (search for
+ * uses of dladm_create_datalink_id() to prove this to
+ * yourself); we don't have the same guarantee for a
+ * physical link which may perform an upcall for a
+ * linkid while dlmgmtd is down but will continue
+ * creating a devnet without the linkid (see
+ * softmac_create_datalink() to see how physical link
+ * creation works). That is why there is no entry in
+ * the id hash but there is one in the macname hash --
+ * softmac couldn't acquire a linkid the first time it
+ * called this function.
+ *
+ * Because of the check above, we also know that
+ * ddp->dd_linkid is not set. Following this, the link
+ * must still be in the DD_INITIALIZING state because
+ * that flag is removed IFF dd_linkid is set. This is
+ * why we can ASSERT the DD_INITIALIZING flag below if
+ * the call to i_dls_devnet_setzid() fails.
*/
if (linkid == DATALINK_INVALID_LINKID ||
class != DATALINK_CLASS_PHYS) {
err = EINVAL;
goto done;
}
+
+ ASSERT(ddp->dd_flags & DD_INITIALIZING);
+
} else {
ddp = kmem_cache_alloc(i_dls_devnet_cachep, KM_SLEEP);
+ ddp->dd_flags = DD_INITIALIZING;
ddp->dd_tref = 0;
ddp->dd_ref++;
ddp->dd_owner_zid = zoneid;
@@ -875,8 +961,19 @@ done:
rw_exit(&i_dls_devnet_lock);
if (err == 0) {
if (zoneid != GLOBAL_ZONEID &&
- (err = i_dls_devnet_setzid(ddp, zoneid, B_FALSE)) != 0)
- (void) dls_devnet_unset(macname, &linkid, B_TRUE);
+ (err = i_dls_devnet_setzid(ddp, zoneid, B_FALSE,
+ B_FALSE)) != 0) {
+ /*
+ * At this point the link is marked as
+ * DD_INITIALIZING -- there can be no
+ * outstanding temp refs and therefore no need
+ * to wait for them.
+ */
+ ASSERT(ddp->dd_flags & DD_INITIALIZING);
+ (void) dls_devnet_unset(mh, &linkid, B_FALSE);
+ return (err);
+ }
+
/*
* The kstat subsystem holds its own locks (rather perimeter)
* before calling the ks_update (dls_devnet_stat_update) entry
@@ -884,20 +981,35 @@ done:
* lock hierarchy is kstat locks -> i_dls_devnet_lock.
*/
if (stat_create)
- dls_devnet_stat_create(ddp, zoneid);
+ dls_devnet_stat_create(ddp, zoneid, zoneid);
if (ddpp != NULL)
*ddpp = ddp;
+
+ mutex_enter(&ddp->dd_mutex);
+ if (linkid != DATALINK_INVALID_LINKID && !ddp->dd_prop_loaded &&
+ ddp->dd_prop_taskid == TASKQID_INVALID) {
+ ddp->dd_prop_taskid = taskq_dispatch(system_taskq,
+ dls_devnet_prop_task, ddp, TQ_SLEEP);
+ }
+ mutex_exit(&ddp->dd_mutex);
+
}
return (err);
}
/*
- * Disassociate a linkid with a given link (identified by macname)
- * This waits until temporary references to the dls_devnet_t are gone.
+ * Disassociate the linkid from the link identified by macname. If
+ * wait is B_TRUE, wait until all temporary refs are released and the
+ * prop task is finished.
+ *
+ * If waiting then you SHOULD NOT call this from inside the MAC perim
+ * as deadlock will ensue. Otherwise, this function is safe to call
+ * from inside or outside the MAC perim.
*/
static int
-dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait)
+dls_devnet_unset(mac_handle_t mh, datalink_id_t *id, boolean_t wait)
{
+ const char *macname = mac_name(mh);
dls_devnet_t *ddp;
int err;
mod_hash_val_t val;
@@ -918,21 +1030,62 @@ dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait)
* deadlock. Return EBUSY if the asynchronous thread started for
* property loading as part of the post attach hasn't yet completed.
*/
- ASSERT(ddp->dd_ref != 0);
+ VERIFY(ddp->dd_ref != 0);
if ((ddp->dd_ref != 1) || (!wait &&
(ddp->dd_tref != 0 || ddp->dd_prop_taskid != 0))) {
- mutex_exit(&ddp->dd_mutex);
- rw_exit(&i_dls_devnet_lock);
- return (EBUSY);
+ int zstatus = 0;
+
+ /*
+ * There are a couple of alternatives that might be going on
+ * here; a) the zone is shutting down and it has a transient
+ * link assigned, in which case we want to clean it up instead
+ * of moving it back to the global zone, or b) its possible
+ * that we're trying to clean up an orphaned vnic that was
+ * delegated to a zone and which wasn't cleaned up properly
+ * when the zone went away. Check for either of these cases
+ * before we simply return EBUSY.
+ *
+ * zstatus indicates which situation we are dealing with:
+ * 0 - means return EBUSY
+ * 1 - means case (a), cleanup transient link
+ * -1 - means case (b), orphained VNIC
+ */
+ if (ddp->dd_ref > 1 && ddp->dd_zid != GLOBAL_ZONEID) {
+ zone_t *zp;
+
+ if ((zp = zone_find_by_id(ddp->dd_zid)) == NULL) {
+ zstatus = -1;
+ } else {
+ if (ddp->dd_transient) {
+ zone_status_t s = zone_status_get(zp);
+
+ if (s >= ZONE_IS_SHUTTING_DOWN)
+ zstatus = 1;
+ }
+ zone_rele(zp);
+ }
+ }
+
+ if (zstatus == 0) {
+ mutex_exit(&ddp->dd_mutex);
+ rw_exit(&i_dls_devnet_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * We want to delete the link, reset ref to 1;
+ */
+ if (zstatus == -1)
+ /* Log a warning, but continue in this case */
+ cmn_err(CE_WARN, "clear orphaned datalink: %s\n",
+ ddp->dd_linkname);
+ ddp->dd_ref = 1;
}
ddp->dd_flags |= DD_CONDEMNED;
ddp->dd_ref--;
*id = ddp->dd_linkid;
- if (ddp->dd_zid != GLOBAL_ZONEID)
- (void) i_dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE);
-
/*
* Remove this dls_devnet_t from the hash table.
*/
@@ -947,19 +1100,40 @@ dls_devnet_unset(const char *macname, datalink_id_t *id, boolean_t wait)
}
rw_exit(&i_dls_devnet_lock);
+ /*
+ * It is important to call i_dls_devnet_setzid() WITHOUT the
+ * i_dls_devnet_lock held. The setzid call grabs the MAC
+ * perim; thus causing DLS -> MAC lock ordering if performed
+ * with the i_dls_devnet_lock held. This forces consumers to
+ * grab the MAC perim before calling dls_devnet_unset() (the
+ * locking rules state MAC -> DLS order). By performing the
+ * setzid outside of the i_dls_devnet_lock consumers can
+ * safely call dls_devnet_unset() outside the MAC perim.
+ */
+ if (ddp->dd_zid != GLOBAL_ZONEID) {
+ dls_devnet_stat_destroy(ddp, ddp->dd_zid);
+ (void) i_dls_devnet_setzid(ddp, GLOBAL_ZONEID, B_FALSE,
+ B_FALSE);
+ }
+
if (wait) {
/*
* Wait until all temporary references are released.
+ * The holders of the tref need the MAC perim to
+ * perform their work and release the tref. To avoid
+ * deadlock, assert that the perim is never held here.
*/
+ ASSERT0(MAC_PERIM_HELD(mh));
while ((ddp->dd_tref != 0) || (ddp->dd_prop_taskid != 0))
cv_wait(&ddp->dd_cv, &ddp->dd_mutex);
} else {
- ASSERT(ddp->dd_tref == 0 &&
- ddp->dd_prop_taskid == (taskqid_t)NULL);
+ VERIFY(ddp->dd_tref == 0);
+ VERIFY(ddp->dd_prop_taskid == (taskqid_t)NULL);
}
- if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
+ if (ddp->dd_linkid != DATALINK_INVALID_LINKID) {
dls_devnet_stat_destroy(ddp, ddp->dd_owner_zid);
+ }
ddp->dd_prop_loaded = B_FALSE;
ddp->dd_linkid = DATALINK_INVALID_LINKID;
@@ -1019,8 +1193,8 @@ dls_devnet_hold_common(datalink_id_t linkid, dls_devnet_t **ddpp,
}
mutex_enter(&ddp->dd_mutex);
- ASSERT(ddp->dd_ref > 0);
- if (ddp->dd_flags & DD_CONDEMNED) {
+ VERIFY(ddp->dd_ref > 0);
+ if (DD_NOT_VISIBLE(ddp->dd_flags)) {
mutex_exit(&ddp->dd_mutex);
rw_exit(&i_dls_devnet_lock);
return (ENOENT);
@@ -1087,8 +1261,8 @@ dls_devnet_hold_by_dev(dev_t dev, dls_dl_handle_t *ddhp)
return (ENOENT);
}
mutex_enter(&ddp->dd_mutex);
- ASSERT(ddp->dd_ref > 0);
- if (ddp->dd_flags & DD_CONDEMNED) {
+ VERIFY(ddp->dd_ref > 0);
+ if (DD_NOT_VISIBLE(ddp->dd_flags)) {
mutex_exit(&ddp->dd_mutex);
rw_exit(&i_dls_devnet_lock);
return (ENOENT);
@@ -1105,7 +1279,7 @@ void
dls_devnet_rele(dls_devnet_t *ddp)
{
mutex_enter(&ddp->dd_mutex);
- ASSERT(ddp->dd_ref > 1);
+ VERIFY(ddp->dd_ref > 1);
ddp->dd_ref--;
if ((ddp->dd_flags & DD_IMPLICIT_IPTUN) && ddp->dd_ref == 1) {
mutex_exit(&ddp->dd_mutex);
@@ -1117,7 +1291,7 @@ dls_devnet_rele(dls_devnet_t *ddp)
}
static int
-dls_devnet_hold_by_name(const char *link, dls_devnet_t **ddpp)
+dls_devnet_hold_by_name(const char *link, dls_devnet_t **ddpp, zoneid_t zid)
{
char drv[MAXLINKNAMELEN];
uint_t ppa;
@@ -1127,7 +1301,7 @@ dls_devnet_hold_by_name(const char *link, dls_devnet_t **ddpp)
dls_dev_handle_t ddh;
int err;
- if ((err = dls_mgmt_get_linkid(link, &linkid)) == 0)
+ if ((err = dls_mgmt_get_linkid_in_zone(link, &linkid, zid)) == 0)
return (dls_devnet_hold(linkid, ddpp));
/*
@@ -1270,9 +1444,15 @@ dls_devnet_phydev(datalink_id_t vlanid, dev_t *devp)
*
* This case does not change the <link name, linkid> mapping, so the link's
* kstats need to be updated with using name associated the given id2.
+ *
+ * The zoneinit parameter is used to allow us to create a VNIC in the global
+ * zone which is assigned to a non-global zone. Since there is a race condition
+ * in the create process if two VNICs have the same name, we need to rename it
+ * after it has been assigned to the zone.
*/
int
-dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
+dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link,
+ boolean_t zoneinit)
{
dls_dev_handle_t ddh = NULL;
int err = 0;
@@ -1317,10 +1497,12 @@ dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
}
mutex_enter(&ddp->dd_mutex);
- if (ddp->dd_ref > 1) {
- mutex_exit(&ddp->dd_mutex);
- err = EBUSY;
- goto done;
+ if (!zoneinit) {
+ if (ddp->dd_ref > 1) {
+ mutex_exit(&ddp->dd_mutex);
+ err = EBUSY;
+ goto done;
+ }
}
mutex_exit(&ddp->dd_mutex);
@@ -1331,7 +1513,15 @@ dls_devnet_rename(datalink_id_t id1, datalink_id_t id2, const char *link)
/* rename mac client name and its flow if exists */
if ((err = mac_open(ddp->dd_mac, &mh)) != 0)
goto done;
- (void) mac_rename_primary(mh, link);
+ if (zoneinit) {
+ char tname[MAXLINKNAMELEN];
+
+ (void) snprintf(tname, sizeof (tname), "z%d_%s",
+ ddp->dd_zid, link);
+ (void) mac_rename_primary(mh, tname);
+ } else {
+ (void) mac_rename_primary(mh, link);
+ }
mac_close(mh);
goto done;
}
@@ -1398,7 +1588,7 @@ done:
rw_exit(&i_dls_devnet_lock);
if (err == 0)
- dls_devnet_stat_rename(ddp);
+ dls_devnet_stat_rename(ddp, zoneinit);
if (mph != NULL)
mac_perim_exit(mph);
@@ -1407,7 +1597,8 @@ done:
}
static int
-i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop)
+i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop,
+ boolean_t transient)
{
int err;
mac_perim_handle_t mph;
@@ -1436,10 +1627,18 @@ i_dls_devnet_setzid(dls_devnet_t *ddp, zoneid_t new_zoneid, boolean_t setprop)
sizeof (retval));
if (err != 0)
goto done;
+
+ /*
+ * We set upcall_done only if the upcall is
+ * successful. This way, if dls_link_setzid() fails,
+ * we know another upcall must be done to reset the
+ * dlmgmtd state.
+ */
upcall_done = B_TRUE;
}
if ((err = dls_link_setzid(ddp->dd_mac, new_zoneid)) == 0) {
ddp->dd_zid = new_zoneid;
+ ddp->dd_transient = transient;
devnet_need_rebuild = B_TRUE;
}
@@ -1454,7 +1653,7 @@ done:
}
int
-dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid)
+dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid, boolean_t transient)
{
dls_devnet_t *ddp;
int err;
@@ -1476,7 +1675,7 @@ dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid)
refheld = B_TRUE;
}
- if ((err = i_dls_devnet_setzid(ddh, new_zid, B_TRUE)) != 0) {
+ if ((err = i_dls_devnet_setzid(ddh, new_zid, B_TRUE, transient)) != 0) {
if (refheld)
dls_devnet_rele(ddp);
return (err);
@@ -1493,7 +1692,7 @@ dls_devnet_setzid(dls_dl_handle_t ddh, zoneid_t new_zid)
if (old_zid != GLOBAL_ZONEID)
dls_devnet_stat_destroy(ddh, old_zid);
if (new_zid != GLOBAL_ZONEID)
- dls_devnet_stat_create(ddh, new_zid);
+ dls_devnet_stat_create(ddh, new_zid, new_zid);
return (0);
}
@@ -1531,15 +1730,19 @@ dls_devnet_islinkvisible(datalink_id_t linkid, zoneid_t zoneid)
* Access a vanity naming node.
*/
int
-dls_devnet_open(const char *link, dls_dl_handle_t *dhp, dev_t *devp)
+dls_devnet_open_in_zone(const char *link, dls_dl_handle_t *dhp, dev_t *devp,
+ zoneid_t zid)
{
dls_devnet_t *ddp;
dls_link_t *dlp;
- zoneid_t zid = getzoneid();
+ zoneid_t czid = getzoneid();
int err;
mac_perim_handle_t mph;
- if ((err = dls_devnet_hold_by_name(link, &ddp)) != 0)
+ if (czid != GLOBAL_ZONEID && czid != zid)
+ return (ENOENT);
+
+ if ((err = dls_devnet_hold_by_name(link, &ddp, zid)) != 0)
return (err);
dls_devnet_prop_task_wait(ddp);
@@ -1572,6 +1775,12 @@ dls_devnet_open(const char *link, dls_dl_handle_t *dhp, dev_t *devp)
return (0);
}
+int
+dls_devnet_open(const char *link, dls_dl_handle_t *dhp, dev_t *devp)
+{
+ return (dls_devnet_open_in_zone(link, dhp, devp, getzoneid()));
+}
+
/*
* Close access to a vanity naming node.
*/
@@ -1628,13 +1837,32 @@ dls_devnet_create(mac_handle_t mh, datalink_id_t linkid, zoneid_t zoneid)
* we need to use the linkid to get the user name for the link
* when we create the MAC client.
*/
- if ((err = dls_devnet_set(mac_name(mh), linkid, zoneid, &ddp)) == 0) {
+ if ((err = dls_devnet_set(mh, linkid, zoneid, &ddp)) == 0) {
if ((err = dls_link_hold_create(mac_name(mh), &dlp)) != 0) {
mac_perim_exit(mph);
- (void) dls_devnet_unset(mac_name(mh), &linkid, B_TRUE);
+ (void) dls_devnet_unset(mh, &linkid, B_FALSE);
return (err);
}
+
+ /*
+ * If dd_linkid is set then the link was successfully
+ * initialized. In this case we can remove the
+ * initializing flag and make the link visible to the
+ * rest of the system.
+ *
+ * If not set then we were called by softmac and it
+ * was unable to obtain a linkid for the physical link
+ * because dlmgmtd is down. In that case softmac will
+ * eventually obtain a linkid and call
+ * dls_devnet_recreate() to complete initialization.
+ */
+ mutex_enter(&ddp->dd_mutex);
+ if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
+ ddp->dd_flags &= ~DD_INITIALIZING;
+ mutex_exit(&ddp->dd_mutex);
+
}
+
mac_perim_exit(mph);
return (err);
}
@@ -1648,8 +1876,19 @@ dls_devnet_create(mac_handle_t mh, datalink_id_t linkid, zoneid_t zoneid)
int
dls_devnet_recreate(mac_handle_t mh, datalink_id_t linkid)
{
- ASSERT(linkid != DATALINK_INVALID_LINKID);
- return (dls_devnet_set(mac_name(mh), linkid, GLOBAL_ZONEID, NULL));
+ dls_devnet_t *ddp;
+ int err;
+
+ VERIFY(linkid != DATALINK_INVALID_LINKID);
+ if ((err = dls_devnet_set(mh, linkid, GLOBAL_ZONEID, &ddp)) == 0) {
+ mutex_enter(&ddp->dd_mutex);
+ if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
+ ddp->dd_flags &= ~DD_INITIALIZING;
+ mutex_exit(&ddp->dd_mutex);
+ }
+
+ return (err);
+
}
int
@@ -1659,15 +1898,52 @@ dls_devnet_destroy(mac_handle_t mh, datalink_id_t *idp, boolean_t wait)
mac_perim_handle_t mph;
*idp = DATALINK_INVALID_LINKID;
- err = dls_devnet_unset(mac_name(mh), idp, wait);
- if (err != 0 && err != ENOENT)
+ err = dls_devnet_unset(mh, idp, wait);
+
+ /*
+ * We continue on in the face of ENOENT because the devnet
+ * unset and DLS link release are not atomic and we may have a
+ * scenario where there is no entry in i_dls_devnet_hash for
+ * the MAC name but there is an entry in i_dls_link_hash. For
+ * example, if the following occurred:
+ *
+ * 1. dls_devnet_unset() returns success, and
+ *
+ * 2. dls_link_rele_by_name() fails with ENOTEMPTY because
+ * flows still exist, and
+ *
+ * 3. dls_devnet_set() fails to set the zone id and calls
+ * dls_devnet_unset() -- leaving an entry in
+ * i_dls_link_hash but no corresponding entry in
+ * i_dls_devnet_hash.
+ *
+ * Even if #3 wasn't true the dls_devnet_set() may fail for
+ * different reasons in the future; the point is that it _can_
+ * fail as part of its contract. We can't rely on it working
+ * so we must assume that these two pieces of state (devnet
+ * and link hashes), which should always be in sync, can get
+ * out of sync and thus even if we get ENOENT from the devnet
+ * hash we should still try to delete from the link hash just
+ * in case.
+ *
+ * We could prevent the ENOTEMPTY from dls_link_rele_by_name()
+ * by calling mac_disable() before calling
+ * dls_devnet_destroy() but that's not currently possible due
+ * to a long-standing bug. OpenSolaris 6791335: The semantics
+ * of mac_disable() were modified by Crossbow such that
+ * dls_devnet_destroy() needs to be called before
+ * mac_disable() can succeed. This is because of the implicit
+ * reference that dls has on the mac_impl_t.
+ */
+ if (err != 0 && err != ENOENT) {
return (err);
+ }
mac_perim_enter_by_mh(mh, &mph);
err = dls_link_rele_by_name(mac_name(mh));
- mac_perim_exit(mph);
-
if (err != 0) {
+ dls_devnet_t *ddp;
+
/*
* XXX It is a general GLDv3 bug that dls_devnet_set() has to
* be called to re-set the link when destroy fails. The
@@ -1675,9 +1951,22 @@ dls_devnet_destroy(mac_handle_t mh, datalink_id_t *idp, boolean_t wait)
* called from kernel context or from a zone other than that
* which initially created the link.
*/
- (void) dls_devnet_set(mac_name(mh), *idp, crgetzoneid(CRED()),
- NULL);
+ (void) dls_devnet_set(mh, *idp, crgetzoneid(CRED()), &ddp);
+
+ /*
+ * You might think dd_linkid should always be set
+ * here, but in the case where dls_devnet_unset()
+ * returns ENOENT it will be DATALINK_INVALID_LINKID.
+ * Stay consistent with the rest of DLS and only
+ * remove the initializing flag if linkid is set.
+ */
+ mutex_enter(&ddp->dd_mutex);
+ if (ddp->dd_linkid != DATALINK_INVALID_LINKID)
+ ddp->dd_flags &= ~DD_INITIALIZING;
+ mutex_exit(&ddp->dd_mutex);
}
+
+ mac_perim_exit(mph);
return (err);
}
diff --git a/usr/src/uts/common/io/dls/dls_stat.c b/usr/src/uts/common/io/dls/dls_stat.c
index 51e4be7260..82dceff278 100644
--- a/usr/src/uts/common/io/dls/dls_stat.c
+++ b/usr/src/uts/common/io/dls/dls_stat.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.
*/
/*
@@ -30,30 +31,33 @@
#include <sys/dld_impl.h>
#include <sys/mac_ether.h>
-static mac_stat_info_t i_dls_si[] = {
- { MAC_STAT_IFSPEED, "ifspeed", KSTAT_DATA_UINT64, 0 },
- { MAC_STAT_MULTIRCV, "multircv", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_BRDCSTRCV, "brdcstrcv", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_MULTIXMT, "multixmt", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_BRDCSTXMT, "brdcstxmt", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_NORCVBUF, "norcvbuf", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_IERRORS, "ierrors", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_NOXMTBUF, "noxmtbuf", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_OERRORS, "oerrors", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_COLLISIONS, "collisions", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_RBYTES, "rbytes", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_IPACKETS, "ipackets", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_OBYTES, "obytes", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_OPACKETS, "opackets", KSTAT_DATA_UINT32, 0 },
- { MAC_STAT_RBYTES, "rbytes64", KSTAT_DATA_UINT64, 0 },
- { MAC_STAT_IPACKETS, "ipackets64", KSTAT_DATA_UINT64, 0 },
- { MAC_STAT_OBYTES, "obytes64", KSTAT_DATA_UINT64, 0 },
- { MAC_STAT_OPACKETS, "opackets64", KSTAT_DATA_UINT64, 0 },
- { MAC_STAT_LINK_STATE, "link_state", KSTAT_DATA_UINT32,
- (uint64_t)LINK_STATE_UNKNOWN}
-};
-
-#define STAT_INFO_COUNT (sizeof (i_dls_si) / sizeof (i_dls_si[0]))
+/*
+ * structure for link kstats
+ */
+typedef struct {
+ kstat_named_t dk_ifspeed;
+ kstat_named_t dk_multircv;
+ kstat_named_t dk_brdcstrcv;
+ kstat_named_t dk_multixmt;
+ kstat_named_t dk_brdcstxmt;
+ kstat_named_t dk_norcvbuf;
+ kstat_named_t dk_ierrors;
+ kstat_named_t dk_noxmtbuf;
+ kstat_named_t dk_oerrors;
+ kstat_named_t dk_collisions;
+ kstat_named_t dk_rbytes;
+ kstat_named_t dk_ipackets;
+ kstat_named_t dk_obytes;
+ kstat_named_t dk_opackets;
+ kstat_named_t dk_rbytes64;
+ kstat_named_t dk_ipackets64;
+ kstat_named_t dk_obytes64;
+ kstat_named_t dk_opackets64;
+ kstat_named_t dk_link_state;
+ kstat_named_t dk_link_duplex;
+ kstat_named_t dk_unknowns;
+ kstat_named_t dk_zonename;
+} dls_kstat_t;
/*
* Exported functions.
@@ -61,42 +65,54 @@ static mac_stat_info_t i_dls_si[] = {
int
dls_stat_update(kstat_t *ksp, dls_link_t *dlp, int rw)
{
- kstat_named_t *knp;
- uint_t i;
- uint64_t val;
+ dls_kstat_t *dkp = ksp->ks_data;
if (rw != KSTAT_READ)
return (EACCES);
- knp = (kstat_named_t *)ksp->ks_data;
- for (i = 0; i < STAT_INFO_COUNT; i++) {
- val = mac_stat_get(dlp->dl_mh, i_dls_si[i].msi_stat);
-
- switch (i_dls_si[i].msi_type) {
- case KSTAT_DATA_UINT64:
- knp->value.ui64 = val;
- break;
- case KSTAT_DATA_UINT32:
- knp->value.ui32 = (uint32_t)val;
- break;
- default:
- ASSERT(B_FALSE);
- }
-
- knp++;
- }
+ dkp->dk_ifspeed.value.ui64 = mac_stat_get(dlp->dl_mh, MAC_STAT_IFSPEED);
+ dkp->dk_multircv.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_MULTIRCV);
+ dkp->dk_brdcstrcv.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_BRDCSTRCV);
+ dkp->dk_multixmt.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_MULTIXMT);
+ dkp->dk_brdcstxmt.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_BRDCSTXMT);
+ dkp->dk_norcvbuf.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_NORCVBUF);
+ dkp->dk_ierrors.value.ui32 = mac_stat_get(dlp->dl_mh, MAC_STAT_IERRORS);
+ dkp->dk_noxmtbuf.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_NOXMTBUF);
+ dkp->dk_oerrors.value.ui32 = mac_stat_get(dlp->dl_mh, MAC_STAT_OERRORS);
+ dkp->dk_collisions.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_COLLISIONS);
+ dkp->dk_rbytes.value.ui32 = mac_stat_get(dlp->dl_mh, MAC_STAT_RBYTES);
+ dkp->dk_ipackets.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_IPACKETS);
+ dkp->dk_obytes.value.ui32 = mac_stat_get(dlp->dl_mh, MAC_STAT_OBYTES);
+ dkp->dk_opackets.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_OPACKETS);
+ dkp->dk_rbytes64.value.ui64 = mac_stat_get(dlp->dl_mh, MAC_STAT_RBYTES);
+ dkp->dk_ipackets64.value.ui64 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_IPACKETS);
+ dkp->dk_obytes64.value.ui64 = mac_stat_get(dlp->dl_mh, MAC_STAT_OBYTES);
+ dkp->dk_opackets64.value.ui64 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_OPACKETS);
+ dkp->dk_link_state.value.ui32 = mac_stat_get(dlp->dl_mh,
+ MAC_STAT_LINK_STATE);
/*
* Ethernet specific kstat "link_duplex"
*/
if (dlp->dl_mip->mi_nativemedia != DL_ETHER) {
- knp->value.ui32 = LINK_DUPLEX_UNKNOWN;
+ dkp->dk_link_duplex.value.ui32 = LINK_DUPLEX_UNKNOWN;
} else {
- val = mac_stat_get(dlp->dl_mh, ETHER_STAT_LINK_DUPLEX);
- knp->value.ui32 = (uint32_t)val;
+ dkp->dk_link_duplex.value.ui32 =
+ (uint32_t)mac_stat_get(dlp->dl_mh, ETHER_STAT_LINK_DUPLEX);
}
- knp++;
- knp->value.ui32 = dlp->dl_unknowns;
+
+ dkp->dk_unknowns.value.ui32 = dlp->dl_unknowns;
return (0);
}
@@ -104,30 +120,66 @@ dls_stat_update(kstat_t *ksp, dls_link_t *dlp, int rw)
int
dls_stat_create(const char *module, int instance, const char *name,
zoneid_t zoneid, int (*update)(struct kstat *, int), void *private,
- kstat_t **kspp)
+ kstat_t **kspp, zoneid_t newzoneid)
{
kstat_t *ksp;
- kstat_named_t *knp;
- uint_t i;
+ zone_t *zone;
+ dls_kstat_t *dkp;
if ((ksp = kstat_create_zone(module, instance, name, "net",
- KSTAT_TYPE_NAMED, STAT_INFO_COUNT + 2, 0, zoneid)) == NULL) {
+ KSTAT_TYPE_NAMED, sizeof (dls_kstat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL, zoneid)) == NULL) {
return (EINVAL);
}
ksp->ks_update = update;
ksp->ks_private = private;
+ dkp = ksp->ks_data = kmem_zalloc(sizeof (dls_kstat_t), KM_SLEEP);
+ if ((zone = zone_find_by_id(newzoneid)) != NULL) {
+ ksp->ks_data_size += strlen(zone->zone_name) + 1;
+ }
- knp = (kstat_named_t *)ksp->ks_data;
- for (i = 0; i < STAT_INFO_COUNT; i++) {
- kstat_named_init(knp, i_dls_si[i].msi_name,
- i_dls_si[i].msi_type);
- knp++;
+ kstat_named_init(&dkp->dk_ifspeed, "ifspeed", KSTAT_DATA_UINT64);
+ kstat_named_init(&dkp->dk_multircv, "multircv", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_brdcstrcv, "brdcstrcv", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_multixmt, "multixmt", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_brdcstxmt, "brdcstxmt", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_norcvbuf, "norcvbuf", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_ierrors, "ierrors", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_noxmtbuf, "noxmtbuf", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_oerrors, "oerrors", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_collisions, "collisions", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_rbytes, "rbytes", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_ipackets, "ipackets", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_obytes, "obytes", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_opackets, "opackets", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_rbytes64, "rbytes64", KSTAT_DATA_UINT64);
+ kstat_named_init(&dkp->dk_ipackets64, "ipackets64", KSTAT_DATA_UINT64);
+ kstat_named_init(&dkp->dk_obytes64, "obytes64", KSTAT_DATA_UINT64);
+ kstat_named_init(&dkp->dk_opackets64, "opackets64", KSTAT_DATA_UINT64);
+ kstat_named_init(&dkp->dk_link_state, "link_state", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_link_duplex, "link_duplex",
+ KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_unknowns, "unknowns", KSTAT_DATA_UINT32);
+ kstat_named_init(&dkp->dk_zonename, "zonename", KSTAT_DATA_STRING);
+
+ if (zone != NULL) {
+ kstat_named_setstr(&dkp->dk_zonename, zone->zone_name);
+ zone_rele(zone);
}
- kstat_named_init(knp++, "link_duplex", KSTAT_DATA_UINT32);
- kstat_named_init(knp, "unknowns", KSTAT_DATA_UINT32);
kstat_install(ksp);
*kspp = ksp;
return (0);
}
+
+void
+dls_stat_delete(kstat_t *ksp)
+{
+ void *data;
+ if (ksp != NULL) {
+ data = ksp->ks_data;
+ kstat_delete(ksp);
+ kmem_free(data, sizeof (dls_kstat_t));
+ }
+}
diff --git a/usr/src/uts/common/io/dump.c b/usr/src/uts/common/io/dump.c
index 4fd52e6448..f4d8c1cf2c 100644
--- a/usr/src/uts/common/io/dump.c
+++ b/usr/src/uts/common/io/dump.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
* Delphix (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
@@ -46,6 +47,7 @@
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
+#include <sys/random.h>
static dev_info_t *dump_devi;
@@ -141,16 +143,20 @@ dump_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
*rvalp = dump_conflags;
if (dumpvp && !(dumpvp->v_flag & VISSWAP))
*rvalp |= DUMP_EXCL;
+
mutex_exit(&dump_lock);
break;
case DIOCSETCONF:
mutex_enter(&dump_lock);
if (arg == DUMP_KERNEL || arg == DUMP_ALL ||
- arg == DUMP_CURPROC)
- dump_conflags = arg;
- else
+ arg == DUMP_CURPROC) {
+ dump_conflags = (dump_conflags & DUMP_STATE) |
+ (arg & DUMP_CONTENT);
+ } else {
error = EINVAL;
+ }
+
mutex_exit(&dump_lock);
break;
@@ -181,6 +187,24 @@ dump_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
VN_RELE(vp);
break;
+ case DIOCSCRYPTKEY: {
+ uint8_t key[DUMP_CRYPT_KEYLEN];
+ uint8_t nonce[DUMP_CRYPT_NONCELEN];
+
+ if ((error = copyin((uint8_t *)arg, key, sizeof (key))) != 0)
+ break;
+
+ (void) random_get_pseudo_bytes(nonce, sizeof (nonce));
+
+ mutex_enter(&dump_lock);
+ bcopy(key, dump_crypt_key, DUMP_CRYPT_KEYLEN);
+ bcopy(nonce, dump_crypt_nonce, DUMP_CRYPT_NONCELEN);
+ dump_conflags |= DUMP_ENCRYPT; /* a one-way trip */
+ mutex_exit(&dump_lock);
+
+ break;
+ }
+
case DIOCDUMP:
mutex_enter(&dump_lock);
if (dumpvp == NULL)
diff --git a/usr/src/uts/common/io/eventfd.c b/usr/src/uts/common/io/eventfd.c
index 32f875917f..efc1f9233f 100644
--- a/usr/src/uts/common/io/eventfd.c
+++ b/usr/src/uts/common/io/eventfd.c
@@ -141,37 +141,39 @@ eventfd_read(dev_t dev, uio_t *uio, cred_t *cr)
* transitions from EVENTFD_VALMAX to a lower value. At all other
* times, it is already considered writable by poll.
*/
- if (oval == EVENTFD_VALMAX) {
+ if (oval >= EVENTFD_VALMAX) {
pollwakeup(&state->efd_pollhd, POLLWRNORM | POLLOUT);
}
return (err);
}
-/*ARGSUSED*/
static int
-eventfd_write(dev_t dev, struct uio *uio, cred_t *credp)
+eventfd_post(eventfd_state_t *state, uint64_t val, boolean_t is_async,
+ boolean_t file_nonblock)
{
- eventfd_state_t *state;
- minor_t minor = getminor(dev);
- uint64_t val, oval;
- int err;
-
- if (uio->uio_resid < sizeof (val))
- return (EINVAL);
-
- if ((err = uiomove(&val, sizeof (val), UIO_WRITE, uio)) != 0)
- return (err);
-
- if (val > EVENTFD_VALMAX)
- return (EINVAL);
-
- state = ddi_get_soft_state(eventfd_softstate, minor);
+ uint64_t oval;
+ boolean_t overflow = B_FALSE;
mutex_enter(&state->efd_lock);
while (val > EVENTFD_VALMAX - state->efd_value) {
- if (uio->uio_fmode & (FNDELAY|FNONBLOCK)) {
+
+ /*
+ * When called from (LX) AIO, expectations about overflow and
+ * blocking are different than normal operation. If the
+ * incoming value would cause overflow, it is clamped to reach
+ * the overflow value exactly. This is added to the existing
+ * value without blocking. Any pollers of the eventfd will see
+ * POLLERR asserted when this occurs.
+ */
+ if (is_async) {
+ val = EVENTFD_VALOVERFLOW - state->efd_value;
+ overflow = B_TRUE;
+ break;
+ }
+
+ if (file_nonblock) {
mutex_exit(&state->efd_lock);
return (EAGAIN);
}
@@ -186,7 +188,7 @@ eventfd_write(dev_t dev, struct uio *uio, cred_t *credp)
}
/*
- * We now know that we can add the value without overflowing.
+ * We now know that we can safely add the value.
*/
state->efd_value = (oval = state->efd_value) + val;
@@ -200,10 +202,13 @@ eventfd_write(dev_t dev, struct uio *uio, cred_t *credp)
mutex_exit(&state->efd_lock);
/*
- * Notify pollers as well if the eventfd is now readable.
+ * Notify pollers as well if the eventfd has become readable or has
+ * transitioned into overflow.
*/
if (oval == 0) {
pollwakeup(&state->efd_pollhd, POLLRDNORM | POLLIN);
+ } else if (overflow && val != 0) {
+ pollwakeup(&state->efd_pollhd, POLLERR);
}
return (0);
@@ -211,6 +216,29 @@ eventfd_write(dev_t dev, struct uio *uio, cred_t *credp)
/*ARGSUSED*/
static int
+eventfd_write(dev_t dev, struct uio *uio, cred_t *credp)
+{
+ eventfd_state_t *state;
+ boolean_t file_nonblock;
+ uint64_t val;
+ int err;
+
+ if (uio->uio_resid < sizeof (val))
+ return (EINVAL);
+
+ if ((err = uiomove(&val, sizeof (val), UIO_WRITE, uio)) != 0)
+ return (err);
+
+ if (val > EVENTFD_VALMAX)
+ return (EINVAL);
+
+ file_nonblock = (uio->uio_fmode & (FNDELAY|FNONBLOCK)) != 0;
+ state = ddi_get_soft_state(eventfd_softstate, getminor(dev));
+ return (eventfd_post(state, val, B_FALSE, file_nonblock));
+}
+
+/*ARGSUSED*/
+static int
eventfd_poll(dev_t dev, short events, int anyyet, short *reventsp,
struct pollhead **phpp)
{
@@ -228,6 +256,9 @@ eventfd_poll(dev_t dev, short events, int anyyet, short *reventsp,
if (state->efd_value < EVENTFD_VALMAX)
revents |= POLLWRNORM | POLLOUT;
+ if (state->efd_value == EVENTFD_VALOVERFLOW)
+ revents |= POLLERR;
+
*reventsp = revents & events;
if ((*reventsp == 0 && !anyyet) || (events & POLLET)) {
*phpp = &state->efd_pollhd;
@@ -244,17 +275,28 @@ eventfd_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
{
eventfd_state_t *state;
minor_t minor = getminor(dev);
+ uint64_t *valp;
state = ddi_get_soft_state(eventfd_softstate, minor);
switch (cmd) {
- case EVENTFDIOC_SEMAPHORE: {
+ case EVENTFDIOC_SEMAPHORE:
mutex_enter(&state->efd_lock);
state->efd_semaphore ^= 1;
mutex_exit(&state->efd_lock);
+ return (0);
+ case EVENTFDIOC_POST:
+ /*
+ * This ioctl is expected to be kernel-internal, used only by
+ * the AIO emulation in LX.
+ */
+ if ((md & FKIOCTL) == 0) {
+ break;
+ }
+ valp = (uint64_t *)arg;
+ VERIFY(eventfd_post(state, *valp, B_TRUE, B_FALSE) == 0);
return (0);
- }
default:
break;
diff --git a/usr/src/uts/common/io/gsqueue/gsqueue.c b/usr/src/uts/common/io/gsqueue/gsqueue.c
new file mode 100644
index 0000000000..03bb799499
--- /dev/null
+++ b/usr/src/uts/common/io/gsqueue/gsqueue.c
@@ -0,0 +1,608 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+/*
+ * Serialization queues are a technique used in illumos to provide what's
+ * commonly known as a 'vertical' perimeter. The idea (described a bit in
+ * uts/common/inet/squeue.c) is to provide a means to make sure that message
+ * blocks (mblk_t) are processed in a specific order. Subsystems like ip and vnd
+ * consume these on different policies, ip on a conn_t basis, vnd on a per
+ * device basis, and use this to ensure that only one packet is being processed
+ * at a given time.
+ *
+ * Serialization queues were originally used by ip. As part of that
+ * implementation, many of the details of ip were baked into it. That includes
+ * things like conn_t, ip receive attributes, and the notion of sets. While an
+ * individual serialization queue, or gsqueue_t, is a useful level of
+ * abstraction, it isn't the basis on which monst consumers want to manage them.
+ * Instead, we have the notion of a set of serialization queues. These sets are
+ * DR (CPU Dynamic reconfiguration) aware, and allow consumers to have a
+ * gsqueue_t per CPU to fanout on without managing them all itself. In the
+ * original implementation, this existed, but they were heavily tied into the
+ * infrastructure of IP, and its notion of polling on the underlying MAC
+ * devices.
+ *
+ * The result of that past is a new interface to serialization queues and a
+ * similar, but slightly different, abstraction to sets of these
+ * (gsqueue_set_t). When designing this there are two different approaches that
+ * one could consider. The first is that the system has one gsqueue_set_t that
+ * the entire world shares, whether IP or some other consumer. The other is that
+ * every consumer has their own set.
+ *
+ * The trade offs between these two failure modes are the pathological failure
+ * modes. There is no guarantee that any two consumers here are equivalent. In
+ * fact, they very likely have very different latency profiles. If they are
+ * being processed in the same queue, that can lead to very odd behaviors. More
+ * generally, if we have a series of processing functions from one consumer
+ * which are generally short, and another which are generally long, that'll
+ * cause undue latency that's harder to observe. If we instead take the approach
+ * that each consumer should have its own set that it fans out over then we
+ * won't end up with the problem that a given serialization queue will have
+ * multiple latency profiles, but instead we'll see cpu contention for the bound
+ * gsqueue_t worker thread. Keep in mind though, that only the gsqueue_t worker
+ * thread is bound and it is in fact possible for it to be processed by other
+ * threads on other CPUs.
+ *
+ * We've opted to go down the second path, so each consumer has its own
+ * independent set of serialization queues that it is bound over.
+ *
+ * Structure Hierarchies
+ * ---------------------
+ *
+ * At the top level, we have a single list of gsqueue_set_t. The gsqueue_set_t
+ * encapsulates all the per-CPU gsqueue_t that exist in the form of
+ * gsqueue_cpu_t. The gsqueue_cpu_t has been designed such that it could
+ * accommodate more than one gsqueue_t, but today there is a one to one mapping.
+ *
+ * We maintain two different lists of gsqueue_cpu_t, the active and defunct
+ * sets. The active set is maintained in the array `gs_cpus`. There are NCPU
+ * entries available in `gs_cpus` with the total number of currently active cpus
+ * described in `gs_ncpus`. The ordering of `gs_cpus` is unimportant. When
+ * there is no longer a need for a given binding (see the following section for
+ * more explanation on when this is the case) then we move the entry to the
+ * `gs_defunct` list which is just a list_t of gsqueue_cpu_t.
+ *
+ * In addition, each gsqueue_set_t can have a series of callbacks registered
+ * with it. These are described in the following section. Graphically, a given
+ * gsqueue_set_t looks roughly like the following:
+ *
+ * +---------------+
+ * | gsqueue_set_t |
+ * +---------------+
+ * | | |
+ * | | * . . . gs_cpus
+ * | | |
+ * | | | +-------------------------------------------------+
+ * | | +--->| gsqueue_cpu_t || gsqueue_cpu_t || gsqueue_cpu_t |...
+ * | | +-------------------------------------------------+
+ * | |
+ * | * . . . gs_defunct
+ * | |
+ * | | +---------------+ +---------------+ +---------------+
+ * | +--->| gsqueue_cpu_t |-->| gsqueue_cpu_t |-->| gsqueue_cpu_t |...
+ * | +---------------+ +---------------+ +---------------+
+ * * . . . gs_cbs
+ * |
+ * | +--------------+ +--------------+ +--------------+
+ * +--->| gsqueue_cb_t |-->| gsqueue_cb_t |->| gsqueue_cb_t |...
+ * +--------------+ +--------------+ +--------------+
+ *
+ * CPU DR, gsqueue_t, and gsqueue_t
+ * --------------------------------
+ *
+ * Recall, that every serialization queue (gsqueue_t or squeue_t) has a worker
+ * thread that may end up doing work. As part of supporting fanout, we have one
+ * gsqueue_t per CPU, and its worker thread is bound to that CPU. Because of
+ * this binding, we need to deal with CPU DR changes.
+ *
+ * The gsqueue driver maintains a single CPU DR callback that is used for the
+ * entire sub-system. We break down CPU DR events into three groups. Offline
+ * events, online events, and events we can ignore. When the first group occurs,
+ * we need to go through every gsqueue_t, find the gsqueue_cpu_t that
+ * corresponds to that processor id, and unbind all of its gsqueue_t's. It's
+ * rather important that we only unbind the gsqueue_t's and not actually destroy
+ * them. When this happens, they could very easily have data queued inside of
+ * them and it's unreasonable to just throw out everything in them at this
+ * point. The data remains intact and service continues uinterrupted.
+ *
+ * When we receive an online event, we do the opposite. We try to find a
+ * gsqueue_cpu_t that previously was bound to this CPU (by leaving its gqc_cpuid
+ * field intact) in the defunct list. If we find one, we remove it from the
+ * defunct list and add it to the active list as well as binding the gsqueue_t
+ * to the CPU in question. If we don't find one, then we create a new one.
+ *
+ * To deal with these kinds of situations, we allow a consumer to register
+ * callbacks for the gsqueue_t that they are interested in. These callbacks will
+ * fire whenever we are handling a topology change. The design of the callbacks
+ * is not that the user can take any administrative action during them, but
+ * rather set something for them to do asynchronously. It is illegal to make any
+ * calls into the gsqueue system while you are in a callback.
+ *
+ * Locking
+ * -------
+ *
+ * The lock ordering here is fairly straightforward. Due to our use of CPU
+ * binding and the CPU DR callbacks, we have an additional lock to consider
+ * cpu_lock. Because of that, the following are the rules for locking:
+ *
+ *
+ * o If performing binding operations, you must grab cpu_lock. cpu_lock is
+ * also at the top of the order.
+ *
+ * o cpu_lock > gsqueue_lock > gsqueue_t`gs_lock > squeue_t`sq_lock
+ * If you need to take multiple locks, you must take the greatest
+ * (left-most) one first.
+ */
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/kmem.h>
+#include <sys/stream.h>
+#include <sys/modctl.h>
+#include <sys/cpuvar.h>
+#include <sys/list.h>
+#include <sys/sysmacros.h>
+
+#include <sys/gsqueue.h>
+#include <sys/squeue_impl.h>
+
+typedef struct gsqueue_cb {
+ struct gsqueue_cb *gcb_next;
+ gsqueue_cb_f gcb_func;
+ void *gcb_arg;
+} gsqueue_cb_t;
+
+typedef struct gsqueue_cpu {
+ list_node_t gqc_lnode;
+ squeue_t *gqc_head;
+ processorid_t gqc_cpuid;
+} gsqueue_cpu_t;
+
+struct gsqueue_set {
+ list_node_t gs_next;
+ pri_t gs_wpri;
+ kmutex_t gs_lock;
+ int gs_ncpus;
+ gsqueue_cpu_t **gs_cpus;
+ list_t gs_defunct;
+ gsqueue_cb_t *gs_cbs;
+};
+
+static kmutex_t gsqueue_lock;
+static list_t gsqueue_list;
+static kmem_cache_t *gsqueue_cb_cache;
+static kmem_cache_t *gsqueue_cpu_cache;
+static kmem_cache_t *gsqueue_set_cache;
+
+static gsqueue_cpu_t *
+gsqueue_cpu_create(pri_t wpri, processorid_t cpuid)
+{
+ gsqueue_cpu_t *scp;
+
+ scp = kmem_cache_alloc(gsqueue_cpu_cache, KM_SLEEP);
+
+ list_link_init(&scp->gqc_lnode);
+ scp->gqc_cpuid = cpuid;
+ scp->gqc_head = squeue_create(wpri, B_FALSE);
+ scp->gqc_head->sq_state = SQS_DEFAULT;
+ squeue_bind(scp->gqc_head, cpuid);
+
+ return (scp);
+}
+
+static void
+gsqueue_cpu_destroy(gsqueue_cpu_t *scp)
+{
+ squeue_destroy(scp->gqc_head);
+ kmem_cache_free(gsqueue_cpu_cache, scp);
+}
+
+gsqueue_set_t *
+gsqueue_set_create(pri_t wpri)
+{
+ int i;
+ gsqueue_set_t *gssp;
+
+ gssp = kmem_cache_alloc(gsqueue_set_cache, KM_SLEEP);
+ gssp->gs_wpri = wpri;
+ gssp->gs_ncpus = 0;
+
+ /*
+ * We're grabbing CPU lock. Once we let go of it we have to ensure all
+ * set up of the gsqueue_set_t is complete, as it'll be in there for the
+ * various CPU DR bits.
+ */
+ mutex_enter(&cpu_lock);
+
+ for (i = 0; i < NCPU; i++) {
+ gsqueue_cpu_t *scp;
+ cpu_t *cp = cpu_get(i);
+ if (cp != NULL && CPU_ACTIVE(cp) &&
+ cp->cpu_flags & CPU_EXISTS) {
+ scp = gsqueue_cpu_create(wpri, cp->cpu_id);
+ gssp->gs_cpus[gssp->gs_ncpus] = scp;
+ gssp->gs_ncpus++;
+ }
+ }
+
+ /* Finally we can add it to our global list and be done */
+ mutex_enter(&gsqueue_lock);
+ list_insert_tail(&gsqueue_list, gssp);
+ mutex_exit(&gsqueue_lock);
+ mutex_exit(&cpu_lock);
+
+ return (gssp);
+}
+
+void
+gsqueue_set_destroy(gsqueue_set_t *gssp)
+{
+ int i;
+ gsqueue_cpu_t *scp;
+
+ /*
+ * Go through and unbind all of the squeues while cpu_lock is held and
+ * move them to the defunct list. Once that's done, we don't need to do
+ * anything else with cpu_lock.
+ */
+ mutex_enter(&cpu_lock);
+ mutex_enter(&gsqueue_lock);
+ list_remove(&gsqueue_list, gssp);
+ mutex_exit(&gsqueue_lock);
+
+ mutex_enter(&gssp->gs_lock);
+
+ for (i = 0; i < gssp->gs_ncpus; i++) {
+ scp = gssp->gs_cpus[i];
+ squeue_unbind(scp->gqc_head);
+ list_insert_tail(&gssp->gs_defunct, scp);
+ gssp->gs_cpus[i] = NULL;
+ }
+ gssp->gs_ncpus = 0;
+
+ mutex_exit(&gssp->gs_lock);
+ mutex_exit(&cpu_lock);
+
+ while ((scp = list_remove_head(&gssp->gs_defunct)) != NULL) {
+ gsqueue_cpu_destroy(scp);
+ }
+
+ while (gssp->gs_cbs != NULL) {
+ gsqueue_cb_t *cbp;
+
+ cbp = gssp->gs_cbs;
+ gssp->gs_cbs = cbp->gcb_next;
+ kmem_cache_free(gsqueue_cb_cache, cbp);
+ }
+
+ ASSERT3U(gssp->gs_ncpus, ==, 0);
+ ASSERT3P(list_head(&gssp->gs_defunct), ==, NULL);
+ ASSERT3P(gssp->gs_cbs, ==, NULL);
+ kmem_cache_free(gsqueue_set_cache, gssp);
+}
+
+gsqueue_t *
+gsqueue_set_get(gsqueue_set_t *gssp, uint_t index)
+{
+ squeue_t *sqp;
+ gsqueue_cpu_t *scp;
+
+ mutex_enter(&gssp->gs_lock);
+ scp = gssp->gs_cpus[index % gssp->gs_ncpus];
+ sqp = scp->gqc_head;
+ mutex_exit(&gssp->gs_lock);
+ return ((gsqueue_t *)sqp);
+}
+
+uintptr_t
+gsqueue_set_cb_add(gsqueue_set_t *gssp, gsqueue_cb_f cb, void *arg)
+{
+ gsqueue_cb_t *cbp;
+
+ cbp = kmem_cache_alloc(gsqueue_cb_cache, KM_SLEEP);
+ cbp->gcb_func = cb;
+ cbp->gcb_arg = arg;
+
+ mutex_enter(&gssp->gs_lock);
+ cbp->gcb_next = gssp->gs_cbs;
+ gssp->gs_cbs = cbp;
+ mutex_exit(&gssp->gs_lock);
+ return ((uintptr_t)cbp);
+}
+
+int
+gsqueue_set_cb_remove(gsqueue_set_t *gssp, uintptr_t id)
+{
+ gsqueue_cb_t *cbp, *prev;
+ mutex_enter(&gssp->gs_lock);
+ cbp = gssp->gs_cbs;
+ prev = NULL;
+ while (cbp != NULL) {
+ if ((uintptr_t)cbp != id) {
+ prev = cbp;
+ cbp = cbp->gcb_next;
+ continue;
+ }
+
+ if (prev == NULL) {
+ gssp->gs_cbs = cbp->gcb_next;
+ } else {
+ prev->gcb_next = cbp->gcb_next;
+ }
+
+ mutex_exit(&gssp->gs_lock);
+ kmem_cache_free(gsqueue_cb_cache, cbp);
+ return (0);
+ }
+ mutex_exit(&gssp->gs_lock);
+ return (-1);
+}
+
+void
+gsqueue_enter_one(gsqueue_t *gsp, mblk_t *mp, gsqueue_proc_f func, void *arg,
+ int flags, uint8_t tag)
+{
+ squeue_t *sqp = (squeue_t *)gsp;
+
+ ASSERT(mp->b_next == NULL);
+ ASSERT(mp->b_prev == NULL);
+ mp->b_queue = (queue_t *)func;
+ mp->b_prev = arg;
+ sqp->sq_enter(sqp, mp, mp, 1, NULL, flags, tag);
+}
+
+static void
+gsqueue_notify(gsqueue_set_t *gssp, squeue_t *sqp, boolean_t online)
+{
+ gsqueue_cb_t *cbp;
+
+ ASSERT(MUTEX_HELD(&gssp->gs_lock));
+ cbp = gssp->gs_cbs;
+ while (cbp != NULL) {
+ cbp->gcb_func(gssp, (gsqueue_t *)sqp, cbp->gcb_arg, online);
+ cbp = cbp->gcb_next;
+ }
+
+}
+
+/*
+ * When we online a processor we need to go through and either bind a defunct
+ * squeue or create a new one. We'll try to reuse a gsqueue_cpu_t from the
+ * defunct list that used to be on that processor. If no such gsqueue_cpu_t
+ * exists, then we'll create a new one. We'd rather avoid taking over an
+ * existing defunct one that used to be on another CPU, as its not unreasonable
+ * to believe that its CPU will come back. More CPUs are offlined and onlined by
+ * the administrator or by creating cpu sets than actually get offlined by FMA.
+ */
+static void
+gsqueue_handle_online(processorid_t id)
+{
+ gsqueue_set_t *gssp;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ mutex_enter(&gsqueue_lock);
+ for (gssp = list_head(&gsqueue_list); gssp != NULL;
+ gssp = list_next(&gsqueue_list, gssp)) {
+ gsqueue_cpu_t *scp;
+
+ mutex_enter(&gssp->gs_lock);
+ for (scp = list_head(&gssp->gs_defunct); scp != NULL;
+ scp = list_next(&gssp->gs_defunct, scp)) {
+ if (scp->gqc_cpuid == id) {
+ list_remove(&gssp->gs_defunct, scp);
+ break;
+ }
+ }
+
+ if (scp == NULL) {
+ scp = gsqueue_cpu_create(gssp->gs_wpri, id);
+ } else {
+ squeue_bind(scp->gqc_head, id);
+ }
+
+ ASSERT(gssp->gs_ncpus < NCPU);
+ gssp->gs_cpus[gssp->gs_ncpus] = scp;
+ gssp->gs_ncpus++;
+ gsqueue_notify(gssp, scp->gqc_head, B_TRUE);
+ mutex_exit(&gssp->gs_lock);
+ }
+ mutex_exit(&gsqueue_lock);
+}
+
+static void
+gsqueue_handle_offline(processorid_t id)
+{
+ gsqueue_set_t *gssp;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ mutex_enter(&gsqueue_lock);
+ for (gssp = list_head(&gsqueue_list); gssp != NULL;
+ gssp = list_next(&gsqueue_list, gssp)) {
+ int i;
+ gsqueue_cpu_t *scp = NULL;
+
+ mutex_enter(&gssp->gs_lock);
+ for (i = 0; i < gssp->gs_ncpus; i++) {
+ if (gssp->gs_cpus[i]->gqc_cpuid == id) {
+ scp = gssp->gs_cpus[i];
+ break;
+ }
+ }
+
+ if (scp != NULL) {
+ squeue_unbind(scp->gqc_head);
+ list_insert_tail(&gssp->gs_defunct, scp);
+ gssp->gs_cpus[i] = gssp->gs_cpus[gssp->gs_ncpus-1];
+ gssp->gs_ncpus--;
+ gsqueue_notify(gssp, scp->gqc_head, B_FALSE);
+ }
+ mutex_exit(&gssp->gs_lock);
+ }
+ mutex_exit(&gsqueue_lock);
+}
+
+/* ARGSUSED */
+static int
+gsqueue_cpu_setup(cpu_setup_t what, int id, void *unused)
+{
+ cpu_t *cp;
+
+ ASSERT(MUTEX_HELD(&cpu_lock));
+ cp = cpu_get(id);
+ switch (what) {
+ case CPU_CONFIG:
+ case CPU_ON:
+ case CPU_INIT:
+ case CPU_CPUPART_IN:
+ if (cp != NULL && CPU_ACTIVE(cp) && cp->cpu_flags & CPU_EXISTS)
+ gsqueue_handle_online(cp->cpu_id);
+ break;
+ case CPU_UNCONFIG:
+ case CPU_OFF:
+ case CPU_CPUPART_OUT:
+ gsqueue_handle_offline(cp->cpu_id);
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+
+/* ARGSUSED */
+static int
+gsqueue_set_cache_construct(void *buf, void *arg, int kmflags)
+{
+ gsqueue_set_t *gssp = buf;
+
+ gssp->gs_cpus = kmem_alloc(sizeof (gsqueue_cpu_t *) * NCPU, kmflags);
+ if (gssp->gs_cpus == NULL)
+ return (-1);
+
+ mutex_init(&gssp->gs_lock, NULL, MUTEX_DRIVER, NULL);
+ list_create(&gssp->gs_defunct, sizeof (gsqueue_cpu_t),
+ offsetof(gsqueue_cpu_t, gqc_lnode));
+ gssp->gs_ncpus = 0;
+ gssp->gs_cbs = NULL;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+gsqueue_set_cache_destruct(void *buf, void *arg)
+{
+ gsqueue_set_t *gssp = buf;
+
+ kmem_free(gssp->gs_cpus, sizeof (gsqueue_cpu_t *) * NCPU);
+ gssp->gs_cpus = NULL;
+ list_destroy(&gssp->gs_defunct);
+ mutex_destroy(&gssp->gs_lock);
+}
+
+static void
+gsqueue_ddiinit(void)
+{
+ list_create(&gsqueue_list, sizeof (gsqueue_set_t),
+ offsetof(gsqueue_set_t, gs_next));
+ mutex_init(&gsqueue_lock, NULL, MUTEX_DRIVER, NULL);
+
+ gsqueue_cb_cache = kmem_cache_create("gsqueue_cb_cache",
+ sizeof (gsqueue_cb_t),
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ gsqueue_cpu_cache = kmem_cache_create("gsqueue_cpu_cache",
+ sizeof (gsqueue_cpu_t),
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ gsqueue_set_cache = kmem_cache_create("squeue_set_cache",
+ sizeof (gsqueue_set_t),
+ 0, gsqueue_set_cache_construct, gsqueue_set_cache_destruct,
+ NULL, NULL, NULL, 0);
+
+
+ mutex_enter(&cpu_lock);
+ register_cpu_setup_func(gsqueue_cpu_setup, NULL);
+ mutex_exit(&cpu_lock);
+}
+
+static int
+gsqueue_ddifini(void)
+{
+ mutex_enter(&gsqueue_lock);
+ if (list_is_empty(&gsqueue_list) == 0) {
+ mutex_exit(&gsqueue_lock);
+ return (EBUSY);
+ }
+ list_destroy(&gsqueue_list);
+ mutex_exit(&gsqueue_lock);
+
+ mutex_enter(&cpu_lock);
+ register_cpu_setup_func(gsqueue_cpu_setup, NULL);
+ mutex_exit(&cpu_lock);
+
+ kmem_cache_destroy(gsqueue_set_cache);
+ kmem_cache_destroy(gsqueue_cpu_cache);
+ kmem_cache_destroy(gsqueue_cb_cache);
+
+ mutex_destroy(&gsqueue_lock);
+
+ return (0);
+}
+
+static struct modlmisc gsqueue_modmisc = {
+ &mod_miscops,
+ "gsqueue"
+};
+
+static struct modlinkage gsqueue_modlinkage = {
+ MODREV_1,
+ &gsqueue_modmisc,
+ NULL
+};
+
+int
+_init(void)
+{
+ int ret;
+
+ gsqueue_ddiinit();
+ if ((ret = mod_install(&gsqueue_modlinkage)) != 0) {
+ VERIFY(gsqueue_ddifini() == 0);
+ return (ret);
+ }
+
+ return (ret);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&gsqueue_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int ret;
+
+ if ((ret = gsqueue_ddifini()) != 0)
+ return (ret);
+
+ if ((ret = mod_remove(&gsqueue_modlinkage)) != 0)
+ return (ret);
+
+ return (0);
+}
diff --git a/usr/src/uts/common/io/inotify.c b/usr/src/uts/common/io/inotify.c
new file mode 100644
index 0000000000..67bf55f213
--- /dev/null
+++ b/usr/src/uts/common/io/inotify.c
@@ -0,0 +1,1559 @@
+/*
+ * 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 2020 Joyent, Inc.
+ * Copyright (c) 2015 The MathWorks, Inc. All rights reserved.
+ */
+
+/*
+ * Support for the inotify facility, a Linux-borne facility for asynchronous
+ * notification of certain events on specified files or directories. Our
+ * implementation broadly leverages the file event monitoring facility, and
+ * would actually be quite straightforward were it not for a very serious
+ * blunder in the inotify interface: in addition to allowing for one to be
+ * notified on events on a particular file or directory, inotify also allows
+ * for one to be notified on certain events on files _within_ a watched
+ * directory -- even though those events have absolutely nothing to do with
+ * the directory itself. This leads to all sorts of madness because file
+ * operations are (of course) not undertaken on paths but rather on open
+ * files -- and the relationships between open files and the paths that resolve
+ * to those files are neither static nor isomorphic. We implement this
+ * concept by having _child watches_ when directories are watched with events
+ * in IN_CHILD_EVENTS. We add child watches when a watch on a directory is
+ * first added, and we modify those child watches dynamically as files are
+ * created, deleted, moved into or moved out of the specified directory. This
+ * mechanism works well, absent hard links. Hard links, unfortunately, break
+ * this rather badly, and the user is warned that watches on directories that
+ * have multiple directory entries referring to the same file may behave
+ * unexpectedly.
+ */
+
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/inotify.h>
+#include <sys/fem.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vmem.h>
+#include <sys/avl.h>
+#include <sys/sysmacros.h>
+#include <sys/cyclic.h>
+#include <sys/filio.h>
+
+struct inotify_state;
+struct inotify_kevent;
+
+typedef struct inotify_watch inotify_watch_t;
+typedef struct inotify_state inotify_state_t;
+typedef struct inotify_kevent inotify_kevent_t;
+
+struct inotify_watch {
+ kmutex_t inw_lock; /* lock protecting ref count */
+ int inw_refcnt; /* reference count */
+ uint8_t inw_zombie:1; /* boolean: is zombie */
+ uint8_t inw_fired:1; /* boolean: fired one-shot */
+ uint8_t inw_active:1; /* boolean: watch is active */
+ uint8_t inw_orphaned:1; /* boolean: orphaned */
+ kcondvar_t inw_cv; /* condvar for zombifier */
+ uint32_t inw_mask; /* mask of watch */
+ int32_t inw_wd; /* watch descriptor */
+ vnode_t *inw_vp; /* underlying vnode */
+ inotify_watch_t *inw_parent; /* parent, if a child */
+ avl_node_t inw_byvp; /* watches by vnode */
+ avl_node_t inw_bywd; /* watches by descriptor */
+ avl_tree_t inw_children; /* children, if a parent */
+ char *inw_name; /* name, if a child */
+ list_node_t inw_orphan; /* orphan list */
+ cred_t *inw_cred; /* cred, if orphaned */
+ inotify_state_t *inw_state; /* corresponding state */
+};
+
+struct inotify_kevent {
+ inotify_kevent_t *ine_next; /* next event in queue */
+ struct inotify_event ine_event; /* event (variable size) */
+};
+
+#define INOTIFY_EVENT_LENGTH(ev) \
+ (sizeof (inotify_kevent_t) + (ev)->ine_event.len)
+
+struct inotify_state {
+ kmutex_t ins_lock; /* lock protecting state */
+ avl_tree_t ins_byvp; /* watches by vnode */
+ avl_tree_t ins_bywd; /* watches by descriptor */
+ vmem_t *ins_wds; /* watch identifier arena */
+ int ins_maxwatches; /* maximum number of watches */
+ int ins_maxevents; /* maximum number of events */
+ int ins_nevents; /* current # of events */
+ int32_t ins_size; /* total size of events */
+ inotify_kevent_t *ins_head; /* head of event queue */
+ inotify_kevent_t *ins_tail; /* tail of event queue */
+ pollhead_t ins_pollhd; /* poll head */
+ kcondvar_t ins_cv; /* condvar for reading */
+ list_t ins_orphans; /* orphan list */
+ ddi_periodic_t ins_cleaner; /* cyclic for cleaning */
+ inotify_watch_t *ins_zombies; /* zombie watch list */
+ cred_t *ins_cred; /* creator's credentials */
+ inotify_state_t *ins_next; /* next state on global list */
+};
+
+/*
+ * Tunables (exported read-only in lx-branded zones via /proc).
+ */
+int inotify_maxwatches = 8192; /* max watches per instance */
+int inotify_maxevents = 16384; /* max events */
+int inotify_maxinstances = 128; /* max instances per user */
+
+/*
+ * Internal global variables.
+ */
+static kmutex_t inotify_lock; /* lock protecting state */
+static dev_info_t *inotify_devi; /* device info */
+static fem_t *inotify_femp; /* FEM pointer */
+static vmem_t *inotify_minor; /* minor number arena */
+static void *inotify_softstate; /* softstate pointer */
+static inotify_state_t *inotify_state; /* global list if state */
+
+static void inotify_watch_event(inotify_watch_t *, uint64_t, char *);
+static void inotify_watch_insert(inotify_watch_t *, vnode_t *, char *);
+static void inotify_watch_delete(inotify_watch_t *, uint32_t);
+static void inotify_watch_remove(inotify_state_t *state,
+ inotify_watch_t *watch);
+
+static int
+inotify_fop_close(femarg_t *vf, int flag, int count, offset_t offset,
+ cred_t *cr, caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_close(vf, flag, count, offset, cr, ct)) == 0) {
+ inotify_watch_event(watch, flag & FWRITE ?
+ IN_CLOSE_WRITE : IN_CLOSE_NOWRITE, NULL);
+ }
+
+ return (rval);
+}
+
+static int
+inotify_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
+ int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
+ vsecattr_t *vsecp)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_create(vf, name, vap, excl, mode,
+ vpp, cr, flag, ct, vsecp)) == 0) {
+ inotify_watch_insert(watch, *vpp, name);
+ inotify_watch_event(watch, IN_CREATE, name);
+ }
+
+ return (rval);
+}
+
+static int
+inotify_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
+ caller_context_t *ct, int flags)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_link(vf, svp, tnm, cr, ct, flags)) == 0) {
+ inotify_watch_insert(watch, svp, tnm);
+ inotify_watch_event(watch, IN_CREATE, tnm);
+ }
+
+ return (rval);
+}
+
+static int
+inotify_fop_mkdir(femarg_t *vf, char *name, vattr_t *vap, vnode_t **vpp,
+ cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_mkdir(vf, name, vap, vpp, cr,
+ ct, flags, vsecp)) == 0) {
+ inotify_watch_insert(watch, *vpp, name);
+ inotify_watch_event(watch, IN_CREATE | IN_ISDIR, name);
+ }
+
+ return (rval);
+}
+
+static int
+inotify_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_open(vf, mode, cr, ct)) == 0)
+ inotify_watch_event(watch, IN_OPEN, NULL);
+
+ return (rval);
+}
+
+static int
+inotify_fop_read(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
+ caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval = vnext_read(vf, uiop, ioflag, cr, ct);
+ inotify_watch_event(watch, IN_ACCESS, NULL);
+
+ return (rval);
+}
+
+static int
+inotify_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
+ caller_context_t *ct, int flags)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval = vnext_readdir(vf, uiop, cr, eofp, ct, flags);
+ inotify_watch_event(watch, IN_ACCESS | IN_ISDIR, NULL);
+
+ return (rval);
+}
+
+int
+inotify_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
+ int flags)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_remove(vf, nm, cr, ct, flags)) == 0)
+ inotify_watch_event(watch, IN_DELETE, nm);
+
+ return (rval);
+}
+
+int
+inotify_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
+ caller_context_t *ct, int flags)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_rmdir(vf, nm, cdir, cr, ct, flags)) == 0)
+ inotify_watch_event(watch, IN_DELETE | IN_ISDIR, nm);
+
+ return (rval);
+}
+
+static int
+inotify_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval;
+
+ if ((rval = vnext_setattr(vf, vap, flags, cr, ct)) == 0)
+ inotify_watch_event(watch, IN_ATTRIB, NULL);
+
+ return (rval);
+}
+
+static int
+inotify_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
+ caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+ int rval = vnext_write(vf, uiop, ioflag, cr, ct);
+ inotify_watch_event(watch, IN_MODIFY, NULL);
+
+ return (rval);
+}
+
+static int
+inotify_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name,
+ caller_context_t *ct)
+{
+ inotify_watch_t *watch = vf->fa_fnode->fn_available;
+
+ switch (vnevent) {
+ case VE_RENAME_SRC:
+ inotify_watch_event(watch, IN_MOVE_SELF, NULL);
+ inotify_watch_delete(watch, IN_MOVE_SELF);
+ break;
+ case VE_REMOVE:
+ /*
+ * Linux will apparently fire an IN_ATTRIB event when the link
+ * count changes (including when it drops to 0 on a remove).
+ * This is merely somewhat odd; what is amazing is that this
+ * IN_ATTRIB event is not visible on an inotify watch on the
+ * parent directory. (IN_ATTRIB events are normally sent to
+ * watches on the parent directory). While it's hard to
+ * believe that this constitutes desired semantics, ltp
+ * unfortunately tests this case (if implicitly); in the name
+ * of bug-for-bug compatibility, we fire IN_ATTRIB iff we are
+ * explicitly watching the file that has been removed.
+ */
+ if (watch->inw_parent == NULL)
+ inotify_watch_event(watch, IN_ATTRIB, NULL);
+
+ /*FALLTHROUGH*/
+ case VE_RENAME_DEST:
+ inotify_watch_event(watch, IN_DELETE_SELF, NULL);
+ inotify_watch_delete(watch, IN_DELETE_SELF);
+ break;
+ case VE_RMDIR:
+ /*
+ * It seems that IN_ISDIR should really be OR'd in here, but
+ * Linux doesn't seem to do that in this case; for the sake of
+ * bug-for-bug compatibility, we don't do it either.
+ */
+ inotify_watch_event(watch, IN_DELETE_SELF, NULL);
+ inotify_watch_delete(watch, IN_DELETE_SELF);
+ break;
+ case VE_CREATE:
+ case VE_TRUNCATE:
+ case VE_RESIZE:
+ inotify_watch_event(watch, IN_MODIFY | IN_ATTRIB, NULL);
+ break;
+ case VE_LINK:
+ inotify_watch_event(watch, IN_ATTRIB, NULL);
+ break;
+ case VE_RENAME_SRC_DIR:
+ inotify_watch_event(watch, IN_MOVED_FROM, name);
+ break;
+ case VE_RENAME_DEST_DIR:
+ if (name == NULL)
+ name = dvp->v_path;
+
+ inotify_watch_insert(watch, dvp, name);
+ inotify_watch_event(watch, IN_MOVED_TO, name);
+ break;
+ case VE_SUPPORT:
+ case VE_MOUNTEDOVER:
+ case VE_PRE_RENAME_SRC:
+ case VE_PRE_RENAME_DEST:
+ case VE_PRE_RENAME_DEST_DIR:
+ break;
+ }
+
+ return (vnext_vnevent(vf, vnevent, dvp, name, ct));
+}
+
+const fs_operation_def_t inotify_vnodesrc_template[] = {
+ VOPNAME_CLOSE, { .femop_close = inotify_fop_close },
+ VOPNAME_CREATE, { .femop_create = inotify_fop_create },
+ VOPNAME_LINK, { .femop_link = inotify_fop_link },
+ VOPNAME_MKDIR, { .femop_mkdir = inotify_fop_mkdir },
+ VOPNAME_OPEN, { .femop_open = inotify_fop_open },
+ VOPNAME_READ, { .femop_read = inotify_fop_read },
+ VOPNAME_READDIR, { .femop_readdir = inotify_fop_readdir },
+ VOPNAME_REMOVE, { .femop_remove = inotify_fop_remove },
+ VOPNAME_RMDIR, { .femop_rmdir = inotify_fop_rmdir },
+ VOPNAME_SETATTR, { .femop_setattr = inotify_fop_setattr },
+ VOPNAME_WRITE, { .femop_write = inotify_fop_write },
+ VOPNAME_VNEVENT, { .femop_vnevent = inotify_fop_vnevent },
+ NULL, NULL
+};
+
+static int
+inotify_watch_cmpwd(inotify_watch_t *lhs, inotify_watch_t *rhs)
+{
+ if (lhs->inw_wd < rhs->inw_wd)
+ return (-1);
+
+ if (lhs->inw_wd > rhs->inw_wd)
+ return (1);
+
+ return (0);
+}
+
+static int
+inotify_watch_cmpvp(inotify_watch_t *lhs, inotify_watch_t *rhs)
+{
+ uintptr_t lvp = (uintptr_t)lhs->inw_vp, rvp = (uintptr_t)rhs->inw_vp;
+
+ if (lvp < rvp)
+ return (-1);
+
+ if (lvp > rvp)
+ return (1);
+
+ return (0);
+}
+
+static void
+inotify_watch_hold(inotify_watch_t *watch)
+{
+ mutex_enter(&watch->inw_lock);
+ VERIFY(watch->inw_refcnt > 0);
+ watch->inw_refcnt++;
+ mutex_exit(&watch->inw_lock);
+}
+
+static void
+inotify_watch_release(inotify_watch_t *watch)
+{
+ mutex_enter(&watch->inw_lock);
+ VERIFY(watch->inw_refcnt > 1);
+
+ if (--watch->inw_refcnt == 1 && watch->inw_zombie) {
+ /*
+ * We're down to our last reference; kick anyone that might be
+ * waiting.
+ */
+ cv_signal(&watch->inw_cv);
+ }
+
+ mutex_exit(&watch->inw_lock);
+}
+
+static void
+inotify_watch_event(inotify_watch_t *watch, uint64_t mask, char *name)
+{
+ inotify_kevent_t *event, *tail;
+ inotify_state_t *state = watch->inw_state;
+ uint32_t wd = watch->inw_wd, cookie = 0, len;
+ boolean_t removal = mask & IN_REMOVAL ? B_TRUE : B_FALSE;
+ inotify_watch_t *source = watch;
+
+ if (!(mask &= watch->inw_mask) || mask == IN_ISDIR)
+ return;
+
+ if (watch->inw_parent != NULL) {
+ /*
+ * This is an event on the child; if this isn't a valid child
+ * event, return. Otherwise, we move our watch to be our
+ * parent (which we know is around because we have a hold on
+ * it) and continue.
+ */
+ if (!(mask & IN_CHILD_EVENTS))
+ return;
+
+ name = watch->inw_name;
+ watch = watch->inw_parent;
+ wd = watch->inw_wd;
+ }
+
+ if (!removal) {
+ mutex_enter(&state->ins_lock);
+
+ if (watch->inw_zombie ||
+ watch->inw_fired || !watch->inw_active) {
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+ } else {
+ if (!watch->inw_active)
+ return;
+
+ VERIFY(MUTEX_HELD(&state->ins_lock));
+ }
+
+ /*
+ * If this is an operation on a directory and it's a child event
+ * (event if it's not on a child), we specify IN_ISDIR.
+ */
+ if (source->inw_vp->v_type == VDIR && (mask & IN_CHILD_EVENTS))
+ mask |= IN_ISDIR;
+
+ if (mask & (IN_MOVED_FROM | IN_MOVED_TO))
+ cookie = (uint32_t)curthread->t_did;
+
+ if (state->ins_nevents >= state->ins_maxevents) {
+ /*
+ * We're at our maximum number of events -- turn our event
+ * into an IN_Q_OVERFLOW event, which will be coalesced if
+ * it's already the tail event.
+ */
+ mask = IN_Q_OVERFLOW;
+ wd = (uint32_t)-1;
+ cookie = 0;
+ len = 0;
+ }
+
+ if ((tail = state->ins_tail) != NULL && tail->ine_event.wd == wd &&
+ tail->ine_event.mask == mask && tail->ine_event.cookie == cookie &&
+ ((tail->ine_event.len == 0 && len == 0) ||
+ (name != NULL && tail->ine_event.len != 0 &&
+ strcmp(tail->ine_event.name, name) == 0))) {
+ /*
+ * This is an implicitly coalesced event; we're done.
+ */
+ if (!removal)
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ if (name != NULL) {
+ /*
+ * We are in the context of a file event monitoring operation,
+ * so the name length is bounded by the kernel.
+ */
+ len = strlen(name) + 1;
+ len = roundup(len, sizeof (struct inotify_event));
+ } else {
+ len = 0;
+ }
+
+ event = kmem_zalloc(sizeof (inotify_kevent_t) + len, KM_SLEEP);
+ event->ine_event.wd = wd;
+ event->ine_event.mask = (uint32_t)mask;
+ event->ine_event.cookie = cookie;
+ event->ine_event.len = len;
+
+ if (name != NULL)
+ (void) strcpy(event->ine_event.name, name);
+
+ if (tail != NULL) {
+ tail->ine_next = event;
+ } else {
+ VERIFY(state->ins_head == NULL);
+ state->ins_head = event;
+ cv_broadcast(&state->ins_cv);
+ }
+
+ state->ins_tail = event;
+ state->ins_nevents++;
+ state->ins_size += sizeof (event->ine_event) + len;
+
+ if (removal)
+ return;
+
+ if ((watch->inw_mask & IN_ONESHOT) && !watch->inw_fired) {
+ /*
+ * If this is a one-shot, we need to remove the watch. (Note
+ * that this will recurse back into inotify_watch_event() to
+ * fire the IN_IGNORED event -- but with "removal" set.)
+ */
+ watch->inw_fired = 1;
+ inotify_watch_remove(state, watch);
+ }
+
+ mutex_exit(&state->ins_lock);
+ pollwakeup(&state->ins_pollhd, POLLRDNORM | POLLIN);
+}
+
+/*
+ * Destroy a watch. By the time we're in here, the watch must have exactly
+ * one reference.
+ */
+static void
+inotify_watch_destroy(inotify_watch_t *watch)
+{
+ VERIFY(MUTEX_HELD(&watch->inw_lock));
+
+ if (watch->inw_name != NULL)
+ kmem_free(watch->inw_name, strlen(watch->inw_name) + 1);
+
+ kmem_free(watch, sizeof (inotify_watch_t));
+}
+
+static int
+inotify_fem_install(vnode_t *vp, inotify_watch_t *watch)
+{
+ /*
+ * For vnodes that are devices (of type VCHR or VBLK), we silently
+ * refuse to actually install any event monitor. This is to avoid
+ * single-thread deadlock when both a special device vnode and its
+ * underlying real vnode are being watched: releasing the device
+ * vnode upon watch removal can induce an attribute update on the
+ * underlying vnode, which will bring us into inotify_watch_event()
+ * with our lock already held. While we could fail earlier and more
+ * explicitly in this case, we choose to keep with the Linux behavior
+ * on unwatchable entities and allow the watch but not generate any
+ * events for it.
+ */
+ if (vp->v_type == VCHR || vp->v_type == VBLK)
+ return (0);
+
+ return (fem_install(vp, inotify_femp, watch, OPARGUNIQ,
+ (void (*)(void *))inotify_watch_hold,
+ (void (*)(void *))inotify_watch_release));
+}
+
+static int
+inotify_fem_uninstall(vnode_t *vp, inotify_watch_t *watch)
+{
+ /*
+ * See inotify_fem_install(), above, for our rationale here.
+ */
+ if (vp->v_type == VCHR || vp->v_type == VBLK)
+ return (0);
+
+ return (fem_uninstall(vp, inotify_femp, watch));
+}
+
+/*
+ * Zombify a watch. By the time we come in here, it must be true that the
+ * watch has already been fem_uninstall()'d -- the only reference should be
+ * in the state's data structure. If we can get away with freeing it, we'll
+ * do that -- but if the reference count is greater than one due to an active
+ * vnode operation, we'll put this watch on the zombie list on the state
+ * structure.
+ */
+static void
+inotify_watch_zombify(inotify_watch_t *watch)
+{
+ inotify_state_t *state = watch->inw_state;
+
+ VERIFY(MUTEX_HELD(&state->ins_lock));
+ VERIFY(!watch->inw_zombie);
+
+ watch->inw_zombie = 1;
+
+ if (watch->inw_parent != NULL) {
+ inotify_watch_release(watch->inw_parent);
+ } else {
+ avl_remove(&state->ins_byvp, watch);
+ avl_remove(&state->ins_bywd, watch);
+ vmem_free(state->ins_wds, (void *)(uintptr_t)watch->inw_wd, 1);
+ watch->inw_wd = -1;
+ }
+
+ mutex_enter(&watch->inw_lock);
+
+ if (watch->inw_refcnt == 1) {
+ /*
+ * There are no operations in flight and there is no way
+ * for anyone to discover this watch -- we can destroy it.
+ */
+ inotify_watch_destroy(watch);
+ } else {
+ /*
+ * There are operations in flight; we will need to enqueue
+ * this for later destruction.
+ */
+ watch->inw_parent = state->ins_zombies;
+ state->ins_zombies = watch;
+ mutex_exit(&watch->inw_lock);
+ }
+}
+
+static inotify_watch_t *
+inotify_watch_add(inotify_state_t *state, inotify_watch_t *parent,
+ const char *name, vnode_t *vp, uint32_t mask)
+{
+ inotify_watch_t *watch;
+ int err;
+
+ VERIFY(MUTEX_HELD(&state->ins_lock));
+
+ watch = kmem_zalloc(sizeof (inotify_watch_t), KM_SLEEP);
+
+ watch->inw_vp = vp;
+ watch->inw_mask = mask;
+ watch->inw_state = state;
+ watch->inw_refcnt = 1;
+
+ if (parent == NULL) {
+ watch->inw_wd = (int)(uintptr_t)vmem_alloc(state->ins_wds,
+ 1, VM_BESTFIT | VM_SLEEP);
+ avl_add(&state->ins_byvp, watch);
+ avl_add(&state->ins_bywd, watch);
+
+ avl_create(&watch->inw_children,
+ (int(*)(const void *, const void *))inotify_watch_cmpvp,
+ sizeof (inotify_watch_t),
+ offsetof(inotify_watch_t, inw_byvp));
+ } else {
+ VERIFY(name != NULL);
+ inotify_watch_hold(parent);
+ watch->inw_mask &= IN_CHILD_EVENTS;
+ watch->inw_parent = parent;
+
+ /*
+ * Copy the name. Note that when the name is user-specified,
+ * its length is bounded by the copyinstr() to be MAXPATHLEN
+ * (and regardless, we know by this point that it exists in
+ * our parent).
+ */
+ watch->inw_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
+ (void) strcpy(watch->inw_name, name);
+
+ avl_add(&parent->inw_children, watch);
+ }
+
+ /*
+ * Add our monitor to the vnode. We must not have the watch lock held
+ * when we do this, as it will immediately hold our watch.
+ */
+ err = inotify_fem_install(vp, watch);
+
+ VERIFY(err == 0);
+
+ return (watch);
+}
+
+/*
+ * Remove a (non-child) watch. This is called from either synchronous context
+ * via inotify_rm_watch() or monitor context via either a vnevent or a
+ * one-shot.
+ */
+static void
+inotify_watch_remove(inotify_state_t *state, inotify_watch_t *watch)
+{
+ inotify_watch_t *child;
+ int err;
+
+ VERIFY(MUTEX_HELD(&state->ins_lock));
+ VERIFY(watch->inw_parent == NULL);
+
+ err = inotify_fem_uninstall(watch->inw_vp, watch);
+ VERIFY(err == 0);
+
+ /*
+ * If we have children, we're going to remove them all and set them
+ * all to be zombies.
+ */
+ while ((child = avl_first(&watch->inw_children)) != NULL) {
+ VERIFY(child->inw_parent == watch);
+ avl_remove(&watch->inw_children, child);
+
+ err = inotify_fem_uninstall(child->inw_vp, child);
+ VERIFY(err == 0);
+
+ /*
+ * If this child watch has been orphaned, remove it from the
+ * state's list of orphans.
+ */
+ if (child->inw_orphaned) {
+ list_remove(&state->ins_orphans, child);
+ crfree(child->inw_cred);
+ }
+
+ VN_PHANTOM_RELE(child->inw_vp);
+
+ /*
+ * We're down (or should be down) to a single reference to
+ * this child watch; it's safe to zombify it.
+ */
+ inotify_watch_zombify(child);
+ }
+
+ inotify_watch_event(watch, IN_IGNORED | IN_REMOVAL, NULL);
+ VN_PHANTOM_RELE(watch->inw_vp);
+
+ /*
+ * It's now safe to zombify the watch -- we know that the only reference
+ * can come from operations in flight.
+ */
+ inotify_watch_zombify(watch);
+}
+
+/*
+ * Delete a watch. Should only be called from VOP context.
+ */
+static void
+inotify_watch_delete(inotify_watch_t *watch, uint32_t event)
+{
+ inotify_state_t *state = watch->inw_state;
+ inotify_watch_t cmp = { .inw_vp = watch->inw_vp }, *parent;
+ int err;
+
+ if (event != IN_DELETE_SELF && !(watch->inw_mask & IN_CHILD_EVENTS))
+ return;
+
+ mutex_enter(&state->ins_lock);
+
+ if (watch->inw_zombie) {
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ if ((parent = watch->inw_parent) == NULL) {
+ if (event == IN_DELETE_SELF) {
+ /*
+ * If we're here because we're being deleted and we
+ * are not a child watch, we need to delete the entire
+ * watch, children and all.
+ */
+ inotify_watch_remove(state, watch);
+ }
+
+ mutex_exit(&state->ins_lock);
+ return;
+ } else {
+ if (event == IN_DELETE_SELF &&
+ !(parent->inw_mask & IN_EXCL_UNLINK)) {
+ /*
+ * This is a child watch for a file that is being
+ * removed and IN_EXCL_UNLINK has not been specified;
+ * indicate that it is orphaned and add it to the list
+ * of orphans. (This list will be checked by the
+ * cleaning cyclic to determine when the watch has
+ * become the only hold on the vnode, at which point
+ * the watch can be zombified.) Note that we check
+ * if the watch is orphaned before we orphan it: hard
+ * links make it possible for VE_REMOVE to be called
+ * multiple times on the same vnode. (!)
+ */
+ if (!watch->inw_orphaned) {
+ watch->inw_orphaned = 1;
+ watch->inw_cred = CRED();
+ crhold(watch->inw_cred);
+ list_insert_head(&state->ins_orphans, watch);
+ }
+
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ if (watch->inw_orphaned) {
+ /*
+ * If we're here, a file was orphaned and then later
+ * moved -- which almost certainly means that hard
+ * links are on the scene. We choose the orphan over
+ * the move because we don't want to spuriously
+ * drop events if we can avoid it.
+ */
+ crfree(watch->inw_cred);
+ list_remove(&state->ins_orphans, watch);
+ }
+ }
+
+ if (avl_find(&parent->inw_children, &cmp, NULL) == NULL) {
+ /*
+ * This watch has already been deleted from the parent.
+ */
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ avl_remove(&parent->inw_children, watch);
+ err = inotify_fem_uninstall(watch->inw_vp, watch);
+ VERIFY(err == 0);
+
+ VN_PHANTOM_RELE(watch->inw_vp);
+
+ /*
+ * It's now safe to zombify the watch -- which won't actually delete
+ * it as we know that the reference count is greater than 1.
+ */
+ inotify_watch_zombify(watch);
+ mutex_exit(&state->ins_lock);
+}
+
+/*
+ * Insert a new child watch. Should only be called from VOP context when
+ * a child is created in a watched directory.
+ */
+static void
+inotify_watch_insert(inotify_watch_t *watch, vnode_t *vp, char *name)
+{
+ inotify_state_t *state = watch->inw_state;
+ inotify_watch_t cmp = { .inw_vp = vp };
+
+ if (!(watch->inw_mask & IN_CHILD_EVENTS))
+ return;
+
+ mutex_enter(&state->ins_lock);
+
+ if (watch->inw_zombie || watch->inw_parent != NULL || vp == NULL) {
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ if (avl_find(&watch->inw_children, &cmp, NULL) != NULL) {
+ mutex_exit(&state->ins_lock);
+ return;
+ }
+
+ VN_PHANTOM_HOLD(vp);
+ watch = inotify_watch_add(state, watch, name, vp, watch->inw_mask);
+ VERIFY(watch != NULL);
+
+ mutex_exit(&state->ins_lock);
+}
+
+
+static int
+inotify_add_watch(inotify_state_t *state, vnode_t *vp, uint32_t mask,
+ int32_t *wdp)
+{
+ inotify_watch_t *watch, cmp = { .inw_vp = vp };
+ uint32_t set;
+
+ set = (mask & (IN_ALL_EVENTS | IN_MODIFIERS)) | IN_UNMASKABLE;
+
+ /*
+ * Lookup our vnode to determine if we already have a watch on it.
+ */
+ mutex_enter(&state->ins_lock);
+
+ if ((watch = avl_find(&state->ins_byvp, &cmp, NULL)) == NULL) {
+ /*
+ * We don't have this watch; allocate a new one, provided that
+ * we have fewer than our limit.
+ */
+ if (avl_numnodes(&state->ins_bywd) >= state->ins_maxwatches) {
+ mutex_exit(&state->ins_lock);
+ return (ENOSPC);
+ }
+
+ VN_PHANTOM_HOLD(vp);
+ watch = inotify_watch_add(state, NULL, NULL, vp, set);
+ *wdp = watch->inw_wd;
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+ }
+
+ VERIFY(!watch->inw_zombie);
+
+ if (!(mask & IN_MASK_ADD)) {
+ /*
+ * Note that if we're resetting our event mask and we're
+ * transitioning from an event mask that includes child events
+ * to one that doesn't, there will be potentially some stale
+ * child watches. This is basically fine: they won't fire,
+ * and they will correctly be removed when the watch is
+ * removed.
+ */
+ watch->inw_mask = 0;
+ }
+
+ watch->inw_mask |= set;
+
+ *wdp = watch->inw_wd;
+
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+}
+
+static int
+inotify_add_child(inotify_state_t *state, vnode_t *vp, char *name)
+{
+ inotify_watch_t *watch, cmp = { .inw_vp = vp };
+ vnode_t *cvp;
+ int err;
+
+ /*
+ * Verify that the specified child doesn't have a directory component
+ * within it.
+ */
+ if (strchr(name, '/') != NULL)
+ return (EINVAL);
+
+ /*
+ * Lookup the underlying file. Note that this will succeed even if
+ * we don't have permissions to actually read the file.
+ */
+ if ((err = lookupnameat(name,
+ UIO_SYSSPACE, NO_FOLLOW, NULL, &cvp, vp)) != 0) {
+ return (err);
+ }
+
+ /*
+ * Use our vnode to find our watch, and then add our child watch to it.
+ */
+ mutex_enter(&state->ins_lock);
+
+ if ((watch = avl_find(&state->ins_byvp, &cmp, NULL)) == NULL) {
+ /*
+ * This is unexpected -- it means that we don't have the
+ * watch that we thought we had.
+ */
+ mutex_exit(&state->ins_lock);
+ VN_RELE(cvp);
+ return (ENXIO);
+ }
+
+ /*
+ * Now lookup the child vnode in the watch; we'll only add it if it
+ * isn't already there.
+ */
+ cmp.inw_vp = cvp;
+
+ if (avl_find(&watch->inw_children, &cmp, NULL) != NULL) {
+ mutex_exit(&state->ins_lock);
+ VN_RELE(cvp);
+ return (0);
+ }
+
+ /* Trade the plain hold from lookupnameat() for a phantom hold */
+ VN_PHANTOM_HOLD(cvp);
+ VN_RELE(cvp);
+
+ watch = inotify_watch_add(state, watch, name, cvp, watch->inw_mask);
+ VERIFY(watch != NULL);
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+}
+
+static int
+inotify_rm_watch(inotify_state_t *state, int32_t wd)
+{
+ inotify_watch_t *watch, cmp = { .inw_wd = wd };
+
+ mutex_enter(&state->ins_lock);
+
+ if ((watch = avl_find(&state->ins_bywd, &cmp, NULL)) == NULL) {
+ mutex_exit(&state->ins_lock);
+ return (EINVAL);
+ }
+
+ inotify_watch_remove(state, watch);
+ mutex_exit(&state->ins_lock);
+
+ /*
+ * Because removing a watch will generate an IN_IGNORED event (and
+ * because inotify_watch_remove() won't alone induce a pollwakeup()),
+ * we need to explicitly issue a pollwakeup().
+ */
+ pollwakeup(&state->ins_pollhd, POLLRDNORM | POLLIN);
+
+ return (0);
+}
+
+static int
+inotify_activate(inotify_state_t *state, int32_t wd)
+{
+ inotify_watch_t *watch, cmp = { .inw_wd = wd };
+
+ mutex_enter(&state->ins_lock);
+
+ if ((watch = avl_find(&state->ins_bywd, &cmp, NULL)) == NULL) {
+ mutex_exit(&state->ins_lock);
+ return (EINVAL);
+ }
+
+ watch->inw_active = 1;
+
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+}
+
+/*
+ * Called periodically as a cyclic to process the orphans and zombies.
+ */
+static void
+inotify_clean(void *arg)
+{
+ inotify_state_t *state = arg;
+ inotify_watch_t *watch, *parent, *next, **prev;
+ cred_t *savecred;
+ int err;
+
+ mutex_enter(&state->ins_lock);
+
+ for (watch = list_head(&state->ins_orphans);
+ watch != NULL; watch = next) {
+ next = list_next(&state->ins_orphans, watch);
+
+ VERIFY(!watch->inw_zombie);
+ VERIFY((parent = watch->inw_parent) != NULL);
+
+ if (watch->inw_vp->v_count > 1)
+ continue;
+
+ avl_remove(&parent->inw_children, watch);
+ err = inotify_fem_uninstall(watch->inw_vp, watch);
+ VERIFY(err == 0);
+
+ list_remove(&state->ins_orphans, watch);
+
+ /*
+ * For purposes of releasing the vnode, we need to switch our
+ * cred to be the cred of the orphaning thread (which we held
+ * at the time this watch was orphaned).
+ */
+ savecred = curthread->t_cred;
+ curthread->t_cred = watch->inw_cred;
+ VN_PHANTOM_RELE(watch->inw_vp);
+ crfree(watch->inw_cred);
+ curthread->t_cred = savecred;
+
+ inotify_watch_zombify(watch);
+ }
+
+ prev = &state->ins_zombies;
+
+ while ((watch = *prev) != NULL) {
+ mutex_enter(&watch->inw_lock);
+
+ if (watch->inw_refcnt == 1) {
+ *prev = watch->inw_parent;
+ inotify_watch_destroy(watch);
+ continue;
+ }
+
+ prev = &watch->inw_parent;
+ mutex_exit(&watch->inw_lock);
+ }
+
+ mutex_exit(&state->ins_lock);
+}
+
+/*ARGSUSED*/
+static int
+inotify_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+ inotify_state_t *state;
+ major_t major = getemajor(*devp);
+ minor_t minor = getminor(*devp);
+ int instances = 0;
+ char c[64];
+
+ if (minor != INOTIFYMNRN_INOTIFY)
+ return (ENXIO);
+
+ mutex_enter(&inotify_lock);
+
+ for (state = inotify_state; state != NULL; state = state->ins_next) {
+ if (state->ins_cred == cred_p)
+ instances++;
+ }
+
+ if (instances >= inotify_maxinstances) {
+ mutex_exit(&inotify_lock);
+ return (EMFILE);
+ }
+
+ minor = (minor_t)(uintptr_t)vmem_alloc(inotify_minor, 1,
+ VM_BESTFIT | VM_SLEEP);
+
+ if (ddi_soft_state_zalloc(inotify_softstate, minor) != DDI_SUCCESS) {
+ vmem_free(inotify_minor, (void *)(uintptr_t)minor, 1);
+ mutex_exit(&inotify_lock);
+ return (EINVAL);
+ }
+
+ state = ddi_get_soft_state(inotify_softstate, minor);
+ *devp = makedevice(major, minor);
+
+ crhold(cred_p);
+ state->ins_cred = cred_p;
+ state->ins_next = inotify_state;
+ inotify_state = state;
+
+ (void) snprintf(c, sizeof (c), "inotify_watchid_%d", minor);
+ state->ins_wds = vmem_create(c, (void *)1, UINT32_MAX, 1,
+ NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER);
+
+ avl_create(&state->ins_bywd,
+ (int(*)(const void *, const void *))inotify_watch_cmpwd,
+ sizeof (inotify_watch_t),
+ offsetof(inotify_watch_t, inw_bywd));
+
+ avl_create(&state->ins_byvp,
+ (int(*)(const void *, const void *))inotify_watch_cmpvp,
+ sizeof (inotify_watch_t),
+ offsetof(inotify_watch_t, inw_byvp));
+
+ list_create(&state->ins_orphans, sizeof (inotify_watch_t),
+ offsetof(inotify_watch_t, inw_orphan));
+
+ state->ins_maxwatches = inotify_maxwatches;
+ state->ins_maxevents = inotify_maxevents;
+
+ mutex_exit(&inotify_lock);
+
+ state->ins_cleaner = ddi_periodic_add(inotify_clean,
+ state, NANOSEC, DDI_IPL_0);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+inotify_read(dev_t dev, uio_t *uio, cred_t *cr)
+{
+ inotify_state_t *state;
+ inotify_kevent_t *event;
+ minor_t minor = getminor(dev);
+ int err = 0, nevents = 0;
+ size_t len;
+
+ state = ddi_get_soft_state(inotify_softstate, minor);
+
+ mutex_enter(&state->ins_lock);
+
+ while (state->ins_head == NULL) {
+ if (uio->uio_fmode & (FNDELAY|FNONBLOCK)) {
+ mutex_exit(&state->ins_lock);
+ return (EAGAIN);
+ }
+
+ if (!cv_wait_sig_swap(&state->ins_cv, &state->ins_lock)) {
+ mutex_exit(&state->ins_lock);
+ return (EINTR);
+ }
+ }
+
+ /*
+ * We have events and we have our lock; return as many as we can.
+ */
+ while ((event = state->ins_head) != NULL) {
+ len = sizeof (event->ine_event) + event->ine_event.len;
+
+ if (uio->uio_resid < len) {
+ if (nevents == 0)
+ err = EINVAL;
+ break;
+ }
+
+ nevents++;
+
+ if ((err = uiomove(&event->ine_event, len, UIO_READ, uio)) != 0)
+ break;
+
+ VERIFY(state->ins_nevents > 0);
+ state->ins_nevents--;
+
+ VERIFY(state->ins_size > 0);
+ state->ins_size -= len;
+
+ if ((state->ins_head = event->ine_next) == NULL) {
+ VERIFY(event == state->ins_tail);
+ VERIFY(state->ins_nevents == 0);
+ state->ins_tail = NULL;
+ }
+
+ kmem_free(event, INOTIFY_EVENT_LENGTH(event));
+ }
+
+ mutex_exit(&state->ins_lock);
+
+ return (err);
+}
+
+static int
+inotify_poll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp)
+{
+ inotify_state_t *state;
+ minor_t minor = getminor(dev);
+
+ state = ddi_get_soft_state(inotify_softstate, minor);
+
+ mutex_enter(&state->ins_lock);
+
+ if (state->ins_head != NULL) {
+ *reventsp = events & (POLLRDNORM | POLLIN);
+ } else {
+ *reventsp = 0;
+ }
+
+ if ((*reventsp == 0 && !anyyet) || (events & POLLET)) {
+ *phpp = &state->ins_pollhd;
+ }
+
+ mutex_exit(&state->ins_lock);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+inotify_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
+{
+ inotify_state_t *state;
+ minor_t minor = getminor(dev);
+ file_t *fp;
+ int rval;
+
+ state = ddi_get_soft_state(inotify_softstate, minor);
+
+ switch (cmd) {
+ case INOTIFYIOC_ADD_WATCH: {
+ inotify_addwatch_t addwatch;
+ file_t *fp;
+
+ if (copyin((void *)arg, &addwatch, sizeof (addwatch)) != 0)
+ return (EFAULT);
+
+ if ((fp = getf(addwatch.inaw_fd)) == NULL)
+ return (EBADF);
+
+ rval = inotify_add_watch(state, fp->f_vnode,
+ addwatch.inaw_mask, rv);
+
+ releasef(addwatch.inaw_fd);
+ return (rval);
+ }
+
+ case INOTIFYIOC_ADD_CHILD: {
+ inotify_addchild_t addchild;
+ char name[MAXPATHLEN];
+
+ if (copyin((void *)arg, &addchild, sizeof (addchild)) != 0)
+ return (EFAULT);
+
+ if (copyinstr(addchild.inac_name, name, MAXPATHLEN, NULL) != 0)
+ return (EFAULT);
+
+ if ((fp = getf(addchild.inac_fd)) == NULL)
+ return (EBADF);
+
+ rval = inotify_add_child(state, fp->f_vnode, name);
+
+ releasef(addchild.inac_fd);
+ return (rval);
+ }
+
+ case INOTIFYIOC_RM_WATCH:
+ return (inotify_rm_watch(state, arg));
+
+ case INOTIFYIOC_ACTIVATE:
+ return (inotify_activate(state, arg));
+
+ case FIONREAD: {
+ int32_t size;
+
+ mutex_enter(&state->ins_lock);
+ size = state->ins_size;
+ mutex_exit(&state->ins_lock);
+
+ if (copyout(&size, (void *)arg, sizeof (size)) != 0)
+ return (EFAULT);
+
+ return (0);
+ }
+
+ default:
+ break;
+ }
+
+ return (ENOTTY);
+}
+
+/*ARGSUSED*/
+static int
+inotify_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
+{
+ inotify_state_t *state, **sp;
+ inotify_watch_t *watch, *zombies;
+ inotify_kevent_t *event;
+ minor_t minor = getminor(dev);
+
+ state = ddi_get_soft_state(inotify_softstate, minor);
+
+ if (state->ins_pollhd.ph_list != NULL) {
+ pollwakeup(&state->ins_pollhd, POLLERR);
+ pollhead_clean(&state->ins_pollhd);
+ }
+
+ mutex_enter(&state->ins_lock);
+
+ /*
+ * First, destroy all of our watches.
+ */
+ while ((watch = avl_first(&state->ins_bywd)) != NULL)
+ inotify_watch_remove(state, watch);
+
+ /*
+ * And now destroy our event queue.
+ */
+ while ((event = state->ins_head) != NULL) {
+ state->ins_head = event->ine_next;
+ kmem_free(event, INOTIFY_EVENT_LENGTH(event));
+ }
+
+ zombies = state->ins_zombies;
+ state->ins_zombies = NULL;
+ mutex_exit(&state->ins_lock);
+
+ /*
+ * Now that our state lock is dropped, we can synchronously wait on
+ * any zombies.
+ */
+ while ((watch = zombies) != NULL) {
+ zombies = zombies->inw_parent;
+
+ mutex_enter(&watch->inw_lock);
+
+ while (watch->inw_refcnt > 1)
+ cv_wait(&watch->inw_cv, &watch->inw_lock);
+
+ inotify_watch_destroy(watch);
+ }
+
+ if (state->ins_cleaner != NULL) {
+ ddi_periodic_delete(state->ins_cleaner);
+ state->ins_cleaner = NULL;
+ }
+
+ mutex_enter(&inotify_lock);
+
+ /*
+ * Remove our state from our global list, and release our hold on
+ * the cred.
+ */
+ for (sp = &inotify_state; *sp != state; sp = &((*sp)->ins_next))
+ VERIFY(*sp != NULL);
+
+ *sp = (*sp)->ins_next;
+ crfree(state->ins_cred);
+ vmem_destroy(state->ins_wds);
+
+ ddi_soft_state_free(inotify_softstate, minor);
+ vmem_free(inotify_minor, (void *)(uintptr_t)minor, 1);
+
+ mutex_exit(&inotify_lock);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+inotify_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ mutex_enter(&inotify_lock);
+
+ if (ddi_soft_state_init(&inotify_softstate,
+ sizeof (inotify_state_t), 0) != 0) {
+ cmn_err(CE_NOTE, "/dev/inotify failed to create soft state");
+ mutex_exit(&inotify_lock);
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_create_minor_node(devi, "inotify", S_IFCHR,
+ INOTIFYMNRN_INOTIFY, DDI_PSEUDO, 0) == DDI_FAILURE) {
+ cmn_err(CE_NOTE, "/dev/inotify couldn't create minor node");
+ ddi_soft_state_fini(&inotify_softstate);
+ mutex_exit(&inotify_lock);
+ return (DDI_FAILURE);
+ }
+
+ if (fem_create("inotify_fem",
+ inotify_vnodesrc_template, &inotify_femp) != 0) {
+ cmn_err(CE_NOTE, "/dev/inotify couldn't create FEM state");
+ ddi_remove_minor_node(devi, NULL);
+ ddi_soft_state_fini(&inotify_softstate);
+ mutex_exit(&inotify_lock);
+ return (DDI_FAILURE);
+ }
+
+ ddi_report_dev(devi);
+ inotify_devi = devi;
+
+ inotify_minor = vmem_create("inotify_minor", (void *)INOTIFYMNRN_CLONE,
+ UINT32_MAX - INOTIFYMNRN_CLONE, 1, NULL, NULL, NULL, 0,
+ VM_SLEEP | VMC_IDENTIFIER);
+
+ mutex_exit(&inotify_lock);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+inotify_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ switch (cmd) {
+ case DDI_DETACH:
+ break;
+
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ mutex_enter(&inotify_lock);
+ fem_free(inotify_femp);
+ vmem_destroy(inotify_minor);
+
+ ddi_remove_minor_node(inotify_devi, NULL);
+ inotify_devi = NULL;
+
+ ddi_soft_state_fini(&inotify_softstate);
+ mutex_exit(&inotify_lock);
+
+ return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+inotify_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ int error;
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = (void *)inotify_devi;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ }
+ return (error);
+}
+
+static struct cb_ops inotify_cb_ops = {
+ inotify_open, /* open */
+ inotify_close, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ inotify_read, /* read */
+ nodev, /* write */
+ inotify_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ inotify_poll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ 0, /* streamtab */
+ D_NEW | D_MP /* Driver compatibility flag */
+};
+
+static struct dev_ops inotify_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ inotify_info, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ inotify_attach, /* attach */
+ inotify_detach, /* detach */
+ nodev, /* reset */
+ &inotify_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev, /* dev power */
+ ddi_quiesce_not_needed, /* quiesce */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* module type (this is a pseudo driver) */
+ "inotify support", /* name of module */
+ &inotify_ops, /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ (void *)&modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&modlinkage));
+}
diff --git a/usr/src/uts/common/io/inotify.conf b/usr/src/uts/common/io/inotify.conf
new file mode 100644
index 0000000000..ce9da6180f
--- /dev/null
+++ b/usr/src/uts/common/io/inotify.conf
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+
+name="inotify" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/io/ixgbe/ixgbe_main.c b/usr/src/uts/common/io/ixgbe/ixgbe_main.c
index 86c46bee44..9966641df4 100644
--- a/usr/src/uts/common/io/ixgbe/ixgbe_main.c
+++ b/usr/src/uts/common/io/ixgbe/ixgbe_main.c
@@ -284,7 +284,7 @@ static adapter_info_t ixgbe_82599eb_cap = {
128, /* default number of rx queues */
64, /* maximum number of rx groups */
1, /* minimum number of rx groups */
- 1, /* default number of rx groups */
+ 32, /* default number of rx groups */
128, /* maximum number of tx queues */
1, /* minimum number of tx queues */
8, /* default number of tx queues */
@@ -315,7 +315,7 @@ static adapter_info_t ixgbe_X540_cap = {
128, /* default number of rx queues */
64, /* maximum number of rx groups */
1, /* minimum number of rx groups */
- 1, /* default number of rx groups */
+ 32, /* default number of rx groups */
128, /* maximum number of tx queues */
1, /* minimum number of tx queues */
8, /* default number of tx queues */
@@ -2049,6 +2049,7 @@ ixgbe_cbfunc(dev_info_t *dip, ddi_cb_action_t cbaction, void *cbarg,
void *arg1, void *arg2)
{
ixgbe_t *ixgbe = (ixgbe_t *)arg1;
+ int prev = ixgbe->intr_cnt;
switch (cbaction) {
/* IRM callback */
@@ -2062,7 +2063,8 @@ ixgbe_cbfunc(dev_info_t *dip, ddi_cb_action_t cbaction, void *cbarg,
if (ixgbe_intr_adjust(ixgbe, cbaction, count) !=
DDI_SUCCESS) {
ixgbe_error(ixgbe,
- "IRM CB: Failed to adjust interrupts");
+ "IRM CB: Failed to adjust interrupts [%d %d %d]",
+ cbaction, count, prev);
goto cb_fail;
}
break;
diff --git a/usr/src/uts/common/io/ksocket/ksocket.c b/usr/src/uts/common/io/ksocket/ksocket.c
index c71297f0de..311fae6719 100644
--- a/usr/src/uts/common/io/ksocket/ksocket.c
+++ b/usr/src/uts/common/io/ksocket/ksocket.c
@@ -22,7 +22,7 @@
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015, Joyent, Inc.
+ * Copyright 2017 Joyent, Inc.
* Copyright 2022 Garrett D'Amore
*/
diff --git a/usr/src/uts/common/io/ksyms.c b/usr/src/uts/common/io/ksyms.c
index 74e71ed7e8..759b524186 100644
--- a/usr/src/uts/common/io/ksyms.c
+++ b/usr/src/uts/common/io/ksyms.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
@@ -219,6 +220,14 @@ ksyms_open(dev_t *devp, int flag, int otyp, struct cred *cred)
char *addr;
void *hptr = NULL;
ksyms_buflist_hdr_t hdr;
+
+ /*
+ * This device should never be visible in a zone, but if it somehow
+ * does get created we refuse to allow the zone to use it.
+ */
+ if (crgetzoneid(cred) != GLOBAL_ZONEID)
+ return (EACCES);
+
bzero(&hdr, sizeof (struct ksyms_buflist_hdr));
list_create(&hdr.blist, PAGESIZE,
offsetof(ksyms_buflist_t, buflist_node));
diff --git a/usr/src/uts/common/io/mac/mac.c b/usr/src/uts/common/io/mac/mac.c
index 41d3ee5fe1..4ce359f87b 100644
--- a/usr/src/uts/common/io/mac/mac.c
+++ b/usr/src/uts/common/io/mac/mac.c
@@ -159,7 +159,7 @@
* perimeter) across a call to any other layer from the mac layer. The call to
* any other layer could be via mi_* entry points, classifier entry points into
* the driver or via upcall pointers into layers above. The mac perimeter may
- * be acquired or held only in the down direction, for e.g. when calling into
+ * be acquired or held only in the down direction, e.g. when calling into
* a mi_* driver enty point to provide atomicity of the operation.
*
* R8. Since it is not guaranteed (see R14) that drivers won't hold locks across
@@ -208,7 +208,7 @@
* number whenever the ring's stop routine is invoked.
* See comments in mac_rx_ring();
*
- * R17 Similarly mi_stop is another synchronization point and the driver must
+ * R17. Similarly mi_stop is another synchronization point and the driver must
* ensure that all upcalls are done and there won't be any future upcall
* before returning from mi_stop.
*
@@ -228,7 +228,7 @@
*
* cpu_lock -> mac_srs_g_lock -> srs_lock -> s_ring_lock [mac_walk_srs_and_bind]
*
- * i_dls_devnet_lock -> mac layer locks [dls_devnet_rename]
+ * mac perim -> i_dls_devnet_lock [dls_devnet_rename]
*
* Perimeters are ordered P1 -> P2 -> P3 from top to bottom in order of mac
* client to driver. In the case of clients that explictly use the mac provided
diff --git a/usr/src/uts/common/io/mac/mac_client.c b/usr/src/uts/common/io/mac/mac_client.c
index 94ad441a65..952b4c844b 100644
--- a/usr/src/uts/common/io/mac/mac_client.c
+++ b/usr/src/uts/common/io/mac/mac_client.c
@@ -3349,6 +3349,11 @@ mac_promisc_add(mac_client_handle_t mch, mac_client_promisc_type_t type,
mac_cb_info_t *mcbi;
int rc;
+ if ((flags & MAC_PROMISC_FLAGS_NO_COPY) &&
+ (flags & MAC_PROMISC_FLAGS_DO_FIXUPS)) {
+ return (EINVAL);
+ }
+
i_mac_perim_enter(mip);
if ((rc = mac_start((mac_handle_t)mip)) != 0) {
@@ -3395,6 +3400,7 @@ mac_promisc_add(mac_client_handle_t mch, mac_client_promisc_type_t type,
mpip->mpi_strip_vlan_tag =
((flags & MAC_PROMISC_FLAGS_VLAN_TAG_STRIP) != 0);
mpip->mpi_no_copy = ((flags & MAC_PROMISC_FLAGS_NO_COPY) != 0);
+ mpip->mpi_do_fixups = ((flags & MAC_PROMISC_FLAGS_DO_FIXUPS) != 0);
mcbi = &mip->mi_promisc_cb_info;
mutex_enter(mcbi->mcbi_lockp);
@@ -4105,13 +4111,33 @@ mac_promisc_dispatch_one(mac_promisc_impl_t *mpip, mblk_t *mp,
{
mblk_t *mp_next;
- if (!mpip->mpi_no_copy || mpip->mpi_strip_vlan_tag) {
+ if (!mpip->mpi_no_copy || mpip->mpi_strip_vlan_tag ||
+ (mpip->mpi_do_fixups && local)) {
mblk_t *mp_copy;
mp_copy = copymsg(mp);
if (mp_copy == NULL)
return;
+ /*
+ * The consumer has requested we emulate HW offloads
+ * for host-local packets.
+ */
+ if (mpip->mpi_do_fixups && local) {
+ /*
+ * Remember that copymsg() doesn't copy
+ * b_next, so we are only passing a single
+ * packet to mac_hw_emul(). Also keep in mind
+ * that mp_copy will become an mblk chain if
+ * the argument is an LSO message.
+ */
+ mac_hw_emul(&mp_copy, NULL, NULL,
+ MAC_HWCKSUM_EMUL | MAC_LSO_EMUL);
+
+ if (mp_copy == NULL)
+ return;
+ }
+
if (mpip->mpi_strip_vlan_tag) {
mp_copy = mac_strip_vlan_tag_chain(mp_copy);
if (mp_copy == NULL)
@@ -4320,16 +4346,15 @@ mac_info_get(const char *name, mac_info_t *minfop)
/*
* To get the capabilities that MAC layer cares about, such as rings, factory
* mac address, vnic or not, it should directly invoke this function. If the
- * link is part of a bridge, then the only "capability" it has is the inability
- * to do zero copy.
+ * link is part of a bridge, then the link is unable to do zero copy.
*/
boolean_t
i_mac_capab_get(mac_handle_t mh, mac_capab_t cap, void *cap_data)
{
mac_impl_t *mip = (mac_impl_t *)mh;
- if (mip->mi_bridge_link != NULL) {
- return (cap == MAC_CAPAB_NO_ZCOPY);
+ if (mip->mi_bridge_link != NULL && cap == MAC_CAPAB_NO_ZCOPY) {
+ return (B_TRUE);
} else if (mip->mi_callbacks->mc_callbacks & MC_GETCAPAB) {
boolean_t res;
diff --git a/usr/src/uts/common/io/mac/mac_protect.c b/usr/src/uts/common/io/mac/mac_protect.c
index 17959ac48d..cff1f884b9 100644
--- a/usr/src/uts/common/io/mac/mac_protect.c
+++ b/usr/src/uts/common/io/mac/mac_protect.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ * Copyright 2017, Joyent, Inc. All rights reserved.
*/
/*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
@@ -209,7 +209,7 @@ typedef struct slaac_addr {
} slaac_addr_t;
static void start_txn_cleanup_timer(mac_client_impl_t *);
-static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t);
+static boolean_t dynamic_method_set(mac_protect_t *, uint32_t);
#define BUMP_STAT(m, s) (m)->mci_misc_stat.mms_##s++
@@ -580,8 +580,7 @@ intercept_dhcpv4_outbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end)
if (get_dhcpv4_info(ipha, end, &dh4) != 0)
return (B_TRUE);
- /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
- if (allowed_ips_set(mrp, IPV4_VERSION))
+ if (!dynamic_method_set(&mrp->mrp_protect, MPT_DYN_DHCPV4))
return (B_FALSE);
if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 ||
@@ -1310,8 +1309,7 @@ intercept_dhcpv6_outbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end)
if (get_dhcpv6_info(ip6h, end, &dh6) != 0)
return (B_TRUE);
- /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
- if (allowed_ips_set(mrp, IPV6_VERSION))
+ if (!dynamic_method_set(&mrp->mrp_protect, MPT_DYN_DHCPV6))
return (B_FALSE);
/*
@@ -1517,6 +1515,10 @@ intercept_ra_inbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end,
{
struct nd_opt_hdr *opt;
int len, optlen;
+ mac_protect_t *protect = &MCIP_RESOURCE_PROPS(mcip)->mrp_protect;
+
+ if (!dynamic_method_set(protect, MPT_DYN_SLAAC))
+ return;
if (ip6h->ip6_hlim != 255) {
DTRACE_PROBE1(invalid__hoplimit, uint8_t, ip6h->ip6_hlim);
@@ -1755,6 +1757,7 @@ ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect,
if (*addr == INADDR_ANY)
return (B_TRUE);
+ /* If any specific addresses or subnets are allowed, check them */
for (i = 0; i < protect->mp_ipaddrcnt; i++) {
mac_ipaddr_t *v4addr = &protect->mp_ipaddrs[i];
@@ -1775,14 +1778,19 @@ ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect,
return (B_TRUE);
}
}
- return (protect->mp_ipaddrcnt == 0 ?
- check_dhcpv4_dyn_ip(mcip, *addr) : B_FALSE);
+
+ if (dynamic_method_set(protect, MPT_DYN_DHCPV4)) {
+ return (check_dhcpv4_dyn_ip(mcip, *addr));
+ }
+
+ return (B_FALSE);
}
static boolean_t
ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect,
in6_addr_t *addr)
{
+ boolean_t slaac_enabled, dhcpv6_enabled;
uint_t i;
/*
@@ -1793,7 +1801,7 @@ ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect,
IN6_ARE_ADDR_EQUAL(&mcip->mci_v6_local_addr, addr)))
return (B_TRUE);
-
+ /* If any specific addresses or subnets are allowed, check them */
for (i = 0; i < protect->mp_ipaddrcnt; i++) {
mac_ipaddr_t *v6addr = &protect->mp_ipaddrs[i];
@@ -1804,12 +1812,15 @@ ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect,
return (B_TRUE);
}
- if (protect->mp_ipaddrcnt == 0) {
- return (check_slaac_ip(mcip, addr) ||
- check_dhcpv6_dyn_ip(mcip, addr));
- } else {
- return (B_FALSE);
- }
+ slaac_enabled = dynamic_method_set(protect, MPT_DYN_SLAAC);
+ if (slaac_enabled && check_slaac_ip(mcip, addr))
+ return (B_TRUE);
+
+ dhcpv6_enabled = dynamic_method_set(protect, MPT_DYN_DHCPV6);
+ if (dhcpv6_enabled && check_dhcpv6_dyn_ip(mcip, addr))
+ return (B_TRUE);
+
+ return (B_FALSE);
}
/*
@@ -2025,6 +2036,9 @@ dhcpnospoof_check_cid(mac_protect_t *p, uchar_t *cid, uint_t cidlen)
bcmp(dcid->dc_id, cid, cidlen) == 0)
return (B_TRUE);
}
+
+ DTRACE_PROBE3(missing__cid, mac_protect_t *, p,
+ uchar_t *, cid, uint_t, cidlen);
return (B_FALSE);
}
@@ -2046,6 +2060,12 @@ dhcpnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *p,
bcmp(mcip->mci_unicast->ma_addr, dh4->chaddr, maclen) != 0) {
return (B_FALSE);
}
+
+ /* Everything after here is checking the Client Identifier */
+ if (p->mp_allcids == MPT_TRUE) {
+ return (B_TRUE);
+ }
+
if (get_dhcpv4_option(dh4, end, CD_CLIENT_ID, &cid, &optlen) == 0)
cidlen = optlen;
@@ -2082,6 +2102,11 @@ dhcpnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *p,
mtype == DHCPV6_MSG_RECONFIGURE)
return (B_TRUE);
+ /* Everything after here is checking the Client Identifier */
+ if (p->mp_allcids == MPT_TRUE) {
+ return (B_TRUE);
+ }
+
d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL,
DHCPV6_OPT_CLIENTID, &cidlen);
if (d6o == NULL || (uchar_t *)d6o + cidlen > end)
@@ -2159,7 +2184,6 @@ dhcpnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect,
return (0);
fail:
- /* increment dhcpnospoof stat here */
freemsg(nmp);
return (err);
}
@@ -2487,6 +2511,11 @@ mac_protect_validate(mac_resource_props_t *mrp)
if ((err = validate_cids(p)) != 0)
return (err);
+ if (p->mp_allcids != MPT_FALSE && p->mp_allcids != MPT_TRUE &&
+ p->mp_allcids != MPT_RESET) {
+ return (EINVAL);
+ }
+
return (0);
}
@@ -2554,6 +2583,16 @@ mac_protect_update(mac_resource_props_t *new, mac_resource_props_t *curr)
cp->mp_cidcnt = 0;
}
}
+ if (np->mp_allcids == MPT_RESET) {
+ cp->mp_allcids = MPT_FALSE;
+ } else if (np->mp_allcids != 0) {
+ cp->mp_allcids = MPT_TRUE;
+ }
+ if (np->mp_dynamic == MPT_RESET) {
+ cp->mp_dynamic = 0;
+ } else if (np->mp_dynamic != 0) {
+ cp->mp_dynamic = np->mp_dynamic;
+ }
}
void
@@ -2597,15 +2636,50 @@ mac_protect_fini(mac_client_impl_t *mcip)
}
static boolean_t
-allowed_ips_set(mac_resource_props_t *mrp, uint32_t af)
+dynamic_method_set(mac_protect_t *mpt, uint32_t method)
+{
+ if (mpt->mp_dynamic != 0) {
+ return ((mpt->mp_dynamic & method) != 0);
+ } else {
+ return (mpt->mp_ipaddrcnt == 0);
+ }
+}
+
+boolean_t
+mac_protect_check_addr(mac_client_handle_t mch, boolean_t isv6,
+ in6_addr_t *v6addr)
{
- int i;
+ mac_perim_handle_t perim;
+ mac_client_impl_t *mcip = (mac_client_impl_t *)mch;
+ mac_handle_t mh = (mac_handle_t)mcip->mci_mip;
- for (i = 0; i < mrp->mrp_protect.mp_ipaddrcnt; i++) {
- if (mrp->mrp_protect.mp_ipaddrs[i].ip_version == af)
- return (B_TRUE);
+ mac_perim_enter_by_mh(mh, &perim);
+
+ mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip);
+ mac_protect_t *p;
+ boolean_t allowed;
+
+ ASSERT(mrp != NULL);
+
+ p = &mrp->mrp_protect;
+
+ /* If mac protection/ipnospoof isn't enabled, return true */
+ if ((mrp->mrp_mask & MRP_PROTECT) == 0 ||
+ (p->mp_types & MPT_IPNOSPOOF) == 0) {
+ allowed = B_TRUE;
+ goto done;
}
- return (B_FALSE);
+
+ if (isv6) {
+ allowed = ipnospoof_check_v6(mcip, p, v6addr);
+ } else {
+ in_addr_t *v4addr = &V4_PART_OF_V6((*v6addr));
+ allowed = ipnospoof_check_v4(mcip, p, v4addr);
+ }
+
+done:
+ mac_perim_exit(perim);
+ return (allowed);
}
mac_protect_t *
diff --git a/usr/src/uts/common/io/mac/mac_stat.c b/usr/src/uts/common/io/mac/mac_stat.c
index dbb5c0a914..e1151565a6 100644
--- a/usr/src/uts/common/io/mac/mac_stat.c
+++ b/usr/src/uts/common/io/mac/mac_stat.c
@@ -391,8 +391,8 @@ i_mac_stat_create(void *handle, const char *modname, const char *statname,
kstat_t *ksp;
kstat_named_t *knp;
- ksp = kstat_create(modname, 0, statname, "net",
- KSTAT_TYPE_NAMED, count, 0);
+ ksp = kstat_create_zone(modname, 0, statname, "net",
+ KSTAT_TYPE_NAMED, count, 0, getzoneid());
if (ksp == NULL)
return (NULL);
@@ -949,9 +949,9 @@ mac_driver_stat_create(mac_impl_t *mip)
major_t major = getmajor(mip->mi_phy_dev);
count = MAC_MOD_NKSTAT + MAC_NKSTAT + mip->mi_type->mt_statcount;
- ksp = kstat_create((const char *)ddi_major_to_name(major),
+ ksp = kstat_create_zone((const char *)ddi_major_to_name(major),
getminor(mip->mi_phy_dev) - 1, MAC_KSTAT_NAME,
- MAC_KSTAT_CLASS, KSTAT_TYPE_NAMED, count, 0);
+ MAC_KSTAT_CLASS, KSTAT_TYPE_NAMED, count, 0, getzoneid());
if (ksp == NULL)
return;
diff --git a/usr/src/uts/common/io/mem.c b/usr/src/uts/common/io/mem.c
index cde4fab94f..f61de9d5eb 100644
--- a/usr/src/uts/common/io/mem.c
+++ b/usr/src/uts/common/io/mem.c
@@ -223,10 +223,19 @@ mmopen(dev_t *devp, int flag, int typ, struct cred *cred)
case M_NULL:
case M_ZERO:
case M_FULL:
+ /* standard devices */
+ break;
+
case M_MEM:
case M_KMEM:
case M_ALLKMEM:
- /* standard devices */
+ /*
+ * These devices should never be visible in a zone, but if they
+ * somehow do get created we refuse to allow the zone to use
+ * them.
+ */
+ if (crgetzoneid(cred) != GLOBAL_ZONEID)
+ return (EACCES);
break;
default:
diff --git a/usr/src/uts/common/io/mr_sas/mr_sas.conf b/usr/src/uts/common/io/mr_sas/mr_sas.conf
index cfda434e23..6c585c6a42 100644
--- a/usr/src/uts/common/io/mr_sas/mr_sas.conf
+++ b/usr/src/uts/common/io/mr_sas/mr_sas.conf
@@ -13,3 +13,11 @@
# Fast-Path specific flag. Default is "yes".
# mrsas-enable-fp="yes";
+flow_control="dmult" queue="qsort" tape="sctp";
+
+# MSI specific flag. To enable MSI modify the flag value to "yes"
+mrsas-enable-msi="yes";
+
+# Fast-Path specific flag. To enable Fast-Path modify the flag value to "yes"
+mrsas-enable-fp="yes";
+
diff --git a/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE b/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..187088ff34
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014, Thales UK Limited
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..cde8b65b37
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+NFAST CRYPTO ACCELERATOR DRIVER
diff --git a/usr/src/uts/common/io/nfp/autoversion.h b/usr/src/uts/common/io/nfp/autoversion.h
new file mode 100644
index 0000000000..b9021942b2
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/autoversion.h
@@ -0,0 +1,21 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+/* AUTOGENERATED - DO NOT EDIT */
+#ifndef AUTOVERSION_H
+#define AUTOVERSION_H
+
+#define VERSION_RELEASEMAJOR 2
+#define VERSION_RELEASEMINOR 26
+#define VERSION_RELEASEPATCH 40
+#define VERSION_NO "2.26.40cam999"
+#define VERSION_COMPNAME "nfdrv"
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/drvlist.c b/usr/src/uts/common/io/nfp/drvlist.c
new file mode 100644
index 0000000000..a04b1fd5b0
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/drvlist.c
@@ -0,0 +1,19 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+#include "nfp_common.h"
+#include "nfp_cmd.h"
+
+const nfpcmd_dev *nfp_drvlist[] = {
+ &i21285_cmddev,
+ &i21555_cmddev,
+ NULL
+};
+
diff --git a/usr/src/uts/common/io/nfp/hostif.c b/usr/src/uts/common/io/nfp/hostif.c
new file mode 100644
index 0000000000..684be703ea
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/hostif.c
@@ -0,0 +1,1192 @@
+/*
+
+hostif.c: nFast PCI driver for Solaris 2.5, 2.6, 2.7 and 2.8
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+06/05/1998 jsh Original solaris 2.6
+21/05/1999 jsh added support for solaris 2.5
+10/06/1999 jsh added support for solaris 2.7 (32 and 64 bit)
+??/??/2001 jsh added support for solaris 2.8 (32 and 64 bit)
+16/10/2001 jsh moved from nfast to new structure in nfdrv
+12/02/2002 jsh added high level interrupt support
+
+*/
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/map.h>
+#include <sys/debug.h>
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/open.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/pci.h>
+
+#include "nfp_common.h"
+#include "nfp_hostif.h"
+#include "nfp_osif.h"
+#include "nfp_cmd.h"
+
+#include "nfp.h"
+
+/* mapped memory attributes, no-swap endianess (done in higher level) */
+static struct ddi_device_acc_attr nosw_attr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_NEVERSWAP_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+/* dma attributes */
+static ddi_dma_attr_t dma_attrs = {
+ DMA_ATTR_V0, /* version number */
+ (uint64_t)0x0, /* low address */
+ (uint64_t)0xffffffff, /* high address */
+ (uint64_t)0xffffff, /* DMA counter max */
+ (uint64_t)0x1, /* alignment */
+ 0x0c, /* burst sizes */
+ 0x1, /* minimum transfer size */
+ (uint64_t)0x3ffffff, /* maximum transfer size */
+ (uint64_t)0x7fff, /* maximum segment size */
+ 1, /* no scatter/gather lists */
+ 1, /* granularity */
+ 0 /* DMA flags */
+};
+
+/*
+ * Debug message control
+ * Debug Levels:
+ * 0 = no messages
+ * 1 = Errors
+ * 2 = Subroutine calls & control flow
+ * 3 = I/O Data (verbose!)
+ * Can be set with adb or in the /etc/system file with
+ * "set nfp:nfp_debug=<value>"
+ */
+
+int nfp_debug= 1;
+
+static void *state_head; /* opaque handle top of state structs */
+
+static int nfp_open(dev_t *dev, int openflags, int otyp, cred_t *credp);
+static int nfp_close(dev_t dev, int openflags, int otyp, cred_t *credp);
+static int nfp_release_dev( dev_info_t *dip );
+
+static int nfp_read(dev_t dev, struct uio *uiop, cred_t *credp);
+static int nfp_write(dev_t dev, struct uio *uiop, cred_t *credp);
+static int nfp_strategy(struct buf *bp);
+
+static int nfp_ioctl(dev_t dev, int cmd, ioctlptr_t arg, int mode, cred_t *credp, int *rvalp);
+static int nfp_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp);
+
+static void nfp_wrtimeout (void *pdev);
+static void nfp_rdtimeout (void *pdev);
+
+static int nfp_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result);
+static int nfp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int nfp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+
+static void nfp_read_complete_final(nfp_dev *pdev, int ok);
+static void nfp_write_complete_final(nfp_dev *pdev, int ok);
+
+/* nfp file ops --------------------------------------------------- */
+
+static struct cb_ops nfp_cb_ops = {
+ nfp_open,
+ nfp_close,
+ nodev, /* no nfp_strategy */
+ nodev, /* no print routine */
+ nodev, /* no dump routine */
+ nfp_read,
+ nfp_write,
+ nfp_ioctl,
+ nodev, /* no devmap routine */
+ nodev, /* no mmap routine */
+ nodev, /* no segmap routine */
+ nfp_chpoll,
+ ddi_prop_op,
+ 0, /* not a STREAMS driver, no cb_str routine */
+ D_NEW | D_MP | EXTRA_CB_FLAGS, /* must be safe for multi-thread/multi-processor */
+ CB_REV,
+ nodev, /* aread */
+ nodev /* awrite */
+};
+
+static struct dev_ops nfp_ops = {
+ DEVO_REV, /* DEVO_REV indicated by manual */
+ 0, /* device reference count */
+ nfp_getinfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ nfp_attach,
+ nfp_detach,
+ nodev, /* device reset routine */
+ &nfp_cb_ops,
+ (struct bus_ops *)0, /* bus operations */
+};
+
+extern struct mod_ops mod_driverops;
+static struct modldrv modldrv = {
+ &mod_driverops,
+ NFP_DRVNAME,
+ &nfp_ops,
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, /* MODREV_1 indicated by manual */
+ (void *)&modldrv,
+ NULL, /* termination of list of linkage structures */
+};
+
+/* interface resource allocation */
+
+int nfp_alloc_pci_push( nfp_dev *pdev ) {
+ /* allocate resources needed for PCI Push,
+ * if not already allocated.
+ * return True if successful
+ */
+ nfp_err ret;
+ uint_t cookie_count;
+ size_t real_length;
+
+ if(!pdev->read_buf) {
+ /* allocate read buffer */
+ pdev->read_buf = kmem_zalloc( NFP_READBUF_SIZE, KM_NOSLEEP );
+ }
+ if(!pdev->read_buf) {
+ nfp_log( NFP_DBG1, "nfp_attach: kmem_zalloc read buffer failed");
+ pdev->read_buf = NULL;
+ return 0;
+ }
+
+ if(!pdev->rd_dma_ok) {
+ /* allocate dma handle for read buffer */
+ ret = ddi_dma_alloc_handle( pdev->dip,
+ &dma_attrs,
+ DDI_DMA_DONTWAIT,
+ NULL,
+ &pdev->read_dma_handle );
+ if( ret != DDI_SUCCESS ) {
+ nfp_log( NFP_DBG1,
+ "nfp_alloc_pci_push: ddi_dma_alloc_handle failed (%d)",
+ ret );
+ return 0;
+ }
+
+ /* Allocate the memory for dma transfers */
+ ret = ddi_dma_mem_alloc(pdev->read_dma_handle, NFP_READBUF_SIZE, &nosw_attr,
+ DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
+ (caddr_t*)&pdev->read_buf, &real_length, &pdev->acchandle);
+ if (ret != DDI_SUCCESS) {
+ nfp_log( NFP_DBG1, "nfp_alloc_pci_push: ddi_dma_mem_alloc failed (%d)", ret);
+ ddi_dma_free_handle( &pdev->read_dma_handle );
+ return 0;
+ }
+
+ ret = ddi_dma_addr_bind_handle( pdev->read_dma_handle,
+ NULL, /* kernel address space */
+ (caddr_t)pdev->read_buf, real_length,
+ DDI_DMA_READ | DDI_DMA_CONSISTENT, /* dma flags */
+ DDI_DMA_DONTWAIT, NULL,
+ &pdev->read_dma_cookie, &cookie_count );
+ if( ret != DDI_DMA_MAPPED ) {
+ nfp_log( NFP_DBG1,
+ "nfp_alloc_pci_push: ddi_dma_addr_bind_handle failed (%d)",
+ ret);
+ ddi_dma_mem_free(&pdev->acchandle);
+ ddi_dma_free_handle( &pdev->read_dma_handle );
+ return 0;
+ }
+ if( cookie_count > 1 ) {
+ nfp_log( NFP_DBG1,
+ "nfp_alloc_pci_push: error:"
+ " ddi_dma_addr_bind_handle wants %d transfers",
+ cookie_count);
+ ddi_dma_mem_free(&pdev->acchandle);
+ (void) ddi_dma_unbind_handle( pdev->read_dma_handle );
+ ddi_dma_free_handle( &pdev->read_dma_handle );
+ return 0;
+ }
+ pdev->rd_dma_ok = 1;
+ }
+ return pdev->rd_dma_ok;
+}
+
+void nfp_free_pci_push( nfp_dev *pdev ) {
+ /* free resources allocated to PCI Push */
+ if( pdev->rd_dma_ok ) {
+ (void) ddi_dma_sync(pdev->read_dma_handle,0,0,DDI_DMA_SYNC_FORKERNEL);
+ ddi_dma_mem_free(&pdev->acchandle);
+ (void) ddi_dma_unbind_handle( pdev->read_dma_handle );
+ ddi_dma_free_handle( &pdev->read_dma_handle );
+ pdev->rd_dma_ok = 0;
+ }
+ if( pdev->read_buf ) {
+ kmem_free( pdev->read_buf, NFP_READBUF_SIZE );
+ pdev->read_buf = NULL;
+ }
+}
+
+/* include definition of nfp_set_ifvers() */
+#define nfp_ifvers NFDEV_IF_PCI_PUSH
+#include "nfp_ifvers.c"
+#undef nfp_ifvers
+
+/*--------------------*/
+/* nfp_isr */
+/*--------------------*/
+
+static u_int nfp_isr( char *pdev_in ) {
+ /* LINTED: alignment */
+ nfp_dev *pdev= (nfp_dev *)pdev_in;
+ nfp_err ne;
+ int handled;
+
+ nfp_log( NFP_DBG3, "nfp_isr: entered");
+
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_isr: cannot find dev");
+ return DDI_INTR_UNCLAIMED;
+ }
+
+ /* The isr needs to be mutex'ed - an SMP can call us while we're still
+ * running!
+ */
+ mutex_enter(&pdev->low_mutex);
+ ne= pdev->cmddev->isr( pdev->common.cmdctx, &handled );
+ mutex_exit(&pdev->low_mutex);
+
+ if( !ne && handled )
+ return DDI_INTR_CLAIMED;
+ if (ne)
+ nfp_log( NFP_DBG1, "nfp_isr: failed");
+ else
+ nfp_log( NFP_DBG3, "nfp_isr: unclaimed");
+ return DDI_INTR_UNCLAIMED;
+}
+
+static u_int nfp_soft_isr( char *pdev_in ) {
+ /* LINTED: alignment */
+ nfp_dev *pdev= (nfp_dev *)pdev_in;
+ int rd, wr;
+
+ nfp_log( NFP_DBG3, "nfp_soft_isr: entered");
+
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_soft_isr: cannot find dev");
+ return DDI_INTR_UNCLAIMED;
+ }
+ rd= wr= 0;
+
+ mutex_enter(&pdev->high_mutex);
+ if(pdev->high_read) {
+ pdev->high_read= 0;
+ mutex_exit(&pdev->high_mutex);
+ rd= 1;
+ }
+ if(pdev->high_write) {
+ pdev->high_write= 0;
+ wr= 1;
+ }
+ mutex_exit(&pdev->high_mutex);
+
+ if(rd) {
+ nfp_log( NFP_DBG3, "nfp_soft_isr: read done");
+ nfp_read_complete_final(pdev, pdev->rd_ok);
+ }
+ if(wr) {
+ nfp_log( NFP_DBG3, "nfp_soft_isr: write done");
+ nfp_write_complete_final(pdev, pdev->wr_ok);
+ }
+ if( rd || wr )
+ return DDI_INTR_CLAIMED;
+
+ nfp_log( NFP_DBG2, "nfp_isr: unclaimed");
+ return DDI_INTR_UNCLAIMED;
+}
+
+
+/*-------------------------*/
+/* nfp_read */
+/*-------------------------*/
+
+void nfp_read_complete(nfp_dev *pdev, int ok) {
+ nfp_log( NFP_DBG2,"nfp_read_complete: entering");
+
+ if(pdev->high_intr) {
+ nfp_log(NFP_DBG2, "nfp_read_complete: high_intr");
+ mutex_enter(&pdev->high_mutex);
+ nfp_log(NFP_DBG3, "nfp_read_complete: high_mutex entered");
+ if(pdev->high_read)
+ nfp_log(NFP_DBG1, "nfp_read_complete: high_read allread set!");
+ pdev->high_read= 1;
+ pdev->rd_ok= ok;
+ nfp_log(NFP_DBG3, "nfp_read_complete: exiting high_mutex");
+ mutex_exit(&pdev->high_mutex);
+ ddi_trigger_softintr(pdev->soft_int_id);
+ } else
+ nfp_read_complete_final( pdev, ok );
+ nfp_log( NFP_DBG2,"nfp_read_complete: exiting");
+}
+
+static void nfp_read_complete_final(nfp_dev *pdev, int ok) {
+ nfp_log( NFP_DBG2,"nfp_read_complete_final: entering");
+ if(pdev->rdtimeout)
+ (void) untimeout(pdev->rdtimeout);
+ if(!pdev->rd_outstanding) {
+ nfp_log( NFP_DBG1,"nfp_read_complete_final: !pdev->rd_outstanding");
+ }
+ nfp_log( NFP_DBG2,"nfp_read_complete_final: pdev->rd_outstanding=0, ok %d", ok);
+ mutex_enter(&pdev->isr_mutex);
+ pdev->rd_outstanding= 0;
+ pdev->rd_ready= 1;
+ pdev->rd_ok= ok;
+ cv_broadcast(&pdev->rd_cv);
+ mutex_exit(&pdev->isr_mutex);
+ pollwakeup (&pdev->pollhead, POLLRDNORM);
+ nfp_log( NFP_DBG2,"nfp_read_complete_final: exiting");
+}
+
+static void nfp_rdtimeout( void *pdev_in )
+{
+ nfp_dev *pdev= (nfp_dev *)pdev_in;
+
+ nfp_log( NFP_DBG1, "nfp_rdtimeout: read timed out");
+
+ if (!pdev) {
+ nfp_log( NFP_DBG1, "nfp_rdtimeout: NULL pdev." );
+ return;
+ }
+ pdev->rdtimeout= 0;
+ nfp_read_complete_final(pdev, 0);
+}
+
+/* ARGSUSED */
+static int nfp_read(dev_t dev, struct uio *uiop, cred_t *credp) {
+ int ret;
+ nfp_log( NFP_DBG2, "nfp_read: entered" );
+ if (ddi_get_soft_state(state_head, getminor(dev)) != NULL) {
+ nfp_log( NFP_DBG1, "nfp_read: unable to get nfp_dev");
+ return (ENODEV);
+ }
+ nfp_log( NFP_DBG2, "nfp_read: about to physio." );
+ ret = physio(nfp_strategy, (struct buf *)0, dev, B_READ, minphys, uiop );
+ if(ret)
+ nfp_log( NFP_DBG1, "nfp_read: physio returned %x.", ret );
+ return ret;
+}
+
+/*-------------------------*/
+/* nfp_write */
+/*-------------------------*/
+
+void nfp_write_complete( nfp_dev *pdev, int ok) {
+ nfp_log( NFP_DBG2,"nfp_write_complete: entering");
+
+ if(pdev->high_intr) {
+ mutex_enter(&pdev->high_mutex);
+ if(pdev->high_write)
+ nfp_log(NFP_DBG1, "nfp_write_complete: high_write allread set!");
+ pdev->high_write= 1;
+ pdev->wr_ok= ok;
+ mutex_exit(&pdev->high_mutex);
+ ddi_trigger_softintr(pdev->soft_int_id);
+ } else
+ nfp_write_complete_final( pdev, ok );
+ nfp_log( NFP_DBG2,"nfp_write_complete: exiting");
+}
+
+static void nfp_write_complete_final( nfp_dev *pdev, int ok) {
+ struct buf *local_wr_bp;
+ nfp_log( NFP_DBG2,"nfp_write_complete_final: entering");
+ if(pdev->wrtimeout)
+ (void) untimeout(pdev->wrtimeout);
+
+ if (!pdev->wr_bp) {
+ nfp_log( NFP_DBG2, "nfp_write_complete_final: write: wr_bp == NULL." );
+ return;
+ }
+
+ bp_mapout(pdev->wr_bp);
+ pdev->wr_bp->b_resid = ok ? 0 : pdev->wr_bp->b_bcount;
+ /* Make sure we set wr_ready before calling biodone to avoid a race */
+ pdev->wr_ready = 1;
+ bioerror(pdev->wr_bp, ok ? 0 : ENXIO);
+ local_wr_bp = pdev->wr_bp;
+ pdev->wr_bp = 0;
+ biodone(local_wr_bp);
+ nfp_log( NFP_DBG2, "nfp_write_complete_final: isr_mutex extited");
+ pollwakeup (&pdev->pollhead, POLLWRNORM);
+
+ nfp_log( NFP_DBG2, "nfp_write_complete_final: leaving");
+}
+
+static void nfp_wrtimeout( void *pdev_in )
+{
+ nfp_dev *pdev= (nfp_dev *)pdev_in;
+
+ nfp_log( NFP_DBG1, "nfp_wrtimeout: write timed out");
+
+ if (!pdev) {
+ nfp_log( NFP_DBG1, "nfp_wrtimeout: NULL pdev." );
+ return;
+ }
+ pdev->wrtimeout= 0;
+ nfp_write_complete_final(pdev, 0);
+}
+
+/* ARGSUSED */
+static int nfp_write(dev_t dev, struct uio *uiop, cred_t *credp) {
+ int ret;
+ nfp_log( NFP_DBG2, "nfp_write: entered." );
+ if (ddi_get_soft_state(state_head, getminor(dev)) == NULL) {
+ nfp_log( NFP_DBG1, "nfp_chread: unable to get nfp_dev.");
+ return (ENODEV);
+ }
+ nfp_log( NFP_DBG2, "nfp_write: about to physio." );
+ ret = physio(nfp_strategy, (struct buf *)0, dev, B_WRITE, minphys, uiop );
+ if(ret)
+ nfp_log( NFP_DBG1, "nfp_write: physio returned %x.", ret );
+ return ret;
+}
+
+/*-------------------------*/
+/* nfp_strategy */
+/*-------------------------*/
+
+#define NFP_STRAT_ERR(thebp,err,txt) \
+ nfp_log( NFP_DBG1, "nfp_strategy: " txt ".\n"); \
+ (thebp)->b_resid = (thebp)->b_bcount; \
+ bioerror ((thebp), err); \
+ biodone ((thebp));
+
+static int nfp_strategy(struct buf *bp) {
+ register struct nfp_dev *pdev;
+ nfp_err ne;
+
+ nfp_log( NFP_DBG2, "nfp_strategy: entered." );
+ if (!(pdev = ddi_get_soft_state(state_head, getminor(bp->b_edev)))) {
+ NFP_STRAT_ERR (bp, ENXIO, "unable to get nfp_dev");
+ return (0);
+ }
+
+ if (bp->b_flags & B_READ) {
+ int count;
+ /* read */
+ if (!pdev->rd_ready) {
+ NFP_STRAT_ERR (bp,ENXIO,"read called when not ready");
+ return (0);
+ }
+ pdev->rd_ready=0;
+ pdev->rd_pending = 0;
+ if( !pdev->rd_ok) {
+ NFP_STRAT_ERR (bp,ENXIO,"read failed");
+ return (0);
+ }
+ /* copy data from module */
+ if(pdev->ifvers >= NFDEV_IF_PCI_PUSH) {
+ nfp_log( NFP_DBG3, "nfp_strategy: copying kernel read buffer");
+ if( ddi_dma_sync(pdev->read_dma_handle,0,0,DDI_DMA_SYNC_FORKERNEL) != DDI_SUCCESS )
+ {
+ NFP_STRAT_ERR(bp,ENXIO,"ddi_dma_sync(read_dma_handle) failed");
+ return (0);
+ }
+ /* LINTED: alignment */
+ count= *(unsigned int *)(pdev->read_buf+4);
+ count= FROM_LE32_MEM(&count);
+ nfp_log( NFP_DBG3, "nfp_strategy: read count %d", count);
+ if(count<0 || count>bp->b_bcount) {
+ NFP_STRAT_ERR(bp,ENXIO,"bad read byte count from device");
+ nfp_log( NFP_DBG1, "nfp_strategy: bad read byte count (%d) from device", count);
+ return (0);
+ }
+ bp_mapin (bp);
+ bcopy( pdev->read_buf + 8, bp->b_un.b_addr, count );
+ bp_mapout (bp);
+ } else {
+ bp_mapin (bp);
+ ne= pdev->cmddev->read_block( bp->b_un.b_addr, bp->b_bcount, pdev->common.cmdctx, &count );
+ bp_mapout (bp);
+ if( ne != NFP_SUCCESS) {
+ NFP_STRAT_ERR (bp,nfp_oserr(ne),"read_block failed");
+ return (0);
+ }
+ }
+ bioerror(bp, 0);
+ bp->b_resid = 0;
+ biodone (bp);
+ } else {
+ /* write */
+ if (!pdev->wr_ready) {
+ NFP_STRAT_ERR (bp,ENXIO,"write called when not ready");
+ return (0);
+ }
+ if (pdev->wr_bp) {
+ NFP_STRAT_ERR (bp,ENXIO,"wr_bp != NULL");
+ return (0);
+ }
+ pdev->wrtimeout= timeout(nfp_wrtimeout, (caddr_t)pdev, NFP_TIMEOUT_SEC * drv_usectohz(1000000));
+ pdev->wr_bp = bp;
+ pdev->wr_ready = 0;
+ bp_mapin (bp);
+ ne= pdev->cmddev->write_block( bp->b_un.b_addr, bp->b_bcount, pdev->common.cmdctx);
+ if( ne != NFP_SUCCESS ) {
+ bp_mapout (bp);
+ (void) untimeout(pdev->wrtimeout);
+ pdev->wr_bp = 0;
+ pdev->wr_ready = 1;
+ NFP_STRAT_ERR (bp,nfp_oserr(ne),"write failed");
+ return (0);
+ }
+ }
+ nfp_log( NFP_DBG2, "nfp_strategy: leaving");
+
+ return (0);
+}
+
+
+/*--------------------*/
+/* poll / select */
+/*--------------------*/
+
+static int nfp_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp) {
+ nfp_dev *pdev;
+ short revents;
+
+ if (!(pdev = ddi_get_soft_state(state_head, getminor(dev)))) {
+ nfp_log( NFP_DBG1, "nfp_chpoll: unable to get nfp_dev");
+ *reventsp=0;
+ return (0);
+ }
+ nfp_log( NFP_DBG2, "nfp_chpoll: entered %x", events);
+
+ revents=0;
+ if (events&POLLWRNORM) {
+ if (pdev->wr_ready) {
+ nfp_log( NFP_DBG2, "nfp_chpoll: write ready");
+ revents|=POLLWRNORM;
+ }
+ }
+
+ if (events&POLLRDNORM) {
+ if (pdev->rd_ready) {
+ nfp_log( NFP_DBG2, "nfp_chpoll: read ready");
+ revents|=POLLRDNORM;
+ }
+ }
+
+ if (!revents && !anyyet) {
+ *phpp=&pdev->pollhead;
+ }
+ *reventsp=revents;
+
+ nfp_log( NFP_DBG2, "nfp_chpoll: leaving");
+ return (0);
+}
+
+
+/*--------------------*/
+/* ioctl */
+/*--------------------*/
+
+/* ARGSUSED */
+static int nfp_ioctl(dev_t dev, int cmd, ioctlptr_t arg, int mode, cred_t *credp, int *rvalp) {
+ register struct nfp_dev *pdev;
+
+ nfp_log( NFP_DBG2, "nfp_ioctl: entered." );
+
+ if (!(pdev = ddi_get_soft_state(state_head, getminor(dev)))) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: unable to get nfp dev.");
+ return (ENXIO);
+ }
+
+ switch (cmd) {
+ case NFDEV_IOCTL_ENQUIRY:
+ {
+ long *outp;
+ int outlen;
+ nfdev_enquiry_str enq_data;
+
+ enq_data.busno = (unsigned int)-1;
+ enq_data.slotno = (unsigned char)-1;
+
+ /* get our bus and slot num */
+ if (ddi_getlongprop (DDI_DEV_T_NONE,
+ pdev->dip, 0, "reg",
+ (caddr_t)&outp, &outlen) != DDI_PROP_NOT_FOUND) {
+ nfp_log( NFP_DBG2, "ddi_getlongprop('reg') ok." );
+ if( outlen > 0 ) {
+ enq_data.busno = ((*outp)>>16) & 0xff;
+ enq_data.slotno = ((*outp)>>11) & 0x1f;
+ nfp_log( NFP_DBG2, "busno %d, slotno %d.",
+ enq_data.busno, enq_data.slotno );
+ }
+ } else
+ nfp_log( NFP_DBG1, "ddi_getlongprop('reg') failed." );
+
+ if( ddi_copyout( (char *)&enq_data, (void *)arg, sizeof(enq_data), mode ) != 0 ) {
+ nfp_log( NFP_DBG1, "ddi_copyout() failed." );
+ return EFAULT;
+ }
+ }
+ break;
+
+ case NFDEV_IOCTL_ENSUREREADING:
+ {
+ unsigned int addr, len;
+ nfp_err ret;
+ if( ddi_copyin( (void *)arg, (char *)&len, sizeof(unsigned int), mode ) != 0 ) {
+ nfp_log( NFP_DBG1, "ddi_copyin() failed." );
+ return (EFAULT);
+ }
+ /* signal a read to the module */
+ nfp_log( NFP_DBG2, "nfp_ioctl: signalling read request to module, len = %x.", len );
+ if (len>8192) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: len >8192 = %x.", len );
+ return EINVAL;
+ }
+ if (pdev->rd_outstanding==1) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: not about to call read with read outstanding.");
+ return EIO;
+ }
+
+ addr= 0;
+ if(pdev->ifvers >= NFDEV_IF_PCI_PUSH) {
+ if( len > NFP_READBUF_SIZE ) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: len > NFP_READBUF_SIZE = %x.", len );
+ return EINVAL;
+ }
+ addr= pdev->read_dma_cookie.dmac_address;
+ }
+
+ pdev->rd_outstanding = 1;
+ nfp_log( NFP_DBG2,"nfp_ioctl: pdev->rd_outstanding=1");
+
+ /* setup timeout timer */
+ pdev->rdtimeout= timeout(nfp_rdtimeout, (caddr_t)pdev, NFP_TIMEOUT_SEC * drv_usectohz(1000000));
+
+ nfp_log( NFP_DBG2, "nfp_ioctl: read request");
+ ret = pdev->cmddev->ensure_reading(addr, len, pdev->common.cmdctx);
+ if ( ret != NFP_SUCCESS ) {
+ (void) untimeout(pdev->rdtimeout);
+ pdev->rdtimeout = 0;
+ pdev->rd_outstanding = 0;
+ nfp_log( NFP_DBG1, "nfp_ioctl : cmddev->ensure_reading failed ");
+ return nfp_oserr( ret );
+ }
+ }
+ break;
+
+ case NFDEV_IOCTL_PCI_IFVERS:
+ {
+ int vers;
+
+ nfp_log( NFP_DBG2, "nfp_ioctl: NFDEV_IOCTL_PCI_IFVERS");
+
+ if( ddi_copyin( (void *)arg, (char *)&vers, sizeof(vers), mode ) != 0 ) {
+ nfp_log( NFP_DBG1, "ddi_copyin() failed." );
+ return (EFAULT);
+ }
+
+ if( pdev->rd_outstanding ) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: can't set ifvers %d as read outstanding", vers);
+ return EIO;
+ }
+
+ nfp_set_ifvers(pdev, vers);
+ if( pdev->ifvers != vers ) {
+ nfp_log( NFP_DBG1, "nfp_ioctl: can't set ifvers %d", vers);
+ return EIO;
+ }
+ }
+ break;
+
+ case NFDEV_IOCTL_STATS:
+ {
+ if( ddi_copyout( (char *)&(pdev->common.stats),
+ (void *)arg,
+ sizeof(nfdev_stats_str),
+ mode ) != 0 ) {
+ nfp_log( NFP_DBG1, "ddi_copyout() failed." );
+ return EFAULT;
+ }
+ }
+ break;
+
+ default:
+ nfp_log( NFP_DBG1, "nfp_ioctl: unknown ioctl." );
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/*-------------------------*/
+/* nfp_open */
+/*-------------------------*/
+
+/* ARGSUSED */
+int nfp_open(dev_t *dev, int openflags, int otyp, cred_t *credp)
+{
+ nfp_err ret;
+ register struct nfp_dev *pdev;
+
+ nfp_log( NFP_DBG2, "entered nfp_open." );
+
+ pdev = (nfp_dev *)ddi_get_soft_state(state_head, getminor(*dev));
+
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_open: unable to get nfp dev.");
+ return (ENODEV);
+ }
+
+ if( otyp != OTYP_CHR ) {
+ nfp_log( NFP_DBG1, "nfp_open: not opened as character device");
+ return (EINVAL);
+ }
+
+ mutex_enter(&pdev->busy_mutex);
+
+ if (pdev->busy) {
+ mutex_exit(&pdev->busy_mutex);
+ nfp_log( NFP_DBG1, "nfp_open: device busy");
+ return EBUSY;
+ }
+ pdev->busy= 1;
+ mutex_exit(&pdev->busy_mutex);
+
+ /* use oldest possible interface until told otherwise */
+ pdev->ifvers= NFDEV_IF_STANDARD;
+ nfp_log( NFP_DBG3, "nfp_open: setting ifvers %d", pdev->ifvers);
+ pdev->rd_ready= 0; /* drop any old data */
+
+ ret = pdev->cmddev->open(pdev->common.cmdctx);
+ if( ret != NFP_SUCCESS ) {
+ nfp_log( NFP_DBG1, "nfp_open : cmddev->open failed ");
+ return nfp_oserr( ret );
+ }
+
+ nfp_log( NFP_DBG2, "nfp_open: done");
+
+ return 0;
+}
+
+/*--------------------*/
+/* nfp_close */
+/*--------------------*/
+
+/* ARGSUSED */
+static int nfp_close(dev_t dev, int openflags, int otyp, cred_t *credp) {
+ nfp_dev *pdev;
+ nfp_err ret;
+
+ nfp_log( NFP_DBG2, "nfp_close: entered");
+
+ pdev = (struct nfp_dev *)ddi_get_soft_state(state_head, getminor(dev));
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_close: cannot find dev.");
+ return ENODEV;
+ }
+
+ mutex_enter(&pdev->isr_mutex);
+ if(pdev->rd_outstanding) {
+ int lbolt, err;
+ nfp_get_lbolt(&lbolt, err);
+ if(!err)
+ (void) cv_timedwait(&pdev->rd_cv, &pdev->isr_mutex, lbolt + (NFP_TIMEOUT_SEC * drv_usectohz(1000000)) );
+ }
+ mutex_exit(&pdev->isr_mutex);
+ ret = pdev->cmddev->close(pdev->common.cmdctx);
+ if (ret != NFP_SUCCESS ) {
+ nfp_log( NFP_DBG1, " nfp_close : cmddev->close failed");
+ return nfp_oserr( ret );
+ }
+
+ mutex_enter(&pdev->busy_mutex);
+ pdev->busy= 0;
+ mutex_exit(&pdev->busy_mutex);
+
+ return 0;
+}
+
+/****************************************************************************
+
+ nfp driver config
+
+ ****************************************************************************/
+
+/*-------------------------*/
+/* nfp_getinfo */
+/*-------------------------*/
+
+/* ARGSUSED */
+static int nfp_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) {
+ int error;
+ nfp_dev *pdev;
+
+ nfp_log( NFP_DBG2, "nfp_getinfo: entered" );
+
+ pdev = (struct nfp_dev *)ddi_get_soft_state(state_head, getminor((dev_t)arg));
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_close: cannot find dev.");
+ return ENODEV;
+ }
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if (pdev == NULL) {
+ *result = NULL;
+ error = DDI_FAILURE;
+ } else {
+ /*
+ * don't need to use a MUTEX even though we are
+ * accessing our instance structure; dev->dip
+ * never changes.
+ */
+ *result = pdev->dip;
+ error = DDI_SUCCESS;
+ }
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(uintptr_t)getminor((dev_t)arg);
+ error = DDI_SUCCESS;
+ break;
+ default:
+ *result = NULL;
+ error = DDI_FAILURE;
+ }
+
+ nfp_log( NFP_DBG2, "nfp_getinfo: leaving." );
+ return (error);
+}
+
+/*-------------------------*/
+/* nfp_release */
+/*-------------------------*/
+
+static int nfp_release_dev( dev_info_t *dip ) {
+ nfp_dev *pdev;
+ int instance, i;
+ nfp_err ret;
+
+ nfp_log( NFP_DBG2, "nfp_release_dev: entering" );
+
+ instance = ddi_get_instance(dip);
+ pdev = (struct nfp_dev *)ddi_get_soft_state(state_head, instance);
+ if (pdev) {
+ nfp_log( NFP_DBG3, "nfp_release_dev: removing device" );
+
+ nfp_free_pci_push(pdev);
+
+ if( pdev->cmddev ) {
+ nfp_log( NFP_DBG3, "nfp_release_dev: destroying cmd dev" );
+ ret = pdev->cmddev->destroy(pdev->common.cmdctx);
+ if (ret != NFP_SUCCESS) {
+ nfp_log( NFP_DBG1, " nfp_release_dev : cmddev->destroy failed ");
+ return nfp_oserr( ret );
+ }
+ }
+
+ if(pdev->high_iblock_cookie) {
+ nfp_log( NFP_DBG3, "nfp_release_dev: removing high and soft irq" );
+ ddi_remove_softintr(pdev->soft_int_id);
+ ddi_remove_intr(pdev->dip, 0, pdev->high_iblock_cookie);
+ mutex_destroy( &pdev->busy_mutex );
+ cv_destroy( &pdev->rd_cv );
+ mutex_destroy( &pdev->isr_mutex );
+ mutex_destroy( &pdev->high_mutex );
+ } else if(pdev->iblock_cookie) {
+ nfp_log( NFP_DBG3, "nfp_release_dev: removing irq" );
+ ddi_remove_intr(pdev->dip, 0, pdev->iblock_cookie);
+ mutex_destroy( &pdev->busy_mutex );
+ cv_destroy( &pdev->rd_cv );
+ mutex_destroy( &pdev->isr_mutex );
+ }
+ if(pdev->low_iblock_cookie) {
+ ddi_remove_intr(pdev->dip, 0, pdev->low_iblock_cookie);
+ mutex_destroy( &pdev->low_mutex);
+ }
+
+ for(i=0;i<6;i++) {
+ if( pdev->common.extra[i] ) {
+ nfp_log( NFP_DBG3, "nfp_release_dev: unmapping BAR %d", i );
+ ddi_regs_map_free ((ddi_acc_handle_t *)&pdev->common.extra[i]);
+ }
+ }
+
+ ddi_remove_minor_node(dip, NULL);
+
+ if (pdev->conf_handle)
+ pci_config_teardown( &pdev->conf_handle );
+
+ ddi_soft_state_free(state_head, instance);
+ }
+ nfp_log( NFP_DBG2, "nfp_release: finished" );
+
+ return DDI_SUCCESS;
+}
+
+
+/*-------------------------*/
+/* nfp_attach */
+/*-------------------------*/
+
+static int nfp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) {
+ int instance;
+ nfp_dev *pdev = NULL;
+ int intres;
+ uint16_t device, vendor, sub_device, sub_vendor;
+ long *outp;
+ nfpcmd_dev const *cmddev;
+ int index, i;
+ nfp_err ret;
+
+ nfp_log( NFP_DBG2, "nfp_attach: entered." );
+
+ if (cmd != DDI_ATTACH) {
+ nfp_log( NFP_DBG1, "nfp_attach: bad command." );
+ goto bailout;
+ }
+
+ instance = ddi_get_instance(dip);
+
+ if (ddi_soft_state_zalloc(state_head, instance) != 0) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_soft_state_zalloc() failed." );
+ goto bailout;
+ }
+
+ pdev = (struct nfp_dev *)ddi_get_soft_state(state_head, instance);
+ if( !pdev ) {
+ nfp_log( NFP_DBG1, "nfp_attach: cannot find dev.");
+ return ENODEV;
+ }
+ pdev->dip = dip;
+
+ /* map in pci config registers */
+ if (pci_config_setup(dip, &pdev->conf_handle)) {
+ nfp_log( NFP_DBG1, "nfp_attach: pci_config_setup() failed." );
+ goto bailout;
+ }
+
+ /* find out what we have got */
+ vendor= PCI_CONFIG_GET16( pdev->conf_handle, PCI_CONF_VENID );
+ device = PCI_CONFIG_GET16( pdev->conf_handle, PCI_CONF_DEVID );
+ sub_vendor = PCI_CONFIG_GET16( pdev->conf_handle, PCI_CONF_SUBVENID );
+ sub_device = PCI_CONFIG_GET16( pdev->conf_handle, PCI_CONF_SUBSYSID );
+
+ index= 0;
+ while( (cmddev = nfp_drvlist[index++]) != NULL ) {
+ if( cmddev->vendorid == vendor &&
+ cmddev->deviceid == device &&
+ cmddev->sub_vendorid == sub_vendor &&
+ cmddev->sub_deviceid == sub_device )
+ break;
+ }
+ if( !cmddev ) {
+ nfp_log( NFP_DBG1, "nfp_attach: unknonw device." );
+ goto bailout;
+ }
+
+ /* map BARs */
+ for( i=0; i<6; i++ ) {
+ if( cmddev->bar_sizes[i] ) {
+ off_t size;
+ if( ddi_dev_regsize(dip, i+1, &size) != DDI_SUCCESS) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_dev_regsize() failed for BAR %d", i );
+ goto bailout;
+ }
+ if( size < (cmddev->bar_sizes[i] & ~NFP_MEMBAR_MASK) ) {
+ nfp_log( NFP_DBG1, "nfp_attach: BAR %d too small %x (%x)", i, size, (cmddev->bar_sizes[i] & ~0xF) );
+ goto bailout;
+ }
+ if (ddi_regs_map_setup(dip, i+1, (caddr_t *)&pdev->common.bar[i],
+ 0, cmddev->bar_sizes[i] & ~NFP_MEMBAR_MASK, &nosw_attr, (ddi_acc_handle_t *)&pdev->common.extra[i] )) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_regs_map_setup() failed for BAR %d", i );
+ goto bailout;
+ }
+ nfp_log( NFP_DBG3, "nfp_attach: BAR[%d] mapped to %x (%x)", i, pdev->common.bar[i], size );
+ }
+ }
+
+ pdev->read_buf = NULL;
+ pdev->rd_dma_ok = 0;
+
+ /* attach to minor node */
+ if (ddi_create_minor_node(dip, "nfp", S_IFCHR, instance, (char *)cmddev->name, 0) == DDI_FAILURE) {
+ ddi_remove_minor_node(dip, NULL);
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_create_minor_node() failed." );
+ goto bailout;
+ }
+
+ pdev->wr_ready = 1;
+ pdev->rd_ready = 0;
+ pdev->rd_pending = 0;
+ pdev->rd_outstanding = 0;
+ pdev->busy=0;
+ pdev->cmddev= cmddev;
+
+ ret = pdev->cmddev->create(&pdev->common);
+ if( ret != NFP_SUCCESS) {
+ nfp_log( NFP_DBG1, "nfp_attach: failed to create command device");
+ goto bailout;
+ }
+ pdev->common.dev= pdev;
+
+ if (ddi_intr_hilevel(dip, 0) != 0){
+ nfp_log( NFP_DBG2, "nfp_attach: high-level interrupt");
+ if( ddi_get_iblock_cookie(dip, 0, &pdev->high_iblock_cookie) ) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_get_iblock_cookie(high) failed." );
+ goto bailout;
+ }
+ if( ddi_get_iblock_cookie(dip, 0, &pdev->low_iblock_cookie) ) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_get_iblock_cookie(low) failed." );
+ goto bailout;
+ }
+ mutex_init(&pdev->high_mutex, NULL, MUTEX_DRIVER,
+ (void *)pdev->high_iblock_cookie);
+ mutex_init(&pdev->low_mutex, NULL, MUTEX_DRIVER,
+ (void *)pdev->low_iblock_cookie);
+ if (ddi_add_intr(dip, 0, NULL,
+ NULL, nfp_isr,
+ (caddr_t)pdev) != DDI_SUCCESS) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_add_intr(high) failed." );
+ goto bailout;
+ }
+ if( ddi_get_soft_iblock_cookie(dip, DDI_SOFTINT_HIGH,
+ &pdev->iblock_cookie) ) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_get_iblock_cookie(soft) failed." );
+ goto bailout;
+ }
+ mutex_init(&pdev->isr_mutex, NULL, MUTEX_DRIVER,
+ (void *)pdev->iblock_cookie);
+ if (ddi_add_softintr(dip, DDI_SOFTINT_HIGH, &pdev->soft_int_id,
+ &pdev->iblock_cookie, NULL,
+ nfp_soft_isr, (caddr_t)pdev) != DDI_SUCCESS)
+ goto bailout;
+ pdev->high_intr= 1;
+ } else {
+ nfp_log( NFP_DBG2, "nfp_attach: low-level interrupt");
+
+ if (ddi_get_iblock_cookie (dip, 0, &pdev->iblock_cookie)) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_get_iblock_cookie() failed." );
+ goto bailout;
+ }
+
+ mutex_init(&pdev->isr_mutex, "nfp isr mutex", MUTEX_DRIVER, (void *)pdev->iblock_cookie);
+
+ if (ddi_add_intr(dip, 0, NULL,
+ (ddi_idevice_cookie_t *)NULL, nfp_isr,
+ (caddr_t)pdev) != DDI_SUCCESS) {
+ nfp_log( NFP_DBG1, "nfp_attach: ddi_add_intr() failed." );
+ goto bailout;
+ }
+ }
+ mutex_init(&pdev->busy_mutex, "nfp busy mutex", MUTEX_DRIVER, NULL );
+ cv_init(&pdev->rd_cv, "nfp read condvar", CV_DRIVER, NULL );
+
+ /* get our bus and slot num */
+ if (ddi_getlongprop (DDI_DEV_T_NONE,
+ pdev->dip, 0, "reg",
+ (caddr_t)&outp, &intres) != DDI_PROP_NOT_FOUND) {
+ nfp_log( NFP_DBG2, "nfp_attach: ddi_getlongprop('reg') ok." );
+ if( intres > 0 ) {
+ nfp_log( NFP_DBG1, "nfp_attach: found PCI nfast bus %x slot %x.",
+ ((*outp)>>16) & 0xff, ((*outp)>>11) & 0x1f );
+ }
+ }
+
+ nfp_log( NFP_DBG2, "nfp_attach: attach succeeded." );
+ return DDI_SUCCESS;
+
+bailout:
+ (void) nfp_release_dev( dip );
+
+ return DDI_FAILURE;
+}
+
+/*-------------------------*/
+/* nfp_detach */
+/*-------------------------*/
+
+/*
+ * When our driver is unloaded, nfp_detach cleans up and frees the resources
+ * we allocated in nfp_attach.
+ */
+static int nfp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) {
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ (void) nfp_release_dev(dip);
+
+ return (DDI_SUCCESS);
+}
+
+/*-------------------------*/
+/* _init */
+/*-------------------------*/
+
+int _init(void) {
+ register int error;
+
+ nfp_log( NFP_DBG2, "_init: entered" );
+
+ if ((error = ddi_soft_state_init(&state_head, sizeof (struct nfp_dev), 1)) != 0) {
+ nfp_log( NFP_DBG1, "_init: soft_state_init() failed" );
+ return (error);
+ }
+
+ if ((error = mod_install(&modlinkage)) != 0) {
+ nfp_log( NFP_DBG1, "_init: mod_install() failed" );
+ ddi_soft_state_fini(&state_head);
+ }
+
+ nfp_log( NFP_DBG2, "_init: leaving" );
+ return (error);
+}
+
+/*-------------------------*/
+/* _info */
+/*-------------------------*/
+
+int _info(struct modinfo *modinfop) {
+ nfp_log( NFP_DBG2, "_info: entered" );
+
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*-------------------------*/
+/* _fini */
+/*-------------------------*/
+
+int _fini(void) {
+ int status;
+
+ nfp_log( NFP_DBG2, "_fini: entered" );
+
+ if ((status = mod_remove(&modlinkage)) != 0) {
+ nfp_log( NFP_DBG2, "_fini: mod_remove() failed." );
+ return (status);
+ }
+
+ ddi_soft_state_fini(&state_head);
+
+ nfp_log( NFP_DBG2, "_fini: leaving" );
+
+ return (status);
+}
+
diff --git a/usr/src/uts/common/io/nfp/i21285.c b/usr/src/uts/common/io/nfp/i21285.c
new file mode 100644
index 0000000000..f51a09188d
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/i21285.c
@@ -0,0 +1,310 @@
+/*
+
+i21285.c: nCipher PCI HSM intel/digital 21285 command driver
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+
+history
+
+09/10/2001 jsh Original
+
+*/
+
+#include "nfp_common.h"
+#include "nfp_error.h"
+#include "nfp_hostif.h"
+#include "nfp_osif.h"
+#include "i21285.h"
+#include "nfp_cmd.h"
+#include "nfpci.h"
+
+/* create ------------------------------------------------------- */
+
+static nfp_err i21285_create( nfp_cdev *pdev ) {
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21285_create: entered");
+ pdev->cmdctx= pdev; /* set our context to just be a pointer to our nfp_cdev */
+
+ nfp_log( NFP_DBG2, "i21285_create: enable doorbell");
+ if(!pdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21285_create: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+ TO_LE32_IO( &tmp32, DOORBELL_ENABLE | POSTLIST_ENABLE);
+ nfp_outl( pdev, IOBAR, I21285_OFFSET_INTERRUPT_MASK, tmp32 );
+
+ return NFP_SUCCESS;
+}
+
+/* stop ------------------------------------------------------- */
+
+static nfp_err i21285_destroy( void * ctx ) {
+ nfp_cdev *pdev;
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21285_destroy: entered");
+
+ pdev= (nfp_cdev *)ctx;
+ if(!pdev) {
+ nfp_log( NFP_DBG1, "i21285_destroy: NULL pdev");
+ return NFP_ENODEV;
+ }
+ if(!pdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21285_destroy: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+ TO_LE32_IO( &tmp32, DOORBELL_DISABLE | POSTLIST_DISABLE );
+ nfp_outl( pdev, IOBAR, I21285_OFFSET_INTERRUPT_MASK, tmp32 );
+
+ return NFP_SUCCESS;
+}
+
+/* open ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21285_open( void * ctx ) {
+ nfp_log( NFP_DBG2, "i21285_open: entered");
+
+ return NFP_SUCCESS;
+}
+
+/* close ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21285_close( void * ctx ) {
+ nfp_log( NFP_DBG2, "i21285_close: entered");
+
+ return NFP_SUCCESS;
+}
+
+/* isr ------------------------------------------------------- */
+
+static nfp_err i21285_isr( void *ctx, int *handled ) {
+ nfp_cdev *pdev;
+ unsigned int doorbell;
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG3, "i21285_isr: entered");
+
+ *handled= 0;
+ pdev= (nfp_cdev *)ctx;
+ if(!pdev) {
+ nfp_log( NFP_DBG1, "i21285_isr: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ doorbell= nfp_inl( pdev, IOBAR, I21285_OFFSET_DOORBELL);
+ doorbell= FROM_LE32_IO(&doorbell) & 0xffff;
+ while( doorbell && doorbell != 0xffff) {
+ *handled= 1;
+ /* service interrupts */
+ if( doorbell & (NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED)) {
+ TO_LE32_IO( &tmp32, NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED);
+ nfp_outl( pdev, IOBAR, I21285_OFFSET_DOORBELL, tmp32 );
+
+ nfp_log(NFP_DBG2, "i21285_isr: write done interrupt, ok = %d.", doorbell & NFAST_INT_DEVICE_WRITE_OK ? 1 : 0 );
+
+ nfp_write_complete(pdev->dev, doorbell & NFAST_INT_DEVICE_WRITE_OK ? 1 : 0 );
+ }
+
+ if( doorbell & (NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED)) {
+ TO_LE32_IO( &tmp32, NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED );
+ nfp_outl( pdev, IOBAR, I21285_OFFSET_DOORBELL, tmp32 );
+
+ nfp_log(NFP_DBG2, "i21285_isr: read ack interrupt, ok = %d.", doorbell & NFAST_INT_DEVICE_READ_OK ? 1 : 0 );
+ nfp_read_complete( pdev->dev, doorbell & NFAST_INT_DEVICE_READ_OK ? 1 : 0);
+ }
+
+ if( doorbell & ~(NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED |
+ NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED)) {
+ nfp_log( NFP_DBG1, "i21285_isr: unexpected interrupt %x", doorbell );
+ TO_LE32_IO( &tmp32, 0xffff & doorbell );
+ nfp_outl( pdev, IOBAR, I21285_OFFSET_DOORBELL, tmp32 );
+ }
+ doorbell= nfp_inl( pdev, IOBAR, I21285_OFFSET_DOORBELL);
+ doorbell= FROM_LE32_IO(&doorbell) & 0xffff;
+ }
+ return 0;
+}
+
+/* write ------------------------------------------------------- */
+
+static nfp_err i21285_write( const char *block, int len, void *ctx ) {
+ nfp_cdev *cdev;
+ unsigned int hdr[2];
+ nfp_err ne;
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21285_write: entered");
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21285_write: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ nfp_log(NFP_DBG2, "i21285_write: pdev->bar[ MEMBAR ]= %x\n", cdev->bar[ MEMBAR ]);
+ nfp_log(NFP_DBG2, "i21285_write: pdev->bar[ IOBAR ]= %x\n", cdev->bar[ IOBAR ]);
+ if(!cdev->bar[ MEMBAR ]) {
+ nfp_log( NFP_DBG1, "i21285_write: null BAR[%d]", MEMBAR );
+ return NFP_ENOMEM;
+ }
+ ne= nfp_copy_from_user_to_dev( cdev, MEMBAR, NFPCI_JOBS_WR_DATA, block, len);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21285_write: nfp_copy_from_user_to_dev failed");
+ return ne;
+ }
+ TO_LE32_MEM(&hdr[0], NFPCI_JOB_CONTROL);
+ TO_LE32_MEM(&hdr[1], len);
+
+ ne= nfp_copy_to_dev( cdev, MEMBAR, NFPCI_JOBS_WR_CONTROL, (const char *)hdr, 8);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21285_write: nfp_copy_to_dev failed");
+ return ne;
+ }
+
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_WR_LENGTH, (char *)hdr, 4);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21285_write: nfp_copy_from_dev failed");
+ return ne;
+ }
+
+ TO_LE32_MEM( &tmp32, len );
+ if ( hdr[0] != tmp32 ) {
+ nfp_log( NFP_DBG1, "i21285_write: length not written");
+ return NFP_EIO;
+ }
+
+ TO_LE32_IO( &tmp32, NFAST_INT_HOST_WRITE_REQUEST);
+
+ nfp_outl( cdev, IOBAR, I21285_OFFSET_DOORBELL, tmp32 );
+
+ nfp_log( NFP_DBG2, "i21285_write: done");
+ return NFP_SUCCESS;
+}
+
+/* read ------------------------------------------------------- */
+
+static nfp_err i21285_read( char *block, int len, void *ctx, int *rcount) {
+ nfp_cdev *cdev;
+ nfp_err ne;
+ int count;
+
+ nfp_log( NFP_DBG2, "i21285_read: entered, len %d", len);
+ *rcount= 0;
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21285_read: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ if(!cdev->bar[ MEMBAR ]) {
+ nfp_log( NFP_DBG1, "i21285_read: null BAR[%d]", MEMBAR );
+ return NFP_ENOMEM;
+ }
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_LENGTH, (char *)&count, 4);
+ if(ne) {
+ nfp_log( NFP_DBG1, "i21285_read: nfp_copy_from_dev failed.");
+ return ne;
+ }
+ count= FROM_LE32_MEM(&count);
+ if(count<0 || count>len) {
+ nfp_log( NFP_DBG1, "i21285_read: bad byte count (%d) from device", count);
+ return NFP_EIO;
+ }
+ ne= nfp_copy_to_user_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_DATA, block, count);
+ if( ne ) {
+ nfp_log( NFP_DBG1, "i21285_read: nfp_copy_to_user_from_dev failed.");
+ return ne;
+ }
+ nfp_log( NFP_DBG2, "i21285_read: done");
+ *rcount= count;
+ return NFP_SUCCESS;
+}
+
+/* chupdate ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21285_chupdate( char *data, int len, void *ctx ) {
+ nfp_log( NFP_DBG1, "i21285_chupdate: NYI");
+ return NFP_SUCCESS;
+}
+
+/* ensure reading -------------------------------------------------- */
+
+static nfp_err i21285_ensure_reading( unsigned int addr, int len, void *ctx ) {
+ nfp_cdev *cdev;
+ unsigned int hdr[2];
+ unsigned int tmp32;
+ nfp_err ne;
+
+ nfp_log( NFP_DBG2, "i21285_ensure_reading: entered");
+
+ if(addr) {
+ nfp_log( NFP_DBG2, "i21285_ensure_reading: bad addr");
+ return -NFP_EINVAL;
+ }
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21285_ensure_reading: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ if(!cdev->bar[ MEMBAR ]) {
+ nfp_log( NFP_DBG1, "i21285_ensure_reading: null BAR[%d]", MEMBAR );
+ return NFP_ENXIO;
+ }
+ nfp_log( NFP_DBG3, "i21285_ensure_reading: pdev->bar[ MEMBAR ]= %x", cdev->bar[ MEMBAR ]);
+ nfp_log( NFP_DBG3, "i21285_ensure_reading: pdev->bar[ IOBAR ]= %x", cdev->bar[ IOBAR ]);
+ TO_LE32_MEM( &hdr[0], NFPCI_JOB_CONTROL);
+ TO_LE32_MEM( &hdr[1], len);
+ ne= nfp_copy_to_dev( cdev, MEMBAR, NFPCI_JOBS_RD_CONTROL, (const char *)hdr, 8);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21285_ensure_reading: nfp_copy_to_dev failed");
+ return ne;
+ }
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_LENGTH, (char *)hdr, 4);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21285_ensure_reading: nfp_copy_from_dev failed");
+ return ne;
+ }
+ TO_LE32_MEM( &tmp32, len );
+ if ( hdr[0] != tmp32 ) {
+ nfp_log( NFP_DBG1, "i21285_ensure_reading: len not written");
+ return NFP_EIO;
+ };
+ TO_LE32_IO( &tmp32, NFAST_INT_HOST_READ_REQUEST );
+ nfp_outl( cdev, IOBAR, I21285_OFFSET_DOORBELL, tmp32 );
+
+ return NFP_SUCCESS;
+}
+
+/* command device structure ------------------------------------- */
+
+
+const nfpcmd_dev i21285_cmddev = {
+ "nCipher Gen 1 PCI",
+ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285,
+ PCI_VENDOR_ID_NCIPHER, PCI_DEVICE_ID_NFAST_GEN1,
+ { 0, IOSIZE | PCI_BASE_ADDRESS_SPACE_IO, NFPCI_RAM_MINSIZE, 0, 0, 0 },
+ NFP_CMD_FLG_NEED_IOBUF,
+ i21285_create,
+ i21285_destroy,
+ i21285_open,
+ i21285_close,
+ i21285_isr,
+ i21285_write,
+ i21285_read,
+ i21285_chupdate,
+ i21285_ensure_reading,
+ 0, /* no debug */
+};
+
diff --git a/usr/src/uts/common/io/nfp/i21285.h b/usr/src/uts/common/io/nfp/i21285.h
new file mode 100644
index 0000000000..4ea1d853ec
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/i21285.h
@@ -0,0 +1,43 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+#ifndef NFP_I21285_H
+#define NFP_I21285_H
+
+#ifndef PCI_VENDOR_ID_DEC
+#define PCI_VENDOR_ID_DEC 0x1011
+#endif
+#ifndef PCI_DEVICE_ID_DEC_21285
+#define PCI_DEVICE_ID_DEC_21285 0x1065
+#endif
+#ifndef PCI_VENDOR_ID_NCIPHER
+#define PCI_VENDOR_ID_NCIPHER 0x0100
+#endif
+
+#ifndef PCI_DEVICE_ID_NFAST_GEN1
+#define PCI_DEVICE_ID_NFAST_GEN1 0x0100
+#endif
+
+#define I21285_OFFSET_DOORBELL 0x60
+#define I21285_OFFSET_INTERRUPT_MASK 0x34
+
+#define DOORBELL_ENABLE 0x0
+#define DOORBELL_DISABLE 0x4
+
+#define POSTLIST_ENABLE 0x0
+#define POSTLIST_DISABLE 0x8
+
+#define IOBAR 1
+#define MEMBAR 2
+
+#define IOSIZE 0x80
+#define MEMSIZE 0x100000
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/i21555.c b/usr/src/uts/common/io/nfp/i21555.c
new file mode 100644
index 0000000000..82024dc800
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/i21555.c
@@ -0,0 +1,423 @@
+/*
+
+i21555.c: nCipher PCI HSM intel 21555 command driver
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+09/10/2001 jsh Original
+
+*/
+
+#include "nfp_common.h"
+#include "nfp_error.h"
+#include "nfp_hostif.h"
+#include "nfp_osif.h"
+#include "i21555.h"
+#include "nfp_cmd.h"
+#include "nfpci.h"
+
+/* started ------------------------------------------------------
+ *
+ * Check that device is ready to talk, by checking that
+ * the i21555 has master enabled on its secondary interface
+ */
+
+static nfp_err i21555_started( nfp_cdev *pdev ) {
+ unsigned int tmp32;
+#ifdef CONFIGSPACE_DEBUG
+ unsigned int reg32[64];
+ int i;
+#endif
+ nfp_err ne;
+
+ nfp_log( NFP_DBG2, "i21555_started: entered");
+
+#ifdef CONFIGSPACE_DEBUG
+ /* Suck up all the registers */
+ for (i=0; i < 64; i++) {
+ ne = nfp_config_inl( pdev, i*4, &reg32[i] );
+ }
+
+ for (i=0; i < 16; i++) {
+ int j = i * 4;
+ nfp_log( NFP_DBG3, "i21555 config reg %2x: %08x %08x %08x %08x", j*4,
+ reg32[j], reg32[j+1], reg32[j+2], reg32[j+3]);
+ }
+#endif
+
+ ne = nfp_config_inl( pdev, I21555_CFG_SEC_CMD_STATUS, &tmp32 );
+ if (ne) {
+ /* succeed if PCI config reads are not implemented */
+ if (ne == NFP_EUNKNOWN)
+ return NFP_SUCCESS;
+ nfp_log( NFP_DBG1, "i21555_started: nfp_config_inl failed");
+ return ne;
+ }
+
+ tmp32= FROM_LE32_IO(&tmp32) & 0xffff;
+
+ if ( tmp32 & CFG_CMD_MASTER ) {
+ nfp_log( NFP_DBG3, "i21555_started: Yes %x", tmp32);
+ return NFP_SUCCESS;
+ } else {
+ nfp_log( NFP_DBG1, "i21555_started: device not started yet %x", tmp32);
+ return NFP_ESTARTING;
+ }
+}
+
+/* create ------------------------------------------------------- */
+
+static nfp_err i21555_create( nfp_cdev *pdev ) {
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21555_create: entered");
+ pdev->cmdctx= pdev; /* set our context to just be a pointer to our nfp_cdev */
+
+ if(!pdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_create: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+ nfp_log( NFP_DBG2, "i21555_create: enable doorbell");
+ TO_LE32_IO( &tmp32, I21555_DOORBELL_PRI_ENABLE );
+ nfp_outl( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_SET_MASK, tmp32 );
+ nfp_outl( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_CLEAR_MASK, tmp32 );
+ return NFP_SUCCESS;
+}
+
+/* stop ------------------------------------------------------- */
+
+static nfp_err i21555_destroy( void * ctx ) {
+ nfp_cdev *pdev;
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21555_destroy: entered");
+
+ pdev= (nfp_cdev *)ctx;
+ if(!pdev) {
+ nfp_log( NFP_DBG1, "i21555_destroy: NULL pdev");
+ return NFP_ENODEV;
+ }
+ if(!pdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_destroy: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+ TO_LE32_IO( &tmp32, I21555_DOORBELL_PRI_DISABLE );
+ nfp_outl( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_SET_MASK, tmp32 );
+ nfp_outl( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_CLEAR_MASK, tmp32 );
+
+ return NFP_SUCCESS;
+}
+
+/* open ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21555_open( void * ctx ) {
+
+ nfp_log( NFP_DBG2, "i21555_open: entered");
+
+ return NFP_SUCCESS;
+}
+
+/* close ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21555_close( void * ctx ) {
+ nfp_log( NFP_DBG2, "i21555_close: entered");
+
+ return NFP_SUCCESS;
+}
+
+/* isr ------------------------------------------------------- */
+
+static nfp_err i21555_isr( void *ctx, int *handled ) {
+ nfp_cdev *pdev;
+ nfp_err ne;
+ unsigned short doorbell;
+ unsigned short tmp16;
+
+ nfp_log( NFP_DBG3, "i21555_isr: entered");
+
+ *handled= 0;
+ pdev= (nfp_cdev *)ctx;
+ if(!pdev) {
+ nfp_log( NFP_DBG1, "i21555_isr: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ pdev->stats.isr++;
+
+ if(!pdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_isr: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+
+ /* This interrupt may not be from our module, so check that it actually is
+ * us before handling it.
+ */
+ ne = i21555_started( pdev );
+ if (ne) {
+ if (ne != NFP_ESTARTING) {
+ nfp_log( NFP_DBG1, "i21555_isr: i21555_started failed");
+ }
+ return ne;
+ }
+
+ doorbell= nfp_inw( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_SET);
+ doorbell= FROM_LE16_IO(&doorbell);
+ while( doorbell && doorbell != 0xffff) {
+ *handled= 1;
+ /* service interrupts */
+ if( doorbell & (NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED)) {
+ pdev->stats.isr_write++;
+ TO_LE16_IO(&tmp16,NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED);
+ nfp_outw( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_CLEAR, tmp16 );
+
+ nfp_log( NFP_DBG2, "i21555_isr: write done interrupt, ok = %d.", doorbell & NFAST_INT_DEVICE_WRITE_OK ? 1 : 0 );
+
+ nfp_write_complete(pdev->dev, doorbell & NFAST_INT_DEVICE_WRITE_OK ? 1 : 0 );
+ }
+
+ if( doorbell & (NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED)) {
+ pdev->stats.isr_read++;
+ TO_LE16_IO(&tmp16,NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED);
+ nfp_outw( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_CLEAR, tmp16 );
+
+ nfp_log( NFP_DBG2, "i21555_isr: read ack interrupt, ok = %d.", doorbell & NFAST_INT_DEVICE_READ_OK ? 1 : 0 );
+ nfp_read_complete( pdev->dev, doorbell & NFAST_INT_DEVICE_READ_OK ? 1 : 0);
+ }
+
+ if( doorbell & ~(NFAST_INT_DEVICE_READ_OK | NFAST_INT_DEVICE_READ_FAILED |
+ NFAST_INT_DEVICE_WRITE_OK | NFAST_INT_DEVICE_WRITE_FAILED)) {
+ TO_LE16_IO(&tmp16,doorbell);
+ nfp_outw( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_CLEAR, tmp16 );
+ nfp_log( NFP_DBG1, "i21555_isr: unexpected interrupt %x", doorbell );
+ }
+ doorbell= nfp_inw( pdev, IOBAR, I21555_OFFSET_DOORBELL_PRI_SET);
+ doorbell= FROM_LE16_IO(&doorbell);
+ }
+ nfp_log( NFP_DBG3, "i21555_isr: exiting");
+ return 0;
+}
+
+/* write ------------------------------------------------------- */
+
+static nfp_err i21555_write( const char *block, int len, void *ctx) {
+ nfp_cdev *cdev;
+ unsigned int hdr[2];
+ nfp_err ne;
+ unsigned short tmp16;
+ unsigned int tmp32;
+
+ nfp_log( NFP_DBG2, "i21555_write: entered");
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21555_write: NULL cdev");
+ return NFP_ENODEV;
+ }
+
+ cdev->stats.write_fail++;
+
+ if(!cdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_write: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+
+ ne = i21555_started( cdev );
+ if (ne) {
+ if (ne != NFP_ESTARTING) {
+ nfp_log( NFP_DBG1, "i21555_write: i21555_started failed");
+ }
+ return ne;
+ }
+
+ nfp_log( NFP_DBG3, "i21555_write: cdev->bar[ MEMBAR ]= %x", cdev->bar[ MEMBAR ]);
+ nfp_log( NFP_DBG3, "i21555_write: cdev->bar[ IOBAR ]= %x", cdev->bar[ IOBAR ]);
+ nfp_log( NFP_DBG3, "i21555_write: block len %d", len );
+ ne= nfp_copy_from_user_to_dev( cdev, MEMBAR, NFPCI_JOBS_WR_DATA, block, len);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_write: nfp_copy_from_user_to_dev failed");
+ return ne;
+ }
+ TO_LE32_MEM(&hdr[0], NFPCI_JOB_CONTROL);
+ TO_LE32_MEM(&hdr[1], len);
+ ne= nfp_copy_to_dev( cdev, MEMBAR, NFPCI_JOBS_WR_CONTROL, (const char *)hdr, 8);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_write: nfp_copy_to_dev failed");
+ return ne;
+ }
+
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_WR_LENGTH, (char *)hdr, 4);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_write: nfp_copy_from_dev failed");
+ return ne;
+ }
+
+ TO_LE32_MEM(&tmp32, len);
+ if ( hdr[0] != tmp32 ) {
+ nfp_log( NFP_DBG1, "i21555_write: length not written");
+ return NFP_EIO;
+ }
+ TO_LE16_IO(&tmp16, NFAST_INT_HOST_WRITE_REQUEST >> 16);
+ nfp_outw( cdev, IOBAR, I21555_OFFSET_DOORBELL_SEC_SET, tmp16);
+
+ cdev->stats.write_fail--;
+ cdev->stats.write_block++;
+ cdev->stats.write_byte += len;
+
+ nfp_log( NFP_DBG2, "i21555_write: done");
+ return NFP_SUCCESS;
+}
+
+/* read ------------------------------------------------------- */
+
+static nfp_err i21555_read( char *block, int len, void *ctx, int *rcount) {
+ nfp_cdev *cdev;
+ nfp_err ne;
+ int count;
+
+ nfp_log( NFP_DBG2, "i21555_read: entered");
+ *rcount= 0;
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21555_read: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ cdev->stats.read_fail++;
+
+ if(!cdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_read: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_LENGTH, (char *)&count, 4);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_read: nfp_copy_from_dev failed.");
+ return ne;
+ }
+ count= FROM_LE32_MEM(&count);
+ if(count<0 || count>len) {
+ nfp_log( NFP_DBG1, "i21555_read: bad byte count (%d) from device", count);
+ return NFP_EIO;
+ }
+ ne= nfp_copy_to_user_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_DATA, block, count);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_read: nfp_copy_to_user failed.");
+ return ne;
+ }
+ nfp_log( NFP_DBG2, "i21555_read: done");
+ *rcount= count;
+ cdev->stats.read_fail--;
+ cdev->stats.read_block++;
+ cdev->stats.read_byte += len;
+ return NFP_SUCCESS;
+}
+
+/* chupdate ------------------------------------------------------- */
+
+/* ARGSUSED */
+static nfp_err i21555_chupdate( char *data, int len, void *ctx ) {
+ nfp_log( NFP_DBG1, "i21555_chupdate: NYI");
+ return NFP_SUCCESS;
+}
+
+/* ensure reading -------------------------------------------------- */
+
+static nfp_err i21555_ensure_reading( unsigned int addr, int len, void *ctx ) {
+ nfp_cdev *cdev;
+ unsigned int hdr[3];
+ unsigned short tmp16;
+ unsigned int tmp32;
+ nfp_err ne;
+ int hdr_len;
+
+ nfp_log( NFP_DBG2, "i21555_ensure_reading: entered");
+
+ cdev= (nfp_cdev *)ctx;
+ if(!cdev) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: NULL pdev");
+ return NFP_ENODEV;
+ }
+
+ cdev->stats.ensure_fail++;
+
+ if(!cdev->bar[ IOBAR ]) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: null BAR[%d]", IOBAR );
+ return NFP_ENOMEM;
+ }
+
+ ne = i21555_started( cdev );
+ if (ne) {
+ if (ne != NFP_ESTARTING) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: i21555_started failed");
+ }
+ return ne;
+ }
+
+ nfp_log( NFP_DBG3, "i21555_ensure_reading: pdev->bar[ MEMBAR ]= %x", cdev->bar[ MEMBAR ]);
+ nfp_log( NFP_DBG3, "i21555_ensure_reading: pdev->bar[ IOBAR ]= %x", cdev->bar[ IOBAR ]);
+ if(addr) {
+ nfp_log( NFP_DBG3, "i21555_ensure_reading: new format, addr %x", addr);
+ TO_LE32_MEM(&hdr[0], NFPCI_JOB_CONTROL_PCI_PUSH);
+ TO_LE32_MEM(&hdr[1], len);
+ TO_LE32_MEM(&hdr[2], addr);
+ hdr_len= 12;
+ } else {
+ TO_LE32_MEM(&hdr[0], NFPCI_JOB_CONTROL);
+ TO_LE32_MEM(&hdr[1], len);
+ hdr_len= 8;
+ }
+ ne= nfp_copy_to_dev( cdev, MEMBAR, NFPCI_JOBS_RD_CONTROL, (const char *)hdr, hdr_len);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: nfp_copy_to_dev failed");
+ return ne;
+ }
+
+ ne= nfp_copy_from_dev( cdev, MEMBAR, NFPCI_JOBS_RD_LENGTH, (char *)hdr, 4);
+ if (ne) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: nfp_copy_from_dev failed");
+ return ne;
+ }
+
+ TO_LE32_MEM(&tmp32, len);
+
+ if ( hdr[0] != tmp32 ) {
+ nfp_log( NFP_DBG1, "i21555_ensure_reading: len not written");
+ return NFP_EIO;
+ }
+ TO_LE16_IO( &tmp16, NFAST_INT_HOST_READ_REQUEST >> 16);
+ nfp_outw( cdev, IOBAR, I21555_OFFSET_DOORBELL_SEC_SET, tmp16);
+
+ cdev->stats.ensure_fail--;
+ cdev->stats.ensure++;
+
+ return NFP_SUCCESS;
+}
+
+/* command device structure ------------------------------------- */
+
+const nfpcmd_dev i21555_cmddev = {
+ "nCipher Gen 2 PCI",
+ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_21555,
+ PCI_VENDOR_ID_NCIPHER, PCI_SUBSYSTEM_ID_NFAST_REV1,
+ { 0, IOSIZE | PCI_BASE_ADDRESS_SPACE_IO, NFPCI_RAM_MINSIZE_JOBS, 0, 0, 0 },
+ NFP_CMD_FLG_NEED_IOBUF,
+ i21555_create,
+ i21555_destroy,
+ i21555_open,
+ i21555_close,
+ i21555_isr,
+ i21555_write,
+ i21555_read,
+ i21555_chupdate,
+ i21555_ensure_reading,
+ i21555_debug,
+};
diff --git a/usr/src/uts/common/io/nfp/i21555.h b/usr/src/uts/common/io/nfp/i21555.h
new file mode 100644
index 0000000000..d8f3965938
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/i21555.h
@@ -0,0 +1,51 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+#ifndef I21555_H
+#define I21555_H
+
+#ifndef PCI_VENDOR_ID_INTEL
+#define PCI_VENDOR_ID_INTEL 0x8086
+#endif
+
+#ifndef PCI_DEVICE_ID_INTEL_21555
+#define PCI_DEVICE_ID_INTEL_21555 0xb555
+#endif
+
+#ifndef PCI_VENDOR_ID_NCIPHER
+#define PCI_VENDOR_ID_NCIPHER 0x0100
+#endif
+
+#ifndef PCI_SUBSYSTEM_ID_NFAST_REV1
+#define PCI_SUBSYSTEM_ID_NFAST_REV1 0x0100
+#endif
+
+#define I21555_OFFSET_DOORBELL_PRI_SET 0x9C
+#define I21555_OFFSET_DOORBELL_SEC_SET 0x9E
+#define I21555_OFFSET_DOORBELL_PRI_CLEAR 0x98
+
+#define I21555_OFFSET_DOORBELL_PRI_SET_MASK 0xA4
+#define I21555_OFFSET_DOORBELL_PRI_CLEAR_MASK 0xA0
+
+#define I21555_DOORBELL_PRI_ENABLE 0x0000
+#define I21555_DOORBELL_PRI_DISABLE 0xFFFF
+
+#define I21555_CFG_SEC_CMD_STATUS 0x44
+
+#define CFG_CMD_MASTER 0x0004
+
+#define IOBAR 1
+#define MEMBAR 2
+
+#define IOSIZE 0x100
+
+extern nfp_err i21555_debug( int cmd, void *ctx );
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/i21555d.c b/usr/src/uts/common/io/nfp/i21555d.c
new file mode 100644
index 0000000000..183ace8275
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/i21555d.c
@@ -0,0 +1,28 @@
+/*
+
+i21555d.c: nCipher PCI HSM intel 21555 debug ioctl
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+
+history
+
+15/05/2002 jsh Original, does nothing
+
+*/
+
+#include "nfp_common.h"
+#include "nfp_error.h"
+#include "nfp_osif.h"
+#include "i21555.h"
+
+/* ARGSUSED */
+nfp_err i21555_debug( int cmd, void *ctx) {
+ nfp_log( NFP_DBG1, "i21555_debug: entered");
+
+ return NFP_EUNKNOWN;
+}
diff --git a/usr/src/uts/common/io/nfp/nfdev-common.h b/usr/src/uts/common/io/nfp/nfdev-common.h
new file mode 100644
index 0000000000..8a97bf2c63
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfdev-common.h
@@ -0,0 +1,141 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+/** \file nfdev-common.h
+ *
+ * \brief nFast device driver (not generic SCSI) ioctl struct definition file
+ * include NFDEV-$(system) for ioctl number definitions
+ *
+ * 1998.07.13 jsh Started
+ *
+ *
+ */
+
+#ifndef NFDEV_COMMON_H
+#define NFDEV_COMMON_H
+
+/**
+ * Result of the ENQUIRY ioctl.
+ */
+typedef struct nfdev_enquiry_str {
+ unsigned int busno; /**< Which bus is the PCI device on. */
+ unsigned char slotno; /**< Which slot is the PCI device in. */
+ unsigned char reserved[3]; /**< for consistant struct alignment */
+} nfdev_enquiry_str;
+
+/**
+ * Result of the STATS ioctl.
+ */
+typedef struct nfdev_stats_str {
+ unsigned long isr; /**< Count interrupts. */
+ unsigned long isr_read; /**< Count read interrupts. */
+ unsigned long isr_write; /**< Count write interrupts. */
+ unsigned long write_fail; /**< Count write failures. */
+ unsigned long write_block; /**< Count blocks written. */
+ unsigned long write_byte; /**< Count bytes written. */
+ unsigned long read_fail; /**< Count read failures. */
+ unsigned long read_block; /**< Count blocks read. */
+ unsigned long read_byte; /**< Count bytes read. */
+ unsigned long ensure_fail; /**< Count read request failures. */
+ unsigned long ensure; /**< Count read requests. */
+} nfdev_stats_str;
+
+/**
+ * Input to the CONTROL ioctl.
+ */
+typedef struct nfdev_control_str {
+ unsigned control; /**< Control flags. */
+} nfdev_control_str;
+
+/** Control bit indicating host supports MOI control */
+#define NFDEV_CONTROL_HOST_MOI 0x0001
+
+/** Index of control bits indicating desired mode
+ *
+ * Desired mode follows the M_ModuleMode enumeration.
+ */
+#define NFDEV_CONTROL_MODE_SHIFT 1
+
+/** Detect a backwards-compatible control value
+ *
+ * Returns true if the request control value "makes no difference", i.e.
+ * and the failure of an attempt to set it is therefore uninteresting.
+ */
+#define NFDEV_CONTROL_HARMLESS(c) ((c) <= 1)
+
+/**
+ * Result of the STATUS ioctl.
+ */
+typedef struct nfdev_status_str {
+ unsigned status; /**< Status flags. */
+ char error[8]; /**< Error string. */
+} nfdev_status_str;
+
+/** Monitor firmware supports MOI control and error reporting */
+#define NFDEV_STATUS_MONITOR_MOI 0x0001
+
+/** Application firmware supports MOI control and error reporting */
+#define NFDEV_STATUS_APPLICATION_MOI 0x0002
+
+/** Application firmware running and supports error reporting */
+#define NFDEV_STATUS_APPLICATION_RUNNING 0x0004
+
+/** HSM failed
+ *
+ * Consult error[] for additional information.
+ */
+#define NFDEV_STATUS_FAILED 0x0008
+
+/** Standard PCI interface. */
+#define NFDEV_IF_STANDARD 0x01
+
+/** PCI interface with results pushed from device
+ * via DMA.
+ */
+#define NFDEV_IF_PCI_PUSH 0x02
+
+/* platform independant base ioctl numbers */
+
+/** Enquiry ioctl.
+ * \return nfdev_enquiry_str describing the attached device. */
+#define NFDEV_IOCTL_NUM_ENQUIRY 0x01
+/** Channel Update ioctl.
+ * \deprecated */
+#define NFDEV_IOCTL_NUM_CHUPDATE 0x02
+/** Ensure Reading ioctl.
+ * Signal a read request to the device.
+ * \param (unsigned int) Length of data to be read.
+ */
+#define NFDEV_IOCTL_NUM_ENSUREREADING 0x03
+/** Device Count ioctl.
+ * Not implemented for on all platforms.
+ * \return (int) the number of attached devices. */
+#define NFDEV_IOCTL_NUM_DEVCOUNT 0x04
+/** Internal Debug ioctl.
+ * Not implemented in release drivers. */
+#define NFDEV_IOCTL_NUM_DEBUG 0x05
+/** PCI Interface Version ioctl.
+ * \param (int) Maximum PCI interface version
+ * supported by the user of the device. */
+#define NFDEV_IOCTL_NUM_PCI_IFVERS 0x06
+/** Statistics ioctl.
+ * \return nfdev_enquiry_str describing the attached device. */
+#define NFDEV_IOCTL_NUM_STATS 0x07
+
+/** Module control ioctl
+ * \param (nfdev_control_str) Value to write to HSM control register
+ */
+#define NFDEV_IOCTL_NUM_CONTROL 0x08
+
+/** Module state ioctl
+ * \return (nfdev_status_str) Values read from HSM status/error registers
+ */
+#define NFDEV_IOCTL_NUM_STATUS 0x09
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/nfdev-solaris.h b/usr/src/uts/common/io/nfp/nfdev-solaris.h
new file mode 100644
index 0000000000..923b902e46
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfdev-solaris.h
@@ -0,0 +1,37 @@
+/*
+
+nfdev-solaris.h: nFast solaris specific device ioctl interface.
+
+(C) Copyright nCipher Corporation Ltd 1998-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+14/07/1998 jsh Original
+
+*/
+
+#ifndef NFDEV_SOLARIS_H
+#define NFDEV_SOLARIS_H
+
+#include "nfdev-common.h"
+
+#define NFDEV_IOCTL_TYPE ('n'<<8)
+
+#define NFDEV_IOCTL_ENQUIRY ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_ENQUIRY )
+#define NFDEV_IOCTL_ENSUREREADING ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_ENSUREREADING )
+#define NFDEV_IOCTL_DEVCOUNT ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_DEVCOUNT )
+#define NFDEV_IOCTL_DEBUG ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_DEBUG )
+#define NFDEV_IOCTL_PCI_IFVERS ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_PCI_IFVERS )
+#define NFDEV_IOCTL_STATS ( NFDEV_IOCTL_TYPE | \
+ NFDEV_IOCTL_NUM_STATS )
+
+#endif /* NFDEV_SOLARIS_H */
diff --git a/usr/src/uts/common/io/nfp/nfp.h b/usr/src/uts/common/io/nfp/nfp.h
new file mode 100644
index 0000000000..9704f04fbc
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp.h
@@ -0,0 +1,113 @@
+/*
+
+nfp.h: nFast PCI driver for Solaris 2.5, 2.6 and 2.7
+
+(C) Copyright nCipher Corporation Ltd 2001-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+06/05/1998 jsh Original solaris 2.6
+21/05/1999 jsh added support for solaris 2.5
+10/06/1999 jsh added support for solaris 2.7 (32 and 64 bit)
+16/10/2001 jsh moved from nfast to new structure in nfdrv
+
+*/
+
+#ifndef NFP_H
+#define NFP_H
+
+#ifndef _KERNEL
+#error Hello? this is a driver, please compile with -D_KERNEL
+#endif
+
+#if ( CH_KERNELVER < 260 )
+typedef int ioctlptr_t;
+typedef unsigned short uint16_t;
+#define DDI_GET32 ddi_getl
+#define DDI_PUT32 ddi_putl
+#define DDI_GET16 ddi_getw
+#define DDI_PUT16 ddi_putw
+#define DDI_REP_GET8 ddi_rep_getb
+#define DDI_REP_PUT8 ddi_rep_putb
+#define DDI_REP_GET32 ddi_rep_getl
+#define DDI_REP_PUT32 ddi_rep_putl
+#define PCI_CONFIG_GET16 pci_config_getw
+#else /* ( CH_KERNELVER >= 260 ) */
+typedef intptr_t ioctlptr_t;
+#define DDI_GET32 ddi_get32
+#define DDI_PUT32 ddi_put32
+#define DDI_GET16 ddi_get16
+#define DDI_PUT16 ddi_put16
+#define DDI_REP_GET8 ddi_rep_get8
+#define DDI_REP_PUT8 ddi_rep_put8
+#define DDI_REP_GET32 ddi_rep_get32
+#define DDI_REP_PUT32 ddi_rep_put32
+#define PCI_CONFIG_GET16 pci_config_get16
+#endif
+
+#if ( CH_KERNELVER < 270 )
+typedef int nfp_timeout_t;
+#define EXTRA_CB_FLAGS 0
+#define VSXPRINTF(s, n, format, ap) vsprintf (s, format, ap)
+#else /* ( CH_KERNELVER >= 270 ) */
+typedef timeout_id_t nfp_timeout_t;
+#define EXTRA_CB_FLAGS D_64BIT
+#define VSXPRINTF(s, n, format, ap) vsnprintf(s, n, format, ap)
+#endif
+
+typedef struct nfp_dev {
+ int rd_ok;
+ int wr_ok;
+
+ int ifvers;
+
+ /* for PCI push read interface */
+ unsigned char *read_buf;
+ ddi_dma_handle_t read_dma_handle;
+ ddi_dma_cookie_t read_dma_cookie;
+
+ ddi_acc_handle_t acchandle;
+
+ int rd_dma_ok;
+
+ nfp_timeout_t wrtimeout;
+ nfp_timeout_t rdtimeout;
+
+ struct buf *wr_bp;
+ int wr_ready;
+ int rd_ready;
+ int rd_pending;
+ int rd_outstanding;
+ kcondvar_t rd_cv;
+
+ struct pollhead pollhead;
+ dev_info_t *dip;
+
+ ddi_iblock_cookie_t high_iblock_cookie; /* for mutex */
+ ddi_iblock_cookie_t low_iblock_cookie; /* for mutex */
+ kmutex_t high_mutex;
+ kmutex_t low_mutex;
+ int high_intr;
+ ddi_softintr_t soft_int_id;
+ int high_read;
+ int high_write;
+
+ ddi_iblock_cookie_t iblock_cookie; /* for mutex */
+ kmutex_t isr_mutex;
+
+ kmutex_t busy_mutex;
+ int busy;
+
+ ddi_acc_handle_t conf_handle;
+
+ nfp_cdev common;
+ const nfpcmd_dev *cmddev;
+} nfp_dev;
+
+extern struct nfp_dev *nfp_dev_list[];
+
+#endif /* NFP_H */
diff --git a/usr/src/uts/common/io/nfp/nfp_cmd.h b/usr/src/uts/common/io/nfp/nfp_cmd.h
new file mode 100644
index 0000000000..db8af0b2f9
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_cmd.h
@@ -0,0 +1,68 @@
+/*
+
+nfp_cmd.h: nCipher PCI HSM command driver decalrations
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+10/10/2001 jsh Original
+
+*/
+
+#ifndef NFPCMD_H
+#define NFPCMD_H
+
+#include "nfp_hostif.h"
+#include "nfp_error.h"
+
+/* read and write called with userspace buffer */
+
+typedef struct nfpcmd_dev {
+ const char *name;
+ unsigned short vendorid, deviceid,
+ sub_vendorid, sub_deviceid;
+ unsigned int bar_sizes[6]; /* includes IO bit */
+ unsigned int flags;
+ nfp_err (*create)(struct nfp_cdev *pdev);
+ nfp_err (*destroy)(void * ctx);
+ nfp_err (*open)(void * ctx);
+ nfp_err (*close)(void * ctx);
+ nfp_err (*isr)(void *ctx, int *handled);
+ nfp_err (*write_block)( const char *ublock, int len, void *ctx );
+ nfp_err (*read_block)( char *ublock, int len, void *ctx, int *rcount);
+ nfp_err (*channel_update)( char *data, int len, void *ctx);
+ nfp_err (*ensure_reading)( unsigned int addr, int len, void *ctx );
+ nfp_err (*debug)( int cmd, void *ctx);
+} nfpcmd_dev;
+
+#define NFP_CMD_FLG_NEED_IOBUF 0x1
+
+/* list of all supported drivers ---------------------------------------- */
+
+extern const nfpcmd_dev *nfp_drvlist[];
+
+extern const nfpcmd_dev i21285_cmddev;
+extern const nfpcmd_dev i21555_cmddev;
+extern const nfpcmd_dev bcm5820_cmddev;
+
+#ifndef PCI_BASE_ADDRESS_SPACE_IO
+#define PCI_BASE_ADDRESS_SPACE_IO 0x1
+#endif
+
+#define NFP_MAXDEV 16
+
+
+#define NFP_MEMBAR_MASK ~0xf
+#define NFP_IOBAR_MASK ~0x3
+/*
+ This masks off the bottom bits of the PCI_CSR_BAR which signify that the
+ BAR is an IO BAR rather than a MEM BAR
+*/
+
+#endif
+
diff --git a/usr/src/uts/common/io/nfp/nfp_common.h b/usr/src/uts/common/io/nfp/nfp_common.h
new file mode 100644
index 0000000000..d1d2100fea
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_common.h
@@ -0,0 +1,68 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+#ifndef NFP_COMMON_H
+#define NFP_COMMON_H
+
+#include <sys/types.h>
+#include <sys/conf.h>
+
+typedef uint32_t UINT32;
+typedef uint8_t BYTE;
+
+#define DEFINE_NFPCI_PACKED_STRUCTS
+#include "nfpci.h"
+#include "nfdev-solaris.h"
+
+typedef int oserr_t;
+
+#if CH_BIGENDIAN
+
+/* Big Endian Sparc */
+
+#define SWP32(x) \
+( (((unsigned int)(x)>>24)&0xff) | (((unsigned int)(x)>>8)&0xff00) | (((unsigned int)(x)<<8)&0xff0000) | (((unsigned int)(x)<<24)&0xff000000) )
+
+#define SWP16(x) ( (((x)>>8)&0xff) | (((x)<<8)&0xff00) )
+
+#define FROM_LE32_IO(x) SWP32(*x)
+#define TO_LE32_IO(x,y) *x=SWP32(y)
+
+#define FROM_LE32_MEM(x) SWP32(*x)
+#define TO_LE32_MEM(x,y) *x=SWP32(y)
+
+#define FROM_LE16_IO(x) SWP16(*x)
+#define TO_LE16_IO(x,y) *x=SWP16(y)
+
+#else
+
+/* Little Endian x86 */
+
+#define FROM_LE32_IO(x) (*x)
+#define TO_LE32_IO(x,y) (*x=y)
+
+#define FROM_LE32_MEM(x) (*x)
+#define TO_LE32_MEM(x,y) (*x=y)
+
+#define FROM_LE16_IO(x) (*x)
+#define TO_LE16_IO(x,y) (*x=y)
+
+#endif /* !CH_BIGENDIAN */
+
+#include <sys/types.h>
+
+#if CH_KERNELVER == 260
+#define nfp_get_lbolt( lbolt, err ) err= drv_getparm( LBOLT, lbolt )
+#else
+#define nfp_get_lbolt( lbolt, err ) { *lbolt= ddi_get_lbolt(); err= 0; }
+#endif
+
+#endif
+
diff --git a/usr/src/uts/common/io/nfp/nfp_error.h b/usr/src/uts/common/io/nfp/nfp_error.h
new file mode 100644
index 0000000000..d64cb78fd4
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_error.h
@@ -0,0 +1,48 @@
+/*
+
+nfp_error.h: nCipher PCI HSM error handling
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+05/12/2001 jsh Original
+
+*/
+
+#ifndef NFP_ERROR_H
+#define NFP_ERROR_H
+
+#include "nfp_common.h"
+
+#define NFP_SUCCESS 0x0
+#define NFP_EFAULT 0x1
+#define NFP_ENOMEM 0x2
+#define NFP_EINVAL 0x3
+#define NFP_EIO 0x4
+#define NFP_ENXIO 0x5
+#define NFP_ENODEV 0x6
+#define NFP_EINTR 0x7
+#define NFP_ESTARTING 0x8
+#define NFP_EAGAIN 0x9
+#define NFP_EUNKNOWN 0x100
+
+typedef int nfp_err;
+
+extern oserr_t nfp_oserr( nfp_err nerr );
+extern nfp_err nfp_error( oserr_t oerr );
+
+#define nfr( x) \
+ return nfp_error((x))
+
+#define nfer(x, fn, msg) \
+ { oserr_t err=(x); if(err) { nfp_log( NFP_DBG1, #fn ": " msg); return nfp_error(err); } }
+
+#define er(x, fn, msg ) \
+{ nfp_err err=(x); if(err) { nfp_log( NFP_DBG1, #fn ": " msg); return err; } }
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/nfp_hostif.h b/usr/src/uts/common/io/nfp/nfp_hostif.h
new file mode 100644
index 0000000000..3e7d8187e5
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_hostif.h
@@ -0,0 +1,54 @@
+/*
+
+nfp_hostif.h: nCipher PCI HSM host interface declarations
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+10/10/2001 jsh Original
+
+*/
+
+#ifndef NFP_HOSTIF_H
+#define NFP_HOSTIF_H
+
+#include "nfdev-common.h"
+
+struct nfp_dev;
+
+/* common device structure */
+
+typedef struct nfp_cdev {
+ unsigned char *bar[6];
+ void *extra[6];
+
+ int busno;
+ int slotno;
+
+ void *cmdctx;
+
+ char *iobuf;
+
+ struct nfp_dev* dev;
+
+ struct nfdev_stats_str stats;
+
+} nfp_cdev;
+
+/* callbacks from command drivers -------------------------------------- */
+
+void nfp_read_complete( struct nfp_dev *pdev, int ok);
+void nfp_write_complete( struct nfp_dev *pdev, int ok);
+
+#define NFP_READ_MAX (8 * 1024)
+#define NFP_READBUF_SIZE (NFP_READ_MAX + 8)
+#define NFP_TIMEOUT_SEC 10
+
+#define NFP_DRVNAME "nCipher nFast PCI driver"
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/nfp_ifvers.c b/usr/src/uts/common/io/nfp/nfp_ifvers.c
new file mode 100644
index 0000000000..807b4f24c5
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_ifvers.c
@@ -0,0 +1,51 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+/*
+ * nfp_ifervs.c - common pci interface versioning
+ *
+ * uses:
+ *
+ * int pdev->ifvers
+ * device interface version
+ *
+ * int nfp_ifvers
+ * interface version limit
+ *
+ * int nfp_alloc_pci_push( nfp_dev *pdev )
+ * allocates resources needed for PCI Push,
+ * if not already allocated, and return True if successful
+ *
+ * void nfp_free_pci_push( nfp_dev *pdev ) {
+ * frees any resources allocated to PCI Push
+ */
+
+void nfp_set_ifvers( nfp_dev *pdev, int vers ) {
+ if( nfp_ifvers != 0 && vers > nfp_ifvers ) {
+ nfp_log( NFP_DBG2,
+ "nfp_set_ifvers: can't set ifvers %d"
+ " as nfp_ifvers wants max ifvers %d",
+ vers, nfp_ifvers);
+ return;
+ }
+ if( vers >= NFDEV_IF_PCI_PUSH ) {
+ if(!nfp_alloc_pci_push(pdev)) {
+ nfp_log( NFP_DBG1,
+ "nfp_set_ifvers: can't set ifvers %d"
+ " as resources not available",
+ vers);
+ return;
+ }
+ } else {
+ nfp_free_pci_push(pdev);
+ }
+ pdev->ifvers= vers;
+ nfp_log( NFP_DBG3, "nfp_set_ifvers: setting ifvers %d", vers);
+}
diff --git a/usr/src/uts/common/io/nfp/nfp_osif.h b/usr/src/uts/common/io/nfp/nfp_osif.h
new file mode 100644
index 0000000000..17ffe469ce
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfp_osif.h
@@ -0,0 +1,105 @@
+/*
+
+nfp_osif.h: nCipher PCI HSM OS interface declarations
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+history
+
+10/10/2001 jsh Original
+
+*/
+
+#ifndef NFP_OSIF_H
+#define NFP_OSIF_H
+
+#include "nfp_hostif.h"
+#include "nfp_error.h"
+
+/* general typedefs ----------------------------------------------- */
+
+typedef volatile unsigned int reg32;
+typedef volatile unsigned short reg16;
+typedef volatile unsigned char reg8;
+
+/* sempaphores, mutexs and events --------------------------------- */
+
+#if 0
+extern nfp_err nfp_sema_init( nfp_sema *sema, int initial);
+extern void nfp_sema_destroy( nfp_sema *sema );
+extern void nfp_sema_post( nfp_sema *sema );
+extern void nfp_sema_wait( nfp_sema *sema );
+extern int nfp_sema_wait_sig( nfp_sema *sema );
+
+extern nfp_err nfp_mutex_init( nfp_mutex *mutex );
+extern void nfp_mutex_destroy( nfp_mutex *mutex );
+extern void nfp_mutex_enter( nfp_mutex *mutex );
+extern void nfp_mutex_exit( nfp_mutex *mutex );
+
+extern nfp_err nfp_event_init( nfp_event *event );
+extern void nfp_event_destroy( nfp_event *event );
+extern void nfp_event_set( nfp_event *event );
+extern void nfp_event_clear( nfp_event *event );
+extern void nfp_event_wait( nfp_event *event );
+extern void nfp_event_wait_sig( nfp_event *event );
+
+#endif
+
+/* timeouts ------------------------------------------------------ */
+
+extern void nfp_sleep( int ms );
+
+/* memory handling ----------------------------------------------- */
+
+#define KMALLOC_DMA 0
+#define KMALLOC_CACHED 1
+
+extern void *nfp_kmalloc( int size, int flags );
+extern void *nfp_krealloc( void *ptr, int size, int flags );
+extern void nfp_kfree( void * );
+
+/* config space access ------------------------------------------------ */
+
+/* return Little Endian 32 bit config register */
+extern nfp_err nfp_config_inl( nfp_cdev *pdev, int offset, unsigned int *res );
+
+/* io space access ------------------------------------------------ */
+
+extern unsigned int nfp_inl( nfp_cdev *pdev, int bar, int offset );
+extern unsigned short nfp_inw( nfp_cdev *pdev, int bar, int offset );
+extern void nfp_outl( nfp_cdev *pdev, int bar, int offset, unsigned int data );
+extern void nfp_outw( nfp_cdev *pdev, int bar, int offset, unsigned short data );
+
+/* user and device memory space access ---------------------------- */
+
+/* NB these 2 functions are not guarenteed to be re-entrant for a given device */
+extern nfp_err nfp_copy_from_user_to_dev( nfp_cdev *cdev, int bar, int offset, const char *ubuf, int len);
+extern nfp_err nfp_copy_to_user_from_dev( nfp_cdev *cdev, int bar, int offset, char *ubuf, int len);
+
+extern nfp_err nfp_copy_from_user( char *kbuf, const char *ubuf, int len );
+extern nfp_err nfp_copy_to_user( char *ubuf, const char *kbuf, int len );
+
+extern nfp_err nfp_copy_from_dev( nfp_cdev *cdev, int bar, int offset, char *kbuf, int len );
+extern nfp_err nfp_copy_to_dev( nfp_cdev *cdev, int bar, int offset, const char *kbuf, int len);
+
+/* debug ------------------------------------------------------------ */
+
+#define NFP_DBG1 1
+#define NFP_DBGE NFP_DBG1
+#define NFP_DBG2 2
+#define NFP_DBG3 3
+#define NFP_DBG4 4
+
+#ifdef STRANGE_VARARGS
+extern void nfp_log();
+#else
+extern void nfp_log( int severity, const char *format, ...);
+#endif
+
+extern int nfp_debug;
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/nfpci.h b/usr/src/uts/common/io/nfp/nfpci.h
new file mode 100644
index 0000000000..793f5995e6
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/nfpci.h
@@ -0,0 +1,171 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+/*
+*
+* NFPCI.H - nFast PCI interface definition file
+*
+*
+*
+* 1998.06.09 IH Started
+*
+* The interface presented by nFast PCI devices consists of:
+*
+* A region of shared RAM used for data transfer & control information
+* A doorbell interrupt register, so both sides can give each other interrupts
+* A number of DMA channels for transferring data
+*/
+
+#ifndef NFPCI_H
+#define NFPCI_H
+
+/* Sizes of some regions */
+#define NFPCI_RAM_MINSIZE 0x00100000
+/* This is the minimum size of shared RAM. In future it may be possible to
+ negotiate larger sizes of shared RAM or auto-detect how big it is */
+#define NFPCI_RAM_MINSIZE_JOBS 0x00020000 /* standard jobs only */
+#define NFPCI_RAM_MINSIZE_KERN 0x00040000 /* standard and kernel jobs */
+
+/* Offsets within shared memory space.
+ The following main regions are:
+ jobs input area
+ jobs output area
+ kernel jobs input area
+ kernel output area
+*/
+
+#define NFPCI_OFFSET_JOBS 0x00000000
+#define NFPCI_OFFSET_JOBS_WR 0x00000000
+#define NFPCI_OFFSET_JOBS_RD 0x00010000
+#define NFPCI_OFFSET_KERN 0x00020000
+#define NFPCI_OFFSET_KERN_WR 0x00020000
+#define NFPCI_OFFSET_KERN_RD 0x00030000
+
+/* Interrupts, defined by bit position in doorbell register */
+
+/* Interrupts from device to host */
+#define NFAST_INT_DEVICE_WRITE_OK 0x00000001
+#define NFAST_INT_DEVICE_WRITE_FAILED 0x00000002
+#define NFAST_INT_DEVICE_READ_OK 0x00000004
+#define NFAST_INT_DEVICE_READ_FAILED 0x00000008
+#define NFAST_INT_DEVICE_KERN_WRITE_OK 0x00000010
+#define NFAST_INT_DEVICE_KERN_WRITE_FAILED 0x00000020
+#define NFAST_INT_DEVICE_KERN_READ_OK 0x00000040
+#define NFAST_INT_DEVICE_KERN_READ_FAILED 0x00000080
+
+/* Interrupts from host to device */
+#define NFAST_INT_HOST_WRITE_REQUEST 0x00010000
+#define NFAST_INT_HOST_READ_REQUEST 0x00020000
+#define NFAST_INT_HOST_DEBUG 0x00040000
+#define NFAST_INT_HOST_KERN_WRITE_REQUEST 0x00080000
+#define NFAST_INT_HOST_KERN_READ_REQUEST 0x00100000
+
+/* Ordinary job submission ------------------------ */
+
+/* The NFPCI_OFFSET_JOBS_WR and NFPCI_OFFSET_JOBS_RD regions are defined
+ by the following (byte) address offsets... */
+
+#define NFPCI_OFFSET_CONTROL 0x0
+#define NFPCI_OFFSET_LENGTH 0x4
+#define NFPCI_OFFSET_DATA 0x8
+#define NFPCI_OFFSET_PUSH_ADDR 0x8
+
+#define NFPCI_JOBS_WR_CONTROL (NFPCI_OFFSET_JOBS_WR + NFPCI_OFFSET_CONTROL)
+#define NFPCI_JOBS_WR_LENGTH (NFPCI_OFFSET_JOBS_WR + NFPCI_OFFSET_LENGTH)
+#define NFPCI_JOBS_WR_DATA (NFPCI_OFFSET_JOBS_WR + NFPCI_OFFSET_DATA)
+#define NFPCI_MAX_JOBS_WR_LEN (0x0000FFF8)
+
+#define NFPCI_JOBS_RD_CONTROL (NFPCI_OFFSET_JOBS_RD + NFPCI_OFFSET_CONTROL)
+#define NFPCI_JOBS_RD_LENGTH (NFPCI_OFFSET_JOBS_RD + NFPCI_OFFSET_LENGTH)
+#define NFPCI_JOBS_RD_DATA (NFPCI_OFFSET_JOBS_RD + NFPCI_OFFSET_DATA)
+/* address in PCI space of host buffer for NFPCI_JOB_CONTROL_PCI_PUSH */
+#define NFPCI_JOBS_RD_PUSH_ADDR (NFPCI_OFFSET_JOBS_RD + NFPCI_OFFSET_PUSH_ADDR)
+#define NFPCI_MAX_JOBS_RD_LEN (0x000FFF8)
+
+/* Kernel inferface job submission ---------------- */
+
+#define NFPCI_KERN_WR_CONTROL (NFPCI_OFFSET_KERN_WR + NFPCI_OFFSET_CONTROL)
+#define NFPCI_KERN_WR_LENGTH (NFPCI_OFFSET_KERN_WR + NFPCI_OFFSET_LENGTH)
+#define NFPCI_KERN_WR_DATA (NFPCI_OFFSET_KERN_WR + NFPCI_OFFSET_DATA)
+#define NFPCI_MAX_KERN_WR_LEN (0x0000FFF8)
+
+#define NFPCI_KERN_RD_CONTROL (NFPCI_OFFSET_KERN_RD + NFPCI_OFFSET_CONTROL)
+#define NFPCI_KERN_RD_LENGTH (NFPCI_OFFSET_KERN_RD + NFPCI_OFFSET_LENGTH)
+#define NFPCI_KERN_RD_DATA (NFPCI_OFFSET_KERN_RD + NFPCI_OFFSET_DATA)
+/* address in PCI space of host buffer for NFPCI_JOB_CONTROL_PCI_PUSH */
+#define NFPCI_KERN_RD_ADDR (NFPCI_OFFSET_KERN_RD + NFPCI_OFFSET_PUSH_ADDR)
+#define NFPCI_MAX_KERN_RD_LEN (0x000FFF8)
+
+#ifdef DEFINE_NFPCI_PACKED_STRUCTS
+typedef struct
+{
+ UINT32 controlword;
+ UINT32 length; /* length of data to follow */
+ union {
+ BYTE data[1];
+ UINT32 addr;
+ } uu;
+}
+ NFPCI_JOBS_BLOCK;
+#endif
+
+
+#define NFPCI_JOB_CONTROL 0x00000001
+#define NFPCI_JOB_CONTROL_PCI_PUSH 0x00000002
+/*
+ The 'Control' word is analogous to the SCSI read/write address;
+ 1 = standard push/pull IO
+ 2 = push/push IO
+
+ To submit a block of job data, the host:
+ - sets the (32-bit, little-endian) word at NFPCI_JOBS_WR_CONTROL to NFPCI_JOB_CONTROL
+ - sets the word at NFPCI_JOBS_WR_LENGTH to the length of the data
+ - copies the data to NFPCI_JOBS_WR_DATA
+ - sets interrupt NFAST_INT_HOST_WRITE_REQUEST in the doorbell register
+ - awaits the NFAST_INT_DEVICE_WRITE_OK (or _FAILED) interrupts back
+
+ To read a block of jobs back, the host:
+ - sets the word at NFPCI_JOBS_RD_CONTROL to NFPCI_JOB_CONTROL
+ - sets the word at NFPCI_JOBS_RD_LENGTH to the max length for returned data
+ - sets interrupt NFAST_INT_HOST_READ_REQUEST
+ - awaits the NFAST_INT_DEVICE_READ_OK (or _FAILED) interrupt
+ - reads the data from NFPCI_JOBS_RD_DATA; the module will set the word at
+ NFPCI_JOBS_RD_LENGTH to its actual length.
+
+ Optionally the host can request the PCI read data to be pushed to host PCI mapped ram:
+ - allocates a contiguous PCI addressable buffer for a NFPCI_JOBS_BLOCK of max
+ size NFPCI_MAX_JOBS_RD_LEN (or NFPCI_MAX_KERN_RD_LEN) + 8
+ - sets the word at NFPCI_JOBS_RD_CONTROL to NFPCI_JOB_CONTROL_PCI_PUSH
+ - sets the word at NFPCI_JOBS_RD_LENGTH to the max length for returned data
+ - sets the word at NFPCI_JOBS_RD_PUSH_ADDR to be the host PCI address of
+ the buffer
+ - sets interrupt NFAST_INT_HOST_READ_REQUEST
+ - awaits the NFAST_INT_DEVICE_READ_OK (or _FAILED) interrupt
+ - reads the data from the buffer at NFPCI_OFFSET_DATA in the buffer. The
+ module will set NFPCI_OFFSET_LENGTH to the actual length.
+*/
+
+#define NFPCI_SCRATCH_CONTROL 0
+
+#define NFPCI_SCRATCH_CONTROL_HOST_MOI (1<<0)
+#define NFPCI_SCRATCH_CONTROL_MODE_SHIFT 1
+#define NFPCI_SCRATCH_CONTROL_MODE_MASK (3<<NFPCI_SCRATCH_CONTROL_MODE_SHIFT)
+
+#define NFPCI_SCRATCH_STATUS 1
+
+#define NFPCI_SCRATCH_STATUS_MONITOR_MOI (1<<0)
+#define NFPCI_SCRATCH_STATUS_APPLICATION_MOI (1<<1)
+#define NFPCI_SCRATCH_STATUS_APPLICATION_RUNNING (1<<2)
+#define NFPCI_SCRATCH_STATUS_ERROR (1<<3)
+
+#define NFPCI_SCRATCH_ERROR_LO 2
+#define NFPCI_SCRATCH_ERROR_HI 3
+
+#endif
diff --git a/usr/src/uts/common/io/nfp/osif.c b/usr/src/uts/common/io/nfp/osif.c
new file mode 100644
index 0000000000..fba62f9a37
--- /dev/null
+++ b/usr/src/uts/common/io/nfp/osif.c
@@ -0,0 +1,184 @@
+/*
+
+(C) Copyright nCipher Corporation Ltd 2002-2008 All rights reserved
+
+Copyright (c) 2008-2013 Thales e-Security All rights reserved
+
+Copyright (c) 2014 Thales UK All rights reserved
+
+*/
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/conf.h>
+#include <sys/uio.h>
+#include <sys/map.h>
+#include <sys/debug.h>
+#include <sys/modctl.h>
+#include <sys/kmem.h>
+#include <sys/cmn_err.h>
+#include <sys/open.h>
+#include <sys/stat.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/pci.h>
+
+#include "nfp_common.h"
+#include "nfp_hostif.h"
+#include "nfp_error.h"
+#include "nfp_osif.h"
+#include "nfp_cmd.h"
+#include "nfp.h"
+#include "autoversion.h"
+
+/* config space access ---------------------------------- */
+
+nfp_err nfp_config_inl( nfp_cdev *pdev, int offset, unsigned int *res ) {
+ unsigned int tmp32;
+ if ( !pdev || !pdev->dev || !pdev->dev->conf_handle )
+ return NFP_ENODEV;
+
+/* pci_config_get32() does byte swapping, so put back to LE */
+ tmp32 = pci_config_get32( pdev->dev->conf_handle, offset );
+ TO_LE32_IO(res, tmp32);
+
+ return NFP_SUCCESS;
+}
+
+/* user space memory access ---------------------------------- */
+
+nfp_err nfp_copy_from_user( char *kbuf, const char *ubuf, int len) {
+ bcopy(ubuf, kbuf, len);
+ return 0;
+}
+
+nfp_err nfp_copy_to_user( char *ubuf, const char *kbuf, int len) {
+ bcopy(kbuf, ubuf, len);
+ return 0;
+}
+
+nfp_err nfp_copy_from_user_to_dev( nfp_cdev *cdev, int bar, int offset, const char *ubuf, int len) {
+ /* dirty hack on Solaris, as we are called from strategy we are, in fact, copying from kernel mem */
+ return nfp_copy_to_dev( cdev, bar, offset, ubuf, len );
+}
+
+nfp_err nfp_copy_to_user_from_dev( nfp_cdev *cdev, int bar, int offset, char *ubuf, int len) {
+ /* dirty hack on Solaris, as we are called from strategy we are, in fact, copying to kernel mem */
+ return nfp_copy_from_dev( cdev, bar, offset, ubuf, len );
+}
+
+nfp_err nfp_copy_from_dev( nfp_cdev *cdev, int bar, int offset, char *kbuf, int len) {
+ if( len & 0x3 || offset & 0x3 )
+ DDI_REP_GET8( cdev->extra[bar], (unsigned char *)kbuf, cdev->bar[bar] + offset, len, DDI_DEV_AUTOINCR);
+ else
+ /* LINTED: alignment */
+ DDI_REP_GET32( cdev->extra[bar], (unsigned int *)kbuf, (unsigned int *)(cdev->bar[bar] + offset), len / 4, DDI_DEV_AUTOINCR);
+ return NFP_SUCCESS;
+}
+
+nfp_err nfp_copy_to_dev( nfp_cdev *cdev, int bar, int offset, const char *kbuf, int len) {
+ if( len & 0x3 || offset & 0x3 )
+ DDI_REP_PUT8( cdev->extra[bar], (unsigned char *)kbuf, cdev->bar[bar] + offset, len, DDI_DEV_AUTOINCR );
+ else
+ /* LINTED: alignment */
+ DDI_REP_PUT32( cdev->extra[bar], (unsigned int *)kbuf, (unsigned int *)(cdev->bar[bar] + offset), len / 4, DDI_DEV_AUTOINCR );
+ return NFP_SUCCESS;
+}
+
+/* pci io space access --------------------------------------- */
+
+unsigned int nfp_inl( nfp_cdev *pdev, int bar, int offset ) {
+ nfp_log( NFP_DBG3, "nfp_inl: addr %x", (uintptr_t) pdev->bar[bar] + offset);
+ /* LINTED: alignment */
+ return DDI_GET32( pdev->extra[bar], (uint32_t *)(pdev->bar[bar] + offset) );
+}
+
+unsigned short nfp_inw( nfp_cdev *pdev, int bar, int offset ) {
+ nfp_log( NFP_DBG3, "nfp_inw: addr %x", (uintptr_t) pdev->bar[bar] + offset);
+ /* LINTED: alignment */
+ return DDI_GET16( pdev->extra[bar], (unsigned short *)(pdev->bar[ bar ] + offset) );
+}
+
+void nfp_outl( nfp_cdev *pdev, int bar, int offset, unsigned int data ) {
+ nfp_log( NFP_DBG3, "nfp_outl: addr %x, data %x", (uintptr_t) pdev->bar[bar] + offset, data);
+ /* LINTED: alignment */
+ DDI_PUT32( pdev->extra[bar], (uint32_t *)(pdev->bar[ bar ] + offset), data );
+}
+
+void nfp_outw( nfp_cdev *pdev, int bar, int offset, unsigned short data ) {
+ nfp_log( NFP_DBG3, "nfp_outl: addr %x, data %x", (uintptr_t) pdev->bar[bar] + offset, data);
+ /* LINTED: alignment */
+ DDI_PUT16( pdev->extra[bar], (unsigned short *)(pdev->bar[ bar ] + offset), data );
+}
+
+/* logging ---------------------------------------------------- */
+
+void nfp_log( int level, const char *fmt, ...)
+{
+ auto char buf[256];
+ va_list ap;
+
+ switch (level) {
+ case NFP_DBG4: if (nfp_debug < 4) break;
+ /*FALLTHROUGH*/
+ case NFP_DBG3: if (nfp_debug < 3) break;
+ /*FALLTHROUGH*/
+ case NFP_DBG2: if (nfp_debug < 2) break;
+ /*FALLTHROUGH*/
+ case NFP_DBG1: if (nfp_debug < 1) break;
+ /*FALLTHROUGH*/
+ default:
+ va_start(ap, fmt);
+ (void) vsnprintf(buf, 256, fmt, ap);
+ va_end(ap);
+ cmn_err(CE_CONT, "!" VERSION_COMPNAME " " VERSION_NO ": %s\n", buf);
+ break;
+ }
+}
+
+struct errstr {
+ int oserr;
+ nfp_err nferr;
+};
+
+
+static struct errstr errtab[] = {
+ { EFAULT, NFP_EFAULT },
+ { ENOMEM, NFP_ENOMEM },
+ { EINVAL, NFP_EINVAL },
+ { EIO, NFP_EIO },
+ { ENXIO, NFP_ENXIO },
+ { ENODEV, NFP_ENODEV },
+ { EINVAL, NFP_EUNKNOWN },
+ { 0, 0 }
+};
+
+nfp_err nfp_error( int oserr )
+{
+ struct errstr *perr;
+ if(!oserr)
+ return 0;
+ perr= errtab;
+ while(perr->nferr) {
+ if(perr->oserr == oserr)
+ return perr->nferr;
+ perr++;
+ }
+ return NFP_EUNKNOWN;
+}
+
+int nfp_oserr( nfp_err nferr )
+{
+ struct errstr *perr;
+ if(nferr == NFP_SUCCESS)
+ return 0;
+ perr= errtab;
+ while(perr->nferr) {
+ if(perr->nferr == nferr)
+ return perr->oserr;
+ perr++;
+ }
+ return EIO;
+}
diff --git a/usr/src/uts/common/io/overlay/overlay.c b/usr/src/uts/common/io/overlay/overlay.c
index 51bb472c97..288f17ccb8 100644
--- a/usr/src/uts/common/io/overlay/overlay.c
+++ b/usr/src/uts/common/io/overlay/overlay.c
@@ -1108,7 +1108,8 @@ out:
mutex_enter(&odd->odd_lock);
overlay_io_done(odd, OVERLAY_F_IN_TX);
mutex_exit(&odd->odd_lock);
- return (mp_chain);
+ freemsgchain(mp_chain);
+ return (NULL);
}
/* ARGSUSED */
diff --git a/usr/src/uts/common/io/overlay/overlay_mux.c b/usr/src/uts/common/io/overlay/overlay_mux.c
index de669ce645..d847beb1c2 100644
--- a/usr/src/uts/common/io/overlay/overlay_mux.c
+++ b/usr/src/uts/common/io/overlay/overlay_mux.c
@@ -351,8 +351,16 @@ overlay_mux_tx(overlay_mux_t *mux, struct msghdr *hdr, mblk_t *mp)
/*
* It'd be nice to be able to use MSG_MBLK_QUICKRELE, unfortunately,
* that isn't actually supported by UDP at this time.
+ *
+ * Send with MSG_DONTWAIT to indicate clogged UDP sockets upstack.
+ */
+ ret = ksocket_sendmblk(mux->omux_ksock, hdr, MSG_DONTWAIT, &mp, kcred);
+ /*
+ * NOTE: ksocket_sendmblk() may send partial packets downstack,
+ * returning what's not sent in &mp (i.e. mp pre-call might be a
+ * b_cont of mp post-call). We can't hold up this message (it's a
+ * datagram), so we drop, and let the caller cope.
*/
- ret = ksocket_sendmblk(mux->omux_ksock, hdr, 0, &mp, kcred);
if (ret != 0)
freemsg(mp);
diff --git a/usr/src/uts/common/io/physmem.c b/usr/src/uts/common/io/physmem.c
index 665c9eff6c..9aaf58fb7b 100644
--- a/usr/src/uts/common/io/physmem.c
+++ b/usr/src/uts/common/io/physmem.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
@@ -807,6 +808,13 @@ physmem_open(dev_t *devp, int flag, int otyp, cred_t *credp)
int ret;
static int msg_printed = 0;
+ /*
+ * This device should never be visible in a zone, but if it somehow
+ * does get created we refuse to allow the zone to use it.
+ */
+ if (crgetzoneid(credp) != GLOBAL_ZONEID)
+ return (EACCES);
+
if ((flag & (FWRITE | FREAD)) != (FWRITE | FREAD)) {
return (EINVAL);
}
diff --git a/usr/src/uts/common/io/pseudo.conf b/usr/src/uts/common/io/pseudo.conf
index 42248e93d6..08affec609 100644
--- a/usr/src/uts/common/io/pseudo.conf
+++ b/usr/src/uts/common/io/pseudo.conf
@@ -22,8 +22,7 @@
#
# Copyright 2003 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright 2014 Joyent, Inc. All rights reserved.
#
# This file is private to the pseudonex driver. It should not be edited.
#
@@ -38,3 +37,9 @@ name="pseudo" class="root" instance=0;
# /pseudo; it has as its children the zone console pseudo nodes.
#
name="zconsnex" parent="/pseudo" instance=1 valid-children="zcons";
+
+#
+# zfdnex is an alias for pseudo; this node is instantiated as a child of
+# /pseudo; it has as its children the zone fd pseudo nodes.
+#
+name="zfdnex" parent="/pseudo" instance=2 valid-children="zfd";
diff --git a/usr/src/uts/common/io/ptm.c b/usr/src/uts/common/io/ptm.c
index 8127d54594..21d641992f 100644
--- a/usr/src/uts/common/io/ptm.c
+++ b/usr/src/uts/common/io/ptm.c
@@ -451,6 +451,18 @@ ptmclose(queue_t *rqp, int flag, cred_t *credp)
return (0);
}
+static boolean_t
+ptmptsopencb(ptmptsopencb_arg_t arg)
+{
+ struct pt_ttys *ptmp = (struct pt_ttys *)arg;
+ boolean_t rval;
+
+ PT_ENTER_READ(ptmp);
+ rval = (ptmp->pt_nullmsg != NULL);
+ PT_EXIT_READ(ptmp);
+ return (rval);
+}
+
/*
* The wput procedure will only handle ioctl and flush messages.
*/
@@ -587,6 +599,41 @@ ptmwput(queue_t *qp, mblk_t *mp)
miocack(qp, mp, 0, 0);
break;
}
+ case PTMPTSOPENCB:
+ {
+ mblk_t *dp; /* ioctl reply data */
+ ptmptsopencb_t *ppocb;
+
+ /* only allow the kernel to invoke this ioctl */
+ if (iocp->ioc_cr != kcred) {
+ miocnak(qp, mp, 0, EINVAL);
+ break;
+ }
+
+ /* we don't support transparent ioctls */
+ ASSERT(iocp->ioc_count != TRANSPARENT);
+ if (iocp->ioc_count == TRANSPARENT) {
+ miocnak(qp, mp, 0, EINVAL);
+ break;
+ }
+
+ /* allocate a response message */
+ dp = allocb(sizeof (ptmptsopencb_t), BPRI_MED);
+ if (dp == NULL) {
+ miocnak(qp, mp, 0, EAGAIN);
+ break;
+ }
+
+ /* initialize the ioctl results */
+ ppocb = (ptmptsopencb_t *)dp->b_rptr;
+ ppocb->ppocb_func = ptmptsopencb;
+ ppocb->ppocb_arg = (ptmptsopencb_arg_t)ptmp;
+
+ /* send the reply data */
+ mioc2ack(mp, dp, sizeof (ptmptsopencb_t), 0);
+ qreply(qp, mp);
+ break;
+ }
}
break;
diff --git a/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/iwarp_sm.jpg b/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/iwarp_sm.jpg
new file mode 100644
index 0000000000..b932ffaa7c
--- /dev/null
+++ b/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/iwarp_sm.jpg
Binary files differ
diff --git a/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-full-36.jpg b/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-full-36.jpg
new file mode 100644
index 0000000000..9421ecc0db
--- /dev/null
+++ b/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-full-36.jpg
Binary files differ
diff --git a/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-full.png b/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-full.png
new file mode 100644
index 0000000000..4b8a66761a
--- /dev/null
+++ b/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-full.png
Binary files differ
diff --git a/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-logo.png b/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-logo.png
new file mode 100644
index 0000000000..3254fbdc3b
--- /dev/null
+++ b/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/qlogic-logo.png
Binary files differ
diff --git a/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/reg_access.jpg b/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/reg_access.jpg
new file mode 100644
index 0000000000..7bb0dbf21b
--- /dev/null
+++ b/usr/src/uts/common/io/qede/579xx/drivers/ecore/documentation/pictures/reg_access.jpg
Binary files differ
diff --git a/usr/src/uts/common/io/qede/579xx/drivers/ecore/ecore_init_values.bin b/usr/src/uts/common/io/qede/579xx/drivers/ecore/ecore_init_values.bin
new file mode 100644
index 0000000000..43014fd8ea
--- /dev/null
+++ b/usr/src/uts/common/io/qede/579xx/drivers/ecore/ecore_init_values.bin
Binary files differ
diff --git a/usr/src/uts/common/io/qede/579xx/drivers/ecore/ecore_init_values_zipped.bin b/usr/src/uts/common/io/qede/579xx/drivers/ecore/ecore_init_values_zipped.bin
new file mode 100644
index 0000000000..9524eb4a63
--- /dev/null
+++ b/usr/src/uts/common/io/qede/579xx/drivers/ecore/ecore_init_values_zipped.bin
Binary files differ
diff --git a/usr/src/uts/common/io/qede/qede_list.h b/usr/src/uts/common/io/qede/qede_list.h
index 2350cb4117..656d2a915f 100644
--- a/usr/src/uts/common/io/qede/qede_list.h
+++ b/usr/src/uts/common/io/qede/qede_list.h
@@ -176,4 +176,3 @@ qede_list_splice_tail(qede_list_t *list,
#define QEDE_LIST_FOR_EACH_ENTRY_SAFE OSAL_LIST_FOR_EACH_ENTRY_SAFE
#endif /* !_QEDE_LIST_H */
-
diff --git a/usr/src/uts/common/io/qede/qede_version.h b/usr/src/uts/common/io/qede/qede_version.h
index 43584f95f0..0ee38b4338 100644
--- a/usr/src/uts/common/io/qede/qede_version.h
+++ b/usr/src/uts/common/io/qede/qede_version.h
@@ -42,4 +42,3 @@
#define REVVERSION 25
#endif /* !_QEDE_VERSION_H */
-
diff --git a/usr/src/uts/common/io/random.c b/usr/src/uts/common/io/random.c
index d79b86362c..a50bbcceec 100644
--- a/usr/src/uts/common/io/random.c
+++ b/usr/src/uts/common/io/random.c
@@ -291,6 +291,9 @@ rnd_write(dev_t dev, struct uio *uiop, cred_t *credp)
if ((error = uiomove(buf, bytes, UIO_WRITE, uiop)) != 0)
return (error);
+ if (crgetzone(credp) != global_zone)
+ continue;
+
switch (devno) {
case DEVRANDOM:
if ((error = random_add_entropy(buf, bytes, 0)) != 0)
diff --git a/usr/src/uts/common/io/rsm/rsm.c b/usr/src/uts/common/io/rsm/rsm.c
index b49d5b735a..d9d40c83fd 100644
--- a/usr/src/uts/common/io/rsm/rsm.c
+++ b/usr/src/uts/common/io/rsm/rsm.c
@@ -22,8 +22,8 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2012 Milan Jurik. All rights reserved.
- * Copyright (c) 2016 by Delphix. All rights reserved.
* Copyright 2017 Joyent, Inc.
+ * Copyright (c) 2016 by Delphix. All rights reserved.
*/
diff --git a/usr/src/uts/common/io/sata/adapters/ahci/ahci.c b/usr/src/uts/common/io/sata/adapters/ahci/ahci.c
index ead7433aef..d3ae7fb6c7 100644
--- a/usr/src/uts/common/io/sata/adapters/ahci/ahci.c
+++ b/usr/src/uts/common/io/sata/adapters/ahci/ahci.c
@@ -10783,6 +10783,7 @@ ahci_em_ioctl_set(ahci_ctl_t *ahci_ctlp, intptr_t arg)
}
task->aelta_ctl = ahci_ctlp;
+ task->aelta_port = set.aiems_port;
task->aelta_port = (uint8_t)set.aiems_port;
task->aelta_op = set.aiems_op;
task->aelta_state = set.aiems_leds;
diff --git a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mpt_sas.conf b/usr/src/uts/common/io/scsi/adapters/mpt_sas/mpt_sas.conf
index c6e017655e..721e66c276 100644
--- a/usr/src/uts/common/io/scsi/adapters/mpt_sas/mpt_sas.conf
+++ b/usr/src/uts/common/io/scsi/adapters/mpt_sas/mpt_sas.conf
@@ -21,7 +21,7 @@
#
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-#
+# Copyright 2019 Joyent, Inc.
#
#
@@ -49,3 +49,8 @@ ddi-vhci-class="scsi_vhci";
# name="mpt_sas" parent="/pci@7c0/pci@0/pci@9" unit-address="0" mpxio-disable="yes";
#
mpxio-disable="no";
+
+#
+# Command/target timeout checking should be done at a 1-second granularity.
+#
+scsi-watchdog-tick=1;
diff --git a/usr/src/uts/common/io/scsi/targets/sd.c b/usr/src/uts/common/io/scsi/targets/sd.c
index a759c735a3..2d25026a9a 100644
--- a/usr/src/uts/common/io/scsi/targets/sd.c
+++ b/usr/src/uts/common/io/scsi/targets/sd.c
@@ -3011,9 +3011,13 @@ sd_set_mmc_caps(sd_ssc_t *ssc)
* according to the successful response to the page
* 0x2A mode sense request.
*/
- scsi_log(SD_DEVINFO(un), sd_label, CE_WARN,
- "sd_set_mmc_caps: Mode Sense returned "
- "invalid block descriptor length\n");
+ /*
+ * The following warning occurs due to the KVM CD-ROM
+ * mishandling the multi-media commands. Ignore it.
+ * scsi_log(SD_DEVINFO(un), sd_label, CE_WARN,
+ * "sd_set_mmc_caps: Mode Sense returned "
+ * "invalid block descriptor length\n");
+ */
kmem_free(buf, BUFLEN_MODE_CDROM_CAP);
return;
}
@@ -3917,19 +3921,78 @@ static int
sd_sdconf_id_match(struct sd_lun *un, char *id, int idlen)
{
struct scsi_inquiry *sd_inq;
- int rval = SD_SUCCESS;
+ int rval = SD_SUCCESS;
+ char *p;
+ int chk_vidlen = 0, chk_pidlen = 0;
+ int has_tail = 0;
+ static const int VSZ = sizeof (sd_inq->inq_vid);
+ static const int PSZ = sizeof (sd_inq->inq_pid);
ASSERT(un != NULL);
sd_inq = un->un_sd->sd_inq;
ASSERT(id != NULL);
/*
- * We use the inq_vid as a pointer to a buffer containing the
- * vid and pid and use the entire vid/pid length of the table
- * entry for the comparison. This works because the inq_pid
- * data member follows inq_vid in the scsi_inquiry structure.
+ * We would like to use the inq_vid as a pointer to a buffer
+ * containing the vid and pid and use the entire vid/pid length of
+ * the table entry for the comparison. However, this does not work
+ * because, while the inq_pid data member follows inq_vid in the
+ * scsi_inquiry structure, we do not control the contents of this
+ * buffer, and some broken devices violate SPC 4.3.1 and return
+ * fields with null bytes in them.
+ */
+ chk_vidlen = MIN(VSZ, idlen);
+ p = id + chk_vidlen - 1;
+ while (*p == ' ' && chk_vidlen > 0) {
+ --p;
+ --chk_vidlen;
+ }
+
+ /*
+ * If it's all spaces, check the whole thing.
+ */
+ if (chk_vidlen == 0)
+ chk_vidlen = MIN(VSZ, idlen);
+
+ if (idlen > VSZ) {
+ chk_pidlen = idlen - VSZ;
+ p = id + idlen - 1;
+ while (*p == ' ' && chk_pidlen > 0) {
+ --p;
+ --chk_pidlen;
+ }
+ if (chk_pidlen == 0)
+ chk_pidlen = MIN(PSZ, idlen - VSZ);
+ }
+
+ /*
+ * There's one more thing we need to do here. If the user specified
+ * an ID with trailing spaces, we need to make sure the inquiry
+ * vid/pid has only spaces or NULs after the check length; otherwise, it
+ * can't match.
*/
- if (strncasecmp(sd_inq->inq_vid, id, idlen) != 0) {
+ if (idlen > chk_vidlen && chk_vidlen < VSZ) {
+ for (p = sd_inq->inq_vid + chk_vidlen;
+ p < sd_inq->inq_vid + VSZ; ++p) {
+ if (*p != ' ' && *p != '\0') {
+ ++has_tail;
+ break;
+ }
+ }
+ }
+ if (idlen > chk_pidlen + VSZ && chk_pidlen < PSZ) {
+ for (p = sd_inq->inq_pid + chk_pidlen;
+ p < sd_inq->inq_pid + PSZ; ++p) {
+ if (*p != ' ' && *p != '\0') {
+ ++has_tail;
+ break;
+ }
+ }
+ }
+
+ if (has_tail || strncasecmp(sd_inq->inq_vid, id, chk_vidlen) != 0 ||
+ (idlen > VSZ &&
+ strncasecmp(sd_inq->inq_pid, id + VSZ, chk_pidlen) != 0)) {
/*
* The user id string is compared to the inquiry vid/pid
* using a case insensitive comparison and ignoring
@@ -6142,7 +6205,7 @@ sdpower(dev_info_t *devi, int component, int level)
time_t intvlp;
struct pm_trans_data sd_pm_tran_data;
uchar_t save_state = SD_STATE_NORMAL;
- int sval;
+ int sval, tursval = 0;
uchar_t state_before_pm;
sd_ssc_t *ssc;
int last_power_level = SD_SPINDLE_UNINIT;
@@ -6426,13 +6489,26 @@ sdpower(dev_info_t *devi, int component, int level)
* a deadlock on un_pm_busy_cv will occur.
*/
if (SD_PM_IS_IO_CAPABLE(un, level)) {
- sval = sd_send_scsi_TEST_UNIT_READY(ssc,
+ tursval = sd_send_scsi_TEST_UNIT_READY(ssc,
SD_DONT_RETRY_TUR | SD_BYPASS_PM);
- if (sval != 0)
+ if (tursval != 0)
sd_ssc_assessment(ssc, SD_FMT_IGNORE);
}
- if (un->un_f_power_condition_supported) {
+ /*
+ * We've encountered certain classes of drives that pass a TUR, but fail
+ * the START STOP UNIT when using power conditions, or worse leave the
+ * drive in an unusable state despite passing SSU. Strictly speaking,
+ * for SPC-4 or greater, no additional actions are required to make the
+ * drive operational when a TUR passes. If we have something that
+ * matches this condition, we continue on and presume the drive is
+ * successfully powered on.
+ */
+ if (un->un_f_power_condition_supported &&
+ SD_SCSI_VERS_IS_GE_SPC_4(un) && SD_PM_IS_IO_CAPABLE(un, level) &&
+ level == SD_SPINDLE_ACTIVE && tursval == 0) {
+ sval = 0;
+ } else if (un->un_f_power_condition_supported) {
char *pm_condition_name[] = {"STOPPED", "STANDBY",
"IDLE", "ACTIVE"};
SD_TRACE(SD_LOG_IO_PM, un,
@@ -6452,6 +6528,7 @@ sdpower(dev_info_t *devi, int component, int level)
sd_ssc_assessment(ssc, SD_FMT_STATUS_CHECK);
else
sd_ssc_assessment(ssc, SD_FMT_IGNORE);
+
}
/* Command failed, check for media present. */
@@ -30373,7 +30450,7 @@ sd_set_unit_attributes(struct sd_lun *un, dev_info_t *devi)
if (SD_PM_CAPABLE_IS_UNDEFINED(pm_cap)) {
un->un_f_log_sense_supported = TRUE;
if (!un->un_f_power_condition_disabled &&
- SD_INQUIRY(un)->inq_ansi == 6) {
+ SD_SCSI_VERS_IS_GE_SPC_4(un)) {
un->un_f_power_condition_supported = TRUE;
}
} else {
@@ -30391,7 +30468,7 @@ sd_set_unit_attributes(struct sd_lun *un, dev_info_t *devi)
/* SD_PM_CAPABLE_IS_TRUE case */
un->un_f_pm_supported = TRUE;
if (!un->un_f_power_condition_disabled &&
- SD_PM_CAPABLE_IS_SPC_4(pm_cap)) {
+ (SD_PM_CAPABLE_IS_GE_SPC_4(pm_cap))) {
un->un_f_power_condition_supported =
TRUE;
}
diff --git a/usr/src/uts/common/io/signalfd.c b/usr/src/uts/common/io/signalfd.c
index 46d616dd79..4dce53e22c 100644
--- a/usr/src/uts/common/io/signalfd.c
+++ b/usr/src/uts/common/io/signalfd.c
@@ -107,6 +107,7 @@
#include <sys/schedctl.h>
#include <sys/id_space.h>
#include <sys/sdt.h>
+#include <sys/brand.h>
#include <sys/disp.h>
#include <sys/taskq_impl.h>
@@ -459,6 +460,9 @@ consume_signal(k_sigset_t set, uio_t *uio, boolean_t block)
lwp->lwp_extsig = 0;
mutex_exit(&p->p_lock);
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sigfd_translate)
+ BROP(p)->b_sigfd_translate(infop);
+
/* Convert k_siginfo into external, datamodel independent, struct. */
bzero(ssp, sizeof (*ssp));
ssp->ssi_signo = infop->si_signo;
diff --git a/usr/src/uts/common/io/vnd/frameio.c b/usr/src/uts/common/io/vnd/frameio.c
new file mode 100644
index 0000000000..d36608efcd
--- /dev/null
+++ b/usr/src/uts/common/io/vnd/frameio.c
@@ -0,0 +1,465 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+/*
+ * Frame I/O utility functions
+ */
+
+#include <sys/frameio.h>
+
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/sysmacros.h>
+#include <sys/inttypes.h>
+
+static kmem_cache_t *frameio_cache;
+
+int
+frameio_init(void)
+{
+ frameio_cache = kmem_cache_create("frameio_cache",
+ sizeof (frameio_t) + sizeof (framevec_t) * FRAMEIO_NVECS_MAX,
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ if (frameio_cache == NULL)
+ return (1);
+
+ return (0);
+}
+
+void
+frameio_fini(void)
+{
+ if (frameio_cache != NULL)
+ kmem_cache_destroy(frameio_cache);
+}
+
+frameio_t *
+frameio_alloc(int kmflags)
+{
+ return (kmem_cache_alloc(frameio_cache, kmflags));
+}
+
+void
+frameio_free(frameio_t *fio)
+{
+ kmem_cache_free(frameio_cache, fio);
+}
+
+/*
+ * Ensure that we don't see any garbage in the framevecs that we're nominally
+ * supposed to work with. Specifically we want to make sure that the buflen and
+ * the address are not zero.
+ */
+static int
+frameio_hdr_check_vecs(frameio_t *fio)
+{
+ int i;
+ for (i = 0; i < fio->fio_nvecs; i++)
+ if (fio->fio_vecs[i].fv_buf == NULL ||
+ fio->fio_vecs[i].fv_buflen == 0)
+ return (EINVAL);
+
+ return (0);
+}
+
+/*
+ * We have to copy in framevec32_t's. To work around the data model issues and
+ * trying not to copy memory we first copy in the framevec32_t data into the
+ * standard fio_vec space. Next we work backwards copying a given framevec32_t
+ * to a temporaory framevec_t and then overwrite the frameio_t's data. Note that
+ * it is important that we do this in reverse so as to ensure that we don't
+ * clobber data as the framevec_t is larger than the framevec32_t.
+ */
+static int
+frameio_hdr_copyin_ilp32(frameio_t *fio, const void *addr)
+{
+ framevec32_t *vec32p;
+ framevec_t fv;
+ int i;
+
+ vec32p = (framevec32_t *)&fio->fio_vecs[0];
+
+ if (ddi_copyin(addr, vec32p, sizeof (framevec32_t) * fio->fio_nvecs,
+ 0) != 0)
+ return (EFAULT);
+
+ for (i = fio->fio_nvecs - 1; i >= 0; i--) {
+ fv.fv_buf = (void *)(uintptr_t)vec32p[i].fv_buf;
+ fv.fv_buflen = vec32p[i].fv_buflen;
+ fv.fv_actlen = vec32p[i].fv_actlen;
+ fio->fio_vecs[i].fv_buf = fv.fv_buf;
+ fio->fio_vecs[i].fv_buflen = fv.fv_buflen;
+ fio->fio_vecs[i].fv_actlen = fv.fv_actlen;
+ }
+
+ return (frameio_hdr_check_vecs(fio));
+}
+
+/*
+ * Copy in a frame io header into fio with space for up to nvecs. If the frameio
+ * contains more vectors than specified it will be ignored. mode should contain
+ * information about the datamodel.
+ */
+int
+frameio_hdr_copyin(frameio_t *fio, int max_vecs, const void *addr, uint_t mode)
+{
+ int model = ddi_model_convert_from(mode & FMODELS);
+ int cpf = mode & FKIOCTL ? FKIOCTL : 0;
+ size_t fsize = model == DDI_MODEL_ILP32 ?
+ sizeof (frameio32_t) : sizeof (frameio_t);
+
+ /*
+ * The start of the header is the same in all data models for the
+ * current verison.
+ */
+ if (ddi_copyin(addr, fio, fsize, cpf) != 0)
+ return (EFAULT);
+
+ if (fio->fio_version != FRAMEIO_VERSION_ONE)
+ return (EINVAL);
+
+ if (fio->fio_nvecs > FRAMEIO_NVECS_MAX || fio->fio_nvecs == 0)
+ return (EINVAL);
+
+ if (fio->fio_nvpf == 0)
+ return (EINVAL);
+
+ if (fio->fio_nvecs % fio->fio_nvpf != 0)
+ return (EINVAL);
+
+ if (fio->fio_nvecs > max_vecs)
+ return (EOVERFLOW);
+
+ addr = (void *)((uintptr_t)addr + fsize);
+ if (model == DDI_MODEL_ILP32) {
+ if (cpf != 0)
+ return (EINVAL);
+ return (frameio_hdr_copyin_ilp32(fio, addr));
+ }
+
+ if (ddi_copyin(addr, &fio->fio_vecs[0],
+ sizeof (framevec_t) * fio->fio_nvecs, cpf) != 0)
+ return (EFAULT);
+
+ return (frameio_hdr_check_vecs(fio));
+}
+
+static mblk_t *
+frameio_allocb(size_t sz)
+{
+ mblk_t *mp;
+
+ mp = allocb(sz, 0);
+ if (mp == NULL)
+ return (NULL);
+
+ mp->b_datap->db_type = M_DATA;
+ return (mp);
+}
+
+static int
+framevec_mblk_read(framevec_t *fv, mblk_t **mpp, int cpf)
+{
+ mblk_t *mp;
+ cpf = cpf != 0 ? FKIOCTL : 0;
+
+ mp = frameio_allocb(fv->fv_buflen);
+
+ if (mp == NULL) {
+ freemsg(mp);
+ return (EAGAIN);
+ }
+
+ if (ddi_copyin(fv->fv_buf, mp->b_wptr, fv->fv_buflen,
+ cpf) != 0) {
+ freemsg(mp);
+ return (EFAULT);
+ }
+
+ mp->b_wptr += fv->fv_buflen;
+ *mpp = mp;
+ return (0);
+}
+
+/*
+ * Read a set of frame vectors that make up a single message boundary and return
+ * that as a single message in *mpp that consists of multiple data parts.
+ */
+static int
+frameio_mblk_read(frameio_t *fio, framevec_t *fv, mblk_t **mpp, int cpf)
+{
+ int nparts = fio->fio_nvpf;
+ int part, error;
+ mblk_t *mp;
+
+ *mpp = NULL;
+ cpf = cpf != 0 ? FKIOCTL : 0;
+
+ /*
+ * Construct the initial frame
+ */
+ for (part = 0; part < nparts; part++) {
+ error = framevec_mblk_read(fv, &mp, cpf);
+ if (error != 0) {
+ freemsg(*mpp);
+ return (error);
+ }
+
+ if (*mpp == NULL)
+ *mpp = mp;
+ else
+ linkb(*mpp, mp);
+ fv++;
+ }
+
+ return (0);
+}
+
+/*
+ * Read data from a series of frameio vectors into a message block chain. A
+ * given frameio request has a number of discrete messages divided into
+ * individual vectors based on fio->fio_nvcspframe. Each discrete message will
+ * be constructed into a message block chain pointed to by b_next.
+ *
+ * If we get an EAGAIN while trying to construct a given message block what we
+ * return depends on what else we've done so far. If we have succesfully
+ * completed at least one message then we free everything else we've done so
+ * far and return that. If no messages have been completed we return EAGAIN. If
+ * instead we encounter a different error, say EFAULT, then all of the fv_actlen
+ * entries values are undefined.
+ */
+int
+frameio_mblk_chain_read(frameio_t *fio, mblk_t **mpp, int *nvecs, int cpf)
+{
+ int error = ENOTSUP;
+ int nframes = fio->fio_nvecs / fio->fio_nvpf;
+ int frame;
+ framevec_t *fv;
+ mblk_t *mp, *bmp = NULL;
+
+ /*
+ * Protect against bogus kernel subsystems.
+ */
+ VERIFY(fio->fio_nvecs > 0);
+ VERIFY(fio->fio_nvecs % fio->fio_nvpf == 0);
+
+ *mpp = NULL;
+ cpf = cpf != 0 ? FKIOCTL : 0;
+
+ fv = &fio->fio_vecs[0];
+ for (frame = 0; frame < nframes; frame++) {
+ error = frameio_mblk_read(fio, fv, &mp, cpf);
+ if (error != 0)
+ goto failed;
+
+ if (bmp != NULL)
+ bmp->b_next = mp;
+ else
+ *mpp = mp;
+ bmp = mp;
+ }
+
+ *nvecs = nframes;
+ return (0);
+failed:
+ /*
+ * On EAGAIN we've already taken care of making sure that we have no
+ * leftover messages, eg. they were never linked in.
+ */
+ if (error == EAGAIN) {
+ if (frame != 0)
+ error = 0;
+ if (nvecs != NULL)
+ *nvecs = frame;
+ ASSERT(*mpp != NULL);
+ } else {
+ for (mp = *mpp; mp != NULL; mp = bmp) {
+ bmp = mp->b_next;
+ freemsg(mp);
+ }
+ if (nvecs != NULL)
+ *nvecs = 0;
+ *mpp = NULL;
+ }
+ return (error);
+}
+
+size_t
+frameio_frame_length(frameio_t *fio, framevec_t *fv)
+{
+ int i;
+ size_t len = 0;
+
+ for (i = 0; i < fio->fio_nvpf; i++, fv++)
+ len += fv->fv_buflen;
+
+ return (len);
+}
+
+/*
+ * Write a portion of an mblk to the current.
+ */
+static int
+framevec_write_mblk_part(framevec_t *fv, mblk_t *mp, size_t len, size_t moff,
+ size_t foff, int cpf)
+{
+ ASSERT(len <= MBLKL(mp) - moff);
+ ASSERT(len <= fv->fv_buflen - fv->fv_actlen);
+ cpf = cpf != 0 ? FKIOCTL : 0;
+
+ if (ddi_copyout(mp->b_rptr + moff, (caddr_t)fv->fv_buf + foff, len,
+ cpf) != 0)
+ return (EFAULT);
+ fv->fv_actlen += len;
+
+ return (0);
+}
+
+/*
+ * Because copying this out to the user might fail we don't want to update the
+ * b_rptr in case we need to copy it out again.
+ */
+static int
+framevec_map_blk(frameio_t *fio, framevec_t *fv, mblk_t *mp, int cpf)
+{
+ int err;
+ size_t msize, blksize, len, moff, foff;
+
+ msize = msgsize(mp);
+ if (msize > frameio_frame_length(fio, fv))
+ return (EOVERFLOW);
+
+ moff = 0;
+ foff = 0;
+ blksize = MBLKL(mp);
+ fv->fv_actlen = 0;
+ while (msize != 0) {
+ len = MIN(blksize, fv->fv_buflen - fv->fv_actlen);
+ err = framevec_write_mblk_part(fv, mp, len, moff, foff, cpf);
+ if (err != 0)
+ return (err);
+
+ msize -= len;
+ blksize -= len;
+ moff += len;
+ foff += len;
+
+ if (blksize == 0 && msize != 0) {
+ mp = mp->b_cont;
+ ASSERT(mp != NULL);
+ moff = 0;
+ blksize = MBLKL(mp);
+ }
+
+ if (fv->fv_buflen == fv->fv_actlen && msize != 0) {
+ fv++;
+ fv->fv_actlen = 0;
+ foff = 0;
+ }
+ }
+
+ return (0);
+}
+
+int
+frameio_mblk_chain_write(frameio_t *fio, frameio_write_mblk_map_t map,
+ mblk_t *mp, int *nwrite, int cpf)
+{
+ int mcount = 0;
+ int ret = 0;
+
+ if (map != MAP_BLK_FRAME)
+ return (EINVAL);
+
+ while (mp != NULL && mcount < fio->fio_nvecs) {
+ ret = framevec_map_blk(fio, &fio->fio_vecs[mcount], mp, cpf);
+ if (ret != 0)
+ break;
+ mcount += fio->fio_nvpf;
+ mp = mp->b_next;
+ }
+
+ if (ret != 0 && mcount == 0) {
+ if (nwrite != NULL)
+ *nwrite = 0;
+ return (ret);
+ }
+
+ if (nwrite != NULL)
+ *nwrite = mcount / fio->fio_nvpf;
+
+ return (0);
+}
+
+/*
+ * Copy out nframes worth of frameio header data back to userland.
+ */
+int
+frameio_hdr_copyout(frameio_t *fio, int nframes, void *addr, uint_t mode)
+{
+ int i;
+ int model = ddi_model_convert_from(mode & FMODELS);
+ framevec32_t *vec32p;
+ framevec32_t f;
+
+ if (fio->fio_nvecs / fio->fio_nvpf < nframes)
+ return (EINVAL);
+
+ fio->fio_nvecs = nframes * fio->fio_nvpf;
+
+ if (model == DDI_MODEL_NONE) {
+ if (ddi_copyout(fio, addr,
+ sizeof (frameio_t) + fio->fio_nvecs * sizeof (framevec_t),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+ return (0);
+ }
+
+ ASSERT(model == DDI_MODEL_ILP32);
+
+ vec32p = (framevec32_t *)&fio->fio_vecs[0];
+ for (i = 0; i < fio->fio_nvecs; i++) {
+ f.fv_buf = (caddr32_t)(uintptr_t)fio->fio_vecs[i].fv_buf;
+ if (fio->fio_vecs[i].fv_buflen > UINT_MAX ||
+ fio->fio_vecs[i].fv_actlen > UINT_MAX)
+ return (EOVERFLOW);
+ f.fv_buflen = fio->fio_vecs[i].fv_buflen;
+ f.fv_actlen = fio->fio_vecs[i].fv_actlen;
+ vec32p[i].fv_buf = f.fv_buf;
+ vec32p[i].fv_buflen = f.fv_buflen;
+ vec32p[i].fv_actlen = f.fv_actlen;
+ }
+
+ if (ddi_copyout(fio, addr,
+ sizeof (frameio32_t) + fio->fio_nvecs * sizeof (framevec32_t),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+ return (0);
+}
+
+void
+frameio_mark_consumed(frameio_t *fio, int nframes)
+{
+ int i;
+
+ ASSERT(fio->fio_nvecs / fio->fio_nvpf >= nframes);
+ for (i = 0; i < nframes * fio->fio_nvpf; i++)
+ fio->fio_vecs[i].fv_actlen = fio->fio_vecs[i].fv_buflen;
+}
diff --git a/usr/src/uts/common/io/vnd/vnd.c b/usr/src/uts/common/io/vnd/vnd.c
new file mode 100644
index 0000000000..8c05c8aee0
--- /dev/null
+++ b/usr/src/uts/common/io/vnd/vnd.c
@@ -0,0 +1,5857 @@
+/*
+ * 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 2019 Joyent, Inc.
+ */
+
+/*
+ * vnd - virtual (machine) networking datapath
+ *
+ * vnd's purpose is to provide a highly performant data path for Layer 2 network
+ * traffic and exist side by side an active IP netstack, each servicing
+ * different datalinks. vnd provides many of the same capabilities as the
+ * current TCP/IP stack does and some specific to layer two. Specifically:
+ *
+ * o Use of the DLD fastpath
+ * o Packet capture hooks
+ * o Ability to use hardware capabilities
+ * o Useful interfaces for handling multiple frames
+ *
+ * The following image shows where vnd fits into today's networking stack:
+ *
+ * +---------+----------+----------+
+ * | libdlpi | libvnd | libsocket|
+ * +---------+----------+----------+
+ * | · · VFS |
+ * | VFS · VFS +----------+
+ * | · | sockfs |
+ * +---------+----------+----------+
+ * | | VND | IP |
+ * | +----------+----------+
+ * | DLD/DLS |
+ * +-------------------------------+
+ * | MAC |
+ * +-------------------------------+
+ * | GLDv3 |
+ * +-------------------------------+
+ *
+ * -----------------------------------------
+ * A Tale of Two Devices - DDI Device Basics
+ * -----------------------------------------
+ *
+ * vnd presents itself to userland as a character device; however, it also is a
+ * STREAMS device so that it can interface with dld and the rest of the
+ * networking stack. Users never interface with the STREAMs devices directly and
+ * they are purely an implementation detail of vnd. Opening the STREAMS device
+ * require kcred and as such userland cannot interact with it or push it onto
+ * the stream head.
+ *
+ * The main vnd character device, /dev/vnd/ctl, is a self-cloning device. Every
+ * clone gets its own minor number; however, minor nodes are not created in the
+ * devices tree for these instances. In this state a user may do two different
+ * things. They may issue ioctls that affect global state or they may issue
+ * ioctls that try to attach it to a given datalink. Once a minor device has
+ * been attached to a datalink, all operations on it are scoped to that context,
+ * therefore subsequent global operations are not permitted.
+ *
+ * A given device can be linked into the /devices and /dev name space via a link
+ * ioctl. That ioctl causes a minor node to be created in /devices and then it
+ * will also appear under /dev/vnd/ due to vnd's sdev plugin. This is similar
+ * to, but simpler than, IP's persistence mechanism.
+ *
+ * ---------------------
+ * Binding to a datalink
+ * ---------------------
+ *
+ * Datalinks are backed by the dld (datalink device) and dls (datalink services)
+ * drivers. These drivers provide a STREAMS device for datalinks on the system
+ * which are exposed through /dev/net. Userland generally manipulates datalinks
+ * through libdlpi. When an IP interface is being plumbed up what actually
+ * happens is that someone does a dlpi_open(3DLPI) of the underlying datalink
+ * and then pushes on the ip STREAMS module with an I_PUSH ioctl. Modules may
+ * then can negotiate with dld and dls to obtain access to various capabilities
+ * and fast paths via a series of STREAMS messages.
+ *
+ * In vnd, we do the same thing, but we leave our STREAMS module as an
+ * implementation detail of the system. We don't want users to be able to
+ * arbitrarily push vnd STREAMS module onto any stream, so we explicitly require
+ * kcred to manipulate it. Thus, when a user issues a request to attach a
+ * datalink to a minor instance of the character device, that vnd minor instance
+ * itself does a layered open (ldi_open_by_name(9F)) of the specified datalink.
+ * vnd does that open using the passed in credentials from the ioctl, not kcred.
+ * This ensures that users who doesn't have permissions to open the device
+ * cannot. Once that's been opened, we push on the vnd streams module.
+ *
+ * Once the vnd STREAMS instance has been created for this device, eg. the
+ * I_PUSH ioctl returns, we explicitly send a STREAMS ioctl
+ * (VND_STRIOC_ASSOCIATE) to associate the vnd STREAMS and character devices.
+ * This association begins the STREAM device's initialization. We start up an
+ * asynchronous state machine that takes care of all the different aspects of
+ * plumbing up the device with dld and dls and enabling the MAC fast path. We
+ * need to guarantee to consumers of the character device that by the time their
+ * ioctl returns, the data path has been fully initialized.
+ *
+ * The state progression is fairly linear. There are two general steady states.
+ * The first is VND_S_ONLINE, which means that everything is jacked up and good
+ * to go. The alternative is VND_S_ZOMBIE, which means that the streams device
+ * encountered an error or we have finished tearing it down and the character
+ * device can clean it up. The following is our state progression and the
+ * meaning of each state:
+ *
+ * |
+ * |
+ * V
+ * +---------------+
+ * | VNS_S_INITIAL | This is our initial state. Every
+ * +---------------+ vnd STREAMS device starts here.
+ * | While in this state, only dlpi
+ * | M_PROTO and M_IOCTL messages can be
+ * | sent or received. All STREAMS based
+ * | data messages are dropped.
+ * | We transition out of this state by
+ * | sending a DL_INFO_REQ to obtain
+ * | information about the underlying
+ * | link.
+ * v
+ * +-----------------+
+ * +--<-| VNS_S_INFO_SENT | In this state, we verify and
+ * | +-----------------+ record information about the
+ * | | underlying device. If the device is
+ * | | not suitable, eg. not of type
+ * v | DL_ETHER, then we immediately
+ * | | become a ZOMBIE. To leave this
+ * | | state we request exclusive active
+ * | | access to the device via
+ * v | DL_EXCLUSIVE_REQ.
+ * | v
+ * | +----------------------+
+ * +--<-| VNS_S_EXCLUSIVE_SENT | In this state, we verify whether
+ * | +----------------------+ or not we were able to obtain
+ * | | | exclusive access to the device. If
+ * | | | we were not able to, then we leave,
+ * v | | as that means that something like
+ * | | | IP is already plumbed up on top of
+ * | | | the datalink. We leave this state
+ * | | | by progressing through to the
+ * | | | appropriate DLPI primitive, either
+ * v | | DLPI_ATTACH_REQ or DLPI_BIND_REQ
+ * | | | depending on the style of the
+ * | | | datalink.
+ * | | v
+ * | | +-------------------+
+ * +------ |--<-| VNS_S_ATTACH_SENT | In this state, we verify we were
+ * | | +-------------------+ able to perform a standard DLPI
+ * | | | attach and if so, go ahead and
+ * v | | send a DLPI_BIND_REQ.
+ * | v v
+ * | +-------------------+
+ * +--<-| VNS_S_BIND_SENT | In this state we see the result of
+ * | +-------------------+ our attempt to bind to PPA 0 of the
+ * v | underlying device. Because we're
+ * | | trying to be a layer two datapath,
+ * | | the specific attachment point isn't
+ * | | too important as we're going to
+ * v | have to enable promiscuous mode. We
+ * | | transition out of this by sending
+ * | | our first of three promiscuous mode
+ * | | requests.
+ * v v
+ * | +------------------------+
+ * +--<-| VNS_S_SAP_PROMISC_SENT | In this state we verify that we
+ * | +------------------------+ were able to enable promiscuous
+ * | | mode at the physical level. We
+ * | | transition out of this by enabling
+ * | | multicast and broadcast promiscuous
+ * v | mode.
+ * | v
+ * | +--------------------------+
+ * +--<-| VNS_S_MULTI_PROMISC_SENT | In this state we verify that we
+ * | +--------------------------+ have enabled DL_PROMISC_MULTI and
+ * v | move onto the second promiscuous
+ * | | mode request.
+ * | v
+ * | +----------------------------+
+ * +--<-| VNS_S_RX_ONLY_PROMISC_SENT | In this state we verify that we
+ * | +----------------------------+ enabled RX_ONLY promiscuous mode.
+ * | | We specifically do this as we don't
+ * v | want to receive our own traffic
+ * | | that we'll send out. We leave this
+ * | | state by enabling the final flag
+ * | | DL_PROMISC_FIXUPS.
+ * | v
+ * | +--------------------------+
+ * +--<-| VNS_S_FIXUP_PROMISC_SENT | In this state we verify that we
+ * | +--------------------------+ enabled FIXUP promiscuous mode.
+ * | | We specifically do this as we need
+ * v | to ensure that traffic which is
+ * | | received by being looped back to us
+ * | | correctly has checksums fixed. We
+ * | | leave this state by requesting the
+ * | | dld/dls capabilities that we can
+ * v | process.
+ * | v
+ * | +--------------------+
+ * +--<-| VNS_S_CAPAB_Q_SENT | We loop over the set of
+ * | +--------------------+ capabilities that dld advertised
+ * | | and enable the ones that currently
+ * v | support for use. See the section
+ * | | later on regarding capabilities
+ * | | for more information. We leave this
+ * | | state by sending an enable request.
+ * v v
+ * | +--------------------+
+ * +--<-| VNS_S_CAPAB_E_SENT | Here we finish all capability
+ * | +--------------------+ initialization. Once finished, we
+ * | | transition to the next state. If
+ * v | the dld fast path is not available,
+ * | | we become a zombie.
+ * | v
+ * | +--------------+
+ * | | VNS_S_ONLINE | This is a vnd STREAMS device's
+ * | +--------------+ steady state. It will normally
+ * | | reside in this state while it is in
+ * | | active use. It will only transition
+ * v | to the next state when the STREAMS
+ * | | device is closed by the character
+ * | | device. In this state, all data
+ * | | flows over the dld fast path.
+ * | v
+ * | +---------------------+
+ * +--->| VNS_S_SHUTTING_DOWN | This vnd state takes care of
+ * | +---------------------+ disabling capabilities and
+ * | | flushing all data. At this point
+ * | | any additional data that we receive
+ * | | will be dropped. We leave this
+ * v | state by trying to remove multicast
+ * | | promiscuity.
+ * | |
+ * | v
+ * | +---------------------------------+
+ * +-->| VNS_S_MULTICAST_PROMISCOFF_SENT | In this state, we check if we have
+ * | +---------------------------------+ successfully removed multicast
+ * | | promiscuous mode. If we have
+ * | | failed, we still carry on but only
+ * | | warn. We leave this state by trying
+ * | | to disable SAP level promiscuous
+ * | | mode.
+ * | v
+ * | +---------------------------+
+ * +-->| VNS_S_SAP_PROMISCOFF_SENT | In this state, we check if we have
+ * | +---------------------------+ successfully removed SAP level
+ * | | promiscuous mode. If we have
+ * | | failed, we still carry on but only
+ * | | warn. Note that we don't worry
+ * | | about either of
+ * | | DL_PROMISC_FIXUPS or
+ * | | DL_PROMISC_RX_ONLY. If these are
+ * | | the only two entries left, then we
+ * | | should have anything that MAC is
+ * | | doing for us at this point,
+ * | | therefore it's safe for us to
+ * | | proceed to unbind, which is how we
+ * | | leave this state via a
+ * | v DL_UNBIND_REQ.
+ * | +-------------------+
+ * +--->| VNS_S_UNBIND_SENT | Here, we check how the unbind
+ * | +-------------------+ request went. Regardless of its
+ * | | success, we always transition to
+ * | | a zombie state.
+ * | v
+ * | +--------------+
+ * +--->| VNS_S_ZOMBIE | In this state, the vnd STREAMS
+ * +--------------+ device is waiting to finish being
+ * reaped. Because we have no more
+ * ways to receive data it should be
+ * safe to destroy all remaining data
+ * structures.
+ *
+ * If the stream association fails for any reason the state machine reaches
+ * VNS_S_ZOMBIE. A more detailed vnd_errno_t will propagate back through the
+ * STREAMS ioctl to the character device. That will fail the user ioctl and
+ * propagate the vnd_errno_t back to userland. If, on the other hand, the
+ * association succeeds, then the vnd STREAMS device will be fully plumbed up
+ * and ready to transmit and receive message blocks. Consumers will be able to
+ * start using the other cbops(9E) entry points once the attach has fully
+ * finished, which will occur after the original user attach ioctl to the
+ * character device returns.
+ *
+ * It's quite important that we end up sending the full series of STREAMS
+ * messages when tearing down. While it's tempting to say that we should just
+ * rely on the STREAMS device being closed to properly ensure that we have no
+ * more additional data, that's not sufficient due to our use of direct
+ * callbacks. DLS does not ensure that by the time we change the direct
+ * callback (vnd_mac_input) that all callers to it will have been quiesced.
+ * However, it does guarantee that if we disable promiscuous mode ourselves and
+ * we turn off the main data path via DL_UNBIND_REQ that it will work.
+ * Therefore, we make sure to do this ourselves rather than letting DLS/DLD do
+ * it as part of tearing down the STREAMS device. This ensures that we'll
+ * quiesce all data before we destroy our data structures and thus we should
+ * eliminate the race in changing the data function.
+ *
+ * --------------------
+ * General Architecture
+ * --------------------
+ *
+ * There are several different devices and structures in the vnd driver. There
+ * is a per-netstack component, pieces related to the character device that
+ * consumers see, the internal STREAMS device state, and the data queues
+ * themselves. The following ASCII art picture describes their relationships and
+ * some of the major pieces of data that contain them. These are not exhaustive,
+ * e.g. synchronization primitives are left out.
+ *
+ * +----------------+ +-----------------+
+ * | global | | global |
+ * | device list | | netstack list |
+ * | vnd_dev_list | | vnd_nsd_list |
+ * +----------------+ +-----------------+
+ * | |
+ * | v
+ * | +-------------------+ +-------------------+
+ * | | per-netstack data | ---> | per-netstack data | --> ...
+ * | | vnd_pnsd_t | | vnd_pnsd_t |
+ * | | | +-------------------+
+ * | | |
+ * | | nestackid_t ---+----> Netstack ID
+ * | | vnd_pnsd_flags_t -+----> Status flags
+ * | | zoneid_t ---+----> Zone ID for this netstack
+ * | | hook_family_t ---+----> VND IPv4 Hooks
+ * | | hook_family_t ---+----> VND IPv6 Hooks
+ * | | list_t ----+ |
+ * | +------------+------+
+ * | |
+ * | v
+ * | +------------------+ +------------------+
+ * | | character device | ---> | character device | -> ...
+ * +---------->| vnd_dev_t | | vnd_dev_t |
+ * | | +------------------+
+ * | |
+ * | minor_t ---+--> device minor number
+ * | ldi_handle_t ---+--> handle to /dev/net/%datalink
+ * | vnd_dev_flags_t -+--> device flags, non blocking, etc.
+ * | char[] ---+--> name if linked
+ * | vnd_str_t * -+ |
+ * +--------------+---+
+ * |
+ * v
+ * +-------------------------+
+ * | STREAMS device |
+ * | vnd_str_t |
+ * | |
+ * | vnd_str_state_t ---+---> State machine state
+ * | gsqueue_t * ---+---> mblk_t Serialization queue
+ * | vnd_str_stat_t ---+---> per-device kstats
+ * | vnd_str_capab_t ---+----------------------------+
+ * | vnd_data_queue_t ---+ | |
+ * | vnd_data_queue_t -+ | | v
+ * +-------------------+-+---+ +---------------------+
+ * | | | Stream capabilities |
+ * | | | vnd_str_capab_t |
+ * | | | |
+ * | | supported caps <--+-- vnd_capab_flags_t |
+ * | | dld cap handle <--+-- void * |
+ * | | direct tx func <--+-- vnd_dld_tx_t |
+ * | | +---------------------+
+ * | |
+ * +----------------+ +-------------+
+ * | |
+ * v v
+ * +-------------------+ +-------------------+
+ * | Read data queue | | Write data queue |
+ * | vnd_data_queue_t | | vnd_data_queue_t |
+ * | | | |
+ * | size_t ----+--> Current size | size_t ----+--> Current size
+ * | size_t ----+--> Max size | size_t ----+--> Max size
+ * | mblk_t * ----+--> Queue head | mblk_t * ----+--> Queue head
+ * | mblk_t * ----+--> Queue tail | mblk_t * ----+--> Queue tail
+ * +-------------------+ +-------------------+
+ *
+ *
+ * Globally, we maintain two lists. One list contains all of the character
+ * device soft states. The other maintains a list of all our netstack soft
+ * states. Each netstack maintains a list of active devices that have been
+ * associated with a datalink in its netstack.
+ *
+ * Recall that a given minor instance of the character device exists in one of
+ * two modes. It can either be a cloned open of /dev/vnd/ctl, the control node,
+ * or it can be associated with a given datalink. When minor instances are in
+ * the former state, they do not exist in a given vnd_pnsd_t's list of devices.
+ * As part of attaching to a datalink, the given vnd_dev_t will be inserted into
+ * the appropriate vnd_pnsd_t. In addition, this will cause a STREAMS device, a
+ * vnd_str_t, to be created and associated to a vnd_dev_t.
+ *
+ * The character device, and its vnd_dev_t, is the interface to the rest of the
+ * system. The vnd_dev_t keeps track of various aspects like whether various
+ * operations, such as read, write and the frameio ioctls, are considered
+ * blocking or non-blocking in the O_NONBLOCK sense. It also is responsible for
+ * keeping track of things like the name of the device, if any, in /dev. The
+ * vnd_str_t, on the other hand manages aspects like buffer sizes and the actual
+ * data queues. However, ioctls that manipulate these properties all go through
+ * the vnd_dev_t to its associated vnd_str_t.
+ *
+ * Each of the STREAMS devices, the vnd_str_t, maintains two data queues. One
+ * for frames to transmit (write queue) and one for frames received (read
+ * queue). These data queues have a maximum size and attempting to add data
+ * beyond that maximum size will result in data being dropped. The sizes are
+ * configurable via ioctls VND_IOC_SETTXBUF, VND_IOC_SETRXBUF. Data either sits
+ * in those buffers or has a reservation in those buffers while they are in vnd
+ * and waiting to be consumed by the user or by mac.
+ *
+ * Finally, the vnd_str_t also has a vnd_str_capab_t which we use to manage the
+ * available, negotiated, and currently active features.
+ *
+ * ----------------------
+ * Data Path and gsqueues
+ * ----------------------
+ *
+ * There's a lot of plumbing in vnd to get to the point where we can send data,
+ * but vnd's bread and butter is the data path, so it's worth diving into it in
+ * more detail. Data enters and exits the system from two ends.
+ *
+ * The first end is the vnd consumer. This comes in the form of read and write
+ * system calls as well as the frame I/O ioctls. The read and write system calls
+ * operate on a single frame at a time. Think of a frame as a single message
+ * that has come in off the wire, which may itself comprise multiple mblk_t's
+ * linked together in the kernel. readv(2) and writev(2) have the same
+ * limitations as read(2) and write(2). We enforce this as the system is
+ * required to fill up every uio(9S) buffer before moving onto the next one.
+ * This means that if you have a MTU sized buffer and two frames come in which
+ * are less than half of the MTU they must fill up the given iovec. Even if we
+ * didn't want to do this, we have no way of informing the supplier of the
+ * iovecs that they were only partially filled or where one frame ends and
+ * another begins. That's life, as such we have frame I/O which solves this
+ * problem. It allows for multiple frames to be consumed as well as for frames
+ * to be broken down into multiple vector components.
+ *
+ * The second end is the mac direct calls. As part of negotiating capabilities
+ * via dld, we give mac a function of ours to call when packets are received
+ * [vnd_mac_input()] and a callback to indicate that flow has been restored
+ * [vnd_mac_flow_control()]. In turn, we also get a function pointer that we can
+ * transmit data with. As part of the contract with mac, mac is allowed to flow
+ * control us by returning a cookie to the transmit function. When that happens,
+ * all outbound traffic is halted until our callback function is called and we
+ * can schedule drains.
+ *
+ * It's worth looking at these in further detail. We'll start with the rx path.
+ *
+ *
+ * |
+ * * . . . packets from gld
+ * |
+ * v
+ * +-------------+
+ * | mac |
+ * +-------------+
+ * |
+ * v
+ * +-------------+
+ * | dld |
+ * +-------------+
+ * |
+ * * . . . dld direct callback
+ * |
+ * v
+ * +---------------+
+ * | vnd_mac_input |
+ * +---------------+
+ * |
+ * v
+ * +---------+ +-------------+
+ * | dropped |<--*---------| vnd_hooks |
+ * | by | . +-------------+
+ * | hooks | . drop probe |
+ * +---------+ kstat bump * . . . Do we have free
+ * | buffer space?
+ * |
+ * no . | . yes
+ * . + .
+ * +---*--+------*-------+
+ * | |
+ * * . . drop probe * . . recv probe
+ * | kstat bump | kstat bump
+ * v |
+ * +---------+ * . . fire pollin
+ * | freemsg | v
+ * +---------+ +-----------------------+
+ * | vnd_str_t`vns_dq_read |
+ * +-----------------------+
+ * ^ ^
+ * +----------+ | | +---------+
+ * | read(9E) |-->-+ +--<--| frameio |
+ * +----------+ +---------+
+ *
+ * The rx path is rather linear. Packets come into us from mac. We always run
+ * them through the various hooks, and if they come out of that, we inspect the
+ * read data queue. If there is not enough space for a packet, we drop it.
+ * Otherwise, we append it to the data queue, and fire read notifications
+ * targetting anyone polling or doing blocking I/O on this device. Those
+ * consumers then drain the head of the data queue.
+ *
+ * The tx path is more complicated due to mac flow control. After any call into
+ * mac, we may have to potentially suspend writes and buffer data for an
+ * arbitrary amount of time. As such, we need to carefully track the total
+ * amount of outstanding data so that we don't waste kernel memory. This is
+ * further complicated by the fact that mac will asynchronously tell us when our
+ * flow has been resumed.
+ *
+ * For data to be able to enter the system, it needs to be able to take a
+ * reservation from the write data queue. Once the reservation has been
+ * obtained, we enter the gsqueue so that we can actually append it. We use
+ * gsqueues (serialization queues) to ensure that packets are manipulated in
+ * order as we deal with the draining and appending packets. We also leverage
+ * its worker thread to help us do draining after mac has restorted our flow.
+ *
+ * The following image describes the flow:
+ *
+ * +-----------+ +--------------+ +-------------------------+ +------+
+ * | write(9E) |-->| Space in the |--*--->| gsqueue_enter_one() |-->| Done |
+ * | frameio | | write queue? | . | +->vnd_squeue_tx_append | +------+
+ * +-----------+ +--------------+ . +-------------------------+
+ * | ^ .
+ * | | . reserve space from gsqueue
+ * | | |
+ * queue . . . * | space v
+ * full | * . . . avail +------------------------+
+ * v | | vnd_squeue_tx_append() |
+ * +--------+ +------------+ +------------------------+
+ * | EAGAIN |<--*------| Non-block? |<-+ |
+ * +--------+ . +------------+ | v
+ * . yes v | wait +--------------+
+ * no . .* * . . for | append chain |
+ * +----+ space | to outgoing |
+ * | mblk chain |
+ * from gsqueue +--------------+
+ * | |
+ * | +-------------------------------------------------+
+ * | |
+ * | | yes . . .
+ * v v .
+ * +-----------------------+ +--------------+ . +------+
+ * | vnd_squeue_tx_drain() |--->| mac blocked? |----*---->| Done |
+ * +-----------------------+ +--------------+ +------+
+ * | |
+ * +---------------------------------|---------------------+
+ * | | tx |
+ * | no . . * queue . . *
+ * | flow controlled . | empty * . fire pollout
+ * | . v | if mblk_t's
+ * +-------------+ . +---------------------+ | sent
+ * | set blocked |<----*------| vnd_squeue_tx_one() |--------^-------+
+ * | flags | +---------------------+ |
+ * +-------------+ More data | | | More data |
+ * and limit ^ v * . . and limit ^
+ * not reached . . * | | reached |
+ * +----+ | |
+ * v |
+ * +----------+ +-------------+ +---------------------------+
+ * | mac flow |--------->| remove mac |--->| gsqueue_enter_one() with |
+ * | control | | block flags | | vnd_squeue_tx_drain() and |
+ * | callback | +-------------+ | GSQUEUE_FILL flag, iff |
+ * +----------+ | not already scheduled |
+ * +---------------------------+
+ *
+ * The final path taken for a given write(9E)/frameio ioctl depends on whether
+ * or not the vnd_dev_t is non-blocking. That controls the initial path of
+ * trying to take a reservation in write data queue. If the device is in
+ * non-blocking mode, we'll return EAGAIN when there is not enough space
+ * available, otherwise, the calling thread blocks on the data queue.
+ *
+ * Today when we call into vnd_squeue_tx_drain() we will not try to drain the
+ * entire queue, as that could be quite large and we don't want to necessarily
+ * keep the thread that's doing the drain until it's been finished. Not only
+ * could more data be coming in, but the draining thread could be a userland
+ * thread that has more work to do. We have two limits today. There is an upper
+ * bound on the total amount of data and the total number of mblk_t chains. If
+ * we hit either limit, then we will schedule another drain in the gsqueue and
+ * go from there.
+ *
+ * It's worth taking some time to describe how we interact with gsqueues. vnd
+ * has a gsqueue_set_t for itself. It's important that it has its own set, as
+ * the profile of work that vnd does is different from other sub-systems in the
+ * kernel. When we open a STREAMS device in vnd_s_open, we get a random gsqueue.
+ * Unlike TCP/IP which uses an gsqueue for per TCP connection, we end up
+ * maintaining one for a given device. Because of that, we want to use a
+ * pseudo-random one to try and spread out the load, and picking one at random
+ * is likely to be just as good as any fancy algorithm we might come up with,
+ * especially as any two devices could have radically different transmit
+ * profiles.
+ *
+ * While some of the write path may seem complicated, it does allow us to
+ * maintain an important property. Once we have acknowledged a write(9E) or
+ * frameio ioctl, we will not drop the packet, excepting something like ipf via
+ * the firewall hooks.
+ *
+ * There is one other source of flow control that can exist in the system which
+ * is in the form of a barrier. The barrier is an internal mechanism used for
+ * ensuring that an gsqueue is drained for a given device. We use this as part
+ * of tearing down. Specifically we disable the write path so nothing new can be
+ * inserted into the gsqueue and then insert a barrier block. Once the barrier
+ * block comes out of the gsqueue, then we know nothing else in the gsqueue that
+ * could refer to the vnd_str_t, being destroyed, exists.
+ *
+ * ---------------------
+ * vnd, zones, netstacks
+ * ---------------------
+ *
+ * vnd devices are scoped to datalinks and datalinks are scoped to a netstack.
+ * Because of that, vnd is also a netstack module. It registers with the
+ * netstack sub-system and receives callbacks every time a netstack is created,
+ * being shutdown, and destroyed. The netstack callbacks drive the creation and
+ * destruction of the vnd_pnsd_t structures.
+ *
+ * Recall from the earlier architecture diagrams that every vnd device is scoped
+ * to a netstack and known about by a given vnd_pnsd_t. When that netstack is
+ * torn down, we also tear down any vnd devices that are hanging around. When
+ * the netstack is torn down, we know that any zones that are scoped to that
+ * netstack are being shut down and have no processes remaining. This is going
+ * to be the case whether they are shared or exclusive stack zones. We have to
+ * perform a careful dance.
+ *
+ * There are two different callbacks that happen on tear down, the first is a
+ * shutdown callback, the second is a destroy callback. When the shutdown
+ * callback is fired we need to prepare for the netstack to go away and ensure
+ * that nothing can continue to persist itself.
+ *
+ * More specifically, when we get notice of a stack being shutdown we first
+ * remove the netstack from the global netstack list to ensure that no one new
+ * can come in and find the netstack and get a reference to it. After that, we
+ * notify the neti hooks that they're going away. Once that's all done, we get
+ * to the heart of the matter.
+ *
+ * When shutting down there could be any number of outstanding contexts that
+ * have a reference on the vnd_pnsd_t and on the individual links. However, we
+ * know that no one new will be able to find the vnd_pnsd_t. To account for
+ * things that have existing references we mark the vnd_pnsd_t`vpnd_flags with
+ * VND_NS_CONDEMNED. This is checked by code paths that wish to append a device
+ * to the netstack's list. If this is set, then they must not append to it.
+ * Once this is set, we know that the netstack's list of devices can never grow,
+ * only shrink.
+ *
+ * Next, for each device we tag it with VND_D_ZONE_DYING. This indicates that
+ * the container for the device is being destroyed and that we should not allow
+ * additional references to the device to be created, whether via open, or
+ * linking. The presence of this bit also allows things like the list ioctl and
+ * sdev to know not to consider its existence. At the conclusion of this being
+ * set, we know that no one else should be able to obtain a new reference to the
+ * device.
+ *
+ * Once that has been set for all devices, we go through and remove any existing
+ * links that have been established in sdev. Because doing that may cause the
+ * final reference for the device to be dropped, which still has a reference to
+ * the netstack, we have to restart our walk due to dropped locks. We know that
+ * this walk will eventually complete because the device cannot be relinked and
+ * no new devices will be attached in this netstack due to VND_NS_CONDEMNED.
+ * Once that's finished, the shutdown callback returns.
+ *
+ * When we reach the destroy callback, we simply wait for references on the
+ * netstack to disappear. Because the zone has been shut down, all processes in
+ * it that have open references have been terminated and reaped. Any threads
+ * that are newly trying to reference it will fail. However, there is one thing
+ * that can halt this that we have no control over, which is the global zone
+ * holding open a reference to the device. In this case the zone halt will hang
+ * in vnd_stack_destroy. Once the last references is dropped we finish destroy
+ * the netinfo hooks and free the vnd_pnsd_t.
+ *
+ * ----
+ * sdev
+ * ----
+ *
+ * vnd registers a sdev plugin which allows it to dynamically fill out /dev/vnd
+ * for both the global and non-global zones. In any given zone we always supply
+ * a control node via /dev/vnd/ctl. This is the self-cloning node. Each zone
+ * will also have an entry per-link in that zone under /dev/vnd/%datalink, eg.
+ * if a link was named net0, there would be a /dev/vnd/net0. The global zone can
+ * also see every link for every zone, ala /dev/net, under
+ * /dev/vnd/%zonename/%datalink, eg. if a zone named 'turin' had a vnd device
+ * named net0, the global zone would have /dev/vnd/turin/net0.
+ *
+ * The sdev plugin has three interfaces that it supplies back to sdev. One is to
+ * validate that a given node is still valid. The next is a callback from sdev
+ * to say that it is no longer using the node. The third and final one is from
+ * sdev where it asks us to fill a directory. All of the heavy lifting is done
+ * in directory filling and in valiation. We opt not to maintain a reference on
+ * the device while there is an sdev node present. This makes the removal of
+ * nodes much simpler and most of the possible failure modes shouldn't cause any
+ * real problems. For example, the open path has to handle both dev_t's which no
+ * longer exist and which are no longer linked.
+ *
+ * -----
+ * hooks
+ * -----
+ *
+ * Like IP, vnd sends all L3 packets through its firewall hooks. Currently vnd
+ * provides these for L3 IP and IPv6 traffic. Each netstack provides these hooks
+ * in a minimal fashion. While we will allow traffic to be filtered through the
+ * hooks, we do not provide means for packet injection or additional inspection
+ * at this time. There are a total of four different events created:
+ *
+ * o IPv4 physical in
+ * o IPv4 physical out
+ * o IPv6 physical in
+ * o IPv6 physical out
+ *
+ * ---------------
+ * Synchronization
+ * ---------------
+ *
+ * To make our synchronization simpler, we've put more effort into making the
+ * metadata/setup paths do more work. That work allows the data paths to make
+ * assumptions around synchronization that simplify the general case. Each major
+ * structure, the vnd_pnsd_t, vnd_dev_t, vnd_str_t, and vnd_data_queue_t is
+ * annotated with the protection that its members receives. The following
+ * annotations are used:
+ *
+ * A Atomics; these values are only modified using atomics values.
+ * Currently this only applies to kstat values.
+ * E Existence; no lock is needed to access this member, it does not
+ * change while the structure is valid.
+ * GL Global Lock; these members are protected by the global
+ * vnd_dev_lock.
+ * L Locked; access to the member is controlled by a lock that is in
+ * the structure.
+ * NSL netstack lock; this member is protected by the containing
+ * netstack. This only applies to the vnd_dev_t`vdd_nslink.
+ * X This member is special, and is discussed in this section.
+ *
+ * In addition to locking, we also have reference counts on the vnd_dev_t and
+ * the vnd_pnsd_t. The reference counts describe the lifetimes of the structure.
+ * With rare exception, once a reference count is decremented, the consumer
+ * should not assume that the data is valid any more. The only exception to this
+ * is the case where we're removing an extant reference count from a link into
+ * /devices or /dev. Reference counts are obtained on these structures as a part
+ * of looking them up.
+ *
+ * # Global Lock Ordering
+ * ######################
+ *
+ * The following is the order that you must take locks in vnd:
+ *
+ * 1) vnd`vnd_dev_lock
+ * 2) vnd_pnsd_t`vpnd_lock
+ * 3) vnd_dev_t`vnd_lock
+ * 4) vnd_str_t`vns_lock
+ * 5) vnd_data_queue_t`vdq_lock
+ *
+ * One must adhere to the following rules:
+ *
+ * o You must acquire a lower numbered lock before a high numbered lock.
+ * o It is NOT legal to hold two locks of the same level concurrently, eg. you
+ * can not hold two different vnd_dev_t's vnd_lock at the same time.
+ * o You may release locks in any order.
+ * o If you release a lock, you must honor the locking rules before acquiring
+ * it again.
+ * o You should not hold any locks when calling any of the rele functions.
+ *
+ * # Special Considerations
+ * ########################
+ *
+ * While most of the locking is what's expected, it's worth going into the
+ * special nature that a few members hold. Today, only two structures have
+ * special considerations: the vnd_dev_t and the vnd_str_t. All members with
+ * special considerations have an additional annotation that describes how you
+ * should interact with it.
+ *
+ * vnd_dev_t: The vdd_nsd and vdd_cr are only valid when the minor node is
+ * attached or in the process of attaching. If the code path that goes through
+ * requires an attached vnd_dev_t, eg. the data path and tear down path, then it
+ * is always legal to dereference that member without a lock held. When they are
+ * added to the system, they should be done under the vdd_lock and done as part
+ * of setting the VND_D_ATTACH_INFLIGHT flag. These should not change during the
+ * lifetime of the vnd_dev_t.
+ *
+ * vnd_dev_t: The vdd_ldih is similar to the vdd_nsd and vdd_cr, except that it
+ * always exists as it is a part of the structure. The only time that it's valid
+ * to be using it is during the attach path with the VND_D_ATTACH_INFLIGHT flag
+ * set or during tear down. Outside of those paths which are naturally
+ * serialized, there is no explicit locking around the member.
+ *
+ * vnd_str_t: The vns_dev and vns_nsd work in similar ways. They are not
+ * initially set as part of creating the structure, but are set as part of
+ * responding to the association ioctl. Anything in the data path or metadata
+ * path that requires association may assume that they exist, as we do not kick
+ * off the state machine until they're set.
+ *
+ * vnd_str_t: The vns_drainblk and vns_barrierblk are similarly special. The
+ * members are designed to be used as part of various operations with the
+ * gsqueues. A lock isn't needed to use them, but to work with them, the
+ * appropriate flag in the vnd_str_t`vns_flags must have been set by the current
+ * thread. Otherwise, it is always fair game to refer to their addresses. Their
+ * contents are ignored by vnd, but some members are manipulated by the gsqueue
+ * subsystem.
+ */
+
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/ddi.h>
+#include <sys/ethernet.h>
+#include <sys/stropts.h>
+#include <sys/sunddi.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/ksynch.h>
+#include <sys/taskq_impl.h>
+#include <sys/sdt.h>
+#include <sys/debug.h>
+#include <sys/sysmacros.h>
+#include <sys/dlpi.h>
+#include <sys/cred.h>
+#include <sys/id_space.h>
+#include <sys/list.h>
+#include <sys/ctype.h>
+#include <sys/policy.h>
+#include <sys/sunldi.h>
+#include <sys/cred.h>
+#include <sys/strsubr.h>
+#include <sys/poll.h>
+#include <sys/neti.h>
+#include <sys/hook.h>
+#include <sys/hook_event.h>
+#include <sys/vlan.h>
+#include <sys/dld.h>
+#include <sys/mac_client.h>
+#include <sys/netstack.h>
+#include <sys/fs/sdev_plugin.h>
+#include <sys/kstat.h>
+#include <sys/atomic.h>
+#include <sys/disp.h>
+#include <sys/random.h>
+#include <sys/gsqueue.h>
+#include <sys/smt.h>
+
+#include <inet/ip.h>
+#include <inet/ip6.h>
+
+#include <sys/vnd.h>
+
+/*
+ * Globals
+ */
+static dev_info_t *vnd_dip;
+static taskq_t *vnd_taskq;
+static kmem_cache_t *vnd_str_cache;
+static kmem_cache_t *vnd_dev_cache;
+static kmem_cache_t *vnd_pnsd_cache;
+static id_space_t *vnd_minors;
+static int vnd_list_init = 0;
+static sdev_plugin_hdl_t vnd_sdev_hdl;
+static gsqueue_set_t *vnd_sqset;
+
+static kmutex_t vnd_dev_lock;
+static list_t vnd_dev_list; /* Protected by the vnd_dev_lock */
+static list_t vnd_nsd_list; /* Protected by the vnd_dev_lock */
+
+/*
+ * STREAMs ioctls
+ *
+ * The STREAMs ioctls are internal to vnd. No one should be seeing them, as such
+ * they aren't a part of the header file.
+ */
+#define VND_STRIOC (('v' << 24) | ('n' << 16) | ('d' << 8) | 0x80)
+
+/*
+ * Private ioctl to associate a given streams instance with a minor instance of
+ * the character device.
+ */
+#define VND_STRIOC_ASSOCIATE (VND_STRIOC | 0x1)
+
+typedef struct vnd_strioc_associate {
+ minor_t vsa_minor; /* minor device node */
+ netstackid_t vsa_nsid; /* netstack id */
+ vnd_errno_t vsa_errno; /* errno */
+} vnd_strioc_associate_t;
+
+typedef enum vnd_strioc_state {
+ VSS_UNKNOWN = 0,
+ VSS_COPYIN = 1,
+ VSS_COPYOUT = 2,
+} vnd_strioc_state_t;
+
+typedef struct vnd_strioc {
+ vnd_strioc_state_t vs_state;
+ caddr_t vs_addr;
+} vnd_strioc_t;
+
+/*
+ * VND SQUEUE TAGS, start at 0x42 so we don't overlap with extent tags. Though
+ * really, overlap is at the end of the day, inevitable.
+ */
+#define VND_SQUEUE_TAG_TX_DRAIN 0x42
+#define VND_SQUEUE_TAG_MAC_FLOW_CONTROL 0x43
+#define VND_SQUEUE_TAG_VND_WRITE 0x44
+#define VND_SQUEUE_TAG_ND_FRAMEIO_WRITE 0x45
+#define VND_SQUEUE_TAG_STRBARRIER 0x46
+
+/*
+ * vnd reserved names. These are names which are reserved by vnd and thus
+ * shouldn't be used by some external program.
+ */
+static char *vnd_reserved_names[] = {
+ "ctl",
+ "zone",
+ NULL
+};
+
+/*
+ * vnd's DTrace probe macros
+ *
+ * DTRACE_VND* are all for a stable provider. We also have an unstable internal
+ * set of probes for reference count manipulation.
+ */
+#define DTRACE_VND3(name, type1, arg1, type2, arg2, type3, arg3) \
+ DTRACE_PROBE3(__vnd_##name, type1, arg1, type2, arg2, type3, arg3);
+
+#define DTRACE_VND4(name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \
+ DTRACE_PROBE4(__vnd_##name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4);
+
+#define DTRACE_VND5(name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4, type5, arg5) \
+ DTRACE_PROBE5(__vnd_##name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4, type5, arg5);
+
+#define DTRACE_VND_REFINC(vdp) \
+ DTRACE_PROBE2(vnd__ref__inc, vnd_dev_t *, vdp, int, vdp->vdd_ref);
+#define DTRACE_VND_REFDEC(vdp) \
+ DTRACE_PROBE2(vnd__ref__dec, vnd_dev_t *, vdp, int, vdp->vdd_ref);
+
+
+/*
+ * Tunables
+ */
+size_t vnd_vdq_default_size = 1024 * 64; /* 64 KB */
+size_t vnd_vdq_hard_max = 1024 * 1024 * 4; /* 4 MB */
+
+/*
+ * These numbers are designed as per-device tunables that are applied when a new
+ * vnd device is attached. They're a rough stab at what may be a reasonable
+ * amount of work to do in one burst in an squeue.
+ */
+size_t vnd_flush_burst_size = 1520 * 10; /* 10 1500 MTU packets */
+size_t vnd_flush_nburst = 10; /* 10 frames */
+
+/*
+ * Constants related to our sdev plugins
+ */
+#define VND_SDEV_NAME "vnd"
+#define VND_SDEV_ROOT "/dev/vnd"
+#define VND_SDEV_ZROOT "/dev/vnd/zone"
+
+/*
+ * vnd relies on privileges, not mode bits to limit access. As such, device
+ * files are read-write to everyone.
+ */
+#define VND_SDEV_MODE (S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | \
+ S_IROTH | S_IWOTH)
+
+/*
+ * Statistic macros
+ */
+#define VND_STAT_INC(vsp, field, val) \
+ atomic_add_64(&(vsp)->vns_ksdata.field.value.ui64, val)
+#define VND_LATENCY_1MS 1000000
+#define VND_LATENCY_10MS 10000000
+#define VND_LATENCY_100MS 100000000
+#define VND_LATENCY_1S 1000000000
+#define VND_LATENCY_10S 10000000000
+
+/*
+ * Constants for vnd hooks
+ */
+static uint8_t vnd_bcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+#define IPV4_MCAST_LEN 3
+static uint8_t vnd_ipv4_mcast[3] = { 0x01, 0x00, 0x5E };
+#define IPV6_MCAST_LEN 2
+static uint8_t vnd_ipv6_mcast[2] = { 0x33, 0x33 };
+
+/*
+ * vnd internal data structures and types
+ */
+
+struct vnd_str;
+struct vnd_dev;
+struct vnd_pnsd;
+
+/*
+ * As part of opening the device stream we need to properly communicate with our
+ * underlying stream. This is a bit of an asynchronous dance and we need to
+ * properly work with dld to get everything set up. We have to initiate the
+ * conversation with dld and as such we keep track of our state here.
+ */
+typedef enum vnd_str_state {
+ VNS_S_INITIAL = 0,
+ VNS_S_INFO_SENT,
+ VNS_S_EXCLUSIVE_SENT,
+ VNS_S_ATTACH_SENT,
+ VNS_S_BIND_SENT,
+ VNS_S_SAP_PROMISC_SENT,
+ VNS_S_MULTI_PROMISC_SENT,
+ VNS_S_RX_ONLY_PROMISC_SENT,
+ VNS_S_FIXUP_PROMISC_SENT,
+ VNS_S_CAPAB_Q_SENT,
+ VNS_S_CAPAB_E_SENT,
+ VNS_S_ONLINE,
+ VNS_S_SHUTTING_DOWN,
+ VNS_S_MULTICAST_PROMISCOFF_SENT,
+ VNS_S_SAP_PROMISCOFF_SENT,
+ VNS_S_UNBIND_SENT,
+ VNS_S_ZOMBIE
+} vnd_str_state_t;
+
+typedef enum vnd_str_flags {
+ VNS_F_NEED_ZONE = 0x1,
+ VNS_F_TASKQ_DISPATCHED = 0x2,
+ VNS_F_CONDEMNED = 0x4,
+ VNS_F_FLOW_CONTROLLED = 0x8,
+ VNS_F_DRAIN_SCHEDULED = 0x10,
+ VNS_F_BARRIER = 0x20,
+ VNS_F_BARRIER_DONE = 0x40
+} vnd_str_flags_t;
+
+typedef enum vnd_capab_flags {
+ VNS_C_HCKSUM = 0x1,
+ VNS_C_DLD = 0x2,
+ VNS_C_DIRECT = 0x4,
+ VNS_C_HCKSUM_BADVERS = 0x8
+} vnd_capab_flags_t;
+
+/*
+ * Definitions to interact with direct callbacks
+ */
+typedef void (*vnd_rx_t)(struct vnd_str *, mac_resource_t *, mblk_t *,
+ mac_header_info_t *);
+typedef uintptr_t vnd_mac_cookie_t;
+/* DLD Direct capability function */
+typedef int (*vnd_dld_cap_t)(void *, uint_t, void *, uint_t);
+/* DLD Direct tx function */
+typedef vnd_mac_cookie_t (*vnd_dld_tx_t)(void *, mblk_t *, uint64_t, uint16_t);
+/* DLD Direct function to set flow control callback */
+typedef void *(*vnd_dld_set_fcb_t)(void *, void (*)(void *, vnd_mac_cookie_t),
+ void *);
+/* DLD Direct function to see if flow controlled still */
+typedef int (*vnd_dld_is_fc_t)(void *, vnd_mac_cookie_t);
+
+/*
+ * The vnd_str_capab_t is always protected by the vnd_str_t it's a member of.
+ */
+typedef struct vnd_str_capab {
+ vnd_capab_flags_t vsc_flags;
+ t_uscalar_t vsc_hcksum_opts;
+ vnd_dld_cap_t vsc_capab_f;
+ void *vsc_capab_hdl;
+ vnd_dld_tx_t vsc_tx_f;
+ void *vsc_tx_hdl;
+ vnd_dld_set_fcb_t vsc_set_fcb_f;
+ void *vsc_set_fcb_hdl;
+ vnd_dld_is_fc_t vsc_is_fc_f;
+ void *vsc_is_fc_hdl;
+ vnd_mac_cookie_t vsc_fc_cookie;
+ void *vsc_tx_fc_hdl;
+} vnd_str_capab_t;
+
+/*
+ * The vnd_data_queue is a simple construct for storing a series of messages in
+ * a queue.
+ *
+ * See synchronization section of the big theory statement for member
+ * annotations.
+ */
+typedef struct vnd_data_queue {
+ struct vnd_str *vdq_vns; /* E */
+ kmutex_t vdq_lock;
+ kcondvar_t vdq_ready; /* Uses vdq_lock */
+ ssize_t vdq_max; /* L */
+ ssize_t vdq_cur; /* L */
+ mblk_t *vdq_head; /* L */
+ mblk_t *vdq_tail; /* L */
+} vnd_data_queue_t;
+
+typedef struct vnd_str_stat {
+ kstat_named_t vks_rbytes;
+ kstat_named_t vks_rpackets;
+ kstat_named_t vks_obytes;
+ kstat_named_t vks_opackets;
+ kstat_named_t vks_nhookindrops;
+ kstat_named_t vks_nhookoutdrops;
+ kstat_named_t vks_ndlpidrops;
+ kstat_named_t vks_ndataindrops;
+ kstat_named_t vks_ndataoutdrops;
+ kstat_named_t vks_tdrops;
+ kstat_named_t vks_linkname;
+ kstat_named_t vks_zonename;
+ kstat_named_t vks_nmacflow;
+ kstat_named_t vks_tmacflow;
+ kstat_named_t vks_mac_flow_1ms;
+ kstat_named_t vks_mac_flow_10ms;
+ kstat_named_t vks_mac_flow_100ms;
+ kstat_named_t vks_mac_flow_1s;
+ kstat_named_t vks_mac_flow_10s;
+} vnd_str_stat_t;
+
+/*
+ * vnd stream structure
+ *
+ * See synchronization section of the big theory statement for member
+ * annotations.
+ */
+typedef struct vnd_str {
+ kmutex_t vns_lock;
+ kcondvar_t vns_cancelcv; /* Uses vns_lock */
+ kcondvar_t vns_barriercv; /* Uses vns_lock */
+ kcondvar_t vns_stcv; /* Uses vns_lock */
+ vnd_str_state_t vns_state; /* L */
+ vnd_str_state_t vns_laststate; /* L */
+ vnd_errno_t vns_errno; /* L */
+ vnd_str_flags_t vns_flags; /* L */
+ vnd_str_capab_t vns_caps; /* L */
+ taskq_ent_t vns_tqe; /* L */
+ vnd_data_queue_t vns_dq_read; /* E */
+ vnd_data_queue_t vns_dq_write; /* E */
+ mblk_t *vns_dlpi_inc; /* L */
+ queue_t *vns_rq; /* E */
+ queue_t *vns_wq; /* E */
+ queue_t *vns_lrq; /* E */
+ t_uscalar_t vns_dlpi_style; /* L */
+ t_uscalar_t vns_minwrite; /* L */
+ t_uscalar_t vns_maxwrite; /* L */
+ hrtime_t vns_fclatch; /* L */
+ hrtime_t vns_fcupdate; /* L */
+ kstat_t *vns_kstat; /* E */
+ gsqueue_t *vns_squeue; /* E */
+ mblk_t vns_drainblk; /* E + X */
+ mblk_t vns_barrierblk; /* E + X */
+ vnd_str_stat_t vns_ksdata; /* A */
+ size_t vns_nflush; /* L */
+ size_t vns_bsize; /* L */
+ struct vnd_dev *vns_dev; /* E + X */
+ struct vnd_pnsd *vns_nsd; /* E + X */
+} vnd_str_t;
+
+typedef enum vnd_dev_flags {
+ VND_D_ATTACH_INFLIGHT = 0x001,
+ VND_D_ATTACHED = 0x002,
+ VND_D_LINK_INFLIGHT = 0x004,
+ VND_D_LINKED = 0x008,
+ VND_D_CONDEMNED = 0x010,
+ VND_D_ZONE_DYING = 0x020,
+ VND_D_OPENED = 0x040
+} vnd_dev_flags_t;
+
+/*
+ * This represents the data associated with a minor device instance.
+ *
+ * See synchronization section of the big theory statement for member
+ * annotations.
+ */
+typedef struct vnd_dev {
+ kmutex_t vdd_lock;
+ list_node_t vdd_link; /* GL */
+ list_node_t vdd_nslink; /* NSL */
+ int vdd_ref; /* L */
+ vnd_dev_flags_t vdd_flags; /* L */
+ minor_t vdd_minor; /* E */
+ dev_t vdd_devid; /* E */
+ ldi_ident_t vdd_ldiid; /* E */
+ ldi_handle_t vdd_ldih; /* X */
+ cred_t *vdd_cr; /* X */
+ vnd_str_t *vdd_str; /* L */
+ struct pollhead vdd_ph; /* E */
+ struct vnd_pnsd *vdd_nsd; /* E + X */
+ char vdd_datalink[VND_NAMELEN]; /* L */
+ char vdd_lname[VND_NAMELEN]; /* L */
+} vnd_dev_t;
+
+typedef enum vnd_pnsd_flags {
+ VND_NS_CONDEMNED = 0x1
+} vnd_pnsd_flags_t;
+
+/*
+ * Per netstack data structure.
+ *
+ * See synchronization section of the big theory statement for member
+ * annotations.
+ */
+typedef struct vnd_pnsd {
+ list_node_t vpnd_link; /* protected by global dev lock */
+ zoneid_t vpnd_zid; /* E */
+ netstackid_t vpnd_nsid; /* E */
+ boolean_t vpnd_hooked; /* E */
+ net_handle_t vpnd_neti_v4; /* E */
+ hook_family_t vpnd_family_v4; /* E */
+ hook_event_t vpnd_event_in_v4; /* E */
+ hook_event_t vpnd_event_out_v4; /* E */
+ hook_event_token_t vpnd_token_in_v4; /* E */
+ hook_event_token_t vpnd_token_out_v4; /* E */
+ net_handle_t vpnd_neti_v6; /* E */
+ hook_family_t vpnd_family_v6; /* E */
+ hook_event_t vpnd_event_in_v6; /* E */
+ hook_event_t vpnd_event_out_v6; /* E */
+ hook_event_token_t vpnd_token_in_v6; /* E */
+ hook_event_token_t vpnd_token_out_v6; /* E */
+ kmutex_t vpnd_lock; /* Protects remaining members */
+ kcondvar_t vpnd_ref_change; /* Uses vpnd_lock */
+ int vpnd_ref; /* L */
+ vnd_pnsd_flags_t vpnd_flags; /* L */
+ list_t vpnd_dev_list; /* L */
+} vnd_pnsd_t;
+
+static void vnd_squeue_tx_drain(void *, mblk_t *, gsqueue_t *, void *);
+
+/*
+ * Drop function signature.
+ */
+typedef void (*vnd_dropper_f)(vnd_str_t *, mblk_t *, const char *);
+
+static void
+vnd_drop_ctl(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ DTRACE_VND4(drop__ctl, mblk_t *, mp, vnd_str_t *, vsp, mblk_t *,
+ mp, const char *, reason);
+ if (mp != NULL) {
+ freemsg(mp);
+ }
+ VND_STAT_INC(vsp, vks_ndlpidrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+}
+
+static void
+vnd_drop_in(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ DTRACE_VND4(drop__in, mblk_t *, mp, vnd_str_t *, vsp, mblk_t *,
+ mp, const char *, reason);
+ if (mp != NULL) {
+ freemsg(mp);
+ }
+ VND_STAT_INC(vsp, vks_ndataindrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+}
+
+static void
+vnd_drop_out(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ DTRACE_VND4(drop__out, mblk_t *, mp, vnd_str_t *, vsp, mblk_t *,
+ mp, const char *, reason);
+ if (mp != NULL) {
+ freemsg(mp);
+ }
+ VND_STAT_INC(vsp, vks_ndataoutdrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+}
+
+static void
+vnd_drop_hook_in(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ DTRACE_VND4(drop__in, mblk_t *, mp, vnd_str_t *, vsp, mblk_t *,
+ mp, const char *, reason);
+ if (mp != NULL) {
+ freemsg(mp);
+ }
+ VND_STAT_INC(vsp, vks_nhookindrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+}
+
+static void
+vnd_drop_hook_out(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ DTRACE_VND4(drop__out, mblk_t *, mp, vnd_str_t *, vsp, mblk_t *,
+ mp, const char *, reason);
+ if (mp != NULL) {
+ freemsg(mp);
+ }
+ VND_STAT_INC(vsp, vks_nhookoutdrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+}
+
+/* ARGSUSED */
+static void
+vnd_drop_panic(vnd_str_t *vsp, mblk_t *mp, const char *reason)
+{
+ panic("illegal vnd drop");
+}
+
+/* ARGSUSED */
+static void
+vnd_mac_drop_input(vnd_str_t *vsp, mac_resource_t *unused, mblk_t *mp_chain,
+ mac_header_info_t *mhip)
+{
+ mblk_t *mp;
+
+ while (mp_chain != NULL) {
+ mp = mp_chain;
+ mp_chain = mp->b_next;
+ vnd_drop_hook_in(vsp, mp, "stream not associated");
+ }
+}
+
+static vnd_pnsd_t *
+vnd_nsd_lookup(netstackid_t nsid)
+{
+ vnd_pnsd_t *nsp;
+
+ mutex_enter(&vnd_dev_lock);
+ for (nsp = list_head(&vnd_nsd_list); nsp != NULL;
+ nsp = list_next(&vnd_nsd_list, nsp)) {
+ if (nsp->vpnd_nsid == nsid) {
+ mutex_enter(&nsp->vpnd_lock);
+ VERIFY(nsp->vpnd_ref >= 0);
+ nsp->vpnd_ref++;
+ mutex_exit(&nsp->vpnd_lock);
+ break;
+ }
+ }
+ mutex_exit(&vnd_dev_lock);
+ return (nsp);
+}
+
+static vnd_pnsd_t *
+vnd_nsd_lookup_by_zid(zoneid_t zid)
+{
+ netstack_t *ns;
+ vnd_pnsd_t *nsp;
+ ns = netstack_find_by_zoneid(zid);
+ if (ns == NULL)
+ return (NULL);
+ nsp = vnd_nsd_lookup(ns->netstack_stackid);
+ netstack_rele(ns);
+ return (nsp);
+}
+
+static vnd_pnsd_t *
+vnd_nsd_lookup_by_zonename(char *zname)
+{
+ zone_t *zonep;
+ vnd_pnsd_t *nsp;
+
+ zonep = zone_find_by_name(zname);
+ if (zonep == NULL)
+ return (NULL);
+
+ nsp = vnd_nsd_lookup_by_zid(zonep->zone_id);
+ zone_rele(zonep);
+ return (nsp);
+}
+
+static void
+vnd_nsd_ref(vnd_pnsd_t *nsp)
+{
+ mutex_enter(&nsp->vpnd_lock);
+ /*
+ * This can only be used on something that has been obtained through
+ * some other means. As such, the caller should already have a reference
+ * before adding another one. This function should not be used as a
+ * means of creating the initial reference.
+ */
+ VERIFY(nsp->vpnd_ref > 0);
+ nsp->vpnd_ref++;
+ mutex_exit(&nsp->vpnd_lock);
+ cv_broadcast(&nsp->vpnd_ref_change);
+}
+
+static void
+vnd_nsd_rele(vnd_pnsd_t *nsp)
+{
+ mutex_enter(&nsp->vpnd_lock);
+ VERIFY(nsp->vpnd_ref > 0);
+ nsp->vpnd_ref--;
+ mutex_exit(&nsp->vpnd_lock);
+ cv_broadcast(&nsp->vpnd_ref_change);
+}
+
+static vnd_dev_t *
+vnd_dev_lookup(minor_t m)
+{
+ vnd_dev_t *vdp;
+ mutex_enter(&vnd_dev_lock);
+ for (vdp = list_head(&vnd_dev_list); vdp != NULL;
+ vdp = list_next(&vnd_dev_list, vdp)) {
+ if (vdp->vdd_minor == m) {
+ mutex_enter(&vdp->vdd_lock);
+ VERIFY(vdp->vdd_ref > 0);
+ vdp->vdd_ref++;
+ DTRACE_VND_REFINC(vdp);
+ mutex_exit(&vdp->vdd_lock);
+ break;
+ }
+ }
+ mutex_exit(&vnd_dev_lock);
+ return (vdp);
+}
+
+static void
+vnd_dev_free(vnd_dev_t *vdp)
+{
+ /*
+ * When the STREAM exists we need to go through and make sure
+ * communication gets torn down. As part of closing the stream, we
+ * guarantee that nothing else should be able to enter the stream layer
+ * at this point. That means no one should be able to call
+ * read(),write() or one of the frameio ioctls.
+ */
+ if (vdp->vdd_flags & VND_D_ATTACHED) {
+ (void) ldi_close(vdp->vdd_ldih, FREAD | FWRITE, vdp->vdd_cr);
+ crfree(vdp->vdd_cr);
+ vdp->vdd_cr = NULL;
+
+ /*
+ * We have to remove ourselves from our parents list now. It is
+ * really quite important that we have already set the condemend
+ * flag here so that our containing netstack basically knows
+ * that we're on the way down and knows not to wait for us. It's
+ * also important that we do that before we put a rele on the
+ * the device as that is the point at which it will check again.
+ */
+ mutex_enter(&vdp->vdd_nsd->vpnd_lock);
+ list_remove(&vdp->vdd_nsd->vpnd_dev_list, vdp);
+ mutex_exit(&vdp->vdd_nsd->vpnd_lock);
+ vnd_nsd_rele(vdp->vdd_nsd);
+ vdp->vdd_nsd = NULL;
+ }
+ ASSERT(vdp->vdd_flags & VND_D_CONDEMNED);
+ id_free(vnd_minors, vdp->vdd_minor);
+ mutex_destroy(&vdp->vdd_lock);
+ kmem_cache_free(vnd_dev_cache, vdp);
+}
+
+static void
+vnd_dev_ref(vnd_dev_t *vdp)
+{
+ mutex_enter(&vdp->vdd_lock);
+ VERIFY(vdp->vdd_ref > 0);
+ vdp->vdd_ref++;
+ DTRACE_VND_REFINC(vdp);
+ mutex_exit(&vdp->vdd_lock);
+}
+
+/*
+ * As part of releasing the hold on this we may tear down a given vnd_dev_t As
+ * such we need to make sure that we grab the list lock first before grabbing
+ * the vnd_dev_t's lock to ensure proper lock ordering.
+ */
+static void
+vnd_dev_rele(vnd_dev_t *vdp)
+{
+ mutex_enter(&vnd_dev_lock);
+ mutex_enter(&vdp->vdd_lock);
+ VERIFY(vdp->vdd_ref > 0);
+ vdp->vdd_ref--;
+ DTRACE_VND_REFDEC(vdp);
+ if (vdp->vdd_ref > 0) {
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vnd_dev_lock);
+ return;
+ }
+
+ /*
+ * Now that we've removed this from the list, we can go ahead and
+ * drop the list lock. No one else can find this device and reference
+ * it. As its reference count is zero, it by definition does not have
+ * any remaining entries in /devices that could lead someone back to
+ * this.
+ */
+ vdp->vdd_flags |= VND_D_CONDEMNED;
+ list_remove(&vnd_dev_list, vdp);
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vnd_dev_lock);
+
+ vnd_dev_free(vdp);
+}
+
+/*
+ * Insert a mesage block chain if there's space, otherwise drop it. Return one
+ * so someone who was waiting for data would now end up having found it. eg.
+ * caller should consider a broadcast.
+ */
+static int
+vnd_dq_push(vnd_data_queue_t *vqp, mblk_t *mp, boolean_t reserved,
+ vnd_dropper_f dropf)
+{
+ size_t msize;
+
+ ASSERT(MUTEX_HELD(&vqp->vdq_lock));
+ if (reserved == B_FALSE) {
+ msize = msgsize(mp);
+ if (vqp->vdq_cur + msize > vqp->vdq_max) {
+ dropf(vqp->vdq_vns, mp, "buffer full");
+ return (0);
+ }
+ vqp->vdq_cur += msize;
+ }
+
+ if (vqp->vdq_head == NULL) {
+ ASSERT(vqp->vdq_tail == NULL);
+ vqp->vdq_head = mp;
+ vqp->vdq_tail = mp;
+ } else {
+ vqp->vdq_tail->b_next = mp;
+ vqp->vdq_tail = mp;
+ }
+
+ return (1);
+}
+
+/*
+ * Remove a message message block chain. If the amount of space in the buffer
+ * has changed we return 1. We have no way of knowing whether or not there is
+ * enough space overall for a given writer who is blocked, so we always end up
+ * having to return true and thus tell consumers that they should consider
+ * signalling.
+ */
+static int
+vnd_dq_pop(vnd_data_queue_t *vqp, mblk_t **mpp)
+{
+ size_t msize;
+ mblk_t *mp;
+
+ ASSERT(MUTEX_HELD(&vqp->vdq_lock));
+ ASSERT(mpp != NULL);
+ if (vqp->vdq_head == NULL) {
+ ASSERT(vqp->vdq_tail == NULL);
+ *mpp = NULL;
+ return (0);
+ }
+
+ mp = vqp->vdq_head;
+ msize = msgsize(mp);
+
+ vqp->vdq_cur -= msize;
+ if (mp->b_next == NULL) {
+ vqp->vdq_head = NULL;
+ vqp->vdq_tail = NULL;
+ /*
+ * We can't be certain that this is always going to be zero.
+ * Someone may have basically taken a reservation of space on
+ * the data queue, eg. claimed spae but not yet pushed it on
+ * yet.
+ */
+ ASSERT(vqp->vdq_cur >= 0);
+ } else {
+ vqp->vdq_head = mp->b_next;
+ ASSERT(vqp->vdq_cur > 0);
+ }
+ mp->b_next = NULL;
+ *mpp = mp;
+ return (1);
+}
+
+/*
+ * Reserve space in the queue. This will bump up the size of the queue and
+ * entitle the user to push something on later without bumping the space.
+ */
+static int
+vnd_dq_reserve(vnd_data_queue_t *vqp, ssize_t size)
+{
+ ASSERT(MUTEX_HELD(&vqp->vdq_lock));
+ ASSERT(size >= 0);
+
+ if (size == 0)
+ return (0);
+
+ if (size + vqp->vdq_cur > vqp->vdq_max)
+ return (0);
+
+ vqp->vdq_cur += size;
+ return (1);
+}
+
+static void
+vnd_dq_unreserve(vnd_data_queue_t *vqp, ssize_t size)
+{
+ ASSERT(MUTEX_HELD(&vqp->vdq_lock));
+ ASSERT(size > 0);
+ ASSERT(size <= vqp->vdq_cur);
+
+ vqp->vdq_cur -= size;
+}
+
+static void
+vnd_dq_flush(vnd_data_queue_t *vqp, vnd_dropper_f dropf)
+{
+ mblk_t *mp, *next;
+
+ mutex_enter(&vqp->vdq_lock);
+ for (mp = vqp->vdq_head; mp != NULL; mp = next) {
+ next = mp->b_next;
+ mp->b_next = NULL;
+ dropf(vqp->vdq_vns, mp, "vnd_dq_flush");
+ }
+ vqp->vdq_cur = 0;
+ vqp->vdq_head = NULL;
+ vqp->vdq_tail = NULL;
+ mutex_exit(&vqp->vdq_lock);
+}
+
+static boolean_t
+vnd_dq_is_empty(vnd_data_queue_t *vqp)
+{
+ boolean_t ret;
+
+ mutex_enter(&vqp->vdq_lock);
+ if (vqp->vdq_head == NULL)
+ ret = B_TRUE;
+ else
+ ret = B_FALSE;
+ mutex_exit(&vqp->vdq_lock);
+
+ return (ret);
+}
+
+/*
+ * Get a network uint16_t from the message and translate it into something the
+ * host understands.
+ */
+static int
+vnd_mbc_getu16(mblk_t *mp, off_t off, uint16_t *out)
+{
+ size_t mpsize;
+ uint8_t *bp;
+
+ mpsize = msgsize(mp);
+ /* Check for overflow */
+ if (off + sizeof (uint16_t) > mpsize)
+ return (1);
+
+ mpsize = MBLKL(mp);
+ while (off >= mpsize) {
+ mp = mp->b_cont;
+ off -= mpsize;
+ mpsize = MBLKL(mp);
+ }
+
+ /*
+ * Data is in network order. Note the second byte of data might be in
+ * the next mp.
+ */
+ bp = mp->b_rptr + off;
+ *out = *bp << 8;
+ if (off + 1 == mpsize) {
+ mp = mp->b_cont;
+ bp = mp->b_rptr;
+ } else {
+ bp++;
+ }
+
+ *out |= *bp;
+ return (0);
+}
+
+/*
+ * Given an mblk chain find the mblk and address of a particular offset.
+ */
+static int
+vnd_mbc_getoffset(mblk_t *mp, off_t off, mblk_t **mpp, uintptr_t *offp)
+{
+ size_t mpsize;
+
+ if (off >= msgsize(mp))
+ return (1);
+
+ mpsize = MBLKL(mp);
+ while (off >= mpsize) {
+ mp = mp->b_cont;
+ off -= mpsize;
+ mpsize = MBLKL(mp);
+ }
+ *mpp = mp;
+ *offp = (uintptr_t)mp->b_rptr + off;
+
+ return (0);
+}
+
+/*
+ * Fetch the destination mac address. Set *dstp to that mac address. If the data
+ * is not contiguous in the first mblk_t, fill in datap and set *dstp to it.
+ */
+static int
+vnd_mbc_getdstmac(mblk_t *mp, uint8_t **dstpp, uint8_t *datap)
+{
+ int i;
+
+ if (MBLKL(mp) >= ETHERADDRL) {
+ *dstpp = mp->b_rptr;
+ return (0);
+ }
+
+ *dstpp = datap;
+ for (i = 0; i < ETHERADDRL; i += 2, datap += 2) {
+ if (vnd_mbc_getu16(mp, i, (uint16_t *)datap) != 0)
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+vnd_hook(vnd_str_t *vsp, mblk_t **mpp, net_handle_t netiv4, hook_event_t hev4,
+ hook_event_token_t hetv4, net_handle_t netiv6, hook_event_t hev6,
+ hook_event_token_t hetv6, vnd_dropper_f hdrop, vnd_dropper_f ddrop)
+{
+ uint16_t etype;
+ hook_pkt_event_t info;
+ size_t offset, mblen;
+ uint8_t *dstp;
+ uint8_t dstaddr[6];
+ hook_event_t he;
+ hook_event_token_t het;
+ net_handle_t neti;
+
+ /*
+ * Before we can ask if we're interested we have to do enough work to
+ * determine the ethertype.
+ */
+
+ /* Byte 12 is either the VLAN tag or the ethertype */
+ if (vnd_mbc_getu16(*mpp, 12, &etype) != 0) {
+ ddrop(vsp, *mpp, "packet has incomplete ethernet header");
+ *mpp = NULL;
+ return (1);
+ }
+
+ if (etype == ETHERTYPE_VLAN) {
+ /* Actual ethertype is another four bytes in */
+ if (vnd_mbc_getu16(*mpp, 16, &etype) != 0) {
+ ddrop(vsp, *mpp,
+ "packet has incomplete ethernet vlan header");
+ *mpp = NULL;
+ return (1);
+ }
+ offset = sizeof (struct ether_vlan_header);
+ } else {
+ offset = sizeof (struct ether_header);
+ }
+
+ /*
+ * At the moment we only hook on the kinds of things that the IP module
+ * would normally.
+ */
+ if (etype != ETHERTYPE_IP && etype != ETHERTYPE_IPV6)
+ return (0);
+
+ if (etype == ETHERTYPE_IP) {
+ neti = netiv4;
+ he = hev4;
+ het = hetv4;
+ } else {
+ neti = netiv6;
+ he = hev6;
+ het = hetv6;
+ }
+
+ if (!he.he_interested)
+ return (0);
+
+
+ if (vnd_mbc_getdstmac(*mpp, &dstp, dstaddr) != 0) {
+ ddrop(vsp, *mpp, "packet has incomplete ethernet header");
+ *mpp = NULL;
+ return (1);
+ }
+
+ /*
+ * Now that we know we're interested, we have to do some additional
+ * sanity checking for IPF's sake, ala ip_check_length(). Specifically
+ * we need to check to make sure that the remaining packet size,
+ * excluding MAC, is at least the size of an IP header.
+ */
+ mblen = msgsize(*mpp);
+ if ((etype == ETHERTYPE_IP &&
+ mblen - offset < IP_SIMPLE_HDR_LENGTH) ||
+ (etype == ETHERTYPE_IPV6 && mblen - offset < IPV6_HDR_LEN)) {
+ ddrop(vsp, *mpp, "packet has invalid IP header");
+ *mpp = NULL;
+ return (1);
+ }
+
+ info.hpe_protocol = neti;
+ info.hpe_ifp = (phy_if_t)vsp;
+ info.hpe_ofp = (phy_if_t)vsp;
+ info.hpe_mp = mpp;
+ info.hpe_flags = 0;
+
+ if (bcmp(vnd_bcast_addr, dstp, ETHERADDRL) == 0)
+ info.hpe_flags |= HPE_BROADCAST;
+ else if (etype == ETHERTYPE_IP &&
+ bcmp(vnd_ipv4_mcast, vnd_bcast_addr, IPV4_MCAST_LEN) == 0)
+ info.hpe_flags |= HPE_MULTICAST;
+ else if (etype == ETHERTYPE_IPV6 &&
+ bcmp(vnd_ipv6_mcast, vnd_bcast_addr, IPV6_MCAST_LEN) == 0)
+ info.hpe_flags |= HPE_MULTICAST;
+
+ if (vnd_mbc_getoffset(*mpp, offset, &info.hpe_mb,
+ (uintptr_t *)&info.hpe_hdr) != 0) {
+ ddrop(vsp, *mpp, "packet too small -- "
+ "unable to find payload");
+ *mpp = NULL;
+ return (1);
+ }
+
+ if (hook_run(neti->netd_hooks, het, (hook_data_t)&info) != 0) {
+ hdrop(vsp, *mpp, "drooped by hooks");
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * This should not be used for DL_INFO_REQ.
+ */
+static mblk_t *
+vnd_dlpi_alloc(size_t len, t_uscalar_t prim)
+{
+ mblk_t *mp;
+ mp = allocb(len, BPRI_MED);
+ if (mp == NULL)
+ return (NULL);
+
+ mp->b_datap->db_type = M_PROTO;
+ mp->b_wptr = mp->b_rptr + len;
+ bzero(mp->b_rptr, len);
+ ((dl_unitdata_req_t *)mp->b_rptr)->dl_primitive = prim;
+
+ return (mp);
+}
+
+static void
+vnd_dlpi_inc_push(vnd_str_t *vsp, mblk_t *mp)
+{
+ mblk_t **mpp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ ASSERT(mp->b_next == NULL);
+ mpp = &vsp->vns_dlpi_inc;
+ while (*mpp != NULL)
+ mpp = &((*mpp)->b_next);
+ *mpp = mp;
+}
+
+static mblk_t *
+vnd_dlpi_inc_pop(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vsp->vns_dlpi_inc;
+ if (mp != NULL) {
+ VERIFY(mp->b_next == NULL || mp->b_next != mp);
+ vsp->vns_dlpi_inc = mp->b_next;
+ mp->b_next = NULL;
+ }
+ return (mp);
+}
+
+static int
+vnd_st_sinfo(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ dl_info_req_t *dlir;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = allocb(MAX(sizeof (dl_info_req_t), sizeof (dl_info_ack_t)),
+ BPRI_HI);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+ vsp->vns_state = VNS_S_INFO_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+
+ mp->b_datap->db_type = M_PCPROTO;
+ dlir = (dl_info_req_t *)mp->b_rptr;
+ mp->b_wptr = (uchar_t *)&dlir[1];
+ dlir->dl_primitive = DL_INFO_REQ;
+ putnext(vsp->vns_wq, mp);
+
+ return (0);
+}
+
+static int
+vnd_st_info(vnd_str_t *vsp)
+{
+ dl_info_ack_t *dlia;
+ mblk_t *mp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+ dlia = (dl_info_ack_t *)mp->b_rptr;
+ vsp->vns_dlpi_style = dlia->dl_provider_style;
+ vsp->vns_minwrite = dlia->dl_min_sdu;
+ vsp->vns_maxwrite = dlia->dl_max_sdu;
+
+ /*
+ * At this time we only support DL_ETHER devices.
+ */
+ if (dlia->dl_mac_type != DL_ETHER) {
+ freemsg(mp);
+ vsp->vns_errno = VND_E_NOTETHER;
+ return (1);
+ }
+
+ /*
+ * Because vnd operates on entire packets, we need to manually account
+ * for the ethernet header information. We add the size of the
+ * ether_vlan_header to account for this, regardless if it is using
+ * vlans or not.
+ */
+ vsp->vns_maxwrite += sizeof (struct ether_vlan_header);
+
+ freemsg(mp);
+ return (0);
+}
+
+static int
+vnd_st_sexclusive(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_alloc(sizeof (dl_attach_req_t), DL_EXCLUSIVE_REQ);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+
+ vsp->vns_state = VNS_S_EXCLUSIVE_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+ putnext(vsp->vns_wq, mp);
+ return (0);
+}
+
+static int
+vnd_st_exclusive(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim, cprim;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+ prim = ((dl_error_ack_t *)mp->b_rptr)->dl_primitive;
+ cprim = ((dl_ok_ack_t *)mp->b_rptr)->dl_correct_primitive;
+
+ if (prim != DL_OK_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp,
+ "wrong dlpi primitive for vnd_st_exclusive");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (cprim != DL_EXCLUSIVE_REQ) {
+ vnd_drop_ctl(vsp, mp,
+ "vnd_st_exclusive: got ack/nack for wrong primitive");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (prim == DL_ERROR_ACK)
+ vsp->vns_errno = VND_E_DLEXCL;
+
+ freemsg(mp);
+ return (prim == DL_ERROR_ACK);
+}
+
+/*
+ * Send down a DLPI_ATTACH_REQ.
+ */
+static int
+vnd_st_sattach(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_alloc(sizeof (dl_attach_req_t), DL_ATTACH_REQ);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+
+ ((dl_attach_req_t *)mp->b_rptr)->dl_ppa = 0;
+ vsp->vns_state = VNS_S_ATTACH_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+ putnext(vsp->vns_wq, mp);
+
+ return (0);
+}
+
+static int
+vnd_st_attach(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim, cprim;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+ prim = ((dl_ok_ack_t *)mp->b_rptr)->dl_primitive;
+ cprim = ((dl_ok_ack_t *)mp->b_rptr)->dl_correct_primitive;
+
+
+ if (prim != DL_OK_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp, "vnd_st_attach: unknown primitive type");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (cprim != DL_ATTACH_REQ) {
+ vnd_drop_ctl(vsp, mp,
+ "vnd_st_attach: Got ack/nack for wrong primitive");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (prim == DL_ERROR_ACK)
+ vsp->vns_errno = VND_E_ATTACHFAIL;
+
+ freemsg(mp);
+ return (prim == DL_ERROR_ACK);
+}
+
+static int
+vnd_st_sbind(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ dl_bind_req_t *dbrp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_alloc(sizeof (dl_bind_req_t) + sizeof (long),
+ DL_BIND_REQ);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+ dbrp = (dl_bind_req_t *)(mp->b_rptr);
+ dbrp->dl_sap = 0;
+ dbrp->dl_service_mode = DL_CLDLS;
+
+ vsp->vns_state = VNS_S_BIND_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+ putnext(vsp->vns_wq, mp);
+
+ return (0);
+}
+
+static int
+vnd_st_bind(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+ prim = ((dl_error_ack_t *)mp->b_rptr)->dl_primitive;
+
+ if (prim != DL_BIND_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp, "wrong dlpi primitive for vnd_st_bind");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (prim == DL_ERROR_ACK)
+ vsp->vns_errno = VND_E_BINDFAIL;
+
+ freemsg(mp);
+ return (prim == DL_ERROR_ACK);
+}
+
+static int
+vnd_st_spromisc(vnd_str_t *vsp, int type, vnd_str_state_t next)
+{
+ mblk_t *mp;
+ dl_promiscon_req_t *dprp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_alloc(sizeof (dl_promiscon_req_t), DL_PROMISCON_REQ);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+
+ dprp = (dl_promiscon_req_t *)mp->b_rptr;
+ dprp->dl_level = type;
+
+ vsp->vns_state = next;
+ cv_broadcast(&vsp->vns_stcv);
+ putnext(vsp->vns_wq, mp);
+
+ return (0);
+}
+
+static int
+vnd_st_promisc(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim, cprim;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+ prim = ((dl_ok_ack_t *)mp->b_rptr)->dl_primitive;
+ cprim = ((dl_ok_ack_t *)mp->b_rptr)->dl_correct_primitive;
+
+ if (prim != DL_OK_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp,
+ "wrong dlpi primitive for vnd_st_promisc");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (cprim != DL_PROMISCON_REQ) {
+ vnd_drop_ctl(vsp, mp,
+ "vnd_st_promisc: Got ack/nack for wrong primitive");
+ vsp->vns_errno = VND_E_DLPIINVAL;
+ return (1);
+ }
+
+ if (prim == DL_ERROR_ACK)
+ vsp->vns_errno = VND_E_PROMISCFAIL;
+
+ freemsg(mp);
+ return (prim == DL_ERROR_ACK);
+}
+
+static int
+vnd_st_scapabq(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+
+ mp = vnd_dlpi_alloc(sizeof (dl_capability_req_t), DL_CAPABILITY_REQ);
+ if (mp == NULL) {
+ vsp->vns_errno = VND_E_NOMEM;
+ return (1);
+ }
+
+ vsp->vns_state = VNS_S_CAPAB_Q_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+ putnext(vsp->vns_wq, mp);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+vnd_mac_input(vnd_str_t *vsp, mac_resource_t *unused, mblk_t *mp_chain,
+ mac_header_info_t *mhip)
+{
+ int signal = 0;
+ mblk_t *mp;
+ vnd_pnsd_t *nsp = vsp->vns_nsd;
+
+ ASSERT(vsp != NULL);
+ ASSERT(mp_chain != NULL);
+
+ for (mp = mp_chain; mp != NULL; mp = mp_chain) {
+ uint16_t vid;
+ mp_chain = mp->b_next;
+ mp->b_next = NULL;
+
+ /*
+ * If we were operating in a traditional dlpi context then we
+ * would have enabled DLIOCRAW and rather than the fast path, we
+ * would come through dld_str_rx_raw. That function does two
+ * things that we have to consider doing ourselves. The first is
+ * that it adjusts the b_rptr back to account for dld bumping us
+ * past the mac header. It also tries to account for cases where
+ * mac provides an illusion of the mac header. Fortunately, dld
+ * only allows the fastpath when the media type is the same as
+ * the native type. Therefore all we have to do here is adjust
+ * the b_rptr.
+ */
+ ASSERT(mp->b_rptr >= DB_BASE(mp) + mhip->mhi_hdrsize);
+ mp->b_rptr -= mhip->mhi_hdrsize;
+ vid = VLAN_ID(mhip->mhi_tci);
+ if (mhip->mhi_istagged && vid != VLAN_ID_NONE) {
+ /*
+ * This is an overlapping copy. Do not use bcopy(9F).
+ */
+ (void) memmove(mp->b_rptr + 4, mp->b_rptr, 12);
+ mp->b_rptr += 4;
+ }
+
+ if (nsp->vpnd_hooked && vnd_hook(vsp, &mp, nsp->vpnd_neti_v4,
+ nsp->vpnd_event_in_v4, nsp->vpnd_token_in_v4,
+ nsp->vpnd_neti_v6, nsp->vpnd_event_in_v6,
+ nsp->vpnd_token_in_v6, vnd_drop_hook_in, vnd_drop_in) != 0)
+ continue;
+
+ VND_STAT_INC(vsp, vks_rpackets, 1);
+ VND_STAT_INC(vsp, vks_rbytes, msgsize(mp));
+ DTRACE_VND5(recv, mblk_t *, mp, void *, NULL, void *, NULL,
+ vnd_str_t *, vsp, mblk_t *, mp);
+ mutex_enter(&vsp->vns_dq_read.vdq_lock);
+ signal |= vnd_dq_push(&vsp->vns_dq_read, mp, B_FALSE,
+ vnd_drop_in);
+ mutex_exit(&vsp->vns_dq_read.vdq_lock);
+ }
+
+ if (signal != 0) {
+ cv_broadcast(&vsp->vns_dq_read.vdq_ready);
+ pollwakeup(&vsp->vns_dev->vdd_ph, POLLIN | POLLRDNORM);
+ }
+
+}
+
+static void
+vnd_mac_flow_control_stat(vnd_str_t *vsp, hrtime_t diff)
+{
+ VND_STAT_INC(vsp, vks_nmacflow, 1);
+ VND_STAT_INC(vsp, vks_tmacflow, diff);
+ if (diff >= VND_LATENCY_1MS)
+ VND_STAT_INC(vsp, vks_mac_flow_1ms, 1);
+ if (diff >= VND_LATENCY_10MS)
+ VND_STAT_INC(vsp, vks_mac_flow_10ms, 1);
+ if (diff >= VND_LATENCY_100MS)
+ VND_STAT_INC(vsp, vks_mac_flow_100ms, 1);
+ if (diff >= VND_LATENCY_1S)
+ VND_STAT_INC(vsp, vks_mac_flow_1s, 1);
+ if (diff >= VND_LATENCY_10S)
+ VND_STAT_INC(vsp, vks_mac_flow_10s, 1);
+}
+
+/*
+ * This is a callback from MAC that indicates that we are allowed to send
+ * packets again.
+ */
+static void
+vnd_mac_flow_control(void *arg, vnd_mac_cookie_t cookie)
+{
+ vnd_str_t *vsp = arg;
+ hrtime_t now;
+
+ mutex_enter(&vsp->vns_lock);
+ now = gethrtime();
+
+ /*
+ * Check for the case that we beat vnd_squeue_tx_one to the punch.
+ * There's also an additional case here that we got notified because
+ * we're sharing a device that ran out of tx descriptors, even though it
+ * wasn't because of us.
+ */
+ if (!(vsp->vns_flags & VNS_F_FLOW_CONTROLLED)) {
+ vsp->vns_fcupdate = now;
+ mutex_exit(&vsp->vns_lock);
+ return;
+ }
+
+ ASSERT(vsp->vns_flags & VNS_F_FLOW_CONTROLLED);
+ ASSERT(vsp->vns_caps.vsc_fc_cookie == cookie);
+ vsp->vns_flags &= ~VNS_F_FLOW_CONTROLLED;
+ vsp->vns_caps.vsc_fc_cookie = (vnd_mac_cookie_t)NULL;
+ vsp->vns_fclatch = 0;
+ DTRACE_VND3(flow__resumed, vnd_str_t *, vsp, uint64_t,
+ vsp->vns_dq_write.vdq_cur, uintptr_t, cookie);
+ /*
+ * If someone has asked to flush the squeue and thus inserted a barrier,
+ * than we shouldn't schedule a drain.
+ */
+ if (!(vsp->vns_flags & (VNS_F_DRAIN_SCHEDULED | VNS_F_BARRIER))) {
+ vsp->vns_flags |= VNS_F_DRAIN_SCHEDULED;
+ gsqueue_enter_one(vsp->vns_squeue, &vsp->vns_drainblk,
+ vnd_squeue_tx_drain, vsp, GSQUEUE_FILL,
+ VND_SQUEUE_TAG_MAC_FLOW_CONTROL);
+ }
+ mutex_exit(&vsp->vns_lock);
+}
+
+static void
+vnd_mac_enter(vnd_str_t *vsp, mac_perim_handle_t *mphp)
+{
+ ASSERT(MUTEX_HELD(&vsp->vns_lock));
+ VERIFY(vsp->vns_caps.vsc_capab_f(vsp->vns_caps.vsc_capab_hdl,
+ DLD_CAPAB_PERIM, mphp, DLD_ENABLE) == 0);
+}
+
+static void
+vnd_mac_exit(vnd_str_t *vsp, mac_perim_handle_t mph)
+{
+ ASSERT(MUTEX_HELD(&vsp->vns_lock));
+ VERIFY(vsp->vns_caps.vsc_capab_f(vsp->vns_caps.vsc_capab_hdl,
+ DLD_CAPAB_PERIM, mph, DLD_DISABLE) == 0);
+}
+
+static int
+vnd_dld_cap_enable(vnd_str_t *vsp, vnd_rx_t rxfunc)
+{
+ int ret;
+ dld_capab_direct_t d;
+ mac_perim_handle_t mph;
+ vnd_str_capab_t *c = &vsp->vns_caps;
+
+ bzero(&d, sizeof (d));
+ d.di_rx_cf = (uintptr_t)rxfunc;
+ d.di_rx_ch = vsp;
+ d.di_flags = DI_DIRECT_RAW;
+
+ vnd_mac_enter(vsp, &mph);
+
+ /*
+ * If we're coming in here for a second pass, we need to make sure that
+ * we remove an existing flow control notification callback, otherwise
+ * we'll create a duplicate that will remain with garbage data.
+ */
+ if (c->vsc_tx_fc_hdl != NULL) {
+ ASSERT(c->vsc_set_fcb_hdl != NULL);
+ (void) c->vsc_set_fcb_f(c->vsc_set_fcb_hdl, NULL,
+ c->vsc_tx_fc_hdl);
+ c->vsc_tx_fc_hdl = NULL;
+ }
+
+ if (vsp->vns_caps.vsc_capab_f(c->vsc_capab_hdl,
+ DLD_CAPAB_DIRECT, &d, DLD_ENABLE) == 0) {
+ c->vsc_tx_f = (vnd_dld_tx_t)d.di_tx_df;
+ c->vsc_tx_hdl = d.di_tx_dh;
+ c->vsc_set_fcb_f = (vnd_dld_set_fcb_t)d.di_tx_cb_df;
+ c->vsc_set_fcb_hdl = d.di_tx_cb_dh;
+ c->vsc_is_fc_f = (vnd_dld_is_fc_t)d.di_tx_fctl_df;
+ c->vsc_is_fc_hdl = d.di_tx_fctl_dh;
+ c->vsc_tx_fc_hdl = c->vsc_set_fcb_f(c->vsc_set_fcb_hdl,
+ vnd_mac_flow_control, vsp);
+ c->vsc_flags |= VNS_C_DIRECT;
+ ret = 0;
+ } else {
+ vsp->vns_errno = VND_E_DIRECTFAIL;
+ ret = 1;
+ }
+ vnd_mac_exit(vsp, mph);
+ return (ret);
+}
+
+static int
+vnd_st_capabq(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ dl_capability_ack_t *cap;
+ dl_capability_sub_t *subp;
+ dl_capab_hcksum_t *hck;
+ dl_capab_dld_t *dld;
+ unsigned char *rp;
+ int ret = 0;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_inc_pop(vsp);
+
+ rp = mp->b_rptr;
+ cap = (dl_capability_ack_t *)rp;
+ if (cap->dl_sub_length == 0)
+ goto done;
+
+ /* Don't try to process something too big */
+ if (sizeof (dl_capability_ack_t) + cap->dl_sub_length > MBLKL(mp)) {
+ VND_STAT_INC(vsp, vks_ndlpidrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+ vsp->vns_errno = VND_E_CAPACKINVAL;
+ ret = 1;
+ goto done;
+ }
+
+ rp += cap->dl_sub_offset;
+
+ while (cap->dl_sub_length > 0) {
+ subp = (dl_capability_sub_t *)rp;
+ /* Sanity check something crazy from down below */
+ if (subp->dl_length + sizeof (dl_capability_sub_t) >
+ cap->dl_sub_length) {
+ VND_STAT_INC(vsp, vks_ndlpidrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+ vsp->vns_errno = VND_E_SUBCAPINVAL;
+ ret = 1;
+ goto done;
+ }
+
+ switch (subp->dl_cap) {
+ case DL_CAPAB_HCKSUM:
+ hck = (dl_capab_hcksum_t *)(rp +
+ sizeof (dl_capability_sub_t));
+ if (hck->hcksum_version != HCKSUM_CURRENT_VERSION) {
+ vsp->vns_caps.vsc_flags |= VNS_C_HCKSUM_BADVERS;
+ break;
+ }
+ if (dlcapabcheckqid(&hck->hcksum_mid, vsp->vns_lrq) !=
+ B_TRUE) {
+ vsp->vns_errno = VND_E_CAPABPASS;
+ ret = 1;
+ goto done;
+ }
+ vsp->vns_caps.vsc_flags |= VNS_C_HCKSUM;
+ vsp->vns_caps.vsc_hcksum_opts = hck->hcksum_txflags;
+ break;
+ case DL_CAPAB_DLD:
+ dld = (dl_capab_dld_t *)(rp +
+ sizeof (dl_capability_sub_t));
+ if (dld->dld_version != DLD_CURRENT_VERSION) {
+ vsp->vns_errno = VND_E_DLDBADVERS;
+ ret = 1;
+ goto done;
+ }
+ if (dlcapabcheckqid(&dld->dld_mid, vsp->vns_lrq) !=
+ B_TRUE) {
+ vsp->vns_errno = VND_E_CAPABPASS;
+ ret = 1;
+ goto done;
+ }
+ vsp->vns_caps.vsc_flags |= VNS_C_DLD;
+ vsp->vns_caps.vsc_capab_f =
+ (vnd_dld_cap_t)dld->dld_capab;
+ vsp->vns_caps.vsc_capab_hdl =
+ (void *)dld->dld_capab_handle;
+ /*
+ * At this point in time, we have to set up a direct
+ * function that drops all input. This validates that
+ * we'll be able to set up direct input and that we can
+ * easily switch it earlier to the real data function
+ * when we've plumbed everything up.
+ */
+ if (vnd_dld_cap_enable(vsp, vnd_mac_drop_input) != 0) {
+ /* vns_errno set by vnd_dld_cap_enable */
+ ret = 1;
+ goto done;
+ }
+ break;
+ default:
+ /* Ignore unsupported cap */
+ break;
+ }
+
+ rp += sizeof (dl_capability_sub_t) + subp->dl_length;
+ cap->dl_sub_length -= sizeof (dl_capability_sub_t) +
+ subp->dl_length;
+ }
+
+done:
+ /* Make sure we enabled direct callbacks */
+ if (ret == 0 && !(vsp->vns_caps.vsc_flags & VNS_C_DIRECT)) {
+ vsp->vns_errno = VND_E_DIRECTNOTSUP;
+ ret = 1;
+ }
+
+ freemsg(mp);
+ return (ret);
+}
+
+static void
+vnd_st_sonline(vnd_str_t *vsp)
+{
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ vsp->vns_state = VNS_S_ONLINE;
+ cv_broadcast(&vsp->vns_stcv);
+}
+
+static void
+vnd_st_shutdown(vnd_str_t *vsp)
+{
+ mac_perim_handle_t mph;
+ vnd_str_capab_t *vsc = &vsp->vns_caps;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+
+ /*
+ * At this point in time we know that there is no one transmitting as
+ * our final reference has been torn down and that vnd_s_close inserted
+ * a barrier to validate that everything is flushed.
+ */
+ if (vsc->vsc_flags & VNS_C_DIRECT) {
+ vnd_mac_enter(vsp, &mph);
+ vsc->vsc_flags &= ~VNS_C_DIRECT;
+ (void) vsc->vsc_set_fcb_f(vsc->vsc_set_fcb_hdl, NULL,
+ vsc->vsc_tx_fc_hdl);
+ vsc->vsc_tx_fc_hdl = NULL;
+ (void) vsc->vsc_capab_f(vsc->vsc_capab_hdl, DLD_CAPAB_DIRECT,
+ NULL, DLD_DISABLE);
+ vnd_mac_exit(vsp, mph);
+ }
+}
+
+static boolean_t
+vnd_st_spromiscoff(vnd_str_t *vsp, int type, vnd_str_state_t next)
+{
+ boolean_t ret = B_TRUE;
+ mblk_t *mp;
+ dl_promiscoff_req_t *dprp;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+ mp = vnd_dlpi_alloc(sizeof (dl_promiscon_req_t), DL_PROMISCOFF_REQ);
+ if (mp == NULL) {
+ cmn_err(CE_NOTE, "!vnd failed to allocate mblk_t for "
+ "promiscoff request");
+ ret = B_FALSE;
+ goto next;
+ }
+
+ dprp = (dl_promiscoff_req_t *)mp->b_rptr;
+ dprp->dl_level = type;
+
+ putnext(vsp->vns_wq, mp);
+next:
+ vsp->vns_state = next;
+ cv_broadcast(&vsp->vns_stcv);
+ return (ret);
+}
+
+static void
+vnd_st_promiscoff(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim, cprim;
+
+ VERIFY(MUTEX_HELD(&vsp->vns_lock));
+
+ /*
+ * Unlike other cases where we guard against the incoming packet being
+ * NULL, during tear down we try to keep driving and therefore we may
+ * have gotten here due to an earlier failure, so there's nothing to do.
+ */
+ mp = vnd_dlpi_inc_pop(vsp);
+ if (mp == NULL)
+ return;
+
+ prim = ((dl_ok_ack_t *)mp->b_rptr)->dl_primitive;
+ cprim = ((dl_ok_ack_t *)mp->b_rptr)->dl_correct_primitive;
+
+ if (prim != DL_OK_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp,
+ "wrong dlpi primitive for vnd_st_promiscoff");
+ return;
+ }
+
+ if (cprim != DL_PROMISCOFF_REQ) {
+ vnd_drop_ctl(vsp, mp,
+ "vnd_st_promiscoff: Got ack/nack for wrong primitive");
+ return;
+ }
+
+ if (prim == DL_ERROR_ACK) {
+ cmn_err(CE_WARN, "!failed to disable promiscuos mode during "
+ "vnd teardown");
+ }
+}
+
+static boolean_t
+vnd_st_sunbind(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ boolean_t ret = B_TRUE;
+
+ mp = vnd_dlpi_alloc(sizeof (dl_unbind_req_t), DL_UNBIND_REQ);
+ if (mp == NULL) {
+ cmn_err(CE_NOTE, "!vnd failed to allocate mblk_t for "
+ "unbind request");
+ ret = B_FALSE;
+ goto next;
+ }
+
+ putnext(vsp->vns_wq, mp);
+next:
+ vsp->vns_state = VNS_S_UNBIND_SENT;
+ cv_broadcast(&vsp->vns_stcv);
+ return (ret);
+}
+
+static void
+vnd_st_unbind(vnd_str_t *vsp)
+{
+ mblk_t *mp;
+ t_uscalar_t prim, cprim;
+
+ /*
+ * Unlike other cases where we guard against the incoming packet being
+ * NULL, during tear down we try to keep driving and therefore we may
+ * have gotten here due to an earlier failure, so there's nothing to do.
+ */
+ mp = vnd_dlpi_inc_pop(vsp);
+ if (mp == NULL)
+ goto next;
+
+ prim = ((dl_ok_ack_t *)mp->b_rptr)->dl_primitive;
+ cprim = ((dl_ok_ack_t *)mp->b_rptr)->dl_correct_primitive;
+
+ if (prim != DL_OK_ACK && prim != DL_ERROR_ACK) {
+ vnd_drop_ctl(vsp, mp,
+ "wrong dlpi primitive for vnd_st_unbind");
+ goto next;
+ }
+
+ if (cprim != DL_UNBIND_REQ) {
+ vnd_drop_ctl(vsp, mp,
+ "vnd_st_unbind: Got ack/nack for wrong primitive");
+ goto next;
+ }
+
+ if (prim == DL_ERROR_ACK) {
+ cmn_err(CE_WARN, "!failed to unbind stream during vnd "
+ "teardown");
+ }
+
+next:
+ vsp->vns_state = VNS_S_ZOMBIE;
+ cv_broadcast(&vsp->vns_stcv);
+}
+
+/*
+ * Perform state transitions. This is a one way shot down the flow chart
+ * described in the big theory statement.
+ */
+static void
+vnd_str_state_transition(void *arg)
+{
+ boolean_t died = B_FALSE;
+ vnd_str_t *vsp = arg;
+ mblk_t *mp;
+
+ mutex_enter(&vsp->vns_lock);
+ if (vsp->vns_dlpi_inc == NULL && (vsp->vns_state != VNS_S_INITIAL &&
+ vsp->vns_state != VNS_S_SHUTTING_DOWN)) {
+ mutex_exit(&vsp->vns_lock);
+ return;
+ }
+
+ /*
+ * When trying to shut down, or unwinding from a failed enabling, rather
+ * than immediately entering the ZOMBIE state, we may instead opt to try
+ * and enter the next state in the progression. This is especially
+ * important when trying to tear everything down.
+ */
+loop:
+ DTRACE_PROBE2(vnd__state__transition, uintptr_t, vsp,
+ vnd_str_state_t, vsp->vns_state);
+ switch (vsp->vns_state) {
+ case VNS_S_INITIAL:
+ VERIFY(vsp->vns_dlpi_inc == NULL);
+ if (vnd_st_sinfo(vsp) != 0)
+ died = B_TRUE;
+ break;
+ case VNS_S_INFO_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_info(vsp) == 0) {
+ if (vnd_st_sexclusive(vsp) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_EXCLUSIVE_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_exclusive(vsp) == 0) {
+ if (vsp->vns_dlpi_style == DL_STYLE2) {
+ if (vnd_st_sattach(vsp) != 0)
+ died = B_TRUE;
+ } else {
+ if (vnd_st_sbind(vsp) != 0)
+ died = B_TRUE;
+ }
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_ATTACH_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_attach(vsp) == 0) {
+ if (vnd_st_sbind(vsp) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_BIND_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_bind(vsp) == 0) {
+ if (vnd_st_spromisc(vsp, DL_PROMISC_SAP,
+ VNS_S_SAP_PROMISC_SENT) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_SAP_PROMISC_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_promisc(vsp) == 0) {
+ if (vnd_st_spromisc(vsp, DL_PROMISC_MULTI,
+ VNS_S_MULTI_PROMISC_SENT) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_MULTI_PROMISC_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_promisc(vsp) == 0) {
+ if (vnd_st_spromisc(vsp, DL_PROMISC_RX_ONLY,
+ VNS_S_RX_ONLY_PROMISC_SENT) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_RX_ONLY_PROMISC_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_promisc(vsp) == 0) {
+ if (vnd_st_spromisc(vsp, DL_PROMISC_FIXUPS,
+ VNS_S_FIXUP_PROMISC_SENT) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_FIXUP_PROMISC_SENT:
+ VERIFY(vsp->vns_dlpi_inc != NULL);
+ if (vnd_st_promisc(vsp) == 0) {
+ if (vnd_st_scapabq(vsp) != 0)
+ died = B_TRUE;
+ } else {
+ died = B_TRUE;
+ }
+ break;
+ case VNS_S_CAPAB_Q_SENT:
+ if (vnd_st_capabq(vsp) != 0)
+ died = B_TRUE;
+ else
+ vnd_st_sonline(vsp);
+ break;
+ case VNS_S_SHUTTING_DOWN:
+ vnd_st_shutdown(vsp);
+ if (vnd_st_spromiscoff(vsp, DL_PROMISC_MULTI,
+ VNS_S_MULTICAST_PROMISCOFF_SENT) == B_FALSE)
+ goto loop;
+ break;
+ case VNS_S_MULTICAST_PROMISCOFF_SENT:
+ vnd_st_promiscoff(vsp);
+ if (vnd_st_spromiscoff(vsp, DL_PROMISC_SAP,
+ VNS_S_SAP_PROMISCOFF_SENT) == B_FALSE)
+ goto loop;
+ break;
+ case VNS_S_SAP_PROMISCOFF_SENT:
+ vnd_st_promiscoff(vsp);
+ if (vnd_st_sunbind(vsp) == B_FALSE)
+ goto loop;
+ break;
+ case VNS_S_UNBIND_SENT:
+ vnd_st_unbind(vsp);
+ break;
+ case VNS_S_ZOMBIE:
+ while ((mp = vnd_dlpi_inc_pop(vsp)) != NULL)
+ vnd_drop_ctl(vsp, mp, "vsp received data as a zombie");
+ break;
+ default:
+ panic("vnd_str_t entered an unknown state");
+ }
+
+ if (died == B_TRUE) {
+ ASSERT(vsp->vns_errno != VND_E_SUCCESS);
+ vsp->vns_laststate = vsp->vns_state;
+ vsp->vns_state = VNS_S_ZOMBIE;
+ cv_broadcast(&vsp->vns_stcv);
+ }
+
+ mutex_exit(&vsp->vns_lock);
+}
+
+static void
+vnd_dlpi_taskq_dispatch(void *arg)
+{
+ vnd_str_t *vsp = arg;
+ int run = 1;
+
+ while (run != 0) {
+ vnd_str_state_transition(vsp);
+ mutex_enter(&vsp->vns_lock);
+ if (vsp->vns_flags & VNS_F_CONDEMNED ||
+ vsp->vns_dlpi_inc == NULL) {
+ run = 0;
+ vsp->vns_flags &= ~VNS_F_TASKQ_DISPATCHED;
+ }
+ if (vsp->vns_flags & VNS_F_CONDEMNED)
+ cv_signal(&vsp->vns_cancelcv);
+ mutex_exit(&vsp->vns_lock);
+ }
+}
+
+/* ARGSUSED */
+static int
+vnd_neti_getifname(net_handle_t neti, phy_if_t phy, char *buf, const size_t len)
+{
+ return (-1);
+}
+
+/* ARGSUSED */
+static int
+vnd_neti_getmtu(net_handle_t neti, phy_if_t phy, lif_if_t ifdata)
+{
+ return (-1);
+}
+
+/* ARGSUSED */
+static int
+vnd_neti_getptmue(net_handle_t neti)
+{
+ return (-1);
+}
+
+/* ARGSUSED */
+static int
+vnd_neti_getlifaddr(net_handle_t neti, phy_if_t phy, lif_if_t ifdata,
+ size_t nelem, net_ifaddr_t type[], void *storage)
+{
+ return (-1);
+}
+
+/* ARGSUSED */
+static int
+vnd_neti_getlifzone(net_handle_t neti, phy_if_t phy, lif_if_t ifdata,
+ zoneid_t *zid)
+{
+ return (-1);
+}
+
+/* ARGSUSED */
+static int
+vnd_neti_getlifflags(net_handle_t neti, phy_if_t phy, lif_if_t ifdata,
+ uint64_t *flags)
+{
+ return (-1);
+}
+
+/* ARGSUSED */
+static phy_if_t
+vnd_neti_phygetnext(net_handle_t neti, phy_if_t phy)
+{
+ return ((phy_if_t)-1);
+}
+
+/* ARGSUSED */
+static phy_if_t
+vnd_neti_phylookup(net_handle_t neti, const char *name)
+{
+ return ((phy_if_t)-1);
+}
+
+/* ARGSUSED */
+static lif_if_t
+vnd_neti_lifgetnext(net_handle_t neti, phy_if_t phy, lif_if_t ifdata)
+{
+ return (-1);
+}
+
+/* ARGSUSED */
+static int
+vnd_neti_inject(net_handle_t neti, inject_t style, net_inject_t *packet)
+{
+ return (-1);
+}
+
+/* ARGSUSED */
+static phy_if_t
+vnd_neti_route(net_handle_t neti, struct sockaddr *address,
+ struct sockaddr *next)
+{
+ return ((phy_if_t)-1);
+}
+
+/* ARGSUSED */
+static int
+vnd_neti_ispchksum(net_handle_t neti, mblk_t *mp)
+{
+ return (-1);
+}
+
+/* ARGSUSED */
+static int
+vnd_neti_isvchksum(net_handle_t neti, mblk_t *mp)
+{
+ return (-1);
+}
+
+static net_protocol_t vnd_neti_info_v4 = {
+ NETINFO_VERSION,
+ NHF_VND_INET,
+ vnd_neti_getifname,
+ vnd_neti_getmtu,
+ vnd_neti_getptmue,
+ vnd_neti_getlifaddr,
+ vnd_neti_getlifzone,
+ vnd_neti_getlifflags,
+ vnd_neti_phygetnext,
+ vnd_neti_phylookup,
+ vnd_neti_lifgetnext,
+ vnd_neti_inject,
+ vnd_neti_route,
+ vnd_neti_ispchksum,
+ vnd_neti_isvchksum
+};
+
+static net_protocol_t vnd_neti_info_v6 = {
+ NETINFO_VERSION,
+ NHF_VND_INET6,
+ vnd_neti_getifname,
+ vnd_neti_getmtu,
+ vnd_neti_getptmue,
+ vnd_neti_getlifaddr,
+ vnd_neti_getlifzone,
+ vnd_neti_getlifflags,
+ vnd_neti_phygetnext,
+ vnd_neti_phylookup,
+ vnd_neti_lifgetnext,
+ vnd_neti_inject,
+ vnd_neti_route,
+ vnd_neti_ispchksum,
+ vnd_neti_isvchksum
+};
+
+
+static int
+vnd_netinfo_init(vnd_pnsd_t *nsp)
+{
+ nsp->vpnd_neti_v4 = net_protocol_register(nsp->vpnd_nsid,
+ &vnd_neti_info_v4);
+ ASSERT(nsp->vpnd_neti_v4 != NULL);
+
+ nsp->vpnd_neti_v6 = net_protocol_register(nsp->vpnd_nsid,
+ &vnd_neti_info_v6);
+ ASSERT(nsp->vpnd_neti_v6 != NULL);
+
+ nsp->vpnd_family_v4.hf_version = HOOK_VERSION;
+ nsp->vpnd_family_v4.hf_name = "vnd_inet";
+
+ if (net_family_register(nsp->vpnd_neti_v4, &nsp->vpnd_family_v4) != 0) {
+ (void) net_protocol_unregister(nsp->vpnd_neti_v4);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_family_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ nsp->vpnd_family_v6.hf_version = HOOK_VERSION;
+ nsp->vpnd_family_v6.hf_name = "vnd_inet6";
+
+ if (net_family_register(nsp->vpnd_neti_v6, &nsp->vpnd_family_v6) != 0) {
+ (void) net_family_unregister(nsp->vpnd_neti_v4,
+ &nsp->vpnd_family_v4);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v4);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_family_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ nsp->vpnd_event_in_v4.he_version = HOOK_VERSION;
+ nsp->vpnd_event_in_v4.he_name = NH_PHYSICAL_IN;
+ nsp->vpnd_event_in_v4.he_flags = 0;
+ nsp->vpnd_event_in_v4.he_interested = B_FALSE;
+
+ nsp->vpnd_token_in_v4 = net_event_register(nsp->vpnd_neti_v4,
+ &nsp->vpnd_event_in_v4);
+ if (nsp->vpnd_token_in_v4 == NULL) {
+ (void) net_family_unregister(nsp->vpnd_neti_v4,
+ &nsp->vpnd_family_v4);
+ (void) net_family_unregister(nsp->vpnd_neti_v6,
+ &nsp->vpnd_family_v6);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v4);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_event_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ nsp->vpnd_event_in_v6.he_version = HOOK_VERSION;
+ nsp->vpnd_event_in_v6.he_name = NH_PHYSICAL_IN;
+ nsp->vpnd_event_in_v6.he_flags = 0;
+ nsp->vpnd_event_in_v6.he_interested = B_FALSE;
+
+ nsp->vpnd_token_in_v6 = net_event_register(nsp->vpnd_neti_v6,
+ &nsp->vpnd_event_in_v6);
+ if (nsp->vpnd_token_in_v6 == NULL) {
+ (void) net_event_shutdown(nsp->vpnd_neti_v4,
+ &nsp->vpnd_event_in_v4);
+ (void) net_event_unregister(nsp->vpnd_neti_v4,
+ &nsp->vpnd_event_in_v4);
+ (void) net_family_unregister(nsp->vpnd_neti_v4,
+ &nsp->vpnd_family_v4);
+ (void) net_family_unregister(nsp->vpnd_neti_v6,
+ &nsp->vpnd_family_v6);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v4);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_event_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ nsp->vpnd_event_out_v4.he_version = HOOK_VERSION;
+ nsp->vpnd_event_out_v4.he_name = NH_PHYSICAL_OUT;
+ nsp->vpnd_event_out_v4.he_flags = 0;
+ nsp->vpnd_event_out_v4.he_interested = B_FALSE;
+
+ nsp->vpnd_token_out_v4 = net_event_register(nsp->vpnd_neti_v4,
+ &nsp->vpnd_event_out_v4);
+ if (nsp->vpnd_token_out_v4 == NULL) {
+ (void) net_event_shutdown(nsp->vpnd_neti_v6,
+ &nsp->vpnd_event_in_v6);
+ (void) net_event_unregister(nsp->vpnd_neti_v6,
+ &nsp->vpnd_event_in_v6);
+ (void) net_event_shutdown(nsp->vpnd_neti_v4,
+ &nsp->vpnd_event_in_v4);
+ (void) net_event_unregister(nsp->vpnd_neti_v4,
+ &nsp->vpnd_event_in_v4);
+ (void) net_family_unregister(nsp->vpnd_neti_v4,
+ &nsp->vpnd_family_v4);
+ (void) net_family_unregister(nsp->vpnd_neti_v6,
+ &nsp->vpnd_family_v6);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v4);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_event_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ nsp->vpnd_event_out_v6.he_version = HOOK_VERSION;
+ nsp->vpnd_event_out_v6.he_name = NH_PHYSICAL_OUT;
+ nsp->vpnd_event_out_v6.he_flags = 0;
+ nsp->vpnd_event_out_v6.he_interested = B_FALSE;
+
+ nsp->vpnd_token_out_v6 = net_event_register(nsp->vpnd_neti_v6,
+ &nsp->vpnd_event_out_v6);
+ if (nsp->vpnd_token_out_v6 == NULL) {
+ (void) net_event_shutdown(nsp->vpnd_neti_v6,
+ &nsp->vpnd_event_in_v6);
+ (void) net_event_unregister(nsp->vpnd_neti_v6,
+ &nsp->vpnd_event_in_v6);
+ (void) net_event_shutdown(nsp->vpnd_neti_v6,
+ &nsp->vpnd_event_in_v6);
+ (void) net_event_unregister(nsp->vpnd_neti_v6,
+ &nsp->vpnd_event_in_v6);
+ (void) net_event_shutdown(nsp->vpnd_neti_v4,
+ &nsp->vpnd_event_in_v4);
+ (void) net_event_unregister(nsp->vpnd_neti_v4,
+ &nsp->vpnd_event_in_v4);
+ (void) net_family_unregister(nsp->vpnd_neti_v4,
+ &nsp->vpnd_family_v4);
+ (void) net_family_unregister(nsp->vpnd_neti_v6,
+ &nsp->vpnd_family_v6);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v4);
+ (void) net_protocol_unregister(nsp->vpnd_neti_v6);
+ cmn_err(CE_NOTE, "vnd_netinfo_init: net_event_register "
+ "failed for stack %d", nsp->vpnd_nsid);
+ return (1);
+ }
+
+ return (0);
+}
+
+static void
+vnd_netinfo_shutdown(vnd_pnsd_t *nsp)
+{
+ int ret;
+
+ ret = net_event_shutdown(nsp->vpnd_neti_v4, &nsp->vpnd_event_in_v4);
+ VERIFY(ret == 0);
+ ret = net_event_shutdown(nsp->vpnd_neti_v4, &nsp->vpnd_event_out_v4);
+ VERIFY(ret == 0);
+ ret = net_event_shutdown(nsp->vpnd_neti_v6, &nsp->vpnd_event_in_v6);
+ VERIFY(ret == 0);
+ ret = net_event_shutdown(nsp->vpnd_neti_v6, &nsp->vpnd_event_out_v6);
+ VERIFY(ret == 0);
+}
+
+static void
+vnd_netinfo_fini(vnd_pnsd_t *nsp)
+{
+ int ret;
+
+ ret = net_event_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_event_in_v4);
+ VERIFY(ret == 0);
+ ret = net_event_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_event_out_v4);
+ VERIFY(ret == 0);
+ ret = net_event_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_event_in_v6);
+ VERIFY(ret == 0);
+ ret = net_event_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_event_out_v6);
+ VERIFY(ret == 0);
+ ret = net_family_unregister(nsp->vpnd_neti_v4, &nsp->vpnd_family_v4);
+ VERIFY(ret == 0);
+ ret = net_family_unregister(nsp->vpnd_neti_v6, &nsp->vpnd_family_v6);
+ VERIFY(ret == 0);
+ ret = net_protocol_unregister(nsp->vpnd_neti_v4);
+ VERIFY(ret == 0);
+ ret = net_protocol_unregister(nsp->vpnd_neti_v6);
+ VERIFY(ret == 0);
+}
+
+/* ARGSUSED */
+static void
+vnd_strbarrier_cb(void *arg, mblk_t *bmp, gsqueue_t *gsp, void *dummy)
+{
+ vnd_str_t *vsp = arg;
+
+ VERIFY(bmp == &vsp->vns_barrierblk);
+ mutex_enter(&vsp->vns_lock);
+ VERIFY(vsp->vns_flags & VNS_F_BARRIER);
+ VERIFY(!(vsp->vns_flags & VNS_F_BARRIER_DONE));
+ vsp->vns_flags |= VNS_F_BARRIER_DONE;
+ mutex_exit(&vsp->vns_lock);
+
+ /*
+ * For better or worse, we have to broadcast here as we could have a
+ * thread that's blocked for completion as well as one that's blocked
+ * waiting to do a barrier itself.
+ */
+ cv_broadcast(&vsp->vns_barriercv);
+}
+
+/*
+ * This is a data barrier for the stream while it is in fastpath mode. It blocks
+ * and ensures that there is nothing else in the squeue.
+ */
+static void
+vnd_strbarrier(vnd_str_t *vsp)
+{
+ mutex_enter(&vsp->vns_lock);
+ while (vsp->vns_flags & VNS_F_BARRIER)
+ cv_wait(&vsp->vns_barriercv, &vsp->vns_lock);
+ vsp->vns_flags |= VNS_F_BARRIER;
+ mutex_exit(&vsp->vns_lock);
+
+ gsqueue_enter_one(vsp->vns_squeue, &vsp->vns_barrierblk,
+ vnd_strbarrier_cb, vsp, GSQUEUE_PROCESS, VND_SQUEUE_TAG_STRBARRIER);
+
+ mutex_enter(&vsp->vns_lock);
+ while (!(vsp->vns_flags & VNS_F_BARRIER_DONE))
+ cv_wait(&vsp->vns_barriercv, &vsp->vns_lock);
+ vsp->vns_flags &= ~VNS_F_BARRIER;
+ vsp->vns_flags &= ~VNS_F_BARRIER_DONE;
+ mutex_exit(&vsp->vns_lock);
+
+ /*
+ * We have to broadcast in case anyone is waiting for the barrier
+ * themselves.
+ */
+ cv_broadcast(&vsp->vns_barriercv);
+}
+
+/*
+ * Based on the type of message that we're dealing with we're going to want to
+ * do one of several things. Basically if it looks like it's something we know
+ * about, we should probably handle it in one of our transition threads.
+ * Otherwise, we should just simply putnext.
+ */
+static int
+vnd_s_rput(queue_t *q, mblk_t *mp)
+{
+ t_uscalar_t prim;
+ int dispatch = 0;
+ vnd_str_t *vsp = q->q_ptr;
+
+ switch (DB_TYPE(mp)) {
+ case M_PROTO:
+ case M_PCPROTO:
+ if (MBLKL(mp) < sizeof (t_uscalar_t)) {
+ vnd_drop_ctl(vsp, mp, "PROTO message too short");
+ break;
+ }
+
+ prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
+ if (prim == DL_UNITDATA_REQ || prim == DL_UNITDATA_IND) {
+ vnd_drop_ctl(vsp, mp,
+ "recieved an unsupported dlpi DATA req");
+ break;
+ }
+
+ /*
+ * Enqueue the entry and fire off a taskq dispatch.
+ */
+ mutex_enter(&vsp->vns_lock);
+ vnd_dlpi_inc_push(vsp, mp);
+ if (!(vsp->vns_flags & VNS_F_TASKQ_DISPATCHED)) {
+ dispatch = 1;
+ vsp->vns_flags |= VNS_F_TASKQ_DISPATCHED;
+ }
+ mutex_exit(&vsp->vns_lock);
+ if (dispatch != 0)
+ taskq_dispatch_ent(vnd_taskq, vnd_dlpi_taskq_dispatch,
+ vsp, 0, &vsp->vns_tqe);
+ break;
+ case M_DATA:
+ vnd_drop_in(vsp, mp, "M_DATA via put(9E)");
+ break;
+ default:
+ putnext(vsp->vns_rq, mp);
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+vnd_strioctl(queue_t *q, vnd_str_t *vsp, mblk_t *mp, struct iocblk *iocp)
+{
+ int error;
+ vnd_strioc_t *visp;
+
+ if (iocp->ioc_cmd != VND_STRIOC_ASSOCIATE ||
+ iocp->ioc_count != TRANSPARENT) {
+ error = EINVAL;
+ goto nak;
+ }
+
+ /*
+ * All streams ioctls that we support must use kcred as a means to
+ * distinguish that this is a layered open by the kernel as opposed to
+ * one by a user who has done an I_PUSH of the module.
+ */
+ if (iocp->ioc_cr != kcred) {
+ error = EPERM;
+ goto nak;
+ }
+
+ if (mp->b_cont == NULL) {
+ error = EAGAIN;
+ goto nak;
+ }
+
+ visp = kmem_alloc(sizeof (vnd_strioc_t), KM_SLEEP);
+ ASSERT(MBLKL(mp->b_cont) == sizeof (caddr_t));
+ visp->vs_addr = *(caddr_t *)mp->b_cont->b_rptr;
+ visp->vs_state = VSS_COPYIN;
+
+ mcopyin(mp, (void *)visp, sizeof (vnd_strioc_associate_t), NULL);
+ qreply(q, mp);
+
+ return;
+
+nak:
+ if (mp->b_cont != NULL) {
+ freemsg(mp->b_cont);
+ mp->b_cont = NULL;
+ }
+
+ iocp->ioc_error = error;
+ mp->b_datap->db_type = M_IOCNAK;
+ iocp->ioc_count = 0;
+ qreply(q, mp);
+}
+
+static void
+vnd_striocdata(queue_t *q, vnd_str_t *vsp, mblk_t *mp, struct copyresp *csp)
+{
+ vnd_str_state_t state;
+ struct copyreq *crp;
+ vnd_strioc_associate_t *vss;
+ vnd_dev_t *vdp = NULL;
+ vnd_pnsd_t *nsp = NULL;
+ char iname[2*VND_NAMELEN];
+ zone_t *zone;
+ vnd_strioc_t *visp;
+
+ visp = (vnd_strioc_t *)csp->cp_private;
+
+ /* If it's not ours, it's not our problem */
+ if (csp->cp_cmd != VND_STRIOC_ASSOCIATE) {
+ if (q->q_next != NULL) {
+ putnext(q, mp);
+ } else {
+ VND_STAT_INC(vsp, vks_ndlpidrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+ vnd_drop_ctl(vsp, mp, "uknown cmd for M_IOCDATA");
+ }
+ kmem_free(visp, sizeof (vnd_strioc_t));
+ return;
+ }
+
+ /* The nak is already sent for us */
+ if (csp->cp_rval != 0) {
+ vnd_drop_ctl(vsp, mp, "M_COPYIN failed");
+ kmem_free(visp, sizeof (vnd_strioc_t));
+ return;
+ }
+
+ /* Data is sitting for us in b_cont */
+ if (mp->b_cont == NULL ||
+ MBLKL(mp->b_cont) != sizeof (vnd_strioc_associate_t)) {
+ kmem_free(visp, sizeof (vnd_strioc_t));
+ miocnak(q, mp, 0, EINVAL);
+ return;
+ }
+
+ vss = (vnd_strioc_associate_t *)mp->b_cont->b_rptr;
+ vdp = vnd_dev_lookup(vss->vsa_minor);
+ if (vdp == NULL) {
+ vss->vsa_errno = VND_E_NODEV;
+ goto nak;
+ }
+
+ nsp = vnd_nsd_lookup(vss->vsa_nsid);
+ if (nsp == NULL) {
+ vss->vsa_errno = VND_E_NONETSTACK;
+ goto nak;
+ }
+
+ mutex_enter(&vsp->vns_lock);
+ if (!(vsp->vns_flags & VNS_F_NEED_ZONE)) {
+ mutex_exit(&vsp->vns_lock);
+ vss->vsa_errno = VND_E_ASSOCIATED;
+ goto nak;
+ }
+
+ vsp->vns_nsd = nsp;
+ vsp->vns_flags &= ~VNS_F_NEED_ZONE;
+ vsp->vns_flags |= VNS_F_TASKQ_DISPATCHED;
+ mutex_exit(&vsp->vns_lock);
+
+ taskq_dispatch_ent(vnd_taskq, vnd_dlpi_taskq_dispatch, vsp, 0,
+ &vsp->vns_tqe);
+
+
+ /* At this point we need to wait until we have transitioned to ONLINE */
+ mutex_enter(&vsp->vns_lock);
+ while (vsp->vns_state != VNS_S_ONLINE && vsp->vns_state != VNS_S_ZOMBIE)
+ cv_wait(&vsp->vns_stcv, &vsp->vns_lock);
+ state = vsp->vns_state;
+ mutex_exit(&vsp->vns_lock);
+
+ if (state == VNS_S_ZOMBIE) {
+ vss->vsa_errno = vsp->vns_errno;
+ goto nak;
+ }
+
+ mutex_enter(&vdp->vdd_lock);
+ mutex_enter(&vsp->vns_lock);
+ VERIFY(vdp->vdd_str == NULL);
+ /*
+ * Now initialize the remaining kstat properties and let's go ahead and
+ * create it.
+ */
+ (void) snprintf(iname, sizeof (iname), "z%d_%d",
+ vdp->vdd_nsd->vpnd_zid, vdp->vdd_minor);
+ vsp->vns_kstat = kstat_create_zone("vnd", vdp->vdd_minor, iname, "net",
+ KSTAT_TYPE_NAMED, sizeof (vnd_str_stat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL, GLOBAL_ZONEID);
+ if (vsp->vns_kstat == NULL) {
+ vss->vsa_errno = VND_E_KSTATCREATE;
+ mutex_exit(&vsp->vns_lock);
+ mutex_exit(&vdp->vdd_lock);
+ goto nak;
+ }
+ vdp->vdd_str = vsp;
+ vsp->vns_dev = vdp;
+
+ /*
+ * Now, it's time to do the las thing that can fail, changing out the
+ * input function. After this we know that we can receive data, so we
+ * should make sure that we're ready.
+ */
+ if (vnd_dld_cap_enable(vsp, vnd_mac_input) != 0) {
+ vss->vsa_errno = VND_E_DIRECTFAIL;
+ vdp->vdd_str = NULL;
+ vsp->vns_dev = NULL;
+ mutex_exit(&vsp->vns_lock);
+ mutex_exit(&vdp->vdd_lock);
+ goto nak;
+ }
+
+ zone = zone_find_by_id(vdp->vdd_nsd->vpnd_zid);
+ ASSERT(zone != NULL);
+ vsp->vns_kstat->ks_data = &vsp->vns_ksdata;
+ /* Account for zone name */
+ vsp->vns_kstat->ks_data_size += strlen(zone->zone_name) + 1;
+ /* Account for eventual link name */
+ vsp->vns_kstat->ks_data_size += VND_NAMELEN;
+ kstat_named_setstr(&vsp->vns_ksdata.vks_zonename, zone->zone_name);
+ kstat_named_setstr(&vdp->vdd_str->vns_ksdata.vks_linkname,
+ vdp->vdd_lname);
+ zone_rele(zone);
+ kstat_install(vsp->vns_kstat);
+
+ mutex_exit(&vsp->vns_lock);
+ mutex_exit(&vdp->vdd_lock);
+
+ /*
+ * Note that the vnd_str_t does not keep a permanent hold on the
+ * vnd_pnsd_t. We leave that up to the vnd_dev_t as that's also what
+ * the nestack goes through to take care of everything.
+ */
+ vss->vsa_errno = VND_E_SUCCESS;
+nak:
+ if (vdp != NULL)
+ vnd_dev_rele(vdp);
+ if (nsp != NULL)
+ vnd_nsd_rele(nsp);
+ /*
+ * Change the copyin request to a copyout. Note that we can't use
+ * mcopyout here as it only works when the DB_TYPE is M_IOCTL. That's
+ * okay, as the copyin vs. copyout is basically the same.
+ */
+ DB_TYPE(mp) = M_COPYOUT;
+ visp->vs_state = VSS_COPYOUT;
+ crp = (struct copyreq *)mp->b_rptr;
+ crp->cq_private = (void *)visp;
+ crp->cq_addr = visp->vs_addr;
+ crp->cq_size = sizeof (vnd_strioc_associate_t);
+ qreply(q, mp);
+}
+
+static void
+vnd_stroutdata(queue_t *q, vnd_str_t *vsp, mblk_t *mp, struct copyresp *csp)
+{
+ ASSERT(csp->cp_private != NULL);
+ kmem_free(csp->cp_private, sizeof (vnd_strioc_t));
+ if (csp->cp_cmd != VND_STRIOC_ASSOCIATE) {
+ if (q->q_next != NULL) {
+ putnext(q, mp);
+ } else {
+ VND_STAT_INC(vsp, vks_ndlpidrops, 1);
+ VND_STAT_INC(vsp, vks_tdrops, 1);
+ vnd_drop_ctl(vsp, mp, "uknown cmd for M_IOCDATA");
+ }
+ return;
+ }
+
+ /* The nak is already sent for us */
+ if (csp->cp_rval != 0) {
+ vnd_drop_ctl(vsp, mp, "M_COPYOUT failed");
+ return;
+ }
+
+ /* Ack and let's be done with it all */
+ miocack(q, mp, 0, 0);
+}
+
+static int
+vnd_s_wput(queue_t *q, mblk_t *mp)
+{
+ vnd_str_t *vsp = q->q_ptr;
+ struct copyresp *crp;
+ vnd_strioc_state_t vstate;
+ vnd_strioc_t *visp;
+
+ switch (DB_TYPE(mp)) {
+ case M_IOCTL:
+ vnd_strioctl(q, vsp, mp, (struct iocblk *)mp->b_rptr);
+ return (0);
+ case M_IOCDATA:
+ crp = (struct copyresp *)mp->b_rptr;
+ ASSERT(crp->cp_private != NULL);
+ visp = (vnd_strioc_t *)crp->cp_private;
+ vstate = visp->vs_state;
+ ASSERT(vstate == VSS_COPYIN || vstate == VSS_COPYOUT);
+ if (vstate == VSS_COPYIN)
+ vnd_striocdata(q, vsp, mp,
+ (struct copyresp *)mp->b_rptr);
+ else
+ vnd_stroutdata(q, vsp, mp,
+ (struct copyresp *)mp->b_rptr);
+ return (0);
+ default:
+ break;
+ }
+ if (q->q_next != NULL)
+ putnext(q, mp);
+ else
+ vnd_drop_ctl(vsp, mp, "!M_IOCTL in wput");
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+vnd_s_open(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *credp)
+{
+ vnd_str_t *vsp;
+ uint_t rand;
+
+ if (q->q_ptr != NULL)
+ return (EINVAL);
+
+ if (!(sflag & MODOPEN))
+ return (ENXIO);
+
+ if (credp != kcred)
+ return (EPERM);
+
+ vsp = kmem_cache_alloc(vnd_str_cache, KM_SLEEP);
+ bzero(vsp, sizeof (*vsp));
+ mutex_init(&vsp->vns_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&vsp->vns_cancelcv, NULL, CV_DRIVER, NULL);
+ cv_init(&vsp->vns_barriercv, NULL, CV_DRIVER, NULL);
+ cv_init(&vsp->vns_stcv, NULL, CV_DRIVER, NULL);
+ vsp->vns_state = VNS_S_INITIAL;
+
+ mutex_init(&vsp->vns_dq_read.vdq_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_init(&vsp->vns_dq_write.vdq_lock, NULL, MUTEX_DRIVER, NULL);
+ mutex_enter(&vnd_dev_lock);
+ vsp->vns_dq_read.vdq_max = vnd_vdq_default_size;
+ vsp->vns_dq_read.vdq_vns = vsp;
+ vsp->vns_dq_write.vdq_max = vnd_vdq_default_size;
+ vsp->vns_dq_write.vdq_vns = vsp;
+ mutex_exit(&vnd_dev_lock);
+ vsp->vns_rq = q;
+ vsp->vns_wq = WR(q);
+ q->q_ptr = WR(q)->q_ptr = vsp;
+ vsp->vns_flags = VNS_F_NEED_ZONE;
+ vsp->vns_nflush = vnd_flush_nburst;
+ vsp->vns_bsize = vnd_flush_burst_size;
+
+ (void) random_get_pseudo_bytes((uint8_t *)&rand, sizeof (rand));
+ vsp->vns_squeue = gsqueue_set_get(vnd_sqset, rand);
+
+ /*
+ * We create our kstat and initialize all of its fields now, but we
+ * don't install it until we actually do the zone association so we can
+ * get everything.
+ */
+ kstat_named_init(&vsp->vns_ksdata.vks_rbytes, "rbytes",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_rpackets, "rpackets",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_obytes, "obytes",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_opackets, "opackets",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_nhookindrops, "nhookindrops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_nhookoutdrops, "nhookoutdrops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_ndlpidrops, "ndlpidrops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_ndataindrops, "ndataindrops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_ndataoutdrops, "ndataoutdrops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_tdrops, "total_drops",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_linkname, "linkname",
+ KSTAT_DATA_STRING);
+ kstat_named_init(&vsp->vns_ksdata.vks_zonename, "zonename",
+ KSTAT_DATA_STRING);
+ kstat_named_init(&vsp->vns_ksdata.vks_nmacflow, "flowcontrol_events",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_tmacflow, "flowcontrol_time",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_mac_flow_1ms, "flowcontrol_1ms",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_mac_flow_10ms, "flowcontrol_10ms",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_mac_flow_100ms,
+ "flowcontrol_100ms", KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_mac_flow_1s, "flowcontrol_1s",
+ KSTAT_DATA_UINT64);
+ kstat_named_init(&vsp->vns_ksdata.vks_mac_flow_10s, "flowcontrol_10s",
+ KSTAT_DATA_UINT64);
+ qprocson(q);
+ /*
+ * Now that we've called qprocson, grab the lower module for making sure
+ * that we don't have any pass through modules.
+ */
+ vsp->vns_lrq = RD(vsp->vns_wq->q_next);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+vnd_s_close(queue_t *q, int flag, cred_t *credp)
+{
+ vnd_str_t *vsp;
+ mblk_t *mp;
+
+ VERIFY(WR(q)->q_next != NULL);
+
+ vsp = q->q_ptr;
+ ASSERT(vsp != NULL);
+
+ /*
+ * We need to transition ourselves down. This means that we have a few
+ * important different things to do in the process of tearing down our
+ * input and output buffers, making sure we've drained the current
+ * squeue, and disabling the fast path. Before we disable the fast path,
+ * we should make sure the squeue is drained. Because we're in streams
+ * close, we know that no packets can come into us from userland, but we
+ * can receive more. As such, the following is the exact order of things
+ * that we do:
+ *
+ * 1) flush the vns_dq_read
+ * 2) Insert the drain mblk
+ * 3) When it's been received, tear down the fast path by kicking
+ * off the state machine.
+ * 4) One final flush of both the vns_dq_read,vns_dq_write
+ */
+
+ vnd_dq_flush(&vsp->vns_dq_read, vnd_drop_in);
+ vnd_strbarrier(vsp);
+ mutex_enter(&vsp->vns_lock);
+ vsp->vns_state = VNS_S_SHUTTING_DOWN;
+ if (!(vsp->vns_flags & VNS_F_TASKQ_DISPATCHED)) {
+ vsp->vns_flags |= VNS_F_TASKQ_DISPATCHED;
+ taskq_dispatch_ent(vnd_taskq, vnd_dlpi_taskq_dispatch, vsp,
+ 0, &vsp->vns_tqe);
+ }
+ while (vsp->vns_state != VNS_S_ZOMBIE)
+ cv_wait(&vsp->vns_stcv, &vsp->vns_lock);
+ mutex_exit(&vsp->vns_lock);
+
+ qprocsoff(q);
+ mutex_enter(&vsp->vns_lock);
+ vsp->vns_flags |= VNS_F_CONDEMNED;
+ while (vsp->vns_flags & VNS_F_TASKQ_DISPATCHED)
+ cv_wait(&vsp->vns_cancelcv, &vsp->vns_lock);
+
+ while ((mp = vnd_dlpi_inc_pop(vsp)) != NULL)
+ vnd_drop_ctl(vsp, mp, "vnd_s_close");
+ mutex_exit(&vsp->vns_lock);
+
+ q->q_ptr = NULL;
+ vnd_dq_flush(&vsp->vns_dq_read, vnd_drop_in);
+ vnd_dq_flush(&vsp->vns_dq_write, vnd_drop_out);
+ mutex_destroy(&vsp->vns_dq_read.vdq_lock);
+ mutex_destroy(&vsp->vns_dq_write.vdq_lock);
+
+ if (vsp->vns_kstat != NULL)
+ kstat_delete(vsp->vns_kstat);
+ mutex_destroy(&vsp->vns_lock);
+ cv_destroy(&vsp->vns_stcv);
+ cv_destroy(&vsp->vns_barriercv);
+ cv_destroy(&vsp->vns_cancelcv);
+ kmem_cache_free(vnd_str_cache, vsp);
+
+ return (0);
+}
+
+static vnd_mac_cookie_t
+vnd_squeue_tx_one(vnd_str_t *vsp, mblk_t *mp)
+{
+ hrtime_t txtime;
+ vnd_mac_cookie_t vc;
+
+ VND_STAT_INC(vsp, vks_opackets, 1);
+ VND_STAT_INC(vsp, vks_obytes, msgsize(mp));
+ DTRACE_VND5(send, mblk_t *, mp, void *, NULL, void *, NULL,
+ vnd_str_t *, vsp, mblk_t *, mp);
+ /* Actually tx now */
+ txtime = gethrtime();
+ vc = vsp->vns_caps.vsc_tx_f(vsp->vns_caps.vsc_tx_hdl,
+ mp, 0, MAC_DROP_ON_NO_DESC);
+
+ /*
+ * We need to check two different conditions before we immediately set
+ * the flow control lock. The first thing that we need to do is verify
+ * that this is an instance of hard flow control, so to say. The flow
+ * control callbacks won't always fire in cases where we still get a
+ * cookie returned. The explicit check for flow control will guarantee
+ * us that we'll get a subsequent notification callback.
+ *
+ * The second case comes about because we do not hold the
+ * vnd_str_t`vns_lock across calls to tx, we need to determine if a flow
+ * control notification already came across for us in a different thread
+ * calling vnd_mac_flow_control(). To deal with this, we record a
+ * timestamp every time that we change the flow control state. We grab
+ * txtime here before we transmit because that guarantees that the
+ * hrtime_t of the call to vnd_mac_flow_control() will be after txtime.
+ *
+ * If the flow control notification beat us to the punch, the value of
+ * vns_fcupdate will be larger than the value of txtime, and we should
+ * just record the statistics. However, if we didn't beat it to the
+ * punch (txtime > vns_fcupdate), then we know that it's safe to wait
+ * for a notification.
+ */
+ if (vc != (vnd_mac_cookie_t)NULL) {
+ hrtime_t diff;
+
+ if (vsp->vns_caps.vsc_is_fc_f(vsp->vns_caps.vsc_is_fc_hdl,
+ vc) == 0)
+ return ((vnd_mac_cookie_t)NULL);
+ mutex_enter(&vsp->vns_lock);
+ diff = vsp->vns_fcupdate - txtime;
+ if (diff > 0) {
+ mutex_exit(&vsp->vns_lock);
+ vnd_mac_flow_control_stat(vsp, diff);
+ return ((vnd_mac_cookie_t)NULL);
+ }
+ vsp->vns_flags |= VNS_F_FLOW_CONTROLLED;
+ vsp->vns_caps.vsc_fc_cookie = vc;
+ vsp->vns_fclatch = txtime;
+ vsp->vns_fcupdate = txtime;
+ DTRACE_VND3(flow__blocked, vnd_str_t *, vsp,
+ uint64_t, vsp->vns_dq_write.vdq_cur, uintptr_t, vc);
+ mutex_exit(&vsp->vns_lock);
+ }
+
+ return (vc);
+}
+
+/* ARGSUSED */
+static void
+vnd_squeue_tx_drain(void *arg, mblk_t *drain_mp, gsqueue_t *gsp, void *dummy)
+{
+ mblk_t *mp;
+ int nmps;
+ size_t mptot, nflush, bsize;
+ boolean_t blocked, empty;
+ vnd_data_queue_t *vqp;
+ vnd_str_t *vsp = arg;
+
+ mutex_enter(&vsp->vns_lock);
+ /*
+ * We either enter here via an squeue or via vnd_squeue_tx_append(). In
+ * the former case we need to mark that there is no longer an active
+ * user of the drain block.
+ */
+ if (drain_mp != NULL) {
+ VERIFY(drain_mp == &vsp->vns_drainblk);
+ VERIFY(vsp->vns_flags & VNS_F_DRAIN_SCHEDULED);
+ vsp->vns_flags &= ~VNS_F_DRAIN_SCHEDULED;
+ }
+
+ /*
+ * If we're still flow controlled or under a flush barrier, nothing to
+ * do.
+ */
+ if (vsp->vns_flags & (VNS_F_FLOW_CONTROLLED | VNS_F_BARRIER)) {
+ mutex_exit(&vsp->vns_lock);
+ return;
+ }
+
+ nflush = vsp->vns_nflush;
+ bsize = vsp->vns_bsize;
+ mutex_exit(&vsp->vns_lock);
+
+ /*
+ * We're potentially going deep into the networking layer; make sure the
+ * guest can't run concurrently.
+ */
+ smt_begin_unsafe();
+
+ nmps = 0;
+ mptot = 0;
+ blocked = B_FALSE;
+ vqp = &vsp->vns_dq_write;
+ while (nmps < nflush && mptot <= bsize) {
+ mutex_enter(&vqp->vdq_lock);
+ if (vnd_dq_pop(vqp, &mp) == 0) {
+ mutex_exit(&vqp->vdq_lock);
+ break;
+ }
+ mutex_exit(&vqp->vdq_lock);
+
+ nmps++;
+ mptot += msgsize(mp);
+ if (vnd_squeue_tx_one(vsp, mp) != (vnd_mac_cookie_t)NULL) {
+ blocked = B_TRUE;
+ break;
+ }
+ }
+
+ smt_end_unsafe();
+
+ empty = vnd_dq_is_empty(&vsp->vns_dq_write);
+
+ /*
+ * If the queue is not empty, we're not blocked, and there isn't a drain
+ * scheduled, put it into the squeue with the drain block and
+ * GSQUEUE_FILL.
+ */
+ if (blocked == B_FALSE && empty == B_FALSE) {
+ mutex_enter(&vsp->vns_lock);
+ if (!(vsp->vns_flags & VNS_F_DRAIN_SCHEDULED)) {
+ mblk_t *mp = &vsp->vns_drainblk;
+ vsp->vns_flags |= VNS_F_DRAIN_SCHEDULED;
+ gsqueue_enter_one(vsp->vns_squeue,
+ mp, vnd_squeue_tx_drain, vsp,
+ GSQUEUE_FILL, VND_SQUEUE_TAG_TX_DRAIN);
+ }
+ mutex_exit(&vsp->vns_lock);
+ }
+
+ /*
+ * If we drained some amount of data, we need to signal the data queue.
+ */
+ if (nmps > 0) {
+ cv_broadcast(&vsp->vns_dq_write.vdq_ready);
+ pollwakeup(&vsp->vns_dev->vdd_ph, POLLOUT);
+ }
+}
+
+/* ARGSUSED */
+static void
+vnd_squeue_tx_append(void *arg, mblk_t *mp, gsqueue_t *gsp, void *dummy)
+{
+ vnd_str_t *vsp = arg;
+ vnd_data_queue_t *vqp = &vsp->vns_dq_write;
+ vnd_pnsd_t *nsp = vsp->vns_nsd;
+ size_t len = msgsize(mp);
+
+ /*
+ * Before we append this packet, we should run it through the firewall
+ * rules.
+ */
+ if (nsp->vpnd_hooked && vnd_hook(vsp, &mp, nsp->vpnd_neti_v4,
+ nsp->vpnd_event_out_v4, nsp->vpnd_token_out_v4, nsp->vpnd_neti_v6,
+ nsp->vpnd_event_out_v6, nsp->vpnd_token_out_v6, vnd_drop_hook_out,
+ vnd_drop_out) != 0) {
+ /*
+ * Because we earlier reserved space for this packet and it's
+ * not making the cut, we need to go through and unreserve that
+ * space. Also note that the message block will likely be freed
+ * by the time we return from vnd_hook so we cannot rely on it.
+ */
+ mutex_enter(&vqp->vdq_lock);
+ vnd_dq_unreserve(vqp, len);
+ mutex_exit(&vqp->vdq_lock);
+ return;
+ }
+
+ /*
+ * We earlier reserved space for this packet. So for now simply append
+ * it and call drain. We know that no other drain can be going on right
+ * now thanks to the squeue.
+ */
+ mutex_enter(&vqp->vdq_lock);
+ (void) vnd_dq_push(&vsp->vns_dq_write, mp, B_TRUE, vnd_drop_panic);
+ mutex_exit(&vqp->vdq_lock);
+ vnd_squeue_tx_drain(vsp, NULL, NULL, NULL);
+}
+
+/*
+ * We need to see if this is a valid name of sorts for us. That means a few
+ * things. First off, we can't assume that what we've been given has actually
+ * been null terminated. More importantly, that it's a valid name as far as
+ * ddi_create_minor_node is concerned (that means no '@', '/', or ' '). We
+ * further constrain ourselves to simply alphanumeric characters and a few
+ * additional ones, ':', '-', and '_'.
+ */
+static int
+vnd_validate_name(const char *buf, size_t buflen)
+{
+ int i, len;
+
+ /* First make sure a null terminator exists */
+ for (i = 0; i < buflen; i++)
+ if (buf[i] == '\0')
+ break;
+ len = i;
+ if (i == 0 || i == buflen)
+ return (0);
+
+ for (i = 0; i < len; i++)
+ if (!isalnum(buf[i]) && buf[i] != ':' && buf[i] != '-' &&
+ buf[i] != '_')
+ return (0);
+
+ return (1);
+}
+
+static int
+vnd_ioctl_attach(vnd_dev_t *vdp, uintptr_t arg, cred_t *credp, int cpflag)
+{
+ vnd_ioc_attach_t via;
+ vnd_strioc_associate_t vss;
+ vnd_pnsd_t *nsp;
+ zone_t *zonep;
+ zoneid_t zid;
+ char buf[2*VND_NAMELEN];
+ int ret, rp;
+
+ if (secpolicy_net_config(credp, B_FALSE) != 0)
+ return (EPERM);
+
+ if (secpolicy_net_rawaccess(credp) != 0)
+ return (EPERM);
+
+ if (ddi_copyin((void *)arg, &via, sizeof (via), cpflag) != 0)
+ return (EFAULT);
+ via.via_errno = VND_E_SUCCESS;
+
+ if (vnd_validate_name(via.via_name, VND_NAMELEN) == 0) {
+ via.via_errno = VND_E_BADNAME;
+ ret = EIO;
+ goto errcopyout;
+ }
+
+ /*
+ * Only the global zone can request to create a device in a different
+ * zone.
+ */
+ zid = crgetzoneid(credp);
+ if (zid != GLOBAL_ZONEID && via.via_zoneid != -1 &&
+ zid != via.via_zoneid) {
+ via.via_errno = VND_E_PERM;
+ ret = EIO;
+ goto errcopyout;
+ }
+
+ if (via.via_zoneid == -1)
+ via.via_zoneid = zid;
+
+ /*
+ * Establish the name we'll use now. We want to be extra paranoid about
+ * the device we're opening so check that now.
+ */
+ if (zid == GLOBAL_ZONEID && via.via_zoneid != zid) {
+ zonep = zone_find_by_id(via.via_zoneid);
+ if (zonep == NULL) {
+ via.via_errno = VND_E_NOZONE;
+ ret = EIO;
+ goto errcopyout;
+ }
+ if (snprintf(NULL, 0, "/dev/net/zone/%s/%s", zonep->zone_name,
+ via.via_name) >= sizeof (buf)) {
+ zone_rele(zonep);
+ via.via_errno = VND_E_BADNAME;
+ ret = EIO;
+ goto errcopyout;
+ }
+ (void) snprintf(buf, sizeof (buf), "/dev/net/zone/%s/%s",
+ zonep->zone_name, via.via_name);
+ zone_rele(zonep);
+ zonep = NULL;
+ } else {
+ if (snprintf(NULL, 0, "/dev/net/%s", via.via_name) >=
+ sizeof (buf)) {
+ via.via_errno = VND_E_BADNAME;
+ ret = EIO;
+ goto errcopyout;
+ }
+ (void) snprintf(buf, sizeof (buf), "/dev/net/%s", via.via_name);
+ }
+
+ /*
+ * If our zone is dying then the netstack will have been removed from
+ * this list.
+ */
+ nsp = vnd_nsd_lookup_by_zid(via.via_zoneid);
+ if (nsp == NULL) {
+ via.via_errno = VND_E_NOZONE;
+ ret = EIO;
+ goto errcopyout;
+ }
+
+ /*
+ * Note we set the attached handle even though we haven't actually
+ * finished the process of attaching the ldi handle.
+ */
+ mutex_enter(&vdp->vdd_lock);
+ if (vdp->vdd_flags & (VND_D_ATTACHED | VND_D_ATTACH_INFLIGHT)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_nsd_rele(nsp);
+ via.via_errno = VND_E_ATTACHED;
+ ret = EIO;
+ goto errcopyout;
+ }
+ vdp->vdd_flags |= VND_D_ATTACH_INFLIGHT;
+ ASSERT(vdp->vdd_cr == NULL);
+ crhold(credp);
+ vdp->vdd_cr = credp;
+ ASSERT(vdp->vdd_nsd == NULL);
+ vdp->vdd_nsd = nsp;
+ mutex_exit(&vdp->vdd_lock);
+
+ /*
+ * Place an additional hold on the vnd_pnsd_t as we go through and do
+ * all of the rest of our work. This will be the hold that we keep for
+ * as long as this thing is attached.
+ */
+ vnd_nsd_ref(nsp);
+
+ ret = ldi_open_by_name(buf, FREAD | FWRITE, vdp->vdd_cr,
+ &vdp->vdd_ldih, vdp->vdd_ldiid);
+ if (ret != 0) {
+ if (ret == ENODEV)
+ via.via_errno = VND_E_NODATALINK;
+ goto err;
+ }
+
+ /*
+ * Unfortunately the I_PUSH interface doesn't allow us a way to detect
+ * whether or not we're coming in from a layered device. We really want
+ * to make sure that a normal user can't push on our streams module.
+ * Currently the only idea I have for this is to make sure that the
+ * credp is kcred which is really terrible.
+ */
+ ret = ldi_ioctl(vdp->vdd_ldih, I_PUSH, (intptr_t)"vnd", FKIOCTL,
+ kcred, &rp);
+ if (ret != 0) {
+ rp = ldi_close(vdp->vdd_ldih, FREAD | FWRITE, vdp->vdd_cr);
+ VERIFY(rp == 0);
+ via.via_errno = VND_E_STRINIT;
+ ret = EIO;
+ goto err;
+ }
+
+ vss.vsa_minor = vdp->vdd_minor;
+ vss.vsa_nsid = nsp->vpnd_nsid;
+
+ ret = ldi_ioctl(vdp->vdd_ldih, VND_STRIOC_ASSOCIATE, (intptr_t)&vss,
+ FKIOCTL, kcred, &rp);
+ if (ret != 0 || vss.vsa_errno != VND_E_SUCCESS) {
+ rp = ldi_close(vdp->vdd_ldih, FREAD | FWRITE, vdp->vdd_cr);
+ VERIFY(rp == 0);
+ if (ret == 0) {
+ via.via_errno = vss.vsa_errno;
+ ret = EIO;
+ }
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_nsd->vpnd_lock);
+
+ /*
+ * There's a chance that our netstack was condemned while we've had a
+ * hold on it. As such we need to check and if so, error out.
+ */
+ if (vdp->vdd_nsd->vpnd_flags & VND_NS_CONDEMNED) {
+ mutex_exit(&vdp->vdd_nsd->vpnd_lock);
+ rp = ldi_close(vdp->vdd_ldih, FREAD | FWRITE, vdp->vdd_cr);
+ VERIFY(rp == 0);
+ ret = EIO;
+ via.via_errno = VND_E_NOZONE;
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_lock);
+ VERIFY(vdp->vdd_str != NULL);
+ vdp->vdd_flags &= ~VND_D_ATTACH_INFLIGHT;
+ vdp->vdd_flags |= VND_D_ATTACHED;
+ (void) strlcpy(vdp->vdd_datalink, via.via_name,
+ sizeof (vdp->vdd_datalink));
+ list_insert_tail(&vdp->vdd_nsd->vpnd_dev_list, vdp);
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vdp->vdd_nsd->vpnd_lock);
+ vnd_nsd_rele(nsp);
+
+ return (0);
+
+err:
+ mutex_enter(&vdp->vdd_lock);
+ vdp->vdd_flags &= ~VND_D_ATTACH_INFLIGHT;
+ crfree(vdp->vdd_cr);
+ vdp->vdd_cr = NULL;
+ vdp->vdd_nsd = NULL;
+ mutex_exit(&vdp->vdd_lock);
+
+ /*
+ * We have two holds to drop here. One for our original reference and
+ * one for the hold this operation would have represented.
+ */
+ vnd_nsd_rele(nsp);
+ vnd_nsd_rele(nsp);
+errcopyout:
+ if (ddi_copyout(&via, (void *)arg, sizeof (via), cpflag) != 0)
+ ret = EFAULT;
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_link(vnd_dev_t *vdp, intptr_t arg, cred_t *credp, int cpflag)
+{
+ int ret = 0;
+ vnd_ioc_link_t vil;
+ char mname[2*VND_NAMELEN];
+ char **c;
+ vnd_dev_t *v;
+ zoneid_t zid;
+
+ /* Not anyone can link something */
+ if (secpolicy_net_config(credp, B_FALSE) != 0)
+ return (EPERM);
+
+ if (ddi_copyin((void *)arg, &vil, sizeof (vil), cpflag) != 0)
+ return (EFAULT);
+
+ if (vnd_validate_name(vil.vil_name, VND_NAMELEN) == 0) {
+ ret = EIO;
+ vil.vil_errno = VND_E_BADNAME;
+ goto errcopyout;
+ }
+
+ c = vnd_reserved_names;
+ while (*c != NULL) {
+ if (strcmp(vil.vil_name, *c) == 0) {
+ ret = EIO;
+ vil.vil_errno = VND_E_BADNAME;
+ goto errcopyout;
+ }
+ c++;
+ }
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ ret = EIO;
+ vil.vil_errno = VND_E_NOTATTACHED;
+ goto errcopyout;
+ }
+
+ if (vdp->vdd_flags & VND_D_ZONE_DYING) {
+ mutex_exit(&vdp->vdd_lock);
+ ret = EIO;
+ vil.vil_errno = VND_E_NOZONE;
+ goto errcopyout;
+ }
+
+ if (vdp->vdd_flags & (VND_D_LINK_INFLIGHT | VND_D_LINKED)) {
+ mutex_exit(&vdp->vdd_lock);
+ ret = EIO;
+ vil.vil_errno = VND_E_LINKED;
+ goto errcopyout;
+ }
+ vdp->vdd_flags |= VND_D_LINK_INFLIGHT;
+ zid = vdp->vdd_nsd->vpnd_zid;
+ mutex_exit(&vdp->vdd_lock);
+
+ if (snprintf(NULL, 0, "z%d:%s", zid, vil.vil_name) >=
+ sizeof (mname)) {
+ ret = EIO;
+ vil.vil_errno = VND_E_BADNAME;
+ goto errcopyout;
+ }
+
+ mutex_enter(&vnd_dev_lock);
+ for (v = list_head(&vnd_dev_list); v != NULL;
+ v = list_next(&vnd_dev_list, v)) {
+ if (!(v->vdd_flags & VND_D_LINKED))
+ continue;
+
+ if (v->vdd_nsd->vpnd_zid == zid &&
+ strcmp(v->vdd_lname, vil.vil_name) == 0) {
+ mutex_exit(&vnd_dev_lock);
+ ret = EIO;
+ vil.vil_errno = VND_E_LINKEXISTS;
+ goto error;
+ }
+ }
+
+ /*
+ * We set the name and mark ourselves attached while holding the list
+ * lock to ensure that no other user can mistakingly find our name.
+ */
+ (void) snprintf(mname, sizeof (mname), "z%d:%s", zid,
+ vil.vil_name);
+ mutex_enter(&vdp->vdd_lock);
+
+ /*
+ * Because we dropped our lock, we need to double check whether or not
+ * the zone was marked as dying while we were here. If it hasn't, then
+ * it's safe for us to link it in.
+ */
+ if (vdp->vdd_flags & VND_D_ZONE_DYING) {
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vnd_dev_lock);
+ ret = EIO;
+ vil.vil_errno = VND_E_NOZONE;
+ goto error;
+ }
+
+ (void) strlcpy(vdp->vdd_lname, vil.vil_name, sizeof (vdp->vdd_lname));
+ if (ddi_create_minor_node(vnd_dip, mname, S_IFCHR, vdp->vdd_minor,
+ DDI_PSEUDO, 0) != DDI_SUCCESS) {
+ ret = EIO;
+ vil.vil_errno = VND_E_MINORNODE;
+ } else {
+ vdp->vdd_flags &= ~VND_D_LINK_INFLIGHT;
+ vdp->vdd_flags |= VND_D_LINKED;
+ kstat_named_setstr(&vdp->vdd_str->vns_ksdata.vks_linkname,
+ vdp->vdd_lname);
+ ret = 0;
+ }
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vnd_dev_lock);
+
+ if (ret == 0) {
+ /*
+ * Add a reference to represent that this device is linked into
+ * the file system name space to ensure that it doesn't
+ * disappear.
+ */
+ vnd_dev_ref(vdp);
+ return (0);
+ }
+
+error:
+ mutex_enter(&vdp->vdd_lock);
+ vdp->vdd_flags &= ~VND_D_LINK_INFLIGHT;
+ vdp->vdd_lname[0] = '\0';
+ mutex_exit(&vdp->vdd_lock);
+
+errcopyout:
+ if (ddi_copyout(&vil, (void *)arg, sizeof (vil), cpflag) != 0)
+ ret = EFAULT;
+ return (ret);
+}
+
+/*
+ * Common unlink function. This is used both from the ioctl path and from the
+ * netstack shutdown path. The caller is required to hold the mutex on the
+ * vnd_dev_t, but they basically will have it relinquished for them. The only
+ * thing the caller is allowed to do afterward is to potentially rele the
+ * vnd_dev_t if they have their own hold. Note that only the ioctl path has its
+ * own hold.
+ */
+static void
+vnd_dev_unlink(vnd_dev_t *vdp)
+{
+ char mname[2*VND_NAMELEN];
+
+ ASSERT(MUTEX_HELD(&vdp->vdd_lock));
+
+ (void) snprintf(mname, sizeof (mname), "z%d:%s",
+ vdp->vdd_nsd->vpnd_zid, vdp->vdd_lname);
+ ddi_remove_minor_node(vnd_dip, mname);
+ vdp->vdd_lname[0] = '\0';
+ vdp->vdd_flags &= ~VND_D_LINKED;
+ kstat_named_setstr(&vdp->vdd_str->vns_ksdata.vks_linkname,
+ vdp->vdd_lname);
+ mutex_exit(&vdp->vdd_lock);
+
+ /*
+ * This rele corresponds to the reference that we took in
+ * vnd_ioctl_link.
+ */
+ vnd_dev_rele(vdp);
+}
+
+static int
+vnd_ioctl_unlink(vnd_dev_t *vdp, intptr_t arg, cred_t *credp, int cpflag)
+{
+ int ret;
+ zoneid_t zid;
+ vnd_ioc_unlink_t viu;
+
+ /* Not anyone can unlink something */
+ if (secpolicy_net_config(credp, B_FALSE) != 0)
+ return (EPERM);
+
+ zid = crgetzoneid(credp);
+
+ if (ddi_copyin((void *)arg, &viu, sizeof (viu), cpflag) != 0)
+ return (EFAULT);
+
+ viu.viu_errno = VND_E_SUCCESS;
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_LINKED)) {
+ mutex_exit(&vdp->vdd_lock);
+ ret = EIO;
+ viu.viu_errno = VND_E_NOTLINKED;
+ goto err;
+ }
+ VERIFY(vdp->vdd_flags & VND_D_ATTACHED);
+
+ if (zid != GLOBAL_ZONEID && zid != vdp->vdd_nsd->vpnd_zid) {
+ mutex_exit(&vdp->vdd_lock);
+ ret = EIO;
+ viu.viu_errno = VND_E_PERM;
+ goto err;
+ }
+
+ /* vnd_dev_unlink releases the vdp mutex for us */
+ vnd_dev_unlink(vdp);
+ ret = 0;
+err:
+ if (ddi_copyout(&viu, (void *)arg, sizeof (viu), cpflag) != 0)
+ return (EFAULT);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_setrxbuf(vnd_dev_t *vdp, intptr_t arg, int cpflag)
+{
+ int ret;
+ vnd_ioc_buf_t vib;
+
+ if (ddi_copyin((void *)arg, &vib, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ mutex_enter(&vnd_dev_lock);
+ if (vib.vib_size > vnd_vdq_hard_max) {
+ mutex_exit(&vnd_dev_lock);
+ vib.vib_errno = VND_E_BUFTOOBIG;
+ ret = EIO;
+ goto err;
+ }
+ mutex_exit(&vnd_dev_lock);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_NOTATTACHED;
+ ret = EIO;
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_str->vns_lock);
+ if (vib.vib_size < vdp->vdd_str->vns_minwrite) {
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_BUFTOOSMALL;
+ ret = EIO;
+ goto err;
+ }
+
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ mutex_enter(&vdp->vdd_str->vns_dq_read.vdq_lock);
+ vdp->vdd_str->vns_dq_read.vdq_max = (size_t)vib.vib_size;
+ mutex_exit(&vdp->vdd_str->vns_dq_read.vdq_lock);
+ mutex_exit(&vdp->vdd_lock);
+ ret = 0;
+
+err:
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_getrxbuf(vnd_dev_t *vdp, intptr_t arg, int cpflag)
+{
+ int ret;
+ vnd_ioc_buf_t vib;
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_NOTATTACHED;
+ ret = EIO;
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_str->vns_dq_read.vdq_lock);
+ vib.vib_size = vdp->vdd_str->vns_dq_read.vdq_max;
+ mutex_exit(&vdp->vdd_str->vns_dq_read.vdq_lock);
+ mutex_exit(&vdp->vdd_lock);
+ ret = 0;
+
+err:
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ return (ret);
+}
+
+/* ARGSUSED */
+static int
+vnd_ioctl_getmaxbuf(vnd_dev_t *vdp, intptr_t arg, int cpflag)
+{
+ vnd_ioc_buf_t vib;
+
+ mutex_enter(&vnd_dev_lock);
+ vib.vib_size = vnd_vdq_hard_max;
+ mutex_exit(&vnd_dev_lock);
+
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+vnd_ioctl_gettxbuf(vnd_dev_t *vdp, intptr_t arg, int cpflag)
+{
+ int ret;
+ vnd_ioc_buf_t vib;
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_NOTATTACHED;
+ ret = EIO;
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_str->vns_dq_write.vdq_lock);
+ vib.vib_size = vdp->vdd_str->vns_dq_write.vdq_max;
+ mutex_exit(&vdp->vdd_str->vns_dq_write.vdq_lock);
+ mutex_exit(&vdp->vdd_lock);
+ ret = 0;
+
+err:
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_settxbuf(vnd_dev_t *vdp, intptr_t arg, int cpflag)
+{
+ int ret;
+ vnd_ioc_buf_t vib;
+
+ if (ddi_copyin((void *)arg, &vib, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ mutex_enter(&vnd_dev_lock);
+ if (vib.vib_size > vnd_vdq_hard_max) {
+ mutex_exit(&vnd_dev_lock);
+ vib.vib_errno = VND_E_BUFTOOBIG;
+ ret = EIO;
+ goto err;
+ }
+ mutex_exit(&vnd_dev_lock);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_NOTATTACHED;
+ ret = EIO;
+ goto err;
+ }
+
+ mutex_enter(&vdp->vdd_str->vns_lock);
+ if (vib.vib_size < vdp->vdd_str->vns_minwrite) {
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ mutex_exit(&vdp->vdd_lock);
+ vib.vib_errno = VND_E_BUFTOOSMALL;
+ ret = EIO;
+ goto err;
+ }
+ mutex_exit(&vdp->vdd_str->vns_lock);
+
+ mutex_enter(&vdp->vdd_str->vns_dq_write.vdq_lock);
+ vdp->vdd_str->vns_dq_write.vdq_max = (size_t)vib.vib_size;
+ mutex_exit(&vdp->vdd_str->vns_dq_write.vdq_lock);
+ mutex_exit(&vdp->vdd_lock);
+ ret = 0;
+
+err:
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), cpflag) != 0)
+ return (EFAULT);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_gettu(vnd_dev_t *vdp, intptr_t arg, int mode, boolean_t min)
+{
+ vnd_ioc_buf_t vib;
+
+ vib.vib_errno = 0;
+ mutex_enter(&vdp->vdd_lock);
+ if (vdp->vdd_flags & VND_D_ATTACHED) {
+ mutex_enter(&vdp->vdd_str->vns_lock);
+ if (min == B_TRUE)
+ vib.vib_size = vdp->vdd_str->vns_minwrite;
+ else
+ vib.vib_size = vdp->vdd_str->vns_maxwrite;
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ } else {
+ vib.vib_errno = VND_E_NOTATTACHED;
+ }
+ mutex_exit(&vdp->vdd_lock);
+
+ if (ddi_copyout(&vib, (void *)arg, sizeof (vib), mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+static int
+vnd_frameio_read(vnd_dev_t *vdp, intptr_t addr, int mode)
+{
+ int ret, nonblock, nwrite;
+ frameio_t *fio;
+ vnd_data_queue_t *vqp;
+ mblk_t *mp;
+
+ fio = frameio_alloc(KM_NOSLEEP_LAZY);
+ if (fio == NULL)
+ return (EAGAIN);
+
+ ret = frameio_hdr_copyin(fio, FRAMEIO_NVECS_MAX, (const void *)addr,
+ mode);
+ if (ret != 0) {
+ frameio_free(fio);
+ return (ret);
+ }
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ frameio_free(fio);
+ return (ENXIO);
+ }
+ mutex_exit(&vdp->vdd_lock);
+
+ nonblock = mode & (FNONBLOCK | FNDELAY);
+
+ vqp = &vdp->vdd_str->vns_dq_read;
+ mutex_enter(&vqp->vdq_lock);
+
+ /* Check empty case */
+ if (vqp->vdq_cur == 0) {
+ if (nonblock != 0) {
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+ return (EWOULDBLOCK);
+ }
+ while (vqp->vdq_cur == 0) {
+ if (cv_wait_sig(&vqp->vdq_ready, &vqp->vdq_lock) <= 0) {
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+ return (EINTR);
+ }
+ }
+ }
+
+ ret = frameio_mblk_chain_write(fio, MAP_BLK_FRAME, vqp->vdq_head,
+ &nwrite, mode & FKIOCTL);
+ if (ret != 0) {
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+ return (ret);
+ }
+
+ ret = frameio_hdr_copyout(fio, nwrite, (void *)addr, mode);
+ if (ret != 0) {
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+ return (ret);
+ }
+
+ while (nwrite > 0) {
+ (void) vnd_dq_pop(vqp, &mp);
+ freemsg(mp);
+ nwrite--;
+ }
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+
+ return (0);
+}
+
+static int
+vnd_frameio_write(vnd_dev_t *vdp, intptr_t addr, int mode)
+{
+ frameio_t *fio;
+ int ret, nonblock, nframes, i, nread;
+ size_t maxwrite, minwrite, total, flen;
+ mblk_t *mp_chain, *mp, *nmp;
+ vnd_data_queue_t *vqp;
+
+ fio = frameio_alloc(KM_NOSLEEP_LAZY);
+ if (fio == NULL)
+ return (EAGAIN);
+
+ ret = frameio_hdr_copyin(fio, FRAMEIO_NVECS_MAX, (void *)addr, mode);
+ if (ret != 0) {
+ frameio_free(fio);
+ return (ret);
+ }
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ frameio_free(fio);
+ return (ENXIO);
+ }
+ mutex_exit(&vdp->vdd_lock);
+
+ nonblock = mode & (FNONBLOCK | FNDELAY);
+
+ /*
+ * Make sure no single frame is larger than we can accept.
+ */
+ mutex_enter(&vdp->vdd_str->vns_lock);
+ minwrite = vdp->vdd_str->vns_minwrite;
+ maxwrite = vdp->vdd_str->vns_maxwrite;
+ mutex_exit(&vdp->vdd_str->vns_lock);
+
+ nframes = fio->fio_nvpf / fio->fio_nvecs;
+ total = 0;
+ for (i = 0; i < nframes; i++) {
+ flen = frameio_frame_length(fio,
+ &fio->fio_vecs[i*fio->fio_nvpf]);
+ if (flen < minwrite || flen > maxwrite) {
+ frameio_free(fio);
+ return (ERANGE);
+ }
+ total += flen;
+ }
+
+ vqp = &vdp->vdd_str->vns_dq_write;
+ mutex_enter(&vqp->vdq_lock);
+ while (vnd_dq_reserve(vqp, total) == 0) {
+ if (nonblock != 0) {
+ frameio_free(fio);
+ mutex_exit(&vqp->vdq_lock);
+ return (EAGAIN);
+ }
+ if (cv_wait_sig(&vqp->vdq_ready, &vqp->vdq_lock) <= 0) {
+ mutex_exit(&vqp->vdq_lock);
+ frameio_free(fio);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&vqp->vdq_lock);
+
+ /*
+ * We've reserved our space, let's copyin and go from here.
+ */
+ ret = frameio_mblk_chain_read(fio, &mp_chain, &nread, mode & FKIOCTL);
+ if (ret != 0) {
+ frameio_free(fio);
+ vnd_dq_unreserve(vqp, total);
+ cv_broadcast(&vqp->vdq_ready);
+ pollwakeup(&vdp->vdd_ph, POLLOUT);
+ return (ret);
+ }
+
+ for (mp = mp_chain; mp != NULL; mp = nmp) {
+ nmp = mp->b_next;
+ mp->b_next = NULL;
+ gsqueue_enter_one(vdp->vdd_str->vns_squeue, mp,
+ vnd_squeue_tx_append, vdp->vdd_str, GSQUEUE_PROCESS,
+ VND_SQUEUE_TAG_VND_WRITE);
+ }
+
+ /*
+ * Update the frameio structure to indicate that we wrote those frames.
+ */
+ frameio_mark_consumed(fio, nread);
+ ret = frameio_hdr_copyout(fio, nread, (void *)addr, mode);
+ frameio_free(fio);
+
+ return (ret);
+}
+
+static int
+vnd_ioctl_list_copy_info(vnd_dev_t *vdp, vnd_ioc_info_t *arg, int mode)
+{
+ const char *link;
+ uint32_t vers = 1;
+ ASSERT(MUTEX_HELD(&vdp->vdd_lock));
+
+ /*
+ * Copy all of the members out to userland.
+ */
+ if (ddi_copyout(&vers, &arg->vii_version, sizeof (uint32_t),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ if (vdp->vdd_flags & VND_D_LINKED)
+ link = vdp->vdd_lname;
+ else
+ link = "<anonymous>";
+ if (ddi_copyout(link, arg->vii_name, sizeof (arg->vii_name),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ if (ddi_copyout(vdp->vdd_datalink, arg->vii_datalink,
+ sizeof (arg->vii_datalink), mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ if (ddi_copyout(&vdp->vdd_nsd->vpnd_zid, &arg->vii_zone,
+ sizeof (zoneid_t), mode & FKIOCTL) != 0)
+ return (EFAULT);
+ return (0);
+}
+
+static int
+vnd_ioctl_list(intptr_t arg, cred_t *credp, int mode)
+{
+ vnd_ioc_list_t vl;
+ vnd_ioc_list32_t vl32;
+ zoneid_t zid;
+ vnd_dev_t *vdp;
+ vnd_ioc_info_t *vip;
+ int found, cancopy, ret;
+
+ if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
+ if (ddi_copyin((void *)arg, &vl32, sizeof (vnd_ioc_list32_t),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+ vl.vl_nents = vl32.vl_nents;
+ vl.vl_actents = vl32.vl_actents;
+ vl.vl_ents = (void *)(uintptr_t)vl32.vl_ents;
+ } else {
+ if (ddi_copyin((void *)arg, &vl, sizeof (vnd_ioc_list_t),
+ mode & FKIOCTL) != 0)
+ return (EFAULT);
+ }
+
+ cancopy = vl.vl_nents;
+ vip = vl.vl_ents;
+ found = 0;
+ zid = crgetzoneid(credp);
+ mutex_enter(&vnd_dev_lock);
+ for (vdp = list_head(&vnd_dev_list); vdp != NULL;
+ vdp = list_next(&vnd_dev_list, vdp)) {
+ mutex_enter(&vdp->vdd_lock);
+ if (vdp->vdd_flags & VND_D_ATTACHED &&
+ !(vdp->vdd_flags & (VND_D_CONDEMNED | VND_D_ZONE_DYING)) &&
+ (zid == GLOBAL_ZONEID || zid == vdp->vdd_nsd->vpnd_zid)) {
+ found++;
+ if (cancopy > 0) {
+ ret = vnd_ioctl_list_copy_info(vdp, vip, mode);
+ if (ret != 0) {
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&vnd_dev_lock);
+ return (ret);
+ }
+ cancopy--;
+ vip++;
+ }
+ }
+ mutex_exit(&vdp->vdd_lock);
+ }
+ mutex_exit(&vnd_dev_lock);
+
+ if (ddi_copyout(&found, &((vnd_ioc_list_t *)arg)->vl_actents,
+ sizeof (uint_t), mode & FKIOCTL) != 0)
+ return (EFAULT);
+
+ return (0);
+}
+
+
+/* ARGSUSED */
+static int
+vnd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ int ret;
+ minor_t m;
+ vnd_dev_t *vdp;
+
+ m = getminor(dev);
+ ASSERT(m != 0);
+
+ /*
+ * Make sure no one has come in on an ioctl from the strioc case.
+ */
+ if ((cmd & VND_STRIOC) == VND_STRIOC)
+ return (ENOTTY);
+
+ /*
+ * Like close, seems like if this minor isn't found, it's a programmer
+ * error somehow.
+ */
+ vdp = vnd_dev_lookup(m);
+ if (vdp == NULL)
+ return (ENXIO);
+
+ switch (cmd) {
+ case VND_IOC_ATTACH:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_attach(vdp, arg, credp, mode);
+ break;
+ case VND_IOC_LINK:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_link(vdp, arg, credp, mode);
+ break;
+ case VND_IOC_UNLINK:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_unlink(vdp, arg, credp, mode);
+ break;
+ case VND_IOC_GETRXBUF:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_getrxbuf(vdp, arg, mode);
+ break;
+ case VND_IOC_SETRXBUF:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_setrxbuf(vdp, arg, mode);
+ break;
+ case VND_IOC_GETTXBUF:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_gettxbuf(vdp, arg, mode);
+ break;
+ case VND_IOC_SETTXBUF:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_settxbuf(vdp, arg, mode);
+ break;
+ case VND_IOC_GETMAXBUF:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ if (crgetzoneid(credp) != GLOBAL_ZONEID) {
+ ret = EPERM;
+ break;
+ }
+ ret = vnd_ioctl_getmaxbuf(vdp, arg, mode);
+ break;
+ case VND_IOC_GETMINTU:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_gettu(vdp, arg, mode, B_TRUE);
+ break;
+ case VND_IOC_GETMAXTU:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_gettu(vdp, arg, mode, B_FALSE);
+ break;
+ case VND_IOC_FRAMEIO_READ:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_frameio_read(vdp, arg, mode);
+ break;
+ case VND_IOC_FRAMEIO_WRITE:
+ if (!(mode & FWRITE)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_frameio_write(vdp, arg, mode);
+ break;
+ case VND_IOC_LIST:
+ if (!(mode & FREAD)) {
+ ret = EBADF;
+ break;
+ }
+ ret = vnd_ioctl_list(arg, credp, mode);
+ break;
+ default:
+ ret = ENOTTY;
+ break;
+ }
+
+ vnd_dev_rele(vdp);
+ return (ret);
+}
+
+static int
+vnd_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+ vnd_dev_t *vdp;
+ minor_t m;
+ zoneid_t zid;
+
+ if (flag & (FEXCL | FNDELAY))
+ return (ENOTSUP);
+
+ if (otyp & OTYP_BLK)
+ return (ENOTSUP);
+
+ zid = crgetzoneid(credp);
+ m = getminor(*devp);
+
+ /*
+ * If we have an open of a non-zero instance then we need to look that
+ * up in our list of entries.
+ */
+ if (m != 0) {
+
+ /*
+ * We don't check for rawaccess globally as a user could be
+ * doing a list ioctl on the control node which doesn't require
+ * this privilege.
+ */
+ if (secpolicy_net_rawaccess(credp) != 0)
+ return (EPERM);
+
+
+ vdp = vnd_dev_lookup(m);
+ if (vdp == NULL)
+ return (ENOENT);
+
+ /*
+ * We need to check to make sure that the user is allowed to
+ * open this node. At this point it should be an attached handle
+ * as that's all we're allowed to access.
+ */
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_LINKED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENOENT);
+ }
+
+ if (vdp->vdd_flags & VND_D_ZONE_DYING) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENOENT);
+ }
+
+ if (zid != GLOBAL_ZONEID && zid != vdp->vdd_nsd->vpnd_zid) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENOENT);
+ }
+
+ if ((flag & FEXCL) && (vdp->vdd_flags & VND_D_OPENED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (EBUSY);
+ }
+
+ if (!(vdp->vdd_flags & VND_D_OPENED)) {
+ vdp->vdd_flags |= VND_D_OPENED;
+ vdp->vdd_ref++;
+ DTRACE_VND_REFINC(vdp);
+ }
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+
+ return (0);
+ }
+
+ if (flag & FEXCL)
+ return (ENOTSUP);
+
+ /*
+ * We need to clone ourselves and set up new a state.
+ */
+ vdp = kmem_cache_alloc(vnd_dev_cache, KM_SLEEP);
+ bzero(vdp, sizeof (vnd_dev_t));
+
+ if (ldi_ident_from_dev(*devp, &vdp->vdd_ldiid) != 0) {
+ kmem_cache_free(vnd_dev_cache, vdp);
+ return (EINVAL);
+ }
+
+ vdp->vdd_minor = id_alloc(vnd_minors);
+ mutex_init(&vdp->vdd_lock, NULL, MUTEX_DRIVER, NULL);
+ list_link_init(&vdp->vdd_link);
+ vdp->vdd_ref = 1;
+ *devp = makedevice(getmajor(*devp), vdp->vdd_minor);
+ vdp->vdd_devid = *devp;
+ DTRACE_VND_REFINC(vdp);
+ vdp->vdd_flags |= VND_D_OPENED;
+
+ mutex_enter(&vnd_dev_lock);
+ list_insert_head(&vnd_dev_list, vdp);
+ mutex_exit(&vnd_dev_lock);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+vnd_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+ minor_t m;
+ vnd_dev_t *vdp;
+
+ m = getminor(dev);
+ if (m == 0)
+ return (ENXIO);
+
+ vdp = vnd_dev_lookup(m);
+ if (vdp == NULL)
+ return (ENXIO);
+
+ mutex_enter(&vdp->vdd_lock);
+ VERIFY(vdp->vdd_flags & VND_D_OPENED);
+ vdp->vdd_flags &= ~VND_D_OPENED;
+ mutex_exit(&vdp->vdd_lock);
+
+ /* Remove the hold from the previous open. */
+ vnd_dev_rele(vdp);
+
+ /* And now from lookup */
+ vnd_dev_rele(vdp);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+vnd_read(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ int nonblock, error = 0;
+ size_t mpsize;
+ vnd_dev_t *vdp;
+ vnd_data_queue_t *vqp;
+ mblk_t *mp = NULL;
+ offset_t u_loffset;
+
+ /*
+ * If we have more than one uio we refuse to do anything. That's for
+ * frameio.
+ */
+ if (uiop->uio_iovcnt > 1)
+ return (EINVAL);
+
+ vdp = vnd_dev_lookup(getminor(dev));
+ if (vdp == NULL)
+ return (ENXIO);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENXIO);
+ }
+ mutex_exit(&vdp->vdd_lock);
+ nonblock = uiop->uio_fmode & (FNONBLOCK | FNDELAY);
+
+ vqp = &vdp->vdd_str->vns_dq_read;
+ mutex_enter(&vqp->vdq_lock);
+
+ /* Check empty case */
+ if (vqp->vdq_cur == 0) {
+ if (nonblock != 0) {
+ error = EWOULDBLOCK;
+ goto err;
+ }
+ while (vqp->vdq_cur == 0) {
+ if (cv_wait_sig(&vqp->vdq_ready, &vqp->vdq_lock) <= 0) {
+ error = EINTR;
+ goto err;
+ }
+ }
+ }
+
+ /* Ensure our buffer is big enough */
+ mp = vqp->vdq_head;
+ ASSERT(mp != NULL);
+ mpsize = msgsize(mp);
+ if (mpsize > uiop->uio_resid) {
+ error = EOVERFLOW;
+ goto err;
+ }
+
+ u_loffset = uiop->uio_loffset;
+ while (mp != NULL) {
+ if (uiomove(mp->b_rptr, MBLKL(mp), UIO_READ, uiop) != 0) {
+ error = EFAULT;
+ uiop->uio_loffset = u_loffset;
+ mp = NULL;
+ goto err;
+ }
+ mpsize -= MBLKL(mp);
+ mp = mp->b_cont;
+ }
+ ASSERT(mpsize == 0);
+ (void) vnd_dq_pop(vqp, &mp);
+ freemsg(mp);
+err:
+ mutex_exit(&vqp->vdq_lock);
+ vnd_dev_rele(vdp);
+
+ return (error);
+}
+
+/* ARGSUSED */
+static int
+vnd_write(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+ int nonblock, error;
+ vnd_dev_t *vdp;
+ mblk_t *mp;
+ ssize_t iosize, origsize;
+ vnd_data_queue_t *vqp;
+
+ if (uiop->uio_iovcnt > 1)
+ return (EINVAL);
+
+ vdp = vnd_dev_lookup(getminor(dev));
+ if (vdp == NULL)
+ return (ENXIO);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENXIO);
+ }
+ mutex_exit(&vdp->vdd_lock);
+ nonblock = uiop->uio_fmode & (FNONBLOCK | FNDELAY);
+
+ mutex_enter(&vdp->vdd_str->vns_lock);
+ if (uiop->uio_resid > vdp->vdd_str->vns_maxwrite ||
+ uiop->uio_resid < vdp->vdd_str->vns_minwrite) {
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ vnd_dev_rele(vdp);
+ return (ERANGE);
+ }
+ mutex_exit(&vdp->vdd_str->vns_lock);
+ VERIFY(vdp->vdd_str != NULL);
+
+ /*
+ * Reserve space in the data queue if we can. If we can't, block or
+ * return EAGAIN. If we can, go and squeue_enter.
+ */
+ vqp = &vdp->vdd_str->vns_dq_write;
+ mutex_enter(&vqp->vdq_lock);
+ while (vnd_dq_reserve(vqp, uiop->uio_resid) == 0) {
+ if (nonblock != 0) {
+ mutex_exit(&vqp->vdq_lock);
+ vnd_dev_rele(vdp);
+ return (EAGAIN);
+ }
+ if (cv_wait_sig(&vqp->vdq_ready, &vqp->vdq_lock) <= 0) {
+ mutex_exit(&vqp->vdq_lock);
+ vnd_dev_rele(vdp);
+ return (EINTR);
+ }
+ }
+ mutex_exit(&vqp->vdq_lock);
+
+ /*
+ * Now that we've reserved the space, try to allocate kernel space for
+ * and copy in the block. To take care of all this we use the
+ * strmakedata subroutine for now.
+ */
+ origsize = iosize = uiop->uio_resid;
+ error = strmakedata(&iosize, uiop, vdp->vdd_str->vns_wq->q_stream, 0,
+ &mp);
+
+ /*
+ * strmakedata() will return an error or it may only consume a portion
+ * of the data.
+ */
+ if (error != 0 || uiop->uio_resid != 0) {
+ vnd_dq_unreserve(vqp, origsize);
+ cv_broadcast(&vqp->vdq_ready);
+ pollwakeup(&vdp->vdd_ph, POLLOUT);
+ vnd_dev_rele(vdp);
+ return (ENOSR);
+ }
+
+ gsqueue_enter_one(vdp->vdd_str->vns_squeue, mp,
+ vnd_squeue_tx_append, vdp->vdd_str, GSQUEUE_PROCESS,
+ VND_SQUEUE_TAG_VND_WRITE);
+
+ vnd_dev_rele(vdp);
+ return (0);
+}
+
+static int
+vnd_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
+ struct pollhead **phpp)
+{
+ short ready = 0;
+ vnd_dev_t *vdp;
+ vnd_data_queue_t *vqp;
+
+ vdp = vnd_dev_lookup(getminor(dev));
+ if (vdp == NULL)
+ return (ENXIO);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_ATTACHED)) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (ENXIO);
+ }
+ mutex_exit(&vdp->vdd_lock);
+
+ if ((events & POLLIN) || (events & POLLRDNORM)) {
+ vqp = &vdp->vdd_str->vns_dq_read;
+ mutex_enter(&vqp->vdq_lock);
+ if (vqp->vdq_head != NULL)
+ ready |= events & (POLLIN | POLLRDNORM);
+ mutex_exit(&vqp->vdq_lock);
+ }
+
+ if (events & POLLOUT) {
+ vqp = &vdp->vdd_str->vns_dq_write;
+ mutex_enter(&vqp->vdq_lock);
+ if (vqp->vdq_cur != vqp->vdq_max)
+ ready |= POLLOUT;
+ mutex_exit(&vqp->vdq_lock);
+ }
+
+ if ((ready == 0 && !anyyet) || (events & POLLET)) {
+ *phpp = &vdp->vdd_ph;
+ }
+ *reventsp = ready;
+ vnd_dev_rele(vdp);
+ return (0);
+}
+
+/* ARGSUSED */
+static void *
+vnd_stack_init(netstackid_t stackid, netstack_t *ns)
+{
+ vnd_pnsd_t *nsp;
+
+ nsp = kmem_cache_alloc(vnd_pnsd_cache, KM_SLEEP);
+ bzero(nsp, sizeof (*nsp));
+ nsp->vpnd_nsid = stackid;
+ nsp->vpnd_zid = netstackid_to_zoneid(stackid);
+ nsp->vpnd_flags = 0;
+ mutex_init(&nsp->vpnd_lock, NULL, MUTEX_DRIVER, NULL);
+ list_create(&nsp->vpnd_dev_list, sizeof (vnd_dev_t),
+ offsetof(vnd_dev_t, vdd_nslink));
+ if (vnd_netinfo_init(nsp) == 0)
+ nsp->vpnd_hooked = B_TRUE;
+
+ mutex_enter(&vnd_dev_lock);
+ list_insert_tail(&vnd_nsd_list, nsp);
+ mutex_exit(&vnd_dev_lock);
+
+ return (nsp);
+}
+
+/* ARGSUSED */
+static void
+vnd_stack_shutdown(netstackid_t stackid, void *arg)
+{
+ vnd_pnsd_t *nsp = arg;
+ vnd_dev_t *vdp;
+
+ ASSERT(nsp != NULL);
+ /*
+ * After shut down no one should be able to find their way to this
+ * netstack again.
+ */
+ mutex_enter(&vnd_dev_lock);
+ list_remove(&vnd_nsd_list, nsp);
+ mutex_exit(&vnd_dev_lock);
+
+ /*
+ * Make sure hooks know that they're going away.
+ */
+ if (nsp->vpnd_hooked == B_TRUE)
+ vnd_netinfo_shutdown(nsp);
+
+ /*
+ * Now we need to go through and notify each zone that they are in
+ * teardown phase. See the big theory statement section on vnd, zones,
+ * netstacks, and sdev for more information about this.
+ */
+ mutex_enter(&nsp->vpnd_lock);
+ nsp->vpnd_flags |= VND_NS_CONDEMNED;
+ for (vdp = list_head(&nsp->vpnd_dev_list); vdp != NULL;
+ vdp = list_next(&nsp->vpnd_dev_list, vdp)) {
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_CONDEMNED))
+ vdp->vdd_flags |= VND_D_ZONE_DYING;
+ mutex_exit(&vdp->vdd_lock);
+ }
+ mutex_exit(&nsp->vpnd_lock);
+
+ /*
+ * Next we remove all the links as we know nothing new can be added to
+ * the list and that none of the extent devices can obtain additional
+ * links.
+ */
+restart:
+ mutex_enter(&nsp->vpnd_lock);
+ for (vdp = list_head(&nsp->vpnd_dev_list); vdp != NULL;
+ vdp = list_next(&nsp->vpnd_dev_list, vdp)) {
+ mutex_enter(&vdp->vdd_lock);
+ if ((vdp->vdd_flags & VND_D_CONDEMNED) ||
+ !(vdp->vdd_flags & VND_D_LINKED)) {
+ mutex_exit(&vdp->vdd_lock);
+ continue;
+ }
+
+ /*
+ * We drop our lock here and restart afterwards. Note that as
+ * part of unlinking we end up doing a rele of the vnd_dev_t. If
+ * this is the final hold on the vnd_dev_t then it might try and
+ * remove itself. Our locking rules requires not to be holding
+ * any locks when we call any of the rele functions.
+ *
+ * Note that the unlink function requires holders to call into
+ * it with the vnd_dev_t->vdd_lock held and will take care of it
+ * for us. Because we don't have a hold on it, we're done at
+ * this point.
+ */
+ mutex_exit(&nsp->vpnd_lock);
+ /* Forcibly unlink */
+ vnd_dev_unlink(vdp);
+ goto restart;
+ }
+ mutex_exit(&nsp->vpnd_lock);
+}
+
+/* ARGSUSED */
+static void
+vnd_stack_destroy(netstackid_t stackid, void *arg)
+{
+ vnd_pnsd_t *nsp = arg;
+
+ ASSERT(nsp != NULL);
+
+ /*
+ * Now that we've unlinked everything we just have to hang out for
+ * it to finish exiting. Now that it's no longer the kernel itself
+ * that's doing this we just need to wait for our reference count to
+ * equal zero and then we're free. If the global zone is holding open a
+ * reference to a vnd device for another zone, that's bad, but there's
+ * nothing much we can do. See the section on 'vnd, zones, netstacks' in
+ * the big theory statement for more information.
+ */
+ mutex_enter(&nsp->vpnd_lock);
+ while (nsp->vpnd_ref != 0)
+ cv_wait(&nsp->vpnd_ref_change, &nsp->vpnd_lock);
+ mutex_exit(&nsp->vpnd_lock);
+
+ /*
+ * During shutdown we removed ourselves from the list and now we have no
+ * more references so we can safely say that there is nothing left and
+ * destroy everything that we had sitting around.
+ */
+ if (nsp->vpnd_hooked == B_TRUE)
+ vnd_netinfo_fini(nsp);
+
+ mutex_destroy(&nsp->vpnd_lock);
+ list_destroy(&nsp->vpnd_dev_list);
+ kmem_cache_free(vnd_pnsd_cache, nsp);
+}
+
+/*
+ * Convert a node with a name of the form /dev/vnd/zone/%zonename and
+ * /dev/vnd/zone/%zonename/%linkname to the corresponding vnd netstack.
+ */
+static vnd_pnsd_t *
+vnd_sdev_ctx_to_ns(sdev_ctx_t ctx)
+{
+ enum vtype vt;
+ const char *path = sdev_ctx_path(ctx);
+ char *zstart, *dup;
+ size_t duplen;
+ vnd_pnsd_t *nsp;
+
+ vt = sdev_ctx_vtype(ctx);
+ ASSERT(strncmp(path, VND_SDEV_ZROOT, strlen(VND_SDEV_ZROOT)) == 0);
+
+ if (vt == VDIR) {
+ zstart = strrchr(path, '/');
+ ASSERT(zstart != NULL);
+ zstart++;
+ return (vnd_nsd_lookup_by_zonename(zstart));
+ }
+
+ ASSERT(vt == VCHR);
+
+ dup = strdup(path);
+ duplen = strlen(dup) + 1;
+ zstart = strrchr(dup, '/');
+ *zstart = '\0';
+ zstart--;
+ zstart = strrchr(dup, '/');
+ zstart++;
+ nsp = vnd_nsd_lookup_by_zonename(zstart);
+ kmem_free(dup, duplen);
+
+ return (nsp);
+}
+
+static sdev_plugin_validate_t
+vnd_sdev_validate_dir(sdev_ctx_t ctx)
+{
+ vnd_pnsd_t *nsp;
+
+ if (strcmp(sdev_ctx_path(ctx), VND_SDEV_ROOT) == 0)
+ return (SDEV_VTOR_VALID);
+
+ if (strcmp(sdev_ctx_path(ctx), VND_SDEV_ZROOT) == 0) {
+ ASSERT(getzoneid() == GLOBAL_ZONEID);
+ ASSERT(sdev_ctx_flags(ctx) & SDEV_CTX_GLOBAL);
+ return (SDEV_VTOR_VALID);
+ }
+
+ nsp = vnd_sdev_ctx_to_ns(ctx);
+ if (nsp == NULL)
+ return (SDEV_VTOR_INVALID);
+ vnd_nsd_rele(nsp);
+
+ return (SDEV_VTOR_VALID);
+}
+
+static sdev_plugin_validate_t
+vnd_sdev_validate(sdev_ctx_t ctx)
+{
+ enum vtype vt;
+ vnd_dev_t *vdp;
+ minor_t minor;
+
+ vt = sdev_ctx_vtype(ctx);
+ if (vt == VDIR)
+ return (vnd_sdev_validate_dir(ctx));
+ ASSERT(vt == VCHR);
+
+ if (strcmp("ctl", sdev_ctx_name(ctx)) == 0)
+ return (SDEV_VTOR_VALID);
+
+ if (sdev_ctx_minor(ctx, &minor) != 0)
+ return (SDEV_VTOR_STALE);
+
+ vdp = vnd_dev_lookup(minor);
+ if (vdp == NULL)
+ return (SDEV_VTOR_STALE);
+
+ mutex_enter(&vdp->vdd_lock);
+ if (!(vdp->vdd_flags & VND_D_LINKED) ||
+ (vdp->vdd_flags & (VND_D_CONDEMNED | VND_D_ZONE_DYING))) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (SDEV_VTOR_STALE);
+ }
+
+ if (strcmp(sdev_ctx_name(ctx), vdp->vdd_lname) != 0) {
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (SDEV_VTOR_STALE);
+ }
+
+ mutex_exit(&vdp->vdd_lock);
+ vnd_dev_rele(vdp);
+ return (SDEV_VTOR_VALID);
+}
+
+/*
+ * This function is a no-op. sdev never has holds on our devices as they can go
+ * away at any time and specfs has to deal with that fact.
+ */
+/* ARGSUSED */
+static void
+vnd_sdev_inactive(sdev_ctx_t ctx)
+{
+}
+
+static int
+vnd_sdev_fillzone(vnd_pnsd_t *nsp, sdev_ctx_t ctx)
+{
+ int ret;
+ vnd_dev_t *vdp;
+
+ mutex_enter(&nsp->vpnd_lock);
+ for (vdp = list_head(&nsp->vpnd_dev_list); vdp != NULL;
+ vdp = list_next(&nsp->vpnd_dev_list, vdp)) {
+ mutex_enter(&vdp->vdd_lock);
+ if ((vdp->vdd_flags & VND_D_LINKED) &&
+ !(vdp->vdd_flags & (VND_D_CONDEMNED | VND_D_ZONE_DYING))) {
+ ret = sdev_plugin_mknod(ctx, vdp->vdd_lname,
+ VND_SDEV_MODE, vdp->vdd_devid);
+ if (ret != 0 && ret != EEXIST) {
+ mutex_exit(&vdp->vdd_lock);
+ mutex_exit(&nsp->vpnd_lock);
+ vnd_nsd_rele(nsp);
+ return (ret);
+ }
+ }
+ mutex_exit(&vdp->vdd_lock);
+ }
+ mutex_exit(&nsp->vpnd_lock);
+
+ return (0);
+}
+
+static int
+vnd_sdev_filldir_root(sdev_ctx_t ctx)
+{
+ zoneid_t zid;
+ vnd_pnsd_t *nsp;
+ int ret;
+
+ zid = getzoneid();
+ nsp = vnd_nsd_lookup(zoneid_to_netstackid(zid));
+ ASSERT(nsp != NULL);
+ ret = vnd_sdev_fillzone(nsp, ctx);
+ vnd_nsd_rele(nsp);
+ if (ret != 0)
+ return (ret);
+
+ /*
+ * Checking the zone id is not sufficient as the global zone could be
+ * reaching down into a non-global zone's mounted /dev.
+ */
+ if (zid == GLOBAL_ZONEID && (sdev_ctx_flags(ctx) & SDEV_CTX_GLOBAL)) {
+ ret = sdev_plugin_mkdir(ctx, "zone");
+ if (ret != 0 && ret != EEXIST)
+ return (ret);
+ }
+
+ /*
+ * Always add a reference to the control node. There's no need to
+ * reference it since it always exists and is always what we clone from.
+ */
+ ret = sdev_plugin_mknod(ctx, "ctl", VND_SDEV_MODE,
+ makedevice(ddi_driver_major(vnd_dip), 0));
+ if (ret != 0 && ret != EEXIST)
+ return (ret);
+
+ return (0);
+}
+
+static int
+vnd_sdev_filldir_zroot(sdev_ctx_t ctx)
+{
+ int ret;
+ vnd_pnsd_t *nsp;
+ zone_t *zonep;
+
+ ASSERT(getzoneid() == GLOBAL_ZONEID);
+ ASSERT(sdev_ctx_flags(ctx) & SDEV_CTX_GLOBAL);
+
+ mutex_enter(&vnd_dev_lock);
+ for (nsp = list_head(&vnd_nsd_list); nsp != NULL;
+ nsp = list_next(&vnd_nsd_list, nsp)) {
+ mutex_enter(&nsp->vpnd_lock);
+ if (list_is_empty(&nsp->vpnd_dev_list)) {
+ mutex_exit(&nsp->vpnd_lock);
+ continue;
+ }
+ mutex_exit(&nsp->vpnd_lock);
+ zonep = zone_find_by_id(nsp->vpnd_zid);
+ /*
+ * This zone must be being torn down, so skip it.
+ */
+ if (zonep == NULL)
+ continue;
+ ret = sdev_plugin_mkdir(ctx, zonep->zone_name);
+ zone_rele(zonep);
+ if (ret != 0 && ret != EEXIST) {
+ mutex_exit(&vnd_dev_lock);
+ return (ret);
+ }
+ }
+ mutex_exit(&vnd_dev_lock);
+ return (0);
+}
+
+static int
+vnd_sdev_filldir(sdev_ctx_t ctx)
+{
+ int ret;
+ vnd_pnsd_t *nsp;
+
+ ASSERT(sdev_ctx_vtype(ctx) == VDIR);
+ if (strcmp(VND_SDEV_ROOT, sdev_ctx_path(ctx)) == 0)
+ return (vnd_sdev_filldir_root(ctx));
+
+ if (strcmp(VND_SDEV_ZROOT, sdev_ctx_path(ctx)) == 0)
+ return (vnd_sdev_filldir_zroot(ctx));
+
+ ASSERT(strncmp(VND_SDEV_ZROOT, sdev_ctx_path(ctx),
+ strlen(VND_SDEV_ZROOT)) == 0);
+ nsp = vnd_sdev_ctx_to_ns(ctx);
+ if (nsp == NULL)
+ return (0);
+
+ ret = vnd_sdev_fillzone(nsp, ctx);
+ vnd_nsd_rele(nsp);
+
+ return (ret);
+}
+
+static sdev_plugin_ops_t vnd_sdev_ops = {
+ SDEV_PLUGIN_VERSION,
+ SDEV_PLUGIN_SUBDIR,
+ vnd_sdev_validate,
+ vnd_sdev_filldir,
+ vnd_sdev_inactive
+};
+
+static int
+vnd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int errp = 0;
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ /*
+ * Only allow one instance.
+ */
+ if (vnd_dip != NULL)
+ return (DDI_FAILURE);
+
+ vnd_dip = dip;
+ if (ddi_create_minor_node(vnd_dip, "vnd", S_IFCHR, 0, DDI_PSEUDO, 0) !=
+ DDI_SUCCESS) {
+ vnd_dip = NULL;
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
+ DDI_KERNEL_IOCTL, NULL, 0) != DDI_PROP_SUCCESS) {
+ ddi_remove_minor_node(vnd_dip, NULL);
+ vnd_dip = NULL;
+ return (DDI_FAILURE);
+ }
+
+ vnd_sdev_hdl = sdev_plugin_register(VND_SDEV_NAME, &vnd_sdev_ops,
+ &errp);
+ if (vnd_sdev_hdl == (sdev_plugin_hdl_t)NULL) {
+ ddi_remove_minor_node(vnd_dip, NULL);
+ ddi_prop_remove_all(vnd_dip);
+ vnd_dip = NULL;
+ return (DDI_FAILURE);
+ }
+
+ vnd_sqset = gsqueue_set_create(GSQUEUE_DEFAULT_PRIORITY);
+
+ return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+static int
+vnd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ mutex_enter(&vnd_dev_lock);
+ if (!list_is_empty(&vnd_dev_list)) {
+ mutex_exit(&vnd_dev_lock);
+ return (DDI_FAILURE);
+ }
+ mutex_exit(&vnd_dev_lock);
+
+ return (DDI_FAILURE);
+}
+
+/* ARGSUSED */
+static int
+vnd_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ int error;
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ *result = (void *)vnd_dip;
+ error = DDI_SUCCESS;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)0;
+ error = DDI_SUCCESS;
+ break;
+ default:
+ error = DDI_FAILURE;
+ break;
+ }
+ return (error);
+}
+
+
+
+static void
+vnd_ddi_fini(void)
+{
+ netstack_unregister(NS_VND);
+ if (vnd_taskq != NULL)
+ taskq_destroy(vnd_taskq);
+ if (vnd_str_cache != NULL)
+ kmem_cache_destroy(vnd_str_cache);
+ if (vnd_dev_cache != NULL)
+ kmem_cache_destroy(vnd_dev_cache);
+ if (vnd_pnsd_cache != NULL)
+ kmem_cache_destroy(vnd_pnsd_cache);
+ if (vnd_minors != NULL)
+ id_space_destroy(vnd_minors);
+ if (vnd_list_init != 0) {
+ list_destroy(&vnd_nsd_list);
+ list_destroy(&vnd_dev_list);
+ mutex_destroy(&vnd_dev_lock);
+ vnd_list_init = 0;
+ }
+ frameio_fini();
+}
+
+static int
+vnd_ddi_init(void)
+{
+ if (frameio_init() != 0)
+ return (DDI_FAILURE);
+
+ vnd_str_cache = kmem_cache_create("vnd_str_cache", sizeof (vnd_str_t),
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ if (vnd_str_cache == NULL) {
+ frameio_fini();
+ return (DDI_FAILURE);
+ }
+ vnd_dev_cache = kmem_cache_create("vnd_dev_cache", sizeof (vnd_dev_t),
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ if (vnd_dev_cache == NULL) {
+ kmem_cache_destroy(vnd_str_cache);
+ frameio_fini();
+ return (DDI_FAILURE);
+ }
+ vnd_pnsd_cache = kmem_cache_create("vnd_pnsd_cache",
+ sizeof (vnd_pnsd_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
+ if (vnd_pnsd_cache == NULL) {
+ kmem_cache_destroy(vnd_dev_cache);
+ kmem_cache_destroy(vnd_str_cache);
+ frameio_fini();
+ return (DDI_FAILURE);
+ }
+
+ vnd_taskq = taskq_create_instance("vnd", -1, 1, minclsyspri, 0, 0, 0);
+ if (vnd_taskq == NULL) {
+ kmem_cache_destroy(vnd_pnsd_cache);
+ kmem_cache_destroy(vnd_dev_cache);
+ kmem_cache_destroy(vnd_str_cache);
+ frameio_fini();
+ return (DDI_FAILURE);
+ }
+
+ vnd_minors = id_space_create("vnd_minors", 1, INT32_MAX);
+ if (vnd_minors == NULL) {
+ taskq_destroy(vnd_taskq);
+ kmem_cache_destroy(vnd_pnsd_cache);
+ kmem_cache_destroy(vnd_dev_cache);
+ kmem_cache_destroy(vnd_str_cache);
+ frameio_fini();
+ return (DDI_FAILURE);
+ }
+
+ mutex_init(&vnd_dev_lock, NULL, MUTEX_DRIVER, NULL);
+ list_create(&vnd_dev_list, sizeof (vnd_dev_t),
+ offsetof(vnd_dev_t, vdd_link));
+ list_create(&vnd_nsd_list, sizeof (vnd_pnsd_t),
+ offsetof(vnd_pnsd_t, vpnd_link));
+ vnd_list_init = 1;
+
+ netstack_register(NS_VND, vnd_stack_init, vnd_stack_shutdown,
+ vnd_stack_destroy);
+
+ return (DDI_SUCCESS);
+}
+
+static struct module_info vnd_minfo = {
+ 0, /* module id */
+ "vnd", /* module name */
+ 1, /* smallest packet size */
+ INFPSZ, /* largest packet size (infinite) */
+ 1, /* high watermark */
+ 0 /* low watermark */
+};
+
+static struct qinit vnd_r_qinit = {
+ vnd_s_rput,
+ NULL,
+ vnd_s_open,
+ vnd_s_close,
+ NULL,
+ &vnd_minfo,
+ NULL
+};
+
+static struct qinit vnd_w_qinit = {
+ vnd_s_wput,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &vnd_minfo,
+ NULL
+};
+
+static struct streamtab vnd_strtab = {
+ &vnd_r_qinit,
+ &vnd_w_qinit,
+ NULL,
+ NULL
+};
+
+
+static struct cb_ops vnd_cb_ops = {
+ vnd_open, /* open */
+ vnd_close, /* close */
+ nulldev, /* strategy */
+ nulldev, /* print */
+ nodev, /* dump */
+ vnd_read, /* read */
+ vnd_write, /* write */
+ vnd_ioctl, /* ioctl */
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ vnd_chpoll, /* poll */
+ ddi_prop_op, /* cb_prop_op */
+ NULL, /* streamtab */
+ D_MP /* Driver compatibility flag */
+};
+
+static struct dev_ops vnd_dev_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* refcnt */
+ vnd_info, /* get_dev_info */
+ nulldev, /* identify */
+ nulldev, /* probe */
+ vnd_attach, /* attach */
+ vnd_detach, /* detach */
+ nodev, /* reset */
+ &vnd_cb_ops, /* driver operations */
+ NULL, /* bus operations */
+ nodev, /* dev power */
+ ddi_quiesce_not_needed /* quiesce */
+};
+
+static struct modldrv vnd_modldrv = {
+ &mod_driverops,
+ "Virtual Networking Datapath Driver",
+ &vnd_dev_ops
+};
+
+static struct fmodsw vnd_fmodfsw = {
+ "vnd",
+ &vnd_strtab,
+ D_NEW | D_MP
+};
+
+static struct modlstrmod vnd_modlstrmod = {
+ &mod_strmodops,
+ "Virtual Networking Datapath Driver",
+ &vnd_fmodfsw
+};
+
+static struct modlinkage vnd_modlinkage = {
+ MODREV_1,
+ &vnd_modldrv,
+ &vnd_modlstrmod,
+ NULL
+};
+
+int
+_init(void)
+{
+ int error;
+
+ /*
+ * We need to do all of our global initialization in init as opposed to
+ * attach and detach. The problem here is that because vnd can be used
+ * from a stream context while being detached, we can not rely on having
+ * run attach to create everything, alas. so it goes in _init, just like
+ * our friend ip.
+ */
+ if ((error = vnd_ddi_init()) != DDI_SUCCESS)
+ return (error);
+ error = mod_install((&vnd_modlinkage));
+ if (error != 0)
+ vnd_ddi_fini();
+ return (error);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&vnd_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ int error;
+
+ error = mod_remove(&vnd_modlinkage);
+ if (error == 0)
+ vnd_ddi_fini();
+ return (error);
+}
diff --git a/usr/src/uts/common/io/vnd/vnd.conf b/usr/src/uts/common/io/vnd/vnd.conf
new file mode 100644
index 0000000000..65872e1ddf
--- /dev/null
+++ b/usr/src/uts/common/io/vnd/vnd.conf
@@ -0,0 +1,16 @@
+#
+# 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.
+#
+
+name="vnd" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/io/zfd.c b/usr/src/uts/common/io/zfd.c
new file mode 100644
index 0000000000..46a9e435cd
--- /dev/null
+++ b/usr/src/uts/common/io/zfd.c
@@ -0,0 +1,1157 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * Zone File Descriptor Driver.
+ *
+ * This driver is derived from the zcons driver which is in turn derived from
+ * the pts/ptm drivers. The purpose is to expose file descriptors within the
+ * zone which are connected to zoneadmd and used for logging or an interactive
+ * connection to a process within the zone.
+ *
+ * Its implementation is straightforward. Each instance of the driver
+ * represents a global-zone/local-zone pair. Unlike the zcons device, zoneadmd
+ * uses these devices unidirectionally to provide stdin, stdout and stderr to
+ * the process within the zone.
+ *
+ * Instances of zfd are onlined as children of /pseudo/zfdnex@2/ by zoneadmd,
+ * using the devctl framework; thus the driver does not need to maintain any
+ * sort of "admin" node.
+ *
+ * The driver shuttles I/O from master side to slave side and back. In a break
+ * from the pts/ptm semantics, if one side is not open, I/O directed towards
+ * it will simply be discarded. This is so that if zoneadmd is not holding the
+ * master side fd open (i.e. it has died somehow), processes in the zone do not
+ * experience any errors and I/O to the fd does not cause the process to hang.
+ *
+ * The driver can also act as a multiplexer so that data written to the
+ * slave side within the zone is also redirected back to another zfd device
+ * inside the zone for consumption (i.e. it can be read). The intention is
+ * that a logging process within the zone can consume data that is being
+ * written by an application onto the primary stream. This is essentially
+ * a tee off of the primary stream into a log stream. This tee can also be
+ * configured to be flow controlled via an ioctl. Flow control happens on the
+ * primary stream and is used to ensure that the log stream receives all of
+ * the messages off the primary stream when consumption of the data off of
+ * the log stream gets behind. Configuring for flow control implies that the
+ * application writing to the primary stream will be blocked when the log
+ * consumer gets behind. Note that closing the log stream (e.g. when the zone
+ * halts) will cause the loss of all messages queued in the stream.
+ *
+ * The zone's zfd device configuration is driven by zoneadmd and a zone mode.
+ * The mode, which is controlled by the zone attribute "zlog-mode" is somewhat
+ * of a misnomer since its purpose has evolved. The attribute can have a
+ * variety of values, but the lowest two positions are used to control how many
+ * zfd devices are created inside the zone and if the primary stream is a tty.
+ *
+ * Here is a summary of how the 4 modes control what zfd devices are created
+ * and how they're used:
+ *
+ * t-: 1 stdio zdev (0) configured as a tty
+ * --: 3 stdio zdevs (0, 1, 2), not configured as a tty
+ * tn: 1 stdio zdev (0) configured as a tty, 1 additional zdev (1)
+ * -n: 3 stdio zdevs (0, 1, 2), not tty, 2 additional zdevs (3, 4)
+ *
+ * With the 't' flag set, stdin/out/err is multiplexed onto a single full-duplex
+ * stream which is configured as a tty. That is, ptem, ldterm and ttycompat are
+ * autopushed onto the stream when the slave side is opened. There is only a
+ * single zfd dev (0) needed for the primary stream.
+ *
+ * When the 'n' flag is set, it is assumed that output logging will be done
+ * within the zone itself. In this configuration 1 or 2 additional zfd devices,
+ * depending on tty mode ('t' flag) are created within the zone. An application
+ * can then configure the zfd streams driver into a multiplexer. Output from
+ * the stdout/stderr zfd(s) will be teed into the correspond logging zfd(s)
+ * within the zone.
+ *
+ * The following is a diagram of how this works for a '-n' configuration:
+ *
+ *
+ * zoneadmd (for zlogin -I stdout)
+ * GZ: ^
+ * |
+ * --------------------------
+ * ^
+ * NGZ: |
+ * app >1 -> zfd1 -> zfd3 -> logger (for logger to consume app's stdout)
+ *
+ * There would be a similar path for the app's stderr into zfd4 for the logger
+ * to consume stderr.
+ */
+
+#include <sys/types.h>
+#include <sys/cmn_err.h>
+#include <sys/conf.h>
+#include <sys/cred.h>
+#include <sys/ddi.h>
+#include <sys/debug.h>
+#include <sys/devops.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kstr.h>
+#include <sys/modctl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/strsun.h>
+#include <sys/sunddi.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/zfd.h>
+#include <sys/vnode.h>
+#include <sys/fs/snode.h>
+#include <sys/zone.h>
+#include <sys/sdt.h>
+
+static kmutex_t zfd_mux_lock;
+
+static int zfd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int zfd_attach(dev_info_t *, ddi_attach_cmd_t);
+static int zfd_detach(dev_info_t *, ddi_detach_cmd_t);
+
+static int zfd_open(queue_t *, dev_t *, int, int, cred_t *);
+static int zfd_close(queue_t *, int, cred_t *);
+static int zfd_wput(queue_t *, mblk_t *);
+static int zfd_rsrv(queue_t *);
+static int zfd_wsrv(queue_t *);
+
+/*
+ * The instance number is encoded in the dev_t in the minor number; the lowest
+ * bit of the minor number is used to track the master vs. slave side of the
+ * fd. The rest of the bits in the minor number are the instance.
+ */
+#define ZFD_MASTER_MINOR 0
+#define ZFD_SLAVE_MINOR 1
+
+#define ZFD_INSTANCE(x) (getminor((x)) >> 1)
+#define ZFD_NODE(x) (getminor((x)) & 0x01)
+
+/*
+ * This macro converts a zfd_state_t pointer to the associated slave minor
+ * node's dev_t.
+ */
+#define ZFD_STATE_TO_SLAVEDEV(x) \
+ (makedevice(ddi_driver_major((x)->zfd_devinfo), \
+ (minor_t)(ddi_get_instance((x)->zfd_devinfo) << 1 | ZFD_SLAVE_MINOR)))
+
+int zfd_debug = 0;
+#define DBG(a) if (zfd_debug) cmn_err(CE_NOTE, a)
+#define DBG1(a, b) if (zfd_debug) cmn_err(CE_NOTE, a, b)
+
+/*
+ * ZFD Pseudo Terminal Module: stream data structure definitions,
+ * based on zcons.
+ */
+static struct module_info zfd_info = {
+ 0x20FD, /* ZOFD - 8445 */
+ "zfd",
+ 0, /* min packet size */
+ INFPSZ, /* max packet size - infinity */
+ 2048, /* high water */
+ 128 /* low water */
+};
+
+static struct qinit zfd_rinit = {
+ NULL,
+ zfd_rsrv,
+ zfd_open,
+ zfd_close,
+ NULL,
+ &zfd_info,
+ NULL
+};
+
+static struct qinit zfd_winit = {
+ zfd_wput,
+ zfd_wsrv,
+ NULL,
+ NULL,
+ NULL,
+ &zfd_info,
+ NULL
+};
+
+static struct streamtab zfd_tab_info = {
+ &zfd_rinit,
+ &zfd_winit,
+ NULL,
+ NULL
+};
+
+#define ZFD_CONF_FLAG (D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL)
+
+/*
+ * this will define (struct cb_ops cb_zfd_ops) and (struct dev_ops zfd_ops)
+ */
+DDI_DEFINE_STREAM_OPS(zfd_ops, nulldev, nulldev, zfd_attach, zfd_detach, \
+ nodev, zfd_getinfo, ZFD_CONF_FLAG, &zfd_tab_info, \
+ ddi_quiesce_not_needed);
+
+/*
+ * Module linkage information for the kernel.
+ */
+
+static struct modldrv modldrv = {
+ &mod_driverops, /* Type of module (this is a pseudo driver) */
+ "Zone FD driver", /* description of module */
+ &zfd_ops /* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1,
+ &modldrv,
+ NULL
+};
+
+typedef enum {
+ ZFD_NO_MUX,
+ ZFD_PRIMARY_STREAM,
+ ZFD_LOG_STREAM
+} zfd_mux_type_t;
+
+typedef struct zfd_state {
+ dev_info_t *zfd_devinfo; /* instance info */
+ queue_t *zfd_master_rdq; /* GZ read queue */
+ queue_t *zfd_slave_rdq; /* in-zone read queue */
+ int zfd_state; /* ZFD_STATE_MOPEN, ZFD_STATE_SOPEN */
+ int zfd_tty; /* ZFD_MAKETTY - strm mods will push */
+ boolean_t zfd_is_flowcon; /* primary stream flow stopped */
+ boolean_t zfd_allow_flowcon; /* use flow control */
+ zfd_mux_type_t zfd_muxt; /* state type: none, primary, log */
+ struct zfd_state *zfd_inst_pri; /* log state's primary ptr */
+ struct zfd_state *zfd_inst_log; /* primary state's log ptr */
+} zfd_state_t;
+
+#define ZFD_STATE_MOPEN 0x01
+#define ZFD_STATE_SOPEN 0x02
+
+static void *zfd_soft_state;
+
+/*
+ * List of STREAMS modules that are autopushed onto a slave instance when its
+ * opened, but only if the ZFD_MAKETTY ioctl has first been received by the
+ * master.
+ */
+static char *zfd_mods[] = {
+ "ptem",
+ "ldterm",
+ "ttcompat",
+ NULL
+};
+
+int
+_init(void)
+{
+ int err;
+
+ if ((err = ddi_soft_state_init(&zfd_soft_state, sizeof (zfd_state_t),
+ 0)) != 0) {
+ return (err);
+ }
+
+ if ((err = mod_install(&modlinkage)) != 0)
+ ddi_soft_state_fini(zfd_soft_state);
+
+ mutex_init(&zfd_mux_lock, NULL, MUTEX_DEFAULT, NULL);
+ return (err);
+}
+
+
+int
+_fini(void)
+{
+ int err;
+
+ if ((err = mod_remove(&modlinkage)) != 0) {
+ return (err);
+ }
+
+ ddi_soft_state_fini(&zfd_soft_state);
+ mutex_destroy(&zfd_mux_lock);
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+static int
+zfd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ zfd_state_t *zfds;
+ int instance;
+ char masternm[ZFD_NAME_LEN], slavenm[ZFD_NAME_LEN];
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ instance = ddi_get_instance(dip);
+ if (ddi_soft_state_zalloc(zfd_soft_state, instance) != DDI_SUCCESS)
+ return (DDI_FAILURE);
+
+ (void) snprintf(masternm, sizeof (masternm), "%s%d", ZFD_MASTER_NAME,
+ instance);
+ (void) snprintf(slavenm, sizeof (slavenm), "%s%d", ZFD_SLAVE_NAME,
+ instance);
+
+ /*
+ * Create the master and slave minor nodes.
+ */
+ if ((ddi_create_minor_node(dip, slavenm, S_IFCHR,
+ instance << 1 | ZFD_SLAVE_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE) ||
+ (ddi_create_minor_node(dip, masternm, S_IFCHR,
+ instance << 1 | ZFD_MASTER_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE)) {
+ ddi_remove_minor_node(dip, NULL);
+ ddi_soft_state_free(zfd_soft_state, instance);
+ return (DDI_FAILURE);
+ }
+
+ VERIFY((zfds = ddi_get_soft_state(zfd_soft_state, instance)) != NULL);
+ zfds->zfd_devinfo = dip;
+ zfds->zfd_tty = 0;
+ zfds->zfd_muxt = ZFD_NO_MUX;
+ zfds->zfd_inst_log = NULL;
+ return (DDI_SUCCESS);
+}
+
+static int
+zfd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ zfd_state_t *zfds;
+ int instance;
+
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ instance = ddi_get_instance(dip);
+ if ((zfds = ddi_get_soft_state(zfd_soft_state, instance)) == NULL)
+ return (DDI_FAILURE);
+
+ if ((zfds->zfd_state & ZFD_STATE_MOPEN) ||
+ (zfds->zfd_state & ZFD_STATE_SOPEN)) {
+ DBG1("zfd_detach: device (dip=%p) still open\n", (void *)dip);
+ return (DDI_FAILURE);
+ }
+
+ ddi_remove_minor_node(dip, NULL);
+ ddi_soft_state_free(zfd_soft_state, instance);
+
+ return (DDI_SUCCESS);
+}
+
+/*
+ * zfd_getinfo()
+ * getinfo(9e) entrypoint.
+ */
+/*ARGSUSED*/
+static int
+zfd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ zfd_state_t *zfds;
+ int instance = ZFD_INSTANCE((dev_t)arg);
+
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if ((zfds = ddi_get_soft_state(zfd_soft_state,
+ instance)) == NULL)
+ return (DDI_FAILURE);
+ *result = zfds->zfd_devinfo;
+ return (DDI_SUCCESS);
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = (void *)(uintptr_t)instance;
+ return (DDI_SUCCESS);
+ }
+ return (DDI_FAILURE);
+}
+
+/*
+ * Return the equivalent queue from the other side of the relationship.
+ * e.g.: given the slave's write queue, return the master's write queue.
+ */
+static queue_t *
+zfd_switch(queue_t *qp)
+{
+ zfd_state_t *zfds = qp->q_ptr;
+ ASSERT(zfds != NULL);
+
+ if (qp == zfds->zfd_master_rdq)
+ return (zfds->zfd_slave_rdq);
+ else if (OTHERQ(qp) == zfds->zfd_master_rdq && zfds->zfd_slave_rdq
+ != NULL)
+ return (OTHERQ(zfds->zfd_slave_rdq));
+ else if (qp == zfds->zfd_slave_rdq)
+ return (zfds->zfd_master_rdq);
+ else if (OTHERQ(qp) == zfds->zfd_slave_rdq && zfds->zfd_master_rdq
+ != NULL)
+ return (OTHERQ(zfds->zfd_master_rdq));
+ else
+ return (NULL);
+}
+
+/*
+ * For debugging and outputting messages. Returns the name of the side of
+ * the relationship associated with this queue.
+ */
+static const char *
+zfd_side(queue_t *qp)
+{
+ zfd_state_t *zfds = qp->q_ptr;
+ ASSERT(zfds != NULL);
+
+ if (qp == zfds->zfd_master_rdq ||
+ OTHERQ(qp) == zfds->zfd_master_rdq) {
+ return ("master");
+ }
+ ASSERT(qp == zfds->zfd_slave_rdq || OTHERQ(qp) == zfds->zfd_slave_rdq);
+ return ("slave");
+}
+
+/*ARGSUSED*/
+static int
+zfd_master_open(zfd_state_t *zfds,
+ queue_t *rqp, /* pointer to the read side queue */
+ dev_t *devp, /* pointer to stream tail's dev */
+ int oflag, /* the user open(2) supplied flags */
+ int sflag, /* open state flag */
+ cred_t *credp) /* credentials */
+{
+ mblk_t *mop;
+ struct stroptions *sop;
+
+ /*
+ * Enforce exclusivity on the master side; the only consumer should
+ * be the zoneadmd for the zone.
+ */
+ if ((zfds->zfd_state & ZFD_STATE_MOPEN) != 0)
+ return (EBUSY);
+
+ if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
+ DBG("zfd_master_open(): mop allocation failed\n");
+ return (ENOMEM);
+ }
+
+ zfds->zfd_state |= ZFD_STATE_MOPEN;
+
+ /*
+ * q_ptr stores driver private data; stash the soft state data on both
+ * read and write sides of the queue.
+ */
+ WR(rqp)->q_ptr = rqp->q_ptr = zfds;
+ qprocson(rqp);
+
+ /*
+ * Following qprocson(), the master side is fully plumbed into the
+ * STREAM and may send/receive messages. Setting zfds->zfd_master_rdq
+ * will allow the slave to send messages to us (the master).
+ * This cannot occur before qprocson() because the master is not
+ * ready to process them until that point.
+ */
+ zfds->zfd_master_rdq = rqp;
+
+ /*
+ * set up hi/lo water marks on stream head read queue and add
+ * controlling tty as needed.
+ */
+ mop->b_datap->db_type = M_SETOPTS;
+ mop->b_wptr += sizeof (struct stroptions);
+ sop = (struct stroptions *)(void *)mop->b_rptr;
+ if (oflag & FNOCTTY)
+ sop->so_flags = SO_HIWAT | SO_LOWAT;
+ else
+ sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
+ sop->so_hiwat = 512;
+ sop->so_lowat = 256;
+ putnext(rqp, mop);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+zfd_slave_open(zfd_state_t *zfds,
+ queue_t *rqp, /* pointer to the read side queue */
+ dev_t *devp, /* pointer to stream tail's dev */
+ int oflag, /* the user open(2) supplied flags */
+ int sflag, /* open state flag */
+ cred_t *credp) /* credentials */
+{
+ mblk_t *mop;
+ struct stroptions *sop;
+ /*
+ * The slave side can be opened as many times as needed.
+ */
+ if ((zfds->zfd_state & ZFD_STATE_SOPEN) != 0) {
+ ASSERT((rqp != NULL) && (WR(rqp)->q_ptr == zfds));
+ return (0);
+ }
+
+ /* A log stream is read-only */
+ if (zfds->zfd_muxt == ZFD_LOG_STREAM &&
+ (oflag & (FREAD | FWRITE)) != FREAD)
+ return (EINVAL);
+
+ if (zfds->zfd_tty == 1) {
+ major_t major;
+ minor_t minor;
+ minor_t lastminor;
+ uint_t anchorindex;
+
+ /*
+ * Set up sad(7D) so that the necessary STREAMS modules will
+ * be in place. A wrinkle is that 'ptem' must be anchored
+ * in place (see streamio(7i)) because we always want the
+ * fd to have terminal semantics.
+ */
+ minor =
+ ddi_get_instance(zfds->zfd_devinfo) << 1 | ZFD_SLAVE_MINOR;
+ major = ddi_driver_major(zfds->zfd_devinfo);
+ lastminor = 0;
+ anchorindex = 1;
+ if (kstr_autopush(SET_AUTOPUSH, &major, &minor, &lastminor,
+ &anchorindex, zfd_mods) != 0) {
+ DBG("zfd_slave_open(): kstr_autopush() failed\n");
+ return (EIO);
+ }
+ }
+
+ if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
+ DBG("zfd_slave_open(): mop allocation failed\n");
+ return (ENOMEM);
+ }
+
+ zfds->zfd_state |= ZFD_STATE_SOPEN;
+
+ /*
+ * q_ptr stores driver private data; stash the soft state data on both
+ * read and write sides of the queue.
+ */
+ WR(rqp)->q_ptr = rqp->q_ptr = zfds;
+
+ qprocson(rqp);
+
+ /*
+ * Must follow qprocson(), since we aren't ready to process until then.
+ */
+ zfds->zfd_slave_rdq = rqp;
+
+ /*
+ * set up hi/lo water marks on stream head read queue and add
+ * controlling tty as needed.
+ */
+ mop->b_datap->db_type = M_SETOPTS;
+ mop->b_wptr += sizeof (struct stroptions);
+ sop = (struct stroptions *)(void *)mop->b_rptr;
+ sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
+ sop->so_hiwat = 512;
+ sop->so_lowat = 256;
+ putnext(rqp, mop);
+
+ return (0);
+}
+
+/*
+ * open(9e) entrypoint; checks sflag, and rejects anything unordinary.
+ */
+static int
+zfd_open(queue_t *rqp, /* pointer to the read side queue */
+ dev_t *devp, /* pointer to stream tail's dev */
+ int oflag, /* the user open(2) supplied flags */
+ int sflag, /* open state flag */
+ cred_t *credp) /* credentials */
+{
+ int instance = ZFD_INSTANCE(*devp);
+ int ret;
+ zfd_state_t *zfds;
+
+ if (sflag != 0)
+ return (EINVAL);
+
+ if ((zfds = ddi_get_soft_state(zfd_soft_state, instance)) == NULL)
+ return (ENXIO);
+
+ switch (ZFD_NODE(*devp)) {
+ case ZFD_MASTER_MINOR:
+ ret = zfd_master_open(zfds, rqp, devp, oflag, sflag, credp);
+ break;
+ case ZFD_SLAVE_MINOR:
+ ret = zfd_slave_open(zfds, rqp, devp, oflag, sflag, credp);
+ /*
+ * If we just opened the log stream and flow control has
+ * been enabled, we want to make sure the primary stream can
+ * start flowing.
+ */
+ if (ret == 0 && zfds->zfd_muxt == ZFD_LOG_STREAM &&
+ zfds->zfd_inst_pri->zfd_allow_flowcon) {
+ zfds->zfd_inst_pri->zfd_is_flowcon = B_FALSE;
+ if (zfds->zfd_inst_pri->zfd_master_rdq != NULL)
+ qenable(RD(zfds->zfd_inst_pri->zfd_master_rdq));
+ }
+ break;
+ default:
+ ret = ENXIO;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * close(9e) entrypoint.
+ */
+/*ARGSUSED1*/
+static int
+zfd_close(queue_t *rqp, int flag, cred_t *credp)
+{
+ queue_t *wqp;
+ mblk_t *bp;
+ zfd_state_t *zfds;
+ major_t major;
+ minor_t minor;
+
+ zfds = (zfd_state_t *)rqp->q_ptr;
+
+ if (rqp == zfds->zfd_master_rdq) {
+ DBG("Closing master side");
+
+ zfds->zfd_master_rdq = NULL;
+ zfds->zfd_state &= ~ZFD_STATE_MOPEN;
+
+ /*
+ * qenable slave side write queue so that it can flush
+ * its messages as master's read queue is going away
+ */
+ if (zfds->zfd_slave_rdq != NULL) {
+ qenable(WR(zfds->zfd_slave_rdq));
+ }
+
+ qprocsoff(rqp);
+ WR(rqp)->q_ptr = rqp->q_ptr = NULL;
+
+ } else if (rqp == zfds->zfd_slave_rdq) {
+
+ DBG("Closing slave side");
+ zfds->zfd_state &= ~ZFD_STATE_SOPEN;
+ zfds->zfd_slave_rdq = NULL;
+
+ wqp = WR(rqp);
+ while ((bp = getq(wqp)) != NULL) {
+ if (zfds->zfd_master_rdq != NULL)
+ putnext(zfds->zfd_master_rdq, bp);
+ else if (bp->b_datap->db_type == M_IOCTL)
+ miocnak(wqp, bp, 0, 0);
+ else
+ freemsg(bp);
+ }
+
+ /*
+ * Qenable master side write queue so that it can flush its
+ * messages as slaves's read queue is going away.
+ */
+ if (zfds->zfd_master_rdq != NULL)
+ qenable(WR(zfds->zfd_master_rdq));
+
+ /*
+ * Qenable primary stream if necessary.
+ */
+ if (zfds->zfd_muxt == ZFD_LOG_STREAM &&
+ zfds->zfd_inst_pri->zfd_allow_flowcon) {
+ zfds->zfd_inst_pri->zfd_is_flowcon = B_FALSE;
+ if (zfds->zfd_inst_pri->zfd_master_rdq != NULL)
+ qenable(RD(zfds->zfd_inst_pri->zfd_master_rdq));
+ }
+
+ qprocsoff(rqp);
+ WR(rqp)->q_ptr = rqp->q_ptr = NULL;
+
+ if (zfds->zfd_tty == 1) {
+ /*
+ * Clear the sad configuration so that reopening
+ * doesn't fail to set up sad configuration.
+ */
+ major = ddi_driver_major(zfds->zfd_devinfo);
+ minor = ddi_get_instance(zfds->zfd_devinfo) << 1 |
+ ZFD_SLAVE_MINOR;
+ (void) kstr_autopush(CLR_AUTOPUSH, &major, &minor,
+ NULL, NULL, NULL);
+ }
+ }
+
+ return (0);
+}
+
+static void
+handle_mflush(queue_t *qp, mblk_t *mp)
+{
+ mblk_t *nmp;
+ DBG1("M_FLUSH on %s side", zfd_side(qp));
+
+ if (*mp->b_rptr & FLUSHW) {
+ DBG1("M_FLUSH, FLUSHW, %s side", zfd_side(qp));
+ flushq(qp, FLUSHDATA);
+ *mp->b_rptr &= ~FLUSHW;
+ if ((*mp->b_rptr & FLUSHR) == 0) {
+ /*
+ * FLUSHW only. Change to FLUSHR and putnext other side,
+ * then we are done.
+ */
+ *mp->b_rptr |= FLUSHR;
+ if (zfd_switch(RD(qp)) != NULL) {
+ putnext(zfd_switch(RD(qp)), mp);
+ return;
+ }
+ } else if ((zfd_switch(RD(qp)) != NULL) &&
+ (nmp = copyb(mp)) != NULL) {
+ /*
+ * It is a FLUSHRW; we copy the mblk and send
+ * it to the other side, since we still need to use
+ * the mblk in FLUSHR processing, below.
+ */
+ putnext(zfd_switch(RD(qp)), nmp);
+ }
+ }
+
+ if (*mp->b_rptr & FLUSHR) {
+ DBG("qreply(qp) turning FLUSHR around\n");
+ qreply(qp, mp);
+ return;
+ }
+ freemsg(mp);
+}
+
+/*
+ * Evaluate the various conditionals to determine if we're teeing into a log
+ * stream and if the primary stream should be flow controlled. This function
+ * can set the zfd_is_flowcon flag as a side effect.
+ *
+ * When teeing with flow control, we always queue the teed msg here and if
+ * the queue is getting full, we set zfd_is_flowcon. The primary stream will
+ * always queue when zfd_is_flowcon and will also not be served when
+ * zfd_is_flowcon is set. This causes backpressure on the primary stream
+ * until the teed queue can drain.
+ */
+static void
+zfd_tee_handler(zfd_state_t *zfds, unsigned char type, mblk_t *mp)
+{
+ queue_t *log_qp;
+ zfd_state_t *log_zfds;
+ mblk_t *lmp;
+
+ if (zfds->zfd_muxt != ZFD_PRIMARY_STREAM)
+ return;
+
+ if (type != M_DATA)
+ return;
+
+ log_zfds = zfds->zfd_inst_log;
+ if (log_zfds == NULL)
+ return;
+
+ ASSERT(log_zfds->zfd_muxt == ZFD_LOG_STREAM);
+
+ if ((log_zfds->zfd_state & ZFD_STATE_SOPEN) == 0) {
+ if (zfds->zfd_allow_flowcon)
+ zfds->zfd_is_flowcon = B_TRUE;
+ return;
+ }
+
+ /* The zfd_slave_rdq is null until the log dev is opened in the zone */
+ log_qp = RD(log_zfds->zfd_slave_rdq);
+ DTRACE_PROBE2(zfd__tee__check, void *, log_qp, void *, zfds);
+
+ if (!zfds->zfd_allow_flowcon) {
+ /*
+ * We're not supposed to tee with flow control and the tee is
+ * full so we skip teeing into the log stream.
+ */
+ if ((log_qp->q_flag & QFULL) != 0)
+ return;
+ }
+
+ /*
+ * Tee the message into the log stream.
+ */
+ lmp = dupmsg(mp);
+ if (lmp == NULL) {
+ if (zfds->zfd_allow_flowcon)
+ zfds->zfd_is_flowcon = B_TRUE;
+ return;
+ }
+
+ if (log_qp->q_first == NULL && bcanputnext(log_qp, lmp->b_band)) {
+ putnext(log_qp, lmp);
+ } else {
+ if (putq(log_qp, lmp) == 0) {
+ /* The logger queue is full, free the msg. */
+ freemsg(lmp);
+ }
+ /*
+ * If we're supposed to tee with flow control and the tee is
+ * over the high water mark then we want the primary stream to
+ * stop flowing. We'll stop queueing the primary stream after
+ * the log stream has drained.
+ */
+ if (zfds->zfd_allow_flowcon &&
+ log_qp->q_count > log_qp->q_hiwat) {
+ zfds->zfd_is_flowcon = B_TRUE;
+ }
+ }
+}
+
+/*
+ * wput(9E) is symmetric for master and slave sides, so this handles both
+ * without splitting the codepath. (The only exception to this is the
+ * processing of zfd ioctls, which is restricted to the master side.)
+ *
+ * zfd_wput() looks at the other side; if there is no process holding that
+ * side open, it frees the message. This prevents processes from hanging
+ * if no one is holding open the fd. Otherwise, it putnext's high
+ * priority messages, putnext's normal messages if possible, and otherwise
+ * enqueues the messages; in the case that something is enqueued, wsrv(9E)
+ * will take care of eventually shuttling I/O to the other side.
+ *
+ * When configured as a multiplexer, then anything written to the stream
+ * from inside the zone is also teed off to the corresponding log stream
+ * for consumption within the zone (i.e. the log stream can be read, but never
+ * written to, by an application inside the zone).
+ */
+static int
+zfd_wput(queue_t *qp, mblk_t *mp)
+{
+ unsigned char type = mp->b_datap->db_type;
+ zfd_state_t *zfds;
+ struct iocblk *iocbp;
+ boolean_t must_queue = B_FALSE;
+
+ ASSERT(qp->q_ptr);
+
+ DBG1("entering zfd_wput, %s side", zfd_side(qp));
+
+ /*
+ * Process zfd ioctl messages if qp is the master side's write queue.
+ */
+ zfds = (zfd_state_t *)qp->q_ptr;
+
+ if (type == M_IOCTL) {
+ iocbp = (struct iocblk *)(void *)mp->b_rptr;
+
+ switch (iocbp->ioc_cmd) {
+ case ZFD_MAKETTY:
+ zfds->zfd_tty = 1;
+ miocack(qp, mp, 0, 0);
+ return (0);
+ case ZFD_EOF:
+ if (zfds->zfd_slave_rdq != NULL)
+ (void) putnextctl(zfds->zfd_slave_rdq,
+ M_HANGUP);
+ miocack(qp, mp, 0, 0);
+ return (0);
+ case ZFD_HAS_SLAVE:
+ if ((zfds->zfd_state & ZFD_STATE_SOPEN) != 0) {
+ miocack(qp, mp, 0, 0);
+ } else {
+ miocack(qp, mp, 0, ENOTTY);
+ }
+ return (0);
+ case ZFD_MUX: {
+ /*
+ * Setup the multiplexer configuration for the two
+ * streams.
+ *
+ * We expect to be called on the stream that will
+ * become the log stream and be passed one data block
+ * with the minor number of the slave side of the
+ * primary stream.
+ */
+ int to;
+ int instance;
+ zfd_state_t *prim_zfds;
+
+ if (iocbp->ioc_count != TRANSPARENT ||
+ mp->b_cont == NULL) {
+ miocack(qp, mp, 0, EINVAL);
+ return (0);
+ }
+
+ /* Get the primary slave minor device number */
+ to = *(int *)mp->b_cont->b_rptr;
+ instance = ZFD_INSTANCE(to);
+
+ if ((prim_zfds = ddi_get_soft_state(zfd_soft_state,
+ instance)) == NULL) {
+ miocack(qp, mp, 0, EINVAL);
+ return (0);
+ }
+
+ /* Disallow changing primary/log once set. */
+ mutex_enter(&zfd_mux_lock);
+ if (zfds->zfd_muxt != ZFD_NO_MUX ||
+ prim_zfds->zfd_muxt != ZFD_NO_MUX) {
+ mutex_exit(&zfd_mux_lock);
+ miocack(qp, mp, 0, EINVAL);
+ return (0);
+ }
+
+ zfds->zfd_muxt = ZFD_LOG_STREAM;
+ zfds->zfd_inst_pri = prim_zfds;
+ prim_zfds->zfd_muxt = ZFD_PRIMARY_STREAM;
+ prim_zfds->zfd_inst_log = zfds;
+ mutex_exit(&zfd_mux_lock);
+ DTRACE_PROBE2(zfd__mux__link, void *, prim_zfds,
+ void *, zfds);
+
+ miocack(qp, mp, 0, 0);
+ return (0);
+ }
+ case ZFD_MUX_FLOWCON: {
+ /*
+ * We expect this ioctl to be issued against the
+ * log stream. We don't use the primary stream since
+ * there can be other streams modules pushed onto that
+ * stream which would interfere with the ioctl.
+ */
+ int val;
+ zfd_state_t *prim_zfds;
+
+ if (iocbp->ioc_count != TRANSPARENT ||
+ mp->b_cont == NULL) {
+ miocack(qp, mp, 0, EINVAL);
+ return (0);
+ }
+
+ if (zfds->zfd_muxt != ZFD_LOG_STREAM) {
+ miocack(qp, mp, 0, EINVAL);
+ return (0);
+ }
+ prim_zfds = zfds->zfd_inst_pri;
+
+ /* Get the flow control setting */
+ val = *(int *)mp->b_cont->b_rptr;
+ if (val != 0 && val != 1) {
+ miocack(qp, mp, 0, EINVAL);
+ return (0);
+ }
+
+ prim_zfds->zfd_allow_flowcon = (boolean_t)val;
+ if (!prim_zfds->zfd_allow_flowcon)
+ prim_zfds->zfd_is_flowcon = B_FALSE;
+
+ DTRACE_PROBE1(zfd__mux__flowcon, void *, prim_zfds);
+ miocack(qp, mp, 0, 0);
+ return (0);
+ }
+ default:
+ break;
+ }
+ }
+
+ /* if on the write side, may need to tee */
+ if (zfds->zfd_slave_rdq != NULL && qp == WR(zfds->zfd_slave_rdq)) {
+ /* tee output to any attached log stream */
+ zfd_tee_handler(zfds, type, mp);
+
+ /* high-priority msgs are not subject to flow control */
+ if (zfds->zfd_is_flowcon && type == M_DATA)
+ must_queue = B_TRUE;
+ }
+
+ if (zfd_switch(RD(qp)) == NULL) {
+ DBG1("wput to %s side (no one listening)", zfd_side(qp));
+ switch (type) {
+ case M_FLUSH:
+ handle_mflush(qp, mp);
+ break;
+ case M_IOCTL:
+ miocnak(qp, mp, 0, 0);
+ break;
+ default:
+ freemsg(mp);
+ break;
+ }
+ return (0);
+ }
+
+ if (type >= QPCTL) {
+ DBG1("(hipri) wput, %s side", zfd_side(qp));
+ switch (type) {
+ case M_READ: /* supposedly from ldterm? */
+ DBG("zfd_wput: tossing M_READ\n");
+ freemsg(mp);
+ break;
+ case M_FLUSH:
+ handle_mflush(qp, mp);
+ break;
+ default:
+ /*
+ * Put this to the other side.
+ */
+ ASSERT(zfd_switch(RD(qp)) != NULL);
+ putnext(zfd_switch(RD(qp)), mp);
+ break;
+ }
+ DBG1("done (hipri) wput, %s side", zfd_side(qp));
+ return (0);
+ }
+
+ /*
+ * If the primary stream has been stopped for flow control then
+ * enqueue the msg, otherwise only putnext if there isn't already
+ * something in the queue. If we don't do this then things would wind
+ * up out of order.
+ */
+ if (!must_queue && qp->q_first == NULL &&
+ bcanputnext(RD(zfd_switch(qp)), mp->b_band)) {
+ putnext(RD(zfd_switch(qp)), mp);
+ } else {
+ /*
+ * zfd_wsrv expects msgs queued on the primary queue. Those
+ * will be handled by zfd_wsrv after zfd_rsrv performs the
+ * qenable on the proper queue.
+ */
+ (void) putq(qp, mp);
+ }
+
+ DBG1("done wput, %s side", zfd_side(qp));
+ return (0);
+}
+
+/*
+ * Read server
+ *
+ * For primary stream:
+ * Under normal execution rsrv(9E) is symmetric for master and slave, so
+ * zfd_rsrv() can handle both without splitting up the codepath. We do this by
+ * enabling the write side of the partner. This triggers the partner to send
+ * messages queued on its write side to this queue's read side.
+ *
+ * For log stream:
+ * Internally we've queued up the msgs that we've teed off to the log stream
+ * so when we're invoked we need to pass these along.
+ */
+static int
+zfd_rsrv(queue_t *qp)
+{
+ zfd_state_t *zfds;
+ zfds = (zfd_state_t *)qp->q_ptr;
+
+ /*
+ * log stream server
+ */
+ if (zfds->zfd_muxt == ZFD_LOG_STREAM && zfds->zfd_slave_rdq != NULL) {
+ queue_t *log_qp;
+ mblk_t *mp;
+
+ log_qp = RD(zfds->zfd_slave_rdq);
+
+ if ((zfds->zfd_state & ZFD_STATE_SOPEN) != 0) {
+ zfd_state_t *pzfds = zfds->zfd_inst_pri;
+
+ while ((mp = getq(qp)) != NULL) {
+ if (bcanputnext(log_qp, mp->b_band)) {
+ putnext(log_qp, mp);
+ } else {
+ (void) putbq(log_qp, mp);
+ break;
+ }
+ }
+
+ if (log_qp->q_count < log_qp->q_lowat) {
+ DTRACE_PROBE(zfd__flow__on);
+ pzfds->zfd_is_flowcon = B_FALSE;
+ if (pzfds->zfd_master_rdq != NULL)
+ qenable(RD(pzfds->zfd_master_rdq));
+ }
+ } else {
+ /* No longer open, drain the queue */
+ while ((mp = getq(qp)) != NULL) {
+ freemsg(mp);
+ }
+ flushq(qp, FLUSHALL);
+ }
+ return (0);
+ }
+
+ /*
+ * Care must be taken here, as either of the master or slave side
+ * qptr could be NULL.
+ */
+ ASSERT(qp == zfds->zfd_master_rdq || qp == zfds->zfd_slave_rdq);
+ if (zfd_switch(qp) == NULL) {
+ DBG("zfd_rsrv: other side isn't listening\n");
+ return (0);
+ }
+ qenable(WR(zfd_switch(qp)));
+ return (0);
+}
+
+/*
+ * Write server
+ *
+ * This routine is symmetric for master and slave, so it handles both without
+ * splitting up the codepath.
+ *
+ * If there are messages on this queue that can be sent to the other, send
+ * them via putnext(). Else, if queued messages cannot be sent, leave them
+ * on this queue.
+ */
+static int
+zfd_wsrv(queue_t *qp)
+{
+ queue_t *swq;
+ mblk_t *mp;
+ zfd_state_t *zfds = (zfd_state_t *)qp->q_ptr;
+
+ ASSERT(zfds != NULL);
+
+ /*
+ * Partner has no read queue, so take the data, and throw it away.
+ */
+ if (zfd_switch(RD(qp)) == NULL) {
+ DBG("zfd_wsrv: other side isn't listening");
+ while ((mp = getq(qp)) != NULL) {
+ if (mp->b_datap->db_type == M_IOCTL)
+ miocnak(qp, mp, 0, 0);
+ else
+ freemsg(mp);
+ }
+ flushq(qp, FLUSHALL);
+ return (0);
+ }
+
+ swq = RD(zfd_switch(qp));
+
+ /*
+ * while there are messages on this write queue...
+ */
+ while (!zfds->zfd_is_flowcon && (mp = getq(qp)) != NULL) {
+ /*
+ * Due to the way zfd_wput is implemented, we should never
+ * see a high priority control message here.
+ */
+ ASSERT(mp->b_datap->db_type < QPCTL);
+
+ if (bcanputnext(swq, mp->b_band)) {
+ putnext(swq, mp);
+ } else {
+ (void) putbq(qp, mp);
+ break;
+ }
+ }
+ return (0);
+}
diff --git a/usr/src/uts/common/klm/klmmod.c b/usr/src/uts/common/klm/klmmod.c
index 1546b3f67b..8a714ec686 100644
--- a/usr/src/uts/common/klm/klmmod.c
+++ b/usr/src/uts/common/klm/klmmod.c
@@ -12,6 +12,7 @@
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2017 Joyent, Inc.
*/
/*
@@ -279,6 +280,10 @@ lm_svc(struct lm_svc_args *args)
rfs4_lease_time = args->grace;
}
+ if (args->n_v4_only == -1) {
+ g->nlm_v4_only = B_TRUE;
+ }
+
mutex_exit(&g->lock);
err = nlm_svc_starting(g, fp, netid, &knc);
mutex_enter(&g->lock);
diff --git a/usr/src/uts/common/klm/mapfile-mod b/usr/src/uts/common/klm/mapfile-mod
index 0debe6d986..b7789d81fd 100644
--- a/usr/src/uts/common/klm/mapfile-mod
+++ b/usr/src/uts/common/klm/mapfile-mod
@@ -11,6 +11,7 @@
#
# Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+# Copyright 2017 Joyent, Inc.
#
@@ -49,6 +50,11 @@ SYMBOL_SCOPE {
nlm_frlock;
nlm_register_lock_locally;
nlm_shrlock;
+# These four functions are available for use within a branded zone.
+ nlm_nsm_clnt_init;
+ nlm_netbuf_to_netobj;
+ sm_mon_1;
+ sm_unmon_1;
local:
*;
diff --git a/usr/src/uts/common/klm/nlm_dispatch.c b/usr/src/uts/common/klm/nlm_dispatch.c
index a0ca2a56c4..8fa9940eae 100644
--- a/usr/src/uts/common/klm/nlm_dispatch.c
+++ b/usr/src/uts/common/klm/nlm_dispatch.c
@@ -11,6 +11,7 @@
/*
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Joyent, Inc. All rights reserved.
*/
/*
@@ -412,13 +413,13 @@ nlm_prog_3_dtable[] = {
0,
0 },
- { /* 16: not used */
- NLM_SVC_FUNC(0),
- (xdrproc_t)0,
- (xdrproc_t)0,
+ { /* 16: Linux NLMPROC_NSM_NOTIFY (same handling as NLM_SM_NOTIFY1) */
+ NLM_SVC_FUNC(nlm_sm_notify1_2_svc),
+ (xdrproc_t)xdr_nlm_sm_status,
+ (xdrproc_t)xdr_void,
NULL,
0,
- 0 },
+ NLM_DISP_NOREMOTE },
{ /* 17: NLM_SM_NOTIFY1 */
NLM_SVC_FUNC(nlm_sm_notify1_2_svc),
diff --git a/usr/src/uts/common/klm/nlm_impl.c b/usr/src/uts/common/klm/nlm_impl.c
index bab08acdae..cbba11f6ed 100644
--- a/usr/src/uts/common/klm/nlm_impl.c
+++ b/usr/src/uts/common/klm/nlm_impl.c
@@ -28,6 +28,7 @@
/*
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2017 Joyent, Inc. All rights reserved.
*/
/*
@@ -57,6 +58,7 @@
#include <sys/queue.h>
#include <sys/bitmap.h>
#include <sys/sdt.h>
+#include <sys/brand.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
@@ -202,6 +204,12 @@ static struct nlm_knc nlm_netconfigs[] = { /* (g) */
};
/*
+ * NLM functions which can be called by a brand hook.
+ */
+void nlm_netbuf_to_netobj(struct netbuf *, int *, netobj *);
+void nlm_nsm_clnt_init(CLIENT *, struct nlm_nsm *);
+
+/*
* NLM misc. function
*/
static void nlm_copy_netbuf(struct netbuf *, struct netbuf *);
@@ -210,8 +218,6 @@ static void nlm_kmem_reclaim(void *);
static void nlm_pool_shutdown(void);
static void nlm_suspend_zone(struct nlm_globals *);
static void nlm_resume_zone(struct nlm_globals *);
-static void nlm_nsm_clnt_init(CLIENT *, struct nlm_nsm *);
-static void nlm_netbuf_to_netobj(struct netbuf *, int *, netobj *);
/*
* NLM thread functions
@@ -1847,6 +1853,12 @@ nlm_host_unmonitor(struct nlm_globals *g, struct nlm_host *host)
return;
host->nh_flags &= ~NLM_NH_MONITORED;
+
+ if (ZONE_IS_BRANDED(curzone) && ZBROP(curzone)->b_rpc_statd != NULL) {
+ ZBROP(curzone)->b_rpc_statd(SM_UNMON, g, host);
+ return;
+ }
+
stat = nlm_nsm_unmon(&g->nlm_nsm, host->nh_name);
if (stat != RPC_SUCCESS) {
NLM_WARN("NLM: Failed to contact statd, stat=%d\n", stat);
@@ -1885,6 +1897,11 @@ nlm_host_monitor(struct nlm_globals *g, struct nlm_host *host, int state)
host->nh_flags |= NLM_NH_MONITORED;
mutex_exit(&host->nh_lock);
+ if (ZONE_IS_BRANDED(curzone) && ZBROP(curzone)->b_rpc_statd != NULL) {
+ ZBROP(curzone)->b_rpc_statd(SM_MON, g, host);
+ return;
+ }
+
/*
* Before we begin monitoring the host register the network address
* associated with this hostname.
@@ -2361,6 +2378,13 @@ nlm_svc_starting(struct nlm_globals *g, struct file *fp,
VERIFY(g->run_status == NLM_ST_STARTING);
VERIFY(g->nlm_gc_thread == NULL);
+ if (g->nlm_v4_only) {
+ NLM_WARN("Zone %d has no rpcbind, NLM is v4 only", getzoneid());
+ bzero(&g->nlm_nsm, sizeof (struct nlm_nsm));
+ g->nlm_nsm.ns_addr_handle = (void *)-1;
+ goto v4_only;
+ }
+
error = nlm_nsm_init_local(&g->nlm_nsm);
if (error != 0) {
NLM_ERR("Failed to initialize NSM handler "
@@ -2397,6 +2421,7 @@ nlm_svc_starting(struct nlm_globals *g, struct file *fp,
"(rpcerr=%d)\n", stat);
goto shutdown_lm;
}
+v4_only:
g->grace_threshold = ddi_get_lbolt() +
SEC_TO_TICK(g->grace_period);
@@ -2520,7 +2545,9 @@ nlm_svc_stopping(struct nlm_globals *g)
ASSERT(TAILQ_EMPTY(&g->nlm_slocks));
- nlm_nsm_fini(&g->nlm_nsm);
+ /* If started with rpcbind (the normal case) */
+ if (g->nlm_nsm.ns_addr_handle != (void *)-1)
+ nlm_nsm_fini(&g->nlm_nsm);
g->lockd_pid = 0;
g->run_status = NLM_ST_DOWN;
}
@@ -2814,14 +2841,14 @@ nlm_cprresume(void)
rw_exit(&lm_lck);
}
-static void
+void
nlm_nsm_clnt_init(CLIENT *clnt, struct nlm_nsm *nsm)
{
(void) clnt_tli_kinit(clnt, &nsm->ns_knc, &nsm->ns_addr, 0,
NLM_RPC_RETRIES, zone_kcred());
}
-static void
+void
nlm_netbuf_to_netobj(struct netbuf *addr, int *family, netobj *obj)
{
/* LINTED pointer alignment */
diff --git a/usr/src/uts/common/klm/nlm_impl.h b/usr/src/uts/common/klm/nlm_impl.h
index e59ea540e3..9caae1a8c7 100644
--- a/usr/src/uts/common/klm/nlm_impl.h
+++ b/usr/src/uts/common/klm/nlm_impl.h
@@ -30,6 +30,7 @@
/*
* Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/*
@@ -459,6 +460,7 @@ struct nlm_globals {
int cn_idle_tmo; /* (z) */
int grace_period; /* (z) */
int retrans_tmo; /* (z) */
+ boolean_t nlm_v4_only; /* (z) */
zoneid_t nlm_zoneid; /* (c) */
kmutex_t clean_lock; /* (c) */
TAILQ_ENTRY(nlm_globals) nlm_link; /* (g) */
diff --git a/usr/src/uts/common/mapfiles/ddi.mapfile b/usr/src/uts/common/mapfiles/ddi.mapfile
index a9f4f2d730..507768a223 100644
--- a/usr/src/uts/common/mapfiles/ddi.mapfile
+++ b/usr/src/uts/common/mapfiles/ddi.mapfile
@@ -194,6 +194,9 @@ SYMBOL_SCOPE {
mutex_tryenter { FLAGS = EXTERN };
nochpoll { FLAGS = EXTERN };
nodev { FLAGS = EXTERN };
+ nvlist_add_string { FLAGS = EXTERN };
+ nvlist_alloc { FLAGS = EXTERN };
+ nvlist_free { FLAGS = EXTERN };
nulldev { FLAGS = EXTERN };
nvlist_add_string { FLAGS = EXTERN };
nvlist_alloc { FLAGS = EXTERN };
diff --git a/usr/src/uts/common/netinet/udp.h b/usr/src/uts/common/netinet/udp.h
index fb9f8a0976..74cff75d43 100644
--- a/usr/src/uts/common/netinet/udp.h
+++ b/usr/src/uts/common/netinet/udp.h
@@ -1,6 +1,7 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
/*
@@ -34,6 +35,7 @@ struct udphdr {
#define UDP_RCVHDR 0x0102 /* for internal use only */
#define UDP_NAT_T_ENDPOINT 0x0103 /* for internal use only */
#define UDP_SRCPORT_HASH 0x0104 /* for internal use only */
+#define UDP_SND_TO_CONNECTED 0x0105 /* for internal use only */
/*
* Hash definitions for UDP_SRCPORT_HASH that effectively tell UDP how to go
diff --git a/usr/src/uts/common/nfs/nfssys.h b/usr/src/uts/common/nfs/nfssys.h
index e9a2746017..7d2401856c 100644
--- a/usr/src/uts/common/nfs/nfssys.h
+++ b/usr/src/uts/common/nfs/nfssys.h
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -122,13 +123,20 @@ struct nfs_revauth_args32 {
enum lm_fmly { LM_INET, LM_INET6, LM_LOOPBACK };
enum lm_proto { LM_TCP, LM_UDP };
+/*
+ * The 'n_v4_only' member was formerly called 'debug'. This member is not used
+ * in the kernel. To avoid a new version of this user/kernel interface
+ * structure, the member was renamed in a binary compatible way. It is now used
+ * by the user-level code to indicate that the zone is not running
+ * rpcbind/rpc.statd and that only NFSv4 locking is needed.
+ */
struct lm_svc_args {
int version; /* keep this first */
int fd;
enum lm_fmly n_fmly; /* protocol family */
enum lm_proto n_proto; /* protocol */
dev_t n_rdev; /* device ID */
- int debug; /* debugging level */
+ int n_v4_only; /* NFSv4 locking only */
time_t timout; /* client handle life (asynch RPCs) */
int grace; /* secs in grace period */
time_t retransmittimeout; /* retransmission interval */
@@ -141,7 +149,7 @@ struct lm_svc_args32 {
enum lm_fmly n_fmly; /* protocol family */
enum lm_proto n_proto; /* protocol */
dev32_t n_rdev; /* device ID */
- int32_t debug; /* debugging level */
+ int32_t n_v4_only; /* NFSv4 locking only */
time32_t timout; /* client handle life (asynch RPCs) */
int32_t grace; /* secs in grace period */
time32_t retransmittimeout; /* retransmission interval */
diff --git a/usr/src/uts/common/os/acct.c b/usr/src/uts/common/os/acct.c
index e598e0d08d..891c4e0836 100644
--- a/usr/src/uts/common/os/acct.c
+++ b/usr/src/uts/common/os/acct.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2017, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -47,6 +48,7 @@
#include <sys/time.h>
#include <sys/msacct.h>
#include <sys/zone.h>
+#include <sys/brand.h>
/*
* Each zone has its own accounting settings (on or off) and associated
@@ -373,7 +375,7 @@ acct_compress(ulong_t t)
* On exit, write a record on the accounting file.
*/
void
-acct(char st)
+acct(int st)
{
struct vnode *vp;
struct cred *cr;
@@ -402,6 +404,21 @@ acct(char st)
* This only gets called from exit after all lwp's have exited so no
* cred locking is needed.
*/
+
+ /* If there is a brand-specific hook, use it instead */
+ if (ZONE_IS_BRANDED(curzone) && ZBROP(curzone)->b_acct_out != NULL) {
+ ZBROP(curzone)->b_acct_out(vp, st);
+ mutex_exit(&ag->aclock);
+ return;
+ }
+
+ /*
+ * The 'st' status value was traditionally masked this way by our
+ * caller, but we now accept the unmasked value for brand handling.
+ * Zones not using the brand hook mask the status here.
+ */
+ st &= 0xff;
+
p = curproc;
ua = PTOU(p);
bcopy(ua->u_comm, ag->acctbuf.ac_comm, sizeof (ag->acctbuf.ac_comm));
diff --git a/usr/src/uts/common/os/brand.c b/usr/src/uts/common/os/brand.c
index 68b699630a..fa3555a82a 100644
--- a/usr/src/uts/common/os/brand.c
+++ b/usr/src/uts/common/os/brand.c
@@ -46,7 +46,7 @@ struct brand_mach_ops native_mach_ops = {
};
#else /* !__sparcv9 */
struct brand_mach_ops native_mach_ops = {
- NULL, NULL, NULL, NULL
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
#endif /* !__sparcv9 */
@@ -54,7 +54,8 @@ brand_t native_brand = {
BRAND_VER_1,
"native",
NULL,
- &native_mach_ops
+ &native_mach_ops,
+ 0
};
/*
@@ -311,46 +312,115 @@ brand_unregister_zone(struct brand *bp)
mutex_exit(&brand_list_lock);
}
-void
-brand_setbrand(proc_t *p)
+int
+brand_setbrand(proc_t *p, boolean_t lwps_ok)
{
brand_t *bp = p->p_zone->zone_brand;
+ void *brand_data = NULL;
- ASSERT(bp != NULL);
- ASSERT(p->p_brand == &native_brand);
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+ VERIFY(bp != NULL);
/*
- * We should only be called from exec(), when we know the process
- * is single-threaded.
+ * Process branding occurs during fork() and exec(). When it happens
+ * during fork(), the LWP count will always be 0 since branding is
+ * performed as part of getproc(), before LWPs have been associated.
+ * The same is not true during exec(), where a multi-LWP process may
+ * undergo branding just prior to gexec(). This is to ensure
+ * exec-related brand hooks are available. While it may seem
+ * complicated to brand a multi-LWP process, the two possible outcomes
+ * simplify things:
+ *
+ * 1. The exec() succeeds: LWPs besides the caller will be killed and
+ * any further branding will occur in a single-LWP context.
+ * 2. The exec() fails: The process will be promptly unbranded since
+ * the hooks are no longer needed.
+ *
+ * To prevent inconsistent brand state from being encountered during
+ * the exec(), LWPs beyond the caller which are associated with this
+ * process must be held temporarily. They will be released either when
+ * they are killed in the exec() success, or when the brand is cleared
+ * after exec() failure.
*/
- ASSERT(p->p_tlist == p->p_tlist->t_forw);
+ if (lwps_ok) {
+ /*
+ * We've been called from a exec() context tolerating the
+ * existence of multiple LWPs during branding is necessary.
+ */
+ VERIFY(p == curproc);
+ VERIFY(p->p_tlist != NULL);
+ if (p->p_tlist != p->p_tlist->t_forw) {
+ /*
+ * Multiple LWPs are present. Hold all but the caller.
+ */
+ if (!holdlwps(SHOLDFORK1)) {
+ return (-1);
+ }
+ }
+ } else {
+ /*
+ * Processes branded during fork() should not have LWPs at all.
+ */
+ VERIFY(p->p_tlist == NULL);
+ }
+
+ if (bp->b_data_size > 0) {
+ brand_data = kmem_zalloc(bp->b_data_size, KM_SLEEP);
+ }
+
+ mutex_enter(&p->p_lock);
+ ASSERT(!PROC_IS_BRANDED(p));
p->p_brand = bp;
+ p->p_brand_data = brand_data;
ASSERT(PROC_IS_BRANDED(p));
BROP(p)->b_setbrand(p);
+ mutex_exit(&p->p_lock);
+ return (0);
}
void
-brand_clearbrand(proc_t *p, boolean_t no_lwps)
+brand_clearbrand(proc_t *p, boolean_t lwps_ok)
{
brand_t *bp = p->p_zone->zone_brand;
- klwp_t *lwp = NULL;
- ASSERT(bp != NULL);
- ASSERT(!no_lwps || (p->p_tlist == NULL));
+ void *brand_data;
- /*
- * If called from exec_common() or proc_exit(),
- * we know the process is single-threaded.
- * If called from fork_fail, p_tlist is NULL.
- */
- if (!no_lwps) {
- ASSERT(p->p_tlist == p->p_tlist->t_forw);
- lwp = p->p_tlist->t_lwp;
- }
+ VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+ VERIFY(bp != NULL);
+ VERIFY(PROC_IS_BRANDED(p));
- ASSERT(PROC_IS_BRANDED(p));
- BROP(p)->b_proc_exit(p, lwp);
+ if (BROP(p)->b_clearbrand != NULL)
+ BROP(p)->b_clearbrand(p, lwps_ok);
+
+ mutex_enter(&p->p_lock);
p->p_brand = &native_brand;
+ brand_data = p->p_brand_data;
+ p->p_brand_data = NULL;
+
+ if (lwps_ok) {
+ VERIFY(p == curproc);
+ /*
+ * A process with multiple LWPs is being de-branded after
+ * failing an exec. The other LWPs were held as part of the
+ * procedure, so they must be resumed now.
+ */
+ if (p->p_tlist != NULL && p->p_tlist != p->p_tlist->t_forw) {
+ continuelwps(p);
+ }
+ } else {
+ /*
+ * While clearing the brand, it's ok for one LWP to be present.
+ * This happens when a native binary is executed inside a
+ * branded zone, since the brand will be removed during the
+ * course of a successful exec.
+ */
+ VERIFY(p->p_tlist == NULL || p->p_tlist == p->p_tlist->t_forw);
+ }
+ mutex_exit(&p->p_lock);
+
+ if (brand_data != NULL) {
+ kmem_free(brand_data, bp->b_data_size);
+ }
}
#if defined(__sparcv9)
@@ -484,7 +554,7 @@ brand_solaris_cmd(int cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
return (ENOSYS);
/* For all other operations this must be a branded process. */
- if (p->p_brand == &native_brand)
+ if (!PROC_IS_BRANDED(p))
return (ENOSYS);
ASSERT(p->p_brand == pbrand);
@@ -601,16 +671,16 @@ restoreexecenv(struct execenv *ep, stack_t *sp)
/*ARGSUSED*/
int
brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
- intpdata_t *idatap, int level, size_t *execsz, int setid, caddr_t exec_file,
- cred_t *cred, int brand_action, struct brand *pbrand, char *bname,
- char *brandlib, char *brandlib32, char *brandlinker, char *brandlinker32)
+ intpdata_t *idatap, int level, size_t *execsz, int setid,
+ caddr_t exec_file, cred_t *cred, int *brand_action, struct brand *pbrand,
+ char *bname, char *brandlib, char *brandlib32)
{
vnode_t *nvp;
Ehdr ehdr;
Addr uphdr_vaddr;
intptr_t voffset;
- int interp;
+ char *interp;
int i, err;
struct execenv env;
struct execenv origenv;
@@ -620,7 +690,6 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
klwp_t *lwp = ttolwp(curthread);
brand_proc_data_t *spd;
brand_elf_data_t sed, *sedp;
- char *linker;
uintptr_t lddata; /* lddata of executable's linker */
ASSERT(curproc->p_brand == pbrand);
@@ -637,12 +706,10 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
*/
if (args->to_model == DATAMODEL_NATIVE) {
args->emulator = brandlib;
- linker = brandlinker;
}
#if defined(_LP64)
else {
args->emulator = brandlib32;
- linker = brandlinker32;
}
#endif /* _LP64 */
@@ -726,7 +793,7 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
if (args->to_model == DATAMODEL_NATIVE) {
err = mapexec_brand(vp, args, &ehdr, &uphdr_vaddr,
&voffset, exec_file, &interp, &env.ex_bssbase,
- &env.ex_brkbase, &env.ex_brksize, NULL);
+ &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
}
#if defined(_LP64)
else {
@@ -734,7 +801,7 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
Elf32_Addr uphdr_vaddr32;
err = mapexec32_brand(vp, args, &ehdr32, &uphdr_vaddr32,
&voffset, exec_file, &interp, &env.ex_bssbase,
- &env.ex_brkbase, &env.ex_brksize, NULL);
+ &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
Ehdr32to64(&ehdr32, &ehdr);
if (uphdr_vaddr32 == (Elf32_Addr)-1)
@@ -745,6 +812,10 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
#endif /* _LP64 */
if (err != 0) {
restoreexecenv(&origenv, &orig_sigaltstack);
+
+ if (interp != NULL)
+ kmem_free(interp, MAXPATHLEN);
+
return (err);
}
@@ -762,7 +833,7 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
sedp->sed_phent = ehdr.e_phentsize;
sedp->sed_phnum = ehdr.e_phnum;
- if (interp) {
+ if (interp != NULL) {
if (ehdr.e_type == ET_DYN) {
/*
* This is a shared object executable, so we
@@ -778,16 +849,20 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
* it in and store relevant information about it in the
* aux vector, where the brand library can find it.
*/
- if ((err = lookupname(linker, UIO_SYSSPACE,
+ if ((err = lookupname(interp, UIO_SYSSPACE,
FOLLOW, NULLVPP, &nvp)) != 0) {
- uprintf("%s: not found.", brandlinker);
+ uprintf("%s: not found.", interp);
restoreexecenv(&origenv, &orig_sigaltstack);
+ kmem_free(interp, MAXPATHLEN);
return (err);
}
+
+ kmem_free(interp, MAXPATHLEN);
+
if (args->to_model == DATAMODEL_NATIVE) {
err = mapexec_brand(nvp, args, &ehdr,
&uphdr_vaddr, &voffset, exec_file, &interp,
- NULL, NULL, NULL, &lddata);
+ NULL, NULL, NULL, &lddata, NULL);
}
#if defined(_LP64)
else {
@@ -795,7 +870,7 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
Elf32_Addr uphdr_vaddr32;
err = mapexec32_brand(nvp, args, &ehdr32,
&uphdr_vaddr32, &voffset, exec_file, &interp,
- NULL, NULL, NULL, &lddata);
+ NULL, NULL, NULL, &lddata, NULL);
Ehdr32to64(&ehdr32, &ehdr);
if (uphdr_vaddr32 == (Elf32_Addr)-1)
@@ -935,9 +1010,9 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
/*
* Third, the /proc aux vectors set up by elfexec() point to
- * brand emulation library and it's linker. Copy these to the
+ * brand emulation library and its linker. Copy these to the
* /proc brand specific aux vector, and update the regular
- * /proc aux vectors to point to the executable (and it's
+ * /proc aux vectors to point to the executable (and its
* linker). This will enable debuggers to access the
* executable via the usual /proc or elf notes aux vectors.
*
@@ -1079,55 +1154,31 @@ brand_solaris_freelwp(klwp_t *l, struct brand *pbrand)
}
/*ARGSUSED*/
-int
+void
brand_solaris_initlwp(klwp_t *l, struct brand *pbrand)
{
ASSERT(l->lwp_procp->p_brand == pbrand);
ASSERT(l->lwp_procp->p_brand_data != NULL);
ASSERT(l->lwp_brand == NULL);
l->lwp_brand = (void *)-1;
- return (0);
}
/*ARGSUSED*/
void
brand_solaris_lwpexit(klwp_t *l, struct brand *pbrand)
{
- proc_t *p = l->lwp_procp;
-
ASSERT(l->lwp_procp->p_brand == pbrand);
ASSERT(l->lwp_procp->p_brand_data != NULL);
ASSERT(l->lwp_brand != NULL);
-
- /*
- * We should never be called for the last thread in a process.
- * (That case is handled by brand_solaris_proc_exit().)
- * Therefore this lwp must be exiting from a multi-threaded
- * process.
- */
- ASSERT(p->p_tlist != p->p_tlist->t_forw);
-
- l->lwp_brand = NULL;
}
/*ARGSUSED*/
void
-brand_solaris_proc_exit(struct proc *p, klwp_t *l, struct brand *pbrand)
+brand_solaris_proc_exit(struct proc *p, struct brand *pbrand)
{
ASSERT(p->p_brand == pbrand);
ASSERT(p->p_brand_data != NULL);
- /*
- * When called from proc_exit(), we know that process is
- * single-threaded and free our lwp brand data.
- * otherwise just free p_brand_data and return.
- */
- if (l != NULL) {
- ASSERT(p->p_tlist == p->p_tlist->t_forw);
- ASSERT(p->p_tlist->t_lwp == l);
- (void) brand_solaris_freelwp(l, pbrand);
- }
-
/* upon exit, free our proc brand data */
kmem_free(p->p_brand_data, sizeof (brand_proc_data_t));
p->p_brand_data = NULL;
@@ -1146,5 +1197,4 @@ brand_solaris_setbrand(proc_t *p, struct brand *pbrand)
ASSERT(p->p_tlist == p->p_tlist->t_forw);
p->p_brand_data = kmem_zalloc(sizeof (brand_proc_data_t), KM_SLEEP);
- (void) brand_solaris_initlwp(p->p_tlist->t_lwp, pbrand);
}
diff --git a/usr/src/uts/common/os/contract.c b/usr/src/uts/common/os/contract.c
index 9e498dc1c7..e4b1db84e1 100644
--- a/usr/src/uts/common/os/contract.c
+++ b/usr/src/uts/common/os/contract.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
/*
* Copyright (c) 2017 by Delphix. All rights reserved.
@@ -290,7 +291,10 @@ contract_ctor(contract_t *ct, ct_type_t *type, ct_template_t *tmpl, void *data,
avl_index_t where;
klwp_t *curlwp = ttolwp(curthread);
- ASSERT(author == curproc);
+ /*
+ * It's possible that author is not curproc if the zone is creating
+ * a new process as a child of zsched.
+ */
mutex_init(&ct->ct_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&ct->ct_reflock, NULL, MUTEX_DEFAULT, NULL);
diff --git a/usr/src/uts/common/os/core.c b/usr/src/uts/common/os/core.c
index d56484ac34..3df98d0f6d 100644
--- a/usr/src/uts/common/os/core.c
+++ b/usr/src/uts/common/os/core.c
@@ -125,6 +125,7 @@ remove_core_file(char *fp, enum core_types core_type)
/*
* Determine what rootvp to use.
*/
+ mutex_enter(&curproc->p_lock);
if (core_type == CORE_PROC) {
rootvp = (PTOU(curproc)->u_rdir == NULL ?
curproc->p_zone->zone_rootvp : PTOU(curproc)->u_rdir);
@@ -140,6 +141,7 @@ remove_core_file(char *fp, enum core_types core_type)
VN_HOLD(startvp);
if (rootvp != rootdir)
VN_HOLD(rootvp);
+ mutex_exit(&curproc->p_lock);
if ((error = lookuppnvp(&pn, NULL, NO_FOLLOW, &dvp, &vp, rootvp,
startvp, CRED())) != 0) {
pn_free(&pn);
diff --git a/usr/src/uts/common/os/cpu.c b/usr/src/uts/common/os/cpu.c
index 075bb6e70a..6a86dbb8cb 100644
--- a/usr/src/uts/common/os/cpu.c
+++ b/usr/src/uts/common/os/cpu.c
@@ -112,7 +112,7 @@ cpu_t *cpu_list; /* list of all CPUs */
cpu_t *clock_cpu_list; /* used by clock to walk CPUs */
cpu_t *cpu_active; /* list of active CPUs */
cpuset_t cpu_active_set; /* cached set of active CPUs */
-static cpuset_t cpu_available; /* set of available CPUs */
+cpuset_t cpu_available; /* set of available CPUs */
cpuset_t cpu_seqid_inuse; /* which cpu_seqids are in use */
cpu_t **cpu_seq; /* ptrs to CPUs, indexed by seq_id */
diff --git a/usr/src/uts/common/os/cred.c b/usr/src/uts/common/os/cred.c
index 3e1df330b7..5e909667de 100644
--- a/usr/src/uts/common/os/cred.c
+++ b/usr/src/uts/common/os/cred.c
@@ -730,6 +730,14 @@ crgetzoneid(const cred_t *cr)
cr->cr_zone->zone_id);
}
+zoneid_t
+crgetzonedid(const cred_t *cr)
+{
+ return (cr->cr_zone == NULL ?
+ (cr->cr_uid == -1 ? (zoneid_t)-1 : GLOBAL_ZONEID) :
+ cr->cr_zone->zone_did);
+}
+
projid_t
crgetprojid(const cred_t *cr)
{
diff --git a/usr/src/uts/common/os/ddi_intr_irm.c b/usr/src/uts/common/os/ddi_intr_irm.c
index 8faa8fea8c..2433c504fc 100644
--- a/usr/src/uts/common/os/ddi_intr_irm.c
+++ b/usr/src/uts/common/os/ddi_intr_irm.c
@@ -1320,7 +1320,7 @@ i_ddi_irm_notify(ddi_irm_pool_t *pool_p, ddi_irm_req_t *req_p)
/* Log callback errors */
if (ret != DDI_SUCCESS) {
- cmn_err(CE_WARN, "%s%d: failed callback (action=%d, ret=%d)\n",
+ cmn_err(CE_WARN, "!%s%d: failed callback (action=%d, ret=%d)\n",
ddi_driver_name(req_p->ireq_dip),
ddi_get_instance(req_p->ireq_dip), (int)action, ret);
}
diff --git a/usr/src/uts/common/os/dumpsubr.c b/usr/src/uts/common/os/dumpsubr.c
index 484b2042e2..868ed9e5c4 100644
--- a/usr/src/uts/common/os/dumpsubr.c
+++ b/usr/src/uts/common/os/dumpsubr.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
@@ -75,6 +75,7 @@
#include <sys/cpu.h>
#include <bzip2/bzlib.h>
+#include <crypto/chacha/chacha.h>
#define ONE_GIG (1024 * 1024 * 1024UL)
@@ -112,6 +113,8 @@ int dump_timeout = 120; /* timeout for dumping pages */
int dump_timeleft; /* portion of dump_timeout remaining */
int dump_ioerr; /* dump i/o error */
int dump_check_used; /* enable check for used pages */
+uint8_t dump_crypt_key[DUMP_CRYPT_KEYLEN]; /* dump encryption key */
+uint8_t dump_crypt_nonce[DUMP_CRYPT_NONCELEN]; /* dump nonce */
char *dump_stack_scratch; /* scratch area for saving stack summary */
/*
@@ -357,6 +360,7 @@ typedef struct dumpsync {
hrtime_t iotime; /* time spent writing nwrite bytes */
hrtime_t iowait; /* time spent waiting for output */
hrtime_t iowaitts; /* iowait timestamp */
+ hrtime_t crypt; /* time spent encrypting */
perpage_t perpage; /* metrics */
perpage_t perpagets;
int dumpcpu; /* master cpu */
@@ -435,6 +439,7 @@ typedef struct dumpbuf {
char *cur; /* dump write pointer */
char *start; /* dump buffer address */
char *end; /* dump buffer end */
+ char *scratch; /* scratch buffer */
size_t size; /* size of dumpbuf in bytes */
size_t iosize; /* best transfer size for device */
} dumpbuf_t;
@@ -493,11 +498,16 @@ dumpbuf_resize(void)
if (new_size <= old_size)
return; /* no need to reallocate buffer */
- new_buf = kmem_alloc(new_size, KM_SLEEP);
+ /*
+ * Allocate thrice the size of buffer to allow for space for the stream
+ * and its ciphertext should encryption be enabled (or become so).
+ */
+ new_buf = kmem_alloc(new_size * 3, KM_SLEEP);
dumpbuf.size = new_size;
dumpbuf.start = new_buf;
dumpbuf.end = new_buf + new_size;
- kmem_free(old_buf, old_size);
+ dumpbuf.scratch = dumpbuf.end + new_size;
+ kmem_free(old_buf, old_size * 3);
}
/*
@@ -1125,9 +1135,16 @@ dumphdr_init(void)
dumphdr->dump_pagesize = PAGESIZE;
dumphdr->dump_utsname = utsname;
(void) strcpy(dumphdr->dump_platform, platform);
+
+ /*
+ * Allocate our buffer, assuring enough room for encryption
+ * should it become configured.
+ */
dumpbuf.size = dumpbuf_iosize(maxphys);
- dumpbuf.start = kmem_alloc(dumpbuf.size, KM_SLEEP);
+ dumpbuf.start = kmem_alloc(dumpbuf.size * 3, KM_SLEEP);
dumpbuf.end = dumpbuf.start + dumpbuf.size;
+ dumpbuf.scratch = dumpbuf.end + dumpbuf.size;
+
dumpcfg.pids = kmem_alloc(v.v_proc * sizeof (pid_t), KM_SLEEP);
dumpcfg.helpermap = kmem_zalloc(BT_SIZEOFMAP(NCPU), KM_SLEEP);
LOCK_INIT_HELD(&dumpcfg.helper_lock);
@@ -1317,6 +1334,41 @@ dumpfini(void)
dumppath = NULL;
}
+static void
+dumpvp_encrypt(size_t size)
+{
+ size_t nelems = size / sizeof (uint64_t), i;
+ uint64_t *start = (uint64_t *)dumpbuf.start;
+ uint64_t *stream = (uint64_t *)dumpbuf.end;
+ uint64_t *crypt = (uint64_t *)dumpbuf.scratch;
+ uint64_t ctr = dumpbuf.vp_off >> DUMP_CRYPT_BLOCKSHIFT;
+ hrtime_t ts = gethrtime();
+ offset_t dumpoff = dumpbuf.vp_off;
+ chacha_ctx_t ctx;
+
+ /*
+ * Our size should be 64-bit aligned and our offset must be aligned
+ * to our crypto blocksize.
+ */
+ ASSERT(!(size & (sizeof (uint64_t) - 1)));
+ ASSERT(!(dumpbuf.vp_off & ((1 << DUMP_CRYPT_BLOCKSHIFT) - 1)));
+
+ chacha_keysetup(&ctx, dump_crypt_key, DUMP_CRYPT_KEYLEN * 8, 0);
+ chacha_ivsetup(&ctx, dump_crypt_nonce, (uint8_t *)&ctr);
+
+ for (i = 0; i < nelems; i++) {
+ stream[i] = dumpoff;
+ dumpoff += sizeof (uint64_t);
+ }
+
+ chacha_encrypt_bytes(&ctx, (uint8_t *)stream, (uint8_t *)crypt, size);
+
+ for (i = 0; i < nelems; i++)
+ start[i] ^= crypt[i];
+
+ dumpsync.crypt += gethrtime() - ts;
+}
+
static offset_t
dumpvp_flush(void)
{
@@ -1328,6 +1380,17 @@ dumpvp_flush(void)
dump_ioerr = ENOSPC;
dumpbuf.vp_off = dumpbuf.vp_limit;
} else if (size != 0) {
+ /*
+ * If our dump is encrypted and this is neither the initial
+ * dump header nor the terminal dump header and metrics,
+ * encrypt the buffer before writing it.
+ */
+ if ((dump_conflags & DUMP_ENCRYPT) &&
+ dumpbuf.vp_off > dumphdr->dump_start &&
+ dumpbuf.vp_off < dumpbuf.vp_limit - DUMP_OFFSET) {
+ dumpvp_encrypt(size);
+ }
+
iotime = gethrtime();
dumpsync.iowait += iotime - dumpsync.iowaitts;
if (panicstr)
@@ -2618,6 +2681,7 @@ dumpsys_metrics(dumpsync_t *ds, char *buf, size_t size)
P("Dump I/O rate MBS,%d.%02d\n", iorate / 100, iorate % 100);
P("..total bytes,%lld\n", (u_longlong_t)ds->nwrite);
P("..total nsec,%lld\n", (u_longlong_t)ds->iotime);
+ P("..crypt nsec,%lld\n", (u_longlong_t)ds->crypt);
P("dumpbuf.iosize,%ld\n", dumpbuf.iosize);
P("dumpbuf.size,%ld\n", dumpbuf.size);
@@ -2658,6 +2722,29 @@ dumpsys_metrics(dumpsync_t *ds, char *buf, size_t size)
}
#endif /* COLLECT_METRICS */
+CTASSERT(DUMP_CRYPT_HMACLEN <= sizeof (struct utsname));
+
+/*
+ * Mark the dump as encrypted and calculate our (crude) HMAC based on the
+ * dump_utsname. (The purpose of the HMAC is to merely allow for incorrect
+ * keys to be quickly rejected.)
+ */
+void
+dumpsys_crypt(dumphdr_t *dumphdr, dump_crypt_t *dcrypt)
+{
+ chacha_ctx_t ctx;
+
+ dumphdr->dump_flags |= DF_ENCRYPTED;
+ bcopy(dump_crypt_nonce, dcrypt->dump_crypt_nonce, DUMP_CRYPT_NONCELEN);
+ dcrypt->dump_crypt_algo = DUMP_CRYPT_ALGO_CHACHA20;
+
+ chacha_keysetup(&ctx, dump_crypt_key, DUMP_CRYPT_KEYLEN * 8, 0);
+ chacha_ivsetup(&ctx, dump_crypt_nonce, NULL);
+
+ chacha_encrypt_bytes(&ctx, (uint8_t *)&dumphdr->dump_utsname,
+ (uint8_t *)&dcrypt->dump_crypt_hmac, DUMP_CRYPT_HMACLEN);
+}
+
/*
* Dump the system.
*/
@@ -2679,6 +2766,7 @@ dumpsys(void)
dumpmlw_t mlw;
dumpcsize_t datatag;
dumpdatahdr_t datahdr;
+ dump_crypt_t dcrypt;
if (dumpvp == NULL || dumphdr == NULL) {
uprintf("skipping system dump - no dump device configured\n");
@@ -2733,6 +2821,9 @@ dumpsys(void)
/* Make sure nodename is current */
bcopy(utsname.nodename, dumphdr->dump_utsname.nodename, SYS_NMLN);
+ if (dump_conflags & DUMP_ENCRYPT)
+ dumpsys_crypt(dumphdr, &dcrypt);
+
/*
* If this is a live dump, try to open a VCHR vnode for better
* performance. We must take care to flush the buffer cache
@@ -2999,11 +3090,19 @@ dumpsys(void)
*/
dumpbuf.vp_off = dumphdr->dump_start;
dumpvp_write(dumphdr, sizeof (dumphdr_t));
+
+ if (dump_conflags & DUMP_ENCRYPT)
+ dumpvp_write(&dcrypt, sizeof (dump_crypt_t));
+
(void) dumpvp_flush();
dumpbuf.vp_limit = dumpvp_size;
dumpbuf.vp_off = dumpbuf.vp_limit - DUMP_OFFSET;
dumpvp_write(dumphdr, sizeof (dumphdr_t));
+
+ if (dump_conflags & DUMP_ENCRYPT)
+ dumpvp_write(&dcrypt, sizeof (dump_crypt_t));
+
dumpvp_write(&datahdr, sizeof (dumpdatahdr_t));
dumpvp_write(dumpcfg.cbuf[0].buf, datahdr.dump_metrics);
diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c
index d663f27ca0..0f9e4ea6dd 100644
--- a/usr/src/uts/common/os/exec.c
+++ b/usr/src/uts/common/os/exec.c
@@ -24,7 +24,7 @@
*/
/* Copyright (c) 1988 AT&T */
-/* All Rights Reserved */
+/* All Rights Reserved */
/*
* Copyright 2019 Joyent, Inc.
* Copyright 2022 Oxide Computer Company
@@ -102,6 +102,7 @@ uint_t auxv_hwcap32_3 = 0; /* 32-bit version of auxv_hwcap3 */
#endif
#define PSUIDFLAGS (SNOCD|SUGID)
+#define RANDOM_LEN 16 /* 16 bytes for AT_RANDOM aux entry */
/*
* These are consumed within the specific exec modules, but are defined here
@@ -268,8 +269,10 @@ exec_common(const char *fname, const char **argp, const char **envp,
* only if the pathname does not contain a "/" the resolved path
* points to a file in the current working (attribute) directory.
*/
- if ((p->p_user.u_cdir->v_flag & V_XATTRDIR) != 0 &&
+ mutex_enter(&p->p_lock);
+ if ((PTOU(p)->u_cdir->v_flag & V_XATTRDIR) != 0 &&
strchr(resolvepn.pn_path, '/') == NULL) {
+ mutex_exit(&p->p_lock);
if (dir != NULL)
VN_RELE(dir);
error = EACCES;
@@ -278,6 +281,7 @@ exec_common(const char *fname, const char **argp, const char **envp,
VN_RELE(vp);
goto out;
}
+ mutex_exit(&p->p_lock);
bzero(exec_file, MAXCOMLEN+1);
(void) strncpy(exec_file, pn.pn_path, MAXCOMLEN);
@@ -325,14 +329,43 @@ exec_common(const char *fname, const char **argp, const char **envp,
ua.argp = argp;
ua.envp = envp;
- /* If necessary, brand this process before we start the exec. */
- if (brandme)
- brand_setbrand(p);
+ /* If necessary, brand this process/lwp before we start the exec. */
+ if (brandme) {
+ void *brand_data = NULL;
+
+ /*
+ * Process branding may fail if multiple LWPs are present and
+ * holdlwps() cannot complete successfully.
+ */
+ error = brand_setbrand(p, B_TRUE);
+
+ if (error == 0 && BROP(p)->b_lwpdata_alloc != NULL) {
+ brand_data = BROP(p)->b_lwpdata_alloc(p);
+ if (brand_data == NULL) {
+ error = 1;
+ }
+ }
+
+ if (error == 0) {
+ mutex_enter(&p->p_lock);
+ BROP(p)->b_initlwp(lwp, brand_data);
+ mutex_exit(&p->p_lock);
+ } else {
+ VN_RELE(vp);
+ if (dir != NULL) {
+ VN_RELE(dir);
+ }
+ pn_free(&resolvepn);
+ goto fail;
+ }
+ }
if ((error = gexec(&vp, &ua, &args, NULL, 0, &execsz,
- exec_file, p->p_cred, brand_action)) != 0) {
- if (brandme)
- brand_clearbrand(p, B_FALSE);
+ exec_file, p->p_cred, &brand_action)) != 0) {
+ if (brandme) {
+ BROP(p)->b_freelwp(lwp);
+ brand_clearbrand(p, B_TRUE);
+ }
VN_RELE(vp);
if (dir != NULL)
VN_RELE(dir);
@@ -364,7 +397,7 @@ exec_common(const char *fname, const char **argp, const char **envp,
/*
* Clear contract template state
*/
- lwp_ctmpl_clear(lwp);
+ lwp_ctmpl_clear(lwp, B_TRUE);
/*
* Save the directory in which we found the executable for expanding
@@ -388,6 +421,8 @@ exec_common(const char *fname, const char **argp, const char **envp,
* pending held signals remain held, so don't clear t_hold.
*/
mutex_enter(&p->p_lock);
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, 0);
lwp->lwp_oldcontext = 0;
lwp->lwp_ustack = 0;
lwp->lwp_old_stk_ctl = 0;
@@ -447,8 +482,10 @@ exec_common(const char *fname, const char **argp, const char **envp,
TRACE_2(TR_FAC_PROC, TR_PROC_EXEC, "proc_exec:p %p up %p", p, up);
/* Unbrand ourself if necessary. */
- if (PROC_IS_BRANDED(p) && (brand_action == EBA_NATIVE))
+ if (PROC_IS_BRANDED(p) && (brand_action == EBA_NATIVE)) {
+ BROP(p)->b_freelwp(lwp);
brand_clearbrand(p, B_FALSE);
+ }
setregs(&args);
@@ -572,7 +609,7 @@ gexec(
size_t *execsz,
caddr_t exec_file,
struct cred *cred,
- int brand_action)
+ int *brand_action)
{
struct vnode *vp, *execvp = NULL;
proc_t *pp = ttoproc(curthread);
@@ -893,8 +930,14 @@ gexec(
if (pp->p_plist || (pp->p_proc_flag & P_PR_TRACE))
args->traceinval = 1;
}
- if (pp->p_proc_flag & P_PR_PTRACE)
+
+ /*
+ * If legacy ptrace is enabled, generate the SIGTRAP.
+ */
+ if (pp->p_proc_flag & P_PR_PTRACE) {
psignal(pp, SIGTRAP);
+ }
+
if (args->traceinval)
prinvalidate(&pp->p_user);
}
@@ -1558,6 +1601,27 @@ stk_add(uarg_t *args, const char *sp, enum uio_seg segflg)
return (0);
}
+/*
+ * Add a fixed size byte array to the stack (only from kernel space).
+ */
+static int
+stk_byte_add(uarg_t *args, const uint8_t *sp, size_t len)
+{
+ int error;
+
+ if (STK_AVAIL(args) < sizeof (int))
+ return (E2BIG);
+ *--args->stk_offp = args->stk_strp - args->stk_base;
+
+ if (len > STK_AVAIL(args))
+ return (E2BIG);
+ bcopy(sp, args->stk_strp, len);
+
+ args->stk_strp += len;
+
+ return (0);
+}
+
static int
stk_getptr(uarg_t *args, char *src, char **dst)
{
@@ -1594,6 +1658,7 @@ stk_copyin(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
size_t size, pad;
char *argv = (char *)uap->argp;
char *envp = (char *)uap->envp;
+ uint8_t rdata[RANDOM_LEN];
/*
* Copy interpreter's name and argument to argv[0] and argv[1].
@@ -1650,7 +1715,9 @@ stk_copyin(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
}
}
argc = (int *)(args->stk_base + args->stk_size) - args->stk_offp;
- args->arglen = args->stk_strp - args->stk_base;
+ args->argstrlen = args->stk_strp - args->stk_base;
+
+ const char *envstr = args->stk_strp;
/*
* Add environ[] strings to the stack.
@@ -1672,12 +1739,15 @@ stk_copyin(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
envp += ptrsize;
}
}
+
+ args->envstrlen = args->stk_strp - envstr;
args->na = (int *)(args->stk_base + args->stk_size) - args->stk_offp;
args->ne = args->na - argc;
/*
- * Add AT_SUN_PLATFORM, AT_SUN_EXECNAME, AT_SUN_BRANDNAME, and
- * AT_SUN_EMULATOR strings to the stack.
+ * Add AT_SUN_PLATFORM, AT_SUN_EXECNAME, AT_SUN_BRANDNAME,
+ * AT_SUN_BRAND_NROOT, and AT_SUN_EMULATOR strings, as well as AT_RANDOM
+ * array, to the stack.
*/
if (auxvpp != NULL && *auxvpp != NULL) {
if ((error = stk_add(args, platform, UIO_SYSSPACE)) != 0)
@@ -1690,6 +1760,20 @@ stk_copyin(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
if (args->emulator != NULL &&
(error = stk_add(args, args->emulator, UIO_SYSSPACE)) != 0)
return (error);
+
+ /*
+ * For the AT_RANDOM aux vector we provide 16 bytes of random
+ * data.
+ */
+ (void) random_get_pseudo_bytes(rdata, sizeof (rdata));
+
+ if ((error = stk_byte_add(args, rdata, sizeof (rdata))) != 0)
+ return (error);
+
+ if (args->brand_nroot != NULL &&
+ (error = stk_add(args, args->brand_nroot,
+ UIO_SYSSPACE)) != 0)
+ return (error);
}
/*
@@ -1746,46 +1830,53 @@ stk_copyout(uarg_t *args, char *usrstack, void **auxvpp, user_t *up)
*/
if (stk_putptr(args, usp, (char *)(uintptr_t)argc))
return (-1);
+ usp += ptrsize;
/*
- * Add argc space (ptrsize) to usp and record argv for /proc.
+ * For the benefit of /proc, record the user address of the argv[] array
+ * as well as the start of the argv string space (argv[0]).
*/
- up->u_argv = (uintptr_t)(usp += ptrsize);
+ up->u_argv = (uintptr_t)usp;
+ up->u_argvstrs = (uintptr_t)(&ustrp[*(offp - 1)]);
+ up->u_argvstrsize = args->argstrlen;
/*
- * Put the argv[] pointers on the stack.
+ * Put the argv[] pointers on the stack, including a NULL terminator.
*/
for (i = 0; i < argc; i++, usp += ptrsize)
if (stk_putptr(args, usp, &ustrp[*--offp]))
return (-1);
+ usp += ptrsize;
/*
* Copy arguments to u_psargs.
*/
- pslen = MIN(args->arglen, PSARGSZ) - 1;
+ pslen = MIN(args->argstrlen, PSARGSZ) - 1;
for (i = 0; i < pslen; i++)
up->u_psargs[i] = (kstrp[i] == '\0' ? ' ' : kstrp[i]);
while (i < PSARGSZ)
up->u_psargs[i++] = '\0';
/*
- * Add space for argv[]'s NULL terminator (ptrsize) to usp and
- * record envp for /proc.
+ * For the benefit of /proc, record the user address of the envp[] array
+ * as well as the start of the envp string space (envp[0]).
*/
- up->u_envp = (uintptr_t)(usp += ptrsize);
+ up->u_envp = (uintptr_t)usp;
+ up->u_envstrs = (uintptr_t)(&ustrp[*(offp - 1)]);
+ up->u_envstrsize = args->envstrlen;
/*
- * Put the envp[] pointers on the stack.
+ * Put the envp[] pointers on the stack, including a NULL terminator.
*/
for (i = 0; i < envc; i++, usp += ptrsize)
if (stk_putptr(args, usp, &ustrp[*--offp]))
return (-1);
+ usp += ptrsize;
/*
- * Add space for envp[]'s NULL terminator (ptrsize) to usp and
- * remember where the stack ends, which is also where auxv begins.
+ * Remember where the stack ends, which is also where auxv begins.
*/
- args->stackend = usp += ptrsize;
+ args->stackend = usp;
/*
* Put all the argv[], envp[], and auxv strings on the stack.
@@ -1796,7 +1887,7 @@ stk_copyout(uarg_t *args, char *usrstack, void **auxvpp, user_t *up)
/*
* Fill in the aux vector now that we know the user stack addresses
* for the AT_SUN_PLATFORM, AT_SUN_EXECNAME, AT_SUN_BRANDNAME and
- * AT_SUN_EMULATOR strings.
+ * AT_SUN_EMULATOR strings, as well as the AT_RANDOM array.
*/
if (auxvpp != NULL && *auxvpp != NULL) {
if (args->to_model == DATAMODEL_NATIVE) {
@@ -1809,6 +1900,11 @@ stk_copyout(uarg_t *args, char *usrstack, void **auxvpp, user_t *up)
if (args->emulator != NULL)
ADDAUX(*a,
AT_SUN_EMULATOR, (long)&ustrp[*--offp])
+ ADDAUX(*a, AT_RANDOM, (long)&ustrp[*--offp])
+ if (args->brand_nroot != NULL) {
+ ADDAUX(*a,
+ AT_SUN_BRAND_NROOT, (long)&ustrp[*--offp])
+ }
} else {
auxv32_t **a = (auxv32_t **)auxvpp;
ADDAUX(*a,
@@ -1821,6 +1917,11 @@ stk_copyout(uarg_t *args, char *usrstack, void **auxvpp, user_t *up)
if (args->emulator != NULL)
ADDAUX(*a, AT_SUN_EMULATOR,
(int)(uintptr_t)&ustrp[*--offp])
+ ADDAUX(*a, AT_RANDOM, (int)(uintptr_t)&ustrp[*--offp])
+ if (args->brand_nroot != NULL) {
+ ADDAUX(*a, AT_SUN_BRAND_NROOT,
+ (int)(uintptr_t)&ustrp[*--offp])
+ }
}
}
@@ -1964,6 +2065,9 @@ exec_args(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
usrstack = (char *)USRSTACK32;
}
+ if (args->maxstack != 0 && (uintptr_t)usrstack > args->maxstack)
+ usrstack = (char *)args->maxstack;
+
ASSERT(P2PHASE((uintptr_t)usrstack, args->stk_align) == 0);
#if defined(__sparc)
@@ -2059,7 +2163,7 @@ exec_args(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
delete_itimer_realprof();
if (AU_AUDITING())
- audit_exec(args->stk_base, args->stk_base + args->arglen,
+ audit_exec(args->stk_base, args->stk_base + args->argstrlen,
args->na - args->ne, args->ne, args->pfcred);
/*
diff --git a/usr/src/uts/common/os/exit.c b/usr/src/uts/common/os/exit.c
index 5a9355ae9f..7ccf9b3221 100644
--- a/usr/src/uts/common/os/exit.c
+++ b/usr/src/uts/common/os/exit.c
@@ -141,11 +141,32 @@ rexit(int rval)
}
/*
+ * Bump the init_restarts kstat and let interested parties know about the
+ * restart.
+ */
+static void
+restart_init_notify(zone_t *zone)
+{
+ nvlist_t *nvl = NULL;
+
+ zone->zone_proc_init_restarts++;
+
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0 &&
+ nvlist_add_uint32(nvl, ZONE_CB_RESTARTS,
+ zone->zone_proc_init_restarts) == 0) {
+ zone_sysevent_publish(zone, ZONE_EVENT_INIT_CLASS,
+ ZONE_EVENT_INIT_RESTART_SC, nvl);
+ }
+
+ nvlist_free(nvl);
+}
+
+/*
* Called by proc_exit() when a zone's init exits, presumably because
* it failed. As long as the given zone is still in the "running"
* state, we will re-exec() init, but first we need to reset things
* which are usually inherited across exec() but will break init's
- * assumption that it is being exec()'d from a virgin process. Most
+ * assumption that it is being exec()'d from a virgin process. Most
* importantly this includes closing all file descriptors (exec only
* closes those marked close-on-exec) and resetting signals (exec only
* resets handled signals, and we need to clear any signals which
@@ -234,7 +255,7 @@ restart_init(int what, int why)
siginfofree(lwp->lwp_curinfo);
lwp->lwp_curinfo = NULL;
}
- lwp_ctmpl_clear(lwp);
+ lwp_ctmpl_clear(lwp, B_FALSE);
/*
* Reset both the process root directory and the current working
@@ -286,6 +307,8 @@ restart_init(int what, int why)
ASSERT(p == curproc);
(void) freectty(B_TRUE);
+ restart_init_notify(p->p_zone);
+
/*
* Now exec() the new init(8) on top of the current process. If we
* succeed, the caller will treat this like a successful system call.
@@ -320,7 +343,7 @@ exit(int why, int what)
/*
* If proc_exit() fails, then some other lwp in the process
* got there first. We just have to call lwp_exit() to allow
- * the other lwp to finish exiting the process. Otherwise we're
+ * the other lwp to finish exiting the process. Otherwise we're
* restarting init, and should return.
*/
if (proc_exit(why, what) != 0) {
@@ -333,7 +356,7 @@ exit(int why, int what)
/*
* Set the SEXITING flag on the process, after making sure /proc does
- * not have it locked. This is done in more places than proc_exit(),
+ * not have it locked. This is done in more places than proc_exit(),
* so it is a separate function.
*/
void
@@ -380,8 +403,9 @@ zone_init_exit(zone_t *z, int why, int what)
*/
if (!z->zone_restart_init) {
/*
- * The zone has been set up to halt when init exits.
+ * The zone has been setup to halt when init exits.
*/
+ z->zone_init_status = wstat(why, what);
(void) zone_kadmin(A_SHUTDOWN, AD_HALT, NULL, zone_kcred());
z->zone_proc_initpid = -1;
return (B_FALSE);
@@ -421,6 +445,7 @@ zone_init_exit(zone_t *z, int why, int what)
(void) zone_kadmin(A_REBOOT, 0, NULL, zone_kcred());
}
+ z->zone_init_status = wstat(why, what);
z->zone_proc_initpid = -1;
return (B_FALSE);
}
@@ -441,14 +466,16 @@ zone_init_exit(zone_t *z, int why, int what)
/*
* No restart modifiers on the zone, attempt to restart init.
*/
- if (restart_init(what, why) == 0)
+ if (restart_init(what, why) == 0) {
return (B_TRUE);
+ }
}
/*
* The restart failed, or the criteria for a restart are not met;
* the zone will shut down.
*/
+ z->zone_init_status = wstat(why, what);
(void) zone_kadmin(A_SHUTDOWN, AD_HALT, NULL, zone_kcred());
z->zone_proc_initpid = -1;
return (B_FALSE);
@@ -483,7 +510,7 @@ proc_exit(int why, int what)
/*
* Stop and discard the process's lwps except for the current one,
- * unless some other lwp beat us to it. If exitlwps() fails then
+ * unless some other lwp beat us to it. If exitlwps() fails then
* return and the calling lwp will call (or continue in) lwp_exit().
*/
proc_is_exiting(p);
@@ -501,19 +528,6 @@ proc_exit(int why, int what)
}
mutex_exit(&p->p_lock);
- DTRACE_PROC(lwp__exit);
- DTRACE_PROC1(exit, int, why);
-
- /*
- * Will perform any brand specific proc exit processing, since this
- * is always the last lwp, will also perform lwp_exit and free brand
- * data
- */
- if (PROC_IS_BRANDED(p)) {
- lwp_detach_brand_hdlrs(lwp);
- brand_clearbrand(p, B_FALSE);
- }
-
/*
* Don't let init exit unless zone_start_init() failed its exec, or
* we are shutting down the zone or the machine.
@@ -527,6 +541,32 @@ proc_exit(int why, int what)
return (0);
}
+ /*
+ * Delay firing probes (and performing brand cleanup) until after the
+ * zone_proc_initpid check. Cases which result in zone shutdown or
+ * restart via zone_kadmin eventually result in a call back to
+ * proc_exit.
+ */
+ DTRACE_PROC(lwp__exit);
+ DTRACE_PROC1(exit, int, why);
+
+ /*
+ * Will perform any brand specific proc exit processing. Since this
+ * is always the last lwp, will also perform lwp exit/free and proc
+ * exit. Brand data will be freed when the process is reaped.
+ */
+ if (PROC_IS_BRANDED(p)) {
+ BROP(p)->b_lwpexit(lwp);
+ BROP(p)->b_proc_exit(p);
+ /*
+ * To ensure that b_proc_exit has access to brand-specific data
+ * contained by the one remaining lwp, call the freelwp hook as
+ * the last part of this clean-up process.
+ */
+ BROP(p)->b_freelwp(lwp);
+ lwp_detach_brand_hdlrs(lwp);
+ }
+
lwp_pcb_exit();
/*
@@ -693,7 +733,7 @@ proc_exit(int why, int what)
semexit(p);
rv = wstat(why, what);
- acct(rv & 0xff);
+ acct(rv);
exacct_commit_proc(p, rv);
/*
@@ -786,10 +826,22 @@ proc_exit(int why, int what)
if ((q = p->p_child) != NULL && p != proc_init) {
struct proc *np;
struct proc *initp = proc_init;
+ pid_t zone_initpid = 1;
+ struct proc *zoneinitp = NULL;
boolean_t setzonetop = B_FALSE;
- if (!INGLOBALZONE(curproc))
- setzonetop = B_TRUE;
+ if (!INGLOBALZONE(curproc)) {
+ zone_initpid = curproc->p_zone->zone_proc_initpid;
+
+ ASSERT(MUTEX_HELD(&pidlock));
+ zoneinitp = prfind(zone_initpid);
+ if (zoneinitp != NULL) {
+ initp = zoneinitp;
+ } else {
+ zone_initpid = 1;
+ setzonetop = B_TRUE;
+ }
+ }
pgdetach(p);
@@ -801,7 +853,8 @@ proc_exit(int why, int what)
*/
delete_ns(q->p_parent, q);
- q->p_ppid = 1;
+ q->p_ppid = zone_initpid;
+
q->p_pidflag &= ~(CLDNOSIGCHLD | CLDWAITPID);
if (setzonetop) {
mutex_enter(&q->p_lock);
@@ -959,7 +1012,7 @@ proc_exit(int why, int what)
* curthread's proc pointer is changed to point to the 'sched'
* process for the corresponding zone, except in the case when
* the exiting process is in fact a zsched instance, in which
- * case the proc pointer is set to p0. We do so, so that the
+ * case the proc pointer is set to p0. We do so, so that the
* process still points at the right zone when we call the VN_RELE()
* below.
*
@@ -975,8 +1028,50 @@ proc_exit(int why, int what)
mutex_exit(&p->p_lock);
if (!evaporate) {
- p->p_pidflag &= ~CLDPEND;
- sigcld(p, sqp);
+ /*
+ * The brand specific code only happens when the brand has a
+ * function to call in place of sigcld and the parent of the
+ * exiting process is not the global zone init. If the parent
+ * is the global zone init, then the process was reparented,
+ * and we don't want brand code delivering possibly strange
+ * signals to init. Also, init is not branded, so any brand
+ * specific exit data will not be picked up by init anyway.
+ */
+ if (PROC_IS_BRANDED(p) &&
+ BROP(p)->b_exit_with_sig != NULL &&
+ p->p_ppid != 1) {
+ /*
+ * The code for _fini that could unload the brand_t
+ * blocks until the count of zones using the module
+ * reaches zero. Zones decrement the refcount on their
+ * brands only after all user tasks in that zone have
+ * exited and been waited on. The decrement on the
+ * brand's refcount happen in zone_destroy(). That
+ * depends on zone_shutdown() having been completed.
+ * zone_shutdown() includes a call to zone_empty(),
+ * where the zone waits for itself to reach the state
+ * ZONE_IS_EMPTY. This state is only set in either
+ * zone_shutdown(), when there are no user processes as
+ * the zone enters this function, or in
+ * zone_task_rele(). zone_task_rele() is called from
+ * code triggered by waiting on processes, not by the
+ * processes exiting through proc_exit(). This means
+ * all the branded processes that could exist for a
+ * specific brand_t must exit and get reaped before the
+ * refcount on the brand_t can reach 0. _fini will
+ * never unload the corresponding brand module before
+ * proc_exit finishes execution for all processes
+ * branded with a particular brand_t, which makes the
+ * operation below safe to do. Brands that wish to use
+ * this mechanism must wait in _fini as described
+ * above.
+ */
+ BROP(p)->b_exit_with_sig(p, sqp);
+ } else {
+ p->p_pidflag &= ~CLDPEND;
+ sigcld(p, sqp);
+ }
+
} else {
/*
* Do what sigcld() would do if the disposition
@@ -1001,7 +1096,7 @@ proc_exit(int why, int what)
/*
* task_rele() may ultimately cause the zone to go away (or
* may cause the last user process in a zone to go away, which
- * signals zsched to go away). So prior to this call, we must
+ * signals zsched to go away). So prior to this call, we must
* no longer point at zsched.
*/
t->t_procp = &p0;
@@ -1055,10 +1150,9 @@ winfo(proc_t *pp, k_siginfo_t *ip, int waitflag)
int
waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
{
- int found;
proc_t *cp, *pp;
- int proc_gone;
int waitflag = !(options & WNOWAIT);
+ boolean_t have_brand_helper = B_FALSE;
/*
* Obsolete flag, defined here only for binary compatibility
@@ -1086,7 +1180,8 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
pp = ttoproc(curthread);
/*
- * lock parent mutex so that sibling chain can be searched.
+ * Anytime you are looking for a process, you take pidlock to prevent
+ * things from changing as you look.
*/
mutex_enter(&pidlock);
@@ -1106,10 +1201,37 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
return (ECHILD);
}
- while (pp->p_child != NULL) {
+ if (PROC_IS_BRANDED(pp) && BROP(pp)->b_waitid_helper != NULL) {
+ have_brand_helper = B_TRUE;
+ }
+
+ while (pp->p_child != NULL || have_brand_helper) {
+ boolean_t brand_wants_wait = B_FALSE;
+ int proc_gone = 0;
+ int found = 0;
- proc_gone = 0;
+ /*
+ * Give the brand a chance to return synthetic results from
+ * this waitid() call before we do the real thing.
+ */
+ if (have_brand_helper) {
+ int ret;
+ if (BROP(pp)->b_waitid_helper(idtype, id, ip, options,
+ &brand_wants_wait, &ret) == 0) {
+ mutex_exit(&pidlock);
+ return (ret);
+ }
+
+ if (pp->p_child == NULL) {
+ goto no_real_children;
+ }
+ }
+
+ /*
+ * Look for interesting children in the newstate list.
+ */
+ VERIFY(pp->p_child != NULL);
for (cp = pp->p_child_ns; cp != NULL; cp = cp->p_sibling_ns) {
if (idtype != P_PID && (cp->p_pidflag & CLDWAITPID))
continue;
@@ -1117,6 +1239,11 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
continue;
if (idtype == P_PGID && id != cp->p_pgrp)
continue;
+ if (PROC_IS_BRANDED(pp)) {
+ if (BROP(pp)->b_wait_filter != NULL &&
+ BROP(pp)->b_wait_filter(pp, cp) == B_FALSE)
+ continue;
+ }
switch (cp->p_wcode) {
@@ -1161,12 +1288,16 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
* Wow! None of the threads on the p_sibling_ns list were
* interesting threads. Check all the kids!
*/
- found = 0;
for (cp = pp->p_child; cp != NULL; cp = cp->p_sibling) {
if (idtype == P_PID && id != cp->p_pid)
continue;
if (idtype == P_PGID && id != cp->p_pgrp)
continue;
+ if (PROC_IS_BRANDED(pp)) {
+ if (BROP(pp)->b_wait_filter != NULL &&
+ BROP(pp)->b_wait_filter(pp, cp) == B_FALSE)
+ continue;
+ }
switch (cp->p_wcode) {
case CLD_TRAPPED:
@@ -1235,11 +1366,12 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
break;
}
+no_real_children:
/*
* If we found no interesting processes at all,
* break out and return ECHILD.
*/
- if (found + proc_gone == 0)
+ if (!brand_wants_wait && (found + proc_gone == 0))
break;
if (options & WNOHANG) {
@@ -1258,7 +1390,7 @@ waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
* change state while we wait, we don't wait at all.
* Get out with ECHILD according to SVID.
*/
- if (found == proc_gone)
+ if (!brand_wants_wait && (found == proc_gone))
break;
if (!cv_wait_sig_swap(&pp->p_cv, &pidlock)) {
@@ -1354,6 +1486,12 @@ freeproc(proc_t *p)
p->p_killsqp = NULL;
}
+ /* Clear any remaining brand data */
+ if (PROC_IS_BRANDED(p)) {
+ brand_clearbrand(p, B_FALSE);
+ }
+
+
prfree(p); /* inform /proc */
/*
diff --git a/usr/src/uts/common/os/fio.c b/usr/src/uts/common/os/fio.c
index c25564d85f..f6179cf301 100644
--- a/usr/src/uts/common/os/fio.c
+++ b/usr/src/uts/common/os/fio.c
@@ -21,7 +21,8 @@
/*
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015, Joyent Inc.
+ * Copyright 2017, Joyent Inc.
+ * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -488,7 +489,7 @@ free_afd(afd_t *afd) /* called below and from thread_free() */
afd->a_fd[i] = -1;
}
-static void
+void
set_active_fd(int fd)
{
afd_t *afd = &curthread->t_activefd;
@@ -958,7 +959,22 @@ closef(file_t *fp)
vp = fp->f_vnode;
- error = VOP_CLOSE(vp, flag, count, offset, fp->f_cred, NULL);
+ /*
+ * The __FLXPATH flag is a private interface for use by the lx
+ * brand in order to emulate open(O_NOFOLLOW|O_PATH) which,
+ * when a symbolic link is encountered, returns a file
+ * descriptor which references it.
+ * See uts/common/brand/lx/syscall/lx_open.c
+ *
+ * When this flag is set, VOP_OPEN() will not have been called when
+ * this file descriptor was opened, and VOP_CLOSE() should not be
+ * called here (for a symlink, most filesystems would return ENOSYS
+ * anyway)
+ */
+ if (fp->f_flag2 & (__FLXPATH >> 16))
+ error = 0;
+ else
+ error = VOP_CLOSE(vp, flag, count, offset, fp->f_cred, NULL);
if (count > 1) {
mutex_exit(&fp->f_tlock);
@@ -1118,7 +1134,7 @@ falloc(vnode_t *vp, int flag, file_t **fpp, int *fdp)
mutex_enter(&fp->f_tlock);
fp->f_count = 1;
fp->f_flag = (ushort_t)flag;
- fp->f_flag2 = (flag & (FSEARCH|FEXEC)) >> 16;
+ fp->f_flag2 = (flag & (FSEARCH|FEXEC|__FLXPATH)) >> 16;
fp->f_vnode = vp;
fp->f_offset = 0;
fp->f_audit_data = 0;
diff --git a/usr/src/uts/common/os/fork.c b/usr/src/uts/common/os/fork.c
index 1caa0b9b7b..183e1f4333 100644
--- a/usr/src/uts/common/os/fork.c
+++ b/usr/src/uts/common/os/fork.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -84,6 +84,7 @@ static int64_t cfork(int, int, int);
static int getproc(proc_t **, pid_t, uint_t);
#define GETPROC_USER 0x0
#define GETPROC_KERNEL 0x1
+#define GETPROC_ZSCHED 0x2
static void fork_fail(proc_t *);
static void forklwp_fail(proc_t *);
@@ -706,7 +707,7 @@ fork_fail(proc_t *cp)
if (PTOU(curproc)->u_cwd)
refstr_rele(PTOU(curproc)->u_cwd);
if (PROC_IS_BRANDED(cp)) {
- brand_clearbrand(cp, B_TRUE);
+ brand_clearbrand(cp, B_FALSE);
}
}
@@ -755,7 +756,7 @@ forklwp_fail(proc_t *p)
kmem_free(t->t_door, sizeof (door_data_t));
t->t_door = NULL;
}
- lwp_ctmpl_clear(ttolwp(t));
+ lwp_ctmpl_clear(ttolwp(t), B_FALSE);
/*
* Remove the thread from the all threads list.
@@ -792,6 +793,9 @@ extern struct as kas;
/*
* fork a kernel process.
+ *
+ * Passing a pid argument of -1 indicates that the new process should be
+ * launched as a child of 'zsched' within the zone.
*/
int
newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
@@ -810,6 +814,7 @@ newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
rctl_set_t *init_set;
ASSERT(pid != 1);
+ ASSERT(pid >= 0);
if (getproc(&p, pid, GETPROC_KERNEL) < 0)
return (EAGAIN);
@@ -853,8 +858,18 @@ newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
rctl_set_t *init_set;
task_t *tk, *tk_old;
klwp_t *lwp;
+ boolean_t pzsched = B_FALSE;
+ int flag = GETPROC_USER;
+
+ /* Handle a new user-level thread as child of zsched. */
+ if (pid < 0) {
+ VERIFY(curzone != global_zone);
+ flag = GETPROC_ZSCHED;
+ pzsched = B_TRUE;
+ pid = 0;
+ }
- if (getproc(&p, pid, GETPROC_USER) < 0)
+ if (getproc(&p, pid, flag) < 0)
return (EAGAIN);
/*
* init creates a new task, distinct from the task
@@ -915,7 +930,8 @@ newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
}
t = lwptot(lwp);
- ctp = contract_process_fork(sys_process_tmpl, p, curproc,
+ ctp = contract_process_fork(sys_process_tmpl, p,
+ (pzsched ? curproc->p_zone->zone_zsched : curproc),
B_FALSE);
ASSERT(ctp != NULL);
if (ct != NULL)
@@ -956,7 +972,11 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)
return (-1); /* no point in starting new processes */
- pp = (flags & GETPROC_KERNEL) ? &p0 : curproc;
+ if (flags & GETPROC_ZSCHED) {
+ pp = curproc->p_zone->zone_zsched;
+ } else {
+ pp = (flags & GETPROC_KERNEL) ? &p0 : curproc;
+ }
task = pp->p_task;
proj = task->tk_proj;
zone = pp->p_zone;
@@ -1017,6 +1037,9 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
cp->p_t1_lgrpid = LGRP_NONE;
cp->p_tr_lgrpid = LGRP_NONE;
+ /* Default to native brand initially */
+ cp->p_brand = &native_brand;
+
if ((newpid = pid_allocate(cp, pid, PID_ALLOC_PROC)) == -1) {
if (nproc == v.v_proc) {
CPU_STATS_ADDQ(CPU, sys, procovf, 1);
@@ -1084,9 +1107,6 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
cp->p_flag = pp->p_flag & (SJCTL|SNOWAIT|SNOCD);
cp->p_sessp = pp->p_sessp;
sess_hold(pp);
- cp->p_brand = pp->p_brand;
- if (PROC_IS_BRANDED(pp))
- BROP(pp)->b_copy_procdata(cp, pp);
cp->p_bssbase = pp->p_bssbase;
cp->p_brkbase = pp->p_brkbase;
cp->p_brksize = pp->p_brksize;
@@ -1171,6 +1191,18 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
mutex_exit(&cp->p_lock);
mutex_exit(&pidlock);
+ if (PROC_IS_BRANDED(pp)) {
+ /*
+ * The only reason why process branding should fail is when
+ * the procedure is complicated by multiple LWPs on the scene.
+ * With an LWP count of 0, this newly allocated process has no
+ * reason to fail branding.
+ */
+ VERIFY0(brand_setbrand(cp, B_FALSE));
+
+ BROP(pp)->b_copy_procdata(cp, pp);
+ }
+
avl_create(&cp->p_ct_held, contract_compar, sizeof (contract_t),
offsetof(contract_t, ct_ctlist));
@@ -1188,6 +1220,7 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
*/
fcnt_add(P_FINFO(pp), 1);
+ mutex_enter(&pp->p_lock);
if (PTOU(pp)->u_cdir) {
VN_HOLD(PTOU(pp)->u_cdir);
} else {
@@ -1201,6 +1234,7 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
VN_HOLD(PTOU(pp)->u_rdir);
if (PTOU(pp)->u_cwd)
refstr_hold(PTOU(pp)->u_cwd);
+ mutex_exit(&pp->p_lock);
/*
* copy the parent's uarea.
diff --git a/usr/src/uts/common/os/grow.c b/usr/src/uts/common/os/grow.c
index da53bce24e..6e2d3c403c 100644
--- a/usr/src/uts/common/os/grow.c
+++ b/usr/src/uts/common/os/grow.c
@@ -21,7 +21,7 @@
/*
* Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved.
- * Copyright 2017 Joyent, Inc.
+ * Copyright 2018 Joyent, Inc.
*/
/*
@@ -55,6 +55,7 @@
#include <sys/fcntl.h>
#include <sys/lwpchan_impl.h>
#include <sys/nbmlock.h>
+#include <sys/brand.h>
#include <vm/hat.h>
#include <vm/as.h>
@@ -570,6 +571,20 @@ choose_addr(struct as *as, caddr_t *addrp, size_t len, offset_t off,
return (0);
}
+caddr_t
+map_userlimit(proc_t *pp, struct as *as, int flags)
+{
+ if (flags & _MAP_LOW32) {
+ if (PROC_IS_BRANDED(pp) && BROP(pp)->b_map32limit != NULL) {
+ return ((caddr_t)(uintptr_t)BROP(pp)->b_map32limit(pp));
+ } else {
+ return ((caddr_t)_userlimit32);
+ }
+ }
+
+ return (as->a_userlimit);
+}
+
/*
* Used for MAP_ANON - fast way to get anonymous pages
@@ -585,8 +600,6 @@ zmap(struct as *as, caddr_t *addrp, size_t len, uint_t uprot, int flags,
return (EACCES);
if ((flags & MAP_FIXED) != 0) {
- caddr_t userlimit;
-
/*
* Use the user address. First verify that
* the address to be used is page aligned.
@@ -595,9 +608,8 @@ zmap(struct as *as, caddr_t *addrp, size_t len, uint_t uprot, int flags,
if (((uintptr_t)*addrp & PAGEOFFSET) != 0)
return (EINVAL);
- userlimit = flags & _MAP_LOW32 ?
- (caddr_t)USERLIMIT32 : as->a_userlimit;
- switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) {
+ switch (valid_usr_range(*addrp, len, uprot, as,
+ map_userlimit(as->a_proc, as, flags))) {
case RANGE_OKAY:
break;
case RANGE_BADPROT:
@@ -638,7 +650,7 @@ zmap(struct as *as, caddr_t *addrp, size_t len, uint_t uprot, int flags,
#define RANDOMIZABLE_MAPPING(addr, flags) (((flags & MAP_FIXED) == 0) && \
!(((flags & MAP_ALIGN) == 0) && (addr != 0) && aslr_respect_mmap_hint))
-static int
+int
smmap_common(caddr_t *addrp, size_t len,
int prot, int flags, struct file *fp, offset_t pos)
{
@@ -771,8 +783,6 @@ smmap_common(caddr_t *addrp, size_t len,
* If the user specified an address, do some simple checks here
*/
if ((flags & MAP_FIXED) != 0) {
- caddr_t userlimit;
-
/*
* Use the user address. First verify that
* the address to be used is page aligned.
@@ -780,10 +790,8 @@ smmap_common(caddr_t *addrp, size_t len,
*/
if (((uintptr_t)*addrp & PAGEOFFSET) != 0)
return (EINVAL);
-
- userlimit = flags & _MAP_LOW32 ?
- (caddr_t)USERLIMIT32 : as->a_userlimit;
- switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) {
+ switch (valid_usr_range(*addrp, len, uprot, as,
+ map_userlimit(curproc, as, flags))) {
case RANGE_OKAY:
break;
case RANGE_BADPROT:
diff --git a/usr/src/uts/common/os/ipc.c b/usr/src/uts/common/os/ipc.c
index 86cb867da8..bf917ef716 100644
--- a/usr/src/uts/common/os/ipc.c
+++ b/usr/src/uts/common/os/ipc.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -1217,6 +1218,23 @@ ipc_remove(ipc_service_t *service, kipc_perm_t *perm)
(IPC_ZONE_USAGE(perm, service) == 0)));
}
+/*
+ * Perform actual IPC_RMID, either via ipc_rmid or due to a delayed *_RMID.
+ */
+void
+ipc_rmsvc(ipc_service_t *service, kipc_perm_t *perm)
+{
+ ASSERT(service->ipcs_count > 0);
+ ASSERT(MUTEX_HELD(&service->ipcs_lock));
+
+ ipc_remove(service, perm);
+ mutex_exit(&service->ipcs_lock);
+
+ /* perform any per-service removal actions */
+ service->ipcs_rmid(perm);
+
+ ipc_rele(service, perm);
+}
/*
* Common code to perform an IPC_RMID. Returns an errno value on
@@ -1247,13 +1265,7 @@ ipc_rmid(ipc_service_t *service, int id, cred_t *cr)
/*
* Nothing can fail from this point on.
*/
- ipc_remove(service, perm);
- mutex_exit(&service->ipcs_lock);
-
- /* perform any per-service removal actions */
- service->ipcs_rmid(perm);
-
- ipc_rele(service, perm);
+ ipc_rmsvc(service, perm);
return (0);
}
diff --git a/usr/src/uts/common/os/kmem.c b/usr/src/uts/common/os/kmem.c
index 394235f26c..4d2c1e6c10 100644
--- a/usr/src/uts/common/os/kmem.c
+++ b/usr/src/uts/common/os/kmem.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
* Copyright (c) 2012, 2017 by Delphix. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
* Copyright 2018, Joyent, Inc.
diff --git a/usr/src/uts/common/os/kstat_fr.c b/usr/src/uts/common/os/kstat_fr.c
index 93c04cff8d..b09b2d3558 100644
--- a/usr/src/uts/common/os/kstat_fr.c
+++ b/usr/src/uts/common/os/kstat_fr.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2017, Joyent, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
@@ -198,6 +198,9 @@ struct {
kstat_named_t pagesfree;
kstat_named_t pageslocked;
kstat_named_t pagestotal;
+ kstat_named_t lowmemscan;
+ kstat_named_t zonecapscan;
+ kstat_named_t nthrottle;
} system_pages_kstat = {
{ "physmem", KSTAT_DATA_ULONG },
{ "nalloc", KSTAT_DATA_ULONG },
@@ -219,6 +222,9 @@ struct {
{ "pagesfree", KSTAT_DATA_ULONG },
{ "pageslocked", KSTAT_DATA_ULONG },
{ "pagestotal", KSTAT_DATA_ULONG },
+ { "low_mem_scan", KSTAT_DATA_ULONG },
+ { "zone_cap_scan", KSTAT_DATA_ULONG },
+ { "n_throttle", KSTAT_DATA_ULONG },
};
static int header_kstat_update(kstat_t *, int);
@@ -912,6 +918,9 @@ system_pages_kstat_update(kstat_t *ksp, int rw)
system_pages_kstat.pageslocked.value.ul = (ulong_t)(availrmem_initial -
availrmem);
system_pages_kstat.pagestotal.value.ul = (ulong_t)total_pages;
+ system_pages_kstat.lowmemscan.value.ul = (ulong_t)low_mem_scan;
+ system_pages_kstat.zonecapscan.value.ul = (ulong_t)zone_cap_scan;
+ system_pages_kstat.nthrottle.value.ul = (ulong_t)n_throttle;
/*
* pp_kernel represents total pages used by the kernel since the
* startup. This formula takes into account the boottime kernel
diff --git a/usr/src/uts/common/os/logsubr.c b/usr/src/uts/common/os/logsubr.c
index b5f41d93f9..6a922343e7 100644
--- a/usr/src/uts/common/os/logsubr.c
+++ b/usr/src/uts/common/os/logsubr.c
@@ -23,6 +23,8 @@
* Copyright 2020 Oxide Computer Company
* Copyright (c) 2013 Gary Mills
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2022 Joyent, Inc.
+ * Copyright 2022 MNX Cloud, Inc.
*/
#include <sys/types.h>
@@ -260,8 +262,11 @@ log_init(void)
#ifdef LEGACY_BANNER
printf("\rSunOS Release %s Version %s %u-bit\n",
utsname.release, utsname.version, NBBY * (uint_t)sizeof (void *));
- printf("Copyright (c) 1983, 2010, Oracle and/or its affiliates. "
- "All rights reserved.\n");
+ /*
+ * Note: In the future this should be 2022-20XX, and delete this
+ * comment when we don't need it anymore
+ */
+ printf("Copyright 2022 MNX Cloud, Inc.\n");
#else
bootbanner_print(log_bootbanner_print, KM_SLEEP);
#endif
diff --git a/usr/src/uts/common/os/lwp.c b/usr/src/uts/common/os/lwp.c
index 5e3b1ec949..f487760e68 100644
--- a/usr/src/uts/common/os/lwp.c
+++ b/usr/src/uts/common/os/lwp.c
@@ -25,7 +25,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2017, Joyent, Inc.
*/
#include <sys/param.h>
@@ -56,6 +56,8 @@
#include <sys/lgrp.h>
#include <sys/rctl.h>
#include <sys/contract_impl.h>
+#include <sys/contract/process.h>
+#include <sys/contract/process_impl.h>
#include <sys/cpc_impl.h>
#include <sys/sdt.h>
#include <sys/cmn_err.h>
@@ -114,7 +116,7 @@ lwp_create(void (*proc)(), caddr_t arg, size_t len, proc_t *p,
ret_tidhash_t *ret_tidhash = NULL;
int i;
int rctlfail = 0;
- boolean_t branded = 0;
+ void *brand_data = NULL;
struct ctxop *ctx = NULL;
ASSERT(cid != sysdccid); /* system threads must start in SYS */
@@ -282,6 +284,19 @@ lwp_create(void (*proc)(), caddr_t arg, size_t len, proc_t *p,
*/
lep = kmem_zalloc(sizeof (*lep), KM_SLEEP);
+ /*
+ * If necessary, speculatively allocate lwp brand data. This is done
+ * ahead of time so p_lock need not be dropped during lwp branding.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_lwpdata_alloc != NULL) {
+ if ((brand_data = BROP(p)->b_lwpdata_alloc(p)) == NULL) {
+ mutex_enter(&p->p_lock);
+ err = 1;
+ atomic_inc_32(&p->p_zone->zone_ffmisc);
+ goto error;
+ }
+ }
+
mutex_enter(&p->p_lock);
grow:
/*
@@ -629,18 +644,6 @@ grow:
} while (lwp_hash_lookup(p, t->t_tid) != NULL);
}
- /*
- * If this is a branded process, let the brand do any necessary lwp
- * initialization.
- */
- if (PROC_IS_BRANDED(p)) {
- if (BROP(p)->b_initlwp(lwp)) {
- err = 1;
- atomic_inc_32(&p->p_zone->zone_ffmisc);
- goto error;
- }
- branded = 1;
- }
if (t->t_tid == 1) {
kpreempt_disable();
@@ -653,7 +656,6 @@ grow:
}
}
- p->p_lwpcnt++;
t->t_waitfor = -1;
/*
@@ -695,8 +697,27 @@ grow:
t->t_post_sys = 1;
/*
+ * Perform lwp branding
+ *
+ * The b_initlwp hook is _not_ allowed to drop p->p_lock as it must be
+ * continuously held between when the tidhash is sized and when the lwp
+ * is inserted into it. Operations requiring p->p_lock to be
+ * temporarily dropped can be performed in b_initlwp_post.
+ */
+ if (PROC_IS_BRANDED(p)) {
+ BROP(p)->b_initlwp(lwp, brand_data);
+ /*
+ * The b_initlwp hook is expected to consume any preallocated
+ * brand_data in a way that prepares it for deallocation by the
+ * b_freelwp hook.
+ */
+ brand_data = NULL;
+ }
+
+ /*
* Insert the new thread into the list of all threads.
*/
+ p->p_lwpcnt++;
if ((tx = p->p_tlist) == NULL) {
t->t_back = t;
t->t_forw = t;
@@ -717,6 +738,13 @@ grow:
lep->le_start = t->t_start;
lwp_hash_in(p, lep, p->p_tidhash, p->p_tidhash_sz, 1);
+ /*
+ * Complete lwp branding
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_initlwp_post != NULL) {
+ BROP(p)->b_initlwp_post(lwp);
+ }
+
lwp_fp_init(lwp);
if (state == TS_RUN) {
@@ -754,8 +782,9 @@ error:
if (cid != NOCLASS && bufp != NULL)
CL_FREE(cid, bufp);
- if (branded)
- BROP(p)->b_freelwp(lwp);
+ if (brand_data != NULL) {
+ BROP(p)->b_lwpdata_free(brand_data);
+ }
mutex_exit(&p->p_lock);
t->t_state = TS_FREE;
@@ -828,8 +857,27 @@ lwp_ctmpl_copy(klwp_t *dst, klwp_t *src)
int i;
for (i = 0; i < ct_ntypes; i++) {
- dst->lwp_ct_active[i] = ctmpl_dup(src->lwp_ct_active[i]);
+ ct_template_t *tmpl = src->lwp_ct_active[i];
+
+ /*
+ * If the process contract template is setup to be preserved
+ * across exec, then if we're forking, perform an implicit
+ * template_clear now. This ensures that future children of
+ * this child will remain in the same contract unless they're
+ * explicitly setup differently. We know we're forking if the
+ * two LWPs belong to different processes.
+ */
+ if (i == CTT_PROCESS && tmpl != NULL) {
+ ctmpl_process_t *ctp = tmpl->ctmpl_data;
+
+ if (dst->lwp_procp != src->lwp_procp &&
+ (ctp->ctp_params & CT_PR_KEEP_EXEC) != 0)
+ tmpl = NULL;
+ }
+
+ dst->lwp_ct_active[i] = ctmpl_dup(tmpl);
dst->lwp_ct_latest[i] = NULL;
+
}
}
@@ -837,21 +885,33 @@ lwp_ctmpl_copy(klwp_t *dst, klwp_t *src)
* Clear an LWP's contract template state.
*/
void
-lwp_ctmpl_clear(klwp_t *lwp)
+lwp_ctmpl_clear(klwp_t *lwp, boolean_t is_exec)
{
ct_template_t *tmpl;
int i;
for (i = 0; i < ct_ntypes; i++) {
- if ((tmpl = lwp->lwp_ct_active[i]) != NULL) {
- ctmpl_free(tmpl);
- lwp->lwp_ct_active[i] = NULL;
- }
-
if (lwp->lwp_ct_latest[i] != NULL) {
contract_rele(lwp->lwp_ct_latest[i]);
lwp->lwp_ct_latest[i] = NULL;
}
+
+ if ((tmpl = lwp->lwp_ct_active[i]) != NULL) {
+ /*
+ * If we're exec-ing a new program and the process
+ * contract template is setup to be preserved across
+ * exec, then don't clear it.
+ */
+ if (is_exec && i == CTT_PROCESS) {
+ ctmpl_process_t *ctp = tmpl->ctmpl_data;
+
+ if ((ctp->ctp_params & CT_PR_KEEP_EXEC) != 0)
+ continue;
+ }
+
+ ctmpl_free(tmpl);
+ lwp->lwp_ct_active[i] = NULL;
+ }
}
}
@@ -892,13 +952,6 @@ lwp_exit(void)
if (t->t_upimutex != NULL)
upimutex_cleanup();
- /*
- * Perform any brand specific exit processing, then release any
- * brand data associated with the lwp
- */
- if (PROC_IS_BRANDED(p))
- BROP(p)->b_lwpexit(lwp);
-
lwp_pcb_exit();
mutex_enter(&p->p_lock);
@@ -942,6 +995,18 @@ lwp_exit(void)
DTRACE_PROC(lwp__exit);
/*
+ * Perform any brand specific exit processing, then release any
+ * brand data associated with the lwp
+ */
+ if (PROC_IS_BRANDED(p)) {
+ mutex_exit(&p->p_lock);
+ BROP(p)->b_lwpexit(lwp);
+ BROP(p)->b_freelwp(lwp);
+ mutex_enter(&p->p_lock);
+ prbarrier(p);
+ }
+
+ /*
* If the lwp is a detached lwp or if the process is exiting,
* remove (lwp_hash_out()) the lwp from the lwp directory.
* Otherwise null out the lwp's le_thread pointer in the lwp
@@ -1096,7 +1161,7 @@ lwp_cleanup(void)
}
kpreempt_enable();
- lwp_ctmpl_clear(ttolwp(t));
+ lwp_ctmpl_clear(ttolwp(t), B_FALSE);
}
int
diff --git a/usr/src/uts/common/os/main.c b/usr/src/uts/common/os/main.c
index 148916d4d8..c57f8a7d2c 100644
--- a/usr/src/uts/common/os/main.c
+++ b/usr/src/uts/common/os/main.c
@@ -159,7 +159,7 @@ exec_init(const char *initpath, const char *args)
int error = 0, count = 0;
proc_t *p = ttoproc(curthread);
klwp_t *lwp = ttolwp(curthread);
- int brand_action;
+ int brand_action = EBA_NONE;
if (args == NULL)
args = "";
@@ -289,7 +289,15 @@ exec_init(const char *initpath, const char *args)
*/
sigemptyset(&curthread->t_hold);
- brand_action = ZONE_IS_BRANDED(p->p_zone) ? EBA_BRAND : EBA_NONE;
+ /*
+ * Only instruct exec_common to brand the process if necessary. It is
+ * possible that the init process is already properly branded due to the
+ * proc_exit -> restart_init -> exec_init call chain.
+ */
+ if (ZONE_IS_BRANDED(p->p_zone) &&
+ p->p_brand != p->p_zone->zone_brand) {
+ brand_action = EBA_BRAND;
+ }
again:
error = exec_common((const char *)exec_fnamep,
(const char **)uap, NULL, brand_action);
diff --git a/usr/src/uts/common/os/mem_config.c b/usr/src/uts/common/os/mem_config.c
index 4c4e78578b..fd74dd3092 100644
--- a/usr/src/uts/common/os/mem_config.c
+++ b/usr/src/uts/common/os/mem_config.c
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc.
*/
#include <sys/types.h>
@@ -1638,7 +1639,7 @@ delthr_get_freemem(struct mem_handle *mhp)
* Put pressure on pageout.
*/
page_needfree(free_get);
- cv_signal(&proc_pageout->p_cv);
+ WAKE_PAGEOUT_SCANNER();
mutex_enter(&mhp->mh_mutex);
(void) cv_reltimedwait(&mhp->mh_cv, &mhp->mh_mutex,
diff --git a/usr/src/uts/common/os/mmapobj.c b/usr/src/uts/common/os/mmapobj.c
index d85df39a62..819d32116d 100644
--- a/usr/src/uts/common/os/mmapobj.c
+++ b/usr/src/uts/common/os/mmapobj.c
@@ -1367,10 +1367,15 @@ calc_loadable(Ehdr *ehdrp, caddr_t phdrbase, int nphdrs, size_t *len,
}
if (num_segs++ == 0) {
/*
- * The p_vaddr of the first PT_LOAD segment
- * must either be NULL or within the first
- * page in order to be interpreted.
- * Otherwise, its an invalid file.
+ * While ELF doesn't specify the meaning of
+ * p_vaddr for PT_LOAD segments in ET_DYN
+ * objects, we mandate that is either NULL or
+ * (to accommodate some historical binaries)
+ * within the first page. (Note that there
+ * exist non-native ET_DYN objects that violate
+ * this constraint that we nonetheless must be
+ * able to execute; see the ET_DYN handling in
+ * mapelfexec() for details.)
*/
if (e_type == ET_DYN &&
((caddr_t)((uintptr_t)vaddr &
diff --git a/usr/src/uts/common/os/pid.c b/usr/src/uts/common/os/pid.c
index 37389a6e4d..d48be19c71 100644
--- a/usr/src/uts/common/os/pid.c
+++ b/usr/src/uts/common/os/pid.c
@@ -113,6 +113,18 @@ pid_lookup(pid_t pid)
return (pidp);
}
+struct pid *
+pid_find(pid_t pid)
+{
+ struct pid *pidp;
+
+ mutex_enter(&pidlinklock);
+ pidp = pid_lookup(pid);
+ mutex_exit(&pidlinklock);
+
+ return (pidp);
+}
+
void
pid_setmin(void)
{
@@ -521,6 +533,19 @@ sprunlock(proc_t *p)
mutex_exit(&p->p_lock);
}
+/*
+ * Undo effects of sprlock but without dropping p->p_lock
+ */
+void
+sprunprlock(proc_t *p)
+{
+ ASSERT(p->p_proc_flag & P_PR_LOCK);
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ cv_signal(&pr_pid_cv[p->p_slot]);
+ p->p_proc_flag &= ~P_PR_LOCK;
+}
+
void
pid_init(void)
{
diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c
index 0e4bd2c73d..b3f01cfab2 100644
--- a/usr/src/uts/common/os/policy.c
+++ b/usr/src/uts/common/os/policy.c
@@ -57,6 +57,7 @@
#include <sys/mntent.h>
#include <sys/contract_impl.h>
#include <sys/dld_ioc.h>
+#include <sys/brand.h>
/*
* There are two possible layers of privilege routines and two possible
@@ -1275,6 +1276,22 @@ secpolicy_vnode_owner(const cred_t *cr, uid_t owner)
void
secpolicy_setid_clear(vattr_t *vap, cred_t *cr)
{
+ proc_t *p = curproc;
+
+ /*
+ * Allow the brand to override this behaviour.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_setid_clear != NULL) {
+ /*
+ * This brand hook will return 0 if handling is complete, or
+ * some other value if the brand would like us to fall back to
+ * the usual behaviour.
+ */
+ if (BROP(p)->b_setid_clear(vap, cr) == 0) {
+ return;
+ }
+ }
+
if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0 &&
secpolicy_vnode_setid_retain(cr,
(vap->va_mode & S_ISUID) != 0 &&
@@ -2123,6 +2140,13 @@ secpolicy_meminfo(const cred_t *cr)
}
int
+secpolicy_fs_import(const cred_t *cr)
+{
+ return (PRIV_POLICY(cr, PRIV_SYS_FS_IMPORT, B_FALSE, EPERM, NULL));
+}
+
+
+int
secpolicy_pfexec_register(const cred_t *cr)
{
return (PRIV_POLICY(cr, PRIV_SYS_ADMIN, B_TRUE, EPERM, NULL));
@@ -2639,3 +2663,11 @@ secpolicy_ppp_config(const cred_t *cr)
return (secpolicy_net_config(cr, B_FALSE));
return (PRIV_POLICY(cr, PRIV_SYS_PPP_CONFIG, B_FALSE, EPERM, NULL));
}
+
+int
+secpolicy_hyprlofs_control(const cred_t *cr)
+{
+ if (PRIV_POLICY(cr, PRIV_HYPRLOFS_CONTROL, B_FALSE, EPERM, NULL))
+ return (EPERM);
+ return (0);
+}
diff --git a/usr/src/uts/common/os/priv_defs b/usr/src/uts/common/os/priv_defs
index 186aafc460..05979dd236 100644
--- a/usr/src/uts/common/os/priv_defs
+++ b/usr/src/uts/common/os/priv_defs
@@ -177,6 +177,10 @@ privilege PRIV_GRAPHICS_MAP
Allows a process to perform privileged mappings through a
graphics device.
+privilege PRIV_HYPRLOFS_CONTROL
+
+ Allows a process to manage hyprlofs entries.
+
privilege PRIV_IPC_DAC_READ
Allows a process to read a System V IPC
@@ -377,6 +381,10 @@ privilege PRIV_SYS_DEVICES
Allows a process to open the real console device directly.
Allows a process to open devices that have been exclusively opened.
+privilege PRIV_SYS_FS_IMPORT
+
+ Allows a process to import a potentially untrusted file system.
+
privilege PRIV_SYS_IPC_CONFIG
Allows a process to increase the size of a System V IPC Message
diff --git a/usr/src/uts/common/os/rctl.c b/usr/src/uts/common/os/rctl.c
index 81a1b5454a..8f52f4ef3a 100644
--- a/usr/src/uts/common/os/rctl.c
+++ b/usr/src/uts/common/os/rctl.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2017, Joyent, Inc.
*/
#include <sys/atomic.h>
@@ -194,6 +195,8 @@ id_space_t *rctl_ids;
kmem_cache_t *rctl_cache; /* kmem cache for rctl structures */
kmem_cache_t *rctl_val_cache; /* kmem cache for rctl values */
+extern rctl_hndl_t rc_process_maxlockedmem;
+
kmutex_t rctl_lists_lock;
rctl_dict_entry_t *rctl_lists[RC_MAX_ENTITY + 1];
@@ -2870,12 +2873,12 @@ rctl_init(void)
* rctl_incr_locked_mem(proc_t *p, kproject_t *proj, rctl_qty_t inc,
* int chargeproc)
*
- * Increments the amount of locked memory on a project, and
- * zone. If proj is non-NULL the project must be held by the
- * caller; if it is NULL the proj and zone of proc_t p are used.
- * If chargeproc is non-zero, then the charged amount is cached
- * on p->p_locked_mem so that the charge can be migrated when a
- * process changes projects.
+ * Increments the amount of locked memory on a process, project, and
+ * zone. If 'proj' is non-NULL, the project must be held by the
+ * caller; if it is NULL, the project and zone of process 'p' are used.
+ * If 'chargeproc' is non-zero, then the charged amount is added
+ * to p->p_locked_mem. This is also used so that the charge can be
+ * migrated when a process changes projects.
*
* Return values
* 0 - success
@@ -2893,6 +2896,7 @@ rctl_incr_locked_mem(proc_t *p, kproject_t *proj, rctl_qty_t inc,
ASSERT(p != NULL);
ASSERT(MUTEX_HELD(&p->p_lock));
+
if (proj != NULL) {
projp = proj;
zonep = proj->kpj_zone;
@@ -2936,11 +2940,23 @@ rctl_incr_locked_mem(proc_t *p, kproject_t *proj, rctl_qty_t inc,
}
}
- zonep->zone_locked_mem += inc;
- projp->kpj_data.kpd_locked_mem += inc;
if (chargeproc != 0) {
+ /* Check for overflow */
+ if ((p->p_locked_mem + inc) < p->p_locked_mem) {
+ ret = EAGAIN;
+ goto out;
+ }
+ if (rctl_test_entity(rc_process_maxlockedmem, p->p_rctls, p,
+ &e, inc, 0) & RCT_DENY) {
+ ret = EAGAIN;
+ goto out;
+ }
+
p->p_locked_mem += inc;
}
+
+ zonep->zone_locked_mem += inc;
+ projp->kpj_data.kpd_locked_mem += inc;
out:
mutex_exit(&zonep->zone_mem_lock);
return (ret);
diff --git a/usr/src/uts/common/os/rctl_proc.c b/usr/src/uts/common/os/rctl_proc.c
index 9b7324fe7b..c62540d2b4 100644
--- a/usr/src/uts/common/os/rctl_proc.c
+++ b/usr/src/uts/common/os/rctl_proc.c
@@ -21,6 +21,7 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc.
*/
#include <sys/types.h>
@@ -32,6 +33,7 @@
#include <sys/port_kernel.h>
#include <sys/signal.h>
#include <sys/var.h>
+#include <sys/policy.h>
#include <sys/vmparam.h>
#include <sys/machparam.h>
@@ -66,6 +68,7 @@ rctl_hndl_t rc_process_semmsl;
rctl_hndl_t rc_process_semopm;
rctl_hndl_t rc_process_portev;
rctl_hndl_t rc_process_sigqueue;
+rctl_hndl_t rc_process_maxlockedmem;
/*
* process.max-cpu-time / RLIMIT_CPU
@@ -212,6 +215,26 @@ static rctl_ops_t proc_vmem_ops = {
};
/*
+ * process.max-locked-memory
+ */
+/*ARGSUSED*/
+static int
+proc_maxlockedmem_test(struct rctl *r, struct proc *p, rctl_entity_p_t *e,
+ struct rctl_val *rv, rctl_qty_t i, uint_t f)
+{
+ if (secpolicy_lock_memory(CRED()) == 0)
+ return (0);
+ return ((p->p_locked_mem + i) > rv->rcv_value);
+}
+
+static rctl_ops_t proc_maxlockedmem_ops = {
+ rcop_no_action,
+ rcop_no_usage,
+ rcop_no_set,
+ proc_maxlockedmem_test
+};
+
+/*
* void rctlproc_default_init()
*
* Overview
@@ -383,6 +406,11 @@ rctlproc_init(void)
rctl_add_default_limit("process.max-sigqueue-size",
_SIGQUEUE_SIZE_PRIVILEGED, RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
+ rc_process_maxlockedmem = rctl_register("process.max-locked-memory",
+ RCENTITY_PROCESS, RCTL_GLOBAL_LOWERABLE | RCTL_GLOBAL_DENY_ALWAYS |
+ RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_BYTES,
+ ULONG_MAX, UINT32_MAX, &proc_maxlockedmem_ops);
+
/*
* Place minimal set of controls on "sched" process for inheritance by
* processes created via newproc().
diff --git a/usr/src/uts/common/os/sched.c b/usr/src/uts/common/os/sched.c
index dba962fa63..a54ab28751 100644
--- a/usr/src/uts/common/os/sched.c
+++ b/usr/src/uts/common/os/sched.c
@@ -27,6 +27,10 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
@@ -643,16 +647,17 @@ top:
klwp_t *lwp = ttolwp(tp);
/*
- * Swapout eligible lwps (specified by the scheduling
- * class) which don't have TS_DONT_SWAP set. Set the
- * "intent to swap" flag (TS_SWAPENQ) on threads
- * which have TS_DONT_SWAP set so that they can be
+ * Swapout eligible lwps (specified by the scheduling class)
+ * which don't have TS_DONT_SWAP set. Set the "intent to swap"
+ * flag (TS_SWAPENQ) on threads which have either TS_DONT_SWAP
+ * set or are currently on a split stack so that they can be
* swapped if and when they reach a safe point.
*/
thread_lock(tp);
thread_pri = CL_SWAPOUT(tp, swapflags);
if (thread_pri != -1) {
- if (tp->t_schedflag & TS_DONT_SWAP) {
+ if ((tp->t_schedflag & TS_DONT_SWAP) ||
+ (tp->t_flag & T_SPLITSTK)) {
tp->t_schedflag |= TS_SWAPENQ;
tp->t_trapret = 1;
aston(tp);
diff --git a/usr/src/uts/common/os/shm.c b/usr/src/uts/common/os/shm.c
index 8f98fcb3f0..d0611eb9bb 100644
--- a/usr/src/uts/common/os/shm.c
+++ b/usr/src/uts/common/os/shm.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -319,6 +320,7 @@ shmat(int shmid, caddr_t uaddr, int uflags, uintptr_t *rvp)
size_t share_size;
struct shm_data ssd;
uintptr_t align_hint;
+ long curprot;
/*
* Pick a share pagesize to use, if (!isspt(sp)).
@@ -453,6 +455,7 @@ shmat(int shmid, caddr_t uaddr, int uflags, uintptr_t *rvp)
}
}
+ curprot = sp->shm_opts & SHM_PROT_MASK;
if (!isspt(sp)) {
error = sptcreate(size, &segspt, sp->shm_amp, prot,
flags, share_szc);
@@ -462,8 +465,8 @@ shmat(int shmid, caddr_t uaddr, int uflags, uintptr_t *rvp)
}
sp->shm_sptinfo->sptas = segspt->s_as;
sp->shm_sptseg = segspt;
- sp->shm_sptprot = prot;
- } else if ((prot & sp->shm_sptprot) != sp->shm_sptprot) {
+ sp->shm_opts = (sp->shm_opts & ~SHM_PROT_MASK) | prot;
+ } else if ((prot & curprot) != curprot) {
/*
* Ensure we're attaching to an ISM segment with
* fewer or equal permissions than what we're
@@ -748,6 +751,23 @@ shmctl(int shmid, int cmd, void *arg)
}
break;
+ /* Stage segment for removal, but don't remove until last detach */
+ case SHM_RMID:
+ if ((error = secpolicy_ipc_owner(cr, (kipc_perm_t *)sp)) != 0)
+ break;
+
+ /*
+ * If attached, just mark it as a pending remove, otherwise
+ * we must perform the normal ipc_rmid now.
+ */
+ if ((sp->shm_perm.ipc_ref - 1) > 0) {
+ sp->shm_opts |= SHM_RM_PENDING;
+ } else {
+ mutex_exit(lock);
+ return (ipc_rmid(shm_svc, shmid, cr));
+ }
+ break;
+
default:
error = EINVAL;
break;
@@ -778,6 +798,23 @@ shm_detach(proc_t *pp, segacct_t *sap)
sp->shm_ismattch--;
sp->shm_dtime = gethrestime_sec();
sp->shm_lpid = pp->p_pid;
+ if ((sp->shm_opts & SHM_RM_PENDING) != 0 &&
+ sp->shm_perm.ipc_ref == 2) {
+ /*
+ * If this is the last detach of the segment across the whole
+ * system then now we can perform the delayed IPC_RMID.
+ * The ipc_ref count has 1 for the original 'get' and one for
+ * each 'attach' (see 'stat' handling in shmctl).
+ */
+ sp->shm_opts &= ~SHM_RM_PENDING;
+ mutex_enter(&shm_svc->ipcs_lock);
+ ipc_rmsvc(shm_svc, (kipc_perm_t *)sp); /* Drops lock */
+ ASSERT(!MUTEX_HELD(&shm_svc->ipcs_lock));
+ ASSERT(((kipc_perm_t *)sp)->ipc_ref > 0);
+
+ /* Lock was dropped, need to retake it for following rele. */
+ (void) ipc_lock(shm_svc, sp->shm_perm.ipc_id);
+ }
ipc_rele(shm_svc, (kipc_perm_t *)sp); /* Drops lock */
kmem_free(sap, sizeof (segacct_t));
diff --git a/usr/src/uts/common/os/sig.c b/usr/src/uts/common/os/sig.c
index 453b1f22d4..67a93581dd 100644
--- a/usr/src/uts/common/os/sig.c
+++ b/usr/src/uts/common/os/sig.c
@@ -22,7 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2017, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -60,6 +60,7 @@
#include <sys/cyclic.h>
#include <sys/dtrace.h>
#include <sys/sdt.h>
+#include <sys/brand.h>
#include <sys/signalfd.h>
const k_sigset_t nullsmask = {0, 0, 0};
@@ -148,6 +149,21 @@ signal_is_blocked(kthread_t *t, int sig)
}
/*
+ * Return true if the signal can safely be ignored.
+ * That is, if the signal is included in the p_ignore mask and doing so is not
+ * forbidden by any process branding.
+ */
+static int
+sig_ignorable(proc_t *p, klwp_t *lwp, int sig)
+{
+ return (sigismember(&p->p_ignore, sig) && /* sig in ignore mask */
+ !(PROC_IS_BRANDED(p) && /* allowed by brand */
+ BROP(p)->b_sig_ignorable != NULL &&
+ BROP(p)->b_sig_ignorable(p, lwp, sig) == B_FALSE));
+
+}
+
+/*
* Return true if the signal can safely be discarded on generation.
* That is, if there is no need for the signal on the receiving end.
* The answer is true if the process is a zombie or
@@ -159,12 +175,13 @@ signal_is_blocked(kthread_t *t, int sig)
* the signal is not being accepted via sigwait()
*/
static int
-sig_discardable(proc_t *p, int sig)
+sig_discardable(proc_t *p, kthread_t *tp, int sig)
{
kthread_t *t = p->p_tlist;
+ klwp_t *lwp = (tp == NULL) ? NULL : tp->t_lwp;
return (t == NULL || /* if zombie or ... */
- (sigismember(&p->p_ignore, sig) && /* signal is ignored */
+ (sig_ignorable(p, lwp, sig) && /* signal is ignored */
t->t_forw == t && /* and single-threaded */
!tracing(p, sig) && /* and no /proc tracing */
!signal_is_blocked(t, sig) && /* and signal not blocked */
@@ -200,7 +217,7 @@ eat_signal(kthread_t *t, int sig)
!(ttoproc(t)->p_proc_flag & P_PR_LOCK)) {
ttoproc(t)->p_stopsig = 0;
t->t_dtrace_stop = 0;
- t->t_schedflag |= TS_XSTART | TS_PSTART;
+ t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
setrun_locked(t);
} else if (t != curthread && t->t_state == TS_ONPROC) {
aston(t); /* make it do issig promptly */
@@ -297,7 +314,7 @@ sigtoproc(proc_t *p, kthread_t *t, int sig)
}
}
- if (sig_discardable(p, sig)) {
+ if (sig_discardable(p, t, sig)) {
DTRACE_PROC3(signal__discard, kthread_t *, p->p_tlist,
proc_t *, p, int, sig);
return;
@@ -497,7 +514,7 @@ issig_justlooking(void)
if (sigismember(&set, sig) &&
(tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sigismember(&p->p_ignore, sig))) {
+ !sig_ignorable(p, lwp, sig))) {
/*
* Don't promote a signal that will stop
* the process when lwp_nostop is set.
@@ -623,6 +640,28 @@ issig_forreal(void)
}
/*
+ * The brand hook name 'b_issig_stop' is a misnomer.
+ * Allow the brand the chance to alter (or suppress) delivery
+ * of this signal.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_issig_stop != NULL) {
+ int r;
+
+ /*
+ * The brand hook will return 0 if it would like
+ * us to drive on, -1 if we should restart
+ * the loop to check other conditions, or 1 if we
+ * should terminate the loop.
+ */
+ r = BROP(p)->b_issig_stop(p, lwp);
+ if (r < 0) {
+ continue;
+ } else if (r > 0) {
+ break;
+ }
+ }
+
+ /*
* Honor requested stop before dealing with the
* current signal; a debugger may change it.
* Do not want to go back to loop here since this is a special
@@ -656,7 +695,7 @@ issig_forreal(void)
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
if (sigismember(&t->t_sigwait, sig) ||
- (!sigismember(&p->p_ignore, sig) &&
+ (!sig_ignorable(p, lwp, sig) &&
!isjobstop(sig))) {
if (p->p_flag & (SEXITLWPS|SKILLED)) {
sig = SIGKILL;
@@ -708,7 +747,7 @@ issig_forreal(void)
toproc = 0;
if (tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sigismember(&p->p_ignore, sig)) {
+ !sig_ignorable(p, lwp, sig)) {
if (sigismember(&t->t_extsig, sig))
ext = 1;
break;
@@ -722,7 +761,7 @@ issig_forreal(void)
toproc = 1;
if (tracing(p, sig) ||
sigismember(&t->t_sigwait, sig) ||
- !sigismember(&p->p_ignore, sig)) {
+ !sig_ignorable(p, lwp, sig)) {
if (sigismember(&p->p_extsig, sig))
ext = 1;
break;
@@ -954,6 +993,16 @@ stop(int why, int what)
}
break;
+ case PR_BRAND:
+ /*
+ * We have been stopped by the brand code for a brand-private
+ * reason. This is an asynchronous stop affecting only this
+ * LWP.
+ */
+ VERIFY(PROC_IS_BRANDED(p));
+ flags &= ~TS_BSTART;
+ break;
+
default: /* /proc stop */
flags &= ~TS_PSTART;
/*
@@ -1065,7 +1114,7 @@ stop(int why, int what)
}
}
- if (why != PR_JOBCONTROL && why != PR_CHECKPOINT) {
+ if (why != PR_JOBCONTROL && why != PR_CHECKPOINT && why != PR_BRAND) {
/*
* Do process-level notification when all lwps are
* either stopped on events of interest to /proc
@@ -1171,6 +1220,13 @@ stop(int why, int what)
if (why == PR_CHECKPOINT)
del_one_utstop();
+ /*
+ * Allow the brand to post notification of this stop condition.
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_stop_notify != NULL) {
+ BROP(p)->b_stop_notify(p, lwp, why, what);
+ }
+
thread_lock(t);
ASSERT((t->t_schedflag & TS_ALLSTART) == 0);
t->t_schedflag |= flags;
@@ -1192,7 +1248,7 @@ stop(int why, int what)
(p->p_flag & (SEXITLWPS|SKILLED))) {
p->p_stopsig = 0;
thread_lock(t);
- t->t_schedflag |= TS_XSTART | TS_PSTART;
+ t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
setrun_locked(t);
thread_unlock_nopreempt(t);
} else if (why == PR_JOBCONTROL) {
@@ -1327,7 +1383,7 @@ psig(void)
* this signal from pending to current (we dropped p->p_lock).
* This can happen only in a multi-threaded process.
*/
- if (sigismember(&p->p_ignore, sig) ||
+ if (sig_ignorable(p, lwp, sig) ||
(func == SIG_DFL && sigismember(&stopdefault, sig))) {
lwp->lwp_cursig = 0;
lwp->lwp_extsig = 0;
@@ -1771,9 +1827,12 @@ post_sigcld(proc_t *cp, sigqueue_t *sqp)
/*
* This can only happen when the parent is init.
* (See call to sigcld(q, NULL) in exit().)
- * Use KM_NOSLEEP to avoid deadlock.
+ * Use KM_NOSLEEP to avoid deadlock. The child procs
+ * initpid can be 1 for zlogin.
*/
- ASSERT(pp == proc_init);
+ ASSERT(pp->p_pidp->pid_id ==
+ cp->p_zone->zone_proc_initpid ||
+ pp->p_pidp->pid_id == 1);
winfo(cp, &info, 0);
sigaddq(pp, NULL, &info, KM_NOSLEEP);
} else {
@@ -1804,6 +1863,15 @@ sigcld_repost()
sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
mutex_enter(&pidlock);
+ if (PROC_IS_BRANDED(pp) && BROP(pp)->b_sigcld_repost != NULL) {
+ /*
+ * Allow the brand to inject synthetic SIGCLD signals.
+ */
+ if (BROP(pp)->b_sigcld_repost(pp, sqp) == 0) {
+ mutex_exit(&pidlock);
+ return;
+ }
+ }
for (cp = pp->p_child; cp; cp = cp->p_sibling) {
if (cp->p_pidflag & CLDPEND) {
post_sigcld(cp, sqp);
@@ -2115,7 +2183,7 @@ sigaddqa(proc_t *p, kthread_t *t, sigqueue_t *sigqp)
ASSERT(MUTEX_HELD(&p->p_lock));
ASSERT(sig >= 1 && sig < NSIG);
- if (sig_discardable(p, sig))
+ if (sig_discardable(p, t, sig))
siginfofree(sigqp);
else
sigaddqins(p, t, sigqp);
@@ -2141,7 +2209,7 @@ sigaddq(proc_t *p, kthread_t *t, k_siginfo_t *infop, int km_flags)
* blocking the signal (it *could* change it's mind while
* the signal is pending) then don't bother creating one.
*/
- if (!sig_discardable(p, sig) &&
+ if (!sig_discardable(p, t, sig) &&
(sigismember(&p->p_siginfo, sig) ||
(curproc->p_ct_process != p->p_ct_process) ||
(sig == SIGCLD && SI_FROMKERNEL(infop))) &&
diff --git a/usr/src/uts/common/os/streamio.c b/usr/src/uts/common/os/streamio.c
index c137a498d1..90a9ea6f0f 100644
--- a/usr/src/uts/common/os/streamio.c
+++ b/usr/src/uts/common/os/streamio.c
@@ -78,6 +78,7 @@
#include <sys/policy.h>
#include <sys/dld.h>
#include <sys/zone.h>
+#include <sys/limits.h>
#include <sys/ptms.h>
#include <sys/limits.h>
#include <c2/audit.h>
@@ -3267,6 +3268,7 @@ job_control_type(int cmd)
case JAGENT: /* Obsolete */
case JTRUN: /* Obsolete */
case JXTPROTO: /* Obsolete */
+ case TIOCSETLD:
return (JCSETP);
}
diff --git a/usr/src/uts/common/os/strsubr.c b/usr/src/uts/common/os/strsubr.c
index fdd0c06aee..f2b91365d9 100644
--- a/usr/src/uts/common/os/strsubr.c
+++ b/usr/src/uts/common/os/strsubr.c
@@ -26,6 +26,7 @@
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright (c) 2016 by Delphix. All rights reserved.
+ * Copyright 2018 Joyent, Inc.
* Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
* Copyright 2018 Joyent, Inc.
* Copyright 2022 Garrett D'Amore
diff --git a/usr/src/uts/common/os/sunddi.c b/usr/src/uts/common/os/sunddi.c
index 30cc5744c2..7c094a0f20 100644
--- a/usr/src/uts/common/os/sunddi.c
+++ b/usr/src/uts/common/os/sunddi.c
@@ -5822,6 +5822,12 @@ ddi_ffs(long mask)
return (ffs(mask));
}
+int
+ddi_ffsll(long long mask)
+{
+ return (ffs(mask));
+}
+
/*
* Find last bit set. Take mask and clear
* all but the most significant bit, and
@@ -5833,8 +5839,14 @@ ddi_ffs(long mask)
int
ddi_fls(long mask)
{
+ return (ddi_flsll(mask));
+}
+
+int
+ddi_flsll(long long mask)
+{
while (mask) {
- long nx;
+ long long nx;
if ((nx = (mask & (mask - 1))) == 0)
break;
diff --git a/usr/src/uts/common/os/sysent.c b/usr/src/uts/common/os/sysent.c
index ab12de3935..41f421c505 100644
--- a/usr/src/uts/common/os/sysent.c
+++ b/usr/src/uts/common/os/sysent.c
@@ -23,6 +23,7 @@
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2012 Milan Jurik. All rights reserved.
* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
* Copyright (c) 2018, Joyent, Inc.
* Copyright 2020 Oxide Computer Company
*/
@@ -62,8 +63,7 @@ struct mmaplf32a;
int access(char *, int);
int alarm(int);
int auditsys(struct auditcalls *, rval_t *);
-int64_t brandsys(int, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
- uintptr_t);
+int64_t brandsys(int, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
intptr_t brk(caddr_t);
int chdir(char *);
int chmod(char *, int);
@@ -645,7 +645,7 @@ struct sysent sysent[NSYSCALL] =
SYSENT_NOSYS(),
SYSENT_C("llseek", llseek32, 4)),
/* 176 */ SYSENT_LOADABLE(), /* inst_sync */
- /* 177 */ SYSENT_CI("brandsys", brandsys, 6),
+ /* 177 */ SYSENT_CI("brandsys", brandsys, 5),
/* 178 */ SYSENT_LOADABLE(), /* kaio */
/* 179 */ SYSENT_LOADABLE(), /* cpc */
/* 180 */ SYSENT_CI("lgrpsys", lgrpsys, 3),
@@ -1000,7 +1000,7 @@ struct sysent sysent32[NSYSCALL] =
/* 174 */ SYSENT_CI("pwrite", pwrite32, 4),
/* 175 */ SYSENT_C("llseek", llseek32, 4),
/* 176 */ SYSENT_LOADABLE32(), /* inst_sync */
- /* 177 */ SYSENT_CI("brandsys", brandsys, 6),
+ /* 177 */ SYSENT_CI("brandsys", brandsys, 5),
/* 178 */ SYSENT_LOADABLE32(), /* kaio */
/* 179 */ SYSENT_LOADABLE32(), /* cpc */
/* 180 */ SYSENT_CI("lgrpsys", lgrpsys, 3),
@@ -1092,18 +1092,20 @@ char **syscallnames;
systrace_sysent_t *systrace_sysent;
void (*systrace_probe)(dtrace_id_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
/*ARGSUSED*/
void
systrace_stub(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
- uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
+ uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5,
+ uintptr_t arg6, uintptr_t arg7)
{}
/*ARGSUSED*/
int64_t
dtrace_systrace_syscall(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
- uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
+ uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6,
+ uintptr_t arg7)
{
systrace_sysent_t *sy = &systrace_sysent[curthread->t_sysnum];
dtrace_id_t id;
@@ -1111,7 +1113,8 @@ dtrace_systrace_syscall(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
proc_t *p;
if ((id = sy->stsy_entry) != DTRACE_IDNONE)
- (*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5);
+ (*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7);
/*
* We want to explicitly allow DTrace consumers to stop a process
@@ -1125,14 +1128,15 @@ dtrace_systrace_syscall(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
}
mutex_exit(&p->p_lock);
- rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5);
+ rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5,
+ arg6, arg7);
if (ttolwp(curthread)->lwp_errno != 0)
rval = -1;
if ((id = sy->stsy_return) != DTRACE_IDNONE)
(*systrace_probe)(id, (uintptr_t)rval, (uintptr_t)rval,
- (uintptr_t)((int64_t)rval >> 32), 0, 0, 0);
+ (uintptr_t)((int64_t)rval >> 32), 0, 0, 0, 0, 0);
return (rval);
}
@@ -1144,7 +1148,8 @@ systrace_sysent_t *systrace_sysent32;
/*ARGSUSED*/
int64_t
dtrace_systrace_syscall32(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
- uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
+ uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6,
+ uintptr_t arg7)
{
systrace_sysent_t *sy = &systrace_sysent32[curthread->t_sysnum];
dtrace_id_t id;
@@ -1152,7 +1157,8 @@ dtrace_systrace_syscall32(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
proc_t *p;
if ((id = sy->stsy_entry) != DTRACE_IDNONE)
- (*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5);
+ (*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5, arg6,
+ arg7);
/*
* We want to explicitly allow DTrace consumers to stop a process
@@ -1166,14 +1172,15 @@ dtrace_systrace_syscall32(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
}
mutex_exit(&p->p_lock);
- rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5);
+ rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5, arg6,
+ arg7);
if (ttolwp(curthread)->lwp_errno != 0)
rval = -1;
if ((id = sy->stsy_return) != DTRACE_IDNONE)
(*systrace_probe)(id, (uintptr_t)rval, (uintptr_t)rval,
- (uintptr_t)((uint64_t)rval >> 32), 0, 0, 0);
+ (uintptr_t)((uint64_t)rval >> 32), 0, 0, 0, 0, 0);
return (rval);
}
@@ -1201,5 +1208,5 @@ dtrace_systrace_rtt(void)
}
if ((id = sy->stsy_return) != DTRACE_IDNONE)
- (*systrace_probe)(id, 0, 0, 0, 0, 0, 0);
+ (*systrace_probe)(id, 0, 0, 0, 0, 0, 0, 0, 0);
}
diff --git a/usr/src/uts/common/os/timer.c b/usr/src/uts/common/os/timer.c
index c78a545360..f587430625 100644
--- a/usr/src/uts/common/os/timer.c
+++ b/usr/src/uts/common/os/timer.c
@@ -82,6 +82,7 @@ timer_lock(proc_t *p, itimer_t *it)
* waiters. p_lock must be held on entry; it will not be dropped by
* timer_unlock().
*/
+/* ARGSUSED */
static void
timer_unlock(proc_t *p, itimer_t *it)
{
@@ -139,7 +140,7 @@ timer_delete_locked(proc_t *p, timer_t tid, itimer_t *it)
it->it_backend->clk_timer_delete(it);
- if (it->it_portev) {
+ if (it->it_flags & IT_PORT) {
mutex_enter(&it->it_mutex);
if (it->it_portev) {
port_kevent_t *pev;
@@ -201,20 +202,20 @@ timer_delete_locked(proc_t *p, timer_t tid, itimer_t *it)
static itimer_t *
timer_grab(proc_t *p, timer_t tid)
{
- itimer_t **itp, *it;
+ itimer_t *it;
if (tid < 0) {
return (NULL);
}
mutex_enter(&p->p_lock);
-
- if ((itp = p->p_itimer) == NULL || tid >= p->p_itimer_sz ||
- (it = itp[tid]) == NULL) {
+ if (p->p_itimer == NULL || tid >= p->p_itimer_sz ||
+ (it = p->p_itimer[tid]) == NULL) {
mutex_exit(&p->p_lock);
return (NULL);
}
+ /* This may drop p_lock temporarily. */
timer_lock(p, it);
if (it->it_lock & ITLK_REMOVE) {
@@ -236,7 +237,7 @@ timer_grab(proc_t *p, timer_t tid)
* should not be held on entry; timer_release() will acquire p_lock but
* will drop it before returning.
*/
-static void
+void
timer_release(proc_t *p, itimer_t *it)
{
mutex_enter(&p->p_lock);
@@ -249,7 +250,7 @@ timer_release(proc_t *p, itimer_t *it)
* p_lock should not be held on entry; timer_delete_grabbed() will acquire
* p_lock, but will drop it before returning.
*/
-static void
+void
timer_delete_grabbed(proc_t *p, timer_t tid, itimer_t *it)
{
mutex_enter(&p->p_lock);
@@ -464,6 +465,9 @@ timer_fire(itimer_t *it)
it->it_pending = 1;
port_send_event((port_kevent_t *)it->it_portev);
mutex_exit(&it->it_mutex);
+ } else if (it->it_flags & IT_CALLBACK) {
+ it->it_cb_func(it);
+ ASSERT(MUTEX_NOT_HELD(&it->it_mutex));
} else if (it->it_flags & IT_SIGNAL) {
it->it_pending = 1;
mutex_exit(&it->it_mutex);
@@ -580,85 +584,27 @@ done:
return (B_TRUE);
}
+/*
+ * Setup a timer
+ *
+ * This allocates an itimer_t (including a timer_t ID and slot in the process),
+ * wires it up according to the provided sigevent, and associates it with the
+ * desired clock backend. Upon successful completion, the timer will be
+ * locked, preventing it from being armed via timer_settime() or deleted via
+ * timer_delete(). This gives the caller a chance to perform any last minute
+ * manipulations (such as configuring the IT_CALLBACK functionality and/or
+ * copying the timer_t out to userspace) before using timer_release() to unlock
+ * it or timer_delete_grabbed() to delete it.
+ */
int
-timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
+timer_setup(clock_backend_t *backend, struct sigevent *evp, port_notify_t *pnp,
+ itimer_t **itp, timer_t *tidp)
{
- struct sigevent ev;
proc_t *p = curproc;
- clock_backend_t *backend;
+ int error = 0;
itimer_t *it;
sigqueue_t *sigq;
- cred_t *cr = CRED();
- int error = 0;
- timer_t i;
- port_notify_t tim_pnevp;
- port_kevent_t *pkevp = NULL;
-
- if ((backend = CLOCK_BACKEND(clock)) == NULL)
- return (set_errno(EINVAL));
-
- if (evp != NULL) {
- /*
- * short copyin() for binary compatibility
- * fetch oldsigevent to determine how much to copy in.
- */
- if (get_udatamodel() == DATAMODEL_NATIVE) {
- if (copyin(evp, &ev, sizeof (struct oldsigevent)))
- return (set_errno(EFAULT));
-
- if (ev.sigev_notify == SIGEV_PORT ||
- ev.sigev_notify == SIGEV_THREAD) {
- if (copyin(ev.sigev_value.sival_ptr, &tim_pnevp,
- sizeof (port_notify_t)))
- return (set_errno(EFAULT));
- }
-#ifdef _SYSCALL32_IMPL
- } else {
- struct sigevent32 ev32;
- port_notify32_t tim_pnevp32;
-
- if (copyin(evp, &ev32, sizeof (struct oldsigevent32)))
- return (set_errno(EFAULT));
- ev.sigev_notify = ev32.sigev_notify;
- ev.sigev_signo = ev32.sigev_signo;
- /*
- * See comment in sigqueue32() on handling of 32-bit
- * sigvals in a 64-bit kernel.
- */
- ev.sigev_value.sival_int = ev32.sigev_value.sival_int;
- if (ev.sigev_notify == SIGEV_PORT ||
- ev.sigev_notify == SIGEV_THREAD) {
- if (copyin((void *)(uintptr_t)
- ev32.sigev_value.sival_ptr,
- (void *)&tim_pnevp32,
- sizeof (port_notify32_t)))
- return (set_errno(EFAULT));
- tim_pnevp.portnfy_port =
- tim_pnevp32.portnfy_port;
- tim_pnevp.portnfy_user =
- (void *)(uintptr_t)tim_pnevp32.portnfy_user;
- }
-#endif
- }
- switch (ev.sigev_notify) {
- case SIGEV_NONE:
- break;
- case SIGEV_SIGNAL:
- if (ev.sigev_signo < 1 || ev.sigev_signo >= NSIG)
- return (set_errno(EINVAL));
- break;
- case SIGEV_THREAD:
- case SIGEV_PORT:
- break;
- default:
- return (set_errno(EINVAL));
- }
- } else {
- /*
- * Use the clock's default sigevent (this is a structure copy).
- */
- ev = backend->clk_default;
- }
+ timer_t tid;
/*
* We'll allocate our sigqueue now, before we grab p_lock.
@@ -674,29 +620,25 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
mutex_init(&it->it_mutex, NULL, MUTEX_DEFAULT, NULL);
mutex_enter(&p->p_lock);
- if (!timer_get_id(p, &i)) {
+ if (!timer_get_id(p, &tid)) {
mutex_exit(&p->p_lock);
- kmem_cache_free(clock_timer_cache, it);
kmem_free(sigq, sizeof (sigqueue_t));
return (set_errno(EAGAIN));
}
- ASSERT(i < p->p_itimer_sz && p->p_itimer[i] == NULL);
+ ASSERT(tid < p->p_itimer_sz && p->p_itimer[tid] == NULL);
/*
* If we develop other notification mechanisms, this will need
* to call into (yet another) backend.
*/
- sigq->sq_info.si_signo = ev.sigev_signo;
- if (evp == NULL)
- sigq->sq_info.si_value.sival_int = i;
- else
- sigq->sq_info.si_value = ev.sigev_value;
+ sigq->sq_info.si_signo = evp->sigev_signo;
+ sigq->sq_info.si_value = evp->sigev_value;
sigq->sq_info.si_code = SI_TIMER;
sigq->sq_info.si_pid = p->p_pid;
sigq->sq_info.si_ctid = PRCTID(p);
sigq->sq_info.si_zoneid = getzoneid();
- sigq->sq_info.si_uid = crgetruid(cr);
+ sigq->sq_info.si_uid = crgetruid(CRED());
sigq->sq_func = timer_signal;
sigq->sq_next = NULL;
sigq->sq_backptr = it;
@@ -704,9 +646,12 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
it->it_backend = backend;
it->it_lock = ITLK_LOCKED;
- if (ev.sigev_notify == SIGEV_THREAD ||
- ev.sigev_notify == SIGEV_PORT) {
+ if (evp->sigev_notify == SIGEV_THREAD ||
+ evp->sigev_notify == SIGEV_PORT) {
int port;
+ port_kevent_t *pkevp = NULL;
+
+ ASSERT(pnp != NULL);
/*
* This timer is programmed to use event port notification when
@@ -726,7 +671,7 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
*/
it->it_flags |= IT_PORT;
- port = tim_pnevp.portnfy_port;
+ port = pnp->portnfy_port;
/* associate timer as event source with the port */
error = port_associate_ksource(port, PORT_SOURCE_TIMER,
@@ -736,7 +681,7 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
mutex_exit(&p->p_lock);
kmem_cache_free(clock_timer_cache, it);
kmem_free(sigq, sizeof (sigqueue_t));
- return (set_errno(error));
+ return (error);
}
/* allocate an event structure/slot */
@@ -748,21 +693,21 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
mutex_exit(&p->p_lock);
kmem_cache_free(clock_timer_cache, it);
kmem_free(sigq, sizeof (sigqueue_t));
- return (set_errno(error));
+ return (error);
}
/* initialize event data */
- port_init_event(pkevp, i, tim_pnevp.portnfy_user,
+ port_init_event(pkevp, tid, pnp->portnfy_user,
timer_port_callback, it);
it->it_portev = pkevp;
it->it_portfd = port;
} else {
- if (ev.sigev_notify == SIGEV_SIGNAL)
+ if (evp->sigev_notify == SIGEV_SIGNAL)
it->it_flags |= IT_SIGNAL;
}
/* Populate the slot now that the timer is prepped. */
- p->p_itimer[i] = it;
+ p->p_itimer[tid] = it;
mutex_exit(&p->p_lock);
/*
@@ -775,17 +720,8 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid)
it->it_lwp = ttolwp(curthread);
it->it_proc = p;
- if (copyout(&i, tid, sizeof (timer_t)) != 0) {
- error = EFAULT;
- goto err;
- }
-
- /*
- * If we're here, then we have successfully created the timer; we
- * just need to release the timer and return.
- */
- timer_release(p, it);
-
+ *itp = it;
+ *tidp = tid;
return (0);
err:
@@ -796,11 +732,115 @@ err:
* impossible for a removal to be pending.
*/
ASSERT(!(it->it_lock & ITLK_REMOVE));
- timer_delete_grabbed(p, i, it);
+ timer_delete_grabbed(p, tid, it);
+
+ return (error);
+}
+
+
+int
+timer_create(clockid_t clock, struct sigevent *evp, timer_t *tidp)
+{
+ int error = 0;
+ proc_t *p = curproc;
+ clock_backend_t *backend;
+ struct sigevent ev;
+ itimer_t *it;
+ timer_t tid;
+ port_notify_t tim_pnevp;
+
+ if ((backend = CLOCK_BACKEND(clock)) == NULL)
+ return (set_errno(EINVAL));
+
+ if (evp != NULL) {
+ /*
+ * short copyin() for binary compatibility
+ * fetch oldsigevent to determine how much to copy in.
+ */
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(evp, &ev, sizeof (struct oldsigevent)))
+ return (set_errno(EFAULT));
+
+ if (ev.sigev_notify == SIGEV_PORT ||
+ ev.sigev_notify == SIGEV_THREAD) {
+ if (copyin(ev.sigev_value.sival_ptr, &tim_pnevp,
+ sizeof (port_notify_t)))
+ return (set_errno(EFAULT));
+ }
+#ifdef _SYSCALL32_IMPL
+ } else {
+ struct sigevent32 ev32;
+ port_notify32_t tim_pnevp32;
- return (set_errno(error));
+ if (copyin(evp, &ev32, sizeof (struct oldsigevent32)))
+ return (set_errno(EFAULT));
+ ev.sigev_notify = ev32.sigev_notify;
+ ev.sigev_signo = ev32.sigev_signo;
+ /*
+ * See comment in sigqueue32() on handling of 32-bit
+ * sigvals in a 64-bit kernel.
+ */
+ ev.sigev_value.sival_int = ev32.sigev_value.sival_int;
+ if (ev.sigev_notify == SIGEV_PORT ||
+ ev.sigev_notify == SIGEV_THREAD) {
+ if (copyin((void *)(uintptr_t)
+ ev32.sigev_value.sival_ptr,
+ (void *)&tim_pnevp32,
+ sizeof (port_notify32_t)))
+ return (set_errno(EFAULT));
+ tim_pnevp.portnfy_port =
+ tim_pnevp32.portnfy_port;
+ tim_pnevp.portnfy_user =
+ (void *)(uintptr_t)tim_pnevp32.portnfy_user;
+ }
+#endif
+ }
+ switch (ev.sigev_notify) {
+ case SIGEV_NONE:
+ break;
+ case SIGEV_SIGNAL:
+ if (ev.sigev_signo < 1 || ev.sigev_signo >= NSIG)
+ return (set_errno(EINVAL));
+ break;
+ case SIGEV_THREAD:
+ case SIGEV_PORT:
+ break;
+ default:
+ return (set_errno(EINVAL));
+ }
+ } else {
+ /*
+ * Use the clock's default sigevent (this is a structure copy).
+ */
+ ev = backend->clk_default;
+ }
+
+ if ((error = timer_setup(backend, &ev, &tim_pnevp, &it, &tid)) != 0) {
+ return (set_errno(error));
+ }
+
+ /*
+ * Populate si_value with the timer ID if no sigevent was passed in.
+ */
+ if (evp == NULL) {
+ it->it_sigq->sq_info.si_value.sival_int = tid;
+ }
+
+ if (copyout(&tid, tidp, sizeof (timer_t)) != 0) {
+ timer_delete_grabbed(p, tid, it);
+ return (set_errno(EFAULT));
+ }
+
+ /*
+ * If we're here, then we have successfully created the timer; we
+ * just need to release the timer and return.
+ */
+ timer_release(p, it);
+
+ return (0);
}
+
int
timer_gettime(timer_t tid, itimerspec_t *val)
{
@@ -923,17 +963,20 @@ timer_lwpexit(void)
uint_t i;
proc_t *p = curproc;
klwp_t *lwp = ttolwp(curthread);
- itimer_t *it, **itp;
+ itimer_t *it;
ASSERT(MUTEX_HELD(&p->p_lock));
- if ((itp = p->p_itimer) == NULL)
+ if (p->p_itimer == NULL) {
return;
+ }
for (i = 0; i < p->p_itimer_sz; i++) {
- if ((it = itp[i]) == NULL)
+ if ((it = p->p_itimer[i]) == NULL) {
continue;
+ }
+ /* This may drop p_lock temporarily. */
timer_lock(p, it);
if ((it->it_lock & ITLK_REMOVE) || it->it_lwp != lwp) {
@@ -967,17 +1010,19 @@ timer_lwpbind()
uint_t i;
proc_t *p = curproc;
klwp_t *lwp = ttolwp(curthread);
- itimer_t *it, **itp;
+ itimer_t *it;
ASSERT(MUTEX_HELD(&p->p_lock));
- if ((itp = p->p_itimer) == NULL)
+ if (p->p_itimer == NULL) {
return;
+ }
for (i = 0; i < p->p_itimer_sz; i++) {
- if ((it = itp[i]) == NULL)
+ if ((it = p->p_itimer[i]) == NULL)
continue;
+ /* This may drop p_lock temporarily. */
timer_lock(p, it);
if (!(it->it_lock & ITLK_REMOVE) && it->it_lwp == lwp) {
@@ -1068,7 +1113,7 @@ timer_close_port(void *arg, int port, pid_t pid, int lastclose)
for (tid = 0; tid < timer_max; tid++) {
if ((it = timer_grab(p, tid)) == NULL)
continue;
- if (it->it_portev) {
+ if (it->it_flags & IT_PORT) {
mutex_enter(&it->it_mutex);
if (it->it_portfd == port) {
port_kevent_t *pev;
diff --git a/usr/src/uts/common/os/vm_pageout.c b/usr/src/uts/common/os/vm_pageout.c
index b65a6cea2f..0cfcf80d62 100644
--- a/usr/src/uts/common/os/vm_pageout.c
+++ b/usr/src/uts/common/os/vm_pageout.c
@@ -27,6 +27,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2018 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -63,6 +64,7 @@
#include <sys/callb.h>
#include <sys/mem_cage.h>
#include <sys/time.h>
+#include <sys/zone.h>
#include <sys/stdbool.h>
#include <vm/hat.h>
@@ -239,15 +241,22 @@ pgcnt_t lotsfree = 0;
pgcnt_t needfree = 0;
pgcnt_t throttlefree = 0;
pgcnt_t pageout_reserve = 0;
+pri_t pageout_pri;
pgcnt_t deficit;
pgcnt_t nscan;
pgcnt_t desscan;
+/* kstats */
+uint64_t low_mem_scan;
+uint64_t zone_cap_scan;
+
+#define MAX_PSCAN_THREADS 16
+
/*
- * Values for min_pageout_nsec, max_pageout_nsec and pageout_nsec are the
- * number of nanoseconds in each wakeup cycle that gives the equivalent of some
- * underlying %CPU duty cycle.
+ * Values for min_pageout_nsec, max_pageout_nsec, pageout_nsec and
+ * zone_pageout_nsec are the number of nanoseconds in each wakeup cycle
+ * that gives the equivalent of some underlying %CPU duty cycle.
*
* min_pageout_nsec:
* nanoseconds/wakeup equivalent of min_percent_cpu.
@@ -259,15 +268,31 @@ pgcnt_t desscan;
* Number of nanoseconds budgeted for each wakeup cycle.
* Computed each time around by schedpaging().
* Varies between min_pageout_nsec and max_pageout_nsec,
- * depending on memory pressure.
+ * depending on memory pressure or zones over their cap.
+ *
+ * zone_pageout_nsec:
+ * Number of nanoseconds budget for each cycle when a zone
+ * is over its memory cap. If this is zero, then the value
+ * of max_pageout_nsec is used instead.
*/
static hrtime_t min_pageout_nsec;
static hrtime_t max_pageout_nsec;
static hrtime_t pageout_nsec;
+static hrtime_t zone_pageout_nsec;
-static uint_t reset_hands;
+static boolean_t reset_hands[MAX_PSCAN_THREADS];
#define PAGES_POLL_MASK 1023
+#define SCHEDPAGING_HZ 4
+
+/*
+ * despagescanners:
+ * The desired number of page scanner threads. The value can be set in
+ * /etc/system or tuned directly with 'mdb -kw'. The system will bring
+ * the actual number of threads into line with the desired number. If set
+ * to an invalid value, the system will correct the setting.
+ */
+uint_t despagescanners = 0;
/*
* pageout_sample_lim:
@@ -293,26 +318,29 @@ static uint_t reset_hands;
* pageout_scanner(), which then sets this value once per system boot after
* enough samples have been recorded (pageout_sample_cnt). Once set, this
* new value is used for fastscan and handspreadpages.
- *
- * sample_start, sample_end:
- * The hrtime at which the last pageout_scanner() sample began and ended.
*/
typedef hrtime_t hrrate_t;
static uint64_t pageout_sample_lim = 4;
static uint64_t pageout_sample_cnt = 0;
static pgcnt_t pageout_sample_pages = 0;
+static hrtime_t pageout_sample_etime = 0;
static hrrate_t pageout_rate = 0;
static pgcnt_t pageout_new_spread = 0;
-static hrtime_t pageout_cycle_nsec;
-static hrtime_t sample_start, sample_end;
-static hrtime_t pageout_sample_etime = 0;
+/* True if the page scanner is first starting up */
+#define PAGE_SCAN_STARTUP (pageout_sample_cnt < pageout_sample_lim)
+
+/* The current number of page scanner threads */
+static uint_t n_page_scanners = 1;
+/* The number of page scanner threads that are actively scanning. */
+static uint_t pageouts_running;
/*
* Record number of times a pageout_scanner() wakeup cycle finished because it
* timed out (exceeded its CPU budget), rather than because it visited
- * its budgeted number of pages.
+ * its budgeted number of pages. This is only done when scanning under low
+ * free memory conditions, not when scanning for zones over their cap.
*/
uint64_t pageout_timeouts = 0;
@@ -356,9 +384,10 @@ static struct clockinit {
pgcnt_t ci_fastscan;
pgcnt_t ci_slowscan;
pgcnt_t ci_handspreadpages;
+ uint_t ci_despagescanners;
} clockinit = { .ci_init = false };
-static pgcnt_t
+static inline pgcnt_t
clamp(pgcnt_t value, pgcnt_t minimum, pgcnt_t maximum)
{
if (value < minimum) {
@@ -381,6 +410,83 @@ tune(pgcnt_t initval, pgcnt_t initval_ceiling, pgcnt_t defval)
}
/*
+ * Local boolean to control scanning when zones are over their cap. Avoids
+ * accessing the zone_num_over_cap variable except within schedpaging(), which
+ * only runs periodically. This is here only to reduce our access to
+ * zone_num_over_cap, since it is already accessed a lot during paging, and
+ * the page scanner accesses the zones_over variable on each page during a
+ * scan. There is no lock needed for zone_num_over_cap since schedpaging()
+ * doesn't modify the variable, it only cares if the variable is 0 or non-0.
+ */
+static boolean_t zones_over = B_FALSE;
+
+/*
+ * On large memory systems, multiple instances of the page scanner are run,
+ * each responsible for a separate region of memory. This speeds up page
+ * invalidation under low memory conditions.
+ *
+ * despagescanners can be set in /etc/system or via mdb and it will
+ * be used as a guide for how many page scanners to create; the value
+ * will be adjusted if it is not sensible. Otherwise, the number of
+ * page scanners is determined dynamically based on handspreadpages.
+ */
+static void
+recalc_pagescanners(void)
+{
+ pgcnt_t sz;
+ uint_t des;
+
+ /* If the initial calibration has not been done, take no action. */
+ if (pageout_new_spread == 0)
+ return;
+
+ /*
+ * If the desired number of scanners is set in /etc/system
+ * then try to use it.
+ */
+ if (despagescanners == 0 && clockinit.ci_despagescanners != 0)
+ despagescanners = clockinit.ci_despagescanners;
+
+ if (despagescanners != 0) {
+ /*
+ * We have a desired number of page scanners, either from
+ * /etc/system or set via mdb. Try and use it (it will be
+ * clamped below).
+ */
+ des = despagescanners;
+ } else {
+ /*
+ * Calculate the number of desired scanners based on the
+ * system's memory size.
+ *
+ * A 64GiB region size is used as the basis for calculating how
+ * many scanner threads should be created. For systems with up
+ * to 64GiB of RAM, a single thread is used; for very large
+ * memory systems the threads are limited to MAX_PSCAN_THREADS.
+ */
+ sz = btop(64ULL << 30);
+
+ if (sz > looppages) {
+ des = 1;
+ } else {
+ pgcnt_t tmp = sz;
+
+ for (des = 1; tmp < looppages; des++)
+ tmp += sz;
+ }
+ }
+
+ /*
+ * clamp the number of scanners so that we are under MAX_PSCAN_THREADS
+ * and so that each scanner covers at least 10% more than
+ * handspreadpages.
+ */
+ des = clamp(des, 1,
+ looppages / (handspreadpages + handspreadpages / 10));
+ despagescanners = clamp(des, 1, MAX_PSCAN_THREADS);
+}
+
+/*
* Set up the paging constants for the clock algorithm used by
* pageout_scanner(), and by the virtual memory system overall. See the
* comments at the top of this file for more information about the threshold
@@ -394,7 +500,6 @@ tune(pgcnt_t initval, pgcnt_t initval_ceiling, pgcnt_t defval)
void
setupclock(void)
{
- pgcnt_t defval;
bool half = (pageout_threshold_style == 1);
bool recalc = true;
@@ -423,6 +528,7 @@ setupclock(void)
clockinit.ci_fastscan = fastscan;
clockinit.ci_slowscan = slowscan;
clockinit.ci_handspreadpages = handspreadpages;
+ clockinit.ci_despagescanners = despagescanners;
/*
* The first call does not trigger a recalculation, only
@@ -604,7 +710,7 @@ setupclock(void)
}
/*
- * Handspreadpages is distance (in pages) between front and back
+ * Handspreadpages is the distance (in pages) between front and back
* pageout daemon hands. The amount of time to reclaim a page
* once pageout examines it increases with this distance and
* decreases as the scan rate rises. It must be < the amount
@@ -640,12 +746,31 @@ setupclock(void)
}
/*
- * If we have been called to recalculate the parameters, set a flag to
- * re-evaluate the clock hand pointers.
+ * Establish the minimum and maximum length of time to be spent
+ * scanning pages per wakeup, limiting the scanner duty cycle. The
+ * input percentage values (0-100) must be converted to a fraction of
+ * the number of nanoseconds in a second of wall time, then further
+ * scaled down by the number of scanner wakeups in a second.
*/
- if (recalc) {
- reset_hands = 1;
- }
+ min_pageout_nsec = MAX(1,
+ NANOSEC * min_percent_cpu / 100 / SCHEDPAGING_HZ);
+ max_pageout_nsec = MAX(min_pageout_nsec,
+ NANOSEC * max_percent_cpu / 100 / SCHEDPAGING_HZ);
+
+ /*
+ * If not called for recalculation, return and skip the remaining
+ * steps.
+ */
+ if (!recalc)
+ return;
+
+ /*
+ * Set a flag to re-evaluate the clock hand positions.
+ */
+ for (uint_t i = 0; i < MAX_PSCAN_THREADS; i++)
+ reset_hands[i] = B_TRUE;
+
+ recalc_pagescanners();
}
/*
@@ -659,9 +784,8 @@ setupclock(void)
* in its next pass; schedpaging() sets this value based on the amount of
* currently available memory.
*/
-#define SCHEDPAGING_HZ 4
-static kmutex_t pageout_mutex; /* held while pageout or schedpaging running */
+static kmutex_t pageout_mutex;
/*
* Pool of available async pageout putpage requests.
@@ -689,9 +813,9 @@ static bool pageout_pushing = false;
static uint64_t pageout_pushcount = 0;
static uint64_t pageout_pushcount_seen = 0;
-static int async_list_size = 256; /* number of async request structs */
+static int async_list_size = 8192; /* number of async request structs */
-static void pageout_scanner(void);
+static void pageout_scanner(void *);
/*
* If a page is being shared more than "po_share" times
@@ -721,24 +845,17 @@ schedpaging(void *arg)
kcage_cageout_wakeup();
if (mutex_tryenter(&pageout_mutex)) {
- /* pageout() not running */
+
+ if (pageouts_running != 0)
+ goto out;
+
+ /* No pageout scanner threads running. */
nscan = 0;
vavail = freemem - deficit;
if (pageout_new_spread != 0)
vavail -= needfree;
- if (vavail < 0)
- vavail = 0;
- if (vavail > lotsfree)
- vavail = lotsfree;
+ vavail = clamp(vavail, 0, lotsfree);
- /*
- * Fix for 1161438 (CRS SPR# 73922). All variables
- * in the original calculation for desscan were 32 bit signed
- * ints. As freemem approaches 0x0 on a system with 1 Gig or
- * more of memory, the calculation can overflow. When this
- * happens, desscan becomes negative and pageout_scanner()
- * stops paging out.
- */
if (needfree > 0 && pageout_new_spread == 0) {
/*
* If we've not yet collected enough samples to
@@ -764,14 +881,92 @@ schedpaging(void *arg)
pageout_nsec = min_pageout_nsec + (lotsfree - vavail) *
(max_pageout_nsec - min_pageout_nsec) / nz(lotsfree);
- if (freemem < lotsfree + needfree ||
- pageout_sample_cnt < pageout_sample_lim) {
+ DTRACE_PROBE2(schedpage__calc, pgcnt_t, desscan, hrtime_t,
+ pageout_nsec);
+
+ if (pageout_new_spread != 0 && despagescanners != 0 &&
+ despagescanners != n_page_scanners) {
+ /*
+ * We have finished the pagescan initialisation and the
+ * desired number of page scanners has changed, either
+ * because initialisation just finished, because of a
+ * memory DR, or because despagescanners has been
+ * modified on the fly (i.e. by mdb).
+ */
+ uint_t i, curr_nscan = n_page_scanners;
+
+ /* Re-validate despagescanners */
+ recalc_pagescanners();
+
+ n_page_scanners = despagescanners;
+
+ for (i = 0; i < MAX_PSCAN_THREADS; i++)
+ reset_hands[i] = B_TRUE;
+
+ /* If we need more scanners, start them now. */
+ if (n_page_scanners > curr_nscan) {
+ for (i = curr_nscan; i < n_page_scanners; i++) {
+ (void) lwp_kernel_create(proc_pageout,
+ pageout_scanner,
+ (void *)(uintptr_t)i, TS_RUN,
+ pageout_pri);
+ }
+ }
+
+ /*
+ * If the number of scanners has decreased, trigger a
+ * wakeup so that the excess threads will terminate.
+ */
+ if (n_page_scanners < curr_nscan) {
+ WAKE_PAGEOUT_SCANNER();
+ }
+ }
+
+ zones_over = B_FALSE;
+
+ if (PAGE_SCAN_STARTUP) {
/*
- * Either we need more memory, or we still need to
- * measure the average scan rate. Wake the scanner.
+ * We still need to measure the rate at which the
+ * system is able to scan pages of memory. Each of
+ * these initial samples is a scan of as much system
+ * memory as practical, regardless of whether or not we
+ * are experiencing memory pressure.
*/
- DTRACE_PROBE(pageout__cv__signal);
- cv_signal(&proc_pageout->p_cv);
+ desscan = total_pages;
+ pageout_nsec = max_pageout_nsec;
+
+ DTRACE_PROBE(schedpage__wake__sample);
+ WAKE_PAGEOUT_SCANNER();
+ } else if (freemem < lotsfree + needfree) {
+ /*
+ * We need more memory.
+ */
+ low_mem_scan++;
+
+ DTRACE_PROBE(schedpage__wake__low);
+ WAKE_PAGEOUT_SCANNER();
+ } else if (zone_num_over_cap > 0) {
+ /*
+ * One of more zones are over their cap.
+ */
+
+ /* No page limit */
+ desscan = total_pages;
+
+ /*
+ * Increase the scanning CPU% to the max. This implies
+ * 80% of one CPU/sec if the scanner can run each
+ * opportunity. Can also be tuned via setting
+ * zone_pageout_nsec in /etc/system or with mdb.
+ */
+ pageout_nsec = (zone_pageout_nsec != 0) ?
+ zone_pageout_nsec : max_pageout_nsec;
+
+ zones_over = B_TRUE;
+ zone_cap_scan++;
+
+ DTRACE_PROBE(schedpage__wake__zone);
+ WAKE_PAGEOUT_SCANNER();
} else {
/*
* There are enough free pages, no need to
@@ -784,6 +979,7 @@ schedpaging(void *arg)
po_share >>= 1;
}
}
+out:
mutex_exit(&pageout_mutex);
}
@@ -812,37 +1008,39 @@ uint_t dopageout = 1;
/*
* The page out daemon, which runs as process 2.
*
- * As long as there are at least lotsfree pages,
- * this process is not run. When the number of free
- * pages stays in the range desfree to lotsfree,
- * this daemon runs through the pages in the loop
- * at a rate determined in schedpaging(). Pageout manages
- * two hands on the clock. The front hand moves through
- * memory, clearing the reference bit,
- * and stealing pages from procs that are over maxrss.
- * The back hand travels a distance behind the front hand,
- * freeing the pages that have not been referenced in the time
- * since the front hand passed. If modified, they are pushed to
- * swap before being freed.
+ * The daemon treats physical memory as a circular array of pages and scans
+ * the pages using a 'two-handed clock' algorithm. The front hand moves
+ * through the pages, clearing the reference bit. The back hand travels a
+ * distance (handspreadpages) behind the front hand, freeing the pages that
+ * have not been referenced in the time since the front hand passed. If
+ * modified, they are first written to their backing store before being
+ * freed.
+ *
+ * In order to make page invalidation more responsive on machines with
+ * larger memory, multiple pageout_scanner threads may be created. In this
+ * case, each thread is given a segment of the memory "clock face" so that
+ * memory can be reclaimed more quickly.
*
- * There are 2 threads that act on behalf of the pageout process.
- * One thread scans pages (pageout_scanner) and frees them up if
- * they don't require any VOP_PUTPAGE operation. If a page must be
- * written back to its backing store, the request is put on a list
- * and the other (pageout) thread is signaled. The pageout thread
- * grabs VOP_PUTPAGE requests from the list, and processes them.
- * Some filesystems may require resources for the VOP_PUTPAGE
- * operations (like memory) and hence can block the pageout
- * thread, but the scanner thread can still operate. There is still
- * no guarantee that memory deadlocks cannot occur.
+ * As long as there are at least lotsfree pages, or no zones over their
+ * cap, then pageout_scanner threads are not run. When pageout_scanner
+ * threads are running for case (a), all pages are considered for pageout.
+ * For case (b), only pages belonging to a zone over its cap will be
+ * considered for pageout.
*
- * For now, this thing is in very rough form.
+ * There are multiple threads that act on behalf of the pageout process. A
+ * set of threads scan pages (pageout_scanner) and frees them up if they
+ * don't require any VOP_PUTPAGE operation. If a page must be written back
+ * to its backing store, the request is put on a list and the other
+ * (pageout) thread is signaled. The pageout thread grabs VOP_PUTPAGE
+ * requests from the list, and processes them. Some filesystems may require
+ * resources for the VOP_PUTPAGE operations (like memory) and hence can
+ * block the pageout thread, but the scanner thread can still operate.
+ * There is still no guarantee that memory deadlocks cannot occur.
*/
void
pageout()
{
struct async_reqs *arg;
- pri_t pageout_pri;
int i;
pgcnt_t max_pushes;
callb_cpr_t cprinfo;
@@ -873,11 +1071,12 @@ pageout()
push_req[i].a_next = &push_req[i + 1];
}
- pageout_pri = curthread->t_pri;
+ pageout_pri = curthread->t_pri - 1;
- /* Create the pageout scanner thread. */
- (void) lwp_kernel_create(proc_pageout, pageout_scanner, NULL, TS_RUN,
- pageout_pri - 1);
+ /* Create the first pageout scanner thread. */
+ (void) lwp_kernel_create(proc_pageout, pageout_scanner,
+ (void *)0, /* this is instance 0, not NULL */
+ TS_RUN, pageout_pri);
/*
* kick off pageout scheduler.
@@ -912,6 +1111,8 @@ pageout()
pageout_pushing = true;
mutex_exit(&push_lock);
+ DTRACE_PROBE(pageout__push);
+
if (VOP_PUTPAGE(arg->a_vp, (offset_t)arg->a_off,
arg->a_len, arg->a_flags, arg->a_cred, NULL) == 0) {
pushes++;
@@ -934,14 +1135,19 @@ pageout()
* Kernel thread that scans pages looking for ones to free
*/
static void
-pageout_scanner(void)
+pageout_scanner(void *a)
{
- struct page *fronthand, *backhand;
+ struct page *fronthand, *backhand, *fronthandstart;
+ struct page *regionstart, *regionend;
uint_t laps;
callb_cpr_t cprinfo;
- pgcnt_t nscan_limit;
+ pgcnt_t nscan_cnt, tick;
pgcnt_t pcount;
- bool sampling;
+ bool bhwrapping, fhwrapping;
+ hrtime_t sample_start, sample_end;
+ uint_t inst = (uint_t)(uintptr_t)a;
+
+ VERIFY3U(inst, <, MAX_PSCAN_THREADS);
CALLB_CPR_INIT(&cprinfo, &pageout_mutex, callb_generic_cpr, "poscan");
mutex_enter(&pageout_mutex);
@@ -951,113 +1157,153 @@ pageout_scanner(void)
* the right point on the assumption that after one circuit things
* will have settled down, and restarts shouldn't be that often.
*/
+ reset_hands[inst] = B_TRUE;
- /*
- * Set the two clock hands to be separated by a reasonable amount,
- * but no more than 360 degrees apart.
- */
- backhand = page_first();
- if (handspreadpages >= total_pages) {
- fronthand = page_nextn(backhand, total_pages - 1);
- } else {
- fronthand = page_nextn(backhand, handspreadpages);
- }
-
- /*
- * Establish the minimum and maximum length of time to be spent
- * scanning pages per wakeup, limiting the scanner duty cycle. The
- * input percentage values (0-100) must be converted to a fraction of
- * the number of nanoseconds in a second of wall time, then further
- * scaled down by the number of scanner wakeups in a second:
- */
- min_pageout_nsec = MAX(1,
- NANOSEC * min_percent_cpu / 100 / SCHEDPAGING_HZ);
- max_pageout_nsec = MAX(min_pageout_nsec,
- NANOSEC * max_percent_cpu / 100 / SCHEDPAGING_HZ);
+ pageouts_running++;
+ mutex_exit(&pageout_mutex);
loop:
cv_signal_pageout();
+ mutex_enter(&pageout_mutex);
+ pageouts_running--;
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(&proc_pageout->p_cv, &pageout_mutex);
CALLB_CPR_SAFE_END(&cprinfo, &pageout_mutex);
+ pageouts_running++;
+ mutex_exit(&pageout_mutex);
/*
- * Check if pageout has been disabled for debugging purposes:
+ * Check if pageout has been disabled for debugging purposes.
*/
if (!dopageout) {
goto loop;
}
/*
- * One may reset the clock hands for debugging purposes. Hands will
- * also be reset if memory is added to or removed from the system.
+ * One may reset the clock hands and scanned region for debugging
+ * purposes. Hands will also be reset on first thread startup, if
+ * the number of scanning threads (n_page_scanners) changes, or if
+ * memory is added to, or removed from, the system.
*/
- if (reset_hands) {
- reset_hands = 0;
+ if (reset_hands[inst]) {
+ struct page *first;
+
+ reset_hands[inst] = B_FALSE;
+
+ if (inst >= n_page_scanners) {
+ /*
+ * The desired number of page scanners has been
+ * reduced and this instance is no longer wanted.
+ * Exit the lwp.
+ */
+ VERIFY3U(inst, !=, 0);
+ DTRACE_PROBE1(pageout__exit, uint_t, inst);
+ mutex_enter(&pageout_mutex);
+ pageouts_running--;
+ mutex_exit(&pageout_mutex);
+ mutex_enter(&curproc->p_lock);
+ lwp_exit();
+ /* NOTREACHED */
+ }
+
+ first = page_first();
+
+ /*
+ * Each scanner thread gets its own sector of the memory
+ * clock face.
+ */
+ pgcnt_t span, offset;
- backhand = page_first();
- if (handspreadpages >= total_pages) {
- fronthand = page_nextn(backhand, total_pages - 1);
+ span = looppages / n_page_scanners;
+ VERIFY3U(span, >, handspreadpages);
+
+ offset = inst * span;
+ regionstart = page_nextn(first, offset);
+ if (inst == n_page_scanners - 1) {
+ /* The last instance goes up to the last page */
+ regionend = page_nextn(first, looppages - 1);
} else {
- fronthand = page_nextn(backhand, handspreadpages);
+ regionend = page_nextn(regionstart, span - 1);
}
+
+ backhand = regionstart;
+ fronthand = page_nextn(backhand, handspreadpages);
+ tick = 1;
+
+ bhwrapping = fhwrapping = B_FALSE;
+
+ DTRACE_PROBE4(pageout__reset, uint_t, inst,
+ pgcnt_t, regionstart, pgcnt_t, regionend,
+ pgcnt_t, fronthand);
}
+ /*
+ * This CPU kstat is only incremented here and we're obviously
+ * on this CPU, so no lock.
+ */
CPU_STATS_ADDQ(CPU, vm, pgrrun, 1);
/*
* Keep track of the number of times we have scanned all the way around
- * the loop:
+ * the loop on this wakeup.
*/
laps = 0;
- DTRACE_PROBE(pageout__start);
-
/*
* Track the number of pages visited during this scan so that we can
* periodically measure our duty cycle.
*/
+ nscan_cnt = 0;
pcount = 0;
- if (pageout_sample_cnt < pageout_sample_lim) {
- /*
- * We need to measure the rate at which the system is able to
- * scan pages of memory. Each of these initial samples is a
- * scan of all system memory, regardless of whether or not we
- * are experiencing memory pressure.
- */
- nscan_limit = total_pages;
- sampling = true;
- } else {
- nscan_limit = desscan;
- sampling = false;
- }
+ DTRACE_PROBE5(pageout__start, uint_t, inst, pgcnt_t, desscan,
+ hrtime_t, pageout_nsec, page_t *, backhand, page_t *, fronthand);
+
+ /*
+ * Record the initial position of the front hand for this cycle so
+ * that we can detect when the hand wraps around.
+ */
+ fronthandstart = fronthand;
sample_start = gethrtime();
/*
* Scan the appropriate number of pages for a single duty cycle.
*/
- while (nscan < nscan_limit) {
+ while (nscan_cnt < desscan) {
checkpage_result_t rvfront, rvback;
- if (!sampling && freemem >= lotsfree + needfree) {
+ /*
+ * Only scan while at least one of these is true:
+ * 1) one or more zones is over its cap
+ * 2) there is not enough free memory
+ * 3) during page scan startup when determining sample data
+ */
+ if (!PAGE_SCAN_STARTUP && freemem >= lotsfree + needfree &&
+ !zones_over) {
/*
* We are not sampling and enough memory has become
* available that scanning is no longer required.
*/
+ DTRACE_PROBE1(pageout__memfree, uint_t, inst);
break;
}
+ DTRACE_PROBE2(pageout__loop, uint_t, inst, pgcnt_t, pcount);
+
/*
* Periodically check to see if we have exceeded the CPU duty
* cycle for a single wakeup.
*/
if ((pcount & PAGES_POLL_MASK) == PAGES_POLL_MASK) {
+ hrtime_t pageout_cycle_nsec;
+
pageout_cycle_nsec = gethrtime() - sample_start;
if (pageout_cycle_nsec >= pageout_nsec) {
- ++pageout_timeouts;
+ if (!zones_over)
+ atomic_inc_64(&pageout_timeouts);
+ DTRACE_PROBE1(pageout__timeout, uint_t, inst);
break;
}
}
@@ -1076,7 +1322,8 @@ loop:
++pcount;
/*
- * Protected by pageout_mutex instead of cpu_stat_lock:
+ * This CPU kstat is only incremented here and we're obviously
+ * on this CPU, so no lock.
*/
CPU_STATS_ADDQ(CPU, vm, scan, 1);
@@ -1084,26 +1331,48 @@ loop:
* Don't include ineligible pages in the number scanned.
*/
if (rvfront != CKP_INELIGIBLE || rvback != CKP_INELIGIBLE) {
- nscan++;
+ nscan_cnt++;
}
- backhand = page_next(backhand);
- fronthand = page_next(fronthand);
+ if (bhwrapping) {
+ backhand = regionstart;
+ bhwrapping = B_FALSE;
+ } else {
+ backhand = page_nextn(backhand, tick);
+ if (backhand == regionend)
+ bhwrapping = B_TRUE;
+ }
+
+ if (fhwrapping) {
+ fronthand = regionstart;
+ fhwrapping = B_FALSE;
+ } else {
+ fronthand = page_nextn(fronthand, tick);
+ if (fronthand == regionend)
+ fhwrapping = B_TRUE;
+ }
/*
- * The front hand has wrapped around to the first page in the
- * loop.
+ * The front hand has wrapped around during this wakeup.
*/
- if (fronthand == page_first()) {
+ if (fronthand == fronthandstart) {
laps++;
- DTRACE_PROBE1(pageout__hand__wrap, uint_t, laps);
+ DTRACE_PROBE2(pageout__hand__wrap, uint_t, inst,
+ uint_t, laps);
/*
- * Protected by pageout_mutex instead of cpu_stat_lock:
+ * This CPU kstat is only incremented here and we're
+ * obviously on this CPU, so no lock.
*/
CPU_STATS_ADDQ(CPU, vm, rev, 1);
- if (laps > 1) {
+ /*
+ * then when we wraparound memory we want to try to
+ * reclaim more pages.
+ * If scanning only because zones are over their cap,
+ * then wrapping is common and we simply keep going.
+ */
+ if (laps > 1 && freemem < lotsfree + needfree) {
/*
* Extremely unlikely, but it happens.
* We went around the loop at least once
@@ -1122,21 +1391,30 @@ loop:
}
sample_end = gethrtime();
+ atomic_add_long(&nscan, nscan_cnt);
- DTRACE_PROBE1(pageout__end, uint_t, laps);
+ DTRACE_PROBE4(pageout__end, uint_t, inst, uint_t, laps,
+ pgcnt_t, nscan_cnt, pgcnt_t, pcount)
+ /*
+ * The global variables used below are only modified by this thread and
+ * only during initial scanning when there is a single page scanner
+ * thread running.
+ */
if (pageout_new_spread == 0) {
- if (pageout_sample_cnt < pageout_sample_lim) {
+ VERIFY3U(inst, ==, 0);
+
+ if (PAGE_SCAN_STARTUP) {
/*
* Continue accumulating samples until we have enough
- * to get a reasonable value for average scan rate:
+ * to get a reasonable value for average scan rate.
*/
pageout_sample_pages += pcount;
pageout_sample_etime += sample_end - sample_start;
++pageout_sample_cnt;
}
- if (pageout_sample_cnt >= pageout_sample_lim) {
+ if (!PAGE_SCAN_STARTUP) {
/*
* We have enough samples, set the spread.
*/
@@ -1222,6 +1500,7 @@ checkpage(struct page *pp, pageout_hand_t whichhand)
int isfs = 0;
int isexec = 0;
int pagesync_flag;
+ zoneid_t zid = ALL_ZONES;
/*
* Skip pages:
@@ -1264,6 +1543,21 @@ checkpage(struct page *pp, pageout_hand_t whichhand)
return (CKP_INELIGIBLE);
}
+ if (zones_over) {
+ ASSERT(pp->p_zoneid == ALL_ZONES ||
+ pp->p_zoneid >= 0 && pp->p_zoneid <= MAX_ZONEID);
+ if (pp->p_zoneid == ALL_ZONES ||
+ zone_pdata[pp->p_zoneid].zpers_over == 0) {
+ /*
+ * Cross-zone shared page, or zone not over it's cap.
+ * Leave the page alone.
+ */
+ page_unlock(pp);
+ return (CKP_INELIGIBLE);
+ }
+ zid = pp->p_zoneid;
+ }
+
/*
* Maintain statistics for what we are freeing
*/
@@ -1371,6 +1665,11 @@ recheck:
VN_RELE(vp);
return (CKP_NOT_FREED);
}
+ if (isfs) {
+ zone_pageout_stat(zid, ZPO_DIRTY);
+ } else {
+ zone_pageout_stat(zid, ZPO_ANONDIRTY);
+ }
return (CKP_FREED);
}
@@ -1397,8 +1696,10 @@ recheck:
} else {
CPU_STATS_ADD_K(vm, fsfree, 1);
}
+ zone_pageout_stat(zid, ZPO_FS);
} else {
CPU_STATS_ADD_K(vm, anonfree, 1);
+ zone_pageout_stat(zid, ZPO_ANON);
}
return (CKP_FREED);
diff --git a/usr/src/uts/common/os/vmem.c b/usr/src/uts/common/os/vmem.c
index 7d2b89408a..933834aee9 100644
--- a/usr/src/uts/common/os/vmem.c
+++ b/usr/src/uts/common/os/vmem.c
@@ -1629,7 +1629,7 @@ vmem_destroy(vmem_t *vmp)
leaked = vmem_size(vmp, VMEM_ALLOC);
if (leaked != 0)
- cmn_err(CE_WARN, "vmem_destroy('%s'): leaked %lu %s",
+ cmn_err(CE_WARN, "!vmem_destroy('%s'): leaked %lu %s",
vmp->vm_name, leaked, (vmp->vm_cflags & VMC_IDENTIFIER) ?
"identifiers" : "bytes");
diff --git a/usr/src/uts/common/os/zone.c b/usr/src/uts/common/os/zone.c
index a398830833..fa841df9ff 100644
--- a/usr/src/uts/common/os/zone.c
+++ b/usr/src/uts/common/os/zone.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015, Joyent Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
* Copyright (c) 2016 by Delphix. All rights reserved.
* Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
*/
@@ -106,14 +106,16 @@
* removed from the list of active zones. zone_destroy() returns, and
* the zone can be recreated.
*
- * ZONE_IS_FREE (internal state): zone_ref goes to 0, ZSD destructor
- * callbacks are executed, and all memory associated with the zone is
- * freed.
+ * ZONE_IS_FREE (internal state): All references have been dropped and
+ * the zone_t is no longer in the zone_active nor zone_deathrow lists.
+ * The zone_t is in the process of being freed. This state exists
+ * only for publishing a sysevent to indicate that the zone by this
+ * name can be booted again.
*
- * Threads can wait for the zone to enter a requested state by using
- * zone_status_wait() or zone_status_timedwait() with the desired
- * state passed in as an argument. Zone state transitions are
- * uni-directional; it is not possible to move back to an earlier state.
+ * Threads can wait for the zone to enter a requested state (other than
+ * ZONE_IS_FREE) by using zone_status_wait() or zone_status_timedwait()
+ * with the desired state passed in as an argument. Zone state transitions
+ * are uni-directional; it is not possible to move back to an earlier state.
*
*
* Zone-Specific Data:
@@ -252,6 +254,8 @@
#include <sys/cpucaps.h>
#include <vm/seg.h>
#include <sys/mac.h>
+#include <sys/rt.h>
+#include <sys/fx.h>
/*
* This constant specifies the number of seconds that threads waiting for
@@ -312,6 +316,7 @@ static id_space_t *zoneid_space;
* 'global_zone'.
*/
zone_t zone0;
+zone_zfs_io_t zone0_zp_zfs;
zone_t *global_zone = NULL; /* Set when the global zone is initialized */
/*
@@ -327,8 +332,8 @@ static list_t zone_active;
static list_t zone_deathrow;
static kmutex_t zone_deathrow_lock;
-/* number of zones is limited by virtual interface limit in IP */
-uint_t maxzones = 8192;
+/* This can be dynamically reduced if various subsystems hit internal limits. */
+uint_t maxzones = MAX_ZONES;
/* Event channel to sent zone state change notifications */
evchan_t *zone_event_chan;
@@ -350,6 +355,7 @@ const char *zone_status_table[] = {
ZONE_EVENT_SHUTTING_DOWN, /* down */
ZONE_EVENT_SHUTTING_DOWN, /* dying */
ZONE_EVENT_UNINITIALIZED, /* dead */
+ ZONE_EVENT_FREE, /* free */
};
/*
@@ -372,8 +378,12 @@ static char *zone_ref_subsys_names[] = {
rctl_hndl_t rc_zone_cpu_shares;
rctl_hndl_t rc_zone_locked_mem;
rctl_hndl_t rc_zone_max_swap;
+rctl_hndl_t rc_zone_phys_mem;
rctl_hndl_t rc_zone_max_lofi;
rctl_hndl_t rc_zone_cpu_cap;
+rctl_hndl_t rc_zone_cpu_baseline;
+rctl_hndl_t rc_zone_cpu_burst_time;
+rctl_hndl_t rc_zone_zfs_io_pri;
rctl_hndl_t rc_zone_nlwps;
rctl_hndl_t rc_zone_nprocs;
rctl_hndl_t rc_zone_shmmax;
@@ -389,6 +399,7 @@ static int zone_remove_datalink(zoneid_t, datalink_id_t);
static int zone_list_datalink(zoneid_t, int *, datalink_id_t *);
static int zone_set_network(zoneid_t, zone_net_data_t *);
static int zone_get_network(zoneid_t, zone_net_data_t *);
+static void zone_status_set(zone_t *, zone_status_t);
typedef boolean_t zsd_applyfn_t(kmutex_t *, boolean_t, zone_t *, zone_key_t);
@@ -419,8 +430,72 @@ static boolean_t zsd_wait_for_inprogress(zone_t *, struct zsd_entry *,
* Version 5 alters the zone_boot system call, and converts its old
* bootargs parameter to be set by the zone_setattr API instead.
* Version 6 adds the flag argument to zone_create.
+ * Version 7 adds the requested zoneid to zone_create.
*/
-static const int ZONE_SYSCALL_API_VERSION = 6;
+static const int ZONE_SYSCALL_API_VERSION = 7;
+
+/*
+ * "zone_pdata" is an array indexed by zoneid. It is used to store "persistent"
+ * data which can be referenced independently of the zone_t structure. This
+ * data falls into two categories;
+ * 1) pages and RSS data associated with processes inside a zone
+ * 2) in-flight ZFS I/O data
+ *
+ * Each member of zone_persist_t stores the zone's current page usage, its page
+ * limit, a flag indicating if the zone is over its physical memory cap and
+ * various page-related statistics. The zpers_over flag is the interface for
+ * the page scanner to use when reclaiming pages for zones that are over their
+ * cap. The zone_persist_t structure also includes a mutex and a reference to a
+ * zone_zfs_io_t structure used for tracking the zone's ZFS I/O data.
+ *
+ * All zone physical memory cap data is stored in this array instead of within
+ * the zone structure itself. This is because zone structures come and go, but
+ * paging-related work can be asynchronous to any particular zone. In,
+ * particular:
+ * 1) Page scanning to reclaim pages occurs from a kernel thread that is not
+ * associated with any zone.
+ * 2) Freeing segkp pages can occur long after the zone which first
+ * instantiated those pages has gone away.
+ * We want to be able to account for pages/zone without constantly having to
+ * take extra locks and finding the relevant zone structure, particularly during
+ * page scanning.
+ *
+ * The page scanner can run when "zone_num_over_cap" is non-zero. It can
+ * do a direct lookup of a zoneid into the "zone_pdata" array to determine
+ * if that zone is over its cap.
+ *
+ * There is no locking for the page scanner to perform these two checks.
+ * We cannot have the page scanner blocking normal paging activity for
+ * running processes. Because the physical memory cap is a soft cap, it is
+ * fine for the scanner to simply read the current state of the counter and
+ * the zone's zpers_over entry in the array. The scanner should never modify
+ * either of these items. Internally the entries and the counter are managed
+ * with the "zone_physcap_lock" mutex as we add/remove mappings to pages. We
+ * take care to ensure that we only take the zone_physcap_lock mutex when a
+ * zone is transitioning over/under its physical memory cap.
+ *
+ * The "zone_incr_capped" and "zone_decr_capped" functions are used to manage
+ * the "zone_pdata" array and associated counter.
+ *
+ * The zone_persist_t structure tracks the zone's physical cap and phyiscal
+ * usage in terms of pages. These values are currently defined as uint32. Thus,
+ * the maximum number of pages we can track is a UINT_MAX-1 (4,294,967,295)
+ * since UINT_MAX means the zone's RSS is unlimited. Assuming a 4k page size, a
+ * zone's maximum RSS is limited to 17.5 TB and twice that with an 8k page size.
+ * In the future we may need to expand these counters to 64-bit, but for now
+ * we're using 32-bit to conserve memory, since this array is statically
+ * allocated within the kernel based on the maximum number of zones supported.
+ *
+ * With respect to the zone_zfs_io_t referenced by the zone_persist_t, under
+ * a heavy I/O workload, the "zonehash_lock" would become extremely hot if we
+ * had to continuously find the zone structure associated with an I/O that has
+ * just completed. To avoid that overhead, we track the I/O data within the
+ * zone_zfs_io_t instead. We can directly access that data without having to
+ * lookup the full zone_t structure.
+ */
+uint_t zone_num_over_cap;
+zone_persist_t zone_pdata[MAX_ZONES];
+static kmutex_t zone_physcap_lock;
/*
* Certain filesystems (such as NFS and autofs) need to know which zone
@@ -1379,6 +1454,127 @@ static rctl_ops_t zone_cpu_cap_ops = {
/*ARGSUSED*/
static rctl_qty_t
+zone_cpu_base_get(rctl_t *rctl, struct proc *p)
+{
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ return (cpucaps_zone_get_base(p->p_zone));
+}
+
+/*
+ * The zone cpu base is used to set the baseline CPU for the zone
+ * so we can track when the zone is bursting.
+ */
+/*ARGSUSED*/
+static int
+zone_cpu_base_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+ rctl_qty_t nv)
+{
+ zone_t *zone = e->rcep_p.zone;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_ZONE);
+
+ if (zone == NULL)
+ return (0);
+
+ return (cpucaps_zone_set_base(zone, nv));
+}
+
+static rctl_ops_t zone_cpu_base_ops = {
+ rcop_no_action,
+ zone_cpu_base_get,
+ zone_cpu_base_set,
+ rcop_no_test
+};
+
+/*ARGSUSED*/
+static rctl_qty_t
+zone_cpu_burst_time_get(rctl_t *rctl, struct proc *p)
+{
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ return (cpucaps_zone_get_burst_time(p->p_zone));
+}
+
+/*
+ * The zone cpu burst time is used to set the amount of time CPU(s) can be
+ * bursting for the zone.
+ */
+/*ARGSUSED*/
+static int
+zone_cpu_burst_time_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+ rctl_qty_t nv)
+{
+ zone_t *zone = e->rcep_p.zone;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_ZONE);
+
+ if (zone == NULL)
+ return (0);
+
+ return (cpucaps_zone_set_burst_time(zone, nv));
+}
+
+static rctl_ops_t zone_cpu_burst_time_ops = {
+ rcop_no_action,
+ zone_cpu_burst_time_get,
+ zone_cpu_burst_time_set,
+ rcop_no_test
+};
+
+/*
+ * zone.zfs-io-pri resource control support (IO priority).
+ */
+/*ARGSUSED*/
+static rctl_qty_t
+zone_zfs_io_pri_get(rctl_t *rctl, struct proc *p)
+{
+ zone_persist_t *zp = &zone_pdata[p->p_zone->zone_id];
+ rctl_qty_t r = 0;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ mutex_enter(&zp->zpers_zfs_lock);
+ if (zp->zpers_zfsp != NULL)
+ r = (rctl_qty_t)zp->zpers_zfsp->zpers_zfs_io_pri;
+ mutex_exit(&zp->zpers_zfs_lock);
+
+ return (r);
+}
+
+/*ARGSUSED*/
+static int
+zone_zfs_io_pri_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+ rctl_qty_t nv)
+{
+ zone_t *zone = e->rcep_p.zone;
+ zone_persist_t *zp;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_ZONE);
+
+ if (zone == NULL)
+ return (0);
+
+ /*
+ * set priority to the new value.
+ */
+ zp = &zone_pdata[zone->zone_id];
+ mutex_enter(&zp->zpers_zfs_lock);
+ if (zp->zpers_zfsp != NULL)
+ zp->zpers_zfsp->zpers_zfs_io_pri = (uint16_t)nv;
+ mutex_exit(&zp->zpers_zfs_lock);
+ return (0);
+}
+
+static rctl_ops_t zone_zfs_io_pri_ops = {
+ rcop_no_action,
+ zone_zfs_io_pri_get,
+ zone_zfs_io_pri_set,
+ rcop_no_test
+};
+
+/*ARGSUSED*/
+static rctl_qty_t
zone_lwps_usage(rctl_t *r, proc_t *p)
{
rctl_qty_t nlwps;
@@ -1705,6 +1901,57 @@ static rctl_ops_t zone_max_swap_ops = {
/*ARGSUSED*/
static rctl_qty_t
+zone_phys_mem_usage(rctl_t *rctl, struct proc *p)
+{
+ rctl_qty_t q;
+ zone_persist_t *zp = &zone_pdata[p->p_zone->zone_id];
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ q = ptob(zp->zpers_pg_cnt);
+ return (q);
+}
+
+/*ARGSUSED*/
+static int
+zone_phys_mem_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+ rctl_qty_t nv)
+{
+ zoneid_t zid;
+ uint_t pg_val;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_ZONE);
+ if (e->rcep_p.zone == NULL)
+ return (0);
+ zid = e->rcep_p.zone->zone_id;
+ if (nv == UINT64_MAX) {
+ pg_val = UINT32_MAX;
+ } else {
+ uint64_t pages = btop(nv);
+
+ /*
+ * Return from RCTLOP_SET is always ignored so just clamp an
+ * out-of-range value to our largest "limited" value.
+ */
+ if (pages >= UINT32_MAX) {
+ pg_val = UINT32_MAX - 1;
+ } else {
+ pg_val = (uint_t)pages;
+ }
+ }
+ zone_pdata[zid].zpers_pg_limit = pg_val;
+ return (0);
+}
+
+static rctl_ops_t zone_phys_mem_ops = {
+ rcop_no_action,
+ zone_phys_mem_usage,
+ zone_phys_mem_set,
+ rcop_no_test
+};
+
+/*ARGSUSED*/
+static rctl_qty_t
zone_max_lofi_usage(rctl_t *rctl, struct proc *p)
{
rctl_qty_t q;
@@ -1798,6 +2045,21 @@ zone_lockedmem_kstat_update(kstat_t *ksp, int rw)
}
static int
+zone_physmem_kstat_update(kstat_t *ksp, int rw)
+{
+ zone_t *zone = ksp->ks_private;
+ zone_kstat_t *zk = ksp->ks_data;
+ zone_persist_t *zp = &zone_pdata[zone->zone_id];
+
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ zk->zk_usage.value.ui64 = ptob(zp->zpers_pg_cnt);
+ zk->zk_value.value.ui64 = ptob(zp->zpers_pg_limit);
+ return (0);
+}
+
+static int
zone_nprocs_kstat_update(kstat_t *ksp, int rw)
{
zone_t *zone = ksp->ks_private;
@@ -1826,7 +2088,7 @@ zone_swapresv_kstat_update(kstat_t *ksp, int rw)
}
static kstat_t *
-zone_kstat_create_common(zone_t *zone, char *name,
+zone_rctl_kstat_create_common(zone_t *zone, char *name,
int (*updatefunc) (kstat_t *, int))
{
kstat_t *ksp;
@@ -1851,16 +2113,200 @@ zone_kstat_create_common(zone_t *zone, char *name,
return (ksp);
}
+static int
+zone_vfs_kstat_update(kstat_t *ksp, int rw)
+{
+ zone_t *zone = ksp->ks_private;
+ zone_vfs_kstat_t *zvp = ksp->ks_data;
+ kstat_io_t *kiop = &zone->zone_vfs_rwstats;
+
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ /*
+ * Extract the VFS statistics from the kstat_io_t structure used by
+ * kstat_runq_enter() and related functions. Since the slow ops
+ * counters are updated directly by the VFS layer, there's no need to
+ * copy those statistics here.
+ *
+ * Note that kstat_runq_enter() and the related functions use
+ * gethrtime_unscaled(), so scale the time here.
+ */
+ zvp->zv_nread.value.ui64 = kiop->nread;
+ zvp->zv_reads.value.ui64 = kiop->reads;
+ zvp->zv_rtime.value.ui64 = kiop->rtime;
+ zvp->zv_rcnt.value.ui64 = kiop->rcnt;
+ zvp->zv_rlentime.value.ui64 = kiop->rlentime;
+ zvp->zv_nwritten.value.ui64 = kiop->nwritten;
+ zvp->zv_writes.value.ui64 = kiop->writes;
+ zvp->zv_wtime.value.ui64 = kiop->wtime;
+ zvp->zv_wcnt.value.ui64 = kiop->wcnt;
+ zvp->zv_wlentime.value.ui64 = kiop->wlentime;
+
+ scalehrtime((hrtime_t *)&zvp->zv_rtime.value.ui64);
+ scalehrtime((hrtime_t *)&zvp->zv_rlentime.value.ui64);
+ scalehrtime((hrtime_t *)&zvp->zv_wtime.value.ui64);
+ scalehrtime((hrtime_t *)&zvp->zv_wlentime.value.ui64);
+
+ return (0);
+}
+
+static kstat_t *
+zone_vfs_kstat_create(zone_t *zone)
+{
+ kstat_t *ksp;
+ zone_vfs_kstat_t *zvp;
+
+ if ((ksp = kstat_create_zone("zone_vfs", zone->zone_id,
+ zone->zone_name, "zone_vfs", KSTAT_TYPE_NAMED,
+ sizeof (zone_vfs_kstat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL, zone->zone_id)) == NULL)
+ return (NULL);
+
+ if (zone->zone_id != GLOBAL_ZONEID)
+ kstat_zone_add(ksp, GLOBAL_ZONEID);
+
+ zvp = ksp->ks_data = kmem_zalloc(sizeof (zone_vfs_kstat_t), KM_SLEEP);
+ ksp->ks_data_size += strlen(zone->zone_name) + 1;
+ ksp->ks_lock = &zone->zone_vfs_lock;
+ zone->zone_vfs_stats = zvp;
+
+ /* The kstat "name" field is not large enough for a full zonename */
+ kstat_named_init(&zvp->zv_zonename, "zonename", KSTAT_DATA_STRING);
+ kstat_named_setstr(&zvp->zv_zonename, zone->zone_name);
+ kstat_named_init(&zvp->zv_nread, "nread", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_reads, "reads", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_rtime, "rtime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_rcnt, "rcnt", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_rlentime, "rlentime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_nwritten, "nwritten", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_writes, "writes", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_wtime, "wtime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_wcnt, "wcnt", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_wlentime, "wlentime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_10ms_ops, "10ms_ops", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_100ms_ops, "100ms_ops", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_1s_ops, "1s_ops", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_10s_ops, "10s_ops", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_delay_cnt, "delay_cnt", KSTAT_DATA_UINT64);
+ kstat_named_init(&zvp->zv_delay_time, "delay_time", KSTAT_DATA_UINT64);
+
+ ksp->ks_update = zone_vfs_kstat_update;
+ ksp->ks_private = zone;
+
+ kstat_install(ksp);
+ return (ksp);
+}
+
+static int
+zone_zfs_kstat_update(kstat_t *ksp, int rw)
+{
+ zone_t *zone = ksp->ks_private;
+ zone_zfs_kstat_t *zzp = ksp->ks_data;
+ zone_persist_t *zp = &zone_pdata[zone->zone_id];
+
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ mutex_enter(&zp->zpers_zfs_lock);
+ if (zp->zpers_zfsp == NULL) {
+ zzp->zz_nread.value.ui64 = 0;
+ zzp->zz_reads.value.ui64 = 0;
+ zzp->zz_rtime.value.ui64 = 0;
+ zzp->zz_rlentime.value.ui64 = 0;
+ zzp->zz_nwritten.value.ui64 = 0;
+ zzp->zz_writes.value.ui64 = 0;
+ zzp->zz_waittime.value.ui64 = 0;
+ } else {
+ kstat_io_t *kiop = &zp->zpers_zfsp->zpers_zfs_rwstats;
+
+ /*
+ * Extract the ZFS statistics from the kstat_io_t structure
+ * used by kstat_runq_enter() and related functions. Since the
+ * I/O throttle counters are updated directly by the ZFS layer,
+ * there's no need to copy those statistics here.
+ *
+ * Note that kstat_runq_enter() and the related functions use
+ * gethrtime_unscaled(), so scale the time here.
+ */
+ zzp->zz_nread.value.ui64 = kiop->nread;
+ zzp->zz_reads.value.ui64 = kiop->reads;
+ zzp->zz_rtime.value.ui64 = kiop->rtime;
+ zzp->zz_rlentime.value.ui64 = kiop->rlentime;
+ zzp->zz_nwritten.value.ui64 = kiop->nwritten;
+ zzp->zz_writes.value.ui64 = kiop->writes;
+ zzp->zz_waittime.value.ui64 =
+ zp->zpers_zfsp->zpers_zfs_rd_waittime;
+ }
+ mutex_exit(&zp->zpers_zfs_lock);
+
+ scalehrtime((hrtime_t *)&zzp->zz_rtime.value.ui64);
+ scalehrtime((hrtime_t *)&zzp->zz_rlentime.value.ui64);
+
+ return (0);
+}
+
+static kstat_t *
+zone_zfs_kstat_create(zone_t *zone)
+{
+ kstat_t *ksp;
+ zone_zfs_kstat_t *zzp;
+
+ if ((ksp = kstat_create_zone("zone_zfs", zone->zone_id,
+ zone->zone_name, "zone_zfs", KSTAT_TYPE_NAMED,
+ sizeof (zone_zfs_kstat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL, zone->zone_id)) == NULL)
+ return (NULL);
+
+ if (zone->zone_id != GLOBAL_ZONEID)
+ kstat_zone_add(ksp, GLOBAL_ZONEID);
+
+ zzp = ksp->ks_data = kmem_zalloc(sizeof (zone_zfs_kstat_t), KM_SLEEP);
+ ksp->ks_data_size += strlen(zone->zone_name) + 1;
+ ksp->ks_lock = &zone->zone_zfs_lock;
+ zone->zone_zfs_stats = zzp;
+
+ /* The kstat "name" field is not large enough for a full zonename */
+ kstat_named_init(&zzp->zz_zonename, "zonename", KSTAT_DATA_STRING);
+ kstat_named_setstr(&zzp->zz_zonename, zone->zone_name);
+ kstat_named_init(&zzp->zz_nread, "nread", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_reads, "reads", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_rtime, "rtime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_rlentime, "rlentime", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_nwritten, "nwritten", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_writes, "writes", KSTAT_DATA_UINT64);
+ kstat_named_init(&zzp->zz_waittime, "waittime", KSTAT_DATA_UINT64);
+
+ ksp->ks_update = zone_zfs_kstat_update;
+ ksp->ks_private = zone;
+
+ kstat_install(ksp);
+ return (ksp);
+}
static int
zone_mcap_kstat_update(kstat_t *ksp, int rw)
{
zone_t *zone = ksp->ks_private;
zone_mcap_kstat_t *zmp = ksp->ks_data;
+ zone_persist_t *zp;
if (rw == KSTAT_WRITE)
return (EACCES);
+ zp = &zone_pdata[zone->zone_id];
+
+ zmp->zm_rss.value.ui64 = ptob(zp->zpers_pg_cnt);
+ zmp->zm_phys_cap.value.ui64 = ptob(zp->zpers_pg_limit);
+ zmp->zm_swap.value.ui64 = zone->zone_max_swap;
+ zmp->zm_swap_cap.value.ui64 = zone->zone_max_swap_ctl;
+ zmp->zm_nover.value.ui64 = zp->zpers_nover;
+#ifndef DEBUG
+ zmp->zm_pagedout.value.ui64 = ptob(zp->zpers_pg_out);
+#else
+ zmp->zm_pagedout.value.ui64 = ptob(zp->zpers_pg_fsdirty +
+ zp->zpers_pg_fs + zp->zpers_pg_anon + zp->zpers_pg_anondirty);
+#endif
zmp->zm_pgpgin.value.ui64 = zone->zone_pgpgin;
zmp->zm_anonpgin.value.ui64 = zone->zone_anonpgin;
zmp->zm_execpgin.value.ui64 = zone->zone_execpgin;
@@ -1893,6 +2339,12 @@ zone_mcap_kstat_create(zone_t *zone)
/* The kstat "name" field is not large enough for a full zonename */
kstat_named_init(&zmp->zm_zonename, "zonename", KSTAT_DATA_STRING);
kstat_named_setstr(&zmp->zm_zonename, zone->zone_name);
+ kstat_named_init(&zmp->zm_rss, "rss", KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_phys_cap, "physcap", KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_swap, "swap", KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_swap_cap, "swapcap", KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_nover, "nover", KSTAT_DATA_UINT64);
+ kstat_named_init(&zmp->zm_pagedout, "pagedout", KSTAT_DATA_UINT64);
kstat_named_init(&zmp->zm_pgpgin, "pgpgin", KSTAT_DATA_UINT64);
kstat_named_init(&zmp->zm_anonpgin, "anonpgin", KSTAT_DATA_UINT64);
kstat_named_init(&zmp->zm_execpgin, "execpgin", KSTAT_DATA_UINT64);
@@ -1942,9 +2394,12 @@ zone_misc_kstat_update(kstat_t *ksp, int rw)
zmp->zm_ffnomem.value.ui32 = zone->zone_ffnomem;
zmp->zm_ffmisc.value.ui32 = zone->zone_ffmisc;
+ zmp->zm_mfseglim.value.ui32 = zone->zone_mfseglim;
+
zmp->zm_nested_intp.value.ui32 = zone->zone_nested_intp;
zmp->zm_init_pid.value.ui32 = zone->zone_proc_initpid;
+ zmp->zm_init_restarts.value.ui32 = zone->zone_proc_init_restarts;
zmp->zm_boot_time.value.ui64 = (uint64_t)zone->zone_boot_time;
return (0);
@@ -1985,9 +2440,13 @@ zone_misc_kstat_create(zone_t *zone)
KSTAT_DATA_UINT32);
kstat_named_init(&zmp->zm_ffnomem, "forkfail_nomem", KSTAT_DATA_UINT32);
kstat_named_init(&zmp->zm_ffmisc, "forkfail_misc", KSTAT_DATA_UINT32);
+ kstat_named_init(&zmp->zm_mfseglim, "mapfail_seglim",
+ KSTAT_DATA_UINT32);
kstat_named_init(&zmp->zm_nested_intp, "nested_interp",
KSTAT_DATA_UINT32);
kstat_named_init(&zmp->zm_init_pid, "init_pid", KSTAT_DATA_UINT32);
+ kstat_named_init(&zmp->zm_init_restarts, "init_restarts",
+ KSTAT_DATA_UINT32);
kstat_named_init(&zmp->zm_boot_time, "boot_time", KSTAT_DATA_UINT64);
ksp->ks_update = zone_misc_kstat_update;
@@ -2000,13 +2459,25 @@ zone_misc_kstat_create(zone_t *zone)
static void
zone_kstat_create(zone_t *zone)
{
- zone->zone_lockedmem_kstat = zone_kstat_create_common(zone,
+ zone->zone_lockedmem_kstat = zone_rctl_kstat_create_common(zone,
"lockedmem", zone_lockedmem_kstat_update);
- zone->zone_swapresv_kstat = zone_kstat_create_common(zone,
+ zone->zone_swapresv_kstat = zone_rctl_kstat_create_common(zone,
"swapresv", zone_swapresv_kstat_update);
- zone->zone_nprocs_kstat = zone_kstat_create_common(zone,
+ zone->zone_physmem_kstat = zone_rctl_kstat_create_common(zone,
+ "physicalmem", zone_physmem_kstat_update);
+ zone->zone_nprocs_kstat = zone_rctl_kstat_create_common(zone,
"nprocs", zone_nprocs_kstat_update);
+ if ((zone->zone_vfs_ksp = zone_vfs_kstat_create(zone)) == NULL) {
+ zone->zone_vfs_stats = kmem_zalloc(
+ sizeof (zone_vfs_kstat_t), KM_SLEEP);
+ }
+
+ if ((zone->zone_zfs_ksp = zone_zfs_kstat_create(zone)) == NULL) {
+ zone->zone_zfs_stats = kmem_zalloc(
+ sizeof (zone_zfs_kstat_t), KM_SLEEP);
+ }
+
if ((zone->zone_mcap_ksp = zone_mcap_kstat_create(zone)) == NULL) {
zone->zone_mcap_stats = kmem_zalloc(
sizeof (zone_mcap_kstat_t), KM_SLEEP);
@@ -2038,8 +2509,15 @@ zone_kstat_delete(zone_t *zone)
sizeof (zone_kstat_t));
zone_kstat_delete_common(&zone->zone_swapresv_kstat,
sizeof (zone_kstat_t));
+ zone_kstat_delete_common(&zone->zone_physmem_kstat,
+ sizeof (zone_kstat_t));
zone_kstat_delete_common(&zone->zone_nprocs_kstat,
sizeof (zone_kstat_t));
+
+ zone_kstat_delete_common(&zone->zone_vfs_ksp,
+ sizeof (zone_vfs_kstat_t));
+ zone_kstat_delete_common(&zone->zone_zfs_ksp,
+ sizeof (zone_zfs_kstat_t));
zone_kstat_delete_common(&zone->zone_mcap_ksp,
sizeof (zone_mcap_kstat_t));
zone_kstat_delete_common(&zone->zone_misc_ksp,
@@ -2101,8 +2579,12 @@ zone_zsd_init(void)
zone0.zone_initname = initname;
zone0.zone_lockedmem_kstat = NULL;
zone0.zone_swapresv_kstat = NULL;
+ zone0.zone_physmem_kstat = NULL;
zone0.zone_nprocs_kstat = NULL;
+ zone_pdata[0].zpers_zfsp = &zone0_zp_zfs;
+ zone_pdata[0].zpers_zfsp->zpers_zfs_io_pri = 1;
+
list_create(&zone0.zone_ref_list, sizeof (zone_ref_t),
offsetof(zone_ref_t, zref_linkage));
list_create(&zone0.zone_zsd, sizeof (struct zsd_entry),
@@ -2209,6 +2691,21 @@ zone_init(void)
RCTL_GLOBAL_INFINITE,
MAXCAP, MAXCAP, &zone_cpu_cap_ops);
+ rc_zone_cpu_baseline = rctl_register("zone.cpu-baseline",
+ RCENTITY_ZONE, RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_DENY_NEVER |
+ RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER,
+ MAXCAP, MAXCAP, &zone_cpu_base_ops);
+
+ rc_zone_cpu_burst_time = rctl_register("zone.cpu-burst-time",
+ RCENTITY_ZONE, RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_DENY_NEVER |
+ RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER,
+ INT_MAX, INT_MAX, &zone_cpu_burst_time_ops);
+
+ rc_zone_zfs_io_pri = rctl_register("zone.zfs-io-priority",
+ RCENTITY_ZONE, RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_DENY_NEVER |
+ RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER,
+ 16384, 16384, &zone_zfs_io_pri_ops);
+
rc_zone_nlwps = rctl_register("zone.max-lwps", RCENTITY_ZONE,
RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
INT_MAX, INT_MAX, &zone_lwps_ops);
@@ -2250,6 +2747,20 @@ zone_init(void)
rde = rctl_dict_lookup("zone.cpu-shares");
(void) rctl_val_list_insert(&rde->rcd_default_value, dval);
+ /*
+ * Create a rctl_val with PRIVILEGED, NOACTION, value = 1. Then attach
+ * this at the head of the rctl_dict_entry for ``zone.zfs-io-priority'.
+ */
+ dval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
+ bzero(dval, sizeof (rctl_val_t));
+ dval->rcv_value = 1;
+ dval->rcv_privilege = RCPRIV_PRIVILEGED;
+ dval->rcv_flagaction = RCTL_LOCAL_NOACTION;
+ dval->rcv_action_recip_pid = -1;
+
+ rde = rctl_dict_lookup("zone.zfs-io-priority");
+ (void) rctl_val_list_insert(&rde->rcd_default_value, dval);
+
rc_zone_locked_mem = rctl_register("zone.max-locked-memory",
RCENTITY_ZONE, RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_BYTES |
RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
@@ -2260,6 +2771,11 @@ zone_init(void)
RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
&zone_max_swap_ops);
+ rc_zone_phys_mem = rctl_register("zone.max-physical-memory",
+ RCENTITY_ZONE, RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_BYTES |
+ RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
+ &zone_phys_mem_ops);
+
rc_zone_max_lofi = rctl_register("zone.max-lofi",
RCENTITY_ZONE, RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT |
RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
@@ -2283,6 +2799,7 @@ zone_init(void)
zone0.zone_restart_init = B_TRUE;
zone0.zone_reboot_on_init_exit = B_FALSE;
zone0.zone_restart_init_0 = B_FALSE;
+ zone0.zone_init_status = -1;
zone0.zone_brand = &native_brand;
rctl_prealloc_destroy(gp);
/*
@@ -2364,6 +2881,8 @@ zone_init(void)
static void
zone_free(zone_t *zone)
{
+ zone_dl_t *zdl;
+
ASSERT(zone != global_zone);
ASSERT(zone->zone_ntasks == 0);
ASSERT(zone->zone_nlwps == 0);
@@ -2379,6 +2898,9 @@ zone_free(zone_t *zone)
*/
cpucaps_zone_remove(zone);
+ /* Clear physical memory capping data. */
+ bzero(&zone_pdata[zone->zone_id], sizeof (zone_persist_t));
+
ASSERT(zone->zone_cpucap == NULL);
/* remove from deathrow list */
@@ -2392,8 +2914,30 @@ zone_free(zone_t *zone)
list_destroy(&zone->zone_ref_list);
zone_free_zsd(zone);
zone_free_datasets(zone);
+
+ /*
+ * While dlmgmtd should have removed all of these, it could have left
+ * something behind or crashed. In which case it's not safe for us to
+ * assume that the list is empty which list_destroy() will ASSERT. We
+ * clean up for our userland comrades which may have crashed, or worse,
+ * been disabled by SMF.
+ */
+ while ((zdl = list_remove_head(&zone->zone_dl_list)) != NULL) {
+ if (zdl->zdl_net != NULL)
+ nvlist_free(zdl->zdl_net);
+ kmem_free(zdl, sizeof (zone_dl_t));
+ }
list_destroy(&zone->zone_dl_list);
+ /*
+ * This zone_t can no longer inhibit creation of another zone_t
+ * with the same name or debug ID. Generate a sysevent so that
+ * userspace tools know it is safe to carry on.
+ */
+ mutex_enter(&zone_status_lock);
+ zone_status_set(zone, ZONE_IS_FREE);
+ mutex_exit(&zone_status_lock);
+
cpu_uarray_free(zone->zone_ustate);
if (zone->zone_rootvp != NULL)
@@ -2438,11 +2982,17 @@ zone_free(zone_t *zone)
static void
zone_status_set(zone_t *zone, zone_status_t status)
{
+ timestruc_t now;
+ uint64_t t;
nvlist_t *nvl = NULL;
ASSERT(MUTEX_HELD(&zone_status_lock));
- ASSERT(status > ZONE_MIN_STATE && status <= ZONE_MAX_STATE &&
- status >= zone_status_get(zone));
+ ASSERT((status > ZONE_MIN_STATE && status <= ZONE_MAX_STATE ||
+ status == ZONE_IS_FREE) && status >= zone_status_get(zone));
+
+ /* Current time since Jan 1 1970 but consumers expect NS */
+ gethrestime(&now);
+ t = (now.tv_sec * NANOSEC) + now.tv_nsec;
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) ||
nvlist_add_string(nvl, ZONE_CB_NAME, zone->zone_name) ||
@@ -2451,12 +3001,14 @@ zone_status_set(zone_t *zone, zone_status_t status)
nvlist_add_string(nvl, ZONE_CB_OLDSTATE,
zone_status_table[zone->zone_status]) ||
nvlist_add_int32(nvl, ZONE_CB_ZONEID, zone->zone_id) ||
- nvlist_add_uint64(nvl, ZONE_CB_TIMESTAMP, (uint64_t)gethrtime()) ||
+ nvlist_add_uint64(nvl, ZONE_CB_TIMESTAMP, t) ||
sysevent_evc_publish(zone_event_chan, ZONE_EVENT_STATUS_CLASS,
ZONE_EVENT_STATUS_SUBCLASS, "sun.com", "kernel", nvl, EVCH_SLEEP)) {
#ifdef DEBUG
(void) printf(
"Failed to allocate and send zone state change event.\n");
+#else
+ /* EMPTY */
#endif
}
nvlist_free(nvl);
@@ -2476,6 +3028,38 @@ zone_status_get(zone_t *zone)
return (zone->zone_status);
}
+/*
+ * Publish a zones-related sysevent for purposes other than zone state changes.
+ * While it is unfortunate that zone_event_chan is associated with
+ * "com.sun:zones:status" (rather than "com.sun:zones") state changes should be
+ * the only ones with class "status" and subclass "change".
+ */
+void
+zone_sysevent_publish(zone_t *zone, const char *class, const char *subclass,
+ nvlist_t *ev_nvl)
+{
+ nvlist_t *nvl = NULL;
+ timestruc_t now;
+ uint64_t t;
+
+ gethrestime(&now);
+ t = (now.tv_sec * NANOSEC) + now.tv_nsec;
+
+ if (nvlist_dup(ev_nvl, &nvl, KM_SLEEP) != 0 ||
+ nvlist_add_string(nvl, ZONE_CB_NAME, zone->zone_name) != 0 ||
+ nvlist_add_uint64(nvl, ZONE_CB_ZONEID, zone->zone_id) != 0 ||
+ nvlist_add_uint64(nvl, ZONE_CB_TIMESTAMP, t) != 0 ||
+ sysevent_evc_publish(zone_event_chan, class, subclass, "sun.com",
+ "kernel", nvl, EVCH_SLEEP) != 0) {
+#ifdef DEBUG
+ (void) printf("Failed to allocate and send zone misc event.\n");
+#else
+ /* EMPTY */
+#endif
+ }
+ nvlist_free(nvl);
+}
+
static int
zone_set_bootargs(zone_t *zone, const char *zone_bootargs)
{
@@ -2529,9 +3113,14 @@ zone_set_brand(zone_t *zone, const char *brand)
return (EINVAL);
}
- /* set up the brand specific data */
+ /*
+ * Set up the brand specific data.
+ * Note that it's possible that the hook has to drop the
+ * zone_status_lock and reaquire it before returning so we can't
+ * assume the lock has been held the entire time.
+ */
zone->zone_brand = bp;
- ZBROP(zone)->b_init_brand_data(zone);
+ ZBROP(zone)->b_init_brand_data(zone, &zone_status_lock);
mutex_exit(&zone_status_lock);
return (0);
@@ -2604,18 +3193,6 @@ zone_set_initname(zone_t *zone, const char *zone_initname)
}
static int
-zone_set_phys_mcap(zone_t *zone, const uint64_t *zone_mcap)
-{
- uint64_t mcap;
- int err = 0;
-
- if ((err = copyin(zone_mcap, &mcap, sizeof (uint64_t))) == 0)
- zone->zone_phys_mcap = mcap;
-
- return (err);
-}
-
-static int
zone_set_sched_class(zone_t *zone, const char *new_class)
{
char sched_class[PC_CLNMSZ];
@@ -3022,6 +3599,12 @@ getzoneid(void)
return (curproc->p_zone->zone_id);
}
+zoneid_t
+getzonedid(void)
+{
+ return (curproc->p_zone->zone_did);
+}
+
/*
* Internal versions of zone_find_by_*(). These don't zone_hold() or
* check the validity of a zone's state.
@@ -3768,6 +4351,17 @@ zone_start_init(void)
*/
z->zone_proc_initpid = p->p_pid;
+ if (z->zone_setup_app_contract == B_TRUE) {
+ /*
+ * Normally a process cannot modify its own contract, but we're
+ * just starting the zone's init process and its contract is
+ * always initialized from the sys_process_tmpl template, so
+ * this is the simplest way to setup init's contract to kill
+ * the process if any other process in the contract exits.
+ */
+ p->p_ct_process->conp_ev_fatal |= CT_PR_EV_EXIT;
+ }
+
/*
* We maintain zone_boot_err so that we can return the cause of the
* failure back to the caller of the zone_boot syscall.
@@ -3796,9 +4390,54 @@ zone_start_init(void)
lwp_exit();
}
} else {
+ id_t cid = curthread->t_cid;
+
if (zone_status_get(z) == ZONE_IS_BOOTING)
zone_status_set(z, ZONE_IS_RUNNING);
mutex_exit(&zone_status_lock);
+
+ mutex_enter(&class_lock);
+ ASSERT(cid < loaded_classes);
+ if (strcmp(sclass[cid].cl_name, "FX") == 0 &&
+ z->zone_fixed_hipri) {
+ /*
+ * If the zone is using FX then by default all
+ * processes start at the lowest priority and stay
+ * there. We provide a mechanism for the zone to
+ * indicate that it should run at "high priority". In
+ * this case we setup init to run at the highest FX
+ * priority (which is one level higher than the
+ * non-fixed scheduling classes can use).
+ */
+ pcparms_t pcparms;
+
+ pcparms.pc_cid = cid;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_upri = FXMAXUPRI;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_uprilim =
+ FXMAXUPRI;
+ ((fxkparms_t *)pcparms.pc_clparms)->fx_cflags =
+ FX_DOUPRILIM | FX_DOUPRI;
+
+ mutex_enter(&pidlock);
+ mutex_enter(&curproc->p_lock);
+
+ (void) parmsset(&pcparms, curthread);
+
+ mutex_exit(&curproc->p_lock);
+ mutex_exit(&pidlock);
+ } else if (strcmp(sclass[cid].cl_name, "RT") == 0) {
+ /*
+ * zsched always starts the init lwp at priority
+ * minclsyspri - 1. This priority gets set in t_pri and
+ * is invalid for RT, but RT never uses t_pri. However
+ * t_pri is used by procfs, so we always see processes
+ * within an RT zone with an invalid priority value.
+ * We fix that up now.
+ */
+ curthread->t_pri = RTGPPRIO0;
+ }
+ mutex_exit(&class_lock);
+
/* cause the process to return to userland. */
lwp_rtt();
}
@@ -3839,7 +4478,11 @@ zsched(void *arg)
bcopy("zsched", PTOU(pp)->u_comm, sizeof ("zsched"));
PTOU(pp)->u_argc = 0;
PTOU(pp)->u_argv = 0;
+ PTOU(pp)->u_argvstrs = 0;
+ PTOU(pp)->u_argvstrsize = 0;
PTOU(pp)->u_envp = 0;
+ PTOU(pp)->u_envstrs = 0;
+ PTOU(pp)->u_envstrsize = 0;
PTOU(pp)->u_commpagep = 0;
closeall(P_FINFO(pp));
@@ -4284,8 +4927,9 @@ parse_rctls(caddr_t ubuf, size_t buflen, nvlist_t **nvlp)
error = EINVAL;
name = nvpair_name(nvp);
- if (strncmp(nvpair_name(nvp), "zone.", sizeof ("zone.") - 1)
- != 0 || nvpair_type(nvp) != DATA_TYPE_NVLIST_ARRAY) {
+ if ((strncmp(name, "zone.", sizeof ("zone.") - 1) != 0 &&
+ strncmp(name, "project.", sizeof ("project.") - 1) != 0) ||
+ nvpair_type(nvp) != DATA_TYPE_NVLIST_ARRAY) {
goto out;
}
if ((hndl = rctl_hndl_lookup(name)) == -1) {
@@ -4404,7 +5048,7 @@ zone_create(const char *zone_name, const char *zone_root,
caddr_t rctlbuf, size_t rctlbufsz,
caddr_t zfsbuf, size_t zfsbufsz, int *extended_error,
int match, uint32_t doi, const bslabel_t *label,
- int flags)
+ int flags, zoneid_t zone_did)
{
struct zsched_arg zarg;
nvlist_t *rctls = NULL;
@@ -4476,6 +5120,7 @@ zone_create(const char *zone_name, const char *zone_root,
zone = kmem_zalloc(sizeof (zone_t), KM_SLEEP);
zone->zone_id = zoneid;
+ zone->zone_did = zone_did;
zone->zone_status = ZONE_IS_UNINITIALIZED;
zone->zone_pool = pool_default;
zone->zone_pool_mod = gethrtime();
@@ -4485,6 +5130,7 @@ zone_create(const char *zone_name, const char *zone_root,
zone->zone_restart_init = B_TRUE;
zone->zone_reboot_on_init_exit = B_FALSE;
zone->zone_restart_init_0 = B_FALSE;
+ zone->zone_init_status = -1;
zone->zone_brand = &native_brand;
zone->zone_initname = NULL;
mutex_init(&zone->zone_lock, NULL, MUTEX_DEFAULT, NULL);
@@ -4551,8 +5197,13 @@ zone_create(const char *zone_name, const char *zone_root,
zone->zone_max_swap_ctl = UINT64_MAX;
zone->zone_max_lofi = 0;
zone->zone_max_lofi_ctl = UINT64_MAX;
- zone0.zone_lockedmem_kstat = NULL;
- zone0.zone_swapresv_kstat = NULL;
+ zone->zone_lockedmem_kstat = NULL;
+ zone->zone_swapresv_kstat = NULL;
+ zone->zone_physmem_kstat = NULL;
+
+ zone_pdata[zoneid].zpers_zfsp =
+ kmem_zalloc(sizeof (zone_zfs_io_t), KM_SLEEP);
+ zone_pdata[zoneid].zpers_zfsp->zpers_zfs_io_pri = 1;
zone->zone_ustate = cpu_uarray_zalloc(ZONE_USTATE_MAX, KM_SLEEP);
@@ -4561,6 +5212,13 @@ zone_create(const char *zone_name, const char *zone_root,
*/
zone->zone_rctls = NULL;
+ /*
+ * Ensure page count is 0 (in case zoneid has wrapped).
+ * Initialize physical memory cap as unlimited.
+ */
+ zone_pdata[zoneid].zpers_pg_cnt = 0;
+ zone_pdata[zoneid].zpers_pg_limit = UINT32_MAX;
+
if ((error = parse_rctls(rctlbuf, rctlbufsz, &rctls)) != 0) {
zone_free(zone);
return (zone_create_error(error, 0, extended_error));
@@ -4709,8 +5367,8 @@ zone_create(const char *zone_name, const char *zone_root,
/*
* The process, task, and project rctls are probably wrong;
* we need an interface to get the default values of all rctls,
- * and initialize zsched appropriately. I'm not sure that that
- * makes much of a difference, though.
+ * and initialize zsched appropriately. However, we allow zoneadmd
+ * to pass down both zone and project rctls for the zone's init.
*/
error = newproc(zsched, (void *)&zarg, syscid, minclsyspri, NULL, 0);
if (error != 0) {
@@ -4849,6 +5507,7 @@ zone_boot(zoneid_t zoneid)
static int
zone_empty(zone_t *zone)
{
+ int cnt = 0;
int waitstatus;
/*
@@ -4859,7 +5518,16 @@ zone_empty(zone_t *zone)
ASSERT(MUTEX_NOT_HELD(&zonehash_lock));
while ((waitstatus = zone_status_timedwait_sig(zone,
ddi_get_lbolt() + hz, ZONE_IS_EMPTY)) == -1) {
- killall(zone->zone_id);
+ boolean_t force = B_FALSE;
+
+ /* Every 30 seconds, try harder */
+ if (cnt++ >= 30) {
+ cmn_err(CE_WARN, "attempt to force kill zone %d\n",
+ zone->zone_id);
+ force = B_TRUE;
+ cnt = 0;
+ }
+ killall(zone->zone_id, force);
}
/*
* return EINTR if we were signaled
@@ -5188,6 +5856,7 @@ zone_destroy(zoneid_t zoneid)
zone_status_t status;
clock_t wait_time;
boolean_t log_refcounts;
+ zone_persist_t *zp;
if (secpolicy_zone_config(CRED()) != 0)
return (set_errno(EPERM));
@@ -5221,6 +5890,12 @@ zone_destroy(zoneid_t zoneid)
zone_hold(zone);
mutex_exit(&zonehash_lock);
+ zp = &zone_pdata[zoneid];
+ mutex_enter(&zp->zpers_zfs_lock);
+ kmem_free(zp->zpers_zfsp, sizeof (zone_zfs_io_t));
+ zp->zpers_zfsp = NULL;
+ mutex_exit(&zp->zpers_zfs_lock);
+
/*
* wait for zsched to exit
*/
@@ -5610,14 +6285,6 @@ zone_getattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
error = EFAULT;
}
break;
- case ZONE_ATTR_PHYS_MCAP:
- size = sizeof (zone->zone_phys_mcap);
- if (bufsize > size)
- bufsize = size;
- if (buf != NULL &&
- copyout(&zone->zone_phys_mcap, buf, bufsize) != 0)
- error = EFAULT;
- break;
case ZONE_ATTR_SCHED_CLASS:
mutex_enter(&class_lock);
@@ -5681,6 +6348,23 @@ zone_getattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
}
kmem_free(zbuf, bufsize);
break;
+ case ZONE_ATTR_DID:
+ size = sizeof (zoneid_t);
+ if (bufsize > size)
+ bufsize = size;
+
+ if (buf != NULL && copyout(&zone->zone_did, buf, bufsize) != 0)
+ error = EFAULT;
+ break;
+ case ZONE_ATTR_SCHED_FIXEDHI:
+ size = sizeof (boolean_t);
+ if (bufsize > size)
+ bufsize = size;
+
+ if (buf != NULL && copyout(&zone->zone_fixed_hipri, buf,
+ bufsize) != 0)
+ error = EFAULT;
+ break;
default:
if ((attr >= ZONE_ATTR_BRAND_ATTRS) && ZONE_IS_BRANDED(zone)) {
size = bufsize;
@@ -5712,10 +6396,9 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
return (set_errno(EPERM));
/*
- * Only the ZONE_ATTR_PHYS_MCAP attribute can be set on the
- * global zone.
+ * No attributes can be set on the global zone.
*/
- if (zoneid == GLOBAL_ZONEID && attr != ZONE_ATTR_PHYS_MCAP) {
+ if (zoneid == GLOBAL_ZONEID) {
return (set_errno(EINVAL));
}
@@ -5728,11 +6411,11 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
mutex_exit(&zonehash_lock);
/*
- * At present most attributes can only be set on non-running,
+ * At present attributes can only be set on non-running,
* non-global zones.
*/
zone_status = zone_status_get(zone);
- if (attr != ZONE_ATTR_PHYS_MCAP && zone_status > ZONE_IS_READY) {
+ if (zone_status > ZONE_IS_READY) {
err = EINVAL;
goto done;
}
@@ -5765,9 +6448,6 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
case ZONE_ATTR_SECFLAGS:
err = zone_set_secflags(zone, (psecflags_t *)buf);
break;
- case ZONE_ATTR_PHYS_MCAP:
- err = zone_set_phys_mcap(zone, (const uint64_t *)buf);
- break;
case ZONE_ATTR_SCHED_CLASS:
err = zone_set_sched_class(zone, (const char *)buf);
break;
@@ -5795,6 +6475,22 @@ zone_setattr(zoneid_t zoneid, int attr, void *buf, size_t bufsize)
err = zone_set_network(zoneid, zbuf);
kmem_free(zbuf, bufsize);
break;
+ case ZONE_ATTR_APP_SVC_CT:
+ if (bufsize != sizeof (boolean_t)) {
+ err = EINVAL;
+ } else {
+ zone->zone_setup_app_contract = (boolean_t)buf;
+ err = 0;
+ }
+ break;
+ case ZONE_ATTR_SCHED_FIXEDHI:
+ if (bufsize != sizeof (boolean_t)) {
+ err = EINVAL;
+ } else {
+ zone->zone_fixed_hipri = (boolean_t)buf;
+ err = 0;
+ }
+ break;
default:
if ((attr >= ZONE_ATTR_BRAND_ATTRS) && ZONE_IS_BRANDED(zone))
err = ZBROP(zone)->b_setattr(zone, attr, buf, bufsize);
@@ -6493,6 +7189,7 @@ zone(int cmd, void *arg1, void *arg2, void *arg3, void *arg4)
zs.doi = zs32.doi;
zs.label = (const bslabel_t *)(uintptr_t)zs32.label;
zs.flags = zs32.flags;
+ zs.zoneid = zs32.zoneid;
#else
panic("get_udatamodel() returned bogus result\n");
#endif
@@ -6503,7 +7200,7 @@ zone(int cmd, void *arg1, void *arg2, void *arg3, void *arg4)
(caddr_t)zs.rctlbuf, zs.rctlbufsz,
(caddr_t)zs.zfsbuf, zs.zfsbufsz,
zs.extended_error, zs.match, zs.doi,
- zs.label, zs.flags));
+ zs.label, zs.flags, zs.zoneid));
case ZONE_BOOT:
return (zone_boot((zoneid_t)(uintptr_t)arg1));
case ZONE_DESTROY:
@@ -6604,6 +7301,7 @@ zone_ki_call_zoneadmd(struct zarg *zargp)
bcopy(zone->zone_name, zone_name, zone_namelen);
zoneid = zone->zone_id;
uniqid = zone->zone_uniqid;
+ arg.status = zone->zone_init_status;
/*
* zoneadmd may be down, but at least we can empty out the zone.
* We can ignore the return value of zone_empty() since we're called
@@ -6781,7 +7479,7 @@ zone_kadmin(int cmd, int fcn, const char *mdep, cred_t *credp)
* zone_ki_call_zoneadmd() will do a more thorough job of this
* later.
*/
- killall(zone->zone_id);
+ killall(zone->zone_id, B_FALSE);
/*
* Now, create the thread to contact zoneadmd and do the rest of the
* work. This thread can't be created in our zone otherwise
@@ -6844,16 +7542,15 @@ zone_shutdown_global(void)
}
/*
- * Returns true if the named dataset is visible in the current zone.
+ * Returns true if the named dataset is visible in the specified zone.
* The 'write' parameter is set to 1 if the dataset is also writable.
*/
int
-zone_dataset_visible(const char *dataset, int *write)
+zone_dataset_visible_inzone(zone_t *zone, const char *dataset, int *write)
{
static int zfstype = -1;
zone_dataset_t *zd;
size_t len;
- zone_t *zone = curproc->p_zone;
const char *name = NULL;
vfs_t *vfsp = NULL;
@@ -6921,7 +7618,8 @@ zone_dataset_visible(const char *dataset, int *write)
vfs_list_read_lock();
vfsp = zone->zone_vfslist;
do {
- ASSERT(vfsp);
+ if (vfsp == NULL)
+ break;
if (vfsp->vfs_fstype == zfstype) {
name = refstr_value(vfsp->vfs_resource);
@@ -6958,6 +7656,18 @@ zone_dataset_visible(const char *dataset, int *write)
}
/*
+ * Returns true if the named dataset is visible in the current zone.
+ * The 'write' parameter is set to 1 if the dataset is also writable.
+ */
+int
+zone_dataset_visible(const char *dataset, int *write)
+{
+ zone_t *zone = curproc->p_zone;
+
+ return (zone_dataset_visible_inzone(zone, dataset, write));
+}
+
+/*
* zone_find_by_any_path() -
*
* kernel-private routine similar to zone_find_by_path(), but which
@@ -7059,6 +7769,27 @@ zone_add_datalink(zoneid_t zoneid, datalink_id_t linkid)
zone_t *zone;
zone_t *thiszone;
+ /*
+ * Only the GZ may add a datalink to a zone's list.
+ */
+ if (getzoneid() != GLOBAL_ZONEID)
+ return (set_errno(EPERM));
+
+ /*
+ * Only a process with the datalink config priv may add a
+ * datalink to a zone's list.
+ */
+ if (secpolicy_dl_config(CRED()) != 0)
+ return (set_errno(EPERM));
+
+ /*
+ * When links exist in the GZ, they aren't added to the GZ's
+ * zone_dl_list. We must enforce this because link_activate()
+ * depends on zone_check_datalink() returning only NGZs.
+ */
+ if (zoneid == GLOBAL_ZONEID)
+ return (set_errno(EINVAL));
+
if ((thiszone = zone_find_by_id(zoneid)) == NULL)
return (set_errno(ENXIO));
@@ -7091,6 +7822,26 @@ zone_remove_datalink(zoneid_t zoneid, datalink_id_t linkid)
zone_t *zone;
int err = 0;
+ /*
+ * Only the GZ may remove a datalink from a zone's list.
+ */
+ if (getzoneid() != GLOBAL_ZONEID)
+ return (set_errno(EPERM));
+
+ /*
+ * Only a process with the datalink config priv may remove a
+ * datalink from a zone's list.
+ */
+ if (secpolicy_dl_config(CRED()) != 0)
+ return (set_errno(EPERM));
+
+ /*
+ * If we can't add a datalink to the GZ's zone_dl_list then we
+ * certainly can't remove them either.
+ */
+ if (zoneid == GLOBAL_ZONEID)
+ return (set_errno(EINVAL));
+
if ((zone = zone_find_by_id(zoneid)) == NULL)
return (set_errno(EINVAL));
@@ -7108,25 +7859,63 @@ zone_remove_datalink(zoneid_t zoneid, datalink_id_t linkid)
}
/*
- * Using the zoneidp as ALL_ZONES, we can lookup which zone has been assigned
- * the linkid. Otherwise we just check if the specified zoneidp has been
- * assigned the supplied linkid.
+ *
+ * This function may be used in two ways:
+ *
+ * 1. to get the zoneid of the zone this link is under, or
+ *
+ * 2. to verify that the link is under a specific zone.
+ *
+ * The first use is achieved by passing a zoneid of ALL_ZONES. The
+ * function then iterates the datalink list of every zone on the
+ * system until it finds the linkid. If the linkid is found then the
+ * function returns 0 and zoneidp is updated. Otherwise, ENXIO is
+ * returned and zoneidp is not modified. The use of ALL_ZONES is
+ * limited to callers in the GZ to prevent leaking information to
+ * NGZs. If an NGZ passes ALL_ZONES it's query is implicitly changed
+ * to the second type in the list above.
+ *
+ * The second use is achieved by passing a specific zoneid. The GZ can
+ * use this to verify a link is under a particular zone. An NGZ can
+ * use this to verify a link is under itself. But an NGZ cannot use
+ * this to determine if a link is under some other zone as that would
+ * result in information leakage. If the link exists under the zone
+ * then 0 is returned. Otherwise, ENXIO is returned.
*/
int
zone_check_datalink(zoneid_t *zoneidp, datalink_id_t linkid)
{
zone_t *zone;
+ zoneid_t zoneid = *zoneidp;
+ zoneid_t caller = getzoneid();
int err = ENXIO;
- if (*zoneidp != ALL_ZONES) {
- if ((zone = zone_find_by_id(*zoneidp)) != NULL) {
- if (zone_dl_exists(zone, linkid))
+ /*
+ * Only the GZ may enquire about all zones; an NGZ may only
+ * enuqire about itself.
+ */
+ if (zoneid == ALL_ZONES && caller != GLOBAL_ZONEID)
+ zoneid = caller;
+
+ if (zoneid != caller && caller != GLOBAL_ZONEID)
+ return (err);
+
+ if (zoneid != ALL_ZONES) {
+ if ((zone = zone_find_by_id(zoneid)) != NULL) {
+ if (zone_dl_exists(zone, linkid)) {
+ /*
+ * We need to set this in case an NGZ
+ * passes ALL_ZONES.
+ */
+ *zoneidp = zoneid;
err = 0;
+ }
zone_rele(zone);
}
return (err);
}
+ ASSERT(caller == GLOBAL_ZONEID);
mutex_enter(&zonehash_lock);
for (zone = list_head(&zone_active); zone != NULL;
zone = list_next(&zone_active, zone)) {
@@ -7137,6 +7926,7 @@ zone_check_datalink(zoneid_t *zoneidp, datalink_id_t linkid)
}
}
mutex_exit(&zonehash_lock);
+
return (err);
}
@@ -7157,6 +7947,12 @@ zone_list_datalink(zoneid_t zoneid, int *nump, datalink_id_t *idarray)
zone_dl_t *zdl;
datalink_id_t *idptr = idarray;
+ /*
+ * Only the GZ or the owning zone may look at the datalink list.
+ */
+ if ((getzoneid() != GLOBAL_ZONEID) && (getzoneid() != zoneid))
+ return (set_errno(EPERM));
+
if (copyin(nump, &dlcount, sizeof (dlcount)) != 0)
return (set_errno(EFAULT));
if ((zone = zone_find_by_id(zoneid)) == NULL)
@@ -7182,6 +7978,13 @@ zone_list_datalink(zoneid_t zoneid, int *nump, datalink_id_t *idarray)
mutex_exit(&zone->zone_lock);
zone_rele(zone);
+ /*
+ * Prevent returning negative nump values -- we should never
+ * have this many links anyways.
+ */
+ if (num > INT_MAX)
+ return (set_errno(EOVERFLOW));
+
/* Increased or decreased, caller should be notified. */
if (num != dlcount) {
if (copyout(&num, nump, sizeof (num)) != 0)
@@ -7395,3 +8198,231 @@ done:
else
return (0);
}
+
+static void
+zone_incr_capped(zoneid_t zid)
+{
+ zone_persist_t *zp = &zone_pdata[zid];
+
+ /* See if over (unlimited is UINT32_MAX), or already marked that way. */
+ if (zp->zpers_pg_cnt <= zp->zpers_pg_limit || zp->zpers_over == 1) {
+ return;
+ }
+
+ mutex_enter(&zone_physcap_lock);
+ /* Recheck setting under mutex */
+ if (zp->zpers_pg_cnt > zp->zpers_pg_limit && zp->zpers_over == 0) {
+ zp->zpers_over = 1;
+ zp->zpers_nover++;
+ zone_num_over_cap++;
+ DTRACE_PROBE1(zone__over__pcap, zoneid_t, zid);
+ }
+ mutex_exit(&zone_physcap_lock);
+}
+
+/*
+ * We want some hysteresis when the zone is going under its cap so that we're
+ * not continuously toggling page scanning back and forth by a single page
+ * around the cap. Using ~1% of the zone's page limit seems to be a good
+ * quantity. This table shows some various zone memory caps and the number of
+ * pages (assuming a 4k page size). Given this, we choose to shift the page
+ * limit by 7 places to get a hysteresis that is slightly less than 1%.
+ *
+ * cap pages pages 1% shift7 shift7
+ * 128M 32768 0x0008000 327 256 0x00100
+ * 512M 131072 0x0020000 1310 1024 0x00400
+ * 1G 262144 0x0040000 2621 2048 0x00800
+ * 4G 1048576 0x0100000 10485 8192 0x02000
+ * 8G 2097152 0x0200000 20971 16384 0x04000
+ * 16G 4194304 0x0400000 41943 32768 0x08000
+ * 32G 8388608 0x0800000 83886 65536 0x10000
+ * 64G 16777216 0x1000000 167772 131072 0x20000
+ */
+static void
+zone_decr_capped(zoneid_t zid)
+{
+ zone_persist_t *zp = &zone_pdata[zid];
+ uint32_t adjusted_limit;
+
+ /*
+ * See if under, or already marked that way. There is no need to
+ * check for an unlimited cap (zpers_pg_limit == UINT32_MAX)
+ * since we'll never set zpers_over in zone_incr_capped().
+ */
+ if (zp->zpers_over == 0 || zp->zpers_pg_cnt >= zp->zpers_pg_limit) {
+ return;
+ }
+
+ adjusted_limit = zp->zpers_pg_limit - (zp->zpers_pg_limit >> 7);
+
+ /* Recheck, accounting for our hysteresis. */
+ if (zp->zpers_pg_cnt >= adjusted_limit) {
+ return;
+ }
+
+ mutex_enter(&zone_physcap_lock);
+ /* Recheck under mutex. */
+ if (zp->zpers_pg_cnt < adjusted_limit && zp->zpers_over == 1) {
+ zp->zpers_over = 0;
+ ASSERT(zone_num_over_cap > 0);
+ zone_num_over_cap--;
+ DTRACE_PROBE1(zone__under__pcap, zoneid_t, zid);
+ }
+ mutex_exit(&zone_physcap_lock);
+}
+
+/*
+ * For zone_add_page() and zone_rm_page(), access to the page we're touching is
+ * controlled by our caller's locking.
+ * On x86 our callers already did: ASSERT(x86_hm_held(pp))
+ * On SPARC our callers already did: ASSERT(sfmmu_mlist_held(pp))
+ */
+void
+zone_add_page(page_t *pp)
+{
+ uint_t pcnt;
+ zone_persist_t *zp;
+ zoneid_t zid;
+
+ /* Skip pages in segkmem, etc. (KV_KVP, ...) */
+ if (PP_ISKAS(pp))
+ return;
+
+ ASSERT(!PP_ISFREE(pp));
+
+ zid = curzone->zone_id;
+ if (pp->p_zoneid == zid) {
+ /* Another mapping to this page for this zone, do nothing */
+ return;
+ }
+
+ if (pp->p_szc == 0) {
+ pcnt = 1;
+ } else {
+ /* large page */
+ pcnt = page_get_pagecnt(pp->p_szc);
+ }
+
+ if (pp->p_share == 0) {
+ /* First mapping to this page. */
+ pp->p_zoneid = zid;
+ zp = &zone_pdata[zid];
+ ASSERT(zp->zpers_pg_cnt + pcnt < UINT32_MAX);
+ atomic_add_32((uint32_t *)&zp->zpers_pg_cnt, pcnt);
+ zone_incr_capped(zid);
+ return;
+ }
+
+ if (pp->p_zoneid != ALL_ZONES) {
+ /*
+ * The page is now being shared across a different zone.
+ * Decrement the original zone's usage.
+ */
+ zid = pp->p_zoneid;
+ pp->p_zoneid = ALL_ZONES;
+ ASSERT(zid >= 0 && zid <= MAX_ZONEID);
+ zp = &zone_pdata[zid];
+
+ if (zp->zpers_pg_cnt > 0) {
+ atomic_add_32((uint32_t *)&zp->zpers_pg_cnt, -pcnt);
+ }
+ zone_decr_capped(zid);
+ }
+}
+
+void
+zone_rm_page(page_t *pp)
+{
+ uint_t pcnt;
+ zone_persist_t *zp;
+ zoneid_t zid;
+
+ /* Skip pages in segkmem, etc. (KV_KVP, ...) */
+ if (PP_ISKAS(pp))
+ return;
+
+ zid = pp->p_zoneid;
+ if (zid == ALL_ZONES || pp->p_share != 0)
+ return;
+
+ /* This is the last mapping to the page for a zone. */
+ if (pp->p_szc == 0) {
+ pcnt = 1;
+ } else {
+ /* large page */
+ pcnt = (int64_t)page_get_pagecnt(pp->p_szc);
+ }
+
+ ASSERT(zid >= 0 && zid <= MAX_ZONEID);
+ zp = &zone_pdata[zid];
+ if (zp->zpers_pg_cnt > 0) {
+ atomic_add_32((uint32_t *)&zp->zpers_pg_cnt, -pcnt);
+ }
+ zone_decr_capped(zid);
+ pp->p_zoneid = ALL_ZONES;
+}
+
+void
+zone_pageout_stat(int zid, zone_pageout_op_t op)
+{
+ zone_persist_t *zp;
+
+ if (zid == ALL_ZONES)
+ return;
+
+ ASSERT(zid >= 0 && zid <= MAX_ZONEID);
+ zp = &zone_pdata[zid];
+
+#ifndef DEBUG
+ atomic_add_64(&zp->zpers_pg_out, 1);
+#else
+ switch (op) {
+ case ZPO_DIRTY:
+ atomic_add_64(&zp->zpers_pg_fsdirty, 1);
+ break;
+ case ZPO_FS:
+ atomic_add_64(&zp->zpers_pg_fs, 1);
+ break;
+ case ZPO_ANON:
+ atomic_add_64(&zp->zpers_pg_anon, 1);
+ break;
+ case ZPO_ANONDIRTY:
+ atomic_add_64(&zp->zpers_pg_anondirty, 1);
+ break;
+ default:
+ cmn_err(CE_PANIC, "Invalid pageout operator %d", op);
+ break;
+ }
+#endif
+}
+
+/*
+ * Return the zone's physical memory cap and current free memory (in pages).
+ */
+void
+zone_get_physmem_data(int zid, pgcnt_t *memcap, pgcnt_t *free)
+{
+ zone_persist_t *zp;
+
+ ASSERT(zid >= 0 && zid <= MAX_ZONEID);
+ zp = &zone_pdata[zid];
+
+ /*
+ * If memory or swap limits are set on the zone, use those, otherwise
+ * use the system values. physmem and freemem are also in pages.
+ */
+ if (zp->zpers_pg_limit == UINT32_MAX) {
+ *memcap = physmem;
+ *free = freemem;
+ } else {
+ int64_t freemem;
+
+ *memcap = (pgcnt_t)zp->zpers_pg_limit;
+ freemem = zp->zpers_pg_limit - zp->zpers_pg_cnt;
+ if (freemem > 0) {
+ *free = (pgcnt_t)freemem;
+ } else {
+ *free = (pgcnt_t)0;
+ }
+ }
+}
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 70c8342377..94922f459b 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -259,6 +259,7 @@ CHKHDRS= \
flock.h \
flock_impl.h \
fork.h \
+ frameio.h \
fss.h \
fsspriocntl.h \
fsid.h \
@@ -284,6 +285,7 @@ CHKHDRS= \
idmap.h \
ieeefp.h \
id_space.h \
+ inotify.h \
instance.h \
int_const.h \
int_fmtio.h \
@@ -496,6 +498,7 @@ CHKHDRS= \
rctl_impl.h \
rds.h \
reboot.h \
+ refhash.h \
refstr.h \
refstr_impl.h \
resource.h \
@@ -661,6 +664,8 @@ CHKHDRS= \
vmem.h \
vmem_impl.h \
vmsystm.h \
+ vnd.h \
+ vnd_errno.h \
vnic.h \
vnic_impl.h \
vnode.h \
@@ -678,6 +683,7 @@ CHKHDRS= \
watchpoint.h \
winlockio.h \
zcons.h \
+ zfd.h \
zone.h \
xti_inet.h \
xti_osi.h \
@@ -839,14 +845,14 @@ FSHDRS= \
autofs.h \
decomp.h \
dv_node.h \
- sdev_impl.h \
- sdev_plugin.h \
fifonode.h \
hsfs_isospec.h \
hsfs_node.h \
hsfs_rrip.h \
hsfs_spec.h \
hsfs_susp.h \
+ hyprlofs.h \
+ hyprlofs_info.h \
lofs_info.h \
lofs_node.h \
mntdata.h \
@@ -856,6 +862,8 @@ FSHDRS= \
pc_label.h \
pc_node.h \
pxfs_ki.h \
+ sdev_impl.h \
+ sdev_plugin.h \
snode.h \
swapnode.h \
tmp.h \
diff --git a/usr/src/uts/common/sys/acct.h b/usr/src/uts/common/sys/acct.h
index f00884681b..e01ad61025 100644
--- a/usr/src/uts/common/sys/acct.h
+++ b/usr/src/uts/common/sys/acct.h
@@ -22,6 +22,7 @@
/*
* Copyright 2014 Garrett D'Amore <garrett@damore.org>
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2017 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -88,7 +89,7 @@ extern int acct(const char *);
#if defined(_KERNEL)
-void acct(char);
+void acct(int);
int sysacct(char *);
struct vnode;
diff --git a/usr/src/uts/common/sys/auxv.h b/usr/src/uts/common/sys/auxv.h
index e591320025..203b884cd3 100644
--- a/usr/src/uts/common/sys/auxv.h
+++ b/usr/src/uts/common/sys/auxv.h
@@ -79,6 +79,9 @@ typedef struct {
#define AT_FLAGS 8 /* processor flags */
#define AT_ENTRY 9 /* a.out entry point */
+/* First introduced on Linux */
+#define AT_RANDOM 25 /* address of 16 random bytes */
+
/*
* These relate to the original PPC ABI document; Linux reused
* the values for other things (see below), so disambiguation of
@@ -91,19 +94,18 @@ typedef struct {
* These are the values from LSB 1.3, the first five are also described
* in the draft amd64 ABI.
*
- * At the time of writing, Solaris doesn't place any of these values into
- * the aux vector, except AT_CLKTCK which is placed on the aux vector for
- * lx branded processes; also, we do similar things via AT_SUN_ values.
+ * At the time of writing, illumos doesn't place any of these values into the
+ * aux vector, except where noted. We do similar things via AT_SUN_ values.
*
* AT_NOTELF 10 program is not ELF?
- * AT_UID 11 real user id
- * AT_EUID 12 effective user id
- * AT_GID 13 real group id
- * AT_EGID 14 effective group id
+ * AT_UID 11 real user id (provided in LX)
+ * AT_EUID 12 effective user id (provided in LX)
+ * AT_GID 13 real group id (provided in LX)
+ * AT_EGID 14 effective group id (provided in LX)
*
* AT_PLATFORM 15
* AT_HWCAP 16
- * AT_CLKTCK 17 c.f. _SC_CLK_TCK
+ * AT_CLKTCK 17 c.f. _SC_CLK_TCK (provided in LX)
* AT_FPUCW 18
*
* AT_DCACHEBSIZE 19 (moved from 10)
@@ -111,6 +113,16 @@ typedef struct {
* AT_UCACHEBSIZE 21 (moved from 12)
*
* AT_IGNOREPPC 22
+ *
+ * On Linux:
+ * AT_* values 18 through 22 are reserved
+ * AT_SECURE 23 secure mode boolean (provided in LX)
+ * AT_BASE_PLATFORM 24 string identifying real platform, may
+ * differ from AT_PLATFORM.
+ * AT_HWCAP2 26 extension of AT_HWCAP
+ * AT_EXECFN 31 filename of program
+ * AT_SYSINFO 32
+ * AT_SYSINFO_EHDR 33 The vDSO location
*/
/*
@@ -190,6 +202,8 @@ extern uint_t getisax(uint32_t *, uint_t);
#define AT_SUN_BRAND_AUX1 2020
#define AT_SUN_BRAND_AUX2 2021
#define AT_SUN_BRAND_AUX3 2022
+#define AT_SUN_BRAND_AUX4 2025
+#define AT_SUN_BRAND_NROOT 2024
/*
* Aux vector for comm page
diff --git a/usr/src/uts/common/sys/brand.h b/usr/src/uts/common/sys/brand.h
index a2feda573d..bace1142f9 100644
--- a/usr/src/uts/common/sys/brand.h
+++ b/usr/src/uts/common/sys/brand.h
@@ -103,28 +103,106 @@ struct brand_mach_ops;
struct intpdata;
struct execa;
+/*
+ * Common structure to define hooks for brand operation.
+ *
+ * Required Fields:
+ * b_init_brand_data - Setup zone brand data during zone_setbrand
+ * b_free_brand_data - Free zone brand data during zone_destroy
+ * b_brandsys - Syscall handler for brandsys
+ * b_setbrand - Initialize process brand data
+ * b_getattr - Get brand-custom zone attribute
+ * b_setattr - Set brand-custom zone attribute
+ * b_copy_procdata - Copy process brand data during fork
+ * b_proc_exit - Perform process brand exit processing
+ * b_exec - Reset branded process state on exec
+ * b_lwp_setrval - Set return code for forked child
+ * b_initlwp - Initialize lwp brand data (cannot drop p->p_lock)
+ * b_forklwp - Copy lwp brand data during fork
+ * b_freelwp - Free lwp brand data
+ * b_lwpexit - Perform lwp-specific brand exit processing
+ * b_elfexec - Load and execute ELF binary
+ * b_sigset_native_to_brand - Convert sigset native->brand
+ * b_sigset_brand_to_native - Convert sigset brand->native
+ * b_nsig - Maxiumum signal number
+ * b_sendsig - Update process state after sendsig
+ *
+ * Optional Fields:
+ * b_lwpdata_alloc - Speculatively allocate data for use in b_initlwp
+ * b_lwpdata_free - Free data from allocated by b_lwpdata_alloc if errors occur
+ * during lwp creation before b_initlwp could be called.
+ * b_initlwp_post - Complete lwp branding (can temporarily drop p->p_lock)
+ * b_exit_with_sig - Instead of sending SIGCLD, exit with custom behavior
+ * b_psig_to_proc - Custom additional behavior during psig
+ * b_wait_filter - Filter processes from being matched by waitid
+ * b_native_exec - Provide interpreter path prefix for executables
+ * b_ptrace_exectrap - Custom behavior for legacy ptrace traps
+ * b_map32limit - Specify alternate limit for MAP_32BIT mappings
+ * b_stop_notify - Hook process stop events
+ * b_waitid_helper - Generate synthetic results for waitid
+ * b_sigcld_repost - Post synthetic SIGCLD signals
+ * b_issig_stop - Alter/suppress signal delivery during issig
+ * b_sig_ignorable - Disallow discarding of signals
+ * b_savecontext - Alter context during savecontext
+ * b_restorecontext - Alter context during restorecontext
+ * b_sendsig_stack - Override stack used for signal delivery
+ * b_setid_clear - Override setid_clear behavior
+ * b_pagefault - Trap pagefault events
+ * b_intp_parse_arg - Controls interpreter argument handling (allow 1 or all)
+ * b_clearbrand - Perform any actions necessary when clearing the brand.
+ * b_rpc_statd - Upcall to rpc.statd running within the zone
+ * b_acct_out - Output properly formatted accounting record
+ */
struct brand_ops {
- void (*b_init_brand_data)(zone_t *);
+ void (*b_init_brand_data)(zone_t *, kmutex_t *);
void (*b_free_brand_data)(zone_t *);
int (*b_brandsys)(int, int64_t *, uintptr_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t);
void (*b_setbrand)(struct proc *);
int (*b_getattr)(zone_t *, int, void *, size_t *);
int (*b_setattr)(zone_t *, int, void *, size_t);
void (*b_copy_procdata)(struct proc *, struct proc *);
- void (*b_proc_exit)(struct proc *, klwp_t *);
+ void (*b_proc_exit)(struct proc *);
void (*b_exec)();
void (*b_lwp_setrval)(klwp_t *, int, int);
- int (*b_initlwp)(klwp_t *);
+ void *(*b_lwpdata_alloc)(struct proc *);
+ void (*b_lwpdata_free)(void *);
+ void (*b_initlwp)(klwp_t *, void *);
+ void (*b_initlwp_post)(klwp_t *);
void (*b_forklwp)(klwp_t *, klwp_t *);
void (*b_freelwp)(klwp_t *);
void (*b_lwpexit)(klwp_t *);
int (*b_elfexec)(struct vnode *, struct execa *, struct uarg *,
- struct intpdata *, int, size_t *, int, caddr_t, struct cred *,
- int);
+ struct intpdata *, int, size_t *, int, caddr_t, struct cred *,
+ int *);
void (*b_sigset_native_to_brand)(sigset_t *);
void (*b_sigset_brand_to_native)(sigset_t *);
+ void (*b_sigfd_translate)(k_siginfo_t *);
int b_nsig;
+ void (*b_exit_with_sig)(proc_t *, sigqueue_t *);
+ boolean_t (*b_wait_filter)(proc_t *, proc_t *);
+ boolean_t (*b_native_exec)(uint8_t, const char **);
+ uint32_t (*b_map32limit)(proc_t *);
+ void (*b_stop_notify)(proc_t *, klwp_t *, ushort_t, ushort_t);
+ int (*b_waitid_helper)(idtype_t, id_t, k_siginfo_t *, int,
+ boolean_t *, int *);
+ int (*b_sigcld_repost)(proc_t *, sigqueue_t *);
+ int (*b_issig_stop)(proc_t *, klwp_t *);
+ boolean_t (*b_sig_ignorable)(proc_t *, klwp_t *, int);
+ void (*b_savecontext)(ucontext_t *);
+#if defined(_SYSCALL32_IMPL)
+ void (*b_savecontext32)(ucontext32_t *);
+#endif
+ void (*b_restorecontext)(ucontext_t *);
+ caddr_t (*b_sendsig_stack)(int);
+ void (*b_sendsig)(int);
+ int (*b_setid_clear)(vattr_t *vap, cred_t *cr);
+ int (*b_pagefault)(proc_t *, klwp_t *, caddr_t, enum fault_type,
+ enum seg_rw);
+ boolean_t b_intp_parse_arg;
+ void (*b_clearbrand)(proc_t *, boolean_t);
+ void (*b_rpc_statd)(int, void *, void *);
+ void (*b_acct_out)(struct vnode *, int);
};
/*
@@ -135,6 +213,7 @@ typedef struct brand {
char *b_name;
struct brand_ops *b_ops;
struct brand_mach_ops *b_machops;
+ size_t b_data_size;
} brand_t;
extern brand_t native_brand;
@@ -165,7 +244,7 @@ extern brand_t *brand_register_zone(struct brand_attr *);
extern brand_t *brand_find_name(char *);
extern void brand_unregister_zone(brand_t *);
extern int brand_zone_count(brand_t *);
-extern void brand_setbrand(proc_t *);
+extern int brand_setbrand(proc_t *, boolean_t);
extern void brand_clearbrand(proc_t *, boolean_t);
/*
@@ -178,17 +257,16 @@ extern int brand_solaris_cmd(int, uintptr_t, uintptr_t, uintptr_t,
extern void brand_solaris_copy_procdata(proc_t *, proc_t *,
struct brand *);
extern int brand_solaris_elfexec(vnode_t *, execa_t *, uarg_t *,
- intpdata_t *, int, size_t *, int, caddr_t, cred_t *, int,
- struct brand *, char *, char *, char *, char *, char *);
+ intpdata_t *, int, size_t *, int, caddr_t, cred_t *, int *,
+ struct brand *, char *, char *, char *);
extern void brand_solaris_exec(struct brand *);
extern int brand_solaris_fini(char **, struct modlinkage *,
struct brand *);
extern void brand_solaris_forklwp(klwp_t *, klwp_t *, struct brand *);
extern void brand_solaris_freelwp(klwp_t *, struct brand *);
-extern int brand_solaris_initlwp(klwp_t *, struct brand *);
+extern void brand_solaris_initlwp(klwp_t *, struct brand *);
extern void brand_solaris_lwpexit(klwp_t *, struct brand *);
-extern void brand_solaris_proc_exit(struct proc *, klwp_t *,
- struct brand *);
+extern void brand_solaris_proc_exit(struct proc *, struct brand *);
extern void brand_solaris_setbrand(proc_t *, struct brand *);
#if defined(_SYSCALL32)
diff --git a/usr/src/uts/common/sys/buf.h b/usr/src/uts/common/sys/buf.h
index e20e0e0c35..b6b5c20e44 100644
--- a/usr/src/uts/common/sys/buf.h
+++ b/usr/src/uts/common/sys/buf.h
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2012 Joyent, Inc. All rights reserved.
*
* Copyright 2017 RackTop Systems.
*/
@@ -188,6 +189,7 @@ struct biostats {
#define B_STARTED 0x2000000 /* io:::start probe called for buf */
#define B_ABRWRITE 0x4000000 /* Application based recovery active */
#define B_PAGE_NOWAIT 0x8000000 /* Skip the page if it is locked */
+#define B_INVALCURONLY 0x10000000 /* invalidate only for curproc */
/*
* There is some confusion over the meaning of B_FREE and B_INVAL and what
@@ -200,6 +202,12 @@ struct biostats {
* between the sole use of these two flags. In both cases, IO will be done
* if the page is not yet committed to storage.
*
+ * The B_INVALCURONLY flag modifies the behavior of the B_INVAL flag and is
+ * intended to be used in conjunction with B_INVAL. B_INVALCURONLY has no
+ * meaning on its own. When both B_INVALCURONLY and B_INVAL are set, then
+ * the mapping for the page is only invalidated for the current process.
+ * In this case, the page is not destroyed unless this was the final mapping.
+ *
* In order to discard pages without writing them back, (B_INVAL | B_TRUNC)
* should be used.
*
diff --git a/usr/src/uts/common/sys/contract/process.h b/usr/src/uts/common/sys/contract/process.h
index 21cf94dcf9..2c70d7c9f1 100644
--- a/usr/src/uts/common/sys/contract/process.h
+++ b/usr/src/uts/common/sys/contract/process.h
@@ -21,13 +21,12 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_CONTRACT_PROCESS_H
#define _SYS_CONTRACT_PROCESS_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/contract.h>
#include <sys/time.h>
@@ -55,7 +54,8 @@ typedef struct cont_process cont_process_t;
#define CT_PR_NOORPHAN 0x2 /* kill when contract is abandoned */
#define CT_PR_PGRPONLY 0x4 /* only kill process group on fatal errors */
#define CT_PR_REGENT 0x8 /* automatically detach inherited contracts */
-#define CT_PR_ALLPARAM 0xf
+#define CT_PR_KEEP_EXEC 0x10 /* preserve template accross exec */
+#define CT_PR_ALLPARAM 0x1f
/*
* ctr_ev_* flags
diff --git a/usr/src/uts/common/sys/cpucaps.h b/usr/src/uts/common/sys/cpucaps.h
index 6063ff4380..6bc042108c 100644
--- a/usr/src/uts/common/sys/cpucaps.h
+++ b/usr/src/uts/common/sys/cpucaps.h
@@ -22,6 +22,7 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2011, 2012, Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_CPUCAPS_H
@@ -84,12 +85,16 @@ extern void cpucaps_zone_remove(zone_t *);
*/
extern int cpucaps_project_set(kproject_t *, rctl_qty_t);
extern int cpucaps_zone_set(zone_t *, rctl_qty_t);
+extern int cpucaps_zone_set_base(zone_t *, rctl_qty_t);
+extern int cpucaps_zone_set_burst_time(zone_t *, rctl_qty_t);
/*
* Get current CPU usage for a project/zone.
*/
extern rctl_qty_t cpucaps_project_get(kproject_t *);
extern rctl_qty_t cpucaps_zone_get(zone_t *);
+extern rctl_qty_t cpucaps_zone_get_base(zone_t *);
+extern rctl_qty_t cpucaps_zone_get_burst_time(zone_t *);
/*
* Scheduling class hooks into CPU caps framework.
diff --git a/usr/src/uts/common/sys/cpucaps_impl.h b/usr/src/uts/common/sys/cpucaps_impl.h
index 95afd21827..2cd4ed644d 100644
--- a/usr/src/uts/common/sys/cpucaps_impl.h
+++ b/usr/src/uts/common/sys/cpucaps_impl.h
@@ -22,6 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2011, 2012, Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_CPUCAPS_IMPL_H
@@ -66,8 +67,12 @@ typedef struct cpucap {
waitq_t cap_waitq; /* waitq for capped threads */
kstat_t *cap_kstat; /* cpucaps specific kstat */
int64_t cap_gen; /* zone cap specific */
+ hrtime_t cap_chk_value; /* effective CPU usage cap */
hrtime_t cap_value; /* scaled CPU usage cap */
hrtime_t cap_usage; /* current CPU usage */
+ hrtime_t cap_base; /* base CPU for burst */
+ u_longlong_t cap_burst_limit; /* max secs (in tics) for a burst */
+ u_longlong_t cap_bursting; /* # of ticks currently bursting */
disp_lock_t cap_usagelock; /* protects cap_usage above */
/*
* Per cap statistics.
@@ -75,6 +80,7 @@ typedef struct cpucap {
hrtime_t cap_maxusage; /* maximum cap usage */
u_longlong_t cap_below; /* # of ticks spend below the cap */
u_longlong_t cap_above; /* # of ticks spend above the cap */
+ u_longlong_t cap_above_base; /* # of ticks spent above the base */
} cpucap_t;
/*
diff --git a/usr/src/uts/common/sys/cpuvar.h b/usr/src/uts/common/sys/cpuvar.h
index 7b153a3e9e..24adbb7418 100644
--- a/usr/src/uts/common/sys/cpuvar.h
+++ b/usr/src/uts/common/sys/cpuvar.h
@@ -390,6 +390,8 @@ extern cpu_core_t cpu_core[];
#define CPU_DISP_DONTSTEAL 0x01 /* CPU undergoing context swtch */
#define CPU_DISP_HALTED 0x02 /* CPU halted waiting for interrupt */
+/* Note: inside ifdef: _KERNEL || _KMEMUSER || _BOOT */
+
/*
* Macros for manipulating sets of CPUs as a bitmap. Note that this
* bitmap may vary in size depending on the maximum CPU id a specific
@@ -512,6 +514,7 @@ extern struct cpu **cpu_seq; /* indexed by sequential CPU id */
extern cpu_t *cpu_list; /* list of CPUs */
extern cpu_t *cpu_active; /* list of active CPUs */
extern cpuset_t cpu_active_set; /* cached set of active CPUs */
+extern cpuset_t cpu_available; /* cached set of available CPUs */
extern int ncpus; /* number of CPUs present */
extern int ncpus_online; /* number of CPUs not quiesced */
extern int ncpus_intr_enabled; /* nr of CPUs taking I/O intrs */
diff --git a/usr/src/uts/common/sys/cred.h b/usr/src/uts/common/sys/cred.h
index fb79dfecde..1f938132e0 100644
--- a/usr/src/uts/common/sys/cred.h
+++ b/usr/src/uts/common/sys/cred.h
@@ -93,6 +93,7 @@ extern gid_t crgetgid(const cred_t *);
extern gid_t crgetrgid(const cred_t *);
extern gid_t crgetsgid(const cred_t *);
extern zoneid_t crgetzoneid(const cred_t *);
+extern zoneid_t crgetzonedid(const cred_t *);
extern projid_t crgetprojid(const cred_t *);
extern cred_t *crgetmapped(const cred_t *);
diff --git a/usr/src/uts/common/sys/dktp/dadk.h b/usr/src/uts/common/sys/dktp/dadk.h
index f5c990e7c0..2178ad1f0d 100644
--- a/usr/src/uts/common/sys/dktp/dadk.h
+++ b/usr/src/uts/common/sys/dktp/dadk.h
@@ -65,6 +65,8 @@ struct dadk {
kstat_t *dad_errstats; /* error stats */
kmutex_t dad_cmd_mutex;
int dad_cmd_count;
+ uint32_t dad_err_cnt; /* number of recent errors */
+ hrtime_t dad_last_log; /* time of last error log */
};
#define DAD_SECSIZ dad_phyg.g_secsiz
diff --git a/usr/src/uts/common/sys/dld.h b/usr/src/uts/common/sys/dld.h
index de7ac46db4..b73d22249a 100644
--- a/usr/src/uts/common/sys/dld.h
+++ b/usr/src/uts/common/sys/dld.h
@@ -192,6 +192,7 @@ typedef struct dld_ioc_rename {
datalink_id_t dir_linkid1;
datalink_id_t dir_linkid2;
char dir_link[MAXLINKNAMELEN];
+ boolean_t dir_zoneinit;
} dld_ioc_rename_t;
/*
@@ -204,6 +205,7 @@ typedef struct dld_ioc_rename {
typedef struct dld_ioc_zid {
zoneid_t diz_zid;
datalink_id_t diz_linkid;
+ boolean_t diz_transient;
} dld_ioc_zid_t;
/*
@@ -356,6 +358,7 @@ typedef struct dld_ioc_led {
#define DLD_CAPAB_POLL 0x00000002
#define DLD_CAPAB_PERIM 0x00000003
#define DLD_CAPAB_LSO 0x00000004
+#define DLD_CAPAB_IPCHECK 0x00000005
#define DLD_ENABLE 0x00000001
#define DLD_DISABLE 0x00000002
@@ -382,6 +385,7 @@ typedef struct dld_ioc_led {
*/
typedef int (*dld_capab_func_t)(void *, uint_t, void *, uint_t);
+#define DI_DIRECT_RAW 0x1
/*
* Direct Tx/Rx capability.
*/
@@ -406,8 +410,16 @@ typedef struct dld_capab_direct_s {
/* flow control "can I put on a ring" callback */
uintptr_t di_tx_fctl_df; /* canput-like callback */
void *di_tx_fctl_dh;
+
+ /* flags that control our behavior */
+ uint_t di_flags;
} dld_capab_direct_t;
+typedef struct dld_capab_ipcheck_s {
+ uintptr_t ipc_allowed_df;
+ void *ipc_allowed_dh;
+} dld_capab_ipcheck_t;
+
/*
* Polling/softring capability.
*/
diff --git a/usr/src/uts/common/sys/dld_impl.h b/usr/src/uts/common/sys/dld_impl.h
index 035eea893a..336fa9cb67 100644
--- a/usr/src/uts/common/sys/dld_impl.h
+++ b/usr/src/uts/common/sys/dld_impl.h
@@ -53,7 +53,8 @@ typedef enum {
typedef enum {
DLD_UNINITIALIZED,
DLD_PASSIVE,
- DLD_ACTIVE
+ DLD_ACTIVE,
+ DLD_EXCLUSIVE
} dld_passivestate_t;
/*
@@ -256,6 +257,8 @@ extern void dld_str_rx_unitdata(void *, mac_resource_handle_t,
extern void dld_str_notify_ind(dld_str_t *);
extern mac_tx_cookie_t str_mdata_fastpath_put(dld_str_t *, mblk_t *,
uintptr_t, uint16_t);
+extern mac_tx_cookie_t str_mdata_raw_fastpath_put(dld_str_t *, mblk_t *,
+ uintptr_t, uint16_t);
extern int dld_flow_ctl_callb(dld_str_t *, uint64_t,
int (*func)(), void *);
diff --git a/usr/src/uts/common/sys/dlpi.h b/usr/src/uts/common/sys/dlpi.h
index e9ac27cddd..e71a55ab84 100644
--- a/usr/src/uts/common/sys/dlpi.h
+++ b/usr/src/uts/common/sys/dlpi.h
@@ -109,6 +109,7 @@ typedef struct dl_ipnetinfo {
#define DL_PASSIVE_REQ 0x114 /* Allow access to aggregated link */
#define DL_INTR_MODE_REQ 0x115 /* Request Rx processing in INTR mode */
#define DL_NOTIFY_CONF 0x116 /* Notification from upstream */
+#define DL_EXCLUSIVE_REQ 0x117 /* Make bind active */
/*
* Primitives used for Connectionless Service
@@ -391,6 +392,7 @@ typedef struct dl_ipnetinfo {
#define DL_PROMISC_SAP 0x02 /* promiscuous mode at sap level */
#define DL_PROMISC_MULTI 0x03 /* promiscuous mode for multicast */
#define DL_PROMISC_RX_ONLY 0x04 /* above only enabled for rx */
+#define DL_PROMISC_FIXUPS 0x05 /* above will be fixed up */
/*
* DLPI notification codes for DL_NOTIFY_REQ primitives.
@@ -1085,6 +1087,13 @@ typedef struct {
} dl_intr_mode_req_t;
/*
+ * DL_EXCLUSIVE_REQ, M_PROTO type
+ */
+typedef struct {
+ t_uscalar_t dl_primitive;
+} dl_exclusive_req_t;
+
+/*
* CONNECTION-ORIENTED SERVICE PRIMITIVES
*/
@@ -1506,6 +1515,7 @@ union DL_primitives {
dl_control_ack_t control_ack;
dl_passive_req_t passive_req;
dl_intr_mode_req_t intr_mode_req;
+ dl_exclusive_req_t exclusive_req;
};
#define DL_INFO_REQ_SIZE sizeof (dl_info_req_t)
@@ -1574,6 +1584,7 @@ union DL_primitives {
#define DL_CONTROL_ACK_SIZE sizeof (dl_control_ack_t)
#define DL_PASSIVE_REQ_SIZE sizeof (dl_passive_req_t)
#define DL_INTR_MODE_REQ_SIZE sizeof (dl_intr_mode_req_t)
+#define DL_EXCLUSIVE_REQ_SIZE sizeof (dl_exclusive_req_t)
#ifdef _KERNEL
/*
diff --git a/usr/src/uts/common/sys/dls.h b/usr/src/uts/common/sys/dls.h
index cd3749dc21..0c5ffb0dd7 100644
--- a/usr/src/uts/common/sys/dls.h
+++ b/usr/src/uts/common/sys/dls.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_DLS_H
@@ -86,6 +87,7 @@ typedef struct dls_link_s dls_link_t;
#define DLS_PROMISC_MULTI 0x00000002
#define DLS_PROMISC_PHYS 0x00000004
#define DLS_PROMISC_RX_ONLY 0x00000008
+#define DLS_PROMISC_FIXUPS 0x00000010
extern int dls_open(dls_link_t *, dls_dl_handle_t, dld_str_t *);
extern void dls_close(dld_str_t *);
@@ -107,11 +109,13 @@ extern void str_notify(void *, mac_notify_type_t);
extern int dls_devnet_open(const char *,
dls_dl_handle_t *, dev_t *);
+extern int dls_devnet_open_in_zone(const char *,
+ dls_dl_handle_t *, dev_t *, zoneid_t);
extern void dls_devnet_close(dls_dl_handle_t);
extern boolean_t dls_devnet_rebuild();
extern int dls_devnet_rename(datalink_id_t, datalink_id_t,
- const char *);
+ const char *, boolean_t);
extern int dls_devnet_create(mac_handle_t, datalink_id_t,
zoneid_t);
extern int dls_devnet_destroy(mac_handle_t, datalink_id_t *,
@@ -129,7 +133,7 @@ extern uint16_t dls_devnet_vid(dls_dl_handle_t);
extern datalink_id_t dls_devnet_linkid(dls_dl_handle_t);
extern int dls_devnet_dev2linkid(dev_t, datalink_id_t *);
extern int dls_devnet_phydev(datalink_id_t, dev_t *);
-extern int dls_devnet_setzid(dls_dl_handle_t, zoneid_t);
+extern int dls_devnet_setzid(dls_dl_handle_t, zoneid_t, boolean_t);
extern zoneid_t dls_devnet_getzid(dls_dl_handle_t);
extern zoneid_t dls_devnet_getownerzid(dls_dl_handle_t);
extern boolean_t dls_devnet_islinkvisible(datalink_id_t, zoneid_t);
@@ -143,6 +147,8 @@ extern int dls_mgmt_update(const char *, uint32_t, boolean_t,
extern int dls_mgmt_get_linkinfo(datalink_id_t, char *,
datalink_class_t *, uint32_t *, uint32_t *);
extern int dls_mgmt_get_linkid(const char *, datalink_id_t *);
+extern int dls_mgmt_get_linkid_in_zone(const char *,
+ datalink_id_t *, zoneid_t);
extern datalink_id_t dls_mgmt_get_next(datalink_id_t, datalink_class_t,
datalink_media_t, uint32_t);
extern int dls_devnet_macname2linkid(const char *,
diff --git a/usr/src/uts/common/sys/dls_impl.h b/usr/src/uts/common/sys/dls_impl.h
index cd13a41413..329f8dd08e 100644
--- a/usr/src/uts/common/sys/dls_impl.h
+++ b/usr/src/uts/common/sys/dls_impl.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_DLS_IMPL_H
@@ -62,6 +63,7 @@ struct dls_link_s { /* Protected by */
uint_t dl_zone_ref;
link_tagmode_t dl_tagmode; /* atomic */
uint_t dl_nonip_cnt; /* SL */
+ uint_t dl_exclusive; /* SL */
};
typedef struct dls_head_s {
@@ -97,7 +99,8 @@ extern void dls_create_str_kstats(dld_str_t *);
extern int dls_stat_update(kstat_t *, dls_link_t *, int);
extern int dls_stat_create(const char *, int, const char *,
zoneid_t, int (*)(struct kstat *, int), void *,
- kstat_t **);
+ kstat_t **, zoneid_t);
+extern void dls_stat_delete(kstat_t *);
extern int dls_devnet_open_by_dev(dev_t, dls_link_t **,
dls_dl_handle_t *);
@@ -129,6 +132,7 @@ extern void dls_mgmt_init(void);
extern void dls_mgmt_fini(void);
extern int dls_mgmt_get_phydev(datalink_id_t, dev_t *);
+extern int dls_exclusive_set(dld_str_t *, boolean_t);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/sys/dls_mgmt.h b/usr/src/uts/common/sys/dls_mgmt.h
index b60e53b267..6fec277991 100644
--- a/usr/src/uts/common/sys/dls_mgmt.h
+++ b/usr/src/uts/common/sys/dls_mgmt.h
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015, Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _DLS_MGMT_H
@@ -114,10 +114,14 @@ typedef uint64_t datalink_media_t;
#define DLMGMT_CMD_BASE 128
/*
- * Indicate the link mapping is active or persistent
+ * Indicate if the link mapping is active, persistent, or transient. A
+ * transient link is an active link with a twist -- it is an active
+ * link which is destroyed along with the zone rather than reassigned
+ * to the GZ.
*/
#define DLMGMT_ACTIVE 0x01
#define DLMGMT_PERSIST 0x02
+#define DLMGMT_TRANSIENT 0x04
/* upcall argument */
typedef struct dlmgmt_door_arg {
@@ -168,6 +172,7 @@ typedef struct dlmgmt_door_getname {
typedef struct dlmgmt_door_getlinkid {
int ld_cmd;
char ld_link[MAXLINKNAMELEN];
+ zoneid_t ld_zoneid;
} dlmgmt_door_getlinkid_t;
typedef struct dlmgmt_door_getnext_s {
diff --git a/usr/src/uts/common/sys/dumpadm.h b/usr/src/uts/common/sys/dumpadm.h
index 616828bb2b..8ca10ff3c5 100644
--- a/usr/src/uts/common/sys/dumpadm.h
+++ b/usr/src/uts/common/sys/dumpadm.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _SYS_DUMPADM_H
@@ -44,11 +45,13 @@ extern "C" {
#define DIOCSETUUID (DDIOC | 0x17)
#define DIOCGETUUID (DDIOC | 0x18)
#define DIOCRMDEV (DDIOC | 0x19)
+#define DIOCSCRYPTKEY (DDIOC | 0x1a)
/*
* Kernel-controlled dump state flags for dump_conflags
*/
#define DUMP_EXCL 0x00000001 /* dedicated dump device (not swap) */
+#define DUMP_ENCRYPT 0x00000002 /* encrypt dump */
#define DUMP_STATE 0x0000ffff /* the set of all kernel flags */
/*
diff --git a/usr/src/uts/common/sys/dumphdr.h b/usr/src/uts/common/sys/dumphdr.h
index 2019f60a5d..aa2fbde7a5 100644
--- a/usr/src/uts/common/sys/dumphdr.h
+++ b/usr/src/uts/common/sys/dumphdr.h
@@ -21,6 +21,7 @@
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
#ifndef _SYS_DUMPHDR_H
@@ -60,6 +61,22 @@ extern "C" {
sizeof (summary_dump_t) + 1024), \
DUMP_OFFSET)) /* summary save area */
+#define DUMP_CRYPT_KEYLEN 32 /* byte len for crypto key */
+#define DUMP_CRYPT_NONCELEN 8 /* byte len for nonce */
+#define DUMP_CRYPT_HMACLEN 64 /* byte len for HMAC */
+#define DUMP_CRYPT_BLOCKSHIFT 6 /* 64-byte blocks */
+
+#define DUMP_CRYPT_ALGO_NONE 0 /* dump not encrypted */
+#define DUMP_CRYPT_ALGO_CHACHA20 1 /* ChaCha20 */
+
+#if DUMP_OFFSET & ((1 << DUMP_CRYPT_BLOCKSHIFT) - 1)
+#error DUMP_OFFSET not DUMP_CRYPT_BLOCKSHIFT aligned
+#endif
+
+#if DUMP_LOGSIZE & ((1 << DUMP_CRYPT_BLOCKSHIFT) - 1)
+#error DUMP_LOGSIZE not DUMP_CRYPT_BLOCKSHIFT aligned
+#endif
+
typedef struct dumphdr {
uint32_t dump_magic; /* magic number */
uint32_t dump_version; /* version number */
@@ -86,12 +103,22 @@ typedef struct dumphdr {
} dumphdr_t;
/*
+ * If DF_ENCRYPTED is set, this header will be found after the dumphdr.
+ */
+typedef struct dump_crypt {
+ uint8_t dump_crypt_algo; /* encryption algorithm */
+ uint8_t dump_crypt_hmac[DUMP_CRYPT_HMACLEN]; /* HMAC for crypto key */
+ uint8_t dump_crypt_nonce[DUMP_CRYPT_NONCELEN]; /* encryption none */
+} dump_crypt_t;
+
+/*
* Values for dump_flags
*/
#define DF_VALID 0x00000001 /* Dump is valid (savecore clears) */
#define DF_COMPLETE 0x00000002 /* All pages present as configured */
#define DF_LIVE 0x00000004 /* Dump was taken on a live system */
#define DF_COMPRESSED 0x00000008 /* Dump is compressed */
+#define DF_ENCRYPTED 0x00000010 /* Dump is encrypted */
#define DF_KERNEL 0x00010000 /* Contains kernel pages only */
#define DF_ALL 0x00020000 /* Contains all pages */
#define DF_CURPROC 0x00040000 /* Contains kernel + cur proc pages */
@@ -175,6 +202,8 @@ extern u_offset_t dumpvp_size;
extern struct dumphdr *dumphdr;
extern int dump_conflags;
extern char *dumppath;
+extern uint8_t dump_crypt_key[DUMP_CRYPT_KEYLEN];
+extern uint8_t dump_crypt_nonce[DUMP_CRYPT_NONCELEN];
extern int dump_timeout;
extern int dump_timeleft;
diff --git a/usr/src/uts/common/sys/elf.h b/usr/src/uts/common/sys/elf.h
index 1f290c282c..556a49c60f 100644
--- a/usr/src/uts/common/sys/elf.h
+++ b/usr/src/uts/common/sys/elf.h
@@ -501,6 +501,11 @@ typedef struct {
#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */
#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
+/*
+ * Linux specific program headers not even used by Linux (!!)
+ */
+#define PT_PAX_FLAGS 0x65041580 /* PaX flags (see below) */
+
#define PT_LOSUNW 0x6ffffffa
#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment (unused) */
#define PT_SUNWSTACK 0x6ffffffb /* describes the stack segment */
@@ -516,6 +521,45 @@ typedef struct {
#define PF_W 0x2
#define PF_X 0x1
+/*
+ * PaX is a regrettable series of never-integrated Linux patches for a
+ * facility to provide additional protections on memory pages for purposes of
+ * increasing security, and for allowing binaries to demand (or refuse) those
+ * protections via the PT_PAX_FLAGS program header. (Portents of its
+ * rudderless existence, "PaX" is a term of indefinite origin written by an
+ * unknown group of people.) This facility is unfortunate in any number of
+ * ways, and was largely obviated by the broad adoption of non-executable
+ * stacks at any rate -- but it lives on in binaries that continue to mark
+ * themselves to explicitly refuse the (never-integrated, now-obviated)
+ * facility. One might cringe that PaX overloads the meaning of the p_flags
+ * to specify protections, but that is the least of its transgressions:
+ * instead of using one p_type constant to explicitly enable a series of
+ * protections and another to explicitly disable others, it insists on
+ * conflating both actions into PT_PAX_FLAGS. The resulting doubling of
+ * constant definitions (two constant definitions for every protection instead
+ * of merely one) assures that the values can't even fit in the eight
+ * PF_MASKOS bits putatively defined to provide a modicum of cleanliness for
+ * such filthy functionality. And were all of this not enough, there is one
+ * final nomenclature insult to be added to this semantic injury: the
+ * constants for the p_flags don't even embed "_PAX_" in their name -- despite
+ * the fact that this is their only purpose! We resist the temptation to
+ * right this final wrong here; we grit our teeth and provide exactly the
+ * Linux definitions -- or rather, what would have been the Linux definitions
+ * had this belching jalopy ever been permitted to crash itself into mainline.
+ */
+#define PF_PAGEEXEC 0x00000010 /* PaX: enable PAGEEXEC */
+#define PF_NOPAGEEXEC 0x00000020 /* PaX: disable PAGEEXEC */
+#define PF_SEGMEXEC 0x00000040 /* PaX: enable SEGMEXEC */
+#define PF_NOSEGMEXEC 0x00000080 /* PaX: disable SEGMEXEC */
+#define PF_MPROTECT 0x00000100 /* PaX: enable MPROTECT */
+#define PF_NOMPROTECT 0x00000200 /* PaX: disable MPROTECT */
+#define PF_RANDEXEC 0x00000400 /* PaX: enable RANDEXEC */
+#define PF_NORANDEXEC 0x00000800 /* PaX: disable RANDEXEC */
+#define PF_EMUTRAMP 0x00001000 /* PaX: enable EMUTRAMP */
+#define PF_NOEMUTRAMP 0x00002000 /* PaX: disable EMUTRAMP */
+#define PF_RANDMMAP 0x00004000 /* PaX: enable RANDMMAP */
+#define PF_NORANDMMAP 0x00008000 /* PaX: disable RANDMMAP */
+
#define PF_MASKOS 0x0ff00000 /* OS specific values */
#define PF_MASKPROC 0xf0000000 /* processor specific values */
diff --git a/usr/src/uts/common/sys/eventfd.h b/usr/src/uts/common/sys/eventfd.h
index 1b0d961b0b..b64a101348 100644
--- a/usr/src/uts/common/sys/eventfd.h
+++ b/usr/src/uts/common/sys/eventfd.h
@@ -10,7 +10,7 @@
*/
/*
- * Copyright (c) 2015 Joyent, Inc. All rights reserved.
+ * Copyright (c) 2017, Joyent, Inc.
*/
/*
@@ -47,6 +47,13 @@ typedef uint64_t eventfd_t;
#define EVENTFDIOC (('e' << 24) | ('f' << 16) | ('d' << 8))
#define EVENTFDIOC_SEMAPHORE (EVENTFDIOC | 1) /* toggle sem state */
+/*
+ * Kernel-internal method to write to eventfd while bypassing overflow limits,
+ * therefore avoiding potential to block as well. This is used to fulfill AIO
+ * behavior in LX related to eventfd notification.
+ */
+#define EVENTFDIOC_POST (EVENTFDIOC | 2)
+
#ifndef _KERNEL
extern int eventfd(unsigned int, int);
@@ -58,6 +65,7 @@ extern int eventfd_write(int, eventfd_t);
#define EVENTFDMNRN_EVENTFD 0
#define EVENTFDMNRN_CLONE 1
#define EVENTFD_VALMAX (ULLONG_MAX - 1ULL)
+#define EVENTFD_VALOVERFLOW ULLONG_MAX
#endif /* _KERNEL */
diff --git a/usr/src/uts/common/sys/exec.h b/usr/src/uts/common/sys/exec.h
index 23eb5b6bf7..0d5b4c4611 100644
--- a/usr/src/uts/common/sys/exec.h
+++ b/usr/src/uts/common/sys/exec.h
@@ -81,7 +81,8 @@ typedef struct uarg {
ssize_t na;
ssize_t ne;
ssize_t nc;
- ssize_t arglen;
+ size_t argstrlen;
+ size_t envstrlen;
char *fname;
char *pathname;
size_t auxsize;
@@ -107,10 +108,13 @@ typedef struct uarg {
vnode_t *ex_vp;
char *emulator;
char *brandname;
+ const char *brand_nroot;
char *auxp_auxflags; /* addr of auxflags auxv on the user stack */
char *auxp_brand; /* address of first brand auxv on user stack */
cred_t *pfcred;
boolean_t scrubenv;
+ uintptr_t maxstack;
+ boolean_t stk_prot_override;
uintptr_t commpage;
} uarg_t;
@@ -181,7 +185,7 @@ struct execsw {
int (*exec_func)(struct vnode *vp, struct execa *uap,
struct uarg *args, struct intpdata *idata, int level,
size_t *execsz, int setid, caddr_t exec_file,
- struct cred *cred, int brand_action);
+ struct cred *cred, int *brand_action);
int (*exec_core)(struct vnode *vp, struct proc *p,
struct cred *cred, rlim64_t rlimit, int sig,
core_content_t content);
@@ -209,7 +213,7 @@ extern int exec_common(const char *fname, const char **argp,
const char **envp, int brand_action);
extern int gexec(vnode_t **vp, struct execa *uap, struct uarg *args,
struct intpdata *idata, int level, size_t *execsz, caddr_t exec_file,
- struct cred *cred, int brand_action);
+ struct cred *cred, int *brand_action);
extern struct execsw *allocate_execsw(char *name, char *magic,
size_t magic_size);
extern struct execsw *findexecsw(char *magic);
@@ -234,18 +238,20 @@ extern void exec_set_sp(size_t);
* when compiling the 32-bit compatability elf code in the elfexec module.
*/
extern int elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
- size_t *, int, caddr_t, cred_t *, int);
+ size_t *, int, caddr_t, cred_t *, int *);
extern int mapexec_brand(vnode_t *, uarg_t *, Ehdr *, Addr *,
- intptr_t *, caddr_t, int *, caddr_t *, caddr_t *, size_t *, uintptr_t *);
+ intptr_t *, caddr_t, char **, caddr_t *, caddr_t *, size_t *,
+ uintptr_t *, uintptr_t *);
extern int elfreadhdr(vnode_t *, cred_t *, Ehdr *, uint_t *, caddr_t *,
size_t *);
#endif /* !_ELF32_COMPAT */
#if defined(_LP64)
extern int elf32exec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
- size_t *, int, caddr_t, cred_t *, int);
+ size_t *, int, caddr_t, cred_t *, int *);
extern int mapexec32_brand(vnode_t *, uarg_t *, Elf32_Ehdr *, Elf32_Addr *,
- intptr_t *, caddr_t, int *, caddr_t *, caddr_t *, size_t *, uintptr_t *);
+ intptr_t *, caddr_t, char **, caddr_t *, caddr_t *, size_t *,
+ uintptr_t *, uintptr_t *);
extern int elf32readhdr(vnode_t *, cred_t *, Elf32_Ehdr *, uint_t *, caddr_t *,
size_t *);
#endif /* _LP64 */
diff --git a/usr/src/uts/common/sys/file.h b/usr/src/uts/common/sys/file.h
index d300b940e2..66620ab7b9 100644
--- a/usr/src/uts/common/sys/file.h
+++ b/usr/src/uts/common/sys/file.h
@@ -28,6 +28,7 @@
/* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. */
/* Copyright 2020 Joyent, Inc. */
+/* Copyright 2021 OmniOS Community Edition (OmniOSce) Association. */
#ifndef _SYS_FILE_H
#define _SYS_FILE_H
@@ -120,6 +121,15 @@ typedef struct fpollinfo {
#define FCLOEXEC 0x800000 /* O_CLOEXEC = 0x800000 */
#define FDIRECTORY 0x1000000 /* O_DIRECTORY = 0x1000000 */
#define FDIRECT 0x2000000 /* O_DIRECT = 0x2000000 */
+/*
+ * Private interface for lx O_PATH|O_NOFOLLOW emulation for symlinks.
+ */
+#define __FLXPATH 0x80000000
+/*
+ * Private interface for lx fstatat(AT_NO_AUTOMOUNT) emulation.
+ * Since usage is disjoint, the __FLXPATH bit is re-used.
+ */
+#define __FLXNOAUTO 0x80000000
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
@@ -224,6 +234,7 @@ extern void fcnt_add(struct uf_info *, int);
extern void close_exec(struct uf_info *);
extern void clear_stale_fd(void);
extern void clear_active_fd(int);
+extern void set_active_fd(int);
extern void free_afd(afd_t *afd);
extern int fgetstartvp(int, char *, struct vnode **);
extern int fsetattrat(int, char *, int, struct vattr *);
diff --git a/usr/src/uts/common/sys/frameio.h b/usr/src/uts/common/sys/frameio.h
new file mode 100644
index 0000000000..54e6dbeedf
--- /dev/null
+++ b/usr/src/uts/common/sys/frameio.h
@@ -0,0 +1,107 @@
+/*
+ * 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 _SYS_FRAMEIO_H
+#define _SYS_FRAMEIO_H
+
+/*
+ * Frame I/O definitions
+ */
+
+#include <sys/types.h>
+
+#ifdef _KERNEL
+/* Kernel only headers */
+#include <sys/stream.h>
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * An individual frame vector component. Collections of these are used to make
+ * ioctls.
+ */
+typedef struct framevec {
+ void *fv_buf; /* Buffer with data */
+ size_t fv_buflen; /* Size of the buffer */
+ size_t fv_actlen; /* Amount of buffer consumed, ignore on error */
+} framevec_t;
+
+/*
+ * The base unit used with frameio.
+ */
+typedef struct frameio {
+ uint_t fio_version; /* Should always be FRAMEIO_CURRENT_VERSION */
+ uint_t fio_nvpf; /* How many vectors make up one frame */
+ uint_t fio_nvecs; /* The total number of vectors */
+ framevec_t fio_vecs[]; /* C99 VLA */
+} frameio_t;
+
+
+#define FRAMEIO_VERSION_ONE 1
+#define FRAMEIO_CURRENT_VERSION FRAMEIO_VERSION_ONE
+
+#define FRAMEIO_NVECS_MAX 32
+
+/*
+ * Definitions for kernel modules to include as helpers. These are consolidation
+ * private.
+ */
+#ifdef _KERNEL
+
+/*
+ * 32-bit versions for 64-bit kernels
+ */
+typedef struct framevec32 {
+ caddr32_t fv_buf;
+ size32_t fv_buflen;
+ size32_t fv_actlen;
+} framevec32_t;
+
+typedef struct frameio32 {
+ uint_t fio_version;
+ uint_t fio_vecspframe;
+ uint_t fio_nvecs;
+ framevec32_t fio_vecs[];
+} frameio32_t;
+
+/*
+ * Describe the different ways that vectors should map to frames.
+ */
+typedef enum frameio_write_mblk_map {
+ MAP_BLK_FRAME
+} frameio_write_mblk_map_t;
+
+int frameio_init(void);
+void frameio_fini(void);
+frameio_t *frameio_alloc(int);
+void frameio_free(frameio_t *);
+int frameio_hdr_copyin(frameio_t *, int, const void *, uint_t);
+int frameio_mblk_chain_read(frameio_t *, mblk_t **, int *, int);
+int frameio_mblk_chain_write(frameio_t *, frameio_write_mblk_map_t, mblk_t *,
+ int *, int);
+int frameio_hdr_copyout(frameio_t *, int, void *, uint_t);
+size_t frameio_frame_length(frameio_t *, framevec_t *);
+void frameio_mark_consumed(frameio_t *, int);
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_FRAMEIO_H */
diff --git a/usr/src/uts/common/sys/fs/fifonode.h b/usr/src/uts/common/sys/fs/fifonode.h
index d8b158ce3c..1ea8563e1c 100644
--- a/usr/src/uts/common/sys/fs/fifonode.h
+++ b/usr/src/uts/common/sys/fs/fifonode.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -83,6 +84,7 @@ struct fifonode {
struct msgb *fn_tail; /* last message to read */
fifolock_t *fn_lock; /* pointer to per fifo lock */
uint_t fn_count; /* Number of bytes on fn_mp */
+ uint_t fn_hiwat; /* pipe (fifofast) high water */
kcondvar_t fn_wait_cv; /* fifo conditional variable */
ushort_t fn_wcnt; /* number of writers */
ushort_t fn_rcnt; /* number of readers */
@@ -135,6 +137,8 @@ typedef struct fifodata {
#define FIFOPOLLRBAND 0x20000
#define FIFOSTAYFAST 0x40000 /* don't turn into stream mode */
#define FIFOWAITMODE 0x80000 /* waiting for the possibility to change mode */
+/* Data on loan, block reads. Use in conjunction with FIFOSTAYFAST. */
+#define FIFORDBLOCK 0x100000
#define FIFOHIWAT (16 * 1024)
#define FIFOLOWAT (0)
@@ -147,16 +151,6 @@ typedef struct fifodata {
#if defined(_KERNEL)
-/*
- * Fifohiwat defined as a variable is to allow tuning of the high
- * water mark if needed. It is not meant to be released.
- */
-#if FIFODEBUG
-extern int Fifohiwat;
-#else /* FIFODEBUG */
-#define Fifohiwat FIFOHIWAT
-#endif /* FIFODEBUG */
-
extern struct vnodeops *fifo_vnodeops;
extern const struct fs_operation_def fifo_vnodeops_template[];
extern struct kmem_cache *fnode_cache;
@@ -181,6 +175,8 @@ extern void fifo_fastoff(fifonode_t *);
extern struct streamtab *fifo_getinfo();
extern void fifo_wakereader(fifonode_t *, fifolock_t *);
extern void fifo_wakewriter(fifonode_t *, fifolock_t *);
+extern boolean_t fifo_stayfast_enter(fifonode_t *);
+extern void fifo_stayfast_exit(fifonode_t *);
#endif /* _KERNEL */
diff --git a/usr/src/uts/common/sys/fs/hyprlofs.h b/usr/src/uts/common/sys/fs/hyprlofs.h
new file mode 100644
index 0000000000..b8c4149df2
--- /dev/null
+++ b/usr/src/uts/common/sys/fs/hyprlofs.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 2012, Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_FS_HYPRLOFS_H
+#define _SYS_FS_HYPRLOFS_H
+
+#include <sys/param.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * hyprlofs ioctl numbers.
+ */
+#define HYPRLOFS_IOC ('H' << 8)
+
+#define HYPRLOFS_ADD_ENTRIES (HYPRLOFS_IOC | 1)
+#define HYPRLOFS_RM_ENTRIES (HYPRLOFS_IOC | 2)
+#define HYPRLOFS_RM_ALL (HYPRLOFS_IOC | 3)
+#define HYPRLOFS_GET_ENTRIES (HYPRLOFS_IOC | 4)
+
+typedef struct {
+ char *hle_path;
+ uint_t hle_plen;
+ char *hle_name;
+ uint_t hle_nlen;
+} hyprlofs_entry_t;
+
+typedef struct {
+ hyprlofs_entry_t *hle_entries;
+ uint_t hle_len;
+} hyprlofs_entries_t;
+
+typedef struct {
+ char hce_path[MAXPATHLEN];
+ char hce_name[MAXPATHLEN];
+} hyprlofs_curr_entry_t;
+
+typedef struct {
+ hyprlofs_curr_entry_t *hce_entries;
+ uint_t hce_cnt;
+} hyprlofs_curr_entries_t;
+
+#ifdef _KERNEL
+typedef struct {
+ caddr32_t hle_path;
+ uint_t hle_plen;
+ caddr32_t hle_name;
+ uint_t hle_nlen;
+} hyprlofs_entry32_t;
+
+typedef struct {
+ caddr32_t hle_entries;
+ uint_t hle_len;
+} hyprlofs_entries32_t;
+
+typedef struct {
+ caddr32_t hce_entries;
+ uint_t hce_cnt;
+} hyprlofs_curr_entries32_t;
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_FS_HYPRLOFS_H */
diff --git a/usr/src/uts/common/sys/fs/hyprlofs_info.h b/usr/src/uts/common/sys/fs/hyprlofs_info.h
new file mode 100644
index 0000000000..38389f77d9
--- /dev/null
+++ b/usr/src/uts/common/sys/fs/hyprlofs_info.h
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYS_FS_HYPRLOFS_INFO_H
+#define _SYS_FS_HYPRLOFS_INFO_H
+
+#include <sys/t_lock.h>
+#include <vm/seg.h>
+#include <vm/seg_vn.h>
+#include <sys/vfs_opreg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * hlnode is the file system dependent node for hyprlofs.
+ * It is modeled on the tmpfs tmpnode.
+ *
+ * hln_rwlock protects access of the directory list at hln_dir
+ * as well as syncronizing read/writes to directory hlnodes.
+ * hln_tlock protects updates to hln_mode and hln_nlink.
+ * hln_tlock doesn't require any hlnode locks.
+ */
+typedef struct hlnode {
+ struct hlnode *hln_back; /* linked list of hlnodes */
+ struct hlnode *hln_forw; /* linked list of hlnodes */
+ union {
+ struct {
+ struct hldirent *un_dirlist; /* dirent list */
+ uint_t un_dirents; /* number of dirents */
+ } un_dirstruct;
+ vnode_t *un_realvp; /* real vnode */
+ } un_hlnode;
+ vnode_t *hln_vnode; /* vnode for this hlnode */
+ int hln_gen; /* pseudo gen num for hlfid */
+ int hln_looped; /* flag indicating loopback */
+ vattr_t hln_attr; /* attributes */
+ krwlock_t hln_rwlock; /* rw - serialize mods and */
+ /* directory updates */
+ kmutex_t hln_tlock; /* time, flag, and nlink lock */
+} hlnode_t;
+
+/*
+ * hyprlofs per-mount data structure.
+ * All fields are protected by hlm_contents.
+ */
+typedef struct {
+ vfs_t *hlm_vfsp; /* filesystem's vfs struct */
+ hlnode_t *hlm_rootnode; /* root hlnode */
+ char *hlm_mntpath; /* name of hyprlofs mount point */
+ dev_t hlm_dev; /* unique dev # of mounted `device' */
+ uint_t hlm_gen; /* pseudo generation number for files */
+ kmutex_t hlm_contents; /* lock for hlfsmount structure */
+} hlfsmount_t;
+
+/*
+ * hyprlofs directories are made up of a linked list of hldirent structures
+ * hanging off directory hlnodes. File names are not fixed length,
+ * but are null terminated.
+ */
+typedef struct hldirent {
+ hlnode_t *hld_hlnode; /* hlnode for this file */
+ struct hldirent *hld_next; /* next directory entry */
+ struct hldirent *hld_prev; /* prev directory entry */
+ uint_t hld_offset; /* "offset" of dir entry */
+ uint_t hld_hash; /* a hash of td_name */
+ struct hldirent *hld_link; /* linked via the hash table */
+ hlnode_t *hld_parent; /* parent, dir we are in */
+ char *hld_name; /* must be null terminated */
+ /* max length is MAXNAMELEN */
+} hldirent_t;
+
+/*
+ * hlfid overlays the fid structure (for VFS_VGET)
+ */
+typedef struct {
+ uint16_t hlfid_len;
+ ino32_t hlfid_ino;
+ int32_t hlfid_gen;
+} hlfid_t;
+
+/*
+ * File system independent to hyprlofs conversion macros
+ */
+#define VFSTOHLM(vfsp) ((hlfsmount_t *)(vfsp)->vfs_data)
+#define VTOHLM(vp) ((hlfsmount_t *)(vp)->v_vfsp->vfs_data)
+#define VTOHLN(vp) ((hlnode_t *)(vp)->v_data)
+#define HLNTOV(tp) ((tp)->hln_vnode)
+#define REALVP(vp) ((vnode_t *)VTOHLN(vp)->hln_realvp)
+#define hlnode_hold(tp) VN_HOLD(HLNTOV(tp))
+#define hlnode_rele(tp) VN_RELE(HLNTOV(tp))
+
+#define hln_dir un_hlnode.un_dirstruct.un_dirlist
+#define hln_dirents un_hlnode.un_dirstruct.un_dirents
+#define hln_realvp un_hlnode.un_realvp
+
+/*
+ * Attributes
+ */
+#define hln_mask hln_attr.va_mask
+#define hln_type hln_attr.va_type
+#define hln_mode hln_attr.va_mode
+#define hln_uid hln_attr.va_uid
+#define hln_gid hln_attr.va_gid
+#define hln_fsid hln_attr.va_fsid
+#define hln_nodeid hln_attr.va_nodeid
+#define hln_nlink hln_attr.va_nlink
+#define hln_size hln_attr.va_size
+#define hln_atime hln_attr.va_atime
+#define hln_mtime hln_attr.va_mtime
+#define hln_ctime hln_attr.va_ctime
+#define hln_rdev hln_attr.va_rdev
+#define hln_blksize hln_attr.va_blksize
+#define hln_nblocks hln_attr.va_nblocks
+#define hln_seq hln_attr.va_seq
+
+/*
+ * enums
+ */
+enum de_op { DE_CREATE, DE_MKDIR }; /* direnter ops */
+enum dr_op { DR_REMOVE, DR_RMDIR }; /* dirremove ops */
+
+/*
+ * hyprlofs_minfree is the amount (in pages) of anonymous memory that hyprlofs
+ * leaves free for the rest of the system. The default value for
+ * hyprlofs_minfree is btopr(HYPRLOFSMINFREE) but it can be patched to a
+ * different number of pages. Since hyprlofs doesn't actually use much
+ * memory, its unlikely this ever needs to be patched.
+ */
+#define HYPRLOFSMINFREE 8 * 1024 * 1024 /* 8 Megabytes */
+
+extern size_t hyprlofs_minfree; /* Anonymous memory in pages */
+
+extern void hyprlofs_node_init(hlfsmount_t *, hlnode_t *, vattr_t *,
+ cred_t *);
+extern int hyprlofs_dirlookup(hlnode_t *, char *, hlnode_t **, cred_t *);
+extern int hyprlofs_dirdelete(hlnode_t *, hlnode_t *, char *, enum dr_op,
+ cred_t *);
+extern void hyprlofs_dirinit(hlnode_t *, hlnode_t *);
+extern void hyprlofs_dirtrunc(hlnode_t *);
+extern int hyprlofs_taccess(void *, int, cred_t *);
+extern int hyprlofs_direnter(hlfsmount_t *, hlnode_t *, char *, enum de_op,
+ vnode_t *, vattr_t *, hlnode_t **, cred_t *);
+
+extern struct vnodeops *hyprlofs_vnodeops;
+extern const struct fs_operation_def hyprlofs_vnodeops_template[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_FS_HYPRLOFS_INFO_H */
diff --git a/usr/src/uts/common/sys/fs/sdev_impl.h b/usr/src/uts/common/sys/fs/sdev_impl.h
index dc6601bb43..676193fcfa 100644
--- a/usr/src/uts/common/sys/fs/sdev_impl.h
+++ b/usr/src/uts/common/sys/fs/sdev_impl.h
@@ -37,6 +37,7 @@ extern "C" {
#include <sys/vfs_opreg.h>
#include <sys/list.h>
#include <sys/nvpair.h>
+#include <sys/fs/sdev_plugin.h>
#include <sys/sunddi.h>
#include <sys/fs/sdev_plugin.h>
diff --git a/usr/src/uts/common/sys/fs/tmp.h b/usr/src/uts/common/sys/fs/tmp.h
index fb07de6588..f4cee09244 100644
--- a/usr/src/uts/common/sys/fs/tmp.h
+++ b/usr/src/uts/common/sys/fs/tmp.h
@@ -23,7 +23,7 @@
* All rights reserved. Use is subject to license terms.
*/
/*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _SYS_FS_TMP_H
@@ -43,8 +43,10 @@ struct tmount {
struct vfs *tm_vfsp; /* filesystem's vfs struct */
struct tmpnode *tm_rootnode; /* root tmpnode */
char *tm_mntpath; /* name of tmpfs mount point */
- ulong_t tm_anonmax; /* file system max anon reservation */
- pgcnt_t tm_anonmem; /* pages of reserved anon memory */
+ size_t tm_anonmax; /* file system max anon reservation */
+ size_t tm_anonmem; /* bytes of reserved anon memory */
+ /* and allocated kmem for the fs */
+ size_t tm_allocmem; /* bytes alloced from tmp_kmem_ funcs */
dev_t tm_dev; /* unique dev # of mounted `device' */
uint_t tm_gen; /* pseudo generation number for files */
kmutex_t tm_contents; /* lock for tmount structure */
@@ -58,6 +60,7 @@ struct tmount {
#define VTOTM(vp) ((struct tmount *)(vp)->v_vfsp->vfs_data)
#define VTOTN(vp) ((struct tmpnode *)(vp)->v_data)
#define TNTOV(tp) ((tp)->tn_vnode)
+#define TNTOTM(tp) (VTOTM(TNTOV(tp)))
#define tmpnode_hold(tp) VN_HOLD(TNTOV(tp))
#define tmpnode_rele(tp) VN_RELE(TNTOV(tp))
@@ -69,41 +72,39 @@ enum dr_op { DR_REMOVE, DR_RMDIR, DR_RENAME }; /* dirremove ops */
/*
* tmpfs_minfree is the amount (in pages) of anonymous memory that tmpfs
- * leaves free for the rest of the system. E.g. in a system with 32MB of
- * configured swap space, if 16MB were reserved (leaving 16MB free),
- * tmpfs could allocate up to 16MB - tmpfs_minfree. The default value
- * for tmpfs_minfree is btopr(TMPMINFREE) but it can cautiously patched
- * to a different number of pages.
- * NB: If tmpfs allocates too much swap space, other processes will be
- * unable to execute.
+ * leaves free for the rest of the system. In antiquity, this number could be
+ * relevant on a system-wide basis, as physical DRAM was routinely exhausted;
+ * however, in more modern times, the relative growth of DRAM with respect to
+ * application footprint means that this number is only likely to become
+ * factor in a virtualized OS environment (e.g., a zone) -- and even then only
+ * when DRAM and swap have both been capped low to allow for maximum tenancy.
+ * TMPMINFREE -- the value from which tmpfs_minfree is derived -- should
+ * therefore be configured to a value that is roughly the smallest practical
+ * value for memory + swap minus the largest reasonable size for tmpfs in such
+ * a configuration. As of this writing, the smallest practical memory + swap
+ * configuration is 128MB, and it seems reasonable to allow tmpfs to consume
+ * no more than seven-eighths of this, yielding a TMPMINFREE of 16MB. Care
+ * should be exercised in changing this: tuning this value too high will
+ * result in spurious ENOSPC errors in tmpfs in small zones (a problem that
+ * can induce cascading failure surprisingly often); tuning this value too low
+ * will result in tmpfs consumption alone to alone induce application-level
+ * memory allocation failure.
*/
-#define TMPMINFREE 2 * 1024 * 1024 /* 2 Megabytes */
+#define TMPMINFREE 16 * 1024 * 1024 /* 16 Megabytes */
extern size_t tmpfs_minfree; /* Anonymous memory in pages */
-/*
- * tmpfs can allocate only a certain percentage of kernel memory,
- * which is used for tmpnodes, directories, file names, etc.
- * This is statically set as TMPMAXFRACKMEM of physical memory.
- * The actual number of allocatable bytes can be patched in tmpfs_maxkmem.
- */
-#define TMPMAXFRACKMEM 25 /* 1/25 of physical memory */
-
-extern size_t tmp_kmemspace;
-extern size_t tmpfs_maxkmem; /* Allocatable kernel memory in bytes */
-
extern void tmpnode_init(struct tmount *, struct tmpnode *,
struct vattr *, struct cred *);
+extern void tmpnode_cleanup(struct tmpnode *tp);
extern int tmpnode_trunc(struct tmount *, struct tmpnode *, ulong_t);
extern void tmpnode_growmap(struct tmpnode *, ulong_t);
extern int tdirlookup(struct tmpnode *, char *, struct tmpnode **,
struct cred *);
extern int tdirdelete(struct tmpnode *, struct tmpnode *, char *,
enum dr_op, struct cred *);
-extern void tdirinit(struct tmpnode *, struct tmpnode *);
+extern int tdirinit(struct tmpnode *, struct tmpnode *);
extern void tdirtrunc(struct tmpnode *);
-extern void *tmp_memalloc(size_t, int);
-extern void tmp_memfree(void *, size_t);
extern int tmp_resv(struct tmount *, struct tmpnode *, size_t, int);
extern int tmp_taccess(void *, int, struct cred *);
extern int tmp_sticky_remove_access(struct tmpnode *, struct tmpnode *,
@@ -114,6 +115,9 @@ extern int tdirenter(struct tmount *, struct tmpnode *, char *,
enum de_op, struct tmpnode *, struct tmpnode *, struct vattr *,
struct tmpnode **, struct cred *, caller_context_t *);
+extern void *tmp_kmem_zalloc(struct tmount *, size_t, int);
+extern void tmp_kmem_free(struct tmount *, void *, size_t);
+
#define TMP_MUSTHAVE 0x01
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/fss.h b/usr/src/uts/common/sys/fss.h
index 6168e9d9ed..87d798d6c1 100644
--- a/usr/src/uts/common/sys/fss.h
+++ b/usr/src/uts/common/sys/fss.h
@@ -160,6 +160,7 @@ typedef struct fsszone {
/*
* fss_flags
*/
+/* Formerly: FSSKPRI 0x01 - the thread is in kernel mode */
#define FSSBACKQ 0x02 /* thread should be placed at the back of */
/* the dispatch queue if preempted */
#define FSSRESTORE 0x04 /* thread was not preempted, due to schedctl */
diff --git a/usr/src/uts/common/sys/fx.h b/usr/src/uts/common/sys/fx.h
index 2d4e1aa7fb..4a48af52a1 100644
--- a/usr/src/uts/common/sys/fx.h
+++ b/usr/src/uts/common/sys/fx.h
@@ -21,13 +21,12 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_FX_H
#define _SYS_FX_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <sys/thread.h>
#include <sys/ddi.h>
@@ -145,7 +144,14 @@ typedef struct fxkparms {
uint_t fx_cflags;
} fxkparms_t;
+/*
+ * control flags (kparms->fx_cflags).
+ */
+#define FX_DOUPRILIM 0x01 /* change user priority limit */
+#define FX_DOUPRI 0x02 /* change user priority */
+#define FX_DOTQ 0x04 /* change FX time quantum */
+#define FXMAXUPRI 60 /* maximum user priority setting */
/*
* Interface for partner private code. This is not a public interface.
diff --git a/usr/src/uts/common/sys/gsqueue.h b/usr/src/uts/common/sys/gsqueue.h
new file mode 100644
index 0000000000..91ab46fc44
--- /dev/null
+++ b/usr/src/uts/common/sys/gsqueue.h
@@ -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 2017 Joyent, Inc.
+ */
+
+#ifndef _SYS_GSQUEUE_H
+#define _SYS_GSQUEUE_H
+
+/*
+ * Standard interfaces to serializaion queues for everyone (except IP).
+ */
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+
+typedef struct gsqueue gsqueue_t;
+typedef struct gsqueue_set gsqueue_set_t;
+
+typedef void (*gsqueue_cb_f)(gsqueue_set_t *, gsqueue_t *, void *, boolean_t);
+typedef void (*gsqueue_proc_f)(void *, mblk_t *, gsqueue_t *, void *);
+
+extern gsqueue_set_t *gsqueue_set_create(pri_t);
+extern void gsqueue_set_destroy(gsqueue_set_t *);
+extern gsqueue_t *gsqueue_set_get(gsqueue_set_t *, uint_t);
+
+extern uintptr_t gsqueue_set_cb_add(gsqueue_set_t *, gsqueue_cb_f, void *);
+extern int gsqueue_set_cb_remove(gsqueue_set_t *, uintptr_t);
+
+#define GSQUEUE_FILL 0x0001
+#define GSQUEUE_NODRAIN 0x0002
+#define GSQUEUE_PROCESS 0x0004
+
+extern void gsqueue_enter_one(gsqueue_t *, mblk_t *, gsqueue_proc_f, void *,
+ int, uint8_t);
+
+#define GSQUEUE_DEFAULT_PRIORITY MAXCLSYSPRI
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_GSQUEUE_H */
diff --git a/usr/src/uts/common/sys/ia.h b/usr/src/uts/common/sys/ia.h
index 02dc29aaec..567c121bb0 100644
--- a/usr/src/uts/common/sys/ia.h
+++ b/usr/src/uts/common/sys/ia.h
@@ -86,6 +86,7 @@ typedef struct iaproc {
/* flags */
+/* Formerly: IAKPRI 0x01 - thread at kernel model priority */
#define IABACKQ 0x02 /* thread goes to back of disp q when preempted */
#define IASLEPT 0x04 /* thread had long-term suspend - give new slice */
diff --git a/usr/src/uts/common/sys/inotify.h b/usr/src/uts/common/sys/inotify.h
new file mode 100644
index 0000000000..8acc1a7280
--- /dev/null
+++ b/usr/src/uts/common/sys/inotify.h
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+/*
+ * Header file to support for the inotify facility. Note that this facility
+ * is designed to be binary compatible with the Linux inotify facility; values
+ * for constants here should therefore exactly match those found in Linux, and
+ * this facility shouldn't be extended independently of Linux.
+ */
+
+#ifndef _SYS_INOTIFY_H
+#define _SYS_INOTIFY_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Events that can be explicitly requested on any inotify watch.
+ */
+#define IN_ACCESS 0x00000001
+#define IN_MODIFY 0x00000002
+#define IN_ATTRIB 0x00000004
+#define IN_CLOSE_WRITE 0x00000008
+#define IN_CLOSE_NOWRITE 0x00000010
+#define IN_OPEN 0x00000020
+#define IN_MOVED_FROM 0x00000040
+#define IN_MOVED_TO 0x00000080
+#define IN_CREATE 0x00000100
+#define IN_DELETE 0x00000200
+#define IN_DELETE_SELF 0x00000400
+#define IN_MOVE_SELF 0x00000800
+
+/*
+ * Events that can be sent to an inotify watch -- requested or not.
+ */
+#define IN_UNMOUNT 0x00002000
+#define IN_Q_OVERFLOW 0x00004000
+#define IN_IGNORED 0x00008000
+
+/*
+ * Flags that can modify an inotify event.
+ */
+#define IN_ONLYDIR 0x01000000
+#define IN_DONT_FOLLOW 0x02000000
+#define IN_EXCL_UNLINK 0x04000000
+#define IN_MASK_ADD 0x20000000
+#define IN_ISDIR 0x40000000
+#define IN_ONESHOT 0x80000000
+
+/*
+ * Helpful constants.
+ */
+#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
+#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO)
+#define IN_ALL_EVENTS \
+ (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
+ IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | IN_MOVED_TO | \
+ IN_DELETE | IN_CREATE | IN_DELETE_SELF | IN_MOVE_SELF)
+
+#define IN_CHILD_EVENTS \
+ (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
+ IN_CLOSE_NOWRITE | IN_MODIFY | IN_OPEN)
+
+/*
+ * To assure binary compatibility with Linux, these values are fixed at their
+ * Linux equivalents, not their native ones.
+ */
+#define IN_CLOEXEC 02000000 /* LX_O_CLOEXEC */
+#define IN_NONBLOCK 04000 /* LX_O_NONBLOCK */
+
+struct inotify_event {
+ int32_t wd; /* watch descriptor */
+ uint32_t mask; /* mask of events */
+ uint32_t cookie; /* event association cookie, if any */
+ uint32_t len; /* size of name field */
+ char name[]; /* optional NUL-terminated name */
+};
+
+/*
+ * These ioctl values are specific to the native implementation; applications
+ * shouldn't be using them directly, and they should therefore be safe to
+ * change without breaking apps.
+ */
+#define INOTIFYIOC (('i' << 24) | ('n' << 16) | ('y' << 8))
+#define INOTIFYIOC_ADD_WATCH (INOTIFYIOC | 1) /* add watch */
+#define INOTIFYIOC_RM_WATCH (INOTIFYIOC | 2) /* remove watch */
+#define INOTIFYIOC_ADD_CHILD (INOTIFYIOC | 3) /* add child watch */
+#define INOTIFYIOC_ACTIVATE (INOTIFYIOC | 4) /* activate watch */
+
+#ifndef _LP64
+#ifndef _LITTLE_ENDIAN
+#define INOTIFY_PTR(type, name) uint32_t name##pad; type *name
+#else
+#define INOTIFY_PTR(type, name) type *name; uint32_t name##pad
+#endif
+#else
+#define INOTIFY_PTR(type, name) type *name
+#endif
+
+typedef struct inotify_addwatch {
+ int inaw_fd; /* open fd for object */
+ uint32_t inaw_mask; /* desired mask */
+} inotify_addwatch_t;
+
+typedef struct inotify_addchild {
+ INOTIFY_PTR(char, inac_name); /* pointer to name */
+ int inac_fd; /* open fd for parent */
+} inotify_addchild_t;
+
+#ifndef _KERNEL
+
+extern int inotify_init(void);
+extern int inotify_init1(int);
+extern int inotify_add_watch(int, const char *, uint32_t);
+extern int inotify_rm_watch(int, int);
+
+#else
+
+#define IN_UNMASKABLE \
+ (IN_UNMOUNT | IN_Q_OVERFLOW | IN_IGNORED | IN_ISDIR)
+
+#define IN_MODIFIERS \
+ (IN_EXCL_UNLINK | IN_ONESHOT)
+
+#define IN_FLAGS \
+ (IN_ONLYDIR | IN_DONT_FOLLOW | IN_MASK_ADD)
+
+#define IN_REMOVAL (1ULL << 32)
+#define INOTIFYMNRN_INOTIFY 0
+#define INOTIFYMNRN_CLONE 1
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_INOTIFY_H */
diff --git a/usr/src/uts/common/sys/ipc_impl.h b/usr/src/uts/common/sys/ipc_impl.h
index 0569c3e967..d7dc365c09 100644
--- a/usr/src/uts/common/sys/ipc_impl.h
+++ b/usr/src/uts/common/sys/ipc_impl.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
#ifndef _IPC_IMPL_H
@@ -226,6 +227,7 @@ int ipc_commit_begin(ipc_service_t *, key_t, int, kipc_perm_t *);
kmutex_t *ipc_commit_end(ipc_service_t *, kipc_perm_t *);
void ipc_cleanup(ipc_service_t *, kipc_perm_t *);
+void ipc_rmsvc(ipc_service_t *, kipc_perm_t *);
int ipc_rmid(ipc_service_t *, int, cred_t *);
int ipc_ids(ipc_service_t *, int *, uint_t, uint_t *);
diff --git a/usr/src/uts/common/sys/ipd.h b/usr/src/uts/common/sys/ipd.h
index bad74f8b81..f21c3fb5af 100644
--- a/usr/src/uts/common/sys/ipd.h
+++ b/usr/src/uts/common/sys/ipd.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2018, Joyent, Inc. All rights reserved.
*/
/*
@@ -35,7 +35,7 @@ extern "C" {
#endif
#define IPD_DEV_PATH "/dev/ipd"
-#define IPD_MAX_DELAY 10000 /* 10 ms in us */
+#define IPD_MAX_DELAY 1000000 /* 1 second in microseconds */
typedef struct ipd_ioc_perturb {
zoneid_t ipip_zoneid;
diff --git a/usr/src/uts/common/sys/iso/signal_iso.h b/usr/src/uts/common/sys/iso/signal_iso.h
index bf89ef0d33..0a76ee19a7 100644
--- a/usr/src/uts/common/sys/iso/signal_iso.h
+++ b/usr/src/uts/common/sys/iso/signal_iso.h
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -95,7 +96,7 @@ extern "C" {
/* insert new signals here, and move _SIGRTM* appropriately */
#define _SIGRTMIN 42 /* first (highest-priority) realtime signal */
-#define _SIGRTMAX 73 /* last (lowest-priority) realtime signal */
+#define _SIGRTMAX 74 /* last (lowest-priority) realtime signal */
extern long _sysconf(int); /* System Private interface to sysconf() */
#define SIGRTMIN ((int)_sysconf(_SC_SIGRT_MIN)) /* first realtime signal */
#define SIGRTMAX ((int)_sysconf(_SC_SIGRT_MAX)) /* last realtime signal */
diff --git a/usr/src/uts/common/sys/klwp.h b/usr/src/uts/common/sys/klwp.h
index 41b70f6a6e..0ea1a396b9 100644
--- a/usr/src/uts/common/sys/klwp.h
+++ b/usr/src/uts/common/sys/klwp.h
@@ -24,7 +24,7 @@
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _SYS_KLWP_H
@@ -191,7 +191,14 @@ typedef struct _klwp {
struct ct_template *lwp_ct_active[CTT_MAXTYPE]; /* active templates */
struct contract *lwp_ct_latest[CTT_MAXTYPE]; /* last created contract */
- void *lwp_brand; /* per-lwp brand data */
+ /*
+ * Branding:
+ * lwp_brand - per-lwp brand data
+ * lwp_brand_syscall - brand syscall interposer
+ */
+ void *lwp_brand;
+ int (*lwp_brand_syscall)(void);
+
struct psinfo *lwp_spymaster; /* if an agent LWP, our spymaster */
} klwp_t;
diff --git a/usr/src/uts/common/sys/mac_client.h b/usr/src/uts/common/sys/mac_client.h
index 1d1915a816..8fff314bfe 100644
--- a/usr/src/uts/common/sys/mac_client.h
+++ b/usr/src/uts/common/sys/mac_client.h
@@ -116,6 +116,7 @@ typedef enum {
#define MAC_PROMISC_FLAGS_NO_PHYS 0x0002
#define MAC_PROMISC_FLAGS_VLAN_TAG_STRIP 0x0004
#define MAC_PROMISC_FLAGS_NO_COPY 0x0008
+#define MAC_PROMISC_FLAGS_DO_FIXUPS 0x0010
/* flags passed to mac_tx() */
#define MAC_DROP_ON_NO_DESC 0x01 /* freemsg() if no tx descs */
diff --git a/usr/src/uts/common/sys/mac_client_impl.h b/usr/src/uts/common/sys/mac_client_impl.h
index 0e3a6306e0..0f8be50fde 100644
--- a/usr/src/uts/common/sys/mac_client_impl.h
+++ b/usr/src/uts/common/sys/mac_client_impl.h
@@ -83,6 +83,7 @@ typedef struct mac_promisc_impl_s { /* Protected by */
boolean_t mpi_no_phys; /* WO */
boolean_t mpi_strip_vlan_tag; /* WO */
boolean_t mpi_no_copy; /* WO */
+ boolean_t mpi_do_fixups; /* WO */
} mac_promisc_impl_t;
typedef union mac_tx_percpu_s {
diff --git a/usr/src/uts/common/sys/mac_client_priv.h b/usr/src/uts/common/sys/mac_client_priv.h
index 01cb27644c..97b3fd685a 100644
--- a/usr/src/uts/common/sys/mac_client_priv.h
+++ b/usr/src/uts/common/sys/mac_client_priv.h
@@ -58,6 +58,9 @@ extern const mac_info_t *mac_info(mac_handle_t);
extern boolean_t mac_info_get(const char *, mac_info_t *);
extern boolean_t mac_promisc_get(mac_handle_t);
+extern boolean_t mac_protect_check_addr(mac_client_handle_t, boolean_t,
+ in6_addr_t *);
+
extern int mac_start(mac_handle_t);
extern void mac_stop(mac_handle_t);
diff --git a/usr/src/uts/common/sys/mac_flow.h b/usr/src/uts/common/sys/mac_flow.h
index 04aa8be3f3..a9a2a5f61e 100644
--- a/usr/src/uts/common/sys/mac_flow.h
+++ b/usr/src/uts/common/sys/mac_flow.h
@@ -22,7 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2013 Joyent, Inc. All rights reserved.
+ * Copyright 2017 Joyent, Inc. All rights reserved.
* Copyright 2020 RackTop Systems, Inc.
*/
@@ -156,6 +156,14 @@ typedef enum {
#define MPT_MAXIPADDR MPT_MAXCNT
#define MPT_MAXCID MPT_MAXCNT
#define MPT_MAXCIDLEN 256
+#define MPT_FALSE 0x00000000
+#define MPT_TRUE 0x00000001
+
+/* Dynamic address detection types */
+#define MPT_DYN_DHCPV4 0x00000001
+#define MPT_DYN_DHCPV6 0x00000002
+#define MPT_DYN_SLAAC 0x00000004
+#define MPT_DYN_ALL 0x00000007
typedef struct mac_ipaddr_s {
uint32_t ip_version;
@@ -176,11 +184,13 @@ typedef struct mac_dhcpcid_s {
} mac_dhcpcid_t;
typedef struct mac_protect_s {
- uint32_t mp_types;
- uint32_t mp_ipaddrcnt;
- mac_ipaddr_t mp_ipaddrs[MPT_MAXIPADDR];
- uint32_t mp_cidcnt;
- mac_dhcpcid_t mp_cids[MPT_MAXCID];
+ uint32_t mp_types; /* Enabled protection types */
+ uint32_t mp_ipaddrcnt; /* Count of allowed IPs */
+ mac_ipaddr_t mp_ipaddrs[MPT_MAXIPADDR]; /* Allowed IPs */
+ uint32_t mp_cidcnt; /* Count of allowed DHCP CIDs */
+ mac_dhcpcid_t mp_cids[MPT_MAXCID]; /* Allowed DHCP CIDs */
+ uint32_t mp_allcids; /* Whether to allow all CIDs through */
+ uint32_t mp_dynamic; /* Enabled dynamic address methods */
} mac_protect_t;
/* The default priority for links */
diff --git a/usr/src/uts/common/sys/mman.h b/usr/src/uts/common/sys/mman.h
index 11fa46e571..6906cb3dbf 100644
--- a/usr/src/uts/common/sys/mman.h
+++ b/usr/src/uts/common/sys/mman.h
@@ -337,6 +337,7 @@ struct memcntl_mha32 {
#define MS_SYNC 0x4 /* wait for msync */
#define MS_ASYNC 0x1 /* return immediately */
#define MS_INVALIDATE 0x2 /* invalidate caches */
+#define MS_INVALCURPROC 0x8 /* invalidate cache for curproc only */
#if !defined(_STRICT_POSIX) || (_POSIX_C_SOURCE > 2) || defined(_XPG5)
/* flags to mlockall */
diff --git a/usr/src/uts/common/sys/mntent.h b/usr/src/uts/common/sys/mntent.h
index 88c98dc5a4..7196f7b3ac 100644
--- a/usr/src/uts/common/sys/mntent.h
+++ b/usr/src/uts/common/sys/mntent.h
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2012, Joyent, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*
* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
@@ -47,6 +48,7 @@ extern "C" {
#define MNTTYPE_PCFS "pcfs" /* PC (MSDOS) file system */
#define MNTTYPE_PC MNTTYPE_PCFS /* Deprecated name; use MNTTYPE_PCFS */
#define MNTTYPE_LOFS "lofs" /* Loop back file system */
+#define MNTTYPE_HYPRLOFS "hyprlofs" /* Hyperlofs file system */
#define MNTTYPE_LO MNTTYPE_LOFS /* Deprecated name; use MNTTYPE_LOFS */
#define MNTTYPE_HSFS "hsfs" /* High Sierra (9660) file system */
#define MNTTYPE_SWAP "swap" /* Swap file system */
diff --git a/usr/src/uts/common/sys/netconfig.h b/usr/src/uts/common/sys/netconfig.h
index 6407534a3b..658f9f3f6b 100644
--- a/usr/src/uts/common/sys/netconfig.h
+++ b/usr/src/uts/common/sys/netconfig.h
@@ -28,6 +28,7 @@
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_NETCONFIG_H
diff --git a/usr/src/uts/common/sys/neti.h b/usr/src/uts/common/sys/neti.h
index e7027f8ece..92bd5b897d 100644
--- a/usr/src/uts/common/sys/neti.h
+++ b/usr/src/uts/common/sys/neti.h
@@ -48,6 +48,8 @@ struct msgb; /* avoiding sys/stream.h here */
#define NHF_INET "NHF_INET"
#define NHF_INET6 "NHF_INET6"
#define NHF_ARP "NHF_ARP"
+#define NHF_VND_INET "NHF_VND_INET"
+#define NHF_VND_INET6 "NHF_VND_INET6"
#define NHF_VIONA "NHF_VIONA"
/*
diff --git a/usr/src/uts/common/sys/netstack.h b/usr/src/uts/common/sys/netstack.h
index 7ee33318cd..b327e69fad 100644
--- a/usr/src/uts/common/sys/netstack.h
+++ b/usr/src/uts/common/sys/netstack.h
@@ -88,7 +88,8 @@ typedef id_t netstackid_t;
#define NS_IPSECESP 16
#define NS_IPNET 17
#define NS_ILB 18
-#define NS_MAX (NS_ILB+1)
+#define NS_VND 19
+#define NS_MAX (NS_VND+1)
/*
* State maintained for each module which tracks the state of
diff --git a/usr/src/uts/common/sys/param.h b/usr/src/uts/common/sys/param.h
index 282d84b912..66bd91f76f 100644
--- a/usr/src/uts/common/sys/param.h
+++ b/usr/src/uts/common/sys/param.h
@@ -116,7 +116,7 @@ extern "C" {
#define DEFAULT_MAXPID 999999
#define DEFAULT_JUMPPID 100000
#else
-#define DEFAULT_MAXPID 30000
+#define DEFAULT_MAXPID 99999
#define DEFAULT_JUMPPID 0
#endif
diff --git a/usr/src/uts/common/sys/policy.h b/usr/src/uts/common/sys/policy.h
index d8983a28c4..9f1b80d390 100644
--- a/usr/src/uts/common/sys/policy.h
+++ b/usr/src/uts/common/sys/policy.h
@@ -108,6 +108,7 @@ int secpolicy_ipc_owner(const cred_t *, const struct kipc_perm *);
int secpolicy_kmdb(const cred_t *);
int secpolicy_lock_memory(const cred_t *);
int secpolicy_meminfo(const cred_t *);
+int secpolicy_fs_import(const cred_t *);
int secpolicy_modctl(const cred_t *, int);
int secpolicy_net(const cred_t *, int, boolean_t);
int secpolicy_net_bindmlp(const cred_t *);
@@ -176,6 +177,7 @@ int secpolicy_setid_setsticky_clear(vnode_t *, vattr_t *,
const vattr_t *, cred_t *);
int secpolicy_xvattr(xvattr_t *, uid_t, cred_t *, vtype_t);
int secpolicy_xvm_control(const cred_t *);
+int secpolicy_hyprlofs_control(const cred_t *);
int secpolicy_basic_exec(const cred_t *, vnode_t *);
int secpolicy_basic_fork(const cred_t *);
diff --git a/usr/src/uts/common/sys/poll_impl.h b/usr/src/uts/common/sys/poll_impl.h
index ff277f89c8..388849a14f 100644
--- a/usr/src/uts/common/sys/poll_impl.h
+++ b/usr/src/uts/common/sys/poll_impl.h
@@ -141,6 +141,7 @@ struct pollstate {
pollstate_t *ps_contend_nextp; /* next in contender list */
pollstate_t **ps_contend_pnextp; /* pointer-to-previous-next */
int ps_flags; /* state flags */
+ short ps_implicit_ev; /* implicit poll event interest */
};
/* pollstate flags */
diff --git a/usr/src/uts/common/sys/proc.h b/usr/src/uts/common/sys/proc.h
index 06e5a8caf4..d05886d1fc 100644
--- a/usr/src/uts/common/sys/proc.h
+++ b/usr/src/uts/common/sys/proc.h
@@ -357,6 +357,7 @@ typedef struct proc {
struct zone *p_zone; /* zone in which process lives */
struct vnode *p_execdir; /* directory that p_exec came from */
struct brand *p_brand; /* process's brand */
+
void *p_brand_data; /* per-process brand state */
psecflags_t p_secflags; /* per-process security flags */
@@ -373,7 +374,6 @@ typedef struct proc {
*/
struct user p_user; /* (see sys/user.h) */
} proc_t;
-
#define PROC_T /* headers relying on proc_t are OK */
#ifdef _KERNEL
@@ -647,6 +647,7 @@ extern int signal_is_blocked(kthread_t *, int);
extern int sigcheck(proc_t *, kthread_t *);
extern void sigdefault(proc_t *);
+extern struct pid *pid_find(pid_t pid);
extern void pid_setmin(void);
extern pid_t pid_allocate(proc_t *, pid_t, int);
extern int pid_rele(struct pid *);
@@ -662,6 +663,7 @@ extern int sprtrylock_proc(proc_t *);
extern void sprwaitlock_proc(proc_t *);
extern void sprlock_proc(proc_t *);
extern void sprunlock(proc_t *);
+extern void sprunprlock(proc_t *);
extern void pid_init(void);
extern proc_t *pid_entry(int);
extern int pid_slot(proc_t *);
@@ -753,6 +755,10 @@ extern kthread_t *thread_unpin(void);
extern void thread_init(void);
extern void thread_load(kthread_t *, void (*)(), caddr_t, size_t);
+extern void thread_splitstack(void (*)(void *), void *, size_t);
+extern void thread_splitstack_run(caddr_t, void (*)(void *), void *);
+extern void thread_splitstack_cleanup(void);
+
extern void tsd_create(uint_t *, void (*)(void *));
extern void tsd_destroy(uint_t *);
extern void *tsd_getcreate(uint_t *, void (*)(void *), void *(*)(void));
@@ -794,7 +800,7 @@ extern void pokelwps(proc_t *);
extern void continuelwps(proc_t *);
extern int exitlwps(int);
extern void lwp_ctmpl_copy(klwp_t *, klwp_t *);
-extern void lwp_ctmpl_clear(klwp_t *);
+extern void lwp_ctmpl_clear(klwp_t *, boolean_t);
extern klwp_t *forklwp(klwp_t *, proc_t *, id_t);
extern void lwp_load(klwp_t *, gregset_t, uintptr_t);
extern void lwp_setrval(klwp_t *, int, int);
diff --git a/usr/src/uts/common/sys/procfs.h b/usr/src/uts/common/sys/procfs.h
index 00ba23594e..3d6760a7b4 100644
--- a/usr/src/uts/common/sys/procfs.h
+++ b/usr/src/uts/common/sys/procfs.h
@@ -25,7 +25,7 @@
*/
/*
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
*/
@@ -237,6 +237,7 @@ typedef struct pstatus {
#define PR_FAULTED 6
#define PR_SUSPENDED 7
#define PR_CHECKPOINT 8
+#define PR_BRAND 9
/*
* lwp ps(1) information file. /proc/<pid>/lwp/<lwpid>/lwpsinfo
@@ -271,10 +272,12 @@ typedef struct lwpsinfo {
int pr_filler[4]; /* reserved for future use */
} lwpsinfo_t;
+#define PRARGSZ 80 /* number of chars of arguments */
+#define PRMAXARGVLEN 4096 /* max len of /proc/%s/argv */
+
/*
* process ps(1) information file. /proc/<pid>/psinfo
*/
-#define PRARGSZ 80 /* number of chars of arguments */
typedef struct psinfo {
int pr_flag; /* process flags (DEPRECATED; do not use) */
int pr_nlwp; /* number of active lwps in the process */
diff --git a/usr/src/uts/common/sys/ptms.h b/usr/src/uts/common/sys/ptms.h
index 23594fdc13..52d69b3416 100644
--- a/usr/src/uts/common/sys/ptms.h
+++ b/usr/src/uts/common/sys/ptms.h
@@ -125,6 +125,12 @@ extern void ptms_logp(char *, uintptr_t);
#define DDBGP(a, b)
#endif
+typedef struct __ptmptsopencb_arg *ptmptsopencb_arg_t;
+typedef struct ptmptsopencb {
+ boolean_t (*ppocb_func)(ptmptsopencb_arg_t);
+ ptmptsopencb_arg_t ppocb_arg;
+} ptmptsopencb_t;
+
#endif /* _KERNEL */
typedef struct pt_own {
@@ -160,6 +166,19 @@ typedef struct pt_own {
#define ZONEPT (('P'<<8)|4) /* set zone of manager/subsidiary pair */
#define OWNERPT (('P'<<8)|5) /* set owner/group for subsidiary */
+#ifdef _KERNEL
+/*
+ * kernel ioctl commands
+ *
+ * PTMPTSOPENCB: Returns a callback function pointer and opaque argument.
+ * The return value of the callback function when it's invoked
+ * with the opaque argument passed to it will indicate if the
+ * pts slave device is currently open.
+ */
+#define PTMPTSOPENCB (('P'<<8)|6) /* check if the slave is open */
+
+#endif /* _KERNEL */
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/refhash.h b/usr/src/uts/common/sys/refhash.h
index b7427a454d..469cb6d686 100644
--- a/usr/src/uts/common/sys/refhash.h
+++ b/usr/src/uts/common/sys/refhash.h
@@ -19,6 +19,10 @@
#include <sys/types.h>
#include <sys/list.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define RHL_F_DEAD 0x01
typedef struct refhash_link {
@@ -58,4 +62,8 @@ extern void *refhash_first(refhash_t *);
extern void *refhash_next(refhash_t *, void *);
extern boolean_t refhash_obj_valid(refhash_t *hp, const void *);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* _SYS_REFHASH_H */
diff --git a/usr/src/uts/common/sys/resource.h b/usr/src/uts/common/sys/resource.h
index 13166f378d..d65ca00f69 100644
--- a/usr/src/uts/common/sys/resource.h
+++ b/usr/src/uts/common/sys/resource.h
@@ -23,6 +23,7 @@
*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
diff --git a/usr/src/uts/common/sys/rt.h b/usr/src/uts/common/sys/rt.h
index d4233aecb5..2ed7320a09 100644
--- a/usr/src/uts/common/sys/rt.h
+++ b/usr/src/uts/common/sys/rt.h
@@ -22,6 +22,7 @@
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -75,6 +76,16 @@ typedef struct rtkparms {
int rt_tqsig; /* real-time time quantum signal */
uint_t rt_cflags; /* real-time control flags */
} rtkparms_t;
+
+#define RTGPPRIO0 100 /* Global priority for RT priority 0 */
+
+/*
+ * control flags (kparms->rt_cflags).
+ */
+#define RT_DOPRI 0x01 /* change priority */
+#define RT_DOTQ 0x02 /* change RT time quantum */
+#define RT_DOSIG 0x04 /* change RT time quantum signal */
+
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mpi/mpi2_pci.h b/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mpi/mpi2_pci.h
new file mode 100644
index 0000000000..afb7a94c58
--- /dev/null
+++ b/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mpi/mpi2_pci.h
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 2012-2015 LSI Corp.
+ * Copyright (c) 2013-2016 Avago Technologies
+ * 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.
+ * 3. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/*
+ * Copyright (c) 2000-2015 LSI Corporation.
+ * Copyright (c) 2013-2016 Avago Technologies
+ * All rights reserved.
+ *
+ *
+ * Name: mpi2_pci.h
+ * Title: MPI PCIe Attached Devices structures and definitions.
+ * Creation Date: October 9, 2012
+ *
+ * mpi2_pci.h Version: 02.00.02
+ *
+ * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
+ * prefix are for use only on MPI v2.5 products, and must not be used
+ * with MPI v2.0 products. Unless otherwise noted, names beginning with
+ * MPI2 or Mpi2 are for use with both MPI v2.0 and MPI v2.5 products.
+ *
+ * Version History
+ * ---------------
+ *
+ * Date Version Description
+ * -------- -------- ------------------------------------------------------
+ * 03-16-15 02.00.00 Initial version.
+ * 02-17-16 02.00.01 Removed AHCI support.
+ * Removed SOP support.
+ * 07-01-16 02.00.02 Added MPI26_NVME_FLAGS_FORCE_ADMIN_ERR_RESP to
+ * NVME Encapsulated Request.
+ * --------------------------------------------------------------------------
+ */
+
+#ifndef MPI2_PCI_H
+#define MPI2_PCI_H
+
+
+/*
+ * Values for the PCIe DeviceInfo field used in PCIe Device Status Change Event
+ * data and PCIe Configuration pages.
+ */
+#define MPI26_PCIE_DEVINFO_DIRECT_ATTACH (0x00000010)
+
+#define MPI26_PCIE_DEVINFO_MASK_DEVICE_TYPE (0x0000000F)
+#define MPI26_PCIE_DEVINFO_NO_DEVICE (0x00000000)
+#define MPI26_PCIE_DEVINFO_PCI_SWITCH (0x00000001)
+#define MPI26_PCIE_DEVINFO_NVME (0x00000003)
+
+
+/****************************************************************************
+* NVMe Encapsulated message
+****************************************************************************/
+
+/* NVME Encapsulated Request Message */
+typedef struct _MPI26_NVME_ENCAPSULATED_REQUEST
+{
+ U16 DevHandle; /* 0x00 */
+ U8 ChainOffset; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 EncapsulatedCommandLength; /* 0x04 */
+ U8 Reserved1; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U32 Reserved3; /* 0x0C */
+ U64 ErrorResponseBaseAddress; /* 0x10 */
+ U16 ErrorResponseAllocationLength; /* 0x18 */
+ U16 Flags; /* 0x1A */
+ U32 DataLength; /* 0x1C */
+ U8 NVMe_Command[4]; /* 0x20 */ /* variable length */
+
+} MPI26_NVME_ENCAPSULATED_REQUEST, MPI2_POINTER PTR_MPI26_NVME_ENCAPSULATED_REQUEST,
+ Mpi26NVMeEncapsulatedRequest_t, MPI2_POINTER pMpi26NVMeEncapsulatedRequest_t;
+
+/* defines for the Flags field */
+#define MPI26_NVME_FLAGS_FORCE_ADMIN_ERR_RESP (0x0020)
+/* Submission Queue Type*/
+#define MPI26_NVME_FLAGS_SUBMISSIONQ_MASK (0x0010)
+#define MPI26_NVME_FLAGS_SUBMISSIONQ_IO (0x0000)
+#define MPI26_NVME_FLAGS_SUBMISSIONQ_ADMIN (0x0010)
+/* Error Response Address Space */
+#define MPI26_NVME_FLAGS_MASK_ERROR_RSP_ADDR (0x000C)
+#define MPI26_NVME_FLAGS_SYSTEM_RSP_ADDR (0x0000)
+#define MPI26_NVME_FLAGS_IOCPLB_RSP_ADDR (0x0008)
+#define MPI26_NVME_FLAGS_IOCPLBNTA_RSP_ADDR (0x000C)
+/* Data Direction*/
+#define MPI26_NVME_FLAGS_DATADIRECTION_MASK (0x0003)
+#define MPI26_NVME_FLAGS_NODATATRANSFER (0x0000)
+#define MPI26_NVME_FLAGS_WRITE (0x0001)
+#define MPI26_NVME_FLAGS_READ (0x0002)
+#define MPI26_NVME_FLAGS_BIDIRECTIONAL (0x0003)
+
+
+/* NVMe Encapuslated Reply Message */
+typedef struct _MPI26_NVME_ENCAPSULATED_ERROR_REPLY
+{
+ U16 DevHandle; /* 0x00 */
+ U8 MsgLength; /* 0x02 */
+ U8 Function; /* 0x03 */
+ U16 EncapsulatedCommandLength; /* 0x04 */
+ U8 Reserved1; /* 0x06 */
+ U8 MsgFlags; /* 0x07 */
+ U8 VP_ID; /* 0x08 */
+ U8 VF_ID; /* 0x09 */
+ U16 Reserved2; /* 0x0A */
+ U16 Reserved3; /* 0x0C */
+ U16 IOCStatus; /* 0x0E */
+ U32 IOCLogInfo; /* 0x10 */
+ U16 ErrorResponseCount; /* 0x14 */
+ U16 Reserved4; /* 0x16 */
+} MPI26_NVME_ENCAPSULATED_ERROR_REPLY,
+ MPI2_POINTER PTR_MPI26_NVME_ENCAPSULATED_ERROR_REPLY,
+ Mpi26NVMeEncapsulatedErrorReply_t,
+ MPI2_POINTER pMpi26NVMeEncapsulatedErrorReply_t;
+
+
+#endif
+
+
diff --git a/usr/src/uts/common/sys/scsi/generic/inquiry.h b/usr/src/uts/common/sys/scsi/generic/inquiry.h
index ddfd683169..fcbf00d5dc 100644
--- a/usr/src/uts/common/sys/scsi/generic/inquiry.h
+++ b/usr/src/uts/common/sys/scsi/generic/inquiry.h
@@ -362,7 +362,8 @@ struct scsi_inquiry {
#define DTYPE_NOTPRESENT (DPQ_NEVER | DTYPE_UNKNOWN)
/*
- * Defined Response Data Formats:
+ * Defined Versions for inquiry data. These represent the base version that a
+ * device supports.
*/
#define RDF_LEVEL0 0x00 /* no conformance claim (SCSI-1) */
#define RDF_CCS 0x01 /* Obsolete (pseudo-spec) */
@@ -370,7 +371,8 @@ struct scsi_inquiry {
#define RDF_SCSI_SPC 0x03 /* ANSI INCITS 301-1997 (SPC) */
#define RDF_SCSI_SPC2 0x04 /* ANSI INCITS 351-2001 (SPC-2) */
#define RDF_SCSI_SPC3 0x05 /* ANSI INCITS 408-2005 (SPC-3) */
-#define RDF_SCSI_SPC4 0x06 /* t10 (SPC-4) */
+#define RDF_SCSI_SPC4 0x06 /* ANSI INCITS 513-2015 (SPC-4) */
+#define RDF_SCSI_SPC5 0x07 /* t10 (SPC-5) */
/*
* Defined Target Port Group Select values:
@@ -436,6 +438,7 @@ struct vpd_desc {
#define PM_CAPABLE_SPC2 RDF_SCSI_SPC2
#define PM_CAPABLE_SPC3 RDF_SCSI_SPC3
#define PM_CAPABLE_SPC4 RDF_SCSI_SPC4
+#define PM_CAPABLE_SPC5 RDF_SCSI_SPC5
#define PM_CAPABLE_LOG_MASK 0xffff0000 /* use upper 16 bit to */
/* indicate log specifics */
#define PM_CAPABLE_LOG_SUPPORTED 0x10000 /* Log page 0xE might be */
diff --git a/usr/src/uts/common/sys/scsi/targets/sddef.h b/usr/src/uts/common/sys/scsi/targets/sddef.h
index d28918d9c5..bb522141af 100644
--- a/usr/src/uts/common/sys/scsi/targets/sddef.h
+++ b/usr/src/uts/common/sys/scsi/targets/sddef.h
@@ -763,6 +763,12 @@ _NOTE(MUTEX_PROTECTS_DATA(sd_lun::un_fi_mutex,
#define SD_FM_LOG(un) (((struct sd_fm_internal *)\
((un)->un_fm_private))->fm_log_level)
+/*
+ * Version Related Macros
+ */
+#define SD_SCSI_VERS_IS_GE_SPC_4(un) \
+ (SD_INQUIRY(un)->inq_ansi == RDF_SCSI_SPC4 || \
+ SD_INQUIRY(un)->inq_ansi == RDF_SCSI_SPC5)
/*
* Values for un_ctype
@@ -1821,6 +1827,10 @@ struct sd_fm_internal {
#define SD_PM_CAPABLE_IS_SPC_4(pm_cap) \
((pm_cap & PM_CAPABLE_PM_MASK) == PM_CAPABLE_SPC4)
+#define SD_PM_CAPABLE_IS_GE_SPC_4(pm_cap) \
+ (((pm_cap & PM_CAPABLE_PM_MASK) == PM_CAPABLE_SPC4) || \
+ ((pm_cap & PM_CAPABLE_PM_MASK) == PM_CAPABLE_SPC5))
+
#define SD_PM_CAP_LOG_SUPPORTED(pm_cap) \
((pm_cap & PM_CAPABLE_LOG_SUPPORTED) ? TRUE : FALSE)
diff --git a/usr/src/uts/common/sys/shm.h b/usr/src/uts/common/sys/shm.h
index 0219fc2cf7..8f530afda2 100644
--- a/usr/src/uts/common/sys/shm.h
+++ b/usr/src/uts/common/sys/shm.h
@@ -21,6 +21,7 @@
*/
/*
* Copyright 2014 Garrett D'Amore <garrett@damore.org>
+ * Copyright 2016 Joyent, Inc.
*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -120,6 +121,10 @@ struct shmid_ds {
#define SHM_LOCK 3 /* Lock segment in core */
#define SHM_UNLOCK 4 /* Unlock segment */
+#if defined(_KERNEL)
+#define SHM_RMID 5 /* Private RMID for lx support */
+#endif
+
#if !defined(_KERNEL)
int shmget(key_t, size_t, int);
int shmids(int *, uint_t, uint_t *);
diff --git a/usr/src/uts/common/sys/shm_impl.h b/usr/src/uts/common/sys/shm_impl.h
index 4d8cdcede5..1eae2ca0a4 100644
--- a/usr/src/uts/common/sys/shm_impl.h
+++ b/usr/src/uts/common/sys/shm_impl.h
@@ -21,13 +21,12 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _SYS_SHM_IMPL_H
#define _SYS_SHM_IMPL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/ipc_impl.h>
#if defined(_KERNEL) || defined(_KMEMUSER)
#include <sys/shm.h>
@@ -70,7 +69,11 @@ typedef struct kshmid {
time_t shm_ctime; /* last change time */
struct sptinfo *shm_sptinfo; /* info about ISM segment */
struct seg *shm_sptseg; /* pointer to ISM segment */
- long shm_sptprot; /* was reserved (still a "long") */
+ ulong_t shm_opts;
+ /*
+ * Composed of: sptprot (uchar_t) and
+ * RM_PENDING flag (1 bit).
+ */
} kshmid_t;
/*
@@ -78,6 +81,14 @@ typedef struct kshmid {
*/
#define SHMSA_ISM 1 /* uses shared page table */
+/*
+ * shm_opts definitions
+ * Low byte in shm_opts is used for sptprot (see PROT_ALL). The upper bits are
+ * used for additional options.
+ */
+#define SHM_PROT_MASK 0xff
+#define SHM_RM_PENDING 0x100
+
typedef struct sptinfo {
struct as *sptas; /* dummy as ptr. for spt segment */
} sptinfo_t;
diff --git a/usr/src/uts/common/sys/signal.h b/usr/src/uts/common/sys/signal.h
index aece147bec..b12dff6034 100644
--- a/usr/src/uts/common/sys/signal.h
+++ b/usr/src/uts/common/sys/signal.h
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -158,8 +159,8 @@ struct sigaction32 {
* use of these symbols by applications is injurious
* to binary compatibility
*/
-#define NSIG 74 /* valid signals range from 1 to NSIG-1 */
-#define MAXSIG 73 /* size of u_signal[], NSIG-1 <= MAXSIG */
+#define NSIG 75 /* valid signals range from 1 to NSIG-1 */
+#define MAXSIG 74 /* size of u_signal[], NSIG-1 <= MAXSIG */
#endif /* defined(__EXTENSIONS__) || !defined(_XPG4_2) */
#define MINSIGSTKSZ 2048
diff --git a/usr/src/uts/common/sys/socket.h b/usr/src/uts/common/sys/socket.h
index 9e61bc7bb0..25880522e9 100644
--- a/usr/src/uts/common/sys/socket.h
+++ b/usr/src/uts/common/sys/socket.h
@@ -40,6 +40,9 @@
/* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. */
+/*
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ */
#ifndef _SYS_SOCKET_H
#define _SYS_SOCKET_H
@@ -205,6 +208,7 @@ struct so_snd_bufinfo {
#define SO_SRCADDR 0x2001 /* Internal: AF_UNIX source address */
#define SO_FILEP 0x2002 /* Internal: AF_UNIX file pointer */
#define SO_UNIX_CLOSE 0x2003 /* Internal: AF_UNIX peer closed */
+#define SO_REUSEPORT 0x2004 /* allow simultaneous port reuse */
#endif /* _KERNEL */
/*
@@ -304,8 +308,9 @@ struct linger {
#define AF_INET_OFFLOAD 30 /* Sun private; do not use */
#define AF_TRILL 31 /* TRILL interface */
#define AF_PACKET 32 /* PF_PACKET Linux socket interface */
+#define AF_LX_NETLINK 33 /* Linux-compatible netlink */
-#define AF_MAX 32
+#define AF_MAX 33
/*
* Protocol families, same as address families for now.
@@ -345,6 +350,7 @@ struct linger {
#define PF_INET_OFFLOAD AF_INET_OFFLOAD /* Sun private; do not use */
#define PF_TRILL AF_TRILL
#define PF_PACKET AF_PACKET
+#define PF_LX_NETLINK AF_LX_NETLINK
#define PF_MAX AF_MAX
diff --git a/usr/src/uts/common/sys/socketvar.h b/usr/src/uts/common/sys/socketvar.h
index 479641a11b..1e48b00dd7 100644
--- a/usr/src/uts/common/sys/socketvar.h
+++ b/usr/src/uts/common/sys/socketvar.h
@@ -304,15 +304,16 @@ struct sonode {
#define SS_OOBPEND 0x00002000 /* OOB pending or present - poll */
#define SS_HAVEOOBDATA 0x00004000 /* OOB data present */
#define SS_HADOOBDATA 0x00008000 /* OOB data consumed */
-#define SS_CLOSING 0x00010000 /* in process of closing */
+#define SS_CLOSING 0x00010000 /* in process of closing */
#define SS_FIL_DEFER 0x00020000 /* filter deferred notification */
#define SS_FILOP_OK 0x00040000 /* socket can attach filters */
#define SS_FIL_RCV_FLOWCTRL 0x00080000 /* filter asserted rcv flow ctrl */
+
#define SS_FIL_SND_FLOWCTRL 0x00100000 /* filter asserted snd flow ctrl */
#define SS_FIL_STOP 0x00200000 /* no more filter actions */
-
#define SS_SODIRECT 0x00400000 /* transport supports sodirect */
+#define SS_FILOP_UNSF 0x00800000 /* block attaching unsafe filters */
#define SS_SENTLASTREADSIG 0x01000000 /* last rx signal has been sent */
#define SS_SENTLASTWRITESIG 0x02000000 /* last tx signal has been sent */
@@ -328,7 +329,8 @@ struct sonode {
/*
* Sockets that can fall back to TPI must ensure that fall back is not
- * initiated while a thread is using a socket.
+ * initiated while a thread is using a socket. Otherwise this disables all
+ * future filter attachment.
*/
#define SO_BLOCK_FALLBACK(so, fn) \
ASSERT(MUTEX_NOT_HELD(&(so)->so_lock)); \
@@ -344,6 +346,24 @@ struct sonode {
} \
}
+/*
+ * Sockets that can fall back to TPI must ensure that fall back is not
+ * initiated while a thread is using a socket. Otherwise this disables all
+ * future unsafe filter attachment. Safe filters can still attach after
+ * we execute the function in which this macro is used.
+ */
+#define SO_BLOCK_FALLBACK_SAFE(so, fn) \
+ ASSERT(MUTEX_NOT_HELD(&(so)->so_lock)); \
+ rw_enter(&(so)->so_fallback_rwlock, RW_READER); \
+ if ((so)->so_state & SS_FALLBACK_COMP) { \
+ rw_exit(&(so)->so_fallback_rwlock); \
+ return (fn); \
+ } else if (((so)->so_state & SS_FILOP_UNSF) == 0) { \
+ mutex_enter(&(so)->so_lock); \
+ (so)->so_state |= SS_FILOP_UNSF; \
+ mutex_exit(&(so)->so_lock); \
+ }
+
#define SO_UNBLOCK_FALLBACK(so) { \
rw_exit(&(so)->so_fallback_rwlock); \
}
@@ -375,6 +395,7 @@ struct sonode {
/* The modes below are only for non-streams sockets */
#define SM_ACCEPTSUPP 0x400 /* can handle accept() */
#define SM_SENDFILESUPP 0x800 /* Private: proto supp sendfile */
+#define SM_DEFERERR 0x1000 /* Private: defer so_error delivery */
/*
* Socket versions. Used by the socket library when calling _so_socket().
diff --git a/usr/src/uts/common/sys/sockfilter.h b/usr/src/uts/common/sys/sockfilter.h
index 9f6d8b499b..c4dd6539de 100644
--- a/usr/src/uts/common/sys/sockfilter.h
+++ b/usr/src/uts/common/sys/sockfilter.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
*/
#ifndef _SYS_SOCKFILTER_H
@@ -129,6 +130,15 @@ typedef struct sof_ops {
#define SOF_VERSION 1
+/*
+ * Flag indicating that the filter module is safe to attach after bind,
+ * getsockname, getsockopt or setsockopt calls. By default filters are unsafe
+ * so may not be attached after any socket operation. However, a safe filter
+ * can still be attached after one of the above calls. This makes attaching
+ * the filter less dependent on the initial socket setup order.
+ */
+#define SOF_ATT_SAFE 0x1
+
extern int sof_register(int, const char *, const sof_ops_t *, int);
extern int sof_unregister(const char *);
diff --git a/usr/src/uts/common/sys/squeue.h b/usr/src/uts/common/sys/squeue.h
index 040963eef7..89b355970e 100644
--- a/usr/src/uts/common/sys/squeue.h
+++ b/usr/src/uts/common/sys/squeue.h
@@ -30,6 +30,17 @@
extern "C" {
#endif
+/*
+ * Originally in illumos, we had an IP-centric view of the serialization queue
+ * abstraction. While that has useful properties, the implementation of squeues
+ * hardcodes various parts of the implementation of IP into it which makes it
+ * unsuitable for other consumers. To enable them, we created another interface,
+ * but opted not to port all of the functionality that IP uses in the form of
+ * ip_squeue.c As other consumers need the functionality that IP has in squeues,
+ * then we'll come up with more genericized methods and add that functionality
+ * to <sys/gsqueue.h>. Please do not continue to use this header.
+ */
+
#include <sys/types.h>
#include <sys/processor.h>
#include <sys/stream.h>
@@ -77,12 +88,13 @@ typedef enum {
struct ip_recv_attr_s;
extern void squeue_init(void);
-extern squeue_t *squeue_create(pri_t);
+extern squeue_t *squeue_create(pri_t, boolean_t);
extern void squeue_bind(squeue_t *, processorid_t);
extern void squeue_unbind(squeue_t *);
extern void squeue_enter(squeue_t *, mblk_t *, mblk_t *,
uint32_t, struct ip_recv_attr_s *, int, uint8_t);
extern uintptr_t *squeue_getprivate(squeue_t *, sqprivate_t);
+extern void squeue_destroy(squeue_t *);
struct conn_s;
extern int squeue_synch_enter(struct conn_s *, mblk_t *);
diff --git a/usr/src/uts/common/sys/squeue_impl.h b/usr/src/uts/common/sys/squeue_impl.h
index 8eb6a30add..2bb717fb52 100644
--- a/usr/src/uts/common/sys/squeue_impl.h
+++ b/usr/src/uts/common/sys/squeue_impl.h
@@ -114,6 +114,7 @@ struct squeue_s {
squeue_set_t *sq_set; /* managed by squeue creator */
pri_t sq_priority; /* squeue thread priority */
+ boolean_t sq_isip; /* use IP-centric features */
/* Keep the debug-only fields at the end of the structure */
#ifdef DEBUG
@@ -161,6 +162,7 @@ struct squeue_s {
#define SQS_POLL_RESTART_DONE 0x01000000
#define SQS_POLL_THR_QUIESCE 0x02000000
#define SQS_PAUSE 0x04000000 /* The squeue has been paused */
+#define SQS_EXIT 0x08000000 /* squeue is being torn down */
#define SQS_WORKER_THR_CONTROL \
(SQS_POLL_QUIESCE | SQS_POLL_RESTART | SQS_POLL_CLEANUP)
diff --git a/usr/src/uts/common/sys/stream.h b/usr/src/uts/common/sys/stream.h
index ea2c3d8e9a..7d118b09e8 100644
--- a/usr/src/uts/common/sys/stream.h
+++ b/usr/src/uts/common/sys/stream.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
* Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
* Copyright 2015 Joyent, Inc. All rights reserved.
* Copyright 2022 Garrett D'Amore
diff --git a/usr/src/uts/common/sys/sunddi.h b/usr/src/uts/common/sys/sunddi.h
index 442595289f..c0dedf555c 100644
--- a/usr/src/uts/common/sys/sunddi.h
+++ b/usr/src/uts/common/sys/sunddi.h
@@ -1599,8 +1599,14 @@ int
ddi_ffs(long mask);
int
+ddi_ffsll(long long mask);
+
+int
ddi_fls(long mask);
+int
+ddi_flsll(long long mask);
+
/*
* The ddi_soft_state* routines comprise generic storage management utilities
* for driver soft state structures. Two types of soft_state indexes are
diff --git a/usr/src/uts/common/sys/systrace.h b/usr/src/uts/common/sys/systrace.h
index d43974451e..17e509d4d8 100644
--- a/usr/src/uts/common/sys/systrace.h
+++ b/usr/src/uts/common/sys/systrace.h
@@ -22,13 +22,12 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_SYSTRACE_H
#define _SYS_SYSTRACE_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/dtrace.h>
#ifdef __cplusplus
@@ -47,16 +46,18 @@ extern systrace_sysent_t *systrace_sysent;
extern systrace_sysent_t *systrace_sysent32;
extern void (*systrace_probe)(dtrace_id_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern void systrace_stub(dtrace_id_t, uintptr_t, uintptr_t,
- uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+ uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern int64_t dtrace_systrace_syscall(uintptr_t arg0, uintptr_t arg1,
- uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5);
+ uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5,
+ uintptr_t arg6, uintptr_t arg7);
#ifdef _SYSCALL32_IMPL
extern int64_t dtrace_systrace_syscall32(uintptr_t arg0, uintptr_t arg1,
- uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5);
+ uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5,
+ uintptr_t arg6, uintptr_t arg7);
#endif
#endif
diff --git a/usr/src/uts/common/sys/termios.h b/usr/src/uts/common/sys/termios.h
index 39106a14fc..4edeb7a41c 100644
--- a/usr/src/uts/common/sys/termios.h
+++ b/usr/src/uts/common/sys/termios.h
@@ -363,6 +363,24 @@ extern pid_t tcgetsid(int);
#define TCSETSF (_TIOC|16)
/*
+ * linux terminal ioctls we need to be aware of
+ */
+#define TIOCSETLD (_TIOC|123) /* set line discipline parms */
+#define TIOCGETLD (_TIOC|124) /* get line discipline parms */
+
+/*
+ * The VMIN and VTIME and solaris overlap with VEOF and VEOL - This is
+ * perfectly legal except, linux expects them to be separate. So we keep
+ * them separately.
+ */
+struct lx_cc {
+ unsigned char veof; /* veof value */
+ unsigned char veol; /* veol value */
+ unsigned char vmin; /* vmin value */
+ unsigned char vtime; /* vtime value */
+};
+
+/*
* NTP PPS ioctls
*/
#define TIOCGPPS (_TIOC|125)
diff --git a/usr/src/uts/common/sys/thread.h b/usr/src/uts/common/sys/thread.h
index 53a31c848c..76e6835349 100644
--- a/usr/src/uts/common/sys/thread.h
+++ b/usr/src/uts/common/sys/thread.h
@@ -375,7 +375,7 @@ typedef struct _kthread {
#define T_WOULDBLOCK 0x0020 /* for lockfs */
#define T_DONTBLOCK 0x0040 /* for lockfs */
#define T_DONTPEND 0x0080 /* for lockfs */
-#define T_SYS_PROF 0x0100 /* profiling on for duration of system call */
+#define T_SPLITSTK 0x0100 /* kernel stack is currently split */
#define T_WAITCVSEM 0x0200 /* waiting for a lwp_cv or lwp_sema on sleepq */
#define T_WATCHPT 0x0400 /* thread undergoing a watchpoint emulation */
#define T_PANIC 0x0800 /* thread initiated a system panic */
@@ -427,8 +427,9 @@ typedef struct _kthread {
#define TS_RESUME 0x1000 /* setrun() by CPR resume process */
#define TS_CREATE 0x2000 /* setrun() by syslwp_create() */
#define TS_RUNQMATCH 0x4000 /* exact run queue balancing by setbackdq() */
+#define TS_BSTART 0x8000 /* setrun() by brand */
#define TS_ALLSTART \
- (TS_CSTART|TS_UNPAUSE|TS_XSTART|TS_PSTART|TS_RESUME|TS_CREATE)
+ (TS_CSTART|TS_UNPAUSE|TS_XSTART|TS_PSTART|TS_RESUME|TS_CREATE|TS_BSTART)
#define TS_ANYWAITQ (TS_PROJWAITQ|TS_ZONEWAITQ)
/*
@@ -456,6 +457,10 @@ typedef struct _kthread {
#define ISTOPPED(t) ((t)->t_state == TS_STOPPED && \
!((t)->t_schedflag & TS_PSTART))
+/* True if thread is stopped for a brand-specific reason */
+#define BSTOPPED(t) ((t)->t_state == TS_STOPPED && \
+ !((t)->t_schedflag & TS_BSTART))
+
/* True if thread is asleep and wakeable */
#define ISWAKEABLE(t) (((t)->t_state == TS_SLEEP && \
((t)->t_flag & T_WAKEABLE)))
diff --git a/usr/src/uts/common/sys/time.h b/usr/src/uts/common/sys/time.h
index 634d5fb3a6..d82508e6b3 100644
--- a/usr/src/uts/common/sys/time.h
+++ b/usr/src/uts/common/sys/time.h
@@ -15,6 +15,7 @@
* Use is subject to license terms.
*
* Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*
* Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
*/
@@ -266,6 +267,14 @@ typedef longlong_t hrtime_t;
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
+/*
+ * Unsigned counterpart to hrtime_t
+ */
+typedef u_longlong_t uhrtime_t;
+
+#define HRTIME_MAX LLONG_MAX
+#define UHRTIME_MAX ULLONG_MAX
+
#include <sys/time_impl.h>
#include <sys/mutex.h>
diff --git a/usr/src/uts/common/sys/timer.h b/usr/src/uts/common/sys/timer.h
index 4bbc5b4fb8..db27960413 100644
--- a/usr/src/uts/common/sys/timer.h
+++ b/usr/src/uts/common/sys/timer.h
@@ -35,6 +35,8 @@
#include <sys/proc.h>
#include <sys/thread.h>
#include <sys/param.h>
+#include <sys/siginfo.h>
+#include <sys/port.h>
#ifdef __cplusplus
extern "C" {
@@ -65,6 +67,7 @@ extern int timer_max;
*/
#define IT_SIGNAL 0x01
#define IT_PORT 0x02 /* use event port notification */
+#define IT_CALLBACK 0x04 /* custom callback function */
struct clock_backend;
@@ -92,14 +95,27 @@ struct itimer {
struct clock_backend *it_backend;
void (*it_fire)(itimer_t *);
kmutex_t it_mutex;
- void *it_portev; /* port_kevent_t pointer */
- void *it_portsrc; /* port_source_t pointer */
- int it_portfd; /* port file descriptor */
+ union {
+ struct {
+ void *_it_portev; /* port_kevent_t pointer */
+ void *_it_portsrc; /* port_source_t pointer */
+ int _it_portfd; /* port file descriptor */
+ } _it_ev_port;
+ struct {
+ void (*_it_cb_func)(itimer_t *);
+ uintptr_t _it_cb_data[2];
+ } _it_ev_cb;
+ } _it_ev_data;
};
#define it_sigq __data.__proc.__it_sigq
#define it_lwp __data.__proc.__it_lwp
#define it_frontend __data.__it_frontend
+#define it_portev _it_ev_data._it_ev_port._it_portev
+#define it_portsrc _it_ev_data._it_ev_port._it_portsrc
+#define it_portfd _it_ev_data._it_ev_port._it_portfd
+#define it_cb_func _it_ev_data._it_ev_cb._it_cb_func
+#define it_cb_data _it_ev_data._it_ev_cb._it_cb_data
typedef struct clock_backend {
struct sigevent clk_default;
@@ -116,7 +132,11 @@ typedef struct clock_backend {
extern void clock_add_backend(clockid_t clock, clock_backend_t *backend);
extern clock_backend_t *clock_get_backend(clockid_t clock);
+extern void timer_release(struct proc *, itimer_t *);
+extern void timer_delete_grabbed(struct proc *, timer_t tid, itimer_t *it);
extern void timer_lwpbind();
+extern int timer_setup(clock_backend_t *, struct sigevent *, port_notify_t *,
+ itimer_t **, timer_t *);
extern void timer_func(sigqueue_t *);
extern void timer_exit(void);
diff --git a/usr/src/uts/common/sys/ts.h b/usr/src/uts/common/sys/ts.h
index 7949058565..2cf5dcade3 100644
--- a/usr/src/uts/common/sys/ts.h
+++ b/usr/src/uts/common/sys/ts.h
@@ -79,6 +79,8 @@ typedef struct tsproc {
} tsproc_t;
/* flags */
+
+/* Formerly: TSKPRI 0x01 - thread at kernel mode priority */
#define TSBACKQ 0x02 /* thread goes to back of dispq if preempted */
#define TSIA 0x04 /* thread is interactive */
#define TSIASET 0x08 /* interactive thread is "on" */
diff --git a/usr/src/uts/common/sys/uadmin.h b/usr/src/uts/common/sys/uadmin.h
index 904b52cac4..75d000b831 100644
--- a/usr/src/uts/common/sys/uadmin.h
+++ b/usr/src/uts/common/sys/uadmin.h
@@ -23,6 +23,7 @@
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2011 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -159,7 +160,7 @@ extern kmutex_t ualock;
extern void mdboot(int, int, char *, boolean_t);
extern void mdpreboot(int, int, char *);
extern int kadmin(int, int, void *, cred_t *);
-extern void killall(zoneid_t);
+extern void killall(zoneid_t, boolean_t);
#endif
extern int uadmin(int, int, uintptr_t);
diff --git a/usr/src/uts/common/sys/uio.h b/usr/src/uts/common/sys/uio.h
index bca1ed1fa3..9584be559f 100644
--- a/usr/src/uts/common/sys/uio.h
+++ b/usr/src/uts/common/sys/uio.h
@@ -145,7 +145,8 @@ typedef struct uioa_s {
*/
typedef enum xuio_type {
UIOTYPE_ASYNCIO,
- UIOTYPE_ZEROCOPY
+ UIOTYPE_ZEROCOPY,
+ UIOTYPE_PEEKSIZE
} xuio_type_t;
typedef struct xuio {
@@ -175,6 +176,15 @@ typedef struct xuio {
int xu_zc_rw; /* read or write buffer */
void *xu_zc_priv; /* fs specific */
} xu_zc;
+
+ /*
+ * Peek Size Support -- facilitate peeking at the size of a
+ * waiting message on a socket.
+ */
+ struct {
+ ssize_t xu_ps_size; /* size of waiting msg */
+ boolean_t xu_ps_set; /* was size calculated? */
+ } xu_ps;
} xu_ext;
} xuio_t;
diff --git a/usr/src/uts/common/sys/user.h b/usr/src/uts/common/sys/user.h
index 7f54dcf3ab..90fde4ef98 100644
--- a/usr/src/uts/common/sys/user.h
+++ b/usr/src/uts/common/sys/user.h
@@ -26,7 +26,7 @@
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
* Copyright 2022 Oxide Computer Company
*/
@@ -203,9 +203,9 @@ typedef struct { /* kernel syscall set type */
* This value should not be changed in a patch.
*/
#if defined(__sparc)
-#define __KERN_NAUXV_IMPL 20
+#define __KERN_NAUXV_IMPL 24
#elif defined(__i386) || defined(__amd64)
-#define __KERN_NAUXV_IMPL 26
+#define __KERN_NAUXV_IMPL 29
#endif
struct execsw;
@@ -228,7 +228,11 @@ typedef struct user {
char u_psargs[PSARGSZ]; /* arguments from exec */
int u_argc; /* value of argc passed to main() */
uintptr_t u_argv; /* value of argv passed to main() */
+ uintptr_t u_argvstrs; /* argv string space pointer */
+ size_t u_argvstrsize; /* size of argv string space */
uintptr_t u_envp; /* value of envp passed to main() */
+ uintptr_t u_envstrs; /* env string space pointer */
+ size_t u_envstrsize; /* size of env string space */
uintptr_t u_commpagep; /* address of mapped comm page */
/*
diff --git a/usr/src/uts/common/sys/vm.h b/usr/src/uts/common/sys/vm.h
index 14b5754b28..b32a789d36 100644
--- a/usr/src/uts/common/sys/vm.h
+++ b/usr/src/uts/common/sys/vm.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1983, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2017 Joyent, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -57,6 +58,8 @@ int queue_io_request(struct vnode *, u_offset_t);
extern kmutex_t memavail_lock;
extern kcondvar_t memavail_cv;
+#define WAKE_PAGEOUT_SCANNER() cv_broadcast(&proc_pageout->p_cv)
+
#endif /* defined(_KERNEL) */
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/vm_usage.h b/usr/src/uts/common/sys/vm_usage.h
index 1aa4a8ee6d..afbf438eff 100644
--- a/usr/src/uts/common/sys/vm_usage.h
+++ b/usr/src/uts/common/sys/vm_usage.h
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc. All rights reserved.
*/
#ifndef _SYS_VM_USAGE_H
@@ -79,8 +80,12 @@ extern "C" {
/* zoneid */
#define VMUSAGE_COL_EUSERS 0x2000 /* same as VMUSAGE_COL_RUSERS, but by */
/* euser */
+#define VMUSAGE_A_ZONE 0x4000 /* rss/swap for a specified zone */
-#define VMUSAGE_MASK 0x3fff /* all valid flags for getvmusage() */
+#define VMUSAGE_MASK 0x7fff /* all valid flags for getvmusage() */
+
+#define VMUSAGE_ZONE_FLAGS (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | \
+ VMUSAGE_A_ZONE)
typedef struct vmusage {
id_t vmu_zoneid; /* zoneid, or ALL_ZONES for */
diff --git a/usr/src/uts/common/sys/vmsystm.h b/usr/src/uts/common/sys/vmsystm.h
index e8e30b7608..daf76f9f51 100644
--- a/usr/src/uts/common/sys/vmsystm.h
+++ b/usr/src/uts/common/sys/vmsystm.h
@@ -19,6 +19,9 @@
* CDDL HEADER END
*/
/*
+ * Copyright (c) 2017, Joyent, Inc. All rights reserved.
+ */
+/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -58,6 +61,9 @@ extern pgcnt_t desscan; /* desired pages scanned per second */
extern pgcnt_t slowscan;
extern pgcnt_t fastscan;
extern pgcnt_t pushes; /* number of pages pushed to swap device */
+extern uint64_t low_mem_scan; /* num times page scan due to low memory */
+extern uint64_t zone_cap_scan; /* num times page scan due to zone cap */
+extern uint64_t n_throttle; /* num times page create throttled */
/* writable copies of tunables */
extern pgcnt_t maxpgio; /* max paging i/o per sec before start swaps */
@@ -160,6 +166,8 @@ extern void *boot_virt_alloc(void *addr, size_t size);
extern size_t exec_get_spslew(void);
+extern caddr_t map_userlimit(proc_t *pp, struct as *as, int flags);
+
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/vnd.h b/usr/src/uts/common/sys/vnd.h
new file mode 100644
index 0000000000..bc7c9c3122
--- /dev/null
+++ b/usr/src/uts/common/sys/vnd.h
@@ -0,0 +1,141 @@
+/*
+ * 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 _SYS_VND_H
+#define _SYS_VND_H
+
+#include <sys/types.h>
+#include <sys/vnd_errno.h>
+#include <sys/frameio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * We distinguish between normal ioctls and private ioctls we issues to out
+ * streams version. Streams ioctls have the upper bit set in the lowest byte.
+ * Note that there are no STREAMs ioctls for userland and all definitions
+ * related to them are not present in this file.
+ */
+#define VND_IOC (('v' << 24) | ('n' << 16) | ('d' << 8))
+
+/*
+ * Attach the current minor instance to a given dlpi datalink identified by a
+ * vnd_ioc_name_t argument. This fails if it's already been attached. Note that
+ * unlike the other ioctls, this is passed directly as opposed to every other
+ * function which is passed as a pointer to the value.
+ */
+#define VND_IOC_ATTACH (VND_IOC | 0x1)
+
+#define VND_NAMELEN 32
+
+typedef struct vnd_ioc_attach {
+ char via_name[VND_NAMELEN];
+ zoneid_t via_zoneid;
+ uint32_t via_errno;
+} vnd_ioc_attach_t;
+
+/*
+ * Link the current minor instance into the /devices name space.
+ *
+ * This ioctl adds entries into /devices with a name of the form z%d:%s vil_zid,
+ * vil_name. The device will be namespaced to the zone. The global zone will be
+ * able to see all minor nodes. In the zone, only the /dev entries will exist.
+ * At this time, a given device can only have one link at a time. Note that a
+ * user cannot specify the zone to pass in, rather it is the zone that the
+ * device was attached in.
+ */
+#define VND_IOC_LINK (VND_IOC | 0x2)
+
+typedef struct vnd_ioc_link {
+ char vil_name[VND_NAMELEN];
+ uint32_t vil_errno;
+} vnd_ioc_link_t;
+
+/*
+ * Unlink the opened minor instance from the /devices name space. A zone may use
+ * this to unlink an extent entry in /dev; however, they will not be able to
+ * link it in again.
+ */
+#define VND_IOC_UNLINK (VND_IOC | 0x3)
+typedef struct vnd_ioc_unlink {
+ uint32_t viu_errno;
+} vnd_ioc_unlink_t;
+
+/*
+ * Controls to get and set the current buffer recieve buffer size.
+ */
+typedef struct vnd_ioc_buf {
+ uint64_t vib_size;
+ uint32_t vib_filler;
+ uint32_t vib_errno;
+} vnd_ioc_buf_t;
+
+#define VND_IOC_GETRXBUF (VND_IOC | 0x04)
+#define VND_IOC_SETRXBUF (VND_IOC | 0x05)
+#define VND_IOC_GETMAXBUF (VND_IOC | 0x06)
+#define VND_IOC_GETTXBUF (VND_IOC | 0x07)
+#define VND_IOC_SETTXBUF (VND_IOC | 0x08)
+#define VND_IOC_GETMINTU (VND_IOC | 0x09)
+#define VND_IOC_GETMAXTU (VND_IOC | 0x0a)
+
+/*
+ * Information and listing ioctls
+ *
+ * This gets information about all of the active vnd instances. vl_actents is
+ * always updated to the number around and vl_nents is the number of
+ * vnd_ioc_info_t elements are allocated in vl_ents.
+ */
+typedef struct vnd_ioc_info {
+ uint32_t vii_version;
+ zoneid_t vii_zone;
+ char vii_name[VND_NAMELEN];
+ char vii_datalink[VND_NAMELEN];
+} vnd_ioc_info_t;
+
+typedef struct vnd_ioc_list {
+ uint_t vl_nents;
+ uint_t vl_actents;
+ vnd_ioc_info_t *vl_ents;
+} vnd_ioc_list_t;
+
+#ifdef _KERNEL
+
+typedef struct vnd_ioc_list32 {
+ uint_t vl_nents;
+ uint_t vl_actents;
+ caddr32_t vl_ents;
+} vnd_ioc_list32_t;
+
+#endif /* _KERNEL */
+
+#define VND_IOC_LIST (VND_IOC | 0x20)
+
+/*
+ * Framed I/O ioctls
+ *
+ * Users should use the standard frameio_t as opposed to a vnd specific type.
+ * This is a consolidation private ioctl pending futher stability in the form of
+ * specific system work.
+ */
+#define VND_IOC_FRAMEIO_READ (VND_IOC | 0x30)
+#define VND_IOC_FRAMEIO_WRITE (VND_IOC | 0x31)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_VND_H */
diff --git a/usr/src/uts/common/sys/vnd_errno.h b/usr/src/uts/common/sys/vnd_errno.h
new file mode 100644
index 0000000000..89e5fc2543
--- /dev/null
+++ b/usr/src/uts/common/sys/vnd_errno.h
@@ -0,0 +1,72 @@
+/*
+ * 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 _SYS_VND_ERRNO_H
+#define _SYS_VND_ERRNO_H
+
+/*
+ * This header contains all of the available vnd errors.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum vnd_errno {
+ VND_E_SUCCESS = 0, /* no error */
+ VND_E_NOMEM, /* no memory */
+ VND_E_NODATALINK, /* no such datalink */
+ VND_E_NOTETHER, /* not DL_ETHER */
+ VND_E_DLPIINVAL, /* Unknown DLPI failures */
+ VND_E_ATTACHFAIL, /* DL_ATTACH_REQ failed */
+ VND_E_BINDFAIL, /* DL_BIND_REQ failed */
+ VND_E_PROMISCFAIL, /* DL_PROMISCON_REQ failed */
+ VND_E_DIRECTFAIL, /* DLD_CAPAB_DIRECT enable failed */
+ VND_E_CAPACKINVAL, /* bad dl_capability_ack_t */
+ VND_E_SUBCAPINVAL, /* bad dl_capability_sub_t */
+ VND_E_DLDBADVERS, /* bad dld version */
+ VND_E_KSTATCREATE, /* failed to create kstats */
+ VND_E_NODEV, /* no such vnd link */
+ VND_E_NONETSTACK, /* netstack doesn't exist */
+ VND_E_ASSOCIATED, /* device already associated */
+ VND_E_ATTACHED, /* device already attached */
+ VND_E_LINKED, /* device already linked */
+ VND_E_BADNAME, /* invalid name */
+ VND_E_PERM, /* can't touch this */
+ VND_E_NOZONE, /* no such zone */
+ VND_E_STRINIT, /* failed to initialize vnd stream module */
+ VND_E_NOTATTACHED, /* device not attached */
+ VND_E_NOTLINKED, /* device not linked */
+ VND_E_LINKEXISTS, /* another device has the same link name */
+ VND_E_MINORNODE, /* failed to create minor node */
+ VND_E_BUFTOOBIG, /* requested buffer size is too large */
+ VND_E_BUFTOOSMALL, /* requested buffer size is too small */
+ VND_E_DLEXCL, /* unable to get dlpi excl access */
+ VND_E_DIRECTNOTSUP,
+ /* DLD direct capability not suported over data link */
+ VND_E_BADPROPSIZE, /* invalid property size */
+ VND_E_BADPROP, /* invalid property */
+ VND_E_PROPRDONLY, /* property is read only */
+ VND_E_SYS, /* unexpected system error */
+ VND_E_CAPABPASS,
+ /* capabilities invalid, pass-through module detected */
+ VND_E_UNKNOWN /* unknown error */
+} vnd_errno_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_VND_ERRNO_H */
diff --git a/usr/src/uts/common/sys/vnode.h b/usr/src/uts/common/sys/vnode.h
index b8702bc8f5..df5da6c2e7 100644
--- a/usr/src/uts/common/sys/vnode.h
+++ b/usr/src/uts/common/sys/vnode.h
@@ -21,7 +21,7 @@
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
* Copyright (c) 2011, 2017 by Delphix. All rights reserved.
* Copyright 2017 RackTop Systems.
*/
@@ -197,6 +197,7 @@ struct vsd_node {
* v_count
* v_shrlocks
* v_path
+ * v_phantom_count
* v_vsd
* v_xattrdir
*
@@ -214,6 +215,7 @@ struct vsd_node {
* v_lock
* v_flag
* v_count
+ * v_phantom_count
* v_data
* v_vfsp
* v_stream
@@ -285,6 +287,8 @@ typedef struct vnode {
kmutex_t v_lock; /* protects vnode fields */
uint_t v_flag; /* vnode flags (see below) */
uint_t v_count; /* reference count */
+ /* non vn_count() ref count (see below) */
+ uint_t v_phantom_count;
void *v_data; /* private data for fs */
struct vfs *v_vfsp; /* ptr to containing VFS */
struct stdata *v_stream; /* associated stream */
@@ -811,13 +815,15 @@ typedef enum vnevent {
VE_REMOVE = 3, /* Remove of vnode's name */
VE_RMDIR = 4, /* Remove of directory vnode's name */
VE_CREATE = 5, /* Create with vnode's name which exists */
- VE_LINK = 6, /* Link with vnode's name as source */
- VE_RENAME_DEST_DIR = 7, /* Rename with vnode as target dir */
- VE_MOUNTEDOVER = 8, /* File or Filesystem got mounted over vnode */
+ VE_LINK = 6, /* Link with vnode's name as source */
+ VE_RENAME_DEST_DIR = 7, /* Rename with vnode as target dir */
+ VE_MOUNTEDOVER = 8, /* File or Filesystem got mounted over vnode */
VE_TRUNCATE = 9, /* Truncate */
VE_PRE_RENAME_SRC = 10, /* Pre-rename, with vnode as source */
VE_PRE_RENAME_DEST = 11, /* Pre-rename, with vnode as target/dest. */
- VE_PRE_RENAME_DEST_DIR = 12 /* Pre-rename with vnode as target dir */
+ VE_PRE_RENAME_DEST_DIR = 12, /* Pre-rename with vnode as target dir */
+ VE_RENAME_SRC_DIR = 13, /* Rename with vnode as source dir */
+ VE_RESIZE = 14 /* Resize/truncate to non-zero offset */
} vnevent_t;
/*
@@ -1292,9 +1298,9 @@ void vn_recycle(vnode_t *);
void vn_free(vnode_t *);
int vn_is_readonly(vnode_t *);
-int vn_is_opened(vnode_t *, v_mode_t);
-int vn_is_mapped(vnode_t *, v_mode_t);
-int vn_has_other_opens(vnode_t *, v_mode_t);
+int vn_is_opened(vnode_t *, v_mode_t);
+int vn_is_mapped(vnode_t *, v_mode_t);
+int vn_has_other_opens(vnode_t *, v_mode_t);
void vn_open_upgrade(vnode_t *, int);
void vn_open_downgrade(vnode_t *, int);
@@ -1333,10 +1339,12 @@ int vn_createat(char *pnamep, enum uio_seg seg, struct vattr *vap,
int vn_rdwr(enum uio_rw rw, struct vnode *vp, caddr_t base, ssize_t len,
offset_t offset, enum uio_seg seg, int ioflag, rlim64_t ulimit,
cred_t *cr, ssize_t *residp);
+uint_t vn_count(struct vnode *vp);
void vn_rele(struct vnode *vp);
void vn_rele_async(struct vnode *vp, struct taskq *taskq);
void vn_rele_dnlc(struct vnode *vp);
void vn_rele_stream(struct vnode *vp);
+void vn_phantom_rele(struct vnode *vp);
int vn_link(char *from, char *to, enum uio_seg seg);
int vn_linkat(vnode_t *fstartvp, char *from, enum symfollow follow,
vnode_t *tstartvp, char *to, enum uio_seg seg);
@@ -1377,7 +1385,8 @@ void vnevent_remove(vnode_t *, vnode_t *, char *, caller_context_t *);
void vnevent_rmdir(vnode_t *, vnode_t *, char *, caller_context_t *);
void vnevent_create(vnode_t *, caller_context_t *);
void vnevent_link(vnode_t *, caller_context_t *);
-void vnevent_rename_dest_dir(vnode_t *, caller_context_t *ct);
+void vnevent_rename_dest_dir(vnode_t *, vnode_t *, char *,
+ caller_context_t *ct);
void vnevent_mountedover(vnode_t *, caller_context_t *);
void vnevent_truncate(vnode_t *, caller_context_t *);
int vnevent_support(vnode_t *, caller_context_t *);
@@ -1387,6 +1396,7 @@ void vnevent_pre_rename_dest(vnode_t *, vnode_t *, char *,
caller_context_t *);
void vnevent_pre_rename_dest_dir(vnode_t *, vnode_t *, char *,
caller_context_t *);
+void vnevent_resize(vnode_t *, caller_context_t *);
/* Vnode specific data */
void vsd_create(uint_t *, void (*)(void *));
@@ -1439,6 +1449,16 @@ extern uint_t pvn_vmodsort_supported;
* this->vp->v_path == NULL ? "NULL" : stringof(this->vp->v_path),
* this->vp->v_count)
* }'
+ *
+ * There are some situations where we don't want a hold to make the vnode
+ * 'busy'. For example, watching a directory via port events or inotify
+ * should not prevent a filesystem from mounting on a watched directory.
+ * For those instances, a phantom hold is used via VN_PHANTOM_HOLD().
+ *
+ * A phantom hold works identically to regular hold, except that those holds
+ * are excluded from the return value of vn_count().
+ *
+ * A phantom hold must be released by VN_PHANTOM_RELE().
*/
#define VN_HOLD_LOCKED(vp) { \
ASSERT(mutex_owned(&(vp)->v_lock)); \
@@ -1467,6 +1487,22 @@ extern uint_t pvn_vmodsort_supported;
DTRACE_PROBE1(vn__rele, vnode_t *, vp); \
}
+#define VN_PHANTOM_HOLD_LOCKED(vp) { \
+ VN_HOLD_LOCKED(vp); \
+ (vp)->v_phantom_count++; \
+ DTRACE_PROBE1(vn__phantom_hold, vnode_t *, vp); \
+}
+
+#define VN_PHANTOM_HOLD(vp) { \
+ mutex_enter(&(vp)->v_lock); \
+ VN_PHANTOM_HOLD_LOCKED(vp); \
+ mutex_exit(&(vp)->v_lock); \
+}
+
+#define VN_PHANTOM_RELE(vp) { \
+ vn_phantom_rele(vp); \
+}
+
#define VN_SET_VFS_TYPE_DEV(vp, vfsp, type, dev) { \
(vp)->v_vfsp = (vfsp); \
(vp)->v_type = (type); \
@@ -1477,7 +1513,7 @@ extern uint_t pvn_vmodsort_supported;
* Compare two vnodes for equality. In general this macro should be used
* in preference to calling VOP_CMP directly.
*/
-#define VN_CMP(VP1, VP2) ((VP1) == (VP2) ? 1 : \
+#define VN_CMP(VP1, VP2) ((VP1) == (VP2) ? 1 : \
((VP1) && (VP2) && (vn_getops(VP1) == vn_getops(VP2)) ? \
VOP_CMP(VP1, VP2, NULL) : 0))
diff --git a/usr/src/uts/common/sys/zfd.h b/usr/src/uts/common/sys/zfd.h
new file mode 100644
index 0000000000..e08d75ecba
--- /dev/null
+++ b/usr/src/uts/common/sys/zfd.h
@@ -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 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_ZFD_H
+#define _SYS_ZFD_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Minor node name of the global zone side (often called the "master" side)
+ * of the zfd dev.
+ */
+#define ZFD_MASTER_NAME "master"
+
+/*
+ * Minor node name of the non-global zone side (often called the "slave"
+ * side) of the zfd dev.
+ */
+#define ZFD_SLAVE_NAME "slave"
+
+#define ZFD_NAME_LEN 16
+
+/*
+ * ZFD_IOC forms the base for all zfd ioctls.
+ */
+#define ZFD_IOC (('Z' << 24) | ('f' << 16) | ('d' << 8))
+
+/*
+ * This ioctl tells the slave side it should push the TTY stream modules
+ * so that the fd looks like a tty.
+ */
+#define ZFD_MAKETTY (ZFD_IOC | 0)
+
+/*
+ * This ioctl puts a hangup into the stream so that the slave side sees EOF.
+ */
+#define ZFD_EOF (ZFD_IOC | 1)
+
+/*
+ * This ioctl succeeds if the slave side is open.
+ */
+#define ZFD_HAS_SLAVE (ZFD_IOC | 2)
+
+/*
+ * This ioctl links two streams into a multiplexer configuration for in-zone
+ * logging.
+ */
+#define ZFD_MUX (ZFD_IOC | 3)
+
+/*
+ * This ioctl controls the flow control setting for the log multiplexer stream
+ * (1 = true, 0 = false). The default is false which implies teeing into the
+ * log stream is "best-effort" but data will be discarded if the stream
+ * becomes full. If set and the log stream begins to fill up, the primary
+ * stream will stop flowing.
+ */
+#define ZFD_MUX_FLOWCON (ZFD_IOC | 4)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_ZFD_H */
diff --git a/usr/src/uts/common/sys/zone.h b/usr/src/uts/common/sys/zone.h
index 26b74ca34a..afef75013f 100644
--- a/usr/src/uts/common/sys/zone.h
+++ b/usr/src/uts/common/sys/zone.h
@@ -50,8 +50,10 @@
#include <sys/socket_impl.h>
#include <sys/secflags.h>
#include <sys/cpu_uarray.h>
+#include <sys/nvpair.h>
#include <sys/list.h>
#include <sys/loadavg.h>
+#include <sys/vnode.h>
#endif /* _KERNEL */
#ifdef __cplusplus
@@ -62,15 +64,27 @@ extern "C" {
* NOTE
*
* The contents of this file are private to the implementation of
- * Solaris and are subject to change at any time without notice.
+ * illumos and are subject to change at any time without notice.
* Applications and drivers using these interfaces may fail to
* run on future releases.
*/
/* Available both in kernel and for user space */
-/* zone id restrictions and special ids */
-#define MAX_ZONEID 9999
+/*
+ * zone id restrictions and special ids.
+ * See 'maxzones' for run-time zone limit.
+ *
+ * The current 8k value for MAX_ZONES was originally derived from the virtual
+ * interface limit in IP when "shared-stack" was the only supported networking
+ * for zones. The virtual interface limit is the number of addresses allowed
+ * on an interface (see MAX_ADDRS_PER_IF). Even with exclusive stacks, an 8k
+ * zone limit is still a reasonable choice at this time, given other limits
+ * within the kernel. Since we only support 8192 zones (which includes GZ),
+ * there is no point in allowing MAX_ZONEID > 8k.
+ */
+#define MAX_ZONES 8192
+#define MAX_ZONEID (MAX_ZONES - 1)
#define MIN_USERZONEID 1 /* lowest user-creatable zone ID */
#define MIN_ZONEID 0 /* minimum zone ID on system */
#define GLOBAL_ZONEID 0
@@ -97,7 +111,13 @@ extern "C" {
#define ZONE_CHECK_DATALINK 12
#define ZONE_LIST_DATALINK 13
-/* zone attributes */
+/*
+ * zone attributes
+ *
+ * Note that values up to ZONE_ATTR_HOSTID are baked into things like Solaris
+ * 10 which can be run under the s10 brand; don't renumber or change them. Ones
+ * which are no longer used are commented out.
+ */
#define ZONE_ATTR_ROOT 1
#define ZONE_ATTR_NAME 2
#define ZONE_ATTR_STATUS 3
@@ -109,17 +129,24 @@ extern "C" {
#define ZONE_ATTR_INITNAME 9
#define ZONE_ATTR_BOOTARGS 10
#define ZONE_ATTR_BRAND 11
-#define ZONE_ATTR_PHYS_MCAP 12
+/* #define ZONE_ATTR_PHYS_MCAP 12 */
#define ZONE_ATTR_SCHED_CLASS 13
#define ZONE_ATTR_FLAGS 14
#define ZONE_ATTR_HOSTID 15
#define ZONE_ATTR_FS_ALLOWED 16
#define ZONE_ATTR_NETWORK 17
+
+/* illumos extensions */
#define ZONE_ATTR_INITNORESTART 20
#define ZONE_ATTR_SECFLAGS 21
#define ZONE_ATTR_INITRESTART0 22
#define ZONE_ATTR_INITREBOOT 23
+/* OmniOS/SmartOS extensions */
+#define ZONE_ATTR_DID 30
+#define ZONE_ATTR_APP_SVC_CT 31
+#define ZONE_ATTR_SCHED_FIXEDHI 32
+
/* Start of the brand-specific attribute namespace */
#define ZONE_ATTR_BRAND_ATTRS 32768
@@ -134,13 +161,18 @@ extern "C" {
#define ZONE_EVENT_READY "ready"
#define ZONE_EVENT_RUNNING "running"
#define ZONE_EVENT_SHUTTING_DOWN "shutting_down"
+#define ZONE_EVENT_FREE "free"
#define ZONE_CB_NAME "zonename"
#define ZONE_CB_NEWSTATE "newstate"
#define ZONE_CB_OLDSTATE "oldstate"
+#define ZONE_CB_RESTARTS "restarts"
#define ZONE_CB_TIMESTAMP "when"
#define ZONE_CB_ZONEID "zoneid"
+#define ZONE_EVENT_INIT_CLASS "init"
+#define ZONE_EVENT_INIT_RESTART_SC "restart"
+
/*
* Exit values that may be returned by scripts or programs invoked by various
* zone commands.
@@ -199,6 +231,7 @@ typedef struct {
uint32_t doi; /* DOI for label */
caddr32_t label; /* label associated with zone */
int flags;
+ zoneid_t zoneid; /* requested zoneid */
} zone_def32;
#endif
typedef struct {
@@ -215,6 +248,7 @@ typedef struct {
uint32_t doi; /* DOI for label */
const bslabel_t *label; /* label associated with zone */
int flags;
+ zoneid_t zoneid; /* requested zoneid */
} zone_def;
/* extended error information */
@@ -239,7 +273,8 @@ typedef enum {
ZONE_IS_EMPTY,
ZONE_IS_DOWN,
ZONE_IS_DYING,
- ZONE_IS_DEAD
+ ZONE_IS_DEAD,
+ ZONE_IS_FREE /* transient state for zone sysevent */
} zone_status_t;
#define ZONE_MIN_STATE ZONE_IS_UNINITIALIZED
#define ZONE_MAX_STATE ZONE_IS_DEAD
@@ -259,9 +294,12 @@ typedef enum zone_cmd {
typedef struct zone_cmd_arg {
uint64_t uniqid; /* unique "generation number" */
zone_cmd_t cmd; /* requested action */
- uint32_t _pad; /* need consistent 32/64 bit alignmt */
+ int status; /* init status on shutdown */
+ uint32_t debug; /* enable brand hook debug */
char locale[MAXPATHLEN]; /* locale in which to render messages */
char bootbuf[BOOTARGS_MAX]; /* arguments passed to zone_boot() */
+ /* Needed for 32/64 zoneadm -> zoneadmd door arg size check. */
+ int pad;
} zone_cmd_arg_t;
/*
@@ -389,7 +427,7 @@ typedef struct zone_dataset {
} zone_dataset_t;
/*
- * structure for zone kstats
+ * structure for rctl zone kstats
*/
typedef struct zone_kstat {
kstat_named_t zk_zonename;
@@ -400,12 +438,57 @@ typedef struct zone_kstat {
struct cpucap;
typedef struct {
+ hrtime_t cycle_start;
+ uint_t cycle_cnt;
+ hrtime_t zone_avg_cnt;
+} sys_zio_cntr_t;
+
+typedef struct {
+ kstat_named_t zv_zonename;
+ kstat_named_t zv_nread;
+ kstat_named_t zv_reads;
+ kstat_named_t zv_rtime;
+ kstat_named_t zv_rlentime;
+ kstat_named_t zv_rcnt;
+ kstat_named_t zv_nwritten;
+ kstat_named_t zv_writes;
+ kstat_named_t zv_wtime;
+ kstat_named_t zv_wlentime;
+ kstat_named_t zv_wcnt;
+ kstat_named_t zv_10ms_ops;
+ kstat_named_t zv_100ms_ops;
+ kstat_named_t zv_1s_ops;
+ kstat_named_t zv_10s_ops;
+ kstat_named_t zv_delay_cnt;
+ kstat_named_t zv_delay_time;
+} zone_vfs_kstat_t;
+
+typedef struct {
+ kstat_named_t zz_zonename;
+ kstat_named_t zz_nread;
+ kstat_named_t zz_reads;
+ kstat_named_t zz_rtime;
+ kstat_named_t zz_rlentime;
+ kstat_named_t zz_nwritten;
+ kstat_named_t zz_writes;
+ kstat_named_t zz_waittime;
+} zone_zfs_kstat_t;
+
+typedef struct {
kstat_named_t zm_zonename;
+ kstat_named_t zm_rss;
+ kstat_named_t zm_phys_cap;
+ kstat_named_t zm_swap;
+ kstat_named_t zm_swap_cap;
+ kstat_named_t zm_nover;
+ kstat_named_t zm_pagedout;
kstat_named_t zm_pgpgin;
kstat_named_t zm_anonpgin;
kstat_named_t zm_execpgin;
kstat_named_t zm_fspgin;
kstat_named_t zm_anon_alloc_fail;
+ kstat_named_t zm_pf_throttle;
+ kstat_named_t zm_pf_throttle_usec;
} zone_mcap_kstat_t;
typedef struct {
@@ -420,8 +503,10 @@ typedef struct {
kstat_named_t zm_ffnoproc;
kstat_named_t zm_ffnomem;
kstat_named_t zm_ffmisc;
+ kstat_named_t zm_mfseglim;
kstat_named_t zm_nested_intp;
kstat_named_t zm_init_pid;
+ kstat_named_t zm_init_restarts;
kstat_named_t zm_boot_time;
} zone_misc_kstat_t;
@@ -464,6 +549,7 @@ typedef struct zone {
*/
list_node_t zone_linkage;
zoneid_t zone_id; /* ID of zone */
+ zoneid_t zone_did; /* persistent debug ID of zone */
uint_t zone_ref; /* count of zone_hold()s on zone */
uint_t zone_cred_ref; /* count of zone_hold_cred()s on zone */
/*
@@ -516,10 +602,11 @@ typedef struct zone {
kcondvar_t zone_cv; /* used to signal state changes */
struct proc *zone_zsched; /* Dummy kernel "zsched" process */
pid_t zone_proc_initpid; /* pid of "init" for this zone */
- char *zone_initname; /* fs path to 'init' */
+ uint_t zone_proc_init_restarts; /* times init restarted */
+ char *zone_initname; /* fs path to 'init' */
+ int zone_init_status; /* init's exit status */
int zone_boot_err; /* for zone_boot() if boot fails */
char *zone_bootargs; /* arguments passed via zone_boot() */
- uint64_t zone_phys_mcap; /* physical memory cap */
/*
* zone_kthreads is protected by zone_status_lock.
*/
@@ -559,9 +646,11 @@ typedef struct zone {
boolean_t zone_restart_init; /* Restart init if it dies? */
boolean_t zone_reboot_on_init_exit; /* Reboot if init dies? */
boolean_t zone_restart_init_0; /* Restart only if it exits 0 */
+ boolean_t zone_setup_app_contract; /* setup contract? */
struct brand *zone_brand; /* zone's brand */
void *zone_brand_data; /* store brand specific data */
id_t zone_defaultcid; /* dflt scheduling class id */
+ boolean_t zone_fixed_hipri; /* fixed sched. hi prio */
kstat_t *zone_swapresv_kstat;
kstat_t *zone_lockedmem_kstat;
/*
@@ -570,8 +659,24 @@ typedef struct zone {
list_t zone_dl_list;
netstack_t *zone_netstack;
struct cpucap *zone_cpucap; /* CPU caps data */
+
/*
- * Solaris Auditing per-zone audit context
+ * kstats and counters for VFS ops and bytes.
+ */
+ kmutex_t zone_vfs_lock; /* protects VFS statistics */
+ kstat_t *zone_vfs_ksp;
+ kstat_io_t zone_vfs_rwstats;
+ zone_vfs_kstat_t *zone_vfs_stats;
+
+ /*
+ * kstats for ZFS I/O ops and bytes.
+ */
+ kmutex_t zone_zfs_lock; /* protects ZFS statistics */
+ kstat_t *zone_zfs_ksp;
+ zone_zfs_kstat_t *zone_zfs_stats;
+
+ /*
+ * illumos Auditing per-zone audit context
*/
struct au_kcontext *zone_audit_kctxt;
/*
@@ -588,7 +693,11 @@ typedef struct zone {
/* zone_rctls->rcs_lock */
kstat_t *zone_nprocs_kstat;
- kmutex_t zone_mcap_lock; /* protects mcap statistics */
+ /*
+ * kstats and counters for physical memory capping.
+ */
+ kstat_t *zone_physmem_kstat;
+ kmutex_t zone_mcap_lock; /* protects mcap statistics */
kstat_t *zone_mcap_ksp;
zone_mcap_kstat_t *zone_mcap_stats;
uint64_t zone_pgpgin; /* pages paged in */
@@ -613,6 +722,8 @@ typedef struct zone {
uint32_t zone_ffnomem; /* as_dup/memory error */
uint32_t zone_ffmisc; /* misc. other error */
+ uint32_t zone_mfseglim; /* map failure (# segs limit) */
+
uint32_t zone_nested_intp; /* nested interp. kstat */
struct loadavg_s zone_loadavg; /* loadavg for this zone */
@@ -640,6 +751,53 @@ typedef struct zone {
} zone_t;
/*
+ * Data and counters used for ZFS fair-share disk IO.
+ */
+typedef struct zone_zfs_io {
+ uint16_t zpers_zfs_io_pri; /* ZFS IO priority - 16k max */
+ uint_t zpers_zfs_queued[2]; /* sync I/O enqueued count */
+ sys_zio_cntr_t zpers_rd_ops; /* Counters for ZFS reads, */
+ sys_zio_cntr_t zpers_wr_ops; /* writes, and */
+ sys_zio_cntr_t zpers_lwr_ops; /* logical writes. */
+ kstat_io_t zpers_zfs_rwstats;
+ uint64_t zpers_io_util; /* IO utilization metric */
+ uint64_t zpers_zfs_rd_waittime;
+ uint8_t zpers_io_delay; /* IO delay on logical r/w */
+ uint8_t zpers_zfs_weight; /* used to prevent starvation */
+ uint8_t zpers_io_util_above_avg; /* IO util percent > avg. */
+} zone_zfs_io_t;
+
+/*
+ * "Persistent" zone data which can be accessed idependently of the zone_t.
+ */
+typedef struct zone_persist {
+ kmutex_t zpers_zfs_lock; /* Protects zpers_zfsp references */
+ zone_zfs_io_t *zpers_zfsp; /* ZFS fair-share IO data */
+ uint8_t zpers_over; /* currently over cap */
+ uint32_t zpers_pg_cnt; /* current RSS in pages */
+ uint32_t zpers_pg_limit; /* current RRS limit in pages */
+ uint32_t zpers_nover; /* # of times over phys. cap */
+#ifndef DEBUG
+ uint64_t zpers_pg_out; /* # pages flushed */
+#else
+ /*
+ * To conserve memory, some detailed kstats are only kept for DEBUG
+ * builds.
+ */
+ uint64_t zpers_zfs_rd_waittime;
+
+ uint64_t zpers_pg_anon; /* # clean anon pages flushed */
+ uint64_t zpers_pg_anondirty; /* # dirty anon pages flushed */
+ uint64_t zpers_pg_fs; /* # clean fs pages flushed */
+ uint64_t zpers_pg_fsdirty; /* # dirty fs pages flushed */
+#endif
+} zone_persist_t;
+
+typedef enum zone_pageout_op {
+ ZPO_DIRTY, ZPO_FS, ZPO_ANON, ZPO_ANONDIRTY
+} zone_pageout_op_t;
+
+/*
* Special value of zone_psetid to indicate that pools are disabled.
*/
#define ZONE_PS_INVAL PS_MYID
@@ -668,6 +826,7 @@ extern zone_t *zone_find_by_name(char *);
extern zone_t *zone_find_by_any_path(const char *, boolean_t);
extern zone_t *zone_find_by_path(const char *);
extern zoneid_t getzoneid(void);
+extern zoneid_t getzonedid(void);
extern zone_t *zone_find_by_id_nolock(zoneid_t);
extern int zone_datalink_walk(zoneid_t, int (*)(datalink_id_t, void *), void *);
extern int zone_check_datalink(zoneid_t *, datalink_id_t);
@@ -802,7 +961,7 @@ struct zsd_entry {
* NOTE: Using the VN_ prefix, even though it's defined here in zone.h.
* NOTE2: See above warning about ZONE_ROOTVP().
*/
-#define VN_IS_CURZONEROOT(vp) (VN_CMP(vp, ZONE_ROOTVP()))
+#define VN_IS_CURZONEROOT(vp) (VN_CMP(vp, ZONE_ROOTVP()))
/*
* Zone-safe version of thread_create() to be used when the caller wants to
@@ -868,6 +1027,7 @@ extern int zone_ncpus_online_get(zone_t *);
* Returns true if the named pool/dataset is visible in the current zone.
*/
extern int zone_dataset_visible(const char *, int *);
+extern int zone_dataset_visible_inzone(zone_t *, const char *, int *);
/*
* zone version of kadmin()
@@ -880,10 +1040,25 @@ extern void mount_completed(zone_t *);
extern int zone_walk(int (*)(zone_t *, void *), void *);
+struct page;
+extern void zone_add_page(struct page *);
+extern void zone_rm_page(struct page *);
+extern void zone_pageout_stat(int, zone_pageout_op_t);
+extern void zone_get_physmem_data(int, pgcnt_t *, pgcnt_t *);
+
+/* Interfaces for page scanning */
+extern uint_t zone_num_over_cap;
+extern zone_persist_t zone_pdata[MAX_ZONES];
+
extern rctl_hndl_t rc_zone_locked_mem;
extern rctl_hndl_t rc_zone_max_swap;
+extern rctl_hndl_t rc_zone_phys_mem;
extern rctl_hndl_t rc_zone_max_lofi;
+/* For publishing sysevents related to a particular zone */
+extern void zone_sysevent_publish(zone_t *, const char *, const char *,
+ nvlist_t *);
+
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/usr/src/uts/common/syscall/brandsys.c b/usr/src/uts/common/syscall/brandsys.c
index 9b4bd38baa..245ef9f14f 100644
--- a/usr/src/uts/common/syscall/brandsys.c
+++ b/usr/src/uts/common/syscall/brandsys.c
@@ -23,7 +23,9 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
#include <sys/brand.h>
#include <sys/systm.h>
@@ -35,7 +37,7 @@
*/
int64_t
brandsys(int cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
- uintptr_t arg4, uintptr_t arg5, uintptr_t arg6)
+ uintptr_t arg4)
{
struct proc *p = curthread->t_procp;
int64_t rval = 0;
@@ -49,7 +51,7 @@ brandsys(int cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
return (set_errno(ENOSYS));
if ((err = ZBROP(p->p_zone)->b_brandsys(cmd, &rval, arg1, arg2, arg3,
- arg4, arg5, arg6)) != 0)
+ arg4)) != 0)
return (set_errno(err));
return (rval);
diff --git a/usr/src/uts/common/syscall/chdir.c b/usr/src/uts/common/syscall/chdir.c
index 84c924f570..deb5532b50 100644
--- a/usr/src/uts/common/syscall/chdir.c
+++ b/usr/src/uts/common/syscall/chdir.c
@@ -21,6 +21,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -62,7 +63,7 @@
/*
* Change current working directory (".").
*/
-static int chdirec(vnode_t *, int ischroot, int do_traverse);
+static int chdirec(vnode_t *, boolean_t ischroot, boolean_t do_traverse);
int
chdir(char *fname)
@@ -78,7 +79,7 @@ lookup:
return (set_errno(error));
}
- error = chdirec(vp, 0, 1);
+ error = chdirec(vp, B_FALSE, B_TRUE);
if (error) {
if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
goto lookup;
@@ -102,7 +103,7 @@ fchdir(int fd)
vp = fp->f_vnode;
VN_HOLD(vp);
releasef(fd);
- error = chdirec(vp, 0, 0);
+ error = chdirec(vp, B_FALSE, B_FALSE);
if (error)
return (set_errno(error));
return (0);
@@ -125,7 +126,7 @@ lookup:
return (set_errno(error));
}
- error = chdirec(vp, 1, 1);
+ error = chdirec(vp, B_TRUE, B_TRUE);
if (error) {
if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
goto lookup;
@@ -152,18 +153,18 @@ fchroot(int fd)
vp = fp->f_vnode;
VN_HOLD(vp);
releasef(fd);
- error = chdirec(vp, 1, 0);
+ error = chdirec(vp, B_TRUE, B_FALSE);
if (error)
return (set_errno(error));
return (0);
}
static int
-chdirec(vnode_t *vp, int ischroot, int do_traverse)
+chdirec_common(proc_t *pp, vnode_t *vp, boolean_t ischroot,
+ boolean_t do_traverse)
{
int error;
vnode_t *oldvp;
- proc_t *pp = curproc;
vnode_t **vpp;
refstr_t *cwd;
int newcwd = 1;
@@ -194,7 +195,7 @@ chdirec(vnode_t *vp, int ischroot, int do_traverse)
if (ischroot) {
struct vattr tattr;
struct vattr rattr;
- vnode_t *zonevp = curproc->p_zone->zone_rootvp;
+ vnode_t *zonevp = pp->p_zone->zone_rootvp;
tattr.va_mask = AT_FSID|AT_NODEID;
if (error = VOP_GETATTR(vp, &tattr, 0, CRED(), NULL))
@@ -243,3 +244,15 @@ bad:
VN_RELE(vp);
return (error);
}
+
+int
+chdir_proc(proc_t *pp, vnode_t *vp, boolean_t ischroot, boolean_t do_traverse)
+{
+ return (chdirec_common(pp, vp, ischroot, do_traverse));
+}
+
+static int
+chdirec(vnode_t *vp, boolean_t ischroot, boolean_t do_traverse)
+{
+ return (chdirec_common(curproc, vp, ischroot, do_traverse));
+}
diff --git a/usr/src/uts/common/syscall/fcntl.c b/usr/src/uts/common/syscall/fcntl.c
index 7b787a4acb..b029d92f1b 100644
--- a/usr/src/uts/common/syscall/fcntl.c
+++ b/usr/src/uts/common/syscall/fcntl.c
@@ -54,7 +54,8 @@
#include <sys/cmn_err.h>
-static int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
+/* This is global so that it can be used by brand emulation. */
+int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
static int flock_get_start(vnode_t *, flock64_t *, offset_t, u_offset_t *);
static void fd_too_big(proc_t *);
diff --git a/usr/src/uts/common/syscall/memcntl.c b/usr/src/uts/common/syscall/memcntl.c
index 9b932af275..027ba7a2bc 100644
--- a/usr/src/uts/common/syscall/memcntl.c
+++ b/usr/src/uts/common/syscall/memcntl.c
@@ -116,13 +116,17 @@ memcntl(caddr_t addr, size_t len, int cmd, caddr_t arg, int attr, int mask)
* MS_SYNC used to be defined to be zero but is now non-zero.
* For binary compatibility we still accept zero
* (the absence of MS_ASYNC) to mean the same thing.
+ * Binary compatibility is not an issue for MS_INVALCURPROC.
*/
iarg = (uintptr_t)arg;
if ((iarg & ~MS_INVALIDATE) == 0)
iarg |= MS_SYNC;
- if (((iarg & ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE)) != 0) ||
- ((iarg & (MS_SYNC|MS_ASYNC)) == (MS_SYNC|MS_ASYNC))) {
+ if (((iarg &
+ ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE|MS_INVALCURPROC)) != 0) ||
+ ((iarg & (MS_SYNC|MS_ASYNC)) == (MS_SYNC|MS_ASYNC)) ||
+ ((iarg & (MS_INVALIDATE|MS_INVALCURPROC)) ==
+ (MS_INVALIDATE|MS_INVALCURPROC))) {
error = set_errno(EINVAL);
} else {
error = as_ctl(as, addr, len, cmd, attr, iarg, NULL, 0);
diff --git a/usr/src/uts/common/syscall/open.c b/usr/src/uts/common/syscall/open.c
index 40d9717a5b..94dc02b284 100644
--- a/usr/src/uts/common/syscall/open.c
+++ b/usr/src/uts/common/syscall/open.c
@@ -75,12 +75,12 @@ copen(int startfd, char *fname, int filemode, int createmode)
if (filemode & (FSEARCH|FEXEC)) {
/*
- * Must be one or the other and neither FREAD nor FWRITE
+ * Must be one or the other.
* Must not be any of FAPPEND FCREAT FTRUNC FXATTR FXATTRDIROPEN
- * XXX: Should these just be silently ignored?
+ * XXX: Should these just be silently ignored like we
+ * silently ignore FREAD|FWRITE?
*/
- if ((filemode & (FREAD|FWRITE)) ||
- (filemode & (FSEARCH|FEXEC)) == (FSEARCH|FEXEC) ||
+ if ((filemode & (FSEARCH|FEXEC)) == (FSEARCH|FEXEC) ||
(filemode & (FAPPEND|FCREAT|FTRUNC|FXATTR|FXATTRDIROPEN)))
return (set_errno(EINVAL));
}
diff --git a/usr/src/uts/common/syscall/poll.c b/usr/src/uts/common/syscall/poll.c
index 7af1c7edfe..a10b2623db 100644
--- a/usr/src/uts/common/syscall/poll.c
+++ b/usr/src/uts/common/syscall/poll.c
@@ -29,7 +29,7 @@
/*
* Copyright (c) 2012, 2016 by Delphix. All rights reserved.
- * Copyright 2015, Joyent, Inc.
+ * Copyright (c) 2017, Joyent, Inc.
* Copyright 2022 Oxide Computer Company
*/
@@ -326,20 +326,57 @@ polllock(pollhead_t *php, kmutex_t *lp)
return (0);
}
-static int
-poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
+int
+poll_copyin(pollstate_t *ps, pollfd_t *fds, nfds_t nfds)
+{
+ pollfd_t *pollfdp;
+ nfds_t old_nfds;
+
+ /*
+ * NOTE: for performance, buffers are saved across poll() calls.
+ * The theory is that if a process polls heavily, it tends to poll
+ * on the same set of descriptors. Therefore, we only reallocate
+ * buffers when nfds changes. There is no hysteresis control,
+ * because there is no data to suggest that this is necessary;
+ * the penalty of reallocating is not *that* great in any event.
+ */
+ old_nfds = ps->ps_nfds;
+ if (nfds != old_nfds) {
+ kmem_free(ps->ps_pollfd, old_nfds * sizeof (pollfd_t));
+ pollfdp = kmem_alloc(nfds * sizeof (pollfd_t), KM_SLEEP);
+ ps->ps_pollfd = pollfdp;
+ ps->ps_nfds = nfds;
+ }
+
+ pollfdp = ps->ps_pollfd;
+ if (copyin(fds, pollfdp, nfds * sizeof (pollfd_t))) {
+ return (EFAULT);
+ }
+
+ if (fds == NULL) {
+ /*
+ * If the process has page 0 mapped, then the copyin() above
+ * will succeed even if fds is NULL. However, our cached
+ * poll lists are keyed by the address of the passed-in fds
+ * structure, and we use the value NULL to indicate an unused
+ * poll cache list entry. As such, we elect not to support
+ * NULL as a valid (user) memory address and fail the poll()
+ * call.
+ */
+ return (EFAULT);
+ }
+ return (0);
+}
+
+int
+poll_common(pollstate_t *ps, pollfd_t *fds, nfds_t nfds, timespec_t *tsp,
+ int *fdcnt)
{
kthread_t *t = curthread;
- klwp_t *lwp = ttolwp(t);
- proc_t *p = ttoproc(t);
- int fdcnt = 0;
- int i;
hrtime_t deadline; /* hrtime value when we want to return */
pollfd_t *pollfdp;
- pollstate_t *ps;
pollcache_t *pcp;
int error = 0;
- nfds_t old_nfds;
int cacheindex = 0; /* which cache set is used */
/*
@@ -349,33 +386,34 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
deadline = -1;
} else if (tsp->tv_sec == 0 && tsp->tv_nsec == 0) {
deadline = 0;
+ } else if (tsp->tv_sec >= HRTIME_MAX/NANOSEC) {
+ /* Use an indefinite timeout if tv_sec would cause overflow */
+ deadline = -1;
} else {
+ /*
+ * The above check, when combined with the protections offered
+ * by itimerspecfix (ensuring that neither field is negative
+ * and that tv_nsec represents less than a whole second), will
+ * prevent overflow during the conversion from timespec_t to
+ * uhrtime_t.
+ */
+ uhrtime_t utime = tsp->tv_sec * NANOSEC;
+ utime += tsp->tv_nsec;
+
/* They must wait at least a tick. */
- deadline = ((hrtime_t)tsp->tv_sec * NANOSEC) + tsp->tv_nsec;
- deadline = MAX(deadline, nsec_per_tick);
- deadline += gethrtime();
- }
+ utime = MAX(utime, nsec_per_tick);
- /*
- * Reset our signal mask, if requested.
- */
- if (ksetp != NULL) {
- mutex_enter(&p->p_lock);
- schedctl_finish_sigblock(t);
- lwp->lwp_sigoldmask = t->t_hold;
- t->t_hold = *ksetp;
- t->t_flag |= T_TOMASK;
/*
- * Call cv_reltimedwait_sig() just to check for signals.
- * We will return immediately with either 0 or -1.
+ * Since utime has an upper bound of HRTIME_MAX, adding the
+ * gethrtime() result cannot incur an overflow as the unsigned
+ * type has an adequate bound.
*/
- if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
- TR_CLOCK_TICK)) {
- mutex_exit(&p->p_lock);
- error = EINTR;
- goto pollout;
+ utime += (uhrtime_t)gethrtime();
+ if (utime > HRTIME_MAX) {
+ deadline = -1;
+ } else {
+ deadline = (hrtime_t)utime;
}
- mutex_exit(&p->p_lock);
}
/*
@@ -383,6 +421,7 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
* If yes then bypass all the other stuff and make it sleep.
*/
if (nfds == 0) {
+ *fdcnt = 0;
/*
* Sleep until we have passed the requested future
* time or until interrupted by a signal.
@@ -394,66 +433,14 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
&t->t_delay_lock, deadline)) > 0)
continue;
mutex_exit(&t->t_delay_lock);
- error = (error == 0) ? EINTR : 0;
+ return ((error == 0) ? EINTR : 0);
}
- goto pollout;
- }
-
- if (nfds > p->p_fno_ctl) {
- mutex_enter(&p->p_lock);
- (void) rctl_action(rctlproc_legacy[RLIMIT_NOFILE],
- p->p_rctls, p, RCA_SAFE);
- mutex_exit(&p->p_lock);
- error = EINVAL;
- goto pollout;
- }
-
- /*
- * Need to allocate memory for pollstate before anything because
- * the mutex and cv are created in this space
- */
- ps = pollstate_create();
-
- if (ps->ps_pcache == NULL)
- ps->ps_pcache = pcache_alloc();
- pcp = ps->ps_pcache;
-
- /*
- * NOTE: for performance, buffers are saved across poll() calls.
- * The theory is that if a process polls heavily, it tends to poll
- * on the same set of descriptors. Therefore, we only reallocate
- * buffers when nfds changes. There is no hysteresis control,
- * because there is no data to suggest that this is necessary;
- * the penalty of reallocating is not *that* great in any event.
- */
- old_nfds = ps->ps_nfds;
- if (nfds != old_nfds) {
-
- kmem_free(ps->ps_pollfd, old_nfds * sizeof (pollfd_t));
- pollfdp = kmem_alloc(nfds * sizeof (pollfd_t), KM_SLEEP);
- ps->ps_pollfd = pollfdp;
- ps->ps_nfds = nfds;
+ return (0);
}
+ VERIFY(ps != NULL);
pollfdp = ps->ps_pollfd;
- if (copyin(fds, pollfdp, nfds * sizeof (pollfd_t))) {
- error = EFAULT;
- goto pollout;
- }
-
- if (fds == NULL) {
- /*
- * If the process has page 0 mapped, then the copyin() above
- * will succeed even if fds is NULL. However, our cached
- * poll lists are keyed by the address of the passed-in fds
- * structure, and we use the value NULL to indicate an unused
- * poll cache list entry. As such, we elect not to support
- * NULL as a valid (user) memory address and fail the poll()
- * call.
- */
- error = EINVAL;
- goto pollout;
- }
+ VERIFY(pollfdp != NULL);
/*
* If this thread polls for the first time, allocate ALL poll
@@ -469,10 +456,10 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
/*
* poll and cache this poll fd list in ps_pcacheset[0].
*/
- error = pcacheset_cache_list(ps, fds, &fdcnt, cacheindex);
- if (fdcnt || error) {
+ error = pcacheset_cache_list(ps, fds, fdcnt, cacheindex);
+ if (error || *fdcnt) {
mutex_exit(&ps->ps_lock);
- goto pollout;
+ return (error);
}
} else {
pollcacheset_t *pcset = ps->ps_pcacheset;
@@ -497,11 +484,11 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
* the callee will guarantee the consistency
* of cached poll list and cache content.
*/
- error = pcacheset_resolve(ps, nfds, &fdcnt,
+ error = pcacheset_resolve(ps, nfds, fdcnt,
cacheindex);
if (error) {
mutex_exit(&ps->ps_lock);
- goto pollout;
+ return (error);
}
break;
}
@@ -518,11 +505,11 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
* found an unused entry. Use it to cache
* this poll list.
*/
- error = pcacheset_cache_list(ps, fds, &fdcnt,
+ error = pcacheset_cache_list(ps, fds, fdcnt,
cacheindex);
- if (fdcnt || error) {
+ if (error || *fdcnt) {
mutex_exit(&ps->ps_lock);
- goto pollout;
+ return (error);
}
break;
}
@@ -536,10 +523,10 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
cacheindex = pcacheset_replace(ps);
ASSERT(cacheindex < ps->ps_nsets);
pcset[cacheindex].pcs_usradr = (uintptr_t)fds;
- error = pcacheset_resolve(ps, nfds, &fdcnt, cacheindex);
+ error = pcacheset_resolve(ps, nfds, fdcnt, cacheindex);
if (error) {
mutex_exit(&ps->ps_lock);
- goto pollout;
+ return (error);
}
}
}
@@ -557,8 +544,8 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
mutex_enter(&pcp->pc_lock);
for (;;) {
pcp->pc_flag = 0;
- error = pcache_poll(pollfdp, ps, nfds, &fdcnt, cacheindex);
- if (fdcnt || error) {
+ error = pcache_poll(pollfdp, ps, nfds, fdcnt, cacheindex);
+ if (error || *fdcnt) {
mutex_exit(&pcp->pc_lock);
mutex_exit(&ps->ps_lock);
break;
@@ -604,13 +591,116 @@ poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
mutex_enter(&pcp->pc_lock);
}
+ return (error);
+}
+
+/*
+ * This is the system call trap that poll(),
+ * select() and pselect() are built upon.
+ * It is a private interface between libc and the kernel.
+ */
+int
+pollsys(pollfd_t *fds, nfds_t nfds, timespec_t *timeoutp, sigset_t *setp)
+{
+ kthread_t *t = curthread;
+ klwp_t *lwp = ttolwp(t);
+ proc_t *p = ttoproc(t);
+ timespec_t ts;
+ timespec_t *tsp;
+ k_sigset_t kset;
+ pollstate_t *ps = NULL;
+ pollfd_t *pollfdp = NULL;
+ int error = 0, fdcnt = 0;
+
+ /*
+ * Copy in timeout
+ */
+ if (timeoutp == NULL) {
+ tsp = NULL;
+ } else {
+ if (get_udatamodel() == DATAMODEL_NATIVE) {
+ if (copyin(timeoutp, &ts, sizeof (ts)))
+ return (set_errno(EFAULT));
+ } else {
+ timespec32_t ts32;
+
+ if (copyin(timeoutp, &ts32, sizeof (ts32)))
+ return (set_errno(EFAULT));
+ TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
+ }
+
+ if (itimerspecfix(&ts))
+ return (set_errno(EINVAL));
+ tsp = &ts;
+ }
+
+ /*
+ * Copy in and reset signal mask, if requested.
+ */
+ if (setp != NULL) {
+ sigset_t set;
+
+ if (copyin(setp, &set, sizeof (set)))
+ return (set_errno(EFAULT));
+ sigutok(&set, &kset);
+
+ mutex_enter(&p->p_lock);
+ schedctl_finish_sigblock(t);
+ lwp->lwp_sigoldmask = t->t_hold;
+ t->t_hold = kset;
+ t->t_flag |= T_TOMASK;
+ /*
+ * Call cv_reltimedwait_sig() just to check for signals.
+ * We will return immediately with either 0 or -1.
+ */
+ if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
+ TR_CLOCK_TICK)) {
+ mutex_exit(&p->p_lock);
+ error = EINTR;
+ goto pollout;
+ }
+ mutex_exit(&p->p_lock);
+ }
+
+ /*
+ * Initialize pollstate and copy in pollfd data if present.
+ * If nfds == 0, we will skip all of the copying and check steps and
+ * proceed directly into poll_common to process the supplied timeout.
+ */
+ if (nfds != 0) {
+ if (nfds > p->p_fno_ctl) {
+ mutex_enter(&p->p_lock);
+ (void) rctl_action(rctlproc_legacy[RLIMIT_NOFILE],
+ p->p_rctls, p, RCA_SAFE);
+ mutex_exit(&p->p_lock);
+ error = EINVAL;
+ goto pollout;
+ }
+
+ /*
+ * Need to allocate memory for pollstate before anything
+ * because the mutex and cv are created in this space
+ */
+ ps = pollstate_create();
+ if (ps->ps_pcache == NULL)
+ ps->ps_pcache = pcache_alloc();
+
+ if ((error = poll_copyin(ps, fds, nfds)) != 0)
+ goto pollout;
+ pollfdp = ps->ps_pollfd;
+ }
+
+ /*
+ * Perform the actual poll.
+ */
+ error = poll_common(ps, fds, nfds, tsp, &fdcnt);
+
pollout:
/*
- * If we changed the signal mask but we received
- * no signal then restore the signal mask.
- * Otherwise psig() will deal with the signal mask.
+ * If we changed the signal mask but we received no signal then restore
+ * the signal mask. Otherwise psig() will deal with the signal mask.
*/
- if (ksetp != NULL) {
+ if (setp != NULL) {
mutex_enter(&p->p_lock);
if (lwp->lwp_cursig == 0) {
t->t_hold = lwp->lwp_sigoldmask;
@@ -621,12 +711,10 @@ pollout:
if (error)
return (set_errno(error));
-
/*
* Copy out the events and return the fdcnt to the user.
*/
- if (nfds != 0 &&
- copyout(pollfdp, fds, nfds * sizeof (pollfd_t)))
+ if (nfds != 0 && copyout(pollfdp, fds, nfds * sizeof (pollfd_t)))
return (set_errno(EFAULT));
#ifdef DEBUG
@@ -634,7 +722,7 @@ pollout:
* Another sanity check:
*/
if (fdcnt) {
- int reventcnt = 0;
+ int i, reventcnt = 0;
for (i = 0; i < nfds; i++) {
if (pollfdp[i].fd < 0) {
@@ -647,6 +735,8 @@ pollout:
}
ASSERT(fdcnt == reventcnt);
} else {
+ int i;
+
for (i = 0; i < nfds; i++) {
ASSERT(pollfdp[i].revents == 0);
}
@@ -657,52 +747,6 @@ pollout:
}
/*
- * This is the system call trap that poll(),
- * select() and pselect() are built upon.
- * It is a private interface between libc and the kernel.
- */
-int
-pollsys(pollfd_t *fds, nfds_t nfds, timespec_t *timeoutp, sigset_t *setp)
-{
- timespec_t ts;
- timespec_t *tsp;
- sigset_t set;
- k_sigset_t kset;
- k_sigset_t *ksetp;
- model_t datamodel = get_udatamodel();
-
- if (timeoutp == NULL)
- tsp = NULL;
- else {
- if (datamodel == DATAMODEL_NATIVE) {
- if (copyin(timeoutp, &ts, sizeof (ts)))
- return (set_errno(EFAULT));
- } else {
- timespec32_t ts32;
-
- if (copyin(timeoutp, &ts32, sizeof (ts32)))
- return (set_errno(EFAULT));
- TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
- }
-
- if (itimerspecfix(&ts))
- return (set_errno(EINVAL));
- tsp = &ts;
- }
-
- if (setp == NULL)
- ksetp = NULL;
- else {
- if (copyin(setp, &set, sizeof (set)))
- return (set_errno(EFAULT));
- sigutok(&set, &kset);
- ksetp = &kset;
- }
-
- return (poll_common(fds, nfds, tsp, ksetp));
-}
-
-/*
* Clean up any state left around by poll(2). Called when a thread exits.
*/
void
@@ -1317,8 +1361,8 @@ pcache_insert(pollstate_t *ps, file_t *fp, pollfd_t *pollfdp, int *fdcntp,
* be OK too.
*/
ASSERT(curthread->t_pollcache == NULL);
- error = VOP_POLL(fp->f_vnode, pollfdp->events, 0, &pollfdp->revents,
- &memphp, NULL);
+ error = VOP_POLL(fp->f_vnode, pollfdp->events | ps->ps_implicit_ev, 0,
+ &pollfdp->revents, &memphp, NULL);
if (error) {
return (error);
}
@@ -2028,7 +2072,8 @@ retry:
* flag.
*/
ASSERT(curthread->t_pollcache == NULL);
- error = VOP_POLL(fp->f_vnode, pollfdp[entry].events, 0,
+ error = VOP_POLL(fp->f_vnode,
+ pollfdp[entry].events | ps->ps_implicit_ev, 0,
&pollfdp[entry].revents, &php, NULL);
/*
* releasef after completely done with this cached
@@ -2330,6 +2375,7 @@ pollstate_create()
} else {
ASSERT(ps->ps_depth == 0);
ASSERT(ps->ps_flags == 0);
+ ASSERT(ps->ps_implicit_ev == 0);
ASSERT(ps->ps_pc_stack[0] == 0);
}
return (ps);
@@ -3067,7 +3113,7 @@ plist_chkdupfd(file_t *fp, polldat_t *pdp, pollstate_t *psp, pollfd_t *pollfdp,
php = NULL;
ASSERT(curthread->t_pollcache == NULL);
error = VOP_POLL(fp->f_vnode,
- pollfdp[i].events, 0,
+ pollfdp[i].events | psp->ps_implicit_ev, 0,
&pollfdp[i].revents, &php, NULL);
if (error) {
return (error);
diff --git a/usr/src/uts/common/syscall/rusagesys.c b/usr/src/uts/common/syscall/rusagesys.c
index 3e0e63f4c0..09f3266ab4 100644
--- a/usr/src/uts/common/syscall/rusagesys.c
+++ b/usr/src/uts/common/syscall/rusagesys.c
@@ -21,6 +21,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc. All rights reserved.
*/
/*
diff --git a/usr/src/uts/common/syscall/stat.c b/usr/src/uts/common/syscall/stat.c
index a37a86f05a..54dc532cd4 100644
--- a/usr/src/uts/common/syscall/stat.c
+++ b/usr/src/uts/common/syscall/stat.c
@@ -61,7 +61,7 @@
* to VOP_GETATTR
*/
-static int
+int
cstatat_getvp(int fd, char *name, int follow, vnode_t **vp, cred_t **cred)
{
vnode_t *startvp;
diff --git a/usr/src/uts/common/syscall/sysconfig.c b/usr/src/uts/common/syscall/sysconfig.c
index 9c50922b7e..96535fdd08 100644
--- a/usr/src/uts/common/syscall/sysconfig.c
+++ b/usr/src/uts/common/syscall/sysconfig.c
@@ -22,7 +22,7 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2016 Joyent, Inc.
+ * Copyright 2017 Joyent, Inc.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -172,44 +172,29 @@ sysconfig(int which)
/*
* If the non-global zone has a phys. memory cap, use that.
* We always report the system-wide value for the global zone,
- * even though rcapd can be used on the global zone too.
+ * even though memory capping can be used on the global zone
+ * too.
*/
- if (!INGLOBALZONE(curproc) &&
- curproc->p_zone->zone_phys_mcap != 0)
- return (MIN(btop(curproc->p_zone->zone_phys_mcap),
- physinstalled));
+ if (!INGLOBALZONE(curproc)) {
+ pgcnt_t cap, free;
+
+ zone_get_physmem_data(curzone->zone_id, &cap, &free);
+ return (MIN(cap, physinstalled));
+ }
return (physinstalled);
case _CONFIG_AVPHYS_PAGES:
/*
- * If the non-global zone has a phys. memory cap, use
- * the phys. memory cap - zone's current rss. We always
- * report the system-wide value for the global zone, even
- * though rcapd can be used on the global zone too.
+ * If the non-global zone has a phys. memory cap, use its
+ * free value. We always report the system-wide value for the
+ * global zone, even though memory capping can be used on the
+ * global zone too.
*/
- if (!INGLOBALZONE(curproc) &&
- curproc->p_zone->zone_phys_mcap != 0) {
- pgcnt_t cap, rss, free;
- vmusage_t in_use;
- size_t cnt = 1;
-
- cap = btop(curproc->p_zone->zone_phys_mcap);
- if (cap > physinstalled)
- return (freemem);
-
- if (vm_getusage(VMUSAGE_ZONE, 1, &in_use, &cnt,
- FKIOCTL) != 0)
- in_use.vmu_rss_all = 0;
- rss = btop(in_use.vmu_rss_all);
- /*
- * Because rcapd implements a soft cap, it is possible
- * for rss to be temporarily over the cap.
- */
- if (cap > rss)
- free = cap - rss;
- else
- free = 0;
+ if (!INGLOBALZONE(curproc)) {
+ pgcnt_t cap, free;
+
+ zone_get_physmem_data(curzone->zone_id, &cap, &free);
return (MIN(free, freemem));
}
diff --git a/usr/src/uts/common/syscall/uadmin.c b/usr/src/uts/common/syscall/uadmin.c
index 858305504d..dfe7f22d44 100644
--- a/usr/src/uts/common/syscall/uadmin.c
+++ b/usr/src/uts/common/syscall/uadmin.c
@@ -22,7 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2013 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <sys/param.h>
@@ -78,7 +78,7 @@ volatile int fastreboot_dryrun = 0;
* system with many zones.
*/
void
-killall(zoneid_t zoneid)
+killall(zoneid_t zoneid, boolean_t force)
{
proc_t *p;
@@ -108,7 +108,7 @@ killall(zoneid_t zoneid)
p->p_stat != SIDL &&
p->p_stat != SZOMB) {
mutex_enter(&p->p_lock);
- if (sigismember(&p->p_sig, SIGKILL)) {
+ if (!force && sigismember(&p->p_sig, SIGKILL)) {
mutex_exit(&p->p_lock);
p = p->p_next;
} else {
@@ -245,12 +245,13 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp)
*/
zone_shutdown_global();
- killall(ALL_ZONES);
+ killall(ALL_ZONES, B_FALSE);
/*
* If we are calling kadmin() from a kernel context then we
* do not release these resources.
*/
if (ttoproc(curthread) != &p0) {
+ mutex_enter(&curproc->p_lock);
VN_RELE(PTOU(curproc)->u_cdir);
if (PTOU(curproc)->u_rdir)
VN_RELE(PTOU(curproc)->u_rdir);
@@ -260,6 +261,7 @@ kadmin(int cmd, int fcn, void *mdep, cred_t *credp)
PTOU(curproc)->u_cdir = rootdir;
PTOU(curproc)->u_rdir = NULL;
PTOU(curproc)->u_cwd = NULL;
+ mutex_exit(&curproc->p_lock);
}
/*
diff --git a/usr/src/uts/common/syscall/umount.c b/usr/src/uts/common/syscall/umount.c
index a2deedb163..b25f89b6d5 100644
--- a/usr/src/uts/common/syscall/umount.c
+++ b/usr/src/uts/common/syscall/umount.c
@@ -22,6 +22,7 @@
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
@@ -125,6 +126,7 @@ umount2(char *pathp, int flag)
struct pathname pn;
struct vfs *vfsp;
int error;
+ boolean_t altroot;
/*
* Some flags are disallowed through the system call interface.
@@ -154,9 +156,12 @@ umount2(char *pathp, int flag)
* isn't in an environment with an alternate root (to the zone's root)
* directory, i.e. chroot(2).
*/
- if (secpolicy_fs_unmount(CRED(), NULL) != 0 ||
- (PTOU(curproc)->u_rdir != NULL &&
- PTOU(curproc)->u_rdir != curproc->p_zone->zone_rootvp) ||
+ mutex_enter(&curproc->p_lock);
+ altroot = (PTOU(curproc)->u_rdir != NULL &&
+ PTOU(curproc)->u_rdir != curproc->p_zone->zone_rootvp);
+ mutex_exit(&curproc->p_lock);
+
+ if (secpolicy_fs_unmount(CRED(), NULL) != 0 || altroot ||
(vfsp = vfs_mntpoint2vfsp(pn.pn_path)) == NULL) {
vnode_t *fsrootvp;
diff --git a/usr/src/uts/common/vm/hat.h b/usr/src/uts/common/vm/hat.h
index a2509e7bb6..3735139068 100644
--- a/usr/src/uts/common/vm/hat.h
+++ b/usr/src/uts/common/vm/hat.h
@@ -21,6 +21,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -269,7 +270,12 @@ void hat_kpm_walk(void (*)(void *, void *, size_t), void *);
* call.
*
* int hat_pageunload(pp, forceflag)
- * unload all translations attached to pp.
+ * Unload all translations attached to pp. On x86 the bulk of the work is
+ * done by hat_page_inval.
+ *
+ * void hat_page_inval(pp, pgsz, curhat)
+ * Unload translations attached to pp. If curhat is provided, only the
+ * translation for that process is unloaded, otherwise all are unloaded.
*
* uint_t hat_pagesync(pp, flags)
* get hw stats from hardware into page struct and reset hw stats
@@ -291,6 +297,7 @@ void hat_page_setattr(struct page *, uint_t);
void hat_page_clrattr(struct page *, uint_t);
uint_t hat_page_getattr(struct page *, uint_t);
int hat_pageunload(struct page *, uint_t);
+void hat_page_inval(struct page *, uint_t, struct hat *);
uint_t hat_pagesync(struct page *, uint_t);
ulong_t hat_page_getshare(struct page *);
int hat_page_checkshare(struct page *, ulong_t);
@@ -460,6 +467,7 @@ void hat_setstat(struct as *, caddr_t, size_t, uint_t);
*/
#define HAT_ADV_PGUNLOAD 0x00
#define HAT_FORCE_PGUNLOAD 0x01
+#define HAT_CURPROC_PGUNLOAD 0x02
/*
* Attributes for hat_page_*attr, hat_setstats and
diff --git a/usr/src/uts/common/vm/page.h b/usr/src/uts/common/vm/page.h
index b2f61429e9..bd17487be3 100644
--- a/usr/src/uts/common/vm/page.h
+++ b/usr/src/uts/common/vm/page.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2017, Joyent, Inc.
* Copyright 2021 Oxide Computer Company
*/
@@ -230,6 +231,7 @@ struct as;
* p_nrm
* p_mapping
* p_share
+ * p_zoneid
*
* The following field is file system dependent. How it is used and
* the locking strategies applied are up to the individual file system
@@ -528,9 +530,8 @@ typedef struct page {
pfn_t p_pagenum; /* physical page number */
uint_t p_share; /* number of translations */
-#if defined(_LP64)
- uint_t p_sharepad; /* pad for growing p_share */
-#endif
+ short p_zoneid; /* zone page use tracking */
+ short p_pad1; /* TBD */
uint_t p_slckcnt; /* number of softlocks */
#if defined(__sparc)
uint_t p_kpmref; /* number of kpm mapping sharers */
diff --git a/usr/src/uts/common/vm/seg_vn.c b/usr/src/uts/common/vm/seg_vn.c
index e1b5b79b8d..75e3bf5acb 100644
--- a/usr/src/uts/common/vm/seg_vn.c
+++ b/usr/src/uts/common/vm/seg_vn.c
@@ -7319,7 +7319,8 @@ segvn_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
vpp = svd->vpage;
offset = svd->offset + (uintptr_t)(addr - seg->s_base);
bflags = ((flags & MS_ASYNC) ? B_ASYNC : 0) |
- ((flags & MS_INVALIDATE) ? B_INVAL : 0);
+ ((flags & MS_INVALIDATE) ? B_INVAL : 0) |
+ ((flags & MS_INVALCURPROC) ? (B_INVALCURONLY | B_INVAL) : 0);
if (attr) {
pageprot = attr & ~(SHARED|PRIVATE);
@@ -7344,11 +7345,11 @@ segvn_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
vpp = &svd->vpage[seg_page(seg, addr)];
} else if (svd->vp && svd->amp == NULL &&
- (flags & MS_INVALIDATE) == 0) {
+ (flags & (MS_INVALIDATE | MS_INVALCURPROC)) == 0) {
/*
- * No attributes, no anonymous pages and MS_INVALIDATE flag
- * is not on, just use one big request.
+ * No attributes, no anonymous pages and MS_INVAL* flags
+ * are not on, just use one big request.
*/
err = VOP_PUTPAGE(svd->vp, (offset_t)offset, len,
bflags, svd->cred, NULL);
@@ -7400,7 +7401,7 @@ segvn_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
* might race in and lock the page after we unlock and before
* we do the PUTPAGE, then PUTPAGE simply does nothing.
*/
- if (flags & MS_INVALIDATE) {
+ if (flags & (MS_INVALIDATE | MS_INVALCURPROC)) {
if ((pp = page_lookup(vp, off, SE_SHARED)) != NULL) {
if (pp->p_lckcnt != 0 || pp->p_cowcnt != 0) {
page_unlock(pp);
diff --git a/usr/src/uts/common/vm/vm_as.c b/usr/src/uts/common/vm/vm_as.c
index c217270943..24e03795e6 100644
--- a/usr/src/uts/common/vm/vm_as.c
+++ b/usr/src/uts/common/vm/vm_as.c
@@ -57,6 +57,7 @@
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/vtrace.h>
+#include <sys/ddi.h>
#include <vm/hat.h>
#include <vm/as.h>
@@ -71,6 +72,8 @@
clock_t deadlk_wait = 1; /* number of ticks to wait before retrying */
+ulong_t as_user_seg_limit = 0xffff; /* max segments in an (non-kas) AS */
+
static struct kmem_cache *as_cache;
static void as_setwatchprot(struct as *, caddr_t, size_t, uint_t);
@@ -838,8 +841,6 @@ as_fault(struct hat *hat, struct as *as, caddr_t addr, size_t size,
int as_lock_held;
klwp_t *lwp = ttolwp(curthread);
-
-
retry:
/*
* Indicate that the lwp is not to be stopped while waiting for a
@@ -1703,6 +1704,20 @@ as_map_locked(struct as *as, caddr_t addr, size_t size, segcreate_func_t crfp,
p->p_rctls, p, RCA_UNSAFE_ALL);
return (ENOMEM);
}
+
+ /*
+ * Keep the number of segments in a userspace AS constrained to
+ * a reasonable limit. Linux enforces a value slightly less
+ * than 64k in order to avoid ELF limits if/when a process
+ * dumps core. While SunOS avoids that specific problem with
+ * other tricks, the limit is still valuable to keep kernel
+ * memory consumption in check.
+ */
+ if (avl_numnodes(&as->a_segtree) >= as_user_seg_limit) {
+ AS_LOCK_EXIT(as);
+ atomic_inc_32(&p->p_zone->zone_mfseglim);
+ return (ENOMEM);
+ }
}
if (AS_MAP_CHECK_VNODE_LPOOB(crfp, argsp)) {
diff --git a/usr/src/uts/common/vm/vm_page.c b/usr/src/uts/common/vm/vm_page.c
index 134b3bcb33..cb99a6b92b 100644
--- a/usr/src/uts/common/vm/vm_page.c
+++ b/usr/src/uts/common/vm/vm_page.c
@@ -84,6 +84,7 @@
static pgcnt_t max_page_get; /* max page_get request size in pages */
pgcnt_t total_pages = 0; /* total number of pages (used by /proc) */
+uint64_t n_throttle = 0; /* num times page create throttled */
/*
* freemem_lock protects all freemem variables:
@@ -1477,6 +1478,8 @@ page_create_throttle(pgcnt_t npages, int flags)
uint_t i;
pgcnt_t tf; /* effective value of throttlefree */
+ atomic_inc_64(&n_throttle);
+
/*
* Normal priority allocations.
*/
@@ -1509,7 +1512,7 @@ page_create_throttle(pgcnt_t npages, int flags)
tf = throttlefree -
((flags & PG_PUSHPAGE) ? pageout_reserve : 0);
- cv_signal(&proc_pageout->p_cv);
+ WAKE_PAGEOUT_SCANNER();
for (;;) {
fm = 0;
@@ -1596,7 +1599,7 @@ checkagain:
}
ASSERT(proc_pageout != NULL);
- cv_signal(&proc_pageout->p_cv);
+ WAKE_PAGEOUT_SCANNER();
TRACE_2(TR_FAC_VM, TR_PAGE_CREATE_SLEEP_START,
"page_create_sleep_start: freemem %ld needfree %ld",
@@ -2243,7 +2246,7 @@ page_create_va_large(vnode_t *vp, u_offset_t off, size_t bytes, uint_t flags,
if (nscan < desscan && freemem < minfree) {
TRACE_1(TR_FAC_VM, TR_PAGEOUT_CV_SIGNAL,
"pageout_cv_signal:freemem %ld", freemem);
- cv_signal(&proc_pageout->p_cv);
+ WAKE_PAGEOUT_SCANNER();
}
pp = rootpp;
@@ -2372,7 +2375,7 @@ page_create_va(vnode_t *vp, u_offset_t off, size_t bytes, uint_t flags,
if (nscan < desscan && freemem < minfree) {
TRACE_1(TR_FAC_VM, TR_PAGEOUT_CV_SIGNAL,
"pageout_cv_signal:freemem %ld", freemem);
- cv_signal(&proc_pageout->p_cv);
+ WAKE_PAGEOUT_SCANNER();
}
/*
diff --git a/usr/src/uts/common/vm/vm_pvn.c b/usr/src/uts/common/vm/vm_pvn.c
index aaad06792b..db8b86d6e6 100644
--- a/usr/src/uts/common/vm/vm_pvn.c
+++ b/usr/src/uts/common/vm/vm_pvn.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
@@ -431,7 +432,14 @@ pvn_write_done(page_t *plist, int flags)
page_io_unlock(pp);
page_unlock(pp);
}
- } else if (flags & B_INVAL) {
+ } else if ((flags & (B_INVAL | B_INVALCURONLY)) == B_INVAL) {
+ /*
+ * If B_INVALCURONLY is set, then we handle that case
+ * in the next conditional if hat_page_is_mapped()
+ * indicates that there are no additional mappings
+ * to the page.
+ */
+
/*
* XXX - Failed writes with B_INVAL set are
* not handled appropriately.
@@ -565,8 +573,9 @@ pvn_write_done(page_t *plist, int flags)
}
/*
- * Flags are composed of {B_ASYNC, B_INVAL, B_FREE, B_DONTNEED, B_DELWRI,
- * B_TRUNC, B_FORCE}. B_DELWRI indicates that this page is part of a kluster
+ * Flags are composed of {B_ASYNC, B_INVAL, B_INVALCURONLY, B_FREE,
+ * B_DONTNEED, B_DELWRI, B_TRUNC, B_FORCE}.
+ * B_DELWRI indicates that this page is part of a kluster
* operation and is only to be considered if it doesn't involve any
* waiting here. B_TRUNC indicates that the file is being truncated
* and so no i/o needs to be done. B_FORCE indicates that the page
@@ -620,13 +629,17 @@ pvn_getdirty(page_t *pp, int flags)
* If we want to free or invalidate the page then
* we need to unload it so that anyone who wants
* it will have to take a minor fault to get it.
+ * If we are only invalidating the page for the
+ * current process, then pass in a different flag.
* Otherwise, we're just writing the page back so we
* need to sync up the hardwre and software mod bit to
* detect any future modifications. We clear the
* software mod bit when we put the page on the dirty
* list.
*/
- if (flags & (B_INVAL | B_FREE)) {
+ if (flags & B_INVALCURONLY) {
+ (void) hat_pageunload(pp, HAT_CURPROC_PGUNLOAD);
+ } else if (flags & (B_INVAL | B_FREE)) {
(void) hat_pageunload(pp, HAT_FORCE_PGUNLOAD);
} else {
(void) hat_pagesync(pp, HAT_SYNC_ZERORM);
@@ -638,7 +651,7 @@ pvn_getdirty(page_t *pp, int flags)
* list after all.
*/
page_io_unlock(pp);
- if (flags & B_INVAL) {
+ if ((flags & (B_INVAL | B_INVALCURONLY)) == B_INVAL) {
/*LINTED: constant in conditional context*/
VN_DISPOSE(pp, B_INVAL, 0, kcred);
} else if (flags & B_FREE) {
@@ -650,6 +663,9 @@ pvn_getdirty(page_t *pp, int flags)
* of VOP_PUTPAGE() who prefer freeing the
* page _only_ if no one else is accessing it.
* E.g. segmap_release()
+ * We also take this path for B_INVALCURONLY and
+ * let page_release call VN_DISPOSE if no one else is
+ * using the page.
*
* The above hat_ismod() check is useless because:
* (1) we may not be holding SE_EXCL lock;
@@ -674,7 +690,7 @@ pvn_getdirty(page_t *pp, int flags)
* We'll detect the fact that they used it when the
* i/o is done and avoid freeing the page.
*/
- if (flags & B_FREE)
+ if (flags & (B_FREE | B_INVALCURONLY))
page_downgrade(pp);
diff --git a/usr/src/uts/common/vm/vm_usage.c b/usr/src/uts/common/vm/vm_usage.c
index 98ed21d059..88c694336d 100644
--- a/usr/src/uts/common/vm/vm_usage.c
+++ b/usr/src/uts/common/vm/vm_usage.c
@@ -25,6 +25,10 @@
*/
/*
+ * Copyright 2018, Joyent, Inc.
+ */
+
+/*
* vm_usage
*
* This file implements the getvmusage() private system call.
@@ -114,7 +118,7 @@
* For accurate counting of map-shared and COW-shared pages.
*
* - visited private anons (refcnt > 1) for each collective.
- * (entity->vme_anon_hash)
+ * (entity->vme_anon)
* For accurate counting of COW-shared pages.
*
* The common accounting structure is the vmu_entity_t, which represents
@@ -152,6 +156,7 @@
#include <sys/vm_usage.h>
#include <sys/zone.h>
#include <sys/sunddi.h>
+#include <sys/sysmacros.h>
#include <sys/avl.h>
#include <vm/anon.h>
#include <vm/as.h>
@@ -199,6 +204,14 @@ typedef struct vmu_object {
} vmu_object_t;
/*
+ * Node for tree of visited COW anons.
+ */
+typedef struct vmu_anon {
+ avl_node_t vma_node;
+ uintptr_t vma_addr;
+} vmu_anon_t;
+
+/*
* Entity by which to count results.
*
* The entity structure keeps the current rss/swap counts for each entity
@@ -221,7 +234,7 @@ typedef struct vmu_entity {
struct vmu_entity *vme_next_calc;
mod_hash_t *vme_vnode_hash; /* vnodes visited for entity */
mod_hash_t *vme_amp_hash; /* shared amps visited for entity */
- mod_hash_t *vme_anon_hash; /* COW anons visited for entity */
+ avl_tree_t vme_anon; /* COW anons visited for entity */
vmusage_t vme_result; /* identifies entity and results */
} vmu_entity_t;
@@ -324,6 +337,23 @@ bounds_cmp(const void *bnd1, const void *bnd2)
}
/*
+ * Comparison routine for our AVL tree of anon structures.
+ */
+static int
+vmu_anon_cmp(const void *lhs, const void *rhs)
+{
+ const vmu_anon_t *l = lhs, *r = rhs;
+
+ if (l->vma_addr == r->vma_addr)
+ return (0);
+
+ if (l->vma_addr < r->vma_addr)
+ return (-1);
+
+ return (1);
+}
+
+/*
* Save a bound on the free list.
*/
static void
@@ -363,13 +393,18 @@ static void
vmu_free_entity(mod_hash_val_t val)
{
vmu_entity_t *entity = (vmu_entity_t *)val;
+ vmu_anon_t *anon;
+ void *cookie = NULL;
if (entity->vme_vnode_hash != NULL)
i_mod_hash_clear_nosync(entity->vme_vnode_hash);
if (entity->vme_amp_hash != NULL)
i_mod_hash_clear_nosync(entity->vme_amp_hash);
- if (entity->vme_anon_hash != NULL)
- i_mod_hash_clear_nosync(entity->vme_anon_hash);
+
+ while ((anon = avl_destroy_nodes(&entity->vme_anon, &cookie)) != NULL)
+ kmem_free(anon, sizeof (vmu_anon_t));
+
+ avl_destroy(&entity->vme_anon);
entity->vme_next = vmu_data.vmu_free_entities;
vmu_data.vmu_free_entities = entity;
@@ -485,10 +520,10 @@ vmu_alloc_entity(id_t id, int type, id_t zoneid)
"vmusage amp hash", VMUSAGE_HASH_SIZE, vmu_free_object,
sizeof (struct anon_map));
- if (entity->vme_anon_hash == NULL)
- entity->vme_anon_hash = mod_hash_create_ptrhash(
- "vmusage anon hash", VMUSAGE_HASH_SIZE,
- mod_hash_null_valdtor, sizeof (struct anon));
+ VERIFY(avl_first(&entity->vme_anon) == NULL);
+
+ avl_create(&entity->vme_anon, vmu_anon_cmp, sizeof (struct vmu_anon),
+ offsetof(struct vmu_anon, vma_node));
entity->vme_next = vmu_data.vmu_entities;
vmu_data.vmu_entities = entity;
@@ -518,7 +553,8 @@ vmu_alloc_zone(id_t id)
zone->vmz_id = id;
- if ((vmu_data.vmu_calc_flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES)) != 0)
+ if ((vmu_data.vmu_calc_flags &
+ (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_A_ZONE)) != 0)
zone->vmz_zone = vmu_alloc_entity(id, VMUSAGE_ZONE, id);
if ((vmu_data.vmu_calc_flags & (VMUSAGE_PROJECTS |
@@ -613,21 +649,19 @@ vmu_find_insert_object(mod_hash_t *hash, caddr_t key, uint_t type)
}
static int
-vmu_find_insert_anon(mod_hash_t *hash, caddr_t key)
+vmu_find_insert_anon(vmu_entity_t *entity, void *key)
{
- int ret;
- caddr_t val;
+ vmu_anon_t anon, *ap;
- ret = i_mod_hash_find_nosync(hash, (mod_hash_key_t)key,
- (mod_hash_val_t *)&val);
+ anon.vma_addr = (uintptr_t)key;
- if (ret == 0)
+ if (avl_find(&entity->vme_anon, &anon, NULL) != NULL)
return (0);
- ret = i_mod_hash_insert_nosync(hash, (mod_hash_key_t)key,
- (mod_hash_val_t)key, (mod_hash_hndl_t)0);
+ ap = kmem_alloc(sizeof (vmu_anon_t), KM_SLEEP);
+ ap->vma_addr = (uintptr_t)key;
- ASSERT(ret == 0);
+ avl_add(&entity->vme_anon, ap);
return (1);
}
@@ -918,6 +952,8 @@ vmu_amp_update_incore_bounds(avl_tree_t *tree, struct anon_map *amp,
next = AVL_NEXT(tree, next);
continue;
}
+
+ ASSERT(next->vmb_type == VMUSAGE_BOUND_UNKNOWN);
bound_type = next->vmb_type;
index = next->vmb_start;
while (index <= next->vmb_end) {
@@ -937,7 +973,10 @@ vmu_amp_update_incore_bounds(avl_tree_t *tree, struct anon_map *amp,
if (ap != NULL && vn != NULL && vn->v_pages != NULL &&
(page = page_exists(vn, off)) != NULL) {
- page_type = VMUSAGE_BOUND_INCORE;
+ if (PP_ISFREE(page))
+ page_type = VMUSAGE_BOUND_NOT_INCORE;
+ else
+ page_type = VMUSAGE_BOUND_INCORE;
if (page->p_szc > 0) {
pgcnt = page_get_pagecnt(page->p_szc);
pgshft = page_get_shift(page->p_szc);
@@ -947,8 +986,10 @@ vmu_amp_update_incore_bounds(avl_tree_t *tree, struct anon_map *amp,
} else {
page_type = VMUSAGE_BOUND_NOT_INCORE;
}
+
if (bound_type == VMUSAGE_BOUND_UNKNOWN) {
next->vmb_type = page_type;
+ bound_type = page_type;
} else if (next->vmb_type != page_type) {
/*
* If current bound type does not match page
@@ -1009,6 +1050,7 @@ vmu_vnode_update_incore_bounds(avl_tree_t *tree, vnode_t *vnode,
continue;
}
+ ASSERT(next->vmb_type == VMUSAGE_BOUND_UNKNOWN);
bound_type = next->vmb_type;
index = next->vmb_start;
while (index <= next->vmb_end) {
@@ -1024,7 +1066,10 @@ vmu_vnode_update_incore_bounds(avl_tree_t *tree, vnode_t *vnode,
if (vnode->v_pages != NULL &&
(page = page_exists(vnode, ptob(index))) != NULL) {
- page_type = VMUSAGE_BOUND_INCORE;
+ if (PP_ISFREE(page))
+ page_type = VMUSAGE_BOUND_NOT_INCORE;
+ else
+ page_type = VMUSAGE_BOUND_INCORE;
if (page->p_szc > 0) {
pgcnt = page_get_pagecnt(page->p_szc);
pgshft = page_get_shift(page->p_szc);
@@ -1034,8 +1079,10 @@ vmu_vnode_update_incore_bounds(avl_tree_t *tree, vnode_t *vnode,
} else {
page_type = VMUSAGE_BOUND_NOT_INCORE;
}
+
if (bound_type == VMUSAGE_BOUND_UNKNOWN) {
next->vmb_type = page_type;
+ bound_type = page_type;
} else if (next->vmb_type != page_type) {
/*
* If current bound type does not match page
@@ -1306,6 +1353,12 @@ vmu_calculate_seg(vmu_entity_t *vmu_entities, struct seg *seg)
}
/*
+ * Pages on the free list aren't counted for the rss.
+ */
+ if (PP_ISFREE(page))
+ continue;
+
+ /*
* Assume anon structs with a refcnt
* of 1 are not COW shared, so there
* is no reason to track them per entity.
@@ -1322,8 +1375,7 @@ vmu_calculate_seg(vmu_entity_t *vmu_entities, struct seg *seg)
* Track COW anons per entity so
* they are not double counted.
*/
- if (vmu_find_insert_anon(entity->vme_anon_hash,
- (caddr_t)ap) == 0)
+ if (vmu_find_insert_anon(entity, ap) == 0)
continue;
result->vmu_rss_all += (pgcnt << PAGESHIFT);
@@ -1463,8 +1515,9 @@ vmu_calculate_proc(proc_t *p)
entities = tmp;
}
if (vmu_data.vmu_calc_flags &
- (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_PROJECTS |
- VMUSAGE_ALL_PROJECTS | VMUSAGE_TASKS | VMUSAGE_ALL_TASKS |
+ (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_A_ZONE |
+ VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS |
+ VMUSAGE_TASKS | VMUSAGE_ALL_TASKS |
VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS | VMUSAGE_EUSERS |
VMUSAGE_ALL_EUSERS)) {
ret = i_mod_hash_find_nosync(vmu_data.vmu_zones_hash,
@@ -1597,8 +1650,7 @@ vmu_free_extra()
mod_hash_destroy_hash(te->vme_vnode_hash);
if (te->vme_amp_hash != NULL)
mod_hash_destroy_hash(te->vme_amp_hash);
- if (te->vme_anon_hash != NULL)
- mod_hash_destroy_hash(te->vme_anon_hash);
+ VERIFY(avl_first(&te->vme_anon) == NULL);
kmem_free(te, sizeof (vmu_entity_t));
}
while (vmu_data.vmu_free_zones != NULL) {
@@ -1619,13 +1671,42 @@ vmu_free_extra()
extern kcondvar_t *pr_pid_cv;
+static void
+vmu_get_zone_rss(zoneid_t zid)
+{
+ vmu_zone_t *zone;
+ zone_t *zp;
+ int ret;
+ uint_t pgcnt;
+
+ if ((zp = zone_find_by_id(zid)) == NULL)
+ return;
+
+ ret = i_mod_hash_find_nosync(vmu_data.vmu_zones_hash,
+ (mod_hash_key_t)(uintptr_t)zid, (mod_hash_val_t *)&zone);
+ if (ret != 0) {
+ zone = vmu_alloc_zone(zid);
+ ret = i_mod_hash_insert_nosync(vmu_data.vmu_zones_hash,
+ (mod_hash_key_t)(uintptr_t)zid,
+ (mod_hash_val_t)zone, (mod_hash_hndl_t)0);
+ ASSERT(ret == 0);
+ }
+
+ ASSERT(zid >= 0 && zid <= MAX_ZONEID);
+ pgcnt = zone_pdata[zid].zpers_pg_cnt;
+ zone->vmz_zone->vme_result.vmu_rss_all = (size_t)ptob(pgcnt);
+ zone->vmz_zone->vme_result.vmu_swap_all = zp->zone_max_swap;
+
+ zone_rele(zp);
+}
+
/*
* Determine which entity types are relevant and allocate the hashes to
- * track them. Then walk the process table and count rss and swap
- * for each process'es address space. Address space object such as
- * vnodes, amps and anons are tracked per entity, so that they are
- * not double counted in the results.
- *
+ * track them. First get the zone rss using the data we already have. Then,
+ * if necessary, walk the process table and count rss and swap for each
+ * process'es address space. Address space object such as vnodes, amps and
+ * anons are tracked per entity, so that they are not double counted in the
+ * results.
*/
static void
vmu_calculate()
@@ -1633,6 +1714,7 @@ vmu_calculate()
int i = 0;
int ret;
proc_t *p;
+ uint_t zone_flags = 0;
vmu_clear_calc();
@@ -1640,9 +1722,34 @@ vmu_calculate()
vmu_data.vmu_system = vmu_alloc_entity(0, VMUSAGE_SYSTEM,
ALL_ZONES);
+ zone_flags = vmu_data.vmu_calc_flags & VMUSAGE_ZONE_FLAGS;
+ if (zone_flags != 0) {
+ /*
+ * Use the accurate zone RSS data we already keep track of.
+ */
+ int i;
+
+ for (i = 0; i <= MAX_ZONEID; i++) {
+ if (zone_pdata[i].zpers_pg_cnt > 0) {
+ vmu_get_zone_rss(i);
+ }
+ }
+ }
+
+ /* If only neeeded zone data, we're done. */
+ if ((vmu_data.vmu_calc_flags & ~VMUSAGE_ZONE_FLAGS) == 0) {
+ return;
+ }
+
+ DTRACE_PROBE(vmu__calculate__all);
+ vmu_data.vmu_calc_flags &= ~VMUSAGE_ZONE_FLAGS;
+
/*
* Walk process table and calculate rss of each proc.
*
+ * Since we already obtained all zone rss above, the following loop
+ * executes with the VMUSAGE_ZONE_FLAGS cleared.
+ *
* Pidlock and p_lock cannot be held while doing the rss calculation.
* This is because:
* 1. The calculation allocates using KM_SLEEP.
@@ -1697,6 +1804,12 @@ again:
mutex_exit(&pidlock);
vmu_free_extra();
+
+ /*
+ * Restore any caller-supplied zone flags we blocked during
+ * the process-table walk.
+ */
+ vmu_data.vmu_calc_flags |= zone_flags;
}
/*
@@ -1747,7 +1860,7 @@ vmu_cache_rele(vmu_cache_t *cache)
*/
static int
vmu_copyout_results(vmu_cache_t *cache, vmusage_t *buf, size_t *nres,
- uint_t flags, int cpflg)
+ uint_t flags, id_t req_zone_id, int cpflg)
{
vmusage_t *result, *out_result;
vmusage_t dummy;
@@ -1766,7 +1879,7 @@ vmu_copyout_results(vmu_cache_t *cache, vmusage_t *buf, size_t *nres,
/* figure out what results the caller is interested in. */
if ((flags & VMUSAGE_SYSTEM) && curproc->p_zone == global_zone)
types |= VMUSAGE_SYSTEM;
- if (flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES))
+ if (flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_A_ZONE))
types |= VMUSAGE_ZONE;
if (flags & (VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS |
VMUSAGE_COL_PROJECTS))
@@ -1829,26 +1942,33 @@ vmu_copyout_results(vmu_cache_t *cache, vmusage_t *buf, size_t *nres,
continue;
}
- /* Skip "other zone" results if not requested */
- if (result->vmu_zoneid != curproc->p_zone->zone_id) {
- if (result->vmu_type == VMUSAGE_ZONE &&
- (flags & VMUSAGE_ALL_ZONES) == 0)
- continue;
- if (result->vmu_type == VMUSAGE_PROJECTS &&
- (flags & (VMUSAGE_ALL_PROJECTS |
- VMUSAGE_COL_PROJECTS)) == 0)
- continue;
- if (result->vmu_type == VMUSAGE_TASKS &&
- (flags & VMUSAGE_ALL_TASKS) == 0)
- continue;
- if (result->vmu_type == VMUSAGE_RUSERS &&
- (flags & (VMUSAGE_ALL_RUSERS |
- VMUSAGE_COL_RUSERS)) == 0)
- continue;
- if (result->vmu_type == VMUSAGE_EUSERS &&
- (flags & (VMUSAGE_ALL_EUSERS |
- VMUSAGE_COL_EUSERS)) == 0)
+ if (result->vmu_type == VMUSAGE_ZONE &&
+ flags & VMUSAGE_A_ZONE) {
+ /* Skip non-requested zone results */
+ if (result->vmu_zoneid != req_zone_id)
continue;
+ } else {
+ /* Skip "other zone" results if not requested */
+ if (result->vmu_zoneid != curproc->p_zone->zone_id) {
+ if (result->vmu_type == VMUSAGE_ZONE &&
+ (flags & VMUSAGE_ALL_ZONES) == 0)
+ continue;
+ if (result->vmu_type == VMUSAGE_PROJECTS &&
+ (flags & (VMUSAGE_ALL_PROJECTS |
+ VMUSAGE_COL_PROJECTS)) == 0)
+ continue;
+ if (result->vmu_type == VMUSAGE_TASKS &&
+ (flags & VMUSAGE_ALL_TASKS) == 0)
+ continue;
+ if (result->vmu_type == VMUSAGE_RUSERS &&
+ (flags & (VMUSAGE_ALL_RUSERS |
+ VMUSAGE_COL_RUSERS)) == 0)
+ continue;
+ if (result->vmu_type == VMUSAGE_EUSERS &&
+ (flags & (VMUSAGE_ALL_EUSERS |
+ VMUSAGE_COL_EUSERS)) == 0)
+ continue;
+ }
}
count++;
if (out_result != NULL) {
@@ -1904,10 +2024,12 @@ vm_getusage(uint_t flags, time_t age, vmusage_t *buf, size_t *nres, int cpflg)
int cacherecent = 0;
hrtime_t now;
uint_t flags_orig;
+ id_t req_zone_id;
/*
* Non-global zones cannot request system wide and/or collated
- * results, or the system result, so munge the flags accordingly.
+ * results, or the system result, or usage of another zone, so munge
+ * the flags accordingly.
*/
flags_orig = flags;
if (curproc->p_zone != global_zone) {
@@ -1927,6 +2049,10 @@ vm_getusage(uint_t flags, time_t age, vmusage_t *buf, size_t *nres, int cpflg)
flags &= ~VMUSAGE_SYSTEM;
flags |= VMUSAGE_ZONE;
}
+ if (flags & VMUSAGE_A_ZONE) {
+ flags &= ~VMUSAGE_A_ZONE;
+ flags |= VMUSAGE_ZONE;
+ }
}
/* Check for unknown flags */
@@ -1937,6 +2063,21 @@ vm_getusage(uint_t flags, time_t age, vmusage_t *buf, size_t *nres, int cpflg)
if ((flags & VMUSAGE_MASK) == 0)
return (set_errno(EINVAL));
+ /* If requesting results for a specific zone, get the zone ID */
+ if (flags & VMUSAGE_A_ZONE) {
+ size_t bufsize;
+ vmusage_t zreq;
+
+ if (ddi_copyin((caddr_t)nres, &bufsize, sizeof (size_t), cpflg))
+ return (set_errno(EFAULT));
+ /* Requested zone ID is passed in buf, so 0 len not allowed */
+ if (bufsize == 0)
+ return (set_errno(EINVAL));
+ if (ddi_copyin((caddr_t)buf, &zreq, sizeof (vmusage_t), cpflg))
+ return (set_errno(EFAULT));
+ req_zone_id = zreq.vmu_id;
+ }
+
mutex_enter(&vmu_data.vmu_lock);
now = gethrtime();
@@ -1956,7 +2097,7 @@ start:
mutex_exit(&vmu_data.vmu_lock);
ret = vmu_copyout_results(cache, buf, nres, flags_orig,
- cpflg);
+ req_zone_id, cpflg);
mutex_enter(&vmu_data.vmu_lock);
vmu_cache_rele(cache);
if (vmu_data.vmu_pending_waiters > 0)
@@ -2013,7 +2154,8 @@ start:
mutex_exit(&vmu_data.vmu_lock);
/* copy cache */
- ret = vmu_copyout_results(cache, buf, nres, flags_orig, cpflg);
+ ret = vmu_copyout_results(cache, buf, nres, flags_orig,
+ req_zone_id, cpflg);
mutex_enter(&vmu_data.vmu_lock);
vmu_cache_rele(cache);
mutex_exit(&vmu_data.vmu_lock);
diff --git a/usr/src/uts/i86pc/Makefile.i86pc b/usr/src/uts/i86pc/Makefile.i86pc
index bed1885700..a398d741dc 100644
--- a/usr/src/uts/i86pc/Makefile.i86pc
+++ b/usr/src/uts/i86pc/Makefile.i86pc
@@ -24,6 +24,7 @@
#
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2013 Andrew Stormont. All rights reserved.
+# Copyright 2019 Joyent, Inc.
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
# Copyright 2019 Joyent, Inc.
#
diff --git a/usr/src/uts/i86pc/dboot/dboot_printf.c b/usr/src/uts/i86pc/dboot/dboot_printf.c
index 9d02c1943a..59d4e247f0 100644
--- a/usr/src/uts/i86pc/dboot/dboot_printf.c
+++ b/usr/src/uts/i86pc/dboot/dboot_printf.c
@@ -203,6 +203,10 @@ unsigned_num:
dboot_putnum(x, B_FALSE, base);
break;
+ case 'z':
+ size = sizeof (size_t);
+ goto again;
+
default:
dboot_puts("dboot_printf(): unknown % escape\n");
}
diff --git a/usr/src/uts/i86pc/dboot/dboot_printf.h b/usr/src/uts/i86pc/dboot/dboot_printf.h
index 22cf561e51..94b3db92e7 100644
--- a/usr/src/uts/i86pc/dboot/dboot_printf.h
+++ b/usr/src/uts/i86pc/dboot/dboot_printf.h
@@ -22,32 +22,29 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2020 Joyent, Inc.
*/
#ifndef _DBOOT_PRINTF_H
#define _DBOOT_PRINTF_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
/*
- * Very primitive printf. This only understands the following simple formats:
- * %%, %c, %s, %d, %ld, %lld, %x, %lx, %llx, %p
+ * Very primitive printf. We mark this as PRINTFLIKE so we can use %z
*/
-/*PRINTFLIKE1*/
extern void dboot_printf(char *fmt, ...)
- __KPRINTFLIKE(1);
+ __PRINTFLIKE(1);
/*
* Primitive version of panic, prints a message, waits for a keystroke,
* then resets the system
*/
-/*PRINTFLIKE1*/
extern void dboot_panic(char *fmt, ...)
- __KPRINTFLIKE(1);
+ __NORETURN __PRINTFLIKE(1);
#ifdef __cplusplus
diff --git a/usr/src/uts/i86pc/dboot/dboot_startkern.c b/usr/src/uts/i86pc/dboot/dboot_startkern.c
index 6621356133..6654244be2 100644
--- a/usr/src/uts/i86pc/dboot/dboot_startkern.c
+++ b/usr/src/uts/i86pc/dboot/dboot_startkern.c
@@ -75,6 +75,10 @@ extern int have_cpuid(void);
#define SHA1_ASCII_LENGTH (SHA1_DIGEST_LENGTH * 2)
+#define ULL(v) ((u_longlong_t)(v))
+
+static void *page_alloc(void);
+
/*
* This file contains code that runs to transition us from either a multiboot
* compliant loader (32 bit non-paging) or a XPV domain loader to
@@ -105,7 +109,10 @@ x86pte_t pte_bits = PT_VALID | PT_REF | PT_WRITABLE | PT_MOD | PT_NOCONSIST;
* virtual address.
*/
paddr_t ktext_phys;
-uint32_t ksize = 2 * FOUR_MEG; /* kernel nucleus is 8Meg */
+/*
+ * Nucleus size is 8Mb, including text, data, and BSS.
+ */
+uint32_t ksize = 2 * FOUR_MEG;
static uint64_t target_kernel_text; /* value to use for KERNEL_TEXT */
@@ -115,9 +122,16 @@ static uint64_t target_kernel_text; /* value to use for KERNEL_TEXT */
char stack_space[STACK_SIZE];
/*
- * Used to track physical memory allocation
+ * The highest address we build page tables for.
*/
-static paddr_t next_avail_addr = 0;
+static paddr_t boot_map_end;
+
+/*
+ * The dboot allocator. This is a small area we use for allocating the
+ * kernel nucleus and pages for the identity page tables we build here.
+ */
+static paddr_t alloc_addr;
+static paddr_t alloc_end;
#if defined(__xpv)
/*
@@ -127,7 +141,6 @@ static paddr_t next_avail_addr = 0;
* to derive a pfn from a pointer, you subtract mfn_base.
*/
-static paddr_t scratch_end = 0; /* we can't write all of mem here */
static paddr_t mfn_base; /* addr corresponding to mfn_list[0] */
start_info_t *xen_info;
@@ -233,6 +246,12 @@ uint_t map_debug = 0;
static char noname[2] = "-";
+static boolean_t
+ranges_intersect(uint64_t s1, uint64_t e1, uint64_t s2, uint64_t e2)
+{
+ return (s1 < e2 && e1 >= s2);
+}
+
/*
* Either hypervisor-specific or grub-specific code builds the initial
* memlists. This code does the sort/merge/link for final use.
@@ -288,8 +307,16 @@ sort_physinstall(void)
if (prom_debug) {
dboot_printf("\nFinal memlists:\n");
for (i = 0; i < memlists_used; ++i) {
- dboot_printf("\t%d: addr=%" PRIx64 " size=%"
- PRIx64 "\n", i, memlists[i].addr, memlists[i].size);
+ dboot_printf("\t%d: 0x%llx-0x%llx size=0x%llx\n",
+ i, ULL(memlists[i].addr), ULL(memlists[i].addr +
+ memlists[i].size), ULL(memlists[i].size));
+ }
+
+ dboot_printf("\nBoot modules:\n");
+ for (i = 0; i < bi->bi_module_cnt; i++) {
+ dboot_printf("\t%d: 0x%llx-0x%llx size=0x%llx\n",
+ i, ULL(modules[i].bm_addr), ULL(modules[i].bm_addr +
+ modules[i].bm_size), ULL(modules[i].bm_size));
}
}
@@ -341,6 +368,8 @@ dboot_halt(void)
while (--i)
(void) HYPERVISOR_yield();
(void) HYPERVISOR_shutdown(SHUTDOWN_poweroff);
+ for (;;)
+ ;
}
/*
@@ -427,7 +456,7 @@ set_pteval(paddr_t table, uint_t index, uint_t level, x86pte_t pteval)
paddr_t
make_ptable(x86pte_t *pteval, uint_t level)
{
- paddr_t new_table = (paddr_t)(uintptr_t)mem_alloc(MMU_PAGESIZE);
+ paddr_t new_table = (paddr_t)(uintptr_t)page_alloc();
if (level == top_level && level == 2)
*pteval = pa_to_ma((uintptr_t)new_table) | PT_VALID;
@@ -659,18 +688,6 @@ exclude_from_pci(uint64_t start, uint64_t end)
}
}
-/*
- * During memory allocation, find the highest address not used yet.
- */
-static void
-check_higher(paddr_t a)
-{
- if (a < next_avail_addr)
- return;
- next_avail_addr = RNDUP(a + 1, MMU_PAGESIZE);
- DBG(next_avail_addr);
-}
-
static int
dboot_loader_mmap_entries(void)
{
@@ -687,7 +704,6 @@ dboot_loader_mmap_entries(void)
DBG(mb_info->mmap_addr);
DBG(mb_info->mmap_length);
- check_higher(mb_info->mmap_addr + mb_info->mmap_length);
for (mmap_addr = mb_info->mmap_addr;
mmap_addr < mb_info->mmap_addr +
@@ -894,17 +910,13 @@ build_pcimemlists(void)
}
#if defined(__xpv)
-/*
- * Initialize memory allocator stuff from hypervisor-supplied start info.
- */
static void
-init_mem_alloc(void)
+init_dboot_alloc(void)
{
int local; /* variables needed to find start region */
- paddr_t scratch_start;
xen_memory_map_t map;
- DBG_MSG("Entered init_mem_alloc()\n");
+ DBG_MSG("Entered init_dboot_alloc()\n");
/*
* Free memory follows the stack. There's at least 512KB of scratch
@@ -913,17 +925,17 @@ init_mem_alloc(void)
* allocated last and will be outside the addressible range. We'll
* switch to new page tables before we unpack the kernel
*/
- scratch_start = RNDUP((paddr_t)(uintptr_t)&local, MMU_PAGESIZE);
- DBG(scratch_start);
- scratch_end = RNDUP((paddr_t)scratch_start + 512 * 1024, TWO_MEG);
- DBG(scratch_end);
+ alloc_addr = RNDUP((paddr_t)(uintptr_t)&local, MMU_PAGESIZE);
+ DBG(alloc_addr);
+ alloc_end = RNDUP((paddr_t)alloc_addr + 512 * 1024, TWO_MEG);
+ DBG(alloc_end);
/*
* For paranoia, leave some space between hypervisor data and ours.
* Use 500 instead of 512.
*/
- next_avail_addr = scratch_end - 500 * 1024;
- DBG(next_avail_addr);
+ alloc_addr = alloc_end - 500 * 1024;
+ DBG(alloc_addr);
/*
* The domain builder gives us at most 1 module
@@ -1271,7 +1283,6 @@ process_module(int midx)
char *cmdline = dboot_multiboot_modcmdline(midx);
char *p, *q;
- check_higher(mod_end);
if (prom_debug) {
dboot_printf("\tmodule #%d: '%s' at 0x%lx, end 0x%lx\n",
midx, cmdline, (ulong_t)mod_start, (ulong_t)mod_end);
@@ -1435,7 +1446,6 @@ static void
dboot_process_modules(void)
{
int i, modcount;
- extern char _end[];
DBG_MSG("\nFinding Modules\n");
modcount = dboot_multiboot_modcount();
@@ -1443,11 +1453,11 @@ dboot_process_modules(void)
dboot_panic("Too many modules (%d) -- the maximum is %d.",
modcount, MAX_BOOT_MODULES);
}
+
/*
* search the modules to find the last used address
* we'll build the module list while we're walking through here
*/
- check_higher((paddr_t)(uintptr_t)&_end);
for (i = 0; i < modcount; ++i) {
process_module(i);
modules_used++;
@@ -1462,6 +1472,80 @@ dboot_process_modules(void)
check_images();
}
+#define CORRUPT_REGION_START 0xc700000
+#define CORRUPT_REGION_SIZE 0x100000
+#define CORRUPT_REGION_END (CORRUPT_REGION_START + CORRUPT_REGION_SIZE)
+
+static void
+dboot_add_memlist(uint64_t start, uint64_t end)
+{
+ if (end > max_mem)
+ max_mem = end;
+
+ /*
+ * Well, this is sad. On some systems, there is a region of memory that
+ * can be corrupted until some number of seconds after we have booted.
+ * And the BIOS doesn't tell us that this memory is unsafe to use. And
+ * we don't know how long it's dangerous. So we'll chop out this range
+ * from any memory list that would otherwise be usable. Note that any
+ * system of this type will give us the new-style (0x40) memlist, so we
+ * need not fix up the other path below.
+ *
+ * However, if we're boot-loaded from something that doesn't have a
+ * RICHMOND-16 workaround (which on many systems is just fine), it could
+ * actually use this region for the boot modules; if we remove it from
+ * the memlist, we'll keel over when trying to access the region.
+ *
+ * So, if we see that a module intersects the region, we presume it's
+ * OK.
+ */
+
+ if (find_boot_prop("disable-RICHMOND-16") != NULL)
+ goto out;
+
+ for (uint32_t i = 0; i < bi->bi_module_cnt; i++) {
+ native_ptr_t mod_start = modules[i].bm_addr;
+ native_ptr_t mod_end = modules[i].bm_addr + modules[i].bm_size;
+
+ if (ranges_intersect(mod_start, mod_end, CORRUPT_REGION_START,
+ CORRUPT_REGION_END)) {
+ if (prom_debug) {
+ dboot_printf("disabling RICHMOND-16 workaround "
+ "due to module #%d: "
+ "name %s addr %lx size %lx\n",
+ i, (char *)(uintptr_t)modules[i].bm_name,
+ (ulong_t)modules[i].bm_addr,
+ (ulong_t)modules[i].bm_size);
+ }
+ goto out;
+ }
+ }
+
+ if (start < CORRUPT_REGION_START && end > CORRUPT_REGION_START) {
+ memlists[memlists_used].addr = start;
+ memlists[memlists_used].size =
+ CORRUPT_REGION_START - start;
+ ++memlists_used;
+ if (end > CORRUPT_REGION_END)
+ start = CORRUPT_REGION_END;
+ else
+ return;
+ }
+
+ if (start >= CORRUPT_REGION_START && start < CORRUPT_REGION_END) {
+ if (end <= CORRUPT_REGION_END)
+ return;
+ start = CORRUPT_REGION_END;
+ }
+
+out:
+ memlists[memlists_used].addr = start;
+ memlists[memlists_used].size = end - start;
+ ++memlists_used;
+ if (memlists_used > MAX_MEMLIST)
+ dboot_panic("too many memlists");
+}
+
/*
* We then build the phys_install memlist from the multiboot information.
*/
@@ -1505,13 +1589,7 @@ dboot_process_mmap(void)
*/
switch (type) {
case 1:
- if (end > max_mem)
- max_mem = end;
- memlists[memlists_used].addr = start;
- memlists[memlists_used].size = end - start;
- ++memlists_used;
- if (memlists_used > MAX_MEMLIST)
- dboot_panic("too many memlists");
+ dboot_add_memlist(start, end);
break;
case 2:
rsvdmemlists[rsvdmemlists_used].addr = start;
@@ -1593,21 +1671,15 @@ dboot_multiboot1_highest_addr(void)
return (addr);
}
-static void
+static uint64_t
dboot_multiboot_highest_addr(void)
{
- paddr_t addr;
-
switch (multiboot_version) {
case 1:
- addr = dboot_multiboot1_highest_addr();
- if (addr != (paddr_t)(uintptr_t)NULL)
- check_higher(addr);
+ return (dboot_multiboot1_highest_addr());
break;
case 2:
- addr = dboot_multiboot2_highest_addr(mb2_info);
- if (addr != (paddr_t)(uintptr_t)NULL)
- check_higher(addr);
+ return (dboot_multiboot2_highest_addr(mb2_info));
break;
default:
dboot_panic("Unknown multiboot version: %d\n",
@@ -1617,15 +1689,97 @@ dboot_multiboot_highest_addr(void)
}
/*
- * Walk the boot loader provided information and find the highest free address.
+ * Set up our simple physical memory allocator. This is used to allocate both
+ * the kernel nucleus (ksize) and our page table pages.
+ *
+ * We need to find a contiguous region in the memlists that is below 4Gb (as
+ * we're 32-bit and need to use the addresses), and isn't otherwise in use by
+ * dboot, multiboot allocations, or boot modules. The memlist is sorted and
+ * merged by this point.
+ *
+ * Historically, this code always did the allocations past the end of the
+ * highest used address, even if there was space below. For reasons unclear, if
+ * we don't do this, then we get massive corruption during early kernel boot.
+ *
+ * Note that find_kalloc_start() starts its search at the end of this
+ * allocation.
+ *
+ * This all falls apart horribly on some EFI systems booting under iPXE, where
+ * we end up with boot module allocation such that there is no room between the
+ * highest used address and our 4Gb limit. To that end, we have an iPXE hack
+ * that limits the maximum address used by its allocations in an attempt to give
+ * us room.
*/
static void
-init_mem_alloc(void)
+init_dboot_alloc(void)
{
- DBG_MSG("Entered init_mem_alloc()\n");
+ extern char _end[];
+
+ DBG_MSG("Entered init_dboot_alloc()\n");
+
dboot_process_modules();
dboot_process_mmap();
- dboot_multiboot_highest_addr();
+
+ size_t align = FOUR_MEG;
+
+ /*
+ * We need enough alloc space for the nucleus memory...
+ */
+ size_t size = RNDUP(ksize, align);
+
+ /*
+ * And enough page table pages to cover potentially 4Gb. Each leaf PT
+ * covers 2Mb, so we need a maximum of 2048 pages for those. Next level
+ * up each covers 1Gb, and so on, so we'll just add a little slop (which
+ * gets aligned up anyway).
+ */
+ size += RNDUP(MMU_PAGESIZE * (2048 + 256), align);
+
+ uint64_t start = MAX(dboot_multiboot_highest_addr(),
+ (paddr_t)(uintptr_t)&_end);
+ start = RNDUP(start, align);
+
+ /*
+ * As mentioned above, only start our search after all the boot modules.
+ */
+ for (uint_t i = 0; i < bi->bi_module_cnt; i++) {
+ native_ptr_t mod_end = modules[i].bm_addr + modules[i].bm_size;
+
+ start = MAX(start, RNDUP(mod_end, MMU_PAGESIZE));
+ }
+
+ uint64_t end = start + size;
+
+ DBG(start);
+ DBG(end);
+
+ for (uint_t i = 0; i < memlists_used; i++) {
+ uint64_t ml_start = memlists[i].addr;
+ uint64_t ml_end = memlists[i].addr + memlists[i].size;
+
+ /*
+ * If we're past our starting point for search, begin at this
+ * memlist.
+ */
+ if (start < ml_start) {
+ start = RNDUP(ml_start, align);
+ end = start + size;
+ }
+
+ if (end >= (uint64_t)UINT32_MAX) {
+ dboot_panic("couldn't find alloc space below 4Gb");
+ }
+
+ if (end < ml_end) {
+ alloc_addr = start;
+ alloc_end = end;
+ DBG(alloc_addr);
+ DBG(alloc_end);
+ return;
+ }
+ }
+
+ dboot_panic("couldn't find alloc space in memlists");
}
static int
@@ -1869,77 +2023,89 @@ print_efi64(EFI_SYSTEM_TABLE64 *efi)
#endif /* !__xpv */
/*
- * Simple memory allocator, allocates aligned physical memory.
- * Note that startup_kernel() only allocates memory, never frees.
- * Memory usage just grows in an upward direction.
+ * Simple memory allocator for aligned physical memory from the area provided by
+ * init_dboot_alloc(). This is a simple bump allocator, and it's never directly
+ * freed by dboot.
*/
static void *
-do_mem_alloc(uint32_t size, uint32_t align)
+dboot_alloc(uint32_t size, uint32_t align)
{
- uint_t i;
- uint64_t best;
- uint64_t start;
- uint64_t end;
+ uint32_t start = RNDUP(alloc_addr, align);
- /*
- * make sure size is a multiple of pagesize
- */
size = RNDUP(size, MMU_PAGESIZE);
- next_avail_addr = RNDUP(next_avail_addr, align);
- /*
- * XXPV fixme joe
- *
- * a really large bootarchive that causes you to run out of memory
- * may cause this to blow up
- */
- /* LINTED E_UNEXPECTED_UINT_PROMOTION */
- best = (uint64_t)-size;
- for (i = 0; i < memlists_used; ++i) {
- start = memlists[i].addr;
-#if defined(__xpv)
- start += mfn_base;
-#endif
- end = start + memlists[i].size;
+ if (start + size > alloc_end) {
+ dboot_panic("%s: couldn't allocate 0x%x bytes aligned 0x%x "
+ "alloc_addr = 0x%llx, alloc_end = 0x%llx", __func__,
+ size, align, (u_longlong_t)alloc_addr,
+ (u_longlong_t)alloc_end);
+ }
- /*
- * did we find the desired address?
- */
- if (start <= next_avail_addr && next_avail_addr + size <= end) {
- best = next_avail_addr;
- goto done;
- }
+ alloc_addr = start + size;
- /*
- * if not is this address the best so far?
- */
- if (start > next_avail_addr && start < best &&
- RNDUP(start, align) + size <= end)
- best = RNDUP(start, align);
+ if (map_debug) {
+ dboot_printf("%s(0x%x, 0x%x) = 0x%x\n", __func__, size,
+ align, start);
}
- /*
- * We didn't find exactly the address we wanted, due to going off the
- * end of a memory region. Return the best found memory address.
- */
-done:
- next_avail_addr = best + size;
-#if defined(__xpv)
- if (next_avail_addr > scratch_end)
- dboot_panic("Out of mem next_avail: 0x%lx, scratch_end: "
- "0x%lx", (ulong_t)next_avail_addr,
- (ulong_t)scratch_end);
-#endif
- (void) memset((void *)(uintptr_t)best, 0, size);
- return ((void *)(uintptr_t)best);
+ (void) memset((void *)(uintptr_t)start, 0, size);
+ return ((void *)(uintptr_t)start);
}
-void *
-mem_alloc(uint32_t size)
+static void *
+page_alloc(void)
{
- return (do_mem_alloc(size, MMU_PAGESIZE));
+ return (dboot_alloc(MMU_PAGESIZE, MMU_PAGESIZE));
}
+/*
+ * This is where we tell the kernel to start physical allocations from, beyond
+ * the end of our allocation area and all boot modules. It might be beyond 4Gb,
+ * so we can't touch that area ourselves.
+ *
+ * We might set kalloc_start to the end of a memlist; if so make sure we skip it
+ * along to the next one.
+ *
+ * This is making the massive assumption that there is a suitably large area for
+ * kernel allocations past the end of the last boot module and the dboot
+ * allocated region. Worse, we don't have a simple way to assert that is so.
+ */
+static paddr_t
+find_kalloc_start(void)
+{
+ paddr_t kalloc_start = alloc_end;
+ uint_t i;
+
+ for (i = 0; i < bi->bi_module_cnt; i++) {
+ native_ptr_t mod_end = modules[i].bm_addr + modules[i].bm_size;
+
+ kalloc_start = MAX(kalloc_start, RNDUP(mod_end, MMU_PAGESIZE));
+ }
+
+ boot_map_end = kalloc_start;
+ DBG(boot_map_end);
+
+ for (i = 0; i < memlists_used; i++) {
+ uint64_t ml_start = memlists[i].addr;
+ uint64_t ml_end = memlists[i].addr + memlists[i].size;
+
+ if (kalloc_start >= ml_end)
+ continue;
+
+ if (kalloc_start < ml_start)
+ kalloc_start = ml_start;
+ break;
+ }
+
+ if (i == memlists_used) {
+ dboot_panic("fell off the end of memlists finding a "
+ "kalloc_start value > 0x%llx", (u_longlong_t)kalloc_start);
+ }
+
+ DBG(kalloc_start);
+
+ return (kalloc_start);
+}
/*
* Build page tables to map all of memory used so far as well as the kernel.
@@ -1962,7 +2128,7 @@ build_page_tables(void)
#if defined(__xpv)
top_page_table = (paddr_t)(uintptr_t)xen_info->pt_base;
#else /* __xpv */
- top_page_table = (paddr_t)(uintptr_t)mem_alloc(MMU_PAGESIZE);
+ top_page_table = (paddr_t)(uintptr_t)page_alloc();
#endif /* __xpv */
DBG((uintptr_t)top_page_table);
@@ -1988,7 +2154,7 @@ build_page_tables(void)
/*
* The kernel will need a 1 page window to work with page tables
*/
- bi->bi_pt_window = (native_ptr_t)(uintptr_t)mem_alloc(MMU_PAGESIZE);
+ bi->bi_pt_window = (native_ptr_t)(uintptr_t)page_alloc();
DBG(bi->bi_pt_window);
bi->bi_pte_to_pt_window =
(native_ptr_t)(uintptr_t)find_pte(bi->bi_pt_window, NULL, 0, 0);
@@ -2029,6 +2195,10 @@ build_page_tables(void)
#if !defined(__xpv)
+ /*
+ * Map every valid memlist address up until boot_map_end: this will
+ * cover at least our alloc region and all boot modules.
+ */
for (i = 0; i < memlists_used; ++i) {
start = memlists[i].addr;
end = start + memlists[i].size;
@@ -2036,11 +2206,11 @@ build_page_tables(void)
if (map_debug)
dboot_printf("1:1 map pa=%" PRIx64 "..%" PRIx64 "\n",
start, end);
- while (start < end && start < next_avail_addr) {
+ while (start < end && start < boot_map_end) {
map_pa_at_va(start, start, 0);
start += MMU_PAGESIZE;
}
- if (start >= next_avail_addr)
+ if (start >= boot_map_end)
break;
}
@@ -2302,7 +2472,9 @@ startup_kernel(void)
/*
* Need correct target_kernel_text value
*/
+#if defined(_BOOT_TARGET_amd64)
target_kernel_text = KERNEL_TEXT;
+#endif
DBG(target_kernel_text);
#if defined(__xpv)
@@ -2462,7 +2634,7 @@ startup_kernel(void)
/*
* initialize the simple memory allocator
*/
- init_mem_alloc();
+ init_dboot_alloc();
#if !defined(__xpv) && !defined(_BOOT_TARGET_amd64)
/*
@@ -2516,7 +2688,7 @@ startup_kernel(void)
* For grub, copy kernel bits from the ELF64 file to final place.
*/
DBG_MSG("\nAllocating nucleus pages.\n");
- ktext_phys = (uintptr_t)do_mem_alloc(ksize, FOUR_MEG);
+ ktext_phys = (uintptr_t)dboot_alloc(ksize, FOUR_MEG);
if (ktext_phys == 0)
dboot_panic("failed to allocate aligned kernel memory");
@@ -2527,6 +2699,8 @@ startup_kernel(void)
DBG(ktext_phys);
+ paddr_t kalloc_start = find_kalloc_start();
+
/*
* Allocate page tables.
*/
@@ -2544,18 +2718,18 @@ startup_kernel(void)
#if defined(__xpv)
- bi->bi_next_paddr = next_avail_addr - mfn_base;
+ bi->bi_next_paddr = kalloc_start - mfn_base;
DBG(bi->bi_next_paddr);
- bi->bi_next_vaddr = (native_ptr_t)(uintptr_t)next_avail_addr;
+ bi->bi_next_vaddr = (native_ptr_t)kalloc_start;
DBG(bi->bi_next_vaddr);
/*
* unmap unused pages in start area to make them available for DMA
*/
- while (next_avail_addr < scratch_end) {
- (void) HYPERVISOR_update_va_mapping(next_avail_addr,
+ while (alloc_addr < alloc_end) {
+ (void) HYPERVISOR_update_va_mapping(alloc_addr,
0, UVMF_INVLPG | UVMF_LOCAL);
- next_avail_addr += MMU_PAGESIZE;
+ alloc_addr += MMU_PAGESIZE;
}
bi->bi_xen_start_info = (native_ptr_t)(uintptr_t)xen_info;
@@ -2565,9 +2739,9 @@ startup_kernel(void)
#else /* __xpv */
- bi->bi_next_paddr = next_avail_addr;
+ bi->bi_next_paddr = kalloc_start;
DBG(bi->bi_next_paddr);
- bi->bi_next_vaddr = (native_ptr_t)(uintptr_t)next_avail_addr;
+ bi->bi_next_vaddr = (native_ptr_t)kalloc_start;
DBG(bi->bi_next_vaddr);
bi->bi_mb_version = multiboot_version;
diff --git a/usr/src/uts/i86pc/dboot/dboot_xboot.h b/usr/src/uts/i86pc/dboot/dboot_xboot.h
index 7d0876c79c..f261f3f2b1 100644
--- a/usr/src/uts/i86pc/dboot/dboot_xboot.h
+++ b/usr/src/uts/i86pc/dboot/dboot_xboot.h
@@ -22,6 +22,8 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2020 Joyent, Inc.
*/
#ifndef _DBOOT_XBOOT_H
@@ -52,16 +54,14 @@ extern uint_t prom_debug;
#define DBG_MSG(s) do { if (prom_debug) \
dboot_printf(s); \
- _NOTE(CONSTANTCONDITION) \
} while (0)
-#define DBG(x) do { if (prom_debug) { \
- dboot_printf("%s is 0x%" PRIx64 "\n", #x, (uint64_t)(x)); \
- _NOTE(CONSTANTCONDITION) \
+#define DBG(x) do { if (prom_debug) { \
+ dboot_printf("%s: %s is 0x%" PRIx64 "\n", \
+ __func__, #x, (uint64_t)(x)); \
} } while (0)
-extern void dboot_halt(void);
-extern void *mem_alloc(uint32_t size);
+extern void dboot_halt(void) __NORETURN;
#define RNDUP(x, y) (((x) + ((y) - 1ul)) & ~((y) - 1ul))
diff --git a/usr/src/uts/i86pc/io/mp_platform_common.c b/usr/src/uts/i86pc/io/mp_platform_common.c
index bff745b483..54a0ac3506 100644
--- a/usr/src/uts/i86pc/io/mp_platform_common.c
+++ b/usr/src/uts/i86pc/io/mp_platform_common.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2016 Nexenta Systems, Inc.
+ * Copyright 2017 Joyent, Inc.
* Copyright (c) 2017 by Delphix. All rights reserved.
* Copyright 2020 Joyent, Inc.
* Copyright 2020 RackTop Systems, Inc.
diff --git a/usr/src/uts/i86pc/io/psm/psm_common.c b/usr/src/uts/i86pc/io/psm/psm_common.c
index b59d87bdcc..623c6e5617 100644
--- a/usr/src/uts/i86pc/io/psm/psm_common.c
+++ b/usr/src/uts/i86pc/io/psm/psm_common.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
*/
#include <sys/types.h>
diff --git a/usr/src/uts/i86pc/ml/kpti_trampolines.s b/usr/src/uts/i86pc/ml/kpti_trampolines.s
index 4b5102d547..17249eb747 100644
--- a/usr/src/uts/i86pc/ml/kpti_trampolines.s
+++ b/usr/src/uts/i86pc/ml/kpti_trampolines.s
@@ -667,6 +667,8 @@ tr_intr_ret_end:
MK_INTR_TRAMPOLINE_NOERR(invaltrap)
MK_INTR_TRAMPOLINE_NOERR(fasttrap)
MK_INTR_TRAMPOLINE_NOERR(dtrace_ret)
+ MK_INTR_TRAMPOLINE_NOERR(brand_sys_int80)
+ MK_INTR_TRAMPOLINE_NOERR(sys_int80)
/*
* These are special because they can interrupt other traps, and
diff --git a/usr/src/uts/i86pc/ml/offsets.in b/usr/src/uts/i86pc/ml/offsets.in
index 622f7cd2a3..6c1de5c145 100644
--- a/usr/src/uts/i86pc/ml/offsets.in
+++ b/usr/src/uts/i86pc/ml/offsets.in
@@ -144,6 +144,7 @@ _klwp
lwp_thread
lwp_procp
lwp_brand
+ lwp_brand_syscall
lwp_eosys
lwp_regs
lwp_arg
diff --git a/usr/src/uts/i86pc/ml/syscall_asm_amd64.s b/usr/src/uts/i86pc/ml/syscall_asm_amd64.s
index 8a68b4bced..8040e35297 100644
--- a/usr/src/uts/i86pc/ml/syscall_asm_amd64.s
+++ b/usr/src/uts/i86pc/ml/syscall_asm_amd64.s
@@ -515,6 +515,7 @@ noprod_sys_syscall:
movq T_LWP(%r15), %r14
ASSERT_NO_RUPDATE_PENDING(%r14)
+
ENABLE_INTR_FLAGS
MSTATE_TRANSITION(LMS_USER, LMS_SYSTEM)
@@ -528,6 +529,37 @@ noprod_sys_syscall:
incq %gs:CPU_STATS_SYS_SYSCALL
+ /*
+ * If our LWP has an alternate system call handler, run that instead of
+ * the regular system call path.
+ */
+ movq LWP_BRAND_SYSCALL(%r14), %rdi
+ testq %rdi, %rdi
+ jz _syscall_no_brand
+
+ pushq %rax
+ subq $8, %rsp /* align stack for call to C */
+ INDIRECT_CALL_REG(rdi)
+ addq $8, %rsp
+
+ /*
+ * If the alternate handler returns non-zero, the normal system call
+ * processing is resumed.
+ */
+ testl %eax, %eax
+ popq %rax
+ jnz _syscall_no_brand
+
+ /*
+ * For branded syscalls which were handled in-kernel, shuffle the
+ * register state as would be done by the native handler before jumping
+ * to the post-syscall logic.
+ */
+ movq REGOFF_RAX(%rsp), %r12
+ movq REGOFF_RDX(%rsp), %r13
+ jmp _syscall_after_brand
+
+_syscall_no_brand:
movw %ax, T_SYSNUM(%r15)
movzbl T_PRE_SYS(%r15), %ebx
ORL_SYSCALLTRACE(%ebx)
@@ -563,6 +595,8 @@ _syscall_invoke:
shrq $32, %r13 /* upper 32-bits into %edx */
movl %r12d, %r12d /* lower 32-bits into %eax */
5:
+
+_syscall_after_brand:
/*
* Optimistically assume that there's no post-syscall
* work to do. (This is to avoid having to call syscall_mstate()
@@ -825,11 +859,46 @@ _syscall32_save:
incq %gs:CPU_STATS_SYS_SYSCALL
/*
+ * If our lwp has an alternate system call handler, run that instead
+ * of the regular system call path.
+ */
+ movq LWP_BRAND_SYSCALL(%r14), %rax
+ testq %rax, %rax
+ jz _syscall32_no_brand
+
+ movb $LWP_SYS, LWP_STATE(%r14)
+ INDIRECT_CALL_REG(rax)
+
+ /*
+ * If the alternate handler returns non-zero, the normal system call
+ * processing is resumed.
+ */
+ testl %eax, %eax
+ jnz _syscall32_no_brand
+
+ /*
+ * For branded syscalls which were handled in-kernel, shuffle the
+ * register state as would be done by the native handler before jumping
+ * to the post-syscall logic.
+ */
+ movl REGOFF_RAX(%rsp), %r12d
+ movl REGOFF_RDX(%rsp), %r13d
+ jmp _syscall32_after_brand
+
+_syscall32_no_brand:
+ /*
* Make some space for MAXSYSARGS (currently 8) 32-bit args placed
* into 64-bit (long) arg slots, maintaining 16 byte alignment. Or
* more succinctly:
*
* SA(MAXSYSARGS * sizeof (long)) == 64
+ *
+ * Note, this space is used both to copy in the arguments from user
+ * land, but also to as part of the old UNIX style syscall_ap() method.
+ * syscall_entry expects that we do not change the values of this space
+ * that we give it. However, this means that when we end up in the more
+ * recent model of passing the arguments based on the calling
+ * conventions, we'll need to save an additional 16 bytes of stack.
*/
#define SYS_DROP 64 /* drop for args */
subq $SYS_DROP, %rsp
@@ -857,12 +926,16 @@ _syscall32_save:
*/
movq %rax, %rbx
- movl 0(%rsp), %edi
- movl 8(%rsp), %esi
- movl 0x10(%rsp), %edx
- movl 0x18(%rsp), %ecx
- movl 0x20(%rsp), %r8d
- movl 0x28(%rsp), %r9d
+ movl 0x0(%rsp), %edi /* arg0 */
+ movl 0x8(%rsp), %esi /* arg1 */
+ movl 0x10(%rsp), %edx /* arg2 */
+ movl 0x38(%rsp), %eax /* arg7 load */
+ movl 0x18(%rsp), %ecx /* arg3 */
+ pushq %rax /* arg7 saved to stack */
+ movl 0x28(%rsp), %r8d /* arg4 */
+ movl 0x38(%rsp), %eax /* arg6 load */
+ movl 0x30(%rsp), %r9d /* arg5 */
+ pushq %rax /* arg6 saved to stack */
movq SY_CALLC(%rbx), %rax
INDIRECT_CALL_REG(rax)
@@ -881,6 +954,8 @@ _syscall32_save:
shrq $32, %r13 /* upper 32-bits into %edx */
movl %eax, %r12d /* lower 32-bits into %eax */
+_syscall32_after_brand:
+
/*
* Optimistically assume that there's no post-syscall
* work to do. (This is to avoid having to call syscall_mstate()
@@ -1133,15 +1208,20 @@ _full_syscall_postsys32:
/*
* Fetch the arguments copied onto the kernel stack and put
* them in the right registers to invoke a C-style syscall handler.
- * %rax contains the handler address.
+ * %rax contains the handler address. For the last two arguments, we
+ * push them onto the stack -- we can't clobber the old arguments.
*/
movq %rax, %rbx
- movl 0(%rsp), %edi
- movl 8(%rsp), %esi
- movl 0x10(%rsp), %edx
- movl 0x18(%rsp), %ecx
- movl 0x20(%rsp), %r8d
- movl 0x28(%rsp), %r9d
+ movl 0x0(%rsp), %edi /* arg0 */
+ movl 0x8(%rsp), %esi /* arg1 */
+ movl 0x10(%rsp), %edx /* arg2 */
+ movl 0x38(%rsp), %eax /* arg7 load */
+ movl 0x18(%rsp), %ecx /* arg3 */
+ pushq %rax /* arg7 saved to stack */
+ movl 0x28(%rsp), %r8d /* arg4 */
+ movl 0x38(%rsp), %eax /* arg6 load */
+ movl 0x30(%rsp), %r9d /* arg5 */
+ pushq %rax /* arg6 saved to stack */
movq SY_CALLC(%rbx), %rax
INDIRECT_CALL_REG(rax)
@@ -1220,6 +1300,66 @@ _full_syscall_postsys32:
SET_SIZE(brand_sys_sysenter)
/*
+ * System call via an int80. This entry point is only used by the Linux
+ * application environment. Unlike the other entry points, there is no
+ * default action to take if no callback is registered for this process.
+ */
+
+ ENTRY_NP(brand_sys_int80)
+ SWAPGS /* kernel gsbase */
+ XPV_TRAP_POP
+ call smap_enable
+
+ /*
+ * We first attempt to call the "b_int80" handler from the "struct
+ * brand_mach_ops" for this brand. If no handler function is installed
+ * for this brand, the BRAND_CALLBACK() macro returns here and we
+ * check the lwp for a "lwp_brand_syscall" handler.
+ */
+ BRAND_CALLBACK(BRAND_CB_INT80, BRAND_URET_FROM_INTR_STACK())
+
+ /*
+ * Check to see if this lwp provides "lwp_brand_syscall". If so, we
+ * will route this int80 through the regular system call handling path.
+ */
+ movq %r15, %gs:CPU_RTMP_R15
+ movq %gs:CPU_THREAD, %r15
+ movq T_LWP(%r15), %r15
+ movq LWP_BRAND_SYSCALL(%r15), %r15
+ testq %r15, %r15
+ movq %gs:CPU_RTMP_R15, %r15
+ jnz nopop_syscall_int
+
+ /*
+ * The brand provided neither a "b_int80", nor a "lwp_brand_syscall"
+ * function, and has thus opted out of handling this trap.
+ */
+ SWAPGS /* user gsbase */
+ jmp nopop_int80
+
+ ENTRY_NP(sys_int80)
+ /*
+ * We hit an int80, but this process isn't of a brand with an int80
+ * handler. Bad process! Make it look as if the INT failed.
+ * Modify %rip to point before the INT, push the expected error
+ * code and fake a GP fault. Note on 64-bit hypervisor we need
+ * to undo the XPV_TRAP_POP and push rcx and r11 back on the stack
+ * because gptrap will pop them again with its own XPV_TRAP_POP.
+ */
+ XPV_TRAP_POP
+ call smap_enable
+nopop_int80:
+ subq $2, (%rsp) /* int insn 2-bytes */
+ pushq $_CONST(_MUL(T_INT80, GATE_DESC_SIZE) + 2)
+#if defined(__xpv)
+ push %r11
+ push %rcx
+#endif
+ jmp gptrap / GP fault
+ SET_SIZE(sys_int80)
+ SET_SIZE(brand_sys_int80)
+
+/*
* This is the destination of the "int $T_SYSCALLINT" interrupt gate, used by
* the generic i386 libc to do system calls. We do a small amount of setup
* before jumping into the existing sys_syscall32 path.
diff --git a/usr/src/uts/i86pc/os/cpr_impl.c b/usr/src/uts/i86pc/os/cpr_impl.c
index 4a5c71b35d..e878f765ef 100644
--- a/usr/src/uts/i86pc/os/cpr_impl.c
+++ b/usr/src/uts/i86pc/os/cpr_impl.c
@@ -23,6 +23,10 @@
*/
/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
* Platform specific implementation code
* Currently only suspend to RAM is supported (ACPI S3)
*/
@@ -737,6 +741,20 @@ i_cpr_is_supported(int sleeptype)
if (sleeptype != CPR_TORAM)
return (0);
+ /*
+ * Unfortunately, the x86 resume code was never implemented for GAS.
+ * The only obvious problem is that a trick necessary to appease Sun
+ * Studio does the wrong thing for GAS. Doubley unfortunate is that
+ * the condition used to detect GAS is incorrect, so we do in fact
+ * compile the Studio path, it just immediately fails in resume.
+ *
+ * Given that, if we were built using GCC, never allow CPR to be
+ * attempted.
+ */
+#ifdef __GNUC__
+ return (0);
+#else
+
/*
* The next statement tests if a specific platform has turned off
* cpr support.
@@ -751,6 +769,7 @@ i_cpr_is_supported(int sleeptype)
return (1);
return (pm_S3_enabled);
+#endif
}
void
diff --git a/usr/src/uts/i86pc/os/ibft.c b/usr/src/uts/i86pc/os/ibft.c
index d9ed882705..fab1324787 100644
--- a/usr/src/uts/i86pc/os/ibft.c
+++ b/usr/src/uts/i86pc/os/ibft.c
@@ -39,6 +39,7 @@
#include <sys/kmem.h>
#include <sys/psm.h>
#include <sys/bootconf.h>
+#include <sys/reboot.h>
typedef enum ibft_structure_type {
Reserved = 0,
@@ -206,6 +207,7 @@ static ibft_status_t iscsi_parse_ibft_NIC(iscsi_ibft_nic_t *nicp);
static ibft_status_t iscsi_parse_ibft_target(char *begin_of_ibft,
iscsi_ibft_tgt_t *tgtp);
+extern int boothowto;
/*
* Return value:
@@ -759,7 +761,9 @@ ld_ib_prop()
* 1) pass "-B ibft-noprobe=1" on kernel command line
* 2) add line "set ibft_noprobe=1" in /etc/system
*/
- cmn_err(CE_NOTE, IBFT_NOPROBE_MSG);
+ if (boothowto & RB_VERBOSE) {
+ cmn_err(CE_NOTE, IBFT_NOPROBE_MSG);
+ }
return;
}
diff --git a/usr/src/uts/i86pc/os/lgrpplat.c b/usr/src/uts/i86pc/os/lgrpplat.c
index ed463fba8f..6320c0a949 100644
--- a/usr/src/uts/i86pc/os/lgrpplat.c
+++ b/usr/src/uts/i86pc/os/lgrpplat.c
@@ -2800,7 +2800,11 @@ lgrp_plat_process_sli(uint32_t domain_id, uchar_t *sli_info,
/*
* Read ACPI System Resource Affinity Table (SRAT) to determine which CPUs
* and memory are local to each other in the same NUMA node and return number
- * of nodes
+ * of nodes.
+ *
+ * The SRAT table pointer is populated during bootup by
+ * build_firmware_properties() in fakebop.c. Several motherboard and BIOS
+ * manufacturers are guilty of not having a SRAT table.
*/
static int
lgrp_plat_process_srat(ACPI_TABLE_SRAT *tp, ACPI_TABLE_MSCT *mp,
@@ -2817,9 +2821,15 @@ lgrp_plat_process_srat(ACPI_TABLE_SRAT *tp, ACPI_TABLE_MSCT *mp,
/*
* Nothing to do when no SRAT or disabled
*/
- if (tp == NULL || !lgrp_plat_srat_enable)
+ if (!lgrp_plat_srat_enable)
return (-1);
+ if (tp == NULL) {
+ cmn_err(CE_WARN, "Couldn't read ACPI SRAT table from BIOS. "
+ "lgrp support will be limited to one group.\n");
+ return (-1);
+ }
+
/*
* Try to get domain information from MSCT table.
* ACPI4.0: OSPM will use information provided by the MSCT only
diff --git a/usr/src/uts/i86pc/os/startup.c b/usr/src/uts/i86pc/os/startup.c
index a0bb296e70..e1e92ffe4f 100644
--- a/usr/src/uts/i86pc/os/startup.c
+++ b/usr/src/uts/i86pc/os/startup.c
@@ -2450,6 +2450,7 @@ add_physmem_cb(page_t *pp, pfn_t pnum)
pp->p_mapping = NULL;
pp->p_embed = 0;
pp->p_share = 0;
+ pp->p_zoneid = ALL_ZONES;
pp->p_mlentry = 0;
}
diff --git a/usr/src/uts/i86pc/os/trap.c b/usr/src/uts/i86pc/os/trap.c
index b7c18bb8c9..063fac49f7 100644
--- a/usr/src/uts/i86pc/os/trap.c
+++ b/usr/src/uts/i86pc/os/trap.c
@@ -99,6 +99,7 @@
#include <sys/hypervisor.h>
#endif
#include <sys/contract/process_impl.h>
+#include <sys/brand.h>
#define USER 0x10000 /* user-mode flag added to trap type */
@@ -810,6 +811,17 @@ trap(struct regs *rp, caddr_t addr, processorid_t cpuid)
fault_type = F_INVAL;
}
+ /*
+ * Allow the brand to interpose on invalid memory accesses
+ * prior to running the native pagefault handler. If this
+ * brand hook returns zero, it was able to handle the fault
+ * completely. Otherwise, drive on and call pagefault().
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_pagefault != NULL &&
+ BROP(p)->b_pagefault(p, lwp, addr, fault_type, rw) == 0) {
+ goto out;
+ }
+
res = pagefault(addr, fault_type, rw, 0);
/*
diff --git a/usr/src/uts/i86pc/sys/apic.h b/usr/src/uts/i86pc/sys/apic.h
index 26626ec5a4..f2528a632f 100644
--- a/usr/src/uts/i86pc/sys/apic.h
+++ b/usr/src/uts/i86pc/sys/apic.h
@@ -386,7 +386,7 @@ struct apic_io_intr {
/* special or reserve vectors */
#define APIC_CHECK_RESERVE_VECTORS(v) \
(((v) == T_FASTTRAP) || ((v) == APIC_SPUR_INTR) || \
- ((v) == T_SYSCALLINT) || ((v) == T_DTRACE_RET))
+ ((v) == T_SYSCALLINT) || ((v) == T_DTRACE_RET) || ((v) == 0x80))
/* cmos shutdown code for BIOS */
#define BIOS_SHUTDOWN 0x0a
diff --git a/usr/src/uts/i86pc/sys/comm_page.h b/usr/src/uts/i86pc/sys/comm_page.h
index 520ad9001d..ea19c856a8 100644
--- a/usr/src/uts/i86pc/sys/comm_page.h
+++ b/usr/src/uts/i86pc/sys/comm_page.h
@@ -27,6 +27,7 @@ extern "C" {
#endif
#define COMM_PAGE_SIZE PAGESIZE
+#define COMM_PAGE_ALIGN 0x4000
#ifndef _ASM
diff --git a/usr/src/uts/i86pc/sys/vm_machparam.h b/usr/src/uts/i86pc/sys/vm_machparam.h
index 90a5245217..0d0c95535c 100644
--- a/usr/src/uts/i86pc/sys/vm_machparam.h
+++ b/usr/src/uts/i86pc/sys/vm_machparam.h
@@ -23,6 +23,7 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc.
*/
#ifndef _SYS_VM_MACHPARAM_H
@@ -129,11 +130,12 @@ extern "C" {
*
* XXX - The system doesn't account for multiple swap devices.
*/
-#define DISKRPM 60
+#define DISKRPM 600
/*
* The maximum value for handspreadpages which is the the distance
- * between the two clock hands in pages.
+ * between the two clock hands in pages. This is only used when the page
+ * scanner is first started.
*/
#define MAXHANDSPREADPAGES ((64 * 1024 * 1024) / PAGESIZE)
diff --git a/usr/src/uts/i86pc/vm/hat_i86.c b/usr/src/uts/i86pc/vm/hat_i86.c
index 7650d28f41..ea9436e881 100644
--- a/usr/src/uts/i86pc/vm/hat_i86.c
+++ b/usr/src/uts/i86pc/vm/hat_i86.c
@@ -3808,7 +3808,7 @@ hat_page_getattr(struct page *pp, uint_t flag)
/*
- * common code used by hat_pageunload() and hment_steal()
+ * common code used by hat_page_inval() and hment_steal()
*/
hment_t *
hati_page_unmap(page_t *pp, htable_t *ht, uint_t entry)
@@ -3864,15 +3864,13 @@ hati_page_unmap(page_t *pp, htable_t *ht, uint_t entry)
extern int vpm_enable;
/*
- * Unload all translations to a page. If the page is a subpage of a large
+ * Unload translations to a page. If the page is a subpage of a large
* page, the large page mappings are also removed.
- *
- * The forceflags are unused.
+ * If curhat is not NULL, then we only unload the translation
+ * for the given process, otherwise all translations are unloaded.
*/
-
-/*ARGSUSED*/
-static int
-hati_pageunload(struct page *pp, uint_t pg_szcd, uint_t forceflag)
+void
+hat_page_inval(struct page *pp, uint_t pg_szcd, struct hat *curhat)
{
page_t *cur_pp = pp;
hment_t *hm;
@@ -3880,16 +3878,11 @@ hati_pageunload(struct page *pp, uint_t pg_szcd, uint_t forceflag)
htable_t *ht;
uint_t entry;
level_t level;
+ ulong_t cnt = 0;
XPV_DISALLOW_MIGRATE();
/*
- * prevent recursion due to kmem_free()
- */
- ++curthread->t_hatdepth;
- ASSERT(curthread->t_hatdepth < 16);
-
- /*
* clear the vpm ref.
*/
if (vpm_enable) {
@@ -3899,6 +3892,8 @@ hati_pageunload(struct page *pp, uint_t pg_szcd, uint_t forceflag)
* The loop with next_size handles pages with multiple pagesize mappings
*/
next_size:
+ if (curhat != NULL)
+ cnt = hat_page_getshare(cur_pp);
for (;;) {
/*
@@ -3910,14 +3905,13 @@ next_size:
if (hm == NULL) {
x86_hm_exit(cur_pp);
+curproc_done:
/*
* If not part of a larger page, we're done.
*/
if (cur_pp->p_szc <= pg_szcd) {
- ASSERT(curthread->t_hatdepth > 0);
- --curthread->t_hatdepth;
XPV_ALLOW_MIGRATE();
- return (0);
+ return;
}
/*
@@ -3936,8 +3930,20 @@ next_size:
* If this mapping size matches, remove it.
*/
level = ht->ht_level;
- if (level == pg_szcd)
- break;
+ if (level == pg_szcd) {
+ if (curhat == NULL || ht->ht_hat == curhat)
+ break;
+ /*
+ * Unloading only the given process but it's
+ * not the hat for the current process. Leave
+ * entry in place. Also do a safety check to
+ * ensure we don't get in an infinite loop
+ */
+ if (cnt-- == 0) {
+ x86_hm_exit(cur_pp);
+ goto curproc_done;
+ }
+ }
}
/*
@@ -3947,14 +3953,44 @@ next_size:
hm = hati_page_unmap(cur_pp, ht, entry);
if (hm != NULL)
hment_free(hm);
+
+ /* Perform check above for being part of a larger page. */
+ if (curhat != NULL)
+ goto curproc_done;
}
}
+/*
+ * Unload translations to a page. If unloadflag is HAT_CURPROC_PGUNLOAD, then
+ * we only unload the translation for the current process, otherwise all
+ * translations are unloaded.
+ */
+static int
+hati_pageunload(struct page *pp, uint_t pg_szcd, uint_t unloadflag)
+{
+ struct hat *curhat = NULL;
+
+ /*
+ * prevent recursion due to kmem_free()
+ */
+ ++curthread->t_hatdepth;
+ ASSERT(curthread->t_hatdepth < 16);
+
+ if (unloadflag == HAT_CURPROC_PGUNLOAD)
+ curhat = curthread->t_procp->p_as->a_hat;
+
+ hat_page_inval(pp, pg_szcd, curhat);
+
+ ASSERT(curthread->t_hatdepth > 0);
+ --curthread->t_hatdepth;
+ return (0);
+}
+
int
-hat_pageunload(struct page *pp, uint_t forceflag)
+hat_pageunload(struct page *pp, uint_t unloadflag)
{
ASSERT(PAGE_EXCL(pp));
- return (hati_pageunload(pp, 0, forceflag));
+ return (hati_pageunload(pp, 0, unloadflag));
}
/*
diff --git a/usr/src/uts/i86pc/vm/hment.c b/usr/src/uts/i86pc/vm/hment.c
index bb18b5c462..769bbd15d2 100644
--- a/usr/src/uts/i86pc/vm/hment.c
+++ b/usr/src/uts/i86pc/vm/hment.c
@@ -21,6 +21,7 @@
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2017 Joyent, Inc.
*/
#include <sys/types.h>
@@ -35,6 +36,7 @@
#include <vm/hat_i86.h>
#include <sys/cmn_err.h>
#include <sys/avl.h>
+#include <sys/zone.h>
/*
@@ -319,6 +321,8 @@ hment_insert(hment_t *hm, page_t *pp)
((hment_t *)pp->p_mapping)->hm_prev = hm;
pp->p_mapping = hm;
+ zone_add_page(pp);
+
/*
* Add the hment to the system-wide hash table.
*/
@@ -460,6 +464,7 @@ hment_assign(htable_t *htable, uint_t entry, page_t *pp, hment_t *hm)
pp->p_embed = 1;
pp->p_mapping = htable;
pp->p_mlentry = entry;
+ zone_add_page(pp);
return;
}
@@ -541,6 +546,7 @@ hment_remove(page_t *pp, htable_t *ht, uint_t entry)
pp->p_mapping = NULL;
pp->p_mlentry = 0;
pp->p_embed = 0;
+ zone_rm_page(pp);
return (NULL);
}
@@ -576,6 +582,7 @@ hment_remove(page_t *pp, htable_t *ht, uint_t entry)
hm->hm_hashlink = null_avl_link;
hm->hm_next = NULL;
hm->hm_prev = NULL;
+ zone_rm_page(pp);
return (hm);
}
diff --git a/usr/src/uts/i86pc/vm/vm_machdep.c b/usr/src/uts/i86pc/vm/vm_machdep.c
index 225628b1c8..bc9d03e7f5 100644
--- a/usr/src/uts/i86pc/vm/vm_machdep.c
+++ b/usr/src/uts/i86pc/vm/vm_machdep.c
@@ -711,10 +711,8 @@ void
map_addr(caddr_t *addrp, size_t len, offset_t off, int vacalign, uint_t flags)
{
struct proc *p = curproc;
- caddr_t userlimit = (flags & _MAP_LOW32) ?
- (caddr_t)_userlimit32 : p->p_as->a_userlimit;
-
- map_addr_proc(addrp, len, off, vacalign, userlimit, curproc, flags);
+ map_addr_proc(addrp, len, off, vacalign,
+ map_userlimit(p, p->p_as, flags), curproc, flags);
}
/*ARGSUSED*/
@@ -3546,7 +3544,7 @@ page_create_io(
if (nscan < desscan && freemem < minfree) {
TRACE_1(TR_FAC_VM, TR_PAGEOUT_CV_SIGNAL,
"pageout_cv_signal:freemem %ld", freemem);
- cv_signal(&proc_pageout->p_cv);
+ WAKE_PAGEOUT_SCANNER();
}
if (flags & PG_PHYSCONTIG) {
diff --git a/usr/src/uts/intel/Makefile b/usr/src/uts/intel/Makefile
index ff9ed42c94..13b7642799 100644
--- a/usr/src/uts/intel/Makefile
+++ b/usr/src/uts/intel/Makefile
@@ -59,7 +59,7 @@ install_h.prereq := TARGET= install_h
.PARALLEL: $(PARALLEL_KMODS) $(XMODS) config
-def all install clean clobber modlist: $(KMODS) $(XMODS) config
+def all install clean clobber modlist: genassym $(KMODS) $(XMODS) config
clobber: clobber.targ
@@ -101,7 +101,7 @@ CLOBBERFILES += $(PRIVS_C)
# intel/dtrace depends on i86pc/genassym, so we need to build both
# i86pc/genassym and intel/genassym.
#
-all.prereq install.prereq def.prereq: genunix FRC
+all.prereq install.prereq def.prereq: genassym genunix FRC
@cd ../i86pc/genassym; pwd; $(MAKE) $(@:%.prereq=%)
#
@@ -111,7 +111,7 @@ all.prereq install.prereq def.prereq: genunix FRC
genunix: $(PRIVS_C)
-$(KMODS) $(SUBDIRS) config: FRC
+genassym $(KMODS) $(SUBDIRS) config: FRC
@cd $@; pwd; $(MAKE) $(NO_STATE) $(TARGET)
$(XMODS): FRC
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index 20d5e89ec9..0ead68a021 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -98,6 +98,30 @@ GENUNIX_OBJS += \
CORE_OBJS += \
prmachdep.o
+LX_CGROUP_OBJS += \
+ cgrps_node.o \
+ cgrps_vfsops.o \
+ cgrps_vnops.o
+
+LX_DEVFS_OBJS += \
+ lxd_attrdb.o \
+ lxd_node.o \
+ lxd_vfsops.o \
+ lxd_vnops.o
+
+LX_PROC_OBJS += \
+ lx_prsubr.o \
+ lx_prvfsops.o \
+ lx_prvnops.o
+
+LX_SYS_OBJS += \
+ lx_syssubr.o \
+ lx_sysvfsops.o \
+ lx_sysvnops.o
+
+LX_AUTOFS_OBJS += \
+ lx_autofs.o
+
#
# shared hypervisor functionality
#
@@ -271,6 +295,74 @@ IOMMULIB_OBJS = iommulib.o
#
SN1_BRAND_OBJS = sn1_brand.o sn1_brand_asm.o
S10_BRAND_OBJS = s10_brand.o s10_brand_asm.o
+LX_BRAND_OBJS = \
+ lx_access.o \
+ lx_acct.o \
+ lx_acl.o \
+ lx_aio.o \
+ lx_archdep.o \
+ lx_audit.o \
+ lx_auxv.o \
+ lx_brand.o \
+ lx_brk.o \
+ lx_chmod.o \
+ lx_chown.o \
+ lx_clone.o \
+ lx_close.o \
+ lx_cpu.o \
+ lx_dup.o \
+ lx_errno.o \
+ lx_epoll.o \
+ lx_eventfd.o \
+ lx_fadvise.o \
+ lx_fallocate.o \
+ lx_fcntl.o \
+ lx_futex.o \
+ lx_getcwd.o \
+ lx_getdents.o \
+ lx_getpid.o \
+ lx_getrandom.o \
+ lx_id.o \
+ lx_ioctl.o \
+ lx_ioprio.o \
+ lx_kill.o \
+ lx_link.o \
+ lx_lseek.o \
+ lx_mem.o \
+ lx_misc.o \
+ lx_miscsys.o \
+ lx_mkdir.o \
+ lx_modify_ldt.o \
+ lx_mount.o \
+ lx_lockd.o \
+ lx_open.o \
+ lx_personality.o \
+ lx_pgrp.o \
+ lx_pid.o \
+ lx_pipe.o \
+ lx_poll.o \
+ lx_prctl.o \
+ lx_priority.o \
+ lx_ptrace.o \
+ lx_rename.o \
+ lx_rlimit.o \
+ lx_rw.o \
+ lx_sched.o \
+ lx_signal.o \
+ lx_signum.o \
+ lx_socket.o \
+ lx_splice.o \
+ lx_stat.o \
+ lx_sync.o \
+ lx_syscall.o \
+ lx_sysinfo.o \
+ lx_thread_area.o \
+ lx_time.o \
+ lx_timer.o \
+ lx_umask.o \
+ lx_uname.o \
+ lx_wait.o \
+ lx_xattr.o
#
# special files
diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel
index 77713eb279..6b6093d555 100644
--- a/usr/src/uts/intel/Makefile.intel
+++ b/usr/src/uts/intel/Makefile.intel
@@ -22,7 +22,7 @@
# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2013 Andrew Stormont. All rights reserved.
# Copyright (c) 2014 by Delphix. All rights reserved.
-# Copyright 2019 Joyent, Inc.
+# Copyright 2021 Joyent, Inc.
# Copyright 2022 Garrett D'Amore <garrett@damore.org>
# Copyright 2018 Nexenta Systems, Inc.
# Copyright 2019 RackTop Systems
@@ -50,6 +50,7 @@ PLATFORM = i86pc
#
UNIX_DIR = $(UTSBASE)/i86pc/unix
GENLIB_DIR = $(UTSBASE)/intel/genunix
+GENASSYM_DIR = $(UTSBASE)/intel/genassym
IPDRV_DIR = $(UTSBASE)/intel/ip
MODSTUBS_DIR = $(UNIX_DIR)
DSF_DIR = $(UTSBASE)/$(PLATFORM)/genassym
@@ -132,6 +133,7 @@ ASFLAGS_XARCH_64 = $(amd64_ASFLAGS)
ASFLAGS_XARCH = $(ASFLAGS_XARCH_$(CLASS))
ASFLAGS += $(ASFLAGS_XARCH)
+AS_INC_PATH += -I$(GENASSYM_DIR)/$(OBJS_DIR)
#
# Define the base directory for installation.
@@ -245,6 +247,7 @@ DRV_KMODS += hxge
DRV_KMODS += i8042
DRV_KMODS += icmp
DRV_KMODS += icmp6
+DRV_KMODS += inotify
DRV_KMODS += intel_nb5000
DRV_KMODS += intel_nhm
DRV_KMODS += ip
@@ -279,6 +282,7 @@ DRV_KMODS += mouse8042
DRV_KMODS += mpt_sas
DRV_KMODS += mr_sas
DRV_KMODS += mwl
+DRV_KMODS += nfp
DRV_KMODS += nsmb
DRV_KMODS += nulldriver
DRV_KMODS += nv_sata
@@ -347,6 +351,8 @@ DRV_KMODS += ural
DRV_KMODS += uath
DRV_KMODS += urtw
DRV_KMODS += vgatext
+DRV_KMODS += vmxnet
+DRV_KMODS += vnd
DRV_KMODS += vnic
DRV_KMODS += vscan
DRV_KMODS += wc
@@ -355,6 +361,7 @@ DRV_KMODS += wpi
DRV_KMODS += xge
DRV_KMODS += yge
DRV_KMODS += zcons
+DRV_KMODS += zfd
DRV_KMODS += zyd
DRV_KMODS += simnet
DRV_KMODS += smrt
@@ -511,7 +518,8 @@ DRV_KMODS += sol_umad
#
# Brand modules
#
-BRAND_KMODS += sn1_brand s10_brand
+BRAND_KMODS += sn1_brand s10_brand lx_brand
+DRV_KMODS += lx_systrace lx_ptm lx_netlink
#
# Exec Class Modules (/kernel/exec):
@@ -526,10 +534,10 @@ SCHED_KMODS += IA RT TS RT_DPTBL TS_DPTBL FSS FX FX_DPTBL SDC
#
# File System Modules (/kernel/fs):
#
-FS_KMODS += autofs ctfs dcfs dev devfs fdfs fifofs hsfs lofs
-FS_KMODS += mntfs namefs nfs objfs zfs zut
-FS_KMODS += pcfs procfs sockfs specfs tmpfs udfs ufs sharefs
-FS_KMODS += smbfs bootfs
+FS_KMODS += autofs ctfs dcfs dev devfs fdfs fifofs hsfs hyprlofs
+FS_KMODS += lofs lxautofs lx_proc lxprocfs mntfs namefs nfs objfs zfs zut
+FS_KMODS += pcfs procfs sockfs specfs tmpfs udfs ufs sharefs lx_sysfs
+FS_KMODS += smbfs bootfs lx_cgroup lx_devfs
#
# Streams Modules (/kernel/strmod):
@@ -587,6 +595,7 @@ MISC_KMODS += dls
MISC_KMODS += fssnap_if
MISC_KMODS += gda
MISC_KMODS += gld
+MISC_KMODS += gsqueue
MISC_KMODS += hidparser
MISC_KMODS += hook
MISC_KMODS += hpcsvc
@@ -713,6 +722,7 @@ SOCKET_KMODS += sockpfp
SOCKET_KMODS += socksctp
SOCKET_KMODS += socksdp
SOCKET_KMODS += sockrds
+SOCKET_KMODS += datafilt
#
# kiconv modules (/kernel/kiconv):
diff --git a/usr/src/uts/intel/Makefile.rules b/usr/src/uts/intel/Makefile.rules
index 31b87f55b0..778f82b13b 100644
--- a/usr/src/uts/intel/Makefile.rules
+++ b/usr/src/uts/intel/Makefile.rules
@@ -178,6 +178,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/intel_nb5000/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/vmxnet/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/intel/io/intel_nhm/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/amd64/krtld/kobj_reloc.c b/usr/src/uts/intel/amd64/krtld/kobj_reloc.c
index 78b1fb7777..69e0342768 100644
--- a/usr/src/uts/intel/amd64/krtld/kobj_reloc.c
+++ b/usr/src/uts/intel/amd64/krtld/kobj_reloc.c
@@ -25,6 +25,9 @@
*
* Copyright 2020 Joyent, Inc.
*/
+/*
+ * Copyright (c) 2017 Joyent, Inc.
+ */
/*
* x86 relocation code.
diff --git a/usr/src/uts/intel/bpf/Makefile b/usr/src/uts/intel/bpf/Makefile
index 3729bd2523..e07edeb12c 100644
--- a/usr/src/uts/intel/bpf/Makefile
+++ b/usr/src/uts/intel/bpf/Makefile
@@ -60,7 +60,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
#
CFLAGS += $(CCVERBOSE)
-LDFLAGS += -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti
+LDFLAGS += -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti -Ndrv/ip
INC_PATH += -I$(UTSBASE)/common/io/bpf
#
diff --git a/usr/src/uts/intel/brand/lx/lx_archdep.c b/usr/src/uts/intel/brand/lx/lx_archdep.c
new file mode 100644
index 0000000000..24f3d2c446
--- /dev/null
+++ b/usr/src/uts/intel/brand/lx/lx_archdep.c
@@ -0,0 +1,1720 @@
+/*
+ * 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 2018 Joyent, Inc.
+ */
+
+/*
+ * LX brand Intel-specific routines.
+ */
+
+#include <sys/types.h>
+#include <sys/sunddi.h>
+#include <sys/ddi.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/privregs.h>
+#include <sys/pcb.h>
+#include <sys/archsystm.h>
+#include <sys/stack.h>
+#include <sys/sdt.h>
+#include <sys/sysmacros.h>
+#include <sys/psw.h>
+#include <lx_errno.h>
+
+/*
+ * Argument constants for fix_segreg.
+ * See usr/src/uts/intel/ia32/os/archdep.c for the originals.
+ */
+#define IS_CS 1
+#define IS_NOT_CS 0
+
+extern greg_t fix_segreg(greg_t, int, model_t);
+
+
+#define LX_REG(ucp, r) ((ucp)->uc_mcontext.gregs[(r)])
+
+#define PSLMERGE(oldval, newval) \
+ (((oldval) & ~PSL_USERMASK) | ((newval) & PSL_USERMASK))
+
+#ifdef __amd64
+/* 64-bit native user_regs_struct */
+typedef struct lx_user_regs64 {
+ int64_t lxur_r15;
+ int64_t lxur_r14;
+ int64_t lxur_r13;
+ int64_t lxur_r12;
+ int64_t lxur_rbp;
+ int64_t lxur_rbx;
+ int64_t lxur_r11;
+ int64_t lxur_r10;
+ int64_t lxur_r9;
+ int64_t lxur_r8;
+ int64_t lxur_rax;
+ int64_t lxur_rcx;
+ int64_t lxur_rdx;
+ int64_t lxur_rsi;
+ int64_t lxur_rdi;
+ int64_t lxur_orig_rax;
+ int64_t lxur_rip;
+ int64_t lxur_xcs;
+ int64_t lxur_rflags;
+ int64_t lxur_rsp;
+ int64_t lxur_xss;
+ int64_t lxur_xfs_base;
+ int64_t lxur_xgs_base;
+ int64_t lxur_xds;
+ int64_t lxur_xes;
+ int64_t lxur_xfs;
+ int64_t lxur_xgs;
+} lx_user_regs64_t;
+
+/* 64-bit native user_fpregs_struct */
+typedef struct lx_user_fpregs64 {
+ uint16_t lxufp_cwd;
+ uint16_t lxufp_swd;
+ uint16_t lxufp_ftw;
+ uint16_t lxufp_fop;
+ uint64_t lxufp_rip;
+ uint64_t lxufp_rdp;
+ uint32_t lxufp_mxcsr;
+ uint32_t lxufp_mxcr_mask;
+ /* 8*16 bytes for each FP-reg = 128 bytes */
+ uint32_t lxufp_st_space[32];
+ /* 16*16 bytes for each XMM-reg = 256 bytes */
+ uint32_t lxufp_xmm_space[64];
+ uint32_t lxufp_padding[24];
+} lx_user_fpregs64_t;
+
+/* 64-bit native user_struct */
+typedef struct lx_user64 {
+ lx_user_regs64_t lxu_regs;
+ int32_t lxu_fpvalid;
+ int32_t lxu_pad0;
+ lx_user_fpregs64_t lxu_i387;
+ uint64_t lxu_tsize;
+ uint64_t lxu_dsize;
+ uint64_t lxu_ssize;
+ uint64_t lxu_start_code;
+ uint64_t lxu_start_stack;
+ int64_t lxu_signal;
+ int32_t lxu_reserved;
+ int32_t lxu_pad1;
+ /* help gdb to locate user_regs structure */
+ caddr_t lxu_ar0;
+ /* help gdb to locate user_fpregs structure */
+ caddr_t lxu_fpstate;
+ uint64_t lxu_magic;
+ char lxu_comm[32];
+ uint64_t lxu_debugreg[8];
+ uint64_t lxu_error_code;
+ uint64_t lxu_fault_address;
+} lx_user64_t;
+
+#endif /* __amd64 */
+
+/* 32-bit native user_regs_struct */
+typedef struct lx_user_regs32 {
+ int32_t lxur_ebx;
+ int32_t lxur_ecx;
+ int32_t lxur_edx;
+ int32_t lxur_esi;
+ int32_t lxur_edi;
+ int32_t lxur_ebp;
+ int32_t lxur_eax;
+ int32_t lxur_xds;
+ int32_t lxur_xes;
+ int32_t lxur_xfs;
+ int32_t lxur_xgs;
+ int32_t lxur_orig_eax;
+ int32_t lxur_eip;
+ int32_t lxur_xcs;
+ int32_t lxur_eflags;
+ int32_t lxur_esp;
+ int32_t lxur_xss;
+} lx_user_regs32_t;
+
+/* 32-bit native user_fpregs_struct */
+typedef struct lx_user_fpregs32 {
+ int32_t lxufp_cwd;
+ int32_t lxufp_swd;
+ int32_t lxufp_twd;
+ int32_t lxufp_fip;
+ int32_t lxufp_fcs;
+ int32_t lxufp_foo;
+ int32_t lxufp_fos;
+ int32_t lxufp_st_space[20];
+} lx_user_fpregs32_t;
+
+/* 32-bit native user_fpxregs_struct */
+typedef struct lx_user_fpxregs32 {
+ uint16_t lxufpx_cwd;
+ uint16_t lxufpx_swd;
+ uint16_t lxufpx_twd;
+ uint16_t lxufpx_fop;
+ int32_t lxufpx_fip;
+ int32_t lxufpx_fcs;
+ int32_t lxufpx_foo;
+ int32_t lxufpx_fos;
+ int32_t lxufpx_mxcsr;
+ int32_t lxufpx_reserved;
+ /* 8*16 bytes for each FP-reg = 128 bytes */
+ int32_t lxufpx_st_space[32];
+ /* 8*16 bytes for each XMM-reg = 128 bytes */
+ int32_t lxufpx_xmm_space[32];
+ int32_t lxufpx_padding[56];
+} lx_user_fpxregs32_t;
+
+/* 32-bit native user_struct */
+typedef struct lx_user32 {
+ lx_user_regs32_t lxu_regs;
+ int32_t lxu_fpvalid;
+ lx_user_fpregs32_t lxu_i387;
+ uint32_t lxu_tsize;
+ uint32_t lxu_dsize;
+ uint32_t lxu_ssize;
+ uint32_t lxu_start_code;
+ uint32_t lxu_start_stack;
+ int32_t lxu_signal;
+ int32_t lxu_reserved;
+ caddr32_t lxu_ar0;
+ caddr32_t lxu_fpstate;
+ uint32_t lxu_magic;
+ char lxu_comm[32];
+ int32_t lxu_debugreg[8];
+} lx_user32_t;
+
+/*
+ * Certain version of strace (on centos6 for example) use the %cs value to
+ * determine what kind of process is being traced. Here is a sample comment:
+ * Check CS register value. On x86-64 linux it is:
+ * 0x33 for long mode (64 bit and x32))
+ * 0x23 for compatibility mode (32 bit)
+ * %ds = 0x2b for x32 mode (x86-64 in 32 bit)
+ * We can't change the %cs value in the ucp (see setgregs and _sys_rtt) so we
+ * emulate the expected value for ptrace use.
+ */
+#define LX_CS_64BIT 0x33
+#define LX_CS_32BIT 0x23
+
+extern int getsetcontext(int, void *);
+#if defined(_SYSCALL32_IMPL)
+extern int getsetcontext32(int, void *);
+#endif
+
+static int
+lx_rw_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz, boolean_t writing)
+{
+ int error = 0;
+ size_t rem = ucsz;
+ off_t pos = 0;
+
+ VERIFY(MUTEX_HELD(&p->p_lock));
+
+ /*
+ * Grab P_PR_LOCK so that we can drop p_lock while doing I/O.
+ */
+ sprlock_proc(p);
+
+ /*
+ * Drop p_lock while we do I/O to avoid deadlock with the clock thread.
+ */
+ mutex_exit(&p->p_lock);
+ while (rem != 0) {
+ uintptr_t addr = (uintptr_t)ucp + pos;
+ size_t len = MIN(rem, PAGESIZE - (addr & PAGEOFFSET));
+
+ if (writing) {
+ error = uwrite(p, (caddr_t)kucp + pos, len, addr);
+ } else {
+ error = uread(p, (caddr_t)kucp + pos, len, addr);
+ }
+
+ if (error != 0) {
+ break;
+ }
+
+ rem -= len;
+ pos += len;
+ }
+ mutex_enter(&p->p_lock);
+
+ sprunlock(p);
+ mutex_enter(&p->p_lock);
+
+ return (error);
+}
+
+/*
+ * Read a ucontext_t from the target process, which may or may not be
+ * the current process.
+ */
+static int
+lx_read_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz)
+{
+ return (lx_rw_uc(p, ucp, kucp, ucsz, B_FALSE));
+}
+
+/*
+ * Write a ucontext_t to the target process, which may or may not be
+ * the current process.
+ */
+static int
+lx_write_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz)
+{
+ return (lx_rw_uc(p, ucp, kucp, ucsz, B_TRUE));
+}
+
+static void
+lx_getfpregs32(lx_lwp_data_t *lwpd, lx_user_fpregs32_t *lfp)
+{
+#ifdef __amd64
+ fpregset32_t fp;
+ getfpregs32(lwpd->br_lwp, &fp);
+#else /* __i386 */
+ fpregset_t fp;
+ getfpregs(lwpd->br_lwp, &fp);
+#endif /* __amd64 */
+
+ /*
+ * The fpchip_state.state field should correspond to all 27 fields in
+ * the 32-bit structure.
+ */
+ bcopy(&fp.fp_reg_set.fpchip_state.state, lfp, sizeof (*lfp));
+}
+
+static void
+lx_setfpregs32(lx_lwp_data_t *lwpd, lx_user_fpregs32_t *lfp)
+{
+#ifdef __amd64
+ fpregset32_t fp;
+#else /* __i386 */
+ fpregset_t fp;
+#endif /* __amd64 */
+
+ /*
+ * The fpchip_state field should correspond to all 27 fields in the
+ * native 32-bit structure.
+ */
+ bcopy(lfp, &fp.fp_reg_set.fpchip_state.state, sizeof (*lfp));
+
+#ifdef __amd64
+ setfpregs32(lwpd->br_lwp, &fp);
+#else /* __i386 */
+ setfpregs(lwpd->br_lwp, &fp);
+#endif /* __amd64 */
+}
+
+static int
+lx_get_user_regs32_uc(klwp_t *lwp, void *ucp, lx_user_regs32_t *lxrp)
+{
+ proc_t *p = lwptoproc(lwp);
+ ucontext32_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ lxrp->lxur_ebx = LX_REG(&uc, EBX);
+ lxrp->lxur_ecx = LX_REG(&uc, ECX);
+ lxrp->lxur_edx = LX_REG(&uc, EDX);
+ lxrp->lxur_esi = LX_REG(&uc, ESI);
+ lxrp->lxur_edi = LX_REG(&uc, EDI);
+ lxrp->lxur_ebp = LX_REG(&uc, EBP);
+ lxrp->lxur_eax = LX_REG(&uc, EAX);
+ lxrp->lxur_orig_eax = 0;
+
+ lxrp->lxur_eip = LX_REG(&uc, EIP);
+ lxrp->lxur_eflags = LX_REG(&uc, EFL);
+ lxrp->lxur_esp = LX_REG(&uc, UESP);
+ lxrp->lxur_xss = LX_REG(&uc, SS);
+
+ /* emulated %cs, see defines */
+ lxrp->lxur_xcs = LX_CS_32BIT;
+ lxrp->lxur_xds = LX_REG(&uc, DS);
+ lxrp->lxur_xes = LX_REG(&uc, ES);
+ lxrp->lxur_xfs = LX_REG(&uc, FS);
+ lxrp->lxur_xgs = LX_REG(&uc, GS);
+ return (0);
+}
+
+static int
+lx_get_user_regs32(lx_lwp_data_t *lwpd, lx_user_regs32_t *lxrp)
+{
+ klwp_t *lwp = lwpd->br_lwp;
+ struct regs *rp = lwptoregs(lwp);
+ void *ucp;
+#ifdef __amd64
+ struct pcb *pcb = &lwp->lwp_pcb;
+#endif
+
+ VERIFY(lwp_getdatamodel(lwp) == DATAMODEL_ILP32);
+
+ switch (lx_regs_location(lwpd, &ucp, B_FALSE)) {
+ case LX_REG_LOC_UNAVAIL:
+ return (-1);
+
+ case LX_REG_LOC_UCP:
+ return (lx_get_user_regs32_uc(lwp, ucp, lxrp));
+
+ case LX_REG_LOC_LWP:
+ /* transformation below */
+ break;
+
+ default:
+ VERIFY(0);
+ break;
+ }
+
+#ifdef __amd64
+ lxrp->lxur_ebx = (int32_t)rp->r_rbx;
+ lxrp->lxur_ecx = (int32_t)rp->r_rcx;
+ lxrp->lxur_edx = (int32_t)rp->r_rdx;
+ lxrp->lxur_esi = (int32_t)rp->r_rsi;
+ lxrp->lxur_edi = (int32_t)rp->r_rdi;
+ lxrp->lxur_ebp = (int32_t)rp->r_rbp;
+ lxrp->lxur_eax = (int32_t)rp->r_rax;
+ lxrp->lxur_orig_eax = 0;
+ lxrp->lxur_eip = (int32_t)rp->r_rip;
+ lxrp->lxur_eflags = (int32_t)rp->r_rfl;
+ lxrp->lxur_esp = (int32_t)rp->r_rsp;
+ lxrp->lxur_xss = (int32_t)rp->r_ss;
+
+ kpreempt_disable();
+ if (PCB_NEED_UPDATE_SEGS(pcb)) {
+ lxrp->lxur_xds = pcb->pcb_ds;
+ lxrp->lxur_xes = pcb->pcb_es;
+ lxrp->lxur_xfs = pcb->pcb_fs;
+ lxrp->lxur_xgs = pcb->pcb_gs;
+ } else {
+ lxrp->lxur_xds = rp->r_ds;
+ lxrp->lxur_xes = rp->r_es;
+ lxrp->lxur_xfs = rp->r_fs;
+ lxrp->lxur_xgs = rp->r_gs;
+ }
+ kpreempt_enable();
+#else /* __i386 */
+ lxrp->lxur_ebx = rp->r_ebx;
+ lxrp->lxur_ecx = rp->r_ecx;
+ lxrp->lxur_edx = rp->r_edx;
+ lxrp->lxur_esi = rp->r_esi;
+ lxrp->lxur_edi = rp->r_edi;
+ lxrp->lxur_ebp = rp->r_ebp;
+ lxrp->lxur_eax = rp->r_eax;
+ lxrp->lxur_orig_eax = 0;
+ lxrp->lxur_eip = rp->r_eip;
+ lxrp->lxur_eflags = rp->r_efl;
+ lxrp->lxur_esp = rp->r_esp;
+ lxrp->lxur_xss = rp->r_ss;
+
+ lxrp->lxur_xds = rp->r_ds;
+ lxrp->lxur_xes = rp->r_es;
+ lxrp->lxur_xfs = rp->r_fs;
+ lxrp->lxur_xgs = rp->r_gs;
+#endif /* __amd64 */
+
+ /* emulated %cs, see defines */
+ lxrp->lxur_xcs = LX_CS_32BIT;
+
+ if (lwpd->br_ptrace_whatstop == LX_PR_SYSENTRY) {
+ lxrp->lxur_eax = (int32_t)-lx_errno(ENOTSUP, EINVAL);
+ lxrp->lxur_orig_eax = (int32_t)lwpd->br_syscall_num;
+ } else if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) {
+ lxrp->lxur_orig_eax = (int32_t)lwpd->br_syscall_num;
+ }
+
+ return (0);
+}
+
+static int
+lx_set_user_regs32_uc(klwp_t *lwp, void *ucp, lx_user_regs32_t *lxrp)
+{
+ proc_t *p = lwptoproc(lwp);
+ ucontext32_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ /*
+ * Note: we currently ignore "lxur_orig_rax" here since this
+ * path should not be used for system call stops.
+ */
+ LX_REG(&uc, EBP) = lxrp->lxur_ebp;
+ LX_REG(&uc, EBX) = lxrp->lxur_ebx;
+ LX_REG(&uc, EAX) = lxrp->lxur_eax;
+ LX_REG(&uc, ECX) = lxrp->lxur_ecx;
+ LX_REG(&uc, EDX) = lxrp->lxur_edx;
+ LX_REG(&uc, ESI) = lxrp->lxur_esi;
+ LX_REG(&uc, EDI) = lxrp->lxur_edi;
+ LX_REG(&uc, EIP) = lxrp->lxur_eip;
+ LX_REG(&uc, EFL) = PSLMERGE(LX_REG(&uc, EFL), lxrp->lxur_eflags);
+ LX_REG(&uc, UESP) = lxrp->lxur_esp;
+ LX_REG(&uc, SS) = fix_segreg(lxrp->lxur_xss, IS_NOT_CS,
+ DATAMODEL_ILP32);
+
+ /* %cs is ignored because of our lies */
+ LX_REG(&uc, DS) = fix_segreg(lxrp->lxur_xds, IS_NOT_CS,
+ DATAMODEL_ILP32);
+ LX_REG(&uc, ES) = fix_segreg(lxrp->lxur_xes, IS_NOT_CS,
+ DATAMODEL_ILP32);
+ LX_REG(&uc, FS) = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS,
+ DATAMODEL_ILP32);
+ LX_REG(&uc, GS) = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS,
+ DATAMODEL_ILP32);
+
+ if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+lx_set_user_regs32(lx_lwp_data_t *lwpd, lx_user_regs32_t *lxrp)
+{
+ klwp_t *lwp = lwpd->br_lwp;
+ struct regs *rp = lwptoregs(lwp);
+ void *ucp;
+#ifdef __amd64
+ struct pcb *pcb = &lwp->lwp_pcb;
+#endif
+
+ VERIFY(lwp_getdatamodel(lwp) == DATAMODEL_ILP32);
+
+ switch (lx_regs_location(lwpd, &ucp, B_TRUE)) {
+ case LX_REG_LOC_UNAVAIL:
+ return (-1);
+
+ case LX_REG_LOC_UCP:
+ return (lx_set_user_regs32_uc(lwp, ucp, lxrp));
+
+ case LX_REG_LOC_LWP:
+ /* transformation below */
+ break;
+
+ default:
+ VERIFY(0);
+ break;
+ }
+
+#ifdef __amd64
+ rp->r_rbx = (int32_t)lxrp->lxur_ebx;
+ rp->r_rcx = (int32_t)lxrp->lxur_ecx;
+ rp->r_rdx = (int32_t)lxrp->lxur_edx;
+ rp->r_rsi = (int32_t)lxrp->lxur_esi;
+ rp->r_rdi = (int32_t)lxrp->lxur_edi;
+ rp->r_rbp = (int32_t)lxrp->lxur_ebp;
+ rp->r_rax = (int32_t)lxrp->lxur_eax;
+ lwpd->br_syscall_num = (int)lxrp->lxur_orig_eax;
+ rp->r_rip = (int32_t)lxrp->lxur_eip;
+ rp->r_rfl = (int32_t)PSLMERGE(rp->r_rfl, lxrp->lxur_eflags);
+ rp->r_rsp = (int32_t)lxrp->lxur_esp;
+ rp->r_ss = (int32_t)fix_segreg(lxrp->lxur_xss, IS_NOT_CS,
+ DATAMODEL_ILP32);
+
+ kpreempt_disable();
+ PCB_SET_UPDATE_SEGS(pcb);
+ pcb->pcb_ds = fix_segreg(lxrp->lxur_xds, IS_NOT_CS, DATAMODEL_ILP32);
+ pcb->pcb_es = fix_segreg(lxrp->lxur_xes, IS_NOT_CS, DATAMODEL_ILP32);
+ pcb->pcb_fs = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS, DATAMODEL_ILP32);
+ pcb->pcb_gs = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS, DATAMODEL_ILP32);
+ kpreempt_enable();
+#else /* __i386 */
+ rp->r_ebx = lxrp->lxur_ebx;
+ rp->r_ecx = lxrp->lxur_ecx;
+ rp->r_edx = lxrp->lxur_edx;
+ rp->r_esi = lxrp->lxur_esi;
+ rp->r_edi = lxrp->lxur_edi;
+ rp->r_ebp = lxrp->lxur_ebp;
+ rp->r_eax = lxrp->lxur_eax;
+ lwpd->br_syscall_num = (int)lxrp->lxur_orig_eax;
+ rp->r_eip = lxrp->lxur_eip;
+ rp->r_efl = PSLMERGE(rp->r_efl, lxrp->lxur_eflags);
+ rp->r_esp = lxrp->lxur_esp;
+ rp->r_ss = fix_segreg(lxrp->lxur_xss, IS_NOT_CS, DATAMODEL_ILP32);
+
+ rp->r_ds = fix_segreg(lxrp->lxur_xds, IS_NOT_CS, DATAMODEL_ILP32);
+ rp->r_es = fix_segreg(lxrp->lxur_xes, IS_NOT_CS, DATAMODEL_ILP32);
+ rp->r_fs = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS, DATAMODEL_ILP32);
+ rp->r_gs = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS, DATAMODEL_ILP32);
+#endif /* __amd64 */
+
+ return (0);
+}
+
+#ifdef __amd64
+
+static void
+lx_getfpregs64(lx_lwp_data_t *lwpd, lx_user_fpregs64_t *lfp)
+{
+ fpregset_t fp;
+
+ getfpregs(lwpd->br_lwp, &fp);
+ /* Drop the extra illumos status/xstatus fields when copying state */
+ bcopy(&fp.fp_reg_set.fpchip_state, lfp, sizeof (*lfp));
+}
+
+static void
+lx_setfpregs64(lx_lwp_data_t *lwpd, lx_user_fpregs64_t *lfp)
+{
+ fpregset_t fp;
+
+ /*
+ * Since the Linux fpregs structure does not contain the same
+ * additional status register which illumos contains, we simply
+ * preserve the existing values when setting fp state.
+ */
+ getfpregs(lwpd->br_lwp, &fp);
+
+ /* Copy the identically formatted state */
+ bcopy(lfp, &fp.fp_reg_set.fpchip_state, sizeof (*lfp));
+
+ setfpregs(lwpd->br_lwp, &fp);
+}
+
+static int
+lx_get_user_regs64_uc(klwp_t *lwp, void *ucp, lx_user_regs64_t *lxrp)
+{
+ proc_t *p = lwptoproc(lwp);
+
+ switch (lwp_getdatamodel(lwp)) {
+ case DATAMODEL_LP64: {
+ ucontext_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ lxrp->lxur_r15 = LX_REG(&uc, REG_R15);
+ lxrp->lxur_r14 = LX_REG(&uc, REG_R14);
+ lxrp->lxur_r13 = LX_REG(&uc, REG_R13);
+ lxrp->lxur_r12 = LX_REG(&uc, REG_R12);
+ lxrp->lxur_rbp = LX_REG(&uc, REG_RBP);
+ lxrp->lxur_rbx = LX_REG(&uc, REG_RBX);
+ lxrp->lxur_r11 = LX_REG(&uc, REG_R11);
+ lxrp->lxur_r10 = LX_REG(&uc, REG_R10);
+ lxrp->lxur_r9 = LX_REG(&uc, REG_R9);
+ lxrp->lxur_r8 = LX_REG(&uc, REG_R8);
+ lxrp->lxur_rax = LX_REG(&uc, REG_RAX);
+ lxrp->lxur_rcx = LX_REG(&uc, REG_RCX);
+ lxrp->lxur_rdx = LX_REG(&uc, REG_RDX);
+ lxrp->lxur_rsi = LX_REG(&uc, REG_RSI);
+ lxrp->lxur_rdi = LX_REG(&uc, REG_RDI);
+ lxrp->lxur_orig_rax = 0;
+ lxrp->lxur_rip = LX_REG(&uc, REG_RIP);
+ lxrp->lxur_rflags = LX_REG(&uc, REG_RFL);
+ lxrp->lxur_rsp = LX_REG(&uc, REG_RSP);
+ lxrp->lxur_xss = LX_REG(&uc, REG_SS);
+ lxrp->lxur_xfs_base = LX_REG(&uc, REG_FSBASE);
+ lxrp->lxur_xgs_base = LX_REG(&uc, REG_GSBASE);
+
+ lxrp->lxur_xds = LX_REG(&uc, REG_DS);
+ lxrp->lxur_xes = LX_REG(&uc, REG_ES);
+ lxrp->lxur_xfs = LX_REG(&uc, REG_FS);
+ lxrp->lxur_xgs = LX_REG(&uc, REG_GS);
+
+ /* emulated %cs, see defines */
+ lxrp->lxur_xcs = LX_CS_64BIT;
+ return (0);
+ }
+
+ case DATAMODEL_ILP32: {
+ ucontext32_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ lxrp->lxur_r15 = 0;
+ lxrp->lxur_r14 = 0;
+ lxrp->lxur_r13 = 0;
+ lxrp->lxur_r12 = 0;
+ lxrp->lxur_r11 = 0;
+ lxrp->lxur_r10 = 0;
+ lxrp->lxur_r9 = 0;
+ lxrp->lxur_r8 = 0;
+ lxrp->lxur_rbp = LX_REG(&uc, EBP);
+ lxrp->lxur_rbx = LX_REG(&uc, EBX);
+ lxrp->lxur_rax = LX_REG(&uc, EAX);
+ lxrp->lxur_orig_rax = 0;
+ lxrp->lxur_rcx = LX_REG(&uc, ECX);
+ lxrp->lxur_rdx = LX_REG(&uc, EDX);
+ lxrp->lxur_rsi = LX_REG(&uc, ESI);
+ lxrp->lxur_rdi = LX_REG(&uc, EDI);
+ lxrp->lxur_rip = LX_REG(&uc, EIP);
+
+ lxrp->lxur_rflags = LX_REG(&uc, EFL);
+ lxrp->lxur_rsp = LX_REG(&uc, UESP);
+ lxrp->lxur_xss = LX_REG(&uc, SS);
+ lxrp->lxur_xfs_base = 0;
+ lxrp->lxur_xgs_base = 0;
+
+ lxrp->lxur_xds = LX_REG(&uc, DS);
+ lxrp->lxur_xes = LX_REG(&uc, ES);
+ lxrp->lxur_xfs = LX_REG(&uc, FS);
+ lxrp->lxur_xgs = LX_REG(&uc, GS);
+
+ /* See comment above re: %cs register */
+ lxrp->lxur_xcs = LX_CS_32BIT;
+ return (0);
+ }
+
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+static int
+lx_get_user_regs64(lx_lwp_data_t *lwpd, lx_user_regs64_t *lxrp)
+{
+ klwp_t *lwp = lwpd->br_lwp;
+ struct regs *rp = lwptoregs(lwp);
+ struct pcb *pcb = &lwp->lwp_pcb;
+ void *ucp;
+
+ switch (lx_regs_location(lwpd, &ucp, B_FALSE)) {
+ case LX_REG_LOC_UNAVAIL:
+ return (-1);
+
+ case LX_REG_LOC_UCP:
+ return (lx_get_user_regs64_uc(lwp, ucp, lxrp));
+
+ case LX_REG_LOC_LWP:
+ /* transformation below */
+ break;
+
+ default:
+ VERIFY(0);
+ break;
+ }
+
+ lxrp->lxur_r15 = rp->r_r15;
+ lxrp->lxur_r14 = rp->r_r14;
+ lxrp->lxur_r13 = rp->r_r13;
+ lxrp->lxur_r12 = rp->r_r12;
+ lxrp->lxur_rbp = rp->r_rbp;
+ lxrp->lxur_rbx = rp->r_rbx;
+ lxrp->lxur_r11 = rp->r_r11;
+ lxrp->lxur_r10 = rp->r_r10;
+ lxrp->lxur_r9 = rp->r_r9;
+ lxrp->lxur_r8 = rp->r_r8;
+ lxrp->lxur_rax = rp->r_rax;
+ lxrp->lxur_rcx = rp->r_rcx;
+ lxrp->lxur_rdx = rp->r_rdx;
+ lxrp->lxur_rsi = rp->r_rsi;
+ lxrp->lxur_rdi = rp->r_rdi;
+ lxrp->lxur_orig_rax = 0;
+ lxrp->lxur_rip = rp->r_rip;
+
+ lxrp->lxur_rflags = rp->r_rfl;
+ lxrp->lxur_rsp = rp->r_rsp;
+ lxrp->lxur_xss = rp->r_ss;
+ lxrp->lxur_xfs_base = pcb->pcb_fsbase;
+ lxrp->lxur_xgs_base = pcb->pcb_gsbase;
+
+ /* emulated %cs, see defines */
+ switch (lwp_getdatamodel(lwp)) {
+ case DATAMODEL_LP64:
+ lxrp->lxur_xcs = LX_CS_64BIT;
+ break;
+ case DATAMODEL_ILP32:
+ lxrp->lxur_xcs = LX_CS_32BIT;
+ break;
+ default:
+ VERIFY(0);
+ break;
+ }
+
+ kpreempt_disable();
+ if (PCB_NEED_UPDATE_SEGS(pcb)) {
+ lxrp->lxur_xds = pcb->pcb_ds;
+ lxrp->lxur_xes = pcb->pcb_es;
+ lxrp->lxur_xfs = pcb->pcb_fs;
+ lxrp->lxur_xgs = pcb->pcb_gs;
+ } else {
+ lxrp->lxur_xds = rp->r_ds;
+ lxrp->lxur_xes = rp->r_es;
+ lxrp->lxur_xfs = rp->r_fs;
+ lxrp->lxur_xgs = rp->r_gs;
+ }
+ kpreempt_enable();
+
+ if (lwpd->br_ptrace_whatstop == LX_PR_SYSENTRY) {
+ lxrp->lxur_rax = -lx_errno(ENOTSUP, EINVAL);
+ lxrp->lxur_orig_rax = lwpd->br_syscall_num;
+ } else if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) {
+ lxrp->lxur_orig_rax = lwpd->br_syscall_num;
+ }
+
+ return (0);
+}
+
+static int
+lx_set_user_regs64_uc(klwp_t *lwp, void *ucp, lx_user_regs64_t *lxrp)
+{
+ proc_t *p = lwptoproc(lwp);
+
+ switch (lwp_getdatamodel(lwp)) {
+ case DATAMODEL_LP64: {
+ ucontext_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ /*
+ * Note: we currently ignore "lxur_orig_rax" here since this
+ * path should not be used for system call stops.
+ */
+ LX_REG(&uc, REG_R15) = lxrp->lxur_r15;
+ LX_REG(&uc, REG_R14) = lxrp->lxur_r14;
+ LX_REG(&uc, REG_R13) = lxrp->lxur_r13;
+ LX_REG(&uc, REG_R12) = lxrp->lxur_r12;
+ LX_REG(&uc, REG_RBP) = lxrp->lxur_rbp;
+ LX_REG(&uc, REG_RBX) = lxrp->lxur_rbx;
+ LX_REG(&uc, REG_R11) = lxrp->lxur_r11;
+ LX_REG(&uc, REG_R10) = lxrp->lxur_r10;
+ LX_REG(&uc, REG_R9) = lxrp->lxur_r9;
+ LX_REG(&uc, REG_R8) = lxrp->lxur_r8;
+ LX_REG(&uc, REG_RAX) = lxrp->lxur_rax;
+ LX_REG(&uc, REG_RCX) = lxrp->lxur_rcx;
+ LX_REG(&uc, REG_RDX) = lxrp->lxur_rdx;
+ LX_REG(&uc, REG_RSI) = lxrp->lxur_rsi;
+ LX_REG(&uc, REG_RDI) = lxrp->lxur_rdi;
+ LX_REG(&uc, REG_RIP) = lxrp->lxur_rip;
+ LX_REG(&uc, REG_RFL) = PSLMERGE(LX_REG(&uc, REG_RFL),
+ lxrp->lxur_rflags);
+ LX_REG(&uc, REG_RSP) = lxrp->lxur_rsp;
+ LX_REG(&uc, REG_SS) = fix_segreg(lxrp->lxur_xss, IS_NOT_CS,
+ DATAMODEL_LP64);
+ LX_REG(&uc, REG_FSBASE) = lxrp->lxur_xfs_base;
+ LX_REG(&uc, REG_GSBASE) = lxrp->lxur_xgs_base;
+
+ /* %cs is ignored because of our lies */
+ LX_REG(&uc, REG_DS) = fix_segreg(lxrp->lxur_xds, IS_NOT_CS,
+ DATAMODEL_LP64);
+ LX_REG(&uc, REG_ES) = fix_segreg(lxrp->lxur_xes, IS_NOT_CS,
+ DATAMODEL_LP64);
+ LX_REG(&uc, REG_FS) = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS,
+ DATAMODEL_LP64);
+ LX_REG(&uc, REG_GS) = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS,
+ DATAMODEL_LP64);
+
+ if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ case DATAMODEL_ILP32: {
+ ucontext32_t uc;
+
+ if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+
+ /*
+ * Note: we currently ignore "lxur_orig_rax" here since this
+ * path should not be used for system call stops.
+ */
+ LX_REG(&uc, EBP) = (int32_t)lxrp->lxur_rbp;
+ LX_REG(&uc, EBX) = (int32_t)lxrp->lxur_rbx;
+ LX_REG(&uc, EAX) = (int32_t)lxrp->lxur_rax;
+ LX_REG(&uc, ECX) = (int32_t)lxrp->lxur_rcx;
+ LX_REG(&uc, EDX) = (int32_t)lxrp->lxur_rdx;
+ LX_REG(&uc, ESI) = (int32_t)lxrp->lxur_rsi;
+ LX_REG(&uc, EDI) = (int32_t)lxrp->lxur_rdi;
+ LX_REG(&uc, EIP) = (int32_t)lxrp->lxur_rip;
+ LX_REG(&uc, EFL) = (int32_t)PSLMERGE(LX_REG(&uc, EFL),
+ lxrp->lxur_rflags);
+ LX_REG(&uc, UESP) = (int32_t)lxrp->lxur_rsp;
+ LX_REG(&uc, SS) = (int32_t)fix_segreg(lxrp->lxur_xss,
+ IS_NOT_CS, DATAMODEL_ILP32);
+
+ /* %cs is ignored because of our lies */
+ LX_REG(&uc, DS) = (int32_t)fix_segreg(lxrp->lxur_xds,
+ IS_NOT_CS, DATAMODEL_ILP32);
+ LX_REG(&uc, ES) = (int32_t)fix_segreg(lxrp->lxur_xes,
+ IS_NOT_CS, DATAMODEL_ILP32);
+ LX_REG(&uc, FS) = (int32_t)fix_segreg(lxrp->lxur_xfs,
+ IS_NOT_CS, DATAMODEL_ILP32);
+ LX_REG(&uc, GS) = (int32_t)fix_segreg(lxrp->lxur_xgs,
+ IS_NOT_CS, DATAMODEL_ILP32);
+
+ if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+ return (-1);
+ }
+ return (0);
+ }
+
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
+static int
+lx_set_user_regs64(lx_lwp_data_t *lwpd, lx_user_regs64_t *lxrp)
+{
+ klwp_t *lwp = lwpd->br_lwp;
+ struct regs *rp = lwptoregs(lwp);
+ struct pcb *pcb = &lwp->lwp_pcb;
+ void *ucp;
+
+ switch (lx_regs_location(lwpd, &ucp, B_TRUE)) {
+ case LX_REG_LOC_UNAVAIL:
+ return (-1);
+
+ case LX_REG_LOC_UCP:
+ return (lx_set_user_regs64_uc(lwp, ucp, lxrp));
+
+ case LX_REG_LOC_LWP:
+ /* transformation below */
+ break;
+
+ default:
+ VERIFY(0);
+ break;
+ }
+
+ rp->r_r15 = lxrp->lxur_r15;
+ rp->r_r14 = lxrp->lxur_r14;
+ rp->r_r13 = lxrp->lxur_r13;
+ rp->r_r12 = lxrp->lxur_r12;
+ rp->r_rbp = lxrp->lxur_rbp;
+ rp->r_rbx = lxrp->lxur_rbx;
+ rp->r_r11 = lxrp->lxur_r11;
+ rp->r_r10 = lxrp->lxur_r10;
+ rp->r_r9 = lxrp->lxur_r9;
+ rp->r_r8 = lxrp->lxur_r8;
+ rp->r_rax = lxrp->lxur_rax;
+ rp->r_rcx = lxrp->lxur_rcx;
+ rp->r_rdx = lxrp->lxur_rdx;
+ rp->r_rsi = lxrp->lxur_rsi;
+ rp->r_rdi = lxrp->lxur_rdi;
+ lwpd->br_syscall_num = (int)lxrp->lxur_orig_rax;
+ rp->r_rip = lxrp->lxur_rip;
+ rp->r_rfl = PSLMERGE(rp->r_rfl, lxrp->lxur_rflags);
+ rp->r_rsp = lxrp->lxur_rsp;
+ rp->r_ss = fix_segreg(lxrp->lxur_xss, IS_NOT_CS, DATAMODEL_LP64);
+ pcb->pcb_fsbase = lxrp->lxur_xfs_base;
+ pcb->pcb_gsbase = lxrp->lxur_xgs_base;
+
+ kpreempt_disable();
+ PCB_SET_UPDATE_SEGS(pcb);
+ pcb->pcb_ds = fix_segreg(lxrp->lxur_xds, IS_NOT_CS, DATAMODEL_LP64);
+ pcb->pcb_es = fix_segreg(lxrp->lxur_xes, IS_NOT_CS, DATAMODEL_LP64);
+ pcb->pcb_fs = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS, DATAMODEL_LP64);
+ pcb->pcb_gs = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS, DATAMODEL_LP64);
+ kpreempt_enable();
+
+ return (0);
+}
+
+#endif /* __amd64 */
+
+static int
+lx_peekuser32(lx_lwp_data_t *lwpd, uintptr_t offset, uint32_t *res)
+{
+ lx_user32_t lxu;
+ boolean_t valid = B_FALSE;
+
+ bzero(&lxu, sizeof (lxu));
+ if (offset < sizeof (lx_user_regs32_t)) {
+ if (lx_get_user_regs32(lwpd, &lxu.lxu_regs) == 0) {
+ valid = B_TRUE;
+ }
+ }
+ if (valid) {
+ uint32_t *data = (uint32_t *)&lxu;
+ *res = data[offset / sizeof (uint32_t)];
+ return (0);
+ }
+ return (-1);
+}
+
+#ifdef __amd64
+static int
+lx_peekuser64(lx_lwp_data_t *lwpd, uintptr_t offset, uintptr_t *res)
+{
+ lx_user64_t lxu;
+ boolean_t valid = B_FALSE;
+
+ bzero(&lxu, sizeof (lxu));
+ if (offset < sizeof (lx_user_regs64_t)) {
+ if (lx_get_user_regs64(lwpd, &lxu.lxu_regs) == 0) {
+ valid = B_TRUE;
+ }
+ }
+ if (valid) {
+ uintptr_t *data = (uintptr_t *)&lxu;
+ *res = data[offset / sizeof (uintptr_t)];
+ return (0);
+ }
+ return (-1);
+}
+#endif /* __amd64 */
+
+int
+lx_user_regs_copyin(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ if (target_model == DATAMODEL_ILP32) {
+ lx_user_regs32_t regs;
+
+ if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ if (lx_set_user_regs32(lwpd, &regs) != 0) {
+ return (EIO);
+ }
+ return (0);
+ }
+ break;
+
+#ifdef __amd64
+ case DATAMODEL_LP64:
+ if (target_model == DATAMODEL_ILP32 ||
+ target_model == DATAMODEL_LP64) {
+ lx_user_regs64_t regs;
+
+ if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ if (lx_set_user_regs64(lwpd, &regs) != 0) {
+ return (EIO);
+ }
+ return (0);
+ }
+ break;
+#endif /* __amd64 */
+
+ default:
+ break;
+ }
+ return (EIO);
+}
+
+int
+lx_user_regs_copyout(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ if (target_model == DATAMODEL_ILP32) {
+ lx_user_regs32_t regs;
+
+ if (lx_get_user_regs32(lwpd, &regs) != 0) {
+ return (EIO);
+ }
+ if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+
+#ifdef __amd64
+ case DATAMODEL_LP64:
+ if (target_model == DATAMODEL_ILP32 ||
+ target_model == DATAMODEL_LP64) {
+ lx_user_regs64_t regs;
+
+ if (lx_get_user_regs64(lwpd, &regs) != 0) {
+ return (EIO);
+ }
+ if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+#endif /* __amd64 */
+
+ default:
+ break;
+ }
+ return (EIO);
+}
+
+int
+lx_user_fpregs_copyin(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ if (target_model == DATAMODEL_ILP32) {
+ lx_user_fpregs32_t regs;
+
+ if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ lx_setfpregs32(lwpd, &regs);
+ return (0);
+ }
+ break;
+
+#ifdef __amd64
+ case DATAMODEL_LP64:
+ if (target_model == DATAMODEL_ILP32 ||
+ target_model == DATAMODEL_LP64) {
+ lx_user_fpregs64_t regs;
+
+ if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ lx_setfpregs64(lwpd, &regs);
+ return (0);
+ }
+ break;
+#endif /* __amd64 */
+
+ default:
+ break;
+ }
+ return (EIO);
+}
+
+int
+lx_user_fpregs_copyout(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ if (target_model == DATAMODEL_ILP32) {
+ lx_user_fpregs32_t regs;
+
+ lx_getfpregs32(lwpd, &regs);
+ if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+
+#ifdef __amd64
+ case DATAMODEL_LP64:
+ if (target_model == DATAMODEL_ILP32 ||
+ target_model == DATAMODEL_LP64) {
+ lx_user_fpregs64_t regs;
+
+ lx_getfpregs64(lwpd, &regs);
+ if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+#endif /* __amd64 */
+
+ default:
+ break;
+ }
+ return (EIO);
+}
+
+/* ARGSUSED */
+int
+lx_user_fpxregs_copyin(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ /* Punt on fpxregs for now */
+ return (EIO);
+}
+
+/* ARGSUSED */
+int
+lx_user_fpxregs_copyout(lx_lwp_data_t *lwpd, void *uregsp)
+{
+ /* Punt on fpxregs for now */
+ return (EIO);
+}
+
+int
+lx_ptrace_peekuser(lx_lwp_data_t *lwpd, uintptr_t offset, void *uptr)
+{
+ model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+ switch (get_udatamodel()) {
+ case DATAMODEL_ILP32:
+ if ((offset & (sizeof (uint32_t) - 1)) != 0) {
+ /* Must be aligned to 32bit boundary */
+ break;
+ }
+ if (target_model == DATAMODEL_ILP32) {
+ uint32_t res;
+
+ if (lx_peekuser32(lwpd, offset, &res) != 0) {
+ return (EIO);
+ }
+ if (copyout(&res, uptr, sizeof (res)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+
+#ifdef __amd64
+ case DATAMODEL_LP64:
+ if ((offset & (sizeof (uintptr_t) - 1)) != 0) {
+ /* Must be aligned to 64bit boundary */
+ break;
+ }
+ if (target_model == DATAMODEL_ILP32 ||
+ target_model == DATAMODEL_LP64) {
+ uintptr_t res;
+
+ if (lx_peekuser64(lwpd, offset, &res) != 0) {
+ return (EIO);
+ }
+ if (copyout(&res, uptr, sizeof (res)) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+ }
+ break;
+#endif /* __amd64 */
+
+ default:
+ break;
+ }
+ return (EIO);
+}
+
+/* ARGSUSED */
+int
+lx_ptrace_pokeuser(lx_lwp_data_t *lwpd, uintptr_t offset, void *uptr)
+{
+ return (EIO);
+}
+
+
+/*
+ * Load registers and repoint the stack and program counter. This function is
+ * used by the B_JUMP_TO_LINUX brand system call to revector to a Linux
+ * entrypoint.
+ */
+int
+lx_runexe(klwp_t *lwp, void *ucp)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ /*
+ * We should only make it here when transitioning to Linux from
+ * the NATIVE or INIT mode.
+ */
+ VERIFY(lwpd->br_stack_mode == LX_STACK_MODE_NATIVE ||
+ lwpd->br_stack_mode == LX_STACK_MODE_INIT);
+
+#if defined(__amd64)
+ if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) {
+ struct pcb *pcb = &lwp->lwp_pcb;
+
+ /*
+ * Preserve the %fs/%gsbase value for this LWP, as set and used
+ * by native illumos code.
+ */
+ lwpd->br_ntv_fsbase = pcb->pcb_fsbase;
+ lwpd->br_ntv_gsbase = pcb->pcb_gsbase;
+
+ return (getsetcontext(SETCONTEXT, ucp));
+ } else {
+ return (getsetcontext32(SETCONTEXT, ucp));
+ }
+#else
+ return (getsetcontext(SETCONTEXT, ucp));
+#endif
+}
+
+/*
+ * The usermode emulation code is illumos library code. This routine ensures
+ * the segment registers are set up correctly for native illumos code. It
+ * should be called _after_ we have stored the outgoing Linux machine state
+ * but _before_ we return from the kernel to any illumos native code; e.g. the
+ * usermode emulation library, or any interposed signal handlers.
+ *
+ * See the comment on lwp_segregs_save() for how we handle the usermode
+ * registers when we come into the kernel and see update_sregs() for how we
+ * restore.
+ */
+void
+lx_switch_to_native(klwp_t *lwp)
+{
+#if defined(__amd64)
+ model_t datamodel = lwp_getdatamodel(lwp);
+
+ switch (datamodel) {
+ case DATAMODEL_ILP32: {
+ struct pcb *pcb = &lwp->lwp_pcb;
+
+ /*
+ * For 32-bit processes, we ensure that the correct %gs value
+ * is loaded:
+ */
+ kpreempt_disable();
+ if (PCB_NEED_UPDATE_SEGS(pcb)) {
+ /*
+ * If we are already flushing the segment registers,
+ * then ensure we are flushing the native %gs.
+ */
+ pcb->pcb_gs = LWPGS_SEL;
+ } else {
+ struct regs *rp = lwptoregs(lwp);
+
+ /*
+ * If we are not flushing the segment registers yet,
+ * only do so if %gs is not correct already:
+ */
+ if (rp->r_gs != LWPGS_SEL) {
+ pcb->pcb_gs = LWPGS_SEL;
+
+ /*
+ * Ensure we go out via update_sregs.
+ */
+ PCB_SET_UPDATE_SEGS(pcb);
+ }
+ }
+ kpreempt_enable();
+ break;
+ }
+
+ case DATAMODEL_LP64: {
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+ /*
+ * For 64-bit processes we ensure that the correct %fsbase
+ * value is loaded:
+ */
+ if (lwpd->br_ntv_fsbase != 0) {
+ struct pcb *pcb = &lwp->lwp_pcb;
+
+ kpreempt_disable();
+ if (pcb->pcb_fsbase != lwpd->br_ntv_fsbase) {
+ pcb->pcb_fsbase = lwpd->br_ntv_fsbase;
+
+ /*
+ * Ensure we go out via update_sregs.
+ */
+ PCB_SET_UPDATE_SEGS(pcb);
+ }
+ kpreempt_enable();
+ }
+ /*
+ * ... and the correct %gsbase
+ */
+ if (lwpd->br_ntv_gsbase != 0) {
+ struct pcb *pcb = &lwp->lwp_pcb;
+
+ kpreempt_disable();
+ if (pcb->pcb_gsbase != lwpd->br_ntv_gsbase) {
+ pcb->pcb_gsbase = lwpd->br_ntv_gsbase;
+
+ /*
+ * Ensure we go out via update_sregs.
+ */
+ PCB_SET_UPDATE_SEGS(pcb);
+ }
+ kpreempt_enable();
+ }
+ break;
+ }
+
+ default:
+ cmn_err(CE_PANIC, "unknown data model: %d", datamodel);
+ }
+#elif defined(__i386)
+ struct regs *rp = lwptoregs(lwp);
+
+ rp->r_gs = LWPGS_SEL;
+#else
+#error "unknown x86"
+#endif
+}
+
+#if defined(__amd64)
+/*
+ * Call frame for the 64-bit usermode emulation handler:
+ * lx_emulate(ucontext_t *ucp, int syscall_num, uintptr_t *args)
+ *
+ * old sp: --------------------------------------------------------------
+ * | - ucontext_t (register state for emulation)
+ * | - uintptr_t[6] (system call arguments array)
+ * V --------------------------------------------------------------
+ * new sp: - bogus return address
+ *
+ * Arguments are passed in registers, per the AMD64 ABI: %rdi, %rsi and %rdx.
+ */
+void
+lx_emulate_user(klwp_t *lwp, int syscall_num, uintptr_t *args)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ struct regs *rp = lwptoregs(lwp);
+ label_t lab;
+ uintptr_t uc_addr;
+ uintptr_t args_addr;
+ uintptr_t top;
+ /*
+ * Variables used after on_fault() returns for a fault
+ * must be volatile.
+ */
+ volatile size_t frsz;
+ volatile uintptr_t sp;
+ volatile proc_t *p = lwptoproc(lwp);
+ volatile int watched;
+
+ /*
+ * We should not be able to get here unless we are running Linux
+ * code for a system call we cannot emulate in the kernel.
+ */
+ VERIFY(lwpd->br_stack_mode == LX_STACK_MODE_BRAND);
+
+ /*
+ * The AMD64 ABI requires us to align the return address on the stack
+ * so that when the called function pushes %rbp, the stack is 16-byte
+ * aligned.
+ *
+ * This routine, like the amd64 version of sendsig(), depends on
+ * STACK_ALIGN being 16 and STACK_ENTRY_ALIGN being 8.
+ */
+#if STACK_ALIGN != 16 || STACK_ENTRY_ALIGN != 8
+#error "lx_emulate_user() amd64 did not find the expected stack alignments"
+#endif
+
+ /*
+ * We begin at the current native stack pointer, and reserve space for
+ * the ucontext_t we are copying onto the stack, as well as the call
+ * arguments for the usermode emulation handler.
+ *
+ * We 16-byte align the entire frame, and then unalign it again by
+ * adding space for the return address.
+ */
+ frsz = SA(sizeof (ucontext_t)) + SA(6 * sizeof (uintptr_t)) +
+ sizeof (uintptr_t);
+ VERIFY((frsz & (STACK_ALIGN - 1UL)) == 8);
+ VERIFY((frsz & (STACK_ENTRY_ALIGN - 1UL)) == 0);
+
+ if (lwpd->br_ntv_stack == lwpd->br_ntv_stack_current) {
+ /*
+ * Nobody else is using the stack right now, so start at the
+ * top.
+ */
+ top = lwpd->br_ntv_stack_current;
+ } else {
+ /*
+ * Drop below the 128-byte reserved region of the stack frame
+ * we are interrupting.
+ */
+ top = lwpd->br_ntv_stack_current - STACK_RESERVE;
+ }
+ top = top & ~(STACK_ALIGN - 1);
+ sp = top - frsz;
+
+ uc_addr = top - SA(sizeof (ucontext_t));
+ args_addr = uc_addr - SA(6 * sizeof (uintptr_t));
+
+ watched = watch_disable_addr((caddr_t)sp, frsz, S_WRITE);
+
+ /*
+ * Save the register state we preserved on the way into this brand
+ * system call and drop it on the native stack.
+ */
+ {
+ /*
+ * Note: the amd64 ucontext_t is 864 bytes.
+ */
+ ucontext_t uc;
+
+ /*
+ * We do not want to save the signal mask for an emulation
+ * context. Some emulated system calls alter the signal mask;
+ * restoring it when the emulation is complete would clobber
+ * those intentional side effects.
+ */
+ savecontext(&uc, NULL);
+
+ if (on_fault(&lab)) {
+ goto badstack;
+ }
+
+ /*
+ * Mark this as a system call emulation context:
+ */
+ uc.uc_brand_data[0] = (void *)((uintptr_t)
+ uc.uc_brand_data[0] | LX_UC_FRAME_IS_SYSCALL);
+
+ copyout_noerr(&uc, (void *)(uintptr_t)uc_addr, sizeof (uc));
+ }
+
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, uc_addr);
+ lwp->lwp_oldcontext = (uintptr_t)uc_addr;
+
+ /*
+ * Copy the system call arguments out to userland:
+ */
+ copyout_noerr(args, (void *)(uintptr_t)args_addr,
+ 6 * sizeof (uintptr_t));
+
+ /*
+ * Drop the bogus return address on the stack.
+ */
+ suword64_noerr((void *)sp, 0);
+
+ no_fault();
+ if (watched) {
+ watch_enable_addr((caddr_t)sp, frsz, S_WRITE);
+ }
+
+ /*
+ * Pass the arguments to lx_emulate() in the appropriate registers.
+ */
+ rp->r_rdi = uc_addr;
+ rp->r_rsi = syscall_num;
+ rp->r_rdx = args_addr;
+
+ /*
+ * In order to be able to restore %edx, we need to JUSTRETURN.
+ */
+ lwp->lwp_eosys = JUSTRETURN;
+ curthread->t_post_sys = 1;
+ aston(curthread);
+
+ /*
+ * Set stack pointer and return address to the usermode emulation
+ * handler:
+ */
+ lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+ lx_lwp_set_native_stack_current(lwpd, sp);
+
+ /*
+ * Divert execution, on our return, to the usermode emulation stack
+ * and handler:
+ */
+ rp->r_fp = 0;
+ rp->r_sp = sp;
+ rp->r_pc = ptolxproc(p)->l_handler;
+
+ /*
+ * Fix up segment registers, etc.
+ */
+ lx_switch_to_native(lwp);
+
+ return;
+
+badstack:
+ no_fault();
+ if (watched) {
+ watch_enable_addr((caddr_t)sp, frsz, S_WRITE);
+ }
+
+#ifdef DEBUG
+ printf("lx_emulate_user: bad native stack cmd=%s, pid=%d, sp=0x%lx\n",
+ PTOU(p)->u_comm, p->p_pid, sp);
+#endif
+
+ exit(CLD_KILLED, SIGSEGV);
+}
+
+#if defined(_SYSCALL32_IMPL)
+/*
+ * Call frame for the 32-bit usermode emulation handler:
+ * lx_emulate(ucontext_t *ucp, int syscall_num, uintptr_t *args)
+ *
+ * old sp: --------------------------------------------------------------
+ * | - ucontext_t (register state for emulation)
+ * | - uintptr_t[6] (system call arguments array)
+ * | --------------------------------------------------------------
+ * | - arg2: uintptr_t * (pointer to arguments array above)
+ * | - arg1: int (system call number)
+ * V - arg0: ucontext_t * (pointer to context above)
+ * new sp: - bogus return address
+ */
+struct lx_emu_frame32 {
+ caddr32_t retaddr; /* 0 */
+ caddr32_t ucontextp; /* 4 */
+ int32_t syscall_num; /* 8 */
+ caddr32_t argsp; /* c */
+};
+
+/*
+ * This function arranges for the lwp to execute the usermode emulation handler
+ * for this system call. The mechanism is similar to signal handling, and this
+ * function is modelled on sendsig32().
+ */
+void
+lx_emulate_user32(klwp_t *lwp, int syscall_num, uintptr_t *args)
+{
+ lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+ struct regs *rp = lwptoregs(lwp);
+ label_t lab;
+ caddr32_t uc_addr;
+ caddr32_t args_addr;
+ caddr32_t top;
+ /*
+ * Variables used after on_fault() returns for a fault
+ * must be volatile.
+ */
+ volatile size_t frsz;
+ volatile caddr32_t sp;
+ volatile proc_t *p = lwptoproc(lwp);
+ volatile int watched;
+
+ /*
+ * We should not be able to get here unless we are running Linux
+ * code for a system call we cannot emulate in the kernel.
+ */
+ VERIFY(lwpd->br_stack_mode == LX_STACK_MODE_BRAND);
+
+ /*
+ * We begin at the current native stack pointer, and reserve space for
+ * the ucontext_t we are copying onto the stack, as well as the call
+ * arguments for the usermode emulation handler.
+ */
+ frsz = SA32(sizeof (ucontext32_t)) + SA32(6 * sizeof (uint32_t)) +
+ SA32(sizeof (struct lx_emu_frame32));
+ VERIFY((frsz & (STACK_ALIGN32 - 1)) == 0);
+
+ top = (caddr32_t)(lwpd->br_ntv_stack_current & ~(STACK_ALIGN32 - 1));
+ sp = top - frsz;
+
+ uc_addr = top - SA32(sizeof (ucontext32_t));
+ args_addr = uc_addr - SA32(6 * sizeof (uint32_t));
+
+ watched = watch_disable_addr((caddr_t)(uintptr_t)sp, frsz, S_WRITE);
+
+ /*
+ * Save the register state we preserved on the way into this brand
+ * system call and drop it on the native stack.
+ */
+ {
+ /*
+ * Note: ucontext32_t is 512 bytes.
+ */
+ ucontext32_t uc;
+
+ /*
+ * We do not want to save the signal mask for an emulation
+ * context. Some emulated system calls alter the signal mask;
+ * restoring it when the emulation is complete would clobber
+ * those intentional side effects.
+ */
+ savecontext32(&uc, NULL);
+
+ if (on_fault(&lab)) {
+ goto badstack;
+ }
+
+ /*
+ * Mark this as a system call emulation context:
+ */
+ uc.uc_brand_data[0] |= LX_UC_FRAME_IS_SYSCALL;
+ copyout_noerr(&uc, (void *)(uintptr_t)uc_addr, sizeof (uc));
+ }
+
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, uc_addr);
+ lwp->lwp_oldcontext = (uintptr_t)uc_addr;
+
+ /*
+ * Copy the system call arguments out to userland:
+ */
+ {
+ uint32_t args32[6];
+
+ args32[0] = args[0];
+ args32[1] = args[1];
+ args32[2] = args[2];
+ args32[3] = args[3];
+ args32[4] = args[4];
+ args32[5] = args[5];
+
+ copyout_noerr(&args32, (void *)(uintptr_t)args_addr,
+ sizeof (args32));
+ }
+
+ /*
+ * Assemble the call frame on the stack.
+ */
+ {
+ struct lx_emu_frame32 frm;
+
+ frm.retaddr = 0;
+ frm.ucontextp = uc_addr;
+ frm.argsp = args_addr;
+ frm.syscall_num = syscall_num;
+
+ copyout_noerr(&frm, (void *)(uintptr_t)sp, sizeof (frm));
+ }
+
+ no_fault();
+ if (watched) {
+ watch_enable_addr((caddr_t)(uintptr_t)sp, frsz, S_WRITE);
+ }
+
+ /*
+ * Set stack pointer and return address to the usermode emulation
+ * handler:
+ */
+ lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+ lx_lwp_set_native_stack_current(lwpd, sp);
+
+ /*
+ * Divert execution, on our return, to the usermode emulation stack
+ * and handler:
+ */
+ rp->r_fp = 0;
+ rp->r_sp = sp;
+ rp->r_pc = ptolxproc(p)->l_handler;
+
+ /*
+ * Fix up segment registers, etc.
+ */
+ lx_switch_to_native(lwp);
+
+ return;
+
+badstack:
+ no_fault();
+ if (watched) {
+ watch_enable_addr((caddr_t)(uintptr_t)sp, frsz, S_WRITE);
+ }
+
+#ifdef DEBUG
+ printf("lx_emulate_user32: bad native stack cmd=%s, pid=%d, sp=0x%x\n",
+ PTOU(p)->u_comm, p->p_pid, sp);
+#endif
+
+ exit(CLD_KILLED, SIGSEGV);
+}
+#endif /* _SYSCALL32_IMPL */
+
+#else /* !__amd64 (__i386) */
+
+/* ARGSUSED */
+void
+lx_emulate_user(klwp_t *lwp, int syscall_num, uintptr_t *args)
+{
+ cmn_err(CE_WARN, "%s: no 32-bit kernel support", __FUNCTION__);
+ exit(CLD_KILLED, SIGSYS);
+}
+
+#endif /* __amd64 */
diff --git a/usr/src/uts/intel/core_pcbe/Makefile b/usr/src/uts/intel/core_pcbe/Makefile
index abb2713efc..f48ff5e69a 100644
--- a/usr/src/uts/intel/core_pcbe/Makefile
+++ b/usr/src/uts/intel/core_pcbe/Makefile
@@ -70,7 +70,7 @@ CPCGEN_SRCS = $(CPCGEN_OBJS:%.o=%.c) core_pcbe_cpcgen.h
MODULE = pcbe.GenuineIntel.6.15
OBJECTS = $(CORE_PCBE_OBJS:%=$(OBJS_DIR)/%)
OBJECTS += $(CPCGEN_OBJS:%=$(OBJS_DIR)/%)
-ROOTMODULE = $(USR_PCBE_DIR)/$(MODULE)
+ROOTMODULE = $(ROOT_PSM_PCBE_DIR)/$(MODULE)
#
# This order matches the families declared in uts/intel/sys/x86_archext.h.
@@ -120,7 +120,7 @@ SOFTLINKS = \
pcbe.GenuineIntel.6.140 \
pcbe.GenuineIntel.6.141
-ROOTSOFTLINKS = $(SOFTLINKS:%=$(USR_PCBE_DIR)/%)
+ROOTSOFTLINKS = $(SOFTLINKS:%=$(ROOT_PSM_PCBE_DIR)/%)
#
# Include common rules.
diff --git a/usr/src/uts/intel/datafilt/Makefile b/usr/src/uts/intel/datafilt/Makefile
new file mode 100644
index 0000000000..89d8354e09
--- /dev/null
+++ b/usr/src/uts/intel/datafilt/Makefile
@@ -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) 2011, OmniTI Computer Consulting, Inc. All rights reserved.
+# Copyright 2012, Nexenta Systems, Inc. All rights reserved.
+#
+
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = datafilt
+OBJECTS = $(DATAFILT_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_SOCK_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -Nfs/sockfs -Ndrv/ip
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/dld/Makefile b/usr/src/uts/intel/dld/Makefile
index acc064ca35..6bed0a217b 100644
--- a/usr/src/uts/intel/dld/Makefile
+++ b/usr/src/uts/intel/dld/Makefile
@@ -54,7 +54,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -N misc/dls -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# For now, disable these warnings; maintainers should endeavor
diff --git a/usr/src/uts/intel/dls/Makefile b/usr/src/uts/intel/dls/Makefile
index e10be370a9..f600dd5391 100644
--- a/usr/src/uts/intel/dls/Makefile
+++ b/usr/src/uts/intel/dls/Makefile
@@ -52,7 +52,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# For now, disable these warnings; maintainers should endeavor
diff --git a/usr/src/uts/intel/dtrace/fasttrap_isa.c b/usr/src/uts/intel/dtrace/fasttrap_isa.c
index 9318fd5e9b..d71d70f9eb 100644
--- a/usr/src/uts/intel/dtrace/fasttrap_isa.c
+++ b/usr/src/uts/intel/dtrace/fasttrap_isa.c
@@ -24,6 +24,10 @@
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
+
#include <sys/fasttrap_isa.h>
#include <sys/fasttrap_impl.h>
#include <sys/dtrace.h>
@@ -36,6 +40,9 @@
#include <sys/sysmacros.h>
#include <sys/trap.h>
#include <sys/archsystm.h>
+#include <sys/proc.h>
+#include <sys/brand.h>
+#include <sys/machbrand.h>
/*
* Lossless User-Land Tracing on x86
@@ -1335,6 +1342,14 @@ fasttrap_pid_probe(struct regs *rp)
*/
if (p->p_model == DATAMODEL_LP64) {
addr = lwp->lwp_pcb.pcb_fsbase;
+
+ /*
+ * If we're branded, convert the fsbase from the
+ * brand's fsbase to the native fsbase.
+ */
+ if (PROC_IS_BRANDED(p) && BRMOP(p)->b_fsbase != NULL)
+ addr = BRMOP(p)->b_fsbase(lwp, addr);
+
addr += sizeof (void *);
} else {
addr = lwp->lwp_pcb.pcb_gsbase;
diff --git a/usr/src/uts/intel/genassym/Makefile b/usr/src/uts/intel/genassym/Makefile
new file mode 100644
index 0000000000..a42925cfab
--- /dev/null
+++ b/usr/src/uts/intel/genassym/Makefile
@@ -0,0 +1,83 @@
+#
+# 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.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# This makefile drives the production of genassym.h through
+# compile time intialized data.
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+GENASSYM_H = $(GENASSYM_DIR)/$(OBJS_DIR)/genassym.h
+OFFSETS_SRC = $(GENASSYM_DIR)/offsets.in
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(GENASSYM_H)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx
+
+#
+# Overrides
+#
+CLEANFILES = Nothing_to_remove
+CLOBBERFILES = $(GENASSYM_H) Nothing_to_remove
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: def
+
+#
+# Create genassym.h
+#
+$(GENASSYM_H): $(OFFSETS_SRC)
+ $(OFFSETS_CREATE) <$(OFFSETS_SRC) >$@
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/genassym/offsets.in b/usr/src/uts/intel/genassym/offsets.in
new file mode 100644
index 0000000000..70221c02f9
--- /dev/null
+++ b/usr/src/uts/intel/genassym/offsets.in
@@ -0,0 +1,43 @@
+\
+\ CDDL HEADER START
+\
+\ The contents of this file are subject to the terms of the
+\ Common Development and Distribution License (the "License").
+\ You may not use this file except in compliance with the License.
+\
+\ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+\ or http://www.opensolaris.org/os/licensing.
+\ See the License for the specific language governing permissions
+\ and limitations under the License.
+\
+\ When distributing Covered Code, include this CDDL HEADER in each
+\ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+\ If applicable, add the following below this CDDL HEADER, with the
+\ fields enclosed by brackets "[]" replaced with your own identifying
+\ information: Portions Copyright [yyyy] [name of copyright owner]
+\
+\ CDDL HEADER END
+\
+\
+\ Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+\ Use is subject to license terms.
+\ Copyright 2015 Joyent, Inc.
+\
+
+\
+\ offsets.in: input file to produce the architecture-dependent genassym.h
+\ using the ctfstabs program
+\
+
+#ifndef _GENASSYM
+#define _GENASSYM
+#endif
+
+#include <sys/lx_brand.h>
+
+lx_proc_data
+ l_handler
+
+lx_lwp_data
+ br_lx_fsbase
+ br_ntv_fsbase
diff --git a/usr/src/uts/intel/gsqueue/Makefile b/usr/src/uts/intel/gsqueue/Makefile
new file mode 100644
index 0000000000..330205cbb6
--- /dev/null
+++ b/usr/src/uts/intel/gsqueue/Makefile
@@ -0,0 +1,41 @@
+#
+# 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.
+#
+
+UTSBASE = ../..
+
+MODULE = gsqueue
+OBJECTS = $(GSQUEUE_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+LDFLAGS += -Ndrv/ip
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/hyprlofs/Makefile b/usr/src/uts/intel/hyprlofs/Makefile
new file mode 100644
index 0000000000..bf7a225fc4
--- /dev/null
+++ b/usr/src/uts/intel/hyprlofs/Makefile
@@ -0,0 +1,74 @@
+#
+# 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
+#
+#
+# uts/intel/hyprlofs/Makefile
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2019 Joyent, Inc.
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = hyprlofs
+OBJECTS = $(HYPRLOFS_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_FS_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+# needs work
+$(OBJS_DIR)/hyprlofs_vnops.o := SMOFF += signed
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/icmp/Makefile b/usr/src/uts/intel/icmp/Makefile
index 87785610c8..c5bd9a810f 100644
--- a/usr/src/uts/intel/icmp/Makefile
+++ b/usr/src/uts/intel/icmp/Makefile
@@ -62,6 +62,8 @@ include $(UTSBASE)/intel/Makefile.intel
ALL_TARGET = $(BINARY) $(SRC_CONFFILE)
INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+INC_PATH += -I$(UTSBASE)/common/io/bpf
+
#
# depends on ip and sockfs
#
diff --git a/usr/src/uts/intel/inotify/Makefile b/usr/src/uts/intel/inotify/Makefile
new file mode 100644
index 0000000000..3198797024
--- /dev/null
+++ b/usr/src/uts/intel/inotify/Makefile
@@ -0,0 +1,61 @@
+#
+# 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.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = inotify
+OBJECTS = $(INOTIFY_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+CERRWARN += -_gcc=-Wno-parentheses
+LDFLAGS += -Nfs/specfs
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/io/dktp/dcdev/dadk.c b/usr/src/uts/intel/io/dktp/dcdev/dadk.c
index 35f97482b8..f74a0d4137 100644
--- a/usr/src/uts/intel/io/dktp/dcdev/dadk.c
+++ b/usr/src/uts/intel/io/dktp/dcdev/dadk.c
@@ -21,6 +21,7 @@
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
*/
/*
@@ -170,6 +171,8 @@ static int dadk_debug = DGEOM;
#endif /* DADK_DEBUG */
+#define ONE_MIN ((longlong_t)60 * NANOSEC)
+
static int dadk_check_media_time = 3000000; /* 3 Second State Check */
static int dadk_dk_maxphys = 0x80000;
@@ -1376,6 +1379,47 @@ static struct dadkio_derr dadk_errtab[] = {
{COMMAND_DONE_ERROR, GDA_FATAL}, /* 23 DERR_RESV */
};
+/*
+ * A bad disk can result in a large number of errors spewed to the log.
+ * This can in turn lead to /var/adm/messages filling up the file system on
+ * a machine with a small root or /var file system.
+ *
+ * Instead of logging every error, if we're seeing repeated errors on a disk
+ * only log them periodically.
+ */
+static void
+dadk_logerr(struct dadk *dadkp, struct cmpkt *pktp, char *label,
+ int severity, daddr_t blkno, daddr_t err_blkno,
+ char **cmdvec, char **senvec)
+{
+ hrtime_t now;
+
+ now = gethrtime();
+ if ((now - dadkp->dad_last_log) < ONE_MIN) {
+ atomic_add_32(&dadkp->dad_err_cnt, 1);
+ return;
+ }
+
+ if (dadkp->dad_err_cnt > 0) {
+ dev_info_t *dev = dadkp->dad_sd->sd_dev;
+ char name[256], buf[256];
+
+ if (dev)
+ (void) snprintf(name, sizeof (name), "%s (%s%d)",
+ ddi_pathname(dev, buf), label,
+ ddi_get_instance(dev));
+ else
+ (void) strlcpy(name, label, sizeof (name));
+ cmn_err(CE_WARN, "%s: %d additional unlogged errors\n",
+ name, dadkp->dad_err_cnt);
+ }
+
+ gda_errmsg(dadkp->dad_sd, pktp, label, severity, blkno, err_blkno,
+ cmdvec, senvec);
+ dadkp->dad_err_cnt = 0;
+ dadkp->dad_last_log = now;
+}
+
static int
dadk_chkerr(struct cmpkt *pktp)
{
@@ -1462,7 +1506,7 @@ dadk_chkerr(struct cmpkt *pktp)
return (COMMAND_DONE);
}
if (pktp->cp_passthru == NULL) {
- gda_errmsg(dadkp->dad_sd, pktp, dadk_name,
+ dadk_logerr(dadkp, pktp, dadk_name,
dadk_errtab[scb].d_severity, pktp->cp_srtsec,
err_blkno, dadk_cmds, dadk_sense);
}
@@ -1519,7 +1563,7 @@ dadk_recorderr(struct cmpkt *pktp, struct dadkio_rwcmd *rwcmdp)
if (rwcmdp->flags & DADKIO_FLAG_SILENT)
return;
- gda_errmsg(dadkp->dad_sd, pktp, dadk_name, dadk_errtab[scb].d_severity,
+ dadk_logerr(dadkp, pktp, dadk_name, dadk_errtab[scb].d_severity,
rwcmdp->blkaddr, rwcmdp->status.failed_blk,
dadk_cmds, dadk_sense);
}
diff --git a/usr/src/uts/intel/io/ipmi/ipmivars.h b/usr/src/uts/intel/io/ipmi/ipmivars.h
index fec94bb24f..cd73753438 100644
--- a/usr/src/uts/intel/io/ipmi/ipmivars.h
+++ b/usr/src/uts/intel/io/ipmi/ipmivars.h
@@ -78,6 +78,7 @@ struct ipmi_request {
#define SMIC_CTL_STS 1
#define SMIC_FLAGS 2
+struct ipmi_softc;
#define IPMI_BUSY 0x1
#define IPMI_CLOSING 0x2
diff --git a/usr/src/uts/intel/io/pci/pci_boot.c b/usr/src/uts/intel/io/pci/pci_boot.c
index ab3b5a5a8f..d5de14a9bc 100644
--- a/usr/src/uts/intel/io/pci/pci_boot.c
+++ b/usr/src/uts/intel/io/pci/pci_boot.c
@@ -3093,7 +3093,7 @@ add_ppb_props(dev_info_t *dip, uchar_t bus, uchar_t dev, uchar_t func,
* If it is unset, we disable i/o and mark it for reconfiguration in
* later passes by setting the base > limit
*/
- val = (uint_t)pci_getw(bus, dev, func, PCI_CONF_COMM);
+ val = (uint64_t)pci_getw(bus, dev, func, PCI_CONF_COMM);
if (val & PCI_COMM_IO) {
val = (uint_t)pci_getb(bus, dev, func, PCI_BCNF_IO_LIMIT_LOW);
io_range[1] = ((val & PCI_BCNF_IO_MASK) << PCI_BCNF_IO_SHIFT) |
diff --git a/usr/src/uts/intel/io/scsi/targets/sd.conf b/usr/src/uts/intel/io/scsi/targets/sd.conf
index 1863937888..b0aebdb5b1 100644
--- a/usr/src/uts/intel/io/scsi/targets/sd.conf
+++ b/usr/src/uts/intel/io/scsi/targets/sd.conf
@@ -42,7 +42,7 @@ name="sd" class="scsi" target=15 lun=0;
#
# The following stub node is needed for pathological bottom-up
-# devid resolution on a self-identifying transport.
+# devid resolution on a self-identifying transport.
#
name="sd" class="scsi-self-identifying";
@@ -50,3 +50,34 @@ name="sd" class="scsi-self-identifying";
# Associate the driver with devid resolution.
#
ddi-devid-registrant=1;
+
+#
+# Certain hardware RAID controllers have nonvolatile caches but do not
+# support the SYNC_NV bit to restrict flushes to the volatile portion of
+# the cache, if any. In order to get acceptable performance out of these
+# devices, we need to suppress cache flushing on them. In most (hopefully
+# all) cases, if the battery fails or the cache otherwise becomes volatile,
+# the controller will switch to write-through mode, and ensure that any
+# underlying drive cache is off. In this case, it should still be safe to
+# dispense with cache flush commands. Controllers for which this is not the
+# case should have cache-nonvolatile set unless data loss and corruption are
+# acceptable.
+#
+# In addition, *all* devices have their retries capped at 1. There are an
+# additional 2 retries for "victim" IOs if a reset is needed. Retrying is
+# very rarely successful, and it is preferable to let ZFS do it where needed.
+#
+# For the Samsung client drives, users have seen data corruption when they use
+# the advertised 512 byte sectors. The actual sector size of the flash
+# translation layer is 4K, so it's relatively safe to make this change which
+# annecdotally solves that problem.
+#
+sd-config-list=
+ "", "retries-timeout:1,retries-busy:1,retries-reset:1,retries-victim:2",
+ "DELL PERC H710", "cache-nonvolatile:true",
+ "DELL PERC H700", "cache-nonvolatile:true",
+ "DELL PERC/6i", "cache-nonvolatile:true",
+ "ATA Samsung SSD 830", "physical-block-size:4096",
+ "ATA Samsung SSD 840", "physical-block-size:4096",
+ "ATA Samsung SSD 850", "physical-block-size:4096",
+ "ATA Samsung SSD 860", "physical-block-size:4096";
diff --git a/usr/src/uts/intel/io/vmxnet/buildNumber.h b/usr/src/uts/intel/io/vmxnet/buildNumber.h
new file mode 100644
index 0000000000..97f18a3cbc
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/buildNumber.h
@@ -0,0 +1,12 @@
+#define BUILD_NUMBER \
+ "build-425873"
+#define BUILD_NUMBER_NUMERIC \
+ 425873
+#define BUILD_NUMBER_NUMERIC_STRING \
+ "425873"
+#define PRODUCT_BUILD_NUMBER \
+ "product-build-6261"
+#define PRODUCT_BUILD_NUMBER_NUMERIC \
+ 6261
+#define PRODUCT_BUILD_NUMBER_NUMERIC_STRING \
+ "6261"
diff --git a/usr/src/uts/intel/io/vmxnet/includeCheck.h b/usr/src/uts/intel/io/vmxnet/includeCheck.h
new file mode 100644
index 0000000000..c414d6daf5
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/includeCheck.h
@@ -0,0 +1,159 @@
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*********************************************************
+ * 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.
+ * 3. Neither the name of VMware Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission of VMware Inc.
+ *
+ * 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.
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * includeCheck.h --
+ *
+ * Restrict include file use.
+ *
+ * In every .h file, define one or more of these
+ *
+ * INCLUDE_ALLOW_VMX
+ * INCLUDE_ALLOW_USERLEVEL
+ * INCLUDE_ALLOW_VMCORE
+ * INCLUDE_ALLOW_MODULE
+ * INCLUDE_ALLOW_VMKERNEL
+ * INCLUDE_ALLOW_DISTRIBUTE
+ * INCLUDE_ALLOW_VMK_MODULE
+ * INCLUDE_ALLOW_VMKDRIVERS
+ * INCLUDE_ALLOW_VMIROM
+ *
+ * Then include this file.
+ *
+ * Any file that has INCLUDE_ALLOW_DISTRIBUTE defined will potentially
+ * be distributed in source form along with GPLed code. Ensure
+ * that this is acceptable.
+ */
+
+
+/*
+ * Declare a VMCORE-only variable to help classify object
+ * files. The variable goes in the common block and does
+ * not create multiple definition link-time conflicts.
+ */
+
+#if defined VMCORE && defined VMX86_DEVEL && defined VMX86_DEBUG && \
+ defined linux && !defined MODULE && \
+ !defined COMPILED_WITH_VMCORE
+#define COMPILED_WITH_VMCORE compiled_with_vmcore
+#ifdef ASM
+ .comm compiled_with_vmcore, 0
+#else
+ asm(".comm compiled_with_vmcore, 0");
+#endif /* ASM */
+#endif
+
+
+#if defined VMCORE && \
+ !(defined VMX86_VMX || defined VMM || \
+ defined MONITOR_APP || defined VMMON)
+#error "Makefile problem: VMCORE without VMX86_VMX or \
+ VMM or MONITOR_APP or MODULE."
+#endif
+
+#if defined VMCORE && !defined INCLUDE_ALLOW_VMCORE
+#error "The surrounding include file is not allowed in vmcore."
+#endif
+#undef INCLUDE_ALLOW_VMCORE
+
+#if defined VMX86_VMX && !defined VMCORE && \
+ !(defined INCLUDE_ALLOW_VMX || defined INCLUDE_ALLOW_USERLEVEL)
+#error "The surrounding include file is not allowed in the VMX."
+#endif
+#undef INCLUDE_ALLOW_VMX
+
+#if defined USERLEVEL && !defined VMX86_VMX && !defined VMCORE && \
+ !defined INCLUDE_ALLOW_USERLEVEL
+#error "The surrounding include file is not allowed at userlevel."
+#endif
+#undef INCLUDE_ALLOW_USERLEVEL
+
+#if defined MODULE && !defined VMKERNEL_MODULE && \
+ !defined VMMON && !defined INCLUDE_ALLOW_MODULE
+#error "The surrounding include file is not allowed in driver modules."
+#endif
+#undef INCLUDE_ALLOW_MODULE
+
+#if defined VMMON && !defined INCLUDE_ALLOW_VMMON
+#error "The surrounding include file is not allowed in vmmon."
+#endif
+#undef INCLUDE_ALLOW_VMMON
+
+#if defined VMKERNEL && !defined INCLUDE_ALLOW_VMKERNEL
+#error "The surrounding include file is not allowed in the vmkernel."
+#endif
+#undef INCLUDE_ALLOW_VMKERNEL
+
+#if defined GPLED_CODE && !defined INCLUDE_ALLOW_DISTRIBUTE
+#error "The surrounding include file is not allowed in GPL code."
+#endif
+#undef INCLUDE_ALLOW_DISTRIBUTE
+
+#if defined VMKERNEL_MODULE && !defined VMKERNEL && \
+ !defined INCLUDE_ALLOW_VMK_MODULE && !defined INCLUDE_ALLOW_VMKDRIVERS
+#error "The surrounding include file is not allowed in vmkernel modules."
+#endif
+#undef INCLUDE_ALLOW_VMK_MODULE
+#undef INCLUDE_ALLOW_VMKDRIVERS
+
+#if defined VMIROM && ! defined INCLUDE_ALLOW_VMIROM
+#error "The surrounding include file is not allowed in vmirom."
+#endif
+#undef INCLUDE_ALLOW_VMIROM
diff --git a/usr/src/uts/intel/io/vmxnet/net.h b/usr/src/uts/intel/io/vmxnet/net.h
new file mode 100644
index 0000000000..41b6eb1d14
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/net.h
@@ -0,0 +1,220 @@
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/************************************************************
+ *
+ * net.h
+ *
+ * This file should contain all network global defines.
+ * No vlance/vmxnet/vnet/vmknet specific stuff should be
+ * put here only defines used/usable by all network code.
+ * --gustav
+ *
+ ************************************************************/
+
+#ifndef VMWARE_DEVICES_NET_H
+#define VMWARE_DEVICES_NET_H
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMCORE
+
+#include "includeCheck.h"
+#include "vm_device_version.h"
+
+#ifdef VMCORE
+#include "config.h"
+#include "str.h"
+#include "strutil.h"
+#endif
+
+#define ETHERNET_MTU 1518
+#define ETH_MIN_FRAME_LEN 60
+
+#ifndef ETHER_ADDR_LEN
+#define ETHER_ADDR_LEN 6 /* length of MAC address */
+#endif
+#define ETH_HEADER_LEN 14 /* length of Ethernet header */
+#define IP_ADDR_LEN 4 /* length of IPv4 address */
+#define IP_HEADER_LEN 20 /* minimum length of IPv4 header */
+
+#define ETHER_MAX_QUEUED_PACKET 1600
+
+
+/*
+ * State's that a NIC can be in currently we only use this
+ * in VLance but if we implement/emulate new adapters that
+ * we also want to be able to morph a new corresponding
+ * state should be added.
+ */
+
+#define LANCE_CHIP 0x2934
+#define VMXNET_CHIP 0x4392
+
+/*
+ * Size of reserved IO space needed by the LANCE adapter and
+ * the VMXNET adapter. If you add more ports to Vmxnet than
+ * there is reserved space you must bump VMXNET_CHIP_IO_RESV_SIZE.
+ * The sizes must be powers of 2.
+ */
+
+#define LANCE_CHIP_IO_RESV_SIZE 0x20
+#define VMXNET_CHIP_IO_RESV_SIZE 0x40
+
+#define MORPH_PORT_SIZE 4
+
+#ifdef VMCORE
+typedef struct Net_AdapterCount {
+ uint8 vlance;
+ uint8 vmxnet2;
+ uint8 vmxnet3;
+ uint8 e1000;
+ uint8 e1000e;
+} Net_AdapterCount;
+#endif
+
+#ifdef USERLEVEL
+
+/*
+ *----------------------------------------------------------------------------
+ *
+ * Net_AddAddrToLADRF --
+ *
+ * Given a MAC address, sets the corresponding bit in the LANCE style
+ * Logical Address Filter 'ladrf'.
+ * The caller should have initialized the ladrf to all 0's, as this
+ * function only ORs on a bit in the array.
+ * 'addr' is presumed to be ETHER_ADDR_LEN in size;
+ * 'ladrf' is presumed to point to a 64-bit vector.
+ *
+ * Derived from a long history of derivations, originally inspired by
+ * sample code from the AMD "Network Products: Ethernet Controllers 1998
+ * Data Book, Book 2", pages 1-53..1-55.
+ *
+ * Returns:
+ * None.
+ *
+ * Side effects:
+ * Updates 'ladrf'.
+ *
+ *----------------------------------------------------------------------------
+ */
+
+static INLINE void
+Net_AddAddrToLadrf(const uint8 *addr, // IN: pointer to MAC address
+ uint8 *ladrf) // IN/OUT: pointer to ladrf
+{
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+
+ uint16 hashcode;
+ int32 crc = 0xffffffff; /* init CRC for each address */
+ int32 j;
+ int32 bit;
+ int32 byte;
+
+ ASSERT(addr);
+ ASSERT(ladrf);
+
+ for (byte = 0; byte < ETHER_ADDR_LEN; byte++) { /* for each address byte */
+ /* process each address bit */
+ for (bit = *addr++, j = 0;
+ j < 8;
+ j++, bit >>= 1) {
+ crc = (crc << 1) ^ ((((crc < 0 ? 1 : 0) ^ bit) & 0x01) ?
+ CRC_POLYNOMIAL_BE : 0);
+ }
+ }
+ hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */
+ for (j = 0; j < 5; j++) { /* ... in reverse order. */
+ hashcode = (hashcode << 1) | ((crc>>=1) & 1);
+ }
+
+ ladrf[hashcode >> 3] |= 1 << (hashcode & 0x07);
+}
+#endif // USERLEVEL
+
+#ifdef VMCORE
+/*
+ *----------------------------------------------------------------------
+ *
+ * Net_GetNumAdapters --
+ *
+ * Returns the number of each type of network adapter configured in this
+ * VM.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static INLINE void
+Net_GetNumAdapters(Net_AdapterCount *counts)
+{
+ uint32 i;
+
+ counts->vlance = 0;
+ counts->vmxnet2 = 0;
+ counts->vmxnet3 = 0;
+ counts->e1000 = 0;
+ counts->e1000e = 0;
+
+ for (i = 0; i < MAX_ETHERNET_CARDS; i++) {
+ char* adapterStr;
+
+ if (!Config_GetBool(FALSE, "ethernet%d.present", i)) {
+ continue;
+ }
+ adapterStr = Config_GetString("vlance", "ethernet%d.virtualDev", i);
+ if (Str_Strcasecmp(adapterStr, "vmxnet3") == 0) {
+ counts->vmxnet3++;
+ } else if (Str_Strcasecmp(adapterStr, "vlance") == 0) {
+ counts->vlance++;
+ } else if (Str_Strcasecmp(adapterStr, "vmxnet") == 0) {
+ counts->vmxnet2++;
+ } else if (Str_Strcasecmp(adapterStr, "e1000") == 0) {
+ counts->e1000++;
+ } else if (Str_Strcasecmp(adapterStr, "e1000e") == 0) {
+ counts->e1000e++;
+ } else {
+ LOG_ONCE(("%s: unknown adapter: %s\n", __FUNCTION__, adapterStr));
+ }
+ free(adapterStr);
+ }
+}
+
+#endif // VMCORE
+
+#endif // VMWARE_DEVICES_NET_H
diff --git a/usr/src/uts/intel/io/vmxnet/net_sg.h b/usr/src/uts/intel/io/vmxnet/net_sg.h
new file mode 100644
index 0000000000..f6c30fb2b5
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/net_sg.h
@@ -0,0 +1,84 @@
+/*********************************************************
+ * Copyright (C) 2000 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * net_sg.h --
+ *
+ * Network packet scatter gather structure.
+ */
+
+
+#ifndef _NET_SG_H
+#define _NET_SG_H
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+
+#define NET_SG_DEFAULT_LENGTH 16
+
+/*
+ * A single scatter-gather element for a network packet.
+ * The address is split into low and high to save space.
+ * If we make it 64 bits then Windows pads things out such that
+ * we lose a lot of space for each scatter gather array.
+ * This adds up when you have embedded scatter-gather
+ * arrays for transmit and receive ring buffers.
+ */
+typedef struct NetSG_Elem {
+ uint32 addrLow;
+ uint16 addrHi;
+ uint16 length;
+} NetSG_Elem;
+
+typedef enum NetSG_AddrType {
+ NET_SG_MACH_ADDR,
+ NET_SG_PHYS_ADDR,
+ NET_SG_VIRT_ADDR,
+} NetSG_AddrType;
+
+typedef struct NetSG_Array {
+ uint16 addrType;
+ uint16 length;
+ NetSG_Elem sg[NET_SG_DEFAULT_LENGTH];
+} NetSG_Array;
+
+#define NET_SG_SIZE(len) (sizeof(NetSG_Array) + (len - NET_SG_DEFAULT_LENGTH) * sizeof(NetSG_Elem))
+
+#define NET_SG_MAKE_PA(elem) (PA)QWORD(elem.addrHi, elem.addrLow)
+#define NET_SG_MAKE_PTR(elem) (char *)(uintptr_t)QWORD(elem.addrHi, elem.addrLow)
+
+#endif
diff --git a/usr/src/uts/intel/io/vmxnet/vm_basic_types.h b/usr/src/uts/intel/io/vmxnet/vm_basic_types.h
new file mode 100644
index 0000000000..adeac1b708
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vm_basic_types.h
@@ -0,0 +1,1037 @@
+/*********************************************************
+ * Copyright (C) 1998-2009 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+/*********************************************************
+ * 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.
+ * 3. Neither the name of VMware Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission of VMware Inc.
+ *
+ * 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.
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ *
+ * vm_basic_types.h --
+ *
+ * basic data types.
+ */
+
+
+#ifndef _VM_BASIC_TYPES_H_
+#define _VM_BASIC_TYPES_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMMON
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_VMKDRIVERS
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_DISTRIBUTE
+#define INCLUDE_ALLOW_VMCORE
+#define INCLUDE_ALLOW_VMIROM
+#include "includeCheck.h"
+
+/* STRICT ANSI means the Xserver build and X defines Bool differently. */
+#if !defined(_XTYPEDEF_BOOL) && \
+ (!defined(__STRICT_ANSI__) || defined(__FreeBSD__) || defined(__MINGW32__))
+#define _XTYPEDEF_BOOL
+typedef char Bool;
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#define IsBool(x) (((x) & ~1) == 0)
+#define IsBool2(x, y) ((((x) | (y)) & ~1) == 0)
+
+/*
+ * Macros __i386__ and __ia64 are intrinsically defined by GCC
+ */
+#if defined _MSC_VER && defined _M_X64
+# define __x86_64__
+#elif defined _MSC_VER && defined _M_IX86
+# define __i386__
+#endif
+
+#ifdef __i386__
+#define VM_I386
+#endif
+
+#ifdef __x86_64__
+#define VM_X86_64
+#define VM_I386
+#define vm_x86_64 (1)
+#else
+#define vm_x86_64 (0)
+#endif
+
+
+#ifdef _MSC_VER
+
+#pragma warning (3 :4505) // unreferenced local function
+#pragma warning (disable :4018) // signed/unsigned mismatch
+#pragma warning (disable :4761) // integral size mismatch in argument; conversion supplied
+#pragma warning (disable :4305) // truncation from 'const int' to 'short'
+#pragma warning (disable :4244) // conversion from 'unsigned short' to 'unsigned char'
+#pragma warning (disable :4267) // truncation of 'size_t'
+#pragma warning (disable :4146) // unary minus operator applied to unsigned type, result still unsigned
+#pragma warning (disable :4142) // benign redefinition of type
+
+#endif
+
+#if defined(__APPLE__) || defined(HAVE_STDINT_H)
+
+/*
+ * TODO: This is a C99 standard header. We should be able to test for
+ * #if __STDC_VERSION__ >= 199901L, but that breaks the Netware build
+ * (which doesn't have stdint.h).
+ */
+
+#include <stdint.h>
+
+typedef uint64_t uint64;
+typedef int64_t int64;
+typedef uint32_t uint32;
+typedef int32_t int32;
+typedef uint16_t uint16;
+typedef int16_t int16;
+typedef uint8_t uint8;
+typedef int8_t int8;
+
+/*
+ * Note: C does not specify whether char is signed or unsigned, and
+ * both gcc and msvc implement processor-specific signedness. With
+ * three types:
+ * typeof(char) != typeof(signed char) != typeof(unsigned char)
+ *
+ * Be careful here, because gcc (4.0.1 and others) likes to warn about
+ * conversions between signed char * and char *.
+ */
+
+#else /* !HAVE_STDINT_H */
+
+#ifdef _MSC_VER
+
+typedef unsigned __int64 uint64;
+typedef signed __int64 int64;
+
+#elif defined(__GNUC__) || defined(__SUNPRO_C)
+/* The Xserver source compiles with -ansi -pendantic */
+# if !defined(__STRICT_ANSI__) || defined(__FreeBSD__)
+# if defined(VM_X86_64)
+typedef unsigned long uint64;
+typedef long int64;
+# else
+typedef unsigned long long uint64;
+typedef long long int64;
+# endif
+# endif
+#else
+# error - Need compiler define for int64/uint64
+#endif /* _MSC_VER */
+
+typedef unsigned int uint32;
+typedef unsigned short uint16;
+typedef unsigned char uint8;
+
+typedef int int32;
+typedef short int16;
+typedef signed char int8;
+
+#endif /* HAVE_STDINT_H */
+
+/*
+ * FreeBSD (for the tools build) unconditionally defines these in
+ * sys/inttypes.h so don't redefine them if this file has already
+ * been included. [greg]
+ *
+ * This applies to Solaris as well.
+ */
+
+/*
+ * Before trying to do the includes based on OS defines, see if we can use
+ * feature-based defines to get as much functionality as possible
+ */
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_INTTYPES_H
+#include <sys/inttypes.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/param.h> /* For __FreeBSD_version */
+#endif
+
+#if !defined(USING_AUTOCONF)
+# if defined(__FreeBSD__) || defined(sun)
+# ifdef KLD_MODULE
+# include <sys/types.h>
+# else
+# if __FreeBSD_version >= 500043
+# if !defined(VMKERNEL)
+# include <inttypes.h>
+# endif
+# include <sys/types.h>
+# else
+# include <sys/inttypes.h>
+# endif
+# endif
+# elif defined __APPLE__
+# if KERNEL
+# include <sys/unistd.h>
+# include <sys/types.h> /* mostly for size_t */
+# include <stdint.h>
+# else
+# include <unistd.h>
+# include <inttypes.h>
+# include <stdlib.h>
+# include <stdint.h>
+# endif
+# else
+# if !defined(__intptr_t_defined) && !defined(intptr_t)
+# ifdef VM_I386
+# define __intptr_t_defined
+# ifdef VM_X86_64
+typedef int64 intptr_t;
+# else
+typedef int32 intptr_t;
+# endif
+# elif defined(__arm__)
+typedef int32 intptr_t;
+# endif
+# endif
+
+# ifndef _STDINT_H
+# ifdef VM_I386
+# ifdef VM_X86_64
+typedef uint64 uintptr_t;
+# else
+typedef uint32 uintptr_t;
+# endif
+# elif defined(__arm__)
+typedef uint32 uintptr_t;
+# endif
+# endif
+# endif
+#endif
+
+
+/*
+ * Time
+ * XXX These should be cleaned up. -- edward
+ */
+
+typedef int64 VmTimeType; /* Time in microseconds */
+typedef int64 VmTimeRealClock; /* Real clock kept in microseconds */
+typedef int64 VmTimeVirtualClock; /* Virtual Clock kept in CPU cycles */
+
+/*
+ * Printf format specifiers for size_t and 64-bit number.
+ * Use them like this:
+ * printf("%"FMT64"d\n", big);
+ *
+ * FMTH is for handles/fds.
+ */
+
+#ifdef _MSC_VER
+ #define FMT64 "I64"
+ #ifdef VM_X86_64
+ #define FMTSZ "I64"
+ #define FMTPD "I64"
+ #define FMTH "I64"
+ #else
+ #define FMTSZ "I"
+ #define FMTPD "I"
+ #define FMTH "I"
+ #endif
+#elif defined __APPLE__
+ /* Mac OS hosts use the same formatters for 32- and 64-bit. */
+ #define FMT64 "ll"
+ #if KERNEL
+ #define FMTSZ "l"
+ #else
+ #define FMTSZ "z"
+ #endif
+ #define FMTPD "l"
+ #define FMTH ""
+#elif defined(__GNUC__) || defined(__SUNPRO_C)
+ #define FMTH ""
+ #if defined(N_PLAT_NLM) || defined(sun) || \
+ (defined(__FreeBSD__) && (__FreeBSD__ + 0) && ((__FreeBSD__ + 0) < 5))
+ /*
+ * Why (__FreeBSD__ + 0)? See bug 141008.
+ * Yes, we really need to test both (__FreeBSD__ + 0) and
+ * ((__FreeBSD__ + 0) < 5). No, we can't remove "+ 0" from
+ * ((__FreeBSD__ + 0) < 5).
+ */
+ #ifdef VM_X86_64
+ #define FMTSZ "l"
+ #define FMTPD "l"
+ #else
+ #define FMTSZ ""
+ #define FMTPD ""
+ #endif
+ #elif defined(__linux__) \
+ || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) \
+ || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) \
+ || (defined(_POSIX2_VERSION) && _POSIX2_VERSION >= 200112L)
+ /* BSD, Linux */
+ #define FMTSZ "z"
+
+ #if defined(VM_X86_64)
+ #define FMTPD "l"
+ #else
+ #define FMTPD ""
+ #endif
+ #else
+ /* Systems with a pre-C99 libc */
+ #define FMTSZ "Z"
+ #ifdef VM_X86_64
+ #define FMTPD "l"
+ #else
+ #define FMTPD ""
+ #endif
+ #endif
+ #ifdef VM_X86_64
+ #define FMT64 "l"
+ #elif defined(sun) || defined(__FreeBSD__)
+ #define FMT64 "ll"
+ #else
+ #define FMT64 "L"
+ #endif
+#else
+ #error - Need compiler define for FMT64 and FMTSZ
+#endif
+
+/*
+ * Suffix for 64-bit constants. Use it like this:
+ * CONST64(0x7fffffffffffffff) for signed or
+ * CONST64U(0x7fffffffffffffff) for unsigned.
+ *
+ * 2004.08.30(thutt):
+ * The vmcore/asm64/gen* programs are compiled as 32-bit
+ * applications, but must handle 64 bit constants. If the
+ * 64-bit-constant defining macros are already defined, the
+ * definition will not be overwritten.
+ */
+
+#if !defined(CONST64) || !defined(CONST64U)
+#ifdef _MSC_VER
+#define CONST64(c) c##I64
+#define CONST64U(c) c##uI64
+#elif defined __APPLE__
+#define CONST64(c) c##LL
+#define CONST64U(c) c##uLL
+#elif defined(__GNUC__) || defined(__SUNPRO_C)
+#ifdef VM_X86_64
+#define CONST64(c) c##L
+#define CONST64U(c) c##uL
+#else
+#define CONST64(c) c##LL
+#define CONST64U(c) c##uLL
+#endif
+#else
+#error - Need compiler define for CONST64
+#endif
+#endif
+
+/*
+ * Use CONST3264/CONST3264U if you want a constant to be
+ * treated as a 32-bit number on 32-bit compiles and
+ * a 64-bit number on 64-bit compiles. Useful in the case
+ * of shifts, like (CONST3264U(1) << x), where x could be
+ * more than 31 on a 64-bit compile.
+ */
+
+#ifdef VM_X86_64
+ #define CONST3264(a) CONST64(a)
+ #define CONST3264U(a) CONST64U(a)
+#else
+ #define CONST3264(a) (a)
+ #define CONST3264U(a) (a)
+#endif
+
+#define MIN_INT8 ((int8)0x80)
+#define MAX_INT8 ((int8)0x7f)
+
+#define MIN_UINT8 ((uint8)0)
+#define MAX_UINT8 ((uint8)0xff)
+
+#define MIN_INT16 ((int16)0x8000)
+#define MAX_INT16 ((int16)0x7fff)
+
+#define MIN_UINT16 ((uint16)0)
+#define MAX_UINT16 ((uint16)0xffff)
+
+#define MIN_INT32 ((int32)0x80000000)
+#define MAX_INT32 ((int32)0x7fffffff)
+
+#define MIN_UINT32 ((uint32)0)
+#define MAX_UINT32 ((uint32)0xffffffff)
+
+#define MIN_INT64 (CONST64(0x8000000000000000))
+#define MAX_INT64 (CONST64(0x7fffffffffffffff))
+
+#define MIN_UINT64 (CONST64U(0))
+#define MAX_UINT64 (CONST64U(0xffffffffffffffff))
+
+typedef uint8 *TCA; /* Pointer into TC (usually). */
+
+/*
+ * Type big enough to hold an integer between 0..100
+ */
+typedef uint8 Percent;
+#define AsPercent(v) ((Percent)(v))
+#define CHOOSE_PERCENT AsPercent(101)
+
+
+typedef uintptr_t VA;
+typedef uintptr_t VPN;
+
+typedef uint64 PA;
+typedef uint32 PPN;
+
+typedef uint64 PhysMemOff;
+typedef uint64 PhysMemSize;
+
+/* The Xserver source compiles with -ansi -pendantic */
+#ifndef __STRICT_ANSI__
+typedef uint64 BA;
+#endif
+typedef uint32 BPN;
+typedef uint32 PageNum;
+typedef unsigned MemHandle;
+typedef int32 World_ID;
+
+/* !! do not alter the definition of INVALID_WORLD_ID without ensuring
+ * that the values defined in both bora/public/vm_basic_types.h and
+ * lib/vprobe/vm_basic_types.h are the same. Additionally, the definition
+ * of VMK_INVALID_WORLD_ID in vmkapi_world.h also must be defined with
+ * the same value
+ */
+
+#define INVALID_WORLD_ID ((World_ID)0)
+
+typedef World_ID User_CartelID;
+#define INVALID_CARTEL_ID INVALID_WORLD_ID
+
+typedef User_CartelID User_SessionID;
+#define INVALID_SESSION_ID INVALID_CARTEL_ID
+
+typedef User_CartelID User_CartelGroupID;
+#define INVALID_CARTELGROUP_ID INVALID_CARTEL_ID
+
+typedef uint32 Worldlet_ID;
+#define INVALID_WORLDLET_ID ((Worldlet_ID)-1)
+
+/* The Xserver source compiles with -ansi -pendantic */
+#ifndef __STRICT_ANSI__
+typedef uint64 MA;
+typedef uint32 MPN;
+#endif
+
+/*
+ * This type should be used for variables that contain sector
+ * position/quantity.
+ */
+typedef uint64 SectorType;
+
+/*
+ * Linear address
+ */
+
+typedef uintptr_t LA;
+typedef uintptr_t LPN;
+#define LA_2_LPN(_la) ((_la) >> PAGE_SHIFT)
+#define LPN_2_LA(_lpn) ((_lpn) << PAGE_SHIFT)
+
+#define LAST_LPN ((((LA) 1) << (8 * sizeof(LA) - PAGE_SHIFT)) - 1)
+#define LAST_LPN32 ((((LA32)1) << (8 * sizeof(LA32) - PAGE_SHIFT)) - 1)
+#define LAST_LPN64 ((((LA64)1) << (8 * sizeof(LA64) - PAGE_SHIFT)) - 1)
+
+/* Valid bits in a LPN. */
+#define LPN_MASK LAST_LPN
+#define LPN_MASK32 LAST_LPN32
+#define LPN_MASK64 LAST_LPN64
+
+/*
+ * On 64 bit platform, address and page number types default
+ * to 64 bit. When we need to represent a 32 bit address, we use
+ * types defined below.
+ *
+ * On 32 bit platform, the following types are the same as the
+ * default types.
+ */
+typedef uint32 VA32;
+typedef uint32 VPN32;
+typedef uint32 LA32;
+typedef uint32 LPN32;
+typedef uint32 PA32;
+typedef uint32 PPN32;
+typedef uint32 MA32;
+typedef uint32 MPN32;
+
+/*
+ * On 64 bit platform, the following types are the same as the
+ * default types.
+ */
+typedef uint64 VA64;
+typedef uint64 VPN64;
+typedef uint64 LA64;
+typedef uint64 LPN64;
+typedef uint64 PA64;
+typedef uint64 PPN64;
+typedef uint64 MA64;
+typedef uint64 MPN64;
+
+/*
+ * VA typedefs for user world apps.
+ */
+typedef VA32 UserVA32;
+typedef VA64 UserVA64;
+typedef UserVA64 UserVAConst; /* Userspace ptr to data that we may only read. */
+typedef UserVA32 UserVA32Const; /* Userspace ptr to data that we may only read. */
+typedef UserVA64 UserVA64Const; /* Used by 64-bit syscalls until conversion is finished. */
+#ifdef VMKERNEL
+typedef UserVA64 UserVA;
+#else
+typedef void * UserVA;
+#endif
+
+
+/*
+ * Maximal possible PPN value (errors too) that PhysMem can handle.
+ * Must be at least as large as MAX_PPN which is the maximum PPN
+ * for any region other than buserror.
+ */
+#define PHYSMEM_MAX_PPN ((PPN)0xffffffff)
+#define MAX_PPN ((PPN)0x1fffffff) /* Maximal observable PPN value. */
+#define INVALID_PPN ((PPN)0xffffffff)
+
+#define INVALID_BPN ((BPN)0x1fffffff)
+
+#define RESERVED_MPN ((MPN) 0)
+#define INVALID_MPN ((MPN)-1)
+#define MEMREF_MPN ((MPN)-2)
+#define RELEASED_MPN ((MPN)-3)
+#define MAX_MPN ((MPN)0x7fffffff) /* 43 bits of address space. */
+
+#define INVALID_LPN ((LPN)-1)
+#define INVALID_VPN ((VPN)-1)
+#define INVALID_LPN64 ((LPN64)-1)
+#define INVALID_PAGENUM ((PageNum)-1)
+
+
+/*
+ * Format modifier for printing VA, LA, and VPN.
+ * Use them like this: Log("%#"FMTLA"x\n", laddr)
+ */
+
+#if defined(VMM) || defined(FROBOS64) || vm_x86_64 || defined __APPLE__
+# define FMTLA "l"
+# define FMTVA "l"
+# define FMTVPN "l"
+#else
+# define FMTLA ""
+# define FMTVA ""
+# define FMTVPN ""
+#endif
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+#define CONST const
+
+
+#ifndef INLINE
+# ifdef _MSC_VER
+# define INLINE __inline
+# else
+# define INLINE inline
+# endif
+#endif
+
+
+/*
+ * Annotation for data that may be exported into a DLL and used by other
+ * apps that load that DLL and import the data.
+ */
+#if defined(_WIN32) && defined(VMX86_IMPORT_DLLDATA)
+# define VMX86_EXTERN_DATA extern __declspec(dllimport)
+#else // !_WIN32
+# define VMX86_EXTERN_DATA extern
+#endif
+
+#if defined(_WIN32) && !defined(VMX86_NO_THREADS)
+#define THREADSPECIFIC __declspec(thread)
+#else
+#define THREADSPECIFIC
+#endif
+
+/*
+ * Due to the wonderful "registry redirection" feature introduced in
+ * 64-bit Windows, if you access any key under HKLM\Software in 64-bit
+ * code, you need to open/create/delete that key with
+ * VMKEY_WOW64_32KEY if you want a consistent view with 32-bit code.
+ */
+
+#ifdef _WIN32
+#ifdef _WIN64
+#define VMW_KEY_WOW64_32KEY KEY_WOW64_32KEY
+#else
+#define VMW_KEY_WOW64_32KEY 0x0
+#endif
+#endif
+
+
+/*
+ * Consider the following reasons functions are inlined:
+ *
+ * 1) inlined for performance reasons
+ * 2) inlined because it's a single-use function
+ *
+ * Functions which meet only condition 2 should be marked with this
+ * inline macro; It is not critical to be inlined (but there is a
+ * code-space & runtime savings by doing so), so when other callers
+ * are added the inline-ness should be removed.
+ */
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+/*
+ * Starting at version 3.3, gcc does not always inline functions marked
+ * 'inline' (it depends on their size). To force gcc to do so, one must use the
+ * extra __always_inline__ attribute.
+ */
+# define INLINE_SINGLE_CALLER INLINE __attribute__((__always_inline__))
+#else
+# define INLINE_SINGLE_CALLER INLINE
+#endif
+
+/*
+ * Used when a hard guaranteed of no inlining is needed. Very few
+ * instances need this since the absence of INLINE is a good hint
+ * that gcc will not do inlining.
+ */
+
+#if defined(__GNUC__) && defined(VMM)
+#define ABSOLUTELY_NOINLINE __attribute__((__noinline__))
+#endif
+
+/*
+ * Attributes placed on function declarations to tell the compiler
+ * that the function never returns.
+ */
+
+#ifdef _MSC_VER
+#define NORETURN __declspec(noreturn)
+#elif __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 9)
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN
+#endif
+
+/*
+ * GCC 3.2 inline asm needs the + constraint for input/ouput memory operands.
+ * Older GCCs don't know about it --hpreg
+ */
+
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+# define VM_ASM_PLUS 1
+#else
+# define VM_ASM_PLUS 0
+#endif
+
+/*
+ * Branch prediction hints:
+ * LIKELY(exp) - Expression exp is likely TRUE.
+ * UNLIKELY(exp) - Expression exp is likely FALSE.
+ * Usage example:
+ * if (LIKELY(excCode == EXC_NONE)) {
+ * or
+ * if (UNLIKELY(REAL_MODE(vc))) {
+ *
+ * We know how to predict branches on gcc3 and later (hopefully),
+ * all others we don't so we do nothing.
+ */
+
+#if (__GNUC__ >= 3)
+/*
+ * gcc3 uses __builtin_expect() to inform the compiler of an expected value.
+ * We use this to inform the static branch predictor. The '!!' in LIKELY
+ * will convert any !=0 to a 1.
+ */
+#define LIKELY(_exp) __builtin_expect(!!(_exp), 1)
+#define UNLIKELY(_exp) __builtin_expect((_exp), 0)
+#else
+#define LIKELY(_exp) (_exp)
+#define UNLIKELY(_exp) (_exp)
+#endif
+
+/*
+ * GCC's argument checking for printf-like functions
+ * This is conditional until we have replaced all `"%x", void *'
+ * with `"0x%08x", (uint32) void *'. Note that %p prints different things
+ * on different platforms. Argument checking is enabled for the
+ * vmkernel, which has already been cleansed.
+ *
+ * fmtPos is the position of the format string argument, beginning at 1
+ * varPos is the position of the variable argument, beginning at 1
+ */
+
+#if defined(__GNUC__)
+# define PRINTF_DECL(fmtPos, varPos) __attribute__((__format__(__printf__, fmtPos, varPos)))
+#else
+# define PRINTF_DECL(fmtPos, varPos)
+#endif
+
+#if defined(__GNUC__)
+# define SCANF_DECL(fmtPos, varPos) __attribute__((__format__(__scanf__, fmtPos, varPos)))
+#else
+# define SCANF_DECL(fmtPos, varPos)
+#endif
+
+/*
+ * UNUSED_PARAM should surround the parameter name and type declaration,
+ * e.g. "int MyFunction(int var1, UNUSED_PARAM(int var2))"
+ *
+ */
+
+#ifndef UNUSED_PARAM
+# if defined(__GNUC__)
+# define UNUSED_PARAM(_parm) _parm __attribute__((__unused__))
+# else
+# define UNUSED_PARAM(_parm) _parm
+# endif
+#endif
+
+/*
+ * REGPARM defaults to REGPARM3; i.e., a request that gcc
+ * put the first three arguments in registers. (It is fine
+ * if the function has fewer than three arguments.) Gcc only.
+ * Syntactically, put REGPARM where you'd put INLINE or NORETURN.
+ *
+ * Note that 64-bit code already puts the first six arguments in
+ * registers, so these attributes are only useful for 32-bit code.
+ */
+
+#if defined(__GNUC__)
+# define REGPARM0 __attribute__((regparm(0)))
+# define REGPARM1 __attribute__((regparm(1)))
+# define REGPARM2 __attribute__((regparm(2)))
+# define REGPARM3 __attribute__((regparm(3)))
+# define REGPARM REGPARM3
+#else
+# define REGPARM0
+# define REGPARM1
+# define REGPARM2
+# define REGPARM3
+# define REGPARM
+#endif
+
+/*
+ * ALIGNED specifies minimum alignment in "n" bytes.
+ */
+
+#ifdef __GNUC__
+#define ALIGNED(n) __attribute__((__aligned__(n)))
+#else
+#define ALIGNED(n)
+#endif
+
+/*
+ * __func__ is a stringified function name that is part of the C99 standard. The block
+ * below defines __func__ on older systems where the compiler does not support that
+ * macro.
+ */
+#if defined(__GNUC__) \
+ && ((__GNUC__ == 2 && __GNUC_MINOR < 96) \
+ || (__GNUC__ < 2))
+# define __func__ __FUNCTION__
+#endif
+
+/*
+ * Once upon a time, this was used to silence compiler warnings that
+ * get generated when the compiler thinks that a function returns
+ * when it is marked noreturn. Don't do it. Use NOT_REACHED().
+ */
+
+#define INFINITE_LOOP() do { } while (1)
+
+/*
+ * On FreeBSD (for the tools build), size_t is typedef'd if _BSD_SIZE_T_
+ * is defined. Use the same logic here so we don't define it twice. [greg]
+ */
+#ifdef __FreeBSD__
+# ifdef _BSD_SIZE_T_
+# undef _BSD_SIZE_T_
+# ifdef VM_I386
+# ifdef VM_X86_64
+ typedef uint64 size_t;
+# else
+ typedef uint32 size_t;
+# endif
+# endif /* VM_I386 */
+# endif
+
+# ifdef _BSD_SSIZE_T_
+# undef _BSD_SSIZE_T_
+# ifdef VM_I386
+# ifdef VM_X86_64
+ typedef int64 ssize_t;
+# else
+ typedef int32 ssize_t;
+# endif
+# endif /* VM_I386 */
+# endif
+
+#else
+# ifndef _SIZE_T
+# ifdef VM_I386
+# define _SIZE_T
+# ifdef VM_X86_64
+ typedef uint64 size_t;
+# else
+ typedef uint32 size_t;
+# endif
+# elif defined(__arm__)
+# define _SIZE_T
+ typedef uint32 size_t;
+# endif
+# endif
+
+# if !defined(FROBOS) && !defined(_SSIZE_T) && !defined(_SSIZE_T_) && \
+ !defined(ssize_t) && !defined(__ssize_t_defined) && \
+ !defined(_SSIZE_T_DECLARED)
+# ifdef VM_I386
+# define _SSIZE_T
+# define __ssize_t_defined
+# define _SSIZE_T_DECLARED
+# ifdef VM_X86_64
+ typedef int64 ssize_t;
+# else
+ typedef int32 ssize_t;
+# endif
+# elif defined(__arm__)
+# define _SSIZE_T
+# define __ssize_t_defined
+# define _SSIZE_T_DECLARED
+ typedef int32 ssize_t;
+# endif
+# endif
+
+#endif
+
+/*
+ * Format modifier for printing pid_t. On sun the pid_t is a ulong, but on
+ * Linux it's an int.
+ * Use this like this: printf("The pid is %"FMTPID".\n", pid);
+ */
+#ifdef sun
+# ifdef VM_X86_64
+# define FMTPID "d"
+# else
+# define FMTPID "lu"
+# endif
+#else
+# define FMTPID "d"
+#endif
+
+/*
+ * Format modifier for printing uid_t. On Solaris 10 and earlier, uid_t
+ * is a ulong, but on other platforms it's an unsigned int.
+ * Use this like this: printf("The uid is %"FMTUID".\n", uid);
+ */
+#if defined(sun) && !defined(SOL11)
+# ifdef VM_X86_64
+# define FMTUID "u"
+# else
+# define FMTUID "lu"
+# endif
+#else
+# define FMTUID "u"
+#endif
+
+/*
+ * Format modifier for printing mode_t. On sun the mode_t is a ulong, but on
+ * Linux it's an int.
+ * Use this like this: printf("The mode is %"FMTMODE".\n", mode);
+ */
+#ifdef sun
+# ifdef VM_X86_64
+# define FMTMODE "o"
+# else
+# define FMTMODE "lo"
+# endif
+#else
+# define FMTMODE "o"
+#endif
+
+/*
+ * Format modifier for printing time_t. Most platforms define a time_t to be
+ * a long int, but on FreeBSD (as of 5.0, it seems), the time_t is a signed
+ * size quantity. Refer to the definition of FMTSZ to see why we need silly
+ * preprocessor arithmetic.
+ * Use this like this: printf("The mode is %"FMTTIME".\n", time);
+ */
+#if defined(__FreeBSD__) && (__FreeBSD__ + 0) && ((__FreeBSD__ + 0) >= 5)
+# define FMTTIME FMTSZ"d"
+#else
+# if defined(_MSC_VER)
+# ifndef _SAFETIME_H_
+# if (_MSC_VER < 1400) || defined(_USE_32BIT_TIME_T)
+# define FMTTIME "ld"
+# else
+# define FMTTIME FMT64"d"
+# endif
+# else
+# ifndef FMTTIME
+# error "safetime.h did not define FMTTIME"
+# endif
+# endif
+# else
+# define FMTTIME "ld"
+# endif
+#endif
+
+#ifdef __APPLE__
+/*
+ * Format specifier for all these annoying types such as {S,U}Int32
+ * which are 'long' in 32-bit builds
+ * and 'int' in 64-bit builds.
+ */
+# ifdef __LP64__
+# define FMTLI ""
+# else
+# define FMTLI "l"
+# endif
+
+/*
+ * Format specifier for all these annoying types such as NS[U]Integer
+ * which are 'int' in 32-bit builds
+ * and 'long' in 64-bit builds.
+ */
+# ifdef __LP64__
+# define FMTIL "l"
+# else
+# define FMTIL ""
+# endif
+#endif
+
+
+/*
+ * Define MXSemaHandle here so both vmmon and vmx see this definition.
+ */
+
+#ifdef _WIN32
+typedef uintptr_t MXSemaHandle;
+#else
+typedef int MXSemaHandle;
+#endif
+
+/*
+ * Define type for poll device handles.
+ */
+
+typedef int64 PollDevHandle;
+
+/*
+ * Define the utf16_t type.
+ */
+
+#if defined(_WIN32) && defined(_NATIVE_WCHAR_T_DEFINED)
+typedef wchar_t utf16_t;
+#else
+typedef uint16 utf16_t;
+#endif
+
+/*
+ * Define for point and rectangle types. Defined here so they
+ * can be used by other externally facing headers in bora/public.
+ */
+
+typedef struct VMPoint {
+ int x, y;
+} VMPoint;
+
+#if defined _WIN32 && defined USERLEVEL
+struct tagRECT;
+typedef struct tagRECT VMRect;
+#else
+typedef struct VMRect {
+ int left;
+ int top;
+ int right;
+ int bottom;
+} VMRect;
+#endif
+
+/*
+ * ranked locks "everywhere"
+ */
+
+typedef uint32 MX_Rank;
+
+#endif /* _VM_BASIC_TYPES_H_ */
diff --git a/usr/src/uts/intel/io/vmxnet/vm_device_version.h b/usr/src/uts/intel/io/vmxnet/vm_device_version.h
new file mode 100644
index 0000000000..7046594a6c
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vm_device_version.h
@@ -0,0 +1,246 @@
+/*********************************************************
+ * Copyright (C) 1998 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *********************************************************/
+
+#ifndef VM_DEVICE_VERSION_H
+#define VM_DEVICE_VERSION_H
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_VMCORE
+#include "includeCheck.h"
+
+#ifdef _WIN32
+#ifdef __MINGW32__
+#include "initguid.h"
+#else
+#include "guiddef.h"
+#endif
+#endif
+
+/* LSILogic 53C1030 Parallel SCSI controller
+ * LSILogic SAS1068 SAS controller
+ */
+#define PCI_VENDOR_ID_LSILOGIC 0x1000
+#define PCI_DEVICE_ID_LSI53C1030 0x0030
+#define PCI_DEVICE_ID_LSISAS1068 0x0054
+
+/* Our own PCI IDs
+ * VMware SVGA II (Unified VGA)
+ * VMware SVGA (PCI Accelerator)
+ * VMware vmxnet (Idealized NIC)
+ * VMware vmxscsi (Abortive idealized SCSI controller)
+ * VMware chipset (Subsystem ID for our motherboards)
+ * VMware e1000 (Subsystem ID)
+ * VMware vmxnet3 (Uniform Pass Through NIC)
+ * VMware HD Audio codec
+ * VMware HD Audio controller
+ */
+#define PCI_VENDOR_ID_VMWARE 0x15AD
+#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405
+#define PCI_DEVICE_ID_VMWARE_SVGA 0x0710
+#define PCI_DEVICE_ID_VMWARE_NET 0x0720
+#define PCI_DEVICE_ID_VMWARE_SCSI 0x0730
+#define PCI_DEVICE_ID_VMWARE_VMCI 0x0740
+#define PCI_DEVICE_ID_VMWARE_CHIPSET 0x1976
+#define PCI_DEVICE_ID_VMWARE_82545EM 0x0750 /* single port */
+#define PCI_DEVICE_ID_VMWARE_82546EB 0x0760 /* dual port */
+#define PCI_DEVICE_ID_VMWARE_EHCI 0x0770
+#define PCI_DEVICE_ID_VMWARE_UHCI 0x0774
+#define PCI_DEVICE_ID_VMWARE_XHCI 0x0778
+#define PCI_DEVICE_ID_VMWARE_1394 0x0780
+#define PCI_DEVICE_ID_VMWARE_BRIDGE 0x0790
+#define PCI_DEVICE_ID_VMWARE_ROOTPORT 0x07A0
+#define PCI_DEVICE_ID_VMWARE_VMXNET3 0x07B0
+#define PCI_DEVICE_ID_VMWARE_VMXWIFI 0x07B8
+#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0
+#define PCI_DEVICE_ID_VMWARE_82574 0x07D0
+#define PCI_DEVICE_ID_VMWARE_HDAUDIO_CODEC 0x1975
+#define PCI_DEVICE_ID_VMWARE_HDAUDIO_CONTROLLER 0x1977
+
+/* The hypervisor device might grow. Please leave room
+ * for 7 more subfunctions.
+ */
+#define PCI_DEVICE_ID_VMWARE_HYPER 0x0800
+#define PCI_DEVICE_ID_VMWARE_VMI 0x0801
+
+#define PCI_DEVICE_VMI_CLASS 0x05
+#define PCI_DEVICE_VMI_SUBCLASS 0x80
+#define PCI_DEVICE_VMI_INTERFACE 0x00
+#define PCI_DEVICE_VMI_REVISION 0x01
+
+/* From linux/pci_ids.h:
+ * AMD Lance Ethernet controller
+ * BusLogic SCSI controller
+ * Ensoniq ES1371 sound controller
+ */
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_DEVICE_ID_AMD_VLANCE 0x2000
+#define PCI_VENDOR_ID_BUSLOGIC 0x104B
+#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC 0x0140
+#define PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER 0x1040
+#define PCI_VENDOR_ID_ENSONIQ 0x1274
+#define PCI_DEVICE_ID_ENSONIQ_ES1371 0x1371
+
+/* From linux/pci_ids.h:
+ * Intel 82439TX (430 HX North Bridge)
+ * Intel 82371AB (PIIX4 South Bridge)
+ * Intel 82443BX (440 BX North Bridge and AGP Bridge)
+ * Intel 82545EM (e1000, server adapter, single port)
+ * Intel 82546EB (e1000, server adapter, dual port)
+ * Intel HECI (as embedded in ich9m)
+ */
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_DEVICE_ID_INTEL_82439TX 0x7100
+#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110
+#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112
+#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113
+#define PCI_DEVICE_ID_INTEL_82371AB 0x7111
+#define PCI_DEVICE_ID_INTEL_82443BX 0x7190
+#define PCI_DEVICE_ID_INTEL_82443BX_1 0x7191
+#define PCI_DEVICE_ID_INTEL_82443BX_2 0x7192 /* Used when no AGP support */
+#define PCI_DEVICE_ID_INTEL_82545EM 0x100f
+#define PCI_DEVICE_ID_INTEL_82546EB 0x1010
+#define PCI_DEVICE_ID_INTEL_82574 0x10d3
+#define PCI_DEVICE_ID_INTEL_82574_APPLE 0x10f6
+#define PCI_DEVICE_ID_INTEL_HECI 0x2a74
+
+#define E1000E_PCI_DEVICE_ID_CONFIG_STR "e1000e.pci.deviceID"
+#define E1000E_PCI_SUB_VENDOR_ID_CONFIG_STR "e1000e.pci.subVendorID"
+#define E1000E_PCI_SUB_DEVICE_ID_CONFIG_STR "e1000e.pci.subDeviceID"
+
+/*
+ * Intel HD Audio controller and Realtek ALC885 codec.
+ */
+#define PCI_DEVICE_ID_INTEL_631XESB_632XESB 0x269a
+#define PCI_VENDOR_ID_REALTEK 0x10ec
+#define PCI_DEVICE_ID_REALTEK_ALC885 0x0885
+
+
+/*
+ * Fresco Logic xHCI (USB 3.0) Controller
+ */
+#define PCI_VENDOR_ID_FRESCO 0x1B73
+#define PCI_DEVICE_ID_FRESCO_FL1000 0x1000 // Original 1-port chip
+#define PCI_DEVICE_ID_FRESCO_FL1009 0x1009 // New 2-port chip (Driver 3.0.98+)
+#define PCI_DEVICE_ID_FRESCO_FL1400 0x1400 // Unknown (4-port? Dev hardware?)
+
+/*
+ * NEC/Renesas xHCI (USB 3.0) Controller
+ */
+#define PCI_VENDOR_ID_NEC 0x1033
+#define PCI_DEVICE_ID_NEC_UPD720200 0x0194
+#define PCI_REVISION_NEC_UPD720200 0x03
+#define PCI_FIRMWARE_NEC_UPD720200 0x3015
+
+
+/************* Strings for IDE Identity Fields **************************/
+#define VIDE_ID_SERIAL_STR "00000000000000000001" /* Must be 20 Bytes */
+#define VIDE_ID_FIRMWARE_STR "00000001" /* Must be 8 Bytes */
+
+/* No longer than 40 Bytes */
+#define VIDE_ATA_MODEL_STR PRODUCT_GENERIC_NAME " Virtual IDE Hard Drive"
+#define VIDE_ATAPI_MODEL_STR PRODUCT_GENERIC_NAME " Virtual IDE CDROM Drive"
+
+#define ATAPI_VENDOR_ID "NECVMWar" /* Must be 8 Bytes */
+#define ATAPI_PRODUCT_ID PRODUCT_GENERIC_NAME " IDE CDROM" /* Must be 16 Bytes */
+#define ATAPI_REV_LEVEL "1.00" /* Must be 4 Bytes */
+
+#define IDE_NUM_INTERFACES 2 /* support for two interfaces */
+#define IDE_DRIVES_PER_IF 2
+
+/************* Strings for SCSI Identity Fields **************************/
+#define SCSI_DISK_MODEL_STR PRODUCT_GENERIC_NAME " Virtual SCSI Hard Drive"
+#define SCSI_DISK_VENDOR_NAME COMPANY_NAME
+#define SCSI_DISK_REV_LEVEL "1.0"
+#define SCSI_CDROM_MODEL_STR PRODUCT_GENERIC_NAME " Virtual SCSI CDROM Drive"
+#define SCSI_CDROM_VENDOR_NAME COMPANY_NAME
+#define SCSI_CDROM_REV_LEVEL "1.0"
+
+/************* SCSI implementation limits ********************************/
+#define SCSI_MAX_CONTROLLERS 4 // Need more than 1 for MSCS clustering
+#define SCSI_MAX_DEVICES 16 // BT-958 emulates only 16
+#define PVSCSI_MAX_DEVICES 255 // 255 (including the controller)
+/*
+ * VSCSI_BV_INTS is the number of uint32's needed for a bit vector
+ * to cover all scsi devices per target.
+ */
+#define VSCSI_BV_INTS CEILING(PVSCSI_MAX_DEVICES, 8 * sizeof (uint32))
+#define SCSI_IDE_CHANNEL SCSI_MAX_CONTROLLERS
+#define SCSI_IDE_HOSTED_CHANNEL (SCSI_MAX_CONTROLLERS + 1)
+#define SCSI_MAX_CHANNELS (SCSI_MAX_CONTROLLERS + 2)
+
+/************* Strings for the VESA BIOS Identity Fields *****************/
+#define VBE_OEM_STRING COMPANY_NAME " SVGA"
+#define VBE_VENDOR_NAME COMPANY_NAME
+#define VBE_PRODUCT_NAME PRODUCT_GENERIC_NAME
+
+/************* PCI implementation limits ********************************/
+#define PCI_MAX_BRIDGES 15
+
+/************* Ethernet implementation limits ***************************/
+#define MAX_ETHERNET_CARDS 10
+
+/********************** Floppy limits ***********************************/
+#define MAX_FLOPPY_DRIVES 2
+
+/************* PCI Passthrough implementation limits ********************/
+#define MAX_PCI_PASSTHRU_DEVICES 6
+
+/************* USB implementation limits ********************************/
+#define MAX_USB_DEVICES_PER_HOST_CONTROLLER 127
+
+/************* Strings for Host USB Driver *******************************/
+
+#ifdef _WIN32
+
+/*
+ * Globally unique ID for the VMware device interface. Define INITGUID before including
+ * this header file to instantiate the variable.
+ */
+DEFINE_GUID(GUID_DEVICE_INTERFACE_VMWARE_USB_DEVICES,
+0x2da1fe75, 0xaab3, 0x4d2c, 0xac, 0xdf, 0x39, 0x8, 0x8c, 0xad, 0xa6, 0x65);
+
+/*
+ * Globally unique ID for the VMware device setup class.
+ */
+DEFINE_GUID(GUID_CLASS_VMWARE_USB_DEVICES,
+0x3b3e62a5, 0x3556, 0x4d7e, 0xad, 0xad, 0xf5, 0xfa, 0x3a, 0x71, 0x2b, 0x56);
+
+/*
+ * This string defines the device ID string of a VMware USB device.
+ * The format is USB\Vid_XXXX&Pid_YYYY, where XXXX and YYYY are the
+ * hexadecimal representations of the vendor and product ids, respectively.
+ *
+ * The official vendor ID for VMware, Inc. is 0x0E0F.
+ * The product id for USB generic devices is 0x0001.
+ */
+#define USB_VMWARE_DEVICE_ID_WIDE L"USB\\Vid_0E0F&Pid_0001"
+#define USB_DEVICE_ID_LENGTH (sizeof(USB_VMWARE_DEVICE_ID_WIDE) / sizeof(WCHAR))
+
+#ifdef UNICODE
+#define USB_PNP_SETUP_CLASS_NAME L"VMwareUSBDevices"
+#define USB_PNP_DRIVER_NAME L"vmusb"
+#else
+#define USB_PNP_SETUP_CLASS_NAME "VMwareUSBDevices"
+#define USB_PNP_DRIVER_NAME "vmusb"
+#endif
+#endif
+
+#endif /* VM_DEVICE_VERSION_H */
diff --git a/usr/src/uts/intel/io/vmxnet/vmnet_def.h b/usr/src/uts/intel/io/vmxnet/vmnet_def.h
new file mode 100644
index 0000000000..6e44aea2bb
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vmnet_def.h
@@ -0,0 +1,91 @@
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * vmnet_def.h
+ *
+ * - definitions which are (mostly) not vmxnet or vlance specific
+ */
+
+#ifndef _VMNET_DEF_H_
+#define _VMNET_DEF_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+#define INCLUDE_ALLOW_VMCORE
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+
+#define VMNET_NAME_BUFFER_LEN 128 /* Increased for i18n. */
+#define VMNET_COAL_SCHEME_NAME_LEN 16
+
+
+/*
+ * capabilities - not all of these are implemented in the virtual HW
+ * (eg VLAN support is in the virtual switch) so even vlance
+ * can use them
+ */
+#define VMNET_CAP_SG 0x0001 /* Can do scatter-gather transmits. */
+#define VMNET_CAP_IP4_CSUM 0x0002 /* Can checksum only TCP/UDP over IPv4. */
+#define VMNET_CAP_HW_CSUM 0x0004 /* Can checksum all packets. */
+#define VMNET_CAP_HIGH_DMA 0x0008 /* Can DMA to high memory. */
+#define VMNET_CAP_TOE 0x0010 /* Supports TCP/IP offload. */
+#define VMNET_CAP_TSO 0x0020 /* Supports TCP Segmentation offload */
+#define VMNET_CAP_SW_TSO 0x0040 /* Supports SW TCP Segmentation */
+#define VMNET_CAP_VMXNET_APROM 0x0080 /* Vmxnet APROM support */
+#define VMNET_CAP_HW_TX_VLAN 0x0100 /* Can we do VLAN tagging in HW */
+#define VMNET_CAP_HW_RX_VLAN 0x0200 /* Can we do VLAN untagging in HW */
+#define VMNET_CAP_SW_VLAN 0x0400 /* Can we do VLAN tagging/untagging in SW */
+#define VMNET_CAP_WAKE_PCKT_RCV 0x0800 /* Can wake on network packet recv? */
+#define VMNET_CAP_ENABLE_INT_INLINE 0x1000 /* Enable Interrupt Inline */
+#define VMNET_CAP_ENABLE_HEADER_COPY 0x2000 /* copy header for vmkernel */
+#define VMNET_CAP_TX_CHAIN 0x4000 /* Guest can use multiple tx entries for a pkt */
+#define VMNET_CAP_RX_CHAIN 0x8000 /* a pkt can span multiple rx entries */
+#define VMNET_CAP_LPD 0x10000 /* large pkt delivery */
+#define VMNET_CAP_BPF 0x20000 /* BPF Support in VMXNET Virtual Hardware */
+#define VMNET_CAP_SG_SPAN_PAGES 0x40000 /* Can do scatter-gather span multiple pages transmits. */
+#define VMNET_CAP_IP6_CSUM 0x80000 /* Can do IPv6 csum offload. */
+#define VMNET_CAP_TSO6 0x100000 /* Can do TSO segmentation offload for IPv6 pkts. */
+#define VMNET_CAP_TSO256k 0x200000 /* Can do TSO segmentation offload for pkts up to 256kB. */
+#define VMNET_CAP_UPT 0x400000 /* Support UPT */
+#define VMNET_CAP_RDONLY_INETHDRS 0x800000 /* Modifies inet headers for TSO/CSUm */
+#define VMNET_CAP_NPA 0x1000000 /* Support NPA */
+#define VMNET_CAP_DCB 0x2000000 /* Support DCB */
+#define VMNET_CAP_OFFLOAD_8OFFSET 0x4000000 /* supports 8bit parameterized offsets */
+#define VMNET_CAP_OFFLOAD_16OFFSET 0x8000000 /* supports 16bit parameterized offsets */
+#define VMNET_CAP_IP6_CSUM_EXT_HDRS 0x10000000 /* support csum of ip6 ext hdrs */
+#define VMNET_CAP_TSO6_EXT_HDRS 0x20000000 /* support TSO for ip6 ext hdrs */
+#define VMNET_CAP_SCHED 0x40000000 /* compliant with network scheduling */
+#endif // _VMNET_DEF_H_
diff --git a/usr/src/uts/intel/io/vmxnet/vmxnet.c b/usr/src/uts/intel/io/vmxnet/vmxnet.c
new file mode 100644
index 0000000000..14a87fa22e
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vmxnet.c
@@ -0,0 +1,2442 @@
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+/*
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/debug.h>
+#include <sys/stropts.h>
+#include <sys/stream.h>
+#include <sys/strlog.h>
+#include <sys/kmem.h>
+#include <sys/stat.h>
+#include <sys/kstat.h>
+#include <sys/vtrace.h>
+#include <sys/dlpi.h>
+#include <sys/strsun.h>
+#include <sys/ethernet.h>
+#include <sys/modctl.h>
+#include <sys/errno.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/ddi_impldefs.h>
+#include <sys/gld.h>
+#include <sys/pci.h>
+#include <sys/strsubr.h>
+
+/*
+ * This used to be defined in sys/gld.h, but was flagged as private,
+ * and we used it anyway. Now it no longer exists, and we're stuck
+ * with it for the time being.
+ */
+#ifndef GLD_MAX_MULTICAST
+#define GLD_MAX_MULTICAST 64
+#endif
+
+#define __intptr_t_defined
+#define _STDINT_H
+#include "vm_basic_types.h"
+#include "vmxnet2_def.h"
+#include "vm_device_version.h"
+#include "net.h"
+#include "buildNumber.h"
+
+#define SOLVMXNET_SUCCESS 1
+#define SOLVMXNET_FAILURE 0
+
+#ifdef SOLVMXNET_DEBUG_LEVEL
+static int vxn_debug = SOLVMXNET_DEBUG_LEVEL;
+#define DPRINTF(n, args) if (vxn_debug>(n)) cmn_err args
+#else
+#define DPRINTF(n, args)
+#endif
+
+static char ident[] = "VMware Ethernet Adapter b" BUILD_NUMBER_NUMERIC_STRING;
+char _depends_on[] = {"misc/gld"};
+
+#define MAX_NUM_RECV_BUFFERS 128
+#define DEFAULT_NUM_RECV_BUFFERS 100
+#define MAX_NUM_XMIT_BUFFERS 128
+#define DEFAULT_NUM_XMIT_BUFFERS 100
+#define CRC_POLYNOMIAL_LE 0xedb88320UL
+#define SOLVMXNET_MAXNAME 20
+#define MAX_TX_WAIT_ON_STOP 2000
+
+#define ETHERALIGN 2
+#define SLACKBYTES 4
+#define MAXPKTBUF (14 + ETHERALIGN + ETHERMTU + SLACKBYTES)
+
+
+#define QHIWATER (MAX_NUM_RECV_BUFFERS*ETHERMTU)
+
+#define OUTB(dp, p, v) \
+ ddi_put8((dp)->vxnIOHdl, \
+ (uint8_t *)((caddr_t)((dp)->vxnIOp) + (p)), v)
+#define OUTW(dp, p, v) \
+ ddi_put16((dp)->vxnIOHdl, \
+ (uint16_t *)((caddr_t)((dp)->vxnIOp) + (p)), v)
+#define OUTL(dp, p, v) \
+ ddi_put32((dp)->vxnIOHdl, \
+ (uint32_t *)((caddr_t)((dp)->vxnIOp) + (p)), v)
+#define INB(dp, p) \
+ ddi_get8((dp)->vxnIOHdl, \
+ (uint8_t *)(((caddr_t)(dp)->vxnIOp) + (p)))
+#define INW(dp, p) \
+ ddi_get16((dp)->vxnIOHdl, \
+ (uint16_t *)(((caddr_t)(dp)->vxnIOp) + (p)))
+#define INL(dp, p) \
+ ddi_get32((dp)->vxnIOHdl, \
+ (uint32_t *)(((caddr_t)(dp)->vxnIOp) + (p)))
+
+#define VMXNET_INC(val, max) \
+ val++; \
+ if (UNLIKELY(val == max)) { \
+ val = 0; \
+ }
+
+#define TX_RINGBUF_MBLK(dp, idx) (dp->txRingBuf[idx].mblk)
+#define TX_RINGBUF_DMAMEM(dp, idx) (dp->txRingBuf[idx].dmaMem)
+
+typedef struct {
+ caddr_t buf; /* Virtual address */
+ uint32_t phyBuf; /* Physical address */
+ size_t bufLen; /* Buffer length */
+ ddi_dma_cookie_t cookie; /* Dma cookie */
+ uint_t cookieCount; /* Cookie count */
+ ddi_dma_handle_t dmaHdl; /* Dma handle */
+ ddi_acc_handle_t dataAccHdl; /* Dada access handle */
+} dma_buf_t;
+
+typedef struct rx_dma_buf {
+ dma_buf_t dmaDesc; /* Dma descriptor */
+ mblk_t *mblk; /* Streams message block */
+ frtn_t freeCB; /* Free callback */
+ struct vxn_softc *softc; /* Back pointer to softc */
+ struct rx_dma_buf *next; /* Next one in list */
+} rx_dma_buf_t;
+
+typedef struct vxn_stats {
+ uint32_t errxmt; /* Transmit errors */
+ uint32_t errrcv; /* Receive errors */
+ uint32_t runt; /* Runt packets */
+ uint32_t norcvbuf; /* Buffer alloc errors */
+ uint32_t interrupts; /* Interrupts */
+ uint32_t defer; /* Deferred transmits */
+} vxn_stats_t;
+
+typedef struct tx_ring_buf {
+ mblk_t *mblk;
+ dma_buf_t dmaMem;
+} tx_ring_buf_t;
+
+typedef struct vxn_softc {
+ char drvName[SOLVMXNET_MAXNAME]; /* Driver name string */
+ int unit; /* Driver instance */
+ vxn_stats_t stats; /* Stats */
+
+ dev_info_t *dip; /* Info pointer */
+ ddi_iblock_cookie_t iblockCookie; /* Interrupt block cookie */
+ gld_mac_info_t *macInfo; /* GLD mac info */
+ ddi_acc_handle_t confHdl; /* Configuration space handle */
+ ddi_acc_handle_t vxnIOHdl; /* I/O space handle */
+ caddr_t vxnIOp; /* I/O space pointer */
+ boolean_t morphed; /* Adapter morphed ? */
+
+ kmutex_t intrlock; /* Interrupt lock */
+ kmutex_t xmitlock; /* Transmit lock */
+ kmutex_t rxlistlock; /* Rx free pool lock */
+
+ boolean_t nicActive; /* NIC active flag */
+ boolean_t inIntr; /* Interrupt processing flag */
+
+ struct ether_addr devAddr; /* MAC address */
+
+ uint32_t vxnNumRxBufs; /* Number of reveice buffers */
+ uint32_t vxnNumTxBufs; /* Number of transmit buffers */
+
+ dma_buf_t driverDataDmaMem; /* Driver Data (dma handle) */
+ Vmxnet2_DriverData *driverData; /* Driver Data */
+ void *driverDataPhy; /* Driver Data busaddr pointer */
+ Vmxnet2_RxRingEntry *rxRing; /* Receive ring */
+ Vmxnet2_TxRingEntry *txRing; /* Transmit ring */
+ ddi_dma_handle_t txDmaHdl; /* Tx buffers dma handle */
+ rx_dma_buf_t *rxRingBuffPtr[MAX_NUM_RECV_BUFFERS];
+ /* DMA buffers associated with rxRing */
+ tx_ring_buf_t txRingBuf[MAX_NUM_XMIT_BUFFERS]; /* tx Ring buffers */
+
+ rx_dma_buf_t *rxFreeBufList;
+ uint32_t rxNumFreeBufs; /* current # of buffers in pool */
+ uint32_t rxMaxFreeBufs; /* max # of buffers in pool */
+
+ uint32_t txPending; /* Pending transmits */
+ uint32_t maxTxFrags; /* Max Tx fragments */
+
+ int multiCount; /* Multicast address count */
+ struct ether_addr multicastList[GLD_MAX_MULTICAST]; /* Multicast list */
+
+ struct vxn_softc *next; /* Circular list of instances */
+ struct vxn_softc *prev;
+} vxn_softc_t;
+
+/* used for rx buffers or buffers allocated by ddi_dma_mem_alloc() */
+static ddi_dma_attr_t vxn_dma_attrs = {
+ DMA_ATTR_V0, /* dma_attr version */
+ 0, /* dma_attr_addr_lo */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_addr_hi */
+ 0x7FFFFFFF, /* dma_attr_count_max */
+ 4, /* dma_attr_align */
+ 0x3F, /* dma_attr_burstsizes */
+ 1, /* dma_attr_minxfer */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_maxxfer */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_seg */
+ 1, /* dma_attr_sgllen */
+ 1, /* dma_attr_granular */
+ 0, /* dma_attr_flags */
+};
+
+/* used for tx buffers */
+static ddi_dma_attr_t vxn_dma_attrs_tx = {
+ DMA_ATTR_V0, /* dma_attr version */
+ 0, /* dma_attr_addr_lo */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_addr_hi */
+ 0x7FFFFFFF, /* dma_attr_count_max */
+ 1, /* dma_attr_align */
+ 0x3F, /* dma_attr_burstsizes */
+ 1, /* dma_attr_minxfer */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_maxxfer */
+ (uint64_t)0xFFFFFFFF, /* dma_attr_seg */
+ 1, /* dma_attr_sgllen */
+ 1, /* dma_attr_granular */
+ 0, /* dma_attr_flags */
+};
+
+
+static struct ether_addr etherbroadcastaddr = {
+ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+};
+
+static struct ddi_device_acc_attr vxn_buf_attrs = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_STRUCTURE_LE_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+static struct ddi_device_acc_attr dev_attr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_STRUCTURE_LE_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+static vxn_softc_t vxnList; /* for debugging */
+static kmutex_t vxnListLock;
+
+static void *Vxn_Memset(void *s, int c, size_t n);
+static int Vxn_Reset(gld_mac_info_t *macInfo);
+static int Vxn_SetPromiscuous(gld_mac_info_t *macInfo, int flag);
+static int Vxn_GetStats(gld_mac_info_t *macInfo, struct gld_stats *gs);
+static void Vxn_ApplyAddressFilter(vxn_softc_t *dp);
+static int Vxn_SetMulticast(gld_mac_info_t *macinfo, uint8_t *ep, int flag);
+static int Vxn_SetMacAddress(gld_mac_info_t *macInfo, uint8_t *mac);
+static int Vxn_Start(gld_mac_info_t *macInfo);
+static int Vxn_Stop(gld_mac_info_t *macInfo);
+static void Vxn_FreeTxBuf(vxn_softc_t *dp, int idx);
+static int Vxn_EncapTxBuf(vxn_softc_t *dp, mblk_t *mp, Vmxnet2_TxRingEntry *xre,
+ tx_ring_buf_t *txBuf);
+static int Vxn_Send(gld_mac_info_t *macinfo, mblk_t *mp);
+static boolean_t Vxn_TxComplete(vxn_softc_t *dp, boolean_t *reschedp);
+static boolean_t Vxn_Receive(vxn_softc_t *dp);
+static u_int Vxn_Interrupt(gld_mac_info_t *macInfo);
+static void Vxn_ReclaimRxBuf(rx_dma_buf_t *rxDesc);
+static void Vxn_FreeRxBuf(rx_dma_buf_t *rxDesc);
+static rx_dma_buf_t *Vxn_AllocRxBuf(vxn_softc_t *dp, int cansleep);
+static void Vxn_FreeInitBuffers(vxn_softc_t *dp);
+static int Vxn_AllocInitBuffers(vxn_softc_t *dp);
+static void Vxn_FreeDmaMem(dma_buf_t *dma);
+static int Vxn_AllocDmaMem(vxn_softc_t *dp, int size, int cansleep, dma_buf_t *dma);
+static void Vxn_FreeDriverData(vxn_softc_t *dp);
+static int Vxn_AllocDriverData(vxn_softc_t *dp);
+static int Vxn_Attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int Vxn_Detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static int Vxn_AllocRxBufPool(vxn_softc_t *dp);
+static void Vxn_FreeRxBufPool(vxn_softc_t *dp);
+static rx_dma_buf_t * Vxn_AllocRxBufFromPool(vxn_softc_t *dp);
+static void Vxn_FreeRxBufToPool(rx_dma_buf_t *rxDesc);
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Memset --
+ * memset() (Because bzero does not get resolved by module loader)
+ *
+ * Results:
+ * pointer to the memory area s
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void *
+Vxn_Memset(void *s, int c, size_t n)
+{
+ while (n--) {
+ ((uint8_t *)s)[n] = c;
+ }
+
+ return s;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Reset --
+ * Stub routine to reset hardware. Presently does nothing. Start/Stop should
+ * take care of resets.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Reset(gld_mac_info_t *macInfo)
+{
+ return GLD_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_SetPromiscuous --
+ * Set/Reset NIC to/from promiscuous mode
+ *
+ * Results:
+ * GLD_SUCCESS
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_SetPromiscuous(gld_mac_info_t *macInfo, int flag)
+{
+ vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private;
+ Vmxnet2_DriverData *dd = dp->driverData;
+
+ mutex_enter(&dp->intrlock);
+ if (flag == GLD_MAC_PROMISC_PHYS) {
+ dd->ifflags |= VMXNET_IFF_PROMISC;
+ } else if (flag == GLD_MAC_PROMISC_MULTI) {
+ /*
+ * This should really set VMXNET_IFF_ALLMULTI,
+ * but unfortunately it doesn't exist. The next
+ * best thing would be to set the LADRFs to all
+ * 0xFFs and set VMXNET_IFF_MULTICAST, but that
+ * opens up a whole new set of potential pitfalls,
+ * so this is a reasonable temporary solution.
+ */
+ dd->ifflags |= VMXNET_IFF_PROMISC;
+ } else if (flag == GLD_MAC_PROMISC_NONE) {
+ dd->ifflags &= ~VMXNET_IFF_PROMISC;
+ } else {
+ /* This could be GLD_MAC_PROMISC_NOOP? */
+ mutex_exit(&dp->intrlock);
+ cmn_err(CE_WARN, "%s%d: Vxn_SetPromiscuous: Unexpected mode flag: 0x%x",
+ dp->drvName, dp->unit, flag);
+
+ return GLD_FAILURE;
+ }
+
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF);
+ mutex_exit(&dp->intrlock);
+
+ return GLD_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_GetStats --
+ * Get driver specific stats
+ *
+ * Results:
+ * GLD_SUCCESS
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_GetStats(gld_mac_info_t *macInfo, struct gld_stats *gs)
+{
+ vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private;
+
+ gs->glds_errxmt = dp->stats.errxmt;
+ gs->glds_errrcv = dp->stats.errrcv;
+ gs->glds_short = dp->stats.runt;
+ gs->glds_norcvbuf = dp->stats.norcvbuf;
+ gs->glds_intr = dp->stats.interrupts;
+ gs->glds_defer = dp->stats.defer;
+
+ return GLD_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_ApplyAddressFilter --
+ * Go over multicast list and compute/apply address filter
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_ApplyAddressFilter(vxn_softc_t *dp)
+{
+ uint8_t *ep;
+ int i, j, bit, byte;
+ uint32_t crc, poly = CRC_POLYNOMIAL_LE;
+ Vmxnet2_DriverData *dd = dp->driverData;
+ volatile uint16_t *mcastTable = (uint16_t *)dd->LADRF;
+
+ ASSERT(MUTEX_HELD(&dp->intrlock));
+
+ /* clear the multicast filter */
+ dd->LADRF[0] = 0;
+ dd->LADRF[1] = 0;
+
+ for (i = 0; i < dp->multiCount; i++) {
+ crc = 0xffffffff;
+ ep = (uint8_t *)&dp->multicastList[i].ether_addr_octet;
+
+ for (byte = 0; byte < 6; byte++) {
+ for (bit = *ep++, j = 0; j < 8; j++, bit >>= 1) {
+ int test;
+
+ test = ((bit ^ crc) & 0x01);
+ crc >>= 1;
+
+ if (test) {
+ crc = crc ^ poly;
+ }
+ }
+ }
+
+ crc = crc >> 26;
+ mcastTable[crc >> 4] |= 1 << (crc & 0xf);
+ }
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_SetMulticast --
+ * Add delete entry from multicast list
+ *
+ * Results:
+ * GLD_FAILURE on failure
+ * GLD_SUCCESS on success
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_SetMulticast(gld_mac_info_t *macinfo, uint8_t *ep, int flag)
+{
+ int i;
+ int copyLen;
+ vxn_softc_t *dp = (vxn_softc_t *)macinfo->gldm_private;
+ Vmxnet2_DriverData *dd = dp->driverData;
+
+ if (flag == GLD_MULTI_ENABLE) {
+ /*
+ * Exceeded multicast address limit
+ */
+ if (dp->multiCount >= GLD_MAX_MULTICAST) {
+ return GLD_FAILURE;
+ }
+
+ /*
+ * Add mac address to multicast list
+ */
+ bcopy(ep, dp->multicastList[dp->multiCount].ether_addr_octet,
+ ETHERADDRL);
+ dp->multiCount++;
+ }
+ else {
+ for (i=0; i<dp->multiCount; i++) {
+ if (bcmp(ep, dp->multicastList[i].ether_addr_octet, ETHERADDRL) == 0) {
+ goto found;
+ }
+ }
+ return GLD_FAILURE;
+
+ found:
+ /*
+ * Delete mac address from multicast list
+ */
+ copyLen = (dp->multiCount - (i+1)) * sizeof(struct ether_addr);
+ if (copyLen > 0) {
+ bcopy(&dp->multicastList[i+1], &dp->multicastList[i], copyLen);
+ }
+ dp->multiCount--;
+ }
+
+ /*
+ * Compute address filter from list of addressed and apply it
+ */
+ mutex_enter(&dp->intrlock);
+ Vxn_ApplyAddressFilter(dp);
+
+ if (dp->multiCount) {
+ ASSERT(dd->LADRF[0] || dd->LADRF[1]);
+ dd->ifflags |= VMXNET_IFF_MULTICAST;
+ } else {
+ ASSERT(!(dd->LADRF[0] || dd->LADRF[1]));
+ dd->ifflags &= ~VMXNET_IFF_MULTICAST;
+ }
+
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF);
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_LADRF);
+ mutex_exit(&dp->intrlock);
+
+ return GLD_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_SetMacAddress --
+ * Change device MAC address
+ *
+ * Results:
+ * GLD_SUCCESS
+ * GLD_FAILURE
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_SetMacAddress(gld_mac_info_t *macInfo, uint8_t *mac)
+{
+ int i;
+ int err = GLD_SUCCESS;
+ vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private;
+
+ mutex_enter(&dp->intrlock);
+ mutex_enter(&dp->xmitlock);
+
+ /*
+ * Don't change MAC address on a running NIC
+ */
+ if (dp->nicActive) {
+ err = GLD_FAILURE;
+ goto out;
+ }
+
+ /*
+ * Save new MAC address
+ */
+ for (i = 0; i < 6; i++) {
+ dp->devAddr.ether_addr_octet[i] = mac[i];
+ }
+
+ /*
+ * Push new MAC address down into hardware
+ */
+ for (i = 0; i < 6; i++) {
+ OUTB(dp, VMXNET_MAC_ADDR + i, mac[i]);
+ }
+
+out:
+ mutex_exit(&dp->xmitlock);
+ mutex_exit(&dp->intrlock);
+ return err;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Start --
+ * Device start routine. Called on "ifconfig plumb"
+ *
+ * Results:
+ * GLD_SUCCESS
+ * GLD_FAILURE
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Start(gld_mac_info_t *macInfo)
+{
+ int err = GLD_SUCCESS;
+ uint32_t r, capabilities, features;
+ vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private;
+
+ mutex_enter(&dp->intrlock);
+ mutex_enter(&dp->xmitlock);
+
+ if (!dp->nicActive) {
+ /*
+ * Register ring structure with hardware
+ *
+ * This downcast is OK because we requested a 32-bit physical address
+ */
+ OUTL(dp, VMXNET_INIT_ADDR, (uint32_t)(uintptr_t)dp->driverDataPhy);
+ OUTL(dp, VMXNET_INIT_LENGTH, dp->driverData->length);
+
+ /*
+ * Make sure registeration succeded
+ */
+ r = INL(dp, VMXNET_INIT_LENGTH);
+ if (!r) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Start: failed to register ring",
+ dp->drvName, dp->unit);
+ err = GLD_FAILURE;
+ goto out;
+ }
+
+ /*
+ * Get maximum tx fragments supported
+ */
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_CAPABILITIES);
+ capabilities = INL(dp, VMXNET_COMMAND_ADDR);
+
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_FEATURES);
+ features = INL(dp, VMXNET_COMMAND_ADDR);
+
+ DPRINTF(3, (CE_CONT, "%s%d: chip capabilities=0x%x features=0x%x\n",
+ dp->drvName, dp->unit, capabilities, features));
+
+ if ((capabilities & VMNET_CAP_SG) &&
+ (features & VMXNET_FEATURE_ZERO_COPY_TX)) {
+ dp->maxTxFrags = VMXNET2_SG_DEFAULT_LENGTH;
+ } else {
+ dp->maxTxFrags = 1;
+ }
+ ASSERT(dp->maxTxFrags >= 1);
+
+ /*
+ * Alloc Tx DMA handle
+ */
+ vxn_dma_attrs_tx.dma_attr_sgllen = dp->maxTxFrags;
+ if (ddi_dma_alloc_handle(dp->dip, &vxn_dma_attrs_tx, DDI_DMA_SLEEP,
+ NULL, &dp->txDmaHdl) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Start: failed to alloc tx dma handle",
+ dp->drvName, dp->unit);
+ err = GLD_FAILURE;
+ goto out;
+ }
+
+ /*
+ * Enable interrupts on the card
+ */
+ dp->driverData->ifflags |= VMXNET_IFF_BROADCAST | VMXNET_IFF_DIRECTED;
+
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_ENABLE);
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_IFF);
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_UPDATE_LADRF);
+
+ dp->nicActive = TRUE;
+ }
+
+out:
+ mutex_exit(&dp->xmitlock);
+ mutex_exit(&dp->intrlock);
+ return err;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Stop --
+ * Device stop routine. Called on "ifconfig unplumb"
+ *
+ * Results:
+ * GLD_SUCCESS
+ * GLD_FAILURE
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Stop(gld_mac_info_t *macInfo)
+{
+ int i;
+ int err = GLD_SUCCESS;
+ vxn_softc_t * dp = (vxn_softc_t *)macInfo->gldm_private;
+ boolean_t resched;
+
+ mutex_enter(&dp->intrlock);
+ mutex_enter(&dp->xmitlock);
+
+ if (!dp->nicActive) {
+ goto out;
+ }
+
+ /*
+ * Disable interrupts
+ */
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_DISABLE);
+
+ /*
+ * Wait for pending transmits
+ */
+ if (dp->txPending) {
+ for (i=0; i < MAX_TX_WAIT_ON_STOP && dp->txPending; i++) {
+ delay(drv_usectohz(1000));
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_CHECK_TX_DONE);
+ (void) Vxn_TxComplete(dp, &resched);
+ /*
+ * Don't worry about rescheduling transmits - GLD handles
+ * this automatically.
+ */
+ }
+ }
+ if (dp->txPending) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Stop: giving up on %d pending transmits",
+ dp->drvName, dp->unit, dp->txPending);
+ }
+
+ OUTL(dp, VMXNET_INIT_ADDR, 0);
+ dp->nicActive = FALSE;
+
+ /*
+ * Free Tx DMA handle
+ *
+ * The ddi_dma_free_handle() man page says that ddi_dma_unbind_handle() must be called
+ * prior to calling ddi_dma_free_handle().
+ * However, call to ddi_dma_unbind_handle() is not required here, because
+ * ddi_dma_addr_bind_handle() and matching ddi_dma_unbind_handle() are called from
+ * Vxn_EncapTxBuf().
+ * xmitlock is held in Vxn_EncapTxBuf() as well as acquired above in Vxn_Stop().
+ */
+ ddi_dma_free_handle(&dp->txDmaHdl);
+ dp->txDmaHdl = NULL;
+
+out:
+ mutex_exit(&dp->xmitlock);
+ mutex_exit(&dp->intrlock);
+ return err;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeTxBuf --
+ * Free transmit buffer
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeTxBuf(vxn_softc_t *dp, int idx)
+{
+ mblk_t **txMblkp = &TX_RINGBUF_MBLK(dp, idx);
+ dma_buf_t *dmaMem = &TX_RINGBUF_DMAMEM(dp, idx);
+
+ if (*txMblkp) {
+ freemsg(*txMblkp);
+ *txMblkp = NULL;
+ }
+
+ if (dmaMem->buf) {
+ Vxn_FreeDmaMem(dmaMem);
+ ASSERT(dmaMem->buf == NULL);
+ }
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_EncapTxBuf --
+ * Go over dma mappings of Tx buffers and drop buffer physical address
+ * into ring entry
+ *
+ * Results:
+ * SOLVMXNET_SUCCESS on success
+ * SOLVMXNET_FAILURE on failure
+ *
+ * Side effects:
+ * None
+ *---------------- -------------------------------------------------------------
+ */
+static int
+Vxn_EncapTxBuf(vxn_softc_t *dp,
+ mblk_t *mp,
+ Vmxnet2_TxRingEntry *xre,
+ tx_ring_buf_t *txBuf)
+{
+ int frag;
+ int fragcount;
+ int rval;
+ mblk_t *tp;
+ mblk_t *mblk;
+ boolean_t needPullup = FALSE;
+ boolean_t dmaMemAlloced = FALSE;
+
+ ASSERT(txBuf);
+ ASSERT(txBuf->mblk == NULL);
+ ASSERT(MUTEX_HELD(&dp->xmitlock));
+
+ xre->sg.length = 0;
+ xre->flags = 0;
+
+ fragcount = 0;
+ for (tp = mp; tp != NULL; tp = tp->b_cont) {
+ fragcount++;
+ }
+ if (fragcount > dp->maxTxFrags) {
+ needPullup = TRUE;
+ }
+
+pullup:
+ frag = 0;
+ if (needPullup) {
+ if (!(mblk = msgpullup(mp, -1))) {
+ cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: msgpullup failed",
+ dp->drvName, dp->unit);
+ goto err;
+ }
+ } else {
+ mblk = mp;
+ }
+
+ /*
+ * Go through message chain and drop packet pointers into ring
+ * scatter/gather array
+ */
+ for (tp = mblk; tp != NULL; tp = tp->b_cont) {
+
+ uint_t nCookies;
+ ddi_dma_cookie_t dmaCookie;
+ int len = tp->b_wptr - tp->b_rptr;
+
+ if (len) {
+ /*
+ * Associate tx buffer with dma handle
+ */
+ ASSERT(dp->txDmaHdl);
+ if ((rval = ddi_dma_addr_bind_handle(dp->txDmaHdl, NULL, (caddr_t)tp->b_rptr,
+ len, DDI_DMA_RDWR | DDI_DMA_STREAMING,
+ DDI_DMA_DONTWAIT, NULL,
+ &dmaCookie, &nCookies))
+ != DDI_DMA_MAPPED) {
+
+ /*
+ * Try to handle bind failure caused by a page boundary spill
+ * by allocating a private dma buffer and copying data into it
+ */
+ if ((rval == DDI_DMA_TOOBIG) && !dmaMemAlloced ) {
+ /*
+ * Force pullup
+ */
+ if (!needPullup && (dp->maxTxFrags > 1)) {
+ needPullup = TRUE;
+ goto pullup;
+ }
+
+ if (Vxn_AllocDmaMem(dp, len, FALSE, &txBuf->dmaMem)
+ != SOLVMXNET_SUCCESS) {
+ goto err;
+ }
+
+ dmaMemAlloced = TRUE;
+
+ /*
+ * Copy data into DMA capable buffer
+ */
+ bcopy(tp->b_rptr, txBuf->dmaMem.buf, len);
+
+ /*
+ * Stick buffer physical addr in the ring
+ */
+ xre->sg.sg[frag].addrLow = txBuf->dmaMem.phyBuf;
+ xre->sg.sg[frag].length = len;
+ frag++;
+
+ continue;
+
+ } else {
+ cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: failed (%d) to bind dma "
+ "handle for len %d. [dmaMemAlloced=%d]",
+ dp->drvName, dp->unit, rval, len, dmaMemAlloced);
+ goto err;
+ }
+ }
+
+ /*
+ * Extract tx buffer physical addresses from cookie
+ */
+ while (nCookies) {
+ if (UNLIKELY(frag == dp->maxTxFrags)) {
+ (void)ddi_dma_unbind_handle(dp->txDmaHdl);
+
+ if (!needPullup) {
+ ASSERT(!dmaMemAlloced);
+ needPullup = TRUE;
+ goto pullup;
+ } else {
+ cmn_err(CE_WARN, "%s%d: Vxn_EncapTxBuf: "
+ "exceeded max (%d) fragments in message",
+ dp->drvName, dp->unit, dp->maxTxFrags);
+ goto err;
+ }
+ }
+
+ /*
+ * Stick it in the ring
+ */
+ xre->sg.sg[frag].addrLow = dmaCookie.dmac_address;
+ xre->sg.sg[frag].length = dmaCookie.dmac_size;
+ frag++;
+
+ if (--nCookies) {
+ ddi_dma_nextcookie(dp->txDmaHdl, &dmaCookie);
+ }
+ }
+
+ (void)ddi_dma_unbind_handle(dp->txDmaHdl);
+ }
+ }
+
+ if (frag > 0) {
+ xre->sg.length = frag;
+
+ /* Give ownership to NIC */
+ xre->sg.addrType = NET_SG_PHYS_ADDR;
+ xre->ownership = VMXNET2_OWNERSHIP_NIC;
+ xre->flags |= VMXNET2_TX_CAN_KEEP;
+ txBuf->mblk = mblk;
+
+ /*
+ * If we called msgpullup to concatenate fragments, free
+ * original mblk now since we're going to return success.
+ */
+ if (mblk != mp) {
+ freemsg(mp);
+ }
+
+ return SOLVMXNET_SUCCESS;
+ }
+
+err:
+ if (mblk != NULL && mblk != mp) {
+ /*
+ * Free mblk allocated by msgpullup.
+ */
+ freemsg(mblk);
+ }
+
+ if (dmaMemAlloced) {
+ ASSERT(txBuf->dmaMem.buf);
+ Vxn_FreeDmaMem(&txBuf->dmaMem);
+ }
+
+ return SOLVMXNET_FAILURE;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Send --
+ * GLD Transmit routine. Starts packet hard tx.
+ *
+ * Results:
+ * GLD_SUCCESS on success
+ * GLD_FAILURE on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Send(gld_mac_info_t *macinfo, mblk_t *mp)
+{
+ Vmxnet2_TxRingEntry *xre;
+ int err = GLD_SUCCESS;
+ vxn_softc_t *dp = (vxn_softc_t *)macinfo->gldm_private;
+ Vmxnet2_DriverData *dd = dp->driverData;
+ boolean_t resched = FALSE;
+
+ mutex_enter(&dp->xmitlock);
+
+ /*
+ * Check if ring entry at drop pointer is available
+ */
+ if (TX_RINGBUF_MBLK(dp, dd->txDriverNext) != NULL) {
+ DPRINTF(3, (CE_NOTE, "%s%d: Vxn_Send: tx ring full",
+ dp->drvName, dp->unit));
+ err = GLD_NORESOURCES;
+ dd->txStopped = TRUE;
+ dp->stats.defer++;
+ goto out;
+ }
+
+ xre = &dp->txRing[dd->txDriverNext];
+
+ /*
+ * Drop packet into ring entry
+ */
+ if (Vxn_EncapTxBuf(dp, mp, xre, &dp->txRingBuf[dd->txDriverNext])
+ != SOLVMXNET_SUCCESS) {
+ err = GLD_FAILURE;
+ dp->stats.errxmt++;
+ goto out;
+ }
+
+ /*
+ * Increment drop pointer
+ */
+ VMXNET_INC(dd->txDriverNext, dd->txRingLength);
+ dd->txNumDeferred++;
+ dp->txPending++;
+
+ /*
+ * Transmit, if number of pending packets > tx cluster length
+ */
+ if (dd->txNumDeferred >= dd->txClusterLength) {
+ dd->txNumDeferred = 0;
+
+ /*
+ * Call hardware transmit
+ */
+ INL(dp, VMXNET_TX_ADDR);
+ }
+
+ /*
+ * Clean up transmit ring. TX completion interrupts are not guaranteed
+ */
+ (void) Vxn_TxComplete(dp, &resched);
+
+out:
+ mutex_exit(&dp->xmitlock);
+ if (resched) {
+ /* Tell GLD to retry any deferred packets */
+ gld_sched(dp->macInfo);
+ }
+ return err;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_TxComplete --
+ * Scan Tx ring for completed transmits. Reclaim Tx buffers.
+ *
+ * Results:
+ * Returns TRUE if it found a completed transmit, FALSE otherwise.
+ * Also sets *reschedp to TRUE if the caller should call gld_sched
+ * to reschedule transmits (once all locks are dropped).
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static boolean_t
+Vxn_TxComplete(vxn_softc_t *dp, boolean_t *reschedp)
+{
+ Vmxnet2_DriverData *dd = dp->driverData;
+ boolean_t found = FALSE;
+ boolean_t needresched = FALSE;
+
+ ASSERT(MUTEX_HELD(&dp->xmitlock));
+
+ while (1) {
+ Vmxnet2_TxRingEntry *xre = &dp->txRing[dd->txDriverCur];
+
+ if (xre->ownership != VMXNET2_OWNERSHIP_DRIVER ||
+ (TX_RINGBUF_MBLK(dp, dd->txDriverCur) == NULL)) {
+ break;
+ }
+
+ found = TRUE;
+ Vxn_FreeTxBuf(dp, dd->txDriverCur);
+
+ dp->txPending--;
+ VMXNET_INC(dd->txDriverCur, dd->txRingLength);
+ if (dd->txStopped) {
+ needresched = TRUE;
+ dd->txStopped = FALSE;
+ }
+ }
+
+ *reschedp = needresched;
+ return found;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Receive --
+ * Rx handler. First assembles the packets into a chain of mblks,
+ * then drops locks and passes them up the stack to GLD.
+ *
+ * Results:
+ * Returns TRUE if it find a packet ready for processing, FALSE
+ * otherwise.
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static boolean_t
+Vxn_Receive(vxn_softc_t *dp)
+{
+ int ringnext;
+ short pktlen;
+ Vmxnet2_DriverData *dd = dp->driverData;
+ rx_dma_buf_t *rxDesc;
+ rx_dma_buf_t *newRxDesc;
+ mblk_t *mblk;
+ mblk_t *head = NULL;
+ mblk_t **tail = &head;
+ mblk_t *next;
+ boolean_t found = FALSE; /* Did we find at least one packet? */
+
+ ASSERT(MUTEX_HELD(&dp->intrlock));
+
+ /*
+ * Walk receive ring looking for entries with ownership
+ * reverted back to driver
+ */
+ while (1) {
+ Vmxnet2_RxRingEntry *rre;
+ rx_dma_buf_t **rbuf;
+
+ ringnext = dd->rxDriverNext;
+ rre = &dp->rxRing[ringnext];
+ rbuf = &dp->rxRingBuffPtr[ringnext];
+
+ if (rre->ownership != VMXNET2_OWNERSHIP_DRIVER) {
+ break;
+ }
+
+ found = TRUE;
+
+ pktlen = rre->actualLength;
+
+ if (pktlen < (60 - 4)) {
+ /*
+ * Ethernet header vlan tags are 4 bytes. Some vendors generate
+ * 60byte frames including vlan tags. When vlan tag
+ * is stripped, such frames become 60 - 4. (PR106153)
+ */
+ dp->stats.errrcv++;
+ if (pktlen != 0) {
+ DPRINTF(3, (CE_CONT, "%s%d: runt packet\n", dp->drvName, dp->unit));
+ dp->stats.runt++;
+ }
+ } else {
+ /*
+ * Alloc new Rx buffer to replace current one
+ */
+ newRxDesc = Vxn_AllocRxBufFromPool(dp);
+
+ if (newRxDesc) {
+ rxDesc = *rbuf;
+ mblk = rxDesc->mblk;
+
+ *rbuf = newRxDesc;
+ rre->paddr = newRxDesc->dmaDesc.phyBuf + ETHERALIGN;
+ rre->bufferLength = MAXPKTBUF - ETHERALIGN;
+ rre->actualLength = 0;
+
+ /*
+ * Advance write pointer past packet length
+ */
+ mblk->b_wptr = mblk->b_rptr + pktlen;
+
+ /*
+ * Add to end of chain.
+ */
+ mblk->b_next = NULL;
+ *tail = mblk;
+ tail = &mblk->b_next;
+ } else {
+ dp->stats.errrcv++;
+ dp->stats.norcvbuf++;
+ }
+ }
+
+ /* Give the descriptor back to NIC */
+ rre->ownership = VMXNET2_OWNERSHIP_NIC;
+ VMXNET_INC(dd->rxDriverNext, dd->rxRingLength);
+ }
+
+ /*
+ * Walk chain and pass mblks up to gld_recv one by one.
+ */
+ mutex_exit(&dp->intrlock);
+ for (mblk = head; mblk != NULL; mblk = next) {
+ next = mblk->b_next;
+ mblk->b_next = NULL;
+ gld_recv(dp->macInfo, mblk);
+ }
+ mutex_enter(&dp->intrlock);
+
+ return (found);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Interrupt --
+ * GLD interrupt handler. Scan: Rx ring for received packets, Tx ring for
+ * completed transmits
+ *
+ * Results:
+ * - DDI_INTR_CLAIMED (if we found something to do)
+ * - DDI_INTR_UNCLAIMED (if not)
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static u_int
+Vxn_Interrupt(gld_mac_info_t *macInfo)
+{
+ u_int ret = DDI_INTR_UNCLAIMED;
+ vxn_softc_t *dp = (vxn_softc_t *)macInfo->gldm_private;
+ boolean_t foundRx, foundTx;
+ boolean_t resched = FALSE;
+
+ mutex_enter(&dp->intrlock);
+ dp->inIntr = TRUE;
+
+ if (!dp->nicActive) {
+ goto out;
+ }
+
+ /*
+ * Ack interrupt
+ */
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_INTR_ACK);
+
+ foundRx = Vxn_Receive(dp);
+
+ mutex_enter(&dp->xmitlock);
+ foundTx = Vxn_TxComplete(dp, &resched);
+ mutex_exit(&dp->xmitlock);
+
+ if (foundRx || foundTx) {
+ ret = DDI_INTR_CLAIMED;
+ dp->stats.interrupts++;
+ }
+
+out:
+ dp->inIntr = FALSE;
+ mutex_exit(&dp->intrlock);
+
+ if (resched) {
+ gld_sched(dp->macInfo);
+ }
+
+ return ret;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_ReclaimRxBuf --
+ * Callback handler invoked by freemsg(). Frees Rx buffer memory and mappings
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_ReclaimRxBuf(rx_dma_buf_t *rxDesc)
+{
+ Vxn_FreeRxBufToPool(rxDesc);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeRxBuf --
+ * Free allocated Rx buffer
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeRxBuf(rx_dma_buf_t *rxDesc)
+{
+ ASSERT(rxDesc);
+
+ if (rxDesc->mblk) {
+ freemsg(rxDesc->mblk);
+ } else {
+ Vxn_FreeDmaMem(&rxDesc->dmaDesc);
+ kmem_free(rxDesc, sizeof(rx_dma_buf_t));
+ }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocRxBuf --
+ * Allocate Rx buffer
+ *
+ * Results:
+ * Pointer to Rx buffer descriptor - on success
+ * NULL - on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static rx_dma_buf_t *
+Vxn_AllocRxBuf(vxn_softc_t *dp, int cansleep)
+{
+ rx_dma_buf_t *rxDesc;
+
+ rxDesc = (rx_dma_buf_t *)kmem_zalloc(sizeof(rx_dma_buf_t),
+ cansleep ? KM_SLEEP : KM_NOSLEEP);
+ if (!rxDesc) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBuf: kmem_zalloc failed",
+ dp->drvName, dp->unit);
+ return NULL;
+ }
+
+ rxDesc->softc = dp;
+
+ /*
+ * Alloc dma-able packet memory
+ */
+ if (Vxn_AllocDmaMem(dp, MAXPKTBUF, cansleep, &rxDesc->dmaDesc)
+ != SOLVMXNET_SUCCESS) {
+ kmem_free(rxDesc, sizeof(rx_dma_buf_t));
+ return NULL;
+ }
+
+ /*
+ * Fill in free callback; fired by freemsg()
+ */
+ rxDesc->freeCB.free_func = &Vxn_ReclaimRxBuf;
+ rxDesc->freeCB.free_arg = (caddr_t) rxDesc;
+
+ rxDesc->mblk = NULL;
+ return rxDesc;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeInitBuffers --
+ * Free allocated Tx and Rx buffers
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeInitBuffers(vxn_softc_t *dp)
+{
+ int i;
+
+ for (i=0; i<dp->vxnNumRxBufs; i++) {
+ if (dp->rxRingBuffPtr[i]) {
+ Vxn_FreeRxBuf(dp->rxRingBuffPtr[i]);
+ dp->rxRingBuffPtr[i] = NULL;
+ }
+ }
+
+ for (i=0; i<dp->vxnNumTxBufs; i++) {
+ if (TX_RINGBUF_MBLK(dp, i)) {
+ Vxn_FreeTxBuf(dp, i);
+ }
+ }
+
+ /*
+ * Rx pool must get freed last. Rx buffers above will
+ * show up on the pool when freemsg callback fires.
+ */
+ Vxn_FreeRxBufPool(dp);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocRxBufPool --
+ * Allocate pool of rx buffers - 3 * configured Rx buffers
+ *
+ * Results:
+ * SOLVMXNET_SUCCESS/SOLVMXNET_FAILURE
+ *
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_AllocRxBufPool(vxn_softc_t *dp)
+{
+ int i;
+
+ dp->rxFreeBufList = NULL;
+
+ // Allow list to double in size if needed. Any additional buffers
+ // that are allocated on the fly will be freed back to main memory.
+ dp->rxMaxFreeBufs = dp->vxnNumRxBufs * 6;
+
+ for (i = 0; i < dp->vxnNumRxBufs * 3; i++) {
+ rx_dma_buf_t *rxDesc;
+
+ /*
+ * Alloc rx buffer
+ */
+ if (!(rxDesc = Vxn_AllocRxBuf(dp, TRUE))) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBufPool: failed to allocate memory",
+ dp->drvName, dp->unit);
+ dp->rxNumFreeBufs = i;
+ return SOLVMXNET_FAILURE;
+ }
+ /*
+ * Add to free list
+ */
+ rxDesc->next = dp->rxFreeBufList;
+ dp->rxFreeBufList = rxDesc;
+ }
+
+ dp->rxNumFreeBufs = i;
+ return SOLVMXNET_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeRxBufPool --
+ * Free rx buffers pool
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeRxBufPool(vxn_softc_t *dp)
+{
+ while (dp->rxFreeBufList) {
+ rx_dma_buf_t *rxDesc = dp->rxFreeBufList;
+
+ /* unlink */
+ dp->rxFreeBufList = rxDesc->next;
+
+ ASSERT(rxDesc->mblk == NULL);
+ Vxn_FreeDmaMem(&rxDesc->dmaDesc);
+ kmem_free(rxDesc, sizeof(rx_dma_buf_t));
+ }
+ dp->rxNumFreeBufs = 0;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocRxBufFromPool --
+ * Allocate Rx buffer from free pool
+ *
+ * Results:
+ * Pointer to Rx buffer descriptor - on success
+ * NULL - on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static rx_dma_buf_t *
+Vxn_AllocRxBufFromPool(vxn_softc_t *dp)
+{
+ rx_dma_buf_t *rxDesc = NULL;
+
+ mutex_enter(&dp->rxlistlock);
+ if (dp->rxFreeBufList) {
+ rxDesc = dp->rxFreeBufList;
+ dp->rxFreeBufList = rxDesc->next;
+ ASSERT(dp->rxNumFreeBufs >= 1);
+ dp->rxNumFreeBufs--;
+ }
+ mutex_exit(&dp->rxlistlock);
+
+ if (!rxDesc) {
+ /*
+ * Try to allocate new descriptor from memory. Can't block here
+ * since we could be being called from interrupt context.
+ */
+ DPRINTF(5, (CE_NOTE, "%s%d: allocating rx buf from memory",
+ dp->drvName, dp->unit));
+ if (!(rxDesc = Vxn_AllocRxBuf(dp, FALSE))) {
+ cmn_err(CE_WARN,
+ "%s%d: Vxn_AllocRxBufFromPool : pool rx alloc failed",
+ dp->drvName, dp->unit);
+ return NULL;
+ }
+ }
+
+ /*
+ * Allocate new message block for this buffer
+ */
+ rxDesc->mblk = desballoc((uchar_t *)rxDesc->dmaDesc.buf + ETHERALIGN,
+ rxDesc->dmaDesc.bufLen - ETHERALIGN,
+ BPRI_MED, &rxDesc->freeCB);
+ if (!rxDesc->mblk) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocRxBufFromPool : desballoc failed",
+ dp->drvName, dp->unit);
+
+ /* put back on free list */
+ Vxn_FreeRxBufToPool(rxDesc);
+ return NULL;
+ }
+
+ return rxDesc;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeRxBufToPool --
+ * Return rx buffer to free pool
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeRxBufToPool(rx_dma_buf_t *rxDesc)
+{
+ vxn_softc_t *dp = rxDesc->softc;
+
+ rxDesc->mblk = NULL;
+
+ /*
+ * Insert on free list, or free if the list is full
+ */
+ mutex_enter(&dp->rxlistlock);
+ if (dp->rxNumFreeBufs >= dp->rxMaxFreeBufs) {
+ DPRINTF(5, (CE_NOTE, "%s%d: freeing rx buf to memory",
+ dp->drvName, dp->unit));
+ Vxn_FreeRxBuf(rxDesc);
+ } else {
+ rxDesc->next = dp->rxFreeBufList;
+ dp->rxFreeBufList = rxDesc;
+ dp->rxNumFreeBufs++;
+ }
+ mutex_exit(&dp->rxlistlock);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocInitBuffers --
+ * Allocated Rx buffers and init ring entries
+ *
+ * Results:
+ * SOLVMXNET_SUCCESS - on success
+ * SOLVMXNET_FAILURE - on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_AllocInitBuffers(vxn_softc_t *dp)
+{
+ Vmxnet2_DriverData *dd;
+ uint32_t i, offset;
+
+ dd = dp->driverData;
+ offset = sizeof(*dd);
+
+ /*
+ * Init shared structures
+ */
+ dd->rxRingLength = dp->vxnNumRxBufs;
+ dd->rxRingOffset = offset;
+ dp->rxRing = (Vmxnet2_RxRingEntry *)((uintptr_t)dd + offset);
+ offset += dp->vxnNumRxBufs * sizeof(Vmxnet2_RxRingEntry);
+
+ dd->rxRingLength2 = 1;
+ dd->rxRingOffset2 = offset;
+ offset += sizeof(Vmxnet2_RxRingEntry);
+
+ dd->txRingLength = dp->vxnNumTxBufs;
+ dd->txRingOffset = offset;
+ dp->txRing = (Vmxnet2_TxRingEntry *)((uintptr_t)dd + offset);
+ offset += dp->vxnNumTxBufs * sizeof(Vmxnet2_TxRingEntry);
+
+ /*
+ * Alloc Rx buffers pool
+ */
+ if ( Vxn_AllocRxBufPool(dp) != SOLVMXNET_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocInitBuffers: failed to alloc buf pool",
+ dp->drvName, dp->unit);
+ return SOLVMXNET_FAILURE;
+ }
+
+ /*
+ * Allocate receive buffers
+ */
+ for (i = 0; i < dp->vxnNumRxBufs; i++) {
+ rx_dma_buf_t *rxDesc;
+ Vmxnet2_RxRingEntry *rre = &dp->rxRing[i];
+
+ if (!(rxDesc = Vxn_AllocRxBufFromPool(dp))) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocInitBuffers: "
+ "failed to alloc buf from pool", dp->drvName, dp->unit);
+ goto err;
+ }
+
+ /*
+ * Init ring entries
+ */
+ rre->paddr = rxDesc->dmaDesc.phyBuf + ETHERALIGN;
+ rre->bufferLength = MAXPKTBUF - ETHERALIGN;
+ rre->actualLength = 0;
+ dp->rxRingBuffPtr[i] = rxDesc;
+ rre->ownership = VMXNET2_OWNERSHIP_NIC;
+ }
+
+ dp->txDmaHdl = NULL;
+
+ /*
+ * Dummy recvRing2 tacked on to the end, with a single unusable entry
+ */
+ dp->rxRing[i].paddr = 0;
+ dp->rxRing[i].bufferLength = 0;
+ dp->rxRing[i].actualLength = 0;
+ dp->rxRingBuffPtr[i] = NULL;
+ dp->rxRing[i].ownership = VMXNET2_OWNERSHIP_DRIVER;
+
+ dd->rxDriverNext = 0;
+
+ /*
+ * Give xmit ring ownership to DRIVER
+ */
+ for (i = 0; i < dp->vxnNumTxBufs; i++) {
+ dp->txRing[i].ownership = VMXNET2_OWNERSHIP_DRIVER;
+ dp->txRingBuf[i].mblk = NULL;
+ dp->txRingBuf[i].dmaMem.buf = NULL;
+ dp->txRing[i].sg.sg[0].addrHi = 0;
+ }
+
+ dd->txDriverCur = dd->txDriverNext = 0;
+ dd->txStopped = FALSE;
+
+ return SOLVMXNET_SUCCESS;
+
+err:
+ for (i=0; i<dp->vxnNumRxBufs; i++) {
+ if (dp->rxRingBuffPtr[i]) {
+ Vxn_FreeRxBuf(dp->rxRingBuffPtr[i]);
+ dp->rxRingBuffPtr[i] = NULL;
+ }
+ }
+
+ Vxn_FreeRxBufPool(dp);
+ return SOLVMXNET_FAILURE;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeDmaMem --
+ * Free allocated dma memory
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeDmaMem(dma_buf_t *dma)
+{
+ ddi_dma_unbind_handle(dma->dmaHdl);
+ ddi_dma_mem_free(&dma->dataAccHdl);
+ ddi_dma_free_handle(&dma->dmaHdl);
+
+ dma->buf = NULL;
+ dma->phyBuf = (uint32_t)(uintptr_t)NULL;
+ dma->bufLen = 0;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocDmaMem --
+ * Allocate dma-able memory and fill passed in dma descriptor pointer
+ * if successful
+ *
+ * Results:
+ * SOLVMXNET_SUCCESS on success
+ * SOLVMXNET_FAILURE on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_AllocDmaMem(vxn_softc_t *dp, int size, int cansleep, dma_buf_t *dma)
+{
+ /*
+ * Allocate handle
+ */
+ if (ddi_dma_alloc_handle(dp->dip, &vxn_dma_attrs,
+ cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT,
+ NULL, &dma->dmaHdl) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: failed to allocate handle",
+ dp->drvName, dp->unit);
+ return SOLVMXNET_FAILURE;
+ }
+
+ /*
+ * Allocate memory
+ */
+ if (ddi_dma_mem_alloc(dma->dmaHdl, size, &vxn_buf_attrs, DDI_DMA_CONSISTENT,
+ cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT, NULL,
+ &dma->buf, &dma->bufLen, &dma->dataAccHdl)
+ != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: "
+ "ddi_dma_mem_alloc %d bytes failed",
+ dp->drvName, dp->unit, size);
+ ddi_dma_free_handle(&dma->dmaHdl);
+ return SOLVMXNET_FAILURE;
+ }
+
+ /*
+ * Mapin memory
+ */
+ if (ddi_dma_addr_bind_handle(dma->dmaHdl, NULL, dma->buf, dma->bufLen,
+ DDI_DMA_RDWR | DDI_DMA_STREAMING,
+ cansleep ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT,
+ NULL, &dma->cookie, &dma->cookieCount)
+ != DDI_DMA_MAPPED) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: failed to bind handle",
+ dp->drvName, dp->unit);
+ ddi_dma_mem_free(&dma->dataAccHdl);
+ ddi_dma_free_handle(&dma->dmaHdl);
+ return SOLVMXNET_FAILURE;
+ }
+
+ if (dma->cookieCount != 1) {
+ cmn_err(CE_WARN, "%s%d: Vxn_AllocDmaMem: too many DMA cookies",
+ dp->drvName, dp->unit);
+ Vxn_FreeDmaMem(dma);
+ return SOLVMXNET_FAILURE;
+ }
+
+ /*
+ * Save physical address (for easy use)
+ */
+ dma->phyBuf = dma->cookie.dmac_address;
+
+ return SOLVMXNET_SUCCESS;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_FreeDriverData --
+ * Free driver data structures and Tx Rx buffers
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static void
+Vxn_FreeDriverData(vxn_softc_t *dp)
+{
+ Vxn_FreeInitBuffers(dp);
+ Vxn_FreeDmaMem(&dp->driverDataDmaMem);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_AllocDriverData --
+ * Allocate driver data structures and Tx Rx buffers on init
+ *
+ * Results:
+ * SOLVMXNET_SUCCESS on success
+ * SOLVMXNET_FAILURE on failure
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_AllocDriverData(vxn_softc_t *dp)
+{
+ uint32_t r, driverDataSize;
+
+ /*
+ * Get configured receive buffers
+ */
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_NUM_RX_BUFFERS);
+ r = INL(dp, VMXNET_COMMAND_ADDR);
+ if (r == 0 || r > MAX_NUM_RECV_BUFFERS) {
+ r = DEFAULT_NUM_RECV_BUFFERS;
+ }
+ dp->vxnNumRxBufs = r;
+
+ /*
+ * Get configured transmit buffers
+ */
+ OUTL(dp, VMXNET_COMMAND_ADDR, VMXNET_CMD_GET_NUM_TX_BUFFERS);
+ r = INL(dp, VMXNET_COMMAND_ADDR);
+ if (r == 0 || r > MAX_NUM_XMIT_BUFFERS) {
+ r = DEFAULT_NUM_XMIT_BUFFERS;
+ }
+ dp->vxnNumTxBufs = r;
+
+ /*
+ * Calculate shared data size and allocate memory for it
+ */
+ driverDataSize =
+ sizeof(Vmxnet2_DriverData) +
+ /* numRecvBuffers + 1 for the dummy recvRing2 (used only by Windows) */
+ (dp->vxnNumRxBufs + 1) * sizeof(Vmxnet2_RxRingEntry) +
+ dp->vxnNumTxBufs * sizeof(Vmxnet2_TxRingEntry);
+
+ if (Vxn_AllocDmaMem(dp, driverDataSize, TRUE, &dp->driverDataDmaMem)
+ != SOLVMXNET_SUCCESS) {
+ return SOLVMXNET_FAILURE;
+ }
+
+ /*
+ * Clear memory (bzero isn't resolved by module loader for some reason)
+ */
+ ASSERT(dp->driverDataDmaMem.buf && dp->driverDataDmaMem.bufLen);
+ Vxn_Memset(dp->driverDataDmaMem.buf, 0, dp->driverDataDmaMem.bufLen);
+
+ dp->driverData = (Vmxnet2_DriverData *)dp->driverDataDmaMem.buf;
+ dp->driverDataPhy = (void *)(uintptr_t)dp->driverDataDmaMem.phyBuf;
+
+ /* So that the vmkernel can check it is compatible */
+ dp->driverData->magic = VMXNET2_MAGIC;
+ dp->driverData->length = driverDataSize;
+
+ /*
+ * Alloc rx/tx buffers, init ring, register with hardware etc.
+ */
+ if (Vxn_AllocInitBuffers(dp) != SOLVMXNET_SUCCESS) {
+ Vxn_FreeDmaMem(&dp->driverDataDmaMem);
+ return SOLVMXNET_FAILURE;
+ }
+
+ DPRINTF(3, (CE_CONT, "%s%d: numRxBufs=(%d*%"FMT64"d) numTxBufs=(%d*%"FMT64"d)"
+ " driverDataSize=%d driverDataPhy=0x%p\n",
+ dp->drvName, dp->unit,
+ dp->vxnNumRxBufs, (uint64_t)sizeof(Vmxnet2_RxRingEntry),
+ dp->vxnNumTxBufs, (uint64_t)sizeof(Vmxnet2_TxRingEntry),
+ driverDataSize, dp->driverDataPhy));
+
+ return SOLVMXNET_SUCCESS;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Attach --
+ * Probe and attach driver to stack
+ *
+ * Results:
+ * DDI_SUCCESS
+ * DDI_FAILURE
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int i, ret, len, unit;
+ const char *drvName;
+ ddi_acc_handle_t confHdl;
+ uint16_t vid, did;
+ uint8_t revid __unused;
+ struct pci_phys_spec *regs;
+ caddr_t vxnIOp;
+ ddi_acc_handle_t vxnIOHdl;
+ uint32_t vLow, vHigh;
+ gld_mac_info_t *macInfo;
+ vxn_softc_t *dp;
+ boolean_t morphed = FALSE;
+ uint_t regSpaceSize;
+ uint_t chip;
+ uint_t vxnIOSize;
+
+ if (cmd != DDI_ATTACH) {
+ return DDI_FAILURE;
+ }
+
+ unit = ddi_get_instance(dip);
+ drvName = ddi_driver_name(dip);
+
+ /*
+ * Check if chip is supported.
+ */
+ if (pci_config_setup(dip, &confHdl) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: pci_config_setup() failed", drvName, unit);
+ return DDI_FAILURE;
+ }
+
+ vid = pci_config_get16(confHdl, PCI_CONF_VENID);
+ did = pci_config_get16(confHdl, PCI_CONF_DEVID);
+ revid = pci_config_get8(confHdl, PCI_CONF_REVID);
+
+ if (vid == PCI_VENDOR_ID_VMWARE && did == PCI_DEVICE_ID_VMWARE_NET) {
+ /* Found vmxnet */
+ chip = VMXNET_CHIP;
+ }
+ else if (vid == PCI_VENDOR_ID_AMD && did == PCI_DEVICE_ID_AMD_VLANCE) {
+ /* Found vlance (maybe a vmxnet disguise) */
+ chip = LANCE_CHIP;
+ }
+ else {
+ /* Not Found */
+ DPRINTF(3, (CE_WARN, "%s: Vxn_Attach: wrong PCI venid/devid (0x%x, 0x%x)",
+ drvName, vid, did));
+ goto err;
+ }
+
+ DPRINTF(3, (CE_CONT, "%s%d: (vid: 0x%04x, did: 0x%04x, revid: 0x%02x)\n",
+ drvName, unit, vid, did, revid));
+
+ /*
+ * Get device properties
+ */
+ regs = NULL;
+ len = 0;
+ if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+ "reg", (caddr_t)&regs, &len) != DDI_PROP_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: failed to get reg property",
+ drvName, unit);
+ goto err;
+ }
+
+ ASSERT(regs != NULL && len > 0);
+
+ /*
+ * Search device properties for IO-space
+ */
+ for (i = 0; i <len / sizeof(struct pci_phys_spec); i++) {
+ if ((regs[i].pci_phys_hi & PCI_REG_ADDR_M) == PCI_ADDR_IO) {
+ regSpaceSize = regs[i].pci_size_low;
+ DPRINTF(5, (CE_CONT, "%s%d: Vxn_Attach: regSpaceSize=%d\n",
+ drvName, unit, regSpaceSize));
+ kmem_free(regs, len);
+ goto map_space_found;
+ }
+ }
+
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: failed to find IO space", drvName, unit);
+ kmem_free(regs, len);
+ goto err;
+
+map_space_found:
+
+ /*
+ * Ensure we can access registers through IO space.
+ */
+ ret = pci_config_get16(confHdl, PCI_CONF_COMM);
+ ret |= PCI_COMM_IO | PCI_COMM_ME;
+ pci_config_put16(confHdl, PCI_CONF_COMM, ret);
+
+ if (ddi_regs_map_setup(dip, i, (caddr_t *)&vxnIOp, 0, 0, &dev_attr,
+ &vxnIOHdl) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: ddi_regs_map_setup failed",
+ drvName, unit);
+ goto err;
+ }
+
+ if (chip == VMXNET_CHIP) {
+ vxnIOSize = VMXNET_CHIP_IO_RESV_SIZE;
+ }
+ else {
+ /*
+ * Since this is a vlance adapter we can only use it if
+ * its I/0 space is big enough for the adapter to be
+ * capable of morphing. This is the first requirement
+ * for this adapter to potentially be morphable. The
+ * layout of a morphable LANCE adapter is
+ *
+ * I/O space:
+ *
+ * |------------------|
+ * | LANCE IO PORTS |
+ * |------------------|
+ * | MORPH PORT |
+ * |------------------|
+ * | VMXNET IO PORTS |
+ * |------------------|
+ *
+ * VLance has 8 ports of size 4 bytes, the morph port is 4 bytes, and
+ * Vmxnet has 10 ports of size 4 bytes.
+ *
+ * We shift up the ioaddr with the size of the LANCE I/O space since
+ * we want to access the vmxnet ports. We also shift the ioaddr up by
+ * the MORPH_PORT_SIZE so other port access can be independent of
+ * whether we are Vmxnet or a morphed VLance. This means that when
+ * we want to access the MORPH port we need to subtract the size
+ * from ioaddr to get to it.
+ */
+ vxnIOp += LANCE_CHIP_IO_RESV_SIZE + MORPH_PORT_SIZE;
+ vxnIOSize = LANCE_CHIP_IO_RESV_SIZE + MORPH_PORT_SIZE +
+ VMXNET_CHIP_IO_RESV_SIZE;
+ }
+
+ /*
+ * Do not attempt to morph non-morphable AMD PCnet
+ */
+ if (vxnIOSize > regSpaceSize) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: "
+ "vlance device is not supported by this driver", drvName, unit);
+ goto err_free_regs_map;
+ }
+
+ /*
+ * Morph, if we found a vlance adapter
+ */
+ if (chip == LANCE_CHIP) {
+ uint16_t magic;
+
+ /* Read morph port to verify that we can morph the adapter */
+ magic = ddi_get16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE));
+ if (magic != LANCE_CHIP && magic != VMXNET_CHIP) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: Invalid magic, read: 0x%08X",
+ drvName, unit, magic);
+ goto err_free_regs_map;
+ }
+
+ /* Morph */
+ ddi_put16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE), VMXNET_CHIP);
+ morphed = TRUE;
+
+ /* Verify that we morphed correctly */
+ magic = ddi_get16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE));
+ if (magic != VMXNET_CHIP) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: Couldn't morph adapter."
+ " Invalid magic, read:: 0x%08X", drvName, unit, magic);
+ goto err_morph_back;
+ }
+ }
+
+ /*
+ * Check the version number of the device implementation
+ */
+ vLow = (uint32_t)ddi_get32(vxnIOHdl,
+ (uint32_t *)(vxnIOp+VMXNET_LOW_VERSION));
+ vHigh = (uint32_t)ddi_get32(vxnIOHdl,
+ (uint32_t *)(vxnIOp+VMXNET_HIGH_VERSION));
+
+ if ((vLow & 0xffff0000) != (VMXNET2_MAGIC & 0xffff0000) ||
+ ((VMXNET2_MAGIC < vLow) || (VMXNET2_MAGIC > vHigh))) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: driver version 0x%08X doesn't "
+ "match device 0x%08X:0x%08X",
+ drvName, unit, VMXNET2_MAGIC, vLow, vHigh);
+ goto err_version_mismatch;
+ }
+
+ /*
+ * Alloc soft state
+ */
+ macInfo = gld_mac_alloc(dip);
+ if (!macInfo) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: gld_mac_alloc failed",
+ drvName, unit);
+ goto err_gld_mac_alloc;
+ }
+
+ dp = (vxn_softc_t *) kmem_zalloc(sizeof(vxn_softc_t), KM_SLEEP);
+ ASSERT(dp);
+
+ /*
+ * Get interrupt cookie
+ */
+ if (ddi_get_iblock_cookie(dip, 0, &dp->iblockCookie) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Attach: ddi_get_iblock_cookie failed",
+ drvName, unit);
+ goto err_get_iblock_cookie;
+ }
+
+ strncpy(dp->drvName, drvName, SOLVMXNET_MAXNAME);
+ dp->unit = unit;
+ dp->dip = dip;
+ dp->macInfo = macInfo;
+ dp->confHdl = confHdl;
+ dp->vxnIOHdl = vxnIOHdl;
+ dp->vxnIOp = vxnIOp;
+ dp->morphed = morphed;
+ dp->nicActive = FALSE;
+ dp->txPending = 0;
+ dp->maxTxFrags = 1;
+
+ /*
+ * Initialize mutexes
+ */
+ mutex_init(&dp->intrlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie);
+ mutex_init(&dp->xmitlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie);
+ mutex_init(&dp->rxlistlock, NULL, MUTEX_DRIVER, (void *)dp->iblockCookie);
+
+ /*
+ * Allocate and initialize our private and shared data structures
+ */
+ if (Vxn_AllocDriverData(dp) != SOLVMXNET_SUCCESS) {
+ goto err_alloc_driverdata;
+ }
+
+ /*
+ * Read the MAC address from the device
+ */
+ for (i = 0; i < 6; i++) {
+ dp->devAddr.ether_addr_octet[i] =
+ (uint8_t)ddi_get8(vxnIOHdl, (uint8_t *)(vxnIOp + VMXNET_MAC_ADDR + i));
+ }
+ macInfo->gldm_vendor_addr = dp->devAddr.ether_addr_octet;
+ macInfo->gldm_broadcast_addr = etherbroadcastaddr.ether_addr_octet;
+
+ DPRINTF(3, (CE_CONT,
+ "MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dp->devAddr.ether_addr_octet[0],
+ dp->devAddr.ether_addr_octet[1],
+ dp->devAddr.ether_addr_octet[2],
+ dp->devAddr.ether_addr_octet[3],
+ dp->devAddr.ether_addr_octet[4],
+ dp->devAddr.ether_addr_octet[5]));
+
+ /*
+ * Configure GLD entry points
+ */
+ macInfo->gldm_devinfo = dip;
+ macInfo->gldm_private = (caddr_t)dp;
+ macInfo->gldm_cookie = dp->iblockCookie;
+ macInfo->gldm_reset = Vxn_Reset;
+ macInfo->gldm_start = Vxn_Start;
+ macInfo->gldm_stop = Vxn_Stop;
+ macInfo->gldm_set_mac_addr = Vxn_SetMacAddress;
+ macInfo->gldm_send = Vxn_Send;
+ macInfo->gldm_set_promiscuous = Vxn_SetPromiscuous;
+ macInfo->gldm_get_stats = Vxn_GetStats;
+ macInfo->gldm_ioctl = NULL;
+ macInfo->gldm_set_multicast= Vxn_SetMulticast;
+ macInfo->gldm_intr = Vxn_Interrupt;
+ macInfo->gldm_mctl = NULL;
+
+ macInfo->gldm_ident = (char *)ddi_driver_name(dip);
+ macInfo->gldm_type = DL_ETHER;
+ macInfo->gldm_minpkt = 0;
+ macInfo->gldm_maxpkt = ETHERMTU;
+ macInfo->gldm_addrlen = ETHERADDRL;
+ macInfo->gldm_saplen = -2;
+ macInfo->gldm_ppa = unit;
+
+ /*
+ * Register with GLD (Generic Lan Driver) framework
+ */
+ if (gld_register(dip,
+ (char *)ddi_driver_name(dip), macInfo) != DDI_SUCCESS) {
+ goto err_gld_register;
+ }
+
+ /*
+ * Add interrupt to system.
+ */
+ if (ddi_add_intr(dip, 0, NULL, NULL, gld_intr,
+ (caddr_t)macInfo) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s%d: ddi_add_intr failed", drvName, unit);
+ goto err_ddi_add_intr;
+ }
+
+ /*
+ * Add to list of interfaces.
+ */
+ mutex_enter(&vxnListLock);
+ dp->next = &vxnList;
+ dp->prev = vxnList.prev;
+ vxnList.prev->next = dp;
+ vxnList.prev = dp;
+ mutex_exit(&vxnListLock);
+
+ /*
+ * Success
+ */
+ return DDI_SUCCESS;
+
+err_ddi_add_intr:
+ gld_unregister(macInfo);
+
+err_gld_register:
+ Vxn_FreeDriverData(dp);
+
+err_alloc_driverdata:
+ mutex_destroy(&dp->intrlock);
+ mutex_destroy(&dp->xmitlock);
+
+err_get_iblock_cookie:
+ kmem_free(dp, sizeof(*dp));
+ gld_mac_free(macInfo);
+
+err_gld_mac_alloc:
+err_version_mismatch:
+err_morph_back:
+ if (morphed) {
+ ddi_put16(vxnIOHdl, (uint16_t *)(vxnIOp - MORPH_PORT_SIZE), LANCE_CHIP);
+ }
+
+err_free_regs_map:
+ ddi_regs_map_free(&vxnIOHdl);
+
+err:
+ pci_config_teardown(&confHdl);
+ return DDI_FAILURE;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Vxn_Detach --
+ * Called on module unload
+ *
+ * Results:
+ * DDI_SUCCESS
+ * DDI_FAILURE
+ *
+ * Side effects:
+ * None
+ *-----------------------------------------------------------------------------
+ */
+static int
+Vxn_Detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ gld_mac_info_t *macInfo;
+ vxn_softc_t *dp;
+
+ macInfo = (gld_mac_info_t *)ddi_get_driver_private(dip);
+ dp = (vxn_softc_t *)macInfo->gldm_private;
+
+ if (cmd == DDI_DETACH) {
+ /*
+ * Tear down interrupt
+ */
+ ddi_remove_intr(dip, 0, macInfo->gldm_cookie);
+ gld_unregister(macInfo);
+
+ /*
+ * Quiesce hardware
+ */
+ Vxn_Stop(macInfo);
+
+ /*
+ * Free driver-data, tx/rx buffers etc
+ */
+ Vxn_FreeDriverData(dp);
+
+ /*
+ * Destroy locks
+ */
+ mutex_destroy(&dp->intrlock);
+ mutex_destroy(&dp->xmitlock);
+
+ /*
+ * Unmorph
+ */
+ if (dp->morphed) {
+ uint16_t magic;
+
+ /* Verify that we had morphed earlier */
+ magic = ddi_get16(dp->vxnIOHdl,
+ (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE));
+ if (magic != VMXNET_CHIP) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Detach: Adapter not morphed"
+ " magic=0x%08X", dp->drvName, dp->unit, magic);
+ }
+ else {
+ /* Unmorph */
+ ddi_put16(dp->vxnIOHdl,
+ (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE), LANCE_CHIP);
+
+ /* Verify */
+ magic = ddi_get16(dp->vxnIOHdl,
+ (uint16_t *)(dp->vxnIOp - MORPH_PORT_SIZE));
+ if (magic != LANCE_CHIP) {
+ cmn_err(CE_WARN, "%s%d: Vxn_Detach: Unable to unmorph adapter"
+ " magic=0x%08X", dp->drvName, dp->unit, magic);
+ }
+ }
+ }
+
+ /*
+ * Release resister mappings
+ */
+ ddi_regs_map_free(&dp->vxnIOHdl);
+ pci_config_teardown(&dp->confHdl);
+
+ /*
+ * Remove from list of interfaces.
+ */
+ mutex_enter(&vxnListLock);
+ ASSERT(dp != &vxnList);
+ dp->prev->next = dp->next;
+ dp->next->prev = dp->prev;
+ mutex_exit(&vxnListLock);
+
+ /*
+ * Release memory
+ */
+ kmem_free(dp, sizeof(*dp));
+ gld_mac_free(macInfo);
+
+ return DDI_SUCCESS;
+ }
+ else {
+ return DDI_FAILURE;
+ }
+}
+
+static struct module_info vxnminfo = {
+ 0, /* mi_idnum */
+ "vmxnet", /* mi_idname */
+ 0, /* mi_minpsz */
+ ETHERMTU, /* mi_maxpsz */
+ QHIWATER, /* mi_hiwat */
+ 1, /* mi_lowat */
+};
+
+static struct qinit vxnrinit = {
+ NULL, /* qi_putp */
+ gld_rsrv, /* qi_srvp */
+ gld_open, /* qi_qopen */
+ gld_close, /* qi_qclose */
+ NULL, /* qi_qadmin */
+ &vxnminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct qinit vxnwinit = {
+ gld_wput, /* qi_putp */
+ gld_wsrv, /* qi_srvp */
+ NULL, /* qi_qopen */
+ NULL, /* qi_qclose */
+ NULL, /* qi_qadmin */
+ &vxnminfo, /* qi_minfo */
+ NULL /* qi_mstat */
+};
+
+static struct streamtab vxn_info = {
+ &vxnrinit, /* st_rdinit */
+ &vxnwinit, /* st_wrinit */
+ NULL, /* st_muxrinit */
+ NULL /* st_muxwrinit */
+};
+
+static struct cb_ops cb_vxn_ops = {
+ nulldev, /* cb_open */
+ nulldev, /* cb_close */
+ nodev, /* cb_strategy */
+ nodev, /* cb_print */
+ nodev, /* cb_dump */
+ nodev, /* cb_read */
+ nodev, /* cb_write */
+ nodev, /* cb_ioctl */
+ nodev, /* cb_devmap */
+ nodev, /* cb_mmap */
+ nodev, /* cb_segmap */
+ nochpoll, /* cb_chpoll */
+ ddi_prop_op, /* cb_prop_op */
+ &vxn_info, /* cb_stream */
+ D_NEW|D_MP /* cb_flag */
+};
+
+static struct dev_ops vxn_ops = {
+ DEVO_REV, /* devo_rev */
+ 0, /* devo_refcnt */
+ gld_getinfo, /* devo_getinfo */
+ nulldev, /* devo_identify */
+ nulldev, /* devo_probe */
+ Vxn_Attach, /* devo_attach */
+ Vxn_Detach, /* devo_detach */
+ nodev, /* devo_reset */
+ &cb_vxn_ops, /* devo_cb_ops */
+ NULL, /* devo_bus_ops */
+ ddi_power /* devo_power */
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ ident,
+ &vxn_ops,
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, {&modldrv, NULL,}
+};
+
+
+/*
+ * Module load entry point
+ */
+int
+_init(void)
+{
+ int err;
+
+ DPRINTF(5, (CE_CONT, "vxn: _init:\n"));
+ /* Initialize interface list */
+ vxnList.next = vxnList.prev = &vxnList;
+ mutex_init(&vxnListLock, NULL, MUTEX_DRIVER, NULL);
+ if ((err = mod_install(&modlinkage)) != 0) {
+ mutex_destroy(&vxnListLock);
+ }
+ return err;
+}
+
+/*
+ * Module unload entry point
+ */
+int
+_fini(void)
+{
+ int err;
+
+ DPRINTF(5, (CE_CONT, "vxn: _fini:\n"));
+ if ((err = mod_remove(&modlinkage)) == 0) {
+ mutex_destroy(&vxnListLock);
+ }
+ return err;
+}
+
+/*
+ * Module info entry point
+ */
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
diff --git a/usr/src/uts/intel/io/vmxnet/vmxnet.conf b/usr/src/uts/intel/io/vmxnet/vmxnet.conf
new file mode 100644
index 0000000000..eb3b160412
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vmxnet.conf
@@ -0,0 +1,24 @@
+#
+# 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.
+# Use is subject to license terms.
+#
diff --git a/usr/src/uts/intel/io/vmxnet/vmxnet2_def.h b/usr/src/uts/intel/io/vmxnet/vmxnet2_def.h
new file mode 100644
index 0000000000..5ea437df72
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vmxnet2_def.h
@@ -0,0 +1,436 @@
+/*********************************************************
+ * Copyright (C) 2004 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+#ifndef _VMXNET2_DEF_H_
+#define _VMXNET2_DEF_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+
+#include "net_sg.h"
+#include "vmxnet_def.h"
+
+
+/*
+ * Magic number that identifies this version of the vmxnet protocol.
+ */
+#define VMXNET2_MAGIC 0xbabe864f
+
+/* size of the rx ring */
+#define VMXNET2_MAX_NUM_RX_BUFFERS 128
+#define VMXNET2_DEFAULT_NUM_RX_BUFFERS 100
+
+
+/* size of the rx ring when enhanced vmxnet is used */
+#define ENHANCED_VMXNET2_MAX_NUM_RX_BUFFERS 512
+#define ENHANCED_VMXNET2_DEFAULT_NUM_RX_BUFFERS 150
+
+/* size of the 2nd rx ring */
+#define VMXNET2_MAX_NUM_RX_BUFFERS2 2048
+#define VMXNET2_DEFAULT_NUM_RX_BUFFERS2 512
+
+/* size of the tx ring */
+#define VMXNET2_MAX_NUM_TX_BUFFERS 128
+#define VMXNET2_DEFAULT_NUM_TX_BUFFERS 100
+
+/* size of the tx ring when tso/jf is used */
+#define VMXNET2_MAX_NUM_TX_BUFFERS_TSO 512
+#define VMXNET2_DEFAULT_NUM_TX_BUFFERS_TSO 256
+
+enum {
+ VMXNET2_OWNERSHIP_DRIVER,
+ VMXNET2_OWNERSHIP_DRIVER_PENDING,
+ VMXNET2_OWNERSHIP_NIC,
+ VMXNET2_OWNERSHIP_NIC_PENDING,
+ VMXNET2_OWNERSHIP_NIC_FRAG,
+ VMXNET2_OWNERSHIP_DRIVER_FRAG,
+};
+
+#define VMXNET2_SG_DEFAULT_LENGTH 6
+
+typedef struct Vmxnet2_SG_Array {
+ uint16 addrType;
+ uint16 length;
+ NetSG_Elem sg[VMXNET2_SG_DEFAULT_LENGTH];
+} Vmxnet2_SG_Array;
+
+typedef struct Vmxnet2_RxRingEntry {
+ uint64 paddr; /* Physical address of the packet data. */
+ uint32 bufferLength; /* The length of the data at paddr. */
+ uint32 actualLength; /* The actual length of the received data. */
+ uint16 ownership; /* Who owns the packet. */
+ uint16 flags; /* Flags as defined below. */
+ uint32 index; /*
+ * Currently:
+ *
+ * This is being used as an packet index to
+ * rx buffers.
+ *
+ * Originally:
+ *
+ * was void* driverData ("Driver specific data.")
+ * which was used for sk_buf**s in Linux and
+ * VmxnetRxBuff*s in Windows. It could not be
+ * here because the structure needs to be the
+ * same size between architectures, and it was
+ * not used on the device side, anyway. Look
+ * for its replacement in
+ * Vmxnet_Private.rxRingBuffPtr on Linux and
+ * VmxnetAdapter.rxRingBuffPtr on Windows.
+ */
+} Vmxnet2_RxRingEntry;
+
+/*
+ * Vmxnet2_RxRingEntry flags:
+ *
+ * VMXNET2_RX_HW_XSUM_OK The hardware verified the TCP/UDP checksum.
+ * VMXNET2_RX_WITH_FRAG More data is in the 2nd ring
+ * VMXNET2_RX_FRAG_EOP This is the last frag, the only valid flag for
+ * 2nd ring entry
+ *
+ */
+#define VMXNET2_RX_HW_XSUM_OK 0x01
+#define VMXNET2_RX_WITH_FRAG 0x02
+#define VMXNET2_RX_FRAG_EOP 0x04
+
+typedef struct Vmxnet2_TxRingEntry {
+ uint16 flags; /* Flags as defined below. */
+ uint16 ownership; /* Who owns this packet. */
+ uint32 extra; /*
+ * was void* driverData ("Driver specific data.")
+ * which was used for sk_buf*s in Linux and
+ * VmxnetTxInfo*s in Windows. It could not be
+ * here because the structure needs to be the
+ * same size between architectures, and it was
+ * not used on the device side, anyway. Look
+ * for its replacement in
+ * Vmxnet_Private.txRingBuffPtr on Linux and
+ * VmxnetAdapter.txRingBuffPtr on Windows.
+ */
+ uint32 tsoMss; /* TSO pkt MSS */
+ Vmxnet2_SG_Array sg; /* Packet data. */
+} Vmxnet2_TxRingEntry;
+
+/*
+ * Vmxnet2_TxRingEntry flags:
+ *
+ * VMXNET2_TX_CAN_KEEP The implementation can return the tx ring entry
+ * to the driver when it is ready as opposed to
+ * before the transmit call from the driver completes.
+ * VMXNET2_TX_RING_LOW The driver's transmit ring buffer is low on free
+ * slots.
+ * VMXNET2_TX_HW_XSUM The hardware should perform the TCP/UDP checksum
+ * VMXNET2_TX_TSO The hardware should do TCP segmentation.
+ * VMXNET2_TX_PINNED_BUFFER The driver used one of the preallocated vmkernel
+ * buffers *and* it has been pinned with Net_PinTxBuffers.
+ * VMXNET2_TX_MORE This is *not* the last tx entry for the pkt.
+ * All flags except VMXNET2_TX_MORE are ignored
+ * for the subsequent tx entries.
+ */
+#define VMXNET2_TX_CAN_KEEP 0x0001
+#define VMXNET2_TX_RING_LOW 0x0002
+#define VMXNET2_TX_HW_XSUM 0x0004
+#define VMXNET2_TX_TSO 0x0008
+#define VMXNET2_TX_PINNED_BUFFER 0x0010
+#define VMXNET2_TX_MORE 0x0020
+
+/*
+ * Structure used by implementations. This structure allows the inline
+ * functions below to be used.
+ */
+typedef struct Vmxnet2_RxRingInfo {
+ Vmxnet2_RxRingEntry *base; /* starting addr of the ring */
+ uint32 nicNext; /* next entry to use in the ring */
+ uint32 ringLength; /* # of entries in the ring */
+ PA startPA; /* PA of the starting addr of the ring */
+#ifdef VMX86_DEBUG
+ const char *name;
+#endif
+} Vmxnet2_RxRingInfo;
+
+typedef struct Vmxnet2_TxRingInfo {
+ Vmxnet2_TxRingEntry *base; /* starting addr of the ring */
+ uint32 nicNext; /* next entry to use in the ring */
+ uint32 ringLength; /* # of entries in the ring */
+ PA startPA; /* PA of the starting addr of the ring */
+#ifdef VMX86_DEBUG
+ const char *name;
+#endif
+} Vmxnet2_TxRingInfo;
+
+typedef struct Vmxnet2_ImplData {
+ Vmxnet2_RxRingInfo rxRing;
+ Vmxnet2_RxRingInfo rxRing2;
+ Vmxnet2_TxRingInfo txRing;
+
+ struct PhysMem_Token *ddPhysMemToken;
+} Vmxnet2_ImplData;
+
+/*
+ * Used internally for performance studies. By default this will be off so there
+ * should be no compatibilty or other interferences.
+ */
+
+/* #define ENABLE_VMXNET2_PROFILING */
+
+
+#ifdef ENABLE_VMXNET2_PROFILING
+typedef struct Vmxnet2_VmmStats {
+ uint64 vIntTSC; /* the time that virtual int was posted */
+ uint64 actionsCount; /* Number of actions received */
+ uint64 numWasteActions; /* Number of non-productive actions */
+} Vmxnet2_VmmStats;
+#endif
+
+typedef struct Vmxnet2_DriverStats {
+ uint32 transmits; /* # of times that the drivers transmit function */
+ /* is called. The driver could transmit more */
+ /* than one packet per call. */
+ uint32 pktsTransmitted; /* # of packets transmitted. */
+ uint32 noCopyTransmits; /* # of packets that are transmitted without */
+ /* copying any data. */
+ uint32 copyTransmits; /* # of packets that are transmittted by copying */
+ /* the data into a buffer. */
+ uint32 maxTxsPending; /* Max # of transmits outstanding. */
+ uint32 txStopped; /* # of times that transmits got stopped because */
+ /* the tx ring was full. */
+ uint32 txRingOverflow; /* # of times that transmits got deferred bc */
+ /* the tx ring was full. This must be >= */
+ /* txStopped since there will be one */
+ /* txStopped when the ring fills up and then */
+ /* one txsRingOverflow for each packet that */
+ /* that gets deferred until there is space. */
+ uint32 interrupts; /* # of times interrupted. */
+ uint32 pktsReceived; /* # of packets received. */
+ uint32 rxBuffersLow; /* # of times that the driver was low on */
+ /* receive buffers. */
+#ifdef ENABLE_VMXNET2_PROFILING
+ Vmxnet2_VmmStats vmmStats; /* vmm related stats for perf study */
+#endif
+} Vmxnet2_DriverStats;
+
+/*
+ * Shared data structure between the vm, the vmm, and the vmkernel.
+ * This structure was originally arranged to try to group common data
+ * on 32-byte cache lines, but bit rot and the fact that we no longer
+ * run on many CPUs with that cacheline size killed that optimization.
+ * vmxnet3 should target 128 byte sizes and alignments to optimize for
+ * the 64 byte cacheline pairs on P4.
+ */
+typedef struct Vmxnet2_DriverData {
+ /*
+ * Magic must be first.
+ */
+ Vmxnet_DDMagic magic;
+
+ /*
+ * Receive fields.
+ */
+ uint32 rxRingLength; /* Length of the receive ring. */
+ uint32 rxDriverNext; /* Index of the next packet that will */
+ /* be filled in by the impl */
+
+ uint32 rxRingLength2; /* Length of the 2nd receive ring. */
+ uint32 rxDriverNext2; /* Index of the next packet that will */
+ /* be filled in by the impl */
+
+ uint32 notUsed1; /* was "irq" */
+
+ /*
+ * Interface flags and multicast filter.
+ */
+ uint32 ifflags;
+ uint32 LADRF[VMXNET_MAX_LADRF];
+
+ /*
+ * Transmit fields
+ */
+ uint32 txDontClusterSize; /* All packets <= this will be transmitted */
+ /* immediately, regardless of clustering */
+ /* settings [was fill[1]] */
+ uint32 txRingLength; /* Length of the transmit ring. */
+ uint32 txDriverCur; /* Index of the next packet to be */
+ /* returned by the implementation.*/
+ uint32 txDriverNext; /* Index of the entry in the ring */
+ /* buffer to use for the next packet.*/
+ uint32 txStopped; /* The driver has stopped transmitting */
+ /* because its ring buffer is full.*/
+ uint32 txClusterLength; /* Maximum number of packets to */
+ /* put in the ring buffer before */
+ /* asking the implementation to */
+ /* transmit the packets in the buffer.*/
+ uint32 txNumDeferred; /* Number of packets that have been */
+ /* queued in the ring buffer since */
+ /* the last time the implementation */
+ /* was asked to transmit. */
+ uint32 notUsed3; /* This field is deprecated but still used */
+ /* as minXmitPhysLength on the escher branch. */
+ /* It cannot be used for other purposes */
+ /* until escher vms no longer are allowed */
+ /* to install this driver. */
+
+ uint32 totalRxBuffers; /* used by esx for max rx buffers */
+ uint64 rxBufferPhysStart; /* used by esx for pinng rx buffers */
+ /*
+ * Extra fields for future expansion.
+ */
+ uint32 extra[2];
+
+ uint16 maxFrags; /* # of frags the driver can handle */
+ uint16 featureCtl; /* for driver to enable some feature */
+
+ /*
+ * The following fields are used to save the nicNext indexes part
+ * of implData in the vmkernel when disconnecting the adapter, we
+ * need them when we reconnect. This mechanism is used for
+ * checkpointing as well.
+ */
+ uint32 savedRxNICNext;
+ uint32 savedRxNICNext2;
+ uint32 savedTxNICNext;
+
+ /*
+ * Fields used during initialization or debugging.
+ */
+ uint32 length;
+ uint32 rxRingOffset;
+ uint32 rxRingOffset2;
+ uint32 txRingOffset;
+ uint32 debugLevel;
+ uint32 txBufferPhysStart;
+ uint32 txBufferPhysLength;
+ uint32 txPktMaxSize;
+
+ /*
+ * Driver statistics.
+ */
+ Vmxnet2_DriverStats stats;
+} Vmxnet2_DriverData;
+
+/*
+ * Shared between VMM and Vmkernel part of vmxnet2 to optimize action posting
+ * VMM writes 1 (don't post) or 0 (okay to post) and vmk reads this.
+ */
+typedef struct VmxnetVMKShared {
+ uint32 dontPostActions;
+} VmxnetVMKShared;
+
+#if defined VMX86_VMX || defined VMKERNEL
+
+/*
+ * Inline functions used to assist the implementation of the vmxnet interface.
+ */
+
+/*
+ * Get the next empty packet out of the receive ring and move to
+ * the next packet.
+ */
+static INLINE Vmxnet2_RxRingEntry *
+Vmxnet2_GetNextRx(Vmxnet2_RxRingInfo *ri, uint16 ownership)
+{
+ Vmxnet2_RxRingEntry *rre = ri->base + ri->nicNext;
+ if (rre->ownership == ownership) {
+ VMXNET_INC(ri->nicNext, ri->ringLength);
+ } else {
+ rre = NULL;
+ }
+
+ return rre;
+}
+
+/*
+ * Return ownership of a packet in the receive ring to the driver.
+ */
+static INLINE void
+Vmxnet2_PutRx(Vmxnet2_RxRingEntry *rre, uint32 pktLength, uint16 ownership)
+{
+ rre->actualLength = pktLength;
+ COMPILER_MEM_BARRIER();
+ rre->ownership = ownership;
+}
+
+/*
+ * Get the next pending packet out of the transmit ring.
+ */
+static INLINE Vmxnet2_TxRingEntry *
+Vmxnet2_GetNextTx(Vmxnet2_TxRingInfo *ri)
+{
+ Vmxnet2_TxRingEntry *txre = ri->base + ri->nicNext;
+ if (txre->ownership == VMXNET2_OWNERSHIP_NIC) {
+ return txre;
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Move to the next entry in the transmit ring.
+ */
+static INLINE unsigned int
+Vmxnet2_IncNextTx(Vmxnet2_TxRingInfo *ri)
+{
+ unsigned int prev = ri->nicNext;
+ Vmxnet2_TxRingEntry *txre = ri->base + ri->nicNext;
+
+ txre->ownership = VMXNET2_OWNERSHIP_NIC_PENDING;
+
+ VMXNET_INC(ri->nicNext, ri->ringLength);
+ return prev;
+}
+
+/*
+ * Get the indicated entry from transmit ring.
+ */
+static INLINE Vmxnet2_TxRingEntry *
+Vmxnet2_GetTxEntry(Vmxnet2_TxRingInfo *ri, unsigned int idx)
+{
+ return ri->base + idx;
+}
+
+/*
+ * Get the indicated entry from the given rx ring
+ */
+static INLINE Vmxnet2_RxRingEntry *
+Vmxnet2_GetRxEntry(Vmxnet2_RxRingInfo *ri, unsigned int idx)
+{
+ return ri->base + idx;
+}
+
+#endif /* defined VMX86_VMX || defined VMKERNEL */
+
+#endif
+
diff --git a/usr/src/uts/intel/io/vmxnet/vmxnet_def.h b/usr/src/uts/intel/io/vmxnet/vmxnet_def.h
new file mode 100644
index 0000000000..703466c995
--- /dev/null
+++ b/usr/src/uts/intel/io/vmxnet/vmxnet_def.h
@@ -0,0 +1,184 @@
+/*********************************************************
+ * Copyright (C) 1999 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ *********************************************************/
+
+/*********************************************************
+ * The contents of this file are subject to the terms of the Common
+ * Development and Distribution License (the "License") version 1.0
+ * and no later version. You may not use this file except in
+ * compliance with the License.
+ *
+ * You can obtain a copy of the License at
+ * http://www.opensource.org/licenses/cddl1.php
+ *
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ *********************************************************/
+
+#ifndef _VMXNET_DEF_H_
+#define _VMXNET_DEF_H_
+
+#define INCLUDE_ALLOW_USERLEVEL
+
+#define INCLUDE_ALLOW_MODULE
+#define INCLUDE_ALLOW_VMK_MODULE
+#define INCLUDE_ALLOW_VMKERNEL
+#define INCLUDE_ALLOW_DISTRIBUTE
+#include "includeCheck.h"
+
+#include "net_sg.h"
+#include "vmnet_def.h"
+
+
+/*
+ * Vmxnet I/O ports, used by both the vmxnet driver and
+ * the device emulation code.
+ */
+
+#define VMXNET_INIT_ADDR 0x00
+#define VMXNET_INIT_LENGTH 0x04
+#define VMXNET_TX_ADDR 0x08
+#define VMXNET_COMMAND_ADDR 0x0c
+#define VMXNET_MAC_ADDR 0x10
+#define VMXNET_LOW_VERSION 0x18
+#define VMXNET_HIGH_VERSION 0x1c
+#define VMXNET_STATUS_ADDR 0x20
+#define VMXNET_TOE_INIT_ADDR 0x24
+#define VMXNET_APROM_ADDR 0x28
+#define VMXNET_INT_ENABLE_ADDR 0x30
+#define VMXNET_WAKE_PKT_PATTERNS 0x34
+
+/*
+ * Vmxnet command register values.
+ */
+#define VMXNET_CMD_INTR_ACK 0x0001
+#define VMXNET_CMD_UPDATE_LADRF 0x0002
+#define VMXNET_CMD_UPDATE_IFF 0x0004
+#define VMXNET_CMD_UNUSED 1 0x0008
+#define VMXNET_CMD_UNUSED_2 0x0010
+#define VMXNET_CMD_INTR_DISABLE 0x0020
+#define VMXNET_CMD_INTR_ENABLE 0x0040
+#define VMXNET_CMD_UNUSED_3 0x0080
+#define VMXNET_CMD_CHECK_TX_DONE 0x0100
+#define VMXNET_CMD_GET_NUM_RX_BUFFERS 0x0200
+#define VMXNET_CMD_GET_NUM_TX_BUFFERS 0x0400
+#define VMXNET_CMD_PIN_TX_BUFFERS 0x0800
+#define VMXNET_CMD_GET_CAPABILITIES 0x1000
+#define VMXNET_CMD_GET_FEATURES 0x2000
+#define VMXNET_CMD_SET_POWER_FULL 0x4000
+#define VMXNET_CMD_SET_POWER_LOW 0x8000
+
+/*
+ * Vmxnet status register values.
+ */
+#define VMXNET_STATUS_CONNECTED 0x0001
+#define VMXNET_STATUS_ENABLED 0x0002
+#define VMXNET_STATUS_TX_PINNED 0x0004
+
+/*
+ * Values for the interface flags.
+ */
+#define VMXNET_IFF_PROMISC 0x01
+#define VMXNET_IFF_BROADCAST 0x02
+#define VMXNET_IFF_MULTICAST 0x04
+#define VMXNET_IFF_DIRECTED 0x08
+
+/*
+ * Length of the multicast address filter.
+ */
+#define VMXNET_MAX_LADRF 2
+
+/*
+ * Size of Vmxnet APROM.
+ */
+#define VMXNET_APROM_SIZE 6
+
+/*
+ * An invalid ring index.
+ */
+#define VMXNET_INVALID_RING_INDEX (-1)
+
+/*
+ * Features that are implemented by the driver. These are driver
+ * specific so not all features will be listed here. In addition not all
+ * drivers have to pay attention to these feature flags.
+ *
+ * VMXNET_FEATURE_ZERO_COPY_TX The driver won't do any copies as long as
+ * the packet length is >
+ * Vmxnet_DriverData.minTxPhysLength.
+ *
+ * VMXNET_FEATURE_TSO The driver will use the TSO capabilities
+ * of the underlying hardware if available
+ * and enabled.
+ *
+ * VMXNET_FEATURE_JUMBO_FRAME The driver can send/rcv jumbo frame
+ *
+ * VMXNET_FEATURE_LPD The backend can deliver large pkts
+ */
+#define VMXNET_FEATURE_ZERO_COPY_TX 0x01
+#define VMXNET_FEATURE_TSO 0x02
+#define VMXNET_FEATURE_JUMBO_FRAME 0x04
+#define VMXNET_FEATURE_LPD 0x08
+
+/*
+ * Define the set of capabilities required by each feature above
+ */
+#define VMXNET_FEATURE_ZERO_COPY_TX_CAPS VMXNET_CAP_SG
+#define VMXNET_FEATURE_TSO_CAPS VMXNET_CAP_TSO
+#define VMXNET_HIGHEST_FEATURE_BIT VMXNET_FEATURE_TSO
+
+#define VMXNET_INC(val, max) \
+ val++; \
+ if (UNLIKELY(val == max)) { \
+ val = 0; \
+ }
+
+/*
+ * code that just wants to switch on the different versions of the
+ * guest<->implementation protocol can cast driver data to this.
+ */
+typedef uint32 Vmxnet_DDMagic;
+
+/*
+ * Wake packet pattern commands sent through VMXNET_WAKE_PKT_PATTERNS port
+ */
+
+#define VMXNET_PM_OPCODE_START 3 /* args: cnt of wake packet patterns */
+#define VMXNET_PM_OPCODE_LEN 2 /* args: index of wake packet pattern */
+ /* number of pattern byte values */
+#define VMXNET_PM_OPCODE_DATA 1 /* args: index of wake packet pattern */
+ /* offset in pattern byte values list */
+ /* packet byte offset */
+ /* packet byte value */
+#define VMXNET_PM_OPCODE_END 0 /* args: <none> */
+
+typedef union Vmxnet_WakePktCmd {
+ uint32 pktData : 32;
+ struct {
+ unsigned cmd : 2; /* wake packet pattern cmd [from list above] */
+ unsigned cnt : 3; /* cnt wk pkt pttrns 1..MAX_NUM_FILTER_PTTRNS */
+ unsigned ind : 3; /* ind wk pkt pttrn 0..MAX_NUM_FILTER_PTTRNS-1 */
+ unsigned lenOff : 8; /* num pttrn byte vals 1..MAX_PKT_FILTER_SIZE */
+ /* OR offset in pattern byte values list */
+ /* 0..MAX_PKT_FILTER_SIZE-1 */
+ unsigned byteOff : 8; /* pkt byte offset 0..MAX_PKT_FILTER_SIZE-1 */
+ unsigned byteVal : 8; /* packet byte value 0..255 */
+ } pktPttrn;
+} Vmxnet_WakePktCmd;
+
+#endif /* _VMXNET_DEF_H_ */
diff --git a/usr/src/uts/intel/ipf/Makefile b/usr/src/uts/intel/ipf/Makefile
index 4dde1e0034..d5dd00155b 100644
--- a/usr/src/uts/intel/ipf/Makefile
+++ b/usr/src/uts/intel/ipf/Makefile
@@ -21,6 +21,7 @@
#
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2018 Joyent, Inc.
#
# Copyright (c) 2018, Joyent, Inc.
@@ -50,7 +51,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
CPPFLAGS += -DIPFILTER_LKM -DIPFILTER_LOG -DIPFILTER_LOOKUP -DUSE_INET6
CPPFLAGS += -DSUNDDI -DSOLARIS2=$(RELEASE_MINOR) -DIRE_ILL_CN
-LDFLAGS += -Ndrv/ip -Nmisc/md5 -Nmisc/neti -Nmisc/hook -Nmisc/kcf
+LDFLAGS += -Ndrv/ip -Nmisc/md5 -Nmisc/neti -Nmisc/hook -Nmisc/kcf -Ndrv/vnd
LDFLAGS += -Nmisc/mac
INC_PATH += -I$(UTSBASE)/common/inet/ipf
diff --git a/usr/src/uts/intel/ipf/ipf.global-objs.debug64 b/usr/src/uts/intel/ipf/ipf.global-objs.debug64
index 846011b4c5..ea5510a78d 100644
--- a/usr/src/uts/intel/ipf/ipf.global-objs.debug64
+++ b/usr/src/uts/intel/ipf/ipf.global-objs.debug64
@@ -22,9 +22,21 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright 2018 Joyent, Inc. All rights reserved
+# Copyright 2019 Joyent, Inc.
#
+cfw_evdrops
+cfw_evreports
+cfw_ring
+cfw_ringcv
+cfw_ringend
+cfw_ringfull
+cfw_ringlock
+cfw_ringmask
+cfw_ringsize
+cfw_ringstart
+cfw_timeout_tries
+cfw_timeout_wait
fr_availfuncs
fr_features
fr_objbytes
@@ -43,6 +55,10 @@ hook4_nicevents
hook4_nicevents_gz
hook4_out
hook4_out_gz
+hook4_vnd_in
+hook4_vnd_in_gz
+hook4_vnd_out
+hook4_vnd_out_gz
hook6_in
hook6_in_gz
hook6_loop_in
@@ -53,6 +69,10 @@ hook6_nicevents
hook6_nicevents_gz
hook6_out
hook6_out_gz
+hook6_vnd_in
+hook6_vnd_in_gz
+hook6_vnd_out
+hook6_vnd_out_gz
icmpreplytype4
icmpreplytype6
icmptoicmp6types
@@ -60,6 +80,7 @@ icmptoicmp6unreach
idletime_tab
ip6exthdr
ipf_cb_ops
+ipf_cfwlog_enabled
ipf_dev_info
ipf_devfiles
ipf_eth_bcast_addr
diff --git a/usr/src/uts/intel/iptun/Makefile b/usr/src/uts/intel/iptun/Makefile
index 5ef3d91df1..3fde3a343e 100644
--- a/usr/src/uts/intel/iptun/Makefile
+++ b/usr/src/uts/intel/iptun/Makefile
@@ -52,7 +52,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -Ndrv/dld -Nmisc/dls -Nmisc/mac -Ndrv/ip
-INC_PATH += -I$(UTSBASE)/common/io/bpf
CERRWARN += -_gcc=-Wno-unused-label
diff --git a/usr/src/uts/intel/lx_brand/Makefile b/usr/src/uts/intel/lx_brand/Makefile
new file mode 100644
index 0000000000..a1c4027afa
--- /dev/null
+++ b/usr/src/uts/intel/lx_brand/Makefile
@@ -0,0 +1,92 @@
+#
+# 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 2019 Joyent, Inc.
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Path to where brand common sources live
+#
+LX_CMN = $(SRC)/common/brand/lx
+
+#
+# Define the module and object file sets.
+#
+MODULE = lx_brand
+OBJECTS = $(LX_BRAND_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_BRAND_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx -I$(LX_CMN) -I$(SRC)/common
+INC_PATH += -I$(UTSBASE)/common/inet/sockmods -I$(UTSBASE)/common/io/bpf
+INC_PATH += -I$(UTSBASE)/common/fs/sockfs
+INC_PATH += -I$(UTSBASE)/common/fs/zfs
+AS_INC_PATH += -I$(UTSBASE)/i86pc/genassym/$(OBJS_DIR)
+
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -Nexec/elfexec -Nfs/fifofs -Nfs/sockfs -Ndrv/ip \
+ -Nfs/zfs -Nmisc/klmmod -Nsys/sysacct
+
+# needs work
+SMATCH=off
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+# Include brand-specific rules
+#
+
+include $(UTSBASE)/intel/lx_brand/Makefile.rules
diff --git a/usr/src/uts/intel/lx_brand/Makefile.rules b/usr/src/uts/intel/lx_brand/Makefile.rules
new file mode 100644
index 0000000000..f1244569b0
--- /dev/null
+++ b/usr/src/uts/intel/lx_brand/Makefile.rules
@@ -0,0 +1,82 @@
+#
+# 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.
+# Copyright 2016 Joyent, Inc.
+#
+#
+
+#
+# Section 1a: C object build rules
+#
+$(OBJS_DIR_OBJ64)/%.o: $(UTSBASE)/common/brand/lx/os/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o: $(UTSBASE)/common/brand/lx/os/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_OBJ64)/%.o: $(UTSBASE)/common/brand/lx/syscall/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o: $(UTSBASE)/common/brand/lx/syscall/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_OBJ64)/%.o: $(UTSBASE)/intel/brand/lx/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o: $(UTSBASE)/intel/brand/lx/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_OBJ64)/%.o: $(UTSBASE)/intel/brand/lx/%.s
+ $(COMPILE.s) -I$(UTSBASE)/i86pc -o $@ $<
+
+$(OBJS_DIR_OBJ64)/%.o: $(LX_CMN)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o: $(UTSBASE)/intel/brand/lx/%.s
+ $(COMPILE.s) -I$(UTSBASE)/i86pc -o $@ $<
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/os/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/syscall/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/intel/brand/lx/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(LX_CMN)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/intel/brand/lx/%.s
+ $(COMPILE.s) -I$(UTSBASE)/i86pc -o $@ $<
diff --git a/usr/src/uts/intel/lx_cgroup/Makefile b/usr/src/uts/intel/lx_cgroup/Makefile
new file mode 100644
index 0000000000..e21a83cace
--- /dev/null
+++ b/usr/src/uts/intel/lx_cgroup/Makefile
@@ -0,0 +1,52 @@
+#
+# 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 2019 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+LX_CMN = $(SRC)/common/brand/lx
+
+MODULE = lx_cgroup
+OBJECTS = $(LX_CGROUP_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -Nbrand/lx_brand
+
+# needs work
+$(OBJS_DIR)/cgrps_vnops.o := SMOFF += signed
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
+
+include $(UTSBASE)/intel/lx_cgroup/Makefile.rules
diff --git a/usr/src/uts/intel/lx_cgroup/Makefile.rules b/usr/src/uts/intel/lx_cgroup/Makefile.rules
new file mode 100644
index 0000000000..f08cb0d6f2
--- /dev/null
+++ b/usr/src/uts/intel/lx_cgroup/Makefile.rules
@@ -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 2015 Joyent, Inc. All rights reserved.
+#
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/cgroups/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/lx_devfs/Makefile b/usr/src/uts/intel/lx_devfs/Makefile
new file mode 100644
index 0000000000..1f5f13b747
--- /dev/null
+++ b/usr/src/uts/intel/lx_devfs/Makefile
@@ -0,0 +1,52 @@
+#
+# 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 2019 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+LX_CMN = $(SRC)/common/brand/lx
+
+MODULE = lx_devfs
+OBJECTS = $(LX_DEVFS_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -Nbrand/lx_brand
+
+# needs work
+$(OBJS_DIR)/lxd_vnops.o := SMOFF += signed
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
+
+include $(UTSBASE)/intel/lx_devfs/Makefile.rules
diff --git a/usr/src/uts/intel/lx_devfs/Makefile.rules b/usr/src/uts/intel/lx_devfs/Makefile.rules
new file mode 100644
index 0000000000..b2bcb2fc89
--- /dev/null
+++ b/usr/src/uts/intel/lx_devfs/Makefile.rules
@@ -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 2015 Joyent, Inc. All rights reserved.
+#
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/devfs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/lx_netlink/Makefile b/usr/src/uts/intel/lx_netlink/Makefile
new file mode 100644
index 0000000000..ed94db631d
--- /dev/null
+++ b/usr/src/uts/intel/lx_netlink/Makefile
@@ -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 2019 Joyent, Inc.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = lx_netlink
+OBJECTS = $(LX_NETLINK_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_SOCK_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CPPFLAGS += -I$(UTSBASE)/common/brand/lx
+LDFLAGS += -Ndrv/ip -Nfs/sockfs -Nbrand/lx_brand
+
+# needs work
+SMOFF += all_func_returns
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/io/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/lx_proc/Makefile b/usr/src/uts/intel/lx_proc/Makefile
new file mode 100644
index 0000000000..4997a34d7b
--- /dev/null
+++ b/usr/src/uts/intel/lx_proc/Makefile
@@ -0,0 +1,101 @@
+#
+# 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
+#
+#
+# uts/intel/lx_proc/Makefile
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2019 Joyent, Inc.
+#
+# This makefile drives the production of the lxproc file system
+# kernel module.
+#
+# i86 architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Path to where brand common sources live
+#
+LX_CMN = $(SRC)/common/brand/lx
+
+#
+# Define the module and object file sets.
+#
+MODULE = lx_proc
+OBJECTS = $(LX_PROC_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+INC_PATH += -I$(UTSBASE)/common/fs/zfs
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Overrides.
+#
+CFLAGS += $(CCVERBOSE)
+
+#
+# Depends on procfs and lx_brand
+#
+LDFLAGS += -Nfs/procfs -Nbrand/lx_brand -Ndrv/inotify -Ndrv/ip
+LDFLAGS += -Nfs/sockfs -Ncrypto/swrand -Nmisc/cc
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+# Include brand-specific rules
+#
+
+include $(UTSBASE)/intel/lx_proc/Makefile.rules
diff --git a/usr/src/uts/intel/lx_proc/Makefile.rules b/usr/src/uts/intel/lx_proc/Makefile.rules
new file mode 100644
index 0000000000..9d3c3b668b
--- /dev/null
+++ b/usr/src/uts/intel/lx_proc/Makefile.rules
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+#
+# Section 1a: C object build rules
+#
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/procfs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/lx_ptm/Makefile b/usr/src/uts/intel/lx_ptm/Makefile
new file mode 100644
index 0000000000..a0e63664f2
--- /dev/null
+++ b/usr/src/uts/intel/lx_ptm/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 (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
+#
+#
+# uts/intel/lx_ptm/Makefile
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# This makefile drives the production of the lx_ptm driver
+#
+# intel architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = lx_ptm
+OBJECTS = $(LX_PTM_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/brand/lx/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+CPPFLAGS += -I$(UTSBASE)/common/brand/lx
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/io/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/lx_sysfs/Makefile b/usr/src/uts/intel/lx_sysfs/Makefile
new file mode 100644
index 0000000000..93599d6723
--- /dev/null
+++ b/usr/src/uts/intel/lx_sysfs/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 2016 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+LX_CMN = $(SRC)/common/brand/lx
+
+MODULE = lx_sysfs
+OBJECTS = $(LX_SYS_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -Nbrand/lx_brand -Ndrv/ip
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
+
+include $(UTSBASE)/intel/lx_sysfs/Makefile.rules
diff --git a/usr/src/uts/intel/lx_sysfs/Makefile.rules b/usr/src/uts/intel/lx_sysfs/Makefile.rules
new file mode 100644
index 0000000000..fab15d52b1
--- /dev/null
+++ b/usr/src/uts/intel/lx_sysfs/Makefile.rules
@@ -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 2015 Joyent, Inc. All rights reserved.
+#
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/sysfs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/lx_systrace/Makefile b/usr/src/uts/intel/lx_systrace/Makefile
new file mode 100644
index 0000000000..a2f9e6be35
--- /dev/null
+++ b/usr/src/uts/intel/lx_systrace/Makefile
@@ -0,0 +1,62 @@
+#
+# 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.
+#
+
+UTSBASE = ../..
+
+MODULE = lx_systrace
+OBJECTS = $(LX_SYSTRACE_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+ROOTLINK = $(USR_DTRACE_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/brand/lx/dtrace
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+
+CPPFLAGS += -I$(UTSBASE)/common/brand/lx
+
+LDFLAGS += -Ndrv/dtrace -Nbrand/lx_brand
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+$(ROOTLINK): $(USR_DTRACE_DIR) $(ROOTMODULE)
+ -$(RM) $@; ln $(ROOTMODULE) $@
+
+include $(UTSBASE)/intel/Makefile.targ
+
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/dtrace/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/lxautofs/Makefile b/usr/src/uts/intel/lxautofs/Makefile
new file mode 100644
index 0000000000..4b87e11966
--- /dev/null
+++ b/usr/src/uts/intel/lxautofs/Makefile
@@ -0,0 +1,102 @@
+#
+# 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 2019 Joyent, Inc.
+#
+
+#
+# This makefile drives the production of the lxautofs file system
+# kernel module.
+#
+# i86 architecture dependent
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+# Note that the name of the actual filesystem is lxautofs and
+# not lx_autofs. This is becase filesystem names are stupidly
+# limited to 8 characters.
+#
+MODULE = lxautofs
+OBJECTS = $(LX_AUTOFS_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+ROOTLINK = $(USR_FS_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/brand/lx/autofs
+
+INC_PATH += -I$(UTSBASE)/common/brand/lx
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+
+#
+# Overrides.
+#
+CFLAGS += $(CCVERBOSE)
+LDFLAGS += -Nfs/nfs
+
+# needs work
+SMOFF += all_func_returns
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+$(ROOTLINK): $(ROOT_FS_DIR) $(ROOTMODULE)
+ -$(RM) $@; ln $(ROOTMODULE) $@
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+# Include brand-specific rules
+#
+
+include $(UTSBASE)/intel/lxautofs/Makefile.rules
diff --git a/usr/src/uts/intel/lxautofs/Makefile.rules b/usr/src/uts/intel/lxautofs/Makefile.rules
new file mode 100644
index 0000000000..ab09a48bc9
--- /dev/null
+++ b/usr/src/uts/intel/lxautofs/Makefile.rules
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+
+#
+# Section 1a: C object build rules
+#
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/brand/lx/autofs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
diff --git a/usr/src/uts/intel/lxprocfs/Makefile b/usr/src/uts/intel/lxprocfs/Makefile
new file mode 100644
index 0000000000..f57d46add5
--- /dev/null
+++ b/usr/src/uts/intel/lxprocfs/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
+#
+#
+# uts/intel/lxprocfs/Makefile
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2019 Joyent, Inc.
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = lxprocfs
+OBJECTS = $(LXPROC_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_FS_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Depends on procfs
+#
+LDFLAGS += -Nfs/procfs
+
+# false positive
+# needs work
+$(OBJS_DIR)/lxpr_vnops.o := SMOFF += strcpy_overflow
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/mac/Makefile b/usr/src/uts/intel/mac/Makefile
index 96fbf52585..bfad21b20a 100644
--- a/usr/src/uts/intel/mac/Makefile
+++ b/usr/src/uts/intel/mac/Makefile
@@ -51,8 +51,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
# Overrides.
#
CFLAGS += $(CCVERBOSE)
-INC_PATH += -I$(UTSBASE)/common/io/bpf
-
CERRWARN += -_gcc=-Wno-unused-label
CERRWARN += $(CNOWARN_UNINIT)
@@ -62,6 +60,7 @@ CERRWARN += -_gcc=-Wno-unused-variable
# needs work
SMOFF += all_func_returns
+$(OBJS_DIR)/mac_sched.o := SMOFF += assign_vs_compare
$(OBJS_DIR)/mac_util.o := SMOFF += signed
#
diff --git a/usr/src/uts/intel/mac_ether/Makefile b/usr/src/uts/intel/mac_ether/Makefile
index c56f6026bc..fadd3402c3 100644
--- a/usr/src/uts/intel/mac_ether/Makefile
+++ b/usr/src/uts/intel/mac_ether/Makefile
@@ -54,7 +54,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/intel/mac_ib/Makefile b/usr/src/uts/intel/mac_ib/Makefile
index 0527fd1dce..e527f88904 100644
--- a/usr/src/uts/intel/mac_ib/Makefile
+++ b/usr/src/uts/intel/mac_ib/Makefile
@@ -54,7 +54,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/intel/mac_wifi/Makefile b/usr/src/uts/intel/mac_wifi/Makefile
index 73efe6ffd7..ebb33564a3 100644
--- a/usr/src/uts/intel/mac_wifi/Makefile
+++ b/usr/src/uts/intel/mac_wifi/Makefile
@@ -56,7 +56,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -Nmisc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/intel/ml/modstubs.s b/usr/src/uts/intel/ml/modstubs.s
index bac97ef672..0994573bd7 100644
--- a/usr/src/uts/intel/ml/modstubs.s
+++ b/usr/src/uts/intel/ml/modstubs.s
@@ -46,7 +46,7 @@
* NOTE: Use NO_UNLOAD_STUBs if the module is NOT unloadable once it is
* loaded.
*/
-#define MAXNARG 10
+#define MAXNARG 12
/*
* WARNING: there is no check for forgetting to write END_MODULE,
@@ -181,7 +181,7 @@ fcnname/**/_info: \
pushq %rcx
pushq %r8
pushq %r9
- /* (next 4 args, if any, are already on the stack above %rbp) */
+ /* (next 6 args, if any, are already on the stack above %rbp) */
movq %r15, %rdi
call mod_hold_stub /* mod_hold_stub(mod_stub_info *) */
cmpl $-1, %eax /* error? */
@@ -192,7 +192,7 @@ fcnname/**/_info: \
jmp .L2
.L1:
/*
- * copy MAXNARG == 10 incoming arguments
+ * copy MAXNARG == 12 incoming arguments
*/
popq %r9
popq %r8
@@ -216,9 +216,11 @@ fcnname/**/_info: \
pushq (%rsp, %r11, 8)
pushq (%rsp, %r11, 8)
pushq (%rsp, %r11, 8)
+ pushq (%rsp, %r11, 8)
+ pushq (%rsp, %r11, 8)
movq (%r15), %rax
INDIRECT_CALL_REG(rax) /* call the stub fn(arg, ..) */
- addq $0x20, %rsp /* pop off last 4 args */
+ addq $0x30, %rsp /* pop off last 6 args */
pushq %rax /* save any return values */
pushq %rdx
movq %r15, %rdi
diff --git a/usr/src/uts/intel/ml/swtch.s b/usr/src/uts/intel/ml/swtch.s
index c6c606b11e..55aaf4e122 100644
--- a/usr/src/uts/intel/ml/swtch.s
+++ b/usr/src/uts/intel/ml/swtch.s
@@ -507,3 +507,41 @@ resume_from_intr_return:
call thread_exit /* destroy thread if it returns. */
/*NOTREACHED*/
SET_SIZE(thread_start)
+
+ ENTRY(thread_splitstack_run)
+ pushq %rbp /* push base pointer */
+ movq %rsp, %rbp /* construct frame */
+ movq %rdi, %rsp /* set stack pinter */
+ movq %rdx, %rdi /* load arg */
+ INDIRECT_CALL_REG(rsi) /* call specified function */
+ leave /* pop base pointer */
+ ret
+ SET_SIZE(thread_splitstack_run)
+
+ /*
+ * Once we're back on our own stack, we need to be sure to set the
+ * value of rsp0 in the TSS back to our original stack: if we gave
+ * up the CPU at all while on our split stack, the rsp0 will point
+ * to that stack from resume (above); if were to try to return to
+ * userland in that state, we will die absolutely horribly (namely,
+ * trying to iretq back to registers in a bunch of freed segkp). We
+ * are expecting this to be called after T_STACK has been restored,
+ * but before we return. It's okay if we are preempted in this code:
+ * when the new CPU picks us up, they will automatically set rsp0
+ * correctly, which is all we're trying to do here.
+ */
+ ENTRY(thread_splitstack_cleanup)
+ LOADCPU(%r8)
+ movq CPU_TSS(%r8), %r9
+ cmpq $1, kpti_enable
+ jne 1f
+ leaq CPU_KPTI_TR_RSP(%r8), %rax
+ jmp 2f
+1:
+ movq CPU_THREAD(%r8), %r10
+ movq T_STACK(%r10), %rax
+ addq $REGSIZE+MINFRAME, %rax
+2:
+ movq %rax, TSS_RSP0(%r9)
+ ret
+ SET_SIZE(thread_splitstack_cleanup)
diff --git a/usr/src/uts/intel/nfp/Makefile b/usr/src/uts/intel/nfp/Makefile
new file mode 100644
index 0000000000..d302cc16c3
--- /dev/null
+++ b/usr/src/uts/intel/nfp/Makefile
@@ -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 2019 Joyent, Inc.
+#
+
+#
+# uts/intel/nfp/Makefile
+#
+# This makefile drives the production of the nfp
+# driver kernel module.
+#
+# intel architecture dependent
+#
+
+#
+# Paths to the base of the uts directory trees
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = nfp
+OBJECTS = $(NFP_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+#
+# Driver-specific flags
+#
+CPPFLAGS += -DCH_KERNELVER=270
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-unused-function
+
+# 3rd party code
+SMOFF += indenting
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/opteron_pcbe/Makefile b/usr/src/uts/intel/opteron_pcbe/Makefile
index 8b04073ac3..4d2180329e 100644
--- a/usr/src/uts/intel/opteron_pcbe/Makefile
+++ b/usr/src/uts/intel/opteron_pcbe/Makefile
@@ -49,7 +49,7 @@ CPCGEN_SRCS = $(CPCGEN_OBJS:%.o=%.c) opteron_pcbe_cpcgen.h
MODULE = pcbe.AuthenticAMD
OBJECTS = $(OPTERON_PCBE_OBJS:%=$(OBJS_DIR)/%)
OBJECTS += $(CPCGEN_OBJS:%=$(OBJS_DIR)/%)
-ROOTMODULE = $(USR_PCBE_DIR)/$(MODULE)
+ROOTMODULE = $(ROOT_PSM_PCBE_DIR)/$(MODULE)
#
# Include common rules.
diff --git a/usr/src/uts/intel/os/archdep.c b/usr/src/uts/intel/os/archdep.c
index 08a593bffd..1f3f438951 100644
--- a/usr/src/uts/intel/os/archdep.c
+++ b/usr/src/uts/intel/os/archdep.c
@@ -521,6 +521,13 @@ ucontext_32ton(const ucontext32_t *src, ucontext_t *dst)
if (src->uc_flags & UC_FPU)
fpregset_32ton(&src->uc_mcontext.fpregs,
&dst->uc_mcontext.fpregs);
+
+ /*
+ * Copy the brand-private data:
+ */
+ dst->uc_brand_data[0] = (void *)(uintptr_t)src->uc_brand_data[0];
+ dst->uc_brand_data[1] = (void *)(uintptr_t)src->uc_brand_data[1];
+ dst->uc_brand_data[2] = (void *)(uintptr_t)src->uc_brand_data[2];
}
#endif /* _SYSCALL32_IMPL */
@@ -575,9 +582,11 @@ getuserpc()
#define IS_NOT_CS 0
/*ARGSUSED*/
-static greg_t
+greg_t
fix_segreg(greg_t sr, int iscs, model_t datamodel)
{
+ kthread_t *t = curthread;
+
switch (sr &= 0xffff) {
case 0:
@@ -610,6 +619,19 @@ fix_segreg(greg_t sr, int iscs, model_t datamodel)
}
/*
+ * Allow this process's brand to do any necessary segment register
+ * manipulation.
+ */
+ if (PROC_IS_BRANDED(t->t_procp) && BRMOP(t->t_procp)->b_fixsegreg) {
+ greg_t bsr = BRMOP(t->t_procp)->b_fixsegreg(sr, datamodel);
+
+ if (bsr == 0 && iscs == IS_CS)
+ return (0 | SEL_UPL);
+ else
+ return (bsr);
+ }
+
+ /*
* Force it into the LDT in ring 3 for 32-bit processes, which by
* default do not have an LDT, so that any attempt to use an invalid
* selector will reference the (non-existant) LDT, and cause a #gp
diff --git a/usr/src/uts/intel/os/comm_page_util.c b/usr/src/uts/intel/os/comm_page_util.c
index f286bee7f6..0674acbc2e 100644
--- a/usr/src/uts/intel/os/comm_page_util.c
+++ b/usr/src/uts/intel/os/comm_page_util.c
@@ -39,12 +39,12 @@ comm_page_mapin()
{
#if !defined(__xpv)
proc_t *p = curproc;
- caddr_t addr = NULL;
+ caddr_t addr = (caddr_t)COMM_PAGE_ALIGN;
size_t len = COMM_PAGE_SIZE;
uint_t prot = PROT_USER | PROT_READ;
segumap_crargs_t suarg;
- map_addr(&addr, len, (offset_t)0, 1, 0);
+ map_addr(&addr, len, (offset_t)0, 1, MAP_ALIGN);
if (addr == NULL || valid_usr_range(addr, len, prot, p->p_as,
p->p_as->a_userlimit) != RANGE_OKAY) {
return (NULL);
diff --git a/usr/src/uts/intel/os/cpuid.c b/usr/src/uts/intel/os/cpuid.c
index 5e564b7acf..1459c034b9 100644
--- a/usr/src/uts/intel/os/cpuid.c
+++ b/usr/src/uts/intel/os/cpuid.c
@@ -5585,6 +5585,12 @@ cpuid_pass_resolve(cpu_t *cpu, void *arg)
hwcap_flags |= AV_386_TSC;
}
+ /* Detect systems with a potential CPUID limit */
+ if (cpi->cpi_vendor == X86_VENDOR_Intel && cpi->cpi_maxeax < 4) {
+ cmn_err(CE_NOTE, "CPUID limit detected, "
+ "see the CPUID(7D) man page for details\n");
+ }
+
/*
* Check a few miscellaneous features.
*/
diff --git a/usr/src/uts/intel/os/desctbls.c b/usr/src/uts/intel/os/desctbls.c
index c54efdb75f..0307aa56c6 100644
--- a/usr/src/uts/intel/os/desctbls.c
+++ b/usr/src/uts/intel/os/desctbls.c
@@ -158,7 +158,7 @@ struct interposing_handler {
* The brand infrastructure interposes on two handlers, and we use one as a
* NULL signpost.
*/
-static struct interposing_handler brand_tbl[2];
+static struct interposing_handler brand_tbl[3];
/*
* software prototypes for default local descriptor table
@@ -774,6 +774,13 @@ init_idt_common(gate_desc_t *idt)
KCS_SEL, SDT_SYSIGT, TRP_KPL, idt_vector_to_ist(T_SIMDFPE));
/*
+ * install "int80" handler at, well, 0x80.
+ */
+ set_gatesegd(&idt0[T_INT80],
+ (kpti_enable == 1) ? &tr_sys_int80 : &sys_int80,
+ KCS_SEL, SDT_SYSIGT, TRP_UPL, idt_vector_to_ist(T_INT80));
+
+ /*
* install fast trap handler at 210.
*/
set_gatesegd(&idt[T_FASTTRAP],
@@ -795,18 +802,25 @@ init_idt_common(gate_desc_t *idt)
KCS_SEL, SDT_SYSIGT, TRP_UPL, idt_vector_to_ist(T_DTRACE_RET));
/*
- * Prepare interposing descriptor for the syscall handler
- * and cache copy of the default descriptor.
+ * Prepare interposing descriptors for the branded "int80"
+ * and syscall handlers and cache copies of the default
+ * descriptors.
*/
- brand_tbl[0].ih_inum = T_SYSCALLINT;
- brand_tbl[0].ih_default_desc = idt0[T_SYSCALLINT];
-
+ brand_tbl[0].ih_inum = T_INT80;
+ brand_tbl[0].ih_default_desc = idt0[T_INT80];
set_gatesegd(&(brand_tbl[0].ih_interp_desc),
+ (kpti_enable == 1) ? &tr_brand_sys_int80 : &brand_sys_int80,
+ KCS_SEL, SDT_SYSIGT, TRP_UPL, idt_vector_to_ist(T_INT80));
+
+ brand_tbl[1].ih_inum = T_SYSCALLINT;
+ brand_tbl[1].ih_default_desc = idt0[T_SYSCALLINT];
+
+ set_gatesegd(&(brand_tbl[1].ih_interp_desc),
(kpti_enable == 1) ? &tr_brand_sys_syscall_int :
&brand_sys_syscall_int, KCS_SEL, SDT_SYSIGT, TRP_UPL,
idt_vector_to_ist(T_SYSCALLINT));
- brand_tbl[1].ih_inum = 0;
+ brand_tbl[2].ih_inum = 0;
}
#if defined(__xpv)
diff --git a/usr/src/uts/intel/os/device_policy b/usr/src/uts/intel/os/device_policy
index 41adb787ce..88d6afa054 100644
--- a/usr/src/uts/intel/os/device_policy
+++ b/usr/src/uts/intel/os/device_policy
@@ -8,4 +8,21 @@
#
* read_priv_set=none write_priv_set=none
-md:admin write_priv_set=sys_config
+bridge:* read_priv_set=net_rawaccess write_priv_set=net_rawaccess
+fssnap:ctl read_priv_set=sys_config write_priv_set=sys_config
+icmp:* read_priv_set=net_icmpaccess write_priv_set=net_icmpaccess
+icmp6:* read_priv_set=net_icmpaccess write_priv_set=net_icmpaccess
+ipf:* read_priv_set=sys_ip_config write_priv_set=sys_ip_config
+ip:* read_priv_set=net_rawaccess write_priv_set=net_rawaccess
+ip6:* read_priv_set=net_rawaccess write_priv_set=net_rawaccess
+ipnet:* read_priv_set=net_observability write_priv_set=net_observability
+ipsecah:* read_priv_set=sys_ip_config write_priv_set=sys_ip_config
+ipsecesp:* read_priv_set=sys_ip_config write_priv_set=sys_ip_config
+keysock:* read_priv_set=sys_ip_config write_priv_set=sys_ip_config
+mm:allkmem read_priv_set=all write_priv_set=all
+mm:kmem read_priv_set=none write_priv_set=all
+mm:mem read_priv_set=none write_priv_set=all
+openeepr:* write_priv_set=all
+random:* write_priv_set=sys_devices
+scsi_vhci:devctl write_priv_set=sys_devices
+spdsock:* read_priv_set=sys_ip_config write_priv_set=sys_ip_config
diff --git a/usr/src/uts/intel/os/driver_aliases b/usr/src/uts/intel/os/driver_aliases
index 3116819932..1a9c082f57 100644
--- a/usr/src/uts/intel/os/driver_aliases
+++ b/usr/src/uts/intel/os/driver_aliases
@@ -1 +1,1851 @@
+aac "pci1028,3"
+aac "pci1028,a"
+aac "pci9005,285"
+aac "pci9005,286"
+aac "pciex9005,285"
+aac "pciex9005,286"
+acpinex "acpivirtnex"
+adpu320 "pci9005,8000"
+adpu320 "pci9005,800f.9005.5f"
+adpu320 "pci9005,8010"
+adpu320 "pci9005,8011"
+adpu320 "pci9005,8012"
+adpu320 "pci9005,8014"
+adpu320 "pci9005,8015"
+adpu320 "pci9005,8016"
+adpu320 "pci9005,8017"
+adpu320 "pci9005,801d"
+adpu320 "pci9005,801e"
+adpu320 "pci9005,801f"
+adpu320 "pci9005,808f"
+afe "pci10b7,9300"
+afe "pci1113,1216"
+afe "pci1317,1985"
+afe "pci1317,9511"
+afe "pci1317,9513"
+afe "pci1317,981"
+afe "pci1317,985"
+afe "pci13d1,ab02"
+afe "pci13d1,ab03"
+afe "pci13d1,ab08"
+afe "pci1737,ab08"
+agptarget "pci1022,7454"
+agptarget "pci8086,1130"
+agptarget "pci8086,2560"
+agptarget "pci8086,2570"
+agptarget "pci8086,2580"
+agptarget "pci8086,2590"
+agptarget "pci8086,2770"
+agptarget "pci8086,27a0"
+agptarget "pci8086,27ac"
+agptarget "pci8086,2970"
+agptarget "pci8086,2980"
+agptarget "pci8086,2990"
+agptarget "pci8086,29a0"
+agptarget "pci8086,29b0"
+agptarget "pci8086,29c0"
+agptarget "pci8086,29d0"
+agptarget "pci8086,2a00"
+agptarget "pci8086,2a10"
+agptarget "pci8086,2a40"
+agptarget "pci8086,2e00"
+agptarget "pci8086,2e10"
+agptarget "pci8086,2e20"
+agptarget "pci8086,2e30"
+agptarget "pci8086,2e40"
+agptarget "pci8086,3575"
+agptarget "pci8086,3580"
+agptarget "pci8086,40"
+agptarget "pci8086,44"
+agptarget "pci8086,62"
+agptarget "pci8086,6a"
+agptarget "pci8086,7120"
+agptarget "pci8086,7122"
+agptarget "pci8086,7124"
+ahci "pciclass,010601"
+ahci "pci8086,2822,p"
+ahci "pci8086,282a,p"
+amd64_gart "pci1022,1103"
+amd8111s "pci1022,7462"
+amd_iommu "pci1002,5a23"
+amd_iommu "pci1022,11ff"
+amdzen_stub "pci1022,1440,p"
+amdzen_stub "pci1022,1441,p"
+amdzen_stub "pci1022,1442,p"
+amdzen_stub "pci1022,1443,p"
+amdzen_stub "pci1022,1444,p"
+amdzen_stub "pci1022,1445,p"
+amdzen_stub "pci1022,1446,p"
+amdzen_stub "pci1022,1447,p"
+amdzen_stub "pci1022,1448,p"
+amdzen_stub "pci1022,1449,p"
+amdzen_stub "pci1022,144a,p"
+amdzen_stub "pci1022,144b,p"
+amdzen_stub "pci1022,144c,p"
+amdzen_stub "pci1022,144d,p"
+amdzen_stub "pci1022,144e,p"
+amdzen_stub "pci1022,144f,p"
+amdzen_stub "pci1022,1450,p"
+amdzen_stub "pci1022,1460,p"
+amdzen_stub "pci1022,1461,p"
+amdzen_stub "pci1022,1462,p"
+amdzen_stub "pci1022,1463,p"
+amdzen_stub "pci1022,1464,p"
+amdzen_stub "pci1022,1465,p"
+amdzen_stub "pci1022,1466,p"
+amdzen_stub "pci1022,1467,p"
+amdzen_stub "pci1022,1480,p"
+amdzen_stub "pci1022,1490,p"
+amdzen_stub "pci1022,1491,p"
+amdzen_stub "pci1022,1492,p"
+amdzen_stub "pci1022,1493,p"
+amdzen_stub "pci1022,1494,p"
+amdzen_stub "pci1022,1495,p"
+amdzen_stub "pci1022,1496,p"
+amdzen_stub "pci1022,1497,p"
+amdzen_stub "pci1022,14a4,p"
+amdzen_stub "pci1022,14ad,p"
+amdzen_stub "pci1022,14ae,p"
+amdzen_stub "pci1022,14af,p"
+amdzen_stub "pci1022,14b0,p"
+amdzen_stub "pci1022,14b1,p"
+amdzen_stub "pci1022,14b2,p"
+amdzen_stub "pci1022,14b3,p"
+amdzen_stub "pci1022,14b4,p"
+amdzen_stub "pci1022,14b5,p"
+amdzen_stub "pci1022,14d8,p"
+amdzen_stub "pci1022,14e0,p"
+amdzen_stub "pci1022,14e1,p"
+amdzen_stub "pci1022,14e2,p"
+amdzen_stub "pci1022,14e3,p"
+amdzen_stub "pci1022,14e4,p"
+amdzen_stub "pci1022,14e5,p"
+amdzen_stub "pci1022,14e6,p"
+amdzen_stub "pci1022,14e7,p"
+amdzen_stub "pci1022,15d0,p"
+amdzen_stub "pci1022,15e8,p"
+amdzen_stub "pci1022,15e9,p"
+amdzen_stub "pci1022,15ea,p"
+amdzen_stub "pci1022,15eb,p"
+amdzen_stub "pci1022,15ec,p"
+amdzen_stub "pci1022,15ed,p"
+amdzen_stub "pci1022,15ee,p"
+amdzen_stub "pci1022,15ef,p"
+amdzen_stub "pci1022,1630,p"
+amdzen_stub "pci1022,1650,p"
+amdzen_stub "pci1022,1651,p"
+amdzen_stub "pci1022,1652,p"
+amdzen_stub "pci1022,1653,p"
+amdzen_stub "pci1022,1654,p"
+amdzen_stub "pci1022,1655,p"
+amdzen_stub "pci1022,1656,p"
+amdzen_stub "pci1022,1657,p"
+amdzen_stub "pci1022,166a,p"
+amdzen_stub "pci1022,166b,p"
+amdzen_stub "pci1022,166c,p"
+amdzen_stub "pci1022,166d,p"
+amdzen_stub "pci1022,166e,p"
+amdzen_stub "pci1022,166f,p"
+amdzen_stub "pci1022,1670,p"
+amdzen_stub "pci1022,1671,p"
+amdzen_stub "pci1022,1679,p"
+amdzen_stub "pci1022,167a,p"
+amdzen_stub "pci1022,167b,p"
+amdzen_stub "pci1022,167c,p"
+amdzen_stub "pci1022,167d,p"
+amdzen_stub "pci1022,167e,p"
+amdzen_stub "pci1022,167f,p"
+amdzen_stub "pci1022,1680,p"
+amdzen_stub "pci1022,1724,p"
+amdzen_stub "pci1022,1725,p"
+amdzen_stub "pci1022,1726,p"
+amdzen_stub "pci1022,1727,p"
+amdzen_stub "pci1022,1728,p"
+amdzen_stub "pci1022,1729,p"
+amdzen_stub "pci1022,172a,p"
+amdzen_stub "pci1022,172b,p"
+amdnbtemp "pci1022,1203,p"
+amdnbtemp "pci1022,1303,p"
+amdnbtemp "pci1022,1403,p"
+amdnbtemp "pci1022,141d,p"
+amdnbtemp "pci1022,1533,p"
+amdnbtemp "pci1022,1583,p"
+amdnbtemp "pci1022,1603,p"
+amdnbtemp "pci1022,1703,p"
+amr "pci1000,1960.1000,532"
+amr "pci101e,1960.1028,493"
+amr "pci1000,1960.1028,518"
+amr "pci1000,1960.1028,520"
+arcmsr "pci17d3,1110"
+arcmsr "pci17d3,1120"
+arcmsr "pci17d3,1130"
+arcmsr "pci17d3,1160"
+arcmsr "pci17d3,1170"
+arcmsr "pci17d3,1201"
+arcmsr "pci17d3,1210"
+arcmsr "pci17d3,1220"
+arcmsr "pci17d3,1230"
+arcmsr "pci17d3,1260"
+arcmsr "pci17d3,1270"
+arcmsr "pci17d3,1280"
+arcmsr "pci17d3,1380"
+arcmsr "pci17d3,1381"
+arcmsr "pci17d3,1680"
+arcmsr "pci17d3,1681"
+arcmsr "pci17d3,1880"
+arcmsr "pci17d3,1882"
asy "pci11c1,480"
+ata "ide"
+atge "pciex1969,1026"
+atge "pciex1969,1048"
+atge "pciex1969,1062"
+atge "pciex1969,1063"
+atge "pciex1969,1073"
+atge "pciex1969,1083"
+atge "pciex1969,2060"
+atge "pciex1969,2062"
+axf "usb7b8,420a"
+axf "usbb95,7720"
+axf "usbb95,772a"
+axf "usb2001,1a00"
+axf "usb77b,2226"
+axf "usb846,1040"
+axf "usbb95,1720"
+axf "usb8dd,90ff"
+axf "usb557,2009"
+axf "usb411,3d"
+axf "usb6189,182d"
+axf "usb7aa,17"
+axf "usb1189,893"
+axf "usb1631,6200"
+axf "usb13b1,18"
+axf "usb1557,7720"
+axf "usb7d1,3c05"
+axf "usb2001,3c05"
+axf "usb5ac,1402"
+bcm_sata "pci1166,24a"
+bfe "pci14e4,170c"
+bfe "pci14e4,4401"
+bfe "pci14e4,4402"
+bge "SUNW,bge"
+bge "pci108e,1647"
+bge "pci108e,1648"
+bge "pci14e4,1682"
+bge "pci14e4,1686"
+bge "pci108e,16a7"
+bge "pci108e,16a8"
+bge "pci14e4,16b0"
+bge "pci14e4,16b1"
+bge "pci14e4,16b2"
+bge "pci14e4,16b3"
+bge "pci14e4,16b4"
+bge "pci14e4,16b5"
+bge "pci14e4,16b6"
+bge "pci14e4,16b7"
+bge "pci14e4,1600"
+bge "pci14e4,1601"
+bge "pci14e4,1643"
+bge "pci14e4,1644"
+bge "pci14e4,1645"
+bge "pci14e4,1647"
+bge "pci14e4,1648"
+bge "pci14e4,1649"
+bge "pci14e4,1653"
+bge "pci14e4,1654"
+bge "pci14e4,1657"
+bge "pci14e4,1659"
+bge "pci14e4,165d"
+bge "pci14e4,165e"
+bge "pci14e4,165f"
+bge "pci14e4,1665"
+bge "pci14e4,1668"
+bge "pci14e4,1669"
+bge "pci14e4,166a"
+bge "pci14e4,166e"
+bge "pci14e4,1677"
+bge "pci14e4,1678"
+bge "pci14e4,1679"
+bge "pci14e4,167d"
+bge "pci14e4,1693"
+bge "pci14e4,1696"
+bge "pci14e4,1699"
+bge "pci14e4,169b"
+bge "pci14e4,169c"
+bge "pci14e4,16a6"
+bge "pci14e4,16a7"
+bge "pci14e4,16a8"
+bge "pci14e4,16c7"
+bge "pci14e4,16f3"
+bge "pciex14e4,1643"
+bge "pciex14e4,1655"
+bge "pciex14e4,1656"
+bge "pciex14e4,1657"
+bge "pciex14e4,165a"
+bge "pciex14e4,165b"
+bge "pciex14e4,165c"
+bge "pciex14e4,165f"
+bge "pciex14e4,1665"
+bge "pciex14e4,1673"
+bge "pciex14e4,1674"
+bge "pciex14e4,1677"
+bge "pciex14e4,167a"
+bge "pciex14e4,167b"
+bge "pciex14e4,1680"
+bge "pciex14e4,1681"
+bge "pciex14e4,1682"
+bge "pciex14e4,1684"
+bge "pciex14e4,1686"
+bge "pciex14e4,1688"
+bge "pciex14e4,1689"
+bge "pciex14e4,1690"
+bge "pciex14e4,1691"
+bge "pciex14e4,1692"
+bge "pciex14e4,1694"
+bge "pciex14e4,1698"
+bge "pciex14e4,169d"
+bge "pciex14e4,16b0"
+bge "pciex14e4,16b1"
+bge "pciex14e4,16b2"
+bge "pciex14e4,16b3"
+bge "pciex14e4,16b4"
+bge "pciex14e4,16b5"
+bge "pciex14e4,16b6"
+bge "pciex14e4,16b7"
+bge "pciex14e4,16fd"
+bge "pciex14e4,16f3"
+bge "pciex14e4,1713"
+bnx "pci14e4,1639"
+bnx "pci14e4,163a"
+bnx "pci14e4,163b"
+bnx "pci14e4,163c"
+bnx "pci14e4,164a"
+bnx "pci14e4,164c"
+bnx "pci14e4,16aa"
+bnx "pci14e4,16ac"
+bnxe "pci14e4,164e"
+bnxe "pci14e4,164f"
+bnxe "pci14e4,1650"
+bnxe "pciex14e4,164e"
+bnxe "pciex14e4,164f"
+bnxe "pciex14e4,1650"
+bnxe "pciex14e4,1662"
+bnxe "pciex14e4,1663"
+bnxe "pciex14e4,168a"
+bnxe "pciex14e4,168d"
+bnxe "pciex14e4,168e"
+bnxe "pciex14e4,16a1"
+bnxe "pciex14e4,16a4"
+bnxe "pciex14e4,16a5"
+bnxe "pciex14e4,16ab"
+bnxe "pciex14e4,16ae"
+bscbus "SVI0101"
+ccid "usbif,classb"
+ce "pci100b,35"
+ce "pci108e,abba"
+chxge "pci1425,7"
+chxge "pci1425,a"
+cpqary3 "pci103c,3211"
+cpqary3 "pci103c,3212"
+cpqary3 "pci103c,3223"
+cpqary3 "pci103c,3225"
+cpqary3 "pci103c,3234"
+cpqary3 "pci103c,3235"
+cpqary3 "pci103c,3237"
+cpqary3 "pci103c,323d"
+cpqary3 "pcie11,4070"
+cpqary3 "pcie11,4080"
+cpqary3 "pcie11,4082"
+cpqary3 "pcie11,4083"
+cpqary3 "pcie11,4091"
+cpqary3 "pcie11,409a"
+cpqary3 "pcie11,409b"
+cpqary3 "pcie11,409c"
+cpqary3 "pcie11,409d"
+cpqary3 "pcie11,409e"
+cpudrv "cpu"
+cpunex "cpus"
+dcam1394 "firewire000104,000100"
+dcam1394 "firewire00a02d,000100"
+dmfe "pci108e,9102"
+dmfe "pci1282,9102"
+dnet "pci1011,14"
+dnet "pci1011,19"
+dnet "pci1011,2"
+dnet "pci1011,9"
+dnet "pci10b8,2001"
+dnet "pci1109,1400"
+dnet "pci1109,2400"
+dnet "pci2646,1"
+dr "acpidr_sbd"
+e1000g "pci8086,1000"
+e1000g "pci8086,1001"
+e1000g "pci8086,1004.0e11.49"
+e1000g "pci8086,1004.0e11.b1a4"
+e1000g "pci8086,1004.1014.10f2"
+e1000g "pci8086,1004.8086.1004"
+e1000g "pci8086,1004.8086.2004"
+e1000g "pci8086,1008"
+e1000g "pci8086,1009"
+e1000g "pci8086,100c"
+e1000g "pci8086,100d"
+e1000g "pci8086,100e"
+e1000g "pci8086,100f"
+e1000g "pci8086,1010"
+e1000g "pci8086,1011"
+e1000g "pci8086,1012"
+e1000g "pci8086,1013"
+e1000g "pci8086,1014"
+e1000g "pci8086,1015"
+e1000g "pci8086,1016"
+e1000g "pci8086,1017"
+e1000g "pci8086,1018"
+e1000g "pci8086,1019"
+e1000g "pci8086,101a"
+e1000g "pci8086,101d"
+e1000g "pci8086,101e"
+e1000g "pci8086,1026"
+e1000g "pci8086,1027"
+e1000g "pci8086,1028"
+e1000g "pci8086,1049"
+e1000g "pci8086,104a"
+e1000g "pci8086,104b"
+e1000g "pci8086,104c"
+e1000g "pci8086,104d"
+e1000g "pci8086,105a"
+e1000g "pci8086,105b"
+e1000g "pci8086,105c"
+e1000g "pci8086,105e"
+e1000g "pci8086,105f"
+e1000g "pci8086,1060"
+e1000g "pci8086,1061"
+e1000g "pci8086,1062"
+e1000g "pci8086,1063"
+e1000g "pci8086,1075"
+e1000g "pci8086,1076"
+e1000g "pci8086,1077"
+e1000g "pci8086,1078"
+e1000g "pci8086,1079"
+e1000g "pci8086,107a"
+e1000g "pci8086,107b"
+e1000g "pci8086,107c"
+e1000g "pci8086,107d"
+e1000g "pci8086,107e"
+e1000g "pci8086,107f"
+e1000g "pci8086,108a"
+e1000g "pci8086,108b"
+e1000g "pci8086,108c"
+e1000g "pci8086,1096"
+e1000g "pci8086,1098"
+e1000g "pci8086,1099"
+e1000g "pci8086,109a"
+e1000g "pci8086,10a4"
+e1000g "pci8086,10a5"
+e1000g "pci8086,10b5"
+e1000g "pci8086,10b9"
+e1000g "pci8086,10ba"
+e1000g "pci8086,10bb"
+e1000g "pci8086,10bc"
+e1000g "pci8086,10bd"
+e1000g "pci8086,10bf"
+e1000g "pci8086,10c0"
+e1000g "pci8086,10c2"
+e1000g "pci8086,10c3"
+e1000g "pci8086,10c4"
+e1000g "pci8086,10c5"
+e1000g "pci8086,10cb"
+e1000g "pci8086,10cc"
+e1000g "pci8086,10cd"
+e1000g "pci8086,10ce"
+e1000g "pci8086,10d3"
+e1000g "pci8086,10d5"
+e1000g "pci8086,10d9"
+e1000g "pci8086,10da"
+e1000g "pci8086,10de"
+e1000g "pci8086,10df"
+e1000g "pci8086,10e5"
+e1000g "pci8086,10ea"
+e1000g "pci8086,10eb"
+e1000g "pci8086,10ef"
+e1000g "pci8086,10f0"
+e1000g "pci8086,10f5"
+e1000g "pci8086,10f6"
+e1000g "pci8086,1502"
+e1000g "pci8086,1503"
+e1000g "pci8086,150c"
+e1000g "pci8086,153a"
+e1000g "pci8086,153b"
+e1000g "pci8086,1559"
+e1000g "pci8086,155a"
+e1000g "pci8086,156f"
+e1000g "pci8086,1570"
+e1000g "pci8086,15a0"
+e1000g "pci8086,15a1"
+e1000g "pci8086,15a2"
+e1000g "pci8086,15a3"
+e1000g "pci8086,15b7"
+e1000g "pci8086,15b8"
+e1000g "pci8086,15b9"
+e1000g "pci8086,15bb"
+e1000g "pci8086,15bc"
+e1000g "pci8086,15bd"
+e1000g "pci8086,15be"
+e1000g "pci8086,15d6"
+e1000g "pci8086,15d7"
+e1000g "pci8086,15d8"
+e1000g "pci8086,15df"
+e1000g "pci8086,15e0"
+e1000g "pci8086,15e1"
+e1000g "pci8086,15e2"
+e1000g "pci8086,15e3"
+e1000g "pci8086,15f4,p"
+e1000g "pci8086,15f5,p"
+e1000g "pci8086,15f9,p"
+e1000g "pci8086,15fa,p"
+e1000g "pci8086,15fb,p"
+e1000g "pci8086,15fc,p"
+e1000g "pci8086,1a1c,p"
+e1000g "pci8086,1a1d,p"
+e1000g "pci8086,1a1e,p"
+e1000g "pci8086,1a1f,p"
+e1000g "pci8086,294c"
+e1000g "pci8086,550a,p"
+e1000g "pci8086,550b,p"
+e1000g "pci8086,550c,p"
+e1000g "pci8086,550d,p"
+e1000g "pci8086,550e,p"
+e1000g "pci8086,550f,p"
+e1000g "pci8086,5510,p"
+e1000g "pci8086,5511,p"
+e1000g "pci8086,d4c,p"
+e1000g "pci8086,d4d,p"
+e1000g "pci8086,d4e,p"
+e1000g "pci8086,d4f,p"
+e1000g "pci8086,d53,p"
+e1000g "pci8086,d55,p"
+e1000g "pci8086,dc5,p"
+e1000g "pci8086,dc6,p"
+e1000g "pci8086,dc7,p"
+e1000g "pci8086,dc8,p"
+e1000g "pci8086,f0fe"
+e1000g "pciex8086,1049"
+e1000g "pciex8086,104a"
+e1000g "pciex8086,104b"
+e1000g "pciex8086,104c"
+e1000g "pciex8086,104d"
+e1000g "pciex8086,105e"
+e1000g "pciex8086,105f"
+e1000g "pciex8086,1060"
+e1000g "pciex8086,107d"
+e1000g "pciex8086,107e"
+e1000g "pciex8086,107f"
+e1000g "pciex8086,108b"
+e1000g "pciex8086,108c"
+e1000g "pciex8086,1096"
+e1000g "pciex8086,1098"
+e1000g "pciex8086,109a"
+e1000g "pciex8086,10a4"
+e1000g "pciex8086,10a5"
+e1000g "pciex8086,10b9"
+e1000g "pciex8086,10ba"
+e1000g "pciex8086,10bb"
+e1000g "pciex8086,10bc"
+e1000g "pciex8086,10bd"
+e1000g "pciex8086,10bf"
+e1000g "pciex8086,10c0"
+e1000g "pciex8086,10c2"
+e1000g "pciex8086,10c3"
+e1000g "pciex8086,10c4"
+e1000g "pciex8086,10c5"
+e1000g "pciex8086,10cb"
+e1000g "pciex8086,10cc"
+e1000g "pciex8086,10cd"
+e1000g "pciex8086,10ce"
+e1000g "pciex8086,10d3"
+e1000g "pciex8086,10d5"
+e1000g "pciex8086,10d9"
+e1000g "pciex8086,10da"
+e1000g "pciex8086,10de"
+e1000g "pciex8086,10df"
+e1000g "pciex8086,10e5"
+e1000g "pciex8086,10ea"
+e1000g "pciex8086,10eb"
+e1000g "pciex8086,10ef"
+e1000g "pciex8086,10f0"
+e1000g "pciex8086,10f5"
+e1000g "pciex8086,10f6"
+e1000g "pciex8086,1502"
+e1000g "pciex8086,1503"
+e1000g "pciex8086,150c"
+e1000g "pciex8086,153a"
+e1000g "pciex8086,153b"
+e1000g "pciex8086,1559"
+e1000g "pciex8086,156f"
+e1000g "pciex8086,1570"
+e1000g "pciex8086,155a"
+e1000g "pciex8086,15a0"
+e1000g "pciex8086,15a1"
+e1000g "pciex8086,15a2"
+e1000g "pciex8086,15a3"
+e1000g "pciex8086,15b7"
+e1000g "pciex8086,15b8"
+e1000g "pciex8086,15b9"
+e1000g "pciex8086,15bb"
+e1000g "pciex8086,15bc"
+e1000g "pciex8086,15bd"
+e1000g "pciex8086,15be"
+e1000g "pciex8086,15d6"
+e1000g "pciex8086,15d7"
+e1000g "pciex8086,15d8"
+e1000g "pciex8086,15df"
+e1000g "pciex8086,15e0"
+e1000g "pciex8086,15e1"
+e1000g "pciex8086,15e2"
+e1000g "pciex8086,15e3"
+e1000g "pciex8086,15f4"
+e1000g "pciex8086,15f5"
+e1000g "pciex8086,15f9"
+e1000g "pciex8086,15fa"
+e1000g "pciex8086,15fb"
+e1000g "pciex8086,15fc"
+e1000g "pciex8086,1a1c"
+e1000g "pciex8086,1a1d"
+e1000g "pciex8086,1a1e"
+e1000g "pciex8086,1a1f"
+e1000g "pciex8086,294c"
+e1000g "pciex8086,550a"
+e1000g "pciex8086,550b"
+e1000g "pciex8086,550c"
+e1000g "pciex8086,550d"
+e1000g "pciex8086,550e"
+e1000g "pciex8086,550f"
+e1000g "pciex8086,5510"
+e1000g "pciex8086,5511"
+e1000g "pciex8086,d4c"
+e1000g "pciex8086,d4d"
+e1000g "pciex8086,d4e"
+e1000g "pciex8086,d4f"
+e1000g "pciex8086,d53"
+e1000g "pciex8086,d55"
+e1000g "pciex8086,dc5"
+e1000g "pciex8086,dc6"
+e1000g "pciex8086,dc7"
+e1000g "pciex8086,dc8"
+e1000g "pciex8086,f0fe"
+ecpp "lp"
+ehci "pciclass,0c0320"
+elxl "pci10b7,9000"
+elxl "pci10b7,9001"
+elxl "pci10b7,9004"
+elxl "pci10b7,9005"
+elxl "pci10b7,9006"
+elxl "pci10b7,9050"
+elxl "pci10b7,9051"
+elxl "pci10b7,9055"
+elxl "pci10b7,9056"
+elxl "pci10b7,905a"
+elxl "pci10b7,9200"
+elxl "pci10b7,9201"
+elxl "pci10b7,9202"
+elxl "pci10b7,9800"
+elxl "pci10b7,9805"
+emlxs "lpfs"
+emlxs "pci10df,f0a5"
+emlxs "pci10df,f800"
+emlxs "pci10df,f900"
+emlxs "pci10df,f980"
+emlxs "pci10df,fa00"
+emlxs "pci10df,fc00"
+emlxs "pci10df,fc10"
+emlxs "pci10df,fc20"
+emlxs "pci10df,fd00"
+emlxs "pci10df,fe00"
+emlxs "pciex10df,e200"
+emlxs "pciex10df,e300"
+emlxs "pciex10df,f100"
+emlxs "pciex10df,f111"
+emlxs "pciex10df,f112"
+emlxs "pciex10df,fc20"
+emlxs "pciex10df,fc40"
+emlxs "pciex10df,fe00"
+emlxs "pciex10df,fe05"
+emlxs "pciex117c,63"
+emlxs "pciex117c,64"
+emlxs "pciex117c,65"
+emlxs "pciex117c,94"
+emlxs "pciex19a2,704"
+emlxs "pciex19a2,714"
+ena "pciex1d0f,ec2"
+ena "pciex1d0f,1ec2"
+ena "pciex1d0f,ec20"
+ena "pciex1d0f,ec21"
+fipe "pci8086,25f0"
+fipe "pci8086,360c"
+glm "pci1000,b"
+hci1394 "pciclass,0c0010"
+heci "pci8086,2974"
+heci "pci8086,2984"
+heci "pci8086,2994"
+heci "pci8086,29a4"
+heci "pci8086,29b4"
+heci "pci8086,29c4"
+heci "pci8086,2e04"
+heci "pci8086,2e14"
+hid "usbif,class3"
+hme "pci108e,1001"
+hubd "usbif,class9"
+hxge "pci108e,aaaa"
+i40e "pciex8086,101f"
+i40e "pciex8086,104e"
+i40e "pciex8086,104f"
+i40e "pciex8086,1572"
+i40e "pciex8086,1580"
+i40e "pciex8086,1581"
+i40e "pciex8086,1583"
+i40e "pciex8086,1584"
+i40e "pciex8086,1585"
+i40e "pciex8086,1586"
+i40e "pciex8086,1589"
+i40e "pciex8086,158a"
+i40e "pciex8086,158b"
+i40e "pciex8086,15ff"
+i40e "pciex8086,37ce"
+i40e "pciex8086,37cf"
+i40e "pciex8086,37d0"
+i40e "pciex8086,37d1"
+i40e "pciex8086,37d2"
+i40e "pciex8086,37d3"
+i40e "pciex8086,cf8"
+i40e "pciex8086,d58"
+i915 "pci8086,2562"
+i915 "pci8086,2572"
+i915 "pci8086,2582"
+i915 "pci8086,2592"
+i915 "pci8086,2772"
+i915 "pci8086,27a2"
+i915 "pci8086,27ae"
+i915 "pci8086,2972"
+i915 "pci8086,2982"
+i915 "pci8086,2992"
+i915 "pci8086,29a2"
+i915 "pci8086,29b2"
+i915 "pci8086,29c2"
+i915 "pci8086,29d2"
+i915 "pci8086,2a02"
+i915 "pci8086,2a12"
+i915 "pci8086,2a42"
+i915 "pci8086,2e02.8086.2e02"
+i915 "pci8086,2e12"
+i915 "pci8086,2e22"
+i915 "pci8086,2e32"
+i915 "pci8086,2e42"
+i915 "pci8086,42"
+i915 "pci8086,46"
+ibp "ib.ipib"
+igb "pciex8086,10a7"
+igb "pciex8086,10a9"
+igb "pciex8086,10c9"
+igb "pciex8086,10d6"
+igb "pciex8086,10e6"
+igb "pciex8086,10e7"
+igb "pciex8086,10e8"
+igb "pciex8086,150a"
+igb "pciex8086,150d"
+igb "pciex8086,150e"
+igb "pciex8086,150f"
+igb "pciex8086,1510"
+igb "pciex8086,1511"
+igb "pciex8086,1516"
+igb "pciex8086,1518"
+igb "pciex8086,1521"
+igb "pciex8086,1522"
+igb "pciex8086,1523"
+igb "pciex8086,1524"
+igb "pciex8086,1526"
+igb "pciex8086,1533"
+igb "pciex8086,1534"
+igb "pciex8086,1535"
+igb "pciex8086,1536"
+igb "pciex8086,1537"
+igb "pciex8086,1538"
+igb "pciex8086,1539"
+igb "pciex8086,1546"
+igb "pciex8086,157b"
+igb "pciex8086,157c"
+igb "pciex8086,1f40"
+igb "pciex8086,1f41"
+igb "pciex8086,1f45"
+igb "pciex8086,438"
+imcstub "pci8086,e1e,p"
+imcstub "pci8086,e1f,p"
+imcstub "pci8086,e60,p"
+imcstub "pci8086,e68,p"
+imcstub "pci8086,e6a,p"
+imcstub "pci8086,e6b,p"
+imcstub "pci8086,e6c,p"
+imcstub "pci8086,e6d,p"
+imcstub "pci8086,e71,p"
+imcstub "pci8086,e79,p"
+imcstub "pci8086,ea0,p"
+imcstub "pci8086,ea8,p"
+imcstub "pci8086,eaa,p"
+imcstub "pci8086,eab,p"
+imcstub "pci8086,eac,p"
+imcstub "pci8086,ead,p"
+imcstub "pci8086,ec8,p"
+imcstub "pci8086,ec9,p"
+imcstub "pci8086,eca,p"
+imcstub "pci8086,2014,p"
+imcstub "pci8086,2016,p"
+imcstub "pci8086,2024,p"
+imcstub "pci8086,2040,p"
+imcstub "pci8086,2044,p"
+imcstub "pci8086,2048,p"
+imcstub "pci8086,2054,p"
+imcstub "pci8086,2055,p"
+imcstub "pci8086,2066,p"
+imcstub "pci8086,208e,p"
+imcstub "pci8086,2f1e,p"
+imcstub "pci8086,2f1f,p"
+imcstub "pci8086,2f28,p"
+imcstub "pci8086,2f60,p"
+imcstub "pci8086,2f68,p"
+imcstub "pci8086,2f6a,p"
+imcstub "pci8086,2f6b,p"
+imcstub "pci8086,2f6c,p"
+imcstub "pci8086,2f6d,p"
+imcstub "pci8086,2f71,p"
+imcstub "pci8086,2f79,p"
+imcstub "pci8086,2fa0,p"
+imcstub "pci8086,2fa8,p"
+imcstub "pci8086,2faa,p"
+imcstub "pci8086,2fab,p"
+imcstub "pci8086,2fac,p"
+imcstub "pci8086,2fad,p"
+imcstub "pci8086,2ffc,p"
+imcstub "pci8086,2ffd,p"
+imcstub "pci8086,3c71,p"
+imcstub "pci8086,3ca0,p"
+imcstub "pci8086,3ca8,p"
+imcstub "pci8086,3caa,p"
+imcstub "pci8086,3cab,p"
+imcstub "pci8086,3cac,p"
+imcstub "pci8086,3cad,p"
+imcstub "pci8086,3ce0,p"
+imcstub "pci8086,3ce3,p"
+imcstub "pci8086,3cf4,p"
+imcstub "pci8086,3cf5,p"
+imcstub "pci8086,3cf6,p"
+imcstub "pci8086,6f1e,p"
+imcstub "pci8086,6f1f,p"
+imcstub "pci8086,6f28,p"
+imcstub "pci8086,6f60,p"
+imcstub "pci8086,6f68,p"
+imcstub "pci8086,6f6a,p"
+imcstub "pci8086,6f6b,p"
+imcstub "pci8086,6f6c,p"
+imcstub "pci8086,6f6d,p"
+imcstub "pci8086,6f71,p"
+imcstub "pci8086,6f79,p"
+imcstub "pci8086,6fa0,p"
+imcstub "pci8086,6fa8,p"
+imcstub "pci8086,6faa,p"
+imcstub "pci8086,6fab,p"
+imcstub "pci8086,6fac,p"
+imcstub "pci8086,6fad,p"
+imcstub "pci8086,6ffc,p"
+imcstub "pci8086,6ffd,p"
+imcstub "pciex8086,e1e"
+imcstub "pciex8086,e1f"
+imcstub "pciex8086,e60"
+imcstub "pciex8086,e68"
+imcstub "pciex8086,e6a"
+imcstub "pciex8086,e6b"
+imcstub "pciex8086,e6c"
+imcstub "pciex8086,e6d"
+imcstub "pciex8086,e71"
+imcstub "pciex8086,e79"
+imcstub "pciex8086,ea0"
+imcstub "pciex8086,ea8"
+imcstub "pciex8086,eaa"
+imcstub "pciex8086,eab"
+imcstub "pciex8086,eac"
+imcstub "pciex8086,ead"
+imcstub "pciex8086,ec8"
+imcstub "pciex8086,ec9"
+imcstub "pciex8086,eca"
+imcstub "pciex8086,2014"
+imcstub "pciex8086,2016"
+imcstub "pciex8086,2024"
+imcstub "pciex8086,2040"
+imcstub "pciex8086,2044"
+imcstub "pciex8086,2048"
+imcstub "pciex8086,2054"
+imcstub "pciex8086,2055"
+imcstub "pciex8086,2066"
+imcstub "pciex8086,208e"
+imcstub "pciex8086,2f1e"
+imcstub "pciex8086,2f1f"
+imcstub "pciex8086,2f28"
+imcstub "pciex8086,2f60"
+imcstub "pciex8086,2f68"
+imcstub "pciex8086,2f6a"
+imcstub "pciex8086,2f6b"
+imcstub "pciex8086,2f6c"
+imcstub "pciex8086,2f6d"
+imcstub "pciex8086,2f71"
+imcstub "pciex8086,2f79"
+imcstub "pciex8086,2fa0"
+imcstub "pciex8086,2fa8"
+imcstub "pciex8086,2faa"
+imcstub "pciex8086,2fab"
+imcstub "pciex8086,2fac"
+imcstub "pciex8086,2fad"
+imcstub "pciex8086,2ffc"
+imcstub "pciex8086,2ffd"
+imcstub "pciex8086,3c71"
+imcstub "pciex8086,3ca0"
+imcstub "pciex8086,3ca8"
+imcstub "pciex8086,3caa"
+imcstub "pciex8086,3cab"
+imcstub "pciex8086,3cac"
+imcstub "pciex8086,3cad"
+imcstub "pciex8086,3ce0"
+imcstub "pciex8086,3ce3"
+imcstub "pciex8086,3cf4"
+imcstub "pciex8086,3cf5"
+imcstub "pciex8086,3cf6"
+imcstub "pciex8086,6f1e"
+imcstub "pciex8086,6f1f"
+imcstub "pciex8086,6f28"
+imcstub "pciex8086,6f60"
+imcstub "pciex8086,6f68"
+imcstub "pciex8086,6f6a"
+imcstub "pciex8086,6f6b"
+imcstub "pciex8086,6f6c"
+imcstub "pciex8086,6f6d"
+imcstub "pciex8086,6f71"
+imcstub "pciex8086,6f79"
+imcstub "pciex8086,6fa0"
+imcstub "pciex8086,6fa8"
+imcstub "pciex8086,6faa"
+imcstub "pciex8086,6fab"
+imcstub "pciex8086,6fac"
+imcstub "pciex8086,6fad"
+imcstub "pciex8086,6ffc"
+imcstub "pciex8086,6ffd"
+intel_nb5000 "pci8086,25c0"
+intel_nb5000 "pci8086,25d0"
+intel_nb5000 "pci8086,25d4"
+intel_nb5000 "pci8086,25d8"
+intel_nb5000 "pci8086,3600"
+intel_nb5000 "pci8086,4000"
+intel_nb5000 "pci8086,4001"
+intel_nb5000 "pci8086,4003"
+intel_nb5000 "pci8086,65c0"
+intel_nhm "pci8086,3423"
+intel_nhm "pci8086,372a"
+intel_nhmex "pci8086,3422"
+intel_nhmex "pci8086,3438"
+ioat "pciex8086,1a38"
+ioat "pciex8086,360b"
+ioat "pciex8086,402f"
+iprb "pci8086,1029"
+iprb "pci8086,1030"
+iprb "pci8086,1031"
+iprb "pci8086,1032"
+iprb "pci8086,1038"
+iprb "pci8086,1039"
+iprb "pci8086,103d"
+iprb "pci8086,103d.8086.103d"
+iprb "pci8086,1050"
+iprb "pci8086,1050.8086.3020"
+iprb "pci8086,1050.8086.302f"
+iprb "pci8086,1050.8086.3427"
+iprb "pci8086,1059"
+iprb "pci8086,1064"
+iprb "pci8086,1068"
+iprb "pci8086,1069"
+iprb "pci8086,1092"
+iprb "pci8086,1209"
+iprb "pci8086,1229"
+iprb "pci8086,1229.8086.1"
+iprb "pci8086,1229.8086.10"
+iprb "pci8086,1229.8086.1009"
+iprb "pci8086,1229.8086.100c"
+iprb "pci8086,1229.8086.1012"
+iprb "pci8086,1229.8086.1013"
+iprb "pci8086,1229.8086.1015"
+iprb "pci8086,1229.8086.1016"
+iprb "pci8086,1229.8086.1017"
+iprb "pci8086,1229.8086.1030"
+iprb "pci8086,1229.8086.1040"
+iprb "pci8086,1229.8086.1041"
+iprb "pci8086,1229.8086.1042"
+iprb "pci8086,1229.8086.1050"
+iprb "pci8086,1229.8086.1051"
+iprb "pci8086,1229.8086.1052"
+iprb "pci8086,1229.8086.10f0"
+iprb "pci8086,1229.8086.11"
+iprb "pci8086,1229.8086.12"
+iprb "pci8086,1229.8086.1229"
+iprb "pci8086,1229.8086.13"
+iprb "pci8086,1229.8086.2"
+iprb "pci8086,1229.8086.2009"
+iprb "pci8086,1229.8086.200d"
+iprb "pci8086,1229.8086.200e"
+iprb "pci8086,1229.8086.200f"
+iprb "pci8086,1229.8086.2010"
+iprb "pci8086,1229.8086.2013"
+iprb "pci8086,1229.8086.2016"
+iprb "pci8086,1229.8086.2017"
+iprb "pci8086,1229.8086.2018"
+iprb "pci8086,1229.8086.2019"
+iprb "pci8086,1229.8086.2101"
+iprb "pci8086,1229.8086.2102"
+iprb "pci8086,1229.8086.2103"
+iprb "pci8086,1229.8086.2104"
+iprb "pci8086,1229.8086.2105"
+iprb "pci8086,1229.8086.2106"
+iprb "pci8086,1229.8086.2107"
+iprb "pci8086,1229.8086.2108"
+iprb "pci8086,1229.8086.2200"
+iprb "pci8086,1229.8086.2201"
+iprb "pci8086,1229.8086.2202"
+iprb "pci8086,1229.8086.2203"
+iprb "pci8086,1229.8086.2204"
+iprb "pci8086,1229.8086.2205"
+iprb "pci8086,1229.8086.2206"
+iprb "pci8086,1229.8086.2207"
+iprb "pci8086,1229.8086.2208"
+iprb "pci8086,1229.8086.2402"
+iprb "pci8086,1229.8086.2407"
+iprb "pci8086,1229.8086.2408"
+iprb "pci8086,1229.8086.2409"
+iprb "pci8086,1229.8086.240f"
+iprb "pci8086,1229.8086.2410"
+iprb "pci8086,1229.8086.2411"
+iprb "pci8086,1229.8086.2412"
+iprb "pci8086,1229.8086.2413"
+iprb "pci8086,1229.8086.3"
+iprb "pci8086,1229.8086.30"
+iprb "pci8086,1229.8086.3000"
+iprb "pci8086,1229.8086.3001"
+iprb "pci8086,1229.8086.3002"
+iprb "pci8086,1229.8086.3006"
+iprb "pci8086,1229.8086.3007"
+iprb "pci8086,1229.8086.3008"
+iprb "pci8086,1229.8086.3010"
+iprb "pci8086,1229.8086.3011"
+iprb "pci8086,1229.8086.3012"
+iprb "pci8086,1229.8086.301a"
+iprb "pci8086,1229.8086.31"
+iprb "pci8086,1229.8086.3411"
+iprb "pci8086,1229.8086.4"
+iprb "pci8086,1229.8086.40"
+iprb "pci8086,1229.8086.41"
+iprb "pci8086,1229.8086.42"
+iprb "pci8086,1229.8086.5"
+iprb "pci8086,1229.8086.50"
+iprb "pci8086,1229.8086.6"
+iprb "pci8086,1229.8086.7"
+iprb "pci8086,1229.8086.8"
+iprb "pci8086,1229.8086.9"
+iprb "pci8086,1229.8086.a"
+iprb "pci8086,1229.8086.b"
+iprb "pci8086,1229.8086.c"
+iprb "pci8086,1229.8086.d"
+iprb "pci8086,1229.8086.e"
+iprb "pci8086,1229.8086.f"
+iprb "pci8086,2449"
+iprb "pci8086,2449.8086.3010"
+iprb "pci8086,2449.8086.3011"
+iprb "pci8086,2449.8086.3012"
+iprb "pci8086,2449.8086.3013"
+iprb "pci8086,2449.8086.3014"
+iprb "pci8086,2449.8086.3015"
+iprb "pci8086,2449.8086.3016"
+iprb "pci8086,2449.8086.3017"
+iprb "pci8086,2449.8086.3018"
+iprb "pci8086,27dc"
+iprb "pci8086,27dc.8086.308d"
+isa "pciclass,060100"
+iwn "pci8086,82"
+iwn "pci8086,83"
+iwn "pci8086,84"
+iwn "pci8086,85"
+iwn "pci8086,87"
+iwn "pci8086,89"
+iwn "pci8086,8a"
+iwn "pci8086,8b"
+iwn "pci8086,8d"
+iwn "pci8086,8e"
+iwn "pci8086,90"
+iwn "pci8086,91"
+iwn "pci8086,887"
+iwn "pci8086,888"
+iwn "pci8086,88e"
+iwn "pci8086,88f"
+iwn "pci8086,890"
+iwn "pci8086,891"
+iwn "pci8086,892"
+iwn "pci8086,893"
+iwn "pci8086,894"
+iwn "pci8086,895"
+iwn "pci8086,896"
+iwn "pci8086,897"
+iwn "pci8086,8ae"
+iwn "pci8086,422b"
+iwn "pci8086,422c"
+iwn "pci8086,4236"
+iwn "pci8086,4238"
+iwn "pci8086,4239"
+ixgb "pci8086,1048"
+ixgb "pci8086,109e"
+ixgb "pci8086,1a48"
+ixgb "pci8086,a11f"
+ixgbe "pciex8086,10b6"
+ixgbe "pciex8086,10c6"
+ixgbe "pciex8086,10c7"
+ixgbe "pciex8086,10c8"
+ixgbe "pciex8086,10db"
+ixgbe "pciex8086,10dd"
+ixgbe "pciex8086,10e1"
+ixgbe "pciex8086,10ec"
+ixgbe "pciex8086,10f1"
+ixgbe "pciex8086,10f4"
+ixgbe "pciex8086,10f7"
+ixgbe "pciex8086,10f8"
+ixgbe "pciex8086,10f9"
+ixgbe "pciex8086,10fb"
+ixgbe "pciex8086,10fc"
+ixgbe "pciex8086,1507"
+ixgbe "pciex8086,1508"
+ixgbe "pciex8086,150b"
+ixgbe "pciex8086,1514"
+ixgbe "pciex8086,1517"
+ixgbe "pciex8086,151c"
+ixgbe "pciex8086,1528"
+ixgbe "pciex8086,154d"
+ixgbe "pciex8086,154a"
+ixgbe "pciex8086,1557"
+ixgbe "pciex8086,1558"
+ixgbe "pciex8086,1560"
+ixgbe "pciex8086,1563"
+ixgbe "pciex8086,15aa"
+ixgbe "pciex8086,15ab"
+ixgbe "pciex8086,15ac"
+ixgbe "pciex8086,15ad"
+ixgbe "pciex8086,15ae"
+ixgbe "pciex8086,15c2"
+ixgbe "pciex8086,15c3"
+ixgbe "pciex8086,15c4"
+ixgbe "pciex8086,15c6"
+ixgbe "pciex8086,15c7"
+ixgbe "pciex8086,15c8"
+ixgbe "pciex8086,15ca"
+ixgbe "pciex8086,15cc"
+ixgbe "pciex8086,15ce"
+ixgbe "pciex8086,15d1"
+ixgbe "pciex8086,15e4"
+ixgbe "pciex8086,15e5"
+kb8042 "pnpPNP,303"
+lsimega "pci1000,1960"
+lsimega "pci1000,407"
+lsimega "pci1000,407.1000.532"
+lsimega "pci1000,408"
+lsimega "pci1000,408.1000.2"
+lsimega "pci1000,409"
+lsimega "pci1028,13"
+marvell88sx "pci11ab,5040"
+marvell88sx "pci11ab,5041"
+marvell88sx "pci11ab,5080"
+marvell88sx "pci11ab,5081"
+marvell88sx "pci11ab,6041.9"
+marvell88sx "pci11ab,6081.9"
+mc-amd "pci1022,1100"
+mc-amd "pci1022,1101"
+mc-amd "pci1022,1102"
+mega_sas "pci1000,411.1000.1001"
+mega_sas "pci1000,411.1000.1002"
+mega_sas "pci1000,411.1000.1003"
+mega_sas "pci1000,411.1000.1004"
+mega_sas "pci1000,411.1000.1008"
+mega_sas "pci1000,411.1000.100c"
+mega_sas "pci1000,411.1000.100d"
+mega_sas "pci1000,411.1000.2004"
+mega_sas "pci1000,411.1000.2005"
+mega_sas "pci1000,411.1033.8287"
+mega_sas "pci1000,411.1054.3016"
+mega_sas "pci1000,411.1734.1081"
+mega_sas "pci1000,411.1734.10a3"
+mega_sas "pci1000,411.8086.1001"
+mega_sas "pci1000,411.8086.1003"
+mega_sas "pci1000,411.8086.1008"
+mega_sas "pci1000,411.8086.3490"
+mega_sas "pci1000,411.8086.3500"
+mega_sas "pci1000,411.8086.3501"
+mega_sas "pci1000,411.8086.3504"
+mega_sas "pci1000,411.8086.3507"
+mega_sas "pci1000,413.1000.1005"
+mega_sas "pci1000,57.8086.3002"
+mega_sas "pci1000,60.1000.1006"
+mega_sas "pci1000,60.1000.100a"
+mega_sas "pci1000,60.1000.100e"
+mega_sas "pci1000,60.1000.100f"
+mega_sas "pci1000,60.1000.1010"
+mega_sas "pci1000,60.1000.1011"
+mega_sas "pci1000,60.1000.1012"
+mega_sas "pci1000,60.1000.1013"
+mega_sas "pci1000,60.1000.1014"
+mega_sas "pci1000,60.1000.1015"
+mega_sas "pci1000,60.1000.1016"
+mega_sas "pci1000,60.1000.1017"
+mega_sas "pci1000,60.1000.1018"
+mega_sas "pci1000,60.1000.1019"
+mega_sas "pci1000,60.1000.101a"
+mega_sas "pci1000,60.1000.101b"
+mega_sas "pci1000,60.1000.1021"
+mega_sas "pci1000,60.1000.1022"
+mega_sas "pci1000,60.1014.363"
+mega_sas "pci1000,60.1014.364"
+mega_sas "pci1000,60.1014.365"
+mega_sas "pci1000,60.1014.379"
+mega_sas "pci1000,60.1014.3a2"
+mega_sas "pci1000,60.1014.3ac"
+mega_sas "pci1000,60.1028.1f0a"
+mega_sas "pci1000,60.1028.1f0b"
+mega_sas "pci1000,60.1028.1f0c"
+mega_sas "pci1000,60.1028.1f0d"
+mega_sas "pci1000,60.1028.1f11"
+mega_sas "pci1000,60.1033.835a"
+mega_sas "pci1000,60.1033.836e"
+mega_sas "pci1000,60.1043.824d"
+mega_sas "pci1000,60.1054.3019"
+mega_sas "pci1000,60.1170.2f"
+mega_sas "pci1000,60.1170.34"
+mega_sas "pci1000,60.1170.36"
+mega_sas "pci1000,60.1458.1000"
+mega_sas "pci1000,60.15d9.c080"
+mega_sas "pci1000,60.1734.10f9"
+mega_sas "pci1000,60.1734.1102"
+mega_sas "pci1000,60.1734.114b"
+mega_sas "pci1000,60.17aa.6b7c"
+mega_sas "pci1000,60.18a1.3"
+mega_sas "pci1000,60.19e5.2001"
+mega_sas "pci1000,60.19e5.2002"
+mega_sas "pci1000,60.19e5.2003"
+mega_sas "pci1000,60.19e5.2004"
+mega_sas "pci1000,60.19e5.2005"
+mega_sas "pci1000,60.19e5.2006"
+mega_sas "pci1000,60.19e5.2010"
+mega_sas "pci1000,60.19e5.2011"
+mega_sas "pci1000,60.19e5.2012"
+mega_sas "pci1000,60.19e5.2013"
+mega_sas "pci1000,60.19e5.2014"
+mega_sas "pci1000,60.19e5.2015"
+mega_sas "pci1000,60.19e5.2016"
+mega_sas "pci1000,60.19e5.2017"
+mega_sas "pci1000,60.19e5.2018"
+mega_sas "pci1000,60.19e5.2019"
+mega_sas "pci1000,60.19e5.201a"
+mega_sas "pci1000,60.19e5.d203"
+mega_sas "pci1000,60.1b0a.14"
+mega_sas "pci1000,60.1fca.2163"
+mega_sas "pci1000,60.1fca.2164"
+mega_sas "pci1000,60.8086.1006"
+mega_sas "pci1000,60.8086.100a"
+mega_sas "pci1000,60.8086.1010"
+mega_sas "pci1000,60.8086.1013"
+mega_sas "pci1000,60.8086.1021"
+mega_sas "pci1000,60.8086.34cc"
+mega_sas "pci1000,60.8086.34cd"
+mega_sas "pci1000,60.8086.34e4"
+mega_sas "pci1000,60.8086.3505"
+mega_sas "pci1000,60.8086.3508"
+mega_sas "pci1000,7c.1000.101c"
+mega_sas "pci1000,7c.1000.101d"
+mega_sas "pci1000,7c.1014.395"
+mega_sas "pci1028,15.1028.1f01"
+mega_sas "pci1028,15.1028.1f02"
+mega_sas "pci1028,15.1028.1f03"
+mouse8042 "pnpPNP,f03"
+mlxcx "pciex15b3,1013"
+mlxcx "pciex15b3,1014"
+mlxcx "pciex15b3,1015"
+mlxcx "pciex15b3,1016"
+mlxcx "pciex15b3,1017"
+mlxcx "pciex15b3,1018"
+mlxcx "pciex15b3,1019"
+mlxcx "pciex15b3,101a"
+mlxcx "pciex15b3,101b"
+mlxcx "pciex15b3,101c"
+mlxcx "pciex15b3,101d"
+mlxcx "pciex15b3,101e"
+mlxcx "pciex15b3,101f"
+mpt "pci1000,30"
+mpt "pci1000,50"
+mpt "pci1000,54"
+mpt "pci1000,56"
+mpt "pci1000,58"
+mpt "pci1000,62"
+mpt "pciex1000,56"
+mpt "pciex1000,58"
+mpt "pciex1000,62"
+mpt_sas "pci1000,64"
+mpt_sas "pci1000,70"
+mpt_sas "pci1000,72"
+mpt_sas "pci1000,76"
+mpt_sas "pciex1000,64"
+mpt_sas "pciex1000,65"
+mpt_sas "pciex1000,6e"
+mpt_sas "pciex1000,70"
+mpt_sas "pciex1000,72"
+mpt_sas "pciex1000,74"
+mpt_sas "pciex1000,76"
+mpt_sas "pciex1000,77"
+mpt_sas "pciex1000,7e"
+mpt_sas "pciex1000,80"
+mpt_sas "pciex1000,81"
+mpt_sas "pciex1000,82"
+mpt_sas "pciex1000,83"
+mpt_sas "pciex1000,84"
+mpt_sas "pciex1000,85"
+mpt_sas "pciex1000,86"
+mpt_sas "pciex1000,87"
+mpt_sas "pciex1000,90"
+mpt_sas "pciex1000,91"
+mpt_sas "pciex1000,92"
+mpt_sas "pciex1000,93"
+mpt_sas "pciex1000,94"
+mpt_sas "pciex1000,95"
+mpt_sas "pciex1000,96"
+mpt_sas "pciex1000,97"
+mpt_sas "pciex1000,aa"
+mpt_sas "pciex1000,ab"
+mpt_sas "pciex1000,ac"
+mpt_sas "pciex1000,ad"
+mpt_sas "pciex1000,ae"
+mpt_sas "pciex1000,af"
+mpt_sas "pciex1000,c0"
+mpt_sas "pciex1000,c1"
+mpt_sas "pciex1000,c2"
+mpt_sas "pciex1000,c3"
+mpt_sas "pciex1000,c4"
+mpt_sas "pciex1000,c5"
+mpt_sas "pciex1000,c6"
+mpt_sas "pciex1000,c7"
+mpt_sas "pciex1000,c8"
+mpt_sas "pciex1000,c9"
+mpt_sas "pciex1000,d0"
+mpt_sas "pciex1000,d1"
+mpt_sas "pciex1000,d2"
+mr_sas "pciex1000,52"
+mr_sas "pciex1000,53"
+mr_sas "pciex1000,5b"
+mr_sas "pciex1000,5d"
+mr_sas "pciex1000,5f"
+mr_sas "pciex1000,71"
+mr_sas "pciex1000,73"
+mr_sas "pciex1000,78"
+mr_sas "pciex1000,79"
+mr_sas "pciex1000,ce"
+mr_sas "pciex1000,cf"
+mwl "pci11ab,2a0a"
+mwl "pci11ab,2a24"
+mxfe "pci10d9,512"
+mxfe "pci10d9,531"
+mxfe "pci11ad,c115"
+mxfe "pci11fc,9881"
+myri10ge "pci14c1,8"
+myri10ge "pci14c1,9"
+myri10ge "pciex14c1,8"
+myri10ge "pciex14c1,9"
+nfp "pci1011,1065.100.100"
+nfp "pciex1011,1065.100.100"
+nfp "pci8086,b555.100.100"
+nfp "pciex8086,b555.100.100"
+nge "pci10de,268"
+nge "pci10de,269"
+nge "pci10de,37"
+nge "pci10de,372"
+nge "pci10de,373"
+nge "pci10de,38"
+nge "pci10de,3ee"
+nge "pci10de,3ef"
+nge "pci10de,56"
+nge "pci10de,57"
+nge "pci10de,760"
+nge "pci10de,ab0"
+nge "pci10de,df"
+nge "pci10de,e6"
+npe "pciex_root_complex"
+ntxn "pci4040,1"
+ntxn "pci4040,100"
+ntxn "pci4040,2"
+ntxn "pci4040,24"
+ntxn "pci4040,25"
+ntxn "pci4040,3"
+ntxn "pci4040,4"
+ntxn "pci4040,5"
+nulldriver "scsa,nodev"
+nulldriver "scsa,probe"
+nv_sata "pci10de,266"
+nv_sata "pci10de,267"
+nv_sata "pci10de,36"
+nv_sata "pci10de,37e"
+nv_sata "pci10de,37f"
+nv_sata "pci10de,3e"
+nv_sata "pci10de,3f6"
+nv_sata "pci10de,3f7"
+nv_sata "pci10de,54"
+nv_sata "pci10de,55"
+nvme "pciclass,010802"
+nvme "pciexclass,010802"
+nxge "SUNW,niusl"
+nxge "pciex108e,abcd"
+oce "pciex19a2,700"
+oce "pciex19a2,710"
+ohci "pciclass,0c0310"
+pcata "pccard,disk"
+pchtemp "pci8086,2f9,p"
+pchtemp "pci8086,6f9,p"
+pchtemp "pci8086,8ca4,p"
+pchtemp "pci8086,8c24,p"
+pchtemp "pci8086,8d24,p"
+pchtemp "pci8086,9ca4,p"
+pchtemp "pci8086,9d31,p"
+pchtemp "pci8086,9df9,p"
+pchtemp "pci8086,a1b1,p"
+pchtemp "pci8086,a231,p"
+pchtemp "pci8086,a2b1,p"
+pchtemp "pci8086,a131,p"
+pchtemp "pci8086,a379,p"
+pci_pci "pci1011,1"
+pci_pci "pci1011,21"
+pci_pci "pci1014,22"
+pci_pci "pciclass,060400"
+pci_pci "pciclass,060401"
+pcic "pciclass,060500"
+pcic "pciclass,060700"
+pcieb "pciexclass,060400"
+pcieb "pciexclass,060401"
+pcn "pci1022,2000"
+pcn "pci103c,104c"
+pcser "pccard,Intel_MODEM_2400+_iNC110US_A-"
+pcser "pccard,serial"
+pcser "pccard102,2"
+pcser "pccard102,5"
+pit_beep "SUNW,pit_beep"
+pmcs "pciex11f8,8001"
+pseudo "zconsnex"
+pseudo "zfdnex"
+pvscsi "pci15ad,7c0"
+qede "pciex1077,1634"
+qede "pciex1077,1629"
+qede "pciex1077,1630"
+qede "pciex1077,1656"
+qede "pciex1077,1654"
+qede "pciex1077,1666"
+qede "pciex1077,8070"
+qede "pciex1077,8071"
+qede "pciex1077,8072"
+qede "pciex1077,8073"
+qlc "pci1077,132"
+qlc "pci1077,2200"
+qlc "pci1077,2300"
+qlc "pci1077,2312"
+qlc "pci1077,2422"
+qlc "pciex1077,2432"
+qlc "pciex1077,2532"
+qlc "pciex1077,5432"
+qlc "pciex1077,8001"
+qlc "pciex1077,8021"
+qlge "pciex1077,8000"
+rge "pci10ec,8136"
+rge "pci10ec,8167"
+rge "pci10ec,8168"
+rge "pci10ec,8169"
+rge "pci16ec,116"
+rge "pciex10ec,8136"
+rge "pciex10ec,8168"
+rge "pciex10ec,8169"
+rtls "pci10ec,8139"
+rtls "pci1113,1211"
+rtls "pci1186,1300"
+rtls "pci1186,1301"
+rum "usb1044,800a"
+rum "usb13b1,20"
+rum "usb148f,2573"
+rum "usb15a9,4"
+rum "usb7d1,3c03"
+rum "usb7d1,3c04"
+rum "usbb05,1723"
+scsa1394 "firewire00609e,010483"
+scsa2usb "usb584,222"
+scsa2usb "usbif,class8"
+sd "scsiclass,00"
+sd "scsiclass,05"
+sdhost "pciclass,080500"
+sdhost "pciclass,080501"
+ses "scsiclass,03S"
+ses "scsiclass,0d"
+sfe "pci100b,20"
+sfe "pci1039,7016"
+sfe "pci1039,900"
+sfxge "pci1924,803"
+sfxge "pci1924,810"
+sfxge "pci1924,813"
+sfxge "pci1924,901"
+sfxge "pci1924,903"
+sfxge "pci1924,923"
+sgen "scsa,08.bfcp"
+sgen "scsa,08.bvhci"
+smrt "pci103c,1920"
+smrt "pci103c,1921"
+smrt "pci103c,1922"
+smrt "pci103c,1923"
+smrt "pci103c,1924"
+smrt "pci103c,1926"
+smrt "pci103c,1928"
+smrt "pci103c,21bd"
+smrt "pci103c,21be"
+smrt "pci103c,21bf"
+smrt "pci103c,21c0"
+smrt "pci103c,21c1"
+smrt "pci103c,21c2"
+smrt "pci103c,21c3"
+smrt "pci103c,21c5"
+smrt "pci103c,21c6"
+smrt "pci103c,21c7"
+smrt "pci103c,21c8"
+smrt "pci103c,21ca"
+smrt "pci103c,21cb"
+smrt "pci103c,21cc"
+smrt "pci103c,21cd"
+smrt "pci103c,21ce"
+smrt "pci103c,3241"
+smrt "pci103c,3243"
+smrt "pci103c,3245"
+smrt "pci103c,3247"
+smrt "pci103c,3249"
+smrt "pci103c,324a"
+smrt "pci103c,324b"
+smrt "pci103c,3350"
+smrt "pci103c,3351"
+smrt "pci103c,3352"
+smrt "pci103c,3353"
+smrt "pci103c,3354"
+smrt "pci103c,3355"
+smrt "pci103c,3356"
+si3124 "pci1095,3124"
+si3124 "pci1095,3132"
+skd "pciex1b39,1"
+st "scsiclass,01"
+t4nex "pciex1425,4400"
+t4nex "pciex1425,4401"
+t4nex "pciex1425,4402"
+t4nex "pciex1425,4403"
+t4nex "pciex1425,4404"
+t4nex "pciex1425,4405"
+t4nex "pciex1425,4406"
+t4nex "pciex1425,4407"
+t4nex "pciex1425,4408"
+t4nex "pciex1425,4409"
+t4nex "pciex1425,440a"
+t4nex "pciex1425,440d"
+t4nex "pciex1425,440e"
+t4nex "pciex1425,5400"
+t4nex "pciex1425,5401"
+t4nex "pciex1425,5402"
+t4nex "pciex1425,5403"
+t4nex "pciex1425,5404"
+t4nex "pciex1425,5405"
+t4nex "pciex1425,5406"
+t4nex "pciex1425,5407"
+t4nex "pciex1425,5408"
+t4nex "pciex1425,5409"
+t4nex "pciex1425,540a"
+t4nex "pciex1425,540b"
+t4nex "pciex1425,540c"
+t4nex "pciex1425,540d"
+t4nex "pciex1425,540e"
+t4nex "pciex1425,540f"
+t4nex "pciex1425,5410"
+t4nex "pciex1425,5411"
+t4nex "pciex1425,5412"
+t4nex "pciex1425,5413"
+t4nex "pciex1425,5414"
+t4nex "pciex1425,5415"
+t4nex "pciex1425,5416"
+t4nex "pciex1425,5417"
+t4nex "pciex1425,5418"
+t4nex "pciex1425,5480"
+t4nex "pciex1425,5481"
+t4nex "pciex1425,5482"
+t4nex "pciex1425,5486"
+t4nex "pciex1425,5487"
+t4nex "pciex1425,5488"
+t4nex "pciex1425,5489"
+t4nex "pciex1425,5490"
+t4nex "pciex1425,5491"
+t4nex "pciex1425,5492"
+t4nex "pciex1425,5493"
+t4nex "pciex1425,5494"
+t4nex "pciex1425,5495"
+t4nex "pciex1425,5496"
+t4nex "pciex1425,5497"
+t4nex "pciex1425,5498"
+t4nex "pciex1425,5499"
+t4nex "pciex1425,549a"
+t4nex "pciex1425,549b"
+t4nex "pciex1425,549c"
+t4nex "pciex1425,549d"
+t4nex "pciex1425,549e"
+t4nex "pciex1425,549f"
+t4nex "pciex1425,54a0"
+t4nex "pciex1425,54a1"
+t4nex "pciex1425,54a2"
+t4nex "pciex1425,54a3"
+t4nex "pciex1425,54a4"
+t4nex "pciex1425,54a5"
+t4nex "pciex1425,54a6"
+t4nex "pciex1425,54a7"
+t4nex "pciex1425,54a8"
+t4nex "pciex1425,54a9"
+t4nex "pciex1425,54aa"
+t4nex "pciex1425,54ab"
+t4nex "pciex1425,54ac"
+t4nex "pciex1425,54ad"
+t4nex "pciex1425,54ae"
+t4nex "pciex1425,54af"
+t4nex "pciex1425,54b0"
+t4nex "pciex1425,6400"
+t4nex "pciex1425,6401"
+t4nex "pciex1425,6402"
+t4nex "pciex1425,6403"
+t4nex "pciex1425,6404"
+t4nex "pciex1425,6405"
+t4nex "pciex1425,6406"
+t4nex "pciex1425,6407"
+t4nex "pciex1425,6408"
+t4nex "pciex1425,6409"
+t4nex "pciex1425,640d"
+t4nex "pciex1425,6410"
+t4nex "pciex1425,6411"
+t4nex "pciex1425,6414"
+t4nex "pciex1425,6415"
+t4nex "pciex1425,6480"
+t4nex "pciex1425,6481"
+t4nex "pciex1425,6482"
+t4nex "pciex1425,6483"
+t4nex "pciex1425,6484"
+t4nex "pciex1425,6485"
+t4nex "pciex1425,6486"
+t4nex "pciex1425,6487"
+t4nex "pciex1425,6488"
+t4nex "pciex1425,6489"
+t4nex "pciex1425,648a"
+t4nex "pciex1425,648b"
+t4nex "pciex1425,6492"
+tavor "pci15b3,5a44"
+tavor "pci15b3,5a45"
+tavor "pci15b3,6278"
+tavor "pci15b3,6279"
+tavor "pciex15b3,6278"
+tavor "pciex15b3,6279"
+udmf "usb7aa,9601"
+udmf "usba46,6688"
+udmf "usba46,268"
+udmf "usba46,9601"
+udmf "usbfe6,8101"
+ugen "usbif51d,class3"
+upf "usb7a6,8511"
+upf "usb411,5"
+upf "usb4bb,904"
+upf "usb4bb,93a"
+upf "usb506,4601"
+upf "usb557,2007"
+upf "usb7b8,110c"
+upf "usb7b8,4104"
+upf "usb7b8,4004"
+upf "usb7b8,4007"
+upf "usb7b8,4102"
+upf "usb7b8,4002"
+upf "usb7b8,400b"
+upf "usb7b8,400c"
+upf "usb7b8,abc1"
+upf "usb7b8,200c"
+upf "usb83a,1046"
+upf "usb83a,5046"
+upf "usb83a,b004"
+upf "usb7a6,8513"
+upf "usb7a6,8515"
+upf "usb7a6,986"
+upf "usb7a6,1986"
+upf "usb334,1701"
+upf "usb7c9,b100"
+upf "usb50d,121"
+upf "usb8dd,986"
+upf "usb8dd,987"
+upf "usb8dd,988"
+upf "usb8dd,8511"
+upf "usb49f,8511"
+upf "usb7aa,4"
+upf "usb7aa,d"
+upf "usb2001,4001"
+upf "usb2001,4002"
+upf "usb2001,4102"
+upf "usb2001,400b"
+upf "usb2001,200c"
+upf "usb2001,4003"
+upf "usb2001,abc1"
+upf "usbdb7,2"
+upf "usb1342,304"
+upf "usb56e,4010"
+upf "usb5cc,3000"
+upf "usb1044,8002"
+upf "usbe66,400c"
+upf "usb3f0,811c"
+upf "usb4bb,913"
+upf "usb951,a"
+upf "usb56e,4002"
+upf "usb56e,4005"
+upf "usb56e,400b"
+upf "usb56e,abc1"
+upf "usb56e,200c"
+upf "usb66b,2202"
+upf "usb66b,2203"
+upf "usb66b,2204"
+upf "usb66b,2206"
+upf "usb66b,8b4"
+upf "usb66b,400b"
+upf "usb66b,200c"
+upf "usb411,1"
+upf "usb411,9"
+upf "usb45e,7a"
+upf "usb846,1020"
+upf "usbb39,109"
+upf "usbb39,901"
+upf "usb8d1,3"
+upf "usb707,200"
+upf "usb707,201"
+upf "usb15e8,9100"
+upf "usb15e8,9110"
+upf "usb67c,1001"
+urf "usbbda,8150"
+urf "usb411,12"
+urf "usb3980,3"
+urf "usb7b8,401a"
+urf "usb1557,8150"
+urf "usb586,401a"
+uhci "pciclass,0c0300"
+usb_ac "usbif,class1.1"
+usb_as "usbif,class1.2"
+usb_ia "usb,ia"
+usb_mid "usb,device"
+usbecm "usb,class2.6.0"
+usbecm "usb430,a4a2"
+usbecm "usbia,class2.6"
+usbecm "usbif,class2.6"
+usbftdi "usb403,6001"
+usbftdi "usb403,6014"
+usbftdi "usb403,6015"
+usbftdi "usb403,cc48"
+usbftdi "usb403,cc49"
+usbftdi "usb403,cc4a"
+usbftdi "usb403,e888"
+usbftdi "usb403,e889"
+usbftdi "usb403,e88b"
+usbftdi "usb403,e88c"
+usbftdi "usb403,fa00"
+usbftdi "usb403,fa01"
+usbftdi "usb403,fa02"
+usbftdi "usb403,fa03"
+usbftdi "usb403,fa04"
+usbftdi "usb403,fc08"
+usbftdi "usb403,fc09"
+usbftdi "usb403,fc0b"
+usbftdi "usb403,fc0c"
+usbftdi "usb403,fc0d"
+usbftdi "usb403,fc82"
+usbftdi "usb411,00b3"
+usbftdi "usb7cc,0421"
+usbftdi "usb856,ac01"
+usbftdi "usb93c,0601"
+usbftdi "usb93c,0701"
+usbprn "usbif,class7.1"
+usbsacm "usb,class2.2.0"
+usbsacm "usb1410,1110"
+usbsacm "usbc88,17da"
+usbsacm "usbif,class2.2"
+usbser_edge "usbif1608,1.100.config1.0"
+usbser_edge "usbif1608,1.config1.0"
+usbser_edge "usbif1608,10.config1.0"
+usbser_edge "usbif1608,11.config1.0"
+usbser_edge "usbif1608,12.config1.0"
+usbser_edge "usbif1608,13.config1.0"
+usbser_edge "usbif1608,14.config1.0"
+usbser_edge "usbif1608,201.config1.0"
+usbser_edge "usbif1608,205.config1.0"
+usbser_edge "usbif1608,206.config1.0"
+usbser_edge "usbif1608,207.config1.0"
+usbser_edge "usbif1608,20c.config1.0"
+usbser_edge "usbif1608,20d.config1.0"
+usbser_edge "usbif1608,215.config1.0"
+usbser_edge "usbif1608,217.config1.0"
+usbser_edge "usbif1608,21a.config1.0"
+usbser_edge "usbif1608,240.config1.0"
+usbser_edge "usbif1608,241.config1.0"
+usbser_edge "usbif1608,242.config1.0"
+usbser_edge "usbif1608,243.config1.0"
+usbser_edge "usbif1608,244.config1.0"
+usbser_edge "usbif1608,247.config1.0"
+usbser_edge "usbif1608,3.config1.0"
+usbser_edge "usbif1608,4.config1.0"
+usbser_edge "usbif1608,5.config1.0"
+usbser_edge "usbif1608,6.config1.0"
+usbser_edge "usbif1608,7.config1.0"
+usbser_edge "usbif1608,c.config1.0"
+usbser_edge "usbif1608,d.config1.0"
+usbser_edge "usbif1608,e.config1.0"
+usbser_edge "usbif1608,f.config1.0"
+usbsksp "usb6cd,11a"
+usbsksp "usb6cd,121"
+usbsksp "usb6cd,12a"
+usbsksp "usb6cd,131"
+usbsprl "usb557,2008"
+usbsprl "usb56e,5004"
+usbsprl "usb5ad,fba"
+usbsprl "usb6189,2068"
+usbsprl "usb67b,2303"
+usbsprl "usb67b,aaa2"
+vgatext "pciclass,000100"
+vgatext "pciclass,030000"
+vgatext "pciclass,030001"
+vio9f "pci1af4,9"
+vioblk "pci1af4,1001"
+vioif "pci1af4,1"
+vioif "pci1af4,1000,p"
+vioscsi "pci1af4,1004"
+vmxnet "pci15ad,720"
+vmxnet3s "pci15ad,7b0"
+vr "pci1106,3043"
+vr "pci1106,3053"
+vr "pci1106,3065"
+vr "pci1106,3106"
+xge "pci17d5,5731"
+xge "pci17d5,5831"
+xge "pci17d5,5832"
+xhci "pciclass,0c0330"
+xnbe "xnb,ioemu"
+xnbo "xnb"
+xnbo "xnb,SUNW_mac"
+xnbu "xnb,netfront"
+xpv "pci5853,1.1"
+yge "pciex1186,4b00"
+yge "pciex11ab,4354"
+yge "pciex11ab,4355"
+yge "pciex11ab,4362"
+yge "pciex11ab,4363"
+yge "pciex11ab,4364"
+yge "pciex11ab,436a"
diff --git a/usr/src/uts/intel/os/driver_classes b/usr/src/uts/intel/os/driver_classes
index e69de29bb2..b1bd2d0ae4 100644
--- a/usr/src/uts/intel/os/driver_classes
+++ b/usr/src/uts/intel/os/driver_classes
@@ -0,0 +1,29 @@
+scsi_vhci scsi-self-identifying
+mpt scsi
+pci_pci pci
+cpqary3 scsi
+aac scsi
+adpu320 scsi
+ahci scsi-self-identifying
+amr scsi
+arcmsr scsi
+bcm_sata scsi-self-identifying
+isa sysbus
+pci pci
+emlxs fibre-channel
+iscsi scsi-self-identify
+lsimega scsi
+mpt_sas scsi-self-identifying
+mr_sas scsi-self-identifying
+mega_sas scsi
+marvell88sx scsi-self-identifying
+nv_sata scsi-self-identifying
+pcic pcmcia
+glm scsi
+pmcs scsi-self-identifying
+ata dada
+ata scsi
+qlc fibre-channel
+si3124 scsi-self-identifying
+skd disk
+smrt scsi-self-identifying
diff --git a/usr/src/uts/intel/os/minor_perm b/usr/src/uts/intel/os/minor_perm
index dbc6a35081..3b4971e69b 100644
--- a/usr/src/uts/intel/os/minor_perm
+++ b/usr/src/uts/intel/os/minor_perm
@@ -1,6 +1,272 @@
+spwr:* 0666 root sys
+dump:dump 0660 root sys
+id:* 0640 root sys
+kstat:* 0666 root sys
+ksyms:* 0666 root sys
+logi:l 0666 root sys
+msm:l 0666 root sys
+pts:* 0644 root sys
+pts:0 0620 root tty
+pts:1 0620 root tty
+pts:2 0620 root tty
+pts:3 0620 root tty
+svvslo:* 0666 root sys
+vol:volctl 0666 root sys
+tnf:tnfctl 0600 root sys
+tnf:tnfmap 0600 root sys
+fssnap:* 0640 root sys
+fssnap:ctl 0666 root sys
+rsm:* 0666 root sys
+vni:* 0666 root sys
+bmc:bmc 0666 root sys
+glm:* 0755 root sys
+usbsacm:* 0666 root sys
+sdpib:* 0644 root sys
+i915:* 0644 root sys
+rdc:* 0666 root sys
+md:* 0640 root sys
+md:admin 0644 root sys
+iscsi:* 0600 root sys
+dcam1394:* 0666 root sys
+nv_sata:* 0644 root sys
+smp:* 0644 root sys
+sv:* 0666 root sys
+dmfe:* 0666 root sys
+clone:dmfe 0666 root sys
+clone:ptmx 0666 root sys
+ehci:* 0644 root sys
+hid:* 0600 root sys
+hubd:* 0644 root sys
+hwahc:* 0644 root sys
+hwarc:* 0644 root sys
+ohci:* 0644 root sys
+uhci:* 0644 root sys
+usb_ac:* 0600 root sys
+usb_as:* 0600 root sys
+usbprn:* 0666 root sys
+wusb_ca:* 0666 root sys
+wusb_df:* 0666 root sys
+ipw:* 0666 root sys
+clone:ipw 0666 root sys
+fcip:* 0600 root sys
+ral:* 0666 root sys
+clone:ral 0666 root sys
+daplt:* 0644 root sys
+atiatom:* 0644 root sys
+rtw:* 0666 root sys
+clone:rtw 0666 root sys
+usbsksp:* 0666 root sys
+wpi:* 0666 root sys
+clone:wpi 0666 root sys
+pcser:* 0666 uucp uucp
+dtrace:* 0666 root sys
+fasttrap:fasttrap 0666 root sys
+fbt:fbt 0644 root sys
+lockstat:* 0644 root sys
+profile:profile 0644 root sys
+sdt:sdt 0644 root sys
+systrace:systrace 0644 root sys
+rum:* 0666 root sys
+clone:rum 0666 root sys
+ce:* 0600 root sys
+rge:* 0666 root sys
+clone:rge 0666 root sys
+fcsm:* 0600 root sys
+bge:* 0666 root sys
+clone:bge 0666 root sys
+winlock:* 0666 root sys
+fp:* 0600 root sys
+fm:* 0644 root sys
+ipf:* 0666 root sys
+smbsrv:* 0640 root sys
+bnx:* 0644 root sys
+pcwl:* 0666 root sys
+clone:pcwl 0666 root sys
+pcan:* 0666 root sys
+clone:pcan 0666 root sys
+ural:* 0666 root sys
+clone:ural 0666 root sys
+mxfe:* 0666 root sys
+clone:mxfe 0666 root sys
+ugen:* 0644 root sys
+zyd:* 0666 root sys
+clone:zyd 0666 root sys
+qlc:* 0666 root sys
+qlge:* 0666 root sys
+igb:* 0666 root sys
+clone:igb 0666 root sys
+afe:* 0666 root sys
+clone:afe 0666 root sys
+scsa1394:* 0666 root sys
+agpgart:* 0644 root sys
+agptarget:* 0644 root sys
+amd64_gart:* 0644 root sys
+nsmb:* 0666 root sys
+fcoet:* 0600 root sys
+nvidia:* 0666 root root
+fcp:* 0600 root sys
+ahci:* 0644 root sys
+adpu320:adpu320ctl 0666 root root
+ses:* 0666 bin bin
+iwk:* 0666 root sys
+clone:iwk 0666 root sys
+iwh:* 0666 root sys
+clone:iwh 0666 root sys
+amd_iommu:* 0644 root sys
+balloon:* 0444 root sys
+domcaps:* 0444 root sys
+evtchn:* 0666 root sys
+privcmd:* 0666 root sys
+xenbus:* 0666 root sys
+xpvtap:* 0666 root sys
+ixgb:* 0666 root root
+pm:* 0666 root sys
+usbvc:* 0666 root sys
+hci1394:* 0600 root sys
+rpcib:* 0644 root sys
+nxge:* 0600 root sys
+audio:* 0666 root sys
+fcoe:* 0600 root sys
+usbser_edge:* 0666 root sys
+srpt:* 0644 root sys
+pool:pool 0666 root sys
+pool:poolctl 0666 root sys
+e1000g:* 0666 root sys
+clone:e1000g 0666 root sys
+sdp:* 0644 root sys
+sdp:sdp 0666 root sys
+ixgbe:* 0666 root sys
+clone:ixgbe 0666 root sys
+xge:* 0666 root sys
+clone:xge 0666 root sys
+aggr:* 0666 root sys
+arp:arp 0666 root sys
+bl:* 0666 root sys
+conskbd:kbd 0666 root sys
+clone:bridge 0666 root sys
+cn:* 0620 root tty
+consms:mouse 0666 root sys
+cpuid:self 0644 root sys
+crypto:crypto 0666 root sys
+cryptoadm:cryptoadm 0644 root sys
+dld:* 0666 root sys
devinfo:devinfo 0640 root sys
devinfo:devinfo,ro 0444 root sys
+dlpistub:* 0666 root sys
+icmp:icmp 0666 root sys
+icmp6:icmp6 0666 root sys
+ip:ip 0666 root sys
+ip6:ip6 0666 root sys
+ipnet:lo0 0666 root sys
+ipsecah:ipsecah 0666 root sys
+ipsecesp:ipsecesp 0666 root sys
+keysock:keysock 0666 root sys
+clone:llc1 0666 root sys
+lofi:* 0600 root sys
+lofi:ctl 0644 root sys
+log:conslog 0666 root sys
+log:log 0640 root sys
+mm:allkmem 0600 root sys
+mm:full 0666 root sys
+mm:kmem 0640 root sys
+mm:mem 0640 root sys
+mm:null 0666 root sys
+mm:zero 0666 root sys
+openeepr:openprom 0640 root sys
+ptc:* 0666 root sys
+poll:* 0666 root sys
+physmem:* 0600 root sys
+ptsl:* 0666 root sys
+rts:rts 0666 root sys
+random:* 0644 root sys
+sad:admin 0666 root sys
+sad:user 0666 root sys
+scsi_vhci:* 0666 root sys
+simnet:* 0666 root sys
+clone:simnet 0666 root sys
+sgen:* 0600 root sys
+spdsock:spdsock 0666 root sys
+st:* 0666 root sys
+sy:tty 0666 root tty
+sysevent:* 0600 root sys
+sysmsg:msglog 0600 root sys
+sysmsg:sysmsg 0600 root sys
+tcp:tcp 0666 root sys
+tcp6:tcp6 0666 root sys
+tl:* 0666 root sys
+clone:ticlts 0666 root sys
+clone:ticots 0666 root sys
+clone:ticotsord 0666 root sys
+udp:udp 0666 root sys
+vnic:* 0666 root sys
+clone:vnic 0666 root sys
+udp6:udp6 0666 root sys
+wc:* 0600 root sys
+acpi_drv:* 0666 root sys
+ramdisk:* 0600 root sys
+ramdisk:ctl 0644 root sys
+sd:* 0640 root sys
+smbios:smbios 0444 root sys
+ucode:* 0644 root sys
+iser:* 0600 root sys
+ath:* 0666 root sys
+clone:ath 0666 root sys
+usbsprl:* 0666 root sys
+marvell88sx:* 0644 root sys
+rtls:* 0666 root sys
+clone:rtls 0666 root sys
+iwi:* 0666 root sys
+clone:iwi 0666 root sys
+arcmsr:* 0600 root sys
+hxge:* 0600 root sys
+cpc:shared 0666 root sys
+emlxs:* 0666 root sys
+oce:* 0666 root sys
+tavor:* 0666 root sys
+zfs:* 0600 root sys
+zfs:zfs 0666 root sys
+iscsit:* 0600 root sys
+dnet:* 0666 root sys
+clone:dnet 0666 root sys
+elxl:* 0666 root sys
+clone:elxl 0666 root sys
+iprb:* 0666 root sys
+clone:iprb 0666 root sys
+pcn:* 0666 root sys
+clone:pcn 0666 root sys
+sfe:* 0666 root root
+chxge:* 0666 root sys
+clone:chxge 0666 root sys
+av1394:* 0600 root sys
+vscan:* 0640 root sys
+si3124:* 0644 root sys
+nge:* 0666 root sys
+clone:nge 0666 root sys
asy:* 0666 root sys
asy:*,cu 0600 uucp uucp
-md:* 0640 root sys
-md:admin 0644 root sys
+cmdk:* 0640 root sys
+ecpp:* 0666 root sys
+fd:* 0666 root sys
+fct:* 0666 root sys
+pppt:* 0666 root sys
+qlt:* 0666 root sys
+stmf:* 0666 root sys
+stmf_sbd:* 0666 root sys
+bpf:bpf 0666 root sys
+ib:* 0644 root sys
+sdhost:* 0644 root root
+vboxdrv:'* 0666 root sys'
+vboxusbmon:'* 0666 root sys'
+blkdev:* 0640 root root
+ibp:* 0666 root sys
+clone:ibp 0666 root sys
+ipmi:ipmi 0600 root sys
+lx_ptm:lx_ptmajor 0666 root sys
+lx_systrace:* 0644 root sys
+inotify:* 0666 root sys
+skd:* 0600 root sys
+eventfd:* 0666 root sys
+timerfd:* 0666 root sys
+signalfd:* 0666 root sys
+iwn:* 0666 root sys
+clone:iwn 0666 root sys
diff --git a/usr/src/uts/intel/os/name_to_major b/usr/src/uts/intel/os/name_to_major
index c5ad4c9bf0..65cd9053b7 100644
--- a/usr/src/uts/intel/os/name_to_major
+++ b/usr/src/uts/intel/os/name_to_major
@@ -2,3 +2,316 @@ md 85
devinfo 88
asy 106
did 239
+dump 0
+fssnap 1
+kstat 2
+ksyms 3
+logindmux 4
+ptm 5
+pts 6
+aggr 7
+arp 8
+bl 9
+clone 10
+conskbd 11
+bridge 12
+cn 13
+consms 14
+cpuid 15
+crypto 16
+cryptoadm 17
+dld 18
+dlpistub 19
+icmp 20
+icmp6 21
+ip 22
+ip6 23
+ipnet 24
+ippctl 25
+ipsecah 26
+ipsecesp 27
+iptun 28
+bnx 29
+iwscn 30
+kb8042 31
+keysock 32
+kmdb 33
+llc1 35
+lofi 36
+log 37
+mm 38
+mouse8042 39
+nulldriver 40
+openeepr 41
+options 42
+ptc 43
+poll 44
+physmem 45
+pseudo 46
+ptsl 47
+rts 48
+random 49
+sad 50
+scsi_vhci 51
+kvm 52
+simnet 54
+sgen 55
+softmac 56
+spdsock 57
+st 58
+sy 59
+sysevent 60
+sysmsg 61
+tcp 62
+tcp6 63
+tl 64
+udp 65
+vnic 66
+udp6 67
+wc 68
+acpi_drv 69
+bscbus 70
+bscv 71
+cpunex 72
+i8042 73
+intel_nb5000 74
+intel_nhm 75
+intel_nhmex 76
+mc-amd 77
+mpt 78
+pci_pci 79
+pcieb 80
+power 81
+ramdisk 82
+sd 83
+smbios 84
+tzmon 86
+ucode 87
+vgatext 89
+zfs 90
+pool 91
+zcons 92
+bnxe 93
+cpqary3 94
+hci1394 95
+audio 96
+audio1575 97
+audioens 98
+audiopci 99
+audiots 100
+audio810 101
+adpu320 102
+afe 103
+amd64_gart 104
+agpgart 105
+agptarget 107
+ahci 108
+amd8111s 109
+amr 110
+heci 111
+arcmsr 112
+atge 113
+bcm_sata 114
+bfe 115
+bge 116
+acpippm 117
+amd_iommu 118
+balloon 119
+cpudrv 120
+evtchn 121
+domcaps 122
+isa 123
+pit_beep 124
+npe 125
+ppm 126
+rootnex 127
+pci 128
+privcmd 129
+acpinex 130
+ehci 131
+hid 132
+hubd 133
+hwahc 134
+hwarc 135
+ohci 136
+scsa2usb 137
+uhci 138
+usb_ac 139
+usb_as 140
+usb_ia 141
+usb_mid 142
+usbprn 143
+wusb_ca 144
+wusb_df 145
+xpvtap 146
+xdb 147
+xdf 148
+xencons 149
+xnbe 150
+xnbu 151
+xpvd 152
+xnf 153
+xnbo 154
+xenbus 155
+ce 156
+chxge 157
+cpc 158
+srn 159
+ioat 160
+dmfe 161
+i915 162
+dtrace 163
+dcpc 164
+fasttrap 165
+fbt 166
+profile 167
+lockstat 168
+sdt 169
+systrace 170
+emlxs 171
+oce 172
+fcp 173
+fp 174
+fcip 175
+fcsm 176
+fipe 177
+fm 178
+dcam1394 179
+hme 180
+hxge 181
+ib 182
+sdpib 183
+sdp 184
+igb 185
+e1000g 186
+ipf 187
+iscsi 189
+iser 190
+ixgb 191
+ixgbe 192
+bpf 193
+lsimega 194
+vioif 195
+overlay 196
+vio9f 197
+mpt_sas 198
+mr_sas 199
+mega_sas 200
+marvell88sx 201
+mwl 202
+mxfe 203
+myri10ge 204
+nge 205
+ntxn 206
+nv_sata 207
+nxge 208
+pcn 209
+dnet 210
+elxl 211
+iprb 212
+pcs 213
+pcic 214
+pcser 215
+glm 216
+pm 217
+pmcs 218
+sppp 219
+sppptun 220
+fd 221
+fdc 222
+ecpp 223
+cmdk 225
+pci-ide 226
+ata 227
+pcata 228
+qlc 229
+qlge 230
+rge 231
+rpcib 232
+rtls 233
+rum 234
+scsa1394 235
+sdhost 237
+ses 238
+sfe 240
+si3124 241
+nsmb 242
+smp 243
+tavor 244
+tpm 245
+usbsacm 246
+daplt 247
+usbser_edge 248
+usbftdi 249
+ugen 250
+usbsksp 251
+usbsprl 252
+audiovia823x 253
+vr 254
+xge 255
+xpv 256
+yge 257
+zyd 258
+fct 259
+stmf 260
+stmf_sbd 261
+pppt 262
+qlt 263
+ibp 264
+blkdev 265
+smbsrv 266
+aac 267
+vioblk 53
+iscsit 269
+vmxnet 270
+ipd 188
+tap 271
+tun 272
+ipmi 273
+t4nex 275
+cxgbe 276
+virtio 277
+usbgem 278
+axf 279
+udmf 280
+upf 281
+urf 282
+vnd 283
+lx_ptm 284
+lx_systrace 286
+nfp 287
+inotify 288
+skd 289
+eventfd 290
+zfd 291
+timerfd 292
+signalfd 293
+nvme 294
+lxautofs 295
+i40e 296
+sfxge 297
+pvscsi 298
+vmxnet3s 299
+smrt 300
+iwn 301
+xhci 302
+qede 303
+vioscsi 304
+vmm 305
+viona 306
+ppt 307
+coretemp 308
+amdf17nbdf 309
+pchtemp 310
+ufm 311
+ufmtest 312
+imcstub 313
+imc 314
+ccid 315
+ksensor 316
+mlxcx 317
+amdzen_stub 318
+amdzen 319
+smntemp 320
+ena 321
+zen_umc 322
diff --git a/usr/src/uts/intel/os/path_to_inst b/usr/src/uts/intel/os/path_to_inst
index 180715f4cd..9853ae8e23 100644
--- a/usr/src/uts/intel/os/path_to_inst
+++ b/usr/src/uts/intel/os/path_to_inst
@@ -1 +1,3 @@
-#path_to_inst_bootstrap_1
+#
+# Caution! This file contains critical kernel state
+#
diff --git a/usr/src/uts/intel/os/sendsig.c b/usr/src/uts/intel/os/sendsig.c
index e3d60eb62b..becea9eeec 100644
--- a/usr/src/uts/intel/os/sendsig.c
+++ b/usr/src/uts/intel/os/sendsig.c
@@ -20,6 +20,9 @@
*/
/*
+ * Copyright 2015 Joyent, Inc.
+ */
+/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -87,6 +90,8 @@
#include <sys/kdi.h>
#include <sys/contract_impl.h>
#include <sys/x86_archext.h>
+#include <sys/brand.h>
+#include <sys/sdt.h>
/*
* Construct the execution environment for the user's signal
@@ -185,7 +190,18 @@ sendsig(int sig, k_siginfo_t *sip, void (*hdlr)())
newstack = sigismember(&PTOU(curproc)->u_sigonstack, sig) &&
!(lwp->lwp_sigaltstack.ss_flags & (SS_ONSTACK|SS_DISABLE));
- if (newstack) {
+ /*
+ * If this is a branded process, the brand may provide an alternate
+ * stack pointer for signal delivery:
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig_stack != NULL) {
+ /*
+ * Use the stack pointer value provided by the brand,
+ * accounting for the 128-byte reserved region.
+ */
+ newstack = 0;
+ fp = BROP(p)->b_sendsig_stack(sig) - STACK_RESERVE;
+ } else if (newstack) {
fp = (caddr_t)(SA((uintptr_t)lwp->lwp_sigaltstack.ss_sp) +
SA(lwp->lwp_sigaltstack.ss_size) - STACK_ALIGN);
} else {
@@ -295,6 +311,8 @@ sendsig(int sig, k_siginfo_t *sip, void (*hdlr)())
kmem_free(tuc, sizeof (*tuc));
tuc = NULL;
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, (uintptr_t)uc);
lwp->lwp_oldcontext = (uintptr_t)uc;
if (newstack) {
@@ -344,6 +362,14 @@ sendsig(int sig, k_siginfo_t *sip, void (*hdlr)())
}
/*
+ * Allow the brand to perform additional book-keeping once the signal
+ * handling frame has been fully assembled:
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig != NULL) {
+ BROP(p)->b_sendsig(sig);
+ }
+
+ /*
* Don't set lwp_eosys here. sendsig() is called via psig() after
* lwp_eosys is handled, so setting it here would affect the next
* system call.
@@ -419,7 +445,17 @@ sendsig32(int sig, k_siginfo_t *sip, void (*hdlr)())
newstack = sigismember(&PTOU(curproc)->u_sigonstack, sig) &&
!(lwp->lwp_sigaltstack.ss_flags & (SS_ONSTACK|SS_DISABLE));
- if (newstack) {
+ /*
+ * If this is a branded process, the brand may provide an alternate
+ * stack pointer for signal delivery:
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig_stack != NULL) {
+ /*
+ * Use the stack pointer value provided by the brand:
+ */
+ newstack = 0;
+ fp = BROP(p)->b_sendsig_stack(sig);
+ } else if (newstack) {
fp = (caddr_t)(SA32((uintptr_t)lwp->lwp_sigaltstack.ss_sp) +
SA32(lwp->lwp_sigaltstack.ss_size) - STACK_ALIGN32);
} else if ((rp->r_ss & 0xffff) != UDS_SEL) {
@@ -434,8 +470,9 @@ sendsig32(int sig, k_siginfo_t *sip, void (*hdlr)())
USEGD_GETBASE(&ldt[SELTOIDX(rp->r_ss)]);
else
fp = (caddr_t)rp->r_sp;
- } else
+ } else {
fp = (caddr_t)rp->r_sp;
+ }
/*
* Force proper stack pointer alignment, even in the face of a
@@ -516,6 +553,8 @@ sendsig32(int sig, k_siginfo_t *sip, void (*hdlr)())
kmem_free(tuc, sizeof (*tuc));
tuc = NULL;
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext, uintptr_t, (uintptr_t)uc);
lwp->lwp_oldcontext = (uintptr_t)uc;
if (newstack) {
@@ -565,6 +604,14 @@ sendsig32(int sig, k_siginfo_t *sip, void (*hdlr)())
}
/*
+ * Allow the brand to perform additional book-keeping once the signal
+ * handling frame has been fully assembled:
+ */
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig != NULL) {
+ BROP(p)->b_sendsig(sig);
+ }
+
+ /*
* Don't set lwp_eosys here. sendsig() is called via psig() after
* lwp_eosys is handled, so setting it here would affect the next
* system call.
diff --git a/usr/src/uts/intel/p4_pcbe/Makefile b/usr/src/uts/intel/p4_pcbe/Makefile
index d7f594ed3f..730c3c4b07 100644
--- a/usr/src/uts/intel/p4_pcbe/Makefile
+++ b/usr/src/uts/intel/p4_pcbe/Makefile
@@ -34,7 +34,7 @@ UTSBASE = ../..
#
MODULE = pcbe.GenuineIntel.15
OBJECTS = $(P4_PCBE_OBJS:%=$(OBJS_DIR)/%)
-ROOTMODULE = $(USR_PCBE_DIR)/$(MODULE)
+ROOTMODULE = $(ROOT_PSM_PCBE_DIR)/$(MODULE)
#
# Include common rules.
diff --git a/usr/src/uts/intel/pcbe/core_pcbe.c b/usr/src/uts/intel/pcbe/core_pcbe.c
index f2b6d07861..ad92c2f62f 100644
--- a/usr/src/uts/intel/pcbe/core_pcbe.c
+++ b/usr/src/uts/intel/pcbe/core_pcbe.c
@@ -819,7 +819,7 @@ core_pcbe_init(void)
for (i = 0; i < num_gpc; i++) {
/*
- * Determine length of all supported event names
+ * determine length of all supported event names
* (architectural + non-architectural)
*/
size = arch_events_string_length;
diff --git a/usr/src/uts/intel/sys/machbrand.h b/usr/src/uts/intel/sys/machbrand.h
index 3f9ebdb6b7..ad7f631649 100644
--- a/usr/src/uts/intel/sys/machbrand.h
+++ b/usr/src/uts/intel/sys/machbrand.h
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _SYS_MACHBRAND_H
@@ -32,20 +33,25 @@ extern "C" {
#ifndef _ASM
#include <sys/model.h>
+#include <sys/thread.h>
struct brand_mach_ops {
void (*b_sysenter)(void);
+ void (*b_int80)(void);
void (*b_int91)(void);
void (*b_syscall)(void);
void (*b_syscall32)(void);
+ greg_t (*b_fixsegreg)(greg_t, model_t);
+ uintptr_t (*b_fsbase)(klwp_t *, uintptr_t);
};
#endif /* _ASM */
#define BRAND_CB_SYSENTER 0
-#define BRAND_CB_INT91 1
-#define BRAND_CB_SYSCALL 2
-#define BRAND_CB_SYSCALL32 3
+#define BRAND_CB_INT80 1
+#define BRAND_CB_INT91 2
+#define BRAND_CB_SYSCALL 3
+#define BRAND_CB_SYSCALL32 4
#ifdef __cplusplus
}
diff --git a/usr/src/uts/intel/sys/segments.h b/usr/src/uts/intel/sys/segments.h
index 774f2fd3b8..4bf3d87378 100644
--- a/usr/src/uts/intel/sys/segments.h
+++ b/usr/src/uts/intel/sys/segments.h
@@ -709,6 +709,8 @@ extern void _start(), cmnint();
extern void achktrap(), mcetrap();
extern void xmtrap();
extern void fasttrap();
+extern void sys_int80();
+extern void brand_sys_int80();
extern void dtrace_ret();
/* KPTI trampolines */
@@ -724,6 +726,8 @@ extern void tr_overrun(), tr_resvtrap();
extern void tr_achktrap(), tr_mcetrap();
extern void tr_xmtrap();
extern void tr_fasttrap();
+extern void tr_sys_int80();
+extern void tr_brand_sys_int80();
extern void tr_dtrace_ret();
#if !defined(__amd64)
diff --git a/usr/src/uts/intel/sys/ucontext.h b/usr/src/uts/intel/sys/ucontext.h
index 66300e71a1..2d4e39b3e8 100644
--- a/usr/src/uts/intel/sys/ucontext.h
+++ b/usr/src/uts/intel/sys/ucontext.h
@@ -20,6 +20,7 @@
*/
/*
+ * Copyright 2015 Joyent, Inc.
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
@@ -84,9 +85,16 @@ struct __ucontext {
sigset_t uc_sigmask;
stack_t uc_stack;
mcontext_t uc_mcontext;
- long uc_filler[5]; /* see ABI spec for Intel386 */
+ /*
+ * The Intel386 ABI specification includes a 5-element array of longs
+ * called "uc_filler", padding the size of the struct to 512 bytes. To
+ * allow zone brands to communicate extra data right the way through
+ * the signal handling process, from sigacthandler to setcontext, we
+ * steal the first three of these longs as a brand-private member.
+ */
+ void *uc_brand_data[3];
+ long uc_filler[2];
};
-
#if defined(_SYSCALL32)
/* Kernel view of user ILP32 ucontext structure */
@@ -97,7 +105,8 @@ typedef struct ucontext32 {
sigset_t uc_sigmask;
stack32_t uc_stack;
mcontext32_t uc_mcontext;
- int32_t uc_filler[5];
+ caddr32_t uc_brand_data[3];
+ int32_t uc_filler[2];
} ucontext32_t;
#if defined(_KERNEL)
diff --git a/usr/src/uts/intel/syscall/getcontext.c b/usr/src/uts/intel/syscall/getcontext.c
index f7c404ba72..a210448dc3 100644
--- a/usr/src/uts/intel/syscall/getcontext.c
+++ b/usr/src/uts/intel/syscall/getcontext.c
@@ -20,6 +20,9 @@
*/
/*
+ * Copyright 2015 Joyent, Inc.
+ */
+/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -46,6 +49,7 @@
#include <sys/schedctl.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
+#include <sys/sdt.h>
/*
* Save user context.
@@ -125,7 +129,23 @@ savecontext(ucontext_t *ucp, const k_sigset_t *mask)
else
ucp->uc_flags &= ~UC_FPU;
- sigktou(mask, &ucp->uc_sigmask);
+ if (mask != NULL) {
+ /*
+ * Save signal mask.
+ */
+ sigktou(mask, &ucp->uc_sigmask);
+ } else {
+ ucp->uc_flags &= ~UC_SIGMASK;
+ bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+ }
+
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_savecontext != NULL) {
+ /*
+ * Allow the brand the chance to modify the context we
+ * saved:
+ */
+ BROP(p)->b_savecontext(ucp);
+ }
}
/*
@@ -136,7 +156,19 @@ restorecontext(ucontext_t *ucp)
{
kthread_t *t = curthread;
klwp_t *lwp = ttolwp(t);
+ proc_t *p = lwptoproc(lwp);
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_restorecontext != NULL) {
+ /*
+ * Allow the brand the chance to modify the context before
+ * we restore it:
+ */
+ BROP(p)->b_restorecontext(ucp);
+ }
+
+ DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+ uintptr_t, lwp->lwp_oldcontext,
+ uintptr_t, (uintptr_t)ucp->uc_link);
lwp->lwp_oldcontext = (uintptr_t)ucp->uc_link;
if (ucp->uc_flags & UC_STACK) {
@@ -184,6 +216,7 @@ getsetcontext(int flag, void *arg)
ucontext_t *ucp;
klwp_t *lwp = ttolwp(curthread);
stack_t dummy_stk;
+ proc_t *p = lwptoproc(lwp);
/*
* In future releases, when the ucontext structure grows,
@@ -228,6 +261,15 @@ getsetcontext(int flag, void *arg)
return (set_errno(EFAULT));
}
+ /*
+ * If this is a branded process, copy in the brand-private
+ * data:
+ */
+ if (PROC_IS_BRANDED(p) && copyin(&ucp->uc_brand_data,
+ &uc.uc_brand_data, sizeof (uc.uc_brand_data)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
restorecontext(&uc);
if ((uc.uc_flags & UC_STACK) && (lwp->lwp_ustack != 0))
@@ -311,7 +353,23 @@ savecontext32(ucontext32_t *ucp, const k_sigset_t *mask)
else
ucp->uc_flags &= ~UC_FPU;
- sigktou(mask, &ucp->uc_sigmask);
+ if (mask != NULL) {
+ /*
+ * Save signal mask.
+ */
+ sigktou(mask, &ucp->uc_sigmask);
+ } else {
+ ucp->uc_flags &= ~UC_SIGMASK;
+ bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+ }
+
+ if (PROC_IS_BRANDED(p) && BROP(p)->b_savecontext32 != NULL) {
+ /*
+ * Allow the brand the chance to modify the context we
+ * saved:
+ */
+ BROP(p)->b_savecontext32(ucp);
+ }
}
int
@@ -323,6 +381,7 @@ getsetcontext32(int flag, void *arg)
klwp_t *lwp = ttolwp(curthread);
caddr32_t ustack32;
stack32_t dummy_stk32;
+ proc_t *p = lwptoproc(lwp);
switch (flag) {
default:
@@ -354,6 +413,15 @@ getsetcontext32(int flag, void *arg)
return (set_errno(EFAULT));
}
+ /*
+ * If this is a branded process, copy in the brand-private
+ * data:
+ */
+ if (PROC_IS_BRANDED(p) && copyin(&ucp->uc_brand_data,
+ &uc.uc_brand_data, sizeof (uc.uc_brand_data)) != 0) {
+ return (set_errno(EFAULT));
+ }
+
ucontext_32ton(&uc, &ucnat);
restorecontext(&ucnat);
diff --git a/usr/src/uts/intel/vmxnet/Makefile b/usr/src/uts/intel/vmxnet/Makefile
new file mode 100644
index 0000000000..581aafbaa1
--- /dev/null
+++ b/usr/src/uts/intel/vmxnet/Makefile
@@ -0,0 +1,85 @@
+# 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 2019 Joyent, Inc.
+#
+
+#
+# VMware Ethernet Adapter b module
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+UTSCLOSED = ../../../../closed/uts
+
+#
+# Define the module and object file sets.
+#
+MODULE = vmxnet
+#
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/intel/io/vmxnet
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+# The list of object files is defined here, rather than in Makefile.files,
+# because the "$(CLOSED_BUILD)" macro has not been defined at the time
+# Makefile.files is processed.
+#
+VMXNET_OBJS += vmxnet.o
+
+OBJECTS = $(VMXNET_OBJS:%=$(OBJS_DIR)/%)
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFFILE)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+CPPFLAGS += -I$(UTSBASE)/i86pc
+LDFLAGS += -N misc/gld
+
+# needs work
+SMOFF += all_func_returns
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include ../Makefile.targ
diff --git a/usr/src/uts/intel/vnd/Makefile b/usr/src/uts/intel/vnd/Makefile
new file mode 100644
index 0000000000..5a412ea94d
--- /dev/null
+++ b/usr/src/uts/intel/vnd/Makefile
@@ -0,0 +1,52 @@
+#
+# 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 2019 Joyent, Inc.
+#
+
+UTSBASE = ../..
+
+MODULE = vnd
+OBJECTS = $(VND_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+CONF_SRCDIR = $(UTSBASE)/common/io/vnd
+
+CPPFLAGS += -I$(UTSBASE)/i86pc
+LDFLAGS += -Nmisc/neti -Nmisch/hook -Nfs/dev -Nmisc/gsqueue
+
+#
+# We use <sys/ctype.h> which causes gcc to think that all of its inline
+# functions are defined and unused.
+#
+CERRWARN += -_gcc=-Wno-unused-function
+
+# needs work
+SMOFF += or_vs_and
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/intel/zfd/Makefile b/usr/src/uts/intel/zfd/Makefile
new file mode 100644
index 0000000000..4f9146510a
--- /dev/null
+++ b/usr/src/uts/intel/zfd/Makefile
@@ -0,0 +1,40 @@
+#
+# 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 2014 Joyent, Inc. All rights reserved.
+#
+# uts/intel/zfd/Makefile
+
+UTSBASE = ../..
+
+MODULE = zfd
+OBJECTS = $(ZFD_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
diff --git a/usr/src/uts/sfmmu/vm/hat_sfmmu.c b/usr/src/uts/sfmmu/vm/hat_sfmmu.c
index 76617cb130..1f0ef11096 100644
--- a/usr/src/uts/sfmmu/vm/hat_sfmmu.c
+++ b/usr/src/uts/sfmmu/vm/hat_sfmmu.c
@@ -86,6 +86,7 @@
#include <sys/fpu/fpusystm.h>
#include <vm/mach_kpm.h>
#include <sys/callb.h>
+#include <sys/zone.h>
#ifdef DEBUG
#define SFMMU_VALIDATE_HMERID(hat, rid, saddr, len) \
@@ -934,6 +935,7 @@ static kphysm_setup_vector_t sfmmu_update_vec = {
} \
pp->p_mapping = hme; \
pp->p_share++; \
+ zone_add_page(pp); \
}
/*
@@ -954,6 +956,7 @@ static kphysm_setup_vector_t sfmmu_update_vec = {
\
ASSERT(pp->p_share > 0); \
pp->p_share--; \
+ zone_rm_page(pp); \
\
if (hme->hme_prev) { \
ASSERT(pp->p_mapping != hme); \
@@ -7346,6 +7349,8 @@ retry:
tpp->p_mapping = NULL;
dpp->p_share = tpp->p_share;
tpp->p_share = 0;
+ dpp->p_zoneid = tpp->p_zoneid;
+ tpp->p_zoneid = ALL_ZONES;
while (index != 0) {
index = index >> 1;
diff --git a/usr/src/uts/sparc/Makefile.sparc b/usr/src/uts/sparc/Makefile.sparc
index 306f84df75..210d2b512a 100644
--- a/usr/src/uts/sparc/Makefile.sparc
+++ b/usr/src/uts/sparc/Makefile.sparc
@@ -231,6 +231,7 @@ DRV_KMODS += nulldriver
DRV_KMODS += bridge trill
DRV_KMODS += bpf
DRV_KMODS += dca
+DRV_KMODS += inotify
DRV_KMODS += eventfd
DRV_KMODS += signalfd
DRV_KMODS += ufm
@@ -496,6 +497,7 @@ SOCKET_KMODS += sockpfp
SOCKET_KMODS += socksctp
SOCKET_KMODS += socksdp
SOCKET_KMODS += sockrds
+SOCKET_KMODS += datafilt
#
# kiconv modules (/kernel/kiconv):
diff --git a/usr/src/uts/sparc/bpf/Makefile b/usr/src/uts/sparc/bpf/Makefile
index cfc7bdfd1d..213c28a29d 100644
--- a/usr/src/uts/sparc/bpf/Makefile
+++ b/usr/src/uts/sparc/bpf/Makefile
@@ -58,8 +58,7 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
#
#
CFLAGS += $(CCVERBOSE)
-LDFLAGS += -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti
-INC_PATH += -I$(UTSBASE)/common/io/bpf
+LDFLAGS += -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti -Ndrv/ip
#
# For now, disable these warnings; maintainers should endeavor
diff --git a/usr/src/uts/sparc/datafilt/Makefile b/usr/src/uts/sparc/datafilt/Makefile
new file mode 100644
index 0000000000..4560585d42
--- /dev/null
+++ b/usr/src/uts/sparc/datafilt/Makefile
@@ -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 (c) 2011, OmniTI Computer Consulting, Inc. All rights reserved.
+# Copyright 2012, Nexenta Systems, Inc. All rights reserved.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = datafilt
+OBJECTS = $(DATAFILT_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_SOCK_DIR)/$(MODULE)
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CFLAGS += $(CCVERBOSE)
+
+LDFLAGS += -Nfs/sockfs -Ndrv/ip
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
diff --git a/usr/src/uts/sparc/dld/Makefile b/usr/src/uts/sparc/dld/Makefile
index ed46cee2f8..7d4c31d3de 100644
--- a/usr/src/uts/sparc/dld/Makefile
+++ b/usr/src/uts/sparc/dld/Makefile
@@ -55,7 +55,6 @@ CFLAGS += $(CCVERBOSE)
$(RELEASE_BUILD)CFLAGS += -xinline=auto -xcrossfile
$(RELEASE_BUILD)COPTIMIZE = -xO5
LDFLAGS += -N misc/dls -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# For now, disable these warnings; maintainers should endeavor
diff --git a/usr/src/uts/sparc/dls/Makefile b/usr/src/uts/sparc/dls/Makefile
index 5fb9c5e27f..055906e0c8 100644
--- a/usr/src/uts/sparc/dls/Makefile
+++ b/usr/src/uts/sparc/dls/Makefile
@@ -53,7 +53,6 @@ CFLAGS += $(CCVERBOSE)
$(RELEASE_BUILD)CFLAGS += -xinline=auto -xcrossfile
$(RELEASE_BUILD)COPTIMIZE = -xO5
LDFLAGS += -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# For now, disable these warnings; maintainers should endeavor
diff --git a/usr/src/uts/sparc/icmp/Makefile b/usr/src/uts/sparc/icmp/Makefile
index 64640cafe7..b36d51cda7 100644
--- a/usr/src/uts/sparc/icmp/Makefile
+++ b/usr/src/uts/sparc/icmp/Makefile
@@ -22,6 +22,7 @@
# uts/sparc/icmp/Makefile
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
#
# This makefile drives the production of the icmp IP driver
#
diff --git a/usr/src/uts/sparc/inotify/Makefile b/usr/src/uts/sparc/inotify/Makefile
new file mode 100644
index 0000000000..89617b71dc
--- /dev/null
+++ b/usr/src/uts/sparc/inotify/Makefile
@@ -0,0 +1,61 @@
+#
+# 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.
+#
+
+#
+# Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE = ../..
+
+#
+# Define the module and object file sets.
+#
+MODULE = inotify
+OBJECTS = $(INOTIFY_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR = $(UTSBASE)/common/io
+
+#
+# Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+CERRWARN += -_gcc=-Wno-parentheses
+LDFLAGS += -Nfs/specfs
+
+#
+# Define targets
+#
+ALL_TARGET = $(BINARY) $(SRC_CONFILE)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+# Default build targets.
+#
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+#
+# Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
diff --git a/usr/src/uts/sparc/ipf/ipf.global-objs.debug64 b/usr/src/uts/sparc/ipf/ipf.global-objs.debug64
index 663613cee3..b42dca618a 100644
--- a/usr/src/uts/sparc/ipf/ipf.global-objs.debug64
+++ b/usr/src/uts/sparc/ipf/ipf.global-objs.debug64
@@ -22,9 +22,25 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright 2013 Joyent, Inc. All rights reserved
+# Copyright 2019 Joyent, Inc.
#
+cfw_evdrops
+cfw_evreports
+cfw_ring
+cfw_ringcv
+cfw_ringend
+cfw_ringfull
+cfw_ringlock
+cfw_ringmask
+cfw_ringsize
+cfw_ringstart
+cfw_timeout_tries
+cfw_timeout_wait
+hook4_vnd_in
+hook4_vnd_out
+hook6_vnd_in
+hook6_vnd_out
fr_availfuncs
fr_features
fr_objbytes
@@ -56,6 +72,7 @@ icmptoicmp6unreach
idletime_tab
ip6exthdr
ipf_cb_ops
+ipf_cfwlog_enabled
ipf_dev_info
ipf_devfiles
ipf_kstat_tmp
diff --git a/usr/src/uts/sparc/mac_ether/Makefile b/usr/src/uts/sparc/mac_ether/Makefile
index 7d2413781e..889da099ea 100644
--- a/usr/src/uts/sparc/mac_ether/Makefile
+++ b/usr/src/uts/sparc/mac_ether/Makefile
@@ -54,7 +54,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/sparc/mac_ib/Makefile b/usr/src/uts/sparc/mac_ib/Makefile
index c44e192745..84855bf749 100644
--- a/usr/src/uts/sparc/mac_ib/Makefile
+++ b/usr/src/uts/sparc/mac_ib/Makefile
@@ -54,7 +54,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -N misc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/sparc/mac_wifi/Makefile b/usr/src/uts/sparc/mac_wifi/Makefile
index 7c3b74a79b..02d03549ac 100644
--- a/usr/src/uts/sparc/mac_wifi/Makefile
+++ b/usr/src/uts/sparc/mac_wifi/Makefile
@@ -56,7 +56,6 @@ INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
#
CFLAGS += $(CCVERBOSE)
LDFLAGS += -Nmisc/mac
-INC_PATH += -I$(UTSBASE)/common/io/bpf
#
# Default build targets.
diff --git a/usr/src/uts/sparc/syscall/getcontext.c b/usr/src/uts/sparc/syscall/getcontext.c
index aa933e5d23..3cf07718ad 100644
--- a/usr/src/uts/sparc/syscall/getcontext.c
+++ b/usr/src/uts/sparc/syscall/getcontext.c
@@ -20,6 +20,9 @@
*/
/*
+ * Copyright 2015 Joyent, Inc.
+ */
+/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -110,10 +113,15 @@ savecontext(ucontext_t *ucp, const k_sigset_t *mask)
ucp->uc_flags &= ~UC_FPU;
ucp->uc_mcontext.gwins = (gwindows_t *)NULL;
- /*
- * Save signal mask.
- */
- sigktou(mask, &ucp->uc_sigmask);
+ if (mask != NULL) {
+ /*
+ * Save signal mask.
+ */
+ sigktou(mask, &ucp->uc_sigmask);
+ } else {
+ ucp->uc_flags &= ~UC_SIGMASK;
+ bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+ }
}
@@ -412,11 +420,16 @@ savecontext32(ucontext32_t *ucp, const k_sigset_t *mask, struct fq32 *dfq)
ucp->uc_flags &= ~UC_FPU;
ucp->uc_mcontext.gwins = (caddr32_t)(uintptr_t)NULL;
- /*
- * Save signal mask (the 32- and 64-bit sigset_t structures are
- * identical).
- */
- sigktou(mask, (sigset_t *)&ucp->uc_sigmask);
+ if (mask != NULL) {
+ /*
+ * Save signal mask (the 32- and 64-bit sigset_t structures are
+ * identical).
+ */
+ sigktou(mask, (sigset_t *)&ucp->uc_sigmask);
+ } else {
+ ucp->uc_flags &= ~UC_SIGMASK;
+ bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+ }
}
int
diff --git a/usr/src/uts/sparc/zfd/Makefile b/usr/src/uts/sparc/zfd/Makefile
new file mode 100644
index 0000000000..6371399988
--- /dev/null
+++ b/usr/src/uts/sparc/zfd/Makefile
@@ -0,0 +1,42 @@
+#
+# 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 2014 Joyent, Inc. All rights reserved.
+#
+# uts/intel/zfd/Makefile
+
+UTSBASE = ../..
+
+MODULE = zfd
+OBJECTS = $(ZFD_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(USR_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/sparc/Makefile.sparc
+
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+
+CFLAGS += $(CCVERBOSE)
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/sparc/Makefile.targ
diff --git a/usr/src/uts/sun4u/io/pci/pcisch.c b/usr/src/uts/sun4u/io/pci/pcisch.c
index 5d2981b6c8..a8ffef185c 100644
--- a/usr/src/uts/sun4u/io/pci/pcisch.c
+++ b/usr/src/uts/sun4u/io/pci/pcisch.c
@@ -2973,11 +2973,12 @@ iommu_tlb_scrub(iommu_t *iommu_p, int scrub)
"\tContext=%lx %sWritable %sStreamable\n"
"\tPCI Page Size=%sk Address in page %lx\n",
ddi_driver_name(dip), ddi_get_instance(dip), errstat, i,
- (tag & TLBTAG_CONTEXT_BITS) >> TLBTAG_CONTEXT_SHIFT,
+ (uint64_t)(tag & TLBTAG_CONTEXT_BITS) >>
+ TLBTAG_CONTEXT_SHIFT,
(tag & TLBTAG_WRITABLE_BIT) ? "" : neg,
(tag & TLBTAG_STREAM_BIT) ? "" : neg,
(tag & TLBTAG_PGSIZE_BIT) ? "64" : "8",
- (tag & TLBTAG_PCIVPN_BITS) << 13);
+ (uint64_t)(tag & TLBTAG_PCIVPN_BITS) << 13);
cmn_err(CE_CONT, "Memory: %sValid %sCacheable Page Frame=%lx\n",
(data & TLBDATA_VALID_BIT) ? "" : neg,
(data & TLBDATA_CACHE_BIT) ? "" : neg, pfn);